pax_global_header00006660000000000000000000000064151127675640014530gustar00rootroot0000000000000052 comment=d2c74dcceb3d5b5ebfd7e55657a06ac2e2124e80 symfc-1.6.0/000077500000000000000000000000001511276756400126555ustar00rootroot00000000000000symfc-1.6.0/.github/000077500000000000000000000000001511276756400142155ustar00rootroot00000000000000symfc-1.6.0/.github/workflows/000077500000000000000000000000001511276756400162525ustar00rootroot00000000000000symfc-1.6.0/.github/workflows/publish-gh-pages.yml000066400000000000000000000020771511276756400221420ustar00rootroot00000000000000name: publish symfc gh-pages on: push: branches: [publish-gh-pages] jobs: docs: runs-on: ubuntu-latest defaults: run: shell: bash -l {0} strategy: matrix: python-version: ["3.12"] steps: - uses: actions/checkout@v4 - uses: conda-incubator/setup-miniconda@v3 with: auto-update-conda: true channels: conda-forge channel-priority: strict python-version: ${{ matrix.python-version }} - name: Install dependencies run: | conda activate test conda install --yes -c conda-forge python=${{ matrix.python-version }} conda install --yes -c conda-forge sphinx-book-theme linkify-it-py myst-parser sphinxcontrib-bibtex ipython - name: Build run: | conda activate test sphinx-build doc docs_build - name: Deploy docs at develop branch if: ${{ github.ref == 'refs/heads/publish-gh-pages' }} uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./docs_build symfc-1.6.0/.github/workflows/publish-to-test-pypi.yml000066400000000000000000000020401511276756400230130ustar00rootroot00000000000000name: publish PyPI and TestPyPI on: push: branches: - main - rc jobs: build-linux: runs-on: ubuntu-latest strategy: matrix: python-version: [3.12, ] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Make sdist run: | pip install numpy scipy build python -m build - name: Publish package to TestPyPI if: github.event_name == 'push' && startsWith(github.ref, 'refs/heads/rc') uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.TEST_PYPI_API_TOKEN }} repository_url: https://test.pypi.org/legacy/ - name: Publish package to PyPI if: github.event_name == 'push' && startsWith(github.ref, 'refs/heads/main') uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} symfc-1.6.0/.github/workflows/tests.yml000066400000000000000000000021231511276756400201350ustar00rootroot00000000000000name: symfc test using conda-forge environment on: pull_request: branches: [ develop ] jobs: build-linux: runs-on: ubuntu-latest defaults: run: shell: bash -l {0} strategy: matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 - uses: conda-incubator/setup-miniconda@v3 with: auto-update-conda: true channels: conda-forge channel-priority: strict python-version: ${{ matrix.python-version }} - name: Install dependencies run: | conda activate test conda install --yes -c conda-forge python=${{ matrix.python-version }} conda install --yes -c conda-forge spglib scipy numpy pytest codecov pytest-cov - name: Setup symfc run: | conda activate test pip install -e . -vvv - name: Test with pytest run: | conda activate test pytest -v --cov=./ --cov-report=xml - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v3 with: verbose: true symfc-1.6.0/.gitignore000066400000000000000000000060271511276756400146520ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # 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 *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Translations *.mo *.pot # Django stuff: *.log local_settings.py db.sqlite3 db.sqlite3-journal # Flask stuff: instance/ .webassets-cache # Scrapy stuff: .scrapy # Sphinx documentation docs/_build/ # PyBuilder .pybuilder/ target/ # Jupyter Notebook .ipynb_checkpoints # IPython profile_default/ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: # .python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. # However, in case of collaboration, if having platform-specific dependencies or dependencies # having no cross-platform support, pipenv may install dependencies that don't work, or not # install all needed dependencies. #Pipfile.lock # poetry # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control #poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. #pdm.lock # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it # in version control. # https://pdm.fming.dev/#use-with-ide .pdm.toml # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Celery stuff celerybeat-schedule celerybeat.pid # SageMath parsed files *.sage.py # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Spyder project settings .spyderproject .spyproject # Rope project settings .ropeproject # mkdocs documentation /site # mypy .mypy_cache/ .dmypy.json dmypy.json # Pyre type checker .pyre/ # pytype static type analyzer .pytype/ # Cython debug symbols cython_debug/ # PyCharm # JetBrains specific template is maintained in a separate JetBrains.gitignore that can # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ .vscode _build/ symfc-1.6.0/.pre-commit-config.yaml000066400000000000000000000007201511276756400171350ustar00rootroot00000000000000# See https://pre-commit.com for more informatio # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml exclude: ^conda/ - id: check-added-large-files - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.14.6 hooks: - id: ruff args: [ "--fix", "--show-fixes" ] - id: ruff-format symfc-1.6.0/LICENSE000066400000000000000000000027341511276756400136700ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2023, symfc project Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. symfc-1.6.0/MANIFEST.in000066400000000000000000000000141511276756400144060ustar00rootroot00000000000000graft tests symfc-1.6.0/README.md000066400000000000000000000031021511276756400141300ustar00rootroot00000000000000# symfc ## What does symfc do? Atomic vibrations in crystals are often conveniently described using the phonon model. In this model, the crystal potential is expanded into a Taylor series with respect to atomic displacements from their equilibrium positions, and the expansion coefficients are referred to as force constants. Predicting phonon properties through computer simulations is becoming increasingly popular, with the supercell approach being one of the techniques employed for phonon calculations. In this method, force constants are derived from datasets of atomic forces and displacements obtained from supercell snapshots, which feature various configurations of atomic displacements. While force constants possess specific symmetries, those computed from displacement-force datasets often do not adhere to these symmetries due to factors such as numerical noise or approximations used. Symfc is a software designed to compute force constants from displacement-force datasets in the supercell approach, ensuring they meet the required symmetry constraints. ## Citation of symfc "Projector-based efficient estimation of force constants", A. Seko and A. Togo, Phys. Rev. B, **110**, 214302 (2024) [[doi](https://doi.org/10.1103/PhysRevB.110.214302)] [[arxiv](https://arxiv.org/abs/2403.03588)]. ``` @article{PhysRevB.110.214302, title = {Projector-based efficient estimation of force constants}, author = {Seko, Atsuto and Togo, Atsushi}, journal = {Phys. Rev. B}, volume = {110}, issue = {21}, pages = {214302}, numpages = {18}, year = {2024}, month = {Dec}, } ``` symfc-1.6.0/doc/000077500000000000000000000000001511276756400134225ustar00rootroot00000000000000symfc-1.6.0/doc/Makefile000066400000000000000000000011721511276756400150630ustar00rootroot00000000000000# Minimal makefile for Sphinx documentation # # You can set these variables from the command line, and also # from the environment for the first two. SPHINXOPTS ?= SPHINXBUILD ?= sphinx-build SOURCEDIR = . BUILDDIR = _build # Put it first so that "make" without argument is like "make help". help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) symfc-1.6.0/doc/changelog.md000066400000000000000000000027001511276756400156720ustar00rootroot00000000000000(changelog)= # Change Log ## Nov-30-2025: Version 1.6.0 - Sparse solver for FC2. - Deprecated `displacements` and `forces` parameters for instantiating the `Symfc` class. Use its attributes after instantiation. ## Jul-21-2025: Version 1.5.4 - Improve eigensolver for large projector. - Check symmetric property of projector and make it symmetric unless the symmetry is largely broken. ## Jul-15-2025: Version 1.5.3 - More optimization for basis set computations. ## Jul-14-2025: Version 1.5.2 - Improve memory efficiency and calculation performance for basis set computations using block matrix tree. ## Jul-10-2025: Version 1.5.1 - Improve memory efficiency and calculation performance for basis set computations. The current implementation shows particular effectiveness with `use_mkl=True` (requires sparse-dot-mkl) for large systems. ## Jul-3-2025: Version 1.5.0 - Improve memory efficiency in computing basis sets ## Jun-26-2025: Version 1.4.1 - Small fix for specific case ## May-31-2025: Version 1.4.0 - Maintenance release after refactoring ## Feb-25-2025: Version 1.3.4 - Maintenance release after refactoring ## Feb-11-2025: Version 1.3.3 - Fix minor translational invariance issues in O2, O3, and O4 ## Feb-10-2025: Version 1.3.2 - Use numerically stable version of O3 translational invariance ## Feb-5-2025: Version 1.3.1 - Enabled cutoff parameter for fc2. ## Feb-4-2025: Version 1.3.0 - Add `Symfc.estimate_basis_set()`. symfc-1.6.0/doc/conf.py000066400000000000000000000026541511276756400147300ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = "symfc" copyright = "2024, symfc project" author = "Atsuto Seko" version = "1.6" release = "1.6.0" # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration extensions = [ "sphinx.ext.mathjax", "myst_parser", "IPython.sphinxext.ipython_console_highlighting", "IPython.sphinxext.ipython_directive", "sphinx.ext.extlinks", ] myst_enable_extensions = ["linkify", "dollarmath", "amsmath"] templates_path = ["_templates"] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output html_theme = "sphinx_book_theme" html_static_path = ["_static"] html_title = "Symfc v.%s" % release extlinks = { "issue": ("https://github.com/symfc/symfc/issues/%s", "issue %s"), "path": ("https://github.com/symfc/symfc/tree/develop/%s", "%s"), "user": ("https://github.com/%s", "%s"), } symfc-1.6.0/doc/index.md000066400000000000000000000027571511276756400150660ustar00rootroot00000000000000# Symfc Symfc is a Python-based force-constants solver that uses the supercell approach. By employing an efficient projector-based algorithm that leverages crystal and force-constant symmetries, it significantly reduces computational and memory requirements. The code accepts displacement–force datasets as input and outputs supercell force constants. Symfc supports the calculation of second-, third-, and fourth-order force constants. ## Usage Detailed documentation will be provided soon. In the meantime, please refer to `api_symfc.py` for more information. Additionally, an [example implementation](https://github.com/phonopy/phonopy/blob/master/phonopy/interface/symfc.py) can be found in the phonopy code, particularly in the `SymfcFCSolver._initialize` method. ## License BSD-3-Clause. ## Citation of symfc "Projector-based efficient estimation of force constants", A. Seko and A. Togo, Phys. Rev. B, **110**, 214302 (2024) [[doi](https://doi.org/10.1103/PhysRevB.110.214302)] [[arxiv](https://arxiv.org/abs/2403.03588)]. ``` @article{PhysRevB.110.214302, title = {Projector-based efficient estimation of force constants}, author = {Seko, Atsuto and Togo, Atsushi}, journal = {Phys. Rev. B}, volume = {110}, issue = {21}, pages = {214302}, numpages = {18}, year = {2024}, month = {Dec}, } ``` ## Contributors - {user}`Atsuto Seko ` (Kyoto university) - {user}`Atsushi Togo ` (National Institute for Materials Science) ```{toctree} :hidden: install changelog ``` symfc-1.6.0/doc/install.md000066400000000000000000000007361511276756400154200ustar00rootroot00000000000000# Installation PyPI and conda-forge packages are available. - https://pypi.org/project/symfc - https://anaconda.org/conda-forge/symfc ## Requirement - numpy - scipy - spglib (optional) ## Installation from source code A simplest installation using conda-forge packages: ```bash % conda create -n symfc -c conda-forge % conda activate symfc % conda install -c conda-forge numpy scipy spglib % git clone https://github.com/symfc/symfc.git % cd symfc % pip install -e . ``` symfc-1.6.0/examples/000077500000000000000000000000001511276756400144735ustar00rootroot00000000000000symfc-1.6.0/examples/NaCl444-rd/000077500000000000000000000000001511276756400161475ustar00rootroot00000000000000symfc-1.6.0/examples/NaCl444-rd/README.md000066400000000000000000000004541511276756400174310ustar00rootroot00000000000000# NaCl example The python example is found in `calc-fc.py`. Force constants are calculated from 20 supercell snapshots. The crystal structure, displacement-force dataset, and parameters needed for non-analytical term correction are found in `phonopy_NaCl444_rd.yaml.xz` in the phonopy-yaml format. symfc-1.6.0/examples/NaCl444-rd/calc-fc.py000066400000000000000000000006051511276756400200120ustar00rootroot00000000000000"""Calculate force constants using symfc.""" import phonopy from symfc import Symfc ph = phonopy.load("phonopy_NaCl444_rd.yaml.xz", produce_fc=False) symfc = Symfc( ph.supercell, displacements=ph.dataset["displacements"], forces=ph.dataset["forces"], log_level=1, ).run(orders=[2]) ph.force_constants = symfc.force_constants[2] ph.auto_band_structure(plot=True).show() symfc-1.6.0/examples/NaCl444-rd/phonopy_NaCl444_rd.yaml.xz000066400000000000000000013142701511276756400230150ustar00rootroot000000000000007zXZִF!t/够]8 DKIa2×M}0h/tt=oǫt#fkM{(A w6uB!D3 q7(r +3ُ_LdhSDoq>uR/ ؘ 8׊QыMi(k0;U3%\Vzj m+ 8OA)Pp^Q2g.WQlA+n(kX_zr/yH@d >k}ä$:1lf8ܤ+])8 p;~ZWl:"^5qhߘOV4 QQ|1C}[l:S)@K"ATwNF 榘iˀѠԺRX ^UoVc 48::B&*o$-K&\ +;mW<>Z[L.BXCi ~%~ 6&t^7p>]?Q=mD̄>`;82A(pl ƣy4]rf6#BCi I.8e{=I$n4׵5}uɮ\E-9ϖ7P/Pzq[icjh+8~Դ;qAU|d#y>&a%cͨwEzŕ.ЩV|`k"1ar\ӴlP{@ChRJLxa?\4a8qG7gN8rxM re(᠋&f)et#݊1dK;pn5ۓ'9}a;_+0Y3?8oFM腁W1=q}۹3<LJf zG ݡpWȾ)4Mqq7`Ey"H<4C5Т 6J5# _输4?si[F, A;P\b@{ݧwĠVtJ$w~gDueT^ނ`vFC(/bՌChm"gQ3 r|dj9&s8b7ZVnA= 9&OV1Kt\4+.Ixd; 9v,Z !*ڡI ŷ@E`0Hl))1Gǣ/Zc>WpP wdTib}AlZt`@V&fx>y`E 7-ȋ|j-p\dăĹG6=dj%pa>(l6{(y#;oUDvVuу 2&|zB uʥѫT+~baoS4x=lS]p`Ʊ?c=_u< qn~wS|5m t1,7k %D^ʧՒEP|zr ;34u 2$jk'c~D H_@6ZOd+Ph,1d@EyxaaS~%DIVFx kvȮ_֑5{zGS`H:(RoF3PpG9$ևNYp9ب!J)=JS,&T/u\{t {8gpMe|T M Ǵظ=6oqh"RrVXpUK9ǩ \."uz!ԬU'=-$ux?F,4V!ihѼT\EStբý9i{is c?-uHOi%`_/ Ɗ7[ 0fiMP,4ɫXb5fWstqGw]qZ_5R#]؎,}v zr vx9ykszN1&>]rD,uuiy:ׂIOЮLyg5o;zSY7H{67|x(X w@7"08,{MMzK'EUͫ JYu u#vтOk#4xxmнZѠ΋F@Se2=Yk̮pnM1ecu=R WD!#⍭H08lR9\BlSMz-n| `W>s48 OtAY6?#V6~1Y3Uq27+@d)]UmAb<8մU$G:_ZV FA)I^4.7Tx#q9SDEf?\}1ڄa>ƥ.J. X^`VyS=lq-y?[?rB".uT0.ٕ̎c| xiO κQ g'yX4'%OLcufʅC,l Rg5S(yE?EAcFga`3-q-c y;kO_5rBIc{stm K]0:9 Q|}Xx4-{ Qj@9}Dez4@b oHI&A{WkC3+qH4LaaAMń~sPFidrq-BCj;ᡙvӽH[I|PXH"#ofe+{s6B`)<ʡD4ƚ)T[N8g19efFY@/9Rm!op-FPOO/L;FIXW[˶mb]KBF68.tMՆ_`kW&Q;ܣB1jc*:p~0qu ÝH8s̷NX?9pvVFeC"&he&'`d(0 ;Dϰuٗ:zn| C}1Eڒ edGFsلZ#(O}$%>Kq 32o.8ۗgv Z>/qĔNȺ^X5T)Ή<#=ARߑf.ǻEPgY^̿2li0*ofY=ߧs2$ı_Dpe j >iň!¨r s[ o.{+WlHۻ +F" tWd+`q 6J3 /G\Ae6c-J9ɁT kKUVD!t+ěm~ŘڧK8sΎ"ېHKiwRN0MXLFfdg_ǟ{s0_^a~Y`IDҵTjl+wWutamV@uYBZψgATHN0"@53iY:q:0W`-UϏ6HwT =Ս㯲T88BF!+O1h0 a[*,?ofBPP#S=VN*Mc@cdl!WHmY:U)5m.n,nw\ uzy-Ig&+o."eo'$AΑcʣ΃Xo'z%J˔DP̿bޱxbرKʴ!^]j8ӓQ;k l̕Xdg*T$e|14ل_ci9'}fxo䵊̸#嚿hJ亄@fARo=D~#Ea5L锋O .!fJe?sn+>QOSjXz"~{{1-)1ITE WUq9f9 n( >ZMn3}t=RbC X1(Rd* 0bw9 c[/[@Y] s/)$Eĵg`ŭ\#5C32,% tjF(|eթI-"Qľvx kGˣR񶯨baT1)K3n]*&A.rƇ n.wh8# ̥xlHhR\KDAwuYՠSHYgh PHM.jU\֤DMQ"7(~"-aJtǸO0w\ ڽzD+'AY"t 1“ȫ _ל־'HVR˽˜g9R4 ζy1A~w V'`\F&8I h>N ?41dgknJzD )A]B>j do iH"Ø $\RI9o5K-nb?Q]aвV&佌zI7~R:# JO1q#ɀ2KrA*z0>KGzÏajɌ_ ^-O,JOf :vhiчs+K$T FJ`">G;`7dd.8Sȇxv@&Lѡr5k,iL\ސЁ7G&JYBp)w 7$tLZgԴnoib$G }:Jl ?(1|9Kz!k d<ډ]ػ"u}k HcX~=+ ) W=iOxcumw')ԶnK  /4KY)OH([9LnnJ ,گ1S V|aYT_$c!i E X>۲br R5#";WǢydx9:اp (T 4䨫(t^z-T=m4h~8b$-zT45ֻ͗#7xdSIqft7/Ar+w` ߸{:K|e mGHM [,!|7\Lo}ITzzW@# ˅9tfwV{E~݀(Dzܦ+D{\! 2NʰG7cT+E-;WN"+C`!RFD@ypK8ԣz9C) FEhZ4='0< RJWAҠD!٬ A5ɕoFWcnnJkw X"KZdNVWW5sTUZ&,7ޅRR0^s`^/ͱ"]e#ς1.9:CهzߙOE6W_G~mw?E}:e- 㫨ݽy6چ 77ލT"<HN1h˻32Nm;A$H#f]ߛZ4I0Mp]=.]^.+(WKR?U6c1_3 Zty9Q/݊aBeJPG㔕 P/x\@=3UPS@9*'hEueYn_ن(FaݖVQ$38|E|2%kaU};J0t+EsIN*,è~)@l?M`G?4 ,QBu)~=+7PƑ븀(Iw6urFD`wD[!oJ5xh/%W ⮣rtdhuWS,f!8ﻫb`^(Wh訰NR'(㸔ނo2#F ḭ'RR>U Լ~ :$c9rlw~4ŽYs~TQPf]rgB6x*z%x3(tHmz#|E"aPSėx6.5:4w{ԀG%uȐe-~R+xl,,P 6Jc7Y}mMnKɷl >UH)0n8!-b"4T,sj ,ŏ>w@h?9T(S$m0)-46CF&j{WٸT7@FkNQ>"Fo? Vții0/¤ek/SӪ|ϬAŐy8jmUH+<HPlb0snCq~,㖭tCW{cv!8Q 0jȐ*Kz&M4]UA X؞t8_X %߹`ʒ̞2yn#ZhE{ :!8?,;5A`I773j|hOqXZO=E_w\âTKgdKOE]ΆM|42;QQcwŤRGu1{]6.N.Gk<í÷=+l\Qg ;sB}0;~!zƤ&񭵡 O3xpfKK`Grw{* Uŝқk:d9-%g>rzjY F>x_uduՊrsEOޛnڶɸIbĚV#?Cg i*W<-qX2$ G嗼$]"4tu3ԃ uf,-RQ*?<^>g RO|QY,R篃}cB n|2sQQ62*6YΉDqꆾuˇ (495ѿ֍J;{eM[BgX>8`vS&Ăyghq^ղ;hײ A:l%z3|lXOʩ66;td,\ PSs|eK\H; 7ʅ'"'WӋSo5LŴ`ڂJݥW՜vJ CW_v0g[|I-"HQeǟ{ˤYi9#=ɚ\D W_y[DS["'D¦ĉ|%\2c5ܺH*n|r:ž,1&uPpZY;_lXpGa7zkH"ϸnL%7q-`9'tu Us?S.OXܳS.,R*h*SnVi/ h"G;@@L)K/J&KzeZ= 7B$z`:'eIb/V9n_-? ' _Gt$?*xXh)@>O~P@zb눦fAO>k *̠n^3p 2@?DrlϯQyH]FF㹿==)`OV$7ֻ6#zv+ o03Ծ Xe? E({Qu4oXq4& d$g+~Gn *ϫ:Ddc_,SSp ?*ǘĨg+8MgvC`u렭Y H6|Y8澯bTE5W@Q |Η2l}cHҰ!O+LEtX@;-[!-vwArMN)15w_&`>&uIv')٠wk%xCV #mx$>eupV9Xܠh_ "+þ*10nB W}*031 ,H%nbg^XHI :ݫ| Nrb*n3Fbu\?ev($|֤_ [3.@;&6-{5pO4x""5C9WOfCN9ĸ۾$EO-+2@ilu"oPG)@ -,%̶ : &ǡ~V&-Dºi=nGą@geO벧 9DOTIsH.qo7bntOcCa]y50hIXUᕲ1zudWrW$S|k@"K\&lBGli*0uk! )_>:83v܆prɂ'=4]&wng6E uͺK+LSy`\O>HՎ9(hy\Z8Iree ThLy !`o TcJ+kwc/S:cO LԜMLLc[bEN_ L;UȊ9QF $6(Yg= wPôAѰP[ #*"^1UeQF/3w{ڧ^^Tm:R4#wu"X$_|aKm4}FIZ*7e-EX+eEÕ2ꅥ?,})x3}T{ KvMq'z>%K7~E Z;wO?tjֲS3s2r#A0S7^<-pn,JW;Yթ$l6CJʑwBáK(] iPnv UYo˭ V Ov)>}hmuÝcɨTWZOnhrUI^.?ztu ;} "<DA(}*wmVg:b 2Ըsz`yP AՁ^Ȋghui30jԦ=~}3|hXf-)м3hnZ+Eǟh{چ9ϝzu4֟ґ`9@;f&*܁]_&,)@%LըZtFHܳhYmnsR߼ gl{ߧez\%B+(FSGbkծL:q!\7Bgu 08S¬eY27mcO~_-g5캭yv«I*P(C[r> 4_X&I./J A0~Vj~D{14^'b'4MQd>7/-4 5ҬSA RBQqG+kx*y7FI5IK`Y&O#j->-! kCVBlMbR+ MX-U`tXu0tj\d4ߜ`!|{mLBa>y8\G3S XMĕ<tf17;8M"*`!8kNL5}1&:}o_qO^>02V|=6QgAԈ 4^JA )'eEU"I 7j yCPv__Z u@, 6q`cH-bD5wcr+##'"sw iqe1vjV䆡#J.&X2GK*ӭk~abީ#Z}v &TB8П0yDƭ},-<)av3T_{oN0@Q= s*>z@UT!A-0y>ɓ _qN{,kOj^ +z BVtAŋNNjtEb֊H:S]C:>$K$32Cƫ:5Aґ79Il(=[¸8鹤bir+> ZkT ;:˝ݯ*FV ?VHsB|-c€3)3~KeUvqzL xM i7imUo΂sqCpaS㐉JON 7mڴfȜ=f}$@Զ wԢ*S}pK7"8V'Lg%t(8xpcGu/|Cde˔RG-UR~ʹv{D fh2 ;DV63H}3THpڰ(EB&yS{q8%iE%z2mW(OPí55 +2zbNc;F՚fEJF]$ .\a}oc.N&ق| %7^sbS([T=t0~)h(L/|<$ԺXȺG4LK$XYyo>0|CԊb=s=ܑ!fqs6 5hZ(ތԒ;^T>t>M?%X`YTC>)U (y?;\qd4{e%J9d\&dqbŹ`Ҕ(mH;( q\[ul:p*Tt@,DHSJW(`L3nCk.l9E@k`bL9BC"4Tܴiٿ3'Mj X@:4-@bC/r:-fWXmD5p)"O!G[ Q\Y8 *i[ufJHtdYz.U?٢]O@9aCQYP{bd(dPj\Γis魱ULXA0./!c!>Һ5D?itMe*(yks%`r:ke_̰:@ifh GA-c'e\ɠS5CGME!;+ߞg!m.}Sh10 `OyC6'SY%l[56k3]H)?2rp)-T 5Mb=EN`]:*1-oIkaG[企+rſ`[~T%$sO* JG3؅ C&nA9ω39b+<-: Kq8 *qtEfa]mf2#B$K:(YUZ7e֎^ ʕ J[gD29# DR{ |d@Vl9E]zZ6TJbr: ߓD{&\Ŝ={ rSء"r`bu>gMUYʱ|]{ 6uU<` n=I~>F >noZ{\~(5E9\1);!42{2m\#3j9^V$ẍJ(jhxAbQCC!CE$:`T)}MQ,4` Γ{[Fu"M<d\3F9޲M"k*)6,cLFC6 % j@K:@kbx,s9Cbdv# εm0-z >T]iŜh(f@\_bDžC_nyz"7M Ѿ_#z&I/>˹l_Q%t+Y?;$֐gE{ =`^1 $冢mZMRQe qR'#bQ(v$).)ڭKAiU?s"q@E␵s uCTLuaOέ\-yA${I!}|j:C~")ʑHգPJB0=/\yk.[ )-OdG#('6lݶ۝8T_xi"UFö^Z|vFRnTFǒBۑd꬛IcSK%}&*F TmӜu΁ۯ\w:!5ҕm!Gdp-rcU_Ƌ)04a($hOY^W5Uav"Rɟ]+)(i2s.qc\Ԋܘ<:NQ˶=9tg#|ϕELki#"NYinGwe @YxVfc|<֋9jɐĕ< W8 әu`쨀9xG*[?H=#bOdtuyeǷf\$|;8  ϽPRґ0K)¥ 7;E}WcmI#.vU)zՎܹNBa,7ѵ &J]4v?ͼ!$,Ww¸-e|θBbr[WxvZw,xVW9QgvLNno`r@CZs5S5#kTJ%D(j*Kv>2-_;yD]$Nti"EuB3v v >rzUZ>ujOX)".y!'Sf@As̲8W{VJ*,O ;:1ֲ)/QK V5c9ҁ@ՀÊj~W2#6_\dkk[Y[c*'4YmEu0MU1p"Ńs \yEgj9WG˥C8^L~Fن8OZؒ ˖dF[E:ᚎ5k@Dø4^jPy-_[2U07KXC0J~'- 68,PH UmOr@s|ZŰ۹|fKW;NH%D{- ۥ G,2n ƿm)L[^͌ f+ HF"L`ыBǨ(qr'xu컙2.F?[2VYI>f!_(l_@:1 ;ׅ Zu@c^G#M<43+UO<mxJpJge P+a$-UUA2dq_d ײoG#UߢsV&j%`Na/m< , Zg47_sf =,*kW5 3PS[gL}lVyP.4Q(j nQӫ?;N2NeTO q!We?"F2dQZ%PRZ:Tτ9-lI'LLV3d섨6<>c!q:jyy]];V9j^W}4K2ȔV^Q1GCo[$߷z̀Α$27sՐ0c_#ֳQUP|npQ8*.J \_7hV]Ś1kZ(j0U)qV{*D?pXGJ_HGf藕1`bN#4O܍)0oF{^2 nsP)܊ʤ;@'FQ27NE3ቷ'Bf7˚)n({IɁIBɱ"V i{ԩğt`X]u+qd*Ué2, 0q3>4 [5D}ou7*7ZBBΕQgY`% b&\?|9<8M1-L[Z{dKJ *};U?\ߐ}0tTW)({1a-cUa#!}h*XP/n{wψa_ŽW<2 @zH]fV+6Z!YzM{(aszDuf_6phpI䜘Z8X!EW+3#mKo>!0X[)PW#UW^kνǍfNΘ̝͍ECҽU I„eTarΥG1R,}5]ћ>|ȁ1WWX0Xl/`ςhi9jy0ퟂr76F](Go].IiCdJ`$ dڨ3 I.d>7˹&N -p!6E`xDR-z4#.KKzxO624͛_e$ڀ `n%1Z#A$mGq$.bF*|?Lu0f=@øM4z VOMxnN81,઼Mwݖ>ՀlF b9 f,v)EH&OPRYj8 [h3S~cBG=@snK&+MPOR7S;`z (41 ƫcS dD!*5rw!ķpT $>|jI90HcT{Cbo ?0hVM/\$vkW i) lihJ0 sL!tlE&lˆB[(dhO}c.$ `tϓ7㈖rDJӭ&N޴'eJݡ϶(E_C06uGJ͹j}aNh^dڻu=hnSQ";v#{9 `ciu:KR?P=km1 c~\ 'S,d9K==u !쐙H6RGN2p#("1w[6ۅqxo1CbKta-LYm#Q&J1敽|ʭjrc$z3~#/͎CpY`H&k3F~jrY;mE"aED?o4DO!'[CӠ91R+Enߟ% o2F2 X2|%%-E:cu`RAMyۥvUbi'U"e>?7<.ZRo[yjivƆK;v.mHDYAe7Z{* Dz%jZ<`> Fc.~,-*t3R8+a/鸂Il3wM:ޛ޵ƝQ//w+q4 (rˢru0?Ο>;PeF>](iQa$ޟ8 t``+K}FRҟ{y=I\nwZqJ,ul+dSSYN`6o+xx(1{jTvqqqq IlyIѧ0%3\<2dлsx[ wL>Q_9QnM^PF/ B*|Z3jgDj^h(glES!m ؤsW[>XsR9`{5- uKOkZn?\^yNBF'S/o#EYR}|ɽKn6JX5?"WW<ָN0sOj۟;'tǪl d;&+|֝j5a<'ibU0i`2j0څ_\">-&թL7Yg8U6ʃ(ҏ7}hMO<3,m`FK* hTdp|wS_>-O6)*D}>SPrfW;XؿvyAaR_~<'> zP( c1JO mv(mpe8u$<$:7=+4^v_? CSCt~\Y0!!i潵vpWl-'6C!n5FĽղN9ηy+jnĎLBPj.MmR ճ3P$EaƸn~ }nYi⢵JPE|9;{!'Ë?χ<\ 1-A1JS _1T0-I&<3Wx#Tg&*_jҐ@iuKg'0πըnejR`Rd$^45(kZWڝU7s8Fe y_TCΰp˲"(ϱ廿:?;Zjq rOBz A2m|$3MDP4̏F";v߱LIƄ+`)'L.ՖUb̅h 2ᛪXT4j퉹/ő:"3O}Km>AVBI[bY7sG|t;!Saz9rI \mPMɑfK}JS1C* g1i臙cLIg9 )N-, iePҟ]:GRrt<ϱj'/C h͗d@?ױɥccOhO1V[gnmGҵ}dJ`VN A1# ݅/X߆R-gw?<:KCk%`ÿzoP L~86NV*5. *&z XOrؖ2(^s)7Mw$ < *M^ k yJfSNqh+􄫽 Ehkg͐ב=iQ&Y];WGCt M}(Mڹqϰ43~bdCpU=,a;Us? KPaIe rkY5sޤNGK4Q@+>wͣ~?~&v9"^k?,J**1|n ~yԁ[0!C!Ow#,yڂ /W䝤ª&&@ b+FӼԏl=.PGDŽ/։yvZy Box&>`1?jyj% !גak ?)~q]f2F_PѪ%e ֮F jA=Jiq:>X]XмF+S/6J|85Z#s7>r'-HoB8hó+r }=>ZptMEsUóe>D/p۸d$p(ީ׃g$ݷ~GbZkmN# 9AN9V-d/R IEvc0saL@ 36.PW;N]ζ-J!2*K' {ߙG]'XiNd=,1em`fYW\r\CK5JϽ{&n&Hf[N<yܟ@- x7vz!V=}O2G$2xM;9P-lݹ,. Nޤ`ph&9|%k 2r4 ~.~u;} Ýf#ǕSy s(mFYYe!=w_˦@"Xf)2qY+Ul%rz/^vوA0 H(S{" %-pKʣ;ە$u c\aW S+30:2]tJ /ҩZ;͠+4,AOTB~{¥5qSpy$Z?0Rt\J&VP"Ć v^ị+yP=` |L&Xl*Ӌ*+FClN[} GGr͌.Ak6tȨ%GysM>TtXn}_i6mP'/mgi 37)@0 ޮjOh>qKר& e۴S$?Yx-|0ܽ9ź"C{'Lqhi0OnˀNy@`-e;4B蔞[ ӿ%g%g}Re'ҙ2$)?XKcqF:Vd x-pAiqI|,ƽY"VREw.1㗐?I2wȟY[!5ջ7782QoA_MVI|&Eby,2=O N랒:8s7\a:vQJR|X]z7sebvm~GS32}Lf˵2!%| +F_'KPYHYJpЗ:k  sk*ZK@+:ǂ@7hĦ)1e 2do\I zRKP00s &^9bs 0d\N( Vi Ol޵%SEQXvXće;6ϸfWXrDp|ma{ E- A#+?syc\lDWG "4hHl]*;9HX: ˗o2m|I$*ߨ(f]*pC[Q#JDEAmktY>ӠmFo޾?CTZ靺|fC"¶ Ӯ!8飲KrBj#?)6mw|xB[Yn B KwU kSO2xd~yjܐrr1S51P f]Ј=e!-Ϥ_[^K~ Pf~\` 37*0#i XP#"-]_ELB}5c? k%S?B'eĵXf҃IosYV$˞yې78ts1p7z(xc0"|H@x9C230ʂΘ)czA<=Ꚓ,D,7 ą}PR4&'"-OD' ADψk],(Z{+}H`J#ѝK!q9L+kqշbђ#yv:O}鉉C2xa]MN\ 7O6I0yۚNlLVꑽ܏j[xnX{`x6얈Ҵԇ)7yߌiMUcx )ں)O,S²s M3 ȹ*Xu`_ZmaHSwۛ\?2tP: >l[~ܒWJ{Y\hجZwGgDظ(b%>$r|~Eϋ1aٮw:^p.&ק~iN:!crMF% j w%Tإ!M *sH~+BUWhTxh4 yr<8#+ 憎G(]mٿMCI >a@:~ØarD}T}J=?c$#67R%oVVr+Y}d 51{-61)WMupF.妖u2 Z\nȿm?(Ik]2[kGHkMcs\?-b]3rR^=&l&ۯ! > )^mXRu{$>$ \zܭ(jԔESGĀKwwmZԿ;acbrn=Q?XʎO^d~" !qQ IC?ʔ8T[NJzBaDbwd?Ǜj|/ g6)/;Aht;S'7xN8CgZB BήQx&eG{١"l|bo /]Ǩ6Mi[OG IkzPY]sRJEѳ٥-{FJvٝWR2Uߧ>{:ħvQgxػ)i%̛4imVQn-X7R[Re[( ٟkLNx.#(`nWư\}pap1Q&!SHOMR^.29mފP3Y O׵>{KAg{Z<9pX{`JlZ0zPiw#3)AƇ.:gd7xңsX'UzDuۀ11 Ie \c^H !:G\l+r=ea)oË{n!do5M|@@Za-uLi1`!!Rmxn>1lsW͗\ ΋olӠ 3s]4E1pOUw߱0l Bk|K-h)Jl#TgAf:ӣA~JV1HqS*[gfOY=k7+F+r]̨1#.O) d >r/DC\BaBRZĜݻKl&&Kb$阫p^U®6y,Y vɇ ku_5dyZa.<@62xVH )zb'>˧Ҏ(Pj^:*M~'xjxCD"XSvEhzfGND(;nC3LEqu'KQ*AxW0u3/bR9"ܽu:#UGg,<`YNl Y'`JD"siapo`#VOt]qUysH2޽GDc)z 9揮pzr~,&>^-{DZg_T.0qyo7ns NFɕ 0> u;`ی&! 7bj'B>{:^j_qmOpEJ[F~V(4 믖mI/dKbh]«P"ن:^rvse(Y]2sIBByׁfK*w)5t*f|!l5 ŝ\y:6qP,B2o{$HD `O9pCr{1Ci:G_??jUE^U+NK/7oVr%aȊn}3&1x|>+9o[k )$ΙaۛS{D!7vB P"DǷy.wIUk gM47Ӹ]0GЊ^nfS; Y^ي4K_,lPhuy}N7y ` o$[{cG$gz] [}]oz.=6uaew h H]s Sc|)&+l 48 $G pRM,;]?Ě~ ]"z$G;sf6JPaU)sL.l지xdB[x@"Ӧ ~ VrWJ~_xVMqW?B/y4OʷyDM¥hpx%ku9~Kx&"I'&_\訆s}*5g3vDE<ήYbN7^#RόD2'DdWMMI4?2%΍Wp$ <꿵]fN=ǐSM1L] 4.? nV*C4#mao| vcu6 ?&*>u,O*)qMfšH+z#w;\l] A$C2ʗN*,^ FE "5o%lޛKM  j#jOД\_> g wѽ>zGfH~:-"omEྣz*7tvy"G⁋ȔNJk"Z$ͷ߹LbşFy!`m:tOO1b PTe$Frnj@ìN֯1f^W{^^$7GxI%DT |X%Ǘ7C2̑,fvƜ=H@Θ<"d, ePkjm{N Trzt_5UW̦*@R;7n K3жEt@B?)?}ፄ8#87An(6FOK&ŬghDMtF+8$mRJn&xN$OU%?eꌅ9i%fœ#xԺVnH&ܪ5Ls AN#Y+S'Jh{v5BvW%:cZKA4ӆ2IX G𑬓G 'bD3T"|?(,ޤu`Q:TBHxA ҚQԒ׬Xf沱Mwho\uapbG^D%T=l?ԩ$g)oqڣAϙ3̟*ran$v H0 @$ dv)cߘ3T(~NArh$Q$>k q"ԅTtN}u$3b{vlzn9-qHov-w1=2GRPlvL^eg{T¦}S/q d )v[OY{TRngu_g6&'sAҹ45 -zԝq[Pxy } ls1yE'̎VmtޓXܻmVB sɚsex16nJ:~?^N(L9<.e;yrW蘨R44{>X-C aҠ9ݷ€>BJS}r i0R``ۤBu.>☖7.FuW<9"He8\I n٥PPcsT<Ia>)zH-* Ff@ j0Έ͕=J26R}@D",ª`f TL ۞6rFH yM {YƋÞ~b; ^,Rn?(SM !$>Wf8\̼{SA}Eh6` s^{\fZZR')YDsd39Px^+.@PҞl;'IA|YǷҐ&SS0Z41 I?vE"B7p':2,GUKޢBQ.Ѡ&=60Ʒ'D4e #ZfMo2un>pZ~=dX A ]Ѣ#=^?%"kآV!u_h3.A~&7C_%u B""BKlNkޘ/s*p9Jnce DR&BgA鱟D;̸R9?"%4/R{\6,{a{ȗ3[>ObXzH;&w~$֌J#pH;98c.2vkug Qqc &8( ◁`Nݻ=CyZG(' )kS Qww9)>L_%EdUq[Yvf,ѺR`Yfdm\،LsusoifEZDINzTksoIN|,XRT }Q%NDçn)K|?$I$PO\#̐sN d/&vwvyVDEt~)ODd"VSiܙ`BZR-1kO63d$5Li ^7q;H7ۍ$6 1+Qq]df&rY^𯸥u_鲍6)3N.e}>Nx>Pݽcb4[<ڮ9uvg'j< #C)s *#WS<;W\ 'mŰZUBCL|Gf6ɜ=x7xjP~mjst,I:M;N=[;A*Z`qd(fgQw_TYݣ6(!AE~Kgũ" <NŸ$v: * $Uc).ni:^q0z<.}tPB נnO ;TG+lYqrHQhhi>%(N5-LrB(rzdn`(rkȘS=k]oJ.,IinηpN5pU$/KڠXI;l?Fwz^Age")GW#gwu0`YGe@I\܎ܬ>,o)39$_@ܙD&@(븳2j_Lq{#:jE2Ԗ@13ٵN"fEe'?6ޏ1Qoun(}vT 2EXz>ܮ!~KJE)aY $F6Ɩʮ,.>dB@QKR9n~& k~~:h=7-Sy ">$vd8vӯяݶ{v6)({+2gZx>;Ybo ϨN%tΆ{NŞqf|t;?M:Y㶂W"ƷrBRj< ^ :glS͹]R!x΃:샀0̀Vmb0w$B@svIMc쟉fȁÄ ʏ>LknH[00Z# (h6CqkZ<ܜ{W\25~!}H[ }6!V XUMXl?cC- G%n*G ZfK ]>b;>!S 8M(1,4% SҬW]Mic-M>1 0is%A: &YSV>a4ppU0Mbqk?PQc9` d=P:7c⛊+nG#C|ou$%1!ӓr,gC>)wMh`p)q,@N}Mj7֋7nc  =͖Z^VjfP 8O|WFf.ȐpGƆޯ(jb"N ߌS:?hL3TغЋ\(,((1.\f@((f '(ވʹ,Gr9Zm 6\NYoγA1T$o1i0fPP|YVkg''+5W2!urO(~^̢Nz ^U+&CxgYHp5\ttB0÷97kl!Ϛ,= _p$H܋b?/HM—j!i%Yk; ::5r-3icܶ kֳ(φ)-13dCծ$w&N@Ten4 @ 8ż\LP{\|^NbHGoix~;])m|0jٖM+{kvqzv /qy8t1ᨒ#4Rrbp6H>W:dj@5K ZDiPWjG /~J ^j`]&/r[R=kwD OQѾ ؎.9x}7Zcq3k Ûoi;1W)9\KV[MvĕDgIm:Yכd*ո6\M嚙 ΌpM@-fj֏5ZZ72zKO0Ʈm*v̲p3{hW,gwS%.#?C:ݻx|9cUD'BI! U%Kgd[,e!9|ٹqad.PQo.Xv֭Ҋ)WutgE"9}W۹YOP`aG*$-E_Gہ GCD#OcͳW'9fBKK xtWid&v{js#p6rE# 8àY I1x-;F]ȷE*et9U[ؑ*rtOjhVג +IY$ʑ>OԮ>J\ϐh МOdY%*u%@ >sHMz4aond}X~:Kd?z˪0=} knX H9pNO-ZN<7sEf \t;dJUaNDc@e":n +L`3A0+hZGe~" }& Cm?3Sq&Xю>Ζhi}dm0.]|{1} oEJ"+A-v`-~bI~#Xyi޼ARka۟,HIzCwV; S*Z7LAx!8%NɨS&QR4ڥ=矪1SD>F,[_87~Ið<dɰ"q{3?;[h@>4Okݧ׉r 6xᡑ* 4e17\$If諾Y95 R*jg=RBbcO9 7u qiR!$:1KYԙdzGO3*3 Ig!%J(Ge2.xG[':٢XSA*MggQ2wJN40 h}6kn>Kс1B(O-61F!'.aMք>s,ޣjRg*$K.B6[MyouԆk@grpBՋU0oFr%z[k,Ս$:G<&5殺0PQ1;JNFK_(*\i+*:-XX}:鼬kʆ>/8ѿϋC=z;$5zZأ'v@ҏ"vL_Kx0z)lſMl@xUm4QE+͓̬PvKO.+;pzi6gBҬu}dn6w=┸i>s#]#.!LbY8hK~¿n:K ;9;\>^rⴃ!o{V#oO$y>B7"Y@73F_X%N|ԋ1 keq/̬.%Ot$OMB讧cVH+x`Ǜml.0GcQ pGa[ Pb AB4n=xr\<[.e04N={ [|PX6igL&q-q.p'C޶uc@ml,HF^ݏTYh;P1N,E+%TFMrIM̸hܩoeR *-Hlu"I&|r޹>]˚F^`f:!.2ߋ lj S(mB'$ ḘFZŽ u/`@'K|KX=ha:_b5pS39ޗB‚h0M>I+ve~Jn09rqrg8mw7`Jʁ]KԭB{%X1Ԗ} yx@ f"x'i8n*9军0WL/Ov]oIɇ#6lΙv-1sn8$:6TTA:F/VldSJ4.ΥzLkPhW i)UNE}ur3Qjs^BPݰjMI!2 2Nr.Uw@,`8fa,p_'U[ChDbӎ8Q5%tot80֓"\uScVGnEbkM>\, EXg8ꚫrm @x)v"F7%,>M~"Gd渺8=KK@]ډ:j&k=,,vaǒRpъy) M?Hdb3 П9Su~6XO|^VƥRi|Fr?SJz7 DkoZxOQuv&ĵE!Ja_K joBM@[bY,i/AsnM+K `pBJ$Cm}^wc@=,&~N'֨-6|/'چ^j8JQ8V8XgNC _NXy<=PB~Ј U:aK!+5nSH/S<#w0MPEG؋Mv[sV&<곺vFwz-5D8Pi't%C nGZTBəG7>"+"34> "9J?J-Gi3*v|xFP#NaCe/0:ާmE< $!cRnHzw[2#UEM Y̲I% а`t :AZgCd}WYk%S9,@;nЮjhh h3czkGxy>IG.Y6gה!떍?y ɯcѾ8:}8$ՅVPS9 v3MRՙI X]Ǵ<{HHWYб{uG]EImNBĉ /Zz:dzhǥ<Hm^Wf ZڔL.PiJK#NRC)ɭWh~dچL1"7*I%a%Ą@Ǻ>N߾urW=XlqG7>#v ?0Uz I7td+R#?4/zp࿟}j;**:GRTPBG% C5>g #MX[Y\xe3BF՚O9g>vUpھ*S(Y~ic\d$̓B; bUEݣ; {`>fG*pՙ( C[ұILR!9UI&єHf2a(/4 Sȅj~'>Ľݾ}3$ʶ8vCKgjW+F\R+@>JJ&[ٹ%֓'lx` \$տ,;(9-,&T`AC(yz?Qd'3!k~=pXZ:î>nys~lKYm yOx+*&:ӝ":$0糠muLQ>9̄dyPob,q͕εL nnZp~fұo,(ƿ?TX$͟ňG^~㍈dXNJ 7"&8V|(mSp^lT ӌ,ʣ&>SjqW%!#1zשkӉl@<1]#KATK9@†,]gK,=(nPvT6 hFMcRCڄlhE.t&ۉC6s n-ӗM*?ů-ڙ ս9dcST%6NL{+F~v^:FnJR lI*@蝡 ;ɭlϒشtD@jzAAW.wt  D!QӦyvTF~5VrM_% 'i  mb傐𺓌_xx#!jxwX-(+K(6RW0IWk56nl .W+3LSf4G8dR;\bI7\._ZCkBF|nbwt3 i3D_V\ꭟK{@-Ex $jVׁOvzCi i:GBN} sF(5 c4AP@qvJMRms[| lfq *IFGBkJ1E^~K̇#99)< ObޣJAv6ړ|4h dO p9㭮-o1^ 8Cwe3 ̄[*n , YjR=$V9:v&Y3M|kwNpLA ZfӼ78e~Jg|gksC^0ɄNWe$\Dħ2ۗڡ1cjEé]1[S+Ƞ(K׎MX3)}}zYvYdZ"˭rJy}2YVut:ހ#"3K|҃ ql 8̯ۯkׁToނB5kM)GaHL"2sO GD吖IIJ:Ae3#Xmzhv8aʍL> R ~ TUw }&ͣmS;LM=F@zAF=tӧ(d%|WMVsՑC(88Jԣrz‰ܟyDL +aETEau,8zw<=g !~T^\Ҽ9cY)\Ig󀾛6 '"˥%dm#^R\W18|f /Ua-?04 8)(s2yq5^8fE ﰦY ;n.X #mb5A_-b!wPyӁNll4p&?ug G2Hj2z7*3pne{,E* >ퟞhi,āXr٤soĈ/&I g:Y)0Ƚ^VwzrRIqҩԙZf;".U˜M,ng`| TIfde9) \~[ ߐ8X/Lǀ0[L2 4hnߎӵ=Kq#o~SVӰO|)+bz3vȃz?t%SR]{lUՓ<&# ex -a6|zw8ɋo5y > EY%.t[YÙ|_^Z%ZZ F-Tn0?hWZ컛'­DGMYi>Ծذ56[#DTj9fXYhJPd$Ϣ?OdI~?! SX+*4d^(c3D6 Ioov9v eP{E4cmƞ^K_c˝񰠤X/̊OY{m5^n#ug?-|KmD|waD8uh`Qj4*$zW?:Y?8K dz [;ZP|<8.d+M^1]ٻP(Ktݢ/zȼ]QZ%5'wD`n|ɬҖ $&eqE옦FA#v7-Zy#Z4Ĕ)ou<&<BL8ߥ1V]ꝗt$ % 87j<6$s?߳fbU͒@pL# @q:qP1q'D.p*cq%'fSt3^]mdWmҗګZ~13J2SKp,HmB$B~{nlڊǙb`rAiGؿ~.r\ݼ>"/cqaat8RSkHP|& CV{dcoze/ eL$Åo֡"%;@IA%/fDfVyq@BB_2/V;梺nG(g?}&ROx+[4︛Ӕ)nVРA`,s2NN)M.7TZa}3SlT7U}_EˡzާMCs V1ňiX,oҊxoEW߿a;A.+؁5WَDA\ |!+Ґ0_ET;=PJJd~ۥ7~.[JN(6@vW|=&5|H>xEq߰ݴZȆf}T ޥؚ mÍWC=~[4Hc(j:1,o o]4Ţ{nVATZ גts{CRM|3ag05ƺy|Ե0K[¸B5δh_:{7䣡'No~_K}=;Uɇ~- 1KDuXWIr8?ޒrE\̬xQ4gNwM˘`Jr6OQJ"ZL^qлa~:/Q Ce\z3NM9Ɗ`vH_bns'7ĪqѨ6xcsweX4/<DC7mU HXH )aN/*>7u@4'`6ΚTSEc[(qx(pY8Գ]/`;w,ME? ZQ-.59֝@L(MFq. r0x%rY!NN QC7OZfЮ>s=pX&)24 2DRR{8b}>jeڬh` .kk[Cf(ڕuDdԯ;{n2RZO]^,n k ܴԶK6prŧf+@dPZ]h_Hΐ[9 M&ʹf#[%ʹUS=HaO@s8.EskMK~ۇ"ϧpH5;^cAqV!(5Eh슞j=LMl*|g.(I>Ra^Alj8K ([-?;$gb;i揾Qhp}_üqhAP7՛wK5ˑҔ5ucw"ĝmB̓2tjWe0Ͼ0 29P.K.x+KoCov[2.s;✧>/^\vR : u\ujLbX+V7gn Hj&} & t2K$@382zy4V^Zv)ΧS4BkS2ܲރO ۛ7|M*;[gxe~کN,,g`t;f0 *75 =O^088(Czg1w~d!1Q`lGT}rQڇ2;FL-hI܆{v[׀d 6 &A-l0~uȈ#2M[ "Wq$ ;Y?iR_78(ZŅ"r29vKLogD'RqLő`zMU҇*zmWޢ*uXpE@`@&Q;g`JzalrkVOa "/:TmUHcQhH]ZXnd?cpネ'CV v.]A )Ct!;i@m<ָ`fGz?v?Ada |vzMjz)Cc;풚p?cvO\p@N Ya>'8\1JlSr'V& CαBnJP^c1sNak Mm,jj^*." BBuHcҽ):jУoADMa0PIu0 #^z^ Clх2-+aY^>#hbCLpʔ$(hVl5Brg>e|R&So>m690CÄ,O[Hvxf3IB$A@5/*ΧiEX27Pԟa8׸"J؅AG^SFX-/oMl>mB~{hrЛxwӀW\^lÛ=T,ػz!iAVf%!1ɰbVX"GR){ j-|%mqB+ԃ[ ]fvN ׳8bQmT$sl67!JK 9IVkBM}&|p^9Yp'3d\z"l #SoC^ wꤌ$U[]`#Cbٓe{yO^MM2y~&I:܄Pk !NNԾ|Hbs˓u 2K"WaY >79I}}e%/:'5[˦ػb{&pn 0 $5IVjoɇL<'/?yӎlO^+iɣ\,u}WǽQŞ %]GM} U:WVtJk&)q˴`[LWy'0<0fU?Fće*MM#x;r!?Np"~T%HekJ12鈫D(L !IN{YgȲn-'ම3`% Ѓ128Mb4:GĶO;h BqPeՄf|$Pٞ(x oeX]ǞjJB=o] v "8(a2 `m3d/FkU)9e/^(:B#1 +<|CfG{TM.] +_%'6;eSdcN)_tA Z[I1;} eVQuMNؼtɋ;k3u*P R:e h??Beِ6.A7;%+$ g),a}h{m5tcܧ >'闰qRiaN֎ܙN`6Iq` F#hkqm7 ÙϱZE(O>c?J@oj%7Z ysRh[y,GnO}M hTkj&2`ŘtϽ3.`L֒0@L<><1s7xWi B^T 0;Q:d7\t꘧6G_c1IV̱މIȺHA?/j$E[$egKx^'@oEq' O_m _黃 ۍ} /LawF 'l5Qi&]eyW4ga"*ZI('wZ|;ϳEH^T$ ʍ ~nf?x0 K\\):1 IziލZCh~|A$Yɥ㚴O(uXeJaSéiI-YjXv,.ޢWx(lU O;b8(ZK L ,~f}W?EY;;l0 :+>{e¹v=rYr0~!w0 vwOTvP~Ul9Qצ $ZInnG37hSo\9|Zo]Jؙr _0R)cu!( D߱}Sy41ڸ=]l/ahT!ƙb(9L]wvTh"fgb~<@Ps%9[+Ф])8a]7UHefKVcaN/"9CfDF,}CBнmFPU6M<$Ɣ?kr>|(29N6h^IԻtluTe ՘@?ƵE$E}(R*KװIS%MUb J ,Vjp'݈T+%7KmƁۢ$nd 5@y=i8[yyVt}YwjPkm;Km{+x'%P[2KޥIf d>xb¿u^H. 4v~3+16aD{ HK+Cb!dsڍqLEMD>=h3'RF_Wҥf! vQj-&пB1h`ܱLc~1t6 GF{]!h>u̫4$`.߭bnK\~*u8)ǐԵ"IUCSR\Z΄bq~)2=ݜU%tSTA M0>5y_(n4]@#Wߔot E"v.p_5;Tq.G3P*KkR)_d|2K]ˊ3|ecAZcz> |ZJh1|n>i:);ohaGta #/lUr7u=sDgJFU6rpQ)D&R[]b,..\|*WCbK{24ߚ.$x%DH1pW5cC l7KC\tք$0Z QqϨB79[vO79Lϗ ^#2ME X8%a7Yvl #<9_ʉV^%e UFƘ ~|D ǀ:Eڙd AJ)82tAU@L8@E!S׻Ru^I_i7дj%gWW߮Kx_U8*/b.aY"))ݽȎ5e[$MYL2(ycր/N`j^r6y͔pͮ+N^-P4Vv@ѫz mYT=ӳDöctTQ H)3Yt & ֌A*Z?IFğ~܀If۵0(㍎noPc djyIhyfq>S"`.fL ۊojUL3ͺRk!F&fI"fŨY$$|WQq[ V%ODdrCGM$L{P6b8͎_8w 0unp' NWA"VUպFFo=-C?5EfKw>qi06V@/?2 .+"49+FL* Ene [}}u6S*;\42H)s2_Ɵ_YDX,ZM?[,C5zw꬘TArZrf5FbELf(U/LgnjIћ)c5] hJUf3@qp*uLVߜ9@aLv ?G+冮J̘>gQś&l҈j#T0ܦs$6}~UJv*/e7D?[O7o"vԋ:b7#8yT5ܠ.e҂\7&Z2RBem'6;J5DwGpfSZZ75y?&|g7ZF*U;zڞR&.X uogV=f UwD*-pm Fk--eE.A'QԲh tN̹ _+o^tGnY#&#X䈴06#:H#mǘpV* 0= bv6m;~?P&5$1ЂuZx4v ^^Ba5M*փN6\󸘓ba&8'`.X4:FԸv׀S*x=>8'}׶\B|So2.cT4~z$0m2eGKFrh7؉S!Ui t&)zN&Oя@SK rS1 ESd`,싁, :| &-ܙTD: QYn]j6ZOX9EԆ;g9JG |Ps~,\r͛ɤ>CjcSa ydu5a`& G$:70| o4)Y&A/hXT> !oT(wuF_UMa;Δ(Y]?twwx? wN*\7BP`LWiQ0 qd}hG:%YiOڑh{r$3݇mJ1+:ð◌gWt_Y'遷 A!TtXdШ<_DʫLiu1+8T.+kKgHyiE (ϧ9-.p]p0YrݎZEAf(~c>dI> 젢 /5yi2:OIT$Eo>j\-qLQ(YasF]ɶ]7#rcIҚtJLcSWUEBh PLyu*lBsđ3rR53RLEħןz/ׇ__xY)WYzM7824\|""ݗ~K&>|:rôzƋ.,GL)~N_LˬRW:VIgε')NyM'B7s\~N6UC%=S # ҦcH:l OAfw8 RFCa?98c{m+4|QXBҲW%?]Ik!Z"eE;j Sl;U[ Qh5R|(bѧm3؟9bEAõZPNbV=gPH<'Pa+JW#H.$3C^^x:q^}[ [P. n^.ܦwE۷Pm,X+"d>X?r%ۺd"Lr2bY+UWG ]|o[SOw w,F];Li=믪$vPOGݛ/D&iW/r_Qc)-VH PKW9׉6}s&[*$gT jՁ%֙Kw1V:"#) ΂XN_f:: !x)d7hH,`qp)?ۅչ-U-Qhϓ^6}@|)G\Ac1cN]"Y|E'f;͈ޖV'WU`xE\߂J]<jHI\&= $A"S,J̿@87aٺX{B&"w1? k )NxZen$_JaոqA98ccuɇ&ؓyvA0Id U;Mɀ k+F ]^19Ԥe0{cSE/mPD ^- }MeR 5[EMĨ]F}RAy<_UeZkS/BLh[ًm]zQa@[kMHqMl7?+l+pj CWK/}sRzw1]eJgpm `Cv_ ksc)jBOxvy%dV/,>d5h+d/0Z+ %@a)*-xP*2A(L ֛qh_Lbtzj꒘}B~_!!IXC>A;ED CrDS#E7f6.hu:Ikd{0ўIN1lU/,lq7/DKbzLT[b4HMAy-iD@s23QK0lzcPA&OYnxADSot[~իh~f5ΆcyhJ(f0}͆zrv[@M?BlHܤ]jFѐ`{ 0ND11!< }K MpI؉ Wm;<9 -3ɗ@)Lm>ȝb\G .skw*۬p#JSoƔ֖dKFSA)H,m0ūrLa63 O`  +g,;1 tVr#D"ht-a}}=goZ%a{Z <'9{ DGDBCGjȶ02wJ\.nȚO8E;=3|O:y+_>V^? }8p(!e[I:t3/ѓ᧠Ҷ[x?c~/V΁LSq|AT  uUڑ:tEݩ{Yc/J0zN{ͭ3CR"[*ݻA\wM_<^x6AjZ ׉КBf'yE|P{H5=5f+:e^ku}h7{jvHc*c:tSv)r+6K_&[ 'wwѬy>ةn0JA6JK2q+Oež_8K!4TKTYn%uˬ>T7R!hBqy̕쓐7 Ed3lΡE.`B1GB5yB*H'RfzpLsYI)E4X&igBKtzwt܀aRiրMb(2U4_nTU*0g Hy;S+^Πq#IY[ha; ǽReXK,a}F6? me91*c.!و@6SblȠ%W|qM 0SzV]m><  +87,M 3HX+sۼ]w"do n~@yᘻ5o.߁ᣡF-yzGК=ђ!N/o==& ɜ=D?g?r7rBF'<_+YKlϱNE砷:bWyWg<N8%mcޝ$Urrdp@$XmiW@4sg!V5t@g-AEE+Rne$h?#Y˱(.#] ApO%th7LѢ5 ą+psy3u߁Y7:bD{8*IIVZ ?{hG !(x>65h5GյzU#ĘYlY(^y޼?Lgavjb]e!CPh &X7I7c䌳B*nC ->s&ӓ~]7 -A@fPCT={kQm8BuGm.$9’#*8d`{dNmit`6φ>З88ji=uyM"vj?5 ٱRHm%R$)y耯t/'qpQpx|6o[|@~ szJ2LKJ%rR *”Of-k ϹODIg3d~s"ƒ 2XkS&T56U}`¢J>l.(Nov3e-MΡP+|wliә[8!uT޾Dc;߅ǯ[IJߟK^nFbϱr̽{?.4z5IcY-=)0LH◅6d7҃gӔ=78[s`8pLf VVPMnW踍\(qB%:,_`3hnx6]I@$qj†RvɱٺRf]}. '`MB~&85)%5RnIfJP $xigBgл5OqN&auqGž2d < E~G*T8aJ ( ovs /S7pm?wPǖT=Qq:dd(ojG ˯݉Hc!e"[UG6 ~t:p 5s Tp>\z˷m> oyϏ̂~p2 4TɺӨ6#( aA]ՉQV/[^0wK+ĸmbJg`[;\ I+8-MʵGAU*8 zs M)/H&qH^-VlAY+W='y04L݁BQ¯cBNz&r߮K7F3 2yrM?df=G^p%0}eQ!WߕSuaCxza5Fxhe`ƩP]k"uL='N b撙f{{L&\G1"4\CqP~DqR^ XJ QN,32-ľXoSox%jo"y,v!eE~KgS~k79ZR*ՠS{XS!'iVYG%|@_5rc`ɀwnāl~.MFy*Ocb3ƹ8V,Z˘l' hȈ0C#4\DAR/%t3t ǟn6)K ?.h"Hu0tV%6Y?J SkjUZd$ :G6IH#}j\!zr"ak=Js1P6lQf$e J+,m}z짌iNEu'~4(/Ǚbةoo1q>X2 d&kcU'͹ND {O`,w);m+OaY g?6IziuxQCnj2f]1ɄV2abݧnLYs#%F4QAZK_AMH߀xqGfG{{Fiy'r[L0N׉yozeTl\ޖ`Z, c) OD0S8BEuݞ(ǚ{=|0FÐ^<†pu @Ch"k ,^r=(5Ο(پch6XP+)Wu|dl W\(~x0H$"hxmr /.lŌn3ǵ| mU3X8;{Pst ݎqw Q{,fUx i d4MW>5/@.%TsDaPNHrTWSBFYf'HN*=UQ2#[n&gcw0;f*Q,j^>^,~*iNZ.wY7#ԳC8. -Xi#5%%8̘p Pf$A-XUp=Afd}sD5d^+'xIR%.?Bc 4~1:]ﮞrf-b|ׅ.bı-! %LϟPSX]+j ]{τAz>]c*2O{(4w]Jeh^ k`Fb NtN>(V4ltBQ5aRЁ\L K>FM̡1$@l=pB=1،%̿M"j)>ev_}7*'ҌX@`LA֤Bg[u&N_ɿq'h_=LJ;1ӟp^-ӹ>98 &*ДFwc% 8߼A*Js#.qo~aGԺeQv({K.=|F@djϺFa '߶Ov9tg'"yb@S7AubGO3C{/6a o:T\䗉|֮.!%.wTYe#RRSD]Y4T g\uQ!^uV8Un*Q֬$reIC7P[@`Pg]1榊c0WmSs!?hev~g1X-ۉj6$%ǽ.؆3z$ Z>c{b'V]jl"u aXz߇r* vc6 :r?x1 `q)2IR!%:9Pzʒ+4/<{ $L ;.ȸD"G&C_^ "|hG &t@؎7DGrדEy]è>^8zL{LTwae[ &D0.M9Wp?a] SQ1LW뙆avb[1v?u'rEPދm!B14Vm$^T,|U:ɚ䱗ѥtAv;d&^y5('5sK"!1[T1 ΗW힒Pa7놇IY:h@4n8ey~/}š3Q|bd$.1t@k9*lI)J[&)/67 .M`s AVek[C1(%r]h st Eo-/˭^˸b.)dď@0 rWSvMp4 1` Ss{!*l0l?Zr3}7ޅo|8q0^xԻ 58E$F&M T ϋNoF P-C<8R`DMfǽ>=鉴x 97LSMj/V~[%ꎅ5 924|~2ߔ:N% k_.Zp<6~6S#92'Ϲ - <1lLjsJ27Ql|2""<|xboj~^E6Re8AsGuΈ&@5!L̊qļ`?jMa.pT {_O1Ш0ui'iO;n vbp{za' 9=]2=d쭮Á$P0 &Уk - i|G{VjP-%&Cil nªJl]Xnͻ'{xYr|wNH< \A$+@wC .JR`F\tySK"ז$#[M2[Y+>7aXUnkW QIPJ.p3)o@~}1R'JCC9a~ډA &bgK-(33`$5cМؿ1r/7cb1LƅRK:\")V7/,dغEb,4W#zǿwc^^ʩڟup?oG WK|А7~+ȿ橀j)>O#Yh]pI_:JZK\]I~L$;zCxİ(80H_vi%QdA\ŝ1K*F'Bg{ZgZB8b₀n,m?-*?+G\icfR)jA$"{fQx <-_Jk ^q56 yYqGd{Y$QCTV`ĶE[ýh[%}"<-#3y?ƾ%oKE64-yi %㷤UFg{s)&i$"Лx0 )7_M%f7/XP]̳2` hɅ #W[vlS M[R(q JK$]3c8 sk >բ,ĕ|KK-X'?fi!IPƪ *邀'A".EL{W +FK/s DZt:F8̿ Lo+vcuso}t26ڙ8*Y 9M׹Ǩa72hǯm8:Em:GN5Cǣ$ThϼM@L$ 6/]q:*/6TXvQOjx$1彦w oWR& '9ʊ/?5%xh7;Ƨ?i_Օ̢@}HP Kܘi4/a+s;axX:hdXGg+Wtȸ3aի.HIqc~LҕyS͂ s~`biGX\ۯSd sj]4.fRFSFjazl*oswN/-H^kav»`1u•9CMx}I.JxGpn DNP^ |ryTlC $fd_T̑U *4M(d1^pCZ^Cvd=OG7H!}B} W\ګ$1c`G6=bHD!5a?w,I*"T _By"ccX@Ahl.*fX^x~S11RzPVFASzQ,LnZ.IWПF Jll\ڎtcbq<Za\Y6QWAՕSȋRP]>㹫[K Hc(4cpތS6~" F!`{9 5'"Ϩ\DKbEm F>B%~{AUڣ^u?ˇulrl! 4DLg1ms{௪fEۅ$7dxDkI4ImfoGDE$$5|A \LM)/|;ݪK+@@6Ϳ`d^wR;b!p$HSqlC\.9'UvI$ a|70}>:ah_prpky 8rD=a*8 CkB{sYOoڬFg n~^pV׺u2sCXIOMU|jxAw'KLg䂸3 C&o jf+ tyfV<{<|d%esRBV8LWZEij.OWOFf@XW8By{Խ5*F} +7A|M=?R24&q.żPdQ=4{ $+2f1o&kTt0' !ߐ9ۄܛFː_xj}G>nx.Pשa[Ar]-;_7ﵤÕk89 u!ӨOȪ!U,]G{Q"I?+F!* s娮u]FhT E aԞ HE=dtq>ǩ/AVE %0Hz Y5,Op_fb[ztas6%.x 8&ޯlYI)ٛXVILuI>BO*m}Z'Q~t5_(/7ɼxTJnƟ7 ={ .fXFfs  -܆x\.#Y:cH0(9t9\X,"DaBi{D=GX݂cK}-/'{^/ۜ-n ѓ ky0&y{黆#V>TugwG15nZc|*E5J0 0pk $Z5eGFgh=4E"It ;[Xd"9ޔ!]^9Lc ݢpf(iEdM?G]&#d7AՌ? E,2@8w hrg}bSfmJp\T5BЋ0^d^l-gq Wt43$)YFJ@sR͖U0h@1V~0*|kstuw9#mLh{zt>C}^%((/Q0pN-CR3?1m+Y" j4O*( K Wa*B<{c }xɐ^VaDB_b`< 9~If^+itB)D50^~iP^<:EA  ۇ#Y#+l/ :4W;v1avM/YFۗu;$X*+IZJ+&\OԸϴƁQFmp)dWpV:駱fN./]è >5fS-ToRN6 tx+zյZixzj2IWM!\mWvwk$$DyH},o _r(Ks;az4}{6~H`b|`7YAp k^~" {'1_9DsI"/ F>jy Ð^šwXA)(I)ynuc6\#ERhnjȦK;,t]A핬4\߫2QKdž0~y]$Xj刧cNw+;XP4Ɵ5qU~OMْcVzT##sm)ɬɢxw T_5կBZ`Ziʋb1ceKP?=yݿ"/PA tEب)_ɧ[fZ1ѪFZnOo p6Gt9>gJ&d@Ǫ0%?)]8wv 9顎'Httj HMÛ/Ae1it3+>Ϻ#"j_5+.Kl, =P"Mrñ=DYutwQ M*r@'3d{iXy ưFMgLS)^Lj̦^-}.ӎDROuϼڍu^tFe;2PHL/>5FFiB#sGkʸN[4i 4oi +b%d̤0rnlP/%B:E99gT$ 7z¹cRmD /mXd N$k7݉]Dj&'hI7&l\]d>y&X/u^i@-8 OyIJ]6nuGot!̲_7R147tEGzUyx:b< # Ja3^䞝xQ87b wg4V; 8!fHvKw^y6`F?Tܶ+T3T"'9c) k_ Y_y*I*A7܎JWy' KYv$ֻ|qYUdL8.:1lK^Q.LNRã{|%Io( U;D_mo2G=~^}?E#. ϥw#=f"jy'2" _uv̱4 *`-n%sKAPv?,L1;;@3!MNum$^oVs%x3"H76R?&WeZΦɞG}=[6 w!==T~XZt)LLaw98D̬:أCoy@(pctŻ'd7d1}`^rɻQGEOB`m/&7_&ZgK70C\|OKؓYoЏP 3DVHbV┝' &&rfUDU$sP3r0߫`&@YssD hj"Jaxc)i T#y*i/vpjlmGSHW朒'^Ml)ՒspVC4" Wd0u.JeG뜯zV,r3]XV8q?4;hGa*n+ǧ 6%<j61ێ"ԃ0o[L\ ꤀ٖhi<{>j$t3񚴋H]q2|v xUOZ x"~6 Q#^cӣg\ ڞs]vD;Tg#A˜܊uBn, XkX8\ a^YBp{A5 хJj  !a~|^qGDӭ́oZ^UlU_AͺN0n-zhKxz7$ݔ$S,q;ALmcq # 4J;P(u%YfIQoQ=1й=7?ܻn p:Mul{p1A?W5QE,Wѝ0!`qb?x p] 2ߗׄ Ue2`&'-*6f+!m9]n+#+2)P?y#: NIѶ0Dp! k#'H3ޥZϡa'h売t?I`T.8i\2)Z|#rQ]iF=ʳXF\)r:,$L㯛5ޚwJMoy}kb}:"+J^!ߣ?ޑCϺ "B}}M#HwtQKWn *wj8OE1}=<ޟUEDM620B?MxtFpf's:u} I6~i+ ȠEAvf?@H6/[;{{l-rVKam^ U;صqB \Nۏy<}A_   C+._p.]n`~<9,:F7}F@6 R8+qsk*4UJIe2Mq`9e>sHg5` %OT4!fP/8ݡ1\ _SW{#=E-sDlWн 89b||@| ;ܫqÈMh-`;񐰺xxEXm2%w,;[4䀂%vmn3zZI|^:7aU}'hȺU=߈PY`L]M8dtGZwƌ)C-&e,gE˕Ƞ|tXPB12]_6"\( W?Ř{Y=ۏ5{f ϓ|ǰx-?{Pg p+J x;7gX:ّd}Ժ%ܪ!Sz2~]SPÌ2aQ$~[+ӠL/35@ d0t**asq , a 67yi TYW ¸HFٌ4xV.6 Ǘ޸&%U3CnWddP/ԙG ƶ=Q%6zUYl e݃kTd CWs =CjkJqH\; ^6ITrfwfhNCR cafؽgu_}9jc8bh0ۿ.bv"]Jh|&QWIof#5oQ= o> Y[fF"9X BK 0 :qzVI,` WiY"8O=\$Z*&'MKHK+M 8vK #:}q+9|c؍+G`HJPAߺu]U$ϞBh_x2m3ݜ`8\ĿwXx*DMl&^O78خBDIA#W{L򣅒Q:? DK+~NT7"I<++u.$s$+XR &&cK@\ }fT l+O'J[6أBʨrp S"{pUUpIeVcsB!kheJi$Xn)ٕ^pkn4(-4B %r? mE#kLUG@M~f&hY=;i;1m쿤TCZ#ty܀}c/!7:<D#x4ĐX;p5#=EGz*ywi)oib#'/^h"Cgj 3E-PEjEAO~[l_dyJT@.wԤ'$ܩ)2-xm:X,l8+x ١f SeŒyѰ6}:Ճ4ˣcK@V6X%{F XσWIב?[WHZIM9^g56&L [1x5$3:Aȗw3`e#L[0ZH_NpyH܇#GJm3@h$ds=7Ҝ"gx:P$~E/u DJܾh쁘=uYc({JD""6:7 cU\Z JؒsMmқ= mh [ !;9 1[ɬ~Ybu #2 b6 +&\@=z) cxu }RuCrlpa^N@e]<`Lj{"v |z|GzLh}fo.ۇ2jV AXCd'm@`йjFaXfϋ61tք݃A\Nyu*bP!rJe1 S mt~3mwnŹ[Rv JR>ʍrنv@9Sl!mw29ycgi(%Va_2@;9mQ_ Jf- `m;0bUl)^7zSA60y;WR*1&1';ȶpxҝPjݞWnڭzTa'J[t%L>$Y @ʚ@nñ1(Y\g{2Y6 9&oO&Hh3|rT+ j[V>R3΅g*@qDk<Oj4T3_&kZh )R/b^hDEuBm^1'zD1֧Nkxؒx c gYpkveЊKoSl<^X!8ԝ_TiX^[ 0-W)Ds)]:Tr1sr:||O]I(N;E>GvVlKp‘7'cM.ZJn<be2p.T(bh.CӋ걙v *҇g2[udToȴm ᢘΏ̕HU:?<>pDW%ID:nid6N{%h]t@|yOQ|5>j 477P#`t' O2L6\d.l[iAm,rɵ0XjS'O[7sy*v*Hُ!mh̕4}dh .<PC"I%7AO5y^eLF=ùT 1OFzyg _!\cp۹2kWl~QƼs!CW?ii޽SųcEO沣6>Q=@Πx#di;11Tao\KJޞ&ڟ {͝ ϘتU(Yg];k.CY,+ݤb)W%M[k>4h3n>.[Pij=W@L$mԖf1&n8 ~c1%qk&ϮCfBHDй1dRfTbƀJ oN7S b[$>2WM[eC3iCN}"g\XhtOЅ KyUF׮IpZ+u"2- dۗedi2*&5>OL |>y*1İFlz'&9P_ 0DFDz < g#Pt0Muz1e3QZBK y Ol2j=~p'3_HHҎR MDJMB`y%?J^z@!Uy,0}$/WW` GpynB0% 203 Ǿ"YU>QArl;cStN_Ca=yYG-*&yޯla e!a1d/ @} xԽHU_l6fHG ew 2܎#Ebs>>QNscj]b׷ gRYuҼ3=svU{/%:]mEBGYKEf헅sO搙j^Y&Vױ;!,޽D _zoKp,'[̇ކo8im63+uMrt Nw矊ʧbqN k $LtrOB“]mdGTދtMIYPg [Y#ͬŦ,d6sk쟡qvkq-ΐԅ 0o҅qRs#Ȥf0֕ X?' ) ccwr2O߫'? wg# !\Hrl }%\e:mZu/.{/ eӋZyʟ K@,Iòr}WB r˹5iMz f,EFۼt(ղ\ R!L R"Iج 15LUUx?1hOSrH wWnՍFWIro R2 RׇwbS62|±[E#Zܢs"S0$A_k'' ANL^trEO݋|,tH, 1"I DaocdErD)Yh޶m8};TV/$HkvܬglRyaΧ4-Hl7Kyc xC hrB~ _)NJ/ቄ:WaYCjWMGwyk<,(ﴆzg`-^o9ƺe]ur^!>n23^)7u` ZĺCSZ2Oo!s*? Ud {mb&󦼦=U(`X*qgP[޷c8~^]1f;Y ̂m 0!PQѵ";0Ή?&Tg izi*hqX =ɿO] 4$0.02c S)M5JSv{5!󆅑UF"Iia ;XysS2!D1}óT'ÇIMppNǥTСFfCs` SL Jw.c3M1]%E(W^TP!ƹ=]qxB5y_VJ@[$ĤK@ngk}tD 'RIVBu9U8zǞ_y<4n|Зۗ# Q7 X |Y ;;xࡄ)MS f[Q1 ״Y%v>R ye.W,F6]F.q\HD5;1TիYѣml'(&Ug2%Qٿ4>󊱏 &. ^ov%P\A6H?tqˠlJ7.ɒ#l?7|8:"!8ή9򖻂z 6S[Nߧ/Jay/bOG%ο"0arޱZϓ$X䓫BG$ҭeeUMs[H.ٹ4`O `u}u3ZoFWn͙P(y!@ q7b;NF5C!b$KyB|ӌ.Mf!^fg(W GU v,]ro3ڒn:bĠzh"ӈ sJ|cYV$ko w֖?P  ֘)%OrWq\Hm"m|$;ᯑ=q0+FO4 A(o-I/ԡn2P2WiiA#om4{O&"UIQ<) VP@f3=duy>8$ֽܳZMO-x@a5R?@0 v٬(RCZڐ ޓblFji-m(G?ݽ~gTK%T[8rxmB@P p~Dzy8և&St2$ > KY;DQbKxG3I[ zN՘KMq|Jz#RT! RQ{~`[)i&_BR4S +.jLW3bq':yf^ Dl"'A-Qv 3ڳ& o_{^*4r s.*Biz!z*]Ο†֙'trFwL$uZ,c0qAd1b%@gb?#(*ހ9=噃h)jb*X73MغB›_}If *⭎i>뿤4z惸e52Yۡ >s2v[51Xv֤$9NOѽ p>#$-e(G P1-R<QS&~|cN%5`iQN,kP(O"^dõ Mu,ϛ85${8'rior`%l}km'GynơVy%DiQ%[\D<&UJKtDG4$~h34}c#*Sj#pF=;c=svMcilO<||83|1;ٝlfu/3I^v6iVS1wK܊ayLo.q Sռ_D_AJ)KKkSGeoaRפ-xJcPKmWlJ+=띤労dUoT8S?먽gy2&Pg&ț?m-1Dd+n7F0t CGQw b@4dt49r`L-vےt^qY+p(9_)շJwEN麈EzTc PbnIp?* \HcYT_ڋPazikVYVHj&A,:kk;wTk _6.U9zXu`C}Ga/9~`-б w$;WI~*RX. g'9p8\Z~ZsgQÐfON6WVH_7Y 7S)tϵx˚e5C  h^YI(;O; m٘89/b.qR9Pܪ/%@fV4kd|ĈliI?{Ig{q.NCOն3sdzuS >k1"G*a%!h+IRQp*7>2%{_Fy*bSz B$]BuAE6bJxs+$O@*cTY?3O0F;Rڋ盳^,鹉+#Tszj؝Ozk`6Wˑe^SM$YH̛ tEjvO!%Ju{=_sA:]ˀrp9GYTP 6'#B`XwW{OϺI=:~9`o F;ZM$KjC]KuLQ*NZZ#v=%槰KY Y&N,|nB+ G8 5qO߆0XG,}|ZERCQ>r ^dfD[p˕ J? ߹J{0]([GdG1ݑ8CkN4{\ ΢6I'vЁc({ s$Y qWits9̀֫5IMqQWW";Dm\)ۙ9n܃?+]v2Se@x>hɡozds_R9n}c$YyѿLcTl^sKĕ3L}Sk&ƁmG'4˲y͚0(7z4m_Ʃ2s71ҢQ'Z)4Ա{cPWm\GPeY5=|QK^K%9dE,ǯgy$DܽGSQ!g6ɉ5(gB 1ǰu0`cI1`zo.Q;+i砲WCW̪~ *`<~hR[ߔ)k6mEgrXL߉LA| ;RۆcȣsY:s/}W=\-i; Xīz7k1IW9qeݙ@FALr1iAFH+47GƄp@i,8(W{wS6emC:xpJf ]Z 6`I+rJ'34h:.@0j6ԯPBU^NZ9rfH]Jx?-'s:,o9lzx҆F6 FpYn/:Z7HXx.2&sIE.aJv.`sÞ,Dpcl57I#{3cSmQ5&lUJCJ 5E5COJ| Ǽa%dg%`g:x~d+@ [ UV5>UH54@l:b/5qVG|q lwVW3&G }6Y\i1/9A;gAbQ6ydb jEsGjϚbszdv5/{W5:3R9Lђߨ6O~y=wRuNV#Y}zV R2K+P-]rҟrsBQ$~Zg ~wpCC#@7mǎOFeq4Hcj"V)ISXzЀ鮋zY*+ٔ"pM2E抴B{ nR|0+.azf&s@L$ I"mqcAB+COE(/r"xR,){?G&hR%{Lvz$@c}4 }YC{Oq゙*8 {it(Na2yw,H6* h*XCZ' $m冘ơWdOi3'OOcwKTC<5WpՠYVdoN͙rNQˀK"qO7l`kGlc8F17?zp=Yk! n֕XŷVCP tÚıgvi;[K/Q;pÄb+](1mōb(aDB-3"20̠A2+0gbYl3+3X1lҪ'G?`Ԣl8`Pav>Z_VD-q>UKcg8y.ʨK 7xL<K|W=L_:XDJZ t薮?, qOS)!jBa+@$| KPoJ)%I>Yџvb^!]NJW/+,]cTsȏԐV]*a网~vųK9иN/yɗSOh8!lw'ΐ<=6?9-08zة&8Bj K=B ׳SRHpjO٤0ZآwOZi/tCO1~E"jf%`V޲%캃I!/L7RBYOj TMF H%F`ؓ)uR ns4wH 9V~XSܠKf=v`8PC)) 0mL*(0Q*V~@x =W/I5}1*E3$+OG|GJ?#A|v-r jˍRyYh@ǐV+p>(iAZB%{j5h J!ZVs`yQb:a>V|gkHw)*%/*tIQ2B5V / we=3-nC)˔R,|%tH@QPe֨_*9?`^=E? 27qM[ 4wުcyUv2х?_{Ӳ!𧣥2ŋXLskfp &< ۀg!;/&.M1 Z$tX=>ȅރIBD7H9D+jq?>8z-G " @Mꇭa[ЌuTV^xZ;W p/vڂ ^e,\͊$m\~F_q|nj'6Z~ܘ27N`)\ܼ" 1܉ BLKEfYp:rK^H~O-T~ɤ-owcRѱD-:ƣ*=K(2׺B)F$#ifOO2EZyx%(`U}`,v%>t>Y岽\l{) Zd0>}V>O]Yb@Sflxr M>5ߗT!N3vFo-ku^8|Ȍ9*xXt퐰ߤʏBVUm7 cL/ 3m/4sE,3;gt:]Un?.|*nKQwA},WLcsӗ$Rj@)qa2@|* haN`bnv-] T[_>Jק1ɳ9 [}5ȩKuT$NiJf&iJ}^߈$)pF.F4؆HEa*\N>l-퇐RQ@xW9(4V9LA . l5IdX6PG"mJSK+^y)5{ω`dSE"h nj(VWГ_rQ#_.'VT]h9D3~5?Y]q"}{S]wwTE>o{(t5ۄ+&686dt,e颼irq+ؚ;#ؤiFS/1zm)pf1º,Ī$tX=_`İ[ iB/M b(, +K yx]h]R=Lo:Fjg'T^ؗ ^5&\5_dqw#-/ tDB_5~PH#jm2K\G9kO#sM O 8o^ ?DUQ}Ҥ;8A*'r;A~{[5dJqO26el׍f]܊u< }%0aT UzXݨk2oɴVL^B^PJMVKw$GեvnӮ w1ٸS-Z][tsG.SGLf*s=S K(kQDtww@7[/7tRۀQֆ|Rx5Y3D$Zv:R !!0 6L^2ߓofmt{F Ň Z+C޸Aj9v󔝂\ zcdLun2z33¡$!o}g,q_M]lKF`/}Ē)4?8L(-*P]J0uǍHmѬWȦ s*&u¸=6P^W1ަ~Dr*`Ȏs&"?1: RSEdB2Aէ>/AqFhN)FW" aqւp팂Mvsv{BJ1Giȩ~Cy~hjh*M1xCHLآ'vu.6κ(4\Z?}fr M 1XTG|Jj'W -ת_31l/^n\6"S x&}՜u7nQ[b9ҐV\Rv{H33}@ dEA(eܕ=yLՇqrPWV&kLTQ!PgRP'96YRpWe<N!-ΨJz0f..sJC(%t$Q ;lAta@;>_qr{eʁIxQHN&gBzaUEӽMFЩKc$?Hb/z6:Jݳ"dl昗=55ruDX Pu9ΰ0G ,dAt٦tE*r8TWQDvsmfI\zm"pf'9K6 2R)Xk?+ B#CC!l.\SK H\s~{Qٱ\W@4d}\P~4KkT([h )YP_dLJo2<'$,TN njް|2$)n!a9DNkKꏟ}HŹp@ p#Mߢu;K860R/e*EE9WWf*:;u={jqSڴZn딠pzY'd)zyoy3+%Q/22Uk5%dNKD0,<:1Hm{Jy)Fsw` .Lr'.qF_*aѸ|q&(B> Pũ7 ah x|_ia0=i5CY|jy}7jw7:6&os »cl}r2x")uo$̲,Я0\9gכ)Lg75G O)MFr-{]b؈{ ~6@}L\PO&5 Zp Q]T@EHz`<>ObO ͟(Rxeχ|+'V[WwdayqSqݥ WfNW:*oJ%6*zmL4aW{ :%H1FTeE 9 Pϳ9_࿜a Ҥmŕл8us acvS2ӽ*;%Iv}Y#(=s9܍PO|pj2MP(cUg0f̎Nfn|L-7)oZb<-v9G!5F/0;HwR$?HzB|k8ē>6O Kp%^fp+&\PȶDoT_KyZ=c6?uv 93G6Ikp*S8#fIrZX).KQ⡦X;o+0MZ0gbg(+ubOw2,6ؑPc*8`:L,[r7Y ŬϴZO #?iN*E1KԾ@ Ւn%z6]"j;CE u~5l>RQI)aPaVIGYU8Ay 4o0x𨙔J|on^ËTΐ6&v 9J|Qnާ2kpnA[U̘G[3AI7\tW`.(1d<ngTgiNC${QJ@8"ͳ%d큸"8}{J) [8mG sa*fl,>wWV^ s'c0,kAܥC-x8 RTnCiO|neCL8ـ)5{K%Mpt#W.g$D|JY)pΜ!>ݜ`b`[PL@+Nei4`FV7~Lg ca@"{b`jb'-m:}, :ߦ}(Bf@+UZLJ٭'Zڲضf_ Y@0GS d8)LCV[^tP%XuE]_64a/jUsHdd )Q,1lWcpҀ]vT풏Xls*=B4= Q-2z4 fHٜGMSC0YОƲ-](ȃIO(c]xG[Hy"z% 틧Dd10Z![Ű^eϨŝ?J] >bFևLi[Bh<3'NIyPcB]:wx b !lӔ gT]}*6ɚU.q 8&EdX @ !>rphxc n+ֽF ok=U}kcC;uM55tO^8޸V8Nkٴ.œan:g8*i'IT$ t^c晘$,рm)p]"g0ٖyfH2F֡ ,Σ_y@:L@, ōY8d;U'$OjE$GqΆ[sx9EۄWm['!aB$p~?ifk:.dxW=A~g;#Gv5N",Ν)knV-}b֪Ȟ2EjLm5}8ϸs]v8q`%y @ I-?YU!j6F(/:tb 2 _7p򘘕zJ]&[4:%X#x5BR@vˁL扉0.gCӬLcu?Q1L0Yu)< IIR /SffɥgbKKx |q}pN&n} >MŝY-wم\ºSrl?b0g&[tؓKרFetWiֲhJ_^yCٖ)O wJV/>_XڰV> B1kހA0$$2cv@GN˻y:HHAG}K ƒ_)[~[e "E(Xi37dGSo_6 ]738ɼW"%Z6JtŸx\NP0RXxX 0>g2tQ4&D?n\BQ}&t#k"&r|ٰ:+;k++XD(V"rS+C&/,df6VmwZfEө (uLjH&V9m̈BPh8x&iA.++1{]d%)-$Dxu meZ ͍p}gy8dhl,k  m=o.C*-$wy}%3cqCt /_0E$JIi%@`^S9RKm i"w"m9\5P|l/9n8@a 9)kVaΫͧ1uTԈ%TC K.5ً<f7waa1_E wI']'jcZ<ԝfRQZ`wF>'ǶRakb;P4i>&L#d23JXJvӃ{7A;|7v #Ot!fH|h>@"~R)d@=be)g^ xs}&!aw! u#~q0&k;%C뗡]1C kd^D%B^j[ YVxhnp$z o댇Ӆ,Y`B9bFiex| 6M Qa+b4FB}_SXU VQOI0T5b[O]`ʿɦVGYBڅ!R$ Cb-7/_+Z W2!~đ1amrwH6;_K^]_ojKi^L7z(M|Y_-32<ޥH]wvP:J1e$h`yoi,h7^$D+_ȼ̊ϳ}ve}}% `C 5nϩ sx(5J⺞s8t yͽyu: /? wNI#jQ Y$@z 8^iT4E(0Xl "`Ycfۉpi5h=~$p y5 lD^5W;L} aM ƍq?&"`5x~7ph2i,֨ xXͺ;iG?<$or=b"*8$~)a.4Ps9B ehh>~jUP{;At- j2s| `. mͫզЏOe"(Ai g*OGJ1tyt0AU|t*^8u4A-̜QשIFHP 4y *b<~}2:zj^!AB6Fl|A{S[Z^@y&MĹr) %4"T=Y/[2zRIxUC{#hkt?O`Ɽc6rE<Ͳ-y\$4=̐ -wX=׋襀WᣚRՃxT MU'<05KXgEC52&Hh) i>J*/Fݮbs3RC&_pciK-ʂqɑ ,p 5U#rl-UC;N)uG|>i'|R2V\oV BVYwsJ}]g~pP= ); w:.> m݉4Qff;H I'S`n7ȩds}mhዬ>z@0LZI:xJq|; JH,71S?09THJ!&2urk`o[Wa7I'+dO%$tF,br_ x7LXu뭾[$[Qn`m@r]v)齿J$4t<wfbَTpKC9/ۀ 2 i5ҮAT6 !ν G -#_x3P6?f| 5=-'/6݆T~k 5($3;GZިV&'baLD!.%@ns Ԓ2;z;jVŻ50|gLO'm7Ke53K~ Rb#DɓZ@b$jQk4#ˤ/[p!O|Hܦ?܏ |N*)I*sC!31Ιku|^yhXAWA3 {!r: Ħ?8;#|-# :8x~H^lwVeQ3ډyf:o)A7K.|0 A/g(VFȊ&&{[= E;KH5\Q:s#)ZgQ!p"7_)è}"]X0!f)Tqfޞ>}};_,Sǁ=w-n~nw4lv|/$$ᅙhtګ27iߩ-|}JC%qӱ38hCz<(*d@Qz^RаׁsS)27{"e.&Dr+!F=D=cnbd ^;^\mE!LFda.Z1'&M Šޤ4Ssg]=L`:7ji|$6#ʨ }%"Q BCdy*4I|[et٣F ́@Q?]HHW-A&^[nCa ntOj{Ũ<]PbKq|0.`9%@k}+Ù&h8ݚGAMytoDTIʝE&A @7beȨ0Q_'wW+:(LDINڬ̙o^F% -A_ NcށKFmbQs]<J?b'k6P!2L/[uqJޤh(L\׽R{vZɤ^l-M/yFs<mߩ&Ѩ1h|Y%w_nb^b 1?>H){sMzK~b EvrmֹqvEG ɖSHjna4:tѻs&uz-ܱ[S-9 wO|N].IoS7)y5~w.ehR7-Nf3w4`0YUpmOq/(KF] @g.,Q< Zņ@*gdwOKRnD L8iSz\%ͷIyag\7X|)0-7m/I72!I4`wG=5 -1H2k.vH ܁N)Z`+~d1yFMJ##{&hw:wDAݝ+bP_DψS]a5_!(*_Xd]aw[ >+w Ô-&R[S*/Vcan3(\.hq1@N*?'c"e,7 fZ)"pf1{@|V;?Oc7b<56V|p.l66Ҍi1\`wPɱ+·MAXb ˎ.#eB| k;]G}(~>z+znTy\|:Jth ӴɞX5B],/ ta{|𠟊 zs PS7L۳U>K6}%Mm\eIdZQBըYFL| 2{KH<{I qآM苬tf ?Ua\ AwMg+s ~]t̸XUisgdk@bNbMiլL$/d<}Rl yNq&~=ˏU0j8!=`V9G+Љn!i7$v$ 0 ٽCg74JFQBv:O4>nZ̶ݓAx/|BS{|N\ZiqLrʮ5KW~<4LIK #7YcX1^y-D E;U@'1F&/m+a0ܯ6m+#?;n~heQaB_+9LqiyuDvԉ ݇pśvs^B9*(V J|OW;=ko~~ƛ 5(C1a!`%DҽFū1vC\5lR R=p+|Şj tYހ 4Դ8H+ÌEff~Ð˾̾?c$<:*<0BsnJ`[ʲǃ5E0(_ڜ>"9}b`(1>ޝupm(Z9׉Th̘ RIF&#?q؀o=wuXj=>l+Y՝GO@#8fu|%B+!5z/_o 71țK,I"Zm9(P+"j጑BjbA4QϋDLe ڿ޸Mnj&rM#"Bd=HmyX+Yif\U.zh|tˌ{?%1b 8`d(|u[0$ȞK0÷(;F{//0N3#Y=/*N#?~:e;'A5QђPAsȴ;e3O{V @`QlȪT{};s_Gǫ6r0A]~_q"Z/EAwbwedΈf=pg)]5sLL}qFh4#)vo(!/x[ ad Xٷ EȣZ|Ԁ*n-@ ?##j k w0R ߢƴzG4> Xd,1UGF1MKXENM}`}1rF&ëb&9A>Fa5c<׏`{+P/j0TL9 C@[tv Y<B|d$B z/;Oe !u$X +**&PVF0Q02ĮY'ܨdXl(+:8(lV.O/WSz/4qBGc#hr4 H( m` t#jZ?.]2JTnXT}.EX36j8q,"lkl8SQYj"آQ_`7'cy^-(մBTz>,5d )d_9^8)S4W=ؒr:ּE)7Y+.b];8j;9!RjK6떔ei1zaM MW?Z \ ޼"7Es;O|^O&&{D 7 P\ ׆ȅp@ _lL'r֩TW~6d&&{ToΆ,(b}}Lw\Qҭ3̫TO(qrU;;,r#} 13 ]'0nv`ݺBf욋{m)== ,!\Euy uҸN!=-x>WZ7iPTa^e\X,XzjvosTh ka1bi|"s| ((c)^|O*XvzCe-\`Z& 'siI?w[4gO^$Q/3G{]S;*n2lqwA/?,3GN,tKDM/_Ŗ)9' SA8RhŃ84~Q 8q(!ǥZWvV}x`JV_\`Em_:Ȯ` !lpoۚ̎qXRnq I\/RL4Fsi_\)E|VbG5 e ʩ[eN|+avެKbcX̧XTiUpe=f涋4Hc]H^l}"S`Ja=%nLšqdv=2!!5B_z!>9dr69 W?3é^M1_@k5b|jչ 1^~F!R85Eh1\RJ:ַFow=Kaّ-3Ȥ(=n;ɭo0Wݎfda3\;c B- qR ic ̍_?~Da=ȵv2xf'hIHY(= g̢q0~m h*#DvZ lφ;-kh&2$Bnl bV'Oj Ţ>s,߯k>'KP@`4&lޮ6]M$vuн\gd;]{9t&30FQʼ4#vVUJ a$X;if,xzy?>>'s@jwXĘ4+Vcpo/NTp:dOV&mIQe䰥i^vI/X3Ud+g˄#Lܮ2^:Iɝڅksk*x%N(քKJkغoK|<MALeu JCdNg]IN*ִ2e4Sʈ) _ṊՆ;+: MEqҪm"[g Y%8N@K$vu+Gui= +"$"vF=a+z vx QGft}SxKs$ Q&U#n-%YC<ᕲ̖,S7]w諹E}:T>Ҹi30;-\2)՝!-\2J7qsY>q[Mwe+Xtq^"o47>: R.d/Lkl> >kwbQ\uڲ8pҘ1UeiJ&) 2pQwY?P΀CzRS9ʸtΈn2 # Ý%ҕ!Cjq{7?*SHzw: XMAXBe1Cs}rOoR|t#1n嶕IZB>ΏŻ=@r؜}ܞqϭk#QgCT}|ҏ:}6q}3}i z~wMëŝ0h}|]l F{*cU&UF֜>\sFI&3>ihXVfSQDN$&^[Yf֊l7gkA~!)-wVcX$ oyi*U\ڄ}ƇX5ԡ?%@|þ&ևM<׮F1h?QEg}L2[uMj 93)C}75Ѡd,9žW2٭:6`a\~k=6R :!yѪQ9<vauL߃ԌvKz2D; ppQvaĜg"A]] =mYHMK&ʷ,um[y?nx9-/C4ͰbqܐqA\ ?V#W&DVeV(f Z*9R5K|9(@"["%y 0}sUǵ_C|EOx F@U6ݨŧHec}iعd%-7x ėg $3"y*u 挐6Q$k%ǹmnī3oUn~& n fX3ˢǻGݝk=~ 1Kjzv;X╽oZr5vqF?p^M(wKeYO*]!{9h?pvXji1RIJI}SZm_!UZ&/&f!loOȯ<= 7ͨTAhH#ޡ>:J|BC%y[rj+Nx#{Hꐮ"ސ$H \$_[U < sY{3l$^`B|Dl(nm^~I οhHΔ l[Y1 {z)tl^:z4ӾHmݝM1r8Y!`d˲'Zx=OJ@\(s>@m"T<[ps5F&J5^;MeMք'Q!G + ۲õy[MeJF3^K&S{MAZȢW9KCCB\Ѻn}Z1rx>1pR&_X#PD7Ռ$һR?b(5omwU\SWa fhiM2SS-" ^Dj]prb)Oi$+6О_2jvɄcbeKQrn#xtŪqZ{#LoJ]1?n n@b~WG$a $-eӅ;Dw5PY~+)$X޲֛P޻#Ԓ]޼/Z!BiHv8!hmvD4o>!1.E##i* "H)IBP$;Wxټ͜cD5nCX-Nc[n_ Gfy/9wdIm缈_[0S%C:;&µҥߛ%=4$Qw5emk܁_P☱i̍?;7#IoH\tysv u(s jORWe<\>B!1YլI93@#DLs,6}/o)|a@QC|e_'t4  {a-|օx r8PTXCdKRpЂ!K2Vc!#2T zϧRQU4| Q:cOަ{@ U1zIo%_@9*bV_06Rш$u LA\bΫVXji搡 dY\{NLiܜ$40}: Sn[7@]܈JPNr?5~OHaB#-$e"dy>Yp͋PV_u7( 1rL g{*{!Z&Ɇ 8SQɉlӷc jbUlOH}X=!t[pFΦO"v,ځЦ<xJ"Ӎ|2XȡGcD s#:O>uDž^uȴ&pw>pZu`j7…G^&l7B+x{bBb|45䨓/Py YqH|ȿ}jUcCn'9efPSoT IFATL vABgWj4M/0xϛRr82)иoL7RjҿFN\y6[Wg'OSͤ"eͩŤ٨>1A,I)!Ѱ/6jHU>}0gU:`~*sx!s3|$^$i~NX3Vt.}kFOb505 f @u[2Ms')ۇr-ʖF :x'[e`U{Ԋ{R?R=Ǩ6,><+mf:ȃj?7FDN_wi}K sGmy'P:GM4=Б7B&Y6b 3&ky  7Kel?s dEs)jٗ6ȇ-d 樂RH4@xu 1έӷ3lM~I\Ri Z{dTw!KNt\7S{ƹD0,P|;pvmI@bj@ȉJg_ϱl6s5oH lBa#֔GlN PNAf L7^<J[' ^Rˇ) : B+tj~nU`!tuo,!XO2 dHb}){FDq9~qGÜ~8da Ylm؜Ms N0/Il>t=4S}-~ w}bȵ 4M^I_euގd $=Y;}CLXTJ%gմڜ) i;dB@R3 lu".\nҤoT 0Kwc>cQ咉ԉjTbfТӊ5sAE AT l/zqBܝVX "{;{oQR;w,:f~=kZrp~9$qI {tc>b߮#꺓o%ժT(ǝ_vLiG8;26ϻ%CB cfB`{n3OyҤ2y7'S:EP1}ņ%wouuJbfnA1:3ࠛ[X).gXpo< E( #H6*Ppz~ =QF8yėj|Zc ,r/J4U"w䡵O_JrJ+pHED9aӽ0g &5O$ ɼrif:ٌ2М>2z # ȋUTy \= v#6攴E?nEKGxhd0.z[kh U&/Z6ݠ3!KSF'ۨuDD)CӲӏA{ fQ!4:(H)}c8"CQz^QyLOGq;Yď2nP!D}P]jzMCzKKZPa Dc߈K>>1^M|UeH䨘$kj!BHC$kpR"DEQZ!rtaIil{}ͣZNPw^AnkkePRY;B 4y!ŋOMg7^]~>o-(]QU K?Т_wn$!㠸Tl8F+BaGDj5CU!&1lw~4hDn> Aꗇl\~J]Ezs>+'1zNeC&ϫx"dpEt'<h+}ɉs|F?{+?˼9e, rgi=rgLڥ,i~]#Q/v䠚N;;Ksrr"eD= Z `N#VVnh~ ޑSoƼ;CȊLxCٵbaktt]+x0;AѠ|\^~378,)sr2н8)/Ǒh; ,$`v@cf*($0 ϒoMKT6yf~?4F)JD撸)IpB FFܦj<$mijĻ2}_DaC%Oׯ9+ /C"FZ/ZIai5%Է$U+ _S:y4` o3ϜTO4lRw&3 ?D ڡ}L7B /`O*)3жF^;=<ӏsxW$@{Kz5qV8_!&sGh5¨MuVoaLX+66fWRq9NܩBb.eXS6weBk7'/˥]69mʗ.SyETgS(#ʴ3 2ϟ~W!о̴X r9֚;;&M`PHdƻ[rD!7,3 G\U*K WFeκnѯ3!Ö=0ӽ~s6A5iv_+܅/{4̘e !xlYb2ߗmNn-@s$ailUEAgu-rGzZ-eT`3I;ƣj҉y_x,.#?NBƆ$+?'̑YxpaJQ[x, qE V0]',:K~z'mmM倰Ђםt3 G/$êѪ0x 9o> IRoaxunޣqV̾≝?$g$NV'Uu aȌAԯXG0npEu+ }߭NFx[_K"7tn Ⱦ({)ր^+ul0lz ~SGؤaWՃC8b rX ^+F8 i=3GKר4T3`NX`7UqHF߇+3LZnZpj5ˤ]#]Ċb@5BU+љ "T ?U =v&*6 t "Swl[d`S'bX>#OI0v1P `r-e8KCou|u\iv0/ eM %"Y7R`=Hײ |5^ 7jo#6dW3p-X M {ZǒdʓxX6!ϔZZ72Z>ʼ1x#νMk5ͼM>nKʪ+@m77 G mڰr3?Dp.07r){Q[S;TCͮGE}sS7Ta[bZ( % 5o%s O'ŕN!\u T9sqxvynDˣlEݹdm·uG(un%\Dg.axðvD6T.QjWʎ+wbL,:( ԠA:2vQeLݥF6[aBR]! ÿ HR"gƾ.jհ϶Mdٲzu&Aeji,P/b5JVߋINCnF#2g~hF*E uT 7z`JG0?<V%KR[u~=$v:waV}} lvwiugKʬJ&ʷ$pcvnZ^Έ촸#.sՉmDt+r+X 1ql5'ge/o{섮~xEjnAZPʰD 0ұjwb+h}&~:\._QY/g#kcsQ+mEQzKcنӸZ6x ^?w8"~&ܮ e p3sxJcj CR5 e`0/GZTL q89X%P%h-Y|ci<iSU[?Wn;\ҧ>r`gLwW#D6"ϛ=x&_WZ$Wծ]J1Pҳ-rl}$'ӝ<;ܤ`0\,9?,?{3060/NR#G3_9ߡ nX mjpP¾DTCj[#Q 6o[Fy" j.Gi`-\b}6Gjus/fah#=>䣨~2?QJݶ!!deuQƒLY0}F;CG T-Dh!eZsxL|.W{vЩ#L 3-!%0rG \qHvE2/aC8y9s(G7Ux۱/#_8&)`-v C!KsNQ#if;۱D~M³A.-ۤv j˜A5p'3 : CTfPM@QAluAnP(1p $r G98rkbK@e6Gő$wl2w ,rj:g~9SYߠ'!4h@55Ε?"LbU^ E=4H點}e,X$۫+>[$"ZwtjɎ""q@S[T0ce")/ivx% 3m4{>P=ihBL{uydgcj;bH̢4:%h.z=zQd=7C =-)},c]0EwDI1(әЂDuc۠r jUe ɀZ!BA1 sA@6p2SɋqCʃ8Jyai(F(}q2]*n(L '+{YwٜDn0_F)h?ou#"gFjwCD1=;ʱ%@ݐ eƸI}xۥ,P ~;[sLC_00^'6@!gBYD}L'e$DON>(-BaK չߏUbZŵX&s:~>9&HZ >O`֕0hۛReAYޮRЯp-+EH ( | ^,vty^*nuCF#h`' PM@:2fppd҃$ 1:9_@`&p\l##Nў|eC \A2*8h<}bMt@``E'Yv.Bde֦Ï;F-BzN7j_p=g_!]^) ~z=\i C |KGSǻI-(!c HԂFZa&o;q/Jږ~plx ؁FInF=.!qSHs[`8cLyn.Ar^جk0[p?wowp32p#)ov9֡)X\|jOאJ4g*V~̂YMڪiYԖUG&3Ɇc>yd/rW $!dbHs}^DurKNLU`7K'Nek/jPfC(JriG [ Qbź(BBx}g=>_awgKaFb";U@_X?eljQcڈJ`K]WfyHya;wbYmP䭁M0INeD,Pej6; fB*t}˂y (@$bDxxW/tjz%>ѩzE+4nC k\Mcyw5-qo{R6_BnyuzqU*TXMCiWO'[-^W: gi6|ͩYӛOCQcAޡHsྸ /H`w<{jkiFKlKdKev(iKD=Ǐ80b13KOryg#9@T,_^+tS1 G,ư^YM}xw01A]:w`,_;9_ ~<'mC'}z-:wVȁ0-zxꂅ.FѤX';!iS|䬨t)׎zxIH bc82T!Aqu-opSD,2\"CjC|)ӜGm:a!"PR"γNB/~] $PӤԨe[ $r[ tKb3j"dža8h`Lh4҉19z ")A4ЋJ 8:[ZGvcn2*:'qF̀#M#:; o.0tf?SZ"߿ lvtƚn+7FTDݒ֖ޙوDs `_Iؘ d6.]kb&W^Ĭm }X~3EyލU$ 1g϶Nnؼ[E"\V*ڜE%T#wT?;+Y׌|y%=96]KQ eQCE@w5]&(I4k'+HW]0-PlǞv1dnlz TmTz{($簷pKœ60NF2ל6ʹlb8=<QPLmeo Sg`1}X<*d&MՀeo7'ݾ5/HxqF& kq0&6QT  B.s?F6DW(.+@&;\n]bc5@q.oU R-Mx {G`/mul*1fc_6,uu; Og.;)%j7z1̢: >S9r:8C0t%w0P\Fޭ?̈)1EmBIghg)GƺO'y>*D,z8&8 E ~zb^Mp]a#;J) ,GW]XCOT0& cw|K\ \:m;N f& F<䙡Ff!8 'hR=H ySutD֢C2mj!Fn.!W'n%$:{xޥ;Y ju :IaERs--4Aċ[Z{l(غ`a~Í{b}DZLHum;ΠSM==k=S+$;Zxˁ?jsn$# *t2 |_DiAvKuKK孀Qg>(le6xnm ojV$ȐjAyB֋IJR;B%gN Ϣ='Lzueŕf֓iutf4f cu@QF|Kv_)d'_hA.w(a0U W4M5 y=O„~`fpgCYl32][m}x3,Dk\RM|{qoͼ7 a 272)BkBZ M@q\̐GgL8mHʠ\U n]{1^::q"P d&xaN4Gfw#;nX7oks<';dx=H> ,_5,.0BZ&}@ IؽtwX"ee],:+&8~D}ƛ޲;.* fzCBU֘fcD ޵Hyt•6l8V\Vd,=ެA>2}+]$A~4@h'؆ "Q{|D{2QAkYY/?ATϵq2" 2k4u.a` j!BuqX ޻-F~W Dp5iQIѵI^idG#00 b6^U LvL"` d zh:4]fʷÎ1B^dl=Ţ$c P+,>gP=f%:Rb$-c@Qd?޺YJ%CKA&"k*t32]/(ל{h`:E["#S3pJ( %LcЂaF f_%T꫗JEMdA#[Nd\C̘jf1:,nk h>J8A=:lmZv^.Җo V%Ɵ7m`3oC_5S8g/r5ҐUy;=#U1m<;ۋC^CPVRU%Rn/Y8q|g Ikrv)-73ri|WP>;0a}i!ݬ'&R}'i!J64r*0O3ApsA;39rǐU޳Kj$z5`H`~|89=߲#w=_fT{'bFZi1X@8T[V"`"\(3<=!N0ajs8y,w .Z)|=ިl&6N,Z"x.Xcdc0I/20 $IxKqYz D12Cf2K}\*א"rl91S,(ݲZ9@ls҄. G9?*Z 4_(=/ݟ'ľ MPvRʱ^-w)NǴ nkm޲ŘMܮG>?DiN9lZlJ+@iq $rWro1Yֳl"bEE;H)yt8j 9wME?i@U!ۮe/c3T}Wu8濰^a})r$fH?j/cjr|LcƄIf>{^L7LuE6` h &5/ٹ?_ + "6F]~zhC ů7Y=g&09J+lyW}FTo-w)S&Z M-d;`|iҽ81-( -Y'̚0\:D(^&qiPtEcG,'qT8bەlu*VW&8,Eߘ}ɆɄ15JuOǻ \jVLOؽJpW1]NXPԹ I6߭JbJ1'Nsr}dmz0KKx \0B"bs+EyS)ZCV[Xt{cg{tR2HOayUsV 8Zht~;@u ɌZHlk9Pҗ%2t,H)НH0P邌M40*vD)sYcgkiDS[kCXШpޘR?mEJF Pb8+%d|iW i%Y?{ +Y.sItW=tnK  gm¦ι>RqXtCV5;''>?QsSp+  WP|b0T,zȦY]i㢁6nvBրl'ivm~iێzA%!T 1z:f!1 m%>v'RLKfZl3t87K#7=էLb#h l@$t}4j2=;VG8I̍ʴ'& sUG3!g >~C'ABRI0+M߃9Eͫ'hᘳw8^^}~߆sAowm czshɆtѻK-_xpSq@Vcq#NeAG?% k0:{!5,<* oG9bD^NW>Kb1n Wj]{pm&~_QJUd@1؈3x» ot[h""pu]ǟs_iÊ tt4s)W2fyN %}+?`eIDQVlBj^G3mNV%wKe7WCdz|ӝ~%Ȩ]eKz<"h aUtܣ]Vߺ^ڽwb$Sy%vlYBh;Wr6$ġHK V^ګȕ#5<=-tMjx2ԪX; zi=tIy2MyzX)lw)1t̶$s~͇g7@G/sݏ縔J{ 70!qJ׾k,b+E{C.]q6D9RDLx8c.zk9[+e #=NfDQ%=!4qwlx1VօxvkD2nwbhmR^]&w:\DWUo,ƾX Ar)eb \DOՠw dW zu0%D0#ԇK ##J갧I ܚuۭm52Hat@^kCUU-g>;(q+,z&-wNp E6bxQFW,Z@p`= $yBBu'i}QϟIIC  J %XyNbGHpޯ l4%BD|R 5H[g<9wi1-K)7 m~$ ֚ru@pL+]2\]쐓*T:X HF91i1ƽ٫0tJĨ() @ޜ'.D=5džDi^zagzTK0+T"v0!]%S c#g#+z v"HYi,}VFs-㌞$30=}˘DA*t jr#:\G: 0n=4T/ǰ@<-'NiJ:X27X*<EUB %+fЊ<^gu}8rϭɶN Td &,Cv.iQtP[HHzGeEEM(s“ڄ'= ;B/rU7v.yk^:<ѬJ,@'JfM杒>.DXGy9}AXnƼ2}~m5;ny8Q\nlFB4lEg,((upY >`Z]n2c'M;iCRz!c _Z+U|w<\ñ k(W|3lP:a&f-T C+9p(ġʂKp:\u4gVmLTx枝Relq^Ndim=.㧬W=ӎpl@W*/;s ;gQ (M~n:h y5h(Ŝ??4T.mZK02?jZQ-o<`^JRB;dNp3DG~hc,MJ׈zOAzMT>88|;8tbf<"tż>B>͢eW4W)WJlyࢱ#\H{iw>FIc3jU Fw˶}S@60uh聪ADїxƢl$'kw:|z%J " L܋Nj \*vO3FXHo(+n7P\?&v *ì_H_=>ͪ=8Glx!R*|Q}7@򏾔)p O\*^c1qyJ@5_C!@S[=$6YqUkwVo{\#وeS[<  וSpeT"Ի@u 毮Q~J=[вZs+%{7!<-Ǫ``0P+8I :^2Vx>}[t&2$xXNe._aEq۟3Joq37RGQQ􇴕+U*4;aƁԤ |NbS;wlz/هVpkcc r3ۡ$RC KM^33BaDz+/𲊩'4y`?%hOrg;@?: ^<`[i>!3$Ώ[0Z{dRcmz;Jgl8,S䙺U<(> GmHyk!LdcccOsR%_n1!셴\#ڹykB + dV77݊JoV/J {&@̩"OuxnK|O1MnCqFށ4AS =SϮ]۴y5ƟZ8 ZlA,\)g͵Z:{w,md׹,Ncp׉ۡU6&7B!E[ʜ5kb/?zzn-sND9.iI@'2(yiؗSۨ02/l4֌`кK9W'{Җ*<2n'Ur1q3mJ7p4?wQꄙ2RR$C@U(ܸy.0~~Rk ؅' pfb}𬋽[':w ZmwaςCJiXr!T^!XI\oBwls=MKm^b]rTfl ;Шx=6l}{$ӐttYe˶#2QU eKanUU*= &ĘY W  P@Og׉h(jҮC,g\}{_]щfr8-v:M#kٙ>Zu~b2O$ڏ PI_}MvD^^t~Bso:cHj|idSĥ|ȓ`1ב-֍w]_-2ov=L|QǯE'7EWǰ-A%f)Ee)~syh׌C4F#{S}9&`=NoP &VЁzmrѦ<2~ N?A+Ԟ)l]0&m୑Z`C;7:>Q=V ^-k79^ί3WxMtuH(՜n E]ZsLKbW G)ѽm3Pыc]e _7? aɹUPRKk $tZ0v_$BxƯVqF":m r 'LyjԲ]OoV6e>Z({a:8ĠPeq˭`^qzKF8ƩgnNmвL)D*F婕E/>#)!5,d =Zɹ]X5PJ_[v]1Yo@7(H GvS0ԕe s[H3cwD,?gw#AJ ]amg\`> : !fszR QR 2K&](^)Ϻ_s; aKls̀n҈U$ f"N֮" %d.3?5?xt]n98fk|_E^#MZH'><#^=@ȈsHӼAk9͋Qc/^i ?fdN:3?r"lnS .94nP) ߳N {`JaƧ:¸`(h  nj_8^{Uy-f! Ɲ$ iG(g$a1VBX= rDdԈA6Zd3,b=1#GޞW0spq~\,Pzɝ!TrAP\wu9,2~ZmM r%&^h q$uQ>,(_nTGSet[f؛=4q3t#mp: ~ ݰvd|X YG R?GE KRx65ܗh .JL;k [f?09G-wH/}6 MD#r3z.c7'$gIm=:b~H.77hG n`,iUP!pevCDte&tL[ru.1E:nMIb~uoz8+DXGvMBF4s*И7yƒz"5= k9@ϲCm!+G*}P:o2Sɓ4zK_h<_UfBOCѿzrGLa`i,f."|Z!1ySVt}(ոf6v"yW7WQM>G-f ;{GUSPȡ'~11/򹕲+ApTOUqJ<{SȐ#@]L'X|u\t%UahG:>5yfWC ha*[`z׶uz#Yȥ9X:.X6P?ÿ^r/ET,•Y[F]:VE{+..L…Yi|q X?ܦ|hIvU^L./, PNʞ )8; 3ҏ;_ȯv ڕEQY|]lL?;QؔxwUu~c}( T3@a:o2 :YP@>=lC/^P̉%Gmh(rk Rh ozy.9%=:n%h#pyUs]Q?eEŋ/Ze)5s/3Y7=\.g[_\XR8Ճ Qt9Ԭ;dSsP,Fj\~hJIH_Z( n3\~g0-uH<]r^C*=fQ*Gȼ> 1UA+E0:F3U`wZ FBk1^}PawDj,-%5 e>!4dL(-z1z?TNو ݰ30`,hYJ`u[m|\y<2p܋G`5= bUF̓pH=S".gU1tcWUk!bUb[н5mFe#nNdp+^Dw>UBQl#hsLЀD%"״2D?N[VT)O\jU.nEKw_C=nyܑӊ0ȑDHo> x:,-;l~%ǩnHQн2IW6:Ǐ1g=n1_ptܒ( >WTR(̟Yn?v⣝,͛vǍDWYK{ 5.>NۼMT nJF7^Ųs6?t҇H oqQq"ٳ^}#["jg )}E$Z}ӣ0s>ED:\TLHjcz7?t(=yoh$ʬ~?B,"JS᰺IFn Ix h BmORm+!.ɄAoi3y;zAJT u[1f{)s ? +QMw-nvr' D#FLoS{Q*=V Me0:p% xKDD[\X =E0 .,H8l"?AEJCw1R|όW\L/&d;\FzM_ ݤ9ɓP%Ĝg"aD^!LmM#h8qv5nTgs6,R,B#i9DySDg 9\/ -FXq*ޭgFH!`)^اu'Fm;E&+Uȝ%A@,*k`lk[*v!ub15AAQ$doȿ+Ɋ¦ws:LkՌpZA{vlq6661 H:h41!jJAVyH 皡߉K4miE&.lt`fA@ V9}Pk2txO$GЂdNvMFGmacjrL¾eM )"RT#@K-w;i{kFo+ĹK20,#\TF4ȧ) Fi &#/1 N1 %  u0_x5!U>(Pl\µ?QqIPD b x.N'k Q"B#ۮ4Y֗z*ƪa}ӌ);Ks_R}@FH^=>y 4?'(36M(ƴ|kS)[ jA| irGs҇{u@]yge '4W]!)[ xDwHP{ƥ iY&Pa247 m٪K%S,1 `OWhBGKEjl+qOf4!8 = }nF^Hyx~!SŜq!؍#DECg}M&_\I7H~թZHw'c2 4eդr` _ z^!:nhZ&&Kz#64bꗅx[u0B3ogq'cPR8K҉ǘT]w\T}f݃+9)a߄;*7)[kz2dҒtAhP$8& ̺2^@8q8?3 b]}8'G>2%:c#1_۰>-\{ 0ʇ*3?b|dԴTTDfb[Nbb`i1M2N/TTxc7gJ`qTT*R'*Y(FfSE`[| v<uf[%˯ rl>eT}A6<vQ (l%o3$)Gf</!,;tQ'?cu>^`\nP4fл&[qP7|J}QqMrt. .{##Jd<)Se3"Skq kM(eYlйlC;CGȎ ;G50#N7_ ގ5ebYh &b'i)/fUr.2X$ ё\po A;5 B,߱œj\H]v?CPl #o5K[-&zp=[-'ukҜX<i^/En[ܿ6)DY{.]lt1  qUt^0<]E!O+4wxfդ0noKsG\~I"P΁[Xm`ă wHC49V["ihf \h]}Dž&{=_j+ Yٺ'w6C۫^sA3ZٿV?@@+8d |@P_o?!.4?K[w]B>E}} (ݍSZk T[Sxz̽!b2ld}-m`:28c{*pnf Z8Itvyv믆]mrA;JƤnb}X[aߪ#}+-crQǖgX]  /u6OȽ n `@3AفBi>z͙ pP̪?P=+eD$ֳ~>maF2n}[--1sa9ʲ}93 `Gp4#=<_ȵ@}X36;_`KFzuO;5>qX2ܦ$ƞ>c`b<Ěc)9Uea "Fתg+%pjtHš`kX2u 9ZGpL*rM;.c tKs` dSj01bW2 k=W|r1#bl܂m6l㷳~rCl="5vHDJJCKC0 ԁZL;Gw7uC %tQ2aA6R?0O(sGp cewX#L2m'@S(i?]3EEHWcoMA-VٴdM(p% ]0kF%C,+g Eg"c.G Xd<4PN|bYng PjqpW KyF824j0ah%ɄՓAK*DcH`%5ڣXQM/PI(l+h5tH7o)Q)+^Z,;1 Hb韘T'zT =rv3B_ IiNLbdq)]ь6QVM2Fx6QRQdD q?Ԩ]#6!HC˗"&ȠXgM;>weV7\>Xi;5  +UT lA#"62OL ޝؕht"Hp++~Hĕz6Kšd/(w{MH_N3KIJIV@>YZ@ -'nHa&wC .?F~*7ZHgNn ,5PL62PNV c0G.q")-2价J~r2^l򲇓ɓy j.i ~ּnȵF,p(kPU _gZ:v8X%6[1;dTGޓ4piQL1PoXw,{ԤB)%VK1Kϸ wMFkȸo`u[WNJp,3S]ù&@7oESɐy3 d=! DqL~l+~+(S dr0z T,}\^- >>Қ(b_?-hq 2iէnPKƶaYs:#$ ᙕP5^ -|]R5qT𫑅mJs1۾'qUdձET}{vaJE 9HmH''[ڦOʚWaNԒG8a ?TJIdW]Β;U {8b׻ !yi}7p*5P.#wFUJPϱMQ o(iX/s]| owN2_Rr w5Sxl76X{_0$͡# }銷a=eT쒕EDf6:T4T7sHnjI)*EX&y+ ,V섯ND&mGa0(Czt+ey}TʑhgOV6hC s\<&:4˄($5ZYB6eUw.S"%f^맨$1[<{˨IG/0}U[o,k!s%X!A%eEWU&sµǙNzVd`97Dkf&g=k@Q'҅Qf2b~, {߶fq5^!20I 7Yz܃P$Yz:*Lsdג F)cfK˙GV%۷vzXtZ'fx"'S?쏈2Cy[סU騝7I*h׎+=pye|ɻ"`.rZĪ$j+*C2MDc})6 _| RC)INˏ~Ǧx#_(Bӱ!?PsJ]R(Vm&-a@Z[mNDQZE`Kg]x]Jh1&hAx!8]g0y-C[z`m " g eYO/gkShOqP:,X.~d>b^I0l',\JHQ[6҉& ߤbF~05cjŋ}[T[PsJx\`Cagׄ Hr`wV"d =m ұd7r ߫ݘc?b EYB9Bڭ#c-l JD744ʞʣ2Z-b9_'Aa6uaĹ2şhia5EUvW[x{dtg bLϿ_F|$Wڬ^Si 1ʼ`Ƒh+Qu~!$/{<%Wc=WU鳅u_ Y2enkl%^sǷv 4A3]tķJkTbxf(DXdx^\Ed IIW7j,QlkYN -(-e` sF'zPS'f5mp_YlsP~FD98`g|&E߽4l\JYzI5xxj*>>Kw]OUp^%?A-gEeN[SUw 0w<ZE:XH]߿UтFhF8{oJ?<#q(.,kpl|3eϸ#bViH["Ǒx9;tEEn#MI^Q!viO /u윖E3nӉG+M4 1" S}Udm3OJHMI>W՗¦_/FUPPdD}XRkKsepżvVBCs~\VFcj 7Z?Kx.FFMmgR0ȧDwȤj+TgBݝR6Eh`%!I|BtݟwZ#z1[ u C|w<ޠ5dq*:񉟅 87PO Cp2Is(k冓FA{)}WW owCdOa*\gCֱƂ M7tŅmc&O^g岏{;fZO/yZzoHOX㎳?!WӬ*p-@Jgן7;B_l[L +]5^ PV0T1p:;6v}2QYY\mY` &x) >Av+]>smi{Գf9\ h%tGO0d6OE} LcD9-uބ- urPL Zr!8w|=93_6遦Hm$~; r`vUnpQ3"/N9*&aB }y.+Ux@:!K.PUk51˷o,O$ D[SI'L޶-T'gB[٩gkXԙضa|%Ĵ8" OkOdՅx=e֘^w>#65}`ϭa=fPe{N7Df$ [\٠2edDTZw,-b (#ZTp"khЙ` AsƓPb2ZsK+Lnbs}-J.|jAyIsotͅ6nB2(u۫8ѡ@$ND|dT)/*tb!LuCF\ĸX:"M0i}oІPo.o2 DķY:kU6dPx!!3.э4rY$0Ybw[ht_ScG0Qiը( EVHܨϓp޺2r.c{Va5nz$҅8TDI3%VGY9*98SAe{ L z*45Ar۹E/\ uGǷ軥orb,a1b`f,NE=Z|S;}4T\ A.iEzn?vjjaW{fʒ6t4 '0͕ ɨ "JYKa{kv謩1(-Cf+Gt7sLd* *R,u%6[rk1:4ؓo"Nkl.0 ̓9VJKڸW]V74p&׷eJHJ^qԝd+>gD9uc{(K}_R@*G)MF ] 7)ieZd'{ݟCK{dzɓ˷T :Wk7ޖmäjϪBiP+vs"ꭌ`ö@d#a3o,gjQwu (K%EfS6eGAkF [YZ|EGPXfb=JR+ ȐJhG6K}'OpƁ| 彊mkUdfm:_dJGǂ+6^Ob~4ӵ1QUZľQ3fRutuO_'|F<V?q7HMa [8DY[Ѵ BpxjȢPOu#N{>!6H ;"E=.B_te^yd씑ffۑ@>[4u@gZIwwacqr`XBg_-qڴ5 Ο[&ȶwy<^å j]˺Fiz!qzK9;>Ot?\d2 %v[TztIl|_=w EbĨe|]ʊU{cw.u)(\TDAG 43 [J"U|s7kgb!uaЅ,5o2YL к ơ1F #rE k\υcӠ cO&؟2Iܗ6t]'6^e{wMgyq0 #q e"N"n\R[ ΢LQF .;PÁ-&Tk}!:x'5?~ g4osx\D!<=Ez]Wu}e]/ƎD F.n.O\ù:1W8T;:5hvrrH`4D07}*Rךb KB4D^K4VrYҠ!tcXeB4ཧ[ĉG ij+^f9qƧfnԩe ex A|E^DF@O1a}*+lGPئEj=A|l;\I0@Q[<0ϵj>L,zQ" Gj+\Cm=3-"~n&qQqc`9޵X ˘cOxjriO vNpNfcsux;b~V3pRf>1/]ӄ&PX mõqi*cM7Qm>&ݧ BF?FE+<א,F6pjj1WgNC6Lv >0vMPS;+(~6Y {4:Xf#VGYUS$r!s/PY|K5qkND{v@̅'v眵 9,Lie-wMakLrVxr-+b:?lDjjG֡hGm]m+w=6C^LK)PGA~_;K=IW\UM1Н-]?5^*h9 Fdem?GX]|fr )$Q*U/v=SFE<3&vM,V@N0'qxJ,H9Z7U #;=WRVN^Rm䩟[lĆFaf}a_z %1ShiE6"RKKX2+>ˉKU=AGTaˮDe1YkP6=xe+$MU:0Rѳ,g?IsGs 9$PFi}0kw2& c#rEESbsKвz|7P&-0ߩt:>C tHi2j^˚/aG,0műK~ΗX$["TjT]7;Vt! H9moPw2"ͨ-qCD$qJ$Z:/\_9lP'+:yqԷ4%1|#}s[l` Ls#_`I[Xp2t.<8d $/qڡ& |!TD9X=gV>dpлo=u4J<"z AFaF>KЂa?{u%UBUcu53yzJXB`|A2RBG}%ϯ+J(e)i)AҝfpjjH{$^o`ArCa:Kӧ$œ 2DޠH[2-2`ݩZ7roKk!un+fBkb8a?螗-ܒΩ9ㄶ>=dh2ղw:F1$M5u]iyG6<^tZ)yBVj$ 9El%WZsRo3xdljFGX/2,j)xҌQHӠ(Upβ7R(HUgs"_J= hm27?Sx4?ç.>%Л2#%: )\ b->&Zl^$`44]#ҫB cЖ?ԣKeOyYӢޮgn 4h|ZWE.m<_AWo{~hDp!Zh洌'_}ICg1NȲ@$ >A6'4˴{xYJ`Q+ @'nt 5 D/ۚh<N7UZSӔ6g$sb)yr pOa4Hd82gOȉ~F=~믩^+7Tvsbz*s_`kҶ {D<_%~ ͶՍA8Z"gAo1Xr&$/LSXdz׮ Ӣ1*a67_^,`1ɽ]uJh2u¾o;|F$ƝM`"6TDr:JnT>cI8GHܔ7lׁ'd,k:b%fqeOzkBs!3B杘jz'|#RG%=$d>3 vkkVIP,! g#[RaYfwƗ`D?W 8Tox5 }(+FF*l\ IAȿUTw@䱵w~Ch% hr>6=`3\ ~#PTVN@;n bM &~BKEE3p< nyg^K_.2anҦ%q''|P%CN,tbŤٕ6t&ئ>5n*8d,׶]QPŶ*k1*BuC-b-j6zg;ԧnۂMo>՚(5$ͯ';RLĉ@ti\kR$[{-@3v1P!r‰PERý4MdNLpAE/͂,~ ˉ2gӈE:="O5;8Qg;=JJy IA[揺x3N~|7!(4-Or+] t8PY0Hrn&Fs_ؙ?qɜxnB / /SΗS Żyw?sGK'isjiZ>%^szBX:n_!#:܋]?lnZuP%F׳?;}^c~ET1I 6H ()ɾ~vDj>`oR?u%"/'= m:嵳ciN!Ҙ#NM2K n 0Vvi 움㡽nJSB S"VSp̳ǣ@ gqDl<ӶbMx>6dQ F-⎞>I!u?2ޣ%ۓs|2Ky!x j{x,=`VV3^mf`seFGP7"yv_!~~2Woo \PuUh~a?C!6 ̇E[8c/4ʛR{:u:(̜Yj1_ Cpcڪnh$AsKj}h @Z c'ou y[Y-PC!ؒީ%BM֎6'QoC:Lr?Hkz-DֶS0"^߆ >omGئ\$DoV7^F A;tn6Od(2svF0By(둮qmD=֙2;.a '#e0QXjMk.'?!9AQeZi8+2zL = LmΝ=M *_NBƪ1H…Zm7Kz.ŋN+\Z]a݌!VL\lzݻJ ݘI8ϕgo$Ԍ|1s$7 3cFh mBuH Ug[(r͔4 sֶ/kܭrlLi-+VNn Z#RqP:C}xT{]C݊BT RFl: ).N8t1!o@xx˽Rk=8^U_JVP-Ϸ9U4a!κ6kr!6jLEAӮ>>uuﲝZp:K(݅ ԓbwi![:lsHlz6p퓋uo *M・!G1S]*{L%ڝ/5-ң]x ql<,_NjauDR.Wª'8[$l4dU[U~tUGYblł['%#]5$Hcg6<մ# k8!!⒪I1sY[Gm]գz nhf{ 32w:{+}K,lqZoxh8r$6Qϟ+p77KD-M1ŋ<[G_Nͩj=̒uP0O,cB -8|eC\*7\&H×Z2&s"{0EHgCL %6SS>%.&˖ T o >\u&zZ) K]c8r==,.l{W7&O}Wc%:#Tn{G@Z3?2hWcV-Y&ĥΐlLPCEr+d"|o<%}y}p[F&b&aEŰL.nxdЧ.-%SI~Bp{jMWj=A^9^؟+Cjܧs:R|!qᡲ qqh_@$@!8Ӹ)J<鳥'\ی{-;`'].L&GU{}xR}DD򄇁tFB%Xm)|b%(fN{v'p 7][X I2[~eC{K;Y欥ASc[ ƳQ#oqs(ߺ5`ѯQ"kJTFtiHt].;M s M%ŠR_!N;y@8H+JzҷFÞl] K2O ֯ʝ Eh[_Y48KS'VɁ};ɯ3[>(h.:G!3gpX2­"oM,./{~J\/NX1[e|{&+w)fqK3l|WH,EA,ݏ ַz} n[^S1/хM^:kO-[aKnm: }LG@Ov1XFCoϸṧU4]%;$POpRXb5h;TLlM/5EFuxߨҢ=Zk_,KγشbɊ!#t~\:sR|-E!ХpYoxc-lh, vJ(\Ggp;q嘒vOmOFe2 Hl!N>+*5?9QZP֮ }db1,?Z1%Bg0m;xծtG./C7Xuv c##W2tLCBjnB=Ac㼐ޯz#W?jHިܩU̇ Ee|1hh{6K15() DWc*r(óY5" l@]"h|vTprDZ 6|ض75 8TVd <9:/.+;y2,v!N+\k?5GQp:.#LEõٿCܕ*x@!`{-b_q6AUG6hi67/,Ӻ(RL᎔F r*)&ZYY,7VP ]G;ɄLo9a 'Ձ8%.K^(g "voA5xv4Xh!5./tS ov&:y Y8f9n.avh?@`JM'KdsZ_#6jU.Z [-'eƇ\ Ύ<\!q]D/j댅]t~Ml;a:f!n]*cx*p2Pʥܩ@0MMJ"è ƸwUCHEb$ Atr4NJ,aA7.5;l3׷9܏]/ jV,P-+ )ļwdI*m1l$;,\ZY\Nkl8wy%O#}o+W9Zɧ"` ?c]]@w+qō'=I](_0yNGB/NʆGߊEv\TOz7g~eN6^sN[ȝMo3G;M ͖1sL4\IkNe].OH: *Q ܢtu[5KU$OhBT &իgB5\T{OOB[<&b{տLZj H:'xwgB;R髅zY[{Z'.Gꯨ6H DA$D镀W[S5yOջmrSp= eDt絙/[5PC;rM;݌^^7%2@ɑm< 5 ߿*Z,ZK7xEg@nh詥 k܃ԏv 7uLLW@p򼁸U-b`j?wj-Bԯ$BXcz 0쟔d6g+xSN$-<>%K+)d[3͉7T)`Q`\=7H#5Llw&mV.m+~BAG3QƠѼ'~Ot̆Ά;Wr'mv0| C\m(<3U0v<9#F \&P70@qSʮ[K&ї0/=+*G ^D+d{`tw"jJwpti\ OkŻm79~A||o.,q3 ga@. (Ѕv2pi{b5 꽓QeN/l_nd<'h}R-kJY^a}ӽ9du#>dY8fN ݈&^Jh]2mrP6,+b_C8*3#.l<͊a!UM&2W&/>p}T :ۋm1+{1D| aqaiBb'mh?Z^(}JX,0R yļ÷PuQ0ݖjE`_jBq.L>ztjz#HBH b#4vղ?E& ?YP fH:jҒ0\>9n}r%K0]icEAQ3E o+jb%L7_epZ(G!8A^[\t_ kSBGOuk^|6 9 o`VqJk y1kCq*sDj&ݺaj4LNzhǂ$ Ev|Z$1)k݊sZV|r5^*ԿD )T~\UlC<֋u푕||ߨ):gN_!͓LRLb3v1'V5Xat ퟾<N2[t~ѢԀdW k<til[a];^[sA+AP8?nhV =żGSxHg\J/ yrmp%Y-"W{0_0M$YY_eqӿ'r)BkwOd.94Sވ%l~Li\*TLӄҼq縪33JߵbYJxm/}De<^Sǿ^rХim(B|8حPeunX? t;m0t9~Ov9eQ{bWdȏSev/BlZ\7f2-<ҀtPlU")#Č1犪l(Do_[ѴhpS%a!BT@lp5SC𫀤]<<#O'欓j⃡GDnI 6I ,s\ ?I;W |v bؼfq̩52Dd.B|:JU*sZWf~$igI*u%wFE`M$ڌ 4/\Ϩ|AYfgd 1JӮ>GP:kO̩$16F ovB?3\pԿ9yj\8`63^-q>OV h?! %BR^ GM|%\$ [\8~&?!@R1Z2(ŃА/Rw-@]wcRnvwve!@*uwۜB%XS P8Ȥ8Pv$ۖe:'*3J%}cP\49E~}zȜm җccnm =eG э&5_d$|7$p ,~D<ф¦% {Ct5agT7zx -y*SaCpg*%W41!;ISIx]P5nGcDFklZӨeՈJ5˷%hռV-Գk#:w .|7-Z@'N3L7W/tyց&j?OԂԧJE+-ǏIAszZ >can=\U9瓴a3Α }lA,e} YF^ xcdNz{ȩz Zr|S"X@ޣpaDkn\긊R={TmOMwPLp}@A/0;pX Uxxg:?k=ꖜX(CJ/GDcQk,w d&S*ZD\?0y(zQ.7`p*8j:?A?Z4Ov^Q-!bPv-o7 OZ*wfY8jWMe(|bOBiDf=[6?ZJb;ɋvv4,XNْe}+Œ];N ,@0w \ 3L Fr ϗՆJjzvJ*pOHOTFγt~%d+WDgs5_ @}h j uL {M%#:ɪ^VY@dA#G9C|+7ML>יT@աb;#^5(JA2+o$j69Nsf:;1vAGU|]өXÞh)4XC璷E^^s*K6QDN SAH_H'vEY 6o6iG uZƸ-Ss$?L{qe|B< wZYiL6!F݊U CO 5BjH~IDRԩl)wJ-x8aaK ޱڀ>`GHf`GTڰM377ZTa/eJ*ᆮ"I:d7xqfņ~|_,/Tf2k,{)Ju,t ~i"^qCޥEk &иy?D93嘨_O[ė}fh؇΃(? 'UMw5p+pKIDZyO 3@h4tTF+Բq)'EtSr,ԫ:fkD"W"S:[Q1y1Llς>~b.K[('Axr1HJqI~iV|2]`sL>ì{V.9" h -v<>uU;|f+ ۞idgk3+z2TdyM^ok1CM6.dI ) h\U.S.N8Eҷ۔sJG*=Ɛ1X{L2N WCJ4v_bp6X[Jx/=׶\-XF-XD),]j\i+5ϗˡg.b^q^tF%M>>瀵Ң=;^Nm52S$. vC4D|a줻m}?ۓ:{7x̒LjvNzH"E ɚp ־ؔ9@\%@3Ke8kenS ܿ{ۆ af T' !U;n4W_g1W06 ~JTYWMj7rz&H2o k~MNڎ=Z\}]!WFVz"S7Yho|_ռӡh.ř”VUs(nvJ| B?n;{5Z8lHzr Л%v 8K uC4:6R_Uvf>RCx/+ƈ܉1גWw8@H< ¿zܩ[_jQ Cŝ[S7HN"NYYK$êais5 `I U#%I$BjyfZ1OYr"(h|R4ŽrAQyV :YiM"A5B3gh֦\eTGs( |P<{i9Ծ-XA75,ʟ䰵ſ9; Y qh&&ӻCGo]PK;y6n.g `_^fjFeMTߏ<{軂=AɎ4i2♡?rY{ Ŏbh3Š!l,Y$Z\牒mM)uN |(uk]62:c KS@'gYVBw_}f@ʩ0-ӗ}FmN塈xfvČ2elVGa FKDa;}!Í:V9&)0OnL4! /RvWT1ַؕ &/43[Z!D=w2#U g?ICGO_g*jKa׋Dp\OϚcC !IḼiD\f I+xd 텄N~4J&-Da\EMnʢF$[YvV(.&d\ #)'PIl5RnAyoXj4E X16'L䝰}#y dr庹Mx>No,_cLBxD˟I7>E=ӿhjdOhCeݯd:g)<8@.xV@߫aiTs{}J3zaiH^$jJ7?aޢK ic3Z.:I[db&$i h [, k:v F'[owYZ Ou ²ϛ'RѠR9x+u2ygDOG)wԴv0(̞vcי `d8فIo1ЄHN )ԍ6II.OߐSY^)'m8 )t ;{pUŞ 2ZcI#aߌvNqOq=PC<3nħpJj)+J=]W{1*Q#.zw{չOdRMx_ӄU 30O'#k4C ^#6:=) SzmBA[( YI,Ns;M1i=~ue4w*%p uW?ܻ,ġ'J'ՙ~RKB9A\ڼ2o~X|3OEㄹ.øVۃs|cie>,*1sT(@RrP wZT߉@2]Or/ <;d) (]^Re YfǝP\ #7Ӯ7_RPpu*Z,S'~oH[Zv[bn%LϮ El /C,7:r>DV`lI+ (x ]t7˻ }|#vAgkqBe̦g AƻKuE4zxIZ,O1Qn|K3#s|(3ZϕZ/J^ԥ ( T (C.I{VSuzb# lc$ӳrD̒..grSHw DsdJD$p0??E }2²܉,h]goo/tvr.P7Go1Sd^Gsʢ8qԓ^NADobt%:Fy_xvd7Ny&9bRPprDOԎ>wy}̍cF: wCl-7R]<銫@?ɱp] ōi6selqX # jcf;_Qya\-Q\+ AD ?xrg]%[>8WCh3QH4 N&Lª9Jy%\bJMe=Y3S#.)ݗm:hZd)xT[fNNoQNkSC-@Gڕ%H&^ c܋LYu8Lȃ?WђYJ:]l(%%ih n %|atu[IN,9߼ PwPtMeq]r;s^úԸ u5lfgp1&6ȝ48q͢41 &nƖ}T@jSJ&uo$0j_M8>G?^MF2sLu63bc߀2)>.?N1 =vE/Xک?畧nȋnжJ۸SJm|DƁiž W緶ZNaהu4/Tva63 ۮ4>$o XyY̏)YBI+ȠtH{HO@6,/k io尡qo/kgP)Vܙ?jlY,q0:0J8cRl~*ݳUh9)l;AѠ~zÒK)I.o(PQ^-Byq_2&VVՒp-{(DR"~x嘼tOzWZT4 p-#2,ӵj>K_M߂w14ʧ?:B6d:c+UF]C7fUF)+ A sBRyO1lvRQ[Wc/*1$6W2v0 T&;D$qks{SRF`CSُ86c>Ӯd"6a s\gdC >_&>c-~^As}ܨMG4@m&9>3{NRQ r`/rƓNrzW$ufeguFM1ÓUWz xվUP ogȕ1]pbB>骼Xr[ &?%MPAv &~~PcVE!rOaMpb#3'Yz*Jl0d@ ?yldG< c5 &LeK^2c!G%M/ʅ]Hf||i p:ިx4[7)RDy( H8fͅ.¡3E20quX@䟷jqowkjQ1!}p;z;ѳB nPnu& W|?T:$$ӥĉ\Ӊt%3/_g xfڅU2}ϴ7UP;'>"4q|tLs$q{{|a=٦A Xjk1Gȍ1뢫ה;[Wclg@hy 'dQg+?;82$´/U.1SX`I.X( S܈zSxu|2v Nn_%ۙgdjyK4sw6vbE>:kw$) )DV{ZgObԣ#8<+JyGH`iP8~,fq"JB~d z׀&ײ%y̹n=jWpjb2eK"[ /I&zBjH7:֝˅æd Gv[ďڮ テ@Ny2\0,`NeG~]ve@z4/ī|RNq1KZ`@֠PJG!Yxo@Xxh#*t]u٬DRII)x=6L#yڸk:n8n-χܷN0~ꑱ{;O_0P-k>,5#Bz^Hڲscz@iϭ+PQie1H 7V^E>!Ľy+tS/ F6/(e?G9#hDE^a_iVYЀʎQ#Gbo>*n>T)μ`I5MLO9E7%wےR{9lp߼8Փ][8?ܒ13q8u L)9/pU:~acFfӫɚkۡmb/#udxANI <@!:U| "rBaFN# _8GZ=pSO/=YL?J-$_knHea B\ h~coJ[䃔w-͠g4Ƨ וV nw˝=QZIXIyY؎KfB2=aF7P7kC9|#!?Z,@QhҊ2Wvj'|u͆_ï;s髐N&?eA;/Y 7 j"Ix{[Eʽo/Cճ\;{/LlpC)KG Oeugy|J궂n erh>'}'+7qW.}Nj#uw jVNR$b7o]]"ǵl4Uaz6J,L ,MQy\lhꔦ1,O[[is<GmL[ML7q/qv*ݲq(~#U )Cg Tr!?;al7+ rس@ڡk]?/35>2RetlϵMiTJ0)lp{zK/<ƻc)ho5w>0RϮ!t8DW;x CIBCsCfJ_ov0Ч0,_6 }ii<1O Tb23M],4dp/GՌ#?%%ͱ8[N  y^A$?WJj_) \`=/E8vɲZm/鋿suNPfUSL@q6ts{.Ņn~k$Fʖв~+FF)XJO9TOg4p 6'r{nPNDהli~ѼG J n5pvE^=j1zj$\FJ\>bXDŽkXe4OK1SIkXռ#4>~H(Bi5YOfNzLah̅AZOt)j[NjO*:'xe u1΋Y=H17gQW\qf@modFf/_XJfEsgv7rTHB:ol9hlDN':>GGw6p_rQwpYVv t3 Z{Ymm43QuƇCxezϯ$稡4 ǀBHєT#əagp3I 2~ּJ?Ύ*O=b8]07cNGsLBΠQ2nL0VC5sviO~ݙu[lF9w@n]ތĩuf)T̥ +r'+l{O 3$aB)3 ֏ &.aZ>cĭws|3,;(]yB$,N7faF [{qA[4:(/mE: Foe^J׸H[5y0 wP#}*(;3W,e\c |Xj ፳F8_Bv|֊P`h*fr Nz^`g5YQ0Y64:yxka_NW) }J`)?c.]_CW5RߊPښhCC;ձ wlyT֋io;)G]~1-nts].0VsX0豲3_yZȈ!—2ռ" vk'ܗ!-\5PEʄQ_$s[ڣ9ܥ4:ZMlf僸Պ S׎u}Hy8ILF2ue6*׈3ͪQ~Cx/1-h\*Qtdǫ l%w*dN]-(FΫvwkq}[l 2xGKqV[Uߚ靻#5}V 뉄"rJ?F:CЀSWJH' SXv>k$aK74?9T٪˰l+{4qH9Z\t L)^ɔ}ٶ%|KONx:6td`_|JLγќyyEJ5Y7KR8xZz3&$ ǫmdnq+Hc'}k`LIP%>g=zIߠ{@(<46@vBI:y)bW hɵŹO:nطΕ#ĸ/ hK;Y/Q}V_˦r˃]X' >*dPÝqBY;(~WE%T14E3r>c ~i,d;zI0z%4 >gϰ3S1\x=.QgmIsz]0J E˙,V8hx>sf / i@YqJ,bc eU&X )ޝ:&'GDX 3*^#Pӻo>{Dߑw@!|Ir5[.A ,1&ŎGn?P`l1V uccZu%ZsssV}QT(R?@ Sܘاʚ*R/4, :P[NH4X`(ҽNǃ}.p?Pqzx37I&*SͣO CDqJ_)lb| I%PV*?FMPmƏZX?<]!2He{QPD)IQ$'Ӓy@vWrCPP"b?ɉWq zYZzSoS.["ZpxTAv.)&ߨ<E )"ΕXHo.O˅q" PD29LsY7ԫ^,;qs9,ѡWmyA1&5~pD8nBz].JRTMޤ2֌U|ьC*mB3Z,}iV7@;}HEM FN:f` rꐢuR+,/T3Qi]o3LV'koXksrk_[Iy6&~lVAe: 7LŁfHq7m3,rVYܫ-J6bXuctGܐVȂs%dm 51JH/~%dC,#~vx4wo~=e*u.qS5a#PETJNfn[^I\3KE@|UZȌ:oۙurxfF_5+oy?DXc3rs!ݽ"jP|maF& T֘ 6 u^zR9%z泸k98e0ԧiWLuZ}EܦN6ώqD?4 Yw(WL2Y 渄F9* (u)#8^KqRw+"wjE.Shm`O^7:FB;['.x[vnt䥋کL( y#:C`s'EgQo>:ƨġ32Bie*E G{`4]zh!vijwvu%;a0nX%/" !J X4#o 0ND4.[[/*]8fTڣzQoװnO.KXKek&$oA,IaEc;hj΢?K8#pŭjI rxT0ə#BvkH4Bx2v/+x CAk4cR${['L@e.kR[=:v|wL$aCQ`vwۿbTphLZr6s`P 붬F7 Aѵܡ%t~KCU`gTC*> s%+V.c4Nq#ݬ}K2?9 BvrV=iGrbA4Oˀ%nKν<ŒJȩBjj8wbPHl+a[yk9=M폍" y+a&ؚ`B`do3KF!/z<dAf޲1'xtZ(ٵV xBjEܜ :xb\W'qRٖR#<<ꭥY9E2U>Yyېd0 Gx'fĽX+A*+EBv9j( :P 6t:ߛY@9A -$2~ŨE[6!5akۼ q677꽤-Os"Сe.~2PYu|i:fB?b S׶2{ʳųA Mc]sv65RJpQ-5~Ew+\tFګ_8iW@T*b-l*Xn+T߮ľD2݉~YC8Mo8Q50lq2//sQנd̢%DlyƢx!uRޙ"6(t1";)+:eB3XfNc&-MJ2~2/F\w)ҴqĒ&ن:#(,!K^WA`\EIy0e۩Nu䕑8XW;ҟ9. wcvБnՋ|'%$H8Ԍ)_R%z^~2/aѹco=yUOWsզ%eN)X΃gJǸH`k!{ Q8!;BXl7+fRY9r/ro];;ж/ۖswD`P&zg\:Xv <qBtz%I*v.g|!xF4w\t r Kplgtb@p"{7ʳbn~I~?R6ܻۆ*4Ps2;6b|A %{]+ӍT)ũ/XI2qW!١-FۤYSE$UEގg]!(DI[9SdޱC;O-@#O9vؓ A0vpPq zØ%bQ6~ Йg+p %ԩKHt7D XE;~Qk|#(Iu =l߼J|V\MI^@,0W~$oᑅT EkLV@>3Vܒ6n9 ןlhyd=O.D2bBW{ٮ2"*~CqP$ִV*w7 X$@Zp46v}d^yidgI0qqt54Bu,̾> U.dg'<JUvo৑SlUuPU{xzD.+م~%66C,?{<tv]=W1?̠4KOo]L`rd{ЭZ!WpS<%m J.r9$ z;Fu"+9*=iB|sbyF_[jG48Иf"E%ٞ0W5ǡ;o-hfᯓ[nd(`R݌cyzq\0^xO\"\ E=]DвJ|^$՟-m2$/(֌IE>@Ypyڥu?6m[93jFa7,6WAbx~8B⼤R.)(K1+/ @}F,UcqXXʶ.aADԴs4Tnpb¢WTUK5<9Z4dSf)(Ub_jwGN>7< ['8D/pDI1{i )S1S*+leFEv%T31mϹ$2 TĿæ$k#c#ˈ[ eټ·9[~.{d&Ѝŏ.2NjFFȩ51ģ6X4W-mn%Gg,vKXګh'撴>ORYlOk6oݐގٙV8Q1vf^CyɎVp[\SfVXd2rP+_;+U{N/Hj7bLHVCh& қAtqQ ڗ؂$t2ZHs=\HZ=ZӆK^9q+ ȴzڽeu3wnKf&$xv1VIjBG;T, o2~eS}LYWsKHWqͳ^.=q4Ow7\/Ǻh%L &f)]_^h1fm~rMK* ,Zu ]V#JM(`{T0%sh ZB[ץwŷ]giH_䆘Y=MDg+}o{u:˪!%Ĩg+>d iA6?w8jl Mճ# :LSj!WECZ1%!FɒRvX3XQ1GN+#,xONfȬH%s8zPICi h|q3f?D:>OACb[5Iq]pcow;l !̮6rl^=0⎉FȮrfX}.V%w1 Yq,fm.ueĠd >Vno ңo[kĻJmrw~ N3X3㳗r@v%z\"+4uBY~;ٞ<l,B7ɒޣ}pGNF~;VftT<׹.:P|a@$6_ǫ}g[B!C2-ot/Gt~ ?btPfYᳰ 5wvolT|2%3`Rqbw=EJ3=ڼ %M6PQ Zv^sAynqpe2%Ot\ŎIER]!Džq$A"0/Xz]Eޠ+GT.$lfK, hG湲?Lr*,*mOŶίOWiqu kO(82MRNi8%"p[~b !ՋP+" ImiyeXYtptB{YLPw@jȼ-j~A+ٖ2_ǭEUv:ܶe@ld X bQu1rSmrWu\(X?c\b-/F>H(LYRcլ@IFq_e{Mކ7nU@-:F Aj8|VJ5Em^S|!ie,73^2n*-YBI%03`ܥqtt#X7œG)f5 d7"v|XTI ҰN Lbϣש+j}?_e{9P؇t/_~S س!-qUq" +~$$ɽRRid9VRC\}C25!:0bNWeˌC1 ^wٰ,]~JؠAԓz}~}DOtUS#|v˸ғI̝?vkKzA\1c0}ܮd=(dXk+%!u~LL%Wr>J8el):' n*~<_z"{o=vե+@dv\鄘Wܚj6PTK8*qsRa"GFU^tm01f\Hx(SAAjU|\)uz4vV\\[Qκw$:{%|Ox̏J^k*3(ըG]I2K9.UTCf Yj徔ˤ1 _V=qsNVƎX567%ZB`tauU{rwn(ӹ{oA뷟\ 9?L4$iLW) _Tz#&}kx_Gd^x텡ܿVWV;ȟja}{Wd t4[X72{Ȩv(Q|O=b ӗJLU md]c}f}f !Ϸf*N$:LSD`f(̘/\_pTE˸[YOs`NtZll ŷݜ$k#vX%9>~eJZ~?%%&0!vlՙxٿniCq61=/~!\ ЃhQ;ϚSh؍$sC4fWj.[beɀA^'IEIE'7DL&DO#@p' 2+9j\ 2ApAEFl^fk<'m# J`Q3.T5Pmf⨐os4?ҟVovǫ]r߱DtS@@"X(X&GzkqQ\5lt 17P1fS#nhPD[D9i|Xʩe0mcOld=#N7rOϞ4:zLD{dB'BK4 5f= 1QXO:q=3i_lFx@u) å}i kc4 |)׳O]Ku9k[ clgOCOK)|sR6+'9l=eBl!&+7*c 3t[ nRh(_h=CUC:CJrKR?܏[MW 9bw:{XQcʿ_8ww0"5CVQEShbBx9 1=XL4 QEBX|Z̡odRhz9YitLoD.^j coz3pߺIk;C"N%$5G`3oSVPkv {nތ3+k^@Τ:f/>jlqg\kaIH֭"AU,0\4Q Vidt=xHrB3{*Ƥ:mck^|aJZt%嵿\{.lcu#wQpH[*5$>rw3$ޅt@,H!kgpgw/pHYOFN*2xAΆw9yc"S* \cb0X_(P8M4#Q_YW#7T{LCtF!mIR-rCHmޣDɡezmG9,tY3̧B^X))\է_i[g$6R$25H5u%dL`! OTvz;"59 wӹ;xcM9 C?4Qƹh]ɕ!hcpHrf%K~ c@ VWJĔ'X"Ya40NAٌ+r"!1"2{hL@ Y\@zwaڼCO)v&f%tq+Hi쿙4QVg򵓲2t&V!S/$`Q8}fwsyu#킂oԄyS7[-O{B{R>ջ3*]) ]\rҋe%J1^4 {w2Kʿm eLFem:TXn焉V H_]:BAdIkQ׳R [@;&yM0тF%"YYh.rԂ8%G=;0v_#'DPߺfyCMy!Wơ*Bίݲk2)J2 }\Z6 ΟWt(LKiۉ'@kBb%8,3:08UE6fl.qe31PPE< ^K~u0!lo-- jobs5m{q| r q`N[Gi U3 ڭy "JBa-)\/-Uvy|=yN[S.hge%4lSL10O. N <֔#C_Iu?@_9I~Fh(7KwVA-뜄t&_8N(q7o7TR*P ]}?` ;)d Yda-]rE|\$;;J Hx z+Xs۵27MKn"Cwr*zLG\AC$SS:rk ~BZ&s7|;k;+ ˭nLlqWP<@: Yv{%z𥡻XH1KOpUjOx =`gl+kˤ}vh:RwkLV<]"ǫ Y> ٘-ƀZ1e-IR8H?Ȩ=,GOAzR]u̒0 ׽}ti@5~?J#9?9[{5.\{f@)r oD߹q۟`5#kT氃m}$>=NUKL.E0mG&Vd%Z諑c|pV ϧs:b_Hk)Sk=:)p+憲E8ȉ'{aLN%x$ꮠꈿQM",|)o8 9X'vGkGFQY G Y !W  鸌ciRnTھ81n]c%Ҙe8vFc!)S!Gg5`FIhQ#e`Q_G9('b徫鍭$\J2 mS0Z+[f}jXW4K Q%[&jLV3zlDkjLTE6iI{2]T L?&Jk'Dqq7cܜu.S9+܂9}g}nVc@Kz9J6`44F @6kg@>'M얇P7,mz=xV lcm<(9-*Y4JN -v@\/"g/7)_pMdEA-m^| dYżCIb#t_FZSWeAy?I GM;xp\Hq=* Y`H2^#B|< , 7%p 8N+4l #E6ܰT[~bFKGRۣ:"&?5or GzYM:Hd7iw_vƚH j\JSwɑM1N={PU@W?N>Cc$w{*@oWɐbRXh 47 *O%k՜>&nVd]wFp^̈́,1_ȤKLH"I+G iՐN!߿lN\_so:RqtQ5ӔϏYH(Rb% . +\%-wE)hdž3JQ}i3j~fS#@ -Q<* 5N(&'v;s#& DtP̢/ {@v]jK.4(CdWn0IC͔yγx~] ;ɏf'>>mF0R̕3u%4']?~&`l6kAX[yɅk۠q,nJXEIy"{oD9gOf1Dt1r곫TF=E'_J9*BÓ[&[ 4[Z tgӽIJ!?1:g Qbxyޞ %&@M 3,tGhb9Qݟ؁ b;$ /kqI{j=T3 в5ڟƖ5}1+C$Γ`Rj8ev8HN۝bW+(jogy2ì2+l1+9P-HSh$wkUdhS](sV1TD-}yiS-. _dŢ4.C0?X)fAs ڱQY\pUNɼ7 3xCoȻ(619.sIuTNRx̦)}[yqR8v/ G\gp7/)rpR@.u8R&zQ5`tgzIq pP}fL7U!J,{ZS81<nhh[q?j)*'ƶf}ӧC\v~˽G[#U#唷 Q7 eR 7seK1p!L)PEi tONdJd8!ѯqGYYgi5aaX3 y4^&5)—AA (ˬCW[#"1yFi3OZ-  4l*Q#YJ8i'vޤ./!m޼]O\5  &~l}l #g'nR?jt\m@+ IgI( \Quuښ<,2XMQz D;DX?GFE+D"ѼZ~t|<.p[r4hrYU˛r9CJ2 p 4J: G.{8R7R!UH#}[*xӤsp*r}^]~*^9gVi?'CN8ON'">RM3 vvxT36"Z=Y< hrZ 15{ؙ`~{%`[3mI~9:T(X֫0#_5CLMJp8S\R+ dpM7G=|غvX! `,sg*(=`e/$XH=2P%]*>L Nsf*ZM'!1X@| ef4G*3jU4obyY; o?-2k#STd6M [BlstM\ R68Sy?ȴ(\;Pխ8v\iaF a_§xupcq˥ݖ6^Qyx7F7'5PP~`WG)=ԅ ;2>s 58?䖲BUiKTt]?t(0r;>&ͯ jᥞ7ZWN:&gG3 *)+%~x2w Qywmx4 8ˎŽ`1I{gq96@[gß%yizz$;mp8,r㊱CXU8z5,tn?aӵ؋E;\bq{l/*Nӑ LvEѕ*iBBuzջxHi$>-A6MVQ3q&`ꅰJӒ :1ycWUk1eڂu.1ך g{_k%WVK_|v@Tu%EDn,"@ѥ_ŠG(g@j Bj&)6`n<^=? P\+aOMZ8O0 ")O1c7|Ҟ,%\jliQ(PJJ`_ȇ6/l/,KppHLwL.X'>==<ą>ߍvHAڣpE7H 48ZcmWn5+ I a猜Xz-JChbe㉿gXLuPJZ.U5 Vk9Q-@t8rff( C7FtwL{ tLd8)hߺ2")gs>G*7Brk9zLB:6*2Mb1UDDID韗>vН!흰n]FϙP]Ә73腎"dQh`KCٶM%JB CݘW/A8F|xqUZVkȹDHp!PK7ɘ}iL@!Rk>W T;>A ȴm_lʢVDFN-Y++-/}kyXg, m M/{NHֱu PdT^ w.B.P 9&W\ur9S勔L>;Ho.g,W껄yjϡ]F<1S(kQL<)F3D[Y#i5]4qBRG0"b[B'\V$ 2M"wF>z-h*@@{ ./:- R2-Ui}snKk/ SIS/" (}V瘊9w W." `Bn%ޞgU?z9qwD-CXn"bE"-+`i !u ֥yE ttϪBe .( f>f84MڹM .#o'F=(BaV%'D;xb.Rn I(V[_ϭ!Q\KY(ʙ Z ͵/C(v CPh, 8/C_V"byĒ4F.Ü-GHl4%D*oϴAp$\Sm!\ۍQ{X aBW4nqDO; зͼ: tJWN㇀Oޥ+mC5A?⓪og~Bb>Zn:%jMF`~9ALkh4ˢ9Z@Cb ߤFn1<+T}6sTQNnzRY16]~ 䲆=/քD1QKӅ^Cf{HKǩ_FeOlF1*|KOgy0?"P/|jbW-lDU(zNz.Wz9u0Tuk]tUC'8瀰W\ )WC6`7@(pg/"nMgK\l $5*G3=ґȥ6tvAȳL ᜡSueӿc7=o>[Q-ݣO;c4|A(٧O"#hvwLDV# CI ]pR4fiᴈKģsulSե}py5F+ i]u ;Ă{U/p ]R*- +^D4ЙJtTކ9YN*F`K/X!ĴX !(\8Ҝ \>*5 7{_-FE(.:np3k/ %cZ!qqow.a $ ҂X7zH DY*#)?B]e2^d3&fWU2gnBZ$ 2VJ>zΤQ,( P&[Bh%ucGb{PȀXaCX`Hb 6隋%E=d"P`^zF@6JЮUޒwn>JH-Y6( l%x߉uUuj :GqeTʉnδ/uB,;O3*M_|C 'r+!76݈MDfxPV:brrEaY 7B:u ɈdUdgd?MťʨE8؜;䩢B]%^]T#RZ_ӵ!NPYjF p0{Eet!)畟ƟPI)%¹ql$ΔAMWsq&W73kGB :|"+=M \M2t)YnsrsfwU5'8dQl&rkC>EⶳxITpTR{qs"s}t s;KqBiJ ^f ayƇ]0J$F̂K e'VlI$%!0@?֜V/L^gT6K]W,Tpx8 in!Qv>{ƷQc~`1"s&w>\~Y)@ MFE&RjvtLHu23U*sl{׆nyQ}dĆ\nu"τeteu QA]luc\'٬ CT Z4,%ڔWesLh3.qBTW7=([Y; 3TW>kCrY('g݀^h&Mߨi9bʁ1wxlV9*:אDy<07NZb/Ş/(!r"8Ԓ[ [f!`Hs9U}i"L :(m8,s$BjkDM{^:X|#I6h? ٩$eJ/Y ,a7VVQW2!a6gJD}b_q̲+7J жKvO"m 睢r5C,>Xl]JTh4 ofW(ikՅ]7*x^>ȳ#(&3LdW2_kkl]g4>q; 얠QWFU0&\= 0NaGo8F2~ʸ &'D(^e'v]pUu{|Sp 3{4>56s qR t2\_1BX;}QڴΌi!X`t?6Tmv(+c:oS‹߆}qT;H4wl? utm9Ӎ ēWmX\ȋVOu]c1 ~ tw_<+0ϖOEFQad7J_f >^`NAOJZY ~WOjR n;t^,Nތ|ƔGfwJD<+8FVyv#4eI. ؟>o/:G7J+Qˬq/D=$Ϻ}eZUnzFGD;uИnҚy)ҋ+⟇ ئgaQgL{]=x j#RPa;<ԃs_}1_8v8P3w iʃ5v誾?&U.UIA g[  NY}8w/AB,P gW}t4*/hjYa:AM@BQuPiM?{칦2Gp6E7[Qro_}J+l,%ԭ ٞ{w?7m}Cnu?yj۶Lޒȱgۤ^OWlljV׶嫪Cy0~vb/8N7rm9yJq\eƬ6R9V ntL`7Ny\)rTcB ZP{Y,,4+?%pAN#=tbN#@ېœ,;p׎GJ_"A< ! ;,wٝb@;i%n|1׊${_4..Pu&|}fh^c?t`aQ`M[M%]x9,k_J@ ِ\# /S$d>gΓݶDW#7Dd4{ WuF[Vac*܇f $Ѭm B>cz%: ب0"Cl #bmuR -f{w9y}Y7B7b&YjƵ^^J"5zWXT _>7h 7AXiBXG;`Km;h(q*ttM\&T‹}j^uGy^6)?\LV++L2S `ixi6֛p2Z\RznG.s}o.Ҍ@)ͅ=_7?S^WzZ1AAYz-iavj}iUcz1C,|S%4v6VfY#hɥ9;|eYq1.?#7֬|q={m"o(ñ(Brkn]C̾]:Re{W=>r[AIvǞ"!'X/aLHh#窻yw\O&ַGRgq *k0`v`8fjpQ$R#΋y>6V_lRV5 A7 .0,Y XtjXGǐi}A GR@ս F(XK)* G ^> 08K-@f.Z7.eb'$5St$ƕX[q-iz^z4ho.Ɏ^.^s;6W6!Mښ 6qg9x{{  "܆F &LK[8}oHOueA;)0#2CPF!$Գ}[6%" l֌/nCg'Jb^,@1^eg>r' y|Rc:Xu(ƉĞ^:MepR9&z4u3\DVM3*Y한OJPFB)i f G5KDqws*lɈ^"c'W+#0HFR> 'Wb1xH̽'2FYX;R8ZH$`>Wc鸩Z8=( c3\>*n{¶3q%}<'&^lwnЬ{B >رW2qUu'|ϠZqj_ Malw+l-MphԀZ xW 2gJnc('>`v+ˬYGGOIUxl7f˙,W6.B&[bt%&; /'_vsIM]gLwA2'vH;3<īqԮdBgri;Lϱ9ȍ%Lֽq *;y)X6#PrD}iI]PoK+t`UQ3͡]^΂}=. +.VsÚe޴Nj8E ]Ef"ajϸm5?@׶֫5XVd<{G j!;^7Vɹ@X| )r-ݷ}VJ8 v̶t8e3p ,0W𓃵/=r ZY^ c Xc<);v;~x9ɧzdiwmرJpc8o#[Q<\eXu` Vwy ֽTeH $ Ǎu^Y]}/PɓPJ2a\5(>zq0d {\ _ Ya5LiZERn[J7Z5mO o[z!S/72f)KeȱCa(޵_Wk^6q|DE$Kܹ;6K.E3w?ugdɂh'fR֍ ~$=Sb\%m(Kdh"/1ǜ%'mo|8 8/O^x^@RJt F`]tk<w{,num6")X}mV`W 2, G>PxMjx~>0SMDk.e_`H㻿۾hXDP^OY:JkGFI dZKmu-V#egXO8QP{&:3+dt/dk' F5u9nQ&tiTVu=%F6V ħҚ\r[}!W'Yp+Tx32dwp"]|Iu'6Uz Vuԕ^O-@Btl%Ư•vyAnqD ,b?k246*yׯ\3jn|Ô`_E.|5Bk 9p{3K]=;5+\5]!J0j-A $@ЉQElK@s+Lm#7쇽݂Z}K#iv꓍T@$.zǢ=Z5ܿ3l<UX :f}P jSoRS^0XE;Je͐ i.gA!0wf ~;o% i 9 25#]z–޵258]K9A%?; 9B[\Fj禙"^ceU ǘ/|0w\Tc]9=j{n%DTQ΃NO֚ U:Y2K٨#ĴT48oZ{p`gXOqev߈Ma?}?Q:M QBǙ)9,,<%G]Ȯ}5k'.||/jT|mhLA機Ņ3Ϯ/<y}>alb_1)Ltؓ%"kʣ/Lh^q?HIu2lꢢDɅP A& i"hF]ѓՖc {,e+{| CN߀)%+~$_6㥋o"}Ⱦ;nociƽ_!OƥHL?tr0:V7x!Tm\n{yBC%:b+q%[jv4.Ē$ER#VcT.q#} pYCe}%l8~$lӯz=ieXhfB_L?hS-S0n_嘄gY+?,jaojY*+Jܪ-#3x 5Stxn>bqB†oVKlvd6DB>?"g[zΡ"'N"YtbĊ"W"q)?qlVl`\$.5k b>=a>l:XȾ wJ5QSR89*-{ 9Կ޺8 |[Z=gi_&+I6u~f%IWWіT.:,SQU䠮G] ]PQkTQb8}7S.Dk<~o!Z(Bx`/lӞwDv_NWm6|C N4Y2u7`Ɛ.ǔ/V#I6I]G5ZA>hV@Itn)u,$B4ȇwzX7;Dt$pF7L g ^L]dkk'NCX6G%ZxuM줝`6 oPH\olšά5xOބ6mrJ\k x?A%\fo6-pU&pkv4^p/P=-xc]f)# s7kQ9Q*'^ly6@tzl HVlVsLpFjs>x?r t j޶_H3v?t`Ld3^=>(1k2ce08C)/VͣVd / GmMii/gLq5z>^V$JZ<c, S p5~~>XLriA*:$)͈d6,2"b֩7EOS+y_{rZ߄ڎ&B[ 5Emchkq3^?ΉMtn򻷼IN6XjUƤ\2" cHor|g_-t'?5Umf!V}u Ĕ}ZO%dbrԒFJx|)m&?ky*:fM4VSkM3&*%=lzHY̖M*J#K tKqUCc$,4"|1U/AE\Cc,Dgh]|#*/yo5'z#jO--l큗Mi-G?^kV]F|@@řwgHJt^,~BJi Bg= * |עCy})_phָݎj JohGGs8vq\Lr|<o@C2&ɾfyUHwb\&zd] 7|9QU,gK վ,{Pe''~cA.wn|gv#ůWv)[Y#$ }-w+䄖,]xH]VB~.uOj_Z.P2mIAIZ[D$a.Vʚ׋$|dQkڵP$^#jXxJ>WlLaHoFGSGe0 N 󣂃b1$B= / U;d~}׋}v}`̼Bl }^KVi1/w= H=U0ۺ-"S좳 )XBi+FM٧]L/tƇ *1%%+`Œ9Mؙcak98k&ZBd6bTVdb&$?$t EJ#'^F?m^P"([Zܟ أgl یPcyd>?cZ=+C[XE}kU@pnÕ( W.{̛'&]G3р C\]zDP`zDc-*An54ѥ n|77X6k=d1`[9FQdtL{@WXG0K_vAn*=2rh#" ZSctm  9$KHϦD}nz@) sNMKlq V4v>#m"Cu? l^-.2JGG-k剜`P37kxՐͭ$10ziA,Wq4%ր< SZB&ujT"cre)Z#452poAOYBϹ3Y|jzneIg2Т|snYp>$Bseu{XJh@^ 7]Lξ?!j`n>r:ulK%ttbD(GcKi%MkG$2:"&%sLL D3j t@)U 1q/Bp)Am(lOZ); r2K9I[nP`'١*?379D&+y63-GX!=+OI `nVHcJjk|ƨW8⮥+dTEgtvJXvrU;d$]=!2?ᘾz]a\kރΐiO.YB햢Y%I@KБ$9P8{ϙKu4*`b0ṉjq:~* A7B Xj`62Zh\4pfٓǸ!'Ӿ%,'ҧ V [[Mg4FcVl[ 'D~ 3;8-Z o;<N#FlGy.pG *t|_6ni,!7I qw|!45y5^slru]Ҙldg/n6<A]15*w=h^q/Dgxr`x-$+wLgodc[zJ;<`KzHO4<Ϻ}A"jU,"s<w2拢iF1PLmbxvhlF=+8 ȕ$+_ulD#_s:6Y^u%Ku1MFV4:K:Kn .5 ”׋3<*&\D؟gJ kxp3.5AD >݋$`6w*bzlϣ& LƔ[nՂN*5[Cb:1z-}B$5K@[׻VV[d\29+)eB63UM[{n"_ |-*X=jѮ>3rQi;)|D߿2=MK"TE;@%1#o>@&F3ӀtWizu_P@;֚>|οɮߪ1j/"2?płysb ZAF@=$Pjؕ7z0qIjq3lizáXfz\JZ% $I.$  jo@v}kcUvvO|C[k8 ?չރzԺaR]nZ.!vv9vӋ^~,5@r}(I=zz'$5ÝrYlIzn*旗ϵK.8vqP k`_#AI^B%%kAG'xuQl˿jI5FBG }Yd}2y$dUEf-0lRQM+0GĻ`rc8S˭y7͠@'5+_hFB;pƅ HAJ+EUvB,2f/wZ0كpg91*=0# s}6E@r?(wRq훲.8=ݬQL 0.! 別vj=$™‹q83vOEk7Oȓ,A+L-T.xF796Ð~+uub+Ixue 6%C#5,- 꼭L}%{g0`ZϒJ^4ޖbpz;wtrs뎧 (*6ɼ>p5Ӱ&Ar"4P)FI}@Wre>A_70/RE`%!XnzfW1!,QC9sOT9\4uZAm5rD^EӇO) : #3l 37)\|6eo)~%VqC:d=gLAy>& jڅ#D]=O<ҕw"߹įoKۺbz{^L>whКxZA-5$s]olԛRʱ@c[ឥ$[f侧=1 .ڣ+DuGZ>`飔G)$ <|Gz\ω=-t4;uH"{03%9[?^>4fH٧{; aȹ>f%C@rh\W䤆 g﮶H]Q݌"DdNG`:r_."5] pQFӺxۛhL<YZSskm-ucgORN)@8O~gi !D"6wBxwοx;-\G 6&ovKLw m". +o e* btwDqU$$K0s`Ԃ4mʷ04-OBxQHuUi(9Y3mE<1+$W|Vlr7f̂+6r]tXa{_VD2#e@ËuG;j~ Vkkڠ\4-pj4IgU<$:)oA2L!- 1۶DQ20¸ SV1[H_XeY)ϙ_'WIorXaqdtbnfhm}9%2.m ߝZtMKB#--:)gڄB.W_<>rwIj.r%amh|e8\X\0W3/ZvˠU/!@_&&:э(q wZ`3U-FJ;e:t$|U}Dub 2)7xlq`,ZMX\@v֒=? m;Mʿ{˸ɰ6czr=í <17U)'{7+Ctz?ґIpU=6{K6VڃbZ.S<8pt]UVr Ͷ 0,@(,7yɸO02%oL֜HPVTn;3DSz8j'Q(/1O9nX dO cNx, <++.}Ƕw8Cb]|YGEM'vYzu(4KyYDJd-6;:)hQBDc]WvUR'6ve.rܴz3En_8hcUv!cS>Ӯ՜7v9KC7?p8BרXA$+eʡ!X^Od*jGlWa0q]U*rc 1ldh<. 4^{HhuYM7/lMuH@DuLÎ;VbS#yKa}ilEr%"6"I0G  &m34U;? vCdtI7ńܪN}78lcpVRQ}WwJwmsXUkЇ-2ťJT.%{S$j\#p4ȸٓnӎGY, N 2+mO)s}`3V*S&7멱_IeP/E E-&&AJPLְqoyD*9l5pו%]`b&'ztR.c'.׋i|Oe#`6=OPhU1@ %׽?C{fn gUV}063aD!Mq^!=|͸gPԨHMM9 ܏{udMHsdȔƝǒGծ/pf%eLŀq>zCM>H` `M!Uw IxN3-3}'=ީ?RFΫ!ZATϠ-!&fQ]IYHO%CU3sz! ާ_LHgr&z"AՎnW]*4Q7HV^655P*kie0ȍ<*+4pdZzvXRfCo_%v`κD09m;F^bY:b G }vؠnLȞ[Q.iFSdwǽt^ү9bZL;xO7[Sd@%4QuQ(N4T֑ȢPr:hyww~nlğMp.V݇f^[ƺQ#)tهڡ;ʇu^]R Zn ] .gq5ve^X7|ַ޲f\9K庥O t/#lʉ6&AR{LD+KHOp'"!9A.~+fU`_ -UhU3Y4=.Y,YY>BBߧgOvL1X9:d /jlk¦Щ\2E4! gɨ&4\?: 76g1=29o^5n^( q>IsoTNLOj6ovcS;6ɩB@e?ctsyE!Mq8GO(3b*@&\20Qdڥ]KU3㠪(Ȃc ={yk\F}'#єr<0_6Y Tȡx>g' oQe_kd@h/Y䙮[KsܻqwXMhv}W0\Y7.o,>a)#fb_+xiC?>'4)sT1M3? GB{EPgU}@i&ݛy2Zih+ɐ{ Fd$~"WŪĪ(|pj"$ZwOJvfE8}g4f$T ״51^4.I1B2^ltl-I(P .ߠQ; 7 0t 2m0` GİgȇT&eHC%ɾצ$)}[IfTuQ08c8wz8o!#RO܃@v\fvZFE;Mn 3~wcYS Nx*:2ÈkN JѓY r(:}Cҩ:ZEy]|fPY-ZXWwJQ,7S=Aq':uђ^76gIA .\%R)MVzVQ`BvwWrDME6|$wXg#SaGԽ?52I g`pshaMOU= ^XI)c*.v2/!X#.F\V It]-}J&޸d%1Ҧ'ܘ9k~/X7w9&08K7'i徰¼NȈCk*oUitd$+A)?%t/r5uuwk 1|'d9l>g$TFP|iC| _fX;95SXxp_&"~i I - XƑɿ5i")I62^'{ [nΧq4d̊*wo9"CbUER?;Y8Uj d}SкsQSzP։^g wQscY3CZ;kU 6%3CSjxǛ!j ;#Nօϝ_@ ѦU4ht9/ P#Ee DڱL6d3p 4rMH31)aiCw28K!ScM4 9zDN1ۜtjr@o/js]fF}鸠THw$}+חN7tTR%FH"[MKX2i|P)cdVɩ`bi^lf3Ԏn& P>ΐ{JnyYy;/J%QfדF&I7l9򩶶+k?e2d 2rIPXpsa1-z9 '8yz4%Zgۚcbs P'$IiY!m&+=V^Io$纂M(RD 焅ŕ'[#9f/ϗ+<7& ;A!k AQSdGA R Z>alR8:bJY[췻0*7"?|Ns,Bp~,=0;೼=O!y < 3ek1ZSVAT3J,) YvrP;aSI=ӦМ;"VEn3$4۵ƒb{֚x(hmt6όtn0y 8!9s^p!s?3j3xJMbܱ7]_w<>>:KbhmS LI,U WP(/$Q8C!:l!A7em!`6nbWd p=$Ͱl!Wh ? T /Ì4L*bNZw[;Aח|/:$޵?cWSXa!D_gg Y-scS2"RH f|o+a4F-:G'r1Y$94n/;|渹L{hSܘ%\ cҼ?4ҊǿxTAWeM Km]"`eO;!sEj~Z\rM/>djj?ߡ@(Œ,M̷퀮 |PVR+;9B2\5@R|\D差AH5.-6pruK&H$~׹s[(s /)%h 9s8aר:ACw `m6v2 q `Y!}Gݍ8HH(l,=]+}Kbٰ?@XdEo I`aoi/2Ln0Rt $Q(*̓u{+\y+nRl4AD4䟸ŗz$OZVn u/A>ъ "4e5ef#.V,b㈷tzKTPCN6~}JU+So~vH;k-:ZX?=Gy}(}ȑ!Rb]eb;PK@f)?ZP)JGenNN]y}dEP.E_2|CI$qzUw >H,\5>I4YslbYL ݃E͡#?؈z|pS?xBm*EWJ) Α*pRh1@2wFrB 8 s؁CwJL7YR4}^.G!>2ч1_Lb+1<7sչ} \L+s#x&Lf2;2֖f&p_F B#1Q^o 7p|S~PU8x-]Z%#ymYlj0=D@ae wӞMTMwv-:1)0[x䨢 =϶GhW3\/lh}3>MK OcA!չaLn~֍Qn HiW}D,M/*0vVjIϰ(ܻm,?mXM! FoDe9zT fJ h-{ki=K䘴b߰F'k LQ׸jxGsRᑵ핦lBQ<\cw20/U%gQ /s6'8/|}ܔ?ɖ+D%Ǯ|9_y{ L*֣ݙi^^%u}¸!f D.i#fϩ;m[ 0>T׺O{%\G)X6;k~ppxaDz?%;?ĺ`…%q?z삷Y)ɿnW} ~?~pDeRHzc.+5%_5i %zs0) *2sF TOQG6d"2;,'3 Qn XzDj ^MhC~zDOyWXn"WDnZod:6ltu#$?uflo׮ 6f ;*j0Qthl/>`l#tƲξ{6g1.7 )|#D\c74r3pK:Э{od  1|,KS»2,g,r8[1&χ~c~5DS-j{FHshR@RV37L#-`cN,*k7)|t|>|'@~_`rb̑qּSz*\Z}{h"%&.M}1/b?[yLŅ T F8!ʼ^ְ}Eg0_ivF1DVOV:dr\ 3F̤*ג lY>ȸ)eR|τ~qP$oj&ܾ-B]aG4žmƘ |:= m(zBI}򰌥{Fۦ"B%cX߳%u$6gq{:14w/r o% 8|W׌ :E=&K8xC1ׇu7* [❍ jF@/hń.+>Ǜ78:XPP_7],A(c2IfI-]4XarX d;a .č2_ϋDh ݠ3>[4'`چ/ߎ²F-&{Ď {#a%G~bid'?kمX[@@wU'g|j8) jR?X ֽ$l!7CMZ`& (: H=½\ }soּeOOH6Wvdr/_YQ892Sⓣ'PH}@c}LډL gɡ w6l*qKEŻT^}N"^lLwpoOm%>]3,#\)dP}qJ-$CaGȉf3|ܓf=h;c_8-@S u6*;o Qgy*W885"'< N?P]]g2fw^!59gI ?&1QK˿q@* Wֿz-^R%m'w!KEDC~3Wkm6;37uYF/sav tɹT<>b<酃4^N6&#aѦE_*H&kԏO qD :d,xP<,H$.G\r|G #oI%,RPA9-LȇHoͤւ[@)3 J)T臭CWG [J[}poS@(0yBe5ebZe1VN'ǭ|nKt+Z|jQv$"R&ZclC.dY0r0:ʯNk <G튤۶L?( m:)g% Id ~~q܄Cza- y9,[)XªUvOe@GP p$,4Ffw =T]Ko]k~D#^7z*ٟ1tjT `?uDQX=bjOF?o3Qu(ޣQmLf.TvW'D4L uT*"V@M0X) HmoIZ,{Ε|Nǐχ ]i9{E'7G_g \撑U'%6^͍n"S /iqAã{8B%2J\2eKϼ9T<UH0%kngWS82 _Bt4ȍ0(ڑE};MY%SNҥi(A-f86RqS\#GgWA&زt"5q ?K65J+wsr[_W9=fB)ѽRbF l{ zzU"]c8{MAZ~]lm A0}1/ZiV9Q#u 1NGѲ`>g!. ]a:ft??,H%UL~nsC06crtN(A;q`?+}X4k+0reڑ8sZg " 6gMA?Gh2$tf{#7`6 罋׽!']$M"}KR`SgǼj+Rf%3=G.\rW k9.uQnXALJh1Ǘ+e@W!R36 VF}n%:V09hf'']{' ZSTfG|@dۍk3x3߂GoqEo \b[zbßq{%eyXMT]? .VxOt{L#i \Ȳ2PE?F ΛcHt* C 98}]y!TGbDfZdGK&uk[0I<Q9DOLA|rHsg ͷW=ge_t/ (x,̦ aЋ4omq#D'ۦr?s @ūރ,*lL{xʓ)#pd)Nr o4n:8 ۆnRv/NZ.-i<ҤGeZ6ul}q.}<;±Dw:,@‘ZPRxU6SbwJxW" IJ('W_C~RoT͠۾O~"Pi>wp+S2¦) ~٥ rҕ<v%ӏb2JJ8T|_ČTmȦmeb&j[Ӭh&lܙoڳUpP?Xg豨%:[2ԙ+q]Ҋ\'4959) ?[T|DKwb08Эb:M;cPdkMcmְq`G)%srn M| 4+($Q{ɫ!V/skU4!ZS\ļC/ d79_@M1К,?(fel<6(^jz[=ju9?Dv"B;2s!{ZlUxl/]^QŅ%!ըDh?fS!8 r)jNh\#J9XNbU % z@m0cE5R>\a 1 5E eqv;*1!bz܅Amƈ˟\L]ZC+[7$ygkn*`ּJZt~{7Kzaep6efa(,')7 'S^dYTgùV%q"ZO0T>`(/=L3 =7@TQƷJl;粽KX,HΜ5MzIbw 㤞"p;ɶ -䟛_XÒI(h~>c/OHP`C)wd{{,JA>n a\9ʠmL;f " >& MHnnR؀.TlG9Ŋoggy^_Pui-aH|ʾR1}e9^0.d^0L[vVW_ׅBin3*T-Q`<w_icHdrmZz:<&Yx/D4˿xٱXK1~u2{Q,rR)s7UC3("ek4^Id`sGpbac\]qF_P:9;B廗FUQX65:&ƣ 7 *q3b9r[@ߪķ&Bv9RU#vg\{81PP#C-$H]Ъzi[挴J-M`7#Z6`q$5Y@rt&o!>({9;m߆ @~S\ZMH0c~ӅTO"tY ŬܾbKz;ZA-bǁ.q7gz0`2d&:MC65OdЄlœtBdS"da9YY.5+DX=&ڕ~'9&Rj78$%NZ~ť;LsbwA]D''׬j)AOסNaNs0u$1oDˆ C H#]7tk5F?DRnȔ;2wQ,lqlqGL%PoqE *M\3D ~y+Q}| $C%]~ 6a.K÷K%`YH܀ϩ/ 4b[߆Vw/"McGF-PPS^1A R|WF4GH '6 ֒ю|\x͚'ig(v@wf^aAHvtRF{)PrX=J'Ss49oG*ls)Xww]80Eb8^Q7<p/4!*Wю?s?zP 7Ao|dtz3< ];䑗w;|Y/vsS kEQ@Sixj N #'Ȧs}Lt)s̛{kQ;D$jsd_Ty+>XΠlf$MPH'䯅&hu_2ŵT ) $[V,\uH9Bj[ ~yO(]{ i[D24X͘6+[ x &N*``|#%1sM hzU{6&hY7Y KB<ڎ]fN l᫁X.SYΟuIAݐxtW{ۚH$z0U8/pD?1_jۣF7*t,zSG&~;yabTC&&Y#?{: җ~f)ct:p--]H84ޗ=܌DNZBDWO@RY9TRt( +A lb?ttcba)e Na!Eym4!υl<2Iv|jy͎/_n݂Oay PJvc,#MS`TݦyRω.1m=TV8{Ea~}cX@mh(;ۛ&!ɔg ]TnX>.;~Krn^M__xy3YS 36 M^cjVx*)BphP4;1:M9|,*[HՏhF/`8^:B#X$R?mW%Tb:w $ ([X\#("㜮w& N(r&@y$pjLTʲ^v xxo'zRكipQ$QёF[) :°w;U;G(4_/$[%UJN)uO?14Hi~EQ_*_x+!b׬lI5 \fZbqe=7BT iK# iI?޽N_j c^ZvG-Ð&H|pl*d[?Ht6Qe3-#8^?=#S'4᳽GɥǤkzqώqJ!񒪡vD׫jA1TEod 򡮖Aؔk+KL=򡅦x*kpE.W_W9WL'qpQTTN[ PVhC'G'Qby̘P-.-{PPry|33TG}c[H ^uVTp\1fs5PLƀ#'ZcTvg)М,8_',t ȂGTnQ-7Ѽ:Ġ›)aX_o^u%2$-&b'!C..ts8 ӡB!vs"(W-/oJ(x،$u\9~Xꐾ7(\wB'.`pqE77k AW 8ߡ)jV!>F͊7ICU)BIdn([*A?5?.B ]5jbRƼ6"g0q*,V%_;Q9,VڒoUJ|_?|ՓR׺MVX䙝$e A“fq{PXO#3^䋀 _c)=>^I; d݅*]6;n = u$>ke;o$g yWv:2n:Du¸RF qyef*amKĶ酳s oTp߷Xϵt9[QtẅٹJ1}K=&-XK>\Z$2;ȌF%A &:+ :v\*Na4J:U7EX[}Z`?7c2fDc;6rlPYaRFާk՚ΛLlpP#!{Da .1(DfREX ;_lP,v"h_3Mixq_|0 3MWGlȍego!"{|k33j^XH-l/CD}F DިKg72.Ճ(vY%, khWp@ױOY85 NeZҐ"YSG?[~2l ; w`<]$#Z*q(|pX pVS5oQC 1VL( -밨%_C2X J2w?X29X\TbxnMFjJGXvPxFx s'Ȟgr$xEn'.>vF$E^lpbk!;Miz 9 a}Cy5YdѤ``**-=2r?x, S(7ǻT`*aZCNȡC嚷wx2$ל Ԃx`{m(eXn9Yåܧbztx_1RF÷qz /º!FI5G!m:lQ"ʤ-e] m8.īw -;TzEܓ[!G1[&K*3w3 Y75?5%ѸF_Ƹ3/KjY*=dm I!ms(Jþʧ+ZYiݍ^3z}kGbC*^[L4H+ttHSjb:);TCRLNؕ[U>N;*J*kfSjD11G(.?롊UU Z@ Ǫ1jedk* \~v p[?95«Vrk'!ٙC gS=o059&}sܧ!``Rx :I`[9轣h"\4? d&J1v !=y=S%Mb =g^BDwĜPgԵLjL> xszlQLYv@U%"-RyTukᩞGG =-0@*; =MΗOz-7Ą]Njr$N Z|M2F}FҐ - E. z9nhi iâ.-a݃n,fz9n\`eRVnK1ܺ-F-7ݠS~苏?morC`_#P'?]R-c; egeYv&2@Lʏ==ъNQ9ob[3̌ݬYs5HAqHWS,;?:@D3ŸwTmtX[15Œ]b0kzQ푐۷x̦t2ఋ! O*P^ Mk2 &(d???Q<&2:A۴7&b)DRQlhw f, 8n{`G<ّWIj>!47G4!̪A UFrDNkR^"cPt: N3:C3{?n,J݋5X Iƛ$*#?uk/8X ZvQg)U=lPҙ!UL688dkS#aa=>ةg0;j hۄ$- K|)XOT c1;y׷l!|Sc  (WmӺn=Id@HUL(t*\X]bZO֒#݊}{עMNp0%gP"-=lKr f/RbQj۽؛a=yᒭl<("Ȧ56B{'Lˎڅg"SԄ*N'iS?v>p0+0cJO{ 39LS9HW.{޿.=}& ?"V!W~v ڣw|v5(@q>B R>GxRTa۰Bf@=Lj?hLWL+;BtJCwz$IHQs)x%oR G󎇻vXca-ln5~L7\^ES1b/f&c~OiՋ43Z6}}. wnt|9%}Vz1CW'2Ӹk4dF ,PO M 0_S-Xo[j%l_hIk'Sq]E_ibJã-ax. o}#Ϣ\M 3OF0k"i;894墚wzc4ҵ/v_v=(iUȔЦv%\ `ZOXz3gVma5+6 kjGfq@MaӔEtU8"г4eb|0ԱN >c34Y -7m,ͿxIpgiDњ~}N9r/`3Q*hf!Ed8L5Y6p3(0-iHG')X*WV?Ysze!u/;Sd͓J;+7KܡYDnT۰X9 aڣX䛏 2 > 12ʓܯgd˶fiŸWndɱ}+BfChMe!zT/:17Tڊжۡ ^|#֔)>CP?P)hXYk ) f@^"4dլH.Mx ,a͖"1Pji }J\-L2`$߉u\D790# N'HSiz9s|Qowu^l =*pmsJu (9 08yy3mAo#L3)d{?"{GH[Rc'>hY/\ b ~#$VSm_f'JiqpjԍK`1=ߘOٻxc4آ8 .+bPO{.eB Q]jje}Q9 ^׬VR4d4Խ{FykXEϹD ޷@TgHe]ȡi\\Ƃ5L@HNF-A' 4ߙ,}[v=۞&-5ɍܿFss=z7}i_Bm9抝J^#sAC?l.-[Q1QG}kpb0Vlr$UU5MKY <ԭfZ1'0 rbR4cjX^y;xW>t59FЫ)w xq!G|:+ {*@vD nv"#bCUńNw.vx{Ïn}qI @%fj+ܲQ?>ϴM)#``],C;Erh]Ы͠G$ge7뻈B@n7秜I'wԍVwtH?i{~C /q_U(/X:hYPtE>ڤPeĶsacp#eyT_-x,FE?*Z$jwQ2[sI+S "ցrjI_{GJxaC![ɧ̖mK_( %L -UVH|\\( 0xuS~xd-EAZ̠OD}JZ/^JJ_ku1j%'osI 8nژ玕8} wm\^򲧯H?ItXeoJXV"!ظn$_nG~T eAaI3l!G,pbWD6(Mï{~o(&jVسf.ǮM)(1hR ]C ߫>N pv?F3:JyD )[t˷o}  )qmbX; pm]=c@J0=6 ?D&ڣfq.Z"ߴ d]ZXXm!J.1 |ӕ(:(p&|M$DZ )|@RޠNbqRgY~ , =Cyp]S<4 %$ڂ!8Ξ~]v=TqtXj*Uf9wk0l![hҲ"ӝ$1\er ƁۆR-cm{'ļo:l$W ӑMxRE:~*\ T͚Y)`ꤸ %t%$z﷊3ZjR]h@8TOjsuzC[nR7㥰4-Ŷqu=J{2v [1~gb=ئ^/mp$<"F Q=9yՔӶ׍5i~Fl=fH pݲHHeOW*LF-n/eSh`ixNa؁V'5XǯαuaҭF0l[{pk+I t%%h9tոںg7ʦ<. >%`(KTԥ+QkaǮ~gxƝB! c6=mG`;57?.cmr^UֹV:zBAJd?lA@l9M0yqͬM^|P%4d=;2'RML}nO`^զgU7b=k#yAWx_3 A4;˲$ayJ**R̦y:[ʊ@] WPBpIƥ _E3GdFKӻd|$4V'MB8V4ݝeHb89_# y W"śGM:ze0 \=kVaֵ4ɸ k<0VFr_*TT ChxX#~s rȍIs|d~rʱ4Yq7'OS#99&k! ;Ժ HGqOQ'rD0cM*.6)ȆV+4G K&63iǛcΏ:TzQ] 2^Țn>}0~1 Y\+w Bצ:4PP:"!țF 䐠PG|Io?b^8=z^4Z^ɍk7D5H KI)Y1_78^ RE :.Z~`d l9܄ܦ#MZe/,}41:A_6Y‡'Rw:JR,φ(W MCxM ˋdʮ-|-hC5k Y{ʷ70.ݖAtҪ6M֣(y,.Aϸs]{$Q&^a.Jmn#Մ9ΨjӪ ɦjџ4`J9'14!X':x%Er >0.-HmL`+ +zo5m_ˆ ۝r %ϕzc4o^9u \E1 L Ub=gDܹ@!`x("NE's5t$W/֮`N'&hB X>#grPPgyVH,*J(H3 %%y|sYpN~57vitd{[R*2?3O"X,AP!JeG>M%1/HO5\ղdw[KWa}kۮ>&a{HrT|!ě wv]otZ&Gqa#z³z\$-6"=]#HuP(IR&B> ՛iFlQpv<ױСnD<_MA[4,W~?j>i!qtܐ򖊱$-61?1Eows_c^4It>4kʸSSӬ~gSܦy#΅BGNr {FŐ !TKtc_l q }+.L ^|I-w"Ȁq{35}*tI}%WECb=͑\F˔7)81=G*hR2Υ14?Ý0| όtgSH}ؑn"|aޣ!jh@ YX#uܚrQ=8Qyf %{:5׵Gf9)f0*P%"C6t*dha6¥4ht!Ċ8j!LwѲ44* 4MQЬ)SAݒy >}'ܺ-2;W]]PɓW'|?B%"1JY^==4;kUK΋<>0 ݯ P'T3.ӎ8QYLNaaџi h?'@Lp,\xJ7y^M{ ߛU54C,ʼn)|t_IRw<9=ntд] ftv2.#7&~i&slz0Vvn*Q 9@d,-A3} ޢ=aLO)( g y^fxIdhg;(v~BJ\yWPIqT./KerM/u@e/`fꤜ2ߓ_iz?0>4J|1 HzwsR}4Es3O/`:7hIw✗}m0#Msk`$gTMc=@bt7䡵ƦQ? e1.єQtC ʯm᧼ =1x_7-&ҧ[?5N@ 5w)#= C1حFdاԄGS' RPK|Fqkir<*dNov/dop,'"QoGwxz3`rx> ,Z׋sϼmV&a=FL>pq4=utvڅQI A6єnwԱhq# cdi/HmޮZMY[d2H9tqd⸮)Wf[oPO`>R xXY-Tm-1k^ ԥfm 90l5o.Fm/{֠]պNu x*EyRswܭu7`]gΚ, 2c֣OB! ]u&߁6?< X#EZi 3|1X"Z˿:Ք}6I @aal?Xou $ z4;O_ ͛@4ϪBΟj{se51qfo+x?PcR~퉽9Y@`ʏ9s2teb=FFε*.#t$U [YY!ڜiƘC~N:)>wt 5+Hoc+t2[ )8rnX^[>A^;wթѶp~.CA$ET^S5Qܙ"<k0l Ђx &[5V ^Zfz?sFOlNχ#)6)*K>chn[1H0Eg  T;TdYEy  Kj}F/wQZ#n1)Я]겚C;:*8Q[/'IeÅ7gSǭ%U͒ srF Ѓ/F$夈PIV BD L &421( MΠ 4.f(Q 2Nd7ޤp=x?PGd_Y(7j!y^#jF-N'XhGߡ[[pǰdmDR힁T eɛdEb+fxOSjU8]C\|SvV9ˁ(kƖA | #| d5aҵiUAW;7M'OSY,x $ }iFڹCi/`j%*FF8aޭM q'9F1m4i5Qֿcぴڬ{nH}2ubRW>꘍>[C}וi 64 Oy`0޻* 9Kra致mo5D'koi,TGG;lq"TsA5?g?7w?MO:+Ơy/4L4K0o`EZn.AqsPjcN'Mc=WQ:A;di>W2ÃO/Rɦ];;|aFγƩUie/J(鉆b ADT/;BN>tAƓ~ӥs`iϙT,q k?H#70?D>Lx_KQ UfI9:0H%dZqڻ2.k ~[p* e^?ys"uMۋb`Q$ JJuDE#6B45r`֤ts[|(Ңc B1Y{2 ȴ|VS؇VhnY-;X4_Bӄ*VwȢEimXFI;v@|&LLnc(…?]WrʫZz#y{%l/+gERڗJ'AhSZ6l_gwնI ~jX‡M0ń7FAg )õP>[ &4wNFHʼnDӁ Eۓ(ʲ {>6P3"DK E(^]*;K9]7Pey N\Q0Y/x&%!tykrQc)%B? ǩz2 #$q^]y X:IډN>ƿldLPFOGݚk"b""zHlj]y@Z5Z (j;MQxN[9omTHWƹ҈{}q桫egPI(% ;)du -xU[}brIA;^K[iXpSZp&~-\$ʺ.\d~!T~2-P13ClҔSUv)҃u&ۍ>WLsxgL/T8wc#_aAUq `o{.c&`)HްC7赩Ky7 >nP"cHm7]a(D&:{0>k÷bH}]ʑK1}M#:Zli뤂'w2ۊwy=;.BD(l6`hROr"U.L߈_}8F֐PR1*t H&&˺G8ɤ`d~1Vx.7Ae]#A2 Tc4r$-׹ Yp3dFddK8,\^2v7-Q$*x՝tn;z M(|ބ.k{TRa >o'orEƘ6$~p0ML~2O~+:Isfbq'^ŚYI!kߙ f M1f,,:L{${B]/u5gu wϢ_qa(^s2A& IEͨ@|y4k˦%VǙ ..DVH]{>TPY"$z Oš T)u2ZN H C6KClyY|) Oj)f+\aftW48Wvt^dL^3e׆(˃}B{t6bRnqbٿ>opTԔAz?Gݬك[աԫH y?BLL֬[Z1B2NR/=[BoS54 0Ae=,x!SؒNQYýx̩)m68~*]0HHv$ @+uQz&A^tx J-.=Mx1Ƹ!&x]yIἻsX1.p TIpe=y/@J˘h@Lj"`r$/:$k*zlKm0=t \f\)%-d^1T,d<6){ #N<̷u(<Ѫr7V&$Q7f9\:3>Fi —F݂R'S~X Q7{6j,:0 \0'dQ4M yD* 0:lQbsri[*Ncڙ׸N݋aTuGiuЎ{w gl(1/cþQ:̓s`ْ8Z\Xfu#YÜ 5->gkZط[VźBSOϗjeu3:jo~|'{Z2'w>2tCMR4k^5Ra˺ ?1fʗ#8>MGH F&2ARU47ά_SmJ4ET,x0_,]71RGƕ (Q+07E ZpUm/V:!s$n2'QWz ЎISh4Ż/nХBU_zEqH A+T`蕩#SDz܀WiD.,TC2֕&e#cGt}>K ?L/8$UQzX'C mT\)m[ZXҵF?Fb oFU!4^Y+(ہ3}ymGCsM s0.YIh׏WJYn _ iYom\HD}DU(~$/@neqa~ x} W4*jgZns97N:oZ7hQR :_-;6XI;+Ui`'+Q7.|Aʨs00rɾ@z|z4jԇmGt^72MJּq`kFv 1>l2SUoUn[qLu! ե͗7rV74]J*ze5O.]ϗPai0&\ ,KȟO%צ>)woRsdv=:+.֤:7[M zEvje,:6eO=#V^\9E[e+n U=ܽvu 5βSq> ٶ7Q#9 5eަWyg]u%r`iX}+d,% 9oDHul}dg൉5Fij ;s7?F2NuVr1Mx`;3/E5=G,Fg<ݫi͗glPZ.4ۇriߤݏ9y*Bڄ*Q+e<=qO4>FC3xCMHo_0DEbWfz0FsQP{sD 86f9%@(ԍ$89KH{J v []+)A`xWN鑐 OAgK9m%V7h3~G]>!fy$7 PgБ,#fIkeF9.aJҍbB1 "ox2ТoB0O)(6yqD!m[nLƯZ߿7W64) 6ռ{ dNKa& yM y*AA ٷ]omU߫i `Hi)U 1]2!Em&\r4nc9[=,bWٰk-"5ml*κg˝c=7HA]44F̕7e $wkj@uƈϧu<wYDudxvӭfDn5~۟z("(7\t>-Fb|W|J_xYWxc/ԶG?JcԆ-cu_Hl,ނ״KDoPu ݽulV5XtmS̔.$naXtf3|M[]L*䁹fGI>oE9*!!Sx@g ;ԧhS B,1R uux!{r@X~_ރ 1^Rg2-Z~ %"7nXFH㱪u/M ͲuF\%G>HA,'maN~.v2К==m 8Bawuϣ+'}۹S^+MX@.T%`% z$?%<w$ !-8Ra^ʴ[,V\p[4N@[½_Z 7m +.6XX1SL*Uޔ|H=:oxIa?:u$>"⾨HͲ#d:F8pB< QH 7˕~"ES=n 6zQ|!l5\ 1lɳZ[Vc|xe5lI0F,;XYI,ᧈ[,zmN|2u,Đh&$sxDrM1Yg9gg$\qĜ3$fZPz.~(]3˹}[I-vY:! Lnc Pi/d|5PV\Y@mnNqXQ"Eb ]+'%BɐZOM&c`/MQ^7l3 `ɝk_7Y$ V>TF$אLX hnTV B*"0R rq3yF11&_QL`-*t_B}~Ńx8,4Fc/*k>ッ_H`.*rw>4etC!AX?0"%U:V)``F b3P[YX*w!r0}>ja59@mzO!Gw7AY@4ׅ-MK||ǟװYLA{=^5|?/&&,z+gMS][ӁF `-Efzʳ[G:z[eS##4lV^u""ӓRa lmyw<+|>eVR"9$UbUc# S['?$nޏqrM_Tn=`ar띥XU=-"Nx iῃ8V~8usbMx#G:S`yAbһ _.n OE͙N E.4A,P5rDy,N{:Nւ|y8*ʄ8.I{K!d:܆CZSأ1m,8 ?Kg jr@0Sۈ ۱HǴ|#Z!p7F37F|0j8Z0HRZs%df.ݐ xX{%&qҌ0GCs9 U9sFh{ťZXfF-' Nh 9uXMjP( y0"xDf*ZhɊp L+{$*w霻bçj!M 7op,,}]kɠuU/4F=~5dD i2^>Wt4aS$U_F%YITOaSP4I,D 쭆d8{,Du"X7ط=&s TvǒC͑|{X0>@Q6z|y81қ;zy}>P\15CEO4D~.㯙zԙGBBNPP/ls&D5KYv˙%3k$`Z& ]i(g6b9}.0nb/zhvC[Skxf pѱ~tB=B^_2 A#JuRK:,R (بX1Ġ$ɻ,:d ,>/˅,yMtt1SE#fǢ(As \YZٶvG9O> {@ݼi!WXvophmq=q˧@׏swR/zjn$$ FuogTBu]bcn/Ȉ j9;ao-o5~+EGIjΥꋒ641z?5HM~~f+Ԧ-ޛ#,ܺ{&71:[aVo ;vA'$4ˋ7$I ۨjGyo;^&@"7<0IZ%XJދ{"X:,c, ]ވPI6'CPK̒u% q\y7!w%}j 9&5YBUG"2}FmX\z&2dLV){!Y3'֕<[:f_dHGfOW{)h54>˜q.>TTrm{,Q4 b91\Q7_UmZ"{,6 v i1e}ʇ7c˕\e~@]ki18Fh79Qn"k_ ਠ ,r I'JJۛ-UM+2Js RS( $v>] sm4:$H[[ D (~k<4k[NWe]YէT̩ VZS:_@fˈ]|䱟(ը8UL7jnf3<`)+i2k9-_­⧂ѩp^@_ΠEnǥ̧5Ip3DnNCM:nM?mq}_Q)e|jaRt,?FXݘHg{:t#qH^ۆ[Yִ5h6-]~S+T\N : UCݎ):_Ԡ֠*ә}6`О-N@lރ"  ֎0y_ `mφ,Jp;k]$~{ϢɑNZi" t.ԑtOr.Gr<`s4mǽv_$J{6{71gfhL,##x'}P 4p_ 1}8MF@Gk5\%%>RM?K䨎H!|u-Y-~G P=+!4CgeђHq[$1ImCk2'xℕ~?] hM+ nĸΟ{PG@()ơZs.&[*e ]_m6qcfQ|Bh؞X)l@vC{GuBYKdV eI&RZ,\h e$1wܼP,U0APaЊ(m[Rʋ 6O _$5u0W]E4# ?Y#)m 8d7.# ߛ#tO?T׈ k6-@R!5fjDR$i,)Ujл@t ڮvˀY'OUoO.)-/Lx|S凶ދx¹жjh4w|a5 ? tsj}mK%@iĕvQg'0Ҿo!>_i=W\*t=WDwO, Qօ4 KpK5j*1O 8nvn"Nb 5u}j m;70WȲ~i*ֽ̀ yfӜ5f? %(.g)3bfj0C2*=jR]$ܡ(nd~+ګ Tps(5Ud~65GҦU~<{SIR4MQ=(q52 ˿ 5d.&ьjI6ON#R4&:~)w!"@ݭYpbh ^ZF{n7i(E/o&L?n"XYhWÂf%K>~Ϭߥ$g!֏!v?#FDe&gFQr%{f0-1zѽ^1h>fģB;n2(wP>h(7ބ0r(z1YBK/l:hF\Rg9ObKpúA o# (i`Lij~kǤ!^P,)dDz=&ߥ+,ɖjͪvdcލ Ȇ#9o&. 귊tOJ#m]@kX(ʖD||l):EQަTH={uc@(e=墧R0R*%\0v^= $qxP1/ÍMKWXz<5M# 8@|lI1\B5fzyЌ[| JAV>"Fˊ Jy P"j)No_vg/s!+Np0k/j{2Qity[7N\Z;|a}+fuH7[Rԅ~L}&/ǖD2h$ЧxO/3+e=f}mFB!i}CVZ ¦ J@ Aˀ,R5Jnn/DیTtz^CkۛQ[̌ 77VoRtٞ9[ %썦3 &'[V\ $F_tڕzoxG0)EGKz"*I5V~ͣq<٣[ADP:EISz?IR\ D-X4Iߖ9P%@Ѭ?QH;aK"c|,oMo~! O'L\X"X62/. h\Ӗz VyL2d3谈ln+w|9kPOBfdn iNQD܃5W`WVvHBP\I72Xw9$>#l6ݟu &:~ ]*gm~hRe AÖI{r{O Jz_`K5?qx=zEh`'GW|J \ʭIT ?C!3t o%W\ӽOzk|)y @W|@&ܵ!$-y:yd]z-Ҿ54IY]Zͷj?xN% TgA@iY XTD~W i;ǿN=e@XƓeE'Ъ!!Vϖ).-I2;m'w *I[lumN3v3/ЅՒ@k(~/q^4kWl͘>ϖu֛nvl ;]'60Cؠ?s zSjf3ݚn.=?nBazoCi T 8jRf=wڐ)${1Ç ƛΟkfPxg|l3>fMtc$^Kwt+҂{HTe\Xz4vgCr( ~!2İVF繡}`o9]*i67#bljg)yߣBV}67*}i:21i'P9urľaLr[p~!D#~ܳ0XN܉ 70UT`"UGmS&uɟ!՛fiCDi%~+QaFaRa~usl"'r O'٧춷U*1_Q8ElckbmH^M w9-9GH BAy10c(94#]{ހ _ѥ?_pDK9+׼;g|Bcuik etHJQ0/4|._vA#H%"Zϩ7I+7[&75!"6`4]3ÐiMIF>%EmQ[0qˌӏ K( ,n$?cY8IeWZ_a8m.q'SҸ\:{mw>6ޜ%8AԓʩkܬKuY扺m<ϲ7I4xv 2l|*90-A+#jvc_D8<9!z:}B5\"?WOi%pAM:IzVB_gI /-c5i{̑Ǧc=95jݧN{nzjŬR:|6LHӼl'ݻAس&q,# Ft t7JJK:߮Qr;q&oP.l$n_ :>L6 &vAgXL`BϭqgD^-S`3NMF[-3tjs v}*A+Wx9zyT苰7:R=.LIwKG MaQW\^6XGznUmP/&FG<}75xܖ;:sL=1srHE iMg5Uq6E0J;_ǜdav,,攘]T .Z!Rd>*p6ƴiZP]bjxkwJײX^+=QCyg˵ ; OrK⑤WY*tNV"Q 2{QWbOWb[ cz4;vg[w,N匍Y Ӻwa$P+þr/0R)VǟdMF(#U F|L4u7qUd{Q Ϭ= gm 7-g`f~;h!%&ŵ@17pJ)ξr7`XcdL)z~ƭgM)ZOY#d9HS{NNx u 1A' g%6_,'!覂 ?E+殶c4H6ѫ7C.*-\ZnM%T?;у¸h ~6l} PSj{z2mQ"ݭ1qv#7lbhvYġJSD0߱:-'V_Qߣ?W Fl縋WY^`+?0{6R5l}9 ϯx\.b|u#^Ay _zyUg%3J!BN>S@͓l uPjSh৪Mw?i><`z+ٳ-U7o!&:|2[Ɏl:eŝ]X=*LqεiUxg86?{LDX̀zl] `w12-)s1 Ҫɾ DZ4vf}>;H*q0MY˿V/18)PǭR Pwre}B+>3]/4V޵ Q72PwG8J7D .neГQD5H͇u0YwaL}v84bʜMva#BҊ\!:7~w_`5Sx{Zģ/Xt_"gf[cO=PE v4[9hP6Ծ|bOʮ̨ 9?$]-@딞Ry؞5qi Vv+91 lb!&~ngπ9#gR" } .y_㦑&,'#ȱ;bY `"0OZM9Jmyr…¡7Dz1`w: +kֲ)ۅ VpRS<,3cAUt]i6tUӀk!ĵA NnD 8MHC'H2'bF*Jv3Ӛ{?Q6RAj̀ǰaq?01)Z47bKoUqp* hH[+vj&6NRœ&`X {c45ZY ^ O7$D|OAF]g9:qwJsFv@}20?.ez5L} ڞe҆6?PЩ=j,;`(aC{7=KGPA?yMǴrnܸE(Ov[KEY+MO0F\=@Ԫ$:PW`O1!uR2Zܪ%ya7|uH;D`/'&V8)B7"\3RӊXuB'. 4sN61A`' {|'Fj!emczdZRs l,-}Qw4Op#:<)0GZ[xgӘ7/WS+~aM?gݷwtqƽl{e$[9'rgFL| PY!|9"bZ@SjäFԊ V$XEՇNVRFRV~ĨP XrH(qL6j#V-vH-u=(UV _@im_ ̨菚$Z5[1b5%1C9B5& Q|?a:*iVe^ nIS`PاqsP9zD lOZV!bHJBk'%@#h_ڥ|Jwy4QBB~p0lky/ІN&ؼtg@P:Pb)K4`p6 mqŔ{j{`힜6ꃪ\:@OȕA Qfee1B"iu<3Ro2M+"O|%G=±$C%tJk1 H>֪ !|kE]-[[YH`g2%#z]7LsCD|j,/SVՉw'?S,Q*[F78]w#F#77yt~a/m)_ui<\'eqieww ~V?/Vfa8ŵFmgOV7nӡSTkw3_* 3LJQz[<}o5G`ZjjJXmAZ<;UY&r?ŏ)4xEh}eFZ.7 MDWW6KC/wmlBU*:4|>O{k$5w36gdƎ1;mX9:\N}WA·Jqu}.*2Ӷi٘L$!30WD(?ZuzeaLq8#c>֙rGv UZa.~f( #ތ@ <-?ubxjܜqX_/09-6G9j }߬Z!;B(klӴo+ZxאO%_S]oߏ1E4 Dp(/2}^zئKU >V),2Z/{1iC^i\g[sl [ɻ _X>f쟐(t?No@ Q=f-od4_2|$D '"E3})i_x. ĸ.n$d 5Y O:!?bck3R =oW=mY⪵.J,W]JB[(a4jk/O| 3|~N]F@D-Ju*;̳t1_{!Na R.hH7M59 -/<Ygɿ_OKn4׉J+$KV`}`I4Z$fl$ͅ~CG}#iK%iGs<˭ચkGXPv$_"а}ڠ'nd0g#G6)܇#.MMa~ ї&0*r_o3 +3RpC@yFArdV#3miȇ8&fڨBlACޙRMq~ r fXPrQ6fǸ+2AGKp9"px|V;[ߋ1M'5lךH7T-)yI+I8@Sؽ?I_ҝX6G~'?؎k-12pPDGYL7ućG# ՊQ'BiUtH|_IHx&T1xNxlW&r#۬,ki@]r~0orPT]^r#\bنRޡRjymބ5;E,n5E`T )%1A7D_o qeYCWJ}ì&YV/ڀ`@t8hkt^`H + yA/"ctmUpbX(i6X.zέطvJjHQ uD;7PLWch$\'K[.V)9'ʥkKQR2"}CQnQ=7(+ " p\V0۸x8f5_V te?{=^B,scjC*&j>s:jfvћi)e~ٜA[Uֳ: `@la쑗,NIHz;A pO_} Ӽ^drȄl4:.YФ(\&+ E mv^0 9ĺS r3?xNy\Dd㪟><#KWFIXlb&%Ǖ$et#ꃗn~ 4y8÷HaR{Ik=uX~Thp7E}s2ϜɊ # SgZ9J@s2Up 6`1 Z"q `◢!ᅳ#k c?oXdjl|&/B $?xI{@fIw(ڌeŕAwJxh* [ߵP rŷ'g'#IEb 7I duw$MG'~8J\_&?+K'L{8hu҂'LoDC:\[_Ci9Ǥھ7gRĞ]M~tO^xW>ztrS|@BKa;5FIH m2 EX[|?j(1ݵR7IHE'ܘ;4É} JFpAGp6[t=D_z rˀްa,?X :GZ#0[X\/54>  4-a4$@oO* J0rmɖlƲj݋MQu->vN$:xj|K"4Mi7v΋,&Ox[bO祈Ƨ\lPpˡLy?E-&K7'1 f#u*!vw _Ts}#- 1|ͧ"zBN YWA_V ef[m޲09 =_Z@J3G-F{‡3IJÞz$ڙH2rtSAmxgU˄TȊigɑ>E)3&DnRÆ`Ą{z0w%l1:V@TlmpZ,oALtAMx,"EOd^DG$\VG0,ױûySfgZl|$!e`h=L`]"wx]j~i mI]QbwӾ 0^#_ ɳYZE"`6~JT%WD;|&D G f$q̣~%pt[,nEq8L*~؛Q ~AYqgW..@TPz d]FyNHmVMߒCx@N9nt= &Syx>ySZUԔJ(VشN)! t[ v[ 話XyQ(ރh*ܒd("ךuGۘt[^-]PST3L5xRih/,+}uIa)AFpDԮv,krJ$˸#.ŢӥOuA4'/W݀XA ͝vGghxl5Q̙*m\2teج[UKWoJCQ'sRDa$@o*sY8LĂ9Q0F}u2$l6ZoBjTyTr-~*)!X\L쩌 uK(㌋AOT!:vPvu!JZ<P)#xQXX 2FuX0NZ2(OlH0%L8`dk{YJdEr9,||lCs#_Y" <-_Q$E抔$ˈ>NG{ʺF%oBp%lbJ{ iLhBA cQ6u}\ z[ ǜYM"qK4CwDc[s,xy(ے<:3];q'FNk"㗟਽ԅ|9M[]jPwJ y0bZZ IuhEF۶ 2`poBF#U b>C^oݣy:巤ߡժ4*J(a7tܹ aP;N $|N(1|.*1DRAE$OAR4 mB SsO %yB\Υ9|YI߄TʇdjDy=ʼnaa_/5-\Lf]h+["RyÃ/z6yJs Qt?\{e%YQKEI#Q]0h Edݥ# "Wܢ0O^D2Ca.w3= xS $Av6jXK/P-ND;=2dOvbY#7 tu_,\ʪMʴ6y õZ=aM4U2'Ĵ+I'j.e.\5暡ڭ\cv~}G"36_6ԓ'T܀0I^S:'PwZi|@H!+J(ۖm`sj h!` :  _&Ӈ8O͉+Z.9(G &}{W8}!R;\.Q0]'D) 8h(fľv5kJ 0qPnGtJW vӘWMW^ Yi5jY_nJ.+n2s 7T1!&.8eVXܷh_P;=>A3gfSL׹VbdXq^ahěǻfugIPoaAu8Wb(vx?Ԟg4S :'('!O*:N(ς?{Llf٬ܝ!Rq MڗT @ )FNc u7ue' ~]ȮM#1?sŊѕu?8& =UQ0l3[3sCʥs. "u>J^+jlnq. ߸x^g W7ttvL̸s8-2fZ'm`sfV3;FÃYv@Rƴh B<0-C%GR@EGo)EQ1-0M>ȵpˈBb;#vK(i(QC1!=|w7(n\//y1sY幸w_t) $l:fd+`S@UoV,2щhˬJ z寅޷~Al;M x'_ @@1[u,D<V'fla]Ǜ;;g,Cx7QaslL*aE>Ռ`v$:!ZǷ-쨟G;'ײXkBh bt yPYúb dIKzM27bEQ#Y s걫;)A[Dzp6o8=TKP_|B6>p6`-` t㮬w| zCDŽZL.]ơIh ͫܚ{"cO_Z #iin.>Ҵ~&15WVn aX tUFo[@1k* An՟3˽Wm>xQ5+\[_(ZZx1܃%+ߦɫ*//1%Dl#D.W8?23PMe Ykv;W(cf^ yN-uP7Dtwh75eB7Qlg>?h@ Ŷh3u'mW?cš[Dx`5KqGm'\ N2t:(_l`Ep@Is͒E*;U}oLB zA+0q2?VGژw ϯ*N%bpHs LZU/ꪘuM62~ |_pZO}Un6v'KXTm)G]c2"In` Ơkv'Hbk5@nE+`҃bg%K^R6ScN}OA[KeXe~ovT J׫dҘ j/81N5xj],`5eTnl i'R*h @zcߩ I,5w—Ye@(Lm5U waڤkbhO>]AlBr߃fKhރ[wfGaRl ƙ%=mTMlC kPJ'b}#V~Ac8'Z'ʛjCPdZ*L5z%^8TzGUx8D>YoXȱNik=Y ۬Fߤer-xKR+Ʀ=o3Q8c8l5A}&x}8@֓\HC7}Œ#Vl*p?.[m= 1׆T<?h^ɜp0PKzy&P3*=)hͪ|r 0UڪէS cD~⹗e`A+Wb 5oQZawKH]P[/Zq0BcF:j.[IA}@bN@Yګ_LJ'nnBrx!`؏ L$!6[ALlK^Y?l Q hzZyH| ~뭰ɻ7Z& y7"xRi=@k`^fY|A_cQ,7e6|WmU%&CW2;.;:@)OogwJ+**˞}oV1:ۧ%^+Q_&blI?ʴ9k4an\xYBuϏ<1x\q~#lb?WΧ9 樧?n7W'_}\.VZʍ̧zN$|nH!;jl Lδ<Ԉ|/FSjN8iBZpy& :Yَ~huOs6vwB@a 1<=$~q<)g|(:$ZuQ(['b* r7h2d966{_ҏ6Aѧ"TVEwRx́=)S10y.4k&Wmf("Nw.l@4TuCvFvkKp(Pe*X4<[9l ^P8eӧ2+0`[-qJi<"Be t.HMlŮ51e[#*R_Ճ0tF&U T:'b1!MXqYOeK%lb5 a"'G_9^vn glbj/J͝10rͺ%jH PXIr* Rbr7?/ RԏAg(K/j!#^qn&۰?5Ku\᧖Ilt o fl*T:SY5[dڂaBPF4j(JGabe[;`LG//)WNG.A%b&H{s'©7? HoQh,OElrŤa8}duPDHZz:y =N eTHGMM|j|bOg}9'CÐ\CGsND|e 6$4{3+*4J&@d+b\UWSdrt(Ay٤#)H+.7?.(jMR0S% ^ PFغMayE-xHm=_wY6Xd&G87:Ak+:w]i@Io%VWS)셧 <ѯ;b2!ǩoqLb2Ȕ+I[3v1+P/W,YKU2Ves[m>m)~x/ptTPl!Ds/ {iUk5f\nZ%Z۳%k!%X¬̖yZ+픸PoH{Շ=ʼnL7ؾāJ5Q@Y\BH<"w\(Xt_#aKM ^ caؑuRE[Z%>U\NyWշ9sI,EsqUYw3åtEU2r~d}aҦ`}_/iSw- (aX̠dTh=^ӻrv @BڒQ<)đ_~ +@ja Gl\0QQHC~p4'JTx/y#.6[gP&F7AmV,XQWU(Z?ԸRS0BRҘnnQOIeLgxA"=me2r&FAˑܪ@S!nMg2bި&yn i]W 'q(BŹ&Ůs{1VLD7)Ôȓ 8`2Ŏ8DILCGD*!a+jR ϧEl?a|/ƌXƲ b%wiw BC _s$:4(A)Ph5'WDnSNF<㨶ˌZgؓSU 6 g5RCXB*L'5lޗq*5˜AVWb(zɏ*b0q _9j&=*, DWm]} Mހ" A]YQoE{5$J w LJ̹>F\rea N,ڝSX"O?ZB^< G Q2 Z}RF6oegD86iQn& a7K@i.џv rk1ה-|0OTaj(Srup5us #OIjJiugamfFrRXk.jjr#߼֣_E*o J('$(Q%|%O} n R y;ί|{28 %W-7&]SC-`I O 5ȺAJeNd}v*nmh *&v w$46!THRvw-_2j"X0) FKT'" O|MUuHwۻ?iKsjSJG7ڙk[[ȇr@$ ǂ{>\Z3#3 0U81ЯIܣw}0DS{5:_9|p3 atUq^y:4ۂvz:s%:ʽVNl+ppWE5o639HAwSϮ@-xU;e(,4Hp!߮e"H=lSꩭ=QwbHxhfv@lY\ˆ"X@One/@LzVE|L5ncTN&߅E0 g'!av  u i9~-.noЖ~LG>ȉﳶd>[= 3^QygԦwg"NCg>f!RFsbU Pk( 8U)9՝ YPЃX[6 FBY{0zN(b>iew_ cS&.͙f79Nz-'{atu:U06LWY>+B_% /$<@klLok`沥N&X:ڹMľ[~M|07"k=_:CCed0ѭ3)iy!7~4?fϷmd< 㣠D͌Mr7.PJ'G*F^SeĒ3Jҁj/ɲ/,J7Ԣ6Ր 0Oe+|U]Z^2!sMhE={3K%N ֝\?{_nasEW F~/2߬hCf~o+ TuQBu]8*obÁ /| QC X->&\ /p3حFF{Zzh6LڹQ>:7׭rJϱ&Q•Iё]o?wu {Vu>!4&k\~AʿrpT85-7G^r}ש[1faa/U+MjbRDd@O\<9ri>BOMh ֖u;.4B ;aS!CvTLb[7#6tya!D똹AK5R Q5U@JVh.]Kzr~.)fȅ y/(FZ㟃!UD~*xN:'@^WmaY':)Y qe?7@RSmt(2W##kSk~T_1l9?}N^/>t_B&iZRՌVk3N秔{V{PBs4^p3nc Vhu'L<7sQ<*n\\ P#v os_~ű _5d[R!,E/KxWo8fpz 'ы7:ڨwP2W^^vLV,S4OԣYe9 X{ [DnN6Vʋw 1&G|ugÛOC'}111RI`!r'WZYu S򿪻Ii{ q̢oV*?KD5g[>'@$\poM *:~GPG?M_婚0uON M Gr1DS0E]S"B;S'm:@4(ݼ"*g+L8cE?ѩT/ A[$0 aﶢ:0[*˓GL Pv@Do6H'm~[*?h&Iq${V>"[Ǝ}1r5?l \UqT]ra'uFn(Alk., b=lHiuĽcqN=Gf":rő"1rfr_o 6aerac&NM Ey6TX0" Tn~)S? K>.2(Hk0QTTG-Gs>xA{+\ReV*Mlū>7Ȃ>­PEP`'ywtnmH$GP8Wj~M"4;c |DNŧ{b%[N21KV ,9h{#u"w۞ ~q3ְߤ j>x?Z 퀇d4[fg$Xj_O08^Z\1>(")d/ ?ႳA حWYtII;Ԗ. AR t9ZC6o/?.Be~Qd^GVJJmp%EDךN)] '?the72˱C NmٽW#3 diM> ZuKx1 1OվG>w{ӆ^swXZ-3]7=G_9ESϫzCꃮ4*pO"y-68mΪgoWsh@DEڙTG 1$G5jQj!G#v6_[Z^'{%q"()ñS.Q} JZ3ZJu&X=~HPHgEK&K2Mh318tWlWcgM=VnhRЮ8 +I BrXErc3`j_qz"{Úτr,}"fg5B426у:D9j6zjFK*z[X?aFBvGZ`bcT̅!b4:qN%a1іX \sn}DrjT=`RBυfnaR8&oZ%s^ qy %I/܇>EVjl%W.QL:.w7T`Ml9'PdHWN cPircifgېT! Is&M/剳6EkɲW_dX^\))*&]E`L&sb{ql`UHRE< :3c?"CXO\RD8h( * GxE34tTdNx⧌@OXb=up>zQmB5c7>&$ʜ )e @z$K~íYFiSFK:%tٵB €2EM$ -ep+{?'Q؎Xy"uTG0qNY3jT3O&U(z?l[h!JtjxHRPV|oN$!tK"rE?ljH~tWu{PE?䓰VȥװaKk_L H-]"ҥ8wd=>J3G ͻQL~}ɚc'mo@`+n;=+c%A@M-ilr!9*Vk=8kߠΣTU^o %og@ٗ[f}xCHlx[Oq d8 gQnTfV7j|UB$߇f4o.kk?11fy"Vxp}.j"0ŀ,qO(XN)=7P+]nqշ[&C3&k*! ".Ias+rsNʮXv-2DW=0rˢ|`m =ҁuЕLZq+-m ?KNŢny>wd|  ZodP=՚mf]wL1. A|dXINq?n_lzz6AwPf N$qw{N !d۔N)5& 7*g.7.{8SEnMenxk j 0г@ 0AˆdY K,5Hr^\` c_kqֹXV xmK;~&.[@:*{u"sR X?*Wj0GYx@S9.1*|=[{!.қRVZOǦe#eW,'Y#^ \V֟|M*$M 5,0)qNC둭"!ucF;qxi3,r`O5՗!R]2̆CJ⇱E:ƾ. L:.7S7j,3ȕA/'[C6آaβ3 ~bY"⬶e\u2`ʺlX5/c܅i=n=ʁ})ѝIhaKjs^ ekq wBd%WQVh(:KxԀ'}8gNI uƩ \}Y1R"jvbX5̾y>z2qZ[lfkDL6=9> 8a4ܝH408;T <+FɵxXz]20295"mνO~*ވ? UA>3OX뽤7iw6]u,m{CVgFӓΝ1fcSarVcନF%NJ'Zg|BMJ멑WǝUDM'M1EP:e3>stG"z!ڿvo` IX6_$l&Ħ 7MnRoyDIjL6W8֞TuCڕR<ПBTTߚE ՖG 9}x*ԅ C+>/zd8=uQبw)?54}%a+PeHvǺ)xAӖ_k|TL|fL䦚w،t;LF1ɌsS^i-M1u]7A*Q˹)25MJxnLB$0=!;{<ȭ /.ȡwJ++ۘ)c+s5 I2];Ƒ ͝(njQtQ³ޫFKЪ ۋdEH?%z[$߀YF.}<pv3 e0:sd~ ύXQ.eXΑ6#+IU_<*Н!.u*>3/}fj*͟٧HxOk \ n? a=2F"Hsd >wP/w1mM(Ɏ5-xW:'ݲnj}bcqb(;km#1!aiN]z*elZ41zoܖ+ ѤT$ݞgҚ5nn45pK/?w. >yRw4ڳ-.6#6q߷ylr ?y]uؖ @"" [в7֖S?hh-峷+a=%r_75`W#sZ>C(o2滯@? XCh \-Z:ޭn*2qDjAN!j^+X9%zyFyX/l~u1lxYH 0T|$%;sTnX1`\jmFix~7PA mP<csIVr*lJW\!p}H-5.c:>i2QX( P4d9֋; v>̆hNFa-6,61@' mEGi^FL]'77\򗖍lü}ؐWx*A)颚/2P-P:ZTjg3?z(a !D.$ERP,Vbt~_*o72r#cK%tZ8J"Ϸ?}6xtEpF^˪KDNJ8ͧj r/ VF L"m5r vT@ģ,3`ysd+(yY6"^79& <\bto:DV0Wt'wK.76x;hznѽq%)|e1'\{)TEL`7S|&\; 3u&?.'EKwaiT)>o#r׺`~](mm#b6}+~~bCX ؊XtWbAwE;zse[Z&:䟡Sl;dty@37Ҷ fY پ<:ge4#伜Tᇺ 7=7ZbYǧaIXrt~!}fQV- M[_F9S阈JЍENWfF8@~=sd L()C{+#VА%ǧ8uIVf,5!\oTPXk\G'pę>vȁ^ZRaqA&QFS~ i,{;zT^It A!Bs3oFĴgZ7~;ޡm{m l܄䧭>py53܏0F%MI6UgiPeJa'&ܥ+!d/9m)@ ;(˵:&MwZ:?x(]5>`9+-78SCJ:l2g1E,-4oYrٽ-5E{* Υ) *7_ bҥao=[` ƙf =S>@/ 0vze"N)Y撏&?T{fH-6>nNhDNJANUA/oZnRgH-,FwmLl!} 7Y$/JPLAȖDAV&OԸYLN|(M#o~P4 TM< .,c/2;ʧ"K'$4(x+FG\K8e0UHԩ^ RlJ!d%1i.} ̻`snNi.nQmс}1)+H-HYHWZ*|˰(Vlk3͛<1:nt_Cy B4iigN,m4K4 ~Cqz ʰ˹&N;-3Jr)ְ@\uFA_@u5LQJ?$&؄6(ٕ b#ݖyBRGa"?+G輱mWOЅ'KG]GϏw]ʹ@;J* g<-Rp[-nwl0[ʅ1Iҳix] x86S\kjb wk lR mx9l5^D6>63bTƖc^M,` C~Ai\ *FރŲ,ا009I}>a]՛o+Լ$_4QF荔!;N^u^-ufYMӆ7deq&QCҴR0ۚUcuy%%tBu D `'۸%G\Խ~xC)vZ@NR͋WC N[z`% `6Է BQmiwbr@NSj;(T?Mѻѯ|rְfG0 BuO?EXU}#Jn3ڿW2-s̘DR''h9-eڕY*PqioD$E fC[[@=HJÀ=T.iV:I h^{>v fФ܀z#nIlaZ&hwv @C,},\hw%jsiA|mS(>Ҍߠif`o,닩8P0sh3#$Z ͱ#+AjDy M:! q܂_6E,DS 0)Kp_Ƙ=tfjhz;ר$}Z5YF,)R=gRK7qQ*߹nA$s(}Hc , B?;\=k3#۾d̶v 8wT ?Q @:̖}ʪ RT>2(~`]A${@kI]H'$ Z=b=ՙzb)}ŜN =[AϘje";@[{b{Y}IRd63c>BJjO7K/҆b-NZhlF80S A^M@.f3Vu933߼K ѝ4Q ղ58e8a_^ڧKF/j1ry-j1 W?u #gjA焋>8_=}y'@$"3IlɌ/78+ B6u(:T|1t( Bvn; U)gkbU*\㈉Rp+D/:1?R 길軍ZQuιnmIwYeiׁ\9?ioԁ7Y&ڟjIRW|2NAZ_md& 8QF=gH`I FœD= ͽ&f.%49.ԎF{L[Xp%m^S5 O!/*4W| Sf7V4{$xz<8nx,(SaTD38@Y՘H`xJG2!rh(ܶ{^-8 1)6A辿*N }UE !Eȉ&myVj:o)]v7\8"Gӷ1n>3G@dl_8%x\ \8^K!fU 5x|V_炱d* 5o<~j llg0$n38+MR?ڹ|OCɆ.';^Hށ*@-ŝv{{#MY{?\eL ֪"sN{ةGΙ @f'X3jDVTGZu5q%2ĪI:/6[},L8;q=%,/yxOjyfJ3͢f+ (ꁤXHtQՕD740|@tӁx&ueJpރ[]| fՏ (q) THH^lDŽ ւPE~k( ɧh; -=ۚ˃v$YYnoj)D{'?;2AM/Vbڭ֯ Ѣjh| ֖4槞Gj(u}:F5dt96SdoqYlaWMmZc<nP\GjΗokJdS1o.yPpSebF{YMXصH\P=^j"λ)8e]Mmooz50S(MS' Xu sO𼘹 $s?U&5n_+|NME̙4>{<@1x!%q%.̋%>ȮϚѧ  q,HBKI=M9Ǖ9hO{7ewXߚi'!u2 7bGΧXX E"' 3ik+y3A\~Yȧyb``TNԉjQ%gcuYSqo&ReEď,`N  Hӌ3{qsLȇ,Rc?,ޱ)N5y A[9! |uH(! 0GB{'z̹bItR(ŊUyzhy%4M+7|uZDf\1}] ЇarTtWZcŇ6$hOg:eN9k8Ǖo?FUu 8WJUUF~]]?﹡@ľ0ɑDAA͆B*U Y^;)`5vP|oN"rB]z_к~Ecc(l,FQ'FV BO~2^9IMd:^r0Q5QoӂTxmja9gCeOq)'j 3KNh#3bO i ܒHt>wɄC0Н]sQ ?Z vCClÏ.A rSأ Ao#N+K=jn8~t;_ѻO5L?PJa]?g=[S_޼HnOu>?-72o}G yri(6 XRV={}5I7eoV~I/72Nu#6ƞcag=T7=IyvVYHQ E1gEKi8{8 ΠzDs˜XreBTA~aA(XPj$/ ^TT ylev>YYpj8@M;quQ/>6Lu56(P5z$lը=#niĸV`.~SX "G~=QQ. /}A[ sֱFpE#p~ToDp5Q7SO*,V7y즽= h5 |sW%: P~9 sSߊj5~ IQ> ?ޥpB2e)CY$Юk`Ҿ7B'~G'8WFTjho]"< Nl}1T? {2N %h%^ )w,n=##<\ '3,^N_@1 1ԌUWXڇXY&3ݷRR\-כ*$R<1lZJkFtTͅ79;lt01,]fD:8B7halJ^J8ɘi>ERvUUVXz<",Sn2d(c S?x+W[9E=?lqSF`O7U^@i^~L\œ_/WYr+ɌN'8+>*9fgt2;+$7=n;nǐ=dvOg76sL Y4WcKdjix~ŜϨ$ϚI\g[&4l?IEpz#=j2< #z,gk9bwvcNc͐q c<\ +J!,&$U7 >,xD._Uֱ=̍j$18Hݺ$8BtVb! DK@gSSw!W-&<"V)J0 [r>+THPaY-7%[؅oX-hKcRP|M%DIOrpT㓻v wF9 /֓+Xvd+  p3Mw]/=f5Yxߧӛy Mgu(72#9Bo651d1nNTz.8ߔԘO8GٍeOFssey"@!.e3[ `{ }5w Y~]D1s{@*?aGIc cQEEILl՞VR ú/%͖3jcY Vk̇6=C2u;nM[H&OM?dUv7$j}oؠWAƎ/(,6.*,\Oqbgv6CF[Ж~nMu.stnېTx0.[Z^`KVmoͩ#3U4$^Ec Y-zyeW϶S/v@A `@c>&T>tU;웭'@Teɗd{+kx#"3(*1_+B^im`YM|r!HXqfL`;Y[ʢX+UV p# k)&! cP<}׉ݦ MAXVhËEg*P/_^BQ}\;dl !L4HP{+f<Д0g4Rb0ndyJCG{-ALuX#%q&RU1T~Z3B w=B97U' l~[>H<`DgS(Lu'nhn@ۋ!->Wm& Rvĵ0疅ԍhF\7v'B”dX = >Kđ說~5A5` BeC䞪s_u5+>->5@X]`FSkXh1!з dEAB'E;}-,SE/9n5q%ãX`m%M>71LIynm&9W5QI~!͞E: tdO%ZW"oA Bk r:rh3+/2f3"4 !Rg.p ʗ<Sup5֥ Q+JiRJqsbYs|`Z[rEpx~NH9OjPD`iȴ*eN2E?;k,S&_\3'܉H9gG2M8==)8gwB'2XOpSJ*o_j΃!mVu^JKI?۵յmˣ2q(h8l5ߊ?'TϻVl.η怼(&YV$ 4Oj.:icGfO^~]K\˥ ' Kɸ!ڳ'"x~`f٣z:ځ-]`9bT>.]JY-DX-(^ExAL@)hbem>>qe)D=gƤnHwI54eDUp⥭&*=YT#k?.$AϣwjHXu|^ȩ:<THdP@r_C%tY@ 2&ᨼy”xQ /={4"bI3!5₩@p?4߸/^J{oFȳڥs3ԽMkSClEw6I󾌫&x XG3 ZOspnU_L)A!nS?slޓZ=tMq"& WS>Œ@ jfSghT]k}mnCJ !Ƚ RF|[\] 2* MtrR\sMivN#X9UmeR, ~9Q #!&O`6+a%}5"p#=Ʒt3(+ղ ZwϹnP=f]& `G`c:[q[GaٞCBkk+/4N8 UYĹmhҚB5H@6^ms6#t=; x/xGP %Ao;"hb;L9yŤWr_Z6ݱrD cqeۛ篤YRgݓF.7JaKt soTd1~yZ1s]NXJ8qv@xҁ$f1,o aH 4불K1("hn k/ȸ;&\m qkwm \`RIW$\[P,jM5 kW]όpU H!ܙ}  cAIq^"Uʔ>swSq+)f9XNi7577œÈY9Ľr}Yj vyU jU\1+d|]H18*pVjsx(]3.0džqvfr&`]Phwӑ6ADqNnȴ "ISS l{70XƼy_.rV8k2+!9ϲ0p=!j-Oa,@^1skmcϝa@Yd_G%5%\B8ec,7C{y2k?HZA 7wfיA*&Ky.)BˑPj8 -ZnqmvrD2j^8ml?G=>$|rLK pr]e?u e-ĕF"i2n6m_$zg|-Z64ufɜJT#6n#TL)4{! 64I8{l >z0wRG5j(d ^M/KC#֐U0Dk>(`gY+i|cvǙV g:sВ.&z+3b \ρrO}+jo&#;lǽjjʆ@PѨJQ@mR( 6bp Z)#^РTZUOkÑDbL0K&8w&, 0|!3EƽnGZ`bA%EBkp/95Jjt`ng3v1o:}Ni-wIՙ.Ӗ'37C(XHUqJgPH8جjnU@[Ac#bpQBc:g$֨,r2 3\1 AR@M4[ (FY_1%) ob֥\ Qods$Qľ0(9z RQ,hu}dJXw |@2#I>iԗiəi))?3+a_I0 =@D͜7{]z (vmψ:3au1Z1B'(h)j&Bho fBL6#Ц|QIRúS3zv)̳cӰ>4:?\W=Zap´z[a%.a3GN>t \Ӣ_ o gWLnm(f^I-U\`$݀~G.jc1_Xcڝ[at&WS>fdH4 EIl:7?2r7}[:5;SN^' 2p|6Bewy8=&oecñ!ޙ_cFW=!MgcCDO'93͖Ftn#41be :Gv%~Ԟ'K޳%g&?Vq͌z] 0 OvqWǍ& 425=҂d i̪JMptŹVypg: Yɴv+}ٙH$g[,Ч\V'RX]{?)h%YE(܅ yr?/ysH+Th9yŢw<̒;f?Z?"ŝ^}kϣ"7zBo/w{;tOX=zCCNhc'ÿ/qsSAu)k>nd-X[kG6I[gp,-3{NdwnA>{L U#SIC0e+<,j&L)#W RBНÊ:k)H!m\ծ>j{ P,@l^G2̥Su%*Ti]y1a<߶~V`xwI.}|7nCm-m'.sUOCWR"+< '/{v^2~c7ma/ߏGk{U˯xd?*fwmHQ^H@gu|O*/>]1:Qʓ1~Ğӈ~ULmF e*kD#8߈1]}pspCm Gs]T:!IvmkɪTohfz:TLgHRL#ʟWl~eը@T1_Þ8L(1 KR20A:Z$]~i hK)[C`+$>،9D <ᑑs[1J6䂈,nx>vc@Asswo A{R>!>1Oސ¾(Imϵ`CvWU\8^9#}ٍ' ^A4A#_`» q7Hhֆ-mшb}i?tcR<#>Am`tM(J(h@oJ^u39e .}ThD0Ytz_/ [qZ|ezbj ilJH@r1r-1 tWC[ Weѯus7t<sRěz(F 69B8x},unI`tԨ$쒕<˸-:m9}?8:MDxE[z:L oD? EɒqxzN&Ay%`M|T+KUZt6&/$jUkFC/:}p nFEg*Q ?DkO`A`RKatEC/p큮`%AR1mA%!12` z8ֳ܁]7k'ȸqn"l~y$LZ;Ťyayh;O &`-?O^ F{T)sڋV%S#L,C5F@2f؏Ov|]Gm, Ps<">I{/HgY=btifSui4 ={h%MowI ]X_#']m|Ks-QvNgT tL03 ckz9r%L##ɐ@%в>fh A>JR6)` a[TL.?(c5rWDRY)%HjH#12o)琦IOh&"& a[]0E7йLHQ4Bbeх˰N5lD[VulS/|?qȷ` F*S͖/}ţZZL=!|,uIvL wKձɵkArHx|w4U $ǃ&/kxF"Lhճ>)AaN}u^1`nT1 w hp{%{]t} |A 6)u%I)sat%6!|vUY_xUTew<{&&|BSqTBKy<|"O`X l*`#*+ "˾chP3kWX1z6$˜ E[=Iր_I*^QV?J` ΠRUR|~SvQ, K'0x[&&F~b:`įmdCY"9Sś5%+ٞoq=b((tpg'.EITҤph*pR, v;ofH(ȕ/BEVeͧx|130Dπ#st.0m"u ry3`8RL3}"~8Еߗ$}%s#^h",ӺMBF^ cHPwPʇxz3,$,s$aTTͯb:L` iROSTE{ush5D " #G ?1{{OI9-h.! NKԒl`T6UI?I:qٖ\۳.Bh=qs5%zÝNG#H}Ϸ kS+H)&:i9dLI)j[W"xh%Q x_e9ꄠ`Цa&[ cB6kc᳌1v΂bkfֿz] 6FP+ćeTЩ|"q.O1樳W}sʑsZЩ[/CḃE~$T9KF^s$y݋ f!WC+%j KIS=eNxJR&udoF /]t Chi؋x|9 ̮/ %TdSd*fB$F%eH_v ut qIYU_ +b4U_q5Nt;߉krݨ4iCY0qlsEs޻3Ok i{/;)z-ˍ]  )1";[w\ 4{Γ^C#whA Lfڻ?eRLP\+% /%M;)OF^Gߘ ^%DMCVzټG&«ȋY +gxF/R =O4duE$(| tO~黙-ht~nӕYgsduO֍xma$@<#%RdObIjhR.a%R]y%B":rZ ODo!աXQb);*R޲yf9eMWxrXCxt8t)PJ]VARyގg/iUlFQnK/ƽ=@q W2G`UOزtGӆ8}10\rǵƃ22_Ѧ=A/lc5` j+T!ɟ,qG'`BX'<+]pRdml^7T`|ƢZ@}R-xTɠ&"36E{@a'C !p*]ihX$PLvKCZǙ{ɿRͣㅶ'{f1Ãz,vXGhtC!MZ]v@ 4b=v#y" f?Y?' :EߑHcJ;"^JXud6 F1&𫨯Z VncG?M.1!W}~ʮEҹw!u<}y%errv_U!nMSΣDwdv$w4RPI" KjscsnsKOӡ[GV-B$ }B{rؙ|&Qk<2I\m@oQ>]? @"LQ2̆:j>`(c<;8Ԧ%TD! =7ɻv$byԺ:i4KR=vB뵙Ko3Fݼgpo"?K5B V{/xnHtҨgid5[ɜG%z>ϒ0ӂ=3[#QA> 2fws9> 樹Oa.yfڔAn {J\@/}1άDA+/\>QFԲ5d_~]Q9ٽeu hm/:[u_[!\JXx*ei T霬CX,S녮wk@N])ZiS K:>[(wÉfhc 3M [Vf+~?R=I::%:T];oC4\$'oY+jOjݧ q,k- 8_}!,fd+ٳ&0t='r"9Ьh,f4#g5e"{|y`j4e`yRY\("Tʈ5u'q1"4RϮC||q_Z t@ip^d'\/d,Uw9>|8)pZآ= 57= -ǕRՏ1?>z@,kܾUUq70- hnpH%{H1CKm.==aY5di.5IR2VNJ4]:Uۏl%Tu$lw˭hi7 kX&YRjğCN& Pjkj5Yfv1^#Wag3!$7=߈l”jڈ7,I~,hF& rauQ#[ךuO$B[[@y-Jt"u *+ Cj^q.@ckбBLkݵ]xxdXW؇sí_fec N)"VRCt?j&weWЗSϫ.7y%n PzW]?;¥,xvmՂtyZ`.ZnLiVNhhBމBjmVP6+YT[:L<\bĮ|Ћ$NtcCDjΆ"4ޞy 5dFt'nIs\-򦼑7D+47IiG `C,(ۚG-Ŷ!dQ{l&;}1: X_H,qiRUc}00a.8p*b)Q#N7(!';f;"fdߚWf!Q6ϓgbcDVv.H]AiJh(7# &\wh'O|GQN|nC]CxtfAu8cO΄dR0غ]\EuM-iT,"\'DlcfT + CMim\͠躠k5HcQ+I zkIai!FfÂt`KlH8iRv<-4Ss0e@nUPRcͣt( HtN:gdCa C R5KǘwH*7J-9/MRې;⫗o lwHFSouzomvFP,-%KpXpz`eC.o~@ѥrdmn:wV ۸J2Ֆ˝pǥML0 W<a+od,ۣN Gک ڂu:Х[cV|і9r̪euD=9B[eH!ӗ;a2oY]st|'I*ɖ-)㆖Z6N|v:~9Ӽov2:,t2~Wv[| b""BS ,zZcMU9ē(pr! ̛u?Nw; Jv/$:٭F)y [k@tjQdբ $ Qi.:ojv Ckj 9a|{Y4c!m+×=KekA"T2 wQWaI=[*SNZgfщ{`x3ڟqg-l Hu%3M9,eCʚ .iy .g@TBTb+Y:F6Kg!.1Цة&*p3zXk**,kbrV5H-3v3^D"XMl?Kޥ z<놴gF@Qԭ`A=Y%RV)GG-Cx\gdzc6;%e[Wcn`&Q0ϖ]PTj9yqB SMm N d XϥGtx|F}*DsѰbiT=[yTc#/d4 8~;SЗc/cXohǁ w@̓~{_~" #MbpK7JD)Vjbuu㪕gMT&|cGnJ:RQ/R$ 1Ԧ"Y=/Ū?_4nĝ9 cP#xNJG1pfb0k ߄we^j2F4B ԈĽ5ȄM'ɵlYeE5oRF|X]6:b&{(* \i|>ߛVHGuR xXUY4ZWgd ^4s7P0q8 UŨꐺ\3f{_g  gTp`חsC\lz#\~(;>G_T9~1sed'|!OC: s#9Z|&~ r-mB:T.ejdY6 v]x!%+],kYjG)8eR]ޗYT(ꑇ/S+pdC%^,aL}aR:X22+C}peVU-!/7xAzhi)z[L=6ߚ1ۑҲR b-7h*OHXu&\EI"f)\\ShP6M(r-|Z34|nހs<-&di/,9bwZ6׾5II{j2#8 ZHم~#{ b}SI0xpn"PJDz2vx"EYGC6w3kq^V #˔5cdzgv+ }[_lqEe.p)D%EKHXs1]֜ ?@p+"mB Z3E@볣ڮy2ʰ\u.!XaYn:8T C /fe%m$码հ+uolݘptH\.$Wi^LG+slwպ٤| }y4Af p%%|U(bmRicAp ibY0n6/gy!jDrQ ի7W ^kX@^N [})8^X ʎ#$ay27kCct B.NʛB%Oۧ2"^<,]v}A8yl.P,5o3GWJD;\h]9^xtʙ[jWhPR L/ǀ*/7(Rs~; _/vcbpׅ8CЭ80 e? !;H@Jc[̧/ ̞(t&@q(M`ʃxW/ LgU#ם ;Ⱥ5oDXZ.5\&>oOMo$8 覮ǫ4/lw |שZ5ybRfu%-f4DֿAlq. maAH]Ҭ/*xU܆bŒF;bfE xJF kM(gi@7"-ż4ipNVkȌ p_ʤM,}tjJ#`OlRUjyT2L3oj$l^Lhz|-C7f`t48 GBXϢ,½$<=:@ )9 CC}'@&j3snDdad3phJnfk.BS#%eFkT&藲;TpOzڌ{JBrVu#@=eT" =ĝwΈȡ2dNK9s5R<-@:m׹ Kag ~h{LHf)QEऒe[ц9+ Nzba'(7h?NSV]x$!9G⪧/}#x|g:=Xշ%&P0[Sb,UivUW0Ҡz̿9T~W֜WǕ# _CL;Dή5{m ]KJ!#+ sϬ-CcH:VamyECLYnNjo @詩JQn+dz'4ynr5!'tTC1L@_y=O+? ୄXtMފݵ6R3t[Ù?O< :4b,њ /?9Kt0Fu=B3=5\_XqY ./h,j\4AEC(LpBhz}OA\!8s |yo;k3*rk;VVQx8es>^ɺQryft)=m,Af֕q!z['CsDέ/Sx8; y$j-wQ)%%OO,lq~OϚm|%N2r|%؉H] {tJ/Z HLOPRJqXYDUgVEAX'!O8XX8<}=TT?Œby"q+#㇚ixn$,&f_P9N2]XEyQK⁡ ٞ[rg㸮҆LS`4ef8c虵 ka^*4^&W3sS0 |rMK!ΎE2{{V2.>;fkt, |SlYtjU2/1 X9 /^i#iJ:k⓶,0RGKŖ%']f2ߕӦHmM}Aa^F: =/|6e%4$sw6-Qh9XǬCGKVX.>q`蟟iQf?3] #%/Ke&xfLɝ7 LED"~H|dLeJ^ss^Lsݴ"jw,(KS< ]0 fQLh'_eB>kWPwSW e#Z.fŧ@wˉ䐉yp\#b}+׉r#%U)ywR/imu UŞE.$Wy.1P1٠B!+Έ(;zV>@hJ8_ p9}@Mp#8rbr58;F` '}b_)'V@fj(9o)ѢC8L@LL$P$f(8M.qLէS sCU0L 30qiCIdI]6߱ɌZA;WLtjv8>+RV2lNzlOi(',(}ȲeD(.-^Hyp,{^Äf,ߨM즿zZ┰c'+Ƞ,=D,`z=~kSR o܂c'cSWOa{4'ᑞvn@տYGD Nlf@ <)i""04rwmMH/lfWΟT̛ڞfxHpFD2/g&ny U_/a_*&wl;轇ֵXc-l:%XR΃[`"%kK@ س XH}@^',!G8;˔#T8-4Et!tl,>Ә_vs6[شhA6.> YRG"0Z6S/tH,mudJO+bѸ]3q%%B#}Hm Q4FJ-BV;)$ ([*Ô׍?%D XKu?DJ:8!"S~8aQ;/9$32n`3᧠I%j&.D (3o*=P8RMOQ@R6) 'uy`XxtMR> S Pc7>CSSQ9MPlr2XTb*[dW^$ 8Lca7FlU 4d07F)j&e`Qa7oZ!ҪSO`J%Uhz>D)fYߩnpG&Pa? 5QE l[j+ygN"hฺ+dPE蠊ydT+D! -FWo){6fUY/Јnkbؼ]3nUeVXr2bH 6@@_7e }6.NњH]A-6CO_d]ѪB|LPHUiYĿ6*1cmL!hQ0W4&dC` j^G4u/` U~.ϦaHfR)zSa#c4Ėdtq!䚓1 v#G@&3y(mv(ƥrw%&M9a]|MDVn=5oAŧlh.m]Bc+֡#sה@)SsPg.ـu^~`_/i;砰IBC7&a_J(0wc Z/2}}?dZzW p*c㽍 N";ǁ`*x%wy[%B4STMEļj.88VW;Y1b bi Abå1Eq[ϨܮІZ]]-*gX*gZcN=Q/ 8@+hXy$zc%9[Y ؇,hSP6;0cʰgU,VHc97ŸhNp1[g#FiZx\|;a"}5Y'oqMLHHyt NQbzOgz<λ$d0<(Rl|/2~˚({|/ Qt %C%U jiح!&WfBblnC!Egs9ĕnJ+ ^MFH!bSTz~ ɳs7R%Ԏ*W%} i$C*ZH7 != ]+ ’K[ɣ_U!JJl  'ofJHu{$z"B5s1xi*]؋(?q = aA0PY~D;GdݧMqdx;V!ɃuNAB) rTm @Q6߳Jk0+ۆ YLhVY@[RwFyW4Pٖn@ %rl}V2LNj/i4 YyiJ?8og0JsILkC)|gXP!eVP334-1Uvz-Ig@<5xͶ x<{uQRH:;>>*sqΊ?{ HS1uz eOfp~ɔ g' _7N&4??xUgN~0ޟxfʀ_i]rD$%Ë.EZe#tN<~5ЂkB#}_5EE )VR*' !-@YgB#g)wAfnE$fb*RϘD+Ҍ[p eB BH]a,>MDv@?3늓ׄM6P[]vg`+HϊlamSWn붊U~~Z"iɐNra: I)WKT_+&X أJu2Yn0C`f?bdPN_ݷBw|DR\tpBiR3/s42S]Td=ڥ@ OVhޓfRGKa>|䝶 _fRf6rsJ2Vʿ!Je"@;o(`a\⭏Eb %B+y/>@8AviDt|%̌Y2rX4zJ ~rge=F'@WKfH&7}Ir<7(5"%$1D˿̲-!L}ȂbWŹ;?(K6("z@Z' ibY+7WcL]krM@5Y9 @ܻD;:fuγ8u}(g?u*=QpQ>R6/Am N7Q5 qɢ*]I#u\C 6+_RHڙS>dCa M1}3"|uK׃̈́E00Y,8OO)ÇJ!xLU!ԂHI;'\8|0"ރ\@ɭ$tɡfP7ش9*[>.):y-Oi^˞t(="GxƕUkH1Hwڀm\V"4S} *Gi4VlGnJ05<՛A,1ZT.}US֝yۺ&ݲy1iN|}]u>/2T2QފR`̊\Zٱ%R&vp[`΀X>V XJAhԓMi7EC25XjiÌ,Z q8-wܖȢ%{ T)[O'&H=T3yDubUKbX1|'7tVMiQ/T/D~$So# /P׭GI S2 xՋ&M-0 } ۽f5]*kp8kXX c~/aרUDhCYm0tH`W2)w?סF H8l|{QLq7S`[;1z{9B0س&d s:`+[| u@{/l}JcZ:,2U#Z$;&UN*gm6z _gPHC|3,jVdK2 RL/0U#^Z"Mb8ܻz zpPpc1:G/\E;/5Pv  iKcg%^c6,? +'6A|2{Ȝ +vfE9sϩ%Q !ZbqZ0b-%&3X('ԑiaJ w }9ub:u !`ROSE9$!%BT0S{8694q2K HUǢk1pήD7y_ijj [-9tP4 F%xUte,K9x;N3P8jh]r '}a \PV}:Gb7qh3 ޕKjv%iB8o pT U/(`sb8:鯭^g*Z "zRsPۭHcIڕ#f3{mt@\×1H@ MOgD%Ql K֬沋]|atB<@Jv&f+V9iVOGBӅo٪p۝z4C'F:;KH0޷8`_S9pFS'5﨤u,1y% TPާBaϯNaeYTX$\&aUq(Dig;RA5*z%.J/GO.4\mn,Q*~D.8 v~޹9yz}: zϴܾm>6(K^%3$ig?l,VLbd3۾6koSp'PkaYQh{,)Si5ܢuuovx*P 9{LD#_Q(1etMz+[OL^(F0Gr5d]MO'x]w2"jj N16ky;lLP Xfyp_~EzZ%QbH\'Ђďe0_$Lj- zXpZpnH]{I%2GMYEr,7>0=0z/ߘ/E 9đUoT*㓪,7mS/.d-u,r,ûm*ٿͰq[')R¶L@U剷7At;Iξ̰em-B>o"l7;К`i*=ƉH*Z1--/*:%֯ؒRX4$+?D`ҭl0q!^*I8i0S[ Ɔ|*"9/=fq<cJ%;F§)mRMeԊ+ڜCSˤX> [4Tׇ`gCo}pWdE6k={7"U=\Ky^jw?^(l/^/lM?g=4 >yF+gIH)|c~AR;2x>6͈C6i- "6?Ě/ GZmL+$t58AӾgPhq 4uqOK˺L ls"PEy(?*~1=BJ{b|  22JmaMCV|Y .duf Þ4FQSb&$E?i渤q3\ƗYn3ɗ665]*0A)!hQw#Zsc06+Iݥ&5CWaH]Q1Q3/!ގ2`8tDw_w +ק&B̺n8[Y5`mA:#݊B6OćF)V$|-v|<6$3DZnm'M֦Qb(SQ V7{O<j`NecBm3򴫘*$#Bch VUbEM9<X#XC9~rgʣ!dzdrc: E3~LؚqMꇰbߖ&8  .1V4qk∾Tv=X]܉2 OSyE"uC̱NttT'(}kB+|&ƎH#]龶U_=B2rf$ٿTj<&-6I 8M̸ZbXPńJ(lb@Pojȶ,,WdH~ % P !txh%5:10 !ߑ<H{];u, +Ö*Kn(1^giQ2j8dF@k6Rqbv4i֮Md5w10 R=[mR iu*v8 #]QAUǸ3E 6]61;"t,Xd0v6@uH,v=-;3~ؖ'>]=E+\C~GdO;.R% LHƓZJ7%787jQ.(3Q)G 3ebS>NO6;vjH ^UDrH33H*>s`Q`"H~Mv"8Vl/ m8j< (lO:QYvopbiCKl @TjnhtZ>ef0o턳ƪxsW;{* 3Nfƀē@Srʰ,K"-C/繃Fƅcs=xte~:y %d.i:Xjj*o6=`պ2dʂPaa\59`?`VZ3=(Ҿµڨv-@On$EJjjR>; 7ӡ͗7n[Ѹ!Z BὮXF8#o<Uƒ䩥1>P\ߩu\C%_yIuV^wph#-TqW.[G@]8a^V/\#&wN\0C2%99y“M+Ѽ0){p78"0_D‰t#K507}J?8dʹԝ*~9.."밖 ʕes@ -|(MYМˠCͻcX$VC~PLeQ]Q?̛QhzQ{bg0A z;D \ĄSb9E|YJ!¯b\:+'u&GS2l_1eT\"g&.B;jZ3]"Znk'B>v?p[ndÃ{{\\oU (k%p jmZoQ#pWrŮvz,/Ϛs9 蘣΃äU1V p(&`0uQej͏4OMuI>(?a| rP|#RUd:ld]poxԹ;0hW7s9P Y,sݴƓݱCIq暹 Փ+mZAo4J1xdlSApӏm.c'E|kjL@ʍL8U{v9?vV9k5PR'YbXPs_}Cú(ZxjCn3V^~Bȍ&1%Vq3lD3R X$ %#Q8~Ւ{ɝBIAfz,s?i6WZvpg6#lxxE0|Ͷj̙tkWk\\s'*E9A._5ܕ O5Kqh<(g!NOTnn%3Rc1 {a;d1?G޺y1o,' PE, 8Z93v @b\`ZL&t1H;KQjxF8>$5Zpd8ua\I6^ 㱻@5!9[_ӄ8#(p-rS'|[Ny-]ۋDWH8 ܦqG nGRI(yraٌByܝYqÿCt|/B0P}@A@0 cr.3fY9FxlsXF*DӁ5Tgd9lwnzrXx 5?H6|s"N}I;#gCm* Lhcu)㄃!H>l|Pdi-s`|596[OСv$F>Hu~R+&AdZ3>Úȱ 0|>{@'z-Qg >mv0zG/O᣻g`6s蔾ڷZ^QfEF2PBGaPۃg\A|DC[RwnJz&7k~QS1*C]NHt- hT`wTb}]F t-v :<323'>-$mtbZ b[Gpft;@% {J,TK2$]](eR!yPǓ<q=DF> ;3?JLlt, 3}YVh.ħg7{Uk#e3Q`ץ)3R- CH15j@>#-b,C RLJ_YtgO G8>M!K!wVm.Pt0qGԥ-&1 tIHX;S8lUӆ.iSp_]XdO-k[O Ao OH/T q,PSw佗bczR,C\eB弇5אָo>m_խ,{r3zTҪ>1T%!i :_ύAAoPɘ׆ s;+lsymG^1^amJ嘤~ϳ>AeDow;0I4%vWGWW6 Î>}3EU9B~5 Cd֥ox YpY jf6ƇF3QXdhL rs.1aR:<sA"Hxk,s70% 5z-2`QaL[;B.0̔dڵ D<ܺYR3D ܋26+?_{GpO9 w$lZDlz1S',_[<֟q R (^I@!DM0Tq4oP#ف߱Y L__I'r Z2r݁\ش[a͘у= L$l^@\G"[5)p2Q-F]wCߧeʹt &s+zÙ}shU\gj+HtҐen25tHk4[>0 ƄXֹn/ʗ eC:D[1= Hqa$vP#Xg O{AY,;^y2C*4EfGޓ:4jn;VJ6r <>|iy&Ѐ:$bS=-.;\oF, ,*WMȍoر>w tbs8m T'1 Z  R7ɤ+:9cJl1|RƟgU] ף ޖmשDg׋|2OsN:h3ɼ>8 ˴Âݿi:PqO[ Wn=%hwmaR܂q;.] 7T8qS e+g-UB1*# bw@CsPV;ҝ1x;n'b y7dR_\+RzKo.Ys-B+@`i":iǎS#Eo o4;lqFf/RG;b?!iʻmO,(PQ29h0Wj} |ڤnY#n5ɤ`iuƥ(hjd&Q]pN; Z0pfBg&ĥf[ +<5uxNhxqQ|&w絶1"Rpy 9F=[[JH_i)6wL_ӷuNHC6.B5t*c=PT;uٍ^ u`swql@]/]q=q*Ycs _AkN%+H/.9$h&- ajv?`bFVKcT7 2۲>?v)I"E=+!Yi[ic!vѦR4` ߖA&M²-~΍Z?: Y`M9Β/}~I )BД 'k!5S\N EQ}O!P$ӘtΣE:їyT'ɅghN]C>C6ѬU K[6,LxwL]qxZ(P,E̛Em'c$)Ho8?sjz#8Lax%I7Jpny1bC90niw5S V* 31{Ȑ mvU/,7ڦ5s#)bҷpoSbŐ{cEE:Ncjny9AakطRcT{_v!F밇.QAV@?ߖ¤"q$sX!ʓMq)JWNo-#9 Fۡm UƯ,*S^u41UJt4{- 1]+ m+A<},e :XJ^d58.^Z\f(TnW%TFU_za!w SU2Gߝ@#mbwn J^K{N)ay;r ^Mgud}I0(X#pWjVͦhޖCmb="- 2JlTOPLsqlx0-/}EZOA~^,6&U͸'^RSl kd4A$sAyv#]ws #w{0U2t ,k0zcߗ+FόS;nbGJS"cFM&3nbCY*U8vp ݖmPO\!Dp] $7I@hs s|TH,{ iz EYrS 8JJf}1)\jЛRJZTqͭ2 JClf(T[ulT3>U@&͘]<$7%SW?Q7熾v3vhan%"#{KPl(7o)Q+%D;*wvS)l%lu\&R8EZ)x۳*av*<8Ń #V6@m 0W95@`7:-b.XQ))@%$rq]j*ԏu0}2<8^gi3G&_ߴEP ?&\d RDuH|/%l>6L*,̵۸aE?__F&u0(+f#` F_i6I)` ݠFڞZÄ he tL> =* 8XF.S H=MQP/IUhIuz`󝶁[.؇%C_IS5)J\0׈OrF+E:IDB9 ܬiuJ WK`Z$ѩKtit-~ }>쬇z-~tz ^Ώ̠ z!Q PH쪥wYFwdxA'i&mpm˥GGcN:,g12ڸe, F3{+E v&~tn Aإ5y1D\5uwFũ:#otvoGO,ݡEx QV"JG}ZńorM&Ȁ [,°-2.,x DEM$@iA;Z`{fZSl?GjSޭF;J=:BwcjVKcy ,? J:e[ *Kݍc4oqtb~B`g^+R@S_kyo$t]:{GQ8y߈ķEړ4ѡ i ߉ l౨ؽN-`VzheTu{Wj<6'' Ic)+dO xPq?T~KzQFX6q@^6i W}1]xu\:&ne]B>;`| =h#ÕE'825HElh1UW 0x*RoI&vcHgVc;:*Xno+=׿sdݿE+O>&^\z2mjxrumc16fS\Q񱉠9-oT} S~8a堓r?VD/c{4~<`wzԞL1'9f9M;I_Ȍ@2Dv\zo(*?Ph_tx{o;, x}}?׍KWa4Aqz3'8(~qL[I` (#NuERU;p- #,\'/)1~8k\}C}5 =Z{.|X@\a3>\DJ9˟DךmQVNc;ЂC )4o4kvRԛf;EAa08{{y$JJG$4Dd8]S/9xqǐ?GFWLFŽC}^wfTĶ.|Skʠ!Z 5sݭ"d> k;$s$ȥBx3tR˹w!/zO@_nT%>fzk瀴+oկ"r[@%}Qj,}q2^3O^g#8D/lneIx,U7)56=rxݡ&-7˂٣I8ΕQ r[ I;uVӲ,]hʃhЕ S3& ybG`yfP"QY,''B:,թѐ:QTef ˨9/VydzΖ ~?ߴn:tWsOM$B1f g#$H))ƽ^&(䢯ic:{CUp 'BVM} BIa7Q튳67Ew`SrAfFPdΛ }yٺyn%X{:ɶ qxRCp8.\OU)> qFw,!}]4X 7'%֡Veةr~AdA{yu2Q_z Qg۪ 5eMPWUӼS}Q[nY!' sf<V^oL_ MZ/gzE9qHy7GB||}P# $"g &>%iaNBϾLD_ ORީ\$a? ^R #q^_4=m':Xԉql+UY5{--"zP?L BP2{mԠDXdyP2aM {/ytİqV [ +;%`Q72[=I(XR}*opYa}A~X1٣ȧ1i%Џeo:1ќ K>eο2Y﹡9\GT1&^?xwZ@o`W V[&la꥛_,\;ca$*~eϬ/bpŪ0ZC8vX%UK-& ] U!#iGmֱш^.WQ#dt*T W&띈TKmVI)K@AΰP "),&69сn@:.5KJx' rD팉Z6%T7M -te.^wIK e)x};z^\Zɮ*Ȑ4#'j-df.=yW.H˲:`|;$(V?YcgD\$J;X:'s)FD 5]Ŏ% ҕ7H)[Qc#Ҫ6Ss rJ Ɂ6~]Vk~3(An|:Ya[;GMҌD7I 1=IFKC/U"@We{ZNP a4WcXmlnxj3]1*CH%N`gOFfϾ nq.\ۯ(/C!1焧{~ I˓"=5 vK/Pü>\y%Zc|2g,ڮ-Xv@A>.YCx>%UK9Cy,EY}ҍuOJU6ғq?k)e;˴/AE *RE^C;EMfeD+A:R\pǧ`[fN&Teώؽݴ"yuu %ohAD)Tf:EbL槝9ufz?dT(6DRUpvkl Eb##~[b*36}Ь*(A7MShܢ: 3gGȞ;7DgU>#RUš@!5c<`K839[j>enh|BY8!TI/d];.4CRØE wuJn/jК i+=V~ί+k9[0.,ŁvLE}*m_{P|G^+9ņm|K:zLǺlƄ4].x/ݜ8u49A[GH6DhJX)UL\E5HQg)ftXHQUA!H՘^׹-mg@rl=;E^``m;::ޓ;ը݂h`UTD4_E'12Jg[ A̻=?cwa5efot]p%H"~*'~h8[Ы˄[28xn&pB E PǘCә֮R6"7N?hf >q9eO ;1~)GŐ םz"l_{|57%;+Vo ͵YӸx`3:c.uKqO9h.2G 8#a`<#hލzr=Iп<-"KzWx[Z >X[㙽}`3JPa+}ez8'*0?~[x$,Ddz}URv,[ `)YUXL ":G >wO&JgR`>euQ!*,Bw_b$9HMC? M뼎 D̄"憞[(Cs&=:)4TnuzD*=Y\s횝n +l@"{P B6{Kp䊺i~`:ᮺ>07>|%A때)ED}WW 8w,`bS6r mn}yUruO2#*V+kCCZ{'M7i Cj$ {rlDL+vadx`\;?pW)1FK V\m)%Qȇ Nꦕ6{yY+Zk,:sGzd݂v(u@YDqCRE 3_' MDŽi&vEfbo?W卦Y ʭl*wTZC zۖP$,a[ڛfAZ'uR7C5f.aKtܾ98~~,ofKXVjJ>g"9ԡtFi.>r^Su#ckZ;؅}H2˥ޑhcDc֊؏۹B? ĖfdLhNOa~-x6=)pr4[ a[,J4m6lTS cݒkC@I˴p0l/CE<33ҩ1b?t؍^,ړZˊ/uRWy\È"sy㍺տv>[%. u8PB]b"r0ܐ[Ě7E0,t8mb|'∾ {o2 *ŷ6*_痛a7z:g21?W`383jk fA3dRlҩM  N8N)2D9+擖XrkF7"1CURP`<14s*^6kSYhb- 긹 ˘lKq8"歬'zz%?挔d9PZ:KmӶ,n 6S|;Sڱc%I6‰Oiz<Ӈ0Z7[_3  q[`j.o.xݽSsx/\wqyHf [UP*Ŕb)εRN ^-f/opsMpgdž#Y՗JDMc[l[8u;CuۛF"6.|~q5xIix6lmBӱ.gRn|Z)z.#,SIrP)Ⱦe' "@l#bAX!}<~=gyfN49CA0BϾq;DTxNH?a\eDTARFp ֪-uge@ITZ5u QKcx89rxoO%T#<'%Xl#N6cI"<81Z/F?owo~%0J2yPP6%ՅcV4^:nܒFHM>~1Rp|K6W%HoKیS.6QN?[j,McC'dtyN-q:Z|)\Cq%2XtҶ"̏y2{E'HBƲ|WXB$'K. zQ;L<[5c_IzT˛ΛˣH&2]A3s ',QZ}` A/\'Ԥc*SDz&1Cl!_EeLW|-t*^wP'/JO.4oS.Z*lQ;bwN##OMV{sľF@Hɠc1n|.Rpr!k'=R^^D5Coߕi0-|hŠKh$1]\H&OB. VL)K,Ryu0ڔ_ [Tefz7[t09mWϓѝfTqV5{?e6]c$6ʼnl;E|(0H'7E1q&w ןq!QV<ܹ2yX\6awd)a5NBc#1kt6m,VZd|lāDfU7_͍3xC(…T##[0;3/m"V \ ֣ei餯cYIrRaVmn'<~bAZ$ T-dBZZ(gm+o#D4kt\] LKEOd^`Sm\qKgS;6Yw93Ӽx`%U]3tX)yx0$D3^E7w11pSj8ulp@$s_2{$L8'%NY2qu>U%=]I6= pSZц:CIE$Hh툵~!g_DuJ zົ >,&(="v![b@]AoJP ct8-obG0`cL'kbXO!%LYJ<~0= u(I'gvUrc$%@b Ft+]R?kiuZT{xK^| {dl rhQ_Xu݀|[)+fT ˁ"5C;+PqgӛD[I~#gi6;F•BR`+;FyoD55"'!15!r!1y{P}%ݹ0ECVOcY 3R[(IN*ϔ~O_^X{Sёtҝ32/`:C~4}TCJ}I+350`ͅR7ٗ&=\; āOAv:V^::$fp-MZo1 }Z:jS,$FOS W2_n&BX1v78v$㍩b~ʙ wtP0lڑBuHc1y%^Y`K8/] 3W`C"(˼9v|;K5Kj gL΍AKS"V1c͘}3(4{#ڵjq4I!$260B7$ a$"! J4m4yX.O`zdp]ɴ҇R7?9H_%d?""%6{l-] 00GPӆU""g$ 'կ ((1|bIEw2?4tqM\hFWVax䭾[|4kεzj 6G:AA5l;#VQ@*$h<ը?ĮuP0ړ!!Rg}l?tG>aZqggT@JOU)R>S+gYN(+1s3rW0B[X]dtvn޴)tČ]e0 AU2#kywgnd>g(JpD<}i:ߤSA0)[lhB|M\ާq޳,ZзНԻwnz7\ "26I)p2YCq2C}ՀmCcx'{%3Z.' I(dPZAъf!n; TQ4N<8d7H[Eѧ;[ijT®0?:^@kKA;y\NdG?W3A>yDu k!)ij9Thus­M{ޡ^mxQgԎ5q́Io̘h_*Pb _̋nj὘4?fDNH?s4zjlXa~b%^T<3]Ydر@nmH׶{TgN/XZCůglޝYnDf+fN =f۾i D:K4ܢDpY!T-`2ULT♕KZbTu.0ya?9FB=T>`̐n_Yv my.KެWvPך 7;gZi'xidaO{LoV(cfHOi'yJ)-ڽD]'j_A%$+]X2DH 9~^G.U =(6OCY9s/8Z?IW!z[LEȍUjl6l%SҠVn瀄_=!Zlݸ W]uyL~Y͓n*DٴGڷX`\3!V9֙T1,.ʧAKaW]k6<^DŽ ko~wGq`K߇GRG˃v+E/4piG\su@uA )$^lmR+ dɳ)-Qp[HGعRt*}oC+F-QzO#6cMQ} ;{kh4՜7N¼4 /6٠(.U8P}! nEAޟ[QA}=lXS5+=RE{>*Ior1EkCƃd б+__GGw S xѷFd˶w{U_g&tQ ,QCR›ܴPno:։özfz?=vprne@BUFqg}8`iHS¸/#*Xr׭̡hgBoPO+,R^kwIi@Xќ0U4!ƴ7lh0tfd`c5x0͓+/+54x_}-X 92WĢ۔aςǟQfثC \ e[}̐ƦvX'}w&UH q`FWFü`U?ISW-SFxKp`C$J(;i@Pr5<!nc=_;8a5C $P~OiƭVspsJ/+wXm]ـ^<.Uh7D7O4{K6BTn?XT}O=;p -3UEj: EtX'+E 9V ( wN w"u?zo9NzsQ@ 6SN6iؙj#,}ae[=s^Cچ2ps&;񵭋 cN C -?>2yIɳ~ȊRZ&5< % ('[ث\(q{P@tW:_dݷ6ݯnK!\YJ\C2(Kv"ŒOa[YQ iG0FHV,ٙ"8X ȍ\tO\C xU_6MT(PIܠc/dp"V 6$?H3ߗ|%ޘ@5-D!@:@ ?dǪ(|I_o< D&s;;dvR<O& >Q{v!% d9Oxh<)-0ŝo[} @}PV n ,h?4Fkn"6/EVieW^.ERqhæ,w5HY{S/k v4#ȃGwᾒ_m-$>39s+Ձb ա8<'/|rugn`ħ蛡`k8mZ[d[®:OxC<6/l;ҵ%Q*/t%.w "ә}d!B<wfgCq޺P@G7\~GnTJ*>t Jkwm[bxFmuy5ro[B,A YɰqC?s OrP>F3GO.(u(Mj؄S@3hr /$bm5s5 v0Vog'R‹a Lio]R{}WVj5{Y ޢ!d,=’@|ΞEc(+g~؄٠ 4xfʒ 71Dh@Iw Ɉ2VО1PXs3^+X#P^eQR\a4*t#hL'F?{ӤQ7G',sTbqL$R=tP// T%Dk|4;(o>fyeTn,#? 3ѿ!NP5#xo|Șp(/jMH}|*A:qyCΫ1 ڲd_8 w] yx_N,[]a@U &y|%qt3. }t 73B/X["nXhvYO1Y*O6Wmp=9ch!A}IxfnFf>YJunj֯Ȗ^\XU] ^ހ6y9m4ErWrEA9Tb1g~NфŮ3u '74tuFy|d o6jKNӀ7/E+$?lW4 z9x?j GWL:mb-A^Wy: 1L0mq^QIk 0Yd^A9֏ m꩕k趠OCէl}ߣ?aql@Ta\cy_;/!.J/5fgs&,sJ[n{AE`snXTiO]`s~V[: He0/f 8ט'k>|qΊ+@0bN19,XC Wy~ZӠZ.1O{ !$w_{|ЌM|?JG-hSH)Ef, ^5HJ ^8D"q ̘W+7fO!d"]C3vpͳ RL^qtLhw18rXI' \' 5)9 sjͫݍZ}4C4{Ύ$hK/n%ꄎKM~t 'wixf3Ɩ&zf4kBet 5U3K`IԂkf@ A 6wUjeV!5iozf ¼Ɨ%IBu=r`of ͌ݻ=gɄ<3iTYLAa.'E($xܺkQ1GaSxJ$Ņ.-J@[%Nz@j%Jr./ދ|AzLc[d>7qO.!`E5%B"6s1 эrq5 auE%c\>gbN ag&jrᏣU SYnd&:^!16lx_|nTheK<%bg6TV3V|i4qIv&;z>Y$KVɵ5xZ6΂SDoXJ50qVy=0:M6ȣgy u<qIt[˅xG p(L1 ͘h :];7! ؓd|ی]O`i{Z)3_W ׿Q:;e7bpJE¼x\NMyp̍]:5>pMq s@:T)g~@5yeM;W7y8#v!gd"A&y#)WHgN7I!xs.2iWޫ~ oM>U[>RA:8LwN R kTW#3"1ٰ6]ƚӭTA EzJ {Yzh3D|fYQ2-e^nC\?Ί}fJ #ap`u2و! r<)kBbpǡ|Hl N^?vyai>_H0[HI}>i S ] @y:2F ǘ2t%|TxώdO1UvV򃅒ĝR3~3¶mC~NE!>E;y(3]|/`7$j <^8(tKpB$B5fN~כۼx_+͋cwԏnwV@xfu`R\('qGrkYқ8^`RSl烎aD&%陰xVk=c%1c,n UCb:&~4=lN0`]|M\丞sO$.]~^x1WZ@\IM)=E#gXy%x56R@B[&N%Qt&={N!<87Ru0 FQBP\˵FVJMy52y>Tو#Vr`cDJEZ[ ,ϏY8awm1RX 1qau-Aj'[ED{hY_PAAFc*7RXwfr S]~vӖ(`(8lQ05 k׾O93s IKZZ+ٺQTXV{4]X3G.nֺMH+ ^Pc8cT%A6ZşT3Ź "MeP;FZ^St񄳷H>~洋K~|$nj:fZ"CvM@l8YMAHZVxhã]QˁNkJ dgc-ˏX={qdyc7N bV1pٲg's4^ۇ;5xXN;N^hb&3;2xv }j JaiZP 班2rV&|k#Cv͢8m,Zlםkka4YDA2G~Q`ۮcg:hDꌮ"@ΙUuok'kzkB˅kX<}2V?LmC@tGJ״R9a(/6r> iճm*WbM,vaMfp#<"<̈́h}t;jC=E pݧhY$Mgg>(; eM,fBħTp҈(KT)[k6 KBC&GZWLs㪛03 FgjM\EG &[S`Y?fpI5TX5TzQ-c8 ךMgqhΚ=3 rڭ11d0qR[a 8&gY od59ttdpRY&$[D-k)V-Cn `@ĖTS,9X oPě\ܹ+4NsbjvȖ 7plVKf =n5^+E.E}ч`K0{ӥY㞎WdOPwԞFq!/XNlκj.5!gՆ*RYJK9'N 1bkx='i}L?fEUݶ*0+vD& z -o?Yʧ;aA{i)+\%AUUz1M$BEކ=Mߴ+I(F`V۰u*[ J:߽31!ӂ͢ǻSZf(kJw+g?J8IX\ؖܕnƘi3ԍQKx3$UQy$ anr5r [՘Ky^֡X].6qƐ@]5)҃ܶ+ *W4خ_I WyjZQgSȓgBrs(ia$FLֈy +SрKîXV%  (메F~7gpM̼((:ܒ}Bn1yE y=(7Պ(ᒷuY ΃•; Vz>L`{ԥ}Ej|w rtVpBJeR71Io^g,X^0YԖ"+KllF}+ĽJpq2^:͗jdMEH*vE`gP\@-YГBI! KʊP欛E&͑, w8Q^,5zJ ͉ѴZ4]*zbpk!/|'58҅ e,x߆%6V-Րy:VxG$bSCaUIab</\g)1isep.`Ȼ@AǠÇVygQT԰ko}Z(1*)B`V\T*41)oR^ hL8j0j1rљ*vo.z'U^If>SdH>Y{LX# !p3M 3,{\gFaL $~Ob!d+l"L̝b* 9gNNUfCֿzA>'L"™(fzpdzj9Qt>c+Ҡ}r;5_ji5Ht+FmIZ"95jF YIEkj1EK0!rː<Ň›XR&WyV8sꢽ+څ^ͶFԁ|Fq9v{HxzFyZl2L4%Mz"((V0g1a\'rC`m0~߸v`,ؒfW#EpԚ$;':HM/s}osC_Etd飑"nWn[]}A1fp`~'nqY9'4[^DLkS}ݗʴh )>@ıS> mC0תh)m貂2'ʣHio}5M]Czk1 LZ.6г`?U<?]0BcC9T|b=Xi)B\-<|'OyqΩlr-&?X}kc0"e,pV69rKq (0;1medtfɭ2Muvc/x`vOԻdKdXW[:n7 i6=R/Qegvx$zl㺃\1'IzD*ɹJṯg%@+yw~~F2aL͏ h=;F 8H gZ!j$ s}|RdrPǖpk?h vSBzhVKh,reVa5P]#HO+5bAKo'fá%֑6 rfK0Mc%IaQ0ӞeA~k}OJeܝ_dH XIwG,0]hua풜odV[$zݧtCMtV41_HVt̾p{wO?tp;CK%BJLMBഖqBiN=(s2f*㺷j|n\(eBo)tw1\|ϥ,0rgiC5Gfy%5Ԏ>?-vB TQwUAxT>`̤ xAxY }bQQ:u\^o ڬ5$C^w"mLO;9um9) T:ucL,v7ZupBe2g ]CN]+!~]VӖH%R_8h6%ݦDABBٽ(wO*O Y7%2gտ])I?\pTΕVBZWA9K`c"igp}wM SBgWXAQ7T[`=pm" |ƙO(Ի3ث4`Q Yb}^ jII$&2jw䕠L3̨ %-s/Va1v c m2z-u{8>>t.Is.\hޏ쉝'#ZSvWJޚO;Н_ M MrA2;ӮXW`B/%<>?:&?+q_DR,L`ҜT%̔ }H·/t4Ʋ=RKVrU#/Y:C-: y B9TC0Y# Lo 37$O{5ˎK""klj!5|6Q 4ߤa( ^kHFɕd:%u*\Z˼ Axg\Y 5EEpdR&ꐏ%UGvUӗqz}ijpUv;L SAo|$tͪ~;!۞ء+]`LtM3H`^Vf?&~[ y|I aXC`6i.]3 [qw=!!h5dž]J% HMؗ{Z}n.Oyhɚ1[XMY9~LЮ*Y MLU0oxqCO2-o6gIZQ*CԾй"|쑛-W%a  Rڌ>W,dߊcVV c>O ͘0!1OsO*x1CjK'%GBOsD*/QdTKSw v-f ~iipY.W3Ջl5}L ߉gP 똥 } V޷*S?v&(/Q]6/zL;[GSO„6&ιBBHZ~[_!&L.7ЕEw-?{uO9_b$QCG~ՅmElEᎩ Fk=tcԶSjRl'EueHm-sϭ1o2f0:s5 efy5<2tݔoO']j[5.l37JԾy('6-:,I~EvH<|/U`}0B׳?l|sibC'7v}r-c 0,.DڢK„^aGm]45* ҧAIoɆܲ,Z7|"l"-LfDPC9 Kl:Q+17\L͟ZFW-2=EƩb&=!b.5g-3Jֿ-#4]ltIg`!enMH_ENlf47+/ʬ1V rn{o ~˄n- ƣâM,JDP- 35+ХzN?) hU]A=7c5.-o~Z1wi5d. ɀ>>m/ 1nDͳhmΣW3ٟ\X6Tm5(ڲ' )%YU]ո.ltx߉LKzLX8NxZ T|kI+>QV8N֜DgV3EZ+qUє-[H:u0OYGW?SƤP@qp"zv]a E'A̦(VX0?9VCǁʙpjϾ6ROx9#:c\jݖ1] 5`/3Ff!YdomnE[UDur:j=CJ 2T 'Do㷏nSU}:9~UzTQ5O%MF\G f8ԗjcҨ` nb蛙HH*N&C "g8*AlyO PL+;l8#<34CT~V7mԏ!wQ{TٴSHTBU+^i=R/-} HwEZr|!]M*\զhBfRn p2; Ӭe35s݃G߭N 7*D&Sˬj*W8MW.#Gw0Aр'dtzTlOt[SQpMy\y&Y l-><kbw98GU֐wB!߈{)]2C]y"/x a4nʘ9/&$[I>̾XGёe 4xo;È0]ch2rڒ?47m mLp[ ˭B'2-8b)aU+o<:)JpEOƽJmt&-קBE ,eWx>/<sL7u⃵A߃E_W4eOBSt@(3p., wMgKjq7q' 43;`}u鸎|ơw{glMyΎX͞-[gaya8I,iGWp4xor·L2v;󺯳6+u'eZ ~$jX%sEw, DK_ojd,"~dqnb \iavX̂4~#͟wjXWKvYELh·ý>?[C0!/;Z2ZJ/sb^bd8̏ܠNlӵG^l}5Zd&OIS썦M~/g:p+'f;:9o +.&!Z RYUHjlp )lk B܃tK`7ՓGY+j&#$+ t+;̢";mZ2ĘT!}o.PUq@6ά@.2:(.tk!# ϫޫ!9kL uBYl]bxkrQlWme GtQ*a~9eb ހ7{G˃zBu[N`9'ؑrx-,@!.!V Ӊ'ZJ;X'13bT0?AykR"ĕ&"= `|N5?Tmǟjm70IT!M\'?.&'|R%_xj.?2V߳=}l;-`4c7?V*- 'NZI$).|t$Oe0#1ɀ\QCh?r ;oUxHʸ2{Hci})ӒkLBjݕhԋ?IQV:"{Mas$Uɳ}TssH 3)"ZO~. , #NHParE L=+*VI- ċNuٓeBx#+Väi5|}8ϐV3`4`?XAfvH/-oʥBq ?܌hLZV58e0D_9GDTmz&\AsQ]D!X%DٴYUce64BEbNdTsͲ ᤽l\9 rܯ/0 uEܩ_ G .Veh*'(]`0H`kUafNV]eyN t]/eIr ɠO!2cbۉ*~XbXG]Oq؉bۥy"O TZ!AW O 2{G#ǁ9)S= P4X$"[,loC9^U5HSoKG#q`J O( },l'f44+Ƞ6@ -spV!DZ/rI/Vء7Gnb/{'& )܇8YEGSӻKz]r> Pw |}Z˓]Qzx }D{Mzu j1,|9#q~TZIټKShٷhhw/h1a12wwN#|Rk5-)Rmc+{JEΟUsem7ywqF@w|NC\455Z| %  n'oIS&a1wI? RHJR>$ՠX/mEӥ1^uaN6U7rEDYu9Ɩ j[p{d5*EBDAYa,Bتߜ:$S?W-.DmHaj/Yׅzf:.Dx^!ִ6c0z [?ҐFbhb /t5W#͗9w Ω5*0 oJ+(1ޫov\u#2Haf0KQ d92jBHG's.xʒyf8j-HY ˇQVz+`{`/$hO]{EIr@Бu1ߒ*V$-I)4צŃ|$ tݘd++:YU=g}Hz*CöINOD>~DWz;\?g)Pp<_/1ǫ9$>6Wݓ{`ZSU*nRR=uWnAA,@`Kԥj[sj)_>>i" >[b./]eD6*(?0"2YrYX|r90$ K1ʦuꡜnIA{ml$j7' r&55M= 61.0"] build-backend = "setuptools.build_meta" [project] name = "symfc" dynamic = ["version"] readme = {file = "README.md", content-type = "text/markdown"} description = "This is the symfc module." authors = [ { name = "Atsuto Seko", email = "seko@cms.mtl.kyoto-u.ac.jp" } ] maintainers = [ {name = "Atsushi Togo", email = "atztogo@gmail.com"} ] requires-python = ">=3.9" dependencies = [ "numpy", "scipy != 1.11.0, != 1.11.1, != 1.11.2", "spglib>=2.5", ] license-files = ["LICENSE"] [project.urls] "Homepage" = "https://github.com/symfc/symfc" [tool.setuptools.dynamic] version = { attr = "symfc.version.__version__" } [tool.ruff] line-length = 88 select = [ "F", # Flake8 "B", # Black "I", # isort "E", # pycodestyle-errors "D", # pydocstyle ] extend-ignore = [ "D417", "D100", ] [tool.ruff.lint.pydocstyle] convention = "numpy" symfc-1.6.0/src/000077500000000000000000000000001511276756400134445ustar00rootroot00000000000000symfc-1.6.0/src/symfc/000077500000000000000000000000001511276756400145655ustar00rootroot00000000000000symfc-1.6.0/src/symfc/__init__.py000066400000000000000000000002601511276756400166740ustar00rootroot00000000000000"""Forcd constants calculation code: Symfc.""" from .api_symfc import Symfc, eigh, eigsh from .version import __version__ __all__ = ["Symfc", "__version__", "eigh", "eigsh"] symfc-1.6.0/src/symfc/api_symfc.py000066400000000000000000000552311511276756400171170ustar00rootroot00000000000000"""Symfc API.""" from __future__ import annotations import warnings from collections.abc import Sequence from typing import cast import numpy as np from numpy.typing import NDArray from scipy.sparse import csr_array from symfc.basis_sets import FCBasisSetBase, FCBasisSetO2, FCBasisSetO3, FCBasisSetO4 from symfc.solvers import ( FCSolverO2, FCSolverO2O3, FCSolverO2O3O4, FCSolverO3, FCSolverO3O4, FCSolverO4, FCSparseSolverO2, ) from symfc.utils.eig_tools import ( eigh_projector, eigsh_projector, eigsh_projector_sumrule, ) from symfc.utils.utils import SymfcAtoms class Symfc: """Symfc API.""" def __init__( self, supercell: SymfcAtoms, displacements: NDArray | None = None, forces: NDArray | None = None, spacegroup_operations: dict | None = None, cutoff: dict[int, float] | None = None, use_mkl: bool = False, log_level: int = 0, ): """Init method. Parameters ---------- supercell : SymfcAtoms Supercell. displacements : ndarray, optional, will be deprecated around v1.7 Displacements of supercell atoms. shape=(n_snapshot, natom, 3), dtype='double', order='C' forces : ndarray, optional, will be deprecated around v1.7 Forces of supercell atoms. shape=(n_snapshot, natom, 3), dtype='double', order='C' spacegroup_operations : dict, optional Space group operations in supercell, by default None. When None, spglib is used. The following keys and values correspond to spglib symmetry dataset: rotations : array_like translations : array_like cutoff : dict[int, float], optional Cutoff radii in angstrom for FC3 and FC4, by default None. For example, {3: 4.0, 4: 4.0} use_mkl : bool, optional Use MKL library, by default False. log_level : int, optional Log level, by default 0. """ self._supercell = supercell if displacements is not None: warnings.warn( ( "displacements argument in __init__ will be deprecated around v1.7." " Use displacements attribute instead." ), DeprecationWarning, stacklevel=2, ) self._displacements = displacements if forces is not None: warnings.warn( ( "forces argument in __init__ will be deprecated around v1.7. " "Use forces attribute instead." ), DeprecationWarning, stacklevel=2, ) self._forces = forces self._spacegroup_operations = spacegroup_operations self._use_mkl = use_mkl self._log_level = log_level self._basis_set: dict[int, FCBasisSetBase] = {} self._force_constants: dict[int, NDArray] = {} self._prepare_cutoff(cutoff) self._atoms_fd: dict = {} self._displacements_fd: dict = {} @property def supercell(self) -> SymfcAtoms: """Return supercell.""" return self._supercell @property def p2s_map(self) -> NDArray | None: """Return indices of translationally independent atoms.""" if self._basis_set: return next(iter(self._basis_set.values())).p2s_map else: raise ValueError("No FCBasisSet set is not set.") @property def basis_set(self) -> dict[int, FCBasisSetBase]: """Setter and getter of basis set. dict[FCBasisSet] The key is the order of basis set in int. """ return self._basis_set @basis_set.setter def basis_set(self, basis_set): self._basis_set = basis_set @property def force_constants(self) -> dict[int, NDArray]: """Return force constants. Returns ------- dict[NDArray] The key is the order of force_constants in int. """ return self._force_constants @property def displacements(self) -> NDArray | None: """Setter and getter of supercell displacements. ndarray shape=(n_snapshot, natom, 3), dtype='double', order='C' """ return self._displacements @displacements.setter def displacements(self, displacements: NDArray | Sequence): self._displacements = np.array(displacements, dtype="double", order="C") @property def displacements_fd(self) -> dict: """Setter and getter of supercell displacements for finite displacements. dict[int, ndarray] key: order value: shape=(n_snapshot, 3), dtype='double', order='C' """ return self._displacements_fd @displacements_fd.setter def displacements_fd(self, displacements: dict): self._displacements_fd = displacements @property def atoms_fd(self) -> dict: """Setter and getter of atoms with displacements for finite displacements. dict[int, ndarray] key: order value: shape=(n_snapshot), dtype='int', order='C' """ return self._atoms_fd @atoms_fd.setter def atoms_fd(self, atoms: dict): self._atoms_fd = atoms @property def forces(self) -> NDArray | None: """Setter and getter of supercell forces. ndarray shape=(n_snapshot, natom, 3), dtype='double', order='C' """ return self._forces @forces.setter def forces(self, forces: NDArray | Sequence): self._forces = np.array(forces, dtype="double", order="C") def run( self, max_order: int | None = None, orders: list | None = None, is_compact_fc: bool = True, batch_size: int = 100, ) -> Symfc: """Run basis set and force constants calculation. Parameters ---------- max_order : int Maximum fc order. orders: list Orders of force constants. is_compact_fc: bool Return compact force constants. batch_size : int, optional Batch size in solvers, by default 100. """ if ( self._displacements is not None and self._forces is not None ) or self.use_fd: self.compute_basis_set(max_order=max_order, orders=orders) self.solve( max_order=max_order, orders=orders, is_compact_fc=is_compact_fc, batch_size=batch_size, ) return self @property def use_fd(self) -> bool: """Return whether finite displacement method is used.""" return bool(self._displacements_fd) and bool(self._atoms_fd) def solve( self, max_order: int | None = None, orders: list | None = None, is_compact_fc: bool = True, batch_size: int = 100, ) -> Symfc: """Calculate force constants. Parameters ---------- max_order : int Maximum fc order. orders: list Orders of force constants. is_compact_fc: bool Return compact force constants. batch_size : int, optional Batch size in solvers, by default 100. """ if self.use_fd: self.solve_sparse( max_order=max_order, orders=orders, is_compact_fc=is_compact_fc, ) else: self.solve_dense( max_order=max_order, orders=orders, is_compact_fc=is_compact_fc, batch_size=batch_size, ) return self def solve_sparse( self, max_order: int | None = None, orders: list | None = None, is_compact_fc: bool = True, ) -> Symfc: """Calculate force constants. Parameters ---------- max_order : int Maximum fc order. orders: list Orders of force constants. is_compact_fc: bool Return compact force constants. batch_size : int, optional Batch size in solvers, by default 100. """ self._check_dataset() _orders = self._check_orders(max_order, orders) if self._atoms_fd is None: raise RuntimeError("Atoms not found.") if self._displacements_fd is None: raise RuntimeError("Displacements not found.") if self._forces is None: raise RuntimeError("Forces not found.") if _orders == (2,): basis_set_o2: FCBasisSetO2 = cast(FCBasisSetO2, self._basis_set[2]) solver_o2 = FCSparseSolverO2( basis_set_o2, use_mkl=self._use_mkl, log_level=self._log_level, ).solve(self._atoms_fd[2], self._displacements_fd[2], self._forces) if is_compact_fc: if solver_o2.compact_fc is not None: self._force_constants[2] = solver_o2.compact_fc else: raise RuntimeError("Failed to obtain compact force constants") else: if solver_o2.full_fc is not None: self._force_constants[2] = solver_o2.full_fc else: raise RuntimeError("Failed to obtain full force constants") else: raise RuntimeError("Sparse FD solver not supported.") return self def solve_dense( self, max_order: int | None = None, orders: list | None = None, is_compact_fc: bool = True, batch_size: int = 100, ) -> Symfc: """Calculate force constants. Parameters ---------- max_order : int Maximum fc order. orders: list Orders of force constants. is_compact_fc: bool Return compact force constants. batch_size : int, optional Batch size in solvers, by default 100. """ self._check_dataset() _orders = self._check_orders(max_order, orders) if self._displacements is None: raise RuntimeError("Displacements not found.") if self._forces is None: raise RuntimeError("Forces not found.") if _orders == (2,): basis_set_o2: FCBasisSetO2 = cast(FCBasisSetO2, self._basis_set[2]) solver_o2 = FCSolverO2( basis_set_o2, use_mkl=self._use_mkl, log_level=self._log_level, ).solve(self._displacements, self._forces) if is_compact_fc: if solver_o2.compact_fc is not None: self._force_constants[2] = solver_o2.compact_fc else: raise RuntimeError("Failed to obtain compact force constants") else: if solver_o2.full_fc is not None: self._force_constants[2] = solver_o2.full_fc else: raise RuntimeError("Failed to obtain full force constants") elif _orders == (3,): basis_set_o3: FCBasisSetO3 = cast(FCBasisSetO3, self._basis_set[3]) solver_o3 = FCSolverO3( basis_set_o3, use_mkl=self._use_mkl, log_level=self._log_level, ).solve(self._displacements, self._forces) if is_compact_fc: if solver_o3.compact_fc is not None: self._force_constants[3] = solver_o3.compact_fc else: raise RuntimeError("Failed to obtain compact force constants") else: if solver_o3.full_fc is not None: self._force_constants[3] = solver_o3.full_fc else: raise RuntimeError("Failed to obtain full force constants") elif _orders == (4,): basis_set_o4: FCBasisSetO4 = cast(FCBasisSetO4, self._basis_set[4]) solver_o4 = FCSolverO4( basis_set_o4, use_mkl=self._use_mkl, log_level=self._log_level, ).solve(self._displacements, self._forces) if is_compact_fc: if solver_o4.compact_fc is not None: self._force_constants[4] = solver_o4.compact_fc else: raise RuntimeError("Failed to obtain compact force constants") else: if solver_o4.full_fc is not None: self._force_constants[4] = solver_o4.full_fc else: raise RuntimeError("Failed to obtain full force constants") elif _orders == (2, 3): basis_set_o2: FCBasisSetO2 = cast(FCBasisSetO2, self._basis_set[2]) basis_set_o3: FCBasisSetO3 = cast(FCBasisSetO3, self._basis_set[3]) solver_o2o3 = FCSolverO2O3( [basis_set_o2, basis_set_o3], use_mkl=self._use_mkl, log_level=self._log_level, ).solve(self._displacements, self._forces, batch_size=batch_size) if is_compact_fc and solver_o2o3.compact_fc is not None: fc2, fc3 = solver_o2o3.compact_fc elif solver_o2o3.full_fc is not None: fc2, fc3 = solver_o2o3.full_fc else: raise RuntimeError("Failed to obtain force constants") self._force_constants[2] = fc2 self._force_constants[3] = fc3 elif _orders == (3, 4): basis_set_o3: FCBasisSetO3 = cast(FCBasisSetO3, self._basis_set[3]) basis_set_o4: FCBasisSetO4 = cast(FCBasisSetO4, self._basis_set[4]) solver_o3o4 = FCSolverO3O4( [basis_set_o3, basis_set_o4], use_mkl=self._use_mkl, log_level=self._log_level, ).solve(self._displacements, self._forces, batch_size=batch_size) if is_compact_fc and solver_o3o4.compact_fc is not None: fc3, fc4 = solver_o3o4.compact_fc elif solver_o3o4.full_fc is not None: fc3, fc4 = solver_o3o4.full_fc else: raise RuntimeError("Failed to obtain force constants") self._force_constants[3] = fc3 self._force_constants[4] = fc4 elif _orders == (2, 3, 4): basis_set_o2: FCBasisSetO2 = cast(FCBasisSetO2, self._basis_set[2]) basis_set_o3: FCBasisSetO3 = cast(FCBasisSetO3, self._basis_set[3]) basis_set_o4: FCBasisSetO4 = cast(FCBasisSetO4, self._basis_set[4]) solver_o2o3o4 = FCSolverO2O3O4( [basis_set_o2, basis_set_o3, basis_set_o4], use_mkl=self._use_mkl, log_level=self._log_level, ).solve(self._displacements, self._forces, batch_size=batch_size) if is_compact_fc and solver_o2o3o4.compact_fc is not None: fc2, fc3, fc4 = solver_o2o3o4.compact_fc elif solver_o2o3o4.full_fc is not None: fc2, fc3, fc4 = solver_o2o3o4.full_fc else: raise RuntimeError("Failed to obtain force constants") self._force_constants[2] = fc2 self._force_constants[3] = fc3 self._force_constants[4] = fc4 return self def compute_basis_set( self, max_order: int | None = None, orders: list | None = None, ) -> Symfc: """Run basis set calculations. Parameters ---------- max_order : int Maximum fc order. orders: list Orders of force constants. """ for order in self._check_orders(max_order, orders): if order == 2: basis_set_o2 = FCBasisSetO2( self._supercell, spacegroup_operations=self._spacegroup_operations, cutoff=self._cutoff[2], use_mkl=self._use_mkl, log_level=self._log_level, ).run() self._basis_set[2] = basis_set_o2 elif order == 3: basis_set_o3 = FCBasisSetO3( self._supercell, spacegroup_operations=self._spacegroup_operations, cutoff=self._cutoff[3], use_mkl=self._use_mkl, log_level=self._log_level, ).run() self._basis_set[3] = basis_set_o3 elif order == 4: basis_set_o4 = FCBasisSetO4( self._supercell, spacegroup_operations=self._spacegroup_operations, cutoff=self._cutoff[4], use_mkl=self._use_mkl, log_level=self._log_level, ).run() self._basis_set[4] = basis_set_o4 return self def estimate_basis_size( self, max_order: int | None = None, orders: list | None = None, ) -> dict: """Estimate the size of basis set. Parameters ---------- max_order : int Maximum fc order. orders: list Orders of force constants. Returns ------- dict : Estimates of basis set sizes for each order. The key of dict is the order. """ basis_size_estimates = {} for order in self._check_orders(max_order, orders): if order < 2 or order > 4: raise NotImplementedError( "Only fc2, fc3 and fc4 basis sets are implemented." ) if order == 2: basis_size_estimates[order] = FCBasisSetO2( self._supercell, spacegroup_operations=self._spacegroup_operations, cutoff=self._cutoff[2], use_mkl=self._use_mkl, log_level=self._log_level, ).estimate_basis_size() elif order == 3: basis_size_estimates[order] = FCBasisSetO3( self._supercell, spacegroup_operations=self._spacegroup_operations, cutoff=self._cutoff[3], use_mkl=self._use_mkl, log_level=self._log_level, ).estimate_basis_size() elif order == 4: basis_size_estimates[order] = FCBasisSetO4( self._supercell, spacegroup_operations=self._spacegroup_operations, cutoff=self._cutoff[4], use_mkl=self._use_mkl, log_level=self._log_level, ).estimate_basis_size() return basis_size_estimates def _check_orders(self, max_order: int | None, orders: list | None) -> tuple: if max_order is None and orders is None: raise RuntimeError("Maximum order and orders not found.") if max_order is not None: if max_order not in (2, 3, 4): raise NotImplementedError( "Only fc2, fc3 and fc4 basis sets are implemented." ) _orders = tuple(list(range(2, max_order + 1))) elif orders is not None: _orders = tuple(sorted(orders)) if _orders not in [(2,), (3,), (4,), (2, 3), (3, 4), (2, 3, 4)]: raise RuntimeError("Invalid FC orders.") return _orders def _check_dataset(self): if self._displacements is None and self._displacements_fd is None: raise RuntimeError("Dispalcements not found.") if self._displacements_fd is not None: if self._atoms_fd is None: raise RuntimeError("Atoms not found.") if self._forces is None: raise RuntimeError("Forces not found.") if self._displacements is not None: if self._displacements.shape != self._forces.shape: raise RuntimeError("Shape mismatch between dispalcements and forces.") if self._displacements.ndim != 3 or self._displacements.shape[1:] != ( len(self._supercell), 3, ): raise RuntimeError( "Inconsistent array shape of displacements " f"{self._displacements.shape} with respect to supercell " f"{len(self._supercell)}." ) if self._forces.ndim != 3 or self._forces.shape[1:] != ( len(self._supercell), 3, ): raise RuntimeError( "Inconsistent array shape of forces " f"{self._forces.shape} with respect to supercell " f"{len(self._supercell)}." ) def _prepare_cutoff(self, cutoff): if cutoff is None: self._cutoff = {2: None, 3: None, 4: None} else: self._cutoff = {} for order in (2, 3, 4): if order in cutoff: self._cutoff[order] = cutoff[order] else: self._cutoff[order] = None def eigh( p: NDArray, atol: float = 1e-8, rtol: float = 0.0, log_level: int = 0, ) -> NDArray: """Solve eigenvalue problem for projector in numpy ndarray. Parameters ---------- p: NDArray Projection matrix to be solved. atol : float, optional atol used in np.isclose. rtol : float, optional rtol used in np.isclose. log_level : int, optional Log level, by default 0. Return ------ Eigenvectors with eigenvalues = 1.0 in NDArray format. Eigenvectors with eigenvalues < 1.0 are eliminated. """ return eigh_projector(p, atol=atol, rtol=rtol, verbose=log_level > 0) def eigsh( p: csr_array, atol: float = 1e-8, rtol: float = 0.0, is_large_block: bool = False, log_level: int = 0, ) -> csr_array | NDArray: """Solve eigenvalue problem for projector in scipy sparse csr_array. Parameters ---------- p: csr_array Projection matrix to be solved. atol : float, optional atol used in np.isclose. rtol : float, optional rtol used in np.isclose. is_large_block: bool, optional Use an algorithm for solving projector with large block matrices. log_level : int, optional Log level, by default 0. Return ------ Eigenvectors with eigenvalues = 1.0. If is_large_block is True, eigenvectors in NDArray are returned. Otherwise, eigenvectors in csr_array are returned. Eigenvectors with eigenvalues < 1.0 are eliminated. """ if is_large_block: return eigsh_projector_sumrule(p, atol=atol, rtol=rtol, verbose=log_level > 0) return eigsh_projector(p, atol=atol, rtol=rtol, verbose=log_level > 0) symfc-1.6.0/src/symfc/basis_sets/000077500000000000000000000000001511276756400167245ustar00rootroot00000000000000symfc-1.6.0/src/symfc/basis_sets/__init__.py000066400000000000000000000005701511276756400210370ustar00rootroot00000000000000"""Symmetry adapted basis sets of force constants.""" from .basis_sets_base import FCBasisSetBase from .basis_sets_O1 import FCBasisSetO1 from .basis_sets_O2 import FCBasisSetO2 from .basis_sets_O3 import FCBasisSetO3 from .basis_sets_O4 import FCBasisSetO4 __all__ = [ "FCBasisSetBase", "FCBasisSetO1", "FCBasisSetO2", "FCBasisSetO3", "FCBasisSetO4", ] symfc-1.6.0/src/symfc/basis_sets/basis_sets_O1.py000066400000000000000000000101001511276756400217640ustar00rootroot00000000000000"""Symmetry adapted basis sets of 1st order force constants.""" from __future__ import annotations from typing import Optional import numpy as np from scipy.sparse import csr_array from symfc.spg_reps import SpgRepsO1 from symfc.utils.eig_tools import eigsh_projector from symfc.utils.translation_tools_O1 import compressed_projector_sum_rules from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O1 import ( get_compr_coset_reps_sum, get_lat_trans_compr_matrix, get_lat_trans_decompr_indices, ) from .basis_sets_base import FCBasisSetBase class FCBasisSetO1(FCBasisSetBase): """Dense symmetry adapted basis set for 1st order force constants. Attributes ---------- basis_set : ndarray Compressed force constants basis set. The first dimension n_x (< n_a) is given as a result of compression, which is depends on the system. shape=(n_x, n_bases), dtype='double' full_basis_set : ndarray Full (decompressed) force constants basis set. shape=(N * 3, n_bases), dtype='double' translation_permutations : ndarray Atom indices after lattice translations. shape=(lattice_translations, supercell_atoms), dtype=int. """ def __init__( self, supercell: SymfcAtoms, spacegroup_operations: Optional[dict] = None, use_mkl: bool = False, log_level: int = 0, ): """Init method. Parameters ---------- supercell : SymfcAtoms Supercell. spacegroup_operations : dict, optional Space group operations in supercell, by default None. When None, spglib is used. The following keys and values correspond to spglib symmetry dataset: rotations : array_like translations : array_like log_level : int, optional Log level. Default is 0. """ super().__init__(supercell, use_mkl=use_mkl, log_level=log_level) self._spg_reps = SpgRepsO1( supercell, spacegroup_operations=spacegroup_operations ) self._n_a_compression_matrix: Optional[csr_array] = None self._basis_set: Optional[np.ndarray] = None # Unused in O1, just dummy variable to satisfy the base class self._atomic_decompr_idx: np.ndarray @property def full_basis_set(self) -> Optional[csr_array]: """Return full (decompressed) basis set. shape=(N*3, n_bases), dtype='double'. Data in first dimension is ordered by (N,3). """ return self._full_basis_set @property def compact_compression_matrix(self) -> Optional[np.ndarray]: """Return compression matrix for compact basis set.""" pass @property def compression_matrix(self) -> Optional[np.ndarray]: """Return compression matrix.""" pass @property def atomic_decompr_idx(self) -> np.ndarray: """Return atomic permutations by lattice translations.""" raise NotImplementedError( "Atomic decompression indices are not defined for O1 basis sets." ) def run(self) -> FCBasisSetO1: """Compute compressed force constants basis set.""" c_trans = self._get_c_trans() coset_reps_sum = get_compr_coset_reps_sum(self._spg_reps) # type: ignore proj_rt = coset_reps_sum if len(proj_rt.data) == 0: raise ValueError("No basis vectors exist.") c_rt = eigsh_projector(proj_rt, verbose=self._log_level > 0) compress_mat = c_trans @ c_rt proj = compressed_projector_sum_rules(compress_mat, self._natom) basis_set = eigsh_projector(proj, verbose=self._log_level > 0) self._full_basis_set = compress_mat @ basis_set self._basis_set = basis_set.toarray() return self def _get_c_trans(self) -> csr_array: trans_perms = self._spg_reps.translation_permutations n_lp, N = trans_perms.shape decompr_idx = get_lat_trans_decompr_indices(trans_perms) c_trans = get_lat_trans_compr_matrix(decompr_idx, N, n_lp) return c_trans symfc-1.6.0/src/symfc/basis_sets/basis_sets_O2.py000066400000000000000000000151021511276756400217740ustar00rootroot00000000000000"""Symmetry adapted basis sets of 2nd order force constants.""" from __future__ import annotations from typing import Optional import numpy as np from scipy.sparse import csr_array from symfc.spg_reps import SpgRepsO2 from symfc.utils.eig_tools import ( eigsh_projector, eigsh_projector_sumrule, ) from symfc.utils.matrix import BlockMatrixNode try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass from symfc.utils.permutation_tools_O2 import compr_permutation_lat_trans_O2 from symfc.utils.rotation_tools_O2 import complementary_compr_projector_rot_sum_rules_O2 from symfc.utils.translation_tools_O2 import compressed_projector_sum_rules_O2 from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O2 import ( _get_atomic_lat_trans_decompr_indices, get_compr_coset_projector_O2, get_lat_trans_compr_matrix_O2, ) from .basis_sets_base import FCBasisSetBase class FCBasisSetO2(FCBasisSetBase): r"""Symmetry adapted basis set for 2nd order force constants. Attributes ---------- basis_set : ndarray Compressed force constants basis set. The first dimension n_compr (<< 9 * N ** 2, \sim n_bases) is given as a result of compression, which depends on the system. shape=(n_compr, n_bases), dtype='double' n_a_compression_matrix : csr_array Compression matrix compressed by lattice translation. The basis set compressed only by lattice translation is obtained by n_a_compression_matrix @ basis_set. shape=(n_a * N * 9, n_compr), dtype='double' translation_permutations : ndarray Atom indices after lattice translations. shape=(lattice_translations, supercell_atoms), dtype=int. """ def __init__( self, supercell: SymfcAtoms, cutoff: Optional[float] = None, spacegroup_operations: Optional[dict] = None, use_mkl: bool = False, log_level: int = 0, ): """Init method. Parameters ---------- supercell : SymfcAtoms Supercell. cutoff: float Cutoff distance in angstroms. Default is None. spacegroup_operations : dict, optional Space group operations in supercell, by default None. When None, spglib is used. The following keys and values correspond to spglib symmetry dataset: rotations : array_like translations : array_like use_mkl : bool Use MKL or not. Default is False. log_level : int, optional Log level. Default is 0. """ super().__init__(supercell, cutoff=cutoff, use_mkl=use_mkl, log_level=log_level) self._spg_reps = SpgRepsO2( supercell, spacegroup_operations=spacegroup_operations ) trans_perms = self._spg_reps.translation_permutations self._atomic_decompr_idx = _get_atomic_lat_trans_decompr_indices(trans_perms) self._n_a_compression_matrix: Optional[csr_array] = None self._basis_set: Optional[np.ndarray] = None self._blocked_basis_set: Optional[BlockMatrixNode] = None @property def compression_matrix(self) -> Optional[csr_array]: """Return compression matrix. This expands fc basis_sets to (N*N*3*3, n_bases). """ if self._n_a_compression_matrix is None: raise ValueError( "Compression matrix is not computed. Call run() method to compute it." ) trans_perms = self._spg_reps.translation_permutations c_trans = get_lat_trans_compr_matrix_O2(trans_perms) return dot_product_sparse( c_trans, self._n_a_compression_matrix, use_mkl=self._use_mkl ) @property def compact_compression_matrix(self) -> Optional[csr_array]: """Return compact compression matrix. This expands fc basis_sets to (n_a*N*3*3, n_bases). """ if self._n_a_compression_matrix is None: raise ValueError( "Compression matrix is not computed. Call run() method to compute it." ) n_lp = self.translation_permutations.shape[0] return self._n_a_compression_matrix / np.sqrt(n_lp) def run(self, rotational_sum_rules: bool = False) -> FCBasisSetO2: """Compute compressed force constants basis set.""" trans_perms = self._spg_reps.translation_permutations c_pt = compr_permutation_lat_trans_O2( trans_perms, atomic_decompr_idx=self._atomic_decompr_idx, fc_cutoff=self._fc_cutoff, verbose=self._log_level > 0, ) proj_rpt = get_compr_coset_projector_O2( self._spg_reps, # type: ignore fc_cutoff=self._fc_cutoff, atomic_decompr_idx=self._atomic_decompr_idx, c_pt=c_pt, # verbose=self._log_level > 0, ) c_rpt = eigsh_projector(proj_rpt, verbose=self._log_level > 0) n_a_compress_mat = dot_product_sparse(c_pt, c_rpt, use_mkl=self._use_mkl) proj = compressed_projector_sum_rules_O2( trans_perms, n_a_compress_mat, atomic_decompr_idx=self._atomic_decompr_idx, fc_cutoff=self._fc_cutoff, use_mkl=self._use_mkl, # verbose=self._log_level > 0, ) if rotational_sum_rules: proj_rot_cmplt = complementary_compr_projector_rot_sum_rules_O2( self._supercell, trans_perms, n_a_compress_mat, use_mkl=self._use_mkl, ) proj -= proj_rot_cmplt eigvecs = eigsh_projector_sumrule( proj, use_mkl=self._use_mkl, verbose=self._log_level > 0, ) self._blocked_basis_set = eigvecs self._n_a_compression_matrix = n_a_compress_mat return self def estimate_basis_size(self) -> int: """Estimate basis set size.""" if self._fc_cutoff is None: n_sym, N = self._spg_reps._permutations.shape basis_size_estimates = 9 * (N**2) / n_sym / 2 return int(np.round(basis_size_estimates).astype(int)) trans_perms = self._spg_reps.translation_permutations c_pt = compr_permutation_lat_trans_O2( trans_perms, atomic_decompr_idx=self._atomic_decompr_idx, fc_cutoff=self._fc_cutoff, verbose=False, ) n_sym_prim = len(self._spg_reps._unique_rotations) basis_size_estimates = c_pt.shape[1] / n_sym_prim # type: ignore return int(np.round(basis_size_estimates).astype(int)) symfc-1.6.0/src/symfc/basis_sets/basis_sets_O3.py000066400000000000000000000200471511276756400220010ustar00rootroot00000000000000"""Symmetry adapted basis sets of 3rd order force constants.""" from __future__ import annotations import time from typing import Optional, Union import numpy as np from scipy.sparse import coo_array, csr_array from symfc.spg_reps import SpgRepsO3 from symfc.utils.eig_tools import ( eigsh_projector, eigsh_projector_sumrule, ) from symfc.utils.matrix import BlockMatrixNode try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass from symfc.utils.permutation_tools_O3 import compr_permutation_lat_trans_O3 from symfc.utils.translation_tools_O3 import compressed_projector_sum_rules_O3 from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O3 import ( get_atomic_lat_trans_decompr_indices_O3, get_compr_coset_projector_O3, get_lat_trans_compr_matrix_O3, ) from . import FCBasisSetBase def print_sp_matrix_size(c: Union[csr_array, coo_array], header: str): """Show sparse matrix size.""" print(header, c.shape, len(c.data), flush=True) class FCBasisSetO3(FCBasisSetBase): r"""Symmetry adapted basis set for 3rd order force constants. Attributes ---------- basis_set : ndarray Compressed force constants basis set. The first dimension n_compr (<< 27 * N ** 3, \sim n_bases) is given as a result of compression, which depends on the system. shape=(n_compr, n_bases), dtype='double' n_a_compression_matrix : csr_array Compression matrix compressed by lattice translation. The basis set compressed only by lattice translation is obtained by n_a_compression_matrix @ basis_set. shape=(n_a * N * N * 27, n_compr), dtype='double' translation_permutations : ndarray Atom indices after lattice translations. shape=(lattice_translations, supercell_atoms), dtype=int. """ def __init__( self, supercell: SymfcAtoms, cutoff: Optional[float] = None, spacegroup_operations: Optional[dict] = None, use_mkl: bool = False, log_level: int = 0, ): """Init method. Parameters ---------- supercell : SymfcAtoms Supercell. cutoff: float Cutoff distance in angstroms. Default is None. spacegroup_operations : dict, optional Space group operations in supercell, by default None. When None, spglib is used. The following keys and values correspond to spglib symmetry dataset: rotations : array_like translations : array_like use_mkl : bool Use MKL or not. Default is False. log_level : int, optional Log level. Default is 0. """ super().__init__(supercell, cutoff=cutoff, use_mkl=use_mkl, log_level=log_level) self._spg_reps = SpgRepsO3( supercell, spacegroup_operations=spacegroup_operations ) trans_perms = self._spg_reps.translation_permutations self._atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O3(trans_perms) self._n_a_compression_matrix: Optional[csr_array] = None self._basis_set: Optional[np.ndarray] = None self._blocked_basis_set: Optional[BlockMatrixNode] = None @property def compression_matrix(self) -> Optional[csr_array]: """Return compression matrix. This expands fc basis_sets to (N*N*N*3*3*3, n_bases). """ if self._n_a_compression_matrix is None: raise ValueError( "Compression matrix is not computed yet. " "Call run() method to compute it." ) trans_perms = self._spg_reps.translation_permutations c_trans = get_lat_trans_compr_matrix_O3(trans_perms) return dot_product_sparse( c_trans, self._n_a_compression_matrix, use_mkl=self._use_mkl ) @property def compact_compression_matrix(self) -> Optional[csr_array]: """Return compact compression matrix. This expands fc basis_sets to (n_a*N*N*3*3*3, n_bases). """ if self._n_a_compression_matrix is None: raise ValueError( "Compression matrix is not computed yet. " "Call run() method to compute it." ) n_lp = self.translation_permutations.shape[0] return self._n_a_compression_matrix / np.sqrt(n_lp) def run(self) -> FCBasisSetO3: """Compute compressed force constants basis set.""" trans_perms = self._spg_reps.translation_permutations tt0 = time.time() c_pt = compr_permutation_lat_trans_O3( trans_perms, atomic_decompr_idx=self._atomic_decompr_idx, fc_cutoff=self._fc_cutoff, verbose=self._log_level > 0, ) if self._log_level: print(" c_pt (size) :", c_pt.shape, flush=True) tt2 = time.time() proj_rpt = get_compr_coset_projector_O3( self._spg_reps, # type: ignore fc_cutoff=self._fc_cutoff, atomic_decompr_idx=self._atomic_decompr_idx, c_pt=c_pt, use_mkl=self._use_mkl, verbose=self._log_level > 0, ) tt3 = time.time() c_rpt = eigsh_projector(proj_rpt, verbose=self._log_level > 0) if self._log_level: print(" c_rpt (size) :", c_rpt.shape, flush=True) tt4 = time.time() n_a_compress_mat = dot_product_sparse(c_pt, c_rpt, use_mkl=self._use_mkl) tt5 = time.time() proj = compressed_projector_sum_rules_O3( trans_perms, n_a_compress_mat, atomic_decompr_idx=self._atomic_decompr_idx, fc_cutoff=self._fc_cutoff, use_mkl=self._use_mkl, verbose=self._log_level > 0, ) tt6 = time.time() eigvecs = eigsh_projector_sumrule( proj, verbose=self._log_level > 0, use_mkl=self._use_mkl, ) if self._log_level: print("Final size of basis set:", eigvecs.shape, flush=True) tt7 = time.time() if self._log_level: print( "Time (perm @ ltrans) :", "{:.3f}".format(tt2 - tt0), flush=True, ) print( "Time (coset) :", "{:.3f}".format(tt3 - tt2), flush=True, ) print( "Time (eigh(coset @ perm @ ltrans)) :", "{:.3f}".format(tt4 - tt3), flush=True, ) print( "Time (c_pt @ c_rpt) :", "{:.3f}".format(tt5 - tt4), flush=True, ) print( "Time (proj(sum)) :", "{:.3f}".format(tt6 - tt5), flush=True, ) print( "Time (eigh(sum)) :", "{:.3f}".format(tt7 - tt6), flush=True, ) print("---", flush=True) print( "Time (Basis FC3) :", "{:.3f}".format(tt7 - tt0), flush=True, ) self._blocked_basis_set = eigvecs self._n_a_compression_matrix = n_a_compress_mat return self def estimate_basis_size(self) -> int: """Estimate basis set size.""" if self._fc_cutoff is None: n_sym, N = self._spg_reps._permutations.shape basis_size_estimates = 27 * (N**3) / n_sym / 6 return int(np.round(basis_size_estimates).astype(int)) trans_perms = self._spg_reps.translation_permutations c_pt = compr_permutation_lat_trans_O3( trans_perms, atomic_decompr_idx=self._atomic_decompr_idx, fc_cutoff=self._fc_cutoff, verbose=False, ) n_sym_prim = len(self._spg_reps._unique_rotations) basis_size_estimates = c_pt.shape[1] / n_sym_prim # type: ignore return int(np.round(basis_size_estimates).astype(int)) symfc-1.6.0/src/symfc/basis_sets/basis_sets_O4.py000066400000000000000000000175651511276756400220150ustar00rootroot00000000000000"""Symmetry adapted basis sets of 4th order force constants.""" from __future__ import annotations import time from typing import Optional import numpy as np from scipy.sparse import csr_array from symfc.spg_reps import SpgRepsO4 from symfc.utils.eig_tools import ( eigsh_projector, eigsh_projector_sumrule, ) from symfc.utils.matrix import BlockMatrixNode try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass from symfc.utils.permutation_tools_O4 import compr_permutation_lat_trans_O4 from symfc.utils.translation_tools_O4 import compressed_projector_sum_rules_O4 from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O4 import ( get_atomic_lat_trans_decompr_indices_O4, get_compr_coset_projector_O4, get_lat_trans_compr_matrix_O4, ) from . import FCBasisSetBase class FCBasisSetO4(FCBasisSetBase): r"""Symmetry adapted basis set for 4th order force constants. Attributes ---------- basis_set : ndarray Compressed force constants basis set. The first dimension n_compr (<< 81 * N ** 4, \sim n_bases) is given as a result of compression, which depends on the system. shape=(n_compr, n_bases), dtype='double' n_a_compression_matrix : csr_array Compression matrix compressed by lattice translation. The basis set compressed only by lattice translation is obtained by n_a_compression_matrix @ basis_set. shape=(n_a * N * N * N * 81, n_compr), dtype='double' translation_permutations : ndarray Atom indices after lattice translations. shape=(lattice_translations, supercell_atoms), dtype=int. """ def __init__( self, supercell: SymfcAtoms, cutoff: Optional[float] = None, spacegroup_operations: Optional[dict] = None, use_mkl: bool = False, log_level: int = 0, ): """Init method. Parameters ---------- supercell : SymfcAtoms Supercell. cutoff: float Cutoff distance in angstroms. Default is None. spacegroup_operations : dict, optional Space group operations in supercell, by default None. When None, spglib is used. The following keys and values correspond to spglib symmetry dataset: rotations : array_like translations : array_like use_mkl : bool Use MKL or not. Default is False. log_level : int, optional Log level. Default is 0. """ super().__init__(supercell, cutoff=cutoff, use_mkl=use_mkl, log_level=log_level) self._spg_reps = SpgRepsO4( supercell, spacegroup_operations=spacegroup_operations ) trans_perms = self._spg_reps.translation_permutations self._atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O4(trans_perms) self._n_a_compression_matrix: Optional[csr_array] = None self._basis_set: Optional[np.ndarray] = None self._blocked_basis_set: Optional[BlockMatrixNode] = None @property def compression_matrix(self) -> Optional[csr_array]: """Return compression matrix. This expands fc basis_sets to (N*N*N*N*3*3*3*3, n_bases). """ if self._n_a_compression_matrix is None: raise ValueError( "Compression matrix is not computed yet. " "Call run() method to compute it." ) trans_perms = self._spg_reps.translation_permutations c_trans = get_lat_trans_compr_matrix_O4(trans_perms) return dot_product_sparse( c_trans, self._n_a_compression_matrix, use_mkl=self._use_mkl ) @property def compact_compression_matrix(self) -> Optional[csr_array]: """Return compact compression matrix. This expands fc basis_sets to (n_a*N*N*N*3*3*3*3, n_bases). """ if self._n_a_compression_matrix is None: raise ValueError( "Compression matrix is not computed yet. " "Call run() method to compute it." ) n_lp = self.translation_permutations.shape[0] return self._n_a_compression_matrix / np.sqrt(n_lp) def run(self) -> FCBasisSetO4: """Compute compressed force constants basis set.""" trans_perms = self._spg_reps.translation_permutations tt0 = time.time() c_pt = compr_permutation_lat_trans_O4( trans_perms, atomic_decompr_idx=self._atomic_decompr_idx, fc_cutoff=self._fc_cutoff, verbose=self._log_level > 0, ) if self._log_level: print(" c_pt (size) :", c_pt.shape, flush=True) tt2 = time.time() proj_rpt = get_compr_coset_projector_O4( self._spg_reps, # type: ignore fc_cutoff=self._fc_cutoff, atomic_decompr_idx=self._atomic_decompr_idx, c_pt=c_pt, use_mkl=self._use_mkl, verbose=self._log_level > 0, ) tt3 = time.time() c_rpt = eigsh_projector(proj_rpt, verbose=self._log_level > 0) if self._log_level: print(" c_rpt (size) :", c_rpt.shape, flush=True) tt4 = time.time() n_a_compress_mat = dot_product_sparse(c_pt, c_rpt, use_mkl=self._use_mkl) tt5 = time.time() proj = compressed_projector_sum_rules_O4( trans_perms, n_a_compress_mat, atomic_decompr_idx=self._atomic_decompr_idx, fc_cutoff=self._fc_cutoff, use_mkl=self._use_mkl, verbose=self._log_level > 0, ) tt6 = time.time() eigvecs = eigsh_projector_sumrule( proj, use_mkl=self._use_mkl, verbose=self._log_level > 0, ) if self._log_level: print("Final size of basis set:", eigvecs.shape, flush=True) tt7 = time.time() if self._log_level: print( "Time (perm @ ltrans) :", "{:.3f}".format(tt2 - tt0), flush=True, ) print( "Time (coset) :", "{:.3f}".format(tt3 - tt2), flush=True, ) print( "Time (eigh(coset @ perm @ ltrans)) :", "{:.3f}".format(tt4 - tt3), flush=True, ) print( "Time (c_pt @ c_rpt) :", "{:.3f}".format(tt5 - tt4), flush=True, ) print( "Time (proj(sum)) :", "{:.3f}".format(tt6 - tt5), flush=True, ) print( "Time (eigh(sum)) :", "{:.3f}".format(tt7 - tt6), flush=True, ) print("---") print( "Time (Basis FC4) :", "{:.3f}".format(tt7 - tt0), flush=True, ) self._blocked_basis_set = eigvecs self._n_a_compression_matrix = n_a_compress_mat return self def estimate_basis_size(self) -> int: """Estimate basis set size.""" if self._fc_cutoff is None: n_sym, N = self._spg_reps._permutations.shape basis_size_estimates = 81 * (N**4) / n_sym / 24 return int(np.round(basis_size_estimates).astype(int)) trans_perms = self._spg_reps.translation_permutations c_pt = compr_permutation_lat_trans_O4( trans_perms, atomic_decompr_idx=self._atomic_decompr_idx, fc_cutoff=self._fc_cutoff, verbose=False, ) n_sym_prim = len(self._spg_reps._unique_rotations) basis_size_estimates = c_pt.shape[1] / n_sym_prim # type: ignore return int(np.round(basis_size_estimates).astype(int)) symfc-1.6.0/src/symfc/basis_sets/basis_sets_base.py000066400000000000000000000061301511276756400224270ustar00rootroot00000000000000"""Symmetry adapted basis sets of force constants.""" from __future__ import annotations from abc import ABC, abstractmethod from typing import Optional import numpy as np from symfc.spg_reps import SpgRepsBase from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.matrix import BlockMatrixNode from symfc.utils.utils import SymfcAtoms class FCBasisSetBase(ABC): """Abstract base class of symmetry adapted basis set for force constants.""" def __init__( self, supercell: SymfcAtoms, cutoff: Optional[float] = None, use_mkl: bool = False, log_level: int = 0, ): """Init method. Parameters ---------- supercell : SymfcAtoms Supercell. cutoff: float Cutoff distance in angstroms. Default is None. use_mkl : bool Use MKL or not. Default is False. log_level : int, optional Log level. Default is 0. """ self._supercell = supercell self._natom = len(supercell) self._use_mkl = use_mkl self._log_level = log_level self._spg_reps: SpgRepsBase self._atomic_decompr_idx: np.ndarray self._basis_set: np.ndarray self._blocked_basis_set: BlockMatrixNode if cutoff is None: self._fc_cutoff = None else: self._fc_cutoff = FCCutoff(supercell, cutoff=cutoff) @property @abstractmethod def compact_compression_matrix(self) -> Optional[np.ndarray]: """Return compression matrix for compact basis set.""" pass @property @abstractmethod def compression_matrix(self) -> Optional[np.ndarray]: """Return compression matrix.""" pass @property def basis_set(self) -> Optional[np.ndarray]: """Return compressed basis set. shape=(n_c, n_bases), dtype='double'. """ return self._blocked_basis_set.recover() @property def blocked_basis_set(self) -> Optional[BlockMatrixNode]: """Return compressed basis set in blocked format.""" return self._blocked_basis_set @property def atomic_decompr_idx(self) -> np.ndarray: """Return atomic permutations by lattice translations.""" return self._atomic_decompr_idx @property def translation_permutations(self) -> np.ndarray: """Return permutations by lattice translation.""" if self._spg_reps is None: raise ValueError("SpgRepsBase is not set.") return self._spg_reps.translation_permutations @property def p2s_map(self) -> np.ndarray: """Return indices of translationally independent atoms.""" if self._spg_reps is None: raise ValueError("SpgRepsBase is not set.") if self._spg_reps.p2s_map is None: raise ValueError("p2s_map is not set.") return self._spg_reps.p2s_map @property def fc_cutoff(self) -> Optional[FCCutoff]: """Return force constants cutoff.""" return self._fc_cutoff @abstractmethod def run(self): """Run basis set calculation.""" pass symfc-1.6.0/src/symfc/solvers/000077500000000000000000000000001511276756400162625ustar00rootroot00000000000000symfc-1.6.0/src/symfc/solvers/__init__.py000066400000000000000000000010001511276756400203620ustar00rootroot00000000000000"""Force constants solvers.""" from .solver_base import FCSolverBase from .solver_O2 import FCSolverO2 from .solver_O2O3 import FCSolverO2O3 from .solver_O2O3O4 import FCSolverO2O3O4 from .solver_O3 import FCSolverO3 from .solver_O3O4 import FCSolverO3O4 from .solver_O4 import FCSolverO4 from .sparse_solver_O2 import FCSparseSolverO2 __all__ = [ "FCSolverBase", "FCSolverO2", "FCSolverO2O3", "FCSolverO2O3O4", "FCSolverO3", "FCSolverO4", "FCSolverO3O4", "FCSparseSolverO2", ] symfc-1.6.0/src/symfc/solvers/solver_O2.py000066400000000000000000000200451511276756400205070ustar00rootroot00000000000000"""2nd order force constants solver.""" from __future__ import annotations import time from typing import Literal, Optional import numpy as np from scipy.sparse import csr_array from symfc.basis_sets import FCBasisSetO2 from symfc.utils.matrix import block_matrix_sandwich from symfc.utils.solver_funcs import get_batch_slice, solve_linear_equation try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass from .solver_base import FCSolverBase class FCSolverO2(FCSolverBase): """Third order force constants solver.""" def __init__( self, basis_set: FCBasisSetO2, use_mkl: bool = False, log_level: int = 0, ): """Init method.""" self._basis_set: FCBasisSetO2 super().__init__(basis_set, use_mkl=use_mkl, log_level=log_level) def solve( self, displacements: np.ndarray, forces: np.ndarray, batch_size: int = 100, ) -> FCSolverO2: """Solve coefficients of basis set from displacements and forces. Parameters ---------- displacements : ndarray Displacements of atoms in Cartesian coordinates. shape=(n_snapshot, N, 3), dtype='double' forces : ndarray Forces of atoms in Cartesian coordinates. shape=(n_snapshot, N, 3), dtype='double' Returns ------- self : FCSolverO2 """ n_data = forces.shape[0] f = forces.reshape(n_data, -1) d = displacements.reshape(n_data, -1) fc2_basis = self._basis_set compress_mat_fc2 = fc2_basis.compact_compression_matrix basis_set_fc2 = fc2_basis.blocked_basis_set atomic_decompr_idx_fc2 = fc2_basis.atomic_decompr_idx self._coefs = run_solver_O2( d, f, compress_mat_fc2, basis_set_fc2, atomic_decompr_idx_fc2, batch_size=batch_size, use_mkl=self._use_mkl, verbose=self._log_level > 0, ) return self @property def full_fc(self) -> Optional[np.ndarray]: """Return full force constants. Returns ------- np.ndarray shape=(N, N, 3, 3), dtype='double', order='C' """ return self._recover_fcs("full") @property def compact_fc(self) -> Optional[np.ndarray]: """Return full force constants. Returns ------- np.ndarray shape=(n_a, N, 3, 3), dtype='double', order='C' """ return self._recover_fcs("compact") def _recover_fcs( self, comp_mat_type: Literal["full", "compact"] ) -> Optional[np.ndarray]: fc2_basis = self._basis_set if self._coefs is None or fc2_basis.basis_set is None: return None if comp_mat_type == "full": comp_mat_fc2 = fc2_basis.compression_matrix elif comp_mat_type == "compact": comp_mat_fc2 = fc2_basis.compact_compression_matrix else: raise ValueError("Invalid comp_mat_type.") N = self._natom fc2 = fc2_basis.blocked_basis_set.dot(self._coefs) fc2 = np.array( (comp_mat_fc2 @ fc2).reshape((-1, N, 3, 3)), dtype="double", order="C" ) return fc2 def reshape_nN33_nx_to_N3_n3nx(mat, N: int, n: int, n_batch: int = 1) -> csr_array: """Reorder and reshape a sparse matrix (nN33,nx)->(N3,n3nx). mat : csr_array Return reordered csr_matrix used for FC2. """ _, nx = mat.shape N3 = N * 3 n3nx = n * 3 * nx mat = mat.tocoo(copy=False) begin_batch, end_batch = get_batch_slice(len(mat.row), len(mat.row) // n_batch) for begin, end in zip(begin_batch, end_batch): div, rem = np.divmod(mat.row[begin:end], 9 * N) mat.col[begin:end] += div * 3 * nx div, rem = np.divmod(rem, 9) mat.row[begin:end] = div * 3 div, rem = np.divmod(rem, 3) mat.col[begin:end] += div * nx mat.row[begin:end] += rem mat.resize((N3, n3nx)) mat = mat.tocsr(copy=False) return mat def prepare_normal_equation_O2( disps: np.ndarray, forces: np.ndarray, compact_compress_mat_fc2, compress_eigvecs_fc2, atomic_decompr_idx_fc2: np.ndarray, batch_size: int = 100, use_sparse_disps: bool = False, use_mkl: bool = False, verbose: bool = False, ): r"""Calculate X.T @ X and X.T @ y. X = displacements @ compress_mat @ compress_eigvecs displacements (fc2): (n_samples, N3) compact_compress_mat_fc2: (n_aN33, n_compr) compress_eigvecs_fc2: (n_compr_fc2, n_basis_fc2) Matrix reshapings are appropriately applied to compress_mat and its products. X.T @ X and X.T @ y are sequentially calculated using divided dataset. X.T @ X = \sum_i X_i.T @ X_i X.T @ y = \sum_i X_i.T @ y_i (i: batch index) """ N3 = disps.shape[1] N = N3 // 3 n_compr_fc2 = compact_compress_mat_fc2.shape[1] n_batch = 1 begin_batch_atom, end_batch_atom = get_batch_slice(N, N // n_batch) begin_batch, end_batch = get_batch_slice(disps.shape[0], batch_size) mat22 = np.zeros((n_compr_fc2, n_compr_fc2), dtype=float) mat2y = np.zeros(n_compr_fc2, dtype=float) t_all1 = time.time() const_fc2 = -1.0 compact_compress_mat_fc2 *= const_fc2 for begin_i, end_i in zip(begin_batch_atom, end_batch_atom): if verbose: print("-----", flush=True) print("Solver_atoms:", begin_i + 1, "--", end_i, "/", N, flush=True) n_atom_batch = end_i - begin_i t1 = time.time() decompr_idx = ( atomic_decompr_idx_fc2[begin_i * N : end_i * N, None] * 9 + np.arange(9)[None, :] ).reshape(-1) compr_mat_fc2 = reshape_nN33_nx_to_N3_n3nx( compact_compress_mat_fc2[decompr_idx], N, n_atom_batch, ) t2 = time.time() if verbose: print( "Time (Solver_compr_matrix_reshape):", "{:.3f}".format(t2 - t1), flush=True, ) for begin, end in zip(begin_batch, end_batch): t1 = time.time() X2 = dot_product_sparse( disps[begin:end], compr_mat_fc2, use_mkl=use_mkl, dense=not use_sparse_disps, ).reshape((-1, n_compr_fc2)) y = forces[begin:end, begin_i * 3 : end_i * 3].reshape(-1) mat22 += X2.T @ X2 mat2y += X2.T @ y t2 = time.time() if verbose: print("Solver_block:", end, "/", disps.shape[0], flush=True) print(" - Time:", "{:.3f}".format(t2 - t1), flush=True) del X2 if verbose: print("Solver:", "Calculate X.T @ X and X.T @ y", flush=True) XTX = block_matrix_sandwich(compress_eigvecs_fc2, compress_eigvecs_fc2, mat22) XTy = compress_eigvecs_fc2.transpose_dot(mat2y) compact_compress_mat_fc2 /= const_fc2 t_all2 = time.time() if verbose: print( " (disp @ compr @ eigvecs).T @ (disp @ compr @ eigvecs):", "{:.3f}".format(t_all2 - t_all1), flush=True, ) return XTX, XTy def run_solver_O2( disps: np.ndarray, forces: np.ndarray, compact_compress_mat_fc2, compress_eigvecs_fc2, atomic_decompr_idx_fc2: np.ndarray, batch_size: int = 100, use_sparse_disps: bool = False, use_mkl: bool = False, verbose: bool = False, ): """Estimate coeffs. in X @ coeffs = y. X = displacements_fc2 @ compress_mat_fc2 @ compress_eigvecs_fc2 Matrix reshapings are appropriately applied. X: features (n_samples * N3, N_basis_fc2) y: observations (forces), (n_samples * N3) """ XTX, XTy = prepare_normal_equation_O2( disps, forces, compact_compress_mat_fc2, compress_eigvecs_fc2, atomic_decompr_idx_fc2, batch_size=batch_size, use_sparse_disps=use_sparse_disps, use_mkl=use_mkl, verbose=verbose, ) coefs = solve_linear_equation(XTX, XTy) return coefs symfc-1.6.0/src/symfc/solvers/solver_O2O3.py000066400000000000000000000322051511276756400207120ustar00rootroot00000000000000"""Solver of 2nd and 3rd order force constants simultaneously.""" from __future__ import annotations import time from collections.abc import Sequence from typing import Literal, Optional, Union, cast import numpy as np from scipy.sparse import csr_array from symfc.basis_sets import FCBasisSetO2, FCBasisSetO3 from symfc.solvers.solver_O2 import reshape_nN33_nx_to_N3_n3nx from symfc.utils.matrix import BlockMatrixNode, block_matrix_sandwich from symfc.utils.solver_funcs import get_batch_slice, solve_linear_equation try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass from .solver_base import FCSolverBase class FCSolverO2O3(FCSolverBase): """Simultaneous second and third order force constants solver.""" def __init__( self, basis_set: Sequence[Union[FCBasisSetO2, FCBasisSetO3]], use_mkl: bool = False, log_level: int = 0, ): """Init method. Parameters ---------- basis_set : Sequence of (FCBasisSetO2, FCBasisSetO3) First element must be FCBasisSetO2 and second must be FCBasisSetO3. use_mkl : bool, optional Use MKL if True. Default is False. log_level : int, optional Logging level. Default is 0. """ if len(basis_set) != 2: raise ValueError("basis_set must contain exactly 2 elements") if not isinstance(basis_set[0], FCBasisSetO2): raise TypeError("First element must be FCBasisSetO2") if not isinstance(basis_set[1], FCBasisSetO3): raise TypeError("Second element must be FCBasisSetO3") self._basis_set: Sequence[Union[FCBasisSetO2, FCBasisSetO3]] super().__init__(basis_set, use_mkl=use_mkl, log_level=log_level) def solve( self, displacements: np.ndarray, forces: np.ndarray, batch_size: int = 100, ) -> FCSolverO2O3: """Solve force constants. Note ---- self._coefs = (coefs_fc2, coefs_fc3) Parameters ---------- displacements : ndarray Displacements of atoms in Cartesian coordinates. shape=(n_snapshot, N, 3), dtype='double' forces : ndarray Forces of atoms in Cartesian coordinates. shape=(n_snapshot, N, 3), dtype='double' Returns ------- ndarray Force constants. shape=(n_a, N, 3, 3) or (N, N, 3, 3). See `is_compact_fc` parameter. dtype='double', order='C' """ n_data = forces.shape[0] f = forces.reshape(n_data, -1) d = displacements.reshape(n_data, -1) fc2_basis: FCBasisSetO2 = cast(FCBasisSetO2, self._basis_set[0]) fc3_basis: FCBasisSetO3 = cast(FCBasisSetO3, self._basis_set[1]) compress_mat_fc2 = fc2_basis.compact_compression_matrix basis_set_fc2 = fc2_basis.blocked_basis_set compress_mat_fc3 = fc3_basis.compact_compression_matrix basis_set_fc3 = fc3_basis.blocked_basis_set atomic_decompr_idx_fc2 = fc2_basis.atomic_decompr_idx atomic_decompr_idx_fc3 = fc3_basis.atomic_decompr_idx if ( compress_mat_fc2 is None or compress_mat_fc3 is None or basis_set_fc2 is None or basis_set_fc3 is None ): raise ValueError( "Compression matrices or basis sets are not set. " "Call run() method to compute them." ) self._coefs = run_solver_O2O3( d, f, compress_mat_fc2, compress_mat_fc3, basis_set_fc2, basis_set_fc3, atomic_decompr_idx_fc2, atomic_decompr_idx_fc3, batch_size=batch_size, use_mkl=self._use_mkl, verbose=self._log_level > 0, ) return self @property def full_fc(self) -> Optional[tuple[np.ndarray, np.ndarray]]: """Return full force constants. Returns ------- tuple[np.ndarray, np.ndarray] shape=(N, N, 3, 3), dtype='double', order='C' shape=(N, N, N, 3, 3, 3), dtype='double', order='C' """ return self._recover_fcs("full") @property def compact_fc(self) -> Optional[tuple[np.ndarray, np.ndarray]]: """Return full force constants. Returns ------- tuple[np.ndarray, np.ndarray] shape=(n_a, N, 3, 3), dtype='double', order='C' shape=(n_a, N, N, 3, 3, 3), dtype='double', order='C' """ return self._recover_fcs("compact") def _recover_fcs( self, comp_mat_type: Literal["full", "compact"], ) -> Optional[tuple[np.ndarray, np.ndarray]]: if self._coefs is None: return None fc2_basis: FCBasisSetO2 = cast(FCBasisSetO2, self._basis_set[0]) fc3_basis: FCBasisSetO3 = cast(FCBasisSetO3, self._basis_set[1]) if comp_mat_type == "full": comp_mat_fc2 = fc2_basis.compression_matrix comp_mat_fc3 = fc3_basis.compression_matrix elif comp_mat_type == "compact": comp_mat_fc2 = fc2_basis.compact_compression_matrix comp_mat_fc3 = fc3_basis.compact_compression_matrix else: raise ValueError("Invalid comp_mat_type.") N = self._natom fc2 = fc2_basis.blocked_basis_set.dot(self._coefs[0]) fc2 = np.array( (comp_mat_fc2 @ fc2).reshape((-1, N, 3, 3)), dtype="double", order="C" ) fc3 = fc3_basis.blocked_basis_set.dot(self._coefs[1]) fc3 = np.array( (comp_mat_fc3 @ fc3).reshape((-1, N, N, 3, 3, 3)), dtype="double", order="C", ) return fc2, fc3 def set_disps_N3N3(disps, sparse=False): """Calculate Kronecker products of displacements. Parameter --------- disps: shape=(n_supercell, N3) Return ------ disps_2nd: shape=(n_supercell, N3N3) """ n_supercell = disps.shape[0] disps_2nd = (disps[:, :, None] * disps[:, None, :]).reshape((n_supercell, -1)) if sparse: return csr_array(disps_2nd) return disps_2nd def reshape_nNN333_nx_to_N3N3_n3nx(mat, N, n, n_batch=9): """Reorder and reshape a sparse matrix (nNN333,nx)->(N3N3,n3nx). Return reordered csr_matrix used for FC3. """ _, nx = mat.shape NN33 = N**2 * 9 n3nx = n * 3 * nx mat = mat.tocoo(copy=False) batch_size = len(mat.row) if len(mat.row) < n_batch else len(mat.row) // n_batch begin_batch, end_batch = get_batch_slice(len(mat.row), batch_size) for begin, end in zip(begin_batch, end_batch): div, rem = np.divmod(mat.row[begin:end], 27 * N * N) mat.col[begin:end] += div * 3 * nx div, rem = np.divmod(rem, 27 * N) mat.row[begin:end] = div * 9 * N div, rem = np.divmod(rem, 27) mat.row[begin:end] += div * 3 div, rem = np.divmod(rem, 9) mat.col[begin:end] += div * nx div, rem = np.divmod(rem, 3) mat.row[begin:end] += div * 3 * N + rem mat.resize((NN33, n3nx)) mat = mat.tocsr(copy=False) return mat def prepare_normal_equation_O2O3( disps: np.ndarray, forces: np.ndarray, compact_compress_mat_fc2: csr_array, compact_compress_mat_fc3: csr_array, compress_eigvecs_fc2: BlockMatrixNode, compress_eigvecs_fc3: BlockMatrixNode, atomic_decompr_idx_fc2: np.ndarray, atomic_decompr_idx_fc3: np.ndarray, batch_size: int = 100, use_mkl: bool = False, verbose: bool = False, ): r"""Calculate X.T @ X and X.T @ y. X = displacements @ compress_mat @ compress_eigvecs X = np.hstack([X_fc2, X_fc3]) displacements (fc2): (n_samples, N3) displacements (fc3): (n_samples, NN33) compact_compress_mat_fc2: (n_aN33, n_compr) compact_compress_mat_fc3: (n_aNN333, n_compr_fc3) compress_eigvecs_fc2: (n_compr_fc2, n_basis_fc2) compress_eigvecs_fc3: (n_compr_fc3, n_basis_fc3) Matrix reshapings are appropriately applied to compress_mat and its products. X.T @ X and X.T @ y are sequentially calculated using divided dataset. X.T @ X = \sum_i X_i.T @ X_i X.T @ y = \sum_i X_i.T @ y_i (i: batch index) """ N3 = disps.shape[1] N = N3 // 3 NN = N * N n_compr_fc2 = compact_compress_mat_fc2.shape[1] # type: ignore n_compr_fc3 = compact_compress_mat_fc3.shape[1] # type: ignore n_batch = (N // 256 + 1) * (n_compr_fc3 // 30000 + 1) n_batch = min(N, n_batch) begin_batch_atom, end_batch_atom = get_batch_slice(N, N // n_batch) begin_batch, end_batch = get_batch_slice(disps.shape[0], batch_size) mat22 = np.zeros((n_compr_fc2, n_compr_fc2), dtype=float) mat23 = np.zeros((n_compr_fc2, n_compr_fc3), dtype=float) mat33 = np.zeros((n_compr_fc3, n_compr_fc3), dtype=float) mat2y = np.zeros(n_compr_fc2, dtype=float) mat3y = np.zeros(n_compr_fc3, dtype=float) t_all1 = time.time() const_fc2 = -1.0 const_fc3 = -0.5 compact_compress_mat_fc2 *= const_fc2 compact_compress_mat_fc3 *= const_fc3 for begin_i, end_i in zip(begin_batch_atom, end_batch_atom): if verbose: print("-----", flush=True) print("Solver_atoms:", begin_i + 1, "--", end_i, "/", N, flush=True) n_atom_batch = end_i - begin_i t1 = time.time() decompr_idx = ( atomic_decompr_idx_fc2[begin_i * N : end_i * N, None] * 9 + np.arange(9)[None, :] ).reshape(-1) compr_mat_fc2 = reshape_nN33_nx_to_N3_n3nx( compact_compress_mat_fc2[decompr_idx], N, n_atom_batch, ) decompr_idx = ( atomic_decompr_idx_fc3[begin_i * NN : end_i * NN, None] * 27 + np.arange(27)[None, :] ).reshape(-1) compr_mat_fc3 = reshape_nNN333_nx_to_N3N3_n3nx( compact_compress_mat_fc3[decompr_idx], N, n_atom_batch, ) t2 = time.time() if verbose: print( "Time (Solver_compr_matrix_reshape):", "{:.3f}".format(t2 - t1), flush=True, ) for begin, end in zip(begin_batch, end_batch): t1 = time.time() X2 = dot_product_sparse( disps[begin:end], compr_mat_fc2, use_mkl=use_mkl, dense=True, ).reshape((-1, n_compr_fc2)) X3 = dot_product_sparse( set_disps_N3N3(disps[begin:end], sparse=False), compr_mat_fc3, use_mkl=use_mkl, dense=True, ).reshape((-1, n_compr_fc3)) y = forces[begin:end, begin_i * 3 : end_i * 3].reshape(-1) mat22 += X2.T @ X2 mat23 += X2.T @ X3 mat33 += X3.T @ X3 mat2y += X2.T @ y mat3y += X3.T @ y t2 = time.time() if verbose: print("Solver_block:", end, "/", disps.shape[0], flush=True) print(" - Time:", "{:.3f}".format(t2 - t1), flush=True) del X3 if verbose: print("Solver:", "Calculate X.T @ X and X.T @ y", flush=True) mat22 = block_matrix_sandwich(compress_eigvecs_fc2, compress_eigvecs_fc2, mat22) mat23 = block_matrix_sandwich(compress_eigvecs_fc2, compress_eigvecs_fc3, mat23) mat33 = block_matrix_sandwich(compress_eigvecs_fc3, compress_eigvecs_fc3, mat33) mat2y = compress_eigvecs_fc2.transpose_dot(mat2y) mat3y = compress_eigvecs_fc3.transpose_dot(mat3y) XTX = np.block([[mat22, mat23], [mat23.T, mat33]]) XTy = np.hstack([mat2y, mat3y]) compact_compress_mat_fc2 /= const_fc2 compact_compress_mat_fc3 /= const_fc3 t_all2 = time.time() if verbose: print( "Time (disp @ compr @ eigvecs).T @ (disp @ compr @ eigvecs):", "{:.3f}".format(t_all2 - t_all1), flush=True, ) return XTX, XTy def run_solver_O2O3( disps: np.ndarray, forces: np.ndarray, compact_compress_mat_fc2: csr_array, compact_compress_mat_fc3: csr_array, compress_eigvecs_fc2: BlockMatrixNode, compress_eigvecs_fc3: BlockMatrixNode, atomic_decompr_idx_fc2: np.ndarray, atomic_decompr_idx_fc3: np.ndarray, batch_size: int = 100, use_mkl: bool = False, verbose: bool = False, ): """Estimate coeffs. in X @ coeffs = y. X_fc2 = displacements_fc2 @ compress_mat_fc2 @ compress_eigvecs_fc2 X_fc3 = displacements_fc3 @ compress_mat_fc3 @ compress_eigvecs_fc3 X = np.hstack([X_fc2, X_fc3]) Matrix reshapings are appropriately applied. X: features (n_samples * N3, N_basis_fc2 + N_basis_fc3) y: observations (forces), (n_samples * N3) """ XTX, XTy = prepare_normal_equation_O2O3( disps, forces, compact_compress_mat_fc2, compact_compress_mat_fc3, compress_eigvecs_fc2, compress_eigvecs_fc3, atomic_decompr_idx_fc2, atomic_decompr_idx_fc3, batch_size=batch_size, use_mkl=use_mkl, verbose=verbose, ) coefs = solve_linear_equation(XTX, XTy) n_basis_fc2 = compress_eigvecs_fc2.shape[1] coefs_fc2, coefs_fc3 = coefs[:n_basis_fc2], coefs[n_basis_fc2:] return coefs_fc2, coefs_fc3 symfc-1.6.0/src/symfc/solvers/solver_O2O3O4.py000066400000000000000000000406421511276756400211210ustar00rootroot00000000000000"""Solver of 2nd, 3rd and 4th order force constants simultaneously.""" from __future__ import annotations import time from collections.abc import Sequence from typing import Literal, Optional, Union, cast import numpy as np from scipy.sparse import csr_array from symfc.basis_sets import FCBasisSetO2, FCBasisSetO3, FCBasisSetO4 from symfc.solvers.solver_O2 import reshape_nN33_nx_to_N3_n3nx from symfc.solvers.solver_O2O3 import reshape_nNN333_nx_to_N3N3_n3nx, set_disps_N3N3 from symfc.utils.matrix import block_matrix_sandwich from symfc.utils.solver_funcs import get_batch_slice, solve_linear_equation try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass from .solver_base import FCSolverBase class FCSolverO2O3O4(FCSolverBase): """Simultaneous second, third and fourth order force constants solver.""" def __init__( self, basis_set: Sequence[Union[FCBasisSetO2, FCBasisSetO3, FCBasisSetO4]], use_mkl: bool = False, log_level: int = 0, ): """Init method. Parameters ---------- basis_set : Sequence of (FCBasisSetO2, FCBasisSetO3, FCBasisSetO4) First element must be FCBasisSetO2, second must be FCBasisSetO3, and third must be FCBasisSetO4. use_mkl : bool, optional Use MKL if True. Default is False. log_level : int, optional Logging level. Default is 0. """ if len(basis_set) != 3: raise ValueError("basis_set must contain exactly 3 elements") if not isinstance(basis_set[0], FCBasisSetO2): raise TypeError("First element must be FCBasisSetO2") if not isinstance(basis_set[1], FCBasisSetO3): raise TypeError("Second element must be FCBasisSetO3") if not isinstance(basis_set[2], FCBasisSetO4): raise TypeError("Third element must be FCBasisSetO4") self._basis_set: Sequence[Union[FCBasisSetO2, FCBasisSetO3, FCBasisSetO4]] super().__init__(basis_set, use_mkl=use_mkl, log_level=log_level) def solve( self, displacements: np.ndarray, forces: np.ndarray, batch_size: int = 36, ) -> FCSolverO2O3O4: """Solve force constants. Note ---- self._coefs = (coefs_fc2, coefs_fc3, coefs_fc4) Parameters ---------- displacements : ndarray Displacements of atoms in Cartesian coordinates. shape=(n_snapshot, N, 3), dtype='double' forces : ndarray Forces of atoms in Cartesian coordinates. shape=(n_snapshot, N, 3), dtype='double' Returns ------- ndarray Force constants. shape=(n_a, N, N, N, 3, 3, 3, 3). See `is_compact_fc` parameter. dtype='double', order='C' """ n_data = forces.shape[0] f = forces.reshape(n_data, -1) d = displacements.reshape(n_data, -1) fc2_basis: FCBasisSetO2 = cast(FCBasisSetO2, self._basis_set[0]) fc3_basis: FCBasisSetO3 = cast(FCBasisSetO3, self._basis_set[1]) fc4_basis: FCBasisSetO4 = cast(FCBasisSetO4, self._basis_set[2]) compress_mat_fc2 = fc2_basis.compact_compression_matrix basis_set_fc2 = fc2_basis.blocked_basis_set compress_mat_fc3 = fc3_basis.compact_compression_matrix basis_set_fc3 = fc3_basis.blocked_basis_set compress_mat_fc4 = fc4_basis.compact_compression_matrix basis_set_fc4 = fc4_basis.blocked_basis_set atomic_decompr_idx_fc2 = fc2_basis.atomic_decompr_idx atomic_decompr_idx_fc3 = fc3_basis.atomic_decompr_idx atomic_decompr_idx_fc4 = fc4_basis.atomic_decompr_idx self._coefs = run_solver_O2O3O4( d, f, compress_mat_fc2, compress_mat_fc3, compress_mat_fc4, basis_set_fc2, basis_set_fc3, basis_set_fc4, atomic_decompr_idx_fc2, atomic_decompr_idx_fc3, atomic_decompr_idx_fc4, batch_size=batch_size, use_mkl=self._use_mkl, verbose=self._log_level > 0, ) return self @property def full_fc(self) -> Optional[tuple[np.ndarray, np.ndarray, np.ndarray]]: """Return full force constants. Returns ------- tuple[np.ndarray, np.ndarray, np.ndarray] shape=(N, N, 3, 3), dtype='double', order='C' shape=(N, N, N, 3, 3, 3), dtype='double', order='C' shape=(N, N, N, N, 3, 3, 3, 3), dtype='double', order='C' """ return self._recover_fcs("full") @property def compact_fc(self) -> Optional[tuple[np.ndarray, np.ndarray, np.ndarray]]: """Return full force constants. Returns ------- tuple[np.ndarray, np.ndarray, np.ndarray] shape=(n_a, N, 3, 3), dtype='double', order='C' shape=(n_a, N, N, 3, 3, 3), dtype='double', order='C' shape=(n_a, N, N, N, 3, 3, 3, 3), dtype='double', order='C' """ return self._recover_fcs("compact") def _recover_fcs( self, comp_mat_type: Literal["full", "compact"] ) -> Optional[tuple[np.ndarray, np.ndarray, np.ndarray]]: if self._coefs is None: return None fc2_basis: FCBasisSetO2 = cast(FCBasisSetO2, self._basis_set[0]) fc3_basis: FCBasisSetO3 = cast(FCBasisSetO3, self._basis_set[1]) fc4_basis: FCBasisSetO4 = cast(FCBasisSetO4, self._basis_set[2]) if comp_mat_type == "full": comp_mat_fc2 = fc2_basis.compression_matrix comp_mat_fc3 = fc3_basis.compression_matrix comp_mat_fc4 = fc4_basis.compression_matrix elif comp_mat_type == "compact": comp_mat_fc2 = fc2_basis.compact_compression_matrix comp_mat_fc3 = fc3_basis.compact_compression_matrix comp_mat_fc4 = fc4_basis.compact_compression_matrix else: raise ValueError("Invalid comp_mat_type.") N = self._natom fc2 = fc2_basis.blocked_basis_set.dot(self._coefs[0]) fc2 = np.array( (comp_mat_fc2 @ fc2).reshape((-1, N, 3, 3)), dtype="double", order="C" ) fc3 = fc3_basis.blocked_basis_set.dot(self._coefs[1]) fc3 = np.array( (comp_mat_fc3 @ fc3).reshape((-1, N, N, 3, 3, 3)), dtype="double", order="C", ) fc4 = fc4_basis.blocked_basis_set.dot(self._coefs[2]) fc4 = np.array( (comp_mat_fc4 @ fc4).reshape((-1, N, N, N, 3, 3, 3, 3)), dtype="double", order="C", ) return fc2, fc3, fc4 def set_disps_N3N3N3(disps, sparse=True, disps_N3N3=None): """Calculate Kronecker products of displacements. Parameter --------- disps: shape=(n_supercell, N3) Return ------ disps_3rd: shape=(n_supercell, N3N3N3) """ n_supercell = disps.shape[0] if disps_N3N3 is not None: disps_3rd = (disps_N3N3[:, :, None] * disps[:, None, :]).reshape( (n_supercell, -1) ) else: disps_3rd = ( disps[:, :, None, None] * disps[:, None, :, None] * disps[:, None, None, :] ).reshape((n_supercell, -1)) if sparse: return csr_array(disps_3rd) return disps_3rd def reshape_nNNN3333_nx_to_N3N3N3_n3nx(mat, N, n, n_batch=36): """Reorder and reshape a sparse matrix (nNNN3333,nx)->(N3N3N3,n3nx). Return reordered csr_matrix used for FC4. """ _, nx = mat.shape NNN333 = N**3 * 27 n3nx = n * 3 * nx mat = mat.tocoo(copy=False) begin_batch, end_batch = get_batch_slice(len(mat.row), len(mat.row) // n_batch) for begin, end in zip(begin_batch, end_batch): div, rem = np.divmod(mat.row[begin:end], 81 * N * N * N) mat.col[begin:end] += div * 3 * nx div, rem = np.divmod(rem, 81 * N * N) mat.row[begin:end] = div * 27 * N * N div, rem = np.divmod(rem, 81 * N) mat.row[begin:end] += div * 9 * N div, rem = np.divmod(rem, 81) mat.row[begin:end] += div * 3 div, rem = np.divmod(rem, 27) mat.col[begin:end] += div * nx div, rem = np.divmod(rem, 9) mat.row[begin:end] += div * 9 * N * N div, rem = np.divmod(rem, 3) mat.row[begin:end] += div * 3 * N + rem mat.resize((NNN333, n3nx)) mat = mat.tocsr(copy=False) return mat def prepare_normal_equation_O2O3O4( disps, forces, compact_compress_mat_fc2, compact_compress_mat_fc3, compact_compress_mat_fc4, compress_eigvecs_fc2, compress_eigvecs_fc3, compress_eigvecs_fc4, atomic_decompr_idx_fc2, atomic_decompr_idx_fc3, atomic_decompr_idx_fc4, batch_size=36, use_mkl=False, verbose=False, ): r"""Calculate X.T @ X and X.T @ y. X = displacements @ compress_mat @ compress_eigvecs X = np.hstack([X_fc2, X_fc3, X_fc4]) displacements (fc2): (n_samples, N3) displacements (fc3): (n_samples, NN33) displacements (fc4): (n_samples, NNN333) compact_compress_mat_fc2: (n_aN33, n_compr) compact_compress_mat_fc3: (n_aNN333, n_compr_fc3) compact_compress_mat_fc4: (n_aNNN3333, n_compr_fc4) compress_eigvecs_fc2: (n_compr_fc2, n_basis_fc2) compress_eigvecs_fc3: (n_compr_fc3, n_basis_fc3) compress_eigvecs_fc4: (n_compr_fc4, n_basis_fc4) Matrix reshapings are appropriately applied to compress_mat and its products. X.T @ X and X.T @ y are sequentially calculated using divided dataset. X.T @ X = \sum_i X_i.T @ X_i X.T @ y = \sum_i X_i.T @ y_i (i: batch index) """ N3 = disps.shape[1] N = N3 // 3 NN = N**2 NNN = N**3 # n_basis_fc2 = compress_eigvecs_fc2.shape[1] # n_basis_fc3 = compress_eigvecs_fc3.shape[1] # n_basis_fc4 = compress_eigvecs_fc4.shape[1] n_compr_fc2 = compact_compress_mat_fc2.shape[1] n_compr_fc3 = compact_compress_mat_fc3.shape[1] n_compr_fc4 = compact_compress_mat_fc4.shape[1] n_batch = (n_compr_fc3 // 10000 + n_compr_fc4 // 5000 + 1) * (N // 50 + 1) n_batch = min(N, n_batch) begin_batch_atom, end_batch_atom = get_batch_slice(N, N // n_batch) begin_batch, end_batch = get_batch_slice(disps.shape[0], batch_size) mat22 = np.zeros((n_compr_fc2, n_compr_fc2), dtype=float) mat23 = np.zeros((n_compr_fc2, n_compr_fc3), dtype=float) mat24 = np.zeros((n_compr_fc2, n_compr_fc4), dtype=float) mat33 = np.zeros((n_compr_fc3, n_compr_fc3), dtype=float) mat34 = np.zeros((n_compr_fc3, n_compr_fc4), dtype=float) mat44 = np.zeros((n_compr_fc4, n_compr_fc4), dtype=float) mat2y = np.zeros(n_compr_fc2, dtype=float) mat3y = np.zeros(n_compr_fc3, dtype=float) mat4y = np.zeros(n_compr_fc4, dtype=float) t_all1 = time.time() const_fc2 = -1.0 const_fc3 = -0.5 const_fc4 = -1.0 / 6.0 compact_compress_mat_fc2 *= const_fc2 compact_compress_mat_fc3 *= const_fc3 compact_compress_mat_fc4 *= const_fc4 for begin_i, end_i in zip(begin_batch_atom, end_batch_atom): if verbose: print("-----", flush=True) print("Solver_atoms:", begin_i + 1, "--", end_i, "/", N, flush=True) n_atom_batch = end_i - begin_i t1 = time.time() decompr_idx = ( atomic_decompr_idx_fc2[begin_i * N : end_i * N, None] * 9 + np.arange(9)[None, :] ).reshape(-1) compr_mat_fc2 = reshape_nN33_nx_to_N3_n3nx( compact_compress_mat_fc2[decompr_idx], N, n_atom_batch, ) decompr_idx = ( atomic_decompr_idx_fc3[begin_i * NN : end_i * NN, None] * 27 + np.arange(27)[None, :] ).reshape(-1) compr_mat_fc3 = reshape_nNN333_nx_to_N3N3_n3nx( compact_compress_mat_fc3[decompr_idx], N, n_atom_batch, ) decompr_idx = ( atomic_decompr_idx_fc4[begin_i * NNN : end_i * NNN, None] * 81 + np.arange(81)[None, :] ).reshape(-1) compr_mat_fc4 = reshape_nNNN3333_nx_to_N3N3N3_n3nx( compact_compress_mat_fc4[decompr_idx], N, n_atom_batch, ) t2 = time.time() if verbose: print( "Time (Solver_compr_matrix_reshape):", "{:.3f}".format(t2 - t1), flush=True, ) for begin, end in zip(begin_batch, end_batch): t1 = time.time() X2 = dot_product_sparse( disps[begin:end], compr_mat_fc2, use_mkl=use_mkl, dense=True, ).reshape((-1, n_compr_fc2)) disps_N3N3 = set_disps_N3N3(disps[begin:end], sparse=False) X3 = dot_product_sparse( disps_N3N3, compr_mat_fc3, use_mkl=use_mkl, dense=True, ).reshape((-1, n_compr_fc3)) X4 = dot_product_sparse( set_disps_N3N3N3(disps[begin:end], sparse=False, disps_N3N3=disps_N3N3), compr_mat_fc4, use_mkl=use_mkl, dense=True, ).reshape((-1, n_compr_fc4)) y = forces[begin:end, begin_i * 3 : end_i * 3].reshape(-1) mat22 += X2.T @ X2 mat23 += X2.T @ X3 mat24 += X2.T @ X4 mat33 += X3.T @ X3 mat34 += X3.T @ X4 mat44 += X4.T @ X4 mat2y += X2.T @ y mat3y += X3.T @ y mat4y += X4.T @ y t2 = time.time() if verbose: print("Solver_block:", end, "/", disps.shape[0], flush=True) print(" - Time:", "{:.3f}".format(t2 - t1), flush=True) if verbose: print("Solver:", "Calculate X.T @ X and X.T @ y", flush=True) mat22 = block_matrix_sandwich(compress_eigvecs_fc2, compress_eigvecs_fc2, mat22) mat23 = block_matrix_sandwich(compress_eigvecs_fc2, compress_eigvecs_fc3, mat23) mat24 = block_matrix_sandwich(compress_eigvecs_fc2, compress_eigvecs_fc4, mat24) mat33 = block_matrix_sandwich(compress_eigvecs_fc3, compress_eigvecs_fc3, mat33) mat34 = block_matrix_sandwich(compress_eigvecs_fc3, compress_eigvecs_fc4, mat34) mat44 = block_matrix_sandwich(compress_eigvecs_fc4, compress_eigvecs_fc4, mat44) mat2y = compress_eigvecs_fc2.transpose_dot(mat2y) mat3y = compress_eigvecs_fc3.transpose_dot(mat3y) mat4y = compress_eigvecs_fc4.transpose_dot(mat4y) XTX = np.block( [[mat22, mat23, mat24], [mat23.T, mat33, mat34], [mat24.T, mat34.T, mat44]] ) XTy = np.hstack([mat2y, mat3y, mat4y]) compact_compress_mat_fc2 /= const_fc2 compact_compress_mat_fc3 /= const_fc3 compact_compress_mat_fc4 /= const_fc4 t_all2 = time.time() if verbose: print( "Time (disp @ compr @ eigvecs).T @ (disp @ compr @ eigvecs):", "{:.3f}".format(t_all2 - t_all1), flush=True, ) return XTX, XTy def run_solver_O2O3O4( disps, forces, compact_compress_mat_fc2, compact_compress_mat_fc3, compact_compress_mat_fc4, compress_eigvecs_fc2, compress_eigvecs_fc3, compress_eigvecs_fc4, atomic_decompr_idx_fc2, atomic_decompr_idx_fc3, atomic_decompr_idx_fc4, batch_size=36, use_mkl=False, verbose=False, ): """Estimate coeffs. in X @ coeffs = y. X_fc2 = displacements_fc2 @ compress_mat_fc2 @ compress_eigvecs_fc2 X_fc3 = displacements_fc3 @ compress_mat_fc3 @ compress_eigvecs_fc3 X_fc4 = displacements_fc4 @ compress_mat_fc4 @ compress_eigvecs_fc4 X = np.hstack([X_fc2, X_fc3, X_fc4]) Matrix reshapings are appropriately applied. X: features (n_samples * N3, N_basis_fc2 + N_basis_fc3 + N_basis_fc4) y: observations (forces), (n_samples * N3) """ XTX, XTy = prepare_normal_equation_O2O3O4( disps, forces, compact_compress_mat_fc2, compact_compress_mat_fc3, compact_compress_mat_fc4, compress_eigvecs_fc2, compress_eigvecs_fc3, compress_eigvecs_fc4, atomic_decompr_idx_fc2, atomic_decompr_idx_fc3, atomic_decompr_idx_fc4, batch_size=batch_size, use_mkl=use_mkl, verbose=verbose, ) coefs = solve_linear_equation(XTX, XTy) n_basis_fc2 = compress_eigvecs_fc2.shape[1] n_basis_fc3 = compress_eigvecs_fc3.shape[1] coefs_fc2, coefs_fc3, coefs_fc4 = ( coefs[:n_basis_fc2], coefs[n_basis_fc2 : n_basis_fc2 + n_basis_fc3], coefs[n_basis_fc2 + n_basis_fc3 :], ) return coefs_fc2, coefs_fc3, coefs_fc4 symfc-1.6.0/src/symfc/solvers/solver_O3.py000066400000000000000000000163761511276756400205240ustar00rootroot00000000000000"""Solver of 3rd order force constants.""" from __future__ import annotations import time from typing import Literal, Optional import numpy as np from symfc.basis_sets import FCBasisSetO3 from symfc.solvers.solver_O2O3 import reshape_nNN333_nx_to_N3N3_n3nx, set_disps_N3N3 from symfc.utils.matrix import block_matrix_sandwich from symfc.utils.solver_funcs import get_batch_slice, solve_linear_equation try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass from .solver_base import FCSolverBase class FCSolverO3(FCSolverBase): """Third order force constants solver.""" def __init__( self, basis_set: FCBasisSetO3, use_mkl: bool = False, log_level: int = 0, ): """Init method.""" self._basis_set: FCBasisSetO3 super().__init__(basis_set, use_mkl=use_mkl, log_level=log_level) def solve( self, displacements: np.ndarray, forces: np.ndarray, batch_size: int = 100, ) -> FCSolverO3: """Solve force constants. Note ---- self._coefs = (coefs_fc3) Parameters ---------- displacements : ndarray Displacements of atoms in Cartesian coordinates. shape=(n_snapshot, N, 3), dtype='double' forces : ndarray Forces of atoms in Cartesian coordinates. shape=(n_snapshot, N, 3), dtype='double' Returns ------- self : FCSolverO3 """ n_data = forces.shape[0] f = forces.reshape(n_data, -1) d = displacements.reshape(n_data, -1) fc3_basis = self._basis_set compress_mat_fc3 = fc3_basis.compact_compression_matrix basis_set_fc3 = fc3_basis.blocked_basis_set atomic_decompr_idx_fc3 = fc3_basis.atomic_decompr_idx self._coefs = run_solver_O3( d, f, compress_mat_fc3, basis_set_fc3, atomic_decompr_idx_fc3, batch_size=batch_size, use_mkl=self._use_mkl, verbose=self._log_level > 0, ) return self @property def full_fc(self) -> Optional[np.ndarray]: """Return full force constants. Returns ------- np.ndarray shape=(N, N, N, 3, 3, 3), dtype='double', order='C' """ return self._recover_fcs("full") @property def compact_fc(self) -> Optional[np.ndarray]: """Return full force constants. Returns ------- np.ndarray shape=(n_a, N, N, 3, 3, 3), dtype='double', order='C' """ return self._recover_fcs("compact") def _recover_fcs( self, comp_mat_type: Literal["full", "compact"] ) -> Optional[np.ndarray]: fc3_basis = self._basis_set if self._coefs is None or fc3_basis.basis_set is None: return None if comp_mat_type == "full": comp_mat_fc3 = fc3_basis.compression_matrix elif comp_mat_type == "compact": comp_mat_fc3 = fc3_basis.compact_compression_matrix else: raise ValueError("Invalid comp_mat_type.") N = self._natom fc3 = fc3_basis.blocked_basis_set.dot(self._coefs) fc3 = np.array( (comp_mat_fc3 @ fc3).reshape((-1, N, N, 3, 3, 3)), dtype="double", order="C", ) return fc3 def prepare_normal_equation_O3( disps, forces, compact_compress_mat_fc3, compress_eigvecs_fc3, atomic_decompr_idx_fc3, batch_size=100, use_mkl=False, verbose=False, ): r"""Calculate X.T @ X and X.T @ y. X = (displacements x displacements) @ compress_mat @ compress_eigvecs displacements (fc3): (n_samples, NN33) compact_compress_mat_fc3: (n_aNN333, n_compr_fc3) compress_eigvecs_fc3: (n_compr_fc3, n_basis_fc3) Matrix reshapings are appropriately applied to compress_mat and its products. X.T @ X and X.T @ y are sequentially calculated using divided dataset. X.T @ X = \sum_i X_i.T @ X_i X.T @ y = \sum_i X_i.T @ y_i (i: batch index) """ N3 = disps.shape[1] N = N3 // 3 NN = N * N n_compr_fc3 = compact_compress_mat_fc3.shape[1] n_batch = (N // 256 + 1) * (n_compr_fc3 // 30000 + 1) n_batch = min(N, n_batch) begin_batch_atom, end_batch_atom = get_batch_slice(N, N // n_batch) begin_batch, end_batch = get_batch_slice(disps.shape[0], batch_size) mat33 = np.zeros((n_compr_fc3, n_compr_fc3), dtype=float) mat3y = np.zeros(n_compr_fc3, dtype=float) t_all1 = time.time() const_fc3 = -0.5 compact_compress_mat_fc3 *= const_fc3 for begin_i, end_i in zip(begin_batch_atom, end_batch_atom): if verbose: print("-----", flush=True) print("Solver_atoms:", begin_i + 1, "--", end_i, "/", N, flush=True) n_atom_batch = end_i - begin_i t1 = time.time() decompr_idx = ( atomic_decompr_idx_fc3[begin_i * NN : end_i * NN, None] * 27 + np.arange(27)[None, :] ).reshape(-1) compr_mat_fc3 = reshape_nNN333_nx_to_N3N3_n3nx( compact_compress_mat_fc3[decompr_idx], N, n_atom_batch, ) t2 = time.time() if verbose: print( "Time (Solver_compr_matrix_reshape):", "{:.3f}".format(t2 - t1), flush=True, ) for begin, end in zip(begin_batch, end_batch): t1 = time.time() X3 = dot_product_sparse( set_disps_N3N3(disps[begin:end], sparse=False), compr_mat_fc3, use_mkl=use_mkl, dense=True, ).reshape((-1, n_compr_fc3)) y = forces[begin:end, begin_i * 3 : end_i * 3].reshape(-1) mat33 += X3.T @ X3 mat3y += X3.T @ y t2 = time.time() if verbose: print("Solver_block:", end, "/", disps.shape[0], flush=True) print(" - Time:", "{:.3f}".format(t2 - t1), flush=True) if verbose: print("Solver:", "Calculate X.T @ X and X.T @ y", flush=True) XTX = block_matrix_sandwich(compress_eigvecs_fc3, compress_eigvecs_fc3, mat33) XTy = compress_eigvecs_fc3.transpose_dot(mat3y) compact_compress_mat_fc3 /= const_fc3 t_all2 = time.time() if verbose: print( "Time (disp @ compr @ eigvecs).T @ (disp @ compr @ eigvecs):", "{:.3f}".format(t_all2 - t_all1), flush=True, ) return XTX, XTy def run_solver_O3( disps, forces, compact_compress_mat_fc3, compress_eigvecs_fc3, atomic_decompr_idx_fc3, batch_size=100, use_mkl=False, verbose=False, ): """Estimate coeffs. in X @ coeffs = y. X = displacements_fc3 @ compress_mat_fc3 @ compress_eigvecs_fc3 Matrix reshapings are appropriately applied. X: features (n_samples * N3, N_basis_fc3) y: observations (forces), (n_samples * N3) """ XTX, XTy = prepare_normal_equation_O3( disps, forces, compact_compress_mat_fc3, compress_eigvecs_fc3, atomic_decompr_idx_fc3, batch_size=batch_size, use_mkl=use_mkl, verbose=verbose, ) coefs = solve_linear_equation(XTX, XTy) return coefs symfc-1.6.0/src/symfc/solvers/solver_O3O4.py000066400000000000000000000264331511276756400207220ustar00rootroot00000000000000"""Solver of 3rd and 4th order force constants simultaneously.""" from __future__ import annotations import time from collections.abc import Sequence from typing import Literal, Optional, Union, cast import numpy as np from symfc.basis_sets import FCBasisSetO3, FCBasisSetO4 from symfc.solvers.solver_O2O3 import reshape_nNN333_nx_to_N3N3_n3nx, set_disps_N3N3 from symfc.solvers.solver_O2O3O4 import ( reshape_nNNN3333_nx_to_N3N3N3_n3nx, set_disps_N3N3N3, ) from symfc.utils.matrix import block_matrix_sandwich from symfc.utils.solver_funcs import get_batch_slice, solve_linear_equation try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass from .solver_base import FCSolverBase class FCSolverO3O4(FCSolverBase): """Simultaneous third and fourth order force constants solver.""" def __init__( self, basis_set: Sequence[Union[FCBasisSetO3, FCBasisSetO4]], use_mkl: bool = False, log_level: int = 0, ): """Init method. Parameters ---------- basis_set : Sequence of (FCBasisSetO3, FCBasisSetO4) First element must be FCBasisSetO3 and second must be FCBasisSetO4. use_mkl : bool, optional Use MKL if True. Default is False. log_level : int, optional Logging level. Default is 0. """ if len(basis_set) != 2: raise ValueError("basis_set must contain exactly 2 elements") if not isinstance(basis_set[0], FCBasisSetO3): raise TypeError("First element must be FCBasisSetO3") if not isinstance(basis_set[1], FCBasisSetO4): raise TypeError("Second element must be FCBasisSetO4") self._basis_set: Sequence[Union[FCBasisSetO3, FCBasisSetO4]] super().__init__(basis_set, use_mkl=use_mkl, log_level=log_level) def solve( self, displacements: np.ndarray, forces: np.ndarray, batch_size: int = 36, ) -> FCSolverO3O4: """Solve force constants. Note ---- self._coefs = (coefs_fc3, coefs_fc4) Parameters ---------- displacements : ndarray Displacements of atoms in Cartesian coordinates. shape=(n_snapshot, N, 3), dtype='double' forces : ndarray Forces of atoms in Cartesian coordinates. shape=(n_snapshot, N, 3), dtype='double' Returns ------- self : FCSolverO3O4 """ n_data = forces.shape[0] f = forces.reshape(n_data, -1) d = displacements.reshape(n_data, -1) fc3_basis: FCBasisSetO3 = cast(FCBasisSetO3, self._basis_set[0]) fc4_basis: FCBasisSetO4 = cast(FCBasisSetO4, self._basis_set[1]) compress_mat_fc3 = fc3_basis.compact_compression_matrix basis_set_fc3 = fc3_basis.blocked_basis_set compress_mat_fc4 = fc4_basis.compact_compression_matrix basis_set_fc4 = fc4_basis.blocked_basis_set atomic_decompr_idx_fc3 = fc3_basis.atomic_decompr_idx atomic_decompr_idx_fc4 = fc4_basis.atomic_decompr_idx self._coefs = run_solver_O3O4( d, f, compress_mat_fc3, compress_mat_fc4, basis_set_fc3, basis_set_fc4, atomic_decompr_idx_fc3, atomic_decompr_idx_fc4, batch_size=batch_size, use_mkl=self._use_mkl, verbose=self._log_level > 0, ) return self @property def full_fc(self) -> Optional[tuple[np.ndarray, np.ndarray]]: """Return full force constants. Returns ------- tuple[np.ndarray, np.ndarray] shape=(N, N, N, 3, 3, 3), dtype='double', order='C' shape=(N, N, N, N, 3, 3, 3, 3), dtype='double', order='C' """ return self._recover_fcs("full") @property def compact_fc(self) -> Optional[tuple[np.ndarray, np.ndarray]]: """Return full force constants. Returns ------- tuple[np.ndarray, np.ndarray] shape=(n_a, N, N, 3, 3, 3), dtype='double', order='C' shape=(n_a, N, N, N, 3, 3, 3, 3), dtype='double', order='C' """ return self._recover_fcs("compact") def _recover_fcs( self, comp_mat_type: Literal["full", "compact"] ) -> Optional[tuple[np.ndarray, np.ndarray]]: if self._coefs is None: return None fc3_basis: FCBasisSetO3 = cast(FCBasisSetO3, self._basis_set[0]) fc4_basis: FCBasisSetO4 = cast(FCBasisSetO4, self._basis_set[1]) if comp_mat_type == "full": comp_mat_fc3 = fc3_basis.compression_matrix comp_mat_fc4 = fc4_basis.compression_matrix elif comp_mat_type == "compact": comp_mat_fc3 = fc3_basis.compact_compression_matrix comp_mat_fc4 = fc4_basis.compact_compression_matrix else: raise ValueError("Invalid comp_mat_type.") N = self._natom fc3 = fc3_basis.blocked_basis_set.dot(self._coefs[0]) fc3 = np.array( (comp_mat_fc3 @ fc3).reshape((-1, N, N, 3, 3, 3)), dtype="double", order="C", ) fc4 = fc4_basis.blocked_basis_set.dot(self._coefs[1]) fc4 = np.array( (comp_mat_fc4 @ fc4).reshape((-1, N, N, N, 3, 3, 3, 3)), dtype="double", order="C", ) return fc3, fc4 def prepare_normal_equation_O3O4( disps, forces, compact_compress_mat_fc3, compact_compress_mat_fc4, compress_eigvecs_fc3, compress_eigvecs_fc4, atomic_decompr_idx_fc3, atomic_decompr_idx_fc4, batch_size=36, use_mkl=False, verbose=False, ): r"""Calculate X.T @ X and X.T @ y. X = displacements @ compress_mat @ compress_eigvecs X = np.hstack([X_fc3, X_fc4]) displacements (fc3): (n_samples, NN33) displacements (fc4): (n_samples, NNN333) compact_compress_mat_fc3: (n_aNN333, n_compr_fc3) compact_compress_mat_fc4: (n_aNNN3333, n_compr_fc4) compress_eigvecs_fc3: (n_compr_fc3, n_basis_fc3) compress_eigvecs_fc4: (n_compr_fc4, n_basis_fc4) Matrix reshapings are appropriately applied to compress_mat and its products. X.T @ X and X.T @ y are sequentially calculated using divided dataset. X.T @ X = \sum_i X_i.T @ X_i X.T @ y = \sum_i X_i.T @ y_i (i: batch index) """ N3 = disps.shape[1] N = N3 // 3 NN = N**2 NNN = N**3 n_compr_fc3 = compact_compress_mat_fc3.shape[1] n_compr_fc4 = compact_compress_mat_fc4.shape[1] n_batch = (n_compr_fc3 // 10000 + n_compr_fc4 // 5000 + 1) * (N // 50 + 1) n_batch = min(N, n_batch) begin_batch_atom, end_batch_atom = get_batch_slice(N, N // n_batch) begin_batch, end_batch = get_batch_slice(disps.shape[0], batch_size) mat33 = np.zeros((n_compr_fc3, n_compr_fc3), dtype=float) mat34 = np.zeros((n_compr_fc3, n_compr_fc4), dtype=float) mat44 = np.zeros((n_compr_fc4, n_compr_fc4), dtype=float) mat3y = np.zeros(n_compr_fc3, dtype=float) mat4y = np.zeros(n_compr_fc4, dtype=float) t_all1 = time.time() const_fc3 = -0.5 const_fc4 = -1.0 / 6.0 compact_compress_mat_fc3 *= const_fc3 compact_compress_mat_fc4 *= const_fc4 for begin_i, end_i in zip(begin_batch_atom, end_batch_atom): if verbose: print("-----", flush=True) print("Solver_atoms:", begin_i + 1, "--", end_i, "/", N, flush=True) n_atom_batch = end_i - begin_i t1 = time.time() decompr_idx = ( atomic_decompr_idx_fc3[begin_i * NN : end_i * NN, None] * 27 + np.arange(27)[None, :] ).reshape(-1) compr_mat_fc3 = reshape_nNN333_nx_to_N3N3_n3nx( compact_compress_mat_fc3[decompr_idx], N, n_atom_batch, ) decompr_idx = ( atomic_decompr_idx_fc4[begin_i * NNN : end_i * NNN, None] * 81 + np.arange(81)[None, :] ).reshape(-1) compr_mat_fc4 = reshape_nNNN3333_nx_to_N3N3N3_n3nx( compact_compress_mat_fc4[decompr_idx], N, n_atom_batch, ) t2 = time.time() if verbose: print( "Time (Solver_compr_matrix_reshape):", "{:.3f}".format(t2 - t1), flush=True, ) for begin, end in zip(begin_batch, end_batch): t1 = time.time() disps_N3N3 = set_disps_N3N3(disps[begin:end], sparse=False) X3 = dot_product_sparse( disps_N3N3, compr_mat_fc3, use_mkl=use_mkl, dense=True, ).reshape((-1, n_compr_fc3)) X4 = dot_product_sparse( set_disps_N3N3N3(disps[begin:end], sparse=False, disps_N3N3=disps_N3N3), compr_mat_fc4, use_mkl=use_mkl, dense=True, ).reshape((-1, n_compr_fc4)) y = forces[begin:end, begin_i * 3 : end_i * 3].reshape(-1) mat33 += X3.T @ X3 mat34 += X3.T @ X4 mat44 += X4.T @ X4 mat3y += X3.T @ y mat4y += X4.T @ y t2 = time.time() if verbose: print("Solver_block:", end, "/", disps.shape[0], flush=True) print(" - Time:", "{:.3f}".format(t2 - t1), flush=True) if verbose: print("Solver:", "Calculate X.T @ X and X.T @ y", flush=True) mat33 = block_matrix_sandwich(compress_eigvecs_fc3, compress_eigvecs_fc3, mat33) mat34 = block_matrix_sandwich(compress_eigvecs_fc3, compress_eigvecs_fc4, mat34) mat44 = block_matrix_sandwich(compress_eigvecs_fc4, compress_eigvecs_fc4, mat44) mat3y = compress_eigvecs_fc3.transpose_dot(mat3y) mat4y = compress_eigvecs_fc4.transpose_dot(mat4y) XTX = np.block([[mat33, mat34], [mat34.T, mat44]]) XTy = np.hstack([mat3y, mat4y]) compact_compress_mat_fc3 /= const_fc3 compact_compress_mat_fc4 /= const_fc4 t_all2 = time.time() if verbose: print( "Time (disp @ compr @ eigvecs).T @ (disp @ compr @ eigvecs):", "{:.3f}".format(t_all2 - t_all1), flush=True, ) return XTX, XTy def run_solver_O3O4( disps, forces, compact_compress_mat_fc3, compact_compress_mat_fc4, compress_eigvecs_fc3, compress_eigvecs_fc4, atomic_decompr_idx_fc3, atomic_decompr_idx_fc4, batch_size=36, use_mkl=False, verbose=False, ): """Estimate coeffs. in X @ coeffs = y. X_fc3 = displacements_fc3 @ compress_mat_fc3 @ compress_eigvecs_fc3 X_fc4 = displacements_fc4 @ compress_mat_fc4 @ compress_eigvecs_fc4 X = np.hstack([X_fc3, X_fc4]) Matrix reshapings are appropriately applied. X: features (n_samples * N3, N_basis_fc3 + N_basis_fc4) y: observations (forces), (n_samples * N3) """ XTX, XTy = prepare_normal_equation_O3O4( disps, forces, compact_compress_mat_fc3, compact_compress_mat_fc4, compress_eigvecs_fc3, compress_eigvecs_fc4, atomic_decompr_idx_fc3, atomic_decompr_idx_fc4, batch_size=batch_size, use_mkl=use_mkl, verbose=verbose, ) coefs = solve_linear_equation(XTX, XTy) n_basis_fc3 = compress_eigvecs_fc3.shape[1] coefs_fc3, coefs_fc4 = coefs[:n_basis_fc3], coefs[n_basis_fc3:] return coefs_fc3, coefs_fc4 symfc-1.6.0/src/symfc/solvers/solver_O4.py000066400000000000000000000164641511276756400205230ustar00rootroot00000000000000"""Solver of 4th order force constants.""" from __future__ import annotations import time from typing import Literal, Optional import numpy as np from symfc.basis_sets import FCBasisSetO4 from symfc.solvers.solver_O2O3O4 import ( reshape_nNNN3333_nx_to_N3N3N3_n3nx, set_disps_N3N3N3, ) from symfc.utils.matrix import block_matrix_sandwich from symfc.utils.solver_funcs import get_batch_slice, solve_linear_equation try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass from .solver_base import FCSolverBase class FCSolverO4(FCSolverBase): """Fourth order force constants solver.""" def __init__( self, basis_set: FCBasisSetO4, use_mkl: bool = False, log_level: int = 0, ): """Init method.""" self._basis_set: FCBasisSetO4 super().__init__(basis_set, use_mkl=use_mkl, log_level=log_level) def solve( self, displacements: np.ndarray, forces: np.ndarray, batch_size: int = 36, ) -> FCSolverO4: """Solve force constants. Note ---- self._coefs = coefs_fc4 Parameters ---------- displacements : ndarray Displacements of atoms in Cartesian coordinates. shape=(n_snapshot, N, 3), dtype='double' forces : ndarray Forces of atoms in Cartesian coordinates. shape=(n_snapshot, N, 3), dtype='double' Returns ------- self : FCSolverO4 """ n_data = forces.shape[0] f = forces.reshape(n_data, -1) d = displacements.reshape(n_data, -1) fc4_basis = self._basis_set compress_mat_fc4 = fc4_basis.compact_compression_matrix basis_set_fc4 = fc4_basis.blocked_basis_set atomic_decompr_idx_fc4 = fc4_basis.atomic_decompr_idx self._coefs = run_solver_O4( d, f, compress_mat_fc4, basis_set_fc4, atomic_decompr_idx_fc4, batch_size=batch_size, use_mkl=self._use_mkl, verbose=self._log_level > 0, ) return self @property def full_fc(self) -> Optional[np.ndarray]: """Return full force constants. Returns ------- np.ndarray shape=(N, N, N, N, 3, 3, 3, 3), dtype='double', order='C' """ return self._recover_fcs("full") @property def compact_fc(self) -> Optional[np.ndarray]: """Return full force constants. Returns ------- np.ndarray shape=(n_a, N, N, N, 3, 3, 3, 3), dtype='double', order='C' """ return self._recover_fcs("compact") def _recover_fcs( self, comp_mat_type: Literal["full", "compact"] ) -> Optional[np.ndarray]: fc4_basis: FCBasisSetO4 = self._basis_set if self._coefs is None or fc4_basis.basis_set is None: return None if comp_mat_type == "full": comp_mat_fc4 = fc4_basis.compression_matrix elif comp_mat_type == "compact": comp_mat_fc4 = fc4_basis.compact_compression_matrix else: raise ValueError("Invalid comp_mat_type.") N = self._natom fc4 = fc4_basis.blocked_basis_set.dot(self._coefs) fc4 = np.array( (comp_mat_fc4 @ fc4).reshape((-1, N, N, N, 3, 3, 3, 3)), dtype="double", order="C", ) return fc4 def prepare_normal_equation_O4( disps, forces, compact_compress_mat_fc4, compress_eigvecs_fc4, atomic_decompr_idx_fc4, batch_size=36, use_mkl=False, verbose=False, ): r"""Calculate X.T @ X and X.T @ y. X = displacements (fc4) @ compress_mat @ compress_eigvecs displacements (fc4): (n_samples, NNN333) compact_compress_mat_fc4: (n_aNNN3333, n_compr_fc4) compress_eigvecs_fc4: (n_compr_fc4, n_basis_fc4) Matrix reshapings are appropriately applied to compress_mat and its products. X.T @ X and X.T @ y are sequentially calculated using divided dataset. X.T @ X = \sum_i X_i.T @ X_i X.T @ y = \sum_i X_i.T @ y_i (i: batch index) """ N3 = disps.shape[1] N = N3 // 3 NNN = N**3 n_compr_fc4 = compact_compress_mat_fc4.shape[1] n_batch = (n_compr_fc4 // 5000 + 1) * (N // 50 + 1) n_batch = min(N, n_batch) begin_batch_atom, end_batch_atom = get_batch_slice(N, N // n_batch) begin_batch, end_batch = get_batch_slice(disps.shape[0], batch_size) mat44 = np.zeros((n_compr_fc4, n_compr_fc4), dtype=float) mat4y = np.zeros(n_compr_fc4, dtype=float) t_all1 = time.time() const_fc4 = -1.0 / 6.0 compact_compress_mat_fc4 *= const_fc4 for begin_i, end_i in zip(begin_batch_atom, end_batch_atom): if verbose: print("-----", flush=True) print("Solver_atoms:", begin_i + 1, "--", end_i, "/", N, flush=True) n_atom_batch = end_i - begin_i t1 = time.time() decompr_idx = ( atomic_decompr_idx_fc4[begin_i * NNN : end_i * NNN, None] * 81 + np.arange(81)[None, :] ).reshape(-1) compr_mat_fc4 = reshape_nNNN3333_nx_to_N3N3N3_n3nx( compact_compress_mat_fc4[decompr_idx], N, n_atom_batch, ) t2 = time.time() if verbose: print( "Time (Solver_compr_matrix_reshape):", "{:.3f}".format(t2 - t1), flush=True, ) for begin, end in zip(begin_batch, end_batch): t1 = time.time() X4 = dot_product_sparse( set_disps_N3N3N3(disps[begin:end], sparse=False), compr_mat_fc4, use_mkl=use_mkl, dense=True, ).reshape((-1, n_compr_fc4)) y = forces[begin:end, begin_i * 3 : end_i * 3].reshape(-1) mat44 += X4.T @ X4 mat4y += X4.T @ y t2 = time.time() if verbose: print("Solver_block:", end, "/", disps.shape[0], flush=True) print(" - Time:", "{:.3f}".format(t2 - t1), flush=True) if verbose: print("Solver:", "Calculate X.T @ X and X.T @ y", flush=True) XTX = block_matrix_sandwich(compress_eigvecs_fc4, compress_eigvecs_fc4, mat44) XTy = compress_eigvecs_fc4.transpose_dot(mat4y) compact_compress_mat_fc4 /= const_fc4 t_all2 = time.time() if verbose: print( "Time (disp @ compr @ eigvecs).T @ (disp @ compr @ eigvecs):", "{:.3f}".format(t_all2 - t_all1), flush=True, ) return XTX, XTy def run_solver_O4( disps, forces, compact_compress_mat_fc4, compress_eigvecs_fc4, atomic_decompr_idx_fc4, batch_size=36, use_mkl=False, verbose=False, ): """Estimate coeffs. in X @ coeffs = y. X = displacements_fc4 @ compress_mat_fc4 @ compress_eigvecs_fc4 Matrix reshapings are appropriately applied. X: features (n_samples * N3, N_basis_fc4) y: observations (forces), (n_samples * N3) """ XTX, XTy = prepare_normal_equation_O4( disps, forces, compact_compress_mat_fc4, compress_eigvecs_fc4, atomic_decompr_idx_fc4, batch_size=batch_size, use_mkl=use_mkl, verbose=verbose, ) coefs = solve_linear_equation(XTX, XTy) return coefs symfc-1.6.0/src/symfc/solvers/solver_base.py000066400000000000000000000030661511276756400211450ustar00rootroot00000000000000"""Base class of force constants solvers.""" from __future__ import annotations from abc import ABC, abstractmethod from collections.abc import Sequence from typing import Optional, Union import numpy as np from symfc.basis_sets import FCBasisSetBase class FCSolverBase(ABC): """Abstract base class of force constants solvers.""" def __init__( self, basis_set: Union[FCBasisSetBase, Sequence[FCBasisSetBase]], use_mkl: bool = False, log_level: int = 0, ): """Init method.""" self._basis_set = basis_set self._use_mkl = use_mkl self._log_level = log_level if isinstance(self._basis_set, Sequence): _basis_set = self._basis_set[0] else: _basis_set = basis_set assert isinstance(_basis_set, FCBasisSetBase) self._natom = _basis_set.translation_permutations.shape[1] self._coefs: Optional[Union[np.ndarray, Sequence[np.ndarray]]] = None @property def coefs(self) -> Optional[Union[np.ndarray, Sequence[np.ndarray]]]: """Return coefficients of force constants with respect to basis set.""" return self._coefs @property @abstractmethod def full_fc(self) -> Optional[np.ndarray]: """Return full force constants.""" pass @property @abstractmethod def compact_fc(self) -> Optional[np.ndarray]: """Return compact force constants.""" pass @abstractmethod def solve(self): """Solve coefficients of basis set from displacements and forces.""" pass symfc-1.6.0/src/symfc/solvers/sparse_solver_O2.py000066400000000000000000000065421511276756400220720ustar00rootroot00000000000000"""2nd order force constants solver using finite displacements.""" from __future__ import annotations from typing import Literal, Optional import numpy as np from symfc.basis_sets import FCBasisSetO2 from symfc.utils.solver_funcs import get_displacement_sparse_matrix from .solver_base import FCSolverBase from .solver_O2 import run_solver_O2 class FCSparseSolverO2(FCSolverBase): """Third order force constants solver.""" def __init__( self, basis_set: FCBasisSetO2, use_mkl: bool = False, log_level: int = 0, ): """Init method.""" self._basis_set: FCBasisSetO2 super().__init__(basis_set, use_mkl=use_mkl, log_level=log_level) def solve( self, atoms: np.ndarray, displacements: np.ndarray, forces: np.ndarray, batch_size: int = 10000, ) -> FCSparseSolverO2: """Solve coefficients of basis set from displacements and forces. Parameters ---------- atoms : ndarray Indices of atoms displaced. shape=(n_snapshot), dtype='int' displacements : ndarray Displacements of atoms in Cartesian coordinates. shape=(n_snapshot, 3), dtype='double' forces : ndarray Forces of atoms in Cartesian coordinates. shape=(n_snapshot, N, 3), dtype='double' Returns ------- self : FCSparseSolverO2 """ n_data, n_atom, _ = forces.shape f = forces.reshape(n_data, -1) d = get_displacement_sparse_matrix(atoms, displacements, n_atom) fc2_basis = self._basis_set compress_mat_fc2 = fc2_basis.compact_compression_matrix basis_set_fc2 = fc2_basis.blocked_basis_set atomic_decompr_idx_fc2 = fc2_basis.atomic_decompr_idx self._coefs = run_solver_O2( d, f, compress_mat_fc2, basis_set_fc2, atomic_decompr_idx_fc2, batch_size=batch_size, use_sparse_disps=True, use_mkl=self._use_mkl, verbose=self._log_level > 0, ) return self @property def full_fc(self) -> Optional[np.ndarray]: """Return full force constants. Returns ------- np.ndarray shape=(N, N, 3, 3), dtype='double', order='C' """ return self._recover_fcs("full") @property def compact_fc(self) -> Optional[np.ndarray]: """Return full force constants. Returns ------- np.ndarray shape=(n_a, N, 3, 3), dtype='double', order='C' """ return self._recover_fcs("compact") def _recover_fcs( self, comp_mat_type: Literal["full", "compact"] ) -> Optional[np.ndarray]: fc2_basis = self._basis_set if self._coefs is None or fc2_basis.basis_set is None: return None if comp_mat_type == "full": comp_mat_fc2 = fc2_basis.compression_matrix elif comp_mat_type == "compact": comp_mat_fc2 = fc2_basis.compact_compression_matrix else: raise ValueError("Invalid comp_mat_type.") N = self._natom fc2 = fc2_basis.blocked_basis_set.dot(self._coefs) fc2 = np.array( (comp_mat_fc2 @ fc2).reshape((-1, N, 3, 3)), dtype="double", order="C" ) return fc2 symfc-1.6.0/src/symfc/spg_reps/000077500000000000000000000000001511276756400164075ustar00rootroot00000000000000symfc-1.6.0/src/symfc/spg_reps/__init__.py000066400000000000000000000005371511276756400205250ustar00rootroot00000000000000"""Representations of space group for N-th order force constants.""" from .spg_reps_base import SpgRepsBase from .spg_reps_O1 import SpgRepsO1 from .spg_reps_O2 import SpgRepsO2 from .spg_reps_O3 import SpgRepsO3 from .spg_reps_O4 import SpgRepsO4 __all__ = [ "SpgRepsBase", "SpgRepsO1", "SpgRepsO2", "SpgRepsO3", "SpgRepsO4", ] symfc-1.6.0/src/symfc/spg_reps/spg_reps_O1.py000066400000000000000000000052071511276756400211460ustar00rootroot00000000000000"""O1 reps of space group ops with respect to atomic coordinate basis.""" from __future__ import annotations from typing import Optional import numpy as np from scipy.sparse import csr_array from symfc.utils.utils import SymfcAtoms from .spg_reps_base import SpgRepsBase class SpgRepsO1(SpgRepsBase): """Class of reps of space group operations for fc1.""" def __init__( self, supercell: SymfcAtoms, spacegroup_operations: Optional[dict] = None ): """Init method. Parameters ---------- supercell : SymfcAtoms Supercell. spacegroup_operations : dict, optional Space group operations in supercell, by default None. When None, spglib is used. The following keys and values correspond to spglib symmetry dataset: rotations : array_like translations : array_like """ self._r1_reps: list[csr_array] self._col: np.ndarray self._data: np.ndarray super().__init__(supercell, spacegroup_operations=spacegroup_operations) @property def r_reps(self) -> list[csr_array]: """Return 1st rank tensor rotation matricies.""" return self._r1_reps def get_sigma1_rep(self, i: int) -> csr_array: """Compute and return i-th atomic permutation matrix. Parameters ---------- i : int Index of coset presentations of space group operations. """ data, row, col, shape = self._get_sigma1_rep_data(i) return csr_array((data, (row, col)), shape=shape) def _prepare(self, spacegroup_operations): super()._prepare(spacegroup_operations) N = len(self._numbers) self._col = np.arange(N, dtype=int) self._data = np.ones(N, dtype=int) self._compute_r1_reps() def _compute_r1_reps(self, tol: float = 1e-10): """Compute and return 1st rank tensor rotation matricies. This is equivalent to rotation matrix of atomic position in Cartesian coordinates. """ r1_reps = [] for r in self._unique_rotations: r1_rep: np.ndarray = self._lattice.T @ r @ np.linalg.inv(self._lattice.T) row, col = np.nonzero(np.abs(r1_rep) > tol) data = r1_rep[(row, col)] r1_reps.append(csr_array((data, (row, col)), shape=r1_rep.shape)) self._r1_reps = r1_reps def _get_sigma1_rep_data(self, i: int) -> tuple: uri = self._unique_rotation_indices permutation = self._permutations[uri[i]] N = len(self._numbers) row = permutation return self._data, row, self._col, (N, N) symfc-1.6.0/src/symfc/spg_reps/spg_reps_O2.py000066400000000000000000000133011511276756400211410ustar00rootroot00000000000000"""O2 reps of space group ops with respect to atomic coordinate basis.""" from __future__ import annotations from typing import Optional import numpy as np from scipy.sparse import csr_array from symfc.utils.utils import SymfcAtoms from .spg_reps_base import SpgRepsBase class SpgRepsO2(SpgRepsBase): """Class of reps of space group operations for fc2.""" def __init__( self, supercell: SymfcAtoms, spacegroup_operations: Optional[dict] = None ): """Init method. Parameters ---------- supercell : SymfcAtoms Supercell. spacegroup_operations : dict, optional Space group operations in supercell, by default None. When None, spglib is used. The following keys and values correspond to spglib symmetry dataset: rotations : array_like translations : array_like """ self._r2_reps: list[csr_array] super().__init__(supercell, spacegroup_operations=spacegroup_operations) @property def r_reps(self) -> list[csr_array]: """Return 2nd rank tensor rotation matricies.""" return self._r2_reps def get_sigma2_rep(self, i: int, nonzero: Optional[np.ndarray] = None) -> csr_array: """Compute vector representation of i-th atomic pair permutation matrix. Parameters ---------- i : int Index of coset presentations of space group operations. """ return self._get_sigma2_rep_data(i, nonzero=nonzero) def _prepare(self, spacegroup_operations): super()._prepare(spacegroup_operations) N = len(self._numbers) self._atom_pairs = (np.mgrid[0:N, 0:N].reshape((2, -1)).T).astype( "uint16", copy=False ) self._coeff = np.array([N, 1], dtype=int) self._compute_r2_reps() def _compute_r2_reps(self, tol: float = 1e-10): """Compute and return 2nd rank tensor rotation matricies.""" r2_reps = [] for r in self._unique_rotations: r_c = self._lattice.T @ r @ np.linalg.inv(self._lattice.T) r2_rep = np.kron(r_c, r_c) row, col = np.nonzero(np.abs(r2_rep) > tol) data = r2_rep[(row, col)] r2_reps.append(csr_array((data, (row, col)), shape=r2_rep.shape)) self._r2_reps = r2_reps def _get_sigma2_rep_data( self, i: int, nonzero: Optional[np.ndarray] = None ) -> csr_array: """Compute vector representation of i-th atomic pair permutation matrix. Operation permutation[self._atom_pairs @ self._coeff is divided to reduce memory allocation. permutation_pairs represents row indices of nonzero elements in permutation matrix. Column indices of nonzero elements in permutation matrix are array indices of permutation_pairs. """ uri = self._unique_rotation_indices permutation = self._permutations[uri[i]] if nonzero is not None: pairs = self._atom_pairs[nonzero] else: pairs = self._atom_pairs permutation_pairs = permutation[pairs[:, 0]] * self._coeff[0] permutation_pairs += permutation[pairs[:, 1]] return permutation_pairs class SpgRepsO2MatrixReps(SpgRepsBase): """Class of reps of space group operations for fc2.""" def __init__( self, supercell: SymfcAtoms, spacegroup_operations: Optional[dict] = None ): """Init method. Parameters ---------- supercell : SymfcAtoms Supercell. spacegroup_operations : dict, optional Space group operations in supercell, by default None. When None, spglib is used. The following keys and values correspond to spglib symmetry dataset: rotations : array_like translations : array_like """ self._r2_reps: list[csr_array] self._col: np.ndarray self._data: np.ndarray super().__init__(supercell, spacegroup_operations=spacegroup_operations) @property def r_reps(self) -> list[csr_array]: """Return 2nd rank tensor rotation matricies.""" return self._r2_reps def get_sigma2_rep(self, i: int) -> csr_array: """Compute and return i-th atomic pair permutation matrix. Parameters ---------- i : int Index of coset presentations of space group operations. """ data, row, col, shape = self._get_sigma2_rep_data(i) return csr_array((data, (row, col)), shape=shape) def _prepare(self, spacegroup_operations): super()._prepare(spacegroup_operations) N = len(self._numbers) a = np.arange(N) self._atom_pairs = np.stack(np.meshgrid(a, a), axis=-1).reshape(-1, 2) self._coeff = np.array([1, N], dtype=int) self._col = self._atom_pairs @ self._coeff self._data = np.ones(N * N, dtype=int) self._compute_r2_reps() def _compute_r2_reps(self, tol: float = 1e-10): """Compute and return 2nd rank tensor rotation matricies.""" r2_reps = [] for r in self._unique_rotations: r_c = self._lattice.T @ r @ np.linalg.inv(self._lattice.T) r2_rep = np.kron(r_c, r_c) row, col = np.nonzero(np.abs(r2_rep) > tol) data = r2_rep[(row, col)] r2_reps.append(csr_array((data, (row, col)), shape=r2_rep.shape)) self._r2_reps = r2_reps def _get_sigma2_rep_data(self, i: int) -> tuple: uri = self._unique_rotation_indices permutation = self._permutations[uri[i]] NN = len(self._numbers) ** 2 row = permutation[self._atom_pairs] @ self._coeff return self._data, row, self._col, (NN, NN) symfc-1.6.0/src/symfc/spg_reps/spg_reps_O3.py000066400000000000000000000066301511276756400211510ustar00rootroot00000000000000"""O3 reps of space group ops with respect to atomic coordinate basis.""" from __future__ import annotations from typing import Optional import numpy as np from scipy.sparse import csr_array from symfc.spg_reps import SpgRepsBase from symfc.utils.utils import SymfcAtoms class SpgRepsO3(SpgRepsBase): """Class of reps of space group operations for fc3.""" def __init__( self, supercell: SymfcAtoms, spacegroup_operations: Optional[dict] = None ): """Init method. Parameters ---------- supercell : SymfcAtoms Supercell. spacegroup_operations : dict, optional Space group operations in supercell, by default None. When None, spglib is used. The following keys and values correspond to spglib symmetry dataset: rotations : array_like translations : array_like """ self._r3_reps: list[csr_array] super().__init__(supercell, spacegroup_operations=spacegroup_operations) @property def r_reps(self) -> list[csr_array]: """Return 3rd rank tensor rotation matricies.""" return self._r3_reps def get_sigma3_rep( self, i: int, nonzero: Optional[np.ndarray] = None ) -> np.ndarray: """Compute vector representation of i-th atomic pair permutation matrix. Parameters ---------- i : int Index of coset presentations of space group operations. """ return self._get_sigma3_rep_data(i, nonzero=nonzero) def _prepare(self, spacegroup_operations): super()._prepare(spacegroup_operations) N = len(self._numbers) """Bottleneck part in memory allocation""" self._atom_triplets = (np.mgrid[0:N, 0:N, 0:N].reshape((3, -1)).T).astype( "uint16", copy=False ) self._coeff = np.array([N**2, N, 1], dtype=int) self._compute_r3_reps() def _compute_r3_reps(self, tol: float = 1e-10): """Compute and return 3rd rank tensor rotation matricies.""" r3_reps = [] for r in self._unique_rotations: r_c = self._lattice.T @ r @ np.linalg.inv(self._lattice.T) r3_rep = np.kron(r_c, np.kron(r_c, r_c)) row, col = np.nonzero(np.abs(r3_rep) > tol) data = r3_rep[(row, col)] r3_reps.append(csr_array((data, (row, col)), shape=r3_rep.shape)) self._r3_reps = r3_reps def _get_sigma3_rep_data( self, i: int, nonzero: Optional[np.ndarray] = None ) -> np.ndarray: """Compute vector representation of i-th atomic pair permutation matrix. Operation permutation[self._atom_triplets] @ self._coeff is divided to reduce memory allocation. permutation_triplets represents row indices of nonzero elements in permutation matrix. Column indices of nonzero elements in permutation matrix are array indices of permutation_triplets. """ uri = self._unique_rotation_indices permutation = self._permutations[uri[i]] if nonzero is not None: triplets = self._atom_triplets[nonzero] else: triplets = self._atom_triplets permutation_triplets = permutation[triplets[:, 0]] * self._coeff[0] permutation_triplets += permutation[triplets[:, 1]] * self._coeff[1] permutation_triplets += permutation[triplets[:, 2]] return permutation_triplets symfc-1.6.0/src/symfc/spg_reps/spg_reps_O4.py000066400000000000000000000070611511276756400211510ustar00rootroot00000000000000"""O4 reps of space group ops with respect to atomic coordinate basis.""" from __future__ import annotations from typing import Optional import numpy as np from scipy.sparse import csr_array from symfc.spg_reps import SpgRepsBase from symfc.utils.utils import SymfcAtoms class SpgRepsO4(SpgRepsBase): """Class of reps of space group operations for fc4.""" def __init__( self, supercell: SymfcAtoms, spacegroup_operations: Optional[dict] = None ): """Init method. Parameters ---------- supercell : SymfcAtoms Supercell. spacegroup_operations : dict, optional Space group operations in supercell, by default None. When None, spglib is used. The following keys and values correspond to spglib symmetry dataset: rotations : array_like translations : array_like """ self._r4_reps: list[csr_array] super().__init__(supercell, spacegroup_operations=spacegroup_operations) @property def r_reps(self) -> list[csr_array]: """Return 4th rank tensor rotation matricies.""" return self._r4_reps def get_sigma4_rep( self, i: int, nonzero: Optional[np.ndarray] = None ) -> np.ndarray: """Compute vector representation of i-th atomic pair permutation matrix. Parameters ---------- i : int Index of coset presentations of space group operations. """ return self._get_sigma4_rep_data(i, nonzero=nonzero) def _prepare(self, spacegroup_operations): super()._prepare(spacegroup_operations) N = len(self._numbers) """Bottleneck part in memory allocation""" self._atom_quadruplets = ( np.mgrid[0:N, 0:N, 0:N, 0:N].reshape((4, -1)).T ).astype("uint16", copy=False) self._coeff = np.array([N**3, N**2, N, 1], dtype=int) self._compute_r4_reps() def _compute_r4_reps(self, tol: float = 1e-10): """Compute and return 4th rank tensor rotation matricies.""" r4_reps = [] for r in self._unique_rotations: r_c = self._lattice.T @ r @ np.linalg.inv(self._lattice.T) r4_rep = np.kron(r_c, np.kron(r_c, np.kron(r_c, r_c))) row, col = np.nonzero(np.abs(r4_rep) > tol) data = r4_rep[(row, col)] r4_reps.append(csr_array((data, (row, col)), shape=r4_rep.shape)) self._r4_reps = r4_reps def _get_sigma4_rep_data( self, i: int, nonzero: Optional[np.ndarray] = None ) -> np.ndarray: """Compute vector representation of i-th atomic pair permutation matrix. Operation permutation[self._atom_quadruplets] @ self._coeff is divided to reduce memory allocation. permutation_quadruplets represents row indices of nonzero elements in permutation matrix. Column indices of nonzero elements in permutation matrix are array indices of permutation_quadruplets. """ uri = self._unique_rotation_indices permutation = self._permutations[uri[i]] if nonzero is not None: quadruplets = self._atom_quadruplets[nonzero] else: quadruplets = self._atom_quadruplets permutation_quadruplets = permutation[quadruplets[:, 0]] * self._coeff[0] permutation_quadruplets += permutation[quadruplets[:, 1]] * self._coeff[1] permutation_quadruplets += permutation[quadruplets[:, 2]] * self._coeff[2] permutation_quadruplets += permutation[quadruplets[:, 3]] return permutation_quadruplets symfc-1.6.0/src/symfc/spg_reps/spg_reps_base.py000066400000000000000000000114571511276756400216050ustar00rootroot00000000000000"""Reps base of space group ops with respect to atomic coordinate basis.""" from __future__ import annotations from typing import Optional import numpy as np from symfc.utils.utils import ( SymfcAtoms, compute_sg_permutations, get_indep_atoms_by_lat_trans, ) class SpgRepsBase: """Base class of reps of space group operations.""" def __init__( self, supercell: SymfcAtoms, spacegroup_operations: Optional[dict] = None ): """Init method. Parameters ---------- supercell : SymfcAtoms Supercell. spacegroup_operations : dict, optional Space group operations in supercell, by default None. When None, spglib is used. The following keys and values correspond to spglib symmetry dataset: rotations : array_like translations : array_like """ self._lattice = np.array(supercell.cell, dtype="double", order="C") self._positions = np.array( supercell.scaled_positions, dtype="double", order="C" ) self._numbers = supercell.numbers self._unique_rotations: np.ndarray self._unique_rotation_indices: np.ndarray self._translation_permutations: np.ndarray self._p2s_map: np.ndarray self._prepare(spacegroup_operations) @property def translation_permutations(self) -> np.ndarray: """Return permutations by lattice translation. Returns ------- Atom indices after lattice translations. shape=(lattice_translations, supercell_atoms), dtype=int """ return self._translation_permutations @property def unique_rotation_indices(self) -> np.ndarray: """Return indices of coset representatives of space group operations.""" return self._unique_rotation_indices @property def p2s_map(self) -> np.ndarray: """Return indices of translationally independent atoms.""" return self._p2s_map def _prepare(self, spacegroup_operations): rotations, translations = self._get_symops(spacegroup_operations) self._unique_rotation_indices, self._unique_rotations = ( self._get_unique_rotation_indices(rotations) ) self._permutations = compute_sg_permutations( self._positions, rotations, translations, self._lattice.T, 1e-5 ) self._translation_permutations = self._get_translation_permutations(rotations) self._p2s_map = get_indep_atoms_by_lat_trans(self._translation_permutations) def _get_translation_permutations(self, rotations) -> np.ndarray: eye3 = np.eye(3, dtype=int) trans_perms = [] for r, perm in zip(rotations, self._permutations): if np.array_equal(r, eye3): trans_perms.append(perm) return np.array(trans_perms, dtype="intc", order="C") def _get_unique_rotation_indices( self, rotations: np.ndarray ) -> tuple[np.ndarray, np.ndarray]: unique_rotations: list[np.ndarray] = [] indices = [] for i, r in enumerate(rotations): is_found = False for ur in unique_rotations: if np.array_equal(r, ur): is_found = True break if not is_found: unique_rotations.append(r) indices.append(i) return np.array(indices, dtype=int), np.array(unique_rotations, dtype=int) def _get_symops( self, spacegroup_operations: Optional[dict] = None ) -> tuple[np.ndarray, np.ndarray]: """Return symmetry operations. The set of inverse operations is the same as the set of the operations. Parameters ---------- spacegroup_operations : dict, optional Space group operations in supercell, by default None. When None, spglib is used to get the operations. Returns ------- rotations : array_like A set of rotation matrices of inverse space group operations. (n_symops, 3, 3), dtype='intc', order='C' translations : array_like A set of translation vectors. It is assumed that inverse matrices are included in this set. (n_symops, 3), dtype='double'. """ if spacegroup_operations is None: try: import spglib except ImportError as exc: raise ModuleNotFoundError( "Spglib python module was not found." ) from exc symops = spglib.get_symmetry( # type: ignore (self._lattice, self._positions, self._numbers) # type: ignore ) else: symops = spacegroup_operations return symops["rotations"], symops["translations"] # type: ignore symfc-1.6.0/src/symfc/utils/000077500000000000000000000000001511276756400157255ustar00rootroot00000000000000symfc-1.6.0/src/symfc/utils/cutoff_tools.py000066400000000000000000000215211511276756400210060ustar00rootroot00000000000000"""Utility functions for introducing zero force constants.""" import itertools from typing import Union import numpy as np from symfc.utils.utils import SymfcAtoms def apply_zeros(C, zero_ids): """Assign zero to matrix elements. Use this function, sparse C can become larger by assigning zeros. Zero elements should be applied to c_trans and c_perm in constructing them. Warning: Slow when zero_ids is large. Method 1 -------- C[zero_ids,:] = 0 Method 2 -------- C = C.tolil() C[zero_ids, :] = 0 C = C.tocsr() """ for i in zero_ids: nonzero_cols = C.getrow(i).nonzero()[1] for j in nonzero_cols: C[i, j] = 0 return C class FCCutoff: """Class for introducing cutoff radius.""" def __init__(self, supercell: SymfcAtoms, cutoff: float = 7.0): """Find nonzero FC elements given by cutoff distance. For FC3, zero elements are determined by the distance between three pairs of atoms. Parameters ---------- supercell: SymfcAtoms Supercell structure. cutoff: float Cutoff distance between two atoms. """ self._supercell = supercell self._cutoff = cutoff self._n_atom = supercell.scaled_positions.shape[0] self._distances = self._calc_distances() self._neighbors = None self._nonzero_fc2 = None self._nonzero_fc3 = None self._nonzero_fc4 = None @property def neighbors(self) -> list[np.ndarray]: """Neighbor atoms: shape=(n_atom, n_neighbors).""" if self._neighbors is None: self._neighbors = [ np.where(self._distances[i] < self._cutoff)[0] for i in range(self._n_atom) ] return self._neighbors @property def outsides(self) -> list[np.ndarray]: """Atoms outside cutoff radius: shape=(n_atom, n_neighbors).""" return [ np.where(self._distances[i] >= self._cutoff)[0] for i in range(self._n_atom) ] @property def distances(self) -> np.ndarray: """Minimum distances between atoms: shape=(n_atom, n_atom).""" return self._distances def combinations1(self) -> np.ndarray: """Return combinations with single index ia.""" return np.array([[i] for i in range(3 * self._n_atom)], dtype=int) def combinations2(self) -> np.ndarray: """Return combinations with two distinguished indices (ia,jb).""" combinations = [] for jb in range(3 * self._n_atom): j = jb // 3 combs = [ [3 * i + a, jb] for i in self.neighbors[j] for a in range(3) if 3 * i + a < jb ] combinations.extend(combs) combinations_fc2 = np.array(combinations) return combinations_fc2 def combinations3_all(self) -> np.ndarray: """Return combinations with three distinguished indices (ia,jb,kc).""" combinations = [] for kc in range(3 * self._n_atom): combs = self.combinations3(kc) combinations.extend(combs) combinations_fc3 = np.array(combinations) return combinations_fc3 def combinations3(self, kc) -> Union[list, np.ndarray]: """Return combinations with three distinguished indices (ia,jb,kc). Return only combinations with kc. """ k = kc // 3 neighbors_N3 = [ 3 * j + b for j in self.neighbors[k] for b in range(3) if 3 * j + b < kc ] combs = np.array(list(itertools.combinations(neighbors_N3, 2))) if len(combs) > 0: indices = np.where( self.distances[(combs[:, 0] // 3, combs[:, 1] // 3)] < self._cutoff )[0] combs = combs[indices] return np.hstack([combs, np.full((combs.shape[0], 1), kc)]) return [] def combinations4_all(self) -> np.ndarray: """Return combinations with three distinguished indices (ia,jb,kc,ld).""" combinations = [] for ld in range(3 * self._n_atom): combs = self.combinations4(ld) combinations.extend(combs) combinations_fc4 = np.array(combinations) return combinations_fc4 def combinations4(self, ld) -> Union[list, np.ndarray]: """Return combinations with three distinguished indices (ia,jb,kc,ld). Return only combinations with kc. """ ll = ld // 3 neighbors_N3 = [ 3 * j + b for j in self.neighbors[ll] for b in range(3) if 3 * j + b < ld ] combs = np.array(list(itertools.combinations(neighbors_N3, 3))) if len(combs) > 0: indices = np.where( (self.distances[(combs[:, 0] // 3, combs[:, 1] // 3)] < self._cutoff) & (self.distances[(combs[:, 0] // 3, combs[:, 2] // 3)] < self._cutoff) & (self.distances[(combs[:, 1] // 3, combs[:, 2] // 3)] < self._cutoff) )[0] combs = combs[indices] return np.hstack([combs, np.full((combs.shape[0], 1), ld)]) return [] def nonzero_atomic_indices_fc2(self) -> np.ndarray: """Return atomic indices of nonzero FC2. Returns ------- nonzero : np.ndarray FC2 element is nonzero (True) or zero (False), shape=(NN). """ if self._nonzero_fc2 is not None: return self._nonzero_fc2 self._nonzero_fc2 = nonzero = np.zeros(self._n_atom**2, dtype=bool) for i in range(self._n_atom): ids = np.array(self.neighbors[i]) + i * self._n_atom nonzero[ids] = True return self._nonzero_fc2 def nonzero_atomic_indices_fc3(self) -> np.ndarray: """Return atomic indices of nonzero FC3. Returns ------- nonzero : np.ndarray FC3 element is nonzero (True) or zero (False), shape=(NNN). """ if self._nonzero_fc3 is not None: return self._nonzero_fc3 self._nonzero_fc3 = nonzero = np.zeros(self._n_atom**3, dtype=bool) for i in range(self._n_atom): jlist = self.neighbors[i] combs = np.array(list(itertools.product(jlist, jlist))) if len(combs) > 0: combs = combs[self.distances[(combs[:, 0], combs[:, 1])] < self._cutoff] ids = combs @ np.array([self._n_atom, 1]) + i * self._n_atom**2 nonzero[ids] = True return self._nonzero_fc3 def nonzero_atomic_indices_fc4(self) -> np.ndarray: """Return atomic indices of nonzero FC4. Returns ------- nonzero : np.ndarray FC4 element is nonzero (True) or zero (False), shape=(NNNN). """ if self._nonzero_fc4 is not None: return self._nonzero_fc4 self._nonzero_fc4 = nonzero = np.zeros(self._n_atom**4, dtype=bool) for i in range(self._n_atom): jlist = self.neighbors[i] combs = np.array(list(itertools.product(*[jlist, jlist, jlist]))) if len(combs) > 0: combs = combs[ (self.distances[(combs[:, 0], combs[:, 1])] < self._cutoff) & (self.distances[(combs[:, 0], combs[:, 2])] < self._cutoff) & (self.distances[(combs[:, 1], combs[:, 2])] < self._cutoff) ] ids = combs @ np.array([self._n_atom**2, self._n_atom, 1]) ids += i * self._n_atom**3 nonzero[ids] = True return self._nonzero_fc4 def _calc_distances(self) -> np.ndarray: """Calculate minimum distances between atoms. This algorithm must be reconsidered. (Not applicable to structures with strange lattice shape) """ try: import spglib except ImportError as exc: raise ModuleNotFoundError("Spglib python module was not found.") from exc reduced_bases = spglib.niggli_reduce(self._supercell.cell) # type: ignore trans_mat_float = self._supercell.cell @ np.linalg.inv(reduced_bases) # type: ignore trans_mat = np.rint(trans_mat_float).astype(int) assert (np.abs(trans_mat_float - trans_mat) < 1e-8).all() scaled_positions = self._supercell.scaled_positions @ trans_mat scaled_positions -= np.rint(scaled_positions) diff = scaled_positions[:, None, :] - scaled_positions[None, :, :] trans = np.array(list(itertools.product(*[[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]]))) norms = np.ones((self._n_atom, self._n_atom)) * 1e10 for t1 in trans: t1_tile = np.tile(t1, (self._n_atom, self._n_atom, 1)) norms_trial = np.linalg.norm((diff - t1_tile) @ reduced_bases, axis=2) match = norms_trial < norms norms[match] = norms_trial[match] self._distances = norms return self._distances symfc-1.6.0/src/symfc/utils/eig_tools.py000066400000000000000000000501461511276756400202710ustar00rootroot00000000000000"""Utility functions for eigenvalue solutions.""" from collections import defaultdict from dataclasses import dataclass from typing import Optional, Union import numpy as np import scipy from scipy.sparse import csr_array from symfc.utils.graph import connected_components from symfc.utils.matrix import ( BlockMatrixNode, append_node, matrix_rank, return_numpy_array, root_block_matrix, ) from symfc.utils.solver_funcs import get_batch_slice def eigh_projector( p: Union[np.ndarray, csr_array], atol: float = 1e-8, rtol: float = 0.0, return_cmplt: bool = False, return_block: bool = False, verbose: bool = True, ) -> Union[ np.ndarray, tuple[np.ndarray, tuple[np.ndarray, np.ndarray]], None, tuple[None, None], ]: """Solve eigenvalue problem using numpy and eliminate eigenvectors with e < 1.0.""" p = return_numpy_array(p) rank = matrix_rank(p) if rank == 0: if return_cmplt: return None, None return None if rank > 32767: raise RuntimeError("Projector rank is too large in eigh.") try: eigvals, eigvecs = np.linalg.eigh(p) except np.linalg.LinAlgError as e: if verbose: print(f"np.linalg.eigh failed: {str(e)}") print("Try scipy.linalg.lapack.dsyev") eigvals, eigvecs, info = scipy.linalg.lapack.dsyev(p.T) if info != 0: raise scipy.linalg.LinAlgError( "scipy.linalg.lapack.dsyev failed: Eigenvalues did not converge" ) from e tol = 1e-8 if np.count_nonzero((eigvals > 1.0 + tol) | (eigvals < -tol)): diff = np.abs(p - p.T) if np.any(diff > 1e-3): raise RuntimeError("Transpose equality not satisfied") elif np.any(diff > 1e-15): eigvals, eigvecs = np.linalg.eigh(0.5 * (p + p.T)) if np.count_nonzero((eigvals > 1.0 + tol) | (eigvals < -tol)): raise ValueError("Eigenvalue error: e > 1 or e < 0.") nonzero = np.isclose(eigvals, 1.0, atol=atol, rtol=rtol) if return_cmplt: cmplt_bool = np.logical_and(np.logical_not(nonzero), eigvals > 1e-12) cmplt_eigvals, cmplt_eigvecs = eigvals[cmplt_bool], eigvecs[:, cmplt_bool] eigvecs = eigvecs[:, nonzero] if return_block: block = root_block_matrix(data=eigvecs) if return_cmplt: return block, (cmplt_eigvals, cmplt_eigvecs) return block if return_cmplt: return eigvecs, (cmplt_eigvals, cmplt_eigvecs) return eigvecs def _compr_projector(p: csr_array) -> tuple[csr_array, Optional[csr_array]]: """Compress projection matrix p with many zero rows and columns.""" _, col_p = p.nonzero() # type: ignore col_p = np.unique(col_p) size = len(col_p) if p.shape[1] > size: compr = csr_array( (np.ones(size), (col_p, np.arange(size))), shape=(p.shape[1], size), dtype="int", ) """p = compr.T @ p @ compr""" p = p[col_p].T p = p[col_p].T return p, compr return p, None def _find_projector_blocks(p: csr_array, verbose: bool = False): """Find block structures in projection matrix.""" if verbose: print("Finding block diagonal structure in projector.", flush=True) if len(p.data) < 2147483647: if verbose: print("Using scipy connected_components.", flush=True) n_components, labels = scipy.sparse.csgraph.connected_components(p) group = defaultdict(list) for i, ll in enumerate(labels): group[ll].append(i) else: if verbose: print("Using symfc connected_components with DFS.", flush=True) group = connected_components(p) return group def _recover_eigvecs_from_uniq_eigvecs( uniq_eigvecs: dict, group: dict, size_projector: int, ) -> csr_array: """Recover all eigenvectors from unique eigenvectors. Parameters ---------- uniq_eigvecs: Unique eigenvectors and submatrixblock indices are included in its values. group: Row indices comprising submatrix blocks. """ total_length = sum( len(labels) * v.shape[0] * v.shape[1] for v, labels in uniq_eigvecs.values() if v is not None ) row = np.zeros(total_length, dtype=int) col = np.zeros(total_length, dtype=int) data = np.zeros(total_length, dtype="double") current_id, col_id = 0, 0 for eigvecs, labels in uniq_eigvecs.values(): if eigvecs is not None: n_row, n_col = eigvecs.shape num_labels = len(labels) end_id = current_id + n_row * n_col * num_labels row[current_id:end_id] = np.repeat( [i for ll in labels for i in group[ll]], n_col ) col[current_id:end_id] = [ j for seq, _ in enumerate(labels) for i in range(n_row) for j in range(col_id + seq * n_col, col_id + (seq + 1) * n_col) ] data[current_id:end_id] = np.tile(eigvecs.flatten(), num_labels) col_id += n_col * num_labels current_id = end_id n_col = col_id eigvecs = csr_array((data, (row, col)), shape=(size_projector, n_col)) return eigvecs @dataclass class DataCSR: """Dataclass for extracting data in projector.""" data: np.ndarray block_labels: np.ndarray block_sizes: np.ndarray slice_begin: Optional[np.ndarray] = None slice_end: Optional[np.ndarray] = None def __post_init__(self): """Init method.""" self.slice_end = np.cumsum(self.block_sizes**2) self.slice_begin = np.zeros_like(self.slice_end) self.slice_begin[1:] = self.slice_end[:-1] def get_data(self, idx: int): """Get data in projector for i-th block.""" if self.slice_begin is None or self.slice_end is None: raise ValueError("Slice begin and end are not initialized.") s1 = self.slice_begin[idx] s2 = self.slice_end[idx] return self.data[s1:s2] def get_block_label(self, idx: int): """Get block label for i-th block.""" return self.block_labels[idx] def get_block_size(self, idx: int): """Get block size for i-th block.""" return self.block_sizes[idx] @property def n_blocks(self): """Return number of blocks in projector.""" return len(self.block_labels) def _extract_sparse_projector_data(p: csr_array, group: dict) -> DataCSR: """Extract data in projector in csr_format efficiently. Parameters ---------- p: Projection matrix in CSR format. group: Row indices comprising submatrix blocks. """ # r = np.array([i for ids in group.values() for i in ids for j in ids]) group_ravel = [i for ids in group.values() for i in ids] lengths = [len(ids) for ids in group.values() for i in ids] r = np.repeat(group_ravel, lengths) c = np.array([j for ids in group.values() for _ in ids for j in ids]) sizes = np.array([len(ids) for ids in group.values()], dtype=int) p_data = DataCSR( data=np.ravel(p[r, c]), block_labels=np.array(list(group.keys())), block_sizes=sizes, ) return p_data def eigsh_projector( p: csr_array, atol: float = 1e-8, rtol: float = 0.0, verbose: bool = True, ) -> csr_array: """Solve eigenvalue problem for matrix p. Return sparse matrix for eigenvectors of matrix p. This algorithm begins with finding block diagonal structures in matrix p. For each block submatrix, eigenvectors are solved. When p = diag(A,B), Av = v, and Bw = w, p[v,0] = [v,0] and p[0,w] = [0,w] are solutions. This function avoids solving numpy.eigh for duplicate block matrices. This function is efficient for matrix p composed of many duplicate block matrices. """ p, compr_p = _compr_projector(p) group = _find_projector_blocks(p, verbose=verbose) if verbose: rank = matrix_rank(p) print("Rank of projector:", rank, flush=True) print("Number of blocks in projector:", len(group), flush=True) p_data = _extract_sparse_projector_data(p, group) uniq_eigvecs = dict() for i in range(p_data.n_blocks): p_block = p_data.get_data(i) block_label = p_data.get_block_label(i) block_size = p_data.get_block_size(i) if block_size > 1: key = tuple(p_block) try: uniq_eigvecs[key][1].append(block_label) except KeyError: p_np = p_block.reshape((block_size, block_size)) eigvecs = eigh_projector(p_np, atol=atol, rtol=rtol, verbose=verbose) uniq_eigvecs[key] = [eigvecs, [block_label]] else: if not np.isclose(p_block[0], 0.0): if "one" in uniq_eigvecs: uniq_eigvecs["one"][1].append(block_label) else: uniq_eigvecs["one"] = [np.array([[1.0]]), [block_label]] c_p = _recover_eigvecs_from_uniq_eigvecs(uniq_eigvecs, group, p.shape[0]) # type: ignore if compr_p is not None: return compr_p @ c_p return c_p def _find_submatrix_eigenvectors( p: Union[np.ndarray, csr_array], batch_size: Optional[int] = None, depth: int = 0, use_mkl: bool = False, verbose: bool = False, ): """Find eigenvectors in division part of submatrix division algorithm.""" p_size = p.shape[0] if p_size < 500: repeat = False elif p_size > 30000: repeat = True if depth < 8 else False else: repeat = True if depth < 3 else False if batch_size is None: if depth == 1: batch_size = max(p_size // 10, 500) elif depth == 2: batch_size = max(p_size // 5, 250) elif depth == 3: batch_size = p_size // 2 else: batch_size = min(int(round(p_size / 1.3)), 20000) sibling, sibling_c = None, None col_id, col_id_c = 0, 0 header = " " * (depth - 1) + "(" + str(depth) + ")" for begin, end in zip(*get_batch_slice(p_size, batch_size)): if verbose: print(header, "Block:", end, "/", p_size, flush=True) p_small = p[begin:end, begin:end] rank = matrix_rank(p_small) if rank > 0: if repeat: block, (cmplt_eigvals, cmplt_vecs) = eigh_projector_division( p_small, atol=1e-12, rtol=0.0, depth=depth, return_cmplt=True, return_block=True, use_mkl=use_mkl, verbose=verbose, ) else: block, (cmplt_eigvals, cmplt_vecs) = eigh_projector( p_small, atol=1e-12, rtol=0.0, return_cmplt=True, return_block=True, verbose=verbose, ) rows = np.arange(begin, end) if block is not None: if verbose: print( header, " ", block.shape[1], "eigenvectors found.", flush=True ) sibling = append_node(block, sibling, rows=rows, col_begin=col_id) col_id += block.shape[1] if cmplt_vecs is not None: sibling_c = append_node( cmplt_vecs, sibling_c, rows=rows, col_begin=col_id_c, eigvals=cmplt_eigvals, ) col_id_c += cmplt_vecs.shape[1] if col_id_c > 0: cmplt = root_block_matrix((p_size, col_id_c), first_child=sibling_c) else: cmplt = None return sibling, col_id, cmplt def _find_complement_eigenvectors( p: Union[np.ndarray, csr_array], sibling: BlockMatrixNode, col_id: int, cmplt: BlockMatrixNode, atol: float = 1e-8, rtol: float = 0.0, depth: int = 0, return_cmplt: bool = False, use_mkl: bool = False, verbose: bool = False, ): """Find eigenvectors in complementary part of submatrix division algorithm.""" p_size = p.shape[0] if p_size < 5000: repeat = False elif p_size > 30000: repeat = True if depth < 8 else False else: repeat = True if depth < 4 else False if depth == 1: batch_size_cmplt = max(cmplt.shape[1] // 3, p_size // 15) elif depth == 2: batch_size_cmplt = int(round(cmplt.shape[1] / 1.5)) elif depth == 3: batch_size_cmplt = int(round(cmplt.shape[1] / 1.3)) else: batch_size_cmplt = min(int(round(cmplt.shape[1] / 1.2)), 20000) header = " " * (depth - 1) + "(" + str(depth) + ")" if verbose: print(header, "Complementary block size:", cmplt.shape[1], flush=True) print(header, "Compute compressed projector.", flush=True) p_cmr = cmplt.compress_matrix(p, use_mkl=use_mkl) if not repeat: if verbose: print(header, "Use standard solver.", flush=True) result = eigh_projector( p_cmr, atol=atol, rtol=rtol, return_cmplt=return_cmplt, verbose=verbose, ) else: if verbose: print(header, "Use submatrix size of", batch_size_cmplt, flush=True) result = eigh_projector_division( p_cmr, atol=atol, rtol=rtol, depth=depth, batch_size=batch_size_cmplt, return_cmplt=return_cmplt, use_mkl=use_mkl, verbose=verbose, ) eigvecs = result[0] if return_cmplt else result if eigvecs is not None: if verbose: print(header, " ", eigvecs.shape[1], "eigenvectors found.", flush=True) sibling = append_node( eigvecs, sibling, rows=np.arange(p_size), col_begin=col_id, compress=cmplt, ) col_id += eigvecs.shape[1] if return_cmplt: cmplt_eigvals, cmplt_small = result[1] if cmplt_small is not None and cmplt_small.shape[1] > 0: cmplt_eigvecs = cmplt.dot(cmplt_small) else: cmplt_eigvals, cmplt_eigvecs = None, None return sibling, col_id, (cmplt_eigvals, cmplt_eigvecs) return sibling, col_id def _run_division( p: Union[np.ndarray, csr_array], batch_size: Optional[int] = None, atol: float = 1e-8, rtol: float = 0.0, depth: int = 0, return_cmplt: bool = False, use_mkl: bool = False, verbose: bool = False, ): """Find eigenvectors in division and complementary parts.""" depth += 1 sibling, col_id, cmplt = _find_submatrix_eigenvectors( p, batch_size=batch_size, depth=depth, use_mkl=use_mkl, verbose=verbose, ) if cmplt is not None: result = _find_complement_eigenvectors( p, sibling, col_id, cmplt, atol=atol, rtol=rtol, depth=depth, return_cmplt=return_cmplt, use_mkl=use_mkl, verbose=verbose, ) if return_cmplt: sibling, col_id, (cmplt_eigvals, cmplt_eigvecs) = result else: sibling, col_id = result else: cmplt_eigvals, cmplt_eigvecs = None, None block = root_block_matrix((p.shape[0], col_id), first_child=sibling) if return_cmplt: return block, (cmplt_eigvals, cmplt_eigvecs) return block def eigh_projector_division( p: Union[np.ndarray, csr_array], atol: float = 1e-8, rtol: float = 0.0, depth: int = 0, batch_size: Optional[int] = None, return_cmplt: bool = False, return_block: bool = False, use_mkl: bool = False, verbose: bool = False, ): r"""Solve eigenvalue problem using submatrix division algorithm. This algorithm is optimized to solve an eigenvalue problem for projection matrix p with large block submatrices. The algorithm for solving each block submatrix is as follows. 1. Divide each block submatrix into reasonable sizes of submatrices, not projectors. 2. Solve eigenvalue problems for these submatrices and eigenvectors with eigenvalues of one are extracted. The eigenvectors with eigenvalues e < 1 are used for compressing the complementary matrix in the next step. 3. Calculate the complementary matrix, which corresponds to the complement of the vector space spanned by the eigenvectors. The complementary matrix is compressed using the eigenvectors with e < 1. 4. Solve eigenvalue problem for the complementary matrix and eigenvectors with e = 1 are calculated. The eigenvalue problems are efficiently solved using the compressed complementary matrix and the eigenvectors are recovered by the compression matrix. 5. Collect all eigenvectors with e = 1. """ p_size = p.shape[0] if p_size < 500: return eigh_projector( p, atol=atol, rtol=rtol, return_cmplt=return_cmplt, return_block=return_block, verbose=verbose, ) result = _run_division( p, batch_size=batch_size, atol=atol, rtol=rtol, depth=depth, return_cmplt=return_cmplt, use_mkl=use_mkl, verbose=verbose, ) if return_block: return result if return_cmplt: block, (cmplt_eigvals, cmplt_eigvecs) = result else: block = result eigvecs = block.recover() if block is not None else None if return_cmplt: return eigvecs, (cmplt_eigvals, cmplt_eigvecs) return eigvecs def eigsh_projector_sumrule( p: csr_array, atol: float = 1e-8, rtol: float = 0.0, size_threshold: int = 500, use_mkl: bool = False, verbose: bool = True, ) -> BlockMatrixNode: """Solve eigenvalue problem for matrix p. Return dense matrix for eigenvectors of matrix p. This algorithm begins with finding block diagonal structures in matrix p. For each block submatrix, eigenvectors are solved. When p = diag(A,B), Av = v, and Bw = w, p[v,0] = [v,0] and p[0,w] = [0,w] are solutions. If p.shape[0] < size_threshold, this function solves numpy.eigh for all block matrices. Otherwise, this function use a submatrix division algorithm to solve the eigenvalue problem of each block matrix. """ group = _find_projector_blocks(p, verbose=verbose) lengths = [-len(ids) for ids in group.values()] order = np.array(list(group.keys()))[np.argsort(lengths)] if verbose: print("Number of blocks in projector (Sum rule):", len(group), flush=True) sibling, col_id = None, 0 for i, key in enumerate(order): ids = np.array(group[key]) if verbose and len(ids) > 0: print("--- Eigsh_solver_block:", i + 1, "/", len(group), "---", flush=True) print("Block_size:", len(ids), flush=True) p_block = p[np.ix_(ids, ids)] rank = matrix_rank(p_block) if rank > 0 and p_block.shape[0] < size_threshold: if verbose: print("Use standard eigh solver.", flush=True) p_block = p_block.toarray() eigvecs = eigh_projector(p_block, atol=atol, rtol=rtol, verbose=verbose) if eigvecs is not None: sibling = append_node(eigvecs, sibling, rows=ids, col_begin=col_id) col_id += eigvecs.shape[1] elif rank > 0 and p_block.shape[0] >= size_threshold: if verbose: print("Use submatrix version of eigh solver.", flush=True) block = eigh_projector_division( p_block, atol=atol, rtol=rtol, return_block=True, use_mkl=use_mkl, verbose=verbose, ) if block is not None: sibling = append_node(block, sibling, rows=ids, col_begin=col_id) col_id += block.shape[1] del p_block block = root_block_matrix((p.shape[0], col_id), first_child=sibling) if verbose: print("Tree of FC basis block matrices:", flush=True) block.print_nodes() return block symfc-1.6.0/src/symfc/utils/graph.py000066400000000000000000000025171511276756400174050ustar00rootroot00000000000000"""Functions for graph.""" import numpy as np from scipy.sparse import csr_array, issparse def get_cols(p: csr_array, node: int): """Return column indices of csr array.""" start = p.indptr[node] end = p.indptr[node + 1] if start == 0: return p.indices[end - 1 :: -1] return p.indices[end - 1 : start - 1 : -1] def dfs_tree_stack(p: csr_array, visited: np.ndarray, initial_node: int = 0): """Run depth first search using stack for sparse matrix p.""" visited_nodes = [] stack = [initial_node] while stack: node = stack.pop() if not visited[node]: visited[node] = True visited_nodes.append(node) cols = get_cols(p, node) unvisited = cols[~visited[cols]] stack.extend(unvisited.tolist()) return visited, sorted(visited_nodes) def connected_components(p: csr_array): """Calculate connected components of graph p.""" if not issparse(p): raise RuntimeError("Not sparse matrix.") p.eliminate_zeros() visited = np.zeros(p.shape[0], dtype=bool) group, label = dict(), 0 for node in range(p.shape[0]): if visited[node]: continue visited, visited_nodes = dfs_tree_stack(p, visited, initial_node=node) group[label] = visited_nodes label += 1 return group symfc-1.6.0/src/symfc/utils/matrix.py000066400000000000000000000305201511276756400176030ustar00rootroot00000000000000"""Utility functions for matrices.""" from dataclasses import dataclass from typing import Any, Optional, Union import numpy as np from scipy.sparse import csr_array try: from sparse_dot_mkl import dot_product_mkl # type: ignore except ImportError: pass import sys sys.setrecursionlimit(100000) def dot_product_sparse( A: Union[np.ndarray, csr_array], B: Union[np.ndarray, csr_array], use_mkl: bool = False, dense: bool = False, ) -> csr_array: """Compute dot-product of sparse matrices.""" if use_mkl: return dot_product_mkl(A, B, dense=dense) return A @ B def is_sparse(p: Union[np.ndarray, csr_array]) -> bool: """Check whether matrix is sparse matrix or not.""" if isinstance(p, np.ndarray): return False elif isinstance(p, list): return False return True def return_numpy_array(p: Union[np.ndarray, csr_array]) -> np.ndarray: """Return numpy array.""" if isinstance(p, np.ndarray): return p return p.toarray() def matrix_rank(p: Union[np.ndarray, csr_array]) -> int: """Calculate projector rank.""" if is_sparse(p): return int(round(p.trace())) return int(round(np.trace(p))) @dataclass class BlockMatrixNode: """Dataclass for node in block matrix tree.""" rows: np.ndarray col_begin: int col_end: int root: bool = False shape: Optional[tuple] = None data: Optional[np.ndarray] = None first_child: Optional[Any] = None next_sibling: Optional[Any] = None compress: Optional[Any] = None rows_root: Optional[np.ndarray] = None col_begin_root: Optional[int] = None col_end_root: Optional[int] = None index: Optional[int] = None eigvals: Optional[np.ndarray] = None def __post_init__(self): """Post init method.""" self.shape = (len(self.rows), self.col_end - self.col_begin) self.rows = np.array(self.rows) self._check_errors() if self.root: self.set_root_indices() def _check_errors(self): """Check errors in node.""" if self.data is None: if self.first_child is None: raise RuntimeError("No data in this node and its children.") if self.compress is not None: raise RuntimeError("Data is required with compress matrix.") else: if self.compress is None: if self.data.shape != self.shape: raise RuntimeError( "Data shape is inconsistent with rows and columns." ) else: if self.compress.shape[0] != self.shape[0]: raise RuntimeError("Data shape is inconsistent with rows.") if self.data.shape[1] != self.shape[1]: raise RuntimeError("Data shape is inconsistent with columns.") def traverse_data_nodes(self): """Traverse all nodes with data.""" if self.first_child is not None: yield from self.first_child.traverse_data_nodes() if self.next_sibling is not None: yield from self.next_sibling.traverse_data_nodes() if self.data is not None: yield self def print_nodes(self, depth: int = 0): """Print all nodes.""" if depth == 0: header = "-" else: header = " " * (depth - 1) + "|--" print(header, self.shape, end=", ", flush=True) if self.data is not None: if self.compress is not None: print("data:", self.compress.shape, "@", self.data.shape, flush=True) else: print("data: True", flush=True) else: print("data: False", flush=True) if self.first_child is not None: self.first_child.print_nodes(depth=depth + 1) if self.next_sibling is not None: self.next_sibling.print_nodes(depth=depth) return self def set_root_indices( self, parent_rows: Optional[np.ndarray] = None, parent_col_begin: Optional[int] = None, ): """Set row and columns indices compatible with root node and full matrix.""" if parent_rows is not None: self.rows_root = parent_rows[self.rows] self.col_begin_root = self.col_begin + parent_col_begin self.col_end_root = self.col_end + parent_col_begin if self.root: self.root = False if self.first_child is not None: if parent_rows is None: self.first_child.set_root_indices(self.rows, self.col_begin) else: self.first_child.set_root_indices( parent_rows[self.rows], parent_col_begin + self.col_begin, ) if self.next_sibling is not None: self.next_sibling.set_root_indices(parent_rows, parent_col_begin) return self def decompress(self): """Decompress compressed data matrix.""" if self.compress is not None: return self.compress.dot(self.data) return self.data def recover(self): """Recover full block matrix.""" if not self.root: raise RuntimeError("Node must be root of tree.") full = np.zeros(self.shape, dtype="double") # type: ignore for b in self.traverse_data_nodes(): full[b.rows_root, b.col_begin_root : b.col_end_root] = b.decompress() return full def dot(self, mat: np.ndarray): """Calculate dot product block_mat @ mat.""" if not self.root: raise RuntimeError("Node must be root of tree.") if len(mat.shape) == 1: prod = np.zeros(self.shape[0]) elif len(mat.shape) == 2: prod = np.zeros((self.shape[0], mat.shape[1])) else: raise RuntimeError("Dimension of input numpy array must be one or two.") for b in self.traverse_data_nodes(): res = b.data @ mat[b.col_begin_root : b.col_end_root] if b.compress is not None: res = b.compress.dot(res) prod[b.rows_root] += res return prod def transpose_dot(self, mat: np.ndarray): """Calculate dot product block_mat.T @ mat.""" if not self.root: raise RuntimeError("Node must be root of tree.") if len(mat.shape) == 1: prod = np.zeros(self.shape[1]) elif len(mat.shape) == 2: prod = np.zeros((self.shape[1], mat.shape[1])) else: raise RuntimeError("Dimension of input numpy array must be one or two.") for b in self.traverse_data_nodes(): if b.compress is not None: res = b.data.T @ b.compress.transpose_dot(mat[b.rows_root]) else: res = b.data.T @ mat[b.rows_root] prod[b.col_begin_root : b.col_end_root] += res return prod def compress_matrix(self, mat: Union[csr_array, np.ndarray], use_mkl: bool = False): """Calculate block_mat.T @ mat @ block_mat. Block matrix must be eigenvectors and include their eigenvalues. """ if not self.root: raise RuntimeError("Node must be root of tree.") if is_sparse(mat): return self.compress_sparse_matrix(mat, use_mkl=use_mkl) return self.compress_dense_matrix(mat) def compress_dense_matrix(self, mat: np.ndarray): """Calculate block_mat.T @ mat @ block_mat for numpy array. Block matrix must be eigenvectors and include their eigenvalues. """ if not self.root: raise RuntimeError("Node must be root of tree.") if self.shape[1] < 10000: return self.recover().T @ mat @ self.recover() res = np.zeros((self.shape[1], self.shape[1])) for i, b1 in enumerate(self.traverse_data_nodes()): col_begin1, col_end1 = b1.col_begin_root, b1.col_end_root data1 = b1.decompress() for c, val in zip(range(col_begin1, col_end1), b1.eigvals): res[c, c] = val for j, b2 in enumerate(self.traverse_data_nodes()): if i != j: col_begin2, col_end2 = b2.col_begin_root, b2.col_end_root data2 = b2.decompress() prod = data1.T @ mat[np.ix_(b1.rows_root, b2.rows_root)] @ data2 res[col_begin1:col_end1, col_begin2:col_end2] = prod return res def compress_sparse_matrix(self, mat: csr_array, use_mkl: bool = False): """Calculate block_mat.T @ mat(csr) @ block_mat for csr_array. Block matrix must be eigenvectors and include their eigenvalues. """ if not self.root: raise RuntimeError("Node must be root of tree.") if mat.shape[0] < 30000: use_mkl = False res = np.zeros((self.shape[1], self.shape[1])) for i, b1 in enumerate(self.traverse_data_nodes()): col_begin1, col_end1 = b1.col_begin_root, b1.col_end_root data1 = b1.decompress() mat1 = mat[b1.rows_root] for c, val in zip(range(col_begin1, col_end1), b1.eigvals): res[c, c] = val for j, b2 in enumerate(self.traverse_data_nodes()): if i > j: col_begin2, col_end2 = b2.col_begin_root, b2.col_end_root data2 = b2.decompress() mat_slice = mat1[:, b2.rows_root] mat_slice_t = mat[np.ix_(b2.rows_root, b1.rows_root)].T mat_slice = 0.5 * (mat_slice + mat_slice_t) prod = dot_product_sparse(mat_slice, data2, use_mkl=use_mkl) prod = dot_product_sparse( data1.T, prod, use_mkl=use_mkl, dense=True ) res[col_begin1:col_end1, col_begin2:col_end2] = prod res[col_begin2:col_end2, col_begin1:col_end1] = prod.T return res def append_node( eigvecs: np.ndarray, next_sibling: BlockMatrixNode, rows: np.ndarray, col_begin, compress: Optional[BlockMatrixNode] = None, eigvals: Optional[np.ndarray] = None, ): """Add eigenvectors to block matrix node.""" if isinstance(eigvecs, BlockMatrixNode): block = eigvecs if block.shape[1] > 0: block.rows = rows block.col_begin = col_begin block.col_end = col_begin + block.shape[1] # type: ignore block.next_sibling = next_sibling block.eigvals = eigvals block.root = False next_sibling = block else: if eigvecs is not None and eigvecs.shape[1] > 0: col_end = col_begin + eigvecs.shape[1] # type: ignore block = BlockMatrixNode( rows=rows, col_begin=col_begin, col_end=col_end, data=eigvecs, next_sibling=next_sibling, compress=compress, eigvals=eigvals, ) next_sibling = block return next_sibling def root_block_matrix( shape: Optional[tuple] = None, data: Optional[np.ndarray] = None, first_child: Optional[BlockMatrixNode] = None, ): """Return root block matrix.""" if shape is None and data is None: raise RuntimeError("Shape or data is required.") if data is not None: shape = data.shape if shape[1] == 0: return None return BlockMatrixNode( rows=np.arange(shape[0]), col_begin=0, col_end=shape[1], first_child=first_child, data=data, root=True, ) def block_matrix_sandwich(bm1: BlockMatrixNode, bm2: BlockMatrixNode, mat: np.ndarray): """Calculate block1.T @ mat @ block2.""" if not bm1.root or not bm2.root: raise RuntimeError("Nodes must be root of tree.") if bm1.shape[1] < 20000 and bm2.shape[1] < 20000: return bm1.recover().T @ mat @ bm2.recover() res = np.zeros((bm1.shape[1], bm2.shape[1])) for b1 in bm1.traverse_data_nodes(): col_begin1, col_end1 = b1.col_begin_root, b1.col_end_root data1 = b1.decompress() for b2 in bm2.traverse_data_nodes(): col_begin2, col_end2 = b2.col_begin_root, b2.col_end_root data2 = b2.decompress() prod = data1.T @ mat[np.ix_(b1.rows_root, b2.rows_root)] @ data2 res[col_begin1:col_end1, col_begin2:col_end2] += prod return res symfc-1.6.0/src/symfc/utils/permutation_tools.py000066400000000000000000000067261511276756400221010ustar00rootroot00000000000000"""Permutation utility functions.""" from typing import Optional import numpy as np import scipy from scipy.sparse import csr_array from symfc.utils.cutoff_tools import FCCutoff def get_entire_combinations(n: int, r: int): """Return numpy array of combinations. combinations = np.array( list(itertools.combinations(range(n), r)), dtype=int ) """ combs = np.ones((r, n - r + 1), dtype=int) combs[0] = np.arange(n - r + 1) for j in range(1, r): reps = (n - r + j) - combs[j - 1] combs = np.repeat(combs, reps, axis=1) ind = np.add.accumulate(reps) combs[j, ind[:-1]] = 1 - reps[1:] combs[j, 0] = j combs[j] = np.add.accumulate(combs[j]) return combs.T def get_combinations( natom: int, order: int, fc_cutoff: Optional[FCCutoff] = None, indep_atoms: Optional[np.ndarray] = None, ): """Return numpy array of FC index combinations.""" if fc_cutoff is None: combinations = get_entire_combinations(3 * natom, order) else: if order == 2: combinations = fc_cutoff.combinations2() elif order == 3: """Combinations can be divided using fc_cut.combiations3(i).""" combinations = fc_cutoff.combinations3_all() elif order == 4: combinations = fc_cutoff.combinations4_all() else: raise NotImplementedError( "Combinations are implemented only for 2 <= order <= 4." ) if indep_atoms is not None: nonzero = np.zeros(combinations.shape[0], dtype=bool) atom_indices = combinations[:, 0] // 3 for i in indep_atoms: nonzero[atom_indices == i] = True combinations = combinations[nonzero] return combinations def _eliminate_zero_elements( perm_decompr_idx: np.ndarray, nonzero: np.ndarray ) -> np.ndarray: """Eliminate zero elements and reindex orbit indexes.""" size_full = len(perm_decompr_idx) if not np.all(nonzero): perm_decompr_idx = perm_decompr_idx[nonzero] nonzero_map = np.ones(size_full, dtype="int") * -1 nonzero_map[nonzero] = np.arange(len(perm_decompr_idx)) perm_decompr_idx = nonzero_map[perm_decompr_idx] return perm_decompr_idx def construct_basis_from_perm_decompr_indices( perm_decompr_idx: np.ndarray, verbose: bool = False ): """Transform perm_decompr_idx into basis matrix. Parameters ---------- perm_decompr_idx: Decompression indices of lattice translation basis using permutations. Return ------ c_pt: Compressed basis matrix for permutations and lattice translations. c_pt = eigh(C_trans.T @ C_perm @ C_perm.T @ C_trans) """ if verbose: print("Construct permutation basis matrix.", flush=True) size_full = len(perm_decompr_idx) nonzero = perm_decompr_idx != -1 perm_decompr_idx = _eliminate_zero_elements(perm_decompr_idx, nonzero) size1 = len(perm_decompr_idx) perm_lat_trans_graph = csr_array( (np.ones(size1, dtype=bool), (np.arange(size1), perm_decompr_idx)), shape=(size1, size1), dtype=bool, ) n_col, cols = scipy.sparse.csgraph.connected_components(perm_lat_trans_graph) key, cnt = np.unique(cols, return_counts=True) values = np.reciprocal(np.sqrt(cnt)) rows = np.where(nonzero)[0] c_pt = csr_array( (values[cols], (rows, cols)), shape=(size_full, n_col), dtype="double", ) return c_pt symfc-1.6.0/src/symfc/utils/permutation_tools_O2.py000066400000000000000000000112021511276756400224220ustar00rootroot00000000000000"""Permutation utility functions for 2nd order force constants.""" from typing import Optional, Union import numpy as np from scipy.sparse import csr_array from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.permutation_tools import ( construct_basis_from_perm_decompr_indices, get_combinations, ) from symfc.utils.solver_funcs import get_batch_slice from symfc.utils.utils import get_indep_atoms_by_lat_trans from symfc.utils.utils_O2 import _get_atomic_lat_trans_decompr_indices def _N3N3_to_NNand33(combs: np.ndarray, N: int) -> tuple[np.ndarray, np.ndarray]: """Transform index order.""" vecNN, vec33 = np.divmod(combs[:, 0], 3) vecNN *= N vec33 *= 3 div, mod = np.divmod(combs[:, 1], 3) vecNN += div vec33 += mod return vecNN, vec33 def compr_permutation_lat_trans_O2( trans_perms: np.ndarray, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, n_batch: Optional[int] = None, verbose: bool = False, ) -> csr_array: r"""Build a compression matrix for permutation rules compressed by C_trans. This calculates C_(trans,perm) without allocating C_trans and C_perm. Batch calculations are used to reduce memory allocation. Parameters ---------- trans_perms : ndarray Permutation of atomic indices by lattice translational symmetry. dtype='intc'. shape=(n_l, N), where n_l and N are the numbers of lattce points and atoms in supercell. fc_cutoff : FCCutoff class object. Default is None. Return ------ c_pt: Compressed basis matrix for permutations and lattice translations. c_pt = eigh(C_trans.T @ C_perm @ C_perm.T @ C_trans) shape: (NN33//n_lp, n_basis_pt). n_basis_pt denotes the size of basis for permutations and lattice translations. Algorithm --------- 1. Calculate combinations {(i, a), (j, b)}. 2. Apply permutations to these combinations and calculate the orbits of {(i, a), (j, b)} related to each other by permutations. 3. Calculate the lattice translation basis indices of the orbits, and represent the lattice translation indices related to each other by a representative in perm_decompr_idx. """ n_lp, natom = trans_perms.shape NN9 = natom**2 * 9 if atomic_decompr_idx is None: atomic_decompr_idx = _get_atomic_lat_trans_decompr_indices(trans_perms) perm_decompr_idx = np.ones(NN9 // n_lp, dtype="int") * -1 indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) # order = 1 combinations = np.array([[i, i] for i in range(3 * natom)], dtype=int) perms = [[0, 0]] perm_decompr_idx = _update_perm_decompr_indices( combinations, perms, atomic_decompr_idx, trans_perms, perm_decompr_idx, n_perms_group=1, n_batch=1, verbose=verbose, ) # order = 2 combinations = get_combinations( natom, order=2, fc_cutoff=fc_cutoff, indep_atoms=indep_atoms ) perms = [[0, 1], [1, 0]] perm_decompr_idx = _update_perm_decompr_indices( combinations, perms, atomic_decompr_idx, trans_perms, perm_decompr_idx, n_perms_group=1, n_batch=1, verbose=verbose, ) c_pt = construct_basis_from_perm_decompr_indices(perm_decompr_idx, verbose=verbose) return c_pt def _update_perm_decompr_indices( combinations: np.ndarray, permutations: Union[np.ndarray, list], atomic_decompr_idx: np.ndarray, trans_perms: np.ndarray, perm_decompr_idx: np.ndarray, n_perms_group: int = 1, n_batch: int = 1, verbose: bool = False, ) -> np.ndarray: """Apply permutations to lattice translation basis. Return ------ perm_decompr_idx: Updated decompression indices of lattice translation basis using permutations. """ n_lp, natom = trans_perms.shape n_comb = combinations.shape[0] n_perms = len(permutations) n_perms_sym = n_perms // n_perms_group for begin, end in zip(*get_batch_slice(n_comb, n_comb // n_batch)): if verbose: print("Permutation basis:", str(end) + "/" + str(n_comb), flush=True) combs_perm = combinations[begin:end][:, permutations].reshape((-1, 2)) combs_perm, combs33 = _N3N3_to_NNand33(combs_perm, natom) decompr_idx_combs_perm = atomic_decompr_idx[combs_perm] * 9 + combs33 decompr_idx_combs_perm = decompr_idx_combs_perm.reshape(-1, n_perms_sym) for orbit_components in decompr_idx_combs_perm.T: perm_decompr_idx[orbit_components] = decompr_idx_combs_perm[:, 0] return perm_decompr_idx symfc-1.6.0/src/symfc/utils/permutation_tools_O3.py000066400000000000000000000127251511276756400224360ustar00rootroot00000000000000"""Permutation utility functions for 3rd order force constants.""" from typing import Optional, Union import numpy as np from scipy.sparse import csr_array from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.permutation_tools import ( construct_basis_from_perm_decompr_indices, get_combinations, ) from symfc.utils.solver_funcs import get_batch_slice from symfc.utils.utils import get_indep_atoms_by_lat_trans from symfc.utils.utils_O3 import get_atomic_lat_trans_decompr_indices_O3 def _N3N3N3_to_NNNand333(combs: np.ndarray, N: int) -> tuple[np.ndarray, np.ndarray]: """Transform index order.""" vecNNN, vec333 = np.divmod(combs[:, 0], 3) vecNNN *= N**2 vec333 *= 9 div, mod = np.divmod(combs[:, 1], 3) vecNNN += div * N vec333 += mod * 3 div, mod = np.divmod(combs[:, 2], 3) vecNNN += div vec333 += mod return vecNNN, vec333 def compr_permutation_lat_trans_O3( trans_perms: np.ndarray, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, n_batch: Optional[int] = None, verbose: bool = False, ) -> csr_array: r"""Build a compression matrix for permutation rules compressed by C_trans. This calculates C_(trans,perm) without allocating C_trans and C_perm. Batch calculations are used to reduce memory allocation. Parameters ---------- trans_perms : ndarray Permutation of atomic indices by lattice translational symmetry. dtype='intc'. shape=(n_l, N), where n_l and N are the numbers of lattce points and atoms in supercell. fc_cutoff : FCCutoff class object. Default is None. Return ------ c_pt: Compressed basis matrix for permutations and lattice translations. c_pt = eigh(C_trans.T @ C_perm @ C_perm.T @ C_trans) shape: (NNN333//n_lp, n_basis_pt). n_basis_pt denotes the size of basis for permutations and lattice translations. Algorithm --------- 1. Calculate combinations {(i, a), (j, b), (k, c)}. 2. Apply permutations to these combinations and calculate the orbits of {(i, a), (j, b), (k, c)} related to each other by permutations. 3. Calculate the lattice translation basis indices of the orbits, and represent the lattice translation indices related to each other by a representative in perm_decompr_idx. """ n_lp, natom = trans_perms.shape NNN27 = natom**3 * 27 if atomic_decompr_idx is None: atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O3(trans_perms) if n_batch is None: n_batch = 1 if natom <= 128 else int(round((natom / 128) ** 2)) perm_decompr_idx = np.ones(NNN27 // n_lp, dtype="int") * -1 indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) # order = 1 combinations = np.array([[i, i, i] for i in range(3 * natom)], dtype=int) perms = [[0, 0, 0]] perm_decompr_idx = _update_perm_decompr_indices( combinations, perms, atomic_decompr_idx, trans_perms, perm_decompr_idx, n_perms_group=1, n_batch=1, verbose=verbose, ) # order = 2 combinations = get_combinations( natom, order=2, fc_cutoff=fc_cutoff, indep_atoms=indep_atoms ) perms = [ [0, 0, 1], [0, 1, 0], [1, 0, 0], [0, 1, 1], [1, 0, 1], [1, 1, 0], ] perm_decompr_idx = _update_perm_decompr_indices( combinations, perms, atomic_decompr_idx, trans_perms, perm_decompr_idx, n_perms_group=2, n_batch=1, verbose=verbose, ) # order = 3 combinations = get_combinations( natom, order=3, fc_cutoff=fc_cutoff, indep_atoms=indep_atoms ) perms = [ [0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0], ] perm_decompr_idx = _update_perm_decompr_indices( combinations, perms, atomic_decompr_idx, trans_perms, perm_decompr_idx, n_perms_group=1, n_batch=n_batch, verbose=verbose, ) c_pt = construct_basis_from_perm_decompr_indices(perm_decompr_idx, verbose=verbose) return c_pt def _update_perm_decompr_indices( combinations: np.ndarray, permutations: Union[np.ndarray, list], atomic_decompr_idx: np.ndarray, trans_perms: np.ndarray, perm_decompr_idx: np.ndarray, n_perms_group: int = 1, n_batch: int = 1, verbose: bool = False, ) -> np.ndarray: """Apply permutations to lattice translation basis. Return ------ perm_decompr_idx: Updated decompression indices of lattice translation basis using permutations. """ n_lp, natom = trans_perms.shape n_comb = combinations.shape[0] n_perms = len(permutations) n_perms_sym = n_perms // n_perms_group for begin, end in zip(*get_batch_slice(n_comb, n_comb // n_batch)): if verbose: print("Permutation basis:", str(end) + "/" + str(n_comb), flush=True) combs_perm = combinations[begin:end][:, permutations].reshape((-1, 3)) combs_perm, combs333 = _N3N3N3_to_NNNand333(combs_perm, natom) decompr_idx_combs_perm = atomic_decompr_idx[combs_perm] * 27 + combs333 decompr_idx_combs_perm = decompr_idx_combs_perm.reshape(-1, n_perms_sym) for orbit_components in decompr_idx_combs_perm.T: perm_decompr_idx[orbit_components] = decompr_idx_combs_perm[:, 0] return perm_decompr_idx symfc-1.6.0/src/symfc/utils/permutation_tools_O4.py000066400000000000000000000155351511276756400224410ustar00rootroot00000000000000"""Permutation utility functions for 4th order force constants.""" import itertools from typing import Optional, Union import numpy as np from scipy.sparse import csr_array from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.permutation_tools import ( construct_basis_from_perm_decompr_indices, get_combinations, ) from symfc.utils.solver_funcs import get_batch_slice from symfc.utils.utils import get_indep_atoms_by_lat_trans from symfc.utils.utils_O4 import get_atomic_lat_trans_decompr_indices_O4 def _N3N3N3N3_to_NNNNand3333( combs: np.ndarray, N: int ) -> tuple[np.ndarray, np.ndarray]: """Transform index order.""" vecNNNN, vec3333 = np.divmod(combs[:, 0], 3) vecNNNN *= N**3 vec3333 *= 27 div, mod = np.divmod(combs[:, 1], 3) vecNNNN += div * N**2 vec3333 += mod * 9 div, mod = np.divmod(combs[:, 2], 3) vecNNNN += div * N vec3333 += mod * 3 div, mod = np.divmod(combs[:, 3], 3) vecNNNN += div vec3333 += mod return vecNNNN, vec3333 def compr_permutation_lat_trans_O4( trans_perms: np.ndarray, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, n_batch: Optional[int] = None, verbose: bool = False, ) -> csr_array: r"""Build a compression matrix for permutation rules compressed by C_trans. This calculates C_(trans,perm) without allocating C_trans and C_perm. Batch calculations are used to reduce memory allocation. Parameters ---------- trans_perms : ndarray Permutation of atomic indices by lattice translational symmetry. dtype='intc'. shape=(n_l, N), where n_l and N are the numbers of lattce points and atoms in supercell. fc_cutoff : FCCutoff class object. Default is None. Return ------ c_pt: Compressed basis matrix for permutations and lattice translations. c_pt = eigh(C_trans.T @ C_perm @ C_perm.T @ C_trans) shape: (NNNN3333//n_lp, n_basis_pt). n_basis_pt denotes the size of basis for permutations and lattice translations. Algorithm --------- 1. Calculate combinations {(i, a), (j, b), (k, c)}. 2. Apply permutations to these combinations and calculate the orbits of {(i, a), (j, b), (k, c)} related to each other by permutations. 3. Calculate the lattice translation basis indices of the orbits, and represent the lattice translation indices related to each other by a representative in perm_decompr_idx. """ n_lp, natom = trans_perms.shape NNNN81 = natom**4 * 81 if atomic_decompr_idx is None: atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O4(trans_perms) perm_decompr_idx = np.ones(NNNN81 // n_lp, dtype="int") * -1 indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) # order = 1 combinations = np.array([[i, i, i] for i in range(3 * natom)], dtype=int) perms = [[0, 0, 0, 0]] perm_decompr_idx = _update_perm_decompr_indices( combinations, perms, atomic_decompr_idx, trans_perms, perm_decompr_idx, n_perms_group=1, n_batch=1, verbose=verbose, ) # order = 2 combinations = get_combinations( natom, order=2, fc_cutoff=fc_cutoff, indep_atoms=indep_atoms ) perms = [ [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [1, 1, 1, 0], [1, 1, 0, 1], [1, 0, 1, 1], [0, 1, 1, 1], ] perm_decompr_idx = _update_perm_decompr_indices( combinations, perms, atomic_decompr_idx, trans_perms, perm_decompr_idx, n_perms_group=2, n_batch=1, verbose=verbose, ) # order = 3 if n_batch is None: n_batch3 = 1 if natom <= 128 else int(round((natom / 128) ** 2)) combinations = get_combinations( natom, order=3, fc_cutoff=fc_cutoff, indep_atoms=indep_atoms ) perms = [ [0, 0, 1, 2], [0, 0, 2, 1], [0, 1, 0, 2], [0, 2, 0, 1], [0, 1, 2, 0], [0, 2, 1, 0], [1, 0, 0, 2], [2, 0, 0, 1], [1, 0, 2, 0], [2, 0, 1, 0], [1, 2, 0, 0], [2, 1, 0, 0], [1, 1, 0, 2], [1, 1, 2, 0], [1, 0, 1, 2], [1, 2, 1, 0], [1, 0, 2, 1], [1, 2, 0, 1], [0, 1, 1, 2], [2, 1, 1, 0], [0, 1, 2, 1], [2, 1, 0, 1], [0, 2, 1, 1], [2, 0, 1, 1], [2, 2, 1, 0], [2, 2, 0, 1], [2, 1, 2, 0], [2, 0, 2, 1], [2, 1, 0, 2], [2, 0, 1, 2], [1, 2, 2, 0], [0, 2, 2, 1], [1, 2, 0, 2], [0, 2, 1, 2], [1, 0, 2, 2], [0, 1, 2, 2], ] perm_decompr_idx = _update_perm_decompr_indices( combinations, perms, atomic_decompr_idx, trans_perms, perm_decompr_idx, n_perms_group=3, n_batch=n_batch3, verbose=verbose, ) # order = 4 if n_batch is None: n_batch4 = 1 if natom <= 16 else int(round((natom / 16) ** 2)) combinations = get_combinations( natom, order=4, fc_cutoff=fc_cutoff, indep_atoms=indep_atoms ) perms = np.array(list(itertools.permutations(range(4)))) perm_decompr_idx = _update_perm_decompr_indices( combinations, perms, atomic_decompr_idx, trans_perms, perm_decompr_idx, n_perms_group=1, n_batch=n_batch4, verbose=verbose, ) c_pt = construct_basis_from_perm_decompr_indices(perm_decompr_idx, verbose=verbose) return c_pt def _update_perm_decompr_indices( combinations: np.ndarray, permutations: Union[np.ndarray, list], atomic_decompr_idx: np.ndarray, trans_perms: np.ndarray, perm_decompr_idx: np.ndarray, n_perms_group: int = 1, n_batch: int = 1, verbose: bool = False, ) -> np.ndarray: """Apply permutations to lattice translation basis. Return ------ perm_decompr_idx: Updated decompression indices of lattice translation basis using permutations. """ n_lp, natom = trans_perms.shape n_comb = combinations.shape[0] n_perms = len(permutations) n_perms_sym = n_perms // n_perms_group for begin, end in zip(*get_batch_slice(n_comb, n_comb // n_batch)): if verbose: print("Permutation basis:", str(end) + "/" + str(n_comb), flush=True) combs_perm = combinations[begin:end][:, permutations].reshape((-1, 4)) combs_perm, combs3333 = _N3N3N3N3_to_NNNNand3333(combs_perm, natom) decompr_idx_combs_perm = atomic_decompr_idx[combs_perm] * 81 + combs3333 decompr_idx_combs_perm = decompr_idx_combs_perm.reshape(-1, n_perms_sym) for orbit_components in decompr_idx_combs_perm.T: perm_decompr_idx[orbit_components] = decompr_idx_combs_perm[:, 0] return perm_decompr_idx symfc-1.6.0/src/symfc/utils/rotation_dev/000077500000000000000000000000001511276756400204225ustar00rootroot00000000000000symfc-1.6.0/src/symfc/utils/rotation_dev/run_rotation_tools_O2.py000066400000000000000000000260611511276756400253040ustar00rootroot00000000000000"""Matrix utility functions for setting rotational invariants.""" # from typing import Optional # import itertools import numpy as np import scipy from pypolymlp.core.interface_vasp import Poscar from pypolymlp.utils.structure_utils import supercell_diagonal from scipy.sparse import csr_array from symfc import Symfc from symfc.basis_sets.basis_sets_O2 import FCBasisSetO2 from symfc.spg_reps import SpgRepsBase from symfc.utils.eig_tools import eigsh_projector, eigsh_projector_sumrule from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O2 import ( get_lat_trans_compr_matrix, get_lat_trans_decompr_indices, ) try: from symfc.utils.eig_tools import dot_product_sparse except ImportError: pass import copy def _orthogonalize_constraints(positions_cartesian: np.ndarray): """Orthogonalize constraints derived from rotational invariance.""" natom = positions_cartesian.shape[0] N3 = 3 * natom C = np.zeros((N3, 3), dtype="double") C[1::3, 0] = -positions_cartesian[:, 0] C[2::3, 2] = positions_cartesian[:, 0] C[2::3, 1] = -positions_cartesian[:, 1] C[0::3, 0] = positions_cartesian[:, 1] C[0::3, 2] = -positions_cartesian[:, 2] C[1::3, 1] = positions_cartesian[:, 2] # # U, S, V = np.linalg.svd(C, full_matrices=False) # # nonzero = np.abs(S) > 1e-15 # # C = U[:,nonzero] # # C[np.abs(C) < 1e-15] = 0.0 # # print(S) proj_C = C @ np.linalg.inv(C.T @ C) @ C.T eigvals, eigvecs = np.linalg.eigh(proj_C) nonzero = np.isclose(eigvals, 1.0) C = eigvecs[:, nonzero] return C def complement_sum_rule(natom, decompr_idx=None, n_lp=None): """Construct sum rule.""" N33 = natom * 9 NN33 = natom**2 * 9 row = [] for j, alpha, beta in itertools.product(range(natom), range(3), range(3)): i = np.arange(natom) * N33 const_add = j * 9 + alpha * 3 + beta row.extend(i + const_add) col = np.repeat(np.arange(N33), natom) data = np.ones(len(col)) / np.sqrt(natom) if decompr_idx is not None: data /= np.sqrt(n_lp) c_sum = csr_array( (data, (decompr_idx[row], col)), shape=(NN33 // n_lp, N33), dtype="double", ) else: c_sum = csr_array( (data, (row, col)), shape=(NN33, N33), dtype="double", ) return c_sum def complementary_compr_projector_rot_sum_rules_O2( supercell: SymfcAtoms, trans_perms: np.ndarray, n_a_compress_mat: np.ndarray, use_mkl: bool = False, ) -> csr_array: """Test function for setting rotational invariants.""" n_lp, natom = trans_perms.shape # TODO: decompr_idx -> atomic_decompr_idx decompr_idx = get_lat_trans_decompr_indices(trans_perms) indep_atoms = list(range(natom)) positions_cartesian = (supercell.scaled_positions) @ supercell.cell positions_cartesian[:, 0] -= np.average(positions_cartesian[:, 0]) positions_cartesian[:, 1] -= np.average(positions_cartesian[:, 1]) positions_cartesian[:, 2] -= np.average(positions_cartesian[:, 2]) C2 = _orthogonalize_constraints(positions_cartesian) N3 = natom * 3 N33 = N3 * 3 NN33 = N3 * N3 n_expand = len(indep_atoms) * 3 data = np.tile(C2.T.reshape(-1), n_expand) col = np.repeat(np.arange(n_expand * 3), N3) row = [] ids_ialpha = (np.arange(natom) * N33)[:, None] + (np.arange(3) * 3)[None, :] ids_ialpha = ids_ialpha.reshape(-1) for j, beta in itertools.product(indep_atoms, range(3)): ids = ids_ialpha + (j * 9 + beta) row.extend(np.tile(ids, 3)) data /= np.sqrt(n_lp) c_rot_cmplt = csr_array( (data, (decompr_idx[row], col)), shape=(NN33 // n_lp, n_expand * 3), dtype="double", ) proj = scipy.sparse.identity(NN33 // n_lp) - c_rot_cmplt @ c_rot_cmplt.T c_rot = eigsh_projector(proj, verbose=True) c_cmplt = n_a_compress_mat.T @ c_rot proj = c_cmplt @ c_cmplt.T return proj def complement_rotational_sum_rule(supercell, indep_atoms, decompr_idx=None, n_lp=None): """Get complement_rotational_sum_rule.""" positions_cartesian = (supercell.scaled_positions) @ supercell.cell positions_cartesian[:, 0] -= np.average(positions_cartesian[:, 0]) positions_cartesian[:, 1] -= np.average(positions_cartesian[:, 1]) positions_cartesian[:, 2] -= np.average(positions_cartesian[:, 2]) C2 = _orthogonalize_constraints(positions_cartesian) natom = positions_cartesian.shape[0] N3 = natom * 3 N33 = N3 * 3 NN33 = N3 * N3 n_expand = len(indep_atoms) * 3 data = np.tile(C2.T.reshape(-1), n_expand) col = np.repeat(np.arange(n_expand * 3), N3) row = [] ids_ialpha = (np.arange(natom) * N33)[:, None] + (np.arange(3) * 3)[None, :] ids_ialpha = ids_ialpha.reshape(-1) for j, beta in itertools.product(indep_atoms, range(3)): ids = ids_ialpha + (j * 9 + beta) row.extend(np.tile(ids, 3)) if decompr_idx is None: c_rot = csr_array( (data, (row, col)), shape=(NN33, n_expand * 3), dtype="double", ) else: data /= np.sqrt(n_lp) c_rot = csr_array( (data, (decompr_idx[row], col)), shape=(NN33 // n_lp, n_expand * 3), dtype="double", ) return c_rot def complementary_compr_projector_rot_O2_test( supercell: SymfcAtoms, trans_perms: np.ndarray, basis_set_fc2: FCBasisSetO2, indep_atoms=None, use_mkl: bool = False, ) -> csr_array: """Test function for setting rotational invariants.""" n_lp, natom = trans_perms.shape N3 = natom * 3 NN33 = N3 * N3 decompr_idx = get_lat_trans_decompr_indices(trans_perms) c_trans = get_lat_trans_compr_matrix(decompr_idx, natom, n_lp) # indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) # indep_atoms = [indep_atoms[0]] if indep_atoms is None: indep_atoms = list(range(natom)) # indep_atoms = [2] # indep_atoms = [0,1,2,3,4,5,6,7] c_rot = complement_rotational_sum_rule(supercell, indep_atoms) proj_rot = c_rot @ c_rot.T c_sum = complement_sum_rule(natom) proj_sum = c_sum @ c_sum.T print("--------------") print("Only lattice translation") proj = dot_product_sparse(c_trans, c_trans.T, use_mkl=use_mkl) print(proj.shape) c = eigsh_projector(proj, verbose=True) print(c.shape) print("--------------") print("Only translational sum rule") proj = scipy.sparse.identity(NN33) - proj_sum c = eigsh_projector(proj, verbose=True) print(c.shape) c = c_trans.T @ c print(c.shape) proj = dot_product_sparse(c, c.T, use_mkl=use_mkl) print(proj.shape) c = eigsh_projector(proj, verbose=True) print(c.shape) c_sum = complement_sum_rule(natom, decompr_idx=decompr_idx, n_lp=n_lp) proj = scipy.sparse.identity(NN33 // n_lp) - c_sum @ c_sum.T c = eigsh_projector(proj, verbose=True) print(c.shape) # print("--------------") # print("Only rotational sum rules (indep_atoms)") # # c_rot1 = complement_rotational_sum_rule( # supercell, [0], decompr_idx=decompr_idx, n_lp=n_lp # ) # # c_rot2 = complement_rotational_sum_rule( # supercell, [2], decompr_idx=decompr_idx, n_lp=n_lp # ) # #print(c_rot1 - c_rot2) print("--------------") print("Only rotational sum rules") c_rot = complement_rotational_sum_rule( supercell, indep_atoms, decompr_idx=decompr_idx, n_lp=n_lp ) proj = scipy.sparse.identity(NN33) - proj_rot c = eigsh_projector(proj, verbose=True) print(c.shape) c = c_trans.T @ c proj = dot_product_sparse(c, c.T, use_mkl=use_mkl) print(proj.shape) c = eigsh_projector(proj, verbose=True) print(c.shape) proj = scipy.sparse.identity(NN33 // n_lp) - c_rot @ c_rot.T proj_return = copy.copy(proj) c = eigsh_projector(proj, verbose=True) print(c.shape) vals, vecs = np.linalg.eigh(proj.toarray()) print(vals) print("--------------") print("Only rotational sum rules (cmplt)") c = eigsh_projector(proj_rot, verbose=True) print(c.shape) print("--------------") print("Only translational and rotational sum rules") proj = scipy.sparse.identity(NN33) - proj_sum - proj_rot c = eigsh_projector(proj, verbose=True) print(c.shape) c = c_trans.T @ c proj = dot_product_sparse(c, c.T, use_mkl=use_mkl) print(proj.shape) c = eigsh_projector(proj, verbose=True) print(c.shape) proj = scipy.sparse.identity(NN33 // n_lp) - c_rot @ c_rot.T - c_sum @ c_sum.T c = eigsh_projector(proj, verbose=True) print(c.shape) print("--------------") """ Compressed projector I - P^(c) P^(c) = n_a_compress_mat.T @ C_trans.T @ C_sum^(c) @ C_sum^(c).T @ C_trans @ n_a_compress_mat """ proj = scipy.sparse.identity(NN33 // n_lp) - c_rot @ c_rot.T c = eigsh_projector(proj, verbose=True) print(c.shape) n_a_compress_mat = np.sqrt(n_lp) * basis_set_fc2.compact_compression_matrix print(n_a_compress_mat.shape) # compress_mat = n_a_compress_mat @ basis_set_fc2.basis_set compress_mat = n_a_compress_mat print(compress_mat.shape) c = compress_mat.T @ c print(c.shape) proj = csr_array(c @ c.T) eigvecs = eigsh_projector(proj) print(eigvecs.shape) vals, vecs = np.linalg.eigh(proj.toarray()) print(vals) return proj_return if __name__ == "__main__": import argparse import signal import time signal.signal(signal.SIGINT, signal.SIG_DFL) parser = argparse.ArgumentParser() parser.add_argument("--poscar", type=str, default=None, help="poscar") parser.add_argument( "--supercell", nargs=3, type=int, default=None, help="Supercell size (diagonal components)", ) args = parser.parse_args() unitcell = Poscar(args.poscar).structure supercell = supercell_diagonal(unitcell, args.supercell) supercell = SymfcAtoms( numbers=supercell.types, cell=supercell.axis.T, scaled_positions=supercell.positions.T, ) spg_reps = SpgRepsBase(supercell) trans_perms = spg_reps.translation_permutations n_lp, natom = trans_perms.shape symfc = Symfc(supercell, use_mkl=True, log_level=1).compute_basis_set(2) basis_set_fc2 = symfc.basis_set[2] n_a_compress_mat = np.sqrt(n_lp) * basis_set_fc2.compact_compression_matrix t1 = time.time() proj = complementary_compr_projector_rot_sum_rules_O2( supercell, trans_perms, n_a_compress_mat, use_mkl=True, ) # proj = scipy.sparse.identity(proj.shape[0]) - proj eigvecs = eigsh_projector_sumrule(proj, verbose=True) t2 = time.time() print("Elapsed time:", t2 - t1) print("Number of eigenvectors:", eigvecs.shape[1]) proj1 = complementary_compr_projector_rot_O2_test( supercell, trans_perms, basis_set_fc2, indep_atoms=[2], ) proj2 = complementary_compr_projector_rot_O2_test( supercell, trans_perms, basis_set_fc2, indep_atoms=[1, 6], ) # print(proj2 - proj1) symfc-1.6.0/src/symfc/utils/rotation_dev/run_rotation_tools_O3.py000066400000000000000000000233331511276756400253040ustar00rootroot00000000000000"""Matrix utility functions for setting rotational invariants.""" # from typing import Optional # import itertools import numpy as np import scipy from pypolymlp.core.interface_vasp import Poscar from pypolymlp.utils.structure_utils import supercell_diagonal from scipy.sparse import csr_array, vstack from symfc import Symfc from symfc.basis_sets import FCBasisSetO2, FCBasisSetO3 from symfc.spg_reps import SpgRepsBase from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O2 import ( get_lat_trans_compr_matrix_O2, get_lat_trans_decompr_indices, ) from symfc.utils.utils_O3 import ( get_lat_trans_compr_matrix_O3, get_lat_trans_decompr_indices_O3, ) # from symfc.utils.solver_funcs import get_batch_slice # from symfc.utils.cutoff_tools import FCCutoff # from symfc.utils.utils_O3 import get_atomic_lat_trans_decompr_indices_O3 try: from symfc.utils.eig_tools import dot_product_sparse except ImportError: pass def delta(a: int, b: int): """Delta function.""" if a == b: return 1 return 0 def _orthogonalize_constraints(positions_cartesian: np.ndarray): """Orthogonalize constraints derived from rotational invariance.""" natom = positions_cartesian.shape[0] N3 = 3 * natom C = np.zeros((N3, 3), dtype="double") C[1::3, 0] = -positions_cartesian[:, 0] C[2::3, 2] = positions_cartesian[:, 0] C[2::3, 1] = -positions_cartesian[:, 1] C[0::3, 0] = positions_cartesian[:, 1] C[0::3, 2] = -positions_cartesian[:, 2] C[1::3, 1] = positions_cartesian[:, 2] C23 = np.zeros((9 + N3 * 9, 27), dtype="double") col_begin = 0 for alpha, beta in itertools.product(range(3), range(3)): col_end = col_begin + 3 row = beta add = np.array([delta(alpha, 1), 0, -delta(alpha, 2)]) C23[row, col_begin:col_end] += add row = alpha * 3 add = np.array([delta(beta, 1), 0, -delta(beta, 2)]) C23[row, col_begin:col_end] += add row = 3 + beta add = np.array([-delta(alpha, 0), delta(alpha, 2), 0]) C23[row, col_begin:col_end] += add row = alpha * 3 + 1 add = np.array([-delta(beta, 0), delta(beta, 2), 0]) C23[row, col_begin:col_end] += add row = 6 + beta add = np.array([0, -delta(alpha, 1), delta(alpha, 0)]) C23[row, col_begin:col_end] += add row = alpha * 3 + 2 add = np.array([0, -delta(beta, 1), delta(beta, 0)]) C23[row, col_begin:col_end] += add col_begin = col_end k_ids = np.repeat(np.arange(natom), 3) * 27 gamma_ids = np.tile(np.arange(3), natom) row_const = k_ids + gamma_ids + 9 col_begin = 0 for alpha, beta in itertools.product(range(3), range(3)): row = row_const + (alpha * 9 + beta * 3) col_end = col_begin + 3 C23[row, col_begin:col_end] = C col_begin = col_end U, S, V = np.linalg.svd(C23, full_matrices=False) nonzero = np.abs(S) > 1e-15 C23 = U[:, nonzero] print(S) # assert np.linalg.matrix_rank(C23) == 27 # assert np.allclose(C23.T @ C23, np.eye(27)) return C23 def _get_complement_matrix(mat, natom, order=2, n_lp=None, decompr_idx=None): """Get complementary matrix.""" NN = natom**2 NN33 = 9 * NN N333 = 27 * natom NN333 = N333 * natom NNN333 = NN333 * natom if order == 2: n_row = 9 size1 = NN33 elif order == 3: n_row = N333 size1 = NNN333 n_col = mat.shape[1] data = np.tile(mat.T.reshape(-1), NN) col = np.repeat(np.arange(NN * n_col), n_row) row = [] for i, j in itertools.product(range(natom), range(natom)): # id must be examined. begin = i * n_row * natom + j * n_row end = begin + n_row row.extend(np.tile(np.arange(begin, end), n_col)) nonzero = np.abs(data) > 1e-15 row = np.array(row)[nonzero] col = np.array(col)[nonzero] data = data[nonzero] if decompr_idx is None: return csr_array((data, (row, col)), shape=(size1, NN * n_col), dtype="double") size_row = decompr_idx.shape[0] // n_lp return csr_array( (data / np.sqrt(n_lp), (decompr_idx[row], col)), shape=(size_row, NN * n_col), dtype="double", ) def complementary_compr_projector_rot( supercell: SymfcAtoms, trans_perms: np.ndarray, basis_set_fc2: FCBasisSetO2, basis_set_fc3: FCBasisSetO3, use_mkl: bool = False, ) -> csr_array: """Test function for setting rotational invariants.""" n_lp, natom = trans_perms.shape positions_cartesian = (supercell.scaled_positions) @ supercell.cell C23 = _orthogonalize_constraints(positions_cartesian) decompr_idx = get_lat_trans_decompr_indices(trans_perms) c_rot_fc2 = _get_complement_matrix( C23[:9], natom, order=2, decompr_idx=decompr_idx, n_lp=n_lp, ) decompr_idx = get_lat_trans_decompr_indices_O3(trans_perms) c_rot_fc3 = _get_complement_matrix( C23[9:], natom, order=3, decompr_idx=decompr_idx, n_lp=n_lp, ) c_rot_fc2 = dot_product_sparse( basis_set_fc2.compact_compression_matrix.T, c_rot_fc2, use_mkl=True, ) print(c_rot_fc2.shape) c_rot_fc2 = dot_product_sparse( csr_array(basis_set_fc2.basis_set.T), c_rot_fc2, use_mkl=True, ) print(c_rot_fc2.shape) c_rot_fc3 = dot_product_sparse( basis_set_fc3.compact_compression_matrix.T, c_rot_fc3, use_mkl=True, ) print(c_rot_fc3.shape) c_rot_fc3 = dot_product_sparse( csr_array(basis_set_fc3.basis_set.T), c_rot_fc3, use_mkl=True, ) print(c_rot_fc3.shape) c_rot = vstack([c_rot_fc2, c_rot_fc3]) proj = dot_product_sparse(c_rot, c_rot.T, use_mkl=use_mkl) return proj def complementary_compr_projector_rot_reference( supercell: SymfcAtoms, trans_perms: np.ndarray, basis_set_fc2: FCBasisSetO2, basis_set_fc3: FCBasisSetO3, ) -> csr_array: """Test function for setting rotational invariants.""" natom = len(supercell.numbers) positions_cartesian = (supercell.scaled_positions) @ supercell.cell C23 = _orthogonalize_constraints(positions_cartesian) c_rot_fc2 = _get_complement_matrix(C23[:9], natom, order=2) c_rot_fc3 = _get_complement_matrix(C23[9:], natom, order=3) c_rot = vstack([c_rot_fc2, c_rot_fc3]) correlation = c_rot.T @ c_rot diff = correlation - scipy.sparse.identity(c_rot.shape[1]) assert np.allclose(diff.data, 0.0) c_trans_fc2 = get_lat_trans_compr_matrix_O2(trans_perms) c_trans_fc3 = get_lat_trans_compr_matrix_O3(trans_perms) c_rot_fc2 = c_trans_fc2.T @ c_rot_fc2 c_rot_fc2 = dot_product_sparse( basis_set_fc2.compact_compression_matrix.T, c_rot_fc2, use_mkl=True, ) c_rot_fc2 = dot_product_sparse( csr_array(basis_set_fc2.basis_set.T), c_rot_fc2, use_mkl=True, ) c_rot_fc3 = c_trans_fc3.T @ c_rot_fc3 c_rot_fc3 = dot_product_sparse( basis_set_fc3.compact_compression_matrix.T, c_rot_fc3, use_mkl=True, ) c_rot_fc3 = dot_product_sparse( csr_array(basis_set_fc3.basis_set.T), c_rot_fc3, use_mkl=True, ) c_rot = vstack([c_rot_fc2, c_rot_fc3]) proj = dot_product_sparse(c_rot, c_rot.T, use_mkl=True) return proj def complementary_projector_rot_reference(supercell: SymfcAtoms) -> np.ndarray: """Test function for setting rotational invariants.""" natom = len(supercell.numbers) positions_cartesian = (supercell.scaled_positions) @ supercell.cell C23 = _orthogonalize_constraints(positions_cartesian) c_rot_fc2 = _get_complement_matrix(C23[:9], natom, order=2) c_rot_fc3 = _get_complement_matrix(C23[9:], natom, order=3) c_rot = vstack([c_rot_fc2, c_rot_fc3]) c_rot = c_rot.toarray() proj = c_rot @ c_rot.T return proj if __name__ == "__main__": import argparse import signal signal.signal(signal.SIGINT, signal.SIG_DFL) parser = argparse.ArgumentParser() parser.add_argument("--poscar", type=str, default=None, help="poscar") parser.add_argument( "--supercell", nargs=3, type=int, default=None, help="Supercell size (diagonal components)", ) parser.add_argument( "--order", type=int, default=3, help="FC order.", ) args = parser.parse_args() unitcell = Poscar(args.poscar).structure supercell = supercell_diagonal(unitcell, args.supercell) supercell = SymfcAtoms( numbers=supercell.types, cell=supercell.axis.T, scaled_positions=supercell.positions.T, ) spg_reps = SpgRepsBase(supercell) trans_perms = spg_reps.translation_permutations symfc = Symfc(supercell, use_mkl=True, log_level=1).compute_basis_set(3) basis_set_fc2 = symfc.basis_set[2] basis_set_fc3 = symfc.basis_set[3] import time t1 = time.time() proj = complementary_compr_projector_rot( supercell, trans_perms, basis_set_fc2, basis_set_fc3, use_mkl=True, ) t2 = time.time() if len(supercell.numbers) < 17: proj_ref = complementary_compr_projector_rot_reference( supercell, trans_perms, basis_set_fc2, basis_set_fc3, ) assert np.allclose((proj_ref - proj).data, 0.0) t3 = time.time() print(t2 - t1, t3 - t2) proj = proj.toarray() print(proj.shape) eigvals, eigvecs = np.linalg.eigh(proj) # eigvecs = eigsh_projector(proj, verbose = True) np.set_printoptions(threshold=np.inf) print(eigvals) print(sum(eigvals)) print(eigvecs.shape) nonzero = np.isclose(eigvals, 1.0) print("Number of eigenvectors:", np.count_nonzero(nonzero)) symfc-1.6.0/src/symfc/utils/rotation_dev/run_test_O2.py000066400000000000000000000160071511276756400232030ustar00rootroot00000000000000"""Matrix utility functions for setting rotational invariants.""" import itertools import numpy as np import scipy from pypolymlp.core.interface_vasp import Poscar from pypolymlp.utils.structure_utils import supercell_diagonal from scipy.sparse import csr_array from symfc import Symfc from symfc.basis_sets.basis_sets_O2 import FCBasisSetO2 from symfc.spg_reps import SpgRepsBase from symfc.utils.eig_tools import eigsh_projector from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O2 import ( get_lat_trans_compr_matrix, get_lat_trans_decompr_indices, ) def _orthogonalize_constraints(positions_cartesian: np.ndarray): """Orthogonalize constraints derived from rotational invariance.""" natom = positions_cartesian.shape[0] N3 = 3 * natom C = np.zeros((N3, 3), dtype="double") C[1::3, 0] = -positions_cartesian[:, 0] C[2::3, 2] = positions_cartesian[:, 0] C[2::3, 1] = -positions_cartesian[:, 1] C[0::3, 0] = positions_cartesian[:, 1] C[0::3, 2] = -positions_cartesian[:, 2] C[1::3, 1] = positions_cartesian[:, 2] proj_C = C @ np.linalg.inv(C.T @ C) @ C.T eigvals, eigvecs = np.linalg.eigh(proj_C) nonzero = np.isclose(eigvals, 1.0) C = eigvecs[:, nonzero] return C def complement_sum_rule(natom, decompr_idx=None, n_lp=None): """Construct sum rule.""" N33 = natom * 9 NN33 = natom**2 * 9 row = [] for j, alpha, beta in itertools.product(range(natom), range(3), range(3)): i = np.arange(natom) * N33 const_add = j * 9 + alpha * 3 + beta row.extend(i + const_add) col = np.repeat(np.arange(N33), natom) data = np.ones(len(col)) / np.sqrt(natom) if decompr_idx is not None: data /= np.sqrt(n_lp) c_sum = csr_array( (data, (decompr_idx[row], col)), shape=(NN33 // n_lp, N33), dtype="double", ) else: c_sum = csr_array( (data, (row, col)), shape=(NN33, N33), dtype="double", ) return c_sum def complement_rotational_sum_rule(supercell, indep_atoms, decompr_idx=None, n_lp=None): """Get complement_rotational_sum_rule.""" positions_cartesian = (supercell.scaled_positions) @ supercell.cell positions_cartesian[:, 0] -= np.average(positions_cartesian[:, 0]) positions_cartesian[:, 1] -= np.average(positions_cartesian[:, 1]) positions_cartesian[:, 2] -= np.average(positions_cartesian[:, 2]) C2 = _orthogonalize_constraints(positions_cartesian) natom = positions_cartesian.shape[0] N3 = natom * 3 N33 = N3 * 3 NN33 = N3 * N3 n_expand = len(indep_atoms) * 3 n_col = n_expand * 3 data = np.tile(C2.T.reshape(-1), n_expand) col = np.repeat(np.arange(n_col), N3) row = [] ids_ialpha = (np.arange(natom) * N33)[:, None] + (np.arange(3) * 3)[None, :] ids_ialpha = ids_ialpha.reshape(-1) for j, beta in itertools.product(indep_atoms, range(3)): ids = ids_ialpha + (j * 9 + beta) row.extend(np.tile(ids, 3)) if decompr_idx is None: c_rot = csr_array((data, (row, col)), shape=(NN33, n_col), dtype="double") else: data /= np.sqrt(n_lp) c_rot = csr_array( (data, (decompr_idx[row], col)), shape=(NN33 // n_lp, n_col), dtype="double", ) return c_rot def complementary_compr_projector_rot_O2_test( supercell: SymfcAtoms, trans_perms: np.ndarray, basis_set_fc2: FCBasisSetO2, use_mkl: bool = False, ) -> csr_array: """Test function for setting rotational invariants.""" n_lp, natom = trans_perms.shape N3 = natom * 3 N33 = N3 * 3 NN33 = N3 * N3 decompr_idx = get_lat_trans_decompr_indices(trans_perms) c_trans = get_lat_trans_compr_matrix(decompr_idx, natom, n_lp) # indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) # indep_atoms = [indep_atoms[0]] # if indep_atoms is None: indep_atoms = list(range(natom)) """ c_rot = complement_rotational_sum_rule(supercell, indep_atoms) c_sum = complement_sum_rule(natom) proj_rot = scipy.sparse.identity(NN33) - c_rot @ c_rot.T proj_sum = scipy.sparse.identity(NN33) - c_sum @ c_sum.T """ c_sum = complement_sum_rule(natom, decompr_idx=decompr_idx, n_lp=n_lp) # proj = scipy.sparse.identity(NN33 // n_lp) - c_sum @ c_sum.T # c = eigsh_projector(proj, verbose=True) print("-------------- Only rotational sum rules ---------------") c_rot = complement_rotational_sum_rule( supercell, indep_atoms, decompr_idx=decompr_idx, n_lp=n_lp, ) proj = scipy.sparse.identity(NN33 // n_lp) - c_rot @ c_rot.T - c_sum @ c_sum.T basis = eigsh_projector(proj, verbose=True) print(basis.shape) # vals, vecs = np.linalg.eigh(proj.toarray()) # print(vals) print(c_trans.shape) basis = c_trans @ basis print(basis.shape) proj_basis = basis @ basis.T positions_cartesian = (supercell.scaled_positions) @ supercell.cell C = np.zeros((N3, 3), dtype="double") C[1::3, 0] = -positions_cartesian[:, 0] C[2::3, 2] = positions_cartesian[:, 0] C[2::3, 1] = -positions_cartesian[:, 1] C[0::3, 0] = positions_cartesian[:, 1] C[0::3, 2] = -positions_cartesian[:, 2] C[1::3, 1] = positions_cartesian[:, 2] trial_vec = np.zeros(NN33, dtype="double") ids_ialpha = (np.arange(natom) * N33)[:, None] + (np.arange(3) * 3)[None, :] ids_ialpha = ids_ialpha.reshape(-1) for gamma in range(3): for j in range(natom): for beta in range(3): ids = ids_ialpha + (j * 9 + beta) trial_vec[ids] = C[:, gamma] projected_vec = proj_basis @ trial_vec print(projected_vec[np.abs(projected_vec) > 1e-14]) return proj if __name__ == "__main__": import argparse import signal signal.signal(signal.SIGINT, signal.SIG_DFL) parser = argparse.ArgumentParser() parser.add_argument("--poscar", type=str, default=None, help="poscar") parser.add_argument( "--supercell", nargs=3, type=int, default=None, help="Supercell size (diagonal components)", ) args = parser.parse_args() unitcell = Poscar(args.poscar).structure supercell = supercell_diagonal(unitcell, args.supercell) supercell = SymfcAtoms( numbers=supercell.types, cell=supercell.axis.T, scaled_positions=supercell.positions.T, ) spg_reps = SpgRepsBase(supercell) trans_perms = spg_reps.translation_permutations # n_lp, natom = trans_perms.shape symfc = Symfc(supercell, use_mkl=True, log_level=1).compute_basis_set(2) basis_set_fc2 = symfc.basis_set[2] # n_a_compress_mat = np.sqrt(n_lp) * basis_set_fc2.compact_compression_matrix proj1 = complementary_compr_projector_rot_O2_test( supercell, trans_perms, basis_set_fc2, ) # proj2 = complementary_compr_projector_rot_O2_test( # supercell, # trans_perms, # basis_set_fc2, # indep_atoms = [1, 6], # ) # print(proj2 - proj1) symfc-1.6.0/src/symfc/utils/rotation_tools_O2.py000066400000000000000000000060471511276756400217250ustar00rootroot00000000000000"""Matrix utility functions for setting rotational invariants.""" # from typing import Optional # import itertools from typing import Union import numpy as np from scipy.sparse import csr_array from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O2 import ( get_lat_trans_decompr_indices, ) # try: # from symfc.utils.matrix import dot_product_sparse # except ImportError: # pass def _orthogonalize_constraints(positions_cartesian: np.ndarray): """Orthogonalize constraints derived from rotational invariance.""" natom = positions_cartesian.shape[0] N3 = 3 * natom # Eliminate translational sum rules positions_cartesian[:, 0] -= np.average(positions_cartesian[:, 0]) positions_cartesian[:, 1] -= np.average(positions_cartesian[:, 1]) positions_cartesian[:, 2] -= np.average(positions_cartesian[:, 2]) C = np.zeros((N3, 3), dtype="double") C[1::3, 0] = -positions_cartesian[:, 0] C[2::3, 2] = positions_cartesian[:, 0] C[2::3, 1] = -positions_cartesian[:, 1] C[0::3, 0] = positions_cartesian[:, 1] C[0::3, 2] = -positions_cartesian[:, 2] C[1::3, 1] = positions_cartesian[:, 2] proj_C = C @ np.linalg.inv(C.T @ C) @ C.T eigvals, eigvecs = np.linalg.eigh(proj_C) nonzero = np.isclose(eigvals, 1.0) C = eigvecs[:, nonzero] return C def complementary_compr_projector_rot_sum_rules_O2( supercell: SymfcAtoms, trans_perms: np.ndarray, n_a_compress_mat: Union[np.ndarray, csr_array], use_mkl: bool = False, ) -> csr_array: """Test function for setting rotational invariants.""" # atomic_decompr_idx: Optional[np.ndarray] = None, # fc_cutoff: Optional[FCCutoff] = None, n_lp, natom = trans_perms.shape # TODO: decompr_idx -> atomic_decompr_idx decompr_idx = get_lat_trans_decompr_indices(trans_perms) indep_atoms = list(range(natom)) positions_cartesian = (supercell.scaled_positions) @ supercell.cell C2 = _orthogonalize_constraints(positions_cartesian) N3 = natom * 3 N33 = N3 * 3 NN33 = N3 * N3 n_expand = len(indep_atoms) * 3 n_col = n_expand * 3 data = np.tile(C2.T.reshape(-1), n_expand) col = np.repeat(np.arange(n_col), N3) row = [] ids_ialpha = (np.arange(natom) * N33)[:, None] + (np.arange(3) * 3)[None, :] ids_ialpha = ids_ialpha.reshape(-1) for j, beta in itertools.product(indep_atoms, range(3)): ids = ids_ialpha + (j * 9 + beta) row.extend(np.tile(ids, 3)) data /= np.sqrt(n_lp) c_rot_cmplt = csr_array( (data, (decompr_idx[row], col)), shape=(NN33 // n_lp, n_col), dtype="double", ) c_rot_cmplt = n_a_compress_mat.T @ c_rot_cmplt p_rot_cmplt = c_rot_cmplt @ c_rot_cmplt.T return p_rot_cmplt """ Another option proj = scipy.sparse.identity(NN33 // n_lp) - c_rot_cmplt @ c_rot_cmplt.T c_rot = eigsh_projector(proj, verbose=True) c_rot = n_a_compress_mat.T @ c_rot proj_rot = c_rot @ c_rot.T Then, proj_rot can be used as proj = proj @ proj_rot @ proj eigvecs = eigsh(proj) """ symfc-1.6.0/src/symfc/utils/solver_funcs.py000066400000000000000000000041341511276756400210110ustar00rootroot00000000000000"""Solver functions.""" import numpy as np from scipy.linalg.lapack import get_lapack_funcs from scipy.sparse import csr_array def solve_linear_equation(A: np.ndarray, b: np.ndarray): """Solve linear equation using lapack in scipy. numpy and scipy implementations x = np.linalg.solve(A, b) x = scipy.linalg.solve(A, b, check_finite=False, assume_a='pos') """ (posv,) = get_lapack_funcs(("posv",), (A, b)) _, x, _ = posv(A, b, lower=False, overwrite_a=False, overwrite_b=False) return x def fit(X: np.ndarray, y: np.ndarray): """Solve a normal equation in least-squares. (X.T @ X) @ coefs = X.T @ y n_samples, n_features = X.shape """ coefs = solve_linear_equation(X.T @ X, X.T @ y) return coefs def get_batch_slice(n_data: int, batch_size: int): """Calculate slice indices for a given batch size.""" begin_batch = list(range(0, n_data, batch_size)) if len(begin_batch) > 1: end_batch = list(begin_batch[1:]) + [n_data] if (end_batch[-1] - end_batch[-2]) < batch_size // 5: end_batch[-2] = end_batch[-1] begin_batch = begin_batch[:-1] end_batch = end_batch[:-1] else: end_batch = [n_data] return begin_batch, end_batch def get_displacement_sparse_matrix( atoms: np.ndarray, displacements: np.ndarray, n_atom: int, tol: float = 1e-15, ) -> csr_array: """Return sparse matrix with displacements. Parameter --------- atoms: Indices of atoms displaced, shape = (n_snapshot). displacements: Displacement vectors, shape = (n_snapshot, 3). n_atom: Number of atoms in structure. tol: Tolerance value for defining nonzero elements. """ if atoms.shape[0] != displacements.shape[0]: raise RuntimeError("Sizes of atoms and displacements are inconsistent.") N3 = n_atom * 3 nonzero = np.abs(displacements) > tol rows, cols = np.where(nonzero) cols += atoms[rows] * 3 mat = csr_array( (displacements[nonzero], (rows, cols)), shape=(displacements.shape[0], N3), dtype="double", ) return mat symfc-1.6.0/src/symfc/utils/translation_tools_O1.py000066400000000000000000000031121511276756400224110ustar00rootroot00000000000000"""Functions for introducing translational invariance in 1st order force constants.""" import numpy as np import scipy from scipy.sparse import csr_array def compressed_projector_sum_rules( compress_mat: csr_array, N: int, use_mkl: bool = False ): """Return projection matrix for sum rule compressed by C.""" proj_cplmt = _compressed_complement_projector_sum_rules( compress_mat, N, use_mkl=use_mkl ) return scipy.sparse.identity(proj_cplmt.shape[0]) - proj_cplmt def _compressed_complement_projector_sum_rules_algo1( compress_mat: csr_array, N: int, use_mkl: bool = False ): r"""Return complementary projection matrix for sum rule compressed by C. proj_sum_cplmt = [C.T @ Csum(c)] @ [Csum(c).T @ C] = c_sum_cplmt_compr.T @ c_sum_cplmt_compr Matrix shape of proj_sum_cplmt is (C.shape[1], C.shape[1]). C.shape[0] must be equal to NN33. Sum rules are given as sums over i: \sum_i \phi_{ia,jb} = 0 """ N3 = 3 * N row = np.arange(N3) col = np.tile(range(3), N) data = np.zeros(N3) data[:] = 1 / np.sqrt(N) c_sum_cplmt = csr_array((data, (row, col)), shape=(N3, 3)) c_sum_cplmt_compr = c_sum_cplmt.T @ compress_mat proj_sum_cplmt = c_sum_cplmt_compr.T @ c_sum_cplmt_compr return proj_sum_cplmt def _compressed_complement_projector_sum_rules( compress_mat: csr_array, N: int, use_mkl: bool = False ): """Return complementary projection matrix for sum rule compressed by C.""" return _compressed_complement_projector_sum_rules_algo1( compress_mat, N, use_mkl=use_mkl ) symfc-1.6.0/src/symfc/utils/translation_tools_O2.py000066400000000000000000000245171511276756400224260ustar00rootroot00000000000000"""Functions for introducing translational invariance in 2nd order force constants.""" from typing import Optional import numpy as np import scipy from scipy.sparse import csr_array from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.solver_funcs import get_batch_slice from symfc.utils.utils import get_indep_atoms_by_lat_trans from symfc.utils.utils_O2 import _get_atomic_lat_trans_decompr_indices try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass def optimize_batch_size_sum_rules_O2(natom: int, n_batch: int): """Calculate batch size for constructing projector for sum rules.""" if n_batch > natom: raise ValueError("n_batch must be smaller than N.") batch_size = natom * (natom // n_batch) return batch_size def compressed_projector_sum_rules_O2( trans_perms: np.ndarray, n_a_compress_mat: csr_array, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, n_batch: int = 1, use_mkl: bool = False, ) -> csr_array: r"""Return projection matrix for translational sum rule. Calculate a compressed projector for translational sum rules efficiently using independent atom with respect to lattice translations. This compression is achieved using C_trans and n_a_compress_mat, without the need to allocate C_trans. The implementation utilizes get_atomic_lat_trans_decompr_indices_O3 to ensure efficient memory usage. Return ------ Compressed projector I - P^(c). I - P^(c) = n_a_compress_mat.T @ C_trans.T @ [I - C_sum^(c) @ C_sum^(c).T] @ C_trans @ n_a_compress_mat = I - [n_a_compress_mat.T @ C_trans.T @ C_sum^(c)] @ [C_sum^(c).T @ C_trans @ n_a_compress_mat] Algorithm --------- 1. C_sum^(c).T = [I, I, I, ...] of size (27N, 27N^2). I denotes the unit matrix of size (27N, 27N). C_sum^(c).T is composed of N unit matrices. In this representation, the translational sum rules are given by \sum_i FC2(i, j, a, b) = 0. 2. To divide the computation of a compressed projector into several batches, C_sum^(c) and C_trans are permuted from the index order of (i, j, a, b) to (a, b, j, i). This is represented by C_sum^(c).T @ C_trans = C_sum^(c).T @ S.T @ S @ C_trans, where S denotes the permutation matrix that changes the index order to (a, b, j, i). Using this permutation, the translational sum rules are represented as C_sum^(c).T @ S.T = [ [1_N.T, 0_N.T, 0_N.T, ...] [0_N.T, 1_N.T, 0_N.T, ...] [0_N.T, 0_N.T, 1_N.T, ...] ... ], where 1_N and 0_N are column vectors of size N with all elements equal to one and zero, respectively. (Example) C_sum^(c).T @ S.T = [ [1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 ...] [0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 ...] [0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 ...] ... ] In this function, the permutation is achieved by using matrix reshapes. 3. Set C_trans.T @ C_sum^(c) @ C_sum^(c).T @ C_trans = [(C_trans.T @ S.T) @ (S @ C_sum^(c))] @ [(C_sum^(c).T @ S.T) @ (S @ C_trans)] = [T_1, T_2, ..., T_N33] @ (S @ C_sum^(c)) @ (C_sum^(c).T @ S.T) @ [T_1, T_2, ..., T_N33].T = \sum_i t_i @ t_i.T, where t_i = \sum_c T_i[:, c]. t_i is represented by c_sum_cplmt.T in this function. T_i is the submatrix of size (N, n_aN33) of permuted C_trans. 4. Compute P^(c) = \sum_i (n_a_compress_mat.T @ t_i) @ (t_i.T @ n_a_compress_mat) 5. Compute P = I - P^(c) """ n_lp, natom = trans_perms.shape NN9 = natom**2 * 9 NN = natom**2 proj_size = n_a_compress_mat.shape[1] # type: ignore proj_cplmt = csr_array((proj_size, proj_size), dtype="double") if atomic_decompr_idx is None: atomic_decompr_idx = _get_atomic_lat_trans_decompr_indices(trans_perms) decompr_idx = atomic_decompr_idx.reshape((natom, natom)).T.reshape(-1) * 9 indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) nonzero = np.zeros((natom, natom), dtype=bool) nonzero[indep_atoms, :] = True nonzero = nonzero.reshape(-1) if fc_cutoff is not None: nonzero_c = fc_cutoff.nonzero_atomic_indices_fc2() nonzero_c = nonzero_c.reshape((natom, natom)).T.reshape(-1) nonzero = nonzero & nonzero_c batch_size = optimize_batch_size_sum_rules_O2(natom, n_batch=n_batch) ab = np.arange(9) for begin, end in zip(*get_batch_slice(NN, batch_size)): size = end - begin size_vector = size * 9 size_row = size_vector // natom nonzero_b = nonzero[begin:end] size_data = np.count_nonzero(nonzero_b) * 9 if size_data == 0: continue decompr_idx_b = decompr_idx[begin:end][nonzero_b] c_sum_cplmt = csr_array( ( np.ones(size_data, dtype="double"), ( np.repeat(np.arange(size_row), natom)[np.tile(nonzero_b, 9)], (ab[:, None] + decompr_idx_b[None, :]).reshape(-1), ), ), shape=(size_row, NN9 // n_lp), dtype="double", ) c_sum_cplmt = dot_product_sparse(c_sum_cplmt, n_a_compress_mat, use_mkl=use_mkl) proj_cplmt += dot_product_sparse(c_sum_cplmt.T, c_sum_cplmt, use_mkl=use_mkl) proj_cplmt /= natom return scipy.sparse.identity(proj_cplmt.shape[0]) - proj_cplmt def compressed_projector_sum_rules_O2_stable( trans_perms: np.ndarray, n_a_compress_mat: csr_array, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, n_batch: int = 1, use_mkl: bool = False, ) -> csr_array: r"""Return projection matrix for translational sum rule. Calculate a compressed projector for translational sum rules. This compression is achieved using C_trans and n_a_compress_mat, without the need to allocate C_trans. The implementation utilizes get_atomic_lat_trans_decompr_indices_O3 to ensure efficient memory usage. Return ------ Compressed projector I - P^(c). I - P^(c) = n_a_compress_mat.T @ C_trans.T @ [I - C_sum^(c) @ C_sum^(c).T] @ C_trans @ n_a_compress_mat = I - [n_a_compress_mat.T @ C_trans.T @ C_sum^(c)] @ [C_sum^(c).T @ C_trans @ n_a_compress_mat] Algorithm --------- 1. C_sum^(c).T = [I, I, I, ...] of size (27N, 27N^2). I denotes the unit matrix of size (27N, 27N). C_sum^(c).T is composed of N unit matrices. In this representation, the translational sum rules are given by \sum_i FC2(i, j, a, b) = 0. 2. To divide the computation of a compressed projector into several batches, C_sum^(c) and C_trans are permuted from the index order of (i, j, a, b) to (a, b, j, i). This is represented by C_sum^(c).T @ C_trans = C_sum^(c).T @ S.T @ S @ C_trans, where S denotes the permutation matrix that changes the index order to (a, b, j, i). Using this permutation, the translational sum rules are represented as C_sum^(c).T @ S.T = [ [1_N.T, 0_N.T, 0_N.T, ...] [0_N.T, 1_N.T, 0_N.T, ...] [0_N.T, 0_N.T, 1_N.T, ...] ... ], where 1_N and 0_N are column vectors of size N with all elements equal to one and zero, respectively. (Example) C_sum^(c).T @ S.T = [ [1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 ...] [0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 ...] [0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 ...] ... ] In this function, the permutation is achieved by using matrix reshapes. 3. Set C_trans.T @ C_sum^(c) @ C_sum^(c).T @ C_trans = [(C_trans.T @ S.T) @ (S @ C_sum^(c))] @ [(C_sum^(c).T @ S.T) @ (S @ C_trans)] = [T_1, T_2, ..., T_N33] @ (S @ C_sum^(c)) @ (C_sum^(c).T @ S.T) @ [T_1, T_2, ..., T_N33].T = \sum_i t_i @ t_i.T, where t_i = \sum_c T_i[:, c]. t_i is represented by c_sum_cplmt.T in this function. T_i is the submatrix of size (N, n_aN33) of permuted C_trans. 4. Compute P^(c) = \sum_i (n_a_compress_mat.T @ t_i) @ (t_i.T @ n_a_compress_mat) 5. Compute P = I - P^(c) """ n_lp, natom = trans_perms.shape NN9 = natom**2 * 9 NN = natom**2 proj_size = n_a_compress_mat.shape[1] # type: ignore proj_cplmt = csr_array((proj_size, proj_size), dtype="double") if atomic_decompr_idx is None: atomic_decompr_idx = _get_atomic_lat_trans_decompr_indices(trans_perms) decompr_idx = atomic_decompr_idx.reshape((natom, natom)).T.reshape(-1) * 9 if fc_cutoff is not None: nonzero = fc_cutoff.nonzero_atomic_indices_fc2() nonzero = nonzero.reshape((natom, natom)).T.reshape(-1) batch_size = optimize_batch_size_sum_rules_O2(natom, n_batch=n_batch) ab = np.arange(9) for begin, end in zip(*get_batch_slice(NN, batch_size)): size = end - begin size_vector = size * 9 size_row = size_vector // natom if fc_cutoff is None: c_sum_cplmt = csr_array( ( np.ones(size_vector, dtype="double"), ( np.repeat(np.arange(size_row), natom), (ab[:, None] + decompr_idx[begin:end][None, :]).reshape(-1), ), ), shape=(size_row, NN9 // n_lp), dtype="double", ) else: nonzero_b = nonzero[begin:end] decompr_idx_b = decompr_idx[begin:end][nonzero_b] size_data = np.count_nonzero(nonzero_b) * 9 c_sum_cplmt = csr_array( ( np.ones(size_data, dtype="double"), ( np.repeat(np.arange(size_row), natom)[np.tile(nonzero_b, 9)], (ab[:, None] + decompr_idx_b[None, :]).reshape(-1), ), ), shape=(size_row, NN9 // n_lp), dtype="double", ) c_sum_cplmt = dot_product_sparse(c_sum_cplmt, n_a_compress_mat, use_mkl=use_mkl) proj_cplmt += dot_product_sparse(c_sum_cplmt.T, c_sum_cplmt, use_mkl=use_mkl) proj_cplmt /= n_lp * natom return scipy.sparse.identity(proj_cplmt.shape[0]) - proj_cplmt symfc-1.6.0/src/symfc/utils/translation_tools_O3.py000066400000000000000000000256501511276756400224260ustar00rootroot00000000000000"""Functions for introducing translational invariance in 3rd order force constants.""" from typing import Optional import numpy as np import scipy from scipy.sparse import csr_array from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.solver_funcs import get_batch_slice from symfc.utils.utils import get_indep_atoms_by_lat_trans from symfc.utils.utils_O3 import get_atomic_lat_trans_decompr_indices_O3 try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass def optimize_batch_size_sum_rules_O3(natom: int, n_batch: Optional[int] = None): """Calculate batch size for constructing projector for sum rules.""" if n_batch is None: if natom < 256: n_batch = natom // min(natom, 16) else: n_batch = natom // 4 if n_batch > natom: raise ValueError("n_batch must be smaller than N.") batch_size = natom**2 * (natom // n_batch) return batch_size def compressed_projector_sum_rules_O3( trans_perms: np.ndarray, n_a_compress_mat: csr_array, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, n_batch: Optional[int] = None, use_mkl: bool = False, verbose: bool = False, ) -> csr_array: r"""Return projection matrix for translational sum rule. Calculate a compressed projector for translational sum rules efficiently using independent atom with respect to lattice translations. This compression is achieved using C_trans and n_a_compress_mat, without the need to allocate C_trans. The implementation utilizes get_atomic_lat_trans_decompr_indices_O3 to ensure efficient memory usage. Return ------ Compressed projector I - P^(c). I - P^(c) = n_a_compress_mat.T @ C_trans.T @ [I - C_sum^(c) @ C_sum^(c).T] @ C_trans @ n_a_compress_mat = I - [n_a_compress_mat.T @ C_trans.T @ C_sum^(c)] @ [C_sum^(c).T @ C_trans @ n_a_compress_mat] Algorithm --------- 1. C_sum^(c).T = [I, I, I, ...] of size (27N^2, 27N^3). I denotes the unit matrix of size (27N^2, 27N^2). C_sum^(c).T is composed of N unit matrices. In this representation, the translational sum rules are given by \sum_i FC3(i, j, k, a, b, c) = 0. 2. To divide the computation of a compressed projector into several batches, C_sum^(c) and C_trans are permuted from the index order of (i, j, k, a, b, c) to (a, b, c, j, k, i). This is represented by C_sum^(c).T @ C_trans = C_sum^(c).T @ S.T @ S @ C_trans, where S denotes the permutation matrix that changes the index order to (a, b, c, j, k, i). Using this permutation, the translational sum rules are represented as C_sum^(c).T @ S.T = [ [1_N.T, 0_N.T, 0_N.T, ...] [0_N.T, 1_N.T, 0_N.T, ...] [0_N.T, 0_N.T, 1_N.T, ...] ... ], where 1_N and 0_N are column vectors of size N with all elements equal to one and zero, respectively. (Example) C_sum^(c).T @ S.T = [ [1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 ...] [0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 ...] [0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 ...] ... ] In this function, the permutation is achieved by using matrix reshapes. 3. Set C_trans.T @ C_sum^(c) @ C_sum^(c).T @ C_trans = [(C_trans.T @ S.T) @ (S @ C_sum^(c))] @ [(C_sum^(c).T @ S.T) @ (S @ C_trans)] = [T_1, T_2, ..., T_NN333] @ (S @ C_sum^(c)) @ (C_sum^(c).T @ S.T) @ [T_1, T_2, ..., T_NN333].T = \sum_i t_i @ t_i.T, where t_i = \sum_c T_i[:, c]. t_i is represented by c_sum_cplmt.T in this function. T_i is the submatrix of size (N, n_aNN333) of permuted C_trans. 4. Compute P^(c) = \sum_i (n_a_compress_mat.T @ t_i) @ (t_i.T @ n_a_compress_mat) 5. Compute P = I - P^(c) """ n_lp, natom = trans_perms.shape NNN27 = natom**3 * 27 NNN = natom**3 NN = natom**2 proj_size = n_a_compress_mat.shape[1] # type: ignore proj_cplmt = csr_array((proj_size, proj_size), dtype="double") if atomic_decompr_idx is None: atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O3(trans_perms) decompr_idx = atomic_decompr_idx.reshape((natom, NN)).T.reshape(-1) * 27 indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) nonzero = np.zeros((natom, natom, natom), dtype=bool) nonzero[indep_atoms, :, :] = True nonzero = nonzero.reshape(-1) if fc_cutoff is not None: nonzero_c = fc_cutoff.nonzero_atomic_indices_fc3() nonzero_c = nonzero_c.reshape((natom, NN)).T.reshape(-1) nonzero = nonzero & nonzero_c batch_size = optimize_batch_size_sum_rules_O3(natom, n_batch=n_batch) abc = np.arange(27) for begin, end in zip(*get_batch_slice(NNN, batch_size)): size = end - begin size_vector = size * 27 size_row = size_vector // natom nonzero_b = nonzero[begin:end] size_data = np.count_nonzero(nonzero_b) * 27 if size_data == 0: continue if verbose: print("Complementary P (Sum rule):", str(end) + "/" + str(NNN), flush=True) decompr_idx_b = decompr_idx[begin:end][nonzero_b] c_sum_cplmt = csr_array( ( np.ones(size_data, dtype="double"), ( np.repeat(np.arange(size_row), natom)[np.tile(nonzero_b, 27)], (abc[:, None] + decompr_idx_b[None, :]).reshape(-1), ), ), shape=(size_row, NNN27 // n_lp), dtype="double", ) c_sum_cplmt = dot_product_sparse(c_sum_cplmt, n_a_compress_mat, use_mkl=use_mkl) proj_cplmt += dot_product_sparse(c_sum_cplmt.T, c_sum_cplmt, use_mkl=use_mkl) proj_cplmt /= natom return scipy.sparse.identity(proj_cplmt.shape[0]) - proj_cplmt def compressed_projector_sum_rules_O3_stable( trans_perms: np.ndarray, n_a_compress_mat: csr_array, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, n_batch: Optional[int] = None, use_mkl: bool = False, verbose: bool = False, ) -> csr_array: r"""Return projection matrix for translational sum rule. Calculate a compressed projector for translational sum rules. This compression is achieved using C_trans and n_a_compress_mat, without the need to allocate C_trans. The implementation utilizes get_atomic_lat_trans_decompr_indices_O3 to ensure efficient memory usage. Return ------ Compressed projector I - P^(c). I - P^(c) = n_a_compress_mat.T @ C_trans.T @ [I - C_sum^(c) @ C_sum^(c).T] @ C_trans @ n_a_compress_mat = I - [n_a_compress_mat.T @ C_trans.T @ C_sum^(c)] @ [C_sum^(c).T @ C_trans @ n_a_compress_mat] Algorithm --------- 1. C_sum^(c).T = [I, I, I, ...] of size (27N^2, 27N^3). I denotes the unit matrix of size (27N^2, 27N^2). C_sum^(c).T is composed of N unit matrices. In this representation, the translational sum rules are given by \sum_i FC3(i, j, k, a, b, c) = 0. 2. To divide the computation of a compressed projector into several batches, C_sum^(c) and C_trans are permuted from the index order of (i, j, k, a, b, c) to (a, b, c, j, k, i). This is represented by C_sum^(c).T @ C_trans = C_sum^(c).T @ S.T @ S @ C_trans, where S denotes the permutation matrix that changes the index order to (a, b, c, j, k, i). Using this permutation, the translational sum rules are represented as C_sum^(c).T @ S.T = [ [1_N.T, 0_N.T, 0_N.T, ...] [0_N.T, 1_N.T, 0_N.T, ...] [0_N.T, 0_N.T, 1_N.T, ...] ... ], where 1_N and 0_N are column vectors of size N with all elements equal to one and zero, respectively. (Example) C_sum^(c).T @ S.T = [ [1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 ...] [0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 ...] [0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 ...] ... ] In this function, the permutation is achieved by using matrix reshapes. 3. Set C_trans.T @ C_sum^(c) @ C_sum^(c).T @ C_trans = [(C_trans.T @ S.T) @ (S @ C_sum^(c))] @ [(C_sum^(c).T @ S.T) @ (S @ C_trans)] = [T_1, T_2, ..., T_NN333] @ (S @ C_sum^(c)) @ (C_sum^(c).T @ S.T) @ [T_1, T_2, ..., T_NN333].T = \sum_i t_i @ t_i.T, where t_i = \sum_c T_i[:, c]. t_i is represented by c_sum_cplmt.T in this function. T_i is the submatrix of size (N, n_aNN333) of permuted C_trans. 4. Compute P^(c) = \sum_i (n_a_compress_mat.T @ t_i) @ (t_i.T @ n_a_compress_mat) 5. Compute P = I - P^(c) """ n_lp, natom = trans_perms.shape NNN27 = natom**3 * 27 NNN = natom**3 NN = natom**2 proj_size = n_a_compress_mat.shape[1] # type: ignore proj_cplmt = csr_array((proj_size, proj_size), dtype="double") if atomic_decompr_idx is None: atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O3(trans_perms) decompr_idx = atomic_decompr_idx.reshape((natom, NN)).T.reshape(-1) * 27 if fc_cutoff is not None: nonzero = fc_cutoff.nonzero_atomic_indices_fc3() nonzero = nonzero.reshape((natom, NN)).T.reshape(-1) batch_size = optimize_batch_size_sum_rules_O3(natom, n_batch=n_batch) abc = np.arange(27) for begin, end in zip(*get_batch_slice(NNN, batch_size)): if verbose: print("Complementary P (Sum rule):", str(end) + "/" + str(NNN), flush=True) size = end - begin size_vector = size * 27 size_row = size_vector // natom if fc_cutoff is None: c_sum_cplmt = csr_array( ( np.ones(size_vector, dtype="double"), ( np.repeat(np.arange(size_row), natom), (abc[:, None] + decompr_idx[begin:end][None, :]).reshape(-1), ), ), shape=(size_row, NNN27 // n_lp), dtype="double", ) else: nonzero_b = nonzero[begin:end] decompr_idx_b = decompr_idx[begin:end][nonzero_b] size_data = np.count_nonzero(nonzero_b) * 27 c_sum_cplmt = csr_array( ( np.ones(size_data, dtype="double"), ( np.repeat(np.arange(size_row), natom)[np.tile(nonzero_b, 27)], (abc[:, None] + decompr_idx_b[None, :]).reshape(-1), ), ), shape=(size_row, NNN27 // n_lp), dtype="double", ) c_sum_cplmt = dot_product_sparse(c_sum_cplmt, n_a_compress_mat, use_mkl=use_mkl) proj_cplmt += dot_product_sparse(c_sum_cplmt.T, c_sum_cplmt, use_mkl=use_mkl) proj_cplmt /= n_lp * natom return scipy.sparse.identity(proj_cplmt.shape[0]) - proj_cplmt symfc-1.6.0/src/symfc/utils/translation_tools_O4.py000066400000000000000000000260001511276756400224150ustar00rootroot00000000000000"""Functions for introducing translational invariance in 4th order force constants.""" from typing import Optional import numpy as np import scipy from scipy.sparse import csr_array from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.solver_funcs import get_batch_slice from symfc.utils.utils import get_indep_atoms_by_lat_trans from symfc.utils.utils_O4 import get_atomic_lat_trans_decompr_indices_O4 try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass def optimize_batch_size_sum_rules_O4(natom: int, n_batch: Optional[int] = None): """Calculate batch size for constructing projector for sum rules.""" if n_batch is None: if natom < 32: n_batch = natom // min(natom, 8) else: n_batch = natom // 4 if n_batch > natom: raise ValueError("n_batch must be smaller than N.") batch_size = natom**3 * (natom // n_batch) return batch_size def compressed_projector_sum_rules_O4( trans_perms: np.ndarray, n_a_compress_mat: csr_array, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, n_batch: Optional[int] = None, use_mkl: bool = False, verbose: bool = False, ) -> csr_array: r"""Return projection matrix for translational sum rule. Calculate a compressed projector for translational sum rules efficiently using independent atom with respect to lattice translations. This compression is achieved using C_trans and n_a_compress_mat, without the need to allocate C_trans. The implementation utilizes get_atomic_lat_trans_decompr_indices_O4 to ensure efficient memory usage. Return ------ Compressed projector I - P^(c). I - P^(c) = n_a_compress_mat.T @ C_trans.T @ [I - C_sum^(c) @ C_sum^(c).T] @ C_trans @ n_a_compress_mat = I - [n_a_compress_mat.T @ C_trans.T @ C_sum^(c)] @ [C_sum^(c).T @ C_trans @ n_a_compress_mat] Algorithm --------- 1. C_sum^(c).T = [I, I, I, ...] of size (27N^3, 27N^4). I denotes the unit matrix of size (27N^3, 27N^3). C_sum^(c).T is composed of N unit matrices. In this representation, the translational sum rules are given by \sum_i FC4(i, j, k, l, a, b, c, d) = 0. 2. To divide the computation of a compressed projector into several batches, C_sum^(c) and C_trans are permuted from the index order of (i, j, k, l, a, b, c, d) to (a, b, c, d, j, k, l, i). This is represented by C_sum^(c).T @ C_trans = C_sum^(c).T @ S.T @ S @ C_trans, where S denotes the permutation matrix that changes the index order to (a, b, c, d, j, k, l, i). Using this permutation, the translational sum rules are represented as C_sum^(c).T @ S.T = [ [1_N.T, 0_N.T, 0_N.T, ...] [0_N.T, 1_N.T, 0_N.T, ...] [0_N.T, 0_N.T, 1_N.T, ...] ... ], where 1_N and 0_N are column vectors of size N with all elements equal to one and zero, respectively. (Example) C_sum^(c).T @ S.T = [ [1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 ...] [0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 ...] [0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 ...] ... ] In this function, the permutation is achieved by using matrix reshapes. 3. Set C_trans.T @ C_sum^(c) @ C_sum^(c).T @ C_trans = [(C_trans.T @ S.T) @ (S @ C_sum^(c))] @ [(C_sum^(c).T @ S.T) @ (S @ C_trans)] = [T_1, T_2, ..., T_NNN3333] @ (S @ C_sum^(c)) @ (C_sum^(c).T @ S.T) @ [T_1, T_2, ..., T_NNN3333].T = \sum_i t_i @ t_i.T, where t_i = \sum_c T_i[:, c]. t_i is represented by c_sum_cplmt.T in this function. T_i is the submatrix of size (N, n_aNNN333) of permuted C_trans. 4. Compute P^(c) = \sum_i (n_a_compress_mat.T @ t_i) @ (t_i.T @ n_a_compress_mat) 5. Compute P = I - P^(c) """ n_lp, natom = trans_perms.shape NNNN81 = natom**4 * 81 NNNN = natom**4 NNN = natom**3 proj_size = n_a_compress_mat.shape[1] # type: ignore proj_cplmt = csr_array((proj_size, proj_size), dtype="double") if atomic_decompr_idx is None: atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O4(trans_perms) decompr_idx = atomic_decompr_idx.reshape((natom, NNN)).T.reshape(-1) * 81 indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) nonzero = np.zeros((natom, natom, natom, natom), dtype=bool) nonzero[indep_atoms, :, :, :] = True nonzero = nonzero.reshape(-1) if fc_cutoff is not None: nonzero_c = fc_cutoff.nonzero_atomic_indices_fc4() nonzero_c = nonzero_c.reshape((natom, NNN)).T.reshape(-1) nonzero = nonzero & nonzero_c batch_size = optimize_batch_size_sum_rules_O4(natom, n_batch=n_batch) abcd = np.arange(81) for begin, end in zip(*get_batch_slice(NNNN, batch_size)): size = end - begin size_vector = size * 81 size_row = size_vector // natom nonzero_b = nonzero[begin:end] size_data = np.count_nonzero(nonzero_b) * 81 if size_data == 0: continue if verbose: print("Complementary P (Sum rule):", str(end) + "/" + str(NNNN), flush=True) decompr_idx_b = decompr_idx[begin:end][nonzero_b] c_sum_cplmt = csr_array( ( np.ones(size_data, dtype="double"), ( np.repeat(np.arange(size_row), natom)[np.tile(nonzero_b, 81)], (abcd[:, None] + decompr_idx_b[None, :]).reshape(-1), ), ), shape=(size_row, NNNN81 // n_lp), dtype="double", ) c_sum_cplmt = dot_product_sparse(c_sum_cplmt, n_a_compress_mat, use_mkl=use_mkl) proj_cplmt += dot_product_sparse(c_sum_cplmt.T, c_sum_cplmt, use_mkl=use_mkl) proj_cplmt /= natom return scipy.sparse.identity(proj_cplmt.shape[0]) - proj_cplmt def compressed_projector_sum_rules_O4_stable( trans_perms: np.ndarray, n_a_compress_mat: csr_array, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, n_batch: Optional[int] = None, use_mkl: bool = False, verbose: bool = False, ) -> csr_array: r"""Return projection matrix for translational sum rule. Calculate a compressed projector for translational sum rules. This compression is achieved using C_trans and n_a_compress_mat, without the need to allocate C_trans. The implementation utilizes get_atomic_lat_trans_decompr_indices_O4 to ensure efficient memory usage. Return ------ Compressed projector I - P^(c). I - P^(c) = n_a_compress_mat.T @ C_trans.T @ [I - C_sum^(c) @ C_sum^(c).T] @ C_trans @ n_a_compress_mat = I - [n_a_compress_mat.T @ C_trans.T @ C_sum^(c)] @ [C_sum^(c).T @ C_trans @ n_a_compress_mat] Algorithm --------- 1. C_sum^(c).T = [I, I, I, ...] of size (27N^3, 27N^4). I denotes the unit matrix of size (27N^3, 27N^3). C_sum^(c).T is composed of N unit matrices. In this representation, the translational sum rules are given by \sum_i FC4(i, j, k, l, a, b, c, d) = 0. 2. To divide the computation of a compressed projector into several batches, C_sum^(c) and C_trans are permuted from the index order of (i, j, k, l, a, b, c, d) to (a, b, c, d, j, k, l, i). This is represented by C_sum^(c).T @ C_trans = C_sum^(c).T @ S.T @ S @ C_trans, where S denotes the permutation matrix that changes the index order to (a, b, c, d, j, k, l, i). Using this permutation, the translational sum rules are represented as C_sum^(c).T @ S.T = [ [1_N.T, 0_N.T, 0_N.T, ...] [0_N.T, 1_N.T, 0_N.T, ...] [0_N.T, 0_N.T, 1_N.T, ...] ... ], where 1_N and 0_N are column vectors of size N with all elements equal to one and zero, respectively. (Example) C_sum^(c).T @ S.T = [ [1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 ...] [0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 ...] [0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 ...] ... ] In this function, the permutation is achieved by using matrix reshapes. 3. Set C_trans.T @ C_sum^(c) @ C_sum^(c).T @ C_trans = [(C_trans.T @ S.T) @ (S @ C_sum^(c))] @ [(C_sum^(c).T @ S.T) @ (S @ C_trans)] = [T_1, T_2, ..., T_NNN3333] @ (S @ C_sum^(c)) @ (C_sum^(c).T @ S.T) @ [T_1, T_2, ..., T_NNN3333].T = \sum_i t_i @ t_i.T, where t_i = \sum_c T_i[:, c]. t_i is represented by c_sum_cplmt.T in this function. T_i is the submatrix of size (N, n_aNNN333) of permuted C_trans. 4. Compute P^(c) = \sum_i (n_a_compress_mat.T @ t_i) @ (t_i.T @ n_a_compress_mat) 5. Compute P = I - P^(c) """ n_lp, natom = trans_perms.shape NNNN81 = natom**4 * 81 NNNN = natom**4 NNN = natom**3 proj_size = n_a_compress_mat.shape[1] # type: ignore proj_cplmt = csr_array((proj_size, proj_size), dtype="double") if atomic_decompr_idx is None: atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O4(trans_perms) decompr_idx = atomic_decompr_idx.reshape((natom, NNN)).T.reshape(-1) * 81 if fc_cutoff is not None: nonzero = fc_cutoff.nonzero_atomic_indices_fc4() nonzero = nonzero.reshape((natom, NNN)).T.reshape(-1) batch_size = optimize_batch_size_sum_rules_O4(natom, n_batch=n_batch) abcd = np.arange(81) for begin, end in zip(*get_batch_slice(NNNN, batch_size)): if verbose: print("Complementary P (Sum rule):", str(end) + "/" + str(NNNN), flush=True) size = end - begin size_vector = size * 81 size_row = size_vector // natom if fc_cutoff is None: c_sum_cplmt = csr_array( ( np.ones(size_vector, dtype="double"), ( np.repeat(np.arange(size_row), natom), (abcd[:, None] + decompr_idx[begin:end][None, :]).reshape(-1), ), ), shape=(size_row, NNNN81 // n_lp), dtype="double", ) else: nonzero_b = nonzero[begin:end] decompr_idx_b = decompr_idx[begin:end][nonzero_b] size_data = np.count_nonzero(nonzero_b) * 81 c_sum_cplmt = csr_array( ( np.ones(size_data, dtype="double"), ( np.repeat(np.arange(size_row), natom)[np.tile(nonzero_b, 81)], (abcd[:, None] + decompr_idx_b[None, :]).reshape(-1), ), ), shape=(size_row, NNNN81 // n_lp), dtype="double", ) c_sum_cplmt = dot_product_sparse(c_sum_cplmt, n_a_compress_mat, use_mkl=use_mkl) proj_cplmt += dot_product_sparse(c_sum_cplmt.T, c_sum_cplmt, use_mkl=use_mkl) proj_cplmt /= n_lp * natom return scipy.sparse.identity(proj_cplmt.shape[0]) - proj_cplmt symfc-1.6.0/src/symfc/utils/utils.py000066400000000000000000000262551511276756400174510ustar00rootroot00000000000000"""Utility functions.""" from __future__ import annotations from collections.abc import Sequence import numpy as np from numpy.typing import NDArray def get_indep_atoms_by_lat_trans(trans_perms: NDArray) -> NDArray: """Return independent atoms by lattice translation symmetry. Parameters ---------- trans_perms : NDArray Atom indices after lattice translations. shape=(lattice_translations, supercell_atoms) Returns ------- NDArray Independent atoms. shape=(n_indep_atoms_by_lattice_translation,), dtype=int """ unique_atoms: list[int] = [] assert np.array_equal(trans_perms[0, :], range(trans_perms.shape[1])) for i, perms in enumerate(trans_perms.T): is_found = False for j in unique_atoms: if j in perms: is_found = True break if not is_found: unique_atoms.append(i) return np.array(unique_atoms, dtype=int) def round_positions(positions: NDArray, tol: float = 1e-13, decimals: int = 5): """Round fractional coordinates of positions (-0.5 <= p < 0.5).""" positions_rint = positions - np.rint(positions) positions_rint[np.abs(positions_rint - 0.5) < tol] = -0.5 positions_rint = np.round(positions_rint, decimals) return positions_rint def argsort_positions(positions: NDArray, tol: float = 1e-13, decimals: int = 5): """Round and sort fractional coordinates of positions (-0.5 <= p < 0.5).""" positions_round = round_positions(positions, tol=tol, decimals=decimals) # Not needed part? positions_rint = 10**decimals * positions_round positions_rint = [tuple(p) for p in positions_rint.astype(int)] # Not needed part? (end) sorted_ids = sorted(range(positions.shape[0]), key=positions_rint.__getitem__) sorted_positions = positions_round[sorted_ids] return sorted_ids, sorted_positions def _find_optimal_decimals(positions: NDArray, tol: float = 1e-13): """Find optimal value of decimals used for sorting atoms.""" n_atom = positions.shape[0] for decimals in range(3, 15): positions_round = round_positions(positions, tol=tol, decimals=decimals) n_atom_uniq = len(set([tuple(pos) for pos in positions_round])) if n_atom_uniq == n_atom: return decimals raise RuntimeError("Optimal decimals not found.") def compute_sg_permutations( positions: NDArray, rotations: NDArray, translations: NDArray, lattice: NDArray, symprec: float = 1e-5, ) -> NDArray: """Compute permutations of atoms by space group operations in supercell. Permutations of atoms of pure translations and coset representatives are first computed. Then permutations of atoms of all the given space group operations are made as the combitation of these two permutations. Parameters ---------- positions : ndarray Fractional positions (like SymfcAtoms.scaled_positions) before applying the space group operation. rotations : ndarray Matrix (rotation) parts of space group operations. shape=(len(operations), 3, 3), dtype='intc' translations : ndarray Vector (translation) parts of space group operations. shape=(len(operations), 3), dtype='double' lattice : ndarray Basis vectors in column vectors (like SymfcAtoms.cell.T). symprec : float Symmetry tolerance of the distance unit. Returns ------- perms : ndarray shape=(len(translations), len(positions)), dtype='intc', order='C' """ trans_perms = [] pure_trans = [] n_atom = positions.shape[0] decimals = _find_optimal_decimals(positions) sorted_ids, sorted_positions = argsort_positions(positions, decimals=decimals) for r, t in zip(rotations, translations): if (r != np.eye(3, dtype=int)).any(): continue trans_positions = positions + t sorted_trans_ids, sorted_trans_positions = argsort_positions( trans_positions, decimals=decimals, ) if np.allclose(sorted_trans_positions - sorted_positions, 0.0): tp = np.zeros(n_atom, dtype=int) tp[sorted_trans_ids] = sorted_ids else: diffs = positions[None, :, :] - trans_positions[:, None, :] diffs -= np.rint(diffs) dists = np.linalg.norm(diffs @ lattice.T, axis=2) rows, cols = np.where(dists < symprec) assert len(positions) == len(np.unique(rows)) == len(np.unique(cols)) tp = cols[np.argsort(rows)] trans_perms.append(tp) pure_trans.append(t) trans_perms = np.array(trans_perms, dtype=int) pure_trans = np.array(pure_trans) unique_r = [] unique_t = [] r2ur = [] unique_rotated_positions = [] for r, t in zip(rotations, translations): is_found = False for j, ur in enumerate(unique_r): if (r == ur).all(): is_found = True r2ur.append(j) break if not is_found: r2ur.append(len(unique_r)) unique_r.append(r) unique_t.append(t) unique_rotated_positions.append(positions @ r.T + t) unique_rotation_perms = [] for rotated_positions in unique_rotated_positions: diffs = positions[None, :, :] - rotated_positions[:, None, :] diffs -= np.rint(diffs) dists = np.linalg.norm(diffs @ lattice.T, axis=2) rows, cols = np.where(dists < symprec) assert len(positions) == len(np.unique(rows)) == len(np.unique(cols)) unique_rotation_perms.append(cols[np.argsort(rows)]) unique_rotation_perms = np.array(unique_rotation_perms, dtype=int) out = [] for i, t in enumerate(translations): perms = unique_rotation_perms[r2ur[i]] lattice_trans = t - unique_t[r2ur[i]] diffs = pure_trans - lattice_trans diffs -= np.rint(diffs) dists = np.linalg.norm(diffs @ lattice.T, axis=1) lat_trans_idx = np.where(dists < symprec) assert len(lat_trans_idx) == 1 out.append(trans_perms[lat_trans_idx[0], perms]) out = np.array(out, dtype="intc", order="C") return out def compute_sg_permutations_stable( positions: NDArray, rotations: NDArray, translations: NDArray, lattice: NDArray, symprec: float = 1e-5, ) -> NDArray: """Compute permutations of atoms by space group operations in supercell. Permutations of atoms of pure translations and coset representatives are first computed. Then permutations of atoms of all the given space group operations are made as the combitation of these two permutations. Parameters ---------- positions : ndarray Fractional positions (like SymfcAtoms.scaled_positions) before applying the space group operation. rotations : ndarray Matrix (rotation) parts of space group operations. shape=(len(operations), 3, 3), dtype='intc' translations : ndarray Vector (translation) parts of space group operations. shape=(len(operations), 3), dtype='double' lattice : ndarray Basis vectors in column vectors (like SymfcAtoms.cell.T). symprec : float Symmetry tolerance of the distance unit. Returns ------- perms : ndarray shape=(len(translations), len(positions)), dtype='intc', order='C' """ trans_perms = [] pure_trans = [] """Bottleneck part""" for r, t in zip(rotations, translations): if (r != np.eye(3, dtype=int)).any(): continue trans_positions = positions + t diffs = positions[None, :, :] - trans_positions[:, None, :] diffs -= np.rint(diffs) dists = np.linalg.norm(diffs @ lattice.T, axis=2) rows, cols = np.where(dists < symprec) assert len(positions) == len(np.unique(rows)) == len(np.unique(cols)) trans_perms.append(cols[np.argsort(rows)]) pure_trans.append(t) trans_perms = np.array(trans_perms, dtype=int) pure_trans = np.array(pure_trans) unique_r = [] unique_t = [] r2ur = [] unique_rotated_positions = [] for r, t in zip(rotations, translations): is_found = False for j, ur in enumerate(unique_r): if (r == ur).all(): is_found = True r2ur.append(j) break if not is_found: r2ur.append(len(unique_r)) unique_r.append(r) unique_t.append(t) unique_rotated_positions.append(positions @ r.T + t) unique_rotation_perms = [] for rotated_positions in unique_rotated_positions: diffs = positions[None, :, :] - rotated_positions[:, None, :] diffs -= np.rint(diffs) dists = np.linalg.norm(diffs @ lattice.T, axis=2) rows, cols = np.where(dists < symprec) assert len(positions) == len(np.unique(rows)) == len(np.unique(cols)) unique_rotation_perms.append(cols[np.argsort(rows)]) unique_rotation_perms = np.array(unique_rotation_perms, dtype=int) out = [] for i, t in enumerate(translations): perms = unique_rotation_perms[r2ur[i]] lattice_trans = t - unique_t[r2ur[i]] diffs = pure_trans - lattice_trans diffs -= np.rint(diffs) dists = np.linalg.norm(diffs @ lattice.T, axis=1) lat_trans_idx = np.where(dists < symprec) assert len(lat_trans_idx) == 1 out.append(trans_perms[lat_trans_idx[0], perms]) out = np.array(out, dtype="intc", order="C") return out class SymfcAtoms: """Class to represent crystal structure mimicing PhonopyAtoms.""" def __init__( self, numbers: Sequence[int] | NDArray, scaled_positions: Sequence[Sequence[float]] | NDArray, cell: Sequence[Sequence[float]] | NDArray, ): """Init method.""" self._cell = np.array(cell, dtype="double") if self._cell.shape != (3, 3): raise ValueError("cell must be 3x3 array.") self._scaled_positions = np.array(scaled_positions, dtype="double") if self._scaled_positions.ndim != 2 or self._scaled_positions.shape[1] != 3: raise ValueError("scaled_positions must be Nx3 array.") self._numbers = np.array(numbers, dtype="intc") if ( self._numbers.ndim != 1 or self._numbers.shape[0] != self._scaled_positions.shape[0] ): raise ValueError( "numbers must be 1D array with the same length as scaled_positions." ) def __len__(self) -> int: """Return number of atoms.""" return len(self.numbers) @property def cell(self) -> NDArray: """Setter and getter of basis vectors. For getter, copy is returned.""" return self._cell.copy() @property def scaled_positions(self) -> NDArray: """Setter and getter of scaled positions. For getter, copy is returned.""" return self._scaled_positions.copy() @property def numbers(self) -> NDArray: """Setter and getter of atomic numbers. For getter, copy is returned.""" return self._numbers.copy() def totuple(self) -> tuple[NDArray, NDArray, NDArray]: """Return (cell, scaled_position, numbers).""" return (self._cell, self._scaled_positions, self._numbers) symfc-1.6.0/src/symfc/utils/utils_O1.py000066400000000000000000000117701511276756400200040ustar00rootroot00000000000000"""Utility functions for 1st order force constants.""" import numpy as np from scipy.sparse import csr_array, kron from symfc.spg_reps import SpgRepsO1 from .utils import get_indep_atoms_by_lat_trans def get_lat_trans_decompr_indices(trans_perms: np.ndarray) -> np.ndarray: """Return indices to de-compress compressed matrix by lat-trans-sym. Usage ----- vec[indices] of shape (n_a*3,) gives an array of shape=(N*3,). 1/sqrt(n_lp) must be multiplied manually after decompression to mimic get_lat_trans_compr_matrix. Parameters ---------- trans_perms : ndarray Permutation of atomic indices by lattice translational symmetry. dtype='intc'. shape=(n_l, N), where n_l and N are the numbers of lattce points and atoms in supercell. Returns ------- indices : ndarray Indices of n_a * 3 elements. shape=(N*3,), dtype='int_'. """ indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) n_a = len(indep_atoms) N = trans_perms.shape[1] n_lp = N // n_a size_row = N * 3 n = 0 indices = np.zeros(size_row, dtype="int_") for i_patom in indep_atoms: index_shift = trans_perms[:, i_patom] * 3 for a in range(3): indices[index_shift + a] = n n += 1 assert n * n_lp == size_row return indices def get_lat_trans_compr_indices(trans_perms: np.ndarray) -> np.ndarray: """Return indices to compress matrix by lat-trans-sym. Usage ----- vec[indices] of shape (N*3,) vec gives an array of shape=(n_a*3, n_lp). 1/sqrt(n_lp) must be multiplied manually after compression to mimic get_lat_trans_compr_matrix. Parameters ---------- trans_perms : ndarray Permutation of atomic indices by lattice translational symmetry. dtype='intc'. shape=(n_l, N), where n_l and N are the numbers of lattce points and atoms in supercell. Returns ------- indices : ndarray shape=(n_a*N9, n_lp), dtype='int_'. """ indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) n_a = len(indep_atoms) N = trans_perms.shape[1] n_lp = N // n_a size_row = N * 3 n = 0 indices = np.zeros((n_a * 3, n_lp), dtype="int_") for i_patom in indep_atoms: for a in range(3): indices[n, :] = trans_perms[:, i_patom] * 3 + a n += 1 assert n * n_lp == size_row return indices def get_lat_trans_compr_matrix(decompr_idx: np.ndarray, N: int, n_lp: int) -> csr_array: """Return compression matrix by lattice translation symmetry. `decompr_idx` is obtained by `get_lat_trans_decompr_indices`. Matrix shape is (N3, n_a*3), where n_a is the number of independent atoms by lattice translation symmetry. Data order is (N, 3, n_a, 3) if it is in dense array. """ N3 = N * 3 compression_mat = csr_array( ( np.full(N3, 1 / np.sqrt(n_lp), dtype="double"), (np.arange(N3, dtype=int), decompr_idx), ), shape=(N3, N3 // n_lp), dtype="double", ) return compression_mat def _get_atomic_lat_trans_decompr_indices(trans_perms: np.ndarray) -> np.ndarray: """Return indices to de-compress compressed matrix by atom-lat-trans-sym. This is atomic permutation only version of get_lat_trans_decompr_indices. Usage ----- vec[indices] of shape (n_a,) gives an array of shape=(N,). 1/sqrt(n_lp) must be multiplied manually after decompression. Parameters ---------- trans_perms : ndarray Permutation of atomic indices by lattice translational symmetry. dtype='intc'. shape=(n_l, N), where n_l and N are the numbers of lattce points and atoms in supercell. Returns ------- indices : ndarray Indices of n_a * N elements. shape=(N^2*,), dtype='int_'. """ indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) n_lp, N = trans_perms.shape size_row = N n = 0 indices = np.zeros(size_row, dtype="int_") for i_patom in indep_atoms: index_shift = trans_perms[:, i_patom] indices[index_shift] = n n += 1 assert n * n_lp == size_row return indices def get_compr_coset_reps_sum(spg_reps: SpgRepsO1): """Return compressed projector of coset reps sum.""" trans_perms = spg_reps.translation_permutations n_lp, N = trans_perms.shape size = N * 3 // n_lp coset_reps_sum = csr_array(([], ([], [])), shape=(size, size), dtype="double") atomic_decompr_idx = _get_atomic_lat_trans_decompr_indices(trans_perms) C = csr_array( ( np.ones(N, dtype=int), (np.arange(N, dtype=int), atomic_decompr_idx), ), shape=(N, N // n_lp), ) factor = 1 / n_lp / len(spg_reps.unique_rotation_indices) for i, _ in enumerate(spg_reps.unique_rotation_indices): mat = spg_reps.get_sigma1_rep(i) mat = mat @ C mat = C.T @ mat coset_reps_sum += kron(mat, spg_reps.r_reps[i] * factor) return coset_reps_sum symfc-1.6.0/src/symfc/utils/utils_O2.py000066400000000000000000000237431511276756400200100ustar00rootroot00000000000000"""Utility functions for 2nd order force constants.""" import itertools from typing import Optional import numpy as np from scipy.sparse import csr_array, kron from symfc.spg_reps import SpgRepsO2 from symfc.utils.cutoff_tools import FCCutoff from .utils import get_indep_atoms_by_lat_trans def get_lat_trans_decompr_indices(trans_perms: np.ndarray) -> np.ndarray: """Return indices to de-compress compressed matrix by lat-trans-sym. Usage ----- vec[indices] of shape (n_a*N*9,) gives an array of shape=(N**2*9,). 1/sqrt(n_lp) must be multiplied manually after decompression to mimic get_lat_trans_compr_matrix. Parameters ---------- trans_perms : ndarray Permutation of atomic indices by lattice translational symmetry. dtype='intc'. shape=(n_l, N), where n_l and N are the numbers of lattice points and atoms in supercell. Returns ------- indices : ndarray Indices of n_a * N9 elements. shape=(N^2*9,), dtype='int_'. """ indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) n_a = len(indep_atoms) N = trans_perms.shape[1] n_lp = N // n_a size_row = (N * 3) ** 2 n = 0 indices = np.zeros(size_row, dtype="int_") for i_patom in indep_atoms: index_shift_i = trans_perms[:, i_patom] * N * 9 for j in range(N): index_shift = index_shift_i + trans_perms[:, j] * 9 for ab in range(9): indices[index_shift + ab] = n n += 1 assert n * n_lp == size_row return indices def get_lat_trans_compr_indices(trans_perms: np.ndarray) -> np.ndarray: """Return indices to compress matrix by lat-trans-sym. Usage ----- vec[indices] of shape (N**2*9,) vec gives an array of shape=(n_a*N*9, n_lp). 1/sqrt(n_lp) must be multiplied manually after compression to mimic get_lat_trans_compr_matrix. Parameters ---------- trans_perms : ndarray Permutation of atomic indices by lattice translational symmetry. dtype='intc'. shape=(n_l, N), where n_l and N are the numbers of lattice points and atoms in supercell. Returns ------- indices : ndarray shape=(n_a*N9, n_lp), dtype='int_'. """ indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) n_a = len(indep_atoms) N = trans_perms.shape[1] n_lp = N // n_a size_row = (N * 3) ** 2 n = 0 indices = np.zeros((n_a * N * 9, n_lp), dtype="int_") for i_patom in indep_atoms: for j in range(N): for ab in range(9): indices[n, :] = ( trans_perms[:, i_patom] * 9 * N + trans_perms[:, j] * 9 + ab ) n += 1 assert n * n_lp == size_row return indices def get_lat_trans_compr_matrix(decompr_idx: np.ndarray, N: int, n_lp: int) -> csr_array: """Return compression matrix by lattice translation symmetry. `decompr_idx` is obtained by `get_lat_trans_decompr_indices`. Matrix shape is (NN33, n_a*N33), where n_a is the number of independent atoms by lattice translation symmetry. Data order is (N, N, 3, 3, n_a, N, 3, 3) if it is in dense array. """ NN9 = N**2 * 9 compression_mat = csr_array( ( np.full(NN9, 1 / np.sqrt(n_lp), dtype="double"), (np.arange(NN9, dtype=int), decompr_idx), ), shape=(NN9, NN9 // n_lp), dtype="double", ) return compression_mat def get_lat_trans_compr_matrix_O2(trans_perms: np.ndarray): """Return lat trans compression matrix.""" n_lp, N = trans_perms.shape decompr_idx = get_lat_trans_decompr_indices(trans_perms) c_trans = get_lat_trans_compr_matrix(decompr_idx, N, n_lp) return c_trans def get_perm_compr_matrix(natom: int) -> csr_array: """Return compression matrix by permutation symmetry. Parameters ---------- natom : int Number of atoms in supercell. Matrix shape is (NN33,(N*3)(N*3+1)/2). Non-zero only ijab and jiba column elements for ijab rows. Rows upper right NN33 matrix elements are selected for rows. For the computational performance, get_perm_compr_matrix is implemented in a tricky way effectively using numpy features, and so it is not easy to read. What is expected may be found reading _get_perm_compr_matrix_reference. """ N = natom A = np.transpose( np.arange(N**2 * 9).reshape(N, N, 3, 3), axes=(0, 2, 1, 3) ).reshape(N * 3, N * 3) ut = np.triu_indices_from(A, k=1) diag = np.diagonal(A) row = np.hstack((np.stack((A[ut], A.T[ut]), axis=1).ravel(), diag)) col = np.hstack( ( np.repeat(np.arange(len(ut[0]), dtype=int), 2), np.arange(len(ut[0]), len(ut[0]) + len(diag), dtype=int), ) ) data = np.hstack((np.full(len(ut[0]) * 2, np.sqrt(2) / 2), np.full(len(diag), 1))) return csr_array( (data, (row, col)), shape=(N**2 * 9, (N * 3 * (N * 3 + 1)) // 2), dtype="double", ) def _get_atomic_lat_trans_decompr_indices(trans_perms: np.ndarray) -> np.ndarray: """Return indices to de-compress compressed matrix by atom-lat-trans-sym. This is atomic permutation only version of get_lat_trans_decompr_indices. Usage ----- vec[indices] of shape (n_a*N,) gives an array of shape=(N**2,). 1/sqrt(n_lp) must be multiplied manually after decompression. Parameters ---------- trans_perms : ndarray Permutation of atomic indices by lattice translational symmetry. dtype='intc'. shape=(n_l, N), where n_l and N are the numbers of lattice points and atoms in supercell. Returns ------- indices : ndarray Indices of n_a * N elements. shape=(N^2*,), dtype='int_'. """ indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) n_lp, N = trans_perms.shape size_row = N**2 n = 0 indices = np.zeros(size_row, dtype="int_") for i_patom in indep_atoms: index_shift_i = trans_perms[:, i_patom] * N for j in range(N): index_shift = index_shift_i + trans_perms[:, j] indices[index_shift] = n n += 1 assert n * n_lp == size_row return indices def get_compr_coset_reps_sum(spg_reps: SpgRepsO2): """Return compressed projector of coset reps sum.""" trans_perms = spg_reps.translation_permutations n_lp, N = trans_perms.shape size = N**2 * 9 // n_lp coset_reps_sum = csr_array(([], ([], [])), shape=(size, size), dtype="double") atomic_decompr_idx = _get_atomic_lat_trans_decompr_indices(trans_perms) C = csr_array( ( np.ones(N**2, dtype=int), (np.arange(N**2, dtype=int), atomic_decompr_idx), ), shape=(N**2, N**2 // n_lp), ) factor = 1 / n_lp / len(spg_reps.unique_rotation_indices) for i, _ in enumerate(spg_reps.unique_rotation_indices): mat = spg_reps.get_sigma2_rep(i) mat = mat @ C mat = C.T @ mat coset_reps_sum += kron(mat, spg_reps.r_reps[i] * factor) return coset_reps_sum def _get_perm_compr_matrix_reference(natom: int) -> csr_array: """Return compression matrix by permutation symmetry. This is a reference implementation of get_perm_compr_matrix. The order of columns is difference from get_perm_compr_matrix, but it is OK if C.T@C is the same. Matrix shape is (NN33,(N*3)(N*3+1)/2). Non-zero only ijab and jiba column elements for ijab rows. Rows upper right NN33 matrix elements are selected for rows. """ def to_serial(i: int, a: int, j: int, b: int, natom: int) -> int: """Return NN33-1D index.""" return (i * 9 * natom) + (j * 9) + (a * 3) + b col, row, data = [], [], [] val = np.sqrt(2) / 2 size_row = natom**2 * 9 n = 0 for ia, jb in itertools.combinations_with_replacement(range(natom * 3), 2): i_i = ia // 3 i_a = ia % 3 i_j = jb // 3 i_b = jb % 3 col.append(n) row.append(to_serial(i_i, i_a, i_j, i_b, natom)) if i_i == i_j and i_a == i_b: data.append(1) else: data.append(val) col.append(n) row.append(to_serial(i_j, i_b, i_i, i_a, natom)) data.append(val) n += 1 if (natom * 3) % 2 == 1: assert (natom * 3) * ((natom * 3 + 1) // 2) == n, f"{natom}, {n}" else: assert ((natom * 3) // 2) * (natom * 3 + 1) == n, f"{natom}, {n}" return csr_array((data, (row, col)), shape=(size_row, n), dtype="double") def get_compr_coset_projector_O2( spg_reps: SpgRepsO2, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, c_pt: Optional[csr_array] = None, ) -> csr_array: """Return compr matrix of sum of coset reps.""" trans_perms = spg_reps.translation_permutations n_lp, N = trans_perms.shape size = N**2 * 9 // n_lp if c_pt is None else c_pt.shape[1] # type: ignore coset_reps_sum = csr_array((size, size), dtype="double") if atomic_decompr_idx is None: _atomic_decompr_idx = _get_atomic_lat_trans_decompr_indices(trans_perms) else: _atomic_decompr_idx = atomic_decompr_idx if fc_cutoff is None: nonzero = None size_data = N**2 col = _atomic_decompr_idx else: nonzero = fc_cutoff.nonzero_atomic_indices_fc2() size_data = np.count_nonzero(nonzero) col = _atomic_decompr_idx[nonzero] factor = 1 / n_lp / len(spg_reps.unique_rotation_indices) for i, _ in enumerate(spg_reps.unique_rotation_indices): permutation = spg_reps.get_sigma2_rep(i, nonzero=nonzero) mat = csr_array( ( np.ones(size_data, dtype="int_"), (_atomic_decompr_idx[permutation], col), # type: ignore ), shape=(N**2 // n_lp, N**2 // n_lp), dtype="int_", ) mat = kron(mat, spg_reps.r_reps[i] * factor) if c_pt is not None: mat = c_pt.T @ mat @ c_pt coset_reps_sum += mat return coset_reps_sum symfc-1.6.0/src/symfc/utils/utils_O3.py000066400000000000000000000205331511276756400200030ustar00rootroot00000000000000"""Utility functions for 3rd order force constants.""" from typing import Optional import numpy as np from scipy.sparse import csr_array, kron from symfc.spg_reps.spg_reps_O3 import SpgRepsO3 from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.utils import get_indep_atoms_by_lat_trans try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass def get_atomic_lat_trans_decompr_indices_O3(trans_perms: np.ndarray) -> np.ndarray: """Return indices to de-compress compressed matrix by atom-lat-trans-sym. This is atomic permutation only version of get_lat_trans_decompr_indices. Usage ----- vec[indices] of shape (n_a*N*N,) gives an array of shape=(N**3,). 1/sqrt(n_lp) must be multiplied manually after decompression. Parameters ---------- trans_perms : ndarray Permutation of atomic indices by lattice translational symmetry. dtype='intc'. shape=(n_l, N), where n_l and N are the numbers of lattice points and atoms in supercell. Returns ------- indices : ndarray Indices of n_a * N * N elements. shape=(N**3,), dtype='int_'. """ indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) n_lp, N = trans_perms.shape size_row = N**3 n = 0 indices = np.zeros(size_row, dtype="int_") for i_patom in indep_atoms: index_shift_i = trans_perms[:, i_patom] * N**2 for j in range(N): index_shift_j = index_shift_i + trans_perms[:, j] * N for k in range(N): index_shift = index_shift_j + trans_perms[:, k] indices[index_shift] = n n += 1 assert n * n_lp == size_row return indices def get_lat_trans_decompr_indices_O3(trans_perms: np.ndarray) -> np.ndarray: """Return indices to de-compress compressed matrix by lat-trans-sym. Usage ----- vec[indices] of shape (n_a*N*N*27,) gives an array of shape=(N**3*27,). 1/sqrt(n_lp) must be multiplied manually after decompression to mimic get_lat_trans_compr_matrix. Parameters ---------- trans_perms : ndarray Permutation of atomic indices by lattice translational symmetry. dtype='intc'. shape=(n_l, N), where n_l and N are the numbers of lattice points and atoms in supercell. Returns ------- indices : ndarray Indices of n_a * N * N * 27 elements. shape=(N^3*27,), dtype='int_'. """ indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) n_a = len(indep_atoms) N = trans_perms.shape[1] n_lp = N // n_a size_row = 27 * N**3 trans_perms = trans_perms.astype("int_") n = 0 indices = np.zeros(size_row, dtype="int_") for i_patom in indep_atoms: index_shift_i = trans_perms[:, i_patom] * N**2 * 27 for j in range(N): index_shift_j = index_shift_i + trans_perms[:, j] * N * 27 for k in range(N): index_shift = index_shift_j + trans_perms[:, k] * 27 for ab in range(27): indices[index_shift + ab] = n n += 1 assert n * n_lp == size_row return indices def get_lat_trans_compr_matrix_O3(trans_perms): """Return lat trans compression matrix.""" n_lp, N = trans_perms.shape decompr_idx = get_lat_trans_decompr_indices_O3(trans_perms) c_trans = _get_lat_trans_compr_matrix_O3(decompr_idx, N, n_lp) return c_trans def _get_lat_trans_compr_matrix_O3( decompr_idx: np.ndarray, N: int, n_lp: int ) -> csr_array: """Return compression matrix by lattice translation symmetry. `decompr_idx` is obtained by `get_lat_trans_decompr_indices`. Matrix shape is (NNN333, n_a*NN333), where n_a is the number of independent atoms by lattice translation symmetry. Data order is (N, N, N, 3, 3, 3, n_a, N, N, 3, 3, 3) if it is in dense array. """ NNN27 = N**3 * 27 compression_mat = csr_array( ( np.full(NNN27, 1 / np.sqrt(n_lp), dtype="double"), (np.arange(NNN27, dtype=int), decompr_idx), ), shape=(NNN27, NNN27 // n_lp), dtype="double", ) return compression_mat def get_compr_coset_projector_O3( spg_reps: SpgRepsO3, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, c_pt: Optional[csr_array] = None, use_mkl: bool = False, verbose: bool = False, ) -> csr_array: """Return compr matrix of sum of coset reps.""" trans_perms = spg_reps.translation_permutations n_lp, N = trans_perms.shape size = N**3 * 27 // n_lp if c_pt is None else c_pt.shape[1] # type: ignore indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) if atomic_decompr_idx is None: atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O3(trans_perms) nonzero_indep_atom = np.zeros(N**3, dtype=bool) atom_indices = np.arange(N**3) // N**2 for i in indep_atoms: nonzero_indep_atom[atom_indices == i] = True if fc_cutoff is None: nonzero = nonzero_indep_atom else: nonzero = fc_cutoff.nonzero_atomic_indices_fc3() nonzero = nonzero & nonzero_indep_atom size_data = np.count_nonzero(nonzero) col = atomic_decompr_idx[nonzero] n_cosets = min([int(np.sqrt(len(spg_reps.unique_rotation_indices))), 4]) cosets = [csr_array(([], ([], [])), shape=(size, size), dtype="double")] * n_cosets factor = 1 / len(spg_reps.unique_rotation_indices) for i, _ in enumerate(spg_reps.unique_rotation_indices): if verbose: n_rot = len(spg_reps.unique_rotation_indices) print("Coset sum:", i + 1, "/", n_rot, flush=True) permutation = spg_reps.get_sigma3_rep(i, nonzero=nonzero) """Equivalent to mat = C.T @ spg_reps.get_sigma3_rep(i) @ C C: atomic_lat_trans_compr_mat, shape=(NNN, NNN/n_lp)""" mat = csr_array( ( np.ones(size_data, dtype="int_"), (atomic_decompr_idx[permutation], col), ), shape=(N**3 // n_lp, N**3 // n_lp), dtype="int_", ) mat = kron(mat, spg_reps.r_reps[i] * factor).tocsr() if c_pt is not None: mat = dot_product_sparse(c_pt.T, mat, use_mkl=use_mkl) mat = dot_product_sparse(mat, c_pt, use_mkl=use_mkl) cosets[i % n_cosets] += mat return sum(cosets) # type: ignore def get_compr_coset_projector_O3_stable( spg_reps: SpgRepsO3, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, c_pt: Optional[csr_array] = None, use_mkl: bool = False, verbose: bool = False, ) -> csr_array: """Return compr matrix of sum of coset reps.""" trans_perms = spg_reps.translation_permutations n_lp, N = trans_perms.shape size = N**3 * 27 // n_lp if c_pt is None else c_pt.shape[1] # type: ignore if atomic_decompr_idx is None: atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O3(trans_perms) if fc_cutoff is None: nonzero = None size_data = N**3 col = atomic_decompr_idx else: nonzero = fc_cutoff.nonzero_atomic_indices_fc3() size_data = np.count_nonzero(nonzero) col = atomic_decompr_idx[nonzero] n_cosets = min([int(np.sqrt(len(spg_reps.unique_rotation_indices))), 4]) cosets = [csr_array(([], ([], [])), shape=(size, size), dtype="double")] * n_cosets factor = 1 / n_lp / len(spg_reps.unique_rotation_indices) for i, _ in enumerate(spg_reps.unique_rotation_indices): if verbose: n_rot = len(spg_reps.unique_rotation_indices) print("Coset sum:", i + 1, "/", n_rot, flush=True) permutation = spg_reps.get_sigma3_rep(i, nonzero=nonzero) """Equivalent to mat = C.T @ spg_reps.get_sigma3_rep(i) @ C C: atomic_lat_trans_compr_mat, shape=(NNN, NNN/n_lp)""" mat = csr_array( ( np.ones(size_data, dtype="int_"), (atomic_decompr_idx[permutation], col), ), shape=(N**3 // n_lp, N**3 // n_lp), dtype="int_", ) mat = kron(mat, spg_reps.r_reps[i] * factor).tocsr() if c_pt is not None: mat = dot_product_sparse(c_pt.T, mat, use_mkl=use_mkl) mat = dot_product_sparse(mat, c_pt, use_mkl=use_mkl) cosets[i % n_cosets] += mat return sum(cosets) # type: ignore symfc-1.6.0/src/symfc/utils/utils_O4.py000066400000000000000000000211611511276756400200020ustar00rootroot00000000000000"""Utility functions for 4th order force constants.""" from typing import Optional import numpy as np from scipy.sparse import csr_array, kron from symfc.spg_reps import SpgRepsO4 from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.utils import get_indep_atoms_by_lat_trans try: from symfc.utils.matrix import dot_product_sparse except ImportError: pass def get_atomic_lat_trans_decompr_indices_O4(trans_perms: np.ndarray) -> np.ndarray: """Return indices to de-compress compressed matrix by atom-lat-trans-sym. This is atomic permutation only version of get_lat_trans_decompr_indices. Usage ----- vec[indices] of shape (n_a*N*N*N,) gives an array of shape=(N**4,). 1/sqrt(n_lp) must be multiplied manually after decompression. Parameters ---------- trans_perms : ndarray Permutation of atomic indices by lattice translational symmetry. dtype='intc'. shape=(n_l, N), where n_l and N are the numbers of lattice points and atoms in supercell. Returns ------- indices : ndarray Indices of n_a * N * N * N elements. shape=(N**4,), dtype='int_'. """ indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) n_lp, N = trans_perms.shape size_row = N**4 n = 0 indices = np.zeros(size_row, dtype="int_") for i_patom in indep_atoms: index_shift_i = trans_perms[:, i_patom] * N**3 for j in range(N): index_shift_j = index_shift_i + trans_perms[:, j] * N**2 for k in range(N): index_shift_k = index_shift_j + trans_perms[:, k] * N for ll in range(N): index_shift = index_shift_k + trans_perms[:, ll] indices[index_shift] = n n += 1 assert n * n_lp == size_row return indices def get_lat_trans_decompr_indices_O4(trans_perms: np.ndarray) -> np.ndarray: """Return indices to de-compress compressed matrix by lat-trans-sym. Usage ----- vec[indices] of shape (n_a*N*N*N*81,) gives an array of shape=(N**4*81,). 1/sqrt(n_lp) must be multiplied manually after decompression to mimic get_lat_trans_compr_matrix. Parameters ---------- trans_perms : ndarray Permutation of atomic indices by lattice translational symmetry. dtype='intc'. shape=(n_l, N), where n_l and N are the numbers of lattice points and atoms in supercell. Returns ------- indices : ndarray Indices of n_a * N * N * N * 81 elements. shape=(N^4*81,), dtype='int_'. """ indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) n_a = len(indep_atoms) N = trans_perms.shape[1] n_lp = N // n_a size_row = 81 * N**4 trans_perms = trans_perms.astype("int_") n = 0 indices = np.zeros(size_row, dtype="int_") for i_patom in indep_atoms: index_shift_i = trans_perms[:, i_patom] * N**3 * 81 for j in range(N): index_shift_j = index_shift_i + trans_perms[:, j] * N**2 * 81 for k in range(N): index_shift_k = index_shift_j + trans_perms[:, k] * N * 81 for ll in range(N): index_shift = index_shift_k + trans_perms[:, ll] * 81 for ab in range(81): indices[index_shift + ab] = n n += 1 assert n * n_lp == size_row return indices def get_lat_trans_compr_matrix_O4(trans_perms): """Return lat trans compression matrix.""" n_lp, N = trans_perms.shape decompr_idx = get_lat_trans_decompr_indices_O4(trans_perms) c_trans = _get_lat_trans_compr_matrix_O4(decompr_idx, N, n_lp) return c_trans def _get_lat_trans_compr_matrix_O4( decompr_idx: np.ndarray, N: int, n_lp: int ) -> csr_array: """Return compression matrix by lattice translation symmetry. `decompr_idx` is obtained by `get_lat_trans_decompr_indices`. Matrix shape is (NNNN3333, n_a*NNN3333), where n_a is the number of independent atoms by lattice translation symmetry. Data order is (N, N, N, N, 3, 3, 3, 3, n_a, N, N, N, 3, 3, 3, 3) if it is in dense array. """ NNNN81 = N**4 * 81 compression_mat = csr_array( ( np.full(NNNN81, 1 / np.sqrt(n_lp), dtype="double"), (np.arange(NNNN81, dtype=int), decompr_idx), ), shape=(NNNN81, NNNN81 // n_lp), dtype="double", ) return compression_mat def get_compr_coset_projector_O4( spg_reps: SpgRepsO4, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, c_pt: Optional[csr_array] = None, use_mkl: bool = False, verbose: bool = False, ) -> csr_array: """Return compr projector of sum of coset reps.""" trans_perms = spg_reps.translation_permutations n_lp, N = trans_perms.shape size = N**4 * 81 // n_lp if c_pt is None else c_pt.shape[1] # type: ignore indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) if atomic_decompr_idx is None: atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O4(trans_perms) nonzero_indep_atom = np.zeros(N**4, dtype=bool) atom_indices = np.arange(N**4) // N**3 for i in indep_atoms: nonzero_indep_atom[atom_indices == i] = True if fc_cutoff is None: nonzero = nonzero_indep_atom else: nonzero = fc_cutoff.nonzero_atomic_indices_fc4() nonzero = nonzero & nonzero_indep_atom size_data = np.count_nonzero(nonzero) col = atomic_decompr_idx[nonzero] n_cosets = min([int(np.sqrt(len(spg_reps.unique_rotation_indices))), 4]) cosets = [csr_array(([], ([], [])), shape=(size, size), dtype="double")] * n_cosets factor = 1 / len(spg_reps.unique_rotation_indices) for i, _ in enumerate(spg_reps.unique_rotation_indices): if verbose: n_rot = len(spg_reps.unique_rotation_indices) print("Coset sum:", i + 1, "/", n_rot, flush=True) permutation = spg_reps.get_sigma4_rep(i, nonzero=nonzero) """Equivalent to mat = C.T @ spg_reps.get_sigma4_rep(i) @ C C: atomic_lat_trans_compr_mat, shape=(NNNN, NNNN/n_lp)""" mat = csr_array( ( np.ones(size_data, dtype="int_"), (atomic_decompr_idx[permutation], col), ), shape=(N**4 // n_lp, N**4 // n_lp), dtype="int_", ) mat = kron(mat, spg_reps.r_reps[i] * factor).tocsr() if c_pt is not None: mat = dot_product_sparse(c_pt.T, mat, use_mkl=use_mkl) mat = dot_product_sparse(mat, c_pt, use_mkl=use_mkl) cosets[i % n_cosets] += mat return sum(cosets) # type: ignore def get_compr_coset_projector_O4_stable( spg_reps: SpgRepsO4, atomic_decompr_idx: Optional[np.ndarray] = None, fc_cutoff: Optional[FCCutoff] = None, c_pt: Optional[csr_array] = None, use_mkl: bool = False, verbose: bool = False, ) -> csr_array: """Return compr projector of sum of coset reps.""" trans_perms = spg_reps.translation_permutations n_lp, N = trans_perms.shape size = N**4 * 81 // n_lp if c_pt is None else c_pt.shape[1] # type: ignore if atomic_decompr_idx is None: atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O4(trans_perms) if fc_cutoff is None: nonzero = None size_data = N**4 col = atomic_decompr_idx else: nonzero = fc_cutoff.nonzero_atomic_indices_fc4() size_data = np.count_nonzero(nonzero) col = atomic_decompr_idx[nonzero] n_cosets = min([int(np.sqrt(len(spg_reps.unique_rotation_indices))), 4]) cosets = [csr_array(([], ([], [])), shape=(size, size), dtype="double")] * n_cosets factor = 1 / n_lp / len(spg_reps.unique_rotation_indices) for i, _ in enumerate(spg_reps.unique_rotation_indices): if verbose: n_rot = len(spg_reps.unique_rotation_indices) print("Coset sum:", i + 1, "/", n_rot, flush=True) permutation = spg_reps.get_sigma4_rep(i, nonzero=nonzero) """Equivalent to mat = C.T @ spg_reps.get_sigma4_rep(i) @ C C: atomic_lat_trans_compr_mat, shape=(NNNN, NNNN/n_lp)""" mat = csr_array( ( np.ones(size_data, dtype="int_"), (atomic_decompr_idx[permutation], col), ), shape=(N**4 // n_lp, N**4 // n_lp), dtype="int_", ) mat = kron(mat, spg_reps.r_reps[i] * factor).tocsr() if c_pt is not None: mat = dot_product_sparse(c_pt.T, mat, use_mkl=use_mkl) mat = dot_product_sparse(mat, c_pt, use_mkl=use_mkl) cosets[i % n_cosets] += mat return sum(cosets) # type: ignore symfc-1.6.0/src/symfc/version.py000066400000000000000000000000741511276756400166250ustar00rootroot00000000000000"""Version number of the package.""" __version__ = "1.6.0" symfc-1.6.0/tests/000077500000000000000000000000001511276756400140175ustar00rootroot00000000000000symfc-1.6.0/tests/NaCl-unitcell.yaml000066400000000000000000000022031511276756400173320ustar00rootroot00000000000000lattice: - [ 5.690301476175671, 0.000000000000000, 0.000000000000000 ] # a - [ 0.000000000000000, 5.690301476175671, 0.000000000000000 ] # b - [ 0.000000000000000, 0.000000000000000, 5.690301476175671 ] # c points: - symbol: Na # 1 coordinates: [ 0.000000000000000, 0.000000000000000, 0.000000000000000 ] mass: 22.989769 - symbol: Na # 2 coordinates: [ 0.000000000000000, 0.500000000000000, 0.500000000000000 ] mass: 22.989769 - symbol: Na # 3 coordinates: [ 0.500000000000000, 0.000000000000000, 0.500000000000000 ] mass: 22.989769 - symbol: Na # 4 coordinates: [ 0.500000000000000, 0.500000000000000, 0.000000000000000 ] mass: 22.989769 - symbol: Cl # 5 coordinates: [ 0.500000000000000, 0.500000000000000, 0.500000000000000 ] mass: 35.453000 - symbol: Cl # 6 coordinates: [ 0.500000000000000, 0.000000000000000, 0.000000000000000 ] mass: 35.453000 - symbol: Cl # 7 coordinates: [ 0.000000000000000, 0.500000000000000, 0.000000000000000 ] mass: 35.453000 - symbol: Cl # 8 coordinates: [ 0.000000000000000, 0.000000000000000, 0.500000000000000 ] mass: 35.453000 symfc-1.6.0/tests/basis_sets/000077500000000000000000000000001511276756400161565ustar00rootroot00000000000000symfc-1.6.0/tests/basis_sets/test_basis_sets_O2.py000066400000000000000000000112751511276756400222740ustar00rootroot00000000000000"""Tests of FCBasisSetO2.""" from __future__ import annotations from pathlib import Path import numpy as np import pytest from symfc.basis_sets import FCBasisSetO2 from symfc.solvers.solver_O2 import FCSolverO2 from symfc.utils.utils import SymfcAtoms cwd = Path(__file__).parent def test_fc_basis_set_o2(): """Test symmetry adapted basis sets of FCBasisSetO2.""" lattice = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 2]]) positions = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) numbers = [1, 1] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) sbs = FCBasisSetO2(supercell, log_level=1).run() np.testing.assert_allclose( np.sort(sbs.basis_set), [[-np.sqrt(2) / 2], [np.sqrt(2) / 2]] ) comp_mat = sbs.compression_matrix proj = comp_mat @ comp_mat.T np.testing.assert_allclose(proj.data, [1.0 / 6.0] * proj.size) ref_col = [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0] ref_row = [0, 4, 8, 9, 13, 17, 18, 22, 26, 27, 31, 35] np.testing.assert_array_equal(comp_mat.tocoo().col, ref_col) np.testing.assert_array_equal(comp_mat.tocoo().row, ref_row) compact_comp_mat = sbs.compact_compression_matrix compact_proj = compact_comp_mat @ compact_comp_mat.T np.testing.assert_allclose(compact_proj.data, [1.0 / 6.0] * compact_proj.size) ref_col = [0, 0, 0, 1, 1, 1] ref_row = [0, 4, 8, 9, 13, 17] np.testing.assert_array_equal(compact_comp_mat.tocoo().col, ref_col) np.testing.assert_array_equal(compact_comp_mat.tocoo().row, ref_row) assert np.linalg.norm(sbs.basis_set) == pytest.approx(1.0) def test_fc_basis_set_o2_nacl222(cell_nacl_222: SymfcAtoms): """Test symmetry adapted basis sets of FCBasisSetO2 by nacl222.""" sbs = FCBasisSetO2(cell_nacl_222, log_level=1).run() assert sbs.basis_set.shape[0] == 33 assert sbs.basis_set.shape[1] == 31 compact_basis = sbs.compact_compression_matrix @ sbs.basis_set assert np.linalg.norm(compact_basis) ** 2 == pytest.approx(0.96875) sbs = FCBasisSetO2(cell_nacl_222, cutoff=5.0, log_level=1).run() assert sbs.basis_set.shape[0] == 12 assert sbs.basis_set.shape[1] == 10 compact_basis = sbs.compact_compression_matrix @ sbs.basis_set assert np.linalg.norm(compact_basis) ** 2 == pytest.approx(0.3125) def test_fc_basis_set_o2_wurtzite332(cell_wurtzite_332: SymfcAtoms): """Test symmetry adapted basis sets of FCBasisSetO2 by wurtzite332.""" sbs = FCBasisSetO2(cell_wurtzite_332, log_level=1).run() assert sbs.basis_set.shape[0] == 130 assert sbs.basis_set.shape[1] == 126 compact_basis = sbs.compact_compression_matrix @ sbs.basis_set assert np.linalg.norm(compact_basis) ** 2 == pytest.approx(7) sbs = FCBasisSetO2(cell_wurtzite_332, cutoff=5.0, log_level=1).run() assert sbs.basis_set.shape[0] == 49 assert sbs.basis_set.shape[1] == 45 compact_basis = sbs.compact_compression_matrix @ sbs.basis_set assert np.linalg.norm(compact_basis) ** 2 == pytest.approx(2.5) @pytest.mark.parametrize("is_compact_fc", [True, False]) def test_fc2_NaCl_222( ph_nacl_222: tuple[SymfcAtoms, np.ndarray, np.ndarray], is_compact_fc: bool ): """Test force constants by NaCl 64 atoms supercell.""" _assert_fc(ph_nacl_222, "NaCl_222", is_compact_fc) @pytest.mark.parametrize("is_compact_fc", [True, False]) def test_fc2_SnO2_223( ph_sno2_223: tuple[SymfcAtoms, np.ndarray, np.ndarray], is_compact_fc: bool ): """Test force constants by SnO2 72 atoms supercell.""" _assert_fc(ph_sno2_223, "SnO2_223", is_compact_fc) def test_fc2_SiO2_221(ph_sio2_221: tuple[SymfcAtoms, np.ndarray, np.ndarray]): """Test force constants by SiO2 36 atoms supercell.""" _assert_fc(ph_sio2_221, "SiO2_221") def test_fc2_GaN_442(ph_gan_442: tuple[SymfcAtoms, np.ndarray, np.ndarray]): """Test force constants by GaN 128 atoms supercell.""" _assert_fc(ph_gan_442, "GaN_442") def test_fc2_GaN_222(ph_gan_222: tuple[SymfcAtoms, np.ndarray, np.ndarray]): """Test force constants by GaN 32 atoms supercell.""" _assert_fc(ph_gan_222, "GaN_222") def _assert_fc( ph: tuple[SymfcAtoms, np.ndarray, np.ndarray], name: str, is_compact_fc: bool = True ): supercell, displacements, forces = ph basis_set = FCBasisSetO2(supercell, log_level=1).run() print(basis_set) fc_solver = FCSolverO2(basis_set, log_level=1).solve(displacements, forces) if is_compact_fc: fc = fc_solver.compact_fc # np.savetxt(f"compact_fc_{name}.xz", fc.ravel()) fc_ref = np.loadtxt(cwd / ".." / f"compact_fc_{name}.xz").reshape(fc.shape) else: fc = fc_solver.full_fc fc_ref = np.loadtxt(cwd / ".." / f"full_fc_{name}.xz").reshape(fc.shape) np.testing.assert_allclose(fc, fc_ref, atol=1e-6) symfc-1.6.0/tests/basis_sets/test_basis_sets_O3.py000066400000000000000000000155141511276756400222750ustar00rootroot00000000000000"""Tests of FCBasisSetO3.""" from pathlib import Path import numpy as np import pytest from symfc.basis_sets import FCBasisSetO2, FCBasisSetO3 from symfc.solvers import FCSolverO2O3 from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O3 import get_lat_trans_compr_matrix_O3 cwd = Path(__file__).parent def test_fc_basis_set_o3(): """Test symmetry adapted basis sets of FCBasisSetO3.""" a = 5.4335600299999998 lattice = np.array([[a, 0, 0], [0, a, 0], [0, 0, a]]) positions = np.array( [ [0.875, 0.875, 0.875], [0.875, 0.375, 0.375], [0.375, 0.875, 0.375], [0.375, 0.375, 0.875], [0.125, 0.125, 0.125], [0.125, 0.625, 0.625], [0.625, 0.125, 0.625], [0.625, 0.625, 0.125], ] ) numbers = [1, 1, 1, 1, 1, 1, 1, 1] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) sbs = FCBasisSetO3(supercell, log_level=1).run() basis_ref = [ 0.671875, 0.656250, 0.765625, 1.000000, 0.750000, 0.875000, 0.562500, 0.765625, 0.718750, 0.671875, 1.000000, 0.875000, 0.875000, 0.625000, 0.750000, 0.562500, 0.875000, ] np.testing.assert_allclose( np.sort([v @ v for v in sbs.basis_set]), np.sort(basis_ref) ) assert np.linalg.norm(sbs.basis_set) ** 2 == pytest.approx(13.0) np.testing.assert_array_equal( sbs.compact_compression_matrix.tocoo().row[[0, 6, 9]], [5, 32, 42] ) norm = sbs.compression_matrix.data[[0, 6, 9]] @ [ -0.1443375672974064, 0.058925565098878946, 0.0833333333333333, ] norm /= np.linalg.norm(sbs.compression_matrix.data[[0, 6, 9]]) norm /= np.linalg.norm( [-0.1443375672974064, 0.058925565098878946, 0.0833333333333333] ) assert norm == pytest.approx(1.0) or norm == pytest.approx(-1.0) np.testing.assert_array_equal( sbs.compression_matrix.tocoo().row[[0, 6, 9]], [5, 32, 42] ) norm = sbs.compression_matrix.data[[0, 6, 9]] @ [ -0.1443375672974064, 0.058925565098878946, 0.0833333333333333, ] norm /= np.linalg.norm(sbs.compression_matrix.data[[0, 6, 9]]) norm /= np.linalg.norm( [-0.1443375672974064, 0.058925565098878946, 0.0833333333333333] ) assert norm == pytest.approx(1.0) or norm == pytest.approx(-1.0) lat_trans_compr_matrix_O3 = get_lat_trans_compr_matrix_O3( sbs.translation_permutations ) np.testing.assert_allclose(lat_trans_compr_matrix_O3.data, [0.5] * 13824) assert lat_trans_compr_matrix_O3.indices[-1] == 2726 def test_si_111_fc3(ph3_si_111_fc3: tuple[SymfcAtoms, np.ndarray, np.ndarray]): """Test fc2 and fc3 by Si-111-222 supercells and compared with ALM. This test with ALM is skipped when ALM is not installed. """ supercell, displacements, forces = ph3_si_111_fc3 basis_set_o2 = FCBasisSetO2(supercell, log_level=1).run() basis_set_o3 = FCBasisSetO3(supercell, log_level=1).run() fc_solver = FCSolverO2O3([basis_set_o2, basis_set_o3], log_level=1).solve( displacements, forces ) fc2, fc3 = fc_solver.compact_fc fc2_ref = np.loadtxt(cwd / ".." / "compact_fc_Si_111_fc3_2.xz").reshape(fc2.shape) fc3_ref = np.loadtxt(cwd / ".." / "compact_fc_Si_111_fc3_3.xz").reshape(fc3.shape) np.testing.assert_allclose(fc2_ref, fc2, atol=1e-6) np.testing.assert_allclose(fc3_ref, fc3, atol=1e-6) def test_fc_basis_set_o3_wurtzite(): """Test symmetry adapted basis sets of FCBasisSetO3.""" lattice = np.array( [ [3.786186160293827, 0, 0], [-1.893093080146913, 3.278933398271515, 0], [0, 0, 6.212678269409001], ] ) positions = np.array( [ [0.333333333333333, 0.666666666666667, 0.002126465711614], [0.666666666666667, 0.333333333333333, 0.502126465711614], [0.333333333333333, 0.666666666666667, 0.376316514288389], [0.666666666666667, 0.333333333333333, 0.876316514288389], ] ) numbers = [1, 1, 2, 2] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) sbs = FCBasisSetO3(supercell, log_level=1).run() assert sbs.basis_set.shape[0] == 40 assert sbs.basis_set.shape[1] == 18 compact_basis = sbs.compact_compression_matrix @ sbs.basis_set assert np.linalg.norm(compact_basis) ** 2 == pytest.approx(18.0) sbs = FCBasisSetO3(supercell, cutoff=3.0, log_level=1).run() assert sbs.basis_set.shape[0] == 22 assert sbs.basis_set.shape[1] == 6 compact_basis = sbs.compact_compression_matrix @ sbs.basis_set assert np.linalg.norm(compact_basis) ** 2 == pytest.approx(6.0) def test_fc_basis_set_o3_diamond(): """Test symmetry adapted basis sets of FCBasisSetO4.""" a = 5.4335600299999998 lattice = np.array([[a, 0, 0], [0, a, 0], [0, 0, a]]) positions = np.array( [ [0.875, 0.875, 0.875], [0.875, 0.375, 0.375], [0.375, 0.875, 0.375], [0.375, 0.375, 0.875], [0.125, 0.125, 0.125], [0.125, 0.625, 0.625], [0.625, 0.125, 0.625], [0.625, 0.625, 0.125], ] ) numbers = [1, 1, 1, 1, 1, 1, 1, 1] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) sbs = FCBasisSetO3(supercell, log_level=1).run() assert sbs.basis_set.shape[0] == 17 assert sbs.basis_set.shape[1] == 13 compact_basis = sbs.compact_compression_matrix @ sbs.basis_set assert np.linalg.norm(compact_basis) ** 2 == pytest.approx(3.25) sbs = FCBasisSetO3(supercell, cutoff=3.5, log_level=1).run() assert sbs.basis_set.shape[0] == 5 assert sbs.basis_set.shape[1] == 3 compact_basis = sbs.compact_compression_matrix @ sbs.basis_set assert np.linalg.norm(compact_basis) ** 2 == pytest.approx(0.75) def test_fc_basis_set_o3_wurtzite_332(cell_wurtzite_332: SymfcAtoms): """Test symmetry adapted basis sets of FCBasisSetO3.""" supercell = cell_wurtzite_332 """ sbs = FCBasisSetO3(supercell, cutoff=6.0, log_level=1).run() assert sbs.basis_set.shape[0] == 2162 assert sbs.basis_set.shape[1] == 1950 """ sbs = FCBasisSetO3(supercell, cutoff=5.0, log_level=1).run() assert sbs.basis_set.shape[0] == 698 assert sbs.basis_set.shape[1] == 569 sbs = FCBasisSetO3(supercell, cutoff=4.0, log_level=1).run() assert sbs.basis_set.shape[0] == 306 assert sbs.basis_set.shape[1] == 218 sbs = FCBasisSetO3(supercell, cutoff=3.8, log_level=1).run() assert sbs.basis_set.shape[0] == 270 assert sbs.basis_set.shape[1] == 187 sbs = FCBasisSetO3(supercell, cutoff=3.0, log_level=1).run() assert sbs.basis_set.shape[0] == 34 assert sbs.basis_set.shape[1] == 9 symfc-1.6.0/tests/basis_sets/test_basis_sets_O4.py000066400000000000000000000112271511276756400222730ustar00rootroot00000000000000"""Tests of FCBasisSetO3.""" from pathlib import Path import numpy as np import pytest from symfc.basis_sets import FCBasisSetO4 from symfc.utils.utils import SymfcAtoms cwd = Path(__file__).parent def test_fc_basis_set_o4(): """Test symmetry adapted basis sets of FCBasisSetO4.""" a = 5.4335600299999998 lattice = np.array([[a, 0, 0], [0, a, 0], [0, 0, a]]) positions = np.array( [ [0.875, 0.875, 0.875], [0.875, 0.375, 0.375], [0.375, 0.875, 0.375], [0.375, 0.375, 0.875], [0.125, 0.125, 0.125], [0.125, 0.625, 0.625], [0.625, 0.125, 0.625], [0.625, 0.625, 0.125], ] ) numbers = [1, 1, 1, 1, 1, 1, 1, 1] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) sbs = FCBasisSetO4(supercell, log_level=1).run() assert sbs.basis_set.shape[0] == 115 assert sbs.basis_set.shape[1] == 72 compact_basis = sbs.compact_compression_matrix @ sbs.basis_set assert np.linalg.norm(compact_basis) ** 2 == pytest.approx(18.0) sbs = FCBasisSetO4(supercell, cutoff=3.5, log_level=1).run() assert sbs.basis_set.shape[0] == 11 assert sbs.basis_set.shape[1] == 2 compact_basis = sbs.compact_compression_matrix @ sbs.basis_set assert np.linalg.norm(compact_basis) ** 2 == pytest.approx(0.5) def test_fc_basis_set_o4_wurtzite(): """Test symmetry adapted basis sets of FCBasisSetO4.""" lattice = np.array( [ [3.786186160293827, 0, 0], [-1.893093080146913, 3.278933398271515, 0], [0, 0, 6.212678269409001], ] ) positions = np.array( [ [0.333333333333333, 0.666666666666667, 0.002126465711614], [0.666666666666667, 0.333333333333333, 0.502126465711614], [0.333333333333333, 0.666666666666667, 0.376316514288389], [0.666666666666667, 0.333333333333333, 0.876316514288389], ] ) numbers = [1, 1, 2, 2] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) sbs = FCBasisSetO4(supercell, log_level=1).run() assert sbs.basis_set.shape[0] == 120 assert sbs.basis_set.shape[1] == 34 compact_basis = sbs.compact_compression_matrix @ sbs.basis_set assert np.linalg.norm(compact_basis) ** 2 == pytest.approx(34.0) sbs = FCBasisSetO4(supercell, cutoff=3.0, log_level=1).run() assert sbs.basis_set.shape[0] == 34 assert sbs.basis_set.shape[1] == 2 compact_basis = sbs.compact_compression_matrix @ sbs.basis_set assert np.linalg.norm(compact_basis) ** 2 == pytest.approx(2.0) def test_fc_basis_set_o3_wurtzite_221(): """Test symmetry adapted basis sets of FCBasisSetO3.""" lattice = np.array( [ [7.572372320587654, 0.0, 0.0], [-3.786186160293826, 6.55786679654303, 0.0], [0.0, 0.0, 6.212678269409001], ] ) positions = np.array( [ [0.166666666666666, 0.333333333333333, 0.002126465711614], [0.666666666666667, 0.333333333333333, 0.002126465711614], [0.166666666666666, 0.833333333333333, 0.002126465711614], [0.666666666666667, 0.833333333333333, 0.002126465711614], [0.333333333333333, 0.166666666666666, 0.502126465711614], [0.833333333333333, 0.166666666666666, 0.502126465711614], [0.333333333333333, 0.666666666666667, 0.502126465711614], [0.833333333333333, 0.666666666666667, 0.502126465711614], [0.166666666666666, 0.333333333333333, 0.376316514288389], [0.666666666666667, 0.333333333333333, 0.376316514288389], [0.166666666666666, 0.833333333333333, 0.376316514288389], [0.666666666666667, 0.833333333333333, 0.376316514288389], [0.333333333333333, 0.166666666666666, 0.876316514288389], [0.833333333333333, 0.166666666666666, 0.876316514288389], [0.333333333333333, 0.666666666666667, 0.876316514288389], [0.833333333333333, 0.666666666666667, 0.876316514288389], ] ) numbers = [1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) """ sbs = FCBasisSetO4(supercell, cutoff=4.5, log_level=1).run() assert sbs.basis_set.shape[0] == 3733 assert sbs.basis_set.shape[1] == 2749 """ sbs = FCBasisSetO4(supercell, cutoff=4.0, log_level=1).run() assert sbs.basis_set.shape[0] == 948 assert sbs.basis_set.shape[1] == 515 sbs = FCBasisSetO4(supercell, cutoff=3.5, log_level=1).run() assert sbs.basis_set.shape[0] == 61 assert sbs.basis_set.shape[1] == 4 symfc-1.6.0/tests/basis_sets/test_basis_sets_base.py000066400000000000000000000006261511276756400227240ustar00rootroot00000000000000"""Tests of FCBasisSetBase.""" from __future__ import annotations import numpy as np import pytest from symfc.basis_sets import FCBasisSetBase from symfc.utils.utils import SymfcAtoms def test_base_fc_basis_set(ph_nacl_222: tuple[SymfcAtoms, np.ndarray, np.ndarray]): """Test that FCBasisSet can not be instantiate.""" with pytest.raises(TypeError): _ = FCBasisSetBase(ph_nacl_222[0]) symfc-1.6.0/tests/compact_fc_GaN_222.xz000066400000000000000000000110701511276756400176110ustar00rootroot000000000000007zXZִF!t/rm]Sq(W>%#3dȄ}u%)WS ȱD)[HSP_ȅQPoBc-Lě2FM$(}r^Q#igD>A`jRMH(kg,JKmWaAܟZ2(m 4Zc]R-g͒RLjg/ (yF"Y(#æ Zms뉏vV -(G̊?ːؖ8Y`H?s@q;BoZ. _\_OnW:糱OB#(gCGu%n ׄ;Nnkd'U9+3+ S-IGke"7uGLYgΰ}`1ci9"j k(E }r:0`%HWmGA&,,{M:ӭ;yjOK4AIRᢖ9n[]Fۡzۿ :|ڴ!kuѰ)i1W#>~^sYY7iBlI;%y(sQ~Dɵws5dQ3~v1VߒgʨTp-M!52sj䰄'cyȠ,f@!Y%ؔ \TWRxwb[W?h_aҪXeb o~阒Zdf&`,]84Az`l .jІC7!x9c6:diZ7zt3PZ-VܵAH*zͅ/9O2'$?{T~TLNe@jЫ-z)i0B`_2KDd=!RϧȰR+C[tQ\mv61*ebVw ,Tq‰ᰤ8a7$~4U 0bʖ39l=sA؜{w tUiU:sM-[}."Z^0WPo0X6\2kR,A$?H! fHko+N:*sqd,+͔Qy)\Wvgϛ:^si(`J -TB+_zd.BQʃ5}-d_os $/n!D܏Up3% 2dU:8f6OO>:S߈ņ!u' Qb>z հ~:e7 l%*:5B'` vNYw${ﳫi+NocyO#!(GgkbONtEVo[]>wY:\ & -؏.m8Ṧh*BZcICu%pLO^d,Uf N 7Ʋb־a&ߴ|[=gv dɗPXS^Φ3(*:jz>A T%ərd.9Is n|a54#|6Q ~BiMn9ºl]#爱mU!Hp@J/supHFQlBv57Zk!;/{3,X89l'oF2F6Gzқ$Əhj4 AHS%Φ$17Y GuJtLyy8PeNsl&q<\Pݷѧ WiS@A0g4[I-7c2,㿋\ Ӆo=wsmf"Ѱ; OL-{_.YʄTNZj?iB8;0d&,nr"R"Cä簄?RhlDYN6i~8XE.>a~Jh1 6IsQVj4}} P< ŭoֲx 5-18\2 `(v>r5-08 *_οs2)ۓl@RŦ6r3{򿧼w[r#eDz&s D >8/ wm=b#i9J$DJD" 󸛟_fc#WB6RCݳxe@<;kV0 >p?4XR10>!Dd+ ] {L5ܜR309O "D I-B9Ǣ7fkLg(MH=M &ӂ5Z縶&TM1|v$~gYZsymfc-1.6.0/tests/compact_fc_GaN_442.xz000066400000000000000000000427441511276756400176310ustar00rootroot000000000000007zXZִF!t/kE]uT.IYvY1Tm-`!EJ[7,9r Ntcbip s}n=0~B=]d$S* ϵj7dM=DK =g$E1f|)H˷`U]?^/*`0}aŕ/Owc*0ׇ_ACҴ] m%>//u;O!Z81_vqٱƃD*ǔzo AMJh'ʫf=@' S=+XkT~Pkp^t=o1C9$-">qsv`ahI2=,fHEM,:WtşvHß)oA3 41ll`idyBqGZF+PS$^6iԁlؑ/Rܗ9E7rޖ$B5D5Q'U$t((r?/]!Qu>4П3^ӆ1ɰssOh?DR@B~8L4Vj=7ƒQ-9;_ަ^wC-^|ޔ (`9Hgv_"pU,uT6c{5mVL SKSS9= *2+9Ausua;.N*ke&ˁJ^>0l) 6 #;{#tz+7ѝ x*n<kdpOz:P1~ŀ^q^8ƭطz|dO K;|,$yKȷiy?g#jo} E`]PUԓ+N(ϳ+ G^ewI@u-k[uGFaIpq8hDhO rKVV=MqI^t[̥(+ukBy3g>Wuk<2ukFi!?̇lLnY#"wDHVeɜi{ +0WLVn!&84՛.5? =VVQGc|CD,  ^O(\^DAnB+?& EðR*P 2!6Hk,޶ lWGw TLhk$*Dgt.d 4w.Z{7%GFdR!2b]Y ²V!_Rp3zAALw]}63XP.uD+\J߆˜@zQbZ(|8>=/i. g_'t&{k<~H> Ĭp?؞P Ԑ 5)&(B)l?no?yJ\//qrKR|'"WnT<>6kC#VeRhrMn zD"T`-@rZ+iMc?*<__ T,>g>[7-%D%dKܓ˿Ӌ%wAK ȤS1@W'1Ehr T[RQوGPFRtU<%*ܦ(詌+#uiV+a8ͧF!;*ObOk3,ZXЅܒRS$},eW a̯d[<"r% g١ZXb3Iw[ JX'J" EGA{&]@P&:~o!k=a;d2? s I|IIgbGl|-DRÖ̪nZ6D` SJTn w2J~aD*O'a- zϥ* \`A)X-Zn75JU679$r}B!Z4oObz^; 18+KS^J;qpe$ן&ٍ}el]:mdO4Ce‘S4^`*A=*]YY*E$aTX_, %0[{ۖq=\)đi(Ax~N9Ѡ۔.pI\.N>B۫X]7WSvh@t^ثlI+N7#(陸,/NF4+0Pȝ^cҧ,p}:KX{{0c9}e|3nIq%[=֙ZDA|\!'@mEu 'Qkn>`_BۛŲ,Aw ID, - )O$;a8gg }gG/G0ެ7I9D@^@(Sl:~ArMW[:?{ͩsTS`%Xm˂"> ^3nQZpxCf?:IUc% ڹx.iPs)nW| sEBhfqL ,wC8KS>߈-0jټ˺ ߼ _Sc!^5 Ein/j9M5f~ #&"muhq75ttb.Ny`.okNs+>&f(`WdHqlpbZ~{\شءm+ 0 I$@\}=pN)jd1`WȉkAXll ΁Rht@ e[h;p;$V>V D6[ٟa2c#q?,#:&-k@G 7#@^] 放w,?7$yTTζvEs!5 阏(ukȜTbhW D!o6^"G^QO>'c(pO1bjPkQYOm.~R_(ʡܽf,1{B8L*&F<)2$mgr r?{3떆N FWNW [E; 萟~B%cwۣE{۱z|X3ZqT! Mk&ib*HF6ޅ:HӸ!7؛`4:3vxH6C'BE: *@ ,envjǿMou~LΪ ê%i:V+_&|n7 ;"+q͡}GgApF|U7Ueuhjƴ ^rСEbq9ߔZ))tuYFɄs-M C4QM?YxwJf/y kvxܬ8ojMfwS42ze-|ǰ" ȷ4Y)Q[j]ͨU):MU('K r\bry&D[?0b—U~ȐB=`ERX67$ -fM;V+΄G~hyNq<IƉZJ/}R^Y&{sC3s!_t d64k}5R{3Fyv VbgYh 2ru7:"^Ǘ)S&[ފTb2r;dwoMB)cڋ݇_Ӷ,):ps3(V&GW"K/fiqXGFx-f֋Ž,.zSC܂TN'q9Hql! niXfrLk-CVUiKh1S3 IJyoJpX&ɖƷV+gO:V\0#`^bdѣqL _"T[B.\pdb0o[f β > ?\=amᑋA JFtyE1]K?!o󪦔 *XPj[OrcjKu8| |AGK ..7fKw:nWn?k$Ϭ#: cP^$ &+8?!'8U# RеfFYwIa9F=/'[*3l~R1:v,KП^V+5`*P5b% )x8(닝v 5f>l;Pcr1bxǴk83,?۾] NG%p<aFH;aBCesV|һ4+W@sȨT**qt|h")aj6/Ġu_׽gHY`/Ќ_& @ g3:d}wԈDe`k>#&?cRoqѣnRT #9LjMBUZ6Q5߭c*Qn+<|'e:(6=e9{&H޾äSlYSו{Aԗ-[Vܪy0@5~3Qy !j&K&6$x G(0)<UHCiT'_Wފw'C1Ci CδcF"ͬd&k!23&s4o҆~Έ; ,Xj(+xqR!0e~G1d‘imvtC/ݖfNʚ^;_XL>)s2췅]0lx=Rcϱu[rf%X=S.ASh?3Z6d]ACk O;x.qЁ"/c)WVU^p'y& I0f)\Qv4s;v01Ox14~C;؁/-8|yԃbZ .ck4K/RSPpSU/T<][+nTʙN?<-O QN4񨩴СRҒ.z+r&$H]9vq[[~)6 8(wd-6cL3f'" R_~VG~cN|D(P΋4%qGU{"]VCԔ4`bB*vܹ쁛B(뙳q|OBpRI|[7پNrޑԪ"8ƕnmJ3muk"A;Yu'JW/9_,'VpBō ,Y#[Vݰ`%<1~1h|T?TA3QG0*DSii 9@bˆ& =LH 3=j5o{]l6Xh;QyYDF+4jVD$Yn.B hIr5gBAka.~dAlX6C5/"6-Y@8%d  ;M$kWNG4R<8Lqc{3r#5^?۝2lo}^=(UnBfN|4 rԡ(ȉr)U~,? 1jU}!6 ?#qM@&Ȏ^8?s?f7ä4 'gPhG&;hrE E:OЩm d*G߱,~Sjٴ@-sO_O*^EW 0VCؗÆ~x.'xT*22;D:5@]3u[:ds6j#Wn[wjlKT0&ڛwg$Ьq5Y5&yFK@s:e1HCcvM# g JN ϋt4[jO50FŊ"-^=V$XQfel[" p*3qWܑwW,Mýw֚/<ԙ+|?,Qn] <a;wr(IqX3cf#Y/s:cQ}1hE(^.)NZE>`i7QtY'œ|(5ue :98{՞na!-.aFO>!<F|ISdVAO?Exv7=:ALu48Wd, BN)9yϮ ౮2zh΍,S}K{7u[>e Vt긊G'IrYI$$Sܓ2X [mKGUb u*sBV3J_C΁ ~wxCosήh |3JaiY)q-tíUk WYεEr8C ]`PYM;JG]%mJR^+3AhIU OAΠkYrC,oe2z֓T4~Jw9Q L91avhy;ZK}3*$&k<Y1E |(pJX2&MWOw'!c-EqMvyRxU\yݿ\oˇ+i]?RP[b' $h+ag~k\oʛQ]xGؠ(',+Ӏzezlj"Nٶ3iOq?ސsg\ncL%ݔ<df"TpM|EvҗܝHN:]Qd>f[{^\aC08h3JZlxP[/TKM0AVT#A4H:%8E! nuCbN?zE*f5zae\awnU)dzH,CHE5keiCRu}'>y8Xgi V֧sZF5SYsN7-MPɨ} )OP3hT nf 答9jhYY #z? h1U1y5yo?^UHYѽp)C$)cТ[.Q6'/Z)]d;U~dIpj!wU沘X%Y{훔$W]m D yۖQޓ+;";*/FP /u 25,V` eʱڎ/O !0Y^zs1,!/u!;h!s@ Ge [lFENE~`NNykw{{3BDuݺp-B^ZXb %bc6juHUYyM:hQՅ3xRQ%M]&\` @,qp|I)|vT1":|#Hd 8Hкewꓧwdk(MWjѪx밿e8`2zK/q%AIޜe} ǢP}ch 7晙wߟv S͍ {Veo bn=MW bb eߕe(){bUVU/~y_wquu(!Lg gu'[C%^{iWe$rzӐh\G_v_[ ,_Ȳ|b0lvy) KsCZOeiXqet& S|yj?pl V &?CΒz ;! Hڎ|(?`N)\*~Ht.q.KxKkDUnHNCC=TlK>&  ۔~\X& *~IahFCP8i(a.j<gZN@FWA2Y8.us t,ԓi73X.叾!,纟L=म;SתFM%:sܪ8oO덥`ai̛ET";Drcӵb*nt3޹p"K>\^Ёo\'K|#y7u,X)(AM![\hOd;#z V!VoU 3C0!'Ac(3մ VG5]S,5(%ʤdX.}LA=:O1Ѻ~pluhnD"V^o/znk}IDzmOYc|h^-ŚǦpEF<5kG5Deؽ汾,^7];ݺ)v]-,pK =Fh c]vr6KuAΐs+g1d>蟀YP]R<iO,So!n傶9i >n6tg.h װfyo,aSV}J0`a=ݵ5c \ef6/Sm}78YDWWW.diS`0OiYk-!fyIX S6T725+Kv{ PK:ԴW-S+%ӐfnB0v \>70"Tna/sB[ˬ/A"TIoeƊ)mNi2A9<mؒH~ñ$O> b!lx+Pʰ ']=e 'R1oT!MQ{Q-AGs.eXͳ]$R*ub# \9)vͨL!SIGL۔+^/naM P_)t -Zbh4yN̕)ek էk*#ʴ4s1eNx}A=iyˆ]~!F]sO?a F()|-\H-ffb!ͣ{)fm^^H10?w$^0|3DQvAxǖqzM;60t֦w#KG: _01[^R'VvKtyMM״Ge= 8 Yvmqk{R+`:R tw>c#ò"]uGjpP̦$<Ȁ Ca!EDIRGnErB9PFT|($|C,D\>:Va!8+:"m vRg}6HZјl ĺOܤÄgdĺ.LK ) kX'gXG <{9U SR4d0(ㆡSyVAXF]aaGI&86@EoS76nFM. GfM4ݭ^,c@L~6$iύ84;e`:LZ;|^]u-_a[?+'g:o=#u4LE+zqrС۷.q]^"Է[DYrٳ9I#nל{ Q;}F I\x+)5RQgW[4ݚ?*yw)anЇƫP|M`w߰-5Yq*Dy7WN-">uCIlx~ſ6d.ǎ$UtlW:T^'F.[տ8:CK7+ҵ\ ({io썂29LU*|a5D^S3c 5hp̚8?^_+Xewq4{۱y0JCB\(K"y>\̾;BIѱ&|'0i$Q,Éb ᥔVPjr?ВP}Ib$sTA$ D[ep_[MYG~.w3tasYҿ0 G̴8;O_~H6UpIC}L Y :?G&*P)UEqNme FマWRkޏ%N"5!,?3A.lƢEGN9}|2Vs2L j3T0=Һlq8<#t5+3P7 2g# f޸PBV72E^EO368JdKܰA+0W,=~nԑgm.tg6k0J6`5Wz̺f`aJIoWP U, X%#羥9(%8ev_hkmn `ʞTEGUMHOdi͎X5xTR׏ 3\ Q|Fz#S5ZJaZ4rbҘwo.UdFB0CMxmA"@'Hľ ȣA)4Ԕl8(0ʹMA& c+nvkV7| qdMTetv,Kȝ]O&O9п #iIPq8JmϾoh jr#*rPܺ""މeȦk#A9i5t7>QvWY~8@׉V{ZK]h=ku q]v ^8Ә݃M|!G#F1I J:6ϙݏ\ N|E@?CiZ3C57dM*jh6ǽrD0hշLOL];;βQn7Oz,:&.E"dGRP$j^ r\NA&TZS ߝfŶ[Sp6\k2eu8c&p3ksx _Y-Б` dCuBl%AWxx͎_ hDdݔ_LP?(ް _~ GTXyO9t9(l˧%~y9{8PPTZ'nS($r2 [Vc8EEДA(MY6Y VVf":?رhl<_$(R< ?4~rBb{h"0@ R9z.. "a[f&>~Cq96+!}+wZMsmk]nءCc|&T|:ݗ ueCa[4 vˊWJ3 K߬dJU8I@G'"~w4 `&T"DK{uB Xpv(BĔ3`o}S̸@xjW奅c1⹞;Tr r_,N4LsoFBG;";;=8GYEQHÑ^^pV]]MD9/)xp==*e}I 9݄p^qs5 TΪhVjޒM[yic?j}WY}s-2b)UN+Кn5+Be"&UfR;3%yA*Gg&? )񣞳Y'}p Č \rGV{2Q` w'LiV _]hV[N].<\cPߴDp*DuN}ᑬ˔ROLoM_%i/vhi>/:x(HdwF<_GE2s @@~ys5N @ڠnIv|ȅ%p5DGdZݖox$8_Z x蘃̤A+:2yvN9w!LKu3'>`E /:>Әl*LUsO"+<mf>`w3evy[5"[@ĎaGgs^cڻW \xd@K1@VA[.ʹ&u&U#xxY 4e5fXT,^?L x-V>BiꚊ#["܆96`CJ LӨ3j3~1(ek&/rWD@0˽ha6Ef"jEC]՜5}]9;2O p/?c5MlyR(,7dps-e.&;A݃3}=!KժSJ5#v)bPP-}*zη,ޱſ={@caLj5ӿ[RZaU)Qd FSJ,Ը0޴GU $yD]df;u8]ZfZsgEE\fʓf͑;+weͽ>͚eTA]`|1l)vl=㡯an?d]3=h2{8%d]G/+XcRA^$9>} Ao2},WIvDgY 7 Fɢ2$6/2=dLnp#&&S]RD/#Y!B]1q LצbURqR=iTfN-OBp'S.aҀF';''{cHŠ_` $F@߻qSYfuv8$joB:znPvd@2Ѹb:X$y>1 YFI" CegVX:ZtKoNhӧV :NmkPzAOʳ0bs:7Ѵw]ٰBl uC 1*5xiI]ħ˥ګ ľw76K3웭&C웿vtec6 ֽ'XJ8% x< H4 uLƕIGrT0~{6y/(wPuͶ$,`#zێ 5(㬓C{o̔w#1fr\^UBpG>(F_ZdS6>BX4e|Qݤ6se mY0kFŊ eݞʿK=xy<8綻u[QHNtT4ʐYʅeE._d1tLZ0iC{SϪJt<i25:T͖sv̅p*!'%Ui=4]h6j$찪)K?8P]}نsu4%mkr65ju\AEE^[o2KW2c0Yb6chu@l,O˅,4O>x|+Ei3Ʋ24.%5G}r\ha972֓[9ʹZYS3M xά0.>8GcJ5 fgo}&e G6!;<\pVX1V.aR'Qdhf5r%Bn<>c\b;5$ J_) u}IA02CZY~Г6 iveٓپ;:Ju{=ȇ.a{agGԱpkmvW;n6MN[ȞKGK/E:#Sړ>~Tυ8)*n6{)=n,>j#L* qx~&mjh\b 7❛Xwt/ 0W0Hr8<\$` ˁW"ݿ_]1%F3O!-6i>(a/WTύoRpxqV7 p3 Ͽ-FɆ9,ۧ[LMx0'P*{8nF#`լVh~Nh])|̤Q2W0P:CkՑ yL;OHR)W$N>@;;įR61TʃQ~@;Co̐}"~Cƍ=gM nH*bqC̲Cu(j.+[%K@P Q& I/q_ #`FEYkVtSv2ܩ~mpl*Df:n~+$`l=`t|$*c:$UM07T+$hKWd lQagµ^B tdn%*{;Hpvpdf˦0Р+!G}Ğ7tMESp:].*! z6??1E?[Fb@"m,)lU@|;1IWCi›ADO@92tzJyRR2e_Ytgn1^fY'& z}Qfڝ~KTB?A5ǘ 1`:zj2n|ІL <$ X(wjgʫIaKǸ?T^~K])C?\?4?!Bamm6r#=܇81s}$/3?&?0f(MavLRpYGV3|daFWXwXFaSxĻN5";sJp[t}yF/QLoËDl IHّt,Ǝ@j=_J8''n6^A_m2<`^ *ƺ()}{)03bh9 3F'䖊 00=gOQ.L׵z5;62n'JR|<2zb"kb$ơR튀8Bk}p)B~ja)8ZN+<}IA;޻g|39'<9U CnF:9P Ơ{Jޑ́^Ww*iAPjosd8$6u~TI>>#!0GOVj vNK5 ,g+)]>geb |bc\, P'[*ΖCdVpELEkf (0TdeZpy|Gu}f}!:>Wi:xWGNJ%"Ty—HA^إa Peh &\t t-"L 8%Pв/WF_5_ys蚲)$>켺hP¼xpD=$;(Am/Q#WG{ WhQL]B8IU&  0z]>* tyIBmh! 75^ģ4٥r:ߍb:JP`kޣ&-}_v/zP!`S ʺ㜬vg1}HuxoRQj}DmLY[ѳ6=4 Eb7u6EK3Fk GO;nm]~"ypR;.`MG6>w%u-#;+xzI^:YQìps@/~+UiF 10tVr @T\$C1H"sNʃ"CeM ̐+ @R'BHT9v}N>0f1XBW 6'8VE<| jmQo3;Zٷ2~~-r6ZPlϖ2̓Q.úJ\NӇ!rcDcZzI|jPj%oJrujy-iBM1|r~!Ɲۄ[l@eLePc\ w,aˀE-LVVSêDd`/mC)]m7`NXk}V=)?LeE3haΓ f&>#H4Δ '0^<^BLE@9Em}p:stVhXR ˞L.sqObL}W<޼eۯ¥y\-#\q`ilK-:cOG*Us7wɲ9dUmf й%6a7@ bL#9$BO~}ԅA)̬zl8fiPPн8fnFksѽȕ_֨ϫAN2͞>|, mmZeVjtKVCmlgҖO j8Ve*2UM)%_@d=.7ۏUّHe;0xB" NwYRB.1d)B%W:q,&',В1:"xCɈMeuP^2zu"|Ǭd@/zuK[f&w_Lm*_x4add]<=vJ#:Q7}PC]1z"oUOvKT( /bmY=kN"~3I@*HA6[ ]E( %_h`l& {Q)0oUOi!.`vuF&wNrz 5BWZ2 E5D4XRJ4ʳg{'3g&h.ֺ_Y~%2}e8:@-OO7曟5mjؾ`4h8Q(Sn|I{`'f}wI7|YrhٵV?e@,iӴ/ ,OLb"BSYNkS7;@(xcpW.jQr@Kt=IH#]\]mGU WrTlv_,RSa@ʮ-Z FmSWl{moHNʍR%r1lW~Ry <, {9U_M#y@n@ChvzpUD6=O΂o]rE%/eH8^wEVB| \`cw WoöNs1犲S >4 2ýxEQdћ6Սw89ri[Y5?~:gVtJh{e&p-T:4-@QA+za^~8]Ԃ?vYk1k_ X(9 ` .=8@"Wvmj ,+*:z'ѡ]K4T/T[kIsMmC}14E֌ l[GP1.a !͐%Hb@> ~vVo1L[^8AWo{W'&hvQ7 xhUl(En@ N\+Q[כn\ҕƿ˛fz_Sb`Ow'V4I>h18 z!WTJjEOQFA򑆡9 +,iec{9|CQkFfv c_(xjO>WgށVv4B!ICKtQXn%""6v >b_ ZF.JB\n|=t㆟gVqTM慜zLwXK:dDe;5D`p%+u'2 뾽qm8_+|ws+H+.qe$@ڗz n*9ӭ&$BE :1JRVe<'!tj=k~}7qc>p)P1Viቷt3|آ͠G~/AH:z=gG//-HAKk~+M(綼҅q'/˻^:ϤW&~Lٔa F,yv5glN@*`aslSXĚ]K 6$XlZ,u{MIFa zS/iʜԵ]]$Gw.7qu~ќXI^0L|;7^ӌZhWYW%oI m{m EBOt?x MBc Bs&{7~bl7ܳv xg;*@RYo}*bx$8p7Uy:SZi1ҳ5 ~p203) ٫*)~6yQ"0r ޢ BK--ٚ>lV(g|9.TSn"H!wkaaclzK:qYԸ񷓣a˟eV+{@GjC;9P{ȦNklVUDzyϬF~T,e`t݃ǐWƺk?ұ' S)".PA(I t\m#4M!HKoa$|vr")գ! T> M7Yp.Xَwkd8i0 Ϣ(]Sz Rٽ !0*SN$h볃7 GG \ӃwZz$+J5 ^.䚪Fu-2u[hEu:@)&AtA27d!IV{0ب#@x17m4vI}D̿iq<8`t5s!MXUUġe&mpiU2ӄۂD]7-8tTsY|%OFx!R2KR!P;@HK^AkP6{sXE=JʎyjALkD~4_b8 v\m9Ib{;YW24ʡy- v{?W!A>` sQˏ%2E_?;"?E?fOG~!:Ų?p߭M9y+ĬQ0/%y'ҀT zɀn~~\ݺ-Q{Us"+W3?u-g (o52Ĥnf.sf$ԙ)OqŁ ZY3 Zt]hA)ߌtR0)短A/Dgw:Wb Q\)߶ "5t%Nᮕ=!) }/*Wk\0E>EnNZ5o xnH2-dPbtP#|IxRw0G\ e7;'E-un[Ghk j'W_ChE}EEs3Æ4. t~\etRU-Ieobh=wr[+;N!s0]zUfnzPRx[#ڇ nP%>o!}XBaI<݈ C*U6Vy] hc?0t]ed7r;.H?bTmQFYM., ƖlP}ܷS~f;t';;ָ̾ ܕ_u/g%I= z潴= 43OTG#:(+׻àhJ$ѧ_~z)0Gj s~?({yHMBtEj`LmΆ{;{d ;m3擉b]JN|_owKS8]J6i鿪,8?]QmI4[I@lO eYoCn6"[mqKp֚;6eSv>`mco(N&HͺvuJ|F#Q},-M/>號!Xe{Mh+|>K-f1ĝ`xF4TܖPaOY+r%F9eӴNIڟ)>e^ NG ~55_mGIu8>"2Zp Cs,ƭoLB{szJeX+2]/x۳AV/F0)T)zE4<zݷ-ڋ "ܨd yW48l͙e[hP =|AC1ϖli->%tTr@n;f?pp;LXLeً8QDG 0&g7CS?Y0Qʮa}1L*^:[z <ӿvj.p7@<3|:_ygFo B9x߀qzuA\"drUQa׭jyB,o$ΚO6MKD[B[WWLiQtբqwragݤ宝8QI( ܶ_$.jž(V NhM|OWA$Mn嗀9&fkǓ]gGxظ)qE<]_CR jK{>l.]]~zTZ짎/}*q-tE5Lg( kB Zy9X, ecS?p"vͰ@eșϔGRʜ%mB8޽gBeȢt {DW[GV?,܈{~4mxxCd!&bnϖAk+Ĩ*հefb>V}LφAMJ|Qpm@!(CWk 5f4IeIގLJKr.kOƮW#^g=wFH=^V{BpΙ:$~@_&wViH;;MJSY'gK#"aV>!/ EiEo˙R3]M@S6454ߗ2nt.blS 1`"GuG1RD|y\-=YDjVʗ--K%*&R=le{V(d0~BZ`0MV(s-_09ANj,pafBGFB* +o%j~(j0Mlo@@_M p7LYIс d m Q'4 Dzt}]uClinߏ)~rG:Xk9_^ ۞ZSVo skoL rJv]̓Eg-S= NOfAwͧ{a'@*،p%1-m6Xs;THt e#8ǚlDSwϞ*Z+|kQDWVES\_8cPeD 4>Kŗ^! eqGj.C1@FfȍT+E/VӯwH /"I1H?dBVbNᶴpԁRiA'8`dޒqc%ͺd݌NHZ5D|w"@|xX!XCt~tWgam^!7vb0+gnɂ=l͏UA=,}r 2rئM˾T]A@*c rI4y!(F=_H&eeßI5LH18ҟ1,R_ mߢ*XT|l?U**7Õ`&xtM si2BZjTiXs Fs8Ʊqb1x-7`e%׊!dգ,&א~\6v\~{{0+4љ]&;) I Xh.TujB3aXR䓪Ͽ7P`CIuWmxL)-ay*tmD8Pڸ0s_Uhhg9Ќ`PyoqP`{>CrAƴ@ZrbE1(NcU] G(G:H,6 `?KX1u}768FV藵3r-%M9ǧt|'{)q^,EG|-q.ɩ'(@>8b8/624B5z@ aEP+no:~Fd)ax(h|ot#|%eڙ:!+$'U^^O#=gH!IܺYC DG2Qk7DLNu_4` )5W(R,)F5ЙS'{Bym T :X:[Xnփ-W` ЄYy >nR+cJXuAx"W,J,hw0$Rl/q)!W0*% ,0T6zMt Xdq!8rL<'q2~r j(I"6nB=[q)ȐrU|AT}LX-0PęeuFrQ⸘^UVY"A *AaQXs_d>ԱgYZsymfc-1.6.0/tests/compact_fc_Si_111_fc3_2.xz000066400000000000000000000004741511276756400204760ustar00rootroot000000000000007zXZִF!t/K]&s@l s5q*uK$o(Fm+*S>ʼ3U%R!\ b]mMFrj%)dU"WJͳt"䈃ջH!(P;D2 *A$ŇJ&X 5SG|*8!m7 %DLT22}CqaXѥv W405սzA^k.d7}yOʛ@qdQgYZsymfc-1.6.0/tests/compact_fc_Si_111_fc3_3.xz000066400000000000000000000075001511276756400204740ustar00rootroot000000000000007zXZִF!t/V] 1]ZR.fXVN?H&e5g\dIp*F:k qaPrnXUJC\^*'m`ׂ0iz7p\S+t=r]TTE2'8# -ZB }~'(@&#x72ѴXݨRzN+s>08=jp ;Yp+Y4&|6,YDb~I0\_ @~*rJti{h!Q_* ]f CX"MZJL%d%B{1 loN"qy-E 3}P.52+\/I=Ǧ!8ao1>}գ"* :Ǯ"d}41rb)6#ZsAQq g"CG[H/Ա#`{O!` 'ʿa 8F4C\.ӉS|1)ظ r䃲]t D97mh5M1 } luCxjሾV7ۢ1h$Yx;B+a悩e29?0"Y<gd=u d wu vQUboOZ]& <,D SU<* x pP}{u"BMyOއ'drWŞ],B@Ƥ63H{eXxC}԰cA#܀w<-J\*FHKFWsҙy;|_kR"̀IJU,\ 73B%˸A?1 US&}hAK_jԢ&m+G%[W^VOVMK9W~FR.Ԟ]m+a9.`myI:_1uG7L1*d㿒( ܯhXY2Pr>κlrL7/IAcX(?7l/OVVmDѿ4bN f0+5Y맘rR[,ډjU&־ÏB4QOK`l2=*x* bM7P z.5ޟ7iܣ2+8U5R2&+[Re{%V=|{Br6̃@!1㦔r:8G":K 7 %ؐv[}zr lE@ܲ!*+4ƥl l)&qRͮL_@]pL`iFɸ~+"+၃aΉT'yD<oA8I]V*EeE#z24E:O^ ov0'ECrp#.I՜$ŔQKCՐ$|;#t;J}y "RoP~+S({ [^ E7PXp5\I\\nKQwȈ$_/h % ?T*ˬ⥪}icr,^7Zp#pl}cY꿓Ӽ,@ⴄ`IeLPڧu)MCFŽ0b=RV@,@d]FoAY0;TflB{]2R]wdRl?"/ƺ*hDoHO)>9uQG_4ӑ&V{9D 5x 9' B=^VLojǛg%젂6A%$LƳtITA1<*FN*Ҍo=T%G#/: JBW_n>|yy'8 SJE(P봽&˙~SP!c{2! Vu]>ZYH~NeHZ<\͙FJ_>8֩4vmjK.,B ݚ,CV_"f4?K^6H@~̱!gCeB^YW0c/C&vڗV(u.4_+C+d~lTu13+laSj dMqėZttYJ3YlPrAyRkgPo%/Lk:7%©:*SބO(L2f*D!KG5('% Ꝋ+|UzߢVƘ!ɗ,7\4{lI$s:UI, e2=k93>@F)jNE]h͵B _ݐy x-l4W%|&}?ZB[hqR(K#%7_k9 YpSO'h'wX|v}drN2HU8+唡g,,)^3)yeu{dS{ REU_x*\^ bCe%+ԶӴLx)kF(۝kk!8 bSw-D(䊝5|{?{|jb 6 B_L#iJGxGOp+ݏ3uުP uW/U.% /Ydo9?=)PZy9ZaX /skۃv͞/SI b̩];3U'+keU0^8=HY+ve-n &,lk d`.},9S"8-:a#8إF=Z^=mor4•j\Oc$RNOwʒduR(C#vY8^@*t<\e6WL` {*i«^{ͣ-xK py?# eRLЦ["~lKi$9QGt4F,4}|4RXC Mk2w(iNampUbL \[=. ARRjizv"}0/TĄaq;$k%~ ayݑ/4 HPg5HЌ!o6(1[_Ί}u cxr!)A&žgYZsymfc-1.6.0/tests/compact_fc_Si_111_fc4_2.xz000066400000000000000000000005641511276756400204770ustar00rootroot000000000000007zXZִF!t/K2]&oͪLg?tXzJ;g |7aXY$ =0c AF}fZ(vaƝ /e*;, };jx"d/Άu<)d 88t2 7Gk}iU=J -BǮ&BEDɈuǐET: ):1(O6TN~P> (-UңlIn&yIrjPk6.3F8.^؇ \[ &yJP5H. "atKQ6C)3bлgYZsymfc-1.6.0/tests/compact_fc_Si_111_fc4_3.xz000066400000000000000000000067301511276756400205010ustar00rootroot000000000000007zXZִF!t/V ] 1]ZR.fXZ*xY9d灊5(6_/S#Q!ǜ2h}oFrn r3 l:3/fr;>JmDP;l洑* 0i\7,ޱªk3HFoMðFR/;0cp߱ZMNdsiEoԣQ zſ8]T ׉ Zmr 0xK#G^? oeദCJA( ^.jCrd~|B{YECF3ZG~/SvN{z^'Yӳ?+>0Kd6ꊱ_ $ ?L#rJv1: z'Ffjў~^^';q!ahZꤾ";h0! (vp>! w]Y˳SjgȐ Ym^m!~ /.K]N+q=އv}uic ?b)dlms}Zk&5ds PhW>N͞_6 Uu]NLlQ3^plHН Y'!S;mx&#9dC"VĎJӸ֤Lyp5`컥̴zގQOoa4i9a[烷3 }XOVNI0FOjگa?@cKI&nv<7[r:^$:1U)oo'˿T8bIU5˨(1? ~z7͙E lY/#A34KE4^>R{l0vIJvgy>_jS2%JH0am =W҆L6 &~D=̳O3Rep;P(|HU#&RU!{ [9:q(Y7`Dmbv5$$ 5]ɧuZ%DUA =͂lEpg/Df2D?=;rc Z,K}_orc^8O"hAI0f155qzzcB.0*M;ܘ}UrWCBx#}NoM[͕f$<ΐeA؉Y\Af5{ ó-H"Mv?Z&, Ms ۨ츼P3쁦Q+3atf`_xqIIs܎L8j_EOGl_˻i꽄N>Qڛ~z(3IV,v;X\$jL?xU0Ȱ ~QѠϐ!.0(`_UhG ɹ4V.΍Ʒ_=!KvX7!h}&7'%Oh]FÓsi{ڭ:sFw;&7tS5'ثYNG_9'RdiL2tZLt3_ W:˧5q1_+q(=--L=XR7oZ !|&%b:`Kw];-VL̨c[.n{_² P %\ڒ43zsB* t2C]^DKmrk$u?lE`k>m6$R\D+;Wg螊A%r+"nY`yI24$ ؤHr^=1qLGX˨,n yo "I;cD [ܛ%oxȁj8 Vo/(-UeoGܑz:SLn*3cbgŀIXrW #;ny _?YTHWm"r CEz!㡆8.KsziaӤ,`$SGP0Rt& dCFgWҋ]*Mn-x`k_:-yټliq䚊xK2ޣ[j JW?so2tT\0זR6 :&I]=doV -ߑ5Oy]m)8t86,MņߥR 85ϡn7ßkI$σ֗/6Q8GB쥚W| [}"d"z"t,?=ݤ8utf('ք4U#G~2V"pjJ?^94 M5sM.i|ߨoUԅWzG]Ke\y t/͇۔"gN\pꇘALsK"^9V(;#<̜`R\+z3 1}G@#+ YA UwBZ)Wzc#AjzޘPgW ctI=gAvMH1V;a2,o_$ŹeLd V.HN!"vSNv́6߈xƇ@ @xE&DŽ3xg*gf3z- qB׳nzHK_)l{z*{:Z :+%d|JO}WiNӲpO<'߀gYZsymfc-1.6.0/tests/compact_fc_Si_111_fc4_4.xz000066400000000000000000002014541511276756400205020ustar00rootroot000000000000007zXZִF!t/ߐ]Aט]p#AI]}_%lQѤ1$|Ә`4d3%/ט\+ ̟s]1w2=iT<5ѫBb-R֒p͕snpF:vzGㅄ7/^J?;Va 6jXFo@2Z1m6|F՛]R-FF;3{-zbMcֵ`n=&GgdJa%Ƅhy3D{ nꎴ mDB{m [V_8UGh wJWpjUqs<=HdQ)k1m6x:gQ=t 3SЈf rli2 + 5Y4[k$/[?QiV<aMDbUi eOZecP|t,dw[r&Z.d]ZL<şxn6\Gl 4z<7y\crȬu窼\R&`!GUP$fA<`U;0`&lJ3?l>RCpiQϋ٥}au syq37LLv{_|a j$Y6+1f_i+)htNn,׌'c1u{Ǜ| 9uFQ+9qD"Bu@ƎmNA] ”'ף)ܫsKI-9OPsJ࿯~Fjjmtn4QZ`=i6qP޿fkbH4BgEy.tC;PJ($z{w?<2MrK[[88n'f1zcuv=zi@Ƒ՘$I NXqb> jVc[E~ 1iARԨܤCft<* =fN>Tl|1"y#}ŏ3-"h1t$qHƢdiq I/m iW~":CP^_bRjf#suS Ez^GLڴG:ZN >LZmN <$Y r 1i |{ŶVM/a mLJ3\b⧋& ]!G4ސ8 rQ>\P/,⪬nYH~'x Ch# oN*&M6[K-j!;0GER|.xZn <u5-9@9N| g%VVh؂ͥ?cCzJB(AZ>VNj\,!212B1rgO>sT&u~ju*a;Zr^Jϩ,wd6z+W;,k,l XEA2٭688 k n1MKOƳdWrMUKFw\ʯoމ7]Y[, 㡬DzyMxδa2 ͬs$eR$unERO-Iَ8|:*^"h#?A g*l`}M^v&+^Zik9A[Amx?6kQ3;ȥ)!tq0SqGZ焈)\ar5(QS MU0R,N|tg qL|YR몞\Uc:R!5(0P|%{cs5c1b9bJ~nI9(!;y pLI+dԛ4wgk}??#!iX&3(R}SCE nL6ɜ`f6=q=WlPhf [@,X?g6=גw*K D+rE|-㷾#c`{tS]FT;$yaOCISf #9r`0& rBrQTw1 DS4Ag5_tBT 9Fekж'2<|o6Uīԍ.ׂt*ކf'=\7 qa}M0NꐼPc+,=<_,h#۝.txy>H;W|/7 /H^gH&L?4US_]WrGqTY3:=OKdWZ6aę%u+:!;S{Y멬:on~1#/=,"Z]'d6^wL3(m7#h?M}\ ӔM&" h\u2F!ۀpQ8;F #N껙KJABk,0*J鮴ϕ[Ж-ܘwFMӥ^1 /F$:0}FM-d5 ڕzIw/"+*P ~~GrchTk:NyCx._3^yBܩ@pڟ"$Fw/w~LBy?a 1GL+) ]WoUEzOMX˘kw5 &~cН2%m,ꁱCSMdLP$͜)€a vЕ`pI6+2A\*o o·qW$]]Lk`S7ؒ0gm[\9c Z.R ̓?hᖛ3_mI*xq"y(1e-RClV I1m {4Md Ö7eZ`?֭A@-2 p_v86Sv awk~X }ٞ~iHVWp_gst% }WrpwXL2P+kc}6C~st騫'5jrDl,ܴ Gp'GeHe$MP\T; 2I$uqn!"cYN9NqB{Ϲ"ϨDc'+ƣn[_/?\X|l% L {Л݁<:,:AbDukw0 ]bc:sɔt2/jg{tα,2SB>κ6t{%jiXZ:eEmgvXD=C3>!|}$q!H1z_*CjrЌl=lYZ@S6P]DzvOt!JbUYJi7\ުOqᦐڮM/դF{Wlz$LEAci:ƤiX2xI_7|mp Z>yt- P-4HКQS JT0hUK/"7oq"Sę͛ߑ4wwp@ojy# ,p㙌Ϊ O1,[ot-xLUd ݉*g!ˎ"{@bOnrcMmy:ʓdE1dieJ3p~BbAr \M3zI`/)2[44-ZA4#^`,Es╆@Dcx | 4u@IKч=iF%yߡL^]]PJS2 ,DQ &y)6sYOˤ2.áIEJIZvOrH!vPm/Gtz"?IVFCQN [@HEv50@C:6*G?i58遦ذ ݖh8NW>Vϙ4]I`a.5U zSCT0pmAF4} cm-`!I뫝vDQ\ #/npZn .݃r7q+䒺Ȯ"Qz<.4Ћ OIf4ã³J^o`褴[I2~E;G9-@ѡ~t`[NP ]/촢غ5d,:P2aWkx@ y[pB|aظTO^X֟YX]pSQc (}ɯs=@R5{`I e)l*Ӎ̑$낅KsyX9ML 7pȪ+5rwqiu ӭK".vls3mT:c8j)#zI? -e9f\zEۘ)L@w,HLW^&R;Uٔvep3ODo9l-lrP@SحMJv"߮Fx1x7D,N>ߺ6_߰a)(Kۢ)蚒}/>猴! 7"MIp<'1x2T~ ]K=$YRvLn3BtRDEq萙]~ p|h1n6YFB@Vﱄ2o4 _G8Uj<59)DI]26v|s)XT[K8ߊnڊ66뛠O Wq|LY-]kD`gLo!hYh}>7,ӗ;k.Ś/$Ij6ukJݨl,B|Cd ~`_bV `ZNO m4quǿ\),"߶pUC~{NT;#(.^(tg@@0V#{+,7/&ZΛs?ٌcx0~)IDrن8w]^:;?@_XAW.ԗym]J&%h(Š#3m+rTK'9*D99zr:V:,䑒&2.i橑0cH֧OC_Fñu6Gd 4 Ӛ Yq3(NKF<'.aa1҃5knZ`\.Ԫab0 >pt -k:,vVa"}f%/_X#(@:r=Kl^V$҉#P~it(c)E`YNЪZ`)2ҋgUuCS@M?g~q%k;Zo-\#AAKtzwTca$$';.p_13een! cΠ}UU!:5 aX W "ҍL M(weS\vb)JҦ~ֺig J+(TX YGɉF?[&qK<$j?- trSĮώDp" 8TW*6fƣ=qsHxY~sn^Q^pڞlEz pʫ<5mPݻ М~]6R|Wz'^[-a&Yxp7|9li1PJՠ*6 9)Jc}DU9 PNI$jc%5ʊc,h%p6 t2jyH 2IeLf39jqK'@U~g&<T}҈Ӑ|g m P<Gg.J}QU'9K٘tP ܍,^\6$~H.!9z@0EgPAҞ1sNK}> y٢"嚺) Ljfy]}Nqsd2:H,Z'vS炦8S =/6s'h:~B݉.2 `dv}O1l˜x.Ș X7* Đ#N& 4C@{NAՀ NI .Poc xll[I5AR*ȯRƻvlغ-n $agn9/(+#]NHQ7QBt^32 r\hrѽ|X|4/}H鋰Sb ź\ 9h[PS#gx늊RY"X(8^ RӸo]GX/߇ϱeUΘh¬w\>R}*CJ8'[ۻ; Ϲq_WLkƝ)]Vqv,X;Q0GT~E8 iRrUr"98uCoSUQyz~+B@ Õ e5]}u#P>uzc sŷJMAF3TU`:X=pO 's$On-n2(C+6^"B&N~/![OE ):WHt%-}F}F-Z)BDA+ϵ2p 9Mm/Tluں8cC H4Ja&ȔoA6Ӈ/&bڅ[ 0u"U f@k@ߖIM.0lvjeiC/JB49w+v4WESG8< $@X"Wc^K:qԔ IFJlÿke [wTx,(_Go!9_t8ǩ]رKK޽ [ے0o?\Af\az23Mv jf16CԦ/X|[[ 3\CAl k*\{|LT+cZtl/x$@q {&BXO9 uTo ܊8K.3mgajyQn; Ҕօz$_=ӎx@luwsƄG}CoA[]?@j+ɲ|^˧3\)gKK^ /d{$đ/n0Y jJjF%&HÜ d11 M k+E)3)R.6MP͔ 70ː}j=LWzQT]tVyXU4GdiaHƌ 眃BH: aΎdaOlLߓ+>#Gy ˉTCKm AYwcbG{6؇]w08 ?EOʏ%|yJadn/p蛣gB`z 7ȣ{nՌ|J>f(x٥;dv(Z!VZ]v %z'[[e:7cgT>#l!]6LT/8oح4~](`Ke1<+yX\;S#n??Qn~IonTOԟ||Ns% Kh8Qp=ܗ/jg / +?c`#,b)k#èmN1Mp9JIؖh &hPlzӌ9|RIpc[0Cx2RXm ["jՏҀBh/flNv  CN]h:FSڿhEAV҇F=uU5FiI%L֤V.Y|1まvl|y ]d!:ܡlj!6:W\05+`s d$0i*?H|x쉭ܲ"D8<.8٘sQ#Oxw Z:9Y!ui&0"Rex+Iܼ?WHO&jZ5H h_ykUX}=qpcAwޥ4rG^1OgSRۓnV?"Ub; Hyie}QY/2dVn_aX&*JZQ0;xCY OnDxzaH` iZߐ=q*÷̎i^bY m#icJ.Yo%Te\f,-/Y>,3}-ω7~nVd/3yC|% j&tⲁ"hFlU,\N-l޾=dHb؊ZkzFӌeE o {6V7Oa$kXÆc(y SWӫA<~3v%LPW)&W5DbbyCv%ޘ:D'&)Ƀl2:-){V7_2 R-1! ̺^6v{2FVh<#ɤhL!uxwm >=ũc=E=yβIa^(!y={ t1Ģs_nu'v@ЫzG'Zc^|]y pԶ<{FDoiN|-$!>KlBmO>S:bBo%.g6卼d3k _.ΊZ7үqjbfLţô#(u4vjM7Mh/ LJ;D:q"|u |t[N'RRJ{:BwV 2ƹ&BypLNj YhxT7&_ $fU٫D+̑Ha)YoE;3햂v)fo8E+4 -$'n&B ÃgB.4iTYXA?טK ~h2%53'\U?3UceNYiV]T(k\ӗcTǖд~~ _꛿!&6% X>>Le51OS=Zq[ӬzWf|%Ug}kѐp~mLյ4dR^O@O*ƍ Yc_NyW]'ZTBD/\}#` BVfzN=&~{I5JkHJ)ls7(%tUn m]!~\TT[&jPkAڝـ% -Jm3f1h[c LEVBӥ@DzQZˍ=9p6E KxA3FG8jOxR\\leS Lyw>FskWĀېε,۠4SSZPHf}avۻ4@pM"1e7ȉ;t$MQpRQN;" #jX ̎yJ's)sΊ.#t}`~kVV$×^ڳ]s 8BoJDpFy\f=`4rˉ㻽 #)݌0D7oZXv!.B=x|>;˾(d2aȥloTI9)i!ky6|4(376R9zYmlD;/=[lwQA7dPʍ1zR/mkDH_Էew<:C{wI6Ͳ6%v.C4jbF!AioӔ'kGixBƏc#BvJQîjHq5__Ocz=hcD9"ê:۹ѭyy MiXPqFv'*i~㥏0&$G, {ZS1D+ ]Qߚ/hlbJnZ##vUބE j ж#)$j$lt)G)n!hL@ž*|J\r̈t[i2SR >-!^t01'>';&NS >DyL9uZ45otP[.Cb 0 \ں96* mT"ףz@[ྤZP67V6fwLft@J17ej!A} ݋?IQ00]7L\%.5'0Q\iLx%xw}\;'z $-U=NK'|MVzx(U' Kvg %xD{4E|,moUn@ӛ bg >ߤ إV3/7].m8b־k%sio{<ͺ| GW s뗱{0~I _/t?$f4Z$8| hiV18`_'Ө.X܁?9FS%KGڅV "O+ Tr]5m )זVxp|YB@9W;mð8I`@5Ly ~9w hA 5)Ԍv\?й$`e [@V$k/u)oxz2LiPAۮ5e\gFՙj/Or3ճj>TCy3W_q4|MԹͫb}yC},>_m3*5 ,#."~IeLbE3rHScKRechݕbG|lf ;K"bH1j_ҩZG#7 \a`qqÌ}d=]tQHulZk"~ȓm%J,Ū@X 7Kw9W)<(GqTrf7Q*ihD8<iXCq$-Bi0[ v!ǯ[51a ۺ֪Aߌ] tiD튨851K5{~.Qd[Ѥ UX_ ")yo+jIb5ei+K0XuO/§Q| ?qz0w\0]97~* Cgv1`*ɇ,2pJP ~Us=5؅7"nE$ڧ>qvm ĬY <\j0aVKL8mnj屍f]į؜59B4BvtPֵFb}ګbO#-G7ژ5e5?/(mm,WASϯ<$&E|Lwrb.S"_1+>iHJ('q Xf&/XJD4h<5Fk1yUL`|6!grz&<`A1CIFU7GW]"?xM|Fj ~օ#LIkbysŘh{܄ک&Vcϖ&.fv{fG{(BViPNd$ygͳ< ED O @9h0P5 ggfEw#2P8WCH$9^2NՌqnOQ[hhK9or[ oNL?Típ`M|R%ijBOS 10ޗMq~ JiPQA x {V8B8iŸ{5?˹&n.p0ڒThf±w5uhw\ee)4z W 5k+x)LS}ɗǷSm|,d?צOC  x.մW4hCOϯ0`pc!Faȏ;Cߝz'XK:odFto~Y:ʒ-2?hދ!/]edQ_|vtHt?J7}Uݏ&!$'g.'D_'ۉ#VD9)YLRn&[#z(ǿ]YH*{} hhi_q#Ҏ`dz2auHtKŐ=,Gό̠,{R^G38;},i<' -`2/&A@Q`mq%$oaX]w!P9 |ZLZڮ67%\;fmWu@Ϯ}GnNĹv:#և~.c(F%b$*`:RWIF_Z(OB[K`nUe7BSFN}_,]"V ț((Y; ;t1Mpn_Zf~8dt܈e;D`wJw4`gSps9茌"I S N4"dZY*Dۢb;zNWWauxV(sNR%,/W`}S $|7K. i0B#+X D*`*5?b6y\d=Tw.YO"j>ؑEhVy @s@5%ٗM@W֮ӷ^o\Y,ȁtHm56ʡm`Ӡ7ɒP".voR6C$? I%8։"^#wTREfy8Y$[tgʢF e\| Lz?&9w C_dڝt0Ki|qEpU&t,}XHt?ױnI!i~GikOù\ߙ!u%&_ r${Ǘʂ[!,wܿ`|kʱFQM~-_)lͶ&dB=:AF/lzH0 Q+bȿ[*LhZbw"&ߨbFKi0餹k' 165|Kw f&A5UK3x͔yfnEc?Xǜي+2 0Vw ^wv?T @G0 s^#SV;*O]&צ*-1Ud=(HQ `ؘ0 EEn;mow…#k]r~cWG1FnSr;h4= cȬf/[=FE@}v+iSs='1sk}LH8x͒QQh;1䙸E%`GOŬB΁ =4t 9*ӎ;J4-5Fxf~q>W&NQQEo$vjϼ-^Ȇ";@< ū=k/7lqO7)OIE嶠-?12UߌY_&.:[bnG'hMbX^TX4.6ɢ9}#qόgg䷲ኲ+nq16MYEp VYVi_fw8DhTHv9"ٵB9]2d({,eJH0Ow%FFb[e3a<¶DԦxfUGq9ɏ#@x46=F!cɝW MX#-Y "n Ǧ2TC\1pen:t2Ͻ`ನp.}1`YRf'ڤ;/3hyPԨkLeDv50 5h7Anɕ~uw`JxZgM@vw_͏[*ՑQX;bq>:`D=AtxY}-*$\^nWP 9h}x$VauE7foRt*58u蜤l*r e^0bcO(lF \,P{ NXl0dbAhIQdq70ؠe!*ƺ ,2Yf'0 lb=05mD< z!j<_9'Лݳ˻/k)U9 ]J{ߕ8َ?XΑi1h'ѷ@{`ᐂ\Q4Fc%=$M?ȟmM1nph !VuGm~QT0pZ3TP؄ouwC!#,5Ԝ6c3O51Nhm{Jm2ӛW|;޼ X}/Q[CONDzH &]mo""T=뛙Ң`'ih$).D%f 2O$v%"t7|UvNd1$%.E-rщLlwrliѱ*>38yHu>Z4\ۻ]⋆kk珥'EۚAkE`,{􈬗DR+D,=s-*NBQ`񥗇=c+Gpܾa| 0$,6rYz.2t :s4TTiVº3.$l*P2hk{ƆeR ӑ{6*QM;K Vl%G5<)YGY|] `veqG 9'LR 1䛹޵/äS@*ID0f."y*'*!mFVrRM*0HѷG0>[(or$Ѵ7sA^v#W>w._nԬ.n\3ch<Lf2AE+z@wmd0;Y*641BcJ]v<7GHRyȧbZX{P{:2)?L$O~@]L[St0蟵1>Qj1qYVo,P*uX-Z@`ضdx{A`4yTnLJhX[`N:x"ĺc Eq항?%{ǵCÓJ ܠsW=؉pBEMm 0*?B4j)"7UCe|?v2q?J.UR?*ҹ$c2q0跨貂z ߄,yk;rO6W;w[#T.V=_%Q5|\1^GG3ŦXhaEyt5{=@gp솬(i,h \֔7=.@xmq! EcԔu%"㖗6eAa۪Bo/B P[+|yLDǴsf"ItM8糈{EJ$RWv72Iey9s;z2U{t)O?TgaleTgɬKzw%Cj h 薹a `J_wK}I}UeT._Xn9k'%+'QI`Ͽ  =j~!2P^x.ڋJX+u9rZp w}M p,C,Gu\nHebr$&bϖ0)Fl.Hfc?NMk:a76G=Vo3)ID Tė Be];jlQ9~wxx z!?x$1$t{1}*%&x|~_+ \Q,.ђpj^Bf]Ɍn6yKp& z޶:HFy'|WyIobF3 wqg+,LrhuzU1lC"Ã`5TGiu1Loa/N(f9ҋ4&`vskg:!0Z^)RnHBM'UgXߍ;/˹"M?wO߷"7߲a9,DfɍzoI[!)^&?cd_QNa590q%fu0`( bJ38jI /EZ;'O~(&pjW3@Fн~:PbXA[a't8EHX~N]ͰgcǶq$@qT^Ԛ-3?-Ј"cbYmm|K zנ ^0m6/Ovf|4- Ԑ!ȹ|A-Da[7/Ɏ!^-V?X*k9  RiKϏ!fT@OCϹ|]h[`ks/e0f+J 9HaI"r߮$⹛lX|AK`B.{Bth\z{ȰEs؀)W蹏S1 e5/|R =owd!td`KFv!;[Ιw) B~% ą"ȑ*ܦ7_#T=HL(>Y|_Y' 3%@ 箻f#MuŬf>GN@}WA+0'x\lK1{N N娞4MC響ئl.X\?C^Pj#whTës| ,m~v[!/'a@D#_m]ltnQjN- MBl"=5TFs`w b̲?}ڣqZ6j"Q;7> r]kv{;'(.e =btJX Lp{mn HZ)pgNjHW21چR3$ݕ Fau$wn3WmЦGճ/=k|2(uݬ<z"A*3+ BJ*5- ;,#H*ojJg[L:8oQPXFPς1++1fC_ lħfx!GC0NT6x #+?_;I\Y]sC W N1fq͔BԳ2rXBq*)Kokiy.fKY )vs߅3p!Perw&1!L{גn% eY@#VFw$tq-YNU+9Y A2D0O L2!$ps>xTn)XQ2@/,(c͂Ǎ:Tٱ)]ס|E&N_ vL/ث& \ĬJ3!- W?chk. J+5.}*!(¶1pgɜGP;$!yFO2f %x>jCxa-D=T-"o!~HRt>j|-_|yk}>t? [4VyJ(YC4cKN7ުS!wH[* "3MCtfbj"C;Җʬht: /#:lR@Kv֫;nt}:Jb)l6HR+ZfGi a4zUrѝt]ՏP^wNr{D0hTus29Զ~ DCQ. /sRX>Ph̏sq '`)&k ZZE^Ȼ>y6C^KϾ ;l5|jFYyutQ6zT7wVA~p/r1JI|W=d:p%r wR}4y?g2g3,xdc$I3Yp ݷXR}uUTYT]M/bXS*Rsl=,+.M:/Ψ +NUy9L# z\V!pp 7xO\>MIhMɦz׌>ӆ"C_3uzC膿%3&, -_W Mԉ)ݛF uNRe5%3L_Û}f>|!6kO6*?/ow#MN xʓi!4MYVu.WUgct=Yiԅ Df@ sDN7eOlM>GPI/Ϟ_Z5=̘ #Cr%8<#?yS` l [?V'П>: AGН#Y ԶV4.| q|{ ٰ9 ~ʇ5_/D+j G{ XFs:YtaK9Z{F-&(Au^pi)Nϴ{L|3Gw3C+啮2dh~Oj)/m_yyC\e$j.O;ꏑ ;M݌H7tQc;ݶσ=F[O{ QdҎ79_ `H r1H(ҦZ>}G媸 VEe iq{r`;>~.#rr ?<-MhfKK~P~Wд=L*x80WGvf{b}/"iUH[2mk,%4K*)X<; W 寳&}k2 Dx5KGkC켻:r~X|n.ET7:4*{Y6B6qQrWR& KJ\T9Fb6#Fw~m3Wf |޹혬%d;l4]p_s&[@XFVڕidZ !J "wkȴR\q潲~f]d .בf-78~Ͳʷ?_J,ye\ ?`^g*^r&涉 .[uzaW#zhgK=g:\tg 'Bt5 \Hz4h(Ty/E=a$K7wvs`!NƉcDBl3[ ]M%iVQ.])hm%,p,sf#Hg o;gtȊxCUlʽ*Jl" 4M؇ӅaTJ08{JH|l7891:PL>+Cuh"e;vg}o^t'^y'꼗tP-fb6vCG|BJ­NXXC&I}?BT KHEN H4 60yMic 8f{[r\wݓ/i$_no̴*"q#:߼"-ybB4 6>+~鲠 2ZW^v `ÿ <({KnQ=Y{BvZٲuy,{( dc`Ff]qFF/0GCFS-:=]O]M )"@36nsnd ,$E<%x 'iRGbDҥ{^W$=na(i2goҸ]98l ?7qҗ@z$lZEؙ~5>D_8mB.|gaF_Zbxx[5oא=s"9 4J6cԈG.PQٟ\ )h??$AQ<0̓Ո _ duO $e MN/&BXb+zx'7B3TWxN*dYp1C4XByGD$=3\6m㕭RֵbuV6 xzE"0E ԓ>(eAoH׌"RUC1yR>PwaZ٢A=Gr#(#9ZhSJ}#́i>nmh&Ŵ\Pt`m>8XmHpU[s`@ilPJQ$( 3: 'ư|Zr7t"0wP?#}Van'ˊ< r\meCG i@PШAuGe3YF'!TQuj&h xձe8Teh ]R}Q2C=; =i(8ȣNC#.·D\6,f*^ $ vi,.pؼ$v]`w"X!3S/}*b:f;_Z,e{Z.mS4e9K4gZ2*\,Yz"t yS)|:xL<^WG[C%Y`)Sf$?QꔀZwj4~]Wz,_?;zL=nn6[l(di>WsJ[B//im`>gپG;- ֎r ?Uy}yo)ֹO%l[@,IrL#icgȍ;\t V熎Fcjs죦~K-3z^UмkO M >v}1 lAKIi8",|;7 PЫ1U_6+mJK*\ZX6\kf}\]h5LS x.լgfDY<qM3Fl{6pBm&⵫#'D\&Ry6@EGϩ=+$m$Jp m⭮7eȉu ,q,7aaly(xgɋuU^X=[,Ӽ0^\6 ~48:{PLSuQZ=r{q|NFJ,a<26&US._?n+71p_[(TCupGrMdq_npuc1;FWzЮF"x/B88*@"7j=fwPB@fP\zlBO1%MvM "e{ovŷŚڃӯS{Ai\ 4(5B_xdtVtIܺ#QOy0k4* 4|e60@ nvf/מX͕I6%@̃/pʡXcIwb1;N:ٹJЦ&m_Z 9d@NJy+L),e R.#8 g,w<#Wᩫfjj(L:Ul _@TLnrI|C5A:H&kxI,a9Oij%M.+ky_:BƓ#S\eίh{쥽;#UX`d,YpT(V?<- >sq ?dV 8/*6\kq}QM 0r*ޘP )Xf? TG]-ɱ t{詾FIrf Zv20 caհr9h0*-fZ?j$0w.0_p2;9ԈQ?S{R,{Wk 2ƽ(L;r:dfSgPJܳ+y#^]b5< \_ljrmɚ@Zj8;5JG1%! "ʖF:(?A؃6&K;L*Rr5&L DSy)\A0M;Pr̲zco--.Ơyv}T{"eXbkݤzMr]=x4]hVQ [T?_|Px(qM8kr~X>!r]6SJ"DB՚'$Zl_³+/=}Tʐ>fHϮф>F!ӟByvEQ ̟&P=qk*)2G! .$xU$?+\l9=|:F{Ceۖ&Vϩ\<SbbS^ɟފ,1i˟h Z9Ò*g:tUJI b{G蜰Zʬ4G\.5Ⱦ޽J`]A"FRmLP|_yݵZ9`.$_@d?<ЭOltSqo66W*X8fmt/+02"V?4[^$~Ә /}pRАNJFqe uu'1dwl>1<KL%M2N?hMl:Z9T,FW5{[k2=X'"U\wK(=v@v~dJ dV:XBv5?Yji>. NUo]sSAЇFȉ A)ʳVTW`'M.WYKjڻ*AltU3R({+4V?l@LuΪ[t,$ a}5{qdh¡"YbbU 躚LOrJ2|Z=UUVqR+v ×X\Lx^!/imbŰrcȞjGP`c 3._y-n'ɥ}t 3n"V?B=lle3ҧ dͻmfoB9%hE__ZΐwKdzL&6d6-^ʰ$1 פ[ߘ\fQT#)UZ \fg3C}GXd9PNH*w\dc ũHU|\scSUȑ@@%|ћa;xJ)ɇf)a7b7&v0tb1k}:%. g}y(q&ɖV9~@e%)}OZ$n1qQ'_3pemO UҨ(,.FI뛌CzcC=:4hh Y#u-Rj!88tӳPzpl(O6m+wX&P@ oxsUAOus=XA@#- J wOUiE8[x8ڽ|㾇doۙqFՐuح=?|$@Y{*#Jaj3ˆcMM*)vOsҹG KȈ6{&7y=Ũ? ##ىAF9"Iv%ە MViZ8` e'?;.OwQݳ&^N 虄i9ZW$5OTQ>R;x#'=x ۓJJ`{s܂tS7X2I8:5U' M&[d[G0ZE!c6NKʦ ΉhrM"YRL%I|AZ_=o L>7/!OVL#VSJ+[#gaM(`{0|,cw1Iak.) 8M?R$ 6|@3?-0ҌB@!h LbBH{*I% }W|/%7OsAw(.$YBU=NC54ߑVqe:da˄BkZ/ɠQIB9'>3AGjKN|螜uP58^l4Xh(UPJ(} 6CE)8isVtHW8[)M,?ɠ\qwS+.:R9:!y䥁cϳ̊[G *A=ش6zkMcYCo9#_c28OYlg>j{NїheO++kQS<]}.wқK >8a6)$r"hd _1Kd5L\Z!|,;>X\6L!jȳIjx^Ɠ 4V ;c1~M蓊;ZL4I ?MaC3-yBJ!H2s#6.[&md(f67 h&êT:(ۿn)Iݞbu0ϑjeM) EӲ*$_^cF6;yK8R  ~Kg/+aBd@ #=2Y'sWX`t̫>mkւtF*7am' xXwXir7tjwf[*3W & X K.E_UMy#_1_@ Ӛ?St m<\14Xf*.016UJV\DP} E %.;K#"ɂܓTv/rU 4Kcgu0E)7zd$Kϫ idND)U5JwN_zL7yn<,d2HYTW`2d1.T߭ ::['ٴ艤+e"4Yý8T8֤jnY7-Ѯt1W77;pjLcNz/sCR[3Jmo~_#,Zaܮ&.*Q?KJuZGk:לbE2Q!;$G&1)y!aEׁ`vݣzO9l\,xᛁSgIѢ] 4$y%X}}FqI7!aό_mcL|ƪ@X+D4 Pn,Vc仵F`}ڗ7saYޝ.]6&$ucņʌC@MJ]bEeDO5^QMX {182?}#We=9O;F(߼F읮+Z(M 0R(ɋ4G9<[58!v}$U]X{V ;>eZ1{TYBq~ o=SUynr(6C }ZuJ?+b"eiҕ_7;VAB:+\ѮT+uQ^8+E%7d۬zc&1OF+ooNby g}?A(Ukk8n5$tm~>k0eLm蛨zi&GkmB\ċ 4OA1)BZSsv#(Hѣ/ $h_RNs*r_!>  IU8qqD!uiG!;R1[®c~GSv˱qfz :kvl*'GL8V o8\cUf1m뜒]r-aZM .)WuI^daTurzNh~KXW\&{pYb B)>c.Fᨿ~ij$m_&FZ~?6#JN[d*6.2,v"qz_?'֭bR[!iv5s4gC9"=Tlgl-REH H%]:me#$B$ Ǵ{V_L"ؿ]"_XE;HՄOw¦JvpHkq,b1ZۏMoc0~Xy+'#!\GqB/ZLi =TN++z{lB a- WϲoJf_e#6~6UOq{E o'7Y.H r[ N{m0Zh$#5g'hqzAC钜wAH5(4leJTK_Q2)"PAWhv*.qr_/MJ*6K2m+X/sؿu>;iʂNXa]FF7]@3Aqʠ׭gJA591&( 5Ƕ n_^)zF+|MS]LMYT=]wӫӖ.DRw+ ,h)_&e)*\gxMc.59dͬ3{,{,S=yHL2U(^ޤU Z+^RZ1\2௯U.]Wհvq'1kٸ20b(C Zuqd7gg9ʠ;g|ǃBJW )cH p'7Xg39sOhٝγEi-aq;Gf`[_lViLJeiQ,L]hsǨ]SE(.4y+ ?灣TLj2%e7ݓ\Sgt,gWS< #D\Kst"bL囷UWh6RW+}$>SB@Hɱ9>̓s%H5<%`KR,XBJU;:yߢ"d5)~{wSk){n=pl߱kV@`I- 3eܑ%X I kjq)BN@L%7Xu^W)GdiTq^E52uWR|A̛,JҳM޹ZHoOګd ]I}X-0qĪX9zD\]C̦ 1%=~ T&nWd`>*ōKDWI/,ꃸ>#=d^8h&aWB e)_7Umm{w> [yN}^oV iK~W\j]៲( /Cœ@͔ʌ%pa}E͛!.#L(5*n1MLCy+LѧY7 Pq{fWfLar˥xb ܷGi02d]\Pf!%b/yiQr ӡ:e S -BfaAR%+ZCD XS)j)Gh? 2i& aARw~,E$,pVzٶ7@.`*?@FҠajQxrOHƀAeOFRsa9W^ŧLP~o+廦 6l5(6/V_w{Zl;䳠iE <>Ym~98mbo&|m۲:?؆ֵi)GzSF*&DA' *`%A.o8d[r1N"$Mm5IX'ŵA@r ]rIjܑw^S)TXt$ U/j$=yf5p*2 kU' Y$-Zv= .62eT(" 81yFk{f z YuT}&YǽZm}ωh7-(b@Kn0aq{ tR+&*Ėv…32´3~o6+7-2I!  |^j<)/#c PjJ*4οÚ+}P\R n4s~aV^XT3jܥa=inNƹ{ve}=nv,qji׏KW8-OC3Vˆg6~5>̬ZaV%!vqfZa*l;Ifv s<̲>G~zpPU V؈u53Q[4} m$N:#(ȧyh?=E BR HcMZ%1N/wM( =QW Bw r2sFN@'Lm[ ^5Ur,/CAo%Q\C-I9~JcR i\V&t4]$*6ܽ"!/)}ݜ:o e?Roi[8 )6SV U\"\ma!uXj!Z/g0!L79؃Ŕ 7N2$gZy3o|(NgY" |J]H&c}p= .Y1uByczI`)݀d|$|JJc Q8Yl2.ptώ +)P|Azt|UV%};.R Aqqwᚥ} v{ =_g }WaI[|Ҁ+S3ur!vzHBM±e/ }<2(}&XrG#zsh/${hM/!nQ&C]ߪjW KĒ`땔Yx)tf6e(\us/C]e):8nuD_"Hpqv´۾NcNӨ=X#H~:NC ղC rjمYB-fު@>~8%3zOIM-?&J`7 =9Fxa`b?UByyԉLfv"5N3 )< 5Iv f0Yt=[v0*Ԫp M{4Y,zn'i'GWL,*-!ɟщMpka lщD;H:ٙz'mBA'Q()LeXvV<䡣Ȇ3!E(.RId]z]+y^!qߵ` %ƣ!dÃOQ6O@N]Gh(|C[KUSf-SkU#q}yαff;SRv80Q&B'–ǃɫJ$EZxF*]qѴq^_şmUV 0*:!^χs3~=DA,;z.P 8bm{4(3xILMu `<%?gsg(щ!grn7U!-OÁw4? ]†:J깶&>P^Y/z"<)sdcj;e  AaK=i ɯnG[j1H+"\7G bd z6֓ @OP~bbf>r"K0!{S CQW6z{ 3giڡU%ZZ3m{!01#x.Z O|/~ͧOdE:RbgeRtpƛXvhdLG!M[4ykK*q8KGLiSf>uR, jpH!ȉחY 9fLg֮m^czuM+@qچ@ͮ^C\l NĜ2MvY|A3RxוO @,lr.ĈkV߫m0OK1!P밲S7Un~FVL6tBpRYuH:Sܙ'LxB4o"A?^eBp?Ss rq*+9|c8|5jdl-%q*vIm0*:8ӵ)ױ!3%Zv 2=Ěo.czZ D )V,pdq4ViXt,ge"W:"~{;c 7dQ8T~ʤƍKڕ^ O[{V.,Gs*r DhObø$^ɤRr,NZsFHataϔW(t&]0Ӊ7]@Q*4-{ia68 ZP뼶 tw%cE8/;tp쒆K__PO SZ֏f~[G(RC#B 2nViHE`L{ZJY᩼RJַLdz~xDR` b4p@QXB!zkϻ%E̩Ϲ9x驂MM|?$٩p>jeMw2& MouD8u10ͧ_XBRPdN]Z\4,HW˷L" oҹִXFQcB-1'61@%loh(AG?[)bCS $9Y"bw(S4nŒpܙ ErZص=~FK.Ț&,`=gP)QZ"}K}ΐ1J gܢ'#Fm䳄13XtTBL2klрyF5`/y jr7ZYwǮFflEW@ިB M TO_7}g3cLm!([Qa3uIq=UN!TlF{\U}K<;=AMҧ:~,dFL>N%TB׍=&g^5V^q`@܎@X_Vf /|1GS\cZX .Enf{@K+Lbh7g0*+čs:d@WE}M`*(e160 /v?68oɁ!0AVN;C0%hFyƔObc L ,U>#J=\uحAڪfhmÜ8[7LbhpL2k"qAo|-ޖ#N;wS۽zLuc{4ƝTV5]ǧ8ALd/[M4Py.߁mOܩgz P]|h&wGGa{mRnl2~Rm@>0$HC"oR8` ǂ5i;[cAr'S_?]HedU;TkN*SYQC:멣3Ѐ\4>JNPX|6lέYo!'ob"WK[-v'|GDD1#H>£ۀ\T}g;Y2*1yyͷ9>{>YA2Cp$ wڂZS 8{=/_.OW;OnM`~ NЗ+f}P,tFEy'sbW:xɵ*(,Sjgi5#zwx>W $u~(t϶kDAΕ}_!cv&|O.,3XDj%*^ݸKcB6B]@69 &' ގj=Ipz dV흑Yv \e\=}kKp9<.K{?p&zƜ~,9]"C>ImonbT"LWC0*n)YlW s4&"n` (OsPMcIfDB[oBUDVl6hLLZ|+@_d!#_<?Ů3I@̔!r` XRsrq_OG3R9XdǃͅD -72Ld 7)[\bnlV1ۥ2i\E}%3VEj~q&(ꢖJ!OFC)o6iUgʽJ8o :K{hytX˺o  $fE _ ; Ywbx·JђQھOL~MA@vxz8PdE#/Vs&\e'2npT@|mL+AgJ"f:Md;/*;5BlzbEгQ4k%N{Ll_7LR&kp,:徖(L zGYog]NG3 b 5K# "|I[:ScĚ]%EK )!ٗlk61]*0&U;=r$cbhq_9A asuN΍l~#8w7μqڞCW(T泉=(c>eEvXw;gO ƥij&'#)0؏F` h L*\ĬՂq,+flak<ۣG˦KR#mHwx V4uO7"}KBloc+'tM@mm]}b\2Z{P:?ԵTs Btfs-W\գ0J~s͋[}OTϟ,.蝭^cIYiM\Y,?%L0 DU EȑN$؁Ek7r.,(0D=o\zorywŕ֦azN5o Bͨr(]ݼ(r}"A4 QKQTaXDMQq*0.FBJK2N.,sWsP[lXwz5+#IĬOn'NI6S$:H x&މ%ڧל'7t^ec24v\jUEQrf %N[ʩsXQKfwi.)<}'oLaK٩F𐀢J6КM{+ytT_{q:T(@K:RdÏA)!v:fIJ[xx(tIQ7 v?= ~IbJˎoE ={S73fSGZk(xQJlvק0v r=zٓ]f*Cx78JV=>݋`,!,e9+>Do>.vR W%ӕ XEZɻ%%3CUtH!}>:eS,%J^r,Y#bXuՋ;6@W ~5J $ͨjsRӯ3YxYk 8Y~P%NEǐDogjNy^SK$~p&b73_H{C|SYLNۼ ~`4Z0 j_~AJu[{*9X'p{ںŒ|ܨ7\q=XUv}A &.nݷF'630up%'HCxU*X:)YxUmp-~gad,#ksb)s3"dwP7>V;5ĵ1J.sOt'⋭^r C*%wQiU-a{䧬OY⇜1:1[ ͠cf-v -Ysv7ThDmPz4@M`*2"$YxWSJ['_粛.|puM+qtRt7㱹8)L3Ĭ69 i1[0M~A([oOk aŴDdC!=T7\sж_ZL!QJ$_m'P$%!d,7 OeKUk 9,0rb,E/Y`ϔ4d7{IHo039Ӿk掙P}Cʡ/^ ̤ӆ/CL}ЦZa&IK,NVNئ[C~ 9H85Hg5 [Mi+33t<rj>0BJ˅H M eWTBͽj$o焬| 0yMHpIr(} @$,D_:Y [}_SyvvߑCKY6:9LGx9xݟ{G5$q,R,G`Yi^У"}L3T 6tWO"ËZų,0d7pK_J,z)27B\0 P!F{DNYJ罯 VV!]dU*@eAy3s9-mf|f} FC+5SܚM;TG)]_hM"(eRA~+1KHG9N빦0S¡#*bl7Jcv2s ZC1OYèǬX#A'HƓ28~҈i  T#x}7ҘgLp Y*K}s ㅮ{ "a AqE*Vc.$ [Jn6-bdHhayDQ"HI8f*}]F1%ULiQ襗>fǭ7ڻ^X_v'Uo%K 3?F4-a؁*B~Co,|[h6@'  A?%, \W`eʧZ$I>`}>Fq{RLp;.y 1CgP!P-vs@Yb -*Si.QJL!vNШs/Z{mubNgbCV5h|\(xU&ht Uz/׵e%BHl@T" deyOU5f>_(PvDt)7å@K~*^!xLGnb QdvC%]~;͘S2 6G %Ugօ :{AI&ƒ]UfFFB>m5̬[ws Dwx'Jt!I0{ZޕkG &` =uhq,i-1|B[ԊQ7sڴ`+&ؾXw}Hc.#gϕȹ|@%͝P>ܚL| $EGq: b`ߎܾ5@l$v%Bwղ0&#%rw8h#"LE`1>)D)cٹrevws;ƗP65s۸-jlZԅ"_ZrMEWDRsHt=ʵ~íF/VG$&P,lZ/VKޜPؐ@,Zix;U);0ύ`J0îhRH &Bi@cu07ۗ k:گC |7v+ûtoVs 2?"ⓓ>4͜]h,b*Eʥ  y#SMuTqĢr5a3~Mp^ G E+7`Xge'U'}=vO|{4A>Nqb ȶh~фBFgUEѻK0Bô#~km Q2"X!V87,Bd<M9EK΅U,쩺 Į T!_G璥$ILc\t`L"RNgv05ԈH-/6~҈!_@UUy6Q'7]5.E}{V\<K/[鵩qٌ c G ]߶z!k ^rT4NUQ@8nO^(ZB"XmN|oG ʛZB3a; Of 3FݘcHN]{C_oG FkѬ8OQ܀k&D}$:[^maB?Xmʟn5yʶ/aJ+K_O 7kQv0ԇݺ#ƝeNH!imm[.IIe-yaQcf @;C:ȩd6`9vB>A³#f)b2g.7/Vd_dŪ%=ɀ:]ZK`CýS/vр{ 9*T^^隂< )C/@ RaGcB8wL9ZU2:gxi* @ AtDVP#ؖHU{+5Ul-b˱r20RZ^6O/@ӏm% P?WR|n('|ƤTI̵gi∄J&y 8,A)0 .*:>o u,amHobg9,0WTzjSl5+)<'ؒ<'$L;=bs3BѬ9X%5vAqe%'y"`tu(q irS 1kY/B:gx&rs/ k'WlgsNH 3?6/wzҤoqjҭP0c9;b<*2a$.1fZ'x'KFeqwaߏW8u‘[EƗNq.pKndɚ,x! \&[,\"7\ؖrTGaU.F،&G%V{%W᱙fKmt\" P|V|^t h,mNf`j;2aOPWQʷG=,hq5Z~:A }{uS{ qAO

2^ЕbK8?rw_ 6ZRImϷ1+&l`!V H 4j*&p' INow8N1`E_e@m{N;VNĆh2dB)gI2 8h,N"dlЋ8L3y]z#_$1SG:U"鱭tWBp7W]jE;I߷8$UטgKtDˢ ul{)L# vp:z4! KXF,^os'K_o<ɤ"dU ADYGF0Y&7yҷ &cʈr] 2:$ }XJS> + qڄًmBjƾ7f, OmppϜF=FZ<h 99Xo3 <)3'gxz4+?t-s*ݠ 0kW .j # SQFkwmpIr4T3PW@M pF. >yAqY\ EGYFJ83:U: F; ^vQ"w‰K1r=?D2*ܠB*j~( :éKޯ'\AUEhdUW-WOpۙ^(z)~B.XpɰA%KKIbBZ3lV2ұoʙ*t)FuJ;挥7dp)7@}Z}*,x4/n]~_x 92$ h=7PVOaqzlj4oȐG]ş[Pғ]1꼄C3LXRw4Ȗ=zwmcjaԳ~_,4J=Bˌ)?>v'1n@q a}NIQhv0(!؏eɅ,U%i smVU) eEH{U܌NdQGXY ,K-Ҫ"w.D@șk n-ք N^C. ^"YwJ7g,> #FK/̾ [Y Fp*2]0 CB'5ya\ׁ4wn$9MdctUH jB̳zZpnUۅ,pޡlAG"`s^Rٙr "ޛ>8dr~ϒ8U|F1aER]ٝzoہX$0Q%2Yv@g&)t9~.܀ՐlN0( '5D][CIEGUDp|Aj*6L'?b7hR(u/OTC>X_sg+uO'\%*b+7`kI\s9x}iQt]aK@b+^b5A'#}rS%Ykߚ w T+u<)+gr6#Z0^=|>UkpRk75O>n ! Җf ;A &cqj⼻UrwQ1A;3ȷ++gAVPPJb2ҙVg PǓ)"%=?9V۠#ad zxz-Ju71eq4SIA ^2ɇK gOk1x_WGnwRh,:?/]uǻ.1-OQq&K;1Ljf+lx۷Q-iyu96LQRsJ{L++ m PZ~r"B."p.ߕoz?ځ\oL\HY#ty cY8#:]1qD '=;dv&T\ǐ4u :+# CV*~g\1V8\],Ǔ}ufW֚m! `\^p(Ӌ;~ɑt8_V9R.H}diuݜ 'C` CT+9.L*D4%Տ`Z듌qYWO(?uJ7ǎ+YձV ] /hK?i*`75vťWA"zcҜ1|ؼ^NACfeN7GbZϳϫ!J[NyD TRtSBWrnʁ} >XWGU[n -(Fk_I Jl|所 3&t<|} o/Woq jIvcpq461tXz$w@I:] w^.F5G#DkVueɁ #jM\X~Lo&سCl\b,auw3e~S%^VuOł)?zl0c>l٠.yhV;b[QZnn7M̌՘j{=p[XcR٤_- !]i<16ttdZPzeY)9f(Du}!`òAf %"qhQ!/ߎ[Z^GèJg V%n[pPѺf9L^^'lxp {$+ʲPv#R&0}AIBѲm}k0,^yySX6`fЩ盏Z^VD:*Yf[,>\_tyGK9d"+2#?e{vU ;+-CA?n=l@fŐ5GEDZFhW$qwC Xbux6k -W}M_EЃLrr4 LL# ߀>A[rJ!jCfm˕yStjoK1cTVXa)xa:SˠI3KC nI3gEiڣ ҝ-I>oE( ~0'@YhU!P `U/L~M$52~n֤kkQ<#՘@%<*K[Y@R\:5:*3'±̡w*sG#y]\ߧ6 !=V=FLZNhJ{q*@/TL2ԄkFk.?fDJ$EZ|,AIx*3Lu1mTӠwͧR]XF d\jO\%=2B%mB^_SV}K8ۯlXCVGks 7 ^P-*_QS .3ym"fj xLs<l `yUTu 2_p~-r?8ͥ~!H+{e#qJkF5MiImy #]1IX|ƒ`uЂ[[^9̥r;^]7T:u Zx.md²cA6GLPly ]w:o}?t/OKa͓[yEutA< $TD˖EqAIA]~Ҷ^zhWPf99;j8 _SD:A:lVkVo.R淚bQta$>ǮD/m[DfəOѕ$sPNsG$]y䇰slxtyʭ`{Qf(ƟчdNZEz AS65'<+#cqzlQV'[ppm{j?H+waeu:sPȮQOZYq)gDQdn p10sN` |7r4T sBŴH1_gft`45T*>l>Z<dbyku4I$Lc 70ʣ-);!;*}4?t͙?+B6ѺT`ݺ?#SY.a}ZJk.snOIp&W0GIn^A5Sjm,8lΚ]%O7WbzAѪ!>Cp .lvBOύ<#pܭ,SA滋m ͕G'R- OJ uă+ tQAHa怪 mmA^}~fIJ IeR8: xڝ^!ǂa}W*s\ 4Q3;O7L8R<:(Bs j泘M =pW<aȋ}o <,/hV:CxK֫V'(tq)]( n@.ŏp3vZtY鲙.Mt] 6+}7v={N!#I[il/i#-@8H)<$e68slʭ2@\ `\d"IusrɓdΏy_뷔-ѧ>RM LX <@O*iQr t!iHOdR}є&&q6ů԰5G۪f/:a`Z:7~74Κ–-q eZz6١_#8ThN)qrM c~eZOR3)fH9Vp⬻e"GSoJB5ph}ݔucD e-=`>w.ՖG䑜 L;YR ),Y\ l`9$"EhK4ɍV &ڧ{񃩇٦4!UÛgqU@QaHG i'ګ'I|=QU )y̓f?˟!>g'*QTp0`t•X8Lv?fQD{q  5q*RX!v(*kGպ(d:!lsLkґ0G-5Bm?X-ў˵m2dxa/Nb^NIwX^ЯUdj`Y527>f%}{7w])ώzӜl0ЄufDٽotrGn^ax|k\vO=eS ^COT4vF6޾>4n,z} ϱHC+U,"ߩMx}DqC+Kƴ**H~1sHqMe_NY:)FZ?_/U<$jFE Rƞp+Z[{_]G4!$+$xrS дT .C>j1zJ K/!j[[\O&on;.o9 "`(bU+:LߥF;8|ޘX̤XPg/tbMrsV=y"<L}#D hEeb}Q=Vv?):&taG+܄~p@/YЬ@1T?^%5Ů%*|fu0Y Ʀ>2s+uup>†ߘ0S($\\5T]+8~P[q! J̳n=ث#rqߌnW"zOj.t a*lκ ɋ$X.~2Rwk# 䈵Cc>4`\^#|@_ײƾy q3ą3$V74:Ǖgun4gFC.DU-< 1M98?b HqgHt3+k Z8'Wph:Ϩ3W[+,Cn*ZH[$mv`FB q|#?ӹ/$SmʱҦBepB]UP Uɸµd)KvQMSIK T(?\YØӠV ?pbu,&BA7$|;E{ա7-nA uV(f|u! K=+j1 _0G wfi(8|RnY$} QT9BOԗR?v q!}S(k.[~WHP ;2|A$iەl:cCOqJ&,M9&1mp5Dؾ- b6Ȋ] %rb׽}ܪAIeͱk8q'3{b2\'G;>.Yr?k/|% Y $_nI9wOI? `j)7Xa倮Q2/)Vs15<lgGu ׭wI1L UMKz|0+cP~AX@T^o=xv _?4}u'a .uWN!@>勧a<Gœdg^5m6a/^P.H^2nY!%t,%^4wȵw҉ѥ< iժV~T@bh,nƔ}ֺ:ןxdXf<׿\~+CD#yfi;U"a[dw&~[o ?&[aU/d_)' ޯ>?P淅ҩ똛+O;7Fo4PW0Ej0ېe%K ~t'Y4^Hm'M Vs]}tMCsx*7:فc=JP,yR9 woHyt}ؾ'u=fR^ ~fqSEƣu$kD6__Xc)v\Q-x.q<)pQ.x$"1M3ʹ$lBjqCY|ĵ;w c_oL$%$Ġy;?jvvu䤈xט͜xc1eV@TR! ܿHp̽|g5x8NW=HE"*㐃VX529v[Gh `S / ƗmPr?YQ6Uh(8be#ø՘@ bkj߆m"8O$ђ+&:3n"Ž΃3 TLr('6BQۅ<2c70(,3)*P[Up]$ӛe:gƷ~/ܘrp5 8%U=oemYJUuE`yr gy am'akuT@&Wz-PR^F3oIcMHzx6JͲI@s,EoIz= yeCȥXn^n.Bxxn\*>#&qT6@YY HhqxKR$Op5ZM%2awV"D[34@b1.lII(n1-IL,qX^%q+ѰX|dzlppU ^w#K\AK#$3 tz %LM=\mӽ?<1VN,|TiP̨NƇB2Qn0Y4Ef`DJG"xd1DZ۔<SVI Y>r+߅"iQkt]KUqTpLޅA \|DuV%nE7;ͤҕYQCzۃ󙯑I.w{_gck4{' Xyy.'z LeMlV:WxX.&A\[XW3Uf]zc&v؜7!E<t-˻EE:} ifთ?IH6y#{aY*2$ɺh/q-74.%m~K16*}P0%5,I-8g)>4'1@C_z0/ As ׄQutKطxWQ_G79#Xu"'Y!4_Cs5Ls¯'$ ^_+38w6lI*N9̑ݱ5\!1iJS&#p֮%`.(5 M!)2.,E:y&Ag 6m"W  C>9wj,~nQpZv7o.&T+TWN:7$IE_;N¥X^Sq7Ɉ (qP0}]_{(^AxMD%#=T#btKIefnZ?z_t~}}@t]ҭQ<V-Tڜԁ2 }8m7(S)7e 4%99&9-OQEVͭM9yfk2_#(sP-F>TO7Y۟ gdNiƺ˔,7+] cmݝSë4uOX 憆HoerALLIq CNM BAނbl68*AcЕJYar`eGspSTVaGEGL}n'&)0b|gc}튃(SA%!CfUrjuTngOvvb9.ѓ ꗱ*9JU):=xY\p#DH%na_tFw Zif}SPGJF5yv+a*ASM-v?jnW4]:h_Cc  $]3WUL{>er@tcT lĆl3ze'K+bveY^1zP&9Mej"rLazW WMYs{p4LXҽ%Ze*r6X*]v #w|%xgB2̕'ϟG~}"z標9VM+|*޳7}1[#9 iX*;^_Ӆi8 mELjZk"2pqSI[i;!Vpf]uDipNfQ\kC";RGNXK 9oy@ {Hh)(݅>S鐩HZ^ 6~h\?'a!,u0-]ҚgZ]O{MXdnбEIbt83WBb6,+yI2VݽS4=#7-& smb;' B =`@!jZn9>Ygy؏S^ggN<;C(,5]O - \NRbzT>~e_cmcӶv@wqV rVROs|]6*5s*̟dZK$/, Z)/2תUp'JTZ<oc8"aXI=PPWg+[FiD=aWH'}t8sP.7ytw3,8lcIC /'k6 ܕVH:Ts ]HHՈ'9$YWyPOzkV$gNO2g39X+o>]X<@B¾;I]%R<-.f~^=J?a5XI.3XCʴ"@Vcr7v8.à= \ [ϚSނeJ;wmps:7A%aJ`«;${ѡTY~6ԭmlCsg-w< 'iSж|Ppa]^JWzQNBFnUـRu~D{'_/IQChO ʻ#Kz=zt|vXUOj!9/`<KUυQWưж:J{1cP ^o?*\E;/2c.mՙ6GvԜlo|*v0 ?|e2sQ,K:QVa 9c=2-be.|nu.\;E~gۢ꛳'ԺaS<.·~:c-c6EL hZH=v.v!Oǭ}Tk5ם̔Դvft|\EOP@4Y"w+U[TUG 1Mo9AKg})\ϡׇ40#HjaETSz(1_̲׌Ks5@&Z YT,Z)]kՔ&u ZC=-#x| l0b4%lN;*s寣}, ?,BiKs^].mz_aHW9%Fy+5*u=h 5EW[<$|>&c7[5S EWyΰv,[Y"8hc_A^JM&dU"1tc/7WFY:B={AE[@doطj D||@tmX3R(u 0~a>$3xsvz|>{ntpMNv)(q6e`{" 6dXƬZ(&r0kK.2A-q49U5qj?Hv4UDE`20GoXdiLH ѻ?} b~^![)\m A!CMQc[=ü.F,gHM>QI y-}ůn7} S)!21*qpnļ NC׽ABzB]Gq.D~k) *C HvĕpC  &. =d 㭾ˁ4xfNS^@ ;bX/rnqixb?`qr#[߮f%ƣ9Y2tbYMRWDF^1c}o)c.:gBMmZՃ-eHQ {ۺ8U~E,ӯp/@Jqm/Ld~l0Ҵύ 2h;Z0֛g<D$|F\'JĕΫD6rt(olZ E9l-ċ9xcUu6}0.5_U 1 ZвF'w~5ʠ<^4Q\her myX›$,˖Fɕꎴ L L.b7CB xuC Xgݭ1j9d$tb#D畇P4 Y=@NTADiVOܪ[..>~<׀.uR2c{#}}j~W$IWNM zڕ_&y # 3Z8xTO%ZE^J,ZPU?r 痛_c &5kfHc)Tk38_~Y뻺0A"COtJP.YRDZ"Gqsw3f-Wz!ّ52Wbl Dc~~[硙ZW\O(M902{w׼CC:5}n3 {[ۊfos|QQH2 6S8Nav3e rDDx~8荘CZ@83FmDjta!܏H9`rx$ءZewXl<7(ģoT&7=A[X74>K =EQV`C&(~ݗbhȧTjW:zM~~s3~ 3Ʊ pl\\X, ;/T09#`uHDO[!l6D:/).8/!W3UP.j6vnW%ʅvXZؓGjmC:\B̤~&\mTߦ+dD[p6S'f6R~1 'N˗P0;b.- SymfcAtoms: """Return unitcell of NaCl.""" lattice = [ [5.690301476175671, 0.000000000000000, 0.000000000000000], [0.000000000000000, 5.690301476175671, 0.000000000000000], [0.000000000000000, 0.000000000000000, 5.690301476175671], ] points = [ [0.000000000000000, 0.000000000000000, 0.000000000000000], [0.000000000000000, 0.500000000000000, 0.500000000000000], [0.500000000000000, 0.000000000000000, 0.500000000000000], [0.500000000000000, 0.500000000000000, 0.000000000000000], [0.500000000000000, 0.500000000000000, 0.500000000000000], [0.500000000000000, 0.000000000000000, 0.000000000000000], [0.000000000000000, 0.500000000000000, 0.000000000000000], [0.000000000000000, 0.000000000000000, 0.500000000000000], ] numbers = [11, 11, 11, 11, 17, 17, 17, 17] cell = SymfcAtoms(cell=lattice, scaled_positions=points, numbers=numbers) return cell @pytest.fixture(scope="session") def cell_gan_111() -> SymfcAtoms: """Return unitcell of GaN.""" lattice = [ [3.180765520000000, 0.000000000000000, 0.000000000000000], [-1.590382760000000, 2.754623740000000, 0.000000000000000], [0.000000000000000, 0.000000000000000, 5.182605840000000], ] points = [ [0.333333329999992, 0.666666669999983, 0.124191919999999], [0.666666669999991, 0.333333329999998, 0.624191919999998], [0.333333329999992, 0.666666669999983, 0.500808080000002], [0.666666669999991, 0.333333329999998, 0.000808080000000], ] numbers = [7, 7, 31, 31] cell = SymfcAtoms(cell=lattice, scaled_positions=points, numbers=numbers) return cell @pytest.fixture(scope="session") def cell_wurtzite_332() -> SymfcAtoms: """Return 3x3x2 supercell of wurtzite.""" lattice = [ [11.35855848088148, 0.0, 0.0], [-5.679279240440739, 9.836800194814545, 0.0], [0.0, 0.0, 12.425356538818003], ] positions = [ [0.111111111111111, 0.222222222222222, 0.001063232855807], [0.444444444444444, 0.222222222222222, 0.001063232855807], [0.777777777777778, 0.222222222222222, 0.001063232855807], [0.111111111111111, 0.555555555555556, 0.001063232855807], [0.444444444444444, 0.555555555555556, 0.001063232855807], [0.777777777777778, 0.555555555555556, 0.001063232855807], [0.111111111111111, 0.888888888888889, 0.001063232855807], [0.444444444444444, 0.888888888888889, 0.001063232855807], [0.777777777777778, 0.888888888888889, 0.001063232855807], [0.111111111111111, 0.222222222222222, 0.501063232855807], [0.444444444444444, 0.222222222222222, 0.501063232855807], [0.777777777777778, 0.222222222222222, 0.501063232855807], [0.111111111111111, 0.555555555555556, 0.501063232855807], [0.444444444444444, 0.555555555555556, 0.501063232855807], [0.777777777777778, 0.555555555555556, 0.501063232855807], [0.111111111111111, 0.888888888888889, 0.501063232855807], [0.444444444444444, 0.888888888888889, 0.501063232855807], [0.777777777777778, 0.888888888888889, 0.501063232855807], [0.222222222222222, 0.111111111111111, 0.251063232855807], [0.555555555555556, 0.111111111111111, 0.251063232855807], [0.888888888888889, 0.111111111111111, 0.251063232855807], [0.222222222222222, 0.444444444444444, 0.251063232855807], [0.555555555555556, 0.444444444444444, 0.251063232855807], [0.888888888888889, 0.444444444444444, 0.251063232855807], [0.222222222222222, 0.777777777777778, 0.251063232855807], [0.555555555555556, 0.777777777777778, 0.251063232855807], [0.888888888888889, 0.777777777777778, 0.251063232855807], [0.222222222222222, 0.111111111111111, 0.751063232855807], [0.555555555555556, 0.111111111111111, 0.751063232855807], [0.888888888888889, 0.111111111111111, 0.751063232855807], [0.222222222222222, 0.444444444444444, 0.751063232855807], [0.555555555555556, 0.444444444444444, 0.751063232855807], [0.888888888888889, 0.444444444444444, 0.751063232855807], [0.222222222222222, 0.777777777777778, 0.751063232855807], [0.555555555555556, 0.777777777777778, 0.751063232855807], [0.888888888888889, 0.777777777777778, 0.751063232855807], [0.111111111111111, 0.222222222222222, 0.188158257144195], [0.444444444444444, 0.222222222222222, 0.188158257144195], [0.777777777777778, 0.222222222222222, 0.188158257144195], [0.111111111111111, 0.555555555555556, 0.188158257144195], [0.444444444444444, 0.555555555555556, 0.188158257144195], [0.777777777777778, 0.555555555555556, 0.188158257144195], [0.111111111111111, 0.888888888888889, 0.188158257144195], [0.444444444444444, 0.888888888888889, 0.188158257144195], [0.777777777777778, 0.888888888888889, 0.188158257144195], [0.111111111111111, 0.222222222222222, 0.688158257144195], [0.444444444444444, 0.222222222222222, 0.688158257144195], [0.777777777777778, 0.222222222222222, 0.688158257144195], [0.111111111111111, 0.555555555555556, 0.688158257144195], [0.444444444444444, 0.555555555555556, 0.688158257144195], [0.777777777777778, 0.555555555555556, 0.688158257144195], [0.111111111111111, 0.888888888888889, 0.688158257144195], [0.444444444444444, 0.888888888888889, 0.688158257144195], [0.777777777777778, 0.888888888888889, 0.688158257144195], [0.222222222222222, 0.111111111111111, 0.438158257144194], [0.555555555555556, 0.111111111111111, 0.438158257144194], [0.888888888888889, 0.111111111111111, 0.438158257144194], [0.222222222222222, 0.444444444444444, 0.438158257144194], [0.555555555555556, 0.444444444444444, 0.438158257144194], [0.888888888888889, 0.444444444444444, 0.438158257144194], [0.222222222222222, 0.777777777777778, 0.438158257144194], [0.555555555555556, 0.777777777777778, 0.438158257144194], [0.888888888888889, 0.777777777777778, 0.438158257144194], [0.222222222222222, 0.111111111111111, 0.938158257144194], [0.555555555555556, 0.111111111111111, 0.938158257144194], [0.888888888888889, 0.111111111111111, 0.938158257144194], [0.222222222222222, 0.444444444444444, 0.938158257144194], [0.555555555555556, 0.444444444444444, 0.938158257144194], [0.888888888888889, 0.444444444444444, 0.938158257144194], [0.222222222222222, 0.777777777777778, 0.938158257144194], [0.555555555555556, 0.777777777777778, 0.938158257144194], [0.888888888888889, 0.777777777777778, 0.938158257144194], ] numbers = [1 for i in range(36)] + [2 for i in range(36)] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) return supercell @pytest.fixture(scope="session") def cell_nacl_222() -> SymfcAtoms: """Return 2x2x2 supercell of NaCl.""" lattice = [ [11.281120000000000, 0.000000000000000, 0.000000000000000], [0.000000000000000, 11.281120000000000, 0.000000000000000], [0.000000000000000, 0.000000000000000, 11.281120000000000], ] points = [ [0.000000000000000, 0.000000000000000, 0.000000000000000], [0.500000000000000, 0.000000000000000, 0.000000000000000], [0.000000000000000, 0.500000000000000, 0.000000000000000], [0.500000000000000, 0.500000000000000, 0.000000000000000], [0.000000000000000, 0.000000000000000, 0.500000000000000], [0.500000000000000, 0.000000000000000, 0.500000000000000], [0.000000000000000, 0.500000000000000, 0.500000000000000], [0.500000000000000, 0.500000000000000, 0.500000000000000], [0.000000000000000, 0.250000000000000, 0.250000000000000], [0.500000000000000, 0.250000000000000, 0.250000000000000], [0.000000000000000, 0.750000000000000, 0.250000000000000], [0.500000000000000, 0.750000000000000, 0.250000000000000], [0.000000000000000, 0.250000000000000, 0.750000000000000], [0.500000000000000, 0.250000000000000, 0.750000000000000], [0.000000000000000, 0.750000000000000, 0.750000000000000], [0.500000000000000, 0.750000000000000, 0.750000000000000], [0.250000000000000, 0.000000000000000, 0.250000000000000], [0.750000000000000, 0.000000000000000, 0.250000000000000], [0.250000000000000, 0.500000000000000, 0.250000000000000], [0.750000000000000, 0.500000000000000, 0.250000000000000], [0.250000000000000, 0.000000000000000, 0.750000000000000], [0.750000000000000, 0.000000000000000, 0.750000000000000], [0.250000000000000, 0.500000000000000, 0.750000000000000], [0.750000000000000, 0.500000000000000, 0.750000000000000], [0.250000000000000, 0.250000000000000, 0.000000000000000], [0.750000000000000, 0.250000000000000, 0.000000000000000], [0.250000000000000, 0.750000000000000, 0.000000000000000], [0.750000000000000, 0.750000000000000, 0.000000000000000], [0.250000000000000, 0.250000000000000, 0.500000000000000], [0.750000000000000, 0.250000000000000, 0.500000000000000], [0.250000000000000, 0.750000000000000, 0.500000000000000], [0.750000000000000, 0.750000000000000, 0.500000000000000], [0.250000000000000, 0.250000000000000, 0.250000000000000], [0.750000000000000, 0.250000000000000, 0.250000000000000], [0.250000000000000, 0.750000000000000, 0.250000000000000], [0.750000000000000, 0.750000000000000, 0.250000000000000], [0.250000000000000, 0.250000000000000, 0.750000000000000], [0.750000000000000, 0.250000000000000, 0.750000000000000], [0.250000000000000, 0.750000000000000, 0.750000000000000], [0.750000000000000, 0.750000000000000, 0.750000000000000], [0.250000000000000, 0.000000000000000, 0.000000000000000], [0.750000000000000, 0.000000000000000, 0.000000000000000], [0.250000000000000, 0.500000000000000, 0.000000000000000], [0.750000000000000, 0.500000000000000, 0.000000000000000], [0.250000000000000, 0.000000000000000, 0.500000000000000], [0.750000000000000, 0.000000000000000, 0.500000000000000], [0.250000000000000, 0.500000000000000, 0.500000000000000], [0.750000000000000, 0.500000000000000, 0.500000000000000], [0.000000000000000, 0.250000000000000, 0.000000000000000], [0.500000000000000, 0.250000000000000, 0.000000000000000], [0.000000000000000, 0.750000000000000, 0.000000000000000], [0.500000000000000, 0.750000000000000, 0.000000000000000], [0.000000000000000, 0.250000000000000, 0.500000000000000], [0.500000000000000, 0.250000000000000, 0.500000000000000], [0.000000000000000, 0.750000000000000, 0.500000000000000], [0.500000000000000, 0.750000000000000, 0.500000000000000], [0.000000000000000, 0.000000000000000, 0.250000000000000], [0.500000000000000, 0.000000000000000, 0.250000000000000], [0.000000000000000, 0.500000000000000, 0.250000000000000], [0.500000000000000, 0.500000000000000, 0.250000000000000], [0.000000000000000, 0.000000000000000, 0.750000000000000], [0.500000000000000, 0.000000000000000, 0.750000000000000], [0.000000000000000, 0.500000000000000, 0.750000000000000], [0.500000000000000, 0.500000000000000, 0.750000000000000], ] numbers = [ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, ] cell = SymfcAtoms(cell=lattice, scaled_positions=points, numbers=numbers) return cell @pytest.fixture(scope="session") def ph_nacl_222(cell_nacl_222) -> tuple[SymfcAtoms, np.ndarray, np.ndarray]: """Return NaCl-222 data.""" N = 20 dfset = np.loadtxt(cwd / "dfset_NaCl_222_rd.xz") d = dfset[:, :3].reshape(N, -1, 3) f = dfset[:, 3:].reshape(N, -1, 3) return cell_nacl_222, d, f @pytest.fixture(scope="session") def ph_sno2_223() -> tuple[SymfcAtoms, np.ndarray, np.ndarray]: """Return SnO2-223 data.""" lattice = [ [9.545162500000000, 0.000000000000000, 0.000000000000000], [0.000000000000000, 9.545162500000000, 0.000000000000000], [0.000000000000000, 0.000000000000000, 9.649138170000001], ] points = [ [0.096865350000001, 0.403134649999997, 0.166666666666667], [0.596865350000001, 0.403134649999997, 0.166666666666667], [0.096865350000001, 0.903134649999997, 0.166666666666667], [0.596865350000001, 0.903134649999997, 0.166666666666667], [0.096865350000001, 0.403134649999997, 0.500000000000000], [0.596865350000001, 0.403134649999997, 0.500000000000000], [0.096865350000001, 0.903134649999997, 0.500000000000000], [0.596865350000001, 0.903134649999997, 0.500000000000000], [0.096865350000001, 0.403134649999997, 0.833333333333333], [0.596865350000001, 0.403134649999997, 0.833333333333333], [0.096865350000001, 0.903134649999997, 0.833333333333333], [0.596865350000001, 0.903134649999997, 0.833333333333333], [0.403134649999997, 0.096865350000001, 0.166666666666667], [0.903134649999997, 0.096865350000001, 0.166666666666667], [0.403134649999997, 0.596865350000001, 0.166666666666667], [0.903134649999997, 0.596865350000001, 0.166666666666667], [0.403134649999997, 0.096865350000001, 0.500000000000000], [0.903134649999997, 0.096865350000001, 0.500000000000000], [0.403134649999997, 0.596865350000001, 0.500000000000000], [0.903134649999997, 0.596865350000001, 0.500000000000000], [0.403134649999997, 0.096865350000001, 0.833333333333333], [0.903134649999997, 0.096865350000001, 0.833333333333333], [0.403134649999997, 0.596865350000001, 0.833333333333333], [0.903134649999997, 0.596865350000001, 0.833333333333333], [0.153134649999998, 0.153134649999998, 0.000000000000000], [0.653134649999997, 0.153134649999998, 0.000000000000000], [0.153134649999998, 0.653134649999997, 0.000000000000000], [0.653134649999997, 0.653134649999997, 0.000000000000000], [0.153134649999998, 0.153134649999998, 0.333333333333333], [0.653134649999997, 0.153134649999998, 0.333333333333333], [0.153134649999998, 0.653134649999997, 0.333333333333333], [0.653134649999997, 0.653134649999997, 0.333333333333333], [0.153134649999998, 0.153134649999998, 0.666666666666667], [0.653134649999997, 0.153134649999998, 0.666666666666667], [0.153134649999998, 0.653134649999997, 0.666666666666667], [0.653134649999997, 0.653134649999997, 0.666666666666667], [0.346865350000003, 0.346865350000003, 0.000000000000000], [0.846865350000003, 0.346865350000003, 0.000000000000000], [0.346865350000003, 0.846865350000003, 0.000000000000000], [0.846865350000003, 0.846865350000003, 0.000000000000000], [0.346865350000003, 0.346865350000003, 0.333333333333333], [0.846865350000003, 0.346865350000003, 0.333333333333333], [0.346865350000003, 0.846865350000003, 0.333333333333333], [0.846865350000003, 0.846865350000003, 0.333333333333333], [0.346865350000003, 0.346865350000003, 0.666666666666667], [0.846865350000003, 0.346865350000003, 0.666666666666667], [0.346865350000003, 0.846865350000003, 0.666666666666667], [0.846865350000003, 0.846865350000003, 0.666666666666667], [0.250000000000000, 0.250000000000000, 0.166666666666667], [0.750000000000000, 0.250000000000000, 0.166666666666667], [0.250000000000000, 0.750000000000000, 0.166666666666667], [0.750000000000000, 0.750000000000000, 0.166666666666667], [0.250000000000000, 0.250000000000000, 0.500000000000000], [0.750000000000000, 0.250000000000000, 0.500000000000000], [0.250000000000000, 0.750000000000000, 0.500000000000000], [0.750000000000000, 0.750000000000000, 0.500000000000000], [0.250000000000000, 0.250000000000000, 0.833333333333333], [0.750000000000000, 0.250000000000000, 0.833333333333333], [0.250000000000000, 0.750000000000000, 0.833333333333333], [0.750000000000000, 0.750000000000000, 0.833333333333333], [0.000000000000000, 0.000000000000000, 0.000000000000000], [0.500000000000000, 0.000000000000000, 0.000000000000000], [0.000000000000000, 0.500000000000000, 0.000000000000000], [0.500000000000000, 0.500000000000000, 0.000000000000000], [0.000000000000000, 0.000000000000000, 0.333333333333333], [0.500000000000000, 0.000000000000000, 0.333333333333333], [0.000000000000000, 0.500000000000000, 0.333333333333333], [0.500000000000000, 0.500000000000000, 0.333333333333333], [0.000000000000000, 0.000000000000000, 0.666666666666667], [0.500000000000000, 0.000000000000000, 0.666666666666667], [0.000000000000000, 0.500000000000000, 0.666666666666667], [0.500000000000000, 0.500000000000000, 0.666666666666667], ] numbers = [ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, ] cell = SymfcAtoms(cell=lattice, scaled_positions=points, numbers=numbers) N = 40 dfset = np.loadtxt(cwd / "dfset_SnO2_223_rd.xz") d = dfset[:, :3].reshape(N, -1, 3) f = dfset[:, 3:].reshape(N, -1, 3) return cell, d, f @pytest.fixture(scope="session") def ph_sio2_221() -> tuple[SymfcAtoms, np.ndarray, np.ndarray]: """Return SiO2-221 data.""" lattice = [ [9.919749440000000, 0.000000000000000, 0.000000000000000], [-4.959874720000000, 8.590755020000000, 0.000000000000000], [0.000000000000000, 0.000000000000000, 5.453559970000000], ] points = [ [0.293442579999996, 0.367112664999997, 0.213280300000002], [0.793442579999996, 0.367112664999997, 0.213280300000002], [0.293442579999996, 0.867112664999997, 0.213280300000002], [0.793442579999996, 0.867112664999997, 0.213280300000002], [0.073670084999997, 0.206557419999994, 0.879946960000002], [0.573670084999997, 0.206557419999994, 0.879946960000002], [0.073670084999997, 0.706557419999994, 0.879946960000002], [0.573670084999997, 0.706557419999994, 0.879946960000002], [0.132887334999998, 0.426329914999998, 0.546613630000002], [0.632887334999998, 0.426329914999998, 0.546613630000002], [0.132887334999998, 0.926329914999998, 0.546613630000002], [0.632887334999998, 0.926329914999998, 0.546613630000002], [0.367112665000005, 0.293442580000006, 0.786719699999998], [0.867112665000005, 0.293442580000006, 0.786719699999998], [0.367112665000005, 0.793442580000006, 0.786719699999998], [0.867112665000005, 0.793442580000006, 0.786719699999998], [0.426329914999999, 0.132887335000003, 0.453386369999998], [0.926329914999999, 0.132887335000003, 0.453386369999998], [0.426329914999999, 0.632887335000003, 0.453386369999998], [0.926329914999999, 0.632887335000003, 0.453386369999998], [0.206557420000001, 0.073670085000000, 0.120053040000000], [0.706557420000001, 0.073670085000000, 0.120053040000000], [0.206557420000001, 0.573670085000000, 0.120053040000000], [0.706557420000001, 0.573670085000000, 0.120053040000000], [0.235454159999997, 0.235454160000002, 0.000000000000000], [0.735454159999998, 0.235454160000002, 0.000000000000000], [0.235454159999997, 0.735454160000002, 0.000000000000000], [0.735454159999998, 0.735454160000002, 0.000000000000000], [0.264545839999997, 0.000000000000000, 0.333333330000000], [0.764545839999997, 0.000000000000000, 0.333333330000000], [0.264545839999997, 0.500000000000000, 0.333333330000000], [0.764545839999997, 0.500000000000000, 0.333333330000000], [0.999999999999995, 0.264545839999998, 0.666666670000000], [0.499999999999996, 0.264545839999998, 0.666666670000000], [0.999999999999995, 0.764545839999998, 0.666666670000000], [0.499999999999996, 0.764545839999998, 0.666666670000000], ] numbers = [ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, ] cell = SymfcAtoms(cell=lattice, scaled_positions=points, numbers=numbers) N = 40 dfset = np.loadtxt(cwd / "dfset_SiO2_221_rd.xz") d = dfset[:, :3].reshape(N, -1, 3) f = dfset[:, 3:].reshape(N, -1, 3) return cell, d, f @pytest.fixture(scope="session") def ph_gan_222() -> tuple[SymfcAtoms, np.ndarray, np.ndarray]: """Return GaN-222 data.""" lattice = [ [6.361531040000000, 0.000000000000000, 0.000000000000000], [-3.180765520000000, 5.509247480000000, 0.000000000000000], [0.000000000000000, 0.000000000000000, 10.365211680000000], ] points = [ [0.166666664999996, 0.333333334999992, 0.062095959999999], [0.666666664999996, 0.333333334999992, 0.062095959999999], [0.166666664999996, 0.833333334999992, 0.062095959999999], [0.666666664999996, 0.833333334999992, 0.062095959999999], [0.166666664999996, 0.333333334999992, 0.562095960000000], [0.666666664999996, 0.333333334999992, 0.562095960000000], [0.166666664999996, 0.833333334999992, 0.562095960000000], [0.666666664999996, 0.833333334999992, 0.562095960000000], [0.333333334999996, 0.166666664999999, 0.312095959999999], [0.833333334999996, 0.166666664999999, 0.312095959999999], [0.333333334999996, 0.666666664999999, 0.312095959999999], [0.833333334999996, 0.666666664999999, 0.312095959999999], [0.333333334999996, 0.166666664999999, 0.812095959999999], [0.833333334999996, 0.166666664999999, 0.812095959999999], [0.333333334999996, 0.666666664999999, 0.812095959999999], [0.833333334999996, 0.666666664999999, 0.812095959999999], [0.166666664999996, 0.333333334999992, 0.250404040000001], [0.666666664999996, 0.333333334999992, 0.250404040000001], [0.166666664999996, 0.833333334999992, 0.250404040000001], [0.666666664999996, 0.833333334999992, 0.250404040000001], [0.166666664999996, 0.333333334999992, 0.750404040000001], [0.666666664999996, 0.333333334999992, 0.750404040000001], [0.166666664999996, 0.833333334999992, 0.750404040000001], [0.666666664999996, 0.833333334999992, 0.750404040000001], [0.333333334999996, 0.166666664999999, 0.000404040000000], [0.833333334999996, 0.166666664999999, 0.000404040000000], [0.333333334999996, 0.666666664999999, 0.000404040000000], [0.833333334999996, 0.666666664999999, 0.000404040000000], [0.333333334999996, 0.166666664999999, 0.500404040000000], [0.833333334999996, 0.166666664999999, 0.500404040000000], [0.333333334999996, 0.666666664999999, 0.500404040000000], [0.833333334999996, 0.666666664999999, 0.500404040000000], ] numbers = [ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, ] cell = SymfcAtoms(cell=lattice, scaled_positions=points, numbers=numbers) N = 40 dfset = np.loadtxt(cwd / "dfset_GaN_222_rd.xz") d = dfset[:, :3].reshape(N, -1, 3) f = dfset[:, 3:].reshape(N, -1, 3) return cell, d, f @pytest.fixture(scope="session") def ph_gan_442() -> tuple[SymfcAtoms, np.ndarray, np.ndarray]: """Return GaN-442 data.""" lattice = [ [12.723062080000000, 0.000000000000000, 0.000000000000000], [-6.361531040000000, 11.018494960000000, 0.000000000000000], [0.000000000000000, 0.000000000000000, 10.365211680000000], ] points = [ [0.083333332499998, 0.166666667499996, 0.062095959999999], [0.333333332499998, 0.166666667499996, 0.062095959999999], [0.583333332499998, 0.166666667499996, 0.062095959999999], [0.833333332499998, 0.166666667499996, 0.062095959999999], [0.083333332499998, 0.416666667499996, 0.062095959999999], [0.333333332499998, 0.416666667499996, 0.062095959999999], [0.583333332499998, 0.416666667499996, 0.062095959999999], [0.833333332499998, 0.416666667499996, 0.062095959999999], [0.083333332499998, 0.666666667499996, 0.062095959999999], [0.333333332499998, 0.666666667499996, 0.062095959999999], [0.583333332499998, 0.666666667499996, 0.062095959999999], [0.833333332499998, 0.666666667499996, 0.062095959999999], [0.083333332499998, 0.916666667499996, 0.062095959999999], [0.333333332499998, 0.916666667499996, 0.062095959999999], [0.583333332499998, 0.916666667499996, 0.062095959999999], [0.833333332499998, 0.916666667499996, 0.062095959999999], [0.083333332499998, 0.166666667499996, 0.562095960000000], [0.333333332499998, 0.166666667499996, 0.562095960000000], [0.583333332499998, 0.166666667499996, 0.562095960000000], [0.833333332499998, 0.166666667499996, 0.562095960000000], [0.083333332499998, 0.416666667499996, 0.562095960000000], [0.333333332499998, 0.416666667499996, 0.562095960000000], [0.583333332499998, 0.416666667499996, 0.562095960000000], [0.833333332499998, 0.416666667499996, 0.562095960000000], [0.083333332499998, 0.666666667499996, 0.562095960000000], [0.333333332499998, 0.666666667499996, 0.562095960000000], [0.583333332499998, 0.666666667499996, 0.562095960000000], [0.833333332499998, 0.666666667499996, 0.562095960000000], [0.083333332499998, 0.916666667499996, 0.562095960000000], [0.333333332499998, 0.916666667499996, 0.562095960000000], [0.583333332499998, 0.916666667499996, 0.562095960000000], [0.833333332499998, 0.916666667499996, 0.562095960000000], [0.166666667499998, 0.083333332499999, 0.312095959999999], [0.416666667499998, 0.083333332499999, 0.312095959999999], [0.666666667499998, 0.083333332499999, 0.312095959999999], [0.916666667499998, 0.083333332499999, 0.312095959999999], [0.166666667499998, 0.333333332500000, 0.312095959999999], [0.416666667499998, 0.333333332500000, 0.312095959999999], [0.666666667499998, 0.333333332500000, 0.312095959999999], [0.916666667499998, 0.333333332500000, 0.312095959999999], [0.166666667499998, 0.583333332500000, 0.312095959999999], [0.416666667499998, 0.583333332500000, 0.312095959999999], [0.666666667499998, 0.583333332500000, 0.312095959999999], [0.916666667499998, 0.583333332500000, 0.312095959999999], [0.166666667499998, 0.833333332500000, 0.312095959999999], [0.416666667499998, 0.833333332500000, 0.312095959999999], [0.666666667499998, 0.833333332500000, 0.312095959999999], [0.916666667499998, 0.833333332500000, 0.312095959999999], [0.166666667499998, 0.083333332499999, 0.812095959999999], [0.416666667499998, 0.083333332499999, 0.812095959999999], [0.666666667499998, 0.083333332499999, 0.812095959999999], [0.916666667499998, 0.083333332499999, 0.812095959999999], [0.166666667499998, 0.333333332500000, 0.812095959999999], [0.416666667499998, 0.333333332500000, 0.812095959999999], [0.666666667499998, 0.333333332500000, 0.812095959999999], [0.916666667499998, 0.333333332500000, 0.812095959999999], [0.166666667499998, 0.583333332500000, 0.812095959999999], [0.416666667499998, 0.583333332500000, 0.812095959999999], [0.666666667499998, 0.583333332500000, 0.812095959999999], [0.916666667499998, 0.583333332500000, 0.812095959999999], [0.166666667499998, 0.833333332500000, 0.812095959999999], [0.416666667499998, 0.833333332500000, 0.812095959999999], [0.666666667499998, 0.833333332500000, 0.812095959999999], [0.916666667499998, 0.833333332500000, 0.812095959999999], [0.083333332499998, 0.166666667499996, 0.250404040000001], [0.333333332499998, 0.166666667499996, 0.250404040000001], [0.583333332499998, 0.166666667499996, 0.250404040000001], [0.833333332499998, 0.166666667499996, 0.250404040000001], [0.083333332499998, 0.416666667499996, 0.250404040000001], [0.333333332499998, 0.416666667499996, 0.250404040000001], [0.583333332499998, 0.416666667499996, 0.250404040000001], [0.833333332499998, 0.416666667499996, 0.250404040000001], [0.083333332499998, 0.666666667499996, 0.250404040000001], [0.333333332499998, 0.666666667499996, 0.250404040000001], [0.583333332499998, 0.666666667499996, 0.250404040000001], [0.833333332499998, 0.666666667499996, 0.250404040000001], [0.083333332499998, 0.916666667499996, 0.250404040000001], [0.333333332499998, 0.916666667499996, 0.250404040000001], [0.583333332499998, 0.916666667499996, 0.250404040000001], [0.833333332499998, 0.916666667499996, 0.250404040000001], [0.083333332499998, 0.166666667499996, 0.750404040000001], [0.333333332499998, 0.166666667499996, 0.750404040000001], [0.583333332499998, 0.166666667499996, 0.750404040000001], [0.833333332499998, 0.166666667499996, 0.750404040000001], [0.083333332499998, 0.416666667499996, 0.750404040000001], [0.333333332499998, 0.416666667499996, 0.750404040000001], [0.583333332499998, 0.416666667499996, 0.750404040000001], [0.833333332499998, 0.416666667499996, 0.750404040000001], [0.083333332499998, 0.666666667499996, 0.750404040000001], [0.333333332499998, 0.666666667499996, 0.750404040000001], [0.583333332499998, 0.666666667499996, 0.750404040000001], [0.833333332499998, 0.666666667499996, 0.750404040000001], [0.083333332499998, 0.916666667499996, 0.750404040000001], [0.333333332499998, 0.916666667499996, 0.750404040000001], [0.583333332499998, 0.916666667499996, 0.750404040000001], [0.833333332499998, 0.916666667499996, 0.750404040000001], [0.166666667499998, 0.083333332499999, 0.000404040000000], [0.416666667499998, 0.083333332499999, 0.000404040000000], [0.666666667499998, 0.083333332499999, 0.000404040000000], [0.916666667499998, 0.083333332499999, 0.000404040000000], [0.166666667499998, 0.333333332500000, 0.000404040000000], [0.416666667499998, 0.333333332500000, 0.000404040000000], [0.666666667499998, 0.333333332500000, 0.000404040000000], [0.916666667499998, 0.333333332500000, 0.000404040000000], [0.166666667499998, 0.583333332500000, 0.000404040000000], [0.416666667499998, 0.583333332500000, 0.000404040000000], [0.666666667499998, 0.583333332500000, 0.000404040000000], [0.916666667499998, 0.583333332500000, 0.000404040000000], [0.166666667499998, 0.833333332500000, 0.000404040000000], [0.416666667499998, 0.833333332500000, 0.000404040000000], [0.666666667499998, 0.833333332500000, 0.000404040000000], [0.916666667499998, 0.833333332500000, 0.000404040000000], [0.166666667499998, 0.083333332499999, 0.500404040000000], [0.416666667499998, 0.083333332499999, 0.500404040000000], [0.666666667499998, 0.083333332499999, 0.500404040000000], [0.916666667499998, 0.083333332499999, 0.500404040000000], [0.166666667499998, 0.333333332500000, 0.500404040000000], [0.416666667499998, 0.333333332500000, 0.500404040000000], [0.666666667499998, 0.333333332500000, 0.500404040000000], [0.916666667499998, 0.333333332500000, 0.500404040000000], [0.166666667499998, 0.583333332500000, 0.500404040000000], [0.416666667499998, 0.583333332500000, 0.500404040000000], [0.666666667499998, 0.583333332500000, 0.500404040000000], [0.916666667499998, 0.583333332500000, 0.500404040000000], [0.166666667499998, 0.833333332500000, 0.500404040000000], [0.416666667499998, 0.833333332500000, 0.500404040000000], [0.666666667499998, 0.833333332500000, 0.500404040000000], [0.916666667499998, 0.833333332500000, 0.500404040000000], ] numbers = [ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, ] cell = SymfcAtoms(cell=lattice, scaled_positions=points, numbers=numbers) N = 40 dfset = np.loadtxt(cwd / "dfset_GaN_442_rd.xz") d = dfset[:, :3].reshape(N, -1, 3) f = dfset[:, 3:].reshape(N, -1, 3) return cell, d, f @pytest.fixture(scope="session") def ph3_si_111_fc3() -> tuple[SymfcAtoms, np.ndarray, np.ndarray]: """Return Si-111 data for fc3.""" lattice = [ [5.433560030000000, 0.000000000000000, 0.000000000000000], [0.000000000000000, 5.433560030000000, 0.000000000000000], [0.000000000000000, 0.000000000000000, 5.433560030000000], ] points = [ [0.875000000000000, 0.875000000000000, 0.875000000000000], [0.875000000000000, 0.375000000000000, 0.375000000000000], [0.375000000000000, 0.875000000000000, 0.375000000000000], [0.375000000000000, 0.375000000000000, 0.875000000000000], [0.125000000000000, 0.125000000000000, 0.125000000000000], [0.125000000000000, 0.625000000000000, 0.625000000000000], [0.625000000000000, 0.125000000000000, 0.625000000000000], [0.625000000000000, 0.625000000000000, 0.125000000000000], ] numbers = [ 14, 14, 14, 14, 14, 14, 14, 14, ] cell = SymfcAtoms(cell=lattice, scaled_positions=points, numbers=numbers) N = 20 dfset = np.loadtxt(cwd / "dfset_Si_111_fc3_rd.xz") d = dfset[:, :3].reshape(N, -1, 3) f = dfset[:, 3:].reshape(N, -1, 3) return cell, d, f symfc-1.6.0/tests/dfset_GaN_222_rd.xz000066400000000000000000000761401511276756400173160ustar00rootroot000000000000007zXZִF!t/9| ] 20rI*쑣D)}Iu^rXُVWa*37*ͷ,|Sd~C2TlwT? 2px#߷N<#g,7@èm&G3Oe&\uql2`&G)] ~=ES ֶZH4-u爣e*OX:/ʣa/(_jSBBhpegaEfQHGCL[Guf2z(^YI-z$shGnT4HdaD)\|XŷʅIDF?Yrz k1ʱȳ^QY&+`" 4U<+_tb gqv%L6.[MK@2 O8䒞hP`IutSK$)JDDnPtLD zWjDЉkl!&'pX-[ґ=-+!L+`&Ckfh=$U 898n!U] Uȧ+l-UtWHڀGO؎u Pt r͊2XQƝN\ xǃTVxԪS'xK˓ pTӶL4"ʀ[iLz}䅘NI dW; jdi MǷ92Q+:Mǔ[_KPp\7kbIP/o2}sAi 5Qg;h7I/ں9 wUZ< 9/h}=£T(9t:sz+cAk}=[q= M9/+!m^b]-pm ʷE6{b?$`[n*FR0'|o&Ђ}OTvd02w}3TH"V ɸo탊RjMlqRQQ1tR2K (sf[^fD1vD>H+&D%Dq$`q3ye.4UuB/ Eb]F|,h AlT@8YL2.H~3 D䄥]F+a{fFW(J^;:lc *M=y9% {6Gҡ|Ytg+xeFEgPqfEnیX^+y¯Hjzj*V7X TQKf zN?5G*JgZƃ5*P'N|erVTSAt@@boNEەq'%=wJ]h흏]JgPx!b܄O=h.aԕ|.r,u]Ĥ,]Jp|DިoұA0EtGED3{fo)Ak!ԡ@LEu3lh5ƥRN"W"OFȖG P5i*V7!HY[8ՎR}J@NJu8b_M|Jӗ}' "5PCK7drjUd0> a4uX ve&d"1dzHP!3JaqtpuHP} Ahp~uM4׳w<28++^F>2v'̣q'Pp󻋎jQca}d n2׵sS4Gk7; s'ZRX_(XU 1nGGY9%]vxMZB7r19B8hCIz0 đd$TnSݢSCŸK@]=_h sm=7lnt{?k.d&fvS( |mK)'\ SW#e3Q uw;{KplA-vHf< b[}SN}{6.LAsq، _ڋS)Cj98@vN P]rt?d &njّ%G/ql TjI/$n41):q!mG;ph*``,uX$x^t]$qVʕji M?U`Ι5SFJ<>|Έ@T>!oRso²ƽQn ly=^Z 1+^$Q#Q[dgڨ k$ߙet'HL}-ݨ9,#KR).41CC˰,rČ6>nCei0I]cS f Q9,1vm`,vDՅB蜇V{Ubp%%\7d,,>NZB 3{L@z= olN Mi}dܓAxKj#ơ)ƿ]QBQβcxw/>N4'i,=JqEr(WC}޺r7S779Ĝ K+IPlS\+F,;Hh!?pTqg>0hOl .!ʑ #gƭyYsPE$GG s-0\4ִ}%JY$8ڂcK?wa_\Eqmˀߜ|U:~ OUib؉|Y0Sە3hc'.vAkk[Slye.4K`ȋ9g D[+$HZLT ֊Zd3o,.K##vl:a)AUUiy7_v\}PŁN32oʳ(s:7Y089f~yKDF籚 -n b~͐|pF5B9gj#*BwW^| ΆX|P 5,{a-Ֆb/.87#2g2{Flnjz*NF6BGNa3/8@;00M9RRwz]s+7!)4u?WZB +>#9|cf}sEN w@P<]d onSYA1z62yVO$tY-q@V>W[^ 2N&Ƽ%~~ )3N_l-5߷#R)!:+{NJ.89x!2d]:EPSYzAY(d1$7n5q9<cXiLSB ]D.Hl*wAD=N2YM[/|>Q3K`\[1^nڰC'Y-GTQP`/wl 3)+F$xp 6znn>|_3n6ж^ȤJNЄ|z=ڥWNmm*܎oudN֜WKE(&vno B<")poޤIsln6PlPS>L@of&ec,Lg4 gi[F1jf㥲Z+֥P:"t~uƷX[4۪S,`#c "dCӹ0l b__Dw08BFXg~3Oo ]gG"yI/7p\:òEW1toYfrw|< ϐǙA :]߳Re4.jx"cmKԟ}z!Mۣ,E):`n5,#B8BA}O] BTq" ɵ4 ,%0gk(m%|x]R(⵨}w(u7,ҫ!=4c)M>P0fzOco3x VIj6Piw-Qn^p"G6טo-pq@guKV܊!&OO[h_-i6wR"'ϞMR6ryiK@7>?Cc(T擡zt9 ۋrb ,R!@)UMaq0-$R CK(D a(PMN]M_h fNh(ob(  XA_.qvѪj\ž|k-[6s |Da$=dSIp9ru՘$Z&Yb^o$! p<Uh9-c>!+Q` aXQur\DvtP.WK^lVuhv-1AO\XKa0ʸUWѭ a$w)]ܖSNF8&xܾGl2,j{2]UP r/HO@ YVw[K^V@sϣ:a-^Lo]7sf&3AqAβ)a(-TݓT`Q]3PyK+po <@ZD*}L+nu_JY@cYq gTQ2?>ǰnǯ7J`¶0`Y+S¿'nW9QeMl•˥ <+:HhXo ;=s/\UDGi(Qcԯh}Ps| g%nkP;XxY Sñl+$A(AзfnΩaG:q{tu{v1^ki!Ɏyf cv,n |PE]*!̉43G0)W@{(We /x !qA $ 5e-׆FCG%%$*₞i9Iqw<_ h//$ɽ*qd:DgI eODYԒTdֲ}lg5NPC274m:VWRw[{CSwWlCe6;0y|8Es FU$3yQ$8! fG!<KBʖ*{-Eڹi "YF4_RKć -{e ^ozk;rhl z7g -ʈHX%+?zaм9Jw2ĩua.LuE z?'06$8F9xS,?&Z5-2>fYk2IjIȹ( 9 G5t0Rص=?R n}\sSwU9+tNR2lQk)VX|ufA|ŽPi%|N.20*MR1v í5.ce`KS{ȉqS;uoi~Oٿ\~݆Im]!CՉh7\WvLSiBD%wdcnًmNF1m,4{a&ŸSs]|db4ڛgI8ܗȭI xLce2ܚݺ¼#a#d|! RH^5B-<$*fܹl1W#Ov_NzΟocp~e|EAqv9ְ:ޛ׫+aMC2.XhX bHytBpa_@A\ # 81"qY9)eYbFxU)_[Gizfyp[:oe}\'Ta;9vSJs K[-!cت0z1=tr=3bQlK jS/ӫc]!J3+02Fy+2$5`|Bv%͖xBxIoKl7b{J$Vǣ򩲬)M[ Y<~y+3:*qp5jglpj%Mdjt'֥Xs^DͪO<8]gHؘMa uw5l#,FűvCmǂp O܁@^3vdhJ<ƪJwb? 4@Oj@m7x!}zHбaٕ&J fT  %B㥦L W:5@vAikM -њ@u2?u*uY6[4#.}DoIlN GDQS|M"dȞ=F#Yj cȚ䇭|E$ign ¸'MGXt]@/U2c =#TB)dtQCQE\AGC%?&$!i^wM.[Ey[ 8,+/ЍaA~Ut9Y3d_%h? J1(Q5-(R,G?kUTn*'䈗ϪڣgO,CT 3cDyO T,K?;}S\ػ/j*3'lrt ^'[ha>S4 ~{qjpM-F i$c:ca-69 m cP47B8AE ^;cfılg!Oh/8Pq$,wXWXp[6MYyHf): Y[G~~9;i p2eI{{qbd;QpB(a^ƈ"wĈuoy uuƧ⚉!JwLGz(DɰGӕhl +Pՠ-_eS-, yA $'Yd1$kn+Ƥ9?~]ZmǤdv"]Tq؆'I\:r8Z7C?DVILB >$1e/). )b4eeEqOt$YqY⋻N8e펳@!ﴽ*vALѴ^,-htI<)eYԂU!зyFU8I2ʯqW_^oW3VpkEv]ȉ,ߝ=.6//#ƘJcl/xze_@Kxc~\mQ:qw<&U zJtj5 00L4m9]qbC≶[w^L‰W `7@.zf_GZΥ?Aت(3 %c!5siX׺x*:L~գ>>p_y&L<('A 8Z"*G8f#!Q`T%^3^E?:cDgg۟ |$̴ i;TǝeS!,өAnlJW[$KWH*m.ʖ*މBwhC 3S(SXj7ΒEz^ H Rv2 m0j^)79Ucc܀UOY-R?u+TՎW vg7X)%d-bcPdȤ:}*]GOWPK*Be>a 0찾gp]+ 1MUPY_TAuݚߨ@'Y_涼)Ғ3@mBB;.M{|| wM1oiY9\e=eR~(4tD?kX,Ap.3ᴶDԴ#/^ٶ2{HM 6:żtF:zݤ>Ә+0SP왘HDDE޷a#qnJq{5 x&!ƿw Fv^<u"O!ֳfEĢM.hԒH끣Zٜ0ؑ% N\dި|L9 $ԅqkn\bc ^$B=;Chy(5cOLX]~Hცu|aLs'ǝȓ_7~I3l-!lı挩z_*'?%wry c'6?2Gx |A2҄鋭p>w+f7-mBcǰ$r*AR#T(3$\`.WЮgr!@,z#b\Qܹ\+hK&S@V[]7ڢ۳U)[·ҧ喙)2]dFW5UIH;P11VJ#g9<M;q1,@\y2@?h36}WQ}kX>(3uL Yg3L t2Ap9B7=HynE[S4uD)*_ TZMMT^lj!-y={콟峽\;H&ngF`(64D_TrG<ij~xU~[] "rWzƥRm @޼Ť-LR\[nOm$Jo#F1xkuף<>g3lh~Ck3|p.osDo{^g\!z;P/&tī@Xf8yO0GetpRvlb1JuʮN4}.Z7@KAO} _" +W=$&ķ31ocq ט7wHEVM Z\{pC: :xGY,䭥-OA5̧Tlmk ׎J7CS7;!'zմ/% &";Y^֛ ,g\ҾL*D0q&W{UW\#rIx3;*.Ցp; .DlܵwPp3a9')UC,@nc8Qh_%/0EYRxk+X]ZXݳy2c 3Rڃ7 tN5qGH>5c0X)jnsWa{-uonxL7C<-Ls|[_AS K/ֶƊ08l0z䕙*y&_pY3*>(X{x?a*UF^ =A4&nc8ЋIqiU4^nl®c:X<"V:oԼ˖Q\.(6uJ4W/H8L ڄ:-}dml[ #.3 efC_Vߧ[ߕ A\~A7nhA)zfGQ$ex )yDh T-aCȸN;*F_#)Z}KQaa0SXA0WGD=\XmKis#eS%dv)Gd~k d_6.34P٦JFw {$P ؗ]M2D1]W[IP%Io48u orBC2zﮌCZ]f>2"VX _:2tIP(&xhu`y Q^rwޖY Mj&2SD Dcqd#]p856jSw(+A8Mv&kܬ'Da {^[ C;IL*L'1P ȫB`T+]F[秐KȄz:z 1tz~e.ίn|5`grJ '+a% I-F= e?/ WA`?ѝMf9y$sSfai _==ROG,0=v3 h s̡'cD1Q;Y88cjjyA\"&#}O^ 4KY!WrXPRTUcGsq]ʹry-b=Y;lqc%{TG1hUT,\ 19MDV T!" (_ tݛV=1֡Vћ^~y4"@*=W^B˷l~Bnc&e_lT/!cqt P!Űz):&:voW&ZױFS<>k5m=W֢t)[ԕtQ}l!LaT+#T, '#3]UjKҶi,GoKJY\!畁g*jWpAεmv[&RtJS"9;5 zUm{\d'GM]_Ku]+Wn,~:\l3Ӭ@"!KtW`Wl-Nո;y`1Lig. ^Rk6: Q [Ar22ĤʟX!y I%!Ց Y-BVp!PzSCcU.2 M:V9h&b%{'fdjGK3ГbEp-Mv>X}:Yg $ g"3;wtzx}MC?uW_ e`>Oa8&- Z4K<c11_pUSC{V1LFO3Vb2<^zxi61me<~ks6\C٠wğ*sSS$~UHÌ|_^J@AϚwn6(e=6f;5sY`COdh9[Km4sG}p0M(>jA1a@FF쳍aŝړ6ҜOσ˷?: -@Pu77ao!_>oh/uȼp!Gkt,' 3{řJ+2SIT/I&٭c8SD6qG%DD1:WHTd_319.Ncjh]_P6MeI FF) Uy=PJò#\z1VM$9ta_S:Cnt|H9qoyMn2Bdj ()g$AB+W)W ڀ<> dpKuTHj (8Kܯ$ I+3c.b uv` xa+2]!v7u`dYMW ]h\1C_&pQ[꣘GSv(-C5 yU}VteLΗS(b:RXuBV˔) tн9CvR ?e4roWy72xӂŸ|vf5$5# GdurO H}J|!> u|kK>rSPKmǢcǜUu K<(! Ǜa2)d{dܘI?.2}_)x~=y$qIfeqC-梖k\"Հi#HL`E8Lשs[>(?0LWց~/Z DE3%\;O?bVRUs !Zg=I |''uD@@_'1;~[C:J(D4BDr/-тy@-k о|u߻QFo[uRUɗfPƴb2qC ~])(_{)R]$ۍ6-4C$Rإڡ脺v0j K_ t7:_u𣼐8K 9,ך*4B~I?,K`@j?r#뒔tT,I^#ǒTv]RXh,{n"y"1šj3^zI:'pR1iVexi&2h pI[W/oi񿦌f $[U^侀51&ƶaׯ!nB|.lqtj;/<[غ6\ByeQ9Օi9A!J;UIk?RQ\Qm6 !dS: *8nqav8C|ͼ"Qܷ]o~Y,^4|[lXbR*S>@gԺL&8^Ðؖk&oc9u{-̼G #W`fCLx@a 3db G/^a 9 Vf!c:y_uN!یӭ@ΜA&`/ VW$y)Q1|Sڜ5'zU6jeՋM޼1ZrxpWO nlm8>>!܌篲+P *KqC-a~)IStC?!ʮNPM!mW3П8!: n]xfϒ fYFaˠgpU$XU|)M$0| g8=-V죥)kuAUJĀΈkϷ4U0mAk'bnSjKiڸt&Hft4_?=o[~Aa] ]yUzrHթccZʖV*')Ӻ4)tIf$"ӔBOqeHj I 7=Sq*w$ 4#1W6ob “R l*q>A)idgw[ ~6 )XyOr23dKfU׆^`{SSWKL>Z_դ0aKr_.⯓~~BwwDkHH8 .g1pm.4 IAFۭՑx&GsFzQVߏ]gdc9 f7eW@->ٮK 7ZrK 򃿲0K)M,`s׷(d֦/hJ,n)TS ;;?Fċ26(E8dyTݐL_*/{cJ۾]KS]j(1b68і:p#~2rHū / ߯(QjwO/wj$zX50|P9&:&.E$סȂ*[Tٹ#)`3~v%_0IQ9N/wͰexk A?\nq~ƳV^T:'{W6U O8l Uy尠l?/GqsC*6 3!2KyL~< \rsM;';6FJGъ_TR$Y2PXמz {D7Eu zL:V!0D&q{Y#5 4m-SL؃LSMzb٣eP JtG+ՙ%:TG8,ox54C1>=dXt*h4Ϛ75M|[)zT4ׯ5Bv?[AG,ދTfB'"Le\(Zm|f(7qM?uFijit; %\4VrT(\<ʲ(G Ľ:"ba] G>?~t=NKC<8pbl4r&+gʾ1ߐv1uèf8)Hl o]2qƑ5'p\Gp@ZXlθNYoL_Q7eÄ9y/MX,sBf IgJCB/Vu말P@gI T)ԨШwU.8d#$i{!Jb4ׄsg>YTd &}UѐP0:4<^ {bh?(0,X;>iS\nt|#- Z}hB(& WDdK169mEf6e6Y^ff0@p}v7ER`4ҌW͝fsfܿ1s[)4su},S\%3dBn|ߌg:%ņRK5G^\Q!mVZsrsY N:8 MOCȝ h}*pi.+ o_|xZd RD q8WH+m㯑79-yepc ?޹o;M(Gs$@Y;=^ԇ" [i[RmWzT;zq% })A[R^!5 Wy Ç%wTd;Vk2 jɜ"^$E]DZcNnL+/M&_oen6X5!CQM`2Rŕtإ_'ѻM]5'TpW7yQ7 hx$ys"C1Y97DdɲdL'b}`&sʮ^h`ca..j^˰zK}b ˮO,OR X e?v1/0mEI ^Y#taDC [0kA vd߾C8̂s]Xa򍑁j0t|YŐ7 J2s‡l̅ d` peڞ7P%d rǕ;ڢP6uEґPt$V}L[7JBi,Z-P*SϢ]N+gXĶ% wFDcIεdU*u49 8WS cR+a`lw?W1\t \GDyJj K\$g6L_Q<]e | zM38oFZBӯ}mN9'3)69JpJ):ٙ ux=]J o;>7cM;9T$gݑO3 CAtB|>DTh4k 9#Rtp/5\XZˣu[xc$$kA v"쁎rFhM le,d2Y!G[k1$"Ǭm>iG5!~OlaG 5qﶿG& XXyڀ#U-jz:62 ear7U$.{'@4V o,jo2)^B,Yxof΁"KKOɷJr\' \!}j,ZYT32ON&v7@/, 4n!k{fc^QË2P79YQ5i'RnX5/|n\+O΢w %(V$0u;Ae UX յoj#Tb4)c)Q/:V,A&ߗ^Jݗ> v6 dɛ7A9@9'䅗ݓW2ZҎ2o3{ yS%moh*Fl~W%(rM s*0>.5s E褯0,H\D5Y*P\h+p^U3[8ئk0Hm/3JͪW|:nX8I3lEKΰ'UOG<FabTfDiwF4Ոse#tŖW6X0dH0pafk5-_τkzr(  &paV-S3BWmc3/;GI3$M<{͞r47h]'_ C%Z {D61+ s[Z1ۜG튐q@V>ɣpm ~iXB1A3&M CJdn(TCDj|·nth r3Z7\pN 1v!.>v4Kl5*-Ʉ.'ːֻIIG%MڕtIxL/Fqc_j>4)ukRM8Oh5t)u8ϸマ'?fsLR2[__D!=q!K*8 Nۈ i]5Xk^=j#if rށm Kq|dSsQ젮 6/Շ](ISZF <`@C2vS68xQo2B1K-Vf%`r/C/8ЊsxLZnC9qef_O E|fӦȮ&:|k^?ʻ ƭ>{hʺiњ/phA!?, _5Ⱥpɛ&gLp[ *>y2cFtaS-YЩai4:׈4QurQ+}7sFXsm@ュXlG !ʙafv#LLj R(,M uf)],\ V8Hc_ZM1ENfv'2B/# i儒cMhՋVo:s3N!*0R&`t{X&Ž84ֿ󀃁C$",'}02ΟQ6a&lfi:HW7ldXCŮԽ#N7;zhɋҏB=7j#lk 9c@EcMX#ԲSE'^f\Hi8-.:P`sUvc$S'm2bÿZJj(}/ >`-8 οO _dM) 0]Y Q"f¸S& 6S|zR%a!r %+C ^D;wiSj Mh+eK ⅼICR&l9H]-S7FCw@,UFLҋº(PإkR$yJISǸM+iA\3;#WQpn?Ʒ0Q0%_Pk\Ăz79|5\>YD*+@_N6Ѷ+Q@Fi'<4B- b=%|?DXk+`[˯:9CjBb>f`T;"-bԲ9]Tuq7{ 9U;S4pL R!"9mtCf'u+x+Rrze^8L]uo |ZƩݫ?f^ 2{&g򷧉=ل Yvﺎbkz?q;!b Ak [8>҃:PIxaz 9c 1ʸe<Rf̘/#JʂIKBezb{9p$JDr,'|K~Ȯ?m|oG68 id H9-hpܖ&I)r=[v DwRr7yp NE)t~PB!"QGG["8r2 Ng)inTS>C4 Yhj&Ѹ j?I~o{(`DR@+79.:X?AuHЅU3REV*_0lGX}B *N@+U+N{rlѧs=FNXw?(Z`L4\hH;8эۮěfjRn1q8tPPuX s,$+_weg((=sFlRzOtoA T{6EBXSx ~pmPf#nBDV%{2v/b*95~jxNi43P;U }PW+pS&­ssԭpxai$*;=ʷkn ;S+7)Y[A?\{H?^OH~ W0Ղjp䕨klO:j̞9ga`FxWOi-99'kǎ5]muEfcFNW5^y^hPQFT]j!hery3i 0+ #5oKt!1q]D,?O {.㠘ڗy٨5yO>JKi#K哘sfϷqT9xm;E'tݷ8dFΪwY8ExZfȅn\΢Ew`N o\eٯ9Ul܀ gw'MW"h*1ilCo `'O;;tN !Ty`:ge0jurt!=-> M$rމN ba4jmz1,=A<9+SOsY`_n\#է 4z$ AKFӣhPJ?hD++m1~|59FƢ̀-sC!?2^icz?x m~2t: 7f_< ҧ0)|>ĥצI :y}(ɾ"/C<k'7lIU"/kٖ6r4!9ہP7C`1z]acQO; @ ju>$?0,H }R}N~vxN&駄ίpy$Ҿ FӒƦ?Y# 3 MAX{ O¬׽7~@ c ݚ?Hu*fQpJY!rjy4Hut{ X4X;dIPddd:&,)s[4r2c7=JgKNPޥtxiV}__pwt[ۯZX[\e Pʃʜ݀ ן!s7SGqX yp:шյL{u"dkj8dȿ8;~Wy"3[N6N:QJVJ ƅv.lܞ):?Z$r7ZoB5xA4kb1Y nT?8@+k*wDSg;wv(B~?!={;F%ǏeF)p﹌ʍ;H5Lm{Šuq~tlBBCΙgZvwl_2.qΩ'qDvOWLJui6zQ?9A~u ɜz[W3k$js'Rzo,U +:n`)))v4,8O\ReӪHI$DeSTfJJÆp\c1iϘm [88Lr*o =epau7I0!9߂G0dL}+_~K:pXFzMUK$yٳ/OÃtJ Sc9yD7b=N@[4` exFjoK%~Ai 屣v4\ Hg0]b# ׆a29ۛ"_NPG~fqDU!7ңՂwM.2.P=x|܉9FR%-xqd}_ɮx28f]Jj[I/sJɤ Xcxx\RˇTծ.ڦo3H-КF Ҫ8@Ջ1 p/$>tWz ;=@ FxqzK\o^? ږZt( E?.^^IaZmuLjLQ4;*Xdb劑ma^Wf"bUW ǰgmERf1GZ&{iHC˓ !rv|{ִv4-dPۯԉEUỗA*uIo&at|+)lTua]@ng:EـY#Ŋ"m vox432h|2QAI3󕉰1g3.b0.O++T49H{+9 zmaU_sRsy鱚tpk7%U*t \ R$K[k(C|"5ǔ zKoMo$% Kv_n &ɽEtCGoox= >A]r\ ]5/(2G ɯbHKōld:#jJ>M|1!̛gYZsymfc-1.6.0/tests/dfset_GaN_442_rd.xz000066400000000000000000003621401511276756400173200ustar00rootroot000000000000007zXZִF!t/:]q* FZݙדJqNs+ͨUX{& ڕS42qnF}D&Y(A eLz᮷N,/1]F&gA~JUgrQ}κS 㰥|Cr.dz.YV)vͦ\OhvڢS4CpkL;k#d b,HH#췾iy*\PuE֯ #NS4[1BF$0 i|O.Yk?z]v}n1b# CeMR5oib[V#hMqm2-Me8QQk.g*ᘬR oou,Y3O9|Rm$7[O]($H=%Y+3Yw,1|v $PayA>Fe-nǕ-0|3h19`Qci78hlׅ/ֱJZꂮ`,ṭMeט.,AoכiHv"~L~_VCVBCtvA}0jLe]H{#槅 L~^D =ĆM23TNJ >ƌ?ϸڤ&ݐ7kH,zﶊooxC&mxdcl:k/Ƽ"sp*own*ob')X xWXL?ZPtph>$CIZ&4aetZ<靵P+qBJs{y U:vfdЛP$bkx+I1Xt%c ?ޥt6gRXէa;+F&$_HlϢʕntaaBDY0,ûm ߽aI\6CܜLDEWaSP_N^Ut?SQÝJ|&$]Ϭў (cЉY-HD!P7uˣ88-lS:$#IE;A˧U4lcPO oq|[_#ƛɧfL"2x]Cl0k.z5A[9:FbL79}0S%ZS0=W:f\]i:r2bwKae_\u79lFRplK+^k/uȹm49l GTN'KGU,I(erJW0Z0J4`K3 = UA:Ղ]@6,?†ݩdg-(jS)hn7N[QUwN qjltVuԐMw^&/׭O gInuu,Ab]ǯQU+8]-ƢH!Uy;ĭqåFoswYC C.ǯk%Jά#fpZbn߉w;WD0)b,zX ֺP1Zm.c`r VdU]c;7ua+MK+v]AQRx猥"ߜ1{RE77 ^&lk,^=ј/|ze .RXBuE[v[m a L;kU@tV)i3ʻHΉ:[wJZ B6;A"oFqU ]!f9YLށibB%aT\QзvQM$HԺwX3`uu/ml${% ؏p p 5QzgIGh9SVM Gq'3%}*N?@A@!NpXېBW'қ&l7D9A8 $b4JV`tYRXtL۟d~V F%%)|CrpYh@A cpŀD8x7gKX"<R%#gnuِ3q/˓o曳]n 1#>וdc6=x!9BiP"A =/ ٞ.pݣ}vT ac1狝GԷ1"<4<[l^@p?SrxFNFV8I%t/)C J]$<86 SlZ_uUߐJucrQEpς[+chvzz_}ޜ{KߗG:b11?qJIG{+;pH Kфh_zWJ\+-}۱WT}Ec6P5TJXY:4a&n*:oOOPAj5-%B Z"&#WoMXY% m]:/>ۆn=GRqHsl=Iiy6 {e[ 4kQl-g3ƚPMjYU&+=o(K>=0GKn2efj-8Zq=!Yf2l=NZe!AxrQ~G[uQ HO^ (;mhTeAS֪7\nz䘬1"r7E /2T לK2z% 8" P _띠"k?jJH\\eZ6!; Ul#t咝b g`y#fo*- UFt6jNo85ik]r@b\ە6LbXd4=mX )}[fS^d5~dm ++I9ߒC|:#,ZhGc^OZ8/S;ǵ7~go쎞O)`Eh$' gJOpZR}j`߶>LNJl\{ l[kgզ6q|ͮi2+[ckun L|`fƊ(tpIO&ɺߺclI >YSlZоz?6T' 9#QA2:p#sF~LGyF[bN໡lq$j٦ _ߑc4'Aq)4~@V瘀k>O^Jʙs3"Uʪv)VGSD,c2 a % Ӓ]KtDZ1x \+p,֬=jYu `Yrcs5P+ċᬀ܍%3K9S+^#Wz[?V[BZ͜'S|$,^CPk<ी;w^2h׳\!F[w$%Tȯk5X:qNF4HH4״k6:t!vũ-7$D0K7mFu}Lhӥ>(q6v`{zxJiw[N=r)Nnqp q)w%?K~_'5#91/$ld"'Ent мwRB"v3Vc. 2w,|c_f=#Y2u=@ Iڈ:=Vo(5HlaAfȱT5WQŦKlg&ڬwSiqvrVXJ'ڽ@B$kjT7sG5t y]CX޽W+͗ތsݚ<+4+eFZ ]ə}~ -5͠ʯ =ޱӉz|wyCT/<ʳ[[P(HKQ,x00 bE=)եHSHE{yo-F9w.1ȲWֵ߿ا_OM;}j^?8}TQR3VD g`ք` `vqG9{p3>dE Lrt*( ]׵cQHi)WU)ʎ|GDPx;22XKI2*84k<3r'LA.Fgݟ}C7Vr'G(9󖮼G 0[L;hY"۶inTX^/ uˢ#K{\i1E]xA,26h'Va kiw>w4*%S hZ\(*hò;\CVm+&\I ?v Q8e` TDU U@9`}eZ\&.|G d[ (h֜Uq"!b"///:j->2Crh-F 9Mqd!#THn:(_nMoY[8fRBD{CZ5<& ۑBD%jHbֳSWt)[92\/ʐJ9v)`]/_*ڊޖxol3:0d`G۹v[[ypRe$01>%\%I½ &s 筓"^ezfeD4T찑t='w?N zVOoh` .ٜiGEܱ&|`^d=W_K0GSύv+!+wpuC]%\DKqPFHo[kHРI!:)#4_y []a:*~n"~lQ2/M'Ho&dɨ)F'7&;qWhmr놆נ[Ii_慷,"fVbSR4\-Jƶ kޠ@wwc$kO\uX^m,vg%|kvmW.mx;=]Bc$CMif *38"(c]hZ4 Jͽ[dZ"tʖaLڌ@ML(LP?h!?ٔrp<>ƾ'Y5ʃRq0nY<Ϫ>Cu>KJ,ŽNMbg^;64Y9jTsWE}v IF4c ;~V!ucGTI#Jpox=h_I - ${)J-A>>'OJV zځ`쀦gJhMʾAxemӎRPM$C4@kB<'! kLht[8̦z*%|맵t#{ݒF"&4`EɊm45'@ @$OْF^éӦ|u;H_? ;C?߇#t-_k<-G镸,3Npgmk 'Ur;$h+|ۀx@ vÎi;ޛN5.on;r'Hex )f1%, Iu耳ThEh~аiG2V1G|]aq%q2(^[I|Mce3 ׶HfC{Hlt_hp}t 9`i>$5 -W){m #J|9خT"O-/(su(C}O6-[cK5sbiy }8AjEϓnɺq w)| !RG3&cv5z %w FpPSn,xAQ5PxMbF?"ߍ*PKN#"F%r j>q, ]!0JԺ{4FiYHt6WO75=!u c8n|Mͦ0;p RM@c"~5EfbmqI_2לO˞)55zŗ+`\.d߹94\%[(۩=Ni KsiT? hJ黥9 exJIB"b2I0j%lwLؕk\Eg"}j w"'aٶmmӑeIA|5d*xs'Fn:Rk 7*&!?KוɍhSbbhϟq[He_3̷GSe*z z`PCn!ohvW2e&Ӌ`Vch3l ӝo7vK5j)yL.ت!w@Q!g3j²hX5!G죍R<8v2B2v[TqϜh' (}<2En]ㄧ%aͅ(ٌ>NK0AG?5/Vn`C6=!M@caedS"Ι5NwLMy_wU r X-?A<MLm@PcrѭϦtZwi`g ӚIyeݰnZ*:nVphl-L|vE4<~h'~#^c%rjyj6ݰ'3UvnD:rR!11ȭu8xvZ h)'(ʕWYDž>wT tD':g L3D s,clXZV{2Ki$b.=?$p>dS?gv$At "+ ]5UQbϥq|_.wrt.# >“|B =_iR[d}Bt64ξtpX, \xmMwJ(8x_^6 <}9OMç̀ gd?͛tG˹u u19{`\}VrJ : ?kqN[MG{l^- X.Q`ZD[qRX첱 G?]V}/ޠ;7B˗FawE_MۃX#[+,j*n#iH&6<'.YMW yfCH+X׍%MHXA'Lx@P*p2O&ISl>.D\]ˎa%(JT6/uLP$B>lWܥIV5ETvMOTnAi.)@$^c |ul9Bt6lEa;{^* kxmHt>?A=gPxo8ԓc+ ukERBw k9F4f:B;]FÆxHS"'RsZA+ OÊ{o"$CE)=P0 !L {/c-Lydʚ]+X[vo ^2^q 7+o2 $O͍'Ay|4>Ԭu[G7oXr0B]նgym"p >H…&LwXWUFHrm[Z똂@qBtDKNQ9K8/Gic\IО{k:èdaOX:p84al]j|M( 'ש nr2"V 62^ou94o'"* bBo5t͈ r“~2S,{4ې&upgAn0a<7@rGy- Dׯ"%6\>01A~_b%VZ?JsnGO?'pJjq)F,^=hg>쩉XeY<Į֔4(K)AD}ͧDz#$)X `qz?.;e-K\j #|, Td|e=1x.Ú^N& p\&!mdm{=(3Fywu k:pl^ 'Eu6wzԷ42adqq8ڬRV%Tc[tg_HpaVdiM݅`mI5*:!Wn}&0:+d`i9-uhlyR Sڡ 1[oojS1 /pfd(NLQ 3I$"j,_ |!qGic7)IEP[ t Y4Ku DZS:Q ƿŹ>@ʮFIg;@Ȭ۳rc1Ѭٟbn [1L{9f X*A,7ɳRƋ^t9n^/_*k<:8 ;o%s_D?쪰6qpsYd)$/ 7xfe;N\631'Dc9y,v#_{f ϦO-@`DX\xeRr˥yPeJ&)ET7YΌ5 |m^SjbSg $+|CwʁxQ!VdYM8,S4+P)ÍŮa \ToĠwQ0wK1M%-;fbQ$Hr*W\}_|a"'7ڶ@!B*O TCZF7:pAt 6ekFsIx<7=b! 6^wDX˾DEI@KEN"[9쳶SRvNB: 3i#'+ǭ1(2t ytԌ hƝt0\ثϡ43YO&ux-IsOBb<{݄\Ry!"~+gcѷқäPm"KGvYPPS93&RB`}##' ]: aJv9-|  A%&-r |vJIؐ+Z4w1*2 ZK fyj`om%՟4Ua 2ӽ/3fg AA"MHKh|tB2 ێBUC$%{Ypd4vfAhYԏ\sOs!ی`+Mt//2R [M7bY+)`$ϳH1n)?Du6P#8[xkX.ZX #֭`dwe DU0=ņu͚Zp >`I=++knzI/iףDSeg-ڍŋ`a#?(n;߼s .(RS.k}+S4.d\0{Lt9yw{_kXp@ ,0BQ pĺ{C=svM /K7b5HM͹/r_.m9<'j˱U|pʧdD0 XGo[H}P3qe?{ر 1I1{qsPqws쬚6IѮ{ȵH72lcuw ,tI8a-Y4{=j0 V΃$3'[HEQs 2Z+P@ /W_7=wKO.CHlr~"$R1(<7 2@4^{Թ@~NtԜz<.zp 5>> ɅzEtF>Z ؕ |n~N·P"Iޥԫ6^<~WK{r_Ih+!*?C)Sx.d}쨁T3a|NDաk(=GesxϸMux #)ٗW+޻552b[2=x1=t?q @\ 9c0}?rFk :In3]:bdW`g[^H2a Kcw0Ekɒ[qSxI;W`d$j8Sx zZ9it|jh߭И\M4[h;[Qu=Lܵ Y.宸X2BfT5ZPeQ@6+?|%974=b6XgEx~]̓U'D@Z.ޣ+֢OuʨlrUfI/VTOdnlq_EJ))zIF~5b ZEBG'ӟ bx Fk/]󠣠lBުDRKW!ΰgsJDkKZ4M YI:byQ _' ]D|H~=,$j՜/4(7f²kfȨ ^/ܑBtGeU+iFQ Zc}gB>sͼD,ߢ )L*kLg7ƅL1Mh+1˶USekbĶD wR_$ty1>h{$޿tC1p/bu/>sejJ#uKj}KE!hx* {J2*v* .]QHs64#^ޯCS])̿8hI)03IIﻟ("WD /'&].ү./+*GQY8%X 60Wf+ND[>؟z'ZN-7hz;ICpR 8" v^VwJaI:%ucJ7 o 6,īpdh䍜&Jt@oFB58_Cت7{`M:,JcGz(PvOSNANOrro7N@O8ioubuXR?0eېRM?XҦ9agVe  ėUV M`c+QMKnT\ (LWE.DԦ9LBF!O_9p=`D-zy{*Ƚ.o"} W^{LX)oR"*Ͼ{}&Jt=hծEI<骢@f "VųEΔh',JW_zM(#3n?t강>&my`IrMY;wUeI.@2yLC=`-O4 vLrX)|BT,jWKyLn!YZ1Xh|K%K৲huҔCLƖ@팇T\ELӯQ"/n}kBq<ѷΏ dCʽ9 #/MD"rHmf,=586\:}SSؾYW7mU;wҁCp 5!eͶ8^tyg"K"WW9lڴIbL'z]TcJ V^4^Q%tMob:!7|N5 %Ubxh=Bds1sZ ~ScZ$:_"M N}ɏ;HJr&6|"e} ,SZwAK rs8;l bG6WO{ʼGAQҰ,QѻXjiy8yE.$]>g`lAoyL!ڿa `U:,(grxH99joWݧWߣ~燐K^$Y!S&SLӏRENy*檶`HHꕤ,Wast!DڍKi$F',F|ޘnX}iˊ(U`*R=R+Z}2cZh0>ݲxg0^;[#|ThLHO³@*1ceeIHqt- D)OƏ@CV #!҄8 3R&m׍K4Ӧ>e?@߀cU@M+PX܎{M,͝ch6ْUx1٩kkzC4}©h /f?נ<yY]^K4='*GzsQ{kJu5LHU+B(:'7/1Wt.^ ص'&n:w'2;L\iL/%96ųd`ڃEh^ ߗ&s{U-#f,%FJu.=2K2J)QA:X#442"Y,kQ @vk]=?hrn~;\n[u਄QpA .|6N [(Q3ܧ^,)R5\T$ *=&ȅ+o${vKߑhx(,ktIS(/܎"j#Wtţ** ?HS7>{ pZX%fzAe厞} DpL.5x6r{<(_ZLY6mv2UeY4Tqd*kfD/#ـ%=8gT\8Fs{$׭+w~Z, u7P2/֐0mr젩*`BG܋oVPYQXti,/XLg<\G9UPceS,uW֨D.ШUǖ<?!~ofa͗YV%N>H{2m' R-hV(ڑhKw;Z<-dGšu#+@ &[u =?mP%W)4e-ԸS(!?'!)5/No|) B*[~qtxG p=tT.a$'[[n )Ƒ*ݕD/Pr)TPhڻLnW,Lm``s:qy?3 zt@]/&EWMNDӉɪryo1LãbXgF ԭiV׭!nс rG:RgKú> *qU%\ey5 88%zC^,:Z~̼XΏJGV 1yǼ)~)V-TS[nG: 5Q݆ k~ƛ k#~AB\ ox߮`[c-8} 2\x%bn=5q9MA;Κ;-dră:BfלCÍULKR ʚXa%^yK \wF`Wx<%1:Łӻ=)gQ,kqث<[m>'$H$AqՆЎHl$RNŨLK<.+!Ag9 ȰvYSШ 787LkRr g` Jc{+ZWFО(ExkQ7ޱWșWe6TUZ:i&%bbӠMܝQ읎nJV_\(9 XGUy_0<[ (antwrl5lW䣑d>;m$Hcu7){9Ɏ8EƐvaKtg1wz# %4A5=b -T9D]ue0G]me;!I,ޯ\M_]x-P5w D: gGo\(BqgGzɹq j4[FES&93Nju۴E׎h^װҕ2*z$*;; ߇O9QEqy8ؽe?G3*)Jh`dIt.*$)v*B(j4u"Y+S?2:H1 Zg^F^ -|W/z"U Ǯyj 6W |\ Y38@-5uk_pF'usH>-eѩ|B<~$P. T/wќk7f@i]b}֔hE[H3mZU#:Ts*4Zeۃ3'N-uCt\߁w j3>-pW W@QՍk̐8sNC*H'/q8"S(E8< 'n̑<. :x;#}RQk3Iq iQ9q3uJ  YKnPpƏ$ `"!UTp`3aCj7ϑ>jwWsܥv,+_'Yg?0jS;yEڧqXg92I¯&{ I7!DЫmJ gi6)NyOd)CWMI3˘kzB p+ p*'Gཉ)S)eM.[]F! [C>_nبpz.fkc ;?>,ܢRivd!킥XCɻ.nshiqVBU|תCՎ74=#^qn&tN2F٧,rѺ]i4-!%y!,dW%5`}Ua6l w@̘W^an&?#vG-=OC~y`5cREDxx0Y:"/~q5e lliU\Mq J!CD'k/m! PIf.1uXD}h>wt Kw1s07(NL~#>IӰs:$ǽHF̭jg :u 2~ߜkK^E0I.n^02݇`YNnUh3g2YmsK$LWd+MqߚKG{#  iu@[ZҔ wmxZ dsUX\n#̖.!?;'q": J%[H5 pܺ<4p[Z&^ڌ@r@{ I\N(cԍqCg {~ #>zx&߷Ka\߰!FMg6lʴٚJtt'@  a`ڡ$j ʣ ˆc<7_,w*Ijm.Tꅮo&2*uLq\YU/ u ޡiU3=7%˕3v]$řgGOoz>Fr\Ӆ U9m6[}XDLJrwQ{E/8APRYq;=!cIڷGsJ0!\9PR;xGy]_7Ag-OQ֣3?jNߜ|1 Gw6(=[X'3j/"?t${Pb>9{! 0;uj,dћ>x :Rc?5]eo2V9vb /Qm'(Y OUehM׍.S}n0t;S}\KUY_ylѽ5 Y4ʦMZVJ/ЗZi!ƚԚE ̴w[3e,lCb8MFq!dVPGn`x3i*1O:by "7R%T$ȏ QYJHc 'oȊjvU @QbBKer# $4m3 ^(]E:M-UQy헩S[-**;q'*O@N)eÑ}ao30bDJ Tw2xqqnGCE:* Kc,0Л%l(^/P c-y *R u'4Ϗ @oxZNyCf? kk?X70yWBGI*iݤЫʉ 3 Ll$$[˜,&Wf=l- 1,ؕfObmrv@W)B xq)H]{!?Swq,D%l+.IЧJ\9tU T.e[ TGy+ʝ^Y" I;&`z;/sb *cà)D9.;8sѴ.v j{y7[kY*d |5[E03DWóy|W"g:\DTh +Gzp瘾(%QS` ȼVO~S\8ԔyvDD!@MS|knzlo߰w9i\;6?\y#@@KETWBy: 0 W-]^ DU)SPy%~BRaӔ%tzVxʖSZՠPKuUWy4-nՃ۶d <|P ==Z׮Mt| z҅A/2qH $oL;<5 H (mEv#]{kS_FN'tq336c)eClh'؋X~fD0ęWP$Foc&\1}i,ScV{Gӭ\ KoEֹ:JOz:adӎ e1!"_j[\9'Sqn,Xᰐ5i־>d ,wppIo% N:hJPa#XDBBܹ[ UXZ_,@iv1 F% 9ig Z^ܕ<ֿ!2/6LeY"=oNV n\+C@?i֛ğP2 *%ۙ[}3O? xs#0*pa>l4SNځ2A'zoiuؑi*6eC\jR!E=iyҶ 8P1£qhx8~UͲ)V{Rc 4ޤUJ"qۃ5.D!^(CR1M9V36ɉ{pU{k zN:[~A# Qx\1:glfJB㙦 =;yiIu']1}e&B:Ci)> Y2 w:+@ULq 0zAؤWdE9zwګ܉E Ÿp NLeRfܷ"oS*R^VD+c5zW캌%JGw ͿgL}HX.&!i oa^é K,:bO:\:b+2}dRVT:6RNye-aU2N/uijALB d4&u]ãjWzATYY)+R88bbQ'%;,-:iYW FD⛳Q˫ $i2Y?@^vɌNY&m.vb35;TS [0[nʠ+q3-^ioNpkfY1{ASMWqh.J,L8+2LNh'{ke$Yᄚlׄ}R'wZկ{b:1TG6UL{ˑj#f^֫Pȱ~]dRC$•;ѕ:S={'t>">P0Tw2I?mi{SuX.k2X:~;Z*Ic'Vj[jOV3ZQ4uSIw]ŷi<:Hݽ"k,EDJQcхߌg$1;Qe Jp.9 knOcjMM}w70k$'~y-ѵne@i AePqfDIP$|(>bb j<| ֌(L4S?S\jJ} S<u4))F˝5" f<݌+Lxt2cNNZqOSsCf9JQ^0J qdȠDp+[66#Vţү{8uhY}EAtz#)0=\{]I6CpuAI#ʂ18m<OI7!~9Ihʸ/IeLny3~yaUOdUZue9ˊ-Wnamt@=n,==JABTχҌhJiB@奿f4Oz|%(]ʾC}u=Lx_ ζ|1?swi G(&Q|0% 1 b&Mr1e,;M+Of9M^BoG?H6!jl|7(8˼w}|+Zk6S;-2r#[rz2!!,jϑyy@|:90;ۜ[|([bu:߆*MnOS Br@FRwy1«oDESjFlZ vg9qm)G8W,.}P)qIJO(Rs>aO!9@JsB_n;&ލ.Ȫ4dU5 O`PZF ¼Zv6KG=^YCk-KF&h˕}ɲÈU9^P_t,xYImTRW Rk,fݓǁivf"i, kyYF]V4Z;؂x8(gJ8^JsLJv^]S(S\/&$}-k7&KS8}#,.ghv&BA%7K5S!?sˮ%|"<Ƽ 09)>Dq:ܬdf5ur#"63#$ R en?i 65;i iyxd=n8uYT:d& rCڋ%Aڽ7&{jTɵy JQ<%eB/0ߔD&ks3o +qHs3Bgkj(|*9*LvdgZQj0AŘF--l'y>c4=gJ7nl(./(xk.YlpM1wT.!.pkV^okNJxڅ1/O8o˟iiZsP' <š&+II"1|}c t'kIwtd3߶}p>6%}_j8NX%uuDn!S=i١|YAc0Hr֌K4V_b2Hг!ĤzzXh؉xӖ^|F\st5C'EH Iᆝ55 Ț.ѨvcjVفth D5YT?O5>Y8wRc@tmݘFkYHV1|MO3`T`?bktk'>ѺdGE ;]ݶf;#rH*Gq2GtG>Z%o"O)6PQ>o5%:Oz <:9O/r=uۂTvKEl"-@DZBƓwA:#ƻ?q3XJ=$)_⦻NW yF>T񺘈z!ץ=x642AaʪˡiUxbG p2}A$cU7(t{y?xav~/Ek|IӲ+hZﭳ=5N(xХʘ ]K=X\|K: ѝKr9uף_ژrJsv.Z'>!e^Q\]ϫy)X6pM^3>iḴ3!sXP ~j-RRډ> HK $q6v2Mדj8:wȭ?#i)]Y H$@g[8 +L@|ŋrGh^/Kݷ寇#LHXv\HBpVǑx+pŞq▷RpxYTp%Mqi\4[T鍡(("XtIEGk3#ɂs5dP~A#+BRF[VAodt#k GNY29g[E@K ^X;~܃HgቜmaZw7&mU ǨފM`p=S[tPF' '{!~r tm/"6# ^+_m狧V.vN69}KIB%^1rW5tO"xgh^/!ZO vQiD2M2ʻLK*]{aSsB.S8:bav(Q? b{$>7™"7}BCN <.}S-9}uee0 yQ;G q]{>[O3fl53 KO*I-L6*@Z5# ??X*\iFǻ RSDpN¬Omӛ@pxhVil;.ߺAzdӀ)xt.9.O1S8A$`zJ 7{W;N#v9R"~ &eJ1WZ) 0ߐV0u{P'šVҕ*o9FX%ЉHBy&ag0#2t!Ҁ^o\~G}Z8n)d#~m?koH`sg>UPG3!a u332ZYS+ai(]z]g"S:Y7ܱOqQ +u`Gl61Hg *ZXt}G5rv"%_=^'*Evwӈ#ǽZ_'|v';!a6ʰeߵ*ٲ7MF@c2~ePfp<wG&;8JO,|Xd*=Vh!ڥoؼ)1n ~`iM``>cW+nH~USk`l9QFJR6^,-&,.)=gߌQHmXӠW㨁K c'漼"/J$RYF\R,XnaNMsX~R͠z2C%w iq(_GKq/Qp6~} ,iFͮiQ4{@/ qm`485pm Qg Qסj{%O?,QsE^ Lnݎ")vW'2ky($+vPj0.EC @+Q,),ߟFЦr d9w@z̰sLb;z@bVs(u\HIT&ho{科~ w[){YSfSKX3sQQ)Ѣ\ϭ o^F]Uܳ|Ϝ S'yD P.Ķ8f0\[>kِV N2s^Үv nu*kj,H:_AM:=C$rP*(#ZO >ߐ!g-tbX0/7TBgꁬc2a}@LA?K.&% = +7}{(betXC83EXӅ?Skv|ŦMI:°ah*Ȝ1rCsF4X tM"\Q}lpa=][Rm`Y aC|wz(h{Y\MFU'@u}@B.FVHa6~uvN Z*M|$FY~r`܂L;ԧɭri mޮRPXRZ- 0F{:Gԋng#(_ [֗2$(-;)BDϻ#R0 biqEy +uR6uǷ(F5JpxU"'5= K WHJovQvC.̷g5518XeqF/vي7p萬YǴ X1P3k b?_jfrbݲ$wp^tj|xP&b%KHHhJ8~WhiҰy<9]1-Z9 y(QPΉЍ?3*#~<5\1$I#f@'JE`Xy,F°{ X+I; 5AKAg!O0K"USä(2G[.uAOSpe 5VqUqta0|BHEѝ$߲4ypvZb*RDG_ӅYg')YUsga?7HvY%h:~(zAdP34Hy6(.gH4B`u:-_=Y X2f1y?oyNv8⒌/B/b ODH?w'{餝jX&fK! ܻZԽZL[ sF 9bzU+&if܋I2f\_fLI}ɒ6HZn7YSDž50Cp۩߳xaiڮm^%3)k% o9ILM3>uv&UbJ-Z%\GŃG! kDv^epq@~x'M7S?4S Z_M+#xIZ)Y{q>IPؘlY[bROҲCux.(+G_@O1I+$iR.GfG}=i-l֧afҕb?Kǡq]ƻ zѼD&I _{V"UQZ!':@ Osa|p~)UxcmV#e~+ .Pŭ8u92u[LA4HBٿ0F%vh}@|vw xy%04.ʨu dKqG8YI3aY$"dd^ٓһ&>|0qSGzDc$? !kyk!I@X򿆄'_e59v op+0TlnK|@()ex¸OhA,[yVLFq Xz|Զ)$VB?mJ~gk3OoJgԗH+hK.D ߑf{.|cmi"Nî+`b!D顇]QC8L~`kv9h0֝FzLb`a3l]3/3fp=[o-3'Pq=YUUW"l5T%%CLʰ#%z::}iVVA&ʂe^ CRc2YNW6 ݙg?ISߕo"+;E֌n)ǰp J,Rx~2-[U gDIyW^_|/kϖ ACSa/6Ȝ7Jegܭ3LO D!Mm: x&N~S;]B:3q.V%EHג)zX-L F@'zۙuoQT~\ e1.^5]Np;4wAQA Z:f!ЎxG$7)H'?=тN}|N>)%]Ց d3HqLԴqA0y9݆.>r[faY3=jJē0}`9pjlDzTC@ߝ,sQExX9'w2{ M/~Rq1+C! 9®W-Qw*``L|~, gQVf,gbٔAմŜiU 2|C:Ȩ)FRmg7J5݀M|=mPuea#(=:ԐYX%Z0.{ZѢ4}aH\I~awM"4$XߵIR~nXg^#*Bi` _;Q9R v75:qZ 3qݼ#x T:%'A瓦II1J;+ODXg9x]PܐvmP Ky]M/|@ :1rfȀx@@Y<7r|$lQ&=&0Ԟ9|3'@0;bntAuK48ÑVة 9&njd^M"xma$Ё+[j?b)b&PFN$XլHd{~3Z)7]jԔD[eH[RM]"<{g\q\uZ/@x;5S*A:(ToBL|6T1%Z-xoeO0A>SʹKK+sU)!)ORDJft_@M| 9yN۫hJjG mӳWr~J6cU\ `? 4Ap.5W]Iy $-=ed˹eg5KiS[D x[tU*!/ -=PdGa5sUF?JYr M|h Z`fEb;bci eϺ3́ej:b:]~5#z%XxNِoJ\rm ܎2 &4r?@>h]Y7@T:PbC7\12bZkԧ zPfEOlMJa0[V`& 78(v58prs+v;W`, K$16plzQwYEu(\H\*( r۶FSR|AElLX &"a*m_:`\l3?+[pk 3^ۅ?sq34 ޻='Э[)p^D =̂nJy k&=>hYDA ^eTEdd`YYr.n;x+2] 8dˏSڳ o8fۡ+a&$)#Ex qUsC ?]NIW&oHtM}+ydG cs~-L*lTG6+nNږw&樸d'\] )zEh;m,Ej.@{l%3пPvYw+,׳lKRV&*Cf) xi1:deB__kp!ϐƯ Gb78:Bj!hG&C)?#mK-GF\clF!N<~i9<.vbg0EIp!lO]g;X+CWϋ @ƴ=Yf,p[,G~QR Z?.m]EA!DZ`ynH̐_鹃(+cP?-j1pe81PEm`iT&l*QdxBgKpppٰ~ R=Քiy4 Ed`ᑒq3j&*D"$# r;/lL#vhMVkA-Ԑ1uY w6>* X*^MEk撦|Șq`r G.3YS%&+5!lfPAo 9z^Kj)&cD6Bnb̮x62>"{bɪxW]ۮ#(t"+AX*/098m_p_Oz]\fAKrZ KwBP7hVzdnQmV..tA+w21]l5!q3 9^t`#ogłfiٝh`b=P Mf,EzVRfw]@ n@t/TyW1 td낽Eo02P^4tR%F r d5ٴ13g0c.a ?/ +sJS"50 (=Gz䮢5[ 1gWNw Uc.d'{`تj%8 pqWFp$e$Wg'Jwbfr9+nv`]E9O$O'F`g`[ǙAr~hL9`Uh牉 -1;$.,(^Fbm_ŨK"1[F!I @i!;3[zmNӛ?3krh06gryoiKf傛vU]D|Q^vZ{l?l3E\ioP!zvV}aͭU+&k'Wpr3igLlG ÇV^=|SOgdSց]a zF9Rm('?>!{JVR|NYC/=ۊ#X4iSFz /!O )=?j>.-VD/c+/ K-gIR7O=[9zr][UDB ި^|JH?gMV $wN3 K[; G?ʘ=5uX `=pYHri#u~6Mi[iIwCevs" Œ Mz9@%HFv fIiDjH/xG qTt;|rtg%BFRz0/0Ef7+?Ǯ6cLwW9T]>CK}'TysZaT:Jtm>d2їF M۔վ sB%Y^ )̀\kJ"Aͻ>s+g_&!Zt Ϸ,ehAH=KC,hu-f.&K>AڧlMn6Wam'9js XU]u7{ёb^ʷR l و"&٨E6 "Tq9HsBt G3j ^ګdiGkEמu)XMLv%qbU WVa=(_a^) \GDžj7e޳j- ts#ox08(#? }D0dyMRuA+^hU0WYcQݽ)iHpаprI 71.ZO 5N('ݝ^PkICo#33Wu É] ^0Y'㗧! ]1)FC֠ >+Uԏldg-"\Sbz +0oZ\EԾYOF^FE鹁&PlʝіL2U4\]An"GgrͿm?3|hK姐(﯏c<iy -/xvn5~`4Je WӅxw8pňU pvU*D {]hoT+p$O,:g V.Ǖ|ʟzq]e?1BDNd!Ic{y3oc٢ rwy"XVX]̗lf(-F~8 ~Œta1nI.A?)Ǵ9 v2Touw vLc9|_F|c <x{a` R|y)UlͯQܵ^b~7j*[Hz!ͅƵ_dPфlqy㇣]O!G>KS5Rfͣ9H(`oTSf!qROi'o@WTuBh_xH 1[oDߴeK4*'N!j?z=t%F_5!MYMY|PauY}̙3'J_z{]W0>!,pz.e1^{BɈ_l8ZJ"ۋ,^zЩdUjl' &K u(_<^!k g57~WAm1 w9?boHC ͵ ˷^kaOYI)Qmg?zsvC7o˷Gߨ(1 }&H~7:iZ%漢Dk/㳁`C 6[XhL['8""c$] 1xA4ʴ;,7G;we;|L pRf@}N AdעoV@f/<2 S I*"_檿>2 VGgs`ekIyLh\h: YML=$96hcZ1ueCcfGg僬 xdT}{,%Z:JomgN%)Ȼjۦ^D#4%o3x22ڲ*_tLRpHv($6I]pmRr-L50ad+uS{-3׭ʛw[,A펪 ay V2xjd &ԝCnnIpI'Ey$گW`A.Q )+gEɁ)&i|f&L9o|e! 1t))!v>11b]^Vr376MrV"ۤ6Y{J% `VT])ʇ%$ Ŋ%ʄ)J J9|B6G>DeR,ܺˠeX%]GQ [S:=,!>W)8l~!A)&j v-GmF(QjooV|ǚ|L: 1+-5 Ϳ; )*lh&h@@[5ٔ]B4GH'SDLDզ oťL|GБnYyZ_tF7?Aޣ7 y)Q8bL3we+*I\hb<ǒ˕/Ok#}pC7_/LT?.84+̇d]2̖V15bеJǠz^fLU\R*Εb$ %W 4_**+$Mlwjޖk?9ߪ5!֭~遊0GA=vҧ@+5qdlAZ.٣R׈X>8*ֲti÷,CʔLpWϕZ?Ϲ !pM̧;܄g`sR.hɬNVa-ߗdD'‚hnqbD&Sa$J |RHc!.J ϕ [XI'QAC0VC' Za( p<4_&;7@0%<ƄlPE>pTOh3/:5~xuP2L6"75kEv{E%oi"l0Y1a T@c-Hu'QT z 1FI$U#4Û7T f댸$(TIIqmxw@%A0w UFy 0:T7EFw!qYif' oϟgq+oVFߗH~C ~|)$iU~h%xs/fWLTgj+5 ~,ΎzpcڿU ːUgp,BZ*VF ]*^/P %k1~) |P2أVi+|o\I*gtV RcjK*@E4HNg0}mM*ҺO*gy <|\Yxђ@HFWVR.n6Llal;MA4 7 Udz;ͬ^LUbT. !.D4{^`d.:!S8sr0 <Ld;n(%)҇6p1iTÉzS&tIzu}}#2_8ˈy4G{21ВAVJŒ _}6VlW2Bmzwx:7p\.RU*x”̏qӇ!1 Hs.%5o2RAr ]{f=3rTu8Ƶ7LyG / 4};r RR ZAҜ0Gvzx e8fQ X>abpI0cVG~`eW=}B0eRBE5o6QY2+=`6ɐ11Bqc39|M7#lK7'ٗCXSQX8&q^x`t2Pdthm&[J2R01Tw*9,RkH$z}3nM\L7!Qd* dw8 -ߐ16yKsÎ:KK2)LY+ r$Q`G@ISڮ;Ÿ^&c8t9$`Et̕Pc8ӱUnc?2 &)AMQ4eEMN]}I(x,y[ȌhBB<;A? їgCm'?'$5FSE6{L(콪7!$9ō\ ^SUf{oc"ؤxY=8BX0Pr RI@`:ȢzGHV VlSm P;@BZL b!x;|Ө>^!|)zSXh|6ƅwgˌ (–=IhғNׇm.K)ϳ-@ ˟tRuiMSl=5y/t7\kȧ/#b$F?t 7CPciFa2itz%!i<嵧_ %)Dի275]wj 0ƿO%$ed|H=qoE16ٺv8|dJے6@6ګ\VÍl\`Uҳym&J !؅Vܰp}HE1,`@Goe~{4ox|%cG9[Q!QJ!CrfbF@R3Ԉc.%eM3aT}Rmɇ;.[ыq_hE/ _.Yh>G7]w=wB OB9[LX5+ ȑ6L)͏+uww\ml*O14\hIS|HC~D"Xxg?6[7IoH}Y AaN>7#RK`c7=$ZgEG#q:j o8$cBqXƕ{vy ߈VlWOIY):Mlpqjo¾7^ǛP}Ւ; `dg3 VǸz䜼EϝȠgJnσEE%L L3e:3"599mh #:?|𸍯xw }[0 s_fGHt GR?Dtz7cz\D*J ..з)[KF:QHLG`WEC1*~)+29ܗ0Gڢ^ZO-sa%caJV8H.7ѩ)po.ϖNGʲ|KJ~;$$!߼ѱ::tpg)cWM 5ɘYHىwo\¯:K}`JBumXԦG,9<G+&:ʏ3jL kF|%( ^cwmI&Nűk" ,uR8^_Z:%afO~MajһVY$e?rFӃۨHaHÂɓT$YX vg٦M8߽n&E'JaP6:K@<ݴaZ%OΐAf !=) 8VkJ~HRoK> WՍNVq$DDoj)&úxT}C/'To)d\ \R s?Wg9(T̲EɽFȕ`SªptY?<3b;ԀK7h`k[ . |imnBꍃtxIÍ`{ˀe!"Qkd10} ѻ#i/(s3=lA MBs$}(*[+Ԑaﵪ4Īc~&ӆߖ*:*5B\džIW֡m1BMrBJB蠿v}(q?Hu`QvZc:U/ajNRӂ y08$| f*_`TN%p#&`0[&@U:Z(onPzs;S}GfRnA9IrVMGh=;UM ZUÕ,eLhQ:Xp3dM 9S8l4ėLƬ^"JS135‡89DH%![ .I;:C Rl,qNZ'z#yNA`b 4YeY4y˓ .GM19^qYcifZ]u`_WPlxڕm>X? 6g6&[;|[3X->9ac ̨/\kȆeK.F>oZ>S$X!Ler3v`o!m}?k}gBBSEmx̌s¬gVր]&@xUS5#ݯ ry̓[qGX' (ERugw [k]1eAWORLb υpԕ(a_AFx{P]2D3Xmm{p [ G43*io^?ӼK50ۯ”@ bE#Ih Sas]p&]tu`oH9L&#"AA ]wަ?mZrLәcX~G5gl_ϸIVzAMr>LgRuwP,hclb{P;:ŗnZĥ{Ap<̻s,Z(i[&CXf?_[dDIPP(P_{vi&hpr ZV|JӳЎ* l1bf^A-Rt!O6' X*ac3O]@}lLBB^}cKV7>zCI F\{,rTe|S`D -^fC -V3*6um=%x@amcKXy%fjeۈkh͙7*ѤVmK"8G~7d 2Tg&A#eE,W\8`oh*{λ|Ƴ(supWY/+zyGJcsfFې-^Tfޖӵ(LS1ٔh4xMöTɔ*!`-Ȉ,d87`wWxzZMCo[{ ;6hry9V'~ Uv͇ KQ վeYH'MA.VRa(b3i2:;p{eZwGp$%X3bÚ`d䲓U5`FAYmT[Un:|8lb5uzD7Sjf,{B՜ԍ-Jijŏx^˰6pC O#^b7ڎ<}۰ʡ!LPUo lγ=v5kS K%\p}kwe0t~HxAY GuY)ZFWvQ;۳ E!9qK&vrW{aV8W 9BG\V R0džrEA> `aNWjnY -1|y A?A(7wvk{z (Q30=,Vƿz50b%yB rs ? s .rr=?VF&_F} "v(k {-ו 'v[lOvˍYl+6>v]W )$"kNN ZhѮ!M 94'=U>|cÑ,&?>`h?Qk~BI["0J"bZ;̛Mk;UH%]ӊ?aH֣Br~1G׻(1|ZR!V啿_aW;[WXJU2B j\&Ѻ%3/a,y';d;+tӒ<%T[֎3ZquHݏv'<7LRh2]X𔰜h~75p-F$q G㵟dfHR8 siIZ/C/ai Trdt*<"| v&/ 5漥mAݯSU $A@ hUS@l3lߑNtՓ:HѠөq~aiC`gߠh(2OܠPeg'RVZ0(+uxC? \=iod cUo#f"7~b.ț~};TFETtոlYm ʼn+zPPD.VuЈ7e8q-dB,%U?&{B C N$Fם풎[DBEv?F'/*ƢۅxWB6h4E "x@BβSTϣfzRdL n9uNȯc[9GP]'F#G@ou#VU)J2?z+c FD<jͺ˃Fd3+IKs2m&>SkpߔԄZ!{4$> y#AϣP)iQf, /RF4l^08Nfv# xGX'?@Nr9,]$*X_MNBB7K>}^ j҄T \Wk _jvl$ *caEB1!:aF@{G?xjK3 ]4;i?4LS0{@= wM30ƆN9GSo@eg♏ 5YY >pP5w`H)|?gLHb(0:UqF`BHV1 g-q66FfV@Hvo]["Y[&[&` d{fn?U8|9y{$_%8ip+>56F*\lnG=CA8 _zfkNNwrqZE~k~mmC1WCt޵~#aVW6rZùlcy:{\TŚ]kT]xLM !9yI c%9z9O$]L7Bn~cþNN0+I+Pvj_3HFcnR9W/  Qz:8KȪŊ(L?x )19OHT%g EQˢM!)+A2 [CYeDܲ MA <sz/ 0M:} făAyZOL[~?AV:m8JDNy;g=bW%5CIJ\/iy3o/mXz+BF3b;@?ڒH[cg.SЩx0`QZ0Om!h%sV 򔉖{G|1E Q`3}nD-L?M2tP{VxLʸt ϊ0n 9@-x AQJ>ἄ;Cv{2'VfɬTLO&޽B 1k/c,d? [ݮ ]]w{? XJdF2`c<NjX:ϖu'4-%NM­ ͞6}8`؝Vaϖ'n6+׋ppӨ>hf¦;8.W%ܐ&i=yu\n% /21I I=0hf=Oa -bcF24^;g[`%d.Sr1ߵ{НXPӪI9iQ$ިVDG8VNWWr4[_ p&Un[ZWC[2"M6|WGȩ9)ڜ8+eHe7+8(wz2 X1X?$9zR:vYg V=\~SQxv"&fƙLxj\nBV/<b4[gw].%9&X6;dKu[]N€#~^p2^*|oN} iQٕi94  L>Z؅ JL ۢOk<ꗺ5~b͠hi,N 9"7)c3;'ˊ-&GjyAg*$:kAQ.Na=T4궪0#cOȽN ]8lb̑|=G|yK0#` vrdP U[]KҷѼc||;`ýz.ۡLEc,{o]G9F`}Yk#Epn,dL<=qânʸkUlr!a%Žbxdi,gJݠ]cwV(-faI$3xk.ۇ?bZ~cx.1F$P qkF +5ke{~bt8yQ<`k'hmˎV] \[2RN2iM2)q! }+%aRHqM~p65Mq}~ȋx ~xwVcw<\jJS1 Vwҋ&\۠:Ct.^wZ 9~uX+d}8G_8Vr^պ%Y-dIKcdUr7ZVm.}WH?bI'ui\3 8߾_X [tŁ!G,Us -MӘ¡px?.R~uve?(CLw<9/.|B% 7ubG\X:V6c⁀ܣҫD^b)ltG +7ʛ95=][q`T*Ѣ[ecC Tl*&6~Gs#OYjz!Jv>[HqD><ŮU{krAfd닕EO%T9u(-m=6Gg43Sy Cmˆo`8" 7bhA\Q/:>$#R;804!A"K"ߥxylD@f[Um Z.!VoT~D somA9N3 .Ur^g/Pz!u_Zy߹%ڹ%A `A-h6* ^=1ubh붉z Vd ۊBԍAЛ~#-d[}-_Q.5Ez0& i  x_ +rBN6Z<&bcո1TӅa*RvHN>|RFyky$e$ Kc3~fC.3 .WGɭeymQpo: ƈp [x'Óx$k)X'Ib%eS" ܿ/}[̜ͩ֠>Wv1 ?75㹛rbFFL􅍆X]& ߴYG:W7ăӪk iiMBDJ2Z]hS0ѐ'+ ė,r=74Rs% ^'QZ>QNvNt5=+laiDeIڭk\o# ={hs"(Fb8>Ruf|b= [LlR-YT4ml IIzIF4BudT|* tMqNK#_:yHsAT<2vW#"Iy614߆*d.)J[8[KC9ݯaq`N#jV}>j&hL!ބ(RӁJ4J`qê.ʱ(Z=M'zhWb=] c$9r,aXqjr(zfxVr5anSÕY;_'27UB/81w]sN-WZwVWBW ۊSQ^LMqfcP\t"CTի)Sqf @9n5kkZgOz2 2n~q\ɡ"g =S9 }2Ctijb4դ 89K*OwJϞO>u^&iኮk#@+簦Z MLwJ{I[. EzXZ9eMjeiCH=Kޏv+; a&<^IXm *kp¿Ig3fbn'=f؟gDY|Dɕ Qqyh0EWͤ; 7NW]tirɛJt_>AfJ +C֙:50V2@;Ʌvw f;0gw[%(κHAe5AΕD } sPުfa yhE2n m:r ~n[Z52y>@P ):r^$']E t%4@[,Zqʬ48aZ<Lo}:=zKKrgVgUYZ9giO&1&si24WK.<{pzh-4,Dv`x+S,0}I,%xf+ uO+:>"$SFjbDJג!gP?@E*/L/-rlȋ% &rxH 2o7[ixځ|]q|<Dw蔒C˭İa*x#$5?[Tmm8nEk$[3UI3jzTi嬓A%O/9 j33wh`SCzݓ;m>,=q(;LF"V*E@[-\Sk"xyc >EȾg"S1h/rU)e!)~<od&?3 uXI < _Ĉ!%;gXaT8w:i𢐽 qv: C!Wlc<\0kxk~zɎt_TfUDyv`hw4a;jI̕#k^fE&[ardEpiщ]7b^6n4o")"r, Bᥲ#ݥVi6,nZN=Ǟ {@β^'Mڑ::n|H\sZo\*5ESnRMm&&!7,2,zR.3f>trL 8<reX=\MUD6f4=mN(Z?b*(g"n{`/-Mڱ' {#"zA$u⏠ٺ)Zdڿ4oar`FRI lkGw%^ GoĺMPthsA}K:wg+ܴ|mA݊u¯˪l )yӂj#e VE 3Y= eD g[uH&g(lI 烋@X0`RɈbYAFXe?x| .?X$r]#0ghcDCƘ/9b]tP9|_-iը,}z ֥*sjב0Rۛ:&#]<&HaB{Y#mK s>eA?ko'UO3|a ^v; @txO>ޥds+([D(}Xh2_jN Rp[㼸q. IM@-ЪwrslF nr ^,Wc2fJ8o&b-Zd-4d _bKfPØM~%8wh%ۼկV+0eLV83W 񋔚)%F 0Trs?aC/R﹑TwD=ɹ[OJ oqo$.T%{(S\NuFY0,j&-#oCQ/zMKY>2h> j;;',Ld||"R.8JӼcM8Hm`D}YL )6 O5-t|e8i. eitlefTpwSYJ1,^v5q[ay*[~$Y[5\Q xHM k+"LHq/Eg@3I' aTͤʕ'0Kvpa+LG AgE<9 i &A'_&2 G{N#Iū*'l2BN3mōB|}k7be%\<$SxPb !^E$[H \R;vc ɷ| k99:?՗<?] 3(x*2-SxNBoeݘxKVmPQ׃#A-,Vi'UwUL(*^<d:蜚ߋZrfԀR Nѝ=Na `'Z;Rjǵ^oze3ARaƹ̀Ҿ8ƖJmЧѼB@aL%Ѐ@y~XHcQ M3Ivxw7%CWo- F6xZ'C`oؾe.AP83:Z~.oF?,O/Xd(Wұ6aE{B7 ̶Q$1mdҰk^}v* Me16ķDazc6W+ s%v >9GYD qՄm>{<, #9OH\_£w^F *X5;-\[>ѓ0cX;d:rOV̙_,,=6W  Q_(1$݆-{O\ `UϛFȵHei .;9GKtzf9;UKYHbnvaՀh a)x# ,*2"SF2?]\m,K 7mBPӫȢ`s;Ût},ՎiT@&Vh V:>zkpS| HYz[-\r,j&2F\SvXsbK }5{).[EQ6r}9S7,CUc"QbdyE9a|axnG˦+'vX=Ɯ-tr2Xu>rD<ݩ O \%o=bU!LJ{e>]6D~Dnk.*97$-D4 " Zqć ϑX=iLu#f#gB G@KLHҀ>9=eix:jLC%|<k#a ˠx=}L9^;}ƹ$L"Űckf.vaa A̎lKK)F=G%i̜a`s=ІHKt0v':^AV 7&v"dۄ9^6"!lCi'J<`/9%羋,r\:mELG샘 JT)#WNQ*z4=NeiWbbcCFy$j%H}7\rG"Y_ ۇ%L)a1A Wc+S$1JQE4'!i^ԳEo (.{p0;P*r p n{*[Lc6Ɏ^CFb6Wnv6<6;7퍸d){BS2qFv*u-*#uE=i銻{u[˴ZN?{"%HdEZ ͫp@$- .o@ We5`ȲK2ŸMא3)O Χq{8&5./yG/2Q>/e36?{t2.n\W _T@*Zk|= _Z7+cQANF (r%}~\oXh%f2/q"=\*瘔 }?/A{?d{3yƅ?tFldG.Vv` >T$c"2#t[mmtm`;0cwuKA KZ ndeV=*[7L V+㪟 x^"CHi9hq/[d6R}n.Iy/>qAl]vE{8` މ|rxΔ4̋jCi]a1-0+LA&'\ct b~@ K^0RjDrygZiξlupnM6 " 2 {|hfo94Ut,],V6qfFK=Ő]6ia-!]2Y& |I.SG=NEVe(1GK+ WlC)gg/:cV@!Txv2i;mvQHYL ^L)=1fkMdq}<+iEh7N6` 'OATY3T%Koc_]}MC0mEO@V^悓5KKyWI&O~(6 -l])>Ut 56T|ltN(5+Ⱊi;N"~=o3w  XtٗTE˶2}M' K} 䱨͔`hJtpl6Ř 4. uBo I5C-#@ GH8kš6ɦ6>9kU[ҵn2ˋ; g"J:C%5t4W"ٜ#N?b!801u`Ȟ0T'6u7@RĬMۻ5WA-,v4O.ERWovt=XZhc-Wr\me'υ L0S$ONȌ?*kD> ?6((&β*.kqȜIQNhtdx)Q"~-\Grn,Xa8pGm܏sPu҉Z¸XJxǂ,3cgTJw s\ڠCjayf;[P88a`\7 ¹!6T$tO敩ʂ{="W/rВ|H8{wuFKr;RdKX=)X-(M͜~ع1t.z}=g} k T"C węGqN+hCH=OT@~M bFV]D|XɆPِJq}bfl>T-i w̻@p9iZ##څi"n5?8-͓o|dFc.p%3UcVKm3ˢb!PJhi'.XYvfZ 7OwƎYqI* m*KKsA_0h<#"cܠ  }AɧIhjxa˰̤ۛJd+khCG+ibo0lxm̗KLO6c<5-q|''w*@I.l #\-$?E tlE%CB\20Be3;j-:†"^Y,@3zݣ)%de"W? (0oЅ(w~6w̤,ܓ=ᴲmphotW:VMה,ڥfTw:Y_fg-o sKE0IgaSd[TCSM1}ˉR (| r:f`":L}4mZmwTD1u0(v!w(yAzޮX;y$6V y\٤TI3^h ;`/%;JKՀ vrY}CIa*lSo}L'T޴L iT*V1 CE,d2~’+X?PGsCꆥj˫`ލpא$ )E5Tj*OK=S1omo=gxTZQ&|mΔDe?߻Ʒ渳Iז4P%W$yZlсjN$.دzJ715 H@Ev3[4[ʹC!|O˕#]m f:!vSoˊМ }fL$#,&g6)5, ӆ/*4/aBH:Ġv4̬BHZ̼/T,g#О0!Xvq/ ;-EVЫSg "'w"9*~J\s+4e836̨$zVu|Yycwʊ"XMvP_*L4+pwO5Ķ-yԄp`3xx5b>(y:& ap9FǦ'RpK :փ6RiVϣ*/y?ezWѾO3ρoG qq=n 7SQOLB9i|8VTWN^8yuB;=c켫W\VQP"e @oD ^ScBAyK+[C ` J6cn5lwǍ.߽&缾Q81Qa>Ah>'-/LsZ<7!-ta}TG1 xjK(p;̘[XBZ_,Եݾ5Ycf 0A؍-8dji u=Ri.9ND]N-|%WGF[qw)BD2(U,I("|: @?ӀFC.|WZι_ / H( |Ͷ&o8&̪TJ.i` [Lz']zp!*|Nq_r;ݘyLf=W4.zIfs@~SM4v*[o |:㾪`C<,]`yϣqx  d 0&',b<SGp(w9K&xmnp9|&M<=UZ]A<)R2>O{==W ᄞOAۯ-7|ʦoc<\,F Y [=ABz2 !4aG%D*tf_P~h#EqQ.4"@5hQB4~Ufs3oKT]!UaO,^v}#ei;Gtb^h[!kܴJW87O?d xwY}Q/xj$t/ZV/;ˇ5K㒸z>cΦD7-OĊ-.Ifiwk'mUya̜詆:栦= 7b~ap5:("yP OM2X:llK#׹>1ZnGo+=4PA8ex>2Z TӜo4dr#Ex֐^ ^TrBDllO$Ofq7Eft&#qY}7E;v. C@ c+qye$C ߼+ŧ:u?Dm"y>j9w|"'Oy .DUHSy`& '!q &|a⿚81 f̧wji `3A-hijMjv&b1̿ ~KՄ5P+vW 0cۗUZD*\³Mz*6O/W換ڿ[[jLSpdapu靘+m ?~:Rr8 0ӍLfJIZ;|JW{mG|[¥DpК7N ZBһ=qOItC;{e` 2wirkK}S?2gE∶ܰV8D'ѐH3xB1 p@:9z!6q1/Xe qy~I}β"C8(6ja1hBC_}]|b83 6Rx۳ eɄOI9 &'JȘ'^rm&nG\n`HEeI/lVE$ЌkYO;x-9 ϭdQ4y~R I}f=M\ȣӦJ!'WHm<1cYɾAn~TOOcμ-^!\ W~RV<8L@Ϥ .Y6KDIHs6}fku}p fd(Dl޴b4h"l41,ĵ7[J;ӢSLmץKr )Yn;#ɵ/O$ wpex3#M?֥ Kq;OSH޺P$nVuW63 ϛ`UTn81aog{âeQ_skɨAWe]uRт&ҭ Sm0w3$J]&:4_bL>g/+MOV>Z)E5ahx>F; _w!_PS%ؑ6\ن٦NV< |&0BmzՏʅ7`S[-Ja̓C'VYp$>Po' þw~z~丽|r dg=[ہk4yjP51%y1_$΀5=Q',Bt$<4j a?0<2mK($TG0;S%nb7b2ٲTՍ3B/}n&u V"1q]d)LH5UnJdc)8)jxoj:y w#=BgfP({mrܫ+&^=J]u\fC^ȻD@GEyJɊEJ4GIฐ:\Y5;ݦsS¼ /vOʓۄn:" A2p*Ɔ ߷5s$hr]c$7E |{pj0"l?zkmkWlO%sYHrrHضP<.\EF"%#*{7 N—v+4Z]Pa|&UOi!5.(MF#I%JF$sC3;n4`G4Aϒuv0[dE+RR*)瓣^2pM~@o^3cZ9#!.[0oJ44 bx;A/xZ+r:S_V2ⶒ9T0 OUhSE?Gq fnY,!ͭ3 ?OHƊS?a]6?QX)Fbr}KG?jr}Ŀc ?ž6T,]sz~Jp f66=@Ή48!ˎPЌc!c)ov+}r,:Pzfh$r6,11ET 7#P^{Zavpj,xtxFc|pf-"4) sDQg"8[oKsm@HSvcuz-H $IHmAkѝvЎR*(uh*4= BܼY֏*$$~=&cy 55?'*AǛ!C6-qŌ(;,khw,%#uKEv`q}/-Ģ̿~g;2!M1E7g5zWe778~ƢKþp{2Ѹ]- J>P\s92;?T|UmUJJ /ҮKwahd72Qҥ4\T 'lV'w奺Iƒ dwRd!~M9=6CDJW=L*!WX%Q@9jx Qrg͐Y%jzߩP3Pϙ-mT?5 )9$V\(-u" |xVŎmIBiлs0@z9ECu^R3技UqPŬy!I5DZVg CM@T^!NM:gBfnF-7w,|ыLu*D&_2C([a|;cMFGy1cOa~h+k@qUB|f?sD'r~px-SΕu?V^.;S9y*&HviqE_xOR!>;(qcHpZ+N"aoyP}/HF ?}>HGTG,k.CH>1yjJ#oQ哜KwsTPY6mͧBgO涭۰Tz̴$ZLѝA5&)W%]Q +%cKma{Jo 1 .YR-0b&0SnWa-0k{Io=XkDďhTZij_X09בfL癡`FPϙ3lpR"eǝ{Kb*9We)VKJ1hxл6BN>Hv*Eb?"J8f`p;c]eހ$ȩeI*ݼ[+9"=2|@bxr@z'Y< 5NPS]ACܥ:g?&!fPIAΡ {s֧֯Tkģq+C>fGf7 Si~D{hzHk-DlÐBQoI3uҲ}dJ %;q5 F$?[ # |-YUfPS)I;m 9kM((ZںE,ʊ=WYJ[.ݍ@aJɢ"`)Baz奄5 mo[-J`^LۂCj^g' c׮l/*/*{O*4 d++"ix" z;'ک-n4'[˫pZiĊкOD դ ׆y> ATyXrwm莱XNgL&bycpJ&K8Ch~ڮ tH0""e!+͠n$)$̊݀; wty?MB:ʮ҄Hu5߻uS7#\* Ֆ`)WZͦwIqrYu{p#Ҋ?Pb~W4Or@Łbì69Ksmtr+E >w!x9 *핏ݷ#@vFiή̷n]v:s'1͎}b)=5G@7VkO^iк/:0 `ȣT16eP;]"g̨ϊLox<l[6K /"/zlCAdFAI𩑿pKH `z|B#[~npيp*! cud˂sb]1xP q@UuMr%E_63n*% 1ڮ"4PˊdAT~?#Pt,s~g#0:V}8JuGR5&(Y;rvJ>|-qy"ֶ)DcW<-SNEoNXuizVgg}[vʟH*Q=qtGd ,j{d!"69K9;vsJty1 !ѧd:K(ݦy&qSɒ Is9 3s1< 8VV*a&ۯ (@ϝ FE}߻K%Է#N+6GZ9U{&Dk*ў18W^08Hi;ÖEiTagjX[AvgHJѕaDem݂r)Aݐ[#J:l=Ă$+`+&!c}U)\bЖ&FOG7}*!g8ٱfa\Li.ÓG^!uQ1F'9$$/s/o-q|ۚkםU C$ HKLLVp.ae+,R} 3!e;  ̽J18H,v Z_Ο/[ ,'rm]Wv[CgwDЬ`#ퟢ@^ƇieޚKukzvDSGuÈHYne_w0)bA4(0[1*@(*!04& mcKnnr $RXd1'Ig.ҦjV)stIҮi5tHDt~-ŢWl6G>$l%y*"~CЪԁBgt8˘Az$ J/c=cWe1p2zm[ua]xEp/ x=` X ;,+!D ׊yev&e7Q?&:xXK8B qםche[3Z @Dm}zh+Z "WhYP|Ei_%b3IX:ɍ!wiSs_Na#btUg(z.75];r|ԫ_11:opRsmkp%2*kzLUL'+jrU ~f{EjP~PP3Aw-@Aj#/TDW#?r^nSlW>ČhqTv,"_*<ʜ:)uѫ]|ŝ[gnFϖJ7ȼ0va8A y5Ecxb(t^kS0e܍XCRgGrCcfm`ȓDD{*=8! ԽAu[z(yH<G=uan#"ù}_q\ɈTd5:-%5Ni,Ʌ깆ҰgRc5k`!`z@9eqs7??h{HFziV: !k5sf[.WՔ@89Y / N3[F4VhՅ./GtC'EDl̥z8ou "^&, A\Se?[y (ލK7}ΏrXt\@W]BZK׳TEk8 WUҽ^{\WPAzَ%YyRڛm}T'|f4Z? $)? YB&DkľYn Z(J;(Δ7Dߎ! G1|5(w|KNsM]t_*"ͽ_\6{hO.U|+s;вpyG*KEFȁ SY'8;>o\NZse""Jc# (49,Gdew,n=0)j_|P/ǒوtևBXsѼ}f^I {/ѣB=o= #q Rh*N\Rz\g$Z'[H-cˬW`DC>u[+ G.ݑ-ѦHpOK7 }DNh_-W+1vbA)ծekeqL#7p*Px<dA`=#Q[J-ݲ1;oF`R1JIuEK`=ݡ@ WAt z寠AЁWDѲY|6PL܃f٘Îir囗ol4Zfh9[JM [GC_y/L&썎9X*2s]^>Py^efUM\9%]uaSrՒ[Ep'jr7p?ٵh4MS6lPO|)>X*SQB2 U f'!}ǘfx7#>yg-iy3m,Ճ%\ц_uyG̏m9տJ"ild5e_iGȓ16#bVk.0n!vd1A&bZcnK*='$yAx-Yh EͶXeL%o<)larT ?0= '俆['{8f}A3Q$Y-h9H:GGǒXvD6`G$tȤw֏Z ωUH~ՠavWO{QE/ ' !1,´f̕ u#GQ4ID>?ҔGMt,V?٧PJGa>\ RwYs4YK]o>K<փijѩfC*mq ߪtXFS(̳xN>đ?#>tKM]CL PuAѭ˥_0`6W~J:J(R.^pMiL9}gO0Ez9{9 vyFXP*Jh󊼏n ońî ޟ3 _weY.#d>K3@&WgM#fl=1K6{WC 3I~f׷>UN`YNT}X&6<0,|Vh1?$B!flzM #kz|wh;2J . 30wÜ Ji;'U6s{ ~)!XK7Ƀ΁*M2eX dpNjD8ѳs;=4>\(Q)73:f_ؗqsP@ | Kы:MJԌ}(m@j o7eMZ.%[8ʝT,9C+lg"5;DDLcxn3`Sxm`ZyT4L˛w4 Xo,Y'^uݐs_ꭚ8տD3"0QlrőLGhwq|G`>CeD콯:E/LukkoSahEg(#ZSg=xQ "' 2٩Rk ^pnwWFK*Cɠ`ykܒu4zI6R"mZumd0騍zʲ2PJLY{JYSU+]j1lpX?*,s.vMg9[[ϕ J:I<[?JUYm}R(N9ꠣ}Êڨr6'6gv3Y`6SmC#q".F{jt}q+N畴AFHĀߐE{ŖҮ;ɨ&+ec䄒y)"autk=IvvVBC'2aST0D6PL|5喼'Iie<Z^hɊ#/~j,ӥdotr# #_Kzؓ0pSAVл9_p &ܣ2O; O''\/ZoV(#H~g[ ]l᥷3]Sɛf,-D&/~SEB [?w؇{8"'[cƄ. o$ٹ?I'V4u@q=t!_BÃŊGPvliQ(AiBL&C|R! aUڢKC34ݦwL}ҵMov9!3̴FiˤF$( -*\ڧM8-0I&i Ij AǪztKX۵dκHt:0xp##{L>y`QM `u=!LJw{Ɩ 4yF_WAD(?tBhR n]DV]Q30gJl\4wmܟu*G8v瓍ɡ`[Tk䍻CZrJoklS_c$9*Ӊg3#(;f]g@2b 9{f{ī;(*Wloiսx"_XU/^qp|P@!rWs? >X"ņA*3לΜ*#(ր| w as7؜| ʁqAx'-F,Pr!>c?sꚡ42;GYBLp퀕ujhQ溱[0qYP?C OۃS,Ό#0y@|\Z L̠t[ʾ/U+ \|3[M2%I}ɳ cf0Xm? iE·E/QN*m)E&(}Tmr3ÿy) ÙvWnAf֍[puk?/#a@?m>vKq "ZXtC='(o I)fG12}RDoJQ7B-i}H1W[/ C i0bZ6`8u~*篳/p{x ,^$n~m=B!O㌤Lg.QT}Yt*Q c`Y@ ~3(!-k[aD& HZi_P:a0sUA!F+4ɣ(dž,XyXTK;KD[YT)-b h8Zot{Jjq `sY@a o5v;o^B_Vm=LJ. e[6ˈ+wyj]YH2Ӳ7[6tiԦ32҃f"ovuڒT3~\ ş-Lq ]=4)&FkcWT\gs;ˏN7PGXsswɫ'bj|lu7ʚTbODΞ4DK=o#t 0:nYB95'}MsdѲf}+1]!DPKV41 7]V:ta9 >|l K ]j쐻J,Uлҟh9@yT>w>9(S _oY:ߡP -i'·dD z]TfeLӊ(pu5CJ*n[lα'$ԷzC/EE|#(fK*&H`> XipSZ;,7Ag̉ .M)pUاʦ:qI oA*Z&gݘ2CRh>=., Ah*`BH$f7ilJTᵛG<~l3dgC<'KA~tDM 걔 8juY 0^80ݎ;|p0bm'Ң/e\ ԜfPBYF㸎NkL*&ח&]{WS$SARv3? cDl6ZQ>s?eEU23ZXNj% DBeS:<ѐcZLIf<+9"Vm`=IܾѮ~2<.['pClV{vz*3ZQ@%2%:m/k~ ֤עwy#k.,#<$ zPD7(0h%~y| ni"iQ_p,/HB^YNK0HT[B2QE <JB@- wyęr4> #',^oo-#VWngY7>qx l]f D;hd4H|γ{U{e#DzԲMLщussL\p)V:U«tvg-"-mY .:1+jUeL0z.y0T.N?d_l6fʦdr,CCH% > xas6d(u; kGoU I*ZこKTg<.j+'5 ߮JA07̛;ݏmph X {7i\?jsS*le =Ve71$:S3*_g +T^Ѽ2-YZ8ueI?p+Έ= i 3h a4T%H|Fr8+ΥAkCbKV^=/ lb6aI1aX Pn8cIj>!QcXAV.?$E"j:lfScU+.TR%e9N: ݆;TeXD_li7"3jֲc[w3PTqh:kzHF2]Ѭn%)f@DJe8 [ɸ08=\zSi\LC$@yt Ct0 Ĭ+p=JQ  o1anrXN9OtsVzJ giůD5r~ }3e!Gɯ0R'|e8ʋȈcاpnrCL\Sp*f0**=;a&ٴdlgWjRIcMgTҊm;k* սb";燦(͑i`\(r,`t|_ܶh3-}܏iyP_C5Ȅ<~܁DhW!1*ae9mB"L5gzjVO8w. N+BꐅGⲅ w޹^q~jzr5' 0zطi3h}gEFFo(Z9%- W<Njǀ@X\FQK{ fYo1Ipc0 l+vTl56 $t&i3>Ri芁bԄ@MandGU.6.=T{Ԅ~9:hPX::8Sn\;1+Pj+}p݅ww/OVkL[ ofV1u{Ċav"-V&^uXIX\yUHigLSP+w5r}\]?!h8N_úJQSߍ4:U}&HwϝrcX8~U(2Yt] &p2G3XTTtN#Ymq( ({]2l?)I͸+;2z,΃'I\Zw7-8"7NV.CZ Z%v^69rZ#<'w{}wB(AcSISePsd"@ 6K54mB2LGf^ tv!+L-::I T1bg +ދ)Uװ^PsG2.Yio^AXլ6&B$oHcG]JmOxW?Ap+KËxPdWS7/SԿ[C/b{n#[S_vtJpz dgީWޅOEDK;v~ P©4D(u$,{'"l3}xjrQN ѧPo7Btt2RF4Dol;Q=/RG}g(E{UD>:^PV"x ^;VOx)Y͖~V<@ϊ,nZrYh|jWH糲)(F]۠a*NMd0N t >}XWJ9K{FzH2t7`rӋgP5=Fhg}\ Rh'J;g/۷Z 'p'TƜ'(EӂƯI_CTo %VTHSLލz\K9"!Ȥ08~\k0j(.@&CPZf^N.$a\6{PjygQ;b bq eRMmOɝDi/9tkc3mqidx|U$ f7`Fd}YahBwG몝ޭ1#kZArF/6+ 'f$Xijo`׳ώnr\fg\YZHn@%eJz AdrRp:^to;d+ =v¥LiVJ$*^)Qb*w\y)!2{ \{A9Gc`dϦjs i"2퀎v\i{vxh>a^ ۀ6dcze`+C';.t‰KfjD7h] Fۈ$dVoN1gwSI}Z@~ZdY5iך~k|:&fgk \gː6i-B:te 4["arc0ryC^xr75k).g9V.ɶ8"{ؚSc&mNkfo[;Ts #*# O&?CͫHUJ)`h5pf]M7>5Gjj+>9p 6jG-IwB y yrn]&>w>L1B3*>=謼G~D.@ 4'D}~i8#Ž1RYP!4(K%,g&sF}-v?s1SHlR!p>wSu4U]7Y끓"hۊbl6' OuĔ爤9>\ɑ x~fTxhոdb`J{F|&yonn~Gl}*E8iҗ9Q!Q|c5ž|m *tKӒ f)*,ᎪӅHv^,,AAA MyŅl8xIno;}Et_TV)8 kD{D?=D2E;ԏV@~79!21^Q\E\Thx BF@v;1Vs_v| M+;S|rsY%F}oqŶ% JI$]uD,?x\bL)EQCq1e7B^W%;C-G,(P(p3g?;2ss:A9Aăe1nt*wUTKJm7bI‹sƶ;;B?~{_W'k[i2¨gq4y9M; jQUQ ޜc&|.͢>aŬכ+GϢ¬)G 1Vv`3ΔBΊBBv@ĆuNﵡq7"/] l$ W;wCtٹ$Ђ B|`d%濧@oO k<á4jҀ,.y0,8댜xsJ 3/Gԕ=e4P/E[)y@ZTk}Z;{:k*fPj}φ-?Gar{̺HJjayt%Rԓu%ҩ|{<ܯ,%-\+q@P&cXW{<[X<>,T/s h{Lr ("jb I={D(ȵITIQ#ߥqfڝLވMncыT3~ПxsEbAQ/اoQDbm~xz(r{X(hZ o[(؀^Y2zHዞ5نR2W׍ o 1-P񗉮RѰBRZ_`|O&AZ(>"\Gg5E+&i^(7߭*X-vY0 IY'#3Hܷ+z'd8HlvRR4|* dZQQ7ڼ -m'%4xF!^d) n(:Wsn43Fkă&%Qդ?!F/e'?nL| t:'E*ȀԴW5VۋB10ԓNĠ益ap΅0){go& 1{R$l?΃ =0n-:&6a˜j7eo"F+ L?`\L|$wu|nĐ35,#7"YɄV>qϟLG>?L7h S~S }PC_;`s\;/Xda3Z>pk0߮zNR [$8ha>0*@M-wy _tFrrZm`%y75.uϪ}\GR׮; '^ۀ[ T*\fdjHf Ж@9 QLv*iZ  QЋ$l6<}:gb#;]+}Ŵ^Kܷ7IIpϸ pneB& ^Z7ͣέ U 2ۀDWV0Hg$p+;7 Aia!U7.4B(HJ0w}^_p]YyGGʏ)рEO>ⅣʿXmړkٙb$y*(wp9$^MєW:t^y[Ş7C?B^g6T.S㱿M? R<{92f?3ӹL%pi x!b!pmӏ|ȕ4YiU^R^l))}$8n~~=6iLf"˱xUQ~})O:1]x n_mMn<~5_< .LJm6"rBt8J' V(@$^{o"wc!Х/Bh{ hcZ!'X1O걏`6jkhpg&_&VZ(7x =4$*j%f; :fp3u3vvoX S{]A fHfSEk||*  掰rl܄V Iyn]Ki&)EN!ma0\:Bj|Q*T>V^$lX2(:W^٬ %!~偔rL>Ű~[oKr9䁺7*RKlƼq6˳Cb hv9=-yRy @?9.R՚rg40EiO$^mOlGFVWSž: ݙwH@ u]bCj7R"+{Z{p~7AOks/Eey'c~XuF*3z\DݿAsj)mR^clADC*QBeB)8w~jg=R,U')rEt7x&Bʸ ^՞󫊠R~Mk^0\ÓhG\+ DHd\a]DlZ7a=N+fj5p ~w'F dIb1HnRvP/F7ވ޾|`ѝfʪ%c_,1)QGjAOVݸGDI}+PzUY&;w2m2ݦEu)'T֕ʛ2|hD!VCEĝ?5RN;zhg;@TC{b dqOI7&IFآU9`Fw)2$8F{p)5JDƌ=!%4XXm_3R`rHAkZ{ Duܸeu6Rp\,@:בּ}|,ɕ b,LR2E~`BmYD^޾qTe󇱺I=5.\EjvR[:!`76l;b)}THRaGM/*M!UArٟJn~=-F:]ϵZ09VX)K?"c o(v=MA55ݛ?厅m`1*Xaqk?)pqwVIM[%1#懐wܱSuK EҤG [M:~r0Ps-1ZUyh.ܥכz9;2:˹S56^rQwebfנW|y^[MC3||VA#բH'cv: 5-$z|Q~ST{Wei!DjM.9CJM~u-䆽vc0)v #*~/Ȏ&+(L-b2TOL+[|t!kY D@/Ry`y7. Z("$"rUsi+a:_cQɬG{\u\ڻj{$tsVhQ̹G +g4MGYe/@cˑ#` k[{5]H=媆iJ&W MW-h!5s64aIcw溘/_iGTw[NZul^Wh~:A>(&-9=lq<6Qրy{[Bp .G ;+ƶSpNRzBX۶'CpvjM4>x}(,|!Lp)t͢-@hE"/- ' R36-ˆI\#0iھ;G 4(mmPKЧ u(:1l&f\7 [@=2u> $ GNPb-v reF캫j1ɑN?d- }2835B`v3Rah ռr_(>%}g˩Ayat*&+q]}n %=k5֫&cfg%qvon* TxtďToF݆Rn4&n>4ԗj2jݙ,cMNũCXRXZ嚶>¹k!DM<^BPQCqք*>Y傮>K\ N0:V 5C'Ɋ* 7ŋϋ(iu;;^UvL?mc{HRX6 KotɋiCC_8a|.R6!A J Xf9MؓII PWQw7i=:͝8aP5D"?5:^hV:95{!ǣGvx@V&)FN /yeHVC;,$tIKXeI&T9Kt`e }"2y-^Dӄo5z^{t27k"?}b"RNVu8M[6`VP.y#u8Z(#j:ϪHRojf/dSm h9 &ca{g:w%a'zĝ^>EEUglXh@s)%xVFƪV8cɶt4ے܃@ٸ:H__Sn.ONhި@ox!Qr#؋u jS7{C>0\!\oRW/N{翀^?ցV{4AؔӒ%e_8/P&ɹ ↑kռlVv;V Gԛ8)8!MeHΈK0Yܚ],W4.^?{ 0ߟ !ڵ.ݞH J?PgM(܍Eÿ8go ng/vkQ9?@(GvV,_|$w4p-iS`q|\cfat@p,ebďM fr1_G4Q}iH `V~YVKkƂ9vJdɫ0}V "/J47`+@葀wɌxlʣ{~-_&/zK J0dLcMJD!}-1ί;H+Bۋa4y|yck6BY l˗y\ %UN{.{L*ߢ3@GQoL釳208v h9JwZ>)Mam/9T$_pԋ;^fn; M/cy%[u]מz[S)_3ƺ-'T*dTPU(K.e9ZRjrѨ~XpT1?-ٍ^ET$3;# dPI=[^2 O~:b֜*GI|#?5rؔ*}D^[G'7m܆+(VfdsPJgp ?wH),`sCjp=6Iahul1i̛] +aC65_Y <̏]QEhB 3@UZa2Mf}[:b YAd,H.ʦaTY AǴR|;Fh1L_iq+)%w%8iqgwkZȟ qbed(=WU5;áP:眳*ILS?aR3j8̻s9T2n@>֥VvjkF"ʽl2ƞ21<.7Qz uUjeHbdU#7='yD &v >3q-+^ AHw',l[$rG G}%9nX5;[XF3u/]#KI'  ˅y6ч:Zڷ"}NTZW)ֹ$g Zl~'~5m!ͭYW"ml/p`p J-һU)- }ۥ$+qhi26|3+v}lAY'k@cZtRu>%@;4ӝ {$Խe;WY"( ;Ă!xтdq 7vV^VNʍ?bNĢeXF3 +q*v?@_AOCF L/N q61Rʃ^E1,bjhƋ7D&P#U1Hd6x I-68_;okS7 ͕@/_WK2Sa0c䒕0YYsk!L\Y~n% gCIy7np0S1,R$i!cLF|kO9=IWqiTKK=:$i7tf:%O}`6'7ĸ(؇İ2 &LYϣç-%q:خpT!~ƒ_Jrk8/td~a'oLǘ֠ ޠ,䇴Q lEO r9֦m{d6c +7,Fwu 4]"HZDH|Q>:rN*u3AMTZLeBpZ1d#PK5]{\e|BG>+7C*2& ?E D,p]/GAT8+d?bHKu*vC[S֜@ZSZT[M+Z@G;d('Z @GxL" ([lYDiIesYv"fcgN4 FoOVDA RX?ng<g2% ^)>BKw`R+ע}CR|{uiK]< J ?LPPra3LĦ@vOI &r ;Qn3{~NZNZ16ֺ3]O,H͎"Lgi\qT5!k^ ᡡH=>V baD102: 2XBY/`}`PCR[䇷֌K]t=؋jM)j;^S..lM[u>IΎKo=L,[OߍÜ0#hY&c˗ފ yDb%j"^-Ql˔KXyjaߩ/{M;.doiʰ9T/zQG]4e:})ojq=LA&8PkjRg:KQQ/G;1̔q n!vJ[`nbl&1gђ#J]R?opL%]͝(ȷpϑLتFSt搘՜4Ƴ-iO:Մ#4Si +^9{OZ3jAдR̴x*Hb˛+kVZ)@8mbV{u3Wߞ^s-P53e4.6( փ EJ hfc p |aKߚxvokP8Hw0kAhC&!QJSN#цwrEqgx@f%*#柍 _ i"5,\d^0z$\ (_17"m5J(M[v% *Dw:9ĵyA}}]II[蛙@c!n"E}p>?Q!elW⇵ƿÜ79l=X<ʌ ,2 GWU{j &S c[iˆzd*yM-kߑ٘,e7KVaQ&GIuw9ByBT@ kf9rJ=3<:HFF٩PR͠f5ēݧC[{A.R. < 95&R~k-,3؎42dd-}x6GvoOãoݴE ;;jo>Ù1NS21klQ pu(9|$AgAּj& !jY$mb:(G@1@b! dz%N{؉ ZubЕ7aKTk905_q5,zҖ5Ն4X2Ai)Iג9Qi15;ʃ1.4\ IK鋐bnɆ⪎el,)lF; 88كیVp4GY"RR.S"O8 ..G5@ϢFԥ"EHX͙3`sox )GBXt['Η{7\VLq ?=JT㬯cha/fiJG%t޲2_JqA%'G % N DiJs:)"Q(n끙;niYƁd2v! Q8G- .vm$ -D PBGb%U3wG`.̱nʣ/mWÅvf}lb׍$GA7x,)F=*I4 o%iS;O\-Ys)ASQRK3=fim]JJ_ [(=% !uH(LJP2-n`͗9a:6u bdOlE#~dȐPР!ĻNcR4ɫZj9{ i!Ԓym?M WbdQb -RN`\ );uh^@:fVyKIT(99xEP?\DLJgOP zJ~^/\etyˈ$HFRrhZ9=TzEhreWJeyyY7-1/!]\Ι'OS:g ͏?LIxY &PAQ%xrٌa`(O?zE(~4u[ߋ / oӘ/"_JB{I \T8~Y0ٝ/4NDlhrDf|ѻ onȴ'V}I(6: ubҮ2P\qɒS ݍTnCM#~] (A>v <vu>$x)gU^s6\&r/̨ZΟJ|ߋqC[|Xk &WmΏ]$ \g WrĠ.:p;^82#.ƽ"Ȇr>QHSXZ紞o^p;8$ RxRf:SH4jí); u#`+; QYGF? DJ QV**뎡`UaeW%{J)Xn@~C0 )ǻ.#*v"/#K-ϐ@fg.Uoda A'Ző OkuH\dh΍MwtZZjDhT 3zUލނC,zgl`B$MO}:W{0:f8V1Ro%u= "7Afovf5r]:JϩՂ-Pxmgf˩@9Yȭv[`<t-o˻#%܃Yl[t֚%`؃˫[L i=(Bks}rNI.Tm |cQ`l8x2_mo1-%t5q(mQENr~Fh{Տ]XPuȜY~LKxIM䶢 tp+ xpAx!e_! P"8fQYG"؞!ǝPTOEkirH<_nR;#R_ k؊^ j.Po;ZyO&50ig嘙 } (M%oɭe$a_6d@uU8=Mt]H N[=w)r],f ]B_*[c0Q3rE -]ycAԚ:u6e$\#4o!8*`/.ͫOGrLgH]hSg7P6Uߘh+nL)1+X 8$k:Q'd ,%ݱ{^@ͨː0u^2N?oXtan>{lM//zPYhlgJ I,Q eP̗K@({MZC-{Me!C+AoTY~51Ǿ(#x Z `"L[!̞,򲳞S*yq*'ʖ|,R6PO[ B3wx,ߑk<ӬS&<_բXtШ/7X*#-vAowtKO;\D.X̮J--t#o0DoJY15J4JCFw"q$L+A$zRS?9Fn6}L3 uge ) `E*Б,sf{N(x$j(kBxzS0Կ,*ʧbsoԨ/E-x00j?rcWF^Yڟڟ&Z)  Z K!\ `TvoAj> TLuh3u[@r{VY$*OT1#}(C4/~<%V๝B Σzuj]d kIlhVˑH{q8? Q? 쓮7qVżr !w2$" ]Ku+u)Ё}|~5k&UC_r-2E[OOw#W|`zRl6G6y83[Z\rͥՃ]MXfa$6{II}R@(1~Ι3"LRZb; qC;a=܈C=(TD-5 DP+\T80w^P)3\_,wcfoI8B^綬ƙbOY !|,Ovo&yt|=#) L֘IkLF|jn6Ón@e/u8g8;v^:C\kt/ D %1U} @ysB͍"ʁ%^*pǽv[E{,a`kwU[6HY?o#:O`H'^SeoSP M!;L>XD*[TD5khSKs"--l^)cW-ݻ3{ x:?͠,NonK{d!~E`ɷ!v/1lz=[xsHoao;)Z 1 EwUYlTTNIђ^Lt:ORS)RH?Oe?f-R $mҊ/'^z/~A"t򥍧uIE`b֖퇾2|J)*In`:‰^ 92dk4&MդO NQnҡ APiqD)a.S*tBR ~n%Of]}Kb i6]ytfp%(nZ7 14pdM#wYVȘd|bFFž:%:# psbR-[jCcog ,/u FSފ!/kwRE,xŽ#dc#dY!;\.e9NyCrJ!qH+= OL2zX>>f/.L7hZ*p,%~JxK,;kHJژI3 º`d'ۡI=F}kw)~9BGgEsT=GCE/ʛԏjOM* qw|m~'Mh4` "J9(2˵uԄ'0dz`4アȱ]5!bŠ!oNX5( uWRFTN7(3n<2z(J^ #3uprAaңO/fdzX+9w8L@"ue}#~9ĤO.1f]U+ EkOEؙzqQϤzd"vA14Ĥ >efzW9l&Çں[h-0 6 w?iY);oy0ܦ7oJ(e:I~j()@z30O"z\~!2Tǻkv)5sST Ӄ=F hƨz2drvL-z]Ce~20SU|."P,JA9SVQά ;\ִ)eP7kJ8 #aU/q[w-ȣct/w FcϚ@Ǒf'+G,?l_\d~O6QJSޝ %"RS4AFy,7{uxD;ҧ%2] hZu+r_:y,{M9 'F./j \ 4I)`Ҥc{?nwaV+m`iTC;("i=lٞ ~ Nch&?Ŀy}{kBL彲``5 NH?wj%i z-yif)~@th|#[<߅ E)LZZX[jN){]qs)Ks+ y'~RҵvI렾'`5pS{Ntٳ@w~l^u##Zr{~8_pzC3w|┺Qk>M3mT 0j5"nFB ^<n,7i/!;%n qxF۩oϲ&5@؛~ #X#ԲSSW#Cr j1H06E!s Е$!9*A6S0#Rt5O𱿏e!;c#z Gbў5Yi0Fif E:dFgʍ_ò}*vՂk"wpM/>xXm 9om%!2y끍ZYpbr-իiM+<|cMBWw3skLI,G<=QIXQNb7i $ۇoD.qKS)q]dw-(jELjw`ぷ`GcLIq6E"7j F5݊_֑[/Ȏ5u-|o6# ,\|Y(w*k\!<:dx D~`q #*'WdHY_4G(*jbZ|Y=^ٔ?Ez1p973skImHzK[\A%c,eM<8d(qhIԲ E%<]fB9"#U2!tPkySVHmJts0=ẈP>w9!r+5иQk~(S+}?P>nYkţ;3ԙpgپ~if<7 iW":CʜDj[M1"124^ʋE%r |Gd.*P>ߨ!\(V]_I 9 !V i<}q11:fS:녟ؕ=n{/K R~_n{7.;4\K ιqqP"+Y>C7nP ńl+QrfSLf5#ZP$k=~vIT ]8 lecG(섃-4 $[ .hFcQA9q7UnQ-OռzW_Re&T/3+zuTW%#1uvT%W`yLJ=cn5%7yf PR{҆aKci ӍKWT1i2m'e{]W5@ۘ ,(IPF/W79srxA! wH6  hh%ٹ!o|gv NNLM1l{b;,Sr1v2zE8lEbse[P0 ;սDiukxQO{B'&|ǩmą9B"w40Rms~ܷWvݣ‚|Q"Q/:p%'SV cQ:kһFIk CZKGPP^M@EtTTĵ o*OĆGd Q$q3^z=DvC_pXR{3wkp}y=4ܬZ[nyPZvfOb|\zBg^u")-&$B:QY{?[6$!,lDC`zUHM/ABBѷvI3EFކCe*hBf?Vqp'nMq)ܯC6RJ5G @/I v+&:sk]V) id M2H^R'ShZ;\#hWzXe_C Yiu%"HB n0Fex8}KZsk MbS9 $ޣ\׶ւ)d )wba7` uVmHeʬQiX˛||Gڽ=Or<69m=CGky䦊# xh\,%FdTW&d[g6SĜS@riuٻCEcRrRWةP6|>0J{Ja63Bv2g$(~'(f%5\2"xAjCETw46E2g,e-w,Txhwޖ [ja6vz L5+LZUX'mMrG4A__#Ƽp);@ՒQA\E+:o_[r>K-v(Q!V&{eit€ZanTic#.' o (\*o%$ٳ@jh{)H?9$Z)iR #҉H϶U=^3jtM#gtubd-(9y-x8 <íi &ͥdS}E­hGb$par=(ڕ[9_mWp]`|HMï}i=t!yU?XxA״(VjۣdYz6iڐZ (ƻ-A(tqS&|y[ zݦWzUr=)^>J2`3Q$ -ݼ9 T)Vk2qg rbƋգSbu #k.4Fvlݥkz=%[>;,\.U9-c#u9.y~ZNr>ey{ڰ<== ~p)Fc1R.3vwL=Sz=耏"!Ș❌')I:uP2fKV"w| .Ϙ|NvDri:@Zj`9DZҸytueRsG acZmS*O. <q" #Dv6ȅ8 5wOd񘩴u\'>b#–؁ơcg$X$L| -@SfΡNG\>Euӂ1!ݩ[m_a1TjVۨT_m2Z"ҹ L:.Sϵ~`X"~"OD tł{FOU#&mWF afn^w)%#@o_P6 P@>vֵM%l<ҸMS hq+_UvIH)CԣٔIi|]2]N7B=wߗfZ2gKJK+j!K ʇ"qVBI6kS`bQ\ာiczX<9 YObUkz G=.sHɃo? F\ ymeEVSv V "F`'k_u@>h5vG]=SoTnn؝AB>荨Si\?o:4&:poVu5#}]B-D 5O6L_ahu^60~Bxkf震~{bVzuOw%*VB {11B9< 'W>H !wJ9]MY>GnRcu DGZL(Зu횚 ڎ8I,% -])Cy2ĩ>&"&Y}^ݡli>9d烖p"FCLVo'<u=RrhPb89 Aw(ec=GoQj_2)iBJ<c hٴzwu8'ò+ _:kn:>_ä灓{g"PQVxHn1UOըXuC`Oo# XOA-KpE&=qR\+wCa=z0baF]Ԟa{t:y&䈹9~!bD5n04G$񛰽{yeژ97T6eΩV_D7FIIl^$h SYLԹ?<8A "ڻu*ul;#rj6SALmړ=2b0UQyyQhpI5w`DN(}]K(xuyUFhr^ʇ}\=ZXmVL'@wcD-)K2-\Kg"F㩊I_59?\}X` l>90#BOQ'Yp86њ'٬ˈ'SFzKE-Tq5"Ԕ묑@qm~ ǢhOV;mf0ڲ>u(oY7CMMC=pt/Y ߱V-`PdBأ/Q {؄` m<+k7+gQ->dB*/оEYcA76X-}0(q7tQU6H<$XwZC1t "~nf>CPˤv桘l7gR)4w\PsX#A`א_!| ˙G{WK}IZ*00x~ǓGrH{$-պ<30؉BJccq  cbe$Gu#ƻqyzwƌZCt4LC/y-J.PQg۲I%EC<4n8 YK p"j\"}-iUw8՜T2 /^~V)1O8HIC5b X Wզ3t7 dL9 k2fZx;\n6CGgԏ#',g Ur)kV#725D32V) ;.YR}r<82̥_y.nOv3;^BA,&DNY rDo^hB >^C\WM05Xk:=*TnROx!J؝;V4s=f>!/}]_)*گ N-l,zM%S(}OiX0'C&Kw+5EЉT]~a븝Ohw}' 2*J[&U׬wS%_s4t !'u\;* mGD ˇ_u"c+>SMe%@/Qlq\0JZgħLϦ$6an\3 \h8d^B9ǰSsoFgnKtΗu_2&1Bz:T"_)c~f\hLՁUνGC0դ59ȠA]ʼ$?FxWG=ub,Od[+n:])kSPR97fy0&.{gZ9+|o/+ &Af9[n7^xy-7w߬a:,?_^0YqKL,$2. ˡwhl9tp|F2}.>)5ԓ.V=x6ӝ<&](_wsPC1c}iaֽ_Ł +N VrLAj L&ݺ7mQCY#5pGQ/d,1sPRQ(fv)n-7q=GS5N4( Ḡ't- 48Y9ٱLdG xx-%PIlcmIˠs>A<<o uڗnS"F+_=!cp9ëS%hcjQVą$zOr>F6cQ{%u-MjSMz \m&{é8cdKMQ$8T-R0^ZvA.]Yz^5߸6%zb2m hDk^);rϨM SL#WZ8sG6)C21տK>Z.a{_I-&p60U $*4NOLц0dza`DM'S!2m3f[MAFE #)a& [gak!{CL!D=?h/6M0I#{g1-WZ@ZTmIа::0 Pʈ0g 47%M=K0TR@En"NXY'`m9gR _P{9NsUT1  J-5 hslMj^$< QAgRr#_% f= KBĩ !X๯o`gv+wIѵe!''19KQZkRD:tK(;Vf^hVW՜yLEs&VlpX[MVyL|V_Wk)\U m~҅p)Ly9j⍛Ն- 2Gƺqa&Κr:sGpƁ &YoDJ _C5P^|t(Da(gNiLsx3S1N;:SdoR6r)d.lN2a7:7E|`7 &.։Z]HsiQalQPBw@ͅT`'ܤMf`ڇ;x t=T|[..!4ڗt#=gfX|.ANu# aպkj0VmYPJ z!C"xY L6?Rh%La&ߧ$uoBLB$ǰ9yP=ՙfOՖӞ.A uޘ"wT yS _j=> J}~Kɏ3ԳBU_r\ ] ٔĥ'ghVǬfuF¤GZṄ#+ch1@bEV&`8st #V\W7-H6kbðIx#gD$zG0WY f#vۘEwDԳrV?mgp hpLhsx'H uZy2I ʪ,$=oQ1%a+Xѝ;4dGdeTb} wwZ7j< yarόNVmZDZMӞxqm7&ۇ}a~4@Uj[> ^3^ayAzKQvaGR;3 \ f<@SFӳls>mf} WN1xRM2de7%ayHa@ik4QXQHi ֨ef+ӽ(R }+* 6juߛ&F]E2t X/X`7eZ o4kx[ a̸ aʹ\H\Vl[g0v l į`L-)aR˧0?%4s΂qBhI>(90ҟjB?'Dƨiµk3eob8IY-h FIfzXyH/pF*Jbi; 댪-jU`'j*zxJ4r Jw+iE%%rcA`O}BGš#X&\gE#j?dHسA*4 27'(GT#]Dvad &x~gDF!V/v[{n։<Џ'}lΎU3^͹6Wa!FtqŞĔaEDd9jwϏ| }*T WQI}vMO/L"nC~YtF' MMMt`7= ?kGvG~-9AjleŶ]O9SISU4^N>iCߜ:oM7[gx9s,M`%x_ߋQIQmʥm}Qvڗ(?ϑmG\3D*k|2q'iL]VacPo^v΁M= wCt@C/s Bq~ǘfm#=dkEɶ}&x'6?K(6Ygם ^b"@S6 qQTl% f p~4D@ed#MuG+oϐ; >uBWRt cE৲ybҹr!ThB&8cw#gYZsymfc-1.6.0/tests/dfset_NaCl_222_rd.xz000066400000000000000000001224041511276756400174610ustar00rootroot000000000000007zXZִF!t/9] ^DJ0r^$[>1jsEPΙH&-E; P6.Y)?e8r˫P.Y;Tj) EV}]N p*rr{BVN/[# AwZ ;>y֍2"g(AM$0-5z 2L`>!vy5]>K斀ؘx 9^Hp]Ͳ>ٛަpESMV{o,qI Q&5ܴ| }"7H[OBSj^a*ɬmSuy>ڴ)ܳx:L )i# ;f$\eq&y#Go<@B!Is"c}.<߂GOjML5ѻzh[8Xw H"Ľ!e\.k%|3nJ{R,7}>"^ 5nSReÒuY ﯯRJW_P1P.M 05ޞ:ñ8hRHVakd (_VC`Tr'Yo CSiًW^'|"B% LKH%/)MVDMt7_Q=Ss9Mr4(Xɚq~2:^Bg!7w+(3si,9{³V*Qp-x R7O=y wm[?WMvA gʉ>~KаUfFhgYN% j2Mn p 4Opu4 n~1؇?.f/2 ^߶I¤u_"O!B52EWepfγNgiP :!Z9Ll!B̲ͩ'F1Pae ҙn_=K-pH1FX2 JiZc|J[ۮ^!u 8ԔIx%i=?}KfOtݙܡΨB{\;Z}-'?BgO~P;ŦN{#H4#%N'$EɚӸc $mM5u1 vHx #[ǒslij_^$,^كE/U@e&ɓx82➌Y#V4˟II܋HB{!0#m,%WfdtǴ%`es'dىtiGb>䮟Bxiʛ؞$ީvJp]i] GcykrXKa&sB=V0b]QϻN5P0Kl)JFq1!1'}Q^z˫ A4抒DXUf䷙nB^JJ㔔"wMQ -=Y ^7Q'<)b 7sE$4-]|s,i y3ĺw| ͣ1Q?j5 ,Ġy20q.s/- #y^ +ʒFT5I;Ÿ&+`}y.\!R`Ց6=h,B|>o}6,\؈# BMzy2 yd_**Fl|H>`Gqn'FztM?G[|gܳa}Ræ6}HES)Q=y++5Tf6!)#eMZ B:19Djq̜ѫ-~6m{,i8s1+LTAquM~2gV)Nјhv"# IIo:KWv1c8%γf0"3[[5@eZV nԲnf+h`_A./9Z4-S53O3xiH7Ɓ-3XexAsHv(wJy>iPsWZqY8X`b=?AMB js}|ü?f@_c3*5l6pM}BtT#}m6NWHQ,Kyk]L_D2rLi%̈ GCa VVp߉! ~KXuŵ~yRjT\h$m1PHWw$Vn⥮?~ɤ6deR& VUBdo݉z u BN, p۫.͖}waʒ-o1@K $cSX8k#9?{ECr,*"8(,UNL$;= g ( Uޛ˙ķ C,,nӔYuoǘH˩+^rMܬ uz޽2q@qâؽ߲1zw1O.>4joӳr0hLԞ+ZP^mOCq:)^ p 6jD0BvD昂[mGT.㕐o5\(XqiΨnd )87tj%FEL ì=Y6*]OzLO |PL/$٬ph_jUɽrzH6 D8$\p} tcH8LD5 r?} ,Ȋ<:H$Ȇ`݅W@jN)C&X:cƯu Ӝʶ[$LX cZۻ :Wt:" ,byaÖ8$.YL,$2RMd*N{f_~(1v8cG/cb_`KrN(/_)̫.ԭV\;$l`0_}'zt=M֔h<|Rܳm&<-k6ve݁bbӕ]$ 0d' FuOMʟ=f,ox$w69Ly3[l$k.)Aޘ-hi@(8y2 i'8wNLM7%`T *ue&~_⋶{#`vIp~3CH[a1f>9 g#q~0КQ%gRZ [l=K`R%P-,l=eCWl❾I-]&k&sG/󈗩;ZW iw%Gg1MWC9:a-E^an| 8fǐYvLJG1quuR :B$V FͦV?$0kp n؃%=gaP9O|l;Pydvt>#Mc`$(Y:Vw_8xFy<7GNWFZ6G3) P{gAHT"sai -GTeI3 &Ic[H?rGТ_JR&Ģ7:E&tЬ8_hոU/ψ%A E7X|urRtB X&`s˅;wד_U t]h12-r#NaK:C.4֢o(Dn 9H8)wAVH"e9jI pdR_]n6/S)~»2(C74 k"lp^Ϝhmu;sK[w^/*N#ym?k]Q6\<]%p02['l|ܛh1>x|V]1 P`uirl)ϡb5Opoޯj==0^Xsst xu`.F˪З/[&LI@cyMtj8vaWʆ QHUdS^UKҗY2]!0k9/IF :rθg^u`ԆקNe\Žr{;Dji6(y;3s2p%^i=4JY=/ОuW< yIW^"ĬnHrj&:A)4g,aZ盜x\BQl E.&ωK(1RƻN.WZ WE ) ڷWgH& Ox!n;iJw]dgKf%̓JENRnn (*9jF$4^gcX#n xj]R=>%0%) %FB[=p̤ pm x$Ny'Y| KY195WL_69aĴ!Cʹ]!VNkM7?<'ƅbzWGFJ>lz-0sVz<8ZXFI gabqJbH}ԫ.6(q&A (;}'!ab6۶ʇS{'~[D(̗z^s1-"?2D`!,G Q 휒M9(D1}U30lZ HJ@iu:>l曃VfKJoTR g۶؇<@BlD ~2e\6~^"D32,bڼoevݧWU.Y~{Jt+nW)j ;ԝ*o5 r3(Ie0C ǘ1~ OXLȳoD k?;N ij+]C[u4STl~V *SBvMFEih% vG# ,5jJ6٬?|sAl'`Z>9yv&u?r}3hjjW&RO=qSZ}O@*P x7&\/9OɷvnB88dPC>̎&G2]HW\겄?ޔ3ed\zPsV7"1/ȪrT7<Q'A\$Hv >gR65H W/Lo 0X@r8Az<Ѐtg;~~NWb(=+ƘK{7>D N| 3z//6t~jDiNz~ N*Cex.v_duQ$_͛嚒[!nw4yX1eEi-e%}$;1wnoifLga 'A՚TXQfL0myY"L:[_q?hssOQJVqkHM׮9Hz;IxסE1G?y;AʾNiRNߑߛ*h3l{K<4;&QFe. _;D(Ԉ(iKVEO 'Rr{PsNGW?TlKlԌ8>L(Ѭ+˦=/򻘿'h7EM< zǻ@*CfF9k]0Z@CIZ+`لV5>>f=m#bf+}I{+]$$q~@J_'L22kڬtsv"N "P O\V *Ytw|2-gF{]u~UOLTpv{$!.Ppe k:T7~$>w/+5jO!U66j8K_@84AdɞNhi:T$5sYgwHؚ S}ݷR_pb2Xj8&!촾gLcʡx8՝Q|ͽ^d~ـUV uCt%Nj\2tDwXeɡϘqAsqٺhm `HqRJzqatd"k K!4q#霪> e燒H?N7#'@%YǤ-ǘ|,3R)Zxpk61>J*{Kf$;lYoz~Þ69aݒ0#{y5CMf:aKm.R"Vp,XgDԈz`K".LNVn*vѯkiIdZZ0n4xM1sˀ|!5"pDǔdtSnL*+ iH#޿.3DM^^W V~Hyh:G]E{\, nkvv'bd1k0]35[wK1OeKla/kz7DJ Uܙ\ fj:gZq!Y`2ucT;/-pُ v_>X\8E`;mYҨt.تSb,h2y!h }$EIJ߬WOzr"q~/q;˻bcǧvry(㛽WjȰjr:!0 Zk.hG%+}o1YT Mm,22a_ ! Ї&fޞZvpצlupެ9GAʰn mGe@*G $5*/7Np{ooyN7,h,ϖIX|9rP[fi"ondz(mR(T8 JVC"F{i{gL7)n5c+]:OD+4U=3sOҲ~9nIi¢U/_}<31)Ho;bN*p^ ༥ 4dl瑠aTM6X.΅*^24>ID y{ס2jtX=-ǬKO3Me'S'Aw_0cȟˉ zֿGwl]6BFAsh~ZP*L1|ZxԆweW׏M/yMZ XNS/9:5Mǰp*<IQE%\uV$u)$wR921`sXEro4qI 9f$?և2i w݊BMFGxb0-(SO_Y p) Ֆxy=5 ,Dߨ"|%??Pq+oXtk۫p\iv rax)2ѾviDlK;uϝ,vkJNkcv}[aP>(zmzs!"985zo̘s2***\^ecx/0g hD0wx3rg$v~a(Avtk3urq'(ՁiQ_IXj|~k;bID5$_9;êH4l\\9%Yb6Cr+ɾqJ޼ 1yϤd^GӬ:OP?B ^1rx(C p򉕞g K8^kwSGP[EqfAQEVG5WI.P#t> +n?4"yDl6rhQ# kY1 oJ;kMٟy,wRNO8wƳDUrDzho*a²ZZ9>MIۻo2w<۪p0S ]~ ߕ}H$ܲk~ISm [t 2j#G0^j&{45=}ϲ6?[Nu忈:)Bb7VѣoC+̵9`ʼni;tVZ-bhqI^tK\H O=Kw:9ˣe1]ϓG'2E F aWE-{S~%tvhσ0t4|?;j-8[bW ю I9s\;q4~`S?7-XbQMeJ;4FCǦ;I/B|!l2WHe Y[biU̅bs6zZNZEd_6Jkz9FW-+qP+%=yVCOZ:Df1F0\UFsa%x!2Ui,nzt#m?qv0H]Rz~o"fhP8?:nI3Fe=i-[nuER7|/7k:U32+SvrOgNslOS 'l)/"p5vٲU/d< ߼!l=.W8!ߌR s@B1]J ^:Nhj kqD.|Tv hY@86݂N>G*NP/%:mLQsY:pl#OtPA{PƴH wte=h8s# OCu9ћ˒XL`^jWQSF\>5xS3 Ⱦ>pI`eiAX/ >#Ԓ6Qi!x Uf)%^9_EIyY_H~t*@VmS;?2C!Rԡ'Uн0O/ϭ.)^x|3Y$4{?#n"O կ]w |GCo|%ܧ8 dŰYl{m{-1ӫ7jFMyLklVkeث1-Y,jd;3Etg4ï=vgF;XCba\VGpG|y+ǣu4v[^aOכKpTw~ĕCI_Om%jlutI-GO>Z)ʽ"k3FΠdˬ:$qW(yQ>5\()| Բ䃴C_q. 2y8o"L9U,@)"8 zonRBRxI뗏idTWku.5e/3|JQ^17;ۧ6Ct֠ظp+4n!:Ӷ^6<Ǩ> KCȸcFw>'@ϵU!k"3 q |VUK ϩ;@|@%(|`ʑtZH͝'^ WK63)X@b!O8mlhT0JU{FC*ؔO-_jBA(|G;2ܵ8u65L= T ё'*"-P/Z:*r;Zk.KAmĕܿ_@}o}GTYa}Wbr8D9 CQKӡjrd .on4uh/GX дǨikZi)ۼ+<=6E,Uxȭ4.RJ F>6LߒGFPv]z,/}*bUIm-*]ch/Ñ+Uu8j2< E{ҷ~N5MAmcJ)g< І˂ZmJ{ݫ**,Oieͯ.yAgowL"]@5]AWgۀ~{AD?*A-lܡ {bEHU>Aa =Ki<"c:19zpAVsThGhi0N%0$ gX$\m?Ͱdb8"suπCjAUXLd%c_K%p&lPK%$4߷ oXJ߸9o{ Cp9ODL6:k"*q:µjnm{)p{J/H; w,[q:mOHׂ_ ")L VS>7v^SS#B@-Hźj eؤ!ZA7(9S=&κj|ԧuԞk2+BȄcX.A )xR ›Ґڵ + v=!9]YWA0Ky7;l:JM/]Qpba#FyxjӠmPcRގ9 =I mg-'X1d(,XGa191=h]5qsP/a5hZMA89SC+җ!2jjdbZ@$؇X4KsU48Ud_5۸Ww.3sZPP@D6nYg6y\\|.B<ϧlq] _Wz||Nq(!bV˿$c2W#{092/i+$| {zYk|` c1jgVtjwPCpPLV$L rBe~ҴJPJݹ-d'\Tyʶ rD-a^5"0BoRƻS<&+O;/" \\HA)%wx6w8j}c.sT0q s,a0{vn$饅jMMfVuvnz!#,%@ B*vU$ $fY尭uvR]~o^ӀuyT tд5o6RGRl,B>Yu?{ >27>bz?$Ι=uF  $rMTQ˪caXޓBc$A>,:lwYȬw#ʎ˨ۙx~ xtI@{ze DZ;fwF] M/1\3-fGg K^'Dhl!Jhue+ݟko.N˒K|rG{Cg""ߴ9h( mQ24'9D2f^:| rܑ"}q#RDjV{ ^VbٝU"D ZqI[]Z\kQF2,l4'n9eT2#'}!IwPf*FnGoQ*TfsH #23gV_fe1`+;[nO8#{5V/: <2Ծ#~xE_g\9O\?ĢYdB7q#59&McQ”rKѢi%<3^)/ALn^j4a]gF5d8`m(RdC=$}ަ,@k!]S m7L&3 mcs?is*&ifj0ɍ>5Xz*F-ed"U\G dLڸ]^\ v䩤z˖ ?0_LIU`"^6XwJqll">\*Wqw_d'sWKCޟ<'$]hB _h ky7 (Q&lB;ڮ(ؑ  & Qt.yA[u_'/,O110raU,ԗ2ǨGR;>^;!AQxnl@Y=pۙaYF?Sk]h!i$Dq_Gk-T={l47Va!u z{;YMx,mk (5r,9z &O7ǓFyw aw8mͼ7|VpʝZ ?d`Q\SXg∦ Hy*3FZpV(DsU]7(bEvU5fsE(Ѽ)p".A=-4G=o8"UEWcKG)X-/"Z͙DL)Qe%KY]B^i3qذ5h *XhP\XS%$ѹjv +L.M~}+ധ`+|^L3#`6 PV5}:3+{ ,l!q-f_Pg hЯN# y,[ CȄf[t@;͹_QwU߲*ihhx!^t drGw[Rj[ teVrgN>jȁ$=0 Mm'e#tnMPEn"$# (z  pOr7]A!鵞A%i8viuYyk#ҟ@pPCd.$)r޾8ɱBUW%2 P ^:TXQ(}ܻ(CY D-bs]Ls\s9,r]sW&(7F,sXh>ʇ&Juy 9e$@f_vF w-DΞ(h>JkNUyޮ@uWl&fy┦ 9k1k9CH&NB% fO5%@>rw%|8}-»qrA'D_DbqȒ)z*Hcziсm[܄WQK;q&6 λ}Q^xK<vJ'dʣp$'ڟyRybW$bb4 "1EAmc}9oCfoek:ڳ_cN„^=6.MEc-CRϜgh eR`L _I;fvůF(صn5uuM_n$Lx* یDj^_@JOщzߘɞ>U쀹F~8"<{)I;w1P"gZErJ|K5㍐d>_kYBZm+Sý=nc:v߶К3͖ -BJZs wͭRROxCŽ(w+usdUDo $]X (Z>UtVXP1yg˲Zh\Xb2l?%9([֞w~c6/L1V%gcKrGiӶƨPϮ0|la#@uCRgmu)%Sw3!0Wlk ^T8VF ]⦺j9&AjmD7"sJ?-!(8(9ve i3ؓ۶h7(g0z$*gN6C83NUs@z ` ~{=,Yd^餪Qa dKGF1F;W1cHet?l8;89Bj1,[-0,|吘3n /c2=:pXEy+%@Vpd~Q!2qisHN61 g&y?M_qAE39I'eT_G;N!xauKxyzDղ37ፉǥyQ9̦=Bf҅ F0ߺ`Ϛ+Ƅ \0%Rph5#3F_F6Vclh0L7G WfCr|2X\K+?ُ}&b26N[})Hr&2g)[LM^ 6L&ׇ,F62Z0}YG}jPU&ca"Jc$J|0ojwN~CV8F>{3Ш z0HMn9CVCP@Zb(Xgd' $ )Eo!j2Hѝ)aX5? ) %cGlGi90>7?;t 7)̴eUaH SңjKo /n)o)y1=|8Ho@]9c<REm x{hNwlHj02cQQWm xs) q Tz=a A`94G&%/xo>ó#/uk!)QBh_H_숍I,7ZNmeIݯ2Jo\"GIyĶ6x0Z9Da E9/(^VZ;X2X mA+/Jv\G;LEUA.SA i~6KI ڨ;+^SUF)5NISf~&,O4SB7Ų+żwTzX"M3n'V"q_I< >Y^u|k_Cǽ-|kUO8Nc+9xTYd_)dz R. ;5]X M8CBh=7r@!~IM*3˴f&Xdqn0FQD\ > Ԑ6AOS DŲ)u^(i}q+YV7Xnzq97䂊)@@bs Uχ]G (s4xR+ *ۜ_$4dg4uzSt6T#T?Oc 'X2ĶkB>G+8(?ZPx-V5_;{|yZ뉳GD d \J*hZI ` g(5Lh./R})*y܃z9b*Qi S2qdZ8;$m#BܺͶYDZk͗o|/1ϓOA"T-%mM; fdɘ"qV _.5բ d6#(ԽٻLʙ=ݡ+>Y⺆k&=*i<,DZpJ Lf727e-pP8c@qLj*5>QТ>٘q05WW/o:Tc^q4s:rkHcXE,gZi8LBܴq&( ɦ_Snd;BD4:PF"o(!T:ƃ *}; Lqb?ĉ6$[QPR?dƗ=헲חn ƐFgcB'!sXBKkʔj잏(P,JCtQ˒mӔ Q Sob{+鋱&abM{ʞy@)ZNUM u}S@I!#͆;$m?́b&H Ќթ3x?Xm!}ϭ1wZ"Ȧlz& >`me1nay 2 hֶ(?ntpOӄy#4{xuC Avnqu߰ry~06z(IRk8!crSXhJZ-k~Si)5(dlKeWٸH&C ewۏd  nG? ] G[SY301#ChX{8])=.YXcJ8ǂQ -SRd?r9CI>;o A};,UN1J@`mw֢etF:#>J10-ſRґ8龭WT%nA M+h]N\DC:Y/LWujvr|F p%22~ ܓf^2pkfzC$c8ч1AZ\Ո%RPLqbfNp!E]Փ% {)} VRr *UhCN"7J~IPiϟړ&8Ҽ*)5􁾡M,bpUPQ88喞f([ex=ŀn*/l *X _i֡x/ҡs7뷊'-or*p=CpQ^c:ImSHÅ(/g su JT#SyW.%CA3& շ;7Z?OHwMjyڛ|!(j+BzЂzmgcwm9bq[GÜ+5ŏy2Ss+cKkOCCK;]/ U"ex}C| LH 9MG椳6sE-|.\C"t^곃D1+.Ԛv^,] v˯!N#iWZ@/}L_$OJN;ڕ䚣/'yD{.@ )fJ]-?}Ȯ(;H0C1B /[ S֜pWϗX7E/gH^]PG#U5h7S?JIN4 (ܦ s+ijSOw?/hbRv,uy-zFQ !;.iE?WOHf/tLJ$yp|-3nӁ􆾑0bVyx0NIdY*B}qbQ1NK1نܻh/SfRYPC{^h4RA /OB.QD_YyNzU:&ђ^S5DG.iax6QjYK;26\/4,>c2 m2AFxF!CáH:|42t7r]fL?UFQ kSaS|,v(KY,lr |]I9^VFDv:R a6ٝ pYgl)?~dQo/C>ψH篇Q$[plElPnZ3zjsȵ 3hV_Gw dZ9q{xHp>|Mߠk+v'Wc, IO2>=/08$!nޡ\&"3MR[SČ8!Y9MCaREWl)D.O@~+ q]dfE ٪*R GBL:6"kjIz f۳@űgy<3-\Eépf>pn_BՒi=s+0zO@O=$֞/ȑb({aB7KzmuZCcWy7=ʢpF!=L b~πXNy wM0GEXHDpWLczP"镨BW;UUk;ʑl=)P/AIz,jBy[`ʾ8`JT Xy!A"KӾv8׫+,-kv&lNhF$c[,PC$L*oӡxݻR)Crֺg@:*=e g`Xճq}çXw ο%>83‰c8@32Ʒ-x;Y³|NgOa(E[ho$>"~<zs#/gLS{r#tqnZ'Nnj~&!G#ˑf~?;LZ*T~9ON~Di=4?%!N<./-aE SR 64 q%LReI ;B}dZ,MtHwroeK_Odh6 \!X#e4H!`VhDSۧ>m1R6 t5vBɋ=bWһm}1 yB6pIPG'ݬG<$F1sрJ:3rFكO.{hu3W3_W^ H4Y`] 4ڋ%$uqE|N_KHL`|! '_|ɗU^WnvB}rCBwAm{}#̌s##J7\  p\1d >qp1SvqcV&Ze ; E6ˣ+0 4Z9SN1Nc2E ~^ xcOh3N1U'u:coCr|OCl)aT\n7&VćLuck͵L>ȮM X/t\F!#N饬B_3@%7A-dr 8J\e9U$~#&G#r7I%d}6"GTM kI|ie~< qQ)>4 +x@(^v( Ed^~2I.x m¾<Z^vq))7,k 7S(afʚl479ȄF r/^GDf v|rUuga֭1z%xh f96I5iܗO|=J9F)b 3892M 2{sw /5,MU "*)w!i˙Ba>Xd 9e*Е>hmEH֒W%o(7R']BݜޔmJ}6zsz=ؙ*vۙ/bQ|mĸV;27\4ξZJ];rev_ Oԩt ~! j@QU\B?N)|1d؉eV8w 2=snYXJq6kHhM؏Y[%ƼtpugD,TJn3Z$eOߢq;d$Z/gr0Qr7K c(,gr"Ƿ3ް3Z7g6E₺JYaU5+h{{ˌH LGiam U4#5qCgWfQ7tI$Cf3veRlf31]FMWbXV6gʉڭL7sW=# Vcy1K4`/_1X${D(g~ 6$VXQ0LA3Q#`=!? (6Ց4ty4J-NDL#>8I?YC8H0>rؔAh#^-A Y0 IrZ=I?f<_Jkޜz4F9cz6_,^gҽzp/geAg(]@ <Ǣ">t sHx &YU8x-<{;_]PVqS` Z=ꕞEc5մMA &tG> fڃ*Oh"ߟ0nr5sIaeшLΦd ]JlDn9^ ~VJ`-XGf*A$ֲw_t[qh@(##~8"%~uޤ2+ܱE>_Ah~ϣomZqF6D!V=BSXf/Lhsm>Uޙ#窐K)0\3?dB ۻp |氧EYlPnq(0nVR0; -#:U>rMMA #,ظGIj1Uc?3pN<[in. YtL q2lY防 ,Vؘ l]Q `e|tʆYp˓*<ϝu(5_j@hٵ+E&7(DcBN ݆h7 {f?pk"9 4t%y5URD]Euse}}u&731T24GϿ%LXqc`la|y]`sH}U,Q9TrkL ODTUxY!L@C||)ƠRH|^ & oUkw;j<ҏ:ohδv3чG"{'Gt9( & >w%#Tߗlt@'염?X!~. `"F)S>vy[BC6a2A;!.9i[!uZ&c&ѐtr{`75AY-k 9+sCj f呓F܏FMˣb m"WKd,\NX+ȷl'@qחga _( r&\ "SlMBX?^L19οr1ztVxuoA|Ikvuc=̣ uf>嘊T^$wYYHvL7DchˤLZAfdF Ҝ!y)ԬxIƔc h]qQ>>)d8s5Z6Ζ:/Rq+ܯLsr\* !dJJy`:9͗N7WPl8FdlC )I4B!7{ #nF%Gn % x~rgIƚjXq R3CcD}O[r尬L!֑q_71xxv1ܱ,RBI44=Bds'׶!%/fcT(01g IV!\z9:q10f̢F;HPK^DžYKxM :|fs#vj| -Wq:}j?$FI ~= m%MC6ԚP3нJSTc8~%SA|4!WVn8e <]Egt=F$^,8*N{ chY?B3},aI5~<8H#RqY+RJp Kh$y &m+E'ғ2 @[#91`dat_ڌk!nРy5>%1H%餌dJ5G}g agnIY)RaOT]CɂڬՑDZX0[=vM QextA [)[$* Em-yP.H"\a35+a`OhMKjva;1k4)i7Na'y̜Ѧ87xbQv΀d*)"?@ r~I; ;,7ۡ,җ6ݤ ZI'ߛIDV7eUm(zon;"/-S،t VӪo䅋PP>2'7 0H5-\:h{4Yhu߉ tK)A|.q!UUE|ڤwp%t%Nd+Im_! <""AzWx^ 0 hX!5FĴMqUۭ+ ni >gʪH~Je4)"mĹ_:r>Ȫ;ȴE!Jpoy~].[AHT[a]3Bi~7Z[YĕUۨVǒTMV"H||g*\!r J8ݎ;S?emBTY$MfϹc6#Vh4W18!꼻6@r OX UPX9I(< '6/ Kk1,qKwW8xAAٶ5Nq#rZ.j\(jc8mm !6[4]QsĎNrf4\Iਂ7DzfpeBAS)u`BwC#"ABםb մ JSm`{%?$H'W fzED XvPH:>j 8leР 5Súٟbխ~j%O۝j1^5D/5N8-xaK6]]Tf+2=&+Z!wZ'u`ޓs|pƳ\-7gA%Vg6wm,QςKZ*I)5@%K%cY=ax0C3yM+.P7{VX>@%w:gQ9's"_@4Zv`6 49P"a-hˇ HyVM |'8P1HYM ]W^Lx_x n:A]Ad{zxqNoJU}Rˀ&$7;<\\b/d5 S:Z=,X;Oѩ VJ {-V^R@]6)p% ~k1>|a-++ (W T3gml5É7 ykο3Ƨ*zi7 ߼q70*]i!Zߔ,}DO]l~$wo;/4EJ\@gNdd?>zo"4Z0`'y0FP].bϜTԎKX k}V(+kC >:ʥe0֕Et 3\Lj*iFIC/C|R-M W Hr\ YXxK两PZuSԞޒ 8WQZ#WG\&>g_U4r+a ac7r/'8697.]w0P?SC FH9/byuV>0"}@ӹtJy7).D9\nP^g&Ԩb"C/*lq.Usᐳn\xj`2?)><{2|%OxDSo`֦!_Q8ĸQ0ݩ]2jAG`yzݤt΢F$vV1z,3I@4sጧrɐw6q ^}­^Wi FT(faEZ_[}x~-#b!_VYPj1*4/b )61x$pb/ᇩne!"Ct BwSyRcQJqz"6w>l楏98霸\J&Jzz VuTeu/XC0n2ܱXP1`@kN/rsw|8|#-P l# -0#u*T]L\S`\/o<@a]'y>6n cJw(a$]6^9AΒ5Heꠚ*$NZ8 ]7{yU[PیܧM]{;923`Rݓ~JnGK^XD3[!.bi nD}9Du2R]Kxʴ)R"PT .D)h%.ѪtuhhN|c)pl3VhtA2xK$i>VCD4~(1?l:\Әm%gƭd#ORL)-\v)tZۺslzdXb!߅ؠ@n:f<~NW V1,.mj} ] HwZ%ǍP0 9m)9Ofo!]mTs!9LLžNg:2>)!S\U tӽ @?i[w,υ X.}OC8bre{ϣ]2SґһfǩiЀ=Q3AMI-p[%ʎȴ j*=v" U^ :"̹]<EN(@wإzWhK|H+s*OOΡ"jpt Wa(0!KָP@7G\ h9v{AɈi/;aZTRwk`1ڳ+XDU$_e'anaF W<`FݪqSO]1ƷK"bݺG>6a#ڋƮeǖ @:ˤc] N#iKr8D8aԊTXO Ss`յhy'SC v[S]IlwbIv}nX6Nl)u'%lYiܾyzGeW#>Li =haCSdE_TVY}?.8_x0lYHP3۷5\co9Spn6 )nb談'U7*nDI3ScV%/J-P: 2iEjl_nj ](RwQp31)授Q+qm3'Be5=K@E?`bw5u"W-kG|5|6M5&'>xsFEY|Xru͑) ?JeGc;f1uKSx02'rJb>ۿ5]S_)ʸIʤ4Rf[ӲjmmsО3шCHkbr҃16EhD0@~2~T$Nw_,T~ wNo!e_P:=@ y/Fsԧ7|'RcdNb`IѠi#pHck+HDø xLX?ǐh} f?JDO$`wNpfB9\TOR`fxHn%*mY^}wryG}GҶe_.ÞQyvt("Ul/d2^5qUQǕa;ڴV(XXyǷK(kUXprU :1}Q'Jߨ$a$EZ)wRZ,0j]B6I#^hlzN./Hax8_kfB7Ԧ#wge",Rx2kP[;KH$푬xeC_j:p̺#[ܳ$Ђ-.;MEPdZei=,`zz=kC@p\sp"(Hb˨)4վw>~|-Wb_J\8͟r3`~):tu!CrpځP@w>;7͖au@ͭKiTN8(6_Zy +pגg%@IQ8rv#:w-見5z@"RgdAܯګu!v-bƒ .:M"(9u>yWQ=sa4ekѿ&3_=T^m nP-AEu  U~^ #Qޑ Zp[\_K4L8ɫӐRL՚zc}_be^ؾrt9]:QfYZp䕛ڝ~2`n5o/ГAz:MJRR]kt=?Q模dl1Bgܕ+q-nH92\%H Ӝ=Icqge$ؔHϲ0/pf1ܲLbuQ?ij- ,eToLe}Z J!],^~*m 2EtflvDQ[,{'4Gzׅt 7$9;3c10׍Ծq\Hu$5Rkʹ)k"&Q p;4SϤ0i:҂rE\DC"2y)e^i7a廳 ,xj$в ey"10/cYR KN-xDNp*Ɓ~ 5dvk>ʻ"@IsFD=6.j9-^}jwQ\s T4"* %## qUI١-WYl54_wBjiY ؜F^I; xo4/uT$r\0Nx7_Sڦ醨 炵2nX*9OJ*ֽXTAɑ"?a+IgRyN 1pMM?jNk?mNnc z[X\a@>& GVE9# hw+h"L @W;IE]K7Jg`# d"R&h f-"\>]::M\i pAM+B>&4zg)Ρ.|t(HVMs+iJ2׿U٭͓ʙ~~mToBY"Žr8 RqN[.G;%6<9`bZU*3,BbpIJ{'rEQd0#v V|ceJRdUo. !CO  7zj څ mhH?)So.Jȳ:f.jQ)EFx|Le90usAwrQCq3K1h?o1);ƈ\j5r1&X Urlq m s_LM*B(SuBScZߡG5=*">6%3/b7!lNmrKut<+eTRNP=f>DpxQͩ;S~)"p֙tMTt(vpn RvE :\%R ~j38+1}i.zukߙd͆-DV b~[(%hnƔW]j`K҂41.ƭ罅F~花Z.Sϝ^Bȫ5i{<;~IŚBΏ*o.cGz"xS5)l]{P3SXzVx{nD k0' _q1ּ'ưG8GƶW\ -" ]ًCIBd}`vp`.bL~RXh~"<]%dzM>۩ߧ# =#O Ŵ ӊP1kuoU$Vlo'AyEޔ7XêPٶKKci7[/~<H>E\Sr eJi( 8l5K ,{`j78G j3Ed Y/\q_Z0_X&5jq] y+jLH/$ ϵsɳWr-'kx9Ia'j%# [2+ U.-ݏBaA}: ;`8#ž1e`$5JfA%jVs{hv*WU~\On'FR"&r)f9cd~1,*$$0Z&wvwj5hs]OJ[FvvL/m4$J5`VLV7$S/ѬFtВEB9>sqU$%Ԅj(6^^)YKbw^l v¿Nw!eM^N1Pu 19Vfw_ 5AŷVн~bv|>Ȋ jhڃOCNvn.ǼgYZsymfc-1.6.0/tests/dfset_SiO2_221_rd.xz000066400000000000000000001070201511276756400174140ustar00rootroot000000000000007zXZִF!t/?] žKcx_a߄+cs0 }hAu`dYNX0ry^Ѣ%4Q#47KXzUJz[ dpƦӗBkq9҆,+˓Sc/?fJ+rajde|r8/9iX&8[Boh;{'BY5#ek,ι d^lrVc;=>l0Dla$ :dlu;(+ȔS9P`fx? 00 ^Ha4بwwıb{ %ω3h\LPH#[QzWRI);Y)1g@Lv/5!id)1{1_:D/d!B(k^M۳3#FJ;S#nֱI+d1RRA)o_^AbGs07=]=U9`t[Nd̚IDBp+s۲ph};' Cjwꈅhlʌ8?4G^b_@&.x[D O*r"\'0łVMcDc>?d03T-Ҥe'pGXss\|?rܤgs:_K-Q+TzwtI'#E@NUE)R7sBPݘ61Anwڶ}3!"H#W<ϐ@z]]W |,zf@[W5o O1qxO\nZH l,R-ͮ1鋬^!hBphG&o-Z;wD@_ cW˲kt/ 7#»l}pJ[rOHMK~WRCA}g:U]DrB.JVi!hyeCU= @;\]Ez~2 fE\peV"~wյS푘k ?ڻ"vݣ֚ HHTQ,(\#G?M:)|L~\GC<܁eBJRw`Ji:pگ|aq*LR }Ƒv[[`eHXD$B㸾A4&>o]wރnSo,nM4_?ef T^u ̗/\0L'JHlKi]BZ7"_ȵ~v/B`SOqw>F]Y a0_"& 0+*Uź3AtˑwRj_db ZV 4  FTZቌT~*y6 "Sb M6|"S稳p{ʢ2r_Hיu)jxPFRGSQV~.~#\?֑D:_#..&\ZVQ%\v3nAUh ]=%,@ 5d߶ ~Z(:CxnƯL=p_+w_vdƠw ph""gt]?UO"ǎxnFD2||2ʉLa&CO[7j;$Tȷ*7yN.{=KWH`lmWyKM 9(sA_,yzeʥ[{PؚzyC"Nk{,&ua`}_E:]gr6Rz;W] sydQ4{Ou@1º$C;o"p-'t5yU;<J(Eq8}|FLi-6T""ܰN॒2p9`8e6G^'ct޸hWOc/ujNj^3!:q0 -ݗ;.`ZǥE5F,S"I.Hcv$83.օ6{ +h)ܧ3-ȠP˸`ci@QD6a_E4o..yPiS׾ٚ,׆[ߞaΨ~R\o< k/?t4PU잩6'[;K)OX!Yf|q-a] ]X@HE;$̶ۙA[Wz.,HdJҺTAo ?{7&g5 ߦ]عf:)*/1ԩ;Z6pT55}+pB0N6̩{r X!i^/I1M\lO zt3=9ў0m珏2X8Ο>S5Ӯ!< ^ۋd/IRFnU*1y@~qk߳_Xs7mk jy٥GnL rRƵN?OZ4c'XC^y' wغGqlXLroC0?RգurA4)bh,B%J(E6B4\4D&<-nYu>.DA= hYsf'9g+EO4}MԴ{ݨp>'1-xhF8 (*z|&v`"jl1.)J]j):Is݁/t ͮƽo)K+fOf?YG3b`rZ9̙憥#fO;cNkxmY}T4!vA?qY9/fjQ@ UctC<0W٧摲}[B0nR=ƼE'URc@*0-[6|uqpN#H=` a4JXE,NMkaZnSS(J #{ ~{J(xÌ{<}.t=z9Kz sgXHQ}8.siQ~O`jb!d(i֖zQRAޟБ$󪳧mcK4 2|d/'4\Jf[FgKYǴ~%8)k|R}coȹiaq8+97W3T4ۈ&})kw -pTZ/ r  R4})N&€uk8S͊9\ qM.WfO7WdfEx,iU Ko!{UTj PHDtƿ9KtY54ŴDZN*SBϡI㺣9K}.V$ &LG`Kf,9' ˿2;6hi Z`S1 wE-_sq2骲Mj,V5 3,"f]й-Z̎xq5&ݕv諰&5+> WkY*y_r u: I(@WQtښ=F0l-;Wt TIށz{jڗ]k*vvY e%oZ}T11R+1M0(T5;5[H.^<;Fv {~'# wAu~ ,N Uâ~#He:y GQU7-V.5wB%L9(PQ7v JH;֖7E@0]6ڶʼsܹ4#t!pǝkV֞d OsE| ѽ@(t= fCj17w0/{f~(ZÆ+ JȠY-/ NajZsDAxx!{dYq!%Ƣ>:>>Lف CLu84V{M#$x=؛1$D_a3!H:ҥxW<#(]|idq3<@vv[JC B$ X!:t=^>}5oq=њaim3]L.eтԠWs/u i+ў`( r<]PWC/+ ϿW)i+$0j3Ub(s5n1GkOjqVzE匯UoyK|]eަDH]h9\ֶsnCLv]@)a!=}͠E^&{nͳnܳC,N{QHf[;?hy>B؋0HK1G]ԉ\qpvVJvS /ߎ"r\ݒap`ʨ45ΞM"A6A^a45X !tCn={suv*%\==ڜo5=eȸQ6XE,QDaĂ -$+ 12 sգ{&%$rSуY>@ Xѳd-AԯY_O/xɣŎ{RmjCN*m?+Mޒ[КN3G Ed!M% [=s7ݚãHg]%A$dId{'t']WOg j3W jy,C赯RZMC+&o4vh?t/2FUSvZN:Gt8JaE>d\[TIHsETID(V+ )HbvS]5ljXyK L[C%\!=4_Q8hv? U0fv{v;;vʲ٨΅׭OXLtŻF\Ѹum\SF&#!85p"+-$1zt:ҝgx65lsl#`G&d>1DOὡk4WG3jUV`gQrr,yYbݶ  $֝nݾwk+DO4%m.ͫ ǒkq4{1p-pGzhްtd#vםXn~Cy̼Hr~hccfa<WBR4^]+ al[|jBWeZCiRwUqv[9;+`FtBm!V&bPO)D?.!ƃDzË4b 49:JH-PAU}u](alWٿnɎ m>rTϓ]/8T<:0ľYU8 !ݥmСh:tdA̠A yA2#[xOE `buuЅ>%xwZW.~j]$"uKo1gzG)r6i?%}cMEL$A剤E YV˒_k0e ∢٠P L8:4c\UɽފI* a)К`$ƓzW2Qs&嚥gUVF=OlȦkQʚO% vW~R[1.݇D%QZuճQkN\ʏdKq ReEl^7=g~q5+ G3X~1_Z)܍0.l%|ODd8!%NAQ.ZWR$mֶ"*R *giiD,aJ|dž="N`zU'=tͪoOЎ[Tŝ@ᛋjW-yid>솹KRq˅^p;ΌZ3AE@Tm.CsCw@3kbķAՙUCmfn u-xc0r1AH1o)yu CS8PNByEC B(he^+[!δ7t{<)Ƨw ^/Q\rSkJv(~'[BQHljbE\q1bj:ƔQ@y]5<Hھ]U@B# LH띥 )MvțDs;KmZ@ٔ=gd34V~uVB3|oeWƚ2[ln=ۻC6U}y41A'!O1Sڽ)Woث#QTEI\9iwaV~hIG29D~śČGLiNs]5?5Iژ@ -Er #{PxfM!~顜w|pƓ~ms= qɌτ0FFTR"! EÜ2*KɪG# j6՜|'!Fܮbp9y\S P:Pb?Hj!%zGvA9.>Oء^M<=yӀM?(&VF\n_:0ڏq |QQяfS>LN;w9e㸙@@xs"UVXoA]y<*|ŀ/#ӿoqǝRq PsM:ZI A?ŦfQwAWsׁq+k[(K J= A%t=k%xk%A cs,QBGz5(~ 4fBKօ:>,DlMԍ:(T^qKx_VFt/ 8 MD3Z+"S.@*t@3QNJt 7rb ~l v`vT=sa9H܁śr썧>"plj6k+At^r~bgy ЕQX`O6Crh4 35&ĜKT66W-" H^u9IL"ɰkP/Tf`noZ`2zI]!) fujЋcYAL'Lb;R'xp?*LW`"g:S>:ɻAOɺ#79;|?vwGY򄭝pioN1/zQ8:\n>NCde;T JװܗC)^ J7Epg_v 0o%A{JfR?.z\ uhvJbIonuA_ cP1\lxWAx r0N:*]ci [;>uϏV:UO`M-JqqrWٔM"wp }e %,OB3ru Qc1hh76+ן"QG{UR?EU.dDXgPނ+W QUOOwKQ~dzgH&3H,t9t_E,L.uxi9Vr0-sbHc#+@虬 ,b^طByY'$Um0=7=`^TlAxKy5e!n2J0/  *qqKZ[Qatm.@(7r.ϿxoC6Um>ZڌlqސV.ec*wNj]OlITPу6:Bw? c;vH>B֡>:vW:N[38߁1 qq_/9[|Ll3^r3;A{q3L*'J)hrgzH%:$s%upO^J6t(\;:O}UY)|::ae_iyז-CU`l?8yEDE,*2MOT Jߑ2"oչٲ+,a4᫲$u"dU܆*VKGK% Ej8KգN vO#ѭAIc$ 7E9v6{22]0ٴq!8MlL)jM ڽL*%/ʽf|_5?ym݊zS*q%- õ_e*hp' r{.bBӑL+ 6{!~8͊V7ju;!&ɐX4){1+|ykfk_QXhr"yza7DbhCRD@Ӣ RUJEE`L v$ș'{XY!z,+G|LI8Y#iՋnlлo8ȨK7K=ա neG[90PWg6Ui F ccP\{LBBj8l53=t @zUյz>Y)#|.R]͠,Ỏ|%F #Rτ'Dqy5e ;u7LmVő VZ~s(iWsyN G~Œb Hu X hWYB=L0cw(׎6e((Řy^k .JuqjQATusmE`Sy|]<aL3[Rh)H':{u,2Ә/\1!SQ]: 4HG[Ǚق/2aޏk]Oašl'whri5XYSgA&9YSO<{ M5P!`rAW;yVY%%sIinR9n|NMSw~xKiDsHMWmPN>; AJԓwEn}[U+/ .됨+cj:\6-;mYnM 3\~p*N2}i7YCn #:L\Uwo}n-f'xIlZP3Έ͚n3 o?˿uhnsAl.6jB|Z\?`W\P26:s֯'eCĈU>RDIB6g V\d؉uyկ ʛMh>G9j|MP]|@}(zWuȸm]wWӔJFf=nag YrkэAҥmC )0\l^g r%N[ hMt/RPe_y 3ߒ\&$0PQhhrCGBs_|1~ da)Smg.Z|ogOe HAgl7PJ; piӪ*7F @dpL qTw ߃B,ϨM.Uت`_T{_j:i) AOf[d/{8PyDG_kU^{ßېt0çaE*&ؕt)YvX{KAxg 4j)kN.~gػqbk @ϩAŊab@8P*^e] T!ۢoFNYuC"h2kư r`x^e9 MI]P5Xzl2jaؒ暄oy1 x\b`4%ח %E0DZoYY1wR&U[/1dEP<&PSтK:cd .\RHBhO*1OW@ĚURp1KK > Kٱ 1k .[7'C+SDn0>?z®\::!1T;O j9g)s, (X31R tʕVŽ,=[AYpnjKZ8es.1#=Ƀk(|1͢pG=BDa׈rs䡥Dk)m܆죑a#ʀK*I'uS^B:pLؽz5 HKPvRT?؜Rń|ΚD GEpyPHߠk,Gdgt;L @pPPNa}[F,+Ï0ƕ[l/||/rn D[%Ii4[}^!jTi+Z,3_Ci$K?X#at ,a@lgc ^rFjgs}:릘{B0' &ѺtĽJuU5:l\B=*Qb_dNBeUeP9%Τ@AZloTk,rΉs?+V1_9&Ft6;zz,w6iE_9 1s s}sg},F~IaFZwA7~\[@H\?0w{ K.UO6Teoc0;0?3e^."t&ddthL ,Z_˾˭Ծrt 1,u{ø*-tJk3ژ!E>8 'WD1MJ!]ill.Dęge\\/IxHtkcLVT$PJ8r-Wh=»\%j/.䫱LX@srcz/L-psSҢ;>t/7fJD` -~v>x/rvٹXd&pMx1I7LOfރ6I>Ԭkp'm]b)XTW! llؙikㆌ0H^IΑUAg4w+o2.(DR*}O*)r{5oKaE ߕxt~w%ߣ5=&ۙ#VɢR'80ܪMr6eK޻'6O'62EjJ[/q;#fE0;p]{mcN^w0I4p}vWȁPAP:i=':ߠ_2!jOA!w]f!pu?c٤d6۞\Iq2)/;-~tm]cN\~ѳ*qשr_Hv1)2|ޑK_ɟ{U{1F}Wlq, t&/w ze1 b)voW1pꏃͫj#!"Rkd,нLZ)Hr~ň,@xD=sJ",5nzԧ^X Tʔ钎6m-l}[Z}쑔x$rhÅP4ory}1h'{Uo:2S#HooS~J5d\ K`@HWDqHzwTh/_G:؍j"z%iO>b!NN5y(=ɩWȚ0b!0< @(19Ks~8U_SI$ў%frSyE7!w3RAˤj(Dnl~aXb]TA z$A.rYe IbP^ m&y8WIZOkPeZ6HNRӫQ:°;s%L'A3gMk$7RD%cOo(H E۝-wD::U֎8'P~*~Wu7@ lxoQ&Tr  nuR@ z1C#Jj7Sp3‰Q@>s* !rITi .!)]{4vى8Eu"LO D e˔Aʣ!б@ f ɹ!*)7 !5lp`vtfc{@oD8G@RSo,SДm>"\DgEtJR~ ^r(|Cqmp@n2׆a2 ?$36a"3O"LMKhEcE &A{ܵcvc*q&E„XvR6kH6~|7KGl^)ՙrf|Wq͹oEc_acʘ  mJrUB{sn=Hh:8nHW2eR!V Uch'Qj#L6 !dtL ''Kלf5C]O[B7As~y*9_v{|[D+abΩ/&He*H3_(yX ,co+r'.Vmؐs֒EbI"m@H?LhTRL᝺ۊ^Ǐ" mT\6]NRY)1z,ٗ׋pWZ]=NzɛM`?v$̬ԝh0(jg~YQ[mOI̢H>\u)?%S\,rWghVP2"VdX"ď'WhwV {S* m[Dv:t~mBFBݕgqmyf!P,c?_},bY> h-Vm$6Bhĵ~0y;Q ՘3TZB2pt'Y *O'Mbf\z`T;Ps-Dʐ :-3Z|,%?ʺSZd97NɏCq[&^_NeR;tT͌!I9ǿazz:=#K4^D<Əd`R;}z#}y+*zhnn݁Bn_^%'8еJm_0dJ QT?-wڜ,5܋C 2}7eԫe1$8vN$3$@*maI9wGiPM:-Z/&G0:"*LޠTeN4NaVxU.ӍDzCs|I٤aADX̃8¶tDc㜖i62'r5a']jSa鷃JS*QR)P*:}ov:|.QK[]iY/?)[҉۹nmO~5P([r{ ۹.њAJ3ԯZRDT%Nhi&2LVg(Fԝ$ʡͷW_ߝ@6[AGSDL+(VCK7}Qsg5DtVߦ)FxF1+*Rd{b26)8t%ަ؆Lݲ8h`mՐz'h CCV&((׷&?<ƂyJm}y-}-^>\VEkנsN =3u4k]Aˣs'lX07gu O[p M߿N>|Ext_wa^|Oc8N*]dLe@\XT1] cr&foxGшA B:5g9'[-N@G_'K/2ς~uj;zP'o5?n?MVowGΠ8/Š`!8 {t 6-, (:T[DbI40ZR16,5'sVwbװŸdbؽJE/^YdH/uycUq7Uceh_Kcj W=cWf}N9c=!{W-ٕIhЋ [}_$֒I>K.',fBJRfhD03#?Pmi|~QQgZ[/sg dmڦ=Kv;0|a.9=F;bXCNVGB3M_Kf*= {/qlyNÉd> h%o{ť F Ԣl*\VuaȞ2eX!yZEe+EU"` [F'*+2m0_$tBvvOD:~om#6B^6F1|*7\Pːtqw*tocA-F{EOM D>""䅣Bv'\msPB82i Aq įA.zq8"SPQ脃@* [pŒ6y|ܶ5izh )q&5s9w74mC*)Yy%r5q r֫ǁ3A/*haT7敢shu~p8?Ss6a/f_ ڊ#n$Y 4qk/Y]Dl3j!;WC-.)}]"wt%OM7tIjd͹o  +/ɚXe?G'[yߑ+E &s I9"OOHeM͵AeCQ'\ʸA EK2Kɒ)/%h7/2C;iwĔB 򶻦Ku08Mh)뉧t_ur6[B+5 iDnp%|yIYwa*("r{#-1| XK6LhŮL4vzgLQ5zRF˙~^Y|j&P9'dRK p4F>ظM =[ds2:PˤY x8T޹#R%"QHpA Y0?ezaߗ@IMeWPIpD1 L)s |Γw3<{ N2xp |FlYCrXBUM[fmTqKo h|*jꪷ!~asAYBa]'z1ߞn+TdK%0'=K&nc\;[N$p}y12Z~[᫅Oze{p'kbݘ:WuR}_\M-KR`>/fE'h1 )2k҄I)إ'Gi'ZW|E@y6L$E?)/mgXF$⡽ƏU!t(]PsO'1[ú-s9yNĶyrF!ࣶڨǰύ:DU?ԙOuiмSQь/N_6O솪UC⻓8Goi1P Ftk7[s+9Tdg˽_U c9? h)Z2Gϗ2׉ #4Q.dͲeq'b^&/C[1VzMw[7iC]Sn}<0>:*9^q{WS(盧!ʍ]d`ϵI P=jyTVX1RTP F>{#?U* +֘CT;PldLpf̗r6ka2GVȪQ8*}P9e"Y($t6{ez}(>+%("hLXˈ`CKnc-{F,!,e%核| [TϧלxVn ;{>.Mo1'Q{Ї\vd!t(V ΅ISgP?K NpEVM\ @B 'qᜠO5bKw ߙ4EƇkz_]o4opɮ땸g'r"aIj 1(J+.7K-W L:eK5nbN)Ė&os&-/7T 3[ .v^ 4[O@Meסa][r{e.x8jvM8H-1)#% SID:ٛa85#@d6n/AXiD_e[!SE4mb`do|OvIJm&rb^5Y`je9Nc`̊W2N< ӭS)99[ +m4?ϓ'KzdI|\gt!w[WSedzlQ| 4Hwhddiج)qRT&pDB#HI`w;5AM3TBhCQ%]J%+-'$MFDV6v+ǁv{jV]4g?)=qAoD994,}gD&>r?p.鏓о qBaL_$4:ۧ[yTtI 0HyB[Ç40 eX7bYZ p<͖;N rd;<]TOudhJ0#ڤ*x慂L&f|=)y{yaQ9FKNGF5X1> b\oEu?RoO'l8`0'hMm`Y۠7/Ƹx1CVnEEdCؗNIӿv׸-'΅Ѐ)@1h0V8>^ E)ajeh ) *SE5.iG6]^]9v;Hf !sesc Q%Z֪6dd[}8{A0Y/@tkc:Jd7}q~aZFbXh[eFE8 E@H5LI%cj#fTuBXM[wXUqpS#nQ5M:q9w!]p]ĭm'/RZgUf5q^Y;nzIΟw`n 5rRca-,N5k;mÊEI=:\Ӣ%hDRI^CmƢHnPWNH:t12y2 PF  Nt̴LTⰼᚚ -Wl,.%.:MHT dYme>8xo<%L=@,[>@d7deW>ec?&jsϰ:fr`^k,`o}J-^Uڛ0w hH{YhV_1=X%@'"+z! FU25+MPD{| kN _޸.T55ɝ.*;kN_-Ҭ_-@kXmTչbPu@}fkexF(F(@"Փ gp1.%}&M7Ws a=INu *GQ fc]>XXYRQ Qf .fP1-Q JSt S)#0U&ӳJ C|^.YtͼY|׶ikf]H_c^L<tXhiV)x]^F BKIstҮӶơs'^u`gu;5?{zXALntM yvN'^7x%v& awg'ikN$?E̯ÒEwEPpN;_I"K?of``s~s FQ֦ob&aT~=f\H@*pȎz[h-GS!M Q@؎*< Ӿ }2S tފR~y .Q `Vퟍt8L\c 'f_ysW\p6m&_s I8"%Xĺ63_ÚI '"@}hVKEw|szuDX /5oDA8>lp3Ӎ WIO+_;D=Kf| n[hz*֫:S&zlJ*PTmqJ!8dVšg4n%z P˭C4S)^1[<~J}bT[6F?h`v!7_VĩNh9kOO3\Ǭ˖A lY0V-Ojo"8sϹsFE-*˦5=13ly %. sða]g'~Yކ5cIYP65 Dxofbm^BvlTT%BPDmm;; 9XFdZsB9roK3g-+{l&L{HXaٰ*H Tхq%bbYYe󬇝W}2 MPLF@bt`R hTkQ$TE&ȩWj]#u2}dd|z;SW3Gةf6@pmm⚛H?@h}bE~^ >DS 4خiOx06sBʟPĻww<c R>Aj1v^ۄEW aC@&1]pV\Z&$m@Mro9 ?k_dQX+{i۹$ǔ B "2k4ﲠCc5ƌ-seOz 'xO JUތR Nis&GP CiF8. 2{ȯiE$H*|7&̬Ÿ)_TS qG<@<`,Β,Rd?.cMtSmiU~jN HDB+Z"ϬȫKJ2%q;piMK ;+*T^6*d$v 1@o IM./dڌ:aOgiPtk0`+L3%:fU ]Q](ڀ;L؏xY_ V8 l0%Vֿ<*5\QU hZz~QQrZ[|I}7&fvy=?L %u`g`Sֽ rSOsSH69Yxjdgβ }YZ/ zaK@M][;b]'r3ds8kč^?qxr>Bf {%ZP׀ne^,qwܫdV,(oWL;>yebGk:AbڬpJ<\veS8zgĤ˛WدY1 ݩ'G^#F*u_iea:kZRQ4ŶtI`N= XJoMXuD',x}!-<B,`6=ߕ^" Տ5Tod>:ڽdWrQ<9\)~Pa8BVk"I jAƚS(̚vu6'6YbKSaQRݗՋw25ȯfq̿V b宪iᗊ+u5wB>*纴}wLOI\aCb{pDMok"p 1Ħ)+` 4@y;D}rpu \Ej K_%a, RIȚJFUiBe^"֛{Mf"97r ȸ#dUsݾu_$W[  [;-ND&ߟD%|Ӛ\Jrn[>2~\S{bp9(K.JЅƱI}噟;AchV/ oA ՀPS˥Okna:BKsj~v2_F)8mVPS9d_>tǟ_PWFZ.ƾ>8]|и'ٿd$LY~E9l Hhsv&Ծvf@ƵDm@gxzNE $ 979ŋjek=!O- tCc>'. X"z `͈խv,!&㜧cM&.,NALaHPSpk"-)׃`tyL]G_Tgbi54):,6`"ؔTk2yVG }ȰZ4#*_+/>PM#n}:0 )pT sSvڽ|u!웷G&BhD+4sKSXmW톽ȃ>R#m)__Θ S3IwTooV:[%`աmo8BlJ>^4K 7h^W~ z ^ZI1`) ӱr[ 7AZ5G 7 vī_;V>ձ rȄvx9xY: SAuF̕4^]xbwWи+q`%XQ@tRa¼bVJb#(h־\M;(.J5 yHI'D7jgղS,n &5k1\-L0lOnZ\hz8 9%k'Uu 'XƣPMZ5"BL\ˋﱨACgn,KH19p)Ӱ}=% +s<}ةapPơoL UM;= l)d/eֈ]0@e$ttpsk% Xڊ(R& 0Ǩ F%Ծt':`u掬'ZMnآII?A*|(]#ܔΑ-~qwjwcSV^6C(=//]et |YE_, ʬ2u?J/hADCW1Qn/Q"9B#*˳dK8iJrPV(xaQ W kdxy{̈́{lHSL B ySo~&7)WW ,7ln7oA0,@N\D&B#q;"Z22K]5%e]@I+eiA+%"u?L/f{/:]aƗ䱌&e;9z+PJW=|@NPF bXbrAK qá801ױN_}YpP(b`jcQl.\_A}{GH4)tA ȕj_Qvt!ۿ pz4ГI^uogj˝x}be%a_yZRRKbPPp̽E׵.t.s8kcc Hn^ B (RSʚ 鷟 _"L`:Ss-cL)ga=N{3'C;*{y`5/6ޘh/g'ҿnM0VOY^%B,D`pztAVu3;]Sg[&CQq, <AR}lC"Qbjkˈ؍O#,>YUܵf;>do0;WGc;%WY\oWF/Û{[Iiaxiƾ+$u_?t υCWk/ny1sЌg鮧e׫Y[zX"4"\=KWgz0PY7rT<^]g%I./8 Z;‘bev EH"9ǟ+le-u6 +z( 6g5޼ Xe8C ܓ16z$1JjhR굑)@c!mR_Fݾ| '.xv '}E ɓ"ſmMb)Abb6{-7EFM$ؠq,Ϝ[bCFQ#C)N[D~Rr-H6.6:gr8d/j4ţo|4# Ctb lMl,з*:yM__8\ PA8['wx=Yv)9+ ۂDv`H=h@ ID 6`WNnT^::YN"{9.ҥSb3{'nI_G[-Q3i8p1Wٻk:ip9vkGI7!G+Xy^-Y[HJƂ#nk1o5praPvVwM.@{v\r-綊#&Nek;eJtK3ZhRJ^ITI!._ Т03F U}?3h*\gj\q$3f'z1 u}VZ=| AQo>Fdfh!kNF΀g_o'Ya|oAo;o0ڑH2<;&[AfeTcJ&I@&r#YYtR琥[,5z Da;'݅,|wꒉB!bM"(-M!4u?vd3{ʖ%/bKK1F|W<:}n\'&! uk|ݳK Me :#o&vp { 8z6?DA+o4b]pE_G{z'A>R$gm72!625zY5{(` wS3ԛ>+#W:%.~%̪')CGt[ů&`tɐ1aO(C=-l8)˴q>vSugIn’@TNZ{2lěTGYfsv`J nGLU8k}0 %Me`(u^u(jLqYBPE 8;`wu->ZHe7iq=͞m aN+C'Z7v>$:J<8lwHlzyyksܴJj8m΁y?$ -Y1WFϥ~F#sҶ;ul&mHGWM')N]CPI);ھb],a< Y,n oim|ro꾇9cP]VBO0XK5QCQ+G qZs=T80i5)Xv,u@QӠ[/\լ*kii(\g̐+m}\Hע->0ҴbOun#L=6p ' q)I.i&enL1btɇIӥZޣܗ]C~ Sa^=%C3ˆfBL! 2a Ȣ(sJ[MBWʕ1?:"/Cc'PIDei o ^'fبr"8NOwxQfHf!kbO[֣)*`. V<Ұ.ɻtK婰L#: phiCZiQF ˆ+RBx[/2爳R3ϏGWR/̄Jݯ:_Ώ7-A so E_`S.4 H%eSkֳ7ˑ#e@B^`:* uޓv 1)V$p3r\\2, ԯ/Ǵy.BdG\17S`5Z]:Fd9Ra]F׋k#L3w1~')!C)r֓$̴}!n@ ې]%a}(͵cX QMNǢjq'W<~žږN3" tǣ˖X1ٗ,3rPe~i`^⭞ 1Ç[a戰+9NLKGkg,VIwN_1.fkf{ A|K WQrpNd [ X{dw]kGCt~\y1= O#H2ui.9oZڲ)^SC9kC: qV,էm//CxxCCp&d-험a50.(L"ExA:>m`U(>{>A齯n!AEkwN0 nOl#Y85sXa<3%:1*K 9䋃>27#R6o*HRsT|fŦX+FƤ0;*iH5>0??~Z}^~0BlbXZ;Zf t"_ j5bL>5ࠠ,>1Q1J˜gGܦ$sYMk66Y:WlBY N*>9;WUyfP"Ϻv_sGǮKz[xE5q RfU<چx>˛`0qw#gkd|[gY@ 3/].|=Sfu`}j@~2k)- $gYZsymfc-1.6.0/tests/dfset_Si_111_fc3_rd.xz000066400000000000000000000130441511276756400177460ustar00rootroot000000000000007zXZִF!t/G?] V|ط\O1SStO*p HU^ mnl;(.ĀV(j+iH2m9sWn]{LM*O]`Uocpr /8vMͯ> oAj\K^(QfaŒ"@+M!YgKр34 ܴC M?:I^Zw3˽l{,GSkm5HZlj=y׬_"CMp2K %ed~(Kx['e1TA7b"+U#N"M蚵;r'x΅YDQ<}ݿGf oRjޢ F$!svSm壄f AtKp;hA/H鱳4ga`]7'84j=of@J Fs;!5;V[}¤>˒k{g+aBE{p| )_h[<4SGU+s·-L377ط;#"ؓA7xEa5} 7.6U֡*3~R|ykn!{6Eܻ X&/nݽF( ~wfQU'UscIHxz푊'1;ӝ]eҐ3$1/kfBqX]C8 G&fvٲ8,/ ݇(aYy:|C] d;jqK;mnܷޔ$/~ ~?B]Q&HIKtx|#iYB pC dhMt16kdKL'ȭr.?==1f,;|؋.4gdf$v8/_D_8( &3鿳}\ӲTzC(p?z6:%,cʶ~^&U;p&щB -TnIX(raZ 9qWD/}fq?3CIJWt>C-e do!'#E4 j鷿xB2h7<&/VHh{gm{ߕ䢈)#L|\D(Ș{ cÏon; mgIit<b FT$MwV# w8tI/'}jVS"3_(JG(;Z$0Ͼ]/ oL?Gɲ9(_)D|V3jAFhuhP$,[ޗUcx[p֎sZ:9=m;S!PTPݻUhޤx9$7qؚJFr ~T~՜yv)~75$ 69PLoɨVm|iUi^0`^ՙ?<9:q/~T딒)==CKŗ7ob.^P,Bp~`rIMBʫ@lfm/Zr T}4 c_T)}Y f1  /Ʊ*CD&c GD49zز?-80d`=ȿL^Uʸ./#kH+c2WSS_LLT\RLYU2kRפBW9x4(tlͿ}H^ W~{ a; %$QWm0hrE;8G3')DKzYӕ w&n^.6F4U0)%4H\QY[48}{ij2< *ȡ plpwuW `.E ,=`n>*ZT/+G^x;N1d杽p;w; ܱ#Z^ q TQW)E\ 8䩟=vVհ:T Zh ڹ]dS3!pUFnA'7?ᢷ*HWqF5a)b7Ӯ^[85YGOA)jW_zJDhat4>v`?5T {HVrh94C~z}`|YQ1+2+&Wt|Dl.#Ntpv15QWQEӝTo|pqE$;2Lήiђ-.|wak9f|&vY~xT?Hrc#ZH{:DX@(8@ҌC 6},kd{Mc1}Zh {.nOkJY`H̥؜?gMG~ğQud:FBi"g )l5&҄ DHgr΍ ԏ~8ޘJωbTUU ko콣~'O .BLZ:XcPwR5(b}ZXaL]w1ÂߴX륱0 a*"cc+![u3_K+)M>4 2KI'4,Wmk:H %Q`& ާHSas~ƥܘIΏDn9̤a2 N[^u5/l}TGݠ oT5.?\Gz씞ٳG[@CƒAB%[=k%x9S@-)v) l h {1K]?MJzKS\QU 5#fG䳿 ' p7jL5$~D}]溑;;5Q?ߐa2mw8=Xa@ ^Z0SuT^Fzj̆Dz}B(.DK_+"5)+RTdS!Kv4b8sD*RlG<q3v3 SΘ9sbu+nAxh~><vrd/&;vm@'9tiPamL.=R}jT˩)4^jL0}}b?yln.i.GIHQ0XC0kCD&àjxJaRDMy!~Apd}36!+~e9l][ϡI+#B@<5Lf=l0?aXHdμ*JUf:1i\ ZqoĢNsI"e}3zq4޶-$v,covqi>GhJN6F=>f8qUG5E.\f^1R/Kd## ٘<փA$Gx%ս+X) dfV쌚bN ?Ǣ߅xkàpȦs#0odQ}Y4+{6QW3Ep͢?gU=Ē ˅vswNHH"\PúV'Z4c_7柝#WU!I:FYҷDuN{3cm4+|! Xy`G I @^7 ?2cc-.ƶ'I8uۇXVnnx ~71g''&eދpPwtMhBӝp\} 7lbT*ӂsٚ2FDn<͆MUu E&"$q4/DcuЧ#h9V#4ׂœ+B9f~L1u+YNMu,]'̳T]|ʒUoiBNТbD,KjBuqjt9$=IVoSN )D X,g`1cS PKtpZj)jur>BjO,0B1=i9Y6Nj͂9ԍH7h {*I&vNöWş/|@rY k!N6Z]9KL%e6t 憐JNnlm "E˸LdzPoJ‚UcV&-ny:3hQ=mMmQ _6ӫe nWYpK)z!Er6L*ÍPF~*5|ܖkA!{GӀiiv0l"eNGZj2RדuB%kӳĎ:/?SV,ùE 룾0fEaHt:lPZ"Ŭ&~uN76)C=%(\|2gM|rubE̜yy[fəc'R!4ăVziF]Sӂ$ы$W,)ڣX~ހ*Jz4BRGN9 ʾ_#b_f+f()bU?J>r+=C`Maަ43~/"+=o5S%?uK)"sG Ӻ]Z6m#fs-  "/`|/!*56AtoT`!C'7L*U*nޙ7!q#*B2?g3nI$w!tȶ} 8Y ..g|t'.^8|&P4{n i3꜃OW|t#qv|NGnͩ`}E]Tzr{!Nت_BLV &)Kdd b Zz l03v|ݶ2.p&Z|Vp_dQN᫙!(ݠ0=g|h܎nP!λD%Dˁr>$cenD_~ƃ rNkƆ$[kÚy@FƤj}գP(j5{Iq*j{/gs6\NUĦ'AT,8XI{3CzL"w^`62vFA'1'ԄzpcJTiD!a0ux$7A@/!x ^!a-d-bP/)f˜zh9w.R vN?pg FKfDvA.*].4c_+ˈb oQl+]%MF@4@)baIǚgu|#n=\m)z-@=mhzY(%` --OxSa;pK_ *1~~ܴEr,3U꺤\Nb3 =^.L|< n~ ƍy<:|||!Lnsfup*ӥ{ Rc%fs[WEY/?f9הժI`'GhSfOa8OfiCvSa%7v-U~V9Zfef,_C}n1[ qyFv7w,LjDL E Uغ$塁ӡ3?wRn[eꭿ,Y6t%۠\H}*w+rSHlז [7P|\zR4G9P m35u{-0N, n#9|[4|e/Zf nAkaշnS:wΪL3[&_9F*uq۟Ev(6=܀} +spEn>y n K 7/ ؁! ,yQ9^mE34+ŜwP7`2#K/Z̞R']NBWB YeLkF))l`ģ;Onx3K"n0^ ?pmdev%##<|p ϦsD2SljGI '#h)]TE@6:U4^1{UYylWDQ)l+ޤ.MC=Ml{adq Dm|iR\<XPG6'V F(cǮNB|hM`W X% ބN?H"qmO7~ZaifZM[s_q0 q]uRfy)?N}\]b3 xFj5ͯ?1 v ,ދ#Oa{3T3T| w3V&~q1]gݰ7щ^@<]Qjr.Y[V.i>ewZ ^}fx4n&a"!텅0TOw_]eHtx,;=RL% hV,ѡՄP5sh_gX-lqy<$zGR;Ɨ7(~\hSШ b"a7ZSD7[r=l$L#,bYnt t{&=WoxJ$V_VF;0m7-eۊZ `-o)GY})g('4ȔX~pMm,*T: ьXcTV@7Em#= :6hتo]OV^+9]A!x]:8*0)vg( dgɩ nnt-‘\ @dn\ DX^}Dq}`${kWXLo{߿ ltayD h;&UR9*cN[x{aJםBBZ2qzbF0{ !v-u}'Dq ~@Re;UT ᝦԙ&=˅+x_Lyb"pm{0N N? s"%0 μhp 9C;FH4qy 'b G[u)xGa *vȠCC9^zW4_.+]pϢwEtpXƈ\>CgA>nq'>p}m?[HS*WaY=.6-fPgBl '@*?hfZ6G**„69_S qܙbvw,m&{&T*r,ٟdqP:ocY1}u[bE~옙UhRSdJH_yM7[ Hx5.HU`w\ѵc;z}i.Tё>ӝ:LVת n_cS¿ QcYUtc._B=X^Y' -.BJs]q̉EwCI Sw0d@〾ff ]JOm``ixoM23:b4{굄$rep^ I!rm2l'3N C,v\}vYTttZBJ!%Ro&+zFTqirC.1yo$f :DP'>U>fmcZ探BCy/a1yւPueeǸ~}YSScw.6}l#~b1<%v}Rxa t-{y^'r4B7 L8d#@$h:0V, y76K m3Fe ^Tyg&*虳: 9a@VfXѰ>K1Ҳeuq%olo/`#0+yeGj}c܋er J> ò|s'עid gp/laFYH'͡K\KYEJ? S*ܯ'zf7\+j rOYtGif?5X.n9ou1mz%7(XsdDmpٛNAtuw}q>ubC!񺚁JP F_;D)勃MQ$=…3@2SpIw#G5 x?Odu GP ]1aX'߮NJxeR c۳,՝HR;;NrN+q>!b5+0ܯx30 "8sਖ਼EOe>3#Ģ}E> i_OCaE )yO)yo⁵ܛ<\v%g9Rvj5r%"&x'LH, '2!۔Ug4~+*]bbNMKʆ]H!-c)g3%m0]>Oo[Q:NPr -Gѫ`O9v84!=h68$\U?fL=ݑ#mQ !n[Ϫr}~\cwV[ʺElJ&J{"o1 \@+<[ e&$υ{v:=&@/ O aDždlBw$D}` V1XtcPT^7"\&ZJSo$*`F.PG cbjf!_1P%; |yM M ;^%zp.rȱ߲-{J[#uG+t6,הQ oRt_m[Du"x'[FÏTo{ ~T!Bif錛*+?n8ĺ^Fr"U@brdI^RԏSvJȰh!c Qݷ.taOEu`آN&_#`B_ N#sOdwRcݖʴָ/?{V Q35? j萣U 6 }fd~Kp6,?W@GN7=(€~4kPrLF޼s F|A=Gؑ\.C D9 c!Nc+( ڹ \DdD e|j[wFAZ <9)0zNV'Y)YEO QRx,UAxġĩU,\$c1Mt?w' ;e_P_ FbKI@dF;.<,tuբ4&N4ϞM'^֓L71qЬ"*T",Jߵ+pٽ~2ƲZt (SGo(PwA)L&)ڱ.UdZ(f? iu}Y6t`Lq!̻跂bFn3{i7,ocףC!dLBXh [!^5<>bĺ0 4X8sˮ+V ߲+х? %+c i4-ug4J)S(W@c~9a$F*Uԅx7QмllT\ (tT̕!7i=C! `:qo^%^B"}n(ˆ'pE0Pt׳W c9X1yCl'әֆNt'!R6#H)( `}&(Ț9H,;g$i1;,8kZ>_uŹ=M4E+bf|,3ҫ}| .J1׀\YdUB,?^Xo^B$+ywNoћwnaO{hː#+,{>UyID^ETa8/̴x/qmPNj5pTUjќ=.Po)=R@cD j WZeVXLL/ae*]g/s BRsٱ!G]gk /呞͛H|)ό (Hu='7ҒC5-F)hɑő7[>{3A!%:b6zMi&3 bJI~)o3jkw'^湳/lm.t@ɛOY O<@WeiBeTL&PЂw*u{_pԭ;{%ep$9OZ|txƟ Dj٫|TZb`R>2>tEB0< &yYcg o^W\R)5%FLWܹ5!Zli/W AH(δS$${ɺlY2dw Z&ֺ_0XR ]7zQ)Dfenҧk#S^[!x}AG*MB̴ n'8lPxoi*Hwyma/aKwL==gdvKWlr:3DfK}bSg.VbwCAHu_98k9 lQqŋuu\w|\5:f#?I1YweIMnb}|Neax!OiɴhΜ341N@Ͽ~re ?glQO_ʾYO7vxH>7YIV S(1) nBJ<(4!f_ZDc-?gAw ommKll+E/~ |@QP*lcm4lv%"(†|ŠlJ&CLN?@0O^šHHe1 &5aB PRJ~\~F&#Vr@*8[+rɁz)E\q;B8pO3ReAj_MK61Uyh:1hM] = 7Ezqj^۷ْYf&sMPo>2{z w5Ņ= a]H{<{6 qT@ute[+ڄHpRw9OK `LqWEi&[5G!Ź*jG\4cHR@C%k~4]K)|n~j+(K5鋖^{Kָpw~Q!#} -2g$׷)oYk\|ϒ&1TeMu:dy'&8QMZVݮhԃxn)$aΚ]@=2 DG?5&B^*ߔ9aTg6ƕsڿо&U *_c#3;Z~_0[%-IF񯥆AB#|Msi xA7̈9i߈ )Vdx\6,=RL4}R| %yCH8p&;[{^q;iG<2DCK_k Jɒo&$)\zO+gV=b7EϓcXYͫ\K0EwvK =L <"I _@"?isg\u&gdp:g`߳Ŧm@sJ$,-<٦i23zic`ܸ56UJ[_&hV>N=8, ]^;3(2%Z-$N핆x-CjjCA#Fr:F@Baכ].^GɣZT\<"WS")ntzAr> %%`Юg\Q˦qs%{Kkl{`-j/|5/}d%M@tS$t,U^R `r>.iΠD{Xr[yE{(FEPިx`*$;%Z%^g1fğ-[r873)Y6\^v }pf镰N<'m}Pt=Q&_ufh+ D ]jn) GfxQfm>Mp|V4aAdw{Hf#5p<#`of0aCe yw@ZԪM,L7܄>i$a\h U_0 L 0[ 5|և0()hd,F,ctUHAD=!b@_cj,YyqlQJH ʹRҥ\th uGɓ%19B]K!Kh[JL. ġx!IzcpӘKsI! -·Fp=神-X&~+L#Ź޴$):ɖY0f ]9OSZ^#Y6e_w 7Y#WHwWaj#w(F4 M]ToԊ)A'Q(_K TV^Llx@8w @m=D{f$p*?+﴿,f@b@qf7" fκX0ūvkh|EJNr>5(>dÎ [/\@ht) bsJ`: x1%v/,]rmBobѭc> ӞoOabEVP̾ !M(h֙1rȓ.w=J(?ӆb1Ai{ s:l3CO{35Q( 8x^h'( ;{?zaIpbF 0 -'MrpPa+hfM0TZՌi׽Wj1(`Z</BZ뼐ӍЍ8-uC국7^q%. (x$v3XK?څտ30ίoj^U*Ux˚-C_~X qolۻ\ pI Ҷȱ҂FA%}r6rS\ްƒdm$fO@M3E/#dp!xU3R΅cT$gTs{DZseA}"SVsRy-)/7s&9a@=孅/1az#Gf|磿z 1`ou֍~4݊+ipUf_+D@wqhy.l%AZ4R>"/kyzU BõUG1ݖYtf2[Z6f8{ -ԧ7_39-7bB愑D"xF".َ/ʴgQ=OFFP1gp٢n~6iA`Y9+.n3ɼӯߨSF޽ƁG\X7 ]x Q Iސ7 C)*>jv衐BJmkc+&K] 3d#G^=ITTy+RaL$X3$g[n: 0 Cvc;l1;QrvӌkuDⱉ0טz鉱EO?~Y&GsEN0cmb 5vE|>3|)jlEAvY׋c(x+LT"e3IJ4Rܮg6RC3H ÇhO7EP@nPuq$c퀱VC: M;f4ѧ(Ϛ`<&Sy ]rga/_/(K2ER5=q0t)v&%eq]NvA)-4njs+؝?wt#  ~~,58T-OApAh -1F lKm#V12@@OL'UOĜ;mǒAuԣ/ٌ mJ3pݿ?c*abbr\ #L&!|\` 2p=`Qڷa}󲸲omg/JޢEEIy9jAtL5?g@ i-E,VtP,N7Ԃ E P6AJ|@>Ǥ1W;?Y8p"(6}Q)D ;e#/geÔHƾI5Fdlf;}Wǒ|b&{V`}KIיu!9`>ـB^)_GM0<7% ;-La1MJz5+jB\&FYw0Θ6%!mPueը anޚ%& t;u74V\C3Bn_Qɟ笄4,onG~{XajuOTs5-(l4xh9pcrͻ˱V!JF@Š6 JB?U&_P%.$#yTM4 2ue-1`kzI,1h`MZ-|#rmSpr[կ]@{=Ne~+tG$8/{p%8jh|I#[&#'^EY{a ׏YPSĊnp0ؼSnDgCL ̥c+7`3T!`F=5;קhAӄ/yfL?z>i>Fxuo^dM51ʦ S$)bTm݈ ueđ{mH2mqaFGRmӆ ?٢jؗ_{}u-!\  T |;20iȰG| dwE;iގYeSe8+ +UD\c`!U!{3H/}R 2M>7L,ʶjyֻZLI Ż$Qm;*FE_ep9&eիy-^50n=h&TP#Rx,7:"s~C w: Q|6ݖFM. ]\\PEe54OYTьwR%|#1r (A,L]QyҜsYFop/0}6E~~:Kՠ6{ yL.6%n_0Ɖ%~*Cx/{RLuMz8L}/LՆZqkݔԧ'z?ư}8.}ۣwÐIItv"X8+DnAB 9F?wIRﶒQ}}n9r8c~rV/U6 }{{u%nz-pgw!H!y9j~p/GXjJ06d e=eqquątN,}۫_Al923jD@N .̯ 1zSzRo$VFc  69ȶȀL彡;I_AuM[n'm9YuEL`rG.M~w]pʄ`N1eʭ覍-aJ`"8*"a `k \ v`nN\Ґ>L; ^ Gi0ΨpyhS u*c J|Th\(I_}BRM _4 !855ف%fD50rz"rHӲSu X HzfsB_ $9CDR֎N< F1q 2wt)J =^R|PEe̍mJfZIrNCdfɖD1;~Zjyfqk1yq{b0bo.lnN.! iEO)#Efqq;sU+m~=NMR (RB t r􍼹#) 1d*]gYhb rwJ+ 粚ٗnRdLcĨiMXMfŖم~ACݾ|uBiA`zPe 8 raH|7Y,hs 2Y,?vY!n/ SFc٨!K"}ߕ )/>y'Ev3Φqp%^\o)QÇ/-- T4-L[ j<_$AfVgZ~!I;Z#+L5ɏn%O_4f_iY#6Kq{xRyŁ~GS>sx76T 8)r,L 򦢄(BZrOɳߡx}3%^GsZpܽY3=<$=8LJ, '#6s(vso۹iFƤ_>^ Ж&N _Mx7or5؈D&^VMCmuPFZEi4s.e} !rHr| w9dȝ,/Yi2R(,92j|^ȥfrm!֝iwJ22Fv@=gQ)bW 4++ɀ_)}!ČT瞩+,iS%վX2uQ(u>(r @ w:׋V}Ǯߥ;C%:P6h#-j𱅲Vdk~0H܄:C8& dV 揆4_B%]FT 7jyL$wESdvW*Xs98UU &SI٬|f|Ƞ,"}L譑6IDGrX+lI,i"&- CZ*mOͧ)4C%Q}`gaK$H '*._a`HwU}4-KF|R9o\}eNtŸBLx<fV,s-qn^/|r"r3^,55@"/&  sf 8g"җT^h+X\I֛UCHE{kh00YvlKZXQphU ág11dGj*Sʌ~daƤ6ɭH.Pin9x4ksylDQAgT`5*Cyޟ}n Kc#, TsC*JR6|Gṕ8`LW32u\h1@ 1W ,b6KU{HY3^(Hkl1~ Aعx'(6S PgoA6CI=Aku* 2:?˵Q@>¢g}#{,SxU %kEo ;׫ M05 :nGQԧYq-.J"y׫/~ƻIJ[hGӄm+?wf44;;"88?v8udLc؀pk`\0ܻq ǣ ?й- l!T߳B?Im:(/pvmبIۙIYl|MMwGQph ^RXv }OvMrʖo (r\5Fa%V9ir  JFNMppLyؘܳ ; W/sǻAcK`Ta+lls4.L(IQqGΥX?/`ׁCRvg4}$h5bPuvjj1L*UGRP8RhP,"[{M9M30Ʀ(JXm?e;orǓK.b~1F?f¢*ĭCwCU1S% p0 _ mLu)^s)z=*.񸖷fᙛ>*36Z&6K?_ܲ"]}?7i:;mtbW}8V;׈i A$mQy,}U+,c#Eg?,͈ӜTPuDSԂ,8>-_\͜$x]t^ ][9B,}(H=k}#^Mk<G?u kYe YOb"Wk`*(b;ϠA`!(p+M$_F=۞| (?~&L5CZ:Ŋ&H}D)/,ε&?vgrAߒ,NccF2+&3*/.ԽuK G.19'|?ߒH Otgvs<9ղ!3ZX ;9J@¾ |6|7>ʊЏM~i]~lcs:[7d]ŖNI'ZdI ıB.zdTY3@7kzYm4X.+6צFd< O(V+^T?{krХMʗwJbNJ5 ZƳ+1x .lSXH g1g.g#bslyl?HZV1{xZlgi*y×҂&gR\QFq.hB!YwiP$MR %0#dcUv A!"oܰTʫ$^W+Mn`&+dנu7tHsxY9BLY8RB i8[( DGA\ftBtQYݍg3lB)wuەhWiUw~lNswj4Wdk#}g5AzյJdz5Cf4Ҵ!P\ZOp婁E6X}L{02|tyl%.%6?DL+]MطP5 D[ p1蝱Ŭ[,l'?ە!.%6 ?AN_t>(60r8a >Ѣ˫X|}gz- 'P@m&D"sbnV1:P0l[na20C,Ol&#hSJpm hL1M|5n6"P]#gEvQ~%ƨ%}@kvAQ9hSвJ,u?PD=[$ FQ9ÂQ)2 q7)=<_~VєYkiAR;Gdy6؜5"; ' |h LV # Xw`x]Ey/ٗr$"jt-dk (œ\yLL(oy'2t gb(R}1^mU0CSΘg&|{n&ʆabZQH΢~ܚ!tdh%>P!^oT(B-eA[?;fR=ݡOiM(Ԟ4$W"nZܵ@XceK/׵Ugk ccLH/8cuS'#{<YxfPg]U An{gw1/堿!E8://U8[r_'-qgwCX0#mdujM&C?qR*"§ R^ޣc i)I֪)SG3W)=P<,t@ '];i)xktiC3i֪]O CEaNxR6Njk0CQ%qu\k`O(e#^`ze Y9LrU\R"*ON{!HW&8Wf-:o4 Q*24FCg0Lڀt' r8<ۃ=(6 .fI4ބ٠͙_~MX_q 9[<6jkETm@5&.=nX5啫ňc 3ܱuދ܊s1:aq IcR M# 86:P9̶{J!GtopiiKJ^ ճuOg\ǼU8ƣra/*qLXsTG@muGCqÅDh37FM6ŗc Y.ڄUy+/܍$Ao'b?ݯFJUq>2fm7f͐+RW6ZB'V/ >]5ߎk@G]{UA%%d5kO_p# (# Tܷ"6tuRthsKݜJt&]NV d{mTn1@UE^CȆq~\rܰ-c+֟hS7޼[[N{I%? D6pЅ֞C(Ve~13π]gV?K-/- 153]8LeF^^Mu4E7kg#j7};Aşv~N}<9wI U93HgjD9\c>)tr#P?'Mފ#a,Z=` }bXlPZ_"}`P`/ Ւ1|6$//ky=K3'0q.AJF! 5E<1Kw달Ih(jg_"3i ^L1Dϩ~eMfMނ =;ɱN,)3 @!zZ6~dl?X-Wⷮ?gtBE398b݀yrRp'L B1a]7Et[d,'{ JnN{dœlD/!苧4&=i#}v{B?lBޯ.Lt\8e?$H_]+7T..x⛾5,kujƒ]'N6=m xĐOW( '~- 5 93Dhl?Qy/#PzѤΖ,o0ghMãؘ7d2{Iԭ@mEe?G8lwia$F8/?5T^>IAڻrn8lx(cBG( FTmeٔފߧhD kJ`l~I`wVigMɬ$RA{t^uUltbVi|,T îx.bt+.s>6,U\E%bw뇏/JlRR>ߥONnD2a״R}5ie!{ZO L Ǿ Z?ߝ4Oݢ R.োZm(o:VΪ'>p_B zՓ>uBYX',"2M&͹ *iZifwƏb29j)jwBX O󰕔TK 4ƽG^ޛURt?.j#3; ֐܏JU/\W.q% w\=!gy-pc\RR Z6E8uʧ c7& ~'#;GK|QF9 sAc7C-݂-_BOiU)SeDoN {0',;z3pN-jޟ[Dl-}A8ɀ[ 3 \wxxU8ndW1cq(6!2b^nĜbNykok,̀KijJE~TqC{ 'ǓWnխث_Uf]]k]6ibT]άnxsyG붅>HKQ!"TK_.7)eP;~m d8ばFe#*љr3@2Нo~M5 #h͂0:4E/UEj.KaX17̆-G808R%Q%rLUjTϭIizN9Tw[OY22!./Cn;'GӮ-^JePi1eOZ,iR'wD[ "PctVW G:*} ?S94p_;y#gy}8sC 8iX1C("L('g +?dʳPGOz(x!]k]M&C:JXq1]$yWAVm sjzX2dLԙ"d^CGݞQHj3M'gX k8a T-|CX0i|;<ٛi&ؑ~Km$\ULaCCŦF٪$Aic$,*"@Kikf]:2V9;zg<UTcw%=Bt٭NݜAzB|isoeUqUVL̯]^mfq35)شM "F^ŊH{;>g,~+v*,UeVLSڠ&c Je^?LW@&N gV%#*loo1^Jn:+'Zn /eG!mGvcx3l"D(/U5_Y'DV]oݯ9RSF4Qk/HJ2Z\~UެŰf eT^_k| jpsenΞwmqNx'`QEr%˿(HlúrZavv*1%|AU#ٶЕg-vW'Fc) CN!w/jWm-89UhW~%c3 .ljG`)O+o:ʌDxEq~z;$E0d{^ b'űV0dqw]xyd= !wڤWFE+/@ oŁgׯ׷yxOv]OEhp.|Pl;O8YQ^.&)`e|#fm$8eOE+(59!(g"{۳oBHHy옇LuҽiwT qlS ǽjrNjiҀEaS yI/D*,3q|eS"-6Tik4^IN52ɫOcw#Q5L$QWHOoz P~X: =F鷃NEr+|ɔgqi`V +$.{pppd{p%8VzF؄["',/ȓHɋVet_;ߩyw Bpq9;[ ;5Bǒ]Ph wIa% (ǽPC&t2O/6HX֭EAh/C9 ړ2 =zo;[ojw gB&lqM3oMQ~{FUP&-7 oz2ݴEI.B%x^2ݙMbW4o0$tֺ9j.M  "ؖߒ|;)X2K<OjLXS>-j>0=E D~RQUR҃ǜ+x)A0ۄN3*tʵi$ ^1jYF<2(l3T67 jTSP?0RD:ٔ8MVA˰.(o]6,8 ծ&\hVڌ2A.шUq!GTxPKL<44;׌rRSC%?i>7qXh?*$] utVf,N|r񐒜'U[> 2'4A46ځ7c Aۚ$MgNv^X&W Rڃg%bK!gZPR^OYFajD|$5TϼԮu5s,p-ÙYN }]>C#Uv]xti<#uKa4la!kew#Mq.V/>Hc˶ :QIUsĊ%#-b1$`ٟ/)\x0CgPY%?&1~^]?.R$xYD}}}M NW=/:|# dϩV Oi~v5R` e0߳}ެF*uq&մӸݖbȇ}BKOs'4SU.VR"%ᠣY#+1e2&!;j@] +Ud2%P'dq#vTG][mmΖfD HfYdb\}]۲yFw(X՜S7fz-\_cLmDDT@kMH' o}٣b'+Kr fx0#e8Yxk&"F% :fT;PLJV$E]PAK9&)9=D;paPG?'/, _-BZCnvl H x19_]/vc j$-Ha 2y\W"ƒ$rVȁqS2IH">w7V )tIJKNQY;[Kq؇L]ֽe*m#y^! Nξeӱ Ր/i *Q):υIǟIOerPKr+0ņO%7vt97.:aU?KW[Oe];rɸOͰ׹Hmpksb `^E).pc}C 1cWJׅ+xnNgM/NC^#5gɕSE%!Lk97,Ǝ4NݼKB\EVpۊѰ0j,\8 >"w4Mv~ \jlX-&FO90A"!1]6ݦ+ඪnŎ!ϕ.iqzV~KI^\:049uӋTd6CRba0wR*8@2 3_c q5|%3ˍT9jxL~A>6V ӎwT) R-bm0Β9>6A<->[fb HL-r[[NVx_SĘi}a :<‰'#VX$t LU\qq\_FnŠM"fBp[;[h'fv񽚰n-c3~hv9,$}+NB D E&*\P1'Ci(EiLrCzPk JnђfgeY{'GyC%>u~y"V~9ΧK[ _^m/ߖaa 5)"iʦ)Y/|q(1>6[Fnx, v!UCv}`22UzICgzo0Ι?Dpj.ed ;/VmpapS!./x )8fB^yq]eꗁ_́]gc/ZdW,>tXՀV&),> ƒ ݋fB8h?~БeJ,$Z4'o\גc?^G{=l"jB2*NKgҁ|PIѾs>PDr7U"O%A8 AFQ I̒'6cK7Ԥy6l;1"_?\m/ ۦ/0nll{qt~@Pxw4TLdpCaͮm&n샇,G;6n`2 !3=K$SKHΫgyo:XX~춙6 Ut?CJvw|\dxaޤE K8k+Ǒ0J.|n !<,%#^vd1A~(M{@Cf+G]Au/,V\GC'Ki:S;* I*@RZ48dh}BeUƯC̀PQ)C@ O/\A;1 >41ε^P<}4`?%k\Rmo頥k<9Qdd֜^QUAJ쳪ҍ2ߢ~,^]J!ȸbhk+9[鮷 3`rodjA‘ Ƨo2_*[X*xa&WvְG*LD`PW<{F3^>V7\}uqd&9 7`qC6K:̈u_0Drng`,.Jjz|i#^Hpտh-bKclq6]^/N]CƆϔt9H*ٵa\9rPߺ?==O_x T^tƄ9DbA D&XmSX>~ȮK:25T\P7XWVU s2&bA⌚IӪ~>?FtgdNV@AhP*_΅AԒ?{8ݐ7F|~l:"j:eT|Xm[+' f"gUZqdHld;kV1dǰq"V]+(pyfijbaHǠg~y%Q}#f9 @Ͷ}ME4 2߫0MMӝz%6Y3Ii@c5T"W]XqCv#$RH7W`]ʽTeƪn|I's[/ Sw|LX?s'bdR.`F/#77ejc'FKĽ, h+𦴅:3Vf,;q|={T1(=R:Yc,͊-X347#5ف4h}i.JvYہ3p@7GM A%i7W=yZU-6c7N{]sU99\*,af6rD q_mKSQ+;\u^f[t(3>&9hX "JMž]|E#A;3e>7к~@|`ۈ: A\SN-tK9A.3F>=xC]gj)Z^+QpIM Y1A:ǂHٵ;2աT`Al*崛Z.«D*ĩIClE:]wJ7ә:[Gؼ ǦwI> s{"e-u^yN6|234bn@aTڏrGe7Fb: Eެ :={%ChIpa%B8blJ]ήԐl!G|s]Wȣ`|y6L`2b䇿je{짌{3];_%*;DŽxjEIy8MN#tUUG2@$CQXyl.<g[ʙ˃VSק`Ax|,1Z$Ůx5~gj^U5neIoRTH/ W3~`9"1)n${}ۻkɒ̌E^i0?)8;Ӽ7ԒhuǪ2}L9OvwXt+d[9ڧPOk)u,)4ܸT3bmAl)_II͞(pʥ*mq:0ޢGE82wHys>;­5pוD Q@  .;JOѼK' ]$qw*sNdFTԌEk~ <`4L,);jID9o+YlgyHoshuY1]c/bW<%VB:X GS{rFǐ]u  [ӧ/b r:blh`Jai n@aI%qhyFZlna _8;;ރ,Z*@hynoS9 !kT0Z3qu48qmr`ȴAu{ax; eѣQ=+c:M?Oy$lq ɢ DAsF]vEY Z¡er$2A˫򞜘&B7$|{|YP&=o 7Gn&Tkb0{ Bf 7E\fD€Q#EH:kb.%p#6vʷ d>%8z9,M.?79,J~tPYd[ H[,S\f? \<8dCʦ1Rc7Tipu\/p#*Į%]QLA1>C̥JFmG 0ɬCک5X0R*BZrйK-DӚDP L;alP2 - UsH"'IɼOg.~zu\nBÉ Uw˿ M7;*lvix2T zoE 篓C;+$a0itli vc,|LÌIib!{ Dn)*nmq%gZa》fz4I+oeq7aD|Ky!} )OK"}<oʦbp?!nf*wBbO<ءzs [#p;X|Ry \FBB OG]R˅\g%1<3#CT[i\XM7reM]RFyx+'?8:Qאd`i{ WcmM:T*`:%*j5>Fy _u@ozApS&a8Qjs՜Vli:aS]oq$.2bf)aΜ=ȼ۳7-9ѡQSG)1TeYY\~uiZ;/'׾CC\{Rhۏ3K1TOo[%C) -\o^dKYwINCzUqtd͒T faGcL}hU^9 [ѥnx}] R5i|.G@] vU":F /^$/栦ۈÖO+K)SOf^)ܯ /ܴ$N5,nx?R4Rm?>p}B@߻!Ի?y6P>m`nq K&XUkg#f"08iOuDvZ:q`@a~Ԅ,4_ E lvcM5` Iec@+Ӥ,r&DZe,]~໲ BdsiVUW0H-ݟљPGؓS_I0i_ i[؋%툏 _r,j+).R+MsHT6+$c ә} 7ooQrQiF,^\kczio/N%W=O G:o{Lsm ʈ)pHxxÕ@ٵ銑gbνOk!I^cu CU7; ߴx{lI ZH{顉iUu^Hv EXs2ȢlvAz3F/U90'1!|3QZZ7ZЁ4]X̄mD込x .ObJ߳U+)isR|gϔ$sBL&)<"nwG0Rq!֥ iꨩF̀GC/5It|oy'OB`$(]p7@or)1u/~U5-Jޫ+aH|;&3 м}>= g-6d#rd-GeZ ܲ'.n0mٙ:"U8h0Y@O"6mH﹜\b=Tp쀀ĎJSޅf*l2xD.\~-E-n-vEEAh *Iw%Q%Y8?uu @r\nX*ѝϐ%rQ =.9ÊF92kFkqAvn4׎LAUdh3_uùgѹCTB"Uc$Wv"(R$d?'qӽkpR-w|#Bd!PA!c޳C22<D ,DŽ}絇7~ׁ :u#kD-,Mŧƙ&=Q\m9 Te=(qbڝM;WL1NVx3G dkw (o?N h`zJN1ŧ ć)aL óA#TҞv8Hgǣ[4HBWuN <4VF{a#PaCDnBDH+D2QhZI}$+I=DUu\?AD)p|\RP)oMdL ֨\G1=EO@`eWJ=߽!7W2Qm gOk;Y28 TmKҭl[3[Zg8֛r9=zAkz:#Z!z0IC W88+z1`nʄ^DjyF[,J1tջLU]\.S${Hy+YDԺ s-T?=&FUV*YF0&gHX/hnI՞]VߋmCGw[ֲ*;=mWMoIp Mj{HSQ#ki﯃Q^vBÀ T<Q#UV{26dSi~СfgmF|LFga{C&-"% ci0 դ2ߩcgS\ycy+`kUWSQnwK_"RWXDzIJp?BN+٠u#4K:G:Cvz Ljw @Ehv !P<&*ȫRl- >zbkY8@||8E-l1!nw> 59R sV8**#/[^b.2 Whe(wI_]̀X8cޱ fz=1]mrH;+ҒYP2<40Yŧ 2ᘤ<~/wV\޶R_E/7s]&!peOg*p2Lݏ:^/jd4XA b/o@6:կ3$"Yԉ{C5)3EQ~Z &3fH@T'c56]1}xIJ(-6?fz=5a@>7 J0>WFfaLeF{n2;,ɞXJu:vd73 reU`qGrD\BUCX0Ȧ20Mry'GILs~~VL>(¼I%f>'&˻dHՊWHGsK^Q%ىe!icq!7ɔtj:@]0VI`[h:o)QHjT̀\.3ׅKc`R3A6S<4pZaвuVIkhGBYY&b ȑ\PJ.EO[D8yQϧW0G#{dY=K4xDi;JC>A.,W8V~@ m(IP\N٣>{@ѥq'L(*"K_ĵƍj*\lP fpH _scF۬ڑ` Xz5ʹH]FD锅&^gȣ/ HځX}=T1TsV;FChՇk03~yhBҜuǦ?yd7h~9̀? B݉ ש)xIRXl,#lGuL--1M%f6 ˚dӉ^3 F lPVLL,G!Jpvs(m^@*#"N[FG3/l2S`&\+(/q %) SFT11$\FmR"z.lHu0:WjD?]!JQxLL>hf*?`ElBOi)8 ǥsؼDNBo烽:lBuQ*E`e'(e%ieM%lF=y?#吮OvZd]ǙDрm8P^)5mpQ e4ttQ.;Z[1Os "x֓6Ha&6hbF5d8+vfJˑx?\TAy6.jÛ+~]tU(@wb8, :CA&'MA8J})]CQ⸎;擣}htH|i<2#6Mei|+@-tԛԒq >&%YuО;m2H^ꦆmm8MUba9mỳR^ 1W9ZjKBe M7@O|mzqU "oAB9s72 1 ?8yPe'UK+)TD{B[bXFbFq5rwܻ̑>#QeSj񼌆], .~iD6N:.Mǔ}2$i+}:_y>z*teKΏR{W!O˝ KfuC)1_恬.XPޒ1/l>@$ubRσИ#vv+锬)odq Z#5(u رhٖyżL'Q 3;4FnCy#\݂sd!%QnVh<υn&q̴G`xٙ>y!+&ڻ3%i15`2q}Q= Up;+c=W8Hk,|k n3fo:827ar {Ij[50'ӸTqm ;@ (rbhX{?98ea *.(i%L.c HΜu1vľF jE20A/mVh ~깩5^HUf#A~q,R=Z tQ#q3d9eJYڛ{?'tWB|( >~װ  q'N ;0*MA$<&ypzW;i'>?{g rxC25[N+w&dbdt,G|jmf=کTXP9{K/=^ Kؼr-_:Ԩy(xHcviT٠tFK ͧM Y{U0M:H,'+H w>SfXpR`ICxĬ/I'YS܁}?6vLHnEd|spPٞ#QMϾR(axOR֙|}6L'nqwCzOX0CG966w2A=7lhQv7}F'7JE?pvJmGH m}?W2t=>=3wTi?jd%{jVhP =Kj%[la]VVln@*ÜCRCt"۶ ּRCs-Ӓ]r"DD1[qWLUuʢTxa3ip/br ה=貦A$WFYkHO{$//QQ:Jϒµq_*HUԬ.K.#hҰsa%# ZE/ݑ/(hEpl槊F9m#q|<1QA1d{`} !5Sj_bɔ>P$e2per)}{=S+B|K`m-8SvpߋON7MS i/;L/ya^"R"o_au?+(H}KP魟Ppds 3A Y +'yW6==g{J^{C˫^f= ;=DXFڵb~2Rf6-]( R$`k[Pm[/a Nq\竃]fs[YCFTE,Uk=;c3N\\˘a]A^^sj9mR;'Y;SzfK8*)ǞB!dR]a Tt+YXxg^A0rwo' ;0lܟf ?;-L\ m Q{<$Fͭ^sW }2iP5 ~IJs }NnNJ!AIrLGVeDN8)s_r*9;w1x@;ڛ15z򰃪~sx(1[);0X슩zv@piDm!rֿx.`tYd˪ɐ\B򦺠;}XcU&s~Kj |7#o$AӄfAGrY=\ LON$HRL^f%$uIhkPZb!ѶY!6~˝ f^݈! z7i`nddH@^^`>3M w+" ;XTx;3j?D8M`=ϑsú}Ϟ#]3y.%?˚uɌ _.889&@z>**Fˣ">v 3\W +$'å]iڠ&i >~*$K^k3+.Kk^Zڕ=+BE^Lߊ83 9*?SF$Zp:n@XIo.ZJW:Kp y1;MOZJm )yk)Rkeۍ14v^_"j? CozD2S"{$j =ц<)۫:^R]* п,I l젘];_WxnEƖyVTh{՟W V!`% 260٭J_[z$oilTV"QT\b!.Aw[v_8@TS.&|ԶT tuo껺6ɟ߆L2i$1]1S|pU:R88ɹd9: =wfx >빷`N/@zB:l-!P|- (%vF|얾_]vP7DLV`Ф5Lk^~?g0?DNK0Rp,GdJncs>Bc'jw:ס m' /xi!(B*N`NuG3Ã{н8^[z:胑lޏV"A[tL7;y᯶yqk:ydMs}6%63|aFos6enkCL̽ġV8ޝb'ն`oh,.m. jOY`%G˜zd8 N1(.^wOY)~u,'n*WѬHK4=e97ua}ֽV9Pcjc mؖ{jm8虑. Մv;iKuPNI]A}= @JZp N$-$^92**u8b*RMQ#ī$?g:1uO&A(l|7i#x*д=ي-/ ˗U[jـYj>ݨ*=j4 םfU _``EJ;xv$PsJʇ9㚲!fBmqs)9 ϲ")"Y}B!F!?nE[kg9[D])~4[JkH2} 87!v j6RB`Mv# PaQPVgqh0loAn3kܛފ0,FJeGaH0݊oA'H 1We'A5+bgZݤ&^iǗv0F"[ӯ})%i`";4u `.뗒-LImc^ :Ʀ`-YPLܗUn#܄#DqY PR(؉7v<$:V]<˲ M,m 4,;峭u8`1{fXq)+xJ|&$ȹQOf jVB{Oqf leB&IDJ/xد=t-XsK ֮- Q‹(H5H&Ql.^eGN ~D2M5iIګ~t_\ERiԽz$Ugm `O/ZK>:*x,r&M< b!gR*Y{*?_A9%Ps2f:4 *w` g3.(~XF{q& ;6m)va{хNoF>7~V(F$IF,6Ᏸw%3S 1}:;'CQ R؉!,08YCӐSapx`.!gb^~a|My}J{kB+~[wjvW{utfPIr(BT0 4B'5R xOS H 6HR1d=Ėo`*$*F{2.:~Xh/ーɲ_R'}R=#Dϯ~&2EB#KcSِcj ڟ yF7>gryɑ Oe#e%87v;dBL%m YGdwU?c!Ns#"J BqiJA$Gg;IЌ֢ x|NeD%lZH FO5#79جWi8*b4KbsѨ\rt\錎Pژrax/ T%bԜd#7ghn&BB8H{nw`Cm]~)Fe o  rUc]_:Eښ%?ޱ-Nzx`oƲX:fDs//4C=atGN'Qif܃O}(Q׺-#ccۛm-I= s} s,WB\1;Vk/_:ʹ ^wםDckbcx/>9B.(BP5'(.T븷c~URU2qfvՐ Ч׻Vx 6qiao\0tPu*G 6}ڪ;U&C&H3ԑE; #ڶ d s!I6h(:7)<`/As|ʒ\fIxxyuf pǼ6?D!4J;3s?M4HZAy_y fA\Z&|EءՋju•;ka}> SA ܗ8^q|+3ZA%:ơgyA0ؚ?ĒWpH(K/P"8& izn!Z@^XQmqxSmɺ0eCkK~Ω;!6՞QL]dWP2v\OHrAͼ_0 [/|K$~ߎ | !w)P'JTR:$Qr KAZJX2=#*޼2R +ܨ%IygA!PMg~P˫4TyUTU=@Le/.}s\󳛾Ek Y#&]o l1T .[6kR#q%nu&KTT?azSܖ]yÛVx_Bs=hKta}eDKXc׶ 1epRN{ʈwC.cm+RɿqFL_~a0r|'G_id-*y˂#Lm9v?_4ue{[a`rͽƤp*(׵iͫMX87 vMNίnmYG?Iiw5*u?oKd\dk5 e^:r{xS dT}_I9Xg/|ZJHb%ϝ{Y%Q `L7&EfYY[{S#vzgd|u8xŶm\_lCf.i85 j,74m3%F\4qb<])|~ NQ!A}]8K] k`֕S[yTBD, \ *pi&a|Ф2LBCIü]dB$e/ar{ si/х0锒nkTխR_meU_vl:{%^qR;î?l\^[b=,z HL4e+sШlWÌivv-s[[>rݚ?CILيv7<$92JOK0Y? Β;ཱི 2LIoWɶ"2Yf$)dgD"rW6) I͈Ū1߁cM>ǛLtpdOˏ};w 4?yV8u /$h=$&klvumд; ʾ>q(CvzDꔫڸ|)eDXZS"nhؾwц%dmt88APYPNcǤTȾ;%"'o f |fX)Wh>k\.G Ý@ԃ,Wݯ^GSEުwQ[F%w)h4_I{9VtK[x;+jmUYv:>e!ױ$z>ಭAx  N`+W65Vuк3ght>VJ} 1V<>^s!r pZ-u -ס?#@UAѥWϘ`௞RXl~^jz Q.obe1Od8k%xՇ_LoV ")qB8ī8:YG5DR&C8t{4MR0F${~#xXNOn]=v4e.{| S2-\=ĐIxaoZP=9 D2BN^1B lk[H܃|P,=R*\[;rnճEfX6˩Ŧ@x^G$k>ց{3n /Ԯ}fij/5%pj d3Nj3^_ԼPTIKetM |4ܱJ3Q5jZ=iĈo2K;K(I5&h]!*Xw7-FRl}M[P ] Q-td4y+'홹˭ .*;ݧ 0v3QC(Vm'ld;_}̻ nC0!{1迹߅5x 0T 238q?H sbG&g93E`Fܳ/:u37`|o]Lr?FHJZf;&^]=گxBNj\HM+55 3@&zG)&=:K08Ҵo3_XUсͽmyDؚ^KT`Ml4WI,=mkP?_1p?? $A|o+~UlYҿ=MP cxE VV+ _R#T,RXROEzWG 2&bѧyȴmpRZZY?"MƆg=HdE>}/D+*) z_,-蓻'a_CD"+%4@L\0U;s-V\EUgz#_8̯$MZdتlhp0$*t_̺^es7w+V ٕrj{9M'r$,ӝtK3Gr 쪨D\D>oPy,${ _u+ES\Listm] H.VUR4~ 5}aS RY:4HZ$sR9ē 2t%,>Ӂ3iW h1;Qqu0pHE@Bt-2NF@P aX\+U58Kx@n)(uEd(֘qiϣN29h 1oX5IeRYnA$=HPFM9JdO[}._kkpu'0nhYS>ٔRcH=m?E#bi:Rϲۑj~_KOkspe9K x6."O.ﺈZ$fب @eyTyMq]ᓱ](@ J[)й $BD`);!6Q=aGg _ #ʵuRУ+ZFgixr}]Gl۞hQ!řqW UL:IG\at\f~Vy6AV(`5>&lQCջ Dz@4r.8"@"Tn98{m50\`E)TgwojލYڵDIt Bͮ)5&iM5v|֙X5J<%I:.n m<\HN &TYH~:r^YQk!D93`ZPq-gcL7*o9GJ@"/V\Dt3!*xE)S7Axdf&Dna,c ::pнD= Eu` K.ÛNAXb独DM8z`6 D ˱JÿL" h/BdA]Lɉr`֠2*9r ~@E6FΊI<9 n*}ɏ[7ڦSyr`\zj΋M6ΚUAm!5֧/-:c/D as.ljj9a3@x^4 [lH.@ȱ-kuN"`Ȱg3G,ׄWYC뮲o'+Pk@-DTGNQN jW+„&o.-R2OϢ :Pa2ZT~8K:X|Rvjb\"{M 'jE:N)Bl:];2I3R7歁.Fɯ{G䵰 30rY:YPG 蒁dD|ˈ|w6ý x v4@;R@;}#M;zPЦSN$DiX{T&?;1?^0\3$1oy m:E#b)2Y8}scrct] ~i\4oqpzQ.rRN BLGz<Nce/I<1 [>9]beiX6LXf S,[҅ŝwISnF&YHgE5J,">g?GԸǦAFy,|tOۥT TO$ө ϴd0{@%CU36hjsEZHCpo& o9Ku ._ ⹠%/b.'7\dҷm[-]::ʹIe7?L{y6ov JLZxtazZ h)w1gN׻׭IdxӲ2= # =Zs(ҽQq=Qküؑg3RAuV61KCNa;P>aI@2[HlD.gX0b) ;g-7YKlEei8wM؎6splD+=?DUؒP}d@tQ&7BBOz0cZhh 4onBڷc1V| $ʱ6,md5|60y9 rr aTu=M%-  axrgvMcU_H %;"[q1gr>iĎ :r"h]*h"=qzg)`{l{.,DcS nOڶSW:rz IPz&4۠=fbx0z@ށxOb/)Etc AyYe.au~@VB/!:φ޼vapo ݦ3=w4[ode5 e(tЯ&JEi>P` 6J9 VEaB }SrOfU yٵTzxD= 8Vwſl\W]cV5Ǒ?ē:m 4B),#*"N+wbN gZz/E9ZlToE8@G=?c9̈US^W o`w^ѽymGV?̳Js (J'vJwc,Ë0^ۉL\uÇXb{i< @xjCgj6l+p@?b^2`,3[Wa5Vsd 0UZJM}L .0f;(FUD#+ JΔ³Vipع:Ŀ)C6"Z\F1% SMޑ >v.~j0ovFJS~njmG-wEi1vøcfN`K*oSvV>f|!%IboP_`?iy  )+Z<л!PY"63{>] Pn8W:pJ% Hr}r6וi@f[:ž4dR?edZm ?x7"MI: ;@I.V\bz,ZbrkC$8e&}LՏvr*Zc%WiQ}_J0%1>S.`݋9vYd}g` fr;@#Oe'|e:C31@K %3Hcc?QcͪZjW!o/wO9 ng${V/z!cZ*xn2Q6A8 8 TF)e<e\ՅYqSPJ Gڙ6D_኶;"lp|ZhE ^BuCQ)Z]j2=49Rք ]P3U|2Vpb@cÁ~bSx= SuR k=K7M*?HeNOndB!V,7.20fi֝e1c<>s4k3Tz?VJw8 z 3_ll1Q YJBcuG?L d[wb?9]#z Z!jM~KxYzD!  tf2Y1>9Zҳ[cj >F( C jUz<8\pEߖ$ad 'N#95JVLV;jobJt1_CAJ+i>Zˈ1ٜ $@}Xɇ.tumr{:oHԚtBiTqF)VqkS\Y2%k͹`tE,I.f_fy37â]mN_DiG 0cAgOlān1GKdEBm%kn& ujIԍ붊FѿY̓<>҃Mr\5ޮ̜ԁ! F&dby,iWmJ\`0ՅM:L;};SyƅV!RMe3^jWJekHodo/VpM <5P0 ;6ƽ=;@c[JvKN$~VP@ F2XHa;9ʪ 1 ]D4bՐ;R"tڕ@SEh/\ɕb;q%fSwj+P~i+ldvџנ8>EP>1|zَ萗%`S S?K'˘ 2/ka8kj ^qJ=qi\\\u'3 ]TS '6X+ΗpƑqȻ^piC!{? 8&C}-Zߑj6r;IA*)k~ECo8atE/\ |>4I+HŤ/vRo#6SʀEI&d>~wEKZj[/& |J2Ȼ2 34eMZ=zS^UlJ)5!$]ʳl!%|v1SLdxW虃ܗ6-aĻNqTqv\s}9t4<̛8ND p`{Kg5/iؘ^AY">)z\0 dC7i5'5/AUL9"a5sY3BZy 9"탆0b'g}2M L[9W.7Z3j,2> 16l4o97S fO;[=<{FG6"4ool.Zx/tb߶>XY[ ~g% vp3 %Oxѹ@h5\)_|D24Mϳ%Udy#DQm)^:NJs6y0aRZ8?]ЎT ަM^3pqQK 1')VMa\Oq""蝍v yb/}rAFmvW=nq^Ggȼl$B >Q]5>g-CѠ ݊;Qk`;sU[4婴}qzdbLRLs',qs>u626(W!%U{e:M2FF^op2[cn-\bC,%dZҀ:heh[L|BE'b+k G<3@I 95̝.# Zq]t7E̩edq!tO*mB\[֒r+UhY\DBFy |ꒂ]T.D`ף?}. tɌ/,Ga#80=5MV֗Y<,^*ONl}CUKa_"erGCD+?H5q1nЭI5pO];at\~vuy]Gf{ 6$ERbTEH =ك{޹.L='l_/%֨Kx:uR/Fvm7Rt"K(xMe5Sיzd֪`:>*!NUIə%9jylhBe)OI -V>NY aS" Ҽw@}.`׽0s4-&̻mVb;!JuE۲cdɞ( #%a{ΧRwJ"7k32ܡAP]8ž.fؼ u{A氃n D?+|'҆ew\"wCF UrpYȷe1xNRm')L{ji7Y ڳ.U4F]plEo C1q)/4$LlyYŦC2rr|5)R0s2[RW[}5ND67 )ʜ\ui[>Z[ d6HZhwe<%ɞ%'A{JC у ]& ic @,FOaw" TMS#lsE9 q.QΪ-SY/tgC`>uS5\]} ?s"I&=P/n:5:Qzϡ"Vmexx}0vjqF eH#T2RGڿMYޙ7/bRP Yc3]BW  qiAFE*!'f(I~OiDƔ8#61Xo8$,k2mQnq\.VC %T*Hx:cll&OنͥxSaP+ TϦ3PIkg'wwp/I3 7J|:%69tv^]S{1+8 aF.[WO);ki*Se.f¥>O} 8&k#Nun$]B-sP>f'hׁm۹@ױX3ƀu =i&9.2eXCwndwu"i r LϚy;2I]Uլ~hs[nuẈ 9ꙀCJYh=IO#+T;x杀,qh'c:yZ7Ю{%C1ȉdW4vs(y&oIg 5cx(-V:$BYFi 6{]gŠ(wzҹ% N%T&z z8tDsi: Ai{/1b%ѫqYk aAm 'Z |:SS#˻bj欤?0Um:;_usqR3GR *6$}ZFF` 9IVGf 4uλXHu,W`-6GīDoS6X~t9Ke[mc5!kIeI'lH0jKJ_(3W"<Ž!En1ri=91mq-)SDO AmN;:x9KG"jP1.at>Tu)ZYBxD|a za~@S}ɤVO+ء[qWS92u)hn*UY Sٟ _ }Q$P, X\v-_Si`??]=SSj_;;YJ&]Y[YH!g2 9h酷\4щhW^z 8}3:Wpj;Pc^qN IL\HBT^(\cl"KէR3 Jl/;3Gmz!+؅"Oqm=E(A9?X+8V,)?SDg 8G_Vh2e媸ٸ>C BF9%nޚ539KvKDD6A 4%YwdkI? ^r9fX>c3g=}6\lzJP)T"L4OCφM|T..}`H Oz qxU5' g%2wh/3e*xDˎPC yP v !=?'T78 6ҦCl Aa|Ec!ݸh.=?{Q'h %SEY]7V*(RLUŎAN{IAEAD".=&ey f%f΢U=,}J'Y {Y9hZmԴXLo0 K#2jjb7s&*OqwN_A_ eP} EHq6zŕ>o2ZݞzܢG68ӷoe#yA\gS}e+H'#(LXŦDSy2Oý jTX&u{!0FuW w+2%vK@Isc@S|X' P!X%g2 l}EV; 2hHx~c!.|9C4]!6v}TEt%G mj}2rZZ7>%QIXQqˮ)SF_ȖJ'3K_?r(<ϵTWIk0==[s~/&9-)g8hcqgt2~XP|R6ԓ}&tUAn3:L2mikʩNy|4f `@<ۤLCr͒]_{TclZ뜩DXCc=d*EM]$}H4#~]|1N&N1gk N^GR!piN[qXʟYB/Y^E)jq_ҏ p/$ MȽqg^gXP1x2AD!Dx ^٧m6kJ-WۙY"{8@2gSN:K4v^I?xP!r|]m6cl$mN2VW!j{9SyJ39Je>?(X`}*g{z8R8]:Z;Θ<}&TnN0f쒾lj2n;Ͷ}G{+Sx1PٷM|Bg9tgͅ!…9@ MBoe|i}#~SZ1ːŸڜ^|X{?AB|BkD؄.{qyZ;FҪ;S0ƨuN@j`ϺvQ@=u ƺRƟ7I>4cYKW"\-ܮ.LɃjfzp-M0cgAk'pIX`cxץFgYZsymfc-1.6.0/tests/full_fc_NaCl_222.xz000066400000000000000000000272401511276756400173030ustar00rootroot000000000000007zXZִF!t/5.`]1J&jaf`&yU`|" Oիf4z}-T@YKʿʸ^$wਢFĽql(3{3=h2{8%d]G/+XcRA^$9>} Ao2},WIvDgY 7 Fɢ2$6/2=dLnp#&&S]RD/#Y!B]1q LצbURqR=iTfN-OBp'S.aҀF';''{cHŠ_` $F@߻qSYfuv8$joB:znPvd@2Ѹb:X$y>1 YFI" CegVX:ZtKoNhӧV :NmkPzAOʳ0bs:7Ѵw]ٰBl uC 1*5xiI]ħ˥ګ ľw76K3웭&C웿vtec6 ֽ'XJ8% x< H4 uLƕIGr<BSQ3=b7[mW0֐7~.$`Uhll~5Yki'PMq {h7^gQqK) N2?)X>F+G01B! LUG!x! /$>P$>ȥԲfE]Cȇ= ߉n8A j|ɓ~ B$bcg!o}-:kMp)X׋7B?{CT /çUD6U_U--b iÕ/䞼 #@P$ڱ?laPNBQr2fDm\GGY* OMrj~DnCG~4BNo GseO=V3j1A4W-< k :vz(=Z+ٲ"7}ѣek{'Jzc`LIQE82 1H5;#E2ڝmHc`kj&X6톈V{/̹KpϨJWU,6:\J7P 1xۚEWw U܏]soF0̤ٟϞȭ.cLi3g1phZϲbڷ~sOV6GK/\dDݎyMQ zLB^D)턀 kĶ䩜2qW'~]$;Á5ј`?B%Qq֔Gtl910M"#@??TM?(L3)Wq=j")g{;nvXE+Ϟ` ӽġ[ܭfJ Ф2M~K6`4z;)GU=ݧ T툐AaCH"cB4_`&g7MY̩IOa:0 eOS#g%!Jkc#Ȍ' @j-t|SDZ!Jo2_\2Ei~.B E1[{*ħ{eX'?2fR.GBP@XCCw_x^ L6e+&ʂxgxFŗ0 ie0l׾0,fK>\?YٰVJwuƂ+"K J$^V/@ x^HZX(14p=_<:1T*hSIw"KLc"Hџǵ 8Q-/[Yu\{886Y~bP}RWR?z萎Gz|1Lyɳ-<. JGy3  B[]~"eA};~ r#`LѾ#[ \2o&{*;0~j#مEœ^F1E5'╗N$EAm<6jT$ DUO1+®_ !~|Ӹu !9݊ ְć5>?+eLfϬpqB5&|yz k9Çڀj(%?.C8 CQ>-ۄ~ǡ&Y0K7j]oEO?WyXc Cc1y}βg* /g ;x2s0-D*eEY?ܣܙVQ(OZ89i gQYx0Vaq߰<)fm#|56,K@dVrDF VC 0 -Qҹ&M@.;>-͡"{U[ HJ lzQ|S$:#JxR-0]ԊI&nw[JAFs|ZhZ6`18 苊7V3O| *mHD)Q'XņGdv?/5ĕzc%̕e:F7əCx9p"F7%oDI7pѲ Ah%JI*>bq|5=;;Vnsח)RǮR.>bHM?A z}\ִs2;$v&y)'g %cTJ7+bCgR@K"W{d}KJQ@*MPpS7ߥiN[_F##|6^=vx̥5\a(_ܥ. 01Km3쒹4a 8Ϸ Z+ {{vQݟ#df"bX-KkW1ָq£I`gE=Z;uKBtM LaJuAE[~O!Yd$Vseۭ\|Xq7WTĿ'`LMv\P]R=ŕv #Fi3_pbQObV TKe}Ԧ0җ[{L5d +ʹcI* "z>]m6mWW;ǖ=M`lV,CHPL5L1UVURoZQj: _$ja8Gn߾~S@gZ8ERAKIL/쿐U mhR -:8Jr M6A7^֎Y tAj)T{k Pı\<{H1{%˼(B , NɹΊpj$MPDl: U|a;X`5jSXA P 8Zt[Gw .FĿ؉-P\hcm yPM~ 0F)YBNcמM)EMϊ]4'Ek+<cػK[^;9_/ic\ HO&%{|20-dʟJ3t8iD̥O)r+ E܌cf/lN}w`-qn 4qT\0`+Ļ8GYK17'#I63(SJ6*m[`]6qg7nd?s<7QMC*i2fq(ZV'mMkC3-DY+M4ŎTMcv8Žx1[+5OP#I$x<7 8|.rs_XP_ x?/'Z|2Xu 7C4nWt!r-RjtHmIz>{  c'xV`f8YMn+r[>ps)Nys-jeEWVb 4"A]v ``tN}(We EYe*,Yo02ӟ 8,Ho?Xf8> i(Qb3H%8,8Q{/Wل`C\+ݗ4!/3 iV)O0B0~|~Kl5Vm`eĺsي̾/`aK%}6f#>%>P҉VoN&W%)rfˊbFiߨDLjL 7biHFA% XS8 [W6)'x BɢPFԛ_*IÃp+^FKu;4SC+6jFa?Ood|5{ 2Tř [Kr|;]e(,qLt}~vt)̄ ۵5[`.v ],/3@)x -ڵ38VJ|@Q>1X |Bs]@NBnw*r)'K`BiH $_wϷoPU@32=j]v#&i`JaPꌗAڃZxqk¨l حlkͅ\^ _ ͧYLW ^zPu@R#B y8䫼Fܭ"UuJAKOBeD[W4qp:PΧ1m~"[$)|*Ob21ʺGIXǮX-gSA6T \3XӪFFg`Fd_hB!@ŏd-v!PU[ZלAL8/l4rvN{zo`P 6 ā.#`g9koA;hx9掓'91c՗Z G)sydR:+`Al\kXGKM+$N . 5bx)#$0Ƭ50IĉZL꬀ΊN*?B(Ȭx!|9A0޻ns/C "mUSOw5zu݁a}Յ_3m. K̨% .b+]Ј tzK{D$#zB0յnwC/c?1'A(Z h#k'sr|o`/p3f5 + g1)3;Kep5"pwg5B ݨ38 w&F3Yb3)]9 ,Oo1. U M%;caE0HR_=fU>^7qbt'p+{U,m2?Nl[eѭI\pШlMmS[ '8n2cn8MsLIsNIy$ݧ }b%6>qlm*+PӬ*OяY#eVģk6ڞ|]6ԴWX9:LUcPtԶOI'*PCJ06yI-HsIi(їx {O^A^ i281Sf !*-0.<.u"~f/tY&qjɡKF~Mt5Ad\{$3 i + 09M͛w]06K4MrQ-leѷuXOKf)}Nh05L{'.k'rT5|PRMqgLS /4apNku|tiU%~m~F䂯qX~N:հTFf @Sw_֠^~>U!EJUpB?#0O^H_z}6*nyqA1H 3 ͛=7T:7qqc'Sא߷cb$:|ŷD&BVfSųcN_M#OJ?c}= WpsXu ?ѮFA>z+A 2}~1z=< t~ҽȎ%^#B6}JӴ76Gu=֕{ t\XcC|@lCU(Sj܃f\W0m;{Eѝ"5NE]^Zjk:p }nXdJ~Yj* @(x6* Fm>D^BW7):Y:r%9R G'k!%SQ&hZjpL@X:zli7 CLMI.REl=~[%kj@ 6mqėLF' 4=BaL\DtwZBP;.4 q[9"?!B[AzvWvcFlҲዃ⥐?&q}5и[o&EϤr[{KɃ*]7|pꀾfvd3[XE7e2}!6Ѱ~7\'yݵ,) 14!ۘ@Kw.`Ǥ ,HsX@J3"ULfQn+-PDu[ڳL>|E{YINa #6;J,)_#X1HCmQ4q%^&UchkM~-1DLW]ܪ4~!/$KNMa9.~wuiQ mɺ-Hx:a50%YT E,i h4+KvMm|YV˟s|UEi2̳~f.4mKSQ?art:A1e\_0xӚKj D` @Xۦs1 Zj1ŃnoO\j&hnPE;WSڹFӤmu0 $k"d!f| [uv eTQvw A”wcyn@AkxoX9i';':>uA1>C!R׾Э-@}zKHW7Hn!)Vsy#G /WGlzÚ5g ܗ$U["|ѩ&h x%hR@ݥ{Uߜ&=Ml04 µQ戯U{?, gs8mvVMn؁}`kC+u߼N4V3F*, UĝL,cSΩe7' ~铡by(#=GV 3Ѷ0;CLG({5q ނQ<s;X5c;<%rY냠)3TN`]YW_0+DEҮ4m4{֘" Ĭ.J({fI'kH{LE*noʽPaHh$܌$bD;A `cwUzXVvD8hij.@SC?f7&A SNݦ|xa|=G3'>$38fG}SErro|ˎ-\9!Lf>7aqTE* aE48o`<U+5, p?OsE4N8Z c[V~M5}p'4k긧8?OaĤy"QS季ж@U þfac1hq +Ѭ %F1+tQۨ~(cJMpG3Z LÇ] vADvMR܁ A&A'Ejg`U=/*ص+,YJ FUQ-aƼB:?Ix8ܺN&úI ݔM6K$#o,sfU(n!eb6̌poPW`<59?Cz?\5qհ,D븴 L4L4-XW9:OdRahPJ?+L  3G|R}XE"~;jIvCwZJȱ䯙J8qH1Ï܋%Ia E c ,6;D398EYweÓ=N5b$L Ƹ.+]5o,i(ٯ:qb~˽ϓV(Nornlid('/U(~d SBiЉ\#OwIގ1* jIJnP|`V֮r [eDjk^̼7mךv7l'c[~N g؏%7gJI_Pt1ъUS2!N0^@2DE4qsF^1@W yb 5EΛ>]=gh~4<ҪOlX wiP}]*C3Zt]NcStomk$+թM.׽mEn7H5t0. uJ#y YBrV|X02,k'YV2=^Jq3$6Rs_ѕOEf"Z sZFVn}^_>I/b\zwc2Hh%;}oӐF<9tڐ+OKz B(v ƚs̴~DSLK6oNlz."\{ZYraOYo!g _@n \(;LJڜ!\a'%z/`gzBD/ZT & 0osf $+QBE@_YRVu݁f[-9cdn-IT&4e#zK1è15Bd Ų#>gǰ?h5\V@gSU;:n'teuۛ{Qe/ 0 ],(BҪ@`-PW!ܞΌҮ >r?]>|mǔYUc+mnmСM>ks@ƨ 3Dc{i_G! |])r @s@W/?`p?! `d2_ߝ{t 6z+XJ<81KFg-|*9fGFBl#0q7tHhΤZX;L>%U՚]%V~w6g#&[=4qO+H^g%{FA m\ ^945(0F v&է_Ȗ^5eho3ai DO<5;UY;ĬRXybz$bPtnV#c4a@ګ,!&-w*{s)> }6β Ks\Om:™vdں]r2p2ärQ qѬ輡5W^|j 4NUwfC+F\%d6)&8[~WuݤZ쥑dZ˳P;_lj$NA\8gYZsymfc-1.6.0/tests/full_fc_Si_111_fc3_2.xz000066400000000000000000000007601511276756400200100ustar00rootroot000000000000007zXZִF!t/9/]&s@l s5q*uK$o(Fm+*S>ʼ3U%R!\ b]mMFrj%)dU"WJͳt"䈃ջH!(P;D2 *A$ŇJ&X 5SG|*8!m7 %DBN/9I1V>[eT^}*&.8dl~hRץ{'Mv~y/{[Gh2ז Z2ˈçu Xj-/gKgsq{[UTzH5>!vA/n\>]j cSԯ_r+y ȏ W8 )^]H@tw9xyrgYZsymfc-1.6.0/tests/full_fc_Si_111_fc3_3.xz000066400000000000000000000143441511276756400200140ustar00rootroot000000000000007zXZִF!t/X_] 1]ZR.fXVN?H&d#}4MZ=67xP[b'A.^21;HXclZdګd{p/ȫ8(An><Pwq0ҔLȕ?_e[H{Ph'na4X/6:vWCF%6Fk/ S 7Gx-d'0C\D>9?nTo*=StϦ7%"5obdX)߽o4߫Msq5vҸ .0GpOuV!L `$ׂ=\-.-rXWA? %MǍ)Q (46;-vM &Òtax̎ŖP{{`+Ix-ഗW9 o\ \Z;jMU=R3zs[*,7қQd1) TɭW藍~?L v~owLnFmVX  ! c EM P{9s!}۴ MYwq >^C Z+e6uA||nSmRym~/u+qTifybIR%[ PHN 0\_5[g2J˥cGm$5)'āA57{CyXff'MkUƌ &8gTz:Tu.ϿvCrUw~;XLa?,up5:7]S[c\>]^ -Y)7TՎI&&ơį鄤DL_JIe0V@y<$XjAnYR=M7ƫK&0?c9tmXy;(5}c..79:['?:(!w˓ֲE/Kuhur3;Cో/ bHC WԨK4Ɖbxz,IţJWXa@s-&ރWXwIQro9I.x);'S-Ŧ~MBRχ"DbTr).!3GwRjt[rY9O+̹mqu\J`g\#`ẅ/кhkխW5d}4!m%g\]ZLw>eUb/ETz :UrdlOV~k<2C 55ܿKBT~,hԶA՝TY\eaU1jiב$Gup ]̿@џqВ!"Sʰh \5n$zmW;̏yR S-9gY- K u0:(0|ь1:1HĄLsNWMJs,Mݒ9D~YacVͯ#u)7xYDW$ƍ]9;:ڍ5%=C2:Ei@(#qX>5n%;g{9d:OllGn?2kI=!|UDyP!mݚ8'Ky`g\-(?qD%w!ń`_6R,5jP2Vs[ t(" _l,ћ2q~N켿tGaQ nJu]`c[jV,fpDã翰Ƭ$u%- .M)u 8l Ͻ]>$7wC"xDX/|oCyF#q'hӮ7V`HYG$&bV_* PGFn܁w+p{,JC|1WB?_Vx1߶s\L3lΧ肙n敗DE&e,Fmx )V ॢIx5ynz Hܜ8YγQ yD'Ntu?0Xd ;u;~oTEkc&Cw1Z61 IBܣxkws{]5Fi8l4puZ;{Qޯvnm{3j4. 1I Θܧ#CTJ*em߰1>x__+GzȒ/\u4Hm_X] {SaB+3 `C LKXti /YJp}˘+ț6Ok'e:lB0Q=]y`Ȍe{ledOD7jY…RT86'&Oj/ݞi IaZU+% 9Rk}ne8;<p ؝~@%=H t/o9PWrċ@sP ߳hkh $ \g 6!agkj d%*]!# smEf*5dE5Щ3csʼ=b&v:K0M{0/@|~ĜS9!R8]? Bo~^kYh]n=ט9/ n4)_>%UJ){aضOlf_ș!2hq\"ۨ]WtNq!ߜ7fLWJkF2QYC)0ЕK3A^PlpK‹JVGGdZ`" a5[kYˇCZSo,t Tb^v.xiEA~z34KƂ^m ӱ[x;s``ۗK/J;Z|i t`X_gԷy(=(3HbAh9/T)˙&ӕmpJ 4,?N8E C԰ laJ>Z_|.K ,kBByߥĆP|Oh.b(Tn-ࡁZVXv%)k $ ֣0iTO˱(9}>p)Fڦhi; >sA):8{yfH'y͟Dm-m /Tا_Tk9 BPMnVa@Bݚki駙O|iʼnogFpu*ݖWv~Bkς' `!ˆW^ayq\-Deb\> Zr68a X#ޛ[ F&w<E4ՂIvéxgVhna@(9Ⱦ[?a|I;.(*?C!Mt1o%@jLHE9)1|/cc(bvI1LXZp{c]󈁟{I<au،=lW`fSqITqB 4_$?<-vF#d1%N彅 2yX+CkD/G.2ɯ1/#nGSF*}GxBSy-i\GL97nF8p$MTSb wײSZ2{xF9v$擯CXr5p*^ge[-sɋ3FIhWc ӆ X?Y#-Fܓm ʳ6'=uQQ63Ɏ$mmkF)#q ~:RW:(p |,@չ. 4MNv~3[ !0%9Jz9+zj< eiNSmQ d|]VL/L SxP n+P9g!ljr;b&IDki2U̝OK{M8w9=ҽΈL6vHh|>㙪|$d΄U0}_fXU@/4&pca[T?PڞEN^L9[:Ěq/ᡨo &(襵(#"_M (tU{߸7k !dEK;1;+PE2__NېB+%FUWnYR~y{?7׭ kdBPYenO[pKtIe!$rME3:k<Wc9vcp16ltbV15N%IC;qJ/$6l4 c/詇L[  >D iyWged(R[E5u=ZVw/Oh8=y)wF3 3VRKme(%c1ߛ2ܱGtУ6$zgU"PBJ--WGmxoNV#r 1tn&K ˺&xG R9o ;$hH+4]n[@]2 Ole?ޝBc%*eQMZ=QVn*1ޠUK}k.LDw4;M]63 CysR^+qJBܛS ê4Yb`F o/qtuW 'fl™Zne1F Q 迗341Tѓ7?'ʗWBCDk괂+eبG_'Xx5&n./™73Tnj!TrKn}ۊS\˴U{퉏`uo:MIS5)|*^C+DjUp:Mq™{Ze§H&Qc)>ZI@U6Tϰ3RK)o[na%Zhƾo'iƲX)8XsDt%idh`N@*֬uge~c$U槈3`>|,'l2{%^3p$ ?EޝR~ ҕaRZ&qfq`N.9@4&D8_ ʎARL4\1,gobWG Dd 8$π!ϦN8)(04z1wgYZsymfc-1.6.0/tests/full_fc_Si_111_fc4_2.xz000066400000000000000000000010501511276756400200020ustar00rootroot000000000000007zXZִF!t/9/]&oͪLg?tXzJ;g |7aXY$ =0c AF}fZ(vaƝ /e*;, };jx"d/Άu<)d 88t2 7Gk}iU=J -BǮ&BEDɈuǐET: ):1(O6TN~P> (-Uҟ.w +@G>q̵Άxm!nCچ 6 m9aA k l-O=+ ޜ/7XH(19٢H A;3 Wm[(ck|֗+BmDPaGhz̷cnm{(M>SeJo^O} F!5_i ?l^fQ6=@9pgYERrj5/gYZsymfc-1.6.0/tests/full_fc_Si_111_fc4_3.xz000066400000000000000000000141441511276756400200130ustar00rootroot000000000000007zXZִF!t/X_#] 1]ZR.fXZ*xY9d灊5(6_/S#Q!ǜ2h}oFrn r3 l:3/fr;>JmDP;l洑* 0i\7,ޱªk3HFoMðFR/;0cp߱ZMNdsiEoԣQ zſ8]T ׉ Zmr 0xK#G^? oeദCJA( ^.jCrd~|B{YECF3ZG~/SvN{z^'Yӳ?+>0Kd6ꊱ_ $ ?L#rJv1: z'Ffjў~^^';q!ahZꤾ";h0! (vp>! w]Y˳SjgȐ Ym^m!~ /.K]N+q=އv}uic ?b)dlms}Zk&5ds PhW>N͞_6 Uu]NLlQ3^plHН Y'!S;mx&#9dC"VĎJӸ֤Lyp5`컥̴zގQOoa4i9a[烷3 }XOVNI0FOjگa?@cKI&nv<7[r:^$:1U)oo'˿T8bIU5˨(1? ~z7͙E lY/#A34KE4^>R{l0vIJvgy>_jS2%JH0am =W҆L6 &~D=̳O3Rep;P(|HU#&RU!{ [9:q(Y7`Dmbv5$$ 5]ɧuZ%DUA =͂lEpg/Df2D?=;rc Z,K}_orc^8O"hAI0f155qzzcB.0*M;ܘ}UrWCBx#}NoM[͕f$<ΐeA؉Y\Af5{ ó-H"Mv?Z&, Ms ۨ츼P3쁦Q+3atf`_xqIIs܎L8j_EOGl_˻i꽄N>Qڛ~z(3IV,v;X\$jL?xU0$`$N8w$W r ښqQ,Ù¥tُnTB@}8Aadjw6Tq _EaIk'>zKފ^M۠uɗz3!cA^\O7na|dYuŸ*^Ͼ?=FFlF2=,kz R oTH2)/v6kmuv ܑ[oM8,#6u^︵Z)){H85EXqߜc 75CxW4r7BOF(BsSQq\$%Dl4+-?]M~տ[Iv9HGvC#k0២Y>gYlܖJ@S&8;{n]`=vq=>9VKMx-^}}-Z@nK3ȦvFp._@#ZDчޭ(KLz{Z!%^E[H6w>O^%SULd& %=.ɴ̩EfN;Mйt!!ma]}/>5W䉔HB220֨WXz.H_~O-zKM=X˸DN;֙:[KJ7HzSNDp7XeɵۖC&ҴY]*R}];=H2PM9Unp(- mHzAPؘk[gD*x=y.5oT{}d< +S$cTZ0h4Q͘iYG^=Y+<ƙȱcĖ o }[Щԃ@)JWj?KG>\[mgt(>aR7 My>ڤl2y0RGJtm-}3޷6. _W O^fRrJ+q'Zҽ3!&r9(t?.~8 w L{}rR,oY 7qwcmQzuc!Ag ϡ4ym0اujfE7' (2]g9S"]94}}u@DčU%#Hw齥[p|Ȕ `#-v 09nܱuEj ?qoA[ÒKJݺD~ Y헊y?7%S%wzPۊ7;rvn4Ln8{Cs/@r/o-8-KLs%ug5+I$~yh_U.s1 -@!CvIlZgCt@ZAQdX!ߡȳ|A?L4V}R?pr)Q\*Nvy lwee3؈Uw^-iK>sbAE6o$!^ؼ버7H8Aa$2b/.X$D 0i0>%dLjpVwi究Z mzD?/u|!.ˋ\6 yl fDp#G[XK!pu)39*_9_g]NC@Mw *t&GS`JL{4^tS+w]ž[|2J ܔp nT E߿*rLgn0BY^V恕B:Yc$HAU$3(%\~Y `'_G : _9'g&*: kq+qYdQƭ̟ܩS;66 -ӹXY)ei+ t΃d?':;4{$d'o&+ ֨-\5gv2UA*s%]F+$ #\Ԍ %*;/?v3rlu@ A9'u&Q<ﻳ߻o"`"49Ġ? ${ Յ#&[I=] &ܩUH Í cH%"Qbù?OZtnByGPCeO7/0y2/CkQo>hW|6=R~Y:k:U‰!zڱ8E-|`lPjV2D2۵yP*T0W#).~cDo R89`.6zk*{\P# F?nn;lq_]]BʓEeX*i风q;iX d)5` }kUfeP*Apby] XP^Gnη.$˾?և})6E]=cݭ>_ q\cHY,?lbHh` 4&g[TOpouzgD-&%˜  퐴T.Z$!ճT<%v]@ 3o08ط\,B(Zq—C ,>9c)IE/YqEw4 yXR:2g}\mTO8|Y-mUCR;Z1*y\eҕ+b45=ABP$Dha0JА 1˯rcr)=V0V8Hgd^BB mt\%$-ʐkPݠ_i۔rn{LMǀo媀~ Pa0~=o;\syGNjW[Opܵݏ]I7Y|֭ ܡc8 zA [l+HSvK|ݭ۫q'ʿ;Lb] R c>2iь?!uXr۽~Fxc)3j@> Z$z vR Wr RN If6R;4B0ar'%\_n uld-KRj;E-KJ3ApSϪ{DXɗCW a0|zp&??ӕqwnU aVBOFU\,7:Y}^on3}+b0i9z72)َacY3/ ,c *VA乗0ǔ|gYZsymfc-1.6.0/tests/full_fc_Si_111_fc4_4.xz000066400000000000000000004410501511276756400200140ustar00rootroot000000000000007zXZִF!t/`G]Aט]p#AI]}_%lQѤ1$|Ә`4d3%/ט\+ ̟s]1w2=iT<5ѫBb-R֒p͕snpF:vzGㅄ7/^J?;Va 6jXFo@2Z1m6|F՛]R-FF;3{-zbMcֵ`n=&GgdJa%Ƅhy3D{ nꎴ mDB{m [V_8UGh wJWpjUqs<=HdQ)k1m6x:gQ=t 3SЈf rli2 + 5Y4[k$/[?QiV<aMDbUi eOZecP|t,dw[r&Z.d]ZL<şxn6\Gl 4z<7y\crȬu窼\R&`!GUP$fA<`U;0`&lJ3?l>RCpiQϋ٥}au syq37LLv{_|a j$Y6+1f_i+)htNn,׌'c1u{Ǜ| 9uFQ+9qD"Bu@ƎmNA] ”'ף)ܫsKI-9OPsJ࿯~Fjjmtn4QZ`=i6qP޿fkbH4BgEy.tC;PJ($z{w?<2MrK[[88n'f1zcuv=zi@Ƒ՘$I NXqb> jVc[E~ 1iARԨܤCft<* =fN>Tl|1"y#}ŏ3-"h1t$qHƢdiq I/m iW~":CP^_bRjf#suS Ez^GLڴG:ZN >LZmN <$Y r 1i |{ŶVM/a mLJ3\b⧋& ]!G4ސ8 rQ>\P/,⪬nYH~'x Ch# oN*&M6[K-j!;0GER|.xZn <u5-9@9N| g%VVh؂ͥ?cCzJB(AZ>VNj\,!212B1rgO>sT&u~ju*a;Zr^Jϩ,wd6z+W;,k,l XEA2٭688 k n1MKOƳdWrMUKFw\ʯoމ7]Y[, 㡬DzyMxδa2 ͬs$eR$unERO-Iَ8|:*^"h#?A g*l`}M^v&+^Zik9A[Amx?6kQ3;ȥ)!tq0SqGZ焈)\ar5(QS MU0R,N|tg qL|YR몞\Uc:R!5(0P|%{cs5c1b9bJ~nI9(!;y pLI+dԛ4wgk}??#!iX&3(R}SCE nL6ɜ`f6=q=WlPhf [@,X?g6=גw*K D+rE|-㷾#c`{tS]FT;$yaOCISf #9r`0& rBrQTw1 DS4Ag5_tBT 9Fekж'2<|o6Uīԍ.ׂt*ކf'=\7 qa}M0NꐼPc+,=<_,h#۝.txy>H;W|/7 /H^gH&L?4US_]WrGqTY3:=OKdWZ6aę%u+:!;S{Y멬:on~1#/=,"Z]'d6^wL3(m7#h?M}\ ӔM&" h\u2F!ۀpQ8;F #N껙KJABk,0*J鮴ϕ[Ж-ܘwFMӥ^1 /F$:0}FM-d5 ڕzIw/"+*P ~~GrchTk:NyCx._3^yBܩ@pڟ"$Fw/w~LBy?a 1GL+) ]WoUEzOMX˘kw5 &~cН2%m,ꁱCSMdLP$͜)€a vЕ`pI6+2A\*o o·qW$]]Lk`S7ؒ0gm[\9c Z.R ̓?hᖛ3_mI*xq"y(1e-RClV I1m {4Md Ö7eZ`?֭A@-2 p_v86Sv awk~X }ٞ~iHVWp_gst% }WrpwXL2P+kc}6C~st騫'5jrDl,ܴ Gp'GeHe$MP\T; 2I$uqn!"cYN9NqB{Ϲ"ϨDc'+ƣn[_/?\X|l% L {Л݁<:,:AbDukw0 ]bc:sɔt2/jg{tα,2SB>κ6t{%jiXZ:eEmgvXD=C3>!|}$q!H1z_*CjrЌl=lYZ@S6P]DzvOt!JbUYJi7\ުOqᦐڮM/դF{Wlz$LEAci:ƤiX2xI_7|mp Z>yt- P-4HКQS JT0hUK/"7oq"Sę͛ߑ4wwp@ojy# ,p㙌Ϊ O1,[ot-xLUd ݉*g!ˎ"{@bOnrcMmy:ʓdE1dieJ3p~BbAr \M3zI`/)2[44-ZA4#^`,Es╆@Dcx | 4u@IKч=iF%yߡL^]]PJS2 ,DQ &y)6sYOˤ2.áIEJIZvOrH!vPm/Gtz"?IVFCQN [@HEv50@C:6*G?i58遦ذ ݖh8NW>Vϙ4]I`a.5U zSCT0pmAF4} cm-`!I뫝vDQ\ #/npZn .݃r7q+䒺Ȯ"Qz<.4Ћ OIf4ã³J^o`褴[I2~E;G9-@ѡ~t`[NP ]/촢غ5d,:P2aWkx@ y[pB|aظTO^X֟YX]pSQc (}ɯs=@R5{`I e)l*Ӎ̑$낅KsyX9ML 7pȪ+5rwqiu ӭK".vls3mT:c8j)#zI? -e9f\zEۘ)L@w,HLW^&R;Uٔvep3ODo9l-lrP@SحMJv"߮Fx1x7D,N>ߺ6_߰a)(Kۢ)蚒}/>猴! 7"MIp<'1x2T~ ]K=$YRvLn3BtRDEq萙]~ p|h1n6YFB@Vﱄ2o4 _G8Uj<59)DI]26v|s)XT[K8ߊnڊ66뛠O Wq|LY-]kD`gLo!hYh}>7,ӗ;k.Ś/$Ij6ukJݨl,B|Cd ~`_bV `ZNO m4quǿ\),"߶pUC~{NT;#(.^(tg@@0V#{+,7/&ZΛs?ٌcx0~)IDrن8w]^:;?@_XAW.ԗym]J&%h(Š#3m+rTK'9*D99zr:V:,䑒&2.i橑0cH֧OC_Fñu6Gd 4 Ӛ Yq3(NKF<'.aa1҃5knZ`\.Ԫab0 >pt -k:,vVa"}f%/_X#(@:r=Kl^V$҉#P~it(c)E`YNЪZ`)2ҋgUuCS@M?g~q%k;Zo-\#AAKtzwTca$$';.p_13een! cΠ}UU!:5 aX W "ҍL M(weS\vb)JҦ~ֺig J+(TX YGɉF?[&qK<$j?- trSĮώDp" 8TW*6fƣ=qsHxY~sn^Q^pڞlEz pʫ<5mPݻ М~]6R|Wz'^[-a&Yxp7|9li1PJՠ*6 9)Jc}DU9 PNI$jc%5ʊc,h%p6 t2jyH 2IeLf39jqK'@U~g&<T}҈Ӑ|g m P<Gg.J}QU'9K٘tP ܍,^\6$~H.!9z@0EgPAҞ1sNK}> y٢"嚺) Ljfy]}Nqsd2:H,Z'vS炦8S =/6s'h:~B݉.2 `dv}O1l˜x.Ș X7* Đ#N& 4C@{NAՀ NI .Poc xll[I5AR*ȯRƻvlغ-n $agn9/(+#]NHQ7QBt^32 r\hrѽ|X|4/}H鋰Sb ź\ 9h[PS#gx늊RY"X(8^ RӸo]GX/߇ϱeUΘh¬w\>R}*CJ8'[ۻ; Ϲq_WLkƝ)]Vqv,X;Q0GT~E8 iRrUr"98uCoSUQyz~+B@ Õ e5]}u#P>uzc sŷJMAF3TU`:X=pO 's$On-n2(C+6^"B&N~/![OE ):WHt%-}F}F-Z)BDA+ϵ2p 9Mm/Tluں8cC H4Ja&ȔoA6Ӈ/&bڅ[ 0u"U f@k@ߖIM.0lvjeiC/JB49w+v4WESG8< $@X"Wc^K:qԔ IFJlÿke [wTx,(_Go!9_t8ǩ]رKK޽ [ے0o?\Af\az23Mv jf16CԦ/X|[[ 3\CAl k*\{|LT+cZtl/x$@q {&BXO9 uTo ܊8K.3mgajyQn; Ҕօz$_=ӎx@luwsƄG}CoA[]?@j+ɲ|^˧3\)gKK^ /d{$đ/n0Y jJjF%&HÜ d11 M k+E)3)R.6MP͔ 70ː}j=LWzQT]tVyXU4GdiaHƌ 眃BH: aΎdaOlLߓ+>#Gy ˉTCKm AYwcbG{6؇]w08 ?EOʏ%|yJadn/p蛣gB`z 7ȣ{nՌ|J>f(x٥;dv(Z!VZ]v %z'[[e:7cgT>#l!]6LT/8oح4~](`Ke1<+yX\;S#n??Qn~IonTOԟ||Ns% Kh8Qp=ܗ/jg / +?c`#,b)k#èmN1Mp9JIؖh &hPlzӌ9|RIpc[0Cx2RXm ["jՏҀBh/flNv  CN]h:FSڿhEAV҇F=uU5FiI%L֤V.Y|1まvl|y ]d!:ܡlj!6:W\05+`s d$0i*?H|x쉭ܲ"D8<.8٘sQ#Oxw Z:9Y!ui&0"Rex+Iܼ?WHO&jZ5H h_ykUX}=qpcAwޥ4rG^1OgSRۓnV?"Ub; Hyie}QY/2dVn_aX&*JZQ0;xCY OnDxzaH` iZߐ=q*÷̎i^bY m#icJ.Yo%Te\f,-/Y>,3}-ω7~nVd/3yC|% j&tⲁ"hFlU,\N-l޾=dHb؊ZkzFӌeE o {6V7Oa$kXÆc(y SWӫA<~3v%LPW)&W5DbbyCv%ޘ:D'&)Ƀl2:-){V7_2 R-1! ̺^6v{2FVh<#ɤhL!uxwm >=ũc=E=yβIa^(!y={ t1Ģs_nu'v@ЫzG'Zc^|]y pԶ<{FDoiN|-$!>KlBmO>S:bBo%.g6卼d3k _.ΊZ7үqjbfLţô#(u4vjM7Mh/ LJ;D:q"|u |t[N'RRJ{:BwV 2ƹ&BypLNj YhxT7&_ $fU٫D+̑Ha)YoE;3햂v)fo8E+4 -$'n&B ÃgB.4iTYXA?טK ~h2%53'\U?3UceNYiV]T(k\ӗcTǖд~~ _꛿!&6% X>>Le51OS=Zq[ӬzWf|%Ug}kѐp~mLյ4dR^O@O*ƍ Yc_NyW]'ZTBD/\}#` BVfzN=&~{I5JkHJ)ls7(%tUn m]!~\TT[&jPkAڝـ% -Jm3f1h[c LEVBӥ@DzQZˍ=9p6E KxA3FG8jOxR\\leS Lyw>FskWĀېε,۠4SSZPHf}avۻ4@pM"1e7ȉ;t$MQpRQN;" #jX ̎yJ's)sΊ.#t}`~kVV$×^ڳ]s 8BoJDpFy\f=`4rˉ㻽 #)݌0D7oZXv!.B=x|>;˾(d2aȥloTI9)i!ky6|4(376R9zYmlD;/=[lwQA7dPʍ1zR/mkDH_Էew<:C{wI6Ͳ6%v.C4jbF!AioӔ'kGixBƏc#BvJQîjHq5__Ocz=hcD9"ê:۹ѭyy MiXPqFv'*i~㥏0&$G, {ZS1D+ ]Qߚ/hlbJnZ##vUބE j ж#)$j$lt)G)n!hL@ž*|J\r̈t[i2SR >-!^t01'>';&NS >DyL9uZ45otP[.Cb 0 \ں96* mT"ףz@[ྤZP67V6fwLft@J17ej!A} ݋?IQ00]7L\%.5'0Q\iLx%xw}\;'z $-U=NK'|MVzx(U' Kvg %xD{4E|,moUn@ӛ bg >ߤ إV3/7].m8b־k%sio{<ͺ| GW s뗱{0~I _/t?$f4Z$8| hiV18`_'Ө.X܁?9FS%KGڅV "O+ Tr]5m )זVxp|YB@9W;mð8I`@5Ly ~9w hA 5)Ԍv\?й$`e [@V$k/u)oxz2LiPAۮ5e\gFՙj/Or3ճj>TCy3W_q4|MԹͫb}yC},>_m3*5 ,#."~IeLbE3rHScKRechݕbG|lf ;K"bH1j_ҩZG#7 \a`qqÌ}d=]tQHulZk"~ȓm%J,Ū@X 7Kw9W)<(GqTrf7Q*ihD8<iXCq$-Bi0[ v!ǯ[51a ۺ֪Aߌ] tiD튨851K5{~.Qd[Ѥ UX_ ")yo+jIb5ei+K0XuO/§Q| ?qz0w\0]97~* Cgv1`*ɇ,2pJP ~Us=5؅7"nE$ڧ>qvm ĬY <\j0aVKL8mnj屍f]į؜59B4BvtPֵFb}ګbO#-G7ژ5e5?/(mm,WASϯ<$&E|Lwrb.S"_1+>iHJ('q Xf&/XJD4h<5Fk1yUL`|6!grz&<`A1CIFU7GW]"?xM|Fj ~օ#LIkbysŘh{܄ک&Vcϖ&.fv{fG{(BViPNd$ygͳ< ED O @9h0P5 ggfEw#2P8WCH$9^2NՌqnOQ[hhK9or[ oNL?Típ`M|R%ijBOS 10ޗMq~ JiPQA x {V8B8iŸ{5?˹&n.p0ڒThf±w5uhw\ee)4z W 5k+x)LS}ɗǷSm|,d?צOC  x.մW4hCOϯ0`pc!Faȏ;Cߝz'XK:odFto~Y:ʒ-2?hދ!/]edQ_|vtHt?J7}Uݏ&!$'g.'D_'ۉ#VD9)YLRn&[#z(ǿ]YH*{} hhi_q#Ҏ`dz2auHtKŐ=,Gό̠,{R^G38;},i<' -`2/&A@Q`mq%$oaX]w!P9 |ZLZڮ67%\;fmWu@Ϯ}GnNĹv:#և~.c(F%b$*`:RWIF_Z(OB[K`nUe7BSFN}_,]"V ț((Y; ;t1Mpn_Zf~8dt܈e;D`wJw4`gSps9茌"I S N4"dZY*Dۢb;zNWWauxV(sNR%,/W`}S $|7K. i0B#+X D*`*5?b6y\d=Tw.YO"j>ؑEhVy @s@5%ٗM@W֮ӷ^o\Y,ȁtHm56ʡm`Ӡ7ɒP".voR6C$? I%8։"^#wTREfy8Y$[tgʢF e\| Lz?&9w C_dڝt0Ki|qEpU&t,}XHt?ױnI!i~GikOù\ߙ!u%&_ r${Ǘʂ[!,wܿ`|kʱFQM~-_)lͶ&dB=:AF/lzH0 Q+bȿ[*LhZbw"&ߨbFKi0餹k' 165|Kw f&A5UK3x͔yfnEc?Xǜي+2 0Vw ^wv?T @G0 s^#SV;*O]&צ*-1Ud=(HQ `ؘ0 EEn;mow…#k]r~cWG1FnSr;h4= cȬf/[=FE@}v+iSs='1sk}LH8x͒QQh;1䙸E%`GOŬB΁ =4t 9*ӎ;J4-5Fxf~q>W&NQQEo$vjϼ-^Ȇ";@< ū=k/7lqO7)OIE嶠-?12UߌY_&.:[bnG'hMbX^TX4.6ɢ9}#qόgg䷲ኲ+nq16MYEp VYVi_fw8DhTHv9"ٵB9]2d({,eJH0Ow%FFb[e3a<¶DԦxfUGq9ɏ#@x46=F!cɝW MX#-Y "n Ǧ2TC\1pen:t2Ͻ`ನp.}1`YRf'ڤ;/3hyPԨkLeDv50 5h7Anɕ~uw`JxZgM@vw_͏[*ՑQX;bq>:`D=AtxY}-*$\^nWP 9h}x$VauE7foRt*58u蜤l*r e^0bcO(lF \,P{ NXl0dbAhIQdq70ؠe!*ƺ ,2Yf'0 lb=05mD< z!j<_9'Лݳ˻/k)U9 ]J{ߕ8َ?XΑi1h'ѷ@{`ᐂ\Q4Fc%=$M?ȟmM1nph !VuGm~QT0pZ3TP؄ouwC!#,5Ԝ6c3O51Nhm{Jm2ӛW|;޼ X}/Q[CONDzH &]mo""T=뛙Ң`'ih$).D%f 2O$v%"t7|UvNd1$%.E-rщLlwrliѱ*>38yHu>Z4\ۻ]⋆kk珥'EۚAkE`,{􈬗DR+D,=s-*NBQ`񥗇=c+Gpܾa| 0$,6rYz.2t :s4TTiVº3.$l*P2hk{ƆeR ӑ{6*QM;K Vl%G5<)YGY|] `veqG 9'LR 1䛹޵/äS@*ID0f."y*'*!mFVrRM*0HѷG0>[(or$Ѵ7sA^v#W>w._nԬ.n\3ch<Lf2AE+z@wmd0;Y*641BcJ]v<7GHRyȧbZX{P{:2)?L$O~@]L[St0蟵1>Qj1qYVo,P*uX-Z@`ضdx{A`4yTnLJhX[`N:x"ĺc Eq항?%{ǵCÓJ ܠsW=؉pBEMm 0*?B4j)"7UCe|?v2q?J.UR?*ҹ$c2q0跨貂z ߄,yk;rO6W;w[#T.V=_%Q5|\1^GG3ŦXhaEyt5{=@gp솬(i,h \֔7=.@xmq! EcԔu%"㖗6eAa۪Bo/B P[+|yLDǴsf"ItM8糈{EJ$RWv72Iey9s;z2U{t)O?TgaleTgɬKzw%Cj h 薹a `J_wK}I}UeT._Xn9k'%+'QI`Ͽ  =j~!2P^x.ڋJX+u9rZp w}M p,C,Gu\nHebr$&bϖ0)Fl.Hfc?NMk:a76G=Vo3)ID Tė Be];jlQ9~wxx z!?x$1$t{1}*%&x|~_+ \Q,.ђpj^Bf]Ɍn6yKp& z޶:HFy'|WyIobF3 wqg+,LrhuzU1lC"Ã`5TGiu1Loa/N(f9ҋ4&`vskg:!0Z^)RnHBM'UgXߍ;/˹"M?wO߷"7߲a9,DfɍzoI[!)^&?cd_QNa590q%fu0`( bJ38jI /EZ;'O~(&pjW3@Fн~:PbXA[a't8EHX~N]ͰgcǶq$@qT^Ԛ-3?-Ј"cbYmm|K zנ ^0m6/Ovf|4- Ԑ!ȹ|A-Da[7/Ɏ!^-V?X*k9  RiKϏ!fT@OCϹ|]h[`ks/e0f+J 9HaI"r߮$⹛lX|AK`B.{Bth\z{ȰEs؀)W蹏S1 e5/|R =owd!td`KFv!;[Ιw) B~% ą"ȑ*ܦ7_#T=HL(>Y|_Y' 3%@ 箻f#MuŬf>GN@}WA+0'x\lK1{N N娞4MC響ئl.X\?C^Pj#whTës| ,m~v[!/'a@D#_m]ltnQjN- MBl"=5TFs`w b̲?}ڣqZ6j"Q;7> r]kv{;'(.e =btJX Lp{mn HZ)pgNjHW21چR3$ݕ Fau$wn3WmЦGճ/=k|2(uݬ<z"A*3+ BJ*5- ;,#H*ojJg[L:8oQPXFPς1++1fC_ lħfx!GC0NT6x #+?_;I\Y]sC W N1fq͔BԳ2rXBq*)Kokiy.fKY )vs߅3p!Perw&1!L{גn% eY@#VFw$tq-YNU+9Y A2D0O L2!$ps>xTn)XQ2@/,(c͂Ǎ:Tٱ)]ס|E&N_ vL/ث& \ĬJ3!- W?chk. J+5.}*!(¶1pgɜGP;$!yFO2f %x>jCxa-D=T-"o!~HRt>j|-_|yk}>t? [4VyJ(YC4cKN7ުS!wH[* "3MCtfbj"C;Җʬht: /#:lR@Kv֫;nt}:Jb)l6HR+ZfGi a4zUrѝt]ՏP^wNr{D0hTus29Զ~ DCQ. /sRX>Ph̏sq '`)&k ZZE^Ȼ>y6C^KϾ ;l5|jFYyutQ6zT7wVA~p/r1JI|W=d:p%r wR}4y?g2g3,xdc$I3Yp ݷXR}uUTYT]M/bXS*Rsl=,+.M:/Ψ +NUy9L# z\V!pp 7xO\>MIhMɦz׌>ӆ"C_3uzC膿%3&, -_W Mԉ)ݛF uNRe5%3L_Û}f>|!6kO6*?/ow#MN xʓi!4MYVu.WUgct=Yiԅ Df@ sDN7eOlM>GPI/Ϟ_Z5=̘ #Cr%8<#?yS` l [?V'П>: AGН#Y ԶV4.| q|{ ٰ9 ~ʇ5_/D+j G{ XFs:YtaK9Z{F-&(Au^pi)Nϴ{L|3Gw3C+啮2dh~Oj)/m_yyC\e$j.O;ꏑ ;M݌H7tQc;ݶσ=F[O{ QdҎ79_ `H r1H(ҦZ>}G媸 VEe iq{r`;>~.#rr ?<-MhfKK~P~Wд=L*x80WGvf{b}/"iUH[2mk,%4K*)X<; W 寳&}k2 Dx5KGkC켻:r~X|n.ET7:4*{Y6B6qQrWR& KJ\T9Fb6#Fw~m3Wf |޹혬%d;l4]p_s&[@XFVڕidZ !J "wkȴR\q潲~f]d .בf-78~Ͳʷ?_J,ye\ ?`^g*^r&涉 .[uzaW#zhgK=g:\tg 'Bt5 \Hz4h(Ty/E=a$K7wvs`!NƉcDBl3[ ]M%iVQ.])hm%,p,sf#Hg o;gtȊxCUlʽ*Jl" 4M؇ӅaTJ08{JH|l7891:PL>+Cuh"e;vg}o^t'^y'꼗tP-fb6vCG|BJ­NXXC&I}?BT KHEN H4 60yMic 8f{[r\wݓ/i$_no̴*"q#:߼"-ybB4 6>+~鲠 2ZW^v `ÿ <({KnQ=Y{BvZٲuy,{( dc`Ff]qFF/0GCFS-:=]O]M )"@36nsnd ,$E<%x 'iRGbDҥ{^W$=na(i2goҸ]98l ?7qҗ@z$lZEؙ~5>D_8mB.|gaF_Zbxx[5oא=s"9 4J6cԈG.PQٟ\ )h??$AQ<0̓Ո _ duO $e MN/&BXb+zx'7B3TWxN*dYp1C4XByGD$=3\6m㕭RֵbuV6 xzE"0E ԓ>(eAoH׌"RUC1yR>PwaZ٢A=Gr#(#9ZhSJ}#́i>nmh&Ŵ\Pt`m>8XmHpU[s`@ilPJQ$( 3: 'ư|Zr7t"0wP?#}Van'ˊ< r\meCG i@PШAuGe3YF'!TQuj&h xձe8Teh ]R}Q2C=; =i(8ȣNC#.·D\6,f*^ $ vi,.pؼ$v]`w"X!3S/}*b:f;_Z,e{Z.mS4e9K4gZ2*\,Yz"t yS)|:xL<^WG[C%Y`)Sf$?QꔀZwj4~]Wz,_?;zL=nn6[l(di>WsJ[B//im`>gپG;- ֎r ?Uy}yo)ֹO%l[@,IrL#icgȍ;\t V熎Fcjs죦~K-3z^UмkO M >v}1 lAKIi8",|;7 PЫ1U_6+mJK*\ZX6\kf}\]h5LS x.լgfDY<qM3Fl{6pBm&⵫#'D\&Ry6@EGϩ=+$m$Jp m⭮7eȉu ,q,7aaly(xgɋuU^X=[,Ӽ0^\6 ~48:{PLSuQZ=r{q|NFJ,a<26&US._?n+71p_[(TCupGrMdq_npuc1;FWzЮF"x/B88*@"7j=fwPB@fP\zlBO1%MvM "e{ovŷŚڃӯS{Ai\ 4(5B_xdtVtIܺ#QOy0k4* 4|e60@ nvf/מX͕I6%@̃/pʡXcIwb1;N:ٹJЦ&m_Z 9d@NJy+L),e R.#8 g,w<#Wᩫfjj(L:Ul _@TLnrI|C5A:H&kxI,a9Oij%M.+ky_:BƓ#S\eίh{쥽;#UX`d,YpT(V?<- >sq ?dV 8/*6\kq}QM 0r*ޘP )Xf? TG]-ɱ t{詾FIrf Zv20 caհr9h0*-fZ?j$0w.0_p2;9ԈQ?S{R,{Wk 2ƽ(L;r:dfSgPJܳ+y#^]b5< \_ljrmɚ@Zj8;5JG1%! "ʖF:(?A؃6&K;L*Rr5&L DSy)\A0M;Pr̲zco--.Ơyv}T{"eXbkݤzMr]=x4]hVQ [T?_|Px(qM8kr~X>!r]6SJ"DB՚'$Zl_³+/=}Tʐ>fHϮф>F!ӟByvEQ ̟&P=qk*)2G! .$xU$?+\l9=|:F{Ceۖ&Vϩ\<SbbS^ɟފ,1i˟h Z9Ò*g:tUJI b{G蜰Zʬ4G\.5Ⱦ޽J`]A"FRmLP|_yݵZ9`.$_@d?<ЭOltSqo66W*X8fmt/+02"V?4[^$~Ә /}pRАNJFqe uu'1dwl>1<KL%M2N?hMl:Z9T,FW5{[k2=X'"U\wK(=v@v~dJ dV:XBv5?Yji>. NUo]sSAЇFȉ A)ʳVTW`'M.WYKjڻ*AltU3R({+4V?l@LuΪ[t,$ a}5{qdh¡"YbbU 躚LOrJ2|Z=UUVqR+v ×X\Lx^!/imbŰrcȞjGP`c 3._y-n'ɥ}t 3n"V?B=lle3ҧ dͻmfoB9%hE__ZΐwKdzL&6d6-^ʰ$1 פ[ߘ\fQT#)UZ \fg3C}GXd9PNH*w\dc ũHU|\scSUȑ@@%|ћa;xJ)ɇf)a7b7&v0tb1k}:%. g}y(q&ɖV9~@e%)}OZ$n1qQ'_3pemO UҨ(,.FI뛌CzcC=:4hh Y#u-Rj!88tӳPzpl(O6m+wX&P@ oxsUAOus=XA@#- J wOUiE8[x8ڽ|㾇doۙqFՐuح=?|$@Y{*#Jaj3ˆcMM*)vOsҹG KȈ6{&7y=Ũ? ##ىAF9"Iv%ە MViZ8` e'?;.OwQݳ&^N 虄i9ZW$5OTQ>R;x#'=x ۓJJ`{s܂tS7X2I8:5U' M&[d[G0ZE!c6NKʦ ΉhrM"YRL%I|AZ_=o L>7/!OVL#VSJ+[#gaM(`{0|,cw1Iak.) 8M?R$ 6|@3?-0ҌB@!h LbBH{*I% }W|/%7OsAw(.$YBU=NC54ߑVqe:da˄BkZ/ɠQIB9'>3AGjKN|螜uP58^l4Xh(UPJ(} 6CE)8isVtHW8[)M,?ɠ\qwS+.:R9:!y䥁cϳ̊[G *A=ش6zkMcYCo9#_c28OYlg>j{NїheO++kQS<]}.wқK >8a6)$r"hd _1Kd5L\Z!|,;>X\6L!jȳIjx^Ɠ 4V ;c1~M蓊;ZL4I ?MaC3-yBJ!H2s#6.[&md(f67 h&êT:(ۿn)Iݞbu0ϑjeM) EӲ*$_^cF6;yK8R  ~Kg/+aBd@ #=2Y'sWX`t̫>mkւtF*7am' xXwXir7tjwf[*3W & X K.E_UMy#_1_@ Ӛ?St m<\14Xf*.016UJV\DP} E %.;K#"ɂܓTv/rU 4Kcgu0E)7zd$Kϫ idND)U5JwN_zL7yn<,d2HYTW`2d1.T߭ ::['ٴ艤+e"4Yý8T8֤jnY7-Ѯt1W77;pjLcNz/sCR[3Jmo~_#,Zaܮ&.*Q?KJuZGk:לbE2Q!;$G&1)y!aEׁ`vݣzO9l\,xᛁSgIѢ] 4$y%X}}FqI7!aό_mcL|ƪ@X+D4 Pn,Vc仵F`}ڗ7saYޝ.]6&$ucņʌC@MJ]bEeDO5^QMX {182?}#We=9O;F(߼F읮+Z(M 0R(ɋ4G9<[58!v}$U]X{V ;>eZ1{TYBq~ o=SUynr(6C }ZuJ?+b"eiҕ_7;VAB:+\ѮT+uQ^8+E%7d۬zc&1OF+ooNby g}?A(Ukk8n5$tm~>k0eLm蛨zi&GkmB\ċ 4OA1)BZSsv#(Hѣ/ $h_RNs*r_!>  IU8qqD!uiG!;R1[®c~GSv˱qfz :kvl*'GL8V o8\cUf1m뜒]r-aZM .)WuI^daTurzNh~KXW\&{pYb B)>c.Fᨿ~ij$m_&FZ~?6#JN[d*6.2,v"qz_?'֭bR[!iv5s4gC9"=Tlgl-REH H%]:me#$B$ Ǵ{V_L"ؿ]"_XE;HՄOw¦JvpHkq,b1ZۏMoc0~Xy+'#!\GqB/ZLi =TN++z{lB a- WϲoJf_e#6~6UOq{E o'7Y.H r[ N{m0Zh$#5~%KeMLHy(Vfx Aze<(|d,T^}]] @*A9ÖH70W]f RmK lLu5!Y # DzP^eIL

S$:JYncV `cŶN1Ҏ%g~#JbzuxjzZBgeYBh7 4jxZ?JyY1_B5yUg >Puy7~,:E`a-ڒx!!RF0VBՈ,>Y )ɝ)Ŗ^y5 _7 ڔ=|`߱GސYo5#h"zmu| ^ ?W._'89ē|tLl"&0͐fVgXhZh7ۑy%/x"P}چFbL( 8ڋ-3I*9d$O vc^ oT2dNXaHDReAP lByud^uM[@"FQ!Ri}nhW{ׂB) )6$+5,.^\7H3U~P{P|U 7Ѓ#62!kmwʰt!{Դ<nu+U[>4%*a>_Xr9y9:\kqiRSؖ\*,-` q9Đ&H)G)I4#ן .`Ø6>CgCm, R]yf rKw6J$zg+}ܲ35 :틚 =t̕K$QQ0qT<'_U{KhAz2l,2ST6lE, Q`Y>x'ea-H*L_a1m'z[Cs-=ILWo4}hy ,gP 5bP }"G60`m=V;/NI2]g]RL)nP2iyX^Ya-.@"0VlPYXeFTXͫrlo y{/m@K3&MKAhn|3RȖ\s~6ZmܜbJRvTw z9yqгwଣ't[iaUԑ_f)aR.ݻA-ɬx\^'XNPgA gJ5z' JM-iA&pw;8OE0kv#&mi&HkiIU/ jߩ)&oYa}pVm_K6 j.0t8ZvceK#Ը!NPϬQDnk3_'+IbHf4iGGvjcbzѩMẁGc2#L*%A|b4O+#-[E1'Ňpɇե>.QN=#t}B> ]`Xi9٣VIKcx/fj諝vr54_yrPߓt~BVy<8]zql͡ChIo *l] tUO3r|CHϏØOqVaF4R`\Fa0 GRk5R3:4W|Z$kMSjmШS2݈l4{u"1sb1)D ,߽L&s8B4a{3{I͖[]O귄L'eEOS0 ߓbM&eKu5)hEKAR} [gVK7 w$hszgܥT*[O~wxRH\XmN40ڤ8@$Qn D bAȅ|nyY$e0"Υ=rj,> v xkC* C;aH}\(Z&Pm' 2x>Їx~q"͵//L1^ `S@r˰ Ŵ?C`-+>ʬ uaf*S;@.1{7* cz aLKXE/PRer쭛9r \ޏ5f\ZCF=ٗ IR]2c>h? ,) ԙa]9@ǹ:fBJҠh@ NƧnV7ȦJP$r 8e[}-EgPj"w!6~>'ɿ[Q4? +DIrrx{V't={cHg&p$DVBKD  B^҇ppyo/i@Tr/Y(RiާP;:Q X 17   p!/*]`$Y@:LQ=7W-wm;6k|& 7FTrUt:]5yFr,H%{"t-m3io M qh gdbn&Vh3fsɹY5FI7Dglox5O2:j5'K^{UP*;eu+toK<;H dx(Z@ ;?coBx6F[{"#պbz@U_!]( 3=ƸZth3 kť&>yKԸP~6UO L1ՠBhU¡1@m];!~S+K&LCS81YnGս(#BW@3Z5[ O?TA]!BJ{ WX_f8EF- z 1K .|ef[eD%nIު  ~ Л*GN}[<_gJ$pX7~xl9 |=/"+J?p"B{ L gӔS)Mmǭn )<#*ʈand8}le!GDUt2zaR>aQS)i:8ᄦl~8u~tKDeܾċh?&EsX ~4Sh_}ǹ53Uy1YS[:\X)㬰3A\6KY,'=iUg=Ccr!Uu09BW]Lص\lts1at"A;eԧdʃe$畖Ky 2X~:hZ};SmX\DE(-Es9 zkwaYu k,D_/O 5W9Z45e{'3"PNd+x=E׍}4UU/#*S\+Og;ĭ({UvϾ:.`DbZFOYYVǴC . vT_eXc68cZ|YωV<PNL1n{Ijt#7Vh>p{|-|%HgJR^F#~}*;m4W R9uwMz3zufZyHՆՖ$ f4ۤpSs:H(֎F.ez>לˈc_CZC{[g7:)[Êo?-bFq`v ZfI٥wz ^@\ TO.͵"D;e.Js5^$p&i<&8JA|KXC$D ;@JK`lvx\I^)tβ4+BYKΤ>(XViS?MPSVr݇Sk7ϲI6J80I }ڕ%Pi~6waPJE埚eM:mX(=Ӷl_<ܛiE gCna*'jԲFΥǀO1hp2e|Q .& ggPJ*ٓJȺx0d^)l^c֗9Xhݶ*&{jN9B1Aj= 0]gi&ӯd6iKxZ{4qAsf7gcF-4F.5ZeZ4#. ]uvizD}F[f뵴s$D'CJ]t_r# #x ZA2яLHҎrj2jS*G\*]U#cަK`]Ɵͨ0 jdDn[K:r&*SkDߡ۬-_ёWU:Vj 'kѺGUy2Aٚ_KSߪddRc"x+DTgTwW**\:X`bewR QD |@p8uX)pvv8CF -=)`CC \*QGqMx~1 k@0+{[JPvU]J_%Z6`RMn%UC~V(㢣;tHXM 2Ry}yA%)#D (YJ'dXw J;9dcgĴ0 /Ъ|&fy]nOi'$NH SnY7'ce3 lb y$)N) /z 4bY8q쟸a bWzY[Z 0+@[^f ̠\S,&$v+C(79E1֘ܖ3W3v-ǡ?kLYHx,Gl|>Jk8_V: ŒrHy&*-v0 0䟕zY}ڻ3W=iG\ O% ]%8Q,i_Ѓp˝6XRaAO+Bmm{R[ M1iMʿS9Ow{Ԍ8䠂š pCd>2ܶwCl@wg>¥x䀮ƒN Y @LkwhT;wzbplR!ӿȍyIW cJt%V}ZDSy0 (?|69w}XvpZWRzTCbXt9FA<z{ȝ6MNm/̇P F Ji%g=%!LS3w+NQezB=Igmݘ-1 %)N1mV|a7Kuݾ+AuwLe: 9-k ̰]{҆vCpKtev2i+ܭܓK iK폼ڷ;f5 UH+`2WDXep z'+sj@ #+2P"'Rg*v6]ݾdDX&r nc=z0-jd߉|o,{b RMX,/ld+ WʪJe]uЅ~HCeU&4B>Ac;a#O2Kf mD- 9$ͺCg?C_y`ك70?iBTGhY@lɒm3d! Dyp3.$ap7!.6R3n *jIdJUT"\@#CRJ3灲;`Xv'~3DtO2ؘbԡq|pmV97YE(̀P4DFVvIvThS@QIO$}%Xx&]9̯Ͳԏ `iblGt u2#F5o&!m56^;)uI }oRhCG \*}gQ zT啚!$6,$ 4 itPu 뇠;}W ian$3Bav@F2L wwhPM=\YFq p֥qqCrZ0z]{oMEnñHS$&=*B9W$fDڄ0LBhЩ9zeFA-qg!P̫ŠjPgRDX< ^5r͡ oG+ t3aG"w>cðh|I' W((PRLe}XB)~w1J%~p K": QWhƕy% RZ$-D/r:ᓔVys>O>9O KP;(ޡ,9]_';jfU֪ NhNҜS6R@ʮd\;Lf:ܖ'ePoBݖJmTukW Ͼ.O{ mx%rỴ͆K*pFJ@q)K r{DQ X_f'YqfX +͝kAޤj3#*sS-e֢~sFRtf>=\كNoZtVMQ0i3Grml$ GH96U=dښd.ntDjq.RVD˲cՁ{Ԃ\^l/nj7qy_PѨ8iNF>q-jv:TPJѽ@_UAq [#~Dgiʷ $ _)r_ l$@Qq#thjZ%!qD#E8He13;cNΧZ~ک'IZMe=Yʹ}|_Xmff)9; *ia94Ja]:1EBj+@Fpl9)*Jd@O;l To`ꏋa% #MQ? vO:Vɭ] }.YezE &^*% }~_X!_䊪&S=m?+~]=P'_C~>~p8aC. A]䒟&u%4c,t~7=R\!1M5"7(~~/K Vt;KO65A8DŽ fz+v"?Ke0-6/N )6>M+o6Bwgl5gH2OƼE`Y2KW:L)=JpQM0+APO.iqyUwS{2*k \D&ȱxR뼅4 ,6&KxLѝK܈U4VŦG-}#~5<B,hyjD?jMg2ZeN(b?cQ;(64GUcj%I@ xURƷGNI_Q0eO ᓛCC%͝=RrViզ܎"K`.zHaŨu6j7FSo5FDfc}{X(}OV,DM}SucI%4hݐ'sN'B/1GLoP%D;Eƶxl*r\;#cS iZK8G$~ *$WUQFRp* >az.Q AD$YLJF' eR"K9 `" eͧ:$ 蠥-YʘL9 4 _x=W:%,(-"^!LB3]Q^s_8M!}jr>WUe0k#o/{*2 =xXWAW⋲yHPR@d4)C&Ժ)܇lv ݀RvjV鉉iG\m*>rUQJ lrv\!\eh5.|7t91MhB.W3!CF(;gjCs2*t8az3iZi m8)6X5)0Pb.ha;*jD ֱ'P鼱]2?Udl[ B01χR=7E?H閊Z=,MUXT ѰV**Lʭzո\jq"h褀Ba\|4Qp@V0@6ʦy@) pǯ@7" 0lڛ1~Nɑ֊uz7ՙ/jP/#גjC[Pd G_MFI]tŬLej+ J_;AG+4Au]_䢗|1+>Lm 0(]/Iԧ?~rOkO 2;S+2lBGûZ#u2(ZaK'$Mo:V>kŹTt'ߓd-kl G$EԼ &(uF#Z`].kʜ8Ttq#9h2ƻ'N.PT(#DAl .Q ^襳A ]sx?+13Yʷl)qǢ#Mld8h81JTJ1+>u&0ͻAYa Trej侒&]E[V=/B9ySFNA-ϢG ]EbmD "7w㭣E"&GO;n~sOʠ:b1%XhOJr-Zq$h7\-7uh[#өv}o]'KѭtKށ#CG_<r6O{ ~ZwL?&+>KyL@X*rM2igvc v+[ܯ|ɛe).Ya! Y̤8"FdEpbݝf[#vriPfgA5:cId۵LalݦjZwpl}Df=_Լ(3[27=G哮 hE%z7݁'ta"s^;f QlmFVݔ8*pfa8}_r |/%@q{pgWj8hǍ1!{;IQLsBIn8kŖܖBvw+0C7F:fV"Ag\.{.0ּM1Fz_|JS@`㕉R<kQR`MG&/S~Giy'v4XO9y{0v$݂Z`2 +IM~ՎH_ 7jƍVZ3{P~R*ęZW%N{pEb? Y5i7ݼ%iD82z) ͤe}uOa[`FA[ҵQDfK<'7euB/4WbR*CD3P+3TyCZkI)RT0{T5W-^ @OM7iYԺ0kJ7e63i8[$ʥ7>)PzۧI2%1cIHLA0X1T_Oj&(!xu3`1x9@| _ew-)u9\ (OT4k AL%=k&'KRVki$kSe3-2XAK.мl(GAb/~m &jr~ՎPskTc*&~x!c~|UWJzŴy>#4G@?N/Q!,ZZ=Na|XH ;CD>C%%C`%頲9a~<$Q*aMGTq6N6OW27 gڣ>wPµ?)Q9W]3+.>3b&fW0?8iTaXAEb/?N-X;iK/N!S.DZW{p*у7aIDH͝J{;-$FW󢬺HSY0I¬qualfiuJbWsld%- .H#Z#LK|kpj~*Ӿ &g~ $ Q հ^ٜM_WLDe?sP@`3q,4v7p^Bf̶}L-M')jMGSպ]keCYA&I>5MdgS(]NK'Z0y ޷DBZ%yvofa`**2WXgV 35uDacFqNDQP?bjEUTU&;ĺoG`;( jlQ̃(2o$W UF&`5=i<9.W\rTYj>oA n{^ Ii$?Y™pehЂǝdd1NYNSik JU=T4cw0P2Y&جt+O^2(ř6X,td]^8 QsM_ʭqaֽ 1GIQhx@(fzSJʚo5l+aOE$s<;0 RP'X7Na1tD8{UC|+`ԥP%tml_1ߗ"(2@@tH=bB;:s83$Sc'I裗?!]v="i8.գ~A;VRs%:s#v-UvK.4I=6B)wW9 #8?@C鷑ǭSx7K䱭-Gd9o%3]R[1Zl+zt Gk;r} 0w[BH1ڟ4G{csS׺r1t42'Vb\^ȄxeΘMR&Vp:f󏽌5ܟmtv7zQdz E48G*uҥM++g/o}n~L[+q4`J?FE %ܳ,Q6xl\-!NOdrHC=K9U{"e7'UYDjlNlaxeQ YRO DeBnSj!pl)Bp`Z+} aMk0mمI]Iʮ8 0N;:DmVAMhxQ2 D"C;usLt<4 ԄLzτIW8AOۤL RpV/rˆ ]yX|K&( !BkQtv9iCU4swKy!ZB]Z&(Xud7BKD}UMRRgIBb]x_qStͱvd_2~` KזKUSj+OT;PS9іA-)Pԩ|fi}w.rSdĂ3>VP5M:_[n h?ba*]YΟYA+yNurɏAPcUM|;@L}Bhk=i))* 'Xm+f%*fj4s3HfTJN A {sw+UXMXl]ȬX !/eCF/Vf])c&To־yav|M}ĢH{Zї :EP 2 qJ!dڛY]h@5)ZݽG\^g4^e 쿇-Ylʘ :ۏ iecb}{P*\ HX ݯCJk zHL#0)Uf?[!~P>>bӭ~yФ/%qֆi`D~G"bU mܹbPbuAؖ^J `ֆbjxi[3kGք6`]yO"Quj ctQNgb7mb1 pƝ@,INKd%,U~ǚ%e~[BGJXF$6d,/vc8/ <)X-}5] 16``}ٸ9޽s+>NOٴ`:KJƁ^BTHmʰ|@p4vbTY4`^l-jiWJ?d.Z&A{VsK#<f?=H}9$>3HZsZ.p&W05!| 5x`X-SP$?)ψ+" Ӧj|M"yJU5[+8p,q L 9B;N]0TmԫaS}gJ`wxD=Ll̓Xݘ=ՓhsQE^)y(H3~W$sMb]vݗ3 Nv{|KuV(*_EQxW:Q e}=|JQb[+>> 8E.91'Y/:s]!?l#c.LtQMhOgT&U6.~n%fq 6åXJF0.a'.acpӶ}(om{Mq0nx*eTHlZxoq[V*YZ+Z|a02w_1OY4DQ jS+,G+jH!#D{Rj:{^06Ջ@jɢBRW, C.ַe32CU%tI] Ubrb)dwķslՍkTwrW'|睱-l\ejy-Kj ae5 =u2#-7=WƨX? n|vWq3YJD>t`:DgÇ _3|Z [}Uю[U#6.UdIqf(Ғ.%T3 *kY :^^d:s. nd[G@ȧoP&O% K.#:*df`nTJYHV;7JƳ5 g'~qjsy+Q v@̇nJV\1lL{ph\эAb?'(h,CM2'/dv#'UW0jFų#vA;`ʟ{ɷqLOypeZU AI{@%S-MSLRD=yK:@:ݿ[KCȾ6Ev ،0|XLnL> ȼ,]` 6?Km1-zx,#$EuPXMK$[N}8oPz's8M~(ttyuc ] ̿அx19Af=xEf"dNaLsּ@n@FMURE_#h$es w\#N׿=Y ox9Iv5]`3*p$&}ܫG!\xT`;- ȁ;7Q#W<űnV.!F)eN)Yi f~WMdn@c#™b}E-OY0Wɘ5=G~ufעxMei̾@=q.S陸Af8]IۧoÑ#Ҥ]1F787V7&4L uѯ5Wv Yv =rwDAI*|`b`:N%܉)@;*Nc')By~ן3[fRq{&-׋>YY֋|b,^wBuN0GXVSg"ɜ́'֨ˢƑG:6eea9y}{tS[u/bKhHN9+cεBG5-ĈxC8OնuK]9,V`SS z5P9)"Hv/? C68}I@k:?]bBG*iJȇ꫞4Kirf#%IZv{Cg"8eFXLAa| `)<xH:M 2^Vp:eu]~qz!d" }HGV uT(z C1e&KKNKRcMp<{,Kf+*gK![5nȰ܇a *Vk_EyWPR2n~EEU+JTלN;ovNxQonN=$Z "j_Ђ"z Lқ"/Dx  N9x4G*+(TDU,: OF-aBRU.B~)q?Gv{a x6E?tkK]~#9W8ޥ71hk +U*~M*n8=0x:> 7V$n E-l0Ca>-a@eav=Q@AgYfZCr;$W82ag-0n<$[pJ?2NMP2x:]g9CzYWIdm,Gr(2ꋀ1D[YlrHj&{Q܈\skQzꠠ D]e4x?Øc[z[10;_ŠCoRz^E_8lЍ tJeQX7K \WS{Qu6V3 q̺,}X2!]p~$ا<%cA)*J#|_J8lSrb3IjG)z@@g@2"WVj,s-?Lڜ׈6V-+?sQhSU 丙WTxA ǎbCcY=VzoHk[:O<U"Mk--)HC,yĂct*0<䥴wמ\ę+KK9G0qP60K Yjn[iFGĩ@(k`bzSᬍ A?*d?a(.V+=7uA"vIF+8u(J~J H€ %@ ׸+( Sl 7/K\2}hžkz1 tɃt T}ې(D}.!Ӏ; VzJۇ hUڨ;0$Wο& /XU,X d";Oq:$u6ԙ 3v[sn>X0Xh4LA.+K2M,Q1RT>dqy{t27x /{1[G mRf`''*v*/yRrIxǀ{FFG~ѷ`hrZO[T&3( kZP ČTIcet|G#F^oz n- q_ٳLlkcm0;Zf-M%slO/T@r'}B9Է<́r.}q]f>Y +JYn'*֍`hrxF] uFR-Sm3HS*?.X= Qmopevf_ri`P-GЅc9W#c(Hy+6MRt,ky0:̉M\M<%HG$ݗFzczS=/D_8tpu{% |[:sӶlOl=#* KH6@Md [qm9Lg}eNOù[ǹ#1IGMeFh["a1OK;c󟘌 ;-\&ٕPPhQc-PAQO$+6ehe~_>l) Fh< Ң*Y%\A\ ʸ!N|[? 7G;g@Z=^Щ:o ?WG6b+M8(Q1Bx*bcd>_fW:`sG ^Drw0r|Q+{*ףHE?ws}'-,'dl'|agb:d-0e 92P> +p񅁯'-]|7g4wv] +4hk B@TUS8mva_`'"w_('y?_P)uSYҙr\nG@xs G1nDUO[5do@OPM#xG RFL 8a ,(KHfNF|8JH۫EK+72xF-[5Ϳ 9  _whNr8m'L %Ǩ+*$ˏʋק˯"rIm>Z3+2Z2j' Y $B jCP:SCBބL[}r߹cD`;Uyt}tT )[*MK+ńh?̷#-p`Zڔ-NT0&Gq!m4ybIXSh$V^+֙‚%G{&J:kgq]ZP]-5n4$AgmHZH~m u*\֥ bd%f4z*cTFP%)ֺNuIPY"am6P;549pZIA7s¹PPOۉTDحͣ Vs\ߑRO8DH^p\ny҅ko6vCiCafʟ0%*Acfzuą[4QM,6A K!T땀tA~,#R=%ɪ"+5Le=.FWtnj 5,Bׂ@~E\閭<"`V-[H+fiȢ;C vrH CjdbB`툺Tph[ ZgBx7LJ>p@^l!l?ś$`ޱ6w_yW#Q!yv ElW$y[TXK`+ڎ ȖdA۲X,jvn 80leңlob▒NJ'0sǏBL2/$AS-g)&^|z%m={7{Zpo!3|^d;J?*Fs%g9h=|6A%x1ht0r- MGQm4qg?oe|g5Kk[`9 P= CuA^w&f9n΁m:5r;g]'!&yϝ/:څ͌:s.[d웒yn+$͹EwiJ6(7kLX. seLCqWm%\iTgV ;zS2 fZxa4$\B?b-Yfl. .걁+Co1G+4Ah-K 2mZEND@e|TYͪ0B2\$M;po70pU{ٓ clȼ7yϣSk_lv_!"RfX32ae5;ylW |fd^dC5 _)O3C@S:glTܫZ/SogVSu }pp#x>=` Q$#||ن[bE%K9")I7_l<):7W0Z^?V6pQ5Xnc$dt)!Z|Ol>S;4ZAz^L>}F.3!MgXK_$e[NJq*(^2(AM T]`}C~w]|/8@|AC 6*=Qe? Rz幭|t%6< YĚhd #@2a1[I%<>ރ)D<}$٥S/l_M;G$嬠%:}ks)Xgk61yK}GG'h8oS7sgƚek~ي쀓OFܭJZd*@sO7RnIP:yYđ̈q1ro 1ΖK F3, >^L?Dh$sY= {;%bg:UH㏷m U!+yCStO.ӜY'b+ODdCNRKK^&P~4%CCr H/='zMD3`(Cp,pRd`)LvU o~8>%: 6(U^cRDGn}*͇-x ?10A¡31+ fel$dEh+G4ҠxSynd`ÈR>Jq Lܜ,s"-@f4a5l~a'u6=q~vZh 9|1]֫(+CRd 5)> C{ JO2)e"-ᩑ6;tMO!`fA| yX_$}0 |#ScUIfǪ6L֘Fb) [YoKjh)W=<"R]]@SYɚ-DI神v`j 9׆^:P*/ ^,Bҁd-yIXV`7U}ϸ4vϭ9f2 A*x~p 2q]nQG3~ʻ.hSRzjz/eh7J@Fҍ^xڬ 1?,q3|tA$w?6+CKi4W7tan%>Jb^wU}03|RvBs,fuR ܦpW.&=5~fv=:fI3‡s擢bzBrbLhP o Lz2,& Lh+}&$V?>ȶUA;g^Q :{tǣvy8W +r逡!իekaTG6Jۂm-3`%W˩2ы38N1=r0g AU0!V H%ԀvS_bXEF8&4j1-D?'@~+q~MkKRȃI>Acuv6l%yzU| |FT4$Tֺ(ٕqd?k[6t綱{ LkX> A#;$|y3%bic :y͖I;9sn0fKLP֖@DƮN|ݳ7W=Pɍ垞'8,CG\ ^O@)gR%uR6^ D-ԬE'?x&F, !ICkTG5~qqOCÑ|# W ҼZe*|,6-H`0~ؑ*[.lswDwy;*zVn5<ӴZ&x)-L-88q.[Esl\IfVmtbb4b5?Lto_KAG@D ij4U%>͏NieN]4,Aolu g!o%!4 |'29T FsQ7Oc](:3SSIy^/bdu}9-;,y]DmatC۝J͇Z bu`0d}o7c^RyTVߜfhXHg׎/ON\jvnBK@/ixqXh~M^D3;>i/kR̴O> %k OkLQ)rDRKPN% b~U|4Ry"0Z kbx/r*`T1Rw *T=y/*3@&2n5ϋ|5ζvAxtE M7\U0lf49&pXy05D,yֵV =k32];aD&  X8]R-t?N3}[ +gTf۷9 y?9S ,օkd%ljo_#e @(5fvX\ζUplo%G=PIR!#Zkb8+0[ X 0L9+ r2`'. ŒnuPe&3)@z L_8^Kp zkk/H֘OKzHx *L] ٝA~ u)ڸ(7<-,,(THh<ܴ8z\X ׻ n =t7]! Gu":NჲV݌,^/y`lah=Mo_D.!LrU5r $7mMu 1i^"#Igƺ v4 AQ6SZpH?V%$1cr8 JO)XڃŨTɟ1^ouK[;3^wy-ϼYJ[Ay߇*onQl%?OPW%\GT[* ّ9M) cD(ު1}^ qs5`}g'f|׾;xv x|.i/D#@%yd,)BgCX! Txj3 αR G J:l[{҉y;N2)?p8u%g\,Y?K$HFA)&JI?4|:L%v'⃪Dkzl T$e/ebd5wL.:d;aWuPfp9s!۳A0CӫepMN{+K )M>,<.@w˚8F4gxq*-p_묤6G2(uW1" a? ylƊɂZMQeϟv%r"GSұ-Y!>ª=ݚ7YK̼6!]t4,v1fVlJ,+Khy^dOxh#hY*'~O E3<%8 CFeG(Gm#ʭ!vI*xk=P<$˖j>ԠB/ `alзGPD|!^J G%&q'+/{vb2,"̂sM_Z]u4Z3J\@iWvO@+r0NWC@SyG=t3l6a ne}M2w^ͭGt&X BGJ5#q:cıHlr-AwG(w:ǺS )*|]乥v3=e;U%>mU/H\+.']dKOػv8KȞ(e+>XxڢuIB@H$mZJ `<]ri ';F1aC{Wvo2iOk{7C|8nwCڷ#'yNU4V+{{z8/pk޺<g\L'!)PdOR'aA]}1D1TiuD}=`f;LF gF >`Sw=כMU8BWg1dcw:Q89qJse VPjaO/DM@Tdc)INYVgeo0/ОfN1geHbIHN:mS|dN]K!Z6(L;y_FIߵ2~NOWg9pe]aL = 8\]|64N'R14IӪuFyoWLA݇2*w.Ԝ~ l''!ĉ,k~8$ǻf˸vh'&N|v/bHzP!saQ|횷Y8ui,n 4=4yn敍 k>ƮCVo' .+qKQϷ8Eŝnu 7&G*F 1s~dHFzX">Uo%w6$Br*i в`u8[Ct;eהg8`rKӭe 2K3GYg)aR 8'VE5MrsSJmQ?,*#4̷zza\J*S!~.z!1vL?x f_0@|Lש$@]|1\fa$\@vzbz3,μŔ+'p8nQΎ>Yc"Y_k>?21? ){Fʳ/GHHO ѪR>kfN_!$06En&7cL؜2\[p=KGWZYdXYk.jLAt ·8utuߴWc\ !Y<33I3R`3bG\9^N~A~)6&,z<9/&5?Cн^tL1ۧ hna؁9K.Bc#:3KSba/ qOބ!'8*0);N};:uz$j yjvgњR~PzsiGAlW 2FC6j0I*؈,}5EVmu&nhXƝ%PS'GIη+ /|R0a ~Ú}>}of׉_ @ 8%k{hXoBz\M쌇5=(pm]N8aܮe)$2`Ki^ W{{5ٔ'7[c?&j4αfܐ| Ot}ZuTZ!4֫8}@I8ಈ[7 Ȝ&~i eIzlF٧V1 ,݇hPrk7\~-I&6q0&[-<#[p-bc85W AńLL"9A3ߛsGx޸1qO٩?G^BWՇD,-]PxArSˢ<=iTɬ+S|}*L߾=B$I܃n3'uNd0 <ˍ@- Fu,yɫ(TD q­p˷C@z3c='cGORqs7bߝ q> 7sRG3grlu@2t"zKc6qp˄K;O%0{bBAB@6?bRg 2˽Yi:| #vҦrw U a.{!//!CSTe5OɉAXCִ<<=T>cVj8,]@Um#)<4a ?M5߆\uKmی(ڕv~iޏjFp-|.j wsTZL3s֒y47A H14 EONWIb,'<01}Y/3#S/{n#&٤7\m_\{G X.%7%Ě6*Kg(V?nRI}HC77殎0Zl4aے JZN1vXz$Avs ~cwU A|r!}/pNsKwZZ1REYGręub7<<6vaV|ص~s|%Q[%ozUpͫ%\CkwbJŲ|TfC\)YA`0Q{Y侃wtiU9__Wua=%Yg7<%ue+[0ǞJM*(za++UN|N~6ru2t_F8W"M62|FdJdHOXƓ"&As|[؉32#uZ|f^iWIy<ˇ$;!*7-;*iE@7Lg.1b~Ku  ][cyGx<@`mSjVGh>>f %sVZ$9wl%T8$3; N3gy9Y9M}>mY7 2gZZH~>o.f_l2ք˷AN>Ywm>6^l9Z񼧖\!)^eq Z5;.KuKbR^CwLEzcP돊q_͐%vNT=\XF=; % 6꾇 L/O߄ )F7Zτ<]}EmF=3QTiÂ5@H7(ժ|e)Q(} ?~@ LBz N]?OG 51p8_ TVCgt(@[}r!!GdKdRh3V>c.Kk+N ne;Ϫš4O@0X&s3S/1yj;fbiGXM='.'q[>7(Y 4fmrd)|SBD ŏNq_=BeݱpdquJT~WRܨ.INy4ցPoWrVLySD$_5fHMHl157r?.:+3$+\ qkGuV8>~P}_ACAۆgud`_$rQFπ-Ue :6⵮/@tK`Ts6#&hhDF,3nl .{DLD3dB7䔿_fw3#=AqtCƨX#QH2.U5FdLЌE$ߧ9m4ײJ eq VȂ=́f.BcΥS里q-s㒐-lTe7$KsgW`F(;A*>&Q3& BM7f=Q׿w-j x78AЃxS`K 8OW})9+NVvșV_U^hUhT-SqU0%Kp|Ey-*oa60n 䕙Y=fh7uQ1}i,6N*\ͨBd>c 4;bj'|]sogҋD©B# b} Li43Nfșe!,=)mX4M2Jxwii =aMsOwýcyT.d%]DRZq|1Rs8=b &~zȸIHSRݑV+ҭM ՠ^Vl(ʭiu;Wr2* .{+G|IPCQc;(1dz0ZFWMz-5Tb/D ͙4^nA bu>Tm5\M;otbC[ɀ%o:OIƛZ% v Hq}OpJ66ok{a7@yvܻK㆐xsi;j1D[5i'Ë8v$|wc%!ddT8 9cb0^G2D$h!z{Ϡm0N0{n- ~0MjYgɦhPi4SH*Q~Ӯ5!|q[ԖVu))vO Ӧ#CXEi+7&GIeoU#UrA("?bJgY/2~#JM{X("|513,jf۾˫ړ+ın?m*5a;38<#W}HE"X͜u3y\qP6H=8h?6"pN`t/P Ȣ4D#2^P(/Y4J4p7G}̟3r LqێPWsR gwN%Hj6D&Ix,b̬6[.Pff4[\f/٘ElUj %! ;(4k&E7]+H;pD 287 >暦2"tUsU\AW^P*A fߞ/'r;9˪wo4>-r࿻cZDHݽȓmҬ%HX$ąKuSo87oB{ƚYEDa8wھ&G+ ٴOpet8;T"4>ЙQ\vL|6(iXV~s5)Zy Bi2kpmMBGџN & 0[&4uoT/7~+>I*m_)k-[~B7glvnW *ld]4 $:EƆY)u2DvQnb(H,h8Cw*S!]&6=t{WUAݱp]ܺ-ox:RVׇGr >8SO޻@Cg 9W>D…Tl}2Jr0{IwNQBcGH8"y O%DShP %`"=^J/ ?@C݃`L0L '/tixO,cYꌻ7wrAM+YN{ꖳ<+ڴ w!g I'Џ"Q Zͽ:r2n2V1)-t 2/}wUv6nE ِ(,2i܅S5ɝ(&?UՉ"%= ;_>owL`ˮ:L60(x϶Zub,.)l?r#9jq'cG0 76Skb+]f zG)'@C.O5!WEDaCZW|&6]hN[b sHc[1tASJG=ԌA-m:ɲ{v߽ropt$ Rb6forZ bnm$Km= */=u6 ,.G3X>@XUNu4'-3[f.8U~K/4fv,J}tGז%h2$QBMJbmņQy7X<3ĊƏG8) >FIHwՋ at"AP`/ S`wp~$EI!1ʘ+[wmĜ+3[#»_70}rF{cl߆LuـϮޫhjB<|wyڋMenb.]Y JFVӠV-)(Ww7p~Y ̴]'9'o1yy ]R{ؙ̻)~~.u,6j,G rq\PlZ'tw!?&9s'|]G91"4>Y$LD8F0Wn:9(6cNO. ÕFA4NqM,`$rʶ(^l^5ugFj(` D\mFMӇ IN'vsls&eh,ȃD9acʜѼ];"  >Tg LaRx=hrmPg4]RieBmHu-DH0ԅo]VLB݈4 ݝHWlZ+ӛPXW$c<ͳ 1@*cm46bPJ F1[cA1uD"|>O|!Ӽs4 (Ns3`e8놚6 ,+i>!L1Nυi3Г+Ԅh}4bfq=FiMcޱotv-,R/j}o716eNw2E}i;qZz_oVLkځIң-9v/p@41_SUHJyH0lJhPI_z+hʉAY[}^YCpuoIk<܀zpPRsr %7 H"Z#$~ԧ qMܡ?nw x =TΌZv0|4І[_`/3髾9 F+s*@vA):$tK?@NP(>ag=h)>H3S/k K{T! g*b]̅4}|(vcB#XD4l"cVuEC_>\`@mU۵ߠV"pas' t0= @=o쉡7O{.rM V8pCBS%J h'-C;Ҁ4]c8`q֟`K#luSA WC" @9ljUKu)9jmHDى0M ;g$U52? mx gAD&tcvupz_% +ٶh.pŭ|ܔ54*!Bq,74![g '5j4Hޯqξws1PgMMr̚[wo|b]p=>sAzE{ e4HsVT|{5;f3 o(RfF.-1/LJ2'm$\F?*jS}D8(ijNq${ 5\~BP6g+`ϐ;|O|c!M*@cy3mƺђuT{\+=*~eh_ mv(!뜽n0^p7 'eWc[oށ89I$qH-Ix ̄,_u&BD{DZ%@KSt,q+{%OPl{=@6azl($L{09ųo17m*AaEy[YIBX@|#Qveb_3.`)G.E̳槶?xpSqxGiY ǩh ~VRm"~>uK6 @#F*NFxa_GW '#/F$pܯ; @T], LI뱎fJ|/kyk^]ԥ(O ="BkU0$ExʨlEj.U%=uh@"gzǵ^fψ%< }ӾN\mԈ5,"B5&ZX<~7hL(VknOpbﱇû)ȵ {c- C32Q,o$A8NdP"æ e ̀a-+I1םDC3?S8xG(-v |]k۸Fc " `TQs$Zbyɤi-]vmTr& onr"7Yҹ |p1\`]pD3OO# 흸Ku.'#ڭK/ m pAbB˨rPc^7Jʊ&Gh6jwwmYi񓥋:A.9DTvakXPObo#Tse:6:Du.*`4kR뭟𖃆EbvV](jrWI06Yiz ieF"Am9Dmž[sf˥ejs^7+UJn)7*I\EGa\@qf|^T6ݍY_O<`Ua_54n-qעпxaʣCT},yr9{rZyN6Ly ]y}UdG 2Nj-_ѣ]CFSo)B~Ei (CiZg=gBi}5"!|8ݼG *Nz;!G:DL"L2Mc/q |'slZvc1x*MvTh1CNqJiw'C”;%jg z|ɭUK=[a0vo⎩z#Z^Rn+QRaSwNNiw5fBJ,߾F;ڂӥ h0\gXJ@}l7gBu]JS_y R4\z0ȫZ!K.#sڿtJ|& vKvNf un{kRh%y|wz^NVEG H]m5kBW`[΁a+r;P9W 8_r']BvG6^[ )>.]00iqz2yn"PKAAiaEBw T^0fJPK'][bA5V JBX6t*M6աf#)-7ef#ߋ.h {2 ׷<p0#㰙4KiGܟtLj1#6,~:ĔI*D<6\0J/6BF=Gs[${i-T~_R/|cS>e28 ЇFNvz'=2BbO]hezO.o}rG0Qť_e)7Ч+'+)Ð 3ٺH' wqz;ihȠ- |pݛ=U"Vآˋ&IVQ Ą 18 d͏szRlDZ?-CSP ʩcbjY=B3ZMӆ˼ދoq(68;(΀D`uaؑ*١6-gY^mߖ3 RGEf Mu= G5xd>Ǽ13޳{وjRtPw>=s#5 YMf7_|O7Xy*^5@&b- Vh ^J'y hqM ]ͯ,Û*HiV[C0@w(``LAu9@^}x)½W|+k=+N&W,*b6t#,r lѡ;x!fz)Mt햆荨c7k˖ %l 0wS%f?3½ʯ"FёhLN9/+uk},00C':mmCqlr'gnZVB\׍ .c{?ŵ hG0#oxpA򠟷p34w{A 1͊akWN;UkKPu/aTQ.)D17,M>sFAҩu9oc$ 1 ycq%)V"?D $FLG];Y،3}ȸ# x{=+RGD ^cBc+>: >b>cAHIf`3WZ f 4ܺ|o0,F*Շ C ڿg[ëoAY\u" !Yy@ra+6SIlq: {Kӝ0!gmb: Tx-bŹ"zxwP/+'HXB )ka8  /w8\3j 5億hQr>0&2*Du Ţ1pp!McWv5ml 'ʚ@"n@ȭT{Ŀ6)絞-߮`-6/h`}۴,p} Zg{$\-Mw!|!dxt'Ӈjb> \uMwkUqa`FhfS2aU^tѣBAJ.3>V8ǏixS aߩlx3Uan(`W_]zW9#ULU%uXڼUXX&=yj7~?u6r+9YM!1?HR7qRoF p?z>.!]M ?cnBy,_B^[{Pԕ} zzcRvǚ5-`'x(7> ΍Dnqs?Nr7WUxb yXj k An/{"qY7!oJD]=!?S{xY,zYo +$N;qhjq#>Yv22Yo `b|;lntĽlyӥMg~xvAe(Yqv&mت{'p/ش*yZ8J/G!u]}O 5}zbV~ST^fg(0lgyf1p/p/84Qٔd6r6 }T[n@ݹI" CkSĈV Jcs%J1h(B03;[[V*=GXQ9W>4A{^=& &<Jܢɘ<†NC#QgRL?dMO}o=0]{',[,VbX Tj8>B9P"ޝ|vlQ߃#n)8Q@{*+f<񬂴n$\?MШ`7P oG;w%+Ӻ/#B [Rl M{.bŞM RbJ\GAN:LK!" xGq;_#cv(ZEؠᇐ:nY"'HGg5oכ|j.a)-5NskI*5C3՜˃Y6LVTZ.j∤|rGQnڤ̔A1Ia&v[5c'ܸ=ՑYDDSKb}68.|'|,GnxG4Ky=Y$ at}d!}EHs[DQh`N* 鏞{ViiOܲ⠱=.J ۱O=Paf-Չ&NoI.%,*{~'?Q1h|{q} ̛tz4' /@d7 ̝hNiV&j#BG"HKJfV5,cD9f4?چ,"߿IEkӌ0jk B>uC4k-J{??* ,ȻW| :QOhj_mnjHvnEAemXt',T|bz9obl{h)sFIѶi Ax4ñ$77Y &#yeN eHoGRPڰWe+|{oy;qv<籐΂=^pIҋLkQ]CW?|VF˱ ߦIǎz{$5;(r&6ϭv)YoGFexi+bVNx5>K\?>͠ʅ3)le}o$Kl>zZ)uUmҊE))-ɞaz ܌u˶C[TmЫ ƁLE,]͈lwωMEŚvܭgz>voXP>Ѐ!cBh9^Dvs`Wù_> mO<MiC_4*"O}:W}HK`@p=4y:mڇ[b("9A<:Sunsf[tTDn_2c v &E?2\Č{6%fiVg@[DZ0@pD~c- b~RKfG(X$0 * &B)x,V`2" ǪΛR^A֫Pb˫׳&DQENA%ryk<[~|h&`Ń;`oWAL!6:j /6"S$nqo'#|Lz9?ўdnt{JŹ)ثø+b/2HX7ҼMEsG""dyuW?mpvѦØFg3 zS&uo;ء/26FlL$ׄz= N-v!5"xO..Zzevm"guwZ~U[4Gv:fN£Fb%WF|jiWy0B -{7]֒`ͫur}PwїXoL-^WW )~#uIg%%1x u{>Jn6}}:=v.'F>m5;KHRm t1|S!|^ QX p10x &nآ\{Y55Zg,ʊ_uac*(Y#[;guU#GHÌlX h{OG<ڥN&@Q7.j.rw!T}_G!&cK>D<Tj^} x jna2W' iQMmDk{✫x#%)\]Q(iYs.s͇i\oZ) jܷSES~[bekTΐx]vAbs tbMᖌ*C?Q!oڮi(W4%,7;hwbAUgS̄ .,fg#C叽 o/& oY{:D]Yk M u/ klw{-`pFv S7CF>H5'*(iYgs}'rHх+Ot1Aa k}vEtۑ{]ض\=B ~Wj_X/}k n1FvŝO&lV-eA0]JBy4>#UEY6(d|v1әDMhH]8IJZ03V%w0jl~IV3ŚɄݖtz5eg$];zƂay.6n06x/ZW΢l) +OPv3!%,KtphWG OIY`GeU=3Girr/| 딎X |;՘(ݔdkaw'xxS8fVa˔鐙YV*+8kf$#Ǯ?|\Y!`ih݈+QH[o((<l k?θcP#_t.QbgL OhJoae nltJ]ul$*1]24)ӖdD."%&Mg fs3Tyr]WQf3`1rE7vðTݯ7ۡ(FjL3+/J;-:cg}f#FQLEhSEyV]0O6p'9+$YEw-(V2:f)ŽcON?~1{ÄQ'7[>yOZ}|S[m |1 %;Cq]hĠ1[޿Gj<YƢvjkn]23AІv'oŧ)c+kH~o, ZHr ˔)'΅<9nz&=d%GM4~c2uu`hN /g,3]c(7%k;5qbǢrA@ cD!o h?)=k8>e}NBhxmC\JO>$.$ LI/ B>83B ,2vTӠia :xXN1 5;f|t]>}Ve??|ҳ2@^jyTAϮ``qq+asH+ȮaD+{aX#F̓b58(R xpijȝ}tYW,Xv\6B:GԢ^m׺۸9L\@-{t^ "}]o \}pDqqAxu] % lW-JN^,<(`p;-+'="j}.dU2f/[㺬b6[,;WcՈc沬׻j4!cX`eG"bǽ>_>\^3%sd Em.WCc`Z[,h4r#pik46:CݸeQtv ÄT| VoY61A3_ raD9 h;ЉSMVS$sƁX[uqW<6sPa*$M_z-,K}}Z{H':9K9ؖVm7#;AX]HG4M|XAУ͖GjhE(m n|]/l'#k]Iӯ"74 w|I6nKEÝK\('Z>B`b[ u¦}s-XX LgNQ')RSG۞.\yrLV,=:r^6d66bܮ?撗`;hZcA+v\+َ8TȄԞ\N S =>V=7f_Ubzxt9R"lԸ 3fjKw71 rE:ͿJ ;wݟQ'[~GͬNcO,/]6> "‘5>A(Uh\א ˛v3KQ5M(\X?PZ\6<0tY)bR1lnrCM^^TY;>z\R/"?&qDնba̼~whXdTu$_# ԣ?W!vHVDq˴F[l"&f_ۀ%`Qm֊Bs24+h< t P:u rX . ^!x">`]ȆWiؕ8czťhshQp\/o.XEk-Mu/o6C/,`W m7o: >NV /P-–2Fg_en%yKy3L a.j,ZB*f2Sk20)+f9Z$\ &*R#>|lP2'7E^ EbkXql>HϏpMlNtpC`4Sl"ϾHB~F[nh0*.e{ˣ{#KS·!4a˚?O4pNhQtL@:m}r:jlDY|KDIvi!x;ZcRHݾpketyyqݶ+5C/G!@@Zm&ZkW՗:C9?@K!G:a 3~Wq-bG8D/OhYN$p´RDQ׶~%ΆG۫;dT옩xbԡ;;m}{?T =%1r!h DPͤBVo f>[7\T9cPŢ]뜳kq>ᐅQ^*80Yzg~` a{!tqI@7__J}_EY|IŰuTz7zw; 5Lk{Hj i. j,?ޗtOS:7ӂfM{2cvƏF&iwj9d; Yo/[L/V# (ca)۰| sv219G 'a\X &ϳx`z>xt/fj.S{M.=b qAM{SD3C.k!Af hJ L.R VIT}#^'qm5sr 3`zfxfys0gyee@0KBu>o-[ hBŒ:X j !UGtج)ڛCOy=jy_dMEQ;+Cy Q/8HA ?/b4FCfօsta?3o>hãVsuѾ`؆~ߛ͆ ~GLԺ,Y{9w6tVG$~SAS1HS^%C*E‹axF v1b5syޜyz0`Y938摉? zniƂnEfJ䞱Nkɧ39潂w=E4 ȯlk `~V6g/_⣏+M.h%h!_l ,EhfpxVEi*igWS (D:6rsV 6+AЖs.IɍT+z"SdbwCU/RmqF,:, 9U16urOdg߳(,\9 7n05eTdhS˸dY :jqPɐn ,`vy:X2z~mƑ?Ddqd:MVs./ o+!<'a8fKNSfY=ApVY~ IH";ޭu{b5"MF{w9*Vmn ,<D51,w΄hL|۠iV-{PE}6kXkQqwGHDC$u^l 8SuUQdL2NEg{JwYEX\Ϝen %gOwj(^BƽYu]la׸4؎\E҅o{da{>jWP@3FuvȻ];WzA :Fe_NQ2zCݸ`T~&8M] C92 0Ly%a^V2x^>dXFdϽI[QJ= &vX(gxb gbA5KLyTއpeHcA۔a* ]X$,NI~[24>; {&-Rˬ9a+T)˜lA)s%gYxdvp6 Zpu^n:!\I#9GE 8y gzp,Q 1'b-oy͊ʜxX,@8@\;4.vJ.&:":\ʗD9M߄->aʳş70c >x3\&B E3-\ +ShPp:O^(4Er*sC kn3c#EP~(Ρ4w{icORu) ,Me)hA[2-:Rt:1Eu!IwzY Qly B/2t/eΰr ̥ؕH>O`w@;4D&gq {ӭGQ<0~F/`%)Z>a-h'`fnc΍z1fs@ncku삇![*2SS'yF2U^=͑NY0pb굿Vb>"gXAnRy̌2\. .G\1L^O.$D)d]^3y|ސ GH.*6OMD+s~g]V1a^'ϣ@LjX&6z %$:I7(cI)aN;9h{Pd0q)qUJ.M5}tVCPw_q3vFJ /_u3&BO:.zڧPYwᲉz@_]J׺`\UB7D_`Wՙ>E D| dpx. PG1zOi7-#}3c7 p3uGI4"3i0+^7;T!soQ=[#l@el}ȵP{O{tJdZՙŏw*$G :m#'òBױ,0n;G^g@FGL;agD>@v^Ntg~[ rOˬWFw(Esʪ: 5Bd~-wz)m5/hED,S> K=:퍮` .Yو 7WUN} ϡ&*{+\trMe/eV j~UA! %7@,xmm0 s:=- Zv{U`26ŗ YU!/_qJ\\ 9շn ΔHC_\:v o~۾H *a JG_A"lYZH?@)\X1ONv*w,~qy -nfnS#)UjjvA2cPlpڵ leXiX :ݣIΎ)zk<*>e5r8F ) [\"u0i&z;aE@f0g3#.(模ښ^!˄ \WcصbȔ,-zyv#U@}RTl#Z*Ҩo(҅'.jYc:54yl#%v<| L?nOv ͷ1iq3A< {իR ΂8a~J lj!>jE= a8 ?J8<]23fU3f7PP[6uƾxE'97eyH]&j5o`88ĬQx G@@ݽoY0wM[EU?zܶIwƷ ,A tMU+k&/93EAIRszfa6LNGsl\H! {/қ濨BX=N)9Ɛ &@o,-ǸyJk43ܵr"z(sR; Q]* a$n#z5555mBD>uy[pO:'8c<>&wbu^IϥV}l$e8Iha66)~u-y=y51@ 3Cp`N#iSۦSuZ! q %%Eg/ո5,arcVx\[L }:9`~+mSh㕗z\`1_NZ' fsUXXn') @[)@o`?Tʉ~J2I=_FP+;2]-ۛ҈l7> d.dc:O9}Oe`AI-}!H=O@-ER)i v@ӂt[ds]eӆA'tM-4<&>&M>?4w>/0T9IvG-ȉeQ@p5gs:B*W>#q4JJy 0+IVM:MBMx/N[(H l Ny:s_m`s\JzyR{T&Ak]R=)+ /0_P v&\-mi5{FV-؜*$ T{Y%}vGIkl @_elQ^$ıXJ!?ng|1 @QеZ _%UdWSϣu;(uKQ:7I(ių1ydhkeb~:~dw}cO8Wϳ+;ʐɌd^w jke"8Fm L8C3ٓFNJۤdyD[R!|oyaldv}3&οvw*pK6 w.H'jX:P(hՖk//ݦ6q4tY?$8Pll.qlhu(J5d*V_xY0@<6E4F8A \ B/i-(?5,Qt_54<Cw-^_sBM]u{Ae^”! ;[֧„ e]rlP3;Q4=K IiCv1&$F I=vi2%>Yz6ҷn\/r)kcфA sir*լd ֒d־ϱu$8tp  gj3"|'oγ'4MC;Ьۯ^~nMVlLݺ:sl?s+<_dQJʢ W*6*P6 o%{d-3t!b @+εʩa8P~2r -BfٚU)<`VLY6B5{RjYfח &+`UUrw{Ӊq6]kzV 2Jӏߙ^LZ) M^W/8*2&Tck524<`5:U_vQ؊A0Zb 4tE B|&& (=ƙXQ /}ĆiU_i6l8b/f+aٺba/'*oP# R},e:mOWÂR@Ͼ87ZDLw; e*_['jYs4gɉс_N ~33ӴAwLP;m_Ap4TI0_My֏MxZlˌ¶s{dJᮟ zj L8~X? E1A\㠝35Ӧ *ԲO^JN"&*hڿܦ,:l"leFK Q94ά{ow0QMiB A#lŒ9;rp% F#s"dVvWH(51ʜ| NtHpDmZ7C˕]ץ]`5WN+o4_9y&0Fע_d +5]sg%g |Tpp\.6)y{YS(6t;>4o'+CzxlM=x}}ap4ܭݺit;<ڀ{B26w k;zR;u.NCPCmoW3KUd?eKx[)i]D3u'E?\"J Lh1@+' 3uYG F&6Xޣ/UDƔaCU9%i~U ,m^۝>IsDَsK#^i D9yRE+88vssJI2 aݏ}`=ِ.2T@EjShwhqO0FM%\#IF}A KSiGS1R}Z1:Y O>`",MliXo k1.(,O֖M 7$[~ 6lʎLW-8Az*;CV -K3i q/V%'&\5 E>nyzN$/g,zLk{l‘-g RwEe-> jrrTB% Q&e_q]tRKJGXP=qODd Tbk4i͙g/ U$(~EMr1Tbp+fƺ|m^Si~MǨ+8P Z2Z Mm-tChU[?HfS5;L ݅{:Mi_Ik /,i\)pq3vrDb!tn\k<]gm 8&'.wTXpt5(CR V[卄scc"q(,84R(UzwB?~l8b W{+|C{6"=à ')^pܼؼg-#Z`YsxhPАī-%lZ~$;>:tdEIlՠ"}?;lg| г&L] Db7T`d,z 3p_`&HpЍKvZ1)ԓ=FJ3a CWP{ibpz߮t&"F8Cx1T܆0Й @,fsύeқؿeٸt8%č, =/:*b£AX D 4V ! )}Mű"Pf^8m0Zu7QjY={OakDW_3HG *rS;!BviSPIۜ 3O8%֩"\NQZRW X>4Lr&9S]f(@lIbVR<1Z<< }aI؇IM \I`ι\qnTwO-3ִEf#R=QTZsp_i_$##@ӌ(\zD03ޞE=*Rq0U.b$< w;)4+@jLZNb\s~\n*pLDyjv[sџCT2^H ʢ( z5caUU~,4>.j E0l"u6%@ڏcCo\T0M3X\EĈdi<DZ,7bY 5} >]CDn0tKFk}?Do纓)2z²qEڳ*oo>C~} }Qt h{AwXx<59|i]^|6[_>N5Vq 0{28 ye^k>APi 1XoR<:R5x-{TRBl*tK 7Y%8;l4"2ÄKzUM(֧.QV 9cK[YI[]~WH|HUCֲO.ЪM*f,aE V߅D改+ƃsKMyxQTmL =v6`t,"90x= V%t4?`@9!%O1 {vB{~3G'M(y?nߠj̷9F?ܮ f2{;c1\; Ԕ vYܤ$ŦK|6ޘ\-x[4`|v@;kJS/^ُ=8+&DK:mr/k-e CJ"p yRNEΠ1&ofn y~yOq93d% ٩z#>iȹC,Fd4b [iO#րz-kkͧ FaݸPZ3ϩ[Dͳ~'}tOI[ַ>3Q $E6D>꺩*|AجՕ+ ϫn:AYmgG0YJL~K6mx2jE@8A* SC0 [Y@zTa z]vkw lm}@O_˝vB )'=@ qsuz'nOɲy%RaX9DA Ӗ080 ]X"ұT p$8k#]9H'E-I-Ww7}/{"jYS CԴ~clrf4Im4̻FȦXU􊦎%h He-r*YT`='G5c>.k{74FqoHG[{ng$.Ӈ*ZݣMdm.l /kT3Ub窨O!s"zh>2.08Z!'ZMhWk_.C].'ΰaٛh8*7XYm뾙k/+0 "Ĵo`hv (aikK)]PTlP| 0Wcpgg5ɀV>eή9qe%Tª*n20 =_E'/yuSC⏿LUrqDژFQfcb{~]v\Xgmu"rnʳhWݍ3|6kf,_mHKhOtZb}nt7ۜ<_{ tƂXXt9TzWwd^U^ar 2ʶ $gO|aDhg-BE+|LDھ݊'½4.:,#w];(leyP?Pach<LG{ !mESifpsTI#X@i>_j>p}76Y$Nʩ;,&prx5۠1 W0$ۮ:VL'0浕DAGޘ11uQ Ȕ8̛q $7㘡rtC%*(- x˘Kw3kB6 ȓf0>+2щvH n;F!I{­DweQjQ;TvQϪ؛?T-X;#+w †#y-UAt(~EUzK׃QY!t7oLbWv:ޟ;TrgCaV"3P:Jmp\v σ&R;]'FSFђ@[ PGf.T" 3Hj(K@QMm!LhE%quRЃ;/nyUPdTzde1Ff<svXDߎFCd?;{2Y;6hHܮOk18\V8BB| r־Fg񼯀qhoaF;S51a}x~Ċ zC/zKׂe1|JCjX5+qH\Yy:ݭjMM楷 dخ'1eLcUae?%P%\<+2ƅ和t%` KV ۷&P[H nUb'=S[$Aф½_z#Yd{J qć9GMҌFu FtMg6!!ahճo)06VlMH Q),>@LF; Jul]*n >yQW͕ЗQh@%ϳ>ӿ!*s´_B Wf$kyk:" ,֕2>;mJ xލ\kG;WDz\H,⩉l#q) -nK3Bݙ(@b @d^|t|⇧) fD'nĐ @i<aŪO6a?KTPP- YΞ5#0YNo3Z혎!d3U]EҚp&XdM?>OuV}WtəU]sX0ڮ48P2۷gX:vt5wL^"GC_Gs/t.m8?߭ƁLI/Y|P6)̵$džV&΄$ο$ɨ>r3 +S|W .\o])~,ue%3GnϢ$rnvn%}KGَr?Fϖf/8 @aiaVACaBm}4"Wsrz+"RX*Vy ;=I oM\DgQUSAeS @珁3aD<_ҮP8#۵]e7kŏ ={4e&P-lF"NUAυ \ HucQr{+(&0T / U՚Gv!ӖQ\qZud ќ0JD4ϕO\CelpBX}e i|81AA0P-0˂c(޻5&x+J' 4OB'dJ LEy}Ybvd}8?/?\0l#K'pMDc!0*_~A'2nq~y\<\-7fprKՇV45>]pr^ ʔ(6􈧿b:@'(rŚYa iHN+c[z=~($mZ7ߺ3sD(^ t#kD FA:v%þDBL<|{ @`eA;7_*6.:`qռ=ބJBZ(m!Fstϩj 4H~l\nln a .J~?5Lv boRkc.o?U.E YZ.S;\䔀Z_rpvJ}I{"y*e%1̅S HA29ne>*]+YAg[u`\rKTKog rV]%-c0#z4S |a2?5i)DDءӯ^T+S"=-.b&hl} ڇ`5p Br߼YbR͏ҋ̹ |5pT;>g5Y+@ m 0#NhE_Db\_uF1 ?Vԗ2ƿ+Wjĉپo'nv9y_%tkrh֢n*VypM@SCdXDဇ#~;M푑 ,3[O.e=+dzdN!Gk br1lw_$_hBR@#5{?(NN!: m^)@Rk-ɝ|YEI(s<Ə烝>(Ȇ#Axa pk ^&jU}hYai}R('pYo^`37ҳ]Ã}} _׾?`I]l Ì1WH7%<}i}_̢\hwX zI 9E_ilF_RhQF$!؅[4^z˰tC8GH$Ζyk@$`Gt\"NN^Y7SOzV|z NJɾ݃AXTmD$j]skpO`,7z{(D<?iӽ9+ uQqWsj?_Atu)9ŅezaMT%Z\Y~0{S"< Ҟ9ХFςK'(Gz3o MxTdUh,:jXtZW_ >OstX>p %W{_YK$GMqҍz7NeJ5~Ew2蠌2(%bw@͑*}6E}fI^cItۯ5)vp~!XX)~+$$X!apD9(>;Oh5Oaa6_PȇomOiQFHZD@j`J"0;|@@IK~>{m5LKlB SfVVɺ5]/3KM날R\Έ^ J L'#t׮|E 䋍`N&^"/'Bo((lTk&ߎfFc kJj*n'Q#{Ѱu0ӳ%OTϺo_,VUBn(́M#K]Q:MW;T6[B%g 05W=zSp\U4b>/o 50 aOՍ+{ϾR:k} ]e3U?MuZ`îXrQI3FH趯I_+Á+$^ Vi/TYp?⠌@04P96SqlȾU߯h_0 X7v%fo J7yAz9),EϥjQ"+Pl+6{ATEh'zK.qLc]!]-+?migB0RnB?3gi3?oN#uQ4ΜTN>~"Z8|oXw,TigqYa玀*i+rܑpV8s2m58QbT]4g|ͳٹt{m9 W're/{ y5E˺ ڹ^ގleU=Ts3 2L. S~>G&fD؋Ǯ@N'kVC`X٠S8LfފV|)w}KJ9,XVĄ04r@ tm?`ȶe"|YX9\ŲuveɊᐕ(YtYfƑGǔ&oYniJEe*Kǧ [v- [Ӌބ$N7A0tiyVZ|LeS!B+")t 4!x2@`}t꾳ß3UU#eB|oNfT!8ǎk̾_ Ω`5KE'HEmL"2e -hxD=qڒ^p}|[]j'K~ַ&tF߯+umqT $/w;^NRJiC+W,2Wz h`t{͖NWfcYL&^S/a l&; W<%`rBﶿ(G!O:K''FE@˩rhb7|d>֏(=Lb%ؗ0F2!E)f` R 8۵:2:`}Fӷqx_wjs7wo/%fVu LѼsbwzJSro'Ą@삘j'R>kH2yvNKi@2zzzza0vV>6;j)~7ǦL,` Cx(}Uƚ|B{LzFHpqV%{XԵtӊ*pWR]X[ RCSU8$@|c]#rW05xW9hi= IOZc&;($Ɇzƃ+2tݸA4~1<8 $rux2z779{v,=E]tq}Uh8k)ryPpZJ[DoSRvcjIiTk){ט.*4O\ eiR᳞i m+g+x؄j]ؙ|g9ufDn->ۤAm;ZM lKnww1#kn0Aw=:Vб -1Hn*f:Oh'U4 B?2>4MCEZZOuS֢Xlf9}δOՍnjuߨMn2R$&&" \ď wXܼz"IH?Mꡒ,ْ ֨5'v` %<NlfYۀ`KG[rbnLO&7>ZOLEn mU+{ B^qV2~NJc[M(6űpۙ 0rs<׉1WEpF&z}T* VzSIp7{VdIK}yL7y%]s ְlrMQp%inPJj`T2š贕/ %E2\Zrj*6Us\Cc }9lJ yv}"OI]UP  VJ>Jц&)/8>S U=LKr |g .79aSZacQohI`qƒ@,.bڕ<)>ýO|cD湯dd9@Fh+MǡGL( 0Q/ӎgS%(j3~ 4O :NrO/JľjKpE1\MQWNpw~lhLR U' x-u`"m؞̐N%'s1rM!d8]ݲ[d$#Kw@z'pfˊ~W=p?)~Itٙqs [);6A͎V[:Z>%*jS c:gVYB>x(@!40ՒH*4 ϳa} Lipqa?t BOoɔg~zmV#xe3+^hj~qA\QqfSTD}AF )u%}ʁ-ۆ7+2Ҧg1^_ѳtj6+H5\6i up {p{}uDs Y蕛MTDoC4n6^]T4-j œҕ Vxp) zfMYcrT"p}CSKImjɁXrN,s;ətN2rGE>mzo= j)*ˈ1o 鏖E 5,1Y2i}廹hʹ6VIuI B_wU/Gul<#{8uds!s-UE&!,[ bO:uk\Cm T A|Uގ:&vemf:caY SAmrMPÆC$%.0ZUvUiH~#%O)?~8E>{OԌpvD1BbE ˋEG0c H t4'8JL@AE݈ӑI*5{nS f`  1G9R'̡KiQ 6 $ ^R`~<8S<{bJB8)ͭk乖iW َ IPQk~۟ 茥U%[di}YGk;9ڸ\7]{v' "뺆hSIJoFIqͷF>|#:E'rֱZpxxkhaL[%vJ;1Ꙥr]JH9)2 x E%2WiW Şti$n[nSX*3ʣiwPq J{?t֣ q-zOu?h ~qȊ <)@_;pb1zp (&l?,,rLbor5f=)m`,SƗUcjqk+»Y*\Q錁wo.LXHYB7;.O!}i";NbU4Q `2m)'ewTM뭙=I"?f1Pnw5Xfݔɇ|d>!Sf;-"`&߭P?eߪB!l0dN*i9^8F{ϼ+<rnOZ C~y%wCE^Z .d̽x/V&)5a)nrra-p%2{}Xx]IHe@ݖ܃GmS440>)ٝ %°ZY > ס+P'.n;5RmI 7ЪQM/{@ԁ>Щ˂:&"Sj`W^IG?\8솤6 !rjWſv/[%Ni_b,]wfh Nm'N_\aږ ﵓ& \nB'x^\:QOVaY_;Ƕ%%A"}_kb5ۺk0XerOd!NXo_Jv}mk)'qՓF^|(`S{W_6LȚ~Σ[CĽڱMϾ"Xd~DO<[)RBK+ g 'sN%ky 3<$ kdX}2i6<&@f7jZY;u}e Rls銜o.Rm>K?!(_l賥CTҷia?t bcPxրn$STS?s> #0EFEOs郴X[{vW\y帮=٭J(?ϲy &z,Z?ʫGbK,SRۺ;0Jo$5=z/-ϲ2 H!_N=#O<}0Bc̗h#7 ~^I JȮ4kj,,ޗj ݷrVԝoaIÚ즎<93$51>lD} ½㞞\]4F#& @@;)Q3f]4׼| %>{#b,b&17'W=Ͻ F <Ǎsy+.V Mz, D"ԫx|1sWfl(:B1@j'8">D&Dz2ls狺k']ji }lfcs 36+݈}eBD&!JZ (#$QB67jouI% h>hɂhGP|:([L}ZLq͡0 w>r3d?H](G=>F)Ҵ|#ͯ=aYd/,IqS]:y OnԱTlK(L&s{A.bO z%a9VL?#1va ^E#[<.bP*qydm9dB ]L7)NV&fq)ۤ4u^0LmjTcxuvŸ4Dv ILMetfJJKBYhn,%vVxM\S+/4g{]?m>S1J> ` ­z)mkA;͗5q5gtȜ0i`1~^ cЛ6Ku)iI /(;V7vHc] |SyU6=K$`+X_š[0 lV,s}{&Q gC-}yщݸ08\DZ> s2yIp5!hV}^K4"lq\Er%1[Vƕ.X'_F%qQu%Ѥqc# Z,%ːdՄFsQ4Ϩ|hͲQGhld`wsŲ%,B[>췄vj5s8wr@y7GCH`2JQ*JGJcL~U^LwPd~"6j9c77YPBd06> 656cf@p9s~\`H,7k!c W?^޻ ﳌ*yo.N)lN; *ݛauv΂R9ł'R@1`Jvӫ. ^fQk@w@t0f 0sJS֋7ٷ/ 4xP+(˂hA. w cH8p> pC$q?蓟:ѣ+';Vm:N5m`[ofIIOXcWB \5WUsARVVfJyn(!5Y {8vϗE: x7z5T#x!ݚ9=#"3֨,>"q 4?k.DR6iK+KgтEh+).~50 }pX;g?=r$ ]7cG9"kZF4OB(.֝h;8}X'>}rҬ\6jMhKB!Tw'4G|~cF758j؀iXTm.tk7ڥba,qM7zf_Y8P.-iju7YZ?dؕ dz-z%˺bv'镜y6fo&a!mp18QwaV1>l#QQƭ^*7+\}qR(N6.nE$hcr&UgxO.TܺF -<ũ](pvu[nPg"Ĺh4@! Jq;lh]Yq6v2{'zmu^!`XXa5<V!:?9A20\Β}3JBnaS׻.hX%DE?bhm-6!9{ ;U^9Z-e3})ONY"=; 4.CJzR{gXU讲mЧAF^9/;d~JuRk7J?,8̬&O}蹁jUfRk-)I-ʫ9?uI^]ݓF[ ɂO[lC׳;$:wd d}J·P_֜Yz&jFOA'b*ῠ~9ÅR*츦[֠=h!(w_vg|[W!"F̬Ggpgܣ/JFS?hJk`|tVqEwnX=D1:,vwk"~e%f]b?L7TYf)luʦAǍ=Yf k]Uz#B>$Xa9ŞZs*]hp\ԂE[BMXEݲfўi1syQ޾ܣ}.`,-whl?la:! Hq-Xp_9(R? Z{'αn&<ӾiZuw\UtT_rE uۦԏΏe6VCbSRF+rz?!2`)qL*}` /I孷,EoE#Ե2 -w0NE+vd!B s6PsPjބK=*˹FE97;;YFnA!{i÷?!μ3Xt19kˌIl .e[wJ`(%azß 6JC $4G TڔAۿL$(lZ/&I]P'(֧ qF$t4! F/3'AKzKvQy;^֝5?gySN%agTIn9ۧML%P}gUWLT>$%S5 eWbv+lbFeeIHDyj;E@ b,m2`**i̓v['13T5 b+b=氟 >Q |°!-ęlnS>(gJpۨq Ϣ l^vh.:%s#+[]ͿʩmL;DMnn-LՔ\Zc'ji b &ُkRV‘gCl.-iҞ`>hBkR|`ENJ3rvkLn&`CM5eee΅z r=irMZFH(2䤘kI a",gܑ!8]P κOz`mHEn\x7fz)3i(!u?1yo^-jT&_{G VubL (kLXˎDi*TT[A};m{2T.f`wr{yDž}+OH$ŠZm<1H#V ̏/%lFϚ| HXy$pms/]/S&t{A7t: -xrrZM-4)d6jai!]aA\FՌ A+:g -@D:òCzgŊKw,BH>XYFxz7TA"8[^fユo 7VqP1ul=ɱCMMA o| {Lk? a=V*{ &doy%$31rq6}ۢCKaWex%)P ȿ"|ahH)𡿆 eGCY,E].sإi~z%WT `[T&I_sn\Q\>@hݠ8G`:GTtaQ[}%r_CN,FLI]ʶEs+oZ\cRjO_L$Cd_s9wp6c:< ^Q#x Fz̭ǻ Zo}ʉ(k4$y8TvBb1SHiwo*KPQ v`1:1ʝ|#9|nMi* AnUqj݅hXJ}UbHEn9PP\ll" JN ·ɽ4/.Js{Wkw6  ̠勽LH -\hoC{Kȋ lj4/DQq9hnmqe@zt mд`S@ -(݅B _s$Y~`9 ]+ZJ W<:ƹ^SWzߔGV}ss@YF)wV3D =PώxK+ ڇrI!UU6-@U(ױ7n:P&]kԓC ^.[(M{~d9/뢅Fcϴx|ĐX0PFJ+2+!"cF4\\UgO6x vhz<қ:vpv݀uCh"Gt\/KmJVHWV8Ab<7P4}*пXn=F/] Ύ(LP6|+Zܣ/##6,ոMT?RvR2EMD uEבؼ `T΍?T S$a﻾r@af@-@ i̢ P (C:CcF{Z>5;`pE\Wʽ0 aWD,gznQ 9Co#'x~9=5C2,A IjzCӍk&AppxI˵@m}´uziu5̭V3:Oxډ^E;`-\ٲ۔Kq1S0% l!:%{P>|z]8>QnÞ)Q0^tA&E-8gP/gGKAOE5u dz wV Ơ./B'o"ȩ$fl=^Tw[vYYTў#qE.!Zfr/{rm߼#'P6K5݊qQ3"3(trQi`wRWl1RgCHH!{͕0bL+ې1)z#jt־Y,޲SOD`Lܭs qOTE56;<]0s2.ivIO~V.WPggZRTg='nwt5åzR0Kw8d" 42OޟK5M6/S u4(\g|e\EZie52?úJ>i@Q~\XXGmʵ]l't$uMzu' Bé9^Qmn"{t_33~q01$DRK-3\ 0l^`*ŢE?\1]|tH|"[7Ґ9hh `s/kކ5mлP_+)+)C wKW d1On"$dJ˓ tvP:T l#D$j.́D ÉpW7S>W.lkHHE'9O0Ɨfg^+ 6>ehԗdV^H}qs?~F`x6[_Frr^DR GZ@iO%vƘUY^2`g9 R\Y}ă8j•aePp2M&< $#tj&Aι2̌ױ!" ?+bdbn77Mdn*^Ipzh k8IBKvmi@VR0 9L:iVyut^^O=G18 s"ڀR3ikRxM\r@NTq83D$, ([{o8) 'P/nr߮жo?0%nǠ.Da&.uY*mgHLq (% 5rڑ8%F/y)$b0̽Pg+ox!w^σ崵ɔ|qGi]<IV@F9Uਤq"q Z4F4{ҾTZ/}Xo7+)Sx'$fiE?{tB:8xܹÉynf<*'s?hfT |7Sڭ 471a>k/[Lee2 tCH5m0cT߿RAS?+. pB:=,TDd7KhA lѻ&6jxr?ls-g>(ھCARյejD k-_>$J+LvA`@6{!@)q$h5Wyc=@ :@=|br_7yUDMA.{e2v' gaLÈyq EoLՋ/ZЩAlq%/fbԾ&Fd:/8} %K|WsӿUP1$}@`aP2LQw5 Y.t%'1$?;кh0 *yZ$JumtNlEQtFPD0ß?X?|ң>(/F I[-ɡg:P^]VS9 G-icSP.ñ?t8sy|%) vu pD݃YV@A1eø%Fٗ ՜vVeAzA^F64v&a @<؁ir^ 2ov,0o:"'=\f ۉ6:?FzXuIεts/\Od0ӗwoX.ï3QR#v>e5E`iTU-@%F'{K l" axdzݾak[ *n!]xnS¸O L9͢漏q1Aq_ p Cûˊ* ߻YsXZ`8UB!H 0+`UCՆQsb,"CIBa6tƛ ЛEXGD ) =JWJ-tS(׽G 3 B@D̄'-ΤRwc{ {gVKC5[VT?mĴKq~:+zIx^]z2PL7k)zbsߍ΅s(5lY:֓Vߴ^$j(<>Ն~D64eYtƑj6D1yw/DxmH/SY1FmL M?JRopqlÃ"&`*Kpɱ*j3>_{رjDJHM71'=;KA\ ˛q>gڧ=k˿6 sZxY|Pq|B}1%ru#wb;CD M)"CTZ Gܚg̼ =5kVɋ383 ֽoV#3~F*,h[P?h'_8 EWgI3uZgaS3UO4W! bUI 46maHeEK/s@ C&"ba_݌u])z>#TC0(ɘȃ/ ~aW Lg,FWMf03?v:TddN#zгD"`fմfVf?׈(uVFs!=AeMěQs_sЍusϪ2 vncO|~eLG&W->Vyii}u!lQIۀAZ+愢bq0WU-}haj9r( yuV):51`;2ECĩz&@ ƩM´{>w䑀Lsby|̼ M]̯|6)ٴܷ;{QHb G]i7sJd)((Z y}Kg؁FܸXBm(FHB9%PzxQYR>*EI,Za ܑE]kLJ;mmG ⰁnA2/%=h*lDS+$IgHyewhitNՎEY,$HU^ f` ZT?KX,}> @_ {(ڢҸOΞ6ulqX|ݺNܷN;6 /PgIIzj%BaH6CpBo#a3}>)|!y9y97VӼm'& J"g:.j]T.5!j:gm93ޚ)ENJe Ow *]>)1bFI"ǗCˑWލA.&s\4,oƬ*0ty}Âl*L 琍;.NumDfɈDU _Ї(ָcRO~ػǀYy@(OV /}z:LFwt1c\ssGnUj\VT|Q -Ĺ@uɠ>!Ӹ$y#b($J9Wk7--CtfPh`I`I"6ŊֵYYqLU`Oaַl&C;~yi&oa, @ B9>e>5-x=t!Umi@E:smj'IA*xS>De:AJzJҟ#l^΢-U낃mp2àpG{d4"[]bsuM :a^RYboڠR$&"i4}bRI6kD53K:2<&qNdeRuXTt6ݘR~x1+ߚS u\5pS"36 CD3ޕxFOLGyGQS0|.+[> *;>I[2'yFuJPA :1:\ x+A2u;*"?X%|k4#>|qUDpViȋkhE{;L0lxE7G]k5֭Mq RaH+?0C>1 A-K]\ՕW+2ũCj#: ARBO:(YugWF]8B# 4$/M^3`¡?D=t TΔBD fT3 p3+v*yGboN!=M #zƣh{ Hm+굳zu:U`3ۂKJAXȎa6wd@;^+NE>/q,Kj)%szᨩl0~jCEDe!]*3Txd}?/c%\ 2T]Sk[\lTunˏ`7C<@eaG~\ ;~=TNj>ք%9Ĥ R8\#xSHdI o|J#7B44N}\BGᐿWc zyˊ:pm }9sNeVMe!GÛe1 ̝p5rIit̄)A|fٴIT^)gESevQ[ gW$/֞ O[meNu!!fp=sH &<0N%&Y>Y"w!xʢT֔<᰽Yw%L=n#mE:%~ľ*{Mji، ";iݍ#(3N)qZ]U滜j[``W攕[ڈPri>"`/?bIp7X F40=bc+0/'x&L |3H/y 5vk怋@cJGi *{S Hߠ5G\M aP0OenyS_p; ag&?.15VEžµ68vu\ b^@):hSgDfԍ"T=8fSF ֮d^{ b|ď4KƗ!*2 oNec-ğAQH}ER.&i_sE:Nr9(LJ7m&_5KZy%MX%|w9lL$ys"~Rѩ@jaXjdzv?W+@(/R~):Nzq;V&դ%Tx$\m+^; 1mJ:"9$@ ~|M01[&b1xoa~޲Ed6+Nl K3Y.n~8)`фzI{9-s]:Z%t1w$^ѳǗQƀx8ؤgJkP(~c_d [DKAlZxZ,jMcfh(]4V|mjH@~{KS3.JqiĀwR 94P^bL&Zk1E ;=wŦ-HIr vFG7Ss Z gO;xPr6TsQdӇ1(c9PN&١2W_ƌ(2`:ڦ앚c 9+D +h]BzԱkƔ-Y 4Y_@D8 yl, j̶qXP88* GlUr~_PA,J5crW֐ 9R?2 7TKg2%ycIR_Cf<-չ0RC5P)7d4 |sHѨ>(#B܏N.h:󉪶k8Y#2h3*kDS+ [9!;0YZsymfc-1.6.0/tests/full_fc_Si_111_fc4_step_2.xz000066400000000000000000000010101511276756400210310ustar00rootroot000000000000007zXZִF!t/9/]4Кs;Z56uY8MIX/$d==s=j]: r`;WCۤWM3FXIW(,P{)8*;[2}i`p{ S6$vzn7a*E8k}oPBrBL&Vd(3~V޹|I3}dL ^ r='CgYZsymfc-1.6.0/tests/full_fc_Si_111_fc4_step_3.xz000066400000000000000000000142241511276756400210450ustar00rootroot000000000000007zXZִF!t/X_T] 1]ZR.fXĘ.vu@u wۑuajC%mީUٲ]ͽPQ";*̃gyvLeIdXS{BS[]T;)Yɽ91dX."6WdU5y]ZV/5է)xG M34]ȱ-Uxm^akÔ`ȁ6#+5B]@: ˡ 8X(ޔvMF7Ҧ~+idoX&ʦg+hL724|~`02L>)WG=҇6/]e>Ř2a=|7Ũ l9~ q導'e:46&hWj_=$niO_Cp]q?[UAבԞ#%yV*U_[(䬇JX ti:o\& (UYby,W lnJ6mT|j"T&g%C CEHq`'B# }g1A++Vr0qMvqFH[*a7,CS< SE蒁/Du7eL}BHSCuʷZf%~O$匱o6 8%AP/@BZZu~ur[pB\9"S1}9ۘ?&1>^&{ UOPV˞*ݩ99(z:J;"_Nx0&f:vcє,SJCHA$aL# k:Q#%x@اݷWRSb\@w3*֯ TuhBcS,7RW_jqvn#'Gᔃ~&oK"CI2')zk.*BJMAjPB<#).L)Y2eg?5/3)ǧEӕ1TD,&N  uH@9{j枀rS( rKۧB ZjNs%-,&|j;b<0b 8)*7lM;%w^YNdZSacŪBݛ bZ.$%1`yYBK:gr]ԅ e;u 5}W[Gp A. @ \w )q% ]¶ŔSucJBB2n_arvHI(|Wn*<;rxFq~+HL\qGP!WGrT! ‹p ^l5ȅd2&{}啌3Sя.;2py^]~o׷>ld ұJ`ͤl~ATfd_3l &,볈 6Ҭ/#&+Ԩ']* 6U<>2ARi-r2F<<ʿwtaeg_~7cDw#w*`όO:K&1ӧW3#4}N2We)Rn6n8'6i"V򔿹/JQ j( hc+&g1ʙz8_ Bڽ-Tr[Lv0 7tyul= lyYy;e~!W-XV Kq9{H苘Ր9y3#% ƞ { xDe g xBH\<#>u[o]GS^0ܻUN0 TwIw2Y.kF:`7pUs?u=k46*Q fT:j1Ό9UEURt!fd$1&NsKK32d1'Dٸy 1A{hmiőǿbH弯W )2xTVRjvL_c3qU$~H-LmM'˰l 4iV!W^pUEFfI׾0%ŋ$_#{bca£;=U&?(ެAiFFyS5X+2<N@\!z.ásm)O0"MؤN`Őj\A {T`MMGMEf-Vul9l L)!>Ebu9`Vj'(|aOI'n Q& wEi4̓IƄIc>L}mϦӈ'D fЕ,_qP6f V$BܷeCf˭tZ# O"i< ,;jFs| Hu7 ׀5hT'԰IH5ܮM]e3w GƙlU7z碸OiN`s@nYc?R<6K'm %`j ?6#EKc3^i|EKuF`(WbYP6*g.iVG A?Sv/hAtW`|I'*ww Z`_s3eloXe|ֿ?x)v̤&z M6|;"J3[1low1{aXwb/$L ك_^L}l˗n./MDODo4AIJ12V jeҳJz9-C8%1SZup,t$>$ 8i]L^o`SnVI $%b59ƶZ [u2D!\6 .w*ktN|f]CDb RHS,vPP멵HG"d?D}}_akgM>P`eQ Wo鱐ўSxw3=ٲ0Ds (\):;ys0 ľ5iHCqsoe6{ kmى[wtfvfB& V0uk ߈7+$[^ت'$c=6bxOިAe-2 $Y!:f>O:?o.'b0yWUν^a@g\V@8W ފozOͿq?8BbyQ%MWZQ2٘;*taTH4.8ԍWKPO[l3]/@1i$1F[=pfk.cq YmVN!ƓpWn3ͽ>oh¼Ҽ1 .,&@\W\NJ}m0y|Zo:,h7⫄jc±-I<&E7gEƼ(  h7G{ +$.pCǓ%^DKkԁT~f\]N禧IY.ixn,ğvLs s1CmBHbeDL6QajY+^cŸ1K1?لl ; <ᰌjO~=mjy=^ xOX=*D?/)(*.sqHL?TL\N~ [#w R=HMk'T|\Vm-\(k/Zw ҇_Qۦ)칤6΄ΎG{aU݌pAB`5H}hS+kn6u^W0TMPR "bTp9}.t: 5b/ua_,O}ylfLI*۰tܨxz ^+sd2 i*!yl@Y-ԙ/}#ko0}Ykv<~PSęj<<q<o"VI` ]aPH4ň3wUG15Mfkj9u?|Mk%K=϶Ϧxj;R.{\=˩ YKq-s29u2O#Taxא{Dҽ),~{WCRp0~,rSgYZsymfc-1.6.0/tests/full_fc_Si_111_fc4_step_4.xz000066400000000000000000004407441511276756400210600ustar00rootroot000000000000007zXZִF!t/]A+v~5-OD3WSR' 8h$ vkӋein'[ԉ9:/!7(+`v _ȁo'+5rRJ3l IVn|ҧUPLLThN `N鈷>e 6Ru|)豧b$; _,s.?M[Y]YѴV|bO_ -yMzTs`]0 opm>TC'nf71\'Ԁ\u-.Zg$"um:m^s*EN>/^QNG)b]￀Rgr0#I۳Wto0=JiX9aYox,Ou+/Zi%jQJG%ͩ fBk _ҙ]ًn~ c|z՟f`ҫx 3r!}ZoN+w"F#5 Z\IR'C_W{USI4 =Kg1jA-ǩU%qBӖ_YBIP: ʅ=0H6o~_d݂ 8WX털Ya'6Ox.UxK񶸶>} ud"M\At"mIBsWWp$ZMc;00kUne!@4P-gXc%EʢzV Tc|gWGU&𯔙WM2bPlI}>JOׂ$wA.F*Se?:z"ˑXTN4U`&~7G-zK^Fu+*̆6"g19=98E؊4+4#G@ƞE+))Rh2 @CwE8{C c* LC/X`8v@cĞ%O=Zގ!|zh@%X ~ywY,˶^Ԗ,@_Iٸ J>97r< %ًQuYe֤)mkNQw(͓ZhXk(2E2UI ZD KvfZB)ap0]> ٨V#/og6$s!K.,%4U- F$Jkr@M(zi,Rx?e^ב_sX@J))ج 6-,aTaL1k {ZSO ":S"~14g>)DӽEsPBw!C#" ?id_/Iu_dK8W'K"(~}ҼL2,#)*jʻp;ChvɬYfR͛Ї'Oơ"{q;9YSCώ`бC}V\ Euሹ$sޅU˗V|WX@|&G|lcmMdKQ;62Tʗ/)hoE;7c+0&B-2~O=|w \*)z:#/fuw╎X;z~QkJdx-} /U*=p;w6Mdud'̖ɑ&XƘ0.ǡFrh+/ Fk]s8DZrX p k]x6*).BxVg=Z0ܛp4|kL= "f,:VeC!|7AlajHo=*ȕI&|;) $Ѷ˦QHe@9~4W}a!,Q1 ۪)61ˇ#~1,8ÿBH|A(sw[Ցsj(H=&L${h|-Y-IR{áNRyl^ Y-ŽÃݔpN͗q-AD/d{`ȭ֔x`Z͏WHWH[^x_S.&9@z[B<kW; :0< q9RZ M 읋Xt շ gڎ<~qhmO'zQ#hiwэ CPI.l?mO"`0nٙ*A!ۓ9:':}sB\SLXkH%Kt+ޛ{`g]Q^sȀ}tBBHjjqG @vR/ih&##;[IcFXU"=d ct)@7Gm9u. qB"#oIvƛ ך5a z~C@&APٓ7NV5YpD/q2SKkiO&("T}n'T(}MզG/]@ rF Qb F':Ckwj`$K ,󴍻qZ{3ԄBb4tXD tn<¶ 1)2uiul*C.̦ϛbB8%X3Եۆ܋ōA'@x '2ȺӨ1Î2׮zD5;@K%-㹌Pה !pǮ37y3iH Ƈ0$c5 8'&5[ǃΛ LF9ҫMzg_7G[bE,1۬Sa홑&@csP0-O^2FHzalj^Vu 8?u*!lnU5f3(-pWci;ʱBP +"0ZߚY1uy p/)DQ 2f!W>HC;B:X|zM bD~D(Rhw=:=IJs==uQIޜYʶXAx$cLd,7|2F%H(# ۨ=]KL;}4@tȣ9?C sZ l FgnC7@]D\ @zdVm0 r1Wm$1fOɤjW|v~B$\7+,m_M %wBWk*KnyJZˆl ig$i.b{TIrbO; >i}sfآKD-q!q]F?<2`#Z?XՈ_407c)r#6&fcAӳ>?b Qg*7tԸS^=`ʃn/K{II$gV0>߮]?֎"{7$ W5]i6u &tbDd!4泽0|.LƲ%jbns >EDiȍJA_6 fƟ^sd%}H|wOu]\4dz$+4)RfaZXX4RD&i(fv39;i#|rԑ:.o.*(Km;ob8!%ѠlvT3;= S:"E c,%PZ!> =? +gO:+"L'ێ?𭓇! n^}D|˯Wm83U ʲ av`ɾoM 5=>H_3 !~>&Cn($yGMjйotf?]Mݒ TExntgJĘ$ 2anwkva[ݑv@' qU˟nkyj"*^PY&+*Ed *}6_}ѭ+jVǬGr湳ɒ87i쿞/137{w$VRP93Ԇm0xiρKHr8 PsZ#O(VDj?85y_b}R@Pk>Qa/ E6 ̬|v*rJ7yr"Mv܅>++6#{#&?U?ukGclc Ԣ,$R |3&Rƃ_bz1Edؖ GZuV.PB8t!Zzv xWBAyN9N/r0Q{4>Qd|g6ߧ;* 6$,xRk5Ɋu2檜S=뫣w03JbNP.XZM~itʶ P%x?5;E=0P.D#?ߛDCzXjޅJN[c/qbIJ%4'O oaaڄQq'(e~Sg'x )yr1YW,䆲׭ wdz7'~j{|TyqB ""e.6aFaxɨhc߉!W1WcM#1n'цtlS,(!HUn; M+ `?BYUiGO\mT@YڷdaB 3]I09goW%)Gr4YP?bO ErB!A@`!k+],Ed<<#2#ʟ_pyȃ]ZSoROi$g hU J1][4| ŧU\6*&9nO% yU$ mLb/m>A wUhzw[gׇY.Hz=̌ˆɲWsn2I~E5(IDP~?GW;=nǷI5RE +j|Q;d~u>`l[̒a5*4qZܔo8+ 5W2 A=,WM39׍ƟΣ% )Q/`Ff{Bnr %+yJ5@h/@{T(()A`{{.`K_32pTpctNJpH/يԬ*4⾓4(FUu[ax5?e9igƩJHtD•RiW .i|纾?FC:Q>9DX7e1tf0x\H5Wd wenDf^>mt^{/TRGNorуǿR'xUHa,s-BC`Z[NCH|]P}دNN}L3GFF&Q(B69F'-30 MNI\)F" J(4ZA#DUylɥ1lG vZE8-5 GWO)E"1MM1N']T AìTȢLسq\ {#H,Ih8Z&bEуȻ'ޠf5#$D-b{g+Aգ ,Zn=}12çN>< ,գG(v[by9ASp?2@SjOBt~ȲGfǏR֓_"W*!URH.? Ki1(wHV074z~F `dbu krᰥS\GMV e'%8ߌU񳼺U' 9-&g-B!SAl2DJd`{EmQU)In !$pBd7=>D^Y;#X2WH"j}]u(V$5Kqa9*%v ˳h%mV@?^42t@ʼn Ck݆t].Ƽ &t ={ Vٿ<QkA v QOri@es&<#uƖ\E@>k#TaYds$Ϛ!thx<ι<%gŁJ$<7-T6nLF`Ԍz?mEIك>4aNoj" 5a] ?ylu58ҁI.:V4f8VUlB-7`rZi Nl 4<,v@fq&"ݹU5sZ n*i \]*;q#- ALV_ v=*w ܢiɂO$MkBi@M>?Ľ[Q[u#o<ߐex_?(jO0y0 Î] ᒁ%$R/CCɍ T[Y @>0g;a/wr`?y``dR?e%WdDs5PW7ʧF -4$I3OIk Z@;4ܣ ]?G6Oi3;D2 Ex)שX4T'ohV6P*5RBr g>O>lPVe%(K㾊rOigF бw !K_Zh@pl{)5FJ9_sU\NI:$ >Pk&+!Vo>3&Ә+{aBr9_ȡbq4X0PIڽB]9w8"#5%pՖN?hx54G 14x'iM N +u%|:]ɛ [=ͧS%v?$*LNt+}NFOTJIa]FQfUupjv! |P2rM1p 5Y 0~kG3`R֕2/k ܚW=ԥ-=X NB[β$gcfJN$?~JB F ѱ-/'c_'f@[hf&qL_3`گbsl(.b)6 \zd4zQmƢKJnU@ZXrΕM'i]D3S`>es흟%Ls(/)r?Kk Lx^ԿfP~+aM]g&]pglWGޙbc@lݔ6+ƿ}ͲOFSJ8peUT8fH.j0 zf6G2mufwHMXeD/CGq\EL/Lӳ\Bp75Ūú3uK5olUx&n d_miJ'+%?*guJɂu zXbrtsr[QSW>F1 VF{W4|ǀ\_i3m&]mOWZ~WWk%Ѭ厣i5Z ы߬&[0Srt9no:P+1mF5jx+֋Jn?$(lM$^'d-kE`h¡P{:{;)3v oF Dy>f* y%0%S:o/cFy]"DrYT1F0d;7 :8Ol$)P0$7F|E)mP"X!൝xP$hK1+v5VğbyUAK {#jE <aj+#FBS>"ۖs6f=SU0m"U6ڣ' 'UISѝBv2 }䄝&%) a^Uȟ8ŗ(HeN;!{ߡ}tQ|5XE܊J7aW<ܼLIp7r_w8uvg2@itB@T(ϙͭgnS Z¢R^6OKN^QT 'e4;iyW\Tzkv0 #"J-(jZE!r]`|.Cd k@ ֨jy)_keA,P2BB-9ӹ/|èҪW~<թAt\\$Bzk. mMtRKpz=[0E5+ si=_J`bhz^y@#!*Φ)*O{tm儇5z;T Q!HY{` ػF̦p7IҴk(mTqA u+?cP/x D%oD:Kpn-mlt/m-ypOßB ;Ht\ET:D.1|P٬|H!%3Hh[1yT]C.y(+x3kvqv~3$V4jK )c0գ"2ՄH2p50[exsGͶ-x%\HƃdWvYv4vu`j(:lhw&+xe ߡѪm3<ngʵD6?439 /Il='#[@.bosHpop;5OsZ4Qogf(i[y^M3h}(8ڪFf 8m~Ԣ/|z\|A 4aS|N; Xڙ4VL : A`)Ҩ?L@Q3Yیm"+yDזhRwc3~-]`1/<@wtd>:nqf,{gu"yGh k q6e3^A/QgS᠕^}M2u( ziJ/EZ8!XUIM)G-V[)\ZXBeA yuH 4E7\07l}(=jf7R7GlYښiїmZR)tYf.jD&5/n-5ћuR?L7=Y3B{dX%wLMOᢪxwa<284͘H9O"ǘC@\2a[S&*P )*HzؿÔλh9kِ!f!M~`7qHZ[:tdU>T6lRCFjz77Q#eC)Ns@f;^4%lRH&JQ8'LnYE-L׀5]3|Ĝޅ>ĀI_D/*frǛqny_H`¦ 8AriR*!G8* uv[>&k)@1MCV%996(VmO69Ofopa-x» k!7懟6c0&i)Mi`DL }}EI|Q?r󭽇OlBCDK+F~N7XJ,&!Ono7n9Mw^K:!Sf!c띃d>sA~}a4$bJEu~.vnj<%ݣL."{?$(L`OU!xNFZ@Ilhzÿ5O%տXޥL͚.BУ_[ H,Y]Da o:EĊ2~/F`/*Io[Hڴ.671WĹ!`L@ dN?yX}{9YG1HX`린^'Dc#esZ%k}#y_4džU-)dvRx5X *./QbW7u{F4w%w@% RCdf≶7X+=P@\Sj[eP9}"XjW"!85ӎ$_P nUO~./<2-$C6|K`4@djwJ{put vV?KY"*irB!ͽPF@OiŬ)9  ŒI -}N!ŝAs^3rĿ|:$6Ls # *Τ"E" tGdSٶ{4wܔOF>Ŕ AlPX<-Tl@&_+2fu G}*YzԖC?oJ꟩wM[2yo@Z,%ūxfjg=|(%Y݃epeɂ9 FggNӋAiG7H.-vC450J_*CElٵO; "[=qjwGKr%wOyd375Kq0)y~[Pnbe#)bR/mwKny ƅnyYm!|ر:ɍ+Xs7Ѿɴ2 ?̰ĉ@'6>3R",>?"=N1 iuσSPvI-OϬf#mm&*~ 6 Ae؇A BOLh|5Jߜ\F{71[qhcjα'}Z‹N|+Of2;Њ+l2)FdwLһwMIhF&rսw1C!)Y#6{{AM RۢPd| y+?j+ )F6s5 :']\܂mqz5u_KCz)`ƣF. l($"*4JgZ/9a Ʈ$oJc:9u."@TrDd14Yn@G"iqLo.AުT?{ouWVvvY=Zq~՜IQu g^{^w*Rپ" !4otw~ܜ#N|cDP4u[בp2{9-"V!3~j-lrV8R-s^ŶRx|;Z7ĸ "gZJVK[bs*jcVO/Bpa-e,*`wQXj qR.8۲6vf^֡T̐CN9yE~-Qv-.e>W;Rr奛{u_GJ" xg<kF[_YwSvsȻ!L@ljoc X.b pr3y5.pZ=+˿Y ;PD2釦$s #G{4coyDhA4?.Mޞl4=+*5^NoVpu]?lH(q˛g|v'xd}NrHG/kh\)Ǎ+wO5pWRqMdJܡ>ssP2Rgv S>Rr<#<9UGHp` } Jy?&v.SѦ9M4UdBkڵ'acÀڈ !ji x2 %WߊJS?Iv-3g?~ZssqmZbhWEn4c~` IkUwx{kAҼ$)uMVzg L 6$Sk=-0JNGR矑E:"?~?}WZhwC\>B6+{Vq7]-tL`Uw,aBjmB! zy92 sAx`;a}` cFufGSfTC_i?Q/UpH%n='U3A (s#?In>^#3+[v^Sa^Dyp G*F*HYlFQWq69DZ#n'V5M#ZLahog;d%Uz Gkz$.a}p Eő\u:^D]4d>ɮ`3=.a|Sa³&eI9ǥs?>Rp&yB[ga"<>C\/'Mn".&Y9pI Fi߳#*zID0ìWWX^[d[R'6pR7,=KMy2d_S9J3$ZQE4=NQٙxIDALMH%4sȄ-?'2]($XwA%~щ, 3 Et$D]?~4ι0_ŝnCP.aĢBvo5bv i+TCimA'-`/"^wx6\k@V.\! (l4Ï4dLlNƷ.-Xb1`M ^t|Z}&S) B#h% nm̷3B7o.'zލHdWVmBk'?%*aϟq&ޓ)u?I3["Mp v1FI@ 5K&ZL64عi)4fZ Y&\܋Lm;ʱl]jtBT_8!SXs! C d |ZcTNW ,גu󋗝glWW˫cg=uNW ?pTmŸ 5.&LE@ v3puCGvvǞ}k㘜Jt*.}I>rdlC`0'ӂifE!y,)HT;lO|kϘ.S:LP'@GFB0Ef1>b|xYɕ&20^mPީkF/to֠Rpp;7 nW6W!^suA Xv|iNJhٕOHTͳc7FI&j\9C_?H5,Rɦ_[wvI.}8k$Oh_!i}dב@!andEn".- wmEPKT6ldH N_iwQk掶!Zn/v\)R㑬q=bR{sWJџ*KݑKvÚm}^<)vȂu0ix@ר;@N@kUoG@7!(Yg&ϔ.v C VQc/ K:V-{͌@oo=4Uci%9d E|D98 0=-[Z `yO.#s4,{:\ܛЖ. c?f+z x;:pV2SXdR ظ`+LᑈyB``gl;pF:#mgH.W!8_d"#&1МW$/7wڦ WU#ȔYQV@&.Q'%wWr^EhЇuیs+ Ȇp&Fp퓩ml~y f_Ew[־)Bz!$aWOaPR 0fJ/K퀚ZJz>'c ]m\ S2+@jxVGhaFfE \ ~y^N g4ʲRkrk+-BG@b+\vw#/H=u}M݀m,SikGSafn'g_߱a"mWrp]}>$OmrE~UܾId߿Vm*(~M#|2ƯvoNsrn#Гܳ?1 sl-QUž3m78]N 'غYWW q=A_n<ߋqӪHYE1->t"&XΥGhbM\@'NV+H) oE3~nB!J,08sHQb平m9،4 N˵4uH=y$E^O ?{2ܯ[5xJsJ~Ǐ[;( 5+<3od:)|ɩVw˖3M9\$?[ ^_{$^!}x-KS| #O+>YjUG;8Zw+M4U]A_ igZzeZ@He]Mڵo3 z+t7(95 [V V%D )8D-.ЏNo@=Ϋ`u7J<19N3_րƛ ܝYLO|:@9+b6sc  )>'M?S _m rM;JTMٓӥa9 N@E45Jw/[]/x RVF? FI9_RYLY8 9(=B-QG~l7f§ݵ<.[0RN2 Ğ:Qf#NflZ#f_aQO3ڄ1#;Q#OyĿ`pc|1(J7aX K D^>2F3TzͬN!U S:uSr˔"/ F|ME. \͚rD%yo׶`@ߺE$4c׊; кJ]A j{P7iwDG–tVpO;;'жSZw[D(^.H0YT+NMesF0X = q޷܉V-DL+*0ʡ+jI*E7fy|AArM#HNY4eOHys?—MnĢEJKǢ [0DeeQmi X\-D|anV|h֗9,B-TIrW㢰Ng" |_Ksʏ[h/zj7v$dj7AMH>k1WSyUT Rʌz4  !h+5L]}Plw-e7v-=W=rabv. [gQslI]w}UuU%!O`%%Bj"8rz8e\Y|u| &b9\c3)gaZ*nM>l23:`nkk{>*x-RD@$ßnXUooJu)jWV8!1QwgZ49#BלqѤ .c2.T2nz /4EaR=;1~MjWRPcy{fߏV'ޕAzy J& {H'â#ipPa˟~C[0ݴU Ӕxl9xT _T_qRi^JWn[?V @1`5NrG ~Rpuj&RrݽW.G"S7O•5/b*&W0oR$ůd4p RtmAiy˭ԿK^"GgvnV9`$eIȻCB$Wз~hC5"a4˭!8RNSҠa]g :.y 0P`9rNR =$U%rn_Z<ԁ-f+?uzGiC]f[[%_P"Ŝ~ka%B/~'!VKo\hԲ Z2'XncuRJ2jv`;y4( 2h\0"u͓:{ҝn] mQ}HmqmEe |ef[ۣ :ZBS4S_is'KQFߴ<~rXҵ>`¿Yd饅`Wcj$![@\2i0(bIn&V| 7VA a:û:8i6@ wxpƙPT+ #qB 34Wuri` d;KaZ:ȩѧe:=ޚKI# ɨmwtm3gjQ,YM#q,<[ӽF_ܨte ?p8r)ƌT\ 0U_x-eFn1T(4T&:Vy!c ޔȌ.\/!Hp 0W~TdQz}k=cѷw<0a8P+uEZe6Ynqbd8)a쥗 )_hl~}^kfz< Qҳc4!w X\#)G/䏼6фsC]?O" "|8"ܦL Y!I`~BW+ )ͱ4J?_aZzPzU@Ҵ8|9G({& Y (8sCosW>4Xy ExTE'x7 +QsNJ#v~BN09tڰMȅ[mG7[# ߊH-K4^$p)<ռlpsoq[V%1XȚsZf-)Ĝ^w**^I߭saV:[P~ cF(Ee/nsKfаa.U?hW:~7WD 8vw,60眬NRΘ_GxmK8 )&Vj6 [>&C("STgI0 D](C}c=ž2E.]hV1U+NTS"=^z_ӻDy-C5f!/$g®WU}:eh MܚsT~ۋ=PSDTS%A"k]|jNzI!30!yF8}7,Drf3m7oy<8G?8e3||w虡d}j'$; _g oIt hO&7cI.ö~I}H2N P h850ANP, 4̽ӞWh«#e H;]U{dTݼU.0/(ݷh |?L^-rh~y삨c IV"+% U-Cvoy%`Ng6q*ȳ-1%q; H-G+9f3&DG` rw*a8&&>sQ^fz0 tUrt )}K3ZSI\tS A lj04ǠEmnO3 r"qDktg a(:$ sG)t%aa_5҆7"BXɁGߋ,YhJjsH(RtKW/+AA2։FzioC8cr__NՏDoE:ʿ,=ƉڸЏQ:gbR[Gy(~BE=0`B8ziU[>/^f[`륦-2ؐu􋾟s֕:?{TNyyv 7zC\6~cuz|5pK1DNg- xA lWb0qէ[o[&aDȌbՄ :poQ R]\LXkbP*F՞2BdJ?8JZo9K _I=-%i{O6m]!T%}}$Mxf/ڐ֓1'㨖4\=;I^5J44CZD/2gLJϤB$bd% )a!DLT½8 '5zi[;5VO[g9-4Xc@45L%=:X'4gy6dqr> sPPJ] Hc& BH[ ğQJ8pu$XAtckK,aCYEk*=7@ 0DD>vg Y\- TYU'L$@Y&tmyBuXىm>`zm+`߾-0W ;TƣuîKެ ]8R*uyHv]]N)Hը`ᘕlMDR0D;saͬs T)OG捤dc(Ux5GpLTC9EHJkM6` ዮ_$1e3ȮK@Xɋ^<Fx5k7Ԇ=Ft/X WLM(6ZOіSd%WZ-_HBDHSߝ;S7\e!arJxaA<ϧڄxKr~T9ddNmN$J,z"53~'V3z,R ao*38;eҋ!y沼/OJ# ld7k33X1R"oVa2naO' pxo{%^̀v_0TnN4M&5δzчFg5Mq^~i!3lT 텾Ik\.Z0=z el9"uw H#B$E1k G\񉧙۬9?| l&k7蘑0&$&<$uÃ֨V2@wF~z3.^?(h#'¯t h)a)aYZfc?@ӧʟaSAA: Yo*݄=ǖ\Rs^Ӄ<$p!Nw-s=tmj ,bU,|vc22D= NbSŸE^BF|ZLCGTk9 9h/@Bgus6@۟ gN9XņE=w_YJ. \R{_ll{ ćPwZV5.ݢWb1>"T^贳%/՛T-cڥm#b&_`c D1zeL 8WKE6c7./G[hCkA_F1k/XY51 ,2|4ﴦ9:ÜB n䲥pBPAQ\YIZ ,c4&&N,\0kc E+6HL9 zAC'ϗyy`F Zsѕu 'üD.j- iO=>PwAL'Sm;Z?B)bo2Z48`$ +"sjYJ Hhp5CUPs `0ٌ$Eop}dDVv!9/4KVd5+彧Ȉ1o*-7ʄLp}T2P攓.2ފ_)-IctB 0} fdW\WM?#:hFM_nUl+aҗ V~D&ңde82xZ@&zu_Ƌr ]/JwU=L?O7t2 yŐJ$EMl :\2/WUK*FscqmUtyI*67vG6Y}-Ƃ l[ݨ7Fm"f%'t2uct*_􋅣d:q9)0WjERk@z:̉vԹǐ,.ߜc(sܗ;@r>:+At` kP+]i@ʳϨCz"Z8#}/HGgBΗXW݊ٙ_ Ðս #(B5a4IH͞35do HCrϬ&*NRGlG`# j!hݔLP0dJKeƝduR!˱Q!pN1)i.q`d*I=-fH/{4H>*MkN8wx0Aa Ll&p/6$tɱWB P j[`N&2uwnl+?-$kS7G@9m@7*^ڿNeJqkrUGH3`Ԛ4qC=nu4.&0B3zOn/_de5f6kkxI~^?E~q ̊;IDk^e01PH970E Sɣ"JOi  ܉;Sե%+GV2b94[M/Jȴ* 9o3q{LRo笝r:MHl{;{Ye7+EI5၇"(ՌM _ DWzE Ѫ-UML-HlO2-Ñ E%cg:;BL5"/Ĵ8i$܉nPhOd0[Q(- 3$|Csى/j$petMlT PٓtKf ʹmNgY?YZ-vo&>"v@[X3"K?L-]i ֬,quE1x&ǚ7;Hj#:I9|t$THG B7 o2EE0"7Eī}I1nFf ,s!xbJj/ %] hnóT^:gY엮GdE鍍nbuiYG{L8:Lo8eTv2>4AEa]J&UצWդBh9 ^Tcp}Micjjk(t&Z D~S8vOBuA`pTEht1 @h+C3TJt04uXOa_^\,9" ؛p/O]_u;ZtjA N _d!*~jN)}9 ~Ol}_|ӱ r7nlq ʚ_٭>+ F^vvԌVLBI֒*kQ;)!D+ ?__ISbk.|x:䵈/и1:LC޿X3)i@`.tٓ/e܂"O&ý}) ۜR@%U(ڠEyњ5°H^2a>=_t4 Tѧ|f sjsS%o?gV~p)ygȅ\(8Pc/YqW6S$4˷7:eI=fg/$ ÎӖ 8wd |&ӻ)@d˕fn:7_ ZkSao nW-/o}.4٧Xyri'.,LX5TuDE(vR\PJ.Pb 12ң8M=N2vz(g=~F{5W9+u/s~Q{#u(0~>}75B3+No,I= 蝴(5eȇpqΛF*i]qS&o_"j&P\J4J7_ w$-u J,ˌ͢AĐ|Xީ|u z =w$B8o&q~T䥳Q+i0"V1H[ϡ J >\и֭M4YF7CX]bfyy۟ =ؚ}o$ .}]2c;4.la'4J}MU`Ԓp~&SzŃiGyܻKbթ/{KY<8CUZpW-&ȌY4sE6w$WK%,BV TF6dJ>) Cd4*@۲ ׾ V#D*Edg)+, qyv× Ώf'9Js,xG!3r$pE=DRnr4W6PaV$6nH}Rhdmy Ye#0n姪]UVCrHu[rǛvI4x}҇9UjOJCbh*t[˻|Ai}0߂0!4V͖UW,1֡y)?Ưs(eNHA F,kb^n++f |O>u~I_8 "{<>D!AFnA`Iid-}l_PlXh14 tQ>ӑpr-]ߡBsP@hMBؕ7&Seze`%!YR˺9 @P/YK-,W{HZ/df:.fZdMli!w#=T2MKҥjH2nKb~^1Ͻe5)sRDo[lI*}Z#~ZƳ}bSz\^|)TG~Np̫:8wvNdmcr>٬cD:Ari9 D~r }q-ր"H*󒱻V/<6$2r/Ḑ\unoAr`kwɂ`O W?:+[WNd1dBձPFTkpX +-zo/b {ШM$ПɡTR>jN\NV.C];] |?6@%EqRSS ֨,IGVG3B)gem2=J~rpL|C.X7ƈ>zn Ru;4xX:3˰bm*z 7#.h|ܿh/lR  kttG$` ec4 Τh oWFJ 0~=O3q&b]*#ppXY' mA@4Ĝe8R:O*-NIk0L_ћ%.3",$u-W}i%Pz g#)Ԑqw28ӆdAw qݥô`x|( zg+Sk?k(o7T% G "=BKä}~ƂFkC;5yu|Y.[#mM"=_СC^h`:<;β9ɑ>0CZ!;cRb)1 H~8|#ŶnR8CΧSڍ֊3=Jn;&_UzP&r&Vm?A"$YR$-ir | tT⤌iT$p)ΎYjz?(SRan|8-^Y*782!s^bSpj F$wewاtlIZ/2o,8j9 2bkB 38:Q Š,g qV/U٭~.!PKILbcgsQKGOWF:1Y]]UzLju8O, |+_~Wpy0T>Q!]Yav=;TQM@:= &[qP4Wrx&eirI;{.(=e!8U4f*i֙jж8k:> jr߻Tu|)X:`s;?,A|!SmGi=1T1 ɇ\a-TdBv7ЅC.bXg f*6*Vkmmk s ")9Ui;!ÐaWvst4:4,O`|yބTsJ~_]~ByWQEp1I+VV8Bt O0Ymy,y%cn=tIU⵳ۀ ^Y\Y儿RBU[ CtAIeI^03"|3eGPq71 wda0Y*Y}y +πv"߬b.Uc<:Emk'cXgџ M[@Od !z*wt_z`62xتD,1 )L"'U 7o zT8F@K+sCeU܁:t2+mki=t>u٧Nw츔Ǚ[pM,2z`wrQ%Gȵ3T;qX%YC&Wm.Tj"=O@7t,@dO~jQ&mc|h%=r8A \`U]h`YO7$Ӊ׀rp\6fdpb\2 Nlh5DD h@sv\hEտmZԛR sAwMS+h2#C&3ӠjD`evdpǘFRkڢ5dL;⦊m fȘ -Uw4‡m7[AT~|y*VbV Bߨx=[Loh>Ƞ޺ 1ٍN=֥hh]OzIGt 0eptm~*4a X¢-fw (ٚxp%񺋀?ÏתfA d}nAM+DR겟 GpAG7A\(2:chdʞHɬ]`"{u7|@q0^JuV4fVЛ RϚ=ƫ8U3}nINӥ3 hnҮ&-$&͜xvbD}[>[Zr@ND#A@DcTe 5)̨{R@-N5d!v,,Ó`tQܾj2̎eE5m2 E09sb_CN[TaXt1Jgq^fҥ ,e~3_-mJ(™R9dHcg& $Y]ʔ1tXr.JĤmDDKL$*D97}gҞ!fHƄ蘇fY*Bs702BuhڷB@:0KsP`l1Qw4G%g7t>EW8ly>(:ɽ PLVחcEρѓ.}c&%Td#Uv%#џžܷ6% zAs1?JYX@Ãd7>w4KfZ K۰*wF5HA?=_7{Qjyc]0pUݍʭGbAE#~!?gd9wd)IşcbsCnSSΆqh>Ud%%$B4u4X$w']i ãH:Br!3CV eM 3466Xc'|\'83.44}ZH }rˮo:Cßݰ[r: [:{_rʸLLƥnitp}~']S'1&1*Vrm2WS#&ԂE+>2O:> PG ^KfbV2DJ Y Od$#&v3eqD#@v:kc("> vI-ќWL'drv~&FK1 ՞SOh7VU˔L}HSo-5e~75yyW db;9^8ǁۺ o*6LPw\Y$NZmfo<&EI? I?`)nYp )u*h1 ~*>yvsNт4:vۚ=B1EoK䷲RN@!8#"u%mNpw?g .&a VhƝqwy'Dhid[>KA^pƙ MϻB ~VPB3&JXe_o70*B^[qSFH<ɉo[=}9kD5q OqP×?k%RB ۴E$+fnc]g좻y@MI9$[d Y[@CCJw}@N$0 >4-MƎMI勶mMךa=B^^ڴL:~/XolQ\"gע;VA> رdLdළ+V;/y@jELjs䅋w;O1MAA.²0֫zh&8x!mDۅqyoQ_#߭Eˡ8Yp/h"t+8hGǼy&ɞ@>['4Š1uPIV N&f KI3{h}З>ʭ]9g'`M^C|Ń>b51+vcrf7@SZկL6sWmnN7O)ɑtmJ::^uBF%1,bB"zͼU~5޲4sEk:n-? TqFv狏Mdh}MЌk[*.X}?F~fRf}ɄSS)v#M3Js9U1KA8x{_-aϴ h%hrC4;P,DIÁ(JF̓ұAql A*s\6vg!40}А)mZ:2&/kPع솗to9e`0ZR|C+ :z6kY!==Vm&LK>G/:)6n5}OhWӱ[{z'tda2>o'(o@ jϔ㐳#wKGVѡs{VLU nKweeiNodIt%~_LkxvJsI6g[z8HH`ޥsj@ Fwc5`eA{OV^7D`j/#|s=I +(?-sE?Tj VG$ V(>۔;60AU cnkm=t.oh.")C4?Ut1ŭ9OZ/#mٕS۪C47@<^szL>O1KǝӇ8^gjzK U`Ϫ+±ž4\YLꐲ!uEhɺQ<Ī$ߺ7+$҉a+k?Qj! (Fˮ̏/ޖ1$jnMVsB"ͫ'܈ˠL:q\`l}?MHڷԩ"\$+h-c|ql8wLZ!x %NX6Z_h?ѭ}ڒ&঴bJΈ$0| s[?xGf4`bYƬ>Cf ?$s-cܴbw qsW}~G.{?*fyx jZ=Z{td$9OzηC,mUo ֦k`np:7OYv}'Iƞ](T' 6f"W`SA6~? at_[|sBs%{cO`F^Z}c GѰ@b+v#RmV3> 荣zX a"7Aaw0r STSP;|{8;/" PF簳ymA'"qR7=!'~|}a#gjE׺ ȡ}5ogh}*jk G1M]SJ0jOMH-DUGQx:M/nDyr(e{3o amҴCo7Et^)뭰y9QꓢJkJyXqáD^K 2CT0Dp+oɰO׼Ϥch{NۻFN}h"\\30ڛi6]s{8ƣQ/[hEWvק4<1cQ/$)71ڼ[R8 g{/ R &q ^ؖli ;^!B.Ȗ5)Yp W )/m?Crbu0<D,l t@"*1W@@%,}Ïuj)tѝKDd,Ÿڷ6lggP8 %K+1a;7g'g6VIyrz sEoP[4ksniA^b>Y꾩¾!H.G;e`/,|-QOh2HkG[hֵSKl^z b Mj`!=UަqCva%@(jSWFԩ>bp0oH1 spofq-ق,Ĥ Fɭn[cEy!-)avaHѫqgZ!6AGPD-bdpT4ӑʂ$2oHLS&jBjZ|؇R)$o|d^9>1_*B-JĠ(/eRrdRzu)lvj>QDe DA,OS#gwXqw)p /GMFbcWQ9\G]:(̻w\52tՆr=AA,."e]n$FU #2&3O:Sh9ZP5'-{<1O9q8g3b8)s6pq*ʄ$A8T5̘D<7;@1j1 !-N i=XߩbC2 {V@ l#YPzk> S8FVщ}fcZO;~Tˡ:A{q9B{?c^p-V-007m# ]p ߓKJ㚦j/h64uSu ]{HQ)029 QF .U3 )1Ce?Kae^:xyΒHnw.i73/z*-(dLW^Ղl獅ji).{^'Kp[s`TնYйf57pjQ`Syc艢]-^ /_j)cvx$}jWK\^&o'=k_Ź0dGBv6PM_yrȃ/$ΨGzB (3 6CquWLϮ۷~'~ÃJCR0ݴASS8C޸TjaI\R용$ʍ9>x_0?Q>䉫-huFsXˋ9b{UҖpUP91E[n>!<8\ d"#s/@ӐoPg㤠ʴW>> ״՝D)\e*Բz:8avwfkmN\ c-oϪ'J9M%GP=t99h0bFR4%`M|6V={-FbdE:K(~,{|Hj#L^PV.DA).KL`?US_d 86ԩ+MXߎLwMmھz%P5zYT5\̡ &5o#ؔ?{/(Ow )\P5'@ae%vijL Z΄ Ҷ:@AM#nFIJoNاd)d5tNRҋJ'"ڄoi@ܵMWCg5S/i$o dv<^R7]e@0g>Ӧj]f_ڥ[f9.ԅtjcV4k>0<o s¾.J2>7c ̠0* ,c362CH,. iHնڟ{OaX"op}2ZDteĴ#xg󄝆SUƾNd$KưOpY.c*rE]W{_xQ^}=I{mm&\[ 6U2D|剂IӋJq(󴉵3STޗZcB=d$BW 5ӛLvngwp$ΰG\1P0 ;}Lnv $ p-?@L`|cL- Y&LHjLp_iu%mT5leuɐF]<NP|yi[VR{ڏ̲+ճ̥Y.u$۹g⟠lZ~Wfi ]'yUg+7Ž*)W7mBb-Mhi]]t+Ǹnl[D;zjQ Q*XQ<'-) İ,ۗAeg\yR3UР!$&`kEYP&d=g"::mו:U <.;tv.Qn5-U:XQU_;K_*`TLtMIYA$2ʤHD x.߲Ȇw:*(!Jg樑ȭ_TiG%{c %$y,Ci MC-¬w%R=1@*ң#ӵ)S/ ]'hMƋ&ķ/.gĺxobųj:gI=B.J|i]= :/@Ktԭc|1(I[_[Znۤ LGt1#ʅqo;$ S]aѵµIܘ$Eox2 kT^1cYo.;.g)u;muvRR@]:ǰʣzK \'cly #,n^u ;E{Okߖj2aMز7q?>G<.a]/ nހP [g b sodj1}6fл?܎$S{ sW\f] o_=xF ,l84wY3@-,H32'y/d Ƞ/,Ռk<.Dm֨ }P;^DE۫ZXlgUA?-VP+o85U: m3?zQٕ\^A\8Sno_ٹ{_\Mwo͌7%=%f(*OYs*&ԁvJkFB+~AB|ꯦ%tNg GpTԈ TOa! \Q2yD=k/˅16󫼻u@w]>-}N&_d ܓwxr`7\c5r!ItWIg7y#jc"P_`+ncGnsq9V8Q)Fs*\lAu- %5hVls w"Tum.,$piq_5+Ζ6kIk؆Xds}nj*F08[*Y3Jq(EP*܃|>{WuiFiyv2!D~Jfk-ѷUY޹NowˠxV?mMϮBC7 MTFVCb)mqcfD#U/;c1j.^C Tمr60qm||@BϿGc8/anxۭ\tQ+]X.Ō;7U3O4r(˫wa7-z[ffXؼd6J,W >ي a5W{yl9K"^8Luvng2|U; oIHT\ʰjƄQO|+tR qP; dozxtD-#U~^xh6jXGD[s%:C{jZO}LY((԰"462FRWj\@\nf̚,psI&L!6q E/t^|jt/h1w9+5iD'?ȱɲts fё9WY7ʀ'+anq"P2 8:qR[ڴI! Ng êGt_du%@I1d.>,cN8S~,(5Jo{cMl1Z 磴b{)YW5Nȏ'[jXI[ ],(sTSzat ܮb&3Y{'Z]-<(n=>ݗf9WT#l88F[PWZ2< E~layܮr;|zWyY`O VU9+bN5+o+ݑ >з}#LT(8wJMEtyd WKYc BR 5+ 2G5(-$gɸoYf9-u#׈;dj%1h9[-g!8Szr^IO}`rG{[oMd뢍1wMlMO98([ZζU ϕJؠ|N 4ҫzI\ [ i6S#'i4{7 "=Gτ4SteˢUq9l~o='(:7s H~mX0xH|/Q]rݍʲO}׎\U))ƽm\sZ;L>qmȎc^8͗" xʗKMwQT1Ԃ6 fqN{ΧAyBjWw d啹N$z6ȑ4k?#2/ZY ӊe d0%I]?ОsCAtjZ4Br6[nIG_֊!wN]T*f/U(M{ R?SzF0(&g :T_-jKx]=m/VEFЙmjvp2,7\"fǪX7 CPͯs\4;RAHqqNp.vZtX'z՚:@ `CԸ!$ɺ~5y%nP;:5 p+WxKyVvdDA/0@"!E;@^͂v~)ivWr?HRP,qn:| -d@oI$hajl~#f8"9F3ظbk,ãX"+WlJ$<* ib3PEw2! co,',3p:~Z &Ŕ"f(iQttvQ)P$!f]x5R;eu}aV|߻Ed}Ru{*jw5=K~t. aDL8k%}M*k_Rt}!^:oZ y,ߖ#fa#PKjTVTR\-)xVT:>ᅄǐZu'}{+ZZҿ\iE7Z< _"n )Λ+K7(p J4AMqJ eL(q*\a.4(I!doK-bm;/x]ou[N-L#,"A·W [2W}yº |IsTM_p;pj vy(:x)1]1Aǀwa!yU'8Q֖;IsW.*8#+Td#Ҩ4j[϶&0aP[>Oa1t23ڵ5M{~&Rg~L ĹH0J`s:C*`X=ic9} Ċ{SBC u{\9aMsSޱ9  rwjViouN즥zvq:MffB b>`2î-jTAjW:O/Xb I{`: Dus)ވUyKE#iK݉6 у`›ӗ4y̍e˯Gm%݇S,_!JLd嫪N # Iɟ.d;7Qr-f \JX a<"*S3/GH{ Ppc/`$Qujj>ED18ґKF钬d"@)I>J79J^] I™@g4t$v% OJe0ߚƝ OGaAҔ"pHmntVj9,/ |-WX?z=[W4D4@۞CM:jj>zS,|p4/DJCFj}Zsb7M^f`l?-Qwz[ E@bN yNkDHB6Ÿ:Kn <+>4)jp%b^Jnu8*&u0AA xX'hGMn5Ht¾#7LaGub&H`OЀ($rCe\WZUV~;Ec&I Fk~ ">XQ nl!î*,aܫxq4SqMO,e2Rb'T|-#k{34srٴ߮Wv vfMn$w漋dWE1o$?I^>:ĸ~=rM?ֈ;eH>U4;f9M)I<1Y۱炬-~z z3 ٥nXayL _ |Ѵ•L]uO?"'/Z W9q1(`@Ƕe 2Fqp芦K tD҈QИ6KP$>RDR:E| &dfHԄ w{%P~I VdqdEoRϴ;ecf2DY| V97 ;:;Tߋ|ȷE⋐%LL/ܯש;gN?6|_#[@295TsGaMiI&y<+y;{45&SKMz@Y2 k@;00&ܷ<3,L &'ƚM/^`OH&p)Wn6e-i] M%C[4Up3kGY8Vx9F8ek|WLZс/Q{9Iw' %9;Lnܓ>9 qzhGFUlRhAyQ{y~У8RX$^ך5R &WTfNԭg'RD$͘qaTbYac Kٸ;%GM +.|~LmRf"9lS4ZcXx_Pʓp;'0LRBKg}BӿRy:b*zOx5A)/+ z qN--cNCVdպGKWp H̆D%o h&> ]C]*?Ǎ^Kt 0=ƌ6yS/|o]𼗩7ftNykegZ ٬GZ?iYiMͻ쉂IPOPV5G 攷8zRV<)d䢊i[Ϭ(TC'-h_gzOQL+ھ̈/`3[&S(86(ׂ*% ]?0 UX_SU?{5yT !!=6)]:,:}Vwl0S8gsk/$esBk!:ŽgLXrVC]?a^4׹$nFpm]X.hf*6 -@Ar)=4z@~gڿ8~9=f4!}Fb"1 Wח\8U-Cl+` 1%PK:3 QߵS/87g$A<|<>ډNde8#Ƹ͗OY.[ִQl>oldѯ[Նl@#2o\gi0ZܴW`- CY[Tp݇t)A덗"/EJ'/?쪃n)i @H 1P7| \ž̙j)P@ yڹ-r3'Znj_e,v ӏ<`J}c~) 6dy}y_~ƽ(ab/xZ2٠ ]RnՓhXD5x4G;5zMEA5deO FuYWWG˷@iMRh/<">Vq×#ɆK 9K23!yxchjĨ!Vgsw=jp#.؍IO5g}+ϋ'(D6'aY4RmSUP(_ 6+;&lJ_f@uJ\}x/,sZpE!kP] p郘{b4`=(qvzm/Rh ̿OATNjQ{);6EBB0EJ"KA+EلuZgE'HK/^zbϲV fg?mlΡg@RH׶#oqMt**:;4T'o,E===^E)D\h+Id9ovؿi.ӞS)m&diIxIcrt? H].g韱GbX,o%ue_7Ɗ1$>2B5'MvGr-i:+]3#9 E4RTMXokh-ܙ>o0YQ4qgr ^%h4 ҒfH;f ̎s7Q xkViYѵ=qHreMlt\+?H">òuX&0\Y|t!Ηcj'q&jq"XE9Hs(~t;ġ'ҬC0H}t| R6ŝITiOH& OPfQIs b#[e~ $,AnnQ% O3ۂ QVS‚AZTҝP!q5ϝT$!.0rlpeoj6LCHMIu,Ӄ҇L։I=9tTn6S4"p׬]gYZ仟R1WmrGo`k#'5pqǮ\Jy6 +8H4ʆ3 eto Ɖ1``Ns=ΏG*G5"PL9Vn͖y6vECD};B$TeR/f鰷&^X*zIvJJ;:8LeB븏ΖT'mbfEp40^Vbr"? 0GAnkQQ 5vpS]ÉU$e1W l}.%}OgK/46UfV`>:)>cj"8"%Ͼ}a5Sx^iOC`ic?yJav O -ycJ4w` /Lb Y= xUtcI=[aZqδHj*2[ԊR()#N4/GТR,mmm]o3NLڅ"ix)Cax.#"Ucoo$%[@Y AnB{aZdKA4ig+v2;nڲ {_ mQLW؁ڊme_Oʛ2,0H}2IL 8b}wkN۸oZNc 0YILBT!}EOgvsEv)Ʒ W0GCBY/*ʇFI~&㢟ȁ{`/~~랈fhZthca6Oٗr7ֶKGͻ ӧ Nz@*GrEʮ$z%Ix&$dbqƹNyԔϏ 2*PMh"B;b0UW -尣 fX wo|UfnFa6 Ѯ5X ש(FRqFW-nG\ EHK 4BT2#<95֟T/fynAb Hej+[bZ "?VG2J+E} /ʢP:2ZSdž$b" #sғHzPGᆥws" G]1gobnvEm\0O8 әZz@W,\J31"#u-muXC+EC( %@dcpwi$瑻-WeqY0ϛJiҔ+ tY4aLO]Å{[Bk:Rő"nG3/ց!g!i5by,PVU~B6e|w-H qJ`?JbG%!,J_b"CݏvqNmj}w!}"$b|'T;3iz ]G-GRFKMvK13U%/i\ XAqw /o Y;c%:)ߩ" znJb5]WwxBn5;[8G;]lv/Q[jH3Ɗ孿Ы}&ܯ nb,2ȈC?'hl=խb߭Qǜj|SfAރr~&"J P5$\ky6_OE˭-l|4NtiX h!n ;1WZ#2ݰ!c:(Lz:QZ\ඦu?RNGI큫a p]^q\LXwT\PW6 |G_uN,1(rPY'>S>m;0(pT3Ԯmyx *D,_3Pj>6y˧)} X4<'5yN߾L2Su[:Uҿ'F%vX %{@__i&4i@ OΝTe$y &pIB֙":++Ti\;DG4?6:y{Dт6 "Z@lʲA?7wdJ"ɒ^#|2e\NZV˶&/̆]l{S0m=PCa":Iy, 2f%6 ҇)UJ]wA%>*cb<\3gL8w9 3ZXl m#6[أ9ގx__yv@”cJV}bagg>a%=OB0aL` {j$skjS#?&Yc;lF?,2[p鉻tI#bzN_"{#u-AĎ\~c;sj)E(XT*0 ft[aikJq`A2!%%HVnCU-'T6 WB%.1D\0KO'kAY^d#7srDPٺzF{H'%mt!y?o x'E$#-(;1nTON0KtoCTrm#ٰ p'7 etbt@\]Z7}.~EﴸF0([>GC7%`%V2+,1:ٞ"3[ET`O#@R'W"@(ZwZI5{ϼ/ ]^R"}' "ݚM^˕2!xJQ骜DtBo]J$8x"N3T `mP2tԚ*Jxn߻ C)—iwA}G)%DC](84sړǓji# i NBMf*cҠZRӓ_+H=FԸ(?'IuJKB5붺ު45q])H-W/4u*ڤE.-CJ!v,Sm,?~&ksoLh<3#+zc\ 8<NvÍaL"[3W",tX0X34Ʌ bi|oa6R]>52S# 3ռjw$ Q2YS 6! _\Kil]VpzqҚ)uO^ g}oƖGЎ@[C*{|adkyӃYTQ9ޮ^ bɺɆӡe( b3uԓgeqM˵>?^hR=T.Vm-knEpBqsRB} #bV籾2zYh $/'\Ck+g{2oB3א0|o@"ÑhCd?,Jׅ,dD|5;g#7\I~/+0OUd!G>ܳTDžE5\ci-7;K]Z6/-;69nkצitZݦXtt-|&!3Ԗڐ.G?Y.(6ߏ*\`0%ti_Z_\N={Nr,q'tR`ϻEF(RHH1od#0ǔ(e8x2ܥJ2 ka.,GQoivsfj$.Qnj* OIGrnmwt)~2Es> ,$??M׃]-7)r>01<¬ i(3]vzg/"LkFS& dJwVۄH%(L]:=9‹˂ȱ9k2S/ =5>zkEo#ݸUpU6%>/>0,0*0tn  [_[Y]oYmC2/wR;)S`BRD}vGN`n{fs = G%^,)dC$we Iݻl nk"pF}:tnuX_ ٯU6n+8OџԲH\(1~V29KV6MzohOk!+5 "jcVQ}c7era /jDmD{fF}jX tް71!SEwl]Y M秉TCjP($|,Đ38OH]mx?#`Hia|=](gsJ}5]e8hOP4%%ܸyWfo^""$ȒeSϨXӣ;:Y;` ltv2wtzI 4XqI3OBuݚlōԸߩ\3 uO*+"gnN7_ n! B64EBNIB̚&+S/&QАs[(ELC7)5fbeB&VID=-*^u&_.~ԙ?|(o{~ hF{bs}Ap_Z*०{Ejf߯5 f&Pő5BjA =fI1\X`uL6 ޚ9m^e_շ5.Qr2Bkse |h42TPڔ?wr`M*°ݛkj 8ZQ)`ʼ7%{5䍴:wp}p/:c2^y$üvFgʃrxy IfYȟ_ ۶dceWKԸc ps.ZW=IN|>lk'ܑKR] V}Fg`o--  u_"AxH OHطy`N`0Y7Io, _kø tܔe::yb ,6_[Ux*_:!(6i.o4 1F?Sh^DV;tchR(#úo8xl"}Q:@CVKͺH rYIGei+'u;M12}p s\LDhM|oDaMM=w]c%$7%04xaH@T3{Ny76SPbᯨH ًTxs7$ha=Fpяz "/ηP0d S j|UZQfs)S/*Gq o9d_/;dI R/e8El)_9!o폥 \X5i?b\Th)"DIFCb.K+Hs2!J-p1v{I>~_rcunM՝ >VPK8X3!ĄF=9D]o>&.ݎQ}O_[!h1qH):TnƸ6yѵh<-d#<ۡ!T, YGU?F ٰMǾNڍûrDև\gOr~oJUٜN 1{U&ۈM j}La%1+-rdLj̍. {vi#2Y98&3-;b*qR{`^"[wހT*>$לp'!+!ݬƌ ѲbkI b(>w8xWl[,)jN_ޜr{h]lKɾ ݃MhtLvmZn4`a'"i&/!sY{V8Cџ떼!zJzXᙑI!ϭ;Jp~l|}lm7*fڧFHƆx &<{OUprPemO4J&eZs~=p_mfKe5$V&'@ 81^5{s#" JAAVs l@؍a=3fHuV#z7vZNeh V+@Ɖ{[Ɋ^CHe 7 8XeQ'IFw5T\\.GV2[M<'h<  wXcu崋">=N[gi颎Nh}KCP26“&/J7D) ǫ@@~a,t綠a.!X8)~WBmSLw!K;\>ν9f5*{Ԣ@J&rWՇZKnQz/%c~r*/q5J̃PEt4ƦPJ|giU_ݦ?)ahzV[$.y&H3d@2C'&xzV>(<~oX[v"t*FСQMFsڲk 73:KBup"){g/ݚ{ߚ]5b}ح- a#F%/cBGV؉74Ru LK6x#|<Ȫ[ByB'=_Ϭڻ[J1:;}7C211]Yl,?dXZ|_V94`f]5G*D?srߋ=&X+]=1ɜ2هz'2;~~ќJ㻖ZWX1gD3w#%h,,F/<2t7(#7eXv>=V4{'Die]2 6rZ=D C,P?9#[osoԐa3d.gs6o$WJ,C&-a-Ȑ(/7r3H5lqwߝ'Lk:NZ,V/M#2z*Q0x=R7w)ݹ-GLb8:@/ 8ETB`K?q9hUm{p52IJj@)mT0^ I؟VJ@^1Q) *]5hL_F:OVIȞE!%Yp<VT&U@an n9zԻ°e$C:RmUCSa waq5W!^mfPҳu|r0Ns $ r*]ZwǕX xZQP%<"UtaY=Y9lj+d%C(ȜhP(TdAR2Gx {.C=O7F|V~lfR$ILZnߜx \" Zz9%]2*Dv݆<%mz5}-o5/'Nؤa1*s_{[sH+XS" aK0&_`"E\\zq=<[aAx]85^nRazl_MYW?r:+&R4)v 0L;L~09ğ]o";Exjx^ ~5]xEBVnZ@-R|q.xr0N@@*~g5V]԰xm"s[}9٭Kzyy)Y>V¨7N7u~“q]?jNJCx"Wh0ğd"`5Dk9K>arOy<W{V[>:gL\y~(xN,^%A*F̰5 řr ZWu* ,Ympe5mٕF@A6O _e@˅n]陃ZRmd!VרrT?]C8FU0C$@yxMgL[J#K!(QEy/-{]p(1.٣Kx8FNTw\ !cQrKVhh=Opogf߰S-`AB vdnǛ|d\P @{2Wcר͍j{>i||;\1) g¡g/sE|<տ8 *(|v2̏vb袥Ge[JkmC3F<|:KWZ' ~ǧ;囷Rm- f K抟MBL]+t^3jrV.J{SY>6^co~<57vebb{&-~M5!>Eb{~EQ2FTj !E7ż"&W@!JŃbO`~J3I&ߠ EapeyqZ,<,^$ kI+<7hX ^(nV&k.~~d$ ;~??UsO}KʺlK]C!r8YfPv4<= fxm5;?mUb `Gw&>8ren{zo'x%@#U n?FO0] UN=s3}&qMӉqx&['C#MpzdiD㪜Q<}h@7eCܴ<^J*NH(?L&xq% .u-PZP/CM2x/g0"`HK0i(b"c/esj.?$^_Yyby]b V"ٜW:/AαVEn1f0c;y_73d1K0!= E,#BDJv0—"Uh|5-}Ņ¡SGߣ#:WrqjYiKƩ lQ<)Nh,$9{_ۜENQyCERYYwێ I1as7Ї9"EZ+faѺaj =M5ąwʵxMTFP,U-cx#} @jOͯ'ߛu'lP=S cTʫZyVA4\ _x-ݜ^299.V4u'xN v}"ڈs22r):8ő2 {FHm?'@^<>F}WN =$.qRŒPj8z IY׌I(,#,Ԉ*[䘤I)[s< |3 B1\6,vCO rw5]Wqb5 D;ʑ|Uo;_FXM\&RTz+Y- j& ir}.s3A@x̀(T5Er ,hٷv()ώQM8CA5l{|(5hߠ>& (@ ^gP26~HI'Dr5[*9l;-0hڏD6ρ\2iwY鶢Ns~ ?&!G,e\XCr:_oʅ8Azj{u`\XGА\B2;К wN_ nkfISsco98ei4^MPT0۸G=U̗~wDd"{bN~) ?.;܁8EBSu=:bZ!}gYIYx#of< ԱNً^ܠ" ˑ- uv'bꄆ;h}Eyil \t#崄B{MLR (db29{;I/"6+!u0.S&Prr:9EŁS!Сڰ@Sah7B)Z"''?8CebŸʟ>rT-Y>eEĦ vV\I_[+h2i] eU̦z)R2A,(E%Êl,hgxO,hѰEB d,Mv%x~)yDGr;zG4_W잰[3i,'7j\dSU S@n mq ]ˇ{P HO40Lte(kWM +[rx;wgWfAea.!gei9epӵpKa.=IH}GoǦ{P݈K`jmJV4]{_øQ,A {]BCWO.2'k_>guU !UB,:x]4b;Fb?$\V &%+9Y?wrPoFU3#rA;ߢWJ8XG6>7v"~2>fAXʂ2vA,e"$\~qe|;{qC`.\ىRx1ice" ^ C,5ӛ#m,}î R okFcShiDa%L/TPz~mz4u`Jh <\dِ=Nxpf+7-XC[T ."GQCleox1(lf7);>ڀJ=vqX$L:/ t߇`c`f_]YJ|֬R[L:c9DRHcEYS|o)QXyL!Py8^;,=@;Q?dz؛5#"~-ڹCgxqGh"T*F^KzܖIT% 1m莖9[(8푏bލ7#/ZK>n¯tїKtDŽxwU %ZSumQ2U`5Bq#ՓG7%ӾQg'Q&3Q^ p@`TDјUuX)DvY 9%!V * HEH <Ɇ(9hߠtPB|׷f{YUW5mh^t-0I@7AqHVP;:h lVUC(wQbhRzQ@krXZVCwOfKt1ZOQe˦'E"@8Dh~Iw?>!1Ӆvd k=J~~"@ϼCHVIg={N5$ZOBI̕0Pooae쾸x:caƀWY˧`w\W=)o73gڵT3͂H[%]TĜ{AypbqVj^XVTEl_E鰷4=Z]`1ׁZoVdc$k|gk(CL:FK{([9ƂZ^d^z4ʋtuVZ{a$S[n; !N񴖅س-%PW l+eIG,q`g) n@tک7𛢢Zq8mdoaeF\yQ oz\he\a SJ!DW g|#8 Es4 >jߒ}<~_bWە54pTڧY%b&g;pr`ڃ8w McLx/`2j ^cEIOcAM M t&j˜2K挚-0Wmlֿ>NbMV1 sul}9xl#|W1B#(TKVj>b>zJsD2~x>%mD6; RĒp&T(Vno?nidfM9e ´;(i_g L IOc)-dj8i(ѱH{)QİKkoL1fy%`y8=VW4?Wch%k2: S\w[1R)3rTEk 1:+'ܝjx ʇ Au/HVg2;<]>d-peR&K 2mPY>39)+3{Y\7FL!!yZ#%Wi&zh1 @aL9ȍć؛G>n?yล0,yJheb50_U9(W =V\RNY#3"L,*ժ_[5TY_W9rVhv93*r#;\;`!;I_2Fk0Nj/3l ݯ+FQ>f{[N=YU*EL~s%u22Ծ>'LJn-ym,Ɂgg%cߊP;ȋ{luʑ4J2yiWv7CNQf $ll3l&sCù߯Bkb! ,l]ɷޠs‘tw=4 Z -7 m7ֹP7WϛجԬw`qHL^xTm 9iۍœFhb(5h’dGZk?}^fP:"0VY 6%]BB+6};@wܩIِ]IarѝDDyș}t*^4GH.0[jd)AC?O:p{onwI =t=8bodBE^0?ԷOoi,>QB !N[S0=Ge}_gdpkCbU(@!~'M~ծLJ=þq^j \Ђdq؛y9Z ug'Z-*몽`B9@0n¹oB%1;|X3< WD|~717lajBZ씉8A@/@5*|CڎUg~z;2WkwA^gK>>r]&8~zl[/&aKjatA$Ai)c3C*|Uf_Aĉ`vpm#A},0m}m,$Sۡ"0|n0JɳgAW,IƢ(y0 r#G$4:H^G bV_QC-7a4s{P}α 1'0c7G:Z{cB%O\ VBS@D. 'XШ˳}@ƫiNjG:wV{$`6[&X0 B=$z\NYj^U>~|}rj?=u@lNd-r_OūPTEhq(7E Cy}\M%Gu;;9K5dGy!: nٹ<"_f$Pv7XERnО" k fͫfIR+i1ifxA'*lPÂ0S&<&afzQw7PSz?Z+/ʬeRi#jߗŠW;\quq/!U Tj\!EG<Cď~9yg~F4F3z8\$A } lH6&ެ?rTTJY۱l SmM$:1g#Ú|ߡͷy2DB; G M9UBy4cf{EmPYk+Z,mbuIŊ ۣ;ۍ /<-@ Z6 u&`A!n25`nTKm4I mqeBCBHNZ,OӔ> r0yj{zF>sJ4J]@#rh@y3O@7 x+Fq']Nm\[%Ml蔯tg>Im[6D._e/Iԧy5f%QNECvf8K`L ݍu2E^w@}-SI(ilLb'E$}@y,w#[{82X:pmrK4_UT(6D َV=JiyF-׳ƾuqF_M I摹$Xh ^Jw O w.ydJ=| DzA%ItXY__ YGEL IYwݿ]j-b.M\cwu6`6 [Hl萡5 a=+9MZDе* rl\C@͜klr' 8=4?e({ !S'dpi?c;L_}y2t "O`+XX 8 KX.)[ M67ZuLgc&{ =#m[V8VλvKNf2 ϼÆtlDsNLgJ =y0.sZi@vХGUx-9+! )c"E ъrq]>Q@Q4CkJ,ͫMLtI)֣:sKF9$Lv\ Rvߚbuv1S8$OI)S_ fUPme]Gcдn+ܡj 3@'6ǫ4Y:hCG%$b%X; P،>/HTx\ܦbhIx\S|4Zڽ0I<5/[hajن[reF~A2BcjL1uqQ8q[>4 I /d#>yj0@(O S|#P-AfNh0C -b `;0^VnfJF~n@R ߥ%{%& (2\߽s)N.dP׶_M۲GdiZ'z|Fz3X(K@DV/KTJ6Q㩲)I?5S3MF <>AE3mW̬EKu $oIgS}~$ |pOL}b-Kl¿G "-W7z:wSfr4RWN>4gURS6cȓS^_Fsyx$(E yK!΃å~o8&.ktwuK6`0bٮXPgZ!*]@&7L⫺ L⹤ ;M ^" 2mu5L 'Px#6I{ΓkE@~5?6k~NÒ\#uqk6{5_SGYOG7@ƹ'i)rX)糥[4vOzN_1h%iI7T( <%QG@U:wT#a8%N=0q\X7~ZdF{1sm&ρTaH5pHhZ!#YR*֦qȤtx0!~jS!娓Dl_|"?#ih C+)Ba^]O/xv-{"i:ad qN{_ڄ}6c}}{`8el!YgNܬTK%>16ND(Wt䇱# zNuTViDBa +޹R}:b` )pW)iq;A[7,]NiTX?n2oh ]ՔD(ЖM#{(C-|;h-S8U ^R}`p:Xy6 Lp:B _T_c `ky&'ǾQC'>O|EwY)L hHh*k<F"d8)Fӿ$V[l&:JJxÂW4%tmk8ͽ03\z{ݜj獆n୞!y0)'y_s%=$$Iꢞ3S=]&A@؊"śF$nU+Amsi%{=cqzwWiJ{1|@A^۝MH 2BT'_yx w6D$p@KHP^ 1B*&ѫ'=A{+Ɛ;xJw{Jy^fT?\]je,8rKW?@YFW8Q:wއPxG6){_y!/w:G*i%JM*PR^V _0|0ӘLAp'?.xt=6wIWuUʂdrO DBzv-hYV2[_Ndtfk254`7jO.RةT_>Q,FŔ/%>܆[ᬯb:%gqӓ%D |lu͛U*Z:\ilw+hÞ*0aD,왋˔PWb`ⵓT4" ,ngz.[ )IV"xiBْxF%mEG.qͿҽ{`dN)xf'//8( (=lεW\7eXh`JڨBeÔ㯜/c Vfky_%T >ѦX$(z"Z`d̵0UT%j 7 ED#[Xz cL=XUЍVO/ m<5Hmn272s7RLs2w>Y}oT :t/Vh}8<貞\81 h Cwb;OJw؈'*TAquu39n do|X~PQEH*<{?a&tj?d|~'B|}'tz{ J$9 JOV-Ywa] UE26|ܡ9 , {ѕfR`"[VI$W>Kt ё;qd6%E{xA5Lj(9e5#ݢΝhDƻ4>!i Oo,[OGUa5^ Di[W>ed5.wHgW*֋b$?`|mf4` {zg}Z2!*R Z| Z)QIbä WT(߼Sq &P3D9AF3ɏPSF}6'3,:P=7Tb?SJ~ƒvHZwiM!h}[H+r<9Ū3Tޏm}3p7m*z]>;L\4Ik{lJG?ݿ7C3 T4PI$6EJ5Dו'q tˬޯvYPʞc]jձ+c_w7Gs˜Aޅ6& bn_F,T ?lGw<-ʹqU>FbѻN/]HpCa4ٻ-ݴimyz-gIDXBt΄\TllQREV^T eb:p<\i]ؤ8?'ErEy-*ZؑI4o9*)N1],ok MB`޵ 6& ~40l'8۽·s"_iG9Z,-6~>2f%Axm|38 tڮ]Tze-uG?m+a*mN;N3 -"< ,TVvKcWKo'5O$,=Ɲ >i$u7WMpx0?]gZ ݊_}_7ZWfuZ^glǕG]H uVq z[0b:_X[E{Xg`N: ۺBxGԹV)tI~ Zb!q FvBRu{Wf`ň&"P0. ,ϰ48zo$o5 ~)LbȗȾrω¹qVYeiرJQgbal#n6HL Szk|& axҮ,{>DU3yߜyÅ1>SP_@BZxJG75]AGMԩ!,SaM y [4JLk>3ɮVV#]:rSG^`y Hj{Y5aBYU*}rt"ahs JGh;쉴^[4Ecjh',b)D +8OEif )v5ֈRTxx|5Qԇ]"0ZF{r?ֳKFmXQGVm>Ne'kMkkv7䥨ֈrGIR/pH&W :bb4̽0]]Z FW#ZBԃG9]J;7E?FT'O2WL04 sSD:*y=oSk(nWv]e[I?2ʬ_yӾsjCszz{[ͭlc/#<\#>x ;PهۑN/eGS^gIWj#-+8o:.8WaW}EI(H|3-Gנɯ(>2&(@I> Sq 6ok ua˓N-X[Z#{0%omx􄩓ǵ@PN:8Gb&By …@#4]}t1,D}/$P(l°W) ]7J\TEũ.ypQPWjDaAs' ]CԼr%o{4I>rD}y(*jluHӂKӓkůelDtZ_jۮ~4F^4^Lگ"Z!}w(7]'d#"bq~ '64=49Z㛋Aq0~sg}C)sOf(!qJ@NO#Òۃ/Eħj6=,^fؾ~~Q'BHg;4SưҊ @uO`<`c Fs(e'[4T,0b #CeXȅWLCOF9Əqx|=Q]d sGb 4:y$B%*U0#ymzW` 1n>V[k6˧BPCY<aZg_'  ڥ"/ 7{:7;@^ (zq UNO'oX8+r^U jp̛ͩ6V)l 9'ⲏ$@ o%AM1Q_ނCnͨ<55.6D%v TdV_7iz{.@ dFVU<{u !Qm7)l-z dq4 ZFѳv9@G2R;2#ROqv$i*YT%OqO^𝭮%]jY/]Y?ˍv7dl?}>e ޠa:ҚMlvΈ)_"( ]3_O"{'C؋˴L}Kٽ}^ `1+B:yoЙeu HeU1, ީꎥL)J#.;AiRP%^/H1 ?@˽|PsGdW4 vX7wV*}Vawq(R!-B#xdD+0P#chX ԧXvev Odgw SaMF,[$ M}|4QNԐa՝IQe|yX'\;oҬ hN.|ڛe uw^yx+vDŽ[ "o^"牳y}&nn=jvR= /| ERN"Ҭ) xJG(귬@bl]_\#EAiva#zgޭW]2) eRo}!ӃIz8!lD(MA6upr9^n mw,B/:r+#X$fE+APδ"c˃L*IMpjOb]/þ%^zCXx2F[ m7SNm =N߼`o`P0Gxcz73(PII-| pfeE4zPHzc? 4Ю9^{b]υ 0ӯR!zo_"::nʚUkA-R#O8U4w}TPm"q/oF! -UۦL [h28 鐽lqdYBTE@kZ6:kusE{ZoρaMׁX="_D(fmt ÇK[t;F*~ |na-|Ex5;i}BN浰4 p :zqOrg@Ճ=p1K&ssŲu9} 7[' p61 `W@ߣ_if Lyp{M|yb^o:C~AA[. C% lKUʂ {Q&7mO&M(8TUuz-ط4D͠PGw5ï")-eq<=dEq̔wZAh%lJ a4E9%vlK!'ve}JO"0DIcyOP6{G[ʦUA; Q`>a/!M>ۡ4e~Y"hL`7R]xo+zLw3ȧDhIrx|Y.dϋ Ǻ :>p (8 %OGgϞ,H '1{ &(dWpSU`MU(zw_ΰB (<: L>gG{D~|]齁't0Rѽп4!=ql;{M[oHr,TεvOTΒLR(k+"I~K\#55WHfoN[/ˎJMu?eprz3=?0 sٞS辨hw׺ WTh}v(?ߢF++F#f}' _e?/|ٴY'[H樦m7kvuWC*\6nij4>jR,Qo<%Yw(J{uR RVv<ӗidnor&P?aZZ2jR(ߪM[]uz 3QҐ摧hě]J}bj8DJ\ߣM6Q"t-+H6%Yn ( e(m7&`O q&ezv0I}ejP Wޝ +֚Id@hƯ,ra{ErQblf+v5ILY/²(Ji~t{H|Vbg< pNFT%?aHEIW% [HTjj]"&Jx"U葅At2.6y4@sZ`tc ^R2ϦZ `.#W`ǥ.ְ ,E%j|cn@/oj?Q@Bg-g4f%ý rnZLLSW!K =P?bz̺"R}̞*h_;xtr}psNj*tfچ$)iu5|}.]Ҫڂ&sz?zN]ݥz# ٛNkvz{R;LqȓZ7Mk٬2GfF(S.<ջWw>4MtG|CdZS@R-*)1(|_NpR`a𽖜:w|蜊 v3ߔϤ l3d1ǪCng_cmm۝t#@2 li鿣UFwM&;iگ\F|Nzi}sCfBCYG0wmX(tv4:}#Ea߷]X}g/lz'%\.otrl)Dg >cbh5ªKջnoWjԜ_@+8lfZ~.H1pκՙ yǿuz2 oAsZW,@.@Z`9R;hep%^+ )Ʈ1){_rVPPjUu[|h'(؋Q5jBz_QycEu'Mno!=Ԛ^GywB[(x < &Ǟ>*e(+Ws tAW@*:}bf_en R@$}9vEBп˸/87a|n|R#1BG*E`T :M J?5,ڦR!;'+`U6Kx/\oWIZ%t}Tmp'Mq93hXIm Y?WiYDW\3xVkYaICm2 6Ñ#JsLQ$MER'/2kA]i஽%S+(6޲8&iH|GWN1NhTl7$ҰЇٷQvh؁88e]oG tt  ټr9pgw+e"@@Z6LJ+8 Aͯ[q 9A,sAZvH:,l U $21P(5k-51@7?>';\8pc8o_]fs-O]H\!mT~$% ?"wn#bQZVlƷ|CT);Xx?O]mc&)6ys=Ȥ<9_zIvQ&SN\EZ6*4knE}a fPCs9~/ 9Z2"WK]9:Oپ*O {AOSUp[RY>$[س(8r&8Uߕ Ll~[)!IL|Ł+{!r-49'/z 5Z';4;>.C3 4+ٞG$(dlC¿jxIB~Bw feNh֦^Cd?KIN{< H P)7 qp9Y VsFn<@8x-v.B:?lݤT.D4Pin2WU-9Ps{(= xQ=ڥ; # Waiv-tmA76w b.YYPs eD׍ư($/k4 +񑧩%FxNf#;V6ȶ2Lk>!ZZ]7%eL"-Q{8MػEȭYRi f,TxE!B ^ # &-{xk]*M(2cyË&㆝h`GЎfKQC[8߉6w*)D'ogFU)<@<,8{%Es4i\@?2 09QqM( lYE`χ$i!OKkEJ*9-"PLj&(=B>/UJ/o|RM6$ )ŊR Z/UnyJCE |iƝxRXO}6!6-QXf@֭M/H}|C0dNv0E¬Ͷ}R|"`RFL4-3_cN|d2~'sy2 &1MTzcRa&#`uw7钢FPHD=щ`Z#C!oiawsJH %Us1QUFĮy}bN <حEs;c}0' /hmF>`N}b|3l.fZ TG[,%0QH lP |6TN@pKZ4 ZB ȥ̣I]yitRhRf"_7kTiΕ)Cދϙ!hW`=OlH^L!|åsvT 6"Ro$t /3vH:6c!wUQdibESK@0ur h5Lܳ鶿>#:c N{u>N-Ȃ^#z6^rKuWA`˾) \ę]-!w?sCBL]&|86{ӽ{(#qԧIw Bhh-V4 o)'r1:7{)A33MS[',Dh,|2U0 )h  H ͐X8E6,&h :ZܩVXR& KG-^e b6qC\؞ t jr aGsٶ-a,g6NԝWhs>0=EWsTsM!zz˖)!Niׯ4% Y}T^ FkM 1 @x( HZ|k,tu|q Djͷ p80 m%cn}*"H~~NlEP p T<$*[GoCޟ 9{+v3a8j(@bGp2C(8~.sv@_y +Y>KXu赾=c *d%"Q_dag@ Bf ٠!'lGJLwޘ;aW Khr8Vk#;ْTL^Q٘K?xWk%7ono'` s*N7wdؼ@JlW2=:pI/%瓓K%58nǝF$$XZ$ySy@܌5|+޸9IlC|5ԕAGGw27ddr6MߧGkxS`|@#p  ?x\JP S"RC Zds)Aq4b&+>1qwzuW)-3q M.<<nDCYX#>őQ~Sʒf6X[ϧ4ζRwZ0C7zvOIaמp*p$W[ClRtJ>A CNB?*8"a,z{vUvdBz^"MMa}T$t"[‘X'xf7 g{'K%bX8#-?ZA]t3769xQ4`quz[CǑ7_}qkt8~+(:Q0ߨ273#='R䆗~\ Rp6^D`gP<3 *Jo_t90rJ vV8o yE|7;l @[9eb7'..qXJh.RNFb_+nI:!UݻoK$ sm8eaM7\(x`2'C#٘$a @-ba8UD.uGA3vb ]hm oRC$d5z7XŌNnOAḯ- G+1瀱i^ǯR= !4A'@Ho2v¿'uF(D{|a1E /z Aсa)Y)AWn2G& 4@2Vssݳ5Ff>j#U]" =e㱟͂ < ^4&WC ȕQd#R?51NRmP2MK?mGM0G]lgM3\S#c9>iET_& ʓY {W^F+%~"Zx! ~ k%yޢ'EMB\F XFQ X6Q(_*u$\ъH|6@oI^@.AK(,2e |% Dd%+ۤ?Hv[fz "\6^0Ɏt*}?,F4+Y Gte2"^#HjِxQ#vb}oSF u^('CmL u U͘kJEUpc'ԨcGBUƅTZ섳RPicj&{_w]'4^k.c:[ǤHzqYXBv;ډ, 6tM}4oK^&2d3rS=f9rNڗ|ArΕCv)]YZ1Dh02y q`q qRfˆجUuHhf{ 7ګr(-m"59,#bC+.$Hkb~-n~ Ŀ0ƹ6ʊ"!3(`]FS-ɤ9`G(A!óʩ Clc{CYzi˩={ N# 2OK'SHVoGCt3g; K0ޏ.1W:#"aU- {&Fm8X3TǸ:=7y9T&&ז0Nݍ|fW]ӴR[oN$m}_޽aP"_ƷI;E0}ؼ~^R<-@42[tvzN{ψevRvR-jA |=]\dYj5x=lJPk$L>9O N)+N6~3_Fzhaz;U?tNK vԣnb. Ԇ.2RqJ؋դܭ۰L د"fs=/>ԱZgِ0oCK`7%"Ew|oōgMIb)Mj o fWUε#uǥpi[xa2yDS\xbSP0ى̉;Хmk>A g[33XxZ͜H0] z }GbM/ᣀ3S5\ő K6b]$&_뻬) G *I* W)U e&kCRM!m i2yVƋL8THm!:PgNB<`j&c4*~H䨢H >io(곘Cn=92~ aIf\N>ik%Hu,7S3Qu-]9`FTe Œm3 Vc!$/d  ܴmaR+dxmhJI.¤R*K1ngYewHz_ CV1fS&@`\(֨i\7!-pοs3:#QkoTEяnp֮a1i͖n:b20Gd'Ӛ{}([WTfX$?Qa'uyM8fV0Ahi!{R w8b6'jCiQtz({kc%.*E`Q\<=k6wE[DH5@jtƼ|ua Hq4a_Ӧ stwR7|Eb1Eg "HgUN6d ^RnzvQ,Us=iG7'+n`_Uxg~~9@ .p(rn⃼yͲ\69ϝJlřLG|#cM, ^/}gwag&-N"C4dG^`m\1tRM H Ҙ^`f#1Nη[VOENGO%[vGnB+ţq)t${"m%g:ԟ_f< i=MLĕn&֏t$@%zecI~cim Y%7g3nAo9eg1(F/?񅥅J2[3h[DcEE .NE7%JU<cZOOKM#ZzqKGpš/*w[hk,!Csoc%5Կ7|t#rCZv=O."fdpuq0?1E*ԿB=Vm ok}=?t!.ոT<˃]ڋ:tp}Q()XH|P5eqW(^XVo1]qQ ^y{is 1ob;~tGrؕe0[s_EE^HV,\x@:C>xFA"fWyPy>A$IH~|Ch̔Ab>XB~C'Cϭb\ I,Z>ZfLtXp)ǞGeC`D%(w@x "&1+4%} Vtk.T >v@]EǎB^<4ήZE-"ka e[Fe|wFpp1ߝ M ˈ`e Hp5\k7}bT$3 QZe4ɏLf%6ȔK> 0YC@~a A|LB?+(W 6e/ϟ#7wab;UGgM1(پV,d]7=̻\h|,LP0nP 0e . 6г X.P賁V|L.~91 pXKHk'AFiބ8&^O OWh_>fcȎMRB͡asЫxlzdo,0CHV,Fl}[>;)A^_rs֟WGc^b?R|RLhŭToZ!{%L{4}YV?̃$rI7{I+`]%A|XhC{90Jpo[OvvLmA%ʐzcr7K H/c0@G"9 ;$(Hg}fVppƹž!":gIh}Hw91LwC^1A>;q­rr %z Q>W]qP=)Rb96şE+YvEB;~ r!7OW:*F2\: sN~RC]d$9[]K6Jp*GpǨkNw(9G׋UehR4`x9xOd9xiy- V|P$ߤ@QZB{C|ch2iQ +,d SKgE@᬴`b`:d.MygP7wFvV\VMJ?RZA>E`aΞI( ЦOL ).f[H9+mf xwɄ#S NRDKBcTJ$|(hװxY?{al'LM-+FeWõT O,2F]ũ PXaӶZ*.E_n^ 'ě8w m,ԣFI޵ };?K뛟p-0]cX85Ó焺7̀ma}"1vOL IT~Ss; H,y-Nܶt‹Nii&c,SA/o_hc ˆ aS'ݸ1+xy[(wKŐZݎ*̻qPM 'E o݊xUsø&)q?*tY3G~D?{ x2F4<%63w{=بoN"Zi*5~m${pL*ݣs=A`3[-CJͺ=wMd N6/垖'NzL6|d[xHy:S0D9<7g޲\6?bAv`v d 4ku݆R K;U;[L5a"x^ *ZP&y(>\HmS)BJKE))9G>#/Pwz h؉0 "AЩ0sOzh*#UWqrI$ƼFt$jn]=&' #ɚ(Rn=E)[VOԗ,z=j(`bpf&2=X>]봧N>֌gr ]xaf{1$\n4 Xz:ejɉ&=dޞZ5K,pxBZ/?嗹Em:s!}\I ڮ׺7WkM2Sd6/ :jS"ZeT~%彋5_\ a -N;P3,A[|ÿRE'dͽ(DDeT +AOH,Ai\BH)Zk$X|B$ ӟޑG ԤFVmaɹ܅at& _ [dR@,g[ڡMڷ573L? ')Hy]cT}]s_{ıt:ˋ`p"L=9ڥoB&2~]. Kq5rTI*y,so^-jYcSDN]05uүEZ3 p7#c4*|K& b7 ώn83BaHt b'ԧ)@uڒEZ[o OиJ3 뛜ޡh/@Cuq |6~?єlv0m;nѵ*j\//Z%Iu{wfBBxmJAkVaػ3 @ڧ *M2 >V5|կ+#x4%axݴ/EN镰.Τ\Qu!2[B %zW7}?'xy[ˢפ;/ o]o -?%Z<\nb`oIn/ڂ?;"V3b˂6*)Wt=`%ˁ~TلԻScKx°BȶTIb! cha2wv72pM*yfƮQ~G6եE?&<4%1:S,3J{[Q(]iV`&t\>m{JzRGkg{;.U%hV9u4bHyao(Mx8PֈR)3lÉMImGVZ&zk+Wg,f߄\Q4t{l?[<+F[4p_ d+kB3ބP.9>F1.r ͅKPɡQ҃M`eiQl uhk@}>O+b 4^g?0Ĭw|$ AB =X*YF1G#іGӚD,l ShdMZgz I<uHaCy[ҒA=gt3`|D8-P&;zp1!`vI!3zCE"v'; |s6>EǼ\֔s]{P^)a¾VZc7+^2aʶ^ں}vc1"%)~tm&=B#q2/~t5 E̹{_Sʐz tI,߫ V-&Ŋ/ղ45N,<_OME5u-q}& `1`nE|&gt阤7?GR܈ &{qސ1ڟunTEQ4; k0-4 8}q)|D]WLo, U0Twsbm~hu:Y&#G>[wy| {L_7ϵ@/ӚQܕ@.7yڷM9@E50^3bڤ+@<+<qW 5tҭER7׊Yyݖ%NT K~WUm>ni/  P^c]Gvn]dYxf 7C)X!'FS/"85;k]ZEzey؅(6&N&;Za"u' ~N5S FJ$r? 6m.>gΚ||@#e)ݎa g ,s>uhs $)_YE;ZN;MpWޢfRrp8֨lq]p^ Q:)g|b" o~#>XϫjAds_! _?9IXʷSkNSHܣ/(u/Jte¹c'aݶ[m?@?˪!SHSp^zJ-txkI-F5}LTJ.QTr-C2zAzq5ղcs׭$*qQPGa B5F ,Rtۛ G/_=4"DƏJҞ&~bKEmSƬwysO; ]^1|'}T{ߑ]Oջh]V0Ipd K|_-*DLɶ;OzW)PahJ,'%eߚ`Q&irͫ.Q5t+˜x?{j}zu+c꣕fM.H6,"0v"N"tJk+Hb^iٞZ^[;2ɔn+hSUF\s%AI'Ck5~z_szC&4fG=$+Hv謯5@]`Z)X0u"꧷u6 K%)-w%#if*trT)B Ix[2`:wᐢ\UuKu{{I-v+oGS_g.) TaĩYי"ء0 GKU; ^d`Ť0 3H]1Nvp1P nɰv3$ӤGReSaZ._^+SSkLGpV2?{'&buӠ@HTRO?'g[QOfIiQqc}XO' -x0AlWHC£)Ƙ8, W 3(`Zr~/a*6TSl1}r$k}'цñ*ź9_˚]x)0ch6S2㟱sC@Oy6%."pb>6>"я#:ܪR;yN4ӫiFo1:jy`Yɣwxط6>i2Zc Sfl b)FB* 7:6b^A$^-I;xQh6Txȇp-$ O,8Ȃ"*]#ygJ Q;"Ϫ$xw%{R ϫYh om՛u۾^<ZW>ҝ/Se]:)n18gڗ0+$_`~"BnK*{PGT]8\sW?_ptyF^2EmQC'`t4& md-{hS8 /W9a݈9 3{}T)k"tET)NBmI&A"WFj $V@a"y V d5dnu1 =l5F?*q^.wEgs#߄WO279;2X_i?K[j]0>=9y$-=H)b/FL\!}\}&&(q=%O]ըId@w+PD2BI@@|ʃEG^DstKm͋nۇHFĨv#9"VEC)2OX-'_!P,z^zds0k|aOO~x?\ϱW>b14 ?Ĵʷ&}?5*S0Lu NxZGr} 8fܓ5uRri G>7 M|! [F ?mO?G[5 `*'8/*BcTQwE pۨ =hЮ>(O&ܣExDŽ\7TH%>%M8ŗ?`Zc>%f0>1PM]b:kP"?7HK*_+8IIozreb]> pt,F2()>fݜrkl_U)09>k̢}ƄIrf&W;l&g Wս ҇E"vX`/:٠Ǟ0ٚA]~_} IF@ςha' =vD;0GTZ<=xӲQhP4@;Т} CKşw׺TʖkIh\K!ړW[ˌ?;bU N_yS6x5_#z?Mv%臞<+kms#Xp V`6I{D?+n J˒^֫e^8@&#s]<)2f:no\ʾa_.#D> 1$b9y~#gLK׵$J܇!f?G.gYə1KmA Yf'P϶^E|RT[,d%FVVV#ŋ+k(xVv&[O*.0w^ yx4wMք])f`׷l(4&Sef9q]G/M5ҩRzS.^B}U}|އJNp`*%R.6s8i]gt6ʂeu`C~/ Pwt4b_u+kÜv(dm"Fk_עT@jg̪qI|:TC˨wҢbSa/d7vSMt&l7k2g>33t `Дo4,8hB!ve\NclejRDVR!k)ϧu $ۉt/`RZU/Psr+[$:υ*'R@8k$FMk^u|]*7( 0J Jff/42F0&jxniBnR`"bh |^:)%,\;&ѰHܰ%o_Uvi,`e8X'bAWZ>G5Е-Õ7I* 863q/}ݰ;Y7>T6 :u Iw kLj6 (K,HVXJ\% qbXWuCEhT[Y!VWEQ̽Ι WidW ~Zi8fGu~:BP>HYoo緼9~O@:zUOYxWr˿pvȾL(ʚ}L^غrV;$he955 Y+\8]ǚea-W5_,6)8 ǃi ABpDuu>Ӎ _(f%Z/є7vz4[O|2@u!Ԋ t IxhY+}͊C1c e)JCOaNgi\ DB7Á D3v){0B ygWTr*ѯҰLWM4Gz̕~G8vEثe[/ZUp]Jh]$O /|7(źP|3QT' 8Z? _:IxQp]y%-臨lt+oPir r =EfA!yp f14`*l_w];`8sѣSwV& v/>:VÂV* d!gPy5)2veG2!˷S{]ǏbYGZ(rl.?raщa6S{}GpYN[}Ms 8¤%|L"efl)벋Ӹk@u!cd@$=V4eQ?v"Lb1̧\+UEs0=JCx\YQb|rFœ=TUr:w~R:CqptB3[i|:x[ iTljE#RaƕT*;;}WU:9vV\.w IʝlrJ*[s|U||Z_LmvRоBAKUeD R y,P/u*A`V.فa;:z;(<>2w' s:<˒_G#;g9|nS.?哧Xt\d~H~,!]ߍ'Xoq)Bϻ^7 5oa8L&Ch-_ƔQN\k.sR?;SJN٩!+5A8:,LYPpm,"i+Ug'z=g^!˪}/w'(Uˡ;Y%gdi)ć~ZԵ|L D&MGL_{÷/~k[6xpgx_I/?HkDc?b͡y 5t5 AL0Z2˻]"hE 3,~D:ײޅ,cTth&M`$#}d]IY%iRm3]/h<dY\`WslV~̟9 s`b׷=[΄3l ճ b*? Znl@ctcC 5v̏;){jƩ\տվu|Rn\ ;S~{M\^7'+{D:Rz9Hz Xmc鲛]~R@ j#SF$,C8@)mګ iqSHV~i})Q;OU% z&a74>̍ґ(-_x^g&s;ʋ(Hs<1uqm}NӰa-/d#}]z vR::bww>bҋ+W$wHef]2KQL%o>k-a#$(tvwx+rl0ە(|7.ff@]ɂ&WixA[5yR`Tqaa?6n[v;,kqhK-N7\{"'|?},>㤍%dP~'iJiq8kDEdm=U2[Jly56HxsB3&tH,o"Z;oJzceTdLB<&愬&l$aYX ece9~Aj!ʝE&sF% XSJ] '&AfCJ9b:/'񫗭NhGW /l@,H=r4`-g;~5̏YR/L0qu&0Eޘ4!:nHa7BLu8ra,jVQoz<e\@^v j_%nMl>BCp jэsWX ~FC&v``ԬHH? kpd*z4K B"-w 4$[(3T2GW |T|&H_nVr2Bb Ak8KlR0*U_M+:fj@$8¯9oGbcQ%f3gs΀?F"+jȀ]~%'CZJ& ]L?Wk>C=!5E뤍[Ipk:D]bOx]XY68;уyd_Ǩ$k|ZÌ$P]>vu!iG)TzӪ.MSjT*v``z@|fg<߫0,+?"á =Mp,], Vi,XL۵E#2հl_x h'0vO_xx63OznNd~+KGd :sDB 2Ȣ)- ܊i'qahȆfqU)/g>ڮf j({*B()cy>Lּǭ9j"+>FT;}ƨSCS Ѱ3/XoyWV7:k`lz^U6V[zմ'Bf_O C6:d1hJoGFhAOQ~)3.YsZd=uY5rhaN,RQ?pDxoapz]l }v:Is6^-U|o9Z L)P T*qvѩbLjg&γtfA]0t$ƾr}1RV@&KJllFYn5ưڠkש8y!9j"i|EojwjH(cVjJ9ξטQam974 >$˸Vg/NٮAכޣ0V?7'ƠOlm-HKpaDHrI%f^.Od:}G(։`Ա.^lO5q`P #D .o;P2sJCg|G¤r8$ RC,<+ s~vuxZ&"}ӁS *$#u0-q@҆Z=XЖ-w6RU|WyAUQ`-k"H_yxLx⟰*lYd/O5'vJu G .8ITf y 6οjA@琯~:<!,#SyC^t;sO`ՒHٕBy fDU Pfny[cMQ)BjLӱ#$wb Ay&HPeړiş {,2a)T1({ y<dً#TxO$@ks +J+t7[7y8ʌ& 59͇zVYA?ƉϺ9Mnس.65=IxpqWX~2n¢v,;r6g#4YR,=QFA#]T80] {DyKbef|?WϨØ=m#n+j9I0?^û"o8冹`fv | AKKq \fZ1<1f]nĎ0e&M֘8ِ39h9O3O ?؈xx_ dTRkOpuQut1p !M?"/Pu^IoNuDV c)Ɏa:RAa"ŀFZHA_z̤a%\Oƥ*nz1hBŌ=/5U?FS8H,y*Qel M-@lB{%Jb{^p۝2¨ZѩYJ*hgx;~_N`j !}UVV[sq#VᲔtkči-LocƎa'ZƯz;^ʪ5D6Bڠ+ 4u!Gq}edp_evc+k/hsdyO!xifH:}(^Aʠr;ʷՙ4ظq%wA#DD~l[vPYJeq/|B+(QYyN\gi~҄F^|g?}_ʩFb⠦.O8T]Bg,6&N-\H#gZ#*J£ޓvjg\GZ" 8"Ybz3~)O[I(|G4zQCu,D_vHJ8k ۛ/ϟhѱD3VCmmte91=ԂRt>@$2bHL0yI;bH^\{]ϙ (wHGxH% 4͌*=PܟrPI3_Eʢ}B~TϲL-[7wWRZ|D+uh@=,7Ti$٧3xZm-CrZӭhSą?)rRz%A^Nj#6kzQh9D@˞5eIWy kV [i XY. h~8^ufI#gOH!f#\hX5Ѹ'e+/eS*`?RT?( N>ˤM^Ʊj@u)]zxXS_YBOHX-Y#s.{j ~] C=6?e® (&Nni:XO,5դBQrDV$Ƈ́E^z/APqņ w53ƴUߠU:4p\b .l{kV_Jhl)I^Y4_yU@GDmbb >`7 V0xvhףWh '-r9a\'#/G's* vܫ<66}v`؀fE3[$H:wiI?#!=86颅BmeĮWDlK-<l+c]ӗ_\$J,bR1DdmoaԄȟцo 5JדTucn@\|#?7jH" )Ag"9y\ 5RAC9ԁA%I`G: /(=DWg!۹" ϪN D(T#Ώ`3ԱL@,[90"f93L8 $ e@;9 }r՟+U(fqzݡG7C+M[oTvWG+- A%P'8qY+v62$ϊpxӻ+nqtzo!I_U7_+["/l0|0V8Q-~YˬWIyd+Z(%aU~ܕxܮ8]:}%XM1dw|+*EvMW}wxHQj{REq8~Mѭo?U+G!]ٕ3[kXCjDtk"Bm-౻ ۥ@}t˽THk}IA4 H~ϛ=k7[F"ՒES~s4ImFBۜ煤x "]_((W3mP/* qO'=Q:ܥ _"+/D%a4OZ% b@>]D ;]E]ϳZ,ekŤI!N2EK)_th)ɢMp.Se'm.xϤנbUW vމ/z e`;/jk^n:`}>Kv2ᄽ0Mpwg5t[&0a#{wwax|J&0ȬxFI#A@4u[%z/po r cϜvWBr:G)x^?` rxY X\#0HvLvQ)in;#f,}}8 ܯ=vg_'Iu7*NXj+c0p\Sul܁*RR"Q+K էª9B}?IOufV:5V!heRȡ<OQ0cu.p,8щiHyBa='>#HiogW$w\bzOoKklYa,z:DKBKejZ \B7ON)5L @PsħDKvnG.< b!.EC>j*!rS%I'oh6-zU(HC;X+}Dx98q;۞S]>4%7WǒFौH{e-M:ьI PV+ѓ~~ h3[ݑO~|y%ěB|v7~eI}Fn bYas5_"f㨏8Պ83h(I#hIOgu0J0:#K_NM b$ˣ`=LY kIbiɝJ,2>VqΚnĔn*Z,U5m2r6q㴉\xo캼Mфj%Q y>6#]˾uyl/h,p6,4YKյo'"_J-"0y܀dMF깚QJ>KIcx`!";JVQϷqh7a!kLL'aGrX  &2G`#}$%@0e^uݦFN6ymjҏIC- }hɜV=v?I.9[§;dE Q,n72-yFg- tL ma;`nX+Z *p0]; ${-.Mrbc0,HK܈8>3`-MV[ƶgX>3k 2AGdkl1PRYP$>m4sB4qUl?E$hL'D4:l\ff·pal2oH\a]%UCb^g$ -[C4 61 ) t!9f|[LfHydI2&وbT`àk6S‚dO! e6"%LDtح>^=SJ2;gH1DgN9^Q@fc>!pFONZ8 ½?B69%K{bn9c^yVs͔c@Jj"x l u'rUXLѤQeSFSȜI S%P)yB8R}uha!,0i!+U+s r߁=ن$h> 0?H#ԊH2G\>N`X;!,wGT<(m)^ >S rid[njPMn>xҪB5&nٶ#h㊧1MsC0eN:Iv~͡-`Kڂ^ ٜi +T[P*h"Wް\}Yo:K;g4*LԵK𪌆tvA>K$$b’p8m1?: eo FJ " }e:|kWYy]}e+tHK $ĆUFb}ݳ{@z]v%;. Y_6Ȇ+r[[w{&H̢}|Ɇ8_,4*ƒf~wd3Xk/ׁI ^ZObV=%& &'`{} 7]N<πW1= vJRC3Ɵl.aM[97xE@Rplﭛ)T*cV3r:V=ʈ R??n-֍y +2zZ&I}4+[g;-ft1UIApt.s1؀5X3mrKhES=tY5ऺH\y d9kg%sqI%0V/s,h jv.@0xty-Dufkn̸g>Qs E& <<_u9^/]A MdPK.#j޺,uB_^Id6Ho͔!fcɏ Ů R]w~*9tDV3RFF}nv}||nGQe6'U;*Ee4xlQYhrYϏac˝\rg$4`=_1ðh7( NiGǨL2l d PI_'? T4&ͺ#UCUAb|i)-nZ8@?r: , ;^1S}M导^m:/6g*aPMB+/ O 936 I"GOuqMXRzWhVc\՜~5;w^ 1R4k~)CKrՄspow.3s#)l)nwbQ<],^/[㿑 ~XdxMymUasT(FU_?[rT[\$/BթԋVdE|˃]GxXh䔒T!*T!E 9"u%MZ3 ^pT&&m5GS라xWiz;o4fg,W%Ds]O9;F/S pqx+HXM6یG((]ה3tgPWuEgdmOd;WYMC51C *cu+2*C=k }EGkg/zS tp ).'ZDAiIlϻY-OkohN{s(+'C^唷+%h*]'N6QZʢX C`tx3SyM}kfp&٦H'D)o~R]7*9UR<:Z@KX_1ߍ|~>ǡ5;Vʬ(םc(z{:W v!)VmL &#NE"AoEdO2*d[2(T!0"m: L|(O-,7A4bd4p`bSy`֯joQNY1*ca9!+Ma?n tE@g\FwjcܹS|o:jD!f<.Gkj"hhUĂ=IQl0N ""G3y갻11k@M=лd 6o^~#UnEbfi~> 3W:x邭͉8j@ `,ya~a, ȒA7 q*pmG梛0~k.N`{m[3-5|*cU \?kil/;(6*qBF{(qEӐe D[ S#zq{?m^OXO߂58l#rb&ko̖wG&N퍉)?4Ϳύ6MH6O89dLڈE2]VBL\k®g6S"jy7J |rf~.TFaX?Y# `iQojVyoNa`c6 r%s0Ɓd /zgc+YvEBA-$ySЛ1uTg:bircs][M9[aɄdso9f[z9+WJ d-mGXA ' B5jnٜwgh5Ӷ(2]ګ8i&VeE_Y[d(o$OEI .4A7]B)rjB &qAQO9ZG uWHDwF1D5xt" &kNb6q9j0;]YG¼B8ީWL6E/%Ybi0q,bp$۵ -T-K!U)'Gwۏt‚5px+'h7U K"z:}cAJ0ʐv/NGCi!|m+ǵP ۈBշ}z4/ P^ (\/y"[0<j3CQMQv`/A}~WMkG^B+Z]ʺ-(xZ+C#폐ˈՌ[;N,3\6!͇׊lTMXٸGݷ"zL9΍~e8!ub.pL !(V`ijcY4Hw$ |8q?I%% )O :c 2&]!h)<AoCǚfY$1ё/茉DeAҫ[(\e,2 elЈlA1IZz:HhRcYՙ&hظQLl (rh3fV ǐwH, ODj9@ w7q*d>Ű\ݦ}oWG538C`;2 8r+벀gV\-oCtuR ):;!h$cͳ's5o y9#|u`~`? V܄ڑA=!\&RxD/Pc \~?a6 ՝:P_.3:44UحutKڂjs^2f2.H>@CefTⅈd8Վ cG&XÎW.54mIJ947S\n_'ޥev" L"gO_dx5V ^1(2]1]ý >rJMr94IJ˥ekqT`YK撽>g'8rܚ $c(=4?õ{ V"jm|9{v4_ I7RyQWEiPƠjOq^e8lmԙkWE/\(hNψtxgɟx /lH ]Hoˇvͱr2,J%!z@%nreizxKYǽ;#R:BCT<;ik&(<,N74kM]z& tC_jg5<ݨtpsW)YWONV܋:S Iȓ IJǙGw\ETzv+pkV;"Ùx#3IUW֒)@#H뤢FH oYc*W7/$G5"=֎Fv[wt/]S.آ v1 v V.)<^KbUI_D@k8xs-x~ I'j fE#쇇~:W*F3y'S 8czq:P'l 8& W8 yg%%4;2}O1gMo Ʀ|Nu>n'*`;0?13 8m|ֶg1l(mp[vD*M (# &^ b@s͉\bs@p)TR8z~`9wh ^2-) PmXu: .NŪ<9Z(8[QgLmsy\oݜ0XZ1~Kf3(6ӧ#+(VV;ɱJ3R1WhٚQGAvdy B/L<%Q rۤѳٹ^_;cXY}d k{݅#ÃՂ7Ot4G]HbF'ހzp?/,ȒjayKYwdg卵&%PeHu7>ܬS_HX"mZmI$F;V< "͝,U qn~Ϻ`xV[atʭ8d,0կ !0ܒ-Q :1n8f+WGUҏg1uJ¦ Z̹1xn BѢ}z5\L.dP;d iAay 9^)Z YcCг;HD^-`%CgcCr)Vzz&ӀGs@'x>s!Oj䃏9g}v=(_=E?C>m8K('je7kdHJ @`sgxidc餼qtL7XQo#ۖk?*DÛ*|b7^e.lFͷ'јØELDz=HryAi(/ pEO#vj{h(Kh HFD0`#PlJ NeOZx?;+A;xR:u!9yEP3wxe0 d:R-Zǵi rZݨ7FX%L_"SfҒvԄFE=V~gU)[H1pP(…J lXc͖3v>da1X^MLkDzèi,Y<⒓?k>s=VL^:,W3*[ۍ1@LnRwmQ+8zw] ֍[k'*9U`O \w67sFN! 'KSLfDЦ+g&7ho}TvQؓeaBXtvٮ Zy\sHG8`\HR͜Ȯ[U.KUIcW1F(r75id߭T0Sg.:5rV g~4?+`.gq : 4A{)==2>^FgUg|oӜM|j.9N| RPt͜םqqEP⾉AlQ*bt|AFD4ܵ$\M/v_Xo mYh dwe@y9:~\tqU;G]i4xg63mCGo6.)"bK._r<=U#e܋({gJ8jH5KS}tia[Ӿ:MA57%&x/i:b5%,7쥲\yY0wkB:Α"*Y \rhn,4 Y[Dvl(.=a_RpJ!<4+uJwPr~hȇ3uK7\B,,+0I[ RBbw2L׮MNJo~D&G9rDȵ ޠO*g|<ډ=32+)dQVy,OEG}bT'e!XU,sڱPF5EI৥&{4asձ?$xU}/<$v%iH^9^\]55cz_U9~bIhLW#4~ ݨ‡^ܯ L10-!xl!Pa=mk;xNٳ'M)xjPt6D~ QOͶ;@Rqb*r~nb¼Xƾ&+ C6in*xBB}JJ3KC\!@]dfV6Ao Wad`4ݟ \nfA HAoBƦmR00,k$t WBH#=7e>qW(?UV}oXj{,QҵVL;eqi2cb) PWrb.66RW6>-cR(ֿJe`JB]5#?0$MwOmI ܀%|*^Q/g1}a=r3lG(B㤮*{/8-]ꖬBfeDo6ar(JQ̥(:;G]kPG scW,5dC"/{]M/БGT]o;GT樀=E2_%Ǯag^oc ͷ#xXW]3_c fAGFžqR'qy"aHJ<Ɨ@muCYkẺ7%Fv4̃yʿokٟ=.XTd!;tr>Cp) H%MaߥC$HX> ]^+{փ|Ⱦe_!>;!>8fK3JR Faf.MZ6| |*139rmgmC$PGzzu.@^RL^ɴڼ*ZTWl4"sϖDe!okudQ7W`1U(XJ*qZ"G^,X:B.n9HdhHWl&48U5:/f5%o;$,OgoU o!'eI BZfkaD}@9- yqG"te&ϲR hB^<X*?hKca7zuQ6E[2LZ~1]{e{e*)7KjxZ@w3(]dJ,M<8O9]Wp+v nTHOԬ$LmR8FD};EƟԔ~Y0̻B>{Au@!fy ac25kŸuŤ,C~Ya>;EJ/FCCߪ:[SGAQĖkHϚǘ \EC /qZ^QLiw CUΓ|SP%/Iᕁ FKf(&k 3{[(r )K VX;,hO6k@WNw@LehI=fK6{sy _ JP[䁓{".k -:D=6҂ܝV=6 Gu(ZV0i荨ϷHh6;lS*|p: PE6bHW( OXpKf[0|4֘㊧`SNGŘN_pb yYsڼHZKL=zkJg`I*N,ݨ73Q52V^A lf]+; hhFCޚ<6D&IaO U1ID`o4JmEgǻ&y(E9m+ Y&ZX, y GYN8w?"D#l6ڨ>T&t(HכDH [!kk<ĪYvTPCT괬i;$v?;߻HiO{n\SW}䋒seD Y9{x.Gq~ʯK.S,x7bpa>,Πd%2,l|<ئs{ W.m-jcLJQqZSeo,e/)h %'/j4?jWp6>:g$^:Bfk&V!< $܇oOgD>SYѨ6p*n^a B+|D]+R*(ļÃ)MBr5V=3:LY*^6R9]аM…9 v?Am~AfQ5G1F\>J!<1r6wL mzV Q+ ,rKag襪|!n+Ntx̷ey&⼲AVbMҀv'YvH2Fs~?^=IMC?ӈN_{ p/P}f)zhuZ{hY&S 0$;顜}7K%&,W_8f6 Amső׃MVlW ) ψk"^ߏuˡqW嘚_rxwdۺe{<ئ(`!O`%jA}dQSɤ<:j!ex-H&\aCEHDQk/\qdS>gR%d{fhkteEvH~LkvI!.JƗ-%KK8kw{("q BM2 rC*⃡@ւ߯d0+Lٮܴ@jNvk=;8x?0Vؗ`6Ž#VԬmu8a.XIW3[raCh_d*mu^N{z:'U`1==YRG䍹3LޚӁTd*4Jo^=HW)NLQyy$+9y HTzׇ9)qiY;*)\hgU}Il3ύXA"FIsR?CJ7ja =#$~YT | z voE3Pk\M-}nw_Ѿb{T"qBfa\']qRɐ @b(Q^;d|*ZfJeJˈkY< (.arTt+scA+j2'3W-hnzSb#<ڼU^8~_Z w0/d\A,4*,Q/2o"?\O"W\4W[YOlQ"7:P$d/6,C * qHC-deBA:^ajGs@h$? ߕsbLWibOyqu~,˝n_HLԯSv1eG9!d~.}d kY~83FskOoP"΂%'`?fmw/%wL}h 噾Xbad#ڛ9{HƹeSPhRtʣE61%<K2m4^peRƁxGZf\ Ӳ59.3< ##7zPASUAb*chX eEKn1|"H[;>J@VF>S1 hPa_;]c 3iַKS,O &fVUu?]} 31G{v5Ӕ1΀gd&!5/XgHeSktD{֝X>xƧiԩ؃dQGĘ^i( E5nB)"#\$qH=M\_ '> s@gtJ*9)B X]s>s.]7I x\S*"b1Z=I [*6Ӛ-~"Xv!tͫQxCZL N1sp^Ӂi5YGV~dW,9hzqUP<B1fÖ|hLn|W+ 1 nlG@?]t"0Fפy|6p%_Wk ɣmĦLkP>%hh+Ҧli"VP$|"y8.\}IYq\Uc`A{oɊ/mxݺ+핃Z 溤S+5dԑ<䵆<[֮i.f@ 8l3j ZU2o>ƬO#dhgj7Rj!C :SbB=dYn& Q+]4(wLPE>#cWn-xHed45mh509Sj|Iǝ^:dH?VASb$+h19,W=m 桾C.L߽M|_Ym8 x s9$] X2 Pڍ@D>):b,4& lX3M5Mbh@! ;0%I>\OȆ1CPI`ނC J|+'19ξb#VG\V{Ye.mK`-*Ķш;1rYx}\WR4nHxl'8Ϣ@Vŝ[e+y=!x3^辍ぬ0wIӄ*f[ߛAjm*ӿh Q: ߹. Rf Ir{x֓?lw{Q8@jF9z&Eze**"9a4<ԛ >@x筦"K7C># ];+@F n =s:F|;;-/4 FnZoL yRG1xJnm Iث\80#ouٵ|#;)T19$ZKH76(5 qTE$[\DRsXżm\N}լEEJ3@nSQ\&uP4(6खf^#e9ՈC ,!zdK͂u9WެyD6׌?~q?gbo~\08þr@c< tDmodq8MJJ/`XZA;AZㅑd=:WM?orIؔC)&_|:by8t۫b͉PDe|4=cB@GV8R_N =-`,[8Xo-@V8g>EY5qU"^~Ҙ@F5UŦ NMMz&n{6vK 1+Q/ FE!e'IJ,B'OeD7+/VpOJS30Гۍش 4O~%Ҋ Z?\=հBvC \-X4Ue X<4(-*+Vfy](0d,&K޹ːx7ri5YXmY\hodjL&8s楷9Ȕ?ä4C D۪2rW=4);Dy. 'Dsi*I !Z6I%2o`n>v  ,< 2!Ǵ׆oJpUwPQbzK9.{e"*jS)vy/:`Aסz*w)d"\8wȕ; XJâcFBG'uy*:~X6D!ұ"ۢ_l8>"φC-fʤu$TQԩXm~KZ}T񡛖ֆ%ۡYl njԮ_u|͓C02nHz]{OqOW@(+IюkyWw-^oVbkZJ(WaGy5`ҭ;φ &Di2kYm٦)Ǵ BҬ"hL1P-WTHRdYt'^pdr^D:n-lg㝹uC.)q(l8 "ڼd >׿v4>rƠȡ|)|GrUNi wƢf$;yL(w26`{,k BffwP[iHRt -l͙FjDk1e*86/S.n07 3fpn&٤/2y!#;1/(]dڰLȮ Vɤ.Ef4.ʅj0l^WFoqH8ԠGK#Sý5z˜}8!0E$}m&f^M%pmc_[#Υi 1ꡣlqp&нEvQ=a/J;Glb ( &F^\m匋iΏh1\lIy1#\ُ  K4)4@s@GPIfߏ]ixr RПp8)n:QgưM @ Ə) lxZ;]R@G &mo' B \k:aC+:eG !NG =6h BWG}䒯e-O"A̺}}*J:L#ʫ򕇞%cO jY-|#qbtS&B h@pFI3,SYj!,B5&ƞiΜY6[YbGp-,˪o 0>|RCe]rQ#8ImQXeI4%4M:@̀Ud8tǹT;k3ުh J,6L>}wiJ"Rhei%r+spW Y[hQÅ>."Kb;9'Dk@']dz^ .D- ׍Y̼uSmĵ&+GxG<.֗N&E Zy <T v }"Jf0P͏ kd^SX *!@ sqz:jַYuP](dJ7!__J63D~#@ŀ˴2@'g.bɣS?^Sw8^$=1,zdGIX\\ߧ]%U_i!!Vj6qna(M QėXk1E]}Ӗ Ldn| W].ʋ,/T7,3s׎]'=,XbϥZ; eʎnы%J4 pr.첚h6n&ph߸I &/@6aqy91J_ i5mz;bhL!F&lVyndb8KQ C8^ME%d(8iPj0UrObipWԇc 5uLףvfʦM E#W!|Y&D'#FFpFOq$#!u"]:JAIW48ZPi3רK,Izi4?9³xx^OWtxh` 0z8AX-,*:7^#Nba- R!!=?h7ȴVARi#iOuV1%1qDec#UIE,B#tӲv m|/xp , mr\a\K(f^m[O%z1@ .Q`=Z]=|Nc`Mif,S5]jpW7r+x=7H0ei6]%\ѹ,%mX1%tbm hTLZcxW+ӫڋ"DB(ߔQ)ou:=$MU`8p)dӗ08Vb {O+=mY @hlE@"ZLV \)hKn.<b *&eP9[ݞSz@.Begz {Pة@J@5d ] LjeW欋PVtk;)ΪNZV> )2A%TmPLJ]Ƕ흼+c@8ɥX/I/PNpPF:ڑ+fr!6&: %S,%>ǣ2Q)fyT7kP}ܐΊ]hi|' o8iʷ4xv˞Nsc>S$-%F1峄}"+ϔִ)q  a" FGޢ"V2]Z& '?]֝zxT;$C;rMy@A^FxN$bء&&ed@b3xO|O-oKdn%Vi۲?U\X02Z.Lqd}lR\Q$$ie2Kkmup:9v {xΝt[k8 zRmy$Yb:u $U((GFw˗ebmig I jzHw[, VI(^ b҂QaØz"RET:_}:&Ừ3_fySNM72O,®%O̮_w$C,΍n,(j\oVjZǪ׷(fF2FKGbIn;mmQm__9t ZaEKj恶gx[)tL"nvIc ,vln[PtxOܶ򏓪@q+/!γed__ЮťOEUM^m5l!U*KupF+.門 &} C浄~I대wuh㡺 P+[a N Yp*A߹(Dʴ?t!l SuTmUbIJC;|{<ߝj 2 ǐBHN&elz|AZH_29Tgō2Xf^%SG)6=Fȸ]S0Blvn ,jH{cvmUn6@y0|+f~a,:A1!6.vbmi@R=Ho8EX߷ jd7\bd=tUV'"R!*[!sx/ 0Ѳ{x+l&5;tϮQ< )9@HKU q5<P*A4gu[< XF""2 ,y "ᶍ51AVM؁xy p7oT| JQa܀k[DU'$g{ YyV)Tߍ%n%e m9N?h}&kWPmYdq771T׌)XUoYOOp $ vص+aoRÉ21GVs7;V睙G=q;N5WYVt~ @oKMs4Lj8 "uhلz§~RnI%#=BKO!̐p1-KCZiG Ti>F\>PZ澥s4nI}JC/ k\4Gp5sF2dټ[ڗPV$.pщ¾: MrL%~%wDg7 ¤jn6T3 >S6 <^sdR{8U75n 51C.cUYLE5& dN{ǀG^rFv n#f /jgot;>d]ŝe]CJߘz&^DO )ћ>}h {?/\"xkf|@<*mBG6d!8pY55 Os^L/@n;1tCGN-+ vU{T|SM"z3gzdb6BRIwS7Sm<Ҕ} qw\vwpYJbq#B]KRPG5J.񮢍9!b o$Q@(yD>*pA/]ƥ(+_ Cld8*Y+-LT&_BbrwS*z!Nw[WJcժF蟘XzE{n6eH2Ei1j0N}Ȁ5W@rE͝a2Wn)d{&A|aV |.G]B[' ^uxfW԰W6xH۾Xy c/QF>r4{ТTip>>'tΝDaݵS$u۲I`lĒX\,z^a ޯrYN 9տDWn諷˧ȤWPHj;bCdJ$«R^GlS#bUc*SZ飳"Mk4,Oݲyy^pO*E8#{~裡qhe+u=9=-.>K5sNb0U6E)آ5]1h9컀"̢RPΓTlǰ G5HTPt|m&PS^q%+?oWcMi b$yA.w*f=Pu헡E֗_נCPZ>lIR@7}LwS?YӇ'w"'~UU`M qEZA\g* hR~{_E5Ǒ"D8 ?$ &86rٮI!/ y:i~^}6I~2XhWU%-zk*3ՖitLx1nhsx|y!,&Mhjߥ OeZ.)ҲNenT3vH #ث}4 &أ'NUCvmR:T9]!| w傜Xpjį|vJHos$ߚSYEt ߾2eN@iFvƠI.81KzfC4(-uXy\F蔄X{sPcLcϦꄳBA$kG4xucmmr7pehߙ@J=^ HIX{iG8itV F͐o1"؀}9_/'{߂9$q-ĆVshb.g%NNRa¬Xe&X@AP0-$#ѥcO=ꄝ'pªJp ܄=NHIQGݘc / ;{8V1=ӅAq%CZ5|TC#&(Ssnd2Ǥ죮']=,rnl2JUg,~jU101'eVb2d/YKA4;4ҧyoB[: ǖC˥S}y۩Μ"!U_&o+lήdLYM˖os l'_{b}&ԐEM2H'P [YCҗ7$~`G"ҘA p̛.3~U tW*:%xx+ɶ=%Z>TP>QN2cXg˰l PO!Pf ʪCIM]k 8`:xV|po;"hh~̫)aj}c5aB穙"z{@_Ta").k/q#;:^7T؛VޒP=#eɚS[*좵[Z?  8qzxZ'UR"oU9*8:h4W37a߂x1tb:$wfb`:yJ_h RbLٰL0WG{=@x̾=87 ZP ?\QDA?^F/LFy帳5; H#7[qdh~ -?&]= WULq' m ?BT|21"k 9+ό_<ȟ)`ES^?2z 9S[k"׫`^S$8=6XH|ui)2v4 A#yw5gʿДL9nF+ʘJ0T dN|2&;X^Ru lPIhS\e 4qn\5a,T>b 䐦mYQ~OG\7Zr B-ݥd18>R-/.oTY,? ljq ŽUGl86<`'5HSMM#T{WR1>M:x9u%g E0H¦$Cy*U}SF@u/O lHOd--Rz"g|[x2ܤ`\BCz̳ڒ4R}hrd{ 0)!]>. 3-főJaҐR%5u_JyaɉX R*fAw P*_s,(ѱ#DxaJ.olP]}82 ؎t;0YZsymfc-1.6.0/tests/full_fc_SnO2_223.xz000066400000000000000000000365041511276756400172530ustar00rootroot000000000000007zXZִF!t/=]}\7~$ø@!ܼh]M">Iq CNM BAނbl68*AcЕJYar`eGspSTVaGEGL}n'&)0b|gc}튃(SA%!CfUrjuTngOvvb9.ѓ ꗱ*9JU):=xY\p#DH%na_tFw Zif}SPGJF5yv+a*ASM-v?jnW4]:h_Cc  $]3WUL{>er@tcT lĆl3ze'K+bveY^1zP&9Mej"rLazW WMYs{p4LXҽ%Ze*r6X*]v #w|%xgB2̕'ϟG~}"z標9VM+|*޳7}1[#9 iX*;^_Ӆi8 mELjZk"2pqSI[i;!Vpf]uDipNfQ\kC";RGNXK 9oy@ {Hh)(݅>S鐩HZ^ 6~h\?'a!,u0-]ҚgZ]O{MXdnбEIbt83WBb6,+yI2VݽS4=#7-& smb;' B =`@!jZn9>Ygy؏S^ggN<;C(,5]O - \NRbzT>~e_cmcӶv@wqV rVROs|]6*5s*̟dZK$/, Z)/2תUp'JTZ<oc8"aXI=PPWg+[FiD=aWH'}t8sP.7ytw3,8lcIC /'k6 ܕVH:Ts ]HHՈ'9$YWyPOzkV$gNO2g39X+o>]X<@B¾;I]%R<-.f~^=J?a5XI.3XCʴ"@Vcr7v8.à= \ [ϚSނeJ;wmps:7A%aJ`«;${ѡTY~6ԭmlCsg-w< 'iSж|Ppa]^JWzQNBFnUـRu~D{'_/IQChO ʻ#Kz=zt|vXUOj!9/`<KUυQWưж:J{1cP ^o?*\E;/2c.mՙ6GvԜlo|*v0 ?|e2sQ,K:QVa 9c=2-be.|nu.\;E~gۢ꛳'ԺaS<.·~:c-c6EL hZH=v.v!Oǭ}Tk5ם̔Դvft|\EOP@4Y"w+U[TUG 1Mo9AKg})\ϡׇ40#HjaETSz(1_̲׌Ks5@&Z YT,Z)]kՔ&u ZC=-#x| l0b4%lN;*s寣}, ?,BiKs^].mz_aHW9%Fy+5*u=6 =8,STkmǶ/õӵ!}D0n~3#`XK咠^>2] u9;Jo|/~2x8S vI0+dV[> / ZӞ MY@~oFBoǝ8 9u(Op >}s(?Ȯg: Ń1i G:):I+ ֹ؛pɖP^U+vwKzg'^VqGԵ*S!U*7D )ݨk%H#!Ys(,.HѶ]T:<‘ռ!jr+nk;epKi9><{~[ CK֪a'~%%N<+phìgJ9?>E1Ӌ?b{59٬]ѱxlQ`m1`p@0(E͊-@5QP "Џ ֖R֛<%R#; `K^IғF7OOLG.GI:NnP,~dqͬ5ucE:%Z :x/-oEFwa9! fuowʅH2b@ԦԐT7z`8e3]ށ}hdKv Rly7D>1P^:ukCQj)hx۾qlLǀ򡟲PlpϚS's wRgNQyH?HZ_Mկv񁉪dU_Wi͸{X$u~In߬[%`!ސ vQNIx_,9~WU g@ݿ=!;$GF*ֵ¤}6y`F,5B1p,Chaa(RiB4$6:(okpzհ#{jc+5@ tǓrǝENG`b]7BZ29ԫ%-28`a%ed> 3Y 0ZZC4 T;A: zfpݿrx_!%-, %IJJYLvK2#fbs9KncE5m5dhz7IV<)XV:oqB^wQ5?0C9t_Hdg)Gp$۠ݲWG*6lNҳdK4[4n}ǩiT5YWH5eCT,fܬNLbNT~q n2W`#`|%,PW' {"8(% GŁqrC֙Za*!Y9ȹ}=kQZڿFkTj;O!jb7 fR9e0{^toEaנD2Wj'pprr2ę&ЗNM^<(tQ0`hѴHƻ#),1}+ݯT밫l!A`$2iH$ZxMt!,Qђ<%xurUul,9"  m,K׸T|1+Qq2Ж}扐;-7n³S骛zaGr6$G'M{(:MoH4Y$|#`ekl@xX<ԧܸ}熤0Վ Ќ}ޚ=b!Iw vCr泶֖JB3aTCm $<u]l.tAGYH{~m)C=}Ѯ|z FΠŝ\>HM1 puüŪI۟G>+jWc6j)==\F#.O\$jKBN nh%?Cs86uũZ@`,c*T:K%?2lᔊկ3_ WӷniTsK.eKUXo I$I3|X?2T°sl}Y\K}lPK4Ӽ%Gw!w_:,7jh>vZNWaj5^@{կ-%J {8 _<[>١7ٓ)ysL@$XXRy^pLѫ^䮊DW/ʩ覛&tɖ z}/t ~>ܰtH]aL;5b!F:Ae#W6zSS7yTBE˖tL-.3 q0-#DM*$[F )/\#\-]Ne_t]SC> iG-t7f-hAmE, <]`˔Dӝv\i4k]쁳&GKlw&rQ+%u? SpKD}$}ZOW_G p,fR[o,|tQ5L$+ ? ޟe{,9$D5s 32\mWS`L/@\ V2O!%3O*_nl0}ϻ @wH6w V41yw7nZɴ^U7$ETꕈ`xn\ֵ yPB"JA\ Z j8 MKz^^Yk>phPi3SۼRRdel-<(1ص;R8Aρ1- 24%#=댍=)=֑=S¹K=`{NG[I5i| 8?gম>/@ʱӳ}Q\wUvE Xb7'FV0\0%o,}xC 9TgCO_yAT1rBXk\hd]56\ $ `&46W@@NDz-I"ͧuU9]@4SNHVӆ*v~!Ta!~Pc{ j)Ӎ45dޘe'f@C}3qB#Qݺ z+'WG#UЈj&g<[*0ՖȀY7gɱ<07aaUJ+u%PmM9ׅF[guM!,DXu@x_vIzloѰe P3Ģ*2u&9rzռA@ہ ZLph~y$[E23YwK弍/V'fTHyS:P'0$Mm,YՅ1$O]^ߵekkIΥ1;K&aIWx"YػB +<5 eմieTtvENGF2ὐՀc%Y*cn2 <&'>yѢȃ5Jwz"k*t }(#H3cBfG?N?=IDiXz2,?[{OSG*H<+:M012kV? mf9CF`"rw>edRIL5rrHt4,Kzc(]ie0k458vC+WHl6 O>5_.B$0R1}:2\ f)]Њ3eG5T9. )목M/ ]2;rmTOz 7MbI06+9+jA! D[X*q;  cU\ƶ$ʹ7=ϵ@}+jR>k&ܼ{4f!3~`O3f =XMoy;lxHGC#!ޠߋ~d,QTv-WIґݔ]5ooMoʊfKRbrXؼ-o!sYY~W|7whyp7mC5HO zr(ށh&)iM/XFKV5B Lmq ,;2Uglhci/G+珛]O3 a.YA`_Y~B{7Jm/>NY[ d<9nUSIpsq0w۪Sl3PU \\?) %.?|pPeP] !pvUn#mZ2l`:]/mJo(fKy0.T(30 #P2|)qa$@Gʳk.ܧ4Eǀz~ &O/\I"sQswXinyMĂ+kH{ĭb5 t_&A"gٓbdO.?]CN=[xA%G^rTpbqrNv( Vy2r_A/) eƗaG/B\ TӚ'vjYNJ̀^NOȍTO 4>WAH=XXt GʃDśMu/T}@)ԃ'K(oQv8Vˏt:I쳿!%Ր\ MLyEꤧ+L5AWF 䙄 $%vv_ݑ ecAc}Mr6!Hm8P#Iwjkȋ!W$.=:,*ke8=;&gjĹ$vNYXϔ[TѾ\UIϊ~4L9)_ w&`UuI(iD/s;?oy9~]hZЇq‹/]|#h ?.:Q/FCJ"9D(D, hh8zN v6Oɏ改?S fޥo.DfF/hM#+ ju@;8qj8 >錇Tt:dQ/`k$@g gk:}^RKraM`/YS .YнyN!C?!w 4%\.UYm)ڍX(\]da<>;RC[L A:ұ+ݎƋiWP/MG|ۼ+GqYb65 B jWKZ'#:VZx]M(P+]NF k{;-\Lo|0ް~!(;P F`~Dx$*<|k1u*F%"S"gP InPLfmѴ@Uh}0)L`KpˍzxipT!wFș#R G2ʯHt?3)(qox&BNtJ,㋇装^G}%NNPOcaBXF" V.6KS*ϑN^fL88&8 *aviCo!`1>.t\)Qɷ1t9PQ/:;#ڥ1F'ݳ~B]]^Fir̲BmCRpNfϗUOl:$}A鵌n$d&TϠxX|2f(JAZ'{t ŁL(JZ{UnLo/1$?ədf8x~>PPifRHMQHU[$q{fOq@HvEZ}*ݰ"zV9;&xA]0.޵K'2AsHG W񫪯Jr_Ў]Vȟrƍ|oD(F='b>142 p=M[ ȹUWF<їJy 3/Xp I K!F3w:.쵐O]s_u*!K2jgI,B-p lErqNu߾|"l E0w,>E#Y?m|B{dH%XgzF)8{ALlj8YUڢCԑ0|*}p4_^|@^Nȴ ]џ(&D}]ݰ:e,P O/%c~.@d3/=zS#qiMXMCS_}-R75xS"ˁZf x&23DBm0_buut#:yiBGQQMgJMێsHPY9 utd};/̗my%Cep%YuRH9p#8ʜ߫KRKDkXlnCiam#v :eG9 |"+P\AQSc'Q'?~ ]DkTqu;ݎ*oUI?v 4* n w' ["otX&kP/L10ծOՎn+ }u:2OP K:cpŸP-y`RpJ[ya"T(nQp,'*8ҞکZ[ xCl:+o.c 4F0$6j&Aəm .٭.8  *b 9E.Ac4m;eILif#:J*bE{SJ0MVxҵ՗\BgYײ\oA(yri??;DkeS+j?yɊ,$48zU%m&)\*؃bW@j^ҙfW?m4^Yp$ˁB|0`㟨t 1e-12)[0] assert np.all(np.isclose(eigvecs[:, i][nonzero], eigvecs[nonzero[0], i])) def test_eigsh_and_eigh(): """Test eigsh.""" # Atomic permutations of four atoms row = np.repeat(np.arange(12), 4) col = np.array([i + j for j in range(3) for i in range(0, 12, 3)]) col = np.tile(col, 4) data = np.full(len(col), 0.25) proj = csr_array((data, (row, col)), shape=(12, 12), dtype=float) eigvecs = eigsh(proj, log_level=0) eigvecs = eigvecs.toarray() _assert_four_atoms_eigvecs(eigvecs) eigvecs = eigsh(proj, is_large_block=True, log_level=0) eigvecs = eigvecs.recover() _assert_four_atoms_eigvecs(eigvecs) eigvecs = eigh(proj.toarray(), log_level=0) _assert_four_atoms_eigvecs(eigvecs) symfc-1.6.0/tests/test_sparse_solver.py000066400000000000000000000026621511276756400203250ustar00rootroot00000000000000"""Tests of Symfc.""" from __future__ import annotations from pathlib import Path import numpy as np from scipy.sparse import csr_array from symfc import Symfc from symfc.solvers.solver_O2 import run_solver_O2 from symfc.utils.utils import SymfcAtoms cwd = Path(__file__).parent def test_api_NaCl_222_with_dataset_fd( ph_nacl_222: tuple[SymfcAtoms, np.ndarray, np.ndarray], ): """Test solver with sparse displacements and forces as input.""" supercell, displacements, forces = ph_nacl_222 symfc = Symfc( supercell, ) symfc.displacements = displacements symfc.forces = forces symfc.compute_basis_set(orders=(2,)) n_data, n_atom, _ = forces.shape f = forces.reshape(n_data, -1) d = csr_array(displacements.reshape(n_data, -1)) fc2_basis = symfc.basis_set[2] compress_mat_fc2 = fc2_basis.compact_compression_matrix basis_set_fc2 = fc2_basis.blocked_basis_set atomic_decompr_idx_fc2 = fc2_basis.atomic_decompr_idx coefs = run_solver_O2( d, f, compress_mat_fc2, basis_set_fc2, atomic_decompr_idx_fc2, use_sparse_disps=True, use_mkl=False, ) fc = fc2_basis.blocked_basis_set.dot(coefs) fc = np.array( (compress_mat_fc2 @ fc).reshape((-1, n_atom, 3, 3)), dtype="double", order="C" ) fc_ref = np.loadtxt(cwd / "compact_fc_NaCl_222.xz").reshape(fc.shape) np.testing.assert_allclose(fc, fc_ref) symfc-1.6.0/tests/utils/000077500000000000000000000000001511276756400151575ustar00rootroot00000000000000symfc-1.6.0/tests/utils/test_block_tree.py000066400000000000000000000114211511276756400207000ustar00rootroot00000000000000"""Tests of block matrix functions.""" import numpy as np from symfc.utils.matrix import BlockMatrixNode def test_block_matrix(): """Test BlockMatrix.""" mat = np.array( [ [5, 4, 3, 2, 7, 4, 0, 0], [4, 3, 2, 1, 8, 3, 0, 0], [0, 0, 6, 2, 8, 7, 7, 2], [0, 0, 1, 4, 3, 3, 1, 8], [0, 0, 0, 0, 5, 3, 9, 1], [0, 0, 0, 0, 3, 4, 1, 2], [0, 0, 0, 0, 7, 8, 0, 0], [0, 0, 0, 0, 7, 2, 0, 0], ] ) block1_1 = BlockMatrixNode( rows=[0, 1], col_begin=0, col_end=2, data=mat[0:2, 0:2], index=0, ) block1_2 = BlockMatrixNode( rows=[0, 1], col_begin=2, col_end=4, data=mat[0:2, 2:4], next_sibling=block1_1, index=1, ) block1_3 = BlockMatrixNode( rows=[2, 3], col_begin=2, col_end=4, data=mat[2:4, 2:4], next_sibling=block1_2, index=2, ) block1 = BlockMatrixNode( rows=[0, 1, 2, 3], col_begin=0, col_end=4, first_child=block1_3, index=3, ) block2_1 = BlockMatrixNode( rows=[0, 1], col_begin=0, col_end=2, data=mat[0:2, 4:6], index=4, ) block2_2 = BlockMatrixNode( rows=[2, 3], col_begin=0, col_end=2, data=mat[2:4, 4:6], next_sibling=block2_1, index=5, ) block2_3 = BlockMatrixNode( rows=[2, 3], col_begin=2, col_end=4, data=mat[2:4, 6:8], next_sibling=block2_2, index=6, ) block2 = BlockMatrixNode( rows=[0, 1, 2, 3], col_begin=4, col_end=8, first_child=block2_3, next_sibling=block1, index=7, ) block3_1 = BlockMatrixNode( rows=[0, 1], col_begin=0, col_end=2, data=mat[4:6, 4:6], index=8, ) block3_2 = BlockMatrixNode( rows=[0, 1], col_begin=2, col_end=4, data=mat[4:6, 6:8], next_sibling=block3_1, index=9, ) block3_3 = BlockMatrixNode( rows=[2, 3], col_begin=0, col_end=2, data=mat[6:8, 4:6], next_sibling=block3_2, index=10, ) block3 = BlockMatrixNode( rows=[4, 5, 6, 7], col_begin=4, col_end=8, first_child=block3_3, next_sibling=block2, index=11, ) cblock1 = BlockMatrixNode( rows=[0, 1], col_begin=0, col_end=1, data=np.array([[4], [5]]), index=101, ) cblock2 = BlockMatrixNode( rows=[2, 3], col_begin=0, col_end=1, data=np.array([[1], [3]]), next_sibling=cblock1, index=102, ) cblock3 = BlockMatrixNode( rows=[2, 3], col_begin=1, col_end=2, data=np.array([[2], [4]]), next_sibling=cblock2, index=103, ) cmplt = BlockMatrixNode( rows=[0, 1, 2, 3], col_begin=0, col_end=2, first_child=cblock3, root=True, index=104, ) eigvecs = np.array([[1, 0], [2, 1]]) mat[4:8, 0:2] = cmplt.dot(eigvecs) block4 = BlockMatrixNode( rows=[4, 5, 6, 7], col_begin=0, col_end=2, data=eigvecs, next_sibling=block3, compress=cmplt, index=12, ) bm = BlockMatrixNode( rows=[0, 1, 2, 3, 4, 5, 6, 7], col_begin=0, col_end=8, first_child=block4, root=True, index=13, ) mat2 = np.array([[3, 1], [5, 7], [2, 8], [5, 9], [3, 2], [4, 5], [7, 2], [2, 1]]) vec2 = np.array([3, 1, 5, 7, 2, 3, 3, 1]) np.testing.assert_array_equal(bm.recover(), mat) np.testing.assert_array_equal(bm.dot(mat2), mat @ mat2) np.testing.assert_array_equal(bm.dot(vec2), mat @ vec2) np.testing.assert_array_equal(bm.transpose_dot(mat2), mat.T @ mat2) np.testing.assert_array_equal(bm.transpose_dot(vec2), mat.T @ vec2) # mat3 = np.array( # [ # [3, 1, 4, 3, 0, 0, 2, 0], # [5, 7, 3, 1, 2, 1, 0, 6], # [2, 8, 7, 4, 3, 9, 9, 0], # [5, 9, 0, 2, 0, 0, 0, 2], # [2, 3, 4, 9, 1, 0, 1, 2], # [0, 2, 0, 2, 0, 8, 0, 3], # [5, 9, 0, 2, 0, 0, 2, 2], # [0, 0, 0, 2, 2, 0, 9, 1], # ] # ) # np.testing.assert_array_equal( # bm.compress_matrix(mat3), # mat.T @ mat3 @ mat, # ) # np.testing.assert_array_equal( # bm.compress_matrix(csr_array(mat3)), # mat.T @ mat3 @ mat, # ) perm = np.array([2, 1, 0, 4, 3, 6, 5, 7]) bm.rows = perm bm.set_root_indices() mat = mat[perm] np.testing.assert_array_equal(bm.dot(mat2), mat @ mat2) symfc-1.6.0/tests/utils/test_cutoff_tools.py000066400000000000000000000465751511276756400213170ustar00rootroot00000000000000"""Tests of FC cutoff tools.""" from __future__ import annotations from collections.abc import Sequence from pathlib import Path import numpy as np from numpy.typing import NDArray from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.utils import SymfcAtoms cwd = Path(__file__).parent def test_FCCutoff(): """Test FCCutoff using NaCl conventional unit cell 2x2x2.""" lattice = [ [11.281120000000000, 0.000000000000000, 0.000000000000000], [0.000000000000000, 11.281120000000000, 0.000000000000000], [0.000000000000000, 0.000000000000000, 11.281120000000000], ] points = [ [0.000000000000000, 0.000000000000000, 0.000000000000000], [0.500000000000000, 0.000000000000000, 0.000000000000000], [0.000000000000000, 0.500000000000000, 0.000000000000000], [0.500000000000000, 0.500000000000000, 0.000000000000000], [0.000000000000000, 0.000000000000000, 0.500000000000000], [0.500000000000000, 0.000000000000000, 0.500000000000000], [0.000000000000000, 0.500000000000000, 0.500000000000000], [0.500000000000000, 0.500000000000000, 0.500000000000000], [0.000000000000000, 0.250000000000000, 0.250000000000000], [0.500000000000000, 0.250000000000000, 0.250000000000000], [0.000000000000000, 0.750000000000000, 0.250000000000000], [0.500000000000000, 0.750000000000000, 0.250000000000000], [0.000000000000000, 0.250000000000000, 0.750000000000000], [0.500000000000000, 0.250000000000000, 0.750000000000000], [0.000000000000000, 0.750000000000000, 0.750000000000000], [0.500000000000000, 0.750000000000000, 0.750000000000000], [0.250000000000000, 0.000000000000000, 0.250000000000000], [0.750000000000000, 0.000000000000000, 0.250000000000000], [0.250000000000000, 0.500000000000000, 0.250000000000000], [0.750000000000000, 0.500000000000000, 0.250000000000000], [0.250000000000000, 0.000000000000000, 0.750000000000000], [0.750000000000000, 0.000000000000000, 0.750000000000000], [0.250000000000000, 0.500000000000000, 0.750000000000000], [0.750000000000000, 0.500000000000000, 0.750000000000000], [0.250000000000000, 0.250000000000000, 0.000000000000000], [0.750000000000000, 0.250000000000000, 0.000000000000000], [0.250000000000000, 0.750000000000000, 0.000000000000000], [0.750000000000000, 0.750000000000000, 0.000000000000000], [0.250000000000000, 0.250000000000000, 0.500000000000000], [0.750000000000000, 0.250000000000000, 0.500000000000000], [0.250000000000000, 0.750000000000000, 0.500000000000000], [0.750000000000000, 0.750000000000000, 0.500000000000000], [0.250000000000000, 0.250000000000000, 0.250000000000000], [0.750000000000000, 0.250000000000000, 0.250000000000000], [0.250000000000000, 0.750000000000000, 0.250000000000000], [0.750000000000000, 0.750000000000000, 0.250000000000000], [0.250000000000000, 0.250000000000000, 0.750000000000000], [0.750000000000000, 0.250000000000000, 0.750000000000000], [0.250000000000000, 0.750000000000000, 0.750000000000000], [0.750000000000000, 0.750000000000000, 0.750000000000000], [0.250000000000000, 0.000000000000000, 0.000000000000000], [0.750000000000000, 0.000000000000000, 0.000000000000000], [0.250000000000000, 0.500000000000000, 0.000000000000000], [0.750000000000000, 0.500000000000000, 0.000000000000000], [0.250000000000000, 0.000000000000000, 0.500000000000000], [0.750000000000000, 0.000000000000000, 0.500000000000000], [0.250000000000000, 0.500000000000000, 0.500000000000000], [0.750000000000000, 0.500000000000000, 0.500000000000000], [0.000000000000000, 0.250000000000000, 0.000000000000000], [0.500000000000000, 0.250000000000000, 0.000000000000000], [0.000000000000000, 0.750000000000000, 0.000000000000000], [0.500000000000000, 0.750000000000000, 0.000000000000000], [0.000000000000000, 0.250000000000000, 0.500000000000000], [0.500000000000000, 0.250000000000000, 0.500000000000000], [0.000000000000000, 0.750000000000000, 0.500000000000000], [0.500000000000000, 0.750000000000000, 0.500000000000000], [0.000000000000000, 0.000000000000000, 0.250000000000000], [0.500000000000000, 0.000000000000000, 0.250000000000000], [0.000000000000000, 0.500000000000000, 0.250000000000000], [0.500000000000000, 0.500000000000000, 0.250000000000000], [0.000000000000000, 0.000000000000000, 0.750000000000000], [0.500000000000000, 0.000000000000000, 0.750000000000000], [0.000000000000000, 0.500000000000000, 0.750000000000000], [0.500000000000000, 0.500000000000000, 0.750000000000000], ] numbers = [ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, ] n_nonzero_elem = _check(lattice, points, numbers) ref = [11200, 32896, 185536, 262144] np.testing.assert_equal(ref, n_nonzero_elem) def test_FCCutoff_gan_443(): """Test FCCutoff using GaN 4x4x3. import spglib from phonopy.structure.atoms import PhonopyAtoms from phonopy.structure.cells import get_supercell dataset = spglib.get_symmetry_dataset( (cell_gan_111.cell, cell_gan_111.scaled_positions, cell_gan_111.numbers) ) phonopy_cell = PhonopyAtoms( cell=dataset.std_lattice, scaled_positions=dataset.std_positions, numbers=dataset.std_types, ) scell = get_supercell(phonopy_cell, [[4, 0, 0], [0, 4, 0], [0, 0, 3]]) for v in scell.cell: print(f"[{v[0]:.10f}, {v[1]:.10f}, {v[2]:.10f}],") for v in scell.scaled_positions: print(f"[{v[0]:.10f}, {v[1]:.10f}, {v[2]:.10f}],") print(", ".join([f"{n}" for n in scell.numbers])) """ lattice = [ [12.7230620734, 0.0000000000, 0.0000000000], [-6.3615310367, 11.0184949695, 0.0000000000], [0.0000000000, 0.0000000000, 15.5478175200], ] points = [ [0.0833333333, 0.1666666667, 0.0413973067], [0.3333333333, 0.1666666667, 0.0413973067], [0.5833333333, 0.1666666667, 0.0413973067], [0.8333333333, 0.1666666667, 0.0413973067], [0.0833333333, 0.4166666667, 0.0413973067], [0.3333333333, 0.4166666667, 0.0413973067], [0.5833333333, 0.4166666667, 0.0413973067], [0.8333333333, 0.4166666667, 0.0413973067], [0.0833333333, 0.6666666667, 0.0413973067], [0.3333333333, 0.6666666667, 0.0413973067], [0.5833333333, 0.6666666667, 0.0413973067], [0.8333333333, 0.6666666667, 0.0413973067], [0.0833333333, 0.9166666667, 0.0413973067], [0.3333333333, 0.9166666667, 0.0413973067], [0.5833333333, 0.9166666667, 0.0413973067], [0.8333333333, 0.9166666667, 0.0413973067], [0.0833333333, 0.1666666667, 0.3747306400], [0.3333333333, 0.1666666667, 0.3747306400], [0.5833333333, 0.1666666667, 0.3747306400], [0.8333333333, 0.1666666667, 0.3747306400], [0.0833333333, 0.4166666667, 0.3747306400], [0.3333333333, 0.4166666667, 0.3747306400], [0.5833333333, 0.4166666667, 0.3747306400], [0.8333333333, 0.4166666667, 0.3747306400], [0.0833333333, 0.6666666667, 0.3747306400], [0.3333333333, 0.6666666667, 0.3747306400], [0.5833333333, 0.6666666667, 0.3747306400], [0.8333333333, 0.6666666667, 0.3747306400], [0.0833333333, 0.9166666667, 0.3747306400], [0.3333333333, 0.9166666667, 0.3747306400], [0.5833333333, 0.9166666667, 0.3747306400], [0.8333333333, 0.9166666667, 0.3747306400], [0.0833333333, 0.1666666667, 0.7080639733], [0.3333333333, 0.1666666667, 0.7080639733], [0.5833333333, 0.1666666667, 0.7080639733], [0.8333333333, 0.1666666667, 0.7080639733], [0.0833333333, 0.4166666667, 0.7080639733], [0.3333333333, 0.4166666667, 0.7080639733], [0.5833333333, 0.4166666667, 0.7080639733], [0.8333333333, 0.4166666667, 0.7080639733], [0.0833333333, 0.6666666667, 0.7080639733], [0.3333333333, 0.6666666667, 0.7080639733], [0.5833333333, 0.6666666667, 0.7080639733], [0.8333333333, 0.6666666667, 0.7080639733], [0.0833333333, 0.9166666667, 0.7080639733], [0.3333333333, 0.9166666667, 0.7080639733], [0.5833333333, 0.9166666667, 0.7080639733], [0.8333333333, 0.9166666667, 0.7080639733], [0.1666666667, 0.0833333333, 0.2080639733], [0.4166666667, 0.0833333333, 0.2080639733], [0.6666666667, 0.0833333333, 0.2080639733], [0.9166666667, 0.0833333333, 0.2080639733], [0.1666666667, 0.3333333333, 0.2080639733], [0.4166666667, 0.3333333333, 0.2080639733], [0.6666666667, 0.3333333333, 0.2080639733], [0.9166666667, 0.3333333333, 0.2080639733], [0.1666666667, 0.5833333333, 0.2080639733], [0.4166666667, 0.5833333333, 0.2080639733], [0.6666666667, 0.5833333333, 0.2080639733], [0.9166666667, 0.5833333333, 0.2080639733], [0.1666666667, 0.8333333333, 0.2080639733], [0.4166666667, 0.8333333333, 0.2080639733], [0.6666666667, 0.8333333333, 0.2080639733], [0.9166666667, 0.8333333333, 0.2080639733], [0.1666666667, 0.0833333333, 0.5413973067], [0.4166666667, 0.0833333333, 0.5413973067], [0.6666666667, 0.0833333333, 0.5413973067], [0.9166666667, 0.0833333333, 0.5413973067], [0.1666666667, 0.3333333333, 0.5413973067], [0.4166666667, 0.3333333333, 0.5413973067], [0.6666666667, 0.3333333333, 0.5413973067], [0.9166666667, 0.3333333333, 0.5413973067], [0.1666666667, 0.5833333333, 0.5413973067], [0.4166666667, 0.5833333333, 0.5413973067], [0.6666666667, 0.5833333333, 0.5413973067], [0.9166666667, 0.5833333333, 0.5413973067], [0.1666666667, 0.8333333333, 0.5413973067], [0.4166666667, 0.8333333333, 0.5413973067], [0.6666666667, 0.8333333333, 0.5413973067], [0.9166666667, 0.8333333333, 0.5413973067], [0.1666666667, 0.0833333333, 0.8747306400], [0.4166666667, 0.0833333333, 0.8747306400], [0.6666666667, 0.0833333333, 0.8747306400], [0.9166666667, 0.0833333333, 0.8747306400], [0.1666666667, 0.3333333333, 0.8747306400], [0.4166666667, 0.3333333333, 0.8747306400], [0.6666666667, 0.3333333333, 0.8747306400], [0.9166666667, 0.3333333333, 0.8747306400], [0.1666666667, 0.5833333333, 0.8747306400], [0.4166666667, 0.5833333333, 0.8747306400], [0.6666666667, 0.5833333333, 0.8747306400], [0.9166666667, 0.5833333333, 0.8747306400], [0.1666666667, 0.8333333333, 0.8747306400], [0.4166666667, 0.8333333333, 0.8747306400], [0.6666666667, 0.8333333333, 0.8747306400], [0.9166666667, 0.8333333333, 0.8747306400], [0.0833333333, 0.1666666667, 0.1669360267], [0.3333333333, 0.1666666667, 0.1669360267], [0.5833333333, 0.1666666667, 0.1669360267], [0.8333333333, 0.1666666667, 0.1669360267], [0.0833333333, 0.4166666667, 0.1669360267], [0.3333333333, 0.4166666667, 0.1669360267], [0.5833333333, 0.4166666667, 0.1669360267], [0.8333333333, 0.4166666667, 0.1669360267], [0.0833333333, 0.6666666667, 0.1669360267], [0.3333333333, 0.6666666667, 0.1669360267], [0.5833333333, 0.6666666667, 0.1669360267], [0.8333333333, 0.6666666667, 0.1669360267], [0.0833333333, 0.9166666667, 0.1669360267], [0.3333333333, 0.9166666667, 0.1669360267], [0.5833333333, 0.9166666667, 0.1669360267], [0.8333333333, 0.9166666667, 0.1669360267], [0.0833333333, 0.1666666667, 0.5002693600], [0.3333333333, 0.1666666667, 0.5002693600], [0.5833333333, 0.1666666667, 0.5002693600], [0.8333333333, 0.1666666667, 0.5002693600], [0.0833333333, 0.4166666667, 0.5002693600], [0.3333333333, 0.4166666667, 0.5002693600], [0.5833333333, 0.4166666667, 0.5002693600], [0.8333333333, 0.4166666667, 0.5002693600], [0.0833333333, 0.6666666667, 0.5002693600], [0.3333333333, 0.6666666667, 0.5002693600], [0.5833333333, 0.6666666667, 0.5002693600], [0.8333333333, 0.6666666667, 0.5002693600], [0.0833333333, 0.9166666667, 0.5002693600], [0.3333333333, 0.9166666667, 0.5002693600], [0.5833333333, 0.9166666667, 0.5002693600], [0.8333333333, 0.9166666667, 0.5002693600], [0.0833333333, 0.1666666667, 0.8336026933], [0.3333333333, 0.1666666667, 0.8336026933], [0.5833333333, 0.1666666667, 0.8336026933], [0.8333333333, 0.1666666667, 0.8336026933], [0.0833333333, 0.4166666667, 0.8336026933], [0.3333333333, 0.4166666667, 0.8336026933], [0.5833333333, 0.4166666667, 0.8336026933], [0.8333333333, 0.4166666667, 0.8336026933], [0.0833333333, 0.6666666667, 0.8336026933], [0.3333333333, 0.6666666667, 0.8336026933], [0.5833333333, 0.6666666667, 0.8336026933], [0.8333333333, 0.6666666667, 0.8336026933], [0.0833333333, 0.9166666667, 0.8336026933], [0.3333333333, 0.9166666667, 0.8336026933], [0.5833333333, 0.9166666667, 0.8336026933], [0.8333333333, 0.9166666667, 0.8336026933], [0.1666666667, 0.0833333333, 0.0002693600], [0.4166666667, 0.0833333333, 0.0002693600], [0.6666666667, 0.0833333333, 0.0002693600], [0.9166666667, 0.0833333333, 0.0002693600], [0.1666666667, 0.3333333333, 0.0002693600], [0.4166666667, 0.3333333333, 0.0002693600], [0.6666666667, 0.3333333333, 0.0002693600], [0.9166666667, 0.3333333333, 0.0002693600], [0.1666666667, 0.5833333333, 0.0002693600], [0.4166666667, 0.5833333333, 0.0002693600], [0.6666666667, 0.5833333333, 0.0002693600], [0.9166666667, 0.5833333333, 0.0002693600], [0.1666666667, 0.8333333333, 0.0002693600], [0.4166666667, 0.8333333333, 0.0002693600], [0.6666666667, 0.8333333333, 0.0002693600], [0.9166666667, 0.8333333333, 0.0002693600], [0.1666666667, 0.0833333333, 0.3336026933], [0.4166666667, 0.0833333333, 0.3336026933], [0.6666666667, 0.0833333333, 0.3336026933], [0.9166666667, 0.0833333333, 0.3336026933], [0.1666666667, 0.3333333333, 0.3336026933], [0.4166666667, 0.3333333333, 0.3336026933], [0.6666666667, 0.3333333333, 0.3336026933], [0.9166666667, 0.3333333333, 0.3336026933], [0.1666666667, 0.5833333333, 0.3336026933], [0.4166666667, 0.5833333333, 0.3336026933], [0.6666666667, 0.5833333333, 0.3336026933], [0.9166666667, 0.5833333333, 0.3336026933], [0.1666666667, 0.8333333333, 0.3336026933], [0.4166666667, 0.8333333333, 0.3336026933], [0.6666666667, 0.8333333333, 0.3336026933], [0.9166666667, 0.8333333333, 0.3336026933], [0.1666666667, 0.0833333333, 0.6669360267], [0.4166666667, 0.0833333333, 0.6669360267], [0.6666666667, 0.0833333333, 0.6669360267], [0.9166666667, 0.0833333333, 0.6669360267], [0.1666666667, 0.3333333333, 0.6669360267], [0.4166666667, 0.3333333333, 0.6669360267], [0.6666666667, 0.3333333333, 0.6669360267], [0.9166666667, 0.3333333333, 0.6669360267], [0.1666666667, 0.5833333333, 0.6669360267], [0.4166666667, 0.5833333333, 0.6669360267], [0.6666666667, 0.5833333333, 0.6669360267], [0.9166666667, 0.5833333333, 0.6669360267], [0.1666666667, 0.8333333333, 0.6669360267], [0.4166666667, 0.8333333333, 0.6669360267], [0.6666666667, 0.8333333333, 0.6669360267], [0.9166666667, 0.8333333333, 0.6669360267], ] numbers = [ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, ] n_nonzero_elem = _check(lattice, points, numbers) print(n_nonzero_elem) ref = [69312, 665280, 3840768, 6644736] np.testing.assert_equal(ref, n_nonzero_elem) def _check( lattice: Sequence[Sequence[float]] | NDArray, points: Sequence[Sequence[float]] | NDArray, numbers: Sequence[int] | NDArray, ): n_nonzero_elem = [] for cutoff in (4, 6, 8, 10): fccutoff = FCCutoff( SymfcAtoms(cell=lattice, scaled_positions=points, numbers=numbers), cutoff=cutoff, ) nonzero_idx = [ f"{i}" for i, nonzero in enumerate(fccutoff.nonzero_atomic_indices_fc3()) if nonzero ] n_nonzero_elem.append(len(nonzero_idx)) return n_nonzero_elem symfc-1.6.0/tests/utils/test_eig_tools.py000066400000000000000000000100651511276756400205560ustar00rootroot00000000000000"""Tests of eigsh functions.""" import numpy as np import pytest from scipy.sparse import csr_array from symfc.utils.eig_tools import ( _compr_projector, eigh_projector, eigh_projector_division, eigsh_projector, eigsh_projector_sumrule, ) def test_eigsh_projector(): """Test eigsh_projector and eigsh_projector_sumrule.""" # Atomic permutations of four atoms row = np.repeat(np.arange(12), 4) col = np.array([i + j for j in range(3) for i in range(0, 12, 3)]) col = np.tile(col, 4) data = np.full(len(col), 0.25) proj = csr_array((data, (row, col)), shape=(12, 12), dtype=float) eigvecs = eigsh_projector(proj, verbose=False) eigvecs = eigvecs.toarray() assert eigvecs.shape[1] == 3 assert np.linalg.norm(eigvecs[:, 0]) == pytest.approx(1.0) assert np.linalg.norm(eigvecs[:, 1]) == pytest.approx(1.0) assert np.linalg.norm(eigvecs[:, 2]) == pytest.approx(1.0) assert eigvecs[:, 0] @ eigvecs[:, 1] == pytest.approx(0.0) assert eigvecs[:, 1] @ eigvecs[:, 2] == pytest.approx(0.0) assert eigvecs[:, 2] @ eigvecs[:, 0] == pytest.approx(0.0) for i in range(3): nonzero = np.where(np.abs(eigvecs[:, i]) > 1e-12)[0] assert np.all(np.isclose(eigvecs[:, i][nonzero], eigvecs[nonzero[0], i])) eigvecs = eigsh_projector_sumrule(proj, verbose=False) eigvecs = eigvecs.recover() assert eigvecs.shape[1] == 3 assert np.linalg.norm(eigvecs[:, 0]) == pytest.approx(1.0) assert np.linalg.norm(eigvecs[:, 1]) == pytest.approx(1.0) assert np.linalg.norm(eigvecs[:, 2]) == pytest.approx(1.0) assert eigvecs[:, 0] @ eigvecs[:, 1] == pytest.approx(0.0) assert eigvecs[:, 1] @ eigvecs[:, 2] == pytest.approx(0.0) assert eigvecs[:, 2] @ eigvecs[:, 0] == pytest.approx(0.0) for i in range(3): nonzero = np.where(np.abs(eigvecs[:, i]) > 1e-12)[0] assert np.all(np.isclose(eigvecs[:, i][nonzero], eigvecs[nonzero[0], i])) def test_eigh_projector(): """Test eigh_projector and eigh_projector_use_submatrix.""" # Atomic permutations of four atoms row = np.repeat(np.arange(12), 4) col = np.array([i + j for j in range(3) for i in range(0, 12, 3)]) col = np.tile(col, 4) data = np.full(len(col), 0.25) proj = csr_array((data, (row, col)), shape=(12, 12), dtype=float).toarray() eigvecs = eigh_projector(proj, verbose=False) assert eigvecs.shape[1] == 3 assert np.linalg.norm(eigvecs[:, 0]) == pytest.approx(1.0) assert np.linalg.norm(eigvecs[:, 1]) == pytest.approx(1.0) assert np.linalg.norm(eigvecs[:, 2]) == pytest.approx(1.0) assert eigvecs[:, 0] @ eigvecs[:, 1] == pytest.approx(0.0) assert eigvecs[:, 1] @ eigvecs[:, 2] == pytest.approx(0.0) assert eigvecs[:, 2] @ eigvecs[:, 0] == pytest.approx(0.0) for i in range(3): nonzero = np.where(np.abs(eigvecs[:, i]) > 1e-12)[0] assert np.all(np.isclose(eigvecs[:, i][nonzero], eigvecs[nonzero[0], i])) eigvecs = eigh_projector_division(proj, verbose=False) assert eigvecs.shape[1] == 3 assert np.linalg.norm(eigvecs[:, 0]) == pytest.approx(1.0) assert np.linalg.norm(eigvecs[:, 1]) == pytest.approx(1.0) assert np.linalg.norm(eigvecs[:, 2]) == pytest.approx(1.0) assert eigvecs[:, 0] @ eigvecs[:, 1] == pytest.approx(0.0) assert eigvecs[:, 1] @ eigvecs[:, 2] == pytest.approx(0.0) assert eigvecs[:, 2] @ eigvecs[:, 0] == pytest.approx(0.0) for i in range(3): nonzero = np.where(np.abs(eigvecs[:, i]) > 1e-12)[0] assert np.all(np.isclose(eigvecs[:, i][nonzero], eigvecs[nonzero[0], i])) def test_compr_projector(): """Test compr_projector.""" row = col = [0, 2, 5] data = [1, 1, 1] proj = csr_array((data, (row, col)), shape=(6, 6), dtype=int) proj_rev, compr = _compr_projector(proj) assert proj_rev.shape == (3, 3) np.testing.assert_allclose(proj_rev.toarray(), np.eye(3)) assert compr.shape == (6, 3) compr_ref = np.zeros(compr.shape, dtype=int) for icol, irow in enumerate(row): compr_ref[irow, icol] = 1 np.testing.assert_allclose(compr.toarray(), compr_ref) symfc-1.6.0/tests/utils/test_graph.py000066400000000000000000000014321511276756400176710ustar00rootroot00000000000000"""Tests of graph.""" import numpy as np from scipy.sparse import csr_array from symfc.utils.graph import connected_components def test_connected_components(): """Test connected_component using DFS.""" mat = np.array( [ [5, 4, 0, 2, 0, 0, 0, 0], [4, 3, 0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 4, 0, 0, 0, 0], [0, 0, 0, 0, 5, 3, 0, 1], [0, 0, 0, 0, 3, 4, 1, 2], [0, 0, 0, 0, 7, 8, 1, 0], [0, 0, 0, 0, 7, 2, 0, 1], ] ) mat = csr_array(mat + mat.T) group = connected_components(mat) np.testing.assert_array_equal(group[0], [0, 1, 3]) np.testing.assert_array_equal(group[1], [2]) np.testing.assert_array_equal(group[2], [4, 5, 6, 7]) symfc-1.6.0/tests/utils/test_permutation_tools_O2.py000066400000000000000000000044171511276756400227250ustar00rootroot00000000000000"""Tests of functions in permutation_tools_O2.""" import numpy as np import pytest from symfc.spg_reps import SpgRepsBase from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.permutation_tools_O2 import ( _N3N3_to_NNand33, compr_permutation_lat_trans_O2, ) from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O2 import _get_atomic_lat_trans_decompr_indices def structure_bcc(): """Get bcc structure.""" lattice = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 2]]) positions = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) numbers = [1, 1] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) spg_reps = SpgRepsBase(supercell) trans_perms = spg_reps.translation_permutations return supercell, trans_perms supercell, trans_perms = structure_bcc() def test_N3N3_to_NNand33(): """Test N3N3_to_NNand33.""" N = 3 combs = np.array([[0, 1], [2, 4], [5, 8]]) vecNN, vec33 = _N3N3_to_NNand33(combs, N) np.testing.assert_allclose(vecNN, [0, 1, 5]) np.testing.assert_allclose(vec33, [1, 7, 8]) def test_projector_permutation_lat_trans_O2(): """Test projector_permutation_lat_trans_O2.""" atomic_decompr_idx = _get_atomic_lat_trans_decompr_indices(trans_perms) c_pt = compr_permutation_lat_trans_O2( trans_perms, atomic_decompr_idx=atomic_decompr_idx, fc_cutoff=None, ) proj = c_pt @ c_pt.T assert proj.trace() == pytest.approx(12.0) assert proj.shape == (18, 18) proj_ref = np.zeros(proj.shape) proj_ref[([0, 4, 8, 9, 13, 17], [0, 4, 8, 9, 13, 17])] = 1.0 for iset in [[1, 3], [2, 6], [5, 7], [10, 12], [11, 15], [14, 16]]: row, col = np.meshgrid(*[iset, iset]) row = row.reshape(-1) col = col.reshape(-1) proj_ref[(row, col)] = 0.5 np.testing.assert_allclose(proj.toarray(), proj_ref) c_pt = compr_permutation_lat_trans_O2( trans_perms, atomic_decompr_idx=atomic_decompr_idx, fc_cutoff=FCCutoff(supercell, cutoff=1), ) proj = c_pt @ c_pt.T proj_ref_cutoff = np.zeros_like(proj_ref) proj_ref_cutoff[0:9, 0:9] = proj_ref[0:9, 0:9] assert proj.trace() == pytest.approx(6.0) assert proj.shape == (18, 18) np.testing.assert_allclose(proj.toarray(), proj_ref_cutoff) symfc-1.6.0/tests/utils/test_permutation_tools_O3.py000066400000000000000000000043071511276756400227240ustar00rootroot00000000000000"""Tests of functions in permutation_tools_O3.""" import numpy as np import pytest from symfc.spg_reps import SpgRepsBase from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.permutation_tools_O3 import ( _N3N3N3_to_NNNand333, compr_permutation_lat_trans_O3, ) from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O3 import get_atomic_lat_trans_decompr_indices_O3 def structure_bcc(): """Get bcc structure.""" lattice = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 2]]) positions = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) numbers = [1, 1] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) spg_reps = SpgRepsBase(supercell) trans_perms = spg_reps.translation_permutations return supercell, trans_perms supercell, trans_perms = structure_bcc() def test_N3N3N3_to_NNNand333(): """Test N3N33_to_NNNand333.""" N = 3 combs = np.array([[0, 1, 2], [2, 4, 6], [3, 5, 8]]) vecNNN, vec333 = _N3N3N3_to_NNNand333(combs, N) np.testing.assert_allclose(vecNNN, [0, 5, 14]) np.testing.assert_allclose(vec333, [5, 21, 8]) def test_projector_permutation_lat_trans_O3(): """Test projector_permutation_lat_trans_O3.""" atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O3(trans_perms) c_pt = compr_permutation_lat_trans_O3( trans_perms, atomic_decompr_idx=atomic_decompr_idx, fc_cutoff=None, ) proj = c_pt @ c_pt.T assert proj.trace() == pytest.approx(28.0) assert proj.shape == (108, 108) assert len(proj.data) == 498 assert np.count_nonzero(np.isclose(proj.data, 1)) == 3 assert np.count_nonzero(np.isclose(proj.data, 1.0 / 3.0)) == 135 assert np.count_nonzero(np.isclose(proj.data, 1.0 / 6.0)) == 360 c_pt = compr_permutation_lat_trans_O3( trans_perms, atomic_decompr_idx=atomic_decompr_idx, fc_cutoff=FCCutoff(supercell, cutoff=1), ) proj = c_pt @ c_pt.T assert proj.trace() == pytest.approx(10.0) assert len(proj.data) == 93 assert np.count_nonzero(np.isclose(proj.data, 1)) == 3 assert np.count_nonzero(np.isclose(proj.data, 1.0 / 3.0)) == 54 assert np.count_nonzero(np.isclose(proj.data, 1.0 / 6.0)) == 36 symfc-1.6.0/tests/utils/test_permutation_tools_O4.py000066400000000000000000000007171511276756400227260ustar00rootroot00000000000000"""Tests of functions in permutation_tools_O4.""" import numpy as np from symfc.utils.permutation_tools_O4 import ( _N3N3N3N3_to_NNNNand3333, ) def test_N3N3N3N3_to_NNNNand3333(): """Test N3N33_to_NNNand333.""" N = 3 combs = np.array([[0, 1, 2, 5], [2, 4, 6, 8], [3, 4, 5, 8]]) vecNNNN, vec3333 = _N3N3N3N3_to_NNNNand3333(combs, N) np.testing.assert_allclose(vecNNNN, [1, 17, 41]) np.testing.assert_allclose(vec3333, [17, 65, 17]) symfc-1.6.0/tests/utils/test_solver_funcs.py000066400000000000000000000032611511276756400213020ustar00rootroot00000000000000"""Tests of functions in solver_funcs.""" import numpy as np from symfc.utils.solver_funcs import ( fit, get_batch_slice, get_displacement_sparse_matrix, ) def test_fit(): """Test linear regression fit.""" X = np.array( [ [1.0, 1.0, 1.0, 1.0], [1.0, 2.0, 4.0, 8.0], [1.0, 4.0, 16.0, 64.0], [1.0, 6.0, 36.0, 216.0], [1.0, 8.0, 64.0, 512.0], [1.0, 10.0, 100.0, 1000.0], ] ) y = np.array([14.1, 41.9, 179.2, 458.7, 931.0, 1642.6]) coefs = fit(X, y) np.testing.assert_allclose(coefs, [3.18818393, 3.46030191, 6.13455974, 0.99131806]) def test_batch_slice(): """Test get_batch_slice.""" begin, end = get_batch_slice(n_data=500, batch_size=50) assert len(begin) == len(end) == 10 diff = np.array(end) - np.array(begin) np.testing.assert_array_equal(diff, [50] * 10) begin, end = get_batch_slice(n_data=1000, batch_size=36) assert len(begin) == len(end) == 28 diff = np.array(end) - np.array(begin) np.testing.assert_array_equal(diff[:-1], [36] * 27) assert diff[-1] == 28 def test_sparse_matrix(): """Test get_displacement_sparse_matrix.""" n_atom = 64 atoms = np.array([0, 0, 32, 32]) displacements = [ [0.01, 0.0, 0.02], [0.0, 0.01, 0.0], [0.01, 0.02, 0.0], [0.0, 0.01, 0.0], ] displacements = np.array(displacements) mat = get_displacement_sparse_matrix(atoms, displacements, n_atom, tol=1e-15) rows, cols = mat.nonzero() assert list(rows) == [0, 0, 1, 2, 2, 3] assert list(cols) == [0, 2, 1, 96, 97, 97] assert list(mat.data) == [0.01, 0.02, 0.01, 0.01, 0.02, 0.01] symfc-1.6.0/tests/utils/test_translation_tools_O2.py000066400000000000000000000032051511276756400227060ustar00rootroot00000000000000"""Tests of functions in translation_tools_O2.""" import numpy as np import pytest import scipy from symfc.spg_reps import SpgRepsBase from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.translation_tools_O2 import compressed_projector_sum_rules_O2 from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O2 import _get_atomic_lat_trans_decompr_indices def structure_bcc(): """Get bcc structure.""" lattice = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 2]]) positions = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) numbers = [1, 1] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) spg_reps = SpgRepsBase(supercell) trans_perms = spg_reps.translation_permutations return supercell, trans_perms supercell, trans_perms = structure_bcc() def test_compressed_projector_sum_rules_O2(): """Test compressed_projector_sum_rules_O2.""" atomic_decompr_idx = _get_atomic_lat_trans_decompr_indices(trans_perms) n_a_compress_mat = scipy.sparse.identity(18) proj = compressed_projector_sum_rules_O2( trans_perms, n_a_compress_mat, atomic_decompr_idx, fc_cutoff=None, ) eigvals, _ = np.linalg.eigh(proj.toarray()) assert proj.shape == (18, 18) assert np.count_nonzero(np.isclose(eigvals, 1.0)) == 9 proj = compressed_projector_sum_rules_O2( trans_perms, n_a_compress_mat, atomic_decompr_idx, fc_cutoff=FCCutoff(supercell, cutoff=1), ) """If the cutoff implementation is changed, the trace value may also change.""" assert proj.trace() == pytest.approx(13.5) assert len(proj.data) == 18 symfc-1.6.0/tests/utils/test_translation_tools_O3.py000066400000000000000000000032161511276756400227110ustar00rootroot00000000000000"""Tests of functions in translation_tools_O3.""" import numpy as np import pytest import scipy from symfc.spg_reps import SpgRepsBase from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.translation_tools_O3 import compressed_projector_sum_rules_O3 from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O3 import get_atomic_lat_trans_decompr_indices_O3 def structure_bcc(): """Get bcc structure.""" lattice = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 2]]) positions = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) numbers = [1, 1] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) spg_reps = SpgRepsBase(supercell) trans_perms = spg_reps.translation_permutations return supercell, trans_perms supercell, trans_perms = structure_bcc() def test_compressed_projector_sum_rules_O3(): """Test compressed_projector_sum_rules_O3.""" atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O3(trans_perms) n_a_compress_mat = scipy.sparse.identity(108) proj = compressed_projector_sum_rules_O3( trans_perms, n_a_compress_mat, atomic_decompr_idx, fc_cutoff=None, ) eigvals, _ = np.linalg.eigh(proj.toarray()) assert proj.shape == (108, 108) assert np.count_nonzero(np.isclose(eigvals, 1.0)) == 54 proj = compressed_projector_sum_rules_O3( trans_perms, n_a_compress_mat, atomic_decompr_idx, fc_cutoff=FCCutoff(supercell, cutoff=1), ) """If the cutoff implementation is changed, the trace value may also change.""" assert proj.trace() == pytest.approx(94.5) assert len(proj.data) == 108 symfc-1.6.0/tests/utils/test_utils.py000066400000000000000000000433031511276756400177330ustar00rootroot00000000000000"""Tests of matrix manipulating functions.""" from __future__ import annotations from pathlib import Path from typing import cast import numpy as np import pytest from numpy.typing import NDArray from symfc.spg_reps import SpgRepsBase from symfc.utils.utils import ( SymfcAtoms, compute_sg_permutations, compute_sg_permutations_stable, get_indep_atoms_by_lat_trans, ) cwd = Path(__file__).parent def test_get_indep_atoms_by_lattice_translation( ph_nacl_222: tuple[SymfcAtoms, NDArray, NDArray], ): """Test of get_indep_atoms_by_lattice_translation.""" supercell, _, _ = ph_nacl_222 sym_op_reps = SpgRepsBase(supercell) trans_perms = sym_op_reps.translation_permutations assert trans_perms.shape == (32, 64) indep_atoms = get_indep_atoms_by_lat_trans(trans_perms) np.testing.assert_array_equal(indep_atoms, [0, 32]) def test_compute_sg_permutations( ph_gan_222: tuple[SymfcAtoms, NDArray, NDArray], cell_gan_111: SymfcAtoms ): """Test compute_sg_permutations.""" pytest.importorskip("spglib") import spglib from spglib.spglib import Cell as SpgCell supercell, _, _ = ph_gan_222 primitive = cell_gan_111 dataset = spglib.get_symmetry_dataset(cast(SpgCell, supercell.totuple())) assert dataset is not None primitive_dataset = spglib.get_symmetry_dataset(cast(SpgCell, primitive.totuple())) assert primitive_dataset is not None perms = compute_sg_permutations( primitive.scaled_positions, primitive_dataset.rotations, primitive_dataset.translations, primitive.cell, ) ref_perms = [ [0, 1, 2, 3], [1, 0, 3, 2], [0, 1, 2, 3], [1, 0, 3, 2], [0, 1, 2, 3], [1, 0, 3, 2], [1, 0, 3, 2], [0, 1, 2, 3], [1, 0, 3, 2], [0, 1, 2, 3], [1, 0, 3, 2], [0, 1, 2, 3], ] np.testing.assert_array_equal(ref_perms, perms) perms_super = compute_sg_permutations( supercell.scaled_positions, dataset.rotations, dataset.translations, supercell.cell, ) # np.savetxt("perms_super.dat", perms_super, fmt="%d") perms_super_ref = np.loadtxt(cwd / ".." / "perms_super.dat", dtype=int) np.testing.assert_array_equal(perms_super_ref, perms_super) def test_compute_sg_permutations_compare_stable(): """Test compute_sg_permutations and compare with compute_sg_permutations_stable.""" pytest.importorskip("spglib") import spglib from spglib.spglib import Cell as SpgCell axis = np.array([[0.0, 1.0, 1.0], [7.0, 6.0, 7.0], [8.0, 8.0, 8.0]]) positions = np.array( [ [0.000000000000000, 0.000000000000000, 0.000000000000000], [0.000000000000000, 0.000000000000000, 0.125000000000000], [0.000000000000000, 0.000000000000000, 0.250000000000000], [0.000000000000000, 0.000000000000000, 0.375000000000000], [0.000000000000000, 0.000000000000000, 0.500000000000000], [0.000000000000000, 0.000000000000000, 0.625000000000000], [0.000000000000000, 0.000000000000000, 0.750000000000000], [0.000000000000000, 0.000000000000000, 0.875000000000000], ] ) types = np.zeros(8, dtype=int) cell = axis, positions, types dataset = spglib.get_symmetry_dataset(cast(SpgCell, cell)) assert dataset is not None perms = compute_sg_permutations( positions, dataset.rotations, dataset.translations, axis, ) perms_stable = compute_sg_permutations_stable( positions, dataset.rotations, dataset.translations, axis, ) ref_perms = [ [0, 1, 2, 3, 4, 5, 6, 7], [0, 7, 6, 5, 4, 3, 2, 1], [0, 7, 6, 5, 4, 3, 2, 1], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 7, 6, 5, 4, 3, 2, 1], [0, 7, 6, 5, 4, 3, 2, 1], [0, 1, 2, 3, 4, 5, 6, 7], [0, 7, 6, 5, 4, 3, 2, 1], [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7], [0, 7, 6, 5, 4, 3, 2, 1], [1, 2, 3, 4, 5, 6, 7, 0], [1, 0, 7, 6, 5, 4, 3, 2], [1, 0, 7, 6, 5, 4, 3, 2], [1, 2, 3, 4, 5, 6, 7, 0], [1, 2, 3, 4, 5, 6, 7, 0], [1, 0, 7, 6, 5, 4, 3, 2], [1, 0, 7, 6, 5, 4, 3, 2], [1, 2, 3, 4, 5, 6, 7, 0], [1, 0, 7, 6, 5, 4, 3, 2], [1, 2, 3, 4, 5, 6, 7, 0], [1, 2, 3, 4, 5, 6, 7, 0], [1, 0, 7, 6, 5, 4, 3, 2], [2, 3, 4, 5, 6, 7, 0, 1], [2, 1, 0, 7, 6, 5, 4, 3], [2, 1, 0, 7, 6, 5, 4, 3], [2, 3, 4, 5, 6, 7, 0, 1], [2, 3, 4, 5, 6, 7, 0, 1], [2, 1, 0, 7, 6, 5, 4, 3], [2, 1, 0, 7, 6, 5, 4, 3], [2, 3, 4, 5, 6, 7, 0, 1], [2, 1, 0, 7, 6, 5, 4, 3], [2, 3, 4, 5, 6, 7, 0, 1], [2, 3, 4, 5, 6, 7, 0, 1], [2, 1, 0, 7, 6, 5, 4, 3], [3, 4, 5, 6, 7, 0, 1, 2], [3, 2, 1, 0, 7, 6, 5, 4], [3, 2, 1, 0, 7, 6, 5, 4], [3, 4, 5, 6, 7, 0, 1, 2], [3, 4, 5, 6, 7, 0, 1, 2], [3, 2, 1, 0, 7, 6, 5, 4], [3, 2, 1, 0, 7, 6, 5, 4], [3, 4, 5, 6, 7, 0, 1, 2], [3, 2, 1, 0, 7, 6, 5, 4], [3, 4, 5, 6, 7, 0, 1, 2], [3, 4, 5, 6, 7, 0, 1, 2], [3, 2, 1, 0, 7, 6, 5, 4], [4, 5, 6, 7, 0, 1, 2, 3], [4, 3, 2, 1, 0, 7, 6, 5], [4, 3, 2, 1, 0, 7, 6, 5], [4, 5, 6, 7, 0, 1, 2, 3], [4, 5, 6, 7, 0, 1, 2, 3], [4, 3, 2, 1, 0, 7, 6, 5], [4, 3, 2, 1, 0, 7, 6, 5], [4, 5, 6, 7, 0, 1, 2, 3], [4, 3, 2, 1, 0, 7, 6, 5], [4, 5, 6, 7, 0, 1, 2, 3], [4, 5, 6, 7, 0, 1, 2, 3], [4, 3, 2, 1, 0, 7, 6, 5], [5, 6, 7, 0, 1, 2, 3, 4], [5, 4, 3, 2, 1, 0, 7, 6], [5, 4, 3, 2, 1, 0, 7, 6], [5, 6, 7, 0, 1, 2, 3, 4], [5, 6, 7, 0, 1, 2, 3, 4], [5, 4, 3, 2, 1, 0, 7, 6], [5, 4, 3, 2, 1, 0, 7, 6], [5, 6, 7, 0, 1, 2, 3, 4], [5, 4, 3, 2, 1, 0, 7, 6], [5, 6, 7, 0, 1, 2, 3, 4], [5, 6, 7, 0, 1, 2, 3, 4], [5, 4, 3, 2, 1, 0, 7, 6], [6, 7, 0, 1, 2, 3, 4, 5], [6, 5, 4, 3, 2, 1, 0, 7], [6, 5, 4, 3, 2, 1, 0, 7], [6, 7, 0, 1, 2, 3, 4, 5], [6, 7, 0, 1, 2, 3, 4, 5], [6, 5, 4, 3, 2, 1, 0, 7], [6, 5, 4, 3, 2, 1, 0, 7], [6, 7, 0, 1, 2, 3, 4, 5], [6, 5, 4, 3, 2, 1, 0, 7], [6, 7, 0, 1, 2, 3, 4, 5], [6, 7, 0, 1, 2, 3, 4, 5], [6, 5, 4, 3, 2, 1, 0, 7], [7, 0, 1, 2, 3, 4, 5, 6], [7, 6, 5, 4, 3, 2, 1, 0], [7, 6, 5, 4, 3, 2, 1, 0], [7, 0, 1, 2, 3, 4, 5, 6], [7, 0, 1, 2, 3, 4, 5, 6], [7, 6, 5, 4, 3, 2, 1, 0], [7, 6, 5, 4, 3, 2, 1, 0], [7, 0, 1, 2, 3, 4, 5, 6], [7, 6, 5, 4, 3, 2, 1, 0], [7, 0, 1, 2, 3, 4, 5, 6], [7, 0, 1, 2, 3, 4, 5, 6], [7, 6, 5, 4, 3, 2, 1, 0], ] np.testing.assert_array_equal(perms, perms_stable) np.testing.assert_array_equal(perms, ref_perms) def test_compute_sg_permutations_compare_stable_nacl( ph_nacl_222: tuple[SymfcAtoms, NDArray, NDArray], ): """Test of compute_sg_permutations for NaCl.""" pytest.importorskip("spglib") import spglib from spglib.spglib import Cell as SpgCell supercell, _, _ = ph_nacl_222 dataset = spglib.get_symmetry_dataset(cast(SpgCell, supercell.totuple())) assert dataset is not None perms = compute_sg_permutations( supercell.scaled_positions, dataset.rotations, dataset.translations, supercell.cell, ) perms_stable = compute_sg_permutations_stable( supercell.scaled_positions, dataset.rotations, dataset.translations, supercell.cell, ) np.testing.assert_array_equal(perms, perms_stable) def test_compute_sg_permutations_compare_stable_sio2( ph_sio2_221: tuple[SymfcAtoms, NDArray, NDArray], ): """Test of compute_sg_permutations for SiO2.""" pytest.importorskip("spglib") import spglib from spglib.spglib import Cell as SpgCell supercell, _, _ = ph_sio2_221 dataset = spglib.get_symmetry_dataset(cast(SpgCell, supercell.totuple())) assert dataset is not None perms = compute_sg_permutations( supercell.scaled_positions, dataset.rotations, dataset.translations, supercell.cell, ) perms_stable = compute_sg_permutations_stable( supercell.scaled_positions, dataset.rotations, dataset.translations, supercell.cell, ) np.testing.assert_array_equal(perms, perms_stable) def test_compute_sg_permutations_compare_stable_baal2o4(): """Test compute_sg_permutations for BaAl2O4.""" pytest.importorskip("spglib") import spglib from spglib.spglib import Cell as SpgCell types = np.zeros(8, dtype=int) axis = np.array( [ [10.4020522437529994, 0.0000000000000000, 0.0000000000000000], [-5.2010261218764997, 9.0084414945829998, 0.0000000000000000], [0.00000000000000000, 0.0000000000000000, 17.8057491600000013], ] ) types = np.hstack( [np.zeros(16, dtype=int), np.ones(32, dtype=int), np.ones(64, dtype=int) * 2] ) positions = np.array( [ [0.0000000000000000, 0.0000000000000000, 0.1250000000000000], [0.5000000000000000, 0.0000000000000000, 0.1250000000000000], [0.0000000000000050, 0.5000000000000000, 0.1250000000000000], [0.4999999999999950, 0.5000000000000000, 0.1250000000000000], [0.0000000000000000, 0.0000000000000000, 0.6250000000000000], [0.5000000000000000, 0.0000000000000000, 0.6250000000000000], [0.0000000000000050, 0.5000000000000000, 0.6250000000000000], [0.4999999999999950, 0.5000000000000000, 0.6250000000000000], [0.0000000000000000, 0.0000000000000000, 0.3750000000000000], [0.5000000000000000, 0.0000000000000000, 0.3750000000000000], [0.0000000000000050, 0.5000000000000000, 0.3750000000000000], [0.4999999999999950, 0.5000000000000000, 0.3750000000000000], [0.0000000000000000, 0.0000000000000000, 0.8750000000000000], [0.5000000000000000, 0.0000000000000000, 0.8750000000000000], [0.0000000000000050, 0.5000000000000000, 0.8750000000000000], [0.4999999999999950, 0.5000000000000000, 0.8750000000000000], [0.1666666666666690, 0.3333333333333370, 0.2768253700000010], [0.6666666666666690, 0.3333333333333370, 0.2768253700000010], [0.1666666666666730, 0.8333333333333370, 0.2768253700000010], [0.6666666666666640, 0.8333333333333370, 0.2768253700000010], [0.1666666666666690, 0.3333333333333370, 0.7768253699999890], [0.6666666666666690, 0.3333333333333370, 0.7768253699999890], [0.1666666666666730, 0.8333333333333370, 0.7768253699999890], [0.6666666666666640, 0.8333333333333370, 0.7768253699999890], [0.3333333333333270, 0.1666666666666630, 0.0268253700000010], [0.8333333333333270, 0.1666666666666630, 0.0268253700000010], [0.3333333333333320, 0.6666666666666630, 0.0268253700000010], [0.8333333333333310, 0.6666666666666630, 0.0268253700000010], [0.3333333333333270, 0.1666666666666630, 0.5268253700000010], [0.8333333333333270, 0.1666666666666630, 0.5268253700000010], [0.3333333333333320, 0.6666666666666630, 0.5268253700000010], [0.8333333333333310, 0.6666666666666630, 0.5268253700000010], [0.3333333333333270, 0.1666666666666630, 0.2231746299999990], [0.8333333333333270, 0.1666666666666630, 0.2231746299999990], [0.3333333333333320, 0.6666666666666630, 0.2231746299999990], [0.8333333333333310, 0.6666666666666630, 0.2231746299999990], [0.3333333333333270, 0.1666666666666630, 0.7231746300000110], [0.8333333333333270, 0.1666666666666630, 0.7231746300000110], [0.3333333333333320, 0.6666666666666630, 0.7231746300000110], [0.8333333333333310, 0.6666666666666630, 0.7231746300000110], [0.1666666666666690, 0.3333333333333370, 0.4731746299999990], [0.6666666666666690, 0.3333333333333370, 0.4731746299999990], [0.1666666666666730, 0.8333333333333370, 0.4731746299999990], [0.6666666666666640, 0.8333333333333370, 0.4731746299999990], [0.1666666666666690, 0.3333333333333370, 0.9731746300000110], [0.6666666666666690, 0.3333333333333370, 0.9731746300000110], [0.1666666666666730, 0.8333333333333370, 0.9731746300000110], [0.6666666666666640, 0.8333333333333370, 0.9731746300000110], [0.1749127449999990, 0.1749127449999980, 0.2500000000000000], [0.6749127449999970, 0.1749127449999980, 0.2500000000000000], [0.1749127450000020, 0.6749127449999980, 0.2500000000000000], [0.6749127450000020, 0.6749127449999980, 0.2500000000000000], [0.1749127449999990, 0.1749127449999980, 0.7500000000000000], [0.6749127449999970, 0.1749127449999980, 0.7500000000000000], [0.1749127450000020, 0.6749127449999980, 0.7500000000000000], [0.6749127450000020, 0.6749127449999980, 0.7500000000000000], [0.0000000000000030, 0.3250872550000020, 0.2500000000000000], [0.5000000000000030, 0.3250872550000020, 0.2500000000000000], [0.9999999999999990, 0.8250872550000020, 0.2500000000000000], [0.5000000000000010, 0.8250872550000020, 0.2500000000000000], [0.0000000000000030, 0.3250872550000020, 0.7500000000000000], [0.5000000000000030, 0.3250872550000020, 0.7500000000000000], [0.9999999999999990, 0.8250872550000020, 0.7500000000000000], [0.5000000000000010, 0.8250872550000020, 0.7500000000000000], [0.9999999999999990, 0.1749127449999980, 0.0000000000000000], [0.5000000000000010, 0.1749127449999980, 0.0000000000000000], [0.9999999999999970, 0.6749127449999980, 0.0000000000000000], [0.4999999999999970, 0.6749127449999980, 0.0000000000000000], [0.9999999999999990, 0.1749127449999980, 0.5000000000000000], [0.5000000000000010, 0.1749127449999980, 0.5000000000000000], [0.9999999999999970, 0.6749127449999980, 0.5000000000000000], [0.4999999999999970, 0.6749127449999980, 0.5000000000000000], [0.1666666666666690, 0.3333333333333370, 0.3750000000000000], [0.6666666666666690, 0.3333333333333370, 0.3750000000000000], [0.1666666666666730, 0.8333333333333370, 0.3750000000000000], [0.6666666666666640, 0.8333333333333370, 0.3750000000000000], [0.1666666666666690, 0.3333333333333370, 0.8750000000000000], [0.6666666666666690, 0.3333333333333370, 0.8750000000000000], [0.1666666666666730, 0.8333333333333370, 0.8750000000000000], [0.6666666666666640, 0.8333333333333370, 0.8750000000000000], [0.3250872549999980, 0.3250872550000020, 0.0000000000000000], [0.8250872549999980, 0.3250872550000020, 0.0000000000000000], [0.3250872550000010, 0.8250872550000020, 0.0000000000000000], [0.8250872550000030, 0.8250872550000020, 0.0000000000000000], [0.3250872549999980, 0.3250872550000020, 0.5000000000000000], [0.8250872549999980, 0.3250872550000020, 0.5000000000000000], [0.3250872550000010, 0.8250872550000020, 0.5000000000000000], [0.8250872550000030, 0.8250872550000020, 0.5000000000000000], [0.3250872550000040, 0.0000000000000000, 0.2500000000000000], [0.8250872550000040, 0.0000000000000000, 0.2500000000000000], [0.3250872550000020, 0.5000000000000000, 0.2500000000000000], [0.8250872550000000, 0.5000000000000000, 0.2500000000000000], [0.3250872550000040, 0.0000000000000000, 0.7500000000000000], [0.8250872550000040, 0.0000000000000000, 0.7500000000000000], [0.3250872550000020, 0.5000000000000000, 0.7500000000000000], [0.8250872550000000, 0.5000000000000000, 0.7500000000000000], [0.3333333333333270, 0.1666666666666630, 0.1250000000000000], [0.8333333333333270, 0.1666666666666630, 0.1250000000000000], [0.3333333333333320, 0.6666666666666630, 0.1250000000000000], [0.8333333333333310, 0.6666666666666630, 0.1250000000000000], [0.3333333333333270, 0.1666666666666630, 0.6250000000000000], [0.8333333333333270, 0.1666666666666630, 0.6250000000000000], [0.3333333333333320, 0.6666666666666630, 0.6250000000000000], [0.8333333333333310, 0.6666666666666630, 0.6250000000000000], [0.1749127449999960, 0.0000000000000000, 0.0000000000000000], [0.6749127449999960, 0.0000000000000000, 0.0000000000000000], [0.1749127449999980, 0.5000000000000000, 0.0000000000000000], [0.6749127450000000, 0.5000000000000000, 0.0000000000000000], [0.1749127449999960, 0.0000000000000000, 0.5000000000000000], [0.6749127449999960, 0.0000000000000000, 0.5000000000000000], [0.1749127449999980, 0.5000000000000000, 0.5000000000000000], [0.6749127450000000, 0.5000000000000000, 0.5000000000000000], ] ) cell = axis, positions, types dataset = spglib.get_symmetry_dataset(cast(SpgCell, cell)) assert dataset is not None perms = compute_sg_permutations( positions, dataset.rotations, dataset.translations, axis, ) perms_stable = compute_sg_permutations_stable( positions, dataset.rotations, dataset.translations, axis, ) np.testing.assert_array_equal(perms, perms_stable) symfc-1.6.0/tests/utils/test_utils_O1.py000066400000000000000000000020031511276756400202620ustar00rootroot00000000000000"""Tests of functions in utils_O1.""" import numpy as np from symfc.spg_reps import SpgRepsBase from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O1 import ( _get_atomic_lat_trans_decompr_indices, get_lat_trans_decompr_indices, ) def structure_bcc(): """Get bcc structure.""" lattice = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 2]]) positions = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) numbers = [1, 1] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) spg_reps = SpgRepsBase(supercell) trans_perms = spg_reps.translation_permutations return supercell, trans_perms supercell, trans_perms = structure_bcc() def test_lat_trans_indices(): """Test lat_trans_indices.""" decompr_idx = get_lat_trans_decompr_indices(trans_perms) np.testing.assert_array_equal(decompr_idx, [0, 1, 2, 0, 1, 2]) atomic_decompr_idx = _get_atomic_lat_trans_decompr_indices(trans_perms) np.testing.assert_array_equal(atomic_decompr_idx, [0, 0]) symfc-1.6.0/tests/utils/test_utils_O2.py000066400000000000000000000266021511276756400202760ustar00rootroot00000000000000"""Tests of matrix manipulating functions for 2nd order force constants.""" import numpy as np from symfc.spg_reps import SpgRepsBase from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O2 import ( _get_atomic_lat_trans_decompr_indices, _get_perm_compr_matrix_reference, get_lat_trans_compr_indices, get_lat_trans_compr_matrix, get_lat_trans_decompr_indices, get_perm_compr_matrix, ) def test_get_perm_compr_matrix(): """Test of get_perm_compr_matrix.""" C1 = get_perm_compr_matrix(8) C2 = _get_perm_compr_matrix_reference(8) np.testing.assert_array_almost_equal((C1 @ C1.T).toarray(), (C2 @ C2.T).toarray()) np.testing.assert_array_almost_equal((C1.T @ C1).toarray(), (C2.T @ C2).toarray()) def test_get_lat_trans_decompr_indices(cell_nacl_111: SymfcAtoms): """Test of get_lat_trans_decompr_indices. The one dimensional array with row-size of compr-mat. Every element indicates column position. """ ref = [ 0, 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, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0, 1, 2, 3, 4, 5, 6, 7, 8, 27, 28, 29, 30, 31, 32, 33, 34, 35, 18, 19, 20, 21, 22, 23, 24, 25, 26, 45, 46, 47, 48, 49, 50, 51, 52, 53, 36, 37, 38, 39, 40, 41, 42, 43, 44, 63, 64, 65, 66, 67, 68, 69, 70, 71, 54, 55, 56, 57, 58, 59, 60, 61, 62, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 27, 28, 29, 30, 31, 32, 33, 34, 35, 18, 19, 20, 21, 22, 23, 24, 25, 26, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0, 1, 2, 3, 4, 5, 6, 7, 8, 63, 64, 65, 66, 67, 68, 69, 70, 71, 54, 55, 56, 57, 58, 59, 60, 61, 62, 45, 46, 47, 48, 49, 50, 51, 52, 53, 36, 37, 38, 39, 40, 41, 42, 43, 44, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 81, 82, 83, 84, 85, 86, 87, 88, 89, 72, 73, 74, 75, 76, 77, 78, 79, 80, 99, 100, 101, 102, 103, 104, 105, 106, 107, 90, 91, 92, 93, 94, 95, 96, 97, 98, 117, 118, 119, 120, 121, 122, 123, 124, 125, 108, 109, 110, 111, 112, 113, 114, 115, 116, 135, 136, 137, 138, 139, 140, 141, 142, 143, 126, 127, 128, 129, 130, 131, 132, 133, 134, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 99, 100, 101, 102, 103, 104, 105, 106, 107, 90, 91, 92, 93, 94, 95, 96, 97, 98, 81, 82, 83, 84, 85, 86, 87, 88, 89, 72, 73, 74, 75, 76, 77, 78, 79, 80, 135, 136, 137, 138, 139, 140, 141, 142, 143, 126, 127, 128, 129, 130, 131, 132, 133, 134, 117, 118, 119, 120, 121, 122, 123, 124, 125, 108, 109, 110, 111, 112, 113, 114, 115, 116, ] trans_perms_ref = np.array( [ [0, 1, 2, 3, 4, 5, 6, 7], [3, 2, 1, 0, 7, 6, 5, 4], [2, 3, 0, 1, 6, 7, 4, 5], [1, 0, 3, 2, 5, 4, 7, 6], ], dtype=int, ) spg_reps = SpgRepsBase(cell_nacl_111) trans_perms = spg_reps.translation_permutations assert trans_perms.shape == (4, 8) check_trans_perms(trans_perms, trans_perms_ref) decompr_idx = get_lat_trans_decompr_indices(trans_perms_ref) np.testing.assert_array_equal(ref, decompr_idx) def test_get_lat_trans_compr_indices(cell_nacl_111: SymfcAtoms): """Test get_lat_trans_compr_indices. The two dimensional array (n_a * N * 9, n_lp) stores NN33 indices where compression matrix elements are non-zero. """ trans_perms_ref = np.array( [ [0, 1, 2, 3, 4, 5, 6, 7], [3, 2, 1, 0, 7, 6, 5, 4], [2, 3, 0, 1, 6, 7, 4, 5], [1, 0, 3, 2, 5, 4, 7, 6], ], dtype=int, ) spg_reps = SpgRepsBase(cell_nacl_111) trans_perms = spg_reps.translation_permutations n_lp, N = trans_perms.shape assert trans_perms.shape == (4, 8) check_trans_perms(trans_perms, trans_perms_ref) decompr_mat = get_lat_trans_decompr_indices(trans_perms_ref) compr_mat = get_lat_trans_compr_matrix(decompr_mat, N, n_lp).toarray() compr_idx = get_lat_trans_compr_indices(trans_perms_ref) for c, elem_idx in enumerate(compr_idx): for r in elem_idx: np.testing.assert_almost_equal(compr_mat[r, c], 0.5) def test_get_atomic_lat_trans_decompr_indices(cell_nacl_111: SymfcAtoms): """Test of get_atomic_lat_trans_decompr_indices. This function is an atomic version of get_lat_trans_decompr_indices. The one dimensional array with row-size of compr-mat. Every element indicates column position. """ ref = [ 0, 1, 2, 3, 4, 5, 6, 7, 1, 0, 3, 2, 5, 4, 7, 6, 2, 3, 0, 1, 6, 7, 4, 5, 3, 2, 1, 0, 7, 6, 5, 4, 8, 9, 10, 11, 12, 13, 14, 15, 9, 8, 11, 10, 13, 12, 15, 14, 10, 11, 8, 9, 14, 15, 12, 13, 11, 10, 9, 8, 15, 14, 13, 12, ] trans_perms_ref = np.array( [ [0, 1, 2, 3, 4, 5, 6, 7], [3, 2, 1, 0, 7, 6, 5, 4], [2, 3, 0, 1, 6, 7, 4, 5], [1, 0, 3, 2, 5, 4, 7, 6], ], dtype=int, ) spg_reps = SpgRepsBase(cell_nacl_111) trans_perms = spg_reps.translation_permutations assert trans_perms.shape == (4, 8) check_trans_perms(trans_perms, trans_perms_ref) atomic_decompr_idx = _get_atomic_lat_trans_decompr_indices(trans_perms_ref) np.testing.assert_array_equal(ref, atomic_decompr_idx) def check_trans_perms(trans_perms, trans_perms_ref): """Check the order of rows of trans_perms. The row order of trans_perms may depend on symmetry finding condition. """ idxs = [] for row in trans_perms: match = np.where(((trans_perms_ref - row) == 0).all(axis=1))[0] assert len(match) == 1 idxs.append(match[0]) np.testing.assert_array_equal(np.sort(idxs), range(len(idxs))) symfc-1.6.0/tests/utils/test_utils_O2_2.py000066400000000000000000000036671511276756400205250ustar00rootroot00000000000000"""Tests of functions in utils_O2.""" import numpy as np import pytest from symfc.spg_reps import SpgRepsO2 from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O2 import ( _get_atomic_lat_trans_decompr_indices, get_compr_coset_projector_O2, get_lat_trans_compr_matrix, get_lat_trans_decompr_indices, ) def structure_bcc(): """Get bcc structure.""" lattice = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 2]]) positions = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) numbers = [1, 1] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) spg_reps = SpgRepsO2(supercell) trans_perms = spg_reps.translation_permutations return supercell, trans_perms, spg_reps supercell, trans_perms, spg_reps = structure_bcc() def test_lat_trans(): """Test lat_trans_indices and lat_trans_compr_matrix.""" decompr_idx = get_lat_trans_decompr_indices(trans_perms) atomic_decompr_idx = _get_atomic_lat_trans_decompr_indices(trans_perms) np.testing.assert_array_equal(atomic_decompr_idx, [0, 1, 1, 0]) decompr_idx_from_atomic = ( atomic_decompr_idx[:, None] * 9 + np.arange(9)[None, :] ).reshape(-1) np.testing.assert_array_equal(decompr_idx, decompr_idx_from_atomic) N, n_lp = trans_perms.shape c_trans = get_lat_trans_compr_matrix(decompr_idx, N, n_lp) row, col = c_trans.nonzero() np.testing.assert_array_equal(decompr_idx, col) np.testing.assert_allclose(c_trans.data, [0.7071067811865475] * len(decompr_idx)) def test_coset_projector_O2(): """Test get_compr_coset_projector_O2.""" atomic_decompr_idx = _get_atomic_lat_trans_decompr_indices(trans_perms) coset = get_compr_coset_projector_O2(spg_reps, atomic_decompr_idx) assert coset.trace() == pytest.approx(2.0) assert np.sum(coset.data) == pytest.approx(6.0) for irow in [1, 2, 3, 5, 6, 7, 10, 11, 12, 14, 15, 16]: assert coset[[irow]].sum() == pytest.approx(0.0) symfc-1.6.0/tests/utils/test_utils_O3.py000066400000000000000000000055161511276756400203000ustar00rootroot00000000000000"""Tests of functions in utils_O3.""" import numpy as np import pytest from symfc.spg_reps import SpgRepsO3 from symfc.utils.cutoff_tools import FCCutoff from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O3 import ( get_atomic_lat_trans_decompr_indices_O3, get_compr_coset_projector_O3, get_lat_trans_compr_matrix_O3, get_lat_trans_decompr_indices_O3, ) def structure_bcc(): """Get bcc structure.""" lattice = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 2]]) positions = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) numbers = [1, 1] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) spg_reps = SpgRepsO3(supercell) trans_perms = spg_reps.translation_permutations return supercell, trans_perms, spg_reps def structure_bcc_rev(): """Get modified bcc structure.""" lattice = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 2]]) positions = np.array([[0, 0, 0], [0.49, 0.5, 0.5]]) numbers = [1, 1] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) spg_reps = SpgRepsO3(supercell) trans_perms = spg_reps.translation_permutations return supercell, trans_perms, spg_reps supercell, trans_perms, spg_reps = structure_bcc() def test_lat_trans(): """Test lat_trans_indices and lat_trans_compr_matrix.""" decompr_idx = get_lat_trans_decompr_indices_O3(trans_perms) atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O3(trans_perms) np.testing.assert_array_equal(atomic_decompr_idx, [0, 1, 2, 3, 3, 2, 1, 0]) decompr_idx_from_atomic = ( atomic_decompr_idx[:, None] * 27 + np.arange(27)[None, :] ).reshape(-1) np.testing.assert_array_equal(decompr_idx, decompr_idx_from_atomic) c_trans = get_lat_trans_compr_matrix_O3(trans_perms) row, col = c_trans.nonzero() np.testing.assert_array_equal(decompr_idx, col) np.testing.assert_allclose(c_trans.data, [0.7071067811865475] * len(decompr_idx)) def test_coset_projector_O3(): """Test get_compr_coset_projector_O3.""" atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O3(trans_perms) coset = get_compr_coset_projector_O3(spg_reps, atomic_decompr_idx) assert coset.trace() == pytest.approx(0.0) assert np.sum(coset.data) == pytest.approx(0.0) supercell_rev, trans_perms_rev, spg_reps_rev = structure_bcc_rev() atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O3(trans_perms_rev) coset = get_compr_coset_projector_O3(spg_reps_rev, atomic_decompr_idx) assert coset.trace() == pytest.approx(16.0) assert np.sum(coset.data) == pytest.approx(0.0) coset = get_compr_coset_projector_O3( spg_reps_rev, atomic_decompr_idx, fc_cutoff=FCCutoff(supercell_rev, cutoff=1), ) assert coset.trace() == pytest.approx(4.0) assert np.sum(coset.data) == pytest.approx(0.0) symfc-1.6.0/tests/utils/test_utils_O4.py000066400000000000000000000027311511276756400202750ustar00rootroot00000000000000"""Tests of functions in utils_O4.""" import numpy as np from symfc.spg_reps import SpgRepsBase from symfc.utils.utils import SymfcAtoms from symfc.utils.utils_O4 import ( get_atomic_lat_trans_decompr_indices_O4, get_lat_trans_compr_matrix_O4, get_lat_trans_decompr_indices_O4, ) def structure_bcc(): """Get bcc structure.""" lattice = np.array([[2, 0, 0], [0, 2, 0], [0, 0, 2]]) positions = np.array([[0, 0, 0], [0.5, 0.5, 0.5]]) numbers = [1, 1] supercell = SymfcAtoms(cell=lattice, scaled_positions=positions, numbers=numbers) spg_reps = SpgRepsBase(supercell) trans_perms = spg_reps.translation_permutations return supercell, trans_perms supercell, trans_perms = structure_bcc() def test_lat_trans(): """Test lat_trans_indices and lat_trans_compr_matrix.""" decompr_idx = get_lat_trans_decompr_indices_O4(trans_perms) atomic_decompr_idx = get_atomic_lat_trans_decompr_indices_O4(trans_perms) np.testing.assert_array_equal( atomic_decompr_idx, [0, 1, 2, 3, 4, 5, 6, 7, 7, 6, 5, 4, 3, 2, 1, 0] ) decompr_idx_from_atomic = ( atomic_decompr_idx[:, None] * 81 + np.arange(81)[None, :] ).reshape(-1) np.testing.assert_array_equal(decompr_idx, decompr_idx_from_atomic) c_trans = get_lat_trans_compr_matrix_O4(trans_perms) row, col = c_trans.nonzero() np.testing.assert_array_equal(decompr_idx, col) np.testing.assert_allclose(c_trans.data, [0.7071067811865475] * len(decompr_idx))