pax_global_header00006660000000000000000000000064151422135560014516gustar00rootroot0000000000000052 comment=4db49ca45ab258c230061fb3f0d29273f7c524ea par2cmdline-turbo-1.4.0/000077500000000000000000000000001514221355600150515ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/.clangd000066400000000000000000000003661514221355600163070ustar00rootroot00000000000000# yaml-language-server: $schema=https://json.schemastore.org/clangd.json CompileFlags: Add: - "--include-directory=." - "--include-directory=.." - "--include=config.h" Diagnostics: MissingIncludes: Strict UnusedIncludes: Strict par2cmdline-turbo-1.4.0/.editorconfig000066400000000000000000000001051514221355600175220ustar00rootroot00000000000000root=true [*] end_of_line = lf indent_style = space indent_size = 2 par2cmdline-turbo-1.4.0/.editorconfig-checker.json000066400000000000000000000006011514221355600220750ustar00rootroot00000000000000{ "Verbose": false, "Debug": false, "IgnoreDefaults": false, "SpacesAfterTabs": false, "NoColor": false, "Exclude": ["\\.par2$"], "AllowedContentTypes": [], "PassedFiles": [], "Disable": { "EndOfLine": false, "Indentation": false, "IndentSize": false, "InsertFinalNewline": false, "TrimTrailingWhitespace": false, "MaxLineLength": false } } par2cmdline-turbo-1.4.0/.gitattributes000066400000000000000000000020071514221355600177430ustar00rootroot00000000000000# Auto detect text files and perform LF normalization # see http://stackoverflow.com/questions/170961/whats-the-best-crlf-carriage-return-line-feed-handling-strategy-with-git * text=auto *.cpp text *.h text *.txt text *.s text *.csproj text merge=union *.sln text=unset merge=union *.vcproj text=unset merge=union *.vcxproj* text=unset merge=union # The VC++ files were already in the repo with DOS line endings # This setting would make git store them internally with LF #*.sln text merge=union eol=crlf # instead, use text=unset to tell git to not do any conversions on # those files # This will only work well in a repo where all the files have Unix line # endings. Having this file in a repo with DOS text files in the tree will # make git see those files as changed (to Unix line endings, regardless of # what they are in your working copy). # Conversely, this will make all new commits of text files commit with Unix # line endings, regardless of what they are in your working copy. par2cmdline-turbo-1.4.0/.github/000077500000000000000000000000001514221355600164115ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/.github/workflows/000077500000000000000000000000001514221355600204465ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/.github/workflows/build-check-freebsd.yml000066400000000000000000000010061514221355600247500ustar00rootroot00000000000000name: par2cmdline CI FreeBSD on: workflow_call: workflow_dispatch: jobs: build-check-freebsd: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Build and test uses: cross-platform-actions/action@v0.32.0 with: operating_system: freebsd version: '15.0' shell: bash run: | sudo pkg install -y autoconf automake libtool ./automake.sh ./configure make make check make distcheckpar2cmdline-turbo-1.4.0/.github/workflows/build-check-linux.yml000066400000000000000000000004631514221355600245030ustar00rootroot00000000000000name: par2cmdline CI Linux on: workflow_call: workflow_dispatch: jobs: build-check-linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Build and test run: | ./automake.sh ./configure make make check make distcheck par2cmdline-turbo-1.4.0/.github/workflows/build-check-macos.yml000066400000000000000000000005501514221355600244430ustar00rootroot00000000000000name: par2cmdline CI MacOS on: workflow_call: workflow_dispatch: jobs: build-check-macos: runs-on: macos-latest steps: - uses: actions/checkout@v6 - name: prepare run: brew install automake - name: Build and test run: | ./automake.sh ./configure make make check make distcheck par2cmdline-turbo-1.4.0/.github/workflows/build-check-windows.yml000066400000000000000000000012001514221355600250240ustar00rootroot00000000000000name: par2cmdline CI windows on: workflow_call: workflow_dispatch: jobs: build-check-windows: runs-on: windows-latest steps: - uses: actions/checkout@v6 - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v2 - name: Build run: msbuild -property:PlatformToolset=ClangCL -property:Configuration=Release -property:Platform=x64 par2cmdline.sln - name: Build Unit Tests run: msbuild -property:Configuration=UnitTests-Release -property:Platform=x64 par2cmdline.sln - name: Run Tests shell: pwsh run: | .\tests\run_tests.ps1 -Par2Binary ".\x64\Release\par2.exe"par2cmdline-turbo-1.4.0/.github/workflows/build-push.yml000066400000000000000000000006311514221355600232450ustar00rootroot00000000000000name: par2cmdline push build on: push: branches: - '**' - '!master' - '!turbo' jobs: build-check-linux: uses: ./.github/workflows/build-check-linux.yml build-check-windows: uses: ./.github/workflows/build-check-windows.yml build-check-macos: uses: ./.github/workflows/build-check-macos.yml build-check-freebsd: uses: ./.github/workflows/build-check-freebsd.yml par2cmdline-turbo-1.4.0/.github/workflows/build-release.yml000066400000000000000000000106201514221355600237050ustar00rootroot00000000000000name: par2cmdline release build on: push: branches: - 'turbo' jobs: build-check-linux: uses: ./.github/workflows/build-check-linux.yml build-check-windows: uses: ./.github/workflows/build-check-windows.yml build-check-macos: uses: ./.github/workflows/build-check-macos.yml build-check-freebsd: uses: ./.github/workflows/build-check-freebsd.yml build-linux: needs: - build-check-linux name: build-linux (${{ matrix.arch }}) strategy: matrix: include: - arch: amd64 configure_host: xcc: - arch: arm64 configure_host: --host=aarch64-linux-gnu xcc: aarch64-linux-gnu- - arch: armhf configure_host: --host=armv7l-linux-gnueabihf xcc: arm-linux-gnueabihf- runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Install cross toolchains if: matrix.arch != 'amd64' run: | sudo apt-get update case "${{ matrix.arch }}" in arm64) sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libc6-dev-arm64-cross ;; armhf) sudo apt-get install -y gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf libc6-dev-armhf-cross ;; esac - name: Build env: CC: ${{ matrix.xcc }}gcc CXX: ${{ matrix.xcc }}g++ LDFLAGS: -static -s run: | ./automake.sh ./configure ${{ matrix.configure_host }} make - name: Upload binary artifact uses: actions/upload-artifact@v6 with: name: par2cmdline-turbo-linux-${{ matrix.arch }} path: | par2 retention-days: 5 build-windows: needs: - build-check-windows name: build-windows (${{ matrix.name }}) strategy: matrix: include: - platform: x64 name: x64 - platform: ARM64 name: arm64 runs-on: windows-latest steps: - uses: actions/checkout@v6 - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v2 - name: Build run: msbuild -property:PlatformToolset=ClangCL -property:Configuration=Release -property:Platform=${{ matrix.platform }} par2cmdline.sln - name: Upload binary artifact uses: actions/upload-artifact@v6 with: name: par2cmdline-turbo-win-${{ matrix.name }} path: | ${{ matrix.platform }}/Release/par2.exe retention-days: 5 build-macos: needs: - build-check-macos name: build-macos (${{ matrix.arch }}) strategy: matrix: include: - arch: amd64 xcxxflags: -arch x86_64 -mmacosx-version-min=10.13 configure_host: --host=x86_64-apple-darwin - arch: arm64 xcxxflags: -mmacosx-version-min=11.0 - arch: universal xcxxflags: -arch x86_64 -arch arm64 -mmacosx-version-min=10.13 runs-on: macos-latest steps: - uses: actions/checkout@v6 - name: prepare run: brew install automake - name: Build env: CFLAGS: ${{ matrix.xcxxflags }} CXXFLAGS: ${{ matrix.xcxxflags }} run: | ./automake.sh ./configure ${{ matrix.configure_host }} make strip par2 - name: Upload binary artifact uses: actions/upload-artifact@v6 with: name: par2cmdline-turbo-macos-${{ matrix.arch }} path: | par2 retention-days: 5 build-freebsd: needs: - build-check-freebsd name: build-freebsd (${{ matrix.arch }}) strategy: matrix: include: - arch: x86-64 artifact_arch: amd64 - arch: arm64 artifact_arch: aarch64 runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Build on FreeBSD uses: cross-platform-actions/action@v0.32.0 env: LDFLAGS: -static -s with: operating_system: freebsd version: '15.0' architecture: ${{ matrix.arch }} environment_variables: LDFLAGS shell: sh run: | sudo pkg install -y autoconf automake libtool ./automake.sh ./configure make strip par2 - name: Upload binary artifact uses: actions/upload-artifact@v6 with: name: par2cmdline-turbo-freebsd-${{ matrix.artifact_arch }} path: | par2 retention-days: 5 par2cmdline-turbo-1.4.0/.gitignore000066400000000000000000000013461514221355600170450ustar00rootroot00000000000000# when developing and you really want to clean everything up # run git clean -fdx # thx @spider-mario # compilation output par2 libpar2.a libparpar*.a *.o .deps/ par2.exe tests/*.exe # test output *.trs *.log tests/runtest* # unit test binaries tests/*_test tests/.dirstamp # autotools output aclocal.m4 autom4te.cache/ compile test-driver Makefile.in Makefile config.h config.status configure stamp-h stamp-h1 src/.dirstamp parpar/*/.dirstamp install-sh missing ar-lib # windows Release/ Debug/ *.old *.vcxproj.user *.suo .vs/ test.* # Linux perf record output perf.* # gcc -fprofile-use *.gcda # eclipse stuff you get when you point it at a Makefile project .cproject .project .settings # emacs files *~ .#* \#*# .~lock.*# .vscode par2cmdline-turbo-1.4.0/AUTHORS000066400000000000000000000003131514221355600161160ustar00rootroot00000000000000Peter Brian Clements Marcel Partap Ike Devolder Jussi Kansanen Michael Nahas par2cmdline-turbo-1.4.0/COPYING000066400000000000000000000431001514221355600161020ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Moe Ghoul, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. par2cmdline-turbo-1.4.0/ChangeLog000066400000000000000000000535731514221355600166400ustar00rootroot0000000000000006 February 2026 Ike Devolder * release: 1.1.1 Release: * #264: Version 1.1.1 (PR #267 version 1.1.1) Issues: * #248: When repairing a file, if there is not enough disk space, the final error message displays garbled characters. (PR #263 fix garbled errormessage on windows) * #253: Why does running par2 in the command line environment forcibly switch the code page to CP65001? PRs: * #261: Cleanup/std namespace search replace oops (https://github.com/Parchive/par2cmdline/pull/242#pullrequestreview-3731414581) * #262: Migrate Windows unit tests to Visual C++ projects (https://github.com/Parchive/par2cmdline/pull/256#issuecomment-3827352914) * #265: add .clangd file for additional editor lsp support * #266: Integer definitions consistency 27 Januari 2026 Ike Devolder * release: 1.1.0 Release: * #215: Version 1.1.0 (PR #260 Version 1.1.0) Issues: * #216: [1.0.0] amd64 binary tests/utf8_test included in release tarball * #225: restore support for older macOS * #220: BUG: : should be an illegal character only on HFS filesystems * #181: Lack of thread guards in DiskFile (PR #237 Add thread guards in diskfile) * #228: How can I know the default values without looking at the source code, or is --dry-run command available? (PR #238 show defaults in help output and man) * #230: Consider adding manual page symlinks for par2create, par2repair and par2verify (PR #239 create symlinks for the long form manpages as well) * #187: global-buffer-overflow by default in unit test (diskfile_test) (PR #240 calculate size in diskfile unittest to avoid bufferoverflow) * #184: rename only repair mode (quickly skip non matching files) (PR #185 POC for a rename only mode and #243 Issue 184) * #139: test failure because testdata archives preserve ownership info (PR #249 repack test tarballs with tar --owner=0 --group=0) * #231: par2 create cannot handle non-ASCII directory names (e.g. ę) (PR #250 Windows unicode issues) * #233: windows - rename using long path notation (PR #250 Windows unicode issues) * #167: Can not create file in root path on Windows. (PR #251 diskfile CreateParentDirectory, take rootpath behaviour into account) * #244: Regression in V1.0.0 for symbolic links to files. (PR #254 Fix symbolic links as primary file.) PRs: * #213: Strip MacOS binary * #214: Add armhf Linux build * #217: fix manpage typ0 * #218: tests/utf8_test is generated before running tests, not needed in dist * #221: Replace deprecated sprintf with snprintf * #223: Remove unused code * #224: Fix README not found * #226: Reduce the minimum supported macOS version and add a universal macOS binary * #229: Update COPYING with the latest GPL v2 license * #236: Fix memory leak. * #241: Build warnings * #245: Cleanup unused * #246: remove define of stat or _stat * #247: add freebsd ci check * #252: remove obsolete autoconf macro AC_HEADER_STDC * #255: use native ubuntu cross building instead of gha-ubuntu-cross * #257: add dependabot check for github actions updates * #256: Windows integration testing * #258: Bump actions/upload-artifact from 4 to 6 * #259: Bump actions/checkout from 4 to 6 Skipped: * #101: properly detect openmp on *BSD * #108: Trying to cross-compile with openmp support * #122: How to detect wrong data files against par2 files? * #115: Crashes with "std::bad_alloc" when running more than one instance * #154: Par2cmdline allocation failure. 30 April 2025 Ike Devolder * release: 1.0.0 * release artifacts are all built with github actions * linux static x86_64 and arm64 * windows x86_64 and arm64 * macos x86_64 and arm64 (experimental, could not test) * Special thanks to everyone for waiting over 5 years for a new release Support: * #146: build fails in Cygwin * #140: A potential Data Race * #151: Understanding par2cmdline performance * #129: Understanding The Par2 Spec and par2cmdline * #163: performance with hundreds of small files * #166: A better error correcting code * #157: Recover impossible without a .par2 "main" file? * #172: Thread count option not shown on Arm based Mac version * #178: Increase block count limit 32768 * #112: Behaviour with insufficient read permissions * #79: Help to create a compatible implementation! * #171: Flag for automatic Blocksize in commandline * #121: Repair-Files created amount to size twice as high as expected from redundancy * #175: New release? Issues: * #141: syntax error in tests (fixed with PR #142) * #143: test failure in unit_tests on s390x, powerpc (fixed with PR #144) * #145: Core dump on single letter file name (duplicate: #192, #165) (fixed with PR #149) * #41: prepare code and header files for libpar2 (e4e449f97ff0758029d73af58da66b0f33619741) * #169: Windows version always recurse into subdirectories (duplicate: #173, #111) (fixed with PR #182) * #193: config.guess timestamp = 2015-03-04 ... time for a newer version? (83bb2495c906660fad3a4f85a72f8ce0de05445b) * #190: Cannot recover one-bit flip (duplicate #156?) (fixed with PR #179) * #120: exit codes from par2 executable not documented? (improved with PR #199) * #126: Problem with PACKED little-endian structs (4a4179f1e42bc11bbcc9d24554690d3bc5c31b38) * #150: Recurse option (-R) does not include files in current directory (more documenation) * #128: Problem with empty (0 Bytes) files. (workaround via PR #200) * #152: Fail earlier during creation if the .par2 file already exists (basic check in PR #203) * #113: Hang with -n=256 (limited in PR #204) * #205: Could not create "./Disk 1 (a-m)/.PAR2 (Recovery Disk 1)/disk1.vol34529+00000.par2": File already exists. (6c2fa05a16bd604e54b46a5546c3acb64eb9fed4) * #189: Properly handle UTF8 and long-paths on Windows (implemented by dnzbk for nzbget, rebased in PR #202) * #186: It would be nice if permissions were checked eariler when creating parchive. (basic check in PR #210) * #164: need option to disable following symlinks on "-R" (fixed in PR #211) * #208: Version 1.0.0 PRs: * #148: fix: enforce -qq silent * #153: Install relative symlinks instead of absolute ones * #161: Allow Creating Files With Over 100% Redundancy (new PR from #158) * #162: Minor cleanup * #168: fix win32 directory recursion * #179: Avoid copying back memory in FileChecksummer when data is valid * #177: CRC32/MD5 optimisations * #182: enable Recurse flag on Windows OS * #198: enable Recurse flag on Windows OS * #199: add exitcodes to manfile * #201: Issue 150 recursive no files from rootpath * #197: Updates from turbo * #200: Issue 128 * #203: Quick bail if the base par2 file already exists * #204: Issue 113 * #206: github workflows * #207: When number of recovery files is set, you get "File already exists" * #202: Issue 189 windows utf8 * #209: Workflow release * #210: Add simple "permissions" check while creating * #211: diskfile, replace stat use with lstat Skipped: * #7: command line flags can not be chained * #91: make use of appveyor for windows * #191: Update diskfile.cpp * #155: Travis-ci: added support for ppc64le * #194: Avoid copying back memory in FileChecksummer when data is valid (superseeded by #202) * #176: Added support for GPU acceleration (CUDA) on recovery file creation. 09 February 2020 Ike Devolder * release: 0.8.1 * Serious rework of internals by Michael Nahas * Added unittests by Michael Nahas * Added support for static libpar2 11 December 2017 Ike Devolder * release: 0.8.0 * improved parallelisation on input files 08 September 2017 Ike Devolder * release: 0.7.4 * performance improvement when not using quiet mode 25 June 2017 Ike Devolder * release: 0.7.3 * fixed critical issue on windows where created parfiles were corrupt 03 June 2017 Ike Devolder * release: 0.7.2 * fixed tests on NetBSD, #102 * fixed failing tests when dir contains whitespace, #103 * added automake.sh to the files, #104 * changed some flags related to windows build, #94 * cleaned up compiler warnings for mingw build 19 May 2017 Ike Devolder * release: 0.7.1 * fixed basepath issues #93 * made distcheck behaviour consistent with different --srcdir #96 * properly package distribution package #98 * cross compile for windows to get redistributable exe #94 * change distribution format to tar.gz and tar.bz2 for unix and keep zip for windows #97 17 April 2017 Ike Devolder * release: 0.7.0 * Merged OpenMP version and so enabled multithreading * added -t flag to set the number of threads (all possible threads used by default) * Added -B flag to set the basepath for difficult to guess situations * bugfixes 27 February 2014 Ike Devolder * release: 0.6.5 * fix blocksize calculation 25 February 2014 JCF Ploemen * lintian warning spelling fix 7 February 2014 Ike Devolder * add manfile for par2 29 January 2014 Jan van Haarst * README in Markdown 21 January 2014 Ike Devolder * add spelling fixes to cmdline output 18 January 2014 Jan van Haarst * fixed some spelling 6 January 2014 Ike Devolder * simplify FindFiles 4 January 2014 Ike Devolder * release: 0.6.4 * when directory is removed, recreate * add test to verify directory behaviour * add test with valgrind 16 December 2013 Ike Devolder * allow verification of files even with duplicates in parfiles 15 December 2013 Ike Devolder * avoid adding of duplicates 14 December 2013 Ike Devolder * release: 0.6.3 * rework original tests so parallelisation is no issue * add test8 which makes sure the paths stored are relative * add test9 rename wrongly named file, passed to par2 result is correctly named files after repair 13 Decebmer 2013 Ike Devolder * move tests in separate folder, tests will be extended and otherwise eventually clutter the source too much 9 December 2013 Ike Devolder * rework parfilename handling + it fixes passing par filename without extension for repair 7 December 2013 Ike Devolder * release: 0.6.2 * basepath was not set correctly in restoring of 'old' way 5 December 2013 Ike Devolder * release: 0.6.1 * show usage / version / copyright on request * restore 'old' way of creating make -a optional 30 November 2013 Ike Devolder * release: 0.6.0 * test8: windows generated recovery files with subdir * windows recurses in . and .. which leads to deadlock so don't recurse in folders starting with . on windows * when recursion happens make sure windows can recover with unix created par2 files * fixed fc -> fd typo in windows part of diskfile * FTBFS fix on GNU/kFreeBSD (by Cyril Brulebois) * fixed non quiet output when creating par2 * Applied fix preventing a stack overflow (by Robert Schneider) 14 October 2013 Ike Devolder * Implemented recursion * NOTE: only for unix like systems * Windows can be done i guess but i have no access to windows machines 16 June 2013 Ike Devolder * Implemented initial subdir handling * added subdir tests 13 June 2013 Ike Devolder * Fixed failing testsuite 9 December 2012 Ike Devolder * rework purging of par(2) files because of possible segfaults and other inconsistencies 4 December 2012 Ike Devolder * fix purge option so it removes the par files when there is no repair needed * add purge option to verify so when there is no repair needed the par2 files are removed when the purge option is given 30 November 2012 Ike Devolder * add purge option remove backup files (mostly .1) and par files on request **USE AT YOUR OWN RISK * purge option available in par1 and par2 repair * fix memory leak when using par1 repair 30 January 2012 Ike Devolder * add inlining performance improvement of Gerard Putter * release: 0.5.4 19 January 2012 Ike Devolder * fix wrongly created par2 files in quiet mode this was related to the -q -qq fix * release: 0.5.3 17 January 2012 Ike Devolder * fix deadlock condition when repairing and filepath is longer than _MAX_PATH * increase _MAX_PATH, 255 is no longer of these days * release: 0.5.2 16 January 2012 Ike Devolder * fixup 0.5 version display * "par2 create" dumps core with -q or -qq, but non-quiet mode works fine FIXED * removed automake files * updated copyrights * release: 0.5.1 12 December 2011 Marcel Partap * Reanimate par2cmdline development on new github repository * Merge available distro patches since 2004 0.4 release back upstream * Release: Version 0.5 ;) 3 January 2005 Peter B Clements * par2creatorsourcefile.cpp: "offset" must be updated when computing file and block hashes even if progress is not being displayed. 23 June 2004 Peter B Clements * diskfile.cpp: Changed DiskFile::GetFileSize for WIN32 so that it uses _stati64() to get the size of a file. * par2creatorsourcefile.cpp: In UpdateHashes() the test that adjusts the length of the last block of a file when before updating the file hash was incorrect. 17 June 2004 Peter B Clements * par1repairer.cpp, par2repairer.cpp: Report empty files when scanning for data. 22 April 2004 Peter B Clements * README: Updated to include new commandline options. * par2cmdline.vcproj: Update VS .NET project file with new version number. * Released: Version 0.4. 15 April 2004 Peter B Clements * par2cmdline.sln, par2cmdline.vcproj: Updated. * commandline.cpp, commandline.h, par1repairer.cpp, par1repairer.h, par2cmdline.cpp, par2cmdline.h, par2creator.cpp, par2creator.h, par2creatorsourcefile.cpp, par2creatorsourcefile.h, par2repairer.cpp, par2repairer.h, reedsolomon.h, par2cmdline.h: Handle -v and -q options from the command line to change the amount of messages displayed. 14 April 2004 Peter B Clements * commandline.h, commandline.cpp: Add -c option to allow the number of recovery blocks to be directly specified rather than indirectly via the -r option. Add -l option to allow the recovery file size to be limited so that the largest will have just enough recovery data to reconstruct the largest source file. * par2creator.h, par2creator.cpp: Use the recovery block count specified via the command line instead of the redundancy % if given. Allocate the sizes of recovery files so the are limited if requested. 12 April 2004 Peter B Clements * config.h.in, configure, configure.ac: Detect what defines are required to enable 64 bit file system support and whether or not fseeko() is available. * diskfile.cpp: Use fseeko and 64 bit file operations if they are available. * par2cmdline.h: Change order of include files. * par2repairer.cpp: When verifying files with long filenames, display a shorter version of the filename. * datablock.h: Change DataBlock::GetDiskFile so that in returns a non const DiskFile. * par2repairer.cpp, par2creator.cpp: Only keep source files open when they are actually needed. This should allow par2 creation and repair to work with large file sets on OS's with a limit on the number of files that can be open simultaneously. * Makefile.in, aclocal.m4, config.guess, config.sub, configure, depcomp, install-sh, missing, mkinstalldirs: Updated Automake to version 1.8.3 and Autoconf to version 2.59. * configure.ac, config.h.in, configure: Check for getopt() and getopt_long(). 19 August 2003 Peter B Clements * Updated par2creator.cpp: When creating par2 files the wrong number of bytes would be reported as written to disk. 13 August 2003 Peter B Clements * Updated par2cmdline.h: Recorrected spelling of STDC_HEADERS! * Updated par1repairersourcefile.cpp, par2repairersourcefile.cpp: Don't treat ':' as a path separator on non Windows platforms. 2 August 2003 Peter B Clements * Updated par1fileformat.h, par2fileformat.h: Use memcmp when comparing MAGIC strings and PACKETTYPE strings. * Updated par2repairer.cpp: When a good packet is found after bad data, use memcpy to copy the packet header. Don't attempt to get the block count directly from the verification packet (which might be missing). * Updated par2repairersourcefile.cpp: Add function to set the block count when the verification packet is missing. 1 August 2003 Peter B Clements * Updated par2cmdline.h: Included . 31 July 2003 Peter B Clements * Updated reedsolomon.h: Added debugging code. 29 July 2003 Peter B Clements * Updated galois.h: Use typename when refering to a typedef in another class. * Updated par1repairer.cpp: Cast size of fileentry in memcpy. * Updated par2repairersourcefile.h: Add function to set the block count for a file when the verification packet is missing. 25 July 2003 Peter B Clements * Updated par2cmdline.h: Correct spelling of STDC_HEADERS. 16 July 2003 Peter B Clements * Release: Version 0.3. 15 July 2003 Peter B Clements * Added config.guess, config.sub: Autoconf files. * Updated configure, Makefile.in: Updated by Autoconf. * Updated configure.ac: Changed par2cmdline version number. Added call to AC_CANONICAL_HOST. * Updated par2cmdline.vcproj: Updated version number. 3 July 2003 Peter B Clements * Updated aclocal.m4, depcomp, INSTALL, install-sh, mkinstalldirs: Upgrade Autoconf to version 1.75 from 1.6.3. * Updated Makefile.am: Changed CXXFLAGS to AM_CXXFLAGS. 24 June 2003 Peter B Clements * Updated commandline.cpp, commandline.h: Added "redundancyseet" member to record whether or not the value of "redundancy" has been specified so that 0% redundancy is permissible. * Updated par2creator.cpp: Detect situation where no recovery blocks are being created and skip related code sections. 14 June 2003 Peter B Clements * Updated galois.h: Corrected bug in the initialisation of log[0] in GaloisTable. 11 June 2003 Peter B Clements * Updated par1repair.cpp, par1repairer.h: Detect buggy version of smartpar which creates PAR1 files with invalid 16k hash values, Change alignment of temporary buffer used for PAR1FILEENTRYs to 8 bytes. 7 June 2003 Peter B Clements * Update par2cmdline.h: Added header include. 3 June 2003 Peter B Clements * Updated verificationhashtable.h: Fixed bug where blocks of data that have the same crc and hash would not be correctly recognised. 26 May 2003 Peter B Clements * Release: Version 0.2. * Added config.h.in, configure, configure.ac, depcomp, missing, mkinstalldirs, stamp-h.in: Autoconf configuration files. * Added NEWS * Added par1fileformat.h, par1fileformat.cpp: Specifies the layout of PAR 1.0 files. * Added par1repairer.h, par1repairer.cpp: Encapsulates the details of repairing using PAR 1.0 files. * Added par1repairersourcefile.h, par1repairersourcefile.cpp: Stores details of a source file. * Added test1, test2, test3, test4, test5, test6, testdata.tar.gz: Test files for "make check". * Changed commandline.cpp, commandline.h: Add "version" member and set it according to whether the recovery file is a .PAR file or a .PAR2 file. Rename "par2filename" member to "parfilename". * Changed creatorpacket.cpp: Made "string creator" a local variable in CreatorPacket::Create instead of a global. Commented out code that does nothing. * Changed criticalpacket.h: Corrected bug in CriticalPacketEntry::operator= which failed to return *this. * Changed descriptionpacket.cpp: Commented out code which does nothing. * Changed diskfile.cpp: Updated wildcard matching code to permit multiple "?" in wildcard. Adjusted the list of characters that are accepted in filenames to include all with bit 7 set and also spaces. Removed restrictions on many other permitted characters. * Changed diskfile.h: Removed cygwin and linux ifdefs which are now handled by autoconf. * Changed galois.cpp: Move the constructors for GaloisTable and GaloisLongMultiplyTable to galois.h. * Changed galois.h: Changed GaloisTable, Galois, and GaloisLongMultipleTable into templates. Corrected bug in Galois::pow and Galois::operator^ which incorrectly returned 0 for x^0 when it should always return 1. Added Galois8 and Galois16 typedefs for PAR1 and PAR2. * Changed letype.h: Added leu16 type for use in PAR1 processing. * Changed mainpacket.cpp: Commented out code which does nothing. * Changed md5.cpp: Adjusted ROL macro to include masking to correct for bug on Alpha CPUs. Added operator<<() and print() to MD5Hash. * Changed md5.h: Added copy and assignment operators for MD5Hash. * Changed par2cmdline.cpp: Made "string version" a local variable instead of global. Use Par1Repairer or Par2Repaire as appropriate when verifying or repairing PAR1 and PAR2 files. * Changed par2cmdline.h: Adjusted to conditionally include headers and to define various types based on the autoconf configuration. * Changed par2cmdline.sln, par2cmdline.vcproj: Updated. * Changed par2creator.cpp: Called Commandline::GetParFilename instead of CommandLine::GetPar2Filename. * Changed par2creator.h: Redifine rs as ReedSolomon. * Changed par2creatorsourcefile.cpp: Comment out code which does nothing. Added typecasts between 32bit and 64bit values. * Changed par2fileformat.cpp: Adjusted initialisation code. * Changed par2fileformat.h: Use packed attribute for gnu compilers. * Changed par2repairer.cpp: Get filename using CommandLine::GetParFilename. * Changed par2repairer.h: Redefine rs as ReedSolomon. * Changed par2repairersourcefile: Add typecast from 32bit to 64bit. * Changed README: Update details of how to compile the source code using the configure script. * Changed recoverypacket.cpp: Commented out code which does nothing. * Changed ReedSolomon.cpp: Move ReedSolomon constructor to ReedSolomon.h. Created template specialisations for Galois8 and Galois16 for SetInput, SetOutput, and Process. * Changed ReedSolomon.h: Converted ReedSolomon to a template. * Changed verificationhashtable.cpp: Removed unused code. * Changed verificationpacket.cpp: Commented out code that does nothing. 7 May 2003 Peter B Clements * Version 0.1: Initial release. par2cmdline-turbo-1.4.0/INSTALL000066400000000000000000000377771514221355600161270ustar00rootroot00000000000000Installation Instructions ************************* Basic Installation ================== The following shell commands: test -f configure || ./bootstrap ./configure make make install should configure, build, and install this package. The first line, which bootstraps, is intended for developers; when building from distribution tarballs it does nothing and can be skipped. The following more-detailed instructions are generic; see the ‘README.md’ file for instructions specific to this package. Some packages provide this ‘INSTALL’ file but do not implement all of the features documented below. The lack of an optional feature in a given package is not necessarily a bug. More recommendations for GNU packages can be found in the GNU Coding Standards. Many packages have scripts meant for developers instead of ordinary builders, as they may use developer tools that are less commonly installed, or they may access the network, which has privacy implications. If the ‘bootstrap’ shell script exists, it attempts to build the ‘configure’ shell script and related files, possibly using developer tools or the network. Because the output of ‘bootstrap’ is system-independent, it is normally run by a package developer so that its output can be put into the distribution tarball and ordinary builders and users need not run ‘bootstrap’. Some packages have commands like ‘./autopull.sh’ and ‘./autogen.sh’ that you can run instead of ‘./bootstrap’, for more fine-grained control over bootstrapping. The ‘configure’ shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a ‘Makefile’ in each directory of the package. It may also create one or more ‘.h’ files containing system-dependent definitions. Finally, it creates a shell script ‘config.status’ that you can run in the future to recreate the current configuration, and a file ‘config.log’ containing output useful for debugging ‘configure’. It can also use an optional file (typically called ‘config.cache’ and enabled with ‘--cache-file=config.cache’ or simply ‘-C’) that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how ‘configure’ could check whether to do them, and mail diffs or instructions to the address given in the ‘README.md’ so they can be considered for the next release. If you are using the cache, and at some point ‘config.cache’ contains results you don’t want to keep, you may remove or edit it. The ‘autoconf’ program generates ‘configure’ from the file ‘configure.ac’. Normally you should edit ‘configure.ac’ instead of editing ‘configure’ directly. The simplest way to compile this package is: 1. ‘cd’ to the directory containing the package’s source code. 2. If this is a developer checkout and file ‘configure’ does not yet exist, type ‘./bootstrap’ to create it. You may need special developer tools and network access to bootstrap, and the network access may have privacy implications. 3. Type ‘./configure’ to configure the package for your system. This might take a while. While running, ‘configure’ prints messages telling which features it is checking for. 4. Type ‘make’ to compile the package. 5. Optionally, type ‘make check’ to run any self-tests that come with the package, generally using the just-built uninstalled binaries. 6. Type ‘make install’ to install the programs and any data files and documentation. When installing into a prefix owned by root, it is recommended that the package be configured and built as a regular user, and only the ‘make install’ phase executed with root privileges. 7. Optionally, type ‘make installcheck’ to repeat any self-tests, but this time using the binaries in their final installed location. This target does not install anything. Running this target as a regular user, particularly if the prior ‘make install’ required root privileges, verifies that the installation completed correctly. 8. You can remove the program binaries and object files from the source code directory by typing ‘make clean’. To also remove the files that ‘configure’ created (so you can compile the package for a different kind of computer), type ‘make distclean’. There is also a ‘make maintainer-clean’ target, but that is intended mainly for the package’s developers. If you use it, you may have to bootstrap again. 9. If the package follows the GNU Coding Standards, you can type ‘make uninstall’ to remove the installed files. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the ‘configure’ script does not know about. Run ‘./configure --help’ for details on some of the pertinent environment variables. You can give ‘configure’ initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=gcc CFLAGS=-g LIBS=-lposix See “Defining Variables” for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each system in their own directory. To do this, you can use GNU ‘make’. ‘cd’ to the directory where you want the object files and executables to go and run the ‘configure’ script. ‘configure’ automatically checks for the source code in the directory that ‘configure’ is in and in ‘..’. This is known as a “VPATH” build. With a non-GNU ‘make’, it is safer to compile the package for one system at a time in the source code directory. After you have installed the package for one system, use ‘make distclean’ before reconfiguring for another system. Some platforms, notably macOS, support “fat” or “universal” binaries, where a single binary can execute on different architectures. On these platforms you can configure and compile just once, with options specific to that platform. Installation Names ================== By default, ‘make install’ installs the package’s commands under ‘/usr/local/bin’, include files under ‘/usr/local/include’, etc. You can specify an installation prefix other than ‘/usr/local’ by giving ‘configure’ the option ‘--prefix=PREFIX’, where PREFIX must be an absolute file name. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option ‘--exec-prefix=PREFIX’ to ‘configure’, the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like ‘--bindir=DIR’ to specify different values for particular kinds of files. Run ‘configure --help’ for a list of the directories you can set and what kinds of files go in them. In general, the default for these options is expressed in terms of ‘${prefix}’, so that specifying just ‘--prefix’ will affect all of the other directory specifications that were not explicitly provided. The most portable way to affect installation locations is to pass the correct locations to ‘configure’; however, many packages provide one or both of the following shortcuts of passing variable assignments to the ‘make install’ command line to change installation locations without having to reconfigure or recompile. The first method involves providing an override variable for each affected directory. For example, ‘make install prefix=/alternate/directory’ will choose an alternate location for all directory configuration variables that were expressed in terms of ‘${prefix}’. Any directories that were specified during ‘configure’, but not in terms of ‘${prefix}’, must each be overridden at install time for the entire installation to be relocated. The approach of makefile variable overrides for each directory variable is required by the GNU Coding Standards, and ideally causes no recompilation. However, some platforms have known limitations with the semantics of shared libraries that end up requiring recompilation when using this method, particularly noticeable in packages that use GNU Libtool. The second method involves providing the ‘DESTDIR’ variable. For example, ‘make install DESTDIR=/alternate/directory’ will prepend ‘/alternate/directory’ before all installation names. The approach of ‘DESTDIR’ overrides is not required by the GNU Coding Standards, and does not work on platforms that have drive letters. On the other hand, it does better at avoiding recompilation issues, and works well even when some directory options were not specified in terms of ‘${prefix}’ at ‘configure’ time. Optional Features ================= If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving ‘configure’ the option ‘--program-prefix=PREFIX’ or ‘--program-suffix=SUFFIX’. Some packages pay attention to ‘--enable-FEATURE’ and ‘--disable-FEATURE’ options to ‘configure’, where FEATURE indicates an optional part of the package. They may also pay attention to ‘--with-PACKAGE’ and ‘--without-PACKAGE’ options, where PACKAGE is something like ‘gnu-ld’. ‘./configure --help’ should mention the ‘--enable-...’ and ‘--with-...’ options that the package recognizes. Some packages offer the ability to configure how verbose the execution of ‘make’ will be. For these packages, running ‘./configure --enable-silent-rules’ sets the default to minimal output, which can be overridden with ‘make V=1’; while running ‘./configure --disable-silent-rules’ sets the default to verbose, which can be overridden with ‘make V=0’. Specifying a System Type ======================== By default ‘configure’ builds for the current system. To create binaries that can run on a different system type, specify a ‘--host=TYPE’ option along with compiler variables that specify how to generate object code for TYPE. For example, to create binaries intended to run on a 64-bit ARM processor: ./configure --host=aarch64-linux-gnu \ CC=aarch64-linux-gnu-gcc \ CXX=aarch64-linux-gnu-g++ If done on a machine that can execute these binaries (e.g., via ‘qemu-aarch64’, ‘$QEMU_LD_PREFIX’, and Linux’s ‘binfmt_misc’ capability), the build behaves like a native build. Otherwise it is a cross-build: ‘configure’ will make cross-compilation guesses instead of running test programs, and ‘make check’ will not work. A system type can either be a short name like ‘mingw64’, or a canonical name like ‘x86_64-pc-linux-gnu’. Canonical names have the form CPU-COMPANY-SYSTEM where SYSTEM is either OS or KERNEL-OS. To canonicalize and validate a system type, you can run the command ‘config.sub’, which is often squirreled away in a subdirectory like ‘build-aux’. For example: $ build-aux/config.sub arm64-linux aarch64-unknown-linux-gnu $ build-aux/config.sub riscv-lnx Invalid configuration 'riscv-lnx': OS 'lnx' not recognized You can look at the ‘config.sub’ file to see which types are recognized. If the file is absent, this package does not need the system type. If ‘configure’ fails with the diagnostic “cannot guess build type”. ‘config.sub’ did not recognize your system’s type. In this case, first fetch the newest versions of these files from the GNU config package (https://savannah.gnu.org/projects/config). If that fixes things, please report it to the maintainers of the package containing ‘configure’. Otherwise, you can try the configure option ‘--build=TYPE’ where TYPE comes close to your system type; also, please report the problem to . For more details about configuring system types, see the Autoconf documentation. Sharing Defaults ================ If you want to set default values for ‘configure’ scripts to share, you can create a site shell script called ‘config.site’ that gives default values for variables like ‘CC’, ‘cache_file’, and ‘prefix’. ‘configure’ looks for ‘PREFIX/share/config.site’ if it exists, then ‘PREFIX/etc/config.site’ if it exists. Or, you can set the ‘CONFIG_SITE’ environment variable to the location of the site script. A warning: not all ‘configure’ scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to ‘configure’. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the ‘configure’ command line, using ‘VAR=value’. For example: ./configure CC=/usr/local2/bin/gcc causes the specified ‘gcc’ to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for ‘CONFIG_SHELL’ due to an Autoconf limitation. Until the limitation is lifted, you can use this workaround: CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash ‘configure’ Invocation ====================== ‘configure’ recognizes the following options to control how it operates. ‘--help’ ‘-h’ Print a summary of all of the options to ‘configure’, and exit. ‘--help=short’ ‘--help=recursive’ Print a summary of the options unique to this package’s ‘configure’, and exit. The ‘short’ variant lists options used only in the top level, while the ‘recursive’ variant lists options also present in any nested packages. ‘--version’ ‘-V’ Print the version of Autoconf used to generate the ‘configure’ script, and exit. ‘--cache-file=FILE’ Enable the cache: use and save the results of the tests in FILE, traditionally ‘config.cache’. FILE defaults to ‘/dev/null’ to disable caching. ‘--config-cache’ ‘-C’ Alias for ‘--cache-file=config.cache’. ‘--srcdir=DIR’ Look for the package’s source code in directory DIR. Usually ‘configure’ can determine that directory automatically. ‘--prefix=DIR’ Use DIR as the installation prefix. See “Installation Names” for more details, including other options available for fine-tuning the installation locations. ‘--host=TYPE’ Build binaries for system TYPE. See “Specifying a System Type”. ‘--enable-FEATURE’ ‘--disable-FEATURE’ Enable or disable the optional FEATURE. See “Optional Features”. ‘--with-PACKAGE’ ‘--without-PACKAGE’ Use or omit PACKAGE when building. See “Optional Features”. ‘--quiet’ ‘--silent’ ‘-q’ Do not print messages saying which checks are being made. To suppress all normal output, redirect it to ‘/dev/null’ (any error messages will still be shown). ‘--no-create’ ‘-n’ Run the configure checks, but stop before creating any output files. ‘configure’ also recognizes several environment variables, and accepts some other, less widely useful, options. Run ‘configure --help’ for more details. Copyright notice ================ Copyright © 1994–1996, 1999–2002, 2004–2017, 2020–2024 Free Software Foundation, Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without warranty of any kind. par2cmdline-turbo-1.4.0/Makefile.am000066400000000000000000000451511514221355600171130ustar00rootroot00000000000000## This file is part of par2cmdline (a PAR 2.0 compatible file verification and ## repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. ## ## Copyright (c) 2003 Peter Brian Clements ## Copyright (c) 2019 Michael D. Nahas ## ## par2cmdline is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## par2cmdline is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA AUTOMAKE_OPTIONS = subdir-objects .DELETE_ON_ERROR: noinst_LIBRARIES = libpar2.a \ libparpar_gf16.a libparpar_gf16_sse2.a libparpar_gf16_ssse3.a libparpar_gf16_avx.a libparpar_gf16_avx2.a libparpar_gf16_avx512.a libparpar_gf16_vbmi.a libparpar_gf16_gfni.a libparpar_gf16_gfni_avx2.a libparpar_gf16_gfni_avx512.a libparpar_gf16_gfni_avx10.a libparpar_gf16_clmul.a libparpar_gf16_avx2_clmul.a libparpar_gf16_vpclmul.a libparpar_gf16_vpclgfni.a libparpar_gf16_neon.a libparpar_gf16_neonsha3.a libparpar_gf16_sve.a libparpar_gf16_sve2.a libparpar_gf16_rvv.a libparpar_gf16_rvv_zvbc.a \ libparpar_hasher.a libparpar_hasher_sse2.a libparpar_hasher_clmul.a libparpar_hasher_xop.a libparpar_hasher_bmi1.a libparpar_hasher_avx2.a libparpar_hasher_avx512.a libparpar_hasher_avx512vl.a libparpar_hasher_armcrc.a libparpar_hasher_neon.a libparpar_hasher_neoncrc.a libparpar_hasher_sve2.a libparpar_hasher_zbkc.a libpar2_a_SOURCES = src/crc.cpp src/crc.h \ src/creatorpacket.cpp src/creatorpacket.h \ src/criticalpacket.cpp src/criticalpacket.h \ src/datablock.cpp src/datablock.h \ src/descriptionpacket.cpp src/descriptionpacket.h \ src/diskfile.cpp src/diskfile.h \ src/filechecksummer.cpp src/filechecksummer.h \ src/galois.cpp src/galois.h \ src/letype.h \ src/mainpacket.cpp src/mainpacket.h \ src/md5.cpp src/md5.h \ src/par1fileformat.cpp src/par1fileformat.h \ src/par1repairer.cpp src/par1repairer.h \ src/par1repairersourcefile.cpp src/par1repairersourcefile.h \ src/par2creator.cpp src/par2creator.h \ src/par2creatorsourcefile.cpp src/par2creatorsourcefile.h \ src/par2fileformat.cpp src/par2fileformat.h \ src/par2repairer.cpp src/par2repairer.h \ src/par2repairersourcefile.cpp src/par2repairersourcefile.h \ src/recoverypacket.cpp src/recoverypacket.h \ src/reedsolomon.cpp src/reedsolomon.h \ src/verificationhashtable.cpp src/verificationhashtable.h \ src/verificationpacket.cpp src/verificationpacket.h \ src/libpar2.cpp src/libpar2.h src/libpar2internal.h \ src/foreach_parallel.h src/hasher.h \ src/utf8.cpp src/utf8.h libpar2_a_DEPENDENCIES = \ libparpar_gf16.a libparpar_gf16_sse2.a libparpar_gf16_ssse3.a libparpar_gf16_avx.a libparpar_gf16_avx2.a libparpar_gf16_avx512.a libparpar_gf16_vbmi.a libparpar_gf16_gfni.a libparpar_gf16_gfni_avx2.a libparpar_gf16_gfni_avx512.a libparpar_gf16_gfni_avx10.a libparpar_gf16_clmul.a libparpar_gf16_avx2_clmul.a libparpar_gf16_vpclmul.a libparpar_gf16_vpclgfni.a libparpar_gf16_neon.a libparpar_gf16_neonsha3.a libparpar_gf16_sve.a libparpar_gf16_sve2.a libparpar_gf16_rvv.a libparpar_gf16_rvv_zvbc.a \ libparpar_hasher.a libparpar_hasher_sse2.a libparpar_hasher_clmul.a libparpar_hasher_xop.a libparpar_hasher_bmi1.a libparpar_hasher_avx2.a libparpar_hasher_avx512.a libparpar_hasher_avx512vl.a libparpar_hasher_armcrc.a libparpar_hasher_neon.a libparpar_hasher_neoncrc.a libparpar_hasher_sve2.a libparpar_hasher_zbkc.a libpar2_a_LIBADD = parpar/gf16/libparpar_gf16*.o parpar/hasher/libparpar_hasher*.o libparpar_gf16_a_SOURCES = \ parpar/gf16/controller.cpp \ parpar/gf16/controller_cpu.cpp \ parpar/gf16/gfmat_coeff.c \ parpar/gf16/gfmat_inv.cpp \ parpar/gf16/gf16pmul.cpp \ parpar/gf16/gf16mul.cpp \ parpar/gf16/gf16_lookup.c \ parpar/gf16/gf_add_generic.c \ parpar/gf16/gf16_cksum_generic.c \ parpar/gf16/controller.h \ parpar/gf16/controller_cpu.h \ parpar/gf16/controller_ocl.h \ parpar/gf16/gf_add.h \ parpar/gf16/gf_add_common.h \ parpar/gf16/gf16_affine.h \ parpar/gf16/gf16_checksum_generic.h \ parpar/gf16/gf16_cksum.h \ parpar/gf16/gf16_cksum_base.h \ parpar/gf16/gf16_clmul.h \ parpar/gf16/gf16_global.h \ parpar/gf16/gf16_lookup.h \ parpar/gf16/gf16_muladd_multi.h \ parpar/gf16/gf16_shuffle.h \ parpar/gf16/gf16_xor.h \ parpar/gf16/gf16_xor_common.h \ parpar/gf16/gf16_xor_common_funcs.h \ parpar/gf16/gf16mul.h \ parpar/gf16/gf16pmul.h \ parpar/gf16/gfmat_coeff.h \ parpar/gf16/gfmat_inv.h \ parpar/gf16/threadqueue.h \ parpar/src/cpuid.h \ parpar/src/hedley.h \ parpar/src/platform.h \ parpar/src/stdint.h libparpar_gf16_a_CFLAGS = $(CFLAGS_POSIX_SOURCE) -D_DARWIN_C_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE libparpar_gf16_a_CPPFLAGS = -Iparpar/gf16 -Iparpar/gf16/opencl-include $(AM_CPPFLAGS) libparpar_gf16_a_CXXFLAGS = libparpar_gf16_sse2_a_SOURCES = \ parpar/gf16/gf16_xor_sse2.c \ parpar/gf16/gf16_lookup_sse2.c \ parpar/gf16/gf_add_sse2.c \ parpar/gf16/gf16_cksum_sse2.c \ parpar/gf16/gf_add_x86.h \ parpar/gf16/gf16_bitdep_init_sse2.h \ parpar/gf16/gf16_checksum_x86.h \ parpar/gf16/gf16_cksum_x86.h \ parpar/gf16/x86_jit.h libparpar_gf16_sse2_a_CFLAGS = $(CFLAGS_SSE2) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_ssse3_a_SOURCES = \ parpar/gf16/gf16_shuffle_ssse3.c \ parpar/gf16/gf16_shuffle_x86.h \ parpar/gf16/gf16_shuffle_x86_common.h \ parpar/gf16/gf16_shuffle_x86_prepare.h libparpar_gf16_ssse3_a_CFLAGS = $(CFLAGS_SSSE3) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_avx_a_SOURCES = \ parpar/gf16/gf16_shuffle_avx.c libparpar_gf16_avx_a_CFLAGS = $(CFLAGS_AVX) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_avx2_a_SOURCES = \ parpar/gf16/gf16_xor_avx2.c \ parpar/gf16/gf16_shuffle_avx2.c \ parpar/gf16/gf_add_avx2.c \ parpar/gf16/gf16_cksum_avx2.c \ parpar/gf16/gf16_bitdep_init_avx2.h \ parpar/gf16/gf16_shuffle2x_x86.h libparpar_gf16_avx2_a_CFLAGS = $(CFLAGS_AVX2) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_avx512_a_SOURCES = \ parpar/gf16/gf16_xor_avx512.c \ parpar/gf16/gf16_shuffle_avx512.c \ parpar/gf16/gf_add_avx512.c \ parpar/gf16/gf16_cksum_avx512.c libparpar_gf16_avx512_a_CFLAGS = $(CFLAGS_AVX512) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_vbmi_a_SOURCES = \ parpar/gf16/gf16_shuffle_vbmi.c libparpar_gf16_vbmi_a_CFLAGS = $(CFLAGS_VBMI) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_gfni_a_SOURCES = \ parpar/gf16/gf16_affine_gfni.c \ parpar/gf16/gf16_affine2x_x86.h libparpar_gf16_gfni_a_CFLAGS = $(CFLAGS_GFNI) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_gfni_avx2_a_SOURCES = \ parpar/gf16/gf16_affine_avx2.c \ parpar/src/platform_warnings.c libparpar_gf16_gfni_avx2_a_CFLAGS = $(CFLAGS_GFNI) $(CFLAGS_AVX2) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_gfni_avx512_a_SOURCES = \ parpar/gf16/gf16_affine_avx512.c libparpar_gf16_gfni_avx512_a_CFLAGS = $(CFLAGS_GFNI) $(CFLAGS_AVX512) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_gfni_avx10_a_SOURCES = \ parpar/gf16/gf16_affine_avx10.c \ parpar/gf16/gf_add_avx10.c \ parpar/gf16/gf16_affine_avx10.h libparpar_gf16_gfni_avx10_a_CFLAGS = $(CFLAGS_GFNI) $(CFLAGS_AVX10) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_clmul_a_SOURCES = \ parpar/gf16/gf16pmul_sse.c \ parpar/gf16/gf16pmul_x86.h libparpar_gf16_clmul_a_CFLAGS = $(CFLAGS_PCLMUL) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_avx2_clmul_a_SOURCES = parpar/gf16/gf16pmul_avx2.c libparpar_gf16_avx2_clmul_a_CFLAGS = $(CFLAGS_PCLMUL) $(CFLAGS_AVX2) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_vpclmul_a_SOURCES = parpar/gf16/gf16pmul_vpclmul.c libparpar_gf16_vpclmul_a_CFLAGS = $(CFLAGS_VPCLMUL) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_vpclgfni_a_SOURCES = parpar/gf16/gf16pmul_vpclgfni.c libparpar_gf16_vpclgfni_a_CFLAGS = $(CFLAGS_VPCLMUL) $(CFLAGS_GFNI) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_neon_a_SOURCES = \ parpar/gf16/gf16_shuffle_neon.c \ parpar/gf16/gf16_clmul_neon.c \ parpar/gf16/gf_add_neon.c \ parpar/gf16/gf16_cksum_neon.c \ parpar/gf16/gf16pmul_neon.c \ parpar/gf16/gf16_checksum_arm.h \ parpar/gf16/gf16_clmul_neon.h \ parpar/gf16/gf16_clmul_neon_base.h \ parpar/gf16/gf16_neon_common.h libparpar_gf16_neon_a_CFLAGS = $(CFLAGS_NEON) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_neonsha3_a_SOURCES = \ parpar/gf16/gf16_clmul_sha3.c libparpar_gf16_neonsha3_a_CFLAGS = $(CFLAGS_NEONSHA3) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_sve_a_SOURCES = \ parpar/gf16/gf16_shuffle128_sve.c \ parpar/gf16/gf_add_sve.c \ parpar/gf16/gf16_cksum_sve.c \ parpar/gf16/gf16_checksum_sve.h \ parpar/gf16/gf16_shuffle128_sve_common.h \ parpar/gf16/gf16_sve_common.h libparpar_gf16_sve_a_CFLAGS = $(CFLAGS_SVE) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_sve2_a_SOURCES = \ parpar/gf16/gf16_shuffle128_sve2.c \ parpar/gf16/gf16_shuffle2x128_sve2.c \ parpar/gf16/gf16_shuffle512_sve2.c \ parpar/gf16/gf16_clmul_sve2.c \ parpar/gf16/gf_add_sve2.c \ parpar/gf16/gf16pmul_sve2.c \ parpar/gf16/gf16_clmul_sve2.h libparpar_gf16_sve2_a_CFLAGS = $(CFLAGS_SVE2) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_rvv_a_SOURCES = \ parpar/gf16/gf16_cksum_rvv.c \ parpar/gf16/gf16_shuffle128_rvv.c \ parpar/gf16/gf_add_rvv.c \ parpar/gf16/gf16_checksum_rvv.h \ parpar/gf16/gf16_rvv_common.h libparpar_gf16_rvv_a_CFLAGS = $(CFLAGS_RV64V) $(CFLAGS_RV32V) $(libparpar_gf16_a_CFLAGS) libparpar_gf16_rvv_zvbc_a_SOURCES = \ parpar/gf16/gf16_clmul_rvv.c \ parpar/gf16/gf16pmul_rvv.c \ parpar/gf16/gf16_clmul_rvv.h libparpar_gf16_rvv_zvbc_a_CFLAGS = $(CFLAGS_RV64ZVBC) $(CFLAGS_RV32ZVBC) $(libparpar_gf16_a_CFLAGS) libparpar_hasher_a_SOURCES = \ parpar/hasher/crc_zeropad.c \ parpar/hasher/hasher.cpp \ parpar/hasher/hasher_scalar.cpp \ parpar/hasher/hasher_input.cpp \ parpar/hasher/hasher_md5crc.cpp \ parpar/hasher/tables.cpp \ parpar/hasher/md5-final.c \ parpar/hasher/crc_arm.h \ parpar/hasher/crc_slice4.h \ parpar/hasher/crc_zeropad.h \ parpar/hasher/hasher.h \ parpar/hasher/hasher_input.h \ parpar/hasher/hasher_input_base.h \ parpar/hasher/hasher_input_impl.h \ parpar/hasher/hasher_input_stub.h \ parpar/hasher/hasher_md5crc.h \ parpar/hasher/hasher_md5crc_base.h \ parpar/hasher/hasher_md5crc_impl.h \ parpar/hasher/hasher_md5crc_stub.h \ parpar/hasher/hasher_md5mb.h \ parpar/hasher/hasher_md5mb_base.h \ parpar/hasher/hasher_md5mb_impl.h \ parpar/hasher/hasher_md5mb_stub.h \ parpar/hasher/md5-arm-asm.h \ parpar/hasher/md5-arm64-asm.h \ parpar/hasher/md5-base.h \ parpar/hasher/md5-final.h \ parpar/hasher/md5-scalar.h \ parpar/hasher/md5-scalar-base.h \ parpar/hasher/md5-x86-asm.h \ parpar/hasher/md5x2-arm-asm.h \ parpar/hasher/md5mb-base.h \ parpar/hasher/md5mb-scalar.h \ parpar/hasher/md5x2-base.h \ parpar/hasher/md5x2-scalar.h \ parpar/hasher/md5x2-x86-asm.h \ parpar/src/cpuid.h \ parpar/src/hedley.h \ parpar/src/platform.h \ parpar/src/stdint.h # these two lines are needed for the right object files to be generated (for some reason) libparpar_hasher_a_CFLAGS = $(CFLAGS_POSIX_SOURCE) libparpar_hasher_a_CXXFLAGS = libparpar_hasher_sse2_a_SOURCES = \ parpar/hasher/hasher_sse.cpp \ parpar/hasher/md5mb-sse.h \ parpar/hasher/md5x2-sse.h \ parpar/hasher/md5x2-sse-asm.h libparpar_hasher_sse2_a_CXXFLAGS = $(CFLAGS_SSE2) $(libparpar_hasher_a_CXXFLAGS) libparpar_hasher_clmul_a_SOURCES = \ parpar/hasher/hasher_clmul.cpp \ parpar/hasher/crc_clmul.h libparpar_hasher_clmul_a_CXXFLAGS = $(CFLAGS_PCLMUL) $(libparpar_hasher_a_CXXFLAGS) libparpar_hasher_xop_a_SOURCES = parpar/hasher/hasher_xop.cpp libparpar_hasher_xop_a_CXXFLAGS = $(CFLAGS_XOP) $(libparpar_hasher_a_CXXFLAGS) libparpar_hasher_bmi1_a_SOURCES = parpar/hasher/hasher_bmi1.cpp libparpar_hasher_bmi1_a_CXXFLAGS = $(CFLAGS_BMI1) $(libparpar_hasher_a_CXXFLAGS) libparpar_hasher_avx2_a_SOURCES = parpar/hasher/hasher_avx2.cpp libparpar_hasher_avx2_a_CXXFLAGS = $(CFLAGS_AVX2) $(libparpar_hasher_a_CXXFLAGS) libparpar_hasher_avx512_a_SOURCES = \ parpar/hasher/hasher_avx512.cpp \ parpar/hasher/md5-avx512.h \ parpar/hasher/md5-avx512-asm.h libparpar_hasher_avx512_a_CXXFLAGS = $(CFLAGS_AVX512F) $(libparpar_hasher_a_CXXFLAGS) libparpar_hasher_avx512vl_a_SOURCES = parpar/hasher/hasher_avx512vl.cpp libparpar_hasher_avx512vl_a_CXXFLAGS = $(CFLAGS_AVX512_HASHER) $(CFLAGS_AVX10) $(libparpar_hasher_a_CXXFLAGS) libparpar_hasher_armcrc_a_SOURCES = parpar/hasher/hasher_armcrc.cpp libparpar_hasher_armcrc_a_CXXFLAGS = $(CFLAGS_ARMCRC) $(CFLAGS_ARMCRC_FP) $(libparpar_hasher_a_CXXFLAGS) libparpar_hasher_neon_a_SOURCES = \ parpar/hasher/hasher_neon.cpp \ parpar/hasher/md5mb-neon.h \ parpar/hasher/md5x2-neon.h \ parpar/hasher/md5x2-neon-asm.h libparpar_hasher_neon_a_CXXFLAGS = $(CFLAGS_NEON) $(libparpar_hasher_a_CXXFLAGS) libparpar_hasher_neoncrc_a_SOURCES = parpar/hasher/hasher_neoncrc.cpp libparpar_hasher_neoncrc_a_CXXFLAGS = $(CFLAGS_NEON) $(CFLAGS_ARMCRC) $(libparpar_hasher_a_CXXFLAGS) libparpar_hasher_sve2_a_SOURCES = \ parpar/hasher/hasher_sve2.cpp \ parpar/hasher/md5mb-sve2.h libparpar_hasher_sve2_a_CXXFLAGS = $(CFLAGS_SVE2) $(libparpar_hasher_a_CXXFLAGS) libparpar_hasher_zbkc_a_SOURCES = \ parpar/hasher/hasher_rvzbc.cpp \ parpar/hasher/crc_rvzbc.h libparpar_hasher_zbkc_a_CXXFLAGS = $(CFLAGS_RV64ZBKC) $(CFLAGS_RV32ZBKC) $(libparpar_hasher_a_CXXFLAGS) bin_PROGRAMS = par2 man_MANS = man/par2.1 par2_SOURCES = src/par2cmdline.cpp \ src/commandline.cpp src/commandline.h par2_LDADD = libpar2.a -lstdc++ $(PTHREAD_LIBS) $(LDFLAGS_LIBATOMIC) LDADD = -lstdc++ $(PTHREAD_LIBS) $(LDFLAGS_LIBATOMIC) AM_CPPFLAGS = -Wall -DNDEBUG -DPARPAR_ENABLE_HASHER_MD5CRC -DPARPAR_INVERT_SUPPORT -DPARPAR_SLIM_GF16 AM_CXXFLAGS = -std=c++14 $(PTHREAD_CFLAGS) EXTRA_DIST = ROADMAP \ man/par2.1 \ automake.sh \ tests/flatdata.tar.gz \ tests/flatdata-par1files.tar.gz \ tests/flatdata-par2files.tar.gz \ tests/subdirdata.tar.gz \ tests/subdirdata-par2files-unix.tar.gz \ tests/subdirdata-par2files-win.tar.gz \ tests/smallsubdirdata.tar.gz \ tests/smallsubdirdata-par2files.tar.gz \ tests/100blocks.tar.gz \ tests/readbeyondeof.tar.gz \ tests/par2-0.6.8-crash.tar.gz \ tests/bug44.tar.gz \ tests/bug128-parfiles.tar.gz \ tests/bug190.tar.gz \ tests/testfuncs.ps1 \ tests/build_unit_tests.ps1 \ tests/run_tests.ps1 \ tests/test1 \ tests/test1.ps1 \ tests/test2 \ tests/test2.ps1 \ tests/test3 \ tests/test3.ps1 \ tests/test4 \ tests/test4.ps1 \ tests/test5 \ tests/test5.ps1 \ tests/test5rk \ tests/test5rk.ps1 \ tests/test6 \ tests/test6.ps1 \ tests/test7 \ tests/test7.ps1 \ tests/test8 \ tests/test8.ps1 \ tests/test9 \ tests/test9.ps1 \ tests/test10 \ tests/test10.ps1 \ tests/test11 \ tests/test11.ps1 \ tests/test12 \ tests/test12.ps1 \ tests/test13 \ tests/test13.ps1 \ tests/test14 \ tests/test14.ps1 \ tests/test15 \ tests/test15.ps1 \ tests/test16 \ tests/test16.ps1 \ tests/test17 \ tests/test17.ps1 \ tests/test18 \ tests/test18.ps1 \ tests/test19 \ tests/test19.ps1 \ tests/test20 \ tests/test20.ps1 \ tests/test21 \ tests/test21.ps1 \ tests/test22 \ tests/test22.ps1 \ tests/test23 \ tests/test23.ps1 \ tests/test24 \ tests/test24.ps1 \ tests/test25 \ tests/test25.ps1 \ tests/test26 \ tests/test26.ps1 \ tests/test27 \ tests/test27.ps1 \ tests/test28 \ tests/test28.ps1 \ tests/test29 \ tests/test29.ps1 \ tests/test30 \ tests/test30.ps1 \ tests/test31 \ tests/test31.ps1 \ tests/test32 \ tests/test32.ps1 \ tests/test33 \ tests/test33.ps1 \ tests/test34 \ tests/test34.ps1 \ tests/test35 \ tests/test35.ps1 \ tests/unit_tests \ tests/unit_tests.ps1 # Programs that need to be compiled for the test suite. # These are the unit tests. check_PROGRAMS = tests/letype_test tests/crc_test tests/md5_test tests/diskfile_test tests/libpar2_test tests/commandline_test tests/descriptionpacket_test tests/criticalpacket_test tests/reedsolomon_test tests/galois_test tests/utf8_test tests_letype_test_SOURCES = src/letype_test.cpp src/letype.h tests_crc_test_SOURCES = src/crc_test.cpp src/crc.cpp src/crc.h tests_crc_test_LDADD = libpar2.a tests_md5_test_SOURCES = src/md5_test.cpp src/md5.cpp src/md5.h tests_md5_test_LDADD = libpar2.a tests_diskfile_test_SOURCES = src/diskfile_test.cpp src/diskfile.cpp src/diskfile.h tests_diskfile_test_LDADD = libpar2.a tests_libpar2_test_SOURCES = src/libpar2_test.cpp src/libpar2.h tests_libpar2_test_LDADD = libpar2.a tests_commandline_test_SOURCES = src/commandline_test.cpp src/commandline.cpp src/commandline.h tests_commandline_test_LDADD = libpar2.a tests_descriptionpacket_test_SOURCES = src/descriptionpacket_test.cpp src/descriptionpacket.cpp src/descriptionpacket.h tests_descriptionpacket_test_LDADD = libpar2.a tests_criticalpacket_test_SOURCES = src/criticalpacket_test.cpp src/criticalpacket.cpp src/criticalpacket.h tests_criticalpacket_test_LDADD = libpar2.a tests_reedsolomon_test_SOURCES = src/reedsolomon_test.cpp src/reedsolomon.cpp src/reedsolomon.h tests_galois_test_SOURCES = src/galois_test.cpp src/galois.cpp src/galois.h tests_utf8_test_SOURCES = src/utf8_test.cpp src/utf8.cpp src/utf8.h # List of all tests. # tests/test* are integration tests that use the binary. # $(check_PROGRAMS) is the list of compiled unit tests. TESTS = tests/test1 \ tests/test2 \ tests/test3 \ tests/test4 \ tests/test5 \ tests/test5rk \ tests/test6 \ tests/test7 \ tests/test8 \ tests/test9 \ tests/test10 \ tests/test11 \ tests/test12 \ tests/test13 \ tests/test14 \ tests/test15 \ tests/test16 \ tests/test17 \ tests/test18 \ tests/test19 \ tests/test20 \ tests/test21 \ tests/test22 \ tests/test23 \ tests/test24 \ tests/test25 \ tests/test26 \ tests/test27 \ tests/test28 \ tests/test29 \ tests/test30 \ tests/test31 \ tests/test32 \ tests/test33 \ tests/test34 \ tests/test35 \ tests/utf8_test \ tests/unit_tests install-exec-hook : cd $(DESTDIR)$(bindir)/ && \ ln -sf par2$(EXEEXT) par2create$(EXEEXT) && \ ln -sf par2$(EXEEXT) par2verify$(EXEEXT) && \ ln -sf par2$(EXEEXT) par2repair$(EXEEXT) install-data-hook : cd $(DESTDIR)$(man1dir)/ && \ ln -sf par2.1 par2create.1 && \ ln -sf par2.1 par2verify.1 && \ ln -sf par2.1 par2repair.1 uninstall-hook : rm -f $(DESTDIR)$(bindir)/par2create$(EXEEXT) rm -f $(DESTDIR)$(bindir)/par2verify$(EXEEXT) rm -f $(DESTDIR)$(bindir)/par2repair$(EXEEXT) rm -f $(DESTDIR)$(man1dir)/par2create.1 rm -f $(DESTDIR)$(man1dir)/par2verify.1 rm -f $(DESTDIR)$(man1dir)/par2repair.1 par2cmdline-turbo-1.4.0/NEWS000066400000000000000000000000001514221355600155360ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/README.md000066400000000000000000000142641514221355600163370ustar00rootroot00000000000000This is a *simple* fork of [par2cmdline](https://github.com/Parchive/par2cmdline) which replaces core computation routines with [ParPar’s](https://github.com/animetosho/ParPar) processing backend, improving par2cmdline’s performance on x86/ARM platforms. The original par2cmdline README [can be found here](https://github.com/Parchive/par2cmdline/blob/master/README.md), which covers more info including build and usage instructions. # Differences with par2cmdline par2cmdline-turbo aims to keep close with its upstream and only differs in the following areas related to performance. As such, the scope of this project covers: * GF16, MD5 and CRC32 computation, using ParPar’s implementation * Use ParPar’s internal checksumming to detect RAM errors during GF16 computation * Adopts ParPar’s stitched hashing approach Note that this fork isn’t intended to change too much of par2cmdline itself and hence, is *not* aimed at fixing bugs or functionality improvements. Bug reports and feature requests should be directed at the [upstream project](https://github.com/Parchive/par2cmdline) where relevant. The minimalistic nature of this project also means that there’s some degree of performance left on the table, and there’s no focus on improving PAR1 performance. Note that par2cmdline-turbo may use slightly more memory than par2cmdline, due to increased buffering in ParPar’s backend. ## Threading & OpenMP par2cmdline-turbo removes par2cmdline's [OpenMP dependency](https://github.com/jkansanen/par2cmdline-mt), replacing it with C++11's threads. This change enables fully static builds, as a number of environments can only dynamically link to the OpenMP runtime. This does have a flow on effect that the `-t`/`-T` flags and libpar2’s *threads* related parameters will be present and work, regardless of OpenMP presence (in par2cmdline, these would differ depending on whether OpenMP support was compiled in). ## Compared to other par2cmdline forks I know of two other forks to par2cmdline: [par2cmdline-tbb](https://web.archive.org/web/20150526072258/http://www.chuchusoft.com/par2_tbb) and [phpar2](http://www.paulhoule.com/phpar2/index.php), both of which focus on performance (like this fork). Key changes of those forks, over the original upstream par2cmdline, from what I can gather: **par2cmdline-tbb** * adds multi-threading via TBB * adds MMX optimized routines for GF16 computation (from phpar2) * adds support for concurrency during creation/verification/repair * adds experimental CUDA (GPU) support * async I/O * only available on x86 platforms, due to use of [Intel TBB](https://www.intel.com/content/www/us/en/developer/articles/guide/get-started-with-tbb.html) and x86 assembly **phpar2** * adds multi-threading * adds MMX optimized routines for GF16 computation * assembly optimized MD5 routine * only available on x86 Windows platforms, due to use of x86 assembly and Windows API reliance **par2cmdline-turbo** differences: * the above forks were based off the original par2cmdline (0.4?), whereas this fork is based off (at time of writing) the latest ‘mainline’ fork (0.8.1 with unreleased changes) * faster GF16 techniques, taking advantage of modern SIMD extensions, are used instead of MMX * faster CRC32 implementations * uses stitched MD5+CRC32 hashing * accelerates matrix inversion * does not add support for async I/O, though it might get some due to the async nature of ParPar’s GF16 backend * does not add GPU computation (but ParPar has elementary OpenCL support, so might arrive in the future) * optimizations for ARM and RISC-V CPUs * cross-platform support ## Benchmarks Some speed comparisons posted by others: * [PAR2 create](https://github.com/animetosho/ParPar/blob/master/benchmarks/info.md) * [PAR2 create](https://github.com/animetosho/par2cmdline-turbo/issues/4#issue-1640569835) (v0.9.0) * [PAR2 verification/repair](https://gist.github.com/thezoggy/3c243b712f0cc960fa4dd78ff1ab56e7) (multiple machines/OSes) * [PAR2 create/verification/repair](https://github.com/pmarreck/par2z#benchmarks) (Apple M-series CPU) ## See Also nzbget.com maintains a [par2cmdline-turbo fork](https://github.com/nzbgetcom/par2cmdline-turbo) which incorporates a few Windows fixes to par2cmdline. # Relation with ParPar [ParPar](https://github.com/animetosho/ParPar) is a from-scratch PAR2 implementation, different from par2cmdline, but only focuses on PAR2 creation. Key reasons to use par2cmdline-turbo over ParPar: * drop-in par2cmdline replacement * par2cmdline supports verification and repair, where ParPar does not Key reasons to use ParPar over par2cmdline-turbo: * par2cmdline-turbo doesn’t implement all performance optimisations in ParPar, such as async I/O, streaming hash computation etc * features unique to ParPar, such as auto-slice size scaling * currently ParPar is more actively maintained than par2cmdline; changes common to these two projects will first be made in ParPar before being ported to par2cmdline-turbo # Installation Pre-built binaries for common systems are available on the [releases page](https://github.com/animetosho/par2cmdline-turbo/releases). ## Packages * Arch Linux: [AUR](https://aur.archlinux.org/packages/par2cmdline-turbo) ([git](https://aur.archlinux.org/packages/par2cmdline-turbo-git)) * Fedora Linux: [COPR](https://copr.fedorainfracloud.org/coprs/errornointernet/par2cmdline-turbo) * Python: [pip](https://pypi.org/project/par2cmdline-turbo/) ## Building * Relatively recent compilers are recommended to take advantage of recent SIMD support (e.g. MSVC >=2019, GCC >=10) * ParPar backend requires C++11 support See [original README](https://github.com/Parchive/par2cmdline/blob/master/README.md#compiling-par2cmdline) for build instructions. # Issues, Bugs & PRs If you have an issue, please test with the original par2cmdline. If the issue is present there, please report it to the par2cmdline repository. Only report issues specific to par2cmdline-turbo here. Similarly, questions and pull requests should also be directed to the par2cmdline repository, unless it *specifically* relates to par2cmdline-turbo. # Other Resources * [MultiPar](https://github.com/Yutaka-Sawada/MultiPar) * [Parchive](https://parchive.github.io/) * [Wikipedia](https://en.wikipedia.org/wiki/Parchive)par2cmdline-turbo-1.4.0/ROADMAP000066400000000000000000000207771514221355600160740ustar00rootroot00000000000000I'm using this file for notes to developers. ----------------------------------- Programs to help you test your code ----------------------------------- Cppcheck Cppcheck is a program that will run a static analysis of the code report likely errors. I used the command: cppcheck --enable=all src/*.cpp > cppcheck_output.txt 2>&1 Valgrind Valgrind is an program that tracks memory usage. So, if you forget to free memory or use memory after it has been freed, it will print out a warning. To run Par2 with Valgrind, you the command is: valgrind --leak-check=yes par2 create foo.par2 input1.txt input2.txt The tests will run with Valgrind if you set the environment variable "VALGRINDOPTS". That environment variable is put on the commandline after "valgrind". So, I use: export PARVALGRINDOPTS="--leak-check=yes" MinGW/WINE If you are on a UNIX box and want to check the Windows code, you can compile using MinGW and run using WINE. MinGW is a package for compiling with GCC against Windows libraries. WINE is a Windows simulator. To compile with MinGW, I use the commands: make clean ./configure --host=x86_64-w64-mingw32 make To run Par2 with WINE, you need to tell it where MinGW's copy of the DLLs are. Start by trying to run WINE by putting "wine" in front of your par.exe command: wine ./par2.exe create foo.par2 input1.txt input2.txt If you get a warning about "libgomp.dll" or any other DLL, use this command: x86_64-w64-mingw32-g++ --print-file-name=libgomp.dll which will tell you where to find MinGW's libgomp.dll. You then convert that Linux path to WINE's Windows path by running the command: winepath On my machine, it just adds "z:" in front of the Linux path. You tell WINE about all the DLL directories by putting those paths into the environment variable WINEPATH, separated by semi-colons. I use: export WINEPATH="z:/usr/lib/gcc/x86_64-w64-mingw32/7.3-win32/;z:/usr/x86_64-w64-mingw32/lib/" Then, you should be able to run "wine par2.exe --help" to test that it works. If WINE is working, you can run tests in WINE with the command: make check WARNING: If the tests see that par2 or the unit tests have an ".exe" extension, they will be automatically run with WINE. When switching from MinGW to normal GCC, you need to run "make clean" or the tests may run the wrong version (or run both versions). ----------------------------------------- Below are some older notes about the code ----------------------------------------- commandline.h / commandline.cpp class CommandLine; This class is used to process the command line arguments passed to par2cmdline. Basic verification is done to ensure that invalid combinations of options are not used and that files exist. crc.h / crc.cpp u32 CRCUpdateChar(u32 crc, u8 ch); u32 CRCUpdateBlock(u32 crc, size_t length, const void *buffer); u32 CRCUpdateBlock(u32 crc, size_t length); void GenerateWindowTable(u64 window, u32 (&windowtable)[256]); u32 ComputeWindowMask(u64 window); u32 CRCSlideChar(u32 crc, u8 chNew, u8 chOld, const u32 (&windowtable)[256]); These functions are used to calculate and verify the CCITT CRC32 values for blocks of data in data files. The latter three functions allow the rapid computation of the crc for data in a sliding window. creatorpacket.h / creatorpacket.cpp class CreatorPacket; This class is used to read and write "Creator Packets" to a recovery file. criticalpacket.h / criticalpacket.cpp class CriticalPacket; class CriticalPacketEntry; CriticalPacket is the base class for DesriptionPacket, VerificationPacket, MainPacket, and CreatorPacket. CriticalPacket encapsulates memory allocation for the packet, computation of the packet hash and writing the packet to disk. CriticalPacketEntry is used to record that a copy of a specific critical packet will be written to a particular file at a specific offset. datablock.h / datablock.cpp class DataBlock; Objects of this type are used to track blocks of data that belong to different files. When data is read from or written to a datablock, it calculates the correct file offset and length to use. descriptionpacket.h / descriptionpacket.cpp class DescriptionPacket; This class is used to read and write "File Description Packets" to a recovery file. The class can compute the file id for the file when creating recovery files and will verify that the packet is ok when it is loaded from a recovery file. diskfile.h / diskfile.cpp class DiskFile; class DiskFileMap; The DiskFile class encapsulates all file access. Each object of this type represents on file that par2cmdline is using, and references to it are used in any other object that needs to refer to a file. The DiskFileMap class is used to track which files have been processed. filechecksummer.h / filechecksummer.cpp class FileCheckSummer; The FileCheckSummer is used to compute the CRC and HASH of a sliding window onto a data file. The CRC is updated continuously as the window is slid allong the file, and the HASH is only calculated when it is needed. galois.h / galois.cpp class Galois; class GaloisTable; class GaloisLongMultiplyTable; The Galois object is used for galois arithmetic. GaloisTable encapsulates the log and antilog tables used for fast multiplation and division, and GaloisLongMultiplyTable is used by the ReedSolomon object to allow rapid multiplication of a block of data by the same value. mainpacket.h / mainpacket.cpp class MainPacket; The MainPacket is used to read and write a "Main Packet" to and from a recovery file. md5.h / md5.cpp class MD5Hash; class MD5Context; MD5Context objects are used to calculate the hash of a block of data. The resulting hash value is then stored in an MD5Hash object. par2cmdline.h / par2cmdline.cpp int main(int argc, char *argv[]); par2cmdline.cpp defines the main() function which is responsible for creating a CommandLine object to parse the command line parameters, and then to create either a Par2Creator or a Par2Repairer to either create recovery files or use them to verify and repair data files. par2cmdline.h contains #include lines for all required header files, as well as a number of type definitions. par2creator.h / par2creator.cpp class Par2Creator; This class encapsulates all of the procedures required to create recovery files from a set of data files. par2creatorsourcefile.h / par2creatorsourcefile.cpp class Par2CreatorSourceFile; Each object of this type represent one of the data files for which recovery files are to be created. It handles creation of a DescriptionPacket and a VerificationPacket, computation of the full file hash and 16k hash values and the calculation and recording of the crc and hash values for each data block withint the file. par2fileformat.h / par2fileformat.cpp struct PACKET_HEADER; struct FILEVERIFICATIONPACKET; struct FILEDESCRIPTIONPACKET; struct MAINPACKET; struct CREATORPACKET; struct RECOVERYBLOCKPACKET; These structures define the exact format used to store the various packets in a recovery file. par2repairer.h / par2repairer.cpp class Par2Repairer; This class encapsulates all of the procedures required to use a set of recovery files to verify and repair the data files for which they were created. par2repairersourcefile.h / par2repairersourcefile.cpp class Par2RepairerSourceFile; Each object of this class represents one of the data files that is being verified and repaired. It has a copy of the description packet and verification packet from the recovery files, and keeps track of exactly which disk files contain data blocks that belong to it. recoverypacket.h / recoverypacket.cpp class RecoveryPacket; This class is used to read and write the header of a Recovery Packet. If contains a DataBlock object which is used to read and write data to or from the rest of the recovery packet. verificationhashtable.h / verificationhashtable.cpp class VerificationHashTable; class VerificationHashEntry; The VerificationHashTable is to store details obtained from all of the verification packets. It is used to check whether or not the crc and hash values calculated by the FileCheckSummer match any of the data blocks from the data files. verificationpacket.h / verificationpacket.cpp class VerificationPacket; This class is used to read a write Verification Packets to and from recovery files. letype.h class leu32; class leu64; These two types are used by fileformat.h and are used to handle the type conversion between little endian and big endian numerical formats. par2cmdline-turbo-1.4.0/automake.sh000077500000000000000000000001311514221355600172110ustar00rootroot00000000000000#!/bin/sh set -e aclocal automake --warnings=all --add-missing autoconf --warnings=all par2cmdline-turbo-1.4.0/build/000077500000000000000000000000001514221355600161505ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/build/build-debug.sh000077500000000000000000000002031514221355600206650ustar00rootroot00000000000000#!/usr/bin/env bash export CFLAGS="-g -O0" export CXXFLAGS="-g -O0" # automake ./automake.sh # configure ./configure # make make par2cmdline-turbo-1.4.0/build/build-static.sh000077500000000000000000000003271514221355600210750ustar00rootroot00000000000000#!/usr/bin/env bash export CFLAGS="-O3 -pipe -fstack-protector-strong" export CXXFLAGS="-O3 -pipe -fstack-protector-strong" export LDFLAGS="-static -s" # automake ./automake.sh # configure ./configure # make make par2cmdline-turbo-1.4.0/build/build.sh000077500000000000000000000002731514221355600176100ustar00rootroot00000000000000#!/usr/bin/env bash export CFLAGS="-O3 -pipe -fstack-protector-strong" export CXXFLAGS="-O3 -pipe -fstack-protector-strong" # automake ./automake.sh # configure ./configure # make make par2cmdline-turbo-1.4.0/build/release.sh000077500000000000000000000007121514221355600201270ustar00rootroot00000000000000#!/usr/bin/env bash set -e git clean -xfd build/build.sh make check make distcheck version=$(ls -1 par2cmdline*.tar.gz | tail -n1 | sed -e 's/par2cmdline-\([0-9\.]\+\)\.tar\.gz/\1/') [[ -d ../par2release ]] && rm -r ../par2release mkdir -p ../par2release mv par2cmdline-$version.tar.gz ../par2release ( cd ../par2release zcat par2cmdline-$version.tar.gz | bzip2 > par2cmdline-$version.tar.bz2 ) # build/release-win.sh git tag -a --sign v$version par2cmdline-turbo-1.4.0/build/sign-release.sh000077500000000000000000000002471514221355600210700ustar00rootroot00000000000000#!/usr/bin/env bash ( cd ../par2release sha512sum *.tar* *.zip > checksums.sha512 for file in *.tar* *.zip; do gpg --detach-sign $file done ) par2cmdline-turbo-1.4.0/config.guess000077500000000000000000001430461514221355600174010ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright 1992-2024 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2024-01-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # # Originally written by Per Bothner; maintained since 2000 by Ben Elliston. # # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.guess # # Please send patches to . # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system '$me' is run on. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright 1992-2024 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try '$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi # Just in case it came from the environment. GUESS= # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still # use 'HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. tmp= # shellcheck disable=SC2172 trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 set_cc_for_build() { # prevent multiple calls if $tmp is already set test "$tmp" && return 0 : "${TMPDIR=/tmp}" # shellcheck disable=SC2039,SC3028 { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } dummy=$tmp/dummy case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in ,,) echo "int x;" > "$dummy.c" for driver in cc gcc c89 c99 ; do if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then CC_FOR_BUILD=$driver break fi done if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac } # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if test -f /.attbin/uname ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown case $UNAME_SYSTEM in Linux|GNU|GNU/*) LIBC=unknown set_cc_for_build cat <<-EOF > "$dummy.c" #if defined(__ANDROID__) LIBC=android #else #include #if defined(__UCLIBC__) LIBC=uclibc #elif defined(__dietlibc__) LIBC=dietlibc #elif defined(__GLIBC__) LIBC=gnu #elif defined(__LLVM_LIBC__) LIBC=llvm #else #include /* First heuristic to detect musl libc. */ #ifdef __DEFINED_va_list LIBC=musl #endif #endif #endif EOF cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` eval "$cc_set_libc" # Second heuristic to detect musl libc. if [ "$LIBC" = unknown ] && command -v ldd >/dev/null && ldd --version 2>&1 | grep -q ^musl; then LIBC=musl fi # If the system lacks a compiler, then just pick glibc. # We could probably try harder. if [ "$LIBC" = unknown ]; then LIBC=gnu fi ;; esac # Note: order is significant - the case branches are not exclusive. case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ echo unknown)` case $UNAME_MACHINE_ARCH in aarch64eb) machine=aarch64_be-unknown ;; armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; sh5el) machine=sh5le-unknown ;; earmv*) arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` machine=${arch}${endian}-unknown ;; *) machine=$UNAME_MACHINE_ARCH-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently (or will in the future) and ABI. case $UNAME_MACHINE_ARCH in earm*) os=netbsdelf ;; arm*|i386|m68k|ns32k|sh3*|sparc|vax) set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ELF__ then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # Determine ABI tags. case $UNAME_MACHINE_ARCH in earm*) expr='s/^earmv[0-9]/-eabi/;s/eb$//' abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case $UNAME_VERSION in Debian*) release='-gnu' ;; *) release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. GUESS=$machine-${os}${release}${abi-} ;; *:Bitrig:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE ;; *:OpenBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE ;; *:SecBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE ;; *:LibertyBSD:*:*) UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE ;; *:MidnightBSD:*:*) GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE ;; *:ekkoBSD:*:*) GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE ;; *:SolidBSD:*:*) GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE ;; *:OS108:*:*) GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE ;; macppc:MirBSD:*:*) GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE ;; *:MirBSD:*:*) GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE ;; *:Sortix:*:*) GUESS=$UNAME_MACHINE-unknown-sortix ;; *:Twizzler:*:*) GUESS=$UNAME_MACHINE-unknown-twizzler ;; *:Redox:*:*) GUESS=$UNAME_MACHINE-unknown-redox ;; mips:OSF1:*.*) GUESS=mips-dec-osf1 ;; alpha:OSF1:*:*) # Reset EXIT trap before exiting to avoid spurious non-zero exit code. trap '' 0 case $UNAME_RELEASE in *4.0) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` ;; *5.*) UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` ;; esac # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case $ALPHA_CPU_TYPE in "EV4 (21064)") UNAME_MACHINE=alpha ;; "EV4.5 (21064)") UNAME_MACHINE=alpha ;; "LCA4 (21066/21068)") UNAME_MACHINE=alpha ;; "EV5 (21164)") UNAME_MACHINE=alphaev5 ;; "EV5.6 (21164A)") UNAME_MACHINE=alphaev56 ;; "EV5.6 (21164PC)") UNAME_MACHINE=alphapca56 ;; "EV5.7 (21164PC)") UNAME_MACHINE=alphapca57 ;; "EV6 (21264)") UNAME_MACHINE=alphaev6 ;; "EV6.7 (21264A)") UNAME_MACHINE=alphaev67 ;; "EV6.8CB (21264C)") UNAME_MACHINE=alphaev68 ;; "EV6.8AL (21264B)") UNAME_MACHINE=alphaev68 ;; "EV6.8CX (21264D)") UNAME_MACHINE=alphaev68 ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE=alphaev69 ;; "EV7 (21364)") UNAME_MACHINE=alphaev7 ;; "EV7.9 (21364A)") UNAME_MACHINE=alphaev79 ;; esac # A Pn.n version is a patched version. # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` GUESS=$UNAME_MACHINE-dec-osf$OSF_REL ;; Amiga*:UNIX_System_V:4.0:*) GUESS=m68k-unknown-sysv4 ;; *:[Aa]miga[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-amigaos ;; *:[Mm]orph[Oo][Ss]:*:*) GUESS=$UNAME_MACHINE-unknown-morphos ;; *:OS/390:*:*) GUESS=i370-ibm-openedition ;; *:z/VM:*:*) GUESS=s390-ibm-zvmoe ;; *:OS400:*:*) GUESS=powerpc-ibm-os400 ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) GUESS=arm-acorn-riscix$UNAME_RELEASE ;; arm*:riscos:*:*|arm*:RISCOS:*:*) GUESS=arm-unknown-riscos ;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) GUESS=hppa1.1-hitachi-hiuxmpp ;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. case `(/bin/universe) 2>/dev/null` in att) GUESS=pyramid-pyramid-sysv3 ;; *) GUESS=pyramid-pyramid-bsd ;; esac ;; NILE*:*:*:dcosx) GUESS=pyramid-pyramid-svr4 ;; DRS?6000:unix:4.0:6*) GUESS=sparc-icl-nx6 ;; DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) case `/usr/bin/uname -p` in sparc) GUESS=sparc-icl-nx7 ;; esac ;; s390x:SunOS:*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL ;; sun4H:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-hal-solaris2$SUN_REL ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris2$SUN_REL ;; i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) GUESS=i386-pc-auroraux$UNAME_RELEASE ;; i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) set_cc_for_build SUN_ARCH=i386 # If there is a compiler, see if it is configured for 64-bit objects. # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. # This test works for both compilers. if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then SUN_ARCH=x86_64 fi fi SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=$SUN_ARCH-pc-solaris2$SUN_REL ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=sparc-sun-solaris3$SUN_REL ;; sun4*:SunOS:*:*) case `/usr/bin/arch -k` in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like '4.1.3-JL'. SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` GUESS=sparc-sun-sunos$SUN_REL ;; sun3*:SunOS:*:*) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 case `/bin/arch` in sun3) GUESS=m68k-sun-sunos$UNAME_RELEASE ;; sun4) GUESS=sparc-sun-sunos$UNAME_RELEASE ;; esac ;; aushp:SunOS:*:*) GUESS=sparc-auspex-sunos$UNAME_RELEASE ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) GUESS=m68k-atari-mint$UNAME_RELEASE ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) GUESS=m68k-milan-mint$UNAME_RELEASE ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) GUESS=m68k-hades-mint$UNAME_RELEASE ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) GUESS=m68k-unknown-mint$UNAME_RELEASE ;; m68k:machten:*:*) GUESS=m68k-apple-machten$UNAME_RELEASE ;; powerpc:machten:*:*) GUESS=powerpc-apple-machten$UNAME_RELEASE ;; RISC*:Mach:*:*) GUESS=mips-dec-mach_bsd4.3 ;; RISC*:ULTRIX:*:*) GUESS=mips-dec-ultrix$UNAME_RELEASE ;; VAX*:ULTRIX*:*:*) GUESS=vax-dec-ultrix$UNAME_RELEASE ;; 2020:CLIX:*:* | 2430:CLIX:*:*) GUESS=clipper-intergraph-clix$UNAME_RELEASE ;; mips:*:*:UMIPS | mips:*:*:RISCos) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && SYSTEM_NAME=`"$dummy" "$dummyarg"` && { echo "$SYSTEM_NAME"; exit; } GUESS=mips-mips-riscos$UNAME_RELEASE ;; Motorola:PowerMAX_OS:*:*) GUESS=powerpc-motorola-powermax ;; Motorola:*:4.3:PL8-*) GUESS=powerpc-harris-powermax ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) GUESS=powerpc-harris-powermax ;; Night_Hawk:Power_UNIX:*:*) GUESS=powerpc-harris-powerunix ;; m88k:CX/UX:7*:*) GUESS=m88k-harris-cxux7 ;; m88k:*:4*:R4*) GUESS=m88k-motorola-sysv4 ;; m88k:*:3*:R3*) GUESS=m88k-motorola-sysv3 ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 then if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ test "$TARGET_BINARY_INTERFACE"x = x then GUESS=m88k-dg-dgux$UNAME_RELEASE else GUESS=m88k-dg-dguxbcs$UNAME_RELEASE fi else GUESS=i586-dg-dgux$UNAME_RELEASE fi ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) GUESS=m88k-dolphin-sysv3 ;; M88*:*:R3*:*) # Delta 88k system running SVR3 GUESS=m88k-motorola-sysv3 ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) GUESS=m88k-tektronix-sysv3 ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) GUESS=m68k-tektronix-bsd ;; *:IRIX*:*:*) IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` GUESS=mips-sgi-irix$IRIX_REL ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) GUESS=i386-ibm-aix ;; ia64:AIX:*:*) if test -x /usr/bin/oslevel ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` then GUESS=$SYSTEM_NAME else GUESS=rs6000-ibm-aix3.2.5 fi elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then GUESS=rs6000-ibm-aix3.2.4 else GUESS=rs6000-ibm-aix3.2 fi ;; *:AIX:*:[4567]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if test -x /usr/bin/lslpp ; then IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` else IBM_REV=$UNAME_VERSION.$UNAME_RELEASE fi GUESS=$IBM_ARCH-ibm-aix$IBM_REV ;; *:AIX:*:*) GUESS=rs6000-ibm-aix ;; ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) GUESS=romp-ibm-bsd4.4 ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) GUESS=rs6000-bull-bosx ;; DPX/2?00:B.O.S.:*:*) GUESS=m68k-bull-sysv3 ;; 9000/[34]??:4.3bsd:1.*:*) GUESS=m68k-hp-bsd ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) GUESS=m68k-hp-bsd4.4 ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` case $UNAME_MACHINE in 9000/31?) HP_ARCH=m68000 ;; 9000/[34]??) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if test -x /usr/bin/getconf; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case $sc_cpu_version in 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case $sc_kernel_bits in 32) HP_ARCH=hppa2.0n ;; 64) HP_ARCH=hppa2.0w ;; '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 esac ;; esac fi if test "$HP_ARCH" = ""; then set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if test "$HP_ARCH" = hppa2.0w then set_cc_for_build # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler # generating 64-bit code. GNU and HP use different nomenclature: # # $ CC_FOR_BUILD=cc ./config.guess # => hppa2.0w-hp-hpux11.23 # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess # => hppa64-hp-hpux11.23 if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | grep -q __LP64__ then HP_ARCH=hppa2.0w else HP_ARCH=hppa64 fi fi GUESS=$HP_ARCH-hp-hpux$HPUX_REV ;; ia64:HP-UX:*:*) HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` GUESS=ia64-hp-hpux$HPUX_REV ;; 3050*:HI-UX:*:*) set_cc_for_build sed 's/^ //' << EOF > "$dummy.c" #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } GUESS=unknown-hitachi-hiuxwe2 ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) GUESS=hppa1.1-hp-bsd ;; 9000/8??:4.3bsd:*:*) GUESS=hppa1.0-hp-bsd ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) GUESS=hppa1.0-hp-mpeix ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) GUESS=hppa1.1-hp-osf ;; hp8??:OSF1:*:*) GUESS=hppa1.0-hp-osf ;; i*86:OSF1:*:*) if test -x /usr/sbin/sysversion ; then GUESS=$UNAME_MACHINE-unknown-osf1mk else GUESS=$UNAME_MACHINE-unknown-osf1 fi ;; parisc*:Lites*:*:*) GUESS=hppa1.1-hp-lites ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) GUESS=c1-convex-bsd ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) GUESS=c34-convex-bsd ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) GUESS=c38-convex-bsd ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) GUESS=c4-convex-bsd ;; CRAY*Y-MP:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=ymp-cray-unicos$CRAY_REL ;; CRAY*[A-Z]90:*:*:*) echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit ;; CRAY*TS:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=t90-cray-unicos$CRAY_REL ;; CRAY*T3E:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=alphaev5-cray-unicosmk$CRAY_REL ;; CRAY*SV1:*:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=sv1-cray-unicos$CRAY_REL ;; *:UNICOS/mp:*:*) CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` GUESS=craynv-cray-unicosmp$CRAY_REL ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; 5000:UNIX_System_V:4.*:*) FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE ;; sparc*:BSD/OS:*:*) GUESS=sparc-unknown-bsdi$UNAME_RELEASE ;; *:BSD/OS:*:*) GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE ;; arm:FreeBSD:*:*) UNAME_PROCESSOR=`uname -p` set_cc_for_build if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi else FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf fi ;; *:FreeBSD:*:*) UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in amd64) UNAME_PROCESSOR=x86_64 ;; i386) UNAME_PROCESSOR=i586 ;; esac FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL ;; i*:CYGWIN*:*) GUESS=$UNAME_MACHINE-pc-cygwin ;; *:MINGW64*:*) GUESS=$UNAME_MACHINE-pc-mingw64 ;; *:MINGW*:*) GUESS=$UNAME_MACHINE-pc-mingw32 ;; *:MSYS*:*) GUESS=$UNAME_MACHINE-pc-msys ;; i*:PW*:*) GUESS=$UNAME_MACHINE-pc-pw32 ;; *:SerenityOS:*:*) GUESS=$UNAME_MACHINE-pc-serenity ;; *:Interix*:*) case $UNAME_MACHINE in x86) GUESS=i586-pc-interix$UNAME_RELEASE ;; authenticamd | genuineintel | EM64T) GUESS=x86_64-unknown-interix$UNAME_RELEASE ;; IA64) GUESS=ia64-unknown-interix$UNAME_RELEASE ;; esac ;; i*:UWIN*:*) GUESS=$UNAME_MACHINE-pc-uwin ;; amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) GUESS=x86_64-pc-cygwin ;; prep*:SunOS:5.*:*) SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` GUESS=powerpcle-unknown-solaris2$SUN_REL ;; *:GNU:*:*) # the GNU system GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL ;; *:GNU/*:*:*) # other systems with GNU libc and userland GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC ;; x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*) GUESS="$UNAME_MACHINE-pc-managarm-mlibc" ;; *:[Mm]anagarm:*:*) GUESS="$UNAME_MACHINE-unknown-managarm-mlibc" ;; *:Minix:*:*) GUESS=$UNAME_MACHINE-unknown-minix ;; aarch64:Linux:*:*) set_cc_for_build CPU=$UNAME_MACHINE LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then ABI=64 sed 's/^ //' << EOF > "$dummy.c" #ifdef __ARM_EABI__ #ifdef __ARM_PCS_VFP ABI=eabihf #else ABI=eabi #endif #endif EOF cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` eval "$cc_set_abi" case $ABI in eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; esac fi GUESS=$CPU-unknown-linux-$LIBCABI ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep -q ld.so.1 if test "$?" = 0 ; then LIBC=gnulibc1 ; fi GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; arm*:Linux:*:*) set_cc_for_build if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_EABI__ then GUESS=$UNAME_MACHINE-unknown-linux-$LIBC else if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | grep -q __ARM_PCS_VFP then GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi else GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf fi fi ;; avr32*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; cris:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; crisv32:Linux:*:*) GUESS=$UNAME_MACHINE-axis-linux-$LIBC ;; e2k:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; frv:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; hexagon:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:Linux:*:*) GUESS=$UNAME_MACHINE-pc-linux-$LIBC ;; ia64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; k1om:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; kvx:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; kvx:cos:*:*) GUESS=$UNAME_MACHINE-unknown-cos ;; kvx:mbr:*:*) GUESS=$UNAME_MACHINE-unknown-mbr ;; loongarch32:Linux:*:* | loongarch64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m32r*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; m68*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; mips:Linux:*:* | mips64:Linux:*:*) set_cc_for_build IS_GLIBC=0 test x"${LIBC}" = xgnu && IS_GLIBC=1 sed 's/^ //' << EOF > "$dummy.c" #undef CPU #undef mips #undef mipsel #undef mips64 #undef mips64el #if ${IS_GLIBC} && defined(_ABI64) LIBCABI=gnuabi64 #else #if ${IS_GLIBC} && defined(_ABIN32) LIBCABI=gnuabin32 #else LIBCABI=${LIBC} #endif #endif #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa64r6 #else #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 CPU=mipsisa32r6 #else #if defined(__mips64) CPU=mips64 #else CPU=mips #endif #endif #endif #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) MIPS_ENDIAN=el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) MIPS_ENDIAN= #else MIPS_ENDIAN= #endif #endif EOF cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` eval "$cc_set_vars" test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } ;; mips64el:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; openrisc*:Linux:*:*) GUESS=or1k-unknown-linux-$LIBC ;; or32:Linux:*:* | or1k*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; padre:Linux:*:*) GUESS=sparc-unknown-linux-$LIBC ;; parisc64:Linux:*:* | hppa64:Linux:*:*) GUESS=hppa64-unknown-linux-$LIBC ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; *) GUESS=hppa-unknown-linux-$LIBC ;; esac ;; ppc64:Linux:*:*) GUESS=powerpc64-unknown-linux-$LIBC ;; ppc:Linux:*:*) GUESS=powerpc-unknown-linux-$LIBC ;; ppc64le:Linux:*:*) GUESS=powerpc64le-unknown-linux-$LIBC ;; ppcle:Linux:*:*) GUESS=powerpcle-unknown-linux-$LIBC ;; riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; s390:Linux:*:* | s390x:Linux:*:*) GUESS=$UNAME_MACHINE-ibm-linux-$LIBC ;; sh64*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sh*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; sparc:Linux:*:* | sparc64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; tile*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; vax:Linux:*:*) GUESS=$UNAME_MACHINE-dec-linux-$LIBC ;; x86_64:Linux:*:*) set_cc_for_build CPU=$UNAME_MACHINE LIBCABI=$LIBC if test "$CC_FOR_BUILD" != no_compiler_found; then ABI=64 sed 's/^ //' << EOF > "$dummy.c" #ifdef __i386__ ABI=x86 #else #ifdef __ILP32__ ABI=x32 #endif #endif EOF cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` eval "$cc_set_abi" case $ABI in x86) CPU=i686 ;; x32) LIBCABI=${LIBC}x32 ;; esac fi GUESS=$CPU-pc-linux-$LIBCABI ;; xtensa*:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. GUESS=i386-sequent-sysv4 ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION ;; i*86:OS/2:*:*) # If we were able to find 'uname', then EMX Unix compatibility # is probably installed. GUESS=$UNAME_MACHINE-pc-os2-emx ;; i*86:XTS-300:*:STOP) GUESS=$UNAME_MACHINE-unknown-stop ;; i*86:atheos:*:*) GUESS=$UNAME_MACHINE-unknown-atheos ;; i*86:syllable:*:*) GUESS=$UNAME_MACHINE-pc-syllable ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) GUESS=i386-unknown-lynxos$UNAME_RELEASE ;; i*86:*DOS:*:*) GUESS=$UNAME_MACHINE-pc-msdosdjgpp ;; i*86:*:4.*:*) UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL fi ;; i*86:*:5:[678]*) # UnixWare 7.x, OpenUNIX and OpenServer 6. case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL else GUESS=$UNAME_MACHINE-pc-sysv32 fi ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i586. # Note: whatever this is, it MUST be the same as what config.sub # prints for the "djgpp" host, or else GDB configure will decide that # this is a cross-build. GUESS=i586-pc-msdosdjgpp ;; Intel:Mach:3*:*) GUESS=i386-pc-mach3 ;; paragon:*:*:*) GUESS=i860-intel-osf1 ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 fi ;; mini*:CTIX:SYS*5:*) # "miniframe" GUESS=m68010-convergent-sysv ;; mc68k:UNIX:SYSTEM5:3.51m) GUESS=m68k-convergent-sysv ;; M680?0:D-NIX:5.3:*) GUESS=m68k-diab-dnix ;; M68*:*:R3V[5678]*:*) test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4; exit; } ;; NCR*:*:4.2:* | MPRAS*:*:4.2:*) OS_REL='.3' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) GUESS=m68k-unknown-lynxos$UNAME_RELEASE ;; mc68030:UNIX_System_V:4.*:*) GUESS=m68k-atari-sysv4 ;; TSUNAMI:LynxOS:2.*:*) GUESS=sparc-unknown-lynxos$UNAME_RELEASE ;; rs6000:LynxOS:2.*:*) GUESS=rs6000-unknown-lynxos$UNAME_RELEASE ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) GUESS=powerpc-unknown-lynxos$UNAME_RELEASE ;; SM[BE]S:UNIX_SV:*:*) GUESS=mips-dde-sysv$UNAME_RELEASE ;; RM*:ReliantUNIX-*:*:*) GUESS=mips-sni-sysv4 ;; RM*:SINIX-*:*:*) GUESS=mips-sni-sysv4 ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` GUESS=$UNAME_MACHINE-sni-sysv4 else GUESS=ns32k-sni-sysv fi ;; PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort # says GUESS=i586-unisys-sysv4 ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm GUESS=hppa1.1-stratus-sysv4 ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. GUESS=i860-stratus-sysv4 ;; i*86:VOS:*:*) # From Paul.Green@stratus.com. GUESS=$UNAME_MACHINE-stratus-vos ;; *:VOS:*:*) # From Paul.Green@stratus.com. GUESS=hppa1.1-stratus-vos ;; mc68*:A/UX:*:*) GUESS=m68k-apple-aux$UNAME_RELEASE ;; news*:NEWS-OS:6*:*) GUESS=mips-sony-newsos6 ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if test -d /usr/nec; then GUESS=mips-nec-sysv$UNAME_RELEASE else GUESS=mips-unknown-sysv$UNAME_RELEASE fi ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. GUESS=powerpc-be-beos ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. GUESS=powerpc-apple-beos ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. GUESS=i586-pc-beos ;; BePC:Haiku:*:*) # Haiku running on Intel PC compatible. GUESS=i586-pc-haiku ;; ppc:Haiku:*:*) # Haiku running on Apple PowerPC GUESS=powerpc-apple-haiku ;; *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) GUESS=$UNAME_MACHINE-unknown-haiku ;; SX-4:SUPER-UX:*:*) GUESS=sx4-nec-superux$UNAME_RELEASE ;; SX-5:SUPER-UX:*:*) GUESS=sx5-nec-superux$UNAME_RELEASE ;; SX-6:SUPER-UX:*:*) GUESS=sx6-nec-superux$UNAME_RELEASE ;; SX-7:SUPER-UX:*:*) GUESS=sx7-nec-superux$UNAME_RELEASE ;; SX-8:SUPER-UX:*:*) GUESS=sx8-nec-superux$UNAME_RELEASE ;; SX-8R:SUPER-UX:*:*) GUESS=sx8r-nec-superux$UNAME_RELEASE ;; SX-ACE:SUPER-UX:*:*) GUESS=sxace-nec-superux$UNAME_RELEASE ;; Power*:Rhapsody:*:*) GUESS=powerpc-apple-rhapsody$UNAME_RELEASE ;; *:Rhapsody:*:*) GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE ;; arm64:Darwin:*:*) GUESS=aarch64-apple-darwin$UNAME_RELEASE ;; *:Darwin:*:*) UNAME_PROCESSOR=`uname -p` case $UNAME_PROCESSOR in unknown) UNAME_PROCESSOR=powerpc ;; esac if command -v xcode-select > /dev/null 2> /dev/null && \ ! xcode-select --print-path > /dev/null 2> /dev/null ; then # Avoid executing cc if there is no toolchain installed as # cc will be a stub that puts up a graphical alert # prompting the user to install developer tools. CC_FOR_BUILD=no_compiler_found else set_cc_for_build fi if test "$CC_FOR_BUILD" != no_compiler_found; then if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_64BIT_ARCH >/dev/null then case $UNAME_PROCESSOR in i386) UNAME_PROCESSOR=x86_64 ;; powerpc) UNAME_PROCESSOR=powerpc64 ;; esac fi # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ grep IS_PPC >/dev/null then UNAME_PROCESSOR=powerpc fi elif test "$UNAME_PROCESSOR" = i386 ; then # uname -m returns i386 or x86_64 UNAME_PROCESSOR=$UNAME_MACHINE fi GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = x86; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE ;; *:QNX:*:4*) GUESS=i386-pc-qnx ;; NEO-*:NONSTOP_KERNEL:*:*) GUESS=neo-tandem-nsk$UNAME_RELEASE ;; NSE-*:NONSTOP_KERNEL:*:*) GUESS=nse-tandem-nsk$UNAME_RELEASE ;; NSR-*:NONSTOP_KERNEL:*:*) GUESS=nsr-tandem-nsk$UNAME_RELEASE ;; NSV-*:NONSTOP_KERNEL:*:*) GUESS=nsv-tandem-nsk$UNAME_RELEASE ;; NSX-*:NONSTOP_KERNEL:*:*) GUESS=nsx-tandem-nsk$UNAME_RELEASE ;; *:NonStop-UX:*:*) GUESS=mips-compaq-nonstopux ;; BS2000:POSIX*:*:*) GUESS=bs2000-siemens-sysv ;; DS/*:UNIX_System_V:*:*) GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "${cputype-}" = 386; then UNAME_MACHINE=i386 elif test "x${cputype-}" != x; then UNAME_MACHINE=$cputype fi GUESS=$UNAME_MACHINE-unknown-plan9 ;; *:TOPS-10:*:*) GUESS=pdp10-unknown-tops10 ;; *:TENEX:*:*) GUESS=pdp10-unknown-tenex ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) GUESS=pdp10-dec-tops20 ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) GUESS=pdp10-xkl-tops20 ;; *:TOPS-20:*:*) GUESS=pdp10-unknown-tops20 ;; *:ITS:*:*) GUESS=pdp10-unknown-its ;; SEI:*:*:SEIUX) GUESS=mips-sei-seiux$UNAME_RELEASE ;; *:DragonFly:*:*) DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL ;; *:*VMS:*:*) UNAME_MACHINE=`(uname -p) 2>/dev/null` case $UNAME_MACHINE in A*) GUESS=alpha-dec-vms ;; I*) GUESS=ia64-dec-vms ;; V*) GUESS=vax-dec-vms ;; esac ;; *:XENIX:*:SysV) GUESS=i386-pc-xenix ;; i*86:skyos:*:*) SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL ;; i*86:rdos:*:*) GUESS=$UNAME_MACHINE-pc-rdos ;; i*86:Fiwix:*:*) GUESS=$UNAME_MACHINE-pc-fiwix ;; *:AROS:*:*) GUESS=$UNAME_MACHINE-unknown-aros ;; x86_64:VMkernel:*:*) GUESS=$UNAME_MACHINE-unknown-esx ;; amd64:Isilon\ OneFS:*:*) GUESS=x86_64-unknown-onefs ;; *:Unleashed:*:*) GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE ;; *:Ironclad:*:*) GUESS=$UNAME_MACHINE-unknown-ironclad ;; esac # Do we have a guess based on uname results? if test "x$GUESS" != x; then echo "$GUESS" exit fi # No uname command or uname output not recognized. set_cc_for_build cat > "$dummy.c" < #include #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #include #if defined(_SIZE_T_) || defined(SIGLOST) #include #endif #endif #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) #if !defined (ultrix) #include #if defined (BSD) #if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); #else #if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); #else printf ("vax-dec-bsd\n"); exit (0); #endif #endif #else printf ("vax-dec-bsd\n"); exit (0); #endif #else #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname un; uname (&un); printf ("vax-dec-ultrix%s\n", un.release); exit (0); #else printf ("vax-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) #if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) #if defined(_SIZE_T_) || defined(SIGLOST) struct utsname *un; uname (&un); printf ("mips-dec-ultrix%s\n", un.release); exit (0); #else printf ("mips-dec-ultrix\n"); exit (0); #endif #endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && { echo "$SYSTEM_NAME"; exit; } # Apollos put the system type in the environment. test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } echo "$0: unable to guess system type" >&2 case $UNAME_MACHINE:$UNAME_SYSTEM in mips:Linux | mips64:Linux) # If we got here on MIPS GNU/Linux, output extra information. cat >&2 <&2 <&2 </dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = "$UNAME_MACHINE" UNAME_RELEASE = "$UNAME_RELEASE" UNAME_SYSTEM = "$UNAME_SYSTEM" UNAME_VERSION = "$UNAME_VERSION" EOF fi exit 1 # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: par2cmdline-turbo-1.4.0/config.h.in000066400000000000000000000110471514221355600170770ustar00rootroot00000000000000/* config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD /* define if the compiler supports basic C++14 syntax */ #undef HAVE_CXX14 /* Define to 1 if you have the declaration of `posix_memalign', and to 0 if you don't. */ #undef HAVE_DECL_POSIX_MEMALIGN /* Define to 1 if you have the header file, and it defines 'DIR'. */ #undef HAVE_DIRENT_H /* Define to 1 if you have the header file. */ #undef HAVE_ENDIAN_H /* Define to 1 if fseeko (and ftello) are declared in stdio.h. */ #undef HAVE_FSEEKO /* Define to 1 if you have the 'getopt' function. */ #undef HAVE_GETOPT /* Define to 1 if you have the header file. */ #undef HAVE_GETOPT_H /* Define to 1 if you have the 'getopt_long' function. */ #undef HAVE_GETOPT_LONG /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H /* Define to 1 if you have the 'memcpy' function. */ #undef HAVE_MEMCPY /* Define to 1 if you have the header file, and it defines 'DIR'. */ #undef HAVE_NDIR_H /* Define if you have POSIX threads libraries and header files. */ #undef HAVE_PTHREAD /* Define to 1 if stdbool.h conforms to C99. */ #undef HAVE_STDBOOL_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDIO_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the 'strcasecmp' function. */ #undef HAVE_STRCASECMP /* Define to 1 if you have the 'strchr' function. */ #undef HAVE_STRCHR /* Define to 1 if you have the 'stricmp' function. */ #undef HAVE_STRICMP /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the header file, and it defines 'DIR'. */ #undef HAVE_SYS_DIR_H /* Define to 1 if you have the header file, and it defines 'DIR'. */ #undef HAVE_SYS_NDIR_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if the system has the type '_Bool'. */ #undef HAVE__BOOL /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to the necessary symbol if this constant uses a non-standard name on your system. */ #undef PTHREAD_CREATE_JOINABLE /* Define to 1 if all of the C89 standard headers exist (not just the ones required in a freestanding environment). This macro is provided for backward compatibility; new code need not use it. */ #undef STDC_HEADERS /* Version number of package */ #undef VERSION /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN # undef WORDS_BIGENDIAN # endif #endif /* Forked package name */ #undef X_PACKAGE /* Forked package version */ #undef X_VERSION /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define to 1 if necessary to make fseeko visible. */ #undef _LARGEFILE_SOURCE /* Define to 1 on platforms where this makes off_t a 64-bit type. */ #undef _LARGE_FILES /* Number of bits in time_t, on hosts where this is settable. */ #undef _TIME_BITS /* Define to 1 on platforms where this makes time_t a 64-bit type. */ #undef __MINGW_USE_VC2005_COMPAT /* Define to empty if 'const' does not conform to ANSI C. */ #undef const /* Define to '__inline__' or '__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define as 'unsigned int' if doesn't define. */ #undef size_t par2cmdline-turbo-1.4.0/config.sub000077500000000000000000001077561514221355600170540ustar00rootroot00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright 1992-2024 Free Software Foundation, Inc. # shellcheck disable=SC2006,SC2268 # see below for rationale timestamp='2024-01-01' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that # program. This Exception is an additional permission under section 7 # of the GNU General Public License, version 3 ("GPLv3"). # Please send patches to . # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # You can get the latest version of this script from: # https://git.savannah.gnu.org/cgit/config.git/plain/config.sub # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. # The "shellcheck disable" line above the timestamp inhibits complaints # about features and limitations of the classic Bourne shell that were # superseded or lifted in POSIX. However, this script identifies a wide # variety of pre-POSIX systems that do not have POSIX shells at all, and # even some reasonably current systems (Solaris 10 as case-in-point) still # have a pre-POSIX /bin/sh. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS Canonicalize a configuration name. Options: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright 1992-2024 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try '$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit ;; --version | -v ) echo "$version" ; exit ;; --help | --h* | -h ) echo "$usage"; exit ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; *local*) # First pass through any local machine types. echo "$1" exit ;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Split fields of configuration type # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read field1 field2 field3 field4 <&2 exit 1 ;; *-*-*-*) basic_machine=$field1-$field2 basic_os=$field3-$field4 ;; *-*-*) # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two # parts maybe_os=$field2-$field3 case $maybe_os in nto-qnx* | linux-* | uclinux-uclibc* \ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ | storm-chaos* | os2-emx* | rtmk-nova* | managarm-* \ | windows-* ) basic_machine=$field1 basic_os=$maybe_os ;; android-linux) basic_machine=$field1-unknown basic_os=linux-android ;; *) basic_machine=$field1-$field2 basic_os=$field3 ;; esac ;; *-*) # A lone config we happen to match not fitting any pattern case $field1-$field2 in decstation-3100) basic_machine=mips-dec basic_os= ;; *-*) # Second component is usually, but not always the OS case $field2 in # Prevent following clause from handling this valid os sun*os*) basic_machine=$field1 basic_os=$field2 ;; zephyr*) basic_machine=$field1-unknown basic_os=$field2 ;; # Manufacturers dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ | unicom* | ibm* | next | hp | isi* | apollo | altos* \ | convergent* | ncr* | news | 32* | 3600* | 3100* \ | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ | ultra | tti* | harris | dolphin | highlevel | gould \ | cbm | ns | masscomp | apple | axis | knuth | cray \ | microblaze* | sim | cisco \ | oki | wec | wrs | winbond) basic_machine=$field1-$field2 basic_os= ;; *) basic_machine=$field1 basic_os=$field2 ;; esac ;; esac ;; *) # Convert single-component short-hands not valid as part of # multi-component configurations. case $field1 in 386bsd) basic_machine=i386-pc basic_os=bsd ;; a29khif) basic_machine=a29k-amd basic_os=udi ;; adobe68k) basic_machine=m68010-adobe basic_os=scout ;; alliant) basic_machine=fx80-alliant basic_os= ;; altos | altos3068) basic_machine=m68k-altos basic_os= ;; am29k) basic_machine=a29k-none basic_os=bsd ;; amdahl) basic_machine=580-amdahl basic_os=sysv ;; amiga) basic_machine=m68k-unknown basic_os= ;; amigaos | amigados) basic_machine=m68k-unknown basic_os=amigaos ;; amigaunix | amix) basic_machine=m68k-unknown basic_os=sysv4 ;; apollo68) basic_machine=m68k-apollo basic_os=sysv ;; apollo68bsd) basic_machine=m68k-apollo basic_os=bsd ;; aros) basic_machine=i386-pc basic_os=aros ;; aux) basic_machine=m68k-apple basic_os=aux ;; balance) basic_machine=ns32k-sequent basic_os=dynix ;; blackfin) basic_machine=bfin-unknown basic_os=linux ;; cegcc) basic_machine=arm-unknown basic_os=cegcc ;; convex-c1) basic_machine=c1-convex basic_os=bsd ;; convex-c2) basic_machine=c2-convex basic_os=bsd ;; convex-c32) basic_machine=c32-convex basic_os=bsd ;; convex-c34) basic_machine=c34-convex basic_os=bsd ;; convex-c38) basic_machine=c38-convex basic_os=bsd ;; cray) basic_machine=j90-cray basic_os=unicos ;; crds | unos) basic_machine=m68k-crds basic_os= ;; da30) basic_machine=m68k-da30 basic_os= ;; decstation | pmax | pmin | dec3100 | decstatn) basic_machine=mips-dec basic_os= ;; delta88) basic_machine=m88k-motorola basic_os=sysv3 ;; dicos) basic_machine=i686-pc basic_os=dicos ;; djgpp) basic_machine=i586-pc basic_os=msdosdjgpp ;; ebmon29k) basic_machine=a29k-amd basic_os=ebmon ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson basic_os=ose ;; gmicro) basic_machine=tron-gmicro basic_os=sysv ;; go32) basic_machine=i386-pc basic_os=go32 ;; h8300hms) basic_machine=h8300-hitachi basic_os=hms ;; h8300xray) basic_machine=h8300-hitachi basic_os=xray ;; h8500hms) basic_machine=h8500-hitachi basic_os=hms ;; harris) basic_machine=m88k-harris basic_os=sysv3 ;; hp300 | hp300hpux) basic_machine=m68k-hp basic_os=hpux ;; hp300bsd) basic_machine=m68k-hp basic_os=bsd ;; hppaosf) basic_machine=hppa1.1-hp basic_os=osf ;; hppro) basic_machine=hppa1.1-hp basic_os=proelf ;; i386mach) basic_machine=i386-mach basic_os=mach ;; isi68 | isi) basic_machine=m68k-isi basic_os=sysv ;; m68knommu) basic_machine=m68k-unknown basic_os=linux ;; magnum | m3230) basic_machine=mips-mips basic_os=sysv ;; merlin) basic_machine=ns32k-utek basic_os=sysv ;; mingw64) basic_machine=x86_64-pc basic_os=mingw64 ;; mingw32) basic_machine=i686-pc basic_os=mingw32 ;; mingw32ce) basic_machine=arm-unknown basic_os=mingw32ce ;; monitor) basic_machine=m68k-rom68k basic_os=coff ;; morphos) basic_machine=powerpc-unknown basic_os=morphos ;; moxiebox) basic_machine=moxie-unknown basic_os=moxiebox ;; msdos) basic_machine=i386-pc basic_os=msdos ;; msys) basic_machine=i686-pc basic_os=msys ;; mvs) basic_machine=i370-ibm basic_os=mvs ;; nacl) basic_machine=le32-unknown basic_os=nacl ;; ncr3000) basic_machine=i486-ncr basic_os=sysv4 ;; netbsd386) basic_machine=i386-pc basic_os=netbsd ;; netwinder) basic_machine=armv4l-rebel basic_os=linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony basic_os=newsos ;; news1000) basic_machine=m68030-sony basic_os=newsos ;; necv70) basic_machine=v70-nec basic_os=sysv ;; nh3000) basic_machine=m68k-harris basic_os=cxux ;; nh[45]000) basic_machine=m88k-harris basic_os=cxux ;; nindy960) basic_machine=i960-intel basic_os=nindy ;; mon960) basic_machine=i960-intel basic_os=mon960 ;; nonstopux) basic_machine=mips-compaq basic_os=nonstopux ;; os400) basic_machine=powerpc-ibm basic_os=os400 ;; OSE68000 | ose68000) basic_machine=m68000-ericsson basic_os=ose ;; os68k) basic_machine=m68k-none basic_os=os68k ;; paragon) basic_machine=i860-intel basic_os=osf ;; parisc) basic_machine=hppa-unknown basic_os=linux ;; psp) basic_machine=mipsallegrexel-sony basic_os=psp ;; pw32) basic_machine=i586-unknown basic_os=pw32 ;; rdos | rdos64) basic_machine=x86_64-pc basic_os=rdos ;; rdos32) basic_machine=i386-pc basic_os=rdos ;; rom68k) basic_machine=m68k-rom68k basic_os=coff ;; sa29200) basic_machine=a29k-amd basic_os=udi ;; sei) basic_machine=mips-sei basic_os=seiux ;; sequent) basic_machine=i386-sequent basic_os= ;; sps7) basic_machine=m68k-bull basic_os=sysv2 ;; st2000) basic_machine=m68k-tandem basic_os= ;; stratus) basic_machine=i860-stratus basic_os=sysv4 ;; sun2) basic_machine=m68000-sun basic_os= ;; sun2os3) basic_machine=m68000-sun basic_os=sunos3 ;; sun2os4) basic_machine=m68000-sun basic_os=sunos4 ;; sun3) basic_machine=m68k-sun basic_os= ;; sun3os3) basic_machine=m68k-sun basic_os=sunos3 ;; sun3os4) basic_machine=m68k-sun basic_os=sunos4 ;; sun4) basic_machine=sparc-sun basic_os= ;; sun4os3) basic_machine=sparc-sun basic_os=sunos3 ;; sun4os4) basic_machine=sparc-sun basic_os=sunos4 ;; sun4sol2) basic_machine=sparc-sun basic_os=solaris2 ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun basic_os= ;; sv1) basic_machine=sv1-cray basic_os=unicos ;; symmetry) basic_machine=i386-sequent basic_os=dynix ;; t3e) basic_machine=alphaev5-cray basic_os=unicos ;; t90) basic_machine=t90-cray basic_os=unicos ;; toad1) basic_machine=pdp10-xkl basic_os=tops20 ;; tpf) basic_machine=s390x-ibm basic_os=tpf ;; udi29k) basic_machine=a29k-amd basic_os=udi ;; ultra3) basic_machine=a29k-nyu basic_os=sym1 ;; v810 | necv810) basic_machine=v810-nec basic_os=none ;; vaxv) basic_machine=vax-dec basic_os=sysv ;; vms) basic_machine=vax-dec basic_os=vms ;; vsta) basic_machine=i386-pc basic_os=vsta ;; vxworks960) basic_machine=i960-wrs basic_os=vxworks ;; vxworks68) basic_machine=m68k-wrs basic_os=vxworks ;; vxworks29k) basic_machine=a29k-wrs basic_os=vxworks ;; xbox) basic_machine=i686-pc basic_os=mingw32 ;; ymp) basic_machine=ymp-cray basic_os=unicos ;; *) basic_machine=$1 basic_os= ;; esac ;; esac # Decode 1-component or ad-hoc basic machines case $basic_machine in # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) cpu=hppa1.1 vendor=winbond ;; op50n) cpu=hppa1.1 vendor=oki ;; op60c) cpu=hppa1.1 vendor=oki ;; ibm*) cpu=i370 vendor=ibm ;; orion105) cpu=clipper vendor=highlevel ;; mac | mpw | mac-mpw) cpu=m68k vendor=apple ;; pmac | pmac-mpw) cpu=powerpc vendor=apple ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) cpu=m68000 vendor=att ;; 3b*) cpu=we32k vendor=att ;; bluegene*) cpu=powerpc vendor=ibm basic_os=cnk ;; decsystem10* | dec10*) cpu=pdp10 vendor=dec basic_os=tops10 ;; decsystem20* | dec20*) cpu=pdp10 vendor=dec basic_os=tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) cpu=m68k vendor=motorola ;; dpx2*) cpu=m68k vendor=bull basic_os=sysv3 ;; encore | umax | mmax) cpu=ns32k vendor=encore ;; elxsi) cpu=elxsi vendor=elxsi basic_os=${basic_os:-bsd} ;; fx2800) cpu=i860 vendor=alliant ;; genix) cpu=ns32k vendor=ns ;; h3050r* | hiux*) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) cpu=m68000 vendor=hp ;; hp9k3[2-9][0-9]) cpu=m68k vendor=hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) cpu=hppa1.1 vendor=hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) cpu=hppa1.1 vendor=hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) cpu=hppa1.0 vendor=hp ;; i*86v32) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv32 ;; i*86v4*) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv4 ;; i*86v) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=sysv ;; i*86sol2) cpu=`echo "$1" | sed -e 's/86.*/86/'` vendor=pc basic_os=solaris2 ;; j90 | j90-cray) cpu=j90 vendor=cray basic_os=${basic_os:-unicos} ;; iris | iris4d) cpu=mips vendor=sgi case $basic_os in irix*) ;; *) basic_os=irix4 ;; esac ;; miniframe) cpu=m68000 vendor=convergent ;; *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) cpu=m68k vendor=atari basic_os=mint ;; news-3600 | risc-news) cpu=mips vendor=sony basic_os=newsos ;; next | m*-next) cpu=m68k vendor=next case $basic_os in openstep*) ;; nextstep*) ;; ns2*) basic_os=nextstep2 ;; *) basic_os=nextstep3 ;; esac ;; np1) cpu=np1 vendor=gould ;; op50n-* | op60c-*) cpu=hppa1.1 vendor=oki basic_os=proelf ;; pa-hitachi) cpu=hppa1.1 vendor=hitachi basic_os=hiuxwe2 ;; pbd) cpu=sparc vendor=tti ;; pbb) cpu=m68k vendor=tti ;; pc532) cpu=ns32k vendor=pc532 ;; pn) cpu=pn vendor=gould ;; power) cpu=power vendor=ibm ;; ps2) cpu=i386 vendor=ibm ;; rm[46]00) cpu=mips vendor=siemens ;; rtpc | rtpc-*) cpu=romp vendor=ibm ;; sde) cpu=mipsisa32 vendor=sde basic_os=${basic_os:-elf} ;; simso-wrs) cpu=sparclite vendor=wrs basic_os=vxworks ;; tower | tower-32) cpu=m68k vendor=ncr ;; vpp*|vx|vx-*) cpu=f301 vendor=fujitsu ;; w65) cpu=w65 vendor=wdc ;; w89k-*) cpu=hppa1.1 vendor=winbond basic_os=proelf ;; none) cpu=none vendor=none ;; leon|leon[3-9]) cpu=sparc vendor=$basic_machine ;; leon-*|leon[3-9]-*) cpu=sparc vendor=`echo "$basic_machine" | sed 's/-.*//'` ;; *-*) # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read cpu vendor <&2 exit 1 ;; esac ;; esac # Here we canonicalize certain aliases for manufacturers. case $vendor in digital*) vendor=dec ;; commodore*) vendor=cbm ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if test x"$basic_os" != x then # First recognize some ad-hoc cases, or perhaps split kernel-os, or else just # set os. obj= case $basic_os in gnu/linux*) kernel=linux os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` ;; os2-emx) kernel=os2 os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` ;; nto-qnx*) kernel=nto os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` ;; *-*) # shellcheck disable=SC2162 saved_IFS=$IFS IFS="-" read kernel os <&2 fi ;; *) echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2 exit 1 ;; esac case $obj in aout* | coff* | elf* | pe*) ;; '') # empty is fine ;; *) echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2 exit 1 ;; esac # Here we handle the constraint that a (synthetic) cpu and os are # valid only in combination with each other and nowhere else. case $cpu-$os in # The "javascript-unknown-ghcjs" triple is used by GHC; we # accept it here in order to tolerate that, but reject any # variations. javascript-ghcjs) ;; javascript-* | *-ghcjs) echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2 exit 1 ;; esac # As a final step for OS-related things, validate the OS-kernel combination # (given a valid OS), if there is a kernel. case $kernel-$os-$obj in linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \ | linux-mlibc*- | linux-musl*- | linux-newlib*- \ | linux-relibc*- | linux-uclibc*- ) ;; uclinux-uclibc*- ) ;; managarm-mlibc*- | managarm-kernel*- ) ;; windows*-msvc*-) ;; -dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \ | -uclibc*- ) # These are just libc implementations, not actual OSes, and thus # require a kernel. echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2 exit 1 ;; -kernel*- ) echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2 exit 1 ;; *-kernel*- ) echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2 exit 1 ;; *-msvc*- ) echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2 exit 1 ;; kfreebsd*-gnu*- | kopensolaris*-gnu*-) ;; vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) ;; nto-qnx*-) ;; os2-emx-) ;; *-eabi*- | *-gnueabi*-) ;; none--*) # None (no kernel, i.e. freestanding / bare metal), # can be paired with an machine code file format ;; -*-) # Blank kernel with real OS is always fine. ;; --*) # Blank kernel and OS with real machine code file format is always fine. ;; *-*-*) echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2 exit 1 ;; esac # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. case $vendor in unknown) case $cpu-$os in *-riscix*) vendor=acorn ;; *-sunos*) vendor=sun ;; *-cnk* | *-aix*) vendor=ibm ;; *-beos*) vendor=be ;; *-hpux*) vendor=hp ;; *-mpeix*) vendor=hp ;; *-hiux*) vendor=hitachi ;; *-unos*) vendor=crds ;; *-dgux*) vendor=dg ;; *-luna*) vendor=omron ;; *-genix*) vendor=ns ;; *-clix*) vendor=intergraph ;; *-mvs* | *-opened*) vendor=ibm ;; *-os400*) vendor=ibm ;; s390-* | s390x-*) vendor=ibm ;; *-ptx*) vendor=sequent ;; *-tpf*) vendor=ibm ;; *-vxsim* | *-vxworks* | *-windiss*) vendor=wrs ;; *-aux*) vendor=apple ;; *-hms*) vendor=hitachi ;; *-mpw* | *-macos*) vendor=apple ;; *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) vendor=atari ;; *-vos*) vendor=stratus ;; esac ;; esac echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}" exit # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: par2cmdline-turbo-1.4.0/configure.ac000077500000000000000000000203661514221355600173510ustar00rootroot00000000000000dnl This file is part of par2cmdline (a PAR 2.0 compatible file verification and dnl repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. dnl dnl Copyright (c) 2003-2015 Peter Brian Clements dnl Copyright (c) 2011-2012 Marcel Partap dnl Copyright (c) 2012-2026 Ike Devolder dnl Copyright (c) 2019 Michael D. Nahas dnl dnl par2cmdline is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl par2cmdline is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the dnl GNU General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA dnl -*- Autoconf -*- dnl Process this file with autoconf to produce a configure script. AC_PREREQ(2.62) AC_INIT([par2cmdline], [1.1.1], [ike.devolder@gmail.com]) AC_DEFINE([X_PACKAGE], ["par2cmdline-turbo"], [Forked package name]) AC_DEFINE([X_VERSION], ["1.4.0"], [Forked package version]) AC_CONFIG_SRCDIR([src/par2cmdline.cpp]) AC_CANONICAL_HOST AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([foreign]) dnl Checks for programs. AC_PROG_CXX AC_PROG_INSTALL dnl Need to define RANLIB used in making static library. AC_PROG_RANLIB dnl gnu.org says the following may be needed for Microsoft libraries. AM_PROG_AR dnl Set language to C++ AC_LANG(C++) dnl Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDBOOL AC_CHECK_HEADERS([stdio.h] [endian.h]) AC_CHECK_HEADERS([getopt.h] [limits.h]) dnl Checks for typedefs, structures, and compiler characteristics. AC_TYPE_SIZE_T AC_C_BIGENDIAN AC_C_CONST AC_C_INLINE AC_SYS_LARGEFILE AC_FUNC_FSEEKO dnl Check for pthreads m4_include([m4/acx_pthread.m4]) ACX_PTHREAD dnl Checks for library functions. AC_FUNC_MEMCMP AC_CHECK_FUNCS([stricmp] [strcasecmp]) AC_CHECK_FUNCS([strchr] [memcpy]) AC_CHECK_FUNCS([getopt] [getopt_long]) dnl C++14 check m4_include([m4/ax_cxx_compile_stdcxx.m4]) AX_CXX_COMPILE_STDCXX(14, noext, mandatory) dnl Enforce C99 CFLAGS="$CFLAGS -std=c99" AC_SUBST(CFLAGS) dnl ParPar backend is mostly compiled as C, so perform compiler checks appropriately AC_LANG_PUSH(C) dnl Platform detection / flags supported by compiler m4_include([m4/ax_check_compile_flag.m4]) AX_CHECK_COMPILE_FLAG([-msse2], , , [-Werror]) AM_CONDITIONAL([HAS_SSE2], [test x$ax_cv_check_cflags__Werror__msse2 = xyes]) AM_COND_IF([HAS_SSE2], [AC_SUBST(CFLAGS_SSE2, "-msse2")]) AX_CHECK_COMPILE_FLAG([-mssse3], , , [-Werror]) AM_CONDITIONAL([HAS_SSSE3], [test x$ax_cv_check_cflags__Werror__mssse3 = xyes]) AM_COND_IF([HAS_SSSE3], [AC_SUBST(CFLAGS_SSSE3, "-mssse3")]) AX_CHECK_COMPILE_FLAG([-mpclmul], , , [-Werror]) AM_CONDITIONAL([HAS_PCLMUL], [test x$ax_cv_check_cflags__Werror__mpclmul = xyes]) AM_COND_IF([HAS_PCLMUL], [AC_SUBST(CFLAGS_PCLMUL, "-mpclmul -msse4.1")]) AX_CHECK_COMPILE_FLAG([-mavx], , , [-Werror]) AM_CONDITIONAL([HAS_AVX], [test x$ax_cv_check_cflags__Werror__mavx = xyes]) AM_COND_IF([HAS_AVX], [AC_SUBST(CFLAGS_AVX, "-mavx")]) AX_CHECK_COMPILE_FLAG([-mxop], , , [-Werror]) AM_CONDITIONAL([HAS_XOP], [test x$ax_cv_check_cflags__Werror__mxop = xyes]) AM_COND_IF([HAS_XOP], [AC_SUBST(CFLAGS_XOP, "-mavx -mxop")]) AX_CHECK_COMPILE_FLAG([-mbmi], , , [-Werror]) AM_CONDITIONAL([HAS_BMI1], [test x$ax_cv_check_cflags__Werror__mbmi = xyes]) AM_COND_IF([HAS_BMI1], [AC_SUBST(CFLAGS_BMI1, "-mavx -mbmi -mpclmul")]) AX_CHECK_COMPILE_FLAG([-mavx2], , , [-Werror]) AM_CONDITIONAL([HAS_AVX2], [test x$ax_cv_check_cflags__Werror__mavx2 = xyes]) AM_COND_IF([HAS_AVX2], [AC_SUBST(CFLAGS_AVX2, "-mavx2")]) AX_CHECK_COMPILE_FLAG([-mavx512vl -mavx512bw], , , [-Werror]) AM_CONDITIONAL([HAS_AVX512], [test x$ax_cv_check_cflags__Werror__mavx512vl__mavx512bw = xyes]) AM_COND_IF([HAS_AVX512], [AC_SUBST(CFLAGS_AVX512, "-mavx512vl -mavx512bw")]) AM_COND_IF([HAS_AVX512], [AC_SUBST(CFLAGS_AVX512F, "-mavx512f")]) AM_COND_IF([HAS_AVX512], [AC_SUBST(CFLAGS_AVX512_HASHER, "-mavx512vl -mavx512bw -mbmi2 -mpclmul")]) AX_CHECK_COMPILE_FLAG([-mavx512vbmi], , , [-Werror]) AM_CONDITIONAL([HAS_VBMI], [test x$ax_cv_check_cflags__Werror__mavx512vbmi = xyes]) AM_COND_IF([HAS_VBMI], [AC_SUBST(CFLAGS_VBMI, "-mavx512vl -mavx512vbmi")]) AX_CHECK_COMPILE_FLAG([-mgfni], , , [-Werror]) AM_CONDITIONAL([HAS_GFNI], [test x$ax_cv_check_cflags__Werror__mgfni = xyes]) AM_COND_IF([HAS_GFNI], [AC_SUBST(CFLAGS_GFNI, "-mgfni -mssse3")]) AX_CHECK_COMPILE_FLAG([-mvpclmulqdq], , , [-Werror]) AM_CONDITIONAL([HAS_VPCLMUL], [test x$ax_cv_check_cflags__Werror__mvpclmulqdq = xyes]) AM_COND_IF([HAS_VPCLMUL], [AC_SUBST(CFLAGS_VPCLMUL, "-mvpclmulqdq -mavx2")]) AX_CHECK_COMPILE_FLAG([-mno-evex512], , , [-Werror]) AM_CONDITIONAL([HAS_AVX10], [test x$ax_cv_check_cflags__Werror__mno_evex512 = xyes]) AM_COND_IF([HAS_AVX10], [AC_SUBST(CFLAGS_AVX10, "-mavx512vl -mavx512bw -mno-evex512")]) AX_CHECK_COMPILE_FLAG([-mfpu=neon -march=armv7-a], , , [-Werror]) AM_CONDITIONAL([HAS_NEON], [test x$ax_cv_check_cflags__Werror__mfpu_neon__march_armv7_a = xyes]) AM_COND_IF([HAS_NEON], [AC_SUBST(CFLAGS_NEON, "-mfpu=neon -fno-lto -march=armv7-a")]) AX_CHECK_COMPILE_FLAG([-mfpu=fp-armv8], , , [-Werror]) AM_CONDITIONAL([HAS_FPARMv8], [test x$ax_cv_check_cflags__Werror__mfpu_fp_armv8 = xyes]) AM_COND_IF([HAS_FPARMv8], [AC_SUBST(CFLAGS_ARMCRC_FP, "-mfpu=fp-armv8")]) AX_CHECK_COMPILE_FLAG([-march=armv8-a+sve], , , [-Werror]) AM_CONDITIONAL([HAS_SVE], [test x$ax_cv_check_cflags__Werror__march_armv8_apsve = xyes]) AM_COND_IF([HAS_SVE], [AC_SUBST(CFLAGS_SVE, "-march=armv8-a+sve")]) AX_CHECK_COMPILE_FLAG([-march=armv8-a+sve2], , , [-Werror]) AM_CONDITIONAL([HAS_SVE2], [test x$ax_cv_check_cflags__Werror__march_armv8_apsve2 = xyes]) AM_COND_IF([HAS_SVE2], [AC_SUBST(CFLAGS_SVE2, "-march=armv8-a+sve2")]) AX_CHECK_COMPILE_FLAG([-march=armv8-a+crc], , , [-Werror]) AM_CONDITIONAL([HAS_ARMCRC], [test x$ax_cv_check_cflags__Werror__march_armv8_apcrc = xyes]) AM_COND_IF([HAS_ARMCRC], [AC_SUBST(CFLAGS_ARMCRC, "-march=armv8-a+crc")]) AX_CHECK_COMPILE_FLAG([-march=armv8.2-a+sha3], , , [-Werror]) AM_CONDITIONAL([HAS_NEONSHA3], [test x$ax_cv_check_cflags__Werror__march_armv8_2_apsha3 = xyes]) AM_COND_IF([HAS_NEONSHA3], [AC_SUBST(CFLAGS_NEONSHA3, "-march=armv8.2-a+sha3")]) AX_CHECK_COMPILE_FLAG([-march=rv64gcv], , , [-Werror]) AM_CONDITIONAL([HAS_RV64V], [test x$ax_cv_check_cflags__Werror__march_rv64gcv = xyes]) AM_COND_IF([HAS_RV64V], [AC_SUBST(CFLAGS_RV64V, "-march=rv64gcv")]) AX_CHECK_COMPILE_FLAG([-march=rv32gcv], , , [-Werror]) AM_CONDITIONAL([HAS_RV32V], [test x$ax_cv_check_cflags__Werror__march_rv32gcv = xyes]) AM_COND_IF([HAS_RV32V], [AC_SUBST(CFLAGS_RV32V, "-march=rv32gcv")]) AX_CHECK_COMPILE_FLAG([-march=rv64gc_zbkc], , , [-Werror]) AM_CONDITIONAL([HAS_RV64ZBKC], [test x$ax_cv_check_cflags__Werror__march_rv64gc_zbkc = xyes]) AM_COND_IF([HAS_RV64ZBKC], [AC_SUBST(CFLAGS_RV64ZBKC, "-march=rv64gc_zbkc")]) AX_CHECK_COMPILE_FLAG([-march=rv32gc_zbkc], , , [-Werror]) AM_CONDITIONAL([HAS_RV32ZBKC], [test x$ax_cv_check_cflags__Werror__march_rv32gc_zbkc = xyes]) AM_COND_IF([HAS_RV32ZBKC], [AC_SUBST(CFLAGS_RV32ZBKC, "-march=rv32gc_zbkc")]) AX_CHECK_COMPILE_FLAG([-march=rv64gcv_zvbc], , , [-Werror]) AM_CONDITIONAL([HAS_RV64ZVBC], [test x$ax_cv_check_cflags__Werror__march_rv64gcv_zvbc = xyes]) AM_COND_IF([HAS_RV64ZVBC], [AC_SUBST(CFLAGS_RV64ZVBC, "-march=rv64gcv_zvbc")]) AX_CHECK_COMPILE_FLAG([-march=rv32gcv_zvbc], , , [-Werror]) AM_CONDITIONAL([HAS_RV32VBC], [test x$ax_cv_check_cflags__Werror__march_rv32gcv_zvbc = xyes]) AM_COND_IF([HAS_RV32VBC], [AC_SUBST(CFLAGS_RV32ZVBC, "-march=rv32gcv_zvbc")]) AC_CHECK_DECLS([posix_memalign], [], [AC_SUBST(CFLAGS_POSIX_SOURCE, "-D_POSIX_C_SOURCE=200112L")], [[#include ]]) dnl Revert back to C++ compiler AC_LANG_POP m4_include([m4/ax_check_link_flag.m4]) AX_CHECK_LINK_FLAG([-latomic]) AM_CONDITIONAL([HAS_LIBATOMIC], [test x$ax_cv_check_ldflags___latomic = xyes]) AM_COND_IF([HAS_LIBATOMIC], [AC_SUBST(LDFLAGS_LIBATOMIC, "-latomic")]) AC_CONFIG_FILES([stamp-h], [echo timestamp > stamp-h]) AC_CONFIG_FILES([Makefile]) AC_OUTPUT par2cmdline-turbo-1.4.0/depcomp000077500000000000000000000562171514221355600164410ustar00rootroot00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2024-06-19.01; # UTC # Copyright (C) 1999-2024 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . GNU Automake home page: . General help using GNU software: . EOF exit $? ;; -v | --v*) echo "depcomp (GNU Automake) $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interference from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsolete pre-3.x GCC compilers. ## but also to in-use compilers like IBM xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: par2cmdline-turbo-1.4.0/libpar2.vcxproj000066400000000000000000000121561514221355600200260ustar00rootroot00000000000000 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B} Win32Proj 10.0 StaticLibrary Unicode v143 <_ProjectFileVersion>10.0.30319.1 libpar2 src\libpar2internal.h {2a658dc2-a6ea-41d1-ad78-2d02675fab14} {c4657dcb-7b83-4608-a1d5-df38d37c6fcf} par2cmdline-turbo-1.4.0/libpar2.vcxproj.filters000066400000000000000000000140271514221355600214740ustar00rootroot00000000000000 {4151047e-39a1-4271-9add-689f1f9eb2ea} cpp;c;cxx;def;odl;idl;hpj;bat;asm {aff7f018-f91f-42f6-a641-e503c124476a} h;hpp;hxx;hm;inl;inc {b8f98794-7ba8-4299-afee-d005206d8845} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files par2cmdline-turbo-1.4.0/m4/000077500000000000000000000000001514221355600153715ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/m4/acx_pthread.m4000066400000000000000000000220771514221355600201250ustar00rootroot00000000000000dnl PGSGL: When updating, comment out port-specific part below; dnl see the comment below with the word "PostgreSQL". dnl dnl Available from the GNU Autoconf Macro Archive at: dnl http://www.gnu.org/software/ac-archive/htmldoc/acx_pthread.html dnl AC_DEFUN([ACX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_PUSH(C) acx_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) AC_MSG_RESULT($acx_pthread_ok) if test x"$acx_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config pthreadGC2" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthread or # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags" ;; esac if test x"$acx_pthread_ok" = xno; then for flag in $acx_pthread_flags; do tryPTHREAD_CFLAGS="" tryPTHREAD_LIBS="" case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) tryPTHREAD_CFLAGS="$flag" ;; pthread-config) # skip this if we already have flags defined, for PostgreSQL if test x"$PTHREAD_CFLAGS" != x -o x"$PTHREAD_LIBS" != x; then continue; fi AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) if test x"$acx_pthread_config" = xno; then continue; fi tryPTHREAD_CFLAGS="`pthread-config --cflags`" tryPTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) tryPTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$tryPTHREAD_LIBS $PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS $tryPTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include ]], [[pthread_t th; pthread_join(th, 0); pthread_attr_init(0); pthread_cleanup_push(0, 0); pthread_create(0,0,0,0); pthread_cleanup_pop(0); ]] )], [acx_pthread_ok=yes], [acx_pthread_ok=no]) if test "x$acx_pthread_ok" = xyes; then # Don't use options that are ignored by the compiler. # We find them by checking stderror. cat >conftest.$ac_ext <<_ACEOF int main (int argc, char **argv) { (void) argc; (void) argv; return 0; } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext if test "`(eval $ac_link 2>&1 1>&5)`" = ""; then # we continue with more flags because Linux needs -lpthread # for libpq builds on PostgreSQL. The test above only # tests for building binaries, not shared libraries. PTHREAD_LIBS=" $tryPTHREAD_LIBS $PTHREAD_LIBS" PTHREAD_CFLAGS="$PTHREAD_CFLAGS $tryPTHREAD_CFLAGS" else acx_pthread_ok=no fi fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($acx_pthread_ok) done fi # Various other checks: if test "x$acx_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: threads are created detached by default # and the JOINABLE attribute has a nonstandard name (UNDETACHED). AC_MSG_CHECKING([for joinable pthread attribute]) AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include ]], [[int attr=PTHREAD_CREATE_JOINABLE;]] )], ok=PTHREAD_CREATE_JOINABLE, ok=unknown) if test x"$ok" = xunknown; then AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include ]], [[int attr=PTHREAD_CREATE_UNDETACHED;]] )], ok=PTHREAD_CREATE_UNDETACHED, ok=unknown) fi if test x"$ok" != xPTHREAD_CREATE_JOINABLE; then AC_DEFINE(PTHREAD_CREATE_JOINABLE, $ok, [Define to the necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_RESULT(${ok}) if test x"$ok" = xunknown; then AC_MSG_WARN([we do not know how to create joinable pthreads]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no # We always add these in PostgreSQL # case "${host_cpu}-${host_os}" in # *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; # *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; # esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # Supporting cc_r would require a special CC in all places that # use libpq, and that is ugly, so we don't do it. Users can still # define their compiler as cc_r to do thread builds of everything. # More AIX lossage: must compile with cc_r AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC}) else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$acx_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else acx_pthread_ok=no $2 fi AC_LANG_POP ])dnl ACX_PTHREAD par2cmdline-turbo-1.4.0/m4/ax_check_compile_flag.m4000066400000000000000000000040701514221355600221020ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) # # DESCRIPTION # # Check whether the given FLAG works with the current language's compiler # or gives an error. (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the current language's default # flags (e.g. CFLAGS) when the check is done. The check is thus made with # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to # force the compiler to issue an error when a bad flag is given. # # INPUT gives an alternative input source to AC_COMPILE_IFELSE. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 6 AC_DEFUN([AX_CHECK_COMPILE_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) AS_VAR_IF(CACHEVAR,yes, [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_COMPILE_FLAGS par2cmdline-turbo-1.4.0/m4/ax_check_link_flag.m4000066400000000000000000000036441514221355600214150ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html # =========================================================================== # # SYNOPSIS # # AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) # # DESCRIPTION # # Check whether the given FLAG works with the linker or gives an error. # (Warnings, however, are ignored) # # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on # success/failure. # # If EXTRA-FLAGS is defined, it is added to the linker's default flags # when the check is done. The check is thus made with the flags: "LDFLAGS # EXTRA-FLAGS FLAG". This can for example be used to force the linker to # issue an error when a bad flag is given. # # INPUT gives an alternative input source to AC_LINK_IFELSE. # # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this # macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG. # # LICENSE # # Copyright (c) 2008 Guido U. Draheim # Copyright (c) 2011 Maarten Bosmans # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 6 AC_DEFUN([AX_CHECK_LINK_FLAG], [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [ ax_check_save_flags=$LDFLAGS LDFLAGS="$LDFLAGS $4 $1" AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], [AS_VAR_SET(CACHEVAR,[yes])], [AS_VAR_SET(CACHEVAR,[no])]) LDFLAGS=$ax_check_save_flags]) AS_VAR_IF(CACHEVAR,yes, [m4_default([$2], :)], [m4_default([$3], :)]) AS_VAR_POPDEF([CACHEVAR])dnl ])dnl AX_CHECK_LINK_FLAGS par2cmdline-turbo-1.4.0/m4/ax_cxx_compile_stdcxx.m4000066400000000000000000000520731514221355600222410ustar00rootroot00000000000000# =========================================================================== # https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html # =========================================================================== # # SYNOPSIS # # AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) # # DESCRIPTION # # Check for baseline language coverage in the compiler for the specified # version of the C++ standard. If necessary, add switches to CXX and # CXXCPP to enable support. VERSION may be '11', '14', '17', or '20' for # the respective C++ standard version. # # The second argument, if specified, indicates whether you insist on an # extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. # -std=c++11). If neither is specified, you get whatever works, with # preference for no added switch, and then for an extended mode. # # The third argument, if specified 'mandatory' or if left unspecified, # indicates that baseline support for the specified C++ standard is # required and that the macro should error out if no mode with that # support is found. If specified 'optional', then configuration proceeds # regardless, after defining HAVE_CXX${VERSION} if and only if a # supporting mode is found. # # LICENSE # # Copyright (c) 2008 Benjamin Kosnik # Copyright (c) 2012 Zack Weinberg # Copyright (c) 2013 Roy Stogner # Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov # Copyright (c) 2015 Paul Norman # Copyright (c) 2015 Moritz Klammler # Copyright (c) 2016, 2018 Krzesimir Nowak # Copyright (c) 2019 Enji Cooper # Copyright (c) 2020 Jason Merrill # Copyright (c) 2021 Jörn Heusipp # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. This file is offered as-is, without any # warranty. #serial 18 dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro dnl (serial version number 13). AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], [$1], [14], [ax_cxx_compile_alternatives="14 1y"], [$1], [17], [ax_cxx_compile_alternatives="17 1z"], [$1], [20], [ax_cxx_compile_alternatives="20"], [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$2], [], [], [$2], [ext], [], [$2], [noext], [], [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], [$3], [optional], [ax_cxx_compile_cxx$1_required=false], [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) AC_LANG_PUSH([C++])dnl ac_success=no m4_if([$2], [], [dnl AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, ax_cv_cxx_compile_cxx$1, [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [ax_cv_cxx_compile_cxx$1=yes], [ax_cv_cxx_compile_cxx$1=no])]) if test x$ax_cv_cxx_compile_cxx$1 = xyes; then ac_success=yes fi]) m4_if([$2], [noext], [], [dnl if test x$ac_success = xno; then for alternative in ${ax_cxx_compile_alternatives}; do switch="-std=gnu++${alternative}" cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi]) m4_if([$2], [ext], [], [dnl if test x$ac_success = xno; then dnl HP's aCC needs +std=c++11 according to: dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf dnl Cray's crayCC needs "-h std=c++11" dnl MSVC needs -std:c++NN for C++17 and later (default is C++14) for alternative in ${ax_cxx_compile_alternatives}; do for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}" MSVC; do if test x"$switch" = xMSVC; then dnl AS_TR_SH maps both `:` and `=` to `_` so -std:c++17 would collide dnl with -std=c++17. We suffix the cache variable name with _MSVC to dnl avoid this. switch=-std:c++${alternative} cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_${switch}_MSVC]) else cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) fi AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, $cachevar, [ac_save_CXX="$CXX" CXX="$CXX $switch" AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], [eval $cachevar=yes], [eval $cachevar=no]) CXX="$ac_save_CXX"]) if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done if test x$ac_success = xyes; then break fi done fi]) AC_LANG_POP([C++]) if test x$ax_cxx_compile_cxx$1_required = xtrue; then if test x$ac_success = xno; then AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) fi fi if test x$ac_success = xno; then HAVE_CXX$1=0 AC_MSG_NOTICE([No compiler with C++$1 support was found]) else HAVE_CXX$1=1 AC_DEFINE(HAVE_CXX$1,1, [define if the compiler supports basic C++$1 syntax]) fi AC_SUBST(HAVE_CXX$1) ]) dnl Test body for checking C++11 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 ) dnl Test body for checking C++14 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 ) dnl Test body for checking C++17 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 ) dnl Test body for checking C++20 support m4_define([_AX_CXX_COMPILE_STDCXX_testbody_20], _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 _AX_CXX_COMPILE_STDCXX_testbody_new_in_20 ) dnl Tests for new features in C++11 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" // MSVC always sets __cplusplus to 199711L in older versions; newer versions // only set it correctly if /Zc:__cplusplus is specified as well as a // /std:c++NN switch: // https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ #elif __cplusplus < 201103L && !defined _MSC_VER #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual ~Base() {} virtual void f() {} }; struct Derived : public Base { virtual ~Derived() override {} virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = [](){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = [](int i, int j){ return i + j; }(1, 2); auto b = []() -> int { return '0'; }(); auto c = [=](){ return a + b; }(); auto d = [&](){ return c; }(); auto e = [a, &b](int x) mutable { const auto identity = [](int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = [](){ return 0; }; const auto unary = [](int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = [](nullary_t f){ return f(); }; const auto higher2nd = [unary](nullary_t f1){ return [unary, f1](unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L ]]) dnl Tests for new features in C++14 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ // If the compiler admits that it is not ready for C++14, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201402L && !defined _MSC_VER #error "This is not a C++14 compiler" #else namespace cxx14 { namespace test_polymorphic_lambdas { int test() { const auto lambda = [](auto&&... args){ const auto istiny = [](auto x){ return (sizeof(x) == 1UL) ? 1 : 0; }; const int aretiny[] = { istiny(args)... }; return aretiny[0]; }; return lambda(1, 1L, 1.0f, '1'); } } namespace test_binary_literals { constexpr auto ivii = 0b0000000000101010; static_assert(ivii == 42, "wrong value"); } namespace test_generalized_constexpr { template < typename CharT > constexpr unsigned long strlen_c(const CharT *const s) noexcept { auto length = 0UL; for (auto p = s; *p; ++p) ++length; return length; } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("x") == 1UL, ""); static_assert(strlen_c("test") == 4UL, ""); static_assert(strlen_c("another\0test") == 7UL, ""); } namespace test_lambda_init_capture { int test() { auto x = 0; const auto lambda1 = [a = x](int b){ return a + b; }; const auto lambda2 = [a = lambda1(x)](){ return a; }; return lambda2(); } } namespace test_digit_separators { constexpr auto ten_million = 100'000'000; static_assert(ten_million == 100000000, ""); } namespace test_return_type_deduction { auto f(int& x) { return x; } decltype(auto) g(int& x) { return x; } template < typename T1, typename T2 > struct is_same { static constexpr auto value = false; }; template < typename T > struct is_same { static constexpr auto value = true; }; int test() { auto x = 0; static_assert(is_same::value, ""); static_assert(is_same::value, ""); return x; } } } // namespace cxx14 #endif // __cplusplus >= 201402L ]]) dnl Tests for new features in C++17 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ // If the compiler admits that it is not ready for C++17, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201703L && !defined _MSC_VER #error "This is not a C++17 compiler" #else #include #include #include namespace cxx17 { namespace test_constexpr_lambdas { constexpr int foo = [](){return 42;}(); } namespace test::nested_namespace::definitions { } namespace test_fold_expression { template int multiply(Args... args) { return (args * ... * 1); } template bool all(Args... args) { return (args && ...); } } namespace test_extended_static_assert { static_assert (true); } namespace test_auto_brace_init_list { auto foo = {5}; auto bar {5}; static_assert(std::is_same, decltype(foo)>::value); static_assert(std::is_same::value); } namespace test_typename_in_template_template_parameter { template typename X> struct D; } namespace test_fallthrough_nodiscard_maybe_unused_attributes { int f1() { return 42; } [[nodiscard]] int f2() { [[maybe_unused]] auto unused = f1(); switch (f1()) { case 17: f1(); [[fallthrough]]; case 42: f1(); } return f1(); } } namespace test_extended_aggregate_initialization { struct base1 { int b1, b2 = 42; }; struct base2 { base2() { b3 = 42; } int b3; }; struct derived : base1, base2 { int d; }; derived d1 {{1, 2}, {}, 4}; // full initialization derived d2 {{}, {}, 4}; // value-initialized bases } namespace test_general_range_based_for_loop { struct iter { int i; int& operator* () { return i; } const int& operator* () const { return i; } iter& operator++() { ++i; return *this; } }; struct sentinel { int i; }; bool operator== (const iter& i, const sentinel& s) { return i.i == s.i; } bool operator!= (const iter& i, const sentinel& s) { return !(i == s); } struct range { iter begin() const { return {0}; } sentinel end() const { return {5}; } }; void f() { range r {}; for (auto i : r) { [[maybe_unused]] auto v = i; } } } namespace test_lambda_capture_asterisk_this_by_value { struct t { int i; int foo() { return [*this]() { return i; }(); } }; } namespace test_enum_class_construction { enum class byte : unsigned char {}; byte foo {42}; } namespace test_constexpr_if { template int f () { if constexpr(cond) { return 13; } else { return 42; } } } namespace test_selection_statement_with_initializer { int f() { return 13; } int f2() { if (auto i = f(); i > 0) { return 3; } switch (auto i = f(); i + 4) { case 17: return 2; default: return 1; } } } namespace test_template_argument_deduction_for_class_templates { template struct pair { pair (T1 p1, T2 p2) : m1 {p1}, m2 {p2} {} T1 m1; T2 m2; }; void f() { [[maybe_unused]] auto p = pair{13, 42u}; } } namespace test_non_type_auto_template_parameters { template struct B {}; B<5> b1; B<'a'> b2; } namespace test_structured_bindings { int arr[2] = { 1, 2 }; std::pair pr = { 1, 2 }; auto f1() -> int(&)[2] { return arr; } auto f2() -> std::pair& { return pr; } struct S { int x1 : 2; volatile double y1; }; S f3() { return {}; } auto [ x1, y1 ] = f1(); auto& [ xr1, yr1 ] = f1(); auto [ x2, y2 ] = f2(); auto& [ xr2, yr2 ] = f2(); const auto [ x3, y3 ] = f3(); } namespace test_exception_spec_type_system { struct Good {}; struct Bad {}; void g1() noexcept; void g2(); template Bad f(T*, T*); template Good f(T1*, T2*); static_assert (std::is_same_v); } namespace test_inline_variables { template void f(T) {} template inline T g(T) { return T{}; } template<> inline void f<>(int) {} template<> int g<>(int) { return 5; } } } // namespace cxx17 #endif // __cplusplus < 201703L && !defined _MSC_VER ]]) dnl Tests for new features in C++20 m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_20], [[ #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 202002L && !defined _MSC_VER #error "This is not a C++20 compiler" #else #include namespace cxx20 { // As C++20 supports feature test macros in the standard, there is no // immediate need to actually test for feature availability on the // Autoconf side. } // namespace cxx20 #endif // __cplusplus < 202002L && !defined _MSC_VER ]]) par2cmdline-turbo-1.4.0/man/000077500000000000000000000000001514221355600156245ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/man/par2.1000066400000000000000000000324301514221355600165540ustar00rootroot00000000000000.\" Manpage for par2 .\" Contact ike.devolder@gmail.com for mistakes. .TH par2 1 "february 2026" "1.1.1" "Parity archive utils" .SH NAME par2 \- PAR 2.0 compatible file verification and repair tool. .SH SYNOPSIS .B par2 c|v|r .RI "[options] <" "PAR2 file" "> [" "files" "]" .br .B par2 c(reate) .RI "[options] <" "PAR2 file" "> [" "files" "]" .br .B par2 v(erify) .RI "[options] <" "PAR2 file" "> [" "files" "]" .br .B par2 r(epair) .RI "[options] <" "PAR2 file" "> [" "files" "]" .br Also: .br .B par2create .RI "[options] <" "PAR2 file" "> [" "files" "]" .br .B par2verify .RI "[options] <" "PAR2 file" "> [" "files" "]" .br .B par2repair .RI "[options] <" "PAR2 file" "> [" "files" "]" .br .SH DESCRIPTION par2cmdline is a program for creating and using PAR2 files to detect damage in data files and repair them if necessary. It can be used with any kind of file. .SH OPTIONS .TP .B \-h Show this help .TP .B \-V Show version .TP .B \-VV Show version and copyright .TP .BI "\-a <" "file" ">" Set the main PAR2 archive name; required on create, optional for verify and repair .TP .B \-b Set the Block\(hyCount (default 2000 when neither \-b nor \-s is specified) .TP .B \-s .RB "Set the Block\(hySize (don't use both " "\-b" " and " "\-s" ")" .TP .B \-r Level of redundancy (percentage, default 5%) .TP .B \-r Redundancy target size, =g(iga),m(ega),k(ilo) bytes .TP .B \-c .RB "Recovery block count (don't use both " "\-r" " and " "\-c" ")" .TP .B \-f First Recovery\(hyBlock\(hyNumber (default 0) .TP .B \-u Uniform recovery file sizes (default is variable sizes) .TP .B \-l .RB "Limit size of recovery files (don't use both " "\-u" " and " "\-l" ")" .TP .B \-n .RB "Number of recovery files (max 31) (don't use both " "\-n" " and " "\-l" ")" .TP .B \-m Memory (in MB) to use (default is half of total physical memory) .TP .B \-t .RB "Number of threads used for main processing (default auto-detected)" .TP .B \-T .RB "Number of files hashed in parallel (default 2)" .TP .B \-v [\-v] Be more verbose .TP .B \-q [\-q] .RB "Be more quiet (" "\-qq" " gives silence)" .TP .B \-p Purge backup files and par files on successful recovery or when no recovery is needed .TP .B \-R Recurse into subdirectories (only useful on create) .TP .B \-O Rename-only mode (skip files that are not perfect matches, useful for quickly fixing renamed files) .TP .B \-N data skipping (find badly mispositioned data blocks) .TP .B \-S Skip leaway (distance +/\- from expected block position, default 64) .TP .B \-B Set the basepath to use as reference for the datafiles .TP .B \-\- Treat all following arguments as filenames .SH EXITCODES .TP .B 0 The process completed successfully .TP .B 1 Repair is possible, there are sufficient recovery blocks .TP .B 2 Repair is *NOT* possible, there are insufficient recovery blocks .TP .B 3 The given combination of commandline flags is invalid .TP .B 4 Insufficient critical data, probably your par files are corrupted .TP .B 5 Repair was completed, but datafiles are still corrupted .TP .B 6 Some file IO error while accessing it .TP .B 7 An internal logic error occurred .TP .B 8 Out of memory or other memory error .SH EXAMPLES With PAR 2.0 you can create PAR2 recovery files for as few as 1 or as many as 32768 files. If you wanted to create PAR1 recovery files for a single file you are forced to split the file into multiple parts and RAR is frequently used for this purpose. You do NOT need to split files with PAR 2.0. To create PAR 2 recovery files for a single data file (e.g. one called test.mpg), you can use the following command: par2 create test.mpg If test.mpg is an 800 MB file, then this will create a total of 8 PAR2 files with the following filenames (taking roughly 6 minutes on a PC with a 1500MHz CPU): test.mpg.par2 - This is an index file for verification only test.mpg.vol00+01.par2 - Recovery file with 1 recovery block test.mpg.vol01+02.par2 - Recovery file with 2 recovery blocks test.mpg.vol03+04.par2 - Recovery file with 4 recovery blocks test.mpg.vol07+08.par2 - Recovery file with 8 recovery blocks test.mpg.vol15+16.par2 - Recovery file with 16 recovery blocks test.mpg.vol31+32.par2 - Recovery file with 32 recovery blocks test.mpg.vol63+37.par2 - Recovery file with 37 recovery blocks The test.mpg.par2 file is 39 KB in size and the other files vary in size from 443 KB to 15 MB. These par2 files will enable the recovery of up to 100 errors totalling 40 MB of lost or damaged data from the original test.mpg file when it and the par2 files are posted on UseNet. When posting on UseNet it is recommended that you use the "-s" option to set a blocksize that is equal to the Article size that you will use to post the data file. If you wanted to post the test.mpg file using an article size of 300 KB then the command you would type is: par2 create -s307200 test.mpg This will create 9 PAR2 files instead of 8, and they will be capable of correcting up to 134 errors totalling 40 MB. It will take roughly 8 minutes to create the recovery files this time. In both of these two examples, the total quantity of recovery data created was 40 MB (which is 5% of 800 MB). If you wish to create a greater or lesser quantity of recovery data, you can use the "-r" option. To create 10% recovery data instead of the default of 5% and also to use a block size of 300 KB, you would use the following command: par2 create -s307200 -r10 test.mpg This would also create 9 PAR2 files, but they would be able to correct up to 269 errors totalling 80 MB. Since twice as much recovery data is created, it will take about 16 minutes to do so with a 1500MHz CPU. The "-u" and "-n" options can be used to control exactly how many recovery files are created and how the recovery blocks are distributed amongst them. They do not affect the total quantity of recovery data created. The "-f" option is used when you create additional recovery data. e.g. If you have already created 10% and want another 5% then you might use the following command: par2 create -s307200 -r5 -f300 test.mpg This specifies the same block size (which is a requirement for additional recovery files), 5% recovery data, and a first block number of 300. The "-m" option controls how much memory par2 uses. It defaults to half of total physical memory unless you override it. CREATING PAR2 FILES FOR MULTIPLE DATA FILES When creating PAR2 recovery files form multiple data files, you must specify the base filename to use for the par2 files and the names of all of the data files. If test.mpg had been split into multiple RAR files, then you could use: par2 create test.mpg.rar.par2 test.mpg.part*.rar The files filename "test.mpg.rar.par2" says what you want the par2 files to be called and "test.mpg.part*.rar" should select all of the RAR files. With "-R" you can also create recovery files for a multi layered tree structure. . ├── aaaa-test.data ├── subdir1 │   └── test-0.data ├── subdir2 │   ├── test-1.data │   ├── test-2.data │   ├── test-3.data │   ├── test-4.data │   ├── test-5.data │   ├── test-6.data │   ├── test-7.data │   ├── test-8.data │   └── test-9.data └── test-a.data par2 create -R recovery.par2 * Will result in: . ├── aaaa-test.data ├── recovery.par2 ├── recovery.vol000+01.par2 ├── recovery.vol001+02.par2 ├── recovery.vol003+04.par2 ├── recovery.vol007+08.par2 ├── recovery.vol015+16.par2 ├── recovery.vol031+32.par2 ├── recovery.vol063+37.par2 ├── subdir1 │   └── test-0.data ├── subdir2 │   ├── test-1.data │   ├── test-2.data │   ├── test-3.data │   ├── test-4.data │   ├── test-5.data │   ├── test-6.data │   ├── test-7.data │   ├── test-8.data │   └── test-9.data └── test-a.data WARNING: Be wary of wildcard shell expansion. Once you pass in more than one unnamed argument the first one will be used as the par2 files name and not be covered by those. VERIFYING AND REPAIRING When using par2 recovery files to verify or repair the data files from which they were created, you only need to specify the filename of one of the par2 files to par2. For example: par2 verify test.mpg.par2 This tells par2 to use the information in test.mpg.par2 to verify the data files. Par2 will automatically search for the other par2 files that were created and use the information they contain to determine the filenames of the original data files and then to verify them. If all of the data files are ok, then par2 will report that repair will not be required. If any of the data files are missing or damaged, par2 will report the details of what it has found. If the recovery files contain enough recovery blocks to repair the damage, you will be told that repair is possible. Otherwise you will be told exactly how many recovery blocks will be required in order to repair. To carry out a repair use the following command: par2 repair test.mpg.par2 This tells par2 to verify and if possible repair any damaged or missing files. If a repair is carried out, then each file which is repaired will be re-verified to confirm that the repair was successful. MISSNAMED AND INCOMPLETE DATA FILES If any of the recovery files or data files have the wrong filename, then par2 will not automatically find and scan them. To have par2 scan such files, you must include them on the command line when attempting to verify or repair; e.g.: par2 r test.mpg.par2 other.mpg This tells par2 to scan the file called other.mpg to see if it contains any data belonging to the original data files. If one of the extra files specified in this way is an exact match for a data file, then the repair process will rename the file so that it has the correct filename. Because par2 is designed to be able to find good data within a damaged file, it can do the same with incomplete files downloaded from UseNet. If some of the articles for a file are missing, you should still download the file and save it to disk for par2 to scan. If you do this then you may find that you can carry out a repair in a situation where you would not otherwise have sufficient recovery data. You can have par2 scan all files that are in the current directory using a command such as: par2 r test.mpg.par2 * RENAME-ONLY MODE If you have accidentally renamed many files that are protected by PAR2 files, you can use the rename-only mode to quickly identify and fix them. In rename-only mode, par2 will skip files that are not perfect matches (i.e., files that are damaged or have mismatched blocks), which provides a massive speedup when dealing with many renamed files. For example, if you have files that were accidentally renamed: mv file1.dat renamed_a.dat mv file2.dat renamed_b.dat mv file3.dat renamed_c.dat You can quickly fix them with: par2 r -O recovery.par2 renamed_*.dat Par2 will scan only the first block of each file to determine if it's a perfect match. If it is, the file will be renamed back to its original name. If it's not a perfect match (e.g., the file is damaged), it will be skipped immediately without scanning the entire file. This is particularly useful when you have hundreds of gigabytes of renamed files protected by multiple PAR2 sets, as it avoids the slow block-by-block scanning that would otherwise be required. WHAT TO DO WHEN YOU ARE TOLD YOU NEED MORE RECOVERY BLOCKS If par2 determines that any of the data files are damaged or missing and finds that there is insufficient recovery data to effect a repair, you will be told that you need a certain number of recovery blocks. You can obtain these by downloading additional recovery files. In order to make things easy, par2 files have filenames that tell you exactly how many recovery blocks each one contains. Assuming that the following command was used to create recovery data: par2 c -b1000 -r5 test.mpg Then the recovery files that are created would be called: test.mpg.par2 test.mpg.vol00+01.par2 test.mpg.vol01+02.par2 test.mpg.vol03+04.par2 test.mpg.vol07+08.par2 test.mpg.vol15+16.par2 test.mpg.vol31+19.par2 The first file in this list does not contain any recovery data, it only contains information sufficient to verify the data files. Each of the other files contains a different number of recovery blocks. The number after the '+' sign is the number of recovery blocks and the number preceding the '+' sign is the block number of the first recovery block in that file. If par2 told you that you needed 10 recovery blocks, then you would need "test.mpg.vol01+02.par2" and "test.mpg.vol07+08.par". You might of course choose to fetch "test.mpg.vol15+16.par2" instead (in which case you would have an extra 6 recovery blocks which would not be used for the repair). HASHING Hashing portion (file verification and creation stages) of the code can't be parallelized without processing multiple files simultaneously. The 2 file/thread default is a good choice for HDDs, using more threads can result in worse performance. Four or more threads can be used for better performance with SSDs. .SH AUTHORS Peter Brian Clements .br Marcel Partap .br Ike Devolder .br Jussi Kansanen .br par2cmdline-turbo-1.4.0/mkinstalldirs000077500000000000000000000071141514221355600176620ustar00rootroot00000000000000#! /bin/sh # mkinstalldirs --- make directory hierarchy scriptversion=2024-06-19.01; # UTC # Original author: Noah Friedman # Created: 1993-05-16 # Public domain. # # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' IFS=" "" $nl" errstatus=0 dirmode= usage="\ Usage: mkinstalldirs [-h] [--help] [--version] [-m MODE] DIR ... Create each directory DIR (with mode MODE, if specified), including all leading file name components. Report bugs to . GNU Automake home page: . General help using GNU software: ." # process command line arguments while test $# -gt 0 ; do case $1 in -h | --help | --h*) # -h for help echo "$usage" exit $? ;; -m) # -m PERM arg shift test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } dirmode=$1 shift ;; --version) echo "$0 (GNU Automake) $scriptversion" exit $? ;; --) # stop option processing shift break ;; -*) # unknown option echo "$usage" 1>&2 exit 1 ;; *) # first non-opt arg break ;; esac done for file do if test -d "$file"; then shift else break fi done case $# in 0) exit 0 ;; esac # Solaris 8's mkdir -p isn't thread-safe. If you mkdir -p a/b and # mkdir -p a/c at the same time, both will detect that a is missing, # one will create a, then the other will try to create a and die with # a "File exists" error. This is a problem when calling mkinstalldirs # from a parallel make. We use --version in the probe to restrict # ourselves to GNU mkdir, which is thread-safe. case $dirmode in '') if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then echo "mkdir -p -- $*" exec mkdir -p -- "$@" else # On NextStep and OpenStep, the 'mkdir' command does not # recognize any option. It will interpret all options as # directories to create, and then abort because '.' already # exists. test -d ./-p && rmdir ./-p test -d ./--version && rmdir ./--version fi ;; *) if mkdir -m "$dirmode" -p --version . >/dev/null 2>&1 && test ! -d ./--version; then echo "umask 22" umask 22 echo "mkdir -m $dirmode -p -- $*" exec mkdir -m "$dirmode" -p -- "$@" else # Clean up after NextStep and OpenStep mkdir. for d in ./-m ./-p ./--version "./$dirmode"; do test -d $d && rmdir $d done fi ;; esac echo "umask 22" umask 22 for file do case $file in /*) pathcomp=/ ;; *) pathcomp= ;; esac oIFS=$IFS IFS=/ set fnord $file shift IFS=$oIFS for d do test "x$d" = x && continue pathcomp=$pathcomp$d case $pathcomp in -*) pathcomp=./$pathcomp ;; esac if test ! -d "$pathcomp"; then echo "mkdir $pathcomp" mkdir "$pathcomp" || lasterr=$? if test ! -d "$pathcomp"; then errstatus=$lasterr fi fi pathcomp=$pathcomp/ done if test ! -z "$dirmode"; then echo "chmod $dirmode $file" chmod "$dirmode" "$file" || errstatus=$? fi done exit $errstatus # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: par2cmdline-turbo-1.4.0/par2cmdline.props000066400000000000000000000047061514221355600203450ustar00rootroot00000000000000 $(SolutionDir)$(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\$(ProjectName)\ true false Disabled WIN32;_DEBUG;_CONSOLE;PACKAGE="par2cmdline";VERSION="1.1.1";X_PACKAGE="par2cmdline-turbo";X_VERSION="1.4.0";PARPAR_ENABLE_HASHER_MD5CRC;%(PreprocessorDefinitions) false EnableFastChecks MultiThreadedDebug Level3 ProgramDatabase true false MaxSpeed AnySuitable Speed true WIN32;NDEBUG;_CONSOLE;PACKAGE="par2cmdline";VERSION="1.1.1";X_PACKAGE="par2cmdline-turbo";X_VERSION="1.4.0";PARPAR_ENABLE_HASHER_MD5CRC;%(PreprocessorDefinitions) true MultiThreaded true Level3 ProgramDatabase true true true par2cmdline-turbo-1.4.0/par2cmdline.sln000066400000000000000000000676601514221355600200060ustar00rootroot00000000000000Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.13.35931.197 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpar2", "libpar2.vcxproj", "{D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "par2cmdline", "par2cmdline.vcxproj", "{54348158-1A8A-41AF-A76A-0AAAD0E43909}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{09795456-9DA5-4253-AE04-DD2AB464238A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "letype_test", "tests\letype_test.vcxproj", "{73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crc_test", "tests\crc_test.vcxproj", "{48AECF41-79C7-4739-A80E-B4CD22CCE099}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "md5_test", "tests\md5_test.vcxproj", "{3C50AA0B-8B10-4BAD-85F8-953861327D0A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "galois_test", "tests\galois_test.vcxproj", "{263DECCF-470B-4BD0-B21A-D349B563CF6C}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "reedsolomon_test", "tests\reedsolomon_test.vcxproj", "{4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "utf8_test", "tests\utf8_test.vcxproj", "{757A04A3-9F41-42E2-813C-0774D3835AFB}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "diskfile_test", "tests\diskfile_test.vcxproj", "{6612248A-AB2C-400B-A879-89CA63DBFCD1}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpar2_test", "tests\libpar2_test.vcxproj", "{BF86226A-49BA-401C-B6E1-DC52A7D7965D}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "commandline_test", "tests\commandline_test.vcxproj", "{E2E11769-76C3-40BD-BF1C-A9198481BD6E}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "descriptionpacket_test", "tests\descriptionpacket_test.vcxproj", "{4E77EA22-B1FA-4FC0-8E13-74953DE245D7}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "criticalpacket_test", "tests\criticalpacket_test.vcxproj", "{862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gf16", "parpar\gf16.vcxproj", "{2A658DC2-A6EA-41D1-AD78-2D02675FAB14}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hasher", "parpar\hasher.vcxproj", "{C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|ARM64 = Release|ARM64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 UnitTests-Debug|ARM64 = UnitTests-Debug|ARM64 UnitTests-Debug|Win32 = UnitTests-Debug|Win32 UnitTests-Debug|x64 = UnitTests-Debug|x64 UnitTests-Release|ARM64 = UnitTests-Release|ARM64 UnitTests-Release|Win32 = UnitTests-Release|Win32 UnitTests-Release|x64 = UnitTests-Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.Debug|ARM64.ActiveCfg = Debug|ARM64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.Debug|ARM64.Build.0 = Debug|ARM64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.Debug|Win32.ActiveCfg = Debug|Win32 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.Debug|Win32.Build.0 = Debug|Win32 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.Debug|x64.ActiveCfg = Debug|x64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.Debug|x64.Build.0 = Debug|x64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.Release|ARM64.ActiveCfg = Release|ARM64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.Release|ARM64.Build.0 = Release|ARM64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.Release|Win32.ActiveCfg = Release|Win32 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.Release|Win32.Build.0 = Release|Win32 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.Release|x64.ActiveCfg = Release|x64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.Release|x64.Build.0 = Release|x64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.UnitTests-Debug|x64.Build.0 = Debug|x64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.UnitTests-Release|Win32.Build.0 = Release|Win32 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.UnitTests-Release|x64.ActiveCfg = Release|x64 {D0A94F83-495E-4FB2-AC33-9A3EC2CC263B}.UnitTests-Release|x64.Build.0 = Release|x64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.Debug|ARM64.ActiveCfg = Debug|ARM64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.Debug|ARM64.Build.0 = Debug|ARM64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.Debug|Win32.ActiveCfg = Debug|Win32 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.Debug|Win32.Build.0 = Debug|Win32 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.Debug|x64.ActiveCfg = Debug|x64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.Debug|x64.Build.0 = Debug|x64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.Release|ARM64.ActiveCfg = Release|ARM64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.Release|ARM64.Build.0 = Release|ARM64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.Release|Win32.ActiveCfg = Release|Win32 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.Release|Win32.Build.0 = Release|Win32 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.Release|x64.ActiveCfg = Release|x64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.Release|x64.Build.0 = Release|x64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.UnitTests-Debug|x64.Build.0 = Debug|x64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.UnitTests-Release|Win32.Build.0 = Release|Win32 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.UnitTests-Release|x64.ActiveCfg = Release|x64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14}.UnitTests-Release|x64.Build.0 = Release|x64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.Debug|ARM64.ActiveCfg = Debug|ARM64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.Debug|ARM64.Build.0 = Debug|ARM64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.Debug|Win32.ActiveCfg = Debug|Win32 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.Debug|Win32.Build.0 = Debug|Win32 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.Debug|x64.ActiveCfg = Debug|x64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.Debug|x64.Build.0 = Debug|x64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.Release|ARM64.ActiveCfg = Release|ARM64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.Release|ARM64.Build.0 = Release|ARM64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.Release|Win32.ActiveCfg = Release|Win32 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.Release|Win32.Build.0 = Release|Win32 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.Release|x64.ActiveCfg = Release|x64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.Release|x64.Build.0 = Release|x64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.UnitTests-Debug|x64.Build.0 = Debug|x64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.UnitTests-Release|Win32.Build.0 = Release|Win32 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.UnitTests-Release|x64.ActiveCfg = Release|x64 {C4657DCB-7B83-4608-A1D5-DF38D37C6FCF}.UnitTests-Release|x64.Build.0 = Release|x64 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.Debug|ARM64.ActiveCfg = Debug|ARM64 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.Debug|ARM64.Build.0 = Debug|ARM64 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.Debug|Win32.ActiveCfg = Debug|Win32 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.Debug|Win32.Build.0 = Debug|Win32 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.Debug|x64.ActiveCfg = Debug|x64 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.Debug|x64.Build.0 = Debug|x64 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.Release|ARM64.ActiveCfg = Release|ARM64 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.Release|ARM64.Build.0 = Release|ARM64 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.Release|Win32.ActiveCfg = Release|Win32 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.Release|Win32.Build.0 = Release|Win32 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.Release|x64.ActiveCfg = Release|x64 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.Release|x64.Build.0 = Release|x64 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {54348158-1A8A-41AF-A76A-0AAAD0E43909}.UnitTests-Release|x64.ActiveCfg = Release|x64 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.Debug|ARM64.ActiveCfg = Debug|ARM64 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.Debug|Win32.ActiveCfg = Debug|Win32 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.Debug|x64.ActiveCfg = Debug|x64 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.Release|ARM64.ActiveCfg = Release|ARM64 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.Release|Win32.ActiveCfg = Release|Win32 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.Release|x64.ActiveCfg = Release|x64 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.UnitTests-Debug|x64.Build.0 = Debug|x64 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.UnitTests-Release|Win32.Build.0 = Release|Win32 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.UnitTests-Release|x64.ActiveCfg = Release|x64 {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C}.UnitTests-Release|x64.Build.0 = Release|x64 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.Debug|ARM64.ActiveCfg = Debug|ARM64 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.Debug|Win32.ActiveCfg = Debug|Win32 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.Debug|x64.ActiveCfg = Debug|x64 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.Release|ARM64.ActiveCfg = Release|ARM64 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.Release|Win32.ActiveCfg = Release|Win32 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.Release|x64.ActiveCfg = Release|x64 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.UnitTests-Debug|x64.Build.0 = Debug|x64 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.UnitTests-Release|Win32.Build.0 = Release|Win32 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.UnitTests-Release|x64.ActiveCfg = Release|x64 {48AECF41-79C7-4739-A80E-B4CD22CCE099}.UnitTests-Release|x64.Build.0 = Release|x64 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.Debug|ARM64.ActiveCfg = Debug|ARM64 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.Debug|Win32.ActiveCfg = Debug|Win32 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.Debug|x64.ActiveCfg = Debug|x64 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.Release|ARM64.ActiveCfg = Release|ARM64 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.Release|Win32.ActiveCfg = Release|Win32 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.Release|x64.ActiveCfg = Release|x64 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.UnitTests-Debug|x64.Build.0 = Debug|x64 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.UnitTests-Release|Win32.Build.0 = Release|Win32 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.UnitTests-Release|x64.ActiveCfg = Release|x64 {3C50AA0B-8B10-4BAD-85F8-953861327D0A}.UnitTests-Release|x64.Build.0 = Release|x64 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.Debug|ARM64.ActiveCfg = Debug|ARM64 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.Debug|Win32.ActiveCfg = Debug|Win32 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.Debug|x64.ActiveCfg = Debug|x64 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.Release|ARM64.ActiveCfg = Release|ARM64 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.Release|Win32.ActiveCfg = Release|Win32 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.Release|x64.ActiveCfg = Release|x64 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.UnitTests-Debug|x64.Build.0 = Debug|x64 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.UnitTests-Release|Win32.Build.0 = Release|Win32 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.UnitTests-Release|x64.ActiveCfg = Release|x64 {263DECCF-470B-4BD0-B21A-D349B563CF6C}.UnitTests-Release|x64.Build.0 = Release|x64 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.Debug|ARM64.ActiveCfg = Debug|ARM64 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.Debug|Win32.ActiveCfg = Debug|Win32 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.Debug|x64.ActiveCfg = Debug|x64 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.Release|ARM64.ActiveCfg = Release|ARM64 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.Release|Win32.ActiveCfg = Release|Win32 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.Release|x64.ActiveCfg = Release|x64 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.UnitTests-Debug|x64.Build.0 = Debug|x64 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.UnitTests-Release|Win32.Build.0 = Release|Win32 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.UnitTests-Release|x64.ActiveCfg = Release|x64 {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1}.UnitTests-Release|x64.Build.0 = Release|x64 {757A04A3-9F41-42E2-813C-0774D3835AFB}.Debug|ARM64.ActiveCfg = Debug|ARM64 {757A04A3-9F41-42E2-813C-0774D3835AFB}.Debug|Win32.ActiveCfg = Debug|Win32 {757A04A3-9F41-42E2-813C-0774D3835AFB}.Debug|x64.ActiveCfg = Debug|x64 {757A04A3-9F41-42E2-813C-0774D3835AFB}.Release|ARM64.ActiveCfg = Release|ARM64 {757A04A3-9F41-42E2-813C-0774D3835AFB}.Release|Win32.ActiveCfg = Release|Win32 {757A04A3-9F41-42E2-813C-0774D3835AFB}.Release|x64.ActiveCfg = Release|x64 {757A04A3-9F41-42E2-813C-0774D3835AFB}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {757A04A3-9F41-42E2-813C-0774D3835AFB}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {757A04A3-9F41-42E2-813C-0774D3835AFB}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {757A04A3-9F41-42E2-813C-0774D3835AFB}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {757A04A3-9F41-42E2-813C-0774D3835AFB}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {757A04A3-9F41-42E2-813C-0774D3835AFB}.UnitTests-Debug|x64.Build.0 = Debug|x64 {757A04A3-9F41-42E2-813C-0774D3835AFB}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {757A04A3-9F41-42E2-813C-0774D3835AFB}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {757A04A3-9F41-42E2-813C-0774D3835AFB}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {757A04A3-9F41-42E2-813C-0774D3835AFB}.UnitTests-Release|Win32.Build.0 = Release|Win32 {757A04A3-9F41-42E2-813C-0774D3835AFB}.UnitTests-Release|x64.ActiveCfg = Release|x64 {757A04A3-9F41-42E2-813C-0774D3835AFB}.UnitTests-Release|x64.Build.0 = Release|x64 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.Debug|ARM64.ActiveCfg = Debug|ARM64 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.Debug|Win32.ActiveCfg = Debug|Win32 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.Debug|x64.ActiveCfg = Debug|x64 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.Release|ARM64.ActiveCfg = Release|ARM64 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.Release|Win32.ActiveCfg = Release|Win32 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.Release|x64.ActiveCfg = Release|x64 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.UnitTests-Debug|x64.Build.0 = Debug|x64 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.UnitTests-Release|Win32.Build.0 = Release|Win32 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.UnitTests-Release|x64.ActiveCfg = Release|x64 {6612248A-AB2C-400B-A879-89CA63DBFCD1}.UnitTests-Release|x64.Build.0 = Release|x64 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.Debug|ARM64.ActiveCfg = Debug|ARM64 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.Debug|Win32.ActiveCfg = Debug|Win32 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.Debug|x64.ActiveCfg = Debug|x64 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.Release|ARM64.ActiveCfg = Release|ARM64 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.Release|Win32.ActiveCfg = Release|Win32 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.Release|x64.ActiveCfg = Release|x64 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.UnitTests-Debug|x64.Build.0 = Debug|x64 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.UnitTests-Release|Win32.Build.0 = Release|Win32 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.UnitTests-Release|x64.ActiveCfg = Release|x64 {BF86226A-49BA-401C-B6E1-DC52A7D7965D}.UnitTests-Release|x64.Build.0 = Release|x64 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.Debug|ARM64.ActiveCfg = Debug|ARM64 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.Debug|Win32.ActiveCfg = Debug|Win32 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.Debug|x64.ActiveCfg = Debug|x64 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.Release|ARM64.ActiveCfg = Release|ARM64 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.Release|Win32.ActiveCfg = Release|Win32 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.Release|x64.ActiveCfg = Release|x64 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.UnitTests-Debug|x64.Build.0 = Debug|x64 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.UnitTests-Release|Win32.Build.0 = Release|Win32 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.UnitTests-Release|x64.ActiveCfg = Release|x64 {E2E11769-76C3-40BD-BF1C-A9198481BD6E}.UnitTests-Release|x64.Build.0 = Release|x64 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.Debug|ARM64.ActiveCfg = Debug|ARM64 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.Debug|Win32.ActiveCfg = Debug|Win32 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.Debug|x64.ActiveCfg = Debug|x64 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.Release|ARM64.ActiveCfg = Release|ARM64 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.Release|Win32.ActiveCfg = Release|Win32 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.Release|x64.ActiveCfg = Release|x64 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.UnitTests-Debug|x64.Build.0 = Debug|x64 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.UnitTests-Release|Win32.Build.0 = Release|Win32 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.UnitTests-Release|x64.ActiveCfg = Release|x64 {4E77EA22-B1FA-4FC0-8E13-74953DE245D7}.UnitTests-Release|x64.Build.0 = Release|x64 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.Debug|ARM64.ActiveCfg = Debug|ARM64 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.Debug|Win32.ActiveCfg = Debug|Win32 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.Debug|x64.ActiveCfg = Debug|x64 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.Release|ARM64.ActiveCfg = Release|ARM64 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.Release|Win32.ActiveCfg = Release|Win32 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.Release|x64.ActiveCfg = Release|x64 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.UnitTests-Debug|ARM64.ActiveCfg = Debug|ARM64 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.UnitTests-Debug|ARM64.Build.0 = Debug|ARM64 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.UnitTests-Debug|Win32.ActiveCfg = Debug|Win32 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.UnitTests-Debug|Win32.Build.0 = Debug|Win32 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.UnitTests-Debug|x64.ActiveCfg = Debug|x64 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.UnitTests-Debug|x64.Build.0 = Debug|x64 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.UnitTests-Release|ARM64.ActiveCfg = Release|ARM64 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.UnitTests-Release|ARM64.Build.0 = Release|ARM64 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.UnitTests-Release|Win32.ActiveCfg = Release|Win32 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.UnitTests-Release|Win32.Build.0 = Release|Win32 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.UnitTests-Release|x64.ActiveCfg = Release|x64 {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2}.UnitTests-Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {73CFCB2E-19E6-4ADF-B825-D2FA87458F3C} = {09795456-9DA5-4253-AE04-DD2AB464238A} {48AECF41-79C7-4739-A80E-B4CD22CCE099} = {09795456-9DA5-4253-AE04-DD2AB464238A} {3C50AA0B-8B10-4BAD-85F8-953861327D0A} = {09795456-9DA5-4253-AE04-DD2AB464238A} {263DECCF-470B-4BD0-B21A-D349B563CF6C} = {09795456-9DA5-4253-AE04-DD2AB464238A} {4F6ACCCE-6D7F-4C47-B57B-8B017AB383E1} = {09795456-9DA5-4253-AE04-DD2AB464238A} {757A04A3-9F41-42E2-813C-0774D3835AFB} = {09795456-9DA5-4253-AE04-DD2AB464238A} {6612248A-AB2C-400B-A879-89CA63DBFCD1} = {09795456-9DA5-4253-AE04-DD2AB464238A} {BF86226A-49BA-401C-B6E1-DC52A7D7965D} = {09795456-9DA5-4253-AE04-DD2AB464238A} {E2E11769-76C3-40BD-BF1C-A9198481BD6E} = {09795456-9DA5-4253-AE04-DD2AB464238A} {4E77EA22-B1FA-4FC0-8E13-74953DE245D7} = {09795456-9DA5-4253-AE04-DD2AB464238A} {862B6ABA-D1C6-4DB8-A893-747CC8B5D0F2} = {09795456-9DA5-4253-AE04-DD2AB464238A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {06E89BF8-F199-48B1-94CB-D7BB1B23DDD9} EndGlobalSection EndGlobal par2cmdline-turbo-1.4.0/par2cmdline.vcxproj000066400000000000000000000065141514221355600206740ustar00rootroot00000000000000 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 {54348158-1a8a-41af-a76a-0aaad0e43909} Win32Proj 10.0 Application Unicode v143 <_ProjectFileVersion>10.0.30319.1 par2 $(OutDir)par2.exe $(OutDir)par2cmdline.pdb Console {d0a94f83-495e-4fb2-ac33-9a3ec2cc263b} par2cmdline-turbo-1.4.0/par2cmdline.vcxproj.filters000066400000000000000000000034311514221355600223360ustar00rootroot00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Header Files par2cmdline-turbo-1.4.0/parpar/000077500000000000000000000000001514221355600163365ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/parpar/gf16.vcxproj000066400000000000000000000544621514221355600205310ustar00rootroot00000000000000 Debug ARM Debug ARM64 Debug Win32 Release ARM Release ARM64 Release Win32 Debug x64 Release x64 {2A658DC2-A6EA-41D1-AD78-2D02675FAB14} parpar_gf16 10.0 StaticLibrary true v143 Unicode StaticLibrary false v143 true Unicode true gf16\opencl-include;$(IncludePath) false gf16\opencl-include;$(IncludePath) Level3 true _DEBUG;_CONSOLE;PARPAR_INVERT_SUPPORT;PARPAR_SLIM_GF16;%(PreprocessorDefinitions) MultiThreadedDebug true true 4267;%(DisableSpecificWarnings) Console true Level3 true true false NDEBUG;_CONSOLE;PARPAR_INVERT_SUPPORT;PARPAR_SLIM_GF16;%(PreprocessorDefinitions) MultiThreaded true false 4267;%(DisableSpecificWarnings) Console true true true AdvancedVectorExtensions512 AdvancedVectorExtensions512 -mavx512bw -mavx512vl -mgfni %(AdditionalOptions) -mavx512bw -mavx512vl -mgfni %(AdditionalOptions) AdvancedVectorExtensions2 AdvancedVectorExtensions2 -mavx2 -mgfni %(AdditionalOptions) -mavx2 -mgfni %(AdditionalOptions) StreamingSIMDExtensions2 -mssse3 -mgfni %(AdditionalOptions) -mssse3 -mgfni %(AdditionalOptions) AdvancedVectorExtensions2 AdvancedVectorExtensions2 -mavx512bw -mavx512vl -mgfni -mno-evex512 %(AdditionalOptions) -mavx512bw -mavx512vl -mgfni -mno-evex512 %(AdditionalOptions) StreamingSIMDExtensions2 AdvancedVectorExtensions AdvancedVectorExtensions AdvancedVectorExtensions2 AdvancedVectorExtensions2 AdvancedVectorExtensions512 AdvancedVectorExtensions512 -mavx512bw -mavx512vl %(AdditionalOptions) -mavx512bw -mavx512vl %(AdditionalOptions) AdvancedVectorExtensions512 AdvancedVectorExtensions512 -mavx512bw -mavx512vl -mavx512vbmi %(AdditionalOptions) -mavx512bw -mavx512vl -mavx512vbmi %(AdditionalOptions) -mfpu=neon %(AdditionalOptions) -march=armv8-a+sve %(AdditionalOptions) -march=armv8-a+sve2 %(AdditionalOptions) -march=armv8-a+sve2 %(AdditionalOptions) -march=armv8-a+sve2 %(AdditionalOptions) -mfpu=neon %(AdditionalOptions) -march=armv8.2-a+sha3 %(AdditionalOptions) -march=armv8-a+sve2 %(AdditionalOptions) StreamingSIMDExtensions2 -mssse3 %(AdditionalOptions) -mssse3 %(AdditionalOptions) AdvancedVectorExtensions2 AdvancedVectorExtensions2 AdvancedVectorExtensions512 AdvancedVectorExtensions512 -mavx512bw -mavx512vl %(AdditionalOptions) -mavx512bw -mavx512vl %(AdditionalOptions) StreamingSIMDExtensions2 StreamingSIMDExtensions2 AdvancedVectorExtensions2 AdvancedVectorExtensions2 AdvancedVectorExtensions512 AdvancedVectorExtensions512 -mavx512bw -mavx512vl %(AdditionalOptions) -mavx512bw -mavx512vl %(AdditionalOptions) AdvancedVectorExtensions2 AdvancedVectorExtensions2 -mavx512bw -mavx512vl -mno-evex512 %(AdditionalOptions) -mavx512bw -mavx512vl -mno-evex512 %(AdditionalOptions) -mfpu=neon %(AdditionalOptions) -march=armv8-a+sve %(AdditionalOptions) -march=armv8-a+sve2 %(AdditionalOptions) StreamingSIMDExtensions2 AdvancedVectorExtensions2 AdvancedVectorExtensions2 AdvancedVectorExtensions512 AdvancedVectorExtensions512 -mavx512bw -mavx512vl %(AdditionalOptions) -mavx512bw -mavx512vl %(AdditionalOptions) -mfpu=neon %(AdditionalOptions) -march=armv8-a+sve %(AdditionalOptions) StreamingSIMDExtensions2 -mpclmul %(AdditionalOptions) -mpclmul %(AdditionalOptions) AdvancedVectorExtensions2 AdvancedVectorExtensions2 -mavx2 -mpclmul %(AdditionalOptions) -mavx2 -mpclmul %(AdditionalOptions) AdvancedVectorExtensions2 AdvancedVectorExtensions2 -mavx2 -mvpclmulqdq %(AdditionalOptions) -mavx2 -mvpclmulqdq %(AdditionalOptions) AdvancedVectorExtensions2 AdvancedVectorExtensions2 -mavx2 -mvpclmulqdq -mgfni %(AdditionalOptions) -mavx2 -mvpclmulqdq -mgfni %(AdditionalOptions) -mfpu=neon %(AdditionalOptions) -march=armv8-a+sve2 %(AdditionalOptions) -mavx2 -mgfni %(AdditionalOptions) Document true true par2cmdline-turbo-1.4.0/parpar/gf16/000077500000000000000000000000001514221355600171015ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/parpar/gf16/controller.cpp000066400000000000000000000355311514221355600217770ustar00rootroot00000000000000#include "controller.h" #include "../src/platform.h" #include "gfmat_coeff.h" #include #include PAR2Proc::PAR2Proc() IF_LIBUV(: endSignalled(false)) { gfmat_init(); } bool PAR2Proc::init(size_t sliceSize, const std::vector& _backends IF_LIBUV(, const PAR2ProcCompleteCb& _progressCb)) { #ifdef USE_LIBUV progressCb = _progressCb; finishCb = nullptr; #endif hasAdded = false; currentSliceSize = sliceSize; // TODO: better distribution backends.resize(_backends.size()); for(unsigned i=0; i<_backends.size(); i++) { size_t size = _backends[i].size; auto& backend = backends[i]; backend.currentSliceSize = size; backend.allocSliceSize = size; backend.currentOffset = _backends[i].offset; backend.be = _backends[i].be; backend.be->setSliceSize(size); #ifdef USE_LIBUV backend.be->setProgressCb([this](int numInputs) { this->onBackendProcess(numInputs); }); #endif } return checkBackendAllocation(); } bool PAR2Proc::checkBackendAllocation() { // check ranges of backends (could maybe make this more optimal with a heap, but I expect few devices, so good enough for now) // determine if we're covering the full slice, and whether there are overlaps (overlap = dynamic scheduling) size_t start = backends[0].currentOffset, end = backends[0].currentOffset+backends[0].currentSliceSize; bool hasOverlap = false; std::vector beChecked(backends.size()); int beUnchecked = backends.size()-1; beChecked[0] = true; while(beUnchecked) { bool beFound = false; for(unsigned i=1; i= start) { if(currentEnd > start) hasOverlap = true; start = backend.currentOffset; if(currentEnd > end) end = currentEnd; } else if(currentEnd >= end && backend.currentOffset <= end) { if(backend.currentOffset < end) hasOverlap = true; end = currentEnd; if(backend.currentOffset < start) start = backend.currentOffset; // this shouldn't be possible I think } else if(backend.currentOffset > start && currentEnd < end) { hasOverlap = true; } else continue; // found a connecting backend beChecked[i] = true; beUnchecked--; beFound = true; // ensure alignment to 16-bit words if(backend.currentOffset & 1) return false; if((backend.currentSliceSize & 1) && backend.currentOffset+backend.currentSliceSize != currentSliceSize) return false; } if(!beFound) return false; } if(hasOverlap) return false; // TODO: eventually support overlapping return (start == 0 && end == currentSliceSize); // fail if backends don't cover the entire slice } // this just reduces the size without resizing backends; TODO: this should be removed bool PAR2Proc::setCurrentSliceSize(size_t newSliceSize) { if(backends.size() == 1) { // one backend only - don't need to worry about distributing the slice currentSliceSize = newSliceSize; backends[0].currentSliceSize = currentSliceSize; backends[0].allocSliceSize = (std::max)(currentSliceSize, backends[0].allocSliceSize); assert(backends[0].currentOffset == 0); return backends[0].be->setCurrentSliceSize(currentSliceSize); } if(newSliceSize > currentSliceSize) { // check if requested amount exceeds initial allocation size_t totalAlloc = 0; for(const auto& backend : backends) totalAlloc += backend.allocSliceSize; if(newSliceSize > totalAlloc) return false; // backends support upsizing, but we don't know how to reallocate the split, so don't allow it for now } currentSliceSize = newSliceSize; bool success = true; size_t pos = 0; for(auto& backend : backends) { backend.currentSliceSize = (std::min)(currentSliceSize-pos, backend.allocSliceSize); backend.currentOffset = pos; success = success && backend.be->setCurrentSliceSize(backend.currentSliceSize); pos += backend.currentSliceSize; } return success; } bool PAR2Proc::setCurrentSliceSize(size_t newSliceSize, const std::vector>& sizeAlloc) { if(backends.size() != sizeAlloc.size()) return false; currentSliceSize = newSliceSize; bool success = true; const auto* alloc = sizeAlloc.data(); for(auto& backend : backends) { backend.currentSliceSize = alloc->second; backend.currentOffset = alloc->first; success = success && backend.be->setCurrentSliceSize(backend.currentSliceSize); alloc++; } return checkBackendAllocation(); } bool PAR2Proc::setRecoverySlices(unsigned numSlices, const uint16_t* exponents) { // TODO: consider throwing if numSlices > previously set, or some mechanism to resize buffer // TODO: may eventually consider splitting by recovery, but for now, just pass through // - though we may still need a way to allocate different recovery to different backends (don't want to split slices to finely) bool success = true; for(auto& backend : backends) success = success && backend.be->setRecoverySlices(numSlices, exponents); return success; } PAR2ProcBackendAddResult PAR2Proc::canAdd() const { bool hasEmpty = false, hasBusy = false, hasFull = false; for(const auto& backend : backends) { auto state = backend.be->canAdd(); if(state == PROC_ADD_OK) hasEmpty = true; if(state == PROC_ADD_OK_BUSY) hasBusy = true; if(state == PROC_ADD_FULL) hasFull = true; } if(!hasEmpty && !hasBusy && hasFull) return PROC_ADD_ALL_FULL; if(hasEmpty && !hasBusy && !hasFull) return PROC_ADD_OK; if(hasFull) return PROC_ADD_FULL; return PROC_ADD_OK_BUSY; } #ifndef USE_LIBUV void PAR2Proc::waitForAdd() { for(auto& backend : backends) backend.be->waitForAdd(); } #endif #ifdef USE_LIBUV template bool PAR2Proc::_addInput(const void* buffer, size_t size, uint16_t inputRef, T inputNumOrCoeffs, bool flush, const PAR2ProcPlainCb& cb) { IF_LIBUV(assert(!endSignalled)); auto cbRef = addCbRefs.find(inputRef); if(cbRef != addCbRefs.end()) { cbRef->second.cb = cb; } else { cbRef = addCbRefs.emplace(std::make_pair(inputRef, PAR2ProcAddCbRef{ (int)backends.size(), cb, [this, inputRef]() { auto itRef = addCbRefs.find(inputRef); auto& ref = itRef->second; if(--ref.backendsActive == 0) { auto cb = ref.cb; addCbRefs.erase(itRef); if(cb) cb(); } } })).first; for(auto& backend : backends) { size_t amount = (std::min)(size-backend.currentOffset, backend.currentSliceSize); if(backend.currentOffset >= size || amount == 0) cbRef->second.backendsActive--; } } // if the last add was unsuccessful, we assume that failed add is now being resent // TODO: consider some better system - e.g. it may be worthwhile allowing accepting backends to continue to get new buffers? or perhaps use this as an opportunity to size up the size? bool success = true; for(auto& backend : backends) { if(backend.currentOffset >= size) continue; size_t amount = (std::min)(size-backend.currentOffset, backend.currentSliceSize); if(amount == 0) continue; if(backend.added.find(inputRef) == backend.added.end()) { bool canAdd = backend.be->canAdd() != PROC_ADD_FULL; if(canAdd) backend.be->addInput(static_cast(buffer) + backend.currentOffset, amount, inputNumOrCoeffs, flush, cbRef->second.backendCb); success = success && canAdd; if(canAdd) backend.added.insert(inputRef); } } if(success) { hasAdded = true; for(auto& backend : backends) backend.added.erase(inputRef); // have seen the above line segfault in qemu-user RV64, but not if changed to `backend.added.erase(backend.added.find(inputRef))` - don't understand why, maybe dodgy C++ runtime? } return success; } bool PAR2Proc::addInput(const void* buffer, size_t size, uint16_t inputNum, bool flush, const PAR2ProcPlainCb& cb) { return _addInput(buffer, size, inputNum, inputNum, flush, cb); } bool PAR2Proc::addInput(const void* buffer, size_t size, const uint16_t* coeffs, bool flush, const PAR2ProcPlainCb& cb) { // use first coefficient as reference return _addInput(buffer, size, coeffs[0], coeffs, flush, cb); } #else static std::future combine_futures(std::vector>&& futures) { return std::async(std::launch::async, [](std::vector>&& futures) { for(auto& f : futures) f.get(); }, std::move(futures)); } static std::future combine_futures_and(std::vector>&& futures) { return std::async(std::launch::async, [](std::vector>&& futures) -> bool { bool result = true; for(auto& f : futures) result = result && f.get(); return result; }, std::move(futures)); } template std::future PAR2Proc::_addInput(const void* buffer, size_t size, T inputNumOfCoeffs, bool flush) { std::vector> addFutures; addFutures.reserve(backends.size()); for(auto& backend : backends) { if(backend.currentOffset >= size) continue; size_t amount = (std::min)(size-backend.currentOffset, backend.currentSliceSize); if(amount == 0) continue; addFutures.push_back(backend.be->addInput(static_cast(buffer) + backend.currentOffset, amount, inputNumOfCoeffs, flush)); } hasAdded = true; return combine_futures(std::move(addFutures)); } FUTURE_RETURN_T PAR2Proc::addInput(const void* buffer, size_t size, uint16_t inputNum, bool flush) { return _addInput(buffer, size, inputNum, flush); } FUTURE_RETURN_T PAR2Proc::addInput(const void* buffer, size_t size, const uint16_t* coeffs, bool flush) { return _addInput(buffer, size, coeffs, flush); } #endif bool PAR2Proc::dummyInput(size_t size, uint16_t inputNum, bool flush) { IF_LIBUV(assert(!endSignalled)); bool success = true; for(auto& backend : backends) { if(backend.currentOffset >= size || backend.currentSliceSize == 0) continue; if(backend.added.find(inputNum) == backend.added.end()) { bool canAdd = backend.be->canAdd() != PROC_ADD_FULL; if(canAdd) backend.be->dummyInput(inputNum, flush); success = success && canAdd; if(canAdd) backend.added.insert(inputNum); } } if(success) { hasAdded = true; for(auto& backend : backends) backend.added.erase(inputNum); } return success; } bool PAR2Proc::fillInput(const void* buffer, size_t size) { IF_LIBUV(assert(!endSignalled)); bool finished = true; for(auto& backend : backends) { if(backend.currentOffset >= size || backend.currentSliceSize == 0) continue; if(backend.added.find(-1) == backend.added.end()) { bool fillSuccessful = backend.be->fillInput(static_cast(buffer) + backend.currentOffset); finished = finished && fillSuccessful; if(fillSuccessful) backend.added.insert(-1); } } return finished; } void PAR2Proc::flush() { for(auto& backend : backends) if(backend.currentSliceSize > 0) backend.be->flush(); } FUTURE_RETURN_T PAR2Proc::endInput(IF_LIBUV(const PAR2ProcPlainCb& _finishCb)) { #ifdef USE_LIBUV assert(!endSignalled); flush(); finishCb = _finishCb; bool allIsEmpty = true; for(auto& backend : backends) { if(backend.currentSliceSize == 0) continue; backend.be->endInput(); allIsEmpty = allIsEmpty && backend.be->isEmpty(); } endSignalled = true; if(allIsEmpty) processing_finished(); #else flush(); std::vector> futures; for(auto& backend : backends) { if(backend.currentSliceSize == 0) continue; futures.push_back(backend.be->endInput()); // this will also call processing_finished when appropriate } return combine_futures(std::move(futures)); #endif } FUTURE_RETURN_BOOL_T PAR2Proc::getOutput(unsigned index, void* output IF_LIBUV(, const PAR2ProcOutputCb& cb)) const { #ifdef USE_LIBUV if(!hasAdded) { // no recovery was computed -> zero fill result memset(output, 0, currentSliceSize); cb(true); return; } auto* cbRef = new int(backends.size()); for(const auto& backend : backends) { if(backend.currentSliceSize == 0) (*cbRef)--; } auto* allValid = new bool(true); for(auto& backend : backends) { if(backend.currentSliceSize == 0) continue; auto outputPtr = static_cast(output) + backend.currentOffset; if(!backend.be->_hasAdded()) { // no computation done on backend -> zero fill part memset(outputPtr, 0, backend.currentSliceSize); if(--(*cbRef) == 0) { delete cbRef; cb(*allValid); delete allValid; } } else { // TODO: for overlapping regions, need to do a xor-merge pass backend.be->getOutput(index, outputPtr, [cbRef, allValid, cb](bool valid) { *allValid = *allValid && valid; if(--(*cbRef) == 0) { delete cbRef; cb(*allValid); delete allValid; } }); } } #else if(!hasAdded) { // no recovery was computed -> zero fill result memset(output, 0, currentSliceSize); std::promise prom; prom.set_value(true); return prom.get_future(); } std::vector> outFutures; outFutures.reserve(backends.size()); for(auto& backend : backends) { if(backend.currentSliceSize == 0) continue; auto outputPtr = static_cast(output) + backend.currentOffset; if(!backend.be->_hasAdded()) { // no computation done on backend -> zero fill part memset(outputPtr, 0, backend.currentSliceSize); } else { outFutures.push_back(backend.be->getOutput(index, outputPtr)); } } return combine_futures_and(std::move(outFutures)); #endif } #ifdef USE_LIBUV void PAR2Proc::onBackendProcess(int numInputs) { // since we need to invoke the callback for each backend which completes (for adds to continue), this means this isn't exactly 'progress' any more // TODO: consider renaming if(progressCb) progressCb(numInputs); if(endSignalled) { bool allIsEmpty = true; for(auto& backend : backends) if(!backend.be->isEmpty()) { allIsEmpty = false; break; } if(allIsEmpty) processing_finished(); } } void PAR2Proc::processing_finished() { endSignalled = false; for(auto& backend : backends) if(backend.currentSliceSize > 0) backend.be->processing_finished(); if(finishCb) finishCb(); finishCb = nullptr; } void PAR2Proc::deinit(PAR2ProcPlainCb cb) { auto* cnt = new int(backends.size()); for(auto& backend : backends) backend.be->deinit([cnt, cb]() { if(--(*cnt) == 0) { delete cnt; cb(); } }); } struct PAR2ProcBackendCloseData { PAR2ProcPlainCb cb; int refCount; }; void IPAR2ProcBackend::deinit(PAR2ProcPlainCb cb) { if(pendingOutCallbacks) { deinitCallback = cb; return; } if(!loop) return; loop = nullptr; _deinit(); auto* freeData = new struct PAR2ProcBackendCloseData; freeData->cb = cb; freeData->refCount = 3; auto closeCb = [](void* data) { auto* freeData = static_cast(data); if(--(freeData->refCount) == 0) { freeData->cb(); delete freeData; } }; _queueSent.close(freeData, closeCb); _queueProc.close(freeData, closeCb); _queueRecv.close(freeData, closeCb); } void IPAR2ProcBackend::deinit() { assert(pendingOutCallbacks == 0); if(!loop) return; loop = nullptr; _deinit(); _queueSent.close(); _queueRecv.close(); _queueProc.close(); } #endif par2cmdline-turbo-1.4.0/parpar/gf16/controller.h000066400000000000000000000234261514221355600214440ustar00rootroot00000000000000#ifndef __GF16_CONTROLLER #define __GF16_CONTROLLER #include "../src/stdint.h" #include #include #include #include #include #include "threadqueue.h" #ifdef USE_LIBUV // callback types typedef std::function PAR2ProcPlainCb; typedef std::function PAR2ProcOutputCb; typedef std::function PAR2ProcCompleteCb; #define FUTURE_RETURN_T void #define FUTURE_RETURN_BOOL_T void #define IF_LIBUV(...) __VA_ARGS__ #define IF_NOT_LIBUV(...) #define NOTIFY_DONE(obj, q, prom, ...) obj->parent->q.notify(obj) #define NOTIFY_DECL(cb, prom) PAR2ProcPlainCb cb #define NOTIFY_BOOL_DECL(cb, prom) PAR2ProcOutputCb cb #else #include #define FUTURE_RETURN_T std::future #define FUTURE_RETURN_BOOL_T std::future #define IF_LIBUV(...) #define IF_NOT_LIBUV(...) __VA_ARGS__ #define NOTIFY_DONE(obj, q, prom, ...) (prom).set_value(__VA_ARGS__); #define NOTIFY_DECL(cb, prom) std::promise prom #define NOTIFY_BOOL_DECL(cb, prom) std::promise prom #endif // backend interface enum PAR2ProcBackendAddResult { PROC_ADD_OK, PROC_ADD_OK_BUSY, PROC_ADD_FULL, PROC_ADD_ALL_FULL // controller only }; class IPAR2ProcStaging { #ifdef USE_LIBUV bool isActive; #else std::promise prom; #endif public: #ifdef USE_LIBUV inline bool getIsActive() const { return isActive; } inline void setIsActive(bool v) { isActive = v; } #else std::shared_future promFuture; inline bool getIsActive() const { return promFuture.wait_for(std::chrono::seconds::zero()) == std::future_status::timeout; } inline void setIsActive(bool v) { if(v) { promFuture.get(); // ensure isActive is false to avoid race with discarding old promise prom = std::promise(); promFuture = prom.get_future(); } else if(getIsActive()) prom.set_value(); } #endif std::vector procCoeffs; IPAR2ProcStaging() { IF_NOT_LIBUV(promFuture = prom.get_future()); } }; class IPAR2ProcBackend { protected: #ifdef USE_LIBUV uv_loop_t* loop; // is NULL when closed #endif std::vector outputExponents; // recovery exponents bool processingAdd; virtual void run_kernel(unsigned stagingArea, unsigned numInputs) = 0; unsigned currentStagingArea, currentStagingInputs; unsigned inputBatchSize, minInBatchSize; unsigned statBatchesStarted; #ifdef USE_LIBUV unsigned stagingActiveCount, pendingInCallbacks, pendingOutCallbacks; bool endSignalled; PAR2ProcCompleteCb progressCb; PAR2ProcPlainCb deinitCallback; ThreadNotifyQueue _queueSent; ThreadNotifyQueue _queueRecv; virtual void _notifySent(void* _req) = 0; virtual void _notifyRecv(void* _req) = 0; virtual void _notifyProc(void* _req) = 0; inline void stagingActiveCount_inc() { stagingActiveCount++; } public: ThreadNotifyQueue _queueProc; inline void stagingActiveCount_dec() { stagingActiveCount--; } protected: inline unsigned stagingActiveCount_get() const { return stagingActiveCount; } #else std::atomic stagingActiveCount; static inline void _waitForAdd(IPAR2ProcStaging& area) { area.promFuture.get(); } template inline std::future _endInput(const std::vector& staging) { std::vector> futures; futures.reserve(staging.size()); for(const auto& area : staging) { futures.push_back(area.promFuture); } return std::async(std::launch::async, [this](std::vector>&& futures) { for(const auto& f : futures) f.get(); processing_finished(); }, std::move(futures)); } inline void stagingActiveCount_inc() { stagingActiveCount.fetch_add(1, std::memory_order_relaxed); } public: inline void stagingActiveCount_dec() { stagingActiveCount.fetch_sub(1, std::memory_order_relaxed); } protected: inline unsigned stagingActiveCount_get() const { return stagingActiveCount.load(std::memory_order_relaxed); } #endif virtual void _deinit() = 0; public: #ifdef USE_LIBUV IPAR2ProcBackend(uv_loop_t* _loop) : loop(_loop), stagingActiveCount(0), pendingInCallbacks(0), pendingOutCallbacks(0), endSignalled(false), progressCb(nullptr), deinitCallback(nullptr) , _queueSent(_loop, this, &IPAR2ProcBackend::_notifySent) , _queueRecv(_loop, this, &IPAR2ProcBackend::_notifyRecv) , _queueProc(_loop, this, &IPAR2ProcBackend::_notifyProc) #else IPAR2ProcBackend() : stagingActiveCount(0) #endif {} int getNumRecoverySlices() const { return outputExponents.size(); } virtual void setSliceSize(size_t size) = 0; #ifdef USE_LIBUV void setProgressCb(const PAR2ProcCompleteCb& _progressCb) { progressCb = _progressCb; } #endif virtual bool setCurrentSliceSize(size_t size) = 0; virtual bool setRecoverySlices(unsigned numSlices, const uint16_t* exponents = nullptr) = 0; virtual PAR2ProcBackendAddResult canAdd() const = 0; virtual FUTURE_RETURN_T addInput(const void* buffer, size_t size, uint16_t inputNum, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)) = 0; virtual FUTURE_RETURN_T addInput(const void* buffer, size_t size, const uint16_t* coeffs, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)) = 0; virtual void dummyInput(uint16_t inputNum, bool flush = false) = 0; virtual bool fillInput(const void* buffer) = 0; virtual void flush() = 0; #ifdef USE_LIBUV FUTURE_RETURN_T endInput() { endSignalled = true; } #else virtual FUTURE_RETURN_T endInput() = 0; #endif // NOTE: for !defined(USE_LIBUV), this may return empty before completion, as the isActive variable is used for synchronisation (not stagingActiveCount) bool isEmpty() const { return stagingActiveCount_get()==0 IF_LIBUV(&& pendingInCallbacks==0); } virtual FUTURE_RETURN_BOOL_T getOutput(unsigned index, void* output IF_LIBUV(, const PAR2ProcOutputCb& cb)) = 0; inline void discardOutput() { processingAdd = false; } inline bool _hasAdded() const { return processingAdd; } virtual void processing_finished() {}; #ifndef USE_LIBUV virtual void waitForAdd() = 0; #endif #ifdef USE_LIBUV void deinit(PAR2ProcPlainCb cb); void deinit(); #else inline void deinit() { _deinit(); } #endif virtual void freeProcessingMem() = 0; inline unsigned getInputBatchSize() const { return inputBatchSize; } inline unsigned getBatchesStarted() const { return statBatchesStarted; } inline void setMinInputBatchSize(unsigned size) { minInBatchSize = size ? size : inputBatchSize; } virtual ~IPAR2ProcBackend() {} }; template struct PAR2ProcBackendBaseComputeReq { uint16_t numInputs; unsigned procIdx; PClass* parent; }; struct Backend { IPAR2ProcBackend* be; size_t currentOffset; size_t currentSliceSize; size_t allocSliceSize; std::unordered_set added; }; #ifdef USE_LIBUV struct PAR2ProcAddCbRef { int backendsActive; PAR2ProcPlainCb cb; PAR2ProcPlainCb backendCb; }; #endif struct PAR2ProcBackendAlloc { IPAR2ProcBackend* be; size_t offset, size; }; class PAR2Proc { private: bool hasAdded; #ifdef USE_LIBUV std::unordered_map addCbRefs; template bool _addInput(const void* buffer, size_t size, uint16_t inputRef, T inputNumOfCoeffs, bool flush, const PAR2ProcPlainCb& cb); #else template std::future _addInput(const void* buffer, size_t size, T inputNumOfCoeffs, bool flush); #endif std::vector backends; bool checkBackendAllocation(); size_t currentSliceSize; // current slice chunk size (<=sliceSize) #ifdef USE_LIBUV bool endSignalled; void processing_finished(); PAR2ProcPlainCb finishCb; PAR2ProcCompleteCb progressCb; void onBackendProcess(int numInputs); #endif // disable copy constructor PAR2Proc(const PAR2Proc&); PAR2Proc& operator=(const PAR2Proc&); public: explicit PAR2Proc(); bool init(size_t sliceSize, const std::vector& _backends IF_LIBUV(, const PAR2ProcCompleteCb& _progressCb = nullptr)); bool setCurrentSliceSize(size_t newSliceSize); bool setCurrentSliceSize(size_t newSliceSize, const std::vector>& sizeAlloc); inline size_t getCurrentSliceSize() const { return currentSliceSize; } bool setRecoverySlices(unsigned numSlices, const uint16_t* exponents = nullptr); inline bool setRecoverySlices(const std::vector& exponents) { return setRecoverySlices(exponents.size(), exponents.data()); } inline int getNumRecoverySlices() const { // TODO: need proper number if splitting recovery blocks; for now, just use the first backend as all are the same return backends[0].be->getNumRecoverySlices(); } PAR2ProcBackendAddResult canAdd() const; #ifndef USE_LIBUV void waitForAdd(); FUTURE_RETURN_T addInput(const void* buffer, size_t size, uint16_t inputNum, bool flush = false); FUTURE_RETURN_T addInput(const void* buffer, size_t size, const uint16_t* coeffs, bool flush = false); #else bool addInput(const void* buffer, size_t size, uint16_t inputNum, bool flush, const PAR2ProcPlainCb& cb); bool addInput(const void* buffer, size_t size, const uint16_t* coeffs, bool flush, const PAR2ProcPlainCb& cb); #endif // dummyInput/fillInput is only used for benchmarking; pretends to add an input without transferring anything to the backend bool dummyInput(size_t size, uint16_t inputNum, bool flush = false); bool fillInput(const void* buffer, size_t size); void flush(); FUTURE_RETURN_T endInput(IF_LIBUV(const PAR2ProcPlainCb& _finishCb)); FUTURE_RETURN_BOOL_T getOutput(unsigned index, void* output IF_LIBUV(, const PAR2ProcOutputCb& cb)) const; inline void discardOutput() { hasAdded = false; for(auto& backend : backends) backend.be->discardOutput(); } inline void deinit() { for(auto& backend : backends) backend.be->deinit(); } #ifdef USE_LIBUV void deinit(PAR2ProcPlainCb cb); #endif inline void freeProcessingMem() { for(auto& backend : backends) backend.be->freeProcessingMem(); } }; #endif // defined(__GF16_CONTROLLER) par2cmdline-turbo-1.4.0/parpar/gf16/controller_cpu.cpp000066400000000000000000000544021514221355600226440ustar00rootroot00000000000000#include "controller_cpu.h" #include "../src/platform.h" #include "gfmat_coeff.h" #include #ifndef MIN # define MIN(a, b) ((a)<(b) ? (a) : (b)) #endif #define CEIL_DIV(a, b) (((a) + (b)-1) / (b)) #define ROUND_DIV(a, b) (((a) + ((b)>>1)) / (b)) PAR2ProcCPUStaging::~PAR2ProcCPUStaging() { if(src) ALIGN_FREE(src); } /** initialization **/ PAR2ProcCPU::PAR2ProcCPU(IF_LIBUV(uv_loop_t* _loop,) int stagingAreas) : IPAR2ProcBackend(IF_LIBUV(_loop)), sliceSize(0), numThreads(0), gf(NULL), staging(stagingAreas), memProcessing(NULL), transferThread(PAR2ProcCPU::transfer_slice) { // default number of threads = number of CPUs available setNumThreads(-1); transferThread.name = "gf_transfer"; #ifdef DEBUG_STAT_THREAD_EMPTY endSignalled = false; statWorkerIdleEvents = 0; #endif } void PAR2ProcCPU::setSliceSize(size_t _sliceSize) { sliceSize = _sliceSize; } void PAR2ProcCPU::freeGf() { for(auto& area : staging) { if(area.src) ALIGN_FREE(area.src); area.src = nullptr; area.procCoeffs.clear(); } freeProcessingMem(); if(!gfScratch.empty()) { for(unsigned i=0; imutScratch_free(gfScratch[i]); gfScratch.clear(); } delete gf; gf = NULL; } void PAR2ProcCPU::setNumThreads(int threads) { if(threads < 0) { threads = hardware_concurrency(); } numThreads = threads; if(!gf) return; int oldThreads = gfScratch.size(); if(threads == oldThreads) return; for(int i=oldThreads-1; i>=threads; i--) { if(gfScratch[i]) gf->mutScratch_free(gfScratch[i]); thWorkers[i].end(); } gfScratch.resize(threads); thWorkers.resize(threads); for(int i=oldThreads; imutScratch_alloc(); thWorkers[i].lowPrio = true; thWorkers[i].name = "gf_worker"; thWorkers[i].setCallback(PAR2ProcCPU::compute_worker); } if(alignedCurrentSliceSize) calcChunkSize(); } bool PAR2ProcCPU::init(Galois16Methods method, unsigned _inputGrouping, size_t _chunkLen) { freeGf(); bool ret = true; // TODO: accept & pass on hint info gf = new Galois16Mul(method); const Galois16MethodInfo& info = gf->info(); chunkLen = _chunkLen ? _chunkLen : info.idealChunkSize; alignment = info.alignment; stride = info.stride; inputBatchSize = _inputGrouping; if(!inputBatchSize) { // round 12 to nearest idealInputMultiple inputBatchSize = (12 + info.idealInputMultiple/2); inputBatchSize -= inputBatchSize % info.idealInputMultiple; if(inputBatchSize < info.idealInputMultiple) inputBatchSize = info.idealInputMultiple; } minInBatchSize = inputBatchSize; alignedSliceSize = gf->alignToStride(sliceSize) + stride; // add extra stride, because checksum requires an extra block alignedCurrentSliceSize = 0; for(auto& area : staging) { // setup indicators to know if buffers are being used area.setIsActive(false); } if(!reallocMemInput()) // allocate input staging area ret = false; processingAdd = false; setNumThreads(numThreads); // init scratch/workers setCurrentSliceSize(sliceSize); // default slice chunk size = declared slice size currentStagingArea = currentStagingInputs = 0; statBatchesStarted = 0; return ret; } bool PAR2ProcCPU::reallocMemInput() { bool ret = true; for(auto& area : staging) { if(area.src) ALIGN_FREE(area.src); ALIGN_ALLOC(area.src, inputBatchSize * alignedSliceSize, alignment); if(!area.src) ret = false; } return ret; } void PAR2ProcCPU::calcChunkSize() { // split the slice evenly across threads size_t targetThreadChunk = CEIL_DIV(alignedCurrentSliceSize, numThreads); // if the per-thread size is much smaller than our target, scale it up and split by output as well if(targetThreadChunk <= chunkLen/2) { numChunks = ROUND_DIV(alignedCurrentSliceSize, chunkLen); if(numChunks < 1) numChunks = 1; } else { numChunks = ROUND_DIV(targetThreadChunk, chunkLen); if(numChunks < 1) numChunks = 1; numChunks *= numThreads; } chunkLen = gf->alignToStride(CEIL_DIV(alignedCurrentSliceSize, numChunks)); // we'll assume that input chunks are memory aligned here // fix up numChunks with actual number (since it may have changed from aligning/rounding) numChunks = CEIL_DIV(alignedCurrentSliceSize, chunkLen); } bool PAR2ProcCPU::setCurrentSliceSize(size_t newSliceSize) { currentSliceSize = newSliceSize; alignedCurrentSliceSize = gf->alignToStride(currentSliceSize) + stride; // add extra stride, because checksum requires an extra block bool ret = true; if(currentSliceSize > sliceSize) { // should never happen, but we'll support this case anyway // need to upsize allocation sliceSize = currentSliceSize; alignedSliceSize = alignedCurrentSliceSize; ret = reallocMemInput(); if(memProcessing) { freeProcessingMem(); if(!outputExponents.empty()) { ALIGN_ALLOC(memProcessing, outputExponents.size() * alignedSliceSize, alignment); if(!memProcessing) ret = false; } } } calcChunkSize(); return ret; } bool PAR2ProcCPU::setRecoverySlices(unsigned numSlices, const uint16_t* exponents) { // TODO: consider throwing if numSlices > previously set, or some mechanism to resize buffer outputExponents.clear(); if(!numSlices) return true; outputExponents.resize(numSlices, 1); // default to 1 to bypass output==0 add shortcut (if we're going with custom coeffs) if(exponents) memcpy(outputExponents.data(), exponents, numSlices * sizeof(uint16_t)); for(auto& area : staging) area.procCoeffs.resize(numSlices * inputBatchSize); if(!memProcessing) { // allocate processing area // TODO: see if we can get an aligned calloc and set processingAdd = true // (investigate mmap or just use calloc and align ourself) // (will need to be careful with discard_output) ALIGN_ALLOC(memProcessing, numSlices * alignedSliceSize, alignment); } return memProcessing != nullptr; } void PAR2ProcCPU::freeProcessingMem() { if(memProcessing) { ALIGN_FREE(memProcessing); memProcessing = NULL; } } void PAR2ProcCPU::_deinit() { for(auto& worker : thWorkers) worker.end(); // TODO: join threads? freeGf(); } PAR2ProcCPU::~PAR2ProcCPU() { deinit(); } /** prepare **/ // TODO: future idea: multiple prepare threads? Not sure if there's a case where it's particularly beneficial... struct transfer_data { bool finish; // false = prepare, true = finish PAR2ProcCPU* parent; void* dst; const void* src; size_t size; unsigned index; size_t chunkLen; Galois16Mul* gf; unsigned numBufs; // prepare specific size_t dstLen; unsigned submitInBufs; unsigned inBufId; NOTIFY_DECL(cbPrep, promPrep); // finish specific NOTIFY_BOOL_DECL(cbOut, promOut); int cksumSuccess; }; // prepare thread process function void PAR2ProcCPU::transfer_slice(ThreadMessageQueue& q) { struct transfer_data* data; while((data = static_cast(q.pop())) != NULL) { if(data->finish) { data->cksumSuccess = data->gf->finish_packed_cksum(data->dst, data->src, data->size, data->numBufs, data->index, data->chunkLen); NOTIFY_DONE(data, _queueRecv, data->promOut, data->cksumSuccess); } else { if(data->src) data->gf->prepare_packed_cksum(data->dst, data->src, data->size, data->dstLen, data->numBufs, data->index, data->chunkLen); if(data->submitInBufs) { // queue async compute data->parent->run_kernel(data->inBufId, data->submitInBufs); } // signal main thread that prepare has completed NOTIFY_DONE(data, _queueSent, data->promPrep); } IF_NOT_LIBUV(delete data); } } #ifdef USE_LIBUV void PAR2ProcCPU::_notifySent(void* _req) { auto data = static_cast(_req); pendingInCallbacks--; if(data->cbPrep && data->src) data->cbPrep(); delete data; // handle possibility of _notifySent being called after the last _notifyProc if(endSignalled && isEmpty() && progressCb) progressCb(0); } #endif PAR2ProcBackendAddResult PAR2ProcCPU::canAdd() const { // NOTE: if add fails due to being full, client resubmitting may be vulnerable to race conditions if it adds an event listener after completion event gets fired if(staging[currentStagingArea].getIsActive()) return PROC_ADD_FULL; return staging[(currentStagingArea == 0 ? staging.size() : currentStagingArea)-1].getIsActive() ? PROC_ADD_OK_BUSY : PROC_ADD_OK; } #ifndef USE_LIBUV void PAR2ProcCPU::waitForAdd() { IPAR2ProcBackend::_waitForAdd(staging[currentStagingArea]); } #endif FUTURE_RETURN_T PAR2ProcCPU::addInput(const void* buffer, size_t size, uint16_t inputNum, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)) { IF_NOT_LIBUV(return) _addInput(buffer, size, inputNum, flush IF_LIBUV(, cb)); } FUTURE_RETURN_T PAR2ProcCPU::addInput(const void* buffer, size_t size, const uint16_t* coeffs, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)) { IF_NOT_LIBUV(return) _addInput(buffer, size, coeffs, flush IF_LIBUV(, cb)); } template FUTURE_RETURN_T PAR2ProcCPU::_addInput(const void* buffer, size_t size, T inputNumOrCoeffs, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)) { IF_LIBUV(assert(!endSignalled)); auto& area = staging[currentStagingArea]; assert(!area.getIsActive()); if(!staging[0].src) reallocMemInput(); set_coeffs(area, currentStagingInputs, inputNumOrCoeffs); struct transfer_data* data = new struct transfer_data; data->finish = false; data->src = buffer; data->size = size; data->parent = this; data->dst = area.src; data->dstLen = alignedCurrentSliceSize - stride; data->numBufs = inputBatchSize; data->index = currentStagingInputs++; data->chunkLen = chunkLen; data->gf = gf; IF_LIBUV(data->cbPrep = cb); data->submitInBufs = (flush || currentStagingInputs == inputBatchSize || ( // allow submitting early if there's no active processing stagingActiveCount_get() == 0 && staging.size() > 1 && currentStagingInputs >= minInBatchSize )) ? currentStagingInputs : 0; data->inBufId = currentStagingArea; if(data->submitInBufs) { stagingActiveCount_inc(); area.setIsActive(true); // lock this buffer until processing is complete statBatchesStarted++; currentStagingInputs = 0; if(++currentStagingArea == staging.size()) currentStagingArea = 0; } IF_LIBUV(pendingInCallbacks++); IF_NOT_LIBUV(auto future = data->promPrep.get_future()); transferThread.send(data); IF_NOT_LIBUV(return future); } void PAR2ProcCPU::dummyInput(uint16_t inputNum, bool flush) { IF_LIBUV(assert(!endSignalled)); auto& area = staging[currentStagingArea]; assert(!area.getIsActive()); if(!staging[0].src) reallocMemInput(); set_coeffs(area, currentStagingInputs, inputNum); currentStagingInputs++; if(flush || currentStagingInputs == inputBatchSize || ( stagingActiveCount_get() == 0 && staging.size() > 1 && currentStagingInputs >= minInBatchSize )) { stagingActiveCount_inc(); area.setIsActive(true); // lock this buffer until processing is complete statBatchesStarted++; run_kernel(currentStagingArea, currentStagingInputs); currentStagingInputs = 0; if(++currentStagingArea == staging.size()) currentStagingArea = 0; } } bool PAR2ProcCPU::fillInput(const void* buffer) { IF_LIBUV(assert(!endSignalled)); if(!staging[0].src) reallocMemInput(); gf->prepare_packed_cksum(staging[currentStagingArea].src, buffer, currentSliceSize, alignedCurrentSliceSize - stride, inputBatchSize, currentStagingInputs, chunkLen); if(++currentStagingInputs == inputBatchSize) { currentStagingInputs = 0; if(++currentStagingArea == staging.size()) { currentStagingArea = 0; return true; // all filled } } return false; } void PAR2ProcCPU::set_coeffs(PAR2ProcCPUStaging& area, unsigned idx, uint16_t inputNum) { // TODO: check if exponents have been set? uint16_t inputLog = gfmat_input_log(inputNum); auto& coeffs = area.procCoeffs; for(unsigned out=0; outfinish = false; data->src = NULL; data->parent = this; data->submitInBufs = currentStagingInputs; data->inBufId = currentStagingArea; data->gf = gf; stagingActiveCount_inc(); staging[currentStagingArea].setIsActive(true); // lock this buffer until processing is complete statBatchesStarted++; currentStagingInputs = 0; if(++currentStagingArea == staging.size()) currentStagingArea = 0; IF_LIBUV(pendingInCallbacks++); transferThread.send(data); } /** finish **/ #ifdef USE_LIBUV void PAR2ProcCPU::_notifyRecv(void* _req) { auto data = static_cast(_req); pendingOutCallbacks--; // signal output ready data->cbOut(data->cksumSuccess); delete data; if(pendingOutCallbacks < 1 && deinitCallback) deinit(deinitCallback); } #endif FUTURE_RETURN_BOOL_T PAR2ProcCPU::getOutput(unsigned index, void* output IF_LIBUV(, const PAR2ProcOutputCb& cb)) { struct transfer_data* data = new struct transfer_data; data->finish = true; data->parent = this; data->src = memProcessing; data->size = currentSliceSize; data->gf = gf; data->dst = output; data->numBufs = outputExponents.size(); data->index = index; data->chunkLen = chunkLen; #ifdef USE_LIBUV data->cbOut = cb; pendingOutCallbacks++; #else auto future = data->promOut.get_future(); #endif transferThread.send(data); IF_NOT_LIBUV(return future); } /** main processing **/ typedef struct __compute_req : PAR2ProcBackendBaseComputeReq { unsigned inputGrouping; uint16_t numOutputs; const uint16_t *outNonZero; const uint16_t* coeffs; size_t len, chunkSize, numChunks; const void* input; void* output; bool add; void* mutScratch; const Galois16Mul* gf; std::atomic* procRefs; } compute_req; void PAR2ProcCPU::compute_worker(ThreadMessageQueue& q) { compute_req* req; while((req = static_cast(q.pop())) != NULL) { const Galois16MethodInfo& gfInfo = req->gf->info(); // compute how many inputs regions get prefetched in a muladd_multi call // TODO: should this be done across all threads? unsigned inputsPrefetchedPerInvok = (req->numInputs / gfInfo.idealInputMultiple); unsigned inputPrefetchOutOffset = req->numOutputs-1; const unsigned MAX_PF_FACTOR = 3; { const unsigned pfFactor = gfInfo.prefetchDownscale; if(inputsPrefetchedPerInvok > (1U<numInputs << MAX_PF_FACTOR, inputsPrefetchedPerInvok); assert(inputPrefetchOutOffset > 0); // at least one pass needed if(req->numOutputs >= inputPrefetchOutOffset) inputPrefetchOutOffset = req->numOutputs - inputPrefetchOutOffset; else inputPrefetchOutOffset = 0; } } for(size_t round = 0; round < req->numChunks; round++) { size_t procSize = MIN(req->len-round*req->chunkSize, req->chunkSize); const char* srcPtr = static_cast(req->input) + round*req->chunkSize*req->inputGrouping; for(unsigned out = 0; out < req->numOutputs; out++) { const uint16_t* vals = req->coeffs + out*req->inputGrouping; char* dstPtr = static_cast(req->output) + out*procSize + round*req->numOutputs*req->chunkSize; if(!req->add) memset(dstPtr, 0, procSize); if(round == req->numChunks-1) { if(out+1 < req->numOutputs) { if(req->outNonZero[out]) req->gf->mul_add_multi_packpf(req->inputGrouping, req->numInputs, dstPtr, srcPtr, procSize, vals, req->mutScratch, NULL, dstPtr+procSize); else req->gf->add_multi_packpf(req->inputGrouping, req->numInputs, dstPtr, srcPtr, procSize, NULL, dstPtr+procSize); } else // TODO: this could also be a 0 output, so consider add_multi optimisation? req->gf->mul_add_multi_packed(req->inputGrouping, req->numInputs, dstPtr, srcPtr, procSize, vals, req->mutScratch); } else { const char* pfInput = out >= inputPrefetchOutOffset ? static_cast(req->input) + (round+1)*req->chunkSize*req->inputGrouping + ((inputsPrefetchedPerInvok*(out-inputPrefetchOutOffset)*procSize)>>MAX_PF_FACTOR) : NULL; // procSize input prefetch may be wrong for final round, but it's the closest we've got; TODO: perhaps consider skipping out of prefetching, if the final round has a different region size if(req->outNonZero[out]) req->gf->mul_add_multi_packpf(req->inputGrouping, req->numInputs, dstPtr, srcPtr, procSize, vals, req->mutScratch, pfInput, dstPtr+procSize); else req->gf->add_multi_packpf(req->inputGrouping, req->numInputs, dstPtr, srcPtr, procSize, pfInput, dstPtr+procSize); } } } // TODO: allow worker to peek into next queue entry for prefetching? #ifdef DEBUG_STAT_THREAD_EMPTY if(q.empty() && !(req->parent->endSignalled IF_NOT_LIBUV(.load(std::memory_order_relaxed)))) req->parent->statWorkerIdleEvents.fetch_add(1, std::memory_order_relaxed); #endif // mark that we've done processing this request if(req->procRefs->fetch_sub(1, std::memory_order_acq_rel) <= 1) { // ensure all prior memory operations to be complete at this point; even though a cross-thread signal requires stricter ordering, it's only guaranteed on the sending thread // signal this input group is done with #ifdef USE_LIBUV req->parent->_queueProc.notify(req); #else req->parent->stagingActiveCount_dec(); req->parent->_setAreaActive(req->procIdx, false); delete req; #endif } else delete req; } } void PAR2ProcCPU::run_kernel(unsigned inBuf, unsigned numInputs) { if(outputExponents.empty()) return; auto& area = staging[inBuf]; // currently do static distribution; TODO: consider dynamic scheduling? bool oldProcessingAdd = processingAdd; processingAdd = true; auto makeReq = [&, this](unsigned thread, size_t sliceOffset) -> compute_req* { compute_req* req = new compute_req; req->numInputs = numInputs; req->inputGrouping = inputBatchSize; req->chunkSize = chunkLen; req->input = static_cast(area.src) + sliceOffset*inputBatchSize; req->add = oldProcessingAdd; req->mutScratch = gfScratch[thread]; // TODO: should this be assigned to the thread instead? req->gf = gf; req->parent = this; req->procRefs = &(area.procRefs); req->procIdx = inBuf; return req; }; // distribute chunks evenly across threads. For remaining chunks, try to distribute the outputs evenly across threads, but don't allow a thread to handle more than one remaining chunk size_t fullChunksPerThread = numChunks / numThreads; unsigned leftoverChunks = numChunks % numThreads; size_t chunk = 0; // start off with remaining chunks if(leftoverChunks) { // send each chunk to this many threads unsigned threadsPerChunk = MIN(numThreads / leftoverChunks, outputExponents.size()); assert(threadsPerChunk >= 1); // number of outputs to send to a thread (this will be rounded appropriately as it's processed) float outputsPerThread = (float)outputExponents.size() / threadsPerChunk; assert(outputsPerThread >= 1); // number of threads that we'll send remaining chunks to unsigned usedThreads = threadsPerChunk * leftoverChunks; assert((int)usedThreads <= numThreads); area.procRefs.store((fullChunksPerThread ? numThreads : 0) + usedThreads, std::memory_order_relaxed); unsigned thread = 0; for(; chunk < leftoverChunks; chunk++) { size_t sliceOffset = chunk*chunkLen; size_t reqLen = MIN(alignedCurrentSliceSize-sliceOffset, chunkLen); char* outputBase = static_cast(memProcessing) + sliceOffset*outputExponents.size(); // split this chunk across threads unsigned outputIdx = 0; for(unsigned tc = 0; tc < threadsPerChunk; tc++) { assert(thread < usedThreads); auto req = makeReq(thread, sliceOffset); req->numOutputs = (unsigned)(outputsPerThread*(tc+1) + 0.5) - outputIdx; assert(req->numOutputs >= 1); req->outNonZero = outputExponents.data() + outputIdx; req->coeffs = area.procCoeffs.data() + outputIdx * inputBatchSize; req->len = reqLen; req->numChunks = 1; req->output = outputBase + outputIdx*req->len; outputIdx += req->numOutputs; if(tc == threadsPerChunk-1) assert(outputIdx == outputExponents.size()); thWorkers[thread].send(req); thread++; } } } else area.procRefs.store(numThreads, std::memory_order_relaxed); // distribute chunks evenly across threads if(fullChunksPerThread) { for(int thread=0; threadnumOutputs = outputExponents.size(); req->outNonZero = outputExponents.data(); req->coeffs = area.procCoeffs.data(); req->len = MIN(alignedCurrentSliceSize-sliceOffset, chunkLen*fullChunksPerThread); req->numChunks = fullChunksPerThread; req->output = static_cast(memProcessing) + sliceOffset*outputExponents.size(); thWorkers[thread].send(req); chunk += fullChunksPerThread; } } assert(chunk == numChunks); } #ifdef USE_LIBUV void PAR2ProcCPU::_notifyProc(void* _req) { auto req = static_cast(_req); stagingActiveCount_dec(); staging[req->procIdx].setIsActive(false); // if add was blocked, allow adds to continue - calling application will need to listen to this event to know to continue if(progressCb) progressCb(req->numInputs); /* // TODO: implement for non-libuv if we go ahead with this // this is currently pointless while minInBatchSize == inputBatchSize if(currentStagingInputs && stagingActiveCount_get() == 0 && staging.size() > 1 && currentStagingInputs >= minInBatchSize) { // TODO: consider firing off next batch of inputs } */ // TODO: also consider idea of dynamically scaling areas - this removes the notion of a 'full' state, and allows caller to manage maximum outstanding regions delete req; } #endif void PAR2ProcCPU::processing_finished() { #if defined(USE_LIBUV) || defined(DEBUG_STAT_THREAD_EMPTY) endSignalled = false; #endif // free memInput so that output fetching can use some of it for(auto& area : staging) { if(area.src) ALIGN_FREE(area.src); area.src = nullptr; } } par2cmdline-turbo-1.4.0/parpar/gf16/controller_cpu.h000066400000000000000000000106161514221355600223100ustar00rootroot00000000000000#ifndef __GF16_CONTROLLER_CPU #define __GF16_CONTROLLER_CPU #include "controller.h" #include #include "threadqueue.h" #include "gf16mul.h" class PAR2ProcCPUStaging : public IPAR2ProcStaging { public: void* src; std::atomic procRefs; PAR2ProcCPUStaging() : IPAR2ProcStaging(), src(nullptr) {} ~PAR2ProcCPUStaging(); }; class PAR2ProcCPU : public IPAR2ProcBackend { private: size_t sliceSize; // actual whole slice size size_t alignedSliceSize; // allocated memory for slice (>=sliceSize) size_t currentSliceSize; // current slice chunk size (<=sliceSize) size_t alignedCurrentSliceSize; // memory used for current slice chunk (<=alignedSliceSize) int numThreads; std::vector thWorkers; // main processing worker threads std::vector gfScratch; // scratch memory for each thread Galois16Mul* gf; size_t chunkLen; // loop tiling size size_t numChunks; unsigned alignment; unsigned stride; void freeGf(); // staging area from which processing is performed std::vector staging; bool reallocMemInput(); void* memProcessing; // TODO: break this into chunks, to avoid massive single allocation void calcChunkSize(); MessageThread transferThread; void set_coeffs(PAR2ProcCPUStaging& area, unsigned idx, uint16_t inputNum); void set_coeffs(PAR2ProcCPUStaging& area, unsigned idx, const uint16_t* inputCoeffs); void run_kernel(unsigned inBuf, unsigned numInputs) override; template FUTURE_RETURN_T _addInput(const void* buffer, size_t size, T inputNumOrCoeffs, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)); #ifdef USE_LIBUV void _notifySent(void* _req) override; void _notifyRecv(void* _req) override; void _notifyProc(void* _req) override; #endif static void transfer_slice(ThreadMessageQueue& q); static void compute_worker(ThreadMessageQueue& q); #ifdef DEBUG_STAT_THREAD_EMPTY std::atomic endSignalled; std::atomic statWorkerIdleEvents; #endif // disable copy constructor PAR2ProcCPU(const PAR2ProcCPU&); PAR2ProcCPU& operator=(const PAR2ProcCPU&); public: explicit PAR2ProcCPU(IF_LIBUV(uv_loop_t* _loop,) int stagingAreas=2); void setSliceSize(size_t _sliceSize) override; void _deinit() override; ~PAR2ProcCPU(); bool init(Galois16Methods method = GF16_AUTO, unsigned inputGrouping = 0, size_t chunkLen = 0); bool setCurrentSliceSize(size_t newSliceSize) override; bool setRecoverySlices(unsigned numSlices, const uint16_t* exponents = NULL) override; void freeProcessingMem() override; inline void _setAreaActive(int area, bool active) { staging[area].setIsActive(active); } void setNumThreads(int threads); inline int getNumThreads() const { return numThreads; } inline const char* getMethodName() const { return gf->info().name; } inline size_t getChunkLen() const { return chunkLen; } inline unsigned getStagingAreas() const { return staging.size(); } inline unsigned getAlignment() const { return alignment; } inline unsigned getStride() const { return stride; } inline size_t getAllocSliceSize() const { return alignedSliceSize; } PAR2ProcBackendAddResult canAdd() const override; FUTURE_RETURN_T addInput(const void* buffer, size_t size, uint16_t inputNum, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)) override; FUTURE_RETURN_T addInput(const void* buffer, size_t size, const uint16_t* coeffs, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)) override; void dummyInput(uint16_t inputNum, bool flush = false) override; bool fillInput(const void* buffer) override; void flush() override; FUTURE_RETURN_BOOL_T getOutput(unsigned index, void* output IF_LIBUV(, const PAR2ProcOutputCb& cb)) override; void processing_finished() override; #ifndef USE_LIBUV void waitForAdd() override; FUTURE_RETURN_T endInput() override { # ifdef DEBUG_STAT_THREAD_EMPTY endSignalled = true; # endif return IPAR2ProcBackend::_endInput(staging); } #endif static inline Galois16Methods default_method() { return Galois16Mul::default_method(); } static inline Galois16MethodInfo info(Galois16Methods method) { return Galois16Mul::info(method); } static inline std::vector availableMethods() { return Galois16Mul::availableMethods(true); } #ifdef DEBUG_STAT_THREAD_EMPTY inline unsigned getWorkerIdleCount() const { return statWorkerIdleEvents.load(std::memory_order_relaxed); } #endif }; #endif // defined(__GF16_CONTROLLER_CPU) par2cmdline-turbo-1.4.0/parpar/gf16/controller_ocl.cpp000066400000000000000000000630401514221355600226300ustar00rootroot00000000000000#include #include "../src/stdint.h" #include // free / calloc #include #include "controller_ocl.h" std::vector PAR2ProcOCL::platforms; int PAR2ProcOCL::load_runtime() { if(load_opencl()) { return 1; } try { cl::Platform::get(&platforms); if(platforms.size() == 0) return 2; } catch(cl::Error const&) { return 2; } return 0; } void PAR2ProcOCL::printError(const char* where, cl::Error const& err) { #ifndef GF16OCL_NO_OUTPUT std::cerr << "OpenCL " << where << ": "; switch(err.err()) { #define _PRINT_ERR(x) case x: std::cerr << #x; break _PRINT_ERR(CL_DEVICE_NOT_FOUND); _PRINT_ERR(CL_DEVICE_NOT_AVAILABLE); _PRINT_ERR(CL_COMPILER_NOT_AVAILABLE); _PRINT_ERR(CL_MEM_OBJECT_ALLOCATION_FAILURE); _PRINT_ERR(CL_OUT_OF_RESOURCES); _PRINT_ERR(CL_OUT_OF_HOST_MEMORY); _PRINT_ERR(CL_PROFILING_INFO_NOT_AVAILABLE); _PRINT_ERR(CL_MEM_COPY_OVERLAP); _PRINT_ERR(CL_IMAGE_FORMAT_MISMATCH); _PRINT_ERR(CL_IMAGE_FORMAT_NOT_SUPPORTED); _PRINT_ERR(CL_BUILD_PROGRAM_FAILURE); _PRINT_ERR(CL_MAP_FAILURE); #ifdef CL_VERSION_1_1 _PRINT_ERR(CL_MISALIGNED_SUB_BUFFER_OFFSET); _PRINT_ERR(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST); #endif #ifdef CL_VERSION_1_2 _PRINT_ERR(CL_COMPILE_PROGRAM_FAILURE); _PRINT_ERR(CL_LINKER_NOT_AVAILABLE); _PRINT_ERR(CL_LINK_PROGRAM_FAILURE); _PRINT_ERR(CL_DEVICE_PARTITION_FAILED); _PRINT_ERR(CL_KERNEL_ARG_INFO_NOT_AVAILABLE); #endif _PRINT_ERR(CL_INVALID_VALUE); _PRINT_ERR(CL_INVALID_DEVICE_TYPE); _PRINT_ERR(CL_INVALID_PLATFORM); _PRINT_ERR(CL_INVALID_DEVICE); _PRINT_ERR(CL_INVALID_CONTEXT); _PRINT_ERR(CL_INVALID_QUEUE_PROPERTIES); _PRINT_ERR(CL_INVALID_COMMAND_QUEUE); _PRINT_ERR(CL_INVALID_HOST_PTR); _PRINT_ERR(CL_INVALID_MEM_OBJECT); _PRINT_ERR(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR); _PRINT_ERR(CL_INVALID_IMAGE_SIZE); _PRINT_ERR(CL_INVALID_SAMPLER); _PRINT_ERR(CL_INVALID_BINARY); _PRINT_ERR(CL_INVALID_BUILD_OPTIONS); _PRINT_ERR(CL_INVALID_PROGRAM); _PRINT_ERR(CL_INVALID_PROGRAM_EXECUTABLE); _PRINT_ERR(CL_INVALID_KERNEL_NAME); _PRINT_ERR(CL_INVALID_KERNEL_DEFINITION); _PRINT_ERR(CL_INVALID_KERNEL); _PRINT_ERR(CL_INVALID_ARG_INDEX); _PRINT_ERR(CL_INVALID_ARG_VALUE); _PRINT_ERR(CL_INVALID_ARG_SIZE); _PRINT_ERR(CL_INVALID_KERNEL_ARGS); _PRINT_ERR(CL_INVALID_WORK_DIMENSION); _PRINT_ERR(CL_INVALID_WORK_GROUP_SIZE); _PRINT_ERR(CL_INVALID_WORK_ITEM_SIZE); _PRINT_ERR(CL_INVALID_GLOBAL_OFFSET); _PRINT_ERR(CL_INVALID_EVENT_WAIT_LIST); _PRINT_ERR(CL_INVALID_EVENT); _PRINT_ERR(CL_INVALID_OPERATION); _PRINT_ERR(CL_INVALID_GL_OBJECT); _PRINT_ERR(CL_INVALID_BUFFER_SIZE); _PRINT_ERR(CL_INVALID_MIP_LEVEL); _PRINT_ERR(CL_INVALID_GLOBAL_WORK_SIZE); #ifdef CL_VERSION_1_1 _PRINT_ERR(CL_INVALID_PROPERTY); #endif #ifdef CL_VERSION_1_2 _PRINT_ERR(CL_INVALID_IMAGE_DESCRIPTOR); _PRINT_ERR(CL_INVALID_COMPILER_OPTIONS); _PRINT_ERR(CL_INVALID_LINKER_OPTIONS); _PRINT_ERR(CL_INVALID_DEVICE_PARTITION_COUNT); #endif #undef _PRINT_ERR default: std::cerr << err.err(); } std::cerr << std::endl; #endif } static int parse_ocl_version(const std::string& ver) { // OpenCL gives the string "OpenCL x.y [extra]" const char* p = ver.c_str() + 7; int major = 0, minor = 0; int* majmin = &major; while(1) { if(*p == '.') majmin = &minor; else if(*p >= '0' && *p <= '9') { *majmin *= 10; *majmin += *p - '0'; } else break; p++; } // note that we assume 1.20 is newer than 1.2, i.e. not interpreted as a floating point value return major * 1000 + minor; } PAR2ProcOCL::PAR2ProcOCL(IF_LIBUV(uv_loop_t* _loop,) int platformId, int deviceId, int stagingAreas) : IPAR2ProcBackend(IF_LIBUV(_loop)), staging(stagingAreas), allocatedSliceSize(0), transferThread(PAR2ProcOCL::transfer_slice) { _initSuccess = false; transferThread.name = "ocl_transfer"; #ifdef USE_LIBUV #define ERROR_EXIT { loop = nullptr; return; } #else #define ERROR_EXIT return; #endif if(!getDevice(device, platformId, deviceId)) ERROR_EXIT context = cl::Context(device); // we only support little-endian hosts and devices #if !defined(_MSC_VER) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ ERROR_EXIT #endif if(device.getInfo() != CL_TRUE) ERROR_EXIT try { queue = cl::CommandQueue(context, device, 0); _initSuccess = true; } catch(cl::Error const& err) { printError("Create Queue Error", err); } #undef ERROR_EXIT queueEvents.reserve(2); _deviceId = deviceId; oclDevVersion = parse_ocl_version(device.getInfo()); cl::Platform platform; getPlatform(platform, platformId); oclPlatVersion = parse_ocl_version(platform.getInfo()); } PAR2ProcOCL::~PAR2ProcOCL() { deinit(); } // _sliceSize must be divisible by 2 void PAR2ProcOCL::setSliceSize(size_t _sliceSize) { sliceSize = _sliceSize; if(gf) sliceSizeCksum = _sliceSize + gf->info().cksumSize; } bool PAR2ProcOCL::init(Galois16OCLMethods method, unsigned targetInputBatch, unsigned targetIters, unsigned targetGrouping, Galois16Methods cksumMethod) { if(!_initSuccess) return false; outputExponents.clear(); if(method == GF16OCL_AUTO) method = default_method(); _setupMethod = method; _setupTargetInputBatch = targetInputBatch; _setupTargetIters = targetIters; _setupTargetGrouping = targetGrouping; reset_state(); coeffType = GF16OCL_COEFF_NORMAL; statBatchesStarted = 0; if(!gf || gfMethod != cksumMethod) gf.reset(new Galois16Mul(cksumMethod)); gfMethod = cksumMethod; sliceSizeCksum = sliceSize + gf->info().cksumSize; outputsInterleaved = 1; return true; } bool PAR2ProcOCL::setRecoverySlices(unsigned _numOutputs, const uint16_t* outputExp) { if(_numOutputs == 0) { outputExponents.clear(); return true; } // check if coeffs are sequential bool coeffIsSeq = false; if(coeffType != GF16OCL_COEFF_LOG && outputExp) { coeffIsSeq = true; uint16_t coeffBase = outputExp[0]; // we don't support _numOutputs < 1 for(unsigned i=1; i<_numOutputs; i++) { if(outputExp[i] != coeffBase+i) { coeffIsSeq = false; break; } } } if(_numOutputs != outputExponents.size() || (coeffType == GF16OCL_COEFF_LOG_SEQ && !coeffIsSeq)) { // need to re-init as the code assumes a fixed number of outputs outputExponents = std::vector(_numOutputs); if(!setup_kernels(_setupMethod, _setupTargetInputBatch, _setupTargetIters, _setupTargetGrouping, coeffIsSeq)) return false; } assert(outputExp || coeffType == GF16OCL_COEFF_NORMAL); // TODO: if outputs not specified, and a Log method is specified, need to re-init away from that method if(outputExp) memcpy(outputExponents.data(), outputExp, outputExponents.size()*sizeof(uint16_t)); if(coeffType == GF16OCL_COEFF_LOG) { cl::Event writeEvent; queue.enqueueWriteBuffer(buffer_outExp, CL_FALSE, 0, outputExponents.size()*sizeof(uint16_t), outputExponents.data(), &queueEvents, &writeEvent); queueEvents.clear(); queueEvents.reserve(2); queueEvents.push_back(writeEvent); } else if(coeffType == GF16OCL_COEFF_LOG_SEQ) { kernelMul.setArg(3, outputExponents[0]); kernelMulAdd.setArg(3, outputExponents[0]); kernelMulLast.setArg(4, outputExponents[0]); kernelMulAddLast.setArg(4, outputExponents[0]); } return true; } void PAR2ProcOCL::reset_state() { currentStagingInputs = 0; currentStagingArea = 0; stagingActiveCount = 0; for(auto& area : staging) { area.event = cl::Event(); // clear any existing event area.setIsActive(false); } processingAdd = false; } void PAR2ProcOCL::_deinit() { queue.finish(); queueEvents.clear(); } void PAR2ProcOCL::processing_finished() { IF_LIBUV(endSignalled = false); } std::vector PAR2ProcOCL::availableMethods(int platformId, int deviceId) { std::vector ret; if(platformId == -1 || deviceId == -1) { // we assume all methods are available ret.push_back(GF16OCL_LOOKUP); ret.push_back(GF16OCL_LOOKUP_HALF); ret.push_back(GF16OCL_LOOKUP_NOCACHE); ret.push_back(GF16OCL_LOOKUP_HALF_NOCACHE); ret.push_back(GF16OCL_LOOKUP_GRP2); ret.push_back(GF16OCL_LOOKUP_GRP2_NOCACHE); /* log methods are known to fail on some platforms, so disable for now TODO: debug these and enable ret.push_back(GF16OCL_LOG); ret.push_back(GF16OCL_LOG_SMALL); ret.push_back(GF16OCL_LOG_SMALL2); ret.push_back(GF16OCL_LOG_TINY); ret.push_back(GF16OCL_LOG_SMALL_LMEM); ret.push_back(GF16OCL_LOG_TINY_LMEM); */ //ret.push_back(GF16OCL_SHUFFLE); ret.push_back(GF16OCL_BY2); } else { if(platformId >= (int)platforms.size()) return ret; std::vector devices; platforms[platformId].getDevices(CL_DEVICE_TYPE_ALL, &devices); if(deviceId >= (int)devices.size()) return ret; cl::Context context(devices[deviceId]); // TODO: actually check capabilities ret.push_back(GF16OCL_LOOKUP); ret.push_back(GF16OCL_LOOKUP_HALF); ret.push_back(GF16OCL_LOOKUP_NOCACHE); ret.push_back(GF16OCL_LOOKUP_HALF_NOCACHE); ret.push_back(GF16OCL_LOOKUP_GRP2); // TODO: consider restricting to platforms with 32b word size (currently it just forces 32b word size) ret.push_back(GF16OCL_LOOKUP_GRP2_NOCACHE); /* log methods are known to fail on some platforms, so disable for now ret.push_back(GF16OCL_LOG); ret.push_back(GF16OCL_LOG_SMALL); ret.push_back(GF16OCL_LOG_SMALL2); ret.push_back(GF16OCL_LOG_TINY); ret.push_back(GF16OCL_LOG_SMALL_LMEM); ret.push_back(GF16OCL_LOG_TINY_LMEM); */ ret.push_back(GF16OCL_BY2); } return ret; } Galois16OCLMethods PAR2ProcOCL::default_method() const { // TODO: determine best method return GF16OCL_LOOKUP; } // reduce slice size bool PAR2ProcOCL::setCurrentSliceSize(size_t newSliceSize) { setSliceSize(newSliceSize); if(sliceSizeCksum > allocatedSliceSize) { // need to re-init everything auto outExp = outputExponents; if(!init(_setupMethod, _setupTargetInputBatch, _setupTargetIters, _setupTargetGrouping)) return false; if(!outExp.empty()) { if(!setRecoverySlices(outExp.size(), outExp.data())) return false; } return true; } size_t sliceGroups = (sliceSizeCksum+bytesPerGroup -1) / bytesPerGroup; processRange = cl::NDRange(sliceGroups * wgSize, processRange[1]); sliceSizeAligned = sliceGroups*bytesPerGroup; return true; } struct transfer_data_ocl { bool finish; // false = prepare, true = finish PAR2ProcOCL* parent; void* local; cl::Buffer* remote; size_t remoteOffset; size_t sliceLen, totalLen; Galois16Mul* gf; // prepare specific size_t srcLen; unsigned submitInBufs; unsigned inBufId; int oclPlatVersion; NOTIFY_DECL(cbPrep, promPrep); // finish specific NOTIFY_BOOL_DECL(cbOut, promOut); int cksumSuccess; unsigned grpSize; unsigned region; }; #ifdef USE_LIBUV void PAR2ProcOCL::_notifySent(void* _req) { auto req = static_cast(_req); pendingInCallbacks--; if(req->cbPrep && req->local) req->cbPrep(); delete req; // handle possibility of _notifySent being called after the last _notifyProc if(endSignalled && isEmpty() && progressCb) progressCb(0); // we got this callback after processing has finished -> need to flag to front-end that we're now done } #endif PAR2ProcBackendAddResult PAR2ProcOCL::canAdd() const { if(staging[currentStagingArea].getIsActive()) return PROC_ADD_FULL; return stagingActiveCount_get() < staging.size()-1 ? PROC_ADD_OK : PROC_ADD_OK_BUSY; } #ifndef USE_LIBUV void PAR2ProcOCL::waitForAdd() { IPAR2ProcBackend::_waitForAdd(staging[currentStagingArea]); } #endif void PAR2ProcOCL::transfer_slice(ThreadMessageQueue& q) { struct transfer_data_ocl* data; while((data = static_cast(q.pop())) != NULL) { // TODO: consider doing a single mapping for the entire slice (if not, consider async mapping) if(data->finish) { void* remote = data->parent->queue.enqueueMapBuffer(*(data->remote), CL_TRUE, CL_MAP_READ, data->remoteOffset, data->totalLen*data->grpSize); if(data->grpSize == 2) { // TODO: look at a way to avoid unnecessary map/unmap calls data->cksumSuccess = data->gf->finish_grp2_cksum(data->local, remote, data->sliceLen, data->region & (data->grpSize-1)); } else { data->cksumSuccess = data->gf->copy_cksum_check(data->local, remote, data->sliceLen); } data->parent->queue.enqueueUnmapMemObject(*(data->remote), remote); NOTIFY_DONE(data, _queueRecv, data->promOut, data->cksumSuccess); } else { if(data->local) { void* remote = data->parent->queue.enqueueMapBuffer(*(data->remote), CL_TRUE, data->oclPlatVersion>=1002 ? CL_MAP_WRITE_INVALIDATE_REGION : CL_MAP_WRITE, data->remoteOffset, data->totalLen); data->gf->copy_cksum(remote, data->local, data->srcLen, data->sliceLen); data->parent->queue.enqueueUnmapMemObject(*(data->remote), remote); } if(data->submitInBufs) { // queue async compute data->parent->run_kernel(data->inBufId, data->submitInBufs); } // signal main thread that prepare has completed NOTIFY_DONE(data, _queueSent, data->promPrep); } IF_NOT_LIBUV(delete data); } } FUTURE_RETURN_T PAR2ProcOCL::addInput(const void* buffer, size_t size, uint16_t inputNum, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)) { IF_NOT_LIBUV(return) _addInput(buffer, size, inputNum, flush IF_LIBUV(, cb)); } FUTURE_RETURN_T PAR2ProcOCL::addInput(const void* buffer, size_t size, const uint16_t* coeffs, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)) { IF_NOT_LIBUV(return) _addInput(buffer, size, coeffs, flush IF_LIBUV(, cb)); } template FUTURE_RETURN_T PAR2ProcOCL::_addInput(const void* buffer, size_t size, T inputNumOrCoeffs, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)) { auto& area = staging[currentStagingArea]; // detect if busy assert(!area.getIsActive()); set_coeffs(area, currentStagingInputs, inputNumOrCoeffs); struct transfer_data_ocl* data = new struct transfer_data_ocl; data->finish = false; data->local = (void*)buffer; data->srcLen = size; data->parent = this; data->remote = &area.input; data->remoteOffset = currentStagingInputs * sliceSizeAligned; data->sliceLen = sliceSize; data->totalLen = sliceSizeCksum; data->gf = gf.get(); data->oclPlatVersion = oclPlatVersion; IF_LIBUV(data->cbPrep = cb); currentStagingInputs++; data->submitInBufs = (flush || currentStagingInputs == inputBatchSize || ( // allow submitting early if there's no active processing stagingActiveCount_get() == 0 && staging.size() > 1 && currentStagingInputs >= minInBatchSize )) ? currentStagingInputs : 0; data->inBufId = currentStagingArea; if(data->submitInBufs) { stagingActiveCount_inc(); area.setIsActive(true); // lock this buffer until processing is complete statBatchesStarted++; currentStagingInputs = 0; if(++currentStagingArea == staging.size()) currentStagingArea = 0; } IF_LIBUV(pendingInCallbacks++); IF_NOT_LIBUV(auto future = data->promPrep.get_future()); transferThread.send(data); IF_NOT_LIBUV(return future); } void PAR2ProcOCL::dummyInput(uint16_t inputNum, bool flush) { auto& area = staging[currentStagingArea]; // detect if busy assert(!area.getIsActive()); set_coeffs(area, currentStagingInputs, inputNum); currentStagingInputs++; if(currentStagingInputs == inputBatchSize || flush || (stagingActiveCount_get() == 0 && staging.size() > 1 && currentStagingInputs >= minInBatchSize)) { stagingActiveCount_inc(); area.setIsActive(true); // lock this buffer until processing is complete statBatchesStarted++; run_kernel(currentStagingArea, currentStagingInputs); currentStagingInputs = 0; if(++currentStagingArea == staging.size()) currentStagingArea = 0; } } bool PAR2ProcOCL::fillInput(const void* buffer) { void* remote = queue.enqueueMapBuffer(staging[currentStagingArea].input, CL_TRUE, oclPlatVersion>=1002 ? CL_MAP_WRITE_INVALIDATE_REGION : CL_MAP_WRITE, currentStagingInputs * sliceSizeAligned, sliceSizeAligned); gf->copy_cksum(remote, buffer, sliceSize, sliceSize); queue.enqueueUnmapMemObject(staging[currentStagingArea].input, remote); if(++currentStagingInputs == inputBatchSize) { currentStagingInputs = 0; if(++currentStagingArea == staging.size()) { currentStagingArea = 0; return true; // all filled } } return false; } #include "gfmat_coeff.h" void PAR2ProcOCL::set_coeffs(PAR2ProcOCLStaging& area, unsigned idx, uint16_t inputNum) { uint16_t inputLog = gfmat_input_log(inputNum); auto& coeffs = area.procCoeffs; if(coeffType != GF16OCL_COEFF_NORMAL) { coeffs[idx] = inputLog; } else { for(unsigned i=0; ifinish = false; data->local = NULL; data->parent = this; data->submitInBufs = currentStagingInputs; data->inBufId = currentStagingArea; data->gf = gf.get(); data->oclPlatVersion = oclPlatVersion; stagingActiveCount_inc(); staging[currentStagingArea].setIsActive(true); // lock this buffer until processing is complete statBatchesStarted++; currentStagingInputs = 0; if(++currentStagingArea == staging.size()) currentStagingArea = 0; IF_LIBUV(pendingInCallbacks++); transferThread.send(data); } struct recv_data { PAR2ProcOCL* parent; NOTIFY_BOOL_DECL(cb, prom); }; #ifdef USE_LIBUV void PAR2ProcOCL::_notifyRecv(void* _req) { auto req = static_cast(_req); pendingOutCallbacks--; req->cbOut(req->cksumSuccess); delete req; if(pendingOutCallbacks < 1 && deinitCallback) deinit(deinitCallback); } #endif FUTURE_RETURN_BOOL_T PAR2ProcOCL::getOutput(unsigned index, void* output IF_LIBUV(, const PAR2ProcOutputCb& cb)) { struct transfer_data_ocl* data = new struct transfer_data_ocl; data->finish = true; data->parent = this; data->remote = &buffer_output; data->remoteOffset = index*sliceSizeAligned; data->sliceLen = sliceSize; data->totalLen = sliceSizeAligned; data->gf = gf.get(); data->oclPlatVersion = oclPlatVersion; data->local = output; data->region = index; data->grpSize = 1; if(outputsInterleaved > 1 && index < ((unsigned)outputExponents.size() & ~(outputsInterleaved-1))) { data->grpSize = outputsInterleaved; data->remoteOffset = (index & ~(outputsInterleaved-1))*sliceSizeAligned; } #ifdef USE_LIBUV data->cbOut = cb; pendingOutCallbacks++; #else auto future = data->promOut.get_future(); #endif transferThread.send(data); IF_NOT_LIBUV(return future); } typedef struct PAR2ProcBackendBaseComputeReq compute_req; #ifdef USE_LIBUV void PAR2ProcOCL::_notifyProc(void* _req) { auto req = static_cast(_req); stagingActiveCount_dec(); staging[req->procIdx].setIsActive(false); // if add was blocked, allow adds to continue - calling application will need to listen to this event to know to continue if(progressCb) progressCb(req->numInputs); delete req; } #endif static void CL_CALLBACK kernel_event_callback(cl_event, cl_int, void* _req) { auto req = static_cast(_req); #ifdef USE_LIBUV req->parent->_queueProc.notify(req); #else req->parent->stagingActiveCount_dec(); req->parent->_setAreaActive(req->procIdx, false); delete req; #endif } void PAR2ProcOCL::run_kernel(unsigned buf, unsigned numInputs) { auto& area = staging[buf]; // transfer coefficient list cl::Event coeffEvent; queue.enqueueWriteBuffer(area.coeffs, CL_FALSE, 0, inputBatchSize * (coeffType!=GF16OCL_COEFF_NORMAL ? 1 : outputExponents.size()) * sizeof(uint16_t), area.procCoeffs.data(), NULL, &coeffEvent); queueEvents.push_back(coeffEvent); // invoke kernel cl::Kernel* kernel; if(numInputs == inputBatchSize) { kernel = processingAdd ? &kernelMulAdd : &kernelMul; } else { // incomplete kernel -> need to pass in number of inputs to read from kernel = processingAdd ? &kernelMulAddLast : &kernelMulLast; kernel->setArg(3, numInputs); } processingAdd = true; kernel->setArg(1, area.input); kernel->setArg(2, area.coeffs); // TODO: try-catch to detect errors (e.g. out of resources) queue.enqueueNDRangeKernel(*kernel, cl::NullRange, processRange, workGroupRange, &queueEvents, &area.event); queueEvents.clear(); // for coeffType!=GF16OCL_COEFF_NORMAL, assume that the outputs are transferred by the time we enqueue the next kernel // when kernel finishes auto* req = new compute_req; req->numInputs = numInputs; req->procIdx = buf; req->parent = this; area.event.setCallback(CL_COMPLETE, kernel_event_callback, req); queue.flush(); // ensure the kernel is executed } bool PAR2ProcOCL::getPlatform(cl::Platform& platform, int platformId) { if(platformId == -1) { try { platform = cl::Platform::getDefault(); } catch(cl::Error const&) { return false; } } else { if(platformId >= (int)platforms.size()) return false; platform = platforms[platformId]; } return true; } std::vector PAR2ProcOCL::getPlatforms() { std::vector ret(platforms.size()); for(unsigned i=0; i(); } return ret; } int PAR2ProcOCL::defaultPlatformId() { cl::Platform platform; try { platform = cl::Platform::getDefault(); } catch(cl::Error const&) { return -1; } const auto vendor = platform.getInfo(); const auto name = platform.getInfo(); int id = 0; for(const auto& plat : platforms) { if(vendor == plat.getInfo() && name == plat.getInfo()) return id; id++; } return -1; } int PAR2ProcOCL::defaultDeviceId(int platformId) { cl::Platform platform; if(!getPlatform(platform, platformId)) return -1; std::vector devices; platform.getDevices(CL_DEVICE_TYPE_ALL, &devices); // match first GPU device that's available int firstAvailable = -1; for(unsigned i=0; i() && device.getInfo() && device.getInfo()) { if(firstAvailable == -1) firstAvailable = i; if(device.getInfo() == CL_DEVICE_TYPE_GPU) return i; // found first GPU } } return firstAvailable; } // based off getWGsizes function from clinfo size_t GF16OCL_DeviceInfo::getWGSize(const cl::Context& context, const cl::Device& device) { try { cl::Program program(context, "#define GWO(type) global type* restrict\n" "#define GRO(type) global const type* restrict\n" "#define BODY int i = get_global_id(0); out[i] = in1[i] + in2[i]\n" "#define _KRN(T, N) kernel void sum##N(GWO(T##N) out, GRO(T##N) in1, GRO(T##N) in2) { BODY; }\n" "#define KRN(N) _KRN(int, N)\n" "KRN()\n/* KRN(2)\nKRN(4)\nKRN(8)\nKRN(16) */\n", true); cl::Kernel kern(program, "sum"); return kern.getWorkGroupInfo(device); } catch(cl::Error const&) { return 0; } } GF16OCL_DeviceInfo::GF16OCL_DeviceInfo(int _id, const cl::Device& device) { id = _id; name = device.getInfo(); vendorId = device.getInfo(); type = device.getInfo(); available = device.getInfo() && device.getInfo(); supported = available && device.getInfo(); memory = device.getInfo(); maxWorkGroup = (unsigned)device.getInfo(); computeUnits = device.getInfo(); globalCache = device.getInfo(); constantMemory = device.getInfo(); localMemory = device.getInfo(); localMemoryIsGlobal = device.getInfo() == CL_GLOBAL; maxAllocation = device.getInfo(); unifiedMemory = device.getInfo(); workGroupMultiple = (unsigned)getWGSize(device, device); } std::vector PAR2ProcOCL::getDevices(int platformId) { cl::Platform platform; if(!getPlatform(platform, platformId)) return std::vector(); std::vector devices; try { platform.getDevices(CL_DEVICE_TYPE_ALL, &devices); } catch(cl::Error const&) { return {}; } std::vector ret; ret.reserve(devices.size()); for(unsigned i=0; i devices; if(deviceId == -1) { // first try GPU try { platform.getDevices(CL_DEVICE_TYPE_GPU, &devices); } catch(cl::Error const&) {} // if no GPU device found, try anything else if(devices.size() < 1) { try { platform.getDevices(CL_DEVICE_TYPE_ALL, &devices); } catch(cl::Error const&) {} } if(devices.size() < 1) return false; // no devices! // match first device that's available bool found = false; for(unsigned i=0; i() && device.getInfo() && device.getInfo()) { found = true; break; } } if(!found) return false; } else { platform.getDevices(CL_DEVICE_TYPE_ALL, &devices); if(deviceId >= (int)devices.size()) return false; device = devices[deviceId]; } return true; } par2cmdline-turbo-1.4.0/parpar/gf16/controller_ocl.h000066400000000000000000000173061514221355600223010ustar00rootroot00000000000000#ifndef __GF16_CONTROLLER_OCL #define __GF16_CONTROLLER_OCL #define CL_HPP_ENABLE_EXCEPTIONS #define CL_HPP_TARGET_OPENCL_VERSION 120 #define CL_HPP_MINIMUM_OPENCL_VERSION 110 #define CL_HPP_CL_1_2_DEFAULT_BUILD #define CL_TARGET_OPENCL_VERSION 120 #define CL_USE_DEPRECATED_OPENCL_1_1_APIS #include "controller.h" #include "threadqueue.h" // must be placed before CL/cl.hpp for some reason #include // must be included before the C++ header #include #include "gf16mul.h" #include enum Galois16OCLMethods { GF16OCL_AUTO, GF16OCL_LOOKUP, GF16OCL_LOOKUP_HALF, GF16OCL_LOOKUP_NOCACHE, GF16OCL_LOOKUP_HALF_NOCACHE, GF16OCL_LOOKUP_GRP2, GF16OCL_LOOKUP_GRP2_NOCACHE, GF16OCL_SHUFFLE, //GF16OCL_SHUFFLE2, // not implemented GF16OCL_LOG, GF16OCL_LOG_SMALL, GF16OCL_LOG_SMALL2, GF16OCL_LOG_TINY, GF16OCL_LOG_SMALL_LMEM, GF16OCL_LOG_TINY_LMEM, GF16OCL_BY2 // reference; will remove //GF16OCL_SPLITMUL }; static const char* Galois16OCLMethodsText[] = { "Auto", "Lookup", "Lookup Half", "Lookup (NoCache)", "Lookup Half (NoCache)", "Lookup Group2", "Lookup Group2 (NoCache)", "Shuffle", //"Shuffle2", "Log", "Log-SmallExp", "Log-Small", "Log-TinyExp", "Log-SmallExp (Local)", "Log-TinyExp (Local)", "ByTwo" }; enum Galois16OCLCoeffType { GF16OCL_COEFF_NORMAL = 0, GF16OCL_COEFF_LOG, GF16OCL_COEFF_LOG_SEQ, }; class GF16OCL_DeviceInfo { public: int id; std::string name; unsigned vendorId; cl_device_type type; bool available; bool supported; uint64_t memory; uint64_t globalCache; bool unifiedMemory; uint64_t constantMemory; uint64_t localMemory; bool localMemoryIsGlobal; uint64_t maxAllocation; unsigned maxWorkGroup; unsigned workGroupMultiple; unsigned computeUnits; // invalid device GF16OCL_DeviceInfo() : id(-1), vendorId(0), available(false), supported(false), memory(0), globalCache(0), constantMemory(0), localMemory(0), maxAllocation(0), maxWorkGroup(0), workGroupMultiple(0), computeUnits(0) {} GF16OCL_DeviceInfo(int _id, const cl::Device& device); private: static size_t getWGSize(const cl::Context& context, const cl::Device& device); }; typedef struct { Galois16OCLMethods id; const char* name; unsigned idealInBatch, idealIters; bool usesOutGrouping; } GF16OCL_MethodInfo; class PAR2ProcOCLStaging : public IPAR2ProcStaging { public: cl::Buffer input; cl::Buffer coeffs; cl::Event event; PAR2ProcOCLStaging() : IPAR2ProcStaging() {} }; class PAR2ProcOCL : public IPAR2ProcBackend { bool _initSuccess; // method/input parameters size_t sliceSize, sliceSizeCksum; size_t sliceSizeAligned; // OpenCL stuff cl::Device device; int _deviceId; cl::CommandQueue queue; std::vector queueEvents; cl::NDRange processRange; cl::NDRange workGroupRange; cl::Kernel kernelMulAdd; cl::Kernel kernelMulAddLast; cl::Kernel kernelMul; cl::Kernel kernelMulLast; std::vector staging; // buffers cl::Buffer buffer_outExp; Galois16OCLCoeffType coeffType; cl::Buffer buffer_output; std::vector extra_buffers; // to enable slice size adjustments size_t allocatedSliceSize; size_t bytesPerGroup; size_t wgSize; unsigned outputsPerGroup; int oclPlatVersion; // = major*1000 + minor int oclDevVersion; std::unique_ptr gf; Galois16Methods gfMethod; MessageThread transferThread; static void transfer_slice(ThreadMessageQueue& q); unsigned outputsInterleaved; // must be a power of 2 // remembered setup params Galois16OCLMethods _setupMethod; unsigned _setupTargetInputBatch, _setupTargetIters, _setupTargetGrouping; void set_coeffs(PAR2ProcOCLStaging& area, unsigned idx, uint16_t inputNum); void set_coeffs(PAR2ProcOCLStaging& area, unsigned idx, const uint16_t* inputCoeffs); template FUTURE_RETURN_T _addInput(const void* buffer, size_t size, T inputNumOrCoeffs, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)); bool setup_kernels(Galois16OCLMethods method, unsigned targetInputBatch, unsigned targetIters, unsigned targetGrouping, bool outputSequential); void run_kernel(unsigned buf, unsigned numInputs) override; cl::Context context; static std::vector platforms; static bool getPlatform(cl::Platform& platform, int platformId = -1); static bool getDevice(cl::Device& device, int platformId = -1, int deviceId = -1); void reset_state(); #ifdef USE_LIBUV void _notifySent(void* _req) override; void _notifyRecv(void* _req) override; void _notifyProc(void* _req) override; #endif static void printError(const char* where, cl::Error const& err); // disable copy constructor PAR2ProcOCL(const PAR2ProcOCL&); PAR2ProcOCL& operator=(const PAR2ProcOCL&); public: static int load_runtime(); static inline int unload_runtime() { platforms.clear(); return unload_opencl(); } static int defaultPlatformId(); static int defaultDeviceId(int platformId = -1); static std::vector getPlatforms(); static std::vector getDevices(int platformId = -1); inline static std::string getPlatform(int platformId = -1) { cl::Platform platform; if(getPlatform(platform, platformId)) return platform.getInfo(); return {}; } inline static GF16OCL_DeviceInfo getDevice(int platformId = -1, int deviceId = -1) { cl::Device device; if(deviceId == -1) deviceId = defaultDeviceId(platformId); if(deviceId >= 0 && getDevice(device, platformId, deviceId)) { return {deviceId, device}; } return {}; } explicit PAR2ProcOCL(IF_LIBUV(uv_loop_t* _loop,) int platformId = -1, int deviceId = -1, int stagingAreas = 2); ~PAR2ProcOCL(); void setSliceSize(size_t _sliceSize) override; bool init(Galois16OCLMethods method = GF16OCL_AUTO, unsigned targetInputBatch=0, unsigned targetIters=0, unsigned targetGrouping=0, Galois16Methods cksumMethod = GF16_AUTO); PAR2ProcBackendAddResult canAdd() const override; FUTURE_RETURN_T addInput(const void* buffer, size_t size, uint16_t inputNum, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)) override; FUTURE_RETURN_T addInput(const void* buffer, size_t size, const uint16_t* coeffs, bool flush IF_LIBUV(, const PAR2ProcPlainCb& cb)) override; void dummyInput(uint16_t inputNum, bool flush = false) override; bool fillInput(const void* buffer) override; void flush() override; FUTURE_RETURN_BOOL_T getOutput(unsigned index, void* output IF_LIBUV(, const PAR2ProcOutputCb& cb)) override; bool setCurrentSliceSize(size_t newSliceSize) override; // can only set to lower than allocated in init() bool setRecoverySlices(unsigned _numOutputs, const uint16_t* outputExp = NULL) override; inline size_t getAllocSliceSize() const { return allocatedSliceSize; } inline size_t getChunkLen() const { return bytesPerGroup; } inline unsigned getOutputGrouping() const { return outputsPerGroup; } void _deinit() override; void freeProcessingMem() override {} void processing_finished() override; inline void _setAreaActive(int area, bool active) { staging[area].setIsActive(active); } #ifndef USE_LIBUV void waitForAdd() override; FUTURE_RETURN_T endInput() override { return IPAR2ProcBackend::_endInput(staging); } #endif inline const char* getMethodName() const { return methodToText(_setupMethod); } inline unsigned getStagingAreas() const { return staging.size(); } static GF16OCL_MethodInfo info(Galois16OCLMethods method); inline GF16OCL_MethodInfo info() const { return info(_setupMethod); } inline GF16OCL_DeviceInfo deviceInfo() { return {_deviceId, device}; } Galois16OCLMethods default_method() const; static std::vector availableMethods(int platformId = -1, int deviceId = -1); static inline const char* methodToText(Galois16OCLMethods m) { return Galois16OCLMethodsText[(int)m]; } }; #endif // defined(__GF16_CONTROLLER_OCL) par2cmdline-turbo-1.4.0/parpar/gf16/controller_ocl_init.cpp000066400000000000000000002326061514221355600236610ustar00rootroot00000000000000#include #include // snprintf #include "../src/stdint.h" #include // strstr #include // std::stringstream #include "controller_ocl.h" #include "gf16_global.h" // GF16_POLYNOMIAL // for viewing compiled code, uncomment //#define DUMP_ASM // uncomment to prefer using the workgroup multiple instead of the max workgroup size // it seems like using the max width on GCN is usually ideal, but on Fermi and older, width is too wide that it hurts perf //#define OCL_PREFER_WORKGROUP_MULTIPLE 8 #ifdef DUMP_ASM # include // std::ofstream #endif #define STRINGIFY(...) #__VA_ARGS__ #define STR_HELPER(x) #x #define STR(x) STR_HELPER(x) const static char _ocl_defines[] = "#define GF16_POLYNOMIAL " STR(GF16_POLYNOMIAL) "\n" "#define SHIFT_TOP_BIT(x) ((x) >> (sizeof(x)*8-1))\n" // shift down top bit to bottom //"#define WORKAROUND_ALIAS_BUG\n" // have seen POCL not handle pointer alias writes properly // can use 24-bit multiply if the slice size fits within 24 bits (rownum guaranteed to be within 16 bits) // TODO: may be possible to do this is if stride is less than 16M *elements*, but we don't have byte addressing, so may not be possible "#if MAX_SLICE_SIZE < 16777216\n" " #define RBUF_REF(row, stride, col) mad24((uint)(row), (uint)(stride), (uint)(col))\n" "#else\n" " #define RBUF_REF(row, stride, col) ((row) * (stride) + (col))\n" "#endif\n" "#if MAX_SLICE_SIZE < 16777216 && MAX_SLICE_SIZE*NUM_OUTPUTS <= 0xffffffffUL\n" " #define WBUF_REF(row, stride, col) mad24((uint)(row), (uint)(stride), (uint)(col))\n" "#else\n" " #define WBUF_REF(row, stride, col) ((row) * (stride) + (col))\n" "#endif\n" "#define COBUF_REF(row, col) ((row) * SUBMIT_INPUTS + (col))\n" // if we're dealing with < 4GB at a time, there's no need for 64-bit memory references "#if MAX_SLICE_SIZE*SUBMIT_INPUTS > 0xffffffffUL || MAX_SLICE_SIZE*NUM_OUTPUTS > 0xffffffffUL\n" " typedef size_t memsize_t;\n" "#else\n" // TODO: check - size_t seems to faster than uint? //" typedef uint memsize_t;\n" " typedef size_t memsize_t;\n" "#endif\n" "#if VECT_WIDTH == 1\n" " #define VECT_ONE 0x0001u\n" " #define SPREAD_POLY(v) (((short)(v) >> 15) & GF16_POLYNOMIAL)\n" " #define nat_ushort4 ushort4\n" " #define nat_uint ushort\n" " #define nat_int short\n" " #define NAT_BITS 16\n" "#elif VECT_WIDTH == 2\n" " #define VECT_ONE 0x00010001ul\n" " #define SPREAD_POLY(v) mul24(((v) >> 15) & VECT_ONE, GF16_POLYNOMIAL & 0xffff)\n" " #define nat_ushort4 uint2\n" " #define nat_uint uint\n" " #define nat_int int\n" " #define NAT_BITS 32\n" "#elif VECT_WIDTH == 4 || VECT_WIDTH == 8\n" " #define VECT_ONE (ulong)0x0001000100010001ull\n" // Nvidia driver on Linux for 8400GS seems to need the explicit ulong cast " #define SPREAD_POLY(v) ((((v) >> 15) & VECT_ONE) * (GF16_POLYNOMIAL & 0xffff))\n" " #define nat_ushort4 ulong\n" " #define nat_uint ulong\n" " #define nat_int long\n" " #define NAT_BITS 64\n" "#else\n" " #error Unsupported vector width\n" "#endif\n" "#if VECT_WIDTH == 8\n" " #define val_t ulong2\n" "#else\n" " #define val_t nat_uint\n" "#endif\n" "#ifndef EX_TABLE_ARGS\n" " #define EX_TABLE_ARGS\n" " #define EX_TABLE_ARGS_DECL\n" "#endif\n" "#ifndef EX_TABLE_KARGS_DECL\n" " #define EX_TABLE_KARGS_DECL EX_TABLE_ARGS_DECL\n" "#endif\n" // experimental flag which probably isn't worth it - only usable in LOG methods, which doesn't have many coeffs to look up //"#define COPY_COEFF\n" // only used for `nolut` kernel: copies coefficients from global memory to local before using "#ifdef COPY_COEFF\n" " #define COEFF_TABLE_TYPE __local\n" "#else\n" " #define COEFF_TABLE_TYPE __global\n" "#endif\n" // TODO: explore using uint4 for reads to maximise bandwidth "#define READ_SRC(src, idx) src[idx]\n"; const static char _ocl_method_by2[] = "#define GF_MULTIPLY gf16_multiply_by2\n" STRINGIFY( // `a` is a packed ushort, `b` is just a ushort val_t gf16_multiply_by2(val_t a, nat_uint b) { nat_int btmp = b << (NAT_BITS - 16); val_t res = SHIFT_TOP_BIT(btmp) & a; ) "\n#pragma unroll\n" STRINGIFY( for(int i=0; i<15; i++) { val_t poly = SPREAD_POLY(res); res = ((res + res) & ~VECT_ONE) ^ poly; btmp <<= 1; res ^= SHIFT_TOP_BIT(btmp) & a; } return res; } ); const static char _ocl_method_lh[] = "#define LUT_REF(name, idx) ((__local ushort*)(name) + (idx)*512)\n" "#define LUT_REF_X2(name, idx) ((__local uint*)(name) + (idx)*512)\n" "#define LUT_DECLARATION(name, size) __local ushort name[512 * (size)] __attribute__ ((aligned (VECT_WIDTH*2)))\n" "#define LUT_DECLARATION_X2(name, size) __local uint name[512 * (size)] __attribute__ ((aligned (VECT_WIDTH*2)))\n" "#define LUT_DECLARATION_X2_1(name, src) __local ushort* name = (__local ushort*)(src)\n" "#define LUT_GENERATE compute_lhtable\n" "#define LUT_GENERATE_X2 compute_lhtable_x2\n" "#define LUT_GENERATE_HAS_BARRIER\n" "#define LUT_MULTIPLY lhtable_multiply\n" "#define LUT_MULTIPLY_X2 lhtable_multiply_x2\n" STRINGIFY( ushort gf16_multiply_256(ushort v) { return (v << 8) ^ mul256poly[v >> 8]; } // always writes 4 entries to table // requires val < 256 (used for lhtable calculation) void gf16_multiply_write_lh(__local nat_uint* table, nat_uint val, nat_uint coeff, nat_ushort4 coeff256) { nat_int b = coeff << (NAT_BITS - 16); ) "\n#if VECT_WIDTH >= 2\n" STRINGIFY( nat_uint prod = 0; // do a clmul, then use a lookup to do the reduction ) "\n#pragma unroll\n" STRINGIFY( for(uint i=1; i<256; i<<=1) prod ^= mul24((uint)(val & i), (uint)coeff); prod ^= mul256poly[prod >> 16]; // align to top to be compatible with old code (also conveniently discards upper bits) prod <<= NAT_BITS - 16; ) "\n#else\n" STRINGIFY( // align a and b to the top so that the top-bit can be replicated with a single arithmetic right-shift nat_int a = val << (NAT_BITS - 8); nat_int prod = SHIFT_TOP_BIT(a) & b; ) "\n#pragma unroll\n" STRINGIFY( for(int i=0; i<7; i++) { nat_uint poly = SHIFT_TOP_BIT(prod) & ((nat_uint)(GF16_POLYNOMIAL & 0xffff) << (NAT_BITS-16)); prod = (prod << 1) ^ poly; a <<= 1; prod ^= SHIFT_TOP_BIT(a) & b; } ) "\n#endif\n" STRINGIFY( // multiply b by 2 (aligned to top) nat_uint b2 = (b << 1) ^ (SHIFT_TOP_BIT(b) & ((nat_uint)(GF16_POLYNOMIAL & 0xffff) << (NAT_BITS-16))); // multiply prod by 256 ushort prod256 = gf16_multiply_256((nat_uint)prod >> (NAT_BITS - 16)); // write out to subsequent products ) "\n#if VECT_WIDTH == 1\n" STRINGIFY( table[val/VECT_WIDTH] = prod; table[val/VECT_WIDTH + 1] = prod ^ b; table[val/VECT_WIDTH + 2] = prod ^ b2; table[val/VECT_WIDTH + 3] = prod ^ b2 ^ b; table[val/VECT_WIDTH + 256/VECT_WIDTH] = prod256; table[val/VECT_WIDTH + 256/VECT_WIDTH + 1] = prod256 ^ coeff256.s1; table[val/VECT_WIDTH + 256/VECT_WIDTH + 2] = prod256 ^ coeff256.s2; table[val/VECT_WIDTH + 256/VECT_WIDTH + 3] = prod256 ^ coeff256.s3; ) "\n#elif VECT_WIDTH == 2\n" STRINGIFY( // add `b` to second element table[val/VECT_WIDTH] = (prod ^ b) | ((nat_uint)prod >> 16); table[val/VECT_WIDTH + 1] = (prod ^ b ^ b2) | (((nat_uint)prod ^ b2) >> 16); table[val/VECT_WIDTH + 256/VECT_WIDTH] = upsample(prod256, prod256) ^ coeff256.s0; table[val/VECT_WIDTH + 256/VECT_WIDTH + 1] = upsample(prod256, prod256) ^ coeff256.s1; ) "\n#elif VECT_WIDTH == 4\n" STRINGIFY( nat_uint res = (prod ^ b2) | ((nat_uint)prod >> 32); table[val/VECT_WIDTH] = (res ^ b ^ ((nat_uint)b >> 32)) | (res >> 16); table[val/VECT_WIDTH + 256/VECT_WIDTH] = (prod256 * VECT_ONE) ^ coeff256; ) "\n#else\n" " #error Vector width not implemented\n" "#endif\n" STRINGIFY( } nat_ushort4 lh_compute_coeff256(nat_uint coeff) { nat_ushort4 result; // compute coeff*0, coeff*256, coeff*512, coeff*768 ushort coeff256 = gf16_multiply_256(coeff); ushort coeff512 = ((nat_uint)coeff256 << 1) ^ (-((nat_uint)coeff256 >> 15) & GF16_POLYNOMIAL); ) "\n#if VECT_WIDTH == 1\n" STRINGIFY( //result[0] = 0; // unused result.s1 = coeff256; result.s2 = coeff512; result.s3 = coeff256 ^ coeff512; ) "\n#elif VECT_WIDTH == 2\n" STRINGIFY( result.s0 = (uint)coeff256 << 16; result.s1 = upsample((ushort)(coeff256 ^ coeff512), coeff512); ) "\n#elif VECT_WIDTH == 4\n" STRINGIFY( result = upsample(upsample((ushort)(coeff256 ^ coeff512), coeff512), (uint)coeff256 << 16); ) "\n#else\n" " #error Vector width not implemented\n" "#endif\n" STRINGIFY( return result; } void compute_lhtable(__local ushort* table, __global const ushort* restrict coeffs, const ushort numCoeff) { barrier(CLK_LOCAL_MEM_FENCE); ) "\n#ifdef WORKAROUND_ALIAS_BUG\n" STRINGIFY( if(((ulong)table & (VECT_WIDTH*2-1)) != 0) { // guaranteed to be false table[0] = 0; // never executed but needed for some reason } else ) "\n#endif\n" STRINGIFY( nat_uint localOffset = get_local_id(0)*4; // assume workgroup size is a power of 2 ) "\n#if COL_GROUP_SIZE*4 > 256\n" STRINGIFY( // process multiple coefficients at a time for(nat_uint coeffBase=0; coeffBase>8) | 0x1000100; return upsample( (ushort)(table[(val>>16) & 0xff] ^ table[(val_h>>16) & 0x1ff]), (ushort)(table[val & 0xff] ^ table[val_h & 0x1ff]) ); ) "\n#else\n" STRINGIFY( nat_uint result = table[val & 0xff] ^ table[((val>>8) & 0xff) + 256]; ) "\n#pragma unroll\n" STRINGIFY( for(nat_uint v=1; v>= 16; result |= (nat_uint)(table[val & 0xff] ^ table[((val>>8) & 0xff) + 256]) << (v*16); } return result; ) "\n#endif\n" STRINGIFY( } ) "\n#if VECT_WIDTH==2 && defined(WRITE_GRP2)\n" STRINGIFY( // LookupGrp2 stuff inline uint lh_mul256_x2(uint val) { uint val256 = upsample( mul256poly[val >> 24], mul256poly[(val >> 8) & 0xff] ); return ((val & 0xff00ff) << 8) ^ val256; } // writes two (low+high) entries void gf16_multiply_write_lh_x2(__local nat_uint* table, nat_uint val, uint coeffPair, uint coeff256Pair) { // align `a` to the top so that the top-bit can be replicated with a single arithmetic right-shift nat_int a = val << (NAT_BITS - 8); uint prod = SHIFT_TOP_BIT(a) & coeffPair; ) "\n#pragma unroll\n" STRINGIFY( for(int i=0; i<7; i++) { uint poly = SPREAD_POLY(prod); prod = ((prod + prod) & ~VECT_ONE) ^ poly; a <<= 1; prod ^= SHIFT_TOP_BIT(a) & coeffPair; } uint coeff2Pair = ((coeffPair + coeffPair) & ~VECT_ONE) ^ SPREAD_POLY(coeffPair); table[val] = prod; table[val+1] = prod ^ coeffPair; table[val+2] = prod ^ coeff2Pair; table[val+3] = prod ^ coeff2Pair ^ coeffPair; uint coeff512Pair = ((coeff256Pair + coeff256Pair) & ~VECT_ONE) ^ SPREAD_POLY(coeff256Pair); uint prod256 = lh_mul256_x2(prod); table[val + 256] = prod256; table[val + 257] = prod256 ^ coeff256Pair; table[val + 258] = prod256 ^ coeff512Pair; table[val + 259] = prod256 ^ coeff512Pair ^ coeff256Pair; /* dynamic number of entries idea: for(int i=0; i 256\n" STRINGIFY( // process multiple coefficients at a time for(nat_uint coeffBase=0; coeffBase>8) | 0x1000100; return (uint2)( table[val & 0xff] ^ table[val_h & 0x1ff], table[(val>>16) & 0xff] ^ table[val_h>>16] ); } ) "\n#endif\n" STRINGIFY( ); const static char _ocl_method_ll[] = "#define LUT_REF(name, idx) ((__local ushort*)(name) + (idx)*256)\n" "#define LUT_DECLARATION(name, size) __local ushort name[256 * (size)] __attribute__ ((aligned (VECT_WIDTH*2)))\n" "#define LUT_GENERATE compute_lltable\n" "#define LUT_GENERATE_HAS_BARRIER\n" "#define LUT_MULTIPLY lltable_multiply\n" STRINGIFY( ushort gf16_multiply_256(ushort v) { return (v << 8) ^ mul256poly[v >> 8]; } // as gf16_multiply_write_lh, but doesn't write 256x entries void gf16_multiply_write_ll(__local nat_uint* table, nat_uint val, nat_uint coeff) { // align a and b to the top so that the top-bit can be replicated with a single arithmetic right-shift nat_int b = coeff << (NAT_BITS - 16); ) "\n#if VECT_WIDTH >= 2\n" STRINGIFY( nat_uint prod = 0; ) "\n#pragma unroll\n" STRINGIFY( for(uint i=1; i<256; i<<=1) prod ^= mul24((uint)(val & i), (uint)coeff); prod ^= mul256poly[prod >> 16]; prod <<= NAT_BITS - 16; ) "\n#else\n" STRINGIFY( nat_int a = val << (NAT_BITS - 8); nat_int prod = SHIFT_TOP_BIT(a) & b; ) "\n#pragma unroll\n" STRINGIFY( for(int i=0; i<7; i++) { nat_uint poly = SHIFT_TOP_BIT(prod) & ((nat_uint)(GF16_POLYNOMIAL & 0xffff) << (NAT_BITS-16)); prod = (prod << 1) ^ poly; a <<= 1; prod ^= SHIFT_TOP_BIT(a) & b; } ) "\n#endif\n" STRINGIFY( // multiply by 2 (aligned to top) nat_uint b2 = (b << 1) ^ (SHIFT_TOP_BIT(b) & ((nat_uint)(GF16_POLYNOMIAL & 0xffff) << (NAT_BITS-16))); // write out to subsequent products ) "\n#if VECT_WIDTH == 1\n" STRINGIFY( table[val/VECT_WIDTH] = prod; table[val/VECT_WIDTH + 1] = prod ^ b; table[val/VECT_WIDTH + 2] = prod ^ b2; table[val/VECT_WIDTH + 3] = prod ^ b2 ^ b; ) "\n#elif VECT_WIDTH == 2\n" STRINGIFY( // add `b` to second element table[val/VECT_WIDTH] = (prod ^ b) | ((nat_uint)prod >> 16); table[val/VECT_WIDTH + 1] = (prod ^ b ^ b2) | (((nat_uint)prod ^ b2) >> 16); ) "\n#elif VECT_WIDTH == 4\n" STRINGIFY( nat_uint res = (prod ^ b2) | ((nat_uint)prod >> 32); table[val/VECT_WIDTH] = (res ^ b ^ ((nat_uint)b >> 32)) | (res >> 16); ) "\n#else\n" " #error Vector width not implemented\n" "#endif\n" STRINGIFY( } void compute_lltable(__local ushort* table, __global const ushort* restrict coeffs, const ushort numCoeff) { barrier(CLK_LOCAL_MEM_FENCE); ) "\n#ifdef WORKAROUND_ALIAS_BUG\n" STRINGIFY( if(((ulong)table & (VECT_WIDTH*2-1)) != 0) { // guaranteed to be false table[0] = 0; // never executed but needed for some reason } else ) "\n#endif\n" STRINGIFY( nat_uint localOffset = get_local_id(0)*4; // assume workgroup size is a power of 2 ) "\n#if COL_GROUP_SIZE*4 > 256\n" STRINGIFY( // process multiple coefficients at a time for(nat_uint coeffBase=0; coeffBase>16) & 0xff] ^ gf16_multiply_256(table[(val>>24) & 0xff])), (ushort)(table[val & 0xff] ^ gf16_multiply_256(table[(val>>8) & 0xff])) ); ) "\n#else\n" STRINGIFY( nat_uint result = table[val & 0xff] ^ gf16_multiply_256(table[(val>>8) & 0xff]); ) "\n#pragma unroll\n" STRINGIFY( for(nat_uint v=1; v>= 16; result |= (nat_uint)(table[val & 0xff] ^ gf16_multiply_256(table[(val>>8) & 0xff])) << (v*16); } return result; ) "\n#endif\n" STRINGIFY( } ); const static char _ocl_method_shuffle[] = "#undef READ_SRC\n" "#define READ_SRC gf16_shuffle_read\n" "#define WRITE_DST_OVERRIDE(dst, idx, val) gf16_shuffle_write(dst, idx, val)\n" "#define WRITE_DST_ADD_OVERRIDE(dst, idx, val) gf16_shuffle_add(dst, idx, val)\n" "#undef val_t\n" "#if VECT_WIDTH==1\n" " #define val_t uchar4\n" " #define val2_t ushort2\n" " #define nat_ucharX uchar2\n" "#elif VECT_WIDTH==2\n" " #define val_t uchar8\n" " #define val2_t ushort4\n" " #define nat_ucharX uchar4\n" "#elif VECT_WIDTH==4\n" " #define val_t uchar16\n" " #define val2_t ushort8\n" " #define nat_ucharX uchar8\n" "#else\n" " #error Unsupported vector width\n" "#endif\n" "#define LUT_REF(name, idx) ((__private uchar4*)(name) + (idx)*16)\n" "#define LUT_DECLARATION(name, size) __private uchar4 name[16 * (size)]\n" "#define LUT_GENERATE compute_shuffletable\n" "#define LUT_MULTIPLY shuffle_multiply\n" STRINGIFY( ushort gf16_mult2(const ushort v) { return (v << 1) ^ (-(v >= 0x8000) & (GF16_POLYNOMIAL & 0xffff)); } void compute_shuffletable(__private uchar4* table, __global const ushort* restrict coeffs, const ushort numCoeff) { //for(uint i=get_local_id(0); i>8, val2>>8, val3>>8); uchar4 ri; ) "\n#if GF16_POLYNOMIAL != 0x1100b\n#error Unsupported polynomial\n#endif\n" "#define MUL4(p, c) " "ri = tbl[p+1] >> (uchar)6;" "tbl[c+1] = ((tbl[p+1] << (uchar)2) | (tbl[p] >> (uchar)6)) ^ (ri << (uchar)4);" "tbl[c] = (tbl[p] << (uchar)2) ^ shuffle((uchar4)((uchar)0, (uchar)0xb, (uchar)(0xb<<1), (uchar)((0xb<<1)^0xb)), ri)\n" STRINGIFY( MUL4(0, 2); MUL4(2, 4); MUL4(4, 6); MUL4(6, 8); MUL4(8, 10); MUL4(10, 12); MUL4(12, 14); ) "\n#undef MUL4\n" STRINGIFY( } } val_t gf16_shuffle_read(__global const val_t* restrict src, const size_t idx) { const val_t data = src[idx]; return (val_t)(data.even, data.odd); } void gf16_shuffle_write(__global val_t* dst, const memsize_t offs, const val_t val) { *(__global val2_t*)(dst + offs) = upsample(val.hi, val.lo); } void gf16_shuffle_add(__global val_t* dst, const memsize_t offs, const val_t val) { *(__global val2_t*)(dst + offs) ^= upsample(val.hi, val.lo); } inline val_t shuffle_multiply(__private const uchar4* table, val_t val) { nat_ucharX resLo = shuffle(table[0], val.lo) ^ shuffle(table[8], val.hi); nat_ucharX resHi = shuffle(table[1], val.lo) ^ shuffle(table[9], val.hi); resLo ^= shuffle(table[2], val.lo >>(uchar)2) ^ shuffle(table[10], val.hi >>(uchar)2); resHi ^= shuffle(table[3], val.lo >>(uchar)2) ^ shuffle(table[11], val.hi >>(uchar)2); resLo ^= shuffle(table[4], val.lo >>(uchar)4) ^ shuffle(table[12], val.hi >>(uchar)4); resHi ^= shuffle(table[5], val.lo >>(uchar)4) ^ shuffle(table[13], val.hi >>(uchar)4); resLo ^= shuffle(table[6], val.lo >>(uchar)6) ^ shuffle(table[14], val.hi >>(uchar)6); resHi ^= shuffle(table[7], val.lo >>(uchar)6) ^ shuffle(table[15], val.hi >>(uchar)6); return (val_t)(resLo, resHi); } ); const static char _ocl_method_log[] = "#define GF_MULTIPLY(a, b) gf16_multiply_log(a, b EX_TABLE_ARGS)\n" "#undef READ_SRC\n" "#define READ_SRC(src, idx) gf16_log_src(src[idx] EX_TABLE_ARGS)\n" "#if defined(OCL_METHOD_LOG_TINY) || defined(OCL_METHOD_LOG_SMALL)\n" " #define LOG_SRC(n) gf16_log_small(n EX_TABLE_ARGS)\n" "#else\n" " #define LOG_SRC(n) ((LOG_MEM_TYPE ushort*)gf16_log)[n]\n" "#endif\n" STRINGIFY( ushort gf16_log_small(nat_uint val EX_TABLE_ARGS_DECL) { if(val == 0) return 65535; ) "\n#ifdef OCL_METHOD_LOG_SMALL\n" " #if __OPENCL_C_VERSION__ >= 200\n" " nat_uint log = ctz(val);\n" " #else\n" " nat_uint log = clz((nat_uint)((val-1)^val)) ^ (NAT_BITS-1);\n" " #endif\n" STRINGIFY( val >>= log; log += ((LOG_MEM_TYPE ushort*)gf16_log)[val >> 1]; ) "\n#else\n" STRINGIFY( nat_uint log = 0; uint prep; nat_uint shift; LOG_MEM_TYPE uint* gf16_log_prep = (gf16_log + 16384/2); prep = gf16_log_prep[val & 0x7ff]; shift = prep >> 16; log += shift; val = (val >> shift) ^ (prep & 0xffff); // (repeat above) prep = gf16_log_prep[val & 0x7ff]; shift = prep >> 16; log += shift; val = (val >> shift) ^ (prep & 0xffff); ushort log2 = ((LOG_MEM_TYPE ushort*)gf16_log)[val >> 2]; ) "\n#if VECT_WIDTH == 1\n" STRINGIFY( log += add_sat(log, log2) == (ushort)65535; log += log2; ) "\n#else\n" STRINGIFY( log += log2; if(log >= 65535) log -= 65535; //if (log == 12345678 && val == 12345678) return 0; // this is an impossible condition, but for some reason on some AMD cards, it makes the val==1 case work (i.e. without it, log(1) may result in 1 instead of 0) // unable to find the reason for the above issue (though have managed to cause different results with different ways to do the same thing) - I'm filing it as a compiler bug ) "\n#endif\n" "#endif\n" STRINGIFY( return log; } val_t gf16_log_src(val_t val EX_TABLE_ARGS_DECL) { ) "\n#if VECT_WIDTH==2\n" STRINGIFY( return upsample(LOG_SRC(val >> 16), LOG_SRC(val & 0xffff)); ) "\n#else\n" STRINGIFY( val_t res = LOG_SRC(val & 0xffff); ) "\n#pragma unroll\n" STRINGIFY( for(int v=1; v>shift) & 0xffff) << shift; } return res; ) "\n#endif\n" STRINGIFY( } nat_uint gfmat_calc_coeff_log(nat_uint recBlock, ushort inCoeff) { uint result = inCoeff * (ushort)recBlock; // calc 'result %= 65535' result = (result >> 16) + (result & 65535); result += result >> 16; return result & 65535; } val_t gf16_multiply_log(val_t a, nat_uint b EX_TABLE_ARGS_DECL) { val_t result = 0; ) "\n#pragma unroll\n" STRINGIFY( for(int v=0; v> 16) + (va & 0xffff); ) "\n#endif\n" STRINGIFY( ) "\n#ifdef OCL_METHOD_EXP_SMALL\n" STRINGIFY( uint vatmp = gf16_antilog[va >> 3]; vatmp <<= va & 7; vatmp ^= gf16_antilog[8192 + (vatmp >> 16)]; va = vatmp & 0xffff; ) "\n#elif defined(OCL_METHOD_EXP_TINY)\n" STRINGIFY( uint vatmp = gf16_antilog[va >> 4]; vatmp <<= va & 15; vatmp ^= gf16_antilog[4096 + (vatmp >> 24)] << 8; vatmp &= 0xffffff; vatmp ^= gf16_antilog[4096 + (vatmp >> 16)]; va = vatmp & 0xffff; ) "\n#else\n" STRINGIFY( va = gf16_antilog[va]; ) "\n#endif\n" STRINGIFY( result |= (isZero ? 0 : va) << (v*16); a >>= 16; } return result; } ); /* // probably not that useful of an idea const static char _ocl_method_splitmul[] = "#define GF_MULTIPLY gf16_multiply_splitmul\n" STRINGIFY( val_t gf16_multiply_splitmul(val_t a, nat_uint b) { val_t result = 0; ushort ba = (b>>12)*16; ushort bb = ((b>>8) & 0xf)*16; ushort bc = ((b>>4) & 0xf)*16; ushort bd = (b & 0xf)*16; ) "\n#pragma unroll\n" STRINGIFY( for(int v=0; v> 8) & 0xff; ushort ay = a & 0xff; ushort val = mul_split1[bd + ay] ^ gf16_mul16(mul_split1[bc + ay]) ^ mul_split256[bb + ay] ^ mul_split256[bd + ax] ^ gf16_mul16(mul_split256[ba + ay] ^ mul_split256[bc + ax]) ^ mul_split4107[bb + ax] ^ gf16_mul16(mul_split4107[ba + ax]) result |= val << (v*16); a >>= 16; } return result; } ); */ // kernel sources // defined during compile: MUL_ONLY, numInputs // strategy which caches several inputs, dot-products to multiple outputs, and doesn't require a LUT per coefficient #define KERNEL_NOLUT_MULGROUP \ "(__global val_t* restrict dst, __global const val_t* restrict src, COEFF_TABLE_TYPE const ushort* restrict coeff, const nat_uint outBlk, const nat_uint numOutputs, const ushort _numInputs, __local val_t* cache EX_TABLE_ARGS_DECL) { \n"\ " const memsize_t len = get_global_size(0)*COL_GROUP_ITERS; \n"\ " const memsize_t globalCol = get_global_id(0)*COL_GROUP_ITERS; \n"\ " const uint col = get_local_id(0)*COL_GROUP_ITERS; \n"\ " __global const val_t* srcBase = src + globalCol; \n"\ " \n"\ " val_t result[OUTPUT_GROUPING][COL_GROUP_ITERS]; \n"\ " val_t val[COL_GROUP_ITERS]; \n"\ " #pragma unroll \n"\ " for(nat_uint iter=0; iter> 16; \n"\ " curCoeff &= 0xffff; \n"\ " } \n"\ " #else \n"\ " #pragma unroll \n"\ " for (nat_uint o = 0; o < numOutputs; o++) { \n"\ " curCoeff = GET_COEFF(outBlk+o, 0u); \n"\ " #pragma unroll \n"\ " for(nat_uint iter=0; iter> 16; \n"\ " curCoeff &= 0xffff; \n"\ " } \n"\ " #else \n"\ " #pragma unroll \n"\ " for (nat_uint o = 0; o < numOutputs; o++) { \n"\ " curCoeff = GET_COEFF(outBlk+o, i); \n"\ " #pragma unroll \n"\ " for(nat_uint iter=0; iter OUTPUT_GROUPING\n" STRINGIFY( // first iteration -> copy loaded data to cache //barrier(CLK_LOCAL_MEM_FENCE); // probably not needed? thread only accesses the same value that it writes KERNFN(mulgroup_cache)(dst, src, lcoeff, outBlk, OUTPUT_GROUPING, numInputs, cache EX_TABLE_ARGS); outBlk += OUTPUT_GROUPING; //barrier(CLK_LOCAL_MEM_FENCE); for (; outBlk < OUTPUTS_PER_THREAD - OUTPUT_GROUPING + 1; outBlk += OUTPUT_GROUPING) { KERNFN(mulgroup_read)(dst, src, lcoeff, outBlk, OUTPUT_GROUPING, numInputs, cache EX_TABLE_ARGS); } ) "\n#endif\n" "#if OUTPUTS_PER_THREAD == OUTPUT_GROUPING || OUTPUTS_PER_THREAD % OUTPUT_GROUPING > 0\n" STRINGIFY( ) "\n#if OUTPUTS_PER_THREAD > OUTPUT_GROUPING\n" STRINGIFY( KERNFN(mulgroup_read)(dst, src, lcoeff, outBlk, (OUTPUTS_PER_THREAD == OUTPUT_GROUPING ? OUTPUTS_PER_THREAD : OUTPUTS_PER_THREAD % OUTPUT_GROUPING), numInputs, cache EX_TABLE_ARGS); ) "\n#else\n" STRINGIFY( KERNFN(mulgroup_nocache)(dst, src, lcoeff, outBlk, (OUTPUTS_PER_THREAD == OUTPUT_GROUPING ? OUTPUTS_PER_THREAD : OUTPUTS_PER_THREAD % OUTPUT_GROUPING), numInputs, (__local val_t*)0 EX_TABLE_ARGS); ) "\n#endif\n" STRINGIFY( ) "\n#endif\n" STRINGIFY( } else { ) "\n#define OUTPUTS_THIS_THREAD (NUM_OUTPUTS % OUTPUTS_PER_THREAD)\n" STRINGIFY( ) "\n#if OUTPUTS_THIS_THREAD > OUTPUT_GROUPING\n" STRINGIFY( // first iteration -> copy loaded data to cache //barrier(CLK_LOCAL_MEM_FENCE); KERNFN(mulgroup_cache)(dst, src, lcoeff, outBlk, OUTPUT_GROUPING, numInputs, cache EX_TABLE_ARGS); outBlk += OUTPUT_GROUPING; //barrier(CLK_LOCAL_MEM_FENCE); for (; outBlk < OUTPUTS_THIS_THREAD - OUTPUT_GROUPING + 1; outBlk += OUTPUT_GROUPING) { KERNFN(mulgroup_read)(dst, src, lcoeff, outBlk, OUTPUT_GROUPING, numInputs, cache EX_TABLE_ARGS); } ) "\n#endif\n#if OUTPUTS_THIS_THREAD == OUTPUT_GROUPING || OUTPUTS_THIS_THREAD % OUTPUT_GROUPING > 0\n" STRINGIFY( ) "\n#if OUTPUTS_THIS_THREAD > OUTPUT_GROUPING\n" STRINGIFY( KERNFN(mulgroup_read)(dst, src, lcoeff, outBlk, (OUTPUTS_THIS_THREAD == OUTPUT_GROUPING ? OUTPUTS_THIS_THREAD : OUTPUTS_THIS_THREAD % OUTPUT_GROUPING), numInputs, cache EX_TABLE_ARGS); ) "\n#else\n" STRINGIFY( KERNFN(mulgroup_nocache)(dst, src, lcoeff, outBlk, (OUTPUTS_THIS_THREAD == OUTPUT_GROUPING ? OUTPUTS_THIS_THREAD : OUTPUTS_THIS_THREAD % OUTPUT_GROUPING), numInputs, (__local val_t*)0 EX_TABLE_ARGS); ) "\n#endif\n" STRINGIFY( ) "\n#endif\n" STRINGIFY( } }) "\n#undef WRITE_DST\n"; // strategy which caches several inputs, uses dot-product operations to a single output, and a LUT per coefficient const static char _ocl_kernel_cachelut[] = "#ifdef WRITE_DST_OVERRIDE\n" " #ifdef MUL_ONLY\n" " #define WRITE_DST WRITE_DST_OVERRIDE\n" " #else\n" " #define WRITE_DST WRITE_DST_ADD_OVERRIDE\n" " #endif\n" "#elif defined(MUL_ONLY)\n" " #define WRITE_DST(dst, idx, val) dst[idx] = val\n" "#else\n" " #define WRITE_DST(dst, idx, val) dst[idx] ^= val\n" "#endif\n" STRINGIFY({ const uint colLocal = get_local_id(0); const memsize_t colGlobal = colLocal + get_group_id(0)*COL_GROUP_SIZE*COL_GROUP_ITERS; const memsize_t len = get_global_size(0) * COL_GROUP_ITERS; // compute output ) "\n#if NUM_OUTPUTS <= OUTPUTS_PER_THREAD\n" STRINGIFY( const nat_uint outputsThisThread = NUM_OUTPUTS; const nat_uint outBase = 0; __global val_t* dstBase = dst + colGlobal; ) "\n#ifdef WRITE_GRP2\n" STRINGIFY( __global val_t* dstBase2 = dst + colGlobal*2; ) "\n#endif\n" STRINGIFY( ) "\n#else\n" STRINGIFY( const nat_uint outBase = get_global_id(1) * OUTPUTS_PER_THREAD; ) "\n#if NUM_OUTPUTS % OUTPUTS_PER_THREAD == 0\n" STRINGIFY( const nat_uint outputsThisThread = OUTPUTS_PER_THREAD; ) "\n#else\n" STRINGIFY( const nat_uint outputsThisThread = (get_global_id(1)+1 == get_global_size(1) ? NUM_OUTPUTS % OUTPUTS_PER_THREAD : OUTPUTS_PER_THREAD); ) "\n#endif\n" STRINGIFY( __global val_t* dstBase = dst + WBUF_REF(outBase, len, colGlobal); ) "\n#ifdef WRITE_GRP2\n" STRINGIFY( __global val_t* dstBase2 = dst + WBUF_REF(outBase, len, colGlobal*2); ) "\n#endif\n" STRINGIFY( ) "\n#endif\n" STRINGIFY( __global const val_t* srcBase = src + colGlobal; ) "\n#ifdef WRITE_GRP2\n" STRINGIFY( LUT_DECLARATION_X2(lut_table_x2, SUBMIT_INPUTS); LUT_DECLARATION_X2_1(lut_table, lut_table_x2); ) "\n#else\n" STRINGIFY( LUT_DECLARATION(lut_table, SUBMIT_INPUTS); ) "\n#endif\n" STRINGIFY( // only one output -> no need to cache (this also handles the special case for *_X2) // TODO: check if this is worth it if(outputsThisThread == 1) { LUT_GENERATE(lut_table, coeff + COBUF_REF(outBase, 0u), numInputs); for(uint iter=0; iter copy loaded data to cache uint iterBase = iter*COL_GROUP_SIZE; val_t val = READ_SRC(srcBase, 0u); cache[0][colLocal+iterBase] = val; ) "\n#ifdef WRITE_GRP2\n" STRINGIFY( uint2 result = LUT_MULTIPLY_X2(LUT_REF_X2(lut_table_x2, 0u), val); ) "\n#else\n" STRINGIFY( val_t result = LUT_MULTIPLY(LUT_REF(lut_table, 0u), val); ) "\n#endif\n" STRINGIFY( __global const val_t* srcIterBase = srcBase; ) "\n#ifdef numInputs\n#pragma unroll\n#endif\n" STRINGIFY( for (nat_uint input = 1; input < numInputs; input++) { srcIterBase += len; val = READ_SRC(srcIterBase, 0u); // copy global input to local cache to be used in subsequent iterations // TODO: investigate using async_work_group_copy cache[input][colLocal + iterBase] = val; ) "\n#ifdef WRITE_GRP2\n" STRINGIFY( result ^= LUT_MULTIPLY_X2(LUT_REF_X2(lut_table_x2, input), val); ) "\n#else\n" STRINGIFY( result ^= LUT_MULTIPLY(LUT_REF(lut_table, input), val); ) "\n#endif\n" STRINGIFY( } ) "\n#ifdef WRITE_GRP2\n" STRINGIFY( WRITE_DST(dstBase2, iterBase*2, result[0]); WRITE_DST(dstBase2, iterBase*2+1, result[1]); ) "\n#else\n" STRINGIFY( WRITE_DST(dstBase, iterBase, result); ) "\n#endif\n" STRINGIFY( srcBase += COL_GROUP_SIZE; } //barrier(CLK_LOCAL_MEM_FENCE); // subsequent iterations: use cache ) "\n#ifdef WRITE_GRP2\n" STRINGIFY( nat_uint output=2; for(; output OUTPUT_GROUPING\n" STRINGIFY( for (; outBlk < OUTPUTS_PER_THREAD - OUTPUT_GROUPING + 1; outBlk += OUTPUT_GROUPING) { KERNFN(mulgroup)(dst, src, coeff, lut_table, outBlk, OUTPUT_GROUPING, numInputs); } ) "\n#endif\n#if OUTPUTS_PER_THREAD == OUTPUT_GROUPING || OUTPUTS_PER_THREAD % OUTPUT_GROUPING > 0\n" STRINGIFY({ ) "\n#define NUM_OUTPUTS_LEFT (OUTPUTS_PER_THREAD == OUTPUT_GROUPING ? OUTPUTS_PER_THREAD : OUTPUTS_PER_THREAD % OUTPUT_GROUPING)\n" STRINGIFY( KERNFN(mulgroup)(dst, src, coeff, lut_table, outBlk, NUM_OUTPUTS_LEFT, numInputs); ) "\n#undef NUM_OUTPUTS_LEFT\n" STRINGIFY( }) "\n#endif\n" STRINGIFY( } else { ) "\n#define OUTPUTS_THIS_THREAD (NUM_OUTPUTS % OUTPUTS_PER_THREAD)\n" STRINGIFY( ) "\n#if OUTPUTS_THIS_THREAD > OUTPUT_GROUPING\n" STRINGIFY( for (; outBlk < OUTPUTS_THIS_THREAD - OUTPUT_GROUPING + 1; outBlk += OUTPUT_GROUPING) { KERNFN(mulgroup)(dst, src, coeff, lut_table, outBlk, OUTPUT_GROUPING, numInputs); } ) "\n#endif\n#if OUTPUTS_THIS_THREAD == OUTPUT_GROUPING || OUTPUTS_THIS_THREAD % OUTPUT_GROUPING > 0\n" STRINGIFY({ ) "\n#define NUM_OUTPUTS_LEFT (OUTPUTS_THIS_THREAD == OUTPUT_GROUPING ? OUTPUTS_THIS_THREAD : OUTPUTS_THIS_THREAD % OUTPUT_GROUPING)\n" STRINGIFY( KERNFN(mulgroup)(dst, src, coeff, lut_table, outBlk, NUM_OUTPUTS_LEFT, numInputs); ) "\n#undef NUM_OUTPUTS_LEFT\n" STRINGIFY( }) "\n#endif\n" STRINGIFY( } }) "\n#undef WRITE_DST\n"; #undef STRINGIFY #undef STR_HELPER #undef STR #define CEIL_DIV(a, b) (((a) + (b)-1) / (b)) // ensure value is a power of 2, rounding down if needed static inline size_t round_down_pow2(size_t v) { if((v & (v-1)) == 0) return v; // is a power of 2 (shortcut exists because this is the common case) // find target number via a float conversion // (usage of float over double does mean that this can be wrong for very large numbers, but we're not expecting these) union { float f; uint32_t u; } tmp; tmp.f = (float)v; // convert to float tmp.u &= 0xff800000; // discard mantissa return (size_t)tmp.f; // convert back to int // alternative approach, avoiding flot<>int conversion, but using bit-scans /* #ifdef _MSC_VER unsigned long idx; _BitScanReverse(&idx, v); return 1 << idx; #else int idx = __builtin_clzl(v); return 1 << ((sizeof(unsigned long)-1) ^ idx); #endif */ } GF16OCL_MethodInfo PAR2ProcOCL::info(Galois16OCLMethods method) { GF16OCL_MethodInfo ret{ method, Galois16OCLMethodsText[static_cast(method)], 8, 1, true }; switch(method) { case GF16OCL_BY2: ret.idealInBatch = 16; break; case GF16OCL_SHUFFLE: ret.idealInBatch = 4; ret.usesOutGrouping = false; break; case GF16OCL_LOG: case GF16OCL_LOG_SMALL: case GF16OCL_LOG_SMALL2: case GF16OCL_LOG_TINY: case GF16OCL_LOG_SMALL_LMEM: case GF16OCL_LOG_TINY_LMEM: ret.idealInBatch = 8; break; case GF16OCL_LOOKUP_GRP2: ret.usesOutGrouping = false; ret.idealInBatch = 4; ret.idealIters = 2; break; case GF16OCL_LOOKUP: case GF16OCL_LOOKUP_HALF: ret.usesOutGrouping = false; ret.idealInBatch = 4; ret.idealIters = 4; break; case GF16OCL_LOOKUP_NOCACHE: case GF16OCL_LOOKUP_HALF_NOCACHE: ret.idealInBatch = 4; ret.idealIters = 8; // generally little reason not to do multiple iters break; case GF16OCL_LOOKUP_GRP2_NOCACHE: ret.idealInBatch = 6; ret.idealIters = 2; break; default: break; // prevent compiler warning } return ret; } // _sliceSize must be divisible by 2 bool PAR2ProcOCL::setup_kernels(Galois16OCLMethods method, unsigned targetInputBatch, unsigned targetIters, unsigned targetGrouping, bool outputSequential) { // TODO: get device info // CL_DEVICE_HOST_UNIFIED_MEMORY (deprecated?) // CL_DEVICE_ADDRESS_BITS (max referencable memory) unsigned infoShortVecSize = device.getInfo(); // seems to usually be the same as CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT if(infoShortVecSize < 4) infoShortVecSize = 2; // assume all GPUs do 32-bit math efficiently (various nVidia platforms return 1, but 2 runs faster) if(infoShortVecSize >= 8 && method == GF16OCL_BY2) infoShortVecSize = 8; // allow BY2 to use VECT_WIDTH=8 else if(infoShortVecSize >= 4) infoShortVecSize = 4; // other than BY2, we currently only support vect-width=1,2,4 wgSize = device.getInfo(); if(device.getInfo() == 1 && device.getInfo() == 0x10de /*Nvidia*/ && wgSize > 32) { // this size is too large for Nvidia cards; we'll assume their max width is ideal for 8-bit ints, so divide it by 4 wgSize /= infoShortVecSize*2; } #ifdef OCL_PREFER_WORKGROUP_MULTIPLE size_t wgSizeMultiple = getWGSize(context, device) * OCL_PREFER_WORKGROUP_MULTIPLE; if(wgSizeMultiple && wgSizeMultiple < wgSize) wgSize = wgSizeMultiple; #endif if(wgSize > 8192) wgSize = 8192; // sanity check size_t deviceAvailConstSize = device.getInfo(); size_t deviceLocalSize = device.getInfo() - 128; // subtract a little to allow some spare space if the device needs it //size_t deviceGlobalSize = device.getInfo(); // TODO: check sliceSizeCksum*numOutputs ? unsigned numOutputs = outputExponents.size(); unsigned outputsPerThread = numOutputs; // currently, process all outputs on every kernel invocation std::stringstream sourceStream; auto methInfo = info(method); inputBatchSize = methInfo.idealInBatch; unsigned groupIterations = methInfo.idealIters; unsigned threadWordSize = infoShortVecSize*2; unsigned sizePerWorkGroup = threadWordSize * wgSize; outputsPerGroup = 8; // for nolut kernel; have seen some cards prefer '4' (on older cards?) const char* kernelCode; const char* kernelFuncs = NULL; const char* methodCode; const char* oclVerArg = ""; // method selector coeffType = GF16OCL_COEFF_NORMAL; switch(method) { case GF16OCL_BY2: while(1) { if(inputBatchSize*sizePerWorkGroup > deviceLocalSize) inputBatchSize = (unsigned)(deviceLocalSize / sizePerWorkGroup); if(inputBatchSize < 1 && deviceLocalSize >= 2048) { // try reducing workgroup if too large if(wgSize > 256) { wgSize = 256; sizePerWorkGroup = threadWordSize * wgSize; inputBatchSize = 16; continue; } } break; } if(targetInputBatch) inputBatchSize = targetInputBatch; kernelCode = _ocl_kernel_nolut; kernelFuncs = _ocl_kernel_nolut_funcs; methodCode = _ocl_method_by2; break; case GF16OCL_SHUFFLE: //case GF16OCL_SHUFFLE2: oclVerArg = "-cl-std=CL1.1"; infoShortVecSize = 2; // currently only implemented for 32-bit on GPUs threadWordSize = infoShortVecSize*4; // process two words, so double per thread word size sizePerWorkGroup = threadWordSize * wgSize; if(targetInputBatch) inputBatchSize = targetInputBatch; // TODO: compute ideal iteration count while(1) { groupIterations = (unsigned)round_down_pow2( deviceLocalSize / (sizePerWorkGroup * inputBatchSize) ); if(groupIterations < 1 && deviceLocalSize >= 2048) { // try reducing workgroup if too large if(wgSize > 256) { wgSize = 256; sizePerWorkGroup = threadWordSize * wgSize; continue; } // try reducing input batch size if(inputBatchSize > 2) { inputBatchSize = 2; continue; } // try reducing workgroup again if(wgSize > 64) { wgSize = 64; sizePerWorkGroup = threadWordSize * wgSize; continue; } } break; } kernelCode = _ocl_kernel_cachelut; methodCode = _ocl_method_shuffle; break; case GF16OCL_LOG: case GF16OCL_LOG_SMALL: case GF16OCL_LOG_SMALL2: case GF16OCL_LOG_TINY: case GF16OCL_LOG_SMALL_LMEM: case GF16OCL_LOG_TINY_LMEM: coeffType = outputSequential ? GF16OCL_COEFF_LOG_SEQ : GF16OCL_COEFF_LOG; { size_t reqLocalMem = 0; if(method == GF16OCL_LOG_SMALL_LMEM) reqLocalMem = 16384+256; if(method == GF16OCL_LOG_TINY_LMEM) reqLocalMem = 8192+512; if(deviceLocalSize < reqLocalMem) return false; deviceLocalSize -= reqLocalMem; } while(1) { if(inputBatchSize*sizePerWorkGroup > deviceLocalSize) inputBatchSize = (unsigned)(deviceLocalSize / sizePerWorkGroup); if(inputBatchSize < 1 && deviceLocalSize >= 2048) { // try reducing workgroup if too large if(wgSize > 256) { wgSize = 256; sizePerWorkGroup = threadWordSize * wgSize; inputBatchSize = 8; continue; } } break; } if(targetInputBatch) inputBatchSize = targetInputBatch; kernelCode = _ocl_kernel_nolut; kernelFuncs = _ocl_kernel_nolut_funcs; sourceStream << "#define OCL_METHOD_LOG\n"; if(method == GF16OCL_LOG_SMALL || method == GF16OCL_LOG_SMALL2 || method == GF16OCL_LOG_SMALL_LMEM) sourceStream << "#define OCL_METHOD_EXP_SMALL\n"; if(method == GF16OCL_LOG_TINY || method == GF16OCL_LOG_TINY_LMEM) sourceStream << "#define OCL_METHOD_EXP_TINY\n"; if(method == GF16OCL_LOG_SMALL2) sourceStream << "#define OCL_METHOD_LOG_TINY\n"; methodCode = _ocl_method_log; break; case GF16OCL_LOOKUP: case GF16OCL_LOOKUP_HALF: case GF16OCL_LOOKUP_NOCACHE: case GF16OCL_LOOKUP_HALF_NOCACHE: case GF16OCL_LOOKUP_GRP2: case GF16OCL_LOOKUP_GRP2_NOCACHE: default: if(method == GF16OCL_LOOKUP_GRP2 || method == GF16OCL_LOOKUP_GRP2_NOCACHE) { if(method == GF16OCL_LOOKUP_GRP2) outputsPerThread += outputsPerThread & 1; // outputsPerThread must be a multiple of two sourceStream << "#define WRITE_GRP2 1\n"; infoShortVecSize = 2; // only supported on 32b // recompute the two variables dependent on infoShortVecSize threadWordSize = infoShortVecSize*2; sizePerWorkGroup = threadWordSize * wgSize; outputsInterleaved = 2; } if(method == GF16OCL_LOOKUP_NOCACHE || method == GF16OCL_LOOKUP_HALF_NOCACHE || method == GF16OCL_LOOKUP_GRP2_NOCACHE) { unsigned tables = (unsigned)(deviceLocalSize / (method == GF16OCL_LOOKUP_HALF_NOCACHE ? 512 : 1024)); if(tables >= 96) inputBatchSize = 8; else if(tables >= 60) inputBatchSize = 6; else if(tables >= 32) inputBatchSize = 4; else inputBatchSize = 2; outputsPerGroup = tables / inputBatchSize; if(outputsPerGroup > 16) outputsPerGroup = 16; if(method == GF16OCL_LOOKUP_GRP2_NOCACHE && outputsPerGroup > 1) { outputsPerGroup &= ~1; // must be a multiple of 2 if outputs are written in pairs targetGrouping &= ~1; } if(targetInputBatch) inputBatchSize = targetInputBatch; kernelCode = _ocl_kernel_lut; kernelFuncs = _ocl_kernel_lut_funcs; } else { if(targetInputBatch) inputBatchSize = targetInputBatch; // TODO: compute ideal iteration count unsigned tableSize = method == GF16OCL_LOOKUP_HALF ? 512 : 1024; if(method == GF16OCL_LOOKUP_GRP2) tableSize *= 2; while(1) { groupIterations = (unsigned)round_down_pow2( (deviceLocalSize - inputBatchSize*tableSize) / (sizePerWorkGroup * inputBatchSize) ); if(groupIterations < 1 && deviceLocalSize >= 8192) { // maybe the workgroup is too big if(wgSize > 256) { wgSize = 256; sizePerWorkGroup = threadWordSize * wgSize; continue; } // try reducing input batch size if(inputBatchSize > 2) { inputBatchSize = 2; continue; } } break; } kernelCode = _ocl_kernel_cachelut; } methodCode = (method == GF16OCL_LOOKUP_HALF || method == GF16OCL_LOOKUP_HALF_NOCACHE) ? _ocl_method_ll : _ocl_method_lh; // write multiply-by-256 reduction table sourceStream << "__constant ushort mul256poly[256] = {0"; for(int i=1; i<256; i++) { int n = i<<8; for(int b=0; b<8; b++) n = (n << 1) ^ (-(n>>15) & GF16_POLYNOMIAL); sourceStream << ',' << (n & 0xffff); } sourceStream << "};\n"; break; } if(inputBatchSize < 1) return false; minInBatchSize = inputBatchSize; if(targetIters) groupIterations = targetIters; if(targetGrouping) outputsPerGroup = targetGrouping; if(groupIterations < 1 || outputsPerGroup < 1) return false; // for very small slices, scale down iterations/wgSize if(sizePerWorkGroup * groupIterations > sliceSizeCksum) { // scale down iterations first if(groupIterations > 1) { groupIterations = (unsigned)CEIL_DIV(sliceSizeCksum, sizePerWorkGroup); if(groupIterations < 1) groupIterations = 1; } // if iterations cannot be scaled down, scale down workgroup size next unsigned minWgSize = 1; if(kernelCode == _ocl_kernel_lut || kernelCode == _ocl_kernel_cachelut) minWgSize = 128; // if computing lookup tables, don't scale down the workgroup below this amount if(groupIterations == 1 && sizePerWorkGroup > sliceSizeCksum && wgSize > minWgSize) { wgSize = CEIL_DIV(sliceSizeCksum, threadWordSize); if(wgSize < minWgSize) wgSize = minWgSize; } } if(method == GF16OCL_LOOKUP || method == GF16OCL_LOOKUP_HALF || method == GF16OCL_LOOKUP_NOCACHE || method == GF16OCL_LOOKUP_HALF_NOCACHE || method == GF16OCL_LOOKUP_GRP2 || method == GF16OCL_LOOKUP_GRP2_NOCACHE) { // lookup method assumes workgroup is a power of two wgSize = round_down_pow2(wgSize); } sizePerWorkGroup = threadWordSize * wgSize; // code generation for log methods uint16_t* tblLog = NULL; uint16_t* tblAntiLog = NULL; unsigned tblAntiLogSize = 0, tblLogSize = 0; if(coeffType != GF16OCL_COEFF_NORMAL) { // construct log/exp table and embed into OpenCL source if(method == GF16OCL_LOG_SMALL2) { tblLog = new uint16_t[tblLogSize = 16384 + 2048*2]; } else if(0) { // half-log idea tblLog = new uint16_t[tblLogSize = 32768]; } else { tblLog = new uint16_t[tblLogSize = 65536]; tblLog[0] = 65535; // special value to represent 0 } int n = 1; if(method == GF16OCL_LOG) { tblAntiLogSize = 65536; tblAntiLog = new uint16_t[tblAntiLogSize]; for (int exp = 0; exp < 65535; exp++) { tblAntiLog[exp] = n; tblLog[n] = exp; n = (n << 1) ^ (-(n>>15) & GF16_POLYNOMIAL); } tblAntiLog[65535] = tblAntiLog[0]; // saves having to wrap around 65535 to 0 } else { int bitsCut = (method == GF16OCL_LOG_SMALL || method == GF16OCL_LOG_SMALL2 || method == GF16OCL_LOG_SMALL_LMEM ? 3 : 4); if(bitsCut == 3) tblAntiLogSize = 8192+128; else tblAntiLogSize = 4096+256; tblAntiLog = new uint16_t[tblAntiLogSize]; for (int exp = 0; exp < 65535; exp++) { if((exp & ((1<> bitsCut] = n; if(method == GF16OCL_LOG_SMALL2) { if((n & 3) == 3) tblLog[n >> 2] = exp; } else if(0) { // half-log idea if((n & 1) == 1) tblLog[n >> 1] = exp; } else tblLog[n] = exp; n = (n << 1) ^ (-(n>>15) & GF16_POLYNOMIAL); } // add reduction table uint16_t* tblALogReduction = tblAntiLog + (uint32_t)(1u << (uint32_t)(16-bitsCut)); tblALogReduction[0] = 0; for (int i = 1; i < (1<<(bitsCut+4)); i++) { n = i << (12-bitsCut); for (int j = 0; j < (bitsCut+4); j++) n = (n << 1) ^ (-(n>>15) & GF16_POLYNOMIAL); tblALogReduction[i] = n; } // add log prep table if(method == GF16OCL_LOG_SMALL2) { uint16_t* tblLogPrep = tblLog + 16384; for (int i = 0; i < 2048; i++) { int iters = 0, r = 0; n = i; while((n&3) != 3 && iters < 10) { if(n & 1) { n ^= 0x1100b; r ^= 0x1100b; } n >>= 1; r >>= 1; iters++; } // assume little endian here tblLogPrep[i*2] = r; tblLogPrep[i*2+1] = iters; } } } // try to fit antiLog table first, then log table if(deviceAvailConstSize >= tblAntiLogSize*2) { deviceAvailConstSize -= tblAntiLogSize*2; sourceStream << "__constant ushort gf16_antilog"; if(method == GF16OCL_LOG_SMALL_LMEM || method == GF16OCL_LOG_TINY_LMEM) sourceStream << "_src"; sourceStream << "[" << tblAntiLogSize << "] __attribute__ ((aligned (VECT_WIDTH*2))) = {" << tblAntiLog[0]; for (unsigned j = 1; j < tblAntiLogSize;) { unsigned amount = tblAntiLogSize - j; if(amount > 8192) amount = 8192; for (unsigned i = 0; i < amount; i++) sourceStream << ',' << tblAntiLog[j+i]; sourceStream << '\n'; // have seen some OpenCL compilers barf at really long lines, so break things up j += amount; } sourceStream << "};\n"; delete[] tblAntiLog; tblAntiLog = NULL; } if(deviceAvailConstSize >= tblLogSize*2) { sourceStream << "#define LOG_MEM_TYPE __constant\n"; // gf16_log is defined as uint to ensure it's 4-byte aligned sourceStream << "__constant uint gf16_log[" << (tblLogSize/2) << "] = {" << ((tblLog[1]<<16) | tblLog[0]); for (unsigned j = 2; j < tblLogSize;) { unsigned amount = tblLogSize - j; if(amount > 8192) amount = 8192; for(unsigned i=0; i(1, device), paramsDump); } catch(cl::Error const& err) { printError("Build Error", err); if(err.err() == CL_BUILD_PROGRAM_FAILURE || err.err() == CL_COMPILE_PROGRAM_FAILURE || err.err() == CL_LINK_PROGRAM_FAILURE) { std::cerr << program.getBuildInfo(device) << std::endl; } if(tblLog) delete[] tblLog; if(tblAntiLog) delete[] tblAntiLog; return false; } std::vector binaries = program.getInfo(); for(unsigned i=0; i(1, device), params); } catch(cl::Error const& err) { printError("Build Error", err); #ifndef GF16OCL_NO_OUTPUT if(err.err() == CL_BUILD_PROGRAM_FAILURE || err.err() == CL_COMPILE_PROGRAM_FAILURE || err.err() == CL_LINK_PROGRAM_FAILURE) { std::cerr << program.getBuildInfo(device) << std::endl; } #endif if(tblLog) delete[] tblLog; if(tblAntiLog) delete[] tblAntiLog; return false; } // variant kernels for first (mul only) & last (misaligned input count) iterations kernelMul = cl::Kernel(program, "gf16_ocl_kernel_first"); kernelMulLast = cl::Kernel(program, "gf16_ocl_kernel_only"); kernelMulAddLast = cl::Kernel(program, "gf16_ocl_kernel_last"); kernelMulAdd = cl::Kernel(program, "gf16_ocl_kernel"); cl_mem_flags bufferFlags = CL_MEM_READ_ONLY | CL_MEM_ALLOC_HOST_PTR; if(oclPlatVersion >= 1002) bufferFlags |= CL_MEM_HOST_WRITE_ONLY; for(auto& area : staging) { area.input = cl::Buffer(context, bufferFlags, inputBatchSize*sliceSizeAligned); if(coeffType != GF16OCL_COEFF_NORMAL) { area.coeffs = cl::Buffer(context, bufferFlags, inputBatchSize*sizeof(uint16_t)); area.procCoeffs.resize(inputBatchSize); } else { area.coeffs = cl::Buffer(context, bufferFlags, inputBatchSize*numOutputs*sizeof(uint16_t)); area.procCoeffs.resize(inputBatchSize*numOutputs); } } // TODO: check if supports CPU shared memory and use host mem (CL_MEM_USE_HOST_PTR) instead? avoids a transfer when retrieving output; downside is that currently our API works by the caller supplying a buffer to write into, so would be a big change // TODO: need to consider CL_DEVICE_MAX_MEM_ALLOC_SIZE and perhaps break this into multiple allocations buffer_output = cl::Buffer(context, CL_MEM_READ_WRITE | (oclPlatVersion>=1002 ? CL_MEM_HOST_READ_ONLY : 0), numOutputs*sliceSizeAligned); workGroupRange = cl::NDRange(wgSize, 1); // attach arguments to kernels kernelMul.setArg(0, buffer_output); kernelMulLast.setArg(0, buffer_output); kernelMulAddLast.setArg(0, buffer_output); kernelMulAdd.setArg(0, buffer_output); extra_buffers.clear(); cl_mem_flags transBufferFlags = CL_MEM_READ_ONLY; if(oclPlatVersion >= 1002) transBufferFlags |= CL_MEM_HOST_WRITE_ONLY; if(coeffType == GF16OCL_COEFF_LOG) { buffer_outExp = cl::Buffer(context, transBufferFlags | CL_MEM_ALLOC_HOST_PTR, numOutputs*sizeof(uint16_t)); kernelMul.setArg(3, buffer_outExp); kernelMulAdd.setArg(3, buffer_outExp); kernelMulLast.setArg(4, buffer_outExp); kernelMulAddLast.setArg(4, buffer_outExp); } else { buffer_outExp = cl::Buffer(); } // if we couldn't embed log tables directly into the source, transfer them now if(tblLog) { const cl::Buffer bufLog(context, transBufferFlags, tblLogSize*2); extra_buffers.push_back(bufLog); kernelMul.setArg(4, bufLog); kernelMulAdd.setArg(4, bufLog); kernelMulLast.setArg(5, bufLog); kernelMulAddLast.setArg(5, bufLog); if(tblAntiLog) { extra_buffers.push_back(cl::Buffer(context, transBufferFlags, tblAntiLogSize*2)); const cl::Buffer& bufALog = extra_buffers.back(); kernelMul.setArg(5, bufALog); kernelMulAdd.setArg(5, bufALog); kernelMulLast.setArg(6, bufALog); kernelMulAddLast.setArg(6, bufALog); std::vector enqueueEvents(2); queue.enqueueWriteBuffer(bufLog, CL_FALSE, 0, tblLogSize*2, tblLog, NULL, &enqueueEvents[0]); queue.enqueueWriteBuffer(bufALog, CL_FALSE, 0, tblAntiLogSize*2, tblAntiLog, NULL, &enqueueEvents[1]); cl::Event::waitForEvents(enqueueEvents); delete[] tblLog; delete[] tblAntiLog; } else { queue.enqueueWriteBuffer(bufLog, CL_TRUE, 0, tblLogSize*2, tblLog); delete[] tblLog; } } // for some implementations, errors don't show up during build, but do during execute, so try a dummy run kernelMul.setArg(1, staging[0].input); kernelMul.setArg(2, staging[0].coeffs); try { queue.enqueueNDRangeKernel(kernelMul, cl::NullRange, workGroupRange, workGroupRange); } catch(cl::Error const& err) { printError("Execute Error", err); return false; } return true; } par2cmdline-turbo-1.4.0/parpar/gf16/gf16_affine.h000066400000000000000000000163211514221355600213300ustar00rootroot00000000000000 #include "../src/hedley.h" #define FUNCS(v) \ void gf16_affine_muladd_##v(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); \ void gf16_affine_muladd_prefetch_##v(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch); \ void gf16_affine_muladd_multi_packed_##v(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); \ void gf16_affine_muladd_multi_packpf_##v(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut); \ void gf16_affine_prepare_packed_cksum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); \ void gf16_affine_prepare_partial_packsum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen, size_t partOffset, size_t partLen); \ extern int gf16_affine_available_##v FUNCS(gfni); FUNCS(avx2); FUNCS(avx512); FUNCS(avx10); #undef FUNCS void* gf16_affine_init_gfni(int polynomial); void* gf16_affine_init_avx2(int polynomial); void* gf16_affine_init_avx512(int polynomial); #ifdef PARPAR_INVERT_SUPPORT #define FUNCS(v) \ void gf16_affine_muladd_multi_##v(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); \ void gf16_affine_muladd_multi_stridepf_##v(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t srcStride, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetch) FUNCS(gfni); FUNCS(avx2); FUNCS(avx512); FUNCS(avx10); #undef FUNCS void gf16_affine_mul_gfni(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); void gf16_affine_mul_avx2(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); void gf16_affine_mul_avx512(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); #endif #ifdef PARPAR_INCLUDE_BASIC_OPS #define FUNCS(v) \ void gf16_affine_prepare_packed_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen) FUNCS(gfni); FUNCS(avx2); FUNCS(avx512); FUNCS(avx10); #undef FUNCS #endif #define FUNCS(v) \ void gf16_affine2x_muladd_##v(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); \ void gf16_affine2x_muladd_multi_packed_##v(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); \ void gf16_affine2x_muladd_multi_packpf_##v(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut); \ void gf16_affine2x_prepare_packed_cksum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); \ void gf16_affine2x_prepare_partial_packsum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen, size_t partOffset, size_t partLen) FUNCS(gfni); FUNCS(avx2); FUNCS(avx512); FUNCS(avx10); #undef FUNCS #define FUNCS(v) \ int gf16_affine2x_finish_packed_cksum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen); \ int gf16_affine2x_finish_partial_packsum_##v(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen, size_t partOffset, size_t partLen) FUNCS(gfni); FUNCS(avx2); FUNCS(avx512); #undef FUNCS #ifdef PARPAR_INVERT_SUPPORT #define FUNCS(v) \ void gf16_affine2x_mul_##v(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); \ void gf16_affine2x_muladd_multi_##v(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); \ void gf16_affine2x_muladd_multi_stridepf_##v(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t srcStride, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetch); \ void gf16_affine2x_prepare_##v(void* dst, const void* src, size_t srcLen); \ void gf16_affine2x_finish_##v(void *HEDLEY_RESTRICT dst, size_t len) FUNCS(gfni); FUNCS(avx2); FUNCS(avx512); #undef FUNCS void gf16_affine2x_muladd_multi_avx10(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); void gf16_affine2x_muladd_multi_stridepf_avx10(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t srcStride, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetch); #endif #ifdef PARPAR_INCLUDE_BASIC_OPS #define FUNCS(v) \ void gf16_affine2x_prepare_packed_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); \ void gf16_affine2x_finish_packed_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen) FUNCS(gfni); FUNCS(avx2); FUNCS(avx512); #undef FUNCS void gf16_affine2x_prepare_packed_avx10(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_affine2x_x86.h000066400000000000000000000101211514221355600222770ustar00rootroot00000000000000 #include "gf16_shuffle_x86_common.h" #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) # include "gf16_checksum_x86.h" static HEDLEY_ALWAYS_INLINE void _FN(gf16_affine2x_prepare_block)(void* dst, const void* src) { _mword data = _MMI(loadu)((_mword*)src); data = separate_low_high(data); _MMI(store)((_mword*)dst, data); } static HEDLEY_ALWAYS_INLINE void _FN(gf16_affine2x_prepare_blocku)(void* dst, const void* src, size_t remaining) { _mword data = partial_load(src, remaining); data = separate_low_high(data); _MMI(store)((_mword*)dst, data); } # ifndef _EXCLUDE_FINISH_FUNCS static HEDLEY_ALWAYS_INLINE void _FN(gf16_affine2x_finish_block)(void *HEDLEY_RESTRICT dst) { _mword shuf = _MM(set_epi32)( #if MWORD_SIZE >= 64 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, #endif #if MWORD_SIZE >= 32 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, #endif 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800 ); _mword data = _MMI(load)((_mword*)dst); data = _MM(shuffle_epi8)(data, shuf); _MMI(store)((_mword*)dst, data); } static HEDLEY_ALWAYS_INLINE void _FN(gf16_affine2x_finish_copy_block)(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { _mword shuf = _MM(set_epi32)( #if MWORD_SIZE >= 64 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, #endif #if MWORD_SIZE >= 32 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, #endif 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800 ); _mword data = _MMI(load)((_mword*)src); data = _MM(shuffle_epi8)(data, shuf); _MMI(storeu)((_mword*)dst, data); } static HEDLEY_ALWAYS_INLINE void _FN(gf16_affine2x_finish_copy_blocku)(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t bytes) { _mword shuf = _MM(set_epi32)( #if MWORD_SIZE >= 64 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, #endif #if MWORD_SIZE >= 32 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, #endif 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800 ); _mword data = _MMI(load)((_mword*)src); data = _MM(shuffle_epi8)(data, shuf); partial_store((_mword*)dst, data, bytes); } # endif #endif #if defined(PARPAR_INVERT_SUPPORT) && !defined(_EXCLUDE_FINISH_FUNCS) void _FN(gf16_affine2x_prepare)(void* dst, const void* src, size_t srcLen) { #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) gf16_prepare(dst, src, srcLen, sizeof(_mword), &_FN(gf16_affine2x_prepare_block), &_FN(gf16_affine2x_prepare_blocku)); _MM_END #else UNUSED(dst); UNUSED(src); UNUSED(srcLen); #endif } #endif #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) # ifdef PLATFORM_AMD64 GF_PREPARE_PACKED_FUNCS(gf16_affine2x, _FNSUFFIX, sizeof(_mword), _FNPREP(gf16_affine2x_prepare_block), _FNPREP(gf16_affine2x_prepare_blocku), AFFINE2X_AMD64_INTERLEAVE, _MM_END, _mword checksum = _MMI(setzero)(), _FNPREP(gf16_checksum_block), _FNPREP(gf16_checksum_blocku), _FNPREP(gf16_checksum_exp), _FNPREP(gf16_checksum_prepare), sizeof(_mword)) # else GF_PREPARE_PACKED_FUNCS(gf16_affine2x, _FNSUFFIX, sizeof(_mword), _FNPREP(gf16_affine2x_prepare_block), _FNPREP(gf16_affine2x_prepare_blocku), 2, _MM_END, _mword checksum = _MMI(setzero)(), _FNPREP(gf16_checksum_block), _FNPREP(gf16_checksum_blocku), _FNPREP(gf16_checksum_exp), _FNPREP(gf16_checksum_prepare), sizeof(_mword)) # endif #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_affine2x, _FNSUFFIX) #endif #ifndef _EXCLUDE_FINISH_FUNCS #ifdef PARPAR_INVERT_SUPPORT void _FN(gf16_affine2x_finish)(void *HEDLEY_RESTRICT dst, size_t len) { #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) gf16_finish(dst, len, sizeof(_mword), &_FN(gf16_affine2x_finish_block)); _MM_END #else UNUSED(dst); UNUSED(len); #endif } #endif #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) GF_FINISH_PACKED_FUNCS(gf16_affine2x, _FNSUFFIX, sizeof(_mword), _FN(gf16_affine2x_finish_copy_block), _FN(gf16_affine2x_finish_copy_blocku), 1, _MM_END, _FN(gf16_checksum_block), _FN(gf16_checksum_blocku), _FN(gf16_checksum_exp), &_FN(gf16_affine2x_finish_block), sizeof(_mword)) #else GF_FINISH_PACKED_FUNCS_STUB(gf16_affine2x, _FNSUFFIX) #endif #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_affine_avx10.c000066400000000000000000000014431514221355600223410ustar00rootroot00000000000000 #include "gf16_global.h" #include "../src/platform.h" #define MWORD_SIZE 32 #define _mword __m256i #define _MM(f) _mm256_ ## f #define _MMI(f) _mm256_ ## f ## _si256 #define _FNSUFFIX _avx10 #define _FNPREP(f) f##_avx10 #define _MM_END _mm256_zeroupper(); // MSVC doesn't officially support AVX10 yet, so we don't compile this on MSVC #if defined(__GFNI__) && defined(__AVX512BW__) && defined(__AVX512VL__) && !defined(__EVEX512__) && (defined(__AVX10_1__) || defined(__EVEX256__)) # define _AVAILABLE 1 #endif #define _EXCLUDE_FINISH_FUNCS 1 // ...because we can just use the AVX2 variants #include "gf16_affine_avx10.h" #ifdef _AVAILABLE # undef _AVAILABLE #endif #undef _MM_END #undef _FNSUFFIX #undef _FNPREP #undef _MMI #undef _MM #undef _mword #undef MWORD_SIZE #undef _EXCLUDE_FINISH_FUNCS par2cmdline-turbo-1.4.0/parpar/gf16/gf16_affine_avx10.h000066400000000000000000000435051514221355600223530ustar00rootroot00000000000000 #ifdef _AVAILABLE int _FN(gf16_affine_available) = 1; # include "gf16_shuffle_x86_prepare.h" # include "gf16_checksum_x86.h" #else int _FN(gf16_affine_available) = 0; #endif #define AFFINE2X_AMD64_INTERLEAVE 12 #include "gf16_affine2x_x86.h" #include "gf16_muladd_multi.h" #ifdef _AVAILABLE # ifdef PLATFORM_AMD64 GF_PREPARE_PACKED_FUNCS(gf16_affine, _FNSUFFIX, sizeof(_mword)*2, _FNPREP(gf16_shuffle_prepare_block), _FNPREP(gf16_shuffle_prepare_blocku), 6, _mm256_zeroupper(), _mword checksum = _MMI(setzero)(), _FNPREP(gf16_checksum_block), _FNPREP(gf16_checksum_blocku), _FNPREP(gf16_checksum_exp), _FNPREP(gf16_checksum_prepare), sizeof(_mword)) # else GF_PREPARE_PACKED_FUNCS(gf16_affine, _FNSUFFIX, sizeof(_mword)*2, _FNPREP(gf16_shuffle_prepare_block), _FNPREP(gf16_shuffle_prepare_blocku), 1, _mm256_zeroupper(), _mword checksum = _MMI(setzero)(), _FNPREP(gf16_checksum_block), _FNPREP(gf16_checksum_blocku), _FNPREP(gf16_checksum_exp), _FNPREP(gf16_checksum_prepare), sizeof(_mword)) # endif #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_affine, _FNSUFFIX) #endif #ifdef _AVAILABLE static HEDLEY_ALWAYS_INLINE __m256i gf16_affine_load_matrix(const void *HEDLEY_RESTRICT scratch, uint16_t coefficient) { __m256i depmask = _mm256_xor_si256( _mm256_load_si256((__m256i*)scratch + (coefficient & 0xf)*4), _mm256_load_si256((__m256i*)((char*)scratch + ((coefficient << 3) & 0x780)) + 1) ); depmask = _mm256_ternarylogic_epi32( depmask, _mm256_load_si256((__m256i*)((char*)scratch + ((coefficient >> 1) & 0x780)) + 2), _mm256_load_si256((__m256i*)((char*)scratch + ((coefficient >> 5) & 0x780)) + 3), 0x96 ); return depmask; } #endif #ifdef _AVAILABLE static HEDLEY_ALWAYS_INLINE void _FN(gf16_affine_muladd_round)(const _mword* src, _mword* tpl, _mword* tph, _mword mat_ll, _mword mat_hl, _mword mat_lh, _mword mat_hh) { _mword ta = _MMI(load)(src); _mword tb = _MMI(load)(src + 1); *tpl = _MM(ternarylogic_epi32)( _MM(gf2p8affine_epi64_epi8)(ta, mat_lh, 0), _MM(gf2p8affine_epi64_epi8)(tb, mat_ll, 0), *tpl, 0x96 ); *tph = _MM(ternarylogic_epi32)( _MM(gf2p8affine_epi64_epi8)(ta, mat_hh, 0), _MM(gf2p8affine_epi64_epi8)(tb, mat_hl, 0), *tph, 0x96 ); } static HEDLEY_ALWAYS_INLINE void _FN(gf16_affine_muladd_x)( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(6); _mword mat_All, mat_Alh, mat_Ahl, mat_Ahh; _mword mat_Bll, mat_Blh, mat_Bhl, mat_Bhh; _mword mat_Cll, mat_Clh, mat_Chl, mat_Chh; _mword mat_Dll, mat_Dlh, mat_Dhl, mat_Dhh; _mword mat_Ell, mat_Elh, mat_Ehl, mat_Ehh; _mword mat_Fll, mat_Flh, mat_Fhl, mat_Fhh; _mword depmask1; #if MWORD_SIZE == 64 __m256i depmask256; __m512i depmask2; #define PERM1(dstVec, srcLL) \ dstVec##hh = _mm512_permutex_epi64(depmask2, _MM_SHUFFLE(3,3,3,3)); \ dstVec##lh = _mm512_permutex_epi64(depmask2, _MM_SHUFFLE(1,1,1,1)); \ dstVec##ll = _mm512_broadcastq_epi64(srcLL); \ dstVec##hl = _mm512_broadcastq_epi64(_mm512_castsi512_si128(depmask2)) #define PERM2(dstVec) \ depmask2 = _mm512_shuffle_i64x2(depmask1, depmask1, _MM_SHUFFLE(2,3,2,3)); \ dstVec##hh = _mm512_permutex_epi64(depmask2, _MM_SHUFFLE(3,3,3,3)); \ dstVec##lh = _mm512_permutex_epi64(depmask2, _MM_SHUFFLE(1,1,1,1)); \ dstVec##ll = _mm512_permutex_epi64(depmask2, _MM_SHUFFLE(2,2,2,2)); \ dstVec##hl = _mm512_broadcastq_epi64(_mm512_castsi512_si128(depmask2)) if(srcCount == 1) { depmask256 = gf16_affine_load_matrix(scratch, coefficients[0]); depmask2 = _mm512_castsi256_si512(depmask256); depmask2 = _mm512_shuffle_i64x2(depmask2, depmask2, _MM_SHUFFLE(0,1,0,1)); PERM1(mat_A, _mm256_castsi256_si128(depmask256)); } else if(srcCount > 1) { depmask1 = gf16_affine_load2_matrix(scratch, coefficients[0], coefficients[1]); depmask2 = _mm512_shuffle_i64x2(depmask1, depmask1, _MM_SHUFFLE(0,1,0,1)); PERM1(mat_A, _mm512_castsi512_si128(depmask1)); PERM2(mat_B); } if(srcCount == 3) { depmask256 = gf16_affine_load_matrix(scratch, coefficients[2]); depmask2 = _mm512_castsi256_si512(depmask256); depmask2 = _mm512_shuffle_i64x2(depmask2, depmask2, _MM_SHUFFLE(0,1,0,1)); PERM1(mat_C, _mm256_castsi256_si128(depmask256)); } else if(srcCount > 3) { depmask1 = gf16_affine_load2_matrix(scratch, coefficients[2], coefficients[3]); depmask2 = _mm512_shuffle_i64x2(depmask1, depmask1, _MM_SHUFFLE(0,1,0,1)); PERM1(mat_C, _mm512_castsi512_si128(depmask1)); PERM2(mat_D); } if(srcCount == 5) { depmask256 = gf16_affine_load_matrix(scratch, coefficients[4]); depmask2 = _mm512_castsi256_si512(depmask256); depmask2 = _mm512_shuffle_i64x2(depmask2, depmask2, _MM_SHUFFLE(0,1,0,1)); PERM1(mat_E, _mm256_castsi256_si128(depmask256)); } else if(srcCount > 5) { depmask1 = gf16_affine_load2_matrix(scratch, coefficients[4], coefficients[5]); depmask2 = _mm512_shuffle_i64x2(depmask1, depmask1, _MM_SHUFFLE(0,1,0,1)); PERM1(mat_E, _mm512_castsi512_si128(depmask1)); PERM2(mat_F); } #undef PERM2 #else #define PERM1(dstVec) \ dstVec##hh = _mm256_permute4x64_epi64(depmask1, _MM_SHUFFLE(1,1,1,1)); \ dstVec##lh = _mm256_permute4x64_epi64(depmask1, _MM_SHUFFLE(3,3,3,3)); \ dstVec##ll = _mm256_broadcastq_epi64(_mm256_castsi256_si128(depmask1)); \ dstVec##hl = _mm256_permute4x64_epi64(depmask1, _MM_SHUFFLE(2,2,2,2)) #define LOAD_SRC(n, dstVec) \ if(srcCount > n) { \ depmask1 = gf16_affine_load_matrix(scratch, coefficients[n]); \ PERM1(dstVec); \ } LOAD_SRC(0, mat_A) LOAD_SRC(1, mat_B) LOAD_SRC(2, mat_C) LOAD_SRC(3, mat_D) LOAD_SRC(4, mat_E) LOAD_SRC(5, mat_F) #undef LOAD_SRC #endif #undef PERM1 for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(_mword)*2) { _mword tph = _MMI(load)((_mword*)(_dst + ptr)); _mword tpl = _MMI(load)((_mword*)(_dst + ptr) + 1); _FN(gf16_affine_muladd_round)((_mword*)(_src1 + ptr*srcScale), &tpl, &tph, mat_All, mat_Ahl, mat_Alh, mat_Ahh); if(srcCount >= 2) _FN(gf16_affine_muladd_round)((_mword*)(_src2 + ptr*srcScale), &tpl, &tph, mat_Bll, mat_Bhl, mat_Blh, mat_Bhh); if(srcCount >= 3) _FN(gf16_affine_muladd_round)((_mword*)(_src3 + ptr*srcScale), &tpl, &tph, mat_Cll, mat_Chl, mat_Clh, mat_Chh); if(srcCount >= 4) _FN(gf16_affine_muladd_round)((_mword*)(_src4 + ptr*srcScale), &tpl, &tph, mat_Dll, mat_Dhl, mat_Dlh, mat_Dhh); if(srcCount >= 5) _FN(gf16_affine_muladd_round)((_mword*)(_src5 + ptr*srcScale), &tpl, &tph, mat_Ell, mat_Ehl, mat_Elh, mat_Ehh); if(srcCount >= 6) _FN(gf16_affine_muladd_round)((_mword*)(_src6 + ptr*srcScale), &tpl, &tph, mat_Fll, mat_Fhl, mat_Flh, mat_Fhh); _MMI(store)((_mword*)(_dst + ptr), tph); _MMI(store)((_mword*)(_dst + ptr)+1, tpl); if(doPrefetch == 1) _mm_prefetch(_pf+(ptr>>1), MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+(ptr>>1), _MM_HINT_T1); } } #endif /*defined(_AVAILABLE)*/ void _FN(gf16_affine_muladd)(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #ifdef _AVAILABLE gf16_muladd_single(scratch, &_FN(gf16_affine_muladd_x), dst, src, len, coefficient); _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } void _FN(gf16_affine_muladd_prefetch)(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch) { UNUSED(mutScratch); #ifdef _AVAILABLE gf16_muladd_prefetch_single(scratch, &_FN(gf16_affine_muladd_x), dst, src, len, coefficient, prefetch); _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); UNUSED(prefetch); #endif } #if defined(_AVAILABLE) && defined(PLATFORM_AMD64) GF16_MULADD_MULTI_FUNCS(gf16_affine, _FNSUFFIX, _FN(gf16_affine_muladd_x), 6, sizeof(_mword)*2, 1, _mm256_zeroupper()) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_affine, _FNSUFFIX) #endif #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) static HEDLEY_ALWAYS_INLINE void _FN(gf16_affine2x_muladd_2round)(const int srcCountOffs, const uint8_t* _src1, const uint8_t* _src2, intptr_t srcOffset, _mword* result, _mword* swapped, _mword matNorm1, _mword matSwap1, _mword matNorm2, _mword matSwap2) { if(srcCountOffs < 0) return; _mword data1 = _MMI(load)((const _mword*)(_src1 + srcOffset)); if(srcCountOffs == 0) { *result = _MMI(xor)( *result, _MM(gf2p8affine_epi64_epi8)(data1, matNorm1, 0) ); *swapped = _MMI(xor)( *swapped, _MM(gf2p8affine_epi64_epi8)(data1, matSwap1, 0) ); } else { // if(srcCountOffs > 0) _mword data2 = _MMI(load)((const _mword*)(_src2 + srcOffset)); *result = _MM(ternarylogic_epi32)( *result, _MM(gf2p8affine_epi64_epi8)(data1, matNorm1, 0), _MM(gf2p8affine_epi64_epi8)(data2, matNorm2, 0), 0x96 ); *swapped = _MM(ternarylogic_epi32)( *swapped, _MM(gf2p8affine_epi64_epi8)(data1, matSwap1, 0), _MM(gf2p8affine_epi64_epi8)(data2, matSwap2, 0), 0x96 ); } } static HEDLEY_ALWAYS_INLINE void _FN(gf16_affine2x_muladd_x)( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(13); _mword depmask; _mword matNormA, matSwapA; _mword matNormB, matSwapB; _mword matNormC, matSwapC; _mword matNormD, matSwapD; _mword matNormE, matSwapE; _mword matNormF, matSwapF; _mword matNormG, matSwapG; _mword matNormH, matSwapH; _mword matNormI, matSwapI; _mword matNormJ, matSwapJ; _mword matNormK, matSwapK; _mword matNormL, matSwapL; _mword matNormM, matSwapM; // prevent MSVC whining matNormB = matSwapB = matNormC = matSwapC = matNormD = matSwapD = matNormE = matSwapE = matNormF = matSwapF = matNormG = matSwapG = matNormH = matSwapH = matNormI = matSwapI = matNormJ = matSwapJ = matNormK = matSwapK = matNormL = matSwapL = matNormM = matSwapM = # if MWORD_SIZE == 64 _mm512_undefined_epi32(); # else _mm256_undefined_si256(); # endif # if MWORD_SIZE == 64 if(srcCount == 1) { depmask = _mm512_castsi256_si512(gf16_affine_load_matrix(scratch, coefficients[0])); matNormA = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); matSwapA = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); } if(srcCount > 1) { depmask = gf16_affine_load2_matrix(scratch, coefficients[0], coefficients[1]); matNormA = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); matSwapA = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); matNormB = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(2,2,2,2)); matSwapB = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(3,3,3,3)); } if(srcCount == 3) { depmask = _mm512_castsi256_si512(gf16_affine_load_matrix(scratch, coefficients[2])); matNormC = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); matSwapC = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); } if(srcCount > 3) { depmask = gf16_affine_load2_matrix(scratch, coefficients[2], coefficients[3]); matNormC = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); matSwapC = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); matNormD = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(2,2,2,2)); matSwapD = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(3,3,3,3)); } if(srcCount == 5) { depmask = _mm512_castsi256_si512(gf16_affine_load_matrix(scratch, coefficients[4])); matNormE = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); matSwapE = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); } if(srcCount > 5) { depmask = gf16_affine_load2_matrix(scratch, coefficients[4], coefficients[5]); matNormE = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); matSwapE = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); matNormF = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(2,2,2,2)); matSwapF = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(3,3,3,3)); } if(srcCount == 7) { depmask = _mm512_castsi256_si512(gf16_affine_load_matrix(scratch, coefficients[6])); matNormG = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); matSwapG = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); } if(srcCount > 7) { depmask = gf16_affine_load2_matrix(scratch, coefficients[6], coefficients[7]); matNormG = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); matSwapG = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); matNormH = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(2,2,2,2)); matSwapH = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(3,3,3,3)); } if(srcCount == 9) { depmask = _mm512_castsi256_si512(gf16_affine_load_matrix(scratch, coefficients[8])); matNormI = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); matSwapI = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); } if(srcCount > 9) { depmask = gf16_affine_load2_matrix(scratch, coefficients[8], coefficients[9]); matNormI = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); matSwapI = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); matNormJ = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(2,2,2,2)); matSwapJ = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(3,3,3,3)); } if(srcCount == 11) { depmask = _mm512_castsi256_si512(gf16_affine_load_matrix(scratch, coefficients[10])); matNormK = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); matSwapK = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); } if(srcCount > 11) { depmask = gf16_affine_load2_matrix(scratch, coefficients[10], coefficients[11]); matNormK = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); matSwapK = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); matNormL = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(2,2,2,2)); matSwapL = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(3,3,3,3)); } if(srcCount == 13) { depmask = _mm512_castsi256_si512(gf16_affine_load_matrix(scratch, coefficients[12])); matNormM = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); matSwapM = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); } # else #define LOAD_SRC(n, mat) \ if(srcCount > n) { \ depmask = gf16_affine_load_matrix(scratch, coefficients[n]); \ matNorm##mat = _mm256_inserti128_si256(depmask, _mm256_castsi256_si128(depmask), 1); \ matSwap##mat = _mm256_permute2x128_si256(depmask, depmask, 0x11); \ } LOAD_SRC(0, A) LOAD_SRC(1, B) LOAD_SRC(2, C) LOAD_SRC(3, D) LOAD_SRC(4, E) LOAD_SRC(5, F) LOAD_SRC(6, G) LOAD_SRC(7, H) LOAD_SRC(8, I) LOAD_SRC(9, J) LOAD_SRC(10, K) LOAD_SRC(11, L) LOAD_SRC(12, M) #undef LOAD_SRC # endif for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(_mword)) { _mword data = _MMI(load)((_mword*)(_src1 + ptr*srcScale)); _mword result = _MM(gf2p8affine_epi64_epi8)(data, matNormA, 0); _mword swapped = _MM(gf2p8affine_epi64_epi8)(data, matSwapA, 0); if(srcCount > 1) data = _MMI(load)((_mword*)(_src2 + ptr*srcScale)); if(srcCount >= 3) { _mword data2 = _MMI(load)((_mword*)(_src3 + ptr*srcScale)); result = _MM(ternarylogic_epi32)( result, _MM(gf2p8affine_epi64_epi8)(data, matNormB, 0), _MM(gf2p8affine_epi64_epi8)(data2, matNormC, 0), 0x96 ); swapped = _MM(ternarylogic_epi32)( swapped, _MM(gf2p8affine_epi64_epi8)(data, matSwapB, 0), _MM(gf2p8affine_epi64_epi8)(data2, matSwapC, 0), 0x96 ); } else if(srcCount == 2) { result = _MMI(xor)( result, _MM(gf2p8affine_epi64_epi8)(data, matNormB, 0) ); swapped = _MMI(xor)( swapped, _MM(gf2p8affine_epi64_epi8)(data, matSwapB, 0) ); } _FN(gf16_affine2x_muladd_2round)(srcCount - 4, _src4, _src5, ptr*srcScale, &result, &swapped, matNormD, matSwapD, matNormE, matSwapE); _FN(gf16_affine2x_muladd_2round)(srcCount - 6, _src6, _src7, ptr*srcScale, &result, &swapped, matNormF, matSwapF, matNormG, matSwapG); _FN(gf16_affine2x_muladd_2round)(srcCount - 8, _src8, _src9, ptr*srcScale, &result, &swapped, matNormH, matSwapH, matNormI, matSwapI); _FN(gf16_affine2x_muladd_2round)(srcCount - 10, _src10, _src11, ptr*srcScale, &result, &swapped, matNormJ, matSwapJ, matNormK, matSwapK); _FN(gf16_affine2x_muladd_2round)(srcCount - 12, _src12, _src13, ptr*srcScale, &result, &swapped, matNormL, matSwapL, matNormM, matSwapM); result = _MM(ternarylogic_epi32)( result, _MM(shuffle_epi32)(swapped, _MM_SHUFFLE(1,0,3,2)), _MMI(load)((_mword*)(_dst + ptr)), 0x96 ); _MMI(store) ((_mword*)(_dst + ptr), result); if(doPrefetch == 1) _mm_prefetch(_pf+ptr, MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+ptr, _MM_HINT_T1); } } #endif /*defined(_AVAILABLE)*/ void _FN(gf16_affine2x_muladd)(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) gf16_muladd_single(scratch, &_FN(gf16_affine2x_muladd_x), dst, src, len, coefficient); _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) # ifdef PLATFORM_AMD64 // TODO: may not want 12 regions for non-packed variant GF16_MULADD_MULTI_FUNCS(gf16_affine2x, _FNSUFFIX, _FN(gf16_affine2x_muladd_x), 12, sizeof(_mword), 0, _mm256_zeroupper()) # else // if only 8 registers available, only allow 2 parallel regions GF16_MULADD_MULTI_FUNCS(gf16_affine2x, _FNSUFFIX, _FN(gf16_affine2x_muladd_x), 2, sizeof(_mword), 0, _mm256_zeroupper()) # endif #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_affine2x, _FNSUFFIX) #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_affine_avx2.c000066400000000000000000000402251514221355600222630ustar00rootroot00000000000000 #include "gf16_global.h" #include "../src/platform.h" #define MWORD_SIZE 32 #define _mword __m256i #define _MM(f) _mm256_ ## f #define _MMI(f) _mm256_ ## f ## _si256 #define _FNSUFFIX _avx2 #define _FNPREP(f) f##_avx2 #define _MM_END _mm256_zeroupper(); #if defined(__GFNI__) && defined(__AVX2__) int gf16_affine_available_avx2 = 1; # define _AVAILABLE 1 # include "gf16_shuffle_x86_prepare.h" # include "gf16_checksum_x86.h" #else int gf16_affine_available_avx2 = 0; #endif #define AFFINE2X_AMD64_INTERLEAVE 6 #include "gf16_affine2x_x86.h" #ifdef _AVAILABLE # undef _AVAILABLE #endif #undef _MM_END #undef _FNSUFFIX #undef _FNPREP #undef _MMI #undef _MM #undef _mword #undef MWORD_SIZE #include "gf16_muladd_multi.h" #if defined(__GFNI__) && defined(__AVX2__) # ifdef PLATFORM_AMD64 GF_PREPARE_PACKED_FUNCS(gf16_affine, _avx2, sizeof(__m256i)*2, gf16_shuffle_prepare_block_avx2, gf16_shuffle_prepare_blocku_avx2, 3, _mm256_zeroupper(), __m256i checksum = _mm256_setzero_si256(), gf16_checksum_block_avx2, gf16_checksum_blocku_avx2, gf16_checksum_exp_avx2, gf16_checksum_prepare_avx2, sizeof(__m256i)) # else GF_PREPARE_PACKED_FUNCS(gf16_affine, _avx2, sizeof(__m256i)*2, gf16_shuffle_prepare_block_avx2, gf16_shuffle_prepare_blocku_avx2, 1, _mm256_zeroupper(), __m256i checksum = _mm256_setzero_si256(), gf16_checksum_block_avx2, gf16_checksum_blocku_avx2, gf16_checksum_exp_avx2, gf16_checksum_prepare_avx2, sizeof(__m256i)) # endif #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_affine, _avx2) #endif #if defined(__GFNI__) && defined(__AVX2__) static HEDLEY_ALWAYS_INLINE __m256i gf16_affine_load_matrix(const void *HEDLEY_RESTRICT scratch, uint16_t coefficient) { __m256i depmask = _mm256_xor_si256( _mm256_load_si256((__m256i*)scratch + (coefficient & 0xf)*4), _mm256_load_si256((__m256i*)((char*)scratch + ((coefficient << 3) & 0x780)) + 1) ); depmask = _mm256_xor_si256(depmask, _mm256_load_si256((__m256i*)((char*)scratch + ((coefficient >> 1) & 0x780)) + 2)); depmask = _mm256_xor_si256(depmask, _mm256_load_si256((__m256i*)((char*)scratch + ((coefficient >> 5) & 0x780)) + 3)); return depmask; } #endif #ifdef PARPAR_INVERT_SUPPORT void gf16_affine_mul_avx2(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__GFNI__) && defined(__AVX2__) __m256i depmask = gf16_affine_load_matrix(scratch, coefficient); __m256i mat_ll = _mm256_broadcastq_epi64(_mm256_castsi256_si128(depmask)); __m256i mat_hh = _mm256_permute4x64_epi64(depmask, _MM_SHUFFLE(1,1,1,1)); __m256i mat_lh = _mm256_permute4x64_epi64(depmask, _MM_SHUFFLE(3,3,3,3)); __m256i mat_hl = _mm256_permute4x64_epi64(depmask, _MM_SHUFFLE(2,2,2,2)); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m256i)*2) { __m256i ta = _mm256_load_si256((__m256i*)(_src + ptr)); __m256i tb = _mm256_load_si256((__m256i*)(_src + ptr) + 1); __m256i tpl = _mm256_xor_si256( _mm256_gf2p8affine_epi64_epi8(ta, mat_lh, 0), _mm256_gf2p8affine_epi64_epi8(tb, mat_ll, 0) ); __m256i tph = _mm256_xor_si256( _mm256_gf2p8affine_epi64_epi8(ta, mat_hh, 0), _mm256_gf2p8affine_epi64_epi8(tb, mat_hl, 0) ); _mm256_store_si256 ((__m256i*)(_dst + ptr), tph); _mm256_store_si256 ((__m256i*)(_dst + ptr) + 1, tpl); } _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } #endif #if defined(__GFNI__) && defined(__AVX2__) static HEDLEY_ALWAYS_INLINE void gf16_affine_muladd_round(const __m256i* src, __m256i* tpl, __m256i* tph, __m256i mat_ll, __m256i mat_hl, __m256i mat_lh, __m256i mat_hh) { __m256i ta = _mm256_load_si256(src); __m256i tb = _mm256_load_si256(src + 1); *tpl = _mm256_xor_si256(*tpl, _mm256_gf2p8affine_epi64_epi8(ta, mat_lh, 0)); *tpl = _mm256_xor_si256(*tpl, _mm256_gf2p8affine_epi64_epi8(tb, mat_ll, 0)); *tph = _mm256_xor_si256(*tph, _mm256_gf2p8affine_epi64_epi8(ta, mat_hh, 0)); *tph = _mm256_xor_si256(*tph, _mm256_gf2p8affine_epi64_epi8(tb, mat_hl, 0)); } static HEDLEY_ALWAYS_INLINE void gf16_affine_muladd_x_avx2( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(3); __m256i depmask = gf16_affine_load_matrix(scratch, coefficients[0]); __m256i mat_All = _mm256_broadcastq_epi64(_mm256_castsi256_si128(depmask)); __m256i mat_Ahh = _mm256_permute4x64_epi64(depmask, _MM_SHUFFLE(1,1,1,1)); __m256i mat_Alh = _mm256_permute4x64_epi64(depmask, _MM_SHUFFLE(3,3,3,3)); __m256i mat_Ahl = _mm256_permute4x64_epi64(depmask, _MM_SHUFFLE(2,2,2,2)); __m256i mat_Bll, mat_Bhh, mat_Bhl, mat_Blh; if(srcCount >= 2) { depmask = gf16_affine_load_matrix(scratch, coefficients[1]); mat_Bll = _mm256_broadcastq_epi64(_mm256_castsi256_si128(depmask)); mat_Bhh = _mm256_permute4x64_epi64(depmask, _MM_SHUFFLE(1,1,1,1)); mat_Blh = _mm256_permute4x64_epi64(depmask, _MM_SHUFFLE(3,3,3,3)); mat_Bhl = _mm256_permute4x64_epi64(depmask, _MM_SHUFFLE(2,2,2,2)); } __m256i mat_Cll, mat_Chh, mat_Chl, mat_Clh; if(srcCount > 2) { depmask = gf16_affine_load_matrix(scratch, coefficients[2]); mat_Cll = _mm256_broadcastq_epi64(_mm256_castsi256_si128(depmask)); mat_Chh = _mm256_permute4x64_epi64(depmask, _MM_SHUFFLE(1,1,1,1)); mat_Clh = _mm256_permute4x64_epi64(depmask, _MM_SHUFFLE(3,3,3,3)); mat_Chl = _mm256_permute4x64_epi64(depmask, _MM_SHUFFLE(2,2,2,2)); } for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m256i)*2) { __m256i tph = _mm256_load_si256((__m256i*)(_dst + ptr)); __m256i tpl = _mm256_load_si256((__m256i*)(_dst + ptr) + 1); gf16_affine_muladd_round((__m256i*)(_src1 + ptr*srcScale), &tpl, &tph, mat_All, mat_Ahl, mat_Alh, mat_Ahh); if(srcCount > 1) gf16_affine_muladd_round((__m256i*)(_src2 + ptr*srcScale), &tpl, &tph, mat_Bll, mat_Bhl, mat_Blh, mat_Bhh); if(srcCount > 2) gf16_affine_muladd_round((__m256i*)(_src3 + ptr*srcScale), &tpl, &tph, mat_Cll, mat_Chl, mat_Clh, mat_Chh); _mm256_store_si256 ((__m256i*)(_dst + ptr), tph); _mm256_store_si256 ((__m256i*)(_dst + ptr)+1, tpl); if(doPrefetch == 1) _mm_prefetch(_pf+(ptr>>1), MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+(ptr>>1), _MM_HINT_T2); } } #endif /*defined(__GFNI__) && defined(__AVX2__)*/ void gf16_affine_muladd_avx2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__GFNI__) && defined(__AVX2__) gf16_muladd_single(scratch, &gf16_affine_muladd_x_avx2, dst, src, len, coefficient); _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } void gf16_affine_muladd_prefetch_avx2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch) { UNUSED(mutScratch); #if defined(__GFNI__) && defined(__AVX2__) gf16_muladd_prefetch_single(scratch, &gf16_affine_muladd_x_avx2, dst, src, len, coefficient, prefetch); _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); UNUSED(prefetch); #endif } #if defined(__GFNI__) && defined(__AVX2__) && defined(PLATFORM_AMD64) GF16_MULADD_MULTI_FUNCS(gf16_affine, _avx2, gf16_affine_muladd_x_avx2, 3, sizeof(__m256i)*2, 1, _mm256_zeroupper()) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_affine, _avx2) #endif #if defined(__GFNI__) && defined(__AVX2__) # include "gf16_bitdep_init_avx2.h" #endif void* gf16_affine_init_avx2(int polynomial) { #if defined(__GFNI__) && defined(__AVX2__) __m256i* ret; ALIGN_ALLOC(ret, sizeof(__m256i)*16*4, 32); gf16_bitdep_init256(ret, polynomial, 1); return ret; #else UNUSED(polynomial); return NULL; #endif } #if defined(__GFNI__) && defined(__AVX2__) && !defined(PARPAR_SLIM_GF16) static HEDLEY_ALWAYS_INLINE void gf16_affine2x_muladd_x_avx2( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(6); __m256i matNormA, matSwapA; __m256i matNormB, matSwapB; __m256i matNormC, matSwapC; __m256i matNormD, matSwapD; __m256i matNormE, matSwapE; __m256i matNormF, matSwapF; // prevent MSVC whining matNormB = matSwapB = matNormC = matSwapC = matNormD = matSwapD = matNormE = matSwapE = matNormF = matSwapF = _mm256_undefined_si256(); __m256i depmask = gf16_affine_load_matrix(scratch, coefficients[0]); matNormA = _mm256_inserti128_si256(depmask, _mm256_castsi256_si128(depmask), 1); matSwapA = _mm256_permute2x128_si256(depmask, depmask, 0x11); if(srcCount >= 2) { depmask = gf16_affine_load_matrix(scratch, coefficients[1]); matNormB = _mm256_inserti128_si256(depmask, _mm256_castsi256_si128(depmask), 1); matSwapB = _mm256_permute2x128_si256(depmask, depmask, 0x11); } if(srcCount >= 3) { depmask = gf16_affine_load_matrix(scratch, coefficients[2]); matNormC = _mm256_inserti128_si256(depmask, _mm256_castsi256_si128(depmask), 1); matSwapC = _mm256_permute2x128_si256(depmask, depmask, 0x11); } if(srcCount >= 4) { depmask = gf16_affine_load_matrix(scratch, coefficients[3]); matNormD = _mm256_inserti128_si256(depmask, _mm256_castsi256_si128(depmask), 1); matSwapD = _mm256_permute2x128_si256(depmask, depmask, 0x11); } if(srcCount >= 5) { depmask = gf16_affine_load_matrix(scratch, coefficients[4]); matNormE = _mm256_inserti128_si256(depmask, _mm256_castsi256_si128(depmask), 1); matSwapE = _mm256_permute2x128_si256(depmask, depmask, 0x11); } if(srcCount >= 6) { depmask = gf16_affine_load_matrix(scratch, coefficients[5]); matNormF = _mm256_inserti128_si256(depmask, _mm256_castsi256_si128(depmask), 1); matSwapF = _mm256_permute2x128_si256(depmask, depmask, 0x11); } intptr_t ptr = -(intptr_t)len; if(doPrefetch) { if(doPrefetch == 1) _mm_prefetch(_pf+ptr, MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+ptr, _MM_HINT_T2); if(ptr & (sizeof(__m256i)*2-1)) { // align to a cacheline boundary __m256i data = _mm256_load_si256((__m256i*)(_src1 + ptr*srcScale)); __m256i result1 = _mm256_gf2p8affine_epi64_epi8(data, matNormA, 0); __m256i result2 = _mm256_gf2p8affine_epi64_epi8(data, matSwapA, 0); if(srcCount >= 2) { data = _mm256_load_si256((__m256i*)(_src2 + ptr*srcScale)); result1 = _mm256_xor_si256(result1, _mm256_gf2p8affine_epi64_epi8(data, matNormB, 0)); result2 = _mm256_xor_si256(result2, _mm256_gf2p8affine_epi64_epi8(data, matSwapB, 0)); } if(srcCount >= 3) { data = _mm256_load_si256((__m256i*)(_src3 + ptr*srcScale)); result1 = _mm256_xor_si256(result1, _mm256_gf2p8affine_epi64_epi8(data, matNormC, 0)); result2 = _mm256_xor_si256(result2, _mm256_gf2p8affine_epi64_epi8(data, matSwapC, 0)); } if(srcCount >= 4) { data = _mm256_load_si256((__m256i*)(_src4 + ptr*srcScale)); result1 = _mm256_xor_si256(result1, _mm256_gf2p8affine_epi64_epi8(data, matNormD, 0)); result2 = _mm256_xor_si256(result2, _mm256_gf2p8affine_epi64_epi8(data, matSwapD, 0)); } if(srcCount >= 5) { data = _mm256_load_si256((__m256i*)(_src5 + ptr*srcScale)); result1 = _mm256_xor_si256(result1, _mm256_gf2p8affine_epi64_epi8(data, matNormE, 0)); result2 = _mm256_xor_si256(result2, _mm256_gf2p8affine_epi64_epi8(data, matSwapE, 0)); } if(srcCount >= 6) { data = _mm256_load_si256((__m256i*)(_src6 + ptr*srcScale)); result1 = _mm256_xor_si256(result1, _mm256_gf2p8affine_epi64_epi8(data, matNormF, 0)); result2 = _mm256_xor_si256(result2, _mm256_gf2p8affine_epi64_epi8(data, matSwapF, 0)); } result1 = _mm256_xor_si256(result1, _mm256_load_si256((__m256i*)(_dst + ptr))); result1 = _mm256_xor_si256(result1, _mm256_shuffle_epi32(result2, _MM_SHUFFLE(1,0,3,2))); _mm256_store_si256((__m256i*)(_dst + ptr), result1); ptr += sizeof(__m256i); } } while(ptr) { if(doPrefetch == 1) _mm_prefetch(_pf+ptr, MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+ptr, _MM_HINT_T2); for(int iter=0; iter<(doPrefetch?2:1); iter++) { // if prefetching, iterate on cachelines __m256i data = _mm256_load_si256((__m256i*)(_src1 + ptr*srcScale)); __m256i result1 = _mm256_gf2p8affine_epi64_epi8(data, matNormA, 0); __m256i result2 = _mm256_gf2p8affine_epi64_epi8(data, matSwapA, 0); if(srcCount >= 2) { data = _mm256_load_si256((__m256i*)(_src2 + ptr*srcScale)); result1 = _mm256_xor_si256(result1, _mm256_gf2p8affine_epi64_epi8(data, matNormB, 0)); result2 = _mm256_xor_si256(result2, _mm256_gf2p8affine_epi64_epi8(data, matSwapB, 0)); } if(srcCount >= 3) { data = _mm256_load_si256((__m256i*)(_src3 + ptr*srcScale)); result1 = _mm256_xor_si256(result1, _mm256_gf2p8affine_epi64_epi8(data, matNormC, 0)); result2 = _mm256_xor_si256(result2, _mm256_gf2p8affine_epi64_epi8(data, matSwapC, 0)); } if(srcCount >= 4) { data = _mm256_load_si256((__m256i*)(_src4 + ptr*srcScale)); result1 = _mm256_xor_si256(result1, _mm256_gf2p8affine_epi64_epi8(data, matNormD, 0)); result2 = _mm256_xor_si256(result2, _mm256_gf2p8affine_epi64_epi8(data, matSwapD, 0)); } if(srcCount >= 5) { data = _mm256_load_si256((__m256i*)(_src5 + ptr*srcScale)); result1 = _mm256_xor_si256(result1, _mm256_gf2p8affine_epi64_epi8(data, matNormE, 0)); result2 = _mm256_xor_si256(result2, _mm256_gf2p8affine_epi64_epi8(data, matSwapE, 0)); } if(srcCount >= 6) { data = _mm256_load_si256((__m256i*)(_src6 + ptr*srcScale)); result1 = _mm256_xor_si256(result1, _mm256_gf2p8affine_epi64_epi8(data, matNormF, 0)); result2 = _mm256_xor_si256(result2, _mm256_gf2p8affine_epi64_epi8(data, matSwapF, 0)); } result1 = _mm256_xor_si256(result1, _mm256_load_si256((__m256i*)(_dst + ptr))); result1 = _mm256_xor_si256(result1, _mm256_shuffle_epi32(result2, _MM_SHUFFLE(1,0,3,2))); _mm256_store_si256((__m256i*)(_dst + ptr), result1); ptr += sizeof(__m256i); } } } #endif /*defined(__GFNI__) && defined(__AVX2__) && !defined(PARPAR_SLIM_GF16)*/ #ifdef PARPAR_INVERT_SUPPORT void gf16_affine2x_mul_avx2(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__GFNI__) && defined(__AVX2__) && !defined(PARPAR_SLIM_GF16) __m256i depmask = gf16_affine_load_matrix(scratch, coefficient); __m256i matNorm = _mm256_inserti128_si256(depmask, _mm256_castsi256_si128(depmask), 1); __m256i matSwap = _mm256_permute2x128_si256(depmask, depmask, 0x11); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m256i)) { __m256i data = _mm256_load_si256((__m256i*)(_src + ptr)); __m256i result1 = _mm256_gf2p8affine_epi64_epi8(data, matNorm, 0); __m256i result2 = _mm256_gf2p8affine_epi64_epi8(data, matSwap, 0); result1 = _mm256_xor_si256(result1, _mm256_shuffle_epi32(result2, _MM_SHUFFLE(1,0,3,2))); _mm256_store_si256((__m256i*)(_dst + ptr), result1); } #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } #endif void gf16_affine2x_muladd_avx2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__GFNI__) && defined(__AVX2__) && !defined(PARPAR_SLIM_GF16) gf16_muladd_single(scratch, &gf16_affine2x_muladd_x_avx2, dst, src, len, coefficient); _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } #if defined(__GFNI__) && defined(__AVX2__) && !defined(PARPAR_SLIM_GF16) # ifdef PLATFORM_AMD64 GF16_MULADD_MULTI_FUNCS(gf16_affine2x, _avx2, gf16_affine2x_muladd_x_avx2, 6, sizeof(__m256i), 0, _mm256_zeroupper()) # else GF16_MULADD_MULTI_FUNCS(gf16_affine2x, _avx2, gf16_affine2x_muladd_x_avx2, 2, sizeof(__m256i), 0, _mm256_zeroupper()) # endif #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_affine2x, _avx2) #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_affine_avx512.c000066400000000000000000000104151514221355600224270ustar00rootroot00000000000000 #include "gf16_global.h" #include "../src/platform.h" #define MWORD_SIZE 64 #define _mword __m512i #define _MM(f) _mm512_ ## f #define _MMI(f) _mm512_ ## f ## _si512 #define _FNSUFFIX _avx512 #define _FNPREP(f) f##_avx512 #define _MM_END _mm256_zeroupper(); #if defined(__GFNI__) && defined(__AVX512BW__) && defined(__AVX512VL__) # define _AVAILABLE 1 static HEDLEY_ALWAYS_INLINE __m512i gf16_affine_load2_matrix(const void *HEDLEY_RESTRICT scratch, uint16_t coeff1, uint16_t coeff2) { __m512i depmask = _mm512_xor_si512( _mm512_inserti64x4( _mm512_castsi256_si512(_mm256_load_si256((__m256i*)scratch + (coeff1 & 0xf)*4)), _mm256_load_si256((__m256i*)scratch + (coeff2 & 0xf)*4), 1 ), _mm512_inserti64x4( _mm512_castsi256_si512(_mm256_load_si256((__m256i*)((char*)scratch + ((coeff1 << 3) & 0x780)) + 1)), _mm256_load_si256((__m256i*)((char*)scratch + ((coeff2 << 3) & 0x780)) + 1), 1 ) ); depmask = _mm512_ternarylogic_epi32( depmask, _mm512_inserti64x4( _mm512_castsi256_si512(_mm256_load_si256((__m256i*)((char*)scratch + ((coeff1 >> 1) & 0x780)) + 2)), _mm256_load_si256((__m256i*)((char*)scratch + ((coeff2 >> 1) & 0x780)) + 2), 1 ), _mm512_inserti64x4( _mm512_castsi256_si512(_mm256_load_si256((__m256i*)((char*)scratch + ((coeff1 >> 5) & 0x780)) + 3)), _mm256_load_si256((__m256i*)((char*)scratch + ((coeff2 >> 5) & 0x780)) + 3), 1 ), 0x96 ); return depmask; } #endif #include "gf16_affine_avx10.h" #ifdef _AVAILABLE # undef _AVAILABLE #endif #undef _MM_END #undef _FNSUFFIX #undef _FNPREP #undef _MMI #undef _MM #undef _mword #undef MWORD_SIZE #ifdef PARPAR_INVERT_SUPPORT void gf16_affine_mul_avx512(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__GFNI__) && defined(__AVX512BW__) && defined(__AVX512VL__) __m256i depmask = gf16_affine_load_matrix(scratch, coefficient); __m512i mat_ll, mat_lh, mat_hl, mat_hh; __m512i depmask2 = _mm512_castsi256_si512(depmask); depmask2 = _mm512_shuffle_i64x2(depmask2, depmask2, _MM_SHUFFLE(0,1,0,1)); // reverse order to allow more abuse of VBROADCASTQ mat_hh = _mm512_permutex_epi64(depmask2, _MM_SHUFFLE(3,3,3,3)); mat_lh = _mm512_permutex_epi64(depmask2, _MM_SHUFFLE(1,1,1,1)); mat_ll = _mm512_broadcastq_epi64(_mm256_castsi256_si128(depmask)); mat_hl = _mm512_broadcastq_epi64(_mm512_castsi512_si128(depmask2)); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m512i)*2) { __m512i ta = _mm512_load_si512((__m512i*)(_src + ptr)); __m512i tb = _mm512_load_si512((__m512i*)(_src + ptr) + 1); __m512i tpl = _mm512_xor_si512( _mm512_gf2p8affine_epi64_epi8(ta, mat_lh, 0), _mm512_gf2p8affine_epi64_epi8(tb, mat_ll, 0) ); __m512i tph = _mm512_xor_si512( _mm512_gf2p8affine_epi64_epi8(ta, mat_hh, 0), _mm512_gf2p8affine_epi64_epi8(tb, mat_hl, 0) ); _mm512_store_si512 ((__m512i*)(_dst + ptr), tph); _mm512_store_si512 ((__m512i*)(_dst + ptr) + 1, tpl); } _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } void gf16_affine2x_mul_avx512(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__GFNI__) && defined(__AVX512BW__) && defined(__AVX512VL__) && !defined(PARPAR_SLIM_GF16) __m512i depmask = _mm512_castsi256_si512(gf16_affine_load_matrix(scratch, coefficient)); __m512i matNorm = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(0,0,0,0)); __m512i matSwap = _mm512_shuffle_i64x2(depmask, depmask, _MM_SHUFFLE(1,1,1,1)); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m512i)) { __m512i data = _mm512_load_si512((__m512i*)(_src + ptr)); __m512i result = _mm512_gf2p8affine_epi64_epi8(data, matNorm, 0); __m512i swapped = _mm512_gf2p8affine_epi64_epi8(data, matSwap, 0); result = _mm512_xor_si512(result, _mm512_shuffle_epi32(swapped, _MM_SHUFFLE(1,0,3,2))); _mm512_store_si512((__m512i*)(_dst + ptr), result); } #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_affine_gfni.c000066400000000000000000000426641514221355600223370ustar00rootroot00000000000000 #include "gf16_global.h" #include "../src/platform.h" #include #define MWORD_SIZE 16 #define _mword __m128i #define _MM(f) _mm_ ## f #define _MMI(f) _mm_ ## f ## _si128 #define _FNSUFFIX _gfni #define _FNPREP(f) f##_gfni #define _MM_END #if defined(__GFNI__) && defined(__SSSE3__) int gf16_affine_available_gfni = 1; # define _AVAILABLE 1 # include "gf16_shuffle_x86_prepare.h" # include "gf16_checksum_x86.h" #else int gf16_affine_available_gfni = 0; #endif #define AFFINE2X_AMD64_INTERLEAVE 6 #include "gf16_affine2x_x86.h" #ifdef _AVAILABLE # undef _AVAILABLE #endif #undef _MM_END #undef _FNSUFFIX #undef _FNPREP #undef _MMI #undef _MM #undef _mword #undef MWORD_SIZE #include "gf16_muladd_multi.h" #if defined(__GFNI__) && defined(__SSSE3__) # ifdef PLATFORM_AMD64 GF_PREPARE_PACKED_FUNCS(gf16_affine, _gfni, sizeof(__m128i)*2, gf16_shuffle_prepare_block_gfni, gf16_shuffle_prepare_blocku_gfni, 3, (void)0, __m128i checksum = _mm_setzero_si128(), gf16_checksum_block_gfni, gf16_checksum_blocku_gfni, gf16_checksum_exp_gfni, gf16_checksum_prepare_gfni, sizeof(__m128i)) # else GF_PREPARE_PACKED_FUNCS(gf16_affine, _gfni, sizeof(__m128i)*2, gf16_shuffle_prepare_block_gfni, gf16_shuffle_prepare_blocku_gfni, 1, (void)0, __m128i checksum = _mm_setzero_si128(), gf16_checksum_block_gfni, gf16_checksum_blocku_gfni, gf16_checksum_exp_gfni, gf16_checksum_prepare_gfni, sizeof(__m128i)) # endif #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_affine, _gfni) #endif #if defined(__GFNI__) && defined(__SSSE3__) static HEDLEY_ALWAYS_INLINE void gf16_affine_load_matrix(const void *HEDLEY_RESTRICT scratch, uint16_t coefficient, __m128i* depmask1, __m128i* depmask2) { *depmask1 = _mm_load_si128((__m128i*)((char*)scratch + ((coefficient & 0xf) << 7))); *depmask2 = _mm_load_si128((__m128i*)((char*)scratch + ((coefficient & 0xf) << 7)) +1); *depmask1 = _mm_xor_si128(*depmask1, _mm_load_si128((__m128i*)((char*)scratch + ((coefficient << 3) & 0x780)) + 1*2)); *depmask2 = _mm_xor_si128(*depmask2, _mm_load_si128((__m128i*)((char*)scratch + ((coefficient << 3) & 0x780)) + 1*2 +1)); *depmask1 = _mm_xor_si128(*depmask1, _mm_load_si128((__m128i*)((char*)scratch + ((coefficient >> 1) & 0x780)) + 2*2)); *depmask2 = _mm_xor_si128(*depmask2, _mm_load_si128((__m128i*)((char*)scratch + ((coefficient >> 1) & 0x780)) + 2*2 +1)); *depmask1 = _mm_xor_si128(*depmask1, _mm_load_si128((__m128i*)((char*)scratch + ((coefficient >> 5) & 0x780)) + 3*2)); *depmask2 = _mm_xor_si128(*depmask2, _mm_load_si128((__m128i*)((char*)scratch + ((coefficient >> 5) & 0x780)) + 3*2 +1)); } #endif #ifdef PARPAR_INVERT_SUPPORT void gf16_affine_mul_gfni(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__GFNI__) && defined(__SSSE3__) __m128i depmask1, depmask2; gf16_affine_load_matrix(scratch, coefficient, &depmask1, &depmask2); __m128i mat_ll = _mm_shuffle_epi32(depmask1, _MM_SHUFFLE(1,0,1,0)); // allows src+dst in SSE encoding __m128i mat_hh = _mm_unpackhi_epi64(depmask1, depmask1); // shorter instruction than above, but destructive __m128i mat_hl = _mm_shuffle_epi32(depmask2, _MM_SHUFFLE(1,0,1,0)); __m128i mat_lh = _mm_unpackhi_epi64(depmask2, depmask2); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m128i)*2) { __m128i ta = _mm_load_si128((__m128i*)(_src + ptr)); __m128i tb = _mm_load_si128((__m128i*)(_src + ptr) + 1); __m128i tpl = _mm_xor_si128( _mm_gf2p8affine_epi64_epi8(ta, mat_lh, 0), _mm_gf2p8affine_epi64_epi8(tb, mat_ll, 0) ); __m128i tph = _mm_xor_si128( _mm_gf2p8affine_epi64_epi8(ta, mat_hh, 0), _mm_gf2p8affine_epi64_epi8(tb, mat_hl, 0) ); _mm_store_si128 ((__m128i*)(_dst + ptr), tph); _mm_store_si128 ((__m128i*)(_dst + ptr) + 1, tpl); } #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } #endif #if defined(__GFNI__) && defined(__SSSE3__) static HEDLEY_ALWAYS_INLINE void gf16_affine_muladd_round(const __m128i* src, __m128i* tpl, __m128i* tph, __m128i mat_ll, __m128i mat_hl, __m128i mat_lh, __m128i mat_hh) { __m128i ta = _mm_load_si128(src); __m128i tb = _mm_load_si128(src + 1); *tpl = _mm_xor_si128(*tpl, _mm_gf2p8affine_epi64_epi8(ta, mat_lh, 0)); *tpl = _mm_xor_si128(*tpl, _mm_gf2p8affine_epi64_epi8(tb, mat_ll, 0)); *tph = _mm_xor_si128(*tph, _mm_gf2p8affine_epi64_epi8(ta, mat_hh, 0)); *tph = _mm_xor_si128(*tph, _mm_gf2p8affine_epi64_epi8(tb, mat_hl, 0)); } static HEDLEY_ALWAYS_INLINE void gf16_affine_muladd_x_gfni( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(3); __m128i depmask1, depmask2; gf16_affine_load_matrix(scratch, coefficients[0], &depmask1, &depmask2); __m128i mat_All = _mm_shuffle_epi32(depmask1, _MM_SHUFFLE(1,0,1,0)); __m128i mat_Ahh = _mm_unpackhi_epi64(depmask1, depmask1); __m128i mat_Ahl = _mm_shuffle_epi32(depmask2, _MM_SHUFFLE(1,0,1,0)); __m128i mat_Alh = _mm_unpackhi_epi64(depmask2, depmask2); __m128i mat_Bll, mat_Bhh, mat_Bhl, mat_Blh; if(srcCount >= 2) { gf16_affine_load_matrix(scratch, coefficients[1], &depmask1, &depmask2); mat_Bll = _mm_shuffle_epi32(depmask1, _MM_SHUFFLE(1,0,1,0)); mat_Bhh = _mm_unpackhi_epi64(depmask1, depmask1); mat_Bhl = _mm_shuffle_epi32(depmask2, _MM_SHUFFLE(1,0,1,0)); mat_Blh = _mm_unpackhi_epi64(depmask2, depmask2); } __m128i mat_Cll, mat_Chh, mat_Chl, mat_Clh; if(srcCount > 2) { gf16_affine_load_matrix(scratch, coefficients[2], &depmask1, &depmask2); mat_Cll = _mm_shuffle_epi32(depmask1, _MM_SHUFFLE(1,0,1,0)); mat_Chh = _mm_unpackhi_epi64(depmask1, depmask1); mat_Chl = _mm_shuffle_epi32(depmask2, _MM_SHUFFLE(1,0,1,0)); mat_Clh = _mm_unpackhi_epi64(depmask2, depmask2); } __m128i tph, tpl; if(doPrefetch) { intptr_t ptr = -(intptr_t)len; if(len & (sizeof(__m128i)*4-1)) { // number of loop iterations isn't even, so do one iteration to make it even tph = _mm_load_si128((__m128i*)(_dst + ptr)); tpl = _mm_load_si128((__m128i*)(_dst + ptr) + 1); gf16_affine_muladd_round((__m128i*)(_src1 + ptr*srcScale), &tpl, &tph, mat_All, mat_Ahl, mat_Alh, mat_Ahh); if(srcCount > 1) gf16_affine_muladd_round((__m128i*)(_src2 + ptr*srcScale), &tpl, &tph, mat_Bll, mat_Bhl, mat_Blh, mat_Bhh); if(srcCount > 2) gf16_affine_muladd_round((__m128i*)(_src3 + ptr*srcScale), &tpl, &tph, mat_Cll, mat_Chl, mat_Clh, mat_Chh); _mm_store_si128 ((__m128i*)(_dst + ptr), tph); _mm_store_si128 ((__m128i*)(_dst + ptr)+1, tpl); if(doPrefetch == 1) _mm_prefetch(_pf+ptr, MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+ptr, _MM_HINT_T2); ptr += sizeof(__m128i)*2; } while(ptr) { tph = _mm_load_si128((__m128i*)(_dst + ptr)); tpl = _mm_load_si128((__m128i*)(_dst + ptr) + 1); gf16_affine_muladd_round((__m128i*)(_src1 + ptr*srcScale), &tpl, &tph, mat_All, mat_Ahl, mat_Alh, mat_Ahh); if(srcCount > 1) gf16_affine_muladd_round((__m128i*)(_src2 + ptr*srcScale), &tpl, &tph, mat_Bll, mat_Bhl, mat_Blh, mat_Bhh); if(srcCount > 2) gf16_affine_muladd_round((__m128i*)(_src3 + ptr*srcScale), &tpl, &tph, mat_Cll, mat_Chl, mat_Clh, mat_Chh); _mm_store_si128 ((__m128i*)(_dst + ptr), tph); _mm_store_si128 ((__m128i*)(_dst + ptr)+1, tpl); ptr += sizeof(__m128i)*2; tph = _mm_load_si128((__m128i*)(_dst + ptr)); tpl = _mm_load_si128((__m128i*)(_dst + ptr) + 1); gf16_affine_muladd_round((__m128i*)(_src1 + ptr*srcScale), &tpl, &tph, mat_All, mat_Ahl, mat_Alh, mat_Ahh); if(srcCount > 1) gf16_affine_muladd_round((__m128i*)(_src2 + ptr*srcScale), &tpl, &tph, mat_Bll, mat_Bhl, mat_Blh, mat_Bhh); if(srcCount > 2) gf16_affine_muladd_round((__m128i*)(_src3 + ptr*srcScale), &tpl, &tph, mat_Cll, mat_Chl, mat_Clh, mat_Chh); _mm_store_si128 ((__m128i*)(_dst + ptr), tph); _mm_store_si128 ((__m128i*)(_dst + ptr)+1, tpl); if(doPrefetch == 1) _mm_prefetch(_pf+(ptr>>1), MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+(ptr>>1), _MM_HINT_T2); ptr += sizeof(__m128i)*2; } } else { for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m128i)*2) { tph = _mm_load_si128((__m128i*)(_dst + ptr)); tpl = _mm_load_si128((__m128i*)(_dst + ptr) + 1); gf16_affine_muladd_round((__m128i*)(_src1 + ptr*srcScale), &tpl, &tph, mat_All, mat_Ahl, mat_Alh, mat_Ahh); if(srcCount > 1) gf16_affine_muladd_round((__m128i*)(_src2 + ptr*srcScale), &tpl, &tph, mat_Bll, mat_Bhl, mat_Blh, mat_Bhh); if(srcCount > 2) gf16_affine_muladd_round((__m128i*)(_src3 + ptr*srcScale), &tpl, &tph, mat_Cll, mat_Chl, mat_Clh, mat_Chh); _mm_store_si128 ((__m128i*)(_dst + ptr), tph); _mm_store_si128 ((__m128i*)(_dst + ptr)+1, tpl); } } } #endif /*defined(__GFNI__) && defined(__SSSE3__)*/ void gf16_affine_muladd_gfni(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__GFNI__) && defined(__SSSE3__) gf16_muladd_single(scratch, &gf16_affine_muladd_x_gfni, dst, src, len, coefficient); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } void gf16_affine_muladd_prefetch_gfni(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch) { UNUSED(mutScratch); #if defined(__GFNI__) && defined(__SSSE3__) gf16_muladd_prefetch_single(scratch, &gf16_affine_muladd_x_gfni, dst, src, len, coefficient, prefetch); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); UNUSED(prefetch); #endif } #if defined(__GFNI__) && defined(__SSSE3__) && defined(PLATFORM_AMD64) GF16_MULADD_MULTI_FUNCS(gf16_affine, _gfni, gf16_affine_muladd_x_gfni, 3, sizeof(__m128i)*2, 0, (void)0) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_affine, _gfni) #endif #include "gf16_bitdep_init_sse2.h" void* gf16_affine_init_gfni(int polynomial) { #if defined(__SSSE3__) __m128i* ret; ALIGN_ALLOC(ret, sizeof(__m128i)*8*16, 16); gf16_bitdep_init128(ret, polynomial, GF16_BITDEP_INIT128_GEN_AFFINE); return ret; #else UNUSED(polynomial); return NULL; #endif } #if defined(__GFNI__) && defined(__SSSE3__) && !defined(PARPAR_SLIM_GF16) static HEDLEY_ALWAYS_INLINE void gf16_affine2x_muladd_x_gfni( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(6); __m128i matNormA, matSwapA; __m128i matNormB, matSwapB; __m128i matNormC, matSwapC; __m128i matNormD, matSwapD; __m128i matNormE, matSwapE; __m128i matNormF, matSwapF; // prevent MSVC whining matNormB = matSwapB = matNormC = matSwapC = matNormD = matSwapD = matNormE = matSwapE = matNormF = matSwapF = _mm_undefined_si128(); gf16_affine_load_matrix(scratch, coefficients[0], &matNormA, &matSwapA); if(srcCount >= 2) gf16_affine_load_matrix(scratch, coefficients[1], &matNormB, &matSwapB); if(srcCount >= 3) gf16_affine_load_matrix(scratch, coefficients[2], &matNormC, &matSwapC); if(srcCount >= 4) gf16_affine_load_matrix(scratch, coefficients[3], &matNormD, &matSwapD); if(srcCount >= 5) gf16_affine_load_matrix(scratch, coefficients[4], &matNormE, &matSwapE); if(srcCount >= 6) gf16_affine_load_matrix(scratch, coefficients[5], &matNormF, &matSwapF); intptr_t ptr = -(intptr_t)len; if(doPrefetch) { if(doPrefetch == 1) _mm_prefetch(_pf+ptr, MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+ptr, _MM_HINT_T2); while(ptr & (sizeof(__m128i)*4-1)) { // loop until we reach a cacheline boundary __m128i data = _mm_load_si128((__m128i*)(_src1 + ptr*srcScale)); __m128i result1 = _mm_gf2p8affine_epi64_epi8(data, matNormA, 0); __m128i result2 = _mm_gf2p8affine_epi64_epi8(data, matSwapA, 0); if(srcCount >= 2) { data = _mm_load_si128((__m128i*)(_src2 + ptr*srcScale)); result1 = _mm_xor_si128(result1, _mm_gf2p8affine_epi64_epi8(data, matNormB, 0)); result2 = _mm_xor_si128(result2, _mm_gf2p8affine_epi64_epi8(data, matSwapB, 0)); } if(srcCount >= 3) { data = _mm_load_si128((__m128i*)(_src3 + ptr*srcScale)); result1 = _mm_xor_si128(result1, _mm_gf2p8affine_epi64_epi8(data, matNormC, 0)); result2 = _mm_xor_si128(result2, _mm_gf2p8affine_epi64_epi8(data, matSwapC, 0)); } if(srcCount >= 4) { data = _mm_load_si128((__m128i*)(_src4 + ptr*srcScale)); result1 = _mm_xor_si128(result1, _mm_gf2p8affine_epi64_epi8(data, matNormD, 0)); result2 = _mm_xor_si128(result2, _mm_gf2p8affine_epi64_epi8(data, matSwapD, 0)); } if(srcCount >= 5) { data = _mm_load_si128((__m128i*)(_src5 + ptr*srcScale)); result1 = _mm_xor_si128(result1, _mm_gf2p8affine_epi64_epi8(data, matNormE, 0)); result2 = _mm_xor_si128(result2, _mm_gf2p8affine_epi64_epi8(data, matSwapE, 0)); } if(srcCount >= 6) { data = _mm_load_si128((__m128i*)(_src6 + ptr*srcScale)); result1 = _mm_xor_si128(result1, _mm_gf2p8affine_epi64_epi8(data, matNormF, 0)); result2 = _mm_xor_si128(result2, _mm_gf2p8affine_epi64_epi8(data, matSwapF, 0)); } result1 = _mm_xor_si128(result1, _mm_load_si128((__m128i*)(_dst + ptr))); result1 = _mm_xor_si128(result1, _mm_shuffle_epi32(result2, _MM_SHUFFLE(1,0,3,2))); _mm_store_si128((__m128i*)(_dst + ptr), result1); ptr += sizeof(__m128i); } } while(ptr) { if(doPrefetch == 1) _mm_prefetch(_pf+ptr, MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+ptr, _MM_HINT_T2); for(int iter=0; iter<(doPrefetch?4:1); iter++) { // if prefetching, iterate on cachelines __m128i data = _mm_load_si128((__m128i*)(_src1 + ptr*srcScale)); __m128i result1 = _mm_gf2p8affine_epi64_epi8(data, matNormA, 0); __m128i result2 = _mm_gf2p8affine_epi64_epi8(data, matSwapA, 0); if(srcCount >= 2) { data = _mm_load_si128((__m128i*)(_src2 + ptr*srcScale)); result1 = _mm_xor_si128(result1, _mm_gf2p8affine_epi64_epi8(data, matNormB, 0)); result2 = _mm_xor_si128(result2, _mm_gf2p8affine_epi64_epi8(data, matSwapB, 0)); } if(srcCount >= 3) { data = _mm_load_si128((__m128i*)(_src3 + ptr*srcScale)); result1 = _mm_xor_si128(result1, _mm_gf2p8affine_epi64_epi8(data, matNormC, 0)); result2 = _mm_xor_si128(result2, _mm_gf2p8affine_epi64_epi8(data, matSwapC, 0)); } if(srcCount >= 4) { data = _mm_load_si128((__m128i*)(_src4 + ptr*srcScale)); result1 = _mm_xor_si128(result1, _mm_gf2p8affine_epi64_epi8(data, matNormD, 0)); result2 = _mm_xor_si128(result2, _mm_gf2p8affine_epi64_epi8(data, matSwapD, 0)); } if(srcCount >= 5) { data = _mm_load_si128((__m128i*)(_src5 + ptr*srcScale)); result1 = _mm_xor_si128(result1, _mm_gf2p8affine_epi64_epi8(data, matNormE, 0)); result2 = _mm_xor_si128(result2, _mm_gf2p8affine_epi64_epi8(data, matSwapE, 0)); } if(srcCount >= 6) { data = _mm_load_si128((__m128i*)(_src6 + ptr*srcScale)); result1 = _mm_xor_si128(result1, _mm_gf2p8affine_epi64_epi8(data, matNormF, 0)); result2 = _mm_xor_si128(result2, _mm_gf2p8affine_epi64_epi8(data, matSwapF, 0)); } result1 = _mm_xor_si128(result1, _mm_load_si128((__m128i*)(_dst + ptr))); result1 = _mm_xor_si128(result1, _mm_shuffle_epi32(result2, _MM_SHUFFLE(1,0,3,2))); _mm_store_si128((__m128i*)(_dst + ptr), result1); ptr += sizeof(__m128i); } } } #endif /*defined(__GFNI__) && defined(__SSSE3__) && !defined(PARPAR_SLIM_GF16)*/ #ifdef PARPAR_INVERT_SUPPORT void gf16_affine2x_mul_gfni(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__GFNI__) && defined(__SSSE3__) && !defined(PARPAR_SLIM_GF16) __m128i matNorm, matSwap; gf16_affine_load_matrix(scratch, coefficient, &matNorm, &matSwap); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m128i)) { __m128i data = _mm_load_si128((__m128i*)(_src + ptr)); __m128i result1 = _mm_gf2p8affine_epi64_epi8(data, matNorm, 0); __m128i result2 = _mm_gf2p8affine_epi64_epi8(data, matSwap, 0); result1 = _mm_xor_si128(result1, _mm_shuffle_epi32(result2, _MM_SHUFFLE(1,0,3,2))); _mm_store_si128((__m128i*)(_dst + ptr), result1); } #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } #endif void gf16_affine2x_muladd_gfni(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__GFNI__) && defined(__SSSE3__) && !defined(PARPAR_SLIM_GF16) gf16_muladd_single(scratch, &gf16_affine2x_muladd_x_gfni, dst, src, len, coefficient); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } #if defined(__GFNI__) && defined(__SSSE3__) && !defined(PARPAR_SLIM_GF16) # ifdef PLATFORM_AMD64 GF16_MULADD_MULTI_FUNCS(gf16_affine2x, _gfni, gf16_affine2x_muladd_x_gfni, 6, sizeof(__m128i), 0, (void)0) # else // if only 8 registers available, only allow 2 parallel regions GF16_MULADD_MULTI_FUNCS(gf16_affine2x, _gfni, gf16_affine2x_muladd_x_gfni, 2, sizeof(__m128i), 0, (void)0) # endif #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_affine2x, _gfni) #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_bitdep_init_avx2.h000066400000000000000000000054471514221355600233410ustar00rootroot00000000000000 #include "../src/hedley.h" #include "../src/platform.h" #ifdef __AVX2__ static inline __m256i gf16_bitdep256_swap(__m256i v, int genAffine) { if(genAffine) { // swap for affine2x return _mm256_permute4x64_epi64(v, _MM_SHUFFLE(1,2,0,3)); } else { // interleave so that word pairs are split __m256i swapped = _mm256_shuffle_epi8(v, _mm256_set_epi32( // first half -> slli_epi16(x, 8) 0x0e800c80, 0x0a800880, 0x06800480, 0x02800080, // second half -> srli_epi16(x, 8) 0x800f800d, 0x800b8009, 0x80078005, 0x80038001 )); swapped = _mm256_permute2x128_si256(swapped, swapped, 0x01); // interleave return _mm256_blendv_epi8(v, swapped, _mm256_set_epi32( 0x00ff00ff, 0x00ff00ff, 0x00ff00ff, 0x00ff00ff, 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00 )); } } #endif #ifdef __AVX2__ static void gf16_bitdep_init256(void* dst, int polynomial, int genAffine) { // expand polynomial into vector __m128i shuf = _mm_cmpeq_epi8( _mm_setzero_si128(), _mm_and_si128( _mm_shuffle_epi8( _mm_cvtsi32_si128(polynomial & 0xffff), _mm_set_epi32(0, 0, 0x01010101, 0x01010101) ), _mm_set_epi32(0x01020408, 0x10204080, 0x01020408, 0x10204080) ) ); /* AVX512 version: __m128i shuf = _mm_shuffle_epi8(_mm_movm_epi8(~(polynomial & 0xFFFF)), _mm_set_epi8( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 )); */ // pre-generate lookup tables for getting bitdeps __m256i addvals = genAffine ? _mm256_set_epi8( 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 ) : _mm256_set_epi8( 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 ); __m256i shuf2 = _mm256_inserti128_si256(_mm256_castsi128_si256(shuf), shuf, 1); for(int val=0; val<16; val++) { __m256i valtest = _mm256_set1_epi16(val << 12); __m256i addmask = _mm256_srai_epi16(valtest, 15); __m256i depmask = _mm256_and_si256(addvals, addmask); for(int i=0; i<3; i++) { // rotate __m256i last = _mm256_shuffle_epi8(depmask, shuf2); depmask = _mm256_srli_si256(depmask, 1); // XOR poly depmask = _mm256_xor_si256(depmask, last); valtest = _mm256_add_epi16(valtest, valtest); addmask = _mm256_srai_epi16(valtest, 15); addmask = _mm256_and_si256(addvals, addmask); depmask = _mm256_xor_si256(depmask, addmask); } _mm256_store_si256((__m256i*)dst + (val*4 + 0), gf16_bitdep256_swap(depmask, genAffine)); for(int j=1; j<4; j++) { for(int i=0; i<4; i++) { __m256i last = _mm256_shuffle_epi8(depmask, shuf2); depmask = _mm256_srli_si256(depmask, 1); depmask = _mm256_xor_si256(depmask, last); } _mm256_store_si256((__m256i*)dst + (val*4 + j), gf16_bitdep256_swap(depmask, genAffine)); } } } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_bitdep_init_sse2.h000066400000000000000000000130051514221355600233220ustar00rootroot00000000000000 #include "../src/hedley.h" #include "../src/platform.h" #define GF16_BITDEP_INIT128_GEN_XOR 0 #define GF16_BITDEP_INIT128_GEN_XORJIT 1 #define GF16_BITDEP_INIT128_GEN_AFFINE 2 #ifdef __SSE2__ static inline void gf16_bitdep128_store(__m128i* dst, __m128i depmask1, __m128i depmask2, int genMode) { if(genMode == GF16_BITDEP_INIT128_GEN_AFFINE) { # ifdef __SSSE3__ __m128 tmp1 = _mm_castsi128_ps(_mm_shuffle_epi8(depmask1, _mm_set_epi8( 14,12,10,8,6,4,2,0, 15,13,11,9,7,5,3,1 ))); __m128 tmp2 = _mm_castsi128_ps(_mm_shuffle_epi8(depmask2, _mm_set_epi8( 14,12,10,8,6,4,2,0, 15,13,11,9,7,5,3,1 ))); // swap around for affine2x depmask1 = _mm_castps_si128(_mm_shuffle_ps(tmp2, tmp1, _MM_SHUFFLE(3,2,1,0))); depmask2 = _mm_castps_si128(_mm_shuffle_ps(tmp1, tmp2, _MM_SHUFFLE(3,2,1,0))); # endif } else if(genMode == GF16_BITDEP_INIT128_GEN_XORJIT) { /* emulate PACKUSDW (SSE4.1 only) with SSE2 shuffles */ /* 01234567 -> 02461357 */ __m128i tmp1 = _mm_shuffle_epi32( _mm_shufflelo_epi16( _mm_shufflehi_epi16(depmask1, 0xD8), /* 0xD8 == 0b11011000 */ 0xD8 ), 0xD8 ); __m128i tmp2 = _mm_shuffle_epi32( _mm_shufflelo_epi16( _mm_shufflehi_epi16(depmask2, 0xD8), 0xD8 ), 0xD8 ); /* [02461357, 8ACE9BDF] -> [02468ACE, 13579BDF]*/ depmask1 = _mm_unpacklo_epi64(tmp1, tmp2); depmask2 = _mm_unpackhi_epi64(tmp1, tmp2); __m128i lmask = _mm_set1_epi8(0xF); /* interleave bits for faster lookups */ __m128i tmp3l = _mm_and_si128(depmask1, lmask); __m128i tmp3h = _mm_and_si128(_mm_srli_epi16(depmask1, 4), lmask); __m128i tmp4l = _mm_and_si128(depmask2, lmask); __m128i tmp4h = _mm_and_si128(_mm_srli_epi16(depmask2, 4), lmask); /* expand bits: idea from https://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN */ #define EXPAND_ROUND(src, shift, mask) _mm_and_si128( \ _mm_or_si128(src, shift==1 ? _mm_add_epi16(src, src) : _mm_slli_epi16(src, shift)), \ _mm_set1_epi16(mask) \ ) /* 8-bit -> 16-bit convert, with 4-bit interleave */ tmp1 = _mm_unpacklo_epi8(tmp3l, tmp3h); tmp2 = _mm_unpacklo_epi8(tmp4l, tmp4h); tmp1 = EXPAND_ROUND(tmp1, 2, 0x3333); tmp2 = EXPAND_ROUND(tmp2, 2, 0x3333); tmp1 = EXPAND_ROUND(tmp1, 1, 0x5555); tmp2 = EXPAND_ROUND(tmp2, 1, 0x5555); depmask1 = _mm_or_si128(tmp1, _mm_add_epi16(tmp2, tmp2)); tmp1 = _mm_unpackhi_epi8(tmp3l, tmp3h); tmp2 = _mm_unpackhi_epi8(tmp4l, tmp4h); tmp1 = EXPAND_ROUND(tmp1, 2, 0x3333); tmp2 = EXPAND_ROUND(tmp2, 2, 0x3333); tmp1 = EXPAND_ROUND(tmp1, 1, 0x5555); tmp2 = EXPAND_ROUND(tmp2, 1, 0x5555); depmask2 = _mm_or_si128(tmp1, _mm_add_epi16(tmp2, tmp2)); #undef EXPAND_ROUND } _mm_store_si128((__m128i*)dst + 0, depmask1); _mm_store_si128((__m128i*)dst + 1, depmask2); } #endif #ifdef __SSE2__ static void gf16_bitdep_init128(void* dst, int polynomial, int genMode) { __m128i polymask1, polymask2; /* duplicate each bit in the polynomial 16 times */ polymask2 = _mm_set1_epi16(polynomial & 0xFFFF); /* chop off top bit, although not really necessary */ polymask1 = _mm_and_si128(polymask2, _mm_set_epi16(0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000)); polymask2 = _mm_and_si128(polymask2, _mm_set_epi16(0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80)); polymask1 = _mm_cmpeq_epi16(_mm_setzero_si128(), polymask1); polymask2 = _mm_cmpeq_epi16(_mm_setzero_si128(), polymask2); polymask1 = _mm_xor_si128(polymask1, _mm_set1_epi8(0xff)); polymask2 = _mm_xor_si128(polymask2, _mm_set1_epi8(0xff)); // pre-generate lookup tables for getting bitdeps __m128i addvals1 = genMode==GF16_BITDEP_INIT128_GEN_AFFINE ? _mm_set_epi16(0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80) : _mm_set_epi16(0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01); __m128i addvals2 = genMode==GF16_BITDEP_INIT128_GEN_AFFINE ? _mm_set_epi16(0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000) : _mm_set_epi16(0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100); for(int val=0; val<16; val++) { __m128i valtest = _mm_set1_epi16(val << 12); __m128i addmask = _mm_srai_epi16(valtest, 15); /* _mm_cmpgt_epi16(_mm_setzero_si128(), valtest) is an alternative, but GCC/Clang prefer the former, so trust the compiler */ __m128i depmask1 = _mm_and_si128(addvals1, addmask); __m128i depmask2 = _mm_and_si128(addvals2, addmask); for(int i=0; i<3; i++) { /* rotate */ __m128i last = _mm_shuffle_epi32(_mm_shufflelo_epi16(depmask1, 0), 0); depmask1 = _mm_or_si128( _mm_srli_si128(depmask1, 2), _mm_slli_si128(depmask2, 14) ); depmask2 = _mm_srli_si128(depmask2, 2); /* XOR poly */ depmask1 = _mm_xor_si128(depmask1, _mm_and_si128(polymask1, last)); depmask2 = _mm_xor_si128(depmask2, _mm_and_si128(polymask2, last)); valtest = _mm_add_epi16(valtest, valtest); addmask = _mm_srai_epi16(valtest, 15); depmask1 = _mm_xor_si128(depmask1, _mm_and_si128(addvals1, addmask)); depmask2 = _mm_xor_si128(depmask2, _mm_and_si128(addvals2, addmask)); } gf16_bitdep128_store((__m128i*)dst + (val*4+0)*2, depmask1, depmask2, genMode); for(int j=1; j<4; j++) { for(int i=0; i<4; i++) { __m128i last = _mm_shuffle_epi32(_mm_shufflelo_epi16(depmask1, 0), 0); depmask1 = _mm_or_si128( _mm_srli_si128(depmask1, 2), _mm_slli_si128(depmask2, 14) ); depmask2 = _mm_srli_si128(depmask2, 2); depmask1 = _mm_xor_si128(depmask1, _mm_and_si128(polymask1, last)); depmask2 = _mm_xor_si128(depmask2, _mm_and_si128(polymask2, last)); } gf16_bitdep128_store((__m128i*)dst + (val*4+j)*2, depmask1, depmask2, genMode); } } } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_checksum_arm.h000066400000000000000000000075551514221355600225520ustar00rootroot00000000000000#ifndef __GF16_CHECKSUM_H #define __GF16_CHECKSUM_H #include "gf16_neon_common.h" static HEDLEY_ALWAYS_INLINE uint8x16_t gf16_vec_mul2_neon(uint8x16_t v) { int16x8_t _v = vreinterpretq_s16_u8(v); return vreinterpretq_u8_s16(veorq_s16( vaddq_s16(_v, _v), vandq_s16( vdupq_n_s16(GF16_POLYNOMIAL & 0xffff), vshrq_n_s16(_v, 15) ) )); } // we want to avoid vld1/vst1 because compilers tend to interpret those quite literally, when we really want the value to be held in a register #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ // however, it seems that this allows big-endian implementations to change the ordering of bytes static HEDLEY_ALWAYS_INLINE uint8x16_t gf16_checksum_load(const void *HEDLEY_RESTRICT checksum) { return vld1q_u8((const uint8_t *HEDLEY_RESTRICT)checksum); } static HEDLEY_ALWAYS_INLINE void gf16_checksum_store(void *HEDLEY_RESTRICT checksum, uint8x16_t v) { vst1q_u8((uint8_t *HEDLEY_RESTRICT)checksum, v); } #else static HEDLEY_ALWAYS_INLINE uint8x16_t gf16_checksum_load(const void *HEDLEY_RESTRICT checksum) { return *(const uint8x16_t *HEDLEY_RESTRICT)checksum; } static HEDLEY_ALWAYS_INLINE void gf16_checksum_store(void *HEDLEY_RESTRICT checksum, uint8x16_t v) { *(uint8x16_t *HEDLEY_RESTRICT)checksum = v; } #endif static HEDLEY_ALWAYS_INLINE void gf16_checksum_block_neon(const void *HEDLEY_RESTRICT src, void *HEDLEY_RESTRICT checksum, const size_t blockLen, const int aligned) { UNUSED(aligned); uint8x16_t v = gf16_checksum_load(checksum); v = gf16_vec_mul2_neon(v); uint8_t* _src = (uint8_t*)src; for(unsigned i=0; i= sizeof(uint8x16_t); amount -= sizeof(uint8x16_t)) { v = veorq_u8(v, vld1q_u8(_src)); _src += sizeof(uint8x16_t); } if(amount) { uint8_t tmp[sizeof(uint8x16_t)] = {0}; memcpy(tmp, _src, amount); v = veorq_u8(v, vld1q_u8(tmp)); } gf16_checksum_store(checksum, v); } static HEDLEY_ALWAYS_INLINE void gf16_checksum_exp_neon(void *HEDLEY_RESTRICT checksum, uint16_t exp) { int16x8_t coeff = vdupq_n_s16(exp); uint8x16_t _checksum = gf16_checksum_load(checksum); uint8x16_t res = vandq_u8(vreinterpretq_u8_s16(vshrq_n_s16(coeff, 15)), _checksum); for(int i=0; i<15; i++) { res = gf16_vec_mul2_neon(res); coeff = vaddq_s16(coeff, coeff); res = veorq_u8(res, vandq_u8( vreinterpretq_u8_s16(vshrq_n_s16(coeff, 15)), _checksum )); } gf16_checksum_store(checksum, res); } static HEDLEY_ALWAYS_INLINE void gf16_checksum_prepare_neon(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT checksum, const size_t blockLen, gf16_transform_block_rst prepareBlock) { #define _X(bl) \ ALIGN_TO(16, uint8_t tmp[bl]) = {0}; \ vst1q_u8(tmp, gf16_checksum_load(checksum)); \ prepareBlock(dst, tmp) if(blockLen == 16) { _X(16); } else if(blockLen == 32) { _X(32); } else if(blockLen == 64) { _X(64); } else { assert(blockLen == 0); } #undef _X } static HEDLEY_ALWAYS_INLINE void gf16_ungrp2a_block_neon(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, const size_t blockLen) { const uint16_t* _src = (const uint16_t*)src; uint16_t* _dst = (uint16_t*)dst; for(unsigned i=0; i> 32); n = ((n & 0xffff0000ffffULL) << 16) | ((n >> 16) & 0xffff0000ffffULL); n = ((n & 0xff00ff00ff00ffULL) << 8) | ((n >> 8) & 0xff00ff00ff00ffULL); return n; } static HEDLEY_ALWAYS_INLINE uint32_t SWAP32(uint32_t n) { return ((n&0xff) << 24) | ((n&0xff00) << 8) | ((n>>8) & 0xff00) | (n >> 24); } static HEDLEY_ALWAYS_INLINE uint16_t SWAP16(uint16_t n) { return ((n&0xff) << 8) | (n >> 8); } #else # define SWAP64(n) (n) # define SWAP32(n) (n) # define SWAP16(n) (n) #endif static HEDLEY_ALWAYS_INLINE uintptr_t gf16_multi_mul2(uintptr_t v) { // assume uintptr_t is at least 2 assert(sizeof(uintptr_t) >= 2); if(sizeof(uintptr_t) >= 8) { const uint64_t mask = 0x0001000100010001ULL; v = ((v*2) & ~mask) ^ (((v >> 15) & mask) * (GF16_POLYNOMIAL & 0xffff)); } else if(sizeof(uintptr_t) >= 4) { const uint32_t mask = 0x00010001; v = ((v*2) & ~mask) ^ (((v >> 15) & mask) * (GF16_POLYNOMIAL & 0xffff)); } else { v = (v*2) ^ (-(v >> 15) & GF16_POLYNOMIAL); } return v; } static HEDLEY_ALWAYS_INLINE void gf16_checksum_blocku_generic(const void *HEDLEY_RESTRICT src, size_t amount, void *HEDLEY_RESTRICT checksum) { uint8_t* _src = (uint8_t*)src; if(sizeof(uintptr_t) >= 8) { uint64_t data = 0; size_t remaining = amount & (sizeof(uint64_t)-1); amount ^= remaining; while(amount) { data ^= read64(_src); _src += sizeof(uint64_t); amount -= sizeof(uint64_t); } if(remaining) { uint64_t dataPart = 0; memcpy(&dataPart, _src, remaining); data ^= dataPart; } write64(checksum, (uint64_t)gf16_multi_mul2(read64(checksum)) ^ SWAP64(data)); } else if(sizeof(uintptr_t) >= 4) { uint32_t data = 0; size_t remaining = amount & (sizeof(uint32_t)-1); amount ^= remaining; while(amount) { data ^= read32(_src); _src += sizeof(uint32_t); amount -= sizeof(uint32_t); } if(remaining) { uint32_t dataPart = 0; memcpy(&dataPart, _src, remaining); data ^= dataPart; } write32(checksum, (uint32_t)gf16_multi_mul2(read32(checksum)) ^ SWAP32(data)); } else { uint16_t data = 0; while(amount > 1) { data ^= read16(_src); _src += sizeof(uint16_t); amount -= sizeof(uint16_t); } if(amount) { uint16_t dataPart = *_src; data ^= SWAP16(dataPart); } write16(checksum, (uint16_t)gf16_multi_mul2(read16(checksum)) ^ SWAP16(data)); } } static HEDLEY_ALWAYS_INLINE void gf16_checksum_block_generic(const void *HEDLEY_RESTRICT src, void *HEDLEY_RESTRICT checksum, const size_t blockLen, const int aligned) { UNUSED(aligned); gf16_checksum_blocku_generic(src, blockLen, checksum); } static HEDLEY_ALWAYS_INLINE void gf16_ungrp2a_block_generic(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, const size_t blockLen) { const uint16_t* _src = (const uint16_t*)src; uint16_t* _dst = (uint16_t*)dst; size_t remaining = blockLen; while(remaining) { if(sizeof(uintptr_t) >= 8) { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ write64(_dst, ((uint64_t)read16(_src) << 48) | ((uint64_t)read16(_src + 2) << 32) | ((uint64_t)read16(_src + 4) << 16) | read16(_src + 6)); #else write64(_dst, read16(_src) | ((uint64_t)read16(_src + 2) << 16) | ((uint64_t)read16(_src + 4) << 32) | ((uint64_t)read16(_src + 6) << 48)); #endif remaining -= 8; _src += 8; _dst += 4; } else if(sizeof(uintptr_t) >= 4) { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ write32(_dst, ((uint32_t)read16(_src) << 16) | read16(_src + 2)); #else write32(_dst, read16(_src) | ((uint32_t)read16(_src + 2) << 16)); #endif remaining -= 4; _src += 4; _dst += 2; } else { write16(_dst, read16(_src)); remaining -= 2; _src += 2; _dst += 1; } } } static HEDLEY_ALWAYS_INLINE void gf16_ungrp2b_block_generic(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, const size_t blockLen) { gf16_ungrp2a_block_generic(dst, (const uint16_t*)src + 1, blockLen); } static HEDLEY_ALWAYS_INLINE void gf16_checksum_exp_generic(void *HEDLEY_RESTRICT checksum, uint16_t exp) { uint16_t coeff = exp; // multiply checksum by coeff if(sizeof(uintptr_t) >= 8) { uint64_t _checksum = read64(checksum); uint64_t res = -(uint64_t)(coeff>>15) & _checksum; for(int i=0; i<15; i++) { res = (uint64_t)gf16_multi_mul2(res); coeff <<= 1; res ^= -(uint64_t)(coeff>>15) & _checksum; } write64(checksum, res); } else if(sizeof(uintptr_t) >= 4) { uint32_t _checksum = read32(checksum); uint32_t res = -(uint32_t)(coeff>>15) & _checksum; for(int i=0; i<15; i++) { res = (uint32_t)gf16_multi_mul2(res); coeff <<= 1; res ^= -(uint32_t)(coeff>>15) & _checksum; } write32(checksum, res); } else { uint16_t _checksum = read16(checksum); uint16_t res = -(coeff>>15) & _checksum; for(int i=0; i<15; i++) { res = (uint16_t)gf16_multi_mul2(res); coeff <<= 1; res ^= -(coeff>>15) & _checksum; } write16(checksum, res); } } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_checksum_rvv.h000066400000000000000000000073131514221355600226000ustar00rootroot00000000000000#ifndef __GF16_CHECKSUM_H #define __GF16_CHECKSUM_H #include "gf16_rvv_common.h" #ifdef __RVV_LE static HEDLEY_ALWAYS_INLINE void gf16_checksum_block_rvv(const void *HEDLEY_RESTRICT src, void *HEDLEY_RESTRICT checksum, const size_t blockLen, const int aligned) { size_t vl = RV(vsetvlmax_e8m1)(); const unsigned words = blockLen/vl; vint16m1_t v = *(vint16m1_t*)checksum; v = gf16_vec_mul2_rvv(v); if(aligned) { vl = RV(vsetvlmax_e16m1)(); int16_t* _src = (int16_t*)src; for(unsigned i=0; i= 12000 vuint16m1x2_t w = RV(vlseg2e16_v_u16m1x2)(_src + i, vl); vuint16m1_t w1 = RV(vget_v_u16m1x2_u16m1)(w, 0); #else vuint16m1_t w1, w2; RV(vlseg2e16_v_u16m1)(&w1, &w2, _src + i, vl); #endif RV(vse16_v_u16m1)(_dst + i/2, w1, vl); } } static HEDLEY_ALWAYS_INLINE void gf16_ungrp2b_block_rvv(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, const size_t blockLen) { size_t vl = RV(vsetvlmax_e8m1)(); const uint16_t* _src = (const uint16_t*)src; uint16_t* _dst = (uint16_t*)dst; for(unsigned i=0; i= 12000 vuint16m1x2_t w = RV(vlseg2e16_v_u16m1x2)(_src + i, vl); vuint16m1_t w2 = RV(vget_v_u16m1x2_u16m1)(w, 1); #else vuint16m1_t w1, w2; RV(vlseg2e16_v_u16m1)(&w1, &w2, _src + i, vl); #endif RV(vse16_v_u16m1)(_dst + i/2, w2, vl); } } #endif #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_checksum_sve.h000066400000000000000000000044541514221355600225630ustar00rootroot00000000000000#ifndef __GF16_CHECKSUM_H #define __GF16_CHECKSUM_H #include "gf16_sve_common.h" #ifdef __ARM_FEATURE_SVE static HEDLEY_ALWAYS_INLINE void gf16_checksum_block_sve(const void *HEDLEY_RESTRICT src, void *HEDLEY_RESTRICT checksum, const size_t blockLen, const int aligned) { UNUSED(aligned); const unsigned words = blockLen/svcntb(); svint16_t v = *(svint16_t*)checksum; v = gf16_vec_mul2_sve(v); int16_t* _src = (int16_t*)src; for(unsigned i=0; i sizeof(_mword)*2; amount -= sizeof(_mword)*2) { v = _mm512_ternarylogic_epi32(v, _mm512_loadu_si512(_src), _mm512_loadu_si512(_src+1), 0x96); _src += 2; } if(amount > sizeof(_mword)) { v = _mm512_xor_si512(v, _mm512_loadu_si512(_src++)); amount -= sizeof(_mword); } #else */ for(; amount >= sizeof(_mword); amount -= sizeof(_mword)) { v = _MMI(xor)(v, _MMI(loadu)(_src++)); } //#endif if(amount) v = _MMI(xor)(v, partial_load(_src, amount)); *(_mword*)checksum = v; } static HEDLEY_ALWAYS_INLINE void _FN(gf16_checksum_exp)(void *HEDLEY_RESTRICT checksum, uint16_t exp) { _mword coeff = _MM(set1_epi16)(exp); _mword _checksum = *(_mword*)checksum; _mword res = _MMI(and)( _MM(srai_epi16)(coeff, 15), _checksum ); for(int i=0; i<15; i++) { res = _FN(gf16_vec_mul2)(res); coeff = _MM(add_epi16)(coeff, coeff); #if MWORD_SIZE==64 res = _mm512_ternarylogic_epi32( res, _mm512_srai_epi16(coeff, 15), _checksum, 0x78 // (a^(b&c)) ); #else res = _MMI(xor)(res, _MMI(and)( _MM(srai_epi16)(coeff, 15), _checksum )); #endif } *(_mword*)checksum = res; } static HEDLEY_ALWAYS_INLINE void _FN(gf16_checksum_prepare)(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT checksum, const size_t blockLen, gf16_transform_block_rst prepareBlock) { // because some compilers don't like `tmp[blockLen]` despite blockLen being constant, just implement every possibility #define _X(bl) \ ALIGN_TO(MWORD_SIZE, uint8_t tmp[bl]) = {0}; \ _MMI(store)((_mword*)tmp, *(_mword*)checksum); \ prepareBlock(dst, tmp) if(blockLen == 16) { _X(16); } else if(blockLen == 32) { _X(32); } else if(blockLen == 64) { _X(64); } else if(blockLen == 128) { _X(128); } else if(blockLen == 256) { _X(256); } else if(blockLen == 512) { _X(512); } else if(blockLen == 1024) { _X(1024); } else { assert(blockLen == 0); } #undef _X } static HEDLEY_ALWAYS_INLINE void _FN(gf16_ungrp2a_block)(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, const size_t blockLen) { const unsigned words = (unsigned)blockLen / sizeof(_mword); const _mword* _src = (const _mword*)src; _mword* _dst = (_mword*)dst; for(unsigned i=0; i #include "gf16_global.h" #ifdef _AVAILABLE #include "gfmat_coeff.h" #include #ifndef CKSUM_SIZE # define CKSUM_SIZE sizeof(cksum_t) #endif static HEDLEY_ALWAYS_INLINE void _FN(gf16_cksum_docopy)(uint8_t *HEDLEY_RESTRICT dst, const uint8_t *HEDLEY_RESTRICT src, size_t srcLen, cksum_t* cksum) { if(srcLen >= CKSUM_SIZE) { for(size_t pos = 0; pos < (srcLen-CKSUM_SIZE+1); pos += CKSUM_SIZE) { const cksum_t* p = (const cksum_t*)(src + pos); cksum_t data; LOAD_DATA(data, p); _FN(gf16_checksum_block)(p, cksum, CKSUM_SIZE, 0); STORE_DATA(dst + pos, data); } } size_t remaining = srcLen % CKSUM_SIZE; size_t lenAligned = srcLen - remaining; if(remaining) { _FN(gf16_checksum_blocku)(src + lenAligned, remaining, cksum); memcpy(dst+lenAligned, src+lenAligned, remaining); } } void _FN(gf16_cksum_copy)(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen) { assert(srcLen <= sliceLen); cksum_t cksum = CKSUM_ZERO; uint8_t* _dst = (uint8_t*)dst; _FN(gf16_cksum_docopy)(_dst, (const uint8_t*)src, srcLen, &cksum); if(srcLen < sliceLen) { // zero rest of block size_t amount = sliceLen - srcLen; size_t cksumDone = ((srcLen + CKSUM_SIZE-1) / CKSUM_SIZE) * CKSUM_SIZE; memset(_dst + srcLen, 0, amount); if(cksumDone < sliceLen) _FN(gf16_checksum_exp)(&cksum, gf16_exp(((sliceLen - cksumDone + CKSUM_SIZE-1) / CKSUM_SIZE) % 65535)); } STORE_DATA(_dst + sliceLen, cksum); } int _FN(gf16_cksum_copy_check)(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len) { const uint8_t* _src = (const uint8_t*)src; uint8_t* _dst = (uint8_t*)dst; cksum_t cksum; LOAD_DATA(cksum, _src + len); // rewind checksum _FN(gf16_checksum_exp)(&cksum, gf16_exp(65535 - (((len+CKSUM_SIZE-1) / CKSUM_SIZE) % 65535))); _FN(gf16_cksum_docopy)(_dst, _src, len, &cksum); return CKSUM_IS_ZERO(cksum); } int _FN(gf16_grp2_finish)(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, unsigned grp) { assert(len % 2 == 0); const uint8_t* _src = (const uint8_t*)src; uint8_t* _dst = (uint8_t*)dst; cksum_t cksum; { uint8_t block[CKSUM_SIZE]; if(grp & 1) _FN(gf16_ungrp2b_block)(block, _src + len*2, CKSUM_SIZE); else _FN(gf16_ungrp2a_block)(block, _src + len*2, CKSUM_SIZE); LOAD_DATA(cksum, block); } // rewind checksum _FN(gf16_checksum_exp)(&cksum, gf16_exp(65535 - (((len+CKSUM_SIZE-1) / CKSUM_SIZE) % 65535))); if(len >= CKSUM_SIZE) { if(grp & 1) { for(size_t pos = 0; pos < (len-CKSUM_SIZE+1); pos += CKSUM_SIZE) { _FN(gf16_ungrp2b_block)(_dst + pos, _src + pos*2, CKSUM_SIZE); _FN(gf16_checksum_block)(_dst + pos, &cksum, CKSUM_SIZE, 0); } } else { for(size_t pos = 0; pos < (len-CKSUM_SIZE+1); pos += CKSUM_SIZE) { _FN(gf16_ungrp2a_block)(_dst + pos, _src + pos*2, CKSUM_SIZE); _FN(gf16_checksum_block)(_dst + pos, &cksum, CKSUM_SIZE, 0); } } } size_t remaining = len % CKSUM_SIZE; size_t lenAligned = len - remaining; if(remaining) { const uint16_t* src16 = (const uint16_t*)(_src + lenAligned*2); src16 += grp & 1; for(unsigned i=0; i> 2` in one op th0_hi3 = vqtbl1q_u8( vmakeq_u8(0,1,2,3,5,4,7,6, 0,0,0,0,0,0,0,0), th0_hi3 ); #else uint8x16_t th0_hi1 = vshrq_n_u8(th0_hi3, 2); // or is `vshrq_n_u8(th0, 7)` better? #endif // mul by 0x1a => we only care about upper byte // note that as hibytes.val[1] can only contain 7 bits (due to 16b*16b->31b), multiplying by 0x18 also works // the 0x10 part is handled above, so just need to shift in for the 0x8 th0 = veorq_u8(th0, vshrq_n_u8(hibytes.val[1], 5)); // multiply by polynomial: 0x100b poly8x16_t redL = vdupq_n_p8(0x0b); hibytes.val[1] = vsliq_n_u8(th0_hi3, th0, 4); th1 = vreinterpretq_u8_p8(vmulq_p8(vreinterpretq_p8_u8(th1), redL)); hibytes.val[0] = vreinterpretq_u8_p8(vmulq_p8(vreinterpretq_p8_u8(th0), redL)); *low1 = vreinterpretq_p16_u8(lobytes.val[0]); *low2 = vreinterpretq_p16_u8(hibytes.val[0]); #if defined(__aarch64__) && !defined(__ARM_FEATURE_SHA3) *high1 = vreinterpretq_p16_u8(veorq_u8(hibytes.val[1], th1)); #else *high1 = vreinterpretq_p16_u8(eor3q_u8(hibytes.val[1], th0_hi1, th1)); #endif *high2 = vreinterpretq_p16_u8(lobytes.val[1]); } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_clmul_neon_base.h000066400000000000000000000160021514221355600232210ustar00rootroot00000000000000 #include "gf16_clmul_neon.h" #include "gf16_muladd_multi.h" // TODO: for any multiplicand byte that's 0 (e.g. for coeff < 256), can shortcut a bunch of stuff, but may not be worth the effort #if defined(_AVAILABLE) static HEDLEY_ALWAYS_INLINE void gf16_clmul_neon_round1(const void* src, poly16x8_t* low1, poly16x8_t* low2, poly16x8_t* mid1, poly16x8_t* mid2, poly16x8_t* high1, poly16x8_t* high2, const coeff_t* coeff) { poly8x16x2_t data = vld2q_p8((const poly8_t*)src); *low1 = pmull_low(data.val[0], coeff[0]); *low2 = pmull_high(data.val[0], coeff[0]); poly8x16_t mid = veorq_p8(data.val[0], data.val[1]); *mid1 = pmull_low(mid, coeff[2]); *mid2 = pmull_high(mid, coeff[2]); *high1 = pmull_low(data.val[1], coeff[1]); *high2 = pmull_high(data.val[1], coeff[1]); // TODO: try idea of forcing an EOR via asm volatile /* Alternative approach for AArch64, which only needs one register per region at the expense of 2 additional instructions; unfortunately compilers won't heed our aim // the `midCoeff` approach can also work with AArch32 coeff_t swapCoeff = vextq_p8(coeff[0], coeff[0], 8); coeff_t midCoeff = veorq_p8(coeff[0], swapCoeff); *low1 = pmull_low(data.val[0], coeff[0]); *low2 = pmull_high(data.val[0], swapCoeff); poly8x16_t mid = veorq_p8(data.val[0], data.val[1]); *mid1 = pmull_low(mid, midCoeff); *mid2 = pmull_high(mid, midCoeff); *high1 = pmull_low(data.val[1], swapCoeff); *high2 = pmull_high(data.val[1], coeff[0]); */ } static HEDLEY_ALWAYS_INLINE void gf16_clmul_neon_round(const void* src, poly16x8_t* low1, poly16x8_t* low2, poly16x8_t* mid1, poly16x8_t* mid2, poly16x8_t* high1, poly16x8_t* high2, const coeff_t* coeff) { poly8x16x2_t data = vld2q_p8((const poly8_t*)src); *low1 = pmacl_low(*low1, data.val[0], coeff[0]); *low2 = pmacl_high(*low2, data.val[0], coeff[0]); poly8x16_t mid = veorq_p8(data.val[0], data.val[1]); *mid1 = pmacl_low(*mid1, mid, coeff[2]); *mid2 = pmacl_high(*mid2, mid, coeff[2]); *high1 = pmacl_low(*high1, data.val[1], coeff[1]); *high2 = pmacl_high(*high2, data.val[1], coeff[1]); } #ifdef __aarch64__ # define CLMUL_NUM_REGIONS 8 #else # define CLMUL_NUM_REGIONS 3 #endif #define CLMUL_COEFF_PER_REGION 3 static HEDLEY_ALWAYS_INLINE void _FN(gf16_clmul_muladd_x)( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(CLMUL_NUM_REGIONS); UNUSED(scratch); coeff_t coeff[CLMUL_COEFF_PER_REGION*CLMUL_NUM_REGIONS]; for(int src=0; src> 8; coeff[src*CLMUL_COEFF_PER_REGION +0] = coeff_fn(vdup, n_p8)(lo); coeff[src*CLMUL_COEFF_PER_REGION +1] = coeff_fn(vdup, n_p8)(hi); coeff[src*CLMUL_COEFF_PER_REGION +2] = coeff_fn(veor, p8)(coeff[src*CLMUL_COEFF_PER_REGION +0], coeff[src*CLMUL_COEFF_PER_REGION +1]); // if we want to have one register per region (AArch64), at the expense of 2 extra instructions per region //coeff[src] = vcombine_p8(vdup_n_p8(lo), vdup_n_p8(hi)); } #ifndef GF16_CLMUL_DO_PROCESS #define GF16_CLMUL_PROCESS_VARS poly16x8_t low1, low2, mid1, mid2, high1, high2 #define GF16_CLMUL_DO_PROCESS \ gf16_clmul_neon_round1(_src1+ptr*srcScale, &low1, &low2, &mid1, &mid2, &high1, &high2, coeff + 0); \ if(srcCount > 1) \ gf16_clmul_neon_round(_src2+ptr*srcScale, &low1, &low2, &mid1, &mid2, &high1, &high2, coeff + CLMUL_COEFF_PER_REGION*1); \ if(srcCount > 2) \ gf16_clmul_neon_round(_src3+ptr*srcScale, &low1, &low2, &mid1, &mid2, &high1, &high2, coeff + CLMUL_COEFF_PER_REGION*2); \ if(srcCount > 3) \ gf16_clmul_neon_round(_src4+ptr*srcScale, &low1, &low2, &mid1, &mid2, &high1, &high2, coeff + CLMUL_COEFF_PER_REGION*3); \ if(srcCount > 4) \ gf16_clmul_neon_round(_src5+ptr*srcScale, &low1, &low2, &mid1, &mid2, &high1, &high2, coeff + CLMUL_COEFF_PER_REGION*4); \ if(srcCount > 5) \ gf16_clmul_neon_round(_src6+ptr*srcScale, &low1, &low2, &mid1, &mid2, &high1, &high2, coeff + CLMUL_COEFF_PER_REGION*5); \ if(srcCount > 6) \ gf16_clmul_neon_round(_src7+ptr*srcScale, &low1, &low2, &mid1, &mid2, &high1, &high2, coeff + CLMUL_COEFF_PER_REGION*6); \ if(srcCount > 7) \ gf16_clmul_neon_round(_src8+ptr*srcScale, &low1, &low2, &mid1, &mid2, &high1, &high2, coeff + CLMUL_COEFF_PER_REGION*7); \ \ gf16_clmul_neon_reduction(&low1, &low2, mid1, mid2, &high1, &high2); \ \ uint8x16x2_t vb = vld2q_u8(_dst+ptr); \ vb.val[0] = veorq_u8(vreinterpretq_u8_p16(veorq_p16(low1, low2)), vb.val[0]); \ vb.val[1] = veorq_u8(vreinterpretq_u8_p16(veorq_p16(high1, high2)), vb.val[1]); \ vst2q_u8(_dst+ptr, vb) #endif GF16_CLMUL_PROCESS_VARS; if(doPrefetch) { intptr_t ptr = -(intptr_t)len; if(doPrefetch == 1) PREFETCH_MEM(_pf+ptr, 1); if(doPrefetch == 2) PREFETCH_MEM(_pf+ptr, 0); while(ptr & (CACHELINE_SIZE-1)) { GF16_CLMUL_DO_PROCESS; ptr += sizeof(uint8x16_t)*2; } while(ptr) { if(doPrefetch == 1) PREFETCH_MEM(_pf+ptr, 1); if(doPrefetch == 2) PREFETCH_MEM(_pf+ptr, 0); for(size_t iter=0; iter<(CACHELINE_SIZE/(sizeof(uint8x16_t)*2)); iter++) { GF16_CLMUL_DO_PROCESS; ptr += sizeof(uint8x16_t)*2; } } } else { for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(uint8x16_t)*2) { GF16_CLMUL_DO_PROCESS; } } #undef GF16_CLMUL_PROCESS_VARS #undef GF16_CLMUL_DO_PROCESS } #endif /*defined(_AVAILABLE)*/ #ifdef PARPAR_INVERT_SUPPORT void _FN(gf16_clmul_mul)(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); UNUSED(scratch); #if defined(_AVAILABLE) coeff_t coeff[3]; coeff[0] = coeff_fn(vdup, n_p8)(val & 0xff); coeff[1] = coeff_fn(vdup, n_p8)(val >> 8); coeff[2] = coeff_fn(veor, p8)(coeff[0], coeff[1]); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; poly16x8_t low1, low2, mid1, mid2, high1, high2; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(uint8x16_t)*2) { gf16_clmul_neon_round1(_src+ptr, &low1, &low2, &mid1, &mid2, &high1, &high2, coeff); gf16_clmul_neon_reduction(&low1, &low2, mid1, mid2, &high1, &high2); uint8x16x2_t out; out.val[0] = vreinterpretq_u8_p16(veorq_p16(low1, low2)); out.val[1] = vreinterpretq_u8_p16(veorq_p16(high1, high2)); vst2q_u8(_dst+ptr, out); } #else UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #endif void _FN(gf16_clmul_muladd)(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(_AVAILABLE) gf16_muladd_single(scratch, &_FN(gf16_clmul_muladd_x), dst, src, len, val); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #if defined(_AVAILABLE) GF16_MULADD_MULTI_FUNCS(gf16_clmul, _FNSUFFIX, _FN(gf16_clmul_muladd_x), CLMUL_NUM_REGIONS, sizeof(uint8x16_t)*2, 0, (void)0) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_clmul, _FNSUFFIX) #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_clmul_rvv.c000066400000000000000000000135121514221355600221030ustar00rootroot00000000000000#include "gf16_clmul_rvv.h" #ifdef RISCV_ZVBC_INTRIN int gf16_available_rvv_zvbc = 1; #else int gf16_available_rvv_zvbc = 0; #endif #include "gf16_muladd_multi.h" #ifdef RISCV_ZVBC_INTRIN static HEDLEY_ALWAYS_INLINE void gf16_clmul_rvv_round0(size_t vl, const void* src, vuint64m1_t* ra, vuint64m1_t* rb, uint16_t coeff) { vuint32m1_t s = RV(vle32_v_u32m1)((const uint32_t*)src, vl); // TODO: consider zero-extending loads? vuint64m1_t tmp = RV(vreinterpret_v_u32m1_u64m1)(RV(vand_vx_u32m1)(s, 0xffff, vl)); *ra = RV(vclmul_vx_u64m1)(tmp, coeff, vl); tmp = RV(vreinterpret_v_u32m1_u64m1)(RV(vsrl_vx_u32m1)(s, 16, vl)); *rb = RV(vclmul_vx_u64m1)(tmp, coeff, vl); } static HEDLEY_ALWAYS_INLINE void gf16_clmul_rvv_round(size_t vl, const void* src, vuint64m1_t* ra, vuint64m1_t* rb, uint16_t coeff) { vuint64m1_t ta, tb; gf16_clmul_rvv_round0(vl, src, &ta, &tb, coeff); *ra = RV(vxor_vv_u64m1)(*ra, ta, vl); *rb = RV(vxor_vv_u64m1)(*rb, tb, vl); } static HEDLEY_ALWAYS_INLINE void gf16_clmul_muladd_x_rvv( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(12); UNUSED(scratch); size_t vl = RV(vsetvlmax_e8m1)(); for(intptr_t ptr = -(intptr_t)len; ptr; ptr += vl) { // TODO: does RISC-V have prefetch instructions? UNUSED(doPrefetch); UNUSED(_pf); vuint64m1_t ra, rb; gf16_clmul_rvv_round0(vl, _src1+ptr*srcScale, &ra, &rb, coefficients[0]); if(srcCount > 1) gf16_clmul_rvv_round(vl, _src2+ptr*srcScale, &ra, &rb, coefficients[1]); if(srcCount > 2) gf16_clmul_rvv_round(vl, _src3+ptr*srcScale, &ra, &rb, coefficients[2]); if(srcCount > 3) gf16_clmul_rvv_round(vl, _src4+ptr*srcScale, &ra, &rb, coefficients[3]); if(srcCount > 4) gf16_clmul_rvv_round(vl, _src5+ptr*srcScale, &ra, &rb, coefficients[4]); if(srcCount > 5) gf16_clmul_rvv_round(vl, _src6+ptr*srcScale, &ra, &rb, coefficients[5]); if(srcCount > 6) gf16_clmul_rvv_round(vl, _src7+ptr*srcScale, &ra, &rb, coefficients[6]); if(srcCount > 7) gf16_clmul_rvv_round(vl, _src8+ptr*srcScale, &ra, &rb, coefficients[7]); if(srcCount > 8) gf16_clmul_rvv_round(vl, _src9+ptr*srcScale, &ra, &rb, coefficients[8]); if(srcCount > 9) gf16_clmul_rvv_round(vl, _src10+ptr*srcScale, &ra, &rb, coefficients[9]); if(srcCount > 10) gf16_clmul_rvv_round(vl, _src11+ptr*srcScale, &ra, &rb, coefficients[10]); if(srcCount > 11) gf16_clmul_rvv_round(vl, _src12+ptr*srcScale, &ra, &rb, coefficients[11]); // reduce & add to dest vuint16m1_t r = RV(vxor_vv_u16m1)( gf16_clmul_rvv_reduction(ra, rb, vl), RV(vle16_v_u16m1)((const uint16_t*)(_dst+ptr), vl), vl ); RV(vse16_v_u16m1)((uint16_t*)(_dst+ptr), r, vl); } } #endif /*defined(RISCV_ZVBC_INTRIN)*/ #ifdef PARPAR_INVERT_SUPPORT void gf16_clmul_mul_rvv(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); UNUSED(scratch); #ifdef RISCV_ZVBC_INTRIN const uint8_t* _src = (const uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; size_t vl = RV(vsetvlmax_e8m1)(); for(intptr_t ptr = -(intptr_t)len; ptr; ptr += vl) { vuint64m1_t ra, rb; gf16_clmul_rvv_round0(vl, _src+ptr, &ra, &rb, val); vuint16m1_t r = gf16_clmul_rvv_reduction(ra, rb, vl); RV(vse16_v_u16m1)((uint16_t*)(_dst+ptr), r, vl); } #else UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #endif void gf16_clmul_muladd_rvv(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #ifdef RISCV_ZVBC_INTRIN gf16_muladd_single(scratch, gf16_clmul_muladd_x_rvv, dst, src, len, val); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #ifdef RISCV_ZVBC_INTRIN GF16_MULADD_MULTI_FUNCS(gf16_clmul, _rvv, gf16_clmul_muladd_x_rvv, 12, RV(vsetvlmax_e8m1)(), 0, (void)0) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_clmul, _rvv) #endif #ifdef __RVV_LE static HEDLEY_ALWAYS_INLINE void gf16_prepare_block_rvv(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { size_t vl = RV(vsetvlmax_e8m1)(); RV(vse8_v_u8m1)((uint8_t*)dst, RV(vle8_v_u8m1)((const uint8_t*)src, vl), vl); } // final block static HEDLEY_ALWAYS_INLINE void gf16_prepare_blocku_rvv(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t remaining) { size_t vlmax = RV(vsetvlmax_e8m1)(); vuint8m1_t v = RV(vmv_v_x_u8m1)(0, vlmax); size_t vl = RV(vsetvl_e8m1)(remaining); #ifdef __riscv_v_intrinsic v = RV(vle8_v_u8m1_tu)(v, (const uint8_t*)src, vl); RV(vse8_v_u8m1)((uint8_t*)dst, v, vlmax); #else // tail-undisturbed not supported, so zero explicitly as a workaround RV(vse8_v_u8m1)((uint8_t*)dst, v, vlmax); RV(vse8_v_u8m1)((uint8_t*)dst, RV(vle8_v_u8m1)((const uint8_t*)src, vl), vl); #endif } static HEDLEY_ALWAYS_INLINE void gf16_finish_blocku_rvv(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t remaining) { size_t vl = RV(vsetvl_e8m1)(remaining); RV(vse8_v_u8m1)((uint8_t*)dst, RV(vle8_v_u8m1)((const uint8_t*)src, vl), vl); } #include "gf16_checksum_rvv.h" GF_PREPARE_PACKED_FUNCS(gf16_clmul, _rvv, RV(vsetvlmax_e8m1)(), gf16_prepare_block_rvv, gf16_prepare_blocku_rvv, 12, (void)0, vuint16m1_t checksum = RV(vmv_v_x_u16m1)(0, RV(vsetvlmax_e16m1)()), gf16_checksum_block_rvv, gf16_checksum_blocku_rvv, gf16_checksum_exp_rvv, gf16_checksum_prepare_rvv, 16) GF_FINISH_PACKED_FUNCS(gf16_clmul, _rvv, RV(vsetvlmax_e8m1)(), gf16_prepare_block_rvv, gf16_finish_blocku_rvv, 1, (void)0, gf16_checksum_block_rvv, gf16_checksum_blocku_rvv, gf16_checksum_exp_rvv, NULL, 16) #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_clmul, _rvv) GF_FINISH_PACKED_FUNCS_STUB(gf16_clmul, _rvv) #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_clmul_rvv.h000066400000000000000000000065431514221355600221160ustar00rootroot00000000000000#include "gf16_rvv_common.h" #if defined(__RVV_LE) && defined(RVV_ZVBC_EMULATE) // temporarily used for testing via Zbc, when Zvbc is unavailable # define RISCV_ZVBC_INTRIN 1 static vuint64m1_t RV(vclmul_vx_u64m1)(vuint64m1_t v, uint64_t x, size_t vl) { size_t evl = RV(vsetvl_e64m1)(vl); uint64_t t[evl]; RV(vse64_v_u64m1)(t, v, evl); for(size_t i=0; i=12000 # if (defined(__clang__) && __clang_major__>=20) || HEDLEY_GCC_VERSION_CHECK(14, 1, 0) # define RISCV_ZVBC_INTRIN 1 // for testing on compilers without Zvbc intrinsics # elif 0 //defined(RISCV_ZVBC_EMULATE) # define RISCV_ZVBC_INTRIN 1 HEDLEY_NEVER_INLINE static vuint64m1_t RV(vclmul_vx_u64m1)(vuint64m1_t v, uint64_t x, size_t vl) { vuint64m1_t d; __asm__ ("vsetvli zero, %3, e64, m1, ta, ma\n" "vclmul.vx %0,%1,%2\n" : "=vr"(d) : "vr"(v), "r"(x), "r"(vl) : /* No clobbers */); return d; } HEDLEY_NEVER_INLINE static vuint64m1_t RV(vclmul_vv_u64m1)(vuint64m1_t v, vuint64m1_t v2, size_t vl) { vuint64m1_t d; __asm__ ("vsetvli zero, %3, e64, m1, ta, ma\n" "vclmul.vv %0,%1,%2\n" : "=vr"(d) : "vr"(v), "vr"(v2), "r"(vl) : /* No clobbers */); return d; } # endif #endif #ifdef RISCV_ZVBC_INTRIN static HEDLEY_ALWAYS_INLINE vuint16m1_t gf16_clmul_rvv_reduction(vuint64m1_t ra, vuint64m1_t rb, size_t vl) { // Barrett reduction vuint64m1_t qa = RV(vreinterpret_v_u32m1_u64m1)(RV(vsrl_vx_u32m1)(RV(vreinterpret_v_u64m1_u32m1)(ra), 16, vl)); vuint64m1_t qb = RV(vreinterpret_v_u32m1_u64m1)(RV(vsrl_vx_u32m1)(RV(vreinterpret_v_u64m1_u32m1)(rb), 16, vl)); // first reduction coefficient is 0x1111a qa = RV(vclmul_vx_u64m1)(qa, 0x1111a, vl); qb = RV(vclmul_vx_u64m1)(qb, 0x1111a, vl); qa = RV(vreinterpret_v_u32m1_u64m1)(RV(vsrl_vx_u32m1)(RV(vreinterpret_v_u64m1_u32m1)(qa), 16, vl)); qb = RV(vreinterpret_v_u32m1_u64m1)(RV(vsrl_vx_u32m1)(RV(vreinterpret_v_u64m1_u32m1)(qb), 16, vl)); // second coefficient is 0x100b qa = RV(vclmul_vx_u64m1)(qa, 0x100b, vl); qb = RV(vclmul_vx_u64m1)(qb, 0x100b0000, vl); // merge halves vuint16m1_t ra16 = RV(vreinterpret_v_u64m1_u16m1)(ra); vuint16m1_t rb16 = RV(vreinterpret_v_u64m1_u16m1)(RV(vsll_vx_u64m1)(rb, 16, vl)); vuint16m1_t qa16 = RV(vreinterpret_v_u64m1_u16m1)(qa); vuint16m1_t qb16 = RV(vreinterpret_v_u64m1_u16m1)(qb); #if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 12000 vbool16_t alt = RV(vreinterpret_b16)(RV(vmv_v_x_u8m1)(0xaa, vl)); #else vuint8m1_t altTmp = RV(vmv_v_x_u8m1)(0xaa, vl); vbool16_t alt = *(vbool16_t*)(&altTmp); #endif #ifdef __riscv_v_intrinsic vuint16m1_t r = RV(vmerge_vvm_u16m1)(ra16, rb16, alt, vl); vuint16m1_t q = RV(vmerge_vvm_u16m1)(qa16, qb16, alt, vl); #else vuint16m1_t r = RV(vmerge_vvm_u16m1)(alt, ra16, rb16, vl); vuint16m1_t q = RV(vmerge_vvm_u16m1)(alt, qa16, qb16, vl); #endif return RV(vxor_vv_u16m1)(r, q, vl); } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_clmul_sha3.c000066400000000000000000000152551514221355600221320ustar00rootroot00000000000000 // this CLMul variant is optimised for Apple M1 #include "gf16_neon_common.h" #if defined(__ARM_NEON) && defined(__ARM_FEATURE_SHA3) int gf16_available_neon_sha3 = 1; static HEDLEY_ALWAYS_INLINE poly16x8_t veorq_p16(poly16x8_t a, poly16x8_t b) { return vreinterpretq_p16_u16(veorq_u16(vreinterpretq_u16_p16(a), vreinterpretq_u16_p16(b))); } #define _AVAILABLE 1 #define eor3q_u8 veor3q_u8 #define pmacl_low(sum, a, b) veorq_p16(sum, pmull_low(a, b)) #define pmacl_high(sum, a, b) veorq_p16(sum, pmull_high(a, b)) #ifdef __APPLE__ // Apple M1 supports PMULL+EOR fusion, so avoid EOR3 for MacOS builds // unknown if Apple A13 supports PMULL+EOR fusion; we'll largely ignore it if it doesn't # if defined(__GNUC__) || defined(__clang__) # undef pmacl_low # undef pmacl_high // Apple M1 supports fusing PMULL+EOR, so ensure these are paired static HEDLEY_ALWAYS_INLINE poly16x8_t pmacl_low(poly16x8_t sum, poly8x16_t a, poly8x16_t b) { poly16x8_t result; __asm__ ("pmull %0.8h,%1.8b,%2.8b\n" "eor %0.16b,%0.16b,%3.16b\n" : "=&w"(result) : "w"(a), "w"(b), "w"(sum) : /* No clobbers */); return result; } static HEDLEY_ALWAYS_INLINE poly16x8_t pmacl_high(poly16x8_t sum, poly8x16_t a, poly8x16_t b) { poly16x8_t result; __asm__ ("pmull2 %0.8h,%1.16b,%2.16b\n" "eor %0.16b,%0.16b,%3.16b\n" : "=&w"(result) : "w"(a), "w"(b), "w"(sum) : /* No clobbers */); return result; } # endif #else // non-Apple chip with SHA3 support without SVE2: likely Neoverse V1 or Qualcomm chips // we use EOR3 for accumulation, since PMULL+EOR isn't fused // as this strategy requires more registers to hold values before accumulation, the number of concurrent regions means spills will occur // changing the number of regions would mean that NEON prepare routines couldn't be used anymore though // regardless of spills, it still seems to bench slightly better than CLMul (NEON) #include "gf16_clmul_neon.h" static HEDLEY_ALWAYS_INLINE void gf16_clmul_neon_round1(const void* src, poly16x8_t* low1, poly16x8_t* low2, poly16x8_t* mid1, poly16x8_t* mid2, poly16x8_t* high1, poly16x8_t* high2, const coeff_t* coeff); static HEDLEY_ALWAYS_INLINE void gf16_clmul_sha3_merge1( poly16x8_t* low1, poly16x8_t* low2, poly16x8_t* mid1, poly16x8_t* mid2, poly16x8_t* high1, poly16x8_t* high2, poly16x8_t low1b, poly16x8_t low2b, poly16x8_t mid1b, poly16x8_t mid2b, poly16x8_t high1b, poly16x8_t high2b ) { *low1 = veorq_p16(*low1, low1b); *low2 = veorq_p16(*low2, low2b); *mid1 = veorq_p16(*mid1, mid1b); *mid2 = veorq_p16(*mid2, mid2b); *high1 = veorq_p16(*high1, high1b); *high2 = veorq_p16(*high2, high2b); } static HEDLEY_ALWAYS_INLINE void gf16_clmul_sha3_merge2( poly16x8_t* low1, poly16x8_t* low2, poly16x8_t* mid1, poly16x8_t* mid2, poly16x8_t* high1, poly16x8_t* high2, poly16x8_t low1b, poly16x8_t low2b, poly16x8_t mid1b, poly16x8_t mid2b, poly16x8_t high1b, poly16x8_t high2b, poly16x8_t low1c, poly16x8_t low2c, poly16x8_t mid1c, poly16x8_t mid2c, poly16x8_t high1c, poly16x8_t high2c ) { *low1 = vreinterpretq_p16_u8(veor3q_u8(vreinterpretq_u8_p16(*low1), vreinterpretq_u8_p16(low1b), vreinterpretq_u8_p16(low1c))); *low2 = vreinterpretq_p16_u8(veor3q_u8(vreinterpretq_u8_p16(*low2), vreinterpretq_u8_p16(low2b), vreinterpretq_u8_p16(low2c))); *mid1 = vreinterpretq_p16_u8(veor3q_u8(vreinterpretq_u8_p16(*mid1), vreinterpretq_u8_p16(mid1b), vreinterpretq_u8_p16(mid1c))); *mid2 = vreinterpretq_p16_u8(veor3q_u8(vreinterpretq_u8_p16(*mid2), vreinterpretq_u8_p16(mid2b), vreinterpretq_u8_p16(mid2c))); *high1 = vreinterpretq_p16_u8(veor3q_u8(vreinterpretq_u8_p16(*high1), vreinterpretq_u8_p16(high1b), vreinterpretq_u8_p16(high1c))); *high2 = vreinterpretq_p16_u8(veor3q_u8(vreinterpretq_u8_p16(*high2), vreinterpretq_u8_p16(high2b), vreinterpretq_u8_p16(high2c))); } #define GF16_CLMUL_PROCESS_VARS \ poly16x8_t low1a, low2a, mid1a, mid2a, high1a, high2a; \ poly16x8_t low1b, low2b, mid1b, mid2b, high1b, high2b; \ poly16x8_t low1c, low2c, mid1c, mid2c, high1c, high2c #define GF16_CLMUL_DO_PROCESS \ gf16_clmul_neon_round1(_src1+ptr*srcScale, &low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, coeff + 0); \ if(srcCount > 1) \ gf16_clmul_neon_round1(_src2+ptr*srcScale, &low1b, &low2b, &mid1b, &mid2b, &high1b, &high2b, coeff + CLMUL_COEFF_PER_REGION*1); \ if(srcCount > 2) { \ gf16_clmul_neon_round1(_src3+ptr*srcScale, &low1c, &low2c, &mid1c, &mid2c, &high1c, &high2c, coeff + CLMUL_COEFF_PER_REGION*2); \ gf16_clmul_sha3_merge2(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b, low1c, low2c, mid1c, mid2c, high1c, high2c); \ } else if(srcCount == 2) \ gf16_clmul_sha3_merge1(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b); \ if(srcCount > 3) \ gf16_clmul_neon_round1(_src4+ptr*srcScale, &low1b, &low2b, &mid1b, &mid2b, &high1b, &high2b, coeff + CLMUL_COEFF_PER_REGION*3); \ if(srcCount > 4) { \ gf16_clmul_neon_round1(_src5+ptr*srcScale, &low1c, &low2c, &mid1c, &mid2c, &high1c, &high2c, coeff + CLMUL_COEFF_PER_REGION*4); \ gf16_clmul_sha3_merge2(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b, low1c, low2c, mid1c, mid2c, high1c, high2c); \ } else if(srcCount == 4) \ gf16_clmul_sha3_merge1(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b); \ if(srcCount > 5) \ gf16_clmul_neon_round1(_src6+ptr*srcScale, &low1b, &low2b, &mid1b, &mid2b, &high1b, &high2b, coeff + CLMUL_COEFF_PER_REGION*5); \ if(srcCount > 6) { \ gf16_clmul_neon_round1(_src7+ptr*srcScale, &low1c, &low2c, &mid1c, &mid2c, &high1c, &high2c, coeff + CLMUL_COEFF_PER_REGION*6); \ gf16_clmul_sha3_merge2(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b, low1c, low2c, mid1c, mid2c, high1c, high2c); \ } else if(srcCount == 6) \ gf16_clmul_sha3_merge1(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b); \ if(srcCount > 7) { \ gf16_clmul_neon_round1(_src8+ptr*srcScale, &low1b, &low2b, &mid1b, &mid2b, &high1b, &high2b, coeff + CLMUL_COEFF_PER_REGION*7); \ gf16_clmul_sha3_merge1(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b); \ } \ gf16_clmul_neon_reduction(&low1a, &low2a, mid1a, mid2a, &high1a, &high2a); \ \ uint8x16x2_t vb = vld2q_u8(_dst+ptr); \ vb.val[0] = veor3q_u8(vreinterpretq_u8_p16(low1a), vreinterpretq_u8_p16(low2a), vb.val[0]); \ vb.val[1] = veor3q_u8(vreinterpretq_u8_p16(high1a), vreinterpretq_u8_p16(high2a), vb.val[1]); \ vst2q_u8(_dst+ptr, vb) #endif // defined(__APPLE__) #else int gf16_available_neon_sha3 = 0; #endif /*defined(__ARM_FEATURE_SHA3)*/ #define _FNSUFFIX _sha3 #include "gf16_clmul_neon_base.h" #undef _FNSUFFIX par2cmdline-turbo-1.4.0/parpar/gf16/gf16_clmul_sve2.c000066400000000000000000000176051514221355600221540ustar00rootroot00000000000000 #include "gf16_clmul_sve2.h" #include "gf16_muladd_multi.h" #if defined(__ARM_FEATURE_SVE2) static HEDLEY_ALWAYS_INLINE void gf16_clmul_sve2_round(const void* src, svuint8_t* low1, svuint8_t* low2, svuint8_t* mid1, svuint8_t* mid2, svuint8_t* high1, svuint8_t* high2, svuint8_t coeff) { svuint8x2_t data = svld2_u8(svptrue_b8(), src); svuint8_t swapCoeff = svreinterpret_u8_u16(NOMASK(svrevb_u16, svreinterpret_u16_u8(coeff))); svuint8_t midCoeff = NOMASK(sveor_u8, coeff, swapCoeff); *low1 = svpmullb_pair_u8(svget2(data, 0), coeff); *low2 = svpmullt_pair_u8(svget2(data, 0), swapCoeff); svuint8_t mid = NOMASK(sveor_u8, svget2(data, 0), svget2(data, 1)); *mid1 = svpmullb_pair_u8(mid, midCoeff); *mid2 = svpmullt_pair_u8(mid, midCoeff); *high1 = svpmullb_pair_u8(svget2(data, 1), swapCoeff); *high2 = svpmullt_pair_u8(svget2(data, 1), coeff); } static HEDLEY_ALWAYS_INLINE void gf16_clmul_sve2_merge1( svuint8_t* low1, svuint8_t* low2, svuint8_t* mid1, svuint8_t* mid2, svuint8_t* high1, svuint8_t* high2, svuint8_t low1b, svuint8_t low2b, svuint8_t mid1b, svuint8_t mid2b, svuint8_t high1b, svuint8_t high2b ) { *low1 = NOMASK(sveor_u8, *low1, low1b); *low2 = NOMASK(sveor_u8, *low2, low2b); *mid1 = NOMASK(sveor_u8, *mid1, mid1b); *mid2 = NOMASK(sveor_u8, *mid2, mid2b); *high1 = NOMASK(sveor_u8, *high1, high1b); *high2 = NOMASK(sveor_u8, *high2, high2b); } static HEDLEY_ALWAYS_INLINE void gf16_clmul_sve2_merge2( svuint8_t* low1, svuint8_t* low2, svuint8_t* mid1, svuint8_t* mid2, svuint8_t* high1, svuint8_t* high2, svuint8_t low1b, svuint8_t low2b, svuint8_t mid1b, svuint8_t mid2b, svuint8_t high1b, svuint8_t high2b, svuint8_t low1c, svuint8_t low2c, svuint8_t mid1c, svuint8_t mid2c, svuint8_t high1c, svuint8_t high2c ) { *low1 = sveor3_u8(*low1, low1b, low1c); *low2 = sveor3_u8(*low2, low2b, low2c); *mid1 = sveor3_u8(*mid1, mid1b, mid1c); *mid2 = sveor3_u8(*mid2, mid2b, mid2c); *high1 = sveor3_u8(*high1, high1b, high1c); *high2 = sveor3_u8(*high2, high2b, high2c); } #define CLMUL_NUM_REGIONS 8 static HEDLEY_ALWAYS_INLINE void gf16_clmul_muladd_x_sve2( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(CLMUL_NUM_REGIONS); UNUSED(scratch); svuint8_t coeff0, coeff1, coeff2, coeff3, coeff4, coeff5, coeff6, coeff7; coeff0 = svreinterpret_u8_u16(svdup_n_u16(coefficients[0])); if(srcCount > 1) coeff1 = svreinterpret_u8_u16(svdup_n_u16(coefficients[1])); if(srcCount > 2) coeff2 = svreinterpret_u8_u16(svdup_n_u16(coefficients[2])); if(srcCount > 3) coeff3 = svreinterpret_u8_u16(svdup_n_u16(coefficients[3])); if(srcCount > 4) coeff4 = svreinterpret_u8_u16(svdup_n_u16(coefficients[4])); if(srcCount > 5) coeff5 = svreinterpret_u8_u16(svdup_n_u16(coefficients[5])); if(srcCount > 6) coeff6 = svreinterpret_u8_u16(svdup_n_u16(coefficients[6])); if(srcCount > 7) coeff7 = svreinterpret_u8_u16(svdup_n_u16(coefficients[7])); svuint8_t low1a, low2a, mid1a, mid2a, high1a, high2a; svuint8_t low1b, low2b, mid1b, mid2b, high1b, high2b; svuint8_t low1c, low2c, mid1c, mid2c, high1c, high2c; #define DO_PROCESS \ gf16_clmul_sve2_round(_src1+ptr*srcScale, &low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, coeff0); \ if(srcCount > 1) \ gf16_clmul_sve2_round(_src2+ptr*srcScale, &low1b, &low2b, &mid1b, &mid2b, &high1b, &high2b, coeff1); \ if(srcCount > 2) { \ gf16_clmul_sve2_round(_src3+ptr*srcScale, &low1c, &low2c, &mid1c, &mid2c, &high1c, &high2c, coeff2); \ gf16_clmul_sve2_merge2(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b, low1c, low2c, mid1c, mid2c, high1c, high2c); \ } else if(srcCount == 2) \ gf16_clmul_sve2_merge1(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b); \ if(srcCount > 3) \ gf16_clmul_sve2_round(_src4+ptr*srcScale, &low1b, &low2b, &mid1b, &mid2b, &high1b, &high2b, coeff3); \ if(srcCount > 4) { \ gf16_clmul_sve2_round(_src5+ptr*srcScale, &low1c, &low2c, &mid1c, &mid2c, &high1c, &high2c, coeff4); \ gf16_clmul_sve2_merge2(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b, low1c, low2c, mid1c, mid2c, high1c, high2c); \ } else if(srcCount == 4) \ gf16_clmul_sve2_merge1(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b); \ if(srcCount > 5) \ gf16_clmul_sve2_round(_src6+ptr*srcScale, &low1b, &low2b, &mid1b, &mid2b, &high1b, &high2b, coeff5); \ if(srcCount > 6) { \ gf16_clmul_sve2_round(_src7+ptr*srcScale, &low1c, &low2c, &mid1c, &mid2c, &high1c, &high2c, coeff6); \ gf16_clmul_sve2_merge2(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b, low1c, low2c, mid1c, mid2c, high1c, high2c); \ } else if(srcCount == 6) \ gf16_clmul_sve2_merge1(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b); \ if(srcCount > 7) { \ gf16_clmul_sve2_round(_src8+ptr*srcScale, &low1b, &low2b, &mid1b, &mid2b, &high1b, &high2b, coeff7); \ gf16_clmul_sve2_merge1(&low1a, &low2a, &mid1a, &mid2a, &high1a, &high2a, low1b, low2b, mid1b, mid2b, high1b, high2b); \ } \ gf16_clmul_sve2_reduction(&low1a, &low2a, mid1a, mid2a, &high1a, &high2a); \ \ svuint8x2_t vb = svld2_u8(svptrue_b8(), _dst+ptr); \ low1a = sveor3_u8(low1a, low2a, svget2(vb, 0)); \ high1a = sveor3_u8(high1a, high2a, svget2(vb, 1)); \ svst2_u8(svptrue_b8(), _dst+ptr, svcreate2_u8(low1a, high1a)) if(doPrefetch) { for(intptr_t ptr = -(intptr_t)len; ptr; ptr += svcntb()*2) { if(doPrefetch == 1) { svprfb(svptrue_b8(), _pf+ptr, SV_PLDL1KEEP); svprfb_vnum(svptrue_b8(), _pf+ptr, 1, SV_PLDL1KEEP); } if(doPrefetch == 2) { svprfb(svptrue_b8(), _pf+ptr, SV_PLDL2KEEP); svprfb_vnum(svptrue_b8(), _pf+ptr, 1, SV_PLDL2KEEP); } DO_PROCESS; } } else { for(intptr_t ptr = -(intptr_t)len; ptr; ptr += svcntb()*2) { DO_PROCESS; } } #undef DO_PROCESS } #endif /*defined(__ARM_FEATURE_SVE2)*/ #ifdef PARPAR_INVERT_SUPPORT void gf16_clmul_mul_sve2(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); UNUSED(scratch); #if defined(__ARM_FEATURE_SVE2) svuint8_t coeff = svreinterpret_u8_u16(svdup_n_u16(val)); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; svuint8_t low1, low2, mid1, mid2, high1, high2; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += svcntb()*2) { gf16_clmul_sve2_round(_src+ptr, &low1, &low2, &mid1, &mid2, &high1, &high2, coeff); gf16_clmul_sve2_reduction(&low1, &low2, mid1, mid2, &high1, &high2); low1 = NOMASK(sveor_u8, low1, low2); high1 = NOMASK(sveor_u8, high1, high2); svst2_u8(svptrue_b8(), _dst+ptr, svcreate2_u8(low1, high1)); } #else UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #endif void gf16_clmul_muladd_sve2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__ARM_FEATURE_SVE2) gf16_muladd_single(scratch, &gf16_clmul_muladd_x_sve2, dst, src, len, val); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #include "gf16_checksum_sve.h" #if defined(__ARM_FEATURE_SVE2) GF16_MULADD_MULTI_FUNCS(gf16_clmul, _sve2, gf16_clmul_muladd_x_sve2, CLMUL_NUM_REGIONS, svcntb()*2, 0, (void)0) GF_PREPARE_PACKED_FUNCS(gf16_clmul, _sve2, svcntb()*2, gf16_prepare_block_sve, gf16_prepare_blocku_sve, CLMUL_NUM_REGIONS, (void)0, svint16_t checksum = svdup_n_s16(0), gf16_checksum_block_sve, gf16_checksum_blocku_sve, gf16_checksum_exp_sve, gf16_checksum_prepare_sve, 16) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_clmul, _sve2) GF_PREPARE_PACKED_FUNCS_STUB(gf16_clmul, _sve2) #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_clmul_sve2.h000066400000000000000000000032031514221355600221460ustar00rootroot00000000000000#include "gf16_sve_common.h" #if defined(__ARM_FEATURE_SVE2) static HEDLEY_ALWAYS_INLINE void gf16_clmul_sve2_reduction(svuint8_t* low1, svuint8_t* low2, svuint8_t mid1, svuint8_t mid2, svuint8_t* high1, svuint8_t* high2) { // put data in proper form svuint8_t hibytesL = svtrn1_u8(*high1, *high2); svuint8_t hibytesH = svtrn2_u8(*high1, *high2); svuint8_t lobytesL = svtrn1_u8(*low1, *low2); svuint8_t lobytesH = svtrn2_u8(*low1, *low2); // merge mid into high/low svuint8_t midbytesL = svtrn1_u8(mid1, mid2); svuint8_t midbytesH = svtrn2_u8(mid1, mid2); svuint8_t libytes = NOMASK(sveor_u8, hibytesL, lobytesH); lobytesH = sveor3_u8(midbytesL, lobytesL, libytes); hibytesL = sveor3_u8(midbytesH, hibytesH, libytes); // Barrett reduction // first reduction coefficient is 0x1111a svuint8_t th1 = NOMASK(sveor_u8, hibytesH, NOMASK(svlsr_n_u8, hibytesH, 4)); svuint8_t th0 = svsri_n_u8(NOMASK(svlsl_n_u8, hibytesH, 4), hibytesL, 4); th0 = sveor3_u8(th0, th1, hibytesL); // alternative strategy to above, using nibble flipped ops; same number of ops, but 0xf vector needs to be constructed, so likely worse; maybe there's a better way to leverage it? // svuint8_t th0 = svxar_n_u8(th1, hibytesL, 4); // th0 = svxar_n_u8(th0, svbsl_n_u8(hibytesH, hibytesL, 0xf), 4); svuint8_t th0_hi3 = NOMASK(svlsr_n_u8, th0, 5); th0 = NOMASK(sveor_u8, th0, NOMASK(svlsr_n_u8, hibytesH, 5)); // multiply by polynomial: 0x100b lobytesH = sveor3_u8( lobytesH, svpmul_n_u8(th1, 0x0b), NOMASK(svlsr_n_u8, th0_hi3, 2) ); *high1 = lobytesH; *high2 = svsli_n_u8(th0_hi3, th0, 4); *low1 = lobytesL; *low2 = svpmul_n_u8(th0, 0x0b); } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_global.h000066400000000000000000000542011514221355600213370ustar00rootroot00000000000000#ifndef __GF16_GLOBAL_H #define __GF16_GLOBAL_H #include "../src/hedley.h" #include "../src/stdint.h" #include "../src/platform.h" #include #define GF16_POLYNOMIAL 0x1100b #define GF16_MULTBY_TWO(p) (((p) << 1) ^ (GF16_POLYNOMIAL & -((p) >> 15))) #define UNUSED(...) (void)(__VA_ARGS__) #ifdef _MSC_VER # define inline __inline # pragma warning (disable : 4146) #endif #ifdef _NDEBUG # define ASSUME HEDLEY_ASSUME #else # define ASSUME assert #endif #if defined(__GNUC__) && !defined(__clang__) && !defined(__OPTIMIZE__) // GCC, for some reason, doesn't like const pointers when forced to inline without optimizations # define CONST_PTR * #else # define CONST_PTR *const #endif typedef void (CONST_PTR gf16_checksum_exp)(void *HEDLEY_RESTRICT checksum, uint16_t exp); typedef void (CONST_PTR gf16_checksum_block)(const void *HEDLEY_RESTRICT src, void *HEDLEY_RESTRICT checksum, const size_t blockLen, const int aligned); typedef void (CONST_PTR gf16_checksum_blocku)(const void *HEDLEY_RESTRICT src, size_t amount, void *HEDLEY_RESTRICT checksum); typedef void (CONST_PTR gf16_transform_block)(void* dst, const void* src); typedef void (CONST_PTR gf16_transform_blocku)(void* dst, const void* src, size_t remaining); typedef void (CONST_PTR gf16_transform_block_rst)(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src); typedef void (CONST_PTR gf16_transform_blocku_rst)(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t remaining); typedef void (CONST_PTR gf16_prepare_checksum)(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT checksum, const size_t blockLen, gf16_transform_block_rst prepareBlock); typedef void (CONST_PTR gf16_finish_block)(void *HEDLEY_RESTRICT dst); #undef CONST_PTR #ifdef PARPAR_INVERT_SUPPORT static HEDLEY_ALWAYS_INLINE void gf16_prepare(void* dst, const void* src, size_t srcLen, const size_t blockLen, gf16_transform_block prepareBlock, gf16_transform_blocku prepareBlockU) { size_t remaining = srcLen % blockLen; size_t len = srcLen - remaining; uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += blockLen) prepareBlock(_dst+ptr, _src+ptr); if(remaining) { // handle misaligned part prepareBlockU(_dst, _src, remaining); } } static HEDLEY_ALWAYS_INLINE void gf16_finish(void *HEDLEY_RESTRICT dst, size_t len, const size_t blockLen, gf16_finish_block finishBlock) { uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += blockLen) finishBlock(_dst+ptr); } #endif void gf16_copy_blocku(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len); static HEDLEY_ALWAYS_INLINE void* gf16_checksum_ptr(void* ptr, size_t sliceLen, const size_t blockLen, unsigned numSlices, unsigned index, size_t chunkLen, const unsigned interleaveSize ) { size_t effectiveLastChunkLen = (sliceLen + blockLen) % chunkLen; if(effectiveLastChunkLen == 0) effectiveLastChunkLen = chunkLen; unsigned interleaveBy = (index >= numSlices - (numSlices%interleaveSize)) ? numSlices%interleaveSize : interleaveSize; unsigned fullChunks = (unsigned)(sliceLen / chunkLen); size_t chunkStride = chunkLen * numSlices; return (uint8_t*)ptr + (index%interleaveSize) * blockLen + chunkStride*fullChunks + (index/interleaveSize) * effectiveLastChunkLen * interleaveSize + effectiveLastChunkLen * interleaveBy - blockLen*interleaveBy; } #include #include #include "gfmat_coeff.h" static HEDLEY_ALWAYS_INLINE void gf16_prepare_packed( void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, const size_t blockLen, gf16_transform_block_rst prepareBlock, gf16_transform_blocku_rst prepareBlockU, unsigned inputPackSize, unsigned inputNum, size_t chunkLen, const unsigned interleaveSize, size_t partOffset, size_t partLen, void *HEDLEY_RESTRICT checksum, gf16_checksum_block checksumBlock, gf16_checksum_blocku checksumBlockU, gf16_checksum_exp checksumExp, gf16_prepare_checksum prepareChecksum ) { size_t checksumLen = checksumBlock ? blockLen : 0; ASSUME(inputNum < inputPackSize); ASSUME(srcLen <= sliceLen); ASSUME(chunkLen <= sliceLen+checksumLen); ASSUME(chunkLen % blockLen == 0); ASSUME(sliceLen % blockLen == 0); ASSUME(partOffset % blockLen == 0); ASSUME(partOffset + partLen == srcLen || partLen % blockLen == 0); ASSUME(partOffset + partLen <= srcLen); // simple hack for now src = (const char*)src - partOffset; size_t partLeft = partLen; if(partOffset + partLen == srcLen) partLeft = ~(size_t)0; // if we're completing the slice, ensure that we never exit early size_t dataChunkLen = chunkLen; if(dataChunkLen > sliceLen) dataChunkLen = sliceLen; // interleaving is reduced for the last few slices, if the number of inputs doesn't align unsigned interleaveBy = (inputNum >= inputPackSize - (inputPackSize%interleaveSize)) ? inputPackSize%interleaveSize : interleaveSize; uint8_t* dstBase = (uint8_t*)dst + (inputNum/interleaveSize) * chunkLen * interleaveSize + (inputNum%interleaveSize) * blockLen; unsigned fullChunks = (unsigned)(srcLen/dataChunkLen); size_t chunkStride = chunkLen * inputPackSize; unsigned chunk = partOffset / dataChunkLen; size_t pos = partOffset % dataChunkLen; for(; chunk (sliceLen/dataChunkLen) * dataChunkLen) { // if this is the last chunk, the length may be shorter lastChunkLen = sliceLen % dataChunkLen; // this will be block aligned, as both sliceLen and chunkLen must be block aligned //if(lastChunkLen == 0) lastChunkLen = dataChunkLen; // if effectiveSliceLen is divisible by chunkLen, the last chunk will be chunkLen (this shouldn't be possible, since if it's divisible, it's never considered special; srcLen cannot be > sliceLen) } if(srcLen > (effectiveSliceLen/chunkLen) * chunkLen) { effectiveLastChunkLen = effectiveSliceLen % chunkLen; // if this is the last chunk (and checksums processed), this will be lastChunkLen+blockLen or 0, however 0 isn't possible due to srcLen < effectiveSliceLen constraint } uint8_t* _src = (uint8_t*)src + dataChunkLen*fullChunks; uint8_t* _dst = (uint8_t*)dst + (inputNum/interleaveSize) * effectiveLastChunkLen * interleaveSize + (inputNum%interleaveSize) * blockLen + chunkStride*fullChunks; for(; pos pos) { // handle misaligned part if(!partLeft) return; if(checksumBlock) checksumBlockU(_src + len, remaining-len, checksum); prepareBlockU(_dst + len*interleaveBy, _src + len, remaining-len); pos += blockLen; partLeft -= blockLen; } // zero fill rest of chunk ASSUME(pos <= lastChunkLen); if(checksumBlock) checksumExp(checksum, gf16_exp((((lastChunkLen-pos < partLeft) ? lastChunkLen-pos : partLeft) / blockLen) % 65535)); for(; pos alignedSliceLen) dataChunkLen = alignedSliceLen; unsigned interleaveBy = (outputNum >= numOutputs - (numOutputs%interleaveSize)) ? numOutputs%interleaveSize : interleaveSize; uint8_t* srcBase = (uint8_t*)src + (outputNum/interleaveSize) * chunkLen * interleaveSize + (outputNum%interleaveSize) * blockLen; unsigned fullChunks = (unsigned)(alignedSliceLen/dataChunkLen); size_t remaining = sliceLen - fullChunks*dataChunkLen; // can be negative - if so, will be fixed below size_t chunkStride = chunkLen * numOutputs; size_t pos = partOffset % dataChunkLen; for(unsigned chunk=partOffset/dataChunkLen; chunk sliceLen) { // last block doesn't align to stride for(; pos> 31))) #define GF16_MULTBY_TWO_X4(p) ( \ (((p) << 1) & 0xffffffffffffffffULL) ^ \ ( \ (GF16_POLYNOMIAL ^ (((uint64_t)GF16_POLYNOMIAL) << 16) ^ (((uint64_t)GF16_POLYNOMIAL) << 32) ^ (((uint64_t)(GF16_POLYNOMIAL&0xffff)) << 48)) & \ -((p) >> 63) \ ) \ ) #define GF16_MULTBY_TWO_UPPER(p) ((((p) << 1) & 0xffffffff) ^ (((GF16_POLYNOMIAL&0xffff) << 16) & -((p) >> 31))) #define GF16_MULTBY_TWO_UPPER_X2(p) ( \ (((p) << 1) & 0xffffffffffffffffULL) ^ \ ( \ ((((uint64_t)GF16_POLYNOMIAL) << 16) ^ (((uint64_t)(GF16_POLYNOMIAL&0xffff)) << 48)) & \ -((p) >> 63) \ ) \ ) #define GF16_MULTBY_TWO_LOWER_X2(p) ( \ (((p) << 1) & 0xffffffffffffffffULL) ^ \ ( \ (GF16_POLYNOMIAL ^ (((uint64_t)GF16_POLYNOMIAL) << 32)) & \ -((p) >> 47) \ ) \ ) static HEDLEY_ALWAYS_INLINE void writeXor16(void* p, uint16_t v) { write16(p, v ^ read16(p)); } static HEDLEY_ALWAYS_INLINE void writeXor32(void* p, uint32_t v) { write32(p, v ^ read32(p)); } static HEDLEY_ALWAYS_INLINE void writeXor64(void* p, uint64_t v) { write64(p, v ^ read64(p)); } #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define PACK_2X16(a, b) (((uint32_t)(a) << 16) | (b)) # define PACK_4X16(a, b, c, d) (((uint64_t)(a) << 48) | ((uint64_t)(b) << 32) | ((uint64_t)(c) << 16) | (uint64_t)(d)) # define XTRACT_BYTE(a, b) (((a) >> 8*(sizeof(a)-1 - b)) & 255) #else # define PACK_2X16(a, b) (((uint32_t)(b) << 16) | (a)) # define PACK_4X16(a, b, c, d) (((uint64_t)(d) << 48) | ((uint64_t)(c) << 32) | ((uint64_t)(b) << 16) | (uint64_t)(a)) # define XTRACT_BYTE(a, b) (((a) >> b*8) & 255) #endif #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define SWAP16x2(n) ((((n)&0xff00ff) << 8) | (((n) >> 8) & 0xff00ff)) # define SWAP16x4(n) ((((n)&0xff00ff00ff00ffULL) << 8) | (((n) >> 8) & 0xff00ff00ff00ffULL)) static HEDLEY_ALWAYS_INLINE void calc_table(uint16_t coefficient, uint16_t* lhtable) { int j, k; if(sizeof(uintptr_t) >= 8) { uint32_t coefficient2 = (coefficient << 16) | coefficient; // [*1, *1] coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); // [*2, *2] write64(lhtable, SWAP16x4(((uint64_t)coefficient << 32) | ((uint64_t)(coefficient2^coefficient)))); // [*0, *1, *2, *3] uint64_t coefficient4 = coefficient2 | ((uint64_t)coefficient2 << 32); // [*2, *2, *2, *2] coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); // [*4, *4, *4, *4] uint64_t coeffFlip = SWAP16x4(coefficient4); for (j = 1; j < 64; j <<= 1) { for (k = 0; k < j; k++) { write64(lhtable + 4*(k+j), coeffFlip ^ read64(lhtable + k*4)); } coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); coeffFlip = SWAP16x4(coefficient4); } uint64_t tmp = coefficient4 & 0xffff0000ffffULL; // [*0, *256, *0, *256] coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); // [*512, *512, *512, *512] write64(lhtable + 256, SWAP16x4(tmp ^ (coefficient4 & 0xffffffff))); // [*0, *256, *512, *768] coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); // [*1024, *1024, *1024, *1024] coeffFlip = SWAP16x4(coefficient4); for (j = 1; j < 64; j <<= 1) { for (k = 0; k < j; k++) { write64(lhtable + 256 + 4*(k+j), coeffFlip ^ read64(lhtable + 256 + k*4)); } coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); coeffFlip = SWAP16x4(coefficient4); } } else if(sizeof(uintptr_t) >= 4) { write32(lhtable, SWAP16(coefficient)); uint32_t coefficient2 = coefficient | (coefficient << 16); coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); uint32_t coeffFlip = SWAP16x2(coefficient2); for (j = 1; j < 128; j <<= 1) { for (k = 0; k < j; k++) { write32(lhtable + 2*(k+j), coeffFlip ^ read32(lhtable + k*2)); } coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); coeffFlip = SWAP16x2(coefficient2); } write32(lhtable + 256, SWAP16(coefficient2 & 0xffff)); // coeffFlip & 0xffff coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); coeffFlip = SWAP16x2(coefficient2); for (j = 1; j < 128; j <<= 1) { for (k = 0; k < j; k++) { write32(lhtable + 256 + 2*(k+j), coeffFlip ^ read32(lhtable + 256 + k*2)); } coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); coeffFlip = SWAP16x2(coefficient2); } } else { uint16_t coeffFlip = SWAP16(coefficient); lhtable[0] = 0; for (j = 1; j < 256; j <<= 1) { for (k = 0; k < j; k++) lhtable[k+j] = (coeffFlip ^ lhtable[k]); coefficient = GF16_MULTBY_TWO(coefficient); coeffFlip = SWAP16(coefficient); } lhtable[256] = 0; for (j = 1; j < 256; j <<= 1) { for (k = 0; k < j; k++) lhtable[256 + k+j] = (coeffFlip ^ lhtable[256 + k]); coefficient = GF16_MULTBY_TWO(coefficient); coeffFlip = SWAP16(coefficient); } } } #else /* little endian CPU */ static HEDLEY_ALWAYS_INLINE void calc_table(uint16_t coefficient, uint16_t* lhtable) { int j, k; if(sizeof(uintptr_t) >= 8) { uint32_t coefficient2 = ((uint32_t)coefficient << 16); // [*0, *1] uint32_t tmp = coefficient2 | coefficient; // [*1, *1] tmp = GF16_MULTBY_TWO_X2(tmp); // [*2, *2] write64(lhtable, coefficient2 | ((uint64_t)(tmp^coefficient2) << 32)); // [*0, *1, *2, *3] uint64_t coefficient4 = tmp | ((uint64_t)tmp << 32); // [*2, *2, *2, *2] coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); // [*4, *4, *4, *4] for (j = 1; j < 64; j <<= 1) { for (k = 0; k < j; k++) { write64(lhtable + 4*(k+j), coefficient4 ^ read64(lhtable + k*4)); } coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); } uint64_t tmp2 = coefficient4 & 0xffff0000ffff0000ULL; // [*0, *256, *0, *256] coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); // [*512, *512, *512, *512] write64(lhtable + 256, tmp2 ^ (coefficient4 << 32)); // [*0, *256, *512, *768] coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); // [*1024, *1024, *1024, *1024] for (j = 1; j < 64; j <<= 1) { for (k = 0; k < j; k++) { write64(lhtable + 256 + 4*(k+j), coefficient4 ^ read64(lhtable + 256 + k*4)); } coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); } } else if(sizeof(uintptr_t) >= 4) { uint32_t coefficient2 = ((uint32_t)coefficient << 16); write32(lhtable, coefficient2); coefficient2 |= coefficient; coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); for (j = 1; j < 128; j <<= 1) { for (k = 0; k < j; k++) { write32(lhtable + 2*(k+j), coefficient2 ^ read32(lhtable + k*2)); } coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); } write32(lhtable + 256, coefficient2 & 0xffff0000); coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); for (j = 1; j < 128; j <<= 1) { for (k = 0; k < j; k++) { write32(lhtable + 256 + 2*(k+j), coefficient2 ^ read32(lhtable + 256 + k*2)); } coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); } } else { lhtable[0] = 0; for (j = 1; j < 256; j <<= 1) { for (k = 0; k < j; k++) lhtable[k+j] = (coefficient ^ lhtable[k]); coefficient = GF16_MULTBY_TWO(coefficient); } lhtable[256] = 0; for (j = 1; j < 256; j <<= 1) { for (k = 0; k < j; k++) lhtable[256 + k+j] = (coefficient ^ lhtable[256 + k]); coefficient = GF16_MULTBY_TWO(coefficient); } } } #endif #ifdef PARPAR_INVERT_SUPPORT void gf16_lookup_mul(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(scratch); UNUSED(mutScratch); uint16_t lhtable[512]; calc_table(coefficient, lhtable); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; if(sizeof(uintptr_t) >= 8) { // process in 64-bit for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=8) { write64(_dst + ptr, PACK_4X16( lhtable[_src[ptr]] ^ lhtable[256 + _src[ptr + 1]], lhtable[_src[ptr + 2]] ^ lhtable[256 + _src[ptr + 3]], lhtable[_src[ptr + 4]] ^ lhtable[256 + _src[ptr + 5]], lhtable[_src[ptr + 6]] ^ lhtable[256 + _src[ptr + 7]] )); } } else if(sizeof(uintptr_t) >= 4) { // assume 32-bit CPU for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=4) { write32(_dst + ptr, PACK_2X16( lhtable[_src[ptr]] ^ lhtable[256 + _src[ptr + 1]], lhtable[_src[ptr + 2]] ^ lhtable[256 + _src[ptr + 3]] )); } } else { // use 2 byte wordsize for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=2) { write16(_dst + ptr, lhtable[_src[ptr]] ^ lhtable[256 + _src[ptr + 1]]); } } } #endif void gf16_lookup_muladd(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(scratch); UNUSED(mutScratch); uint16_t lhtable[512]; calc_table(coefficient, lhtable); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; if(sizeof(uintptr_t) >= 8) { // process in 64-bit for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=8) { writeXor64(_dst + ptr, PACK_4X16( lhtable[_src[ptr]] ^ lhtable[256 + _src[ptr + 1]], lhtable[_src[ptr + 2]] ^ lhtable[256 + _src[ptr + 3]], lhtable[_src[ptr + 4]] ^ lhtable[256 + _src[ptr + 5]], lhtable[_src[ptr + 6]] ^ lhtable[256 + _src[ptr + 7]] )); } } else if(sizeof(uintptr_t) >= 4) { // assume 32-bit CPU for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=4) { writeXor32(_dst + ptr, PACK_2X16( lhtable[_src[ptr]] ^ lhtable[256 + _src[ptr + 1]], lhtable[_src[ptr + 2]] ^ lhtable[256 + _src[ptr + 3]] )); } } else { // use 2 byte wordsize for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=2) { writeXor16(_dst + ptr, lhtable[_src[ptr]] ^ lhtable[256 + _src[ptr + 1]]); } } } #ifdef PARPAR_POW_SUPPORT void gf16_lookup_powadd(const void *HEDLEY_RESTRICT scratch, unsigned outputs, size_t offset, void **HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(scratch); UNUSED(mutScratch); uint16_t lhtable[512]; calc_table(coefficient, lhtable); uint8_t* _src = (uint8_t*)src + len + offset; size_t lenPlusOffset = len + offset; // TODO: consider pre-computing this to all dst pointers if(sizeof(uintptr_t) >= 8) { // process in 64-bit for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=8) { size_t dstPtr = ptr + lenPlusOffset; uint16_t res1 = lhtable[_src[ptr]] ^ lhtable[256 + _src[ptr + 1]]; uint16_t res2 = lhtable[_src[ptr + 2]] ^ lhtable[256 + _src[ptr + 3]]; uint16_t res3 = lhtable[_src[ptr + 4]] ^ lhtable[256 + _src[ptr + 5]]; uint16_t res4 = lhtable[_src[ptr + 6]] ^ lhtable[256 + _src[ptr + 7]]; writeXor64((uint8_t*)dst[0] + dstPtr, PACK_4X16(res1, res2, res3, res4)); for(unsigned output = 1; output < outputs; output++) { res1 = lhtable[XTRACT_BYTE(res1, 0)] ^ lhtable[256 + XTRACT_BYTE(res1, 1)]; res2 = lhtable[XTRACT_BYTE(res2, 0)] ^ lhtable[256 + XTRACT_BYTE(res2, 1)]; res3 = lhtable[XTRACT_BYTE(res3, 0)] ^ lhtable[256 + XTRACT_BYTE(res3, 1)]; res4 = lhtable[XTRACT_BYTE(res4, 0)] ^ lhtable[256 + XTRACT_BYTE(res4, 1)]; writeXor64((uint8_t*)dst[output] + dstPtr, PACK_4X16(res1, res2, res3, res4)); } } } else if(sizeof(uintptr_t) >= 4) { // assume 32-bit CPU for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=4) { size_t dstPtr = ptr + lenPlusOffset; uint16_t res1 = lhtable[_src[ptr]] ^ lhtable[256 + _src[ptr + 1]]; uint16_t res2 = lhtable[_src[ptr + 2]] ^ lhtable[256 + _src[ptr + 3]]; writeXor32((uint8_t*)dst[0] + dstPtr, PACK_2X16(res1, res2)); for(unsigned output = 1; output < outputs; output++) { res1 = lhtable[XTRACT_BYTE(res1, 0)] ^ lhtable[256 + XTRACT_BYTE(res1, 1)]; res2 = lhtable[XTRACT_BYTE(res2, 0)] ^ lhtable[256 + XTRACT_BYTE(res2, 1)]; writeXor32((uint8_t*)dst[output] + dstPtr, PACK_2X16(res1, res2)); } } } else { // use 2 byte wordsize for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=2) { size_t dstPtr = ptr + lenPlusOffset; uint16_t data = read16(_src + ptr); for(unsigned output = 0; output < outputs; output++) { data = lhtable[XTRACT_BYTE(data, 0)] ^ lhtable[256 + XTRACT_BYTE(data, 1)]; writeXor16((uint8_t*)dst[output] + dstPtr, data); } } } } #endif HEDLEY_CONST size_t gf16_lookup_stride() { if(sizeof(uintptr_t) >= 8) return 8; else if(sizeof(uintptr_t) >= 4) return 4; else return 2; } #if !defined(PARPAR_SLIM_GF16) struct gf16_lookup3_tables { uint16_t table1[2048]; // bits 0-10 uint32_t table2[1024]; // bits 11-20 uint16_t table3[2048]; // bits 21-31 }; static HEDLEY_ALWAYS_INLINE void calc_3table(uint16_t coefficient, struct gf16_lookup3_tables* lookup) { if(sizeof(uintptr_t) >= 8) { uint32_t coefficient2 = ((uint32_t)coefficient << 16); uint32_t tmp = coefficient2 | coefficient; tmp = GF16_MULTBY_TWO_X2(tmp); write64(lookup->table1, coefficient2 | ((uint64_t)(tmp^coefficient2) << 32)); uint64_t coefficient4 = tmp | ((uint64_t)tmp << 32); coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); int j, k; for (j = 1; j < 512; j <<= 1) { for (k = 0; k < j; k++) write64(lookup->table1 + 4*(k+j), coefficient4 ^ read64(lookup->table1 + 4*k)); coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); } write64(lookup->table2, coefficient4 & 0xffff00000000ULL); coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); coefficient4 &= 0xffff0000ffffULL; for (j = 1; j < 16; j <<= 1) { for (k = 0; k < j; k++) write64(lookup->table2 + 2*(k+j), coefficient4 ^ read64(lookup->table2 + 2*k)); coefficient4 = GF16_MULTBY_TWO_LOWER_X2(coefficient4); } coefficient4 = (uint64_t)coefficient << 48; coefficient4 |= (uint32_t)coefficient << 16; for (j = 16; j < 512; j <<= 1) { for (k = 0; k < j; k++) write64(lookup->table2 + 2*(k+j), coefficient4 ^ read64(lookup->table2 + 2*k)); coefficient4 = GF16_MULTBY_TWO_UPPER_X2(coefficient4); } uint64_t tmp2 = coefficient4; coefficient4 |= coefficient4 >> 16; coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); write64(lookup->table3, tmp2 ^ (coefficient4 << 32)); coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); for (j = 1; j < 512; j <<= 1) { for (k = 0; k < j; k++) write64(lookup->table3 + 4*(k+j), coefficient4 ^ read64(lookup->table3 + 4*k)); coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); } } else { uint32_t coefficient2 = ((uint32_t)coefficient << 16); write32(lookup->table1, coefficient2); coefficient2 |= coefficient; coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); int j, k; for (j = 1; j < 1024; j <<= 1) { for (k = 0; k < j; k++) write32(lookup->table1 + 2*(k+j), coefficient2 ^ read32(lookup->table1 + 2*k)); coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); } lookup->table2[0] = 0; uint16_t val = coefficient2 & 0xffff; for (j = 1; j < 32; j <<= 1) { for (k = 0; k < j; k++) lookup->table2[k+j] = val ^ lookup->table2[k]; val = GF16_MULTBY_TWO(val); } coefficient2 = (uint32_t)coefficient << 16; for (j = 32; j < 1024; j <<= 1) { for (k = 0; k < j; k++) lookup->table2[k+j] = coefficient2 ^ lookup->table2[k]; coefficient2 = GF16_MULTBY_TWO_UPPER(coefficient2); } write32(lookup->table3, coefficient2); coefficient2 |= coefficient2>>16; coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); for (j = 1; j < 1024; j <<= 1) { for (k = 0; k < j; k++) write32(lookup->table3 + 2*(k+j), coefficient2 ^ read32(lookup->table3 + 2*k)); coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); } } } #endif #ifdef PARPAR_INVERT_SUPPORT void gf16_lookup3_mul(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(scratch); UNUSED(mutScratch); #if !defined(PARPAR_SLIM_GF16) struct gf16_lookup3_tables lookup; calc_3table(coefficient, &lookup); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; if(sizeof(uintptr_t) >= 8) { // assume 64-bit CPU for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=8) { uint64_t data = read64(_src + ptr); uint32_t data2 = data >> 32; write64(_dst + ptr, ( (uint32_t)lookup.table1[data & 0x7ff] ^ lookup.table2[(data & 0x1ff800) >> 11] ^ ((uint32_t)lookup.table3[(data & 0xffe00000) >> 21] << 16) ) ^ ((uint64_t)( lookup.table1[data2 & 0x7ff] ^ lookup.table2[(data2 & 0x1ff800) >> 11] ) << 32) ^ ((uint64_t)lookup.table3[data2 >> 21] << 48)); } } else { for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=4) { uint32_t data = read32(_src + ptr); write32(_dst + ptr, (uint32_t)lookup.table1[data & 0x7ff] ^ lookup.table2[(data & 0x1ff800) >> 11] ^ ((uint32_t)lookup.table3[data >> 21] << 16) ); } } #else UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } #endif void gf16_lookup3_muladd(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(scratch); UNUSED(mutScratch); #if !defined(PARPAR_SLIM_GF16) struct gf16_lookup3_tables lookup; calc_3table(coefficient, &lookup); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; if(sizeof(uintptr_t) >= 8) { // assume 64-bit CPU for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=8) { uint64_t data = read64(_src + ptr); uint32_t data2 = data >> 32; writeXor64(_dst + ptr, ( (uint32_t)lookup.table1[data & 0x7ff] ^ lookup.table2[(data & 0x1ff800) >> 11] ^ ((uint32_t)lookup.table3[(data & 0xffe00000) >> 21] << 16) ) ^ ((uint64_t)( (uint64_t)lookup.table1[data2 & 0x7ff] ^ lookup.table2[(data2 & 0x1ff800) >> 11] ) << 32) ^ ((uint64_t)lookup.table3[data2 >> 21] << 48)); } } else { for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=4) { uint32_t data = read32(_src + ptr); writeXor32(_dst + ptr, (uint32_t)lookup.table1[data & 0x7ff] ^ lookup.table2[(data & 0x1ff800) >> 11] ^ ((uint32_t)lookup.table3[data >> 21] << 16) ); } } #else UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } #if !defined(PARPAR_SLIM_GF16) struct gf16_lookup2_tables { uint16_t table1[2048]; // bits 0-10 & 16-26 uint32_t table2[1024]; // bits 11-15 + 27-31 }; static HEDLEY_ALWAYS_INLINE void calc_2table(uint16_t coefficient, struct gf16_lookup2_tables* lookup) { if(sizeof(uintptr_t) >= 8) { uint32_t coefficient2 = ((uint32_t)coefficient << 16); uint32_t tmp = coefficient2 | coefficient; tmp = GF16_MULTBY_TWO_X2(tmp); write64(lookup->table1, coefficient2 | ((uint64_t)(tmp^coefficient2) << 32)); uint64_t coefficient4 = tmp | ((uint64_t)tmp << 32); coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); int j, k; for (j = 1; j < 512; j <<= 1) { for (k = 0; k < j; k++) write64(lookup->table1 + 4*(k+j), coefficient4 ^ read64(lookup->table1 + 4*k)); coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); } write64(lookup->table2, coefficient4 & 0xffff00000000ULL); coefficient4 = GF16_MULTBY_TWO_X4(coefficient4); coefficient4 &= 0xffff0000ffffULL; for (j = 1; j < 16; j <<= 1) { for (k = 0; k < j; k++) write64(lookup->table2 + 2*(k+j), coefficient4 ^ read64(lookup->table2 + 2*k)); coefficient4 = GF16_MULTBY_TWO_LOWER_X2(coefficient4); } for (j = 1; j < 32; j++) { uint64_t highVal = (uint64_t)lookup->table2[j] * 0x0001000000010000ULL; for (k = 0; k < 16; k++) write64(lookup->table2 + 2*(j*16 + k), highVal | read64(lookup->table2 + 2*k)); } } else { uint32_t coefficient2 = ((uint32_t)coefficient << 16); write32(lookup->table1, coefficient2); coefficient2 |= coefficient; coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); int j, k; for (j = 1; j < 1024; j <<= 1) { for (k = 0; k < j; k++) write32(lookup->table1 + 2*(k+j), coefficient2 ^ read32(lookup->table1 + 2*k)); coefficient2 = GF16_MULTBY_TWO_X2(coefficient2); } lookup->table2[0] = 0; uint16_t val = coefficient2 & 0xffff; for (j = 1; j < 32; j <<= 1) { for (k = 0; k < j; k++) lookup->table2[k+j] = val ^ lookup->table2[k]; val = GF16_MULTBY_TWO(val); } for (j = 1; j < 32; j++) { uint32_t highVal = lookup->table2[j] << 16; for (k = 0; k < 32; k++) lookup->table2[j*32 + k] = lookup->table2[k] | highVal; } } } #endif unsigned gf16_lookup3_muladd_multi_packed(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch) { UNUSED(scratch); UNUSED(packRegions); UNUSED(mutScratch); #if !defined(PARPAR_SLIM_GF16) uint8_t* _dst = (uint8_t*)dst + len; uint8_t* _src = (uint8_t*)src; for(unsigned region=0; region= 8) { // assume 64-bit CPU for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=8) { uint64_t data = read64(_src + ptr); uint32_t data2 = data >> 32; writeXor64(_dst + ptr, ( (uint32_t)lookup.table1[data & 0x7ff] ^ lookup.table2[(data & 0xffc00000) >> 22] ^ ((uint32_t)lookup.table1[(data & 0x3ff800) >> 11] << 16) ) ^ ((uint64_t)( (uint64_t)lookup.table1[data2 & 0x7ff] ^ lookup.table2[data2 >> 22] ) << 32) ^ ((uint64_t)lookup.table1[(data2 & 0x3ff800) >> 11] << 48)); } } else { for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=4) { uint32_t data = read32(_src + ptr); writeXor32(_dst + ptr, (uint32_t)lookup.table1[data & 0x7ff] ^ lookup.table2[data >> 22] ^ ((uint32_t)lookup.table1[(data & 0x3ff800) >> 11] << 16) ); } } } #else UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficients); #endif return regions; } HEDLEY_CONST size_t gf16_lookup3_stride() { #if __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ // we only support this technique on little endian for now if(sizeof(uintptr_t) >= 8) return 8; else if(sizeof(uintptr_t) >= 4) return 4; else #endif return 0; } static HEDLEY_ALWAYS_INLINE void gf16_lookup_checksum_prepare(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT checksum, const size_t blockLen, gf16_transform_block_rst prepareBlock) { UNUSED(prepareBlock); memset(dst, 0, blockLen); if(sizeof(uintptr_t) >= 8) write64(dst, SWAP64(read64(checksum))); else if(sizeof(uintptr_t) >= 4) write32(dst, SWAP32(read32(checksum))); else write16(dst, SWAP16(read16(checksum))); } #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ static HEDLEY_ALWAYS_INLINE void gf16_lookup_checksum_inline_finish(void *HEDLEY_RESTRICT checksum) { if(sizeof(uintptr_t) >= 8) write64(checksum, SWAP64(read64(checksum))); else if(sizeof(uintptr_t) >= 4) write32(checksum, SWAP32(read32(checksum))); else write16(checksum, SWAP16(read16(checksum))); } #else # define gf16_lookup_checksum_inline_finish NULL #endif static HEDLEY_ALWAYS_INLINE void gf16_lookup_copy_block(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { memcpy(dst, src, gf16_lookup_stride()); } static HEDLEY_ALWAYS_INLINE void gf16_lookup_prepare_blocku(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t remaining) { memcpy(dst, src, remaining); memset((char*)dst + remaining, 0, gf16_lookup_stride()-remaining); } void gf16_copy_blocku(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len) { memcpy(dst, src, len); } GF_PREPARE_PACKED_CKSUM_FUNCS(gf16_lookup, _generic, gf16_lookup_stride(), gf16_lookup_copy_block, gf16_lookup_prepare_blocku, 1, (void)0, uintptr_t checksum = 0, gf16_checksum_block_generic, gf16_checksum_blocku_generic, gf16_checksum_exp_generic, gf16_lookup_checksum_prepare, gf16_lookup_stride()) GF_FINISH_PACKED_FUNCS(gf16_lookup, _generic, gf16_lookup_stride(), gf16_lookup_copy_block, gf16_copy_blocku, 1, (void)0, gf16_checksum_block_generic, gf16_checksum_blocku_generic, gf16_checksum_exp_generic, gf16_lookup_checksum_inline_finish, gf16_lookup_stride()) #if !defined(PARPAR_SLIM_GF16) static HEDLEY_ALWAYS_INLINE void gf16_lookup3_prepare_block(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { // pack bits so that we have: 0...10,16...26,11...15,27...31 if(sizeof(uintptr_t) >= 8) { uint64_t data = read64(src); write64(dst, (data & 0xf80007fff80007ffULL) | ((data & 0x07ff000007ff0000ULL) >> 5) | ((data & 0xf8000000f800ULL) << 11)); } else { uint32_t data = read32(src); write32(dst, (data & 0xf80007ff) | ((data & 0x07ff0000) >> 5) | ((data & 0xf800) << 11)); } } static HEDLEY_ALWAYS_INLINE void gf16_lookup3_prepare_blocku(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t remaining) { uintptr_t data = 0; memcpy(&data, src, remaining); gf16_lookup3_prepare_block(dst, &data); } static HEDLEY_ALWAYS_INLINE void gf16_lookup3_checksum_prepare(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT checksum, const size_t blockLen, gf16_transform_block_rst prepareBlock) { UNUSED(prepareBlock); gf16_lookup3_prepare_block(dst, checksum); memset((char*)dst+gf16_lookup3_stride(), 0, blockLen-gf16_lookup3_stride()); } GF_PREPARE_PACKED_FUNCS(gf16_lookup3, _generic, gf16_lookup3_stride(), gf16_lookup3_prepare_block, gf16_lookup3_prepare_blocku, 1, (void)0, uintptr_t checksum = 0, gf16_checksum_block_generic, gf16_checksum_blocku_generic, gf16_checksum_exp_generic, gf16_lookup3_checksum_prepare, gf16_lookup3_stride()) #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_lookup3, _generic) #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_lookup.h000066400000000000000000000110451514221355600214070ustar00rootroot00000000000000#ifndef __GF16_LOOKUP_H #define __GF16_LOOKUP_H #include "../src/hedley.h" #include "../src/stdint.h" #include #ifdef PARPAR_INVERT_SUPPORT void gf16_lookup_mul(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); #endif void gf16_lookup_muladd(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); #ifdef PARPAR_POW_SUPPORT void gf16_lookup_powadd(const void *HEDLEY_RESTRICT scratch, unsigned outputs, size_t offset, void **HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); #endif #ifdef PARPAR_INVERT_SUPPORT void gf16_lookup3_mul(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); #endif void gf16_lookup3_muladd(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); void gf16_lookup3_muladd_multi_packed(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); #ifdef PARPAR_INVERT_SUPPORT void gf16_lookup_mul_sse2(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); #endif void gf16_lookup_muladd_sse2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); HEDLEY_CONST size_t gf16_lookup_stride(); HEDLEY_CONST size_t gf16_lookup3_stride(); void gf16_lookup_prepare_packed_cksum_generic(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); void gf16_lookup_prepare_partial_packsum_generic(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen, size_t partOffset, size_t partLen); #ifdef PARPAR_INCLUDE_BASIC_OPS void gf16_lookup_finish_packed_generic(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen); #endif int gf16_lookup_finish_packed_cksum_generic(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen); int gf16_lookup_finish_partial_packsum_generic(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen, size_t partOffset, size_t partLen); #ifdef PARPAR_INCLUDE_BASIC_OPS void gf16_lookup3_prepare_packed_generic(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); #endif void gf16_lookup3_prepare_packed_cksum_generic(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); void gf16_lookup3_prepare_partial_packsum_generic(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen, size_t partOffset, size_t partLen); void gf16_lookup_prepare_packed_cksum_sse2(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); void gf16_lookup_prepare_partial_packsum_sse2(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen, size_t partOffset, size_t partLen); #ifdef PARPAR_INCLUDE_BASIC_OPS void gf16_lookup_finish_packed_sse2(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen); #endif int gf16_lookup_finish_packed_cksum_sse2(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen); int gf16_lookup_finish_partial_packsum_sse2(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen, size_t partOffset, size_t partLen); #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_lookup_sse2.c000066400000000000000000000200161514221355600223340ustar00rootroot00000000000000 #include "gf16_global.h" #include "../src/platform.h" #if defined(__SSE2__) && !defined(PARPAR_SLIM_GF16) # define _AVAILABLE 1 #endif #ifdef _AVAILABLE static HEDLEY_ALWAYS_INLINE void calc_table(uint16_t val, uint16_t* lhtable) { int j, k; __m128i* _lhtable = (__m128i*)lhtable; int val2 = GF16_MULTBY_TWO(val); int val4 = GF16_MULTBY_TWO(val2); __m128i tmp0 = _mm_cvtsi32_si128((uint32_t)val << 16); tmp0 = _mm_insert_epi16(tmp0, val2, 2); tmp0 = _mm_insert_epi16(tmp0, val2 ^ val, 3); __m128i vval4 = _mm_set1_epi16(val4); tmp0 = _mm_unpacklo_epi64(tmp0, _mm_xor_si128(tmp0, vval4)); _mm_store_si128(_lhtable, tmp0); __m128i poly = _mm_set1_epi16(GF16_POLYNOMIAL & 0xffff); #define MUL2(x) _mm_xor_si128( \ _mm_add_epi16(x, x), \ _mm_and_si128(poly, _mm_cmpgt_epi16( \ _mm_setzero_si128(), x \ )) \ ) __m128i mul = MUL2(vval4); // *8 __m128i tmp8 = _mm_xor_si128(tmp0, mul); _mm_store_si128(_lhtable+1, tmp8); mul = MUL2(mul); // *16 for(j = 2; j < 32; j <<= 1) { // save a few reads by having the first 2 values cached in registers _mm_store_si128(_lhtable + j, _mm_xor_si128(mul, tmp0)); _mm_store_si128(_lhtable + j + 1, _mm_xor_si128(mul, tmp8)); // loop over rest for(k = 2; k < j; k++) _mm_store_si128(_lhtable + j + k, _mm_xor_si128(mul, _mm_load_si128(_lhtable + k))); mul = MUL2(mul); } __m128i tmp256 = _mm_slli_epi32(mul, 16); // [*0, *256, *0, *256 ...] mul = MUL2(mul); // *512 tmp256 = _mm_xor_si128(tmp256, _mm_slli_epi64(mul, 32)); // [*0, *256, *512, *768, *0, *256, *512, *768] mul = MUL2(mul); // *1024 tmp256 = _mm_xor_si128(tmp256, _mm_slli_si128(mul, 8)); // [*0, *256, *512 ...] _mm_store_si128(_lhtable + 32, tmp256); mul = MUL2(mul); // *2048 __m128i tmp2048 = _mm_xor_si128(tmp256, mul); _mm_store_si128(_lhtable + 32+1, tmp2048); mul = MUL2(mul); // *4096 for(j = 2; j < 32; j <<= 1) { _mm_store_si128(_lhtable + 32 + j, _mm_xor_si128(mul, tmp256)); _mm_store_si128(_lhtable + 32 + j + 1, _mm_xor_si128(mul, tmp2048)); for(k = 2; k < j; k++) _mm_store_si128(_lhtable + 32 + j + k, _mm_xor_si128(mul, _mm_load_si128(_lhtable + 32 + k))); mul = MUL2(mul); } #undef MUL2 } #endif #ifdef PARPAR_INVERT_SUPPORT void gf16_lookup_mul_sse2(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(scratch); UNUSED(mutScratch); #ifdef _AVAILABLE ALIGN_TO(16, uint16_t lhtable[513]); // +1 for potential misaligned load at end calc_table(coefficient, lhtable); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=sizeof(__m128i)) { uintptr_t input = readPtr(_src+ptr); __m128i lo1 = _mm_cvtsi32_si128(read32(lhtable + (input & 0xff))); // 1/32 chance of crossing cacheline boundary __m128i hi1 = _mm_cvtsi32_si128(read32(lhtable + (256 + ((input >> 8) & 0xff)))); input >>= 16; lo1 = _mm_insert_epi16(lo1, lhtable[input & 0xff], 1); hi1 = _mm_insert_epi16(hi1, lhtable[256 + ((input >> 8) & 0xff)], 1); if(sizeof(uintptr_t) == 8) input >>= 16; else input = readPtr(_src+ptr+4); lo1 = _mm_insert_epi16(lo1, lhtable[input & 0xff], 2); hi1 = _mm_insert_epi16(hi1, lhtable[256 + ((input >> 8) & 0xff)], 2); input >>= 16; lo1 = _mm_insert_epi16(lo1, lhtable[input & 0xff], 3); hi1 = _mm_insert_epi16(hi1, lhtable[256 + ((input >> 8) & 0xff)], 3); input = readPtr(_src+ptr+8); __m128i lo2 = _mm_cvtsi32_si128(read32(lhtable + (input & 0xff))); __m128i hi2 = _mm_cvtsi32_si128(read32(lhtable + (256 + ((input >> 8) & 0xff)))); input >>= 16; lo2 = _mm_insert_epi16(lo2, lhtable[input & 0xff], 1); hi2 = _mm_insert_epi16(hi2, lhtable[256 + ((input >> 8) & 0xff)], 1); if(sizeof(uintptr_t) == 8) input >>= 16; else input = readPtr(_src+ptr+12); lo2 = _mm_insert_epi16(lo2, lhtable[input & 0xff], 2); hi2 = _mm_insert_epi16(hi2, lhtable[256 + ((input >> 8) & 0xff)], 2); input >>= 16; lo2 = _mm_insert_epi16(lo2, lhtable[input & 0xff], 3); hi2 = _mm_insert_epi16(hi2, lhtable[256 + ((input >> 8) & 0xff)], 3); __m128i res1 = _mm_xor_si128(lo1, hi1); __m128i res2 = _mm_xor_si128(lo2, hi2); _mm_store_si128((__m128i*)(_dst+ptr), _mm_unpacklo_epi64(res1, res2)); } #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } #endif void gf16_lookup_muladd_sse2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { UNUSED(scratch); UNUSED(mutScratch); #ifdef _AVAILABLE ALIGN_TO(16, uint16_t lhtable[513]); // +1 for potential misaligned load at end calc_table(coefficient, lhtable); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr+=sizeof(__m128i)) { uintptr_t input = readPtr(_src+ptr); __m128i lo1 = _mm_cvtsi32_si128(read32(lhtable + (input & 0xff))); __m128i hi1 = _mm_cvtsi32_si128(read32(lhtable + (256 + ((input >> 8) & 0xff)))); input >>= 16; lo1 = _mm_insert_epi16(lo1, lhtable[input & 0xff], 1); hi1 = _mm_insert_epi16(hi1, lhtable[256 + ((input >> 8) & 0xff)], 1); if(sizeof(uintptr_t) == 8) input >>= 16; else input = readPtr(_src+ptr+4); lo1 = _mm_insert_epi16(lo1, lhtable[input & 0xff], 2); hi1 = _mm_insert_epi16(hi1, lhtable[256 + ((input >> 8) & 0xff)], 2); input >>= 16; lo1 = _mm_insert_epi16(lo1, lhtable[input & 0xff], 3); hi1 = _mm_insert_epi16(hi1, lhtable[256 + ((input >> 8) & 0xff)], 3); input = readPtr(_src+ptr+8); __m128i lo2 = _mm_cvtsi32_si128(read32(lhtable + (input & 0xff))); __m128i hi2 = _mm_cvtsi32_si128(read32(lhtable + (256 + ((input >> 8) & 0xff)))); input >>= 16; lo2 = _mm_insert_epi16(lo2, lhtable[input & 0xff], 1); hi2 = _mm_insert_epi16(hi2, lhtable[256 + ((input >> 8) & 0xff)], 1); if(sizeof(uintptr_t) == 8) input >>= 16; else input = readPtr(_src+ptr+12); lo2 = _mm_insert_epi16(lo2, lhtable[input & 0xff], 2); hi2 = _mm_insert_epi16(hi2, lhtable[256 + ((input >> 8) & 0xff)], 2); input >>= 16; lo2 = _mm_insert_epi16(lo2, lhtable[input & 0xff], 3); hi2 = _mm_insert_epi16(hi2, lhtable[256 + ((input >> 8) & 0xff)], 3); __m128i res1 = _mm_xor_si128(lo1, hi1); __m128i res2 = _mm_xor_si128(lo2, hi2); res1 = _mm_unpacklo_epi64(res1, res2); res1 = _mm_xor_si128(res1, _mm_load_si128((__m128i*)(_dst+ptr))); _mm_store_si128((__m128i*)(_dst+ptr), res1); } #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); #endif } #ifdef _AVAILABLE static HEDLEY_ALWAYS_INLINE void gf16_lookup_prepare_block_sse2(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { _mm_store_si128((__m128i*)dst, _mm_loadu_si128(src)); } static HEDLEY_ALWAYS_INLINE void gf16_lookup_finish_block_sse2(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { _mm_storeu_si128((__m128i*)dst, _mm_load_si128(src)); } static HEDLEY_ALWAYS_INLINE void gf16_lookup_prepare_blocku(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t remaining) { memcpy(dst, src, remaining); memset((char*)dst + remaining, 0, sizeof(__m128i)-remaining); } #define MWORD_SIZE 16 #define _mword __m128i #define _MM(f) _mm_ ## f #define _MMI(f) _mm_ ## f ## _si128 #define _FNSUFFIX _sse2 #define _MM_END #include "gf16_checksum_x86.h" #undef MWORD_SIZE #undef _mword #undef _MM #undef _MMI #undef _FNSUFFIX #undef _MM_END #endif #ifdef _AVAILABLE GF_PREPARE_PACKED_CKSUM_FUNCS(gf16_lookup, _sse2, sizeof(__m128i), gf16_lookup_prepare_block_sse2, gf16_lookup_prepare_blocku, 1, (void)0, __m128i checksum = _mm_setzero_si128(), gf16_checksum_block_sse2, gf16_checksum_blocku_sse2, gf16_checksum_exp_sse2, gf16_checksum_prepare_sse2, sizeof(__m128i)) GF_FINISH_PACKED_FUNCS(gf16_lookup, _sse2, sizeof(__m128i), gf16_lookup_finish_block_sse2, gf16_copy_blocku, 1, (void)0, gf16_checksum_block_sse2, gf16_checksum_blocku_sse2, gf16_checksum_exp_sse2, NULL, sizeof(__m128i)) #else GF_PREPARE_PACKED_CKSUM_FUNCS_STUB(gf16_lookup, _sse2) GF_FINISH_PACKED_FUNCS_STUB(gf16_lookup, _sse2) #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_muladd_multi.h000066400000000000000000000742231514221355600225650ustar00rootroot00000000000000 #define GF16_MULADD_MULTI_SRCLIST const int srcCount, \ const uint8_t* _src1, const uint8_t* _src2, const uint8_t* _src3, const uint8_t* _src4, const uint8_t* _src5, const uint8_t* _src6, \ const uint8_t* _src7, const uint8_t* _src8, const uint8_t* _src9, const uint8_t* _src10, const uint8_t* _src11, const uint8_t* _src12, \ const uint8_t* _src13, const uint8_t* _src14, const uint8_t* _src15, const uint8_t* _src16, const uint8_t* _src17, const uint8_t* _src18 #define GF16_MULADD_MULTI_SRC_UNUSED(max) \ HEDLEY_ASSUME(srcCount <= max); \ if(max < 2) UNUSED(_src2); \ if(max < 3) UNUSED(_src3); \ if(max < 4) UNUSED(_src4); \ if(max < 5) UNUSED(_src5); \ if(max < 6) UNUSED(_src6); \ if(max < 7) UNUSED(_src7); \ if(max < 8) UNUSED(_src8); \ if(max < 9) UNUSED(_src9); \ if(max < 10) UNUSED(_src10); \ if(max < 11) UNUSED(_src11); \ if(max < 12) UNUSED(_src12); \ if(max < 13) UNUSED(_src13); \ if(max < 14) UNUSED(_src14); \ if(max < 15) UNUSED(_src15); \ if(max < 16) UNUSED(_src16); \ if(max < 17) UNUSED(_src17); \ if(max < 18) UNUSED(_src18) #ifdef PARPAR_INVERT_SUPPORT # define GF16_MULADD_MULTI_FUNCS(fnpre, fnsuf, xfn, procRegions, blocksize, pfFactor, finisher) \ void TOKENPASTE3(fnpre, _muladd_multi, fnsuf)(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch) { \ UNUSED(mutScratch); \ gf16_muladd_multi(scratch, &xfn, procRegions, regions, offset, dst, src, len, coefficients); \ finisher; \ } \ void TOKENPASTE3(fnpre, _muladd_multi_stridepf, fnsuf)(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t srcStride, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetch) { \ UNUSED(mutScratch); \ gf16_muladd_multi_stridepf(scratch, &xfn, procRegions, regions, srcStride, dst, src, len, coefficients, pfFactor, prefetch); \ finisher; \ } \ void TOKENPASTE3(fnpre, _muladd_multi_packed, fnsuf)(const void *HEDLEY_RESTRICT scratch, unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch) { \ UNUSED(mutScratch); \ gf16_muladd_multi_packed(scratch, &xfn, procRegions, procRegions, packedRegions, regions, dst, src, len, blocksize, coefficients); \ finisher; \ } \ void TOKENPASTE3(fnpre, _muladd_multi_packpf, fnsuf)(const void *HEDLEY_RESTRICT scratch, unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ UNUSED(mutScratch); \ gf16_muladd_multi_packpf(scratch, &xfn, procRegions, procRegions, packedRegions, regions, dst, src, len, blocksize, coefficients, pfFactor, prefetchIn, prefetchOut); \ finisher; \ } # define GF16_MULADD_MULTI_FUNCS_STUB(fnpre, fnsuf) \ void TOKENPASTE3(fnpre, _muladd_multi, fnsuf)(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch) { \ UNUSED(mutScratch); \ UNUSED(scratch); UNUSED(regions); UNUSED(offset); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficients); \ } \ void TOKENPASTE3(fnpre, _muladd_multi_stridepf, fnsuf)(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t srcStride, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetch) { \ UNUSED(mutScratch); \ UNUSED(scratch); UNUSED(regions); UNUSED(srcStride); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficients); UNUSED(prefetch); \ } \ void TOKENPASTE3(fnpre, _muladd_multi_packed, fnsuf)(const void *HEDLEY_RESTRICT scratch, unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch) { \ UNUSED(mutScratch); \ UNUSED(scratch); UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficients); \ } \ void TOKENPASTE3(fnpre, _muladd_multi_packpf, fnsuf)(const void *HEDLEY_RESTRICT scratch, unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ UNUSED(mutScratch); \ UNUSED(scratch); UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficients); UNUSED(prefetchIn); UNUSED(prefetchOut); \ } #else # define GF16_MULADD_MULTI_FUNCS(fnpre, fnsuf, xfn, procRegions, blocksize, pfFactor, finisher) \ void TOKENPASTE3(fnpre, _muladd_multi_packed, fnsuf)(const void *HEDLEY_RESTRICT scratch, unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch) { \ UNUSED(mutScratch); \ gf16_muladd_multi_packed(scratch, &xfn, procRegions, procRegions, packedRegions, regions, dst, src, len, blocksize, coefficients); \ finisher; \ } \ void TOKENPASTE3(fnpre, _muladd_multi_packpf, fnsuf)(const void *HEDLEY_RESTRICT scratch, unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ UNUSED(mutScratch); \ gf16_muladd_multi_packpf(scratch, &xfn, procRegions, procRegions, packedRegions, regions, dst, src, len, blocksize, coefficients, pfFactor, prefetchIn, prefetchOut); \ finisher; \ } # define GF16_MULADD_MULTI_FUNCS_STUB(fnpre, fnsuf) \ void TOKENPASTE3(fnpre, _muladd_multi_packed, fnsuf)(const void *HEDLEY_RESTRICT scratch, unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch) { \ UNUSED(mutScratch); \ UNUSED(scratch); UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficients); \ } \ void TOKENPASTE3(fnpre, _muladd_multi_packpf, fnsuf)(const void *HEDLEY_RESTRICT scratch, unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ UNUSED(mutScratch); \ UNUSED(scratch); UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficients); UNUSED(prefetchIn); UNUSED(prefetchOut); \ } #endif #if defined(__GNUC__) && !defined(__clang__) && !defined(__OPTIMIZE__) // GCC, for some reason, doesn't like const pointers when forced to inline without optimizations typedef void (*fMuladdPF) #else typedef void (*const fMuladdPF) #endif (const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ); // suppress UBSan warning about adding to a NULL pointer; `coefficients` can be NULL from gf_add*, but it's never used there, and it's annoying to have to check and branch on these #if defined(__clang__) && HEDLEY_HAS_ATTRIBUTE(no_sanitize) # define IGNORE_NULL_ADD __attribute__((no_sanitize("pointer-overflow"))) #else # define IGNORE_NULL_ADD #endif static HEDLEY_ALWAYS_INLINE void gf16_muladd_single(const void *HEDLEY_RESTRICT scratch, fMuladdPF muladd_pf, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, uint16_t val) { muladd_pf( scratch, (uint8_t*)dst + len, 1, 1, (const uint8_t*)src + len, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, len, &val, 0, NULL ); } static HEDLEY_ALWAYS_INLINE void gf16_muladd_prefetch_single(const void *HEDLEY_RESTRICT scratch, fMuladdPF muladd_pf, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, uint16_t val, const void *HEDLEY_RESTRICT prefetch) { muladd_pf( scratch, (uint8_t*)dst + len, 1, 1, (const uint8_t*)src + len, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, len, &val, 2, prefetch ); } #define REMAINING_CASES CASE(17); CASE(16); CASE(15); CASE(14); CASE(13); CASE(12); CASE(11); CASE(10); CASE( 9); CASE( 8); CASE( 7); CASE( 6); CASE( 5); CASE( 4); CASE( 3); CASE( 2); CASE( 1) #ifdef PARPAR_INVERT_SUPPORT static HEDLEY_ALWAYS_INLINE void gf16_muladd_multi(const void *HEDLEY_RESTRICT scratch, fMuladdPF muladd_pf, const unsigned interleave, unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients) IGNORE_NULL_ADD { uint8_t* _dst = (uint8_t*)dst + offset + len; #define _SRC(limit, n) limit > n ? (const uint8_t*)src[region+n] + offset + len : NULL unsigned region; for(region = 0; region + interleave <= regions; region += interleave) { muladd_pf( scratch, _dst, 1, interleave, (const uint8_t*)src[region] + offset + len, _SRC(interleave, 1), _SRC(interleave, 2), _SRC(interleave, 3), _SRC(interleave, 4), _SRC(interleave, 5), _SRC(interleave, 6), _SRC(interleave, 7), _SRC(interleave, 8), _SRC(interleave, 9), _SRC(interleave, 10), _SRC(interleave, 11), _SRC(interleave, 12), _SRC(interleave,13), _SRC(interleave, 14), _SRC(interleave, 15), _SRC(interleave, 16), _SRC(interleave,17), len, coefficients + region, 0, NULL ); } unsigned remaining = regions - region; HEDLEY_ASSUME(remaining < interleave); // doesn't seem to always work, so we have additional checks in the switch cases switch(remaining) { #define CASE(x) \ case x: \ HEDLEY_ASSUME(x < interleave); \ muladd_pf( \ scratch, _dst, 1, x, \ (const uint8_t*)src[region] + offset + len, \ _SRC(x, 1), _SRC(x, 2), _SRC(x, 3), _SRC(x, 4), \ _SRC(x, 5), _SRC(x, 6), _SRC(x, 7), _SRC(x, 8), \ _SRC(x, 9), _SRC(x, 10), _SRC(x, 11), _SRC(x, 12), \ _SRC(x,13), _SRC(x, 14), _SRC(x, 15), _SRC(x, 16), \ _SRC(x,17), \ len, coefficients + region, 0, NULL \ ); \ break REMAINING_CASES; #undef CASE default: break; } #undef _SRC } static HEDLEY_ALWAYS_INLINE void gf16_muladd_multi_stridepf(const void *HEDLEY_RESTRICT scratch, fMuladdPF muladd_pf, const unsigned interleave, unsigned regions, size_t srcStride, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const unsigned pfFactor, const void* HEDLEY_RESTRICT prefetch) IGNORE_NULL_ADD { uint8_t* _dst = (uint8_t*)dst + len; uint8_t* srcEnd = (uint8_t*)src + len; size_t pfLen = len>>pfFactor; const char* _pf = (const char*)prefetch + pfLen; unsigned outputPfRounds = 1< n ? srcEnd + srcStride*n : NULL unsigned region; for(region = 0; region + interleave <= regions && outputPfRounds; region += interleave) { muladd_pf( scratch, _dst, 1, interleave, srcEnd, _SRC(interleave, 1), _SRC(interleave, 2), _SRC(interleave, 3), _SRC(interleave, 4), _SRC(interleave, 5), _SRC(interleave, 6), _SRC(interleave, 7), _SRC(interleave, 8), _SRC(interleave, 9), _SRC(interleave, 10), _SRC(interleave, 11), _SRC(interleave, 12), _SRC(interleave,13), _SRC(interleave, 14), _SRC(interleave, 15), _SRC(interleave, 16), _SRC(interleave,17), len, coefficients + region, 2, _pf ); srcEnd += srcStride*interleave; outputPfRounds--; _pf += pfLen; } for(; region + interleave <= regions; region += interleave) { muladd_pf( scratch, _dst, 1, interleave, srcEnd, _SRC(interleave, 1), _SRC(interleave, 2), _SRC(interleave, 3), _SRC(interleave, 4), _SRC(interleave, 5), _SRC(interleave, 6), _SRC(interleave, 7), _SRC(interleave, 8), _SRC(interleave, 9), _SRC(interleave, 10), _SRC(interleave, 11), _SRC(interleave, 12), _SRC(interleave,13), _SRC(interleave, 14), _SRC(interleave, 15), _SRC(interleave, 16), _SRC(interleave,17), len, coefficients + region, 0, NULL ); srcEnd += srcStride*interleave; } unsigned remaining = regions - region; HEDLEY_ASSUME(remaining < interleave); // doesn't seem to always work, so we have additional checks in the switch cases if(outputPfRounds) { switch(remaining) { #define CASE(x) \ case x: \ HEDLEY_ASSUME(x < interleave); \ muladd_pf( \ scratch, _dst, 1, x, \ srcEnd, \ _SRC(x, 1), _SRC(x, 2), _SRC(x, 3), _SRC(x, 4), \ _SRC(x, 5), _SRC(x, 6), _SRC(x, 7), _SRC(x, 8), \ _SRC(x, 9), _SRC(x, 10), _SRC(x, 11), _SRC(x, 12), \ _SRC(x,13), _SRC(x, 14), _SRC(x, 15), _SRC(x, 16), \ _SRC(x,17), \ len, coefficients + region, 2, _pf \ ); \ break REMAINING_CASES; #undef CASE default: break; } } else { switch(remaining) { #define CASE(x) \ case x: \ HEDLEY_ASSUME(x < interleave); \ muladd_pf( \ scratch, _dst, 1, x, \ srcEnd, \ _SRC(x, 1), _SRC(x, 2), _SRC(x, 3), _SRC(x, 4), \ _SRC(x, 5), _SRC(x, 6), _SRC(x, 7), _SRC(x, 8), \ _SRC(x, 9), _SRC(x, 10), _SRC(x, 11), _SRC(x, 12), \ _SRC(x,13), _SRC(x, 14), _SRC(x, 15), _SRC(x, 16), \ _SRC(x,17), \ len, coefficients + region, 0, NULL \ ); \ break REMAINING_CASES; #undef CASE default: break; } } #undef _SRC } #endif static HEDLEY_ALWAYS_INLINE void gf16_muladd_multi_packed(const void *HEDLEY_RESTRICT scratch, fMuladdPF muladd_pf, const unsigned interleave, unsigned regionsPerCall, unsigned inputPackSize, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, size_t blockLen, const uint16_t *HEDLEY_RESTRICT coefficients) IGNORE_NULL_ADD { ASSUME(regions <= inputPackSize); uint8_t* _dst = (uint8_t*)dst + len; const uint8_t* _src = (const uint8_t*)src; const uint8_t* srcEnd; ASSUME(regionsPerCall % interleave == 0); unsigned region = 0; if(regions >= regionsPerCall) do { srcEnd = _src + region * len + len*interleave; muladd_pf( scratch, _dst, interleave, regionsPerCall, srcEnd, srcEnd + len*interleave*( 1/interleave) + blockLen*( 1%interleave), srcEnd + len*interleave*( 2/interleave) + blockLen*( 2%interleave), srcEnd + len*interleave*( 3/interleave) + blockLen*( 3%interleave), srcEnd + len*interleave*( 4/interleave) + blockLen*( 4%interleave), srcEnd + len*interleave*( 5/interleave) + blockLen*( 5%interleave), srcEnd + len*interleave*( 6/interleave) + blockLen*( 6%interleave), srcEnd + len*interleave*( 7/interleave) + blockLen*( 7%interleave), srcEnd + len*interleave*( 8/interleave) + blockLen*( 8%interleave), srcEnd + len*interleave*( 9/interleave) + blockLen*( 9%interleave), srcEnd + len*interleave*(10/interleave) + blockLen*(10%interleave), srcEnd + len*interleave*(11/interleave) + blockLen*(11%interleave), srcEnd + len*interleave*(12/interleave) + blockLen*(12%interleave), srcEnd + len*interleave*(13/interleave) + blockLen*(13%interleave), srcEnd + len*interleave*(14/interleave) + blockLen*(14%interleave), srcEnd + len*interleave*(15/interleave) + blockLen*(15%interleave), srcEnd + len*interleave*(16/interleave) + blockLen*(16%interleave), srcEnd + len*interleave*(17/interleave) + blockLen*(17%interleave), len, coefficients + region, 0, NULL ); region += regionsPerCall; } while(regionsPerCall <= regions - region); unsigned remaining = regions - region; HEDLEY_ASSUME(remaining < regionsPerCall); // doesn't seem to always work, so we have additional checks in the switch cases if(regionsPerCall > interleave && remaining >= interleave) { // latter condition implies the former, but leavel in former for optimisation srcEnd = _src + region * len + len*interleave; regionsPerCall = remaining - (remaining % interleave); switch(regionsPerCall) { #define CASE(x) \ case x: \ muladd_pf( \ scratch, _dst, interleave, x, \ srcEnd, \ srcEnd + len*interleave*( 1/interleave) + blockLen*( 1%interleave), \ srcEnd + len*interleave*( 2/interleave) + blockLen*( 2%interleave), \ srcEnd + len*interleave*( 3/interleave) + blockLen*( 3%interleave), \ srcEnd + len*interleave*( 4/interleave) + blockLen*( 4%interleave), \ srcEnd + len*interleave*( 5/interleave) + blockLen*( 5%interleave), \ srcEnd + len*interleave*( 6/interleave) + blockLen*( 6%interleave), \ srcEnd + len*interleave*( 7/interleave) + blockLen*( 7%interleave), \ srcEnd + len*interleave*( 8/interleave) + blockLen*( 8%interleave), \ srcEnd + len*interleave*( 9/interleave) + blockLen*( 9%interleave), \ srcEnd + len*interleave*(10/interleave) + blockLen*(10%interleave), \ srcEnd + len*interleave*(11/interleave) + blockLen*(11%interleave), \ srcEnd + len*interleave*(12/interleave) + blockLen*(12%interleave), \ srcEnd + len*interleave*(13/interleave) + blockLen*(13%interleave), \ srcEnd + len*interleave*(14/interleave) + blockLen*(14%interleave), \ srcEnd + len*interleave*(15/interleave) + blockLen*(15%interleave), \ srcEnd + len*interleave*(16/interleave) + blockLen*(16%interleave), \ srcEnd + len*interleave*(17/interleave) + blockLen*(17%interleave), \ len, coefficients + region, 0, NULL \ ); \ region += x; \ break REMAINING_CASES; #undef CASE default: HEDLEY_UNREACHABLE(); } remaining %= interleave; } HEDLEY_ASSUME(remaining < interleave); unsigned lastInterleave = inputPackSize - region > interleave ? interleave : inputPackSize - region; switch(remaining) { #define CASE(x) \ case x: \ HEDLEY_ASSUME(x < interleave); \ HEDLEY_ASSUME(x <= lastInterleave); \ srcEnd = _src + region * len + len*lastInterleave; \ muladd_pf( \ scratch, _dst, lastInterleave, x, \ srcEnd, \ srcEnd + blockLen* 1, \ srcEnd + blockLen* 2, \ srcEnd + blockLen* 3, \ srcEnd + blockLen* 4, \ srcEnd + blockLen* 5, \ srcEnd + blockLen* 6, \ srcEnd + blockLen* 7, \ srcEnd + blockLen* 8, \ srcEnd + blockLen* 9, \ srcEnd + blockLen*10, \ srcEnd + blockLen*11, \ srcEnd + blockLen*12, \ srcEnd + blockLen*13, \ srcEnd + blockLen*14, \ srcEnd + blockLen*15, \ srcEnd + blockLen*16, \ srcEnd + blockLen*17, \ len, coefficients + region, 0, NULL \ ); \ break REMAINING_CASES; #undef CASE default: break; } } #if defined(__ICC) || (defined(_MSC_VER) && !defined(__clang__)) || !defined(_MM_HINT_ET1) # define MM_HINT_WT1 _MM_HINT_T1 #else # define MM_HINT_WT1 _MM_HINT_ET1 #endif static HEDLEY_ALWAYS_INLINE void gf16_muladd_multi_packpf(const void *HEDLEY_RESTRICT scratch, fMuladdPF muladd_pf, const unsigned interleave, unsigned regionsPerCall, unsigned inputPackSize, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, size_t blockLen, const uint16_t *HEDLEY_RESTRICT coefficients, const unsigned pfFactor, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) IGNORE_NULL_ADD { ASSUME(regions <= inputPackSize); uint8_t* _dst = (uint8_t*)dst + len; const uint8_t* _src = (const uint8_t*)src; const uint8_t* srcEnd; unsigned region = 0; size_t pfLen = len>>pfFactor; const char* _pf = (const char*)prefetchOut + pfLen; unsigned outputPfRounds = 1<= regionsPerCall) { while(outputPfRounds--) { srcEnd = _src + region * len + len*interleave; muladd_pf( scratch, _dst, interleave, regionsPerCall, srcEnd, srcEnd + len*interleave*( 1/interleave) + blockLen*( 1%interleave), srcEnd + len*interleave*( 2/interleave) + blockLen*( 2%interleave), srcEnd + len*interleave*( 3/interleave) + blockLen*( 3%interleave), srcEnd + len*interleave*( 4/interleave) + blockLen*( 4%interleave), srcEnd + len*interleave*( 5/interleave) + blockLen*( 5%interleave), srcEnd + len*interleave*( 6/interleave) + blockLen*( 6%interleave), srcEnd + len*interleave*( 7/interleave) + blockLen*( 7%interleave), srcEnd + len*interleave*( 8/interleave) + blockLen*( 8%interleave), srcEnd + len*interleave*( 9/interleave) + blockLen*( 9%interleave), srcEnd + len*interleave*(10/interleave) + blockLen*(10%interleave), srcEnd + len*interleave*(11/interleave) + blockLen*(11%interleave), srcEnd + len*interleave*(12/interleave) + blockLen*(12%interleave), srcEnd + len*interleave*(13/interleave) + blockLen*(13%interleave), srcEnd + len*interleave*(14/interleave) + blockLen*(14%interleave), srcEnd + len*interleave*(15/interleave) + blockLen*(15%interleave), srcEnd + len*interleave*(16/interleave) + blockLen*(16%interleave), srcEnd + len*interleave*(17/interleave) + blockLen*(17%interleave), len, coefficients + region, 1, _pf ); region += regionsPerCall; if(outputPfRounds) _pf += pfLen; else _pf = NULL; if(regionsPerCall > regions - region) break; } } if(_pf && regionsPerCall > regions - region) { unsigned remaining = regions - region; if(regionsPerCall > interleave && remaining >= interleave) { srcEnd = _src + region * len + len*interleave; switch(remaining - (remaining % interleave)) { #define CASE(x) \ case x: \ muladd_pf( \ scratch, _dst, interleave, x, \ srcEnd, \ srcEnd + len*interleave*( 1/interleave) + blockLen*( 1%interleave), \ srcEnd + len*interleave*( 2/interleave) + blockLen*( 2%interleave), \ srcEnd + len*interleave*( 3/interleave) + blockLen*( 3%interleave), \ srcEnd + len*interleave*( 4/interleave) + blockLen*( 4%interleave), \ srcEnd + len*interleave*( 5/interleave) + blockLen*( 5%interleave), \ srcEnd + len*interleave*( 6/interleave) + blockLen*( 6%interleave), \ srcEnd + len*interleave*( 7/interleave) + blockLen*( 7%interleave), \ srcEnd + len*interleave*( 8/interleave) + blockLen*( 8%interleave), \ srcEnd + len*interleave*( 9/interleave) + blockLen*( 9%interleave), \ srcEnd + len*interleave*(10/interleave) + blockLen*(10%interleave), \ srcEnd + len*interleave*(11/interleave) + blockLen*(11%interleave), \ srcEnd + len*interleave*(12/interleave) + blockLen*(12%interleave), \ srcEnd + len*interleave*(13/interleave) + blockLen*(13%interleave), \ srcEnd + len*interleave*(14/interleave) + blockLen*(14%interleave), \ srcEnd + len*interleave*(15/interleave) + blockLen*(15%interleave), \ srcEnd + len*interleave*(16/interleave) + blockLen*(16%interleave), \ srcEnd + len*interleave*(17/interleave) + blockLen*(17%interleave), \ len, coefficients + region, 1, _pf \ ); \ region += x; \ break REMAINING_CASES; #undef CASE default: HEDLEY_UNREACHABLE(); } remaining %= interleave; if(--outputPfRounds) _pf += pfLen; else _pf = NULL; } if(_pf) { unsigned lastInterleave = inputPackSize - region > interleave ? interleave : inputPackSize - region; switch(remaining) { #define CASE(x) \ case x: \ HEDLEY_ASSUME(x <= interleave); \ HEDLEY_ASSUME(x <= lastInterleave); \ srcEnd = _src + region * len + len*lastInterleave; \ muladd_pf( \ scratch, _dst, lastInterleave, x, \ srcEnd, \ srcEnd + blockLen* 1, \ srcEnd + blockLen* 2, \ srcEnd + blockLen* 3, \ srcEnd + blockLen* 4, \ srcEnd + blockLen* 5, \ srcEnd + blockLen* 6, \ srcEnd + blockLen* 7, \ srcEnd + blockLen* 8, \ srcEnd + blockLen* 9, \ srcEnd + blockLen*10, \ srcEnd + blockLen*11, \ srcEnd + blockLen*12, \ srcEnd + blockLen*13, \ srcEnd + blockLen*14, \ srcEnd + blockLen*15, \ srcEnd + blockLen*16, \ srcEnd + blockLen*17, \ len, coefficients + region, 1, _pf \ ); \ region += x; \ break REMAINING_CASES; #undef CASE default: break; } return; } // TODO: consider handling prefetchIn? } if(prefetchIn) { _pf = (const char*)prefetchIn + pfLen; while(regionsPerCall <= regions - region) { srcEnd = _src + region * len + len*interleave; muladd_pf( scratch, _dst, interleave, regionsPerCall, srcEnd, srcEnd + len*interleave*( 1/interleave) + blockLen*( 1%interleave), srcEnd + len*interleave*( 2/interleave) + blockLen*( 2%interleave), srcEnd + len*interleave*( 3/interleave) + blockLen*( 3%interleave), srcEnd + len*interleave*( 4/interleave) + blockLen*( 4%interleave), srcEnd + len*interleave*( 5/interleave) + blockLen*( 5%interleave), srcEnd + len*interleave*( 6/interleave) + blockLen*( 6%interleave), srcEnd + len*interleave*( 7/interleave) + blockLen*( 7%interleave), srcEnd + len*interleave*( 8/interleave) + blockLen*( 8%interleave), srcEnd + len*interleave*( 9/interleave) + blockLen*( 9%interleave), srcEnd + len*interleave*(10/interleave) + blockLen*(10%interleave), srcEnd + len*interleave*(11/interleave) + blockLen*(11%interleave), srcEnd + len*interleave*(12/interleave) + blockLen*(12%interleave), srcEnd + len*interleave*(13/interleave) + blockLen*(13%interleave), srcEnd + len*interleave*(14/interleave) + blockLen*(14%interleave), srcEnd + len*interleave*(15/interleave) + blockLen*(15%interleave), srcEnd + len*interleave*(16/interleave) + blockLen*(16%interleave), srcEnd + len*interleave*(17/interleave) + blockLen*(17%interleave), len, coefficients + region, 2, _pf ); region += regionsPerCall; _pf += pfLen; } } else while(regionsPerCall <= regions - region) { srcEnd = _src + region * len + len*interleave; muladd_pf( scratch, _dst, interleave, regionsPerCall, srcEnd, srcEnd + len*interleave*( 1/interleave) + blockLen*( 1%interleave), srcEnd + len*interleave*( 2/interleave) + blockLen*( 2%interleave), srcEnd + len*interleave*( 3/interleave) + blockLen*( 3%interleave), srcEnd + len*interleave*( 4/interleave) + blockLen*( 4%interleave), srcEnd + len*interleave*( 5/interleave) + blockLen*( 5%interleave), srcEnd + len*interleave*( 6/interleave) + blockLen*( 6%interleave), srcEnd + len*interleave*( 7/interleave) + blockLen*( 7%interleave), srcEnd + len*interleave*( 8/interleave) + blockLen*( 8%interleave), srcEnd + len*interleave*( 9/interleave) + blockLen*( 9%interleave), srcEnd + len*interleave*(10/interleave) + blockLen*(10%interleave), srcEnd + len*interleave*(11/interleave) + blockLen*(11%interleave), srcEnd + len*interleave*(12/interleave) + blockLen*(12%interleave), srcEnd + len*interleave*(13/interleave) + blockLen*(13%interleave), srcEnd + len*interleave*(14/interleave) + blockLen*(14%interleave), srcEnd + len*interleave*(15/interleave) + blockLen*(15%interleave), srcEnd + len*interleave*(16/interleave) + blockLen*(16%interleave), srcEnd + len*interleave*(17/interleave) + blockLen*(17%interleave), len, coefficients + region, 0, NULL ); region += regionsPerCall; } unsigned remaining = regions - region; HEDLEY_ASSUME(remaining < regionsPerCall); if(regionsPerCall > interleave && remaining >= interleave) { srcEnd = _src + region * len + len*interleave; regionsPerCall = remaining - (remaining % interleave); switch(regionsPerCall) { #define CASE(x) \ case x: \ muladd_pf( \ scratch, _dst, interleave, x, \ srcEnd, \ srcEnd + len*interleave*( 1/interleave) + blockLen*( 1%interleave), \ srcEnd + len*interleave*( 2/interleave) + blockLen*( 2%interleave), \ srcEnd + len*interleave*( 3/interleave) + blockLen*( 3%interleave), \ srcEnd + len*interleave*( 4/interleave) + blockLen*( 4%interleave), \ srcEnd + len*interleave*( 5/interleave) + blockLen*( 5%interleave), \ srcEnd + len*interleave*( 6/interleave) + blockLen*( 6%interleave), \ srcEnd + len*interleave*( 7/interleave) + blockLen*( 7%interleave), \ srcEnd + len*interleave*( 8/interleave) + blockLen*( 8%interleave), \ srcEnd + len*interleave*( 9/interleave) + blockLen*( 9%interleave), \ srcEnd + len*interleave*(10/interleave) + blockLen*(10%interleave), \ srcEnd + len*interleave*(11/interleave) + blockLen*(11%interleave), \ srcEnd + len*interleave*(12/interleave) + blockLen*(12%interleave), \ srcEnd + len*interleave*(13/interleave) + blockLen*(13%interleave), \ srcEnd + len*interleave*(14/interleave) + blockLen*(14%interleave), \ srcEnd + len*interleave*(15/interleave) + blockLen*(15%interleave), \ srcEnd + len*interleave*(16/interleave) + blockLen*(16%interleave), \ srcEnd + len*interleave*(17/interleave) + blockLen*(17%interleave), \ len, coefficients + region, 0, NULL \ ); \ region += x; \ break REMAINING_CASES; #undef CASE default: HEDLEY_UNREACHABLE(); } remaining %= interleave; } HEDLEY_ASSUME(remaining < interleave); unsigned lastInterleave = inputPackSize - region > interleave ? interleave : inputPackSize - region; switch(remaining) { #define CASE(x) \ case x: \ HEDLEY_ASSUME(x <= interleave); \ HEDLEY_ASSUME(x <= lastInterleave); \ srcEnd = _src + region * len + len*lastInterleave; \ muladd_pf( \ scratch, _dst, lastInterleave, x, \ srcEnd, \ srcEnd + blockLen* 1, \ srcEnd + blockLen* 2, \ srcEnd + blockLen* 3, \ srcEnd + blockLen* 4, \ srcEnd + blockLen* 5, \ srcEnd + blockLen* 6, \ srcEnd + blockLen* 7, \ srcEnd + blockLen* 8, \ srcEnd + blockLen* 9, \ srcEnd + blockLen*10, \ srcEnd + blockLen*11, \ srcEnd + blockLen*12, \ srcEnd + blockLen*13, \ srcEnd + blockLen*14, \ srcEnd + blockLen*15, \ srcEnd + blockLen*16, \ srcEnd + blockLen*17, \ len, coefficients + region, 0, NULL \ ); \ region += x; \ break REMAINING_CASES; #undef CASE default: break; } } #undef REMAINING_CASES #undef IGNORE_NULL_ADD par2cmdline-turbo-1.4.0/parpar/gf16/gf16_neon_common.h000066400000000000000000000112021514221355600224000ustar00rootroot00000000000000#ifndef __GF16_NEON_COMMON_H #define __GF16_NEON_COMMON_H #include "gf16_global.h" #include "../src/platform.h" #if defined(__ARM_NEON) // headers # include # include "gf16_checksum_arm.h" # if defined(_M_ARM64) && !defined(__clang__) /* MSVC header */ # include # endif // V/TBL differences # ifndef __aarch64__ # define vqtbl1q_u8(tbl, v) vcombine_u8(vtbl2_u8(tbl, vget_low_u8(v)), \ vtbl2_u8(tbl, vget_high_u8(v))) typedef uint8x8x2_t qtbl_t; # else typedef uint8x16_t qtbl_t; # endif // aligned loads # if defined(_MSC_VER) && !defined(__clang__) # define vld1_u8_align(p, a) vld1_u8_ex(p, a*8) # define vld1q_u8_align(p, a) vld1q_u8_ex(p, a*8) # define vst1q_u8_align(p, v, a) vst1q_u8_ex(p, v, a*8) # elif defined(__GNUC__) # define vld1_u8_align(p, n) vld1_u8((uint8_t*)__builtin_assume_aligned(p, n)) # define vld1q_u8_align(p, n) vld1q_u8((uint8_t*)__builtin_assume_aligned(p, n)) # define vst1q_u8_align(p, d, n) vst1q_u8((uint8_t*)__builtin_assume_aligned(p, n), d) # else # define vld1_u8_align(p, n) vld1_u8(p) # define vld1q_u8_align(p, n) vld1q_u8(p) # define vst1q_u8_align(p, d, n) vst1q_u8(p) # endif // for compilers that lack these functions // (Clang armv7 crashes with this on versions 9-12) # if (defined(__clang__) && (defined(__aarch64__) || __clang_major__<9 || __clang_major__>12)) || (HEDLEY_GCC_VERSION_CHECK(8,5,0) && defined(__aarch64__)) || HEDLEY_GCC_VERSION_CHECK(14,0,0) # define vld1q_u8_x2_align(p) vld1q_u8_x2((uint8_t*)__builtin_assume_aligned(p, 32)) # define vst1q_u8_x2_align(p, data) vst1q_u8_x2((uint8_t*)__builtin_assume_aligned(p, 32), data) // Clang armv7 always incorrectly assumes 32 byte alignment with these # if !defined(__clang__) || defined(__aarch64__) # define _vld1q_u8_x2 vld1q_u8_x2 # define _vst1q_u8_x2 vst1q_u8_x2 # endif # else static HEDLEY_ALWAYS_INLINE uint8x16x2_t vld1q_u8_x2_align(const uint8_t* p) { uint8x16x2_t r; r.val[0] = vld1q_u8_align(p, 16); r.val[1] = vld1q_u8_align(p+16, 16); return r; } static HEDLEY_ALWAYS_INLINE void vst1q_u8_x2_align(uint8_t* p, uint8x16x2_t data) { vst1q_u8_align(p, data.val[0], 16); vst1q_u8_align(p+16, data.val[1], 16); } # endif # ifndef _vld1q_u8_x2 static HEDLEY_ALWAYS_INLINE uint8x16x2_t _vld1q_u8_x2(const uint8_t* p) { uint8x16x2_t r; r.val[0] = vld1q_u8(p); r.val[1] = vld1q_u8(p+16); return r; } # endif # ifndef _vst1q_u8_x2 static HEDLEY_ALWAYS_INLINE void _vst1q_u8_x2(uint8_t* p, uint8x16x2_t data) { vst1q_u8(p, data.val[0]); vst1q_u8(p+16, data.val[1]); } # endif // ARM provides no standard way to inline define a vector :( static HEDLEY_ALWAYS_INLINE uint8x16_t vmakeq_u8( uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g, uint8_t h, uint8_t i, uint8_t j, uint8_t k, uint8_t l, uint8_t m, uint8_t n, uint8_t o, uint8_t p ) { # if defined(_MSC_VER) || __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ uint8_t t[] = {a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}; return vld1q_u8(t); # else // compilers might reverse the order of the following in big endian, for some reason return (uint8x16_t){a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p}; # endif } // cacheline prefetching // CACHELINE_SIZE must be >= 32 # ifdef __aarch64__ # define CACHELINE_SIZE 64 // do all AArch64 processors have cacheline>=64? # else # define CACHELINE_SIZE 32 // Cortex A7? # endif # if defined(_MSC_VER) && !defined(__clang__) # define PREFETCH_MEM(addr, rw) __prefetch(addr) // TODO: ARM64 intrin is a little different # else # define PREFETCH_MEM(addr, rw) __builtin_prefetch(addr, rw, 2) # endif // copying prepare block for both shuffle/clmul static HEDLEY_ALWAYS_INLINE void gf16_prepare_block_neon(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { #if defined(__clang__) && !(defined(__aarch64__) || __clang_major__<9 || __clang_major__>12) // slightly more efficent than the latter, if we have the Clang crash workaround in place vst2q_u8((uint8_t*)__builtin_assume_aligned(dst, 32), vld2q_u8((uint8_t*)src)); #else vst1q_u8_x2_align(dst, _vld1q_u8_x2(src)); #endif } // final block static HEDLEY_ALWAYS_INLINE void gf16_prepare_blocku_neon(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t remaining) { memcpy(dst, src, remaining); memset((uint8_t*)dst + remaining, 0, sizeof(uint8x16x2_t) - remaining); } static HEDLEY_ALWAYS_INLINE void gf16_finish_block_neon(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { #if defined(__clang__) && !(defined(__aarch64__) || __clang_major__<9 || __clang_major__>12) vst2q_u8((uint8_t*)dst, vld2q_u8((uint8_t*)__builtin_assume_aligned(src, 32))); #else _vst1q_u8_x2(dst, vld1q_u8_x2_align(src)); #endif } #endif #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_rvv_common.h000066400000000000000000000013731514221355600222660ustar00rootroot00000000000000#ifndef __GF16_RVV_COMMON_H #define __GF16_RVV_COMMON_H #include "gf16_global.h" #include "../src/platform.h" #if defined(__riscv_vector) # include # if defined(__clang__) && __clang_major__ < 16 # define RV(f) f # else # define RV(f) __riscv_##f # endif // TODO: evaluate endian requirements # if __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ # define __RVV_LE # endif static HEDLEY_ALWAYS_INLINE vint16m1_t gf16_vec_mul2_rvv(vint16m1_t v) { size_t vl = RV(vsetvlmax_e16m1)(); vbool16_t maskPoly = RV(vmslt_vx_i16m1_b16)(v, 0, vl); v = RV(vadd_vv_i16m1)(v, v, vl); #ifdef __riscv_v_intrinsic return RV(vxor_vx_i16m1_mu) #else return RV(vxor_vx_i16m1_m) #endif ( maskPoly, v, v, GF16_POLYNOMIAL & 0xffff, vl ); } #endif #endifpar2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle.h000066400000000000000000000326161514221355600215410ustar00rootroot00000000000000 #include "../src/hedley.h" // basic #define FUNCS(v) \ void gf16_shuffle_prepare_packed_cksum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); \ void gf16_shuffle_prepare_partial_packsum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen, size_t partOffset, size_t partLen); \ int gf16_shuffle_finish_packed_cksum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen); \ int gf16_shuffle_finish_partial_packsum_##v(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen, size_t partOffset, size_t partLen); \ void gf16_shuffle_muladd_##v(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); \ void gf16_shuffle_muladd_prefetch_##v(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch); \ extern int gf16_shuffle_available_##v FUNCS(ssse3); FUNCS(avx); FUNCS(avx2); FUNCS(avx512); #undef FUNCS #ifdef PARPAR_INVERT_SUPPORT #define FUNCS(v) \ void gf16_shuffle_prepare_##v(void* dst, const void* src, size_t srcLen); \ void gf16_shuffle_finish_##v(void *HEDLEY_RESTRICT dst, size_t len); \ void gf16_shuffle_mul_##v(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) FUNCS(ssse3); FUNCS(avx); FUNCS(avx2); FUNCS(avx512); #undef FUNCS #endif #ifdef PARPAR_INCLUDE_BASIC_OPS #define FUNCS(v) \ void gf16_shuffle_prepare_packed_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); \ void gf16_shuffle_finish_packed_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen) FUNCS(ssse3); FUNCS(avx); FUNCS(avx2); FUNCS(avx512); #undef FUNCS #endif // multi-region #define FUNCS(v) \ void gf16_shuffle_muladd_multi_packed_##v(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); \ void gf16_shuffle_muladd_multi_packpf_##v(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) FUNCS(avx512); FUNCS(vbmi); FUNCS(neon); FUNCS(128_sve); FUNCS(128_sve2); FUNCS(512_sve2); FUNCS(128_rvv); #undef FUNCS #ifdef PARPAR_INVERT_SUPPORT #define FUNCS(v) \ void gf16_shuffle_muladd_multi_##v(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); \ void gf16_shuffle_muladd_multi_stridepf_##v(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t srcStride, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetch) FUNCS(avx512); FUNCS(vbmi); FUNCS(neon); FUNCS(128_sve); FUNCS(128_sve2); FUNCS(512_sve2); FUNCS(128_rvv); #undef FUNCS #endif void gf16_shuffle_muladd_vbmi(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); void gf16_shuffle_muladd_prefetch_vbmi(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch); extern int gf16_shuffle_available_vbmi; #define FUNCS(v) \ void gf16_shuffle_muladd_##v(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) FUNCS(neon); FUNCS(128_sve); FUNCS(128_sve2); FUNCS(512_sve2); FUNCS(128_rvv); #undef FUNCS #ifdef PARPAR_INVERT_SUPPORT #define FUNCS(v) \ void gf16_shuffle_mul_##v(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) FUNCS(neon); FUNCS(128_sve); FUNCS(128_sve2); FUNCS(512_sve2); FUNCS(128_rvv); FUNCS(vbmi); #undef FUNCS #endif #ifdef PARPAR_INCLUDE_BASIC_OPS #define FUNCS(v) \ void gf16_shuffle_prepare_packed_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); \ void gf16_shuffle_prepare_packed_cksum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); \ void gf16_shuffle_prepare_partial_packsum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen, size_t partOffset, size_t partLen) #else #define FUNCS(v) \ void gf16_shuffle_prepare_packed_cksum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); \ void gf16_shuffle_prepare_partial_packsum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen, size_t partOffset, size_t partLen) #endif FUNCS(vbmi); FUNCS(neon); FUNCS(sve); FUNCS(512_sve2); FUNCS(rvv); #undef FUNCS #define FUNCS(v) \ int gf16_shuffle_finish_packed_cksum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen); \ int gf16_shuffle_finish_partial_packsum_##v(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen, size_t partOffset, size_t partLen) FUNCS(neon); FUNCS(sve); FUNCS(rvv); #undef FUNCS #ifdef PARPAR_INCLUDE_BASIC_OPS #define FUNCS(v) \ void gf16_shuffle_finish_packed_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen) FUNCS(neon); FUNCS(sve); FUNCS(rvv); #undef FUNCS #endif // also used for clmul, but declared here for convenience extern int gf16_available_neon; extern int gf16_available_sve; extern int gf16_available_sve2; extern int gf16_available_rvv; // shuffle2x #define FUNCS(v) \ void gf16_shuffle2x_prepare_packed_cksum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); \ void gf16_shuffle2x_prepare_partial_packsum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen, size_t partOffset, size_t partLen); \ int gf16_shuffle2x_finish_packed_cksum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen); \ int gf16_shuffle2x_finish_partial_packsum_##v(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen, size_t partOffset, size_t partLen); \ void gf16_shuffle2x_muladd_##v(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); \ void gf16_shuffle2x_muladd_multi_packed_##v(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); \ void gf16_shuffle2x_muladd_multi_packpf_##v(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) FUNCS(avx2); FUNCS(avx512); #undef FUNCS #ifdef PARPAR_INVERT_SUPPORT #define FUNCS(v) \ void gf16_shuffle2x_mul_##v(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); \ void gf16_shuffle2x_prepare_##v(void* dst, const void* src, size_t srcLen); \ void gf16_shuffle2x_finish_##v(void *HEDLEY_RESTRICT dst, size_t len); \ void gf16_shuffle2x_muladd_multi_##v(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); \ void gf16_shuffle2x_muladd_multi_stridepf_##v(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t srcStride, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetch) FUNCS(avx2); FUNCS(avx512); #undef FUNCS #endif #ifdef PARPAR_INCLUDE_BASIC_OPS #define FUNCS(v) \ void gf16_shuffle2x_prepare_packed_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); \ void gf16_shuffle2x_finish_packed_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen) FUNCS(avx2); FUNCS(avx512); FUNCS(sve); #undef FUNCS #endif void gf16_shuffle2x_prepare_packed_cksum_sve(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); void gf16_shuffle2x_prepare_partial_packsum_sve(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen, size_t partOffset, size_t partLen); int gf16_shuffle2x_finish_packed_cksum_sve(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen); int gf16_shuffle2x_finish_partial_packsum_sve(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen, size_t partOffset, size_t partLen); void gf16_shuffle2x_muladd_128_sve2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); void gf16_shuffle2x_muladd_multi_packed_128_sve2(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); void gf16_shuffle2x_muladd_multi_packpf_128_sve2(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut); #ifdef PARPAR_INVERT_SUPPORT void gf16_shuffle2x_mul_128_sve2(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); void gf16_shuffle2x_muladd_multi_128_sve2(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); void gf16_shuffle2x_muladd_multi_stridepf_128_sve2(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t srcStride, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetch); #endif void* gf16_shuffle_init_x86(int polynomial); void* gf16_shuffle_init_vbmi(int polynomial); void* gf16_shuffle_init_arm(int polynomial); void* gf16_shuffle_init_128_sve(int polynomial); void* gf16_shuffle_init_512_sve(int polynomial); void* gf16_shuffle_init_128_rvv(int polynomial); int gf16_sve_get_size(); int gf16_rvv_get_size(); #ifdef PARPAR_INVERT_SUPPORT uint16_t gf16_affine2x_replace_word(void* data, size_t index, uint16_t newValue); uint16_t gf16_shuffle16_replace_word(void* data, size_t index, uint16_t newValue); uint16_t gf16_shuffle32_replace_word(void* data, size_t index, uint16_t newValue); uint16_t gf16_shuffle64_replace_word(void* data, size_t index, uint16_t newValue); uint16_t gf16_shuffle2x16_replace_word(void* data, size_t index, uint16_t newValue); uint16_t gf16_shuffle2x32_replace_word(void* data, size_t index, uint16_t newValue); #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle128_rvv.c000066400000000000000000000240361514221355600226610ustar00rootroot00000000000000#include "gf16_rvv_common.h" #if defined(__RVV_LE) int gf16_available_rvv = 1; #else int gf16_available_rvv = 0; #endif #include "gf16_muladd_multi.h" #if defined(__RVV_LE) # if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 12000 // intrinsics v0.12.x static HEDLEY_ALWAYS_INLINE void _vlseg2e8(vuint8m1_t* v0, vuint8m1_t* v1, const uint8_t* src, size_t vl) { vuint8m1x2_t d = RV(vlseg2e8_v_u8m1x2)(src, vl); *v0 = RV(vget_v_u8m1x2_u8m1)(d, 0); *v1 = RV(vget_v_u8m1x2_u8m1)(d, 1); } static HEDLEY_ALWAYS_INLINE void _vsseg2e8(uint8_t* dst, vuint8m1_t v0, vuint8m1_t v1, size_t vl) { vuint8m1x2_t d = {}; // __riscv_vcreate_v_u8m1x2 missing in Clang 17 d = RV(vset_v_u8m1_u8m1x2)(d, 0, v0); d = RV(vset_v_u8m1_u8m1x2)(d, 1, v1); RV(vsseg2e8_v_u8m1x2)(dst, d, vl); } # else // intrinsics v0.11.x (up to at least GCC 13 / Clang 16) # define _vlseg2e8 RV(vlseg2e8_v_u8m1) # define _vsseg2e8 RV(vsseg2e8_v_u8m1) # endif static HEDLEY_ALWAYS_INLINE void gf16_shuffle_128_rvv_calc_table(vuint8m1_t poly_l, uint16_t val, vuint8m1_t* tbl_l0, vuint8m1_t* tbl_l1, vuint8m1_t* tbl_l2, vuint8m1_t* tbl_l3, vuint8m1_t* tbl_h0, vuint8m1_t* tbl_h1, vuint8m1_t* tbl_h2, vuint8m1_t* tbl_h3 ) { uint16_t val2 = GF16_MULTBY_TWO(val); uint16_t val4 = GF16_MULTBY_TWO(val2); uint16_t val8 = GF16_MULTBY_TWO(val4); vuint16m1_t tmp0 = RV(vmv_v_x_u16m1)(val ^ val2, 8); tmp0 = RV(vslide1up_vx_u16m1)(tmp0, val2, 8); tmp0 = RV(vslide1up_vx_u16m1)(tmp0, val, 8); tmp0 = RV(vslide1up_vx_u16m1)(tmp0, 0, 8); vuint16m1_t tmp4 = RV(vxor_vv_u16m1)(RV(vmv_v_x_u16m1)(val4, 8), tmp0, 8); tmp0 = RV(vslideup_vx_u16m1)(tmp0, tmp4, 4, 8); vuint16m1_t tmp8 = RV(vxor_vv_u16m1)(tmp0, RV(vmv_v_x_u16m1)(val8, 8), 8); vuint8mf2_t tmpL0, tmpL1, tmpH0, tmpH1; tmpL0 = RV(vnsrl_wx_u8mf2)(tmp0, 0, 8); tmpL1 = RV(vnsrl_wx_u8mf2)(tmp8, 0, 8); tmpH0 = RV(vnsrl_wx_u8mf2)(tmp0, 8, 8); tmpH1 = RV(vnsrl_wx_u8mf2)(tmp8, 8, 8); *tbl_l0 = RV(vslideup_vx_u8m1)(RV(vlmul_ext_v_u8mf2_u8m1)(tmpL0), RV(vlmul_ext_v_u8mf2_u8m1)(tmpL1), 8, 16); *tbl_h0 = RV(vslideup_vx_u8m1)(RV(vlmul_ext_v_u8mf2_u8m1)(tmpH0), RV(vlmul_ext_v_u8mf2_u8m1)(tmpH1), 8, 16); vuint8m1_t ri, rh, rl; // could replace the sll+or with a macc, but probably not worth it #define MUL16(p, c) \ ri = RV(vsrl_vx_u8m1)(*tbl_h##p, 4, 16); \ rl = RV(vsll_vx_u8m1)(*tbl_l##p, 4, 16); \ rh = RV(vxor_vv_u8m1)(*tbl_h##p, ri, 16); \ *tbl_l##c = RV(vxor_vv_u8m1)(rl, RV(vrgather_vv_u8m1)(poly_l, ri, 16), 16); \ *tbl_h##c = RV(vor_vv_u8m1)( \ RV(vsll_vx_u8m1)(rh, 4, 16), \ RV(vsrl_vx_u8m1)(*tbl_l##p, 4, 16), \ 16 \ ) MUL16(0, 1); MUL16(1, 2); MUL16(2, 3); #undef MUL16 } static HEDLEY_ALWAYS_INLINE void gf16_shuffle_128_rvv_round(size_t vl, vuint8m1_t src0, vuint8m1_t src1, vuint8m1_t* rl, vuint8m1_t* rh, vuint8m1_t tbl_l0, vuint8m1_t tbl_l1, vuint8m1_t tbl_l2, vuint8m1_t tbl_l3, vuint8m1_t tbl_h0, vuint8m1_t tbl_h1, vuint8m1_t tbl_h2, vuint8m1_t tbl_h3 ) { vuint8m1_t tmp = RV(vand_vx_u8m1)(src0, 0xf, vl); *rl = RV(vxor_vv_u8m1)(*rl, RV(vrgather_vv_u8m1)(tbl_l0, tmp, vl), vl); *rh = RV(vxor_vv_u8m1)(*rh, RV(vrgather_vv_u8m1)(tbl_h0, tmp, vl), vl); tmp = RV(vand_vx_u8m1)(src1, 0xf, vl); *rl = RV(vxor_vv_u8m1)(*rl, RV(vrgather_vv_u8m1)(tbl_l2, tmp, vl), vl); *rh = RV(vxor_vv_u8m1)(*rh, RV(vrgather_vv_u8m1)(tbl_h2, tmp, vl), vl); tmp = RV(vsrl_vx_u8m1)(src0, 4, vl); *rl = RV(vxor_vv_u8m1)(*rl, RV(vrgather_vv_u8m1)(tbl_l1, tmp, vl), vl); *rh = RV(vxor_vv_u8m1)(*rh, RV(vrgather_vv_u8m1)(tbl_h1, tmp, vl), vl); tmp = RV(vsrl_vx_u8m1)(src1, 4, vl); *rl = RV(vxor_vv_u8m1)(*rl, RV(vrgather_vv_u8m1)(tbl_l3, tmp, vl), vl); *rh = RV(vxor_vv_u8m1)(*rh, RV(vrgather_vv_u8m1)(tbl_h3, tmp, vl), vl); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle_muladd_x_128_rvv( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(3); vuint8m1_t poly_l = RV(vle8_v_u8m1)((const uint8_t*)scratch, 16); vuint8m1_t tbl_Ah0, tbl_Ah1, tbl_Ah2, tbl_Ah3, tbl_Al0, tbl_Al1, tbl_Al2, tbl_Al3; vuint8m1_t tbl_Bh0, tbl_Bh1, tbl_Bh2, tbl_Bh3, tbl_Bl0, tbl_Bl1, tbl_Bl2, tbl_Bl3; vuint8m1_t tbl_Ch0, tbl_Ch1, tbl_Ch2, tbl_Ch3, tbl_Cl0, tbl_Cl1, tbl_Cl2, tbl_Cl3; // TODO: support calcing multiple tables together #define CALC_TABLE(n, t) \ if(srcCount >= n) \ gf16_shuffle_128_rvv_calc_table( \ poly_l, coefficients[n], \ &tbl_##t##l0, &tbl_##t##l1, &tbl_##t##l2, &tbl_##t##l3, &tbl_##t##h0, &tbl_##t##h1, &tbl_##t##h2, &tbl_##t##h3 \ ) CALC_TABLE(0, A); CALC_TABLE(1, B); CALC_TABLE(2, C); #undef CALC_TABLE size_t vl = RV(vsetvlmax_e8m1)(); for(intptr_t ptr = -(intptr_t)len; ptr; ptr += vl*2) { // TODO: does RISC-V have prefetch instructions? UNUSED(doPrefetch); UNUSED(_pf); vuint8m1_t rl, rh; _vlseg2e8(&rl, &rh, _dst+ptr, vl*2); vuint8m1_t in0, in1; _vlseg2e8(&in0, &in1, _src1+ptr*srcScale, vl*2); gf16_shuffle_128_rvv_round(vl, in0, in1, &rl, &rh, tbl_Al0, tbl_Al1, tbl_Al2, tbl_Al3, tbl_Ah0, tbl_Ah1, tbl_Ah2, tbl_Ah3); if(srcCount > 1) { _vlseg2e8(&in0, &in1, _src2+ptr*srcScale, vl*2); gf16_shuffle_128_rvv_round(vl, in0, in1, &rl, &rh, tbl_Bl0, tbl_Bl1, tbl_Bl2, tbl_Bl3, tbl_Bh0, tbl_Bh1, tbl_Bh2, tbl_Bh3); } if(srcCount > 2) { _vlseg2e8(&in0, &in1, _src3+ptr*srcScale, vl*2); gf16_shuffle_128_rvv_round(vl, in0, in1, &rl, &rh, tbl_Cl0, tbl_Cl1, tbl_Cl2, tbl_Cl3, tbl_Ch0, tbl_Ch1, tbl_Ch2, tbl_Ch3); } _vsseg2e8(_dst+ptr, rl, rh, vl*2); } } #endif /*defined(__RVV_LE)*/ #ifdef PARPAR_INVERT_SUPPORT void gf16_shuffle_mul_128_rvv(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__RVV_LE) vuint8m1_t poly_l = RV(vle8_v_u8m1)((const uint8_t*)scratch, 16); vuint8m1_t tbl_h0, tbl_h1, tbl_h2, tbl_h3, tbl_l0, tbl_l1, tbl_l2, tbl_l3; gf16_shuffle_128_rvv_calc_table(poly_l, val, &tbl_l0, &tbl_l1, &tbl_l2, &tbl_l3, &tbl_h0, &tbl_h1, &tbl_h2, &tbl_h3); const uint8_t* _src = (const uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; size_t vl = RV(vsetvlmax_e8m1)(); for(intptr_t ptr = -(intptr_t)len; ptr; ptr += vl*2) { vuint8m1_t in0, in1; _vlseg2e8(&in0, &in1, _src+ptr, vl*2); vuint8m1_t tmp = RV(vand_vx_u8m1)(in0, 0xf, vl); vuint8m1_t rl = RV(vrgather_vv_u8m1)(tbl_l0, tmp, vl); vuint8m1_t rh = RV(vrgather_vv_u8m1)(tbl_h0, tmp, vl); tmp = RV(vand_vx_u8m1)(in1, 0xf, vl); rl = RV(vxor_vv_u8m1)(rl, RV(vrgather_vv_u8m1)(tbl_l2, tmp, vl), vl); rh = RV(vxor_vv_u8m1)(rh, RV(vrgather_vv_u8m1)(tbl_h2, tmp, vl), vl); tmp = RV(vsrl_vx_u8m1)(in0, 4, vl); rl = RV(vxor_vv_u8m1)(rl, RV(vrgather_vv_u8m1)(tbl_l1, tmp, vl), vl); rh = RV(vxor_vv_u8m1)(rh, RV(vrgather_vv_u8m1)(tbl_h1, tmp, vl), vl); tmp = RV(vsrl_vx_u8m1)(in1, 4, vl); rl = RV(vxor_vv_u8m1)(rl, RV(vrgather_vv_u8m1)(tbl_l3, tmp, vl), vl); rh = RV(vxor_vv_u8m1)(rh, RV(vrgather_vv_u8m1)(tbl_h3, tmp, vl), vl); _vsseg2e8(_dst+ptr, rl, rh, vl*2); } #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #endif void gf16_shuffle_muladd_128_rvv(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__RVV_LE) gf16_muladd_single(scratch, gf16_shuffle_muladd_x_128_rvv, dst, src, len, val); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #if defined(__RVV_LE) GF16_MULADD_MULTI_FUNCS(gf16_shuffle, _128_rvv, gf16_shuffle_muladd_x_128_rvv, 3, RV(vsetvlmax_e8m1)()*2, 0, (void)0) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_shuffle, _128_rvv) #endif #ifdef __RVV_LE static HEDLEY_ALWAYS_INLINE void gf16_prepare_block_rvv(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { size_t vl = RV(vsetvlmax_e8m2)(); RV(vse8_v_u8m2)((uint8_t*)dst, RV(vle8_v_u8m2)((const uint8_t*)src, vl), vl); } // final block static HEDLEY_ALWAYS_INLINE void gf16_prepare_blocku_rvv(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t remaining) { size_t vlmax = RV(vsetvlmax_e8m2)(); vuint8m2_t v = RV(vmv_v_x_u8m2)(0, vlmax); size_t vl = RV(vsetvl_e8m2)(remaining); #ifdef __riscv_v_intrinsic v = RV(vle8_v_u8m2_tu)(v, (const uint8_t*)src, vl); RV(vse8_v_u8m2)((uint8_t*)dst, v, vlmax); #else // tail-undisturbed not supported, so zero explicitly as a workaround RV(vse8_v_u8m2)((uint8_t*)dst, v, vlmax); RV(vse8_v_u8m2)((uint8_t*)dst, RV(vle8_v_u8m2)((const uint8_t*)src, vl), vl); #endif } static HEDLEY_ALWAYS_INLINE void gf16_finish_blocku_rvv(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t remaining) { size_t vl = RV(vsetvl_e8m2)(remaining); RV(vse8_v_u8m2)((uint8_t*)dst, RV(vle8_v_u8m2)((const uint8_t*)src, vl), vl); } #include "gf16_checksum_rvv.h" // TODO: should align be width of the vector, instead of 16? GF_PREPARE_PACKED_FUNCS(gf16_shuffle, _rvv, RV(vsetvlmax_e8m1)()*2, gf16_prepare_block_rvv, gf16_prepare_blocku_rvv, 3, (void)0, vuint16m1_t checksum = RV(vmv_v_x_u16m1)(0, RV(vsetvlmax_e16m1)()), gf16_checksum_block_rvv, gf16_checksum_blocku_rvv, gf16_checksum_exp_rvv, gf16_checksum_prepare_rvv, 16) GF_FINISH_PACKED_FUNCS(gf16_shuffle, _rvv, RV(vsetvlmax_e8m1)()*2, gf16_prepare_block_rvv, gf16_finish_blocku_rvv, 1, (void)0, gf16_checksum_block_rvv, gf16_checksum_blocku_rvv, gf16_checksum_exp_rvv, NULL, 16) #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_shuffle, _rvv) GF_FINISH_PACKED_FUNCS_STUB(gf16_shuffle, _rvv) #endif int gf16_rvv_get_size() { #ifdef __RVV_LE return RV(vsetvlmax_e8m1)(); #else return 0; #endif } void* gf16_shuffle_init_128_rvv(int polynomial) { #ifdef __RVV_LE uint8_t* ret; if((polynomial | 0x1f) != 0x1101f) return NULL; ALIGN_ALLOC(ret, 16, 16); for(int i=0; i<16; i++) { int p = 0; if(i & 8) p ^= polynomial << 3; if(i & 4) p ^= polynomial << 2; if(i & 2) p ^= polynomial << 1; if(i & 1) p ^= polynomial << 0; ret[i] = p & 0xff; } return ret; #else UNUSED(polynomial); return NULL; #endif } par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle128_sve.c000066400000000000000000000112171514221355600226360ustar00rootroot00000000000000 #include "gf16_sve_common.h" // only support our target polynomial #if defined(__ARM_FEATURE_SVE) && (GF16_POLYNOMIAL | 0x1f) == 0x1101f && __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ int gf16_available_sve = 1; #else int gf16_available_sve = 0; #endif #if defined(__ARM_FEATURE_SVE) static HEDLEY_ALWAYS_INLINE void gf16_shuffle128_sve_mul16_tables(svuint8_t polyIn, svuint64_t rl, svuint64_t rh, svuint8_t* tbl_l0, svuint8_t* tbl_l1, svuint8_t* tbl_l2, svuint8_t* tbl_l3, svuint8_t* tbl_h0, svuint8_t* tbl_h1, svuint8_t* tbl_h2, svuint8_t* tbl_h3 ) { *tbl_l0 = svtrn1_u8(svreinterpret_u8_u64(rl), svreinterpret_u8_u64(rh)); *tbl_h0 = svtrn2_u8(svreinterpret_u8_u64(rl), svreinterpret_u8_u64(rh)); svuint8_t ti, th, tl; #define MUL16(p, c) \ ti = NOMASK(svlsr_n_u8, *tbl_h##p, 4); \ tl = NOMASK(svlsl_n_u8, *tbl_l##p, 4); \ th = NOMASK(sveor_u8, *tbl_h##p, ti); \ th = NOMASK(svlsl_n_u8, th, 4); \ *tbl_h##c = NOMASK(svorr_u8, th, NOMASK(svlsr_n_u8, *tbl_l##p, 4)); \ *tbl_l##c = NOMASK(sveor_u8, tl, svtbl_u8(polyIn, ti)) MUL16(0, 1); MUL16(1, 2); MUL16(2, 3); #undef MUL16 } static HEDLEY_ALWAYS_INLINE void gf16_shuffle128_sve_round1(svuint8x2_t va, svuint8_t* rl, svuint8_t* rh, svuint8_t tbl_l0, svuint8_t tbl_l1, svuint8_t tbl_l2, svuint8_t tbl_l3, svuint8_t tbl_h0, svuint8_t tbl_h1, svuint8_t tbl_h2, svuint8_t tbl_h3 ) { svuint8_t tmp = NOMASK(svand_n_u8, svget2(va, 0), 0xf); *rl = svtbl_u8(tbl_l0, tmp); *rh = svtbl_u8(tbl_h0, tmp); tmp = NOMASK(svand_n_u8, svget2(va, 1), 0xf); *rl = NOMASK(sveor_u8, *rl, svtbl_u8(tbl_l2, tmp)); *rh = NOMASK(sveor_u8, *rh, svtbl_u8(tbl_h2, tmp)); tmp = NOMASK(svlsr_n_u8, svget2(va, 0), 4); *rl = NOMASK(sveor_u8, *rl, svtbl_u8(tbl_l1, tmp)); *rh = NOMASK(sveor_u8, *rh, svtbl_u8(tbl_h1, tmp)); tmp = NOMASK(svlsr_n_u8, svget2(va, 1), 4); *rl = NOMASK(sveor_u8, *rl, svtbl_u8(tbl_l3, tmp)); *rh = NOMASK(sveor_u8, *rh, svtbl_u8(tbl_h3, tmp)); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle128_sve_round(svuint8x2_t va, svuint8_t* rl, svuint8_t* rh, svuint8_t tbl_l0, svuint8_t tbl_l1, svuint8_t tbl_l2, svuint8_t tbl_l3, svuint8_t tbl_h0, svuint8_t tbl_h1, svuint8_t tbl_h2, svuint8_t tbl_h3 ) { svuint8_t tmp = NOMASK(svand_n_u8, svget2(va, 0), 0xf); *rl = NOMASK(sveor_u8, *rl, svtbl_u8(tbl_l0, tmp)); *rh = NOMASK(sveor_u8, *rh, svtbl_u8(tbl_h0, tmp)); tmp = NOMASK(svand_n_u8, svget2(va, 1), 0xf); *rl = NOMASK(sveor_u8, *rl, svtbl_u8(tbl_l2, tmp)); *rh = NOMASK(sveor_u8, *rh, svtbl_u8(tbl_h2, tmp)); tmp = NOMASK(svlsr_n_u8, svget2(va, 0), 4); *rl = NOMASK(sveor_u8, *rl, svtbl_u8(tbl_l1, tmp)); *rh = NOMASK(sveor_u8, *rh, svtbl_u8(tbl_h1, tmp)); tmp = NOMASK(svlsr_n_u8, svget2(va, 1), 4); *rl = NOMASK(sveor_u8, *rl, svtbl_u8(tbl_l3, tmp)); *rh = NOMASK(sveor_u8, *rh, svtbl_u8(tbl_h3, tmp)); } #define _AVAILABLE #endif /*defined(__ARM_FEATURE_SVE)*/ #define SVE_CALC_TABLE_MUL16(...) gf16_shuffle128_sve_mul16_tables(poly, __VA_ARGS__) #define SVE_CALC_TABLE_LOAD_SCRATCH(s) svuint8_t poly = svld1rq_u8(svptrue_b8(), s) #define SVE_ROUND1 gf16_shuffle128_sve_round1 #define SVE_ROUND gf16_shuffle128_sve_round #define _FNSUFFIX _128_sve #include "gf16_shuffle128_sve_common.h" #undef _FNSUFFIX #undef SVE_ROUND #undef SVE_ROUND1 #undef SVE_CALC_TABLE_LOAD_SCRATCH #undef SVE_CALC_TABLE_MUL16 #ifdef _AVAILABLE #undef _AVAILABLE #endif #if defined(__ARM_FEATURE_SVE) GF16_MULADD_MULTI_FUNCS(gf16_shuffle, _128_sve, gf16_shuffle_muladd_x_128_sve, 3, svcntb()*2, 0, (void)0) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_shuffle, _128_sve) #endif // checksum stuff #include "gf16_checksum_sve.h" #ifdef __ARM_FEATURE_SVE GF_PREPARE_PACKED_FUNCS(gf16_shuffle, _sve, svcntb()*2, gf16_prepare_block_sve, gf16_prepare_blocku_sve, 3, (void)0, svint16_t checksum = svdup_n_s16(0), gf16_checksum_block_sve, gf16_checksum_blocku_sve, gf16_checksum_exp_sve, gf16_checksum_prepare_sve, 16) GF_FINISH_PACKED_FUNCS(gf16_shuffle, _sve, svcntb()*2, gf16_prepare_block_sve, gf16_finish_blocku_sve, 1, (void)0, gf16_checksum_block_sve, gf16_checksum_blocku_sve, gf16_checksum_exp_sve, NULL, 16) #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_shuffle, _sve) GF_FINISH_PACKED_FUNCS_STUB(gf16_shuffle, _sve) #endif int gf16_sve_get_size() { #ifdef __ARM_FEATURE_SVE return svcntb(); #else return 0; #endif } void* gf16_shuffle_init_128_sve(int polynomial) { #ifdef __ARM_FEATURE_SVE uint8_t* ret; if((polynomial | 0x1f) != 0x1101f) return NULL; ALIGN_ALLOC(ret, svcntb(), 16); for(int i=0; i<16; i++) { int p = 0; if(i & 8) p ^= polynomial << 3; if(i & 4) p ^= polynomial << 2; if(i & 2) p ^= polynomial << 1; if(i & 1) p ^= polynomial << 0; ret[i] = p & 0xff; } return ret; #else UNUSED(polynomial); return NULL; #endif } par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle128_sve2.c000066400000000000000000000061641514221355600227250ustar00rootroot00000000000000 #include "gf16_sve_common.h" // only support our target polynomial #if defined(__ARM_FEATURE_SVE2) && (GF16_POLYNOMIAL | 0x1f) == 0x1101f && __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ int gf16_available_sve2 = 1; #else int gf16_available_sve2 = 0; #endif #if defined(__ARM_FEATURE_SVE2) static HEDLEY_ALWAYS_INLINE void gf16_shuffle128_sve2_mul16_tables(svuint64_t rl, svuint64_t rh, svuint8_t* tbl_l0, svuint8_t* tbl_l1, svuint8_t* tbl_l2, svuint8_t* tbl_l3, svuint8_t* tbl_h0, svuint8_t* tbl_h1, svuint8_t* tbl_h2, svuint8_t* tbl_h3 ) { *tbl_l0 = svtrn1_u8(svreinterpret_u8_u64(rl), svreinterpret_u8_u64(rh)); *tbl_h0 = svtrn2_u8(svreinterpret_u8_u64(rl), svreinterpret_u8_u64(rh)); svuint8_t ti, th, tl; #define MUL16(p, c) \ ti = NOMASK(svlsr_n_u8, *tbl_h##p, 4); \ tl = NOMASK(svlsl_n_u8, *tbl_l##p, 4); \ th = svxar_n_u8(*tbl_h##p, ti, 4); \ *tbl_h##c = svsri_n_u8(th, *tbl_l##p, 4); \ *tbl_l##c = NOMASK(sveor_u8, tl, svpmul_n_u8(ti, GF16_POLYNOMIAL & 0x1f)) MUL16(0, 1); MUL16(1, 2); MUL16(2, 3); #undef MUL16 } static HEDLEY_ALWAYS_INLINE void gf16_shuffle128_sve2_round1(svuint8x2_t va, svuint8_t* rl, svuint8_t* rh, svuint8_t tbl_l0, svuint8_t tbl_l1, svuint8_t tbl_l2, svuint8_t tbl_l3, svuint8_t tbl_h0, svuint8_t tbl_h1, svuint8_t tbl_h2, svuint8_t tbl_h3 ) { svuint8_t tmp1 = NOMASK(svand_n_u8, svget2(va, 0), 0xf); svuint8_t tmp2 = NOMASK(svlsr_n_u8, svget2(va, 0), 4); *rl = NOMASK(sveor_u8, svtbl_u8(tbl_l0, tmp1), svtbl_u8(tbl_l1, tmp2)); *rh = NOMASK(sveor_u8, svtbl_u8(tbl_h0, tmp1), svtbl_u8(tbl_h1, tmp2)); tmp1 = NOMASK(svand_n_u8, svget2(va, 1), 0xf); tmp2 = NOMASK(svlsr_n_u8, svget2(va, 1), 4); *rl = sveor3(*rl, svtbl_u8(tbl_l2, tmp1), svtbl_u8(tbl_l3, tmp2)); *rh = sveor3(*rh, svtbl_u8(tbl_h2, tmp1), svtbl_u8(tbl_h3, tmp2)); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle128_sve2_round(svuint8x2_t va, svuint8_t* rl, svuint8_t* rh, svuint8_t tbl_l0, svuint8_t tbl_l1, svuint8_t tbl_l2, svuint8_t tbl_l3, svuint8_t tbl_h0, svuint8_t tbl_h1, svuint8_t tbl_h2, svuint8_t tbl_h3 ) { svuint8_t tmp1 = NOMASK(svand_n_u8, svget2(va, 0), 0xf); svuint8_t tmp2 = NOMASK(svlsr_n_u8, svget2(va, 0), 4); *rl = sveor3(*rl, svtbl_u8(tbl_l0, tmp1), svtbl_u8(tbl_l1, tmp2)); *rh = sveor3(*rh, svtbl_u8(tbl_h0, tmp1), svtbl_u8(tbl_h1, tmp2)); tmp1 = NOMASK(svand_n_u8, svget2(va, 1), 0xf); tmp2 = NOMASK(svlsr_n_u8, svget2(va, 1), 4); *rl = sveor3(*rl, svtbl_u8(tbl_l2, tmp1), svtbl_u8(tbl_l3, tmp2)); *rh = sveor3(*rh, svtbl_u8(tbl_h2, tmp1), svtbl_u8(tbl_h3, tmp2)); } #define _AVAILABLE #endif /*defined(__ARM_FEATURE_SVE2)*/ #define SVE_CALC_TABLE_MUL16 gf16_shuffle128_sve2_mul16_tables #define SVE_ROUND1 gf16_shuffle128_sve2_round1 #define SVE_ROUND gf16_shuffle128_sve2_round #define _FNSUFFIX _128_sve2 #include "gf16_shuffle128_sve_common.h" #undef _FNSUFFIX #undef SVE_ROUND #undef SVE_ROUND1 #undef SVE_CALC_TABLE_MUL16 #ifdef _AVAILABLE #undef _AVAILABLE #endif #if defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16) GF16_MULADD_MULTI_FUNCS(gf16_shuffle, _128_sve2, gf16_shuffle_muladd_x_128_sve2, 3, svcntb()*2, 0, (void)0) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_shuffle, _128_sve2) #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle128_sve_common.h000066400000000000000000000200121514221355600242040ustar00rootroot00000000000000 #include "gf16_muladd_multi.h" #if defined(_AVAILABLE) && defined(SVE_CALC_TABLE_MUL16) static HEDLEY_ALWAYS_INLINE void gf16_shuffle128_sve_calc_tables(const void *HEDLEY_RESTRICT scratch, const unsigned srcCount, const uint16_t *HEDLEY_RESTRICT coefficients, svuint8_t* tbl_Al0, svuint8_t* tbl_Al1, svuint8_t* tbl_Al2, svuint8_t* tbl_Al3, svuint8_t* tbl_Ah0, svuint8_t* tbl_Ah1, svuint8_t* tbl_Ah2, svuint8_t* tbl_Ah3, svuint8_t* tbl_Bl0, svuint8_t* tbl_Bl1, svuint8_t* tbl_Bl2, svuint8_t* tbl_Bl3, svuint8_t* tbl_Bh0, svuint8_t* tbl_Bh1, svuint8_t* tbl_Bh2, svuint8_t* tbl_Bh3, svuint8_t* tbl_Cl0, svuint8_t* tbl_Cl1, svuint8_t* tbl_Cl2, svuint8_t* tbl_Cl3, svuint8_t* tbl_Ch0, svuint8_t* tbl_Ch1, svuint8_t* tbl_Ch2, svuint8_t* tbl_Ch3 ) { #ifdef SVE_CALC_TABLE_LOAD_SCRATCH SVE_CALC_TABLE_LOAD_SCRATCH(scratch); #else UNUSED(scratch); #endif svint16_t val1 = svld1_s16(svwhilelt_b16((uint32_t)0, (uint32_t)srcCount), (int16_t*)coefficients); svint16_t val2 = gf16_vec_mul2_sve(val1); svint16_t val4 = gf16_vec_mul2_sve(val2); svint16_t val8 = gf16_vec_mul2_sve(val4); // expand val1 and val8 so they can be EOR'd correctly svint16_t val8x = svtbl_s16(val8, NOMASK(svlsr_n_u16, svindex_u16(0, 1), 2)); // duplicate each lane 4 times svint16_t val1x = svtbl_s16(val1, NOMASK(svlsr_n_u16, svindex_u16(0, 1), 3)); // duplicate each lane 8 times svint16_t val04 = svzip1_s16(svdup_n_s16(0), val4); svint16_t val26 = NOMASK(sveor_s16, val04, svzip1_s16(val2, val2)); svint16_t val0246a = svzip1_s16(val04, val26); svint16_t val8ACEa = NOMASK(sveor_s16, val0246a, val8x); svuint64_t valEvenA = svzip1_u64( svreinterpret_u64_s16(val0246a), svreinterpret_u64_s16(val8ACEa) ); svuint64_t valOddA = NOMASK(sveor_u64, valEvenA, svreinterpret_u64_s16(val1x)); SVE_CALC_TABLE_MUL16(valEvenA, valOddA, tbl_Al0, tbl_Al1, tbl_Al2, tbl_Al3, tbl_Ah0, tbl_Ah1, tbl_Ah2, tbl_Ah3); #define EXTRACT_LANE(dst, src, lane) \ *dst##l0 = svdupq_lane_u8(*src##l0, lane); \ *dst##l1 = svdupq_lane_u8(*src##l1, lane); \ *dst##l2 = svdupq_lane_u8(*src##l2, lane); \ *dst##l3 = svdupq_lane_u8(*src##l3, lane); \ *dst##h0 = svdupq_lane_u8(*src##h0, lane); \ *dst##h1 = svdupq_lane_u8(*src##h1, lane); \ *dst##h2 = svdupq_lane_u8(*src##h2, lane); \ *dst##h3 = svdupq_lane_u8(*src##h3, lane) if(svcntb() >= srcCount*16 || srcCount == 1) { // all tables computed, just extract to separate registers if(srcCount > 1) { EXTRACT_LANE(tbl_B, tbl_A, 1); } if(srcCount > 2) { EXTRACT_LANE(tbl_C, tbl_A, 2); } } else { svuint64_t valEvenB = svzip2_u64( svreinterpret_u64_s16(val0246a), svreinterpret_u64_s16(val8ACEa) ); if(svcntb() >= srcCount*8) { if(srcCount > 2) { // implies vect-width=256, srcCount=3 svuint64_t valOddB = NOMASK(sveor_u64, valEvenB, svreinterpret_u64_s16(svdup_lane_s16(val1, 2))); SVE_CALC_TABLE_MUL16(valEvenB, valOddB, tbl_Cl0, tbl_Cl1, tbl_Cl2, tbl_Cl3, tbl_Ch0, tbl_Ch1, tbl_Ch2, tbl_Ch3); EXTRACT_LANE(tbl_B, tbl_A, 1); } else { // implies vect-width=128, srcCount=2 svuint64_t valOddB = NOMASK(sveor_u64, valEvenB, svreinterpret_u64_s16(svdup_lane_s16(val1, 1))); SVE_CALC_TABLE_MUL16(valEvenB, valOddB, tbl_Bl0, tbl_Bl1, tbl_Bl2, tbl_Bl3, tbl_Bh0, tbl_Bh1, tbl_Bh2, tbl_Bh3); } } else { // implies vect-width=128, srcCount=3 svuint64_t valOddB = NOMASK(sveor_u64, valEvenB, svreinterpret_u64_s16(svdup_lane_s16(val1, 1))); SVE_CALC_TABLE_MUL16(valEvenB, valOddB, tbl_Bl0, tbl_Bl1, tbl_Bl2, tbl_Bl3, tbl_Bh0, tbl_Bh1, tbl_Bh2, tbl_Bh3); svint16_t val0246b = svzip2_s16(val04, val26); svint16_t val8ACEb = NOMASK(sveor_s16, val0246b, svdup_lane_s16(val8, 2)); valEvenA = svzip1_u64( svreinterpret_u64_s16(val0246b), svreinterpret_u64_s16(val8ACEb) ); valOddA = NOMASK(sveor_u64, valEvenA, svreinterpret_u64_s16(svdup_lane_s16(val1, 2))); SVE_CALC_TABLE_MUL16(valEvenA, valOddA, tbl_Cl0, tbl_Cl1, tbl_Cl2, tbl_Cl3, tbl_Ch0, tbl_Ch1, tbl_Ch2, tbl_Ch3); } } #undef EXTRACT_LANE } static HEDLEY_ALWAYS_INLINE void gf16_shuffle128_sve_calc_single_table(const void *HEDLEY_RESTRICT scratch, uint16_t val, svuint8_t* tbl_l0, svuint8_t* tbl_l1, svuint8_t* tbl_l2, svuint8_t* tbl_l3, svuint8_t* tbl_h0, svuint8_t* tbl_h1, svuint8_t* tbl_h2, svuint8_t* tbl_h3 ) { #ifdef SVE_CALC_TABLE_LOAD_SCRATCH SVE_CALC_TABLE_LOAD_SCRATCH(scratch); #else UNUSED(scratch); #endif int val2 = GF16_MULTBY_TWO(val); int val4 = GF16_MULTBY_TWO(val2); int val8 = GF16_MULTBY_TWO(val4); svuint16_t tmp = svdupq_n_u16(0, val2, val4, val4^val2, 0, 0, 0, 0); svuint64_t rl = svzip1_u64( svreinterpret_u64_u16(tmp), svreinterpret_u64_u16(NOMASK(sveor_u16, tmp, svdup_n_u16(val8))) ); svuint64_t rh = svreinterpret_u64_u16(NOMASK(sveor_u16, svreinterpret_u16_u64(rl), svdup_n_u16(val) )); SVE_CALC_TABLE_MUL16(rl, rh, tbl_l0, tbl_l1, tbl_l2, tbl_l3, tbl_h0, tbl_h1, tbl_h2, tbl_h3); } static HEDLEY_ALWAYS_INLINE void _FN(gf16_shuffle_muladd_x)( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(3); svuint8_t tbl_Ah0, tbl_Ah1, tbl_Ah2, tbl_Ah3, tbl_Al0, tbl_Al1, tbl_Al2, tbl_Al3; svuint8_t tbl_Bh0, tbl_Bh1, tbl_Bh2, tbl_Bh3, tbl_Bl0, tbl_Bl1, tbl_Bl2, tbl_Bl3; svuint8_t tbl_Ch0, tbl_Ch1, tbl_Ch2, tbl_Ch3, tbl_Cl0, tbl_Cl1, tbl_Cl2, tbl_Cl3; gf16_shuffle128_sve_calc_tables(scratch, srcCount, coefficients, &tbl_Al0, &tbl_Al1, &tbl_Al2, &tbl_Al3, &tbl_Ah0, &tbl_Ah1, &tbl_Ah2, &tbl_Ah3, &tbl_Bl0, &tbl_Bl1, &tbl_Bl2, &tbl_Bl3, &tbl_Bh0, &tbl_Bh1, &tbl_Bh2, &tbl_Bh3, &tbl_Cl0, &tbl_Cl1, &tbl_Cl2, &tbl_Cl3, &tbl_Ch0, &tbl_Ch1, &tbl_Ch2, &tbl_Ch3 ); svuint8_t rl, rh; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += svcntb()*2) { if(doPrefetch == 1) { svprfb(svptrue_b8(), _pf+ptr, SV_PLDL1KEEP); svprfb_vnum(svptrue_b8(), _pf+ptr, 1, SV_PLDL1KEEP); } if(doPrefetch == 2) { svprfb(svptrue_b8(), _pf+ptr, SV_PLDL2KEEP); svprfb_vnum(svptrue_b8(), _pf+ptr, 1, SV_PLDL2KEEP); } svuint8x2_t vb = svld2_u8(svptrue_b8(), _dst+ptr); rl = svget2(vb, 0); rh = svget2(vb, 1); SVE_ROUND(svld2_u8(svptrue_b8(), _src1+ptr*srcScale), &rl, &rh, tbl_Al0, tbl_Al1, tbl_Al2, tbl_Al3, tbl_Ah0, tbl_Ah1, tbl_Ah2, tbl_Ah3); if(srcCount > 1) SVE_ROUND(svld2_u8(svptrue_b8(), _src2+ptr*srcScale), &rl, &rh, tbl_Bl0, tbl_Bl1, tbl_Bl2, tbl_Bl3, tbl_Bh0, tbl_Bh1, tbl_Bh2, tbl_Bh3); if(srcCount > 2) SVE_ROUND(svld2_u8(svptrue_b8(), _src3+ptr*srcScale), &rl, &rh, tbl_Cl0, tbl_Cl1, tbl_Cl2, tbl_Cl3, tbl_Ch0, tbl_Ch1, tbl_Ch2, tbl_Ch3); svst2_u8(svptrue_b8(), _dst+ptr, svcreate2_u8(rl, rh)); } } #endif /*defined(_AVAILABLE) && defined(SVE_CALC_TABLE_MUL16)*/ #ifdef PARPAR_INVERT_SUPPORT void _FN(gf16_shuffle_mul)(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(_AVAILABLE) && defined(SVE_CALC_TABLE_MUL16) svuint8_t tbl_h0, tbl_h1, tbl_h2, tbl_h3, tbl_l0, tbl_l1, tbl_l2, tbl_l3; gf16_shuffle128_sve_calc_single_table(scratch, val, &tbl_l0, &tbl_l1, &tbl_l2, &tbl_l3, &tbl_h0, &tbl_h1, &tbl_h2, &tbl_h3); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += svcntb()*2) { svuint8_t ra, rb; SVE_ROUND1(svld2_u8(svptrue_b8(), _src+ptr), &ra, &rb, tbl_l0, tbl_l1, tbl_l2, tbl_l3, tbl_h0, tbl_h1, tbl_h2, tbl_h3); svst2_u8(svptrue_b8(), _dst+ptr, svcreate2_u8(ra, rb)); } #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #endif void _FN(gf16_shuffle_muladd)(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(_AVAILABLE) gf16_muladd_single(scratch, &_FN(gf16_shuffle_muladd_x), dst, src, len, val); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle2x128_sve2.c000066400000000000000000000300401514221355600231650ustar00rootroot00000000000000 #include "gf16_sve_common.h" #include "gf16_muladd_multi.h" #if defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16) // emulate svzip1q_u8 without FP64MatMul feature static HEDLEY_ALWAYS_INLINE svuint8_t join_lane(svuint8_t a, svuint8_t b, int lane) { const svuint64_t tbl2base = svorr_n_u64_m( svnot_b_z(svptrue_b64(), svptrue_pat_b64(SV_VL2)), svdupq_n_u64(0, 1), svcntd() ); return svreinterpret_u8_u64(svtbl2_u64(svcreate2_u64( svreinterpret_u64_u8(a), svreinterpret_u64_u8(b) ), NOMASK(svadd_n_u64, tbl2base, lane*2 ))); } // copied from shuffle128_sve2 static HEDLEY_ALWAYS_INLINE void gf16_shuffle128_sve2_mul16_tables(svuint64_t rl, svuint64_t rh, svuint8_t* tbl_l0, svuint8_t* tbl_l1, svuint8_t* tbl_l2, svuint8_t* tbl_l3, svuint8_t* tbl_h0, svuint8_t* tbl_h1, svuint8_t* tbl_h2, svuint8_t* tbl_h3 ) { *tbl_l0 = svtrn1_u8(svreinterpret_u8_u64(rl), svreinterpret_u8_u64(rh)); *tbl_h0 = svtrn2_u8(svreinterpret_u8_u64(rl), svreinterpret_u8_u64(rh)); svuint8_t ti, th, tl; #define MUL16(p, c) \ ti = NOMASK(svlsr_n_u8, *tbl_h##p, 4); \ tl = NOMASK(svlsl_n_u8, *tbl_l##p, 4); \ th = svxar_n_u8(*tbl_h##p, ti, 4); \ *tbl_h##c = svsri_n_u8(th, *tbl_l##p, 4); \ *tbl_l##c = NOMASK(sveor_u8, tl, svpmul_n_u8(ti, GF16_POLYNOMIAL & 0x1f)) MUL16(0, 1); MUL16(1, 2); MUL16(2, 3); #undef MUL16 } static HEDLEY_ALWAYS_INLINE void gf16_shuffle2x128_sve2_calc_tables(const unsigned srcCount, const uint16_t *HEDLEY_RESTRICT coefficients, svuint8_t* tbl_Aln, svuint8_t* tbl_Als, svuint8_t* tbl_Ahn, svuint8_t* tbl_Ahs, svuint8_t* tbl_Bln, svuint8_t* tbl_Bls, svuint8_t* tbl_Bhn, svuint8_t* tbl_Bhs, svuint8_t* tbl_Cln, svuint8_t* tbl_Cls, svuint8_t* tbl_Chn, svuint8_t* tbl_Chs, svuint8_t* tbl_Dln, svuint8_t* tbl_Dls, svuint8_t* tbl_Dhn, svuint8_t* tbl_Dhs, svuint8_t* tbl_Eln, svuint8_t* tbl_Els, svuint8_t* tbl_Ehn, svuint8_t* tbl_Ehs, svuint8_t* tbl_Fln, svuint8_t* tbl_Fls, svuint8_t* tbl_Fhn, svuint8_t* tbl_Fhs ) { svint16_t val1 = svld1_s16(svwhilelt_b16((uint32_t)0, (uint32_t)srcCount), (int16_t*)coefficients); svint16_t val2 = gf16_vec_mul2_sve(val1); svint16_t val4 = gf16_vec_mul2_sve(val2); svint16_t val8 = gf16_vec_mul2_sve(val4); // expand val1 and val8 so they can be EOR'd correctly svint16_t val8x = svtbl_s16(val8, NOMASK(svlsr_n_u16, svindex_u16(0, 1), 2)); // duplicate each lane 4 times svuint16_t shufQidx = NOMASK(svlsr_n_u16, svindex_u16(0, 1), 3); svint16_t val1x = svtbl_s16(val1, shufQidx); // duplicate each lane 8 times svint16_t val04 = svzip1_s16(svdup_n_s16(0), val4); svint16_t val26 = NOMASK(sveor_s16, val04, svzip1_s16(val2, val2)); svint16_t val0246a = svzip1_s16(val04, val26); svint16_t val8ACEa = NOMASK(sveor_s16, val0246a, val8x); svuint64_t valEvenA = svzip1_u64( svreinterpret_u64_s16(val0246a), svreinterpret_u64_s16(val8ACEa) ); svuint64_t valOddA = NOMASK(sveor_u64, valEvenA, svreinterpret_u64_s16(val1x)); svuint8_t tbl_l0, tbl_l1, tbl_l2, tbl_l3, tbl_h0, tbl_h1, tbl_h2, tbl_h3; gf16_shuffle128_sve2_mul16_tables(valEvenA, valOddA, &tbl_l0, &tbl_l1, &tbl_l2, &tbl_l3, &tbl_h0, &tbl_h1, &tbl_h2, &tbl_h3); #define EXTRACT_LANE(dst, src, lane) \ *dst##ln = join_lane(src##l0, src##h2, lane); \ *dst##ls = join_lane(src##h0, src##l2, lane); \ *dst##hn = join_lane(src##l1, src##h3, lane); \ *dst##hs = join_lane(src##h1, src##l3, lane) EXTRACT_LANE(tbl_A, tbl_, 0); if(srcCount > 1) { EXTRACT_LANE(tbl_B, tbl_, 1); } if(svcntb() >= srcCount*16 || srcCount == 1) { if(srcCount > 2) { EXTRACT_LANE(tbl_C, tbl_, 2); } if(srcCount > 3) { EXTRACT_LANE(tbl_D, tbl_, 3); } if(srcCount > 4) { EXTRACT_LANE(tbl_E, tbl_, 4); } if(srcCount > 5) { EXTRACT_LANE(tbl_F, tbl_, 5); } } else { // implies srcCount >= 3 svuint64_t valEvenB = svzip2_u64( svreinterpret_u64_s16(val0246a), svreinterpret_u64_s16(val8ACEa) ); if(svcntb() >= srcCount*8) { svuint8_t tbl2_l0, tbl2_l1, tbl2_l2, tbl2_l3, tbl2_h0, tbl2_h1, tbl2_h2, tbl2_h3; val1x = svtbl_s16(val1, NOMASK(svadd_n_u16, shufQidx, svcntb()/16)); svuint64_t valOddB = NOMASK(sveor_u64, valEvenB, svreinterpret_u64_s16(val1x)); gf16_shuffle128_sve2_mul16_tables(valEvenB, valOddB, &tbl2_l0, &tbl2_l1, &tbl2_l2, &tbl2_l3, &tbl2_h0, &tbl2_h1, &tbl2_h2, &tbl2_h3); if(svcntb() < 48) { EXTRACT_LANE(tbl_C, tbl2_, 0); } else { EXTRACT_LANE(tbl_C, tbl_, 2); } if(srcCount >= 4) { if(svcntb() < 48) { EXTRACT_LANE(tbl_D, tbl2_, 1); } else if(svcntb() < 64) { EXTRACT_LANE(tbl_D, tbl2_, 0); } else { EXTRACT_LANE(tbl_D, tbl_, 3); } } if(srcCount >= 5) { // implies vect-width >= 384 if(svcntb() < 64) { EXTRACT_LANE(tbl_E, tbl2_, 1); } else if(svcntb() < 80) { EXTRACT_LANE(tbl_E, tbl2_, 0); } else { EXTRACT_LANE(tbl_E, tbl_, 4); } } if(srcCount >= 6) { // implies vect-width >= 384 if(svcntb() < 64) { EXTRACT_LANE(tbl_F, tbl2_, 2); } else if(svcntb() < 80) { EXTRACT_LANE(tbl_F, tbl2_, 1); } else { EXTRACT_LANE(tbl_F, tbl2_, 0); } } } else { // implies srcCount={5 or 6}, vect-width=256 val1x = svtbl_s16(val1, NOMASK(svorr_n_u16, shufQidx, 2)); // duplicate element 2&3 across 256-bits svuint64_t valOddB = NOMASK(sveor_u64, valEvenB, svreinterpret_u64_s16(val1x)); gf16_shuffle128_sve2_mul16_tables(valEvenB, valOddB, &tbl_l0, &tbl_l1, &tbl_l2, &tbl_l3, &tbl_h0, &tbl_h1, &tbl_h2, &tbl_h3); EXTRACT_LANE(tbl_C, tbl_, 0); EXTRACT_LANE(tbl_D, tbl_, 1); svint16_t val0246b = svzip2_s16(val04, val26); svint16_t val8ACEb = NOMASK(sveor_s16, val0246b, svtbl_s16(val8, svdupq_n_u16(4,4,4,4, 5,5,5,5))); valEvenA = svzip1_u64( svreinterpret_u64_s16(val0246b), svreinterpret_u64_s16(val8ACEb) ); val1x = svtbl_s16(val1, NOMASK(svorr_n_u16, shufQidx, 4)); // duplicate element 4&5 across 256-bits valOddA = NOMASK(sveor_u64, valEvenA, svreinterpret_u64_s16(val1x)); gf16_shuffle128_sve2_mul16_tables(valEvenA, valOddA, &tbl_l0, &tbl_l1, &tbl_l2, &tbl_l3, &tbl_h0, &tbl_h1, &tbl_h2, &tbl_h3); EXTRACT_LANE(tbl_E, tbl_, 0); if(srcCount >= 6) { EXTRACT_LANE(tbl_F, tbl_, 1); } } } #undef EXTRACT_LANE } static HEDLEY_ALWAYS_INLINE void gf16_shuffle2x128_sve2_round1(svuint8_t data, svuint8_t* rn, svuint8_t* rs1, svuint8_t* rs2, svuint8_t tbl_ln, svuint8_t tbl_ls, svuint8_t tbl_hn, svuint8_t tbl_hs ) { svuint8_t mask = svreinterpret_u8_u16(svdup_n_u16(0x1000)); svuint8_t tmp1 = svbsl_n_u8(data, mask, 0xf); svuint8_t tmp2 = svsri_n_u8(mask, data, 4); *rn = sveor3_u8(*rn, svtbl_u8(tbl_ln, tmp1), svtbl_u8(tbl_hn, tmp2)); *rs1 = svtbl_u8(tbl_ls, tmp1); *rs2 = svtbl_u8(tbl_hs, tmp2); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle2x128_sve2_round(svuint8_t data, svuint8_t* rn, svuint8_t* rs1, svuint8_t* rs2, svuint8_t tbl_ln, svuint8_t tbl_ls, svuint8_t tbl_hn, svuint8_t tbl_hs ) { svuint8_t mask = svreinterpret_u8_u16(svdup_n_u16(0x1000)); svuint8_t tmp1 = svbsl_n_u8(data, mask, 0xf); svuint8_t tmp2 = svsri_n_u8(mask, data, 4); *rn = sveor3_u8(*rn, svtbl_u8(tbl_ln, tmp1), svtbl_u8(tbl_hn, tmp2)); *rs1 = sveor3_u8(*rs1, *rs2, svtbl_u8(tbl_ls, tmp1)); *rs2 = svtbl_u8(tbl_hs, tmp2); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle2x_muladd_x_sve2( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(6); UNUSED(scratch); svuint8_t tbl_Aln, tbl_Als, tbl_Ahn, tbl_Ahs; svuint8_t tbl_Bln, tbl_Bls, tbl_Bhn, tbl_Bhs; svuint8_t tbl_Cln, tbl_Cls, tbl_Chn, tbl_Chs; svuint8_t tbl_Dln, tbl_Dls, tbl_Dhn, tbl_Dhs; svuint8_t tbl_Eln, tbl_Els, tbl_Ehn, tbl_Ehs; svuint8_t tbl_Fln, tbl_Fls, tbl_Fhn, tbl_Fhs; gf16_shuffle2x128_sve2_calc_tables(srcCount, coefficients, &tbl_Aln, &tbl_Als, &tbl_Ahn, &tbl_Ahs, &tbl_Bln, &tbl_Bls, &tbl_Bhn, &tbl_Bhs, &tbl_Cln, &tbl_Cls, &tbl_Chn, &tbl_Chs, &tbl_Dln, &tbl_Dls, &tbl_Dhn, &tbl_Dhs, &tbl_Eln, &tbl_Els, &tbl_Ehn, &tbl_Ehs, &tbl_Fln, &tbl_Fls, &tbl_Fhn, &tbl_Fhs ); svuint8_t rn, rs1, rs2; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += svcntb()) { if(doPrefetch == 1) svprfb(svptrue_b8(), _pf+ptr, SV_PLDL1KEEP); if(doPrefetch == 2) svprfb(svptrue_b8(), _pf+ptr, SV_PLDL2KEEP); rn = svld1_u8(svptrue_b8(), _dst+ptr); gf16_shuffle2x128_sve2_round1(svld1_u8(svptrue_b8(), _src1+ptr*srcScale), &rn, &rs1, &rs2, tbl_Aln, tbl_Als, tbl_Ahn, tbl_Ahs); if(srcCount > 1) gf16_shuffle2x128_sve2_round(svld1_u8(svptrue_b8(), _src2+ptr*srcScale), &rn, &rs1, &rs2, tbl_Bln, tbl_Bls, tbl_Bhn, tbl_Bhs); if(srcCount > 2) gf16_shuffle2x128_sve2_round(svld1_u8(svptrue_b8(), _src3+ptr*srcScale), &rn, &rs1, &rs2, tbl_Cln, tbl_Cls, tbl_Chn, tbl_Chs); if(srcCount > 3) gf16_shuffle2x128_sve2_round(svld1_u8(svptrue_b8(), _src4+ptr*srcScale), &rn, &rs1, &rs2, tbl_Dln, tbl_Dls, tbl_Dhn, tbl_Dhs); if(srcCount > 4) gf16_shuffle2x128_sve2_round(svld1_u8(svptrue_b8(), _src5+ptr*srcScale), &rn, &rs1, &rs2, tbl_Eln, tbl_Els, tbl_Ehn, tbl_Ehs); if(srcCount > 5) gf16_shuffle2x128_sve2_round(svld1_u8(svptrue_b8(), _src6+ptr*srcScale), &rn, &rs1, &rs2, tbl_Fln, tbl_Fls, tbl_Fhn, tbl_Fhs); rs1 = svreinterpret_u8_u16(svxar_n_u16( svreinterpret_u16_u8(rs1), svreinterpret_u16_u8(rs2), 8 )); rn = NOMASK(sveor_u8, rn, rs1); svst1_u8(svptrue_b8(), _dst+ptr, rn); } } #endif /*defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16)*/ #ifdef PARPAR_INVERT_SUPPORT void gf16_shuffle2x_mul_128_sve2(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); UNUSED(scratch); #if defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16) svuint8_t tbl_ln, tbl_ls, tbl_hn, tbl_hs; gf16_shuffle2x128_sve2_calc_tables(1, &val, &tbl_ln, &tbl_ls, &tbl_hn, &tbl_hs, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ); svuint8_t mask = svreinterpret_u8_u16(svdup_n_u16(0x1000)); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += svcntb()) { svuint8_t data = svld1_u8(svptrue_b8(), _src+ptr);; svuint8_t tmp1 = svbsl_n_u8(data, mask, 0xf); svuint8_t tmp2 = svsri_n_u8(mask, data, 4); data = sveor3_u8( svtbl_u8(tbl_ln, tmp1), svtbl_u8(tbl_hn, tmp2), svreinterpret_u8_u16(svxar_n_u16( svreinterpret_u16_u8(svtbl_u8(tbl_ls, tmp1)), svreinterpret_u16_u8(svtbl_u8(tbl_hs, tmp2)), 8 )) ); svst1_u8(svptrue_b8(), _dst+ptr, data); } #else UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #endif void gf16_shuffle2x_muladd_128_sve2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16) gf16_muladd_single(scratch, &gf16_shuffle2x_muladd_x_sve2, dst, src, len, val); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #if defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16) GF16_MULADD_MULTI_FUNCS(gf16_shuffle2x, _128_sve2, gf16_shuffle2x_muladd_x_sve2, 6, svcntb(), 0, (void)0) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_shuffle2x, _128_sve2) #endif // checksum stuff #include "gf16_checksum_sve.h" #if defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16) GF_PREPARE_PACKED_FUNCS(gf16_shuffle2x, _sve, svcntb(), gf16_prepare_half_block_sve, gf16_prepare_half_blocku_sve, 6, (void)0, svint16_t checksum = svdup_n_s16(0), gf16_checksum_block_sve, gf16_checksum_blocku_sve, gf16_checksum_exp_sve, gf16_checksum_prepare_sve, 16) #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_shuffle2x, _sve) #endif #if defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16) GF_FINISH_PACKED_FUNCS(gf16_shuffle2x, _sve, svcntb(), gf16_prepare_half_block_sve, gf16_finish_half_blocku_sve, 1, (void)0, gf16_checksum_block_sve, gf16_checksum_blocku_sve, gf16_checksum_exp_sve, NULL, 16) #else GF_FINISH_PACKED_FUNCS_STUB(gf16_shuffle2x, _sve) #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle2x_x86.h000066400000000000000000000171731514221355600225210ustar00rootroot00000000000000 #include "gf16_shuffle_x86_common.h" #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) # include "gf16_checksum_x86.h" static HEDLEY_ALWAYS_INLINE void _FN(gf16_shuffle2x_prepare_block)(void* dst, const void* src) { _mword data = _MMI(loadu)((_mword*)src); data = separate_low_high(data); #if MWORD_SIZE >= 64 data = _mm512_permutexvar_epi64(_mm512_set_epi64(7,5,3,1, 6,4,2,0), data); #else data = _mm256_permute4x64_epi64(data, _MM_SHUFFLE(3,1,2,0)); #endif _MMI(store)((_mword*)dst, data); } static HEDLEY_ALWAYS_INLINE void _FN(gf16_shuffle2x_prepare_blocku)(void* dst, const void* src, size_t remaining) { _mword data = partial_load(src, remaining); data = separate_low_high(data); #if MWORD_SIZE >= 64 data = _mm512_permutexvar_epi64(_mm512_set_epi64(7,5,3,1, 6,4,2,0), data); #else data = _mm256_permute4x64_epi64(data, _MM_SHUFFLE(3,1,2,0)); #endif _MMI(store)((_mword*)dst, data); } static HEDLEY_ALWAYS_INLINE void _FN(gf16_shuffle2x_finish_block)(void *HEDLEY_RESTRICT dst) { _mword shuf = _MM(set_epi32)( #if MWORD_SIZE >= 64 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, #endif 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800 ); _mword data = _MMI(load)((_mword*)dst); #if MWORD_SIZE >= 64 data = _mm512_permutexvar_epi64(_mm512_set_epi64(7,3, 6,2, 5,1, 4,0), data); #else data = _mm256_permute4x64_epi64(data, _MM_SHUFFLE(3,1,2,0)); #endif data = _MM(shuffle_epi8)(data, shuf); _MMI(store)((_mword*)dst, data); } static HEDLEY_ALWAYS_INLINE void _FN(gf16_shuffle2x_finish_copy_block)(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { _mword shuf = _MM(set_epi32)( #if MWORD_SIZE >= 64 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, #endif 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800 ); _mword data = _MMI(load)((_mword*)src); #if MWORD_SIZE >= 64 data = _mm512_permutexvar_epi64(_mm512_set_epi64(7,3, 6,2, 5,1, 4,0), data); #else data = _mm256_permute4x64_epi64(data, _MM_SHUFFLE(3,1,2,0)); #endif data = _MM(shuffle_epi8)(data, shuf); _MMI(storeu)((_mword*)dst, data); } static HEDLEY_ALWAYS_INLINE void _FN(gf16_shuffle2x_finish_copy_blocku)(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t bytes) { _mword shuf = _MM(set_epi32)( #if MWORD_SIZE >= 64 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, #endif 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800, 0x0f070e06, 0x0d050c04, 0x0b030a02, 0x09010800 ); _mword data = _MMI(load)((_mword*)src); #if MWORD_SIZE >= 64 data = _mm512_permutexvar_epi64(_mm512_set_epi64(7,3, 6,2, 5,1, 4,0), data); #else data = _mm256_permute4x64_epi64(data, _MM_SHUFFLE(3,1,2,0)); #endif data = _MM(shuffle_epi8)(data, shuf); partial_store((_mword*)dst, data, bytes); } #endif #ifdef PARPAR_INVERT_SUPPORT void _FN(gf16_shuffle2x_prepare)(void* dst, const void* src, size_t srcLen) { #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) gf16_prepare(dst, src, srcLen, sizeof(_mword), &_FN(gf16_shuffle2x_prepare_block), &_FN(gf16_shuffle2x_prepare_blocku)); _MM_END #else UNUSED(dst); UNUSED(src); UNUSED(srcLen); #endif } #endif #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) # ifdef PLATFORM_AMD64 GF_PREPARE_PACKED_FUNCS(gf16_shuffle2x, _FNSUFFIX, sizeof(_mword), _FN(gf16_shuffle2x_prepare_block), _FN(gf16_shuffle2x_prepare_blocku), 2 + (MWORD_SIZE==64)*4, _MM_END, _mword checksum = _MMI(setzero)(), _FN(gf16_checksum_block), _FN(gf16_checksum_blocku), _FN(gf16_checksum_exp), _FN(gf16_checksum_prepare), sizeof(_mword)) # else GF_PREPARE_PACKED_FUNCS(gf16_shuffle2x, _FNSUFFIX, sizeof(_mword), _FN(gf16_shuffle2x_prepare_block), _FN(gf16_shuffle2x_prepare_blocku), 1, _MM_END, _mword checksum = _MMI(setzero)(), _FN(gf16_checksum_block), _FN(gf16_checksum_blocku), _FN(gf16_checksum_exp), _FN(gf16_checksum_prepare), sizeof(_mword)) # endif #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_shuffle2x, _FNSUFFIX) #endif #ifdef PARPAR_INVERT_SUPPORT void _FN(gf16_shuffle2x_finish)(void *HEDLEY_RESTRICT dst, size_t len) { #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) gf16_finish(dst, len, sizeof(_mword), &_FN(gf16_shuffle2x_finish_block)); _MM_END #else UNUSED(dst); UNUSED(len); #endif } #endif #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) GF_FINISH_PACKED_FUNCS(gf16_shuffle2x, _FNSUFFIX, sizeof(_mword), _FN(gf16_shuffle2x_finish_copy_block), _FN(gf16_shuffle2x_finish_copy_blocku), 1, _MM_END, _FN(gf16_checksum_block), _FN(gf16_checksum_blocku), _FN(gf16_checksum_exp), &_FN(gf16_shuffle2x_finish_block), sizeof(_mword)) #else GF_FINISH_PACKED_FUNCS_STUB(gf16_shuffle2x, _FNSUFFIX) #endif #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) void _FN(gf16_shuffle2x_setup_vec)(const void *HEDLEY_RESTRICT scratch, uint16_t val, _mword* shufNormLo, _mword* shufSwapLo, _mword* shufNormHi, _mword* shufSwapHi) { __m128i prodLo0, prodHi0, prodLo1, prodHi1, prodLo2, prodHi2, prodLo3, prodHi3; __m128i pd0, pd1; shuf0_vector(val, &pd0, &pd1); pd0 = _mm_shuffle_epi8(pd0, _mm_set_epi32(0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200)); pd1 = _mm_shuffle_epi8(pd1, _mm_set_epi32(0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200)); prodLo0 = _mm_unpacklo_epi64(pd0, pd1); prodHi0 = _mm_unpackhi_epi64(pd0, pd1); __m128i polyl = _mm_load_si128((__m128i*)scratch); __m128i polyh = _mm_setzero_si128(); #ifndef GF16_POLYNOMIAL_SIMPLE polyh = _mm_load_si128((__m128i*)scratch + 1); #endif mul16_vec128(polyl, polyh, prodLo0, prodHi0, &prodLo1, &prodHi1); mul16_vec128(polyl, polyh, prodLo1, prodHi1, &prodLo2, &prodHi2); mul16_vec128(polyl, polyh, prodLo2, prodHi2, &prodLo3, &prodHi3); // shuffle around products #if MWORD_SIZE==64 # define JOIN_VEC(a, b) _mm512_shuffle_i32x4(_mm512_castsi128_si512(a), _mm512_castsi128_si512(b), _MM_SHUFFLE(0,0,0,0)) #else # define JOIN_VEC(a, b) _mm256_permute2x128_si256(_mm256_castsi128_si256(a), _mm256_castsi128_si256(b), 0x20) #endif *shufNormLo = JOIN_VEC(prodLo0, prodHi2); *shufSwapLo = JOIN_VEC(prodHi0, prodLo2); *shufNormHi = JOIN_VEC(prodLo1, prodHi3); *shufSwapHi = JOIN_VEC(prodHi1, prodLo3); #undef JOIN_VEC } #endif #ifdef PARPAR_INVERT_SUPPORT void _FN(gf16_shuffle2x_mul)(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) _mword shufNormLo, shufSwapLo, shufNormHi, shufSwapHi; _FN(gf16_shuffle2x_setup_vec)(scratch, val, &shufNormLo, &shufSwapLo, &shufNormHi, &shufSwapHi); _mword ti; _mword mask = _MM(set1_epi8) (0x0f); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(_mword)) { _mword data = _MMI(load)((_mword*)(_src+ptr)); ti = _MMI(and) (mask, data); _mword swapped = _MM(shuffle_epi8) (shufSwapLo, ti); _mword result = _MM(shuffle_epi8) (shufNormLo, ti); ti = _MM_SRLI4_EPI8(data); swapped = _MMI(xor)(_MM(shuffle_epi8) (shufSwapHi, ti), swapped); #if MWORD_SIZE >= 64 swapped = _mm512_shuffle_i32x4(swapped, swapped, _MM_SHUFFLE(1,0,3,2)); result = _mm512_ternarylogic_epi32( result, _mm512_shuffle_epi8(shufNormHi, ti), swapped, 0x96 ); #else result = _MMI(xor)(_MM(shuffle_epi8) (shufNormHi, ti), result); swapped = _mm256_permute2x128_si256(swapped, swapped, 0x01); result = _MMI(xor)(result, swapped); #endif _MMI(store) ((_mword*)(_dst+ptr), result); } _MM_END #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle512_sve2.c000066400000000000000000000342501514221355600227170ustar00rootroot00000000000000 #include "gf16_sve_common.h" #include "gf16_muladd_multi.h" #if defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16) static HEDLEY_ALWAYS_INLINE void gf16_shuffle512_mul64(svuint8x2_t poly, svuint8_t rl, svuint8_t rh, svuint8_t* tbl_l1, svuint8_t* tbl_h1, svuint8_t* tbl_l2, svuint8_t* tbl_h2 ) { // multiply by 64 and store into tbl1 svuint8_t tl = NOMASK(svlsl_n_u8, rl, 6); svuint8_t th = svsri_n_u8(NOMASK(svlsl_n_u8, rh, 6), rl, 2); svuint8_t ti = NOMASK(svlsr_n_u8, rh, 2); *tbl_l1 = NOMASK(sveor_u8, tl, svtbl_u8(svget2(poly, 0), ti)); *tbl_h1 = NOMASK(sveor_u8, th, svtbl_u8(svget2(poly, 1), ti)); // multiply by 16 and store to tbl2 ti = NOMASK(svlsr_n_u8, *tbl_h1, 4); tl = NOMASK(svlsl_n_u8, *tbl_l1, 4); th = svxar_n_u8(*tbl_h1, ti, 4); *tbl_h2 = svsri_n_u8(th, *tbl_l1, 4); *tbl_l2 = NOMASK(sveor_u8, tl, svpmul_n_u8(ti, GF16_POLYNOMIAL & 0x1f)); // re-arrange for straddled part (top 2 bits swapped with bottom 2) svuint8_t idx = svbsl_n_u8( svdupq_n_u8(0,4,8,12,1,5,9,13,2,6,10,14,3,7,11,15), svindex_u8(0, 1), 0xf ); *tbl_l1 = svtbl_u8(*tbl_l1, idx); *tbl_h1 = svtbl_u8(*tbl_h1, idx); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle512_sve2_calc_tables( const void *HEDLEY_RESTRICT scratch, const unsigned srcCount, const uint16_t *HEDLEY_RESTRICT coefficients, svuint8_t* tbl_Al0, svuint8_t* tbl_Al1, svuint8_t* tbl_Al2, svuint8_t* tbl_Ah0, svuint8_t* tbl_Ah1, svuint8_t* tbl_Ah2, svuint8_t* tbl_Bl0, svuint8_t* tbl_Bl1, svuint8_t* tbl_Bl2, svuint8_t* tbl_Bh0, svuint8_t* tbl_Bh1, svuint8_t* tbl_Bh2, svuint8_t* tbl_Cl0, svuint8_t* tbl_Cl1, svuint8_t* tbl_Cl2, svuint8_t* tbl_Ch0, svuint8_t* tbl_Ch1, svuint8_t* tbl_Ch2, svuint8_t* tbl_Dl0, svuint8_t* tbl_Dl1, svuint8_t* tbl_Dl2, svuint8_t* tbl_Dh0, svuint8_t* tbl_Dh1, svuint8_t* tbl_Dh2 ) { svuint8x2_t poly = svcreate2_u8( svld1_u8(svwhilelt_b8(0, 64), scratch), svld1_u8(svwhilelt_b8(0, 64), (uint8_t*)scratch + 64) ); svint16_t val = svld1_s16(svwhilelt_b16((uint32_t)0, (uint32_t)srcCount), (int16_t*)coefficients); // dupe 16b elements across 128b vector val = svtbl_s16(val, NOMASK(svlsr_n_u16, svindex_u16(0, 1), 3)); // (alternative idea to do a 16->64b extended load, unpack, mul-lane) // strategy: multiply by 0,4,8... then interleave *1 and *2 to get full 0-63 vector svuint8_t valSwap = svreinterpret_u8_s16(NOMASK(svrevb_s16, val)); svuint8_t mul = svdupq_n_u8(0,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60); svuint8_t prodLoB = svpmullb_pair_u8(svreinterpret_u8_s16(val), mul); svuint8_t prodLoT = svpmullt_pair_u8(valSwap, mul); mul = svreinterpret_u8_u16(NOMASK(svrevb_u16, svreinterpret_u16_u8(mul))); svuint8_t prodHiB = svpmullt_pair_u8(svreinterpret_u8_s16(val), mul); svuint8_t prodHiT = svpmullb_pair_u8(valSwap, mul); // re-arrange into proper form (separate high/low bytes) svuint8_t prodLL = svtrn1_u8(prodLoB, prodLoT); svuint8_t prodLH = svtrn2_u8(prodLoB, prodLoT); svuint8_t prodHL = svtrn1_u8(prodHiB, prodHiT); svuint8_t prodHH = svtrn2_u8(prodHiB, prodHiT); // reduction components svuint8_t ri = NOMASK(svlsr_n_u8, prodHH, 4); svuint8_t red1 = svpmul_n_u8(ri, GF16_POLYNOMIAL & 0x1f); ri = svbcax_n_u8(ri, prodHH, 0xf0); svuint8_t red2 = svpmul_n_u8(ri, GF16_POLYNOMIAL & 0x1f); prodHH = svsri_n_u8(NOMASK(svlsl_n_u8, ri, 4), red1, 4); prodLH = sveor3_u8(prodLH, prodHL, prodHH); prodLL = sveor3_u8(prodLL, red2, NOMASK(svlsl_n_u8, red1, 4)); // we now have products (0,4,8...) in prodLL,prodLH // prepare proper arrangement for *1 and *2 for interleaving svuint8_t val2 = svreinterpret_u8_s16(gf16_vec_mul2_sve(val)); svuint8_t val2L = svtrn1_u8(val2, val2); svuint8_t val2H = svtrn2_u8(val2, val2); val2L = NOMASK(sveor_u8, val2L, prodLL); val2H = NOMASK(sveor_u8, val2H, prodLH); svuint8_t val1 = svreinterpret_u8_s16(val); svuint8_t val1L = svtrn1_u8(val1, val1); svuint8_t val1H = svtrn2_u8(val1, val1); // first interleave round to get tbl_A* svuint8_t prodL = svzip1_u8(prodLL, val2L); svuint8_t prodH = svzip1_u8(prodLH, val2H); svuint8_t val1La = svzip1_u8(val1L, val1L); svuint8_t val1Ha = svzip1_u8(val1H, val1H); val1La = NOMASK(sveor_u8, val1La, prodL); val1Ha = NOMASK(sveor_u8, val1Ha, prodH); *tbl_Al0 = svzip1_u8(prodL, val1La); *tbl_Ah0 = svzip1_u8(prodH, val1Ha); gf16_shuffle512_mul64(poly, *tbl_Al0, *tbl_Ah0, tbl_Al1, tbl_Ah1, tbl_Al2, tbl_Ah2); #define EXTRACT_LANE(dst, src, lane) \ *dst##l0 = svext_u8(*src##l0, *src##l0, lane*64); \ *dst##h0 = svext_u8(*src##h0, *src##h0, lane*64); \ *dst##l1 = svext_u8(*src##l1, *src##l1, lane*64); \ *dst##h1 = svext_u8(*src##h1, *src##h1, lane*64); \ *dst##l2 = svext_u8(*src##l2, *src##l2, lane*64); \ *dst##h2 = svext_u8(*src##h2, *src##h2, lane*64) // now get subsequent tables - needs to be width specific if(svcntb() >= srcCount*64) { // processed all at once // extract components and store out if(srcCount > 1) { EXTRACT_LANE(tbl_B, tbl_A, 1); } if(srcCount > 2) { EXTRACT_LANE(tbl_C, tbl_A, 2); } if(srcCount > 3) { EXTRACT_LANE(tbl_D, tbl_A, 3); } } else if(svcntb() >= 128) { // implies srcCount > 2 EXTRACT_LANE(tbl_B, tbl_A, 1); // do second interleave round // shift stuff into position prodL = svext_u8(prodL, prodL, 64); prodH = svext_u8(prodH, prodH, 64); val1La = svext_u8(val1La, val1La, 64); val1Ha = svext_u8(val1Ha, val1Ha, 64); *tbl_Cl0 = svzip1_u8(prodL, val1La); *tbl_Ch0 = svzip1_u8(prodH, val1Ha); gf16_shuffle512_mul64(poly, *tbl_Cl0, *tbl_Ch0, tbl_Cl1, tbl_Ch1, tbl_Cl2, tbl_Ch2); if(srcCount > 3) { EXTRACT_LANE(tbl_D, tbl_C, 1); } } else { // interleave one src at a time (2-4 interleave rounds) prodL = svext_u8(prodL, prodL, 32); prodH = svext_u8(prodH, prodH, 32); val1La = svext_u8(val1La, val1La, 32); val1Ha = svext_u8(val1Ha, val1Ha, 32); *tbl_Bl0 = svzip1_u8(prodL, val1La); *tbl_Bh0 = svzip1_u8(prodH, val1Ha); gf16_shuffle512_mul64(poly, *tbl_Bl0, *tbl_Bh0, tbl_Bl1, tbl_Bh1, tbl_Bl2, tbl_Bh2); if(srcCount > 2) { prodLL = svext_u8(prodLL, prodLL, 32); prodLH = svext_u8(prodLH, prodLH, 32); val2L = svext_u8(val2L, val2L, 32); val2H = svext_u8(val2H, val2H, 32); val1L = svext_u8(val1L, val1L, 32); val1H = svext_u8(val1H, val1H, 32); prodL = svzip1_u8(prodLL, val2L); prodH = svzip1_u8(prodLH, val2H); val1La = svzip1_u8(val1L, val1L); val1Ha = svzip1_u8(val1H, val1H); val1La = NOMASK(sveor_u8, val1La, prodL); val1Ha = NOMASK(sveor_u8, val1Ha, prodH); *tbl_Cl0 = svzip1_u8(prodL, val1La); *tbl_Ch0 = svzip1_u8(prodH, val1Ha); gf16_shuffle512_mul64(poly, *tbl_Cl0, *tbl_Ch0, tbl_Cl1, tbl_Ch1, tbl_Cl2, tbl_Ch2); if(srcCount > 3) { prodL = svext_u8(prodL, prodL, 32); prodH = svext_u8(prodH, prodH, 32); val1La = svext_u8(val1La, val1La, 32); val1Ha = svext_u8(val1Ha, val1Ha, 32); *tbl_Dl0 = svzip1_u8(prodL, val1La); *tbl_Dh0 = svzip1_u8(prodH, val1Ha); gf16_shuffle512_mul64(poly, *tbl_Dl0, *tbl_Dh0, tbl_Dl1, tbl_Dh1, tbl_Dl2, tbl_Dh2); } } } #undef EXTRACT_LANE } static HEDLEY_ALWAYS_INLINE void gf16_shuffle512_sve2_round1(svuint8x2_t va, svuint8_t* rl, svuint8_t* rh, svuint8_t tbl_l0, svuint8_t tbl_l1, svuint8_t tbl_l2, svuint8_t tbl_h0, svuint8_t tbl_h1, svuint8_t tbl_h2 ) { svuint8_t tmp = NOMASK(svand_n_u8, svget2(va, 0), 0x3f); *rl = svtbl_u8(tbl_l0, tmp); *rh = svtbl_u8(tbl_h0, tmp); tmp = NOMASK(svlsr_n_u8, svget2(va, 1), 2); // straddeld element - top 2 from low, bottom 2 from high // TODO: investigate a non-contiguous straddled component to save a vector register; SRI is generally slower than BSL though so may not be worth it svuint8_t mid = svbsl_n_u8( svget2(va, 1), NOMASK(svlsr_n_u8, svget2(va, 0), 4), 3 ); *rl = sveor3_u8(*rl, svtbl_u8(tbl_l1, mid), svtbl_u8(tbl_l2, tmp)); *rh = sveor3_u8(*rh, svtbl_u8(tbl_h1, mid), svtbl_u8(tbl_h2, tmp)); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle512_sve2_round(svuint8x2_t va, svuint8_t* rl, svuint8_t* rh, svuint8_t* rl2, svuint8_t* rh2, svuint8_t tbl_l0, svuint8_t tbl_l1, svuint8_t tbl_l2, svuint8_t tbl_h0, svuint8_t tbl_h1, svuint8_t tbl_h2 ) { svuint8_t tmp = NOMASK(svand_n_u8, svget2(va, 0), 0x3f); svuint8_t tmp2 = NOMASK(svlsr_n_u8, svget2(va, 1), 2); *rl = sveor3_u8(*rl, svtbl_u8(tbl_l0, tmp), svtbl_u8(tbl_l2, tmp2)); *rh = sveor3_u8(*rh, svtbl_u8(tbl_h0, tmp), svtbl_u8(tbl_h2, tmp2)); svuint8_t mid = svbsl_n_u8( svget2(va, 1), NOMASK(svlsr_n_u8, svget2(va, 0), 4), 3 ); *rl2 = svtbl_u8(tbl_l1, mid); *rh2 = svtbl_u8(tbl_h1, mid); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle512_sve2_round4(svuint8x2_t va, svuint8_t* rl, svuint8_t* rh, svuint8_t rl2, svuint8_t rh2, svuint8_t tbl_l0, svuint8_t tbl_l1, svuint8_t tbl_l2, svuint8_t tbl_h0, svuint8_t tbl_h1, svuint8_t tbl_h2 ) { *rl = NOMASK(sveor_u8, *rl, rl2); // free up a register to avoid a spill svuint8_t tmp = NOMASK(svand_n_u8, svget2(va, 0), 0x3f); *rl = NOMASK(sveor_u8, *rl, svtbl_u8(tbl_l0, tmp)); *rh = sveor3_u8(*rh, svtbl_u8(tbl_h0, tmp), rh2); svuint8_t tmp2 = NOMASK(svlsr_n_u8, svget2(va, 1), 2); svuint8_t mid = svbsl_n_u8( svget2(va, 1), NOMASK(svlsr_n_u8, svget2(va, 0), 4), 3 ); *rl = sveor3_u8(*rl, svtbl_u8(tbl_l1, mid), svtbl_u8(tbl_l2, tmp2)); *rh = sveor3_u8(*rh, svtbl_u8(tbl_h1, mid), svtbl_u8(tbl_h2, tmp2)); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle512_muladd_x_sve2( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(4); svuint8_t tbl_Al0, tbl_Al1, tbl_Al2, tbl_Ah0, tbl_Ah1, tbl_Ah2; svuint8_t tbl_Bl0, tbl_Bl1, tbl_Bl2, tbl_Bh0, tbl_Bh1, tbl_Bh2; svuint8_t tbl_Cl0, tbl_Cl1, tbl_Cl2, tbl_Ch0, tbl_Ch1, tbl_Ch2; svuint8_t tbl_Dl0, tbl_Dl1, tbl_Dl2, tbl_Dh0, tbl_Dh1, tbl_Dh2; gf16_shuffle512_sve2_calc_tables(scratch, srcCount, coefficients, &tbl_Al0, &tbl_Al1, &tbl_Al2, &tbl_Ah0, &tbl_Ah1, &tbl_Ah2, &tbl_Bl0, &tbl_Bl1, &tbl_Bl2, &tbl_Bh0, &tbl_Bh1, &tbl_Bh2, &tbl_Cl0, &tbl_Cl1, &tbl_Cl2, &tbl_Ch0, &tbl_Ch1, &tbl_Ch2, &tbl_Dl0, &tbl_Dl1, &tbl_Dl2, &tbl_Dh0, &tbl_Dh1, &tbl_Dh2 ); svuint8_t rl, rh, rl2, rh2; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += svcntb()*2) { if(doPrefetch == 1) { svprfb(svptrue_b8(), _pf+ptr, SV_PLDL1KEEP); svprfb_vnum(svptrue_b8(), _pf+ptr, 1, SV_PLDL1KEEP); } if(doPrefetch == 2) { svprfb(svptrue_b8(), _pf+ptr, SV_PLDL2KEEP); svprfb_vnum(svptrue_b8(), _pf+ptr, 1, SV_PLDL2KEEP); } svuint8x2_t vb = svld2_u8(svptrue_b8(), _dst+ptr); gf16_shuffle512_sve2_round1(svld2_u8(svptrue_b8(), _src1+ptr*srcScale), &rl, &rh, tbl_Al0, tbl_Al1, tbl_Al2, tbl_Ah0, tbl_Ah1, tbl_Ah2); if(srcCount > 1) { gf16_shuffle512_sve2_round(svld2_u8(svptrue_b8(), _src2+ptr*srcScale), &rl, &rh, &rl2, &rh2, tbl_Bl0, tbl_Bl1, tbl_Bl2, tbl_Bh0, tbl_Bh1, tbl_Bh2); rl = sveor3_u8(rl, rl2, svget2(vb, 0)); rh = sveor3_u8(rh, rh2, svget2(vb, 1)); } else { rl2 = svget2(vb, 0); rh2 = svget2(vb, 1); } if(srcCount > 2) { gf16_shuffle512_sve2_round(svld2_u8(svptrue_b8(), _src3+ptr*srcScale), &rl, &rh, &rl2, &rh2, tbl_Cl0, tbl_Cl1, tbl_Cl2, tbl_Ch0, tbl_Ch1, tbl_Ch2); } if(srcCount > 3) { gf16_shuffle512_sve2_round4(svld2_u8(svptrue_b8(), _src4+ptr*srcScale), &rl, &rh, rl2, rh2, tbl_Dl0, tbl_Dl1, tbl_Dl2, tbl_Dh0, tbl_Dh1, tbl_Dh2); } if(srcCount & 1) { rl = NOMASK(sveor_u8, rl, rl2); rh = NOMASK(sveor_u8, rh, rh2); } svst2_u8(svptrue_b8(), _dst+ptr, svcreate2_u8(rl, rh)); } } #endif /*defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16)*/ #ifdef PARPAR_INVERT_SUPPORT void gf16_shuffle_mul_512_sve2(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16) svuint8_t tbl_l0, tbl_l1, tbl_l2, tbl_h0, tbl_h1, tbl_h2; gf16_shuffle512_sve2_calc_tables(scratch, 1, &val, &tbl_l0, &tbl_l1, &tbl_l2, &tbl_h0, &tbl_h1, &tbl_h2, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; svuint8_t rl, rh; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += svcntb()*2) { gf16_shuffle512_sve2_round1(svld2_u8(svptrue_b8(), _src+ptr), &rl, &rh, tbl_l0, tbl_l1, tbl_l2, tbl_h0, tbl_h1, tbl_h2); svst2_u8(svptrue_b8(), _dst+ptr, svcreate2_u8(rl, rh)); } #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #endif void gf16_shuffle_muladd_512_sve2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16) gf16_muladd_single(scratch, &gf16_shuffle512_muladd_x_sve2, dst, src, len, val); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #if defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16) GF16_MULADD_MULTI_FUNCS(gf16_shuffle, _512_sve2, gf16_shuffle512_muladd_x_sve2, 4, svcntb()*2, 0, (void)0) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_shuffle, _512_sve2) #endif // checksum stuff #include "gf16_checksum_sve.h" #if defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16) GF_PREPARE_PACKED_FUNCS(gf16_shuffle, _512_sve2, svcntb()*2, gf16_prepare_block_sve, gf16_prepare_blocku_sve, 4, (void)0, svint16_t checksum = svdup_n_s16(0), gf16_checksum_block_sve, gf16_checksum_blocku_sve, gf16_checksum_exp_sve, gf16_checksum_prepare_sve, 64) #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_shuffle, _512_sve2) #endif void* gf16_shuffle_init_512_sve(int polynomial) { #if defined(__ARM_FEATURE_SVE2) && !defined(PARPAR_SLIM_GF16) uint8_t* ret; if((polynomial | 0x1f) != 0x1101f) return NULL; ALIGN_ALLOC(ret, 128, 64); for(int i=0; i<64; i++) { int p = i << 16; if(p & 0x200000) p ^= polynomial << 5; if(p & 0x100000) p ^= polynomial << 4; if(p & 0x080000) p ^= polynomial << 3; if(p & 0x040000) p ^= polynomial << 2; if(p & 0x020000) p ^= polynomial << 1; if(p & 0x010000) p ^= polynomial << 0; ret[i] = p & 0xff; ret[i+64] = (p >> 8) & 0xff; } return ret; #else UNUSED(polynomial); return NULL; #endif } par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle_avx.c000066400000000000000000000006301514221355600224010ustar00rootroot00000000000000 #include "../src/platform.h" #define MWORD_SIZE 16 #define _mword __m128i #define _MM(f) _mm_ ## f #define _MMI(f) _mm_ ## f ## _si128 #define _FNSUFFIX _avx #define _MM_END #if defined(__AVX__) # define _AVAILABLE # define _AVAILABLE_AVX #endif #include "gf16_shuffle_x86.h" #undef _AVAILABLE #undef _AVAILABLE_AVX #undef MWORD_SIZE #undef _mword #undef _MM #undef _MMI #undef _FNSUFFIX #undef _MM_END par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle_avx2.c000066400000000000000000000156751514221355600225020ustar00rootroot00000000000000 #include "../src/platform.h" #define MWORD_SIZE 32 #define _mword __m256i #define _MM(f) _mm256_ ## f #define _MMI(f) _mm256_ ## f ## _si256 #define _FNSUFFIX _avx2 #define _MM_END _mm256_zeroupper(); #if defined(__AVX2__) # define _AVAILABLE #endif #include "gf16_shuffle_x86.h" #include "gf16_shuffle2x_x86.h" #include "gf16_muladd_multi.h" #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) static HEDLEY_ALWAYS_INLINE void gf16_shuffle2x_muladd_round_avx2(__m256i* _dst, const int srcCount, const uint8_t* _src1, const uint8_t* _src2, intptr_t srcOffset, __m256i shufNormLoA, __m256i shufNormLoB, __m256i shufNormHiA, __m256i shufNormHiB, __m256i shufSwapLoA, __m256i shufSwapLoB, __m256i shufSwapHiA, __m256i shufSwapHiB) { __m256i data = _mm256_load_si256((const __m256i*)(_src1 + srcOffset)); __m256i mask = _mm256_set1_epi8(0x0f); __m256i ti = _mm256_and_si256(mask, data); __m256i swapped = _mm256_shuffle_epi8(shufSwapLoA, ti); __m256i result = _mm256_shuffle_epi8(shufNormLoA, ti); ti = _mm256_and_si256(mask, _mm256_srli_epi16(data, 4)); swapped = _mm256_xor_si256(_mm256_shuffle_epi8(shufSwapHiA, ti), swapped); result = _mm256_xor_si256(_mm256_shuffle_epi8(shufNormHiA, ti), result); result = _mm256_xor_si256(result, _mm256_load_si256(_dst)); if(srcCount > 1) { data = _mm256_load_si256((const __m256i*)(_src2 + srcOffset)); ti = _mm256_and_si256(mask, data); result = _mm256_xor_si256(_mm256_shuffle_epi8(shufNormLoB, ti), result); swapped = _mm256_xor_si256(_mm256_shuffle_epi8(shufSwapLoB, ti), swapped); ti = _mm256_and_si256(mask, _mm256_srli_epi16(data, 4)); result = _mm256_xor_si256(_mm256_shuffle_epi8(shufNormHiB, ti), result); swapped = _mm256_xor_si256(_mm256_shuffle_epi8(shufSwapHiB, ti), swapped); } swapped = _mm256_permute2x128_si256(swapped, swapped, 0x01); result = _mm256_xor_si256(result, swapped); _mm256_store_si256(_dst, result); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle2x_muladd_x_avx2(const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf) { GF16_MULADD_MULTI_SRC_UNUSED(2); __m256i shufNormLoA, shufSwapLoA, shufNormHiA, shufSwapHiA; __m256i shufNormLoB, shufSwapLoB, shufNormHiB, shufSwapHiB; if(srcCount == 2) { __m256i prodLo0, prodHi0, prodLo1, prodHi1, prodLo2, prodHi2, prodLo3, prodHi3; __m256i polyl = _mm256_broadcastsi128_si256(_mm_load_si128((__m128i*)scratch)); __m256i polyh = _mm256_setzero_si256(); #ifndef GF16_POLYNOMIAL_SIMPLE polyh = _mm256_broadcastsi128_si256(_mm_load_si128((__m128i*)scratch + 1)); #endif __m256i prod0 = _mm256_shuffle_epi8( _mm256_broadcastd_epi32(_mm_cvtsi32_si128(read32(coefficients))), _mm256_set_epi32( 0x03020302, 0x03020302, 0x03020302, 0x03020302, 0x01000100, 0x01000100, 0x01000100, 0x01000100 ) ); __m256i mul2 = gf16_vec256_mul2(prod0); __m256i mul4 = gf16_vec256_mul2(mul2); prod0 = _mm256_slli_epi32(prod0, 16); prod0 = _mm256_xor_si256(prod0, _mm256_slli_epi64(mul2, 32)); prod0 = _mm256_xor_si256(prod0, _mm256_slli_si256(mul4, 8)); __m256i prod8 = _mm256_xor_si256(prod0, gf16_vec256_mul2(mul4)); __m256i shuf = _mm256_set_epi32( 0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200, 0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200 ); prod0 = _mm256_shuffle_epi8(prod0, shuf); prod8 = _mm256_shuffle_epi8(prod8, shuf); prodLo0 = _mm256_unpacklo_epi64(prod0, prod8); prodHi0 = _mm256_unpackhi_epi64(prod0, prod8); mul16_vec2x(polyl, polyh, prodLo0, prodHi0, &prodLo1, &prodHi1); mul16_vec2x(polyl, polyh, prodLo1, prodHi1, &prodLo2, &prodHi2); mul16_vec2x(polyl, polyh, prodLo2, prodHi2, &prodLo3, &prodHi3); // mix vectors #define JOIN_VEC(a, b, i) _mm256_permute2x128_si256(a, b, 0x20 + i*0x11) shufNormLoA = JOIN_VEC(prodLo0, prodHi2, 0); shufSwapLoA = JOIN_VEC(prodHi0, prodLo2, 0); shufNormLoB = JOIN_VEC(prodLo0, prodHi2, 1); shufSwapLoB = JOIN_VEC(prodHi0, prodLo2, 1); shufNormHiA = JOIN_VEC(prodLo1, prodHi3, 0); shufSwapHiA = JOIN_VEC(prodHi1, prodLo3, 0); shufNormHiB = JOIN_VEC(prodLo1, prodHi3, 1); shufSwapHiB = JOIN_VEC(prodHi1, prodLo3, 1); #undef JOIN_VEC } else { gf16_shuffle2x_setup_vec_avx2(scratch, coefficients[0], &shufNormLoA, &shufSwapLoA, &shufNormHiA, &shufSwapHiA); // MSVC, in Debug mode, complains about unintialized variables, so set it to something... // ...but on Clang6 on macOS seems to lack these functions, so avoid that trap #ifndef __APPLE__ shufNormLoB = _mm256_undefined_si256(); shufSwapLoB = _mm256_undefined_si256(); shufNormHiB = _mm256_undefined_si256(); shufSwapHiB = _mm256_undefined_si256(); #endif } if(doPrefetch) { intptr_t ptr = -(intptr_t)len; if(len & (sizeof(__m256i)*2-1)) { // number of loop iterations isn't even, so do one iteration to make it even gf16_shuffle2x_muladd_round_avx2( (__m256i*)(_dst+ptr), srcCount, _src1, _src2, ptr*srcScale, shufNormLoA, shufNormLoB, shufNormHiA, shufNormHiB, shufSwapLoA, shufSwapLoB, shufSwapHiA, shufSwapHiB ); if(doPrefetch == 1) _mm_prefetch(_pf+ptr, MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+ptr, _MM_HINT_T2); ptr += sizeof(__m256i); } while(ptr) { gf16_shuffle2x_muladd_round_avx2( (__m256i*)(_dst+ptr), srcCount, _src1, _src2, ptr*srcScale, shufNormLoA, shufNormLoB, shufNormHiA, shufNormHiB, shufSwapLoA, shufSwapLoB, shufSwapHiA, shufSwapHiB ); ptr += sizeof(__m256i); gf16_shuffle2x_muladd_round_avx2( (__m256i*)(_dst+ptr), srcCount, _src1, _src2, ptr*srcScale, shufNormLoA, shufNormLoB, shufNormHiA, shufNormHiB, shufSwapLoA, shufSwapLoB, shufSwapHiA, shufSwapHiB ); if(doPrefetch == 1) _mm_prefetch(_pf+ptr, MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+ptr, _MM_HINT_T2); ptr += sizeof(__m256i); } } else { for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m256i)) { gf16_shuffle2x_muladd_round_avx2( (__m256i*)(_dst+ptr), srcCount, _src1, _src2, ptr*srcScale, shufNormLoA, shufNormLoB, shufNormHiA, shufNormHiB, shufSwapLoA, shufSwapLoB, shufSwapHiA, shufSwapHiB ); } } } #endif #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) && defined(PLATFORM_AMD64) GF16_MULADD_MULTI_FUNCS(gf16_shuffle2x, _avx2, gf16_shuffle2x_muladd_x_avx2, 2, sizeof(__m256i), 0, _mm256_zeroupper()) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_shuffle2x, _avx2) #endif void gf16_shuffle2x_muladd_avx2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) gf16_muladd_single(scratch, &gf16_shuffle2x_muladd_x_avx2, dst, src, len, val); _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #undef _AVAILABLE #undef MWORD_SIZE #undef _mword #undef _MM #undef _MMI #undef _FNSUFFIX #undef _MM_END par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle_avx512.c000066400000000000000000000360161514221355600226400ustar00rootroot00000000000000 #include "../src/platform.h" #define MWORD_SIZE 64 #define _mword __m512i #define _MM(f) _mm512_ ## f #define _MMI(f) _mm512_ ## f ## _si512 #define _FNSUFFIX _avx512 /* still called "mm256" even in AVX512? */ #define _MM_END _mm256_zeroupper(); #if defined(__AVX512BW__) && defined(__AVX512VL__) # define _AVAILABLE #endif #include "gf16_shuffle_x86.h" #include "gf16_shuffle2x_x86.h" #include "gf16_muladd_multi.h" #ifdef _AVAILABLE static HEDLEY_ALWAYS_INLINE void gf16_shuffle_calc2x_table(const uint16_t* coefficients, __m256i polyl, __m256i polyh, __m256i* prodLo0, __m256i* prodHi0, __m256i* prodLo1, __m256i* prodHi1, __m256i* prodLo2, __m256i* prodHi2, __m256i* prodLo3, __m256i* prodHi3) { __m256i prod0, mul8; gf16_initial_mul_vector_x2(coefficients, &prod0, &mul8); __m256i prod8 = _mm256_xor_si256(prod0, mul8); __m256i shuf = _mm256_set_epi32( 0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200, 0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200 ); prod0 = _mm256_shuffle_epi8(prod0, shuf); prod8 = _mm256_shuffle_epi8(prod8, shuf); *prodLo0 = _mm256_unpacklo_epi64(prod0, prod8); *prodHi0 = _mm256_unpackhi_epi64(prod0, prod8); mul16_vec2x(polyl, polyh, *prodLo0, *prodHi0, prodLo1, prodHi1); mul16_vec2x(polyl, polyh, *prodLo1, *prodHi1, prodLo2, prodHi2); mul16_vec2x(polyl, polyh, *prodLo2, *prodHi2, prodLo3, prodHi3); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle_calc4x_table(const uint16_t* coefficients, const int do4, __m512i polyl, __m512i polyh, __m512i* prodLo0, __m512i* prodHi0, __m512i* prodLo1, __m512i* prodHi1, __m512i* prodLo2, __m512i* prodHi2, __m512i* prodLo3, __m512i* prodHi3) { __m512i prod0, mul8; gf16_initial_mul_vector_x4(coefficients, &prod0, &mul8, do4); __m512i prod8 = _mm512_xor_si512(prod0, mul8); prod0 = separate_low_high512(prod0); prod8 = separate_low_high512(prod8); *prodLo0 = _mm512_unpacklo_epi64(prod0, prod8); *prodHi0 = _mm512_unpackhi_epi64(prod0, prod8); mul16_vec4x(polyl, polyh, *prodLo0, *prodHi0, prodLo1, prodHi1); mul16_vec4x(polyl, polyh, *prodLo1, *prodHi1, prodLo2, prodHi2); mul16_vec4x(polyl, polyh, *prodLo2, *prodHi2, prodLo3, prodHi3); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle_avx512_round( __m512i* src, __m512i* tpl, __m512i* tph, __m512i prodLo0, __m512i prodHi0, __m512i prodLo1, __m512i prodHi1, __m512i prodLo2, __m512i prodHi2, __m512i prodLo3, __m512i prodHi3 ) { __m512i ta = _mm512_load_si512(src); __m512i tb = _mm512_load_si512(src + 1); __m512i til = _mm512_and_si512(_mm512_set1_epi8(0x0f), tb); __m512i tih = _mm512_and_si512(_mm512_set1_epi8(0x0f), _mm512_srli_epi16(tb, 4)); *tpl = _mm512_ternarylogic_epi32(_mm512_shuffle_epi8(prodLo0, til), _mm512_shuffle_epi8(prodLo1, tih), *tpl, 0x96); *tph = _mm512_ternarylogic_epi32(_mm512_shuffle_epi8(prodHi0, til), _mm512_shuffle_epi8(prodHi1, tih), *tph, 0x96); til = _mm512_and_si512(_mm512_set1_epi8(0x0f), ta); tih = _mm512_and_si512(_mm512_set1_epi8(0x0f), _mm512_srli_epi16(ta, 4)); *tpl = _mm512_ternarylogic_epi32(*tpl, _mm512_shuffle_epi8(prodLo2, til), _mm512_shuffle_epi8(prodLo3, tih), 0x96); *tph = _mm512_ternarylogic_epi32(*tph, _mm512_shuffle_epi8(prodHi2, til), _mm512_shuffle_epi8(prodHi3, tih), 0x96); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle_muladd_x_avx512( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(3); __m512i polyl, polyh; if(srcCount > 1) { polyl = _mm512_broadcast_i32x4(_mm_load_si128((__m128i*)scratch)); #ifndef GF16_POLYNOMIAL_SIMPLE polyh = _mm512_broadcast_i32x4(_mm_load_si128((__m128i*)scratch + 1)); #else polyh = _mm512_setzero_si512(); #endif } __m512i lowA0, lowA1, lowA2, lowA3, highA0, highA1, highA2, highA3; __m512i lowB0, lowB1, lowB2, lowB3, highB0, highB1, highB2, highB3; __m512i lowC0, lowC1, lowC2, lowC3, highC0, highC1, highC2, highC3; __m512i lowD0, lowD1, lowD2, lowD3, highD0, highD1, highD2, highD3; if(srcCount >= 3) { __m512i prodLo0, prodHi0, prodLo1, prodHi1, prodLo2, prodHi2, prodLo3, prodHi3; gf16_shuffle_calc4x_table(coefficients, (srcCount==4), polyl, polyh, &prodLo0, &prodHi0, &prodLo1, &prodHi1, &prodLo2, &prodHi2, &prodLo3, &prodHi3); // generate final vecs #define GEN_TABLE(m, n, p) \ low##m = _mm512_shuffle_i32x4(prodLo##n, prodLo##n, _MM_SHUFFLE(p,p,p,p)); \ high##m = _mm512_shuffle_i32x4(prodHi##n, prodHi##n, _MM_SHUFFLE(p,p,p,p)) GEN_TABLE(A0, 0, 0); GEN_TABLE(A1, 1, 0); GEN_TABLE(A2, 2, 0); GEN_TABLE(A3, 3, 0); GEN_TABLE(B0, 0, 1); GEN_TABLE(B1, 1, 1); GEN_TABLE(B2, 2, 1); GEN_TABLE(B3, 3, 1); GEN_TABLE(C0, 0, 2); GEN_TABLE(C1, 1, 2); GEN_TABLE(C2, 2, 2); GEN_TABLE(C3, 3, 2); if(srcCount == 4) { GEN_TABLE(D0, 0, 3); GEN_TABLE(D1, 1, 3); GEN_TABLE(D2, 2, 3); GEN_TABLE(D3, 3, 3); } #undef GEN_TABLE } if(srcCount == 2) { __m256i prodLo0, prodHi0, prodLo1, prodHi1, prodLo2, prodHi2, prodLo3, prodHi3; gf16_shuffle_calc2x_table(coefficients, _mm512_castsi512_si256(polyl), _mm512_castsi512_si256(polyh), &prodLo0, &prodHi0, &prodLo1, &prodHi1, &prodLo2, &prodHi2, &prodLo3, &prodHi3); // generate final vecs #define GEN_TABLE(n) \ lowA##n = _mm512_shuffle_i32x4(_mm512_castsi256_si512(prodLo##n), _mm512_castsi256_si512(prodLo##n), _MM_SHUFFLE(0,0,0,0)); \ highA##n = _mm512_shuffle_i32x4(_mm512_castsi256_si512(prodHi##n), _mm512_castsi256_si512(prodHi##n), _MM_SHUFFLE(0,0,0,0)); \ lowB##n = _mm512_shuffle_i32x4(_mm512_castsi256_si512(prodLo##n), _mm512_castsi256_si512(prodLo##n), _MM_SHUFFLE(1,1,1,1)); \ highB##n = _mm512_shuffle_i32x4(_mm512_castsi256_si512(prodHi##n), _mm512_castsi256_si512(prodHi##n), _MM_SHUFFLE(1,1,1,1)) GEN_TABLE(0); GEN_TABLE(1); GEN_TABLE(2); GEN_TABLE(3); #undef GEN_TABLE } if(srcCount == 1) { gf16_shuffle_setup_vec(scratch, coefficients[0], &lowA0, &highA0, &lowA1, &highA1, &lowA2, &highA2, &lowA3, &highA3); } for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m512i)*2) { __m512i tph = _mm512_load_si512((__m512i*)(_dst+ptr)); __m512i tpl = _mm512_load_si512((__m512i*)(_dst+ptr) + 1); gf16_shuffle_avx512_round((__m512i*)(_src1+ptr*srcScale), &tpl, &tph, lowA0, highA0, lowA1, highA1, lowA2, highA2, lowA3, highA3); if(srcCount >= 2) gf16_shuffle_avx512_round((__m512i*)(_src2+ptr*srcScale), &tpl, &tph, lowB0, highB0, lowB1, highB1, lowB2, highB2, lowB3, highB3); if(srcCount >= 3) gf16_shuffle_avx512_round((__m512i*)(_src3+ptr*srcScale), &tpl, &tph, lowC0, highC0, lowC1, highC1, lowC2, highC2, lowC3, highC3); if(srcCount >= 4) gf16_shuffle_avx512_round((__m512i*)(_src4+ptr*srcScale), &tpl, &tph, lowD0, highD0, lowD1, highD1, lowD2, highD2, lowD3, highD3); _mm512_store_si512((__m512i*)(_dst+ptr), tph); _mm512_store_si512((__m512i*)(_dst+ptr) + 1, tpl); if(doPrefetch == 1) _mm_prefetch(_pf+(ptr>>1), MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+(ptr>>1), _MM_HINT_T1); } } #endif // defined(_AVAILABLE) #if defined(_AVAILABLE) && defined(PLATFORM_AMD64) GF16_MULADD_MULTI_FUNCS(gf16_shuffle, _avx512, gf16_shuffle_muladd_x_avx512, 3, sizeof(__m512i)*2, 1, _mm256_zeroupper()) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_shuffle, _avx512) #endif #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) static HEDLEY_ALWAYS_INLINE void gf16_shuffle2x_avx512_round1( __m512i* src, __m512i* result, __m512i* swapped, __m512i shufNormLo, __m512i shufNormHi, __m512i shufSwapLo, __m512i shufSwapHi ) { __m512i data = _mm512_load_si512(src); __m512i til = _mm512_and_si512(_mm512_set1_epi8(0x0f), data); __m512i tih = _mm512_and_si512(_mm512_set1_epi8(0x0f), _mm512_srli_epi16(data, 4)); *result = _mm512_ternarylogic_epi32( _mm512_shuffle_epi8(shufNormLo, til), _mm512_shuffle_epi8(shufNormHi, tih), *result, 0x96 ); *swapped = _mm512_xor_si512( _mm512_shuffle_epi8(shufSwapLo, til), _mm512_shuffle_epi8(shufSwapHi, tih) ); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle2x_avx512_round( __m512i* src, __m512i* result, __m512i* swapped, __m512i shufNormLo, __m512i shufNormHi, __m512i shufSwapLo, __m512i shufSwapHi ) { __m512i data = _mm512_load_si512(src); __m512i til = _mm512_and_si512(_mm512_set1_epi8(0x0f), data); __m512i tih = _mm512_and_si512(_mm512_set1_epi8(0x0f), _mm512_srli_epi16(data, 4)); *result = _mm512_ternarylogic_epi32( *result, _mm512_shuffle_epi8(shufNormLo, til), _mm512_shuffle_epi8(shufNormHi, tih), 0x96 ); *swapped = _mm512_ternarylogic_epi32( *swapped, _mm512_shuffle_epi8(shufSwapLo, til), _mm512_shuffle_epi8(shufSwapHi, tih), 0x96 ); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle2x_muladd_x_avx512( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(6); __m512i polyl, polyh; if(srcCount > 1) { polyl = _mm512_broadcast_i32x4(_mm_load_si128((__m128i*)scratch)); #ifndef GF16_POLYNOMIAL_SIMPLE polyh = _mm512_broadcast_i32x4(_mm_load_si128((__m128i*)scratch + 1)); #else polyh = _mm512_setzero_si512(); #endif } __m512i shufNormLoA, shufNormHiA, shufSwapLoA, shufSwapHiA; __m512i shufNormLoB, shufNormHiB, shufSwapLoB, shufSwapHiB; __m512i shufNormLoC, shufNormHiC, shufSwapLoC, shufSwapHiC; __m512i shufNormLoD, shufNormHiD, shufSwapLoD, shufSwapHiD; __m512i shufNormLoE, shufNormHiE, shufSwapLoE, shufSwapHiE; __m512i shufNormLoF, shufNormHiF, shufSwapLoF, shufSwapHiF; #define JOIN_VEC(a, b, i) _mm512_shuffle_i32x4(a, b, _MM_SHUFFLE(i,i,i,i)) if(srcCount >= 3) { __m512i prodLo0, prodHi0, prodLo1, prodHi1, prodLo2, prodHi2, prodLo3, prodHi3; const int do4 = (srcCount==4 || srcCount==6); gf16_shuffle_calc4x_table(coefficients, do4, polyl, polyh, &prodLo0, &prodHi0, &prodLo1, &prodHi1, &prodLo2, &prodHi2, &prodLo3, &prodHi3); shufNormLoA = JOIN_VEC(prodLo0, prodHi2, 0); shufSwapLoA = JOIN_VEC(prodHi0, prodLo2, 0); shufNormLoB = JOIN_VEC(prodLo0, prodHi2, 1); shufSwapLoB = JOIN_VEC(prodHi0, prodLo2, 1); shufNormLoC = JOIN_VEC(prodLo0, prodHi2, 2); shufSwapLoC = JOIN_VEC(prodHi0, prodLo2, 2); shufNormLoD = JOIN_VEC(prodLo0, prodHi2, 3); shufSwapLoD = JOIN_VEC(prodHi0, prodLo2, 3); shufNormHiA = JOIN_VEC(prodLo1, prodHi3, 0); shufSwapHiA = JOIN_VEC(prodHi1, prodLo3, 0); shufNormHiB = JOIN_VEC(prodLo1, prodHi3, 1); shufSwapHiB = JOIN_VEC(prodHi1, prodLo3, 1); shufNormHiC = JOIN_VEC(prodLo1, prodHi3, 2); shufSwapHiC = JOIN_VEC(prodHi1, prodLo3, 2); if(do4) { shufNormHiD = JOIN_VEC(prodLo1, prodHi3, 3); shufSwapHiD = JOIN_VEC(prodHi1, prodLo3, 3); } } #undef JOIN_VEC #define JOIN_VEC(a, b, i) _mm512_shuffle_i32x4(_mm512_castsi256_si512(a), _mm512_castsi256_si512(b), _MM_SHUFFLE(i,i,i,i)) if(srcCount == 6) { __m256i prodLo0, prodHi0, prodLo1, prodHi1, prodLo2, prodHi2, prodLo3, prodHi3; gf16_shuffle_calc2x_table(coefficients+4, _mm512_castsi512_si256(polyl), _mm512_castsi512_si256(polyh), &prodLo0, &prodHi0, &prodLo1, &prodHi1, &prodLo2, &prodHi2, &prodLo3, &prodHi3); shufNormLoE = JOIN_VEC(prodLo0, prodHi2, 0); shufSwapLoE = JOIN_VEC(prodHi0, prodLo2, 0); shufNormLoF = JOIN_VEC(prodLo0, prodHi2, 1); shufSwapLoF = JOIN_VEC(prodHi0, prodLo2, 1); shufNormHiE = JOIN_VEC(prodLo1, prodHi3, 0); shufSwapHiE = JOIN_VEC(prodHi1, prodLo3, 0); shufNormHiF = JOIN_VEC(prodLo1, prodHi3, 1); shufSwapHiF = JOIN_VEC(prodHi1, prodLo3, 1); } if(srcCount == 5) { __m256i prodLo0, prodHi0, prodLo1, prodHi1, prodLo2, prodHi2, prodLo3, prodHi3; gf16_shuffle_calc2x_table(coefficients+3, _mm512_castsi512_si256(polyl), _mm512_castsi512_si256(polyh), &prodLo0, &prodHi0, &prodLo1, &prodHi1, &prodLo2, &prodHi2, &prodLo3, &prodHi3); shufNormLoD = JOIN_VEC(prodLo0, prodHi2, 0); shufSwapLoD = JOIN_VEC(prodHi0, prodLo2, 0); shufNormLoE = JOIN_VEC(prodLo0, prodHi2, 1); shufSwapLoE = JOIN_VEC(prodHi0, prodLo2, 1); shufNormHiD = JOIN_VEC(prodLo1, prodHi3, 0); shufSwapHiD = JOIN_VEC(prodHi1, prodLo3, 0); shufNormHiE = JOIN_VEC(prodLo1, prodHi3, 1); shufSwapHiE = JOIN_VEC(prodHi1, prodLo3, 1); } if(srcCount == 2) { __m256i prodLo0, prodHi0, prodLo1, prodHi1, prodLo2, prodHi2, prodLo3, prodHi3; gf16_shuffle_calc2x_table(coefficients, _mm512_castsi512_si256(polyl), _mm512_castsi512_si256(polyh), &prodLo0, &prodHi0, &prodLo1, &prodHi1, &prodLo2, &prodHi2, &prodLo3, &prodHi3); shufNormLoA = JOIN_VEC(prodLo0, prodHi2, 0); shufSwapLoA = JOIN_VEC(prodHi0, prodLo2, 0); shufNormLoB = JOIN_VEC(prodLo0, prodHi2, 1); shufSwapLoB = JOIN_VEC(prodHi0, prodLo2, 1); shufNormHiA = JOIN_VEC(prodLo1, prodHi3, 0); shufSwapHiA = JOIN_VEC(prodHi1, prodLo3, 0); shufNormHiB = JOIN_VEC(prodLo1, prodHi3, 1); shufSwapHiB = JOIN_VEC(prodHi1, prodLo3, 1); } #undef JOIN_VEC if(srcCount == 1) { gf16_shuffle2x_setup_vec_avx512(scratch, coefficients[0], &shufNormLoA, &shufSwapLoA, &shufNormHiA, &shufSwapHiA); } for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m512i)) { __m512i swapped, result = _mm512_load_si512((__m512i*)(_dst+ptr)); gf16_shuffle2x_avx512_round1((__m512i*)(_src1+ptr*srcScale), &result, &swapped, shufNormLoA, shufNormHiA, shufSwapLoA, shufSwapHiA); if(srcCount >= 2) gf16_shuffle2x_avx512_round((__m512i*)(_src2+ptr*srcScale), &result, &swapped, shufNormLoB, shufNormHiB, shufSwapLoB, shufSwapHiB); if(srcCount >= 3) gf16_shuffle2x_avx512_round((__m512i*)(_src3+ptr*srcScale), &result, &swapped, shufNormLoC, shufNormHiC, shufSwapLoC, shufSwapHiC); if(srcCount >= 4) gf16_shuffle2x_avx512_round((__m512i*)(_src4+ptr*srcScale), &result, &swapped, shufNormLoD, shufNormHiD, shufSwapLoD, shufSwapHiD); if(srcCount >= 5) gf16_shuffle2x_avx512_round((__m512i*)(_src5+ptr*srcScale), &result, &swapped, shufNormLoE, shufNormHiE, shufSwapLoE, shufSwapHiE); if(srcCount >= 6) gf16_shuffle2x_avx512_round((__m512i*)(_src6+ptr*srcScale), &result, &swapped, shufNormLoF, shufNormHiF, shufSwapLoF, shufSwapHiF); swapped = _mm512_shuffle_i32x4(swapped, swapped, _MM_SHUFFLE(1,0,3,2)); result = _mm512_xor_si512(result, swapped); _mm512_store_si512((__m512i*)(_dst+ptr), result); if(doPrefetch == 1) _mm_prefetch(_pf+ptr, MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+ptr, _MM_HINT_T1); } } #endif // defined(_AVAILABLE) #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) && defined(PLATFORM_AMD64) GF16_MULADD_MULTI_FUNCS(gf16_shuffle2x, _avx512, gf16_shuffle2x_muladd_x_avx512, 6, sizeof(__m512i), 0, _mm256_zeroupper()) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_shuffle2x, _avx512) #endif void gf16_shuffle2x_muladd_avx512(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(_AVAILABLE) && !defined(PARPAR_SLIM_GF16) gf16_muladd_single(scratch, &gf16_shuffle2x_muladd_x_avx512, dst, src, len, val); _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #undef _AVAILABLE #undef MWORD_SIZE #undef _mword #undef _MM #undef _MMI #undef _FNSUFFIX #undef _MM_END par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle_neon.c000066400000000000000000000324001514221355600225420ustar00rootroot00000000000000 #include "gf16_neon_common.h" #include "gf16_muladd_multi.h" #if defined(__ARM_NEON) int gf16_available_neon = 1; #else int gf16_available_neon = 0; #endif // EOR3 could be useful, but few CPUs support it (Apple M1, ARM V1); supported in SVE2 instead, so perhaps defer this to an SVE implementation // BCAX might also be useful for a 5+5+5+1 bit approach (or maybe rely on predicated EOR) #if (GF16_POLYNOMIAL | 0x1f) == 0x1101f // enable special routine if targeting our default 0x1100b polynomial # define GF16_POLYNOMIAL_SIMPLE #endif #if defined(__ARM_NEON) && !defined(PARPAR_SLIM_GF16) static HEDLEY_ALWAYS_INLINE void gf16_shuffle_neon_calc_tables( #ifdef GF16_POLYNOMIAL_SIMPLE uint8x16_t polyIn, #else uint8x16x2_t polyIn, #endif uint16_t val, qtbl_t* tbl_l, qtbl_t* tbl_h) { int val2 = GF16_MULTBY_TWO(val); int val4 = GF16_MULTBY_TWO(val2); uint16x4_t tmp = vreinterpret_u16_u32(vdup_n_u32((uint32_t)val << 16)); uint16x4_t tmp2 = veor_u16(tmp, vdup_n_u16(val2)); tmp = vext_u16(tmp, tmp2, 2); uint8x16_t rl = vreinterpretq_u8_u16(vcombine_u16( tmp, veor_u16(tmp, vdup_n_u16(val4)) )); uint8x16_t rh = veorq_u8( rl, vreinterpretq_u8_u16(vdupq_n_u16(GF16_MULTBY_TWO(val4))) ); /* Idea of using PMULL, if it later is useful; an alternative idea would be to use PMUL and break everything into 4 bit chunks // mul by 2,4,6,8 uint16x4_t vval = vdup_n_u16(val); uint16x8_t prod = vreinterpretq_u16_p8(vmull_p8((poly8x8_t){2,2,4,4,6,6,8,8}, vreinterpret_p8_u16(vval))); uint16x4_t prodLo = vreinterpret_u16_u8(vmovn_u16(prod)); uint16x4_t prodHi = vreinterpret_u16_u8(vshrn_n_u16(prod, 8)); // prodHi contains excess bits from multiplication which need to be merged in // top byte needs reduction, bottom byte needs to be shifted right by 4 uint8x8_t reduction = vreinterpret_u8_p8(vmul_p8( vreinterpret_p8_p16(vdup_n_p16(((GF16_POLYNOMIAL & 0x1f) << 8) | 1)), vreinterpret_p8_u16(prodHi) )); reduction = vrev16_u8(reduction); prodLo = veor_u16(prodLo, vreinterpret_u16_u8(reduction)); prodLo = veor_u16( prodLo, vreinterpret_u16_p8(vmul_p8(vreinterpret_u8_p16(vdup_n_p16(0x1000)), prodHi)) ); uint16x8_t prod8 = vdupq_lane_u16(vreinterpret_u16_u8(prodLo), 3); prodLo = vext_u16(vdup_n_u8(0), prodLo, 3); uint16x4x2_t prod0 = vzip_u16(prodLo, veor_u16(prodLo, vval)); uint8x16_t rl = vreinterpretq_u8_u16(vcombine_u16(prod0.val[0], prod0.val[1])); uint8x16_t rh = veorq_u8( rl, vreinterpretq_u8_u16(prod8) ); */ uint8x16_t ri; /* uint16_t* multbl = (uint16_t*)(ltd->poly + 1); uint16x8_t factor0 = vreinterpret_u16_u8(vld1q_u8(multbl + ((val & 0xf) << 3))); factor0 = veorq_u16(factor0, vreinterpret_u16_u8(vld1q_u8(multbl + ((16 + ((val & 0xf0) >> 4)) << 3)))); factor0 = veorq_u16(factor0, vreinterpret_u16_u8(vld1q_u8(multbl + ((32 + ((val & 0xf00) >> 8)) << 3)))); factor0 = veorq_u16(factor0, vreinterpret_u16_u8(vld1q_u8(multbl + ((48 + ((val & 0xf000) >> 12)) << 3)))); uint16x8_t factor8 = vdupq_lane_u16(vget_low_u16(factor0), 0); factor0 = vsetq_lane_u16(0, factor0, 0); factor8 = veorq_u16(factor0, factor8); rl = vreinterpretq_u8_u16(factor0); rh = vreinterpretq_u8_u16(factor8); */ #ifdef __aarch64__ tbl_l[0] = vuzp1q_u8(rl, rh); tbl_h[0] = vuzp2q_u8(rl, rh); # ifdef GF16_POLYNOMIAL_SIMPLE #define MUL16(p, c) \ ri = vshrq_n_u8(tbl_h[p], 4); \ rl = vshlq_n_u8(tbl_l[p], 4); \ rh = veorq_u8(tbl_h[p], ri); \ rh = vshlq_n_u8(rh, 4); \ tbl_h[c] = vsriq_n_u8(rh, tbl_l[p], 4); \ tbl_l[c] = veorq_u8(rl, vqtbl1q_u8(polyIn, ri)) # else #define MUL16(p, c) \ ri = vshrq_n_u8(tbl_h[p], 4); \ rl = vshlq_n_u8(tbl_l[p], 4); \ rh = vshlq_n_u8(tbl_h[p], 4); \ rh = vsriq_n_u8(rh, tbl_l[p], 4); \ tbl_l[c] = veorq_u8(rl, vqtbl1q_u8(polyIn.val[0], ri)); \ tbl_h[c] = veorq_u8(rh, vqtbl1q_u8(polyIn.val[1], ri)) # endif #else uint8x16x2_t tbl = vuzpq_u8(rl, rh); tbl_l[0].val[0] = vget_low_u8(tbl.val[0]); tbl_l[0].val[1] = vget_high_u8(tbl.val[0]); tbl_h[0].val[0] = vget_low_u8(tbl.val[1]); tbl_h[0].val[1] = vget_high_u8(tbl.val[1]); # ifdef GF16_POLYNOMIAL_SIMPLE poly8x16_t poly_l = vreinterpretq_p8_u8(polyIn); #define MUL16(p, c) \ ri = vshrq_n_u8(tbl.val[1], 4); \ rl = vshlq_n_u8(tbl.val[0], 4); \ rh = veorq_u8(tbl.val[1], ri); \ rh = vshlq_n_u8(rh, 4); \ tbl.val[1] = vsriq_n_u8(rh, tbl.val[0], 4); \ tbl.val[0] = veorq_u8(rl, vreinterpretq_u8_p8(vmulq_p8(poly_l, vreinterpretq_p8_u8(ri)))); \ tbl_l[c].val[0] = vget_low_u8(tbl.val[0]); \ tbl_l[c].val[1] = vget_high_u8(tbl.val[0]); \ tbl_h[c].val[0] = vget_low_u8(tbl.val[1]); \ tbl_h[c].val[1] = vget_high_u8(tbl.val[1]) # else uint8x8x2_t poly_l = {{vget_low_u8(polyIn.val[0]), vget_high_u8(polyIn.val[0])}}; uint8x8x2_t poly_h = {{vget_low_u8(polyIn.val[1]), vget_high_u8(polyIn.val[1])}}; #define MUL16(p, c) \ ri = vshrq_n_u8(tbl.val[1], 4); \ rl = vshlq_n_u8(tbl.val[0], 4); \ rh = vshlq_n_u8(tbl.val[1], 4); \ rh = vsriq_n_u8(rh, tbl.val[0], 4); \ tbl.val[0] = veorq_u8(rl, vqtbl1q_u8(poly_l, ri)); \ tbl.val[1] = veorq_u8(rh, vqtbl1q_u8(poly_h, ri)); \ tbl_l[c].val[0] = vget_low_u8(tbl.val[0]); \ tbl_l[c].val[1] = vget_high_u8(tbl.val[0]); \ tbl_h[c].val[0] = vget_low_u8(tbl.val[1]); \ tbl_h[c].val[1] = vget_high_u8(tbl.val[1]) # endif #endif MUL16(0, 1); MUL16(1, 2); MUL16(2, 3); #undef MUL16 } static HEDLEY_ALWAYS_INLINE void gf16_shuffle_neon_round1(uint8x16x2_t va, uint8x16_t* rl, uint8x16_t* rh, qtbl_t* tbl_l, qtbl_t* tbl_h) { uint8x16_t loset = vdupq_n_u8(0xf); uint8x16_t tmp = vandq_u8(va.val[0], loset); *rl = vqtbl1q_u8(tbl_l[0], tmp); *rh = vqtbl1q_u8(tbl_h[0], tmp); tmp = vandq_u8(va.val[1], loset); *rl = veorq_u8(*rl, vqtbl1q_u8(tbl_l[2], tmp)); *rh = veorq_u8(*rh, vqtbl1q_u8(tbl_h[2], tmp)); va.val[0] = vshrq_n_u8(va.val[0], 4); va.val[1] = vshrq_n_u8(va.val[1], 4); *rl = veorq_u8(*rl, vqtbl1q_u8(tbl_l[1], va.val[0])); *rh = veorq_u8(*rh, vqtbl1q_u8(tbl_h[1], va.val[0])); *rl = veorq_u8(*rl, vqtbl1q_u8(tbl_l[3], va.val[1])); *rh = veorq_u8(*rh, vqtbl1q_u8(tbl_h[3], va.val[1])); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle_neon_round(uint8x16x2_t va, uint8x16_t* rl, uint8x16_t* rh, qtbl_t* tbl_l, qtbl_t* tbl_h) { uint8x16_t loset = vdupq_n_u8(0xf); uint8x16_t tmp = vandq_u8(va.val[0], loset); *rl = veorq_u8(*rl, vqtbl1q_u8(tbl_l[0], tmp)); *rh = veorq_u8(*rh, vqtbl1q_u8(tbl_h[0], tmp)); tmp = vandq_u8(va.val[1], loset); *rl = veorq_u8(*rl, vqtbl1q_u8(tbl_l[2], tmp)); *rh = veorq_u8(*rh, vqtbl1q_u8(tbl_h[2], tmp)); va.val[0] = vshrq_n_u8(va.val[0], 4); va.val[1] = vshrq_n_u8(va.val[1], 4); *rl = veorq_u8(*rl, vqtbl1q_u8(tbl_l[1], va.val[0])); *rh = veorq_u8(*rh, vqtbl1q_u8(tbl_h[1], va.val[0])); *rl = veorq_u8(*rl, vqtbl1q_u8(tbl_l[3], va.val[1])); *rh = veorq_u8(*rh, vqtbl1q_u8(tbl_h[3], va.val[1])); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle_muladd_x_neon( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(3); #ifdef GF16_POLYNOMIAL_SIMPLE uint8x16_t poly = vld1q_u8_align(scratch, 16); #else uint8x16x2_t poly = vld1q_u8_x2_align(scratch); #endif qtbl_t tbl_Ah[4], tbl_Al[4]; qtbl_t tbl_Bh[4], tbl_Bl[4]; qtbl_t tbl_Ch[4], tbl_Cl[4]; gf16_shuffle_neon_calc_tables(poly, coefficients[0], tbl_Al, tbl_Ah); if(srcCount > 1) gf16_shuffle_neon_calc_tables(poly, coefficients[1], tbl_Bl, tbl_Bh); if(srcCount > 2) gf16_shuffle_neon_calc_tables(poly, coefficients[2], tbl_Cl, tbl_Ch); uint8x16_t rl, rh; if(doPrefetch) { intptr_t ptr = -(intptr_t)len; if(doPrefetch == 1) PREFETCH_MEM(_pf+ptr, 1); if(doPrefetch == 2) PREFETCH_MEM(_pf+ptr, 0); while(ptr & (CACHELINE_SIZE-1)) { gf16_shuffle_neon_round1(vld2q_u8(_src1+ptr*srcScale), &rl, &rh, tbl_Al, tbl_Ah); if(srcCount > 1) gf16_shuffle_neon_round(vld2q_u8(_src2+ptr*srcScale), &rl, &rh, tbl_Bl, tbl_Bh); if(srcCount > 2) gf16_shuffle_neon_round(vld2q_u8(_src3+ptr*srcScale), &rl, &rh, tbl_Cl, tbl_Ch); uint8x16x2_t vb = vld2q_u8(_dst+ptr); vb.val[0] = veorq_u8(rl, vb.val[0]); vb.val[1] = veorq_u8(rh, vb.val[1]); vst2q_u8(_dst+ptr, vb); ptr += sizeof(uint8x16_t)*2; } while(ptr) { if(doPrefetch == 1) PREFETCH_MEM(_pf+ptr, 1); if(doPrefetch == 2) PREFETCH_MEM(_pf+ptr, 0); for(size_t iter=0; iter<(CACHELINE_SIZE/(sizeof(uint8x16_t)*2)); iter++) { gf16_shuffle_neon_round1(vld2q_u8(_src1+ptr*srcScale), &rl, &rh, tbl_Al, tbl_Ah); if(srcCount > 1) gf16_shuffle_neon_round(vld2q_u8(_src2+ptr*srcScale), &rl, &rh, tbl_Bl, tbl_Bh); if(srcCount > 2) gf16_shuffle_neon_round(vld2q_u8(_src3+ptr*srcScale), &rl, &rh, tbl_Cl, tbl_Ch); uint8x16x2_t vb = vld2q_u8(_dst+ptr); vb.val[0] = veorq_u8(rl, vb.val[0]); vb.val[1] = veorq_u8(rh, vb.val[1]); vst2q_u8(_dst+ptr, vb); ptr += sizeof(uint8x16_t)*2; } } } else { for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(uint8x16_t)*2) { gf16_shuffle_neon_round1(vld2q_u8(_src1+ptr*srcScale), &rl, &rh, tbl_Al, tbl_Ah); if(srcCount > 1) gf16_shuffle_neon_round(vld2q_u8(_src2+ptr*srcScale), &rl, &rh, tbl_Bl, tbl_Bh); if(srcCount > 2) gf16_shuffle_neon_round(vld2q_u8(_src3+ptr*srcScale), &rl, &rh, tbl_Cl, tbl_Ch); uint8x16x2_t vb = vld2q_u8(_dst+ptr); vb.val[0] = veorq_u8(rl, vb.val[0]); vb.val[1] = veorq_u8(rh, vb.val[1]); vst2q_u8(_dst+ptr, vb); } } } #endif /*defined(__ARM_NEON) && !defined(PARPAR_SLIM_GF16)*/ #ifdef PARPAR_INVERT_SUPPORT void gf16_shuffle_mul_neon(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__ARM_NEON) && !defined(PARPAR_SLIM_GF16) qtbl_t tbl_h[4], tbl_l[4]; #ifdef GF16_POLYNOMIAL_SIMPLE uint8x16_t poly = vld1q_u8_align(scratch, 16); #else uint8x16x2_t poly = vld1q_u8_x2_align(scratch); #endif gf16_shuffle_neon_calc_tables(poly, val, tbl_l, tbl_h); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(uint8x16_t)*2) { uint8x16x2_t r; gf16_shuffle_neon_round1(vld2q_u8(_src+ptr), &r.val[0], &r.val[1], tbl_l, tbl_h); vst2q_u8(_dst+ptr, r); } #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #endif void gf16_shuffle_muladd_neon(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__ARM_NEON) && !defined(PARPAR_SLIM_GF16) gf16_muladd_single(scratch, &gf16_shuffle_muladd_x_neon, dst, src, len, val); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #if defined(__ARM_NEON) && defined(__aarch64__) && !defined(PARPAR_SLIM_GF16) GF16_MULADD_MULTI_FUNCS(gf16_shuffle, _neon, gf16_shuffle_muladd_x_neon, 2, sizeof(uint8x16_t)*2, 0, (void)0) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_shuffle, _neon) #endif #if defined(__ARM_NEON) && !defined(PARPAR_SLIM_GF16) # ifdef __aarch64__ GF_PREPARE_PACKED_FUNCS(gf16_shuffle, _neon, sizeof(uint8x16x2_t), gf16_prepare_block_neon, gf16_prepare_blocku_neon, 2, (void)0, uint8x16_t checksum = vdupq_n_u8(0), gf16_checksum_block_neon, gf16_checksum_blocku_neon, gf16_checksum_exp_neon, gf16_checksum_prepare_neon, sizeof(uint8x16_t)) # else GF_PREPARE_PACKED_FUNCS(gf16_shuffle, _neon, sizeof(uint8x16x2_t), gf16_prepare_block_neon, gf16_prepare_blocku_neon, 1, (void)0, uint8x16_t checksum = vdupq_n_u8(0), gf16_checksum_block_neon, gf16_checksum_blocku_neon, gf16_checksum_exp_neon, gf16_checksum_prepare_neon, sizeof(uint8x16_t)) # endif #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_shuffle, _neon) #endif #if defined(__ARM_NEON) // NOTE: these are used in CLMul kernels as well GF_FINISH_PACKED_FUNCS(gf16_shuffle, _neon, sizeof(uint8x16x2_t), gf16_finish_block_neon, gf16_copy_blocku, 1, (void)0, gf16_checksum_block_neon, gf16_checksum_blocku_neon, gf16_checksum_exp_neon, NULL, sizeof(uint8x16_t)) #else GF_FINISH_PACKED_FUNCS_STUB(gf16_shuffle, _neon) #endif void* gf16_shuffle_init_arm(int polynomial) { #if defined(__ARM_NEON) && !defined(PARPAR_SLIM_GF16) uint8_t* ret; # ifdef GF16_POLYNOMIAL_SIMPLE if((polynomial | 0x1f) != 0x1101f) return NULL; ALIGN_ALLOC(ret, sizeof(uint8x16_t), 16); # ifndef __aarch64__ memset(ret, polynomial & 0x1f, sizeof(uint8x16_t)); return ret; # endif # else ALIGN_ALLOC(ret, sizeof(uint8x16x2_t), 32); # endif for(int i=0; i<16; i++) { int p = 0; if(i & 8) p ^= polynomial << 3; if(i & 4) p ^= polynomial << 2; if(i & 2) p ^= polynomial << 1; if(i & 1) p ^= polynomial << 0; ret[i] = p & 0xff; # ifndef GF16_POLYNOMIAL_SIMPLE ret[16+i] = (p>>8) & 0xff; # endif } return ret; /* uint16_t* multbl = (uint16_t*)(ltd->poly + 1); int shift; for(shift=0; shift<16; shift+=4) { for(i=0; i<16; i++) { int val = i << shift; int val2 = GF16_MULTBY_TWO(val); int val4 = GF16_MULTBY_TWO(val2); uint16x4_t tmp = {0, val, val2, val2 ^ val}; uint16x8_t r = vcombine_u16( tmp, veor_u16(tmp, vdup_n_u16(val4)) ); // put in *8 factor so we don't have to calculate it later r = vsetq_lane_u16(GF16_MULTBY_TWO(val4), r, 0); vst1q_u8(multbl + ((shift*4 + i) << 3), vreinterpret_u8_u16(r)); } } */ #else UNUSED(polynomial); return NULL; #endif } par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle_ssse3.c000066400000000000000000000100601514221355600226410ustar00rootroot00000000000000 #include "../src/platform.h" #define MWORD_SIZE 16 #define _mword __m128i #define _MM(f) _mm_ ## f #define _MMI(f) _mm_ ## f ## _si128 #define _FNSUFFIX _ssse3 #define _MM_END #if defined(__SSSE3__) # define _AVAILABLE #endif #include "gf16_shuffle_x86.h" #undef _AVAILABLE #undef MWORD_SIZE #undef _mword #undef _MM #undef _MMI #undef _FNSUFFIX #undef _MM_END #ifdef PARPAR_INVERT_SUPPORT static HEDLEY_ALWAYS_INLINE uint16_t gf16_shuffleX_replace_word(void* data, size_t index, uint16_t newValue, size_t width) { uint8_t* base = (uint8_t*)data + (index & ~(width-1)) * 2; unsigned pos = index & (width-1); if(width > 16) pos = (pos & 7) | ((pos & ((width/2)-8)) << 1) | ((pos & (width/2)) ? 8 : 0); // handle awkward positioning due to avoiding cross-lane shuffles uint16_t oldValue = base[pos + width] | (base[pos] << 8); base[pos + width] = newValue & 0xff; base[pos] = newValue >> 8; return oldValue; } static HEDLEY_ALWAYS_INLINE uint16_t gf16_shuffle2X_replace_word(void* data, size_t index, uint16_t newValue, size_t width) { uint8_t* base = (uint8_t*)data + (index & ~(width-1)) * 2; unsigned pos = index & (width-1); uint16_t oldValue = base[pos] | (base[pos + width] << 8); base[pos] = newValue & 0xff; base[pos + width] = newValue >> 8; return oldValue; } uint16_t gf16_affine2x_replace_word(void* data, size_t index, uint16_t newValue) { return gf16_shuffle2X_replace_word(data, index, newValue, 8); } uint16_t gf16_shuffle16_replace_word(void* data, size_t index, uint16_t newValue) { return gf16_shuffleX_replace_word(data, index, newValue, 16); } uint16_t gf16_shuffle32_replace_word(void* data, size_t index, uint16_t newValue) { return gf16_shuffleX_replace_word(data, index, newValue, 32); } uint16_t gf16_shuffle64_replace_word(void* data, size_t index, uint16_t newValue) { return gf16_shuffleX_replace_word(data, index, newValue, 64); } uint16_t gf16_shuffle2x16_replace_word(void* data, size_t index, uint16_t newValue) { return gf16_shuffle2X_replace_word(data, index, newValue, 16); } uint16_t gf16_shuffle2x32_replace_word(void* data, size_t index, uint16_t newValue) { return gf16_shuffle2X_replace_word(data, index, newValue, 32); } #endif void* gf16_shuffle_init_x86(int polynomial) { #if defined(__SSSE3__) /* generate polynomial table stuff */ uint16_t _poly[16]; __m128i tmp1, tmp2; __m128i* ret; #ifdef GF16_POLYNOMIAL_SIMPLE if((polynomial | 0x1f) != 0x1101f) return NULL; ALIGN_ALLOC(ret, sizeof(__m128i), 16); #else ALIGN_ALLOC(ret, sizeof(__m128i)*2, 32); #endif for(int i=0; i<16; i++) { int p = 0; if(i & 8) p ^= polynomial << 3; if(i & 4) p ^= polynomial << 2; if(i & 2) p ^= polynomial << 1; if(i & 1) p ^= polynomial << 0; _poly[i] = p & 0xffff; } tmp1 = _mm_loadu_si128((__m128i*)_poly); tmp2 = _mm_loadu_si128((__m128i*)_poly + 1); tmp1 = _mm_shuffle_epi8(tmp1, _mm_set_epi32(0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200)); tmp2 = _mm_shuffle_epi8(tmp2, _mm_set_epi32(0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200)); _mm_store_si128(ret, _mm_unpacklo_epi64(tmp1, tmp2)); #ifndef GF16_POLYNOMIAL_SIMPLE _mm_store_si128(ret + 1, _mm_unpackhi_epi64(tmp1, tmp2)); #endif return ret; #else UNUSED(polynomial); return NULL; #endif /* factor tables - currently not used __m128i* multbl = (__m128i*)(ltd->poly + 1); int shift, i; for(shift=0; shift<16; shift+=4) { for(i=0; i<16; i++) { int val = i << shift; int val2 = GF_MULTBY_TWO(val); int val4 = GF_MULTBY_TWO(val2); __m128i tmp = _mm_cvtsi32_si128(val << 16); tmp = _mm_insert_epi16(tmp, val2, 2); tmp = _mm_insert_epi16(tmp, val2 ^ val, 3); tmp = _mm_shuffle_epi32(tmp, 0x44); tmp = _mm_xor_si128(tmp, _mm_shufflehi_epi16( _mm_insert_epi16(_mm_setzero_si128(), val4, 4), 0 )); // put in *8 factor so we don't have to calculate it later tmp = _mm_srli_si128(tmp, 2); // could be eliminated by byte shuffle below, if I really cared tmp = _mm_insert_epi16(tmp, GF_MULTBY_TWO(val4), 7); _mm_store_si128(multbl + shift*4 + i, _mm_shuffle_epi8(tmp, _mm_set_epi8(15, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 0))); } } */ } par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle_vbmi.c000066400000000000000000000436051514221355600225510ustar00rootroot00000000000000 #include "gf16_global.h" #include "../src/platform.h" #include "gf16_muladd_multi.h" #if defined(__AVX512VBMI__) && defined(__AVX512VL__) # define MWORD_SIZE 64 # define _mword __m512i # define _MM(f) _mm512_ ## f # define _MMI(f) _mm512_ ## f ## _si512 # define _FNSUFFIX _vbmi # define _AVAILABLE_AVX 1 # define _AVAILABLE 1 # include "gf16_shuffle_x86_common.h" # include "gf16_shuffle_x86_prepare.h" # include "gf16_checksum_x86.h" # undef _AVAILABLE # undef _AVAILABLE_AVX # undef _FNSUFFIX # undef _MMI # undef _MM # undef _mword # undef MWORD_SIZE int gf16_shuffle_available_vbmi = 1; static HEDLEY_ALWAYS_INLINE __m128i gf16_vec128_mul2(__m128i v) { return _mm_ternarylogic_epi32( _mm_add_epi16(v, v), _mm_cmpgt_epi16(_mm_setzero_si128(), v), _mm_set1_epi16(GF16_POLYNOMIAL & 0xffff), 0x78 // (a^(b&c)) ); } static HEDLEY_ALWAYS_INLINE void mul64_vec(__m512i mulLo, __m512i mulHi, __m512i srcLo, __m512i srcHi, __m512i* dstLo, __m512i *dstHi) { __m512i ti = _mm512_srli_epi16(srcHi, 2); __m512i th = _mm512_ternarylogic_epi32( _mm512_srli_epi16(srcLo, 2), _mm512_set1_epi8(0x3f), _mm512_slli_epi16(srcHi, 6), 0xE2 ); *dstLo = _mm512_ternarylogic_epi32( _mm512_permutexvar_epi8(ti, mulLo), _mm512_set1_epi8(0x3f), _mm512_slli_epi16(srcLo, 6), 0xD2 ); *dstHi = _mm512_xor_si512(th, _mm512_permutexvar_epi8(ti, mulHi)); } static HEDLEY_ALWAYS_INLINE void deinterleave_bytes(__m512i a, __m512i b, __m512i* lo, __m512i* hi) { __m512i idx0 = _mm512_set_epi32( 0x7e7c7a78, 0x76747270, 0x6e6c6a68, 0x66646260, 0x5e5c5a58, 0x56545250, 0x4e4c4a48, 0x46444240, 0x3e3c3a38, 0x36343230, 0x2e2c2a28, 0x26242220, 0x1e1c1a18, 0x16141210, 0x0e0c0a08, 0x06040200 ); __m512i idx1 = _mm512_or_si512(idx0, _mm512_set1_epi8(1)); __m512i res1 = _mm512_permutex2var_epi8(a, idx0, b); __m512i res2 = _mm512_permutex2var_epi8(a, idx1, b); *lo = res1; *hi = res2; } static HEDLEY_ALWAYS_INLINE void generate_first_lookup(uint16_t val, __m512i* lo0, __m512i* hi0) { __m128i tmp, prod; initial_mul_vector(val, &tmp, &prod); tmp = _mm_unpacklo_epi64(tmp, _mm_xor_si128(tmp, prod)); // products 0-7 // 4*2 = 8 prod = gf16_vec128_mul2(prod); __m256i tmp2 = _mm256_inserti128_si256( // products 0-15 _mm256_castsi128_si256(tmp), _mm_xor_si128(tmp, prod), 1 ); // 8*2 = 16 prod = gf16_vec128_mul2(prod); __m256i prod2 = _mm256_inserti128_si256(_mm256_castsi128_si256(prod), prod, 1); __m512i tmp4 = _mm512_inserti64x4( // products 0-31 _mm512_castsi256_si512(tmp2), _mm256_xor_si256(tmp2, prod2), 1 ); // 16*2 = 32 prod = gf16_vec128_mul2(prod); // products 32-63 __m512i tmp4b = _mm512_xor_si512(tmp4, _mm512_shuffle_i32x4(_mm512_castsi128_si512(prod), _mm512_castsi128_si512(prod), 0)); // extract low/high pairs deinterleave_bytes(tmp4, tmp4b, lo0, hi0); } static HEDLEY_ALWAYS_INLINE void generate_first_lookup_x2(const uint16_t* coefficients, __m512i* lo0A, __m512i* hi0A, __m512i* lo0B, __m512i* hi0B) { __m256i prod0, mul8; gf16_initial_mul_vector_x2(coefficients, &prod0, &mul8); __m256i prod8 = _mm256_xor_si256(prod0, mul8); __m512i prod08 = _mm512_inserti64x4( // products 0-7,0-7,8-15,8-15 _mm512_castsi256_si512(prod0), prod8, 1 ); // 8*2 = 16 __m256i mul16 = gf16_vec256_mul2(mul8); __m512i mul16b = _mm512_inserti64x4(_mm512_castsi256_si512(mul16), mul16, 1); __m512i prod16 = _mm512_xor_si512(prod08, mul16b); // products 16-23,16-23,24-31,24-31 // 16*2 = 32 __m512i mul32 = gf16_vec512_mul2(mul16b); __m512i prod32 = _mm512_xor_si512(prod08, mul32); // products 32-39 (x2),40-47 (x2) __m512i prod48 = _mm512_xor_si512(prod16, mul32); // products 48-55 (x2),56-63 (x2) // mix to get desired output prod08 = separate_low_high512(prod08); prod16 = separate_low_high512(prod16); prod32 = separate_low_high512(prod32); prod48 = separate_low_high512(prod48); __m512i prod0Lo = _mm512_unpacklo_epi64(prod08, prod16); // 0-7,16-23 | 0-7,16-23 | 8-15,24-31 | 8-15,24-31 __m512i prod0Hi = _mm512_unpackhi_epi64(prod08, prod16); __m512i prod32Lo = _mm512_unpacklo_epi64(prod32, prod48); __m512i prod32Hi = _mm512_unpackhi_epi64(prod32, prod48); __m512i idx0 = _mm512_set_epi64(13,9,12,8,5,1,4,0); __m512i idx1 = _mm512_set_epi64(15,11,14,10,7,3,6,2); *lo0A = _mm512_permutex2var_epi64(prod0Lo, idx0, prod32Lo); *hi0A = _mm512_permutex2var_epi64(prod0Hi, idx0, prod32Hi); *lo0B = _mm512_permutex2var_epi64(prod0Lo, idx1, prod32Lo); *hi0B = _mm512_permutex2var_epi64(prod0Hi, idx1, prod32Hi); } static HEDLEY_ALWAYS_INLINE void generate_first_lookup_x4(const uint16_t* coefficients, const int do4, __m512i* lo0A, __m512i* hi0A, __m512i* lo0B, __m512i* hi0B, __m512i* lo0C, __m512i* hi0C, __m512i* lo0D, __m512i* hi0D) { __m512i prod0, mul8; gf16_initial_mul_vector_x4(coefficients, &prod0, &mul8, do4); __m512i prod8 = _mm512_xor_si512(prod0, mul8); // 8*2 = 16 __m512i mul16 = gf16_vec512_mul2(mul8); __m512i prod16 = _mm512_xor_si512(prod0, mul16); // products 16-23 __m512i prod24 = _mm512_xor_si512(prod8, mul16); // products 24-31 // 16*2 = 32 __m512i mul32 = gf16_vec512_mul2(mul16); __m512i prod32 = _mm512_xor_si512(prod0, mul32); __m512i prod40 = _mm512_xor_si512(prod8, mul32); __m512i prod48 = _mm512_xor_si512(prod16, mul32); __m512i prod56 = _mm512_xor_si512(prod24, mul32); // mix to get desired output prod0 = separate_low_high512(prod0); prod8 = separate_low_high512(prod8); prod16 = separate_low_high512(prod16); prod24 = separate_low_high512(prod24); prod32 = separate_low_high512(prod32); prod40 = separate_low_high512(prod40); prod48 = separate_low_high512(prod48); prod56 = separate_low_high512(prod56); __m512i prod0Lo = _mm512_unpacklo_epi64(prod0, prod8); __m512i prod0Hi = _mm512_unpackhi_epi64(prod0, prod8); __m512i prod16Lo = _mm512_unpacklo_epi64(prod16, prod24); __m512i prod16Hi = _mm512_unpackhi_epi64(prod16, prod24); __m512i prod32Lo = _mm512_unpacklo_epi64(prod32, prod40); __m512i prod32Hi = _mm512_unpackhi_epi64(prod32, prod40); __m512i prod48Lo = _mm512_unpacklo_epi64(prod48, prod56); __m512i prod48Hi = _mm512_unpackhi_epi64(prod48, prod56); __m512i prod0LoA = _mm512_shuffle_i32x4(prod0Lo, prod16Lo, _MM_SHUFFLE(2,0,2,0)); __m512i prod0LoB = _mm512_shuffle_i32x4(prod0Lo, prod16Lo, _MM_SHUFFLE(3,1,3,1)); __m512i prod0HiA = _mm512_shuffle_i32x4(prod0Hi, prod16Hi, _MM_SHUFFLE(2,0,2,0)); __m512i prod0HiB = _mm512_shuffle_i32x4(prod0Hi, prod16Hi, _MM_SHUFFLE(3,1,3,1)); __m512i prod32LoA = _mm512_shuffle_i32x4(prod32Lo, prod48Lo, _MM_SHUFFLE(2,0,2,0)); __m512i prod32LoB = _mm512_shuffle_i32x4(prod32Lo, prod48Lo, _MM_SHUFFLE(3,1,3,1)); __m512i prod32HiA = _mm512_shuffle_i32x4(prod32Hi, prod48Hi, _MM_SHUFFLE(2,0,2,0)); __m512i prod32HiB = _mm512_shuffle_i32x4(prod32Hi, prod48Hi, _MM_SHUFFLE(3,1,3,1)); *lo0A = _mm512_shuffle_i32x4(prod0LoA, prod32LoA, _MM_SHUFFLE(2,0,2,0)); *hi0A = _mm512_shuffle_i32x4(prod0HiA, prod32HiA, _MM_SHUFFLE(2,0,2,0)); *lo0B = _mm512_shuffle_i32x4(prod0LoB, prod32LoB, _MM_SHUFFLE(2,0,2,0)); *hi0B = _mm512_shuffle_i32x4(prod0HiB, prod32HiB, _MM_SHUFFLE(2,0,2,0)); *lo0C = _mm512_shuffle_i32x4(prod0LoA, prod32LoA, _MM_SHUFFLE(3,1,3,1)); *hi0C = _mm512_shuffle_i32x4(prod0HiA, prod32HiA, _MM_SHUFFLE(3,1,3,1)); if(do4) { *lo0D = _mm512_shuffle_i32x4(prod0LoB, prod32LoB, _MM_SHUFFLE(3,1,3,1)); *hi0D = _mm512_shuffle_i32x4(prod0HiB, prod32HiB, _MM_SHUFFLE(3,1,3,1)); } } static HEDLEY_ALWAYS_INLINE void generate_remaining_lookup(__m512i mulLo, __m512i mulHi, __m512i lo0, __m512i hi0, __m512i* lo1, __m512i* hi1, __m512i* lo2, __m512i* hi2) { // multiply by 64 to get 0,64,128..960 repeated; will need some rearrangement __m512i tmpLo, tmpHi; mul64_vec(mulLo, mulHi, lo0, hi0, &tmpLo, &tmpHi); // rearrange for bit swap (the two bits are flipped around) *lo1 = _mm512_castsi128_si512(_mm_shuffle_epi8( _mm512_castsi512_si128(tmpLo), _mm_set_epi32(0x0f0b0703, 0x0e0a0602, 0x0d090501, 0x0c080400) )); *lo1 = _mm512_shuffle_i32x4(*lo1, *lo1, 0); *hi1 = _mm512_castsi128_si512(_mm_shuffle_epi8( _mm512_castsi512_si128(tmpHi), _mm_set_epi32(0x0f0b0703, 0x0e0a0602, 0x0d090501, 0x0c080400) )); *hi1 = _mm512_shuffle_i32x4(*hi1, *hi1, 0); // then mul above by 16 to get 0,1024,2048..64512 #ifndef GF16_POLYNOMIAL_SIMPLE mul16_vec4x(_mm512_shuffle_i32x4(mulLo, mulLo, 0), _mm512_shuffle_i32x4(mulHi, mulHi, 0), tmpLo, tmpHi, lo2, hi2); #else mul16_vec4x(_mm512_shuffle_i32x4(mulLo, mulLo, 0), _mm512_setzero_si512(), tmpLo, tmpHi, lo2, hi2); #endif } static HEDLEY_ALWAYS_INLINE void gf16_shuffle_mul_vbmi_round(__m512i ta, __m512i tb, __m512i lo0, __m512i hi0, __m512i lo1, __m512i hi1, __m512i lo2, __m512i hi2, __m512i* tpl, __m512i* tph) { // get straddled component (bottom 2 bits of ta, followed by top 2 bits from tb) __m512i ti = _mm512_ternarylogic_epi32( _mm512_srli_epi16(tb, 4), ta, _mm512_set1_epi8(3), 0xd8 // bit-select: ((b&c) | (a&~c)) ); // can replace 2x vpermb with 2x vpshufb if an AND is applied to ti // this does, however, require an additional register; we can avoid this by changing the bit arrangement from 6+4+6 to 6+6+4, since the 0xf mask can be used twice in this case ta = _mm512_srli_epi16(ta, 2); *tph = _mm512_ternarylogic_epi32( _mm512_permutexvar_epi8(tb, hi0), _mm512_permutexvar_epi8(ti, hi1), _mm512_permutexvar_epi8(ta, hi2), 0x96 // double-XOR: (a^b^c) ); *tpl = _mm512_ternarylogic_epi32( _mm512_permutexvar_epi8(tb, lo0), _mm512_permutexvar_epi8(ti, lo1), _mm512_permutexvar_epi8(ta, lo2), 0x96 ); } static HEDLEY_ALWAYS_INLINE void gf16_shuffle_mul_vbmi_round_merge(__m512i ta, __m512i tb, __m512i lo0, __m512i hi0, __m512i lo1, __m512i hi1, __m512i lo2, __m512i hi2, __m512i* tpl, __m512i* tph, __m512i* tl, __m512i* th) { // get straddled component (bottom 2 bits of ta, followed by top 2 bits from tb) __m512i ti = _mm512_ternarylogic_epi32( _mm512_srli_epi16(tb, 4), ta, _mm512_set1_epi8(3), 0xd8 // bit-select: ((b&c) | (a&~c)) ); ta = _mm512_srli_epi16(ta, 2); *tph = _mm512_ternarylogic_epi32( *tph, _mm512_permutexvar_epi8(tb, hi0), _mm512_permutexvar_epi8(ti, hi1), 0x96 // double-XOR: (a^b^c) ); *tpl = _mm512_ternarylogic_epi32( *tpl, _mm512_permutexvar_epi8(tb, lo0), _mm512_permutexvar_epi8(ti, lo1), 0x96 ); *th = _mm512_permutexvar_epi8(ta, hi2); *tl = _mm512_permutexvar_epi8(ta, lo2); } #else int gf16_shuffle_available_vbmi = 0; #endif #if defined(__AVX512VBMI__) && defined(__AVX512VL__) static HEDLEY_ALWAYS_INLINE void gf16_shuffle_muladd_x_vbmi( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { GF16_MULADD_MULTI_SRC_UNUSED(4); __m512i mulLo = _mm512_load_si512((__m512i*)scratch + 1); __m512i mulHi = _mm512_load_si512((__m512i*)scratch); __m512i loA0, loA1, loA2, hiA0, hiA1, hiA2; __m512i loB0, loB1, loB2, hiB0, hiB1, hiB2; __m512i loC0, loC1, loC2, hiC0, hiC1, hiC2; __m512i loD0, loD1, loD2, hiD0, hiD1, hiD2; // get first lookup if(srcCount == 4) generate_first_lookup_x4(coefficients, 1, &loA0, &hiA0, &loB0, &hiB0, &loC0, &hiC0, &loD0, &hiD0); else if(srcCount == 3) generate_first_lookup_x4(coefficients, 0, &loA0, &hiA0, &loB0, &hiB0, &loC0, &hiC0, NULL, NULL); else if(srcCount == 2) generate_first_lookup_x2(coefficients, &loA0, &hiA0, &loB0, &hiB0); else // srcCount == 1 generate_first_lookup(coefficients[0], &loA0, &hiA0); // multiply by 64/16 to get remaining lookups generate_remaining_lookup(mulLo, mulHi, loA0, hiA0, &loA1, &hiA1, &loA2, &hiA2); if(srcCount > 1) generate_remaining_lookup(mulLo, mulHi, loB0, hiB0, &loB1, &hiB1, &loB2, &hiB2); if(srcCount > 2) generate_remaining_lookup(mulLo, mulHi, loC0, hiC0, &loC1, &hiC1, &loC2, &hiC2); if(srcCount > 3) generate_remaining_lookup(mulLo, mulHi, loD0, hiD0, &loD1, &hiD1, &loD2, &hiD2); for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m512i)*2) { __m512i tpl, tph; gf16_shuffle_mul_vbmi_round( _mm512_load_si512((__m512i*)(_src1+ptr*srcScale)), _mm512_load_si512((__m512i*)(_src1+ptr*srcScale) +1), loA0, hiA0, loA1, hiA1, loA2, hiA2, &tpl, &tph ); __m512i tl, th; if(srcCount > 1) { gf16_shuffle_mul_vbmi_round_merge( _mm512_load_si512((__m512i*)(_src2+ptr*srcScale)), _mm512_load_si512((__m512i*)(_src2+ptr*srcScale) +1), loB0, hiB0, loB1, hiB1, loB2, hiB2, &tpl, &tph, &tl, &th ); tph = _mm512_ternarylogic_epi32(tph, th, _mm512_load_si512((__m512i*)(_dst+ptr)), 0x96); tpl = _mm512_ternarylogic_epi32(tpl, tl, _mm512_load_si512((__m512i*)(_dst+ptr) + 1), 0x96); } else { th = _mm512_load_si512((__m512i*)(_dst+ptr)); tl = _mm512_load_si512((__m512i*)(_dst+ptr) + 1); } if(srcCount > 2) { gf16_shuffle_mul_vbmi_round_merge( _mm512_load_si512((__m512i*)(_src3+ptr*srcScale)), _mm512_load_si512((__m512i*)(_src3+ptr*srcScale) +1), loC0, hiC0, loC1, hiC1, loC2, hiC2, &tpl, &tph, &tl, &th ); } if(srcCount > 3) { __m512i ta = _mm512_load_si512((__m512i*)(_src4+ptr*srcScale)); __m512i tb = _mm512_load_si512((__m512i*)(_src4+ptr*srcScale) +1); tph = _mm512_ternarylogic_epi32( tph, th, _mm512_permutexvar_epi8(tb, hiD0), 0x96 // double-XOR: (a^b^c) ); tpl = _mm512_ternarylogic_epi32( tpl, tl, _mm512_permutexvar_epi8(tb, loD0), 0x96 ); __m512i ti = _mm512_ternarylogic_epi32( _mm512_srli_epi16(tb, 4), ta, _mm512_set1_epi8(3), 0xd8 // bit-select: ((b&c) | (a&~c)) ); ta = _mm512_srli_epi16(ta, 2); tpl = _mm512_ternarylogic_epi32( tpl, _mm512_permutexvar_epi8(ti, loD1), _mm512_permutexvar_epi8(ta, loD2), 0x96 ); tph = _mm512_ternarylogic_epi32( tph, _mm512_permutexvar_epi8(ti, hiD1), _mm512_permutexvar_epi8(ta, hiD2), 0x96 ); } if(srcCount & 1) { tpl = _mm512_xor_si512(tpl, tl); tph = _mm512_xor_si512(tph, th); } _mm512_store_si512((__m512i*)(_dst+ptr), tph); _mm512_store_si512((__m512i*)(_dst+ptr) + 1, tpl); if(doPrefetch == 1) { _mm_prefetch(_pf+(ptr>>1), MM_HINT_WT1); } if(doPrefetch == 2) { _mm_prefetch(_pf+(ptr>>1), _MM_HINT_T1); } } } #endif #ifdef PARPAR_INVERT_SUPPORT void gf16_shuffle_mul_vbmi(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__AVX512VBMI__) && defined(__AVX512VL__) __m512i lo0, lo1, lo2, hi0, hi1, hi2; // get first lookup generate_first_lookup(val, &lo0, &hi0); // multiply by 64/16 to get remaining lookups generate_remaining_lookup( _mm512_load_si512((__m512i*)scratch + 1), _mm512_load_si512((__m512i*)scratch), lo0, hi0, &lo1, &hi1, &lo2, &hi2 ); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m512i)*2) { __m512i tpl, tph; gf16_shuffle_mul_vbmi_round( _mm512_load_si512((__m512i*)(_src+ptr)), _mm512_load_si512((__m512i*)(_src+ptr) +1), lo0, hi0, lo1, hi1, lo2, hi2, &tpl, &tph ); _mm512_store_si512((__m512i*)(_dst+ptr), tph); _mm512_store_si512((__m512i*)(_dst+ptr) + 1, tpl); } _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #endif void gf16_shuffle_muladd_vbmi(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #if defined(__AVX512VBMI__) && defined(__AVX512VL__) gf16_muladd_single(scratch, &gf16_shuffle_muladd_x_vbmi, dst, src, len, val); _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } void gf16_shuffle_muladd_prefetch_vbmi(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch) { UNUSED(mutScratch); #if defined(__AVX512VBMI__) && defined(__AVX512VL__) gf16_muladd_prefetch_single(scratch, &gf16_shuffle_muladd_x_vbmi, dst, src, len, val, prefetch); _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); UNUSED(prefetch); #endif } #if defined(__AVX512VBMI__) && defined(__AVX512VL__) && defined(PLATFORM_AMD64) GF16_MULADD_MULTI_FUNCS(gf16_shuffle, _vbmi, gf16_shuffle_muladd_x_vbmi, 4, sizeof(__m512i)*2, 1, _mm256_zeroupper()) #else GF16_MULADD_MULTI_FUNCS_STUB(gf16_shuffle, _vbmi) #endif #if defined(__AVX512VBMI__) && defined(__AVX512VL__) # ifdef PLATFORM_AMD64 GF_PREPARE_PACKED_FUNCS(gf16_shuffle, _vbmi, sizeof(__m512i)*2, gf16_shuffle_prepare_block_vbmi, gf16_shuffle_prepare_blocku_vbmi, 4, _mm256_zeroupper(), __m512i checksum = _mm512_setzero_si512(), gf16_checksum_block_vbmi, gf16_checksum_blocku_vbmi, gf16_checksum_exp_vbmi, gf16_checksum_prepare_vbmi, sizeof(__m512i)) # else GF_PREPARE_PACKED_FUNCS(gf16_shuffle, _vbmi, sizeof(__m512i)*2, gf16_shuffle_prepare_block_vbmi, gf16_shuffle_prepare_blocku_vbmi, 1, _mm256_zeroupper(), __m512i checksum = _mm512_setzero_si512(), gf16_checksum_block_vbmi, gf16_checksum_blocku_vbmi, gf16_checksum_exp_vbmi, gf16_checksum_prepare_vbmi, sizeof(__m512i)) # endif #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_shuffle, _vbmi) #endif void* gf16_shuffle_init_vbmi(int polynomial) { #if defined(__AVX512VBMI__) && defined(__AVX512VL__) /* generate polynomial table stuff */ uint16_t _poly[64]; __m512i tmp1, tmp2; __m512i* ret; ALIGN_ALLOC(ret, sizeof(__m512i)*2, 64); for(int i=0; i<64; i++) { int p = i << 16; if(p & 0x200000) p ^= polynomial << 5; if(p & 0x100000) p ^= polynomial << 4; if(p & 0x080000) p ^= polynomial << 3; if(p & 0x040000) p ^= polynomial << 2; if(p & 0x020000) p ^= polynomial << 1; if(p & 0x010000) p ^= polynomial << 0; _poly[i] = p & 0xffff; } tmp1 = _mm512_loadu_si512((__m512i*)_poly); tmp2 = _mm512_loadu_si512((__m512i*)_poly + 1); deinterleave_bytes(tmp1, tmp2, &tmp1, &tmp2); _mm512_store_si512(ret + 1, tmp1); _mm512_store_si512(ret, tmp2); return ret; #else UNUSED(polynomial); return NULL; #endif } par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle_x86.h000066400000000000000000000252051514221355600222420ustar00rootroot00000000000000 #include "gf16_shuffle_x86_common.h" #include "gf16_global.h" #ifdef _AVAILABLE # include "gf16_checksum_x86.h" int _FN(gf16_shuffle_available) = 1; #include "gf16_shuffle_x86_prepare.h" #else int _FN(gf16_shuffle_available) = 0; #endif #ifdef PARPAR_INVERT_SUPPORT void _FN(gf16_shuffle_prepare)(void* dst, const void* src, size_t srcLen) { #ifdef _AVAILABLE gf16_prepare(dst, src, srcLen, sizeof(_mword)*2, &_FN(gf16_shuffle_prepare_block), &_FN(gf16_shuffle_prepare_blocku)); _MM_END #else UNUSED(dst); UNUSED(src); UNUSED(srcLen); #endif } #endif #ifdef _AVAILABLE # if MWORD_SIZE==64 && defined(PLATFORM_AMD64) GF_PREPARE_PACKED_FUNCS(gf16_shuffle, _FNSUFFIX, sizeof(_mword)*2, _FN(gf16_shuffle_prepare_block), _FN(gf16_shuffle_prepare_blocku), 3, _MM_END, _mword checksum = _MMI(setzero)(), _FN(gf16_checksum_block), _FN(gf16_checksum_blocku), _FN(gf16_checksum_exp), _FN(gf16_checksum_prepare), sizeof(_mword)) # else GF_PREPARE_PACKED_FUNCS(gf16_shuffle, _FNSUFFIX, sizeof(_mword)*2, _FN(gf16_shuffle_prepare_block), _FN(gf16_shuffle_prepare_blocku), 1, _MM_END, _mword checksum = _MMI(setzero)(), _FN(gf16_checksum_block), _FN(gf16_checksum_blocku), _FN(gf16_checksum_exp), _FN(gf16_checksum_prepare), sizeof(_mword)) # endif #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_shuffle, _FNSUFFIX) #endif #ifdef PARPAR_INVERT_SUPPORT void _FN(gf16_shuffle_finish)(void *HEDLEY_RESTRICT dst, size_t len) { #ifdef _AVAILABLE gf16_finish(dst, len, sizeof(_mword)*2, &_FN(gf16_shuffle_finish_block)); _MM_END #else UNUSED(dst); UNUSED(len); #endif } #endif #ifdef _AVAILABLE GF_FINISH_PACKED_FUNCS(gf16_shuffle, _FNSUFFIX, sizeof(_mword)*2, _FN(gf16_shuffle_finish_copy_block), _FN(gf16_shuffle_finish_copy_blocku), 1, _MM_END, _FN(gf16_checksum_block), _FN(gf16_checksum_blocku), _FN(gf16_checksum_exp), &_FN(gf16_shuffle_finish_block), sizeof(_mword)) #else GF_FINISH_PACKED_FUNCS_STUB(gf16_shuffle, _FNSUFFIX) #endif #if MWORD_SIZE >= 32 # ifdef _AVAILABLE # ifdef GF16_POLYNOMIAL_SIMPLE static HEDLEY_ALWAYS_INLINE __m256i mul16_vec256(__m128i poly, __m256i src) { __m256i prodHi = _mm256_andnot_si256(_mm256_set1_epi8(0xf), src); __m256i idx = _mm256_srli_epi16(prodHi, 4); __m256i merge = _mm256_inserti128_si256(prodHi, _mm_shuffle_epi8(poly, _mm256_castsi256_si128(idx)), 1); # if MWORD_SIZE == 64 src = _mm256_ternarylogic_epi32( extract_top128_256(idx), _mm256_set1_epi8(0xf), _mm256_slli_epi16(src, 4), 0xF2 ); # else __m256i prodLo = _mm256_slli_epi16(_mm256_and_si256(_mm256_set1_epi8(0xf), src), 4); src = _mm256_or_si256(prodLo, extract_top128_256(idx)); # endif return _mm256_xor_si256(src, merge); } # else // GF16_POLYNOMIAL_SIMPLE static HEDLEY_ALWAYS_INLINE __m256i mul16_vec256(__m256i poly, __m256i src) { __m256i prodHi = _mm256_and_si256(_mm256_set1_epi8(0xf), _mm256_srli_epi16(src, 4)); __m256i idx = _mm256_inserti128_si256(prodHi, _mm256_castsi256_si128(prodHi), 1); # if MWORD_SIZE == 64 src = _mm256_ternarylogic_epi32( extract_top128_256(prodHi), _mm256_set1_epi8(0xf), _mm256_slli_epi16(src, 4), 0xF2 ); # else __m256i prodLo = _mm256_slli_epi16(_mm256_and_si256(_mm256_set1_epi8(0xf), src), 4); src = _mm256_or_si256(prodLo, extract_top128_256(prodHi)); # endif return _mm256_xor_si256(src, _mm256_shuffle_epi8(poly, idx)); // another idea with AVX512 is to keep each 4-bit part of the 16-bit results in a 128-bit lane, and shuffle lanes to handle the shift } # endif # endif # if MWORD_SIZE == 64 # define BCAST_HI(v) _mm512_shuffle_i32x4(_mm512_castsi256_si512(v), _mm512_castsi256_si512(v), _MM_SHUFFLE(1,1,1,1)) # define BCAST_LO(v) _mm512_shuffle_i32x4(_mm512_castsi256_si512(v), _mm512_castsi256_si512(v), _MM_SHUFFLE(0,0,0,0)) # else # define BCAST_HI(v) _mm256_permute4x64_epi64(v, _MM_SHUFFLE(3,2,3,2)) # define BCAST_LO(v) _mm256_inserti128_si256(v, _mm256_castsi256_si128(v), 1) # endif #endif #ifdef _AVAILABLE static HEDLEY_ALWAYS_INLINE void gf16_shuffle_setup_vec(const void *HEDLEY_RESTRICT scratch, uint16_t val, _mword* low0, _mword* high0, _mword* low1, _mword* high1, _mword* low2, _mword* high2, _mword* low3, _mword* high3) { __m128i pd0, pd1; shuf0_vector(val, &pd0, &pd1); #if MWORD_SIZE >= 32 # ifdef GF16_POLYNOMIAL_SIMPLE __m128i poly = _mm_load_si128((__m128i*)scratch); # else __m256i poly = _mm256_load_si256((__m256i*)scratch); # endif __m256i prod = _mm256_inserti128_si256(_mm256_castsi128_si256(pd0), pd1, 1); prod = _mm256_shuffle_epi8(prod, _mm256_set_epi32( 0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200, 0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200 )); prod = _mm256_permute4x64_epi64(prod, _MM_SHUFFLE(2,0,3,1)); *low0 = BCAST_HI(prod); *high0 = BCAST_LO(prod); prod = mul16_vec256(poly, prod); *low1 = BCAST_HI(prod); *high1 = BCAST_LO(prod); prod = mul16_vec256(poly, prod); *low2 = BCAST_HI(prod); *high2 = BCAST_LO(prod); prod = mul16_vec256(poly, prod); *low3 = BCAST_HI(prod); *high3 = BCAST_LO(prod); #else pd0 = _mm_shuffle_epi8(pd0, _mm_set_epi32(0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200)); pd1 = _mm_shuffle_epi8(pd1, _mm_set_epi32(0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200)); *low0 = _mm_unpacklo_epi64(pd0, pd1); *high0 = _mm_unpackhi_epi64(pd0, pd1); __m128i polyl = _mm_load_si128((__m128i*)scratch); __m128i polyh = _mm_setzero_si128(); #ifndef GF16_POLYNOMIAL_SIMPLE polyh = _mm_load_si128((__m128i*)scratch + 1); #endif mul16_vec128(polyl, polyh, *low0, *high0, low1, high1); mul16_vec128(polyl, polyh, *low1, *high1, low2, high2); mul16_vec128(polyl, polyh, *low2, *high2, low3, high3); #endif } static HEDLEY_ALWAYS_INLINE void gf16_shuffle_muladd_round(_mword* _dst, _mword* _src, _mword low0, _mword high0, _mword low1, _mword high1, _mword low2, _mword high2, _mword low3, _mword high3) { _mword mask = _MM(set1_epi8) (0x0f); _mword ta = _MMI(load)(_src); _mword tb = _MMI(load)(_src+1); _mword ti = _MMI(and) (mask, tb); _mword tph = _MM(shuffle_epi8) (high0, ti); _mword tpl = _MM(shuffle_epi8) (low0, ti); ti = _MM_SRLI4_EPI8(tb); #if MWORD_SIZE == 64 tpl = _mm512_ternarylogic_epi32(tpl, _MM(shuffle_epi8) (low1, ti), _MMI(load)(_dst+1), 0x96); tph = _mm512_ternarylogic_epi32(tph, _MM(shuffle_epi8) (high1, ti), _MMI(load)(_dst), 0x96); ti = _MMI(and) (mask, ta); _mword ti2 = _MMI(and) (mask, _MM(srli_epi16)(ta, 4)); tpl = _mm512_ternarylogic_epi32(tpl, _MM(shuffle_epi8) (low2, ti), _MM(shuffle_epi8) (low3, ti2), 0x96); tph = _mm512_ternarylogic_epi32(tph, _MM(shuffle_epi8) (high2, ti), _MM(shuffle_epi8) (high3, ti2), 0x96); #else tpl = _MMI(xor)(_MM(shuffle_epi8) (low1, ti), tpl); tph = _MMI(xor)(_MM(shuffle_epi8) (high1, ti), tph); tph = _MMI(xor)(tph, _MMI(load)(_dst)); tpl = _MMI(xor)(tpl, _MMI(load)(_dst+1)); ti = _MMI(and) (mask, ta); tpl = _MMI(xor)(_MM(shuffle_epi8) (low2, ti), tpl); tph = _MMI(xor)(_MM(shuffle_epi8) (high2, ti), tph); ti = _MM_SRLI4_EPI8(ta); tpl = _MMI(xor)(_MM(shuffle_epi8) (low3, ti), tpl); tph = _MMI(xor)(_MM(shuffle_epi8) (high3, ti), tph); #endif _MMI(store) (_dst, tph); _MMI(store) (_dst+1, tpl); } #endif #ifdef PARPAR_INVERT_SUPPORT void _FN(gf16_shuffle_mul)(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #ifdef _AVAILABLE _mword low0, low1, low2, low3, high0, high1, high2, high3; gf16_shuffle_setup_vec(scratch, val, &low0, &high0, &low1, &high1, &low2, &high2, &low3, &high3); _mword mask = _MM(set1_epi8) (0x0f); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(_mword)*2) { _mword ta = _MMI(load)((_mword*)(_src+ptr)); _mword tb = _MMI(load)((_mword*)(_src+ptr) + 1); _mword ti = _MMI(and) (mask, tb); _mword tph = _MM(shuffle_epi8) (high0, ti); _mword tpl = _MM(shuffle_epi8) (low0, ti); ti = _MM_SRLI4_EPI8(tb); #if MWORD_SIZE == 64 _mword ti2 = _MMI(and) (mask, ta); tpl = _mm512_ternarylogic_epi32(tpl, _MM(shuffle_epi8) (low1, ti), _MM(shuffle_epi8) (low2, ti2), 0x96); tph = _mm512_ternarylogic_epi32(tph, _MM(shuffle_epi8) (high1, ti), _MM(shuffle_epi8) (high2, ti2), 0x96); #else tpl = _MMI(xor)(_MM(shuffle_epi8) (low1, ti), tpl); tph = _MMI(xor)(_MM(shuffle_epi8) (high1, ti), tph); ti = _MMI(and) (mask, ta); tpl = _MMI(xor)(_MM(shuffle_epi8) (low2, ti), tpl); tph = _MMI(xor)(_MM(shuffle_epi8) (high2, ti), tph); #endif ti = _MM_SRLI4_EPI8(ta); tpl = _MMI(xor)(_MM(shuffle_epi8) (low3, ti), tpl); tph = _MMI(xor)(_MM(shuffle_epi8) (high3, ti), tph); _MMI(store) ((_mword*)(_dst+ptr), tph); _MMI(store) ((_mword*)(_dst+ptr) + 1, tpl); } _MM_END #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #endif void _FN(gf16_shuffle_muladd)(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #ifdef _AVAILABLE _mword low0, low1, low2, low3, high0, high1, high2, high3; gf16_shuffle_setup_vec(scratch, val, &low0, &high0, &low1, &high1, &low2, &high2, &low3, &high3); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(_mword)*2) { gf16_shuffle_muladd_round((_mword*)(_dst+ptr), (_mword*)(_src+ptr), low0, high0, low1, high1, low2, high2, low3, high3); } _MM_END #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } void _FN(gf16_shuffle_muladd_prefetch)(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch) { UNUSED(mutScratch); #ifdef _AVAILABLE _mword low0, low1, low2, low3, high0, high1, high2, high3; gf16_shuffle_setup_vec(scratch, val, &low0, &high0, &low1, &high1, &low2, &high2, &low3, &high3); uint8_t* _src = (uint8_t*)src + len; uint8_t* _dst = (uint8_t*)dst + len; char* _pf = (char*)prefetch + len/2; intptr_t ptr = -(intptr_t)len; // we'll prefetch at half-rate (one cacheline per 128 bytes), as it seems to work best // initial alignment _mm_prefetch(_pf+(ptr>>1), _MM_HINT_T1); while(ptr & 127) { gf16_shuffle_muladd_round((_mword*)(_dst+ptr), (_mword*)(_src+ptr), low0, high0, low1, high1, low2, high2, low3, high3); ptr += sizeof(_mword)*2; } while(ptr) { _mm_prefetch(_pf+(ptr>>1), _MM_HINT_T1); for(unsigned round=0; round<64/sizeof(_mword); round++) { gf16_shuffle_muladd_round((_mword*)(_dst+ptr), (_mword*)(_src+ptr), low0, high0, low1, high1, low2, high2, low3, high3); ptr += sizeof(_mword)*2; } } _MM_END #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(prefetch); UNUSED(val); #endif } par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle_x86_common.h000066400000000000000000000233121514221355600236070ustar00rootroot00000000000000#ifndef _GF16_SHUFFLE_X86_COMMON_ #define _GF16_SHUFFLE_X86_COMMON_ #include "gf16_global.h" #include "../src/platform.h" #if (GF16_POLYNOMIAL | 0x1f) == 0x1101f // enable special routine if targeting our default 0x1100b polynomial # define GF16_POLYNOMIAL_SIMPLE #endif #define GF16_MULTBY_TWO_X2(p) ((((p) << 1) & 0xffffffff) ^ ((GF16_POLYNOMIAL ^ ((GF16_POLYNOMIAL&0xffff) << 16)) & -((p) >> 31))) #ifdef __SSSE3__ static HEDLEY_ALWAYS_INLINE void initial_mul_vector(uint16_t val, __m128i* prod, __m128i* prod4) { uint32_t val1 = (uint32_t)val << 16; uint32_t val2 = val1 | val; val2 = GF16_MULTBY_TWO_X2(val2); __m128i tmp = _mm_cvtsi32_si128(val1); #if defined(_AVAILABLE_AVX) || (MWORD_SIZE >= 32 && defined(_AVAILABLE)) *prod = _mm_insert_epi32(tmp, val2 ^ val1, 1); #else *prod = _mm_unpacklo_epi32(tmp, _mm_cvtsi32_si128(val2 ^ val1)); #endif *prod4 = _mm_set1_epi32(GF16_MULTBY_TWO_X2(val2)); } #endif #ifdef _AVAILABLE static HEDLEY_ALWAYS_INLINE void shuf0_vector(uint16_t val, __m128i* prod0, __m128i* prod8) { __m128i tmp, vval4; initial_mul_vector(val, &tmp, &vval4); *prod0 = _mm_unpacklo_epi64(tmp, _mm_xor_si128(tmp, vval4)); // multiply by 2 and add prod0 to give prod8 __m128i poly = _mm_and_si128(_mm_set1_epi16(GF16_POLYNOMIAL & 0xffff), _mm_cmpgt_epi16( _mm_setzero_si128(), vval4 )); #if MWORD_SIZE == 64 *prod8 = _mm_ternarylogic_epi32( _mm_add_epi16(vval4, vval4), poly, *prod0, 0x96 ); #else *prod8 = _mm_xor_si128( _mm_add_epi16(vval4, vval4), _mm_xor_si128(*prod0, poly) ); #endif /* // although the following seems simpler, it doesn't actually seem to be faster, although I don't know why uint8_t* multbl = (uint8_t*)scratch + sizeof(__m128i)*2; __m128i factor0 = _mm_load_si128((__m128i*)multbl + (val & 0xf)); factor0 = _mm_xor_si128(factor0, _mm_load_si128((__m128i*)(multbl + (val & 0xf0)) + 16)); factor0 = _mm_xor_si128(factor0, _mm_load_si128((__m128i*)(multbl + ((val>>4) & 0xf0)) + 32)); factor0 = _mm_xor_si128(factor0, _mm_load_si128((__m128i*)(multbl + ((val>>8) & 0xf0)) + 48)); __m128i factor8 = _mm_shuffle_epi8(factor0, _mm_set_epi32(0x0f0f0f0f, 0x0f0f0f0f, 0x07070707, 0x07070707)); factor0 = _mm_slli_epi64(factor0, 8); factor8 = _mm_xor_si128(factor0, factor8); low0 = BCAST(_mm_unpacklo_epi64(factor0, factor8)); high0 = BCAST(_mm_unpackhi_epi64(factor0, factor8)); */ } static HEDLEY_ALWAYS_INLINE _mword separate_low_high(_mword data) { // MSVC < 2019 doesn't seem to like #if or defines inside _mm512_set*, so we expand it manually #if MWORD_SIZE == 64 return _MM(shuffle_epi8)(data, _mm512_set4_epi32(0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200)); #else return _MM(shuffle_epi8)(data, _MM(set_epi32)( # if MWORD_SIZE >= 32 0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200, # endif 0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200 )); #endif } #if MWORD_SIZE == 16 && defined(_AVAILABLE_XOP) #define _MM_SRLI4_EPI8(v) _mm_shl_epi8(v, _mm_set1_epi8(-4)) #define _MM_SLLI4_EPI8(v) _mm_shl_epi8(v, _mm_set1_epi8(4)) #else #define _MM_SRLI4_EPI8(v) _MMI(and)(_MM(srli_epi16)(v, 4), _MM(set1_epi8)(0xf)) #define _MM_SLLI4_EPI8(v) _MM(slli_epi16)(_MMI(and)(v, _MM(set1_epi8)(0xf)), 4) #endif #if MWORD_SIZE >= 32 static HEDLEY_ALWAYS_INLINE void mul16_vec2x(__m256i mulLo, __m256i mulHi, __m256i srcLo, __m256i srcHi, __m256i* dstLo, __m256i *dstHi) { __m256i ti = _mm256_and_si256(_mm256_srli_epi16(srcHi, 4), _mm256_set1_epi8(0xf)); #ifdef GF16_POLYNOMIAL_SIMPLE srcHi = _mm256_xor_si256(srcHi, ti); #endif #if MWORD_SIZE == 64 __m256i th = _mm256_ternarylogic_epi32( _mm256_srli_epi16(srcLo, 4), _mm256_set1_epi8(0xf), _mm256_slli_epi16(srcHi, 4), 0xE2 ); *dstLo = _mm256_ternarylogic_epi32( _mm256_shuffle_epi8(mulLo, ti), _mm256_set1_epi8(0xf), _mm256_slli_epi16(srcLo, 4), 0xD2 ); #else __m256i tl = _mm256_slli_epi16(_mm256_and_si256(_mm256_set1_epi8(0xf), srcLo), 4); __m256i th = _mm256_slli_epi16(_mm256_and_si256(_mm256_set1_epi8(0xf), srcHi), 4); th = _mm256_or_si256(th, _mm256_and_si256(_mm256_srli_epi16(srcLo, 4), _mm256_set1_epi8(0xf))); *dstLo = _mm256_xor_si256(tl, _mm256_shuffle_epi8(mulLo, ti)); #endif #ifdef GF16_POLYNOMIAL_SIMPLE *dstHi = th; UNUSED(mulHi); #else *dstHi = _mm256_xor_si256(th, _mm256_shuffle_epi8(mulHi, ti)); #endif } static HEDLEY_ALWAYS_INLINE __m256i gf16_vec256_mul2(__m256i v) { # if MWORD_SIZE == 64 return _mm256_ternarylogic_epi32( _mm256_add_epi16(v, v), _mm256_cmpgt_epi16(_mm256_setzero_si256(), v), _mm256_set1_epi16(GF16_POLYNOMIAL & 0xffff), 0x78 // (a^(b&c)) ); # else return _mm256_xor_si256( _mm256_add_epi16(v, v), _mm256_and_si256(_mm256_set1_epi16(GF16_POLYNOMIAL & 0xffff), _mm256_cmpgt_epi16( _mm256_setzero_si256(), v )) ); # endif } #endif #if defined(_AVAILABLE_XOP) #define _MM128_SRLI4_EPI8(v) _mm_shl_epi8(v, _mm_set1_epi8(-4)) #define _MM128_SLLI4_EPI8(v) _mm_shl_epi8(v, _mm_set1_epi8(4)) #else #define _MM128_SRLI4_EPI8(v) _mm_and_si128(_mm_srli_epi16(v, 4), _mm_set1_epi8(0xf)) #define _MM128_SLLI4_EPI8(v) _mm_slli_epi16(_mm_and_si128(v, _mm_set1_epi8(0xf)), 4) #endif static HEDLEY_ALWAYS_INLINE void mul16_vec128(__m128i mulLo, __m128i mulHi, __m128i srcLo, __m128i srcHi, __m128i* dstLo, __m128i *dstHi) { __m128i ti = _MM128_SRLI4_EPI8(srcHi); __m128i sh = srcHi; // MSVC x86 seems to have an odd bug where the parent's variable (e.g. high0/1/2/3) can be modified if srcHi is modified; we work around this by explicitly assigning it to something else #ifdef GF16_POLYNOMIAL_SIMPLE sh = _mm_xor_si128(sh, ti); #endif #if MWORD_SIZE == 64 __m128i th = _mm_ternarylogic_epi32( _mm_srli_epi16(srcLo, 4), _mm_set1_epi8(0xf), _mm_slli_epi16(sh, 4), 0xE2 ); *dstLo = _mm_ternarylogic_epi32( _mm_shuffle_epi8(mulLo, ti), _mm_set1_epi8(0xf), _mm_slli_epi16(srcLo, 4), 0xD2 ); #else __m128i tl = _MM128_SLLI4_EPI8(srcLo); __m128i th = _MM128_SLLI4_EPI8(sh); th = _mm_or_si128(th, _MM128_SRLI4_EPI8(srcLo)); *dstLo = _mm_xor_si128(tl, _mm_shuffle_epi8(mulLo, ti)); #endif #ifdef GF16_POLYNOMIAL_SIMPLE *dstHi = th; UNUSED(mulHi); #else *dstHi = _mm_xor_si128(th, _mm_shuffle_epi8(mulHi, ti)); #endif } #if MWORD_SIZE == 64 static HEDLEY_ALWAYS_INLINE void mul16_vec4x(__m512i mulLo, __m512i mulHi, __m512i srcLo, __m512i srcHi, __m512i* dstLo, __m512i *dstHi) { __m512i ti = _mm512_and_si512(_mm512_srli_epi16(srcHi, 4), _mm512_set1_epi8(0xf)); #ifdef GF16_POLYNOMIAL_SIMPLE srcHi = _mm512_xor_si512(srcHi, ti); #endif __m512i th = _mm512_ternarylogic_epi32( _mm512_srli_epi16(srcLo, 4), _mm512_set1_epi8(0xf), _mm512_slli_epi16(srcHi, 4), 0xE2 ); *dstLo = _mm512_ternarylogic_epi32( _mm512_shuffle_epi8(mulLo, ti), _mm512_set1_epi8(0xf), _mm512_slli_epi16(srcLo, 4), 0xD2 ); #ifdef GF16_POLYNOMIAL_SIMPLE *dstHi = th; UNUSED(mulHi); #else *dstHi = _mm512_xor_si512(th, _mm512_shuffle_epi8(mulHi, ti)); #endif } static HEDLEY_ALWAYS_INLINE __m512i separate_low_high512(__m512i v) { return _mm512_shuffle_epi8(v, _mm512_set4_epi32(0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200)); } static HEDLEY_ALWAYS_INLINE __m512i gf16_vec512_mul2(__m512i v) { return _mm512_ternarylogic_epi32( _mm512_add_epi16(v, v), _mm512_srai_epi16(v, 15), _mm512_set1_epi16(GF16_POLYNOMIAL & 0xffff), 0x78 // (a^(b&c)) ); } static HEDLEY_ALWAYS_INLINE void gf16_initial_mul_vector_x2(const uint16_t* coefficients, __m256i* prod0, __m256i* mul8) { *prod0 = _mm256_shuffle_epi8( _mm256_broadcastd_epi32(_mm_cvtsi32_si128(read32(coefficients))), _mm256_set_epi32( 0x03020302, 0x03020302, 0x03020302, 0x03020302, 0x01000100, 0x01000100, 0x01000100, 0x01000100 ) ); #if GF16_POLYNOMIAL & 0xe000 // we assume top three bits aren't set # error Polynomial unsupported #endif __mmask8 mul4poly = _mm256_test_epi32_mask(*prod0, _mm256_set1_epi32(0x4000)); __mmask8 mul8poly = _mm256_test_epi32_mask(*prod0, _mm256_set1_epi32(0x2000)); __m256i poly = _mm256_set1_epi16(GF16_POLYNOMIAL & 0xffff); __m256i mul2 = gf16_vec256_mul2(*prod0); __m256i mul4 = _mm256_add_epi16(mul2, mul2); mul4 = _mm256_mask_xor_epi32(mul4, mul4poly, mul4, poly); *prod0 = _mm256_slli_epi32(*prod0, 16); // 10101010 *prod0 = _mm256_mask_xor_epi32(*prod0, 0xaa, mul2, *prod0); // 32103210 *prod0 = _mm256_mask_xor_epi64(*prod0, 0xaa, mul4, *prod0); // 76543210 *mul8 = _mm256_add_epi16(mul4, mul4); *mul8 = _mm256_mask_xor_epi32(*mul8, mul8poly, *mul8, poly); } static HEDLEY_ALWAYS_INLINE void gf16_initial_mul_vector_x4(const uint16_t* coefficients, __m512i* prod0, __m512i* mul8, const int do4) { __m128i coeff; if(do4) coeff = _mm_loadl_epi64((__m128i*)coefficients); else coeff = _mm_insert_epi16(_mm_cvtsi32_si128(read32(coefficients)), coefficients[2], 2); *prod0 = _mm512_shuffle_epi8(_mm512_broadcastq_epi64(coeff), _mm512_set_epi32( 0x07060706, 0x07060706, 0x07060706, 0x07060706, 0x05040504, 0x05040504, 0x05040504, 0x05040504, 0x03020302, 0x03020302, 0x03020302, 0x03020302, 0x01000100, 0x01000100, 0x01000100, 0x01000100 )); #if GF16_POLYNOMIAL & 0xe000 // we assume top three bits aren't set # error Polynomial unsupported #endif __mmask16 mul4poly = _mm512_test_epi32_mask(*prod0, _mm512_set1_epi32(0x4000)); __mmask16 mul8poly = _mm512_test_epi32_mask(*prod0, _mm512_set1_epi32(0x2000)); __m512i poly = _mm512_set1_epi16(GF16_POLYNOMIAL & 0xffff); __m512i mul2 = gf16_vec512_mul2(*prod0); __m512i mul4 = _mm512_add_epi16(mul2, mul2); mul4 = _mm512_mask_xor_epi32(mul4, mul4poly, mul4, poly); *prod0 = _mm512_slli_epi32(*prod0, 16); // 10101010 *prod0 = _mm512_mask_xor_epi32(*prod0, 0xaaaa, mul2, *prod0); // 32103210 *prod0 = _mm512_mask_xor_epi64(*prod0, 0xaa, mul4, *prod0); // 76543210 *mul8 = _mm512_add_epi16(mul4, mul4); *mul8 = _mm512_mask_xor_epi32(*mul8, mul8poly, *mul8, poly); } #endif #endif // defined(_AVAILABLE) #endif // defined(_GF16_SHUFFLE_X86_COMMON_) par2cmdline-turbo-1.4.0/parpar/gf16/gf16_shuffle_x86_prepare.h000066400000000000000000000041651514221355600237620ustar00rootroot00000000000000 #include "gf16_shuffle_x86_common.h" #include "gf16_checksum_x86.h" static HEDLEY_ALWAYS_INLINE void _FN(gf16_shuffle_prepare_block)(void* dst, const void* src) { _mword ta = _MMI(loadu)((_mword*)src); _mword tb = _MMI(loadu)((_mword*)src + 1); ta = separate_low_high(ta); tb = separate_low_high(tb); _MMI(store)((_mword*)dst, _MM(unpackhi_epi64)(ta, tb) ); _MMI(store)((_mword*)dst + 1, _MM(unpacklo_epi64)(ta, tb) ); } // final block static HEDLEY_ALWAYS_INLINE void _FN(gf16_shuffle_prepare_blocku)(void* dst, const void* src, size_t remaining) { _mword ta, tb; if(remaining & sizeof(_mword)) ta = _MMI(loadu)((_mword*)src); else ta = partial_load(src, remaining); ta = separate_low_high(ta); if(remaining <= sizeof(_mword)) tb = _MMI(setzero)(); else { tb = partial_load((char*)src + sizeof(_mword), remaining - sizeof(_mword)); tb = separate_low_high(tb); } _MMI(store)((_mword*)dst, _MM(unpackhi_epi64)(ta, tb) ); _MMI(store)((_mword*)dst + 1, _MM(unpacklo_epi64)(ta, tb) ); } static HEDLEY_ALWAYS_INLINE void _FN(gf16_shuffle_finish_block)(void *HEDLEY_RESTRICT dst) { _mword ta = _MMI(load)((_mword*)dst); _mword tb = _MMI(load)((_mword*)dst + 1); _MMI(store)((_mword*)dst, _MM(unpacklo_epi8)(tb, ta)); _MMI(store)((_mword*)dst + 1, _MM(unpackhi_epi8)(tb, ta)); } static HEDLEY_ALWAYS_INLINE void _FN(gf16_shuffle_finish_copy_block)(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { _mword ta = _MMI(load)((_mword*)src); _mword tb = _MMI(load)((_mword*)src + 1); _MMI(storeu)((_mword*)dst, _MM(unpacklo_epi8)(tb, ta)); _MMI(storeu)((_mword*)dst + 1, _MM(unpackhi_epi8)(tb, ta)); } static HEDLEY_ALWAYS_INLINE void _FN(gf16_shuffle_finish_copy_blocku)(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t bytes) { _mword ta = _MMI(load)((_mword*)src); _mword tb = _MMI(load)((_mword*)src + 1); _mword a = _MM(unpacklo_epi8)(tb, ta); _mword b = _MM(unpackhi_epi8)(tb, ta); if(bytes & sizeof(_mword)) { _MMI(storeu)((_mword*)dst, a); bytes ^= sizeof(_mword); if(bytes) partial_store((_mword*)dst + 1, b, bytes); } else partial_store(dst, a, bytes); } par2cmdline-turbo-1.4.0/parpar/gf16/gf16_sve_common.h000066400000000000000000000046211514221355600222450ustar00rootroot00000000000000#ifndef __GF16_SVE_COMMON_H #define __GF16_SVE_COMMON_H #include "gf16_global.h" #include "../src/platform.h" #if defined(__ARM_FEATURE_SVE) #define NOMASK(f, ...) f ## _x(svptrue_b8(), __VA_ARGS__) // headers # include static HEDLEY_ALWAYS_INLINE svint16_t gf16_vec_mul2_sve(svint16_t v) { return sveor_n_s16_m( svcmplt_n_s16(svptrue_b16(), v, 0), NOMASK(svadd_s16, v, v), GF16_POLYNOMIAL & 0xffff ); } // copying prepare block for both shuffle/clmul static HEDLEY_ALWAYS_INLINE void gf16_prepare_block_sve(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { svst1_u8(svptrue_b8(), dst, svld1_u8(svptrue_b8(), src)); svst1_vnum_u8(svptrue_b8(), dst, 1, svld1_vnum_u8(svptrue_b8(), src, 1)); } static HEDLEY_ALWAYS_INLINE void gf16_prepare_half_block_sve(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { svst1_u8(svptrue_b8(), dst, svld1_u8(svptrue_b8(), src)); } // final block static HEDLEY_ALWAYS_INLINE void gf16_prepare_blocku_sve(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t remaining) { svst1_u8(svptrue_b8(), dst, svld1_u8(svwhilelt_b8((uint64_t)0, (uint64_t)remaining), src)); svst1_vnum_u8(svptrue_b8(), dst, 1, svld1_vnum_u8(svwhilelt_b8((uint64_t)svcntb(), (uint64_t)remaining), src, 1)); } static HEDLEY_ALWAYS_INLINE void gf16_prepare_half_blocku_sve(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t remaining) { svst1_u8(svptrue_b8(), dst, svld1_u8(svwhilelt_b8((uint64_t)0, (uint64_t)remaining), src)); } static HEDLEY_ALWAYS_INLINE void gf16_finish_blocku_sve(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t remaining) { svst1_u8(svwhilelt_b8((uint64_t)0, (uint64_t)remaining), dst, svld1_u8(svptrue_b8(), src)); svst1_vnum_u8(svwhilelt_b8((uint64_t)svcntb(), (uint64_t)remaining), dst, 1, svld1_vnum_u8(svptrue_b8(), src, 1)); } static HEDLEY_ALWAYS_INLINE void gf16_finish_half_blocku_sve(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t remaining) { svst1_u8(svwhilelt_b8((uint64_t)0, (uint64_t)remaining), dst, svld1_u8(svptrue_b8(), src)); } static HEDLEY_ALWAYS_INLINE void gf16_checksum_prepare_sve(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT checksum, const size_t blockLen, gf16_transform_block_rst prepareBlock) { ALIGN_TO(16, int16_t tmp[blockLen/2]); memset(tmp, 0, blockLen); svst1_s16(svptrue_b16(), tmp, *(svint16_t*)checksum); prepareBlock(dst, tmp); } #endif #endifpar2cmdline-turbo-1.4.0/parpar/gf16/gf16_xor.h000066400000000000000000000074311514221355600207120ustar00rootroot00000000000000 #include "../src/hedley.h" #define FUNCS(v) \ void* gf16_xor_jit_init_##v(int polynomial, int jitOptStrat); \ void* gf16_xor_jit_init_mut_##v(); \ void gf16_xor_prepare_packed_cksum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); \ void gf16_xor_prepare_partial_packsum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen, size_t partOffset, size_t partLen); \ int gf16_xor_finish_packed_cksum_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen); \ int gf16_xor_finish_partial_packsum_##v(void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen, size_t partOffset, size_t partLen); \ void gf16_xor_jit_muladd_##v(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); \ void gf16_xor_jit_muladd_prefetch_##v(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch); \ extern int gf16_xor_available_##v FUNCS(sse2); FUNCS(avx2); FUNCS(avx512); #undef FUNCS #ifdef PARPAR_INVERT_SUPPORT #define FUNCS(v) \ void gf16_xor_prepare_##v(void* dst, const void* src, size_t srcLen); \ void gf16_xor_finish_##v(void *HEDLEY_RESTRICT dst, size_t len); \ void gf16_xor_jit_mul_##v(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) FUNCS(sse2); FUNCS(avx2); FUNCS(avx512); #undef FUNCS #endif #ifdef PARPAR_INCLUDE_BASIC_OPS #define FUNCS(v) \ void gf16_xor_prepare_packed_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); \ void gf16_xor_finish_packed_##v(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen) FUNCS(sse2); FUNCS(avx2); FUNCS(avx512); #undef FUNCS #endif void gf16_xor_jit_muladd_multi_avx512(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); void gf16_xor_jit_muladd_multi_packed_avx512(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); void gf16_xor_jit_uninit(void* scratch); // non-JIT version void* gf16_xor_init_sse2(int polynomial); #ifdef PARPAR_INVERT_SUPPORT void gf16_xor_mul_sse2(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); #endif void gf16_xor_muladd_sse2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); // JIT strategies for current processor #define GF16_XOR_JIT_STRAT_NONE 0 #define GF16_XOR_JIT_STRAT_COPYNT 1 #define GF16_XOR_JIT_STRAT_COPY 2 #define GF16_XOR_JIT_STRAT_CLR 3 #ifdef PARPAR_INVERT_SUPPORT uint16_t gf16_xor16_replace_word(void* data, size_t index, uint16_t newValue); uint16_t gf16_xor32_replace_word(void* data, size_t index, uint16_t newValue); uint16_t gf16_xor64_replace_word(void* data, size_t index, uint16_t newValue); #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16_xor_avx2.c000066400000000000000000000645421514221355600216530ustar00rootroot00000000000000 #define _GF16_XORJIT_COPY_ALIGN 32 #include "gf16_xor_common.h" #undef _GF16_XORJIT_COPY_ALIGN #include #if defined(__AVX2__) && defined(PLATFORM_AMD64) int gf16_xor_available_avx2 = 1; #else int gf16_xor_available_avx2 = 0; #endif #if defined(__AVX2__) && defined(PLATFORM_AMD64) ALIGN_TO(16, __m128i xor256_jit_clut_code1[64]); ALIGN_TO(16, uint8_t xor256_jit_clut_info_mem[64]); ALIGN_TO(16, __m64 xor256_jit_nums[128]); ALIGN_TO(16, __m64 xor256_jit_rmask[128]); static int xor256_jit_created = 0; static void gf16_xor_create_jit_lut_avx2(void) { uint_fast32_t i; int j; if(xor256_jit_created) return; xor256_jit_created = 1; memset(xor256_jit_clut_code1, 0, sizeof(xor256_jit_clut_code1)); for(i=0; i<64; i++) { int m = (i&1) | ((i&8)>>2) | ((i&2)<<1) | ((i&16)>>1) | ((i&4)<<2) | (i&32); /* interleave bits */ uint_fast8_t posM = 0; uint8_t* pC = (uint8_t*)(xor256_jit_clut_code1 + i); for(j=0; j<3; j++) { int msk = m&3; if(msk) { int reg = msk-1; /* if we ever support 32-bit, need to ensure that vpxor/load is fixed length */ pC += _jit_vpxor_m(pC, reg, reg, AX, lshift32(j-4, 5)); /* advance pointers */ posM += 5; } m >>= 2; } xor256_jit_clut_info_mem[i] = posM; } memset(xor256_jit_nums, 255, sizeof(xor256_jit_nums)); memset(xor256_jit_rmask, 0, sizeof(xor256_jit_rmask)); for(i=0; i<128; i++) { uint8_t* nums = (uint8_t*)(xor256_jit_nums + i), * rmask = (uint8_t*)(xor256_jit_rmask + i); for(j=0; j<8; j++) { if(i & (1< 2) { *jitptr += _jit_vpxor_m(*jitptr, reg, (uint_fast8_t)highest, AX, lowest*32-128); } else if(highest >= 0) { *jitptr += _jit_vmovdqa_load(*jitptr, reg, AX, highest*32-128); *jitptr += _jit_vpxor_m(*jitptr, reg, reg, AX, lowest*32-128); } else *jitptr += _jit_vmovdqa_load(*jitptr, reg, AX, lowest*32-128); } else { if(highest >= 0) { /* highest dep cannot be sourced from memory */ *jitptr += _jit_vpxor_r(*jitptr, reg, (uint_fast8_t)highest, (uint_fast8_t)lowest); } else #ifdef XORDEP_AVX_XOR_OPTIMAL { /* just change XOR at end to merge from this register */ return lowest; } #else /* just a move */ *jitptr += _jit_vmovdqa(*jitptr, reg, (uint_fast8_t)lowest); #endif } } return reg; } // table originally from http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetTable // modified for our use (items pre-multiplied by 4, only 128 entries) static const unsigned char xor256_jit_len[128] = { # define B2(n) n, n+4, n+4, n+8 # define B4(n) B2(n), B2(n+4), B2(n+4), B2(n+8) # define B6(n) B4(n), B4(n+4), B4(n+4), B4(n+8) B6(0), B6(4) #undef B2 #undef B4 #undef B6 }; static inline int xor_write_avx_main_part(void* jitptr, uint8_t dep1, uint8_t dep2, int high) { uint8_t dep = dep1 | dep2; __m128i nums = _mm_loadl_epi64((__m128i*)(xor256_jit_nums + dep)); // expand to 8x32b + shift into place __m256i srcs = _mm256_slli_epi32(_mm256_cvtepu8_epi32(_mm_add_epi8(nums, _mm_set1_epi8(high ? 10 : 3))), 11); __m128i regs = _mm_loadl_epi64((__m128i*)(xor256_jit_rmask + dep1)); __m128i regs2 = _mm_loadl_epi64((__m128i*)(xor256_jit_rmask + dep2)); regs = _mm_or_si128(regs, _mm_add_epi8(regs2, regs2)); regs = _mm_shuffle_epi8(regs, nums); __m256i inst = _mm256_add_epi8( _mm256_slli_epi32(_mm256_cvtepu8_epi32(regs), 24), _mm256_set1_epi32(0xB7EFFDC5) /* VPXOR op-code, but last byte is 0xC0 - ((1<<3)+1) to offset the fact that our registers num is +1 too much */ ); _mm256_storeu_si256((__m256i*)jitptr, _mm256_xor_si256(srcs, inst)); return xor256_jit_len[dep]; } static inline void* xor_write_jit_avx(const void *HEDLEY_RESTRICT _scratch, uint8_t *HEDLEY_RESTRICT jitptr, uint16_t val, const int mode, const int prefetch) { const struct gf16_xor_scratch *HEDLEY_RESTRICT scratch = (const struct gf16_xor_scratch*)_scratch; uint_fast32_t bit; __m256i depmask = _mm256_load_si256((__m256i*)scratch->deps + (val & 0xf)*4); depmask = _mm256_xor_si256(depmask, _mm256_load_si256((__m256i*)(scratch->deps + ((val << 3) & 0x780)) + 1) ); depmask = _mm256_xor_si256(depmask, _mm256_load_si256((__m256i*)(scratch->deps + ((val >> 1) & 0x780)) + 2) ); depmask = _mm256_xor_si256(depmask, _mm256_load_si256((__m256i*)(scratch->deps + ((val >> 5) & 0x780)) + 3) ); __m128i common_mask, tmp3, tmp4; tmp3 = _mm256_castsi256_si128(depmask); tmp4 = _mm256_extracti128_si256(depmask, 1); ALIGN_TO(16, int16_t common_highest[8]); ALIGN_TO(16, int16_t common_lowest[8]); ALIGN_TO(16, int16_t dep1_highest[8]); ALIGN_TO(16, int16_t dep1_lowest[8]); ALIGN_TO(16, int16_t dep2_highest[8]); ALIGN_TO(16, int16_t dep2_lowest[8]); /* obtain index of lowest bit set, and clear it */ common_mask = _mm_and_si128(tmp3, tmp4); __m128i lowest = ssse3_tzcnt_epi16(common_mask); _mm_store_si128((__m128i*)common_lowest, lowest); __m128i common_sub1 = _mm_add_epi16(common_mask, _mm_set1_epi16(-1)); // TODO: could re-use the VPXOR constant with _mm_sign_epi16 (and invert and/not below) __m128i common_elim = _mm_andnot_si128(common_sub1, common_mask); __m128i highest; common_mask = _mm_and_si128(common_mask, common_sub1); highest = ssse3_lzcnt_epi16(common_mask); _mm_store_si128((__m128i*)common_highest, _mm_sub_epi16(_mm_set1_epi16(15), highest)); common_elim = _mm_or_si128(common_elim, sse4_lzcnt_to_mask_epi16(highest)); /* clear highest/lowest bit from tmp3/4 */ tmp3 = _mm_xor_si128(tmp3, common_elim); tmp4 = _mm_xor_si128(tmp4, common_elim); if(mode != XORDEP_JIT_MODE_MULADD) { lowest = ssse3_tzcnt_epi16(tmp3); _mm_store_si128((__m128i*)dep1_lowest, lowest); tmp3 = _mm_and_si128(tmp3, _mm_add_epi16(tmp3, _mm_set1_epi16(-1))); lowest = ssse3_tzcnt_epi16(tmp4); _mm_store_si128((__m128i*)dep2_lowest, lowest); tmp4 = _mm_and_si128(tmp4, _mm_add_epi16(tmp4, _mm_set1_epi16(-1))); } highest = ssse3_lzcnt_epi16(tmp3); _mm_store_si128((__m128i*)dep1_highest, _mm_sub_epi16(_mm_set1_epi16(15), highest)); tmp3 = _mm_xor_si128(tmp3, sse4_lzcnt_to_mask_epi16(highest)); highest = ssse3_lzcnt_epi16(tmp4); _mm_store_si128((__m128i*)dep2_highest, _mm_sub_epi16(_mm_set1_epi16(15), highest)); tmp4 = _mm_xor_si128(tmp4, sse4_lzcnt_to_mask_epi16(highest)); ALIGN_TO(16, uint16_t memDeps[8]); _mm_store_si128((__m128i*)memDeps, _mm_or_si128( _mm_and_si128(tmp3, _mm_set1_epi16(7)), _mm_slli_epi16(_mm_and_si128(tmp4, _mm_set1_epi16(7)), 3) )); ALIGN_TO(16, uint8_t deps1[16]); ALIGN_TO(16, uint8_t deps2[16]); tmp3 = _mm_srli_epi16(tmp3, 3); tmp4 = _mm_srli_epi16(tmp4, 3); tmp3 = _mm_blendv_epi8(_mm_add_epi16(tmp3, tmp3), _mm_and_si128(tmp3, _mm_set1_epi8(0x7f)), _mm_set1_epi16(0xff)); tmp4 = _mm_blendv_epi8(_mm_add_epi16(tmp4, tmp4), _mm_and_si128(tmp4, _mm_set1_epi8(0x7f)), _mm_set1_epi16(0xff)); _mm_store_si128((__m128i*)deps1, tmp3); _mm_store_si128((__m128i*)deps2, tmp4); if(prefetch) { jitptr += _jit_add_i(jitptr, SI, 256); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, -128); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, -64); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, 0); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, 64); } // TODO: optimize these #undef _LD_DQA #define _LD_DQA(yreg, mreg, offs) \ jitptr += _jit_vmovdqa_load(jitptr, yreg, mreg, offs) #undef _ST_DQA #define _ST_DQA(mreg, offs, yreg) \ jitptr += _jit_vmovdqa_store(jitptr, mreg, offs, yreg) //_jit_pxor_r(jit, r2, r1) /* #define _PXOR_R_(r2, r1, tr) \ write32(jitptr, (0xC0EF0F66 + ((r2) <<27) + ((r1) <<24)) ^ (tr)) #define _PXOR_R(r2, r1) \ _PXOR_R_(r2, r1, 0); \ jitptr += 4 #define _C_PXOR_R(r2, r1, c) \ _PXOR_R_(r2, r1, 0); \ jitptr += (c)<<2 */ #undef _PXOR_R #define _PXOR_R(r2, r1) jitptr += _jit_vpxor_r(jitptr, r2, r2, r1) #undef _C_PXOR_R #define _C_PXOR_R(rD, r2, r1, c) jitptr += _jit_vpxor_r(jitptr, rD, r2, r1) & -(c) /* generate code */ if(mode == XORDEP_JIT_MODE_MULADD) { for(bit=0; bit<8; bit++) { int destOffs = (bit<<6)-128; int destOffs2 = destOffs+32; uint8_t common_reg; /* if there's a higest bit set, do a VPXOR-load, otherwise, regular load + VPXOR-load */ if(dep1_highest[bit] > 2) { jitptr += _jit_vpxor_m(jitptr, 0, (uint_fast8_t)dep1_highest[bit], DX, destOffs); } else { _LD_DQA(0, DX, destOffs); if(dep1_highest[bit] >= 0) jitptr += _jit_vpxor_m(jitptr, 0, 0, AX, dep1_highest[bit]*32-128); } if(dep2_highest[bit] > 2) { jitptr += _jit_vpxor_m(jitptr, 1, (uint_fast8_t)dep2_highest[bit], DX, destOffs2); } else { _LD_DQA(1, DX, destOffs2); if(dep2_highest[bit] >= 0) jitptr += _jit_vpxor_m(jitptr, 1, 1, AX, dep2_highest[bit]*32-128); } /* for common mask, if two lowest bits available, do VPXOR, else if only one, just XOR at end (consider no common mask optimization to eliminate this case) */ common_reg = xor_write_avx_load_part(&jitptr, 2, common_lowest[bit], common_highest[bit]); _mm_storeu_si128((__m128i*)jitptr, _mm_load_si128(&xor256_jit_clut_code1[memDeps[bit]])); jitptr += xor256_jit_clut_info_mem[memDeps[bit]]; jitptr += xor_write_avx_main_part(jitptr, deps1[bit*2], deps2[bit*2], 0); jitptr += xor_write_avx_main_part(jitptr, deps1[bit*2+1], deps2[bit*2+1], 1); _C_PXOR_R(0, common_reg, 0, common_lowest[bit] < 16); _C_PXOR_R(1, common_reg, 1, common_lowest[bit] < 16); _ST_DQA(DX, destOffs, 0); _ST_DQA(DX, destOffs2, 1); } } else { for(bit=0; bit<8; bit++) { int destOffs = (bit<<6)-128; int destOffs2 = destOffs+32; uint8_t common_reg, reg1, reg2; reg1 = xor_write_avx_load_part(&jitptr, 0, dep1_lowest[bit], dep1_highest[bit]); reg2 = xor_write_avx_load_part(&jitptr, 1, dep2_lowest[bit], dep2_highest[bit]); common_reg = xor_write_avx_load_part(&jitptr, 2, common_lowest[bit], common_highest[bit]); _mm_storeu_si128((__m128i*)jitptr, _mm_load_si128(&xor256_jit_clut_code1[memDeps[bit]])); jitptr += xor256_jit_clut_info_mem[memDeps[bit]]; jitptr += xor_write_avx_main_part(jitptr, deps1[bit*2], deps2[bit*2], 0); jitptr += xor_write_avx_main_part(jitptr, deps1[bit*2+1], deps2[bit*2+1], 1); if(dep1_lowest[bit] < 16) { #ifdef XORDEP_AVX_XOR_OPTIMAL if(common_lowest[bit] < 16) { jitptr += _jit_vpxor_r(jitptr, 0, reg1, common_reg); _ST_DQA(DX, destOffs, 0); } else { _ST_DQA(DX, destOffs, reg1); } #else _C_PXOR_R(0, reg1, common_reg, common_lowest[bit] < 16); _ST_DQA(DX, destOffs, 0); #endif } else { /* dep1 must be sourced from the common mask */ _ST_DQA(DX, destOffs, common_reg); } if(dep2_lowest[bit] < 16) { #ifdef XORDEP_AVX_XOR_OPTIMAL if(common_lowest[bit] < 16) { jitptr += _jit_vpxor_r(jitptr, 1, reg2, common_reg); _ST_DQA(DX, destOffs2, 1); } else { _ST_DQA(DX, destOffs2, reg2); } #else _C_PXOR_R(1, reg2, common_reg, common_lowest[bit] < 16); _ST_DQA(DX, destOffs2, 1); #endif } else { _ST_DQA(DX, destOffs2, common_reg); } } } /* cmp/jcc */ write64(jitptr, 0x800FC03948 | (DX <<16) | (CX <<19) | ((uint64_t)JL <<32)); return jitptr+5; } static HEDLEY_ALWAYS_INLINE void gf16_xor_jit_mul_avx2_base(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const int mode, const int doPrefetch, const void *HEDLEY_RESTRICT prefetch) { jit_wx_pair* jit = (jit_wx_pair*)mutScratch; gf16_xorjit_write_jit(scratch, coefficient, jit, mode, doPrefetch, &xor_write_jit_avx, scratch); if(mode == XORDEP_JIT_MODE_MUL_INSITU) { ALIGN_TO(32, __m256i spill[3]); gf16_xor256_jit_stub( (intptr_t)spill + 128, (intptr_t)dst + len - 384, (intptr_t)dst - 384, (intptr_t)prefetch - 128, (uint8_t*)jit->x + XORDEP_JIT_SIZE/2 ); } else { gf16_xor256_jit_stub( (intptr_t)src - 384, (intptr_t)dst + len - 384, (intptr_t)dst - 384, (intptr_t)prefetch - 128, jit->x ); } _mm256_zeroupper(); } #endif /* defined(__AVX2__) && defined(PLATFORM_AMD64) */ #ifdef PARPAR_INVERT_SUPPORT void gf16_xor_jit_mul_avx2(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { #if defined(__AVX2__) && defined(PLATFORM_AMD64) if(coefficient == 0) { memset(dst, 0, len); return; } gf16_xor_jit_mul_avx2_base(scratch, dst, src, len, coefficient, mutScratch, dst==src ? XORDEP_JIT_MODE_MUL_INSITU : XORDEP_JIT_MODE_MUL, 0, NULL); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); UNUSED(mutScratch); #endif } #endif void gf16_xor_jit_muladd_avx2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { #if defined(__AVX2__) && defined(PLATFORM_AMD64) if(coefficient == 0) return; gf16_xor_jit_mul_avx2_base(scratch, dst, src, len, coefficient, mutScratch, XORDEP_JIT_MODE_MULADD, 0, NULL); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); UNUSED(mutScratch); #endif } void gf16_xor_jit_muladd_prefetch_avx2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch) { #if defined(__AVX2__) && defined(PLATFORM_AMD64) if(coefficient == 0) return; gf16_xor_jit_mul_avx2_base(scratch, dst, src, len, coefficient, mutScratch, XORDEP_JIT_MODE_MULADD, _MM_HINT_T1, prefetch); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); UNUSED(mutScratch); UNUSED(prefetch); #endif } #if defined(__AVX2__) && defined(PLATFORM_AMD64) // extract top bits; interleaving of 16-bit words needed due to byte arrangement for pmovmskb static HEDLEY_ALWAYS_INLINE __m256i gf16_xor_finish_extract_bits(__m256i src) { #define EXTRACT_BITS_NIBBLE(targVec, srcVec) { \ uint32_t mskA, mskB, mskC, mskD; \ mskD = _mm256_movemask_epi8(srcVec); \ srcVec = _mm256_add_epi8(srcVec, srcVec); \ mskC = _mm256_movemask_epi8(srcVec); \ srcVec = _mm256_add_epi8(srcVec, srcVec); \ mskB = _mm256_movemask_epi8(srcVec); \ srcVec = _mm256_add_epi8(srcVec, srcVec); \ mskA = _mm256_movemask_epi8(srcVec); \ targVec = _mm_cvtsi32_si128(mskA); \ targVec = _mm_insert_epi32(targVec, mskB, 1); \ targVec = _mm_insert_epi32(targVec, mskC, 2); \ targVec = _mm_insert_epi32(targVec, mskD, 3); \ } __m128i words1, words2; EXTRACT_BITS_NIBBLE(words1, src) src = _mm256_add_epi8(src, src); EXTRACT_BITS_NIBBLE(words2, src) __m256i words = _mm256_inserti128_si256(_mm256_castsi128_si256(words2), words1, 1); words = _mm256_shuffle_epi8(words, _mm256_set_epi32( 0x0f0e0b0a, 0x07060302, 0x0d0c0908, 0x05040100, 0x0f0e0b0a, 0x07060302, 0x0d0c0908, 0x05040100 )); return _mm256_permute4x64_epi64(words, _MM_SHUFFLE(3,1,2,0)); #undef EXTRACT_BITS_NIBBLE } static HEDLEY_ALWAYS_INLINE void gf16_xor_finish_extract_bits_store(uint32_t* dst, __m256i src) { __m256i srcShifted = _mm256_add_epi8(src, src); __m256i lane = _mm256_inserti128_si256(srcShifted, _mm256_castsi256_si128(src), 1); write32(dst+3, _mm256_movemask_epi8(lane)); lane = _mm256_slli_epi16(lane, 2); write32(dst+2, _mm256_movemask_epi8(lane)); lane = _mm256_slli_epi16(lane, 2); write32(dst+1, _mm256_movemask_epi8(lane)); lane = _mm256_slli_epi16(lane, 2); write32(dst+0, _mm256_movemask_epi8(lane)); lane = _mm256_permute2x128_si256(srcShifted, src, 0x31); write32(dst+7, _mm256_movemask_epi8(lane)); lane = _mm256_slli_epi16(lane, 2); write32(dst+6, _mm256_movemask_epi8(lane)); lane = _mm256_slli_epi16(lane, 2); write32(dst+5, _mm256_movemask_epi8(lane)); lane = _mm256_slli_epi16(lane, 2); write32(dst+4, _mm256_movemask_epi8(lane)); } #define LOAD_HALVES(a, b, upper) \ _mm256_inserti128_si256( \ _mm256_castsi128_si256(_mm_load_si128((__m128i*)(_src + 120 + upper*4 - (a)*8))), \ _mm_load_si128((__m128i*)(_src + 120 + upper*4 - (b)*8)), \ 1 \ ) #define LOAD_X4(offs, dst1, dst2, upper) { \ __m256i in1 = LOAD_HALVES(offs+0, offs+8, upper); /* 88888888 00000000 */ \ __m256i in2 = LOAD_HALVES(offs+1, offs+9, upper); /* 99999999 11111111 */ \ dst1 = _mm256_unpacklo_epi8(in1, in2); /* 98989898 10101010 */ \ dst2 = _mm256_unpackhi_epi8(in1, in2); \ } #define UNPACK_VECTS \ srcD0a = _mm256_unpacklo_epi16(srcW0, srcW2); /* ba98ba98 32103210 */ \ srcD0b = _mm256_unpackhi_epi16(srcW0, srcW2); \ srcD0c = _mm256_unpacklo_epi16(srcW1, srcW3); \ srcD0d = _mm256_unpackhi_epi16(srcW1, srcW3); \ srcD4a = _mm256_unpacklo_epi16(srcW4, srcW6); \ srcD4b = _mm256_unpackhi_epi16(srcW4, srcW6); \ srcD4c = _mm256_unpacklo_epi16(srcW5, srcW7); \ srcD4d = _mm256_unpackhi_epi16(srcW5, srcW7); \ \ srcQa = _mm256_unpacklo_epi32(srcD0a, srcD4a); /* fedcba98 76543210 */ \ srcQb = _mm256_unpackhi_epi32(srcD0a, srcD4a); \ srcQc = _mm256_unpacklo_epi32(srcD0b, srcD4b); \ srcQd = _mm256_unpackhi_epi32(srcD0b, srcD4b); \ srcQe = _mm256_unpacklo_epi32(srcD0c, srcD4c); \ srcQf = _mm256_unpackhi_epi32(srcD0c, srcD4c); \ srcQg = _mm256_unpacklo_epi32(srcD0d, srcD4d); \ srcQh = _mm256_unpackhi_epi32(srcD0d, srcD4d); \ \ srcQa = _mm256_permute4x64_epi64(srcQa, _MM_SHUFFLE(3,1,2,0)); /* fedcba9876543210 fedcba9876543210 */ \ srcQb = _mm256_permute4x64_epi64(srcQb, _MM_SHUFFLE(3,1,2,0)); \ srcQc = _mm256_permute4x64_epi64(srcQc, _MM_SHUFFLE(3,1,2,0)); \ srcQd = _mm256_permute4x64_epi64(srcQd, _MM_SHUFFLE(3,1,2,0)); \ srcQe = _mm256_permute4x64_epi64(srcQe, _MM_SHUFFLE(3,1,2,0)); \ srcQf = _mm256_permute4x64_epi64(srcQf, _MM_SHUFFLE(3,1,2,0)); \ srcQg = _mm256_permute4x64_epi64(srcQg, _MM_SHUFFLE(3,1,2,0)); \ srcQh = _mm256_permute4x64_epi64(srcQh, _MM_SHUFFLE(3,1,2,0)) void gf16_xor_finish_block_avx2(void *HEDLEY_RESTRICT dst) { uint32_t* _dst = (uint32_t*)dst; const uint32_t* _src = _dst; __m256i srcW0, srcW1, srcW2, srcW3, srcW4, srcW5, srcW6, srcW7; __m256i srcD0a, srcD0b, srcD0c, srcD0d, srcD4a, srcD4b, srcD4c, srcD4d; __m256i srcQa, srcQb, srcQc, srcQd, srcQe, srcQf, srcQg, srcQh; // load 16x 128-bit inputs LOAD_X4(0, srcW0, srcW1, 0) LOAD_X4(2, srcW2, srcW3, 0) LOAD_X4(4, srcW4, srcW5, 0) LOAD_X4(6, srcW6, srcW7, 0) // interleave bytes in all 8 vectors UNPACK_VECTS; // save extracted bits (can't write these yet as they'd overwrite the next round) __m256i dstA = gf16_xor_finish_extract_bits(srcQa); __m256i dstB = gf16_xor_finish_extract_bits(srcQb); __m256i dstC = gf16_xor_finish_extract_bits(srcQc); __m256i dstD = gf16_xor_finish_extract_bits(srcQd); __m256i dstE = gf16_xor_finish_extract_bits(srcQe); __m256i dstF = gf16_xor_finish_extract_bits(srcQf); __m256i dstG = gf16_xor_finish_extract_bits(srcQg); __m256i dstH = gf16_xor_finish_extract_bits(srcQh); // load second half & store saved data once relevant stuff read LOAD_X4(6, srcW6, srcW7, 1) _mm256_store_si256((__m256i*)(_dst + 0), dstA); _mm256_store_si256((__m256i*)(_dst + 8), dstB); LOAD_X4(4, srcW4, srcW5, 1) _mm256_store_si256((__m256i*)(_dst + 16), dstC); _mm256_store_si256((__m256i*)(_dst + 24), dstD); LOAD_X4(2, srcW2, srcW3, 1) _mm256_store_si256((__m256i*)(_dst + 32), dstE); _mm256_store_si256((__m256i*)(_dst + 40), dstF); LOAD_X4(0, srcW0, srcW1, 1) _mm256_store_si256((__m256i*)(_dst + 48), dstG); _mm256_store_si256((__m256i*)(_dst + 56), dstH); UNPACK_VECTS; gf16_xor_finish_extract_bits_store(_dst + 64 + 0, srcQa); gf16_xor_finish_extract_bits_store(_dst + 64 + 8, srcQb); gf16_xor_finish_extract_bits_store(_dst + 64 + 16, srcQc); gf16_xor_finish_extract_bits_store(_dst + 64 + 24, srcQd); gf16_xor_finish_extract_bits_store(_dst + 64 + 32, srcQe); gf16_xor_finish_extract_bits_store(_dst + 64 + 40, srcQf); gf16_xor_finish_extract_bits_store(_dst + 64 + 48, srcQg); gf16_xor_finish_extract_bits_store(_dst + 64 + 56, srcQh); } void gf16_xor_finish_copy_block_avx2(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { uint32_t* _dst = (uint32_t*)dst; const uint32_t* _src = (uint32_t*)src; __m256i srcW0, srcW1, srcW2, srcW3, srcW4, srcW5, srcW6, srcW7; __m256i srcD0a, srcD0b, srcD0c, srcD0d, srcD4a, srcD4b, srcD4c, srcD4d; __m256i srcQa, srcQb, srcQc, srcQd, srcQe, srcQf, srcQg, srcQh; // load 16x 128-bit inputs LOAD_X4(0, srcW0, srcW1, 0) LOAD_X4(2, srcW2, srcW3, 0) LOAD_X4(4, srcW4, srcW5, 0) LOAD_X4(6, srcW6, srcW7, 0) // interleave bytes in all 8 vectors UNPACK_VECTS; // save extracted bits gf16_xor_finish_extract_bits_store(_dst + 0, srcQa); gf16_xor_finish_extract_bits_store(_dst + 8, srcQb); gf16_xor_finish_extract_bits_store(_dst + 16, srcQc); gf16_xor_finish_extract_bits_store(_dst + 24, srcQd); gf16_xor_finish_extract_bits_store(_dst + 32, srcQe); gf16_xor_finish_extract_bits_store(_dst + 40, srcQf); gf16_xor_finish_extract_bits_store(_dst + 48, srcQg); gf16_xor_finish_extract_bits_store(_dst + 56, srcQh); // load second half & store saved data once relevant stuff read LOAD_X4(0, srcW0, srcW1, 1) LOAD_X4(2, srcW2, srcW3, 1) LOAD_X4(4, srcW4, srcW5, 1) LOAD_X4(6, srcW6, srcW7, 1) UNPACK_VECTS; gf16_xor_finish_extract_bits_store(_dst + 64 + 0, srcQa); gf16_xor_finish_extract_bits_store(_dst + 64 + 8, srcQb); gf16_xor_finish_extract_bits_store(_dst + 64 + 16, srcQc); gf16_xor_finish_extract_bits_store(_dst + 64 + 24, srcQd); gf16_xor_finish_extract_bits_store(_dst + 64 + 32, srcQe); gf16_xor_finish_extract_bits_store(_dst + 64 + 40, srcQf); gf16_xor_finish_extract_bits_store(_dst + 64 + 48, srcQg); gf16_xor_finish_extract_bits_store(_dst + 64 + 56, srcQh); } void gf16_xor_finish_copy_blocku_avx2(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t bytes) { uint16_t block[256]; gf16_xor_finish_copy_block_avx2(block, src); memcpy(dst, block, bytes); } #undef UNPACK_VECTS #undef LOAD_HALVES #undef LOAD_X4 #endif #define MWORD_SIZE 32 #define _mword __m256i #define _MM(f) _mm256_ ## f #define _MMI(f) _mm256_ ## f ## _si256 #define _FNSUFFIX _avx2 #define _MM_END _mm256_zeroupper(); #if defined(__AVX2__) && defined(PLATFORM_AMD64) # define _AVAILABLE # include "gf16_checksum_x86.h" #endif #include "gf16_xor_common_funcs.h" #undef _AVAILABLE #undef MWORD_SIZE #undef _mword #undef _MM #undef _MMI #undef _FNSUFFIX #undef _MM_END #if defined(__AVX2__) && defined(PLATFORM_AMD64) GF_FINISH_PACKED_FUNCS(gf16_xor, _avx2, sizeof(__m256i)*16, gf16_xor_finish_copy_block_avx2, gf16_xor_finish_copy_blocku_avx2, 1, _mm256_zeroupper(), gf16_checksum_block_avx2, gf16_checksum_blocku_avx2, gf16_checksum_exp_avx2, &gf16_xor_finish_block_avx2, sizeof(__m256i)) #else GF_FINISH_PACKED_FUNCS_STUB(gf16_xor, _avx2) #endif #if defined(__AVX2__) && defined(PLATFORM_AMD64) static void xor_write_init_jit(uint8_t *jitCodeNorm, uint8_t *jitCodeInsitu, uint_fast16_t* sizeNorm, uint_fast16_t* sizeInsitu) { uint8_t *jitCodeStart = jitCodeNorm; jitCodeNorm += _jit_add_i(jitCodeNorm, AX, 512); jitCodeNorm += _jit_add_i(jitCodeNorm, DX, 512); /* only 64-bit supported*/ for(int i=3; i<16; i++) { jitCodeNorm += _jit_vmovdqa_load(jitCodeNorm, i, AX, lshift32(i-4, 5)); } if(sizeNorm) *sizeNorm = (uint_fast16_t)(jitCodeNorm-jitCodeStart); jitCodeStart = jitCodeInsitu; jitCodeInsitu += _jit_add_i(jitCodeInsitu, DX, 512); for(int i=0; i<16; i++) { jitCodeInsitu += _jit_vmovdqa_load(jitCodeInsitu, i, DX, lshift32(i-4, 5)); } for(int i=0; i<3; i++) { jitCodeInsitu += _jit_vmovdqa_store(jitCodeInsitu, AX, lshift32(i-4, 5), i); } if(sizeInsitu) *sizeInsitu = (uint_fast16_t)(jitCodeInsitu-jitCodeStart); } # include "gf16_bitdep_init_avx2.h" #endif void* gf16_xor_jit_init_avx2(int polynomial, int jitOptStrat) { #if defined(__AVX2__) && defined(PLATFORM_AMD64) struct gf16_xor_scratch* ret; uint8_t tmpCode[XORDEP_JIT_CODE_SIZE]; ALIGN_ALLOC(ret, sizeof(struct gf16_xor_scratch), 32); gf16_bitdep_init256(ret->deps, polynomial, 0); gf16_xor_create_jit_lut_avx2(); ret->jitOptStrat = jitOptStrat; xor_write_init_jit(tmpCode, tmpCode, &(ret->codeStart), &(ret->codeStartInsitu)); return ret; #else UNUSED(polynomial); UNUSED(jitOptStrat); return NULL; #endif } void* gf16_xor_jit_init_mut_avx2() { #if defined(__AVX2__) && defined(PLATFORM_AMD64) jit_wx_pair *jitCode = jit_alloc(XORDEP_JIT_SIZE); if(!jitCode) return NULL; xor_write_init_jit(jitCode->w, (uint8_t*)jitCode->w + XORDEP_JIT_SIZE/2, NULL, NULL); return jitCode; #else return NULL; #endif } par2cmdline-turbo-1.4.0/parpar/gf16/gf16_xor_avx512.c000066400000000000000000001416601514221355600220160ustar00rootroot00000000000000 #define _GF16_XORJIT_COPY_ALIGN 32 #include "gf16_xor_common.h" #undef _GF16_XORJIT_COPY_ALIGN #if defined(__AVX512BW__) && defined(__AVX512VL__) && defined(PLATFORM_AMD64) && !defined(PARPAR_SLIM_GF16) # define _AVAILABLE #endif #ifdef _AVAILABLE int gf16_xor_available_avx512 = 1; #else int gf16_xor_available_avx512 = 0; #endif #ifdef _AVAILABLE static size_t xor_write_init_jit(uint8_t *jitCode) { uint8_t *jitCodeStart = jitCode; jitCode += _jit_add_i(jitCode, AX, 1024); jitCode += _jit_add_i(jitCode, DX, 1024); /* only 64-bit supported*/ for(int i=1; i<16; i++) { jitCode += _jit_vmovdqa32_load(jitCode, 16+i, DX, i<<6); } return jitCode-jitCodeStart; } # include "gf16_bitdep_init_avx2.h" /* because some versions of GCC (e.g. 6.3.0) lack _mm512_set_epi8, emulate it */ #define _P(e3,e2,e1,e0) ((((uint32_t)(e3&0xff))<<24) | (((uint32_t)(e2&0xff))<<16) | (((uint32_t)(e1&0xff))<<8) | ((uint32_t)(e0&0xff))) static HEDLEY_ALWAYS_INLINE __m512i MM512_SET_BYTES(char e63, char e62, char e61, char e60, char e59, char e58, char e57, char e56, char e55, char e54, char e53, char e52, char e51, char e50, char e49, char e48, char e47, char e46, char e45, char e44, char e43, char e42, char e41, char e40, char e39, char e38, char e37, char e36, char e35, char e34, char e33, char e32, char e31, char e30, char e29, char e28, char e27, char e26, char e25, char e24, char e23, char e22, char e21, char e20, char e19, char e18, char e17, char e16, char e15, char e14, char e13, char e12, char e11, char e10, char e9, char e8, char e7, char e6, char e5, char e4, char e3, char e2, char e1, char e0) { return _mm512_set_epi32(_P(e63,e62,e61,e60),_P(e59,e58,e57,e56),_P(e55,e54,e53,e52),_P(e51,e50,e49,e48),_P(e47,e46,e45,e44),_P(e43,e42,e41,e40),_P(e39,e38,e37,e36),_P(e35,e34,e33,e32),_P(e31,e30,e29,e28),_P(e27,e26,e25,e24),_P(e23,e22,e21,e20),_P(e19,e18,e17,e16),_P(e15,e14,e13,e12),_P(e11,e10,e9,e8),_P(e7,e6,e5,e4),_P(e3,e2,e1,e0)); } #undef _P static HEDLEY_ALWAYS_INLINE __m512i avx3_popcnt_epi8(__m512i src) { __m512i lmask = _mm512_set1_epi8(0xf); __m512i tbl = MM512_SET_BYTES( 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0, 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0, 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0, 4, 3, 3, 2, 3, 2, 2, 1, 3, 2, 2, 1, 2, 1, 1, 0 ); return _mm512_add_epi8( _mm512_shuffle_epi8(tbl, _mm512_and_si512(src, lmask)), _mm512_shuffle_epi8(tbl, _mm512_and_si512(_mm512_srli_epi16(src, 4), lmask)) ); } static HEDLEY_ALWAYS_INLINE __m512i avx3_popcnt_epi16(__m512i src) { return _mm512_maddubs_epi16(avx3_popcnt_epi8(src), _mm512_set1_epi8(1)); } /* static HEDLEY_ALWAYS_INLINE __m128i sse_load_halves(void* lo, void* hi) { return _mm_castps_si128(_mm_loadh_pi( _mm_castsi128_ps(_mm_loadl_epi64((__m128i*)lo)), hi )); } */ static HEDLEY_ALWAYS_INLINE __m512i xor_avx512_main_part_fromreg(int odd, int r, __m128i indicies) { __m512i idx = _mm512_shuffle_i32x4(_mm512_castsi128_si512(indicies), _mm512_castsi128_si512(indicies), 0); int destRegPart1 = (r&16) | ((r&8)<<4), destRegPart2 = (r&7)<<3; __m512i inst; if(odd) { // pre-shift first byte of every pair by 3 (position for instruction placement) idx = _mm512_mask_blend_epi8(0x5555555555555555, _mm512_slli_epi16(idx, 3), idx); // shuffle bytes into position #define _SEQ(n) -1,(n)+1,-1,-1,(n),(n)+1,-1 idx = _mm512_shuffle_epi8(idx, MM512_SET_BYTES( -1,-1, _SEQ(15), _SEQ(13), _SEQ(11), _SEQ(9), _SEQ(7), _SEQ(5), _SEQ(3), _SEQ(1), 0,-1,-1,-1, 0,-1 )); #undef _SEQ #define _SEQ 0x96,0xC0+destRegPart2,0x25,0x40,0x7D,0xB3^destRegPart1,0x62 /*VPTERNLOGD*/ inst = MM512_SET_BYTES( 0x00,0x00, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, 0xC0+destRegPart2, 0x6F,0x48,0x7D,0xB1^destRegPart1,0x62 /*VMOVDQA32*/ ); #undef _SEQ } else { idx = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, _mm512_slli_epi16(idx, 3), idx); #define _SEQ(n) -1,(n)+1,-1,-1,(n),(n)+1,-1 idx = _mm512_shuffle_epi8(idx, MM512_SET_BYTES( -1,-1,-1,-1,-1,-1,-1,-1,-1, _SEQ(14), _SEQ(12), _SEQ(10), _SEQ(8), _SEQ(6), _SEQ(4), _SEQ(2), 1,-1,-1, 0, 1,-1 )); #undef _SEQ #define _SEQ 0x96,0xC0+destRegPart2,0x25,0x40,0x7D,0xB3^destRegPart1,0x62 /*VPTERNLOGD*/ inst = MM512_SET_BYTES( 0x00,0x00, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, 0xC0+destRegPart2, 0xEF,0x40,0x7D,0xB1^destRegPart1,0x62 /*VPXORD*/ ); #undef _SEQ } // appropriate shifts/masks etc #define _SEQ -1,-1,-1,-1,-1, 7,-1 __mmask64 high3 = _mm512_cmpgt_epu8_mask(idx, MM512_SET_BYTES( -1,-1, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, -1,-1,-1,-1, 7,-1 )); #undef _SEQ #define _SEQ -1, 7,-1,-1,-1, 0,-1 idx = _mm512_and_si512(idx, MM512_SET_BYTES( -1,-1, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, 7,-1,-1,-1, 0,-1 )); #undef _SEQ idx = _mm512_mask_blend_epi8(high3, idx, _mm512_set1_epi8(1<<5)); // add in vpternlog return _mm512_xor_si512(idx, inst); } static HEDLEY_ALWAYS_INLINE __m512i xor_avx512_main_part_frommem(int odd, int r, int memreg, __m128i indicies) { __m512i idx = _mm512_shuffle_i32x4(_mm512_castsi128_si512(indicies), _mm512_castsi128_si512(indicies), 0); int destRegPart1 = (r&16) | ((r&8)<<4), destRegPart2 = (r&7)<<3; __m512i inst; if(odd) { // pre-shift first byte of every pair by 3 (position for instruction placement) idx = _mm512_mask_blend_epi8(0x5555555555555554, _mm512_slli_epi16(idx, 3), idx); // shuffle bytes into position #define _SEQ(n) -1,(n)+1,-1,-1,(n),(n)+1,-1 idx = _mm512_shuffle_epi8(idx, MM512_SET_BYTES( -1,-1, _SEQ(15), _SEQ(13), _SEQ(11), _SEQ(9), _SEQ(7), _SEQ(5), _SEQ(3), _SEQ(1), -1,-1,-1,-1,-1,-1 )); #undef _SEQ #define _SEQ 0x96,0xC0+destRegPart2,0x25,0x40,0x7D,0xB3^destRegPart1,0x62 /*VPTERNLOGD*/ inst = MM512_SET_BYTES( 0x00,0x00, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, 0x00+destRegPart2, 0x6F,0x48,0x7D,0xF1^destRegPart1,0x62 /*VMOVDQA32 load*/ ); #undef _SEQ } else { idx = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAA8, _mm512_slli_epi16(idx, 3), idx); #define _SEQ(n) -1,(n)+1,-1,-1,(n),(n)+1,-1 idx = _mm512_shuffle_epi8(idx, MM512_SET_BYTES( -1,-1,-1,-1,-1,-1,-1,-1,-1, _SEQ(14), _SEQ(12), _SEQ(10), _SEQ(8), _SEQ(6), _SEQ(4), _SEQ(2), -1,-1,-1, 1,-1,-1 )); #undef _SEQ #define _SEQ 0x96,0xC0+destRegPart2,0x25,0x40,0x7D,0xB3^destRegPart1,0x62 /*VPTERNLOGD*/ inst = MM512_SET_BYTES( 0x00,0x00, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, 0x00+destRegPart2, 0xEF,0x48^8/*reg1 +16*/,0x7D,0xF1^destRegPart1,0x62 /*VPXORD*/ ); #undef _SEQ } // appropriate shifts/masks etc #define _SEQ -1,-1,-1,-1,-1, 7,-1 __mmask64 high3 = _mm512_cmpgt_epu8_mask(idx, MM512_SET_BYTES( -1,-1, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, -1,-1,-1,-1,-1,-1 )); #undef _SEQ #define _SEQ -1, 7,-1,-1,-1, 0,-1 idx = _mm512_and_si512(idx, MM512_SET_BYTES( -1,-1, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, -1,-1,-1,-1,-1,-1 )); #undef _SEQ idx = _mm512_mask_blend_epi8(high3, idx, _mm512_set1_epi8(1<<5)); // add in memory source register __m128i sr = _mm_cvtsi64_si128((((uint64_t)memreg & 8) << 10) | (((uint64_t)memreg & 7) << 40)); // add in vpternlog + source reg return _mm512_ternarylogic_epi32(idx, inst, zext128_512(sr), 0x96); } static HEDLEY_ALWAYS_INLINE int xor_avx512_main_part(uint8_t *HEDLEY_RESTRICT jitptr, int popcnt, int odd, int r, int memreg, __m128i indicies) { __m512i result; if(_mm_extract_epi8(indicies, 0) == 0) result = xor_avx512_main_part_frommem(odd, r, memreg, indicies); else result = xor_avx512_main_part_fromreg(odd, r, indicies); _mm512_storeu_si512((__m512i*)jitptr, result); return (popcnt >> 1) * 7 + 6; } static HEDLEY_ALWAYS_INLINE __m512i xor_avx512_merge_part_fromreg(int odd, int r, __m128i indicies) { __m512i idx = _mm512_shuffle_i32x4(_mm512_castsi128_si512(indicies), _mm512_castsi128_si512(indicies), 0); int destRegPart1 = (r&16) | ((r&8)<<4), destRegPart2 = (r&7)<<3; __m512i inst; __mmask64 high3; if(odd) { // pre-shift first byte of every pair by 3 (position for instruction placement) idx = _mm512_mask_blend_epi8(0x5555555555555555, _mm512_slli_epi16(idx, 3), idx); // shuffle bytes into position #define _SEQ(n) -1,(n)+1,-1,-1,(n),(n)+1,-1 idx = _mm512_shuffle_epi8(idx, MM512_SET_BYTES( -1,-1, _SEQ(15), _SEQ(13), _SEQ(11), _SEQ(9), _SEQ(7), _SEQ(5), _SEQ(3), _SEQ(1), 0,-1,-1,-1, 0,-1 )); #undef _SEQ #define _SEQ 0x96,0xC0+destRegPart2,0x25,0x40,0x7D,0xB3^destRegPart1,0x62 /*VPTERNLOGD*/ inst = MM512_SET_BYTES( 0x00,0x00, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, 0xC0+destRegPart2, 0xEF,0x48^((r&16)>>1),0x7D^((r&15) << 3),0xF1^destRegPart1^0x40/*+16 reg2*/,0x62 /*VPXORD*/ ); #undef _SEQ // appropriate shifts/masks etc #define _SEQ -1,-1,-1,-1,-1, 7,-1 high3 = _mm512_cmpgt_epu8_mask(idx, MM512_SET_BYTES( -1,-1, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, -1,-1,-1,-1, 7,-1 )); #undef _SEQ #define _SEQ -1, 7,-1,-1,-1, 0,-1 idx = _mm512_and_si512(idx, MM512_SET_BYTES( -1,-1, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, 7,-1,-1,-1, 0,-1 )); #undef _SEQ } else { idx = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAAA, _mm512_slli_epi16(idx, 3), idx); #define _SEQ(n) -1,(n)+1,-1,-1,(n),(n)+1,-1 idx = _mm512_shuffle_epi8(idx, MM512_SET_BYTES( -1,-1,-1,-1,-1,-1,-1,-1, _SEQ(14), _SEQ(12), _SEQ(10), _SEQ(8), _SEQ(6), _SEQ(4), _SEQ(2), _SEQ(0) )); #undef _SEQ #define _SEQ 0x96,0xC0+destRegPart2,0x25,0x40,0x7D,0xB3^destRegPart1,0x62 /*VPTERNLOGD*/ inst = MM512_SET_BYTES( 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ ); #undef _SEQ // appropriate shifts/masks etc #define _SEQ -1,-1,-1,-1,-1, 7,-1 high3 = _mm512_cmpgt_epu8_mask(idx, MM512_SET_BYTES( -1,-1,-1,-1,-1,-1,-1,-1, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ )); #undef _SEQ #define _SEQ -1, 7,-1,-1,-1, 0,-1 idx = _mm512_and_si512(idx, MM512_SET_BYTES( -1,-1,-1,-1,-1,-1,-1,-1, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ )); #undef _SEQ } idx = _mm512_mask_blend_epi8(high3, idx, _mm512_set1_epi8(1<<5)); // add in vpternlog return _mm512_xor_si512(idx, inst); } static HEDLEY_ALWAYS_INLINE __m512i xor_avx512_merge_part_frommem(int odd, int r, int memreg, __m128i indicies) { __m512i idx = _mm512_shuffle_i32x4(_mm512_castsi128_si512(indicies), _mm512_castsi128_si512(indicies), 0); int destRegPart1 = (r&16) | ((r&8)<<4), destRegPart2 = (r&7)<<3; __m512i inst; __mmask64 high3; if(odd) { // pre-shift first byte of every pair by 3 (position for instruction placement) idx = _mm512_mask_blend_epi8(0x5555555555555554, _mm512_slli_epi16(idx, 3), idx); // shuffle bytes into position #define _SEQ(n) -1,(n)+1,-1,-1,(n),(n)+1,-1 idx = _mm512_shuffle_epi8(idx, MM512_SET_BYTES( -1,-1,-1,-1,-1,-1,-1,-1,-1, _SEQ(13), _SEQ(11), _SEQ(9), _SEQ(7), _SEQ(5), _SEQ(3), _SEQ(1), -1,-1,-1,-1,-1,-1 )); #undef _SEQ #define _SEQ 0x96,0xC0+destRegPart2,0x25,0x40,0x7D,0xB3^destRegPart1,0x62 /*VPTERNLOGD*/ inst = MM512_SET_BYTES( 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, 0x00+destRegPart2, 0xEF,0x48^((r&16)>>1),0x7D^((r&15) << 3),0xF1^destRegPart1^0x40/*+16 reg2*/,0x62 /*VPXORD*/ ); #undef _SEQ // appropriate shifts/masks etc #define _SEQ -1,-1,-1,-1,-1, 7,-1 high3 = _mm512_cmpgt_epu8_mask(idx, MM512_SET_BYTES( -1,-1, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, -1,-1,-1,-1,-1,-1 )); #undef _SEQ #define _SEQ -1, 7,-1,-1,-1, 0,-1 idx = _mm512_and_si512(idx, MM512_SET_BYTES( -1,-1, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, -1,-1,-1,-1,-1,-1 )); #undef _SEQ } else { idx = _mm512_mask_blend_epi8(0xAAAAAAAAAAAAAAA8, _mm512_slli_epi16(idx, 3), idx); #define _SEQ(n) -1,(n)+1,-1,-1,(n),(n)+1,-1 idx = _mm512_shuffle_epi8(idx, MM512_SET_BYTES( -1,-1,-1,-1,-1,-1,-1,-1, _SEQ(14), _SEQ(12), _SEQ(10), _SEQ(8), _SEQ(6), _SEQ(4), _SEQ(2), -1,-1,-1,-1, 1,-1,-1 )); #undef _SEQ #define _SEQ 0x96,0xC0+destRegPart2,0x25,0x40,0x7D,0xB3^destRegPart1,0x62 /*VPTERNLOGD*/ inst = MM512_SET_BYTES( -1,-1,-1,-1,-1,-1,-1,-1, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, 0x96,0x00+destRegPart2, 0x25,0x48^8/*reg1 +16*/,0x7D,0xF3^destRegPart1^0x40/*+16 reg2*/,0x62 /*VPTERNLOGD*/ ); #undef _SEQ // appropriate shifts/masks etc #define _SEQ -1,-1,-1,-1,-1, 7,-1 high3 = _mm512_cmpgt_epu8_mask(idx, MM512_SET_BYTES( -1, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, -1,-1,-1,-1,-1,-1,-1 )); #undef _SEQ #define _SEQ -1, 7,-1,-1,-1, 0,-1 idx = _mm512_and_si512(idx, MM512_SET_BYTES( -1, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, _SEQ, -1,-1,-1,-1,-1,-1,-1 )); #undef _SEQ } idx = _mm512_mask_blend_epi8(high3, idx, _mm512_set1_epi8(1<<5)); // add in memory source register __m128i sr = _mm_cvtsi64_si128((((uint64_t)memreg & 8) << 10) | (((uint64_t)memreg & 7) << 40)); // add in vpternlog + source reg return _mm512_ternarylogic_epi32(idx, inst, zext128_512(sr), 0x96); } static HEDLEY_ALWAYS_INLINE int xor_avx512_merge_part(uint8_t *HEDLEY_RESTRICT jitptr, int popcnt, int odd, int r, int memreg, __m128i indicies) { __m512i result; if(_mm_extract_epi8(indicies, 0) == 0) result = xor_avx512_merge_part_frommem(odd, r, memreg, indicies); else result = xor_avx512_merge_part_fromreg(odd, r, indicies); _mm512_storeu_si512((__m512i*)jitptr, result); return ((popcnt+1) >> 1) * 7 - odd; } static inline void* xor_write_jit_avx512(const void *HEDLEY_RESTRICT _scratch, uint8_t *HEDLEY_RESTRICT jitptr, uint16_t val, const int mode, const int prefetch) { const struct gf16_xor_scratch *HEDLEY_RESTRICT scratch = (const struct gf16_xor_scratch*)_scratch; uint_fast32_t bit; __m256i depmask = _mm256_load_si256((__m256i*)scratch->deps + (val & 0xf)*4); depmask = _mm256_xor_si256(depmask, _mm256_load_si256((__m256i*)(scratch->deps + ((val << 3) & 0x780)) + 1) ); depmask = _mm256_ternarylogic_epi32( depmask, _mm256_load_si256((__m256i*)(scratch->deps + ((val >> 1) & 0x780)) + 2), _mm256_load_si256((__m256i*)(scratch->deps + ((val >> 5) & 0x780)) + 3), 0x96 ); __m128i common_mask = _mm_and_si128( _mm256_castsi256_si128(depmask), _mm256_extracti128_si256(depmask, 1) ); /* eliminate pointless common_mask entries */ common_mask = _mm_maskz_mov_epi16( /* "(v & (v-1)) == 0" is true if only zero/one bit is set in each word */ _mm_test_epi16_mask(common_mask, _mm_add_epi16(common_mask, _mm_set1_epi16(-1))), common_mask ); __m512i common_mask384 = _mm512_castsi128_si512(common_mask); common_mask384 = _mm512_shuffle_i32x4(common_mask384, common_mask384, _MM_SHUFFLE(0,0,0,0)); __m512i depmask384 = _mm512_xor_si512(zext256_512(depmask), common_mask384); /* count bits */ ALIGN_TO(64, uint16_t depABC[32]); // only first 24 elements are used for these two arrays, the rest is needed for 512-bit stores to work ALIGN_TO(64, uint16_t popcntABC[32]); _mm512_store_si512(depABC, depmask384); _mm512_store_si512(popcntABC, avx3_popcnt_epi16(depmask384)); __m512i numbers = _mm512_set_epi32( 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ); if(prefetch) { jitptr += _jit_add_i(jitptr, SI, 512); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, -128); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, -64); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, 0); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, 64); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, 128); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, 192); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, 256); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, 320); } jitptr += _jit_vmovdqa32_load(jitptr, 16, DX, 0); /* generate code */ if(mode == XORDEP_JIT_MODE_MULADD) { for(bit=0; bit<8; bit++) { int destOffs = bit<<7; int destOffs2 = destOffs+64; __m128i idxC, idxA, idxB; /* idxC = sse_load_halves(&xor256_jit_nums[depABC[16+bit] & 0xff], &xor256_jit_nums[depABC[16+bit] >> 8]); idxA = sse_load_halves(&xor256_jit_nums[depABC[bit] & 0xff], &xor256_jit_nums[depABC[bit] >> 8]); idxB = sse_load_halves(&xor256_jit_nums[depABC[8+bit] & 0xff], &xor256_jit_nums[depABC[8+bit] >> 8]); // TODO: need to shuffle merge the halves! */ // TODO: above idea is probably faster, this is just easier to code idxC = _mm512_cvtepi32_epi8(_mm512_maskz_compress_epi32(depABC[16+bit], numbers)); idxA = _mm512_cvtepi32_epi8(_mm512_maskz_compress_epi32(depABC[bit], numbers)); idxB = _mm512_cvtepi32_epi8(_mm512_maskz_compress_epi32(depABC[8+bit], numbers)); if(popcntABC[16+bit]) { // popcntABC[16+bit] cannot == 1 (eliminated above) _mm512_storeu_si512((__m512i*)jitptr, xor_avx512_main_part_fromreg((popcntABC[16+bit] & 1), 3, idxC)); jitptr += ((popcntABC[16+bit]-1) >> 1) * 7 + 6; // last xor of pipes A/B are a merge if(popcntABC[bit] == 0) { jitptr += _jit_vpxord_m(jitptr, 1, 3, AX, destOffs); } else { _mm512_storeu_si512((__m512i*)jitptr, xor_avx512_main_part_fromreg((popcntABC[bit] & 1), 1, idxA)); jitptr += ((popcntABC[bit]-1) >> 1) * 7 + 6; // TODO: perhaps ideally the load is done earlier? jitptr += _jit_vpternlogd_m(jitptr, 1, 3, AX, destOffs, 0x96); } if(popcntABC[8+bit] == 0) { jitptr += _jit_vpxord_m(jitptr, 2, 3, AX, destOffs2); } else { _mm512_storeu_si512((__m512i*)jitptr, xor_avx512_main_part_fromreg((popcntABC[8+bit] & 1), 2, idxB)); jitptr += ((popcntABC[8+bit]-1) >> 1) * 7 + 6; jitptr += _jit_vpternlogd_m(jitptr, 2, 3, AX, destOffs2, 0x96); } } else { // if no common queue, popcntA/B assumed to be >= 1 if(popcntABC[bit] == 1) { jitptr += _jit_vpxord_m(jitptr, 1, _mm_extract_epi8(idxA, 0)|16, AX, destOffs); } else { _mm512_storeu_si512((__m512i*)jitptr, xor_avx512_main_part_fromreg((~popcntABC[bit] & 1), 1, idxA)); jitptr += (popcntABC[bit] >> 1) * 7 + 6; // patch final vpternlog to merge from memory // TODO: optimize *(jitptr-6) |= 24<<2; // clear zreg3 bits write32(jitptr-2, ((1<<3) | AX | 0x40) | (0x96<<16) | ((destOffs<<(8-6)) & 0xff00)); jitptr++; } if(popcntABC[8+bit] == 1) { jitptr += _jit_vpxord_m(jitptr, 2, _mm_extract_epi8(idxB, 0)|16, AX, destOffs2); } else { _mm512_storeu_si512((__m512i*)jitptr, xor_avx512_main_part_fromreg((~popcntABC[8+bit] & 1), 2, idxB)); jitptr += (popcntABC[8+bit] >> 1) * 7 + 6; *(jitptr-6) |= 24<<2; // clear zreg3 bits write32(jitptr-2, ((2<<3) | AX | 0x40) | (0x96<<16) | ((destOffs2<<(8-6)) & 0xff00)); jitptr++; } } jitptr += _jit_vmovdqa32_store(jitptr, AX, destOffs, 1); jitptr += _jit_vmovdqa32_store(jitptr, AX, destOffs2, 2); } } else { for(bit=0; bit<8; bit++) { int destOffs = bit<<7; int destOffs2 = destOffs+64; __m128i idxC, idxA, idxB; idxC = _mm512_cvtepi32_epi8(_mm512_maskz_compress_epi32(depABC[16+bit], numbers)); idxA = _mm512_cvtepi32_epi8(_mm512_maskz_compress_epi32(depABC[bit], numbers)); idxB = _mm512_cvtepi32_epi8(_mm512_maskz_compress_epi32(depABC[8+bit], numbers)); if(popcntABC[16+bit]) { // popcntABC[16+bit] cannot == 1 (eliminated above) _mm512_storeu_si512((__m512i*)jitptr, xor_avx512_main_part_fromreg((popcntABC[16+bit] & 1), 3, idxC)); jitptr += ((popcntABC[16+bit]-1) >> 1) * 7 + 6; // last xor of pipes A/B are a merge if(popcntABC[bit] == 0) { jitptr += _jit_vmovdqa32_store(jitptr, AX, destOffs, 3); } else { _mm512_storeu_si512((__m512i*)jitptr, xor_avx512_main_part_fromreg((~popcntABC[bit] & 1), 1, idxA)); jitptr += (popcntABC[bit] >> 1) * 7 + 6; // patch final vpternlog/vpxor to merge from common mask // TODO: optimize uint8_t* ptr = (jitptr-6 + (popcntABC[bit] == 1)); *ptr |= 24<<2; // clear zreg3 bits *(ptr+4) = 0xC0 + 3 + (1<<3); jitptr += _jit_vmovdqa32_store(jitptr, AX, destOffs, 1); } if(popcntABC[8+bit] == 0) { jitptr += _jit_vmovdqa32_store(jitptr, AX, destOffs2, 3); } else { _mm512_storeu_si512((__m512i*)jitptr, xor_avx512_main_part_fromreg((~popcntABC[8+bit] & 1), 2, idxB)); jitptr += (popcntABC[8+bit] >> 1) * 7 + 6; uint8_t* ptr = (jitptr-6 + (popcntABC[8+bit] == 1)); *ptr |= 24<<2; *(ptr+4) = 0xC0 + 3 + (2<<3); jitptr += _jit_vmovdqa32_store(jitptr, AX, destOffs2, 2); } } else { // if no common queue, popcntA/B assumed to be >= 1 if(popcntABC[bit] == 1) { jitptr += _jit_vmovdqa32_store(jitptr, AX, destOffs, _mm_extract_epi8(idxA, 0)|16); } else { _mm512_storeu_si512((__m512i*)jitptr, xor_avx512_main_part_fromreg((popcntABC[bit] & 1), 1, idxA)); jitptr += ((popcntABC[bit]-1) >> 1) * 7 + 6; jitptr += _jit_vmovdqa32_store(jitptr, AX, destOffs, 1); } if(popcntABC[8+bit] == 1) { jitptr += _jit_vmovdqa32_store(jitptr, AX, destOffs2, _mm_extract_epi8(idxB, 0)|16); } else { _mm512_storeu_si512((__m512i*)jitptr, xor_avx512_main_part_fromreg((popcntABC[8+bit] & 1), 2, idxB)); jitptr += ((popcntABC[8+bit]-1) >> 1) * 7 + 6; jitptr += _jit_vmovdqa32_store(jitptr, AX, destOffs2, 2); } } } } /* cmp/jcc */ write64(jitptr, 0x800FC03948 | (AX <<16) | (CX <<19) | ((uint64_t)JL <<32)); return jitptr+5; } // TODO: merge this into above // note: xor can be 3 values: 0=clear, 1=xor (merge), 2=xor (load) static void* xor_write_jit_avx512_multi(const struct gf16_xor_scratch *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT jitptr, int memreg, uint16_t val, int xor) { // explicitly special case multiply by 0 if(val == 0) { if(xor == 1) return jitptr; for(int bit=0; bit<8; bit++) { if(xor == 0) { jitptr += _jit_vpxord_r(jitptr, bit, bit, bit); jitptr += _jit_vpxord_r(jitptr, bit+8, bit+8, bit+8); } if(xor == 2) { jitptr += _jit_vmovdqa32_load(jitptr, bit, AX, bit<<7); jitptr += _jit_vmovdqa32_load(jitptr, bit+8, AX, (bit<<7) + 64); } } return jitptr; } __m256i depmask = _mm256_load_si256((__m256i*)scratch->deps + (val & 0xf)*4); depmask = _mm256_xor_si256(depmask, _mm256_load_si256((__m256i*)(scratch->deps + ((val << 3) & 0x780)) + 1) ); depmask = _mm256_ternarylogic_epi32( depmask, _mm256_load_si256((__m256i*)(scratch->deps + ((val >> 1) & 0x780)) + 2), _mm256_load_si256((__m256i*)(scratch->deps + ((val >> 5) & 0x780)) + 3), 0x96 ); __m128i common_mask = _mm_and_si128( _mm256_castsi256_si128(depmask), _mm256_extracti128_si256(depmask, 1) ); /* eliminate pointless common_mask entries */ common_mask = _mm_maskz_mov_epi16( /* "(v & (v-1)) == 0" is true if only zero/one bit is set in each word */ _mm_test_epi16_mask(common_mask, _mm_add_epi16(common_mask, _mm_set1_epi16(-1))), common_mask ); __m512i common_mask384 = _mm512_castsi128_si512(common_mask); common_mask384 = _mm512_shuffle_i32x4(common_mask384, common_mask384, _MM_SHUFFLE(0,0,0,0)); __m512i depmask384 = _mm512_xor_si512(zext256_512(depmask), common_mask384); /* count bits */ ALIGN_TO(64, uint16_t depABC[32]); // only first 24 elements are used for these two arrays, the rest is needed for 512-bit stores to work ALIGN_TO(64, uint16_t popcntABC[32]); _mm512_store_si512(depABC, depmask384); _mm512_store_si512(popcntABC, avx3_popcnt_epi16(depmask384)); __m512i numbers = _mm512_set_epi32( 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ); /* generate code */ // TODO: check that code paths are all optimized for(int bit=0; bit<8; bit++) { int destOffs = bit<<7; int destOffs2 = destOffs+64; __m128i idxC, idxA, idxB; idxC = _mm512_cvtepi32_epi8(_mm512_maskz_compress_epi32(depABC[16+bit], numbers)); idxA = _mm512_cvtepi32_epi8(_mm512_maskz_compress_epi32(depABC[bit], numbers)); idxB = _mm512_cvtepi32_epi8(_mm512_maskz_compress_epi32(depABC[8+bit], numbers)); if(popcntABC[16+bit]) { // popcntC[bit] cannot == 1 (eliminated above) jitptr += xor_avx512_main_part(jitptr, popcntABC[16+bit]-1, (popcntABC[16+bit] & 1), 16, memreg, idxC); if(xor == 2) { // last xor of pipes A/B are a merge if(popcntABC[bit] == 0) { jitptr += _jit_vpxord_m(jitptr, bit, 16, AX, destOffs); } else { jitptr += xor_avx512_main_part(jitptr, popcntABC[bit]-1, (popcntABC[bit] & 1), bit, memreg, idxA); // TODO: perhaps ideally the load is done earlier? jitptr += _jit_vpternlogd_m(jitptr, bit, 16, AX, destOffs, 0x96); } if(popcntABC[8+bit] == 0) { jitptr += _jit_vpxord_m(jitptr, bit+8, 16, AX, destOffs2); } else { jitptr += xor_avx512_main_part(jitptr, popcntABC[8+bit]-1, (popcntABC[8+bit] & 1), bit+8, memreg, idxB); jitptr += _jit_vpternlogd_m(jitptr, bit+8, 16, AX, destOffs2, 0x96); } } else if(xor) { // last xor of pipes A/B are a merge if(popcntABC[bit] == 0) { jitptr += _jit_vpxord_r(jitptr, bit, 16, bit); } else { // increase the popcnt by 1; since idxA is zero filled at the end, this has the convenient effect of merging an XOR with zmm16 (common queue) jitptr += xor_avx512_merge_part(jitptr, popcntABC[bit]+1, (~popcntABC[bit] & 1), bit, memreg, idxA); } if(popcntABC[8+bit] == 0) { jitptr += _jit_vpxord_r(jitptr, bit+8, 16, bit+8); } else { jitptr += xor_avx512_merge_part(jitptr, popcntABC[8+bit]+1, (~popcntABC[8+bit] & 1), bit+8, memreg, idxB); } } else { // last xor of pipes A/B are a merge if(popcntABC[bit] == 0) { jitptr += _jit_vmovdqa32(jitptr, bit, 16); } else if(popcntABC[bit] <= 2 && _mm_extract_epi8(idxA, 0) == 0) { // special case if we need to merge w/ memory'd source if(popcntABC[bit] == 2) { jitptr += _jit_vmovdqa32(jitptr, bit, 16); jitptr += _jit_vpternlogd_m(jitptr, bit, _mm_extract_epi8(idxA, 1)|16, memreg, 0, 0x96); } else { jitptr += _jit_vpxord_m(jitptr, bit, 16, memreg, 0); } } else { jitptr += xor_avx512_main_part(jitptr, popcntABC[bit], (~popcntABC[bit] & 1), bit, memreg, idxA); // patch final vpternlog/vpxor to merge from common mask uint8_t* ptr = (jitptr-6 + (popcntABC[bit] == 1)); *ptr |= 8<<2; // set zreg3 to 16 (the highest bit is always set, so we don't need to do anything special for that) *(ptr+4) = 0xC0 + 0 + (bit<<3); } if(popcntABC[8+bit] == 0) { jitptr += _jit_vmovdqa32(jitptr, bit+8, 16); } else if(popcntABC[8+bit] <= 2 && _mm_extract_epi8(idxB, 0) == 0) { if(popcntABC[8+bit] == 2) { jitptr += _jit_vmovdqa32(jitptr, bit+8, 16); jitptr += _jit_vpternlogd_m(jitptr, bit+8, _mm_extract_epi8(idxB, 1)|16, memreg, 0, 0x96); } else { jitptr += _jit_vpxord_m(jitptr, bit+8, 16, memreg, 0); } } else { jitptr += xor_avx512_main_part(jitptr, popcntABC[8+bit], (~popcntABC[8+bit] & 1), bit+8, memreg, idxB); uint8_t* ptr = (jitptr-6 + (popcntABC[8+bit] == 1)); *ptr |= 8<<2; //*ptr &= ~(8<<4); // set +8 for zreg1 *(ptr+4) = 0xC0 + 0 + (bit<<3); } } } else { if(xor == 2) { // if no common queue, popcntA/B assumed to be >= 1 if(popcntABC[bit] == 1) { int inNum = _mm_extract_epi8(idxA, 0); if(inNum == 0) // we can re-use reg 16 since we don't have a common queue jitptr += _jit_vmovdqa32_load(jitptr, 16, memreg, 0); jitptr += _jit_vpxord_m(jitptr, bit, inNum|16, AX, destOffs); } else { jitptr += xor_avx512_main_part(jitptr, popcntABC[bit], (~popcntABC[bit] & 1), bit, memreg, idxA); // patch final vpternlog to merge from memory // TODO: optimize *(uint8_t*)(jitptr-6) |= 24<<2; // clear zreg3 bits //*(jitptr-6) &= ~(8<<4); // set +8 flag for zreg1 write32(jitptr-2, ((bit<<3) | AX | 0x40) | (0x96<<16) | ((destOffs<<(8-6)) & 0xff00)); jitptr++; } if(popcntABC[8+bit] == 1) { int inNum = _mm_extract_epi8(idxB, 0); if(inNum == 0) jitptr += _jit_vmovdqa32_load(jitptr, 16, memreg, 0); jitptr += _jit_vpxord_m(jitptr, bit+8, inNum|16, AX, destOffs2); } else { jitptr += xor_avx512_main_part(jitptr, popcntABC[8+bit], (~popcntABC[8+bit] & 1), bit+8, memreg, idxB); *(uint8_t*)(jitptr-6) |= 24<<2; // clear zreg3 bits //*(jitptr-6) &= ~(8<<4); write32(jitptr-2, ((bit<<3) | AX | 0x40) | (0x96<<16) | ((destOffs2<<(8-6)) & 0xff00)); jitptr++; } } else if(xor) { jitptr += xor_avx512_merge_part(jitptr, popcntABC[bit], popcntABC[bit] & 1, bit, memreg, idxA); jitptr += xor_avx512_merge_part(jitptr, popcntABC[8+bit], popcntABC[8+bit] & 1, bit+8, memreg, idxB); } else { // if no common queue, popcntA/B assumed to be >= 1 if(popcntABC[bit] == 1) { int inNum = _mm_extract_epi8(idxA, 0); if(inNum == 0) jitptr += _jit_vmovdqa32_load(jitptr, bit, memreg, 0); else jitptr += _jit_vmovdqa32(jitptr, bit, inNum|16); } else { jitptr += xor_avx512_main_part(jitptr, popcntABC[bit]-1, (popcntABC[bit] & 1), bit, memreg, idxA); } if(popcntABC[8+bit] == 1) { int inNum = _mm_extract_epi8(idxB, 0); if(inNum == 0) jitptr += _jit_vmovdqa32_load(jitptr, bit+8, memreg, 0); else jitptr += _jit_vmovdqa32(jitptr, bit+8, inNum|16); } else { jitptr += xor_avx512_main_part(jitptr, popcntABC[8+bit]-1, (popcntABC[8+bit] & 1), bit+8, memreg, idxB); } } } } return jitptr; } static HEDLEY_ALWAYS_INLINE void gf16_xor_jit_mul_avx512_base(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const int mode, const int doPrefetch, const void *HEDLEY_RESTRICT prefetch) { jit_wx_pair* jit = (jit_wx_pair*)mutScratch; gf16_xorjit_write_jit(scratch, coefficient, jit, mode, doPrefetch, &xor_write_jit_avx512, scratch); gf16_xor512_jit_stub( (intptr_t)dst - 1024, (intptr_t)dst + len - 1024, (intptr_t)src - 1024, (intptr_t)prefetch - 384, jit->x ); _mm256_zeroupper(); } #endif /* defined(_AVAILABLE) */ #ifdef PARPAR_INVERT_SUPPORT void gf16_xor_jit_mul_avx512(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { #ifdef _AVAILABLE if(coefficient == 0) { memset(dst, 0, len); return; } gf16_xor_jit_mul_avx512_base(scratch, dst, src, len, coefficient, mutScratch, XORDEP_JIT_MODE_MUL, 0, NULL); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); UNUSED(mutScratch); #endif } #endif void gf16_xor_jit_muladd_avx512(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { #ifdef _AVAILABLE if(coefficient == 0) return; gf16_xor_jit_mul_avx512_base(scratch, dst, src, len, coefficient, mutScratch, XORDEP_JIT_MODE_MULADD, 0, NULL); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); UNUSED(mutScratch); #endif } void gf16_xor_jit_muladd_prefetch_avx512(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch) { #ifdef _AVAILABLE if(coefficient == 0) return; gf16_xor_jit_mul_avx512_base(scratch, dst, src, len, coefficient, mutScratch, XORDEP_JIT_MODE_MULADD, _MM_HINT_T1, prefetch); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); UNUSED(mutScratch); UNUSED(prefetch); #endif } #define XOR512_MULTI_REGIONS 6 // we support up to 10, but 6 seems more optimal (cache associativity reasons?) // other registers used (hence 10 supported): dest (0), end point (1), SP (4), one source (3), R12/R13 is avoided due to different encoding length; GCC doesn't like overriding BP (5) so skip that too void gf16_xor_jit_muladd_multi_avx512(const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch) { #ifdef _AVAILABLE const struct gf16_xor_scratch *HEDLEY_RESTRICT info = (const struct gf16_xor_scratch*)scratch; jit_wx_pair* jit = (jit_wx_pair*)mutScratch; ALIGN_TO(64, uint8_t jitTemp[XORDEP_JIT_SIZE]); // for copying only uint8_t* jitCode = (uint8_t*)jit->w + info->codeStart; ALIGN_TO(32, const void* srcPtr[XOR512_MULTI_REGIONS]); for(unsigned region=0; region XOR512_MULTI_REGIONS) numRegions = XOR512_MULTI_REGIONS; uint8_t* jitptr = jitCode; uint8_t* jitdst = jitptr; if(info->jitOptStrat == GF16_XOR_JIT_STRAT_COPYNT || info->jitOptStrat == GF16_XOR_JIT_STRAT_COPY) { if((uintptr_t)jitdst & 0x1F) { /* copy unaligned part (might not be worth it for these CPUs, but meh) */ _mm256_store_si256((__m256i*)jitTemp, _mm256_load_si256((__m256i*)((uintptr_t)jitptr & ~(uintptr_t)0x1F))); jitptr = jitTemp + ((uintptr_t)jitdst & 0x1F); jitdst -= (uintptr_t)jitdst & 0x1F; } else jitptr = jitTemp; } else if(info->jitOptStrat == GF16_XOR_JIT_STRAT_CLR) { for(int i=0; i= 13) reg++; // R13 has a required offset, which changes length, so skip it jitptr += _jit_add_i(jitptr, reg, 1024); for(int i=1; i<16; i++) { jitptr += _jit_vmovdqa32_load(jitptr, 16+i, reg, i<<6); } jitptr = xor_write_jit_avx512_multi(info, jitptr, reg, coefficients[region+in], 1); srcPtr[in] = (char*)src[region+in] + offset - 1024; } // write out registers for(int i=0; i<16; i+=2) { jitptr += _jit_vmovdqa32_store(jitptr, AX, i<<6, i>>1); jitptr += _jit_vmovdqa32_store(jitptr, AX, (i+1)<<6, (i>>1)+8); } /* cmp/jcc */ write64(jitptr, 0x800FC03948 | (AX <<16) | (CX <<19) | ((uint64_t)JL <<32)); if(info->jitOptStrat == GF16_XOR_JIT_STRAT_COPYNT || info->jitOptStrat == GF16_XOR_JIT_STRAT_COPY) { write32(jitptr +5, (int32_t)(((intptr_t)jitTemp - (jitdst - (uint8_t*)jit->w)) - (intptr_t)jitptr -9)); jitptr[9] = 0xC3; /* ret */ /* memcpy to destination */ if(info->jitOptStrat == GF16_XOR_JIT_STRAT_COPYNT) { // 256-bit NT copies never seem to be better, so just stick to 128-bit for(uint_fast32_t i=0; i<(uint_fast32_t)(jitptr+10-jitTemp); i+=64) { __m128i ta = _mm_load_si128((__m128i*)(jitTemp + i)); __m128i tb = _mm_load_si128((__m128i*)(jitTemp + i + 16)); __m128i tc = _mm_load_si128((__m128i*)(jitTemp + i + 32)); __m128i td = _mm_load_si128((__m128i*)(jitTemp + i + 48)); _mm_stream_si128((__m128i*)(jitdst + i), ta); _mm_stream_si128((__m128i*)(jitdst + i + 16), tb); _mm_stream_si128((__m128i*)(jitdst + i + 32), tc); _mm_stream_si128((__m128i*)(jitdst + i + 48), td); } } else { /* AVX does result in fewer writes, but testing on Haswell seems to indicate minimal benefit over SSE2 */ for(uint_fast32_t i=0; i<(uint_fast32_t)(jitptr+10-jitTemp); i+=64) { __m256i ta = _mm256_load_si256((__m256i*)(jitTemp + i)); __m256i tb = _mm256_load_si256((__m256i*)(jitTemp + i + 32)); _mm256_store_si256((__m256i*)(jitdst + i), ta); _mm256_store_si256((__m256i*)(jitdst + i + 32), tb); } } } else { write32(jitptr +5, (int32_t)((uint8_t*)jit->w - jitptr -9)); jitptr[9] = 0xC3; /* ret */ } #ifdef GF16_XORJIT_ENABLE_DUAL_MAPPING if(jit->w != jit->x) { // TODO: need to serialize? } #endif gf16_xor512_jit_multi_stub( (intptr_t)dst + offset - 1024, (intptr_t)dst + offset + len - 1024, srcPtr, jit->x ); } _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(regions); UNUSED(offset); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficients); UNUSED(mutScratch); #endif } void gf16_xor_jit_muladd_multi_packed_avx512(const void *HEDLEY_RESTRICT scratch, unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch) { #ifdef _AVAILABLE const struct gf16_xor_scratch *HEDLEY_RESTRICT info = (const struct gf16_xor_scratch*)scratch; jit_wx_pair* jit = (jit_wx_pair*)mutScratch; ALIGN_TO(64, uint8_t jitTemp[XORDEP_JIT_SIZE]); // for copying only uint8_t* jitCode = (uint8_t*)jit->w + info->codeStart; for(unsigned region=0; region XOR512_MULTI_REGIONS) numRegions = XOR512_MULTI_REGIONS; if(lastRegions > XOR512_MULTI_REGIONS) lastRegions = XOR512_MULTI_REGIONS; uint8_t* jitptr = jitCode; uint8_t* jitdst = jitptr; if(info->jitOptStrat == GF16_XOR_JIT_STRAT_COPYNT || info->jitOptStrat == GF16_XOR_JIT_STRAT_COPY) { if((uintptr_t)jitdst & 0x1F) { /* copy unaligned part (might not be worth it for these CPUs, but meh) */ _mm256_store_si256((__m256i*)jitTemp, _mm256_load_si256((__m256i*)((uintptr_t)jitptr & ~(uintptr_t)0x1F))); jitptr = jitTemp + ((uintptr_t)jitdst & 0x1F); jitdst -= (uintptr_t)jitdst & 0x1F; } else jitptr = jitTemp; } else if(info->jitOptStrat == GF16_XOR_JIT_STRAT_CLR) { for(int i=0; i>1); jitptr += _jit_vmovdqa32_store(jitptr, AX, (i+1)<<6, (i>>1)+8); } /* cmp/jcc */ write64(jitptr, 0x800FC03948 | (AX <<16) | (CX <<19) | ((uint64_t)JL <<32)); if(info->jitOptStrat == GF16_XOR_JIT_STRAT_COPYNT || info->jitOptStrat == GF16_XOR_JIT_STRAT_COPY) { write32(jitptr +5, (int32_t)(((intptr_t)jitTemp - (jitdst - (uint8_t*)jit->w)) - (intptr_t)jitptr -9)); jitptr[9] = 0xC3; /* ret */ /* memcpy to destination */ if(info->jitOptStrat == GF16_XOR_JIT_STRAT_COPYNT) { // 256-bit NT copies never seem to be better, so just stick to 128-bit for(uint_fast32_t i=0; i<(uint_fast32_t)(jitptr+10-jitTemp); i+=64) { __m128i ta = _mm_load_si128((__m128i*)(jitTemp + i)); __m128i tb = _mm_load_si128((__m128i*)(jitTemp + i + 16)); __m128i tc = _mm_load_si128((__m128i*)(jitTemp + i + 32)); __m128i td = _mm_load_si128((__m128i*)(jitTemp + i + 48)); _mm_stream_si128((__m128i*)(jitdst + i), ta); _mm_stream_si128((__m128i*)(jitdst + i + 16), tb); _mm_stream_si128((__m128i*)(jitdst + i + 32), tc); _mm_stream_si128((__m128i*)(jitdst + i + 48), td); } } else { /* AVX does result in fewer writes, but testing on Haswell seems to indicate minimal benefit over SSE2 */ for(uint_fast32_t i=0; i<(uint_fast32_t)(jitptr+10-jitTemp); i+=64) { __m256i ta = _mm256_load_si256((__m256i*)(jitTemp + i)); __m256i tb = _mm256_load_si256((__m256i*)(jitTemp + i + 32)); _mm256_store_si256((__m256i*)(jitdst + i), ta); _mm256_store_si256((__m256i*)(jitdst + i + 32), tb); } } } else { write32(jitptr +5, (int32_t)((uint8_t*)jit->w - jitptr -9)); jitptr[9] = 0xC3; /* ret */ } #ifdef GF16_XORJIT_ENABLE_DUAL_MAPPING if(jit->w != jit->x) { // TODO: need to serialize? } #endif gf16_xor512_jit_stub( (intptr_t)dst - 1024, (intptr_t)dst + len - 1024, (intptr_t)src + len*region - 1024, 0, jit->x ); } _mm256_zeroupper(); #else UNUSED(scratch); UNUSED(packRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficients); UNUSED(mutScratch); #endif } // TODO: gf16_xor_jit_muladd_multi_packpf_avx512 if bored enough #ifdef _AVAILABLE static HEDLEY_ALWAYS_INLINE void gf16_xor_finish_bit_extract(uint64_t* dst, __m512i src) { __m512i lo_nibble_test = _mm512_set_epi32( 0x08080808, 0x08080808, 0x08080808, 0x08080808, 0x04040404, 0x04040404, 0x04040404, 0x04040404, 0x02020202, 0x02020202, 0x02020202, 0x02020202, 0x01010101, 0x01010101, 0x01010101, 0x01010101 ); __m512i hi_nibble_test = _mm512_set_epi32( 0x80808080, 0x80808080, 0x80808080, 0x80808080, 0x40404040, 0x40404040, 0x40404040, 0x40404040, 0x20202020, 0x20202020, 0x20202020, 0x20202020, 0x10101010, 0x10101010, 0x10101010, 0x10101010 ); __m512i lane = _mm512_shuffle_i32x4(src, src, _MM_SHUFFLE(0,0,0,0)); write64(dst+0, _mm512_test_epi8_mask(lane, lo_nibble_test)); write64(dst+1, _mm512_test_epi8_mask(lane, hi_nibble_test)); lane = _mm512_shuffle_i32x4(src, src, _MM_SHUFFLE(1,1,1,1)); write64(dst+32 +0, _mm512_test_epi8_mask(lane, lo_nibble_test)); write64(dst+32 +1, _mm512_test_epi8_mask(lane, hi_nibble_test)); lane = _mm512_shuffle_i32x4(src, src, _MM_SHUFFLE(2,2,2,2)); write64(dst+64 +0, _mm512_test_epi8_mask(lane, lo_nibble_test)); write64(dst+64 +1, _mm512_test_epi8_mask(lane, hi_nibble_test)); lane = _mm512_shuffle_i32x4(src, src, _MM_SHUFFLE(3,3,3,3)); write64(dst+96 +0, _mm512_test_epi8_mask(lane, lo_nibble_test)); write64(dst+96 +1, _mm512_test_epi8_mask(lane, hi_nibble_test)); } static HEDLEY_ALWAYS_INLINE void _gf16_xor_finish_copy_block_avx512(void* dst, const void* src) { uint64_t* _src = (uint64_t*)src; uint64_t* _dst = (uint64_t*)dst; // 32 registers available, so load entire block // Clang doesn't seem to like arrays (always spills them to memory), so write out everything __m512i src0 = _mm512_load_si512(_src + 120 - 0*8); __m512i src1 = _mm512_load_si512(_src + 120 - 1*8); __m512i src2 = _mm512_load_si512(_src + 120 - 2*8); __m512i src3 = _mm512_load_si512(_src + 120 - 3*8); __m512i src4 = _mm512_load_si512(_src + 120 - 4*8); __m512i src5 = _mm512_load_si512(_src + 120 - 5*8); __m512i src6 = _mm512_load_si512(_src + 120 - 6*8); __m512i src7 = _mm512_load_si512(_src + 120 - 7*8); __m512i src8 = _mm512_load_si512(_src + 120 - 8*8); __m512i src9 = _mm512_load_si512(_src + 120 - 9*8); __m512i src10 = _mm512_load_si512(_src + 120 - 10*8); __m512i src11 = _mm512_load_si512(_src + 120 - 11*8); __m512i src12 = _mm512_load_si512(_src + 120 - 12*8); __m512i src13 = _mm512_load_si512(_src + 120 - 13*8); __m512i src14 = _mm512_load_si512(_src + 120 - 14*8); __m512i src15 = _mm512_load_si512(_src + 120 - 15*8); // interleave to words, dwords, qwords etc __m512i srcW0 = _mm512_unpacklo_epi8(src0, src1); __m512i srcW1 = _mm512_unpackhi_epi8(src0, src1); __m512i srcW2 = _mm512_unpacklo_epi8(src2, src3); __m512i srcW3 = _mm512_unpackhi_epi8(src2, src3); __m512i srcW4 = _mm512_unpacklo_epi8(src4, src5); __m512i srcW5 = _mm512_unpackhi_epi8(src4, src5); __m512i srcW6 = _mm512_unpacklo_epi8(src6, src7); __m512i srcW7 = _mm512_unpackhi_epi8(src6, src7); __m512i srcW8 = _mm512_unpacklo_epi8(src8, src9); __m512i srcW9 = _mm512_unpackhi_epi8(src8, src9); __m512i srcW10 = _mm512_unpacklo_epi8(src10, src11); __m512i srcW11 = _mm512_unpackhi_epi8(src10, src11); __m512i srcW12 = _mm512_unpacklo_epi8(src12, src13); __m512i srcW13 = _mm512_unpackhi_epi8(src12, src13); __m512i srcW14 = _mm512_unpacklo_epi8(src14, src15); __m512i srcW15 = _mm512_unpackhi_epi8(src14, src15); __m512i srcD0 = _mm512_unpacklo_epi16(srcW0, srcW2); __m512i srcD1 = _mm512_unpackhi_epi16(srcW0, srcW2); __m512i srcD2 = _mm512_unpacklo_epi16(srcW1, srcW3); __m512i srcD3 = _mm512_unpackhi_epi16(srcW1, srcW3); __m512i srcD4 = _mm512_unpacklo_epi16(srcW4, srcW6); __m512i srcD5 = _mm512_unpackhi_epi16(srcW4, srcW6); __m512i srcD6 = _mm512_unpacklo_epi16(srcW5, srcW7); __m512i srcD7 = _mm512_unpackhi_epi16(srcW5, srcW7); __m512i srcD8 = _mm512_unpacklo_epi16(srcW8, srcW10); __m512i srcD9 = _mm512_unpackhi_epi16(srcW8, srcW10); __m512i srcD10 = _mm512_unpacklo_epi16(srcW9, srcW11); __m512i srcD11 = _mm512_unpackhi_epi16(srcW9, srcW11); __m512i srcD12 = _mm512_unpacklo_epi16(srcW12, srcW14); __m512i srcD13 = _mm512_unpackhi_epi16(srcW12, srcW14); __m512i srcD14 = _mm512_unpacklo_epi16(srcW13, srcW15); __m512i srcD15 = _mm512_unpackhi_epi16(srcW13, srcW15); __m512i srcQ0 = _mm512_unpacklo_epi32(srcD0, srcD4); __m512i srcQ1 = _mm512_unpackhi_epi32(srcD0, srcD4); __m512i srcQ2 = _mm512_unpacklo_epi32(srcD1, srcD5); __m512i srcQ3 = _mm512_unpackhi_epi32(srcD1, srcD5); __m512i srcQ4 = _mm512_unpacklo_epi32(srcD2, srcD6); __m512i srcQ5 = _mm512_unpackhi_epi32(srcD2, srcD6); __m512i srcQ6 = _mm512_unpacklo_epi32(srcD3, srcD7); __m512i srcQ7 = _mm512_unpackhi_epi32(srcD3, srcD7); __m512i srcQ8 = _mm512_unpacklo_epi32(srcD8, srcD12); __m512i srcQ9 = _mm512_unpackhi_epi32(srcD8, srcD12); __m512i srcQ10 = _mm512_unpacklo_epi32(srcD9, srcD13); __m512i srcQ11 = _mm512_unpackhi_epi32(srcD9, srcD13); __m512i srcQ12 = _mm512_unpacklo_epi32(srcD10, srcD14); __m512i srcQ13 = _mm512_unpackhi_epi32(srcD10, srcD14); __m512i srcQ14 = _mm512_unpacklo_epi32(srcD11, srcD15); __m512i srcQ15 = _mm512_unpackhi_epi32(srcD11, srcD15); __m512i srcDQ0 = _mm512_unpacklo_epi64(srcQ0, srcQ8); __m512i srcDQ1 = _mm512_unpackhi_epi64(srcQ0, srcQ8); __m512i srcDQ2 = _mm512_unpacklo_epi64(srcQ1, srcQ9); __m512i srcDQ3 = _mm512_unpackhi_epi64(srcQ1, srcQ9); __m512i srcDQ4 = _mm512_unpacklo_epi64(srcQ2, srcQ10); __m512i srcDQ5 = _mm512_unpackhi_epi64(srcQ2, srcQ10); __m512i srcDQ6 = _mm512_unpacklo_epi64(srcQ3, srcQ11); __m512i srcDQ7 = _mm512_unpackhi_epi64(srcQ3, srcQ11); __m512i srcDQ8 = _mm512_unpacklo_epi64(srcQ4, srcQ12); __m512i srcDQ9 = _mm512_unpackhi_epi64(srcQ4, srcQ12); __m512i srcDQ10 = _mm512_unpacklo_epi64(srcQ5, srcQ13); __m512i srcDQ11 = _mm512_unpackhi_epi64(srcQ5, srcQ13); __m512i srcDQ12 = _mm512_unpacklo_epi64(srcQ6, srcQ14); __m512i srcDQ13 = _mm512_unpackhi_epi64(srcQ6, srcQ14); __m512i srcDQ14 = _mm512_unpacklo_epi64(srcQ7, srcQ15); __m512i srcDQ15 = _mm512_unpackhi_epi64(srcQ7, srcQ15); // for each vector, broadcast each lane, and use a testmb to pull the bits in the right order. These can be stored straight to memory // unfortunately, GCC 9.2 insists on moving the mask back to a vector register, even if the `_store_mask64` intrinsic is used, so this doesn't perform too well. But still seems to bench better than the previous code, which tried to move masks to a vector register to shuffle the words into place. Not an issue on Clang 9. gf16_xor_finish_bit_extract(_dst + 0, srcDQ0); gf16_xor_finish_bit_extract(_dst + 2, srcDQ1); gf16_xor_finish_bit_extract(_dst + 4, srcDQ2); gf16_xor_finish_bit_extract(_dst + 6, srcDQ3); gf16_xor_finish_bit_extract(_dst + 8, srcDQ4); gf16_xor_finish_bit_extract(_dst + 10, srcDQ5); gf16_xor_finish_bit_extract(_dst + 12, srcDQ6); gf16_xor_finish_bit_extract(_dst + 14, srcDQ7); gf16_xor_finish_bit_extract(_dst + 16, srcDQ8); gf16_xor_finish_bit_extract(_dst + 18, srcDQ9); gf16_xor_finish_bit_extract(_dst + 20, srcDQ10); gf16_xor_finish_bit_extract(_dst + 22, srcDQ11); gf16_xor_finish_bit_extract(_dst + 24, srcDQ12); gf16_xor_finish_bit_extract(_dst + 26, srcDQ13); gf16_xor_finish_bit_extract(_dst + 28, srcDQ14); gf16_xor_finish_bit_extract(_dst + 30, srcDQ15); } void gf16_xor_finish_block_avx512(void *HEDLEY_RESTRICT dst) { _gf16_xor_finish_copy_block_avx512(dst, dst); } void gf16_xor_finish_copy_block_avx512(void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src) { _gf16_xor_finish_copy_block_avx512(dst, src); } void gf16_xor_finish_copy_blocku_avx512(void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t bytes) { uint16_t block[512]; _gf16_xor_finish_copy_block_avx512(block, src); memcpy(dst, block, bytes); } #endif #define MWORD_SIZE 64 #define _mword __m512i #define _MM(f) _mm512_ ## f #define _MMI(f) _mm512_ ## f ## _si512 #define _FNSUFFIX _avx512 #define _MM_END _mm256_zeroupper(); #ifdef _AVAILABLE # include "gf16_checksum_x86.h" #endif #include "gf16_xor_common_funcs.h" #undef MWORD_SIZE #undef _mword #undef _MM #undef _MMI #undef _FNSUFFIX #undef _MM_END #ifdef _AVAILABLE GF_FINISH_PACKED_FUNCS(gf16_xor, _avx512, sizeof(__m512i)*16, gf16_xor_finish_copy_block_avx512, gf16_xor_finish_copy_blocku_avx512, 1, _mm256_zeroupper(), gf16_checksum_block_avx512, gf16_checksum_blocku_avx512, gf16_checksum_exp_avx512, &gf16_xor_finish_block_avx512, sizeof(__m512i)) #else GF_FINISH_PACKED_FUNCS_STUB(gf16_xor, _avx512) #endif void* gf16_xor_jit_init_avx512(int polynomial, int jitOptStrat) { #ifdef _AVAILABLE struct gf16_xor_scratch* ret; uint8_t tmpCode[XORDEP_JIT_CODE_SIZE]; ALIGN_ALLOC(ret, sizeof(struct gf16_xor_scratch), 32); gf16_bitdep_init256(ret->deps, polynomial, 0); ret->jitOptStrat = jitOptStrat; ret->codeStart = (uint_fast16_t)xor_write_init_jit(tmpCode); return ret; #else UNUSED(polynomial); UNUSED(jitOptStrat); return NULL; #endif } void* gf16_xor_jit_init_mut_avx512() { #ifdef _AVAILABLE jit_wx_pair *jitCode = jit_alloc(XORDEP_JIT_SIZE*2); if(!jitCode) return NULL; xor_write_init_jit(jitCode->w); return jitCode; #else return NULL; #endif } par2cmdline-turbo-1.4.0/parpar/gf16/gf16_xor_common.h000066400000000000000000000216621514221355600222640ustar00rootroot00000000000000#include "gf16_global.h" #include "../src/platform.h" #ifdef PLATFORM_X86 #include "x86_jit.h" #include "gf16_xor.h" #define XORDEP_JIT_SIZE 4096 #define XORDEP_JIT_CODE_SIZE 1280 #define XORDEP_JIT_MODE_MUL 0 #define XORDEP_JIT_MODE_MULADD 1 #define XORDEP_JIT_MODE_MUL_INSITU 2 /* we support MSVC and GCC style ASM */ #ifdef PLATFORM_AMD64 # ifdef _MSC_VER /* specified in external file, as we can't use inline ASM for 64-bit MSVC */ extern void gf16_xor_jit_stub(intptr_t src, intptr_t dEnd, intptr_t dest, intptr_t pf, void* fn); # ifdef __AVX2__ # define gf16_xor512_jit_stub gf16_xor256_jit_stub # define gf16_xor512_jit_multi_stub gf16_xor256_jit_multi_stub extern void gf16_xor256_jit_stub(intptr_t src, intptr_t dEnd, intptr_t dest, intptr_t pf, void* fn); extern void gf16_xor256_jit_multi_stub(intptr_t dst, intptr_t dstEnd, const void** src, void* fn); # endif # else # define CALL_PTR "callq *%q" # ifdef DBG_XORDEP # include # define WRITE_JIT(l) { \ FILE* fp = fopen("code.bin", "wb"); \ fwrite(fn, l, 1, fp); \ fclose(fp); \ } // disassemble with `objdump -b binary -D -m i386:x86-64 -M intel code.bin|less` # else # define WRITE_JIT(l) # endif static HEDLEY_ALWAYS_INLINE void gf16_xor_jit_stub(intptr_t src, intptr_t dEnd, intptr_t dest, intptr_t pf, void* fn) { WRITE_JIT(2048) __asm__ volatile( CALL_PTR "[f]\n" : "+a"(src), "+d"(dest), "+S"(pf) : "c"(dEnd), [f]"r"(fn) : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12", "%xmm13", "%xmm14", "%xmm15", "memory" ); } # ifdef __AVX2__ static HEDLEY_ALWAYS_INLINE void gf16_xor256_jit_stub(intptr_t src, intptr_t dEnd, intptr_t dest, intptr_t pf, void* fn) { WRITE_JIT(2048) __asm__ volatile( CALL_PTR "[f]\n" : "+a"(src), "+d"(dest), "+S"(pf) : "c"(dEnd), [f]"r"(fn) : "memory" // GCC pre 4.9 doesn't accept YMM registers # if HEDLEY_GCC_VERSION_CHECK(4,9,0) || !defined(HEDLEY_GCC_VERSION) , "%ymm0", "%ymm1", "%ymm2", "%ymm3", "%ymm4", "%ymm5", "%ymm6", "%ymm7", "%ymm8", "%ymm9", "%ymm10", "%ymm11", "%ymm12", "%ymm13", "%ymm14", "%ymm15" # endif ); } # endif # ifdef __AVX512F__ static HEDLEY_ALWAYS_INLINE void gf16_xor512_jit_stub(intptr_t src, intptr_t dEnd, intptr_t dest, intptr_t pf, void* fn) { WRITE_JIT(2048) __asm__ volatile( CALL_PTR "[f]\n" : "+a"(src), "+d"(dest), "+S"(pf) : "c"(dEnd), [f]"r"(fn) : "%zmm1", "%zmm2", "%zmm3", "%zmm16", "%zmm17", "%zmm18", "%zmm19", "%zmm20", "%zmm21", "%zmm22", "%zmm23", "%zmm24", "%zmm25", "%zmm26", "%zmm27", "%zmm28", "%zmm29", "%zmm30", "%zmm31", "memory" ); } static HEDLEY_ALWAYS_INLINE void gf16_xor512_jit_multi_stub( intptr_t dst, intptr_t dstEnd, const void** src, void* fn ) { WRITE_JIT(8192) if(sizeof(*src) == 8) { __asm__ volatile( "movq 8(%%rdx), %%rsi\n" "movq 16(%%rdx), %%rdi\n" "movq 24(%%rdx), %%r8\n" "movq 32(%%rdx), %%r9\n" "movq 40(%%rdx), %%r10\n" "movq 48(%%rdx), %%r11\n" "movq 56(%%rdx), %%rbx\n" "movq 64(%%rdx), %%r14\n" "movq 72(%%rdx), %%r15\n" "movq (%%rdx), %%rdx\n" CALL_PTR "[f]\n" : "+a"(dst), "+d"(src) : "c"(dstEnd), [f]"r"(fn) : "%rbx", "%rsi", "%rdi", "%r8", "%r9", "%r10", "%r11", "%r14", "%r15", "%zmm0", "%zmm1", "%zmm2", "%zmm3", "%zmm4", "%zmm5", "%zmm6", "%zmm7", "%zmm8", "%zmm9", "%zmm10", "%zmm11", "%zmm12", "%zmm13", "%zmm14", "%zmm15", "%zmm16", "%zmm17", "%zmm18", "%zmm19", "%zmm20", "%zmm21", "%zmm22", "%zmm23", "%zmm24", "%zmm25", "%zmm26", "%zmm27", "%zmm28", "%zmm29", "%zmm30", "%zmm31", "memory" ); } else { // x32 assert(sizeof(*src) == 4); __asm__ volatile( "movl 4(%%rdx), %%esi\n" "movl 8(%%rdx), %%edi\n" "movl 12(%%rdx), %%r8d\n" "movl 16(%%rdx), %%r9d\n" "movl 20(%%rdx), %%r10d\n" "movl 24(%%rdx), %%r11d\n" "movl 28(%%rdx), %%ebx\n" "movl 32(%%rdx), %%r14d\n" "movl 36(%%rdx), %%r15d\n" "movl (%%rdx), %%edx\n" CALL_PTR "[f]\n" : "+a"(dst), "+d"(src) : "c"(dstEnd), [f]"r"(fn) : "%rbx", "%rsi", "%rdi", "%r8", "%r9", "%r10", "%r11", "%r14", "%r15", "%zmm0", "%zmm1", "%zmm2", "%zmm3", "%zmm4", "%zmm5", "%zmm6", "%zmm7", "%zmm8", "%zmm9", "%zmm10", "%zmm11", "%zmm12", "%zmm13", "%zmm14", "%zmm15", "%zmm16", "%zmm17", "%zmm18", "%zmm19", "%zmm20", "%zmm21", "%zmm22", "%zmm23", "%zmm24", "%zmm25", "%zmm26", "%zmm27", "%zmm28", "%zmm29", "%zmm30", "%zmm31", "memory" ); } } # endif # undef CALL_PTR # undef WRITE_JIT # endif #else # ifdef _MSC_VER static HEDLEY_ALWAYS_INLINE void gf16_xor_jit_stub(intptr_t src, intptr_t dEnd, intptr_t dest, intptr_t pf, void* fn) { __asm { mov eax, src mov ecx, dEnd mov edx, dest mov esi, pf call fn } } # else static HEDLEY_ALWAYS_INLINE void gf16_xor_jit_stub(intptr_t src, intptr_t dEnd, intptr_t dest, intptr_t pf, void* fn) { __asm__ volatile( "calll *%[f]\n" : "+a"(src), "+d"(dest), "+S"(pf) : "c"(dEnd), [f]"r"(fn) : "%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7", "memory" ); } # endif #endif struct gf16_xor_scratch { uint8_t deps[16*16*2*4]; int jitOptStrat; // GF16_XOR_JIT_STRAT_* uint_fast16_t codeStart; uint_fast16_t codeStartInsitu; }; #ifdef __SSE2__ typedef void*(*gf16_xorjit_write_func)(const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT jitptr, uint16_t val, const int xor, const int prefetch); static HEDLEY_ALWAYS_INLINE void gf16_xorjit_write_jit(const void* scratch, uint16_t coefficient, jit_wx_pair* jit, const int mode, const int prefetch, gf16_xorjit_write_func writeFunc, const void* funcScratch) { const struct gf16_xor_scratch* info = (const struct gf16_xor_scratch*)scratch; uint8_t* jitWPtr = (uint8_t*)jit->w; uint8_t* jitptr; if(mode == XORDEP_JIT_MODE_MUL_INSITU) { jitWPtr += XORDEP_JIT_SIZE/2; jitptr = jitWPtr + info->codeStartInsitu; } else { jitptr = jitWPtr + info->codeStart; } if(info->jitOptStrat == GF16_XOR_JIT_STRAT_COPYNT || info->jitOptStrat == GF16_XOR_JIT_STRAT_COPY) { ALIGN_TO(_GF16_XORJIT_COPY_ALIGN, uint8_t jitTemp[XORDEP_JIT_CODE_SIZE]); uintptr_t copyOffset = (mode == XORDEP_JIT_MODE_MUL_INSITU) ? info->codeStartInsitu : info->codeStart; if((uintptr_t)jitptr & (_GF16_XORJIT_COPY_ALIGN-1)) { // copy unaligned part #if _GF16_XORJIT_COPY_ALIGN == 32 && defined(__AVX2__) _mm256_store_si256((__m256i*)jitTemp, _mm256_load_si256((__m256i*)((uintptr_t)jitptr & ~(uintptr_t)(_GF16_XORJIT_COPY_ALIGN-1)))); #else _mm_store_si128((__m128i*)jitTemp, _mm_load_si128((__m128i*)((uintptr_t)jitptr & ~(uintptr_t)(_GF16_XORJIT_COPY_ALIGN-1)))); #endif copyOffset -= (uintptr_t)jitptr & (_GF16_XORJIT_COPY_ALIGN-1); jitptr = jitTemp + ((uintptr_t)jitptr & (_GF16_XORJIT_COPY_ALIGN-1)); } else jitptr = jitTemp; jitptr = writeFunc(funcScratch, jitptr, coefficient, mode, prefetch); write32(jitptr, (int32_t)((intptr_t)jitTemp - copyOffset - (intptr_t)jitptr -4)); jitptr[4] = 0xC3; /* ret */ jitptr += 5; /* memcpy to destination */ uint8_t* jitdst = jitWPtr + copyOffset; if(info->jitOptStrat == GF16_XOR_JIT_STRAT_COPYNT) { // 256-bit NT copies never seem to be better, so just stick to 128-bit for(uint_fast32_t i=0; i<(uint_fast32_t)(jitptr-jitTemp); i+=64) { __m128i ta = _mm_load_si128((__m128i*)(jitTemp + i)); __m128i tb = _mm_load_si128((__m128i*)(jitTemp + i + 16)); __m128i tc = _mm_load_si128((__m128i*)(jitTemp + i + 32)); __m128i td = _mm_load_si128((__m128i*)(jitTemp + i + 48)); _mm_stream_si128((__m128i*)(jitdst + i), ta); _mm_stream_si128((__m128i*)(jitdst + i + 16), tb); _mm_stream_si128((__m128i*)(jitdst + i + 32), tc); _mm_stream_si128((__m128i*)(jitdst + i + 48), td); } } else { // GCC probably turns these into memcpy calls anyway... #if _GF16_XORJIT_COPY_ALIGN == 32 && defined(__AVX2__) for(uint_fast32_t i=0; i<(uint_fast32_t)(jitptr-jitTemp); i+=64) { __m256i ta = _mm256_load_si256((__m256i*)(jitTemp + i)); __m256i tb = _mm256_load_si256((__m256i*)(jitTemp + i + 32)); _mm256_store_si256((__m256i*)(jitdst + i), ta); _mm256_store_si256((__m256i*)(jitdst + i + 32), tb); } #else for(uint_fast32_t i=0; i<(uint_fast32_t)(jitptr-jitTemp); i+=64) { __m128i ta = _mm_load_si128((__m128i*)(jitTemp + i)); __m128i tb = _mm_load_si128((__m128i*)(jitTemp + i + 16)); __m128i tc = _mm_load_si128((__m128i*)(jitTemp + i + 32)); __m128i td = _mm_load_si128((__m128i*)(jitTemp + i + 48)); _mm_store_si128((__m128i*)(jitdst + i), ta); _mm_store_si128((__m128i*)(jitdst + i + 16), tb); _mm_store_si128((__m128i*)(jitdst + i + 32), tc); _mm_store_si128((__m128i*)(jitdst + i + 48), td); } #endif } } else { if(info->jitOptStrat == GF16_XOR_JIT_STRAT_CLR) { // clear 1 byte per cacheline for(int i=0; iw != jit->x) { // TODO: need to serialize? } #endif } #endif #endif /* PLATFORM_X86 */ par2cmdline-turbo-1.4.0/parpar/gf16/gf16_xor_common_funcs.h000066400000000000000000000135471514221355600234650ustar00rootroot00000000000000 #include "../src/hedley.h" #include #include /* type returned by *movemask* function */ #if MWORD_SIZE == 64 # define umask_t uint64_t #elif MWORD_SIZE == 32 # define umask_t uint32_t #else # define umask_t uint16_t #endif #if MWORD_SIZE == 64 # define MOVMASK _mm512_movepi8_mask #else # define MOVMASK _MM(movemask_epi8) #endif #ifdef _AVAILABLE # include "gf16_checksum_x86.h" static HEDLEY_ALWAYS_INLINE void gf16_xor_prep_split(_mword ta, _mword tb, _mword* tl, _mword* th) { /* split to high/low parts */ #if MWORD_SIZE == 64 // arrange to hlhl... _mword tmp1 = _mm512_shuffle_epi8(ta, _mm512_set4_epi32(0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200)); _mword tmp2 = _mm512_shuffle_epi8(tb, _mm512_set4_epi32(0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200)); *th = _mm512_permutex2var_epi64(tmp1, _mm512_set_epi64( 15, 13, 11, 9, 7, 5, 3, 1 ), tmp2); *tl = _mm512_permutex2var_epi64(tmp1, _mm512_set_epi64( 14, 12, 10, 8, 6, 4, 2, 0 ), tmp2); #elif MWORD_SIZE == 32 // arrange to hhhhhhhhllllllllhhhhhhhhllllllll _mword tmp1 = _mm256_shuffle_epi8(ta, _mm256_set_epi32( 0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200, 0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200 )); // arrange to llllllllhhhhhhhhllllllllhhhhhhhh _mword tmp2 = _mm256_shuffle_epi8(tb, _mm256_set_epi32( 0x0e0c0a08, 0x06040200, 0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200, 0x0f0d0b09, 0x07050301 )); *th = _mm256_blend_epi32(tmp1, tmp2, 0x33); *tl = _mm256_blend_epi32(tmp2, tmp1, 0x33); *tl = _mm256_permute4x64_epi64(*tl, _MM_SHUFFLE(3,1,2,0)); *th = _mm256_permute4x64_epi64(*th, _MM_SHUFFLE(2,0,3,1)); #else *th = _mm_packus_epi16( _mm_srli_epi16(ta, 8), _mm_srli_epi16(tb, 8) ); *tl = _mm_packus_epi16( _mm_and_si128(ta, _mm_set1_epi16(0xff)), _mm_and_si128(tb, _mm_set1_epi16(0xff)) ); #endif } static HEDLEY_ALWAYS_INLINE void gf16_xor_prep_write(umask_t* _dst, _mword bytes) { _dst[0] = MOVMASK(bytes); for(int i=1; i<8; i++) { bytes = _MM(add_epi8)(bytes, bytes); _dst[i*8] = MOVMASK(bytes); } } static HEDLEY_ALWAYS_INLINE void _FN(gf16_xor_prepare_block)(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { uint8_t* _src = (uint8_t*)src; umask_t* _dst = (umask_t*)dst; _mword tl, th; for(int j=0; j<8; j++) { gf16_xor_prep_split(_MMI(loadu)((_mword*)_src), _MMI(loadu)((_mword*)_src + 1), &tl, &th); /* save to dest by extracting masks */ gf16_xor_prep_write(_dst, th); gf16_xor_prep_write(_dst+64, tl); _src += sizeof(_mword)*2; _dst++; } } static HEDLEY_ALWAYS_INLINE void _FN(gf16_xor_prepare_block_insitu)(void* dst, const void* src) { assert(dst == src); _mword* _src = (_mword*)src; umask_t* _dst = (umask_t*)dst; _mword tl0, tl1, tl2, tl3, tl4, tl5, tl6, tl7; _mword th0, th1, th2, th3, th4, th5, th6, th7; // load 8 registers (need to load the first half of the block) gf16_xor_prep_split(_MMI(loadu)(_src + 0), _MMI(loadu)(_src + 1), &tl0, &th0); gf16_xor_prep_split(_MMI(loadu)(_src + 2), _MMI(loadu)(_src + 3), &tl1, &th1); gf16_xor_prep_split(_MMI(loadu)(_src + 4), _MMI(loadu)(_src + 5), &tl2, &th2); gf16_xor_prep_split(_MMI(loadu)(_src + 6), _MMI(loadu)(_src + 7), &tl3, &th3); // free up 4 of them (th* can now be freely written) gf16_xor_prep_write(_dst+0, th0); gf16_xor_prep_write(_dst+1, th1); gf16_xor_prep_write(_dst+2, th2); gf16_xor_prep_write(_dst+3, th3); gf16_xor_prep_split(_MMI(loadu)(_src + 8), _MMI(loadu)(_src + 9), &tl4, &th4); gf16_xor_prep_write(_dst+4, th4); gf16_xor_prep_split(_MMI(loadu)(_src + 10), _MMI(loadu)(_src + 11), &tl5, &th5); gf16_xor_prep_write(_dst+5, th5); gf16_xor_prep_split(_MMI(loadu)(_src + 12), _MMI(loadu)(_src + 13), &tl6, &th6); gf16_xor_prep_write(_dst+6, th6); gf16_xor_prep_split(_MMI(loadu)(_src + 14), _MMI(loadu)(_src + 15), &tl7, &th7); gf16_xor_prep_write(_dst+7, th7); gf16_xor_prep_write(_dst+64, tl0); gf16_xor_prep_write(_dst+65, tl1); gf16_xor_prep_write(_dst+66, tl2); gf16_xor_prep_write(_dst+67, tl3); gf16_xor_prep_write(_dst+68, tl4); gf16_xor_prep_write(_dst+69, tl5); gf16_xor_prep_write(_dst+70, tl6); gf16_xor_prep_write(_dst+71, tl7); } static HEDLEY_ALWAYS_INLINE void _FN(gf16_xor_prepare_blocku)(void* dst, const void* src, size_t remaining) { // handle unaligned area with a simple copy and repeat uint8_t tmp[MWORD_SIZE*16] = {0}; memcpy(tmp, src, remaining); _FN(gf16_xor_prepare_block)(dst, tmp); } #endif #ifdef PARPAR_INVERT_SUPPORT void _FN(gf16_xor_prepare)(void* dst, const void* src, size_t srcLen) { #ifdef _AVAILABLE if(dst == src) { // prepare_blocku is unused for in-situ prepare assert(srcLen % (sizeof(_mword)*16) == 0); gf16_prepare(dst, src, srcLen, sizeof(_mword)*16, &_FN(gf16_xor_prepare_block_insitu), &_FN(gf16_xor_prepare_blocku)); } else gf16_prepare(dst, src, srcLen, sizeof(_mword)*16, &_FN(gf16_xor_prepare_block), &_FN(gf16_xor_prepare_blocku)); _MM_END #else UNUSED(dst); UNUSED(src); UNUSED(srcLen); #endif } #endif #ifdef _AVAILABLE # if MWORD_SIZE == 64 GF_PREPARE_PACKED_FUNCS(gf16_xor, _FNSUFFIX, sizeof(_mword)*16, _FN(gf16_xor_prepare_block), _FN(gf16_xor_prepare_blocku), XOR512_MULTI_REGIONS, _MM_END, _mword checksum = _MMI(setzero)(), _FN(gf16_checksum_block), _FN(gf16_checksum_blocku), _FN(gf16_checksum_exp), _FN(gf16_checksum_prepare), sizeof(_mword)) # else GF_PREPARE_PACKED_FUNCS(gf16_xor, _FNSUFFIX, sizeof(_mword)*16, _FN(gf16_xor_prepare_block), _FN(gf16_xor_prepare_blocku), 1, _MM_END, _mword checksum = _MMI(setzero)(), _FN(gf16_checksum_block), _FN(gf16_checksum_blocku), _FN(gf16_checksum_exp), _FN(gf16_checksum_prepare), sizeof(_mword)) # endif #else GF_PREPARE_PACKED_FUNCS_STUB(gf16_xor, _FNSUFFIX) #endif #ifdef PARPAR_INVERT_SUPPORT void _FN(gf16_xor_finish)(void *HEDLEY_RESTRICT dst, size_t len) { #ifdef _AVAILABLE gf16_finish(dst, len, sizeof(_mword)*16, &_FN(gf16_xor_finish_block)); _MM_END #else UNUSED(dst); UNUSED(len); #endif } #endif #undef umask_t #undef MOVMASK par2cmdline-turbo-1.4.0/parpar/gf16/gf16_xor_sse2.c000066400000000000000000001352071514221355600216440ustar00rootroot00000000000000 #define _GF16_XORJIT_COPY_ALIGN 16 #include "gf16_xor_common.h" #undef _GF16_XORJIT_COPY_ALIGN #include #ifdef __SSE2__ int gf16_xor_available_sse2 = 1; #else int gf16_xor_available_sse2 = 0; #endif #ifdef __SSE2__ /* modified versions of PXOR/XORPS mem to have fixed sized instructions */ static HEDLEY_ALWAYS_INLINE size_t _jit_pxor_mod(uint8_t* jit, uint8_t xreg, uint8_t mreg, int32_t offs) { *(jit++) = 0x66; size_t p = _jit_rex_pref(&jit, xreg, 0) +1; xreg &= 7; write32(jit, 0x40EF0F | (xreg <<19) | (mreg <<16)); jit[3] = (uint8_t)offs; return p+4; } static HEDLEY_ALWAYS_INLINE size_t _jit_xorps_mod(uint8_t* jit, uint8_t xreg, uint8_t mreg, int32_t offs) { size_t p = _jit_rex_pref(&jit, xreg, 0); xreg &= 7; write32(jit, 0x40570F | (xreg <<19) | (mreg <<16) | lshift32(offs, 24)); return p+4; } struct gf16_xor_jit_scratch_sse2 { /* code lookup tables for XOR-JIT; should align to 64 to maximize cache line usage */ // TODO: for 32-bit, consider interleaving register sourced items more ALIGN_TO(16, __m128i clut_code1[64]); ALIGN_TO(16, __m128i clut_code2[64]); #ifdef PLATFORM_AMD64 ALIGN_TO(16, __m128i clut_code3[16]); ALIGN_TO(16, __m128i clut_code4[64]); #else ALIGN_TO(16, __m128i clut_code3[64]); ALIGN_TO(16, __m128i clut_code4[16]); #endif ALIGN_TO(16, __m128i clut_code5[64]); ALIGN_TO(16, __m128i clut_code6[16]); ALIGN_TO(16, uint16_t clut_info_mem[64]); ALIGN_TO(16, uint16_t clut_info_reg[64]); ALIGN_TO(16, __m128i clut_nocomm[8*16]); ALIGN_TO(16, uint16_t clut_ncinfo_mem[15]); ALIGN_TO(16, uint16_t clut_ncinfo_rm[15]); ALIGN_TO(16, uint16_t clut_ncinfo_reg[15]); struct gf16_xor_scratch s; }; // seems like the no-common optimisation isn't worth it, so disable it by default #define XORDEP_DISABLE_NO_COMMON 1 static void gf16_xor_create_jit_lut_sse2(struct gf16_xor_jit_scratch_sse2* scratch) { int i, j; memset(scratch->clut_code1, 0, sizeof(scratch->clut_code1)); memset(scratch->clut_code2, 0, sizeof(scratch->clut_code2)); memset(scratch->clut_code3, 0, sizeof(scratch->clut_code3)); memset(scratch->clut_code4, 0, sizeof(scratch->clut_code4)); memset(scratch->clut_code5, 0, sizeof(scratch->clut_code5)); memset(scratch->clut_code6, 0, sizeof(scratch->clut_code6)); /* XOR pairs/triples from memory */ #ifdef PLATFORM_AMD64 #define MEM_XP 1 #define MEM_XT 1 #else #define MEM_XP 5 #define MEM_XT 3 #endif for(i=0; i<64; i++) { int m = i; int posM[4] = {0, 0, 0, 0}; int posR[4] = {0, 0, 0, 0}; uint8_t* pC[6] = { (uint8_t*)(scratch->clut_code1 + i), (uint8_t*)(scratch->clut_code2 + i), i < (int)(sizeof(scratch->clut_code3)/sizeof(*scratch->clut_code3)) ? (uint8_t*)(scratch->clut_code3 + i) : NULL, i < (int)(sizeof(scratch->clut_code4)/sizeof(*scratch->clut_code4)) ? (uint8_t*)(scratch->clut_code4 + i) : NULL, (uint8_t*)(scratch->clut_code5 + i), i < (int)(sizeof(scratch->clut_code6)/sizeof(*scratch->clut_code6)) ? (uint8_t*)(scratch->clut_code6 + i) : NULL }; for(j=0; j<3; j++) { int msk = m&3; int k; if(msk == 1) { // (XORPS) for(k=0; k MOVAPS) if(posM[1] == 0) posM[1] = posM[0] +1; if(posR[1] == 0) posR[1] = posR[0] +1; } else if(msk) { int isCommon = msk == 3; int reg = 1 + isCommon; // (PXOR) for(k=0; k MOVDQA) if(posM[reg+1] == 0) posM[reg+1] = posM[0] +2; if(posR[reg+1] == 0) posR[reg+1] = posR[0] +2; } if(msk) { // bit1 || bit2 int xb = msk != 1; // only bit1 set -> using XORPS (1 less byte) /* advance pointers */ posM[0] += 4+xb; posR[0] += 3+xb; } m >>= 2; } scratch->clut_info_mem[i] = posM[0] | (posM[1] << 4) | (posM[2] << 8) | (posM[3] << 12); scratch->clut_info_reg[i] = posR[0] | (posR[1] << 4) | (posR[2] << 8) | (posR[3] << 12); } #ifndef XORDEP_DISABLE_NO_COMMON memset(scratch->clut_nocomm, 0, sizeof(scratch->clut_code6)); // handle cases of no common-mask optimisation for(i=0; i<15 /* not 16 */; i++) { // since we can only fit 2 pairs in an XMM register, cannot do 6 bit lookups int m = i; int k; int posM[3] = {0, 0, 0}; int posR[3] = {0, 0, 0}; int posRM[3] = {0, 0, 0}; uint8_t* pC[8]; for(k=0; k<8; k++) { pC[k] = (uint8_t*)(scratch->clut_nocomm + i + k*16); } for(j=0; j<2; j++) { if(m & 1) { // (XORPS) for(k=0; k MOVAPS) if(posM[1] == 0) posM[1] = posM[0] +1; if(posR[1] == 0) posR[1] = posR[0] +1; if(posRM[1] == 0) posRM[1] = posRM[0] +1; posM[0] += 4; posR[0] += 3; posRM[0] += 3 + (j==0); } if(m & 2) { // (PXOR) for(k=0; k MOVDQA) if(posM[2] == 0) posM[2] = posM[0] +2; if(posR[2] == 0) posR[2] = posR[0] +2; if(posRM[2] == 0) posRM[2] = posRM[0] +2; posM[0] += 5; posR[0] += 4; posRM[0] += 4 + (j==0); } m >>= 2; } scratch->clut_ncinfo_mem[i] = posM[0] | (posM[1] << 8) | (posM[2] << 12); scratch->clut_ncinfo_reg[i] = posR[0] | (posR[1] << 8) | (posR[2] << 12); scratch->clut_ncinfo_rm[i] = posRM[0] | (posRM[1] << 8) | (posRM[2] << 12); } #endif #undef MEM_XP #undef MEM_XT } /* tune flags set by GCC; not ideal, but good enough I guess (note, I don't care about anything older than Core2) */ #if defined(__tune_core2__) || defined(__tune_atom__) /* on pre-Nehalem Intel CPUs, it is faster to store unaligned XMM registers in halves */ static HEDLEY_ALWAYS_INLINE void STOREU_XMM(void* dest, __m128i xmm) { _mm_storel_epi64((__m128i*)(dest), xmm); _mm_storeh_pi(((__m64*)(dest) +1), _mm_castsi128_ps(xmm)); } #else # define STOREU_XMM(dest, xmm) \ _mm_storeu_si128((__m128i*)(dest), xmm) #endif /* conditional move, because, for whatever reason, no-one thought of making a CMOVcc intrinsic */ #if defined(__GNUC__) || defined(__clang__) #define CMOV(cond, dst, src) __asm__( \ "test %[c], %[c]\n" \ "cmovnz %[s], %[d]\n" \ : [d]"+r"(dst): [c]"r"(cond), [s]"r"(src)) #else //#define CMOV(c,d,s) (d) = ((c) & (s)) | (~(uintptr_t)(c) & (d)); #define CMOV(c, d, s) if(c) (d) = (s) #endif static HEDLEY_ALWAYS_INLINE uint_fast16_t xor_jit_bitpair3(uint8_t* dest, uint_fast32_t mask, const __m128i* tCode, const uint16_t* tInfo, intptr_t* posC, long* movC, uint_fast8_t isR64) { uint_fast16_t info = tInfo[mask>>1]; intptr_t pC = info >> 12; // copy code segment STOREU_XMM(dest, _mm_load_si128((__m128i*)((const uint64_t*)tCode + mask))); // handle conditional move for common mask (since it's always done) CMOV(*movC, *posC, pC+isR64); *posC -= info & 0xF; *movC &= -(long)(pC == 0); return info; } static HEDLEY_ALWAYS_INLINE uint_fast16_t xor_jit_bitpair3_noxor(uint8_t* dest, uint_fast16_t info, intptr_t* pos1, long* mov1, intptr_t* pos2, long* mov2, int isR64) { UNUSED(dest); uintptr_t p1 = (info >> 4) & 0xF; uintptr_t p2 = (info >> 8) & 0xF; CMOV(*mov1, *pos1, p1+isR64); CMOV(*mov2, *pos2, p2+isR64); *pos1 -= info & 0xF; *pos2 -= info & 0xF; *mov1 &= -(long)(p1 == 0); *mov2 &= -(long)(p2 == 0); return info & 0xF; } static HEDLEY_ALWAYS_INLINE uint_fast16_t xor_jit_bitpair3_nc_noxor(uint8_t* dest, uint_fast16_t info, intptr_t* pos1, long* mov1, intptr_t* pos2, long* mov2, int isR64) { UNUSED(dest); uintptr_t p1 = (info >> 8) & 0xF; uintptr_t p2 = info >> 12; CMOV(*mov1, *pos1, p1+isR64); CMOV(*mov2, *pos2, p2+isR64); *pos1 -= info & 0xF; *pos2 -= info & 0xF; *mov1 &= -(long)(p1 == 0); *mov2 &= -(long)(p2 == 0); return info & 0xF; } #undef CMOV static inline void* xor_write_jit_sse(const void *HEDLEY_RESTRICT _scratch, uint8_t *HEDLEY_RESTRICT jitptr, uint16_t val, const int mode, const int prefetch) { const struct gf16_xor_jit_scratch_sse2 *HEDLEY_RESTRICT scratch = (const struct gf16_xor_jit_scratch_sse2*)_scratch; uint_fast32_t bit; ALIGN_TO(16, uint32_t lumask[8]); __m128i depmask1 = _mm_load_si128((__m128i*)(scratch->s.deps + ((val & 0xf) << 7))); __m128i depmask2 = _mm_load_si128((__m128i*)(scratch->s.deps + ((val & 0xf) << 7)) +1); depmask1 = _mm_xor_si128(depmask1, _mm_load_si128((__m128i*)(scratch->s.deps + ((val << 3) & 0x780)) + 1*2)); depmask2 = _mm_xor_si128(depmask2, _mm_load_si128((__m128i*)(scratch->s.deps + ((val << 3) & 0x780)) + 1*2 +1)); depmask1 = _mm_xor_si128(depmask1, _mm_load_si128((__m128i*)(scratch->s.deps + ((val >> 1) & 0x780)) + 2*2)); depmask2 = _mm_xor_si128(depmask2, _mm_load_si128((__m128i*)(scratch->s.deps + ((val >> 1) & 0x780)) + 2*2 +1)); depmask1 = _mm_xor_si128(depmask1, _mm_load_si128((__m128i*)(scratch->s.deps + ((val >> 5) & 0x780)) + 3*2)); depmask2 = _mm_xor_si128(depmask2, _mm_load_si128((__m128i*)(scratch->s.deps + ((val >> 5) & 0x780)) + 3*2 +1)); _mm_store_si128((__m128i*)(lumask), depmask1); _mm_store_si128((__m128i*)(lumask + 4), depmask2); #ifndef XORDEP_DISABLE_NO_COMMON /* find cases where we don't wish to create the common queue - this is an optimisation to remove a single move operation when the common queue only contains one element */ /* we have the common elements between pairs, but it doesn't make sense to process a separate queue if there's only one common element (0 XORs), so find those */ __m128i common_mask1 = _mm_and_si128(depmask1, _mm_add_epi32(depmask1, depmask1)); common_mask1 = _mm_and_si128(common_mask1, _mm_set1_epi8(0xaa)); __m128i common_mask2 = _mm_and_si128(depmask2, _mm_add_epi32(depmask2, depmask2)); common_mask2 = _mm_and_si128(common_mask2, _mm_set1_epi8(0xaa)); __m128i common_mask_packed = _mm_packs_epi32(common_mask1, common_mask2); /* "(v & (v-1)) == 0" is true if only zero/one bit is set in each word */ common_mask1 = _mm_and_si128(common_mask1, _mm_add_epi32(common_mask1, _mm_set1_epi32(-1))); common_mask2 = _mm_and_si128(common_mask2, _mm_add_epi32(common_mask2, _mm_set1_epi32(-1))); __m128i common_mask = _mm_andnot_si128( _mm_cmpeq_epi16(_mm_setzero_si128(), common_mask_packed), _mm_cmpeq_epi16(_mm_setzero_si128(), _mm_packs_epi32(common_mask1, common_mask2)) ); /* now we have a 8x16 mask of one-bit common masks we wish to remove; pack into an int for easy dealing with */ // TODO: if this is in a memory source, consider allowing the common mask int no_common_mask = _mm_movemask_epi8(common_mask); #else #define no_common_mask 0 #endif if(prefetch) { jitptr += _jit_add_i(jitptr, SI, 128); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, 0); jitptr += _jit_prefetch_m(jitptr, prefetch, SI, 64); } //_jit_movaps_load(jit, reg, xreg, offs) // (we just save a conditional by hardcoding this) #define _LD_APS(xreg, mreg, offs) \ write32((jitptr), 0x40280F + ((xreg) <<19) + ((mreg) <<16) + ((uint32_t)((offs)&0xFF) <<24)); \ jitptr += 4 #define _ST_APS(mreg, offs, xreg) \ write32((jitptr), 0x40290F + ((xreg) <<19) + ((mreg) <<16) + ((uint32_t)((offs)&0xFF) <<24)); \ jitptr += 4 #define _LD_APS64(xreg, mreg, offs) \ write64((jitptr), 0x40280F44 + ((xreg-8) <<27) + ((mreg) <<24) + ((int64_t)((offs)&0xFF) <<32)); \ jitptr += 5 #define _ST_APS64(mreg, offs, xreg) \ write64((jitptr), 0x40290F44 + ((xreg-8) <<27) + ((mreg) <<24) + ((int64_t)((offs)&0xFF) <<32)); \ jitptr += 5 #ifdef PLATFORM_AMD64 #define _LD_DQA(xreg, mreg, offs) \ write64((jitptr), 0x406F0F66 + ((xreg) <<27) + ((mreg) <<24) + ((int64_t)((offs)&0xFF) <<32)); \ jitptr += 5 #else #define _LD_DQA(xreg, mreg, offs) \ write32((jitptr), 0x406F0F66 + ((xreg) <<27) + ((mreg) <<24)); \ *(jitptr +4) = (uint8_t)((offs)&0xFF); \ jitptr += 5 #endif #define _LD_DQA64(xreg, mreg, offs) \ write64((jitptr), 0x406F0F4466 + ((int64_t)(xreg-8) <<35) + ((int64_t)(mreg) <<32) + ((int64_t)((offs)&0xFF) <<40)); \ jitptr += 6 //_jit_xorps_m(jit, reg, AX, lshift32(offs, 4)); #define _XORPS_M_(reg, offs, tr) \ write32((jitptr), (0x40570F + ((reg) << 19) + (((offs)&0xFF) <<28)) ^ (tr)) #define _C_XORPS_M(reg, offs, c) \ _XORPS_M_(reg, offs, 0); \ jitptr += (c)<<2 #define _XORPS_M64_(reg, offs, tr) \ write64((jitptr), (0x40570F44 + (((reg)-8) << 27) + ((int64_t)((offs)&0xFF) <<36)) ^ ((tr)<<8)) #define _C_XORPS_M64(reg, offs, c) \ _XORPS_M64_(reg, offs, 0); \ jitptr += ((c)<<2)+(c) //_jit_pxor_m(jit, 1, AX, lshift32(offs, 4)); #ifdef PLATFORM_AMD64 #define _PXOR_M_(reg, offs, tr) \ write64((jitptr), (0x40EF0F66 + ((reg) << 27) + ((int64_t)((offs)&0xFF) << 36)) ^ (tr)) #else #define _PXOR_M_(reg, offs, tr) \ write32((jitptr), (0x40EF0F66 + ((reg) << 27)) ^ (tr)); \ *(jitptr +4) = (uint8_t)(((offs)&0xFF) << 4) #endif #define _PXOR_M(reg, offs) \ _PXOR_M_(reg, offs, 0); \ jitptr += 5 #define _C_PXOR_M(reg, offs, c) \ _PXOR_M_(reg, offs, 0); \ jitptr += ((c)<<2)+(c) #define _PXOR_M64_(reg, offs, tr) \ write64((jitptr), (0x40EF0F4466 + ((int64_t)((reg)-8) << 35) + ((int64_t)((offs)&0xFF) << 44)) ^ ((tr)<<8)) #define _C_PXOR_M64(reg, offs, c) \ _PXOR_M64_(reg, offs, 0); \ jitptr += ((c)<<2)+((c)<<1) //_jit_xorps_r(jit, r2, r1) #define _XORPS_R_(r2, r1, tr) \ write32((jitptr), (0xC0570F + ((r2) <<19) + ((r1) <<16)) ^ (tr)) #define _XORPS_R(r2, r1) \ _XORPS_R_(r2, r1, 0); \ jitptr += 3 #define _C_XORPS_R(r2, r1, c) \ _XORPS_R_(r2, r1, 0); \ jitptr += ((c)<<1)+(c) // r2 is always < 8, r1 here is >= 8 #define _XORPS_R64_(r2, r1, tr) \ write32((jitptr), (0xC0570F41 + ((r2) <<27) + ((r1) <<24)) ^ ((tr)<<8)) #define _C_XORPS_R64(r2, r1, c) \ _XORPS_R64_(r2, r1, 0); \ jitptr += (c)<<2 //_jit_pxor_r(jit, r2, r1) #define _PXOR_R_(r2, r1, tr) \ write32((jitptr), (0xC0EF0F66 + ((r2) <<27) + ((r1) <<24)) ^ (tr)) #define _PXOR_R(r2, r1) \ _PXOR_R_(r2, r1, 0); \ jitptr += 4 #define _C_PXOR_R(r2, r1, c) \ _PXOR_R_(r2, r1, 0); \ jitptr += (c)<<2 #define _PXOR_R64_(r2, r1, tr) \ write64((jitptr), (0xC0EF0F4166 + ((int64_t)(r2) <<35) + ((int64_t)(r1) <<32)) ^ (((int64_t)tr)<<8)) #define _C_PXOR_R64(r2, r1, c) \ _PXOR_R64_(r2, r1, 0); \ jitptr += ((c)<<2)+(c) /* optimised mix of xor/mov operations */ #define _MOV_OR_XOR_FP_M(reg, offs, flag, c) \ _XORPS_M_(reg, offs, flag); \ flag &= (c)-1; \ jitptr += (c)<<2 #define _MOV_OR_XOR_FP_M64(reg, offs, flag, c) \ _XORPS_M64_(reg, offs, flag); \ flag &= (c)-1; \ jitptr += ((c)<<2)+(c) #define _MOV_OR_XOR_FP_INIT (0x570F ^ 0x280F) #define _MOV_OR_XOR_INT_M(reg, offs, flag, c) \ _PXOR_M_(reg, offs, flag); \ flag &= (c)-1; \ jitptr += ((c)<<2)+(c) #define _MOV_OR_XOR_INT_M64(reg, offs, flag, c) \ _PXOR_M64_(reg, offs, flag); \ flag &= (c)-1; \ jitptr += ((c)<<2)+((c)<<1) #define _MOV_OR_XOR_INT_INIT (0xEF0F00 ^ 0x6F0F00) #define _MOV_OR_XOR_R_FP(r2, r1, flag, c) \ _XORPS_R_(r2, r1, flag); \ flag &= (c)-1; \ jitptr += ((c)<<1)+(c) #define _MOV_OR_XOR_R64_FP(r2, r1, flag, c) \ _XORPS_R64_(r2, r1, flag); \ flag &= (c)-1; \ jitptr += (c)<<2 #define _MOV_OR_XOR_R_INT(r2, r1, flag, c) \ _PXOR_R_(r2, r1, flag); \ flag &= (c)-1; \ jitptr += (c)<<2 #define _MOV_OR_XOR_R64_INT(r2, r1, flag, c) \ _PXOR_R64_(r2, r1, flag); \ flag &= (c)-1; \ jitptr += ((c)<<2)+(c) /* generate code */ if(mode == XORDEP_JIT_MODE_MULADD) { for(bit=0; bit<8; bit++) { int destOffs = (bit<<5)-128; int destOffs2 = destOffs+16; long movC = 0xFF; intptr_t posC = 0; uint_fast32_t mask = lumask[bit]; _LD_APS(0, DX, destOffs); _LD_DQA(1, DX, destOffs2); if(no_common_mask & 1) { #define PROC_BITPAIR(n, inf, m) \ STOREU_XMM(jitptr, _mm_load_si128((__m128i*)((uint64_t*)scratch->clut_nocomm + (n<<5) + ((m) & (0xF<<1))))); \ jitptr += ((uint8_t*)(scratch->clut_ncinfo_ ##inf))[(m) & (0xF<<1)]; \ mask >>= 4 PROC_BITPAIR(0, mem, mask<<1); mask <<= 1; #ifdef PLATFORM_AMD64 PROC_BITPAIR(1, rm, mask); PROC_BITPAIR(2, reg, mask); PROC_BITPAIR(3, reg, mask); PROC_BITPAIR(4, mem, mask); PROC_BITPAIR(5, mem, mask); PROC_BITPAIR(6, mem, mask); PROC_BITPAIR(7, mem, mask); #else PROC_BITPAIR(1, mem, mask); PROC_BITPAIR(2, mem, mask); PROC_BITPAIR(3, mem, mask); PROC_BITPAIR(4, mem, mask); PROC_BITPAIR(5, rm, mask); PROC_BITPAIR(6, reg, mask); PROC_BITPAIR(7, reg, mask); #endif #undef PROC_BITPAIR } else { #define PROC_BITPAIR(n, bits, inf, m, r64) \ jitptr += xor_jit_bitpair3(jitptr, (m) & ((2<clut_code ##n, scratch->clut_info_ ##inf, &posC, &movC, r64) & 0xF; \ mask >>= bits PROC_BITPAIR(1, 6, mem, mask<<1, 0); mask <<= 1; #ifdef PLATFORM_AMD64 PROC_BITPAIR(2, 6, reg, mask, 0); PROC_BITPAIR(3, 4, reg, mask, 0); PROC_BITPAIR(4, 6, mem, mask, 1); PROC_BITPAIR(5, 6, mem, mask, 1); PROC_BITPAIR(6, 4, mem, mask, 1); #else PROC_BITPAIR(2, 6, mem, mask, 0); PROC_BITPAIR(3, 6, mem, mask, 0); PROC_BITPAIR(4, 4, mem, mask, 0); PROC_BITPAIR(5, 6, reg, mask, 0); PROC_BITPAIR(6, 4, reg, mask, 0); #endif #undef PROC_BITPAIR jitptr[posC + movC] = 0x6F; // PXOR -> MOVDQA #ifdef PLATFORM_AMD64 write64(jitptr, (0xC0570F + (2 <<16)) + ((0xC0EF0F66ULL + (1 <<27) + (2 <<24)) <<24)); jitptr += ((movC==0)<<3) - (movC==0); #else _C_XORPS_R(0, 2, movC==0); _C_PXOR_R(1, 2, movC==0); /*penalty?*/ #endif } #ifndef XORDEP_DISABLE_NO_COMMON no_common_mask >>= 2; #endif _ST_APS(DX, destOffs, 0); _ST_APS(DX, destOffs2, 1); } } else { for(bit=0; bit<8; bit++) { int destOffs = (bit<<5)-128; int destOffs2 = destOffs+16; long mov1 = 0xFF, mov2 = 0xFF, movC = 0xFF; intptr_t pos1 = 0, pos2 = 0, posC = 0; uint_fast32_t mask = lumask[bit]; if(no_common_mask & 1) { #define PROC_BITPAIR(n, inf, m, r64) \ STOREU_XMM(jitptr, _mm_load_si128((__m128i*)((uint64_t*)scratch->clut_nocomm + (n<<5) + ((m) & (0xF<<1))))); \ jitptr += xor_jit_bitpair3_nc_noxor(jitptr, scratch->clut_ncinfo_ ##inf[((m) & (0xF<<1))>>1], &pos1, &mov1, &pos2, &mov2, r64); \ mask >>= 4 PROC_BITPAIR(0, mem, mask<<1, 0); mask <<= 1; #ifdef PLATFORM_AMD64 PROC_BITPAIR(1, rm, mask, 0); PROC_BITPAIR(2, reg, mask, 0); PROC_BITPAIR(3, reg, mask, 0); PROC_BITPAIR(4, mem, mask, 1); PROC_BITPAIR(5, mem, mask, 1); PROC_BITPAIR(6, mem, mask, 1); PROC_BITPAIR(7, mem, mask, 1); #else PROC_BITPAIR(1, mem, mask, 0); PROC_BITPAIR(2, mem, mask, 0); PROC_BITPAIR(3, mem, mask, 0); PROC_BITPAIR(4, mem, mask, 0); PROC_BITPAIR(5, rm, mask, 0); PROC_BITPAIR(6, reg, mask, 0); PROC_BITPAIR(7, reg, mask, 0); #endif #undef PROC_BITPAIR jitptr[pos1 + mov1] = 0x28; // XORPS -> MOVAPS jitptr[pos2 + mov2] = 0x6F; // PXOR -> MOVDQA } else { #define PROC_BITPAIR(n, bits, inf, m, r64) \ jitptr += xor_jit_bitpair3_noxor(jitptr, xor_jit_bitpair3(jitptr, (m) & ((2<clut_code ##n, scratch->clut_info_ ##inf, &posC, &movC, r64), &pos1, &mov1, &pos2, &mov2, r64); \ mask >>= bits PROC_BITPAIR(1, 6, mem, mask<<1, 0); mask <<= 1; #ifdef PLATFORM_AMD64 PROC_BITPAIR(2, 6, reg, mask, 0); PROC_BITPAIR(3, 4, reg, mask, 0); PROC_BITPAIR(4, 6, mem, mask, 1); PROC_BITPAIR(5, 6, mem, mask, 1); PROC_BITPAIR(6, 4, mem, mask, 1); #else PROC_BITPAIR(2, 6, mem, mask, 0); PROC_BITPAIR(3, 6, mem, mask, 0); PROC_BITPAIR(4, 4, mem, mask, 0); PROC_BITPAIR(5, 6, reg, mask, 0); PROC_BITPAIR(6, 4, reg, mask, 0); #endif #undef PROC_BITPAIR jitptr[pos1 + mov1] = 0x28; // XORPS -> MOVAPS jitptr[pos2 + mov2] = 0x6F; // PXOR -> MOVDQA if(!movC) { jitptr[posC] = 0x6F; // PXOR -> MOVDQA if(mov1) { /* no additional XORs were made? */ _ST_APS(DX, destOffs, 2); } else { _XORPS_R(0, 2); } if(mov2) { _ST_APS(DX, destOffs2, 2); } else { _PXOR_R(1, 2); /*penalty?*/ } } } #ifndef XORDEP_DISABLE_NO_COMMON no_common_mask >>= 2; #else #undef no_common_mask #endif if(!mov1) { _ST_APS(DX, destOffs, 0); } if(!mov2) { _ST_APS(DX, destOffs2, 1); } } } /* cmp */ #ifdef PLATFORM_AMD64 write64(jitptr, 0x800FC03948 | (DX <<16) | (CX <<19) | ((uint64_t)JL <<32)); jitptr += 5; #else write32(jitptr, 0x800FC039 | (DX <<8) | (CX <<11) | (JL <<24)); jitptr += 4; #endif return jitptr; } static HEDLEY_ALWAYS_INLINE void gf16_xor_jit_mul_sse2_base(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const int mode, const int doPrefetch, const void *HEDLEY_RESTRICT prefetch) { jit_wx_pair* jit = (jit_wx_pair*)mutScratch; gf16_xorjit_write_jit(&(((struct gf16_xor_jit_scratch_sse2*)scratch)->s), coefficient, jit, mode, doPrefetch, &xor_write_jit_sse, scratch); // exec /* adding 128 to the destination pointer allows the register offset to be coded in 1 byte * eg: 'movdqa xmm0, [rdx+0x90]' is 8 bytes, whilst 'movdqa xmm0, [rdx-0x60]' is 5 bytes */ if(mode == XORDEP_JIT_MODE_MUL_INSITU) { // need a place to store a copy of the source, that won't fit in registers; these will be used as the memory source #ifdef PLATFORM_AMD64 ALIGN_TO(16, __m128i spill[3]); #else ALIGN_TO(16, __m128i spill[11]); #endif gf16_xor_jit_stub( (intptr_t)spill + 128, (intptr_t)dst + len - 128, (intptr_t)dst - 128, (intptr_t)prefetch - 128, (uint8_t*)jit->x + XORDEP_JIT_SIZE/2 ); } else { gf16_xor_jit_stub( (intptr_t)src - 128, (intptr_t)dst + len - 128, (intptr_t)dst - 128, (intptr_t)prefetch - 128, jit->x ); } } #endif /* defined(__SSE2__) */ #ifdef PARPAR_INVERT_SUPPORT void gf16_xor_jit_mul_sse2(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { #ifdef __SSE2__ if(coefficient == 0) { memset(dst, 0, len); return; } gf16_xor_jit_mul_sse2_base(scratch, dst, src, len, coefficient, mutScratch, dst==src ? XORDEP_JIT_MODE_MUL_INSITU : XORDEP_JIT_MODE_MUL, 0, NULL); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); UNUSED(mutScratch); #endif } #endif void gf16_xor_jit_muladd_sse2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) { #ifdef __SSE2__ if(coefficient == 0) return; gf16_xor_jit_mul_sse2_base(scratch, dst, src, len, coefficient, mutScratch, XORDEP_JIT_MODE_MULADD, 0, NULL); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); UNUSED(mutScratch); #endif } void gf16_xor_jit_muladd_prefetch_sse2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch) { #ifdef __SSE2__ if(coefficient == 0) return; gf16_xor_jit_mul_sse2_base(scratch, dst, src, len, coefficient, mutScratch, XORDEP_JIT_MODE_MULADD, _MM_HINT_T1, prefetch); #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(coefficient); UNUSED(mutScratch); UNUSED(prefetch); #endif } #ifdef __SSE2__ struct gf16_xor_tables { ALIGN_TO(16, __m128 deps[8*16]); uint64_t BitsIndexTable[256]; }; static const unsigned char BitsSetTable256[256] = { # define B2(n) n, n+1, n+1, n+2 # define B4(n) B2(n), B2(n+1), B2(n+1), B2(n+2) # define B6(n) B4(n), B4(n+1), B4(n+1), B4(n+2) B6(0), B6(1), B6(1), B6(2) }; static HEDLEY_ALWAYS_INLINE void gf16_xor_write_deptable(intptr_t *HEDLEY_RESTRICT deptable, uint_fast32_t *HEDLEY_RESTRICT counts, const struct gf16_xor_tables *HEDLEY_RESTRICT scratch, uint16_t val, uintptr_t dstSrcOffset) { ALIGN_TO(16, uint16_t tmp_depmask[16]); const uint8_t* deps = (const uint8_t*)(scratch->deps); __m128i depmask1 = _mm_load_si128((__m128i*)(deps + ((val & 0xf) << 7))); __m128i depmask2 = _mm_load_si128((__m128i*)(deps + ((val & 0xf) << 7)) +1); depmask1 = _mm_xor_si128(depmask1, _mm_load_si128((__m128i*)(deps + ((val << 3) & 0x780)) + 1*2)); depmask2 = _mm_xor_si128(depmask2, _mm_load_si128((__m128i*)(deps + ((val << 3) & 0x780)) + 1*2 +1)); depmask1 = _mm_xor_si128(depmask1, _mm_load_si128((__m128i*)(deps + ((val >> 1) & 0x780)) + 2*2)); depmask2 = _mm_xor_si128(depmask2, _mm_load_si128((__m128i*)(deps + ((val >> 1) & 0x780)) + 2*2 +1)); depmask1 = _mm_xor_si128(depmask1, _mm_load_si128((__m128i*)(deps + ((val >> 5) & 0x780)) + 3*2)); depmask2 = _mm_xor_si128(depmask2, _mm_load_si128((__m128i*)(deps + ((val >> 5) & 0x780)) + 3*2 +1)); /* generate needed tables */ _mm_store_si128((__m128i*)(tmp_depmask), depmask1); _mm_store_si128((__m128i*)(tmp_depmask + 8), depmask2); for(int bit=0; bit<16; bit++) { uint_fast32_t cnt = BitsSetTable256[tmp_depmask[bit] & 0xff]; __m128i idx = _mm_loadl_epi64((__m128i*)(scratch->BitsIndexTable + (tmp_depmask[bit] & 0xff))); __m128i idx2 = _mm_or_si128( _mm_set1_epi8(8U<<4), _mm_loadl_epi64((__m128i*)(scratch->BitsIndexTable + (tmp_depmask[bit] >> 8))) ); counts[bit] = cnt + BitsSetTable256[tmp_depmask[bit] >> 8] -1; if(sizeof(uintptr_t) == 8) { __m128i addr = _mm_set1_epi64x(dstSrcOffset); #define STORE_TBL(storetype, offs, data) \ _mm_##storetype##_si128((__m128i*)(deptable + bit*16 +offs), _mm_add_epi64( \ data, addr \ )) idx = _mm_unpacklo_epi8(idx, _mm_setzero_si128()); __m128i tmp1 = _mm_unpacklo_epi16(idx, _mm_setzero_si128()); __m128i tmp2 = _mm_unpackhi_epi16(idx, _mm_setzero_si128()); STORE_TBL(store, 0, _mm_unpacklo_epi32(tmp1, _mm_setzero_si128())); STORE_TBL(store, 2, _mm_unpackhi_epi32(tmp1, _mm_setzero_si128())); STORE_TBL(store, 4, _mm_unpacklo_epi32(tmp2, _mm_setzero_si128())); STORE_TBL(store, 6, _mm_unpackhi_epi32(tmp2, _mm_setzero_si128())); idx2 = _mm_unpacklo_epi8(idx2, _mm_setzero_si128()); tmp1 = _mm_unpacklo_epi16(idx2, _mm_setzero_si128()); tmp2 = _mm_unpackhi_epi16(idx2, _mm_setzero_si128()); STORE_TBL(storeu, cnt+0, _mm_unpacklo_epi32(tmp1, _mm_setzero_si128())); STORE_TBL(storeu, cnt+2, _mm_unpackhi_epi32(tmp1, _mm_setzero_si128())); STORE_TBL(storeu, cnt+4, _mm_unpacklo_epi32(tmp2, _mm_setzero_si128())); STORE_TBL(storeu, cnt+6, _mm_unpackhi_epi32(tmp2, _mm_setzero_si128())); #undef STORE_TBL } else { // 32-bit __m128i addr = _mm_set1_epi32((int)dstSrcOffset); idx = _mm_unpacklo_epi8(idx, _mm_setzero_si128()); _mm_store_si128((__m128i*)(deptable + bit*16 +0), _mm_add_epi32( _mm_unpacklo_epi16(idx, _mm_setzero_si128()), addr )); _mm_store_si128((__m128i*)(deptable + bit*16 +4), _mm_add_epi32( _mm_unpackhi_epi16(idx, _mm_setzero_si128()), addr )); idx2 = _mm_unpacklo_epi8(idx2, _mm_setzero_si128()); _mm_storeu_si128((__m128i*)(deptable + bit*16 +cnt+0), _mm_add_epi32( _mm_unpacklo_epi16(idx2, _mm_setzero_si128()), addr )); _mm_storeu_si128((__m128i*)(deptable + bit*16 +cnt+4), _mm_add_epi32( _mm_unpackhi_epi16(idx2, _mm_setzero_si128()), addr )); } /* for storing as byte indicies; we do the full expansion above because it saves a register in the main loop _mm_storel_epi64( (__m128i*)(deptable + bit*16), _mm_loadl_epi64((__m128i*)(scratch->BitsIndexTable + (tmp_depmask[bit] & 0xff))) ); _mm_storel_epi64( (__m128i*)(deptable + bit*16 + cnt), _mm_or_si128( _mm_set1_epi8(8<<4), _mm_loadl_epi64((__m128i*)(scratch->BitsIndexTable + (tmp_depmask[bit] >> 8))) ) ); */ } } static HEDLEY_ALWAYS_INLINE void gf16_xor_mul_block_sse2(const uint8_t* inP, uint8_t* outP, uint_fast32_t counts[16], intptr_t deptable[256]) { /* Note that we assume that all counts are at least 1; I don't think it's possible for that to be false */ #define STEP(bit, type, typev, typed) { \ intptr_t* deps = deptable + bit*16; \ typev tmp = _mm_load_ ## type((typed*)(inP + deps[ 0])); \ HEDLEY_ASSUME(counts[bit] <= 15); \ switch(counts[bit]) { \ case 15: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[15])); /* FALLTHRU */ \ case 14: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[14])); /* FALLTHRU */ \ case 13: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[13])); /* FALLTHRU */ \ case 12: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[12])); /* FALLTHRU */ \ case 11: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[11])); /* FALLTHRU */ \ case 10: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[10])); /* FALLTHRU */ \ case 9: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[ 9])); /* FALLTHRU */ \ case 8: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[ 8])); /* FALLTHRU */ \ case 7: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[ 7])); /* FALLTHRU */ \ case 6: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[ 6])); /* FALLTHRU */ \ case 5: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[ 5])); /* FALLTHRU */ \ case 4: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[ 4])); /* FALLTHRU */ \ case 3: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[ 3])); /* FALLTHRU */ \ case 2: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[ 2])); /* FALLTHRU */ \ case 1: tmp = _mm_xor_ ## type(tmp, *(typev*)(inP + deps[ 1])); /* FALLTHRU */ \ } \ _mm_store_ ## type((typed*)outP + bit, tmp); \ } STEP( 0, si128, __m128i, __m128i) STEP( 1, si128, __m128i, __m128i) STEP( 2, si128, __m128i, __m128i) STEP( 3, si128, __m128i, __m128i) STEP( 4, si128, __m128i, __m128i) STEP( 5, si128, __m128i, __m128i) STEP( 6, si128, __m128i, __m128i) STEP( 7, si128, __m128i, __m128i) STEP( 8, si128, __m128i, __m128i) STEP( 9, si128, __m128i, __m128i) STEP(10, si128, __m128i, __m128i) STEP(11, si128, __m128i, __m128i) STEP(12, si128, __m128i, __m128i) STEP(13, si128, __m128i, __m128i) STEP(14, si128, __m128i, __m128i) STEP(15, si128, __m128i, __m128i) #undef STEP } #endif #ifdef PARPAR_INVERT_SUPPORT void gf16_xor_mul_sse2(const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #ifdef __SSE2__ if(val == 0) { memset(dst, 0, len); return; } uint_fast32_t counts[16]; ALIGN_TO(16, intptr_t deptable[256]); uint8_t* _dst = (uint8_t*)dst + len; if(dst == src) { // for in-situ mul, write to a temp block and copy back ALIGN_TO(16, uint8_t tmp[256]); __m128i* _tmp = (__m128i*)tmp; gf16_xor_write_deptable(deptable, counts, (const struct gf16_xor_tables*)scratch, val, 0); for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m128i)*16) { uint8_t* p = _dst + ptr; gf16_xor_mul_block_sse2(p, tmp, counts, deptable); for(int i=0; i<16; i++) { _mm_store_si128((__m128i*)p + i, _mm_load_si128(_tmp+i)); } } } else { gf16_xor_write_deptable(deptable, counts, (const struct gf16_xor_tables*)scratch, val, (uintptr_t)src - (uintptr_t)dst); for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m128i)*16) { gf16_xor_mul_block_sse2(_dst + ptr, _dst + ptr, counts, deptable); } } #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #endif void gf16_xor_muladd_sse2(const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t val, void *HEDLEY_RESTRICT mutScratch) { UNUSED(mutScratch); #ifdef __SSE2__ if(val == 0) return; uint_fast32_t counts[16]; ALIGN_TO(16, intptr_t deptable[256]); uint8_t* _dst = (uint8_t*)dst + len; gf16_xor_write_deptable(deptable, counts, (const struct gf16_xor_tables*)scratch, val, (uintptr_t)src - (uintptr_t)dst); for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(__m128i)*16) { uint8_t* p = _dst + ptr; #define STEP(bit, type, typev, typed) { \ intptr_t* deps = deptable + bit*16; \ typev tmp = _mm_load_ ## type((typed*)((typed*)p + bit)); \ HEDLEY_ASSUME(counts[bit] <= 15); \ switch(counts[bit]) { \ case 15: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[15])); /* FALLTHRU */ \ case 14: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[14])); /* FALLTHRU */ \ case 13: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[13])); /* FALLTHRU */ \ case 12: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[12])); /* FALLTHRU */ \ case 11: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[11])); /* FALLTHRU */ \ case 10: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[10])); /* FALLTHRU */ \ case 9: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[ 9])); /* FALLTHRU */ \ case 8: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[ 8])); /* FALLTHRU */ \ case 7: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[ 7])); /* FALLTHRU */ \ case 6: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[ 6])); /* FALLTHRU */ \ case 5: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[ 5])); /* FALLTHRU */ \ case 4: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[ 4])); /* FALLTHRU */ \ case 3: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[ 3])); /* FALLTHRU */ \ case 2: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[ 2])); /* FALLTHRU */ \ case 1: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[ 1])); /* FALLTHRU */ \ case 0: tmp = _mm_xor_ ## type(tmp, *(typev*)(p + deps[ 0])); /* FALLTHRU */ \ } \ _mm_store_ ## type((typed*)p + bit, tmp); \ } STEP( 0, si128, __m128i, __m128i) STEP( 1, si128, __m128i, __m128i) STEP( 2, si128, __m128i, __m128i) STEP( 3, si128, __m128i, __m128i) STEP( 4, si128, __m128i, __m128i) STEP( 5, si128, __m128i, __m128i) STEP( 6, si128, __m128i, __m128i) STEP( 7, si128, __m128i, __m128i) STEP( 8, si128, __m128i, __m128i) STEP( 9, si128, __m128i, __m128i) STEP(10, si128, __m128i, __m128i) STEP(11, si128, __m128i, __m128i) STEP(12, si128, __m128i, __m128i) STEP(13, si128, __m128i, __m128i) STEP(14, si128, __m128i, __m128i) STEP(15, si128, __m128i, __m128i) #undef STEP } #else UNUSED(scratch); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(val); #endif } #ifdef __SSE2__ #define LOAD_HALVES(a, b, upper) \ _mm_castps_si128(_mm_loadh_pi( \ _mm_castsi128_ps(_mm_loadl_epi64((__m128i*)(_src + 120 + upper*4 - (a)*8))), \ (__m64*)(_src + 120 + upper*4 - (b)*8) \ )) #define LOAD_X4(offs, dst1, dst2, upper) { \ __m128i src02 = LOAD_HALVES(offs+0, offs+2, upper); /* 22222222 00000000 */ \ __m128i src13 = LOAD_HALVES(offs+1, offs+3, upper); /* 33333333 11111111 */ \ __m128i src01 = _mm_unpacklo_epi8(src02, src13); /* 10101010 10101010 */ \ __m128i src23 = _mm_unpackhi_epi8(src02, src13); /* 32323232 32323232 */ \ dst1 = _mm_unpacklo_epi16(src01, src23); /* 32103210 32103210 */ \ dst2 = _mm_unpackhi_epi16(src01, src23); /* 32103210 32103210 */ \ } #define UNPACK_VECTS \ srcQ0a = _mm_unpacklo_epi32(srcD0a, srcD4a); /* 76543210 76543210 */ \ srcQ0b = _mm_unpackhi_epi32(srcD0a, srcD4a); \ srcQ0c = _mm_unpacklo_epi32(srcD0b, srcD4b); \ srcQ0d = _mm_unpackhi_epi32(srcD0b, srcD4b); \ srcQ8a = _mm_unpacklo_epi32(srcD8a, srcD12a); \ srcQ8b = _mm_unpackhi_epi32(srcD8a, srcD12a); \ srcQ8c = _mm_unpacklo_epi32(srcD8b, srcD12b); \ srcQ8d = _mm_unpackhi_epi32(srcD8b, srcD12b); \ \ srcDQa = _mm_unpacklo_epi64(srcQ0a, srcQ8a); \ srcDQb = _mm_unpackhi_epi64(srcQ0a, srcQ8a); \ srcDQc = _mm_unpacklo_epi64(srcQ0b, srcQ8b); \ srcDQd = _mm_unpackhi_epi64(srcQ0b, srcQ8b); \ srcDQe = _mm_unpacklo_epi64(srcQ0c, srcQ8c); \ srcDQf = _mm_unpackhi_epi64(srcQ0c, srcQ8c); \ srcDQg = _mm_unpacklo_epi64(srcQ0d, srcQ8d); \ srcDQh = _mm_unpackhi_epi64(srcQ0d, srcQ8d) #define EXTRACT_BITS(target, srcVec) \ write16((target) + 7, _mm_movemask_epi8(srcVec)); \ for(int i=6; i>=0; i--) { \ srcVec = _mm_add_epi8(srcVec, srcVec); \ write16((target) + i, _mm_movemask_epi8(srcVec)); \ } void gf16_xor_finish_block_sse2(void *HEDLEY_RESTRICT dst) { uint16_t* _dst = (uint16_t*)dst; const uint16_t* _src = _dst; __m128i srcD0a, srcD0b, srcD4a, srcD4b, srcD8a, srcD8b, srcD12a, srcD12b; __m128i srcQ0a, srcQ0b, srcQ0c, srcQ0d, srcQ8a, srcQ8b, srcQ8c, srcQ8d; __m128i srcDQa, srcDQb, srcDQc, srcDQd, srcDQe, srcDQf, srcDQg, srcDQh; __m128i dstA, dstB, dstC, dstD; // setting to undefined to stop ICC warning about uninitialized values #ifndef __APPLE__ dstA = _mm_undefined_si128(); dstB = _mm_undefined_si128(); dstC = _mm_undefined_si128(); dstD = _mm_undefined_si128(); #endif // load 16x 64-bit inputs LOAD_X4( 0, srcD0a , srcD0b, 0) LOAD_X4( 4, srcD4a , srcD4b, 0) LOAD_X4( 8, srcD8a , srcD8b, 0) LOAD_X4(12, srcD12a, srcD12b,0) // interleave bytes in all 8 vectors UNPACK_VECTS; // write half of these vectors, and store the other half for later #define EXTRACT_BITS_HALF(target, targVec, vecUpper, srcVec) { \ uint16_t mskA, mskB, mskC, mskD; \ mskD = _mm_movemask_epi8(srcVec); \ srcVec = _mm_add_epi8(srcVec, srcVec); \ mskC = _mm_movemask_epi8(srcVec); \ srcVec = _mm_add_epi8(srcVec, srcVec); \ mskB = _mm_movemask_epi8(srcVec); \ srcVec = _mm_add_epi8(srcVec, srcVec); \ mskA = _mm_movemask_epi8(srcVec); \ targVec = vecUpper ? _mm_insert_epi16(targVec, mskA, 4) : _mm_cvtsi32_si128(mskA); \ targVec = _mm_insert_epi16(targVec, mskB, 1 + vecUpper*4); \ targVec = _mm_insert_epi16(targVec, mskC, 2 + vecUpper*4); \ targVec = _mm_insert_epi16(targVec, mskD, 3 + vecUpper*4); \ srcVec = _mm_add_epi8(srcVec, srcVec); \ write16((target)+3, _mm_movemask_epi8(srcVec)); \ srcVec = _mm_add_epi8(srcVec, srcVec); \ write16((target)+2, _mm_movemask_epi8(srcVec)); \ srcVec = _mm_add_epi8(srcVec, srcVec); \ write16((target)+1, _mm_movemask_epi8(srcVec)); \ srcVec = _mm_add_epi8(srcVec, srcVec); \ write16((target)+0, _mm_movemask_epi8(srcVec)); \ } EXTRACT_BITS_HALF(_dst + 0, dstA, 0, srcDQa) EXTRACT_BITS_HALF(_dst + 8, dstA, 1, srcDQb) EXTRACT_BITS_HALF(_dst + 16, dstB, 0, srcDQc) EXTRACT_BITS_HALF(_dst + 24, dstB, 1, srcDQd) EXTRACT_BITS_HALF(_dst + 32, dstC, 0, srcDQe) EXTRACT_BITS_HALF(_dst + 40, dstC, 1, srcDQf) EXTRACT_BITS_HALF(_dst + 48, dstD, 0, srcDQg) EXTRACT_BITS_HALF(_dst + 56, dstD, 1, srcDQh) #undef EXTRACT_BITS_HALF // load second half & write saved output once source has been read LOAD_X4(12, srcD12a, srcD12b,1) _mm_storel_epi64((__m128i*)(_dst + 4), dstA); _mm_storeh_pi((__m64*)(_dst + 12), _mm_castsi128_ps(dstA)); _mm_storel_epi64((__m128i*)(_dst + 20), dstB); _mm_storeh_pi((__m64*)(_dst + 28), _mm_castsi128_ps(dstB)); LOAD_X4( 8, srcD8a , srcD8b, 1) _mm_storel_epi64((__m128i*)(_dst + 36), dstC); _mm_storeh_pi((__m64*)(_dst + 44), _mm_castsi128_ps(dstC)); _mm_storel_epi64((__m128i*)(_dst + 52), dstD); _mm_storeh_pi((__m64*)(_dst + 60), _mm_castsi128_ps(dstD)); LOAD_X4( 4, srcD4a , srcD4b, 1) LOAD_X4( 0, srcD0a , srcD0b, 1) UNPACK_VECTS; // extract & write all bits // TODO: consider saving some to a register to reduce write ops EXTRACT_BITS(_dst + 64 + 0, srcDQa) EXTRACT_BITS(_dst + 64 + 8, srcDQb) EXTRACT_BITS(_dst + 64 + 16, srcDQc) EXTRACT_BITS(_dst + 64 + 24, srcDQd) EXTRACT_BITS(_dst + 64 + 32, srcDQe) EXTRACT_BITS(_dst + 64 + 40, srcDQf) EXTRACT_BITS(_dst + 64 + 48, srcDQg) EXTRACT_BITS(_dst + 64 + 56, srcDQh) } void gf16_xor_finish_copy_block_sse2(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src) { uint16_t* _dst = (uint16_t*)dst; const uint16_t* _src = (uint16_t*)src; __m128i srcD0a, srcD0b, srcD4a, srcD4b, srcD8a, srcD8b, srcD12a, srcD12b; __m128i srcQ0a, srcQ0b, srcQ0c, srcQ0d, srcQ8a, srcQ8b, srcQ8c, srcQ8d; __m128i srcDQa, srcDQb, srcDQc, srcDQd, srcDQe, srcDQf, srcDQg, srcDQh; // load 16x 64-bit inputs LOAD_X4( 0, srcD0a , srcD0b, 0) LOAD_X4( 4, srcD4a , srcD4b, 0) LOAD_X4( 8, srcD8a , srcD8b, 0) LOAD_X4(12, srcD12a, srcD12b,0) // interleave bytes in all 8 vectors UNPACK_VECTS; // write extracted bits EXTRACT_BITS(_dst + 0, srcDQa) EXTRACT_BITS(_dst + 8, srcDQb) EXTRACT_BITS(_dst + 16, srcDQc) EXTRACT_BITS(_dst + 24, srcDQd) EXTRACT_BITS(_dst + 32, srcDQe) EXTRACT_BITS(_dst + 40, srcDQf) EXTRACT_BITS(_dst + 48, srcDQg) EXTRACT_BITS(_dst + 56, srcDQh) // load second half LOAD_X4( 0, srcD0a , srcD0b, 1) LOAD_X4( 4, srcD4a , srcD4b, 1) LOAD_X4( 8, srcD8a , srcD8b, 1) LOAD_X4(12, srcD12a, srcD12b,1) UNPACK_VECTS; EXTRACT_BITS(_dst + 64 + 0, srcDQa) EXTRACT_BITS(_dst + 64 + 8, srcDQb) EXTRACT_BITS(_dst + 64 + 16, srcDQc) EXTRACT_BITS(_dst + 64 + 24, srcDQd) EXTRACT_BITS(_dst + 64 + 32, srcDQe) EXTRACT_BITS(_dst + 64 + 40, srcDQf) EXTRACT_BITS(_dst + 64 + 48, srcDQg) EXTRACT_BITS(_dst + 64 + 56, srcDQh) } void gf16_xor_finish_copy_blocku_sse2(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t bytes) { uint16_t block[128]; gf16_xor_finish_copy_block_sse2(block, src); memcpy(dst, block, bytes); } #undef EXTRACT_BITS #undef UNPACK_VECTS #undef LOAD_HALVES #undef LOAD_X4 #endif #define MWORD_SIZE 16 #define _mword __m128i #define _MM(f) _mm_ ## f #define _MMI(f) _mm_ ## f ## _si128 #define _FNSUFFIX _sse2 #define _MM_END #if defined(__SSE2__) # define _AVAILABLE # include "gf16_checksum_x86.h" #endif #include "gf16_xor_common_funcs.h" #undef _AVAILABLE #undef MWORD_SIZE #undef _mword #undef _MM #undef _MMI #undef _FNSUFFIX #undef _MM_END #ifdef __SSE2__ GF_FINISH_PACKED_FUNCS(gf16_xor, _sse2, sizeof(__m128i)*16, gf16_xor_finish_copy_block_sse2, gf16_xor_finish_copy_blocku_sse2, 1, (void)0, gf16_checksum_block_sse2, gf16_checksum_blocku_sse2, gf16_checksum_exp_sse2, &gf16_xor_finish_block_sse2, sizeof(__m128i)) #else GF_FINISH_PACKED_FUNCS_STUB(gf16_xor, _sse2) #endif #include "gf16_bitdep_init_sse2.h" #ifdef PLATFORM_X86 static void xor_write_init_jit(uint8_t *jitCodeNorm, uint8_t *jitCodeInsitu, uint_fast16_t* sizeNorm, uint_fast16_t* sizeInsitu) { uint8_t *jitCodeStart = jitCodeNorm; jitCodeNorm += _jit_add_i(jitCodeNorm, AX, 256); jitCodeNorm += _jit_add_i(jitCodeNorm, DX, 256); # ifdef PLATFORM_AMD64 /* preload upper 13 inputs into registers */ for(int i=3; i<16; i++) { jitCodeNorm += _jit_movaps_load(jitCodeNorm, i, AX, lshift32(i-8, 4)); } # else /* can only fit 5 in 32-bit mode :( */ for(int i=3; i<8; i++) { /* despite appearances, we're actually loading the top 5, not mid 5 */ jitCodeNorm += _jit_movaps_load(jitCodeNorm, i, AX, i<<4); } # endif if(sizeNorm) *sizeNorm = (uint_fast16_t)(jitCodeNorm-jitCodeStart); // in-situ version jitCodeStart = jitCodeInsitu; jitCodeInsitu += _jit_add_i(jitCodeInsitu, DX, 256); # ifdef PLATFORM_AMD64 for(int i=0; i<16; i++) { jitCodeInsitu += _jit_movaps_load(jitCodeInsitu, i, DX, lshift32(i-8, 4)); } for(int i=0; i<3; i++) { jitCodeInsitu += _jit_movaps_store(jitCodeInsitu, AX, lshift32(i-8, 4), i); } # else for(int i=0; i<11; i++) { jitCodeInsitu += _jit_movaps_load(jitCodeInsitu, 0, DX, lshift32(i-8, 4)); jitCodeInsitu += _jit_movaps_store(jitCodeInsitu, AX, lshift32(i-8, 4), 0); } for(int i=3; i<8; i++) { /* despite appearances, we're actually loading the top 5, not mid 5 */ jitCodeInsitu += _jit_movaps_load(jitCodeInsitu, i, DX, i<<4); } # endif if(sizeInsitu) *sizeInsitu = (uint_fast16_t)(jitCodeInsitu-jitCodeStart); } #endif void* gf16_xor_jit_init_sse2(int polynomial, int jitOptStrat) { #ifdef __SSE2__ struct gf16_xor_jit_scratch_sse2* ret; uint8_t tmpCode[XORDEP_JIT_CODE_SIZE]; ALIGN_ALLOC(ret, sizeof(struct gf16_xor_jit_scratch_sse2), 64); // align to 16 required, 64 might better use cachelines gf16_bitdep_init128(ret->s.deps, polynomial, GF16_BITDEP_INIT128_GEN_XORJIT); gf16_xor_create_jit_lut_sse2(ret); ret->s.jitOptStrat = jitOptStrat; xor_write_init_jit(tmpCode, tmpCode, &(ret->s.codeStart), &(ret->s.codeStartInsitu)); return ret; #else UNUSED(polynomial); UNUSED(jitOptStrat); return NULL; #endif } void* gf16_xor_jit_init_mut_sse2() { #ifdef PLATFORM_X86 jit_wx_pair *jitCode = jit_alloc(XORDEP_JIT_SIZE); if(!jitCode) return NULL; xor_write_init_jit(jitCode->w, (uint8_t*)jitCode->w + XORDEP_JIT_SIZE/2, NULL, NULL); return jitCode; #else return NULL; #endif } void gf16_xor_jit_uninit(void* scratch) { #ifdef PLATFORM_X86 jit_free(scratch); #else UNUSED(scratch); #endif } #ifdef PARPAR_INVERT_SUPPORT static HEDLEY_ALWAYS_INLINE uint16_t gf16_xorX_replace_word(void* data, size_t index, uint16_t newValue, size_t width) { uint8_t* base = (uint8_t*)data + (index & ~(width*8-1)) * 2; // advance pointer to correct group base += ((index >> 3) & (width-1)); // advance to correct byte unsigned bitIndex = index&7; uint16_t oldValue = 0; unsigned _newValue = newValue << bitIndex; uint8_t byteMask = 1 << bitIndex; for(int i=0; i<16; i++) { uint8_t byte = base[i*width]; oldValue <<= 1; _newValue <<= 1; oldValue |= (byte >> bitIndex) & 1; base[i*width] = (byte & ~byteMask) | ((_newValue >> 16) & byteMask); } return oldValue; } uint16_t gf16_xor16_replace_word(void* data, size_t index, uint16_t newValue) { return gf16_xorX_replace_word(data, index, newValue, 16); } uint16_t gf16_xor32_replace_word(void* data, size_t index, uint16_t newValue) { return gf16_xorX_replace_word(data, index, newValue, 32); } uint16_t gf16_xor64_replace_word(void* data, size_t index, uint16_t newValue) { return gf16_xorX_replace_word(data, index, newValue, 64); } #endif void* gf16_xor_init_sse2(int polynomial) { #ifdef __SSE2__ struct gf16_xor_tables* ret; ALIGN_ALLOC(ret, sizeof(struct gf16_xor_tables), 16); gf16_bitdep_init128(ret->deps, polynomial, GF16_BITDEP_INIT128_GEN_XOR); for(int i=0; i<256; i++) { uint64_t n=0; int j=8; while(j--) if(i & (1<BitsIndexTable[i] = n; } return ret; #else UNUSED(polynomial); return NULL; #endif } par2cmdline-turbo-1.4.0/parpar/gf16/gf16mul.cpp000066400000000000000000002164741514221355600211040ustar00rootroot00000000000000#include "gf16mul.h" #include "gf16_global.h" #include extern "C" { #include "gf16_lookup.h" #include "gf16_shuffle.h" #include "gf16_clmul.h" #include "gf16_affine.h" #include "gf16_xor.h" #include "gf_add.h" #include "gf16_cksum.h" } // CPUID stuff #include "../src/cpuid.h" #ifdef PLATFORM_X86 # ifdef __APPLE__ # include # include # endif # include "x86_jit.h" struct GF16CpuCap { bool hasSSE2, hasSSSE3, hasAVX, hasAVX2, hasAVX512VLBW, hasAVX512VBMI, hasGFNI, hasAVX10; size_t propPrefShuffleThresh; bool propFastJit, propHT; bool canMemWX, isEmulated; int jitOptStrat; GF16CpuCap(bool detect) : hasSSE2(true), hasSSSE3(true), hasAVX(true), hasAVX2(true), hasAVX512VLBW(true), hasAVX512VBMI(true), hasGFNI(true), hasAVX10(true), propPrefShuffleThresh(0), propFastJit(false), propHT(false), canMemWX(true), isEmulated(false), jitOptStrat(GF16_XOR_JIT_STRAT_NONE) { if(!detect) return; int cpuInfo[4]; int cpuInfoX[4]; int family, model, hasMulticore; _cpuid(cpuInfo, 1); hasMulticore = (cpuInfo[3] & (1<<28)); hasSSE2 = (cpuInfo[3] & 0x4000000); hasSSSE3 = (cpuInfo[2] & 0x200); family = ((cpuInfo[0]>>8) & 0xf) + ((cpuInfo[0]>>16) & 0xff0); model = ((cpuInfo[0]>>4) & 0xf) + ((cpuInfo[0]>>12) & 0xf0); propPrefShuffleThresh = 32768; // it seems like XOR JIT is always faster than shuffle at 16-32K sizes bool isAtom = false, isICoreOld = false, isICoreNew = false; if(family == 6) { /* from handy table at http://a4lg.com/tech/x86/database/x86-families-and-models.en.html */ if(CPU_MODEL_IS_BNL_SLM(model)) { /* we have a Bonnell/Silvermont CPU with a really slow pshufb instruction; pretend SSSE3 doesn't exist, as XOR_DEPENDS is much faster */ propPrefShuffleThresh = 2048; isAtom = true; } if(model == 0x0F || model == 0x16) { /* Conroe CPU with relatively slow pshufb; pretend SSSE3 doesn't exist, as XOR_DEPENDS is generally faster */ propPrefShuffleThresh = 8192; } if((model == 0x1A || model == 0x1E || model == 0x1F || model == 0x2E) /*Nehalem*/ || (model == 0x25 || model == 0x2C || model == 0x2F) /*Westmere*/ || CPU_MODEL_IS_SNB_CNL(model) ) isICoreOld = true; if((model == 0x7E || model == 0x7D || model == 0x6A || model == 0x6C) // Icelake client/server || (model == 0xA7) // Rocketlake || (model == 0x8C || model == 0x8D) // Tigerlake || (model == 0x8F || model == 0xCF) // Sapphire/Emerald Rapids ) isICoreNew = true; if(model == 0x8A) { // Lakefield isICoreNew = true; isAtom = true; } if(CPU_MODEL_IS_GLM(model) || CPU_MODEL_IS_TMT(model)) isAtom = true; // Gracemont and newer doesn't set isAtom, so Intel hybrid doesn't have any JIT tuning } if(CPU_FAMMDL_IS_AMDCAT(family, model)) { /* Jaguar has a slow shuffle instruction and XOR is much faster; presumably the same for Bobcat/Puma */ propPrefShuffleThresh = 4096; } propFastJit = ( // basically, AMD, prefer 256-bit XorJit over Shuffle family == 0x6f // AMD Bulldozer family || family == 0x7f // AMD Jaguar/Puma family || family == 0x8f // AMD Zen1/2 family || family == 0xaf // AMD Zen3/4 family ); hasAVX = false; hasAVX2 = false; hasAVX512VLBW = false; hasAVX512VBMI = false; hasGFNI = false; hasAVX10 = false; #if !defined(_MSC_VER) || _MSC_VER >= 1600 _cpuidX(cpuInfoX, 7, 0); if((cpuInfo[2] & 0x1C000000) == 0x1C000000) { // has AVX + OSXSAVE + XSAVE int xcr = _GET_XCR() & 0xff; if((xcr & 6) == 6) { // AVX enabled hasAVX = true; hasAVX2 = cpuInfoX[1] & 0x20; if((xcr & 0xE0) == 0xE0) { // checks AVX512BW + AVX512VL + AVX512F hasAVX512VLBW = ((cpuInfoX[1] & 0xC0010000) == 0xC0010000); hasAVX512VBMI = ((cpuInfoX[2] & 2) == 2 && hasAVX512VLBW); int cpuInfo2[4]; _cpuidX(cpuInfo2, 7, 1); if(cpuInfo2[3] & 0x80000) { _cpuidX(cpuInfo2, 0x24, 0); hasAVX10 = (cpuInfo2[1] & 0xff) >= 1 /* minimum AVX10.1 */ && cpuInfo2[1] & 0x20000; // AVX10/256 } } } } hasGFNI = (cpuInfoX[2] & 0x100) == 0x100; #endif _cpuid(cpuInfo, 0); isEmulated = ( // "Virtual CPU " (Windows on ARM) (cpuInfo[1] == 0x74726956 && cpuInfo[3] == 0x206c6175 && cpuInfo[2] == 0x20555043) // "MicrosoftXTA" || (cpuInfo[1] == 0x7263694d && cpuInfo[3] == 0x666f736f && cpuInfo[2] == 0x41545874) // "VirtualApple" (new Rosetta2) || (cpuInfo[1] == 0x74726956 && cpuInfo[3] == 0x416c6175 && cpuInfo[2] == 0x656c7070) ); #ifdef __APPLE__ if(!isEmulated) { // also check Rosetta: https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment#Determine-Whether-Your-App-Is-Running-as-a-Translated-Binary int proc_translated = 0; size_t int_size = sizeof(proc_translated); if(sysctlbyname("sysctl.proc_translated", &proc_translated, &int_size, NULL, 0) == 0) isEmulated = (bool)proc_translated; } #endif #if defined(PLATFORM_AMD64) && (defined(_WINDOWS) || defined(__WINDOWS__) || defined(_WIN32) || defined(_WIN64)) if(!isEmulated) { // Windows 11's x64 emulation on ARM64 may pretend to be an Athlon64 [https://github.com/animetosho/par2cmdline-turbo/issues/3#issuecomment-1555965971] // try to detect by checking the brand name instead union { int i[12]; char s[49]; } brand; _cpuid(brand.i+0, 0x80000002); _cpuid(brand.i+4, 0x80000003); _cpuid(brand.i+8, 0x80000004); brand.s[48] = 0; isEmulated = !!strstr(brand.s, "Virtual CPU"); } #endif /* try to detect hyper-threading */ propHT = false; if(hasMulticore) { /* only Intel CPUs have HT (VMs which obscure CPUID -> too bad); we won't include AMD Zen here */ int cpuInfoModel[4]; _cpuid(cpuInfoModel, 0); if(cpuInfoModel[1] == 0x756E6547 && cpuInfoModel[2] == 0x6C65746E && cpuInfoModel[3] == 0x49656E69 && cpuInfoModel[0] >= 11) { _cpuidX(cpuInfoModel, 11, 0); if(((cpuInfoModel[2] >> 8) & 0xFF) == 1 // SMT level && (cpuInfoModel[1] & 0xFFFF) > 1) // multiple threads per core propHT = true; } } // test for JIT capability jit_wx_pair* jitTest = jit_alloc(256); canMemWX = (jitTest != NULL); if(jitTest) jit_free(jitTest); // optimal JIT strategy if(isICoreOld) jitOptStrat = GF16_XOR_JIT_STRAT_CLR; else if(isAtom || isICoreNew || family == 0x6f /*AMDfam15*/ || family == 0x1f /*K10*/) // difference on Silvermont appears to be minimal (despite difference in other tests) jitOptStrat = GF16_XOR_JIT_STRAT_COPYNT; else if(family == 0x8f /*AMDfam17*/ || family == 0x9f /*Hygon*/ || family == 0xaf /*AMDfam19*/) // despite other tests, clearing seems to work better than copying // GF16_XOR_JIT_STRAT_COPY seems to perform slightly worse than doing nothing, COPYNT is *much* worse, whereas clearing is slightly better jitOptStrat = GF16_XOR_JIT_STRAT_CLR; else // AMDfam16, Core2 or unknown jitOptStrat = GF16_XOR_JIT_STRAT_NONE; } }; #endif #ifdef PLATFORM_ARM struct GF16CpuCap { bool hasNEON; bool hasSHA3; bool hasSVE; bool hasSVE2; GF16CpuCap(bool detect) : hasNEON(true), hasSHA3(true), hasSVE(true), hasSVE2(true) { if(!detect) return; hasNEON = CPU_HAS_NEON; hasSHA3 = CPU_HAS_NEON_SHA3; hasSVE = CPU_HAS_SVE; hasSVE2 = CPU_HAS_SVE2; if(hasSVE) { size_t sz = gf16_sve_get_size(); if(sz & (sz-1)) { // we don't support non-pow2 vector widths hasSVE = false; hasSVE2 = false; } } } }; #endif #ifdef __riscv struct GF16CpuCap { bool hasVector, hasZvbc; GF16CpuCap(bool detect) : hasVector(true), hasZvbc(true) { if(!detect) return; hasVector = CPU_HAS_VECTOR && CPU_HAS_GC; hasZvbc = hasVector && CPU_HAS_Zvbc; } }; #endif Galois16MethodInfo Galois16Mul::info(Galois16Methods _method) { Galois16Methods method = _method == GF16_AUTO ? default_method() : _method; Galois16MethodInfo _info; _info.id = method; _info.idealInputMultiple = 1; _info.prefetchDownscale = 0; _info.alignment = 2; _info.stride = 2; _info.cksumSize = 0; // set to alignment by default switch(method) { case GF16_SHUFFLE_AVX: case GF16_SHUFFLE_SSSE3: _info.alignment = 16; _info.prefetchDownscale = 1; _info.stride = 32; break; case GF16_SHUFFLE_AVX2: _info.alignment = 32; _info.prefetchDownscale = 1; _info.stride = 64; break; case GF16_SHUFFLE_AVX512: _info.alignment = 64; _info.prefetchDownscale = 1; _info.stride = 128; #ifdef PLATFORM_AMD64 // if 32 registers are available, can do multi-region _info.idealInputMultiple = 3; #endif break; case GF16_SHUFFLE_VBMI: #ifdef PLATFORM_AMD64 _info.idealInputMultiple = 4; _info.prefetchDownscale = 1; #endif _info.alignment = 64; _info.stride = 128; break; case GF16_SHUFFLE2X_AVX512: #ifdef PLATFORM_AMD64 _info.idealInputMultiple = 6; #endif _info.alignment = 64; _info.stride = 64; break; case GF16_SHUFFLE2X_AVX2: #ifdef PLATFORM_AMD64 _info.idealInputMultiple = 2; #endif _info.alignment = 32; _info.stride = 32; break; case GF16_SHUFFLE_NEON: _info.alignment = 32; // presumably double-loads work best when aligned to 32 instead of 16? _info.stride = 32; _info.cksumSize = 16; #ifdef __aarch64__ _info.idealInputMultiple = 2; #endif break; case GF16_CLMUL_NEON: case GF16_CLMUL_SHA3: _info.alignment = 32; // presumably double-loads work best when aligned to 32 instead of 16? _info.stride = 32; _info.cksumSize = 16; #ifdef __aarch64__ _info.idealInputMultiple = 8; #else _info.idealInputMultiple = 3; #endif break; case GF16_SHUFFLE_128_SVE: case GF16_SHUFFLE_128_SVE2: _info.alignment = 16; // I guess this is good enough... _info.cksumSize = gf16_sve_get_size(); _info.stride = _info.cksumSize*2; _info.idealInputMultiple = 3; break; case GF16_SHUFFLE2X_128_SVE2: _info.alignment = 32; _info.cksumSize = gf16_sve_get_size(); _info.stride = _info.cksumSize; _info.idealInputMultiple = 6; break; case GF16_SHUFFLE_512_SVE2: _info.alignment = 64; _info.cksumSize = gf16_sve_get_size(); _info.stride = _info.cksumSize*2; _info.idealInputMultiple = 4; break; case GF16_SHUFFLE_128_RVV: _info.alignment = 16; // I guess this is good enough... _info.cksumSize = gf16_rvv_get_size(); _info.stride = _info.cksumSize*2; _info.idealInputMultiple = 3; break; case GF16_CLMUL_RVV: _info.alignment = 16; // I guess this is good enough... _info.cksumSize = gf16_rvv_get_size(); _info.stride = _info.cksumSize; _info.idealInputMultiple = 12; break; case GF16_CLMUL_SVE2: _info.alignment = 16; _info.cksumSize = gf16_sve_get_size(); _info.stride = _info.cksumSize*2; _info.idealInputMultiple = 8; break; case GF16_AFFINE_AVX512: _info.alignment = 64; _info.stride = 128; #ifdef PLATFORM_AMD64 _info.idealInputMultiple = 6; _info.prefetchDownscale = 1; #endif break; case GF16_AFFINE_AVX10: _info.alignment = 32; _info.stride = 64; #ifdef PLATFORM_AMD64 _info.idealInputMultiple = 6; #endif break; case GF16_AFFINE_AVX2: _info.alignment = 32; _info.stride = 64; #ifdef PLATFORM_AMD64 _info.idealInputMultiple = 3; #endif break; case GF16_AFFINE_GFNI: _info.alignment = 16; _info.stride = 32; #ifdef PLATFORM_AMD64 _info.idealInputMultiple = 3; #endif break; case GF16_AFFINE2X_AVX512: _info.alignment = 64; _info.stride = 64; #ifdef PLATFORM_AMD64 _info.idealInputMultiple = 12; #else _info.idealInputMultiple = 2; #endif break; case GF16_AFFINE2X_AVX10: _info.alignment = 32; _info.stride = 32; #ifdef PLATFORM_AMD64 _info.idealInputMultiple = 12; #else _info.idealInputMultiple = 2; #endif break; case GF16_AFFINE2X_AVX2: _info.alignment = 32; _info.stride = 32; #ifdef PLATFORM_AMD64 _info.idealInputMultiple = 6; #else _info.idealInputMultiple = 2; #endif break; case GF16_AFFINE2X_GFNI: _info.alignment = 16; _info.stride = 16; #ifdef PLATFORM_AMD64 _info.idealInputMultiple = 6; #else _info.idealInputMultiple = 2; #endif break; case GF16_XOR_JIT_AVX512: case GF16_XOR_JIT_AVX2: //case GF16_XOR_JIT_AVX: case GF16_XOR_JIT_SSE2: case GF16_XOR_SSE2: { _info.alignment = 16; _info.prefetchDownscale = 1; if(method == GF16_XOR_JIT_AVX2) _info.alignment = 32; if(method == GF16_XOR_JIT_AVX512) { _info.idealInputMultiple = 6; _info.alignment = 64; } _info.stride = _info.alignment*16; } break; case GF16_LOOKUP_SSE2: _info.alignment = 16; _info.stride = 16; break; case GF16_LOOKUP3: _info.stride = gf16_lookup3_stride(); _info.alignment = _info.stride; // assume platform doesn't like misalignment if(_info.stride) break; // else fallthrough case GF16_LOOKUP: default: _info.id = GF16_LOOKUP; _info.stride = gf16_lookup_stride(); _info.alignment = _info.stride; // assume platform doesn't like misalignment break; } _info.name = methodToText(_info.id); if(!_info.cksumSize) _info.cksumSize = _info.alignment; // TODO: improve these? // TODO: this probably needs to be variable depending on the CPU cache size // although these defaults are pretty good across most CPUs switch(method) { case GF16_XOR_JIT_SSE2: // JIT is a little slow, so larger blocks make things faster _info.idealChunkSize = 64*1024; // seems like weaker processors prefer 32K break; case GF16_XOR_JIT_AVX2: // 64-96K generally seems ideal, but this method is most likely used on Zen, which seems to prefer 128-256K _info.idealChunkSize = 128*1024; break; case GF16_XOR_JIT_AVX512: _info.idealChunkSize = 48*1024; // peak on Skylake-X break; case GF16_LOOKUP: case GF16_LOOKUP_SSE2: case GF16_LOOKUP3: case GF16_XOR_SSE2: // these seem to generally perfer larger sizes, particularly XOR // lookup is mostly run on weaker processors, so prefer smaller to avoid overloading cache _info.idealChunkSize = 32*1024; break; case GF16_SHUFFLE_SSSE3: case GF16_SHUFFLE_AVX: case GF16_SHUFFLE_NEON: case GF16_SHUFFLE_128_SVE: // may need smaller chunks for larger vector size case GF16_SHUFFLE_128_SVE2: case GF16_SHUFFLE_128_RVV: _info.idealChunkSize = 16*1024; break; case GF16_SHUFFLE_AVX2: case GF16_SHUFFLE_AVX512: case GF16_SHUFFLE_VBMI: case GF16_SHUFFLE2X_AVX2: case GF16_SHUFFLE2X_AVX512: case GF16_SHUFFLE2X_128_SVE2: case GF16_SHUFFLE_512_SVE2: // try to target L2 _info.idealChunkSize = 8*1024; break; case GF16_AFFINE_AVX2: case GF16_AFFINE2X_AVX2: case GF16_AFFINE_AVX10: case GF16_AFFINE2X_AVX10: _info.idealChunkSize = 4*1024; // completely untested break; case GF16_AFFINE_AVX512: case GF16_AFFINE2X_AVX512: _info.idealChunkSize = 4*1024; break; case GF16_CLMUL_NEON: // faster init than Shuffle, and usually faster case GF16_CLMUL_SHA3: case GF16_CLMUL_SVE2: // may want smaller chunk size for wider vectors case GF16_CLMUL_RVV: // ^ case GF16_AFFINE_GFNI: case GF16_AFFINE2X_GFNI: _info.idealChunkSize = 8*1024; break; default: // shouldn't reach here as all options are covered above _info.idealChunkSize = 32*1024; // random generic size that's a rough average of everything } return _info; } void Galois16Mul::setupMethod(Galois16Methods _method) { Galois16Methods method = _method == GF16_AUTO ? default_method() : _method; if(scratch) { ALIGN_FREE(scratch); scratch = NULL; } #define METHOD_REQUIRES(c, cx) if(!(c)) abort(); \ if(!(cx)) { \ setupMethod(_method == GF16_AUTO ? GF16_LOOKUP : GF16_AUTO); \ return; \ } #ifdef PARPAR_INVERT_SUPPORT #define SET_FOR_INVERT(var, fn) var = &fn #else #define SET_FOR_INVERT(var, fn) (void)0 #endif #ifdef PARPAR_INCLUDE_BASIC_OPS #define SET_BASIC_OP(var, fn) var = &fn #else #define SET_BASIC_OP(var, fn) (void)0 #endif #ifdef PARPAR_OPENCL_SUPPORT #define SET_FOR_OPENCL(var, fn) var = &fn #else #define SET_FOR_OPENCL(var, fn) (void)0 #endif switch(method) { case GF16_SHUFFLE_AVX512: case GF16_SHUFFLE_AVX2: case GF16_SHUFFLE_AVX: case GF16_SHUFFLE_SSSE3: scratch = gf16_shuffle_init_x86(GF16_POLYNOMIAL); switch(method) { case GF16_SHUFFLE_SSSE3: METHOD_REQUIRES(gf16_shuffle_available_ssse3, scratch) SET_FOR_INVERT(_mul, gf16_shuffle_mul_ssse3); _mul_add = &gf16_shuffle_muladd_ssse3; _mul_add_pf = &gf16_shuffle_muladd_prefetch_ssse3; SET_BASIC_OP(add_multi, gf_add_multi_sse2); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i1_sse2); add_multi_packpf = &gf_add_multi_packpf_v2i1_sse2; SET_FOR_INVERT(prepare, gf16_shuffle_prepare_ssse3); SET_BASIC_OP(prepare_packed, gf16_shuffle_prepare_packed_ssse3); prepare_packed_cksum = &gf16_shuffle_prepare_packed_cksum_ssse3; prepare_partial_packsum = &gf16_shuffle_prepare_partial_packsum_ssse3; SET_FOR_INVERT(finish, gf16_shuffle_finish_ssse3); SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_ssse3); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_ssse3; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_ssse3; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_sse2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_sse2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_sse2); SET_FOR_INVERT(replace_word, gf16_shuffle16_replace_word); break; case GF16_SHUFFLE_AVX: METHOD_REQUIRES(gf16_shuffle_available_avx, scratch) SET_FOR_INVERT(_mul, gf16_shuffle_mul_avx); _mul_add = &gf16_shuffle_muladd_avx; _mul_add_pf = &gf16_shuffle_muladd_prefetch_avx; SET_BASIC_OP(add_multi, gf_add_multi_sse2); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i1_sse2); add_multi_packpf = &gf_add_multi_packpf_v2i1_sse2; SET_FOR_INVERT(prepare, gf16_shuffle_prepare_avx); SET_BASIC_OP(prepare_packed, gf16_shuffle_prepare_packed_avx); prepare_packed_cksum = &gf16_shuffle_prepare_packed_cksum_avx; prepare_partial_packsum = &gf16_shuffle_prepare_partial_packsum_avx; SET_FOR_INVERT(finish, gf16_shuffle_finish_avx); SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_avx); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_avx; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_avx; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_sse2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_sse2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_sse2); SET_FOR_INVERT(replace_word, gf16_shuffle16_replace_word); break; case GF16_SHUFFLE_AVX2: METHOD_REQUIRES(gf16_shuffle_available_avx2, scratch) SET_FOR_INVERT(_mul, gf16_shuffle_mul_avx2); _mul_add = &gf16_shuffle_muladd_avx2; _mul_add_pf = &gf16_shuffle_muladd_prefetch_avx2; SET_BASIC_OP(add_multi, gf_add_multi_avx2); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i1_avx2); add_multi_packpf = &gf_add_multi_packpf_v2i1_avx2; SET_FOR_INVERT(prepare, gf16_shuffle_prepare_avx2); SET_BASIC_OP(prepare_packed, gf16_shuffle_prepare_packed_avx2); prepare_packed_cksum = &gf16_shuffle_prepare_packed_cksum_avx2; prepare_partial_packsum = &gf16_shuffle_prepare_partial_packsum_avx2; SET_FOR_INVERT(finish, gf16_shuffle_finish_avx2); SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_avx2); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_avx2; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_avx2; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_avx2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_avx2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_avx2); SET_FOR_INVERT(replace_word, gf16_shuffle32_replace_word); break; case GF16_SHUFFLE_AVX512: METHOD_REQUIRES(gf16_shuffle_available_avx512, scratch) SET_FOR_INVERT(_mul, gf16_shuffle_mul_avx512); _mul_add = &gf16_shuffle_muladd_avx512; _mul_add_pf = &gf16_shuffle_muladd_prefetch_avx512; SET_BASIC_OP(add_multi, gf_add_multi_avx512); #ifdef PLATFORM_AMD64 // if 32 registers are available, can do multi-region SET_FOR_INVERT(_mul_add_multi, gf16_shuffle_muladd_multi_avx512); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_shuffle_muladd_multi_stridepf_avx512); _mul_add_multi_packed = &gf16_shuffle_muladd_multi_packed_avx512; _mul_add_multi_packpf = &gf16_shuffle_muladd_multi_packpf_avx512; SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i3_avx512); add_multi_packpf = &gf_add_multi_packpf_v2i3_avx512; #else SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i1_avx512); add_multi_packpf = &gf_add_multi_packpf_v2i1_avx512; #endif SET_FOR_INVERT(prepare, gf16_shuffle_prepare_avx512); SET_BASIC_OP(prepare_packed, gf16_shuffle_prepare_packed_avx512); prepare_packed_cksum = &gf16_shuffle_prepare_packed_cksum_avx512; prepare_partial_packsum = &gf16_shuffle_prepare_partial_packsum_avx512; SET_FOR_INVERT(finish, gf16_shuffle_finish_avx512); SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_avx512); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_avx512; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_avx512; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_avx512); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_avx512); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_avx512); SET_FOR_INVERT(replace_word, gf16_shuffle64_replace_word); break; default: break; // for pedantic compilers } break; case GF16_SHUFFLE_VBMI: scratch = gf16_shuffle_init_vbmi(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_shuffle_available_vbmi, scratch) SET_FOR_INVERT(_mul, gf16_shuffle_mul_vbmi); _mul_add = &gf16_shuffle_muladd_vbmi; _mul_add_pf = &gf16_shuffle_muladd_prefetch_vbmi; SET_BASIC_OP(add_multi, gf_add_multi_avx512); #ifdef PLATFORM_AMD64 SET_FOR_INVERT(_mul_add_multi, gf16_shuffle_muladd_multi_vbmi); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_shuffle_muladd_multi_stridepf_vbmi); _mul_add_multi_packed = &gf16_shuffle_muladd_multi_packed_vbmi; _mul_add_multi_packpf = &gf16_shuffle_muladd_multi_packpf_vbmi; SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i4_avx512); add_multi_packpf = &gf_add_multi_packpf_v2i4_avx512; #else SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i1_avx512); add_multi_packpf = &gf_add_multi_packpf_v2i1_avx512; #endif SET_FOR_INVERT(prepare, gf16_shuffle_prepare_avx512); SET_BASIC_OP(prepare_packed, gf16_shuffle_prepare_packed_vbmi); prepare_packed_cksum = &gf16_shuffle_prepare_packed_cksum_vbmi; prepare_partial_packsum = &gf16_shuffle_prepare_partial_packsum_vbmi; SET_FOR_INVERT(finish, gf16_shuffle_finish_avx512); SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_avx512); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_avx512; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_avx512; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_avx512); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_avx512); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_avx512); SET_FOR_INVERT(replace_word, gf16_shuffle64_replace_word); break; case GF16_SHUFFLE2X_AVX512: scratch = gf16_shuffle_init_x86(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_shuffle_available_avx512, scratch) SET_FOR_INVERT(_mul, gf16_shuffle2x_mul_avx512); _mul_add = &gf16_shuffle2x_muladd_avx512; SET_BASIC_OP(add_multi, gf_add_multi_avx512); #ifdef PLATFORM_AMD64 SET_FOR_INVERT(_mul_add_multi, gf16_shuffle2x_muladd_multi_avx512); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_shuffle2x_muladd_multi_stridepf_avx512); _mul_add_multi_packed = &gf16_shuffle2x_muladd_multi_packed_avx512; _mul_add_multi_packpf = &gf16_shuffle2x_muladd_multi_packpf_avx512; SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i6_avx512); add_multi_packpf = &gf_add_multi_packpf_v1i6_avx512; #else SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i1_avx512); add_multi_packpf = &gf_add_multi_packpf_v1i1_avx512; #endif SET_FOR_INVERT(prepare, gf16_shuffle2x_prepare_avx512); SET_BASIC_OP(prepare_packed, gf16_shuffle2x_prepare_packed_avx512); prepare_packed_cksum = &gf16_shuffle2x_prepare_packed_cksum_avx512; prepare_partial_packsum = &gf16_shuffle2x_prepare_partial_packsum_avx512; SET_FOR_INVERT(finish, gf16_shuffle2x_finish_avx512); SET_BASIC_OP(finish_packed, gf16_shuffle2x_finish_packed_avx512); finish_packed_cksum = &gf16_shuffle2x_finish_packed_cksum_avx512; finish_partial_packsum = &gf16_shuffle2x_finish_partial_packsum_avx512; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_avx512); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_avx512); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_avx512); SET_FOR_INVERT(replace_word, gf16_shuffle2x32_replace_word); break; case GF16_SHUFFLE2X_AVX2: scratch = gf16_shuffle_init_x86(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_shuffle_available_avx2, scratch) SET_FOR_INVERT(_mul, gf16_shuffle2x_mul_avx2); _mul_add = &gf16_shuffle2x_muladd_avx2; SET_BASIC_OP(add_multi, gf_add_multi_avx2); #ifdef PLATFORM_AMD64 SET_FOR_INVERT(_mul_add_multi, gf16_shuffle2x_muladd_multi_avx2); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_shuffle2x_muladd_multi_stridepf_avx2); _mul_add_multi_packed = &gf16_shuffle2x_muladd_multi_packed_avx2; _mul_add_multi_packpf = &gf16_shuffle2x_muladd_multi_packpf_avx2; SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i2_avx2); add_multi_packpf = &gf_add_multi_packpf_v1i2_avx2; #else SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i1_avx2); add_multi_packpf = &gf_add_multi_packpf_v1i1_avx2; #endif SET_FOR_INVERT(prepare, gf16_shuffle2x_prepare_avx2); SET_BASIC_OP(prepare_packed, gf16_shuffle2x_prepare_packed_avx2); prepare_packed_cksum = &gf16_shuffle2x_prepare_packed_cksum_avx2; prepare_partial_packsum = &gf16_shuffle2x_prepare_partial_packsum_avx2; SET_FOR_INVERT(finish, gf16_shuffle2x_finish_avx2); SET_BASIC_OP(finish_packed, gf16_shuffle2x_finish_packed_avx2); finish_packed_cksum = &gf16_shuffle2x_finish_packed_cksum_avx2; finish_partial_packsum = &gf16_shuffle2x_finish_partial_packsum_avx2; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_avx2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_avx2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_avx2); SET_FOR_INVERT(replace_word, gf16_shuffle2x16_replace_word); break; case GF16_SHUFFLE_NEON: scratch = gf16_shuffle_init_arm(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_available_neon, scratch) SET_FOR_INVERT(_mul, gf16_shuffle_mul_neon); _mul_add = &gf16_shuffle_muladd_neon; SET_BASIC_OP(add_multi, gf_add_multi_neon); #ifdef __aarch64__ // enable only if 32 registers available SET_FOR_INVERT(_mul_add_multi, gf16_shuffle_muladd_multi_neon); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_shuffle_muladd_multi_stridepf_neon); _mul_add_multi_packed = &gf16_shuffle_muladd_multi_packed_neon; // TODO: on Cortex A53, prefetching seems to be slower, so disabled for now //_mul_add_multi_packpf = &gf16_shuffle_muladd_multi_packpf_neon; SET_BASIC_OP(prepare_packed, gf16_shuffle_prepare_packed_neon); #endif SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_shuffle_neon); add_multi_packpf = &gf_add_multi_packpf_shuffle_neon; prepare_packed_cksum = &gf16_shuffle_prepare_packed_cksum_neon; prepare_partial_packsum = &gf16_shuffle_prepare_partial_packsum_neon; SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_neon); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_neon; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_neon; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_neon); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_neon); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_neon); break; case GF16_CLMUL_NEON: { int available = gf16_clmul_init_arm(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_available_neon && available, 1) // use Shuffle for single region multiplies, because it's faster (NOTE: disabled for slim GF16 build) scratch = gf16_shuffle_init_arm(GF16_POLYNOMIAL); if(scratch) { SET_FOR_INVERT(_mul, gf16_shuffle_mul_neon); _mul_add = &gf16_shuffle_muladd_neon; } else { SET_FOR_INVERT(_mul, gf16_clmul_mul_neon); _mul_add = &gf16_clmul_muladd_neon; } SET_FOR_INVERT(_mul_add_multi, gf16_clmul_muladd_multi_neon); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_clmul_muladd_multi_stridepf_neon); _mul_add_multi_packed = &gf16_clmul_muladd_multi_packed_neon; SET_BASIC_OP(add_multi, gf_add_multi_neon); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_clmul_neon); add_multi_packpf = &gf_add_multi_packpf_clmul_neon; // TODO: on Cortex A53, prefetching seems to be slower, so disabled for now //_mul_add_multi_packpf = &gf16_clmul_muladd_multi_packpf_neon; SET_BASIC_OP(prepare_packed, gf16_clmul_prepare_packed_neon); prepare_packed_cksum = &gf16_clmul_prepare_packed_cksum_neon; prepare_partial_packsum = &gf16_clmul_prepare_partial_packsum_neon; SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_neon); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_neon; // re-use shuffle routine finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_neon; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_neon); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_neon); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_neon); } break; case GF16_CLMUL_SHA3: { int available = gf16_clmul_init_arm(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_available_neon_sha3 && available, 1) scratch = gf16_shuffle_init_arm(GF16_POLYNOMIAL); if(scratch) { SET_FOR_INVERT(_mul, gf16_shuffle_mul_neon); _mul_add = &gf16_shuffle_muladd_neon; } else { SET_FOR_INVERT(_mul, gf16_clmul_mul_sha3); _mul_add = &gf16_clmul_muladd_sha3; } SET_FOR_INVERT(_mul_add_multi, gf16_clmul_muladd_multi_sha3); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_clmul_muladd_multi_stridepf_sha3); _mul_add_multi_packed = &gf16_clmul_muladd_multi_packed_sha3; SET_BASIC_OP(add_multi, gf_add_multi_neon); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_clmul_neon); add_multi_packpf = &gf_add_multi_packpf_clmul_neon; _mul_add_multi_packpf = &gf16_clmul_muladd_multi_packpf_sha3; SET_BASIC_OP(prepare_packed, gf16_clmul_prepare_packed_neon); prepare_packed_cksum = &gf16_clmul_prepare_packed_cksum_neon; prepare_partial_packsum = &gf16_clmul_prepare_partial_packsum_neon; SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_neon); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_neon; // re-use shuffle routine finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_neon; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_neon); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_neon); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_neon); } break; case GF16_SHUFFLE_128_SVE: METHOD_REQUIRES(gf16_available_sve, 1) scratch = gf16_shuffle_init_128_sve(GF16_POLYNOMIAL); SET_FOR_INVERT(_mul, gf16_shuffle_mul_128_sve); _mul_add = &gf16_shuffle_muladd_128_sve; SET_FOR_INVERT(_mul_add_multi, gf16_shuffle_muladd_multi_128_sve); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_shuffle_muladd_multi_stridepf_128_sve); _mul_add_multi_packed = &gf16_shuffle_muladd_multi_packed_128_sve; //_mul_add_multi_packpf = &gf16_shuffle_muladd_multi_packpf_128_sve; SET_BASIC_OP(add_multi, gf_add_multi_sve); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_sve); add_multi_packpf = &gf_add_multi_packpf_sve; SET_BASIC_OP(prepare_packed, gf16_shuffle_prepare_packed_sve); prepare_packed_cksum = &gf16_shuffle_prepare_packed_cksum_sve; prepare_partial_packsum = &gf16_shuffle_prepare_partial_packsum_sve; SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_sve); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_sve; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_sve; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_sve); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_sve); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_sve); break; case GF16_SHUFFLE_128_SVE2: METHOD_REQUIRES(gf16_available_sve2, 1) SET_FOR_INVERT(_mul, gf16_shuffle_mul_128_sve2); _mul_add = &gf16_shuffle_muladd_128_sve2; SET_FOR_INVERT(_mul_add_multi, gf16_shuffle_muladd_multi_128_sve2); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_shuffle_muladd_multi_stridepf_128_sve2); _mul_add_multi_packed = &gf16_shuffle_muladd_multi_packed_128_sve2; //_mul_add_multi_packpf = &gf16_shuffle_muladd_multi_packpf_128_sve2; SET_BASIC_OP(add_multi, gf_add_multi_sve2); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i3_sve2); add_multi_packpf = &gf_add_multi_packpf_v2i3_sve2; SET_BASIC_OP(prepare_packed, gf16_shuffle_prepare_packed_sve); prepare_packed_cksum = &gf16_shuffle_prepare_packed_cksum_sve; prepare_partial_packsum = &gf16_shuffle_prepare_partial_packsum_sve; SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_sve); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_sve; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_sve; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_sve); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_sve); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_sve); break; case GF16_SHUFFLE2X_128_SVE2: METHOD_REQUIRES(gf16_available_sve2, gf16_sve_get_size() >= 32) SET_FOR_INVERT(_mul, gf16_shuffle2x_mul_128_sve2); _mul_add = &gf16_shuffle2x_muladd_128_sve2; SET_FOR_INVERT(_mul_add_multi, gf16_shuffle2x_muladd_multi_128_sve2); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_shuffle2x_muladd_multi_stridepf_128_sve2); _mul_add_multi_packed = &gf16_shuffle2x_muladd_multi_packed_128_sve2; //_mul_add_multi_packpf = &gf16_shuffle2x_muladd_multi_packpf_128_sve2; SET_BASIC_OP(add_multi, gf_add_multi_sve2); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i6_sve2); add_multi_packpf = &gf_add_multi_packpf_v1i6_sve2; SET_BASIC_OP(prepare_packed, gf16_shuffle2x_prepare_packed_sve); prepare_packed_cksum = &gf16_shuffle2x_prepare_packed_cksum_sve; prepare_partial_packsum = &gf16_shuffle2x_prepare_partial_packsum_sve; SET_BASIC_OP(finish_packed, gf16_shuffle2x_finish_packed_sve); finish_packed_cksum = &gf16_shuffle2x_finish_packed_cksum_sve; finish_partial_packsum = &gf16_shuffle2x_finish_partial_packsum_sve; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_sve); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_sve); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_sve); break; case GF16_SHUFFLE_512_SVE2: METHOD_REQUIRES(gf16_available_sve2, gf16_sve_get_size() >= 64) // TODO: this could be made to work on vect-size>=256b using TBL2 scratch = gf16_shuffle_init_512_sve(GF16_POLYNOMIAL); SET_FOR_INVERT(_mul, gf16_shuffle_mul_512_sve2); _mul_add = &gf16_shuffle_muladd_512_sve2; SET_FOR_INVERT(_mul_add_multi, gf16_shuffle_muladd_multi_512_sve2); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_shuffle_muladd_multi_stridepf_512_sve2); _mul_add_multi_packed = &gf16_shuffle_muladd_multi_packed_512_sve2; //_mul_add_multi_packpf = &gf16_shuffle_muladd_multi_packpf_512_sve2; SET_BASIC_OP(add_multi, gf_add_multi_sve2); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i4_sve2); add_multi_packpf = &gf_add_multi_packpf_v2i4_sve2; SET_BASIC_OP(prepare_packed, gf16_shuffle_prepare_packed_512_sve2); prepare_packed_cksum = &gf16_shuffle_prepare_packed_cksum_512_sve2; prepare_partial_packsum = &gf16_shuffle_prepare_partial_packsum_512_sve2; SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_sve); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_sve; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_sve; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_sve); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_sve); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_sve); break; case GF16_CLMUL_SVE2: METHOD_REQUIRES(gf16_available_sve2, 1) // single region multiplies (_mul/add) use Shuffle-128 instead SET_FOR_INVERT(_mul, gf16_shuffle_mul_128_sve2); _mul_add = &gf16_shuffle_muladd_128_sve2; SET_FOR_INVERT(_mul_add_multi, gf16_clmul_muladd_multi_sve2); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_clmul_muladd_multi_stridepf_sve2); _mul_add_multi_packed = &gf16_clmul_muladd_multi_packed_sve2; //_mul_add_multi_packpf = &gf16_clmul_muladd_multi_packpf_sve2; SET_BASIC_OP(add_multi, gf_add_multi_sve2); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i8_sve2); add_multi_packpf = &gf_add_multi_packpf_v2i8_sve2; SET_BASIC_OP(prepare_packed, gf16_clmul_prepare_packed_sve2); prepare_packed_cksum = &gf16_clmul_prepare_packed_cksum_sve2; prepare_partial_packsum = &gf16_clmul_prepare_partial_packsum_sve2; SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_sve); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_sve; // reuse shuffle finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_sve; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_sve); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_sve); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_sve); break; case GF16_SHUFFLE_128_RVV: scratch = gf16_shuffle_init_128_rvv(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_available_rvv, 1) SET_FOR_INVERT(_mul, gf16_shuffle_mul_128_rvv); _mul_add = &gf16_shuffle_muladd_128_rvv; SET_FOR_INVERT(_mul_add_multi, gf16_shuffle_muladd_multi_128_rvv); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_shuffle_muladd_multi_stridepf_128_rvv); _mul_add_multi_packed = &gf16_shuffle_muladd_multi_packed_128_rvv; //_mul_add_multi_packpf = &gf16_shuffle_muladd_multi_packpf_128_rvv; SET_BASIC_OP(add_multi, gf_add_multi_rvv); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i3_rvv); add_multi_packpf = &gf_add_multi_packpf_v2i3_rvv; SET_BASIC_OP(prepare_packed, gf16_shuffle_prepare_packed_rvv); prepare_packed_cksum = &gf16_shuffle_prepare_packed_cksum_rvv; prepare_partial_packsum = &gf16_shuffle_prepare_partial_packsum_rvv; SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_rvv); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_rvv; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_rvv; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_rvv); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_rvv); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_rvv); break; case GF16_CLMUL_RVV: METHOD_REQUIRES(gf16_available_rvv_zvbc, 1) SET_FOR_INVERT(_mul, gf16_clmul_mul_rvv); _mul_add = &gf16_clmul_muladd_rvv; SET_FOR_INVERT(_mul_add_multi, gf16_clmul_muladd_multi_rvv); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_clmul_muladd_multi_stridepf_rvv); _mul_add_multi_packed = &gf16_clmul_muladd_multi_packed_rvv; //_mul_add_multi_packpf = &gf16_clmul_muladd_multi_packpf_rvv; SET_BASIC_OP(add_multi, gf_add_multi_rvv); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i12_rvv); add_multi_packpf = &gf_add_multi_packpf_v1i12_rvv; SET_BASIC_OP(prepare_packed, gf16_clmul_prepare_packed_rvv); prepare_packed_cksum = &gf16_clmul_prepare_packed_cksum_rvv; prepare_partial_packsum = &gf16_clmul_prepare_partial_packsum_rvv; SET_BASIC_OP(finish_packed, gf16_clmul_finish_packed_rvv); finish_packed_cksum = &gf16_clmul_finish_packed_cksum_rvv; finish_partial_packsum = &gf16_clmul_finish_partial_packsum_rvv; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_rvv); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_rvv); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_rvv); break; case GF16_AFFINE_AVX512: scratch = gf16_affine_init_avx2(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_affine_available_avx512 && gf16_shuffle_available_avx512, 1) SET_FOR_INVERT(_mul, gf16_affine_mul_avx512); _mul_add = &gf16_affine_muladd_avx512; _mul_add_pf = &gf16_affine_muladd_prefetch_avx512; SET_BASIC_OP(add_multi, gf_add_multi_avx512); #ifdef PLATFORM_AMD64 SET_FOR_INVERT(_mul_add_multi, gf16_affine_muladd_multi_avx512); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_affine_muladd_multi_stridepf_avx512); _mul_add_multi_packed = &gf16_affine_muladd_multi_packed_avx512; _mul_add_multi_packpf = &gf16_affine_muladd_multi_packpf_avx512; SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i6_avx512); add_multi_packpf = &gf_add_multi_packpf_v2i6_avx512; #else SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i1_avx512); add_multi_packpf = &gf_add_multi_packpf_v2i1_avx512; #endif SET_FOR_INVERT(prepare, gf16_shuffle_prepare_avx512); SET_BASIC_OP(prepare_packed, gf16_affine_prepare_packed_avx512); prepare_packed_cksum = &gf16_affine_prepare_packed_cksum_avx512; prepare_partial_packsum = &gf16_affine_prepare_partial_packsum_avx512; SET_FOR_INVERT(finish, gf16_shuffle_finish_avx512); SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_avx512); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_avx512; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_avx512; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_avx512); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_avx512); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_avx512); SET_FOR_INVERT(replace_word, gf16_shuffle64_replace_word); break; case GF16_AFFINE_AVX10: scratch = gf16_affine_init_avx2(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_affine_available_avx10 && gf16_shuffle_available_avx2, 1) SET_FOR_INVERT(_mul, gf16_affine_mul_avx2); _mul_add = &gf16_affine_muladd_avx10; _mul_add_pf = &gf16_affine_muladd_prefetch_avx10; SET_BASIC_OP(add_multi, gf_add_multi_avx2); #ifdef PLATFORM_AMD64 SET_FOR_INVERT(_mul_add_multi, gf16_affine_muladd_multi_avx10); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_affine_muladd_multi_stridepf_avx10); _mul_add_multi_packed = &gf16_affine_muladd_multi_packed_avx10; _mul_add_multi_packpf = &gf16_affine_muladd_multi_packpf_avx10; SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i6_avx10); add_multi_packpf = &gf_add_multi_packpf_v2i6_avx10; #else SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i1_avx2); add_multi_packpf = &gf_add_multi_packpf_v2i1_avx2; #endif SET_FOR_INVERT(prepare, gf16_shuffle_prepare_avx2); SET_BASIC_OP(prepare_packed, gf16_affine_prepare_packed_avx10); prepare_packed_cksum = &gf16_affine_prepare_packed_cksum_avx10; prepare_partial_packsum = &gf16_affine_prepare_partial_packsum_avx10; SET_FOR_INVERT(finish, gf16_shuffle_finish_avx2); SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_avx2); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_avx2; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_avx2; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_avx2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_avx2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_avx2); SET_FOR_INVERT(replace_word, gf16_shuffle32_replace_word); break; case GF16_AFFINE_AVX2: scratch = gf16_affine_init_avx2(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_affine_available_avx2 && gf16_shuffle_available_avx2, 1) SET_FOR_INVERT(_mul, gf16_affine_mul_avx2); _mul_add = &gf16_affine_muladd_avx2; _mul_add_pf = &gf16_affine_muladd_prefetch_avx2; SET_BASIC_OP(add_multi, gf_add_multi_avx2); #ifdef PLATFORM_AMD64 SET_FOR_INVERT(_mul_add_multi, gf16_affine_muladd_multi_avx2); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_affine_muladd_multi_stridepf_avx2); _mul_add_multi_packed = &gf16_affine_muladd_multi_packed_avx2; _mul_add_multi_packpf = &gf16_affine_muladd_multi_packpf_avx2; SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i3_avx2); add_multi_packpf = &gf_add_multi_packpf_v2i3_avx2; #else SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i1_avx2); add_multi_packpf = &gf_add_multi_packpf_v2i1_avx2; #endif SET_FOR_INVERT(prepare, gf16_shuffle_prepare_avx2); SET_BASIC_OP(prepare_packed, gf16_affine_prepare_packed_avx2); prepare_packed_cksum = &gf16_affine_prepare_packed_cksum_avx2; prepare_partial_packsum = &gf16_affine_prepare_partial_packsum_avx2; SET_FOR_INVERT(finish, gf16_shuffle_finish_avx2); SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_avx2); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_avx2; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_avx2; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_avx2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_avx2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_avx2); SET_FOR_INVERT(replace_word, gf16_shuffle32_replace_word); break; case GF16_AFFINE_GFNI: scratch = gf16_affine_init_gfni(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_affine_available_gfni && gf16_shuffle_available_ssse3, 1) SET_FOR_INVERT(_mul, gf16_affine_mul_gfni); _mul_add = &gf16_affine_muladd_gfni; _mul_add_pf = &gf16_affine_muladd_prefetch_gfni; SET_BASIC_OP(add_multi, gf_add_multi_sse2); #ifdef PLATFORM_AMD64 SET_FOR_INVERT(_mul_add_multi, gf16_affine_muladd_multi_gfni); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_affine_muladd_multi_stridepf_gfni); _mul_add_multi_packed = &gf16_affine_muladd_multi_packed_gfni; _mul_add_multi_packpf = &gf16_affine_muladd_multi_packpf_gfni; SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i3_sse2); add_multi_packpf = &gf_add_multi_packpf_v2i3_sse2; #else SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v2i1_sse2); add_multi_packpf = &gf_add_multi_packpf_v2i1_sse2; #endif SET_FOR_INVERT(prepare, gf16_shuffle_prepare_ssse3); SET_BASIC_OP(prepare_packed, gf16_affine_prepare_packed_gfni); prepare_packed_cksum = &gf16_affine_prepare_packed_cksum_gfni; prepare_partial_packsum = &gf16_affine_prepare_partial_packsum_gfni; SET_FOR_INVERT(finish, gf16_shuffle_finish_ssse3); SET_BASIC_OP(finish_packed, gf16_shuffle_finish_packed_ssse3); finish_packed_cksum = &gf16_shuffle_finish_packed_cksum_ssse3; finish_partial_packsum = &gf16_shuffle_finish_partial_packsum_ssse3; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_sse2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_sse2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_sse2); SET_FOR_INVERT(replace_word, gf16_shuffle16_replace_word); break; case GF16_AFFINE2X_AVX512: scratch = gf16_affine_init_avx2(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_affine_available_avx512 && gf16_shuffle_available_avx512, 1) SET_FOR_INVERT(_mul, gf16_affine2x_mul_avx512); _mul_add = &gf16_affine2x_muladd_avx512; SET_FOR_INVERT(_mul_add_multi, gf16_affine2x_muladd_multi_avx512); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_affine2x_muladd_multi_stridepf_avx512); _mul_add_multi_packed = &gf16_affine2x_muladd_multi_packed_avx512; _mul_add_multi_packpf = &gf16_affine2x_muladd_multi_packpf_avx512; SET_BASIC_OP(add_multi, gf_add_multi_avx512); #ifdef PLATFORM_AMD64 SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i12_avx512); add_multi_packpf = &gf_add_multi_packpf_v1i12_avx512; #else SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i2_avx512); add_multi_packpf = &gf_add_multi_packpf_v1i2_avx512; #endif SET_FOR_INVERT(prepare, gf16_affine2x_prepare_avx512); SET_BASIC_OP(prepare_packed, gf16_affine2x_prepare_packed_avx512); prepare_packed_cksum = &gf16_affine2x_prepare_packed_cksum_avx512; prepare_partial_packsum = &gf16_affine2x_prepare_partial_packsum_avx512; SET_FOR_INVERT(finish, gf16_affine2x_finish_avx512); SET_BASIC_OP(finish_packed, gf16_affine2x_finish_packed_avx512); finish_packed_cksum = &gf16_affine2x_finish_packed_cksum_avx512; finish_partial_packsum = &gf16_affine2x_finish_partial_packsum_avx512; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_avx512); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_avx512); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_avx512); SET_FOR_INVERT(replace_word, gf16_affine2x_replace_word); break; case GF16_AFFINE2X_AVX10: scratch = gf16_affine_init_avx2(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_affine_available_avx10 && gf16_shuffle_available_avx2, 1) SET_FOR_INVERT(_mul, gf16_affine2x_mul_avx2); _mul_add = &gf16_affine2x_muladd_avx10; SET_FOR_INVERT(_mul_add_multi, gf16_affine2x_muladd_multi_avx10); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_affine2x_muladd_multi_stridepf_avx10); _mul_add_multi_packed = &gf16_affine2x_muladd_multi_packed_avx10; _mul_add_multi_packpf = &gf16_affine2x_muladd_multi_packpf_avx10; SET_BASIC_OP(add_multi, gf_add_multi_avx2); #ifdef PLATFORM_AMD64 SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i12_avx10); add_multi_packpf = &gf_add_multi_packpf_v1i12_avx10; #else SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i2_avx2); add_multi_packpf = &gf_add_multi_packpf_v1i2_avx2; #endif SET_FOR_INVERT(prepare, gf16_affine2x_prepare_avx2); SET_BASIC_OP(prepare_packed, gf16_affine2x_prepare_packed_avx10); prepare_packed_cksum = &gf16_affine2x_prepare_packed_cksum_avx10; prepare_partial_packsum = &gf16_affine2x_prepare_partial_packsum_avx10; SET_FOR_INVERT(finish, gf16_affine2x_finish_avx2); SET_BASIC_OP(finish_packed, gf16_affine2x_finish_packed_avx2); finish_packed_cksum = &gf16_affine2x_finish_packed_cksum_avx2; finish_partial_packsum = &gf16_affine2x_finish_partial_packsum_avx2; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_avx2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_avx2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_avx2); SET_FOR_INVERT(replace_word, gf16_affine2x_replace_word); break; case GF16_AFFINE2X_AVX2: scratch = gf16_affine_init_avx2(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_affine_available_avx2 && gf16_shuffle_available_avx2, 1) SET_FOR_INVERT(_mul, gf16_affine2x_mul_avx2); _mul_add = &gf16_affine2x_muladd_avx2; SET_FOR_INVERT(_mul_add_multi, gf16_affine2x_muladd_multi_avx2); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_affine2x_muladd_multi_stridepf_avx2); _mul_add_multi_packed = &gf16_affine2x_muladd_multi_packed_avx2; _mul_add_multi_packpf = &gf16_affine2x_muladd_multi_packpf_avx2; SET_BASIC_OP(add_multi, gf_add_multi_avx2); #ifdef PLATFORM_AMD64 SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i6_avx2); add_multi_packpf = &gf_add_multi_packpf_v1i6_avx2; #else SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i2_avx2); add_multi_packpf = &gf_add_multi_packpf_v1i2_avx2; #endif SET_FOR_INVERT(prepare, gf16_affine2x_prepare_avx2); SET_BASIC_OP(prepare_packed, gf16_affine2x_prepare_packed_avx2); prepare_packed_cksum = &gf16_affine2x_prepare_packed_cksum_avx2; prepare_partial_packsum = &gf16_affine2x_prepare_partial_packsum_avx2; SET_FOR_INVERT(finish, gf16_affine2x_finish_avx2); SET_BASIC_OP(finish_packed, gf16_affine2x_finish_packed_avx2); finish_packed_cksum = &gf16_affine2x_finish_packed_cksum_avx2; finish_partial_packsum = &gf16_affine2x_finish_partial_packsum_avx2; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_avx2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_avx2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_avx2); SET_FOR_INVERT(replace_word, gf16_affine2x_replace_word); break; case GF16_AFFINE2X_GFNI: scratch = gf16_affine_init_gfni(GF16_POLYNOMIAL); METHOD_REQUIRES(gf16_affine_available_gfni && gf16_shuffle_available_ssse3, 1) SET_FOR_INVERT(_mul, gf16_affine2x_mul_gfni); _mul_add = &gf16_affine2x_muladd_gfni; SET_FOR_INVERT(_mul_add_multi, gf16_affine2x_muladd_multi_gfni); SET_FOR_INVERT(_mul_add_multi_stridepf, gf16_affine2x_muladd_multi_stridepf_gfni); _mul_add_multi_packed = &gf16_affine2x_muladd_multi_packed_gfni; _mul_add_multi_packpf = &gf16_affine2x_muladd_multi_packpf_gfni; SET_BASIC_OP(add_multi, gf_add_multi_sse2); #ifdef PLATFORM_AMD64 SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i6_sse2); add_multi_packpf = &gf_add_multi_packpf_v1i6_sse2; #else SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v1i2_sse2); add_multi_packpf = &gf_add_multi_packpf_v1i2_sse2; #endif SET_FOR_INVERT(prepare, gf16_affine2x_prepare_gfni); SET_BASIC_OP(prepare_packed, gf16_affine2x_prepare_packed_gfni); prepare_packed_cksum = &gf16_affine2x_prepare_packed_cksum_gfni; prepare_partial_packsum = &gf16_affine2x_prepare_partial_packsum_gfni; SET_FOR_INVERT(finish, gf16_affine2x_finish_gfni); SET_BASIC_OP(finish_packed, gf16_affine2x_finish_packed_gfni); finish_packed_cksum = &gf16_affine2x_finish_packed_cksum_gfni; finish_partial_packsum = &gf16_affine2x_finish_partial_packsum_gfni; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_sse2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_sse2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_sse2); SET_FOR_INVERT(replace_word, gf16_affine2x_replace_word); break; case GF16_XOR_JIT_AVX512: case GF16_XOR_JIT_AVX2: //case GF16_XOR_JIT_AVX: case GF16_XOR_JIT_SSE2: case GF16_XOR_SSE2: { #ifdef PLATFORM_X86 int jitOptStrat = GF16CpuCap(true).jitOptStrat; switch(method) { case GF16_XOR_JIT_SSE2: case GF16_XOR_SSE2: METHOD_REQUIRES(gf16_xor_available_sse2, 1) if(method == GF16_XOR_SSE2) { scratch = gf16_xor_init_sse2(GF16_POLYNOMIAL); SET_FOR_INVERT(_mul, gf16_xor_mul_sse2); _mul_add = &gf16_xor_muladd_sse2; } else { scratch = gf16_xor_jit_init_sse2(GF16_POLYNOMIAL, jitOptStrat); SET_FOR_INVERT(_mul, gf16_xor_jit_mul_sse2); _mul_add = &gf16_xor_jit_muladd_sse2; _mul_add_pf = &gf16_xor_jit_muladd_prefetch_sse2; } SET_BASIC_OP(add_multi, gf_add_multi_sse2); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v16i1_sse2); add_multi_packpf = &gf_add_multi_packpf_v16i1_sse2; SET_FOR_INVERT(prepare, gf16_xor_prepare_sse2); SET_BASIC_OP(prepare_packed, gf16_xor_prepare_packed_sse2); prepare_packed_cksum = &gf16_xor_prepare_packed_cksum_sse2; prepare_partial_packsum = &gf16_xor_prepare_partial_packsum_sse2; SET_FOR_INVERT(finish, gf16_xor_finish_sse2); SET_BASIC_OP(finish_packed, gf16_xor_finish_packed_sse2); finish_packed_cksum = &gf16_xor_finish_packed_cksum_sse2; finish_partial_packsum = &gf16_xor_finish_partial_packsum_sse2; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_sse2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_sse2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_sse2); SET_FOR_INVERT(replace_word, gf16_xor16_replace_word); break; /* case GF16_XOR_JIT_AVX: METHOD_REQUIRES(gf16_xor_available_avx, 1) scratch = gf16_xor_jit_init_sse2(GF16_POLYNOMIAL, jitOptStrat); SET_FOR_INVERT(_mul, gf16_xor_jit_mul_avx); _mul_add = &gf16_xor_jit_muladd_avx; _mul_add_pf = &gf16_xor_jit_muladd_prefetch_avx; SET_BASIC_OP(add_multi, gf_add_multi_sse2); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v16i1_sse2); add_multi_packpf = &gf_add_multi_packpf_v16i1_sse2; SET_FOR_INVERT(prepare, gf16_xor_prepare_avx); SET_BASIC_OP(prepare_packed, gf16_xor_prepare_packed_avx); prepare_packed_cksum = &gf16_xor_prepare_packed_cksum_avx; prepare_partial_packsum = &gf16_xor_prepare_partial_packsum_avx; SET_FOR_INVERT(finish, gf16_xor_finish_avx); SET_BASIC_OP(finish_packed, gf16_xor_finish_packed_avx); finish_packed_cksum = &gf16_xor_finish_packed_cksum_avx; finish_partial_packsum = &gf16_xor_finish_partial_packsum_avx; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_sse2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_sse2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_sse2); SET_FOR_INVERT(replace_word, gf16_xor16_replace_word); break; */ case GF16_XOR_JIT_AVX2: METHOD_REQUIRES(gf16_xor_available_avx2, 1) #ifdef PLATFORM_AMD64 scratch = gf16_xor_jit_init_avx2(GF16_POLYNOMIAL, jitOptStrat); SET_FOR_INVERT(_mul, gf16_xor_jit_mul_avx2); _mul_add = &gf16_xor_jit_muladd_avx2; _mul_add_pf = &gf16_xor_jit_muladd_prefetch_avx2; SET_BASIC_OP(add_multi, gf_add_multi_avx2); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v16i1_avx2); add_multi_packpf = &gf_add_multi_packpf_v16i1_avx2; SET_FOR_INVERT(prepare, gf16_xor_prepare_avx2); SET_BASIC_OP(prepare_packed, gf16_xor_prepare_packed_avx2); prepare_packed_cksum = &gf16_xor_prepare_packed_cksum_avx2; prepare_partial_packsum = &gf16_xor_prepare_partial_packsum_avx2; SET_FOR_INVERT(finish, gf16_xor_finish_avx2); SET_BASIC_OP(finish_packed, gf16_xor_finish_packed_avx2); finish_packed_cksum = &gf16_xor_finish_packed_cksum_avx2; finish_partial_packsum = &gf16_xor_finish_partial_packsum_avx2; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_avx2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_avx2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_avx2); SET_FOR_INVERT(replace_word, gf16_xor32_replace_word); #endif break; case GF16_XOR_JIT_AVX512: #ifdef PLATFORM_AMD64 METHOD_REQUIRES(gf16_xor_available_avx512, 1) scratch = gf16_xor_jit_init_avx512(GF16_POLYNOMIAL, jitOptStrat); SET_FOR_INVERT(_mul, gf16_xor_jit_mul_avx512); _mul_add = &gf16_xor_jit_muladd_avx512; _mul_add_pf = &gf16_xor_jit_muladd_prefetch_avx512; SET_FOR_INVERT(_mul_add_multi, gf16_xor_jit_muladd_multi_avx512); _mul_add_multi_packed = &gf16_xor_jit_muladd_multi_packed_avx512; SET_BASIC_OP(add_multi, gf_add_multi_avx512); SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_v16i6_avx512); add_multi_packpf = &gf_add_multi_packpf_v16i6_avx512; SET_FOR_INVERT(prepare, gf16_xor_prepare_avx512); SET_BASIC_OP(prepare_packed, gf16_xor_prepare_packed_avx512); prepare_packed_cksum = &gf16_xor_prepare_packed_cksum_avx512; prepare_partial_packsum = &gf16_xor_prepare_partial_packsum_avx512; SET_FOR_INVERT(finish, gf16_xor_finish_avx512); SET_BASIC_OP(finish_packed, gf16_xor_finish_packed_avx512); finish_packed_cksum = &gf16_xor_finish_packed_cksum_avx512; finish_partial_packsum = &gf16_xor_finish_partial_packsum_avx512; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_avx512); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_avx512); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_avx512); SET_FOR_INVERT(replace_word, gf16_xor64_replace_word); #endif break; default: break; // for pedantic compilers } #else METHOD_REQUIRES(1, 0) #endif } break; case GF16_LOOKUP_SSE2: SET_FOR_INVERT(_mul, gf16_lookup_mul_sse2); _mul_add = &gf16_lookup_muladd_sse2; prepare_packed_cksum = &gf16_lookup_prepare_packed_cksum_sse2; prepare_partial_packsum = &gf16_lookup_prepare_partial_packsum_sse2; SET_BASIC_OP(finish_packed, gf16_lookup_finish_packed_sse2); finish_packed_cksum = &gf16_lookup_finish_packed_cksum_sse2; finish_partial_packsum = &gf16_lookup_finish_partial_packsum_sse2; SET_FOR_OPENCL(copy_cksum, gf16_cksum_copy_sse2); SET_FOR_OPENCL(copy_cksum_check, gf16_cksum_copy_check_sse2); SET_FOR_OPENCL(finish_grp2_cksum, gf16_grp2_finish_sse2); break; #ifndef PARPAR_SLIM_GF16 case GF16_LOOKUP3: SET_FOR_INVERT(_mul, gf16_lookup3_mul); _mul_add = &gf16_lookup3_muladd; _mul_add_multi_packed = &gf16_lookup3_muladd_multi_packed; SET_BASIC_OP(add_multi_packed, gf_add_multi_packed_lookup3); add_multi_packpf = &gf_add_multi_packpf_lookup3; SET_BASIC_OP(prepare_packed, gf16_lookup3_prepare_packed_generic); prepare_packed_cksum = &gf16_lookup3_prepare_packed_cksum_generic; prepare_partial_packsum = &gf16_lookup3_prepare_partial_packsum_generic; SET_BASIC_OP(finish_packed, gf16_lookup_finish_packed_generic); finish_packed_cksum = &gf16_lookup_finish_packed_cksum_generic; finish_partial_packsum = &gf16_lookup_finish_partial_packsum_generic; if(gf16_lookup3_stride()) break; #endif // fallthrough case GF16_LOOKUP: default: SET_FOR_INVERT(_mul, gf16_lookup_mul); _mul_add = &gf16_lookup_muladd; #ifdef PARPAR_POW_SUPPORT _pow_add = &gf16_lookup_powadd; #endif prepare_packed_cksum = &gf16_lookup_prepare_packed_cksum_generic; prepare_partial_packsum = &gf16_lookup_prepare_partial_packsum_generic; SET_BASIC_OP(finish_packed, gf16_lookup_finish_packed_generic); finish_packed_cksum = &gf16_lookup_finish_packed_cksum_generic; finish_partial_packsum = &gf16_lookup_finish_partial_packsum_generic; break; } #undef METHOD_REQUIRES #undef SET_FOR_INVERT #undef SET_BASIC_OP #undef SET_FOR_OPENCL _info = info(method); } Galois16Mul::Galois16Mul(Galois16Methods method) { scratch = NULL; #ifdef PARPAR_INVERT_SUPPORT prepare = &Galois16Mul::_prepare_none; finish = &Galois16Mul::_finish_none; replace_word = &Galois16Mul::_replace_word; #endif _mul_add_pf = NULL; #ifdef PARPAR_INCLUDE_BASIC_OPS prepare_packed = &Galois16Mul::_prepare_packed_none; finish_packed = NULL; add_multi = &gf_add_multi_generic; add_multi_packed = &gf_add_multi_packed_generic; #endif add_multi_packpf = &gf_add_multi_packpf_generic; #ifdef PARPAR_INVERT_SUPPORT _mul_add_multi = NULL; _mul_add_multi_stridepf = NULL; #endif _mul_add_multi_packed = NULL; _mul_add_multi_packpf = NULL; #ifdef PARPAR_OPENCL_SUPPORT copy_cksum = &gf16_cksum_copy_generic; copy_cksum_check = &gf16_cksum_copy_check_generic; finish_grp2_cksum = &gf16_grp2_finish_generic; #endif #ifdef PARPAR_POW_SUPPORT _pow = NULL; _pow_add = NULL; #endif setupMethod(method); } Galois16Mul::~Galois16Mul() { if(scratch) ALIGN_FREE(scratch); } #ifdef __cpp_rvalue_references void Galois16Mul::move(Galois16Mul& other) { scratch = other.scratch; other.scratch = NULL; #ifdef PARPAR_INVERT_SUPPORT prepare = other.prepare; finish = other.finish; replace_word = other.replace_word; #endif #ifdef PARPAR_INCLUDE_BASIC_OPS prepare_packed = other.prepare_packed; finish_packed = other.finish_packed; add_multi = other.add_multi; add_multi_packed = other.add_multi_packed; #endif prepare_packed_cksum = other.prepare_packed_cksum; prepare_partial_packsum = other.prepare_partial_packsum; finish_packed_cksum = other.finish_packed_cksum; finish_partial_packsum = other.finish_partial_packsum; _info = other._info; add_multi_packpf = other.add_multi_packpf; _mul_add = other._mul_add; _mul_add_pf = other._mul_add_pf; #ifdef PARPAR_INVERT_SUPPORT _mul = other._mul; _mul_add_multi = other._mul_add_multi; _mul_add_multi_stridepf = other._mul_add_multi_stridepf; #endif _mul_add_multi_packed = other._mul_add_multi_packed; _mul_add_multi_packpf = other._mul_add_multi_packpf; #ifdef PARPAR_POW_SUPPORT _pow = other._pow; _pow_add = other._pow_add; #endif #ifdef PARPAR_OPENCL_SUPPORT copy_cksum = other.copy_cksum; copy_cksum_check = other.copy_cksum_check; finish_grp2_cksum = other.finish_grp2_cksum; #endif } #endif void* Galois16Mul::mutScratch_alloc() const { switch(_info.id) { case GF16_XOR_JIT_SSE2: return gf16_xor_jit_init_mut_sse2(); case GF16_XOR_JIT_AVX2: return gf16_xor_jit_init_mut_avx2(); case GF16_XOR_JIT_AVX512: return gf16_xor_jit_init_mut_avx512(); break; default: return NULL; } } void Galois16Mul::mutScratch_free(void* mutScratch) const { switch(_info.id) { case GF16_XOR_JIT_SSE2: case GF16_XOR_JIT_AVX2: case GF16_XOR_JIT_AVX512: gf16_xor_jit_uninit(mutScratch); break; default: break; } } Galois16Methods Galois16Mul::default_method(size_t regionSizeHint, unsigned inputs, unsigned /*outputs*/, bool forInvert) { (void)regionSizeHint; (void)inputs; (void)forInvert; #ifdef PLATFORM_X86 const GF16CpuCap caps(true); if(caps.hasGFNI) { if(gf16_affine_available_avx512 && caps.hasAVX512VLBW) return GF16_AFFINE_AVX512; if(gf16_affine_available_avx10 && caps.hasAVX10) return GF16_AFFINE_AVX10; if(gf16_affine_available_avx2 && caps.hasAVX2) return GF16_AFFINE_AVX2; } if(caps.hasAVX512VLBW) { if(gf16_shuffle_available_vbmi && caps.hasAVX512VBMI) return GF16_SHUFFLE_VBMI; if(gf16_shuffle_available_avx512) return GF16_SHUFFLE_AVX512; } if(caps.hasAVX2) { # ifdef PLATFORM_AMD64 if(gf16_xor_available_avx2 && caps.canMemWX && caps.propFastJit && !caps.isEmulated && !forInvert) // TODO: check size hint? return GF16_XOR_JIT_AVX2; # endif if(gf16_shuffle_available_avx2) return GF16_SHUFFLE_AVX2; } if(gf16_affine_available_gfni && caps.hasGFNI && gf16_shuffle_available_ssse3 && caps.hasSSSE3) return GF16_AFFINE_GFNI; // this should beat XOR-JIT; even seems to generally beat Shuffle2x AVX2 if(!caps.isEmulated && regionSizeHint > caps.propPrefShuffleThresh && !forInvert) { // TODO: if only a few recovery slices being made (e.g. 3), prefer shuffle //if(gf16_xor_available_avx && caps.hasAVX && caps.canMemWX) // return GF16_XOR_JIT_AVX; if(gf16_xor_available_sse2 && caps.hasSSE2 && caps.canMemWX) return GF16_XOR_JIT_SSE2; } if(gf16_shuffle_available_avx && caps.hasAVX) return GF16_SHUFFLE_AVX; if(gf16_shuffle_available_ssse3 && caps.hasSSSE3) return GF16_SHUFFLE_SSSE3; if(gf16_xor_available_sse2 && caps.hasSSE2) return GF16_XOR_SSE2; #endif #ifdef PLATFORM_ARM const GF16CpuCap caps(true); if(gf16_available_sve2 && caps.hasSVE2) { // preferred technique seems to vary across cores // Cortex A510: prefers CLMul SHA3, and CLMul over Shuffle // Cortex A710: prefers CLMul NEON > Shuffle SVE2 > CLMul SVE2 > CLMul SHA3 > Shuffle SVE > Shuffle NEON // Cortex X2: prefers Shuffle SVE2 > CLMul SVE2 > CLMul SHA3 > Shuffle NEON > Shuffle SVE > CLMul NEON # ifdef PARPAR_SLIM_GF16 return GF16_CLMUL_SVE2; # else if(gf16_sve_get_size() >= 64) return GF16_SHUFFLE_512_SVE2; return inputs > 3 ? GF16_CLMUL_SVE2 : GF16_SHUFFLE_128_SVE2; # endif } if(gf16_available_sve && caps.hasSVE && gf16_sve_get_size() > 16) return GF16_SHUFFLE_128_SVE; # ifdef __aarch64__ if(gf16_available_neon_sha3 && caps.hasSHA3) # if PARPAR_SLIM_GF16 return GF16_CLMUL_SHA3; # else return inputs > 3 ? GF16_CLMUL_SHA3 : GF16_SHUFFLE_NEON; # endif # endif if(gf16_available_neon && caps.hasNEON) return # if PARPAR_SLIM_GF16 GF16_CLMUL_NEON; # else # ifdef __aarch64__ inputs > 3 # else inputs > 1 # endif ? GF16_CLMUL_NEON : GF16_SHUFFLE_NEON; # endif #endif #ifdef __riscv const GF16CpuCap caps(true); if(caps.hasVector && gf16_available_rvv && gf16_rvv_get_size() >= 16) { if(caps.hasZvbc && gf16_available_rvv_zvbc) return GF16_CLMUL_RVV; return GF16_SHUFFLE_128_RVV; } #endif // lookup vs lookup3: latter seems to be slightly faster than former in most cases (SKX, Silvermont, Zen1, Rpi3 (arm64; arm32 faster muladd, slower mul)), sometimes slightly slower (Haswell, IvB?, Piledriver) // but test w/ multi-region lh-lookup & fat table before preferring it return GF16_LOOKUP; } std::vector Galois16Mul::availableMethods(bool checkCpuid) { UNUSED(checkCpuid); std::vector ret; ret.push_back(GF16_LOOKUP); #if !defined(PARPAR_SLIM_GF16) if(gf16_lookup3_stride()) ret.push_back(GF16_LOOKUP3); #endif #ifdef PLATFORM_X86 const GF16CpuCap caps(checkCpuid); if(gf16_shuffle_available_ssse3 && caps.hasSSSE3) ret.push_back(GF16_SHUFFLE_SSSE3); if(gf16_shuffle_available_avx && caps.hasAVX) ret.push_back(GF16_SHUFFLE_AVX); if(gf16_shuffle_available_avx2 && caps.hasAVX2) { ret.push_back(GF16_SHUFFLE_AVX2); #if !defined(PARPAR_SLIM_GF16) ret.push_back(GF16_SHUFFLE2X_AVX2); #endif } if(gf16_shuffle_available_avx512 && caps.hasAVX512VLBW) { ret.push_back(GF16_SHUFFLE_AVX512); #if !defined(PARPAR_SLIM_GF16) ret.push_back(GF16_SHUFFLE2X_AVX512); #endif } if(gf16_shuffle_available_vbmi && caps.hasAVX512VBMI) { ret.push_back(GF16_SHUFFLE_VBMI); } if(caps.hasGFNI) { if(gf16_affine_available_gfni && gf16_shuffle_available_ssse3 && caps.hasSSSE3) { ret.push_back(GF16_AFFINE_GFNI); #if !defined(PARPAR_SLIM_GF16) ret.push_back(GF16_AFFINE2X_GFNI); #endif } if(gf16_affine_available_avx2 && gf16_shuffle_available_avx2 && caps.hasAVX2) { ret.push_back(GF16_AFFINE_AVX2); #if !defined(PARPAR_SLIM_GF16) ret.push_back(GF16_AFFINE2X_AVX2); #endif } if(gf16_affine_available_avx512 && gf16_shuffle_available_avx512 && caps.hasAVX512VLBW) { ret.push_back(GF16_AFFINE_AVX512); #if !defined(PARPAR_SLIM_GF16) ret.push_back(GF16_AFFINE2X_AVX512); #endif } if(gf16_affine_available_avx10 && gf16_shuffle_available_avx2 && (caps.hasAVX10 || caps.hasAVX512VBMI)) { ret.push_back(GF16_AFFINE_AVX10); #if !defined(PARPAR_SLIM_GF16) ret.push_back(GF16_AFFINE2X_AVX10); #endif } } if(gf16_xor_available_sse2 && caps.hasSSE2) { ret.push_back(GF16_XOR_SSE2); #if !defined(PARPAR_SLIM_GF16) ret.push_back(GF16_LOOKUP_SSE2); #endif } if(caps.canMemWX) { if(gf16_xor_available_sse2 && caps.hasSSE2) ret.push_back(GF16_XOR_JIT_SSE2); //if(gf16_xor_available_avx && caps.hasAVX) // ret.push_back(GF16_XOR_JIT_AVX); if(gf16_xor_available_avx2 && caps.hasAVX2) ret.push_back(GF16_XOR_JIT_AVX2); if(gf16_xor_available_avx512 && caps.hasAVX512VLBW) ret.push_back(GF16_XOR_JIT_AVX512); } #endif #ifdef PLATFORM_ARM const GF16CpuCap caps(checkCpuid); if(gf16_available_neon && caps.hasNEON) { #if !defined(PARPAR_SLIM_GF16) ret.push_back(GF16_SHUFFLE_NEON); #endif ret.push_back(GF16_CLMUL_NEON); } if(gf16_available_neon_sha3 && caps.hasSHA3) { ret.push_back(GF16_CLMUL_SHA3); } if(gf16_available_sve && caps.hasSVE) ret.push_back(GF16_SHUFFLE_128_SVE); if(gf16_available_sve2 && caps.hasSVE2) { ret.push_back(GF16_CLMUL_SVE2); #if !defined(PARPAR_SLIM_GF16) ret.push_back(GF16_SHUFFLE_128_SVE2); if(gf16_sve_get_size() >= 32) ret.push_back(GF16_SHUFFLE2X_128_SVE2); if(gf16_sve_get_size() >= 64) ret.push_back(GF16_SHUFFLE_512_SVE2); #endif } #endif #ifdef __riscv const GF16CpuCap caps(checkCpuid); if(gf16_available_rvv && caps.hasVector && gf16_rvv_get_size() >= 16) { if(caps.hasZvbc && gf16_available_rvv_zvbc) ret.push_back(GF16_CLMUL_RVV); ret.push_back(GF16_SHUFFLE_128_RVV); } #endif return ret; } #ifdef PARPAR_INCLUDE_BASIC_OPS void Galois16Mul::_prepare_packed_none(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen) { ASSUME(inputNum < inputPackSize); ASSUME(srcLen <= sliceLen); ASSUME(chunkLen <= sliceLen); uint8_t* dstBase = (uint8_t*)dst + inputNum * chunkLen; unsigned fullChunks = (unsigned)(srcLen/chunkLen); size_t chunkStride = chunkLen * inputPackSize; for(unsigned chunk=0; chunk (sliceLen/chunkLen) * chunkLen) { // if this is the last chunk, the length may be shorter lastChunkLen = sliceLen % chunkLen; // this will be block aligned, as both sliceLen and chunkLen must be block aligned if(lastChunkLen == 0) lastChunkLen = chunkLen; // if sliceLen is divisible by chunkLen, the last chunk will be chunkLen } uint8_t* dstPtr = (uint8_t*)dst + chunkStride * fullChunks + lastChunkLen*inputNum; memcpy(dstPtr, (uint8_t*)src + chunkLen*fullChunks, remaining); memset(dstPtr + remaining, 0, lastChunkLen - remaining); if(lastChunkLen != chunkLen) return; // we processed an unevenly sized last chunk = we're done (we may be done otherwise, but the rest of the code below handles that) fullChunks++; } // zero fill remaining full blocks unsigned sliceFullChunks = (unsigned)(sliceLen/chunkLen); for(unsigned chunk=fullChunks; chunk #include "../src/stdint.h" #include "../src/hedley.h" #include #include typedef void(*Galois16MulTransform) (void* dst, const void* src, size_t srcLen); typedef void(*Galois16MulTransformPacked) (void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); typedef void(*Galois16MulTransformPackedPartial) (void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen, size_t partOffset, size_t partLen); typedef void(*Galois16MulUntransform) (void *HEDLEY_RESTRICT dst, size_t len); typedef void(*Galois16MulUntransformPacked) (void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen); typedef int(*Galois16MulUntransformPackedCksum) (void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen); typedef int(*Galois16MulUntransformPackedCksumPartial) (void *HEDLEY_RESTRICT dst, void *HEDLEY_RESTRICT src, size_t sliceLen, unsigned numOutputs, unsigned outputNum, size_t chunkLen, size_t partOffset, size_t partLen); typedef uint16_t(*Galois16ReplaceWord) (void* data, size_t index, uint16_t newValue); typedef void(*Galois16MulFunc) (const void *HEDLEY_RESTRICT scratch, void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); typedef void(*Galois16MulRstFunc) (const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); typedef void(*Galois16MulPfFunc) (const void *HEDLEY_RESTRICT scratch, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch); typedef void(*Galois16PowFunc) (const void *HEDLEY_RESTRICT scratch, unsigned outputs, size_t offset, void **HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch); typedef void(*Galois16MulMultiFunc) (const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); typedef void(*Galois16MulStridePfFunc) (const void *HEDLEY_RESTRICT scratch, unsigned regions, size_t srcStride, void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch); typedef void(*Galois16MulPackedFunc) (const void *HEDLEY_RESTRICT scratch, unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch); typedef void(*Galois16MulPackPfFunc) (const void *HEDLEY_RESTRICT scratch, unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut); typedef void(*Galois16AddFunc) (void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len); typedef void(*Galois16AddMultiFunc) (unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len); typedef void(*Galois16AddPackedFunc) (unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len); typedef void(*Galois16AddPackPfFunc) (unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut); typedef void(*Galois16CopyCksum) (void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen); typedef int(*Galois16CopyCksumCheck) (void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len); typedef int(*Galois16UngrpCksumCheck) (void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, unsigned grp); enum Galois16Methods { // * = excluded if PARPAR_SLIM_GF16 // Apple builds strip out SVE kernels if PARPAR_SLIM_GF16 is defined GF16_AUTO, GF16_LOOKUP, GF16_LOOKUP_SSE2, // * GF16_LOOKUP3, // * GF16_SHUFFLE_NEON, // * GF16_SHUFFLE_128_SVE, GF16_SHUFFLE_128_SVE2, // * GF16_SHUFFLE2X_128_SVE2, // * GF16_SHUFFLE_512_SVE2, // * GF16_SHUFFLE_128_RVV, GF16_SHUFFLE_SSSE3, GF16_SHUFFLE_AVX, GF16_SHUFFLE_AVX2, GF16_SHUFFLE_AVX512, GF16_SHUFFLE_VBMI, GF16_SHUFFLE2X_AVX2, // * GF16_SHUFFLE2X_AVX512, // * GF16_XOR_SSE2, GF16_XOR_JIT_SSE2, GF16_XOR_JIT_AVX2, GF16_XOR_JIT_AVX512, // * GF16_AFFINE_GFNI, GF16_AFFINE_AVX2, GF16_AFFINE_AVX10, GF16_AFFINE_AVX512, GF16_AFFINE2X_GFNI, // * GF16_AFFINE2X_AVX2, // * GF16_AFFINE2X_AVX10, // * GF16_AFFINE2X_AVX512, // * GF16_CLMUL_NEON, GF16_CLMUL_SHA3, GF16_CLMUL_SVE2, GF16_CLMUL_RVV // TODO: consider non-transforming shuffle/affine }; static const char* Galois16MethodsText[] = { "Auto", "Lookup", "Lookup (SSE2)", "3-part Lookup", "Shuffle (NEON)", "Shuffle-128 (SVE)", "Shuffle-128 (SVE2)", "Shuffle2x-128 (SVE2)", "Shuffle-512 (SVE2)", "Shuffle-128 (RVV)", "Shuffle (SSSE3)", "Shuffle (AVX)", "Shuffle (AVX2)", "Shuffle (AVX512)", "Shuffle (VBMI)", "Shuffle2x (AVX2)", "Shuffle2x (AVX512)", "Xor (SSE2)", "Xor-Jit (SSE2)", "Xor-Jit (AVX2)", "Xor-Jit (AVX512)", "Affine (GFNI)", "Affine (GFNI+AVX2)", "Affine (GFNI+AVX10)", "Affine (GFNI+AVX512)", "Affine2x (GFNI)", "Affine2x (GFNI+AVX2)", "Affine2x (GFNI+AVX10)", "Affine2x (GFNI+AVX512)", "CLMul (NEON)", "CLMul (SHA3)", "CLMul (SVE2)", "CLMul (RVV+Zvbc)" }; typedef struct { Galois16Methods id; const char* name; size_t alignment; size_t stride; size_t idealChunkSize; unsigned idealInputMultiple; unsigned prefetchDownscale; unsigned cksumSize; } Galois16MethodInfo; class Galois16Mul { private: void* scratch; Galois16MethodInfo _info; Galois16MulRstFunc _mul_add; Galois16MulPfFunc _mul_add_pf; #ifdef PARPAR_POW_SUPPORT Galois16PowFunc _pow; Galois16PowFunc _pow_add; #endif #ifdef PARPAR_INVERT_SUPPORT Galois16MulFunc _mul; Galois16MulMultiFunc _mul_add_multi; Galois16MulStridePfFunc _mul_add_multi_stridepf; #endif Galois16MulPackedFunc _mul_add_multi_packed; Galois16MulPackPfFunc _mul_add_multi_packpf; static void _prepare_none(void* dst, const void* src, size_t srcLen) { if(dst != src) memcpy(dst, src, srcLen); } static void _finish_none(void *HEDLEY_RESTRICT, size_t) {} #ifdef PARPAR_INVERT_SUPPORT static void _prepare_packed_none(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t srcLen, size_t sliceLen, unsigned inputPackSize, unsigned inputNum, size_t chunkLen); static uint16_t _replace_word(void* data, size_t index, uint16_t newValue) { uint8_t* p = (uint8_t*)data + index*2; uint16_t oldValue = p[0] | (p[1]<<8); p[0] = newValue & 0xff; p[1] = newValue>>8; return oldValue; } #endif Galois16Methods _method; void setupMethod(Galois16Methods method); // disable copy constructor Galois16Mul(const Galois16Mul&); Galois16Mul& operator=(const Galois16Mul&); #ifdef __cpp_rvalue_references void move(Galois16Mul& other); #endif public: static Galois16Methods default_method(size_t regionSizeHint = 1048576, unsigned inputs = 32768, unsigned outputs = 65535, bool forInvert = false); Galois16Mul(Galois16Methods method = GF16_AUTO); ~Galois16Mul(); #ifdef __cpp_rvalue_references Galois16Mul(Galois16Mul&& other) noexcept { move(other); } Galois16Mul& operator=(Galois16Mul&& other) noexcept { move(other); return *this; } #endif #ifdef PARPAR_INVERT_SUPPORT inline bool needPrepare() const { return prepare != &Galois16Mul::_prepare_none; }; inline bool hasMultiMulAdd() const { return _mul_add_multi != NULL; }; #endif inline bool hasMultiMulAddPacked() const { return _mul_add_multi_packed != NULL; }; #ifdef PARPAR_POW_SUPPORT inline bool hasPowAdd() const { return _pow_add != NULL; }; #endif static std::vector availableMethods(bool checkCpuid); static inline const char* methodToText(Galois16Methods m) { return Galois16MethodsText[(int)m]; } inline const Galois16MethodInfo& info() const { return _info; } static Galois16MethodInfo info(Galois16Methods _method); inline HEDLEY_CONST bool isMultipleOfStride(size_t len) const { return (len & (_info.stride-1)) == 0; } inline HEDLEY_CONST size_t alignToStride(size_t len) const { size_t alignMask = _info.stride-1; return (len + alignMask) & ~alignMask; } #ifdef PARPAR_INVERT_SUPPORT Galois16MulTransform prepare; Galois16MulUntransform finish; Galois16ReplaceWord replace_word; #endif #ifdef PARPAR_INCLUDE_BASIC_OPS Galois16MulTransformPacked prepare_packed; Galois16MulUntransformPacked finish_packed; Galois16AddMultiFunc add_multi; Galois16AddPackedFunc add_multi_packed; #endif Galois16MulTransformPacked prepare_packed_cksum; Galois16MulTransformPackedPartial prepare_partial_packsum; // TODO: consider a nicer interface for this Galois16MulUntransformPackedCksum finish_packed_cksum; Galois16MulUntransformPackedCksumPartial finish_partial_packsum; Galois16AddPackPfFunc add_multi_packpf; #ifdef PARPAR_OPENCL_SUPPORT Galois16CopyCksum copy_cksum; Galois16CopyCksumCheck copy_cksum_check; Galois16UngrpCksumCheck finish_grp2_cksum; #endif HEDLEY_MALLOC void* mutScratch_alloc() const; void mutScratch_free(void* mutScratch) const; #ifdef PARPAR_INVERT_SUPPORT inline void mul(void* dst, const void* src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) const { assert(isMultipleOfStride(len)); assert(len > 0); if(HEDLEY_UNLIKELY(!(coefficient & 0xfffe))) { if(coefficient == 0) memset(dst, 0, len); else if(dst != src) memcpy(dst, src, len); } else _mul(scratch, dst, src, len, coefficient, mutScratch); } inline void mul_add(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) const { assert(isMultipleOfStride(len)); assert(len > 0); if(HEDLEY_UNLIKELY(coefficient == 0)) return; _mul_add(scratch, dst, src, len, coefficient, mutScratch); } inline void mul_add_pf(void *HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch, const void *HEDLEY_RESTRICT prefetch) const { assert(isMultipleOfStride(len)); assert(len > 0); if(HEDLEY_UNLIKELY(coefficient == 0)) return; if(_mul_add_pf) _mul_add_pf(scratch, dst, src, len, coefficient, mutScratch, prefetch); else _mul_add(scratch, dst, src, len, coefficient, mutScratch); } #endif #ifdef PARPAR_POW_SUPPORT inline void pow(unsigned outputs, size_t offset, void **HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) const { assert(isMultipleOfStride(len)); assert(len > 0); assert(outputs > 0); if(HEDLEY_UNLIKELY(!(coefficient & 0xfffe))) { if(coefficient == 0) { for(unsigned output = 0; output < outputs; output++) memset((uint8_t*)dst[output] + offset, 0, len); } else { for(unsigned output = 0; output < outputs; output++) memcpy((uint8_t*)dst[output] + offset, (uint8_t*)src + offset, len); } } else if(_pow) _pow(scratch, outputs, offset, dst, src, len, coefficient, mutScratch); else if(_pow_add) { for(unsigned output = 0; output < outputs; output++) memset((uint8_t*)dst[output] + offset, 0, len); _pow_add(scratch, outputs, offset, dst, src, len, coefficient, mutScratch); } else { void* prev = (uint8_t*)src + offset; for(unsigned output = 0; output < outputs; output++) { void* cur = (uint8_t*)dst[output] + offset; _mul(scratch, cur, prev, len, coefficient, mutScratch); prev = cur; } } } inline void pow_add(unsigned outputs, size_t offset, void **HEDLEY_RESTRICT dst, const void *HEDLEY_RESTRICT src, size_t len, uint16_t coefficient, void *HEDLEY_RESTRICT mutScratch) const { assert(isMultipleOfStride(len)); assert(len > 0); assert(outputs > 0); if(HEDLEY_UNLIKELY(coefficient == 0)) return; _pow_add(scratch, outputs, offset, dst, src, len, coefficient, mutScratch); } #endif #ifdef PARPAR_INVERT_SUPPORT inline void mul_add_multi(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, void *HEDLEY_RESTRICT mutScratch) const { assert(isMultipleOfStride(len)); assert(len > 0); assert(regions > 0); if(_mul_add_multi) _mul_add_multi(scratch, regions, offset, dst, src, len, coefficients, mutScratch); else { for(unsigned region = 0; region 0); assert(srcStride > 0); assert(regions > 0); if(_mul_add_multi_stridepf) { _mul_add_multi_stridepf(scratch, regions, srcStride, dst, src, len, coefficients, mutScratch, prefetch); return; } // assume _mul_add_multi isn't set (exception: XorJit AVX512) // fallback to using single multiplies unsigned region = 0; size_t pfLen = len>>_info.prefetchDownscale; const char* _pf = (const char*)prefetch; for(unsigned outputPfRounds = 1<<_info.prefetchDownscale; region 0); assert(regions > 0); if(_mul_add_multi_packed) _mul_add_multi_packed(scratch, packedRegions, regions, dst, src, len, coefficients, mutScratch); else { for(unsigned region = 0; region 0); assert(regions > 0); // TODO: mul by 1? if(_mul_add_multi_packpf) { _mul_add_multi_packpf(scratch, packedRegions, regions, dst, src, len, coefficients, mutScratch, prefetchIn, prefetchOut); return; } if(_mul_add_multi_packed || !_mul_add_pf) { // implies no support for prefetching mul_add_multi_packed(packedRegions, regions, dst, src, len, coefficients, mutScratch); return; } // do using single multiplies unsigned region = 0; size_t pfLen = len>>_info.prefetchDownscale; // firstly, prefetch output const char* _pf = (const char*)prefetchOut; for(unsigned outputPfRounds = 1<<_info.prefetchDownscale; region= 1600 _cpuidX(cpuInfoX, 7, 0); if((cpuInfo[2] & 0x1C000000) == 0x1C000000) { // has AVX + OSXSAVE + XSAVE int xcr = _GET_XCR() & 0xff; if((xcr & 6) == 6) { // AVX enabled hasAVX2 = cpuInfoX[1] & 0x20; hasVPCLMUL = hasAVX2 && (cpuInfoX[2] & 0x400); } } hasGFNI = (cpuInfoX[2] & 0x100) == 0x100; #endif if(!hasGFNI) gf16pmul_available_vpclgfni = 0; if(!hasVPCLMUL) { gf16pmul_available_vpclmul = 0; gf16pmul_available_vpclgfni = 0; } if(!hasAVX2) gf16pmul_available_avx2 = 0; if(!hasClMul) gf16pmul_available_sse = 0; if(gf16pmul_available_vpclgfni) { gf16pmul = &gf16pmul_vpclgfni; gf16pmul_method = GF16PMUL_VPCLMUL_GFNI; gf16pmul_alignment = 32; gf16pmul_blocklen = 64; } else if(gf16pmul_available_vpclmul) { gf16pmul = &gf16pmul_vpclmul; gf16pmul_method = GF16PMUL_VPCLMUL; gf16pmul_alignment = 32; gf16pmul_blocklen = 32; } else if(gf16pmul_available_avx2) { gf16pmul = &gf16pmul_avx2; gf16pmul_method = GF16PMUL_AVX2; gf16pmul_alignment = 32; gf16pmul_blocklen = 32; } else if(gf16pmul_available_sse) { gf16pmul = &gf16pmul_sse; gf16pmul_method = GF16PMUL_PCLMUL; gf16pmul_alignment = 16; gf16pmul_blocklen = 16; } #endif #ifdef PLATFORM_ARM if(!CPU_HAS_SVE2) gf16pmul_available_sve2 = 0; if(!CPU_HAS_NEON) gf16pmul_available_neon = 0; if(gf16pmul_available_sve2) { gf16pmul = &gf16pmul_sve2; gf16pmul_method = GF16PMUL_SVE2; gf16pmul_alignment = gf16pmul_sve2_width(); gf16pmul_blocklen = gf16pmul_alignment*2; } else if(gf16pmul_available_neon) { gf16pmul = &gf16pmul_neon; gf16pmul_method = GF16PMUL_NEON; gf16pmul_alignment = 16; gf16pmul_blocklen = 32; } #endif #ifdef __riscv if(!CPU_HAS_VECTOR || !CPU_HAS_GC || !CPU_HAS_Zvbc) gf16pmul_available_rvv = 0; if(gf16pmul_available_rvv) { gf16pmul = &gf16pmul_rvv; gf16pmul_method = GF16PMUL_RVV; gf16pmul_alignment = gf16pmul_rvv_width(); gf16pmul_blocklen = gf16pmul_alignment; } #endif } const char* gf16pmul_methodName(Galois16PointMulMethods method) { const char* names[] = { "None", "PCLMUL", "AVX2", "VPCLMUL", "VPCLMUL+GFNI", "NEON", "SVE2", "RVV+Zvbc" }; return names[(int)method]; } par2cmdline-turbo-1.4.0/parpar/gf16/gf16pmul.h000066400000000000000000000022231514221355600207120ustar00rootroot00000000000000#ifndef __GF16PMUL_H__ #define __GF16PMUL_H__ #include "../src/hedley.h" #include enum Galois16PointMulMethods { GF16PMUL_NONE, GF16PMUL_PCLMUL, GF16PMUL_AVX2, GF16PMUL_VPCLMUL, GF16PMUL_VPCLMUL_GFNI, GF16PMUL_NEON, GF16PMUL_SVE2, GF16PMUL_RVV }; // TODO: consider multi-dest typedef void(*Gf16PMulFunc)(void *HEDLEY_RESTRICT dst, const void* src1, const void* src2, size_t len); extern Gf16PMulFunc gf16pmul; extern Galois16PointMulMethods gf16pmul_method; extern size_t gf16pmul_alignment; extern size_t gf16pmul_blocklen; const char* gf16pmul_methodName(Galois16PointMulMethods method); inline const char* gf16pmul_methodName() { return gf16pmul_methodName(gf16pmul_method); } void setup_pmul(); HEDLEY_BEGIN_C_DECLS #define _PMUL_DECL(f) \ void gf16pmul_##f(void *HEDLEY_RESTRICT dst, const void* src1, const void* src2, size_t len); \ extern int gf16pmul_available_##f _PMUL_DECL(sse); _PMUL_DECL(avx2); _PMUL_DECL(vpclmul); _PMUL_DECL(vpclgfni); _PMUL_DECL(neon); _PMUL_DECL(sve2); _PMUL_DECL(rvv); #undef _PMUL_DECL unsigned gf16pmul_sve2_width(); unsigned gf16pmul_rvv_width(); HEDLEY_END_C_DECLS #endif // defined(__GF16PMUL_H__) par2cmdline-turbo-1.4.0/parpar/gf16/gf16pmul_avx2.c000066400000000000000000000004131514221355600216440ustar00rootroot00000000000000#include "../src/platform.h" #define _mword __m256i #define _MM(f) _mm256_ ## f #define _MMI(f) _mm256_ ## f ## _si256 #define MWORD_SIZE 32 #define _FNSUFFIX _avx2 #if defined(__PCLMUL__) && defined(__AVX2__) # define _AVAILABLE 1 #endif #include "gf16pmul_x86.h" par2cmdline-turbo-1.4.0/parpar/gf16/gf16pmul_neon.c000066400000000000000000000036431514221355600217330ustar00rootroot00000000000000#include "gf16_global.h" #ifdef __ARM_NEON # define _AVAILABLE #endif #include "gf16_clmul_neon.h" #ifdef __ARM_NEON # undef _AVAILABLE int gf16pmul_available_neon = 1; void gf16pmul_neon(void *HEDLEY_RESTRICT dst, const void* src1, const void* src2, size_t len) { assert(len % sizeof(uint8x16_t)*2 == 0); const poly8_t* _src1 = (const poly8_t*)src1 + len; const poly8_t* _src2 = (const poly8_t*)src2 + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(uint8x16_t)*2) { poly8x16x2_t data1 = vld2q_p8(_src1+ptr); poly8x16x2_t data2 = vld2q_p8(_src2+ptr); poly16x8_t low1 = vmull_p8(vget_low_p8(data1.val[0]), vget_low_p8(data2.val[0])); poly8x16_t dataMid1 = veorq_p8(data1.val[0], data1.val[1]); poly8x16_t dataMid2 = veorq_p8(data2.val[0], data2.val[1]); poly16x8_t mid1 = vmull_p8(vget_low_p8(dataMid1), vget_low_p8(dataMid2)); poly16x8_t high1 = vmull_p8(vget_low_p8(data1.val[1]), vget_low_p8(data2.val[1])); #ifdef __aarch64__ poly16x8_t low2 = pmull_high(data1.val[0], data2.val[0]); poly16x8_t mid2 = pmull_high(dataMid1, dataMid2); poly16x8_t high2 = pmull_high(data1.val[1], data2.val[1]); #else poly16x8_t low2 = vmull_p8(vget_high_p8(data1.val[0]), vget_high_p8(data2.val[0])); poly16x8_t mid2 = vmull_p8(vget_high_p8(dataMid1), vget_high_p8(dataMid2)); poly16x8_t high2 = vmull_p8(vget_high_p8(data1.val[1]), vget_high_p8(data2.val[1])); #endif gf16_clmul_neon_reduction(&low1, &low2, mid1, mid2, &high1, &high2); uint8x16x2_t out; out.val[0] = veorq_u8(vreinterpretq_u8_p16(low1), vreinterpretq_u8_p16(low2)); out.val[1] = veorq_u8(vreinterpretq_u8_p16(high1), vreinterpretq_u8_p16(high2)); vst2q_u8(_dst+ptr, out); } } #else // defined(__ARM_NEON) int gf16pmul_available_neon = 0; void gf16pmul_neon(void *HEDLEY_RESTRICT dst, const void* src1, const void* src2, size_t len) { UNUSED(dst); UNUSED(src1); UNUSED(src2); UNUSED(len); } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16pmul_rvv.c000066400000000000000000000046561514221355600216160ustar00rootroot00000000000000#include "gf16_global.h" #include "gf16_clmul_rvv.h" #ifdef RISCV_ZVBC_INTRIN int gf16pmul_available_rvv = 1; void gf16pmul_rvv(void *HEDLEY_RESTRICT dst, const void* src1, const void* src2, size_t len) { size_t vl = RV(vsetvlmax_e8m1)(); assert(len % vl == 0); const uint8_t* _src1 = (const uint8_t*)src1 + len; const uint8_t* _src2 = (const uint8_t*)src2 + len; uint8_t* _dst = (uint8_t*)dst + len; #if defined(__riscv_v_intrinsic) && __riscv_v_intrinsic >= 12000 vbool32_t alt = RV(vreinterpret_b32)(RV(vmv_v_x_u8m1)(0xaa, vl)); #else vuint8m1_t altTmp = RV(vmv_v_x_u8m1)(0xaa, vl); vbool32_t alt = *(vbool32_t*)(&altTmp); #endif // TODO: consider using LMUL=2 for(intptr_t ptr = -(intptr_t)len; ptr; ptr += vl) { vuint32m1_t s1 = RV(vle32_v_u32m1)((const uint32_t*)(_src1+ptr), vl); vuint32m1_t s2 = RV(vle32_v_u32m1)((const uint32_t*)(_src2+ptr), vl); vuint64m1_t tmp1 = RV(vreinterpret_v_u32m1_u64m1)(RV(vand_vx_u32m1)(s1, 0xffff, vl)); vuint64m1_t tmp2 = RV(vreinterpret_v_u32m1_u64m1)(RV(vand_vx_u32m1)(s2, 0xffff, vl)); vuint32m1_t raa = RV(vreinterpret_v_u64m1_u32m1)(RV(vclmul_vv_u64m1)(tmp1, tmp2, vl)); vuint32m1_t rab = RV(vreinterpret_v_u64m1_u32m1)(RV(vclmul_vv_u64m1)(RV(vsrl_vx_u64m1)(tmp1, 32, vl), tmp2, vl)); tmp1 = RV(vreinterpret_v_u32m1_u64m1)(RV(vsrl_vx_u32m1)(s1, 16, vl)); tmp2 = RV(vreinterpret_v_u32m1_u64m1)(RV(vsrl_vx_u32m1)(s2, 16, vl)); vuint32m1_t rba = RV(vreinterpret_v_u64m1_u32m1)(RV(vclmul_vv_u64m1)(tmp1, tmp2, vl)); vuint32m1_t rbb = RV(vreinterpret_v_u64m1_u32m1)(RV(vclmul_vv_u64m1)(RV(vsrl_vx_u64m1)(tmp1, 32, vl), tmp2, vl)); #ifdef __riscv_v_intrinsic vuint64m1_t ra = RV(vreinterpret_v_u32m1_u64m1)(RV(vmerge_vvm_u32m1)(raa, rab, alt, vl)); vuint64m1_t rb = RV(vreinterpret_v_u32m1_u64m1)(RV(vmerge_vvm_u32m1)(rba, rbb, alt, vl)); #else vuint64m1_t ra = RV(vreinterpret_v_u32m1_u64m1)(RV(vmerge_vvm_u32m1)(alt, raa, rab, vl)); vuint64m1_t rb = RV(vreinterpret_v_u32m1_u64m1)(RV(vmerge_vvm_u32m1)(alt, rba, rbb, vl)); #endif vuint16m1_t res = gf16_clmul_rvv_reduction(ra, rb, vl); RV(vse16_v_u16m1)((uint16_t*)(_dst+ptr), res, vl); } } unsigned gf16pmul_rvv_width() { return RV(vsetvlmax_e8m1)(); } #else // defined(RISCV_ZVBC_INTRIN) int gf16pmul_available_rvv = 0; void gf16pmul_rvv(void *HEDLEY_RESTRICT dst, const void* src1, const void* src2, size_t len) { UNUSED(dst); UNUSED(src1); UNUSED(src2); UNUSED(len); } unsigned gf16pmul_rvv_width() { return 1; } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16pmul_sse.c000066400000000000000000000004341514221355600215610ustar00rootroot00000000000000#include "../src/platform.h" #define _mword __m128i #define _MM(f) _mm_ ## f #define _MMI(f) _mm_ ## f ## _si128 #define MWORD_SIZE 16 #define _FNSUFFIX _sse #if defined(__PCLMUL__) && defined(__SSSE3__) && defined(__SSE4_1__) # define _AVAILABLE 1 #endif #include "gf16pmul_x86.h" par2cmdline-turbo-1.4.0/parpar/gf16/gf16pmul_sve2.c000066400000000000000000000032221514221355600216440ustar00rootroot00000000000000#include "gf16_global.h" #include "gf16_clmul_sve2.h" #ifdef __ARM_FEATURE_SVE2 int gf16pmul_available_sve2 = 1; void gf16pmul_sve2(void *HEDLEY_RESTRICT dst, const void* src1, const void* src2, size_t len) { assert(len % svcntb()*2 == 0); const uint8_t* _src1 = (const uint8_t*)src1 + len; const uint8_t* _src2 = (const uint8_t*)src2 + len; uint8_t* _dst = (uint8_t*)dst + len; for(intptr_t ptr = -(intptr_t)len; ptr; ptr += svcntb()*2) { svuint8x2_t data1 = svld2_u8(svptrue_b8(), _src1+ptr); svuint8x2_t data2 = svld2_u8(svptrue_b8(), _src2+ptr); svuint8_t low1 = svpmullb_pair_u8(svget2(data1, 0), svget2(data2, 0)); svuint8_t low2 = svpmullt_pair_u8(svget2(data1, 0), svget2(data2, 0)); svuint8_t dataMid1 = NOMASK(sveor_u8, svget2(data1, 0), svget2(data1, 1)); svuint8_t dataMid2 = NOMASK(sveor_u8, svget2(data2, 0), svget2(data2, 1)); svuint8_t mid1 = svpmullb_pair_u8(dataMid1, dataMid2); svuint8_t mid2 = svpmullt_pair_u8(dataMid1, dataMid2); svuint8_t high1 = svpmullb_pair_u8(svget2(data1, 1), svget2(data2, 1)); svuint8_t high2 = svpmullt_pair_u8(svget2(data1, 1), svget2(data2, 1)); gf16_clmul_sve2_reduction(&low1, &low2, mid1, mid2, &high1, &high2); low1 = NOMASK(sveor_u8, low1, low2); high1 = NOMASK(sveor_u8, high1, high2); svst2_u8(svptrue_b8(), _dst+ptr, svcreate2_u8(low1, high1)); } } unsigned gf16pmul_sve2_width() { return svcntb(); } #else // defined(__ARM_FEATURE_SVE2) int gf16pmul_available_sve2 = 0; void gf16pmul_sve2(void *HEDLEY_RESTRICT dst, const void* src1, const void* src2, size_t len) { UNUSED(dst); UNUSED(src1); UNUSED(src2); UNUSED(len); } unsigned gf16pmul_sve2_width() { return 1; } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf16pmul_vpclgfni.c000066400000000000000000000005241514221355600225770ustar00rootroot00000000000000#include "../src/platform.h" #define _mword __m256i #define _MM(f) _mm256_ ## f #define _MMI(f) _mm256_ ## f ## _si256 #define MWORD_SIZE 32 #define _FNSUFFIX _vpclgfni #define _USE_VPCLMUL 1 #define _USE_GFNI 1 #if defined(__VPCLMULQDQ__) && defined(__GFNI__) && defined(__AVX2__) # define _AVAILABLE 1 #endif #include "gf16pmul_x86.h" par2cmdline-turbo-1.4.0/parpar/gf16/gf16pmul_vpclmul.c000066400000000000000000000004521514221355600224510ustar00rootroot00000000000000#include "../src/platform.h" #define _mword __m256i #define _MM(f) _mm256_ ## f #define _MMI(f) _mm256_ ## f ## _si256 #define MWORD_SIZE 32 #define _FNSUFFIX _vpclmul #define _USE_VPCLMUL 1 #if defined(__VPCLMULQDQ__) && defined(__AVX2__) # define _AVAILABLE 1 #endif #include "gf16pmul_x86.h" par2cmdline-turbo-1.4.0/parpar/gf16/gf16pmul_x86.h000066400000000000000000000213731514221355600214260ustar00rootroot00000000000000#include "gf16_global.h" #if defined(_AVAILABLE) int _FN(gf16pmul_available) = 1; static HEDLEY_ALWAYS_INLINE void _FN(gf16pmul_initmul)(const _mword* src1, const _mword* src2, _mword* prod1, _mword* prod2) { _mword wordMask = _MM(set1_epi32)(0xffff); _mword data1 = _MMI(load)(src1); _mword data2 = _MMI(load)(src2); // do multiply _mword data1Even = _MMI(and)(wordMask, data1); _mword data1Odd = _MMI(andnot)(wordMask, data1); _mword data2Even = _MMI(and)(wordMask, data2); _mword data2Odd = _MMI(andnot)(wordMask, data2); #if MWORD_SIZE == 32 && !defined(_USE_VPCLMUL) __m128i data1EvenA = _mm256_castsi256_si128(data1Even); __m128i data1EvenB = _mm256_extracti128_si256(data1Even, 1); __m128i data1OddA = _mm256_castsi256_si128(data1Odd); __m128i data1OddB = _mm256_extracti128_si256(data1Odd, 1); __m128i data2EvenA = _mm256_castsi256_si128(data2Even); __m128i data2EvenB = _mm256_extracti128_si256(data2Even, 1); __m128i data2OddA = _mm256_castsi256_si128(data2Odd); __m128i data2OddB = _mm256_extracti128_si256(data2Odd, 1); __m128i prod1EvenA = _mm_clmulepi64_si128(data1EvenA, data2EvenA, 0x00); __m128i prod1EvenB = _mm_clmulepi64_si128(data1EvenB, data2EvenB, 0x00); __m128i prod2EvenA = _mm_clmulepi64_si128(data1EvenA, data2EvenA, 0x11); __m128i prod2EvenB = _mm_clmulepi64_si128(data1EvenB, data2EvenB, 0x11); __m128i prod1OddA = _mm_clmulepi64_si128(data1OddA, data2OddA, 0x00); __m128i prod1OddB = _mm_clmulepi64_si128(data1OddB, data2OddB, 0x00); __m128i prod2OddA = _mm_clmulepi64_si128(data1OddA, data2OddA, 0x11); __m128i prod2OddB = _mm_clmulepi64_si128(data1OddB, data2OddB, 0x11); __m128i prod1A = _mm_blend_epi16(prod1EvenA, prod1OddA, 0xCC); __m128i prod1B = _mm_blend_epi16(prod1EvenB, prod1OddB, 0xCC); __m128i prod2A = _mm_blend_epi16(prod2EvenA, prod2OddA, 0xCC); __m128i prod2B = _mm_blend_epi16(prod2EvenB, prod2OddB, 0xCC); *prod1 = _mm256_inserti128_si256(_mm256_castsi128_si256(prod1A), prod1B, 1); *prod2 = _mm256_inserti128_si256(_mm256_castsi128_si256(prod2A), prod2B, 1); #else # if MWORD_SIZE == 16 _mword prod1Even = _mm_clmulepi64_si128(data1Even, data2Even, 0x00); _mword prod2Even = _mm_clmulepi64_si128(data1Even, data2Even, 0x11); _mword prod1Odd = _mm_clmulepi64_si128(data1Odd, data2Odd, 0x00); _mword prod2Odd = _mm_clmulepi64_si128(data1Odd, data2Odd, 0x11); # else _mword prod1Even = _MM(clmulepi64_epi128)(data1Even, data2Even, 0x00); _mword prod2Even = _MM(clmulepi64_epi128)(data1Even, data2Even, 0x11); _mword prod1Odd = _MM(clmulepi64_epi128)(data1Odd, data2Odd, 0x00); _mword prod2Odd = _MM(clmulepi64_epi128)(data1Odd, data2Odd, 0x11); # endif # if MWORD_SIZE >= 64 *prod1 = _MM(mask_blend_epi32)(0xAAAA, prod1Even, prod1Odd); *prod2 = _MM(mask_blend_epi32)(0xAAAA, prod2Even, prod2Odd); # else *prod1 = _MM(blend_epi16)(prod1Even, prod1Odd, 0xCC); *prod2 = _MM(blend_epi16)(prod2Even, prod2Odd, 0xCC); # endif #endif } void _FN(gf16pmul)(void *HEDLEY_RESTRICT dst, const void* src1, const void* src2, size_t len) { assert(len % sizeof(_mword) == 0); const uint8_t* _src1 = (const uint8_t*)src1 + len; const uint8_t* _src2 = (const uint8_t*)src2 + len; uint8_t* _dst = (uint8_t*)dst + len; #if MWORD_SIZE >= 64 _mword shufLoHi = _MM(set4_epi32)(0x0f0e0b0a, 0x07060302, 0x0d0c0908, 0x05040100); #else _mword shufLoHi = _MM(set_epi16)( # if MWORD_SIZE >= 32 0x0f0e, 0x0b0a, 0x0706, 0x0302, 0x0d0c, 0x0908, 0x0504, 0x0100, # endif 0x0f0e, 0x0b0a, 0x0706, 0x0302, 0x0d0c, 0x0908, 0x0504, 0x0100 ); #endif #ifdef _USE_GFNI assert(len % (sizeof(_mword)*2) == 0); # if MWORD_SIZE >= 64 _mword shufBLoHi = _MM(set4_epi32)(0x0f0d0b09, 0x07050301, 0x0e0c0a08, 0x06040200); # else _mword shufBLoHi = _MM(set_epi8)( # if MWORD_SIZE >= 32 15,13,11,9,7,5,3,1,14,12,10,8,6,4,2,0, # endif 15,13,11,9,7,5,3,1,14,12,10,8,6,4,2,0 ); # endif for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(_mword)*2) { _mword prod1, prod2, prod3, prod4; _FN(gf16pmul_initmul)((_mword*)(_src1 + ptr), (_mword*)(_src2 + ptr), &prod1, &prod2); _FN(gf16pmul_initmul)((_mword*)(_src1 + ptr) +1, (_mword*)(_src2 + ptr) +1, &prod3, &prod4); // split low/high _mword tmp1 = _MM(shuffle_epi8)(prod1, shufLoHi); _mword tmp2 = _MM(shuffle_epi8)(prod2, shufLoHi); _mword rem1 = _MM(unpacklo_epi64)(tmp1, tmp2); _mword quot1 = _MM(unpackhi_epi64)(tmp1, tmp2); tmp1 = _MM(shuffle_epi8)(prod3, shufLoHi); tmp2 = _MM(shuffle_epi8)(prod4, shufLoHi); _mword rem2 = _MM(unpacklo_epi64)(tmp1, tmp2); _mword quot2 = _MM(unpackhi_epi64)(tmp1, tmp2); // split quot into bytes tmp1 = _MM(shuffle_epi8)(quot1, shufBLoHi); tmp2 = _MM(shuffle_epi8)(quot2, shufBLoHi); quot1 = _MM(unpacklo_epi64)(tmp1, tmp2); quot2 = _MM(unpackhi_epi64)(tmp1, tmp2); // do reduction #if MWORD_SIZE >= 64 # define SET1_EPI64 _MM(set1_epi64) #else # define SET1_EPI64 _MM(set1_epi64x) #endif tmp2 = _MMI(xor)( _MM(gf2p8affine_epi64_epi8)(quot2, SET1_EPI64(0xbb77eedd0b162c58), 0), _MM(gf2p8affine_epi64_epi8)(quot1, SET1_EPI64(0xa040800011224488), 0) ); tmp1 = _MMI(xor)( _MM(gf2p8affine_epi64_epi8)(quot2, SET1_EPI64(0xb1d3a6fdfbf7eedd), 0), _MM(gf2p8affine_epi64_epi8)(quot1, SET1_EPI64(0x113366ddba74e8d0), 0) ); #undef SET1_EPI64 /* mappings for above affine matrices: (tmp1 = bottom, tmp2 = top) * Mul by 0x1111a * top->top: top ^ top>>4 * top->bot: top ^ top>>4 ^ top<<4 ^ top>>5 ^ top>>7 * bot->bot: bot ^ bot>>4 * Mul by 0x100b * top->top: top ^ top<<1 ^ top<<3 * bot->top: bot>>7 ^ bot>>5 ^ bot<<4 * bot->bot: bot ^ bot<<1 ^ bot<<3 * Together: * top->top: * b = top ^ top<<4 ^ top>>4 ^ top>>5 ^ top>>7 * top ^= top>>4 * top ^= top<<1 ^ top<<3 * top ^= b>>7 ^ b>>5 ^ b<<4 * top->bot: * bot = top ^ top<<4 ^ top>>4 ^ top>>5 ^ top>>7 * bot ^= bot<<1 ^ bot<<3 * bot->top: * b = bot ^ bot>>4 * top = b>>7 ^ b>>5 ^ b<<4 * bot->bot: * bot ^= bot>>4 * bot ^= bot<<1 ^ bot<<3 */ // join together quot1 = _MM(unpacklo_epi8)(tmp1, tmp2); quot2 = _MM(unpackhi_epi8)(tmp1, tmp2); // xor with rem quot1 = _MMI(xor)(quot1, rem1); quot2 = _MMI(xor)(quot2, rem2); _MMI(store)((_mword*)(_dst + ptr), quot1); _MMI(store)((_mword*)(_dst + ptr) + 1, quot2); } #else for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(_mword)) { _mword prod1, prod2; _FN(gf16pmul_initmul)((_mword*)(_src1 + ptr), (_mword*)(_src2 + ptr), &prod1, &prod2); // do reduction /* obvious Barret reduction strategy, using CLMUL instructions const __m128i barretConst = _mm_set_epi32(0, 0x1100b, 0, 0x1111a); __m128i quot1 = _mm_srli_epi32(prod1, 16); __m128i quot2 = _mm_srli_epi32(prod2, 16); __m128i quot11 = _mm_clmulepi64_si128(quot1, barretConst, 0x00); __m128i quot12 = _mm_clmulepi64_si128(quot1, barretConst, 0x01); __m128i quot21 = _mm_clmulepi64_si128(quot2, barretConst, 0x00); __m128i quot22 = _mm_clmulepi64_si128(quot2, barretConst, 0x01); quot1 = _mm_unpacklo_epi64(quot11, quot12); quot2 = _mm_unpacklo_epi64(quot21, quot22); quot1 = _mm_srli_epi32(quot1, 16); quot2 = _mm_srli_epi32(quot2, 16); quot11 = _mm_clmulepi64_si128(quot1, barretConst, 0x10); quot12 = _mm_clmulepi64_si128(quot1, barretConst, 0x11); quot21 = _mm_clmulepi64_si128(quot2, barretConst, 0x10); quot22 = _mm_clmulepi64_si128(quot2, barretConst, 0x11); quot1 = _mm_unpacklo_epi64(quot11, quot12); quot2 = _mm_unpacklo_epi64(quot21, quot22); quot1 = _mm_xor_si128(quot1, prod1); quot2 = _mm_xor_si128(quot2, prod2); __m128i result = _mm_packus_epi32( _mm_and_si128(wordMask, quot1), _mm_and_si128(wordMask, quot2) ); */ // since there aren't that many bits in the Barret constants, doing manual shift+xor is more efficient // split low/high 16-bit parts _mword tmp1 = _MM(shuffle_epi8)(prod1, shufLoHi); _mword tmp2 = _MM(shuffle_epi8)(prod2, shufLoHi); _mword rem = _MM(unpacklo_epi64)(tmp1, tmp2); _mword quot = _MM(unpackhi_epi64)(tmp1, tmp2); // multiply by 0x1111a (or rather, 0x11118, since the '2' bit doesn't matter due to the product being at most 31 bits) and retain high half tmp1 = _MMI(xor)(quot, _MM(srli_epi16)(quot, 4)); tmp1 = _MMI(xor)(tmp1, _MM(srli_epi16)(tmp1, 8)); quot = _MMI(xor)(tmp1, _MM(srli_epi16)(quot, 13)); // multiply by 0x100b, retain low half tmp1 = _MMI(xor)(quot, _MM(slli_epi16)(quot, 3)); tmp1 = _MMI(xor)(tmp1, _MM(add_epi16)(quot, quot)); quot = _MMI(xor)(tmp1, _MM(slli_epi16)(quot, 12)); _mword result = _MMI(xor)(quot, rem); _MMI(store)((_mword*)(_dst + ptr), result); } #endif #if MWORD_SIZE >= 32 _mm256_zeroupper(); #endif } #else int _FN(gf16pmul_available) = 0; void _FN(gf16pmul)(void *HEDLEY_RESTRICT dst, const void* src1, const void* src2, size_t len) { UNUSED(dst); UNUSED(src1); UNUSED(src2); UNUSED(len); } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf_add.h000066400000000000000000000046711514221355600204660ustar00rootroot00000000000000 #include "../src/hedley.h" #ifdef PARPAR_INCLUDE_BASIC_OPS void gf_add_multi_generic(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len); void gf_add_multi_sse2(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len); void gf_add_multi_avx2(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len); void gf_add_multi_avx512(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len); void gf_add_multi_neon(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len); void gf_add_multi_sve(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len); void gf_add_multi_sve2(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len); void gf_add_multi_rvv(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len); #endif #ifdef PARPAR_INCLUDE_BASIC_OPS #define FUNCS(f) \ void gf_add_multi_packed_##f(unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len); \ void gf_add_multi_packpf_##f(unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) #else #define FUNCS(f) \ void gf_add_multi_packpf_##f(unsigned packRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) #endif #ifdef PLATFORM_AMD64 FUNCS(v1i6_sse2); FUNCS(v2i3_sse2); FUNCS(v1i6_avx2); FUNCS(v16i1_avx2); FUNCS(v1i6_avx512); FUNCS(v1i12_avx512); FUNCS(v1i12_avx10); FUNCS(v2i3_avx512); FUNCS(v2i4_avx512); FUNCS(v2i6_avx512); FUNCS(v2i6_avx10); FUNCS(v16i6_avx512); #else FUNCS(v1i2_sse2); FUNCS(v1i1_avx2); FUNCS(v1i1_avx512); FUNCS(v1i2_avx512); FUNCS(v2i1_avx512); #endif FUNCS(v2i1_sse2); FUNCS(v16i1_sse2); FUNCS(v1i2_avx2); FUNCS(v2i1_avx2); FUNCS(v2i3_avx2); FUNCS(shuffle_neon); FUNCS(clmul_neon); FUNCS(sve); FUNCS(v1i6_sve2); FUNCS(v2i3_sve2); FUNCS(v2i4_sve2); FUNCS(v2i8_sve2); FUNCS(v2i3_rvv); FUNCS(v1i12_rvv); FUNCS(generic); FUNCS(lookup3); #undef FUNCS par2cmdline-turbo-1.4.0/parpar/gf16/gf_add_avx10.c000066400000000000000000000037321514221355600214750ustar00rootroot00000000000000#include "gf16_global.h" #include "../src/platform.h" #include "gf_add_common.h" #define _mword __m256i #define _MM(f) _mm256_ ## f #define _MMI(f) _mm256_ ## f ## _si256 #define _FNSUFFIX _avx10 #if defined(__AVX512VL__) && !defined(__EVEX512__) && (defined(__AVX10_1__) || defined(__EVEX256__)) # define _AVAILABLE #endif #define _ADD_USE_TERNLOG #include "gf_add_x86.h" #undef _ADD_USE_TERNLOG #undef _FNSUFFIX #undef _MMI #undef _MM #undef _mword #ifdef _AVAILABLE # ifdef PARPAR_INCLUDE_BASIC_OPS # define PACKED_FUNC(vs, il, it) \ void gf_add_multi_packed_v##vs##i##il##_avx10(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len) { \ gf16_muladd_multi_packed((void*)vs, &gf_add_x_avx10, il, it, packedRegions, regions, dst, src, len, sizeof(__m256i)*vs, NULL); \ _mm256_zeroupper(); \ } \ void gf_add_multi_packpf_v##vs##i##il##_avx10(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ gf16_muladd_multi_packpf((void*)vs, &gf_add_x_avx10, il, it, packedRegions, regions, dst, src, len, sizeof(__m256i)*vs, NULL, vs>1, prefetchIn, prefetchOut); \ _mm256_zeroupper(); \ } # else # define PACKED_FUNC(vs, il, it) \ void gf_add_multi_packpf_v##vs##i##il##_avx10(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ gf16_muladd_multi_packpf((void*)vs, &gf_add_x_avx10, il, it, packedRegions, regions, dst, src, len, sizeof(__m256i)*vs, NULL, vs>1, prefetchIn, prefetchOut); \ _mm256_zeroupper(); \ } # endif #else # define PACKED_FUNC(vs, il, it) PACKED_STUB(avx10, vs, il, it) #endif #ifdef PLATFORM_AMD64 PACKED_FUNC_NOTSLIM(avx10, 1, 12, 12) PACKED_FUNC(2, 6, 18) #endif #undef PACKED_FUNC #ifdef _AVAILABLE # undef _AVAILABLE #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf_add_avx2.c000066400000000000000000000045011514221355600214110ustar00rootroot00000000000000#include "gf16_global.h" #include "../src/platform.h" #include "gf_add_common.h" #define _mword __m256i #define _MM(f) _mm256_ ## f #define _MMI(f) _mm256_ ## f ## _si256 #define _FNSUFFIX _avx2 #ifdef __AVX2__ # define _AVAILABLE #endif #include "gf_add_x86.h" #ifdef _AVAILABLE # undef _AVAILABLE #endif #undef _FNSUFFIX #undef _MMI #undef _MM #undef _mword #ifdef PARPAR_INCLUDE_BASIC_OPS void gf_add_multi_avx2(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len) { #ifdef __AVX2__ gf16_muladd_multi((void*)1, &gf_add_x_avx2, 6, regions, offset, dst, src, len, NULL); _mm256_zeroupper(); #else UNUSED(regions); UNUSED(offset); UNUSED(dst); UNUSED(src); UNUSED(len); #endif } #endif #ifdef __AVX2__ # ifdef PARPAR_INCLUDE_BASIC_OPS # define PACKED_FUNC(vs, il, it) \ void gf_add_multi_packed_v##vs##i##il##_avx2(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len) { \ gf16_muladd_multi_packed((void*)vs, &gf_add_x_avx2, il, it, packedRegions, regions, dst, src, len, sizeof(__m256i)*vs, NULL); \ _mm256_zeroupper(); \ } \ void gf_add_multi_packpf_v##vs##i##il##_avx2(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ gf16_muladd_multi_packpf((void*)vs, &gf_add_x_avx2, il, it, packedRegions, regions, dst, src, len, sizeof(__m256i)*vs, NULL, vs>1, prefetchIn, prefetchOut); \ _mm256_zeroupper(); \ } # else # define PACKED_FUNC(vs, il, it) \ void gf_add_multi_packpf_v##vs##i##il##_avx2(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ gf16_muladd_multi_packpf((void*)vs, &gf_add_x_avx2, il, it, packedRegions, regions, dst, src, len, sizeof(__m256i)*vs, NULL, vs>1, prefetchIn, prefetchOut); \ _mm256_zeroupper(); \ } # endif #else # define PACKED_FUNC(vs, il, it) PACKED_STUB(avx2, vs, il, it) #endif PACKED_FUNC_NOTSLIM(avx2, 1, 2, 8) #ifdef PLATFORM_AMD64 PACKED_FUNC_NOTSLIM(avx2, 1, 6, 18) PACKED_FUNC(16, 1, 6) #else PACKED_FUNC_NOTSLIM(avx2, 1, 1, 6) #endif PACKED_FUNC(2, 1, 6) PACKED_FUNC(2, 3, 12) #undef PACKED_FUNC par2cmdline-turbo-1.4.0/parpar/gf16/gf_add_avx512.c000066400000000000000000000047721514221355600215710ustar00rootroot00000000000000#include "gf16_global.h" #include "../src/platform.h" #include "gf_add_common.h" #define _mword __m512i #define _MM(f) _mm512_ ## f #define _MMI(f) _mm512_ ## f ## _si512 #define _FNSUFFIX _avx512 #ifdef __AVX512F__ # define _AVAILABLE #endif #define _ADD_USE_TERNLOG #include "gf_add_x86.h" #undef _ADD_USE_TERNLOG #ifdef _AVAILABLE # undef _AVAILABLE #endif #undef _FNSUFFIX #undef _MMI #undef _MM #undef _mword #ifdef PARPAR_INCLUDE_BASIC_OPS void gf_add_multi_avx512(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len) { #ifdef __AVX512F__ gf16_muladd_multi((void*)1, &gf_add_x_avx512, 6, regions, offset, dst, src, len, NULL); _mm256_zeroupper(); #else UNUSED(regions); UNUSED(offset); UNUSED(dst); UNUSED(src); UNUSED(len); #endif } #endif #ifdef __AVX512F__ # ifdef PARPAR_INCLUDE_BASIC_OPS # define PACKED_FUNC(vs, il, it) \ void gf_add_multi_packed_v##vs##i##il##_avx512(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len) { \ gf16_muladd_multi_packed((void*)vs, &gf_add_x_avx512, il, it, packedRegions, regions, dst, src, len, sizeof(__m512i)*vs, NULL); \ _mm256_zeroupper(); \ } \ void gf_add_multi_packpf_v##vs##i##il##_avx512(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ gf16_muladd_multi_packpf((void*)vs, &gf_add_x_avx512, il, it, packedRegions, regions, dst, src, len, sizeof(__m512i)*vs, NULL, vs>1, prefetchIn, prefetchOut); \ _mm256_zeroupper(); \ } # else # define PACKED_FUNC(vs, il, it) \ void gf_add_multi_packpf_v##vs##i##il##_avx512(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ gf16_muladd_multi_packpf((void*)vs, &gf_add_x_avx512, il, it, packedRegions, regions, dst, src, len, sizeof(__m512i)*vs, NULL, vs>1, prefetchIn, prefetchOut); \ _mm256_zeroupper(); \ } # endif #else # define PACKED_FUNC(vs, il, it) PACKED_STUB(avx512, vs, il, it) #endif #ifdef PLATFORM_AMD64 PACKED_FUNC_NOTSLIM(avx512, 1, 6, 18) PACKED_FUNC_NOTSLIM(avx512, 1, 12, 12) PACKED_FUNC(2, 3, 12) PACKED_FUNC(2, 4, 12) PACKED_FUNC(2, 6, 18) PACKED_FUNC_NOTSLIM(avx512, 16, 6, 18) #else PACKED_FUNC_NOTSLIM(avx512, 1, 1, 6) PACKED_FUNC_NOTSLIM(avx512, 1, 2, 8) PACKED_FUNC(2, 1, 6) #endif #undef PACKED_FUNC par2cmdline-turbo-1.4.0/parpar/gf16/gf_add_common.h000066400000000000000000000023621514221355600220310ustar00rootroot00000000000000 #ifdef PARPAR_INCLUDE_BASIC_OPS # define PACKED_STUB(suf, vs, il, it) \ void gf_add_multi_packed_v##vs##i##il##_##suf(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len) { \ UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); \ } \ void gf_add_multi_packpf_v##vs##i##il##_##suf(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(prefetchIn); UNUSED(prefetchOut); \ } #else # define PACKED_STUB(suf, vs, il, it) \ void gf_add_multi_packpf_v##vs##i##il##_##suf(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(prefetchIn); UNUSED(prefetchOut); \ } #endif #ifndef PARPAR_SLIM_GF16 # define PACKED_FUNC_NOTSLIM(suf, vs, il, it) PACKED_FUNC(vs, il, it) #else # define PACKED_FUNC_NOTSLIM PACKED_STUB #endif par2cmdline-turbo-1.4.0/parpar/gf16/gf_add_generic.c000066400000000000000000000100371514221355600221460ustar00rootroot00000000000000#include "gf16_global.h" #include "gf16_muladd_multi.h" static HEDLEY_ALWAYS_INLINE void gf_add_x_generic( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { ASSUME(len > 0); GF16_MULADD_MULTI_SRC_UNUSED(8); UNUSED(coefficients); UNUSED(doPrefetch); UNUSED(_pf); int lookup3Rearrange = (int)((intptr_t)scratch); // abuse this variable // probably never happens, but ensure length is a multiple of the native int intptr_t ptr = -(intptr_t)len; while(ptr & (sizeof(uintptr_t)-1)) { uint8_t data = _src1[ptr*srcScale]; if(srcCount >= 2) data ^= _src2[ptr*srcScale]; if(srcCount >= 3) data ^= _src3[ptr*srcScale]; if(srcCount >= 4) data ^= _src4[ptr*srcScale]; if(srcCount >= 5) data ^= _src5[ptr*srcScale]; if(srcCount >= 6) data ^= _src6[ptr*srcScale]; if(srcCount >= 7) data ^= _src7[ptr*srcScale]; if(srcCount >= 8) data ^= _src8[ptr*srcScale]; assert(!lookup3Rearrange); _dst[ptr] ^= data; ptr++; } for(; ptr; ptr += sizeof(uintptr_t)) { uintptr_t data; data = readPtr(_src1+ptr*srcScale); if(srcCount >= 2) data ^= readPtr(_src2+ptr*srcScale); if(srcCount >= 3) data ^= readPtr(_src3+ptr*srcScale); if(srcCount >= 4) data ^= readPtr(_src4+ptr*srcScale); if(srcCount >= 5) data ^= readPtr(_src5+ptr*srcScale); if(srcCount >= 6) data ^= readPtr(_src6+ptr*srcScale); if(srcCount >= 7) data ^= readPtr(_src7+ptr*srcScale); if(srcCount >= 8) data ^= readPtr(_src8+ptr*srcScale); if(lookup3Rearrange) { // revert bit rearrangement for LOOKUP3 method if(sizeof(uintptr_t) >= 8) { data = (data & 0xf80007fff80007ffULL) | ((data & 0x003ff800003ff800ULL) << 5) | ((data & 0x07c0000007c00000ULL) >> 11); } else { data = (data & 0xf80007ff) | ((data & 0x003ff800) << 5) | ((data & 0x07c00000) >> 11); } } writePtr(_dst+ptr, readPtr(_dst+ptr) ^ data); } } #include "gf16_lookup.h" #ifdef PARPAR_INCLUDE_BASIC_OPS void gf_add_multi_generic(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len) { gf16_muladd_multi(NULL, &gf_add_x_generic, 4, regions, offset, dst, src, len, NULL); } // assumes word-size packing (for lookup algorithms) void gf_add_multi_packed_generic(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len) { gf16_muladd_multi_packed(NULL, &gf_add_x_generic, 1, 4, packedRegions, regions, dst, src, len, gf16_lookup_stride(), NULL); } #endif void gf_add_multi_packpf_generic(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { // no support for prefetching on generic implementation, so defer to regular function UNUSED(prefetchIn); UNUSED(prefetchOut); gf16_muladd_multi_packed(NULL, &gf_add_x_generic, 1, 4, packedRegions, regions, dst, src, len, gf16_lookup_stride(), NULL); } #ifdef PARPAR_INCLUDE_BASIC_OPS void gf_add_multi_packed_lookup3(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len) { #ifndef PARPAR_SLIM_GF16 gf16_muladd_multi_packed((void*)1, &gf_add_x_generic, 1, 4, packedRegions, regions, dst, src, len, gf16_lookup_stride(), NULL); #else UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); #endif } #endif void gf_add_multi_packpf_lookup3(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { UNUSED(prefetchIn); UNUSED(prefetchOut); #ifndef PARPAR_SLIM_GF16 gf16_muladd_multi_packed((void*)1, &gf_add_x_generic, 1, 4, packedRegions, regions, dst, src, len, gf16_lookup_stride(), NULL); #else UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); #endif } par2cmdline-turbo-1.4.0/parpar/gf16/gf_add_neon.c000066400000000000000000000137041514221355600214750ustar00rootroot00000000000000#include "gf16_neon_common.h" #include "gf16_muladd_multi.h" #ifdef __ARM_NEON static HEDLEY_ALWAYS_INLINE uint8x16x2_t veorq_u8_x2(uint8x16x2_t a, uint8x16x2_t b) { uint8x16x2_t result; result.val[0] = veorq_u8(a.val[0], b.val[0]); result.val[1] = veorq_u8(a.val[1], b.val[1]); return result; } static HEDLEY_ALWAYS_INLINE void gf_add_x_neon( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { ASSUME((len & (sizeof(uint8x16_t)*2-1)) == 0); ASSUME(((uintptr_t)_dst & (sizeof(uint8x16_t)-1)) == 0); ASSUME(len > 0); GF16_MULADD_MULTI_SRC_UNUSED(18); UNUSED(scratch); UNUSED(coefficients); #define DO_PROCESS \ uint8x16x2_t data = vld1q_u8_x2_align(_dst+ptr); \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src1+ptr*srcScale)); \ if(srcCount >= 2) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src2+ptr*srcScale)); \ if(srcCount >= 3) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src3+ptr*srcScale)); \ if(srcCount >= 4) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src4+ptr*srcScale)); \ if(srcCount >= 5) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src5+ptr*srcScale)); \ if(srcCount >= 6) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src6+ptr*srcScale)); \ if(srcCount >= 7) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src7+ptr*srcScale)); \ if(srcCount >= 8) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src8+ptr*srcScale)); \ if(srcCount >= 9) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src9+ptr*srcScale)); \ if(srcCount >= 10) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src10+ptr*srcScale)); \ if(srcCount >= 11) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src11+ptr*srcScale)); \ if(srcCount >= 12) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src12+ptr*srcScale)); \ if(srcCount >= 13) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src13+ptr*srcScale)); \ if(srcCount >= 14) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src14+ptr*srcScale)); \ if(srcCount >= 15) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src15+ptr*srcScale)); \ if(srcCount >= 16) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src16+ptr*srcScale)); \ if(srcCount >= 17) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src17+ptr*srcScale)); \ if(srcCount >= 18) \ data = veorq_u8_x2(data, _vld1q_u8_x2(_src18+ptr*srcScale)); \ vst1q_u8_x2_align(_dst+ptr, data) if(doPrefetch) { intptr_t ptr = -(intptr_t)len; if(doPrefetch == 1) PREFETCH_MEM(_pf+(ptr>>1), 1); if(doPrefetch == 2) PREFETCH_MEM(_pf+(ptr>>1), 0); while(ptr & (CACHELINE_SIZE-1)) { DO_PROCESS; ptr += sizeof(uint8x16_t)*2; } while(ptr) { if(doPrefetch == 1) PREFETCH_MEM(_pf+(ptr>>1), 1); if(doPrefetch == 2) PREFETCH_MEM(_pf+(ptr>>1), 0); for(size_t iter=0; iter<(CACHELINE_SIZE/(sizeof(uint8x16_t)*2)); iter++) { DO_PROCESS; ptr += sizeof(uint8x16_t)*2; } } } else { for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(uint8x16_t)*2) { DO_PROCESS; } } #undef DO_PROCESS } #endif #ifdef PARPAR_INCLUDE_BASIC_OPS void gf_add_multi_neon(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len) { #ifdef __ARM_NEON gf16_muladd_multi(NULL, &gf_add_x_neon, 4, regions, offset, dst, src, len, NULL); #else UNUSED(regions); UNUSED(offset); UNUSED(dst); UNUSED(src); UNUSED(len); #endif } void gf_add_multi_packed_shuffle_neon(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len) { #if defined(__ARM_NEON) && !defined(PARPAR_SLIM_GF16) # ifdef __aarch64__ gf16_muladd_multi_packed(NULL, &gf_add_x_neon, 2, 8, packedRegions, regions, dst, src, len, sizeof(uint8x16_t)*2, NULL); # else gf16_muladd_multi_packed(NULL, &gf_add_x_neon, 1, 6, packedRegions, regions, dst, src, len, sizeof(uint8x16_t)*2, NULL); # endif #else UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); #endif } void gf_add_multi_packed_clmul_neon(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len) { #ifdef __ARM_NEON # ifdef __aarch64__ gf16_muladd_multi_packed(NULL, &gf_add_x_neon, 8, 16, packedRegions, regions, dst, src, len, sizeof(uint8x16_t)*2, NULL); # else gf16_muladd_multi_packed(NULL, &gf_add_x_neon, 3, 12, packedRegions, regions, dst, src, len, sizeof(uint8x16_t)*2, NULL); # endif #else UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); #endif } #endif void gf_add_multi_packpf_shuffle_neon(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { #if defined(__ARM_NEON) && !defined(PARPAR_SLIM_GF16) # ifdef __aarch64__ gf16_muladd_multi_packpf(NULL, &gf_add_x_neon, 2, 8, packedRegions, regions, dst, src, len, sizeof(uint8x16_t)*2, NULL, 1, prefetchIn, prefetchOut); # else gf16_muladd_multi_packpf(NULL, &gf_add_x_neon, 1, 6, packedRegions, regions, dst, src, len, sizeof(uint8x16_t)*2, NULL, 1, prefetchIn, prefetchOut); # endif #else UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(prefetchIn); UNUSED(prefetchOut); #endif } void gf_add_multi_packpf_clmul_neon(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { #ifdef __ARM_NEON # ifdef __aarch64__ gf16_muladd_multi_packpf(NULL, &gf_add_x_neon, 8, 16, packedRegions, regions, dst, src, len, sizeof(uint8x16_t)*2, NULL, 1, prefetchIn, prefetchOut); # else gf16_muladd_multi_packpf(NULL, &gf_add_x_neon, 3, 12, packedRegions, regions, dst, src, len, sizeof(uint8x16_t)*2, NULL, 1, prefetchIn, prefetchOut); # endif #else UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(prefetchIn); UNUSED(prefetchOut); #endif } par2cmdline-turbo-1.4.0/parpar/gf16/gf_add_rvv.c000066400000000000000000000064411514221355600213530ustar00rootroot00000000000000#include "gf16_rvv_common.h" #include "gf16_muladd_multi.h" #include "gf_add_common.h" #ifdef __riscv_vector static HEDLEY_ALWAYS_INLINE void gf_add_x_rvv( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { ASSUME(len > 0); GF16_MULADD_MULTI_SRC_UNUSED(18); UNUSED(coefficients); unsigned vecStride = (unsigned)((uintptr_t)scratch); // abuse this otherwise unused variable #define XOR_LOAD(n, lmul) \ if(srcCount >= n) \ data = RV(vxor_vv_u8m ## lmul)(data, RV(vle8_v_u8m ## lmul)(_src##n+ptr*srcScale, vl), vl) #define DO_ADD(lmul) \ size_t vl = RV(vsetvlmax_e8m ## lmul)(); \ for(intptr_t ptr = -(intptr_t)len; ptr; ptr += vl) { \ vuint8m ## lmul ## _t data = RV(vle8_v_u8m ## lmul)(_dst+ptr, vl); \ \ XOR_LOAD(1, lmul); \ XOR_LOAD(2, lmul); \ XOR_LOAD(3, lmul); \ XOR_LOAD(4, lmul); \ XOR_LOAD(5, lmul); \ XOR_LOAD(6, lmul); \ XOR_LOAD(7, lmul); \ XOR_LOAD(8, lmul); \ XOR_LOAD(9, lmul); \ XOR_LOAD(10, lmul); \ XOR_LOAD(11, lmul); \ XOR_LOAD(12, lmul); \ XOR_LOAD(13, lmul); \ XOR_LOAD(14, lmul); \ XOR_LOAD(15, lmul); \ XOR_LOAD(16, lmul); \ XOR_LOAD(17, lmul); \ XOR_LOAD(18, lmul); \ \ RV(vse8_v_u8m ## lmul)(_dst+ptr, data, vl); \ } UNUSED(doPrefetch); UNUSED(_pf); if(vecStride == 2) { // shuffle DO_ADD(2) } else { // clmul assert(vecStride == 1); DO_ADD(1) } #undef XOR_LOAD #undef DO_ADD } #endif #ifdef PARPAR_INCLUDE_BASIC_OPS void gf_add_multi_rvv(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len) { #ifdef __riscv_vector gf16_muladd_multi((void*)2, &gf_add_x_rvv, 4, regions, offset, dst, src, len, NULL); #else UNUSED(regions); UNUSED(offset); UNUSED(dst); UNUSED(src); UNUSED(len); #endif } #endif #ifdef __riscv_vector # ifdef PARPAR_INCLUDE_BASIC_OPS # define PACKED_FUNC(vs, il, it) \ void gf_add_multi_packed_v##vs##i##il##_rvv(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len) { \ gf16_muladd_multi_packed((void*)vs, &gf_add_x_rvv, il, it, packedRegions, regions, dst, src, len, RV(vsetvlmax_e8m1)()*vs, NULL); \ } \ void gf_add_multi_packpf_v##vs##i##il##_rvv(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ gf16_muladd_multi_packpf((void*)vs, &gf_add_x_rvv, il, it, packedRegions, regions, dst, src, len, RV(vsetvlmax_e8m1)()*vs, NULL, vs>1, prefetchIn, prefetchOut); \ } # else # define PACKED_FUNC(vs, il, it) \ void gf_add_multi_packpf_v##vs##i##il##_rvv(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ gf16_muladd_multi_packpf((void*)vs, &gf_add_x_rvv, il, it, packedRegions, regions, dst, src, len, RV(vsetvlmax_e8m1)()*vs, NULL, vs>1, prefetchIn, prefetchOut); \ } # endif #else # define PACKED_FUNC(vs, il, it) PACKED_STUB(rvv, vs, il, it) #endif PACKED_FUNC(2, 3, 12) PACKED_FUNC(1, 12, 12) #undef PACKED_FUNC par2cmdline-turbo-1.4.0/parpar/gf16/gf_add_sse2.c000066400000000000000000000042771514221355600214170ustar00rootroot00000000000000#include "gf16_global.h" #include "../src/platform.h" #include "gf_add_common.h" #define _mword __m128i #define _MM(f) _mm_ ## f #define _MMI(f) _mm_ ## f ## _si128 #define _FNSUFFIX _sse2 #ifdef __SSE2__ # define _AVAILABLE #endif #include "gf_add_x86.h" #ifdef _AVAILABLE # undef _AVAILABLE #endif #undef _FNSUFFIX #undef _MMI #undef _MM #undef _mword #ifdef PARPAR_INCLUDE_BASIC_OPS void gf_add_multi_sse2(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len) { #ifdef __SSE2__ gf16_muladd_multi((void*)1, &gf_add_x_sse2, 4, regions, offset, dst, src, len, NULL); #else UNUSED(regions); UNUSED(offset); UNUSED(dst); UNUSED(src); UNUSED(len); #endif } #endif #ifdef __SSE2__ # ifdef PARPAR_INCLUDE_BASIC_OPS # define PACKED_FUNC(vs, il, it) \ void gf_add_multi_packed_v##vs##i##il##_sse2(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len) { \ gf16_muladd_multi_packed((void*)vs, &gf_add_x_sse2, il, it, packedRegions, regions, dst, src, len, sizeof(__m128i)*vs, NULL); \ } \ void gf_add_multi_packpf_v##vs##i##il##_sse2(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ gf16_muladd_multi_packpf((void*)vs, &gf_add_x_sse2, il, it, packedRegions, regions, dst, src, len, sizeof(__m128i)*vs, NULL, vs>1, prefetchIn, prefetchOut); \ } # else # define PACKED_FUNC(vs, il, it) \ void gf_add_multi_packpf_v##vs##i##il##_sse2(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ gf16_muladd_multi_packpf((void*)vs, &gf_add_x_sse2, il, it, packedRegions, regions, dst, src, len, sizeof(__m128i)*vs, NULL, vs>1, prefetchIn, prefetchOut); \ } # endif #else # define PACKED_FUNC(vs, il, it) PACKED_STUB(sse2, vs, il, it) #endif #ifdef PLATFORM_AMD64 PACKED_FUNC_NOTSLIM(sse2, 1, 6, 18) PACKED_FUNC(2, 3, 12) #else PACKED_FUNC_NOTSLIM(sse2, 1, 2, 8) #endif PACKED_FUNC(2, 1, 4) PACKED_FUNC(16, 1, 4) #undef PACKED_FUNC par2cmdline-turbo-1.4.0/parpar/gf16/gf_add_sve.c000066400000000000000000000071741514221355600213370ustar00rootroot00000000000000#include "gf16_sve_common.h" #include "gf16_muladd_multi.h" #ifdef __ARM_FEATURE_SVE static HEDLEY_ALWAYS_INLINE void gf_add2_sve(svuint8_t* data1, svuint8_t* data2, const uint8_t* src) { *data1 = NOMASK(sveor_u8, *data1, svld1_u8(svptrue_b8(), src)); *data2 = NOMASK(sveor_u8, *data2, svld1_vnum_u8(svptrue_b8(), src, 1)); } static HEDLEY_ALWAYS_INLINE void gf_add_x_sve( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { ASSUME(len > 0); GF16_MULADD_MULTI_SRC_UNUSED(18); UNUSED(scratch); UNUSED(coefficients); for(intptr_t ptr = -(intptr_t)len; ptr; ptr += svcntb()*2) { svuint8_t data1 = svld1_u8(svptrue_b8(), _dst+ptr); svuint8_t data2 = svld1_vnum_u8(svptrue_b8(), _dst+ptr, 1); gf_add2_sve(&data1, &data2, _src1+ptr*srcScale); if(srcCount >= 2) gf_add2_sve(&data1, &data2, _src2+ptr*srcScale); if(srcCount >= 3) gf_add2_sve(&data1, &data2, _src3+ptr*srcScale); if(srcCount >= 4) gf_add2_sve(&data1, &data2, _src4+ptr*srcScale); if(srcCount >= 5) gf_add2_sve(&data1, &data2, _src5+ptr*srcScale); if(srcCount >= 6) gf_add2_sve(&data1, &data2, _src6+ptr*srcScale); if(srcCount >= 7) gf_add2_sve(&data1, &data2, _src7+ptr*srcScale); if(srcCount >= 8) gf_add2_sve(&data1, &data2, _src8+ptr*srcScale); if(srcCount >= 9) gf_add2_sve(&data1, &data2, _src9+ptr*srcScale); if(srcCount >= 10) gf_add2_sve(&data1, &data2, _src10+ptr*srcScale); if(srcCount >= 11) gf_add2_sve(&data1, &data2, _src11+ptr*srcScale); if(srcCount >= 12) gf_add2_sve(&data1, &data2, _src12+ptr*srcScale); if(srcCount >= 13) gf_add2_sve(&data1, &data2, _src13+ptr*srcScale); if(srcCount >= 14) gf_add2_sve(&data1, &data2, _src14+ptr*srcScale); if(srcCount >= 15) gf_add2_sve(&data1, &data2, _src15+ptr*srcScale); if(srcCount >= 16) gf_add2_sve(&data1, &data2, _src16+ptr*srcScale); if(srcCount >= 17) gf_add2_sve(&data1, &data2, _src17+ptr*srcScale); if(srcCount >= 18) gf_add2_sve(&data1, &data2, _src18+ptr*srcScale); svst1_u8(svptrue_b8(), _dst+ptr, data1); svst1_vnum_u8(svptrue_b8(), _dst+ptr, 1, data2); if(doPrefetch == 1) svprfb(svptrue_b8(), _pf+(ptr>>1), SV_PLDL1KEEP); if(doPrefetch == 2) svprfb(svptrue_b8(), _pf+(ptr>>1), SV_PLDL2KEEP); } } #endif #ifdef PARPAR_INCLUDE_BASIC_OPS void gf_add_multi_sve(unsigned regions, size_t offset, void *HEDLEY_RESTRICT dst, const void* const*HEDLEY_RESTRICT src, size_t len) { #ifdef __ARM_FEATURE_SVE gf16_muladd_multi(NULL, &gf_add_x_sve, 4, regions, offset, dst, src, len, NULL); #else UNUSED(regions); UNUSED(offset); UNUSED(dst); UNUSED(src); UNUSED(len); #endif } void gf_add_multi_packed_sve(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len) { #ifdef __ARM_FEATURE_SVE gf16_muladd_multi_packed(NULL, &gf_add_x_sve, 3, 12, packedRegions, regions, dst, src, len, svcntb()*2, NULL); #else UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); #endif } #endif void gf_add_multi_packpf_sve(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { #ifdef __ARM_FEATURE_SVE gf16_muladd_multi_packpf(NULL, &gf_add_x_sve, 3, 12, packedRegions, regions, dst, src, len, svcntb()*2, NULL, 1, prefetchIn, prefetchOut); #else UNUSED(packedRegions); UNUSED(regions); UNUSED(dst); UNUSED(src); UNUSED(len); UNUSED(prefetchIn); UNUSED(prefetchOut); #endif } par2cmdline-turbo-1.4.0/parpar/gf16/gf_add_sve2.c000066400000000000000000000074711514221355600214210ustar00rootroot00000000000000#include "gf16_sve_common.h" #include "gf16_muladd_multi.h" #include "gf_add_common.h" #ifdef __ARM_FEATURE_SVE2 static HEDLEY_ALWAYS_INLINE void gf_add2_sve2(int srcCount, svuint8_t* data, unsigned v, const uint8_t* src1, const uint8_t* src2) { if(srcCount < 1) return; if(srcCount > 1) *data = sveor3_u8(*data, svld1_vnum_u8(svptrue_b8(), src1, v), svld1_vnum_u8(svptrue_b8(), src2, v)); else *data = NOMASK(sveor_u8, *data, svld1_vnum_u8(svptrue_b8(), src1, v)); } static HEDLEY_ALWAYS_INLINE void gf_add_x_sve2( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { ASSUME(len > 0); GF16_MULADD_MULTI_SRC_UNUSED(18); UNUSED(coefficients); unsigned vecStride = (unsigned)((uintptr_t)scratch); // abuse this otherwise unused variable for(intptr_t ptr = -(intptr_t)len; ptr; ptr += svcntb()*vecStride) { for(unsigned v=0; v1, prefetchIn, prefetchOut); \ } # else # define PACKED_FUNC(vs, il, it) \ void gf_add_multi_packpf_v##vs##i##il##_sve2(unsigned packedRegions, unsigned regions, void *HEDLEY_RESTRICT dst, const void* HEDLEY_RESTRICT src, size_t len, const void* HEDLEY_RESTRICT prefetchIn, const void* HEDLEY_RESTRICT prefetchOut) { \ gf16_muladd_multi_packpf((void*)vs, &gf_add_x_sve2, il, it, packedRegions, regions, dst, src, len, svcntb()*vs, NULL, vs>1, prefetchIn, prefetchOut); \ } # endif #else # define PACKED_FUNC(vs, il, it) PACKED_STUB(sve2, vs, il, it) #endif PACKED_FUNC_NOTSLIM(sve2, 1, 6, 18) PACKED_FUNC_NOTSLIM(sve2, 2, 3, 12) PACKED_FUNC_NOTSLIM(sve2, 2, 4, 12) PACKED_FUNC(2, 8, 16) #undef PACKED_FUNC par2cmdline-turbo-1.4.0/parpar/gf16/gf_add_x86.h000066400000000000000000000055571514221355600211770ustar00rootroot00000000000000 #include "gf16_muladd_multi.h" #ifdef _AVAILABLE static HEDLEY_ALWAYS_INLINE void _FN(gf_add_x)( const void *HEDLEY_RESTRICT scratch, uint8_t *HEDLEY_RESTRICT _dst, const unsigned srcScale, GF16_MULADD_MULTI_SRCLIST, size_t len, const uint16_t *HEDLEY_RESTRICT coefficients, const int doPrefetch, const char* _pf ) { ASSUME(((uintptr_t)_dst & (sizeof(_mword)-1)) == 0); ASSUME(len > 0); GF16_MULADD_MULTI_SRC_UNUSED(18); UNUSED(coefficients); unsigned vecStride = (unsigned)((uintptr_t)scratch); // abuse this otherwise unused variable ASSUME((len & (sizeof(_mword)*vecStride-1)) == 0); // vecStride assumed to be a known compile-time constant for(intptr_t ptr = -(intptr_t)len; ptr; ptr += sizeof(_mword)*vecStride) { for(unsigned v=0; v m) \ data = _MM(ternarylogic_epi32)(data, \ _MMI(load)((_mword*)(_src##m+ptr*srcScale) + v), \ _MMI(load)((_mword*)(_src##n+ptr*srcScale) + v), \ 0x96) #else # define ADD_PAIR(m, n) \ if(srcCount >= m) \ data = _MMI(xor)(data, _MMI(load)((_mword*)(_src##m+ptr*srcScale) + v)); \ if(srcCount >= n) \ data = _MMI(xor)(data, _MMI(load)((_mword*)(_src##n+ptr*srcScale) + v)) #endif ADD_PAIR(1, 2); ADD_PAIR(3, 4); ADD_PAIR(5, 6); ADD_PAIR(7, 8); ADD_PAIR(9, 10); ADD_PAIR(11, 12); ADD_PAIR(13, 14); ADD_PAIR(15, 16); ADD_PAIR(17, 18); #undef ADD_PAIR _MMI(store)((_mword*)(_dst+ptr) + v, data); } if(vecStride == 16) { // for xor kernels, need to do 4x prefetch if(doPrefetch) { const char* pfBase = _pf+(ptr>>1); if(doPrefetch == 1) { _mm_prefetch(pfBase, MM_HINT_WT1); _mm_prefetch(pfBase+64, MM_HINT_WT1); if(sizeof(_mword) > 16) { _mm_prefetch(pfBase+128, MM_HINT_WT1); _mm_prefetch(pfBase+192, MM_HINT_WT1); } if(sizeof(_mword) > 32) { _mm_prefetch(pfBase+256, MM_HINT_WT1); _mm_prefetch(pfBase+320, MM_HINT_WT1); _mm_prefetch(pfBase+384, MM_HINT_WT1); _mm_prefetch(pfBase+448, MM_HINT_WT1); } } if(doPrefetch == 2) { _mm_prefetch(pfBase, _MM_HINT_T1); _mm_prefetch(pfBase+64, _MM_HINT_T1); if(sizeof(_mword) > 16) { _mm_prefetch(pfBase+128, _MM_HINT_T1); _mm_prefetch(pfBase+192, _MM_HINT_T1); } if(sizeof(_mword) > 32) { _mm_prefetch(pfBase+256, _MM_HINT_T1); _mm_prefetch(pfBase+320, _MM_HINT_T1); _mm_prefetch(pfBase+384, _MM_HINT_T1); _mm_prefetch(pfBase+448, _MM_HINT_T1); } } } } else { if(doPrefetch == 1) _mm_prefetch(_pf+(ptr/vecStride), MM_HINT_WT1); if(doPrefetch == 2) _mm_prefetch(_pf+(ptr/vecStride), _MM_HINT_T1); } } } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gfmat_coeff.c000066400000000000000000000063031514221355600215070ustar00rootroot00000000000000#include "gfmat_coeff.h" #include static int8_t* input_diff = NULL; // difference between predicted input coefficient and actual (number range is -4...5, so could be compressed to 4 bits, but I don't feel it's worth the savings) static uint16_t* gf_exp = NULL; // pre-calculated exponents in GF(2^16), missing bottom 3 bits, followed by 128-entry polynomial shift table #ifdef PARPAR_INVERT_SUPPORT uint16_t* gf16_recip = NULL; // full GF(2^16) reciprocal table #endif void gfmat_init() { if(input_diff) return; input_diff = (int8_t*)malloc(32768); gf_exp = (uint16_t*)malloc((8192+128)*2); #ifdef PARPAR_INVERT_SUPPORT gf16_recip = (uint16_t*)malloc(65536*2); #endif int exp = 0, n = 1; for (int i = 0; i < 32768; i++) { do { #ifdef PARPAR_INVERT_SUPPORT gf16_recip[n] = exp; // essentially construct a log table, then alter it later to get the reciprocal #endif if((exp & 7) == 0) gf_exp[exp>>3] = n; exp++; // exp will reach 65534 by the end of the loop n <<= 1; if(n > 65535) n ^= 0x1100B; } while( !(exp%3) || !(exp%5) || !(exp%17) || !(exp%257) ); input_diff[i] = exp - i*2; } #ifdef PARPAR_INVERT_SUPPORT gf16_recip[n] = exp; #endif // correction values for handling the missing bottom 3 bits of exp // essentially this is a table to speed up multiplication by 0...127 by applying the effects of polynomial masking for (int i = 0; i < 128; i++) { n = i << 9; for (int j = 0; j < 7; j++) { n <<= 1; if(n > 65535) n ^= 0x1100B; } gf_exp[8192+i] = n; } #ifdef PARPAR_INVERT_SUPPORT gf16_recip[1] = 65535; // exponentiate for reciprocals for (int i = 1; i < 65536; i++) { gf16_recip[i] = gf16_exp(65535 - gf16_recip[i]); } #endif } void gfmat_free() { free(input_diff); free(gf_exp); input_diff = NULL; gf_exp = NULL; #ifdef PARPAR_INVERT_SUPPORT free(gf16_recip); gf16_recip = NULL; #endif } HEDLEY_CONST uint16_t gf16_exp(uint_fast16_t v) { uint_fast32_t result = gf_exp[v>>3]; result <<= (v&7); return result ^ gf_exp[8192 + (result>>16)]; /* alternative idea which only omits bottom bit of gf_exp lookup, but avoids a second lookup // GCC doesn't handle the unpredictable check that well uint_fast32_t result0 = gf_exp[result>>1]; uint_fast32_t result1 = (result0 << 1) ^ (-(result0 >> 15) & 0x1100B); // multiply by 2? return HEDLEY_UNPREDICTABLE(result & 1) ? result1 : result0; */ } HEDLEY_CONST uint16_t gfmat_input_log(uint_fast16_t inputBlock) { return (inputBlock*2 + input_diff[inputBlock]); } HEDLEY_CONST uint16_t gfmat_coeff_log(uint_fast16_t inputLog, uint_fast16_t recoveryBlock) { //assert(recoveryBlock < 65535); // if ==65535, gets an invalid exponent // calculate POW(inputBlockConstant, recoveryBlock) in GF uint_fast32_t result = inputLog * recoveryBlock; // clever bit hack for 'result %= 65535' from MultiPar sources result = (result >> 16) + (result & 65535); result += result >> 16; return result; } HEDLEY_CONST uint16_t gfmat_coeff_from_log(uint_fast16_t inputLog, uint_fast16_t recoveryBlock) { return gf16_exp(gfmat_coeff_log(inputLog, recoveryBlock)); } HEDLEY_CONST uint16_t gfmat_coeff(uint_fast16_t inputBlock, uint_fast16_t recoveryBlock) { return gfmat_coeff_from_log(gfmat_input_log(inputBlock), recoveryBlock); } par2cmdline-turbo-1.4.0/parpar/gf16/gfmat_coeff.h000066400000000000000000000011401514221355600215060ustar00rootroot00000000000000#ifndef GFMAT_COEFF_H #define GFMAT_COEFF_H #include "../src/hedley.h" #include "../src/stdint.h" #ifdef __cplusplus extern "C" { #endif void gfmat_init(); void gfmat_free(); HEDLEY_CONST uint16_t gfmat_coeff_from_log(uint_fast16_t inputLog, uint_fast16_t recoveryBlock); HEDLEY_CONST uint16_t gfmat_coeff(uint_fast16_t inputBlock, uint_fast16_t recoveryBlock); HEDLEY_CONST uint16_t gfmat_input_log(uint_fast16_t inputBlock); HEDLEY_CONST uint16_t gfmat_coeff_log(uint_fast16_t inputLog, uint_fast16_t recoveryBlock); HEDLEY_CONST uint16_t gf16_exp(uint_fast16_t v); #ifdef __cplusplus } #endif #endif par2cmdline-turbo-1.4.0/parpar/gf16/gfmat_inv.cpp000066400000000000000000000613271514221355600215700ustar00rootroot00000000000000#include "gfmat_coeff.h" #include "gfmat_inv.h" #include "gf16pmul.h" #include #ifdef PARPAR_INVERT_SUPPORT extern "C" uint16_t* gf16_recip; #include #include "../src/platform.h" // for ALIGN_* #include "gf16mul.h" #include "threadqueue.h" #include static const unsigned MIN_THREAD_REC = 10; // minimum number of rows to process on a thread struct Galois16RecMatrixComputeState { uint16_t* coeff; Galois16Mul gf; void* gfScratch; unsigned validCount; void* srcRowsBase[PP_INVERT_MAX_MULTI_ROWS]; std::vector workers; unsigned pfFactor; Galois16RecMatrixComputeState(Galois16Methods method) : gf(method) {} }; class Galois16RecMatrixWorker { const Galois16Mul& gf; public: MessageThread thread; void* gfScratch; explicit Galois16RecMatrixWorker(const Galois16Mul& _gf) : gf(_gf) { gfScratch = _gf.mutScratch_alloc(); } Galois16RecMatrixWorker(Galois16RecMatrixWorker&& other) noexcept : gf(other.gf) { thread = std::move(other.thread); gfScratch = other.gfScratch; other.gfScratch = nullptr; } ~Galois16RecMatrixWorker() { thread.end(); if(gfScratch) gf.mutScratch_free(gfScratch); } }; struct Galois16RecMatrixWorkerMessage { unsigned stripeStart, stripeEnd; unsigned recFirst, recLast; unsigned recSrc; unsigned recSrcCount; uint16_t* rowCoeffs; Galois16Mul* gf; void* gfScratch; void* (&srcRowsBase)[PP_INVERT_MAX_MULTI_ROWS]; unsigned coeffWidth; void(Galois16RecMatrix::*fn)(unsigned, unsigned, unsigned, unsigned, unsigned, unsigned, uint16_t*, unsigned, void*(&)[PP_INVERT_MAX_MULTI_ROWS], Galois16Mul&, void*, const void*, unsigned); unsigned pfFactor; Galois16RecMatrix* parent; std::atomic* procRefs; std::promise* done; Galois16RecMatrixWorkerMessage(Galois16RecMatrixComputeState& state) : rowCoeffs(state.coeff), gf(&state.gf), srcRowsBase(state.srcRowsBase), pfFactor(state.pfFactor) {} }; static void invert_worker(ThreadMessageQueue& q) { Galois16RecMatrixWorkerMessage* req; while((req = static_cast(q.pop())) != NULL) { (req->parent->*(req->fn))(req->stripeStart, req->stripeEnd, req->recFirst, req->recLast, req->recSrc, req->recSrcCount, req->rowCoeffs, req->coeffWidth, req->srcRowsBase, *(req->gf), req->gfScratch, nullptr, req->pfFactor); if(req->procRefs->fetch_sub(1, std::memory_order_acq_rel) <= 1) { req->done->set_value(); } delete req; } } #define MAT_ROW(s, r) (mat + (((s)*numRec) + (r)) * (stripeWidth / sizeof(uint16_t))) #define CEIL_DIV(a, b) (((a) + (b)-1) / (b)) #define ROUND_DIV(a, b) (((a) + ((b)>>1)) / (b)) template void Galois16RecMatrix::invertLoop(unsigned stripeStart, unsigned stripeEnd, unsigned recFirst, unsigned recLast, unsigned recSrc, unsigned recSrcCount, uint16_t* rowCoeffs, unsigned coeffWidth, void* (&srcRowsBase)[PP_INVERT_MAX_MULTI_ROWS], Galois16Mul& gf, void* gfScratch, const void* nextPf, unsigned pfFactor) { assert(recSrcCount % rows == 0); // when to start prefetching the next stripe unsigned recStartPf = 0, recSrcStartPf = recFirst; if(recSrcCount > rows< rows<= recStartPf) { if(recI == recStartPf && curRec2 == recFirst) { if(stripe < stripeEnd-1) // prefetch next stripe pf = (const uint8_t*)(MAT_ROW(stripe+1, recFirst)); else pf = (const uint8_t*)nextPf; } // TODO: if numStripes==1, we might want to avoid prefetching the same row group as the first applyRows loop would } else { if(curRec2 == recSrcStartPf) // prefetch next rowSrc pf = (const uint8_t*)(MAT_ROW(stripe, rec+rows)); else if(curRec2 < recSrcStartPf) pf = nullptr; } uint16_t* target = MAT_ROW(stripe, curRec2); uint16_t* coeffPtr = rowCoeffs + (curRec2-recFirst)*coeffWidth + recI; if(rows > 1) { if(pf) gf.mul_add_multi_stridepf(rows, stripeWidth, target, MAT_ROW(stripe, rec), stripeWidth, coeffPtr, gfScratch, pf); else { unsigned offset = rec*stripeWidth; gf.mul_add_multi(rows, stripeWidth*numRec*stripe + offset, MAT_ROW(0, curRec2) - offset/sizeof(uint16_t), srcRowsBase, stripeWidth, coeffPtr, gfScratch); } } else { if(pf) gf.mul_add_pf(target, MAT_ROW(stripe, rec), stripeWidth, *coeffPtr, gfScratch, pf); else gf.mul_add(target, MAT_ROW(stripe, rec), stripeWidth, *coeffPtr, gfScratch); } if(pf) pf += stripeWidth >> pfFactor; } } } } #define REPLACE_WORD(r, c, v) state.gf.replace_word(MAT_ROW((c)/(stripeWidth / sizeof(uint16_t)), r), (c)%(stripeWidth / sizeof(uint16_t)), v) template int Galois16RecMatrix::scaleRows(Galois16RecMatrixComputeState& state, unsigned rec, unsigned recFirst, unsigned recLast) { assert(recFirst <= recLast); assert(rec != recFirst); unsigned missingCol = state.validCount + rec; uint16_t tmpCoeff; #define SCALE_ROW(row) \ tmpCoeff = REPLACE_WORD(rec+row, missingCol+row, 1); \ if(HEDLEY_UNLIKELY(tmpCoeff == 0)) /* bad recovery coeff */ \ return row; \ if(HEDLEY_LIKELY(tmpCoeff != 1)) { \ for(unsigned stripe=0; stripe= 2) { // multiply-add to the next row MULADD_ROW(rec+1, 0); // scale it, and multiply-add back SCALE_ROW(1); if(rows > 2) { MULADD_ROW_PF(rec+0, 1, MAT_ROW(0, 2)); } else MULADD_LASTROW(rec+0, 1) } else { if(recFirst >= numRec) return -1; } if(rows >= 3) { if(rows >= 4) { MULADD_MULTI_ROW_PF(rec+2, 0, 2, MAT_ROW(0, 3)); SCALE_ROW(2); MULADD_MULTI_ROW(rec+3, 0, 2); MULADD_ROW(rec+3, 2); SCALE_ROW(3); MULADD_ROW(rec+2, 3); MULADD_MULTI_ROW(rec+0, 2, 2); if(rows > 4) { MULADD_MULTI_ROW_PF(rec+1, 2, 2, MAT_ROW(0, 4)); } else MULADD_MULTI_LASTROW(rec+1, 2, 2) } else { MULADD_MULTI_ROW(rec+2, 0, 2); SCALE_ROW(2); MULADD_ROW(rec+0, 2); MULADD_LASTROW(rec+1, 2) } } if(rows >= 5) { if(rows >= 6) { MULADD_MULTI_ROW_PF(rec+4, 0, 4, MAT_ROW(0, 5)); SCALE_ROW(4); MULADD_MULTI_ROW(rec+5, 0, 4); MULADD_ROW(rec+5, 4); SCALE_ROW(5); MULADD_ROW(rec+4, 5); for(unsigned r = 0; r < 3; r++) { MULADD_MULTI_ROW(rec+r, 4, 2); } MULADD_MULTI_LASTROW(rec+3, 4, 2) } else { MULADD_MULTI_ROW(rec+4, 0, 4); SCALE_ROW(4); for(unsigned r = 0; r < 3; r++) { MULADD_ROW(rec+r, 4); } MULADD_LASTROW(rec+3, 4) } } HEDLEY_STATIC_ASSERT(rows <= PP_INVERT_MAX_MULTI_ROWS && rows <= 6, "PP_INVERT_MAX_MULTI_ROWS > 6 case not handled"); return -1; #undef SCALE_ROW #undef MULADD_ROW #undef MULADD_ROW_PF #undef MULADD_MULTI_ROW #undef MULADD_MULTI_ROW_PF #undef MULADD_LASTROW #undef MULADD_MULTI_LASTROW } void Galois16RecMatrix::fillCoeffs(Galois16RecMatrixComputeState& state, unsigned rows, unsigned recFirst, unsigned recLast, unsigned rec, unsigned coeffWidth) { assert(rec != recFirst); unsigned missingCol = state.validCount + rec; for(unsigned r=recFirst; r void Galois16RecMatrix::applyRows(Galois16RecMatrixComputeState& state, unsigned rec, unsigned recCount, unsigned recFirst, unsigned recLast, unsigned coeffWidth, int nextRow) { // TODO: consider optimisation for numStripes == 1 ? assert(recFirst < recLast); assert(rec != recFirst); // do main elimination, using the source group if(state.workers.empty()) { // process elimination directly uint16_t* nextScaleRow = nextRow >= 0 ? MAT_ROW(0, (unsigned)nextRow) : nullptr; invertLoop(0, numStripes, recFirst, recLast, rec, recCount, state.coeff, coeffWidth, state.srcRowsBase, state.gf, state.gfScratch, nextScaleRow, state.pfFactor); } else { // process using workers std::atomic procRefs; std::promise done; auto makeReq = [&, this]() -> Galois16RecMatrixWorkerMessage* { auto* req = new Galois16RecMatrixWorkerMessage(state); req->recFirst = recFirst; req->recLast = recLast; req->recSrc = rec; req->recSrcCount = recCount; req->coeffWidth = coeffWidth; req->fn = &Galois16RecMatrix::invertLoop; req->parent = this; req->procRefs = &procRefs; req->done = &done; return req; }; if(numStripes >= state.workers.size()) { // split full stripes across workers float stripesPerWorker = (float)numStripes / state.workers.size(); float stripe = 0.5; procRefs.store((int)state.workers.size()); for(auto& worker : state.workers) { auto* req = makeReq(); req->stripeStart = (unsigned)stripe; req->stripeEnd = (unsigned)(stripe + stripesPerWorker); req->gfScratch = worker.gfScratch; worker.thread.send(req); stripe += stripesPerWorker; } assert((unsigned)stripe == numStripes); } else { // each stripe may need >1 worker std::vector reqs; reqs.reserve(state.workers.size()); float workersPerStripe = (float)state.workers.size() / numStripes; float workerCnt = 0.5; for(unsigned stripe=0; stripe= recFirst && rec < recLast) numRows -= rows; numRows = CEIL_DIV(numRows, workerNum); if(numRows < MIN_THREAD_REC) numRows = MIN_THREAD_REC; // ensure workers have a half decent amount of stuff to do unsigned rowPos = recFirst; while(rowPos < recLast) { unsigned sendRows = numRows; if(rowPos+sendRows > rec && rowPos <= rec) // need to send extra to compensate for the gap sendRows += rows; if(rowPos+sendRows > recLast) sendRows = recLast - rowPos; auto* req = makeReq(); req->stripeStart = stripe; req->stripeEnd = stripe+1; req->recFirst = rowPos; req->recLast = rowPos+sendRows; req->rowCoeffs += (rowPos-recFirst) * coeffWidth; reqs.push_back(req); rowPos += sendRows; if(rowPos == rec) rowPos += rows; } workerCnt += workersPerStripe; } assert(reqs.size() <= state.workers.size()); assert((size_t)workerCnt == state.workers.size()); procRefs.store((int)reqs.size()); assert(procRefs > 0); for(unsigned i=0; igfScratch = worker.gfScratch; worker.thread.send(req); } } // wait for threads to finish done.get_future().wait(); } } #undef REPLACE_WORD #undef MAT_ROW template int Galois16RecMatrix::processRows(Galois16RecMatrixComputeState& state, unsigned& rec, unsigned rowGroupSize, std::function progressCb, uint16_t progressOffset, uint16_t totalProgress) { unsigned alignedRowGroupSize = (rowGroupSize / rows) * rows; while(rec <= numRec-rows) { unsigned curRowGroupSize = alignedRowGroupSize; if(numRec-rec < curRowGroupSize) { curRowGroupSize = numRec-rec; curRowGroupSize -= curRowGroupSize % rows; } assert(curRowGroupSize > 0); unsigned recStart = rec; // for progress indicator, we'll even it out by computing a ratio to advance by unsigned progressRatio = (curRowGroupSize<<16)/numRec; unsigned progressBase = recStart + progressOffset; // loop through this row group (normalize values) for(; rec < curRowGroupSize+recStart; rec+=rows) { if(progressCb) progressCb(progressBase + (((rec-recStart)*progressRatio+32768)>>16), totalProgress); unsigned recFirst = recStart; if(recFirst == rec) recFirst += rows; int badRowOffset = scaleRows(state, rec, recFirst, curRowGroupSize+recStart); if(badRowOffset >= 0) return rec+badRowOffset; if(recFirst == curRowGroupSize+recStart) continue; fillCoeffs(state, rows, recFirst, curRowGroupSize+recStart, rec, rows); int nextRow = recStart; if(rec+rows == curRowGroupSize+recStart) nextRow = recStart > 0 ? 0 : (numRec>=curRowGroupSize*2 ? curRowGroupSize : -1); applyRows(state, rec, rows, recFirst, curRowGroupSize+recStart, rows, nextRow); } // apply current row group to all other row groups for(unsigned recGroup=0; recGroup>16), totalProgress); } unsigned curRowGroupSize2 = rowGroupSize; if(numRec-recGroup < curRowGroupSize2) curRowGroupSize2 = numRec-recGroup; if(recGroup < recStart && recGroup+curRowGroupSize2 > recStart) curRowGroupSize2 = recStart-recGroup; // don't let this group cross into the normalized group assert(curRowGroupSize2 > 0); fillCoeffs(state, curRowGroupSize, recGroup, recGroup+curRowGroupSize2, recStart, curRowGroupSize); int nextRow = recGroup + curRowGroupSize2; if((unsigned)nextRow >= numRec) nextRow = rec+curRowGroupSize2 < numRec ? rec : -1; else if((unsigned)nextRow+curRowGroupSize2 > numRec) nextRow = -1; // don't over prefetch; TODO: is there a way to still prefetch something? applyRows(state, recStart, curRowGroupSize, recGroup, recGroup+curRowGroupSize2, curRowGroupSize, nextRow); recGroup += curRowGroupSize2; } } return -1; } // construct initial matrix (pre-inversion) void Galois16RecMatrix::Construct(const std::vector& inputValid, unsigned validCount, const std::vector& recovery) { unsigned validCol = 0; unsigned missingCol = validCount; unsigned recStart = 0; unsigned sw16 = stripeWidth/sizeof(uint16_t); if(recovery.at(0) == 0) { // first recovery having exponent 0 is a common case for(unsigned stripe=0; stripe= recovery.size()) return; unsigned input = 0; const unsigned GROUP_AMOUNT = 4; #define CONSTRUCT_VIA_EXP(loopcond) \ for(; input + GROUP_AMOUNT <= inputValid.size(); input+=GROUP_AMOUNT) { \ uint16_t inputLog[GROUP_AMOUNT]; \ unsigned targetCol[GROUP_AMOUNT]; \ for(unsigned i=0; i recSkips; recSkips.reserve(numRec); recSkips.push_back(recStart); unsigned maxSkips = numRec/2; // TODO: tune threshold uint16_t lastExp = 1; for(unsigned rec = recStart+1; rec < numRec; rec++) { uint16_t exp = recovery.at(rec); if(exp != lastExp+1) { recSkips.push_back(rec); if(recSkips.size() >= maxSkips) break; } lastExp = exp; } if(recSkips.size() < maxSkips) { // not many gaps - use the strategy of filling these gaps first... CONSTRUCT_VIA_EXP(uint16_t rec : recSkips); // ...then compute most of the rows via multiplication for(unsigned stripe=0; stripe& inputValid, unsigned validCount, std::vector& recovery, std::function progressCb) { numRec = (unsigned)inputValid.size() - validCount; assert(validCount < inputValid.size()); // i.e. numRec > 0 assert(inputValid.size() <= 32768 && inputValid.size() > 0); assert(recovery.size() <= 65535 && recovery.size() > 0); if(numRec > recovery.size()) return false; unsigned matWidth = (unsigned)inputValid.size() * sizeof(uint16_t); if(regionMethod == GF16_AUTO) { regionMethod = Galois16Mul::default_method(matWidth, numRec, numRec, true); } Galois16RecMatrixComputeState state((Galois16Methods)regionMethod); state.validCount = validCount; const auto gfInfo = state.gf.info(); state.pfFactor = gfInfo.prefetchDownscale; // divide the matrix up into evenly sized stripes (for loop tiling optimisation) numStripes = ROUND_DIV(matWidth, (unsigned)gfInfo.idealChunkSize); if(numStripes < 1) numStripes = 1; stripeWidth = (unsigned)state.gf.alignToStride(CEIL_DIV(matWidth, numStripes)); numStripes = CEIL_DIV(matWidth, stripeWidth); assert(numStripes >= 1); if(mat) ALIGN_FREE(mat); unsigned matSize = numRec * stripeWidth*numStripes; ALIGN_ALLOC(mat, matSize, gfInfo.alignment); uint16_t totalProgress = numRec + (state.gf.needPrepare() ? 3 : 1); // provision for prepare/finish/init-calc // easier to handle if exponents are in order std::sort(recovery.begin(), recovery.end()); static bool pmulInit = false; if(!pmulInit) { pmulInit = true; setup_pmul(); } state.srcRowsBase[0] = mat; for(unsigned i=1; i 1) { state.workers.reserve(_numThreads); for(unsigned i=0; i<_numThreads; i++) { state.workers.emplace_back(state.gf); state.workers[i].thread.name = "gauss_worker"; state.workers[i].thread.setCallback(invert_worker); } state.gfScratch = state.workers[0].gfScratch; } else state.gfScratch = state.gf.mutScratch_alloc(); // target L2 size - 512K seems to be a reasonable guess for now; TODO: improve this // - targeting larger L2 (e.g. >1MB) seems to perform worse, so a fixed size might end up being better unsigned rowGroupSize = (512*1024 / stripeWidth); // if it's going to be split amongst cores, increase the number of rows in a group if(numStripes < _numThreads) rowGroupSize *= _numThreads/numStripes; unsigned rowMultiple = (std::min)(gfInfo.idealInputMultiple, PP_INVERT_MAX_MULTI_ROWS); if(rowGroupSize < rowMultiple*2) rowGroupSize = rowMultiple*2; if(rowGroupSize > numRec) rowGroupSize = numRec; std::vector stateCoeff(rowGroupSize*rowGroupSize); state.coeff = stateCoeff.data(); invert_loop: { // loop, in the unlikely case we hit the PAR2 un-invertability flaw; TODO: is there a faster way than just retrying? if(numRec > recovery.size()) { // not enough recovery if(_numThreads <= 1) state.gf.mutScratch_free(state.gfScratch); ALIGN_FREE(mat); mat = nullptr; return false; } if(progressCb) progressCb(0, totalProgress); Construct(inputValid, validCount, recovery); // pre-transform uint16_t progressOffset = 1; if(state.gf.needPrepare()) { if(progressCb) progressCb(1, totalProgress); progressOffset = 2; state.gf.prepare(mat, mat, matSize); } // invert unsigned rec = 0; #define INVERT_GROUP(rows) \ if(gfInfo.idealInputMultiple >= rows && numRec >= rows) { \ int badRow = processRows(state, rec, rowGroupSize, progressCb, progressOffset, totalProgress); \ if(badRow >= 0) { \ /* ignore this recovery row and try again */ \ recovery.erase(recovery.begin() + badRow); \ goto invert_loop; \ } \ } // max out at 6 groups (registers + cache assoc?) INVERT_GROUP(6) INVERT_GROUP(5) INVERT_GROUP(4) INVERT_GROUP(3) INVERT_GROUP(2) INVERT_GROUP(1) #undef INVERT_GROUP // post transform if(state.gf.needPrepare()) { if(progressCb) progressCb(totalProgress-1, totalProgress); state.gf.finish(mat, matSize); // TODO: check for zeroes?? } } // remove excess recovery recovery.resize(numRec); if(_numThreads <= 1) state.gf.mutScratch_free(state.gfScratch); return true; } const char* Galois16RecMatrix::getPointMulMethodName() const { return gf16pmul_methodName(); } Galois16RecMatrix::Galois16RecMatrix() : mat(nullptr) { numThreads = hardware_concurrency(); numRec = 0; numStripes = 0; stripeWidth = 0; regionMethod = (int)GF16_AUTO; } Galois16RecMatrix::~Galois16RecMatrix() { if(mat) ALIGN_FREE(mat); } #endif par2cmdline-turbo-1.4.0/parpar/gf16/gfmat_inv.h000066400000000000000000000045161514221355600212320ustar00rootroot00000000000000#ifndef GFMAT_INV_H #define GFMAT_INV_H #include #include #include "../src/stdint.h" #ifdef PARPAR_INVERT_SUPPORT #include "../src/platform.h" // for _LE16 const unsigned PP_INVERT_MAX_MULTI_ROWS = 6; // process up to 6 rows in a multi-mul call class Galois16Mul; class Galois16RecMatrixWorker; struct Galois16RecMatrixComputeState; class Galois16RecMatrix { uint16_t* mat; unsigned numStripes; unsigned stripeWidth; unsigned numRec; unsigned numThreads; void Construct(const std::vector& inputValid, unsigned validCount, const std::vector& recovery); template void invertLoop(unsigned stripeStart, unsigned stripeEnd, unsigned recFirst, unsigned recLast, unsigned recSrc, unsigned recSrcCount, uint16_t* rowCoeffs, unsigned coeffWidth, void* (&srcRowsBase)[PP_INVERT_MAX_MULTI_ROWS], Galois16Mul& gf, void* gfScratch, const void* nextPf, unsigned pfFactor); template int scaleRows(Galois16RecMatrixComputeState& state, unsigned rec, unsigned recFirst, unsigned recLast); void fillCoeffs(Galois16RecMatrixComputeState& state, unsigned rows, unsigned recFirst, unsigned recLast, unsigned rec, unsigned coeffWidth); template void applyRows(Galois16RecMatrixComputeState& state, unsigned rec, unsigned recCount, unsigned recFirst, unsigned recLast, unsigned coeffWidth, int nextRow); template int processRows(Galois16RecMatrixComputeState& state, unsigned& rec, unsigned rowGroupSize, std::function progressCb, uint16_t progressOffset, uint16_t totalProgress); public: Galois16RecMatrix(); ~Galois16RecMatrix(); void setNumThreads(int threads) { numThreads = threads; } bool Compute(const std::vector& inputValid, unsigned validCount, std::vector& recovery, std::function progressCb = nullptr); inline uint16_t GetFactor(uint16_t inIdx, uint16_t recIdx) const { // TODO: check if numStripes==1? consider optimising division? unsigned sw = stripeWidth/sizeof(uint16_t); unsigned stripe = inIdx / sw; return _LE16(mat[stripe * numRec*sw + recIdx * sw + (inIdx % sw)]); } // these should only be queried after Compute has started (i.e. from the progressCb, or after it returns) /*Galois16Methods*/ int regionMethod; const char* getPointMulMethodName() const; }; #endif #endif par2cmdline-turbo-1.4.0/parpar/gf16/opencl-include/000077500000000000000000000000001514221355600220025ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/parpar/gf16/opencl-include/CL/000077500000000000000000000000001514221355600223005ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/parpar/gf16/opencl-include/CL/cl.h000066400000000000000000002376171514221355600230670ustar00rootroot00000000000000/******************************************************************************* * Copyright (c) 2008-2020 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ #ifndef __OPENCL_CL_H #define __OPENCL_CL_H #include #include #ifdef __cplusplus extern "C" { #endif /******************************************************************************/ typedef struct _cl_platform_id * cl_platform_id; typedef struct _cl_device_id * cl_device_id; typedef struct _cl_context * cl_context; typedef struct _cl_command_queue * cl_command_queue; typedef struct _cl_mem * cl_mem; typedef struct _cl_program * cl_program; typedef struct _cl_kernel * cl_kernel; typedef struct _cl_event * cl_event; typedef struct _cl_sampler * cl_sampler; typedef cl_uint cl_bool; /* WARNING! Unlike cl_ types in cl_platform.h, cl_bool is not guaranteed to be the same size as the bool in kernels. */ typedef cl_ulong cl_bitfield; typedef cl_ulong cl_properties; typedef cl_bitfield cl_device_type; typedef cl_uint cl_platform_info; typedef cl_uint cl_device_info; typedef cl_bitfield cl_device_fp_config; typedef cl_uint cl_device_mem_cache_type; typedef cl_uint cl_device_local_mem_type; typedef cl_bitfield cl_device_exec_capabilities; #ifdef CL_VERSION_2_0 typedef cl_bitfield cl_device_svm_capabilities; #endif typedef cl_bitfield cl_command_queue_properties; #ifdef CL_VERSION_1_2 typedef intptr_t cl_device_partition_property; typedef cl_bitfield cl_device_affinity_domain; #endif typedef intptr_t cl_context_properties; typedef cl_uint cl_context_info; #ifdef CL_VERSION_2_0 typedef cl_properties cl_queue_properties; #endif typedef cl_uint cl_command_queue_info; typedef cl_uint cl_channel_order; typedef cl_uint cl_channel_type; typedef cl_bitfield cl_mem_flags; #ifdef CL_VERSION_2_0 typedef cl_bitfield cl_svm_mem_flags; #endif typedef cl_uint cl_mem_object_type; typedef cl_uint cl_mem_info; #ifdef CL_VERSION_1_2 typedef cl_bitfield cl_mem_migration_flags; #endif typedef cl_uint cl_image_info; #ifdef CL_VERSION_1_1 typedef cl_uint cl_buffer_create_type; #endif typedef cl_uint cl_addressing_mode; typedef cl_uint cl_filter_mode; typedef cl_uint cl_sampler_info; typedef cl_bitfield cl_map_flags; #ifdef CL_VERSION_2_0 typedef intptr_t cl_pipe_properties; typedef cl_uint cl_pipe_info; #endif typedef cl_uint cl_program_info; typedef cl_uint cl_program_build_info; #ifdef CL_VERSION_1_2 typedef cl_uint cl_program_binary_type; #endif typedef cl_int cl_build_status; typedef cl_uint cl_kernel_info; #ifdef CL_VERSION_1_2 typedef cl_uint cl_kernel_arg_info; typedef cl_uint cl_kernel_arg_address_qualifier; typedef cl_uint cl_kernel_arg_access_qualifier; typedef cl_bitfield cl_kernel_arg_type_qualifier; #endif typedef cl_uint cl_kernel_work_group_info; #ifdef CL_VERSION_2_1 typedef cl_uint cl_kernel_sub_group_info; #endif typedef cl_uint cl_event_info; typedef cl_uint cl_command_type; typedef cl_uint cl_profiling_info; #ifdef CL_VERSION_2_0 typedef cl_properties cl_sampler_properties; typedef cl_uint cl_kernel_exec_info; #endif #ifdef CL_VERSION_3_0 typedef cl_bitfield cl_device_atomic_capabilities; typedef cl_bitfield cl_device_device_enqueue_capabilities; typedef cl_uint cl_khronos_vendor_id; typedef cl_properties cl_mem_properties; typedef cl_uint cl_version; #endif typedef struct _cl_image_format { cl_channel_order image_channel_order; cl_channel_type image_channel_data_type; } cl_image_format; #ifdef CL_VERSION_1_2 typedef struct _cl_image_desc { cl_mem_object_type image_type; size_t image_width; size_t image_height; size_t image_depth; size_t image_array_size; size_t image_row_pitch; size_t image_slice_pitch; cl_uint num_mip_levels; cl_uint num_samples; #ifdef CL_VERSION_2_0 #if defined(__GNUC__) __extension__ /* Prevents warnings about anonymous union in -pedantic builds */ #endif #if defined(_MSC_VER) && !defined(__STDC__) #pragma warning( push ) #pragma warning( disable : 4201 ) /* Prevents warning about nameless struct/union in /W4 builds */ #endif #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wc11-extensions" /* Prevents warning about nameless union being C11 extension*/ #endif #if defined(_MSC_VER) && defined(__STDC__) /* Anonymous unions are not supported in /Za builds */ #else union { #endif #endif cl_mem buffer; #ifdef CL_VERSION_2_0 #if defined(_MSC_VER) && defined(__STDC__) /* Anonymous unions are not supported in /Za builds */ #else cl_mem mem_object; }; #endif #if defined(_MSC_VER) && !defined(__STDC__) #pragma warning( pop ) #endif #ifdef __clang__ #pragma clang diagnostic pop #endif #endif } cl_image_desc; #endif #ifdef CL_VERSION_1_1 typedef struct _cl_buffer_region { size_t origin; size_t size; } cl_buffer_region; #endif #ifdef CL_VERSION_3_0 #define CL_NAME_VERSION_MAX_NAME_SIZE 64 typedef struct _cl_name_version { cl_version version; char name[CL_NAME_VERSION_MAX_NAME_SIZE]; } cl_name_version; #endif /******************************************************************************/ /* Error Codes */ #define CL_SUCCESS 0 #define CL_DEVICE_NOT_FOUND -1 #define CL_DEVICE_NOT_AVAILABLE -2 #define CL_COMPILER_NOT_AVAILABLE -3 #define CL_MEM_OBJECT_ALLOCATION_FAILURE -4 #define CL_OUT_OF_RESOURCES -5 #define CL_OUT_OF_HOST_MEMORY -6 #define CL_PROFILING_INFO_NOT_AVAILABLE -7 #define CL_MEM_COPY_OVERLAP -8 #define CL_IMAGE_FORMAT_MISMATCH -9 #define CL_IMAGE_FORMAT_NOT_SUPPORTED -10 #define CL_BUILD_PROGRAM_FAILURE -11 #define CL_MAP_FAILURE -12 #ifdef CL_VERSION_1_1 #define CL_MISALIGNED_SUB_BUFFER_OFFSET -13 #define CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST -14 #endif #ifdef CL_VERSION_1_2 #define CL_COMPILE_PROGRAM_FAILURE -15 #define CL_LINKER_NOT_AVAILABLE -16 #define CL_LINK_PROGRAM_FAILURE -17 #define CL_DEVICE_PARTITION_FAILED -18 #define CL_KERNEL_ARG_INFO_NOT_AVAILABLE -19 #endif #define CL_INVALID_VALUE -30 #define CL_INVALID_DEVICE_TYPE -31 #define CL_INVALID_PLATFORM -32 #define CL_INVALID_DEVICE -33 #define CL_INVALID_CONTEXT -34 #define CL_INVALID_QUEUE_PROPERTIES -35 #define CL_INVALID_COMMAND_QUEUE -36 #define CL_INVALID_HOST_PTR -37 #define CL_INVALID_MEM_OBJECT -38 #define CL_INVALID_IMAGE_FORMAT_DESCRIPTOR -39 #define CL_INVALID_IMAGE_SIZE -40 #define CL_INVALID_SAMPLER -41 #define CL_INVALID_BINARY -42 #define CL_INVALID_BUILD_OPTIONS -43 #define CL_INVALID_PROGRAM -44 #define CL_INVALID_PROGRAM_EXECUTABLE -45 #define CL_INVALID_KERNEL_NAME -46 #define CL_INVALID_KERNEL_DEFINITION -47 #define CL_INVALID_KERNEL -48 #define CL_INVALID_ARG_INDEX -49 #define CL_INVALID_ARG_VALUE -50 #define CL_INVALID_ARG_SIZE -51 #define CL_INVALID_KERNEL_ARGS -52 #define CL_INVALID_WORK_DIMENSION -53 #define CL_INVALID_WORK_GROUP_SIZE -54 #define CL_INVALID_WORK_ITEM_SIZE -55 #define CL_INVALID_GLOBAL_OFFSET -56 #define CL_INVALID_EVENT_WAIT_LIST -57 #define CL_INVALID_EVENT -58 #define CL_INVALID_OPERATION -59 #define CL_INVALID_GL_OBJECT -60 #define CL_INVALID_BUFFER_SIZE -61 #define CL_INVALID_MIP_LEVEL -62 #define CL_INVALID_GLOBAL_WORK_SIZE -63 #ifdef CL_VERSION_1_1 #define CL_INVALID_PROPERTY -64 #endif #ifdef CL_VERSION_1_2 #define CL_INVALID_IMAGE_DESCRIPTOR -65 #define CL_INVALID_COMPILER_OPTIONS -66 #define CL_INVALID_LINKER_OPTIONS -67 #define CL_INVALID_DEVICE_PARTITION_COUNT -68 #endif #ifdef CL_VERSION_2_0 #define CL_INVALID_PIPE_SIZE -69 #define CL_INVALID_DEVICE_QUEUE -70 #endif #ifdef CL_VERSION_2_2 #define CL_INVALID_SPEC_ID -71 #define CL_MAX_SIZE_RESTRICTION_EXCEEDED -72 #endif /* cl_bool */ #define CL_FALSE 0 #define CL_TRUE 1 #ifdef CL_VERSION_1_2 #define CL_BLOCKING CL_TRUE #define CL_NON_BLOCKING CL_FALSE #endif /* cl_platform_info */ #define CL_PLATFORM_PROFILE 0x0900 #define CL_PLATFORM_VERSION 0x0901 #define CL_PLATFORM_NAME 0x0902 #define CL_PLATFORM_VENDOR 0x0903 #define CL_PLATFORM_EXTENSIONS 0x0904 #ifdef CL_VERSION_2_1 #define CL_PLATFORM_HOST_TIMER_RESOLUTION 0x0905 #endif #ifdef CL_VERSION_3_0 #define CL_PLATFORM_NUMERIC_VERSION 0x0906 #define CL_PLATFORM_EXTENSIONS_WITH_VERSION 0x0907 #endif /* cl_device_type - bitfield */ #define CL_DEVICE_TYPE_DEFAULT (1 << 0) #define CL_DEVICE_TYPE_CPU (1 << 1) #define CL_DEVICE_TYPE_GPU (1 << 2) #define CL_DEVICE_TYPE_ACCELERATOR (1 << 3) #ifdef CL_VERSION_1_2 #define CL_DEVICE_TYPE_CUSTOM (1 << 4) #endif #define CL_DEVICE_TYPE_ALL 0xFFFFFFFF /* cl_device_info */ #define CL_DEVICE_TYPE 0x1000 #define CL_DEVICE_VENDOR_ID 0x1001 #define CL_DEVICE_MAX_COMPUTE_UNITS 0x1002 #define CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS 0x1003 #define CL_DEVICE_MAX_WORK_GROUP_SIZE 0x1004 #define CL_DEVICE_MAX_WORK_ITEM_SIZES 0x1005 #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR 0x1006 #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT 0x1007 #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT 0x1008 #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG 0x1009 #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT 0x100A #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE 0x100B #define CL_DEVICE_MAX_CLOCK_FREQUENCY 0x100C #define CL_DEVICE_ADDRESS_BITS 0x100D #define CL_DEVICE_MAX_READ_IMAGE_ARGS 0x100E #define CL_DEVICE_MAX_WRITE_IMAGE_ARGS 0x100F #define CL_DEVICE_MAX_MEM_ALLOC_SIZE 0x1010 #define CL_DEVICE_IMAGE2D_MAX_WIDTH 0x1011 #define CL_DEVICE_IMAGE2D_MAX_HEIGHT 0x1012 #define CL_DEVICE_IMAGE3D_MAX_WIDTH 0x1013 #define CL_DEVICE_IMAGE3D_MAX_HEIGHT 0x1014 #define CL_DEVICE_IMAGE3D_MAX_DEPTH 0x1015 #define CL_DEVICE_IMAGE_SUPPORT 0x1016 #define CL_DEVICE_MAX_PARAMETER_SIZE 0x1017 #define CL_DEVICE_MAX_SAMPLERS 0x1018 #define CL_DEVICE_MEM_BASE_ADDR_ALIGN 0x1019 #define CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE 0x101A #define CL_DEVICE_SINGLE_FP_CONFIG 0x101B #define CL_DEVICE_GLOBAL_MEM_CACHE_TYPE 0x101C #define CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE 0x101D #define CL_DEVICE_GLOBAL_MEM_CACHE_SIZE 0x101E #define CL_DEVICE_GLOBAL_MEM_SIZE 0x101F #define CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE 0x1020 #define CL_DEVICE_MAX_CONSTANT_ARGS 0x1021 #define CL_DEVICE_LOCAL_MEM_TYPE 0x1022 #define CL_DEVICE_LOCAL_MEM_SIZE 0x1023 #define CL_DEVICE_ERROR_CORRECTION_SUPPORT 0x1024 #define CL_DEVICE_PROFILING_TIMER_RESOLUTION 0x1025 #define CL_DEVICE_ENDIAN_LITTLE 0x1026 #define CL_DEVICE_AVAILABLE 0x1027 #define CL_DEVICE_COMPILER_AVAILABLE 0x1028 #define CL_DEVICE_EXECUTION_CAPABILITIES 0x1029 #define CL_DEVICE_QUEUE_PROPERTIES 0x102A /* deprecated */ #ifdef CL_VERSION_2_0 #define CL_DEVICE_QUEUE_ON_HOST_PROPERTIES 0x102A #endif #define CL_DEVICE_NAME 0x102B #define CL_DEVICE_VENDOR 0x102C #define CL_DRIVER_VERSION 0x102D #define CL_DEVICE_PROFILE 0x102E #define CL_DEVICE_VERSION 0x102F #define CL_DEVICE_EXTENSIONS 0x1030 #define CL_DEVICE_PLATFORM 0x1031 #ifdef CL_VERSION_1_2 #define CL_DEVICE_DOUBLE_FP_CONFIG 0x1032 #endif /* 0x1033 reserved for CL_DEVICE_HALF_FP_CONFIG which is already defined in "cl_ext.h" */ #ifdef CL_VERSION_1_1 #define CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF 0x1034 #define CL_DEVICE_HOST_UNIFIED_MEMORY 0x1035 /* deprecated */ #define CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR 0x1036 #define CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT 0x1037 #define CL_DEVICE_NATIVE_VECTOR_WIDTH_INT 0x1038 #define CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG 0x1039 #define CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT 0x103A #define CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE 0x103B #define CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF 0x103C #define CL_DEVICE_OPENCL_C_VERSION 0x103D #endif #ifdef CL_VERSION_1_2 #define CL_DEVICE_LINKER_AVAILABLE 0x103E #define CL_DEVICE_BUILT_IN_KERNELS 0x103F #define CL_DEVICE_IMAGE_MAX_BUFFER_SIZE 0x1040 #define CL_DEVICE_IMAGE_MAX_ARRAY_SIZE 0x1041 #define CL_DEVICE_PARENT_DEVICE 0x1042 #define CL_DEVICE_PARTITION_MAX_SUB_DEVICES 0x1043 #define CL_DEVICE_PARTITION_PROPERTIES 0x1044 #define CL_DEVICE_PARTITION_AFFINITY_DOMAIN 0x1045 #define CL_DEVICE_PARTITION_TYPE 0x1046 #define CL_DEVICE_REFERENCE_COUNT 0x1047 #define CL_DEVICE_PREFERRED_INTEROP_USER_SYNC 0x1048 #define CL_DEVICE_PRINTF_BUFFER_SIZE 0x1049 #endif #ifdef CL_VERSION_2_0 #define CL_DEVICE_IMAGE_PITCH_ALIGNMENT 0x104A #define CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT 0x104B #define CL_DEVICE_MAX_READ_WRITE_IMAGE_ARGS 0x104C #define CL_DEVICE_MAX_GLOBAL_VARIABLE_SIZE 0x104D #define CL_DEVICE_QUEUE_ON_DEVICE_PROPERTIES 0x104E #define CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE 0x104F #define CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE 0x1050 #define CL_DEVICE_MAX_ON_DEVICE_QUEUES 0x1051 #define CL_DEVICE_MAX_ON_DEVICE_EVENTS 0x1052 #define CL_DEVICE_SVM_CAPABILITIES 0x1053 #define CL_DEVICE_GLOBAL_VARIABLE_PREFERRED_TOTAL_SIZE 0x1054 #define CL_DEVICE_MAX_PIPE_ARGS 0x1055 #define CL_DEVICE_PIPE_MAX_ACTIVE_RESERVATIONS 0x1056 #define CL_DEVICE_PIPE_MAX_PACKET_SIZE 0x1057 #define CL_DEVICE_PREFERRED_PLATFORM_ATOMIC_ALIGNMENT 0x1058 #define CL_DEVICE_PREFERRED_GLOBAL_ATOMIC_ALIGNMENT 0x1059 #define CL_DEVICE_PREFERRED_LOCAL_ATOMIC_ALIGNMENT 0x105A #endif #ifdef CL_VERSION_2_1 #define CL_DEVICE_IL_VERSION 0x105B #define CL_DEVICE_MAX_NUM_SUB_GROUPS 0x105C #define CL_DEVICE_SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS 0x105D #endif #ifdef CL_VERSION_3_0 #define CL_DEVICE_NUMERIC_VERSION 0x105E #define CL_DEVICE_EXTENSIONS_WITH_VERSION 0x1060 #define CL_DEVICE_ILS_WITH_VERSION 0x1061 #define CL_DEVICE_BUILT_IN_KERNELS_WITH_VERSION 0x1062 #define CL_DEVICE_ATOMIC_MEMORY_CAPABILITIES 0x1063 #define CL_DEVICE_ATOMIC_FENCE_CAPABILITIES 0x1064 #define CL_DEVICE_NON_UNIFORM_WORK_GROUP_SUPPORT 0x1065 #define CL_DEVICE_OPENCL_C_ALL_VERSIONS 0x1066 #define CL_DEVICE_PREFERRED_WORK_GROUP_SIZE_MULTIPLE 0x1067 #define CL_DEVICE_WORK_GROUP_COLLECTIVE_FUNCTIONS_SUPPORT 0x1068 #define CL_DEVICE_GENERIC_ADDRESS_SPACE_SUPPORT 0x1069 /* 0x106A to 0x106E - Reserved for upcoming KHR extension */ #define CL_DEVICE_OPENCL_C_FEATURES 0x106F #define CL_DEVICE_DEVICE_ENQUEUE_CAPABILITIES 0x1070 #define CL_DEVICE_PIPE_SUPPORT 0x1071 #define CL_DEVICE_LATEST_CONFORMANCE_VERSION_PASSED 0x1072 #endif /* cl_device_fp_config - bitfield */ #define CL_FP_DENORM (1 << 0) #define CL_FP_INF_NAN (1 << 1) #define CL_FP_ROUND_TO_NEAREST (1 << 2) #define CL_FP_ROUND_TO_ZERO (1 << 3) #define CL_FP_ROUND_TO_INF (1 << 4) #define CL_FP_FMA (1 << 5) #ifdef CL_VERSION_1_1 #define CL_FP_SOFT_FLOAT (1 << 6) #endif #ifdef CL_VERSION_1_2 #define CL_FP_CORRECTLY_ROUNDED_DIVIDE_SQRT (1 << 7) #endif /* cl_device_mem_cache_type */ #define CL_NONE 0x0 #define CL_READ_ONLY_CACHE 0x1 #define CL_READ_WRITE_CACHE 0x2 /* cl_device_local_mem_type */ #define CL_LOCAL 0x1 #define CL_GLOBAL 0x2 /* cl_device_exec_capabilities - bitfield */ #define CL_EXEC_KERNEL (1 << 0) #define CL_EXEC_NATIVE_KERNEL (1 << 1) /* cl_command_queue_properties - bitfield */ #define CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE (1 << 0) #define CL_QUEUE_PROFILING_ENABLE (1 << 1) #ifdef CL_VERSION_2_0 #define CL_QUEUE_ON_DEVICE (1 << 2) #define CL_QUEUE_ON_DEVICE_DEFAULT (1 << 3) #endif /* cl_context_info */ #define CL_CONTEXT_REFERENCE_COUNT 0x1080 #define CL_CONTEXT_DEVICES 0x1081 #define CL_CONTEXT_PROPERTIES 0x1082 #ifdef CL_VERSION_1_1 #define CL_CONTEXT_NUM_DEVICES 0x1083 #endif /* cl_context_properties */ #define CL_CONTEXT_PLATFORM 0x1084 #ifdef CL_VERSION_1_2 #define CL_CONTEXT_INTEROP_USER_SYNC 0x1085 #endif #ifdef CL_VERSION_1_2 /* cl_device_partition_property */ #define CL_DEVICE_PARTITION_EQUALLY 0x1086 #define CL_DEVICE_PARTITION_BY_COUNTS 0x1087 #define CL_DEVICE_PARTITION_BY_COUNTS_LIST_END 0x0 #define CL_DEVICE_PARTITION_BY_AFFINITY_DOMAIN 0x1088 #endif #ifdef CL_VERSION_1_2 /* cl_device_affinity_domain */ #define CL_DEVICE_AFFINITY_DOMAIN_NUMA (1 << 0) #define CL_DEVICE_AFFINITY_DOMAIN_L4_CACHE (1 << 1) #define CL_DEVICE_AFFINITY_DOMAIN_L3_CACHE (1 << 2) #define CL_DEVICE_AFFINITY_DOMAIN_L2_CACHE (1 << 3) #define CL_DEVICE_AFFINITY_DOMAIN_L1_CACHE (1 << 4) #define CL_DEVICE_AFFINITY_DOMAIN_NEXT_PARTITIONABLE (1 << 5) #endif #ifdef CL_VERSION_2_0 /* cl_device_svm_capabilities */ #define CL_DEVICE_SVM_COARSE_GRAIN_BUFFER (1 << 0) #define CL_DEVICE_SVM_FINE_GRAIN_BUFFER (1 << 1) #define CL_DEVICE_SVM_FINE_GRAIN_SYSTEM (1 << 2) #define CL_DEVICE_SVM_ATOMICS (1 << 3) #endif /* cl_command_queue_info */ #define CL_QUEUE_CONTEXT 0x1090 #define CL_QUEUE_DEVICE 0x1091 #define CL_QUEUE_REFERENCE_COUNT 0x1092 #define CL_QUEUE_PROPERTIES 0x1093 #ifdef CL_VERSION_2_0 #define CL_QUEUE_SIZE 0x1094 #endif #ifdef CL_VERSION_2_1 #define CL_QUEUE_DEVICE_DEFAULT 0x1095 #endif #ifdef CL_VERSION_3_0 #define CL_QUEUE_PROPERTIES_ARRAY 0x1098 #endif /* cl_mem_flags and cl_svm_mem_flags - bitfield */ #define CL_MEM_READ_WRITE (1 << 0) #define CL_MEM_WRITE_ONLY (1 << 1) #define CL_MEM_READ_ONLY (1 << 2) #define CL_MEM_USE_HOST_PTR (1 << 3) #define CL_MEM_ALLOC_HOST_PTR (1 << 4) #define CL_MEM_COPY_HOST_PTR (1 << 5) /* reserved (1 << 6) */ #ifdef CL_VERSION_1_2 #define CL_MEM_HOST_WRITE_ONLY (1 << 7) #define CL_MEM_HOST_READ_ONLY (1 << 8) #define CL_MEM_HOST_NO_ACCESS (1 << 9) #endif #ifdef CL_VERSION_2_0 #define CL_MEM_SVM_FINE_GRAIN_BUFFER (1 << 10) /* used by cl_svm_mem_flags only */ #define CL_MEM_SVM_ATOMICS (1 << 11) /* used by cl_svm_mem_flags only */ #define CL_MEM_KERNEL_READ_AND_WRITE (1 << 12) #endif #ifdef CL_VERSION_1_2 /* cl_mem_migration_flags - bitfield */ #define CL_MIGRATE_MEM_OBJECT_HOST (1 << 0) #define CL_MIGRATE_MEM_OBJECT_CONTENT_UNDEFINED (1 << 1) #endif /* cl_channel_order */ #define CL_R 0x10B0 #define CL_A 0x10B1 #define CL_RG 0x10B2 #define CL_RA 0x10B3 #define CL_RGB 0x10B4 #define CL_RGBA 0x10B5 #define CL_BGRA 0x10B6 #define CL_ARGB 0x10B7 #define CL_INTENSITY 0x10B8 #define CL_LUMINANCE 0x10B9 #ifdef CL_VERSION_1_1 #define CL_Rx 0x10BA #define CL_RGx 0x10BB #define CL_RGBx 0x10BC #endif #ifdef CL_VERSION_1_2 #define CL_DEPTH 0x10BD #define CL_DEPTH_STENCIL 0x10BE #endif #ifdef CL_VERSION_2_0 #define CL_sRGB 0x10BF #define CL_sRGBx 0x10C0 #define CL_sRGBA 0x10C1 #define CL_sBGRA 0x10C2 #define CL_ABGR 0x10C3 #endif /* cl_channel_type */ #define CL_SNORM_INT8 0x10D0 #define CL_SNORM_INT16 0x10D1 #define CL_UNORM_INT8 0x10D2 #define CL_UNORM_INT16 0x10D3 #define CL_UNORM_SHORT_565 0x10D4 #define CL_UNORM_SHORT_555 0x10D5 #define CL_UNORM_INT_101010 0x10D6 #define CL_SIGNED_INT8 0x10D7 #define CL_SIGNED_INT16 0x10D8 #define CL_SIGNED_INT32 0x10D9 #define CL_UNSIGNED_INT8 0x10DA #define CL_UNSIGNED_INT16 0x10DB #define CL_UNSIGNED_INT32 0x10DC #define CL_HALF_FLOAT 0x10DD #define CL_FLOAT 0x10DE #ifdef CL_VERSION_1_2 #define CL_UNORM_INT24 0x10DF #endif #ifdef CL_VERSION_2_1 #define CL_UNORM_INT_101010_2 0x10E0 #endif /* cl_mem_object_type */ #define CL_MEM_OBJECT_BUFFER 0x10F0 #define CL_MEM_OBJECT_IMAGE2D 0x10F1 #define CL_MEM_OBJECT_IMAGE3D 0x10F2 #ifdef CL_VERSION_1_2 #define CL_MEM_OBJECT_IMAGE2D_ARRAY 0x10F3 #define CL_MEM_OBJECT_IMAGE1D 0x10F4 #define CL_MEM_OBJECT_IMAGE1D_ARRAY 0x10F5 #define CL_MEM_OBJECT_IMAGE1D_BUFFER 0x10F6 #endif #ifdef CL_VERSION_2_0 #define CL_MEM_OBJECT_PIPE 0x10F7 #endif /* cl_mem_info */ #define CL_MEM_TYPE 0x1100 #define CL_MEM_FLAGS 0x1101 #define CL_MEM_SIZE 0x1102 #define CL_MEM_HOST_PTR 0x1103 #define CL_MEM_MAP_COUNT 0x1104 #define CL_MEM_REFERENCE_COUNT 0x1105 #define CL_MEM_CONTEXT 0x1106 #ifdef CL_VERSION_1_1 #define CL_MEM_ASSOCIATED_MEMOBJECT 0x1107 #define CL_MEM_OFFSET 0x1108 #endif #ifdef CL_VERSION_2_0 #define CL_MEM_USES_SVM_POINTER 0x1109 #endif #ifdef CL_VERSION_3_0 #define CL_MEM_PROPERTIES 0x110A #endif /* cl_image_info */ #define CL_IMAGE_FORMAT 0x1110 #define CL_IMAGE_ELEMENT_SIZE 0x1111 #define CL_IMAGE_ROW_PITCH 0x1112 #define CL_IMAGE_SLICE_PITCH 0x1113 #define CL_IMAGE_WIDTH 0x1114 #define CL_IMAGE_HEIGHT 0x1115 #define CL_IMAGE_DEPTH 0x1116 #ifdef CL_VERSION_1_2 #define CL_IMAGE_ARRAY_SIZE 0x1117 #define CL_IMAGE_BUFFER 0x1118 #define CL_IMAGE_NUM_MIP_LEVELS 0x1119 #define CL_IMAGE_NUM_SAMPLES 0x111A #endif /* cl_pipe_info */ #ifdef CL_VERSION_2_0 #define CL_PIPE_PACKET_SIZE 0x1120 #define CL_PIPE_MAX_PACKETS 0x1121 #endif #ifdef CL_VERSION_3_0 #define CL_PIPE_PROPERTIES 0x1122 #endif /* cl_addressing_mode */ #define CL_ADDRESS_NONE 0x1130 #define CL_ADDRESS_CLAMP_TO_EDGE 0x1131 #define CL_ADDRESS_CLAMP 0x1132 #define CL_ADDRESS_REPEAT 0x1133 #ifdef CL_VERSION_1_1 #define CL_ADDRESS_MIRRORED_REPEAT 0x1134 #endif /* cl_filter_mode */ #define CL_FILTER_NEAREST 0x1140 #define CL_FILTER_LINEAR 0x1141 /* cl_sampler_info */ #define CL_SAMPLER_REFERENCE_COUNT 0x1150 #define CL_SAMPLER_CONTEXT 0x1151 #define CL_SAMPLER_NORMALIZED_COORDS 0x1152 #define CL_SAMPLER_ADDRESSING_MODE 0x1153 #define CL_SAMPLER_FILTER_MODE 0x1154 #ifdef CL_VERSION_2_0 /* These enumerants are for the cl_khr_mipmap_image extension. They have since been added to cl_ext.h with an appropriate KHR suffix, but are left here for backwards compatibility. */ #define CL_SAMPLER_MIP_FILTER_MODE 0x1155 #define CL_SAMPLER_LOD_MIN 0x1156 #define CL_SAMPLER_LOD_MAX 0x1157 #endif #ifdef CL_VERSION_3_0 #define CL_SAMPLER_PROPERTIES 0x1158 #endif /* cl_map_flags - bitfield */ #define CL_MAP_READ (1 << 0) #define CL_MAP_WRITE (1 << 1) #ifdef CL_VERSION_1_2 #define CL_MAP_WRITE_INVALIDATE_REGION (1 << 2) #endif /* cl_program_info */ #define CL_PROGRAM_REFERENCE_COUNT 0x1160 #define CL_PROGRAM_CONTEXT 0x1161 #define CL_PROGRAM_NUM_DEVICES 0x1162 #define CL_PROGRAM_DEVICES 0x1163 #define CL_PROGRAM_SOURCE 0x1164 #define CL_PROGRAM_BINARY_SIZES 0x1165 #define CL_PROGRAM_BINARIES 0x1166 #ifdef CL_VERSION_1_2 #define CL_PROGRAM_NUM_KERNELS 0x1167 #define CL_PROGRAM_KERNEL_NAMES 0x1168 #endif #ifdef CL_VERSION_2_1 #define CL_PROGRAM_IL 0x1169 #endif #ifdef CL_VERSION_2_2 #define CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT 0x116A #define CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT 0x116B #endif /* cl_program_build_info */ #define CL_PROGRAM_BUILD_STATUS 0x1181 #define CL_PROGRAM_BUILD_OPTIONS 0x1182 #define CL_PROGRAM_BUILD_LOG 0x1183 #ifdef CL_VERSION_1_2 #define CL_PROGRAM_BINARY_TYPE 0x1184 #endif #ifdef CL_VERSION_2_0 #define CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE 0x1185 #endif #ifdef CL_VERSION_1_2 /* cl_program_binary_type */ #define CL_PROGRAM_BINARY_TYPE_NONE 0x0 #define CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT 0x1 #define CL_PROGRAM_BINARY_TYPE_LIBRARY 0x2 #define CL_PROGRAM_BINARY_TYPE_EXECUTABLE 0x4 #endif /* cl_build_status */ #define CL_BUILD_SUCCESS 0 #define CL_BUILD_NONE -1 #define CL_BUILD_ERROR -2 #define CL_BUILD_IN_PROGRESS -3 /* cl_kernel_info */ #define CL_KERNEL_FUNCTION_NAME 0x1190 #define CL_KERNEL_NUM_ARGS 0x1191 #define CL_KERNEL_REFERENCE_COUNT 0x1192 #define CL_KERNEL_CONTEXT 0x1193 #define CL_KERNEL_PROGRAM 0x1194 #ifdef CL_VERSION_1_2 #define CL_KERNEL_ATTRIBUTES 0x1195 #endif #ifdef CL_VERSION_1_2 /* cl_kernel_arg_info */ #define CL_KERNEL_ARG_ADDRESS_QUALIFIER 0x1196 #define CL_KERNEL_ARG_ACCESS_QUALIFIER 0x1197 #define CL_KERNEL_ARG_TYPE_NAME 0x1198 #define CL_KERNEL_ARG_TYPE_QUALIFIER 0x1199 #define CL_KERNEL_ARG_NAME 0x119A #endif #ifdef CL_VERSION_1_2 /* cl_kernel_arg_address_qualifier */ #define CL_KERNEL_ARG_ADDRESS_GLOBAL 0x119B #define CL_KERNEL_ARG_ADDRESS_LOCAL 0x119C #define CL_KERNEL_ARG_ADDRESS_CONSTANT 0x119D #define CL_KERNEL_ARG_ADDRESS_PRIVATE 0x119E #endif #ifdef CL_VERSION_1_2 /* cl_kernel_arg_access_qualifier */ #define CL_KERNEL_ARG_ACCESS_READ_ONLY 0x11A0 #define CL_KERNEL_ARG_ACCESS_WRITE_ONLY 0x11A1 #define CL_KERNEL_ARG_ACCESS_READ_WRITE 0x11A2 #define CL_KERNEL_ARG_ACCESS_NONE 0x11A3 #endif #ifdef CL_VERSION_1_2 /* cl_kernel_arg_type_qualifier */ #define CL_KERNEL_ARG_TYPE_NONE 0 #define CL_KERNEL_ARG_TYPE_CONST (1 << 0) #define CL_KERNEL_ARG_TYPE_RESTRICT (1 << 1) #define CL_KERNEL_ARG_TYPE_VOLATILE (1 << 2) #ifdef CL_VERSION_2_0 #define CL_KERNEL_ARG_TYPE_PIPE (1 << 3) #endif #endif /* cl_kernel_work_group_info */ #define CL_KERNEL_WORK_GROUP_SIZE 0x11B0 #define CL_KERNEL_COMPILE_WORK_GROUP_SIZE 0x11B1 #define CL_KERNEL_LOCAL_MEM_SIZE 0x11B2 #define CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE 0x11B3 #define CL_KERNEL_PRIVATE_MEM_SIZE 0x11B4 #ifdef CL_VERSION_1_2 #define CL_KERNEL_GLOBAL_WORK_SIZE 0x11B5 #endif #ifdef CL_VERSION_2_1 /* cl_kernel_sub_group_info */ #define CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE 0x2033 #define CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE 0x2034 #define CL_KERNEL_LOCAL_SIZE_FOR_SUB_GROUP_COUNT 0x11B8 #define CL_KERNEL_MAX_NUM_SUB_GROUPS 0x11B9 #define CL_KERNEL_COMPILE_NUM_SUB_GROUPS 0x11BA #endif #ifdef CL_VERSION_2_0 /* cl_kernel_exec_info */ #define CL_KERNEL_EXEC_INFO_SVM_PTRS 0x11B6 #define CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM 0x11B7 #endif /* cl_event_info */ #define CL_EVENT_COMMAND_QUEUE 0x11D0 #define CL_EVENT_COMMAND_TYPE 0x11D1 #define CL_EVENT_REFERENCE_COUNT 0x11D2 #define CL_EVENT_COMMAND_EXECUTION_STATUS 0x11D3 #ifdef CL_VERSION_1_1 #define CL_EVENT_CONTEXT 0x11D4 #endif /* cl_command_type */ #define CL_COMMAND_NDRANGE_KERNEL 0x11F0 #define CL_COMMAND_TASK 0x11F1 #define CL_COMMAND_NATIVE_KERNEL 0x11F2 #define CL_COMMAND_READ_BUFFER 0x11F3 #define CL_COMMAND_WRITE_BUFFER 0x11F4 #define CL_COMMAND_COPY_BUFFER 0x11F5 #define CL_COMMAND_READ_IMAGE 0x11F6 #define CL_COMMAND_WRITE_IMAGE 0x11F7 #define CL_COMMAND_COPY_IMAGE 0x11F8 #define CL_COMMAND_COPY_IMAGE_TO_BUFFER 0x11F9 #define CL_COMMAND_COPY_BUFFER_TO_IMAGE 0x11FA #define CL_COMMAND_MAP_BUFFER 0x11FB #define CL_COMMAND_MAP_IMAGE 0x11FC #define CL_COMMAND_UNMAP_MEM_OBJECT 0x11FD #define CL_COMMAND_MARKER 0x11FE #define CL_COMMAND_ACQUIRE_GL_OBJECTS 0x11FF #define CL_COMMAND_RELEASE_GL_OBJECTS 0x1200 #ifdef CL_VERSION_1_1 #define CL_COMMAND_READ_BUFFER_RECT 0x1201 #define CL_COMMAND_WRITE_BUFFER_RECT 0x1202 #define CL_COMMAND_COPY_BUFFER_RECT 0x1203 #define CL_COMMAND_USER 0x1204 #endif #ifdef CL_VERSION_1_2 #define CL_COMMAND_BARRIER 0x1205 #define CL_COMMAND_MIGRATE_MEM_OBJECTS 0x1206 #define CL_COMMAND_FILL_BUFFER 0x1207 #define CL_COMMAND_FILL_IMAGE 0x1208 #endif #ifdef CL_VERSION_2_0 #define CL_COMMAND_SVM_FREE 0x1209 #define CL_COMMAND_SVM_MEMCPY 0x120A #define CL_COMMAND_SVM_MEMFILL 0x120B #define CL_COMMAND_SVM_MAP 0x120C #define CL_COMMAND_SVM_UNMAP 0x120D #endif #ifdef CL_VERSION_3_0 #define CL_COMMAND_SVM_MIGRATE_MEM 0x120E #endif /* command execution status */ #define CL_COMPLETE 0x0 #define CL_RUNNING 0x1 #define CL_SUBMITTED 0x2 #define CL_QUEUED 0x3 /* cl_buffer_create_type */ #ifdef CL_VERSION_1_1 #define CL_BUFFER_CREATE_TYPE_REGION 0x1220 #endif /* cl_profiling_info */ #define CL_PROFILING_COMMAND_QUEUED 0x1280 #define CL_PROFILING_COMMAND_SUBMIT 0x1281 #define CL_PROFILING_COMMAND_START 0x1282 #define CL_PROFILING_COMMAND_END 0x1283 #ifdef CL_VERSION_2_0 #define CL_PROFILING_COMMAND_COMPLETE 0x1284 #endif /* cl_device_atomic_capabilities - bitfield */ #ifdef CL_VERSION_3_0 #define CL_DEVICE_ATOMIC_ORDER_RELAXED (1 << 0) #define CL_DEVICE_ATOMIC_ORDER_ACQ_REL (1 << 1) #define CL_DEVICE_ATOMIC_ORDER_SEQ_CST (1 << 2) #define CL_DEVICE_ATOMIC_SCOPE_WORK_ITEM (1 << 3) #define CL_DEVICE_ATOMIC_SCOPE_WORK_GROUP (1 << 4) #define CL_DEVICE_ATOMIC_SCOPE_DEVICE (1 << 5) #define CL_DEVICE_ATOMIC_SCOPE_ALL_DEVICES (1 << 6) #endif /* cl_device_device_enqueue_capabilities - bitfield */ #ifdef CL_VERSION_3_0 #define CL_DEVICE_QUEUE_SUPPORTED (1 << 0) #define CL_DEVICE_QUEUE_REPLACEABLE_DEFAULT (1 << 1) #endif /* cl_khronos_vendor_id */ #define CL_KHRONOS_VENDOR_ID_CODEPLAY 0x10004 #ifdef CL_VERSION_3_0 /* cl_version */ #define CL_VERSION_MAJOR_BITS (10) #define CL_VERSION_MINOR_BITS (10) #define CL_VERSION_PATCH_BITS (12) #define CL_VERSION_MAJOR_MASK ((1 << CL_VERSION_MAJOR_BITS) - 1) #define CL_VERSION_MINOR_MASK ((1 << CL_VERSION_MINOR_BITS) - 1) #define CL_VERSION_PATCH_MASK ((1 << CL_VERSION_PATCH_BITS) - 1) #define CL_VERSION_MAJOR(version) \ ((version) >> (CL_VERSION_MINOR_BITS + CL_VERSION_PATCH_BITS)) #define CL_VERSION_MINOR(version) \ (((version) >> CL_VERSION_PATCH_BITS) & CL_VERSION_MINOR_MASK) #define CL_VERSION_PATCH(version) ((version) & CL_VERSION_PATCH_MASK) #define CL_MAKE_VERSION(major, minor, patch) \ ((((major) & CL_VERSION_MAJOR_MASK) \ << (CL_VERSION_MINOR_BITS + CL_VERSION_PATCH_BITS)) | \ (((minor) & CL_VERSION_MINOR_MASK) << CL_VERSION_PATCH_BITS) | \ ((patch) & CL_VERSION_PATCH_MASK)) #endif /********************************************************************************************************/ /* CL_NO_PROTOTYPES implies CL_NO_CORE_PROTOTYPES: */ #if defined(CL_NO_PROTOTYPES) && !defined(CL_NO_CORE_PROTOTYPES) #define CL_NO_CORE_PROTOTYPES #endif #if !defined(CL_NO_CORE_PROTOTYPES) /* Platform API */ extern CL_API_ENTRY cl_int CL_API_CALL clGetPlatformIDs(cl_uint num_entries, cl_platform_id * platforms, cl_uint * num_platforms) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetPlatformInfo(cl_platform_id platform, cl_platform_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; /* Device APIs */ extern CL_API_ENTRY cl_int CL_API_CALL clGetDeviceIDs(cl_platform_id platform, cl_device_type device_type, cl_uint num_entries, cl_device_id * devices, cl_uint * num_devices) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetDeviceInfo(cl_device_id device, cl_device_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_1_2 extern CL_API_ENTRY cl_int CL_API_CALL clCreateSubDevices(cl_device_id in_device, const cl_device_partition_property * properties, cl_uint num_devices, cl_device_id * out_devices, cl_uint * num_devices_ret) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clRetainDevice(cl_device_id device) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseDevice(cl_device_id device) CL_API_SUFFIX__VERSION_1_2; #endif #ifdef CL_VERSION_2_1 extern CL_API_ENTRY cl_int CL_API_CALL clSetDefaultDeviceCommandQueue(cl_context context, cl_device_id device, cl_command_queue command_queue) CL_API_SUFFIX__VERSION_2_1; extern CL_API_ENTRY cl_int CL_API_CALL clGetDeviceAndHostTimer(cl_device_id device, cl_ulong* device_timestamp, cl_ulong* host_timestamp) CL_API_SUFFIX__VERSION_2_1; extern CL_API_ENTRY cl_int CL_API_CALL clGetHostTimer(cl_device_id device, cl_ulong * host_timestamp) CL_API_SUFFIX__VERSION_2_1; #endif /* Context APIs */ extern CL_API_ENTRY cl_context CL_API_CALL clCreateContext(const cl_context_properties * properties, cl_uint num_devices, const cl_device_id * devices, void (CL_CALLBACK * pfn_notify)(const char * errinfo, const void * private_info, size_t cb, void * user_data), void * user_data, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_context CL_API_CALL clCreateContextFromType(const cl_context_properties * properties, cl_device_type device_type, void (CL_CALLBACK * pfn_notify)(const char * errinfo, const void * private_info, size_t cb, void * user_data), void * user_data, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clRetainContext(cl_context context) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseContext(cl_context context) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetContextInfo(cl_context context, cl_context_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_3_0 extern CL_API_ENTRY cl_int CL_API_CALL clSetContextDestructorCallback(cl_context context, void (CL_CALLBACK* pfn_notify)(cl_context context, void* user_data), void* user_data) CL_API_SUFFIX__VERSION_3_0; #endif /* Command Queue APIs */ #ifdef CL_VERSION_2_0 extern CL_API_ENTRY cl_command_queue CL_API_CALL clCreateCommandQueueWithProperties(cl_context context, cl_device_id device, const cl_queue_properties * properties, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_2_0; #endif extern CL_API_ENTRY cl_int CL_API_CALL clRetainCommandQueue(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseCommandQueue(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetCommandQueueInfo(cl_command_queue command_queue, cl_command_queue_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; /* Memory Object APIs */ extern CL_API_ENTRY cl_mem CL_API_CALL clCreateBuffer(cl_context context, cl_mem_flags flags, size_t size, void * host_ptr, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_1_1 extern CL_API_ENTRY cl_mem CL_API_CALL clCreateSubBuffer(cl_mem buffer, cl_mem_flags flags, cl_buffer_create_type buffer_create_type, const void * buffer_create_info, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_1; #endif #ifdef CL_VERSION_1_2 extern CL_API_ENTRY cl_mem CL_API_CALL clCreateImage(cl_context context, cl_mem_flags flags, const cl_image_format * image_format, const cl_image_desc * image_desc, void * host_ptr, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_2; #endif #ifdef CL_VERSION_2_0 extern CL_API_ENTRY cl_mem CL_API_CALL clCreatePipe(cl_context context, cl_mem_flags flags, cl_uint pipe_packet_size, cl_uint pipe_max_packets, const cl_pipe_properties * properties, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_2_0; #endif #ifdef CL_VERSION_3_0 extern CL_API_ENTRY cl_mem CL_API_CALL clCreateBufferWithProperties(cl_context context, const cl_mem_properties * properties, cl_mem_flags flags, size_t size, void * host_ptr, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_3_0; extern CL_API_ENTRY cl_mem CL_API_CALL clCreateImageWithProperties(cl_context context, const cl_mem_properties * properties, cl_mem_flags flags, const cl_image_format * image_format, const cl_image_desc * image_desc, void * host_ptr, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_3_0; #endif extern CL_API_ENTRY cl_int CL_API_CALL clRetainMemObject(cl_mem memobj) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseMemObject(cl_mem memobj) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetSupportedImageFormats(cl_context context, cl_mem_flags flags, cl_mem_object_type image_type, cl_uint num_entries, cl_image_format * image_formats, cl_uint * num_image_formats) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetMemObjectInfo(cl_mem memobj, cl_mem_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetImageInfo(cl_mem image, cl_image_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_2_0 extern CL_API_ENTRY cl_int CL_API_CALL clGetPipeInfo(cl_mem pipe, cl_pipe_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_2_0; #endif #ifdef CL_VERSION_1_1 extern CL_API_ENTRY cl_int CL_API_CALL clSetMemObjectDestructorCallback(cl_mem memobj, void (CL_CALLBACK * pfn_notify)(cl_mem memobj, void * user_data), void * user_data) CL_API_SUFFIX__VERSION_1_1; #endif /* SVM Allocation APIs */ #ifdef CL_VERSION_2_0 extern CL_API_ENTRY void * CL_API_CALL clSVMAlloc(cl_context context, cl_svm_mem_flags flags, size_t size, cl_uint alignment) CL_API_SUFFIX__VERSION_2_0; extern CL_API_ENTRY void CL_API_CALL clSVMFree(cl_context context, void * svm_pointer) CL_API_SUFFIX__VERSION_2_0; #endif /* Sampler APIs */ #ifdef CL_VERSION_2_0 extern CL_API_ENTRY cl_sampler CL_API_CALL clCreateSamplerWithProperties(cl_context context, const cl_sampler_properties * sampler_properties, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_2_0; #endif extern CL_API_ENTRY cl_int CL_API_CALL clRetainSampler(cl_sampler sampler) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseSampler(cl_sampler sampler) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetSamplerInfo(cl_sampler sampler, cl_sampler_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; /* Program Object APIs */ extern CL_API_ENTRY cl_program CL_API_CALL clCreateProgramWithSource(cl_context context, cl_uint count, const char ** strings, const size_t * lengths, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_program CL_API_CALL clCreateProgramWithBinary(cl_context context, cl_uint num_devices, const cl_device_id * device_list, const size_t * lengths, const unsigned char ** binaries, cl_int * binary_status, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_1_2 extern CL_API_ENTRY cl_program CL_API_CALL clCreateProgramWithBuiltInKernels(cl_context context, cl_uint num_devices, const cl_device_id * device_list, const char * kernel_names, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_2; #endif #ifdef CL_VERSION_2_1 extern CL_API_ENTRY cl_program CL_API_CALL clCreateProgramWithIL(cl_context context, const void* il, size_t length, cl_int* errcode_ret) CL_API_SUFFIX__VERSION_2_1; #endif extern CL_API_ENTRY cl_int CL_API_CALL clRetainProgram(cl_program program) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseProgram(cl_program program) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clBuildProgram(cl_program program, cl_uint num_devices, const cl_device_id * device_list, const char * options, void (CL_CALLBACK * pfn_notify)(cl_program program, void * user_data), void * user_data) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_1_2 extern CL_API_ENTRY cl_int CL_API_CALL clCompileProgram(cl_program program, cl_uint num_devices, const cl_device_id * device_list, const char * options, cl_uint num_input_headers, const cl_program * input_headers, const char ** header_include_names, void (CL_CALLBACK * pfn_notify)(cl_program program, void * user_data), void * user_data) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_program CL_API_CALL clLinkProgram(cl_context context, cl_uint num_devices, const cl_device_id * device_list, const char * options, cl_uint num_input_programs, const cl_program * input_programs, void (CL_CALLBACK * pfn_notify)(cl_program program, void * user_data), void * user_data, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_2; #endif #ifdef CL_VERSION_2_2 extern CL_API_ENTRY CL_API_PREFIX__VERSION_2_2_DEPRECATED cl_int CL_API_CALL clSetProgramReleaseCallback(cl_program program, void (CL_CALLBACK * pfn_notify)(cl_program program, void * user_data), void * user_data) CL_API_SUFFIX__VERSION_2_2_DEPRECATED; extern CL_API_ENTRY cl_int CL_API_CALL clSetProgramSpecializationConstant(cl_program program, cl_uint spec_id, size_t spec_size, const void* spec_value) CL_API_SUFFIX__VERSION_2_2; #endif #ifdef CL_VERSION_1_2 extern CL_API_ENTRY cl_int CL_API_CALL clUnloadPlatformCompiler(cl_platform_id platform) CL_API_SUFFIX__VERSION_1_2; #endif extern CL_API_ENTRY cl_int CL_API_CALL clGetProgramInfo(cl_program program, cl_program_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetProgramBuildInfo(cl_program program, cl_device_id device, cl_program_build_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; /* Kernel Object APIs */ extern CL_API_ENTRY cl_kernel CL_API_CALL clCreateKernel(cl_program program, const char * kernel_name, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clCreateKernelsInProgram(cl_program program, cl_uint num_kernels, cl_kernel * kernels, cl_uint * num_kernels_ret) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_2_1 extern CL_API_ENTRY cl_kernel CL_API_CALL clCloneKernel(cl_kernel source_kernel, cl_int* errcode_ret) CL_API_SUFFIX__VERSION_2_1; #endif extern CL_API_ENTRY cl_int CL_API_CALL clRetainKernel(cl_kernel kernel) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseKernel(cl_kernel kernel) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clSetKernelArg(cl_kernel kernel, cl_uint arg_index, size_t arg_size, const void * arg_value) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_2_0 extern CL_API_ENTRY cl_int CL_API_CALL clSetKernelArgSVMPointer(cl_kernel kernel, cl_uint arg_index, const void * arg_value) CL_API_SUFFIX__VERSION_2_0; extern CL_API_ENTRY cl_int CL_API_CALL clSetKernelExecInfo(cl_kernel kernel, cl_kernel_exec_info param_name, size_t param_value_size, const void * param_value) CL_API_SUFFIX__VERSION_2_0; #endif extern CL_API_ENTRY cl_int CL_API_CALL clGetKernelInfo(cl_kernel kernel, cl_kernel_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_1_2 extern CL_API_ENTRY cl_int CL_API_CALL clGetKernelArgInfo(cl_kernel kernel, cl_uint arg_indx, cl_kernel_arg_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_2; #endif extern CL_API_ENTRY cl_int CL_API_CALL clGetKernelWorkGroupInfo(cl_kernel kernel, cl_device_id device, cl_kernel_work_group_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_2_1 extern CL_API_ENTRY cl_int CL_API_CALL clGetKernelSubGroupInfo(cl_kernel kernel, cl_device_id device, cl_kernel_sub_group_info param_name, size_t input_value_size, const void* input_value, size_t param_value_size, void* param_value, size_t* param_value_size_ret) CL_API_SUFFIX__VERSION_2_1; #endif /* Event Object APIs */ extern CL_API_ENTRY cl_int CL_API_CALL clWaitForEvents(cl_uint num_events, const cl_event * event_list) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clGetEventInfo(cl_event event, cl_event_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_1_1 extern CL_API_ENTRY cl_event CL_API_CALL clCreateUserEvent(cl_context context, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_1; #endif extern CL_API_ENTRY cl_int CL_API_CALL clRetainEvent(cl_event event) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clReleaseEvent(cl_event event) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_1_1 extern CL_API_ENTRY cl_int CL_API_CALL clSetUserEventStatus(cl_event event, cl_int execution_status) CL_API_SUFFIX__VERSION_1_1; extern CL_API_ENTRY cl_int CL_API_CALL clSetEventCallback(cl_event event, cl_int command_exec_callback_type, void (CL_CALLBACK * pfn_notify)(cl_event event, cl_int event_command_status, void * user_data), void * user_data) CL_API_SUFFIX__VERSION_1_1; #endif /* Profiling APIs */ extern CL_API_ENTRY cl_int CL_API_CALL clGetEventProfilingInfo(cl_event event, cl_profiling_info param_name, size_t param_value_size, void * param_value, size_t * param_value_size_ret) CL_API_SUFFIX__VERSION_1_0; /* Flush and Finish APIs */ extern CL_API_ENTRY cl_int CL_API_CALL clFlush(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clFinish(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_0; /* Enqueued Commands APIs */ extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueReadBuffer(cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_read, size_t offset, size_t size, void * ptr, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_1_1 extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueReadBufferRect(cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_read, const size_t * buffer_origin, const size_t * host_origin, const size_t * region, size_t buffer_row_pitch, size_t buffer_slice_pitch, size_t host_row_pitch, size_t host_slice_pitch, void * ptr, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_1; #endif extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueWriteBuffer(cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_write, size_t offset, size_t size, const void * ptr, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_1_1 extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueWriteBufferRect(cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_write, const size_t * buffer_origin, const size_t * host_origin, const size_t * region, size_t buffer_row_pitch, size_t buffer_slice_pitch, size_t host_row_pitch, size_t host_slice_pitch, const void * ptr, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_1; #endif #ifdef CL_VERSION_1_2 extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueFillBuffer(cl_command_queue command_queue, cl_mem buffer, const void * pattern, size_t pattern_size, size_t offset, size_t size, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_2; #endif extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueCopyBuffer(cl_command_queue command_queue, cl_mem src_buffer, cl_mem dst_buffer, size_t src_offset, size_t dst_offset, size_t size, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_1_1 extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueCopyBufferRect(cl_command_queue command_queue, cl_mem src_buffer, cl_mem dst_buffer, const size_t * src_origin, const size_t * dst_origin, const size_t * region, size_t src_row_pitch, size_t src_slice_pitch, size_t dst_row_pitch, size_t dst_slice_pitch, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_1; #endif extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueReadImage(cl_command_queue command_queue, cl_mem image, cl_bool blocking_read, const size_t * origin, const size_t * region, size_t row_pitch, size_t slice_pitch, void * ptr, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueWriteImage(cl_command_queue command_queue, cl_mem image, cl_bool blocking_write, const size_t * origin, const size_t * region, size_t input_row_pitch, size_t input_slice_pitch, const void * ptr, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_1_2 extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueFillImage(cl_command_queue command_queue, cl_mem image, const void * fill_color, const size_t * origin, const size_t * region, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_2; #endif extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueCopyImage(cl_command_queue command_queue, cl_mem src_image, cl_mem dst_image, const size_t * src_origin, const size_t * dst_origin, const size_t * region, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueCopyImageToBuffer(cl_command_queue command_queue, cl_mem src_image, cl_mem dst_buffer, const size_t * src_origin, const size_t * region, size_t dst_offset, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueCopyBufferToImage(cl_command_queue command_queue, cl_mem src_buffer, cl_mem dst_image, size_t src_offset, const size_t * dst_origin, const size_t * region, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY void * CL_API_CALL clEnqueueMapBuffer(cl_command_queue command_queue, cl_mem buffer, cl_bool blocking_map, cl_map_flags map_flags, size_t offset, size_t size, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY void * CL_API_CALL clEnqueueMapImage(cl_command_queue command_queue, cl_mem image, cl_bool blocking_map, cl_map_flags map_flags, const size_t * origin, const size_t * region, size_t * image_row_pitch, size_t * image_slice_pitch, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueUnmapMemObject(cl_command_queue command_queue, cl_mem memobj, void * mapped_ptr, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_1_2 extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueMigrateMemObjects(cl_command_queue command_queue, cl_uint num_mem_objects, const cl_mem * mem_objects, cl_mem_migration_flags flags, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_2; #endif extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueNDRangeKernel(cl_command_queue command_queue, cl_kernel kernel, cl_uint work_dim, const size_t * global_work_offset, const size_t * global_work_size, const size_t * local_work_size, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueNativeKernel(cl_command_queue command_queue, void (CL_CALLBACK * user_func)(void *), void * args, size_t cb_args, cl_uint num_mem_objects, const cl_mem * mem_list, const void ** args_mem_loc, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_1_2 extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueMarkerWithWaitList(cl_command_queue command_queue, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_2; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueBarrierWithWaitList(cl_command_queue command_queue, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_2; #endif #ifdef CL_VERSION_2_0 extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueSVMFree(cl_command_queue command_queue, cl_uint num_svm_pointers, void * svm_pointers[], void (CL_CALLBACK * pfn_free_func)(cl_command_queue queue, cl_uint num_svm_pointers, void * svm_pointers[], void * user_data), void * user_data, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_2_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueSVMMemcpy(cl_command_queue command_queue, cl_bool blocking_copy, void * dst_ptr, const void * src_ptr, size_t size, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_2_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueSVMMemFill(cl_command_queue command_queue, void * svm_ptr, const void * pattern, size_t pattern_size, size_t size, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_2_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueSVMMap(cl_command_queue command_queue, cl_bool blocking_map, cl_map_flags flags, void * svm_ptr, size_t size, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_2_0; extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueSVMUnmap(cl_command_queue command_queue, void * svm_ptr, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_2_0; #endif #ifdef CL_VERSION_2_1 extern CL_API_ENTRY cl_int CL_API_CALL clEnqueueSVMMigrateMem(cl_command_queue command_queue, cl_uint num_svm_pointers, const void ** svm_pointers, const size_t * sizes, cl_mem_migration_flags flags, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_2_1; #endif #ifdef CL_VERSION_1_2 /* Extension function access * * Returns the extension function address for the given function name, * or NULL if a valid function can not be found. The client must * check to make sure the address is not NULL, before using or * calling the returned function address. */ extern CL_API_ENTRY void * CL_API_CALL clGetExtensionFunctionAddressForPlatform(cl_platform_id platform, const char * func_name) CL_API_SUFFIX__VERSION_1_2; #endif #ifdef CL_USE_DEPRECATED_OPENCL_1_0_APIS /* * WARNING: * This API introduces mutable state into the OpenCL implementation. It has been REMOVED * to better facilitate thread safety. The 1.0 API is not thread safe. It is not tested by the * OpenCL 1.1 conformance test, and consequently may not work or may not work dependably. * It is likely to be non-performant. Use of this API is not advised. Use at your own risk. * * Software developers previously relying on this API are instructed to set the command queue * properties when creating the queue, instead. */ extern CL_API_ENTRY cl_int CL_API_CALL clSetCommandQueueProperty(cl_command_queue command_queue, cl_command_queue_properties properties, cl_bool enable, cl_command_queue_properties * old_properties) CL_API_SUFFIX__VERSION_1_0_DEPRECATED; #endif /* CL_USE_DEPRECATED_OPENCL_1_0_APIS */ /* Deprecated OpenCL 1.1 APIs */ extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL clCreateImage2D(cl_context context, cl_mem_flags flags, const cl_image_format * image_format, size_t image_width, size_t image_height, size_t image_row_pitch, void * host_ptr, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_mem CL_API_CALL clCreateImage3D(cl_context context, cl_mem_flags flags, const cl_image_format * image_format, size_t image_width, size_t image_height, size_t image_depth, size_t image_row_pitch, size_t image_slice_pitch, void * host_ptr, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL clEnqueueMarker(cl_command_queue command_queue, cl_event * event) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL clEnqueueWaitForEvents(cl_command_queue command_queue, cl_uint num_events, const cl_event * event_list) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL clEnqueueBarrier(cl_command_queue command_queue) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int CL_API_CALL clUnloadCompiler(void) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_1_DEPRECATED void * CL_API_CALL clGetExtensionFunctionAddress(const char * func_name) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; /* Deprecated OpenCL 2.0 APIs */ extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_2_DEPRECATED cl_command_queue CL_API_CALL clCreateCommandQueue(cl_context context, cl_device_id device, cl_command_queue_properties properties, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_2_DEPRECATED; extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_2_DEPRECATED cl_sampler CL_API_CALL clCreateSampler(cl_context context, cl_bool normalized_coords, cl_addressing_mode addressing_mode, cl_filter_mode filter_mode, cl_int * errcode_ret) CL_API_SUFFIX__VERSION_1_2_DEPRECATED; extern CL_API_ENTRY CL_API_PREFIX__VERSION_1_2_DEPRECATED cl_int CL_API_CALL clEnqueueTask(cl_command_queue command_queue, cl_kernel kernel, cl_uint num_events_in_wait_list, const cl_event * event_wait_list, cl_event * event) CL_API_SUFFIX__VERSION_1_2_DEPRECATED; #endif /* !defined(CL_NO_CORE_PROTOTYPES) */ #ifdef __cplusplus } #endif #endif /* __OPENCL_CL_H */ par2cmdline-turbo-1.4.0/parpar/gf16/opencl-include/CL/cl_platform.h000066400000000000000000001251441514221355600247620ustar00rootroot00000000000000/******************************************************************************* * Copyright (c) 2008-2020 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ #ifndef __CL_PLATFORM_H #define __CL_PLATFORM_H #include #ifdef __cplusplus extern "C" { #endif #if defined(_WIN32) #if !defined(CL_API_ENTRY) #define CL_API_ENTRY #endif #if !defined(CL_API_CALL) #define CL_API_CALL __stdcall #endif #if !defined(CL_CALLBACK) #define CL_CALLBACK __stdcall #endif #else #if !defined(CL_API_ENTRY) #define CL_API_ENTRY #endif #if !defined(CL_API_CALL) #define CL_API_CALL #endif #if !defined(CL_CALLBACK) #define CL_CALLBACK #endif #endif /* * Deprecation flags refer to the last version of the header in which the * feature was not deprecated. * * E.g. VERSION_1_1_DEPRECATED means the feature is present in 1.1 without * deprecation but is deprecated in versions later than 1.1. */ #ifndef CL_API_SUFFIX_USER #define CL_API_SUFFIX_USER #endif #ifndef CL_API_PREFIX_USER #define CL_API_PREFIX_USER #endif #define CL_API_SUFFIX_COMMON CL_API_SUFFIX_USER #define CL_API_PREFIX_COMMON CL_API_PREFIX_USER #define CL_API_SUFFIX__VERSION_1_0 CL_API_SUFFIX_COMMON #define CL_API_SUFFIX__VERSION_1_1 CL_API_SUFFIX_COMMON #define CL_API_SUFFIX__VERSION_1_2 CL_API_SUFFIX_COMMON #define CL_API_SUFFIX__VERSION_2_0 CL_API_SUFFIX_COMMON #define CL_API_SUFFIX__VERSION_2_1 CL_API_SUFFIX_COMMON #define CL_API_SUFFIX__VERSION_2_2 CL_API_SUFFIX_COMMON #define CL_API_SUFFIX__VERSION_3_0 CL_API_SUFFIX_COMMON #define CL_API_SUFFIX__EXPERIMENTAL CL_API_SUFFIX_COMMON #ifdef __GNUC__ #define CL_API_SUFFIX_DEPRECATED __attribute__((deprecated)) #define CL_API_PREFIX_DEPRECATED #elif defined(_WIN32) #define CL_API_SUFFIX_DEPRECATED #define CL_API_PREFIX_DEPRECATED __declspec(deprecated) #else #define CL_API_SUFFIX_DEPRECATED #define CL_API_PREFIX_DEPRECATED #endif #ifdef CL_USE_DEPRECATED_OPENCL_1_0_APIS #define CL_API_SUFFIX__VERSION_1_0_DEPRECATED CL_API_SUFFIX_COMMON #define CL_API_PREFIX__VERSION_1_0_DEPRECATED CL_API_PREFIX_COMMON #else #define CL_API_SUFFIX__VERSION_1_0_DEPRECATED CL_API_SUFFIX_COMMON CL_API_SUFFIX_DEPRECATED #define CL_API_PREFIX__VERSION_1_0_DEPRECATED CL_API_PREFIX_COMMON CL_API_PREFIX_DEPRECATED #endif #ifdef CL_USE_DEPRECATED_OPENCL_1_1_APIS #define CL_API_SUFFIX__VERSION_1_1_DEPRECATED CL_API_SUFFIX_COMMON #define CL_API_PREFIX__VERSION_1_1_DEPRECATED CL_API_PREFIX_COMMON #else #define CL_API_SUFFIX__VERSION_1_1_DEPRECATED CL_API_SUFFIX_COMMON CL_API_SUFFIX_DEPRECATED #define CL_API_PREFIX__VERSION_1_1_DEPRECATED CL_API_PREFIX_COMMON CL_API_PREFIX_DEPRECATED #endif #ifdef CL_USE_DEPRECATED_OPENCL_1_2_APIS #define CL_API_SUFFIX__VERSION_1_2_DEPRECATED CL_API_SUFFIX_COMMON #define CL_API_PREFIX__VERSION_1_2_DEPRECATED CL_API_PREFIX_COMMON #else #define CL_API_SUFFIX__VERSION_1_2_DEPRECATED CL_API_SUFFIX_COMMON CL_API_SUFFIX_DEPRECATED #define CL_API_PREFIX__VERSION_1_2_DEPRECATED CL_API_PREFIX_COMMON CL_API_PREFIX_DEPRECATED #endif #ifdef CL_USE_DEPRECATED_OPENCL_2_0_APIS #define CL_API_SUFFIX__VERSION_2_0_DEPRECATED CL_API_SUFFIX_COMMON #define CL_API_PREFIX__VERSION_2_0_DEPRECATED CL_API_PREFIX_COMMON #else #define CL_API_SUFFIX__VERSION_2_0_DEPRECATED CL_API_SUFFIX_COMMON CL_API_SUFFIX_DEPRECATED #define CL_API_PREFIX__VERSION_2_0_DEPRECATED CL_API_PREFIX_COMMON CL_API_PREFIX_DEPRECATED #endif #ifdef CL_USE_DEPRECATED_OPENCL_2_1_APIS #define CL_API_SUFFIX__VERSION_2_1_DEPRECATED CL_API_SUFFIX_COMMON #define CL_API_PREFIX__VERSION_2_1_DEPRECATED CL_API_PREFIX_COMMON #else #define CL_API_SUFFIX__VERSION_2_1_DEPRECATED CL_API_SUFFIX_COMMON CL_API_SUFFIX_DEPRECATED #define CL_API_PREFIX__VERSION_2_1_DEPRECATED CL_API_PREFIX_COMMON CL_API_PREFIX_DEPRECATED #endif #ifdef CL_USE_DEPRECATED_OPENCL_2_2_APIS #define CL_API_SUFFIX__VERSION_2_2_DEPRECATED CL_API_SUFFIX_COMMON #define CL_API_PREFIX__VERSION_2_2_DEPRECATED CL_API_PREFIX_COMMON #else #define CL_API_SUFFIX__VERSION_2_2_DEPRECATED CL_API_SUFFIX_COMMON CL_API_SUFFIX_DEPRECATED #define CL_API_PREFIX__VERSION_2_2_DEPRECATED CL_API_PREFIX_COMMON CL_API_PREFIX_DEPRECATED #endif #if (defined (_WIN32) && defined(_MSC_VER)) #if defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wlanguage-extension-token" #endif /* intptr_t is used in cl.h and provided by stddef.h in Visual C++, but not in clang */ /* stdint.h was missing before Visual Studio 2010, include it for later versions and for clang */ #if defined(__clang__) || _MSC_VER >= 1600 #include #endif /* scalar types */ typedef signed __int8 cl_char; typedef unsigned __int8 cl_uchar; typedef signed __int16 cl_short; typedef unsigned __int16 cl_ushort; typedef signed __int32 cl_int; typedef unsigned __int32 cl_uint; typedef signed __int64 cl_long; typedef unsigned __int64 cl_ulong; typedef unsigned __int16 cl_half; typedef float cl_float; typedef double cl_double; #if defined(__clang__) #pragma clang diagnostic pop #endif /* Macro names and corresponding values defined by OpenCL */ #define CL_CHAR_BIT 8 #define CL_SCHAR_MAX 127 #define CL_SCHAR_MIN (-127-1) #define CL_CHAR_MAX CL_SCHAR_MAX #define CL_CHAR_MIN CL_SCHAR_MIN #define CL_UCHAR_MAX 255 #define CL_SHRT_MAX 32767 #define CL_SHRT_MIN (-32767-1) #define CL_USHRT_MAX 65535 #define CL_INT_MAX 2147483647 #define CL_INT_MIN (-2147483647-1) #define CL_UINT_MAX 0xffffffffU #define CL_LONG_MAX ((cl_long) 0x7FFFFFFFFFFFFFFFLL) #define CL_LONG_MIN ((cl_long) -0x7FFFFFFFFFFFFFFFLL - 1LL) #define CL_ULONG_MAX ((cl_ulong) 0xFFFFFFFFFFFFFFFFULL) #define CL_FLT_DIG 6 #define CL_FLT_MANT_DIG 24 #define CL_FLT_MAX_10_EXP +38 #define CL_FLT_MAX_EXP +128 #define CL_FLT_MIN_10_EXP -37 #define CL_FLT_MIN_EXP -125 #define CL_FLT_RADIX 2 #define CL_FLT_MAX 340282346638528859811704183484516925440.0f #define CL_FLT_MIN 1.175494350822287507969e-38f #define CL_FLT_EPSILON 1.1920928955078125e-7f #define CL_HALF_DIG 3 #define CL_HALF_MANT_DIG 11 #define CL_HALF_MAX_10_EXP +4 #define CL_HALF_MAX_EXP +16 #define CL_HALF_MIN_10_EXP -4 #define CL_HALF_MIN_EXP -13 #define CL_HALF_RADIX 2 #define CL_HALF_MAX 65504.0f #define CL_HALF_MIN 6.103515625e-05f #define CL_HALF_EPSILON 9.765625e-04f #define CL_DBL_DIG 15 #define CL_DBL_MANT_DIG 53 #define CL_DBL_MAX_10_EXP +308 #define CL_DBL_MAX_EXP +1024 #define CL_DBL_MIN_10_EXP -307 #define CL_DBL_MIN_EXP -1021 #define CL_DBL_RADIX 2 #define CL_DBL_MAX 1.7976931348623158e+308 #define CL_DBL_MIN 2.225073858507201383090e-308 #define CL_DBL_EPSILON 2.220446049250313080847e-16 #define CL_M_E 2.7182818284590452354 #define CL_M_LOG2E 1.4426950408889634074 #define CL_M_LOG10E 0.43429448190325182765 #define CL_M_LN2 0.69314718055994530942 #define CL_M_LN10 2.30258509299404568402 #define CL_M_PI 3.14159265358979323846 #define CL_M_PI_2 1.57079632679489661923 #define CL_M_PI_4 0.78539816339744830962 #define CL_M_1_PI 0.31830988618379067154 #define CL_M_2_PI 0.63661977236758134308 #define CL_M_2_SQRTPI 1.12837916709551257390 #define CL_M_SQRT2 1.41421356237309504880 #define CL_M_SQRT1_2 0.70710678118654752440 #define CL_M_E_F 2.718281828f #define CL_M_LOG2E_F 1.442695041f #define CL_M_LOG10E_F 0.434294482f #define CL_M_LN2_F 0.693147181f #define CL_M_LN10_F 2.302585093f #define CL_M_PI_F 3.141592654f #define CL_M_PI_2_F 1.570796327f #define CL_M_PI_4_F 0.785398163f #define CL_M_1_PI_F 0.318309886f #define CL_M_2_PI_F 0.636619772f #define CL_M_2_SQRTPI_F 1.128379167f #define CL_M_SQRT2_F 1.414213562f #define CL_M_SQRT1_2_F 0.707106781f #define CL_NAN (CL_INFINITY - CL_INFINITY) #define CL_HUGE_VALF ((cl_float) 1e50) #define CL_HUGE_VAL ((cl_double) 1e500) #define CL_MAXFLOAT CL_FLT_MAX #define CL_INFINITY CL_HUGE_VALF #else #include /* scalar types */ typedef int8_t cl_char; typedef uint8_t cl_uchar; typedef int16_t cl_short; typedef uint16_t cl_ushort; typedef int32_t cl_int; typedef uint32_t cl_uint; typedef int64_t cl_long; typedef uint64_t cl_ulong; typedef uint16_t cl_half; typedef float cl_float; typedef double cl_double; /* Macro names and corresponding values defined by OpenCL */ #define CL_CHAR_BIT 8 #define CL_SCHAR_MAX 127 #define CL_SCHAR_MIN (-127-1) #define CL_CHAR_MAX CL_SCHAR_MAX #define CL_CHAR_MIN CL_SCHAR_MIN #define CL_UCHAR_MAX 255 #define CL_SHRT_MAX 32767 #define CL_SHRT_MIN (-32767-1) #define CL_USHRT_MAX 65535 #define CL_INT_MAX 2147483647 #define CL_INT_MIN (-2147483647-1) #define CL_UINT_MAX 0xffffffffU #define CL_LONG_MAX ((cl_long) 0x7FFFFFFFFFFFFFFFLL) #define CL_LONG_MIN ((cl_long) -0x7FFFFFFFFFFFFFFFLL - 1LL) #define CL_ULONG_MAX ((cl_ulong) 0xFFFFFFFFFFFFFFFFULL) #define CL_FLT_DIG 6 #define CL_FLT_MANT_DIG 24 #define CL_FLT_MAX_10_EXP +38 #define CL_FLT_MAX_EXP +128 #define CL_FLT_MIN_10_EXP -37 #define CL_FLT_MIN_EXP -125 #define CL_FLT_RADIX 2 #define CL_FLT_MAX 340282346638528859811704183484516925440.0f #define CL_FLT_MIN 1.175494350822287507969e-38f #define CL_FLT_EPSILON 1.1920928955078125e-7f #define CL_HALF_DIG 3 #define CL_HALF_MANT_DIG 11 #define CL_HALF_MAX_10_EXP +4 #define CL_HALF_MAX_EXP +16 #define CL_HALF_MIN_10_EXP -4 #define CL_HALF_MIN_EXP -13 #define CL_HALF_RADIX 2 #define CL_HALF_MAX 65504.0f #define CL_HALF_MIN 6.103515625e-05f #define CL_HALF_EPSILON 9.765625e-04f #define CL_DBL_DIG 15 #define CL_DBL_MANT_DIG 53 #define CL_DBL_MAX_10_EXP +308 #define CL_DBL_MAX_EXP +1024 #define CL_DBL_MIN_10_EXP -307 #define CL_DBL_MIN_EXP -1021 #define CL_DBL_RADIX 2 #define CL_DBL_MAX 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0 #define CL_DBL_MIN 2.225073858507201383090e-308 #define CL_DBL_EPSILON 2.220446049250313080847e-16 #define CL_M_E 2.7182818284590452354 #define CL_M_LOG2E 1.4426950408889634074 #define CL_M_LOG10E 0.43429448190325182765 #define CL_M_LN2 0.69314718055994530942 #define CL_M_LN10 2.30258509299404568402 #define CL_M_PI 3.14159265358979323846 #define CL_M_PI_2 1.57079632679489661923 #define CL_M_PI_4 0.78539816339744830962 #define CL_M_1_PI 0.31830988618379067154 #define CL_M_2_PI 0.63661977236758134308 #define CL_M_2_SQRTPI 1.12837916709551257390 #define CL_M_SQRT2 1.41421356237309504880 #define CL_M_SQRT1_2 0.70710678118654752440 #define CL_M_E_F 2.718281828f #define CL_M_LOG2E_F 1.442695041f #define CL_M_LOG10E_F 0.434294482f #define CL_M_LN2_F 0.693147181f #define CL_M_LN10_F 2.302585093f #define CL_M_PI_F 3.141592654f #define CL_M_PI_2_F 1.570796327f #define CL_M_PI_4_F 0.785398163f #define CL_M_1_PI_F 0.318309886f #define CL_M_2_PI_F 0.636619772f #define CL_M_2_SQRTPI_F 1.128379167f #define CL_M_SQRT2_F 1.414213562f #define CL_M_SQRT1_2_F 0.707106781f #if defined( __GNUC__ ) #define CL_HUGE_VALF __builtin_huge_valf() #define CL_HUGE_VAL __builtin_huge_val() #define CL_NAN __builtin_nanf( "" ) #else #define CL_HUGE_VALF ((cl_float) 1e50) #define CL_HUGE_VAL ((cl_double) 1e500) float nanf( const char * ); #define CL_NAN nanf( "" ) #endif #define CL_MAXFLOAT CL_FLT_MAX #define CL_INFINITY CL_HUGE_VALF #endif #include /* Mirror types to GL types. Mirror types allow us to avoid deciding which 87s to load based on whether we are using GL or GLES here. */ typedef unsigned int cl_GLuint; typedef int cl_GLint; typedef unsigned int cl_GLenum; /* * Vector types * * Note: OpenCL requires that all types be naturally aligned. * This means that vector types must be naturally aligned. * For example, a vector of four floats must be aligned to * a 16 byte boundary (calculated as 4 * the natural 4-byte * alignment of the float). The alignment qualifiers here * will only function properly if your compiler supports them * and if you don't actively work to defeat them. For example, * in order for a cl_float4 to be 16 byte aligned in a struct, * the start of the struct must itself be 16-byte aligned. * * Maintaining proper alignment is the user's responsibility. */ /* Define basic vector types */ // disabled Altivec support as it causes unknown build issues [https://github.com/animetosho/par2cmdline-turbo/issues/18]; can't seem to reproduce, but it's not really necessary anyway #if defined( __VEC__ ) && 0 #if !defined(__clang__) #include /* may be omitted depending on compiler. AltiVec spec provides no way to detect whether the header is required. */ #endif typedef __vector unsigned char __cl_uchar16; typedef __vector signed char __cl_char16; typedef __vector unsigned short __cl_ushort8; typedef __vector signed short __cl_short8; typedef __vector unsigned int __cl_uint4; typedef __vector signed int __cl_int4; typedef __vector float __cl_float4; #define __CL_UCHAR16__ 1 #define __CL_CHAR16__ 1 #define __CL_USHORT8__ 1 #define __CL_SHORT8__ 1 #define __CL_UINT4__ 1 #define __CL_INT4__ 1 #define __CL_FLOAT4__ 1 #endif #if defined( __SSE__ ) #if defined( __MINGW64__ ) #include #else #include #endif #if defined( __GNUC__ ) typedef float __cl_float4 __attribute__((vector_size(16))); #else typedef __m128 __cl_float4; #endif #define __CL_FLOAT4__ 1 #endif #if defined( __SSE2__ ) #if defined( __MINGW64__ ) #include #else #include #endif #if defined( __GNUC__ ) typedef cl_uchar __cl_uchar16 __attribute__((vector_size(16))); typedef cl_char __cl_char16 __attribute__((vector_size(16))); typedef cl_ushort __cl_ushort8 __attribute__((vector_size(16))); typedef cl_short __cl_short8 __attribute__((vector_size(16))); typedef cl_uint __cl_uint4 __attribute__((vector_size(16))); typedef cl_int __cl_int4 __attribute__((vector_size(16))); typedef cl_ulong __cl_ulong2 __attribute__((vector_size(16))); typedef cl_long __cl_long2 __attribute__((vector_size(16))); typedef cl_double __cl_double2 __attribute__((vector_size(16))); #else typedef __m128i __cl_uchar16; typedef __m128i __cl_char16; typedef __m128i __cl_ushort8; typedef __m128i __cl_short8; typedef __m128i __cl_uint4; typedef __m128i __cl_int4; typedef __m128i __cl_ulong2; typedef __m128i __cl_long2; typedef __m128d __cl_double2; #endif #define __CL_UCHAR16__ 1 #define __CL_CHAR16__ 1 #define __CL_USHORT8__ 1 #define __CL_SHORT8__ 1 #define __CL_INT4__ 1 #define __CL_UINT4__ 1 #define __CL_ULONG2__ 1 #define __CL_LONG2__ 1 #define __CL_DOUBLE2__ 1 #endif #if defined( __MMX__ ) #include #if defined( __GNUC__ ) typedef cl_uchar __cl_uchar8 __attribute__((vector_size(8))); typedef cl_char __cl_char8 __attribute__((vector_size(8))); typedef cl_ushort __cl_ushort4 __attribute__((vector_size(8))); typedef cl_short __cl_short4 __attribute__((vector_size(8))); typedef cl_uint __cl_uint2 __attribute__((vector_size(8))); typedef cl_int __cl_int2 __attribute__((vector_size(8))); typedef cl_ulong __cl_ulong1 __attribute__((vector_size(8))); typedef cl_long __cl_long1 __attribute__((vector_size(8))); typedef cl_float __cl_float2 __attribute__((vector_size(8))); #else typedef __m64 __cl_uchar8; typedef __m64 __cl_char8; typedef __m64 __cl_ushort4; typedef __m64 __cl_short4; typedef __m64 __cl_uint2; typedef __m64 __cl_int2; typedef __m64 __cl_ulong1; typedef __m64 __cl_long1; typedef __m64 __cl_float2; #endif #define __CL_UCHAR8__ 1 #define __CL_CHAR8__ 1 #define __CL_USHORT4__ 1 #define __CL_SHORT4__ 1 #define __CL_INT2__ 1 #define __CL_UINT2__ 1 #define __CL_ULONG1__ 1 #define __CL_LONG1__ 1 #define __CL_FLOAT2__ 1 #endif #if defined( __AVX__ ) #if defined( __MINGW64__ ) #include #else #include #endif #if defined( __GNUC__ ) typedef cl_float __cl_float8 __attribute__((vector_size(32))); typedef cl_double __cl_double4 __attribute__((vector_size(32))); #else typedef __m256 __cl_float8; typedef __m256d __cl_double4; #endif #define __CL_FLOAT8__ 1 #define __CL_DOUBLE4__ 1 #endif /* Define capabilities for anonymous struct members. */ #if !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L #define __CL_HAS_ANON_STRUCT__ 1 #define __CL_ANON_STRUCT__ #elif defined(_WIN32) && defined(_MSC_VER) && !defined(__STDC__) #define __CL_HAS_ANON_STRUCT__ 1 #define __CL_ANON_STRUCT__ #elif defined(__GNUC__) && ! defined(__STRICT_ANSI__) #define __CL_HAS_ANON_STRUCT__ 1 #define __CL_ANON_STRUCT__ __extension__ #elif defined(__clang__) #define __CL_HAS_ANON_STRUCT__ 1 #define __CL_ANON_STRUCT__ __extension__ #else #define __CL_HAS_ANON_STRUCT__ 0 #define __CL_ANON_STRUCT__ #endif #if defined(_WIN32) && defined(_MSC_VER) && __CL_HAS_ANON_STRUCT__ /* Disable warning C4201: nonstandard extension used : nameless struct/union */ #pragma warning( push ) #pragma warning( disable : 4201 ) #endif /* Define alignment keys */ #if defined( __GNUC__ ) || defined(__INTEGRITY) #define CL_ALIGNED(_x) __attribute__ ((aligned(_x))) #elif defined( _WIN32) && (_MSC_VER) /* Alignment keys neutered on windows because MSVC can't swallow function arguments with alignment requirements */ /* http://msdn.microsoft.com/en-us/library/373ak2y1%28VS.71%29.aspx */ /* #include */ /* #define CL_ALIGNED(_x) _CRT_ALIGN(_x) */ #define CL_ALIGNED(_x) #else #warning Need to implement some method to align data here #define CL_ALIGNED(_x) #endif /* Indicate whether .xyzw, .s0123 and .hi.lo are supported */ #if __CL_HAS_ANON_STRUCT__ /* .xyzw and .s0123...{f|F} are supported */ #define CL_HAS_NAMED_VECTOR_FIELDS 1 /* .hi and .lo are supported */ #define CL_HAS_HI_LO_VECTOR_FIELDS 1 #endif /* Define cl_vector types */ /* ---- cl_charn ---- */ typedef union { cl_char CL_ALIGNED(2) s[2]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_char x, y; }; __CL_ANON_STRUCT__ struct{ cl_char s0, s1; }; __CL_ANON_STRUCT__ struct{ cl_char lo, hi; }; #endif #if defined( __CL_CHAR2__) __cl_char2 v2; #endif }cl_char2; typedef union { cl_char CL_ALIGNED(4) s[4]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_char x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_char s0, s1, s2, s3; }; __CL_ANON_STRUCT__ struct{ cl_char2 lo, hi; }; #endif #if defined( __CL_CHAR2__) __cl_char2 v2[2]; #endif #if defined( __CL_CHAR4__) __cl_char4 v4; #endif }cl_char4; /* cl_char3 is identical in size, alignment and behavior to cl_char4. See section 6.1.5. */ typedef cl_char4 cl_char3; typedef union { cl_char CL_ALIGNED(8) s[8]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_char x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_char s0, s1, s2, s3, s4, s5, s6, s7; }; __CL_ANON_STRUCT__ struct{ cl_char4 lo, hi; }; #endif #if defined( __CL_CHAR2__) __cl_char2 v2[4]; #endif #if defined( __CL_CHAR4__) __cl_char4 v4[2]; #endif #if defined( __CL_CHAR8__ ) __cl_char8 v8; #endif }cl_char8; typedef union { cl_char CL_ALIGNED(16) s[16]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_char x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __CL_ANON_STRUCT__ struct{ cl_char s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __CL_ANON_STRUCT__ struct{ cl_char8 lo, hi; }; #endif #if defined( __CL_CHAR2__) __cl_char2 v2[8]; #endif #if defined( __CL_CHAR4__) __cl_char4 v4[4]; #endif #if defined( __CL_CHAR8__ ) __cl_char8 v8[2]; #endif #if defined( __CL_CHAR16__ ) __cl_char16 v16; #endif }cl_char16; /* ---- cl_ucharn ---- */ typedef union { cl_uchar CL_ALIGNED(2) s[2]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_uchar x, y; }; __CL_ANON_STRUCT__ struct{ cl_uchar s0, s1; }; __CL_ANON_STRUCT__ struct{ cl_uchar lo, hi; }; #endif #if defined( __cl_uchar2__) __cl_uchar2 v2; #endif }cl_uchar2; typedef union { cl_uchar CL_ALIGNED(4) s[4]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_uchar x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_uchar s0, s1, s2, s3; }; __CL_ANON_STRUCT__ struct{ cl_uchar2 lo, hi; }; #endif #if defined( __CL_UCHAR2__) __cl_uchar2 v2[2]; #endif #if defined( __CL_UCHAR4__) __cl_uchar4 v4; #endif }cl_uchar4; /* cl_uchar3 is identical in size, alignment and behavior to cl_uchar4. See section 6.1.5. */ typedef cl_uchar4 cl_uchar3; typedef union { cl_uchar CL_ALIGNED(8) s[8]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_uchar x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_uchar s0, s1, s2, s3, s4, s5, s6, s7; }; __CL_ANON_STRUCT__ struct{ cl_uchar4 lo, hi; }; #endif #if defined( __CL_UCHAR2__) __cl_uchar2 v2[4]; #endif #if defined( __CL_UCHAR4__) __cl_uchar4 v4[2]; #endif #if defined( __CL_UCHAR8__ ) __cl_uchar8 v8; #endif }cl_uchar8; typedef union { cl_uchar CL_ALIGNED(16) s[16]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_uchar x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __CL_ANON_STRUCT__ struct{ cl_uchar s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __CL_ANON_STRUCT__ struct{ cl_uchar8 lo, hi; }; #endif #if defined( __CL_UCHAR2__) __cl_uchar2 v2[8]; #endif #if defined( __CL_UCHAR4__) __cl_uchar4 v4[4]; #endif #if defined( __CL_UCHAR8__ ) __cl_uchar8 v8[2]; #endif #if defined( __CL_UCHAR16__ ) __cl_uchar16 v16; #endif }cl_uchar16; /* ---- cl_shortn ---- */ typedef union { cl_short CL_ALIGNED(4) s[2]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_short x, y; }; __CL_ANON_STRUCT__ struct{ cl_short s0, s1; }; __CL_ANON_STRUCT__ struct{ cl_short lo, hi; }; #endif #if defined( __CL_SHORT2__) __cl_short2 v2; #endif }cl_short2; typedef union { cl_short CL_ALIGNED(8) s[4]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_short x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_short s0, s1, s2, s3; }; __CL_ANON_STRUCT__ struct{ cl_short2 lo, hi; }; #endif #if defined( __CL_SHORT2__) __cl_short2 v2[2]; #endif #if defined( __CL_SHORT4__) __cl_short4 v4; #endif }cl_short4; /* cl_short3 is identical in size, alignment and behavior to cl_short4. See section 6.1.5. */ typedef cl_short4 cl_short3; typedef union { cl_short CL_ALIGNED(16) s[8]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_short x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_short s0, s1, s2, s3, s4, s5, s6, s7; }; __CL_ANON_STRUCT__ struct{ cl_short4 lo, hi; }; #endif #if defined( __CL_SHORT2__) __cl_short2 v2[4]; #endif #if defined( __CL_SHORT4__) __cl_short4 v4[2]; #endif #if defined( __CL_SHORT8__ ) __cl_short8 v8; #endif }cl_short8; typedef union { cl_short CL_ALIGNED(32) s[16]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_short x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __CL_ANON_STRUCT__ struct{ cl_short s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __CL_ANON_STRUCT__ struct{ cl_short8 lo, hi; }; #endif #if defined( __CL_SHORT2__) __cl_short2 v2[8]; #endif #if defined( __CL_SHORT4__) __cl_short4 v4[4]; #endif #if defined( __CL_SHORT8__ ) __cl_short8 v8[2]; #endif #if defined( __CL_SHORT16__ ) __cl_short16 v16; #endif }cl_short16; /* ---- cl_ushortn ---- */ typedef union { cl_ushort CL_ALIGNED(4) s[2]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_ushort x, y; }; __CL_ANON_STRUCT__ struct{ cl_ushort s0, s1; }; __CL_ANON_STRUCT__ struct{ cl_ushort lo, hi; }; #endif #if defined( __CL_USHORT2__) __cl_ushort2 v2; #endif }cl_ushort2; typedef union { cl_ushort CL_ALIGNED(8) s[4]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_ushort x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_ushort s0, s1, s2, s3; }; __CL_ANON_STRUCT__ struct{ cl_ushort2 lo, hi; }; #endif #if defined( __CL_USHORT2__) __cl_ushort2 v2[2]; #endif #if defined( __CL_USHORT4__) __cl_ushort4 v4; #endif }cl_ushort4; /* cl_ushort3 is identical in size, alignment and behavior to cl_ushort4. See section 6.1.5. */ typedef cl_ushort4 cl_ushort3; typedef union { cl_ushort CL_ALIGNED(16) s[8]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_ushort x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_ushort s0, s1, s2, s3, s4, s5, s6, s7; }; __CL_ANON_STRUCT__ struct{ cl_ushort4 lo, hi; }; #endif #if defined( __CL_USHORT2__) __cl_ushort2 v2[4]; #endif #if defined( __CL_USHORT4__) __cl_ushort4 v4[2]; #endif #if defined( __CL_USHORT8__ ) __cl_ushort8 v8; #endif }cl_ushort8; typedef union { cl_ushort CL_ALIGNED(32) s[16]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_ushort x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __CL_ANON_STRUCT__ struct{ cl_ushort s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __CL_ANON_STRUCT__ struct{ cl_ushort8 lo, hi; }; #endif #if defined( __CL_USHORT2__) __cl_ushort2 v2[8]; #endif #if defined( __CL_USHORT4__) __cl_ushort4 v4[4]; #endif #if defined( __CL_USHORT8__ ) __cl_ushort8 v8[2]; #endif #if defined( __CL_USHORT16__ ) __cl_ushort16 v16; #endif }cl_ushort16; /* ---- cl_halfn ---- */ typedef union { cl_half CL_ALIGNED(4) s[2]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_half x, y; }; __CL_ANON_STRUCT__ struct{ cl_half s0, s1; }; __CL_ANON_STRUCT__ struct{ cl_half lo, hi; }; #endif #if defined( __CL_HALF2__) __cl_half2 v2; #endif }cl_half2; typedef union { cl_half CL_ALIGNED(8) s[4]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_half x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_half s0, s1, s2, s3; }; __CL_ANON_STRUCT__ struct{ cl_half2 lo, hi; }; #endif #if defined( __CL_HALF2__) __cl_half2 v2[2]; #endif #if defined( __CL_HALF4__) __cl_half4 v4; #endif }cl_half4; /* cl_half3 is identical in size, alignment and behavior to cl_half4. See section 6.1.5. */ typedef cl_half4 cl_half3; typedef union { cl_half CL_ALIGNED(16) s[8]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_half x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_half s0, s1, s2, s3, s4, s5, s6, s7; }; __CL_ANON_STRUCT__ struct{ cl_half4 lo, hi; }; #endif #if defined( __CL_HALF2__) __cl_half2 v2[4]; #endif #if defined( __CL_HALF4__) __cl_half4 v4[2]; #endif #if defined( __CL_HALF8__ ) __cl_half8 v8; #endif }cl_half8; typedef union { cl_half CL_ALIGNED(32) s[16]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_half x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __CL_ANON_STRUCT__ struct{ cl_half s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __CL_ANON_STRUCT__ struct{ cl_half8 lo, hi; }; #endif #if defined( __CL_HALF2__) __cl_half2 v2[8]; #endif #if defined( __CL_HALF4__) __cl_half4 v4[4]; #endif #if defined( __CL_HALF8__ ) __cl_half8 v8[2]; #endif #if defined( __CL_HALF16__ ) __cl_half16 v16; #endif }cl_half16; /* ---- cl_intn ---- */ typedef union { cl_int CL_ALIGNED(8) s[2]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_int x, y; }; __CL_ANON_STRUCT__ struct{ cl_int s0, s1; }; __CL_ANON_STRUCT__ struct{ cl_int lo, hi; }; #endif #if defined( __CL_INT2__) __cl_int2 v2; #endif }cl_int2; typedef union { cl_int CL_ALIGNED(16) s[4]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_int x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_int s0, s1, s2, s3; }; __CL_ANON_STRUCT__ struct{ cl_int2 lo, hi; }; #endif #if defined( __CL_INT2__) __cl_int2 v2[2]; #endif #if defined( __CL_INT4__) __cl_int4 v4; #endif }cl_int4; /* cl_int3 is identical in size, alignment and behavior to cl_int4. See section 6.1.5. */ typedef cl_int4 cl_int3; typedef union { cl_int CL_ALIGNED(32) s[8]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_int x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_int s0, s1, s2, s3, s4, s5, s6, s7; }; __CL_ANON_STRUCT__ struct{ cl_int4 lo, hi; }; #endif #if defined( __CL_INT2__) __cl_int2 v2[4]; #endif #if defined( __CL_INT4__) __cl_int4 v4[2]; #endif #if defined( __CL_INT8__ ) __cl_int8 v8; #endif }cl_int8; typedef union { cl_int CL_ALIGNED(64) s[16]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_int x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __CL_ANON_STRUCT__ struct{ cl_int s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __CL_ANON_STRUCT__ struct{ cl_int8 lo, hi; }; #endif #if defined( __CL_INT2__) __cl_int2 v2[8]; #endif #if defined( __CL_INT4__) __cl_int4 v4[4]; #endif #if defined( __CL_INT8__ ) __cl_int8 v8[2]; #endif #if defined( __CL_INT16__ ) __cl_int16 v16; #endif }cl_int16; /* ---- cl_uintn ---- */ typedef union { cl_uint CL_ALIGNED(8) s[2]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_uint x, y; }; __CL_ANON_STRUCT__ struct{ cl_uint s0, s1; }; __CL_ANON_STRUCT__ struct{ cl_uint lo, hi; }; #endif #if defined( __CL_UINT2__) __cl_uint2 v2; #endif }cl_uint2; typedef union { cl_uint CL_ALIGNED(16) s[4]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_uint x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_uint s0, s1, s2, s3; }; __CL_ANON_STRUCT__ struct{ cl_uint2 lo, hi; }; #endif #if defined( __CL_UINT2__) __cl_uint2 v2[2]; #endif #if defined( __CL_UINT4__) __cl_uint4 v4; #endif }cl_uint4; /* cl_uint3 is identical in size, alignment and behavior to cl_uint4. See section 6.1.5. */ typedef cl_uint4 cl_uint3; typedef union { cl_uint CL_ALIGNED(32) s[8]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_uint x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_uint s0, s1, s2, s3, s4, s5, s6, s7; }; __CL_ANON_STRUCT__ struct{ cl_uint4 lo, hi; }; #endif #if defined( __CL_UINT2__) __cl_uint2 v2[4]; #endif #if defined( __CL_UINT4__) __cl_uint4 v4[2]; #endif #if defined( __CL_UINT8__ ) __cl_uint8 v8; #endif }cl_uint8; typedef union { cl_uint CL_ALIGNED(64) s[16]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_uint x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __CL_ANON_STRUCT__ struct{ cl_uint s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __CL_ANON_STRUCT__ struct{ cl_uint8 lo, hi; }; #endif #if defined( __CL_UINT2__) __cl_uint2 v2[8]; #endif #if defined( __CL_UINT4__) __cl_uint4 v4[4]; #endif #if defined( __CL_UINT8__ ) __cl_uint8 v8[2]; #endif #if defined( __CL_UINT16__ ) __cl_uint16 v16; #endif }cl_uint16; /* ---- cl_longn ---- */ typedef union { cl_long CL_ALIGNED(16) s[2]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_long x, y; }; __CL_ANON_STRUCT__ struct{ cl_long s0, s1; }; __CL_ANON_STRUCT__ struct{ cl_long lo, hi; }; #endif #if defined( __CL_LONG2__) __cl_long2 v2; #endif }cl_long2; typedef union { cl_long CL_ALIGNED(32) s[4]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_long x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_long s0, s1, s2, s3; }; __CL_ANON_STRUCT__ struct{ cl_long2 lo, hi; }; #endif #if defined( __CL_LONG2__) __cl_long2 v2[2]; #endif #if defined( __CL_LONG4__) __cl_long4 v4; #endif }cl_long4; /* cl_long3 is identical in size, alignment and behavior to cl_long4. See section 6.1.5. */ typedef cl_long4 cl_long3; typedef union { cl_long CL_ALIGNED(64) s[8]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_long x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_long s0, s1, s2, s3, s4, s5, s6, s7; }; __CL_ANON_STRUCT__ struct{ cl_long4 lo, hi; }; #endif #if defined( __CL_LONG2__) __cl_long2 v2[4]; #endif #if defined( __CL_LONG4__) __cl_long4 v4[2]; #endif #if defined( __CL_LONG8__ ) __cl_long8 v8; #endif }cl_long8; typedef union { cl_long CL_ALIGNED(128) s[16]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_long x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __CL_ANON_STRUCT__ struct{ cl_long s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __CL_ANON_STRUCT__ struct{ cl_long8 lo, hi; }; #endif #if defined( __CL_LONG2__) __cl_long2 v2[8]; #endif #if defined( __CL_LONG4__) __cl_long4 v4[4]; #endif #if defined( __CL_LONG8__ ) __cl_long8 v8[2]; #endif #if defined( __CL_LONG16__ ) __cl_long16 v16; #endif }cl_long16; /* ---- cl_ulongn ---- */ typedef union { cl_ulong CL_ALIGNED(16) s[2]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_ulong x, y; }; __CL_ANON_STRUCT__ struct{ cl_ulong s0, s1; }; __CL_ANON_STRUCT__ struct{ cl_ulong lo, hi; }; #endif #if defined( __CL_ULONG2__) __cl_ulong2 v2; #endif }cl_ulong2; typedef union { cl_ulong CL_ALIGNED(32) s[4]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_ulong x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_ulong s0, s1, s2, s3; }; __CL_ANON_STRUCT__ struct{ cl_ulong2 lo, hi; }; #endif #if defined( __CL_ULONG2__) __cl_ulong2 v2[2]; #endif #if defined( __CL_ULONG4__) __cl_ulong4 v4; #endif }cl_ulong4; /* cl_ulong3 is identical in size, alignment and behavior to cl_ulong4. See section 6.1.5. */ typedef cl_ulong4 cl_ulong3; typedef union { cl_ulong CL_ALIGNED(64) s[8]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_ulong x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_ulong s0, s1, s2, s3, s4, s5, s6, s7; }; __CL_ANON_STRUCT__ struct{ cl_ulong4 lo, hi; }; #endif #if defined( __CL_ULONG2__) __cl_ulong2 v2[4]; #endif #if defined( __CL_ULONG4__) __cl_ulong4 v4[2]; #endif #if defined( __CL_ULONG8__ ) __cl_ulong8 v8; #endif }cl_ulong8; typedef union { cl_ulong CL_ALIGNED(128) s[16]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_ulong x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __CL_ANON_STRUCT__ struct{ cl_ulong s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __CL_ANON_STRUCT__ struct{ cl_ulong8 lo, hi; }; #endif #if defined( __CL_ULONG2__) __cl_ulong2 v2[8]; #endif #if defined( __CL_ULONG4__) __cl_ulong4 v4[4]; #endif #if defined( __CL_ULONG8__ ) __cl_ulong8 v8[2]; #endif #if defined( __CL_ULONG16__ ) __cl_ulong16 v16; #endif }cl_ulong16; /* --- cl_floatn ---- */ typedef union { cl_float CL_ALIGNED(8) s[2]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_float x, y; }; __CL_ANON_STRUCT__ struct{ cl_float s0, s1; }; __CL_ANON_STRUCT__ struct{ cl_float lo, hi; }; #endif #if defined( __CL_FLOAT2__) __cl_float2 v2; #endif }cl_float2; typedef union { cl_float CL_ALIGNED(16) s[4]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_float x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_float s0, s1, s2, s3; }; __CL_ANON_STRUCT__ struct{ cl_float2 lo, hi; }; #endif #if defined( __CL_FLOAT2__) __cl_float2 v2[2]; #endif #if defined( __CL_FLOAT4__) __cl_float4 v4; #endif }cl_float4; /* cl_float3 is identical in size, alignment and behavior to cl_float4. See section 6.1.5. */ typedef cl_float4 cl_float3; typedef union { cl_float CL_ALIGNED(32) s[8]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_float x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_float s0, s1, s2, s3, s4, s5, s6, s7; }; __CL_ANON_STRUCT__ struct{ cl_float4 lo, hi; }; #endif #if defined( __CL_FLOAT2__) __cl_float2 v2[4]; #endif #if defined( __CL_FLOAT4__) __cl_float4 v4[2]; #endif #if defined( __CL_FLOAT8__ ) __cl_float8 v8; #endif }cl_float8; typedef union { cl_float CL_ALIGNED(64) s[16]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_float x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __CL_ANON_STRUCT__ struct{ cl_float s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __CL_ANON_STRUCT__ struct{ cl_float8 lo, hi; }; #endif #if defined( __CL_FLOAT2__) __cl_float2 v2[8]; #endif #if defined( __CL_FLOAT4__) __cl_float4 v4[4]; #endif #if defined( __CL_FLOAT8__ ) __cl_float8 v8[2]; #endif #if defined( __CL_FLOAT16__ ) __cl_float16 v16; #endif }cl_float16; /* --- cl_doublen ---- */ typedef union { cl_double CL_ALIGNED(16) s[2]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_double x, y; }; __CL_ANON_STRUCT__ struct{ cl_double s0, s1; }; __CL_ANON_STRUCT__ struct{ cl_double lo, hi; }; #endif #if defined( __CL_DOUBLE2__) __cl_double2 v2; #endif }cl_double2; typedef union { cl_double CL_ALIGNED(32) s[4]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_double x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_double s0, s1, s2, s3; }; __CL_ANON_STRUCT__ struct{ cl_double2 lo, hi; }; #endif #if defined( __CL_DOUBLE2__) __cl_double2 v2[2]; #endif #if defined( __CL_DOUBLE4__) __cl_double4 v4; #endif }cl_double4; /* cl_double3 is identical in size, alignment and behavior to cl_double4. See section 6.1.5. */ typedef cl_double4 cl_double3; typedef union { cl_double CL_ALIGNED(64) s[8]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_double x, y, z, w; }; __CL_ANON_STRUCT__ struct{ cl_double s0, s1, s2, s3, s4, s5, s6, s7; }; __CL_ANON_STRUCT__ struct{ cl_double4 lo, hi; }; #endif #if defined( __CL_DOUBLE2__) __cl_double2 v2[4]; #endif #if defined( __CL_DOUBLE4__) __cl_double4 v4[2]; #endif #if defined( __CL_DOUBLE8__ ) __cl_double8 v8; #endif }cl_double8; typedef union { cl_double CL_ALIGNED(128) s[16]; #if __CL_HAS_ANON_STRUCT__ __CL_ANON_STRUCT__ struct{ cl_double x, y, z, w, __spacer4, __spacer5, __spacer6, __spacer7, __spacer8, __spacer9, sa, sb, sc, sd, se, sf; }; __CL_ANON_STRUCT__ struct{ cl_double s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, sA, sB, sC, sD, sE, sF; }; __CL_ANON_STRUCT__ struct{ cl_double8 lo, hi; }; #endif #if defined( __CL_DOUBLE2__) __cl_double2 v2[8]; #endif #if defined( __CL_DOUBLE4__) __cl_double4 v4[4]; #endif #if defined( __CL_DOUBLE8__ ) __cl_double8 v8[2]; #endif #if defined( __CL_DOUBLE16__ ) __cl_double16 v16; #endif }cl_double16; /* Macro to facilitate debugging * Usage: * Place CL_PROGRAM_STRING_DEBUG_INFO on the line before the first line of your source. * The first line ends with: CL_PROGRAM_STRING_DEBUG_INFO \" * Each line thereafter of OpenCL C source must end with: \n\ * The last line ends in "; * * Example: * * const char *my_program = CL_PROGRAM_STRING_DEBUG_INFO "\ * kernel void foo( int a, float * b ) \n\ * { \n\ * // my comment \n\ * *b[ get_global_id(0)] = a; \n\ * } \n\ * "; * * This should correctly set up the line, (column) and file information for your source * string so you can do source level debugging. */ #define __CL_STRINGIFY( _x ) # _x #define _CL_STRINGIFY( _x ) __CL_STRINGIFY( _x ) #define CL_PROGRAM_STRING_DEBUG_INFO "#line " _CL_STRINGIFY(__LINE__) " \"" __FILE__ "\" \n\n" #ifdef __cplusplus } #endif #if defined(_WIN32) && defined(_MSC_VER) && __CL_HAS_ANON_STRUCT__ #pragma warning( pop ) #endif #endif /* __CL_PLATFORM_H */ par2cmdline-turbo-1.4.0/parpar/gf16/opencl-include/CL/cl_version.h000066400000000000000000000060651514221355600246230ustar00rootroot00000000000000/******************************************************************************* * Copyright (c) 2018-2020 The Khronos Group Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ #ifndef __CL_VERSION_H #define __CL_VERSION_H /* Detect which version to target */ #if !defined(CL_TARGET_OPENCL_VERSION) #pragma message("cl_version.h: CL_TARGET_OPENCL_VERSION is not defined. Defaulting to 300 (OpenCL 3.0)") #define CL_TARGET_OPENCL_VERSION 300 #endif #if CL_TARGET_OPENCL_VERSION != 100 && \ CL_TARGET_OPENCL_VERSION != 110 && \ CL_TARGET_OPENCL_VERSION != 120 && \ CL_TARGET_OPENCL_VERSION != 200 && \ CL_TARGET_OPENCL_VERSION != 210 && \ CL_TARGET_OPENCL_VERSION != 220 && \ CL_TARGET_OPENCL_VERSION != 300 #pragma message("cl_version: CL_TARGET_OPENCL_VERSION is not a valid value (100, 110, 120, 200, 210, 220, 300). Defaulting to 300 (OpenCL 3.0)") #undef CL_TARGET_OPENCL_VERSION #define CL_TARGET_OPENCL_VERSION 300 #endif /* OpenCL Version */ #if CL_TARGET_OPENCL_VERSION >= 300 && !defined(CL_VERSION_3_0) #define CL_VERSION_3_0 1 #endif #if CL_TARGET_OPENCL_VERSION >= 220 && !defined(CL_VERSION_2_2) #define CL_VERSION_2_2 1 #endif #if CL_TARGET_OPENCL_VERSION >= 210 && !defined(CL_VERSION_2_1) #define CL_VERSION_2_1 1 #endif #if CL_TARGET_OPENCL_VERSION >= 200 && !defined(CL_VERSION_2_0) #define CL_VERSION_2_0 1 #endif #if CL_TARGET_OPENCL_VERSION >= 120 && !defined(CL_VERSION_1_2) #define CL_VERSION_1_2 1 #endif #if CL_TARGET_OPENCL_VERSION >= 110 && !defined(CL_VERSION_1_1) #define CL_VERSION_1_1 1 #endif #if CL_TARGET_OPENCL_VERSION >= 100 && !defined(CL_VERSION_1_0) #define CL_VERSION_1_0 1 #endif /* Allow deprecated APIs for older OpenCL versions. */ #if CL_TARGET_OPENCL_VERSION <= 220 && !defined(CL_USE_DEPRECATED_OPENCL_2_2_APIS) #define CL_USE_DEPRECATED_OPENCL_2_2_APIS #endif #if CL_TARGET_OPENCL_VERSION <= 210 && !defined(CL_USE_DEPRECATED_OPENCL_2_1_APIS) #define CL_USE_DEPRECATED_OPENCL_2_1_APIS #endif #if CL_TARGET_OPENCL_VERSION <= 200 && !defined(CL_USE_DEPRECATED_OPENCL_2_0_APIS) #define CL_USE_DEPRECATED_OPENCL_2_0_APIS #endif #if CL_TARGET_OPENCL_VERSION <= 120 && !defined(CL_USE_DEPRECATED_OPENCL_1_2_APIS) #define CL_USE_DEPRECATED_OPENCL_1_2_APIS #endif #if CL_TARGET_OPENCL_VERSION <= 110 && !defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) #define CL_USE_DEPRECATED_OPENCL_1_1_APIS #endif #if CL_TARGET_OPENCL_VERSION <= 100 && !defined(CL_USE_DEPRECATED_OPENCL_1_0_APIS) #define CL_USE_DEPRECATED_OPENCL_1_0_APIS #endif #endif /* __CL_VERSION_H */ par2cmdline-turbo-1.4.0/parpar/gf16/opencl-include/CL/opencl.hpp000066400000000000000000013164341514221355600243050ustar00rootroot00000000000000// // Copyright (c) 2008-2023 The Khronos Group Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // /*! \file * * \brief C++ bindings for OpenCL 1.0, OpenCL 1.1, OpenCL 1.2, * OpenCL 2.0, OpenCL 2.1, OpenCL 2.2, and OpenCL 3.0. * \author Lee Howes and Bruce Merry * * Derived from the OpenCL 1.x C++ bindings written by * Benedict R. Gaster, Laurent Morichetti and Lee Howes * With additions and fixes from: * Brian Cole, March 3rd 2010 and April 2012 * Matt Gruenke, April 2012. * Bruce Merry, February 2013. * Tom Deakin and Simon McIntosh-Smith, July 2013 * James Price, 2015- * \version 2.2.0 * \date 2019-09-18 * * Optional extension support * * cl_khr_d3d10_sharing * #define CL_HPP_USE_DX_INTEROP * cl_khr_il_program * #define CL_HPP_USE_IL_KHR * cl_khr_sub_groups * #define CL_HPP_USE_CL_SUB_GROUPS_KHR * * Doxygen documentation for this header is available here: * * http://khronosgroup.github.io/OpenCL-CLHPP/ * * The latest version of this header can be found on the GitHub releases page: * * https://github.com/KhronosGroup/OpenCL-CLHPP/releases * * Bugs and patches can be submitted to the GitHub repository: * * https://github.com/KhronosGroup/OpenCL-CLHPP */ /*! \mainpage * \section intro Introduction * For many large applications C++ is the language of choice and so it seems * reasonable to define C++ bindings for OpenCL. * * The interface is contained with a single C++ header file \em opencl.hpp and all * definitions are contained within the namespace \em cl. There is no additional * requirement to include \em cl.h and to use either the C++ or original C * bindings; it is enough to simply include \em opencl.hpp. * * The bindings themselves are lightweight and correspond closely to the * underlying C API. Using the C++ bindings introduces no additional execution * overhead. * * There are numerous compatibility, portability and memory management * fixes in the new header as well as additional OpenCL 2.0 features. * As a result the header is not directly backward compatible and for this * reason we release it as opencl.hpp rather than a new version of cl.hpp. * * * \section compatibility Compatibility * Due to the evolution of the underlying OpenCL API the 2.0 C++ bindings * include an updated approach to defining supported feature versions * and the range of valid underlying OpenCL runtime versions supported. * * The combination of preprocessor macros CL_HPP_TARGET_OPENCL_VERSION and * CL_HPP_MINIMUM_OPENCL_VERSION control this range. These are three digit * decimal values representing OpenCL runtime versions. The default for * the target is 300, representing OpenCL 3.0. The minimum is defined as 200. * These settings would use 2.0 and newer API calls only. * If backward compatibility with a 1.2 runtime is required, the minimum * version may be set to 120. * * Note that this is a compile-time setting, and so affects linking against * a particular SDK version rather than the versioning of the loaded runtime. * * The earlier versions of the header included basic vector and string * classes based loosely on STL versions. These were difficult to * maintain and very rarely used. For the 2.0 header we now assume * the presence of the standard library unless requested otherwise. * We use std::array, std::vector, std::shared_ptr and std::string * throughout to safely manage memory and reduce the chance of a * recurrance of earlier memory management bugs. * * These classes are used through typedefs in the cl namespace: * cl::array, cl::vector, cl::pointer and cl::string. * In addition cl::allocate_pointer forwards to std::allocate_shared * by default. * In all cases these standard library classes can be replaced with * custom interface-compatible versions using the CL_HPP_NO_STD_ARRAY, * CL_HPP_NO_STD_VECTOR, CL_HPP_NO_STD_UNIQUE_PTR and * CL_HPP_NO_STD_STRING macros. * * The OpenCL 1.x versions of the C++ bindings included a size_t wrapper * class to interface with kernel enqueue. This caused unpleasant interactions * with the standard size_t declaration and led to namespacing bugs. * In the 2.0 version we have replaced this with a std::array-based interface. * However, the old behaviour can be regained for backward compatibility * using the CL_HPP_ENABLE_SIZE_T_COMPATIBILITY macro. * * Finally, the program construction interface used a clumsy vector-of-pairs * design in the earlier versions. We have replaced that with a cleaner * vector-of-vectors and vector-of-strings design. However, for backward * compatibility old behaviour can be regained with the * CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY macro. * * In OpenCL 2.0 OpenCL C is not entirely backward compatibility with * earlier versions. As a result a flag must be passed to the OpenCL C * compiled to request OpenCL 2.0 compilation of kernels with 1.2 as * the default in the absence of the flag. * In some cases the C++ bindings automatically compile code for ease. * For those cases the compilation defaults to OpenCL C 2.0. * If this is not wanted, the CL_HPP_CL_1_2_DEFAULT_BUILD macro may * be specified to assume 1.2 compilation. * If more fine-grained decisions on a per-kernel bases are required * then explicit build operations that take the flag should be used. * * * \section parameterization Parameters * This header may be parameterized by a set of preprocessor macros. * * - CL_HPP_TARGET_OPENCL_VERSION * * Defines the target OpenCL runtime version to build the header * against. Defaults to 300, representing OpenCL 3.0. * * - CL_HPP_MINIMUM_OPENCL_VERSION * * Defines the minimum OpenCL runtime version to build the header * against. Defaults to 200, representing OpenCL 2.0. * * - CL_HPP_NO_STD_STRING * * Do not use the standard library string class. cl::string is not * defined and may be defined by the user before opencl.hpp is * included. * * - CL_HPP_NO_STD_VECTOR * * Do not use the standard library vector class. cl::vector is not * defined and may be defined by the user before opencl.hpp is * included. * * - CL_HPP_NO_STD_ARRAY * * Do not use the standard library array class. cl::array is not * defined and may be defined by the user before opencl.hpp is * included. * * - CL_HPP_NO_STD_UNIQUE_PTR * * Do not use the standard library unique_ptr class. cl::pointer and * the cl::allocate_pointer functions are not defined and may be * defined by the user before opencl.hpp is included. * * - CL_HPP_ENABLE_EXCEPTIONS * * Enable exceptions for use in the C++ bindings header. This is the * preferred error handling mechanism but is not required. * * - CL_HPP_ENABLE_SIZE_T_COMPATIBILITY * * Backward compatibility option to support cl.hpp-style size_t * class. Replaces the updated std::array derived version and * removal of size_t from the namespace. Note that in this case the * new size_t class is placed in the cl::compatibility namespace and * thus requires an additional using declaration for direct backward * compatibility. * * - CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY * * Enable older vector of pairs interface for construction of * programs. * * - CL_HPP_CL_1_2_DEFAULT_BUILD * * Default to OpenCL C 1.2 compilation rather than OpenCL C 2.0 * applies to use of cl::Program construction and other program * build variants. * * * - CL_HPP_USE_CL_SUB_GROUPS_KHR * * Enable the cl_khr_subgroups extension. * * - CL_HPP_USE_DX_INTEROP * * Enable the cl_khr_d3d10_sharing extension. * * - CL_HPP_USE_IL_KHR * * Enable the cl_khr_il_program extension. * * * \section example Example * * The following example shows a general use case for the C++ * bindings, including support for the optional exception feature and * also the supplied vector and string classes, see following sections for * decriptions of these features. * * Note: the C++ bindings use std::call_once and therefore may need to be * compiled using special command-line options (such as "-pthread") on some * platforms! * * \code #define CL_HPP_ENABLE_EXCEPTIONS #define CL_HPP_TARGET_OPENCL_VERSION 200 #include #include #include #include #include const int numElements = 32; int main(void) { // Filter for a 2.0 or newer platform and set it as the default std::vector platforms; cl::Platform::get(&platforms); cl::Platform plat; for (auto &p : platforms) { std::string platver = p.getInfo(); if (platver.find("OpenCL 2.") != std::string::npos || platver.find("OpenCL 3.") != std::string::npos) { // Note: an OpenCL 3.x platform may not support all required features! plat = p; } } if (plat() == 0) { std::cout << "No OpenCL 2.0 or newer platform found.\n"; return -1; } cl::Platform newP = cl::Platform::setDefault(plat); if (newP != plat) { std::cout << "Error setting default platform.\n"; return -1; } // C++11 raw string literal for the first kernel std::string kernel1{R"CLC( global int globalA; kernel void updateGlobal() { globalA = 75; } )CLC"}; // Raw string literal for the second kernel std::string kernel2{R"CLC( typedef struct { global int *bar; } Foo; kernel void vectorAdd(global const Foo* aNum, global const int *inputA, global const int *inputB, global int *output, int val, write_only pipe int outPipe, queue_t childQueue) { output[get_global_id(0)] = inputA[get_global_id(0)] + inputB[get_global_id(0)] + val + *(aNum->bar); write_pipe(outPipe, &val); queue_t default_queue = get_default_queue(); ndrange_t ndrange = ndrange_1D(get_global_size(0)/2, get_global_size(0)/2); // Have a child kernel write into third quarter of output enqueue_kernel(default_queue, CLK_ENQUEUE_FLAGS_WAIT_KERNEL, ndrange, ^{ output[get_global_size(0)*2 + get_global_id(0)] = inputA[get_global_size(0)*2 + get_global_id(0)] + inputB[get_global_size(0)*2 + get_global_id(0)] + globalA; }); // Have a child kernel write into last quarter of output enqueue_kernel(childQueue, CLK_ENQUEUE_FLAGS_WAIT_KERNEL, ndrange, ^{ output[get_global_size(0)*3 + get_global_id(0)] = inputA[get_global_size(0)*3 + get_global_id(0)] + inputB[get_global_size(0)*3 + get_global_id(0)] + globalA + 2; }); } )CLC"}; std::vector programStrings; programStrings.push_back(kernel1); programStrings.push_back(kernel2); cl::Program vectorAddProgram(programStrings); try { vectorAddProgram.build("-cl-std=CL2.0"); } catch (...) { // Print build info for all devices cl_int buildErr = CL_SUCCESS; auto buildInfo = vectorAddProgram.getBuildInfo(&buildErr); for (auto &pair : buildInfo) { std::cerr << pair.second << std::endl << std::endl; } return 1; } typedef struct { int *bar; } Foo; // Get and run kernel that initializes the program-scope global // A test for kernels that take no arguments auto program2Kernel = cl::KernelFunctor<>(vectorAddProgram, "updateGlobal"); program2Kernel( cl::EnqueueArgs( cl::NDRange(1))); ////////////////// // SVM allocations auto anSVMInt = cl::allocate_svm>(); *anSVMInt = 5; cl::SVMAllocator>> svmAllocReadOnly; auto fooPointer = cl::allocate_pointer(svmAllocReadOnly); fooPointer->bar = anSVMInt.get(); cl::SVMAllocator> svmAlloc; std::vector>> inputA(numElements, 1, svmAlloc); cl::coarse_svm_vector inputB(numElements, 2, svmAlloc); ////////////// // Traditional cl_mem allocations std::vector output(numElements, 0xdeadbeef); cl::Buffer outputBuffer(begin(output), end(output), false); cl::Pipe aPipe(sizeof(cl_int), numElements / 2); // Default command queue, also passed in as a parameter cl::DeviceCommandQueue defaultDeviceQueue = cl::DeviceCommandQueue::makeDefault( cl::Context::getDefault(), cl::Device::getDefault()); auto vectorAddKernel = cl::KernelFunctor< decltype(fooPointer)&, int*, cl::coarse_svm_vector&, cl::Buffer, int, cl::Pipe&, cl::DeviceCommandQueue >(vectorAddProgram, "vectorAdd"); // Ensure that the additional SVM pointer is available to the kernel // This one was not passed as a parameter vectorAddKernel.setSVMPointers(anSVMInt); cl_int error; vectorAddKernel( cl::EnqueueArgs( cl::NDRange(numElements/2), cl::NDRange(numElements/2)), fooPointer, inputA.data(), inputB, outputBuffer, 3, aPipe, defaultDeviceQueue, error ); cl::copy(outputBuffer, begin(output), end(output)); cl::Device d = cl::Device::getDefault(); std::cout << "Output:\n"; for (int i = 1; i < numElements; ++i) { std::cout << "\t" << output[i] << "\n"; } std::cout << "\n\n"; return 0; } * * \endcode * */ #ifndef CL_HPP_ #define CL_HPP_ /* Handle deprecated preprocessor definitions. In each case, we only check for * the old name if the new name is not defined, so that user code can define * both and hence work with either version of the bindings. */ #if !defined(CL_HPP_USE_DX_INTEROP) && defined(USE_DX_INTEROP) # pragma message("opencl.hpp: USE_DX_INTEROP is deprecated. Define CL_HPP_USE_DX_INTEROP instead") # define CL_HPP_USE_DX_INTEROP #endif #if !defined(CL_HPP_ENABLE_EXCEPTIONS) && defined(__CL_ENABLE_EXCEPTIONS) # pragma message("opencl.hpp: __CL_ENABLE_EXCEPTIONS is deprecated. Define CL_HPP_ENABLE_EXCEPTIONS instead") # define CL_HPP_ENABLE_EXCEPTIONS #endif #if !defined(CL_HPP_NO_STD_VECTOR) && defined(__NO_STD_VECTOR) # pragma message("opencl.hpp: __NO_STD_VECTOR is deprecated. Define CL_HPP_NO_STD_VECTOR instead") # define CL_HPP_NO_STD_VECTOR #endif #if !defined(CL_HPP_NO_STD_STRING) && defined(__NO_STD_STRING) # pragma message("opencl.hpp: __NO_STD_STRING is deprecated. Define CL_HPP_NO_STD_STRING instead") # define CL_HPP_NO_STD_STRING #endif #if defined(VECTOR_CLASS) # pragma message("opencl.hpp: VECTOR_CLASS is deprecated. Alias cl::vector instead") #endif #if defined(STRING_CLASS) # pragma message("opencl.hpp: STRING_CLASS is deprecated. Alias cl::string instead.") #endif #if !defined(CL_HPP_USER_OVERRIDE_ERROR_STRINGS) && defined(__CL_USER_OVERRIDE_ERROR_STRINGS) # pragma message("opencl.hpp: __CL_USER_OVERRIDE_ERROR_STRINGS is deprecated. Define CL_HPP_USER_OVERRIDE_ERROR_STRINGS instead") # define CL_HPP_USER_OVERRIDE_ERROR_STRINGS #endif /* Warn about features that are no longer supported */ #if defined(__USE_DEV_VECTOR) # pragma message("opencl.hpp: __USE_DEV_VECTOR is no longer supported. Expect compilation errors") #endif #if defined(__USE_DEV_STRING) # pragma message("opencl.hpp: __USE_DEV_STRING is no longer supported. Expect compilation errors") #endif /* Detect which version to target */ #if !defined(CL_HPP_TARGET_OPENCL_VERSION) # pragma message("opencl.hpp: CL_HPP_TARGET_OPENCL_VERSION is not defined. It will default to 300 (OpenCL 3.0)") # define CL_HPP_TARGET_OPENCL_VERSION 300 #endif #if CL_HPP_TARGET_OPENCL_VERSION != 100 && \ CL_HPP_TARGET_OPENCL_VERSION != 110 && \ CL_HPP_TARGET_OPENCL_VERSION != 120 && \ CL_HPP_TARGET_OPENCL_VERSION != 200 && \ CL_HPP_TARGET_OPENCL_VERSION != 210 && \ CL_HPP_TARGET_OPENCL_VERSION != 220 && \ CL_HPP_TARGET_OPENCL_VERSION != 300 # pragma message("opencl.hpp: CL_HPP_TARGET_OPENCL_VERSION is not a valid value (100, 110, 120, 200, 210, 220 or 300). It will be set to 300 (OpenCL 3.0).") # undef CL_HPP_TARGET_OPENCL_VERSION # define CL_HPP_TARGET_OPENCL_VERSION 300 #endif /* Forward target OpenCL version to C headers if necessary */ #if defined(CL_TARGET_OPENCL_VERSION) /* Warn if prior definition of CL_TARGET_OPENCL_VERSION is lower than * requested C++ bindings version */ #if CL_TARGET_OPENCL_VERSION < CL_HPP_TARGET_OPENCL_VERSION # pragma message("CL_TARGET_OPENCL_VERSION is already defined as is lower than CL_HPP_TARGET_OPENCL_VERSION") #endif #else # define CL_TARGET_OPENCL_VERSION CL_HPP_TARGET_OPENCL_VERSION #endif #if !defined(CL_HPP_MINIMUM_OPENCL_VERSION) # define CL_HPP_MINIMUM_OPENCL_VERSION 200 #endif #if CL_HPP_MINIMUM_OPENCL_VERSION != 100 && \ CL_HPP_MINIMUM_OPENCL_VERSION != 110 && \ CL_HPP_MINIMUM_OPENCL_VERSION != 120 && \ CL_HPP_MINIMUM_OPENCL_VERSION != 200 && \ CL_HPP_MINIMUM_OPENCL_VERSION != 210 && \ CL_HPP_MINIMUM_OPENCL_VERSION != 220 && \ CL_HPP_MINIMUM_OPENCL_VERSION != 300 # pragma message("opencl.hpp: CL_HPP_MINIMUM_OPENCL_VERSION is not a valid value (100, 110, 120, 200, 210, 220 or 300). It will be set to 100") # undef CL_HPP_MINIMUM_OPENCL_VERSION # define CL_HPP_MINIMUM_OPENCL_VERSION 100 #endif #if CL_HPP_MINIMUM_OPENCL_VERSION > CL_HPP_TARGET_OPENCL_VERSION # error "CL_HPP_MINIMUM_OPENCL_VERSION must not be greater than CL_HPP_TARGET_OPENCL_VERSION" #endif #if CL_HPP_MINIMUM_OPENCL_VERSION <= 100 && !defined(CL_USE_DEPRECATED_OPENCL_1_0_APIS) # define CL_USE_DEPRECATED_OPENCL_1_0_APIS #endif #if CL_HPP_MINIMUM_OPENCL_VERSION <= 110 && !defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) # define CL_USE_DEPRECATED_OPENCL_1_1_APIS #endif #if CL_HPP_MINIMUM_OPENCL_VERSION <= 120 && !defined(CL_USE_DEPRECATED_OPENCL_1_2_APIS) # define CL_USE_DEPRECATED_OPENCL_1_2_APIS #endif #if CL_HPP_MINIMUM_OPENCL_VERSION <= 200 && !defined(CL_USE_DEPRECATED_OPENCL_2_0_APIS) # define CL_USE_DEPRECATED_OPENCL_2_0_APIS #endif #if CL_HPP_MINIMUM_OPENCL_VERSION <= 210 && !defined(CL_USE_DEPRECATED_OPENCL_2_1_APIS) # define CL_USE_DEPRECATED_OPENCL_2_1_APIS #endif #if CL_HPP_MINIMUM_OPENCL_VERSION <= 220 && !defined(CL_USE_DEPRECATED_OPENCL_2_2_APIS) # define CL_USE_DEPRECATED_OPENCL_2_2_APIS #endif #ifdef _WIN32 #include #if defined(CL_HPP_USE_DX_INTEROP) #include #include #endif #endif // _WIN32 #if defined(_MSC_VER) #include #endif // _MSC_VER // Check for a valid C++ version // Need to do both tests here because for some reason __cplusplus is not // updated in visual studio #if (!defined(_MSC_VER) && __cplusplus < 201103L) || (defined(_MSC_VER) && _MSC_VER < 1700) #error Visual studio 2013 or another C++11-supporting compiler required #endif #if (__cplusplus >= 201103L || _MSVC_LANG >= 201103L ) #define CL_HPP_NOEXCEPT_ noexcept #else #define CL_HPP_NOEXCEPT_ #endif #if __cplusplus >= 201703L # define CL_HPP_DEFINE_STATIC_MEMBER_ inline #elif defined(_MSC_VER) # define CL_HPP_DEFINE_STATIC_MEMBER_ __declspec(selectany) #elif defined(__MINGW32__) # define CL_HPP_DEFINE_STATIC_MEMBER_ __attribute__((selectany)) #else # define CL_HPP_DEFINE_STATIC_MEMBER_ __attribute__((weak)) #endif // !_MSC_VER // Define deprecated prefixes and suffixes to ensure compilation // in case they are not pre-defined #if !defined(CL_API_PREFIX__VERSION_1_1_DEPRECATED) #define CL_API_PREFIX__VERSION_1_1_DEPRECATED #endif // #if !defined(CL_API_PREFIX__VERSION_1_1_DEPRECATED) #if !defined(CL_API_SUFFIX__VERSION_1_1_DEPRECATED) #define CL_API_SUFFIX__VERSION_1_1_DEPRECATED #endif // #if !defined(CL_API_SUFFIX__VERSION_1_1_DEPRECATED) #if !defined(CL_API_PREFIX__VERSION_1_2_DEPRECATED) #define CL_API_PREFIX__VERSION_1_2_DEPRECATED #endif // #if !defined(CL_API_PREFIX__VERSION_1_2_DEPRECATED) #if !defined(CL_API_SUFFIX__VERSION_1_2_DEPRECATED) #define CL_API_SUFFIX__VERSION_1_2_DEPRECATED #endif // #if !defined(CL_API_SUFFIX__VERSION_1_2_DEPRECATED) #if !defined(CL_API_PREFIX__VERSION_2_2_DEPRECATED) #define CL_API_PREFIX__VERSION_2_2_DEPRECATED #endif // #if !defined(CL_API_PREFIX__VERSION_2_2_DEPRECATED) #if !defined(CL_API_SUFFIX__VERSION_2_2_DEPRECATED) #define CL_API_SUFFIX__VERSION_2_2_DEPRECATED #endif // #if !defined(CL_API_SUFFIX__VERSION_2_2_DEPRECATED) #if !defined(CL_CALLBACK) #define CL_CALLBACK #endif //CL_CALLBACK #include #include #include #include #include #include // Define a size_type to represent a correctly resolved size_t #if defined(CL_HPP_ENABLE_SIZE_T_COMPATIBILITY) namespace cl { using size_type = ::size_t; } // namespace cl #else // #if defined(CL_HPP_ENABLE_SIZE_T_COMPATIBILITY) namespace cl { using size_type = size_t; } // namespace cl #endif // #if defined(CL_HPP_ENABLE_SIZE_T_COMPATIBILITY) #if defined(CL_HPP_ENABLE_EXCEPTIONS) #include #endif // #if defined(CL_HPP_ENABLE_EXCEPTIONS) #if !defined(CL_HPP_NO_STD_VECTOR) #include namespace cl { template < class T, class Alloc = std::allocator > using vector = std::vector; } // namespace cl #endif // #if !defined(CL_HPP_NO_STD_VECTOR) #if !defined(CL_HPP_NO_STD_STRING) #include namespace cl { using string = std::string; } // namespace cl #endif // #if !defined(CL_HPP_NO_STD_STRING) #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #if !defined(CL_HPP_NO_STD_UNIQUE_PTR) #include namespace cl { // Replace unique_ptr and allocate_pointer for internal use // to allow user to replace them template using pointer = std::unique_ptr; } // namespace cl #endif #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #if !defined(CL_HPP_NO_STD_ARRAY) #include namespace cl { template < class T, size_type N > using array = std::array; } // namespace cl #endif // #if !defined(CL_HPP_NO_STD_ARRAY) // Define size_type appropriately to allow backward-compatibility // use of the old size_t interface class #if defined(CL_HPP_ENABLE_SIZE_T_COMPATIBILITY) namespace cl { namespace compatibility { /*! \brief class used to interface between C++ and * OpenCL C calls that require arrays of size_t values, whose * size is known statically. */ template class size_t { private: size_type data_[N]; public: //! \brief Initialize size_t to all 0s size_t() { for (int i = 0; i < N; ++i) { data_[i] = 0; } } size_t(const array &rhs) { for (int i = 0; i < N; ++i) { data_[i] = rhs[i]; } } size_type& operator[](int index) { return data_[index]; } const size_type& operator[](int index) const { return data_[index]; } //! \brief Conversion operator to T*. operator size_type* () { return data_; } //! \brief Conversion operator to const T*. operator const size_type* () const { return data_; } operator array() const { array ret; for (int i = 0; i < N; ++i) { ret[i] = data_[i]; } return ret; } }; } // namespace compatibility template using size_t = compatibility::size_t; } // namespace cl #endif // #if defined(CL_HPP_ENABLE_SIZE_T_COMPATIBILITY) // Helper alias to avoid confusing the macros namespace cl { namespace detail { using size_t_array = array; } // namespace detail } // namespace cl /*! \namespace cl * * \brief The OpenCL C++ bindings are defined within this namespace. * */ namespace cl { #define CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(name) \ using PFN_##name = name##_fn #define CL_HPP_INIT_CL_EXT_FCN_PTR_(name) \ if (!pfn_##name) { \ pfn_##name = (PFN_##name)clGetExtensionFunctionAddress(#name); \ } #define CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, name) \ if (!pfn_##name) { \ pfn_##name = (PFN_##name) \ clGetExtensionFunctionAddressForPlatform(platform, #name); \ } #ifdef cl_khr_external_memory enum class ExternalMemoryType : cl_external_memory_handle_type_khr; #endif class Memory; class Program; class Device; class Context; class CommandQueue; class DeviceCommandQueue; class Memory; class Buffer; class Pipe; #ifdef cl_khr_semaphore class Semaphore; #endif #if defined(cl_khr_command_buffer) class CommandBufferKhr; class MutableCommandKhr; #endif // cl_khr_command_buffer #if defined(CL_HPP_ENABLE_EXCEPTIONS) /*! \brief Exception class * * This may be thrown by API functions when CL_HPP_ENABLE_EXCEPTIONS is defined. */ class Error : public std::exception { private: cl_int err_; const char * errStr_; public: /*! \brief Create a new CL error exception for a given error code * and corresponding message. * * \param err error code value. * * \param errStr a descriptive string that must remain in scope until * handling of the exception has concluded. If set, it * will be returned by what(). */ Error(cl_int err, const char * errStr = nullptr) : err_(err), errStr_(errStr) {} ~Error() throw() {} /*! \brief Get error string associated with exception * * \return A memory pointer to the error message string. */ virtual const char * what() const throw () { if (errStr_ == nullptr) { return "empty"; } else { return errStr_; } } /*! \brief Get error code associated with exception * * \return The error code. */ cl_int err(void) const { return err_; } }; #define CL_HPP_ERR_STR_(x) #x #else #define CL_HPP_ERR_STR_(x) nullptr #endif // CL_HPP_ENABLE_EXCEPTIONS namespace detail { #if defined(CL_HPP_ENABLE_EXCEPTIONS) static inline cl_int errHandler ( cl_int err, const char * errStr = nullptr) { if (err != CL_SUCCESS) { throw Error(err, errStr); } return err; } #else static inline cl_int errHandler (cl_int err, const char * errStr = nullptr) { (void) errStr; // suppress unused variable warning return err; } #endif // CL_HPP_ENABLE_EXCEPTIONS } //! \cond DOXYGEN_DETAIL #if !defined(CL_HPP_USER_OVERRIDE_ERROR_STRINGS) #define __GET_DEVICE_INFO_ERR CL_HPP_ERR_STR_(clGetDeviceInfo) #define __GET_PLATFORM_INFO_ERR CL_HPP_ERR_STR_(clGetPlatformInfo) #define __GET_DEVICE_IDS_ERR CL_HPP_ERR_STR_(clGetDeviceIDs) #define __GET_PLATFORM_IDS_ERR CL_HPP_ERR_STR_(clGetPlatformIDs) #define __GET_CONTEXT_INFO_ERR CL_HPP_ERR_STR_(clGetContextInfo) #define __GET_EVENT_INFO_ERR CL_HPP_ERR_STR_(clGetEventInfo) #define __GET_EVENT_PROFILE_INFO_ERR CL_HPP_ERR_STR_(clGetEventProfileInfo) #define __GET_MEM_OBJECT_INFO_ERR CL_HPP_ERR_STR_(clGetMemObjectInfo) #define __GET_IMAGE_INFO_ERR CL_HPP_ERR_STR_(clGetImageInfo) #define __GET_SAMPLER_INFO_ERR CL_HPP_ERR_STR_(clGetSamplerInfo) #define __GET_KERNEL_INFO_ERR CL_HPP_ERR_STR_(clGetKernelInfo) #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __GET_KERNEL_ARG_INFO_ERR CL_HPP_ERR_STR_(clGetKernelArgInfo) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_TARGET_OPENCL_VERSION >= 210 #define __GET_KERNEL_SUB_GROUP_INFO_ERR CL_HPP_ERR_STR_(clGetKernelSubGroupInfo) #else #define __GET_KERNEL_SUB_GROUP_INFO_ERR CL_HPP_ERR_STR_(clGetKernelSubGroupInfoKHR) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 210 #define __GET_KERNEL_WORK_GROUP_INFO_ERR CL_HPP_ERR_STR_(clGetKernelWorkGroupInfo) #define __GET_PROGRAM_INFO_ERR CL_HPP_ERR_STR_(clGetProgramInfo) #define __GET_PROGRAM_BUILD_INFO_ERR CL_HPP_ERR_STR_(clGetProgramBuildInfo) #define __GET_COMMAND_QUEUE_INFO_ERR CL_HPP_ERR_STR_(clGetCommandQueueInfo) #define __CREATE_CONTEXT_ERR CL_HPP_ERR_STR_(clCreateContext) #define __CREATE_CONTEXT_FROM_TYPE_ERR CL_HPP_ERR_STR_(clCreateContextFromType) #define __GET_SUPPORTED_IMAGE_FORMATS_ERR CL_HPP_ERR_STR_(clGetSupportedImageFormats) #if CL_HPP_TARGET_OPENCL_VERSION >= 300 #define __SET_CONTEXT_DESCTRUCTOR_CALLBACK_ERR CL_HPP_ERR_STR_(clSetContextDestructorCallback) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 300 #define __CREATE_BUFFER_ERR CL_HPP_ERR_STR_(clCreateBuffer) #define __COPY_ERR CL_HPP_ERR_STR_(cl::copy) #define __CREATE_SUBBUFFER_ERR CL_HPP_ERR_STR_(clCreateSubBuffer) #define __CREATE_GL_BUFFER_ERR CL_HPP_ERR_STR_(clCreateFromGLBuffer) #define __CREATE_GL_RENDER_BUFFER_ERR CL_HPP_ERR_STR_(clCreateFromGLBuffer) #define __GET_GL_OBJECT_INFO_ERR CL_HPP_ERR_STR_(clGetGLObjectInfo) #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __CREATE_IMAGE_ERR CL_HPP_ERR_STR_(clCreateImage) #define __CREATE_GL_TEXTURE_ERR CL_HPP_ERR_STR_(clCreateFromGLTexture) #define __IMAGE_DIMENSION_ERR CL_HPP_ERR_STR_(Incorrect image dimensions) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR CL_HPP_ERR_STR_(clSetMemObjectDestructorCallback) #define __CREATE_USER_EVENT_ERR CL_HPP_ERR_STR_(clCreateUserEvent) #define __SET_USER_EVENT_STATUS_ERR CL_HPP_ERR_STR_(clSetUserEventStatus) #define __SET_EVENT_CALLBACK_ERR CL_HPP_ERR_STR_(clSetEventCallback) #define __WAIT_FOR_EVENTS_ERR CL_HPP_ERR_STR_(clWaitForEvents) #define __CREATE_KERNEL_ERR CL_HPP_ERR_STR_(clCreateKernel) #define __SET_KERNEL_ARGS_ERR CL_HPP_ERR_STR_(clSetKernelArg) #define __CREATE_PROGRAM_WITH_SOURCE_ERR CL_HPP_ERR_STR_(clCreateProgramWithSource) #define __CREATE_PROGRAM_WITH_BINARY_ERR CL_HPP_ERR_STR_(clCreateProgramWithBinary) #if CL_HPP_TARGET_OPENCL_VERSION >= 210 #define __CREATE_PROGRAM_WITH_IL_ERR CL_HPP_ERR_STR_(clCreateProgramWithIL) #else #define __CREATE_PROGRAM_WITH_IL_ERR CL_HPP_ERR_STR_(clCreateProgramWithILKHR) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 210 #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __CREATE_PROGRAM_WITH_BUILT_IN_KERNELS_ERR CL_HPP_ERR_STR_(clCreateProgramWithBuiltInKernels) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __BUILD_PROGRAM_ERR CL_HPP_ERR_STR_(clBuildProgram) #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __COMPILE_PROGRAM_ERR CL_HPP_ERR_STR_(clCompileProgram) #define __LINK_PROGRAM_ERR CL_HPP_ERR_STR_(clLinkProgram) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __CREATE_KERNELS_IN_PROGRAM_ERR CL_HPP_ERR_STR_(clCreateKernelsInProgram) #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #define __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR CL_HPP_ERR_STR_(clCreateCommandQueueWithProperties) #define __CREATE_SAMPLER_WITH_PROPERTIES_ERR CL_HPP_ERR_STR_(clCreateSamplerWithProperties) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #define __SET_COMMAND_QUEUE_PROPERTY_ERR CL_HPP_ERR_STR_(clSetCommandQueueProperty) #define __ENQUEUE_READ_BUFFER_ERR CL_HPP_ERR_STR_(clEnqueueReadBuffer) #define __ENQUEUE_READ_BUFFER_RECT_ERR CL_HPP_ERR_STR_(clEnqueueReadBufferRect) #define __ENQUEUE_WRITE_BUFFER_ERR CL_HPP_ERR_STR_(clEnqueueWriteBuffer) #define __ENQUEUE_WRITE_BUFFER_RECT_ERR CL_HPP_ERR_STR_(clEnqueueWriteBufferRect) #define __ENQEUE_COPY_BUFFER_ERR CL_HPP_ERR_STR_(clEnqueueCopyBuffer) #define __ENQEUE_COPY_BUFFER_RECT_ERR CL_HPP_ERR_STR_(clEnqueueCopyBufferRect) #define __ENQUEUE_FILL_BUFFER_ERR CL_HPP_ERR_STR_(clEnqueueFillBuffer) #define __ENQUEUE_READ_IMAGE_ERR CL_HPP_ERR_STR_(clEnqueueReadImage) #define __ENQUEUE_WRITE_IMAGE_ERR CL_HPP_ERR_STR_(clEnqueueWriteImage) #define __ENQUEUE_COPY_IMAGE_ERR CL_HPP_ERR_STR_(clEnqueueCopyImage) #define __ENQUEUE_FILL_IMAGE_ERR CL_HPP_ERR_STR_(clEnqueueFillImage) #define __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR CL_HPP_ERR_STR_(clEnqueueCopyImageToBuffer) #define __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR CL_HPP_ERR_STR_(clEnqueueCopyBufferToImage) #define __ENQUEUE_MAP_BUFFER_ERR CL_HPP_ERR_STR_(clEnqueueMapBuffer) #define __ENQUEUE_MAP_IMAGE_ERR CL_HPP_ERR_STR_(clEnqueueMapImage) #define __ENQUEUE_UNMAP_MEM_OBJECT_ERR CL_HPP_ERR_STR_(clEnqueueUnMapMemObject) #define __ENQUEUE_NDRANGE_KERNEL_ERR CL_HPP_ERR_STR_(clEnqueueNDRangeKernel) #define __ENQUEUE_NATIVE_KERNEL CL_HPP_ERR_STR_(clEnqueueNativeKernel) #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __ENQUEUE_MIGRATE_MEM_OBJECTS_ERR CL_HPP_ERR_STR_(clEnqueueMigrateMemObjects) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_TARGET_OPENCL_VERSION >= 210 #define __ENQUEUE_MIGRATE_SVM_ERR CL_HPP_ERR_STR_(clEnqueueSVMMigrateMem) #define __SET_DEFAULT_DEVICE_COMMAND_QUEUE_ERR CL_HPP_ERR_STR_(clSetDefaultDeviceCommandQueue) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 210 #define __ENQUEUE_ACQUIRE_GL_ERR CL_HPP_ERR_STR_(clEnqueueAcquireGLObjects) #define __ENQUEUE_RELEASE_GL_ERR CL_HPP_ERR_STR_(clEnqueueReleaseGLObjects) #define __CREATE_PIPE_ERR CL_HPP_ERR_STR_(clCreatePipe) #define __GET_PIPE_INFO_ERR CL_HPP_ERR_STR_(clGetPipeInfo) #define __RETAIN_ERR CL_HPP_ERR_STR_(Retain Object) #define __RELEASE_ERR CL_HPP_ERR_STR_(Release Object) #define __FLUSH_ERR CL_HPP_ERR_STR_(clFlush) #define __FINISH_ERR CL_HPP_ERR_STR_(clFinish) #define __VECTOR_CAPACITY_ERR CL_HPP_ERR_STR_(Vector capacity error) #if CL_HPP_TARGET_OPENCL_VERSION >= 210 #define __GET_HOST_TIMER_ERR CL_HPP_ERR_STR_(clGetHostTimer) #define __GET_DEVICE_AND_HOST_TIMER_ERR CL_HPP_ERR_STR_(clGetDeviceAndHostTimer) #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 220 #define __SET_PROGRAM_RELEASE_CALLBACK_ERR CL_HPP_ERR_STR_(clSetProgramReleaseCallback) #define __SET_PROGRAM_SPECIALIZATION_CONSTANT_ERR CL_HPP_ERR_STR_(clSetProgramSpecializationConstant) #endif #ifdef cl_khr_external_memory #define __ENQUEUE_ACQUIRE_EXTERNAL_MEMORY_ERR CL_HPP_ERR_STR_(clEnqueueAcquireExternalMemObjectsKHR) #define __ENQUEUE_RELEASE_EXTERNAL_MEMORY_ERR CL_HPP_ERR_STR_(clEnqueueReleaseExternalMemObjectsKHR) #endif #ifdef cl_khr_semaphore #define __GET_SEMAPHORE_KHR_INFO_ERR CL_HPP_ERR_STR_(clGetSemaphoreInfoKHR) #define __CREATE_SEMAPHORE_KHR_WITH_PROPERTIES_ERR CL_HPP_ERR_STR_(clCreateSemaphoreWithPropertiesKHR) #define __ENQUEUE_WAIT_SEMAPHORE_KHR_ERR CL_HPP_ERR_STR_(clEnqueueWaitSemaphoresKHR) #define __ENQUEUE_SIGNAL_SEMAPHORE_KHR_ERR CL_HPP_ERR_STR_(clEnqueueSignalSemaphoresKHR) #define __RETAIN_SEMAPHORE_KHR_ERR CL_HPP_ERR_STR_(clRetainSemaphoreKHR) #define __RELEASE_SEMAPHORE_KHR_ERR CL_HPP_ERR_STR_(clReleaseSemaphoreKHR) #endif #if defined(cl_khr_command_buffer) #define __CREATE_COMMAND_BUFFER_KHR_ERR CL_HPP_ERR_STR_(clCreateCommandBufferKHR) #define __GET_COMMAND_BUFFER_INFO_KHR_ERR CL_HPP_ERR_STR_(clGetCommandBufferInfoKHR) #define __FINALIZE_COMMAND_BUFFER_KHR_ERR CL_HPP_ERR_STR_(clFinalizeCommandBufferKHR) #define __ENQUEUE_COMMAND_BUFFER_KHR_ERR CL_HPP_ERR_STR_(clEnqueueCommandBufferKHR) #define __COMMAND_BARRIER_WITH_WAIT_LIST_KHR_ERR CL_HPP_ERR_STR_(clCommandBarrierWithWaitListKHR) #define __COMMAND_COPY_BUFFER_KHR_ERR CL_HPP_ERR_STR_(clCommandCopyBufferKHR) #define __COMMAND_COPY_BUFFER_RECT_KHR_ERR CL_HPP_ERR_STR_(clCommandCopyBufferRectKHR) #define __COMMAND_COPY_BUFFER_TO_IMAGE_KHR_ERR CL_HPP_ERR_STR_(clCommandCopyBufferToImageKHR) #define __COMMAND_COPY_IMAGE_KHR_ERR CL_HPP_ERR_STR_(clCommandCopyImageKHR) #define __COMMAND_COPY_IMAGE_TO_BUFFER_KHR_ERR CL_HPP_ERR_STR_(clCommandCopyImageToBufferKHR) #define __COMMAND_FILL_BUFFER_KHR_ERR CL_HPP_ERR_STR_(clCommandFillBufferKHR) #define __COMMAND_FILL_IMAGE_KHR_ERR CL_HPP_ERR_STR_(clCommandFillImageKHR) #define __COMMAND_NDRANGE_KERNEL_KHR_ERR CL_HPP_ERR_STR_(clCommandNDRangeKernelKHR) #define __UPDATE_MUTABLE_COMMANDS_KHR_ERR CL_HPP_ERR_STR_(clUpdateMutableCommandsKHR) #define __GET_MUTABLE_COMMAND_INFO_KHR_ERR CL_HPP_ERR_STR_(clGetMutableCommandInfoKHR) #define __RETAIN_COMMAND_BUFFER_KHR_ERR CL_HPP_ERR_STR_(clRetainCommandBufferKHR) #define __RELEASE_COMMAND_BUFFER_KHR_ERR CL_HPP_ERR_STR_(clReleaseCommandBufferKHR) #endif // cl_khr_command_buffer /** * CL 1.2 version that uses device fission. */ #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __CREATE_SUB_DEVICES_ERR CL_HPP_ERR_STR_(clCreateSubDevices) #else #define __CREATE_SUB_DEVICES_ERR CL_HPP_ERR_STR_(clCreateSubDevicesEXT) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * Deprecated APIs for 1.2 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) #define __ENQUEUE_MARKER_ERR CL_HPP_ERR_STR_(clEnqueueMarker) #define __ENQUEUE_WAIT_FOR_EVENTS_ERR CL_HPP_ERR_STR_(clEnqueueWaitForEvents) #define __ENQUEUE_BARRIER_ERR CL_HPP_ERR_STR_(clEnqueueBarrier) #define __UNLOAD_COMPILER_ERR CL_HPP_ERR_STR_(clUnloadCompiler) #define __CREATE_GL_TEXTURE_2D_ERR CL_HPP_ERR_STR_(clCreateFromGLTexture2D) #define __CREATE_GL_TEXTURE_3D_ERR CL_HPP_ERR_STR_(clCreateFromGLTexture3D) #define __CREATE_IMAGE2D_ERR CL_HPP_ERR_STR_(clCreateImage2D) #define __CREATE_IMAGE3D_ERR CL_HPP_ERR_STR_(clCreateImage3D) #endif // #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) /** * Deprecated APIs for 2.0 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_2_APIS) #define __CREATE_COMMAND_QUEUE_ERR CL_HPP_ERR_STR_(clCreateCommandQueue) #define __ENQUEUE_TASK_ERR CL_HPP_ERR_STR_(clEnqueueTask) #define __CREATE_SAMPLER_ERR CL_HPP_ERR_STR_(clCreateSampler) #endif // #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) /** * CL 1.2 marker and barrier commands */ #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #define __ENQUEUE_MARKER_WAIT_LIST_ERR CL_HPP_ERR_STR_(clEnqueueMarkerWithWaitList) #define __ENQUEUE_BARRIER_WAIT_LIST_ERR CL_HPP_ERR_STR_(clEnqueueBarrierWithWaitList) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_TARGET_OPENCL_VERSION >= 210 #define __CLONE_KERNEL_ERR CL_HPP_ERR_STR_(clCloneKernel) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 210 #endif // CL_HPP_USER_OVERRIDE_ERROR_STRINGS //! \endcond #ifdef cl_khr_external_memory CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clEnqueueAcquireExternalMemObjectsKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clEnqueueReleaseExternalMemObjectsKHR); CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clEnqueueAcquireExternalMemObjectsKHR pfn_clEnqueueAcquireExternalMemObjectsKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clEnqueueReleaseExternalMemObjectsKHR pfn_clEnqueueReleaseExternalMemObjectsKHR = nullptr; #endif // cl_khr_external_memory #ifdef cl_khr_semaphore CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clCreateSemaphoreWithPropertiesKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clReleaseSemaphoreKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clRetainSemaphoreKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clEnqueueWaitSemaphoresKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clEnqueueSignalSemaphoresKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clGetSemaphoreInfoKHR); CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clCreateSemaphoreWithPropertiesKHR pfn_clCreateSemaphoreWithPropertiesKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clReleaseSemaphoreKHR pfn_clReleaseSemaphoreKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clRetainSemaphoreKHR pfn_clRetainSemaphoreKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clEnqueueWaitSemaphoresKHR pfn_clEnqueueWaitSemaphoresKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clEnqueueSignalSemaphoresKHR pfn_clEnqueueSignalSemaphoresKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clGetSemaphoreInfoKHR pfn_clGetSemaphoreInfoKHR = nullptr; #endif // cl_khr_semaphore #if defined(cl_khr_command_buffer) CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clCreateCommandBufferKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clFinalizeCommandBufferKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clRetainCommandBufferKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clReleaseCommandBufferKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clGetCommandBufferInfoKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clEnqueueCommandBufferKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clCommandBarrierWithWaitListKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clCommandCopyBufferKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clCommandCopyBufferRectKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clCommandCopyBufferToImageKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clCommandCopyImageKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clCommandCopyImageToBufferKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clCommandFillBufferKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clCommandFillImageKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clCommandNDRangeKernelKHR); CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clCreateCommandBufferKHR pfn_clCreateCommandBufferKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clFinalizeCommandBufferKHR pfn_clFinalizeCommandBufferKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clRetainCommandBufferKHR pfn_clRetainCommandBufferKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clReleaseCommandBufferKHR pfn_clReleaseCommandBufferKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clGetCommandBufferInfoKHR pfn_clGetCommandBufferInfoKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clEnqueueCommandBufferKHR pfn_clEnqueueCommandBufferKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clCommandBarrierWithWaitListKHR pfn_clCommandBarrierWithWaitListKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clCommandCopyBufferKHR pfn_clCommandCopyBufferKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clCommandCopyBufferRectKHR pfn_clCommandCopyBufferRectKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clCommandCopyBufferToImageKHR pfn_clCommandCopyBufferToImageKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clCommandCopyImageKHR pfn_clCommandCopyImageKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clCommandCopyImageToBufferKHR pfn_clCommandCopyImageToBufferKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clCommandFillBufferKHR pfn_clCommandFillBufferKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clCommandFillImageKHR pfn_clCommandFillImageKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clCommandNDRangeKernelKHR pfn_clCommandNDRangeKernelKHR = nullptr; #endif /* cl_khr_command_buffer */ #if defined(cl_khr_command_buffer_mutable_dispatch) CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clUpdateMutableCommandsKHR); CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_(clGetMutableCommandInfoKHR); CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clUpdateMutableCommandsKHR pfn_clUpdateMutableCommandsKHR = nullptr; CL_HPP_DEFINE_STATIC_MEMBER_ PFN_clGetMutableCommandInfoKHR pfn_clGetMutableCommandInfoKHR = nullptr; #endif /* cl_khr_command_buffer_mutable_dispatch */ namespace detail { // Generic getInfoHelper. The final parameter is used to guide overload // resolution: the actual parameter passed is an int, which makes this // a worse conversion sequence than a specialization that declares the // parameter as an int. template inline cl_int getInfoHelper(Functor f, cl_uint name, T* param, long) { return f(name, sizeof(T), param, nullptr); } // Specialized for getInfo // Assumes that the output vector was correctly resized on the way in template inline cl_int getInfoHelper(Func f, cl_uint name, vector>* param, int) { if (name != CL_PROGRAM_BINARIES) { return CL_INVALID_VALUE; } if (param) { // Create array of pointers, calculate total size and pass pointer array in size_type numBinaries = param->size(); vector binariesPointers(numBinaries); for (size_type i = 0; i < numBinaries; ++i) { binariesPointers[i] = (*param)[i].data(); } cl_int err = f(name, numBinaries * sizeof(unsigned char*), binariesPointers.data(), nullptr); if (err != CL_SUCCESS) { return err; } } return CL_SUCCESS; } // Specialized getInfoHelper for vector params template inline cl_int getInfoHelper(Func f, cl_uint name, vector* param, long) { size_type required; cl_int err = f(name, 0, nullptr, &required); if (err != CL_SUCCESS) { return err; } const size_type elements = required / sizeof(T); // Temporary to avoid changing param on an error vector localData(elements); err = f(name, required, localData.data(), nullptr); if (err != CL_SUCCESS) { return err; } if (param) { *param = std::move(localData); } return CL_SUCCESS; } /* Specialization for reference-counted types. This depends on the * existence of Wrapper::cl_type, and none of the other types having the * cl_type member. Note that simplify specifying the parameter as Wrapper * does not work, because when using a derived type (e.g. Context) the generic * template will provide a better match. */ template inline cl_int getInfoHelper( Func f, cl_uint name, vector* param, int, typename T::cl_type = 0) { size_type required; cl_int err = f(name, 0, nullptr, &required); if (err != CL_SUCCESS) { return err; } const size_type elements = required / sizeof(typename T::cl_type); vector value(elements); err = f(name, required, value.data(), nullptr); if (err != CL_SUCCESS) { return err; } if (param) { // Assign to convert CL type to T for each element param->resize(elements); // Assign to param, constructing with retain behaviour // to correctly capture each underlying CL object for (size_type i = 0; i < elements; i++) { (*param)[i] = T(value[i], true); } } return CL_SUCCESS; } // Specialized GetInfoHelper for string params template inline cl_int getInfoHelper(Func f, cl_uint name, string* param, long) { size_type required; cl_int err = f(name, 0, nullptr, &required); if (err != CL_SUCCESS) { return err; } // std::string has a constant data member // a char vector does not if (required > 0) { vector value(required); err = f(name, required, value.data(), nullptr); if (err != CL_SUCCESS) { return err; } if (param) { param->assign(begin(value), prev(end(value))); } } else if (param) { param->assign(""); } return CL_SUCCESS; } // Specialized GetInfoHelper for clsize_t params template inline cl_int getInfoHelper(Func f, cl_uint name, array* param, long) { size_type required; cl_int err = f(name, 0, nullptr, &required); if (err != CL_SUCCESS) { return err; } size_type elements = required / sizeof(size_type); vector value(elements, 0); err = f(name, required, value.data(), nullptr); if (err != CL_SUCCESS) { return err; } // Bound the copy with N to prevent overruns // if passed N > than the amount copied if (elements > N) { elements = N; } for (size_type i = 0; i < elements; ++i) { (*param)[i] = value[i]; } return CL_SUCCESS; } template struct ReferenceHandler; /* Specialization for reference-counted types. This depends on the * existence of Wrapper::cl_type, and none of the other types having the * cl_type member. Note that simplify specifying the parameter as Wrapper * does not work, because when using a derived type (e.g. Context) the generic * template will provide a better match. */ template inline cl_int getInfoHelper(Func f, cl_uint name, T* param, int, typename T::cl_type = 0) { typename T::cl_type value; cl_int err = f(name, sizeof(value), &value, nullptr); if (err != CL_SUCCESS) { return err; } *param = value; if (value != nullptr) { err = param->retain(); if (err != CL_SUCCESS) { return err; } } return CL_SUCCESS; } #ifndef CL_DEVICE_HALF_FP_CONFIG // without requiring cl_ext.h #define CL_DEVICE_HALF_FP_CONFIG 0x1033 #endif #define CL_HPP_PARAM_NAME_INFO_1_0_(F) \ F(cl_platform_info, CL_PLATFORM_PROFILE, string) \ F(cl_platform_info, CL_PLATFORM_VERSION, string) \ F(cl_platform_info, CL_PLATFORM_NAME, string) \ F(cl_platform_info, CL_PLATFORM_VENDOR, string) \ F(cl_platform_info, CL_PLATFORM_EXTENSIONS, string) \ \ F(cl_device_info, CL_DEVICE_TYPE, cl_device_type) \ F(cl_device_info, CL_DEVICE_VENDOR_ID, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_COMPUTE_UNITS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_WORK_GROUP_SIZE, size_type) \ F(cl_device_info, CL_DEVICE_MAX_WORK_ITEM_SIZES, cl::vector) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_CLOCK_FREQUENCY, cl_uint) \ F(cl_device_info, CL_DEVICE_ADDRESS_BITS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_READ_IMAGE_ARGS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_WRITE_IMAGE_ARGS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_MEM_ALLOC_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_IMAGE2D_MAX_WIDTH, size_type) \ F(cl_device_info, CL_DEVICE_IMAGE2D_MAX_HEIGHT, size_type) \ F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_WIDTH, size_type) \ F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_HEIGHT, size_type) \ F(cl_device_info, CL_DEVICE_IMAGE3D_MAX_DEPTH, size_type) \ F(cl_device_info, CL_DEVICE_IMAGE_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_MAX_PARAMETER_SIZE, size_type) \ F(cl_device_info, CL_DEVICE_MAX_SAMPLERS, cl_uint) \ F(cl_device_info, CL_DEVICE_MEM_BASE_ADDR_ALIGN, cl_uint) \ F(cl_device_info, CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, cl_uint) \ F(cl_device_info, CL_DEVICE_SINGLE_FP_CONFIG, cl_device_fp_config) \ F(cl_device_info, CL_DEVICE_DOUBLE_FP_CONFIG, cl_device_fp_config) \ F(cl_device_info, CL_DEVICE_HALF_FP_CONFIG, cl_device_fp_config) \ F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, cl_device_mem_cache_type) \ F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, cl_uint)\ F(cl_device_info, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_GLOBAL_MEM_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_MAX_CONSTANT_ARGS, cl_uint) \ F(cl_device_info, CL_DEVICE_LOCAL_MEM_TYPE, cl_device_local_mem_type) \ F(cl_device_info, CL_DEVICE_LOCAL_MEM_SIZE, cl_ulong) \ F(cl_device_info, CL_DEVICE_ERROR_CORRECTION_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_PROFILING_TIMER_RESOLUTION, size_type) \ F(cl_device_info, CL_DEVICE_ENDIAN_LITTLE, cl_bool) \ F(cl_device_info, CL_DEVICE_AVAILABLE, cl_bool) \ F(cl_device_info, CL_DEVICE_COMPILER_AVAILABLE, cl_bool) \ F(cl_device_info, CL_DEVICE_EXECUTION_CAPABILITIES, cl_device_exec_capabilities) \ F(cl_device_info, CL_DEVICE_PLATFORM, cl_platform_id) \ F(cl_device_info, CL_DEVICE_NAME, string) \ F(cl_device_info, CL_DEVICE_VENDOR, string) \ F(cl_device_info, CL_DRIVER_VERSION, string) \ F(cl_device_info, CL_DEVICE_PROFILE, string) \ F(cl_device_info, CL_DEVICE_VERSION, string) \ F(cl_device_info, CL_DEVICE_EXTENSIONS, string) \ \ F(cl_context_info, CL_CONTEXT_REFERENCE_COUNT, cl_uint) \ F(cl_context_info, CL_CONTEXT_DEVICES, cl::vector) \ F(cl_context_info, CL_CONTEXT_PROPERTIES, cl::vector) \ \ F(cl_event_info, CL_EVENT_COMMAND_QUEUE, cl::CommandQueue) \ F(cl_event_info, CL_EVENT_COMMAND_TYPE, cl_command_type) \ F(cl_event_info, CL_EVENT_REFERENCE_COUNT, cl_uint) \ F(cl_event_info, CL_EVENT_COMMAND_EXECUTION_STATUS, cl_int) \ \ F(cl_profiling_info, CL_PROFILING_COMMAND_QUEUED, cl_ulong) \ F(cl_profiling_info, CL_PROFILING_COMMAND_SUBMIT, cl_ulong) \ F(cl_profiling_info, CL_PROFILING_COMMAND_START, cl_ulong) \ F(cl_profiling_info, CL_PROFILING_COMMAND_END, cl_ulong) \ \ F(cl_mem_info, CL_MEM_TYPE, cl_mem_object_type) \ F(cl_mem_info, CL_MEM_FLAGS, cl_mem_flags) \ F(cl_mem_info, CL_MEM_SIZE, size_type) \ F(cl_mem_info, CL_MEM_HOST_PTR, void*) \ F(cl_mem_info, CL_MEM_MAP_COUNT, cl_uint) \ F(cl_mem_info, CL_MEM_REFERENCE_COUNT, cl_uint) \ F(cl_mem_info, CL_MEM_CONTEXT, cl::Context) \ \ F(cl_image_info, CL_IMAGE_FORMAT, cl_image_format) \ F(cl_image_info, CL_IMAGE_ELEMENT_SIZE, size_type) \ F(cl_image_info, CL_IMAGE_ROW_PITCH, size_type) \ F(cl_image_info, CL_IMAGE_SLICE_PITCH, size_type) \ F(cl_image_info, CL_IMAGE_WIDTH, size_type) \ F(cl_image_info, CL_IMAGE_HEIGHT, size_type) \ F(cl_image_info, CL_IMAGE_DEPTH, size_type) \ \ F(cl_sampler_info, CL_SAMPLER_REFERENCE_COUNT, cl_uint) \ F(cl_sampler_info, CL_SAMPLER_CONTEXT, cl::Context) \ F(cl_sampler_info, CL_SAMPLER_NORMALIZED_COORDS, cl_bool) \ F(cl_sampler_info, CL_SAMPLER_ADDRESSING_MODE, cl_addressing_mode) \ F(cl_sampler_info, CL_SAMPLER_FILTER_MODE, cl_filter_mode) \ \ F(cl_program_info, CL_PROGRAM_REFERENCE_COUNT, cl_uint) \ F(cl_program_info, CL_PROGRAM_CONTEXT, cl::Context) \ F(cl_program_info, CL_PROGRAM_NUM_DEVICES, cl_uint) \ F(cl_program_info, CL_PROGRAM_DEVICES, cl::vector) \ F(cl_program_info, CL_PROGRAM_SOURCE, string) \ F(cl_program_info, CL_PROGRAM_BINARY_SIZES, cl::vector) \ F(cl_program_info, CL_PROGRAM_BINARIES, cl::vector>) \ \ F(cl_program_build_info, CL_PROGRAM_BUILD_STATUS, cl_build_status) \ F(cl_program_build_info, CL_PROGRAM_BUILD_OPTIONS, string) \ F(cl_program_build_info, CL_PROGRAM_BUILD_LOG, string) \ \ F(cl_kernel_info, CL_KERNEL_FUNCTION_NAME, string) \ F(cl_kernel_info, CL_KERNEL_NUM_ARGS, cl_uint) \ F(cl_kernel_info, CL_KERNEL_REFERENCE_COUNT, cl_uint) \ F(cl_kernel_info, CL_KERNEL_CONTEXT, cl::Context) \ F(cl_kernel_info, CL_KERNEL_PROGRAM, cl::Program) \ \ F(cl_kernel_work_group_info, CL_KERNEL_WORK_GROUP_SIZE, size_type) \ F(cl_kernel_work_group_info, CL_KERNEL_COMPILE_WORK_GROUP_SIZE, cl::detail::size_t_array) \ F(cl_kernel_work_group_info, CL_KERNEL_LOCAL_MEM_SIZE, cl_ulong) \ \ F(cl_command_queue_info, CL_QUEUE_CONTEXT, cl::Context) \ F(cl_command_queue_info, CL_QUEUE_DEVICE, cl::Device) \ F(cl_command_queue_info, CL_QUEUE_REFERENCE_COUNT, cl_uint) \ F(cl_command_queue_info, CL_QUEUE_PROPERTIES, cl_command_queue_properties) #define CL_HPP_PARAM_NAME_INFO_1_1_(F) \ F(cl_context_info, CL_CONTEXT_NUM_DEVICES, cl_uint)\ F(cl_device_info, CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_INT, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE, cl_uint) \ F(cl_device_info, CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF, cl_uint) \ F(cl_device_info, CL_DEVICE_OPENCL_C_VERSION, string) \ \ F(cl_mem_info, CL_MEM_ASSOCIATED_MEMOBJECT, cl::Memory) \ F(cl_mem_info, CL_MEM_OFFSET, size_type) \ \ F(cl_kernel_work_group_info, CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, size_type) \ F(cl_kernel_work_group_info, CL_KERNEL_PRIVATE_MEM_SIZE, cl_ulong) \ \ F(cl_event_info, CL_EVENT_CONTEXT, cl::Context) #define CL_HPP_PARAM_NAME_INFO_1_2_(F) \ F(cl_program_info, CL_PROGRAM_NUM_KERNELS, size_type) \ F(cl_program_info, CL_PROGRAM_KERNEL_NAMES, string) \ \ F(cl_program_build_info, CL_PROGRAM_BINARY_TYPE, cl_program_binary_type) \ \ F(cl_kernel_info, CL_KERNEL_ATTRIBUTES, string) \ \ F(cl_kernel_arg_info, CL_KERNEL_ARG_ADDRESS_QUALIFIER, cl_kernel_arg_address_qualifier) \ F(cl_kernel_arg_info, CL_KERNEL_ARG_ACCESS_QUALIFIER, cl_kernel_arg_access_qualifier) \ F(cl_kernel_arg_info, CL_KERNEL_ARG_TYPE_NAME, string) \ F(cl_kernel_arg_info, CL_KERNEL_ARG_NAME, string) \ F(cl_kernel_arg_info, CL_KERNEL_ARG_TYPE_QUALIFIER, cl_kernel_arg_type_qualifier) \ \ F(cl_kernel_work_group_info, CL_KERNEL_GLOBAL_WORK_SIZE, cl::detail::size_t_array) \ \ F(cl_device_info, CL_DEVICE_LINKER_AVAILABLE, cl_bool) \ F(cl_device_info, CL_DEVICE_IMAGE_MAX_BUFFER_SIZE, size_type) \ F(cl_device_info, CL_DEVICE_IMAGE_MAX_ARRAY_SIZE, size_type) \ F(cl_device_info, CL_DEVICE_PARENT_DEVICE, cl::Device) \ F(cl_device_info, CL_DEVICE_PARTITION_MAX_SUB_DEVICES, cl_uint) \ F(cl_device_info, CL_DEVICE_PARTITION_PROPERTIES, cl::vector) \ F(cl_device_info, CL_DEVICE_PARTITION_TYPE, cl::vector) \ F(cl_device_info, CL_DEVICE_REFERENCE_COUNT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_INTEROP_USER_SYNC, cl_bool) \ F(cl_device_info, CL_DEVICE_PARTITION_AFFINITY_DOMAIN, cl_device_affinity_domain) \ F(cl_device_info, CL_DEVICE_BUILT_IN_KERNELS, string) \ F(cl_device_info, CL_DEVICE_PRINTF_BUFFER_SIZE, size_type) \ \ F(cl_image_info, CL_IMAGE_ARRAY_SIZE, size_type) \ F(cl_image_info, CL_IMAGE_NUM_MIP_LEVELS, cl_uint) \ F(cl_image_info, CL_IMAGE_NUM_SAMPLES, cl_uint) #define CL_HPP_PARAM_NAME_INFO_2_0_(F) \ F(cl_device_info, CL_DEVICE_QUEUE_ON_HOST_PROPERTIES, cl_command_queue_properties) \ F(cl_device_info, CL_DEVICE_QUEUE_ON_DEVICE_PROPERTIES, cl_command_queue_properties) \ F(cl_device_info, CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE, cl_uint) \ F(cl_device_info, CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_ON_DEVICE_QUEUES, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_ON_DEVICE_EVENTS, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_PIPE_ARGS, cl_uint) \ F(cl_device_info, CL_DEVICE_PIPE_MAX_ACTIVE_RESERVATIONS, cl_uint) \ F(cl_device_info, CL_DEVICE_PIPE_MAX_PACKET_SIZE, cl_uint) \ F(cl_device_info, CL_DEVICE_SVM_CAPABILITIES, cl_device_svm_capabilities) \ F(cl_device_info, CL_DEVICE_PREFERRED_PLATFORM_ATOMIC_ALIGNMENT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_GLOBAL_ATOMIC_ALIGNMENT, cl_uint) \ F(cl_device_info, CL_DEVICE_PREFERRED_LOCAL_ATOMIC_ALIGNMENT, cl_uint) \ F(cl_device_info, CL_DEVICE_IMAGE_PITCH_ALIGNMENT, cl_uint) \ F(cl_device_info, CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT, cl_uint) \ F(cl_device_info, CL_DEVICE_MAX_READ_WRITE_IMAGE_ARGS, cl_uint ) \ F(cl_device_info, CL_DEVICE_MAX_GLOBAL_VARIABLE_SIZE, size_type ) \ F(cl_device_info, CL_DEVICE_GLOBAL_VARIABLE_PREFERRED_TOTAL_SIZE, size_type ) \ F(cl_profiling_info, CL_PROFILING_COMMAND_COMPLETE, cl_ulong) \ F(cl_kernel_exec_info, CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM, cl_bool) \ F(cl_kernel_exec_info, CL_KERNEL_EXEC_INFO_SVM_PTRS, void**) \ F(cl_command_queue_info, CL_QUEUE_SIZE, cl_uint) \ F(cl_mem_info, CL_MEM_USES_SVM_POINTER, cl_bool) \ F(cl_program_build_info, CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE, size_type) \ F(cl_pipe_info, CL_PIPE_PACKET_SIZE, cl_uint) \ F(cl_pipe_info, CL_PIPE_MAX_PACKETS, cl_uint) #define CL_HPP_PARAM_NAME_INFO_SUBGROUP_KHR_(F) \ F(cl_kernel_sub_group_info, CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE_KHR, size_type) \ F(cl_kernel_sub_group_info, CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE_KHR, size_type) #define CL_HPP_PARAM_NAME_INFO_IL_KHR_(F) \ F(cl_device_info, CL_DEVICE_IL_VERSION_KHR, string) \ F(cl_program_info, CL_PROGRAM_IL_KHR, cl::vector) #define CL_HPP_PARAM_NAME_INFO_2_1_(F) \ F(cl_platform_info, CL_PLATFORM_HOST_TIMER_RESOLUTION, cl_ulong) \ F(cl_program_info, CL_PROGRAM_IL, cl::vector) \ F(cl_device_info, CL_DEVICE_MAX_NUM_SUB_GROUPS, cl_uint) \ F(cl_device_info, CL_DEVICE_IL_VERSION, string) \ F(cl_device_info, CL_DEVICE_SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS, cl_bool) \ F(cl_command_queue_info, CL_QUEUE_DEVICE_DEFAULT, cl::DeviceCommandQueue) \ F(cl_kernel_sub_group_info, CL_KERNEL_MAX_SUB_GROUP_SIZE_FOR_NDRANGE, size_type) \ F(cl_kernel_sub_group_info, CL_KERNEL_SUB_GROUP_COUNT_FOR_NDRANGE, size_type) \ F(cl_kernel_sub_group_info, CL_KERNEL_LOCAL_SIZE_FOR_SUB_GROUP_COUNT, cl::detail::size_t_array) \ F(cl_kernel_sub_group_info, CL_KERNEL_MAX_NUM_SUB_GROUPS, size_type) \ F(cl_kernel_sub_group_info, CL_KERNEL_COMPILE_NUM_SUB_GROUPS, size_type) #define CL_HPP_PARAM_NAME_INFO_2_2_(F) \ F(cl_program_info, CL_PROGRAM_SCOPE_GLOBAL_CTORS_PRESENT, cl_bool) \ F(cl_program_info, CL_PROGRAM_SCOPE_GLOBAL_DTORS_PRESENT, cl_bool) #define CL_HPP_PARAM_NAME_DEVICE_FISSION_EXT_(F) \ F(cl_device_info, CL_DEVICE_PARENT_DEVICE_EXT, cl::Device) \ F(cl_device_info, CL_DEVICE_PARTITION_TYPES_EXT, cl::vector) \ F(cl_device_info, CL_DEVICE_AFFINITY_DOMAINS_EXT, cl::vector) \ F(cl_device_info, CL_DEVICE_REFERENCE_COUNT_EXT , cl_uint) \ F(cl_device_info, CL_DEVICE_PARTITION_STYLE_EXT, cl::vector) #define CL_HPP_PARAM_NAME_CL_KHR_EXTENDED_VERSIONING_CL3_SHARED_(F) \ F(cl_platform_info, CL_PLATFORM_NUMERIC_VERSION_KHR, cl_version_khr) \ F(cl_platform_info, CL_PLATFORM_EXTENSIONS_WITH_VERSION_KHR, cl::vector) \ \ F(cl_device_info, CL_DEVICE_NUMERIC_VERSION_KHR, cl_version_khr) \ F(cl_device_info, CL_DEVICE_EXTENSIONS_WITH_VERSION_KHR, cl::vector) \ F(cl_device_info, CL_DEVICE_ILS_WITH_VERSION_KHR, cl::vector) \ F(cl_device_info, CL_DEVICE_BUILT_IN_KERNELS_WITH_VERSION_KHR, cl::vector) #define CL_HPP_PARAM_NAME_CL_KHR_EXTENDED_VERSIONING_KHRONLY_(F) \ F(cl_device_info, CL_DEVICE_OPENCL_C_NUMERIC_VERSION_KHR, cl_version_khr) #define CL_HPP_PARAM_NAME_CL_KHR_SEMAPHORE_(F) \ F(cl_semaphore_info_khr, CL_SEMAPHORE_PROPERTIES_KHR, cl::vector) \ F(cl_platform_info, CL_PLATFORM_SEMAPHORE_TYPES_KHR, cl::vector) \ F(cl_device_info, CL_DEVICE_SEMAPHORE_TYPES_KHR, cl::vector) \ #define CL_HPP_PARAM_NAME_CL_KHR_EXTERNAL_MEMORY_(F) \ F(cl_device_info, CL_DEVICE_EXTERNAL_MEMORY_IMPORT_HANDLE_TYPES_KHR, cl::vector) \ F(cl_platform_info, CL_PLATFORM_EXTERNAL_MEMORY_IMPORT_HANDLE_TYPES_KHR, cl::vector) #define CL_HPP_PARAM_NAME_INFO_3_0_(F) \ F(cl_platform_info, CL_PLATFORM_NUMERIC_VERSION, cl_version) \ F(cl_platform_info, CL_PLATFORM_EXTENSIONS_WITH_VERSION, cl::vector) \ \ F(cl_device_info, CL_DEVICE_NUMERIC_VERSION, cl_version) \ F(cl_device_info, CL_DEVICE_EXTENSIONS_WITH_VERSION, cl::vector) \ F(cl_device_info, CL_DEVICE_ILS_WITH_VERSION, cl::vector) \ F(cl_device_info, CL_DEVICE_BUILT_IN_KERNELS_WITH_VERSION, cl::vector) \ F(cl_device_info, CL_DEVICE_ATOMIC_MEMORY_CAPABILITIES, cl_device_atomic_capabilities) \ F(cl_device_info, CL_DEVICE_ATOMIC_FENCE_CAPABILITIES, cl_device_atomic_capabilities) \ F(cl_device_info, CL_DEVICE_NON_UNIFORM_WORK_GROUP_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_OPENCL_C_ALL_VERSIONS, cl::vector) \ F(cl_device_info, CL_DEVICE_PREFERRED_WORK_GROUP_SIZE_MULTIPLE, size_type) \ F(cl_device_info, CL_DEVICE_WORK_GROUP_COLLECTIVE_FUNCTIONS_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_GENERIC_ADDRESS_SPACE_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_OPENCL_C_FEATURES, cl::vector) \ F(cl_device_info, CL_DEVICE_DEVICE_ENQUEUE_CAPABILITIES, cl_device_device_enqueue_capabilities) \ F(cl_device_info, CL_DEVICE_PIPE_SUPPORT, cl_bool) \ F(cl_device_info, CL_DEVICE_LATEST_CONFORMANCE_VERSION_PASSED, string) \ \ F(cl_command_queue_info, CL_QUEUE_PROPERTIES_ARRAY, cl::vector) \ F(cl_mem_info, CL_MEM_PROPERTIES, cl::vector) \ F(cl_pipe_info, CL_PIPE_PROPERTIES, cl::vector) \ F(cl_sampler_info, CL_SAMPLER_PROPERTIES, cl::vector) \ template struct param_traits {}; #define CL_HPP_DECLARE_PARAM_TRAITS_(token, param_name, T) \ struct token; \ template<> \ struct param_traits \ { \ enum { value = param_name }; \ typedef T param_type; \ }; CL_HPP_PARAM_NAME_INFO_1_0_(CL_HPP_DECLARE_PARAM_TRAITS_) #if CL_HPP_TARGET_OPENCL_VERSION >= 110 CL_HPP_PARAM_NAME_INFO_1_1_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 #if CL_HPP_TARGET_OPENCL_VERSION >= 120 CL_HPP_PARAM_NAME_INFO_1_2_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_TARGET_OPENCL_VERSION >= 200 CL_HPP_PARAM_NAME_INFO_2_0_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_TARGET_OPENCL_VERSION >= 210 CL_HPP_PARAM_NAME_INFO_2_1_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 210 #if CL_HPP_TARGET_OPENCL_VERSION >= 220 CL_HPP_PARAM_NAME_INFO_2_2_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 220 #if CL_HPP_TARGET_OPENCL_VERSION >= 300 CL_HPP_PARAM_NAME_INFO_3_0_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION >= 300 #if defined(cl_khr_subgroups) && CL_HPP_TARGET_OPENCL_VERSION < 210 CL_HPP_PARAM_NAME_INFO_SUBGROUP_KHR_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // #if defined(cl_khr_subgroups) && CL_HPP_TARGET_OPENCL_VERSION < 210 #if defined(cl_khr_il_program) && CL_HPP_TARGET_OPENCL_VERSION < 210 CL_HPP_PARAM_NAME_INFO_IL_KHR_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // #if defined(cl_khr_il_program) && CL_HPP_TARGET_OPENCL_VERSION < 210 // Flags deprecated in OpenCL 2.0 #define CL_HPP_PARAM_NAME_INFO_1_0_DEPRECATED_IN_2_0_(F) \ F(cl_device_info, CL_DEVICE_QUEUE_PROPERTIES, cl_command_queue_properties) #define CL_HPP_PARAM_NAME_INFO_1_1_DEPRECATED_IN_2_0_(F) \ F(cl_device_info, CL_DEVICE_HOST_UNIFIED_MEMORY, cl_bool) #define CL_HPP_PARAM_NAME_INFO_1_2_DEPRECATED_IN_2_0_(F) \ F(cl_image_info, CL_IMAGE_BUFFER, cl::Buffer) // Include deprecated query flags based on versions // Only include deprecated 1.0 flags if 2.0 not active as there is an enum clash #if CL_HPP_TARGET_OPENCL_VERSION > 100 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 && CL_HPP_TARGET_OPENCL_VERSION < 200 CL_HPP_PARAM_NAME_INFO_1_0_DEPRECATED_IN_2_0_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 110 #if CL_HPP_TARGET_OPENCL_VERSION > 110 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 CL_HPP_PARAM_NAME_INFO_1_1_DEPRECATED_IN_2_0_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 120 #if CL_HPP_TARGET_OPENCL_VERSION > 120 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 CL_HPP_PARAM_NAME_INFO_1_2_DEPRECATED_IN_2_0_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 #if defined(cl_ext_device_fission) CL_HPP_PARAM_NAME_DEVICE_FISSION_EXT_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // cl_ext_device_fission #if defined(cl_khr_extended_versioning) #if CL_HPP_TARGET_OPENCL_VERSION < 300 CL_HPP_PARAM_NAME_CL_KHR_EXTENDED_VERSIONING_CL3_SHARED_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // CL_HPP_TARGET_OPENCL_VERSION < 300 CL_HPP_PARAM_NAME_CL_KHR_EXTENDED_VERSIONING_KHRONLY_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // cl_khr_extended_versioning #if defined(cl_khr_semaphore) CL_HPP_PARAM_NAME_CL_KHR_SEMAPHORE_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // cl_khr_semaphore #ifdef cl_khr_external_memory CL_HPP_PARAM_NAME_CL_KHR_EXTERNAL_MEMORY_(CL_HPP_DECLARE_PARAM_TRAITS_) #endif // cl_khr_external_memory #if defined(cl_khr_device_uuid) using uuid_array = array; using luid_array = array; CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_UUID_KHR, uuid_array) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DRIVER_UUID_KHR, uuid_array) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_LUID_VALID_KHR, cl_bool) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_LUID_KHR, luid_array) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_NODE_MASK_KHR, cl_uint) #endif #if defined(cl_khr_pci_bus_info) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_PCI_BUS_INFO_KHR, cl_device_pci_bus_info_khr) #endif // Note: some headers do not define cl_khr_image2d_from_buffer #if CL_HPP_TARGET_OPENCL_VERSION < 200 #if defined(CL_DEVICE_IMAGE_PITCH_ALIGNMENT_KHR) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_IMAGE_PITCH_ALIGNMENT_KHR, cl_uint) #endif #if defined(CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT_KHR) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT_KHR, cl_uint) #endif #endif // CL_HPP_TARGET_OPENCL_VERSION < 200 #if defined(cl_khr_integer_dot_product) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_INTEGER_DOT_PRODUCT_CAPABILITIES_KHR, cl_device_integer_dot_product_capabilities_khr) #if defined(CL_DEVICE_INTEGER_DOT_PRODUCT_ACCELERATION_PROPERTIES_8BIT_KHR) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_INTEGER_DOT_PRODUCT_ACCELERATION_PROPERTIES_8BIT_KHR, cl_device_integer_dot_product_acceleration_properties_khr) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_INTEGER_DOT_PRODUCT_ACCELERATION_PROPERTIES_4x8BIT_PACKED_KHR, cl_device_integer_dot_product_acceleration_properties_khr) #endif // defined(CL_DEVICE_INTEGER_DOT_PRODUCT_ACCELERATION_PROPERTIES_8BIT_KHR) #endif // defined(cl_khr_integer_dot_product) #ifdef CL_PLATFORM_ICD_SUFFIX_KHR CL_HPP_DECLARE_PARAM_TRAITS_(cl_platform_info, CL_PLATFORM_ICD_SUFFIX_KHR, string) #endif #ifdef CL_DEVICE_PROFILING_TIMER_OFFSET_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_PROFILING_TIMER_OFFSET_AMD, cl_ulong) #endif #ifdef CL_DEVICE_GLOBAL_FREE_MEMORY_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_GLOBAL_FREE_MEMORY_AMD, vector) #endif #ifdef CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_SIMD_PER_COMPUTE_UNIT_AMD, cl_uint) #endif #ifdef CL_DEVICE_SIMD_WIDTH_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_SIMD_WIDTH_AMD, cl_uint) #endif #ifdef CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_SIMD_INSTRUCTION_WIDTH_AMD, cl_uint) #endif #ifdef CL_DEVICE_WAVEFRONT_WIDTH_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_WAVEFRONT_WIDTH_AMD, cl_uint) #endif #ifdef CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_GLOBAL_MEM_CHANNELS_AMD, cl_uint) #endif #ifdef CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_GLOBAL_MEM_CHANNEL_BANKS_AMD, cl_uint) #endif #ifdef CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_GLOBAL_MEM_CHANNEL_BANK_WIDTH_AMD, cl_uint) #endif #ifdef CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_LOCAL_MEM_SIZE_PER_COMPUTE_UNIT_AMD, cl_uint) #endif #ifdef CL_DEVICE_LOCAL_MEM_BANKS_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_LOCAL_MEM_BANKS_AMD, cl_uint) #endif #ifdef CL_DEVICE_BOARD_NAME_AMD CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_BOARD_NAME_AMD, string) #endif #ifdef CL_DEVICE_COMPUTE_UNITS_BITFIELD_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_COMPUTE_UNITS_BITFIELD_ARM, cl_ulong) #endif #ifdef CL_DEVICE_JOB_SLOTS_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_JOB_SLOTS_ARM, cl_uint) #endif #ifdef CL_DEVICE_SCHEDULING_CONTROLS_CAPABILITIES_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_SCHEDULING_CONTROLS_CAPABILITIES_ARM, cl_bitfield) #endif #ifdef CL_DEVICE_SUPPORTED_REGISTER_ALLOCATIONS_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_SUPPORTED_REGISTER_ALLOCATIONS_ARM, vector) #endif #ifdef CL_DEVICE_MAX_WARP_COUNT_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_MAX_WARP_COUNT_ARM, cl_uint) #endif #ifdef CL_KERNEL_MAX_WARP_COUNT_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_kernel_info, CL_KERNEL_MAX_WARP_COUNT_ARM, cl_uint) #endif #ifdef CL_KERNEL_EXEC_INFO_WORKGROUP_BATCH_SIZE_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_kernel_exec_info, CL_KERNEL_EXEC_INFO_WORKGROUP_BATCH_SIZE_ARM, cl_uint) #endif #ifdef CL_KERNEL_EXEC_INFO_WORKGROUP_BATCH_SIZE_MODIFIER_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_kernel_exec_info, CL_KERNEL_EXEC_INFO_WORKGROUP_BATCH_SIZE_MODIFIER_ARM, cl_int) #endif #ifdef CL_KERNEL_EXEC_INFO_WARP_COUNT_LIMIT_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_kernel_exec_info, CL_KERNEL_EXEC_INFO_WARP_COUNT_LIMIT_ARM, cl_uint) #endif #ifdef CL_KERNEL_EXEC_INFO_COMPUTE_UNIT_MAX_QUEUED_BATCHES_ARM CL_HPP_DECLARE_PARAM_TRAITS_(cl_kernel_exec_info, CL_KERNEL_EXEC_INFO_COMPUTE_UNIT_MAX_QUEUED_BATCHES_ARM, cl_uint) #endif #ifdef CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV, cl_uint) #endif #ifdef CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV, cl_uint) #endif #ifdef CL_DEVICE_REGISTERS_PER_BLOCK_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_REGISTERS_PER_BLOCK_NV, cl_uint) #endif #ifdef CL_DEVICE_WARP_SIZE_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_WARP_SIZE_NV, cl_uint) #endif #ifdef CL_DEVICE_GPU_OVERLAP_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_GPU_OVERLAP_NV, cl_bool) #endif #ifdef CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_KERNEL_EXEC_TIMEOUT_NV, cl_bool) #endif #ifdef CL_DEVICE_INTEGRATED_MEMORY_NV CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_INTEGRATED_MEMORY_NV, cl_bool) #endif #if defined(cl_khr_command_buffer) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_COMMAND_BUFFER_CAPABILITIES_KHR, cl_device_command_buffer_capabilities_khr) CL_HPP_DECLARE_PARAM_TRAITS_(cl_device_info, CL_DEVICE_COMMAND_BUFFER_REQUIRED_QUEUE_PROPERTIES_KHR, cl_command_buffer_properties_khr) CL_HPP_DECLARE_PARAM_TRAITS_(cl_command_buffer_info_khr, CL_COMMAND_BUFFER_QUEUES_KHR, cl::vector) CL_HPP_DECLARE_PARAM_TRAITS_(cl_command_buffer_info_khr, CL_COMMAND_BUFFER_NUM_QUEUES_KHR, cl_uint) CL_HPP_DECLARE_PARAM_TRAITS_(cl_command_buffer_info_khr, CL_COMMAND_BUFFER_REFERENCE_COUNT_KHR, cl_uint) CL_HPP_DECLARE_PARAM_TRAITS_(cl_command_buffer_info_khr, CL_COMMAND_BUFFER_STATE_KHR, cl_command_buffer_state_khr) CL_HPP_DECLARE_PARAM_TRAITS_(cl_command_buffer_info_khr, CL_COMMAND_BUFFER_PROPERTIES_ARRAY_KHR, cl::vector) #endif /* cl_khr_command_buffer */ #if defined(cl_khr_command_buffer_mutable_dispatch) CL_HPP_DECLARE_PARAM_TRAITS_(cl_mutable_command_info_khr, CL_MUTABLE_COMMAND_COMMAND_QUEUE_KHR, CommandQueue) CL_HPP_DECLARE_PARAM_TRAITS_(cl_mutable_command_info_khr, CL_MUTABLE_COMMAND_COMMAND_BUFFER_KHR, CommandBufferKhr) CL_HPP_DECLARE_PARAM_TRAITS_(cl_mutable_command_info_khr, CL_MUTABLE_COMMAND_COMMAND_TYPE_KHR, cl_command_type) CL_HPP_DECLARE_PARAM_TRAITS_(cl_mutable_command_info_khr, CL_MUTABLE_DISPATCH_PROPERTIES_ARRAY_KHR, cl::vector) CL_HPP_DECLARE_PARAM_TRAITS_(cl_mutable_command_info_khr, CL_MUTABLE_DISPATCH_KERNEL_KHR, cl_kernel) CL_HPP_DECLARE_PARAM_TRAITS_(cl_mutable_command_info_khr, CL_MUTABLE_DISPATCH_DIMENSIONS_KHR, cl_uint) CL_HPP_DECLARE_PARAM_TRAITS_(cl_mutable_command_info_khr, CL_MUTABLE_DISPATCH_GLOBAL_WORK_OFFSET_KHR, cl::vector) CL_HPP_DECLARE_PARAM_TRAITS_(cl_mutable_command_info_khr, CL_MUTABLE_DISPATCH_GLOBAL_WORK_SIZE_KHR, cl::vector) CL_HPP_DECLARE_PARAM_TRAITS_(cl_mutable_command_info_khr, CL_MUTABLE_DISPATCH_LOCAL_WORK_SIZE_KHR, cl::vector) #endif /* cl_khr_command_buffer_mutable_dispatch */ // Convenience functions template inline cl_int getInfo(Func f, cl_uint name, T* param) { return getInfoHelper(f, name, param, 0); } template struct GetInfoFunctor0 { Func f_; const Arg0& arg0_; cl_int operator ()( cl_uint param, size_type size, void* value, size_type* size_ret) { return f_(arg0_, param, size, value, size_ret); } }; template struct GetInfoFunctor1 { Func f_; const Arg0& arg0_; const Arg1& arg1_; cl_int operator ()( cl_uint param, size_type size, void* value, size_type* size_ret) { return f_(arg0_, arg1_, param, size, value, size_ret); } }; template inline cl_int getInfo(Func f, const Arg0& arg0, cl_uint name, T* param) { GetInfoFunctor0 f0 = { f, arg0 }; return getInfoHelper(f0, name, param, 0); } template inline cl_int getInfo(Func f, const Arg0& arg0, const Arg1& arg1, cl_uint name, T* param) { GetInfoFunctor1 f0 = { f, arg0, arg1 }; return getInfoHelper(f0, name, param, 0); } template struct ReferenceHandler { }; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * OpenCL 1.2 devices do have retain/release. */ template <> struct ReferenceHandler { /** * Retain the device. * \param device A valid device created using createSubDevices * \return * CL_SUCCESS if the function executed successfully. * CL_INVALID_DEVICE if device was not a valid subdevice * CL_OUT_OF_RESOURCES * CL_OUT_OF_HOST_MEMORY */ static cl_int retain(cl_device_id device) { return ::clRetainDevice(device); } /** * Retain the device. * \param device A valid device created using createSubDevices * \return * CL_SUCCESS if the function executed successfully. * CL_INVALID_DEVICE if device was not a valid subdevice * CL_OUT_OF_RESOURCES * CL_OUT_OF_HOST_MEMORY */ static cl_int release(cl_device_id device) { return ::clReleaseDevice(device); } }; #else // CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * OpenCL 1.1 devices do not have retain/release. */ template <> struct ReferenceHandler { // cl_device_id does not have retain(). static cl_int retain(cl_device_id) { return CL_SUCCESS; } // cl_device_id does not have release(). static cl_int release(cl_device_id) { return CL_SUCCESS; } }; #endif // ! (CL_HPP_TARGET_OPENCL_VERSION >= 120) template <> struct ReferenceHandler { // cl_platform_id does not have retain(). static cl_int retain(cl_platform_id) { return CL_SUCCESS; } // cl_platform_id does not have release(). static cl_int release(cl_platform_id) { return CL_SUCCESS; } }; template <> struct ReferenceHandler { static cl_int retain(cl_context context) { return ::clRetainContext(context); } static cl_int release(cl_context context) { return ::clReleaseContext(context); } }; template <> struct ReferenceHandler { static cl_int retain(cl_command_queue queue) { return ::clRetainCommandQueue(queue); } static cl_int release(cl_command_queue queue) { return ::clReleaseCommandQueue(queue); } }; template <> struct ReferenceHandler { static cl_int retain(cl_mem memory) { return ::clRetainMemObject(memory); } static cl_int release(cl_mem memory) { return ::clReleaseMemObject(memory); } }; template <> struct ReferenceHandler { static cl_int retain(cl_sampler sampler) { return ::clRetainSampler(sampler); } static cl_int release(cl_sampler sampler) { return ::clReleaseSampler(sampler); } }; template <> struct ReferenceHandler { static cl_int retain(cl_program program) { return ::clRetainProgram(program); } static cl_int release(cl_program program) { return ::clReleaseProgram(program); } }; template <> struct ReferenceHandler { static cl_int retain(cl_kernel kernel) { return ::clRetainKernel(kernel); } static cl_int release(cl_kernel kernel) { return ::clReleaseKernel(kernel); } }; template <> struct ReferenceHandler { static cl_int retain(cl_event event) { return ::clRetainEvent(event); } static cl_int release(cl_event event) { return ::clReleaseEvent(event); } }; #ifdef cl_khr_semaphore template <> struct ReferenceHandler { static cl_int retain(cl_semaphore_khr semaphore) { if (pfn_clRetainSemaphoreKHR != nullptr) { return pfn_clRetainSemaphoreKHR(semaphore); } return CL_INVALID_OPERATION; } static cl_int release(cl_semaphore_khr semaphore) { if (pfn_clReleaseSemaphoreKHR != nullptr) { return pfn_clReleaseSemaphoreKHR(semaphore); } return CL_INVALID_OPERATION; } }; #endif // cl_khr_semaphore #if defined(cl_khr_command_buffer) template <> struct ReferenceHandler { static cl_int retain(cl_command_buffer_khr cmdBufferKhr) { if (pfn_clRetainCommandBufferKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __RETAIN_COMMAND_BUFFER_KHR_ERR); } return pfn_clRetainCommandBufferKHR(cmdBufferKhr); } static cl_int release(cl_command_buffer_khr cmdBufferKhr) { if (pfn_clReleaseCommandBufferKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __RELEASE_COMMAND_BUFFER_KHR_ERR); } return pfn_clReleaseCommandBufferKHR(cmdBufferKhr); } }; template <> struct ReferenceHandler { // cl_mutable_command_khr does not have retain(). static cl_int retain(cl_mutable_command_khr) { return CL_SUCCESS; } // cl_mutable_command_khr does not have release(). static cl_int release(cl_mutable_command_khr) { return CL_SUCCESS; } }; #endif // cl_khr_command_buffer #if CL_HPP_TARGET_OPENCL_VERSION >= 120 && CL_HPP_MINIMUM_OPENCL_VERSION < 120 // Extracts version number with major in the upper 16 bits, minor in the lower 16 static cl_uint getVersion(const vector &versionInfo) { int highVersion = 0; int lowVersion = 0; int index = 7; while(versionInfo[index] != '.' ) { highVersion *= 10; highVersion += versionInfo[index]-'0'; ++index; } ++index; while(versionInfo[index] != ' ' && versionInfo[index] != '\0') { lowVersion *= 10; lowVersion += versionInfo[index]-'0'; ++index; } return (highVersion << 16) | lowVersion; } static cl_uint getPlatformVersion(cl_platform_id platform) { size_type size = 0; clGetPlatformInfo(platform, CL_PLATFORM_VERSION, 0, nullptr, &size); vector versionInfo(size); clGetPlatformInfo(platform, CL_PLATFORM_VERSION, size, versionInfo.data(), &size); return getVersion(versionInfo); } static cl_uint getDevicePlatformVersion(cl_device_id device) { cl_platform_id platform; clGetDeviceInfo(device, CL_DEVICE_PLATFORM, sizeof(platform), &platform, nullptr); return getPlatformVersion(platform); } static cl_uint getContextPlatformVersion(cl_context context) { // The platform cannot be queried directly, so we first have to grab a // device and obtain its context size_type size = 0; clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, nullptr, &size); if (size == 0) return 0; vector devices(size/sizeof(cl_device_id)); clGetContextInfo(context, CL_CONTEXT_DEVICES, size, devices.data(), nullptr); return getDevicePlatformVersion(devices[0]); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 && CL_HPP_MINIMUM_OPENCL_VERSION < 120 template class Wrapper { public: typedef T cl_type; protected: cl_type object_; public: Wrapper() : object_(nullptr) { } Wrapper(const cl_type &obj, bool retainObject) : object_(obj) { if (retainObject) { detail::errHandler(retain(), __RETAIN_ERR); } } ~Wrapper() { if (object_ != nullptr) { release(); } } Wrapper(const Wrapper& rhs) { object_ = rhs.object_; detail::errHandler(retain(), __RETAIN_ERR); } Wrapper(Wrapper&& rhs) CL_HPP_NOEXCEPT_ { object_ = rhs.object_; rhs.object_ = nullptr; } Wrapper& operator = (const Wrapper& rhs) { if (this != &rhs) { detail::errHandler(release(), __RELEASE_ERR); object_ = rhs.object_; detail::errHandler(retain(), __RETAIN_ERR); } return *this; } Wrapper& operator = (Wrapper&& rhs) { if (this != &rhs) { detail::errHandler(release(), __RELEASE_ERR); object_ = rhs.object_; rhs.object_ = nullptr; } return *this; } Wrapper& operator = (const cl_type &rhs) { detail::errHandler(release(), __RELEASE_ERR); object_ = rhs; return *this; } const cl_type& operator ()() const { return object_; } cl_type& operator ()() { return object_; } cl_type get() const { return object_; } protected: template friend inline cl_int getInfoHelper(Func, cl_uint, U*, int, typename U::cl_type); cl_int retain() const { if (object_ != nullptr) { return ReferenceHandler::retain(object_); } else { return CL_SUCCESS; } } cl_int release() const { if (object_ != nullptr) { return ReferenceHandler::release(object_); } else { return CL_SUCCESS; } } }; template <> class Wrapper { public: typedef cl_device_id cl_type; protected: cl_type object_; bool referenceCountable_; static bool isReferenceCountable(cl_device_id device) { bool retVal = false; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_MINIMUM_OPENCL_VERSION < 120 if (device != nullptr) { int version = getDevicePlatformVersion(device); if(version > ((1 << 16) + 1)) { retVal = true; } } #else // CL_HPP_MINIMUM_OPENCL_VERSION < 120 retVal = true; #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 120 #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 (void)device; return retVal; } public: Wrapper() : object_(nullptr), referenceCountable_(false) { } Wrapper(const cl_type &obj, bool retainObject) : object_(obj), referenceCountable_(false) { referenceCountable_ = isReferenceCountable(obj); if (retainObject) { detail::errHandler(retain(), __RETAIN_ERR); } } ~Wrapper() { release(); } Wrapper(const Wrapper& rhs) { object_ = rhs.object_; referenceCountable_ = isReferenceCountable(object_); detail::errHandler(retain(), __RETAIN_ERR); } Wrapper(Wrapper&& rhs) CL_HPP_NOEXCEPT_ { object_ = rhs.object_; referenceCountable_ = rhs.referenceCountable_; rhs.object_ = nullptr; rhs.referenceCountable_ = false; } Wrapper& operator = (const Wrapper& rhs) { if (this != &rhs) { detail::errHandler(release(), __RELEASE_ERR); object_ = rhs.object_; referenceCountable_ = rhs.referenceCountable_; detail::errHandler(retain(), __RETAIN_ERR); } return *this; } Wrapper& operator = (Wrapper&& rhs) { if (this != &rhs) { detail::errHandler(release(), __RELEASE_ERR); object_ = rhs.object_; referenceCountable_ = rhs.referenceCountable_; rhs.object_ = nullptr; rhs.referenceCountable_ = false; } return *this; } Wrapper& operator = (const cl_type &rhs) { detail::errHandler(release(), __RELEASE_ERR); object_ = rhs; referenceCountable_ = isReferenceCountable(object_); return *this; } const cl_type& operator ()() const { return object_; } cl_type& operator ()() { return object_; } cl_type get() const { return object_; } protected: template friend inline cl_int getInfoHelper(Func, cl_uint, U*, int, typename U::cl_type); template friend inline cl_int getInfoHelper(Func, cl_uint, vector*, int, typename U::cl_type); cl_int retain() const { if( object_ != nullptr && referenceCountable_ ) { return ReferenceHandler::retain(object_); } else { return CL_SUCCESS; } } cl_int release() const { if (object_ != nullptr && referenceCountable_) { return ReferenceHandler::release(object_); } else { return CL_SUCCESS; } } }; template inline bool operator==(const Wrapper &lhs, const Wrapper &rhs) { return lhs() == rhs(); } template inline bool operator!=(const Wrapper &lhs, const Wrapper &rhs) { return !operator==(lhs, rhs); } } // namespace detail //! \endcond /*! \stuct ImageFormat * \brief Adds constructors and member functions for cl_image_format. * * \see cl_image_format */ struct ImageFormat : public cl_image_format { //! \brief Default constructor - performs no initialization. ImageFormat(){} //! \brief Initializing constructor. ImageFormat(cl_channel_order order, cl_channel_type type) { image_channel_order = order; image_channel_data_type = type; } //! \brief Copy constructor. ImageFormat(const ImageFormat &other) { *this = other; } //! \brief Assignment operator. ImageFormat& operator = (const ImageFormat& rhs) { if (this != &rhs) { this->image_channel_data_type = rhs.image_channel_data_type; this->image_channel_order = rhs.image_channel_order; } return *this; } }; /*! \brief Class interface for cl_device_id. * * \note Copies of these objects are inexpensive, since they don't 'own' * any underlying resources or data structures. * * \see cl_device_id */ class Device : public detail::Wrapper { private: static std::once_flag default_initialized_; static Device default_; static cl_int default_error_; /*! \brief Create the default context. * * This sets @c default_ and @c default_error_. It does not throw * @c cl::Error. */ static void makeDefault(); /*! \brief Create the default platform from a provided platform. * * This sets @c default_. It does not throw * @c cl::Error. */ static void makeDefaultProvided(const Device &p) { default_ = p; } public: #ifdef CL_HPP_UNIT_TEST_ENABLE /*! \brief Reset the default. * * This sets @c default_ to an empty value to support cleanup in * the unit test framework. * This function is not thread safe. */ static void unitTestClearDefault() { default_ = Device(); } #endif // #ifdef CL_HPP_UNIT_TEST_ENABLE //! \brief Default constructor - initializes to nullptr. Device() : detail::Wrapper() { } /*! \brief Constructor from cl_device_id. * * This simply copies the device ID value, which is an inexpensive operation. */ explicit Device(const cl_device_id &device, bool retainObject = false) : detail::Wrapper(device, retainObject) { } /*! \brief Returns the first device on the default context. * * \see Context::getDefault() */ static Device getDefault( cl_int *errResult = nullptr) { std::call_once(default_initialized_, makeDefault); detail::errHandler(default_error_); if (errResult != nullptr) { *errResult = default_error_; } return default_; } /** * Modify the default device to be used by * subsequent operations. * Will only set the default if no default was previously created. * @return updated default device. * Should be compared to the passed value to ensure that it was updated. */ static Device setDefault(const Device &default_device) { std::call_once(default_initialized_, makeDefaultProvided, std::cref(default_device)); detail::errHandler(default_error_); return default_; } /*! \brief Assignment operator from cl_device_id. * * This simply copies the device ID value, which is an inexpensive operation. */ Device& operator = (const cl_device_id& rhs) { detail::Wrapper::operator=(rhs); return *this; } //! \brief Wrapper for clGetDeviceInfo(). template cl_int getInfo(cl_device_info name, T* param) const { return detail::errHandler( detail::getInfo(::clGetDeviceInfo, object_, name, param), __GET_DEVICE_INFO_ERR); } //! \brief Wrapper for clGetDeviceInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_device_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } #if CL_HPP_TARGET_OPENCL_VERSION >= 210 /** * Return the current value of the host clock as seen by the device. * The resolution of the device timer may be queried with the * CL_DEVICE_PROFILING_TIMER_RESOLUTION query. * @return The host timer value. */ cl_ulong getHostTimer(cl_int *error = nullptr) { cl_ulong retVal = 0; cl_int err = clGetHostTimer(this->get(), &retVal); detail::errHandler( err, __GET_HOST_TIMER_ERR); if (error) { *error = err; } return retVal; } /** * Return a synchronized pair of host and device timestamps as seen by device. * Use to correlate the clocks and get the host timer only using getHostTimer * as a lower cost mechanism in between calls. * The resolution of the host timer may be queried with the * CL_PLATFORM_HOST_TIMER_RESOLUTION query. * The resolution of the device timer may be queried with the * CL_DEVICE_PROFILING_TIMER_RESOLUTION query. * @return A pair of (device timer, host timer) timer values. */ std::pair getDeviceAndHostTimer(cl_int *error = nullptr) { std::pair retVal; cl_int err = clGetDeviceAndHostTimer(this->get(), &(retVal.first), &(retVal.second)); detail::errHandler( err, __GET_DEVICE_AND_HOST_TIMER_ERR); if (error) { *error = err; } return retVal; } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 #if CL_HPP_TARGET_OPENCL_VERSION >= 120 //! \brief Wrapper for clCreateSubDevices(). cl_int createSubDevices( const cl_device_partition_property * properties, vector* devices) { cl_uint n = 0; cl_int err = clCreateSubDevices(object_, properties, 0, nullptr, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_SUB_DEVICES_ERR); } vector ids(n); err = clCreateSubDevices(object_, properties, n, ids.data(), nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_SUB_DEVICES_ERR); } // Cannot trivially assign because we need to capture intermediates // with safe construction if (devices) { devices->resize(ids.size()); // Assign to param, constructing with retain behaviour // to correctly capture each underlying CL object for (size_type i = 0; i < ids.size(); i++) { // We do not need to retain because this device is being created // by the runtime (*devices)[i] = Device(ids[i], false); } } return CL_SUCCESS; } #endif #if defined(cl_ext_device_fission) //! \brief Wrapper for clCreateSubDevices(). cl_int createSubDevices( const cl_device_partition_property_ext * properties, vector* devices) { typedef CL_API_ENTRY cl_int ( CL_API_CALL * PFN_clCreateSubDevicesEXT)( cl_device_id /*in_device*/, const cl_device_partition_property_ext * /* properties */, cl_uint /*num_entries*/, cl_device_id * /*out_devices*/, cl_uint * /*num_devices*/ ) CL_API_SUFFIX__VERSION_1_1; static PFN_clCreateSubDevicesEXT pfn_clCreateSubDevicesEXT = nullptr; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 cl::Device device(object_); cl_platform_id platform = device.getInfo(); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCreateSubDevicesEXT); #endif #if CL_HPP_MINIMUM_OPENCL_VERSION < 120 CL_HPP_INIT_CL_EXT_FCN_PTR_(clCreateSubDevicesEXT); #endif cl_uint n = 0; cl_int err = pfn_clCreateSubDevicesEXT(object_, properties, 0, nullptr, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_SUB_DEVICES_ERR); } vector ids(n); err = pfn_clCreateSubDevicesEXT(object_, properties, n, ids.data(), nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_SUB_DEVICES_ERR); } // Cannot trivially assign because we need to capture intermediates // with safe construction if (devices) { devices->resize(ids.size()); // Assign to param, constructing with retain behaviour // to correctly capture each underlying CL object for (size_type i = 0; i < ids.size(); i++) { // We do not need to retain because this device is being created // by the runtime (*devices)[i] = Device(ids[i], false); } } return CL_SUCCESS; } #endif // defined(cl_ext_device_fission) }; using BuildLogType = vector::param_type>>; #if defined(CL_HPP_ENABLE_EXCEPTIONS) /** * Exception class for build errors to carry build info */ class BuildError : public Error { private: BuildLogType buildLogs; public: BuildError(cl_int err, const char * errStr, const BuildLogType &vec) : Error(err, errStr), buildLogs(vec) { } BuildLogType getBuildLog() const { return buildLogs; } }; namespace detail { static inline cl_int buildErrHandler( cl_int err, const char * errStr, const BuildLogType &buildLogs) { if (err != CL_SUCCESS) { throw BuildError(err, errStr, buildLogs); } return err; } } // namespace detail #else namespace detail { static inline cl_int buildErrHandler( cl_int err, const char * errStr, const BuildLogType &buildLogs) { (void)buildLogs; // suppress unused variable warning (void)errStr; return err; } } // namespace detail #endif // #if defined(CL_HPP_ENABLE_EXCEPTIONS) CL_HPP_DEFINE_STATIC_MEMBER_ std::once_flag Device::default_initialized_; CL_HPP_DEFINE_STATIC_MEMBER_ Device Device::default_; CL_HPP_DEFINE_STATIC_MEMBER_ cl_int Device::default_error_ = CL_SUCCESS; /*! \brief Class interface for cl_platform_id. * * \note Copies of these objects are inexpensive, since they don't 'own' * any underlying resources or data structures. * * \see cl_platform_id */ class Platform : public detail::Wrapper { private: static std::once_flag default_initialized_; static Platform default_; static cl_int default_error_; /*! \brief Create the default context. * * This sets @c default_ and @c default_error_. It does not throw * @c cl::Error. */ static void makeDefault() { /* Throwing an exception from a call_once invocation does not do * what we wish, so we catch it and save the error. */ #if defined(CL_HPP_ENABLE_EXCEPTIONS) try #endif { // If default wasn't passed ,generate one // Otherwise set it cl_uint n = 0; cl_int err = ::clGetPlatformIDs(0, nullptr, &n); if (err != CL_SUCCESS) { default_error_ = err; return; } if (n == 0) { default_error_ = CL_INVALID_PLATFORM; return; } vector ids(n); err = ::clGetPlatformIDs(n, ids.data(), nullptr); if (err != CL_SUCCESS) { default_error_ = err; return; } default_ = Platform(ids[0]); } #if defined(CL_HPP_ENABLE_EXCEPTIONS) catch (cl::Error &e) { default_error_ = e.err(); } #endif } /*! \brief Create the default platform from a provided platform. * * This sets @c default_. It does not throw * @c cl::Error. */ static void makeDefaultProvided(const Platform &p) { default_ = p; } public: #ifdef CL_HPP_UNIT_TEST_ENABLE /*! \brief Reset the default. * * This sets @c default_ to an empty value to support cleanup in * the unit test framework. * This function is not thread safe. */ static void unitTestClearDefault() { default_ = Platform(); } #endif // #ifdef CL_HPP_UNIT_TEST_ENABLE //! \brief Default constructor - initializes to nullptr. Platform() : detail::Wrapper() { } /*! \brief Constructor from cl_platform_id. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * This simply copies the platform ID value, which is an inexpensive operation. */ explicit Platform(const cl_platform_id &platform, bool retainObject = false) : detail::Wrapper(platform, retainObject) { } /*! \brief Assignment operator from cl_platform_id. * * This simply copies the platform ID value, which is an inexpensive operation. */ Platform& operator = (const cl_platform_id& rhs) { detail::Wrapper::operator=(rhs); return *this; } static Platform getDefault( cl_int *errResult = nullptr) { std::call_once(default_initialized_, makeDefault); detail::errHandler(default_error_); if (errResult != nullptr) { *errResult = default_error_; } return default_; } /** * Modify the default platform to be used by * subsequent operations. * Will only set the default if no default was previously created. * @return updated default platform. * Should be compared to the passed value to ensure that it was updated. */ static Platform setDefault(const Platform &default_platform) { std::call_once(default_initialized_, makeDefaultProvided, std::cref(default_platform)); detail::errHandler(default_error_); return default_; } //! \brief Wrapper for clGetPlatformInfo(). template cl_int getInfo(cl_platform_info name, T* param) const { return detail::errHandler( detail::getInfo(::clGetPlatformInfo, object_, name, param), __GET_PLATFORM_INFO_ERR); } //! \brief Wrapper for clGetPlatformInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_platform_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } /*! \brief Gets a list of devices for this platform. * * Wraps clGetDeviceIDs(). */ cl_int getDevices( cl_device_type type, vector* devices) const { cl_uint n = 0; if( devices == nullptr ) { return detail::errHandler(CL_INVALID_ARG_VALUE, __GET_DEVICE_IDS_ERR); } cl_int err = ::clGetDeviceIDs(object_, type, 0, nullptr, &n); if (err != CL_SUCCESS && err != CL_DEVICE_NOT_FOUND) { return detail::errHandler(err, __GET_DEVICE_IDS_ERR); } vector ids(n); if (n>0) { err = ::clGetDeviceIDs(object_, type, n, ids.data(), nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_DEVICE_IDS_ERR); } } // Cannot trivially assign because we need to capture intermediates // with safe construction // We must retain things we obtain from the API to avoid releasing // API-owned objects. if (devices) { devices->resize(ids.size()); // Assign to param, constructing with retain behaviour // to correctly capture each underlying CL object for (size_type i = 0; i < ids.size(); i++) { (*devices)[i] = Device(ids[i], true); } } return CL_SUCCESS; } #if defined(CL_HPP_USE_DX_INTEROP) /*! \brief Get the list of available D3D10 devices. * * \param d3d_device_source. * * \param d3d_object. * * \param d3d_device_set. * * \param devices returns a vector of OpenCL D3D10 devices found. The cl::Device * values returned in devices can be used to identify a specific OpenCL * device. If \a devices argument is nullptr, this argument is ignored. * * \return One of the following values: * - CL_SUCCESS if the function is executed successfully. * * The application can query specific capabilities of the OpenCL device(s) * returned by cl::getDevices. This can be used by the application to * determine which device(s) to use. * * \note In the case that exceptions are enabled and a return value * other than CL_SUCCESS is generated, then cl::Error exception is * generated. */ cl_int getDevices( cl_d3d10_device_source_khr d3d_device_source, void * d3d_object, cl_d3d10_device_set_khr d3d_device_set, vector* devices) const { typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clGetDeviceIDsFromD3D10KHR)( cl_platform_id platform, cl_d3d10_device_source_khr d3d_device_source, void * d3d_object, cl_d3d10_device_set_khr d3d_device_set, cl_uint num_entries, cl_device_id * devices, cl_uint* num_devices); if( devices == nullptr ) { return detail::errHandler(CL_INVALID_ARG_VALUE, __GET_DEVICE_IDS_ERR); } static PFN_clGetDeviceIDsFromD3D10KHR pfn_clGetDeviceIDsFromD3D10KHR = nullptr; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(object_, clGetDeviceIDsFromD3D10KHR); #endif #if CL_HPP_MINIMUM_OPENCL_VERSION < 120 CL_HPP_INIT_CL_EXT_FCN_PTR_(clGetDeviceIDsFromD3D10KHR); #endif cl_uint n = 0; cl_int err = pfn_clGetDeviceIDsFromD3D10KHR( object_, d3d_device_source, d3d_object, d3d_device_set, 0, nullptr, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_DEVICE_IDS_ERR); } vector ids(n); err = pfn_clGetDeviceIDsFromD3D10KHR( object_, d3d_device_source, d3d_object, d3d_device_set, n, ids.data(), nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_DEVICE_IDS_ERR); } // Cannot trivially assign because we need to capture intermediates // with safe construction // We must retain things we obtain from the API to avoid releasing // API-owned objects. if (devices) { devices->resize(ids.size()); // Assign to param, constructing with retain behaviour // to correctly capture each underlying CL object for (size_type i = 0; i < ids.size(); i++) { (*devices)[i] = Device(ids[i], true); } } return CL_SUCCESS; } #endif /*! \brief Gets a list of available platforms. * * Wraps clGetPlatformIDs(). */ static cl_int get( vector* platforms) { cl_uint n = 0; if( platforms == nullptr ) { return detail::errHandler(CL_INVALID_ARG_VALUE, __GET_PLATFORM_IDS_ERR); } cl_int err = ::clGetPlatformIDs(0, nullptr, &n); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); } vector ids(n); err = ::clGetPlatformIDs(n, ids.data(), nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_PLATFORM_IDS_ERR); } if (platforms) { platforms->resize(ids.size()); // Platforms don't reference count for (size_type i = 0; i < ids.size(); i++) { (*platforms)[i] = Platform(ids[i]); } } return CL_SUCCESS; } /*! \brief Gets the first available platform. * * Wraps clGetPlatformIDs(), returning the first result. */ static cl_int get( Platform * platform) { cl_int err; Platform default_platform = Platform::getDefault(&err); if (platform) { *platform = default_platform; } return err; } /*! \brief Gets the first available platform, returning it by value. * * \return Returns a valid platform if one is available. * If no platform is available will return a null platform. * Throws an exception if no platforms are available * or an error condition occurs. * Wraps clGetPlatformIDs(), returning the first result. */ static Platform get( cl_int * errResult = nullptr) { cl_int err; Platform default_platform = Platform::getDefault(&err); if (errResult) { *errResult = err; } return default_platform; } #if CL_HPP_TARGET_OPENCL_VERSION >= 120 //! \brief Wrapper for clUnloadCompiler(). cl_int unloadCompiler() { return ::clUnloadPlatformCompiler(object_); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 }; // class Platform CL_HPP_DEFINE_STATIC_MEMBER_ std::once_flag Platform::default_initialized_; CL_HPP_DEFINE_STATIC_MEMBER_ Platform Platform::default_; CL_HPP_DEFINE_STATIC_MEMBER_ cl_int Platform::default_error_ = CL_SUCCESS; /** * Deprecated APIs for 1.2 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) /** * Unload the OpenCL compiler. * \note Deprecated for OpenCL 1.2. Use Platform::unloadCompiler instead. */ inline CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int UnloadCompiler() CL_API_SUFFIX__VERSION_1_1_DEPRECATED; inline cl_int UnloadCompiler() { return ::clUnloadCompiler(); } #endif // #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) /*! \brief Class interface for cl_context. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_context as the original. For details, see * clRetainContext() and clReleaseContext(). * * \see cl_context */ class Context : public detail::Wrapper { private: static std::once_flag default_initialized_; static Context default_; static cl_int default_error_; /*! \brief Create the default context from the default device type in the default platform. * * This sets @c default_ and @c default_error_. It does not throw * @c cl::Error. */ static void makeDefault() { /* Throwing an exception from a call_once invocation does not do * what we wish, so we catch it and save the error. */ #if defined(CL_HPP_ENABLE_EXCEPTIONS) try #endif { #if !defined(__APPLE__) && !defined(__MACOS) const Platform &p = Platform::getDefault(); cl_platform_id defaultPlatform = p(); cl_context_properties properties[3] = { CL_CONTEXT_PLATFORM, (cl_context_properties)defaultPlatform, 0 }; #else // #if !defined(__APPLE__) && !defined(__MACOS) cl_context_properties *properties = nullptr; #endif // #if !defined(__APPLE__) && !defined(__MACOS) default_ = Context( CL_DEVICE_TYPE_DEFAULT, properties, nullptr, nullptr, &default_error_); } #if defined(CL_HPP_ENABLE_EXCEPTIONS) catch (cl::Error &e) { default_error_ = e.err(); } #endif } /*! \brief Create the default context from a provided Context. * * This sets @c default_. It does not throw * @c cl::Error. */ static void makeDefaultProvided(const Context &c) { default_ = c; } public: #ifdef CL_HPP_UNIT_TEST_ENABLE /*! \brief Reset the default. * * This sets @c default_ to an empty value to support cleanup in * the unit test framework. * This function is not thread safe. */ static void unitTestClearDefault() { default_ = Context(); } #endif // #ifdef CL_HPP_UNIT_TEST_ENABLE /*! \brief Constructs a context including a list of specified devices. * * Wraps clCreateContext(). */ Context( const vector& devices, const cl_context_properties* properties = nullptr, void (CL_CALLBACK * notifyFptr)( const char *, const void *, size_type, void *) = nullptr, void* data = nullptr, cl_int* err = nullptr) { cl_int error; size_type numDevices = devices.size(); vector deviceIDs(numDevices); for( size_type deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex ) { deviceIDs[deviceIndex] = (devices[deviceIndex])(); } object_ = ::clCreateContext( properties, (cl_uint) numDevices, deviceIDs.data(), notifyFptr, data, &error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (err != nullptr) { *err = error; } } /*! \brief Constructs a context including a specific device. * * Wraps clCreateContext(). */ Context( const Device& device, const cl_context_properties* properties = nullptr, void (CL_CALLBACK * notifyFptr)( const char *, const void *, size_type, void *) = nullptr, void* data = nullptr, cl_int* err = nullptr) { cl_int error; cl_device_id deviceID = device(); object_ = ::clCreateContext( properties, 1, &deviceID, notifyFptr, data, &error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (err != nullptr) { *err = error; } } /*! \brief Constructs a context including all or a subset of devices of a specified type. * * Wraps clCreateContextFromType(). */ Context( cl_device_type type, const cl_context_properties* properties = nullptr, void (CL_CALLBACK * notifyFptr)( const char *, const void *, size_type, void *) = nullptr, void* data = nullptr, cl_int* err = nullptr) { cl_int error; #if !defined(__APPLE__) && !defined(__MACOS) cl_context_properties prop[4] = {CL_CONTEXT_PLATFORM, 0, 0, 0 }; if (properties == nullptr) { // Get a valid platform ID as we cannot send in a blank one vector platforms; error = Platform::get(&platforms); if (error != CL_SUCCESS) { detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); if (err != nullptr) { *err = error; } return; } // Check the platforms we found for a device of our specified type cl_context_properties platform_id = 0; for (unsigned int i = 0; i < platforms.size(); i++) { vector devices; #if defined(CL_HPP_ENABLE_EXCEPTIONS) try { #endif error = platforms[i].getDevices(type, &devices); #if defined(CL_HPP_ENABLE_EXCEPTIONS) } catch (cl::Error& e) { error = e.err(); } // Catch if exceptions are enabled as we don't want to exit if first platform has no devices of type // We do error checking next anyway, and can throw there if needed #endif // Only squash CL_SUCCESS and CL_DEVICE_NOT_FOUND if (error != CL_SUCCESS && error != CL_DEVICE_NOT_FOUND) { detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); if (err != nullptr) { *err = error; } } if (devices.size() > 0) { platform_id = (cl_context_properties)platforms[i](); break; } } if (platform_id == 0) { detail::errHandler(CL_DEVICE_NOT_FOUND, __CREATE_CONTEXT_FROM_TYPE_ERR); if (err != nullptr) { *err = CL_DEVICE_NOT_FOUND; } return; } prop[1] = platform_id; properties = &prop[0]; } #endif object_ = ::clCreateContextFromType( properties, type, notifyFptr, data, &error); detail::errHandler(error, __CREATE_CONTEXT_FROM_TYPE_ERR); if (err != nullptr) { *err = error; } } /*! \brief Returns a singleton context including all devices of CL_DEVICE_TYPE_DEFAULT. * * \note All calls to this function return the same cl_context as the first. */ static Context getDefault(cl_int * err = nullptr) { std::call_once(default_initialized_, makeDefault); detail::errHandler(default_error_); if (err != nullptr) { *err = default_error_; } return default_; } /** * Modify the default context to be used by * subsequent operations. * Will only set the default if no default was previously created. * @return updated default context. * Should be compared to the passed value to ensure that it was updated. */ static Context setDefault(const Context &default_context) { std::call_once(default_initialized_, makeDefaultProvided, std::cref(default_context)); detail::errHandler(default_error_); return default_; } //! \brief Default constructor - initializes to nullptr. Context() : detail::Wrapper() { } /*! \brief Constructor from cl_context - takes ownership. * * This effectively transfers ownership of a refcount on the cl_context * into the new Context object. */ explicit Context(const cl_context& context, bool retainObject = false) : detail::Wrapper(context, retainObject) { } /*! \brief Assignment operator from cl_context - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseContext() on the value previously held by this instance. */ Context& operator = (const cl_context& rhs) { detail::Wrapper::operator=(rhs); return *this; } //! \brief Wrapper for clGetContextInfo(). template cl_int getInfo(cl_context_info name, T* param) const { return detail::errHandler( detail::getInfo(::clGetContextInfo, object_, name, param), __GET_CONTEXT_INFO_ERR); } //! \brief Wrapper for clGetContextInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_context_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } /*! \brief Gets a list of supported image formats. * * Wraps clGetSupportedImageFormats(). */ cl_int getSupportedImageFormats( cl_mem_flags flags, cl_mem_object_type type, vector* formats) const { cl_uint numEntries; if (!formats) { return CL_SUCCESS; } cl_int err = ::clGetSupportedImageFormats( object_, flags, type, 0, nullptr, &numEntries); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_SUPPORTED_IMAGE_FORMATS_ERR); } if (numEntries > 0) { vector value(numEntries); err = ::clGetSupportedImageFormats( object_, flags, type, numEntries, (cl_image_format*)value.data(), nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __GET_SUPPORTED_IMAGE_FORMATS_ERR); } formats->assign(begin(value), end(value)); } else { // If no values are being returned, ensure an empty vector comes back formats->clear(); } return CL_SUCCESS; } #if CL_HPP_TARGET_OPENCL_VERSION >= 300 /*! \brief Registers a destructor callback function with a context. * * Wraps clSetContextDestructorCallback(). * * Each call to this function registers the specified callback function on * a destructor callback stack associated with context. The registered * callback functions are called in the reverse order in which they were registered. * If a context callback function was specified when context was created, * it will not be called after any context destructor callback is called. */ cl_int setDestructorCallback( void (CL_CALLBACK * pfn_notify)(cl_context, void *), void * user_data = nullptr) { return detail::errHandler( ::clSetContextDestructorCallback( object_, pfn_notify, user_data), __SET_CONTEXT_DESCTRUCTOR_CALLBACK_ERR); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 300 }; inline void Device::makeDefault() { /* Throwing an exception from a call_once invocation does not do * what we wish, so we catch it and save the error. */ #if defined(CL_HPP_ENABLE_EXCEPTIONS) try #endif { cl_int error = 0; Context context = Context::getDefault(&error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (error != CL_SUCCESS) { default_error_ = error; } else { default_ = context.getInfo()[0]; default_error_ = CL_SUCCESS; } } #if defined(CL_HPP_ENABLE_EXCEPTIONS) catch (cl::Error &e) { default_error_ = e.err(); } #endif } CL_HPP_DEFINE_STATIC_MEMBER_ std::once_flag Context::default_initialized_; CL_HPP_DEFINE_STATIC_MEMBER_ Context Context::default_; CL_HPP_DEFINE_STATIC_MEMBER_ cl_int Context::default_error_ = CL_SUCCESS; /*! \brief Class interface for cl_event. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_event as the original. For details, see * clRetainEvent() and clReleaseEvent(). * * \see cl_event */ class Event : public detail::Wrapper { public: //! \brief Default constructor - initializes to nullptr. Event() : detail::Wrapper() { } /*! \brief Constructor from cl_event - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * This effectively transfers ownership of a refcount on the cl_event * into the new Event object. */ explicit Event(const cl_event& event, bool retainObject = false) : detail::Wrapper(event, retainObject) { } /*! \brief Assignment operator from cl_event - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseEvent() on the value previously held by this instance. */ Event& operator = (const cl_event& rhs) { detail::Wrapper::operator=(rhs); return *this; } //! \brief Wrapper for clGetEventInfo(). template cl_int getInfo(cl_event_info name, T* param) const { return detail::errHandler( detail::getInfo(::clGetEventInfo, object_, name, param), __GET_EVENT_INFO_ERR); } //! \brief Wrapper for clGetEventInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_event_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } //! \brief Wrapper for clGetEventProfilingInfo(). template cl_int getProfilingInfo(cl_profiling_info name, T* param) const { return detail::errHandler(detail::getInfo( ::clGetEventProfilingInfo, object_, name, param), __GET_EVENT_PROFILE_INFO_ERR); } //! \brief Wrapper for clGetEventProfilingInfo() that returns by value. template typename detail::param_traits::param_type getProfilingInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_profiling_info, name>::param_type param; cl_int result = getProfilingInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } /*! \brief Blocks the calling thread until this event completes. * * Wraps clWaitForEvents(). */ cl_int wait() const { return detail::errHandler( ::clWaitForEvents(1, &object_), __WAIT_FOR_EVENTS_ERR); } #if CL_HPP_TARGET_OPENCL_VERSION >= 110 /*! \brief Registers a user callback function for a specific command execution status. * * Wraps clSetEventCallback(). */ cl_int setCallback( cl_int type, void (CL_CALLBACK * pfn_notify)(cl_event, cl_int, void *), void * user_data = nullptr) { return detail::errHandler( ::clSetEventCallback( object_, type, pfn_notify, user_data), __SET_EVENT_CALLBACK_ERR); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 /*! \brief Blocks the calling thread until every event specified is complete. * * Wraps clWaitForEvents(). */ static cl_int waitForEvents(const vector& events) { static_assert(sizeof(cl::Event) == sizeof(cl_event), "Size of cl::Event must be equal to size of cl_event"); return detail::errHandler( ::clWaitForEvents( (cl_uint) events.size(), (events.size() > 0) ? (cl_event*)&events.front() : nullptr), __WAIT_FOR_EVENTS_ERR); } }; #if CL_HPP_TARGET_OPENCL_VERSION >= 110 /*! \brief Class interface for user events (a subset of cl_event's). * * See Event for details about copy semantics, etc. */ class UserEvent : public Event { public: /*! \brief Constructs a user event on a given context. * * Wraps clCreateUserEvent(). */ UserEvent( const Context& context, cl_int * err = nullptr) { cl_int error; object_ = ::clCreateUserEvent( context(), &error); detail::errHandler(error, __CREATE_USER_EVENT_ERR); if (err != nullptr) { *err = error; } } //! \brief Default constructor - initializes to nullptr. UserEvent() : Event() { } /*! \brief Sets the execution status of a user event object. * * Wraps clSetUserEventStatus(). */ cl_int setStatus(cl_int status) { return detail::errHandler( ::clSetUserEventStatus(object_,status), __SET_USER_EVENT_STATUS_ERR); } }; #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 /*! \brief Blocks the calling thread until every event specified is complete. * * Wraps clWaitForEvents(). */ inline static cl_int WaitForEvents(const vector& events) { return detail::errHandler( ::clWaitForEvents( (cl_uint) events.size(), (events.size() > 0) ? (cl_event*)&events.front() : nullptr), __WAIT_FOR_EVENTS_ERR); } /*! \brief Class interface for cl_mem. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_mem as the original. For details, see * clRetainMemObject() and clReleaseMemObject(). * * \see cl_mem */ class Memory : public detail::Wrapper { public: //! \brief Default constructor - initializes to nullptr. Memory() : detail::Wrapper() { } /*! \brief Constructor from cl_mem - takes ownership. * * Optionally transfer ownership of a refcount on the cl_mem * into the new Memory object. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * * See Memory for further details. */ explicit Memory(const cl_mem& memory, bool retainObject) : detail::Wrapper(memory, retainObject) { } /*! \brief Assignment operator from cl_mem - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseMemObject() on the value previously held by this instance. */ Memory& operator = (const cl_mem& rhs) { detail::Wrapper::operator=(rhs); return *this; } //! \brief Wrapper for clGetMemObjectInfo(). template cl_int getInfo(cl_mem_info name, T* param) const { return detail::errHandler( detail::getInfo(::clGetMemObjectInfo, object_, name, param), __GET_MEM_OBJECT_INFO_ERR); } //! \brief Wrapper for clGetMemObjectInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_mem_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } #if CL_HPP_TARGET_OPENCL_VERSION >= 110 /*! \brief Registers a callback function to be called when the memory object * is no longer needed. * * Wraps clSetMemObjectDestructorCallback(). * * Repeated calls to this function, for a given cl_mem value, will append * to the list of functions called (in reverse order) when memory object's * resources are freed and the memory object is deleted. * * \note * The registered callbacks are associated with the underlying cl_mem * value - not the Memory class instance. */ cl_int setDestructorCallback( void (CL_CALLBACK * pfn_notify)(cl_mem, void *), void * user_data = nullptr) { return detail::errHandler( ::clSetMemObjectDestructorCallback( object_, pfn_notify, user_data), __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 }; // Pre-declare copy functions class Buffer; template< typename IteratorType > cl_int copy( IteratorType startIterator, IteratorType endIterator, cl::Buffer &buffer ); template< typename IteratorType > cl_int copy( const cl::Buffer &buffer, IteratorType startIterator, IteratorType endIterator ); template< typename IteratorType > cl_int copy( const CommandQueue &queue, IteratorType startIterator, IteratorType endIterator, cl::Buffer &buffer ); template< typename IteratorType > cl_int copy( const CommandQueue &queue, const cl::Buffer &buffer, IteratorType startIterator, IteratorType endIterator ); #if CL_HPP_TARGET_OPENCL_VERSION >= 200 namespace detail { class SVMTraitNull { public: static cl_svm_mem_flags getSVMMemFlags() { return 0; } }; } // namespace detail template class SVMTraitReadWrite { public: static cl_svm_mem_flags getSVMMemFlags() { return CL_MEM_READ_WRITE | Trait::getSVMMemFlags(); } }; template class SVMTraitReadOnly { public: static cl_svm_mem_flags getSVMMemFlags() { return CL_MEM_READ_ONLY | Trait::getSVMMemFlags(); } }; template class SVMTraitWriteOnly { public: static cl_svm_mem_flags getSVMMemFlags() { return CL_MEM_WRITE_ONLY | Trait::getSVMMemFlags(); } }; template> class SVMTraitCoarse { public: static cl_svm_mem_flags getSVMMemFlags() { return Trait::getSVMMemFlags(); } }; template> class SVMTraitFine { public: static cl_svm_mem_flags getSVMMemFlags() { return CL_MEM_SVM_FINE_GRAIN_BUFFER | Trait::getSVMMemFlags(); } }; template> class SVMTraitAtomic { public: static cl_svm_mem_flags getSVMMemFlags() { return CL_MEM_SVM_FINE_GRAIN_BUFFER | CL_MEM_SVM_ATOMICS | Trait::getSVMMemFlags(); } }; // Pre-declare SVM map function template inline cl_int enqueueMapSVM( T* ptr, cl_bool blocking, cl_map_flags flags, size_type size, const vector* events = nullptr, Event* event = nullptr); /** * STL-like allocator class for managing SVM objects provided for convenience. * * Note that while this behaves like an allocator for the purposes of constructing vectors and similar objects, * care must be taken when using with smart pointers. * The allocator should not be used to construct a unique_ptr if we are using coarse-grained SVM mode because * the coarse-grained management behaviour would behave incorrectly with respect to reference counting. * * Instead the allocator embeds a Deleter which may be used with unique_ptr and is used * with the allocate_shared and allocate_ptr supplied operations. */ template class SVMAllocator { private: Context context_; public: typedef T value_type; typedef value_type* pointer; typedef const value_type* const_pointer; typedef value_type& reference; typedef const value_type& const_reference; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; template struct rebind { typedef SVMAllocator other; }; template friend class SVMAllocator; SVMAllocator() : context_(Context::getDefault()) { } explicit SVMAllocator(cl::Context context) : context_(context) { } SVMAllocator(const SVMAllocator &other) : context_(other.context_) { } template SVMAllocator(const SVMAllocator &other) : context_(other.context_) { } ~SVMAllocator() { } pointer address(reference r) CL_HPP_NOEXCEPT_ { return std::addressof(r); } const_pointer address(const_reference r) CL_HPP_NOEXCEPT_ { return std::addressof(r); } /** * Allocate an SVM pointer. * * If the allocator is coarse-grained, this will take ownership to allow * containers to correctly construct data in place. */ pointer allocate( size_type size, typename cl::SVMAllocator::const_pointer = 0) { // Allocate memory with default alignment matching the size of the type void* voidPointer = clSVMAlloc( context_(), SVMTrait::getSVMMemFlags(), size*sizeof(T), 0); pointer retValue = reinterpret_cast( voidPointer); #if defined(CL_HPP_ENABLE_EXCEPTIONS) if (!retValue) { std::bad_alloc excep; throw excep; } #endif // #if defined(CL_HPP_ENABLE_EXCEPTIONS) // If allocation was coarse-grained then map it if (!(SVMTrait::getSVMMemFlags() & CL_MEM_SVM_FINE_GRAIN_BUFFER)) { cl_int err = enqueueMapSVM(retValue, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE, size*sizeof(T)); if (err != CL_SUCCESS) { std::bad_alloc excep; throw excep; } } // If exceptions disabled, return null pointer from allocator return retValue; } void deallocate(pointer p, size_type) { clSVMFree(context_(), p); } /** * Return the maximum possible allocation size. * This is the minimum of the maximum sizes of all devices in the context. */ size_type max_size() const CL_HPP_NOEXCEPT_ { size_type maxSize = std::numeric_limits::max() / sizeof(T); for (const Device &d : context_.getInfo()) { maxSize = std::min( maxSize, static_cast(d.getInfo())); } return maxSize; } template< class U, class... Args > void construct(U* p, Args&&... args) { new(p)T(args...); } template< class U > void destroy(U* p) { p->~U(); } /** * Returns true if the contexts match. */ inline bool operator==(SVMAllocator const& rhs) { return (context_==rhs.context_); } inline bool operator!=(SVMAllocator const& a) { return !operator==(a); } }; // class SVMAllocator return cl::pointer(tmp, detail::Deleter{alloc, copies}); template class SVMAllocator { public: typedef void value_type; typedef value_type* pointer; typedef const value_type* const_pointer; template struct rebind { typedef SVMAllocator other; }; template friend class SVMAllocator; }; #if !defined(CL_HPP_NO_STD_UNIQUE_PTR) namespace detail { template class Deleter { private: Alloc alloc_; size_type copies_; public: typedef typename std::allocator_traits::pointer pointer; Deleter(const Alloc &alloc, size_type copies) : alloc_{ alloc }, copies_{ copies } { } void operator()(pointer ptr) const { Alloc tmpAlloc{ alloc_ }; std::allocator_traits::destroy(tmpAlloc, std::addressof(*ptr)); std::allocator_traits::deallocate(tmpAlloc, ptr, copies_); } }; } // namespace detail /** * Allocation operation compatible with std::allocate_ptr. * Creates a unique_ptr by default. * This requirement is to ensure that the control block is not * allocated in memory inaccessible to the host. */ template cl::pointer> allocate_pointer(const Alloc &alloc_, Args&&... args) { Alloc alloc(alloc_); static const size_type copies = 1; // Ensure that creation of the management block and the // object are dealt with separately such that we only provide a deleter T* tmp = std::allocator_traits::allocate(alloc, copies); if (!tmp) { std::bad_alloc excep; throw excep; } try { std::allocator_traits::construct( alloc, std::addressof(*tmp), std::forward(args)...); return cl::pointer>(tmp, detail::Deleter{alloc, copies}); } catch (std::bad_alloc&) { std::allocator_traits::deallocate(alloc, tmp, copies); throw; } } template< class T, class SVMTrait, class... Args > cl::pointer>> allocate_svm(Args... args) { SVMAllocator alloc; return cl::allocate_pointer(alloc, args...); } template< class T, class SVMTrait, class... Args > cl::pointer>> allocate_svm(const cl::Context &c, Args... args) { SVMAllocator alloc(c); return cl::allocate_pointer(alloc, args...); } #endif // #if !defined(CL_HPP_NO_STD_UNIQUE_PTR) /*! \brief Vector alias to simplify contruction of coarse-grained SVM containers. * */ template < class T > using coarse_svm_vector = vector>>; /*! \brief Vector alias to simplify contruction of fine-grained SVM containers. * */ template < class T > using fine_svm_vector = vector>>; /*! \brief Vector alias to simplify contruction of fine-grained SVM containers that support platform atomics. * */ template < class T > using atomic_svm_vector = vector>>; #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! \brief Class interface for Buffer Memory Objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Buffer : public Memory { public: /*! \brief Constructs a Buffer in a specified context. * * Wraps clCreateBuffer(). * * \param host_ptr Storage to be used if the CL_MEM_USE_HOST_PTR flag was * specified. Note alignment & exclusivity requirements. */ Buffer( const Context& context, cl_mem_flags flags, size_type size, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; object_ = ::clCreateBuffer(context(), flags, size, host_ptr, &error); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } } #if CL_HPP_TARGET_OPENCL_VERSION >= 300 /*! \brief Constructs a Buffer in a specified context and with specified properties. * * Wraps clCreateBufferWithProperties(). * * \param properties Optional list of properties for the buffer object and * their corresponding values. The non-empty list must * end with 0. * \param host_ptr Storage to be used if the CL_MEM_USE_HOST_PTR flag was * specified. Note alignment & exclusivity requirements. */ Buffer( const Context& context, const vector& properties, cl_mem_flags flags, size_type size, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; if (properties.empty()) { object_ = ::clCreateBufferWithProperties(context(), nullptr, flags, size, host_ptr, &error); } else { object_ = ::clCreateBufferWithProperties( context(), properties.data(), flags, size, host_ptr, &error); } detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } } #endif /*! \brief Constructs a Buffer in the default context. * * Wraps clCreateBuffer(). * * \param host_ptr Storage to be used if the CL_MEM_USE_HOST_PTR flag was * specified. Note alignment & exclusivity requirements. * * \see Context::getDefault() */ Buffer( cl_mem_flags flags, size_type size, void* host_ptr = nullptr, cl_int* err = nullptr) : Buffer(Context::getDefault(err), flags, size, host_ptr, err) { } #if CL_HPP_TARGET_OPENCL_VERSION >= 300 /*! \brief Constructs a Buffer in the default context and with specified properties. * * Wraps clCreateBufferWithProperties(). * * \param properties Optional list of properties for the buffer object and * their corresponding values. The non-empty list must * end with 0. * \param host_ptr Storage to be used if the CL_MEM_USE_HOST_PTR flag was * specified. Note alignment & exclusivity requirements. * * \see Context::getDefault() */ Buffer( const vector& properties, cl_mem_flags flags, size_type size, void* host_ptr = nullptr, cl_int* err = nullptr) : Buffer(Context::getDefault(err), properties, flags, size, host_ptr, err) { } #endif /*! * \brief Construct a Buffer from a host container via iterators. * IteratorType must be random access. * If useHostPtr is specified iterators must represent contiguous data. */ template< typename IteratorType > Buffer( IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr = false, cl_int* err = nullptr) { typedef typename std::iterator_traits::value_type DataType; cl_int error; cl_mem_flags flags = 0; if( readOnly ) { flags |= CL_MEM_READ_ONLY; } else { flags |= CL_MEM_READ_WRITE; } if( useHostPtr ) { flags |= CL_MEM_USE_HOST_PTR; } size_type size = sizeof(DataType)*(endIterator - startIterator); Context context = Context::getDefault(err); if( useHostPtr ) { object_ = ::clCreateBuffer(context(), flags, size, const_cast(&*startIterator), &error); } else { object_ = ::clCreateBuffer(context(), flags, size, 0, &error); } detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } if( !useHostPtr ) { error = cl::copy(startIterator, endIterator, *this); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } } } /*! * \brief Construct a Buffer from a host container via iterators using a specified context. * IteratorType must be random access. * If useHostPtr is specified iterators must represent contiguous data. */ template< typename IteratorType > Buffer(const Context &context, IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr = false, cl_int* err = nullptr); /*! * \brief Construct a Buffer from a host container via iterators using a specified queue. * If useHostPtr is specified iterators must be random access. */ template< typename IteratorType > Buffer(const CommandQueue &queue, IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr = false, cl_int* err = nullptr); //! \brief Default constructor - initializes to nullptr. Buffer() : Memory() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with earlier versions. * * See Memory for further details. */ explicit Buffer(const cl_mem& buffer, bool retainObject = false) : Memory(buffer, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Buffer& operator = (const cl_mem& rhs) { Memory::operator=(rhs); return *this; } #if CL_HPP_TARGET_OPENCL_VERSION >= 110 /*! \brief Creates a new buffer object from this. * * Wraps clCreateSubBuffer(). */ Buffer createSubBuffer( cl_mem_flags flags, cl_buffer_create_type buffer_create_type, const void * buffer_create_info, cl_int * err = nullptr) { Buffer result; cl_int error; result.object_ = ::clCreateSubBuffer( object_, flags, buffer_create_type, buffer_create_info, &error); detail::errHandler(error, __CREATE_SUBBUFFER_ERR); if (err != nullptr) { *err = error; } return result; } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 }; #if defined (CL_HPP_USE_DX_INTEROP) /*! \brief Class interface for creating OpenCL buffers from ID3D10Buffer's. * * This is provided to facilitate interoperability with Direct3D. * * See Memory for details about copy semantics, etc. * * \see Memory */ class BufferD3D10 : public Buffer { public: /*! \brief Constructs a BufferD3D10, in a specified context, from a * given ID3D10Buffer. * * Wraps clCreateFromD3D10BufferKHR(). */ BufferD3D10( const Context& context, cl_mem_flags flags, ID3D10Buffer* bufobj, cl_int * err = nullptr) : pfn_clCreateFromD3D10BufferKHR(nullptr) { typedef CL_API_ENTRY cl_mem (CL_API_CALL *PFN_clCreateFromD3D10BufferKHR)( cl_context context, cl_mem_flags flags, ID3D10Buffer* buffer, cl_int* errcode_ret); PFN_clCreateFromD3D10BufferKHR pfn_clCreateFromD3D10BufferKHR; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 vector props = context.getInfo(); cl_platform platform = nullptr; for( int i = 0; i < props.size(); ++i ) { if( props[i] == CL_CONTEXT_PLATFORM ) { platform = props[i+1]; } } CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCreateFromD3D10BufferKHR); #endif #if CL_HPP_MINIMUM_OPENCL_VERSION < 120 CL_HPP_INIT_CL_EXT_FCN_PTR_(clCreateFromD3D10BufferKHR); #endif cl_int error; object_ = pfn_clCreateFromD3D10BufferKHR( context(), flags, bufobj, &error); // TODO: This should really have a D3D10 rerror code! detail::errHandler(error, __CREATE_GL_BUFFER_ERR); if (err != nullptr) { *err = error; } } //! \brief Default constructor - initializes to nullptr. BufferD3D10() : Buffer() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit BufferD3D10(const cl_mem& buffer, bool retainObject = false) : Buffer(buffer, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ BufferD3D10& operator = (const cl_mem& rhs) { Buffer::operator=(rhs); return *this; } }; #endif /*! \brief C++ base class for Image Memory objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image : public Memory { protected: //! \brief Default constructor - initializes to nullptr. Image() : Memory() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image(const cl_mem& image, bool retainObject = false) : Memory(image, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image& operator = (const cl_mem& rhs) { Memory::operator=(rhs); return *this; } public: //! \brief Wrapper for clGetImageInfo(). template cl_int getImageInfo(cl_image_info name, T* param) const { return detail::errHandler( detail::getInfo(::clGetImageInfo, object_, name, param), __GET_IMAGE_INFO_ERR); } //! \brief Wrapper for clGetImageInfo() that returns by value. template typename detail::param_traits::param_type getImageInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_image_info, name>::param_type param; cl_int result = getImageInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } }; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /*! \brief Class interface for 1D Image Memory objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image1D : public Image { public: /*! \brief Constructs a 1D Image in a specified context. * * Wraps clCreateImage(). */ Image1D( const Context& context, cl_mem_flags flags, ImageFormat format, size_type width, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; cl_image_desc desc = {}; desc.image_type = CL_MEM_OBJECT_IMAGE1D; desc.image_width = width; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } //! \brief Default constructor - initializes to nullptr. Image1D() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image1D(const cl_mem& image1D, bool retainObject = false) : Image(image1D, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image1D& operator = (const cl_mem& rhs) { Image::operator=(rhs); return *this; } }; /*! \class Image1DBuffer * \brief Image interface for 1D buffer images. */ class Image1DBuffer : public Image { public: Image1DBuffer( const Context& context, cl_mem_flags flags, ImageFormat format, size_type width, const Buffer &buffer, cl_int* err = nullptr) { cl_int error; cl_image_desc desc = {}; desc.image_type = CL_MEM_OBJECT_IMAGE1D_BUFFER; desc.image_width = width; desc.buffer = buffer(); object_ = ::clCreateImage( context(), flags, &format, &desc, nullptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } Image1DBuffer() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image1DBuffer(const cl_mem& image1D, bool retainObject = false) : Image(image1D, retainObject) { } Image1DBuffer& operator = (const cl_mem& rhs) { Image::operator=(rhs); return *this; } }; /*! \class Image1DArray * \brief Image interface for arrays of 1D images. */ class Image1DArray : public Image { public: Image1DArray( const Context& context, cl_mem_flags flags, ImageFormat format, size_type arraySize, size_type width, size_type rowPitch, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; cl_image_desc desc = {}; desc.image_type = CL_MEM_OBJECT_IMAGE1D_ARRAY; desc.image_width = width; desc.image_array_size = arraySize; desc.image_row_pitch = rowPitch; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } Image1DArray() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image1DArray(const cl_mem& imageArray, bool retainObject = false) : Image(imageArray, retainObject) { } Image1DArray& operator = (const cl_mem& rhs) { Image::operator=(rhs); return *this; } }; #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /*! \brief Class interface for 2D Image Memory objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image2D : public Image { public: /*! \brief Constructs a 2D Image in a specified context. * * Wraps clCreateImage(). */ Image2D( const Context& context, cl_mem_flags flags, ImageFormat format, size_type width, size_type height, size_type row_pitch = 0, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; bool useCreateImage; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 && CL_HPP_MINIMUM_OPENCL_VERSION < 120 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useCreateImage = (version >= 0x10002); // OpenCL 1.2 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 120 useCreateImage = true; #else useCreateImage = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 120 if (useCreateImage) { cl_image_desc desc = {}; desc.image_type = CL_MEM_OBJECT_IMAGE2D; desc.image_width = width; desc.image_height = height; desc.image_row_pitch = row_pitch; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_MINIMUM_OPENCL_VERSION < 120 if (!useCreateImage) { object_ = ::clCreateImage2D( context(), flags,&format, width, height, row_pitch, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE2D_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 120 } #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /*! \brief Constructs a 2D Image from a buffer. * \note This will share storage with the underlying buffer. * * Requires OpenCL 2.0 or newer or OpenCL 1.2 and the * cl_khr_image2d_from_buffer extension. * * Wraps clCreateImage(). */ Image2D( const Context& context, ImageFormat format, const Buffer &sourceBuffer, size_type width, size_type height, size_type row_pitch = 0, cl_int* err = nullptr) { cl_int error; cl_image_desc desc = {}; desc.image_type = CL_MEM_OBJECT_IMAGE2D; desc.image_width = width; desc.image_height = height; desc.image_row_pitch = row_pitch; desc.buffer = sourceBuffer(); object_ = ::clCreateImage( context(), 0, // flags inherited from buffer &format, &desc, nullptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! \brief Constructs a 2D Image from an image. * \note This will share storage with the underlying image but may * reinterpret the channel order and type. * * The image will be created matching with a descriptor matching the source. * * \param order is the channel order to reinterpret the image data as. * The channel order may differ as described in the OpenCL * 2.0 API specification. * * Wraps clCreateImage(). */ Image2D( const Context& context, cl_channel_order order, const Image &sourceImage, cl_int* err = nullptr) { cl_int error; // Descriptor fields have to match source image size_type sourceWidth = sourceImage.getImageInfo(); size_type sourceHeight = sourceImage.getImageInfo(); size_type sourceRowPitch = sourceImage.getImageInfo(); cl_uint sourceNumMIPLevels = sourceImage.getImageInfo(); cl_uint sourceNumSamples = sourceImage.getImageInfo(); cl_image_format sourceFormat = sourceImage.getImageInfo(); // Update only the channel order. // Channel format inherited from source. sourceFormat.image_channel_order = order; cl_image_desc desc = {}; desc.image_type = CL_MEM_OBJECT_IMAGE2D; desc.image_width = sourceWidth; desc.image_height = sourceHeight; desc.image_row_pitch = sourceRowPitch; desc.num_mip_levels = sourceNumMIPLevels; desc.num_samples = sourceNumSamples; desc.buffer = sourceImage(); object_ = ::clCreateImage( context(), 0, // flags should be inherited from mem_object &sourceFormat, &desc, nullptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } #endif //#if CL_HPP_TARGET_OPENCL_VERSION >= 200 //! \brief Default constructor - initializes to nullptr. Image2D() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image2D(const cl_mem& image2D, bool retainObject = false) : Image(image2D, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image2D& operator = (const cl_mem& rhs) { Image::operator=(rhs); return *this; } }; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /*! \class Image2DArray * \brief Image interface for arrays of 2D images. */ class Image2DArray : public Image { public: Image2DArray( const Context& context, cl_mem_flags flags, ImageFormat format, size_type arraySize, size_type width, size_type height, size_type rowPitch, size_type slicePitch, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; cl_image_desc desc = {}; desc.image_type = CL_MEM_OBJECT_IMAGE2D_ARRAY; desc.image_width = width; desc.image_height = height; desc.image_array_size = arraySize; desc.image_row_pitch = rowPitch; desc.image_slice_pitch = slicePitch; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } Image2DArray() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image2DArray(const cl_mem& imageArray, bool retainObject = false) : Image(imageArray, retainObject) { } Image2DArray& operator = (const cl_mem& rhs) { Image::operator=(rhs); return *this; } }; #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /*! \brief Class interface for 3D Image Memory objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Image3D : public Image { public: /*! \brief Constructs a 3D Image in a specified context. * * Wraps clCreateImage(). */ Image3D( const Context& context, cl_mem_flags flags, ImageFormat format, size_type width, size_type height, size_type depth, size_type row_pitch = 0, size_type slice_pitch = 0, void* host_ptr = nullptr, cl_int* err = nullptr) { cl_int error; bool useCreateImage; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 && CL_HPP_MINIMUM_OPENCL_VERSION < 120 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useCreateImage = (version >= 0x10002); // OpenCL 1.2 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 120 useCreateImage = true; #else useCreateImage = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 120 if (useCreateImage) { cl_image_desc desc = {}; desc.image_type = CL_MEM_OBJECT_IMAGE3D; desc.image_width = width; desc.image_height = height; desc.image_depth = depth; desc.image_row_pitch = row_pitch; desc.image_slice_pitch = slice_pitch; object_ = ::clCreateImage( context(), flags, &format, &desc, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_MINIMUM_OPENCL_VERSION < 120 if (!useCreateImage) { object_ = ::clCreateImage3D( context(), flags, &format, width, height, depth, row_pitch, slice_pitch, host_ptr, &error); detail::errHandler(error, __CREATE_IMAGE3D_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 120 } //! \brief Default constructor - initializes to nullptr. Image3D() : Image() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * See Memory for further details. */ explicit Image3D(const cl_mem& image3D, bool retainObject = false) : Image(image3D, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Image3D& operator = (const cl_mem& rhs) { Image::operator=(rhs); return *this; } }; #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! \brief Class interface for Pipe Memory Objects. * * See Memory for details about copy semantics, etc. * * \see Memory */ class Pipe : public Memory { public: /*! \brief Constructs a Pipe in a specified context. * * Wraps clCreatePipe(). * @param context Context in which to create the pipe. * @param flags Bitfield. Only CL_MEM_READ_WRITE and CL_MEM_HOST_NO_ACCESS are valid. * @param packet_size Size in bytes of a single packet of the pipe. * @param max_packets Number of packets that may be stored in the pipe. * */ Pipe( const Context& context, cl_uint packet_size, cl_uint max_packets, cl_int* err = nullptr) { cl_int error; cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_HOST_NO_ACCESS; object_ = ::clCreatePipe(context(), flags, packet_size, max_packets, nullptr, &error); detail::errHandler(error, __CREATE_PIPE_ERR); if (err != nullptr) { *err = error; } } /*! \brief Constructs a Pipe in a the default context. * * Wraps clCreatePipe(). * @param flags Bitfield. Only CL_MEM_READ_WRITE and CL_MEM_HOST_NO_ACCESS are valid. * @param packet_size Size in bytes of a single packet of the pipe. * @param max_packets Number of packets that may be stored in the pipe. * */ Pipe( cl_uint packet_size, cl_uint max_packets, cl_int* err = nullptr) { cl_int error; Context context = Context::getDefault(err); cl_mem_flags flags = CL_MEM_READ_WRITE | CL_MEM_HOST_NO_ACCESS; object_ = ::clCreatePipe(context(), flags, packet_size, max_packets, nullptr, &error); detail::errHandler(error, __CREATE_PIPE_ERR); if (err != nullptr) { *err = error; } } //! \brief Default constructor - initializes to nullptr. Pipe() : Memory() { } /*! \brief Constructor from cl_mem - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with earlier versions. * * See Memory for further details. */ explicit Pipe(const cl_mem& pipe, bool retainObject = false) : Memory(pipe, retainObject) { } /*! \brief Assignment from cl_mem - performs shallow copy. * * See Memory for further details. */ Pipe& operator = (const cl_mem& rhs) { Memory::operator=(rhs); return *this; } //! \brief Wrapper for clGetMemObjectInfo(). template cl_int getInfo(cl_pipe_info name, T* param) const { return detail::errHandler( detail::getInfo(::clGetPipeInfo, object_, name, param), __GET_PIPE_INFO_ERR); } //! \brief Wrapper for clGetMemObjectInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_pipe_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } }; // class Pipe #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! \brief Class interface for cl_sampler. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_sampler as the original. For details, see * clRetainSampler() and clReleaseSampler(). * * \see cl_sampler */ class Sampler : public detail::Wrapper { public: //! \brief Default constructor - initializes to nullptr. Sampler() { } /*! \brief Constructs a Sampler in a specified context. * * Wraps clCreateSampler(). */ Sampler( const Context& context, cl_bool normalized_coords, cl_addressing_mode addressing_mode, cl_filter_mode filter_mode, cl_int* err = nullptr) { cl_int error; #if CL_HPP_TARGET_OPENCL_VERSION >= 200 cl_sampler_properties sampler_properties[] = { CL_SAMPLER_NORMALIZED_COORDS, normalized_coords, CL_SAMPLER_ADDRESSING_MODE, addressing_mode, CL_SAMPLER_FILTER_MODE, filter_mode, 0 }; object_ = ::clCreateSamplerWithProperties( context(), sampler_properties, &error); detail::errHandler(error, __CREATE_SAMPLER_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } #else object_ = ::clCreateSampler( context(), normalized_coords, addressing_mode, filter_mode, &error); detail::errHandler(error, __CREATE_SAMPLER_ERR); if (err != nullptr) { *err = error; } #endif } /*! \brief Constructor from cl_sampler - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * This effectively transfers ownership of a refcount on the cl_sampler * into the new Sampler object. */ explicit Sampler(const cl_sampler& sampler, bool retainObject = false) : detail::Wrapper(sampler, retainObject) { } /*! \brief Assignment operator from cl_sampler - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseSampler() on the value previously held by this instance. */ Sampler& operator = (const cl_sampler& rhs) { detail::Wrapper::operator=(rhs); return *this; } //! \brief Wrapper for clGetSamplerInfo(). template cl_int getInfo(cl_sampler_info name, T* param) const { return detail::errHandler( detail::getInfo(::clGetSamplerInfo, object_, name, param), __GET_SAMPLER_INFO_ERR); } //! \brief Wrapper for clGetSamplerInfo() that returns by value. template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_sampler_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } }; class Program; class CommandQueue; class DeviceCommandQueue; class Kernel; //! \brief Class interface for specifying NDRange values. class NDRange { private: size_type sizes_[3]; cl_uint dimensions_; public: //! \brief Default constructor - resulting range has zero dimensions. NDRange() : dimensions_(0) { sizes_[0] = 0; sizes_[1] = 0; sizes_[2] = 0; } //! \brief Constructs one-dimensional range. NDRange(size_type size0) : dimensions_(1) { sizes_[0] = size0; sizes_[1] = 1; sizes_[2] = 1; } //! \brief Constructs two-dimensional range. NDRange(size_type size0, size_type size1) : dimensions_(2) { sizes_[0] = size0; sizes_[1] = size1; sizes_[2] = 1; } //! \brief Constructs three-dimensional range. NDRange(size_type size0, size_type size1, size_type size2) : dimensions_(3) { sizes_[0] = size0; sizes_[1] = size1; sizes_[2] = size2; } //! \brief Constructs one-dimensional range. NDRange(array a) : NDRange(a[0]){} //! \brief Constructs two-dimensional range. NDRange(array a) : NDRange(a[0], a[1]){} //! \brief Constructs three-dimensional range. NDRange(array a) : NDRange(a[0], a[1], a[2]){} /*! \brief Conversion operator to const size_type *. * * \returns a pointer to the size of the first dimension. */ operator const size_type*() const { return sizes_; } //! \brief Queries the number of dimensions in the range. size_type dimensions() const { return dimensions_; } //! \brief Returns the size of the object in bytes based on the // runtime number of dimensions size_type size() const { return dimensions_*sizeof(size_type); } size_type* get() { return sizes_; } const size_type* get() const { return sizes_; } }; //! \brief A zero-dimensional range. static const NDRange NullRange; //! \brief Local address wrapper for use with Kernel::setArg struct LocalSpaceArg { size_type size_; }; namespace detail { template struct KernelArgumentHandler; // Enable for objects that are not subclasses of memory // Pointers, constants etc template struct KernelArgumentHandler::value>::type> { static size_type size(const T&) { return sizeof(T); } static const T* ptr(const T& value) { return &value; } }; // Enable for subclasses of memory where we want to get a reference to the cl_mem out // and pass that in for safety template struct KernelArgumentHandler::value>::type> { static size_type size(const T&) { return sizeof(cl_mem); } static const cl_mem* ptr(const T& value) { return &(value()); } }; // Specialization for DeviceCommandQueue defined later template <> struct KernelArgumentHandler { static size_type size(const LocalSpaceArg& value) { return value.size_; } static const void* ptr(const LocalSpaceArg&) { return nullptr; } }; } //! \endcond /*! Local * \brief Helper function for generating LocalSpaceArg objects. */ inline LocalSpaceArg Local(size_type size) { LocalSpaceArg ret = { size }; return ret; } /*! \brief Class interface for cl_kernel. * * \note Copies of these objects are shallow, meaning that the copy will refer * to the same underlying cl_kernel as the original. For details, see * clRetainKernel() and clReleaseKernel(). * * \see cl_kernel */ class Kernel : public detail::Wrapper { public: inline Kernel(const Program& program, const char* name, cl_int* err = nullptr); //! \brief Default constructor - initializes to nullptr. Kernel() { } /*! \brief Constructor from cl_kernel - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. * This effectively transfers ownership of a refcount on the cl_kernel * into the new Kernel object. */ explicit Kernel(const cl_kernel& kernel, bool retainObject = false) : detail::Wrapper(kernel, retainObject) { } /*! \brief Assignment operator from cl_kernel - takes ownership. * * This effectively transfers ownership of a refcount on the rhs and calls * clReleaseKernel() on the value previously held by this instance. */ Kernel& operator = (const cl_kernel& rhs) { detail::Wrapper::operator=(rhs); return *this; } template cl_int getInfo(cl_kernel_info name, T* param) const { return detail::errHandler( detail::getInfo(::clGetKernelInfo, object_, name, param), __GET_KERNEL_INFO_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_kernel_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } #if CL_HPP_TARGET_OPENCL_VERSION >= 120 template cl_int getArgInfo(cl_uint argIndex, cl_kernel_arg_info name, T* param) const { return detail::errHandler( detail::getInfo(::clGetKernelArgInfo, object_, argIndex, name, param), __GET_KERNEL_ARG_INFO_ERR); } template typename detail::param_traits::param_type getArgInfo(cl_uint argIndex, cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_kernel_arg_info, name>::param_type param; cl_int result = getArgInfo(argIndex, name, ¶m); if (err != nullptr) { *err = result; } return param; } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 template cl_int getWorkGroupInfo( const Device& device, cl_kernel_work_group_info name, T* param) const { return detail::errHandler( detail::getInfo( ::clGetKernelWorkGroupInfo, object_, device(), name, param), __GET_KERNEL_WORK_GROUP_INFO_ERR); } template typename detail::param_traits::param_type getWorkGroupInfo(const Device& device, cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_kernel_work_group_info, name>::param_type param; cl_int result = getWorkGroupInfo(device, name, ¶m); if (err != nullptr) { *err = result; } return param; } #if defined(CL_HPP_USE_CL_SUB_GROUPS_KHR) || CL_HPP_TARGET_OPENCL_VERSION >= 210 cl_int getSubGroupInfo(const cl::Device &dev, cl_kernel_sub_group_info name, const cl::NDRange &range, size_type* param) const { #if CL_HPP_TARGET_OPENCL_VERSION >= 210 return detail::errHandler( clGetKernelSubGroupInfo(object_, dev(), name, range.size(), range.get(), sizeof(size_type), param, nullptr), __GET_KERNEL_SUB_GROUP_INFO_ERR); #else // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 typedef clGetKernelSubGroupInfoKHR_fn PFN_clGetKernelSubGroupInfoKHR; static PFN_clGetKernelSubGroupInfoKHR pfn_clGetKernelSubGroupInfoKHR = nullptr; CL_HPP_INIT_CL_EXT_FCN_PTR_(clGetKernelSubGroupInfoKHR); return detail::errHandler( pfn_clGetKernelSubGroupInfoKHR(object_, dev(), name, range.size(), range.get(), sizeof(size_type), param, nullptr), __GET_KERNEL_SUB_GROUP_INFO_ERR); #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 } template size_type getSubGroupInfo(const cl::Device &dev, const cl::NDRange &range, cl_int* err = nullptr) const { size_type param; cl_int result = getSubGroupInfo(dev, name, range, ¶m); if (err != nullptr) { *err = result; } return param; } #endif // defined(CL_HPP_USE_CL_SUB_GROUPS_KHR) || CL_HPP_TARGET_OPENCL_VERSION >= 210 #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! \brief setArg overload taking a shared_ptr type */ template cl_int setArg(cl_uint index, const cl::pointer &argPtr) { return detail::errHandler( ::clSetKernelArgSVMPointer(object_, index, argPtr.get()), __SET_KERNEL_ARGS_ERR); } /*! \brief setArg overload taking a vector type. */ template cl_int setArg(cl_uint index, const cl::vector &argPtr) { return detail::errHandler( ::clSetKernelArgSVMPointer(object_, index, argPtr.data()), __SET_KERNEL_ARGS_ERR); } /*! \brief setArg overload taking a pointer type */ template typename std::enable_if::value, cl_int>::type setArg(cl_uint index, const T argPtr) { return detail::errHandler( ::clSetKernelArgSVMPointer(object_, index, argPtr), __SET_KERNEL_ARGS_ERR); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! \brief setArg overload taking a POD type */ template typename std::enable_if::value, cl_int>::type setArg(cl_uint index, const T &value) { return detail::errHandler( ::clSetKernelArg( object_, index, detail::KernelArgumentHandler::size(value), detail::KernelArgumentHandler::ptr(value)), __SET_KERNEL_ARGS_ERR); } cl_int setArg(cl_uint index, size_type size, const void* argPtr) { return detail::errHandler( ::clSetKernelArg(object_, index, size, argPtr), __SET_KERNEL_ARGS_ERR); } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /*! * Specify a vector of SVM pointers that the kernel may access in * addition to its arguments. */ cl_int setSVMPointers(const vector &pointerList) { return detail::errHandler( ::clSetKernelExecInfo( object_, CL_KERNEL_EXEC_INFO_SVM_PTRS, sizeof(void*)*pointerList.size(), pointerList.data())); } /*! * Specify a std::array of SVM pointers that the kernel may access in * addition to its arguments. */ template cl_int setSVMPointers(const std::array &pointerList) { return detail::errHandler( ::clSetKernelExecInfo( object_, CL_KERNEL_EXEC_INFO_SVM_PTRS, sizeof(void*)*pointerList.size(), pointerList.data())); } /*! \brief Enable fine-grained system SVM. * * \note It is only possible to enable fine-grained system SVM if all devices * in the context associated with kernel support it. * * \param svmEnabled True if fine-grained system SVM is requested. False otherwise. * \return CL_SUCCESS if the function was executed succesfully. CL_INVALID_OPERATION * if no devices in the context support fine-grained system SVM. * * \see clSetKernelExecInfo */ cl_int enableFineGrainedSystemSVM(bool svmEnabled) { cl_bool svmEnabled_ = svmEnabled ? CL_TRUE : CL_FALSE; return detail::errHandler( ::clSetKernelExecInfo( object_, CL_KERNEL_EXEC_INFO_SVM_FINE_GRAIN_SYSTEM, sizeof(cl_bool), &svmEnabled_ ) ); } template void setSVMPointersHelper(std::array &pointerList, const pointer &t0, const pointer &t1, Ts & ... ts) { pointerList[index] = static_cast(t0.get()); setSVMPointersHelper(pointerList, t1, ts...); } template typename std::enable_if::value, void>::type setSVMPointersHelper(std::array &pointerList, T0 t0, T1 t1, Ts... ts) { pointerList[index] = static_cast(t0); setSVMPointersHelper(pointerList, t1, ts...); } template void setSVMPointersHelper(std::array &pointerList, const pointer &t0) { pointerList[index] = static_cast(t0.get()); } template typename std::enable_if::value, void>::type setSVMPointersHelper(std::array &pointerList, T0 t0) { pointerList[index] = static_cast(t0); } template cl_int setSVMPointers(const T0 &t0, Ts & ... ts) { std::array pointerList; setSVMPointersHelper<0, 1 + sizeof...(Ts)>(pointerList, t0, ts...); return detail::errHandler( ::clSetKernelExecInfo( object_, CL_KERNEL_EXEC_INFO_SVM_PTRS, sizeof(void*)*(1 + sizeof...(Ts)), pointerList.data())); } template cl_int setExecInfo(cl_kernel_exec_info param_name, const T& val) { return detail::errHandler( ::clSetKernelExecInfo( object_, param_name, sizeof(T), &val)); } template cl_int setExecInfo(typename detail::param_traits::param_type& val) { return setExecInfo(name, val); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_TARGET_OPENCL_VERSION >= 210 /** * Make a deep copy of the kernel object including its arguments. * @return A new kernel object with internal state entirely separate from that * of the original but with any arguments set on the original intact. */ Kernel clone() { cl_int error; Kernel retValue(clCloneKernel(this->get(), &error)); detail::errHandler(error, __CLONE_KERNEL_ERR); return retValue; } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 }; /*! \class Program * \brief Program interface that implements cl_program. */ class Program : public detail::Wrapper { public: #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) typedef vector> Binaries; typedef vector Sources; #else // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) typedef vector > Binaries; typedef vector > Sources; #endif // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) Program( const string& source, bool build = false, cl_int* err = nullptr) { cl_int error; const char * strings = source.c_str(); const size_type length = source.size(); Context context = Context::getDefault(err); object_ = ::clCreateProgramWithSource( context(), (cl_uint)1, &strings, &length, &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); if (error == CL_SUCCESS && build) { error = ::clBuildProgram( object_, 0, nullptr, #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) "-cl-std=CL2.0", #else "", #endif // #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) nullptr, nullptr); detail::buildErrHandler(error, __BUILD_PROGRAM_ERR, getBuildInfo()); } if (err != nullptr) { *err = error; } } Program( const Context& context, const string& source, bool build = false, cl_int* err = nullptr) { cl_int error; const char * strings = source.c_str(); const size_type length = source.size(); object_ = ::clCreateProgramWithSource( context(), (cl_uint)1, &strings, &length, &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); if (error == CL_SUCCESS && build) { error = ::clBuildProgram( object_, 0, nullptr, #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) "-cl-std=CL2.0", #else "", #endif // #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) nullptr, nullptr); detail::buildErrHandler(error, __BUILD_PROGRAM_ERR, getBuildInfo()); } if (err != nullptr) { *err = error; } } /** * Create a program from a vector of source strings and the default context. * Does not compile or link the program. */ Program( const Sources& sources, cl_int* err = nullptr) { cl_int error; Context context = Context::getDefault(err); const size_type n = (size_type)sources.size(); vector lengths(n); vector strings(n); for (size_type i = 0; i < n; ++i) { #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) strings[i] = sources[(int)i].data(); lengths[i] = sources[(int)i].length(); #else // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) strings[i] = sources[(int)i].first; lengths[i] = sources[(int)i].second; #endif // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) } object_ = ::clCreateProgramWithSource( context(), (cl_uint)n, strings.data(), lengths.data(), &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); if (err != nullptr) { *err = error; } } /** * Create a program from a vector of source strings and a provided context. * Does not compile or link the program. */ Program( const Context& context, const Sources& sources, cl_int* err = nullptr) { cl_int error; const size_type n = (size_type)sources.size(); vector lengths(n); vector strings(n); for (size_type i = 0; i < n; ++i) { #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) strings[i] = sources[(int)i].data(); lengths[i] = sources[(int)i].length(); #else // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) strings[i] = sources[(int)i].first; lengths[i] = sources[(int)i].second; #endif // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) } object_ = ::clCreateProgramWithSource( context(), (cl_uint)n, strings.data(), lengths.data(), &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_SOURCE_ERR); if (err != nullptr) { *err = error; } } #if defined(CL_HPP_USE_IL_KHR) || CL_HPP_TARGET_OPENCL_VERSION >= 210 /** * Program constructor to allow construction of program from SPIR-V or another IL. * * Requires OpenCL 2.1 or newer or the cl_khr_il_program extension. */ Program( const vector& IL, bool build = false, cl_int* err = nullptr) { cl_int error; Context context = Context::getDefault(err); #if CL_HPP_TARGET_OPENCL_VERSION >= 210 object_ = ::clCreateProgramWithIL( context(), static_cast(IL.data()), IL.size(), &error); #else // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 typedef clCreateProgramWithILKHR_fn PFN_clCreateProgramWithILKHR; static PFN_clCreateProgramWithILKHR pfn_clCreateProgramWithILKHR = nullptr; CL_HPP_INIT_CL_EXT_FCN_PTR_(clCreateProgramWithILKHR); object_ = pfn_clCreateProgramWithILKHR( context(), static_cast(IL.data()), IL.size(), &error); #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 detail::errHandler(error, __CREATE_PROGRAM_WITH_IL_ERR); if (error == CL_SUCCESS && build) { error = ::clBuildProgram( object_, 0, nullptr, #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) "-cl-std=CL2.0", #else "", #endif // #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) nullptr, nullptr); detail::buildErrHandler(error, __BUILD_PROGRAM_ERR, getBuildInfo()); } if (err != nullptr) { *err = error; } } /** * Program constructor to allow construction of program from SPIR-V or another IL * for a specific context. * * Requires OpenCL 2.1 or newer or the cl_khr_il_program extension. */ Program( const Context& context, const vector& IL, bool build = false, cl_int* err = nullptr) { cl_int error; #if CL_HPP_TARGET_OPENCL_VERSION >= 210 object_ = ::clCreateProgramWithIL( context(), static_cast(IL.data()), IL.size(), &error); #else // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 typedef clCreateProgramWithILKHR_fn PFN_clCreateProgramWithILKHR; static PFN_clCreateProgramWithILKHR pfn_clCreateProgramWithILKHR = nullptr; CL_HPP_INIT_CL_EXT_FCN_PTR_(clCreateProgramWithILKHR); object_ = pfn_clCreateProgramWithILKHR( context(), static_cast(IL.data()), IL.size(), &error); #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 detail::errHandler(error, __CREATE_PROGRAM_WITH_IL_ERR); if (error == CL_SUCCESS && build) { error = ::clBuildProgram( object_, 0, nullptr, #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) "-cl-std=CL2.0", #else "", #endif // #if !defined(CL_HPP_CL_1_2_DEFAULT_BUILD) nullptr, nullptr); detail::buildErrHandler(error, __BUILD_PROGRAM_ERR, getBuildInfo()); } if (err != nullptr) { *err = error; } } #endif // defined(CL_HPP_USE_IL_KHR) || CL_HPP_TARGET_OPENCL_VERSION >= 210 /** * Construct a program object from a list of devices and a per-device list of binaries. * \param context A valid OpenCL context in which to construct the program. * \param devices A vector of OpenCL device objects for which the program will be created. * \param binaries A vector of pairs of a pointer to a binary object and its length. * \param binaryStatus An optional vector that on completion will be resized to * match the size of binaries and filled with values to specify if each binary * was successfully loaded. * Set to CL_SUCCESS if the binary was successfully loaded. * Set to CL_INVALID_VALUE if the length is 0 or the binary pointer is nullptr. * Set to CL_INVALID_BINARY if the binary provided is not valid for the matching device. * \param err if non-nullptr will be set to CL_SUCCESS on successful operation or one of the following errors: * CL_INVALID_CONTEXT if context is not a valid context. * CL_INVALID_VALUE if the length of devices is zero; or if the length of binaries does not match the length of devices; * or if any entry in binaries is nullptr or has length 0. * CL_INVALID_DEVICE if OpenCL devices listed in devices are not in the list of devices associated with context. * CL_INVALID_BINARY if an invalid program binary was encountered for any device. binaryStatus will return specific status for each device. * CL_OUT_OF_HOST_MEMORY if there is a failure to allocate resources required by the OpenCL implementation on the host. */ Program( const Context& context, const vector& devices, const Binaries& binaries, vector* binaryStatus = nullptr, cl_int* err = nullptr) { cl_int error; const size_type numDevices = devices.size(); // Catch size mismatch early and return if(binaries.size() != numDevices) { error = CL_INVALID_VALUE; detail::errHandler(error, __CREATE_PROGRAM_WITH_BINARY_ERR); if (err != nullptr) { *err = error; } return; } vector lengths(numDevices); vector images(numDevices); #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) for (size_type i = 0; i < numDevices; ++i) { images[i] = binaries[i].data(); lengths[i] = binaries[(int)i].size(); } #else // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) for (size_type i = 0; i < numDevices; ++i) { images[i] = (const unsigned char*)binaries[i].first; lengths[i] = binaries[(int)i].second; } #endif // #if !defined(CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY) vector deviceIDs(numDevices); for( size_type deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex ) { deviceIDs[deviceIndex] = (devices[deviceIndex])(); } if(binaryStatus) { binaryStatus->resize(numDevices); } object_ = ::clCreateProgramWithBinary( context(), (cl_uint) devices.size(), deviceIDs.data(), lengths.data(), images.data(), (binaryStatus != nullptr && numDevices > 0) ? &binaryStatus->front() : nullptr, &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_BINARY_ERR); if (err != nullptr) { *err = error; } } #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * Create program using builtin kernels. * \param kernelNames Semi-colon separated list of builtin kernel names */ Program( const Context& context, const vector& devices, const string& kernelNames, cl_int* err = nullptr) { cl_int error; size_type numDevices = devices.size(); vector deviceIDs(numDevices); for( size_type deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex ) { deviceIDs[deviceIndex] = (devices[deviceIndex])(); } object_ = ::clCreateProgramWithBuiltInKernels( context(), (cl_uint) devices.size(), deviceIDs.data(), kernelNames.c_str(), &error); detail::errHandler(error, __CREATE_PROGRAM_WITH_BUILT_IN_KERNELS_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 Program() { } /*! \brief Constructor from cl_program - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. */ explicit Program(const cl_program& program, bool retainObject = false) : detail::Wrapper(program, retainObject) { } Program& operator = (const cl_program& rhs) { detail::Wrapper::operator=(rhs); return *this; } cl_int build( const vector& devices, const char* options = nullptr, void (CL_CALLBACK * notifyFptr)(cl_program, void *) = nullptr, void* data = nullptr) const { size_type numDevices = devices.size(); vector deviceIDs(numDevices); for( size_type deviceIndex = 0; deviceIndex < numDevices; ++deviceIndex ) { deviceIDs[deviceIndex] = (devices[deviceIndex])(); } cl_int buildError = ::clBuildProgram( object_, (cl_uint) devices.size(), deviceIDs.data(), options, notifyFptr, data); return detail::buildErrHandler(buildError, __BUILD_PROGRAM_ERR, getBuildInfo()); } cl_int build( const Device& device, const char* options = nullptr, void (CL_CALLBACK * notifyFptr)(cl_program, void *) = nullptr, void* data = nullptr) const { cl_device_id deviceID = device(); cl_int buildError = ::clBuildProgram( object_, 1, &deviceID, options, notifyFptr, data); BuildLogType buildLog(0); buildLog.push_back(std::make_pair(device, getBuildInfo(device))); return detail::buildErrHandler(buildError, __BUILD_PROGRAM_ERR, buildLog); } cl_int build( const char* options = nullptr, void (CL_CALLBACK * notifyFptr)(cl_program, void *) = nullptr, void* data = nullptr) const { cl_int buildError = ::clBuildProgram( object_, 0, nullptr, options, notifyFptr, data); return detail::buildErrHandler(buildError, __BUILD_PROGRAM_ERR, getBuildInfo()); } #if CL_HPP_TARGET_OPENCL_VERSION >= 120 cl_int compile( const char* options = nullptr, void (CL_CALLBACK * notifyFptr)(cl_program, void *) = nullptr, void* data = nullptr) const { cl_int error = ::clCompileProgram( object_, 0, nullptr, options, 0, nullptr, nullptr, notifyFptr, data); return detail::buildErrHandler(error, __COMPILE_PROGRAM_ERR, getBuildInfo()); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 template cl_int getInfo(cl_program_info name, T* param) const { return detail::errHandler( detail::getInfo(::clGetProgramInfo, object_, name, param), __GET_PROGRAM_INFO_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_program_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } template cl_int getBuildInfo( const Device& device, cl_program_build_info name, T* param) const { return detail::errHandler( detail::getInfo( ::clGetProgramBuildInfo, object_, device(), name, param), __GET_PROGRAM_BUILD_INFO_ERR); } template typename detail::param_traits::param_type getBuildInfo(const Device& device, cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_program_build_info, name>::param_type param; cl_int result = getBuildInfo(device, name, ¶m); if (err != nullptr) { *err = result; } return param; } /** * Build info function that returns a vector of device/info pairs for the specified * info type and for all devices in the program. * On an error reading the info for any device, an empty vector of info will be returned. */ template vector::param_type>> getBuildInfo(cl_int *err = nullptr) const { cl_int result = CL_SUCCESS; auto devs = getInfo(&result); vector::param_type>> devInfo; // If there was an initial error from getInfo return the error if (result != CL_SUCCESS) { if (err != nullptr) { *err = result; } return devInfo; } for (const cl::Device &d : devs) { typename detail::param_traits< detail::cl_program_build_info, name>::param_type param; result = getBuildInfo(d, name, ¶m); devInfo.push_back( std::pair::param_type> (d, param)); if (result != CL_SUCCESS) { // On error, leave the loop and return the error code break; } } if (err != nullptr) { *err = result; } if (result != CL_SUCCESS) { devInfo.clear(); } return devInfo; } cl_int createKernels(vector* kernels) { cl_uint numKernels; cl_int err = ::clCreateKernelsInProgram(object_, 0, nullptr, &numKernels); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR); } vector value(numKernels); err = ::clCreateKernelsInProgram( object_, numKernels, value.data(), nullptr); if (err != CL_SUCCESS) { return detail::errHandler(err, __CREATE_KERNELS_IN_PROGRAM_ERR); } if (kernels) { kernels->resize(value.size()); // Assign to param, constructing with retain behaviour // to correctly capture each underlying CL object for (size_type i = 0; i < value.size(); i++) { // We do not need to retain because this kernel is being created // by the runtime (*kernels)[i] = Kernel(value[i], false); } } return CL_SUCCESS; } #if CL_HPP_TARGET_OPENCL_VERSION >= 220 #if defined(CL_USE_DEPRECATED_OPENCL_2_2_APIS) /*! \brief Registers a callback function to be called when destructors for * program scope global variables are complete and before the * program is released. * * Wraps clSetProgramReleaseCallback(). * * Each call to this function registers the specified user callback function * on a callback stack associated with program. The registered user callback * functions are called in the reverse order in which they were registered. */ CL_API_PREFIX__VERSION_2_2_DEPRECATED cl_int setReleaseCallback( void (CL_CALLBACK * pfn_notify)(cl_program program, void * user_data), void * user_data = nullptr) CL_API_SUFFIX__VERSION_2_2_DEPRECATED { return detail::errHandler( ::clSetProgramReleaseCallback( object_, pfn_notify, user_data), __SET_PROGRAM_RELEASE_CALLBACK_ERR); } #endif // #if defined(CL_USE_DEPRECATED_OPENCL_2_2_APIS) /*! \brief Sets a SPIR-V specialization constant. * * Wraps clSetProgramSpecializationConstant(). */ template typename std::enable_if::value, cl_int>::type setSpecializationConstant(cl_uint index, const T &value) { return detail::errHandler( ::clSetProgramSpecializationConstant( object_, index, sizeof(value), &value), __SET_PROGRAM_SPECIALIZATION_CONSTANT_ERR); } /*! \brief Sets a SPIR-V specialization constant. * * Wraps clSetProgramSpecializationConstant(). */ cl_int setSpecializationConstant(cl_uint index, size_type size, const void* value) { return detail::errHandler( ::clSetProgramSpecializationConstant( object_, index, size, value), __SET_PROGRAM_SPECIALIZATION_CONSTANT_ERR); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 220 }; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 inline Program linkProgram( const Program& input1, const Program& input2, const char* options = nullptr, void (CL_CALLBACK * notifyFptr)(cl_program, void *) = nullptr, void* data = nullptr, cl_int* err = nullptr) { cl_int error_local = CL_SUCCESS; cl_program programs[2] = { input1(), input2() }; Context ctx = input1.getInfo(&error_local); if(error_local!=CL_SUCCESS) { detail::errHandler(error_local, __LINK_PROGRAM_ERR); } cl_program prog = ::clLinkProgram( ctx(), 0, nullptr, options, 2, programs, notifyFptr, data, &error_local); detail::errHandler(error_local,__COMPILE_PROGRAM_ERR); if (err != nullptr) { *err = error_local; } return Program(prog); } inline Program linkProgram( const vector& inputPrograms, const char* options = nullptr, void (CL_CALLBACK * notifyFptr)(cl_program, void *) = nullptr, void* data = nullptr, cl_int* err = nullptr) { cl_int error_local = CL_SUCCESS; Context ctx; static_assert(sizeof(cl::Program) == sizeof(cl_program), "Size of cl::Program must be equal to size of cl_program"); if(inputPrograms.size() > 0) { ctx = inputPrograms[0].getInfo(&error_local); if(error_local!=CL_SUCCESS) { detail::errHandler(error_local, __LINK_PROGRAM_ERR); } } cl_program prog = ::clLinkProgram( ctx(), 0, nullptr, options, static_cast(inputPrograms.size()), reinterpret_cast(inputPrograms.data()), notifyFptr, data, &error_local); detail::errHandler(error_local,__COMPILE_PROGRAM_ERR); if (err != nullptr) { *err = error_local; } return Program(prog); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 // Template specialization for CL_PROGRAM_BINARIES template <> inline cl_int cl::Program::getInfo(cl_program_info name, vector>* param) const { if (name != CL_PROGRAM_BINARIES) { return CL_INVALID_VALUE; } if (param) { // Resize the parameter array appropriately for each allocation // and pass down to the helper vector sizes = getInfo(); size_type numBinaries = sizes.size(); // Resize the parameter array and constituent arrays param->resize(numBinaries); for (size_type i = 0; i < numBinaries; ++i) { (*param)[i].resize(sizes[i]); } return detail::errHandler( detail::getInfo(::clGetProgramInfo, object_, name, param), __GET_PROGRAM_INFO_ERR); } return CL_SUCCESS; } template<> inline vector> cl::Program::getInfo(cl_int* err) const { vector> binariesVectors; cl_int result = getInfo(CL_PROGRAM_BINARIES, &binariesVectors); if (err != nullptr) { *err = result; } return binariesVectors; } #if CL_HPP_TARGET_OPENCL_VERSION >= 220 // Template specialization for clSetProgramSpecializationConstant template <> inline cl_int cl::Program::setSpecializationConstant(cl_uint index, const bool &value) { cl_uchar ucValue = value ? CL_UCHAR_MAX : 0; return detail::errHandler( ::clSetProgramSpecializationConstant( object_, index, sizeof(ucValue), &ucValue), __SET_PROGRAM_SPECIALIZATION_CONSTANT_ERR); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 220 inline Kernel::Kernel(const Program& program, const char* name, cl_int* err) { cl_int error; object_ = ::clCreateKernel(program(), name, &error); detail::errHandler(error, __CREATE_KERNEL_ERR); if (err != nullptr) { *err = error; } } #ifdef cl_khr_external_memory enum class ExternalMemoryType : cl_external_memory_handle_type_khr { None = 0, OpaqueFd = CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_FD_KHR, OpaqueWin32 = CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KHR, OpaqueWin32Kmt = CL_EXTERNAL_MEMORY_HANDLE_OPAQUE_WIN32_KMT_KHR, D3D11Texture = CL_EXTERNAL_MEMORY_HANDLE_D3D11_TEXTURE_KHR, D3D11TextureKmt = CL_EXTERNAL_MEMORY_HANDLE_D3D11_TEXTURE_KMT_KHR, D3D12Heap = CL_EXTERNAL_MEMORY_HANDLE_D3D12_HEAP_KHR, D3D12Resource = CL_EXTERNAL_MEMORY_HANDLE_D3D12_RESOURCE_KHR, DmaBuf = CL_EXTERNAL_MEMORY_HANDLE_DMA_BUF_KHR, }; #endif enum class QueueProperties : cl_command_queue_properties { None = 0, Profiling = CL_QUEUE_PROFILING_ENABLE, OutOfOrder = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, }; inline QueueProperties operator|(QueueProperties lhs, QueueProperties rhs) { return static_cast(static_cast(lhs) | static_cast(rhs)); } inline QueueProperties operator&(QueueProperties lhs, QueueProperties rhs) { return static_cast(static_cast(lhs) & static_cast(rhs)); } /*! \class CommandQueue * \brief CommandQueue interface for cl_command_queue. */ class CommandQueue : public detail::Wrapper { private: static std::once_flag default_initialized_; static CommandQueue default_; static cl_int default_error_; /*! \brief Create the default command queue returned by @ref getDefault. * * It sets default_error_ to indicate success or failure. It does not throw * @c cl::Error. */ static void makeDefault() { /* We don't want to throw an error from this function, so we have to * catch and set the error flag. */ #if defined(CL_HPP_ENABLE_EXCEPTIONS) try #endif { int error; Context context = Context::getDefault(&error); if (error != CL_SUCCESS) { default_error_ = error; } else { Device device = Device::getDefault(); default_ = CommandQueue(context, device, 0, &default_error_); } } #if defined(CL_HPP_ENABLE_EXCEPTIONS) catch (cl::Error &e) { default_error_ = e.err(); } #endif } /*! \brief Create the default command queue. * * This sets @c default_. It does not throw * @c cl::Error. */ static void makeDefaultProvided(const CommandQueue &c) { default_ = c; } #ifdef cl_khr_external_memory static std::once_flag ext_memory_initialized_; static void initMemoryExtension(const cl::Device& device) { auto platform = device.getInfo(); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clEnqueueAcquireExternalMemObjectsKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clEnqueueReleaseExternalMemObjectsKHR); if ((pfn_clEnqueueAcquireExternalMemObjectsKHR == nullptr) && (pfn_clEnqueueReleaseExternalMemObjectsKHR == nullptr)) { detail::errHandler(CL_INVALID_VALUE, __ENQUEUE_ACQUIRE_EXTERNAL_MEMORY_ERR); } } #endif // cl_khr_external_memory public: #ifdef CL_HPP_UNIT_TEST_ENABLE /*! \brief Reset the default. * * This sets @c default_ to an empty value to support cleanup in * the unit test framework. * This function is not thread safe. */ static void unitTestClearDefault() { default_ = CommandQueue(); } #endif // #ifdef CL_HPP_UNIT_TEST_ENABLE /*! * \brief Constructs a CommandQueue based on passed properties. * Will return an CL_INVALID_QUEUE_PROPERTIES error if CL_QUEUE_ON_DEVICE is specified. */ CommandQueue( cl_command_queue_properties properties, cl_int* err = nullptr) { cl_int error; Context context = Context::getDefault(&error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (error != CL_SUCCESS) { if (err != nullptr) { *err = error; } } else { Device device = context.getInfo()[0]; bool useWithProperties; #if CL_HPP_TARGET_OPENCL_VERSION >= 200 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useWithProperties = (version >= 0x20000); // OpenCL 2.0 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 200 useWithProperties = true; #else useWithProperties = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 200 if (useWithProperties) { cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, properties, 0 }; if ((properties & CL_QUEUE_ON_DEVICE) == 0) { object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); } else { error = CL_INVALID_QUEUE_PROPERTIES; } detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_MINIMUM_OPENCL_VERSION < 200 if (!useWithProperties) { object_ = ::clCreateCommandQueue( context(), device(), properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 } } /*! * \brief Constructs a CommandQueue based on passed properties. * Will return an CL_INVALID_QUEUE_PROPERTIES error if CL_QUEUE_ON_DEVICE is specified. */ CommandQueue( QueueProperties properties, cl_int* err = nullptr) { cl_int error; Context context = Context::getDefault(&error); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (error != CL_SUCCESS) { if (err != nullptr) { *err = error; } } else { Device device = context.getInfo()[0]; bool useWithProperties; #if CL_HPP_TARGET_OPENCL_VERSION >= 200 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useWithProperties = (version >= 0x20000); // OpenCL 2.0 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 200 useWithProperties = true; #else useWithProperties = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 200 if (useWithProperties) { cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, static_cast(properties), 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_MINIMUM_OPENCL_VERSION < 200 if (!useWithProperties) { object_ = ::clCreateCommandQueue( context(), device(), static_cast(properties), &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 } } /*! * \brief Constructs a CommandQueue for an implementation defined device in the given context * Will return an CL_INVALID_QUEUE_PROPERTIES error if CL_QUEUE_ON_DEVICE is specified. */ explicit CommandQueue( const Context& context, cl_command_queue_properties properties = 0, cl_int* err = nullptr) { cl_int error; bool useWithProperties; vector devices; error = context.getInfo(CL_CONTEXT_DEVICES, &devices); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (error != CL_SUCCESS) { if (err != nullptr) { *err = error; } return; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useWithProperties = (version >= 0x20000); // OpenCL 2.0 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 200 useWithProperties = true; #else useWithProperties = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 200 if (useWithProperties) { cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, properties, 0 }; if ((properties & CL_QUEUE_ON_DEVICE) == 0) { object_ = ::clCreateCommandQueueWithProperties( context(), devices[0](), queue_properties, &error); } else { error = CL_INVALID_QUEUE_PROPERTIES; } detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_MINIMUM_OPENCL_VERSION < 200 if (!useWithProperties) { object_ = ::clCreateCommandQueue( context(), devices[0](), properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 } /*! * \brief Constructs a CommandQueue for an implementation defined device in the given context * Will return an CL_INVALID_QUEUE_PROPERTIES error if CL_QUEUE_ON_DEVICE is specified. */ explicit CommandQueue( const Context& context, QueueProperties properties, cl_int* err = nullptr) { cl_int error; bool useWithProperties; vector devices; error = context.getInfo(CL_CONTEXT_DEVICES, &devices); detail::errHandler(error, __CREATE_CONTEXT_ERR); if (error != CL_SUCCESS) { if (err != nullptr) { *err = error; } return; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useWithProperties = (version >= 0x20000); // OpenCL 2.0 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 200 useWithProperties = true; #else useWithProperties = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 200 if (useWithProperties) { cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, static_cast(properties), 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), devices[0](), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_MINIMUM_OPENCL_VERSION < 200 if (!useWithProperties) { object_ = ::clCreateCommandQueue( context(), devices[0](), static_cast(properties), &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 } /*! * \brief Constructs a CommandQueue for a passed device and context * Will return an CL_INVALID_QUEUE_PROPERTIES error if CL_QUEUE_ON_DEVICE is specified. */ CommandQueue( const Context& context, const Device& device, cl_command_queue_properties properties = 0, cl_int* err = nullptr) { cl_int error; bool useWithProperties; #if CL_HPP_TARGET_OPENCL_VERSION >= 200 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useWithProperties = (version >= 0x20000); // OpenCL 2.0 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 200 useWithProperties = true; #else useWithProperties = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 200 if (useWithProperties) { cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, properties, 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_MINIMUM_OPENCL_VERSION < 200 if (!useWithProperties) { object_ = ::clCreateCommandQueue( context(), device(), properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 } /*! * \brief Constructs a CommandQueue for a passed device and context * Will return an CL_INVALID_QUEUE_PROPERTIES error if CL_QUEUE_ON_DEVICE is specified. */ CommandQueue( const Context& context, const Device& device, QueueProperties properties, cl_int* err = nullptr) { cl_int error; bool useWithProperties; #if CL_HPP_TARGET_OPENCL_VERSION >= 200 && CL_HPP_MINIMUM_OPENCL_VERSION < 200 // Run-time decision based on the actual platform { cl_uint version = detail::getContextPlatformVersion(context()); useWithProperties = (version >= 0x20000); // OpenCL 2.0 or above } #elif CL_HPP_TARGET_OPENCL_VERSION >= 200 useWithProperties = true; #else useWithProperties = false; #endif #if CL_HPP_TARGET_OPENCL_VERSION >= 200 if (useWithProperties) { cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, static_cast(properties), 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_MINIMUM_OPENCL_VERSION < 200 if (!useWithProperties) { object_ = ::clCreateCommandQueue( context(), device(), static_cast(properties), &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_ERR); if (err != nullptr) { *err = error; } } #endif // CL_HPP_MINIMUM_OPENCL_VERSION < 200 } static CommandQueue getDefault(cl_int * err = nullptr) { std::call_once(default_initialized_, makeDefault); #if CL_HPP_TARGET_OPENCL_VERSION >= 200 detail::errHandler(default_error_, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); #else // CL_HPP_TARGET_OPENCL_VERSION >= 200 detail::errHandler(default_error_, __CREATE_COMMAND_QUEUE_ERR); #endif // CL_HPP_TARGET_OPENCL_VERSION >= 200 if (err != nullptr) { *err = default_error_; } return default_; } /** * Modify the default command queue to be used by * subsequent operations. * Will only set the default if no default was previously created. * @return updated default command queue. * Should be compared to the passed value to ensure that it was updated. */ static CommandQueue setDefault(const CommandQueue &default_queue) { std::call_once(default_initialized_, makeDefaultProvided, std::cref(default_queue)); detail::errHandler(default_error_); return default_; } CommandQueue() { } /*! \brief Constructor from cl_command_queue - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. */ explicit CommandQueue(const cl_command_queue& commandQueue, bool retainObject = false) : detail::Wrapper(commandQueue, retainObject) { } CommandQueue& operator = (const cl_command_queue& rhs) { detail::Wrapper::operator=(rhs); return *this; } template cl_int getInfo(cl_command_queue_info name, T* param) const { return detail::errHandler( detail::getInfo( ::clGetCommandQueueInfo, object_, name, param), __GET_COMMAND_QUEUE_INFO_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_command_queue_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } cl_int enqueueReadBuffer( const Buffer& buffer, cl_bool blocking, size_type offset, size_type size, void* ptr, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueReadBuffer( object_, buffer(), blocking, offset, size, ptr, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_READ_BUFFER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueWriteBuffer( const Buffer& buffer, cl_bool blocking, size_type offset, size_type size, const void* ptr, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueWriteBuffer( object_, buffer(), blocking, offset, size, ptr, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_WRITE_BUFFER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueCopyBuffer( const Buffer& src, const Buffer& dst, size_type src_offset, size_type dst_offset, size_type size, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyBuffer( object_, src(), dst(), src_offset, dst_offset, size, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQEUE_COPY_BUFFER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #if CL_HPP_TARGET_OPENCL_VERSION >= 110 cl_int enqueueReadBufferRect( const Buffer& buffer, cl_bool blocking, const array& buffer_offset, const array& host_offset, const array& region, size_type buffer_row_pitch, size_type buffer_slice_pitch, size_type host_row_pitch, size_type host_slice_pitch, void *ptr, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueReadBufferRect( object_, buffer(), blocking, buffer_offset.data(), host_offset.data(), region.data(), buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_READ_BUFFER_RECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueReadBufferRect( const Buffer& buffer, cl_bool blocking, const array& buffer_offset, const array& host_offset, const array& region, size_type buffer_row_pitch, size_type buffer_slice_pitch, size_type host_row_pitch, size_type host_slice_pitch, void* ptr, const vector* events = nullptr, Event* event = nullptr) const { return enqueueReadBufferRect( buffer, blocking, { buffer_offset[0], buffer_offset[1], 0 }, { host_offset[0], host_offset[1], 0 }, { region[0], region[1], 1 }, buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, events, event); } cl_int enqueueWriteBufferRect( const Buffer& buffer, cl_bool blocking, const array& buffer_offset, const array& host_offset, const array& region, size_type buffer_row_pitch, size_type buffer_slice_pitch, size_type host_row_pitch, size_type host_slice_pitch, const void *ptr, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueWriteBufferRect( object_, buffer(), blocking, buffer_offset.data(), host_offset.data(), region.data(), buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_WRITE_BUFFER_RECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueWriteBufferRect( const Buffer& buffer, cl_bool blocking, const array& buffer_offset, const array& host_offset, const array& region, size_type buffer_row_pitch, size_type buffer_slice_pitch, size_type host_row_pitch, size_type host_slice_pitch, const void* ptr, const vector* events = nullptr, Event* event = nullptr) const { return enqueueWriteBufferRect( buffer, blocking, { buffer_offset[0], buffer_offset[1], 0 }, { host_offset[0], host_offset[1], 0 }, { region[0], region[1], 1 }, buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, events, event); } cl_int enqueueCopyBufferRect( const Buffer& src, const Buffer& dst, const array& src_origin, const array& dst_origin, const array& region, size_type src_row_pitch, size_type src_slice_pitch, size_type dst_row_pitch, size_type dst_slice_pitch, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyBufferRect( object_, src(), dst(), src_origin.data(), dst_origin.data(), region.data(), src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQEUE_COPY_BUFFER_RECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueCopyBufferRect( const Buffer& src, const Buffer& dst, const array& src_origin, const array& dst_origin, const array& region, size_type src_row_pitch, size_type src_slice_pitch, size_type dst_row_pitch, size_type dst_slice_pitch, const vector* events = nullptr, Event* event = nullptr) const { return enqueueCopyBufferRect( src, dst, { src_origin[0], src_origin[1], 0 }, { dst_origin[0], dst_origin[1], 0 }, { region[0], region[1], 1 }, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch, events, event); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * Enqueue a command to fill a buffer object with a pattern * of a given size. The pattern is specified as a vector type. * \tparam PatternType The datatype of the pattern field. * The pattern type must be an accepted OpenCL data type. * \tparam offset Is the offset in bytes into the buffer at * which to start filling. This must be a multiple of * the pattern size. * \tparam size Is the size in bytes of the region to fill. * This must be a multiple of the pattern size. */ template cl_int enqueueFillBuffer( const Buffer& buffer, PatternType pattern, size_type offset, size_type size, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueFillBuffer( object_, buffer(), static_cast(&pattern), sizeof(PatternType), offset, size, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_FILL_BUFFER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 cl_int enqueueReadImage( const Image& image, cl_bool blocking, const array& origin, const array& region, size_type row_pitch, size_type slice_pitch, void* ptr, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueReadImage( object_, image(), blocking, origin.data(), region.data(), row_pitch, slice_pitch, ptr, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_READ_IMAGE_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueReadImage( const Image& image, cl_bool blocking, const array& origin, const array& region, size_type row_pitch, size_type slice_pitch, void* ptr, const vector* events = nullptr, Event* event = nullptr) const { return enqueueReadImage( image, blocking, { origin[0], origin[1], 0 }, { region[0], region[1], 1 }, row_pitch, slice_pitch, ptr, events, event); } cl_int enqueueWriteImage( const Image& image, cl_bool blocking, const array& origin, const array& region, size_type row_pitch, size_type slice_pitch, const void* ptr, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueWriteImage( object_, image(), blocking, origin.data(), region.data(), row_pitch, slice_pitch, ptr, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_WRITE_IMAGE_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueWriteImage( const Image& image, cl_bool blocking, const array& origin, const array& region, size_type row_pitch, size_type slice_pitch, const void* ptr, const vector* events = nullptr, Event* event = nullptr) const { return enqueueWriteImage( image, blocking, { origin[0], origin[1], 0 }, { region[0], region[1], 1 }, row_pitch, slice_pitch, ptr, events, event); } cl_int enqueueCopyImage( const Image& src, const Image& dst, const array& src_origin, const array& dst_origin, const array& region, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyImage( object_, src(), dst(), src_origin.data(), dst_origin.data(), region.data(), (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_COPY_IMAGE_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueCopyImage( const Image& src, const Image& dst, const array& src_origin, const array& dst_origin, const array& region, const vector* events = nullptr, Event* event = nullptr) const { return enqueueCopyImage( src, dst, { src_origin[0], src_origin[1], 0 }, { dst_origin[0], dst_origin[1], 0 }, { region[0], region[1], 1 }, events, event); } #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * Enqueue a command to fill an image object with a specified color. * \param fillColor is the color to use to fill the image. * This is a four component RGBA floating-point, signed integer * or unsigned integer color value if the image channel data * type is an unnormalized signed integer type. */ template typename std::enable_if::value || std::is_same::value || std::is_same::value, cl_int>::type enqueueFillImage( const Image& image, T fillColor, const array& origin, const array& region, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueFillImage( object_, image(), static_cast(&fillColor), origin.data(), region.data(), (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : NULL, (event != NULL) ? &tmp : nullptr), __ENQUEUE_FILL_IMAGE_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueue a command to fill an image object with a specified color. * \param fillColor is the color to use to fill the image. * This is a four component RGBA floating-point, signed integer * or unsigned integer color value if the image channel data * type is an unnormalized signed integer type. */ template typename std::enable_if::value || std::is_same::value || std::is_same::value, cl_int>::type enqueueFillImage( const Image& image, T fillColor, const array& origin, const array& region, const vector* events = nullptr, Event* event = nullptr) const { return enqueueFillImage( image, fillColor, { origin[0], origin[1], 0 }, { region[0], region[1], 1 }, events, event ); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 cl_int enqueueCopyImageToBuffer( const Image& src, const Buffer& dst, const array& src_origin, const array& region, size_type dst_offset, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyImageToBuffer( object_, src(), dst(), src_origin.data(), region.data(), dst_offset, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueCopyImageToBuffer( const Image& src, const Buffer& dst, const array& src_origin, const array& region, size_type dst_offset, const vector* events = nullptr, Event* event = nullptr) const { return enqueueCopyImageToBuffer( src, dst, { src_origin[0], src_origin[1], 0 }, { region[0], region[1], 1 }, dst_offset, events, event); } cl_int enqueueCopyBufferToImage( const Buffer& src, const Image& dst, size_type src_offset, const array& dst_origin, const array& region, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueCopyBufferToImage( object_, src(), dst(), src_offset, dst_origin.data(), region.data(), (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueCopyBufferToImage( const Buffer& src, const Image& dst, size_type src_offset, const array& dst_origin, const array& region, const vector* events = nullptr, Event* event = nullptr) const { return enqueueCopyBufferToImage( src, dst, src_offset, { dst_origin[0], dst_origin[1], 0 }, { region[0], region[1], 1 }, events, event); } void* enqueueMapBuffer( const Buffer& buffer, cl_bool blocking, cl_map_flags flags, size_type offset, size_type size, const vector* events = nullptr, Event* event = nullptr, cl_int* err = nullptr) const { cl_event tmp; cl_int error; void * result = ::clEnqueueMapBuffer( object_, buffer(), blocking, flags, offset, size, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr, &error); detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); if (err != nullptr) { *err = error; } if (event != nullptr && error == CL_SUCCESS) *event = tmp; return result; } void* enqueueMapImage( const Image& image, cl_bool blocking, cl_map_flags flags, const array& origin, const array& region, size_type * row_pitch, size_type * slice_pitch, const vector* events = nullptr, Event* event = nullptr, cl_int* err = nullptr) const { cl_event tmp; cl_int error; void * result = ::clEnqueueMapImage( object_, image(), blocking, flags, origin.data(), region.data(), row_pitch, slice_pitch, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr, &error); detail::errHandler(error, __ENQUEUE_MAP_IMAGE_ERR); if (err != nullptr) { *err = error; } if (event != nullptr && error == CL_SUCCESS) *event = tmp; return result; } void* enqueueMapImage( const Image& image, cl_bool blocking, cl_map_flags flags, const array& origin, const array& region, size_type* row_pitch, size_type* slice_pitch, const vector* events = nullptr, Event* event = nullptr, cl_int* err = nullptr) const { return enqueueMapImage(image, blocking, flags, { origin[0], origin[1], 0 }, { region[0], region[1], 1 }, row_pitch, slice_pitch, events, event, err); } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /** * Enqueues a command that will allow the host to update a region of a coarse-grained SVM buffer. * This variant takes a raw SVM pointer. */ template cl_int enqueueMapSVM( T* ptr, cl_bool blocking, cl_map_flags flags, size_type size, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler(::clEnqueueSVMMap( object_, blocking, flags, static_cast(ptr), size, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_MAP_BUFFER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueues a command that will allow the host to update a region of a coarse-grained SVM buffer. * This variant takes a cl::pointer instance. */ template cl_int enqueueMapSVM( cl::pointer &ptr, cl_bool blocking, cl_map_flags flags, size_type size, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler(::clEnqueueSVMMap( object_, blocking, flags, static_cast(ptr.get()), size, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_MAP_BUFFER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueues a command that will allow the host to update a region of a coarse-grained SVM buffer. * This variant takes a cl::vector instance. */ template cl_int enqueueMapSVM( cl::vector &container, cl_bool blocking, cl_map_flags flags, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler(::clEnqueueSVMMap( object_, blocking, flags, static_cast(container.data()), container.size()*sizeof(T), (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_MAP_BUFFER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 cl_int enqueueUnmapMemObject( const Memory& memory, void* mapped_ptr, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueUnmapMemObject( object_, memory(), mapped_ptr, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /** * Enqueues a command that will release a coarse-grained SVM buffer back to the OpenCL runtime. * This variant takes a raw SVM pointer. */ template cl_int enqueueUnmapSVM( T* ptr, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueSVMUnmap( object_, static_cast(ptr), (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueues a command that will release a coarse-grained SVM buffer back to the OpenCL runtime. * This variant takes a cl::pointer instance. */ template cl_int enqueueUnmapSVM( cl::pointer &ptr, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueSVMUnmap( object_, static_cast(ptr.get()), (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueues a command that will release a coarse-grained SVM buffer back to the OpenCL runtime. * This variant takes a cl::vector instance. */ template cl_int enqueueUnmapSVM( cl::vector &container, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueSVMUnmap( object_, static_cast(container.data()), (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_TARGET_OPENCL_VERSION >= 120 /** * Enqueues a marker command which waits for either a list of events to complete, * or all previously enqueued commands to complete. * * Enqueues a marker command which waits for either a list of events to complete, * or if the list is empty it waits for all commands previously enqueued in command_queue * to complete before it completes. This command returns an event which can be waited on, * i.e. this event can be waited on to insure that all events either in the event_wait_list * or all previously enqueued commands, queued before this command to command_queue, * have completed. */ cl_int enqueueMarkerWithWaitList( const vector *events = nullptr, Event *event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueMarkerWithWaitList( object_, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_MARKER_WAIT_LIST_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * A synchronization point that enqueues a barrier operation. * * Enqueues a barrier command which waits for either a list of events to complete, * or if the list is empty it waits for all commands previously enqueued in command_queue * to complete before it completes. This command blocks command execution, that is, any * following commands enqueued after it do not execute until it completes. This command * returns an event which can be waited on, i.e. this event can be waited on to insure that * all events either in the event_wait_list or all previously enqueued commands, queued * before this command to command_queue, have completed. */ cl_int enqueueBarrierWithWaitList( const vector *events = nullptr, Event *event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueBarrierWithWaitList( object_, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_BARRIER_WAIT_LIST_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueues a command to indicate with which device a set of memory objects * should be associated. */ cl_int enqueueMigrateMemObjects( const vector &memObjects, cl_mem_migration_flags flags, const vector* events = nullptr, Event* event = nullptr ) const { cl_event tmp; vector localMemObjects(memObjects.size()); for( int i = 0; i < (int)memObjects.size(); ++i ) { localMemObjects[i] = memObjects[i](); } cl_int err = detail::errHandler( ::clEnqueueMigrateMemObjects( object_, (cl_uint)memObjects.size(), localMemObjects.data(), flags, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 120 #if CL_HPP_TARGET_OPENCL_VERSION >= 210 /** * Enqueues a command that will allow the host associate ranges within a set of * SVM allocations with a device. * @param sizes - The length from each pointer to migrate. */ template cl_int enqueueMigrateSVM( const cl::vector &svmRawPointers, const cl::vector &sizes, cl_mem_migration_flags flags = 0, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler(::clEnqueueSVMMigrateMem( object_, svmRawPointers.size(), static_cast(svmRawPointers.data()), sizes.data(), // array of sizes not passed flags, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_MIGRATE_SVM_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * Enqueues a command that will allow the host associate a set of SVM allocations with * a device. */ template cl_int enqueueMigrateSVM( const cl::vector &svmRawPointers, cl_mem_migration_flags flags = 0, const vector* events = nullptr, Event* event = nullptr) const { return enqueueMigrateSVM(svmRawPointers, cl::vector(svmRawPointers.size()), flags, events, event); } /** * Enqueues a command that will allow the host associate ranges within a set of * SVM allocations with a device. * @param sizes - The length from each pointer to migrate. */ template cl_int enqueueMigrateSVM( const cl::vector> &svmPointers, const cl::vector &sizes, cl_mem_migration_flags flags = 0, const vector* events = nullptr, Event* event = nullptr) const { cl::vector svmRawPointers; svmRawPointers.reserve(svmPointers.size()); for (auto p : svmPointers) { svmRawPointers.push_back(static_cast(p.get())); } return enqueueMigrateSVM(svmRawPointers, sizes, flags, events, event); } /** * Enqueues a command that will allow the host associate a set of SVM allocations with * a device. */ template cl_int enqueueMigrateSVM( const cl::vector> &svmPointers, cl_mem_migration_flags flags = 0, const vector* events = nullptr, Event* event = nullptr) const { return enqueueMigrateSVM(svmPointers, cl::vector(svmPointers.size()), flags, events, event); } /** * Enqueues a command that will allow the host associate ranges within a set of * SVM allocations with a device. * @param sizes - The length from the beginning of each container to migrate. */ template cl_int enqueueMigrateSVM( const cl::vector> &svmContainers, const cl::vector &sizes, cl_mem_migration_flags flags = 0, const vector* events = nullptr, Event* event = nullptr) const { cl::vector svmRawPointers; svmRawPointers.reserve(svmContainers.size()); for (auto p : svmContainers) { svmRawPointers.push_back(static_cast(p.data())); } return enqueueMigrateSVM(svmRawPointers, sizes, flags, events, event); } /** * Enqueues a command that will allow the host associate a set of SVM allocations with * a device. */ template cl_int enqueueMigrateSVM( const cl::vector> &svmContainers, cl_mem_migration_flags flags = 0, const vector* events = nullptr, Event* event = nullptr) const { return enqueueMigrateSVM(svmContainers, cl::vector(svmContainers.size()), flags, events, event); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 cl_int enqueueNDRangeKernel( const Kernel& kernel, const NDRange& offset, const NDRange& global, const NDRange& local = NullRange, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueNDRangeKernel( object_, kernel(), (cl_uint) global.dimensions(), offset.dimensions() != 0 ? (const size_type*) offset : nullptr, (const size_type*) global, local.dimensions() != 0 ? (const size_type*) local : nullptr, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_NDRANGE_KERNEL_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #if defined(CL_USE_DEPRECATED_OPENCL_1_2_APIS) CL_API_PREFIX__VERSION_1_2_DEPRECATED cl_int enqueueTask( const Kernel& kernel, const vector* events = nullptr, Event* event = nullptr) const CL_API_SUFFIX__VERSION_1_2_DEPRECATED { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueTask( object_, kernel(), (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_TASK_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #endif // #if defined(CL_USE_DEPRECATED_OPENCL_1_2_APIS) cl_int enqueueNativeKernel( void (CL_CALLBACK *userFptr)(void *), std::pair args, const vector* mem_objects = nullptr, const vector* mem_locs = nullptr, const vector* events = nullptr, Event* event = nullptr) const { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueNativeKernel( object_, userFptr, args.first, args.second, (mem_objects != nullptr) ? (cl_uint) mem_objects->size() : 0, (mem_objects->size() > 0 ) ? reinterpret_cast(mem_objects->data()) : nullptr, (mem_locs != nullptr && mem_locs->size() > 0) ? (const void **) &mem_locs->front() : nullptr, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_NATIVE_KERNEL); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } /** * Deprecated APIs for 1.2 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int enqueueMarker(Event* event = nullptr) const CL_API_SUFFIX__VERSION_1_1_DEPRECATED { cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueMarker( object_, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_MARKER_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int enqueueWaitForEvents(const vector& events) const CL_API_SUFFIX__VERSION_1_1_DEPRECATED { return detail::errHandler( ::clEnqueueWaitForEvents( object_, (cl_uint) events.size(), events.size() > 0 ? (const cl_event*) &events.front() : nullptr), __ENQUEUE_WAIT_FOR_EVENTS_ERR); } #endif // defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) #if defined (CL_HPP_USE_DX_INTEROP) typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clEnqueueAcquireD3D10ObjectsKHR)( cl_command_queue command_queue, cl_uint num_objects, const cl_mem* mem_objects, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); typedef CL_API_ENTRY cl_int (CL_API_CALL *PFN_clEnqueueReleaseD3D10ObjectsKHR)( cl_command_queue command_queue, cl_uint num_objects, const cl_mem* mem_objects, cl_uint num_events_in_wait_list, const cl_event* event_wait_list, cl_event* event); cl_int enqueueAcquireD3D10Objects( const vector* mem_objects = nullptr, const vector* events = nullptr, Event* event = nullptr) const { static PFN_clEnqueueAcquireD3D10ObjectsKHR pfn_clEnqueueAcquireD3D10ObjectsKHR = nullptr; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 cl_context context = getInfo(); cl::Device device(getInfo()); cl_platform_id platform = device.getInfo(); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clEnqueueAcquireD3D10ObjectsKHR); #endif #if CL_HPP_MINIMUM_OPENCL_VERSION < 120 CL_HPP_INIT_CL_EXT_FCN_PTR_(clEnqueueAcquireD3D10ObjectsKHR); #endif cl_event tmp; cl_int err = detail::errHandler( pfn_clEnqueueAcquireD3D10ObjectsKHR( object_, (mem_objects != nullptr) ? (cl_uint) mem_objects->size() : 0, (mem_objects != nullptr && mem_objects->size() > 0) ? (const cl_mem *) &mem_objects->front(): nullptr, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_ACQUIRE_GL_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueReleaseD3D10Objects( const vector* mem_objects = nullptr, const vector* events = nullptr, Event* event = nullptr) const { static PFN_clEnqueueReleaseD3D10ObjectsKHR pfn_clEnqueueReleaseD3D10ObjectsKHR = nullptr; #if CL_HPP_TARGET_OPENCL_VERSION >= 120 cl_context context = getInfo(); cl::Device device(getInfo()); cl_platform_id platform = device.getInfo(); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clEnqueueReleaseD3D10ObjectsKHR); #endif #if CL_HPP_MINIMUM_OPENCL_VERSION < 120 CL_HPP_INIT_CL_EXT_FCN_PTR_(clEnqueueReleaseD3D10ObjectsKHR); #endif cl_event tmp; cl_int err = detail::errHandler( pfn_clEnqueueReleaseD3D10ObjectsKHR( object_, (mem_objects != nullptr) ? (cl_uint) mem_objects->size() : 0, (mem_objects != nullptr && mem_objects->size() > 0) ? (const cl_mem *) &mem_objects->front(): nullptr, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_RELEASE_GL_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #endif /** * Deprecated APIs for 1.2 */ #if defined(CL_USE_DEPRECATED_OPENCL_1_1_APIS) CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int enqueueBarrier() const CL_API_SUFFIX__VERSION_1_1_DEPRECATED { return detail::errHandler( ::clEnqueueBarrier(object_), __ENQUEUE_BARRIER_ERR); } #endif // CL_USE_DEPRECATED_OPENCL_1_1_APIS cl_int flush() const { return detail::errHandler(::clFlush(object_), __FLUSH_ERR); } cl_int finish() const { return detail::errHandler(::clFinish(object_), __FINISH_ERR); } #ifdef cl_khr_external_memory cl_int enqueueAcquireExternalMemObjects( const vector& mem_objects, const vector* events_wait = nullptr, Event *event = nullptr) { cl_int err = CL_INVALID_OPERATION; cl_event tmp; std::call_once(ext_memory_initialized_, initMemoryExtension, this->getInfo()); if (pfn_clEnqueueAcquireExternalMemObjectsKHR) { err = pfn_clEnqueueAcquireExternalMemObjectsKHR( object_, static_cast(mem_objects.size()), (mem_objects.size() > 0) ? reinterpret_cast(mem_objects.data()) : nullptr, (events_wait != nullptr) ? static_cast(events_wait->size()) : 0, (events_wait != nullptr && events_wait->size() > 0) ? reinterpret_cast(events_wait->data()) : nullptr, &tmp); } detail::errHandler(err, __ENQUEUE_ACQUIRE_EXTERNAL_MEMORY_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } cl_int enqueueReleaseExternalMemObjects( const vector& mem_objects, const vector* events_wait = nullptr, Event *event = nullptr) { cl_int err = CL_INVALID_OPERATION; cl_event tmp; std::call_once(ext_memory_initialized_, initMemoryExtension, this->getInfo()); if (pfn_clEnqueueReleaseExternalMemObjectsKHR) { err = pfn_clEnqueueReleaseExternalMemObjectsKHR( object_, static_cast(mem_objects.size()), (mem_objects.size() > 0) ? reinterpret_cast(mem_objects.data()) : nullptr, (events_wait != nullptr) ? static_cast(events_wait->size()) : 0, (events_wait != nullptr && events_wait->size() > 0) ? reinterpret_cast(events_wait->data()) : nullptr, &tmp); } detail::errHandler(err, __ENQUEUE_RELEASE_EXTERNAL_MEMORY_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #endif // cl_khr_external_memory && CL_HPP_TARGET_OPENCL_VERSION >= 300 #ifdef cl_khr_semaphore cl_int enqueueWaitSemaphores( const vector &sema_objects, const vector &sema_payloads = {}, const vector* events_wait_list = nullptr, Event *event = nullptr) const; cl_int enqueueSignalSemaphores( const vector &sema_objects, const vector& sema_payloads = {}, const vector* events_wait_list = nullptr, Event* event = nullptr); #endif // cl_khr_semaphore }; // CommandQueue #ifdef cl_khr_external_memory CL_HPP_DEFINE_STATIC_MEMBER_ std::once_flag CommandQueue::ext_memory_initialized_; #endif CL_HPP_DEFINE_STATIC_MEMBER_ std::once_flag CommandQueue::default_initialized_; CL_HPP_DEFINE_STATIC_MEMBER_ CommandQueue CommandQueue::default_; CL_HPP_DEFINE_STATIC_MEMBER_ cl_int CommandQueue::default_error_ = CL_SUCCESS; #if CL_HPP_TARGET_OPENCL_VERSION >= 200 enum class DeviceQueueProperties : cl_command_queue_properties { None = 0, Profiling = CL_QUEUE_PROFILING_ENABLE, }; inline DeviceQueueProperties operator|(DeviceQueueProperties lhs, DeviceQueueProperties rhs) { return static_cast(static_cast(lhs) | static_cast(rhs)); } /*! \class DeviceCommandQueue * \brief DeviceCommandQueue interface for device cl_command_queues. */ class DeviceCommandQueue : public detail::Wrapper { public: /*! * Trivial empty constructor to create a null queue. */ DeviceCommandQueue() { } /*! * Default construct device command queue on default context and device */ DeviceCommandQueue(DeviceQueueProperties properties, cl_int* err = nullptr) { cl_int error; cl::Context context = cl::Context::getDefault(); cl::Device device = cl::Device::getDefault(); cl_command_queue_properties mergedProperties = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_ON_DEVICE | static_cast(properties); cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, mergedProperties, 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } } /*! * Create a device command queue for a specified device in the passed context. */ DeviceCommandQueue( const Context& context, const Device& device, DeviceQueueProperties properties = DeviceQueueProperties::None, cl_int* err = nullptr) { cl_int error; cl_command_queue_properties mergedProperties = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_ON_DEVICE | static_cast(properties); cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, mergedProperties, 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } } /*! * Create a device command queue for a specified device in the passed context. */ DeviceCommandQueue( const Context& context, const Device& device, cl_uint queueSize, DeviceQueueProperties properties = DeviceQueueProperties::None, cl_int* err = nullptr) { cl_int error; cl_command_queue_properties mergedProperties = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_ON_DEVICE | static_cast(properties); cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, mergedProperties, CL_QUEUE_SIZE, queueSize, 0 }; object_ = ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } } /*! \brief Constructor from cl_command_queue - takes ownership. * * \param retainObject will cause the constructor to retain its cl object. * Defaults to false to maintain compatibility with * earlier versions. */ explicit DeviceCommandQueue(const cl_command_queue& commandQueue, bool retainObject = false) : detail::Wrapper(commandQueue, retainObject) { } DeviceCommandQueue& operator = (const cl_command_queue& rhs) { detail::Wrapper::operator=(rhs); return *this; } template cl_int getInfo(cl_command_queue_info name, T* param) const { return detail::errHandler( detail::getInfo( ::clGetCommandQueueInfo, object_, name, param), __GET_COMMAND_QUEUE_INFO_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_command_queue_info, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } /*! * Create a new default device command queue for the default device, * in the default context and of the default size. * If there is already a default queue for the specified device this * function will return the pre-existing queue. */ static DeviceCommandQueue makeDefault( cl_int *err = nullptr) { cl_int error; cl::Context context = cl::Context::getDefault(); cl::Device device = cl::Device::getDefault(); cl_command_queue_properties properties = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_ON_DEVICE | CL_QUEUE_ON_DEVICE_DEFAULT; cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, properties, 0 }; DeviceCommandQueue deviceQueue( ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error)); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } return deviceQueue; } /*! * Create a new default device command queue for the specified device * and of the default size. * If there is already a default queue for the specified device this * function will return the pre-existing queue. */ static DeviceCommandQueue makeDefault( const Context &context, const Device &device, cl_int *err = nullptr) { cl_int error; cl_command_queue_properties properties = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_ON_DEVICE | CL_QUEUE_ON_DEVICE_DEFAULT; cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, properties, 0 }; DeviceCommandQueue deviceQueue( ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error)); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } return deviceQueue; } /*! * Create a new default device command queue for the specified device * and of the requested size in bytes. * If there is already a default queue for the specified device this * function will return the pre-existing queue. */ static DeviceCommandQueue makeDefault( const Context &context, const Device &device, cl_uint queueSize, cl_int *err = nullptr) { cl_int error; cl_command_queue_properties properties = CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_ON_DEVICE | CL_QUEUE_ON_DEVICE_DEFAULT; cl_queue_properties queue_properties[] = { CL_QUEUE_PROPERTIES, properties, CL_QUEUE_SIZE, queueSize, 0 }; DeviceCommandQueue deviceQueue( ::clCreateCommandQueueWithProperties( context(), device(), queue_properties, &error)); detail::errHandler(error, __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } return deviceQueue; } #if CL_HPP_TARGET_OPENCL_VERSION >= 210 /*! * Modify the default device command queue to be used for subsequent kernels. * This can update the default command queue for a device repeatedly to account * for kernels that rely on the default. * @return updated default device command queue. */ static DeviceCommandQueue updateDefault(const Context &context, const Device &device, const DeviceCommandQueue &default_queue, cl_int *err = nullptr) { cl_int error; error = clSetDefaultDeviceCommandQueue(context.get(), device.get(), default_queue.get()); detail::errHandler(error, __SET_DEFAULT_DEVICE_COMMAND_QUEUE_ERR); if (err != nullptr) { *err = error; } return default_queue; } /*! * Return the current default command queue for the specified command queue */ static DeviceCommandQueue getDefault(const CommandQueue &queue, cl_int * err = nullptr) { return queue.getInfo(err); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 210 }; // DeviceCommandQueue namespace detail { // Specialization for device command queue template <> struct KernelArgumentHandler { static size_type size(const cl::DeviceCommandQueue&) { return sizeof(cl_command_queue); } static const cl_command_queue* ptr(const cl::DeviceCommandQueue& value) { return &(value()); } }; } // namespace detail #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 template< typename IteratorType > Buffer::Buffer( const Context &context, IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr, cl_int* err) { typedef typename std::iterator_traits::value_type DataType; cl_int error; cl_mem_flags flags = 0; if( readOnly ) { flags |= CL_MEM_READ_ONLY; } else { flags |= CL_MEM_READ_WRITE; } if( useHostPtr ) { flags |= CL_MEM_USE_HOST_PTR; } size_type size = sizeof(DataType)*(endIterator - startIterator); if( useHostPtr ) { object_ = ::clCreateBuffer(context(), flags, size, const_cast(&*startIterator), &error); } else { object_ = ::clCreateBuffer(context(), flags, size, 0, &error); } detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } if( !useHostPtr ) { CommandQueue queue(context, 0, &error); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } error = cl::copy(queue, startIterator, endIterator, *this); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } } } template< typename IteratorType > Buffer::Buffer( const CommandQueue &queue, IteratorType startIterator, IteratorType endIterator, bool readOnly, bool useHostPtr, cl_int* err) { typedef typename std::iterator_traits::value_type DataType; cl_int error; cl_mem_flags flags = 0; if (readOnly) { flags |= CL_MEM_READ_ONLY; } else { flags |= CL_MEM_READ_WRITE; } if (useHostPtr) { flags |= CL_MEM_USE_HOST_PTR; } size_type size = sizeof(DataType)*(endIterator - startIterator); Context context = queue.getInfo(); if (useHostPtr) { object_ = ::clCreateBuffer(context(), flags, size, const_cast(&*startIterator), &error); } else { object_ = ::clCreateBuffer(context(), flags, size, 0, &error); } detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } if (!useHostPtr) { error = cl::copy(queue, startIterator, endIterator, *this); detail::errHandler(error, __CREATE_BUFFER_ERR); if (err != nullptr) { *err = error; } } } inline cl_int enqueueReadBuffer( const Buffer& buffer, cl_bool blocking, size_type offset, size_type size, void* ptr, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueReadBuffer(buffer, blocking, offset, size, ptr, events, event); } inline cl_int enqueueWriteBuffer( const Buffer& buffer, cl_bool blocking, size_type offset, size_type size, const void* ptr, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueWriteBuffer(buffer, blocking, offset, size, ptr, events, event); } inline void* enqueueMapBuffer( const Buffer& buffer, cl_bool blocking, cl_map_flags flags, size_type offset, size_type size, const vector* events = nullptr, Event* event = nullptr, cl_int* err = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); if (err != nullptr) { *err = error; } void * result = ::clEnqueueMapBuffer( queue(), buffer(), blocking, flags, offset, size, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (cl_event*) event, &error); detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); if (err != nullptr) { *err = error; } return result; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /** * Enqueues to the default queue a command that will allow the host to * update a region of a coarse-grained SVM buffer. * This variant takes a raw SVM pointer. */ template inline cl_int enqueueMapSVM( T* ptr, cl_bool blocking, cl_map_flags flags, size_type size, const vector* events, Event* event) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); } return queue.enqueueMapSVM( ptr, blocking, flags, size, events, event); } /** * Enqueues to the default queue a command that will allow the host to * update a region of a coarse-grained SVM buffer. * This variant takes a cl::pointer instance. */ template inline cl_int enqueueMapSVM( cl::pointer &ptr, cl_bool blocking, cl_map_flags flags, size_type size, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); } return queue.enqueueMapSVM( ptr, blocking, flags, size, events, event); } /** * Enqueues to the default queue a command that will allow the host to * update a region of a coarse-grained SVM buffer. * This variant takes a cl::vector instance. */ template inline cl_int enqueueMapSVM( cl::vector &container, cl_bool blocking, cl_map_flags flags, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); } return queue.enqueueMapSVM( container, blocking, flags, events, event); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 inline cl_int enqueueUnmapMemObject( const Memory& memory, void* mapped_ptr, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); detail::errHandler(error, __ENQUEUE_MAP_BUFFER_ERR); if (error != CL_SUCCESS) { return error; } cl_event tmp; cl_int err = detail::errHandler( ::clEnqueueUnmapMemObject( queue(), memory(), mapped_ptr, (events != nullptr) ? (cl_uint)events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*)&events->front() : nullptr, (event != nullptr) ? &tmp : nullptr), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /** * Enqueues to the default queue a command that will release a coarse-grained * SVM buffer back to the OpenCL runtime. * This variant takes a raw SVM pointer. */ template inline cl_int enqueueUnmapSVM( T* ptr, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return detail::errHandler(error, __ENQUEUE_UNMAP_MEM_OBJECT_ERR); } return detail::errHandler(queue.enqueueUnmapSVM(ptr, events, event), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); } /** * Enqueues to the default queue a command that will release a coarse-grained * SVM buffer back to the OpenCL runtime. * This variant takes a cl::pointer instance. */ template inline cl_int enqueueUnmapSVM( cl::pointer &ptr, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return detail::errHandler(error, __ENQUEUE_UNMAP_MEM_OBJECT_ERR); } return detail::errHandler(queue.enqueueUnmapSVM(ptr, events, event), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); } /** * Enqueues to the default queue a command that will release a coarse-grained * SVM buffer back to the OpenCL runtime. * This variant takes a cl::vector instance. */ template inline cl_int enqueueUnmapSVM( cl::vector &container, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return detail::errHandler(error, __ENQUEUE_UNMAP_MEM_OBJECT_ERR); } return detail::errHandler(queue.enqueueUnmapSVM(container, events, event), __ENQUEUE_UNMAP_MEM_OBJECT_ERR); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 inline cl_int enqueueCopyBuffer( const Buffer& src, const Buffer& dst, size_type src_offset, size_type dst_offset, size_type size, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyBuffer(src, dst, src_offset, dst_offset, size, events, event); } /** * Blocking copy operation between iterators and a buffer. * Host to Device. * Uses default command queue. */ template< typename IteratorType > inline cl_int copy( IteratorType startIterator, IteratorType endIterator, cl::Buffer &buffer ) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) return error; return cl::copy(queue, startIterator, endIterator, buffer); } /** * Blocking copy operation between iterators and a buffer. * Device to Host. * Uses default command queue. */ template< typename IteratorType > inline cl_int copy( const cl::Buffer &buffer, IteratorType startIterator, IteratorType endIterator ) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) return error; return cl::copy(queue, buffer, startIterator, endIterator); } /** * Blocking copy operation between iterators and a buffer. * Host to Device. * Uses specified queue. */ template< typename IteratorType > inline cl_int copy( const CommandQueue &queue, IteratorType startIterator, IteratorType endIterator, cl::Buffer &buffer ) { typedef typename std::iterator_traits::value_type DataType; cl_int error; size_type length = endIterator-startIterator; size_type byteLength = length*sizeof(DataType); DataType *pointer = static_cast(queue.enqueueMapBuffer(buffer, CL_TRUE, CL_MAP_WRITE, 0, byteLength, 0, 0, &error)); // if exceptions enabled, enqueueMapBuffer will throw if( error != CL_SUCCESS ) { return error; } #if defined(_MSC_VER) std::copy( startIterator, endIterator, stdext::checked_array_iterator( pointer, length)); #else std::copy(startIterator, endIterator, pointer); #endif Event endEvent; error = queue.enqueueUnmapMemObject(buffer, pointer, 0, &endEvent); // if exceptions enabled, enqueueUnmapMemObject will throw if( error != CL_SUCCESS ) { return error; } endEvent.wait(); return CL_SUCCESS; } /** * Blocking copy operation between iterators and a buffer. * Device to Host. * Uses specified queue. */ template< typename IteratorType > inline cl_int copy( const CommandQueue &queue, const cl::Buffer &buffer, IteratorType startIterator, IteratorType endIterator ) { typedef typename std::iterator_traits::value_type DataType; cl_int error; size_type length = endIterator-startIterator; size_type byteLength = length*sizeof(DataType); DataType *pointer = static_cast(queue.enqueueMapBuffer(buffer, CL_TRUE, CL_MAP_READ, 0, byteLength, 0, 0, &error)); // if exceptions enabled, enqueueMapBuffer will throw if( error != CL_SUCCESS ) { return error; } std::copy(pointer, pointer + length, startIterator); Event endEvent; error = queue.enqueueUnmapMemObject(buffer, pointer, 0, &endEvent); // if exceptions enabled, enqueueUnmapMemObject will throw if( error != CL_SUCCESS ) { return error; } endEvent.wait(); return CL_SUCCESS; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 /** * Blocking SVM map operation - performs a blocking map underneath. */ template inline cl_int mapSVM(cl::vector &container) { return enqueueMapSVM(container, CL_TRUE, CL_MAP_READ | CL_MAP_WRITE); } /** * Blocking SVM map operation - performs a blocking map underneath. */ template inline cl_int unmapSVM(cl::vector &container) { return enqueueUnmapSVM(container); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 #if CL_HPP_TARGET_OPENCL_VERSION >= 110 inline cl_int enqueueReadBufferRect( const Buffer& buffer, cl_bool blocking, const array& buffer_offset, const array& host_offset, const array& region, size_type buffer_row_pitch, size_type buffer_slice_pitch, size_type host_row_pitch, size_type host_slice_pitch, void *ptr, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueReadBufferRect( buffer, blocking, buffer_offset, host_offset, region, buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, events, event); } inline cl_int enqueueReadBufferRect( const Buffer& buffer, cl_bool blocking, const array& buffer_offset, const array& host_offset, const array& region, size_type buffer_row_pitch, size_type buffer_slice_pitch, size_type host_row_pitch, size_type host_slice_pitch, void* ptr, const vector* events = nullptr, Event* event = nullptr) { return enqueueReadBufferRect( buffer, blocking, { buffer_offset[0], buffer_offset[1], 0 }, { host_offset[0], host_offset[1], 0 }, { region[0], region[1], 1 }, buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, events, event); } inline cl_int enqueueWriteBufferRect( const Buffer& buffer, cl_bool blocking, const array& buffer_offset, const array& host_offset, const array& region, size_type buffer_row_pitch, size_type buffer_slice_pitch, size_type host_row_pitch, size_type host_slice_pitch, const void *ptr, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueWriteBufferRect( buffer, blocking, buffer_offset, host_offset, region, buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, events, event); } inline cl_int enqueueWriteBufferRect( const Buffer& buffer, cl_bool blocking, const array& buffer_offset, const array& host_offset, const array& region, size_type buffer_row_pitch, size_type buffer_slice_pitch, size_type host_row_pitch, size_type host_slice_pitch, const void* ptr, const vector* events = nullptr, Event* event = nullptr) { return enqueueWriteBufferRect( buffer, blocking, { buffer_offset[0], buffer_offset[1], 0 }, { host_offset[0], host_offset[1], 0 }, { region[0], region[1], 1 }, buffer_row_pitch, buffer_slice_pitch, host_row_pitch, host_slice_pitch, ptr, events, event); } inline cl_int enqueueCopyBufferRect( const Buffer& src, const Buffer& dst, const array& src_origin, const array& dst_origin, const array& region, size_type src_row_pitch, size_type src_slice_pitch, size_type dst_row_pitch, size_type dst_slice_pitch, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyBufferRect( src, dst, src_origin, dst_origin, region, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch, events, event); } inline cl_int enqueueCopyBufferRect( const Buffer& src, const Buffer& dst, const array& src_origin, const array& dst_origin, const array& region, size_type src_row_pitch, size_type src_slice_pitch, size_type dst_row_pitch, size_type dst_slice_pitch, const vector* events = nullptr, Event* event = nullptr) { return enqueueCopyBufferRect( src, dst, { src_origin[0], src_origin[1], 0 }, { dst_origin[0], dst_origin[1], 0 }, { region[0], region[1], 1 }, src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch, events, event); } #endif // CL_HPP_TARGET_OPENCL_VERSION >= 110 inline cl_int enqueueReadImage( const Image& image, cl_bool blocking, const array& origin, const array& region, size_type row_pitch, size_type slice_pitch, void* ptr, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueReadImage( image, blocking, origin, region, row_pitch, slice_pitch, ptr, events, event); } inline cl_int enqueueReadImage( const Image& image, cl_bool blocking, const array& origin, const array& region, size_type row_pitch, size_type slice_pitch, void* ptr, const vector* events = nullptr, Event* event = nullptr) { return enqueueReadImage( image, blocking, { origin[0], origin[1], 0 }, { region[0], region[1], 1 }, row_pitch, slice_pitch, ptr, events, event); } inline cl_int enqueueWriteImage( const Image& image, cl_bool blocking, const array& origin, const array& region, size_type row_pitch, size_type slice_pitch, const void* ptr, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueWriteImage( image, blocking, origin, region, row_pitch, slice_pitch, ptr, events, event); } inline cl_int enqueueWriteImage( const Image& image, cl_bool blocking, const array& origin, const array& region, size_type row_pitch, size_type slice_pitch, const void* ptr, const vector* events = nullptr, Event* event = nullptr) { return enqueueWriteImage( image, blocking, { origin[0], origin[1], 0 }, { region[0], region[1], 1 }, row_pitch, slice_pitch, ptr, events, event); } inline cl_int enqueueCopyImage( const Image& src, const Image& dst, const array& src_origin, const array& dst_origin, const array& region, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyImage( src, dst, src_origin, dst_origin, region, events, event); } inline cl_int enqueueCopyImage( const Image& src, const Image& dst, const array& src_origin, const array& dst_origin, const array& region, const vector* events = nullptr, Event* event = nullptr) { return enqueueCopyImage( src, dst, { src_origin[0], src_origin[1], 0 }, { dst_origin[0], dst_origin[1], 0 }, { region[0], region[1], 1 }, events, event); } inline cl_int enqueueCopyImageToBuffer( const Image& src, const Buffer& dst, const array& src_origin, const array& region, size_type dst_offset, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyImageToBuffer( src, dst, src_origin, region, dst_offset, events, event); } inline cl_int enqueueCopyImageToBuffer( const Image& src, const Buffer& dst, const array& src_origin, const array& region, size_type dst_offset, const vector* events = nullptr, Event* event = nullptr) { return enqueueCopyImageToBuffer( src, dst, { src_origin[0], src_origin[1], 0 }, { region[0], region[1], 1 }, dst_offset, events, event); } inline cl_int enqueueCopyBufferToImage( const Buffer& src, const Image& dst, size_type src_offset, const array& dst_origin, const array& region, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.enqueueCopyBufferToImage( src, dst, src_offset, dst_origin, region, events, event); } inline cl_int enqueueCopyBufferToImage( const Buffer& src, const Image& dst, size_type src_offset, const array& dst_origin, const array& region, const vector* events = nullptr, Event* event = nullptr) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return enqueueCopyBufferToImage( src, dst, src_offset, { dst_origin[0], dst_origin[1], 0 }, { region[0], region[1], 1 }, events, event); } inline cl_int flush(void) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.flush(); } inline cl_int finish(void) { cl_int error; CommandQueue queue = CommandQueue::getDefault(&error); if (error != CL_SUCCESS) { return error; } return queue.finish(); } class EnqueueArgs { private: CommandQueue queue_; const NDRange offset_; const NDRange global_; const NDRange local_; vector events_; template friend class KernelFunctor; public: EnqueueArgs(NDRange global) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(NullRange) { } EnqueueArgs(NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(local) { } EnqueueArgs(NDRange offset, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(offset), global_(global), local_(local) { } EnqueueArgs(Event e, NDRange global) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(NullRange) { events_.push_back(e); } EnqueueArgs(Event e, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(local) { events_.push_back(e); } EnqueueArgs(Event e, NDRange offset, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(offset), global_(global), local_(local) { events_.push_back(e); } EnqueueArgs(const vector &events, NDRange global) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(NullRange), events_(events) { } EnqueueArgs(const vector &events, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(NullRange), global_(global), local_(local), events_(events) { } EnqueueArgs(const vector &events, NDRange offset, NDRange global, NDRange local) : queue_(CommandQueue::getDefault()), offset_(offset), global_(global), local_(local), events_(events) { } EnqueueArgs(CommandQueue &queue, NDRange global) : queue_(queue), offset_(NullRange), global_(global), local_(NullRange) { } EnqueueArgs(CommandQueue &queue, NDRange global, NDRange local) : queue_(queue), offset_(NullRange), global_(global), local_(local) { } EnqueueArgs(CommandQueue &queue, NDRange offset, NDRange global, NDRange local) : queue_(queue), offset_(offset), global_(global), local_(local) { } EnqueueArgs(CommandQueue &queue, Event e, NDRange global) : queue_(queue), offset_(NullRange), global_(global), local_(NullRange) { events_.push_back(e); } EnqueueArgs(CommandQueue &queue, Event e, NDRange global, NDRange local) : queue_(queue), offset_(NullRange), global_(global), local_(local) { events_.push_back(e); } EnqueueArgs(CommandQueue &queue, Event e, NDRange offset, NDRange global, NDRange local) : queue_(queue), offset_(offset), global_(global), local_(local) { events_.push_back(e); } EnqueueArgs(CommandQueue &queue, const vector &events, NDRange global) : queue_(queue), offset_(NullRange), global_(global), local_(NullRange), events_(events) { } EnqueueArgs(CommandQueue &queue, const vector &events, NDRange global, NDRange local) : queue_(queue), offset_(NullRange), global_(global), local_(local), events_(events) { } EnqueueArgs(CommandQueue &queue, const vector &events, NDRange offset, NDRange global, NDRange local) : queue_(queue), offset_(offset), global_(global), local_(local), events_(events) { } }; //---------------------------------------------------------------------------------------------- /** * Type safe kernel functor. * */ template class KernelFunctor { private: Kernel kernel_; template void setArgs(T0&& t0, T1s&&... t1s) { kernel_.setArg(index, t0); setArgs(std::forward(t1s)...); } template void setArgs(T0&& t0) { kernel_.setArg(index, t0); } template void setArgs() { } public: KernelFunctor(Kernel kernel) : kernel_(kernel) {} KernelFunctor( const Program& program, const string name, cl_int * err = nullptr) : kernel_(program, name.c_str(), err) {} //! \brief Return type of the functor typedef Event result_type; /** * Enqueue kernel. * @param args Launch parameters of the kernel. * @param t0... List of kernel arguments based on the template type of the functor. */ Event operator() ( const EnqueueArgs& args, Ts... ts) { Event event; setArgs<0>(std::forward(ts)...); args.queue_.enqueueNDRangeKernel( kernel_, args.offset_, args.global_, args.local_, &args.events_, &event); return event; } /** * Enqueue kernel with support for error code. * @param args Launch parameters of the kernel. * @param t0... List of kernel arguments based on the template type of the functor. * @param error Out parameter returning the error code from the execution. */ Event operator() ( const EnqueueArgs& args, Ts... ts, cl_int &error) { Event event; setArgs<0>(std::forward(ts)...); error = args.queue_.enqueueNDRangeKernel( kernel_, args.offset_, args.global_, args.local_, &args.events_, &event); return event; } #if CL_HPP_TARGET_OPENCL_VERSION >= 200 cl_int setSVMPointers(const vector &pointerList) { return kernel_.setSVMPointers(pointerList); } template cl_int setSVMPointers(const T0 &t0, T1s &... ts) { return kernel_.setSVMPointers(t0, ts...); } #endif // #if CL_HPP_TARGET_OPENCL_VERSION >= 200 Kernel getKernel() { return kernel_; } }; namespace compatibility { /** * Backward compatibility class to ensure that cl.hpp code works with opencl.hpp. * Please use KernelFunctor directly. */ template struct make_kernel { typedef KernelFunctor FunctorType; FunctorType functor_; make_kernel( const Program& program, const string name, cl_int * err = nullptr) : functor_(FunctorType(program, name, err)) {} make_kernel( const Kernel kernel) : functor_(FunctorType(kernel)) {} //! \brief Return type of the functor typedef Event result_type; //! \brief Function signature of kernel functor with no event dependency. typedef Event type_( const EnqueueArgs&, Ts...); Event operator()( const EnqueueArgs& enqueueArgs, Ts... args) { return functor_( enqueueArgs, args...); } }; } // namespace compatibility #ifdef cl_khr_semaphore class Semaphore : public detail::Wrapper { public: Semaphore() : detail::Wrapper() {} Semaphore( const Context &context, const vector& sema_props, cl_int *err = nullptr) { /* initialization of addresses to extension functions (it is done only once) */ std::call_once(ext_init_, initExtensions, context); cl_int error = CL_INVALID_OPERATION; if (pfn_clCreateSemaphoreWithPropertiesKHR) { object_ = pfn_clCreateSemaphoreWithPropertiesKHR( context(), sema_props.data(), &error); } detail::errHandler(error, __CREATE_SEMAPHORE_KHR_WITH_PROPERTIES_ERR); if (err != nullptr) { *err = error; } } Semaphore( const vector& sema_props, cl_int* err = nullptr):Semaphore(Context::getDefault(err), sema_props, err) {} explicit Semaphore(const cl_semaphore_khr& semaphore, bool retainObject = false) : detail::Wrapper(semaphore, retainObject) {} Semaphore& operator = (const cl_semaphore_khr& rhs) { detail::Wrapper::operator=(rhs); return *this; } template cl_int getInfo(cl_semaphore_info_khr name, T* param) const { if (pfn_clGetSemaphoreInfoKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __GET_SEMAPHORE_KHR_INFO_ERR); } return detail::errHandler( detail::getInfo(&pfn_clGetSemaphoreInfoKHR, object_, name, param), __GET_SEMAPHORE_KHR_INFO_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_semaphore_info_khr, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } cl_int retain() { if (pfn_clRetainSemaphoreKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __RETAIN_SEMAPHORE_KHR_ERR); } return pfn_clRetainSemaphoreKHR(object_); } cl_int release() { if (pfn_clReleaseSemaphoreKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __RELEASE_SEMAPHORE_KHR_ERR); } return pfn_clReleaseSemaphoreKHR(object_); } private: static std::once_flag ext_init_; static void initExtensions(const Context& context) { #if CL_HPP_TARGET_OPENCL_VERSION >= 120 Device device = context.getInfo().at(0); cl_platform_id platform = device.getInfo(); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCreateSemaphoreWithPropertiesKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clReleaseSemaphoreKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clRetainSemaphoreKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clEnqueueWaitSemaphoresKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clEnqueueSignalSemaphoresKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clGetSemaphoreInfoKHR); #else CL_HPP_INIT_CL_EXT_FCN_PTR_(clCreateSemaphoreWithPropertiesKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clReleaseSemaphoreKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clRetainSemaphoreKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clEnqueueWaitSemaphoresKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clEnqueueSignalSemaphoresKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clGetSemaphoreInfoKHR); #endif if ((pfn_clCreateSemaphoreWithPropertiesKHR == nullptr) && (pfn_clReleaseSemaphoreKHR == nullptr) && (pfn_clRetainSemaphoreKHR == nullptr) && (pfn_clEnqueueWaitSemaphoresKHR == nullptr) && (pfn_clEnqueueSignalSemaphoresKHR == nullptr) && (pfn_clGetSemaphoreInfoKHR == nullptr)) { detail::errHandler(CL_INVALID_VALUE, __CREATE_SEMAPHORE_KHR_WITH_PROPERTIES_ERR); } } }; CL_HPP_DEFINE_STATIC_MEMBER_ std::once_flag Semaphore::ext_init_; inline cl_int CommandQueue::enqueueWaitSemaphores( const vector &sema_objects, const vector &sema_payloads, const vector* events_wait_list, Event *event) const { cl_event tmp; cl_int err = CL_INVALID_OPERATION; if (pfn_clEnqueueWaitSemaphoresKHR != nullptr) { err = pfn_clEnqueueWaitSemaphoresKHR( object_, (cl_uint)sema_objects.size(), (const cl_semaphore_khr *) &sema_objects.front(), (sema_payloads.size() > 0) ? &sema_payloads.front() : nullptr, (events_wait_list != nullptr) ? (cl_uint) events_wait_list->size() : 0, (events_wait_list != nullptr && events_wait_list->size() > 0) ? (cl_event*) &events_wait_list->front() : nullptr, (event != nullptr) ? &tmp : nullptr); } detail::errHandler(err, __ENQUEUE_WAIT_SEMAPHORE_KHR_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } inline cl_int CommandQueue::enqueueSignalSemaphores( const vector &sema_objects, const vector& sema_payloads, const vector* events_wait_list, Event* event) { cl_event tmp; cl_int err = CL_INVALID_OPERATION; if (pfn_clEnqueueSignalSemaphoresKHR != nullptr) { err = pfn_clEnqueueSignalSemaphoresKHR( object_, (cl_uint)sema_objects.size(), (const cl_semaphore_khr*) &sema_objects.front(), (sema_payloads.size() > 0) ? &sema_payloads.front() : nullptr, (events_wait_list != nullptr) ? (cl_uint) events_wait_list->size() : 0, (events_wait_list != nullptr && events_wait_list->size() > 0) ? (cl_event*) &events_wait_list->front() : nullptr, (event != nullptr) ? &tmp : nullptr); } detail::errHandler(err, __ENQUEUE_SIGNAL_SEMAPHORE_KHR_ERR); if (event != nullptr && err == CL_SUCCESS) *event = tmp; return err; } #endif // cl_khr_semaphore #if defined(cl_khr_command_buffer) /*! \class CommandBufferKhr * \brief CommandBufferKhr interface for cl_command_buffer_khr. */ class CommandBufferKhr : public detail::Wrapper { public: //! \brief Default constructor - initializes to nullptr. CommandBufferKhr() : detail::Wrapper() { } explicit CommandBufferKhr(const vector &queues, cl_command_buffer_properties_khr properties = 0, cl_int* errcode_ret = nullptr) { cl_command_buffer_properties_khr command_buffer_properties[] = { CL_COMMAND_BUFFER_FLAGS_KHR, properties, 0 }; /* initialization of addresses to extension functions (it is done only once) */ std::call_once(ext_init_, [&] { initExtensions(queues[0].getInfo()); }); cl_int error = CL_INVALID_OPERATION; static_assert(sizeof(cl::CommandQueue) == sizeof(cl_command_queue), "Size of cl::CommandQueue must be equal to size of cl_command_queue"); if (pfn_clCreateCommandBufferKHR) { object_ = pfn_clCreateCommandBufferKHR((cl_uint) queues.size(), (cl_command_queue *) &queues.front(), command_buffer_properties, &error); } detail::errHandler(error, __CREATE_COMMAND_BUFFER_KHR_ERR); if (errcode_ret != nullptr) { *errcode_ret = error; } } explicit CommandBufferKhr(const cl_command_buffer_khr& commandBufferKhr, bool retainObject = false) : detail::Wrapper(commandBufferKhr, retainObject) { } CommandBufferKhr& operator=(const cl_command_buffer_khr& rhs) { detail::Wrapper::operator=(rhs); return *this; } template cl_int getInfo(cl_command_buffer_info_khr name, T* param) const { if (pfn_clGetCommandBufferInfoKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __GET_COMMAND_BUFFER_INFO_KHR_ERR); } return detail::errHandler( detail::getInfo(pfn_clGetCommandBufferInfoKHR, object_, name, param), __GET_COMMAND_BUFFER_INFO_KHR_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_command_buffer_info_khr, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } cl_int finalizeCommandBuffer() const { return detail::errHandler(::clFinalizeCommandBufferKHR(object_), __FINALIZE_COMMAND_BUFFER_KHR_ERR); } cl_int enqueueCommandBuffer(vector &queues, const vector* events = nullptr, Event* event = nullptr) { if (pfn_clEnqueueCommandBufferKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __ENQUEUE_COMMAND_BUFFER_KHR_ERR); } static_assert(sizeof(cl::CommandQueue) == sizeof(cl_command_queue), "Size of cl::CommandQueue must be equal to size of cl_command_queue"); return detail::errHandler(pfn_clEnqueueCommandBufferKHR((cl_uint) queues.size(), (cl_command_queue *) &queues.front(), object_, (events != nullptr) ? (cl_uint) events->size() : 0, (events != nullptr && events->size() > 0) ? (cl_event*) &events->front() : nullptr, (cl_event*) event), __ENQUEUE_COMMAND_BUFFER_KHR_ERR); } cl_int commandBarrierWithWaitList(const vector* sync_points_vec = nullptr, cl_sync_point_khr* sync_point = nullptr, MutableCommandKhr* mutable_handle = nullptr, const CommandQueue* command_queue = nullptr) { if (pfn_clCommandBarrierWithWaitListKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __COMMAND_BARRIER_WITH_WAIT_LIST_KHR_ERR); } cl_sync_point_khr tmp_sync_point; cl_int error = detail::errHandler( pfn_clCommandBarrierWithWaitListKHR(object_, (command_queue != nullptr) ? (*command_queue)() : nullptr, (sync_points_vec != nullptr) ? (cl_uint) sync_points_vec->size() : 0, (sync_points_vec != nullptr && sync_points_vec->size() > 0) ? &sync_points_vec->front() : nullptr, (sync_point != nullptr) ? &tmp_sync_point : nullptr, (cl_mutable_command_khr*) mutable_handle), __COMMAND_BARRIER_WITH_WAIT_LIST_KHR_ERR); if (sync_point != nullptr && error == CL_SUCCESS) *sync_point = tmp_sync_point; return error; } cl_int commandCopyBuffer(const Buffer& src, const Buffer& dst, size_type src_offset, size_type dst_offset, size_type size, const vector* sync_points_vec = nullptr, cl_sync_point_khr* sync_point = nullptr, MutableCommandKhr* mutable_handle = nullptr, const CommandQueue* command_queue = nullptr) { if (pfn_clCommandCopyBufferKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __COMMAND_COPY_BUFFER_KHR_ERR); } cl_sync_point_khr tmp_sync_point; cl_int error = detail::errHandler( pfn_clCommandCopyBufferKHR(object_, (command_queue != nullptr) ? (*command_queue)() : nullptr, src(), dst(), src_offset, dst_offset, size, (sync_points_vec != nullptr) ? (cl_uint) sync_points_vec->size() : 0, (sync_points_vec != nullptr && sync_points_vec->size() > 0) ? &sync_points_vec->front() : nullptr, (sync_point != nullptr) ? &tmp_sync_point : nullptr, (cl_mutable_command_khr*) mutable_handle), __COMMAND_COPY_BUFFER_KHR_ERR); if (sync_point != nullptr && error == CL_SUCCESS) *sync_point = tmp_sync_point; return error; } cl_int commandCopyBufferRect(const Buffer& src, const Buffer& dst, const array& src_origin, const array& dst_origin, const array& region, size_type src_row_pitch, size_type src_slice_pitch, size_type dst_row_pitch, size_type dst_slice_pitch, const vector* sync_points_vec = nullptr, cl_sync_point_khr* sync_point = nullptr, MutableCommandKhr* mutable_handle = nullptr, const CommandQueue* command_queue = nullptr) { if (pfn_clCommandCopyBufferRectKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __COMMAND_COPY_BUFFER_RECT_KHR_ERR); } cl_sync_point_khr tmp_sync_point; cl_int error = detail::errHandler( pfn_clCommandCopyBufferRectKHR(object_, (command_queue != nullptr) ? (*command_queue)() : nullptr, src(), dst(), src_origin.data(), dst_origin.data(), region.data(), src_row_pitch, src_slice_pitch, dst_row_pitch, dst_slice_pitch, (sync_points_vec != nullptr) ? (cl_uint) sync_points_vec->size() : 0, (sync_points_vec != nullptr && sync_points_vec->size() > 0) ? &sync_points_vec->front() : nullptr, (sync_point != nullptr) ? &tmp_sync_point : nullptr, (cl_mutable_command_khr*) mutable_handle), __COMMAND_COPY_BUFFER_RECT_KHR_ERR); if (sync_point != nullptr && error == CL_SUCCESS) *sync_point = tmp_sync_point; return error; } cl_int commandCopyBufferToImage(const Buffer& src, const Image& dst, size_type src_offset, const array& dst_origin, const array& region, const vector* sync_points_vec = nullptr, cl_sync_point_khr* sync_point = nullptr, MutableCommandKhr* mutable_handle = nullptr, const CommandQueue* command_queue = nullptr) { if (pfn_clCommandCopyBufferToImageKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __COMMAND_COPY_BUFFER_TO_IMAGE_KHR_ERR); } cl_sync_point_khr tmp_sync_point; cl_int error = detail::errHandler( pfn_clCommandCopyBufferToImageKHR(object_, (command_queue != nullptr) ? (*command_queue)() : nullptr, src(), dst(), src_offset, dst_origin.data(), region.data(), (sync_points_vec != nullptr) ? (cl_uint) sync_points_vec->size() : 0, (sync_points_vec != nullptr && sync_points_vec->size() > 0) ? &sync_points_vec->front() : nullptr, (sync_point != nullptr) ? &tmp_sync_point : nullptr, (cl_mutable_command_khr*) mutable_handle), __COMMAND_COPY_BUFFER_TO_IMAGE_KHR_ERR); if (sync_point != nullptr && error == CL_SUCCESS) *sync_point = tmp_sync_point; return error; } cl_int commandCopyImage(const Image& src, const Image& dst, const array& src_origin, const array& dst_origin, const array& region, const vector* sync_points_vec = nullptr, cl_sync_point_khr* sync_point = nullptr, MutableCommandKhr* mutable_handle = nullptr, const CommandQueue* command_queue = nullptr) { if (pfn_clCommandCopyImageKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __COMMAND_COPY_IMAGE_KHR_ERR); } cl_sync_point_khr tmp_sync_point; cl_int error = detail::errHandler( pfn_clCommandCopyImageKHR(object_, (command_queue != nullptr) ? (*command_queue)() : nullptr, src(), dst(), src_origin.data(), dst_origin.data(), region.data(), (sync_points_vec != nullptr) ? (cl_uint) sync_points_vec->size() : 0, (sync_points_vec != nullptr && sync_points_vec->size() > 0) ? &sync_points_vec->front() : nullptr, (sync_point != nullptr) ? &tmp_sync_point : nullptr, (cl_mutable_command_khr*) mutable_handle), __COMMAND_COPY_IMAGE_KHR_ERR); if (sync_point != nullptr && error == CL_SUCCESS) *sync_point = tmp_sync_point; return error; } cl_int commandCopyImageToBuffer(const Image& src, const Buffer& dst, const array& src_origin, const array& region, size_type dst_offset, const vector* sync_points_vec = nullptr, cl_sync_point_khr* sync_point = nullptr, MutableCommandKhr* mutable_handle = nullptr, const CommandQueue* command_queue = nullptr) { if (pfn_clCommandCopyImageToBufferKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __COMMAND_COPY_IMAGE_TO_BUFFER_KHR_ERR); } cl_sync_point_khr tmp_sync_point; cl_int error = detail::errHandler( pfn_clCommandCopyImageToBufferKHR(object_, (command_queue != nullptr) ? (*command_queue)() : nullptr, src(), dst(), src_origin.data(), region.data(), dst_offset, (sync_points_vec != nullptr) ? (cl_uint) sync_points_vec->size() : 0, (sync_points_vec != nullptr && sync_points_vec->size() > 0) ? &sync_points_vec->front() : nullptr, (sync_point != nullptr) ? &tmp_sync_point : nullptr, (cl_mutable_command_khr*) mutable_handle), __COMMAND_COPY_IMAGE_TO_BUFFER_KHR_ERR); if (sync_point != nullptr && error == CL_SUCCESS) *sync_point = tmp_sync_point; return error; } template cl_int commandFillBuffer(const Buffer& buffer, PatternType pattern, size_type offset, size_type size, const vector* sync_points_vec = nullptr, cl_sync_point_khr* sync_point = nullptr, MutableCommandKhr* mutable_handle = nullptr, const CommandQueue* command_queue = nullptr) { if (pfn_clCommandFillBufferKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __COMMAND_FILL_BUFFER_KHR_ERR); } cl_sync_point_khr tmp_sync_point; cl_int error = detail::errHandler( pfn_clCommandFillBufferKHR(object_, (command_queue != nullptr) ? (*command_queue)() : nullptr, buffer(), static_cast(&pattern), sizeof(PatternType), offset, size, (sync_points_vec != nullptr) ? (cl_uint) sync_points_vec->size() : 0, (sync_points_vec != nullptr && sync_points_vec->size() > 0) ? &sync_points_vec->front() : nullptr, (sync_point != nullptr) ? &tmp_sync_point : nullptr, (cl_mutable_command_khr*) mutable_handle), __COMMAND_FILL_BUFFER_KHR_ERR); if (sync_point != nullptr && error == CL_SUCCESS) *sync_point = tmp_sync_point; return error; } cl_int commandFillImage(const Image& image, cl_float4 fillColor, const array& origin, const array& region, const vector* sync_points_vec = nullptr, cl_sync_point_khr* sync_point = nullptr, MutableCommandKhr* mutable_handle = nullptr, const CommandQueue* command_queue = nullptr) { if (pfn_clCommandFillImageKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __COMMAND_FILL_IMAGE_KHR_ERR); } cl_sync_point_khr tmp_sync_point; cl_int error = detail::errHandler( pfn_clCommandFillImageKHR(object_, (command_queue != nullptr) ? (*command_queue)() : nullptr, image(), static_cast(&fillColor), origin.data(), region.data(), (sync_points_vec != nullptr) ? (cl_uint) sync_points_vec->size() : 0, (sync_points_vec != nullptr && sync_points_vec->size() > 0) ? &sync_points_vec->front() : nullptr, (sync_point != nullptr) ? &tmp_sync_point : nullptr, (cl_mutable_command_khr*) mutable_handle), __COMMAND_FILL_IMAGE_KHR_ERR); if (sync_point != nullptr && error == CL_SUCCESS) *sync_point = tmp_sync_point; return error; } cl_int commandNDRangeKernel(const cl::vector &properties, const Kernel& kernel, const NDRange& offset, const NDRange& global, const NDRange& local = NullRange, const vector* sync_points_vec = nullptr, cl_sync_point_khr* sync_point = nullptr, MutableCommandKhr* mutable_handle = nullptr, const CommandQueue* command_queue = nullptr) { if (pfn_clCommandNDRangeKernelKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __COMMAND_NDRANGE_KERNEL_KHR_ERR); } cl_sync_point_khr tmp_sync_point; cl_int error = detail::errHandler( pfn_clCommandNDRangeKernelKHR(object_, (command_queue != nullptr) ? (*command_queue)() : nullptr, &properties[0], kernel(), (cl_uint) global.dimensions(), offset.dimensions() != 0 ? (const size_type*) offset : nullptr, (const size_type*) global, local.dimensions() != 0 ? (const size_type*) local : nullptr, (sync_points_vec != nullptr) ? (cl_uint) sync_points_vec->size() : 0, (sync_points_vec != nullptr && sync_points_vec->size() > 0) ? &sync_points_vec->front() : nullptr, (sync_point != nullptr) ? &tmp_sync_point : nullptr, (cl_mutable_command_khr*) mutable_handle), __COMMAND_NDRANGE_KERNEL_KHR_ERR); if (sync_point != nullptr && error == CL_SUCCESS) *sync_point = tmp_sync_point; return error; } #if defined(cl_khr_command_buffer_mutable_dispatch) cl_int updateMutableCommands(const cl_mutable_base_config_khr* mutable_config) { if (pfn_clUpdateMutableCommandsKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __UPDATE_MUTABLE_COMMANDS_KHR_ERR); } return detail::errHandler(pfn_clUpdateMutableCommandsKHR(object_, mutable_config), __UPDATE_MUTABLE_COMMANDS_KHR_ERR); } #endif /* cl_khr_command_buffer_mutable_dispatch */ private: static std::once_flag ext_init_; static void initExtensions(const cl::Device& device) { #if CL_HPP_TARGET_OPENCL_VERSION >= 120 cl_platform_id platform = device.getInfo(); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCreateCommandBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clFinalizeCommandBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clRetainCommandBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clReleaseCommandBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clGetCommandBufferInfoKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clEnqueueCommandBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCommandBarrierWithWaitListKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCommandCopyBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCommandCopyBufferRectKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCommandCopyBufferToImageKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCommandCopyImageKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCommandCopyImageToBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCommandFillBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCommandFillImageKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clCommandNDRangeKernelKHR); #if defined(cl_khr_command_buffer_mutable_dispatch) CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clUpdateMutableCommandsKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_(platform, clGetMutableCommandInfoKHR); #endif /* cl_khr_command_buffer_mutable_dispatch */ #elif CL_HPP_TARGET_OPENCL_VERSION >= 110 CL_HPP_INIT_CL_EXT_FCN_PTR_(clCreateCommandBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clFinalizeCommandBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clRetainCommandBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clReleaseCommandBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clGetCommandBufferInfoKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clEnqueueCommandBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clCommandBarrierWithWaitListKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clCommandCopyBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clCommandCopyBufferRectKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clCommandCopyBufferToImageKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clCommandCopyImageKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clCommandCopyImageToBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clCommandFillBufferKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clCommandFillImageKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clCommandNDRangeKernelKHR); #if defined(cl_khr_command_buffer_mutable_dispatch) CL_HPP_INIT_CL_EXT_FCN_PTR_(clUpdateMutableCommandsKHR); CL_HPP_INIT_CL_EXT_FCN_PTR_(clGetMutableCommandInfoKHR); #endif /* cl_khr_command_buffer_mutable_dispatch */ #endif if ((pfn_clCreateCommandBufferKHR == nullptr) && (pfn_clFinalizeCommandBufferKHR == nullptr) && (pfn_clRetainCommandBufferKHR == nullptr) && (pfn_clReleaseCommandBufferKHR == nullptr) && (pfn_clGetCommandBufferInfoKHR == nullptr) && (pfn_clEnqueueCommandBufferKHR == nullptr) && (pfn_clCommandBarrierWithWaitListKHR == nullptr) && (pfn_clCommandCopyBufferKHR == nullptr) && (pfn_clCommandCopyBufferRectKHR == nullptr) && (pfn_clCommandCopyBufferToImageKHR == nullptr) && (pfn_clCommandCopyImageKHR == nullptr) && (pfn_clCommandCopyImageToBufferKHR == nullptr) && (pfn_clCommandFillBufferKHR == nullptr) && (pfn_clCommandFillImageKHR == nullptr) && (pfn_clCommandNDRangeKernelKHR == nullptr) #if defined(cl_khr_command_buffer_mutable_dispatch) && (pfn_clUpdateMutableCommandsKHR == nullptr) && (pfn_clGetMutableCommandInfoKHR == nullptr) #endif /* cl_khr_command_buffer_mutable_dispatch */ ) { detail::errHandler(CL_INVALID_VALUE, __CREATE_COMMAND_BUFFER_KHR_ERR); } } }; // CommandBufferKhr CL_HPP_DEFINE_STATIC_MEMBER_ std::once_flag CommandBufferKhr::ext_init_; #if defined(cl_khr_command_buffer_mutable_dispatch) /*! \class MutableCommandKhr * \brief MutableCommandKhr interface for cl_mutable_command_khr. */ class MutableCommandKhr : public detail::Wrapper { public: //! \brief Default constructor - initializes to nullptr. MutableCommandKhr() : detail::Wrapper() { } explicit MutableCommandKhr(const cl_mutable_command_khr& mutableCommandKhr, bool retainObject = false) : detail::Wrapper(mutableCommandKhr, retainObject) { } MutableCommandKhr& operator=(const cl_mutable_command_khr& rhs) { detail::Wrapper::operator=(rhs); return *this; } template cl_int getInfo(cl_mutable_command_info_khr name, T* param) const { if (pfn_clGetMutableCommandInfoKHR == nullptr) { return detail::errHandler(CL_INVALID_OPERATION, __GET_MUTABLE_COMMAND_INFO_KHR_ERR); } return detail::errHandler( detail::getInfo(pfn_clGetMutableCommandInfoKHR, object_, name, param), __GET_MUTABLE_COMMAND_INFO_KHR_ERR); } template typename detail::param_traits::param_type getInfo(cl_int* err = nullptr) const { typename detail::param_traits< detail::cl_mutable_command_info_khr, name>::param_type param; cl_int result = getInfo(name, ¶m); if (err != nullptr) { *err = result; } return param; } }; // MutableCommandKhr #endif /* cl_khr_command_buffer_mutable_dispatch */ #endif // cl_khr_command_buffer //---------------------------------------------------------------------------------------------------------------------- #undef CL_HPP_ERR_STR_ #if !defined(CL_HPP_USER_OVERRIDE_ERROR_STRINGS) #undef __GET_DEVICE_INFO_ERR #undef __GET_PLATFORM_INFO_ERR #undef __GET_DEVICE_IDS_ERR #undef __GET_PLATFORM_IDS_ERR #undef __GET_CONTEXT_INFO_ERR #undef __GET_EVENT_INFO_ERR #undef __GET_EVENT_PROFILE_INFO_ERR #undef __GET_MEM_OBJECT_INFO_ERR #undef __GET_IMAGE_INFO_ERR #undef __GET_SAMPLER_INFO_ERR #undef __GET_KERNEL_INFO_ERR #undef __GET_KERNEL_ARG_INFO_ERR #undef __GET_KERNEL_SUB_GROUP_INFO_ERR #undef __GET_KERNEL_WORK_GROUP_INFO_ERR #undef __GET_PROGRAM_INFO_ERR #undef __GET_PROGRAM_BUILD_INFO_ERR #undef __GET_COMMAND_QUEUE_INFO_ERR #undef __CREATE_CONTEXT_ERR #undef __CREATE_CONTEXT_FROM_TYPE_ERR #undef __CREATE_COMMAND_BUFFER_KHR_ERR #undef __GET_COMMAND_BUFFER_INFO_KHR_ERR #undef __FINALIZE_COMMAND_BUFFER_KHR_ERR #undef __ENQUEUE_COMMAND_BUFFER_KHR_ERR #undef __COMMAND_BARRIER_WITH_WAIT_LIST_KHR_ERR #undef __COMMAND_COPY_BUFFER_KHR_ERR #undef __COMMAND_COPY_BUFFER_RECT_KHR_ERR #undef __COMMAND_COPY_BUFFER_TO_IMAGE_KHR_ERR #undef __COMMAND_COPY_IMAGE_KHR_ERR #undef __COMMAND_COPY_IMAGE_TO_BUFFER_KHR_ERR #undef __COMMAND_FILL_BUFFER_KHR_ERR #undef __COMMAND_FILL_IMAGE_KHR_ERR #undef __COMMAND_NDRANGE_KERNEL_KHR_ERR #undef __UPDATE_MUTABLE_COMMANDS_KHR_ERR #undef __GET_MUTABLE_COMMAND_INFO_KHR_ERR #undef __RETAIN_COMMAND_BUFFER_KHR_ERR #undef __RELEASE_COMMAND_BUFFER_KHR_ERR #undef __GET_SUPPORTED_IMAGE_FORMATS_ERR #undef __SET_CONTEXT_DESCTRUCTOR_CALLBACK_ERR #undef __CREATE_BUFFER_ERR #undef __COPY_ERR #undef __CREATE_SUBBUFFER_ERR #undef __CREATE_GL_BUFFER_ERR #undef __CREATE_GL_RENDER_BUFFER_ERR #undef __GET_GL_OBJECT_INFO_ERR #undef __CREATE_IMAGE_ERR #undef __CREATE_GL_TEXTURE_ERR #undef __IMAGE_DIMENSION_ERR #undef __SET_MEM_OBJECT_DESTRUCTOR_CALLBACK_ERR #undef __CREATE_USER_EVENT_ERR #undef __SET_USER_EVENT_STATUS_ERR #undef __SET_EVENT_CALLBACK_ERR #undef __WAIT_FOR_EVENTS_ERR #undef __CREATE_KERNEL_ERR #undef __SET_KERNEL_ARGS_ERR #undef __CREATE_PROGRAM_WITH_SOURCE_ERR #undef __CREATE_PROGRAM_WITH_BINARY_ERR #undef __CREATE_PROGRAM_WITH_IL_ERR #undef __CREATE_PROGRAM_WITH_BUILT_IN_KERNELS_ERR #undef __BUILD_PROGRAM_ERR #undef __COMPILE_PROGRAM_ERR #undef __LINK_PROGRAM_ERR #undef __CREATE_KERNELS_IN_PROGRAM_ERR #undef __CREATE_COMMAND_QUEUE_WITH_PROPERTIES_ERR #undef __CREATE_SAMPLER_WITH_PROPERTIES_ERR #undef __SET_COMMAND_QUEUE_PROPERTY_ERR #undef __ENQUEUE_READ_BUFFER_ERR #undef __ENQUEUE_READ_BUFFER_RECT_ERR #undef __ENQUEUE_WRITE_BUFFER_ERR #undef __ENQUEUE_WRITE_BUFFER_RECT_ERR #undef __ENQEUE_COPY_BUFFER_ERR #undef __ENQEUE_COPY_BUFFER_RECT_ERR #undef __ENQUEUE_FILL_BUFFER_ERR #undef __ENQUEUE_READ_IMAGE_ERR #undef __ENQUEUE_WRITE_IMAGE_ERR #undef __ENQUEUE_COPY_IMAGE_ERR #undef __ENQUEUE_FILL_IMAGE_ERR #undef __ENQUEUE_COPY_IMAGE_TO_BUFFER_ERR #undef __ENQUEUE_COPY_BUFFER_TO_IMAGE_ERR #undef __ENQUEUE_MAP_BUFFER_ERR #undef __ENQUEUE_MAP_IMAGE_ERR #undef __ENQUEUE_UNMAP_MEM_OBJECT_ERR #undef __ENQUEUE_NDRANGE_KERNEL_ERR #undef __ENQUEUE_NATIVE_KERNEL #undef __ENQUEUE_MIGRATE_MEM_OBJECTS_ERR #undef __ENQUEUE_MIGRATE_SVM_ERR #undef __ENQUEUE_ACQUIRE_GL_ERR #undef __ENQUEUE_RELEASE_GL_ERR #undef __CREATE_PIPE_ERR #undef __GET_PIPE_INFO_ERR #undef __RETAIN_ERR #undef __RELEASE_ERR #undef __FLUSH_ERR #undef __FINISH_ERR #undef __VECTOR_CAPACITY_ERR #undef __CREATE_SUB_DEVICES_ERR #undef __ENQUEUE_ACQUIRE_EXTERNAL_MEMORY_ERR #undef __ENQUEUE_RELEASE_EXTERNAL_MEMORY_ERR #undef __ENQUEUE_MARKER_ERR #undef __ENQUEUE_WAIT_FOR_EVENTS_ERR #undef __ENQUEUE_BARRIER_ERR #undef __UNLOAD_COMPILER_ERR #undef __CREATE_GL_TEXTURE_2D_ERR #undef __CREATE_GL_TEXTURE_3D_ERR #undef __CREATE_IMAGE2D_ERR #undef __CREATE_IMAGE3D_ERR #undef __CREATE_COMMAND_QUEUE_ERR #undef __ENQUEUE_TASK_ERR #undef __CREATE_SAMPLER_ERR #undef __ENQUEUE_MARKER_WAIT_LIST_ERR #undef __ENQUEUE_BARRIER_WAIT_LIST_ERR #undef __CLONE_KERNEL_ERR #undef __GET_HOST_TIMER_ERR #undef __GET_DEVICE_AND_HOST_TIMER_ERR #undef __GET_SEMAPHORE_KHR_INFO_ERR #undef __CREATE_SEMAPHORE_KHR_WITH_PROPERTIES_ERR #undef __ENQUEUE_WAIT_SEMAPHORE_KHR_ERR #undef __ENQUEUE_SIGNAL_SEMAPHORE_KHR_ERR #endif //CL_HPP_USER_OVERRIDE_ERROR_STRINGS // Extensions #undef CL_HPP_CREATE_CL_EXT_FCN_PTR_ALIAS_ #undef CL_HPP_INIT_CL_EXT_FCN_PTR_ #undef CL_HPP_INIT_CL_EXT_FCN_PTR_PLATFORM_ #undef CL_HPP_NOEXCEPT_ #undef CL_HPP_DEFINE_STATIC_MEMBER_ } // namespace cl #endif // CL_HPP_ par2cmdline-turbo-1.4.0/parpar/gf16/opencl-include/cl.c000066400000000000000000000122621514221355600225470ustar00rootroot00000000000000#define CL_API_EXTERN #define CL_TARGET_OPENCL_VERSION 120 #define CL_USE_DEPRECATED_OPENCL_1_1_APIS #include "cl.h" static void* handle = NULL; #define LOAD_FN_REQ(n) if(!(LOAD_FN(n))) goto sym_load_failed #ifdef _WIN32 # define WIN32_LEAN_AND_MEAN # include # define LOAD_FN(n) *(void**)&n = GetProcAddress(handle, #n) #elif defined(PARPAR_LIBDL_SUPPORT) # include # define LOAD_FN(n) *(void**)&n = dlsym(handle, #n) #else # define LOAD_FN(n) # undef LOAD_FN_REQ # define LOAD_FN_REQ(n) goto sym_load_failed #endif int load_opencl() { if(handle) return 0; // already loaded #ifdef _WIN32 handle = LoadLibrary(TEXT("OpenCL.dll")); #elif defined(PARPAR_LIBDL_SUPPORT) handle = dlopen("libOpenCL.so", RTLD_NOW); if(!handle) handle = dlopen("libOpenCL.so.1", RTLD_NOW); // another common name seen in a number of Linux/BSD distros (DEB, RPM, APK, Arch etc) if(!handle) handle = dlopen("libOpenCL.so.1.0.0", RTLD_NOW); // final fallback #endif if(!handle) return 1; // load functions LOAD_FN_REQ(clGetPlatformIDs); LOAD_FN_REQ(clGetPlatformInfo); LOAD_FN_REQ(clGetDeviceIDs); LOAD_FN_REQ(clGetDeviceInfo); LOAD_FN(clCreateSubDevices); // 1.2 LOAD_FN(clRetainDevice); // 1.2 LOAD_FN(clReleaseDevice); // 1.2 LOAD_FN_REQ(clCreateContext); LOAD_FN_REQ(clCreateContextFromType); LOAD_FN_REQ(clRetainContext); LOAD_FN_REQ(clReleaseContext); LOAD_FN_REQ(clGetContextInfo); //LOAD_FN(clCreateCommandQueueWithProperties); // 2.0 LOAD_FN_REQ(clRetainCommandQueue); LOAD_FN_REQ(clReleaseCommandQueue); LOAD_FN_REQ(clGetCommandQueueInfo); LOAD_FN_REQ(clCreateBuffer); LOAD_FN_REQ(clCreateSubBuffer); // 1.1 LOAD_FN(clCreateImage); // 1.2 //LOAD_FN(clCreatePipe); // 2.0 LOAD_FN_REQ(clRetainMemObject); LOAD_FN_REQ(clReleaseMemObject); LOAD_FN_REQ(clGetSupportedImageFormats); LOAD_FN_REQ(clGetMemObjectInfo); LOAD_FN_REQ(clGetImageInfo); //LOAD_FN(clGetPipeInfo); // 2.0 LOAD_FN_REQ(clSetMemObjectDestructorCallback); // 1.1 //LOAD_FN(clSVMAlloc); // 2.0 //LOAD_FN(clSVMFree); // 2.0 //LOAD_FN(clCreateSamplerWithProperties); // 2.0 LOAD_FN_REQ(clRetainSampler); LOAD_FN_REQ(clReleaseSampler); LOAD_FN_REQ(clGetSamplerInfo); LOAD_FN_REQ(clCreateProgramWithSource); LOAD_FN_REQ(clCreateProgramWithBinary); LOAD_FN(clCreateProgramWithBuiltInKernels); // 1.2 LOAD_FN_REQ(clRetainProgram); LOAD_FN_REQ(clReleaseProgram); LOAD_FN_REQ(clBuildProgram); LOAD_FN(clCompileProgram); // 1.2 LOAD_FN(clLinkProgram); // 1.2 LOAD_FN(clUnloadPlatformCompiler); // 1.2 LOAD_FN_REQ(clGetProgramInfo); LOAD_FN_REQ(clGetProgramBuildInfo); LOAD_FN_REQ(clCreateKernel); LOAD_FN_REQ(clCreateKernelsInProgram); LOAD_FN_REQ(clRetainKernel); LOAD_FN_REQ(clReleaseKernel); LOAD_FN_REQ(clSetKernelArg); //LOAD_FN(clSetKernelArgSVMPointer); // 2.0 //LOAD_FN(clSetKernelExecInfo); // 2.0 LOAD_FN_REQ(clGetKernelInfo); LOAD_FN(clGetKernelArgInfo); // 1.2 LOAD_FN_REQ(clGetKernelWorkGroupInfo); LOAD_FN_REQ(clWaitForEvents); LOAD_FN_REQ(clGetEventInfo); LOAD_FN_REQ(clCreateUserEvent); // 1.1 LOAD_FN_REQ(clRetainEvent); LOAD_FN_REQ(clReleaseEvent); LOAD_FN_REQ(clSetUserEventStatus); // 1.1 LOAD_FN_REQ(clSetEventCallback); // 1.1 LOAD_FN_REQ(clGetEventProfilingInfo); LOAD_FN_REQ(clFlush); LOAD_FN_REQ(clFinish); LOAD_FN_REQ(clEnqueueReadBuffer); LOAD_FN_REQ(clEnqueueReadBufferRect); // 1.1 LOAD_FN_REQ(clEnqueueWriteBuffer); LOAD_FN_REQ(clEnqueueWriteBufferRect); // 1.1 LOAD_FN(clEnqueueFillBuffer); // 1.2 LOAD_FN_REQ(clEnqueueCopyBuffer); LOAD_FN_REQ(clEnqueueCopyBufferRect); // 1.1 LOAD_FN_REQ(clEnqueueReadImage); LOAD_FN_REQ(clEnqueueWriteImage); LOAD_FN(clEnqueueFillImage); // 1.2 LOAD_FN_REQ(clEnqueueCopyImage); LOAD_FN_REQ(clEnqueueCopyImageToBuffer); LOAD_FN_REQ(clEnqueueCopyBufferToImage); LOAD_FN_REQ(clEnqueueMapBuffer); LOAD_FN_REQ(clEnqueueMapImage); LOAD_FN_REQ(clEnqueueUnmapMemObject); LOAD_FN(clEnqueueMigrateMemObjects); // 1.2 LOAD_FN_REQ(clEnqueueNDRangeKernel); LOAD_FN_REQ(clEnqueueNativeKernel); LOAD_FN(clEnqueueMarkerWithWaitList); // 1.2 LOAD_FN(clEnqueueBarrierWithWaitList); // 1.2 //LOAD_FN(clEnqueueSVMFree); // 2.0 //LOAD_FN(clEnqueueSVMMemcpy); // 2.0 //LOAD_FN(clEnqueueSVMMemFill); // 2.0 //LOAD_FN(clEnqueueSVMMap); // 2.0 //LOAD_FN(clEnqueueSVMUnmap); // 2.0 LOAD_FN(clGetExtensionFunctionAddressForPlatform); // 1.2 #ifdef CL_USE_DEPRECATED_OPENCL_1_1_APIS LOAD_FN_REQ(clCreateImage2D); LOAD_FN_REQ(clCreateImage3D); LOAD_FN_REQ(clEnqueueMarker); LOAD_FN_REQ(clEnqueueWaitForEvents); LOAD_FN_REQ(clEnqueueBarrier); LOAD_FN_REQ(clUnloadCompiler); LOAD_FN_REQ(clGetExtensionFunctionAddress); #endif #ifdef CL_USE_DEPRECATED_OPENCL_2_0_APIS LOAD_FN_REQ(clCreateCommandQueue); LOAD_FN_REQ(clCreateSampler); LOAD_FN_REQ(clEnqueueTask); #endif return 0; sym_load_failed: unload_opencl(); return 2; } int unload_opencl() { #ifdef _WIN32 BOOL ret = FreeLibrary((HMODULE)handle); handle = NULL; return !ret; #elif defined(PARPAR_LIBDL_SUPPORT) int ret = dlclose(handle); handle = NULL; return ret; #else return 1; #endif } par2cmdline-turbo-1.4.0/parpar/gf16/opencl-include/cl.h000066400000000000000000001151231514221355600225540ustar00rootroot00000000000000 #define CL_NO_PROTOTYPES #include #ifndef CL_API_EXTERN # ifdef __cplusplus # define CL_API_EXTERN extern "C" # else # define CL_API_EXTERN extern # endif #endif #ifdef __cplusplus extern "C" int load_opencl(); extern "C" int unload_opencl(); #else int load_opencl(); int unload_opencl(); #endif /* Platform API */ CL_API_EXTERN cl_int (CL_API_CALL *clGetPlatformIDs)(cl_uint /* num_entries */, cl_platform_id * /* platforms */, cl_uint * /* num_platforms */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clGetPlatformInfo)(cl_platform_id /* platform */, cl_platform_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Device APIs */ CL_API_EXTERN cl_int (CL_API_CALL *clGetDeviceIDs)(cl_platform_id /* platform */, cl_device_type /* device_type */, cl_uint /* num_entries */, cl_device_id * /* devices */, cl_uint * /* num_devices */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clGetDeviceInfo)(cl_device_id /* device */, cl_device_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clCreateSubDevices)(cl_device_id /* in_device */, const cl_device_partition_property * /* properties */, cl_uint /* num_devices */, cl_device_id * /* out_devices */, cl_uint * /* num_devices_ret */) CL_API_SUFFIX__VERSION_1_2; CL_API_EXTERN cl_int (CL_API_CALL *clRetainDevice)(cl_device_id /* device */) CL_API_SUFFIX__VERSION_1_2; CL_API_EXTERN cl_int (CL_API_CALL *clReleaseDevice)(cl_device_id /* device */) CL_API_SUFFIX__VERSION_1_2; /* Context APIs */ CL_API_EXTERN cl_context (CL_API_CALL *clCreateContext)(const cl_context_properties * /* properties */, cl_uint /* num_devices */, const cl_device_id * /* devices */, void (CL_CALLBACK * /* pfn_notify */)(const char *, const void *, size_t, void *), void * /* user_data */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_context (CL_API_CALL *clCreateContextFromType)(const cl_context_properties * /* properties */, cl_device_type /* device_type */, void (CL_CALLBACK * /* pfn_notify*/ )(const char *, const void *, size_t, void *), void * /* user_data */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clRetainContext)(cl_context /* context */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clReleaseContext)(cl_context /* context */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clGetContextInfo)(cl_context /* context */, cl_context_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Command Queue APIs */ #ifdef CL_VERSION_2_0 CL_API_EXTERN cl_command_queue (CL_API_CALL *clCreateCommandQueueWithProperties)(cl_context /* context */, cl_device_id /* device */, const cl_queue_properties * /* properties */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0; #endif CL_API_EXTERN cl_int (CL_API_CALL *clRetainCommandQueue)(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clReleaseCommandQueue)(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clGetCommandQueueInfo)(cl_command_queue /* command_queue */, cl_command_queue_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Memory Object APIs */ CL_API_EXTERN cl_mem (CL_API_CALL *clCreateBuffer)(cl_context /* context */, cl_mem_flags /* flags */, size_t /* size */, void * /* host_ptr */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_mem (CL_API_CALL *clCreateSubBuffer)(cl_mem /* buffer */, cl_mem_flags /* flags */, cl_buffer_create_type /* buffer_create_type */, const void * /* buffer_create_info */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1; CL_API_EXTERN cl_mem (CL_API_CALL *clCreateImage)(cl_context /* context */, cl_mem_flags /* flags */, const cl_image_format * /* image_format */, const cl_image_desc * /* image_desc */, void * /* host_ptr */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_2; #ifdef CL_VERSION_2_0 CL_API_EXTERN cl_mem (CL_API_CALL *clCreatePipe)(cl_context /* context */, cl_mem_flags /* flags */, cl_uint /* pipe_packet_size */, cl_uint /* pipe_max_packets */, const cl_pipe_properties * /* properties */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0; #endif CL_API_EXTERN cl_int (CL_API_CALL *clRetainMemObject)(cl_mem /* memobj */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clReleaseMemObject)(cl_mem /* memobj */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clGetSupportedImageFormats)(cl_context /* context */, cl_mem_flags /* flags */, cl_mem_object_type /* image_type */, cl_uint /* num_entries */, cl_image_format * /* image_formats */, cl_uint * /* num_image_formats */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clGetMemObjectInfo)(cl_mem /* memobj */, cl_mem_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clGetImageInfo)(cl_mem /* image */, cl_image_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_2_0 CL_API_EXTERN cl_int (CL_API_CALL *clGetPipeInfo)(cl_mem /* pipe */, cl_pipe_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_2_0; #endif CL_API_EXTERN cl_int (CL_API_CALL *clSetMemObjectDestructorCallback)(cl_mem /* memobj */, void (CL_CALLBACK * /*pfn_notify*/)( cl_mem /* memobj */, void* /*user_data*/), void * /*user_data */ ) CL_API_SUFFIX__VERSION_1_1; /* SVM Allocation APIs */ #ifdef CL_VERSION_2_0 CL_API_EXTERN void * (CL_API_CALL *clSVMAlloc)(cl_context /* context */, cl_svm_mem_flags /* flags */, size_t /* size */, cl_uint /* alignment */) CL_API_SUFFIX__VERSION_2_0; CL_API_EXTERN void (CL_API_CALL *clSVMFree)(cl_context /* context */, void * /* svm_pointer */) CL_API_SUFFIX__VERSION_2_0; /* Sampler APIs */ CL_API_EXTERN cl_sampler (CL_API_CALL *clCreateSamplerWithProperties)(cl_context /* context */, const cl_sampler_properties * /* normalized_coords */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0; #endif CL_API_EXTERN cl_int (CL_API_CALL *clRetainSampler)(cl_sampler /* sampler */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clReleaseSampler)(cl_sampler /* sampler */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clGetSamplerInfo)(cl_sampler /* sampler */, cl_sampler_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Program Object APIs */ CL_API_EXTERN cl_program (CL_API_CALL *clCreateProgramWithSource)(cl_context /* context */, cl_uint /* count */, const char ** /* strings */, const size_t * /* lengths */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_program (CL_API_CALL *clCreateProgramWithBinary)(cl_context /* context */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const size_t * /* lengths */, const unsigned char ** /* binaries */, cl_int * /* binary_status */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_program (CL_API_CALL *clCreateProgramWithBuiltInKernels)(cl_context /* context */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const char * /* kernel_names */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_2; CL_API_EXTERN cl_int (CL_API_CALL *clRetainProgram)(cl_program /* program */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clReleaseProgram)(cl_program /* program */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clBuildProgram)(cl_program /* program */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const char * /* options */, void (CL_CALLBACK * /* pfn_notify */)(cl_program /* program */, void * /* user_data */), void * /* user_data */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clCompileProgram)(cl_program /* program */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const char * /* options */, cl_uint /* num_input_headers */, const cl_program * /* input_headers */, const char ** /* header_include_names */, void (CL_CALLBACK * /* pfn_notify */)(cl_program /* program */, void * /* user_data */), void * /* user_data */) CL_API_SUFFIX__VERSION_1_2; CL_API_EXTERN cl_program (CL_API_CALL *clLinkProgram)(cl_context /* context */, cl_uint /* num_devices */, const cl_device_id * /* device_list */, const char * /* options */, cl_uint /* num_input_programs */, const cl_program * /* input_programs */, void (CL_CALLBACK * /* pfn_notify */)(cl_program /* program */, void * /* user_data */), void * /* user_data */, cl_int * /* errcode_ret */ ) CL_API_SUFFIX__VERSION_1_2; CL_API_EXTERN cl_int (CL_API_CALL *clUnloadPlatformCompiler)(cl_platform_id /* platform */) CL_API_SUFFIX__VERSION_1_2; CL_API_EXTERN cl_int (CL_API_CALL *clGetProgramInfo)(cl_program /* program */, cl_program_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clGetProgramBuildInfo)(cl_program /* program */, cl_device_id /* device */, cl_program_build_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Kernel Object APIs */ CL_API_EXTERN cl_kernel (CL_API_CALL *clCreateKernel)(cl_program /* program */, const char * /* kernel_name */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clCreateKernelsInProgram)(cl_program /* program */, cl_uint /* num_kernels */, cl_kernel * /* kernels */, cl_uint * /* num_kernels_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clRetainKernel)(cl_kernel /* kernel */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clReleaseKernel)(cl_kernel /* kernel */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clSetKernelArg)(cl_kernel /* kernel */, cl_uint /* arg_index */, size_t /* arg_size */, const void * /* arg_value */) CL_API_SUFFIX__VERSION_1_0; #ifdef CL_VERSION_2_0 CL_API_EXTERN cl_int (CL_API_CALL *clSetKernelArgSVMPointer)(cl_kernel /* kernel */, cl_uint /* arg_index */, const void * /* arg_value */) CL_API_SUFFIX__VERSION_2_0; CL_API_EXTERN cl_int (CL_API_CALL *clSetKernelExecInfo)(cl_kernel /* kernel */, cl_kernel_exec_info /* param_name */, size_t /* param_value_size */, const void * /* param_value */) CL_API_SUFFIX__VERSION_2_0; #endif CL_API_EXTERN cl_int (CL_API_CALL *clGetKernelInfo)(cl_kernel /* kernel */, cl_kernel_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clGetKernelArgInfo)(cl_kernel /* kernel */, cl_uint /* arg_indx */, cl_kernel_arg_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_2; CL_API_EXTERN cl_int (CL_API_CALL *clGetKernelWorkGroupInfo)(cl_kernel /* kernel */, cl_device_id /* device */, cl_kernel_work_group_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Event Object APIs */ CL_API_EXTERN cl_int (CL_API_CALL *clWaitForEvents)(cl_uint /* num_events */, const cl_event * /* event_list */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clGetEventInfo)(cl_event /* event */, cl_event_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_event (CL_API_CALL *clCreateUserEvent)(cl_context /* context */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1; CL_API_EXTERN cl_int (CL_API_CALL *clRetainEvent)(cl_event /* event */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clReleaseEvent)(cl_event /* event */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clSetUserEventStatus)(cl_event /* event */, cl_int /* execution_status */) CL_API_SUFFIX__VERSION_1_1; CL_API_EXTERN cl_int (CL_API_CALL *clSetEventCallback)( cl_event /* event */, cl_int /* command_exec_callback_type */, void (CL_CALLBACK * /* pfn_notify */)(cl_event, cl_int, void *), void * /* user_data */) CL_API_SUFFIX__VERSION_1_1; /* Profiling APIs */ CL_API_EXTERN cl_int (CL_API_CALL *clGetEventProfilingInfo)(cl_event /* event */, cl_profiling_info /* param_name */, size_t /* param_value_size */, void * /* param_value */, size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; /* Flush and Finish APIs */ CL_API_EXTERN cl_int (CL_API_CALL *clFlush)(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clFinish)(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; /* Enqueued Commands APIs */ CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueReadBuffer)(cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_read */, size_t /* offset */, size_t /* size */, void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueReadBufferRect)(cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_read */, const size_t * /* buffer_offset */, const size_t * /* host_offset */, const size_t * /* region */, size_t /* buffer_row_pitch */, size_t /* buffer_slice_pitch */, size_t /* host_row_pitch */, size_t /* host_slice_pitch */, void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_1; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueWriteBuffer)(cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_write */, size_t /* offset */, size_t /* size */, const void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueWriteBufferRect)(cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_write */, const size_t * /* buffer_offset */, const size_t * /* host_offset */, const size_t * /* region */, size_t /* buffer_row_pitch */, size_t /* buffer_slice_pitch */, size_t /* host_row_pitch */, size_t /* host_slice_pitch */, const void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_1; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueFillBuffer)(cl_command_queue /* command_queue */, cl_mem /* buffer */, const void * /* pattern */, size_t /* pattern_size */, size_t /* offset */, size_t /* size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueCopyBuffer)(cl_command_queue /* command_queue */, cl_mem /* src_buffer */, cl_mem /* dst_buffer */, size_t /* src_offset */, size_t /* dst_offset */, size_t /* size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueCopyBufferRect)(cl_command_queue /* command_queue */, cl_mem /* src_buffer */, cl_mem /* dst_buffer */, const size_t * /* src_origin */, const size_t * /* dst_origin */, const size_t * /* region */, size_t /* src_row_pitch */, size_t /* src_slice_pitch */, size_t /* dst_row_pitch */, size_t /* dst_slice_pitch */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_1; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueReadImage)(cl_command_queue /* command_queue */, cl_mem /* image */, cl_bool /* blocking_read */, const size_t * /* origin[3] */, const size_t * /* region[3] */, size_t /* row_pitch */, size_t /* slice_pitch */, void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueWriteImage)(cl_command_queue /* command_queue */, cl_mem /* image */, cl_bool /* blocking_write */, const size_t * /* origin[3] */, const size_t * /* region[3] */, size_t /* input_row_pitch */, size_t /* input_slice_pitch */, const void * /* ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueFillImage)(cl_command_queue /* command_queue */, cl_mem /* image */, const void * /* fill_color */, const size_t * /* origin[3] */, const size_t * /* region[3] */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueCopyImage)(cl_command_queue /* command_queue */, cl_mem /* src_image */, cl_mem /* dst_image */, const size_t * /* src_origin[3] */, const size_t * /* dst_origin[3] */, const size_t * /* region[3] */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueCopyImageToBuffer)(cl_command_queue /* command_queue */, cl_mem /* src_image */, cl_mem /* dst_buffer */, const size_t * /* src_origin[3] */, const size_t * /* region[3] */, size_t /* dst_offset */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueCopyBufferToImage)(cl_command_queue /* command_queue */, cl_mem /* src_buffer */, cl_mem /* dst_image */, size_t /* src_offset */, const size_t * /* dst_origin[3] */, const size_t * /* region[3] */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN void * (CL_API_CALL *clEnqueueMapBuffer)(cl_command_queue /* command_queue */, cl_mem /* buffer */, cl_bool /* blocking_map */, cl_map_flags /* map_flags */, size_t /* offset */, size_t /* size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN void * (CL_API_CALL *clEnqueueMapImage)(cl_command_queue /* command_queue */, cl_mem /* image */, cl_bool /* blocking_map */, cl_map_flags /* map_flags */, const size_t * /* origin[3] */, const size_t * /* region[3] */, size_t * /* image_row_pitch */, size_t * /* image_slice_pitch */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueUnmapMemObject)(cl_command_queue /* command_queue */, cl_mem /* memobj */, void * /* mapped_ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueMigrateMemObjects)(cl_command_queue /* command_queue */, cl_uint /* num_mem_objects */, const cl_mem * /* mem_objects */, cl_mem_migration_flags /* flags */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueNDRangeKernel)(cl_command_queue /* command_queue */, cl_kernel /* kernel */, cl_uint /* work_dim */, const size_t * /* global_work_offset */, const size_t * /* global_work_size */, const size_t * /* local_work_size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueNativeKernel)(cl_command_queue /* command_queue */, void (CL_CALLBACK * /*user_func*/)(void *), void * /* args */, size_t /* cb_args */, cl_uint /* num_mem_objects */, const cl_mem * /* mem_list */, const void ** /* args_mem_loc */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueMarkerWithWaitList)(cl_command_queue /* command_queue */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueBarrierWithWaitList)(cl_command_queue /* command_queue */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_2; #ifdef CL_VERSION_2_0 CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueSVMFree)(cl_command_queue /* command_queue */, cl_uint /* num_svm_pointers */, void *[] /* svm_pointers[] */, void (CL_CALLBACK * /*pfn_free_func*/)(cl_command_queue /* queue */, cl_uint /* num_svm_pointers */, void *[] /* svm_pointers[] */, void * /* user_data */), void * /* user_data */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueSVMMemcpy)(cl_command_queue /* command_queue */, cl_bool /* blocking_copy */, void * /* dst_ptr */, const void * /* src_ptr */, size_t /* size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueSVMMemFill)(cl_command_queue /* command_queue */, void * /* svm_ptr */, const void * /* pattern */, size_t /* pattern_size */, size_t /* size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueSVMMap)(cl_command_queue /* command_queue */, cl_bool /* blocking_map */, cl_map_flags /* flags */, void * /* svm_ptr */, size_t /* size */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0; CL_API_EXTERN cl_int (CL_API_CALL *clEnqueueSVMUnmap)(cl_command_queue /* command_queue */, void * /* svm_ptr */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0; #endif /* Extension function access * * Returns the extension function address for the given function name, * or NULL if a valid function can not be found. The client must * check to make sure the address is not NULL, before using or * calling the returned function address. */ CL_API_EXTERN void * (CL_API_CALL *clGetExtensionFunctionAddressForPlatform)(cl_platform_id /* platform */, const char * /* func_name */) CL_API_SUFFIX__VERSION_1_2; /* Deprecated OpenCL 1.1 APIs */ #ifdef CL_USE_DEPRECATED_OPENCL_1_1_APIS CL_API_EXTERN CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_mem (CL_API_CALL *clCreateImage2D)(cl_context /* context */, cl_mem_flags /* flags */, const cl_image_format * /* image_format */, size_t /* image_width */, size_t /* image_height */, size_t /* image_row_pitch */, void * /* host_ptr */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; CL_API_EXTERN CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_mem (CL_API_CALL *clCreateImage3D)(cl_context /* context */, cl_mem_flags /* flags */, const cl_image_format * /* image_format */, size_t /* image_width */, size_t /* image_height */, size_t /* image_depth */, size_t /* image_row_pitch */, size_t /* image_slice_pitch */, void * /* host_ptr */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; CL_API_EXTERN CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int (CL_API_CALL *clEnqueueMarker)(cl_command_queue /* command_queue */, cl_event * /* event */) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; CL_API_EXTERN CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int (CL_API_CALL *clEnqueueWaitForEvents)(cl_command_queue /* command_queue */, cl_uint /* num_events */, const cl_event * /* event_list */) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; CL_API_EXTERN CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int (CL_API_CALL *clEnqueueBarrier)(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; CL_API_EXTERN CL_API_PREFIX__VERSION_1_1_DEPRECATED cl_int (CL_API_CALL *clUnloadCompiler)(void) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; CL_API_EXTERN CL_API_PREFIX__VERSION_1_1_DEPRECATED void * (CL_API_CALL *clGetExtensionFunctionAddress)(const char * /* func_name */) CL_API_SUFFIX__VERSION_1_1_DEPRECATED; #endif /* Deprecated OpenCL 2.0 APIs */ #ifdef CL_USE_DEPRECATED_OPENCL_2_0_APIS CL_API_EXTERN CL_API_PREFIX__VERSION_2_0_DEPRECATED cl_command_queue (CL_API_CALL *clCreateCommandQueue)(cl_context /* context */, cl_device_id /* device */, cl_command_queue_properties /* properties */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0_DEPRECATED; CL_API_EXTERN CL_API_PREFIX__VERSION_2_0_DEPRECATED cl_sampler (CL_API_CALL *clCreateSampler)(cl_context /* context */, cl_bool /* normalized_coords */, cl_addressing_mode /* addressing_mode */, cl_filter_mode /* filter_mode */, cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_2_0_DEPRECATED; CL_API_EXTERN CL_API_PREFIX__VERSION_2_0_DEPRECATED cl_int (CL_API_CALL *clEnqueueTask)(cl_command_queue /* command_queue */, cl_kernel /* kernel */, cl_uint /* num_events_in_wait_list */, const cl_event * /* event_wait_list */, cl_event * /* event */) CL_API_SUFFIX__VERSION_2_0_DEPRECATED; #endif par2cmdline-turbo-1.4.0/parpar/gf16/opencl-include/readme.txt000066400000000000000000000011601514221355600237760ustar00rootroot00000000000000The files in the CL directory are taken from the following: * https://github.com/KhronosGroup/OpenCL-Headers/releases/tag/v2023.04.17 * https://github.com/KhronosGroup/OpenCL-CLHPP/releases/tag/v2023.04.17 The OpenCL C++ header has been modified to remove dependency on cl_ext/cl_gl headers, and handle the function pointers introduced in the wrapper below. The cl.c/cl.h files here are a wrapper to enable runtime linking of OpenCL. cl.h will include the OpenCL C headers, and must be included before the C++ header. * users must include cl.c for compilation, and call load_opencl() before using any OpenCL functionality par2cmdline-turbo-1.4.0/parpar/gf16/suppressions-valgrind.supp000066400000000000000000000000771514221355600243770ustar00rootroot00000000000000{ Memcheck:Leak fun:malloc fun:gfmat_init } par2cmdline-turbo-1.4.0/parpar/gf16/threadqueue.h000066400000000000000000000252471514221355600216000ustar00rootroot00000000000000#ifndef __THREADQUEUE_H__ #define __THREADQUEUE_H__ #include #ifdef USE_LIBUV # include # define thread_t uv_thread_t # define thread_create(t, f, a) uv_thread_create(&(t), f, a) # define thread_join(t) uv_thread_join(&(t)) # define mutex_t uv_mutex_t # define mutex_init(m) uv_mutex_init(&(m)) # define mutex_destroy(m) uv_mutex_destroy(&(m)) # define mutex_lock(m) uv_mutex_lock(&(m)) # define mutex_unlock(m) uv_mutex_unlock(&(m)) # define condvar_t uv_cond_t # define condvar_init(c) uv_cond_init(&(c)) # define condvar_destroy(c) uv_cond_destroy(&(c)) # define condvar_signal(c) uv_cond_signal(&(c)) #else # include # define thread_t std::thread # define thread_create(t, f, a) t = std::thread(f, a) # define thread_join(t) t.join() # include # define mutex_t std::unique_ptr # define mutex_init(m) m = std::unique_ptr(new std::mutex()) # define mutex_destroy(m) # define mutex_lock(m) m->lock() # define mutex_unlock(m) m->unlock() # include # define condvar_t std::unique_ptr # define condvar_init(c) c = std::unique_ptr(new std::condition_variable()) # define condvar_destroy(c) # define condvar_signal(c) c->notify_one() #endif #include template class ThreadMessageQueue { std::queue q; mutable mutex_t mutex; condvar_t cond; bool skipDestructor; // disable copy constructor ThreadMessageQueue(const ThreadMessageQueue&); ThreadMessageQueue& operator=(const ThreadMessageQueue&); // ...but allow moves void move(ThreadMessageQueue& other) { q = std::move(other.q); #ifdef USE_LIBUV mutex = other.mutex; cond = other.cond; #else mutex = std::move(other.mutex); cond = std::move(other.cond); #endif skipDestructor = false; other.skipDestructor = true; } public: ThreadMessageQueue() { mutex_init(mutex); condvar_init(cond); skipDestructor = false; } ~ThreadMessageQueue() { if(skipDestructor) return; mutex_destroy(mutex); condvar_destroy(cond); } ThreadMessageQueue(ThreadMessageQueue&& other) noexcept { move(other); } ThreadMessageQueue& operator=(ThreadMessageQueue&& other) noexcept { move(other); return *this; } void push(T item) { mutex_lock(mutex); q.push(item); condvar_signal(cond); mutex_unlock(mutex); } template void push_multi(const Iterable& list) { mutex_lock(mutex); for(auto it = list.cbegin(); it != list.cend(); ++it) { q.push(*it); } condvar_signal(cond); mutex_unlock(mutex); } T pop() { #ifdef USE_LIBUV mutex_lock(mutex); while(q.empty()) { uv_cond_wait(&cond, &mutex); } T item = q.front(); q.pop(); mutex_unlock(mutex); #else std::unique_lock lk(*mutex); cond->wait(lk, [this]{ return !q.empty(); }); T item = q.front(); q.pop(); #endif return item; } bool trypop(T* item) { bool notEmpty; mutex_lock(mutex); notEmpty = !q.empty(); if(notEmpty) { *item = q.front(); q.pop(); } mutex_unlock(mutex); return notEmpty; } // NOTE: unlike 'pop', this doesn't wait - it's more like trypop std::vector popall() { std::vector copy; mutex_lock(mutex); copy.reserve(q.size()); while(!q.empty()) { copy.emplace_back(std::move(q.front())); q.pop(); } mutex_unlock(mutex); return copy; } size_t size() const { mutex_lock(mutex); size_t s = q.size(); mutex_unlock(mutex); return s; } bool empty() const { mutex_lock(mutex); bool e = q.empty(); mutex_unlock(mutex); return e; } }; #ifdef USE_LIBUV struct tnqCloseWrap { void(*cb)(void*); void* data; }; template class ThreadNotifyQueue { ThreadMessageQueue q; std::unique_ptr a; P* o; void (P::*cb)(void*); static void notified(uv_async_t *handle #if UV_VERSION_MAJOR < 1 , int #endif ) { auto self = static_cast(handle->data); auto notifications = self->q.popall(); for(void* notification : notifications) (self->o->*(self->cb))(notification); } public: explicit ThreadNotifyQueue(uv_loop_t* loop, P* object, void (P::*callback)(void*)) { a.reset(new uv_async_t()); uv_async_init(loop, a.get(), notified); a->data = static_cast(this); cb = callback; o = object; } void notify(void* item) { q.push(item); uv_async_send(a.get()); } void close(void* data, void(*closeCb)(void*)) { auto* d = new tnqCloseWrap; d->cb = closeCb; d->data = data; a->data = d; uv_close(reinterpret_cast(a.release()), [](uv_handle_t* handle) { auto* d = static_cast(handle->data); d->cb(d->data); delete d; delete handle; }); } void close() { uv_close(reinterpret_cast(a.release()), [](uv_handle_t* handle) { delete handle; }); } }; #endif #ifdef USE_LIBUV typedef void(*thread_cb_t)(ThreadMessageQueue&); #else typedef std::function&)> thread_cb_t; #endif #if defined(_WINDOWS) || defined(__WINDOWS__) || defined(_WIN32) || defined(_WIN64) # ifndef NOMINMAX # define NOMINMAX # endif # define WIN32_LEAN_AND_MEAN # include #else # include # ifdef __OpenBSD__ # include # endif #endif #if defined(__linux) || defined(__linux__) # include # include #endif class MessageThread { ThreadMessageQueue q; thread_t thread; bool threadActive; bool threadCreated; thread_cb_t cb; static void thread_func(void* parent) { MessageThread* self = static_cast(parent); if(self->lowPrio) { #if defined(_WINDOWS) || defined(__WINDOWS__) || defined(_WIN32) || defined(_WIN64) HANDLE hThread = GetCurrentThread(); switch(GetThreadPriority(hThread)) { case THREAD_PRIORITY_TIME_CRITICAL: SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST); break; case THREAD_PRIORITY_HIGHEST: SetThreadPriority(hThread, THREAD_PRIORITY_ABOVE_NORMAL); break; case THREAD_PRIORITY_ABOVE_NORMAL: SetThreadPriority(hThread, THREAD_PRIORITY_NORMAL); break; case THREAD_PRIORITY_NORMAL: SetThreadPriority(hThread, THREAD_PRIORITY_BELOW_NORMAL); break; case THREAD_PRIORITY_BELOW_NORMAL: SetThreadPriority(hThread, THREAD_PRIORITY_LOWEST); break; case THREAD_PRIORITY_LOWEST: SetThreadPriority(hThread, THREAD_PRIORITY_IDLE); break; case THREAD_PRIORITY_IDLE: // can't go lower default: // do nothing break; } #else // it seems that threads cannot have lower priority on POSIX, unless it's scheduled realtime, however we can declare it to be CPU intensive int policy; struct sched_param param; pthread_t self = pthread_self(); if(!pthread_getschedparam(self, &policy, ¶m)) { if(policy == SCHED_OTHER) { #ifndef SCHED_BATCH // MacOS doesn't support SCHED_BATCH, but does seem to permit priorities on SCHED_OTHER int min = sched_get_priority_min(policy); if(min < param.sched_priority) { param.sched_priority -= 1; pthread_setschedparam(self, policy, ¶m); } #else pthread_setschedparam(self, SCHED_BATCH, ¶m); #endif } } # if defined(__linux) || defined(__linux__) // ...but Linux allows per-thread priority (void)!nice(1); // we don't care if this fails # endif #endif } if(self->name) { #if defined(_WINDOWS) || defined(__WINDOWS__) || defined(_WIN32) || defined(_WIN64) HMODULE h = GetModuleHandleA("kernelbase.dll"); if(h) { HRESULT(__stdcall *fnSetTD)(HANDLE, PCWSTR) = (HRESULT(__stdcall *)(HANDLE, PCWSTR))((void*)GetProcAddress(h, "SetThreadDescription")); if(fnSetTD) { wchar_t nameUCS2[17]; //assert(strlen(self->name) <= 16); // always hard-coded string, plus Linux limits it to 16 chars, so shouldn't ever overflow MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, self->name, -1, nameUCS2, sizeof(nameUCS2)/sizeof(wchar_t) -1); fnSetTD(GetCurrentThread(), nameUCS2); } } #elif defined(__linux) || defined(__linux__) prctl(PR_SET_NAME, self->name, 0, 0, 0); #elif defined(__MACH__) pthread_setname_np(self->name); #elif defined(__FreeBSD__) || defined(__DragonFly__) pthread_setname_np(pthread_self(), self->name); #elif defined(__NetBSD__) pthread_setname_np(pthread_self(), self->name, NULL); #elif defined(__OpenBSD__) pthread_set_name_np(pthread_self(), self->name); #endif } self->cb(self->q); } // disable copy constructor MessageThread(const MessageThread&); MessageThread& operator=(const MessageThread&); // ...but allow moves void move(MessageThread& other) { q = std::move(other.q); #ifdef USE_LIBUV thread = other.thread; #else thread = std::move(other.thread); #endif threadActive = other.threadActive; threadCreated = other.threadCreated; cb = other.cb; name = other.name; lowPrio = other.lowPrio; other.threadActive = false; other.threadCreated = false; } public: bool lowPrio; const char* name; MessageThread() { cb = NULL; threadActive = false; threadCreated = false; lowPrio = false; name = NULL; } MessageThread(thread_cb_t callback) { cb = callback; threadActive = false; threadCreated = false; lowPrio = false; name = NULL; } void setCallback(thread_cb_t callback) { cb = callback; } ~MessageThread() { if(threadActive) q.push(NULL); if(threadCreated) thread_join(thread); } MessageThread(MessageThread&& other) noexcept { move(other); } MessageThread& operator=(MessageThread&& other) noexcept { move(other); return *this; } void start() { if(threadActive) return; threadActive = true; if(threadCreated) // previously created, but end fired, so need to wait for this thread to close before starting another thread_join(thread); threadCreated = true; thread_create(thread, thread_func, this); } // item cannot be NULL void send(void* item) { start(); q.push(item); } template void send_multi(Iterable items) { start(); q.push_multi(items); } void end() { if(threadActive) { q.push(NULL); threadActive = false; } } size_t size() { return q.size(); } bool empty() { return q.empty(); } }; static inline int hardware_concurrency() { int threads; #ifdef USE_LIBUV #if UV_VERSION_HEX >= 0x12c00 // 1.44.0 threads = uv_available_parallelism(); #else uv_cpu_info_t *info; uv_cpu_info(&info, &threads); uv_free_cpu_info(info, threads); #endif #else threads = (int)std::thread::hardware_concurrency(); #endif if(threads < 1) threads = 1; return threads; } #undef thread_t #undef thread_create #undef thread_join #undef mutex_t #undef mutex_init #undef mutex_destroy #undef mutex_lock #undef mutex_unlock #undef condvar_t #undef condvar_init #undef condvar_destroy #undef condvar_signal #endif // defined(__THREADQUEUE_H__) par2cmdline-turbo-1.4.0/parpar/gf16/x86_jit.h000066400000000000000000000566641514221355600205660ustar00rootroot00000000000000#ifndef __GF_XOR_JIT__ #define __GF_XOR_JIT__ #include "gf16_global.h" #include "../src/platform.h" /* registers */ #define AX 0 #define BX 3 #define CX 1 #define DX 2 #define DI 7 #define SI 6 #define BP 5 #define SP 4 /* conditional jumps */ #define JE 0x4 #define JNE 0x5 #define JL 0xC #define JGE 0xD #define JLE 0xE #define JG 0xF static HEDLEY_ALWAYS_INLINE int_fast32_t lshift32(int_fast32_t v, unsigned n) { // left-shifting a negative number is undefined behaviour, so we'll define it here return (int_fast32_t)((uint_fast32_t)v << n); } static HEDLEY_ALWAYS_INLINE size_t _jit_rex_pref(uint8_t** jit, uint_fast8_t xreg, uint_fast8_t xreg2) { #ifdef PLATFORM_AMD64 if(xreg > 7 || xreg2 > 7) { *((*jit)++) = 0x40 | (xreg2 >>3) | ((xreg >>1)&4); return 1; } #else UNUSED(jit); UNUSED(xreg); UNUSED(xreg2); #endif return 0; } static HEDLEY_ALWAYS_INLINE size_t _jit_rxx_pref(uint8_t** jit, uint_fast8_t reg, uint_fast8_t reg2) { #ifdef PLATFORM_AMD64 *((*jit)++) = 0x48 | (reg >>3) | ((reg2 >>1)&4); return 1; #else UNUSED(jit); UNUSED(reg); UNUSED(reg2); #endif return 0; } static HEDLEY_ALWAYS_INLINE size_t _jit_xorps_m(uint8_t* jit, uint_fast8_t xreg, uint_fast8_t mreg, int32_t offs) { size_t p = _jit_rex_pref(&jit, xreg, 0); p += mreg == 12; xreg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x2480570F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; write32(jit +3, offs); return p+7; } else if(offs || mreg == 13) { if(mreg == 12) { write32(jit, 0x2440570F | (xreg <<19) | (mreg <<16)); jit[4] = offs; } else { write32(jit, 0x40570F | (xreg <<19) | (mreg <<16) | lshift32(offs, 24)); } return p+4; } else { /* can overflow, but we don't care */ write32(jit, 0x2400570F | (xreg <<19) | (mreg <<16)); return p+3; } } static HEDLEY_ALWAYS_INLINE size_t _jit_xorps_r(uint8_t* jit, uint_fast8_t xreg2, uint_fast8_t xreg1) { size_t p = _jit_rex_pref(&jit, xreg2, xreg1); xreg1 &= 7; xreg2 &= 7; /* can overflow, but we don't care */ write32(jit, 0xC0570F | (xreg2 <<19) | (xreg1 <<16)); return p+3; } static HEDLEY_ALWAYS_INLINE size_t _jit_pxor_m(uint8_t* jit, uint_fast8_t xreg, uint_fast8_t mreg, int32_t offs) { *(jit++) = 0x66; size_t p = _jit_rex_pref(&jit, xreg, 0) +1; p += mreg == 12; xreg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x2480EF0F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; write32(jit +3, offs); return p+7; } else if(offs || mreg == 13) { write32(jit, 0x2440EF0F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; jit[3] = (uint8_t)offs; return p+4; } else { write32(jit, 0x2400EF0F | (xreg <<19) | (mreg <<16)); return p+3; } } static HEDLEY_ALWAYS_INLINE size_t _jit_pxor_r(uint8_t* jit, uint_fast8_t xreg2, uint_fast8_t xreg1) { *(jit++) = 0x66; size_t p = _jit_rex_pref(&jit, xreg2, xreg1) +1; xreg1 &= 7; xreg2 &= 7; write32(jit, 0xC0EF0F | (xreg2 <<19) | (xreg1 <<16)); return p+3; } static HEDLEY_ALWAYS_INLINE size_t _jit_xorpd_m(uint8_t* jit, uint_fast8_t xreg, uint_fast8_t mreg, int32_t offs) { size_t p = _jit_rex_pref(&jit, xreg, 0); p += mreg == 12; xreg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x2480570F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; write32(jit +3, offs); return p+7; } else if(offs || mreg == 13) { write32(jit, 0x2440570F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; jit[3] = (uint8_t)offs; return p+4; } else { write32(jit, 0x2400570F | (xreg <<19) | (mreg <<16)); return p+3; } } static HEDLEY_ALWAYS_INLINE size_t _jit_xorpd_r(uint8_t* jit, uint_fast8_t xreg2, uint_fast8_t xreg1) { *(jit++) = 0x66; size_t p = _jit_rex_pref(&jit, xreg2, xreg1) +1; xreg1 &= 7; xreg2 &= 7; write32(jit, 0xC0570F | (xreg2 <<19) | (xreg1 <<16)); return p+3; } static HEDLEY_ALWAYS_INLINE size_t _jit_movaps(uint8_t* jit, uint_fast8_t xreg, uint_fast8_t xreg2) { size_t p = _jit_rex_pref(&jit, xreg, xreg2); xreg &= 7; xreg2 &= 7; /* can overflow, but we don't care */ write32(jit, 0xC0280F | (xreg <<19) | (xreg2 <<16)); return p+3; } static HEDLEY_ALWAYS_INLINE size_t _jit_movaps_load(uint8_t* jit, uint_fast8_t xreg, uint_fast8_t mreg, int32_t offs) { size_t p = _jit_rex_pref(&jit, xreg, 0); p += mreg == 12; xreg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x2480280F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; write32(jit +3, offs); return p+7; } else if(offs || mreg == 13) { if(mreg == 12) { write32(jit, 0x2440280F | (xreg <<19) | (mreg <<16)); jit[4] = offs; } else write32(jit, 0x40280F | (xreg <<19) | (mreg <<16) | lshift32(offs, 24)); return p+4; } else { /* can overflow, but we don't care */ write32(jit, 0x2400280F | (xreg <<19) | (mreg <<16)); return p+3; } } static HEDLEY_ALWAYS_INLINE size_t _jit_movaps_store(uint8_t* jit, uint_fast8_t mreg, int32_t offs, uint_fast8_t xreg) { size_t p = _jit_rex_pref(&jit, xreg, 0); p += mreg == 12; xreg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x2480290F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; write32(jit +3, offs); return p+7; } else if(offs || mreg == 13) { if(mreg == 12) { write32(jit, 0x2440290F | (xreg <<19) | (mreg <<16)); jit[4] = offs; } else write32(jit, 0x40290F | (xreg <<19) | (mreg <<16) | lshift32(offs, 24)); return p+4; } else { /* can overflow, but we don't care */ write32(jit, 0x2400290F | (xreg <<19) | (mreg <<16)); return p+3; } } static HEDLEY_ALWAYS_INLINE size_t _jit_movdqa(uint8_t* jit, uint_fast8_t xreg, uint_fast8_t xreg2) { *(jit++) = 0x66; size_t p = _jit_rex_pref(&jit, xreg, xreg2) +1; xreg &= 7; xreg2 &= 7; write32(jit, 0xC06F0F | (xreg <<19) | (xreg2 <<16)); return p+3; } static HEDLEY_ALWAYS_INLINE size_t _jit_movdqa_load(uint8_t* jit, uint_fast8_t xreg, uint_fast8_t mreg, int32_t offs) { *(jit++) = 0x66; size_t p = _jit_rex_pref(&jit, xreg, 0) +1; p += mreg == 12; xreg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x24806F0F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; write32(jit +3, offs); return p+7; } else if(offs || mreg == 13) { write32(jit, 0x24406F0F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; jit[3] = (uint8_t)offs; return p+4; } else { write32(jit, 0x24006F0F | (xreg <<19) | (mreg <<16)); return p+3; } } static HEDLEY_ALWAYS_INLINE size_t _jit_movdqa_store(uint8_t* jit, uint_fast8_t mreg, int32_t offs, uint_fast8_t xreg) { *(jit++) = 0x66; size_t p = _jit_rex_pref(&jit, xreg, 0) +1; p += mreg == 12; xreg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x24807F0F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; write32(jit +3, offs); return p+7; } else if(offs || mreg == 13) { write32(jit, 0x24407F0F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; jit[3] = (uint8_t)offs; return p+4; } else { write32(jit, 0x24007F0F | (xreg <<19) | (mreg <<16)); return p+3; } } static HEDLEY_ALWAYS_INLINE size_t _jit_movapd(uint8_t* jit, uint_fast8_t xreg, uint_fast8_t xreg2) { *(jit++) = 0x66; size_t p = _jit_rex_pref(&jit, xreg, xreg2) +1; xreg &= 7; xreg2 &= 7; write32(jit, 0xC0280F | (xreg <<19) | (xreg2 <<16)); return p+3; } static HEDLEY_ALWAYS_INLINE size_t _jit_movapd_load(uint8_t* jit, uint_fast8_t xreg, uint_fast8_t mreg, int32_t offs) { *(jit++) = 0x66; size_t p = _jit_rex_pref(&jit, xreg, 0) +1; p += mreg == 12; xreg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x2480280F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; write32(jit +3, offs); return p+7; } else if(offs || mreg == 13) { write32(jit, 0x2440280F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; jit[3] = (uint8_t)offs; return p+4; } else { write32(jit, 0x2400280F | (xreg <<19) | (mreg <<16)); return p+3; } } static HEDLEY_ALWAYS_INLINE size_t _jit_movapd_store(uint8_t* jit, uint_fast8_t mreg, int32_t offs, uint_fast8_t xreg) { *(jit++) = 0x66; size_t p = _jit_rex_pref(&jit, xreg, 0) +1; p += mreg == 12; xreg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x2480290F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; write32(jit +3, offs); return p+7; } else if(offs || mreg == 13) { write32(jit, 0x2440290F | (xreg <<19) | (mreg <<16)); jit += mreg == 12; jit[3] = (uint8_t)offs; return p+4; } else { write32(jit, 0x2400290F | (xreg <<19) | (mreg <<16)); return p+3; } } /** AVX (256-bit) VEX coded instructions **/ static HEDLEY_ALWAYS_INLINE size_t _jit_vpxor_m(uint8_t* jit, uint_fast8_t yregD, uint_fast8_t yreg1, uint_fast8_t mreg, int32_t offs) { size_t p; unsigned offsFlag = (offs != 0 || mreg == 13) << (int)(((offs+128) & ~0xFF) != 0); if(mreg > 7) { write32(jit, 0xEF7DE1C4 ^ ((yregD >> 3) << 15) ^ ((mreg >> 3) << 13) ^ (yreg1 <<19)); jit[4] = (offsFlag<<6) | ((yregD & 7) <<3) | ((mreg & 7) <<0); p = 5; } else { write32(jit, (0x00EFFDC5 | ((yregD & 7) <<27) | ((mreg & 7) <<24) | (offsFlag << 30)) ^ ((yregD >> 3) << 15) ^ (yreg1 <<11)); p = 4; } if(mreg == 12) jit[p++] = 0x24; if(offsFlag == 2) { write32(jit +p, offs); return p+4; } else if(offsFlag) { jit[p] = (uint8_t)offs; return p+1; } return p; } static HEDLEY_ALWAYS_INLINE size_t _jit_vpxor_r(uint8_t* jit, uint_fast8_t yregD, uint_fast8_t yreg1, uint_fast8_t yreg2) { if(yreg2 > 7) { write32(jit, 0xEF7DE1C4 ^ ((yregD >> 3) << 15) ^ ((yreg2 >> 3) << 13) ^ (yreg1 <<19)); jit[4] = 0xC0 | ((yregD & 7) <<3) | ((yreg2 & 7) <<0); return 5; } else { write32(jit, (0xC0EFFDC5 | ((yregD & 7) <<27) | ((yreg2 & 7) <<24)) ^ ((yregD >> 3) << 15) ^ (yreg1 <<11)); return 4; } } static HEDLEY_ALWAYS_INLINE size_t _jit_vmovdqa(uint8_t* jit, uint_fast8_t yreg, uint_fast8_t yreg2) { if(yreg2 > 7) { write32(jit, 0x6F7DE1C4 ^ ((yreg >> 3) << 15) ^ ((yreg2 >> 3) << 13)); jit[4] = 0xC0 | ((yreg & 7) <<3) | ((yreg2 & 7) <<0); return 5; } else { write32(jit, (0xC06FFDC5 | ((yreg & 7) <<27) | ((yreg2 & 7) <<24)) ^ ((yreg >> 3) << 15)); return 4; } } static HEDLEY_ALWAYS_INLINE size_t _jit_vmovdqa_load(uint8_t* jit, uint_fast8_t yreg, uint_fast8_t mreg, int32_t offs) { size_t p; unsigned offsFlag = (offs != 0 || mreg == 13) << (int)(((offs+128) & ~0xFF) != 0); if(mreg > 7) { write32(jit, 0x6F7DE1C4 ^ ((yreg >> 3) << 15) ^ ((mreg >> 3) << 13)); jit[4] = (offsFlag<<6) | ((yreg & 7) <<3) | ((mreg & 7) <<0); p = 5; } else { write32(jit, (0x006FFDC5 | ((yreg & 7) <<27) | ((mreg & 7) <<24) | (offsFlag << 30)) ^ ((yreg >> 3) << 15)); p = 4; } if(mreg == 12) jit[p++] = 0x24; if(offsFlag == 2) { write32(jit +p, offs); return p+4; } else if(offsFlag) { jit[p] = (uint8_t)offs; return p+1; } return p; } static HEDLEY_ALWAYS_INLINE size_t _jit_vmovdqa_store(uint8_t* jit, uint_fast8_t mreg, int32_t offs, uint_fast8_t yreg) { size_t p; unsigned offsFlag = (offs != 0 || mreg == 13) << (int)(((offs+128) & ~0xFF) != 0); if(mreg > 7) { write32(jit, 0x7F7DE1C4 ^ ((yreg >> 3) << 15) ^ ((mreg >> 3) << 13)); jit[4] = (offsFlag<<6) | ((yreg & 7) <<3) | ((mreg & 7) <<0); p = 5; } else { write32(jit, (0x007FFDC5 | ((yreg & 7) <<27) | ((mreg & 7) <<24) | (offsFlag << 30)) ^ ((yreg >> 3) << 15)); p = 4; } if(mreg == 12) jit[p++] = 0x24; if(offsFlag == 2) { write32(jit +p, offs); return p+4; } else if(offsFlag) { jit[p] = (uint8_t)offs; return p+1; } return p; } /** AVX3 (512-bit) EVEX coded instructions **/ static HEDLEY_ALWAYS_INLINE size_t _jit_vpxord_m(uint8_t* jit, uint_fast8_t zregD, uint_fast8_t zreg1, uint_fast8_t mreg, int32_t offs) { unsigned offsFlag = (offs != 0 || mreg == 13) << (int)(((offs+128*64) & ~0x3FC0) != 0); write32(jit, 0x487DF162 ^ ((zregD & 8) <<12) ^ ((zregD & 16) << 8) ^ ((zreg1 & 15) <<19) ^ ((zreg1 & 16) <<23) ^ ((mreg & 8) <<10)); write32(jit+4, 0x2400EF | ((zregD & 7) <<11) | ((mreg & 7) << 8) | (offsFlag << 14)); int isM12 = (mreg == 12); jit += 6+isM12; if(offsFlag == 2) { write32(jit, offs); return 10+isM12; } else if(offs || mreg == 13) { *jit = (offs>>6); return 7+isM12; } return 6+isM12; } static HEDLEY_ALWAYS_INLINE size_t _jit_vpxord_r(uint8_t* jit, uint_fast8_t zregD, uint_fast8_t zreg1, uint_fast8_t zreg2) { write32(jit, 0x487DF162 ^ ((zregD & 8) <<12) ^ ((zregD & 16) << 8) ^ ((zreg1 & 15) <<19) ^ ((zreg1 & 16) <<23) ^ ((zreg2 & 24) <<10)); write16(jit+4, 0xC0EF | ((zregD & 7) <<11) | ((zreg2 & 7) << 8)); return 6; } static HEDLEY_ALWAYS_INLINE size_t _jit_vpternlogd_m(uint8_t* jit, uint_fast8_t zreg1, uint_fast8_t zreg2, uint_fast8_t mreg, int32_t offs, uint8_t op) { unsigned offsFlag = (offs != 0 || mreg == 13) << (int)(((offs+128*64) & ~0x3FC0) != 0); write32(jit, 0x487DF362 ^ ((zreg1 & 8) <<12) ^ ((zreg1 & 16) << 8) ^ ((zreg2 & 15) <<19) ^ ((zreg2 & 16) <<23) ^ ((mreg & 8) <<10)); write32(jit+4, 0x240025 | ((zreg1 & 7) <<11) | ((mreg & 7) << 8) | (offsFlag << 14)); int isM12 = (mreg == 12); jit += 6+isM12; if(offsFlag == 2) { write32(jit, offs); *(jit+4) = op; return 11+isM12; } else if(offs || mreg == 13) { write16(jit, (uint8_t)(offs>>6) | (op <<8)); return 8+isM12; } *jit = op; return 7+isM12; } static HEDLEY_ALWAYS_INLINE size_t _jit_vpternlogd_r(uint8_t* jit, uint_fast8_t zreg1, uint_fast8_t zreg2, uint_fast8_t zreg3, uint8_t op) { write32(jit, 0x487DF362 ^ ((zreg1 & 8) <<12) ^ ((zreg1 & 16) << 8) ^ ((zreg2 & 15) <<19) ^ ((zreg2 & 16) <<23) ^ ((zreg3 & 24) <<10)); write32(jit+4, 0x00C025 | ((zreg1 & 7) <<11) | ((zreg3 & 7) << 8) | (op<<24)); return 7; } static HEDLEY_ALWAYS_INLINE size_t _jit_vmovdqa32(uint8_t* jit, uint_fast8_t zregD, uint_fast8_t zreg) { write32(jit, 0x487DF162 ^ ((zregD & 8) <<12) ^ ((zregD & 16) << 8) ^ ((zreg & 24) <<10)); write16(jit+4, 0xC06F | ((zregD & 7) <<11) | ((zreg & 7) << 8)); return 6; } static HEDLEY_ALWAYS_INLINE size_t _jit_vmovdqa32_load(uint8_t* jit, uint_fast8_t zreg, uint_fast8_t mreg, int32_t offs) { unsigned offsFlag = (offs != 0 || mreg == 13) << (int)(((offs+128*64) & ~0x3FC0) != 0); write32(jit, 0x487DF162 ^ ((zreg & 8) <<12) ^ ((zreg & 16) << 8) ^ ((mreg & 8) <<10)); write32(jit+4, 0x24006F | ((zreg & 7) <<11) | ((mreg & 7) << 8) | (offsFlag << 14)); int isM12 = (mreg == 12); jit += 6+isM12; if(offsFlag == 2) { write32(jit, offs); return 10+isM12; } else if(offs || mreg == 13) { *jit = (offs>>6); return 7+isM12; } return 6+isM12; } static HEDLEY_ALWAYS_INLINE size_t _jit_vmovdqa32_store(uint8_t* jit, uint_fast8_t mreg, int32_t offs, uint_fast8_t zreg) { unsigned offsFlag = (offs != 0 || mreg == 13) << (int)(((offs+128*64) & ~0x3FC0) != 0); write32(jit, 0x487DF162 ^ ((zreg & 8) <<12) ^ ((zreg & 16) << 8) ^ ((mreg & 8) <<10)); write32(jit+4, 0x24007F | ((zreg & 7) <<11) | ((mreg & 7) << 8) | (offsFlag << 14)); int isM12 = (mreg == 12); jit += 6+isM12; if(offsFlag == 2) { write32(jit, offs); return 10+isM12; } else if(offs || mreg == 13) { *jit = (offs>>6); return 7+isM12; } return 6+isM12; } static HEDLEY_ALWAYS_INLINE size_t _jit_push(uint8_t* jit, uint_fast8_t reg) { jit[0] = 0x50 | reg; return 1; } static HEDLEY_ALWAYS_INLINE size_t _jit_pop(uint8_t* jit, uint_fast8_t reg) { jit[0] = 0x58 | reg; return 1; } static HEDLEY_ALWAYS_INLINE size_t _jit_jmp(uint8_t* jit, uint8_t* addr) { int32_t target = (int32_t)(addr - jit -2); if((target+128) & ~0xFF) { *(jit++) = 0xE9; write32(jit, target -3); return 5; } else { write16(jit, 0xEB | lshift32(target & 0xff, 8)); return 2; } } static HEDLEY_ALWAYS_INLINE size_t _jit_jcc(uint8_t* jit, char op, uint8_t* addr) { int32_t target = (int32_t)(addr - jit -2); if((target+128) & ~0xFF) { *(jit++) = 0x0F; *(jit++) = 0x80 | op; write32(jit, target -4); return 6; } else { write16(jit, 0x70 | op | lshift32(target & 0xff, 8)); return 2; } } static HEDLEY_ALWAYS_INLINE size_t _jit_cmp_r(uint8_t* jit, uint_fast8_t reg, uint_fast8_t reg2) { size_t p = _jit_rxx_pref(&jit, reg, reg2); reg &= 7; reg2 &= 7; write16(jit, 0xC039 | ((uint16_t)reg2 << 11) | ((uint16_t)reg << 8)); return p+2; } static HEDLEY_ALWAYS_INLINE size_t _jit_add_i(uint8_t* jit, uint_fast8_t reg, int32_t val) { size_t p = _jit_rxx_pref(&jit, reg, 0); if((val + 128) & ~0xff) { if(reg == AX) { *jit = 5; jit += 1; write32(jit, val); return p+5; } else { write16(jit, 0xC081 | ((reg&7) << 8)); jit += 2; write32(jit, val); return p+6; } } else { write16(jit, 0xC083 | ((reg&7) << 8)); jit += 2; *jit = (int8_t)val; return p+3; } } /* TODO: consider supporting shorter sequences for sub, xor, and etc */ static HEDLEY_ALWAYS_INLINE size_t _jit_sub_i(uint8_t* jit, uint_fast8_t reg, int32_t val) { size_t p = _jit_rxx_pref(&jit, reg, 0); reg &= 7; write16(jit, 0xC083 | (reg << 8)); jit += 2; write32(jit, val); return p+6; } static HEDLEY_ALWAYS_INLINE size_t _jit_sub_r(uint8_t* jit, uint_fast8_t reg, uint_fast8_t reg2) { size_t p = _jit_rxx_pref(&jit, reg, reg2); reg &= 7; reg2 &= 7; write16(jit, 0xC029 | (reg2 << 11) | (reg << 8)); return p+2; } static HEDLEY_ALWAYS_INLINE size_t _jit_and_i(uint8_t* jit, uint_fast8_t reg, int32_t val) { size_t p = _jit_rxx_pref(&jit, reg, 0); reg &= 7; write16(jit, 0xE081 | (reg << 11)); jit += 2; write32(jit, val); return p+6; } static HEDLEY_ALWAYS_INLINE size_t _jit_xor_r(uint8_t* jit, uint_fast8_t reg, uint_fast8_t reg2) { size_t p = _jit_rxx_pref(&jit, reg, reg2); reg &= 7; reg2 &= 7; write16(jit, 0xC031 | (reg2 << 11) | (reg << 8)); return p+2; } static HEDLEY_ALWAYS_INLINE size_t _jit_xor_m(uint8_t* jit, uint_fast8_t reg, uint_fast8_t mreg, int32_t offs) { size_t p = _jit_rxx_pref(&jit, mreg, reg); p += mreg == 12; reg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x248033 | (reg <<11) | ((mreg&7) << 8)); jit += mreg == 12; write32(jit +2, offs); return p+6; } else if(offs || mreg == 13) { write32(jit, 0x244033 | (reg <<11) | ((mreg&7) << 8)); jit += mreg == 12; jit[2] = (uint8_t)offs; return p+3; } else { write32(jit, 0x240033 | (reg <<11) | ((mreg&7) << 8)); return p+2; } } static HEDLEY_ALWAYS_INLINE size_t _jit_xor_rm(uint8_t* jit, uint_fast8_t mreg, int32_t offs, uint_fast8_t reg) { size_t p = _jit_rxx_pref(&jit, mreg, reg); p += mreg == 12; reg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x248031 | (reg <<11) | ((mreg&7) << 8)); jit += mreg == 12; write32(jit +2, offs); return p+6; } else if(offs || mreg == 13) { write32(jit, 0x244031 | (reg <<11) | ((mreg&7) << 8)); jit += mreg == 12; jit[2] = (uint8_t)offs; return p+3; } else { write32(jit, 0x240031 | (reg <<11) | ((mreg&7) << 8)); return p+2; } } static HEDLEY_ALWAYS_INLINE size_t _jit_mov_i(uint8_t* jit, uint_fast8_t reg, intptr_t val) { #ifdef PLATFORM_AMD64 _jit_rxx_pref(&jit, reg, 0); reg &= 7; if(val > 0x3fffffffLL || val < -0x40000000LL) { write16(jit, 0xB8 | reg); write64(jit +2, val); return 10; } else { write32(jit, 0xC0C7 | (reg << 8)); write32(jit +3, (int32_t)val); return 7; } #else *(jit++) = 0xB8 | reg; write32(jit, (int32_t)val); return 5; #endif } static HEDLEY_ALWAYS_INLINE size_t _jit_mov_r(uint8_t* jit, uint_fast8_t reg, uint_fast8_t reg2) { size_t p = _jit_rxx_pref(&jit, reg, reg2); reg &= 7; reg2 &= 7; write16(jit, 0xC089 | (reg2 << 11) | (reg << 8)); return p+2; } static HEDLEY_ALWAYS_INLINE size_t _jit_mov_load(uint8_t* jit, uint_fast8_t reg, uint_fast8_t mreg, int32_t offs) { size_t p = _jit_rxx_pref(&jit, mreg, reg); p += mreg == 12; reg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x24808B | (reg <<11) | ((mreg&7) << 8)); jit += mreg == 12; write32(jit +2, offs); return p+6; } else if(offs || mreg == 13) { write32(jit, 0x24408B | (reg <<11) | ((mreg&7) << 8)); jit += mreg == 12; jit[2] = (uint8_t)offs; return p+3; } else { write32(jit, 0x24008B | (reg <<11) | ((mreg&7) << 8)); return p+2; } } static HEDLEY_ALWAYS_INLINE size_t _jit_mov_store(uint8_t* jit, uint_fast8_t mreg, int32_t offs, uint_fast8_t reg) { size_t p = _jit_rxx_pref(&jit, mreg, reg); p += mreg == 12; reg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x248089 | (reg <<11) | ((mreg&7) << 8)); jit += mreg == 12; write32(jit +2, offs); return p+6; } else if(offs || mreg == 13) { write32(jit, 0x244089 | (reg <<11) | ((mreg&7) << 8)); jit += mreg == 12; jit[2] = (uint8_t)offs; return p+3; } else { write32(jit, 0x240089 | (reg <<11) | ((mreg&7) << 8)); return p+2; } } static HEDLEY_ALWAYS_INLINE size_t _jit_prefetch_m(uint8_t* jit, uint_fast8_t level, uint_fast8_t mreg, int32_t offs) { assert(level-1 < 3); // use _MM_HINT_T* constants size_t p = _jit_rex_pref(&jit, 0, mreg); p += mreg == 12; mreg &= 7; if((offs+128) & ~0xFF) { write32(jit, 0x2480180F | (level << 19) | (mreg << 16)); jit += mreg == 12; write32(jit +3, offs); return p+7; } else if(offs || mreg == 13) { write32(jit, 0x2440180F | (level << 19) | (mreg << 16)); jit += mreg == 12; jit[3] = (uint8_t)offs; return p+4; } else { write32(jit, 0x2400180F | (level << 19) | (mreg << 16)); return p+3; } } static HEDLEY_ALWAYS_INLINE size_t _jit_nop(uint8_t* jit) { jit[0] = 0x90; return 1; } static HEDLEY_ALWAYS_INLINE size_t _jit_align32(uint8_t* jit) { size_t p = 0; while((intptr_t)jit & 0x1F) { p += _jit_nop(jit++); } return p; } static HEDLEY_ALWAYS_INLINE size_t _jit_ret(uint8_t* jit) { jit[0] = 0xC3; return 1; } typedef struct { void* w; // write pointer void* x; // execute pointer size_t len; } jit_wx_pair; #if defined(_WINDOWS) || defined(__WINDOWS__) || defined(_WIN32) || defined(_WIN64) # ifndef NOMINMAX # define NOMINMAX # endif # include static HEDLEY_ALWAYS_INLINE jit_wx_pair* jit_alloc(size_t len) { void* mem = VirtualAlloc(NULL, len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if(!mem) return NULL; if((uintptr_t)mem & 63) { // allocated page not cacheline aligned? something's not right... VirtualFree(mem, 0, MEM_RELEASE); return NULL; } jit_wx_pair* ret = (jit_wx_pair*)malloc(sizeof(jit_wx_pair)); if(!ret) { VirtualFree(mem, 0, MEM_RELEASE); return NULL; } ret->x = mem; ret->w = mem; ret->len = len; return ret; } static HEDLEY_ALWAYS_INLINE void jit_free(void* mem) { jit_wx_pair* pair = (jit_wx_pair*)mem; VirtualFree(pair->w, 0, MEM_RELEASE); free(mem); } #else # include # ifdef GF16_XORJIT_ENABLE_DUAL_MAPPING # include # include # include # endif static HEDLEY_ALWAYS_INLINE jit_wx_pair* jit_alloc(size_t len) { jit_wx_pair* ret = (jit_wx_pair*)malloc(sizeof(jit_wx_pair)); if(!ret) return NULL; ret->len = len; void* mem = mmap(NULL, len, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | # ifdef MAP_ANONYMOUS MAP_ANONYMOUS, # else MAP_ANON, # endif -1, 0); if(mem) { if((uintptr_t)mem & 63) { // page not cacheline aligned? something's gone wrong... munmap(mem, len); free(ret); return NULL; } ret->w = mem; ret->x = mem; return ret; } // couldn't map W+X page, try dual mapping trick (map aliased W and X pages) #ifdef GF16_XORJIT_ENABLE_DUAL_MAPPING // shm_open requires linking with librt, and seems to import some threading references, so try not to include if not needed char path[128]; snprintf(path, sizeof(path), "/gf16_xorjit_shm_alloc(%lu)", (long)getpid()); int fd = shm_open(path, O_RDWR | O_CREAT | O_EXCL, 0700); if(fd != -1) { shm_unlink(path); if(ftruncate(fd, len) != -1) { ret->w = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); ret->x = mmap(NULL, len, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0); if(ret->w && ret->x && (uintptr_t)(ret->w) & 63 == 0 && (uintptr_t)(ret->x) & 63 == 0) { // success close(fd); return ret; } if(ret->w) munmap(ret->w, len); if(ret->x) munmap(ret->x, len); } close(fd); } #endif free(ret); return NULL; } static HEDLEY_ALWAYS_INLINE void jit_free(void* mem) { jit_wx_pair* pair = (jit_wx_pair*)mem; if(pair->w != pair->x) munmap(pair->x, pair->len); munmap(pair->w, pair->len); free(mem); } #endif #endif /*__GF_XOR_JIT__*/ par2cmdline-turbo-1.4.0/parpar/gf16/xor_jit_stub_masm64.asm000066400000000000000000000061641514221355600235140ustar00rootroot00000000000000IFDEF RAX .code PUBLIC gf16_xor_jit_stub gf16_xor_jit_stub PROC ; setup stack push rbp push rsi lea rax, [rsp-16*12] mov rbp, rax and rax, 0Fh sub rbp, rax ; move registers to what we need them as mov rax, rcx mov rcx, rdx mov rdx, r8 mov rsi, r9 ; save XMM registers movaps [rbp+16*1], xmm6 movaps [rbp+16*2], xmm7 movaps [rbp+16*3], xmm8 movaps [rbp+16*4], xmm9 movaps [rbp+16*5], xmm10 movaps [rbp+16*6], xmm11 movaps [rbp+16*7], xmm12 movaps [rbp+16*8], xmm13 movaps [rbp+16*9], xmm14 movaps [rbp+16*10], xmm15 ; run JIT code call qword ptr [rsp+56] ; restore XMM registers movaps xmm6, [rbp+16*1] movaps xmm7, [rbp+16*2] movaps xmm8, [rbp+16*3] movaps xmm9, [rbp+16*4] movaps xmm10, [rbp+16*5] movaps xmm11, [rbp+16*6] movaps xmm12, [rbp+16*7] movaps xmm13, [rbp+16*8] movaps xmm14, [rbp+16*9] movaps xmm15, [rbp+16*10] pop rsi pop rbp ret gf16_xor_jit_stub ENDP IFDEF YMM0 PUBLIC gf16_xor256_jit_stub gf16_xor256_jit_stub PROC ; setup stack push rbp push rsi lea rax, [rsp-16*12] mov rbp, rax and rax, 0Fh sub rbp, rax ; move registers to what we need them as mov rax, rcx mov rcx, rdx mov rdx, r8 mov rsi, r9 ; save XMM registers vmovaps [rbp+16*1], xmm6 vmovaps [rbp+16*2], xmm7 vmovaps [rbp+16*3], xmm8 vmovaps [rbp+16*4], xmm9 vmovaps [rbp+16*5], xmm10 vmovaps [rbp+16*6], xmm11 vmovaps [rbp+16*7], xmm12 vmovaps [rbp+16*8], xmm13 vmovaps [rbp+16*9], xmm14 vmovaps [rbp+16*10], xmm15 ; run JIT code call qword ptr [rsp+56] ; restore XMM registers vmovaps xmm6, [rbp+16*1] vmovaps xmm7, [rbp+16*2] vmovaps xmm8, [rbp+16*3] vmovaps xmm9, [rbp+16*4] vmovaps xmm10, [rbp+16*5] vmovaps xmm11, [rbp+16*6] vmovaps xmm12, [rbp+16*7] vmovaps xmm13, [rbp+16*8] vmovaps xmm14, [rbp+16*9] vmovaps xmm15, [rbp+16*10] pop rsi pop rbp ret gf16_xor256_jit_stub ENDP PUBLIC gf16_xor256_jit_multi_stub gf16_xor256_jit_multi_stub PROC ; non-volatile regs push rsi push rdi push rbx push r12 push r14 push r15 push rbp ; setup stack lea rax, [rsp-16*12] mov rbp, rax and rax, 0Fh sub rbp, rax ; save XMM registers vmovaps [rbp+16*1], xmm6 vmovaps [rbp+16*2], xmm7 vmovaps [rbp+16*3], xmm8 vmovaps [rbp+16*4], xmm9 vmovaps [rbp+16*5], xmm10 vmovaps [rbp+16*6], xmm11 vmovaps [rbp+16*7], xmm12 vmovaps [rbp+16*8], xmm13 vmovaps [rbp+16*9], xmm14 vmovaps [rbp+16*10], xmm15 ; move registers to what we need them as mov rax, rcx ; dst mov rcx, rdx ; dstEnd mov r12, r9 ; fn ; load src pointers into registers mov rdx, [r8] mov rsi, [r8+8] mov rdi, [r8+16] mov r9 , [r8+32] mov r10, [r8+40] mov r11, [r8+48] mov rbx, [r8+56] mov r14, [r8+64] mov r15, [r8+72] mov r8 , [r8+24] ; run JIT code call r12 ; restore XMM registers vmovaps xmm6, [rbp+16*1] vmovaps xmm7, [rbp+16*2] vmovaps xmm8, [rbp+16*3] vmovaps xmm9, [rbp+16*4] vmovaps xmm10, [rbp+16*5] vmovaps xmm11, [rbp+16*6] vmovaps xmm12, [rbp+16*7] vmovaps xmm13, [rbp+16*8] vmovaps xmm14, [rbp+16*9] vmovaps xmm15, [rbp+16*10] pop rbp pop r15 pop r14 pop r12 pop rbx pop rdi pop rsi ret gf16_xor256_jit_multi_stub ENDP ENDIF ; IFDEF YMM0 ENDIF ; IFDEF RAX END par2cmdline-turbo-1.4.0/parpar/hasher.vcxproj000066400000000000000000000255141514221355600212340ustar00rootroot00000000000000 Debug ARM Debug ARM64 Debug Win32 Release ARM Release ARM64 Release Win32 Debug x64 Release x64 {c4657dcb-7b83-4608-a1d5-df38d37c6fcf} parpar_hasher 10.0 StaticLibrary true v143 Unicode StaticLibrary false v143 true Unicode true false Level3 true _DEBUG;_CONSOLE;PARPAR_ENABLE_HASHER_MD5CRC;%(PreprocessorDefinitions) MultiThreadedDebug true true 4267;%(DisableSpecificWarnings) Console true Level3 true true false NDEBUG;_CONSOLE;PARPAR_ENABLE_HASHER_MD5CRC;%(PreprocessorDefinitions) MultiThreaded true false 4267;%(DisableSpecificWarnings) Console true true true StreamingSIMDExtensions2 StreamingSIMDExtensions2 -mpclmul -msse4.1 %(AdditionalOptions) -mpclmul -msse4.1 %(AdditionalOptions) AdvancedVectorExtensions AdvancedVectorExtensions -mxop -mavx %(AdditionalOptions) -mxop -mavx %(AdditionalOptions) AdvancedVectorExtensions AdvancedVectorExtensions -mpclmul -mavx -mbmi %(AdditionalOptions) -mpclmul -mavx -mbmi %(AdditionalOptions) AdvancedVectorExtensions2 AdvancedVectorExtensions2 AdvancedVectorExtensions512 AdvancedVectorExtensions512 -mavx512f %(AdditionalOptions) -mavx512f %(AdditionalOptions) AdvancedVectorExtensions512 AdvancedVectorExtensions512 -mavx512vl -mavx512bw -mbmi2 -mpclmul -mno-evex512 %(AdditionalOptions) -mavx512vl -mavx512bw -mbmi2 -mpclmul -mno-evex512 %(AdditionalOptions) -march=armv8-a+crc %(AdditionalOptions) -march=armv8-a+crc %(AdditionalOptions) -mfpu=neon %(AdditionalOptions) -mfpu=neon -march=armv8-a+crc %(AdditionalOptions) -march=armv8-a+crc %(AdditionalOptions) -march=armv8-a+sve2 %(AdditionalOptions) par2cmdline-turbo-1.4.0/parpar/hasher/000077500000000000000000000000001514221355600176105ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/parpar/hasher/crc_arm.h000066400000000000000000000030161514221355600213670ustar00rootroot00000000000000#include "../src/platform.h" #include "../src/stdint.h" #if defined(_MSC_VER) && !defined(__clang__) #include #else #include #endif // function unused /* HEDLEY_MALLOC static void* crc_alloc_arm() { uint32_t* mem = (uint32_t*)malloc(4); *mem = 0xffffffff; return mem; } */ static HEDLEY_ALWAYS_INLINE void crc_init_arm(void* crc) { memset(crc, 0xff, sizeof(uint32_t)); } static HEDLEY_ALWAYS_INLINE void crc_process_block_arm(void* HEDLEY_RESTRICT crc, const void* HEDLEY_RESTRICT src) { uint32_t* _crc = (uint32_t*)crc; #ifdef __aarch64__ for(int i=0; i<8; i++) *_crc = __crc32d(*_crc, _LE64(read64((uint64_t *)src + i))); #else for(int i=0; i<16; i++) *_crc = __crc32w(*_crc, _LE32(read32((uint32_t *)src + i))); #endif } static HEDLEY_ALWAYS_INLINE uint32_t crc_finish_arm(void* HEDLEY_RESTRICT state, const void* HEDLEY_RESTRICT src, size_t len) { uint32_t crc = read32(state); uint8_t* src_ = (uint8_t*)src; #ifdef __aarch64__ while (len >= sizeof(uint64_t)) { crc = __crc32d(crc, _LE64(read64(src_))); src_ += sizeof(uint64_t); len -= sizeof(uint64_t); } if (len & sizeof(uint32_t)) { crc = __crc32w(crc, _LE32(read32(src_))); src_ += sizeof(uint32_t); } #else while (len >= sizeof(uint32_t)) { crc = __crc32w(crc, _LE32(read32(src_))); src_ += sizeof(uint32_t); len -= sizeof(uint32_t); } #endif if (len & sizeof(uint16_t)) { crc = __crc32h(crc, _LE16(read16(src_))); src_ += sizeof(uint16_t); } if (len & sizeof(uint8_t)) crc = __crc32b(crc, *src_); return ~crc; } par2cmdline-turbo-1.4.0/parpar/hasher/crc_clmul.h000066400000000000000000000133011514221355600217220ustar00rootroot00000000000000// taken from zlib-ng / Intel's zlib patch, modified to remove zlib dependencies /* * Compute the CRC32 using a parallelized folding approach with the PCLMULQDQ * instruction. * * A white paper describing this algorithm can be found at: * http://www.intel.com/content/dam/www/public/us/en/documents/white-papers/fast-crc-computation-generic-polynomials-pclmulqdq-paper.pdf * * Copyright (C) 2013 Intel Corporation. All rights reserved. * Authors: * Wajdi Feghali * Jim Guilford * Vinodh Gopal * Erdinc Ozturk * Jim Kukunas * * For conditions of distribution and use, see copyright notice in zlib.h */ #include "../src/platform.h" #include "../src/stdint.h" static HEDLEY_ALWAYS_INLINE __m128i double_xor(__m128i a, __m128i b, __m128i c) { #ifdef _CRC_USE_AVX512_ return _mm_ternarylogic_epi32(a, b, c, 0x96); #else a = _mm_xor_si128(a, b); return _mm_xor_si128(a, c); #endif } static HEDLEY_ALWAYS_INLINE __m128i do_one_fold_merge(__m128i src, __m128i data) { const __m128i xmm_fold4 = _mm_set_epi32( 0x00000001, 0x54442bd4, 0x00000001, 0xc6e41596 ); return double_xor( _mm_clmulepi64_si128(src, xmm_fold4, 0x01), data, _mm_clmulepi64_si128(src, xmm_fold4, 0x10) ); } static HEDLEY_ALWAYS_INLINE void crc_init_clmul(void* state) { __m128i* state_ = (__m128i*)state; state_[0] = _mm_cvtsi32_si128(0x9db42487); state_[1] = _mm_setzero_si128(); state_[2] = _mm_setzero_si128(); state_[3] = _mm_setzero_si128(); } // function currently unused /* HEDLEY_MALLOC static void* crc_alloc_clmul() { __m128i* state = (__m128i*)malloc(64); crc_init_clmul(state); return state; } */ static HEDLEY_ALWAYS_INLINE void crc_process_block_clmul(void* HEDLEY_RESTRICT state, const void* HEDLEY_RESTRICT src) { __m128i xmm_t0 = _mm_loadu_si128((__m128i *)src); __m128i xmm_t1 = _mm_loadu_si128((__m128i *)src + 1); __m128i xmm_t2 = _mm_loadu_si128((__m128i *)src + 2); __m128i xmm_t3 = _mm_loadu_si128((__m128i *)src + 3); __m128i* crc = (__m128i*)state; crc[0] = do_one_fold_merge(crc[0], xmm_t0); crc[1] = do_one_fold_merge(crc[1], xmm_t1); crc[2] = do_one_fold_merge(crc[2], xmm_t2); crc[3] = do_one_fold_merge(crc[3], xmm_t3); } extern const unsigned pshufb_shf_table[60]; static uint32_t crc_finish_clmul(void* HEDLEY_RESTRICT state, const void* HEDLEY_RESTRICT src, long len) { __m128i xmm_t0, xmm_t1, xmm_t2, xmm_t3; __m128i crc_fold; uint8_t* _src = (uint8_t*)src; __m128i* crc = (__m128i*)state; if(len >= 48) { xmm_t0 = _mm_loadu_si128((__m128i *)_src); xmm_t1 = _mm_loadu_si128((__m128i *)_src + 1); xmm_t2 = _mm_loadu_si128((__m128i *)_src + 2); xmm_t3 = crc[3]; crc[3] = do_one_fold_merge(crc[2], xmm_t2); crc[2] = do_one_fold_merge(crc[1], xmm_t1); crc[1] = do_one_fold_merge(crc[0], xmm_t0); crc[0] = xmm_t3; } else if(len >= 32) { xmm_t0 = _mm_loadu_si128((__m128i *)_src); xmm_t1 = _mm_loadu_si128((__m128i *)_src + 1); xmm_t2 = crc[2]; xmm_t3 = crc[3]; crc[3] = do_one_fold_merge(crc[1], xmm_t1); crc[2] = do_one_fold_merge(crc[0], xmm_t0); crc[1] = xmm_t3; crc[0] = xmm_t2; } else if(len >= 16) { xmm_t0 = _mm_loadu_si128((__m128i *)_src); xmm_t3 = crc[3]; crc[3] = do_one_fold_merge(crc[0], xmm_t0); crc[0] = crc[1]; crc[1] = crc[2]; crc[2] = xmm_t3; } _src += (len & 48); len &= 15; if(len > 0) { __m128i xmm_shl = _mm_load_si128((__m128i *)pshufb_shf_table + (len - 1)); __m128i xmm_shr = _mm_xor_si128(xmm_shl, _mm_set1_epi8(-128)); #ifdef _CRC_USE_AVX512_ xmm_t0 = _mm_maskz_loadu_epi8(_bzhi_u32(-1, len), _src); #else xmm_t0 = _mm_setzero_si128(); memcpy(&xmm_t0, _src, len); #endif xmm_t1 = _mm_shuffle_epi8(crc[0], xmm_shl); crc[0] = _mm_or_si128( _mm_shuffle_epi8(crc[0], xmm_shr), _mm_shuffle_epi8(crc[1], xmm_shl) ); crc[1] = _mm_or_si128( _mm_shuffle_epi8(crc[1], xmm_shr), _mm_shuffle_epi8(crc[2], xmm_shl) ); crc[2] = _mm_or_si128( _mm_shuffle_epi8(crc[2], xmm_shr), _mm_shuffle_epi8(crc[3], xmm_shl) ); crc[3] = _mm_or_si128( _mm_shuffle_epi8(crc[3], xmm_shr), _mm_shuffle_epi8(xmm_t0, xmm_shl) ); crc[3] = do_one_fold_merge(xmm_t1, crc[3]); } crc_fold = _mm_set_epi32( 0x00000001, 0x751997d0, // rk2 0x00000000, 0xccaa009e // rk1 ); xmm_t0 = double_xor( crc[1], _mm_clmulepi64_si128(crc[0], crc_fold, 0x10), _mm_clmulepi64_si128(crc[0], crc_fold, 0x01) ); xmm_t0 = double_xor( crc[2], _mm_clmulepi64_si128(xmm_t0, crc_fold, 0x10), _mm_clmulepi64_si128(xmm_t0, crc_fold, 0x01) ); xmm_t0 = double_xor( crc[3], _mm_clmulepi64_si128(xmm_t0, crc_fold, 0x10), _mm_clmulepi64_si128(xmm_t0, crc_fold, 0x01) ); crc_fold = _mm_set_epi32( 0x00000001, 0x63cd6124, // rk6 0x00000000, 0xccaa009e // rk5 / rk1 ); xmm_t1 = _mm_xor_si128( _mm_clmulepi64_si128(xmm_t0, crc_fold, 0), _mm_srli_si128(xmm_t0, 8) ); xmm_t0 = _mm_slli_si128(xmm_t1, 4); xmm_t0 = _mm_clmulepi64_si128(xmm_t0, crc_fold, 0x10); #ifdef _CRC_USE_AVX512_ xmm_t0 = _mm_ternarylogic_epi32(xmm_t0, xmm_t1, _mm_set_epi32(0, -1, -1, 0), 0x28); #else xmm_t1 = _mm_and_si128(xmm_t1, _mm_set_epi32(0, -1, -1, 0)); xmm_t0 = _mm_xor_si128(xmm_t0, xmm_t1); #endif crc_fold = _mm_set_epi32( 0x00000001, 0xdb710640, // rk8 0x00000000, 0xf7011641 // rk7 ); xmm_t1 = _mm_clmulepi64_si128(xmm_t0, crc_fold, 0); xmm_t1 = _mm_clmulepi64_si128(xmm_t1, crc_fold, 0x10); #ifdef _CRC_USE_AVX512_ xmm_t1 = _mm_ternarylogic_epi32(xmm_t1, xmm_t0, xmm_t0, 0xC3); // NOT(XOR(t1, t0)) #else xmm_t0 = _mm_xor_si128(xmm_t0, _mm_set_epi32(0, -1, -1, 0)); xmm_t1 = _mm_xor_si128(xmm_t1, xmm_t0); #endif return _mm_extract_epi32(xmm_t1, 2); } par2cmdline-turbo-1.4.0/parpar/hasher/crc_rvzbc.h000066400000000000000000000124151514221355600217410ustar00rootroot00000000000000#include "../src/platform.h" #include "../src/stdint.h" #if __has_include() # include # if __riscv_xlen == 64 # define rv_clmul __riscv_clmul_64 # define rv_clmulh __riscv_clmulh_64 # else # define rv_clmul __riscv_clmul_32 # define rv_clmulh __riscv_clmulh_32 # endif #else static HEDLEY_ALWAYS_INLINE uintptr_t rv_clmul(uintptr_t x, uintptr_t y) { uintptr_t r; __asm__("clmul %0, %1, %2\n" : "=r"(r) : "r"(x), "r"(y) :); return r; } static HEDLEY_ALWAYS_INLINE uintptr_t rv_clmulh(uintptr_t x, uintptr_t y) { uintptr_t r; __asm__("clmulh %0, %1, %2\n" : "=r"(r) : "r"(x), "r"(y) :); return r; } #endif // TODO: test big-endian #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # if __riscv_xlen == 64 # define SWAP __builtin_bswap64 # else # define SWAP __builtin_bswap32 # endif #else # define SWAP(d) (d) #endif static HEDLEY_ALWAYS_INLINE uintptr_t read_partial(const void* p, unsigned sz) { uintptr_t data = 0; memcpy(&data, p, sz); return SWAP(data); } static HEDLEY_ALWAYS_INLINE uintptr_t read_full(const uintptr_t* p) { return SWAP(*p); } #undef SWAP static HEDLEY_ALWAYS_INLINE void crc_init_rvzbc(void* crc) { memset(crc, 0, sizeof(uintptr_t)*3); uintptr_t init = 0x9226f562; // -1 / 2^32 #if __riscv_xlen == 64 init <<= 32; #endif memcpy((uintptr_t*)crc + 3, &init, sizeof(init)); } #if __riscv_xlen == 64 static const uint64_t MUL_HI = 0x15a546366 /*2^224*/, MUL_LO = 0xf1da05aa /*2^288*/; #define CLMULL rv_clmul #define CLMULH rv_clmulh #elif __riscv_xlen == 32 static const uint64_t MUL_HI = 0x140d44a2e /*2^128*/, MUL_LO = 0x1751997d0 /*2^160*/; #define CLMULL(x, k) rv_clmul(x, k & 0xffffffff) #define CLMULH(x, k) (rv_clmulh(x, k & 0xffffffff) ^ (k > 0xffffffffULL ? (x) : 0)) #else #error "Unknown __riscv_xlen" #endif static HEDLEY_ALWAYS_INLINE void crc_process_block_rvzbc(void* HEDLEY_RESTRICT crc, const void* HEDLEY_RESTRICT src) { uintptr_t* accum = (uintptr_t*)crc; uintptr_t* srcW = (uintptr_t*)src; for(int i=0; i<64; i+=sizeof(uintptr_t)*4) { uintptr_t tmpHi, tmpLo; tmpLo = CLMULL(accum[0], MUL_LO) ^ CLMULL(accum[1], MUL_HI); tmpHi = CLMULH(accum[0], MUL_LO) ^ CLMULH(accum[1], MUL_HI); accum[0] = tmpLo ^ read_full(srcW++); accum[1] = tmpHi ^ read_full(srcW++); tmpLo = CLMULL(accum[2], MUL_LO) ^ CLMULL(accum[3], MUL_HI); tmpHi = CLMULH(accum[2], MUL_LO) ^ CLMULH(accum[3], MUL_HI); accum[2] = tmpLo ^ read_full(srcW++); accum[3] = tmpHi ^ read_full(srcW++); } } static HEDLEY_ALWAYS_INLINE uint32_t crc_finish_rvzbc(void* HEDLEY_RESTRICT state, const void* HEDLEY_RESTRICT src, size_t len) { uintptr_t* accum = (uintptr_t*)state; uintptr_t* srcW = (uintptr_t*)src; #if __riscv_xlen == 64 if(len & sizeof(uintptr_t)*4) #else while(len >= sizeof(uintptr_t)*4) #endif { uintptr_t tmpHi, tmpLo; tmpLo = CLMULL(accum[0], MUL_LO) ^ CLMULL(accum[1], MUL_HI); tmpHi = CLMULH(accum[0], MUL_LO) ^ CLMULH(accum[1], MUL_HI); accum[0] = tmpLo ^ read_full(srcW++); accum[1] = tmpHi ^ read_full(srcW++); tmpLo = CLMULL(accum[2], MUL_LO) ^ CLMULL(accum[3], MUL_HI); tmpHi = CLMULH(accum[2], MUL_LO) ^ CLMULH(accum[3], MUL_HI); accum[2] = tmpLo ^ read_full(srcW++); accum[3] = tmpHi ^ read_full(srcW++); #if __riscv_xlen != 64 len -= sizeof(uintptr_t)*4; #endif } if(len & sizeof(uintptr_t)*2) { uintptr_t tmpLo = CLMULL(accum[0], MUL_LO) ^ CLMULL(accum[1], MUL_HI); uintptr_t tmpHi = CLMULH(accum[0], MUL_LO) ^ CLMULH(accum[1], MUL_HI); accum[0] = accum[2]; accum[1] = accum[3]; accum[2] = tmpLo ^ read_full(srcW++); accum[3] = tmpHi ^ read_full(srcW++); } if(len & sizeof(uintptr_t)) { uintptr_t tmpLo = CLMULL(accum[0], MUL_HI); uintptr_t tmpHi = CLMULH(accum[0], MUL_HI); accum[0] = accum[1]; accum[1] = accum[2]; accum[2] = accum[3] ^ tmpLo; accum[3] = tmpHi ^ read_full(srcW++); } size_t tail = len & (sizeof(uintptr_t)-1); if(tail) { unsigned shl = ((sizeof(uintptr_t) - tail) * 8), shr = tail * 8; uintptr_t tmp = accum[0] << shl; uintptr_t tmpLo = CLMULL(tmp, MUL_HI); uintptr_t tmpHi = CLMULH(tmp, MUL_HI); accum[0] = (accum[0] >> shr) | (accum[1] << shl); accum[1] = (accum[1] >> shr) | (accum[2] << shl); accum[2] = (accum[2] >> shr) | (accum[3] << shl); accum[3] = (accum[3] >> shr) | (read_partial(srcW, tail) << shl); accum[2] ^= tmpLo; accum[3] ^= tmpHi; } // done processing: fold everything down #if __riscv_xlen == 64 // fold 0,1 -> 2,3 accum[2] ^= rv_clmul(accum[0], 0x1751997d0) ^ rv_clmul(accum[1], 0xccaa009e); accum[3] ^= rv_clmulh(accum[0], 0x1751997d0) ^ rv_clmulh(accum[1], 0xccaa009e); // fold 2->3 accum[0] = rv_clmulh(accum[2], 0xccaa009e); accum[3] ^= rv_clmul(accum[2], 0xccaa009e); // fold 64b->32b accum[1] = rv_clmul(accum[3] & 0xffffffff, 0x163cd6124); accum[0] ^= accum[1] >> 32; accum[3] = accum[1] ^ (accum[3] >> 32); accum[3] <<= 32; #else // fold 0,1 -> 2,3 accum[2] ^= rv_clmul(accum[0], 0xccaa009e) ^ CLMULL(accum[1], 0x163cd6124); accum[3] ^= rv_clmulh(accum[0], 0xccaa009e) ^ CLMULH(accum[1], 0x163cd6124); // fold 2->3 accum[0] = CLMULH(accum[2], 0x163cd6124); accum[3] ^= CLMULL(accum[2], 0x163cd6124); #endif // reduction accum[3] = CLMULL(accum[3], 0xf7011641); accum[3] = CLMULH(accum[3], 0x1db710640); // maybe consider clmulr for XLEN=32 uint32_t crc = accum[0] ^ accum[3]; return ~crc; } par2cmdline-turbo-1.4.0/parpar/hasher/crc_slice4.h000066400000000000000000000034631514221355600220010ustar00rootroot00000000000000#include "../src/platform.h" #include "../src/stdint.h" // function currently unused /* HEDLEY_MALLOC static void* crc_alloc_slice4() { uint32_t* mem = (uint32_t*)malloc(4); *mem = 0xffffffff; return mem; } */ static HEDLEY_ALWAYS_INLINE void crc_init_slice4(void* crc) { memset(crc, 0xff, sizeof(uint32_t)); } // this is based off Fast CRC32 slice-by-4: https://create.stephan-brumme.com/crc32/ extern const uint32_t Crc32Lookup[4][256]; static HEDLEY_ALWAYS_INLINE uint32_t crc_process_iter_slice4(uint32_t crc, uint32_t current) { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ current ^= (((crc&0xff) << 24) | ((crc&0xff00) << 8) | ((crc>>8) & 0xff00) | ((crc>>24) & 0xff)); return Crc32Lookup[0][ current & 0xFF] ^ Crc32Lookup[1][(current>> 8) & 0xFF] ^ Crc32Lookup[2][(current>>16) & 0xFF] ^ Crc32Lookup[3][(current>>24) & 0xFF]; #else current ^= crc; return Crc32Lookup[0][(current>>24) & 0xFF] ^ Crc32Lookup[1][(current>>16) & 0xFF] ^ Crc32Lookup[2][(current>> 8) & 0xFF] ^ Crc32Lookup[3][ current & 0xFF]; #endif } static HEDLEY_ALWAYS_INLINE void crc_process_block_slice4(void* HEDLEY_RESTRICT state, const void* HEDLEY_RESTRICT src) { uint32_t crc = *(uint32_t*)state; const uint32_t* current = (uint32_t*)src; for(int i=0; i<16; i++) crc = crc_process_iter_slice4(crc, read32(current+i)); *(uint32_t*)state = crc; } static HEDLEY_ALWAYS_INLINE uint32_t crc_finish_slice4(void* HEDLEY_RESTRICT state, const void* HEDLEY_RESTRICT src, size_t len) { uint32_t crc = read32(state); const uint32_t* current = (uint32_t*)src; for(; len >= 4; len -= 4) crc = crc_process_iter_slice4(crc, read32(current++)); const uint8_t* currentChar = (const uint8_t*)current; while(len--) crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *currentChar++]; return ~crc; } par2cmdline-turbo-1.4.0/parpar/hasher/crc_zeropad.c000066400000000000000000000043101514221355600222450ustar00rootroot00000000000000#include "../src/hedley.h" #include "../src/stdint.h" // workaround MSVC complaining "unary minus operator applied to unsigned type, result still unsigned" #define NEGATE(n) (uint32_t)(-((int32_t)(n))) static HEDLEY_ALWAYS_INLINE uint32_t crc_multiply(uint32_t a, uint32_t b) { uint32_t res = 0; for(int i=0; i<31; i++) { res ^= NEGATE(b>>31) & a; a = ((a >> 1) ^ (0xEDB88320 & NEGATE(a&1))); b <<= 1; } res ^= NEGATE(b>>31) & a; return res; } /* clmul version static HEDLEY_ALWAYS_INLINE uint32_t crc_multiply(uint32_t a, uint32_t b) { // do the actual multiply __m128i prod = _mm_clmulepi64_si128(_mm_cvtsi32_si128(a), _mm_cvtsi32_si128(b), 0); // prepare product for reduction prod = _mm_add_epi64(prod, prod); // bit alignment fix, due to CRC32 being bit-reversal prod = _mm_slli_si128(prod, 4); // straddle low/high halves across 64-bit boundary - this provides automatic truncation during reduction // do Barrett reduction back into 32-bit field const __m128i reduction_const = _mm_set_epi32( 1, 0xdb710640, // polynomial * 2 0, 0xf7011641 // 2**63 / polynomial ); __m128i t = _mm_clmulepi64_si128(prod, reduction_const, 0); t = _mm_clmulepi64_si128(t, reduction_const, 0x10); t = _mm_xor_si128(t, prod); return _mm_extract_epi32(t, 2); } */ static const uint32_t crc_power[] = { // pre-computed 2^(2^n), with first 3 entries removed (saves a shift) 0x00800000, 0x00008000, 0xedb88320, 0xb1e6b092, 0xa06a2517, 0xed627dae, 0x88d14467, 0xd7bbfe6a, 0xec447f11, 0x8e7ea170, 0x6427800e, 0x4d47bae0, 0x09fe548f, 0x83852d0f, 0x30362f1a, 0x7b5a9cc3, 0x31fec169, 0x9fec022a, 0x6c8dedc4, 0x15d6874d, 0x5fde7a4e, 0xbad90e37, 0x2e4e5eef, 0x4eaba214, 0xa8a472c0, 0x429a969e, 0x148d302a, 0xc40ba6d0, 0xc4e22c3c, 0x40000000, 0x20000000, 0x08000000 }; /* above table can be computed with int main(void) { uint32_t k = 0x80000000 >> 1; for (size_t i = 0; i < 32+3; ++i) { if(i>2) printf("0x%08x, ", k); k = crc_multiply(k, k); } return 0; } */ uint32_t crc_zeroPad(uint32_t crc, uint64_t zeroPad) { // multiply by 2^(8n) unsigned power = 0; crc = ~crc; while(zeroPad) { if(zeroPad & 1) crc = crc_multiply(crc, crc_power[power]); zeroPad >>= 1; power = (power+1) & 31; } return ~crc; } par2cmdline-turbo-1.4.0/parpar/hasher/crc_zeropad.h000066400000000000000000000003061514221355600222530ustar00rootroot00000000000000#ifndef __CRC_ZEROPAD_H #define __CRC_ZEROPAD_H #include "../src/stdint.h" #ifdef __cplusplus extern "C" #endif uint32_t crc_zeroPad(uint32_t crc, uint64_t zeroPad); #endif /* __CRC_ZEROPAD_H */ par2cmdline-turbo-1.4.0/parpar/hasher/hasher.cpp000066400000000000000000000227211514221355600215720ustar00rootroot00000000000000#include "hasher.h" #include "../src/cpuid.h" #include #ifdef PLATFORM_X86 extern bool hasher_avx10_compatible; #endif struct HasherCpuCap { #ifdef PLATFORM_X86 bool hasSSE2, hasClMul, hasXOP, hasBMI1, hasAVX2, hasAVX512F, hasAVX512VLBW; // AVX512VL also represents AVX10 bool isSmallCore, isLEASlow, isVecRotSlow; HasherCpuCap(bool detect) : hasSSE2(true), hasClMul(true), hasXOP(true), hasBMI1(true), hasAVX2(true), hasAVX512F(true), hasAVX512VLBW(true), isSmallCore(false), isLEASlow(false), isVecRotSlow(false) { if(!detect) return; bool hasAVX = false; int cpuInfo[4]; int cpuInfoX[4]; int family, model; _cpuid(cpuInfo, 1); hasSSE2 = (cpuInfo[3] & 0x4000000); hasClMul = ((cpuInfo[2] & 0x80202) == 0x80202); // SSE4.1 + SSSE3 + CLMUL family = ((cpuInfo[0]>>8) & 0xf) + ((cpuInfo[0]>>16) & 0xff0); model = ((cpuInfo[0]>>4) & 0xf) + ((cpuInfo[0]>>12) & 0xf0); // TODO: check perf on small cores isLEASlow = false; if(family == 6) { // Goldmont also prefers SSE for 2xMD5; a wash on Gracemont, Tremont's preference is unknown isSmallCore = CPU_MODEL_IS_BNL_SLM(model) || CPU_MODEL_IS_GLM(model); // Intel Sandy Bridge to Skylake has slow 3-component LEA isLEASlow = CPU_MODEL_IS_SNB_CNL(model); } else { isSmallCore = CPU_FAMMDL_IS_AMDCAT(family, model); } isVecRotSlow = (family == 0xaf || family == 0xbf); // vector rotate has 2 cycle latency on Zen4/5 hasAVX = false; hasBMI1 = false; hasAVX2 = false; hasAVX512F = false; hasAVX512VLBW = false; #if !defined(_MSC_VER) || _MSC_VER >= 1600 _cpuidX(cpuInfoX, 7, 0); if((cpuInfo[2] & 0x1C000000) == 0x1C000000) { // has AVX + OSXSAVE + XSAVE int xcr = _GET_XCR() & 0xff; if((xcr & 6) == 6) { // AVX enabled hasAVX = true; hasBMI1 = hasAVX && (cpuInfoX[1] & 0x08); hasAVX2 = cpuInfoX[1] & 0x20; if((xcr & 0xE0) == 0xE0) { hasAVX512F = ((cpuInfoX[1] & 0x10000) == 0x10000); hasAVX512VLBW = ((cpuInfoX[1] & 0xC0010100) == 0xC0010100); // AVX512VL + AVX512BW + AVX512F + BMI2 if(hasher_avx10_compatible && !hasAVX512VLBW) { // if compiled with AVX10, it can substitute AVX512VL int cpuInfo2[4]; _cpuidX(cpuInfo2, 7, 1); if(cpuInfo2[3] & 0x80000) { _cpuidX(cpuInfo2, 0x24, 0); hasAVX512VLBW = (cpuInfo2[1] & 0xff) >= 1 /* minimum AVX10.1 */ && cpuInfo2[1] & 0x20000; // AVX10/256 } } } } } #endif _cpuid(cpuInfo, 0x80000001); hasXOP = hasAVX && (cpuInfo[2] & 0x800); } #endif #ifdef PLATFORM_ARM bool hasCRC, hasNEON, hasSVE2; HasherCpuCap(bool detect) : hasCRC(true), hasNEON(true), hasSVE2(true) { if(!detect) return; hasCRC = CPU_HAS_ARMCRC; hasNEON = CPU_HAS_NEON; hasSVE2 = CPU_HAS_SVE2; } #endif #ifdef __riscv bool hasZbkc; HasherCpuCap(bool detect) : hasZbkc(true) { if(!detect) return; hasZbkc = CPU_HAS_Zbkc; } #endif }; void setup_hasher() { // TODO: split this up if(HasherInput_Create) return; set_hasherInput(INHASH_SCALAR); #ifdef PARPAR_ENABLE_HASHER_MD5CRC set_hasherMD5CRC(MD5CRCMETH_SCALAR); #endif #ifdef PLATFORM_X86 struct HasherCpuCap caps(true); if(caps.hasAVX512VLBW && caps.hasClMul && !caps.isVecRotSlow && HasherInput_AVX512::isAvailable) set_hasherInput(INHASH_AVX512); // SSE seems to be faster than scalar on Zen1/2, not Zen3; BMI > SSE on Zen1, unknown on Zen2 else if(caps.hasClMul && !caps.isSmallCore && HasherInput_ClMulScalar::isAvailable) { // Gracemont: SSE > scalar, but SSE ~= BMI if(caps.hasBMI1 && HasherInput_BMI1::isAvailable) set_hasherInput(INHASH_BMI1); else set_hasherInput(INHASH_CRC); } else if(caps.hasClMul && caps.isSmallCore && HasherInput_ClMulSSE::isAvailable) set_hasherInput(INHASH_SIMD_CRC); else if(caps.hasSSE2 && caps.isSmallCore && HasherInput_SSE::isAvailable) // TODO: CPU w/o ClMul might all be small enough set_hasherInput(INHASH_SIMD); # ifdef PARPAR_ENABLE_HASHER_MD5CRC if(caps.hasAVX512VLBW && caps.hasClMul && !caps.isVecRotSlow && MD5CRC_isAvailable_AVX512) set_hasherMD5CRC(MD5CRCMETH_AVX512); else if(caps.isLEASlow && caps.hasClMul && MD5CRC_isAvailable_NoLEA) set_hasherMD5CRC(MD5CRCMETH_NOLEA); // for some reason, single MD5 BMI1 seems to be slower on most cores, except Jaguar... unsure why else if(caps.hasBMI1 && caps.hasClMul && caps.isSmallCore && MD5CRC_isAvailable_BMI1) set_hasherMD5CRC(MD5CRCMETH_BMI1); else if(caps.hasClMul && MD5CRC_isAvailable_ClMul) set_hasherMD5CRC(MD5CRCMETH_PCLMUL); # endif #endif #ifdef PLATFORM_ARM struct HasherCpuCap caps(true); if(caps.hasCRC && HasherInput_ARMCRC::isAvailable) // TODO: fast core only set_hasherInput(INHASH_CRC); else if(caps.hasNEON) { // TODO: slow core only if(caps.hasCRC && HasherInput_NEONCRC::isAvailable) set_hasherInput(INHASH_SIMD_CRC); else if(HasherInput_NEON::isAvailable) set_hasherInput(INHASH_SIMD); } # ifdef PARPAR_ENABLE_HASHER_MD5CRC if(caps.hasCRC && MD5CRC_isAvailable_ARMCRC) set_hasherMD5CRC(MD5CRCMETH_ARMCRC); # endif #endif #ifdef __riscv struct HasherCpuCap caps(true); if(caps.hasZbkc && HasherInput_RVZbc::isAvailable) set_hasherInput(INHASH_CRC); # ifdef PARPAR_ENABLE_HASHER_MD5CRC if(caps.hasZbkc && MD5CRC_isAvailable_RVZbc) set_hasherMD5CRC(MD5CRCMETH_RVZBC); # endif #endif #ifdef PARPAR_ENABLE_HASHER_MULTIMD5 // note that this logic assumes that if a compiler can compile for more advanced ISAs, it supports simpler ones as well # ifdef PLATFORM_X86 if(caps.hasAVX512VLBW && MD5Multi_AVX512_256::isAvailable) HasherMD5Multi_level = caps.hasAVX512F ? MD5MULT_AVX512VL : MD5MULT_AVX10; else if(caps.hasAVX512F && MD5Multi_AVX512::isAvailable) HasherMD5Multi_level = MD5MULT_AVX512F; else if(caps.hasXOP && MD5Multi_XOP::isAvailable) HasherMD5Multi_level = MD5MULT_XOP; // for the only CPU with AVX2 + XOP (Excavator) I imagine XOP works better than AVX2, due to half rate AVX else if(caps.hasAVX2 && MD5Multi_AVX2::isAvailable) HasherMD5Multi_level = MD5MULT_AVX2; else if(caps.hasSSE2 && MD5Multi_SSE::isAvailable) HasherMD5Multi_level = MD5MULT_SSE; else # endif # ifdef PLATFORM_ARM // TODO: if SVE2 width = 128b, prefer NEON? if(caps.hasSVE2 && MD5Multi_SVE2::isAvailable) HasherMD5Multi_level = MD5MULT_SVE2; else if(caps.hasNEON && MD5Multi_NEON::isAvailable) HasherMD5Multi_level = MD5MULT_NEON; else # endif HasherMD5Multi_level = MD5MULT_SCALAR; #endif } std::vector hasherInput_availableMethods(bool checkCpuid) { (void)checkCpuid; std::vector ret; ret.push_back(INHASH_SCALAR); #ifdef PLATFORM_X86 const HasherCpuCap caps(checkCpuid); if(caps.hasClMul) { if(caps.hasAVX512VLBW && HasherInput_AVX512::isAvailable) ret.push_back(INHASH_AVX512); if(caps.hasBMI1 && HasherInput_BMI1::isAvailable) ret.push_back(INHASH_BMI1); if(HasherInput_ClMulSSE::isAvailable) ret.push_back(INHASH_SIMD_CRC); if(HasherInput_ClMulScalar::isAvailable) ret.push_back(INHASH_CRC); } if(caps.hasSSE2 && HasherInput_SSE::isAvailable) ret.push_back(INHASH_SIMD); #endif #ifdef PLATFORM_ARM const HasherCpuCap caps(checkCpuid); if(caps.hasCRC && HasherInput_ARMCRC::isAvailable) ret.push_back(INHASH_CRC); if(caps.hasNEON && HasherInput_NEON::isAvailable) ret.push_back(INHASH_SIMD); if(caps.hasCRC && caps.hasNEON && HasherInput_NEONCRC::isAvailable) ret.push_back(INHASH_SIMD_CRC); #endif #ifdef __riscv const HasherCpuCap caps(checkCpuid); if(caps.hasZbkc && HasherInput_RVZbc::isAvailable) ret.push_back(INHASH_CRC); #endif return ret; } #ifdef PARPAR_ENABLE_HASHER_MD5CRC std::vector hasherMD5CRC_availableMethods(bool checkCpuid, int types) { (void)checkCpuid; std::vector ret; ret.push_back(MD5CRCMETH_SCALAR); #ifdef PLATFORM_X86 const HasherCpuCap caps(checkCpuid); if(caps.hasClMul) { if(caps.hasAVX512VLBW && MD5CRC_isAvailable_AVX512 && (types & HASHER_MD5CRC_TYPE_MD5)) ret.push_back(MD5CRCMETH_AVX512); if(MD5CRC_isAvailable_NoLEA && (types & HASHER_MD5CRC_TYPE_MD5)) ret.push_back(MD5CRCMETH_NOLEA); if(caps.hasBMI1 && MD5CRC_isAvailable_BMI1 && (types & HASHER_MD5CRC_TYPE_MD5)) ret.push_back(MD5CRCMETH_BMI1); if(MD5CRC_isAvailable_ClMul && (types & HASHER_MD5CRC_TYPE_CRC)) ret.push_back(MD5CRCMETH_PCLMUL); } #endif #ifdef PLATFORM_ARM const HasherCpuCap caps(checkCpuid); if(caps.hasCRC && MD5CRC_isAvailable_ARMCRC && (types & HASHER_MD5CRC_TYPE_CRC)) ret.push_back(MD5CRCMETH_ARMCRC); #endif #ifdef __riscv const HasherCpuCap caps(checkCpuid); if(caps.hasZbkc && MD5CRC_isAvailable_RVZbc && (types & HASHER_MD5CRC_TYPE_CRC)) ret.push_back(MD5CRCMETH_RVZBC); #endif return ret; } #endif #ifdef PARPAR_ENABLE_HASHER_MULTIMD5 std::vector hasherMD5Multi_availableMethods(bool checkCpuid) { (void)checkCpuid; std::vector ret; ret.push_back(MD5MULT_SCALAR); #ifdef PLATFORM_X86 const HasherCpuCap caps(checkCpuid); if(caps.hasAVX512VLBW && MD5Multi_AVX512_256::isAvailable) { if(caps.hasAVX512F) ret.push_back(MD5MULT_AVX512VL); ret.push_back(MD5MULT_AVX10); } if(caps.hasAVX512F && MD5Multi_AVX512::isAvailable) ret.push_back(MD5MULT_AVX512F); if(caps.hasXOP && MD5Multi_XOP::isAvailable) ret.push_back(MD5MULT_XOP); if(caps.hasAVX2 && MD5Multi_AVX2::isAvailable) ret.push_back(MD5MULT_AVX2); if(caps.hasSSE2 && MD5Multi_SSE::isAvailable) ret.push_back(MD5MULT_SSE); #endif #ifdef PLATFORM_ARM const HasherCpuCap caps(checkCpuid); if(caps.hasSVE2 && MD5Multi_SVE2::isAvailable) ret.push_back(MD5MULT_SVE2); if(caps.hasNEON && MD5Multi_NEON::isAvailable) ret.push_back(MD5MULT_NEON); #endif return ret; } #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher.h000066400000000000000000000011421514221355600212310ustar00rootroot00000000000000#ifndef __HASHER_H #define __HASHER_H void setup_hasher(); #ifdef PARPAR_ENABLE_HASHER_MD5CRC #include "hasher_md5crc.h" #define HASHER_MD5CRC_TYPE_MD5 1 #define HASHER_MD5CRC_TYPE_CRC 2 std::vector hasherMD5CRC_availableMethods(bool checkCpuid, int types=HASHER_MD5CRC_TYPE_MD5|HASHER_MD5CRC_TYPE_CRC); #endif #include "hasher_input.h" std::vector hasherInput_availableMethods(bool checkCpuid); #ifdef PARPAR_ENABLE_HASHER_MULTIMD5 #include "hasher_md5mb.h" std::vector hasherMD5Multi_availableMethods(bool checkCpuid); #endif #endif /* __HASHER_H */ par2cmdline-turbo-1.4.0/parpar/hasher/hasher_armcrc.cpp000066400000000000000000000040341514221355600231160ustar00rootroot00000000000000#include "../src/platform.h" #if defined(PLATFORM_ARM) && defined(_MSC_VER) && defined(__clang__) && !defined(__ARM_FEATURE_CRC32) // I don't think GYP provides a nice way to detect whether MSVC or clang-cl is being used, but it doesn't use clang-cl by default, so a warning here is probably sufficient HEDLEY_WARNING("CRC32 acceleration is not enabled under ARM clang-cl by default; add `-march=armv8-a+crc` to additional compiler arguments to enable"); #endif // disable CRC on GCC versions with broken arm_acle.h #if defined(__ARM_FEATURE_CRC32) && defined(HEDLEY_GCC_VERSION) # if !defined(__aarch64__) && HEDLEY_GCC_VERSION_CHECK(7,0,0) && !HEDLEY_GCC_VERSION_CHECK(8,1,1) # undef __ARM_FEATURE_CRC32 HEDLEY_WARNING("CRC32 acceleration has been disabled due to broken arm_acle.h shipped in GCC 7.0 - 8.1 [https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81497]. If you need this feature, please use a different compiler or version of GCC"); # endif # if defined(__aarch64__) && HEDLEY_GCC_VERSION_CHECK(9,4,0) && !HEDLEY_GCC_VERSION_CHECK(9,5,0) # undef __ARM_FEATURE_CRC32 HEDLEY_WARNING("CRC32 acceleration has been disabled due to broken arm_acle.h shipped in GCC 9.4 [https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100985]. If you need this feature, please use a different compiler or version of GCC"); # endif #endif #if defined(__ARM_FEATURE_CRC32) && defined(__has_include) # if !__has_include() # undef __ARM_FEATURE_CRC32 HEDLEY_WARNING("CRC32 acceleration has been disabled due to missing arm_acle.h"); # endif #endif #define HasherInput HasherInput_ARMCRC #define CRC32Impl(n) n##_ARMCRC #define MD5CRC(f) MD5CRC_##f##_ARMCRC #define _FNMD5(f) f##_scalar #define _FNMD5x2(f) f##_scalar #define _FNCRC(f) f##_arm #if defined(__ARM_FEATURE_CRC32) || (defined(_M_ARM64) && !defined(__clang__)) // MSVC doesn't support CRC for ARM32 # include "crc_arm.h" # include "md5x2-scalar.h" # include "md5-scalar.h" # include "hasher_input_base.h" # include "hasher_md5crc_base.h" #else # include "hasher_input_stub.h" # include "hasher_md5crc_stub.h" #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_avx2.cpp000066400000000000000000000011121514221355600225210ustar00rootroot00000000000000#include "../src/platform.h" #define MD5Multi MD5Multi_AVX2 #define _FNMD5mb(f) f##_avx2 #define _FNMD5mb2(f) f##_avx2 #define md5mb_base_regions md5mb_regions_avx2 #define md5mb_alignment md5mb_alignment_avx2 #define CLEAR_VEC _mm256_zeroupper() #ifdef __AVX2__ # include "md5mb-sse.h" # include "hasher_md5mb_base.h" #else # include "hasher_md5mb_stub.h" #endif #undef MD5Multi #undef _FNMD5mb2 #define MD5Multi MD5Multi2_AVX2 #define _FNMD5mb2(f) f##2_avx2 #define md5mb_interleave 2 #ifdef __AVX2__ # include "hasher_md5mb_base.h" #else # include "hasher_md5mb_stub.h" #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_avx512.cpp000066400000000000000000000016401514221355600226750ustar00rootroot00000000000000// suppress warning spam in GCC 12.0-12.2 (caused by some AVX512 intrinsics) #include "../src/hedley.h" #if HEDLEY_GCC_VERSION_CHECK(12,0,0) && !HEDLEY_GCC_VERSION_CHECK(12,3,0) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wuninitialized" # pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif #include "../src/platform.h" #define MD5Multi MD5Multi_AVX512 #define _FNMD5mb(f) f##_avx512 #define _FNMD5mb2(f) f##_avx512 #define md5mb_base_regions md5mb_regions_avx512 #define md5mb_alignment md5mb_alignment_avx512 #define CLEAR_VEC _mm256_zeroupper() #ifdef __AVX512F__ # include "md5mb-sse.h" # include "hasher_md5mb_base.h" #else # include "hasher_md5mb_stub.h" #endif #undef MD5Multi #undef _FNMD5mb2 #define MD5Multi MD5Multi2_AVX512 #define _FNMD5mb2(f) f##2_avx512 #define md5mb_interleave 2 #ifdef __AVX512F__ # include "hasher_md5mb_base.h" #else # include "hasher_md5mb_stub.h" #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_avx512vl.cpp000066400000000000000000000032331514221355600232370ustar00rootroot00000000000000#include "../src/platform.h" // despite the name, this does also require AVX512BW #define _CRC_USE_AVX512_ 1 #define HasherInput HasherInput_AVX512 #define MD5SingleVer(f) MD5Single_##f##_AVX512 #define MD5CRC(f) MD5CRC_##f##_AVX512 #define _FNMD5(f) f##_avx512 #define _FNMD5x2(f) f##_avx512 #define _FNCRC(f) f##_clmul #if defined(__AVX512VL__) && defined(__AVX512BW__) # include "crc_clmul.h" # include "md5x2-sse.h" # include "md5-avx512.h" # include "hasher_input_base.h" # include "hasher_md5crc_base.h" #else # include "hasher_input_stub.h" # include "hasher_md5crc_stub.h" #endif #define MD5Multi MD5Multi_AVX512_128 #define _FNMD5mb(f) f##_avx512_128 #define _FNMD5mb2(f) f##_avx512_128 #define md5mb_base_regions md5mb_regions_avx512_128 #define md5mb_alignment md5mb_alignment_avx512_128 #define CLEAR_VEC (void)0 #if defined(__AVX512VL__) && defined(__AVX512BW__) # include "md5mb-sse.h" # include "hasher_md5mb_base.h" #else # include "hasher_md5mb_stub.h" #endif #undef MD5Multi #undef _FNMD5mb #undef _FNMD5mb2 #undef md5mb_alignment #undef CLEAR_VEC #undef md5mb_base_regions #define MD5Multi MD5Multi_AVX512_256 #define _FNMD5mb(f) f##_avx512_256 #define _FNMD5mb2(f) f##_avx512_256 #define md5mb_base_regions md5mb_regions_avx512_256 #define md5mb_alignment md5mb_alignment_avx512_256 #define CLEAR_VEC _mm256_zeroupper() #if defined(__AVX512VL__) && defined(__AVX512BW__) # include "hasher_md5mb_base.h" #else # include "hasher_md5mb_stub.h" #endif #if defined(__AVX512VL__) && defined(__AVX512BW__) && !defined(__EVEX512__) && (defined(__AVX10_1__) || defined(__EVEX256__)) bool hasher_avx10_compatible = true; #else bool hasher_avx10_compatible = false; #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_bmi1.cpp000066400000000000000000000010421514221355600224730ustar00rootroot00000000000000#include "../src/platform.h" #define _MD5_USE_BMI1_ 1 #define HasherInput HasherInput_BMI1 #define MD5SingleVer(f) MD5Single_##f##_BMI1 #define MD5CRC(f) MD5CRC_##f##_BMI1 #define _FNMD5(f) f##_scalar #define _FNMD5x2(f) f##_scalar #define _FNCRC(f) f##_clmul #if defined(__PCLMUL__) && defined(__AVX__) && defined(__BMI__) # include "crc_clmul.h" # include "md5x2-scalar.h" # include "md5-scalar.h" # include "hasher_input_base.h" # include "hasher_md5crc_base.h" #else # include "hasher_input_stub.h" # include "hasher_md5crc_stub.h" #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_clmul.cpp000066400000000000000000000021521514221355600227620ustar00rootroot00000000000000#include "../src/platform.h" #define _FNCRC(f) f##_clmul #define HasherInput HasherInput_ClMulSSE #define _FNMD5x2(f) f##_sse #if defined(__PCLMUL__) && defined(__SSSE3__) && defined(__SSE4_1__) # include "crc_clmul.h" # include "md5x2-sse.h" # include "hasher_input_base.h" #else # include "hasher_input_stub.h" #endif #undef HasherInput #undef _FNMD5x2 #define HasherInput HasherInput_ClMulScalar #define CRC32Impl(n) n##_ClMul #define MD5CRC(f) MD5CRC_##f##_ClMul #define _FNMD5(f) f##_scalar #define _FNMD5x2(f) f##_scalar #if defined(__PCLMUL__) && defined(__SSSE3__) && defined(__SSE4_1__) # include "md5x2-scalar.h" # include "md5-scalar.h" # include "hasher_input_base.h" # include "hasher_md5crc_base.h" #else # include "hasher_input_stub.h" # include "hasher_md5crc_stub.h" #endif #undef MD5CRC #undef _FNMD5 #undef CRC32Impl #define MD5SingleVer(f) MD5Single_##f##_NoLEA #define MD5CRC(f) MD5CRC_##f##_NoLEA #define _FNMD5(f) f##_nolea #if defined(__PCLMUL__) && defined(__SSSE3__) && defined(__SSE4_1__) && defined(MD5_HAS_NOLEA) # include "hasher_md5crc_base.h" #else # include "hasher_md5crc_stub.h" #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_input.cpp000066400000000000000000000026141514221355600230100ustar00rootroot00000000000000#include "hasher_input.h" #include #include "../src/platform.h" IHasherInput*(*HasherInput_Create)() = NULL; HasherInputMethods HasherInput_Method = INHASH_SCALAR; bool set_hasherInput(HasherInputMethods method) { #define SET_HASHER(h, x) if(method == h) { \ if(!x::isAvailable) return false; \ HasherInput_Create = &x::create; \ HasherInput_Method = h; \ return true; \ } SET_HASHER(INHASH_SCALAR, HasherInput_Scalar) #ifdef PLATFORM_X86 SET_HASHER(INHASH_SIMD, HasherInput_SSE) SET_HASHER(INHASH_CRC, HasherInput_ClMulScalar) SET_HASHER(INHASH_SIMD_CRC, HasherInput_ClMulSSE) SET_HASHER(INHASH_BMI1, HasherInput_BMI1) SET_HASHER(INHASH_AVX512, HasherInput_AVX512) #endif #ifdef PLATFORM_ARM SET_HASHER(INHASH_SIMD, HasherInput_NEON) SET_HASHER(INHASH_CRC, HasherInput_ARMCRC) SET_HASHER(INHASH_SIMD_CRC, HasherInput_NEONCRC) #endif #ifdef __riscv SET_HASHER(INHASH_CRC, HasherInput_RVZbc) #endif #undef SET_HASHER return false; } const char* hasherInput_methodName(HasherInputMethods m) { const char* names[] = { "Scalar+Generic", #ifdef PLATFORM_X86 "SSE2+Generic", "Scalar+PCLMUL", "SSE2+PCLMUL", #elif defined(PLATFORM_ARM) "NEON+Generic", "Scalar+ARMCRC", "NEON+ARMCRC", #elif defined(__riscv) "SIMD+Generic", "Scalar+Zbc", "SIMD+Zbc", #else "SIMD+Generic", "Scalar+CRC", "SIMD+CRC", #endif "BMI1+PCLMUL", "AVX512" }; return names[(int)m]; } par2cmdline-turbo-1.4.0/parpar/hasher/hasher_input.h000066400000000000000000000010461514221355600224530ustar00rootroot00000000000000#ifndef __HASHER_INPUT_H #define __HASHER_INPUT_H #include "hasher_input_impl.h" #include enum HasherInputMethods { INHASH_SCALAR, INHASH_SIMD, INHASH_CRC, INHASH_SIMD_CRC, INHASH_BMI1, INHASH_AVX512 }; bool set_hasherInput(HasherInputMethods method); extern IHasherInput*(*HasherInput_Create)(); extern HasherInputMethods HasherInput_Method; const char* hasherInput_methodName(HasherInputMethods m); inline const char* hasherInput_methodName() { return hasherInput_methodName(HasherInput_Method); } #endif /* __HASHER_INPUT_H */ par2cmdline-turbo-1.4.0/parpar/hasher/hasher_input_base.h000066400000000000000000000073071514221355600234530ustar00rootroot00000000000000 #include "hasher_input_impl.h" #include "crc_zeropad.h" #include #include #ifndef MD5_BLOCKSIZE # define MD5_BLOCKSIZE 64 #endif #ifndef HASH2X_BLOCK # define HASH2X_BLOCK 0 # define HASH2X_FILE 1 #endif #ifdef HasherInput #include "md5-final.h" const bool HasherInput::isAvailable = true; HasherInput::HasherInput() { reset(); } void HasherInput::reset() { _FNMD5x2(md5_init_x2)(md5State); tmpLen = 0; posOffset = 0; _FNCRC(crc_init)(crcState); dataLen[HASH2X_BLOCK] = 0; dataLen[HASH2X_FILE] = 0; } #ifndef _MD5x2_UPDATEFN_ATTRIB # define _MD5x2_UPDATEFN_ATTRIB #endif _MD5x2_UPDATEFN_ATTRIB void HasherInput::update(const void* data, size_t len) { dataLen[HASH2X_BLOCK] += len; dataLen[HASH2X_FILE] += len; const char* data_ = (const char*)data; // if there's data in tmp, process one block from there if(tmpLen) { while(1) { // this loop iterates 1-2 times only assert(tmpLen >= posOffset); uint_fast8_t wanted = MD5_BLOCKSIZE+posOffset - tmpLen; if(len < wanted) { memcpy(tmp + tmpLen, data_, len); tmpLen += len; return; } else { // process one block const void* blockData = tmp + posOffset; if(tmpLen <= posOffset) { // the block's hash will come from source data instead of tmp wanted = MD5_BLOCKSIZE - tmpLen; blockData = data_; } memcpy(tmp + tmpLen, data_, wanted); _FNCRC(crc_process_block)(crcState, blockData); _FNMD5x2(md5_update_block_x2)(md5State, blockData, tmp); len -= wanted; data_ += wanted; if(tmpLen > posOffset) { // if both hashes came from tmp, shift file hash across as this won't occur on the next iteration memcpy(tmp, tmp+MD5_BLOCKSIZE, posOffset); tmpLen = posOffset; continue; } } break; } } while(len >= (unsigned)MD5_BLOCKSIZE+posOffset) { _FNCRC(crc_process_block)(crcState, data_ + posOffset); _FNMD5x2(md5_update_block_x2)(md5State, data_ + posOffset, data_); data_ += MD5_BLOCKSIZE; len -= MD5_BLOCKSIZE; } memcpy(tmp, data_, len); tmpLen = len; } _MD5x2_UPDATEFN_ATTRIB void HasherInput::getBlock(void* md5crc, uint64_t zeroPad) { _FNMD5x2(md5_extract_x2)(md5crc, md5State, HASH2X_BLOCK); md5_final_block(md5crc, tmp + posOffset, dataLen[HASH2X_BLOCK], zeroPad); uint32_t crc = _FNCRC(crc_finish)(crcState, tmp + posOffset, dataLen[HASH2X_BLOCK] & (MD5_BLOCKSIZE-1)); crc = crc_zeroPad(crc, zeroPad); uint8_t* _crc = (uint8_t*)md5crc + 16; _crc[0] = crc & 0xff; _crc[1] = (crc >> 8) & 0xff; _crc[2] = (crc >> 16) & 0xff; _crc[3] = (crc >> 24) & 0xff; if(tmpLen >= MD5_BLOCKSIZE) { // push through one block on file hash _FNMD5x2(md5_update_block_x2)(md5State, (const char*)tmp, (const char*)tmp); tmpLen -= MD5_BLOCKSIZE; memcpy(tmp, tmp+MD5_BLOCKSIZE, tmpLen); } _FNMD5x2(md5_init_lane_x2)(md5State, HASH2X_BLOCK); _FNCRC(crc_init)(crcState); posOffset = tmpLen; dataLen[HASH2X_BLOCK] = 0; } _MD5x2_UPDATEFN_ATTRIB void HasherInput::end(void* md5) { if(tmpLen >= MD5_BLOCKSIZE) { // this generally shouldn't happen, as getBlock should handle this case, but we'll deal with it in case the caller doesn't want to use getBlock _FNMD5x2(md5_update_block_x2)(md5State, (const char*)tmp, (const char*)tmp); } _FNMD5x2(md5_extract_x2)(md5, md5State, HASH2X_FILE); md5_final_block(md5, tmp, dataLen[HASH2X_FILE], 0); } #ifdef PARPAR_ENABLE_HASHER_MD5CRC void HasherInput::extractFileMD5(MD5Single& outMD5) { _FNMD5x2(md5_extract_x2)(outMD5.md5State, md5State, HASH2X_FILE); outMD5.dataLen = dataLen[HASH2X_FILE]; if(tmpLen >= MD5_BLOCKSIZE) { MD5Single::_update(outMD5.md5State, tmp, 1); memcpy(outMD5.tmp, tmp + MD5_BLOCKSIZE, tmpLen & (MD5_BLOCKSIZE-1)); } else { memcpy(outMD5.tmp, tmp, tmpLen); } } #endif #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_input_impl.h000066400000000000000000000043111514221355600234720ustar00rootroot00000000000000#ifndef __HASHER_INPUT_IMPL_H #define __HASHER_INPUT_IMPL_H #include "../src/platform.h" #include "../src/stdint.h" #include "../src/hedley.h" #include #include #ifdef PARPAR_ENABLE_HASHER_MD5CRC #include "hasher_md5crc_impl.h" #endif class IHasherInput { protected: uint8_t tmp[128]; // TODO: merge md5/crc for ASM routines (also prevents a lot of duplication w/ inlining) ALIGN_TO(16, char md5State[64]); // large enough to handle all implementations of MD5 state (4x16B) #ifdef PLATFORM_X86 char crcState[64]; // ClMul uses 4x16B state, others use 4B #elif defined(__riscv) char crcState[__riscv_xlen/2]; #else char crcState[4]; #endif uint_fast8_t tmpLen; uint_fast8_t posOffset; uint64_t dataLen[2]; public: virtual void update(const void* data, size_t len) = 0; virtual void getBlock(void* md5crc, uint64_t zeroPad) = 0; virtual void end(void* md5) = 0; virtual void reset() = 0; #ifdef PARPAR_ENABLE_HASHER_MD5CRC virtual void extractFileMD5(MD5Single& outMD5) = 0; #endif virtual ~IHasherInput() {} inline void destroy() { ALIGN_FREE(this); } \ }; #ifdef PARPAR_ENABLE_HASHER_MD5CRC # define __DECL_HASHERINPUT_EXTRACT void extractFileMD5(MD5Single& outMD5); #else # define __DECL_HASHERINPUT_EXTRACT #endif #define __DECL_HASHERINPUT(name) \ class HasherInput_##name : public IHasherInput { \ HasherInput_##name(); \ HasherInput_##name(const HasherInput_##name&); \ HasherInput_##name& operator=(const HasherInput_##name&); \ public: \ static const bool isAvailable; \ static inline HEDLEY_MALLOC IHasherInput* create() { \ HasherInput_##name* ptr; \ ALIGN_ALLOC(ptr, sizeof(HasherInput_##name), 16); \ return new(ptr) HasherInput_##name(); \ } \ void update(const void* data, size_t len); \ void getBlock(void* md5crc, uint64_t zeroPad); \ void end(void* md5); \ void reset(); \ __DECL_HASHERINPUT_EXTRACT \ } __DECL_HASHERINPUT(Scalar); __DECL_HASHERINPUT(SSE); __DECL_HASHERINPUT(ClMulScalar); __DECL_HASHERINPUT(ClMulSSE); __DECL_HASHERINPUT(BMI1); __DECL_HASHERINPUT(AVX512); __DECL_HASHERINPUT(ARMCRC); __DECL_HASHERINPUT(NEON); __DECL_HASHERINPUT(NEONCRC); __DECL_HASHERINPUT(RVZbc); #undef __DECL_HASHERINPUT_EXTRACT #undef __DECL_HASHERINPUT #endif /* __HASHER_INPUT_IMPL_H */ par2cmdline-turbo-1.4.0/parpar/hasher/hasher_input_stub.h000066400000000000000000000005741514221355600235150ustar00rootroot00000000000000#include "hasher_input_impl.h" #ifdef HasherInput const bool HasherInput::isAvailable = false; HasherInput::HasherInput() {} void HasherInput::reset() {} void HasherInput::update(const void*, size_t) {} void HasherInput::getBlock(void*, uint64_t) {} void HasherInput::end(void*) {} #ifdef PARPAR_ENABLE_HASHER_MD5CRC void HasherInput::extractFileMD5(MD5Single&) {} #endif #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_md5crc.cpp000066400000000000000000000077051514221355600230340ustar00rootroot00000000000000#include "hasher_md5crc.h" #include #include "../src/platform.h" #ifdef PARPAR_ENABLE_HASHER_MD5CRC uint32_t(*MD5CRC_Calc)(const void*, size_t, size_t, void*) = NULL; MD5CRCMethods MD5CRC_Method = MD5CRCMETH_SCALAR; uint32_t(*CRC32_Calc)(const void*, size_t) = NULL; MD5CRCMethods CRC32_Method = MD5CRCMETH_SCALAR; bool set_hasherMD5CRC(MD5CRCMethods method) { #define SET_HASHER(h, x, hMd5, hCrc) case h: { \ if(!MD5CRC_isAvailable_##x) return false; \ MD5CRC_Calc = &MD5CRC_Calc_##x; \ MD5CRC_Method = h; \ MD5Single::method = hMd5; \ CRC32_Method = hCrc; \ break; \ } switch(method) { SET_HASHER(MD5CRCMETH_SCALAR, Scalar, MD5CRCMETH_SCALAR, MD5CRCMETH_SCALAR) #ifdef PLATFORM_X86 SET_HASHER(MD5CRCMETH_BMI1, BMI1, MD5CRCMETH_BMI1, MD5CRCMETH_PCLMUL) SET_HASHER(MD5CRCMETH_NOLEA, NoLEA, MD5CRCMETH_NOLEA, MD5CRCMETH_PCLMUL) SET_HASHER(MD5CRCMETH_AVX512, AVX512, MD5CRCMETH_AVX512, MD5CRCMETH_PCLMUL) SET_HASHER(MD5CRCMETH_PCLMUL, ClMul, MD5CRCMETH_SCALAR, MD5CRCMETH_PCLMUL) #endif #ifdef PLATFORM_ARM SET_HASHER(MD5CRCMETH_ARMCRC, ARMCRC, MD5CRCMETH_SCALAR, MD5CRCMETH_ARMCRC) #endif #ifdef __riscv SET_HASHER(MD5CRCMETH_RVZBC, RVZbc, MD5CRCMETH_SCALAR, MD5CRCMETH_RVZBC) #endif default: return false; } #undef SET_HASHER switch(MD5Single::method) { case MD5CRCMETH_AVX512: MD5Single::_update = &MD5Single_update_AVX512; MD5Single::_updateZero = &MD5Single_updateZero_AVX512; break; case MD5CRCMETH_NOLEA: MD5Single::_update = &MD5Single_update_NoLEA; MD5Single::_updateZero = &MD5Single_updateZero_NoLEA; break; case MD5CRCMETH_BMI1: MD5Single::_update = &MD5Single_update_BMI1; MD5Single::_updateZero = &MD5Single_updateZero_BMI1; break; case MD5CRCMETH_SCALAR: MD5Single::_update = &MD5Single_update_Scalar; MD5Single::_updateZero = &MD5Single_updateZero_Scalar; break; default: return false; // shouldn't happen } switch(CRC32_Method) { case MD5CRCMETH_PCLMUL: CRC32_Calc = &CRC32_Calc_ClMul; break; case MD5CRCMETH_ARMCRC: CRC32_Calc = &CRC32_Calc_ARMCRC; break; case MD5CRCMETH_RVZBC: CRC32_Calc = &CRC32_Calc_RVZbc; break; case MD5CRCMETH_SCALAR: CRC32_Calc = &CRC32_Calc_Slice4; break; default: return false; // shouldn't happen } return true; } void(*MD5Single::_update)(uint32_t*, const void*, size_t) = &MD5Single_update_Scalar; void(*MD5Single::_updateZero)(uint32_t*, size_t) = &MD5Single_updateZero_Scalar; MD5CRCMethods MD5Single::method = MD5CRCMETH_SCALAR; const size_t MD5_BLOCKSIZE = 64; void MD5Single::update(const void* data, size_t len) { uint_fast8_t buffered = dataLen & (MD5_BLOCKSIZE-1); dataLen += len; const uint8_t* data_ = (const uint8_t*)data; // if there's data in tmp, process one block from there if(buffered) { uint_fast8_t wanted = MD5_BLOCKSIZE - buffered; if(len < wanted) { memcpy(tmp + buffered, data_, len); return; } memcpy(tmp + buffered, data_, wanted); _update(md5State, tmp, 1); len -= wanted; data_ += wanted; } _update(md5State, data_, len / MD5_BLOCKSIZE); data_ += len & ~(MD5_BLOCKSIZE-1); memcpy(tmp, data_, len & (MD5_BLOCKSIZE-1)); } void MD5Single::updateZero(size_t len) { uint_fast8_t buffered = dataLen & (MD5_BLOCKSIZE-1); dataLen += len; // if there's data in tmp, process one block from there if(buffered) { uint_fast8_t wanted = MD5_BLOCKSIZE - buffered; if(len < wanted) { memset(tmp + buffered, 0, len); return; } memset(tmp + buffered, 0, wanted); _update(md5State, tmp, 1); len -= wanted; } _updateZero(md5State, len / MD5_BLOCKSIZE); memset(tmp, 0, len & (MD5_BLOCKSIZE-1)); } #include "md5-final.h" void MD5Single::end(void* md5) { md5_final_block(md5State, tmp, dataLen, 0); memcpy(md5, md5State, 16); } const char* md5crc_methodName(MD5CRCMethods m) { const char* names[] = { "Generic", // or Slice4 for CRC "BMI1", "NoLEA", "AVX512", "ARMCRC", "PCLMUL", "Zbc" }; return names[(int)m]; } #endif // defined(PARPAR_ENABLE_HASHER_MD5CRC) par2cmdline-turbo-1.4.0/parpar/hasher/hasher_md5crc.h000066400000000000000000000010231514221355600224640ustar00rootroot00000000000000#ifndef __HASHER_MD5CRC_H #define __HASHER_MD5CRC_H #include "hasher_md5crc_impl.h" #include // single hash instances extern uint32_t(*CRC32_Calc)(const void*, size_t); extern MD5CRCMethods CRC32_Method; extern uint32_t(*MD5CRC_Calc)(const void*, size_t, size_t, void*); extern MD5CRCMethods MD5CRC_Method; bool set_hasherMD5CRC(MD5CRCMethods method); const char* md5crc_methodName(MD5CRCMethods m); inline const char* md5crc_methodName() { return md5crc_methodName(MD5CRC_Method); } #endif /* __HASHER_MD5CRC_H */ par2cmdline-turbo-1.4.0/parpar/hasher/hasher_md5crc_base.h000066400000000000000000000052051514221355600234640ustar00rootroot00000000000000 #ifdef PARPAR_ENABLE_HASHER_MD5CRC #include "hasher_md5crc_impl.h" #include "crc_zeropad.h" #include #ifndef MD5_BLOCKSIZE # define MD5_BLOCKSIZE 64 #endif #ifdef MD5SingleVer const bool MD5SingleVer(isAvailable) = true; void MD5SingleVer(update)(uint32_t* md5State, const void* data, size_t blocks) { const uint8_t* blockPtr[] = {(const uint8_t*)data}; uint32_t state[4]; state[0] = md5State[0]; state[1] = md5State[1]; state[2] = md5State[2]; state[3] = md5State[3]; while(blocks--) { _FNMD5(md5_process_block)(state, blockPtr, 0); blockPtr[0] += MD5_BLOCKSIZE; } md5State[0] = state[0]; md5State[1] = state[1]; md5State[2] = state[2]; md5State[3] = state[3]; } void MD5SingleVer(updateZero)(uint32_t* md5State, size_t blocks) { uint8_t data[64] = { 0 }; const uint8_t* blockPtr[] = {data}; uint32_t state[4]; state[0] = md5State[0]; state[1] = md5State[1]; state[2] = md5State[2]; state[3] = md5State[3]; while(blocks--) { _FNMD5(md5_process_block)(state, blockPtr, 0); } md5State[0] = state[0]; md5State[1] = state[1]; md5State[2] = state[2]; md5State[3] = state[3]; } #endif #ifdef MD5CRC #include "md5-final.h" const bool MD5CRC(isAvailable) = true; uint32_t MD5CRC(Calc)(const void* data, size_t length, size_t zeroPad, void* md5) { uint32_t md5State[4] = {0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476}; #ifdef PLATFORM_X86 ALIGN_TO(16, char crcState[64]); // ClMul uses 4x16B state, others use 4B #elif defined(__riscv) ALIGN_TO(8, char crcState[__riscv_xlen/2]); #else char crcState[4]; #endif const uint8_t* blockPtr[] = {(const uint8_t*)data}; size_t origLength = length; _FNCRC(crc_init)(crcState); while(length >= (unsigned)MD5_BLOCKSIZE) { _FNMD5(md5_process_block)(md5State, blockPtr, 0); _FNCRC(crc_process_block)(crcState, blockPtr[0]); blockPtr[0] += MD5_BLOCKSIZE; length -= MD5_BLOCKSIZE; } md5_final_block(md5State, blockPtr[0], origLength, zeroPad); memcpy(md5, md5State, 16); uint32_t crc = _FNCRC(crc_finish)(crcState, blockPtr[0], length); return crc_zeroPad(crc, zeroPad); } #endif #ifdef CRC32Impl const bool CRC32Impl(CRC32_isAvailable) = true; uint32_t CRC32Impl(CRC32_Calc)(const void* data, size_t len) { #ifdef PLATFORM_X86 ALIGN_TO(16, char crcState[64]); // ClMul uses 4x16B state, others use 4B #elif defined(__riscv) ALIGN_TO(8, char crcState[__riscv_xlen/2]); #else char crcState[4]; #endif _FNCRC(crc_init)(crcState); const char* data_ = (const char*)data; while(len >= (unsigned)MD5_BLOCKSIZE) { _FNCRC(crc_process_block)(crcState, data_); data_ += MD5_BLOCKSIZE; len -= MD5_BLOCKSIZE; } return _FNCRC(crc_finish)(crcState, data_, len); } #endif #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_md5crc_impl.h000066400000000000000000000035151514221355600235150ustar00rootroot00000000000000#ifndef __HASHER_MD5CRC_IMPL_H #define __HASHER_MD5CRC_IMPL_H #include "../src/stdint.h" #include enum MD5CRCMethods { MD5CRCMETH_SCALAR, // MD5 MD5CRCMETH_BMI1, MD5CRCMETH_NOLEA, MD5CRCMETH_AVX512, // CRC32 MD5CRCMETH_ARMCRC, MD5CRCMETH_PCLMUL, MD5CRCMETH_RVZBC }; class MD5Single { public: // private internal state, but set by IHasherInput::extractFileMD5 uint8_t tmp[64]; uint32_t md5State[4]; uint64_t dataLen; // private, set by setup_hasher static void(*_update)(uint32_t*, const void*, size_t); static void(*_updateZero)(uint32_t*, size_t); static MD5CRCMethods method; // public, read-only // public interface void reset() { md5State[0] = 0x67452301; md5State[1] = 0xefcdab89; md5State[2] = 0x98badcfe; md5State[3] = 0x10325476; dataLen = 0; } inline MD5Single() { reset(); } void update(const void* data, size_t len); void updateZero(size_t len); void end(void* md5); }; #define __DECL_MD5SINGLE(name) \ void MD5Single_update_##name(uint32_t*, const void*, size_t); \ void MD5Single_updateZero_##name(uint32_t*, size_t); \ extern const bool MD5Single_isAvailable_##name __DECL_MD5SINGLE(Scalar); __DECL_MD5SINGLE(NoLEA); __DECL_MD5SINGLE(BMI1); __DECL_MD5SINGLE(AVX512); #undef __DECL_MD5SINGLE #define __DECL_MD5CRC(name) \ uint32_t MD5CRC_Calc_##name(const void*, size_t, size_t, void*); \ extern const bool MD5CRC_isAvailable_##name __DECL_MD5CRC(Scalar); __DECL_MD5CRC(NoLEA); __DECL_MD5CRC(ClMul); __DECL_MD5CRC(BMI1); __DECL_MD5CRC(AVX512); __DECL_MD5CRC(ARMCRC); __DECL_MD5CRC(RVZbc); #undef __DECL_MD5CRC #define __DECL_CRC32(name) \ uint32_t CRC32_Calc_##name(const void*, size_t); \ extern const bool CRC32_isAvailable_##name __DECL_CRC32(Slice4); __DECL_CRC32(ClMul); //__DECL_CRC32(VClMul); __DECL_CRC32(ARMCRC); __DECL_CRC32(RVZbc); #undef __DECL_CRC32 #endif /* __HASHER_MD5CRC_IMPL_H */ par2cmdline-turbo-1.4.0/parpar/hasher/hasher_md5crc_stub.h000066400000000000000000000010311514221355600235200ustar00rootroot00000000000000#ifdef PARPAR_ENABLE_HASHER_MD5CRC #include "hasher_md5crc_impl.h" #ifdef MD5SingleVer const bool MD5SingleVer(isAvailable) = false; void MD5SingleVer(update)(uint32_t*, const void*, size_t) {} void MD5SingleVer(updateZero)(uint32_t*, size_t) {} #endif #ifdef MD5CRC const bool MD5CRC(isAvailable) = false; uint32_t MD5CRC(Calc)(const void*, size_t, size_t, void*) { return 0; } #endif #ifdef CRC32Impl const bool CRC32Impl(CRC32_isAvailable) = false; uint32_t CRC32Impl(CRC32_Calc)(const void*, size_t) { return 0; } #endif #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_md5mb.cpp000066400000000000000000000144371514221355600226630ustar00rootroot00000000000000#include "hasher_md5mb.h" #include #include "../src/platform.h" #ifdef PARPAR_ENABLE_HASHER_MULTIMD5 MD5MultiLevels HasherMD5Multi_level; void set_hasherMD5MultiLevel(MD5MultiLevels level) { #define SET_LEVEL(h, l) \ if(h::isAvailable) { \ HasherMD5Multi_level = l; \ return; \ } switch(level) { #ifdef PLATFORM_X86 case MD5MULT_AVX512VL: SET_LEVEL(MD5Multi_AVX512_256, MD5MULT_AVX512VL) // fallthrough case MD5MULT_AVX512F: case MD5MULT_AVX10: if(level == MD5MULT_AVX10) { SET_LEVEL(MD5Multi_AVX512, MD5MULT_AVX10) } else { SET_LEVEL(MD5Multi_AVX512, MD5MULT_AVX512F) } // fallthrough case MD5MULT_XOP: SET_LEVEL(MD5Multi_XOP, MD5MULT_XOP) // fallthrough case MD5MULT_AVX2: if(level != MD5MULT_XOP) { SET_LEVEL(MD5Multi_AVX2, MD5MULT_AVX2) } // fallthrough case MD5MULT_SSE: SET_LEVEL(MD5Multi_SSE, MD5MULT_SSE) break; // prevent compiler warnings case MD5MULT_SVE2: case MD5MULT_NEON: #endif #ifdef PLATFORM_ARM case MD5MULT_SVE2: SET_LEVEL(MD5Multi_SVE2, MD5MULT_SVE2) break; case MD5MULT_NEON: SET_LEVEL(MD5Multi_NEON, MD5MULT_NEON) break; // prevent compiler warnings case MD5MULT_AVX10: case MD5MULT_AVX512VL: case MD5MULT_AVX512F: case MD5MULT_XOP: case MD5MULT_AVX2: case MD5MULT_SSE: #endif default: case MD5MULT_SCALAR: break; } #undef SET_LEVEL HasherMD5Multi_level = MD5MULT_SCALAR; } MD5Multi::MD5Multi(int srcCount) { // first, fill ctx with largest supported hasher #define ADD_CTX2(feat) { \ while(srcCount >= MD5Multi2_##feat::getNumRegions()) { \ ctx.push_back(new MD5Multi2_##feat()); \ srcCount -= MD5Multi2_##feat::getNumRegions(); \ } \ } #define ADD_CTX1(feat) { if(MD5Multi_##feat::isAvailable) { \ while(srcCount >= MD5Multi_##feat::getNumRegions()) { \ ctx.push_back(new MD5Multi_##feat()); \ srcCount -= MD5Multi_##feat::getNumRegions(); \ } \ } } #ifdef PLATFORM_X86 # ifdef PLATFORM_AMD64 # define ADD_CTX ADD_CTX2 # else # define ADD_CTX ADD_CTX1 # endif if(HasherMD5Multi_level >= MD5MULT_AVX512F) ADD_CTX(AVX512) else if(HasherMD5Multi_level == MD5MULT_XOP) ADD_CTX(XOP) else if(HasherMD5Multi_level == MD5MULT_AVX2) ADD_CTX(AVX2) else if(HasherMD5Multi_level == MD5MULT_SSE) ADD_CTX(SSE) else # undef ADD_CTX #endif #ifdef PLATFORM_ARM // TODO: if SVE2 width = 128b, prefer NEON? if(HasherMD5Multi_level == MD5MULT_SVE2) ADD_CTX2(SVE2) else if(HasherMD5Multi_level == MD5MULT_NEON) ADD_CTX2(NEON) else #endif ADD_CTX2(Scalar) #undef ADD_CTX2 #undef ADD_CTX1 // now for last hasher, find smallest hasher which covers all remaining if(srcCount) { #define ADD_LAST_CTX(cond, impl) \ if((cond) && srcCount <= impl::getNumRegions()) { \ lastCtxData.resize(impl::getNumRegions()); \ ctx.push_back(new impl()); \ lastCtxDataDup = impl::getNumRegions() - srcCount; \ } ADD_LAST_CTX(1, MD5Multi_Scalar) else ADD_LAST_CTX(1, MD5Multi2_Scalar) #ifdef PLATFORM_X86 // AVX512 hasher would be faster than scalar on Intel, but slower on AMD, so we won't bother trying to do that optimisation else ADD_LAST_CTX(HasherMD5Multi_level == MD5MULT_AVX512VL || HasherMD5Multi_level == MD5MULT_AVX10, MD5Multi_AVX512_128) else ADD_LAST_CTX(HasherMD5Multi_level == MD5MULT_XOP, MD5Multi_XOP) else ADD_LAST_CTX(HasherMD5Multi_level >= MD5MULT_SSE, MD5Multi_SSE) else ADD_LAST_CTX(HasherMD5Multi_level == MD5MULT_AVX512VL || HasherMD5Multi_level == MD5MULT_AVX10, MD5Multi_AVX512_256) else ADD_LAST_CTX(HasherMD5Multi_level >= MD5MULT_AVX2 && HasherMD5Multi_level != MD5MULT_XOP, MD5Multi_AVX2) else ADD_LAST_CTX(HasherMD5Multi_level >= MD5MULT_AVX512F, MD5Multi_AVX512) # ifdef PLATFORM_AMD64 else ADD_LAST_CTX(HasherMD5Multi_level == MD5MULT_XOP, MD5Multi2_XOP) else ADD_LAST_CTX(HasherMD5Multi_level >= MD5MULT_SSE, MD5Multi2_SSE) else ADD_LAST_CTX(HasherMD5Multi_level >= MD5MULT_AVX2 && HasherMD5Multi_level != MD5MULT_XOP, MD5Multi2_AVX2) else ADD_LAST_CTX(HasherMD5Multi_level >= MD5MULT_AVX512F, MD5Multi2_AVX512) # endif #endif #ifdef PLATFORM_ARM else ADD_LAST_CTX(HasherMD5Multi_level == MD5MULT_NEON, MD5Multi_NEON) else ADD_LAST_CTX(HasherMD5Multi_level == MD5MULT_SVE2, MD5Multi_SVE2) else ADD_LAST_CTX(HasherMD5Multi_level == MD5MULT_NEON, MD5Multi2_NEON) else ADD_LAST_CTX(HasherMD5Multi_level == MD5MULT_SVE2, MD5Multi2_SVE2) #endif #undef ADD_LAST_CTX } else { lastCtxDataDup = 0; } } MD5Multi::~MD5Multi() { for(unsigned i=0; iupdate(dataPtr, len); dataPtr += ctx[ctxI]->numRegions; } if(lastCtxDataDup) { // for last hasher, we will need to fill its quota unsigned lastNumRegions = ctx[ctxI]->numRegions-lastCtxDataDup; memcpy(lastCtxData.data(), dataPtr, lastNumRegions * sizeof(void*)); // copy the first region's pointer to all remaining required regions const void** lastData = lastCtxData.data() + lastNumRegions; for(unsigned i=0; iupdate(lastCtxData.data(), len); } else { ctx[ctxI]->update(dataPtr, len); } } void MD5Multi::get1(unsigned index, void* md5) { // TODO: consider better method for(unsigned i=0; inumRegions) { ctx[i]->get1(index, md5); return; } index -= ctx[i]->numRegions; } } void MD5Multi::get(void* md5s) { char* md5_ = (char*)md5s; unsigned ctxI = 0; for(; ctxIget(md5_); md5_ += 16*ctx[ctxI]->numRegions; } if(lastCtxDataDup) { // prevent exceeding destination buffer, which isn't expecting all hashes we'd normally give it // do this by writing to a temporary buffer and copy what's desired across unsigned lastRegions = ctx[ctxI]->numRegions; std::vector tmp(16*lastRegions); ctx[ctxI]->get(tmp.data()); memcpy(md5_, tmp.data(), 16*(lastRegions-lastCtxDataDup)); } else { ctx[ctxI]->get(md5_); } } const char* hasherMD5Multi_methodName(MD5MultiLevels l) { const char* names[] = { "Scalar", "SSE2", "AVX2", "XOP", "AVX10", "AVX512F", "AVX512VL", "NEON", "SVE2" }; return names[(int)l]; } #endif // defined(PARPAR_ENABLE_HASHER_MULTIMD5) par2cmdline-turbo-1.4.0/parpar/hasher/hasher_md5mb.h000066400000000000000000000022771514221355600223270ustar00rootroot00000000000000#ifndef __HASHER_MULTIMD5_H #define __HASHER_MULTIMD5_H #include "hasher_md5mb_impl.h" #include enum MD5MultiLevels { MD5MULT_SCALAR, MD5MULT_SSE, MD5MULT_AVX2, MD5MULT_XOP, MD5MULT_AVX10, MD5MULT_AVX512F, MD5MULT_AVX512VL, MD5MULT_NEON, MD5MULT_SVE2 }; void set_hasherMD5MultiLevel(MD5MultiLevels level); extern MD5MultiLevels HasherMD5Multi_level; const char* hasherMD5Multi_methodName(MD5MultiLevels l); inline const char* hasherMD5Multi_methodName() { return hasherMD5Multi_methodName(HasherMD5Multi_level); } class MD5Multi { std::vector ctx; std::vector lastCtxData; unsigned lastCtxDataDup; // disable copy constructor MD5Multi(const MD5Multi&); MD5Multi& operator=(const MD5Multi&); public: explicit MD5Multi(int srcCount); ~MD5Multi(); void update(const void* const* data, size_t len); void get1(unsigned index, void* md5); void get(void* md5s); inline void end() { for(unsigned i=0; iend(); } inline void reset() { for(unsigned i=0; ireset(); } inline IMD5Multi* _getFirstCtx() const { // only used for testing return ctx[0]; } }; #endif /* __HASHER_MULTIMD5_H */ par2cmdline-turbo-1.4.0/parpar/hasher/hasher_md5mb_base.h000066400000000000000000000042541514221355600233160ustar00rootroot00000000000000#ifdef PARPAR_ENABLE_HASHER_MULTIMD5 #include "hasher_md5mb_impl.h" #include #ifndef MD5_BLOCKSIZE # define MD5_BLOCKSIZE 64 #endif #ifdef MD5Multi #ifdef md5mb_interleave # define md5mb_regions md5mb_base_regions*md5mb_interleave #else # define md5mb_regions md5mb_base_regions #endif const bool MD5Multi::isAvailable = true; int MD5Multi::getNumRegions() { return md5mb_regions; } MD5Multi::MD5Multi() : IMD5Multi(md5mb_regions, md5mb_alignment) { state = _FNMD5mb2(md5_alloc_mb)(); ALIGN_ALLOC(tmp, MD5_BLOCKSIZE*md5mb_regions, md5mb_alignment); tmpPtrs = new const void*[md5mb_regions]; for(unsigned input=0; input < md5mb_regions; input++) tmpPtrs[input] = tmp + MD5_BLOCKSIZE*input; reset(); } MD5Multi::~MD5Multi() { md5_free(state); ALIGN_FREE(tmp); delete[] tmpPtrs; } void MD5Multi::update(const void* const* data, size_t len) { dataLen += len; size_t p = 0; if(tmpLen) { uint_fast8_t wanted = MD5_BLOCKSIZE - tmpLen; uint_fast8_t copy = len < wanted ? len : wanted; for(unsigned input=0; input < md5mb_regions; input++) memcpy(tmp + MD5_BLOCKSIZE*input + tmpLen, data[input], copy); if(len < wanted) { tmpLen += len; return; } // process one block from tmp _FNMD5mb2(md5_update_block_mb)(state, tmpPtrs, 0); p = wanted; } for(; p+(MD5_BLOCKSIZE-1) < len; p+=MD5_BLOCKSIZE) { _FNMD5mb2(md5_update_block_mb)(state, data, p); } CLEAR_VEC; tmpLen = len - p; if(tmpLen) { for(unsigned input=0; input < md5mb_regions; input++) { memcpy(tmp + MD5_BLOCKSIZE*input, (char*)(data[input]) + p, tmpLen); } } } void MD5Multi::end() { _FNMD5mb2(md5_final_block_mb)(state, tmpPtrs, 0, dataLen); CLEAR_VEC; } void MD5Multi::get1(unsigned index, void* md5) { _FNMD5mb(md5_extract_mb)(md5, state, index); CLEAR_VEC; } void MD5Multi::get(void* md5s) { _FNMD5mb(md5_extract_all_mb)(md5s, state, 0); #ifdef md5mb_interleave char* md5_ = (char*)md5s; for(int i=1; i #include "../src/hedley.h" class IMD5Multi { public: virtual void update(const void* const* data, size_t len) = 0; virtual void get1(unsigned index, void* md5) = 0; virtual void get(void* md5s) = 0; virtual void end() = 0; virtual void reset() = 0; virtual ~IMD5Multi() {} const int numRegions; const unsigned alignment; protected: uint8_t* tmp; const void** tmpPtrs; uint8_t tmpLen; uint64_t dataLen; void* state; explicit IMD5Multi(int regions, unsigned align) : numRegions(regions), alignment(align) {} }; #define __DECL_MD5MULTI(name) \ class MD5Multi##name : public IMD5Multi { \ MD5Multi##name(const MD5Multi##name&); \ MD5Multi##name& operator=(const MD5Multi##name&); \ public: \ static const bool isAvailable; \ HEDLEY_CONST static int getNumRegions(); \ MD5Multi##name(); \ ~MD5Multi##name(); \ void update(const void* const* data, size_t len); \ void get1(unsigned index, void* md5); \ void get(void* md5s); \ void end(); \ void reset(); \ } __DECL_MD5MULTI(_Scalar); __DECL_MD5MULTI(2_Scalar); __DECL_MD5MULTI(_SSE); __DECL_MD5MULTI(2_SSE); __DECL_MD5MULTI(_XOP); __DECL_MD5MULTI(2_XOP); __DECL_MD5MULTI(_AVX2); __DECL_MD5MULTI(2_AVX2); __DECL_MD5MULTI(_AVX512_128); __DECL_MD5MULTI(_AVX512_256); __DECL_MD5MULTI(_AVX512); __DECL_MD5MULTI(2_AVX512); __DECL_MD5MULTI(_NEON); __DECL_MD5MULTI(2_NEON); __DECL_MD5MULTI(_SVE2); __DECL_MD5MULTI(2_SVE2); #undef __DECL_MD5MULTI #endif /* __HASHER_MD5MB_IMPL_H */ par2cmdline-turbo-1.4.0/parpar/hasher/hasher_md5mb_stub.h000066400000000000000000000006531514221355600233600ustar00rootroot00000000000000#ifdef PARPAR_ENABLE_HASHER_MULTIMD5 #include "hasher_md5mb_impl.h" #ifdef MD5Multi int MD5Multi::getNumRegions() { return 0; } const bool MD5Multi::isAvailable = false; MD5Multi::MD5Multi() : IMD5Multi(0, 1) {} MD5Multi::~MD5Multi() {} void MD5Multi::update(const void* const*, size_t) {} void MD5Multi::end() {} void MD5Multi::get1(unsigned, void*) {} void MD5Multi::get(void*) {} void MD5Multi::reset() {} #endif #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_neon.cpp000066400000000000000000000014231514221355600226050ustar00rootroot00000000000000#include "../src/platform.h" #include "crc_slice4.h" #define HasherInput HasherInput_NEON #define _FNMD5x2(f) f##_neon #define _FNCRC(f) f##_slice4 #define MD5Multi MD5Multi_NEON #define _FNMD5mb(f) f##_neon #define _FNMD5mb2(f) f##_neon #define md5mb_base_regions md5mb_regions_neon #define md5mb_alignment md5mb_alignment_neon #define CLEAR_VEC (void)0 #ifdef __ARM_NEON # include "md5x2-neon.h" # include "md5mb-neon.h" # include "hasher_input_base.h" # include "hasher_md5mb_base.h" #else # include "hasher_input_stub.h" # include "hasher_md5mb_stub.h" #endif #undef MD5Multi #undef _FNMD5mb2 #define MD5Multi MD5Multi2_NEON #define _FNMD5mb2(f) f##2_neon #define md5mb_interleave 2 #ifdef __ARM_NEON # include "hasher_md5mb_base.h" #else # include "hasher_md5mb_stub.h" #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_neoncrc.cpp000066400000000000000000000016351514221355600233020ustar00rootroot00000000000000#include "../src/platform.h" // disable CRC on GCC versions with broken arm_acle.h #if defined(__ARM_FEATURE_CRC32) && defined(HEDLEY_GCC_VERSION) # if !defined(__aarch64__) && HEDLEY_GCC_VERSION_CHECK(7,0,0) && !HEDLEY_GCC_VERSION_CHECK(8,1,1) # undef __ARM_FEATURE_CRC32 # endif # if defined(__aarch64__) && HEDLEY_GCC_VERSION_CHECK(9,4,0) && !HEDLEY_GCC_VERSION_CHECK(9,5,0) # undef __ARM_FEATURE_CRC32 # endif #endif #if defined(__ARM_FEATURE_CRC32) && defined(__has_include) # if !__has_include() # undef __ARM_FEATURE_CRC32 # endif #endif #define HasherInput HasherInput_NEONCRC #define _FNMD5x2(f) f##_neon #define _FNCRC(f) f##_arm #if (defined(__ARM_FEATURE_CRC32) && defined(__ARM_NEON)) || (defined(_M_ARM64) && !defined(__clang__)) // MSVC doesn't support CRC for ARM32 # include "crc_arm.h" # include "md5x2-neon.h" # include "hasher_input_base.h" #else # include "hasher_input_stub.h" #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_rvzbc.cpp000066400000000000000000000010321514221355600227700ustar00rootroot00000000000000#include "../src/platform.h" #define HasherInput HasherInput_RVZbc #define CRC32Impl(n) n##_RVZbc #define MD5CRC(f) MD5CRC_##f##_RVZbc #define _FNMD5(f) f##_scalar #define _FNMD5x2(f) f##_scalar #define _FNCRC(f) f##_rvzbc #if defined(__riscv) && defined(__GNUC__) && (defined(__riscv_zbkc) || defined(__riscv_zbc)) # include "crc_rvzbc.h" # include "md5x2-scalar.h" # include "md5-scalar.h" # include "hasher_input_base.h" # include "hasher_md5crc_base.h" #else # include "hasher_input_stub.h" # include "hasher_md5crc_stub.h" #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_scalar.cpp000066400000000000000000000015211514221355600231120ustar00rootroot00000000000000#include "../src/platform.h" #include "md5-scalar.h" #include "md5x2-scalar.h" #include "md5mb-scalar.h" #include "crc_slice4.h" #define HasherInput HasherInput_Scalar #define MD5SingleVer(f) MD5Single_##f##_Scalar #define MD5CRC(f) MD5CRC_##f##_Scalar #define CRC32Impl(n) n##_Slice4 #define _FNMD5(f) f##_scalar #define _FNMD5x2(f) f##_scalar #define _FNCRC(f) f##_slice4 #define MD5Multi MD5Multi_Scalar #define _FNMD5mb(f) f##_scalar #define _FNMD5mb2(f) f##_scalar #define md5mb_base_regions md5mb_regions_scalar #define md5mb_alignment md5mb_alignment_scalar #define CLEAR_VEC (void)0 #include "hasher_input_base.h" #include "hasher_md5crc_base.h" #include "hasher_md5mb_base.h" #undef MD5Multi #undef _FNMD5mb2 #define MD5Multi MD5Multi2_Scalar #define _FNMD5mb2(f) f##2_scalar #define md5mb_interleave 2 #include "hasher_md5mb_base.h" par2cmdline-turbo-1.4.0/parpar/hasher/hasher_sse.cpp000066400000000000000000000014041514221355600224370ustar00rootroot00000000000000#include "../src/platform.h" #include "crc_slice4.h" #define HasherInput HasherInput_SSE #define _FNMD5x2(f) f##_sse #define _FNCRC(f) f##_slice4 #define MD5Multi MD5Multi_SSE #define _FNMD5mb(f) f##_sse #define _FNMD5mb2(f) f##_sse #define md5mb_base_regions md5mb_regions_sse #define md5mb_alignment md5mb_alignment_sse #define CLEAR_VEC (void)0 #ifdef __SSE2__ # include "md5x2-sse.h" # include "md5mb-sse.h" # include "hasher_input_base.h" # include "hasher_md5mb_base.h" #else # include "hasher_input_stub.h" # include "hasher_md5mb_stub.h" #endif #undef MD5Multi #undef _FNMD5mb2 #define MD5Multi MD5Multi2_SSE #define _FNMD5mb2(f) f##2_sse #define md5mb_interleave 2 #ifdef __SSE2__ # include "hasher_md5mb_base.h" #else # include "hasher_md5mb_stub.h" #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_sve2.cpp000066400000000000000000000011521514221355600225240ustar00rootroot00000000000000#include "../src/platform.h" #define MD5Multi MD5Multi_SVE2 #define _FNMD5mb(f) f##_sve2 #define _FNMD5mb2(f) f##_sve2 #define md5mb_base_regions md5mb_regions_sve2 #define md5mb_alignment md5mb_alignment_sve2 #define CLEAR_VEC (void)0 #ifdef __ARM_FEATURE_SVE2 # include # include "md5mb-sve2.h" # include "hasher_md5mb_base.h" #else # include "hasher_md5mb_stub.h" #endif #undef MD5Multi #undef _FNMD5mb2 #define MD5Multi MD5Multi2_SVE2 #define _FNMD5mb2(f) f##2_sve2 #define md5mb_interleave 2 #ifdef __ARM_FEATURE_SVE2 # include "hasher_md5mb_base.h" #else # include "hasher_md5mb_stub.h" #endif par2cmdline-turbo-1.4.0/parpar/hasher/hasher_xop.cpp000066400000000000000000000012501514221355600224520ustar00rootroot00000000000000#include "../src/platform.h" #define MD5Multi MD5Multi_XOP #define _FNMD5mb(f) f##_xop #define _FNMD5mb2(f) f##_xop #define md5mb_base_regions md5mb_regions_xop #define md5mb_alignment md5mb_alignment_xop #define CLEAR_VEC (void)0 #if defined(_MSC_VER) && !defined(__clang__) && !defined(__XOP__) && defined(__AVX__) # define __XOP__ 1 #endif #ifdef __XOP__ # include "md5mb-sse.h" # include "hasher_md5mb_base.h" #else # include "hasher_md5mb_stub.h" #endif #undef MD5Multi #undef _FNMD5mb2 #define MD5Multi MD5Multi2_XOP #define _FNMD5mb2(f) f##2_xop #define md5mb_interleave 2 #ifdef __XOP__ # include "hasher_md5mb_base.h" #else # include "hasher_md5mb_stub.h" #endif par2cmdline-turbo-1.4.0/parpar/hasher/md5-arm-asm.h000066400000000000000000000174071514221355600220120ustar00rootroot00000000000000 #ifndef STR # define STR_HELPER(x) #x # define STR(x) STR_HELPER(x) #endif #ifdef PLATFORM_ARM #if __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ # define REV(R) #else // requires ARMv6 # define REV(R) "rev " REG(R) ", " REG(R) "\n" #endif #if !defined(__aarch64__) && (!defined(__clang__) || (defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB < 2)) // GCC refuses to allow >9 registers in Thumb mode; Clang has no qualms, as long as it's Thumb2 # define ARM_THUMB_LIMIT_REGS 1 #endif static HEDLEY_ALWAYS_INLINE void md5_process_block_scalar(uint32_t* HEDLEY_RESTRICT state, const uint8_t* const* HEDLEY_RESTRICT data, size_t offset) { (void)offset; uint32_t A; uint32_t B; uint32_t C; uint32_t D; const uint32_t* HEDLEY_RESTRICT _in = (const uint32_t* HEDLEY_RESTRICT)(data[0]); uint32_t tmp1, tmp2, tmp3; #ifdef __aarch64__ # define SETI_L(dst, x) "mov %w[" STR(dst) "], #" STR(x) "\n" # define SETI_H(dst, x) "movk %w[" STR(dst) "], #" STR(x) ", lsl #16\n" # define REG(x) "%w[" STR(x) "]" # define ROR_ADD(A, B, R) \ "ror %w[" STR(A) "], %w[" STR(A) "], #" STR(R) "\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[" STR(B) "]\n" #else # if defined(__armv7__) || defined(_M_ARM) || defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_8A__) || (defined(__ARM_ARCH) && __ARM_ARCH >= 7) # define SETI_L(dst, x) "movw %[" STR(dst) "], #" STR(x) "\n" # define SETI_H(dst, x) "movt %[" STR(dst) "], #" STR(x) "\n" # else # define SETI_L(dst, x) "mov %[" STR(dst) "], #" STR(x) " & 0xff\n orr %[" STR(dst) "], #" STR(x) " & 0xff00\n" # define SETI_H(dst, x) "orr %[" STR(dst) "], #(" STR(x) " & 0xff)<<16\n orr %[" STR(dst) "], #(" STR(x) " & 0xff00)<<16\n" # endif # define REG(x) "%[" STR(x) "]" # define ROR_ADD(A, B, R) \ "add %[" STR(A) "], %[" STR(B) "], %[" STR(A) "], ror #" STR(R) "\n" #endif #define ROUND_F(IA, A, B, C, D, NEXT_IN, KL, KH, R) \ SETI_L(TMP1, KL) \ "add " REG(A) ", " REG(IA) ", " REG(TMP2) "\n" \ "eor " REG(TMP3) ", " REG(C) ", " REG(D) "\n" \ SETI_H(TMP1, KH) \ "ldr " REG(TMP2) ", " NEXT_IN "\n" \ "and " REG(TMP3) ", " REG(TMP3) ", " REG(B) "\n" \ "add " REG(A) ", " REG(A) ", " REG(TMP1) "\n" \ "eor " REG(TMP3) ", " REG(TMP3) ", " REG(D) "\n" \ "add " REG(A) ", " REG(A) ", " REG(TMP3) "\n" \ REV(TMP2) \ ROR_ADD(A, B, R) #define ROUND_H(A, B, C, D, NEXT_IN, KL, KH, R) \ SETI_L(TMP1, KL) \ "add " REG(A) ", " REG(A) ", " REG(TMP2) "\n" \ "eor " REG(TMP3) ", " REG(C) ", " REG(D) "\n" \ SETI_H(TMP1, KH) \ "ldr " REG(TMP2) ", " NEXT_IN "\n" \ "eor " REG(TMP3) ", " REG(TMP3) ", " REG(B) "\n" \ "add " REG(A) ", " REG(A) ", " REG(TMP1) "\n" \ "add " REG(A) ", " REG(A) ", " REG(TMP3) "\n" \ REV(TMP2) \ ROR_ADD(A, B, R) #define ROUND_I(A, B, C, D, NEXT_IN, KL, KH, R) \ SETI_L(TMP1, KL) \ "add " REG(A) ", " REG(A) ", " REG(TMP2) "\n" \ SETI_H(TMP1, KH) \ "bic " REG(TMP3) ", " REG(D) ", " REG(B) "\n" \ "ldr " REG(TMP2) ", " NEXT_IN "\n" \ "eor " REG(TMP3) ", " REG(TMP3) ", " REG(C) "\n" \ "add " REG(A) ", " REG(A) ", " REG(TMP1) "\n" \ "sub " REG(A) ", " REG(A) ", " REG(TMP3) "\n" \ REV(TMP2) \ ROR_ADD(A, B, R) #define ROUND_I_LAST(A, B, C, D, KL, KH, R) \ SETI_L(TMP1, KL) \ "add " REG(A) ", " REG(A) ", " REG(TMP2) "\n" \ "bic " REG(TMP3) ", " REG(D) ", " REG(B) "\n" \ SETI_H(TMP1, KH) \ "eor " REG(TMP3) ", " REG(TMP3) ", " REG(C) "\n" \ "add " REG(A) ", " REG(A) ", " REG(TMP1) "\n" \ "sub " REG(A) ", " REG(A) ", " REG(TMP3) "\n" \ ROR_ADD(A, B, R) #define ROUND_G(A, B, C, D, NEXT_IN, KL, KH, R) \ SETI_L(TMP1, KL) \ "add " REG(A) ", " REG(A) ", " REG(TMP2) "\n" \ SETI_H(TMP1, KH) \ "ldr " REG(TMP2) ", " NEXT_IN "\n" \ "add " REG(A) ", " REG(A) ", " REG(TMP1) "\n" \ "bic " REG(TMP3) ", " REG(C) ", " REG(D) "\n" \ "and " REG(TMP1) ", " REG(B) ", " REG(D) "\n" \ "add " REG(A) ", " REG(A) ", " REG(TMP3) "\n" \ "add " REG(A) ", " REG(A) ", " REG(TMP1) "\n" \ REV(TMP2) \ ROR_ADD(A, B, R) #define RF4(I, i0, i1, i2, i3, k0l, k0h, k1l, k1h, k2l, k2h, k3l, k3h) \ ROUND_F(I##A, A, I##B, I##C, I##D, "[%[in], #4*" STR(i0) "]", k0l, k0h, 25) \ ROUND_F(I##D, D, A, I##B, I##C, "[%[in], #4*" STR(i1) "]", k1l, k1h, 20) \ ROUND_F(I##C, C, D, A, I##B, "[%[in], #4*" STR(i2) "]", k2l, k2h, 15) \ ROUND_F(I##B, B, C, D, A, "[%[in], #4*" STR(i3) "]", k3l, k3h, 10) #define RG4(i0, i1, i2, i3, k0l, k0h, k1l, k1h, k2l, k2h, k3l, k3h) \ ROUND_G(A, B, C, D, "[%[in], #4*" STR(i0) "]", k0l, k0h, 27) \ ROUND_G(D, A, B, C, "[%[in], #4*" STR(i1) "]", k1l, k1h, 23) \ ROUND_G(C, D, A, B, "[%[in], #4*" STR(i2) "]", k2l, k2h, 18) \ ROUND_G(B, C, D, A, "[%[in], #4*" STR(i3) "]", k3l, k3h, 12) #define RH4(i0, i1, i2, i3, k0l, k0h, k1l, k1h, k2l, k2h, k3l, k3h) \ ROUND_H(A, B, C, D, "[%[in], #4*" STR(i0) "]", k0l, k0h, 28) \ ROUND_H(D, A, B, C, "[%[in], #4*" STR(i1) "]", k1l, k1h, 21) \ ROUND_H(C, D, A, B, "[%[in], #4*" STR(i2) "]", k2l, k2h, 16) \ ROUND_H(B, C, D, A, "[%[in], #4*" STR(i3) "]", k3l, k3h, 9) #define RI4(i0, i1, i2, i3, k0l, k0h, k1l, k1h, k2l, k2h, k3l, k3h) \ ROUND_I(A, B, C, D, "[%[in], #4*" STR(i0) "]", k0l, k0h, 26) \ ROUND_I(D, A, B, C, "[%[in], #4*" STR(i1) "]", k1l, k1h, 22) \ ROUND_I(C, D, A, B, "[%[in], #4*" STR(i2) "]", k2l, k2h, 17) \ ROUND_I(B, C, D, A, "[%[in], #4*" STR(i3) "]", k3l, k3h, 11) #ifdef ARM_THUMB_LIMIT_REGS A = state[0]; B = state[1]; C = state[2]; D = state[3]; #endif __asm__( "ldr " REG(TMP2) ", [%[in]]\n" REV(TMP2) #ifdef ARM_THUMB_LIMIT_REGS RF4(, 1, 2, 3, 4, 0xa478, 0xd76a, 0xb756, 0xe8c7, 0x70db, 0x2420, 0xceee, 0xc1bd) #else RF4(I, 1, 2, 3, 4, 0xa478, 0xd76a, 0xb756, 0xe8c7, 0x70db, 0x2420, 0xceee, 0xc1bd) #endif RF4(, 5, 6, 7, 8, 0x0faf, 0xf57c, 0xc62a, 0x4787, 0x4613, 0xa830, 0x9501, 0xfd46) RF4(, 9, 10, 11, 12, 0x98d8, 0x6980, 0xf7af, 0x8b44, 0x5bb1, 0xffff, 0xd7be, 0x895c) RF4(,13, 14, 15, 1, 0x1122, 0x6b90, 0x7193, 0xfd98, 0x438e, 0xa679, 0x0821, 0x49b4) RG4( 6, 11, 0, 5, 0x2562, 0xf61e, 0xb340, 0xc040, 0x5a51, 0x265e, 0xc7aa, 0xe9b6) RG4(10, 15, 4, 9, 0x105d, 0xd62f, 0x1453, 0x0244, 0xe681, 0xd8a1, 0xfbc8, 0xe7d3) RG4(14, 3, 8, 13, 0xcde6, 0x21e1, 0x07d6, 0xc337, 0x0d87, 0xf4d5, 0x14ed, 0x455a) RG4( 2, 7, 12, 5, 0xe905, 0xa9e3, 0xa3f8, 0xfcef, 0x02d9, 0x676f, 0x4c8a, 0x8d2a) RH4( 8, 11, 14, 1, 0x3942, 0xfffa, 0xf681, 0x8771, 0x6122, 0x6d9d, 0x380c, 0xfde5) RH4( 4, 7, 10, 13, 0xea44, 0xa4be, 0xcfa9, 0x4bde, 0x4b60, 0xf6bb, 0xbc70, 0xbebf) RH4( 0, 3, 6, 9, 0x7ec6, 0x289b, 0x27fa, 0xeaa1, 0x3085, 0xd4ef, 0x1d05, 0x0488) RH4(12, 15, 2, 0, 0xd039, 0xd9d4, 0x99e5, 0xe6db, 0x7cf8, 0x1fa2, 0x5665, 0xc4ac) RI4( 7, 14, 5, 12, 0x2243, 0xf429, 0xff96, 0x432a, 0x23a6, 0xab94, 0xa038, 0xfc93) RI4( 3, 10, 1, 8, 0x59c2, 0x655b, 0xcc91, 0x8f0c, 0xf47c, 0xffef, 0x5dd0, 0x8584) RI4(15, 6, 13, 4, 0x7e4e, 0x6fa8, 0xe6df, 0xfe2c, 0x4313, 0xa301, 0x11a0, 0x4e08) ROUND_I(A, B, C, D, "[%[in], #4*11]", 0x7e81, 0xf753, 26) ROUND_I(D, A, B, C, "[%[in], #4*2]", 0xf234, 0xbd3a, 22) ROUND_I(C, D, A, B, "[%[in], #4*9]", 0xd2ba, 0x2ad7, 17) ROUND_I_LAST(B, C, D, A, 0xd390, 0xeb86, 11) : #ifdef ARM_THUMB_LIMIT_REGS [A]"+&l"(A), [B]"+&l"(B), [C]"+&l"(C), [D]"+&l"(D), #else [A]"=&l"(A), [B]"=&l"(B), [C]"=&l"(C), [D]"=&l"(D), #endif [TMP1]"=&l"(tmp1), [TMP2]"=&l"(tmp2), [TMP3]"=&l"(tmp3) : [in]"r"(_in) // Clang doesn't seem to like "m" references (over allocates registers? causes "assembly requires more registers than available" errors) , "m"(*(const uint32_t (*)[16])_in) // ensure the memory is written before we try to run this #ifndef ARM_THUMB_LIMIT_REGS , [IA]"r"(state[0]), [IB]"r"(state[1]), [IC]"r"(state[2]), [ID]"r"(state[3]) #endif :); state[0] += A; state[1] += B; state[2] += C; state[3] += D; #undef ROUND_F #undef ROUND_G #undef ROUND_H #undef ROUND_I #undef ROUND_I_LAST #undef RF4 #undef RG4 #undef RH4 #undef RI4 #undef SETI_L #undef SETI_H #undef ROR_ADD #undef REG } #endif par2cmdline-turbo-1.4.0/parpar/hasher/md5-arm64-asm.h000066400000000000000000000212461514221355600221600ustar00rootroot00000000000000 #ifndef STR # define STR_HELPER(x) #x # define STR(x) STR_HELPER(x) #endif #if __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ # define REV(R) #else # define REV(R) "rev " R ", " R "\n" #endif static const uint64_t md5_constants_aarch64[32] __attribute__((aligned(8))) = { #define _NUM_PAIR(a,b) 0x##b##a##ULL // F _NUM_PAIR(d76aa478,e8c7b756), _NUM_PAIR(242070db,c1bdceee), _NUM_PAIR(f57c0faf,4787c62a), _NUM_PAIR(a8304613,fd469501), _NUM_PAIR(698098d8,8b44f7af), _NUM_PAIR(ffff5bb1,895cd7be), _NUM_PAIR(6b901122,fd987193), _NUM_PAIR(a679438e,49b40821), // G _NUM_PAIR(f61e2562,c040b340), _NUM_PAIR(265e5a51,e9b6c7aa), _NUM_PAIR(d62f105d,02441453), _NUM_PAIR(d8a1e681,e7d3fbc8), _NUM_PAIR(21e1cde6,c33707d6), _NUM_PAIR(f4d50d87,455a14ed), _NUM_PAIR(a9e3e905,fcefa3f8), _NUM_PAIR(676f02d9,8d2a4c8a), // H _NUM_PAIR(fffa3942,8771f681), _NUM_PAIR(6d9d6122,fde5380c), _NUM_PAIR(a4beea44,4bdecfa9), _NUM_PAIR(f6bb4b60,bebfbc70), _NUM_PAIR(289b7ec6,eaa127fa), _NUM_PAIR(d4ef3085,04881d05), _NUM_PAIR(d9d4d039,e6db99e5), _NUM_PAIR(1fa27cf8,c4ac5665), // I _NUM_PAIR(f4292244,432aff97), _NUM_PAIR(ab9423a7,fc93a039), _NUM_PAIR(655b59c3,8f0ccc92), _NUM_PAIR(ffeff47d,85845dd1), _NUM_PAIR(6fa87e4f,fe2ce6e0), _NUM_PAIR(a3014314,4e0811a1), _NUM_PAIR(f7537e82,bd3af235), _NUM_PAIR(2ad7d2bb,eb86d391) #undef _NUM_PAIR }; static HEDLEY_ALWAYS_INLINE void md5_process_block_scalar(uint32_t* HEDLEY_RESTRICT state, const uint8_t* const* HEDLEY_RESTRICT data, size_t offset) { (void)offset; uint32_t A; uint32_t B; uint32_t C; uint32_t D; const uint32_t* HEDLEY_RESTRICT _in = (const uint32_t* HEDLEY_RESTRICT)(data[0]); uint32_t tmp1, tmp2; uint64_t k0, k1; uint32_t cache[16]; #ifdef __clang__ // "Ump" constraint not supported in Clang 16 [https://github.com/llvm/llvm-project/issues/62769] - so this is a workaround # define LDP_SRC(i) "[%[in], #(" STR(i) "*4)]" # define LDP_REF(offs, ...) [in]"r"(_in + offs), "m"(*(const uint32_t (*)[16])_in) // TODO: investigate if the 'Q' constraint can be used above #else # define LDP_SRC(i) "%[in" STR(i) "]" # define LDP_REF(offs, ...) __VA_ARGS__ #endif #define ASM_PARAMS(x) [A]x(A), [B]x(B), [C]x(C), [D]x(D), \ [TMP1]x(tmp1), [TMP2]x(tmp2), [k0]x(k0), [k1]x(k1) #define ROUND_F(IA, A, B, C, D, I, K, KEXTRA, R, LEXTRA) \ REV(I) \ "add %w[" STR(A) "], %w[" STR(IA) "], " I "\n" \ "eor %w[TMP2], %w[" STR(C) "], %w[" STR(D) "]\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[" K "]\n" \ "and %w[TMP2], %w[TMP2], %w[" STR(B) "]\n" \ KEXTRA "\n" \ "eor %w[TMP2], %w[TMP2], %w[" STR(D) "]\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[TMP2]\n" \ LEXTRA "\n" \ "ror %w[" STR(A) "], %w[" STR(A) "], #" STR(R) "\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[" STR(B) "]\n" #define ROUND_H(A, B, C, D, I, K, KEXTRA, R) \ "eor %w[TMP2], %w[" STR(C) "], %w[" STR(D) "]\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[" K "]\n" \ "eor %w[TMP2], %w[TMP2], %w[" STR(B) "]\n" \ KEXTRA "\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[TMP2]\n" \ "ror %w[" STR(A) "], %w[" STR(A) "], #" STR(R) "\n" \ "add %w[" STR(D) "], %w[" STR(D) "], " I "\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[" STR(B) "]\n" #define ROUND_I(A, B, C, D, I, K, KEXTRA, R) \ "orn %w[TMP2], %w[" STR(B) "], %w[" STR(D) "]\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[" K "]\n" \ "eor %w[TMP2], %w[TMP2], %w[" STR(C) "]\n" \ KEXTRA "\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[TMP2]\n" \ "ror %w[" STR(A) "], %w[" STR(A) "], #" STR(R) "\n" \ "add %w[" STR(D) "], %w[" STR(D) "], " I "\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[" STR(B) "]\n" #define ROUND_G(A, B, C, D, I, K, KEXTRA, R) \ "bic %w[TMP2], %w[" STR(C) "], %w[" STR(D) "]\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[" K "]\n" \ "and %w[TMP1], %w[" STR(B) "], %w[" STR(D) "]\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[TMP2]\n" \ KEXTRA "\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[TMP1]\n" \ "ror %w[" STR(A) "], %w[" STR(A) "], #" STR(R) "\n" \ "add %w[" STR(D) "], %w[" STR(D) "], " I "\n" \ "add %w[" STR(A) "], %w[" STR(A) "], %w[" STR(B) "]\n" #define RF4(i0, i1, i2, i3, i4, i5, kr) \ __asm__( \ ROUND_F(A, A, B, C, D, "%w[cache0]", "k0", "lsr %[k0], %[k0], #32", 25, "ldp %w[cache2], %w[cache3], " LDP_SRC(2)) \ ROUND_F(D, D, A, B, C, "%w[cache1]", "k0", "", 20, "") \ ROUND_F(C, C, D, A, B, "%w[cache2]", "k1", "lsr %[k1], %[k1], #32", 15, "ldp %w[cache4], %w[cache5], " LDP_SRC(4)) \ ROUND_F(B, B, C, D, A, "%w[cache3]", "k1", "ldp %[k0], %[k1], [%[kM], #" STR(kr) "]", 10, "") \ : ASM_PARAMS("+&r"), [cache2]"=&r"(cache[i2]), [cache3]"=&r"(cache[i3]), [cache4]"=&r"(cache[i4]), [cache5]"=&r"(cache[i5]) \ : LDP_REF(i2-2, [in2]"Ump"(_in[i2]), [in4]"Ump"(_in[i4])), [kM]"r"(md5_constants_aarch64), \ [cache0]"r"(cache[i0]), [cache1]"r"(cache[i1]) \ :); #define RG4(i0, i1, i2, i3, kr) \ __asm__( \ ROUND_G(A, B, C, D, "%w[cache0]", "k0", "lsr %[k0], %[k0], #32", 27) \ ROUND_G(D, A, B, C, "%w[cache1]", "k0", "", 23) \ ROUND_G(C, D, A, B, "%w[cache2]", "k1", "lsr %[k1], %[k1], #32", 18) \ ROUND_G(B, C, D, A, "%w[cache3]", "k1", "ldp %[k0], %[k1], [%[kM], #" STR(kr) "]", 12) \ : ASM_PARAMS("+&r") \ : [kM]"r"(md5_constants_aarch64), [cache0]"r"(cache[i0]), [cache1]"r"(cache[i1]), [cache2]"r"(cache[i2]), [cache3]"r"(cache[i3]) \ :); #define RH4(i0, i1, i2, i3, kr) \ __asm__( \ ROUND_H(A, B, C, D, "%w[cache0]", "k0", "lsr %[k0], %[k0], #32", 28) \ ROUND_H(D, A, B, C, "%w[cache1]", "k0", "", 21) \ ROUND_H(C, D, A, B, "%w[cache2]", "k1", "lsr %[k1], %[k1], #32", 16) \ ROUND_H(B, C, D, A, "%w[cache3]", "k1", "ldp %[k0], %[k1], [%[kM], #" STR(kr) "]", 9) \ : ASM_PARAMS("+&r") \ : [kM]"r"(md5_constants_aarch64), [cache0]"r"(cache[i0]), [cache1]"r"(cache[i1]), [cache2]"r"(cache[i2]), [cache3]"r"(cache[i3]) \ :); #define RI4(i0, i1, i2, i3, kr) \ __asm__( \ ROUND_I(A, B, C, D, "%w[cache0]", "k0", "lsr %[k0], %[k0], #32", 26) \ ROUND_I(D, A, B, C, "%w[cache1]", "k0", "", 22) \ ROUND_I(C, D, A, B, "%w[cache2]", "k1", "lsr %[k1], %[k1], #32", 17) \ ROUND_I(B, C, D, A, "%w[cache3]", "k1", "ldp %[k0], %[k1], [%[kM], #" STR(kr) "]", 11) \ : ASM_PARAMS("+&r") \ : [kM]"r"(md5_constants_aarch64), [cache0]"r"(cache[i0]), [cache1]"r"(cache[i1]), [cache2]"r"(cache[i2]), [cache3]"r"(cache[i3]) \ :); __asm__( "ldp %w[cache0], %w[cache1], " LDP_SRC(0) "\n" "ldp %[k0], %[k1], [%[kM]]\n" ROUND_F(IA, A, IB, IC, ID, "%w[cache0]", "k0", "lsr %[k0], %[k0], #32", 25, "ldp %w[cache2], %w[cache3], " LDP_SRC(2)) ROUND_F(ID, D, A, IB, IC, "%w[cache1]", "k0", "", 20, "") ROUND_F(IC, C, D, A, IB, "%w[cache2]", "k1", "lsr %[k1], %[k1], #32", 15, "ldp %w[cache4], %w[cache5], " LDP_SRC(4)) ROUND_F(IB, B, C, D, A, "%w[cache3]", "k1", "ldp %[k0], %[k1], [%[kM], #16]", 10, "") : ASM_PARAMS("=&r"), [cache0]"=&r"(cache[0]), [cache1]"=&r"(cache[1]), [cache2]"=&r"(cache[2]), [cache3]"=&r"(cache[3]), [cache4]"=&r"(cache[4]), [cache5]"=&r"(cache[5]) : LDP_REF(0, [in0]"Ump"(_in[0]), [in2]"Ump"(_in[2]), [in4]"Ump"(_in[4])), [kM]"r"(md5_constants_aarch64), [IA]"r"(state[0]), [IB]"r"(state[1]), [IC]"r"(state[2]), [ID]"r"(state[3]) :); RF4( 4, 5, 6, 7, 8, 9, 32) RF4( 8, 9, 10, 11, 12, 13, 48) __asm__( ROUND_F(A, A, B, C, D, "%w[cache0]", "k0", "lsr %[k0], %[k0], #32", 25, "ldp %w[cache2], %w[cache3], " LDP_SRC(14)) ROUND_F(D, D, A, B, C, "%w[cache1]", "k0", "", 20, "") ROUND_F(C, C, D, A, B, "%w[cache2]", "k1", "lsr %[k1], %[k1], #32", 15, "") ROUND_F(B, B, C, D, A, "%w[cache3]", "k1", "ldp %[k0], %[k1], [%[kM], #64]", 10, "") "add %w[A], %w[A], %w[cacheN]\n" : ASM_PARAMS("+&r"), [cache2]"=&r"(cache[14]), [cache3]"=&r"(cache[15]) : LDP_REF(0, [in14]"Ump"(_in[14])), [kM]"r"(md5_constants_aarch64), [cache0]"r"(cache[12]), [cache1]"r"(cache[13]), [cacheN]"r"(cache[1]) :); RG4( 6, 11, 0, 5, 80) RG4( 10, 15, 4, 9, 96) RG4( 14, 3, 8,13, 112) RG4( 2, 7, 12, 5, 128) RH4( 8, 11, 14, 1, 144) RH4( 4, 7, 10,13, 160) RH4( 0, 3, 6, 9, 176) RH4( 12, 15, 2, 0, 192) RI4( 7, 14, 5,12, 208) RI4( 3, 10, 1, 8, 224) RI4( 15, 6, 13, 4, 240) __asm__( ROUND_I(A, B, C, D, "%w[cache0]", "k0", "lsr %[k0], %[k0], #32", 26) ROUND_I(D, A, B, C, "%w[cache1]", "k0", "", 22) ROUND_I(C, D, A, B, "%w[cache2]", "k1", "lsr %[k1], %[k1], #32", 17) // ROUND_I last "orn %w[TMP2], %w[C], %w[A]\n" "add %w[B], %w[B], %w[k1]\n" "eor %w[TMP2], %w[TMP2], %w[D]\n" "add %w[B], %w[B], %w[TMP2]\n" "ror %w[B], %w[B], #11\n" "add %w[B], %w[B], %w[C]\n" : ASM_PARAMS("+&r") : [kM]"r"(md5_constants_aarch64), [cache0]"r"(cache[11]), [cache1]"r"(cache[2]), [cache2]"r"(cache[9]) :); state[0] += A; state[1] += B; state[2] += C; state[3] += D; #undef ROUND_F #undef ROUND_G #undef ROUND_H #undef ROUND_I #undef RF4 #undef RG4 #undef RH4 #undef RI4 #undef REV } par2cmdline-turbo-1.4.0/parpar/hasher/md5-avx512-asm.h000066400000000000000000000154051514221355600222550ustar00rootroot00000000000000 #ifndef STR # define STR_HELPER(x) #x # define STR(x) STR_HELPER(x) #endif #if defined(__AVX512VL__) && defined(PLATFORM_AMD64) #include static const uint32_t md5_constants_avx512[64] __attribute__((aligned(16))) = { // F 0xd76aa478L, 0xe8c7b756L, 0x242070dbL, 0xc1bdceeeL, 0xf57c0fafL, 0x4787c62aL, 0xa8304613L, 0xfd469501L, 0x698098d8L, 0x8b44f7afL, 0xffff5bb1L, 0x895cd7beL, 0x6b901122L, 0xfd987193L, 0xa679438eL, 0x49b40821L, // G-F 0x124c2332L, 0x0d566e0cL, 0xd8cf331dL, 0x33173e99L, 0xf257ec19L, 0x8ea74a33L, 0x18106d2dL, 0x6a286dd8L, 0xdbd97c15L, 0x969cd637L, 0x0244b8a2L, 0x9d018293L, 0x219a3b68L, 0xac4b7772L, 0x1cbdc448L, 0x8eedde60L, // H-G 0x00ea6050L, 0xaea0c4e2L, 0xc7bcb26dL, 0xe01a22feL, 0x640ad3e1L, 0x29cb28e5L, 0x444769c5L, 0x8f4c4887L, 0x4217e194L, 0xb7f30253L, 0xbc7ba81dL, 0x473f06d1L, 0x59b14d5bL, 0x7eb795c1L, 0x3aae3036L, 0x47009677L, // I-H 0x0987fa4aL, 0xe0c5738dL, 0x662b7c56L, 0xba1d9c0dL, 0xab74aed9L, 0xfc9966f7L, 0x9e79260fL, 0x4c6fb437L, 0xe83687ceL, 0x11b20358L, 0x4130380dL, 0x4f9d9113L, 0x7e7fbfdeL, 0x256c92dbL, 0xadaeeb9bL, 0xde8a69e8L }; static HEDLEY_ALWAYS_INLINE void md5_process_block_avx512(uint32_t* HEDLEY_RESTRICT state, const uint8_t* const* HEDLEY_RESTRICT data, size_t offset) { (void)offset; __m128i A; __m128i B; __m128i C; __m128i D; const __m128i* _in = (const __m128i*)(data[0]); __m128i tmp1, tmp2; __m128i in0, in4, in8, in12; __m128i stateA = _mm_cvtsi32_si128(state[0]); __m128i stateB = _mm_cvtsi32_si128(state[1]); __m128i stateC = _mm_cvtsi32_si128(state[2]); __m128i stateD = _mm_cvtsi32_si128(state[3]); #define ASM_OUTPUTS [A]"+&x"(A), [B]"+&x"(B), [C]"+&x"(C), [D]"+&x"(D), [TMP1]"=&x"(tmp1), [TMP2]"+&x"(tmp2) #define RF4(i) \ "vmovdqu %[input" STR(i) "], %[in" STR(i) "]\n" \ "vpaddd %[k" STR(i) "], %[in" STR(i) "], %[in" STR(i) "]\n" \ ROUND_X(0xd8, A, A, B, C, D, "%[in" STR(i) "]", 25) \ "vpsrlq $32, %[in" STR(i) "], %[TMP1]\n" \ ROUND_X(0xd8, D, D, A, B, C, "%[TMP1]", 20) \ "vpunpckhqdq %[in" STR(i) "], %[in" STR(i) "], %[TMP1]\n" \ ROUND_X(0xd8, C, C, D, A, B, "%[TMP1]", 15) \ "vpsrlq $32, %[TMP1], %[TMP1]\n" \ ROUND_X(0xd8, B, B, C, D, A, "%[TMP1]", 10) #define RF4_FIRST(i) \ "vmovdqu %[input" STR(i) "], %[in" STR(i) "]\n" \ "vpaddd %[k" STR(i) "], %[in" STR(i) "], %[in" STR(i) "]\n" \ ROUND_X(0xd8, IA, A, IB, IC, ID, "%[in" STR(i) "]", 25) \ "vpsrlq $32, %[in" STR(i) "], %[TMP1]\n" \ ROUND_X(0xd8, ID, D, A, IB, IC, "%[TMP1]", 20) \ "vpunpckhqdq %[in" STR(i) "], %[in" STR(i) "], %[TMP1]\n" \ ROUND_X(0xd8, IC, C, D, A, IB, "%[TMP1]", 15) \ "vpsrlq $32, %[TMP1], %[TMP1]\n" \ ROUND_X(0xd8, IB, B, C, D, A, "%[TMP1]", 10) #define RG4(rs, r1, r2) \ "vpsrlq $32, " rs ", %[TMP1]\n" \ ROUND_X(0xac, A, A, B, C, D, "%[TMP1]", 27) \ "vpunpckhqdq " r1 ", " r1 ", %[TMP1]\n" \ ROUND_X(0xac, D, D, A, B, C, "%[TMP1]", 23) \ "vpsrldq $12, " r2 ", %[TMP1]\n" \ ROUND_X(0xac, C, C, D, A, B, "%[TMP1]", 18) \ ROUND_X(0xac, B, B, C, D, A, rs, 12) #define RH4(r1, rs, r2) \ "vpsrlq $32, " r1 ", %[TMP1]\n" \ ROUND_H(A, B, C, D, "%[TMP1]", 28) \ ROUND_H(D, A, B, C, rs, 21) \ "vpsrldq $12, " rs ", %[TMP1]\n" \ ROUND_H(C, D, A, B, "%[TMP1]", 16) \ "vpunpckhqdq " r2 ", " r2 ", %[TMP1]\n" \ ROUND_H(B, C, D, A, "%[TMP1]", 9) #define RI4(r1, rs, r2) \ ROUND_X(0x63, A, A, B, C, D, r1, 26) \ "vpsrldq $12, " rs ", %[TMP1]\n" \ ROUND_X(0x63, D, D, A, B, C, "%[TMP1]", 22) \ "vpunpckhqdq " r2 ", " r2 ", %[TMP1]\n" \ ROUND_X(0x63, C, C, D, A, B, "%[TMP1]", 17) \ "vpsrlq $32, " rs ", %[TMP1]\n" \ ROUND_X(0x63, B, B, C, D, A, "%[TMP1]", 11) #define ROUND_X(T, IA, A, B, C, D, I, R) \ "vpaddd " I ", %[" STR(IA) "], %[" STR(A) "]\n" \ "vpternlogd $" STR(T) ", %[" STR(B) "], %[" STR(C) "], %[TMP2]\n" \ "vpaddd %[TMP2], %[" STR(A) "], %[" STR(A) "]\n" \ "vprord $" STR(R) ", %[" STR(A) "], %[" STR(A) "]\n" \ "vmovdqa %[" STR(C) "], %[TMP2]\n" \ "vpaddd %[" STR(B) "], %[" STR(A) "], %[" STR(A) "]\n" #define ROUND_H(A, B, C, D, I, R) \ "vpaddd " I ", %[" STR(A) "], %[TMP1]\n" \ "vpternlogd $0x96, %[" STR(B) "], %[" STR(A) "], %[TMP2]\n" \ "vpaddd %[TMP2], %[TMP1], %[" STR(A) "]\n" \ "vprord $" STR(R) ", %[" STR(A) "], %[" STR(A) "]\n" \ "vpaddd %[" STR(B) "], %[" STR(A) "], %[" STR(A) "]\n" __asm__( "vmovdqa %[ID], %[TMP2]\n" RF4_FIRST(0) RF4(4) RF4(8) RF4(12) : [A]"=&x"(A), [B]"=&x"(B), [C]"=&x"(C), [D]"=&x"(D), [TMP1]"=&x"(tmp1), [TMP2]"=&x"(tmp2), [in0]"=&x"(in0), [in4]"=&x"(in4), [in8]"=&x"(in8), [in12]"=&x"(in12), // marked as output to prevent bad clobbering by compilers [IA]"+&x"(stateA), [IB]"+&x"(stateB), [IC]"+&x"(stateC), [ID]"+&x"(stateD) : [input0]"m"(_in[0]), [input4]"m"(_in[1]), [input8]"m"(_in[2]), [input12]"m"(_in[3]), [k0]"m"(md5_constants_avx512[0]), [k4]"m"(md5_constants_avx512[4]), [k8]"m"(md5_constants_avx512[8]), [k12]"m"(md5_constants_avx512[12]) :); #define ASM_PARAMS(n) \ ASM_OUTPUTS, [in0]"+&x"(in0), [in4]"+&x"(in4), [in8]"+&x"(in8), [in12]"+&x"(in12) \ : [k0]"m"(md5_constants_avx512[n]), [k1]"m"(md5_constants_avx512[n+4]), [k2]"m"(md5_constants_avx512[n+8]), [k3]"m"(md5_constants_avx512[n+12]) \ : __asm__( "vpaddd %[k0], %[in0], %[in0]\n" "vpaddd %[k1], %[in4], %[in4]\n" "vpaddd %[k2], %[in8], %[in8]\n" RG4("%[in0]", "%[in4]", "%[in8]") "vpaddd %[k3], %[in12], %[in12]\n" RG4("%[in4]", "%[in8]", "%[in12]") RG4("%[in8]", "%[in12]", "%[in0]") RG4("%[in12]", "%[in0]", "%[in4]") : ASM_PARAMS(16)); __asm__( "vpaddd %[k1], %[in4], %[in4]\n" "vpsrlq $32, %[in4], %[TMP1]\n" "vpaddd %[TMP1], %[A], %[A]\n" "vpternlogd $0x96, %[B], %[C], %[TMP2]\n" "vpaddd %[TMP2], %[A], %[A]\n" "vprord $28, %[A], %[A]\n" "vpaddd %[B], %[A], %[A]\n" "vpaddd %[k2], %[in8], %[in8]\n" ROUND_H(D, A, B, C, "%[in8]", 21) "vpsrldq $12, %[in8], %[TMP1]\n" "vpaddd %[k3], %[in12], %[in12]\n" ROUND_H(C, D, A, B, "%[TMP1]", 16) "vpunpckhqdq %[in12], %[in12], %[TMP1]\n" ROUND_H(B, C, D, A, "%[TMP1]", 9) "vpaddd %[k0], %[in0], %[in0]\n" RH4("%[in0]", "%[in4]", "%[in8]") RH4("%[in12]", "%[in0]", "%[in4]") RH4("%[in8]", "%[in12]", "%[in0]") "vmovdqa %[D], %[TMP2]\n" : ASM_PARAMS(32)); __asm__( "vpaddd %[k0], %[in0], %[in0]\n" "vpaddd %[k1], %[in4], %[in4]\n" "vpaddd %[k3], %[in12], %[in12]\n" RI4("%[in0]", "%[in4]", "%[in12]") "vpaddd %[k2], %[in8], %[in8]\n" RI4("%[in12]", "%[in0]", "%[in8]") RI4("%[in8]", "%[in12]", "%[in4]") RI4("%[in4]", "%[in8]", "%[in0]") // contains an unnecessary move on final ROUND_X... oh well : ASM_PARAMS(48)); state[0] = _mm_cvtsi128_si32(_mm_add_epi32(A, stateA)); state[1] = _mm_cvtsi128_si32(_mm_add_epi32(B, stateB)); state[2] = _mm_cvtsi128_si32(_mm_add_epi32(C, stateC)); state[3] = _mm_cvtsi128_si32(_mm_add_epi32(D, stateD)); #undef ROUND_X #undef RF4 #undef RG4 #undef RH4 #undef RI4 #undef ASM_OUTPUTS #undef ASM_PARAMS } #endif par2cmdline-turbo-1.4.0/parpar/hasher/md5-avx512.h000066400000000000000000000053621514221355600215000ustar00rootroot00000000000000 #if (defined(__GNUC__) || defined(__clang__)) && defined(__AVX512VL__) && defined(PLATFORM_AMD64) # define MD5_USE_ASM # include "md5-avx512-asm.h" #endif #define ADD _mm_add_epi32 #define state_word_t uint32_t #define word_t __m128i #define ROTATE _mm_rol_epi32 #define LOAD_STATE(state, word) _mm_cvtsi32_si128(state[word]) // work around missing _mm_storeu_si32: https://stackoverflow.com/questions/58063933/how-can-a-sse2-function-be-missing-from-the-header-it-is-supposed-to-be-in #define SET_STATE(state, word, val) _mm_store_ss((float*)(state + word), _mm_castsi128_ps(val)) #define INPUT(k, set, ptr, offs, idx, var) ( \ idx < 8 ? ( \ idx < 4 ? ( \ idx < 2 ? ( \ idx == 0 ? XX1 : XX2 \ ) : ( \ idx == 2 ? _mm_unpackhi_epi64(XX1, XX1) : _mm_unpackhi_epi64(XX2, XX2) \ ) \ ) : ( \ idx < 6 ? ( \ idx == 4 ? XX5 : XX6 \ ) : ( \ idx == 6 ? _mm_unpackhi_epi64(XX5, XX5) : _mm_unpackhi_epi64(XX6, XX6) \ ) \ ) \ ) : ( \ idx < 12 ? ( \ idx < 10 ? ( \ idx == 8 ? XX9 : XX10 \ ) : ( \ idx == 10 ? _mm_unpackhi_epi64(XX9, XX9) : _mm_unpackhi_epi64(XX10, XX10) \ ) \ ) : ( \ idx < 14 ? ( \ idx == 12 ? XX13 : XX14 \ ) : ( \ idx == 14 ? _mm_unpackhi_epi64(XX13, XX13) : _mm_unpackhi_epi64(XX14, XX14) \ ) \ ) \ ) \ ) #define LOAD INPUT #define LOAD16(set, ptr, offs, var0, var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, var11, var12, var13, var14, var15) { \ var0 = _mm_loadu_si128((__m128i*)(ptr[set])); \ var4 = _mm_loadu_si128((__m128i*)(ptr[set]) + 1); \ var8 = _mm_loadu_si128((__m128i*)(ptr[set]) + 2); \ var12 = _mm_loadu_si128((__m128i*)(ptr[set]) + 3); \ } #define ADD16(var0, var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, var11, var12, var13, var14, var15, k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15) { \ var1 = _mm_add_epi32(var0, _mm_set_epi32(k3, k2, k1, k0)); \ var5 = _mm_add_epi32(var4, _mm_set_epi32(k7, k6, k5, k4)); \ var9 = _mm_add_epi32(var8, _mm_set_epi32(k11, k10, k9, k8)); \ var13 = _mm_add_epi32(var12, _mm_set_epi32(k15, k14, k13, k12)); \ var2 = _mm_srli_epi64(var1, 32); \ var6 = _mm_srli_epi64(var5, 32); \ var10 = _mm_srli_epi64(var9, 32); \ var14 = _mm_srli_epi64(var13, 32); \ } #include #define F(b,c,d) _mm_ternarylogic_epi32(d,c,b,0xD8) #define G(b,c,d) _mm_ternarylogic_epi32(d,c,b,0xAC) #define H(b,c,d) _mm_ternarylogic_epi32(d,c,b,0x96) #define I(b,c,d) _mm_ternarylogic_epi32(d,c,b,0x63) #define FNB(f) f##_avx512 #include "md5-base.h" #undef FNB #undef ADD #undef word_t // state_word_t undef'd by md5-base.h #undef ROTATE #undef LOAD_STATE #undef SET_STATE #undef INPUT #undef LOAD #undef LOAD16 #undef ADD16 #undef F #undef G #undef H #undef I #ifdef MD5_USE_ASM # undef MD5_USE_ASM #endif par2cmdline-turbo-1.4.0/parpar/hasher/md5-base.h000066400000000000000000000257441514221355600213720ustar00rootroot00000000000000 #include "../src/platform.h" #include "../src/hedley.h" #ifndef UNUSED # define UNUSED(...) (void)(__VA_ARGS__) #endif #ifdef LOAD_STATE # define _LOAD_STATE LOAD_STATE #else # define _LOAD_STATE(state, n) state[n] #endif #ifdef SET_STATE # define _SET_STATE SET_STATE #else # define _SET_STATE(state, n, val) state[n] = val #endif #ifndef state_word_t # define state_word_t word_t #endif #ifdef ADDF # define _ADDF ADDF #else # define _ADDF(f,a,b,c,d) ADD(a, f(b,c,d)) #endif /* code was originally based off OpenSSL's implementation */ #ifndef _RX #define _RX(f,a,b,c,d,ik,r) \ a = ADD(a, ik); \ a = _ADDF(f, a, b, c, d); \ a = ROTATE(a, r); \ a = ADD(a, b) #endif #ifdef MD5X2 # define RX(f,a,b,c,d,x,i,r,k) _RX(f,a,b,c,d,x(i,k),r); _RX(f,a##2,b##2,c##2,d##2,x##2(i,k),r) #else # define RX(f,a,b,c,d,x,i,r,k) _RX(f,a,b,c,d,x(i,k),r) #endif #ifndef MD5_USE_ASM static HEDLEY_ALWAYS_INLINE void FNB(md5_process_block)(state_word_t* state, const uint8_t* const* HEDLEY_RESTRICT data, size_t offset) { UNUSED(offset); // only ignored in x2/x1 word_t A, B, C, D; word_t oA, oB, oC, oD; /* some compilers don't optimise arrays well (i.e. register spills), so use local variables */ word_t XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15; /* mark as unused, for setups which don't use them */ UNUSED(XX0); UNUSED(XX1); UNUSED(XX2); UNUSED(XX3); UNUSED(XX4); UNUSED(XX5); UNUSED(XX6); UNUSED(XX7); UNUSED(XX8); UNUSED(XX9); UNUSED(XX10); UNUSED(XX11); UNUSED(XX12); UNUSED(XX13); UNUSED(XX14); UNUSED(XX15); #define L(i,k) LOAD(k, 0, data, offset, i, XX##i) #define X(i,k) INPUT(k, 0, data, offset, i, XX##i) A = _LOAD_STATE(state, 0); B = _LOAD_STATE(state, 1); C = _LOAD_STATE(state, 2); D = _LOAD_STATE(state, 3); oA = A; oB = B; oC = C; oD = D; #ifdef MD5X2 word_t A2, B2, C2, D2; word_t oA2, oB2, oC2, oD2; word_t XX0b, XX1b, XX2b, XX3b, XX4b, XX5b, XX6b, XX7b, XX8b, XX9b, XX10b, XX11b, XX12b, XX13b, XX14b, XX15b; UNUSED(XX0b); UNUSED(XX1b); UNUSED(XX2b); UNUSED(XX3b); UNUSED(XX4b); UNUSED(XX5b); UNUSED(XX6b); UNUSED(XX7b); UNUSED(XX8b); UNUSED(XX9b); UNUSED(XX10b); UNUSED(XX11b); UNUSED(XX12b); UNUSED(XX13b); UNUSED(XX14b); UNUSED(XX15b); # define L2(i,k) LOAD(k, 1, data, offset, i, XX##i##b) # define X2(i,k) INPUT(k, 1, data, offset, i, XX##i##b) A2 = _LOAD_STATE(state, 4); B2 = _LOAD_STATE(state, 5); C2 = _LOAD_STATE(state, 6); D2 = _LOAD_STATE(state, 7); oA2 = A2; oB2 = B2; oC2 = C2; oD2 = D2; # ifdef LOAD2 # define L2X(i, j) LOAD2(0, data, offset, i, XX##i, XX##j); LOAD2(1, data, offset, i, XX##i##b, XX##j##b) # else # define L2X(i, j) # endif # ifdef LOAD4 # define L4X(i, j, k, l) LOAD4(0, data, offset, i, XX##i, XX##j, XX##k, XX##l); LOAD4(1, data, offset, i, XX##i##b, XX##j##b, XX##k##b, XX##l##b) # else # define L4X(i, j, k, l) # endif # ifdef LOAD8 # define L8X(i, j, k, l, m, n, o, p) LOAD8(0, data, offset, i, XX##i, XX##j, XX##k, XX##l, XX##m, XX##n, XX##o, XX##p); LOAD8(1, data, offset, i, XX##i##b, XX##j##b, XX##k##b, XX##l##b, XX##m##b, XX##n##b, XX##o##b, XX##p##b) # else # define L8X(i, j, k, l, m, n, o, p) # endif #else # ifdef LOAD2 # define L2X(i, j) LOAD2(0, data, offset, i, XX##i, XX##j) # else # define L2X(i, j) # endif # ifdef LOAD4 # define L4X(i, j, k, l) LOAD4(0, data, offset, i, XX##i, XX##j, XX##k, XX##l) # else # define L4X(i, j, k, l) # endif # ifdef LOAD8 # define L8X(i, j, k, l, m, n, o, p) LOAD8(0, data, offset, i, XX##i, XX##j, XX##k, XX##l, XX##m, XX##n, XX##o, XX##p) # else # define L8X(i, j, k, l, m, n, o, p) # endif #endif /* Round 0 */ #ifdef LOAD16 LOAD16(0, data, offset, XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15); # ifdef MD5X2 LOAD16(1, data, offset, XX0b, XX1b, XX2b, XX3b, XX4b, XX5b, XX6b, XX7b, XX8b, XX9b, XX10b, XX11b, XX12b, XX13b, XX14b, XX15b); # endif #endif #ifdef ADD16 ADD16(XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15, 0xd76aa478L, 0xe8c7b756L, 0x242070dbL, 0xc1bdceeeL, 0xf57c0fafL, 0x4787c62aL, 0xa8304613L, 0xfd469501L, 0x698098d8L, 0x8b44f7afL, 0xffff5bb1L, 0x895cd7beL, 0x6b901122L, 0xfd987193L, 0xa679438eL, 0x49b40821L); # ifdef MD5X2 ADD16(XX0b, XX1b, XX2b, XX3b, XX4b, XX5b, XX6b, XX7b, XX8b, XX9b, XX10b, XX11b, XX12b, XX13b, XX14b, XX15b, 0xd76aa478L, 0xe8c7b756L, 0x242070dbL, 0xc1bdceeeL, 0xf57c0fafL, 0x4787c62aL, 0xa8304613L, 0xfd469501L, 0x698098d8L, 0x8b44f7afL, 0xffff5bb1L, 0x895cd7beL, 0x6b901122L, 0xfd987193L, 0xa679438eL, 0x49b40821L); # endif #endif L8X(0, 1, 2, 3, 4, 5, 6, 7); L4X(0, 1, 2, 3); L2X(0, 1); RX(F, A, B, C, D, L, 0, 7, 0xd76aa478L); RX(F, D, A, B, C, L, 1, 12, 0xe8c7b756L); L2X(2, 3); RX(F, C, D, A, B, L, 2, 17, 0x242070dbL); RX(F, B, C, D, A, L, 3, 22, 0xc1bdceeeL); L4X(4, 5, 6, 7); L2X(4, 5); RX(F, A, B, C, D, L, 4, 7, 0xf57c0fafL); RX(F, D, A, B, C, L, 5, 12, 0x4787c62aL); L2X(6, 7); RX(F, C, D, A, B, L, 6, 17, 0xa8304613L); RX(F, B, C, D, A, L, 7, 22, 0xfd469501L); L8X(8, 9, 10, 11, 12, 13, 14, 15); L4X(8, 9, 10, 11); L2X(8, 9); RX(F, A, B, C, D, L, 8, 7, 0x698098d8L); RX(F, D, A, B, C, L, 9, 12, 0x8b44f7afL); L2X(10, 11); RX(F, C, D, A, B, L, 10, 17, 0xffff5bb1L); RX(F, B, C, D, A, L, 11, 22, 0x895cd7beL); L4X(12, 13, 14, 15); L2X(12, 13); RX(F, A, B, C, D, L, 12, 7, 0x6b901122L); RX(F, D, A, B, C, L, 13, 12, 0xfd987193L); L2X(14, 15); RX(F, C, D, A, B, L, 14, 17, 0xa679438eL); RX(F, B, C, D, A, L, 15, 22, 0x49b40821L); /* Round 1 */ #ifdef ADD16 ADD16(XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15, 0xe9b6c7aaL, 0xf61e2562L, 0xfcefa3f8L, 0xf4d50d87L, 0xe7d3fbc8L, 0xd62f105dL, 0xc040b340L, 0x676f02d9L, 0x455a14edL, 0x21e1cde6L, 0x02441453L, 0x265e5a51L, 0x8d2a4c8aL, 0xa9e3e905L, 0xc33707d6L, 0xd8a1e681L); # ifdef MD5X2 ADD16(XX0b, XX1b, XX2b, XX3b, XX4b, XX5b, XX6b, XX7b, XX8b, XX9b, XX10b, XX11b, XX12b, XX13b, XX14b, XX15b, 0xe9b6c7aaL, 0xf61e2562L, 0xfcefa3f8L, 0xf4d50d87L, 0xe7d3fbc8L, 0xd62f105dL, 0xc040b340L, 0x676f02d9L, 0x455a14edL, 0x21e1cde6L, 0x02441453L, 0x265e5a51L, 0x8d2a4c8aL, 0xa9e3e905L, 0xc33707d6L, 0xd8a1e681L); # endif #endif RX(G, A, B, C, D, X, 1, 5, 0xf61e2562L); RX(G, D, A, B, C, X, 6, 9, 0xc040b340L); RX(G, C, D, A, B, X, 11, 14, 0x265e5a51L); RX(G, B, C, D, A, X, 0, 20, 0xe9b6c7aaL); RX(G, A, B, C, D, X, 5, 5, 0xd62f105dL); RX(G, D, A, B, C, X, 10, 9, 0x02441453L); RX(G, C, D, A, B, X, 15, 14, 0xd8a1e681L); RX(G, B, C, D, A, X, 4, 20, 0xe7d3fbc8L); RX(G, A, B, C, D, X, 9, 5, 0x21e1cde6L); RX(G, D, A, B, C, X, 14, 9, 0xc33707d6L); RX(G, C, D, A, B, X, 3, 14, 0xf4d50d87L); RX(G, B, C, D, A, X, 8, 20, 0x455a14edL); RX(G, A, B, C, D, X, 13, 5, 0xa9e3e905L); RX(G, D, A, B, C, X, 2, 9, 0xfcefa3f8L); RX(G, C, D, A, B, X, 7, 14, 0x676f02d9L); RX(G, B, C, D, A, X, 12, 20, 0x8d2a4c8aL); /* Round 2 */ #ifdef ADD16 ADD16(XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15, 0xeaa127faL, 0xa4beea44L, 0xc4ac5665L, 0xd4ef3085L, 0x4bdecfa9L, 0xfffa3942L, 0x04881d05L, 0xf6bb4b60L, 0x8771f681L, 0xd9d4d039L, 0xbebfbc70L, 0x6d9d6122L, 0xe6db99e5L, 0x289b7ec6L, 0xfde5380cL, 0x1fa27cf8L); # ifdef MD5X2 ADD16(XX0b, XX1b, XX2b, XX3b, XX4b, XX5b, XX6b, XX7b, XX8b, XX9b, XX10b, XX11b, XX12b, XX13b, XX14b, XX15b, 0xeaa127faL, 0xa4beea44L, 0xc4ac5665L, 0xd4ef3085L, 0x4bdecfa9L, 0xfffa3942L, 0x04881d05L, 0xf6bb4b60L, 0x8771f681L, 0xd9d4d039L, 0xbebfbc70L, 0x6d9d6122L, 0xe6db99e5L, 0x289b7ec6L, 0xfde5380cL, 0x1fa27cf8L); # endif #endif RX(H, A, B, C, D, X, 5, 4, 0xfffa3942L); RX(H, D, A, B, C, X, 8, 11, 0x8771f681L); RX(H, C, D, A, B, X, 11, 16, 0x6d9d6122L); RX(H, B, C, D, A, X, 14, 23, 0xfde5380cL); RX(H, A, B, C, D, X, 1, 4, 0xa4beea44L); RX(H, D, A, B, C, X, 4, 11, 0x4bdecfa9L); RX(H, C, D, A, B, X, 7, 16, 0xf6bb4b60L); RX(H, B, C, D, A, X, 10, 23, 0xbebfbc70L); RX(H, A, B, C, D, X, 13, 4, 0x289b7ec6L); RX(H, D, A, B, C, X, 0, 11, 0xeaa127faL); RX(H, C, D, A, B, X, 3, 16, 0xd4ef3085L); RX(H, B, C, D, A, X, 6, 23, 0x04881d05L); RX(H, A, B, C, D, X, 9, 4, 0xd9d4d039L); RX(H, D, A, B, C, X, 12, 11, 0xe6db99e5L); RX(H, C, D, A, B, X, 15, 16, 0x1fa27cf8L); RX(H, B, C, D, A, X, 2, 23, 0xc4ac5665L); /* Round 3 */ #ifdef IOFFSET # define _IOF(n) (int32_t)(n IOFFSET) #else # define _IOF(n) n #endif #ifdef ADD16 ADD16(XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10, XX11, XX12, XX13, XX14, XX15, _IOF(0xf4292244L), _IOF(0x85845dd1L), _IOF(0x2ad7d2bbL), _IOF(0x8f0ccc92L), _IOF(0xf7537e82L), _IOF(0xfc93a039L), _IOF(0xa3014314L), _IOF(0x432aff97L), _IOF(0x6fa87e4fL), _IOF(0xeb86d391L), _IOF(0xffeff47dL), _IOF(0xbd3af235L), _IOF(0x655b59c3L), _IOF(0x4e0811a1L), _IOF(0xab9423a7L), _IOF(0xfe2ce6e0L)); # ifdef MD5X2 ADD16(XX0b, XX1b, XX2b, XX3b, XX4b, XX5b, XX6b, XX7b, XX8b, XX9b, XX10b, XX11b, XX12b, XX13b, XX14b, XX15b, _IOF(0xf4292244L), _IOF(0x85845dd1L), _IOF(0x2ad7d2bbL), _IOF(0x8f0ccc92L), _IOF(0xf7537e82L), _IOF(0xfc93a039L), _IOF(0xa3014314L), _IOF(0x432aff97L), _IOF(0x6fa87e4fL), _IOF(0xeb86d391L), _IOF(0xffeff47dL), _IOF(0xbd3af235L), _IOF(0x655b59c3L), _IOF(0x4e0811a1L), _IOF(0xab9423a7L), _IOF(0xfe2ce6e0L)); # endif #endif RX(I, A, B, C, D, X, 0, 6, _IOF(0xf4292244L)); RX(I, D, A, B, C, X, 7, 10, _IOF(0x432aff97L)); RX(I, C, D, A, B, X, 14, 15, _IOF(0xab9423a7L)); RX(I, B, C, D, A, X, 5, 21, _IOF(0xfc93a039L)); RX(I, A, B, C, D, X, 12, 6, _IOF(0x655b59c3L)); RX(I, D, A, B, C, X, 3, 10, _IOF(0x8f0ccc92L)); RX(I, C, D, A, B, X, 10, 15, _IOF(0xffeff47dL)); RX(I, B, C, D, A, X, 1, 21, _IOF(0x85845dd1L)); RX(I, A, B, C, D, X, 8, 6, _IOF(0x6fa87e4fL)); RX(I, D, A, B, C, X, 15, 10, _IOF(0xfe2ce6e0L)); RX(I, C, D, A, B, X, 6, 15, _IOF(0xa3014314L)); RX(I, B, C, D, A, X, 13, 21, _IOF(0x4e0811a1L)); RX(I, A, B, C, D, X, 4, 6, _IOF(0xf7537e82L)); RX(I, D, A, B, C, X, 11, 10, _IOF(0xbd3af235L)); RX(I, C, D, A, B, X, 2, 15, _IOF(0x2ad7d2bbL)); RX(I, B, C, D, A, X, 9, 21, _IOF(0xeb86d391L)); #undef _IOF _SET_STATE(state, 0, ADD(oA, A)); _SET_STATE(state, 1, ADD(oB, B)); _SET_STATE(state, 2, ADD(oC, C)); _SET_STATE(state, 3, ADD(oD, D)); #ifdef MD5X2 _SET_STATE(state, 4, ADD(oA2, A2)); _SET_STATE(state, 5, ADD(oB2, B2)); _SET_STATE(state, 6, ADD(oC2, C2)); _SET_STATE(state, 7, ADD(oD2, D2)); #endif #undef L #undef X #undef L2X #undef L4X #undef L8X } #endif #undef RX #undef _RX #undef _ADDF #undef _LOAD_STATE #undef _SET_STATE #undef state_word_t #ifndef md5_free # define md5_free ALIGN_FREE #endif HEDLEY_MALLOC static HEDLEY_ALWAYS_INLINE void* FNB(md5_alloc)() { void* ret; #ifdef MD5X2 size_t words = 8; #else size_t words = 4; #endif #ifdef STATE_WORD_SIZE // assume SVE ALIGN_ALLOC(ret, STATE_WORD_SIZE*words, sizeof(void*)); #else ALIGN_ALLOC(ret, sizeof(word_t)*words, sizeof(word_t) < sizeof(void*) ? sizeof(void*) : sizeof(word_t)); #endif return ret; } par2cmdline-turbo-1.4.0/parpar/hasher/md5-final.c000066400000000000000000000027441514221355600215370ustar00rootroot00000000000000// single scalar implementation for finishing block #include "md5-scalar.h" void md5_final_block(void* state, const void *HEDLEY_RESTRICT data, uint64_t totalLength, uint64_t zeroPad) { ALIGN_TO(8, uint8_t block[64]); const uint8_t* blockPtr[] = {block}; size_t remaining = totalLength & 63; memcpy(block, data, remaining); memset(block + remaining, 0, 64-remaining); totalLength += zeroPad; int loopState = (remaining + zeroPad < 64)*2; // write this in a funky loop to avoid duplicating the force-inlined process_block function twice while(1) { if(loopState == 1 && zeroPad < 64) loopState = 2; if(loopState == 2) { remaining = totalLength & 63; block[remaining++] = 0x80; if(remaining <= 64-8) loopState = 4; else { loopState = 3; remaining = 0; } } if(loopState == 4) { memset(block + remaining, 0, 64-8 - remaining); totalLength <<= 3; // bytes -> bits write64(block + 64-8, _LE64(totalLength)); } md5_process_block_scalar((uint32_t*)state, blockPtr, 0); if(loopState == 4) break; else if(loopState == 3) loopState = 4; else if(loopState == 1) zeroPad -= 64; else if(loopState == 0) { memset(block, 0, 64); zeroPad -= 64-remaining; loopState = 1; } } #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ uint32_t* hash = (uint32_t*)state; write32(hash+0, _LE32(read32(hash+0))); write32(hash+1, _LE32(read32(hash+1))); write32(hash+2, _LE32(read32(hash+2))); write32(hash+3, _LE32(read32(hash+3))); #endif } par2cmdline-turbo-1.4.0/parpar/hasher/md5-final.h000066400000000000000000000003371514221355600215400ustar00rootroot00000000000000#ifndef __MD5_FINAL #define __MD5_FINAL #include "../src/hedley.h" #ifdef __cplusplus extern "C" #endif void md5_final_block(void* state, const void *HEDLEY_RESTRICT data, uint64_t totalLength, uint64_t zeroPad); #endif par2cmdline-turbo-1.4.0/parpar/hasher/md5-scalar-base.h000066400000000000000000000056411514221355600226270ustar00rootroot00000000000000#include // memcpy+memset #include "../src/stdint.h" #include "../src/platform.h" #define ADD(a, b) (a+b) #define VAL(k) (k) #define word_t uint32_t #define INPUT(k, set, ptr, offs, idx, var) (var + k) #define LOAD(k, set, ptr, offs, idx, var) (var = _LE32(read32(((char*)(ptr[set])) + offs + idx*4)), var + k) # if defined(_MSC_VER) # define ROTATE(a,n) _lrotl(a,n) # elif defined(__ICC) # define ROTATE(a,n) _rotl(a,n) # elif defined(__MWERKS__) # if defined(__POWERPC__) # define ROTATE(a,n) __rlwinm(a,n,0,31) # elif defined(__MC68K__) /* Motorola specific tweak. */ # define ROTATE(a,n) ( n<24 ? __rol(a,n) : __ror(a,32-n) ) # else # define ROTATE(a,n) __rol(a,n) # endif # elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM) /* * Some GNU C inline assembler templates. Note that these are * rotates by *constant* number of bits! But that's exactly * what we need here... * */ # if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) # define ROTATE(a,n) ({ unsigned int ret; \ __asm__ ( \ "roll %1,%0" \ : "=r"(ret) \ : "I"(n), "0"((unsigned int)(a)) \ : "cc"); \ ret; \ }) # elif defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \ defined(__powerpc) || defined(__ppc__) || defined(__powerpc64__) # define ROTATE(a,n) ({ unsigned int ret; \ __asm__ ( \ "rlwinm %0,%1,%2,0,31" \ : "=r"(ret) \ : "r"(a), "I"(n)); \ ret; \ }) # elif defined(__s390x__) # define ROTATE(a,n) ({ unsigned int ret; \ __asm__ ("rll %0,%1,%2" \ : "=r"(ret) \ : "r"(a), "I"(n)); \ ret; \ }) # endif # endif # ifndef ROTATE # define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n)))) # endif #define F 1 #define G 2 #define H 3 #define I 4 // this is defined to allow a special sequence for the 'G' function - essentially, the usual bitwise OR can be replaced with an ADD, and re-ordering can be done to slightly defer the dependency on the 'b' input #define ADDF(f,a,b,c,d) ( \ f==G ? (((~d & c) + a) + (d & b)) : a + ( \ f==F ? (((c ^ d) & b) ^ d) : ( \ f==H ? ((d ^ c) ^ b) : \ ((~d | b) ^ c) \ ) \ ) \ ) par2cmdline-turbo-1.4.0/parpar/hasher/md5-scalar.h000066400000000000000000000021651514221355600217150ustar00rootroot00000000000000 #include // memcpy+memset #include "../src/platform.h" #include "../src/stdint.h" #if (defined(__GNUC__) || defined(__clang__)) && defined(PLATFORM_X86) && defined(__OPTIMIZE__) && (!defined(HEDLEY_GCC_VERSION) || !defined(HAS_UBSAN)) # define MD5_USE_ASM # ifdef PLATFORM_AMD64 # define MD5_HAS_NOLEA 1 # else # define md5_process_block_nolea md5_process_block_scalar # endif # include "md5-x86-asm.h" #else # define md5_process_block_nolea md5_process_block_scalar #endif #if (defined(__GNUC__) || defined(__clang__)) && defined(PLATFORM_ARM) && defined(__OPTIMIZE__) # ifdef __aarch64__ # define MD5_USE_ASM # include "md5-arm64-asm.h" # elif (__BYTE_ORDER__ != __ORDER_BIG_ENDIAN__) || (defined(__ARM_ARCH) && __ARM_ARCH >= 6) || defined(__armv7__) || defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_8A__) || defined(_M_ARM) // require ARMv6 for big-endian support # define MD5_USE_ASM # include "md5-arm-asm.h" # endif #endif #include "md5-scalar-base.h" #define FNB(f) f##_scalar #include "md5-base.h" #undef FNB #ifdef MD5_USE_ASM # undef MD5_USE_ASM #endif par2cmdline-turbo-1.4.0/parpar/hasher/md5-x86-asm.h000066400000000000000000000410711514221355600216520ustar00rootroot00000000000000 #ifndef STR # define STR_HELPER(x) #x # define STR(x) STR_HELPER(x) #endif #ifdef PLATFORM_X86 // GCC insanity [https://gcc.gnu.org/legacy-ml/gcc-help/2011-04/msg00566.html] #define ASM_INPUTS [input0]"m"(_in[0]), [input1]"m"(_in[1]), [input2]"m"(_in[2]), [input3]"m"(_in[3]), [input4]"m"(_in[4]), [input5]"m"(_in[5]), [input6]"m"(_in[6]), [input7]"m"(_in[7]), [input8]"m"(_in[8]), [input9]"m"(_in[9]), [input10]"m"(_in[10]), [input11]"m"(_in[11]), [input12]"m"(_in[12]), [input13]"m"(_in[13]), [input14]"m"(_in[14]), [input15]"m"(_in[15]) // usually the x86 code would use 6 registers (+1 for addressing input), but some compilers don't seem to like it (ClangCL, MSYS GCC) so we'll restrict usage to 5 registers by load-op the input #ifdef PLATFORM_AMD64 # define ADD_CONST(K, KO, A, I) "leal " STR(K) KO "(%k[" STR(I) STR(A) "], %k[TMP2]), %k[" STR(A) "]\n" # define PRELOAD_INPUT(NEXT_IN) "movl " NEXT_IN ", %k[TMP2]\n" # define ADD_INPUT(NEXT_IN, D) #else # define ADD_CONST(K, KO, A, I) "add $" STR(K) KO ", %k[" STR(A) "]\n" # define PRELOAD_INPUT(NEXT_IN) // don't need to worry about D/ID distinction here, because it's not enabled on x86 # define ADD_INPUT(NEXT_IN, D) "add " NEXT_IN ", %k[" STR(D) "]\n" #endif #define ROUND_F(I, A, B, C, D, NEXT_IN, K, R) \ "xorl %k[" STR(C) "], %k[TMP1]\n" \ ADD_CONST(K, , A, I) \ "andl %k[" STR(B) "], %k[TMP1]\n" \ PRELOAD_INPUT(NEXT_IN) \ "xorl %k[" STR(D) "], %k[TMP1]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ ADD_INPUT(NEXT_IN, D) \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "movl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" #ifdef _MD5_USE_BMI1_ # define ROUND_F_LAST(A, B, C, D, NEXT_IN, K, R) \ "xorl %k[" STR(C) "], %k[TMP1]\n" \ ADD_CONST(K, , A, ) \ "andl %k[" STR(B) "], %k[TMP1]\n" \ PRELOAD_INPUT(NEXT_IN) \ "xorl %k[" STR(D) "], %k[TMP1]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ ADD_INPUT(NEXT_IN, D) \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "andnl %k[" STR(B) "], %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" # ifdef PLATFORM_AMD64 # define ROUND_G(A, B, C, D, NEXT_IN, K, R) \ ADD_CONST(K, , A, ) \ PRELOAD_INPUT(NEXT_IN) \ "movl %k[" STR(D) "], %k[TMP3]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ "andl %k[" STR(B) "], %k[TMP3]\n" \ "addl %k[TMP3], %k[" STR(A) "]\n" \ ADD_INPUT(NEXT_IN, D) \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "andnl %k[" STR(B) "], %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" # define ROUND_G_LAST(A, B, C, D, NEXT_IN, K, R) \ ADD_CONST(K, , A, ) \ PRELOAD_INPUT(NEXT_IN) \ "movl %k[" STR(D) "], %k[TMP3]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ "andl %k[" STR(B) "], %k[TMP3]\n" \ "addl %k[TMP3], %k[" STR(A) "]\n" \ ADD_INPUT(NEXT_IN, D) \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "movl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" # else # define ROUND_G(A, B, C, D, NEXT_IN, K, R) \ ADD_CONST(K, , A, ) \ "addl %k[TMP1], %k[" STR(A) "]\n" \ PRELOAD_INPUT(NEXT_IN) \ "movl %k[" STR(D) "], %k[TMP1]\n" \ "andl %k[" STR(B) "], %k[TMP1]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ ADD_INPUT(NEXT_IN, D) \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "andnl %k[" STR(B) "], %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" # define ROUND_G_LAST(A, B, C, D, NEXT_IN, K, R) \ ADD_CONST(K, , A, ) \ "addl %k[TMP1], %k[" STR(A) "]\n" \ PRELOAD_INPUT(NEXT_IN) \ "movl %k[" STR(D) "], %k[TMP1]\n" \ "andl %k[" STR(B) "], %k[TMP1]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ ADD_INPUT(NEXT_IN, D) \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "movl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" # endif #else # define ROUND_F_LAST(A, B, C, D, NEXT_IN, K, R) ROUND_F(, A, B, C, D, NEXT_IN, K, R) # define ROUND_G_LAST ROUND_G # ifdef PLATFORM_AMD64 # define ROUND_G(A, B, C, D, NEXT_IN, K, R) \ "notl %k[TMP1]\n" \ ADD_CONST(K, , A, ) \ "andl %k[" STR(C) "], %k[TMP1]\n" \ PRELOAD_INPUT(NEXT_IN) \ "movl %k[" STR(D) "], %k[TMP3]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ "andl %k[" STR(B) "], %k[TMP3]\n" \ "addl %k[TMP3], %k[" STR(A) "]\n" \ ADD_INPUT(NEXT_IN, D) \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "movl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" # else # define ROUND_G(A, B, C, D, NEXT_IN, K, R) \ "notl %k[TMP1]\n" \ ADD_CONST(K, , A, ) \ "andl %k[" STR(C) "], %k[TMP1]\n" \ PRELOAD_INPUT(NEXT_IN) \ "addl %k[TMP1], %k[" STR(A) "]\n" \ "movl %k[" STR(D) "], %k[TMP1]\n" \ "andl %k[" STR(B) "], %k[TMP1]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ ADD_INPUT(NEXT_IN, D) \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "movl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" # endif #endif #define ROUND_H(A, B, C, D, NEXT_IN, K, R) \ ADD_CONST(K, , A, ) \ "xorl %k[" STR(B) "], %k[TMP1]\n" \ PRELOAD_INPUT(NEXT_IN) \ "addl %k[TMP1], %k[" STR(A) "]\n" \ "xorl %k[" STR(D) "], %k[TMP1]\n" \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ ADD_INPUT(NEXT_IN, D) \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" #ifdef _MD5_USE_BMI1_ #define ROUND_I(A, B, C, D, NEXT_IN, K, R) \ ADD_CONST(K, "-1", A, ) \ "andnl %k[" STR(D) "], %k[" STR(B) "], %k[TMP1]\n" \ PRELOAD_INPUT(NEXT_IN) \ "xorl %k[" STR(C) "], %k[TMP1]\n" \ "subl %k[TMP1], %k[" STR(A) "]\n" \ ADD_INPUT(NEXT_IN, D) \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" #define ROUND_I_LAST(A, B, C, D, K, R) \ ADD_CONST(K, "-1", A, ) \ "andnl %k[" STR(D) "], %k[" STR(B) "], %k[TMP1]\n" \ "xorl %k[" STR(C) "], %k[TMP1]\n" \ "subl %k[TMP1], %k[" STR(A) "]\n" \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" #else #define ROUND_I(A, B, C, D, NEXT_IN, K, R) \ "notl %k[TMP1]\n" \ ADD_CONST(K, , A, ) \ "orl %k[" STR(B) "], %k[TMP1]\n" \ PRELOAD_INPUT(NEXT_IN) \ "xorl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ ADD_INPUT(NEXT_IN, D) \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "movl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" #define ROUND_I_LAST(A, B, C, D, K, R) \ "notl %k[TMP1]\n" \ ADD_CONST(K, , A, ) \ "orl %k[" STR(B) "], %k[TMP1]\n" \ "xorl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" #endif #define RF4(I, i0, i1, i2, i3, k0, k1, k2, k3) \ ROUND_F(I, A, I##B, I##C, I##D, "%[input" STR(i0) "]", k0, 7) \ ROUND_F(I, D, A, I##B, I##C, "%[input" STR(i1) "]", k1, 12) \ ROUND_F(I, C, D, A, I##B, "%[input" STR(i2) "]", k2, 17) \ ROUND_F(I, B, C, D, A, "%[input" STR(i3) "]", k3, 22) #define RG4(i0, i1, i2, i3, k0, k1, k2, k3) \ ROUND_G(A, B, C, D, "%[input" STR(i0) "]", k0, 5) \ ROUND_G(D, A, B, C, "%[input" STR(i1) "]", k1, 9) \ ROUND_G(C, D, A, B, "%[input" STR(i2) "]", k2, 14) \ ROUND_G(B, C, D, A, "%[input" STR(i3) "]", k3, 20) #define RH4(i0, i1, i2, i3, k0, k1, k2, k3) \ ROUND_H(A, B, C, D, "%[input" STR(i0) "]", k0, 4) \ ROUND_H(D, A, B, C, "%[input" STR(i1) "]", k1, 11) \ ROUND_H(C, D, A, B, "%[input" STR(i2) "]", k2, 16) \ ROUND_H(B, C, D, A, "%[input" STR(i3) "]", k3, 23) #define RI4(i0, i1, i2, i3, k0, k1, k2, k3) \ ROUND_I(A, B, C, D, "%[input" STR(i0) "]", k0, 6) \ ROUND_I(D, A, B, C, "%[input" STR(i1) "]", k1, 10) \ ROUND_I(C, D, A, B, "%[input" STR(i2) "]", k2, 15) \ ROUND_I(B, C, D, A, "%[input" STR(i3) "]", k3, 21) static HEDLEY_ALWAYS_INLINE void md5_process_block_scalar(uint32_t* HEDLEY_RESTRICT state, const uint8_t* const* HEDLEY_RESTRICT data, size_t offset) { (void)offset; uint32_t A; uint32_t B; uint32_t C; uint32_t D; const uint32_t* _in = (const uint32_t*)(data[0]); void *tmp1; #ifndef PLATFORM_AMD64 A = state[0]; B = state[1]; C = state[2]; D = state[3]; #else void *tmp2, *tmp3; #endif __asm__( #ifdef PLATFORM_AMD64 "movl %[input0], %k[TMP2]\n" "movl %k[ID], %k[TMP1]\n" RF4(I, 1, 2, 3, 4, -0x28955b88, -0x173848aa, 0x242070db, -0x3e423112) #else "addl %[input0], %k[A]\n" "movl %k[D], %k[TMP1]\n" RF4(, 1, 2, 3, 4, -0x28955b88, -0x173848aa, 0x242070db, -0x3e423112) #endif RF4(, 5, 6, 7, 8, -0x0a83f051, 0x4787c62a, -0x57cfb9ed, -0x02b96aff) RF4(, 9, 10, 11, 12, 0x698098d8, -0x74bb0851, -0x0000a44f, -0x76a32842) ROUND_F(, A, B, C, D, "%[input13]", 0x6b901122, 7) ROUND_F(, D, A, B, C, "%[input14]", -0x02678e6d, 12) ROUND_F(, C, D, A, B, "%[input15]", -0x5986bc72, 17) ROUND_F_LAST(B, C, D, A, "%[input1]", 0x49b40821, 22) : [TMP1]"=&R"(tmp1), #ifdef PLATFORM_AMD64 [A]"=&R"(A), [B]"=&R"(B), [C]"=&R"(C), [D]"=&R"(D), [TMP2]"=&r"(tmp2) : [IA]"r"(state[0]), [IB]"r"(state[1]), [IC]"r"(state[2]), [ID]"r"(state[3]), #else [A]"+&R"(A), [B]"+&R"(B), [C]"+&R"(C), [D]"+&R"(D) : #endif ASM_INPUTS :); __asm__( RG4( 6, 11, 0, 5, -0x09e1da9e, -0x3fbf4cc0, 0x265e5a51, -0x16493856) RG4(10, 15, 4, 9, -0x29d0efa3, 0x02441453, -0x275e197f, -0x182c0438) RG4(14, 3, 8, 13, 0x21e1cde6, -0x3cc8f82a, -0x0b2af279, 0x455a14ed) ROUND_G(A, B, C, D, "%[input2]", -0x561c16fb, 5) ROUND_G(D, A, B, C, "%[input7]", -0x03105c08, 9) ROUND_G(C, D, A, B, "%[input12]", 0x676f02d9, 14) ROUND_G_LAST(B, C, D, A, "%[input5]", -0x72d5b376, 20) "xorl %k[C], %k[TMP1]\n" ADD_CONST(-0x0005c6be, , A, ) "xorl %k[B], %k[TMP1]\n" PRELOAD_INPUT("%[input8]") "addl %k[TMP1], %k[A]\n" "xorl %k[D], %k[TMP1]\n" "roll $4, %k[A]\n" ADD_INPUT("%[input8]", D) "addl %k[B], %k[A]\n" ROUND_H(D, A, B, C, "%[input11]", -0x788e097f, 11) ROUND_H(C, D, A, B, "%[input14]", 0x6d9d6122, 16) ROUND_H(B, C, D, A, "%[input1]", -0x021ac7f4, 23) RH4( 4, 7, 10, 13, -0x5b4115bc, 0x4bdecfa9, -0x0944b4a0, -0x41404390) RH4( 0, 3, 6, 9, 0x289b7ec6, -0x155ed806, -0x2b10cf7b, 0x04881d05) RH4(12, 15, 2, 0, -0x262b2fc7, -0x1924661b, 0x1fa27cf8, -0x3b53a99b) // above contains a redundant XOR - TODO: consider eliminating #ifndef _MD5_USE_BMI1_ "movl %k[D], %k[TMP1]\n" #endif RI4( 7, 14, 5, 12, -0x0bd6ddbc, 0x432aff97, -0x546bdc59, -0x036c5fc7) RI4( 3, 10, 1, 8, 0x655b59c3, -0x70f3336e, -0x00100b83, -0x7a7ba22f) RI4(15, 6, 13, 4, 0x6fa87e4f, -0x01d31920, -0x5cfebcec, 0x4e0811a1) ROUND_I(A, B, C, D, "%[input11]", -0x08ac817e, 6) ROUND_I(D, A, B, C, "%[input2]" , -0x42c50dcb, 10) ROUND_I(C, D, A, B, "%[input9]" , 0x2ad7d2bb, 15) ROUND_I_LAST(B, C, D, A, -0x14792c6f, 21) : [TMP1]"+&R"(tmp1), #ifdef PLATFORM_AMD64 [A]"+&R"(A), [B]"+&R"(B), [C]"+&R"(C), [D]"+&R"(D) , [TMP2]"+&r"(tmp2), [TMP3]"=&r"(tmp3) #else [A]"+&R"(A), [B]"+&R"(B), [C]"+&R"(C), [D]"+&R"(D) #endif : ASM_INPUTS :); state[0] += A; state[1] += B; state[2] += C; state[3] += D; } #undef ROUND_F #undef ROUND_F_LAST #undef ROUND_G #undef ROUND_G_LAST #undef ROUND_H #undef ROUND_I #undef ROUND_I_LAST #undef RF4 #undef RG4 #undef RH4 #undef RI4 #ifdef PLATFORM_AMD64 #define ROUND_F(A, B, C, D, NEXT_IN, K, R) \ "xorl %k[" STR(C) "], %k[TMP1]\n" \ "addl $" STR(K) ", %k[" STR(A) "]\n" \ "andl %k[" STR(B) "], %k[TMP1]\n" \ "xorl %k[" STR(D) "], %k[TMP1]\n" \ "addl " NEXT_IN ", %k[" STR(D) "]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "movl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" #ifdef PLATFORM_AMD64 #define ROUND_G(A, B, C, D, NEXT_IN, K, R) \ "notl %k[TMP1]\n" \ "addl $" STR(K) ", %k[" STR(A) "]\n" \ "andl %k[" STR(C) "], %k[TMP1]\n" \ "movl %k[" STR(D) "], %k[TMP2]\n" \ "addl " NEXT_IN ", %k[" STR(D) "]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ "andl %k[" STR(B) "], %k[TMP2]\n" \ "addl %k[TMP2], %k[" STR(A) "]\n" \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "movl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" #else #define ROUND_G(A, B, C, D, NEXT_IN, K, R) \ "notl %k[TMP1]\n" \ "addl $" STR(K) ", %k[" STR(A) "]\n" \ "andl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ "movl %k[" STR(D) "], %k[TMP1]\n" \ "addl " NEXT_IN ", %k[" STR(D) "]\n" \ "andl %k[" STR(B) "], %k[TMP1]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "movl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" #endif #define ROUND_H(A, B, C, D, NEXT_IN, K, R) \ "addl $" STR(K) ", %k[" STR(A) "]\n" \ "xorl %k[" STR(B) "], %k[TMP1]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ "xorl %k[" STR(D) "], %k[TMP1]\n" \ "addl " NEXT_IN ", %k[" STR(D) "]\n" \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" #define ROUND_I(A, B, C, D, NEXT_IN, K, R) \ "notl %k[TMP1]\n" \ "addl $" STR(K) ", %k[" STR(A) "]\n" \ "addl " NEXT_IN ", %k[" STR(D) "]\n" \ "orl %k[" STR(B) "], %k[TMP1]\n" \ "xorl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "movl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" #define ROUND_I_LAST(A, B, C, D, K, R) \ "notl %k[TMP1]\n" \ "addl $" STR(K) ", %k[" STR(A) "]\n" \ "orl %k[" STR(B) "], %k[TMP1]\n" \ "xorl %k[" STR(C) "], %k[TMP1]\n" \ "addl %k[TMP1], %k[" STR(A) "]\n" \ "roll $" STR(R) ", %k[" STR(A) "]\n" \ "addl %k[" STR(B) "], %k[" STR(A) "]\n" #define RF4(I, i0, i1, i2, i3, k0, k1, k2, k3) \ ROUND_F(A, I##B, I##C, I##D, "%[input" STR(i0) "]", k0, 7) \ ROUND_F(D, A, I##B, I##C, "%[input" STR(i1) "]", k1, 12) \ ROUND_F(C, D, A, I##B, "%[input" STR(i2) "]", k2, 17) \ ROUND_F(B, C, D, A, "%[input" STR(i3) "]", k3, 22) #define RG4(i0, i1, i2, i3, k0, k1, k2, k3) \ ROUND_G(A, B, C, D, "%[input" STR(i0) "]", k0, 5) \ ROUND_G(D, A, B, C, "%[input" STR(i1) "]", k1, 9) \ ROUND_G(C, D, A, B, "%[input" STR(i2) "]", k2, 14) \ ROUND_G(B, C, D, A, "%[input" STR(i3) "]", k3, 20) #define RH4(i0, i1, i2, i3, k0, k1, k2, k3) \ ROUND_H(A, B, C, D, "%[input" STR(i0) "]", k0, 4) \ ROUND_H(D, A, B, C, "%[input" STR(i1) "]", k1, 11) \ ROUND_H(C, D, A, B, "%[input" STR(i2) "]", k2, 16) \ ROUND_H(B, C, D, A, "%[input" STR(i3) "]", k3, 23) #define RI4(i0, i1, i2, i3, k0, k1, k2, k3) \ ROUND_I(A, B, C, D, "%[input" STR(i0) "]", k0, 6) \ ROUND_I(D, A, B, C, "%[input" STR(i1) "]", k1, 10) \ ROUND_I(C, D, A, B, "%[input" STR(i2) "]", k2, 15) \ ROUND_I(B, C, D, A, "%[input" STR(i3) "]", k3, 21) static HEDLEY_ALWAYS_INLINE void md5_process_block_nolea(uint32_t* HEDLEY_RESTRICT state, const uint8_t* const* HEDLEY_RESTRICT data, size_t offset) { (void)offset; uint32_t A; uint32_t B; uint32_t C; uint32_t D; const uint32_t* _in = (const uint32_t*)(data[0]); void *tmp1; #ifdef PLATFORM_AMD64 void *tmp2; #endif A = state[0]; B = state[1]; C = state[2]; D = state[3]; __asm__( "addl %[input0], %k[A]\n" "movl %k[D], %k[TMP1]\n" RF4(, 1, 2, 3, 4, -0x28955b88, -0x173848aa, 0x242070db, -0x3e423112) RF4(, 5, 6, 7, 8, -0x0a83f051, 0x4787c62a, -0x57cfb9ed, -0x02b96aff) RF4(, 9, 10, 11, 12, 0x698098d8, -0x74bb0851, -0x0000a44f, -0x76a32842) RF4(, 13, 14, 15, 1, 0x6b901122, -0x02678e6d, -0x5986bc72, 0x49b40821) : [TMP1]"=&R"(tmp1), #ifdef PLATFORM_AMD64 [TMP2]"=&r"(tmp2), #endif [A]"+&R"(A), [B]"+&R"(B), [C]"+&R"(C), [D]"+&R"(D) : ASM_INPUTS :); __asm__( RG4( 6, 11, 0, 5, -0x09e1da9e, -0x3fbf4cc0, 0x265e5a51, -0x16493856) RG4(10, 15, 4, 9, -0x29d0efa3, 0x02441453, -0x275e197f, -0x182c0438) RG4(14, 3, 8, 13, 0x21e1cde6, -0x3cc8f82a, -0x0b2af279, 0x455a14ed) RG4( 2, 7, 12, 5, -0x561c16fb, -0x03105c08, 0x676f02d9, -0x72d5b376) "xorl %k[C], %k[TMP1]\n" "addl $-0x0005c6be, %k[A]\n" "xorl %k[B], %k[TMP1]\n" "addl %k[TMP1], %k[A]\n" "xorl %k[D], %k[TMP1]\n" "addl %[input8], %k[D]\n" "roll $4, %k[A]\n" "addl %k[B], %k[A]\n" ROUND_H(D, A, B, C, "%[input11]", -0x788e097f, 11) ROUND_H(C, D, A, B, "%[input14]", 0x6d9d6122, 16) ROUND_H(B, C, D, A, "%[input1]", -0x021ac7f4, 23) RH4( 4, 7, 10, 13, -0x5b4115bc, 0x4bdecfa9, -0x0944b4a0, -0x41404390) RH4( 0, 3, 6, 9, 0x289b7ec6, -0x155ed806, -0x2b10cf7b, 0x04881d05) RH4(12, 15, 2, 0, -0x262b2fc7, -0x1924661b, 0x1fa27cf8, -0x3b53a99b) // above contains a redundant XOR - TODO: consider eliminating "movl %k[D], %k[TMP1]\n" RI4( 7, 14, 5, 12, -0x0bd6ddbc, 0x432aff97, -0x546bdc59, -0x036c5fc7) RI4( 3, 10, 1, 8, 0x655b59c3, -0x70f3336e, -0x00100b83, -0x7a7ba22f) RI4(15, 6, 13, 4, 0x6fa87e4f, -0x01d31920, -0x5cfebcec, 0x4e0811a1) ROUND_I(A, B, C, D, "%[input11]", -0x08ac817e, 6) ROUND_I(D, A, B, C, "%[input2]" , -0x42c50dcb, 10) ROUND_I(C, D, A, B, "%[input9]" , 0x2ad7d2bb, 15) ROUND_I_LAST(B, C, D, A, -0x14792c6f, 21) : [TMP1]"+&R"(tmp1), #ifdef PLATFORM_AMD64 [TMP2]"=&r"(tmp2), #endif [A]"+&R"(A), [B]"+&R"(B), [C]"+&R"(C), [D]"+&R"(D) : ASM_INPUTS :); state[0] += A; state[1] += B; state[2] += C; state[3] += D; } #undef ROUND_F #undef ROUND_G #undef ROUND_H #undef ROUND_I #undef ROUND_I_LAST #undef RF4 #undef RG4 #undef RH4 #undef RI4 #endif #endif // PLATFORM_X86 par2cmdline-turbo-1.4.0/parpar/hasher/md5mb-base.h000066400000000000000000000043571514221355600217060ustar00rootroot00000000000000 #include #include "../src/stdint.h" #ifdef MD5X2 # define FNB(f) _FN(f##_mb2) # define FN_REGIONS(d) _FN(md5mb_##d)*2 #else # define FNB(f) _FN(f##_mb) # define FN_REGIONS(d) _FN(md5mb_##d) #endif #include "md5-base.h" static HEDLEY_ALWAYS_INLINE void FNB(md5_update_block)(void* state, const void* const* data, size_t offset) { FNB(md5_process_block)((word_t*)state, (const uint8_t* const*)data, offset); } static HEDLEY_ALWAYS_INLINE void FNB(md5_final_block)(void* state, const void *HEDLEY_RESTRICT const*HEDLEY_RESTRICT data, size_t offset, uint64_t totalLength) { ALIGN_TO(_FN(md5mb_alignment), uint8_t block[FN_REGIONS(max_regions)][64]); const uint8_t* blockPtr[FN_REGIONS(max_regions)]; size_t remaining = totalLength & 63; for(unsigned i=0; i bits for(unsigned i=0; i #define ADD vaddq_u32 #define VAL vdupq_n_u32 #define word_t uint32x4_t #define INPUT(k, set, ptr, offs, idx, var) ADD(var, VAL(k)) #define LOAD INPUT #define LOAD4(set, ptr, offs, idx, var0, var1, var2, var3) { \ uint32x4x2_t in01 = vzipq_u32( \ vreinterpretq_u32_u8(vld1q_u8((uint8_t*)ptr[0+set*4] + offs + idx*4)), \ vreinterpretq_u32_u8(vld1q_u8((uint8_t*)ptr[1+set*4] + offs + idx*4)) \ ); \ uint32x4x2_t in23 = vzipq_u32( \ vreinterpretq_u32_u8(vld1q_u8((uint8_t*)ptr[2+set*4] + offs + idx*4)), \ vreinterpretq_u32_u8(vld1q_u8((uint8_t*)ptr[3+set*4] + offs + idx*4)) \ ); \ var0 = vcombine_u32(vget_low_u32(in01.val[0]), vget_low_u32(in23.val[0])); \ var1 = vcombine_u32(vget_high_u32(in01.val[0]), vget_high_u32(in23.val[0])); \ var2 = vcombine_u32(vget_low_u32(in01.val[1]), vget_low_u32(in23.val[1])); \ var3 = vcombine_u32(vget_high_u32(in01.val[1]), vget_high_u32(in23.val[1])); \ } #define ROTATE(a, r) r==16 ? vreinterpretq_u32_u16(vrev32q_u16(vreinterpretq_u16_u32(a))) : vsliq_n_u32(vshrq_n_u32(a, 32-r), a, r) #define md5mb_regions_neon 4 #define md5mb_max_regions_neon md5mb_regions_neon #define md5mb_alignment_neon 16 #define F vbslq_u32 #define G(b,c,d) vbslq_u32(d, b, c) #define H(b,c,d) veorq_u32(veorq_u32(d, c), b) #define I(b,c,d) veorq_u32(vornq_u32(b, d), c) /* the following (I function shortcut) often performs worse - is it because VBIT is more expensive than VORN? #define F 1 #define G 2 #define H 3 #define I 4 #define ADDF(f,a,b,c,d) ( \ f==G ? ADD(a, vbslq_u32(d, b, c)) : ( \ f==I ? vsubq_u32(a, vbslq_u32(b, c, veorq_u32(c, d))) : ADD(a, f==F ? \ vbslq_u32(b, c, d) : \ veorq_u32(veorq_u32(d, c), b) \ ) \ ) \ ) #define IOFFSET -1 */ #define _FN(f) f##_neon #include "md5mb-base.h" #define MD5X2 #include "md5mb-base.h" #undef MD5X2 #undef _FN #undef ROTATE #undef ADD #undef VAL #undef word_t #undef INPUT #undef LOAD #undef LOAD4 #undef F #undef G #undef H #undef I //#undef ADDF //#undef IOFFSET static HEDLEY_ALWAYS_INLINE void md5_extract_mb_neon(void* dst, void* state, int idx) { uint32x4_t* state_ = (uint32x4_t*)state + (idx & 4); // re-arrange to pairs uint32x4x2_t tmp1 = vzipq_u32(state_[0], state_[1]); uint32x4x2_t tmp2 = vzipq_u32(state_[2], state_[3]); idx &= 3; if(idx == 0) vst1q_u8((uint8_t*)dst, vreinterpretq_u8_u32(vcombine_u32(vget_low_u32(tmp1.val[0]), vget_low_u32(tmp2.val[0])))); if(idx == 1) vst1q_u8((uint8_t*)dst, vreinterpretq_u8_u32(vcombine_u32(vget_high_u32(tmp1.val[0]), vget_high_u32(tmp2.val[0])))); if(idx == 2) vst1q_u8((uint8_t*)dst, vreinterpretq_u8_u32(vcombine_u32(vget_low_u32(tmp1.val[1]), vget_low_u32(tmp2.val[1])))); if(idx == 3) vst1q_u8((uint8_t*)dst, vreinterpretq_u8_u32(vcombine_u32(vget_high_u32(tmp1.val[1]), vget_high_u32(tmp2.val[1])))); } static HEDLEY_ALWAYS_INLINE void md5_extract_all_mb_neon(void* dst, void* state, int group) { uint32x4_t* state_ = (uint32x4_t*)state + group*4; uint32x4x4_t t; t.val[0] = state_[0]; t.val[1] = state_[1]; t.val[2] = state_[2]; t.val[3] = state_[3]; #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ t.val[0] = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(t.val[0]))); t.val[1] = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(t.val[1]))); t.val[2] = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(t.val[2]))); t.val[3] = vreinterpretq_u32_u8(vrev32q_u8(vreinterpretq_u8_u32(t.val[3]))); #endif vst4q_u32((uint32_t*)dst, t); } #endif par2cmdline-turbo-1.4.0/parpar/hasher/md5mb-scalar.h000066400000000000000000000013571514221355600222360ustar00rootroot00000000000000#include "md5-scalar-base.h" #define md5mb_regions_scalar 1 #define md5mb_max_regions_scalar 1 #define md5mb_alignment_scalar 4 #define _FN(f) f##_scalar #include "md5mb-base.h" #define MD5X2 #include "md5mb-base.h" #undef MD5X2 #undef _FN #undef ROTATE #undef ADD #undef VAL #undef word_t #undef INPUT #undef LOAD #undef F #undef G #undef H #undef I #undef ADDF static HEDLEY_ALWAYS_INLINE void md5_extract_mb_scalar(void* dst, void* state, int idx) { uint32_t* state_ = (uint32_t*)state + idx*4; uint32_t* dst_ = (uint32_t*)dst; for(int i=0; i<4; i++) write32(dst_+i, _LE32(read32(state_+i))); } static HEDLEY_ALWAYS_INLINE void md5_extract_all_mb_scalar(void* dst, void* state, int group) { md5_extract_mb_scalar(dst, state, group); } par2cmdline-turbo-1.4.0/parpar/hasher/md5mb-sse.h000066400000000000000000000525131514221355600215630ustar00rootroot00000000000000 #include "../src/platform.h" #define INPUT(k, set, ptr, offs, idx, var) ADD(var, VAL(k)) #define LOAD INPUT #ifdef __SSE2__ #include #define ADD _mm_add_epi32 #define VAL _mm_set1_epi32 #define word_t __m128i // TODO: for SSE, might be better to use shufps because it enables movaps which is 1 byte shorter // probably worse for AVX since all instructions are 4 bytes (and shufps has an immediate byte) #define LOAD4(set, ptr, offs, idx, var0, var1, var2, var3) { \ __m128i in0 = _mm_loadu_si128((__m128i*)(ptr[0+set*4] + offs + idx*4)); \ __m128i in1 = _mm_loadu_si128((__m128i*)(ptr[1+set*4] + offs + idx*4)); \ __m128i in2 = _mm_loadu_si128((__m128i*)(ptr[2+set*4] + offs + idx*4)); \ __m128i in3 = _mm_loadu_si128((__m128i*)(ptr[3+set*4] + offs + idx*4)); \ __m128i in01a = _mm_unpacklo_epi32(in0, in1); \ __m128i in01b = _mm_unpackhi_epi32(in0, in1); \ __m128i in23a = _mm_unpacklo_epi32(in2, in3); \ __m128i in23b = _mm_unpackhi_epi32(in2, in3); \ var0 = _mm_unpacklo_epi64(in01a, in23a); \ var1 = _mm_unpackhi_epi64(in01a, in23a); \ var2 = _mm_unpacklo_epi64(in01b, in23b); \ var3 = _mm_unpackhi_epi64(in01b, in23b); \ } #define ROTATE(a, r) (r == 16 ? \ _mm_shufflehi_epi16(_mm_shufflelo_epi16(a, 0xb1), 0xb1) \ : _mm_or_si128(_mm_slli_epi32(a, r), _mm_srli_epi32(a, 32-r)) \ ) #define md5mb_regions_sse 4 #define md5mb_max_regions_sse md5mb_regions_sse #define md5mb_alignment_sse 16 #define F 1 #define G 2 #define H 3 #define I 4 #define ADDF(f,a,b,c,d) ( \ f==G ? ADD(ADD(_mm_andnot_si128(d, c), a), _mm_and_si128(d, b)) : ADD(a, \ f==F ? _mm_xor_si128(_mm_and_si128(_mm_xor_si128(c, d), b), d) : ( \ f==H ? _mm_xor_si128(_mm_xor_si128(d, c), b) : \ _mm_xor_si128(_mm_or_si128(_mm_xor_si128(d, _mm_set1_epi8(-1)), b), c) \ ) \ ) \ ) #define _FN(f) f##_sse #include "md5mb-base.h" #define MD5X2 #include "md5mb-base.h" #undef MD5X2 #undef _FN static HEDLEY_ALWAYS_INLINE void md5_extract_mb_sse(void* dst, void* state, int idx) { HEDLEY_ASSUME(idx >= 0 && idx < md5mb_regions_sse*2); // 2 = md5mb_interleave __m128i* state_ = (__m128i*)state + (idx & 4); __m128i tmp1 = _mm_unpacklo_epi32(state_[0], state_[1]); __m128i tmp2 = _mm_unpackhi_epi32(state_[0], state_[1]); __m128i tmp3 = _mm_unpacklo_epi32(state_[2], state_[3]); __m128i tmp4 = _mm_unpackhi_epi32(state_[2], state_[3]); idx &= 3; if(idx == 0) _mm_storeu_si128((__m128i*)dst, _mm_unpacklo_epi64(tmp1, tmp3)); if(idx == 1) _mm_storeu_si128((__m128i*)dst, _mm_unpackhi_epi64(tmp1, tmp3)); if(idx == 2) _mm_storeu_si128((__m128i*)dst, _mm_unpacklo_epi64(tmp2, tmp4)); if(idx == 3) _mm_storeu_si128((__m128i*)dst, _mm_unpackhi_epi64(tmp2, tmp4)); } static HEDLEY_ALWAYS_INLINE void md5_extract_all_mb_sse(void* dst, void* state, int group) { __m128i* state_ = (__m128i*)state + group*4; __m128i tmp1 = _mm_unpacklo_epi32(state_[0], state_[1]); __m128i tmp2 = _mm_unpackhi_epi32(state_[0], state_[1]); __m128i tmp3 = _mm_unpacklo_epi32(state_[2], state_[3]); __m128i tmp4 = _mm_unpackhi_epi32(state_[2], state_[3]); __m128i* dst_ = (__m128i*)dst; _mm_storeu_si128(dst_+0, _mm_unpacklo_epi64(tmp1, tmp3)); _mm_storeu_si128(dst_+1, _mm_unpackhi_epi64(tmp1, tmp3)); _mm_storeu_si128(dst_+2, _mm_unpacklo_epi64(tmp2, tmp4)); _mm_storeu_si128(dst_+3, _mm_unpackhi_epi64(tmp2, tmp4)); } #endif #ifdef IOFFSET # undef IOFFSET #endif #define IOFFSET -1 #ifdef __AVX__ # define md5mb_regions_avx md5mb_regions_sse # define md5mb_max_regions_avx md5mb_regions_avx # define md5mb_alignment_avx md5mb_alignment_sse #undef ADDF #define ADDF(f,a,b,c,d) ( \ f==G ? ADD(ADD(_mm_andnot_si128(d, c), a), _mm_and_si128(d, b)) : ( \ f==I ? _mm_sub_epi32(a, _mm_xor_si128(c, _mm_andnot_si128(b, d))) : ADD(a, f==F ? \ _mm_xor_si128(_mm_and_si128(_mm_xor_si128(c, d), b), d) : \ _mm_xor_si128(_mm_xor_si128(d, c), b) \ ) \ ) \ ) # define _FN(f) f##_avx # include "md5mb-base.h" # define MD5X2 # include "md5mb-base.h" # undef MD5X2 # undef _FN # define md5_extract_mb_avx md5_extract_mb_sse # define md5_extract_all_mb_avx md5_extract_all_mb_sse #endif #ifdef ROTATE # undef ROTATE #endif #ifdef __XOP__ #ifdef _MSC_VER # include #else # include #endif #define ROTATE _mm_roti_epi32 #define md5mb_regions_xop md5mb_regions_sse #define md5mb_max_regions_xop md5mb_regions_xop #define md5mb_alignment_xop md5mb_alignment_sse #undef ADDF #define ADDF(f,a,b,c,d) ( \ f==G ? ADD(a, _mm_cmov_si128(b, c, d)) : ( \ f==I ? _mm_sub_epi32(a, _mm_cmov_si128(c, _mm_xor_si128(d, c), b)) : ADD(a, f==F ? \ _mm_cmov_si128(c, d, b) : \ _mm_xor_si128(_mm_xor_si128(d, c), b) \ ) \ ) \ ) #define _FN(f) f##_xop #include "md5mb-base.h" #define MD5X2 #include "md5mb-base.h" #undef MD5X2 #undef _FN #undef ROTATE #define md5_extract_mb_xop md5_extract_mb_sse #define md5_extract_all_mb_xop md5_extract_all_mb_sse #endif #ifdef IOFFSET # undef IOFFSET #endif #ifdef __AVX512VL__ #include #define ROTATE _mm_rol_epi32 #define md5mb_regions_avx512_128 md5mb_regions_sse #define md5mb_max_regions_avx512_128 md5mb_regions_avx512_128 #define md5mb_alignment_avx512_128 md5mb_alignment_sse #undef F #undef G #undef H #undef I #ifdef ADDF # undef ADDF #endif # define F(b,c,d) _mm_ternarylogic_epi32(d,c,b,0xD8) # define G(b,c,d) _mm_ternarylogic_epi32(d,c,b,0xAC) # define H(b,c,d) _mm_ternarylogic_epi32(d,c,b,0x96) # define I(b,c,d) _mm_ternarylogic_epi32(d,c,b,0x63) #define _FN(f) f##_avx512_128 #include "md5mb-base.h" #undef _FN #undef ROTATE #define md5_extract_mb_avx512_128 md5_extract_mb_sse #define md5_extract_all_mb_avx512_128 md5_extract_all_mb_sse #endif #ifdef __AVX2__ #include #undef ADD #undef VAL #undef word_t #undef LOAD4 #undef F #undef G #undef H #undef I #ifdef ADDF # undef ADDF #endif #define ADD _mm256_add_epi32 #define VAL _mm256_set1_epi32 #define word_t __m256i #define LOAD8(set, ptr, offs, idx, var0, var1, var2, var3, var4, var5, var6, var7) { \ __m256i in0 = _mm256_loadu_si256((__m256i*)(ptr[0+set*8] + offs + idx*4)); \ __m256i in1 = _mm256_loadu_si256((__m256i*)(ptr[1+set*8] + offs + idx*4)); \ __m256i in2 = _mm256_loadu_si256((__m256i*)(ptr[2+set*8] + offs + idx*4)); \ __m256i in3 = _mm256_loadu_si256((__m256i*)(ptr[3+set*8] + offs + idx*4)); \ __m256i in4 = _mm256_loadu_si256((__m256i*)(ptr[4+set*8] + offs + idx*4)); \ __m256i in5 = _mm256_loadu_si256((__m256i*)(ptr[5+set*8] + offs + idx*4)); \ __m256i in6 = _mm256_loadu_si256((__m256i*)(ptr[6+set*8] + offs + idx*4)); \ __m256i in7 = _mm256_loadu_si256((__m256i*)(ptr[7+set*8] + offs + idx*4)); \ __m256i in01a = _mm256_unpacklo_epi32(in0, in1); \ __m256i in01b = _mm256_unpackhi_epi32(in0, in1); \ __m256i in23a = _mm256_unpacklo_epi32(in2, in3); \ __m256i in23b = _mm256_unpackhi_epi32(in2, in3); \ __m256i in45a = _mm256_unpacklo_epi32(in4, in5); \ __m256i in45b = _mm256_unpackhi_epi32(in4, in5); \ __m256i in67a = _mm256_unpacklo_epi32(in6, in7); \ __m256i in67b = _mm256_unpackhi_epi32(in6, in7); \ __m256i in0123a = _mm256_unpacklo_epi64(in01a, in23a); \ __m256i in0123b = _mm256_unpackhi_epi64(in01a, in23a); \ __m256i in0123c = _mm256_unpacklo_epi64(in01b, in23b); \ __m256i in0123d = _mm256_unpackhi_epi64(in01b, in23b); \ __m256i in4567a = _mm256_unpacklo_epi64(in45a, in67a); \ __m256i in4567b = _mm256_unpackhi_epi64(in45a, in67a); \ __m256i in4567c = _mm256_unpacklo_epi64(in45b, in67b); \ __m256i in4567d = _mm256_unpackhi_epi64(in45b, in67b); \ var0 = _mm256_inserti128_si256(in0123a, _mm256_castsi256_si128(in4567a), 1); \ var1 = _mm256_inserti128_si256(in0123b, _mm256_castsi256_si128(in4567b), 1); \ var2 = _mm256_inserti128_si256(in0123c, _mm256_castsi256_si128(in4567c), 1); \ var3 = _mm256_inserti128_si256(in0123d, _mm256_castsi256_si128(in4567d), 1); \ var4 = _mm256_permute2x128_si256(in0123a, in4567a, 0x31); \ var5 = _mm256_permute2x128_si256(in0123b, in4567b, 0x31); \ var6 = _mm256_permute2x128_si256(in0123c, in4567c, 0x31); \ var7 = _mm256_permute2x128_si256(in0123d, in4567d, 0x31); \ } #define ROTATE(a, r) (r == 16 ? \ _mm256_shuffle_epi8(a, _mm256_set_epi32(0x0d0c0f0e, 0x09080b0a, 0x05040706, 0x01000302, 0x0d0c0f0e, 0x09080b0a, 0x05040706, 0x01000302)) \ : _mm256_or_si256(_mm256_slli_epi32(a, r), _mm256_srli_epi32(a, 32-r)) \ ) #define md5mb_regions_avx2 8 #define md5mb_max_regions_avx2 md5mb_regions_avx2 #define md5mb_alignment_avx2 32 #ifdef IOFFSET # undef IOFFSET #endif #define IOFFSET -1 #define F 1 #define G 2 #define H 3 #define I 4 #define ADDF(f,a,b,c,d) ( \ f==G ? ADD(ADD(_mm256_andnot_si256(d, c), a), _mm256_and_si256(d, b)) : ( \ f==I ? _mm256_sub_epi32(a, _mm256_xor_si256(c, _mm256_andnot_si256(b, d))) : ADD(a, f==F ? \ _mm256_xor_si256(_mm256_and_si256(_mm256_xor_si256(c, d), b), d) : \ _mm256_xor_si256(_mm256_xor_si256(d, c), b) \ ) \ ) \ ) #define _FN(f) f##_avx2 #include "md5mb-base.h" #define MD5X2 #include "md5mb-base.h" #undef MD5X2 #undef _FN static HEDLEY_ALWAYS_INLINE void md5_extract_mb_avx2(void* dst, void* state, int idx) { HEDLEY_ASSUME(idx >= 0 && idx < md5mb_regions_avx2*2); __m256i* state_ = (__m256i*)state + ((idx & 8) >> 1); __m256i tmpAB0 = _mm256_unpacklo_epi32(state_[0], state_[1]); __m256i tmpAB2 = _mm256_unpackhi_epi32(state_[0], state_[1]); __m256i tmpCD0 = _mm256_unpacklo_epi32(state_[2], state_[3]); __m256i tmpCD2 = _mm256_unpackhi_epi32(state_[2], state_[3]); __m256i tmp0 = _mm256_unpacklo_epi64(tmpAB0, tmpCD0); __m256i tmp1 = _mm256_unpackhi_epi64(tmpAB0, tmpCD0); __m256i tmp2 = _mm256_unpacklo_epi64(tmpAB2, tmpCD2); __m256i tmp3 = _mm256_unpackhi_epi64(tmpAB2, tmpCD2); idx &= 7; if(idx == 0) _mm_storeu_si128((__m128i*)dst, _mm256_castsi256_si128(tmp0)); if(idx == 1) _mm_storeu_si128((__m128i*)dst, _mm256_castsi256_si128(tmp1)); if(idx == 2) _mm_storeu_si128((__m128i*)dst, _mm256_castsi256_si128(tmp2)); if(idx == 3) _mm_storeu_si128((__m128i*)dst, _mm256_castsi256_si128(tmp3)); if(idx == 4) _mm_storeu_si128((__m128i*)dst, _mm256_extracti128_si256(tmp0, 1)); if(idx == 5) _mm_storeu_si128((__m128i*)dst, _mm256_extracti128_si256(tmp1, 1)); if(idx == 6) _mm_storeu_si128((__m128i*)dst, _mm256_extracti128_si256(tmp2, 1)); if(idx == 7) _mm_storeu_si128((__m128i*)dst, _mm256_extracti128_si256(tmp3, 1)); } static HEDLEY_ALWAYS_INLINE void md5_extract_all_mb_avx2(void* dst, void* state, int group) { __m256i* state_ = (__m256i*)state + group*4; __m256i tmpAB0 = _mm256_unpacklo_epi32(state_[0], state_[1]); __m256i tmpAB2 = _mm256_unpackhi_epi32(state_[0], state_[1]); __m256i tmpCD0 = _mm256_unpacklo_epi32(state_[2], state_[3]); __m256i tmpCD2 = _mm256_unpackhi_epi32(state_[2], state_[3]); __m256i tmp0 = _mm256_unpacklo_epi64(tmpAB0, tmpCD0); __m256i tmp1 = _mm256_unpackhi_epi64(tmpAB0, tmpCD0); __m256i tmp2 = _mm256_unpacklo_epi64(tmpAB2, tmpCD2); __m256i tmp3 = _mm256_unpackhi_epi64(tmpAB2, tmpCD2); __m256i* dst_ = (__m256i*)dst; _mm256_storeu_si256(dst_+0, _mm256_inserti128_si256(tmp0, _mm256_castsi256_si128(tmp1), 1)); _mm256_storeu_si256(dst_+1, _mm256_inserti128_si256(tmp2, _mm256_castsi256_si128(tmp3), 1)); _mm256_storeu_si256(dst_+2, _mm256_permute2x128_si256(tmp0, tmp1, 0x31)); _mm256_storeu_si256(dst_+3, _mm256_permute2x128_si256(tmp2, tmp3, 0x31)); } #endif #ifdef ADDF # undef ADDF #endif #ifdef IOFFSET # undef IOFFSET #endif #ifdef __AVX512VL__ #undef ROTATE #define ROTATE _mm256_rol_epi32 #define md5mb_regions_avx512_256 md5mb_regions_avx2 #define md5mb_max_regions_avx512_256 md5mb_regions_avx512_256 #define md5mb_alignment_avx512_256 md5mb_alignment_avx2 #undef F #undef G #undef H #undef I # define F(b,c,d) _mm256_ternarylogic_epi32(d,c,b,0xD8) # define G(b,c,d) _mm256_ternarylogic_epi32(d,c,b,0xAC) # define H(b,c,d) _mm256_ternarylogic_epi32(d,c,b,0x96) # define I(b,c,d) _mm256_ternarylogic_epi32(d,c,b,0x63) #define _FN(f) f##_avx512_256 #include "md5mb-base.h" #undef _FN #define md5_extract_mb_avx512_256 md5_extract_mb_avx2 #define md5_extract_all_mb_avx512_256 md5_extract_all_mb_avx2 #endif #ifdef __AVX512F__ #include #undef ROTATE #undef ADD #undef VAL #undef word_t #undef LOAD8 #define ADD _mm512_add_epi32 #define VAL _mm512_set1_epi32 #define word_t __m512i #define LOAD16(set, ptr, offs, var0, var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, var11, var12, var13, var14, var15) { \ __m512i in0 = _mm512_loadu_si512(ptr[0+set*16] + offs); \ __m512i in1 = _mm512_loadu_si512(ptr[1+set*16] + offs); \ __m512i in2 = _mm512_loadu_si512(ptr[2+set*16] + offs); \ __m512i in3 = _mm512_loadu_si512(ptr[3+set*16] + offs); \ __m512i in4 = _mm512_loadu_si512(ptr[4+set*16] + offs); \ __m512i in5 = _mm512_loadu_si512(ptr[5+set*16] + offs); \ __m512i in6 = _mm512_loadu_si512(ptr[6+set*16] + offs); \ __m512i in7 = _mm512_loadu_si512(ptr[7+set*16] + offs); \ __m512i in8 = _mm512_loadu_si512(ptr[8+set*16] + offs); \ __m512i in9 = _mm512_loadu_si512(ptr[9+set*16] + offs); \ __m512i in10 = _mm512_loadu_si512(ptr[10+set*16] + offs); \ __m512i in11 = _mm512_loadu_si512(ptr[11+set*16] + offs); \ __m512i in12 = _mm512_loadu_si512(ptr[12+set*16] + offs); \ __m512i in13 = _mm512_loadu_si512(ptr[13+set*16] + offs); \ __m512i in14 = _mm512_loadu_si512(ptr[14+set*16] + offs); \ __m512i in15 = _mm512_loadu_si512(ptr[15+set*16] + offs); \ __m512i in01a = _mm512_unpacklo_epi32(in0, in1); \ __m512i in01b = _mm512_unpackhi_epi32(in0, in1); \ __m512i in23a = _mm512_unpacklo_epi32(in2, in3); \ __m512i in23b = _mm512_unpackhi_epi32(in2, in3); \ __m512i in45a = _mm512_unpacklo_epi32(in4, in5); \ __m512i in45b = _mm512_unpackhi_epi32(in4, in5); \ __m512i in67a = _mm512_unpacklo_epi32(in6, in7); \ __m512i in67b = _mm512_unpackhi_epi32(in6, in7); \ __m512i in89a = _mm512_unpacklo_epi32(in8, in9); \ __m512i in89b = _mm512_unpackhi_epi32(in8, in9); \ __m512i in101a = _mm512_unpacklo_epi32(in10, in11); \ __m512i in101b = _mm512_unpackhi_epi32(in10, in11); \ __m512i in123a = _mm512_unpacklo_epi32(in12, in13); \ __m512i in123b = _mm512_unpackhi_epi32(in12, in13); \ __m512i in145a = _mm512_unpacklo_epi32(in14, in15); \ __m512i in145b = _mm512_unpackhi_epi32(in14, in15); \ __m512i in0123a = _mm512_unpacklo_epi64(in01a, in23a); \ __m512i in0123b = _mm512_unpackhi_epi64(in01a, in23a); \ __m512i in0123c = _mm512_unpacklo_epi64(in01b, in23b); \ __m512i in0123d = _mm512_unpackhi_epi64(in01b, in23b); \ __m512i in4567a = _mm512_unpacklo_epi64(in45a, in67a); \ __m512i in4567b = _mm512_unpackhi_epi64(in45a, in67a); \ __m512i in4567c = _mm512_unpacklo_epi64(in45b, in67b); \ __m512i in4567d = _mm512_unpackhi_epi64(in45b, in67b); \ __m512i in8901a = _mm512_unpacklo_epi64(in89a, in101a); \ __m512i in8901b = _mm512_unpackhi_epi64(in89a, in101a); \ __m512i in8901c = _mm512_unpacklo_epi64(in89b, in101b); \ __m512i in8901d = _mm512_unpackhi_epi64(in89b, in101b); \ __m512i in2345a = _mm512_unpacklo_epi64(in123a, in145a); \ __m512i in2345b = _mm512_unpackhi_epi64(in123a, in145a); \ __m512i in2345c = _mm512_unpacklo_epi64(in123b, in145b); \ __m512i in2345d = _mm512_unpackhi_epi64(in123b, in145b); \ __m512i in07a = _mm512_inserti64x4(in0123a, _mm512_castsi512_si256(in4567a), 1); \ __m512i in07b = _mm512_inserti64x4(in0123b, _mm512_castsi512_si256(in4567b), 1); \ __m512i in07c = _mm512_inserti64x4(in0123c, _mm512_castsi512_si256(in4567c), 1); \ __m512i in07d = _mm512_inserti64x4(in0123d, _mm512_castsi512_si256(in4567d), 1); \ __m512i in07e = _mm512_shuffle_i64x2(in0123a, in4567a, _MM_SHUFFLE(3,2,3,2)); \ __m512i in07f = _mm512_shuffle_i64x2(in0123b, in4567b, _MM_SHUFFLE(3,2,3,2)); \ __m512i in07g = _mm512_shuffle_i64x2(in0123c, in4567c, _MM_SHUFFLE(3,2,3,2)); \ __m512i in07h = _mm512_shuffle_i64x2(in0123d, in4567d, _MM_SHUFFLE(3,2,3,2)); \ __m512i in85a = _mm512_inserti64x4(in8901a, _mm512_castsi512_si256(in2345a), 1); \ __m512i in85b = _mm512_inserti64x4(in8901b, _mm512_castsi512_si256(in2345b), 1); \ __m512i in85c = _mm512_inserti64x4(in8901c, _mm512_castsi512_si256(in2345c), 1); \ __m512i in85d = _mm512_inserti64x4(in8901d, _mm512_castsi512_si256(in2345d), 1); \ __m512i in85e = _mm512_shuffle_i64x2(in8901a, in2345a, _MM_SHUFFLE(3,2,3,2)); \ __m512i in85f = _mm512_shuffle_i64x2(in8901b, in2345b, _MM_SHUFFLE(3,2,3,2)); \ __m512i in85g = _mm512_shuffle_i64x2(in8901c, in2345c, _MM_SHUFFLE(3,2,3,2)); \ __m512i in85h = _mm512_shuffle_i64x2(in8901d, in2345d, _MM_SHUFFLE(3,2,3,2)); \ var0 = _mm512_shuffle_i64x2(in07a, in85a, _MM_SHUFFLE(2,0,2,0)); \ var1 = _mm512_shuffle_i64x2(in07b, in85b, _MM_SHUFFLE(2,0,2,0)); \ var2 = _mm512_shuffle_i64x2(in07c, in85c, _MM_SHUFFLE(2,0,2,0)); \ var3 = _mm512_shuffle_i64x2(in07d, in85d, _MM_SHUFFLE(2,0,2,0)); \ var4 = _mm512_shuffle_i64x2(in07a, in85a, _MM_SHUFFLE(3,1,3,1)); \ var5 = _mm512_shuffle_i64x2(in07b, in85b, _MM_SHUFFLE(3,1,3,1)); \ var6 = _mm512_shuffle_i64x2(in07c, in85c, _MM_SHUFFLE(3,1,3,1)); \ var7 = _mm512_shuffle_i64x2(in07d, in85d, _MM_SHUFFLE(3,1,3,1)); \ var8 = _mm512_shuffle_i64x2(in07e, in85e, _MM_SHUFFLE(2,0,2,0)); \ var9 = _mm512_shuffle_i64x2(in07f, in85f, _MM_SHUFFLE(2,0,2,0)); \ var10 = _mm512_shuffle_i64x2(in07g, in85g, _MM_SHUFFLE(2,0,2,0)); \ var11 = _mm512_shuffle_i64x2(in07h, in85h, _MM_SHUFFLE(2,0,2,0)); \ var12 = _mm512_shuffle_i64x2(in07e, in85e, _MM_SHUFFLE(3,1,3,1)); \ var13 = _mm512_shuffle_i64x2(in07f, in85f, _MM_SHUFFLE(3,1,3,1)); \ var14 = _mm512_shuffle_i64x2(in07g, in85g, _MM_SHUFFLE(3,1,3,1)); \ var15 = _mm512_shuffle_i64x2(in07h, in85h, _MM_SHUFFLE(3,1,3,1)); \ } #define ROTATE _mm512_rol_epi32 #define md5mb_regions_avx512 16 #define md5mb_max_regions_avx512 md5mb_regions_avx512 #define md5mb_alignment_avx512 64 #undef F #undef G #undef H #undef I # define F(b,c,d) _mm512_ternarylogic_epi32(d,c,b,0xD8) # define G(b,c,d) _mm512_ternarylogic_epi32(d,c,b,0xAC) # define H(b,c,d) _mm512_ternarylogic_epi32(d,c,b,0x96) # define I(b,c,d) _mm512_ternarylogic_epi32(d,c,b,0x63) #define _FN(f) f##_avx512 #include "md5mb-base.h" #define MD5X2 #include "md5mb-base.h" #undef MD5X2 #undef _FN #undef ROTATE #undef LOAD16 static HEDLEY_ALWAYS_INLINE void md5_extract_mb_avx512(void* dst, void* state, int idx) { HEDLEY_ASSUME(idx >= 0 && idx < md5mb_regions_avx512*2); __m512i* state_ = (__m512i*)state + ((idx & 16) >> 2); __m512i tmpAB0 = _mm512_unpacklo_epi32(state_[0], state_[1]); __m512i tmpAB2 = _mm512_unpackhi_epi32(state_[0], state_[1]); __m512i tmpCD0 = _mm512_unpacklo_epi32(state_[2], state_[3]); __m512i tmpCD2 = _mm512_unpackhi_epi32(state_[2], state_[3]); __m512i tmp0 = _mm512_unpacklo_epi64(tmpAB0, tmpCD0); __m512i tmp1 = _mm512_unpackhi_epi64(tmpAB0, tmpCD0); __m512i tmp2 = _mm512_unpacklo_epi64(tmpAB2, tmpCD2); __m512i tmp3 = _mm512_unpackhi_epi64(tmpAB2, tmpCD2); idx &= 15; if(idx == 0) _mm_storeu_si128((__m128i*)dst, _mm512_castsi512_si128(tmp0)); if(idx == 1) _mm_storeu_si128((__m128i*)dst, _mm512_castsi512_si128(tmp1)); if(idx == 2) _mm_storeu_si128((__m128i*)dst, _mm512_castsi512_si128(tmp2)); if(idx == 3) _mm_storeu_si128((__m128i*)dst, _mm512_castsi512_si128(tmp3)); if(idx == 4) _mm_storeu_si128((__m128i*)dst, _mm512_extracti32x4_epi32(tmp0, 1)); if(idx == 5) _mm_storeu_si128((__m128i*)dst, _mm512_extracti32x4_epi32(tmp1, 1)); if(idx == 6) _mm_storeu_si128((__m128i*)dst, _mm512_extracti32x4_epi32(tmp2, 1)); if(idx == 7) _mm_storeu_si128((__m128i*)dst, _mm512_extracti32x4_epi32(tmp3, 1)); if(idx == 8) _mm_storeu_si128((__m128i*)dst, _mm512_extracti32x4_epi32(tmp0, 2)); if(idx == 9) _mm_storeu_si128((__m128i*)dst, _mm512_extracti32x4_epi32(tmp1, 2)); if(idx == 10) _mm_storeu_si128((__m128i*)dst, _mm512_extracti32x4_epi32(tmp2, 2)); if(idx == 11) _mm_storeu_si128((__m128i*)dst, _mm512_extracti32x4_epi32(tmp3, 2)); if(idx == 12) _mm_storeu_si128((__m128i*)dst, _mm512_extracti32x4_epi32(tmp0, 3)); if(idx == 13) _mm_storeu_si128((__m128i*)dst, _mm512_extracti32x4_epi32(tmp1, 3)); if(idx == 14) _mm_storeu_si128((__m128i*)dst, _mm512_extracti32x4_epi32(tmp2, 3)); if(idx == 15) _mm_storeu_si128((__m128i*)dst, _mm512_extracti32x4_epi32(tmp3, 3)); } static HEDLEY_ALWAYS_INLINE void md5_extract_all_mb_avx512(void* dst, void* state, int group) { __m512i* state_ = (__m512i*)state + group*4; __m512i tmpAB0 = _mm512_unpacklo_epi32(state_[0], state_[1]); __m512i tmpAB2 = _mm512_unpackhi_epi32(state_[0], state_[1]); __m512i tmpCD0 = _mm512_unpacklo_epi32(state_[2], state_[3]); __m512i tmpCD2 = _mm512_unpackhi_epi32(state_[2], state_[3]); __m512i tmp0 = _mm512_unpacklo_epi64(tmpAB0, tmpCD0); __m512i tmp1 = _mm512_unpackhi_epi64(tmpAB0, tmpCD0); __m512i tmp2 = _mm512_unpacklo_epi64(tmpAB2, tmpCD2); __m512i tmp3 = _mm512_unpackhi_epi64(tmpAB2, tmpCD2); __m512i out0415 = _mm512_inserti64x4(tmp0, _mm512_castsi512_si256(tmp1), 1); __m512i out2637 = _mm512_inserti64x4(tmp2, _mm512_castsi512_si256(tmp3), 1); __m512i out8C9D = _mm512_shuffle_i64x2(tmp0, tmp1, _MM_SHUFFLE(3,2,3,2)); __m512i outAEBF = _mm512_shuffle_i64x2(tmp2, tmp3, _MM_SHUFFLE(3,2,3,2)); __m512i out0123 = _mm512_shuffle_i64x2(out0415, out2637, _MM_SHUFFLE(2,0,2,0)); __m512i out4567 = _mm512_shuffle_i64x2(out0415, out2637, _MM_SHUFFLE(3,1,3,1)); __m512i out89AB = _mm512_shuffle_i64x2(out8C9D, outAEBF, _MM_SHUFFLE(2,0,2,0)); __m512i outCDEF = _mm512_shuffle_i64x2(out8C9D, outAEBF, _MM_SHUFFLE(3,1,3,1)); __m512i* dst_ = (__m512i*)dst; _mm512_storeu_si512(dst_+0, out0123); _mm512_storeu_si512(dst_+1, out4567); _mm512_storeu_si512(dst_+2, out89AB); _mm512_storeu_si512(dst_+3, outCDEF); } #endif #ifdef ADD # undef ADD # undef VAL # undef word_t # undef INPUT # undef LOAD #endif #ifdef F # undef F # undef G # undef H # undef I #endif #ifdef IOFFSET # undef IOFFSET #endif #undef INPUT #undef LOAD par2cmdline-turbo-1.4.0/parpar/hasher/md5mb-sve2.h000066400000000000000000000226701514221355600216510ustar00rootroot00000000000000 #if defined(__ARM_FEATURE_SVE2) && __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ #include // have found Clang 11 to mis-compile this, but works on 12 #if defined(__clang__) && __clang_major__ < 12 && defined(__OPTIMIZE__) HEDLEY_WARNING("Clang prior to version 12 may break SVE2 MD5 code"); #endif #define ADD(a, b) svadd_u32_x(svptrue_b32(), a, b) #define VAL svdup_n_u32 #define STATE_WORD_SIZE svcntb() #define word_t svuint32_t #define INPUT(k, set, ptr, offs, idx, var) svadd_n_u32_x(svptrue_b32(), var, k) #define LOAD INPUT #define LOAD_STATE(state, n) svld1_vnum_u32(svptrue_b32(), (const uint32_t*)state, n) #define SET_STATE(state, n, val) svst1_vnum_u32(svptrue_b32(), (uint32_t*)state, n, val) /* // if we want to try width specific implementations... #define LOAD4(set, ptr, offs, idx, var0, var1, var2, var3) { \ var0 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[0+set*4] + offs + idx*4)); \ var1 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[1+set*4] + offs + idx*4)); \ var2 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[2+set*4] + offs + idx*4)); \ var3 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[3+set*4] + offs + idx*4)); \ svuint32_t in0 = svzip1_u32(var0, var2); \ svuint32_t in1 = svzip2_u32(var0, var2); \ svuint32_t in2 = svzip1_u32(var1, var3); \ svuint32_t in3 = svzip2_u32(var1, var3); \ var0 = svzip1_u32(in0, in2); \ var1 = svzip2_u32(in0, in2); \ var2 = svzip1_u32(in1, in3); \ var3 = svzip2_u32(in1, in3); \ } #define LOAD8(set, ptr, offs, idx, var0, var1, var2, var3, var4, var5, var6, var7) { \ var0 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[0+set*8] + offs + idx*4)); \ var1 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[1+set*8] + offs + idx*4)); \ var2 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[2+set*8] + offs + idx*4)); \ var3 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[3+set*8] + offs + idx*4)); \ var4 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[4+set*8] + offs + idx*4)); \ var5 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[5+set*8] + offs + idx*4)); \ var6 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[6+set*8] + offs + idx*4)); \ var7 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[7+set*8] + offs + idx*4)); \ svuint32_t in0 = svzip1_u32(var0, var4); \ svuint32_t in1 = svzip2_u32(var0, var4); \ svuint32_t in2 = svzip1_u32(var1, var5); \ svuint32_t in3 = svzip2_u32(var1, var5); \ svuint32_t in4 = svzip1_u32(var2, var6); \ svuint32_t in5 = svzip2_u32(var2, var6); \ svuint32_t in6 = svzip1_u32(var3, var7); \ svuint32_t in7 = svzip2_u32(var3, var7); \ svuint32_t in0b = svzip1_u32(in0, in4); \ svuint32_t in1b = svzip2_u32(in0, in4); \ svuint32_t in2b = svzip1_u32(in1, in5); \ svuint32_t in3b = svzip2_u32(in1, in5); \ svuint32_t in4b = svzip1_u32(in2, in6); \ svuint32_t in5b = svzip2_u32(in2, in6); \ svuint32_t in6b = svzip1_u32(in3, in7); \ svuint32_t in7b = svzip2_u32(in3, in7); \ var0 = svzip1_u32(in0b, in4b); \ var1 = svzip2_u32(in0b, in4b); \ var2 = svzip1_u32(in1b, in5b); \ var3 = svzip2_u32(in1b, in5b); \ var4 = svzip1_u32(in2b, in6b); \ var5 = svzip2_u32(in2b, in6b); \ var6 = svzip1_u32(in3b, in7b); \ var7 = svzip2_u32(in3b, in7b); \ } #define LOAD16(set, ptr, offs, var0, var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, var11, var12, var13, var14, var15) { \ var0 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[0+set*16] + offs)); \ var1 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[1+set*16] + offs)); \ var2 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[2+set*16] + offs)); \ var3 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[3+set*16] + offs)); \ var4 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[4+set*16] + offs)); \ var5 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[5+set*16] + offs)); \ var6 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[6+set*16] + offs)); \ var7 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[7+set*16] + offs)); \ var8 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[8+set*16] + offs)); \ var9 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[9+set*16] + offs)); \ var10 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[10+set*16] + offs)); \ var11 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[11+set*16] + offs)); \ var12 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[12+set*16] + offs)); \ var13 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[13+set*16] + offs)); \ var14 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[14+set*16] + offs)); \ var15 = svld1_u32(svptrue_b32(), (uint32_t*)(ptr[15+set*16] + offs)); \ svuint32_t in0 = svzip1_u32(var0, var8); \ svuint32_t in1 = svzip2_u32(var0, var8); \ svuint32_t in2 = svzip1_u32(var1, var9); \ svuint32_t in3 = svzip2_u32(var1, var9); \ svuint32_t in4 = svzip1_u32(var2, var10); \ svuint32_t in5 = svzip2_u32(var2, var10); \ svuint32_t in6 = svzip1_u32(var3, var11); \ svuint32_t in7 = svzip2_u32(var3, var11); \ svuint32_t in8 = svzip1_u32(var4, var12); \ svuint32_t in9 = svzip2_u32(var4, var12); \ svuint32_t in10 = svzip1_u32(var5, var13); \ svuint32_t in11 = svzip2_u32(var5, var13); \ svuint32_t in12 = svzip1_u32(var6, var14); \ svuint32_t in13 = svzip2_u32(var6, var14); \ svuint32_t in14 = svzip1_u32(var7, var15); \ svuint32_t in15 = svzip2_u32(var7, var15); \ var0 = svzip1_u32(in0, in8); \ var1 = svzip2_u32(in0, in8); \ var2 = svzip1_u32(in1, in9); \ var3 = svzip2_u32(in1, in9); \ var4 = svzip1_u32(in2, in10); \ var5 = svzip2_u32(in2, in10); \ var6 = svzip1_u32(in3, in11); \ var7 = svzip2_u32(in3, in11); \ var8 = svzip1_u32(in4, in12); \ var9 = svzip2_u32(in4, in12); \ var10 = svzip1_u32(in5, in13); \ var11 = svzip2_u32(in5, in13); \ var12 = svzip1_u32(in6, in14); \ var13 = svzip2_u32(in6, in14); \ var14 = svzip1_u32(in7, in15); \ var15 = svzip2_u32(in7, in15); \ in0 = svzip1_u32(var0, var8); \ in1 = svzip2_u32(var0, var8); \ in2 = svzip1_u32(var1, var9); \ in3 = svzip2_u32(var1, var9); \ in4 = svzip1_u32(var2, var10); \ in5 = svzip2_u32(var2, var10); \ in6 = svzip1_u32(var3, var11); \ in7 = svzip2_u32(var3, var11); \ in8 = svzip1_u32(var4, var12); \ in9 = svzip2_u32(var4, var12); \ in10 = svzip1_u32(var5, var13); \ in11 = svzip2_u32(var5, var13); \ in12 = svzip1_u32(var6, var14); \ in13 = svzip2_u32(var6, var14); \ in14 = svzip1_u32(var7, var15); \ in15 = svzip2_u32(var7, var15); \ var0 = svzip1_u32(in0, in8); \ var1 = svzip2_u32(in0, in8); \ var2 = svzip1_u32(in1, in9); \ var3 = svzip2_u32(in1, in9); \ var4 = svzip1_u32(in2, in10); \ var5 = svzip2_u32(in2, in10); \ var6 = svzip1_u32(in3, in11); \ var7 = svzip2_u32(in3, in11); \ var8 = svzip1_u32(in4, in12); \ var9 = svzip2_u32(in4, in12); \ var10 = svzip1_u32(in5, in13); \ var11 = svzip2_u32(in5, in13); \ var12 = svzip1_u32(in6, in14); \ var13 = svzip2_u32(in6, in14); \ var14 = svzip1_u32(in7, in15); \ var15 = svzip2_u32(in7, in15); \ } */ // using gather #define LOAD2(set, ptr, offs, idx, var0, var1) { \ svuint64x2_t base = svld2_u64(svptrue_b64(), (const uint64_t*)(ptr + set*svcntw())); \ svuint32_t data0 = svreinterpret_u32_u64(svld1_gather_offset_u64(svptrue_b64(), svget2(base, 0), offs + idx*4)); \ svuint32_t data1 = svreinterpret_u32_u64(svld1_gather_offset_u64(svptrue_b64(), svget2(base, 1), offs + idx*4)); \ var0 = svtrn1_u32(data0, data1); \ var1 = svtrn2_u32(data0, data1); \ } #define ROTATE(a, r) svxar_n_u32(a, svdup_u32(0), 32-r) #define md5mb_regions_sve2 svcntw() #define md5mb_max_regions_sve2 64 #define md5mb_alignment_sve2 16 #define F(b,c,d) svbsl_u32(c, d, b) #define G(b,c,d) svbsl_u32(b, c, d) #define H(b,c,d) sveor3_u32(c, d, b) #define I(b,c,d) svnbsl_u32(c, sveor_u32_x(svptrue_b32(), c, d), b) #define _FN(f) f##_sve2 #include "md5mb-base.h" #define MD5X2 #include "md5mb-base.h" #undef MD5X2 #undef _FN #undef ROTATE #undef ADD #undef VAL #undef word_t #undef STATE_WORD_SIZE #undef INPUT #undef LOAD #undef LOAD4 #undef LOAD_STATE #undef SET_STATE #undef F #undef G #undef H #undef I static HEDLEY_ALWAYS_INLINE void md5_extract_mb_sve2(void* dst, void* state, int idx) { uint32_t* state_ = (uint32_t*)state; if(idx >= (int)svcntw()) { state_ += 4*svcntw(); idx -= svcntw(); } // re-arrange to pairs svuint64_t tmp0 = svreinterpret_u64_u32(svzip1_u32(svld1_u32(svptrue_b32(), state_), svld1_vnum_u32(svptrue_b32(), state_, 1))); svuint64_t tmp1 = svreinterpret_u64_u32(svzip2_u32(svld1_u32(svptrue_b32(), state_), svld1_vnum_u32(svptrue_b32(), state_, 1))); svuint64_t tmp2 = svreinterpret_u64_u32(svzip1_u32(svld1_vnum_u32(svptrue_b32(), state_, 2), svld1_vnum_u32(svptrue_b32(), state_, 3))); svuint64_t tmp3 = svreinterpret_u64_u32(svzip2_u32(svld1_vnum_u32(svptrue_b32(), state_, 2), svld1_vnum_u32(svptrue_b32(), state_, 3))); // construct target vector svuint32_t vect; switch(idx / (svcntw()/4)) { case 0: vect = svreinterpret_u32_u64(svzip1_u64(tmp0, tmp2)); break; case 1: vect = svreinterpret_u32_u64(svzip2_u64(tmp0, tmp2)); break; case 2: vect = svreinterpret_u32_u64(svzip1_u64(tmp1, tmp3)); break; case 3: vect = svreinterpret_u32_u64(svzip2_u64(tmp1, tmp3)); break; default: HEDLEY_UNREACHABLE(); } // store 128-bit part of target vector int subIdx = idx % (svcntw()/4); svbool_t mask = svcmpeq_n_u32(svptrue_b32(), svlsr_n_u32_x(svptrue_b32(), svindex_u32(0, 1), 2), subIdx ); svst1_u32(mask, (uint32_t*)dst - subIdx*4, vect); } static HEDLEY_ALWAYS_INLINE void md5_extract_all_mb_sve2(void* dst, void* state, int group) { uint32_t* state_ = (uint32_t*)state + group*(int)svcntb(); svst4_u32(svptrue_b32(), (uint32_t*)dst, svcreate4_u32( svld1_u32(svptrue_b32(), state_), svld1_vnum_u32(svptrue_b32(), state_, 1), svld1_vnum_u32(svptrue_b32(), state_, 2), svld1_vnum_u32(svptrue_b32(), state_, 3) )); } #endif par2cmdline-turbo-1.4.0/parpar/hasher/md5x2-arm-asm.h000066400000000000000000000244541514221355600222640ustar00rootroot00000000000000#include "../src/platform.h" #include "../src/stdint.h" #ifndef STR # define STR_HELPER(x) #x # define STR(x) STR_HELPER(x) #endif #ifndef UNUSED # define UNUSED(...) (void)(__VA_ARGS__) #endif // parent function which inlines this, may need to be marked as targeting ARM #if !defined(__aarch64__) && (!defined(__clang__) || (defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB < 2)) // GCC refuses to allow >9 registers in Thumb mode; Clang has no qualms, as long as it's Thumb2 # define _MD5x2_UPDATEFN_ATTRIB __attribute__((target("arm"))) #endif static HEDLEY_ALWAYS_INLINE void md5_process_block_x2_scalar(uint32_t* state, const uint8_t* const* HEDLEY_RESTRICT data, size_t offset) { UNUSED(offset); uint32_t A1 = state[0]; uint32_t B1 = state[1]; uint32_t C1 = state[2]; uint32_t D1 = state[3]; uint32_t A2 = state[4]; uint32_t B2 = state[5]; uint32_t C2 = state[6]; uint32_t D2 = state[7]; uint32_t tmp1, tmp2; #ifdef __aarch64__ # define SETI_L(dst, x) "mov %w[" dst "], #" STR(x) "\n" # define SETI_H(dst, x) "movk %w[" dst "], #" STR(x) ", lsl #16\n" # define REG(x) "%w[" STR(x) "]" # define ROR_ADD(A1, B1, A2, B2, R) \ "ror %w[" STR(A1) "], %w[" STR(A1) "], #" STR(R) "\n" \ "ror %w[" STR(A2) "], %w[" STR(A2) "], #" STR(R) "\n" \ "add %w[" STR(A1) "], %w[" STR(A1) "], %w[" STR(B1) "]\n" \ "add %w[" STR(A2) "], %w[" STR(A2) "], %w[" STR(B2) "]\n" #else # if defined(__armv7__) || defined(_M_ARM) || defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_8A__) || (defined(__ARM_ARCH) && __ARM_ARCH >= 7) # define SETI_L(dst, x) "movw %[" dst "], #" STR(x) "\n" # define SETI_H(dst, x) "movt %[" dst "], #" STR(x) "\n" # else # define SETI_L(dst, x) "mov %[" dst "], #" STR(x) " & 0xff\n orr %[" dst "], #" STR(x) " & 0xff00\n" # define SETI_H(dst, x) "orr %[" dst "], #(" STR(x) " & 0xff)<<16\n orr %[" dst "], #(" STR(x) " & 0xff00)<<16\n" # endif # define REG(x) "%[" STR(x) "]" # define ROR_ADD(A1, B1, A2, B2, R) \ "add %[" STR(A1) "], %[" STR(B1) "], %[" STR(A1) "], ror #" STR(R) "\n" \ "add %[" STR(A2) "], %[" STR(B2) "], %[" STR(A2) "], ror #" STR(R) "\n" #endif #if __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__ # define LD2(NEXT_IN1, NEXT_IN2) \ "ldr " REG(TMP1) ", " NEXT_IN1 "\n" \ "ldr " REG(TMP2) ", " NEXT_IN2 "\n" #else // REV requires ARMv6 # define LD2(NEXT_IN1, NEXT_IN2) \ "ldr " REG(TMP1) ", " NEXT_IN1 "\n" \ "ldr " REG(TMP2) ", " NEXT_IN2 "\n" \ "rev " REG(TMP1) ", " REG(TMP1) "\n" \ "rev " REG(TMP2) ", " REG(TMP2) "\n" #endif #define ROUND_F(A1, B1, C1, D1, A2, B2, C2, D2, NEXT_IN1, NEXT_IN2, KL, KH, R) \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ SETI_L("TMP1", KL) \ "add " REG(A2) ", " REG(A2) ", " REG(TMP2) "\n" \ SETI_H("TMP1", KH) \ "eor " REG(TMP2) ", " REG(C2) ", " REG(D2) "\n" \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ "add " REG(A2) ", " REG(A2) ", " REG(TMP1) "\n" \ "eor " REG(TMP1) ", " REG(C1) ", " REG(D1) "\n" \ "and " REG(TMP2) ", " REG(TMP2) ", " REG(B2) "\n" \ "and " REG(TMP1) ", " REG(TMP1) ", " REG(B1) "\n" \ "eor " REG(TMP2) ", " REG(TMP2) ", " REG(D2) "\n" \ "eor " REG(TMP1) ", " REG(TMP1) ", " REG(D1) "\n" \ "add " REG(A2) ", " REG(A2) ", " REG(TMP2) "\n" \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ LD2(NEXT_IN1, NEXT_IN2) \ ROR_ADD(A1, B1, A2, B2, R) #define ROUND_H(A1, B1, C1, D1, A2, B2, C2, D2, NEXT_IN1, NEXT_IN2, KL, KH, R) \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ SETI_L("TMP1", KL) \ "add " REG(A2) ", " REG(A2) ", " REG(TMP2) "\n" \ SETI_H("TMP1", KH) \ "eor " REG(TMP2) ", " REG(C2) ", " REG(D2) "\n" \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ "add " REG(A2) ", " REG(A2) ", " REG(TMP1) "\n" \ "eor " REG(TMP1) ", " REG(C1) ", " REG(D1) "\n" \ "eor " REG(TMP2) ", " REG(TMP2) ", " REG(B2) "\n" \ "eor " REG(TMP1) ", " REG(TMP1) ", " REG(B1) "\n" \ "add " REG(A2) ", " REG(A2) ", " REG(TMP2) "\n" \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ LD2(NEXT_IN1, NEXT_IN2) \ ROR_ADD(A1, B1, A2, B2, R) #define ROUND_I(A1, B1, C1, D1, A2, B2, C2, D2, NEXT_IN1, NEXT_IN2, KL, KH, R) \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ SETI_L("TMP1", KL) \ "add " REG(A2) ", " REG(A2) ", " REG(TMP2) "\n" \ SETI_H("TMP1", KH) \ "bic " REG(TMP2) ", " REG(D2) ", " REG(B2) "\n" \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ "add " REG(A2) ", " REG(A2) ", " REG(TMP1) "\n" \ "bic " REG(TMP1) ", " REG(D1) ", " REG(B1) "\n" \ "eor " REG(TMP2) ", " REG(TMP2) ", " REG(C2) "\n" \ "eor " REG(TMP1) ", " REG(TMP1) ", " REG(C1) "\n" \ "sub " REG(A2) ", " REG(A2) ", " REG(TMP2) "\n" \ "sub " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ LD2(NEXT_IN1, NEXT_IN2) \ ROR_ADD(A1, B1, A2, B2, R) #define ROUND_I_LAST(A1, B1, C1, D1, A2, B2, C2, D2, KL, KH, R) \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ SETI_L("TMP1", KL) \ "add " REG(A2) ", " REG(A2) ", " REG(TMP2) "\n" \ SETI_H("TMP1", KH) \ "bic " REG(TMP2) ", " REG(D2) ", " REG(B2) "\n" \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ "add " REG(A2) ", " REG(A2) ", " REG(TMP1) "\n" \ "bic " REG(TMP1) ", " REG(D1) ", " REG(B1) "\n" \ "eor " REG(TMP2) ", " REG(TMP2) ", " REG(C2) "\n" \ "eor " REG(TMP1) ", " REG(TMP1) ", " REG(C1) "\n" \ "sub " REG(A2) ", " REG(A2) ", " REG(TMP2) "\n" \ "sub " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ ROR_ADD(A1, B1, A2, B2, R) #define ROUND_G(A1, B1, C1, D1, A2, B2, C2, D2, NEXT_IN1, NEXT_IN2, KL, KH, R) \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ SETI_L("TMP1", KL) \ "add " REG(A2) ", " REG(A2) ", " REG(TMP2) "\n" \ SETI_H("TMP1", KH) \ "bic " REG(TMP2) ", " REG(C2) ", " REG(D2) "\n" \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ "add " REG(A2) ", " REG(A2) ", " REG(TMP1) "\n" \ "bic " REG(TMP1) ", " REG(C1) ", " REG(D1) "\n" \ "add " REG(A2) ", " REG(A2) ", " REG(TMP2) "\n" \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ "and " REG(TMP2) ", " REG(B2) ", " REG(D2) "\n" \ "and " REG(TMP1) ", " REG(B1) ", " REG(D1) "\n" \ "add " REG(A2) ", " REG(A2) ", " REG(TMP2) "\n" \ "add " REG(A1) ", " REG(A1) ", " REG(TMP1) "\n" \ LD2(NEXT_IN1, NEXT_IN2) \ ROR_ADD(A1, B1, A2, B2, R) #define RF4(i0, i1, i2, i3, k0l, k0h, k1l, k1h, k2l, k2h, k3l, k3h) \ ROUND_F(A1, B1, C1, D1, A2, B2, C2, D2, "[%[i0], #" STR(i0) "]", "[%[i1], #" STR(i0) "]", k0l, k0h, 25) \ ROUND_F(D1, A1, B1, C1, D2, A2, B2, C2, "[%[i0], #" STR(i1) "]", "[%[i1], #" STR(i1) "]", k1l, k1h, 20) \ ROUND_F(C1, D1, A1, B1, C2, D2, A2, B2, "[%[i0], #" STR(i2) "]", "[%[i1], #" STR(i2) "]", k2l, k2h, 15) \ ROUND_F(B1, C1, D1, A1, B2, C2, D2, A2, "[%[i0], #" STR(i3) "]", "[%[i1], #" STR(i3) "]", k3l, k3h, 10) #define RG4(i0, i1, i2, i3, k0l, k0h, k1l, k1h, k2l, k2h, k3l, k3h) \ ROUND_G(A1, B1, C1, D1, A2, B2, C2, D2, "[%[i0], #" STR(i0) "]", "[%[i1], #" STR(i0) "]", k0l, k0h, 27) \ ROUND_G(D1, A1, B1, C1, D2, A2, B2, C2, "[%[i0], #" STR(i1) "]", "[%[i1], #" STR(i1) "]", k1l, k1h, 23) \ ROUND_G(C1, D1, A1, B1, C2, D2, A2, B2, "[%[i0], #" STR(i2) "]", "[%[i1], #" STR(i2) "]", k2l, k2h, 18) \ ROUND_G(B1, C1, D1, A1, B2, C2, D2, A2, "[%[i0], #" STR(i3) "]", "[%[i1], #" STR(i3) "]", k3l, k3h, 12) #define RH4(i0, i1, i2, i3, k0l, k0h, k1l, k1h, k2l, k2h, k3l, k3h) \ ROUND_H(A1, B1, C1, D1, A2, B2, C2, D2, "[%[i0], #" STR(i0) "]", "[%[i1], #" STR(i0) "]", k0l, k0h, 28) \ ROUND_H(D1, A1, B1, C1, D2, A2, B2, C2, "[%[i0], #" STR(i1) "]", "[%[i1], #" STR(i1) "]", k1l, k1h, 21) \ ROUND_H(C1, D1, A1, B1, C2, D2, A2, B2, "[%[i0], #" STR(i2) "]", "[%[i1], #" STR(i2) "]", k2l, k2h, 16) \ ROUND_H(B1, C1, D1, A1, B2, C2, D2, A2, "[%[i0], #" STR(i3) "]", "[%[i1], #" STR(i3) "]", k3l, k3h, 9) #define RI4(i0, i1, i2, i3, k0l, k0h, k1l, k1h, k2l, k2h, k3l, k3h) \ ROUND_I(A1, B1, C1, D1, A2, B2, C2, D2, "[%[i0], #" STR(i0) "]", "[%[i1], #" STR(i0) "]", k0l, k0h, 26) \ ROUND_I(D1, A1, B1, C1, D2, A2, B2, C2, "[%[i0], #" STR(i1) "]", "[%[i1], #" STR(i1) "]", k1l, k1h, 22) \ ROUND_I(C1, D1, A1, B1, C2, D2, A2, B2, "[%[i0], #" STR(i2) "]", "[%[i1], #" STR(i2) "]", k2l, k2h, 17) \ ROUND_I(B1, C1, D1, A1, B2, C2, D2, A2, "[%[i0], #" STR(i3) "]", "[%[i1], #" STR(i3) "]", k3l, k3h, 11) __asm__( "ldr " REG(TMP1) ", [%[i0]]\n" "ldr " REG(TMP2) ", [%[i1]]\n" #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ "rev " REG(TMP1) ", " REG(TMP1) "\n" "rev " REG(TMP2) ", " REG(TMP2) "\n" #endif RF4( 4, 8, 12, 16, 0xa478, 0xd76a, 0xb756, 0xe8c7, 0x70db, 0x2420, 0xceee, 0xc1bd) RF4(20, 24, 28, 32, 0x0faf, 0xf57c, 0xc62a, 0x4787, 0x4613, 0xa830, 0x9501, 0xfd46) RF4(36, 40, 44, 48, 0x98d8, 0x6980, 0xf7af, 0x8b44, 0x5bb1, 0xffff, 0xd7be, 0x895c) RF4(52, 56, 60, 4, 0x1122, 0x6b90, 0x7193, 0xfd98, 0x438e, 0xa679, 0x0821, 0x49b4) RG4(24, 44, 0, 20, 0x2562, 0xf61e, 0xb340, 0xc040, 0x5a51, 0x265e, 0xc7aa, 0xe9b6) RG4(40, 60, 16, 36, 0x105d, 0xd62f, 0x1453, 0x0244, 0xe681, 0xd8a1, 0xfbc8, 0xe7d3) RG4(56, 12, 32, 52, 0xcde6, 0x21e1, 0x07d6, 0xc337, 0x0d87, 0xf4d5, 0x14ed, 0x455a) RG4( 8, 28, 48, 20, 0xe905, 0xa9e3, 0xa3f8, 0xfcef, 0x02d9, 0x676f, 0x4c8a, 0x8d2a) RH4(32, 44, 56, 4, 0x3942, 0xfffa, 0xf681, 0x8771, 0x6122, 0x6d9d, 0x380c, 0xfde5) RH4(16, 28, 40, 52, 0xea44, 0xa4be, 0xcfa9, 0x4bde, 0x4b60, 0xf6bb, 0xbc70, 0xbebf) RH4( 0, 12, 24, 36, 0x7ec6, 0x289b, 0x27fa, 0xeaa1, 0x3085, 0xd4ef, 0x1d05, 0x0488) RH4(48, 60, 8, 0, 0xd039, 0xd9d4, 0x99e5, 0xe6db, 0x7cf8, 0x1fa2, 0x5665, 0xc4ac) RI4(28, 56, 20, 48, 0x2243, 0xf429, 0xff96, 0x432a, 0x23a6, 0xab94, 0xa038, 0xfc93) RI4(12, 40, 4, 32, 0x59c2, 0x655b, 0xcc91, 0x8f0c, 0xf47c, 0xffef, 0x5dd0, 0x8584) RI4(60, 24, 52, 16, 0x7e4e, 0x6fa8, 0xe6df, 0xfe2c, 0x4313, 0xa301, 0x11a0, 0x4e08) ROUND_I(A1, B1, C1, D1, A2, B2, C2, D2, "[%[i0], #44]", "[%[i1], #44]", 0x7e81, 0xf753, 26) ROUND_I(D1, A1, B1, C1, D2, A2, B2, C2, "[%[i0], #8]", "[%[i1], #8]", 0xf234, 0xbd3a, 22) ROUND_I(C1, D1, A1, B1, C2, D2, A2, B2, "[%[i0], #36]", "[%[i1], #36]", 0xd2ba, 0x2ad7, 17) ROUND_I_LAST(B1, C1, D1, A1, B2, C2, D2, A2, 0xd390, 0xeb86, 11) : [A1]"+&r"(A1), [B1]"+&r"(B1), [C1]"+&r"(C1), [D1]"+&r"(D1), [A2]"+&r"(A2), [B2]"+&r"(B2), [C2]"+&r"(C2), [D2]"+&r"(D2), [TMP1]"=&r"(tmp1), [TMP2]"=&r"(tmp2) : [i0]"r"(data[0]), [i1]"r"(data[1]) : ); state[0] += A1; state[1] += B1; state[2] += C1; state[3] += D1; state[4] += A2; state[5] += B2; state[6] += C2; state[7] += D2; #undef ROUND_F #undef ROUND_G #undef ROUND_H #undef ROUND_I #undef ROUND_I_LAST #undef RF4 #undef RG4 #undef RH4 #undef RI4 #undef SETI_L #undef SETI_H #undef ROR_ADD #undef REG } par2cmdline-turbo-1.4.0/parpar/hasher/md5x2-base.h000066400000000000000000000006661514221355600216400ustar00rootroot00000000000000 #define FNB(f) _FN(f##_x2) #include "md5-base.h" #undef FNB static HEDLEY_ALWAYS_INLINE void _FN(md5_update_block_x2)(void* state, const void* src1, const void* src2) { const uint8_t* const src[] = {(const uint8_t*)src1, (const uint8_t*)src2}; _FN(md5_process_block_x2)((word_t*)state, src, 0); } static HEDLEY_ALWAYS_INLINE void _FN(md5_init_x2)(void* state) { _FN(md5_init_lane_x2)(state, 0); _FN(md5_init_lane_x2)(state, 1); } par2cmdline-turbo-1.4.0/parpar/hasher/md5x2-neon-asm.h000066400000000000000000000216011514221355600224330ustar00rootroot00000000000000#include "../src/platform.h" #ifndef STR # define STR_HELPER(x) #x # define STR(x) STR_HELPER(x) #endif #ifndef UNUSED # define UNUSED(...) (void)(__VA_ARGS__) #endif extern const uint32_t md5_constants_arm[64]; #ifdef __ARM_NEON static HEDLEY_ALWAYS_INLINE void md5_process_block_x2_neon(uint32x2_t* state, const uint8_t* const* HEDLEY_RESTRICT data, size_t offset) { UNUSED(offset); uint32x2_t A = state[0]; uint32x2_t B = state[1]; uint32x2_t C = state[2]; uint32x2_t D = state[3]; const uint32_t* k = md5_constants_arm; #ifdef __aarch64__ #define ROUND_X(A, B, I, R, L) \ "add %[" STR(A) "].2s, %[" STR(A) "].2s, " I ".2s\n" \ "add v20.2s, %[" STR(A) "].2s, v20.2s\n" \ "ushr %[" STR(A) "].2s, v20.2s, #" STR(R) "\n" \ "sli %[" STR(A) "].2s, v20.2s, #" STR(L) "\n" \ "add %[" STR(A) "].2s, %[" STR(A) "].2s, %[" STR(B) "].2s\n" \ #define ROUND_F(A, B, C, D, I, R, L) \ "mov v20.8b, %[" STR(D) "].8b\n" \ "bit v20.8b, %[" STR(C) "].8b, %[" STR(B) "].8b\n" \ ROUND_X(A, B, I, R, L) #define ROUND_G(A, B, C, D, I, R, L) \ "mov v20.8b, %[" STR(D) "].8b\n" \ "bsl v20.8b, %[" STR(B) "].8b, %[" STR(C) "].8b\n" \ ROUND_X(A, B, I, R, L) #define ROUND_H(A, B, C, D, I, R, L) \ "eor v20.8b, %[" STR(C) "].8b, %[" STR(D) "].8b\n" \ "eor v20.8b, v20.8b, %[" STR(B) "].8b\n" \ ROUND_X(A, B, I, R, L) #define ROUND_H16(A, B, C, D, I) \ "eor v20.8b, %[" STR(C) "].8b, %[" STR(D) "].8b\n" \ "eor v20.8b, v20.8b, %[" STR(B) "].8b\n" \ "add %[" STR(A) "].2s, %[" STR(A) "].2s, " I ".2s\n" \ "add %[" STR(A) "].2s, %[" STR(A) "].2s, v20.2s\n" \ "rev32 %[" STR(A) "].4h, %[" STR(A) "].4h\n" \ "add %[" STR(A) "].2s, %[" STR(A) "].2s, %[" STR(B) "].2s\n" #define ROUND_I(A, B, C, D, I, R, L) \ "eor v20.8b, %[" STR(C) "].8b, %[" STR(D) "].8b\n" \ "bit v20.8b, %[" STR(C) "].8b, %[" STR(B) "].8b\n" \ "add %[" STR(A) "].2s, %[" STR(A) "].2s, " I ".2s\n" \ "sub v20.2s, %[" STR(A) "].2s, v20.2s\n" \ "ushr %[" STR(A) "].2s, v20.2s, #" STR(R) "\n" \ "sli %[" STR(A) "].2s, v20.2s, #" STR(L) "\n" \ "add %[" STR(A) "].2s, %[" STR(A) "].2s, %[" STR(B) "].2s\n" \ // TODO: subtract 1 from constant above #define RF4(r1, r2) \ "ld4r {v16.2s, v17.2s, v18.2s, v19.2s}, [%[k]], #16\n" \ "add v16.2s, v16.2s, " r1 ".2s\n" \ ROUND_F(A, B, C, D, "v16", 25, 7) \ "ext v16.16b, " r1 ".16b, " r1 ".16b, #8\n" \ "add v17.2s, v16.2s, v17.2s\n" \ ROUND_F(D, A, B, C, "v17", 20, 12) \ "add v18.2s, v18.2s, " r2 ".2s\n" \ ROUND_F(C, D, A, B, "v18", 15, 17) \ "ext v16.16b, " r2 ".16b, " r2 ".16b, #8\n" \ "add v19.2s, v16.2s, v19.2s\n" \ ROUND_F(B, C, D, A, "v19", 10, 22) #define RG4(rs, r1, r2) \ "ld4r {v16.2s, v17.2s, v18.2s, v19.2s}, [%[k]], #16\n" \ "zip1 v16.2d, v16.2d, v17.2d\n" \ "add v16.4s, v16.4s, " rs ".4s\n" \ "ext v17.16b, v16.16b, v16.16b, #8\n" \ ROUND_G(A, B, C, D, "v17", 27, 5) \ "add v18.2s, v18.2s, " r1 ".2s\n" \ "ext v17.16b, " r2 ".16b, " r2 ".16b, #8\n" \ ROUND_G(D, A, B, C, "v18", 23, 9) \ "add v19.2s, v19.2s, v17.2s\n" \ ROUND_G(C, D, A, B, "v19", 18, 14) \ ROUND_G(B, C, D, A, "v16", 12, 20) #define RH4(r1, r2, r3, r4) \ "ld4r {v16.2s, v17.2s, v18.2s, v19.2s}, [%[k]], #16\n" \ "ext v21.16b, " r1 ".16b, " r1 ".16b, #8\n" \ "ext v22.16b, " r3 ".16b, " r3 ".16b, #8\n" \ "add v16.2s, v16.2s, v21.2s\n" \ "add v17.2s, v17.2s, " r2 ".2s\n" \ "add v18.2s, v18.2s, v22.2s\n" \ "add v19.2s, v19.2s, " r4 ".2s\n" \ ROUND_H(A, B, C, D, "v16", 28, 4) \ ROUND_H(D, A, B, C, "v17", 21, 11) \ ROUND_H16(C, D, A, B, "v18") \ ROUND_H(B, C, D, A, "v19", 9, 23) #define RI4(r1, r2, r3, r4) \ "ld4r {v16.2s, v17.2s, v18.2s, v19.2s}, [%[k]], #16\n" \ "ext v21.16b, " r2 ".16b, " r2 ".16b, #8\n" \ "ext v22.16b, " r4 ".16b, " r4 ".16b, #8\n" \ "add v16.2s, v16.2s, " r1 ".2s\n" \ "add v17.2s, v17.2s, v21.2s\n" \ "add v18.2s, v18.2s, " r3 ".2s\n" \ "add v19.2s, v19.2s, v22.2s\n" \ ROUND_I(A, B, C, D, "v16", 26, 6) \ ROUND_I(D, A, B, C, "v17", 22, 10) \ ROUND_I(C, D, A, B, "v18", 17, 15) \ ROUND_I(B, C, D, A, "v19", 11, 21) __asm__( "ld1 {v20.16b, v21.16b, v22.16b, v23.16b}, [%[i0]]\n" "ld1 {v28.16b, v29.16b, v30.16b, v31.16b}, [%[i1]]\n" "zip1 v24.4s, v20.4s, v28.4s\n" "zip2 v28.4s, v20.4s, v28.4s\n" RF4("v24", "v28") "zip1 v25.4s, v21.4s, v29.4s\n" "zip2 v29.4s, v21.4s, v29.4s\n" RF4("v25", "v29") "zip1 v26.4s, v22.4s, v30.4s\n" "zip2 v30.4s, v22.4s, v30.4s\n" RF4("v26", "v30") "zip1 v27.4s, v23.4s, v31.4s\n" "zip2 v31.4s, v23.4s, v31.4s\n" RF4("v27", "v31") RG4("v24", "v29", "v30") RG4("v25", "v30", "v31") RG4("v26", "v31", "v28") RG4("v27", "v28", "v29") RH4("v25", "v26", "v30", "v31") RH4("v24", "v25", "v29", "v30") RH4("v27", "v24", "v28", "v29") RH4("v26", "v27", "v31", "v28") RI4("v24", "v29", "v31", "v25") RI4("v27", "v28", "v30", "v24") RI4("v26", "v31", "v29", "v27") RI4("v25", "v30", "v28", "v26") : [A]"+&w"(A), [B]"+&w"(B), [C]"+&w"(C), [D]"+&w"(D), [k]"+r"(k) : [i0]"r"(data[0]), [i1]"r"(data[1]) : "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31" ); #else #define ROUND_X(A, B, I, R, L) \ "vadd.i32 %P[" STR(A) "], %P[" STR(A) "], " I "\n" \ "vadd.i32 d11, %P[" STR(A) "], d11\n" \ "vshr.u32 %P[" STR(A) "], d11, #" STR(R) "\n" \ "vsli.32 %P[" STR(A) "], d11, #" STR(L) "\n" \ "vadd.i32 %P[" STR(A) "], %P[" STR(A) "], %P[" STR(B) "]\n" \ #define ROUND_F(A, B, C, D, I, R, L) \ "vorr d11, %P[" STR(D) "], %P[" STR(D) "]\n" \ "vbit d11, %P[" STR(C) "], %P[" STR(B) "]\n" \ ROUND_X(A, B, I, R, L) #define ROUND_G(A, B, C, D, I, R, L) \ "vorr d11, %P[" STR(D) "], %P[" STR(D) "]\n" \ "vbsl d11, %P[" STR(B) "], %P[" STR(C) "]\n" \ ROUND_X(A, B, I, R, L) #define ROUND_H(A, B, C, D, I, R, L) \ "veor d11, %P[" STR(C) "], %P[" STR(D) "]\n" \ "veor d11, d11, %P[" STR(B) "]\n" \ ROUND_X(A, B, I, R, L) #define ROUND_H16(A, B, C, D, I) \ "veor d11, %P[" STR(C) "], %P[" STR(D) "]\n" \ "veor d11, d11, %P[" STR(B) "]\n" \ "vadd.i32 %P[" STR(A) "], %P[" STR(A) "], " I "\n" \ "vadd.i32 %P[" STR(A) "], %P[" STR(A) "], d11\n" \ "vrev32.i16 %P[" STR(A) "], %P[" STR(A) "]\n" \ "vadd.i32 %P[" STR(A) "], %P[" STR(A) "], %P[" STR(B) "]\n" #define ROUND_I(A, B, C, D, I, R, L) \ "veor d11, %P[" STR(C) "], %P[" STR(D) "]\n" \ "vbit d11, %P[" STR(C) "], %P[" STR(B) "]\n" \ "vadd.i32 %P[" STR(A) "], %P[" STR(A) "], " I "\n" \ "vsub.i32 d11, %P[" STR(A) "], d11\n" \ "vshr.u32 %P[" STR(A) "], d11, #" STR(R) "\n" \ "vsli.32 %P[" STR(A) "], d11, #" STR(L) "\n" \ "vadd.i32 %P[" STR(A) "], %P[" STR(A) "], %P[" STR(B) "]\n" \ #define RF4(r1, r2) \ "vld4.32 {d12[],d13[],d14[],d15[]}, [%[k] :128]!\n" \ "vzip.32 " r1 ", " r2 "\n" \ "vadd.i32 q6, q6, " r1 "\n" \ "vadd.i32 q7, q7, " r2 "\n" \ ROUND_F(A, B, C, D, "d12", 25, 7) \ ROUND_F(D, A, B, C, "d13", 20, 12) \ ROUND_F(C, D, A, B, "d14", 15, 17) \ ROUND_F(B, C, D, A, "d15", 10, 22) #define RG4(rs, r1, r2) \ "vld4.32 {d12[],d13[],d14[],d15[]}, [%[k] :128]!\n" \ "vadd.i32 q6, q6, " rs "\n" \ "vadd.i32 d14, d14, " r1 " \n" \ "vadd.i32 d15, d15, " r2 " \n" \ ROUND_G(A, B, C, D, "d13", 27, 5) \ ROUND_G(D, A, B, C, "d14", 23, 9) \ ROUND_G(C, D, A, B, "d15", 18, 14) \ ROUND_G(B, C, D, A, "d12", 12, 20) #define RH4(r1, r2, r3, r4) \ "vld4.32 {d12[],d13[],d14[],d15[]}, [%[k] :128]!\n" \ "vadd.i32 d12, d12, " r1 " \n" \ "vadd.i32 d13, d13, " r2 " \n" \ "vadd.i32 d14, d14, " r3 " \n" \ "vadd.i32 d15, d15, " r4 " \n" \ ROUND_H(A, B, C, D, "d12", 28, 4) \ ROUND_H(D, A, B, C, "d13", 21, 11) \ ROUND_H16(C, D, A, B, "d14") \ ROUND_H(B, C, D, A, "d15", 9, 23) #define RI4(r1, r2, r3, r4) \ "vld4.32 {d12[],d13[],d14[],d15[]}, [%[k] :128]!\n" \ "vadd.i32 d12, d12, " r1 " \n" \ "vadd.i32 d13, d13, " r2 " \n" \ "vadd.i32 d14, d14, " r3 " \n" \ "vadd.i32 d15, d15, " r4 " \n" \ ROUND_I(A, B, C, D, "d12", 26, 6) \ ROUND_I(D, A, B, C, "d13", 22, 10) \ ROUND_I(C, D, A, B, "d14", 17, 15) \ ROUND_I(B, C, D, A, "d15", 11, 21) __asm__( "vld1.8 {d16-d19}, [%[i0]]\n" "add r4, %[i0], #32\n" "vld1.8 {d24-d27}, [%[i1]]\n" "add r5, %[i1], #32\n" "vld1.8 {d20-d23}, [r4]\n" "vld1.8 {d28-d31}, [r5]\n" RF4("q8", "q12") RF4("q9", "q13") RF4("q10", "q14") RF4("q11", "q15") RG4("q8", "d26", "d29") RG4("q9", "d28", "d31") RG4("q10", "d30", "d25") RG4("q11", "d24", "d27") RH4("d19", "d20", "d29", "d30") RH4("d17", "d18", "d27", "d28") RH4("d23", "d16", "d25", "d26") RH4("d21", "d22", "d31", "d24") RI4("d16", "d27", "d30", "d19") RI4("d22", "d25", "d28", "d17") RI4("d20", "d31", "d26", "d23") RI4("d18", "d29", "d24", "d21") : [A]"+&w"(A), [B]"+&w"(B), [C]"+&w"(C), [D]"+&w"(D), [k]"+r"(k) : [i0]"r"(data[0]), [i1]"r"(data[1]) : "d11", "q6", "q7", "q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15", "r4", "r5" ); #endif state[0] = vadd_u32(A, state[0]); state[1] = vadd_u32(B, state[1]); state[2] = vadd_u32(C, state[2]); state[3] = vadd_u32(D, state[3]); #undef ROUND_X #undef ROUND_F #undef ROUND_G #undef ROUND_H #undef ROUND_I #undef RF4 #undef RG4 #undef RH4 #undef RI4 } #endif par2cmdline-turbo-1.4.0/parpar/hasher/md5x2-neon.h000066400000000000000000000054361514221355600216650ustar00rootroot00000000000000 #include #if defined(__GNUC__) || defined(__clang__) # define MD5_USE_ASM # include "md5x2-neon-asm.h" #endif #ifdef __ARM_NEON static HEDLEY_ALWAYS_INLINE uint32x2_t vmake_u32le( uint32_t a, uint32_t b ) { # if defined(_MSC_VER) || __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ uint32_t t[] = {a,b}; return vreinterpret_u32_u8(vld1_u8((const uint8_t*)t)); # else return (uint32x2_t){a,b}; # endif } #define ADD vadd_u32 #define VAL vdup_n_u32 #define word_t uint32x2_t #define INPUT(k, set, ptr, offs, idx, var) ADD(var, VAL(k)) #define LOAD(k, set, ptr, offs, idx, var) ADD(var = vmake_u32le(((uint32_t*)(ptr[0]))[idx], ((uint32_t*)(ptr[1]))[idx]), VAL(k)) #define LOAD4(set, ptr, offs, idx, var0, var1, var2, var3) { \ uint32x4_t in0 = vreinterpretq_u32_u8(vld1q_u8((uint8_t*)ptr[0] + idx*4)); \ uint32x4_t in1 = vreinterpretq_u32_u8(vld1q_u8((uint8_t*)ptr[1] + idx*4)); \ uint32x4x2_t tmp = vzipq_u32(in0, in1); \ var0 = vget_low_u32(tmp.val[0]); \ var1 = vget_high_u32(tmp.val[0]); \ var2 = vget_low_u32(tmp.val[1]); \ var3 = vget_high_u32(tmp.val[1]); \ } #define ROTATE(a, r) r==16 ? vreinterpret_u32_u16(vrev32_u16(vreinterpret_u16_u32(a))) : vsli_n_u32(vshr_n_u32(a, 32-r), a, r) // this runs slower on Cortex A53/55/76 for ARMv7, marginally faster on A53/55 for AArch64, marginally slower on A76 for AArch64 /* #define _RX(f,a,b,c,d,ik,r) \ a = vadd_u32(a, ik); \ a = _ADDF(f, a, b, c, d); \ if(r == 16) \ a = vadd_u32(b, vreinterpret_u32_u16(vrev32_u16(vreinterpret_u16_u32(a)))); \ else \ a = vadd_u32( \ vsra_n_u32(b, a, 32-r), \ vshl_n_u32(a, r) \ ) */ #define md5_init_lane_x2_neon(state, idx) { \ uint32x2_t* state_ = (uint32x2_t*)state; \ state_[0] = vset_lane_u32(0x67452301L, state_[0], idx); \ state_[1] = vset_lane_u32(0xefcdab89L, state_[1], idx); \ state_[2] = vset_lane_u32(0x98badcfeL, state_[2], idx); \ state_[3] = vset_lane_u32(0x10325476L, state_[3], idx); \ } #define _FN(f) f##_neon #define F 1 #define G 2 #define H 3 #define I 4 #define ADDF(f,a,b,c,d) ( \ f==G ? ADD(a, vbsl_u32(d, b, c)) : ( \ f==I ? vsub_u32(a, vbsl_u32(b, c, veor_u32(c, d))) : ADD(a, f==F ? \ vbsl_u32(b, c, d) : \ veor_u32(veor_u32(d, c), b) \ ) \ ) \ ) #define IOFFSET -1 #include "md5x2-base.h" #undef ROTATE #undef _FN #undef ADD #undef VAL #undef word_t #undef INPUT #undef LOAD #undef LOAD4 #undef F #undef G #undef H #undef I #undef ADDF #undef IOFFSET static HEDLEY_ALWAYS_INLINE void md5_extract_x2_neon(void* dst, void* state, const int idx) { uint32x2_t* state_ = (uint32x2_t*)state; // re-arrange into two hashes uint32x2x2_t tmp1 = vzip_u32(state_[0], state_[1]); uint32x2x2_t tmp2 = vzip_u32(state_[2], state_[3]); vst1_u32((uint32_t*)dst, tmp1.val[idx]); vst1_u32((uint32_t*)dst + 2, tmp2.val[idx]); } #endif #ifdef MD5_USE_ASM # undef MD5_USE_ASM #endif par2cmdline-turbo-1.4.0/parpar/hasher/md5x2-scalar.h000066400000000000000000000026341514221355600221700ustar00rootroot00000000000000#include // memcpy+memset #include "../src/platform.h" #include "../src/stdint.h" #if (defined(__GNUC__) || defined(__clang__)) && defined(PLATFORM_AMD64) && defined(__OPTIMIZE__) # define MD5_USE_ASM # include "md5x2-x86-asm.h" #endif #if (defined(__GNUC__) || defined(__clang__)) && defined(PLATFORM_ARM) && defined(__OPTIMIZE__) \ && (defined(__aarch64__) || (__BYTE_ORDER__ != __ORDER_BIG_ENDIAN__) || (defined(__ARM_ARCH) && __ARM_ARCH >= 6) || defined(__armv7__) || defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_8A__) || defined(_M_ARM)) // disable ASM on ARMv5 or older if big-endian # define MD5_USE_ASM # include "md5x2-arm-asm.h" #endif static HEDLEY_ALWAYS_INLINE void md5_init_lane_x2_scalar(void* state, const int idx) { uint32_t* state_ = (uint32_t*)state; state_[0 + idx*4] = 0x67452301L; state_[1 + idx*4] = 0xefcdab89L; state_[2 + idx*4] = 0x98badcfeL; state_[3 + idx*4] = 0x10325476L; } #include "md5-scalar-base.h" #define _FN(f) f##_scalar #define MD5X2 #include "md5x2-base.h" #ifdef MD5X2 # undef MD5X2 #endif #undef _FN #undef ROTATE #undef ADD #undef VAL #undef word_t #undef INPUT #undef LOAD #undef F #undef G #undef H #undef I #undef ADDF #ifdef MD5_USE_ASM # undef MD5_USE_ASM #endif static HEDLEY_ALWAYS_INLINE void md5_extract_x2_scalar(void* dst, void* state, const int idx) { memcpy(dst, (uint32_t*)state + idx*4, 16); } par2cmdline-turbo-1.4.0/parpar/hasher/md5x2-sse-asm.h000066400000000000000000000364011514221355600222720ustar00rootroot00000000000000#include "../src/platform.h" #include "../src/stdint.h" #ifndef STR # define STR_HELPER(x) #x # define STR(x) STR_HELPER(x) #endif #ifndef UNUSED # define UNUSED(...) (void)(__VA_ARGS__) #endif extern const uint32_t md5_constants_sse[160]; #ifdef PLATFORM_AMD64 #define ASM_PARAMS_F(n, c0, c1) \ [A]"+&x"(A), [B]"+&x"(B), [C]"+&x"(C), [D]"+&x"(D), [TMPI1]"=&x"(tmpI1), [TMPI2]"=&x"(tmpI2), [TMPF1]"=&x"(tmpF1), [TMPF2]"=&x"(tmpF2), \ [cache0]"=&x"(cache##c0), [cache1]"=&x"(cache##c1) \ : \ [k0]"m"(md5_constants_sse[n*8]), [k1]"m"(md5_constants_sse[n*8+4]), [i0]"m"(_in[0][n]), [i1]"m"(_in[1][n]) : #define ASM_PARAMS(n) \ [A]"+&x"(A), [B]"+&x"(B), [C]"+&x"(C), [D]"+&x"(D), [TMPI1]"=&x"(tmpI1), [TMPI2]"=&x"(tmpI2), [TMPF1]"=&x"(tmpF1), [TMPF2]"=&x"(tmpF2) \ : [input0]"x"(cache0), [input1]"x"(cache1), [input2]"x"(cache2), [input3]"x"(cache3), [input4]"x"(cache4), [input5]"x"(cache5), [input6]"x"(cache6), [input7]"x"(cache7), \ [k0_0]"m"(md5_constants_sse[n+0]), [k1_0]"m"(md5_constants_sse[n+4]), [k0_1]"m"(md5_constants_sse[n+8]), [k1_1]"m"(md5_constants_sse[n+12]), [k0_2]"m"(md5_constants_sse[n+16]), [k1_2]"m"(md5_constants_sse[n+20]), [k0_3]"m"(md5_constants_sse[n+24]) , [k1_3]"m"(md5_constants_sse[n+28]) : #define FN_VARS \ UNUSED(offset); \ __m128i A = state[0]; \ __m128i B = state[1]; \ __m128i C = state[2]; \ __m128i D = state[3]; \ __m128i tmpI1, tmpI2, tmpF1, tmpF2; \ const __m128i* const* HEDLEY_RESTRICT _in = (const __m128i* const* HEDLEY_RESTRICT)data; \ __m128i cache0, cache1, cache2, cache3, cache4, cache5, cache6, cache7 #else #define ASM_PARAMS_F(n, c0, c1) \ [A]"+&x"(A), [B]"+&x"(B), [C]"+&x"(C), [D]"+&x"(D), [TMPI1]"=&x"(tmpI1), [TMPI2]"=&x"(tmpI2), [TMPF1]"=&x"(tmpF1), [TMPF2]"=&x"(tmpF2) \ : \ [k0]"m"(md5_constants_sse[n*8]), [k1]"m"(md5_constants_sse[n*8+4]), [i0]"m"(_in[0][n]), [i1]"m"(_in[1][n]), [scratch0]"m"(scratch[c0*4]), [scratch1]"m"(scratch[c1*4]) : #define ASM_PARAMS(n) \ [A]"+&x"(A), [B]"+&x"(B), [C]"+&x"(C), [D]"+&x"(D), [TMPI1]"=&x"(tmpI1), [TMPI2]"=&x"(tmpI2), [TMPF1]"=&x"(tmpF1), [TMPF2]"=&x"(tmpF2) \ : \ [k0_0]"m"(md5_constants_sse[n+0]), [k1_0]"m"(md5_constants_sse[n+4]), [k0_1]"m"(md5_constants_sse[n+8]), [k1_1]"m"(md5_constants_sse[n+12]), [k0_2]"m"(md5_constants_sse[n+16]), [k1_2]"m"(md5_constants_sse[n+20]), [k0_3]"m"(md5_constants_sse[n+24]) , [k1_3]"m"(md5_constants_sse[n+28]), \ [input0]"m"(scratch[0]), [input1]"m"(scratch[4]), [input2]"m"(scratch[8]), [input3]"m"(scratch[12]), [input4]"m"(scratch[16]), [input5]"m"(scratch[20]), [input6]"m"(scratch[24]), [input7]"m"(scratch[28]) : #define FN_VARS \ UNUSED(offset); \ __m128i A = state[0]; \ __m128i B = state[1]; \ __m128i C = state[2]; \ __m128i D = state[3]; \ __m128i tmpI1, tmpI2, tmpF1, tmpF2; \ const __m128i* const* HEDLEY_RESTRICT _in = (const __m128i* const* HEDLEY_RESTRICT)data; \ ALIGN_TO(16, uint32_t scratch[32]) #endif #ifdef __SSE2__ static HEDLEY_ALWAYS_INLINE void md5_process_block_x2_sse(__m128i* state, const uint8_t* const* HEDLEY_RESTRICT data, size_t offset) { FN_VARS; #define ROUND_X(A, B, I, R) \ "paddd " I ", %[" STR(A) "]\n" \ "paddd %[TMPF1], %[" STR(A) "]\n" \ "pshufd $0b10100000, %[" STR(A) "], %[" STR(A) "]\n" \ "psrlq $" STR(R) ", %[" STR(A) "]\n" \ "paddd %[" STR(B) "], %[" STR(A) "]\n" #ifdef PLATFORM_AMD64 #define READ4 \ "movdqu %[i0], %[cache0]\n" \ "movdqu %[i1], %[TMPI2]\n" \ "movdqa %[k0], %[TMPI1]\n" \ "movdqa %[cache0], %[cache1]\n" \ "punpcklqdq %[TMPI2], %[cache0]\n" \ "punpckhqdq %[TMPI2], %[cache1]\n" \ "movdqa %[k1], %[TMPI2]\n" \ "paddd %[cache0], %[TMPI1]\n" \ "paddd %[cache1], %[TMPI2]\n" #else #define READ4 \ "movdqu %[i0], %[TMPI1]\n" \ "movdqu %[i1], %[TMPF2]\n" \ "movdqa %[TMPI1], %[TMPI2]\n" \ "punpcklqdq %[TMPF2], %[TMPI1]\n" \ "punpckhqdq %[TMPF2], %[TMPI2]\n" \ "movaps %[TMPI1], %[scratch0]\n" \ "paddd %[k0], %[TMPI1]\n" \ "movaps %[TMPI2], %[scratch1]\n" \ "paddd %[k1], %[TMPI2]\n" #endif #define ROUND_F(A, B, C, D, I, R) \ "movdqa %[" STR(D) "], %[TMPF1]\n" \ "pxor %[" STR(C) "], %[TMPF1]\n" \ "pand %[" STR(B) "], %[TMPF1]\n" \ "pxor %[" STR(D) "], %[TMPF1]\n" \ ROUND_X(A, B, I, R) #define ROUND_H_FIRST(A, B, C, D, I, R) \ "movdqa %[" STR(D) "], %[TMPF1]\n" \ "pxor %[" STR(C) "], %[TMPF1]\n" \ "pxor %[" STR(B) "], %[TMPF1]\n" \ ROUND_X(A, B, I, R) #define ROUND_H(A, B, C, D, I, R) \ "pxor %[" STR(A) "], %[TMPF1]\n" \ "pxor %[" STR(B) "], %[TMPF1]\n" \ ROUND_X(A, B, I, R) #define ROUND_I(A, B, C, D, I, R) \ "movdqa %[" STR(D) "], %[TMPF1]\n" \ "pxor %[TMPF2], %[TMPF1]\n" \ "por %[" STR(B) "], %[TMPF1]\n" \ "pxor %[" STR(C) "], %[TMPF1]\n" \ ROUND_X(A, B, I, R) #define ROUND_G(A, B, C, D, I, R) \ "movdqa %[" STR(D) "], %[TMPF1]\n" \ "paddd " I ", %[" STR(A) "]\n" \ "pandn %[" STR(C) "], %[TMPF1]\n" \ "movdqa %[" STR(D) "], %[TMPF2]\n" \ "paddd %[TMPF1], %[" STR(A) "]\n" \ "pand %[" STR(B) "], %[TMPF2]\n" \ "paddd %[TMPF2], %[" STR(A) "]\n" \ "pshufd $0b10100000, %[" STR(A) "], %[" STR(A) "]\n" \ "psrlq $" STR(R) ", %[" STR(A) "]\n" \ "paddd %[" STR(B) "], %[" STR(A) "]\n" #define RF4(offs, r1, r2) __asm__( \ READ4 \ ROUND_F(A, B, C, D, "%[TMPI1]", 25) \ "psrlq $32, %[TMPI1]\n" \ ROUND_F(D, A, B, C, "%[TMPI1]", 20) \ ROUND_F(C, D, A, B, "%[TMPI2]", 15) \ "psrlq $32, %[TMPI2]\n" \ ROUND_F(B, C, D, A, "%[TMPI2]", 10) \ : ASM_PARAMS_F(offs, r1, r2)); #define RG4(offs, rs, r1, r2) \ "movaps %[input" STR(r1) "], %[TMPI2]\n" \ "movdqa %[k0_" STR(offs) "], %[TMPI1]\n" \ "shufps $0b11011000, %[input" STR(r2) "], %[TMPI2]\n" \ "paddd %[input" STR(rs) "], %[TMPI1]\n" \ "shufps $0b11011000, %[TMPI2], %[TMPI2]\n" \ "pshufd $0b10110001, %[TMPI1], %[TMPF2]\n" \ "paddd %[k1_" STR(offs) "], %[TMPI2]\n" \ \ ROUND_G(A, B, C, D, "%[TMPF2]", 27) \ ROUND_G(D, A, B, C, "%[TMPI2]", 23) \ "psrlq $32, %[TMPI2]\n" \ ROUND_G(C, D, A, B, "%[TMPI2]", 18) \ ROUND_G(B, C, D, A, "%[TMPI1]", 12) #define RH4(offs, ff, r1, r2, r3, r4) \ "movaps %[input" STR(r1) "], %[TMPI1]\n" \ "shufps $0b10001101, %[input" STR(r2) "], %[TMPI1]\n" \ "movaps %[input" STR(r3) "], %[TMPI2]\n" \ "shufps $0b01110010, %[TMPI1], %[TMPI1]\n" \ "shufps $0b10001101, %[input" STR(r4) "], %[TMPI2]\n" \ "shufps $0b01110010, %[TMPI2], %[TMPI2]\n" \ "paddd %[k0_" STR(offs) "], %[TMPI1]\n" \ "paddd %[k1_" STR(offs) "], %[TMPI2]\n" \ \ "pshufd $0b11110101, %[TMPI1], %[TMPF2]\n" \ ff(A, B, C, D, "%[TMPF2]", 28) \ ROUND_H(D, A, B, C, "%[TMPI1]", 21) \ "pshufd $0b11110101, %[TMPI2], %[TMPF2]\n" \ ROUND_H(C, D, A, B, "%[TMPF2]", 16) \ ROUND_H(B, C, D, A, "%[TMPI2]", 9) #define RI4(offs, r1, r2, r3, r4) \ "movaps %[input" STR(r1) "], %[TMPI1]\n" \ "movaps %[input" STR(r3) "], %[TMPI2]\n" \ "shufps $0b11011000, %[input" STR(r2) "], %[TMPI1]\n" \ "shufps $0b11011000, %[input" STR(r4) "], %[TMPI2]\n" \ "shufps $0b11011000, %[TMPI1], %[TMPI1]\n" \ "shufps $0b11011000, %[TMPI2], %[TMPI2]\n" \ "paddd %[k0_" STR(offs) "], %[TMPI1]\n" \ "paddd %[k1_" STR(offs) "], %[TMPI2]\n" \ \ ROUND_I(A, B, C, D, "%[TMPI1]", 26) \ "psrlq $32, %[TMPI1]\n" \ ROUND_I(D, A, B, C, "%[TMPI1]", 22) \ ROUND_I(C, D, A, B, "%[TMPI2]", 17) \ "psrlq $32, %[TMPI2]\n" \ ROUND_I(B, C, D, A, "%[TMPI2]", 11) RF4(0, 0, 1) RF4(1, 2, 3) RF4(2, 4, 5) RF4(3, 6, 7) __asm__( RG4(0, 0, 3, 5) RG4(1, 2, 5, 7) RG4(2, 4, 7, 1) RG4(3, 6, 1, 3) : ASM_PARAMS(32)); __asm__( RH4(0, ROUND_H_FIRST, 2, 4, 5, 7) RH4(1, ROUND_H, 0, 2, 3, 5) RH4(2, ROUND_H, 6, 0, 1, 3) RH4(3, ROUND_H, 4, 6, 7, 1) : ASM_PARAMS(64)); __asm__( "pcmpeqb %[TMPF2], %[TMPF2]\n" RI4(0, 0, 3, 7, 2) RI4(1, 6, 1, 5, 0) RI4(2, 4, 7, 3, 6) RI4(3, 2, 5, 1, 4) : ASM_PARAMS(96)); state[0] = _mm_add_epi32(A, state[0]); state[1] = _mm_add_epi32(B, state[1]); state[2] = _mm_add_epi32(C, state[2]); state[3] = _mm_add_epi32(D, state[3]); #undef ROUND_X #undef READ4 #undef ROUND_F #undef ROUND_G #undef ROUND_H #undef ROUND_H_FIRST #undef ROUND_I #undef RF4 #undef RG4 #undef RH4 #undef RI4 } #endif #ifdef __AVX__ static HEDLEY_ALWAYS_INLINE void md5_process_block_x2_avx(__m128i* state, const uint8_t* const* HEDLEY_RESTRICT data, size_t offset) { FN_VARS; // TODO: consider vpshufb for rotate by 16 #define ROUND_X(IA, A, B, I, R) \ "vpaddd " I ", %[" STR(IA) "], %[" STR(A) "]\n" \ "vpaddd %[TMPF1], %[" STR(A) "], %[" STR(A) "]\n" \ "vpshufd $0b10100000, %[" STR(A) "], %[" STR(A) "]\n" \ "vpsrlq $" STR(R) ", %[" STR(A) "], %[" STR(A) "]\n" \ "vpaddd %[" STR(B) "], %[" STR(A) "], %[" STR(A) "]\n" #ifdef PLATFORM_AMD64 #define READ4 \ "vmovdqu %[i0], %[cache0]\n" \ "vmovdqu %[i1], %[TMPI1]\n" \ "vpunpckhqdq %[TMPI1], %[cache0], %[cache1]\n" \ "vpunpcklqdq %[TMPI1], %[cache0], %[cache0]\n" \ "vpaddd %[k0], %[cache0], %[TMPI1]\n" \ "vpaddd %[k1], %[cache1], %[TMPI2]\n" #else #define READ4 \ "vmovdqu %[i0], %[TMPI1]\n" \ "vmovdqu %[i1], %[TMPF2]\n" \ "vpunpckhqdq %[TMPF2], %[TMPI1], %[TMPI2]\n" \ "vpunpcklqdq %[TMPF2], %[TMPI1], %[TMPI1]\n" \ "vmovdqa %[TMPI1], %[scratch0]\n" \ "vmovdqa %[TMPI2], %[scratch1]\n" \ "vpaddd %[k0], %[TMPI1], %[TMPI1]\n" \ "vpaddd %[k1], %[TMPI2], %[TMPI2]\n" #endif #define ROUND_F(A, B, C, D, I, R) \ "vpxor %[" STR(D) "], %[" STR(C) "], %[TMPF1]\n" \ "vpand %[" STR(B) "], %[TMPF1], %[TMPF1]\n" \ "vpxor %[" STR(D) "], %[TMPF1], %[TMPF1]\n" \ ROUND_X(A, A, B, I, R) #define ROUND_H(A, B, C, D, I, R) \ "vpxor %[" STR(D) "], %[" STR(C) "], %[TMPF1]\n" \ "vpxor %[" STR(B) "], %[TMPF1], %[TMPF1]\n" \ ROUND_X(A, A, B, I, R) #define ROUND_I(A, B, C, D, I, R) \ "vpandn %[" STR(D) "], %[" STR(B) "], %[TMPF1]\n" \ "vpaddd " I ", %[" STR(A) "], %[" STR(A) "]\n" \ "vpxor %[" STR(C) "], %[TMPF1], %[TMPF1]\n" \ "vpsubd %[TMPF1], %[" STR(A) "], %[" STR(A) "]\n" \ "vpshufd $0b10100000, %[" STR(A) "], %[" STR(A) "]\n" \ "vpsrlq $" STR(R) ", %[" STR(A) "], %[" STR(A) "]\n" \ "vpaddd %[" STR(B) "], %[" STR(A) "], %[" STR(A) "]\n" #define ROUND_G(A, B, C, D, I, R) \ "vpaddd " I ", %[" STR(A) "], %[" STR(A) "]\n" \ "vpandn %[" STR(C) "], %[" STR(D) "], %[TMPF1]\n" \ "vpaddd %[TMPF1], %[" STR(A) "], %[" STR(A) "]\n" \ "vpand %[" STR(B) "], %[" STR(D) "], %[TMPF1]\n" \ "vpaddd %[TMPF1], %[" STR(A) "], %[" STR(A) "]\n" \ "vpshufd $0b10100000, %[" STR(A) "], %[" STR(A) "]\n" \ "vpsrlq $" STR(R) ", %[" STR(A) "], %[" STR(A) "]\n" \ "vpaddd %[" STR(B) "], %[" STR(A) "], %[" STR(A) "]\n" #define RF4(offs, r1, r2) __asm__( \ READ4 \ ROUND_F(A, B, C, D, "%[TMPI1]", 25) \ "vpsrlq $32, %[TMPI1], %[TMPI1]\n" \ ROUND_F(D, A, B, C, "%[TMPI1]", 20) \ ROUND_F(C, D, A, B, "%[TMPI2]", 15) \ "vpsrlq $32, %[TMPI2], %[TMPI2]\n" \ ROUND_F(B, C, D, A, "%[TMPI2]", 10) \ : ASM_PARAMS_F(offs, r1, r2)); // BLENDPS is faster than PBLENDW on Haswell and later, no difference elsewhere #ifdef PLATFORM_AMD64 #define BLENDD(r1, r2, target) \ "vblendps $0b1010, %[input" STR(r2) "], %[input" STR(r1) "], " target "\n" #define G_ADDS(offs, r) "vpaddd %[k0_" STR(offs) "], %[input" STR(r) "], %[TMPI1]\n" #else #define BLENDD(r1, r2, target) \ "vmovdqa %[input" STR(r1) "], " target "\n" \ "vblendps $0b1010, %[input" STR(r2) "], " target ", " target "\n" #define G_ADDS(offs, r) \ "vmovdqa %[input" STR(r) "], %[TMPI1]\n" \ "vpaddd %[k0_" STR(offs) "], %[TMPI1], %[TMPI1]\n" #endif #define RG4(offs, rs, r1, r2) \ BLENDD(r1, r2, "%[TMPI2]") \ G_ADDS(offs, rs) \ "vpaddd %[k1_" STR(offs) "], %[TMPI2], %[TMPI2]\n" \ "vpsrlq $32, %[TMPI1], %[TMPF2]\n" \ \ ROUND_G(A, B, C, D, "%[TMPF2]", 27) \ ROUND_G(D, A, B, C, "%[TMPI2]", 23) \ "vpsrlq $32, %[TMPI2], %[TMPI2]\n" \ ROUND_G(C, D, A, B, "%[TMPI2]", 18) \ ROUND_G(B, C, D, A, "%[TMPI1]", 12) #define RH4(offs, ff, r1, r2, r3, r4) \ BLENDD(r2, r1, "%[TMPI1]") \ BLENDD(r4, r3, "%[TMPI2]") \ "vpaddd %[k0_" STR(offs) "], %[TMPI1], %[TMPI1]\n" \ "vpaddd %[k1_" STR(offs) "], %[TMPI2], %[TMPI2]\n" \ \ "vpsrlq $32, %[TMPI1], %[TMPF2]\n" \ ff(A, B, C, D, "%[TMPF2]", 28) \ ROUND_H(D, A, B, C, "%[TMPI1]", 21) \ "vpsrlq $32, %[TMPI2], %[TMPF2]\n" \ ROUND_H(C, D, A, B, "%[TMPF2]", 16) \ ROUND_H(B, C, D, A, "%[TMPI2]", 9) #define RI4(offs, r1, r2, r3, r4) \ BLENDD(r1, r2, "%[TMPI1]") \ BLENDD(r3, r4, "%[TMPI2]") \ "vpaddd %[k0_" STR(offs) "], %[TMPI1], %[TMPI1]\n" \ "vpaddd %[k1_" STR(offs) "], %[TMPI2], %[TMPI2]\n" \ \ ROUND_I(A, B, C, D, "%[TMPI1]", 26) \ "vpsrlq $32, %[TMPI1], %[TMPI1]\n" \ ROUND_I(D, A, B, C, "%[TMPI1]", 22) \ ROUND_I(C, D, A, B, "%[TMPI2]", 17) \ "vpsrlq $32, %[TMPI2], %[TMPI2]\n" \ ROUND_I(B, C, D, A, "%[TMPI2]", 11) RF4(0, 0, 1) RF4(1, 2, 3) RF4(2, 4, 5) RF4(3, 6, 7) __asm__( RG4(0, 0, 3, 5) RG4(1, 2, 5, 7) RG4(2, 4, 7, 1) RG4(3, 6, 1, 3) : ASM_PARAMS(32)); __asm__( RH4(0, ROUND_H, 2, 4, 5, 7) RH4(1, ROUND_H, 0, 2, 3, 5) RH4(2, ROUND_H, 6, 0, 1, 3) RH4(3, ROUND_H, 4, 6, 7, 1) : ASM_PARAMS(64)); __asm__( RI4(0, 0, 3, 7, 2) RI4(1, 6, 1, 5, 0) RI4(2, 4, 7, 3, 6) RI4(3, 2, 5, 1, 4) : ASM_PARAMS(128)); // use I-1 instead of I constants state[0] = _mm_add_epi32(A, state[0]); state[1] = _mm_add_epi32(B, state[1]); state[2] = _mm_add_epi32(C, state[2]); state[3] = _mm_add_epi32(D, state[3]); #undef ROUND_X #undef ROUND_F #undef ROUND_G #undef ROUND_H #undef ROUND_I #undef BLENDD } #endif #ifdef __AVX512VL__ static HEDLEY_ALWAYS_INLINE void md5_process_block_x2_avx512(__m128i* state, const uint8_t* const* HEDLEY_RESTRICT data, size_t offset) { FN_VARS; #define ROUND_X(IA, A, B, I, R) \ "vpaddd " I ", %[" STR(IA) "], %[" STR(A) "]\n" \ "vpaddd %[TMPF1], %[" STR(A) "], %[" STR(A) "]\n" \ "vprord $" STR(R) ", %[" STR(A) "], %[" STR(A) "]\n" \ "vpaddd %[" STR(B) "], %[" STR(A) "], %[" STR(A) "]\n" #define ROUND_F(A, B, C, D, I, R) \ "vmovdqa %[" STR(D) "], %[TMPF1]\n" \ "vpternlogd $0xD8, %[" STR(B) "], %[" STR(C) "], %[TMPF1]\n" \ ROUND_X(A, A, B, I, R) #define ROUND_G(A, B, C, D, I, R) \ "vmovdqa %[" STR(D) "], %[TMPF1]\n" \ "vpternlogd $0xAC, %[" STR(B) "], %[" STR(C) "], %[TMPF1]\n" \ ROUND_X(A, A, B, I, R) #define ROUND_H_FIRST(A, B, C, D, I, R) \ "vmovdqa %[" STR(D) "], %[TMPF1]\n" \ "vpternlogd $0x96, %[" STR(B) "], %[" STR(C) "], %[TMPF1]\n" \ ROUND_X(A, A, B, I, R) #define ROUND_H(A, B, C, D, I, R) \ "vpternlogd $0x96, %[" STR(B) "], %[" STR(A) "], %[TMPF1]\n" \ ROUND_X(A, A, B, I, R) #define ROUND_I(A, B, C, D, I, R) \ "vmovdqa %[" STR(D) "], %[TMPF1]\n" \ "vpternlogd $0x63, %[" STR(B) "], %[" STR(C) "], %[TMPF1]\n" \ ROUND_X(A, A, B, I, R) #ifdef PLATFORM_AMD64 #define BLENDD(r1, r2, target) \ "vpblendd $0b1010, %[input" STR(r2) "], %[input" STR(r1) "], " target "\n" #else #define BLENDD(r1, r2, target) \ "vmovdqa %[input" STR(r1) "], " target "\n" \ "vpblendd $0b1010, %[input" STR(r2) "], " target ", " target "\n" #endif RF4(0, 0, 1) RF4(1, 2, 3) RF4(2, 4, 5) RF4(3, 6, 7) __asm__( RG4(0, 0, 3, 5) RG4(1, 2, 5, 7) RG4(2, 4, 7, 1) RG4(3, 6, 1, 3) : ASM_PARAMS(32)); __asm__( RH4(0, ROUND_H_FIRST, 2, 4, 5, 7) RH4(1, ROUND_H, 0, 2, 3, 5) RH4(2, ROUND_H, 6, 0, 1, 3) RH4(3, ROUND_H, 4, 6, 7, 1) : ASM_PARAMS(64)); __asm__( RI4(0, 0, 3, 7, 2) RI4(1, 6, 1, 5, 0) RI4(2, 4, 7, 3, 6) RI4(3, 2, 5, 1, 4) : ASM_PARAMS(96)); state[0] = _mm_add_epi32(A, state[0]); state[1] = _mm_add_epi32(B, state[1]); state[2] = _mm_add_epi32(C, state[2]); state[3] = _mm_add_epi32(D, state[3]); #undef ROUND_X #undef ROUND_F #undef ROUND_G #undef ROUND_H #undef ROUND_H_FIRST #undef ROUND_I #undef BLENDD } #endif #ifdef __AVX__ #undef RF4 #undef RG4 #undef RH4 #undef RI4 #undef READ4 #undef G_ADDS #endif #undef ASM_PARAMS_F #undef ASM_PARAMS #undef FN_VARS par2cmdline-turbo-1.4.0/parpar/hasher/md5x2-sse.h000066400000000000000000000103321514221355600215070ustar00rootroot00000000000000 #if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE__) # define MD5_USE_ASM # include "md5x2-sse-asm.h" #endif #define ADD _mm_add_epi32 #define VAL _mm_set1_epi32 #define word_t __m128i #define INPUT(k, set, ptr, offs, idx, var) ADD(var, VAL(k)) #define LOAD INPUT #define LOAD4(set, ptr, offs, idx, var0, var1, var2, var3) { \ __m128i in0 = _mm_loadu_si128((__m128i*)(ptr[0] + idx*4)); \ __m128i in1 = _mm_loadu_si128((__m128i*)(ptr[1] + idx*4)); \ var0 = _mm_unpacklo_epi64(in0, in1); \ var1 = _mm_shuffle_epi32(var0, _MM_SHUFFLE(2,3,0,1)); \ var2 = _mm_unpackhi_epi64(in0, in1); \ var3 = _mm_shuffle_epi32(var2, _MM_SHUFFLE(2,3,0,1)); \ } #ifdef __SSE2__ #include #define md5_init_lane_x2_sse(state, idx) { \ __m128i* state_ = (__m128i*)state; \ state_[0] = _mm_insert_epi16(state_[0], 0x2301, idx*4); \ state_[0] = _mm_insert_epi16(state_[0], 0x6745, idx*4 + 1); \ state_[1] = _mm_insert_epi16(state_[1], 0xab89, idx*4); \ state_[1] = _mm_insert_epi16(state_[1], 0xefcd, idx*4 + 1); \ state_[2] = _mm_insert_epi16(state_[2], 0xdcfe, idx*4); \ state_[2] = _mm_insert_epi16(state_[2], 0x98ba, idx*4 + 1); \ state_[3] = _mm_insert_epi16(state_[3], 0x5476, idx*4); \ state_[3] = _mm_insert_epi16(state_[3], 0x1032, idx*4 + 1); \ } // simulate a 32b rotate by duplicating lanes and doing a single 64b shift #define ROTATE(a, r) _mm_srli_epi64(_mm_shuffle_epi32(a, _MM_SHUFFLE(2,2,0,0)), 32-r) #define _FN(f) f##_sse #define F 1 #define G 2 #define H 3 #define I 4 // this is defined to allow a special sequence for the 'G' function - essentially, the usual bitwise OR can be replaced with an ADD, and re-ordering can be done to slightly defer the dependency on the 'b' input #define ADDF(f,a,b,c,d) ( \ f==G ? ADD(ADD(_mm_andnot_si128(d, c), a), _mm_and_si128(d, b)) : ADD(a, \ f==F ? _mm_xor_si128(_mm_and_si128(_mm_xor_si128(c, d), b), d) : ( \ f==H ? _mm_xor_si128(_mm_xor_si128(d, c), b) : \ _mm_xor_si128(_mm_or_si128(_mm_xor_si128(d, _mm_set1_epi8(-1)), b), c) \ ) \ ) \ ) #include "md5x2-base.h" #undef _FN static HEDLEY_ALWAYS_INLINE void md5_extract_x2_sse(void* dst, void* state, const int idx) { __m128* state_ = (__m128*)state; // re-arrange to AABB, CCDD __m128 tmp1 = _mm_shuffle_ps(state_[0], state_[1], _MM_SHUFFLE(2,0,2,0)); __m128 tmp2 = _mm_shuffle_ps(state_[2], state_[3], _MM_SHUFFLE(2,0,2,0)); if(idx == 0) { _mm_storeu_ps((float*)dst, _mm_shuffle_ps(tmp1, tmp2, _MM_SHUFFLE(2,0,2,0))); } else { _mm_storeu_ps((float*)dst, _mm_shuffle_ps(tmp1, tmp2, _MM_SHUFFLE(3,1,3,1))); } } #endif #ifdef __AVX__ #define md5_init_lane_x2_avx(state, idx) { \ __m128i* state_ = (__m128i*)state; \ state_[0] = _mm_insert_epi32(state_[0], 0x67452301L, idx*2); \ state_[1] = _mm_insert_epi32(state_[1], 0xefcdab89L, idx*2); \ state_[2] = _mm_insert_epi32(state_[2], 0x98badcfeL, idx*2); \ state_[3] = _mm_insert_epi32(state_[3], 0x10325476L, idx*2); \ } // TODO: consider using PSHUFB for rotate by 16 #undef ADDF #define ADDF(f,a,b,c,d) ( \ f==G ? ADD(ADD(_mm_andnot_si128(d, c), a), _mm_and_si128(d, b)) : ( \ f==I ? _mm_sub_epi32(a, _mm_xor_si128(c, _mm_andnot_si128(b, d))) : ADD(a, f==F ? \ _mm_xor_si128(_mm_and_si128(_mm_xor_si128(c, d), b), d) : \ _mm_xor_si128(_mm_xor_si128(d, c), b) \ ) \ ) \ ) #ifdef IOFFSET # undef IOFFSET #endif #define IOFFSET -1 # define _FN(f) f##_avx # include "md5x2-base.h" # undef _FN # define md5_extract_x2_avx md5_extract_x2_sse #endif #ifdef ROTATE # undef ROTATE #endif #ifdef ADDF # undef ADDF #endif #ifdef __AVX512VL__ #include #define md5_init_lane_x2_avx512 md5_init_lane_x2_avx #define ROTATE _mm_rol_epi32 #define _FN(f) f##_avx512 #undef F #undef G #undef H #undef I # define F(b,c,d) _mm_ternarylogic_epi32(d,c,b,0xD8) # define G(b,c,d) _mm_ternarylogic_epi32(d,c,b,0xAC) # define H(b,c,d) _mm_ternarylogic_epi32(d,c,b,0x96) # define I(b,c,d) _mm_ternarylogic_epi32(d,c,b,0x63) # ifdef IOFFSET # undef IOFFSET # endif #include "md5x2-base.h" #undef _FN #undef ROTATE #define md5_extract_x2_avx512 md5_extract_x2_sse #endif #undef ADD #undef VAL #undef word_t #undef INPUT #undef LOAD #undef LOAD4 #ifdef F # undef F # undef G # undef H # undef I #endif #ifdef IOFFSET # undef IOFFSET #endif #ifdef MD5_USE_ASM # undef MD5_USE_ASM #endif par2cmdline-turbo-1.4.0/parpar/hasher/md5x2-x86-asm.h000066400000000000000000000224331514221355600221250ustar00rootroot00000000000000#include "../src/platform.h" #include "../src/stdint.h" #ifndef STR # define STR_HELPER(x) #x # define STR(x) STR_HELPER(x) #endif #ifndef UNUSED # define UNUSED(...) (void)(__VA_ARGS__) #endif static HEDLEY_ALWAYS_INLINE void md5_process_block_x2_scalar(uint32_t* state, const uint8_t* const* HEDLEY_RESTRICT data, size_t offset) { UNUSED(offset); uint32_t A1 = state[0]; uint32_t B1 = state[1]; uint32_t C1 = state[2]; uint32_t D1 = state[3]; uint32_t A2 = state[4]; uint32_t B2 = state[5]; uint32_t C2 = state[6]; uint32_t D2 = state[7]; uintptr_t tmp1, tmp2; const uint32_t* const* HEDLEY_RESTRICT _data = (const uint32_t* const* HEDLEY_RESTRICT)data; #define ROUND_F(A1, B1, C1, D1, A2, B2, C2, D2, NEXT_IN1, NEXT_IN2, K, R) \ "movl %k[" STR(D1) "], %k[TMP1]\n" \ "movl %k[" STR(D2) "], %k[TMP2]\n" \ "addl $" STR(K) ", %k[" STR(A1) "]\n" \ "addl $" STR(K) ", %k[" STR(A2) "]\n" \ "xorl %k[" STR(C1) "], %k[TMP1]\n" \ "xorl %k[" STR(C2) "], %k[TMP2]\n" \ "andl %k[" STR(B1) "], %k[TMP1]\n" \ "andl %k[" STR(B2) "], %k[TMP2]\n" \ "xorl %k[" STR(D1) "], %k[TMP1]\n" \ "xorl %k[" STR(D2) "], %k[TMP2]\n" \ "addl " NEXT_IN1 ", %k[" STR(D1) "]\n" \ "addl " NEXT_IN2 ", %k[" STR(D2) "]\n" \ "addl %k[TMP1], %k[" STR(A1) "]\n" \ "addl %k[TMP2], %k[" STR(A2) "]\n" \ "roll $" STR(R) ", %k[" STR(A1) "]\n" \ "roll $" STR(R) ", %k[" STR(A2) "]\n" \ "addl %k[" STR(B1) "], %k[" STR(A1) "]\n" \ "addl %k[" STR(B2) "], %k[" STR(A2) "]\n" // can't use H shortcut because D input is updated early #define ROUND_H(A1, B1, C1, D1, A2, B2, C2, D2, NEXT_IN1, NEXT_IN2, K, R) \ "movl %k[" STR(D1) "], %k[TMP1]\n" \ "movl %k[" STR(D2) "], %k[TMP2]\n" \ "addl $" STR(K) ", %k[" STR(A1) "]\n" \ "addl $" STR(K) ", %k[" STR(A2) "]\n" \ "xorl %k[" STR(C1) "], %k[TMP1]\n" \ "xorl %k[" STR(C2) "], %k[TMP2]\n" \ "addl " NEXT_IN1 ", %k[" STR(D1) "]\n" \ "addl " NEXT_IN2 ", %k[" STR(D2) "]\n" \ "xorl %k[" STR(B1) "], %k[TMP1]\n" \ "xorl %k[" STR(B2) "], %k[TMP2]\n" \ "addl %k[TMP1], %k[" STR(A1) "]\n" \ "addl %k[TMP2], %k[" STR(A2) "]\n" \ "roll $" STR(R) ", %k[" STR(A1) "]\n" \ "roll $" STR(R) ", %k[" STR(A2) "]\n" \ "addl %k[" STR(B1) "], %k[" STR(A1) "]\n" \ "addl %k[" STR(B2) "], %k[" STR(A2) "]\n" #ifdef _MD5_USE_BMI1_ #define ROUND_I_INIT(A1, B1, D1, A2, B2, D2, K) \ "addl $" STR(K) "-1, %k[" STR(A1) "]\n" \ "addl $" STR(K) "-1, %k[" STR(A2) "]\n" \ "andnl %k[" STR(D1) "], %k[" STR(B1) "], %k[TMP1]\n" \ "andnl %k[" STR(D2) "], %k[" STR(B2) "], %k[TMP2]\n" #define ROUND_I_ADD "subl" #else #define ROUND_I_INIT(A1, B1, D1, A2, B2, D2, K) \ "movl %k[" STR(D1) "], %k[TMP1]\n" \ "movl %k[" STR(D2) "], %k[TMP2]\n" \ "addl $" STR(K) ", %k[" STR(A1) "]\n" \ "addl $" STR(K) ", %k[" STR(A2) "]\n" \ "notl %k[TMP1]\n" \ "notl %k[TMP2]\n" \ "orl %k[" STR(B1) "], %k[TMP1]\n" \ "orl %k[" STR(B2) "], %k[TMP2]\n" #define ROUND_I_ADD "addl" #endif #define ROUND_I(A1, B1, C1, D1, A2, B2, C2, D2, NEXT_IN1, NEXT_IN2, K, R) \ ROUND_I_INIT(A1, B1, D1, A2, B2, D2, K) \ "xorl %k[" STR(C1) "], %k[TMP1]\n" \ "xorl %k[" STR(C2) "], %k[TMP2]\n" \ "addl " NEXT_IN1 ", %k[" STR(D1) "]\n" \ "addl " NEXT_IN2 ", %k[" STR(D2) "]\n" \ ROUND_I_ADD " %k[TMP1], %k[" STR(A1) "]\n" \ ROUND_I_ADD " %k[TMP2], %k[" STR(A2) "]\n" \ "roll $" STR(R) ", %k[" STR(A1) "]\n" \ "roll $" STR(R) ", %k[" STR(A2) "]\n" \ "addl %k[" STR(B1) "], %k[" STR(A1) "]\n" \ "addl %k[" STR(B2) "], %k[" STR(A2) "]\n" #define ROUND_I_LAST(A1, B1, C1, D1, A2, B2, C2, D2, K, R) \ ROUND_I_INIT(A1, B1, D1, A2, B2, D2, K) \ "xorl %k[" STR(C1) "], %k[TMP1]\n" \ "xorl %k[" STR(C2) "], %k[TMP2]\n" \ ROUND_I_ADD " %k[TMP1], %k[" STR(A1) "]\n" \ ROUND_I_ADD " %k[TMP2], %k[" STR(A2) "]\n" \ "roll $" STR(R) ", %k[" STR(A1) "]\n" \ "roll $" STR(R) ", %k[" STR(A2) "]\n" \ "addl %k[" STR(B1) "], %k[" STR(A1) "]\n" \ "addl %k[" STR(B2) "], %k[" STR(A2) "]\n" #ifdef _MD5_USE_BMI1_ #define ROUND_G(A1, B1, C1, D1, A2, B2, C2, D2, NEXT_IN1, NEXT_IN2, K, R) \ "addl $" STR(K) ", %k[" STR(A1) "]\n" \ "addl $" STR(K) ", %k[" STR(A2) "]\n" \ "andnl %k[" STR(C1) "], %k[" STR(D1) "], %k[TMP1]\n" \ "andnl %k[" STR(C2) "], %k[" STR(D2) "], %k[TMP2]\n" \ "addl %k[TMP1], %k[" STR(A1) "]\n" \ "addl %k[TMP2], %k[" STR(A2) "]\n" \ "movl %k[" STR(D1) "], %k[TMP1]\n" \ "movl %k[" STR(D2) "], %k[TMP2]\n" \ "addl " NEXT_IN1 ", %k[" STR(D1) "]\n" \ "addl " NEXT_IN2 ", %k[" STR(D2) "]\n" \ "andl %k[" STR(B1) "], %k[TMP1]\n" \ "andl %k[" STR(B2) "], %k[TMP2]\n" \ "addl %k[TMP1], %k[" STR(A1) "]\n" \ "addl %k[TMP2], %k[" STR(A2) "]\n" \ "roll $" STR(R) ", %k[" STR(A1) "]\n" \ "roll $" STR(R) ", %k[" STR(A2) "]\n" \ "addl %k[" STR(B1) "], %k[" STR(A1) "]\n" \ "addl %k[" STR(B2) "], %k[" STR(A2) "]\n" #else #define ROUND_G(A1, B1, C1, D1, A2, B2, C2, D2, NEXT_IN1, NEXT_IN2, K, R) \ "movl %k[" STR(D1) "], %k[TMP1]\n" \ "movl %k[" STR(D2) "], %k[TMP2]\n" \ "addl $" STR(K) ", %k[" STR(A1) "]\n" \ "addl $" STR(K) ", %k[" STR(A2) "]\n" \ "notl %k[TMP1]\n" \ "notl %k[TMP2]\n" \ "andl %k[" STR(C1) "], %k[TMP1]\n" \ "andl %k[" STR(C2) "], %k[TMP2]\n" \ "addl %k[TMP1], %k[" STR(A1) "]\n" \ "addl %k[TMP2], %k[" STR(A2) "]\n" \ "movl %k[" STR(D1) "], %k[TMP1]\n" \ "movl %k[" STR(D2) "], %k[TMP2]\n" \ "addl " NEXT_IN1 ", %k[" STR(D1) "]\n" \ "addl " NEXT_IN2 ", %k[" STR(D2) "]\n" \ "andl %k[" STR(B1) "], %k[TMP1]\n" \ "andl %k[" STR(B2) "], %k[TMP2]\n" \ "addl %k[TMP1], %k[" STR(A1) "]\n" \ "addl %k[TMP2], %k[" STR(A2) "]\n" \ "roll $" STR(R) ", %k[" STR(A1) "]\n" \ "roll $" STR(R) ", %k[" STR(A2) "]\n" \ "addl %k[" STR(B1) "], %k[" STR(A1) "]\n" \ "addl %k[" STR(B2) "], %k[" STR(A2) "]\n" #endif #define ASM_PARAMS(in) \ [A1]"+&r"(A1), [B1]"+&r"(B1), [C1]"+&r"(C1), [D1]"+&r"(D1), \ [A2]"+&r"(A2), [B2]"+&r"(B2), [C2]"+&r"(C2), [D2]"+&r"(D2), \ [TMP1]"=&r"(tmp1), [TMP2]"=&r"(tmp2) \ : [i0]"m"(_data[0][in]), [i1]"m"(_data[1][in]) \ : #define RF4(i0, i1, i2, i3, k0, k1, k2, k3) __asm__( \ ROUND_F(A1, B1, C1, D1, A2, B2, C2, D2, "%[i0]", "%[i1]", k0, 7) \ : ASM_PARAMS(i0)); __asm__( \ ROUND_F(D1, A1, B1, C1, D2, A2, B2, C2, "%[i0]", "%[i1]", k1, 12) \ : ASM_PARAMS(i1)); __asm__( \ ROUND_F(C1, D1, A1, B1, C2, D2, A2, B2, "%[i0]", "%[i1]", k2, 17) \ : ASM_PARAMS(i2)); __asm__( \ ROUND_F(B1, C1, D1, A1, B2, C2, D2, A2, "%[i0]", "%[i1]", k3, 22) \ : ASM_PARAMS(i3)); #define RG4(i0, i1, i2, i3, k0, k1, k2, k3) __asm__( \ ROUND_G(A1, B1, C1, D1, A2, B2, C2, D2, "%[i0]", "%[i1]", k0, 5) \ : ASM_PARAMS(i0)); __asm__( \ ROUND_G(D1, A1, B1, C1, D2, A2, B2, C2, "%[i0]", "%[i1]", k1, 9) \ : ASM_PARAMS(i1)); __asm__( \ ROUND_G(C1, D1, A1, B1, C2, D2, A2, B2, "%[i0]", "%[i1]", k2, 14) \ : ASM_PARAMS(i2)); __asm__( \ ROUND_G(B1, C1, D1, A1, B2, C2, D2, A2, "%[i0]", "%[i1]", k3, 20) \ : ASM_PARAMS(i3)); #define RH4(i0, i1, i2, i3, k0, k1, k2, k3) __asm__( \ ROUND_H(A1, B1, C1, D1, A2, B2, C2, D2, "%[i0]", "%[i1]", k0, 4) \ : ASM_PARAMS(i0)); __asm__( \ ROUND_H(D1, A1, B1, C1, D2, A2, B2, C2, "%[i0]", "%[i1]", k1, 11) \ : ASM_PARAMS(i1)); __asm__( \ ROUND_H(C1, D1, A1, B1, C2, D2, A2, B2, "%[i0]", "%[i1]", k2, 16) \ : ASM_PARAMS(i2)); __asm__( \ ROUND_H(B1, C1, D1, A1, B2, C2, D2, A2, "%[i0]", "%[i1]", k3, 23) \ : ASM_PARAMS(i3)); #define RI4(i0, i1, i2, i3, k0, k1, k2, k3) __asm__( \ ROUND_I(A1, B1, C1, D1, A2, B2, C2, D2, "%[i0]", "%[i1]", k0, 6) \ : ASM_PARAMS(i0)); __asm__( \ ROUND_I(D1, A1, B1, C1, D2, A2, B2, C2, "%[i0]", "%[i1]", k1, 10) \ : ASM_PARAMS(i1)); __asm__( \ ROUND_I(C1, D1, A1, B1, C2, D2, A2, B2, "%[i0]", "%[i1]", k2, 15) \ : ASM_PARAMS(i2)); __asm__( \ ROUND_I(B1, C1, D1, A1, B2, C2, D2, A2, "%[i0]", "%[i1]", k3, 21) \ : ASM_PARAMS(i3)); A1 += read32(_data[0]); A2 += read32(_data[1]); RF4( 1, 2, 3, 4, -0x28955b88, -0x173848aa, 0x242070db, -0x3e423112) RF4( 5, 6, 7, 8, -0x0a83f051, 0x4787c62a, -0x57cfb9ed, -0x02b96aff) RF4( 9, 10, 11, 12, 0x698098d8, -0x74bb0851, -0x0000a44f, -0x76a32842) RF4(13, 14, 15, 1, 0x6b901122, -0x02678e6d, -0x5986bc72, 0x49b40821) RG4( 6, 11, 0, 5, -0x09e1da9e, -0x3fbf4cc0, 0x265e5a51, -0x16493856) RG4(10, 15, 4, 9, -0x29d0efa3, 0x02441453, -0x275e197f, -0x182c0438) RG4(14, 3, 8, 13, 0x21e1cde6, -0x3cc8f82a, -0x0b2af279, 0x455a14ed) RG4( 2, 7, 12, 5, -0x561c16fb, -0x03105c08, 0x676f02d9, -0x72d5b376) RH4( 8, 11, 14, 1, -0x0005c6be, -0x788e097f, 0x6d9d6122, -0x021ac7f4) RH4( 4, 7, 10, 13, -0x5b4115bc, 0x4bdecfa9, -0x0944b4a0, -0x41404390) RH4( 0, 3, 6, 9, 0x289b7ec6, -0x155ed806, -0x2b10cf7b, 0x04881d05) RH4(12, 15, 2, 0, -0x262b2fc7, -0x1924661b, 0x1fa27cf8, -0x3b53a99b) RI4( 7, 14, 5, 12, -0x0bd6ddbc, 0x432aff97, -0x546bdc59, -0x036c5fc7) RI4( 3, 10, 1, 8, 0x655b59c3, -0x70f3336e, -0x00100b83, -0x7a7ba22f) RI4(15, 6, 13, 4, 0x6fa87e4f, -0x01d31920, -0x5cfebcec, 0x4e0811a1) __asm__( ROUND_I(A1, B1, C1, D1, A2, B2, C2, D2, "%[i0]", "%[i1]", -0x08ac817e, 6) : ASM_PARAMS(11)); __asm__( ROUND_I(D1, A1, B1, C1, D2, A2, B2, C2, "%[i0]", "%[i1]", -0x42c50dcb, 10) : ASM_PARAMS(2)); __asm__( ROUND_I(C1, D1, A1, B1, C2, D2, A2, B2, "%[i0]", "%[i1]", 0x2ad7d2bb, 15) : ASM_PARAMS(9)); __asm__( ROUND_I_LAST(B1, C1, D1, A1, B2, C2, D2, A2, -0x14792c6f, 21) : ASM_PARAMS(0)); state[0] += A1; state[1] += B1; state[2] += C1; state[3] += D1; state[4] += A2; state[5] += B2; state[6] += C2; state[7] += D2; #undef ROUND_F #undef ROUND_G #undef ROUND_H #undef ROUND_I #undef ROUND_I_LAST #undef RF4 #undef RG4 #undef RH4 #undef RI4 #undef ASM_PARAMS } par2cmdline-turbo-1.4.0/parpar/hasher/tables.cpp000066400000000000000000000377641514221355600216070ustar00rootroot00000000000000#include "../src/platform.h" #include "../src/stdint.h" // this is based off Fast CRC32 slice-by-4: https://create.stephan-brumme.com/crc32/ extern const uint32_t Crc32Lookup[4][256]; const uint32_t Crc32Lookup[4][256] = {{ 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3, 0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, 0x1DB71064,0x6AB020F2,0xF3B97148,0x84BE41DE,0x1ADAD47D,0x6DDDE4EB,0xF4D4B551,0x83D385C7, 0x136C9856,0x646BA8C0,0xFD62F97A,0x8A65C9EC,0x14015C4F,0x63066CD9,0xFA0F3D63,0x8D080DF5, 0x3B6E20C8,0x4C69105E,0xD56041E4,0xA2677172,0x3C03E4D1,0x4B04D447,0xD20D85FD,0xA50AB56B, 0x35B5A8FA,0x42B2986C,0xDBBBC9D6,0xACBCF940,0x32D86CE3,0x45DF5C75,0xDCD60DCF,0xABD13D59, 0x26D930AC,0x51DE003A,0xC8D75180,0xBFD06116,0x21B4F4B5,0x56B3C423,0xCFBA9599,0xB8BDA50F, 0x2802B89E,0x5F058808,0xC60CD9B2,0xB10BE924,0x2F6F7C87,0x58684C11,0xC1611DAB,0xB6662D3D, 0x76DC4190,0x01DB7106,0x98D220BC,0xEFD5102A,0x71B18589,0x06B6B51F,0x9FBFE4A5,0xE8B8D433, 0x7807C9A2,0x0F00F934,0x9609A88E,0xE10E9818,0x7F6A0DBB,0x086D3D2D,0x91646C97,0xE6635C01, 0x6B6B51F4,0x1C6C6162,0x856530D8,0xF262004E,0x6C0695ED,0x1B01A57B,0x8208F4C1,0xF50FC457, 0x65B0D9C6,0x12B7E950,0x8BBEB8EA,0xFCB9887C,0x62DD1DDF,0x15DA2D49,0x8CD37CF3,0xFBD44C65, 0x4DB26158,0x3AB551CE,0xA3BC0074,0xD4BB30E2,0x4ADFA541,0x3DD895D7,0xA4D1C46D,0xD3D6F4FB, 0x4369E96A,0x346ED9FC,0xAD678846,0xDA60B8D0,0x44042D73,0x33031DE5,0xAA0A4C5F,0xDD0D7CC9, 0x5005713C,0x270241AA,0xBE0B1010,0xC90C2086,0x5768B525,0x206F85B3,0xB966D409,0xCE61E49F, 0x5EDEF90E,0x29D9C998,0xB0D09822,0xC7D7A8B4,0x59B33D17,0x2EB40D81,0xB7BD5C3B,0xC0BA6CAD, 0xEDB88320,0x9ABFB3B6,0x03B6E20C,0x74B1D29A,0xEAD54739,0x9DD277AF,0x04DB2615,0x73DC1683, 0xE3630B12,0x94643B84,0x0D6D6A3E,0x7A6A5AA8,0xE40ECF0B,0x9309FF9D,0x0A00AE27,0x7D079EB1, 0xF00F9344,0x8708A3D2,0x1E01F268,0x6906C2FE,0xF762575D,0x806567CB,0x196C3671,0x6E6B06E7, 0xFED41B76,0x89D32BE0,0x10DA7A5A,0x67DD4ACC,0xF9B9DF6F,0x8EBEEFF9,0x17B7BE43,0x60B08ED5, 0xD6D6A3E8,0xA1D1937E,0x38D8C2C4,0x4FDFF252,0xD1BB67F1,0xA6BC5767,0x3FB506DD,0x48B2364B, 0xD80D2BDA,0xAF0A1B4C,0x36034AF6,0x41047A60,0xDF60EFC3,0xA867DF55,0x316E8EEF,0x4669BE79, 0xCB61B38C,0xBC66831A,0x256FD2A0,0x5268E236,0xCC0C7795,0xBB0B4703,0x220216B9,0x5505262F, 0xC5BA3BBE,0xB2BD0B28,0x2BB45A92,0x5CB36A04,0xC2D7FFA7,0xB5D0CF31,0x2CD99E8B,0x5BDEAE1D, 0x9B64C2B0,0xEC63F226,0x756AA39C,0x026D930A,0x9C0906A9,0xEB0E363F,0x72076785,0x05005713, 0x95BF4A82,0xE2B87A14,0x7BB12BAE,0x0CB61B38,0x92D28E9B,0xE5D5BE0D,0x7CDCEFB7,0x0BDBDF21, 0x86D3D2D4,0xF1D4E242,0x68DDB3F8,0x1FDA836E,0x81BE16CD,0xF6B9265B,0x6FB077E1,0x18B74777, 0x88085AE6,0xFF0F6A70,0x66063BCA,0x11010B5C,0x8F659EFF,0xF862AE69,0x616BFFD3,0x166CCF45, 0xA00AE278,0xD70DD2EE,0x4E048354,0x3903B3C2,0xA7672661,0xD06016F7,0x4969474D,0x3E6E77DB, 0xAED16A4A,0xD9D65ADC,0x40DF0B66,0x37D83BF0,0xA9BCAE53,0xDEBB9EC5,0x47B2CF7F,0x30B5FFE9, 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF, 0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D }, { 0x00000000,0x191B3141,0x32366282,0x2B2D53C3,0x646CC504,0x7D77F445,0x565AA786,0x4F4196C7, 0xC8D98A08,0xD1C2BB49,0xFAEFE88A,0xE3F4D9CB,0xACB54F0C,0xB5AE7E4D,0x9E832D8E,0x87981CCF, 0x4AC21251,0x53D92310,0x78F470D3,0x61EF4192,0x2EAED755,0x37B5E614,0x1C98B5D7,0x05838496, 0x821B9859,0x9B00A918,0xB02DFADB,0xA936CB9A,0xE6775D5D,0xFF6C6C1C,0xD4413FDF,0xCD5A0E9E, 0x958424A2,0x8C9F15E3,0xA7B24620,0xBEA97761,0xF1E8E1A6,0xE8F3D0E7,0xC3DE8324,0xDAC5B265, 0x5D5DAEAA,0x44469FEB,0x6F6BCC28,0x7670FD69,0x39316BAE,0x202A5AEF,0x0B07092C,0x121C386D, 0xDF4636F3,0xC65D07B2,0xED705471,0xF46B6530,0xBB2AF3F7,0xA231C2B6,0x891C9175,0x9007A034, 0x179FBCFB,0x0E848DBA,0x25A9DE79,0x3CB2EF38,0x73F379FF,0x6AE848BE,0x41C51B7D,0x58DE2A3C, 0xF0794F05,0xE9627E44,0xC24F2D87,0xDB541CC6,0x94158A01,0x8D0EBB40,0xA623E883,0xBF38D9C2, 0x38A0C50D,0x21BBF44C,0x0A96A78F,0x138D96CE,0x5CCC0009,0x45D73148,0x6EFA628B,0x77E153CA, 0xBABB5D54,0xA3A06C15,0x888D3FD6,0x91960E97,0xDED79850,0xC7CCA911,0xECE1FAD2,0xF5FACB93, 0x7262D75C,0x6B79E61D,0x4054B5DE,0x594F849F,0x160E1258,0x0F152319,0x243870DA,0x3D23419B, 0x65FD6BA7,0x7CE65AE6,0x57CB0925,0x4ED03864,0x0191AEA3,0x188A9FE2,0x33A7CC21,0x2ABCFD60, 0xAD24E1AF,0xB43FD0EE,0x9F12832D,0x8609B26C,0xC94824AB,0xD05315EA,0xFB7E4629,0xE2657768, 0x2F3F79F6,0x362448B7,0x1D091B74,0x04122A35,0x4B53BCF2,0x52488DB3,0x7965DE70,0x607EEF31, 0xE7E6F3FE,0xFEFDC2BF,0xD5D0917C,0xCCCBA03D,0x838A36FA,0x9A9107BB,0xB1BC5478,0xA8A76539, 0x3B83984B,0x2298A90A,0x09B5FAC9,0x10AECB88,0x5FEF5D4F,0x46F46C0E,0x6DD93FCD,0x74C20E8C, 0xF35A1243,0xEA412302,0xC16C70C1,0xD8774180,0x9736D747,0x8E2DE606,0xA500B5C5,0xBC1B8484, 0x71418A1A,0x685ABB5B,0x4377E898,0x5A6CD9D9,0x152D4F1E,0x0C367E5F,0x271B2D9C,0x3E001CDD, 0xB9980012,0xA0833153,0x8BAE6290,0x92B553D1,0xDDF4C516,0xC4EFF457,0xEFC2A794,0xF6D996D5, 0xAE07BCE9,0xB71C8DA8,0x9C31DE6B,0x852AEF2A,0xCA6B79ED,0xD37048AC,0xF85D1B6F,0xE1462A2E, 0x66DE36E1,0x7FC507A0,0x54E85463,0x4DF36522,0x02B2F3E5,0x1BA9C2A4,0x30849167,0x299FA026, 0xE4C5AEB8,0xFDDE9FF9,0xD6F3CC3A,0xCFE8FD7B,0x80A96BBC,0x99B25AFD,0xB29F093E,0xAB84387F, 0x2C1C24B0,0x350715F1,0x1E2A4632,0x07317773,0x4870E1B4,0x516BD0F5,0x7A468336,0x635DB277, 0xCBFAD74E,0xD2E1E60F,0xF9CCB5CC,0xE0D7848D,0xAF96124A,0xB68D230B,0x9DA070C8,0x84BB4189, 0x03235D46,0x1A386C07,0x31153FC4,0x280E0E85,0x674F9842,0x7E54A903,0x5579FAC0,0x4C62CB81, 0x8138C51F,0x9823F45E,0xB30EA79D,0xAA1596DC,0xE554001B,0xFC4F315A,0xD7626299,0xCE7953D8, 0x49E14F17,0x50FA7E56,0x7BD72D95,0x62CC1CD4,0x2D8D8A13,0x3496BB52,0x1FBBE891,0x06A0D9D0, 0x5E7EF3EC,0x4765C2AD,0x6C48916E,0x7553A02F,0x3A1236E8,0x230907A9,0x0824546A,0x113F652B, 0x96A779E4,0x8FBC48A5,0xA4911B66,0xBD8A2A27,0xF2CBBCE0,0xEBD08DA1,0xC0FDDE62,0xD9E6EF23, 0x14BCE1BD,0x0DA7D0FC,0x268A833F,0x3F91B27E,0x70D024B9,0x69CB15F8,0x42E6463B,0x5BFD777A, 0xDC656BB5,0xC57E5AF4,0xEE530937,0xF7483876,0xB809AEB1,0xA1129FF0,0x8A3FCC33,0x9324FD72 }, { 0x00000000,0x01C26A37,0x0384D46E,0x0246BE59,0x0709A8DC,0x06CBC2EB,0x048D7CB2,0x054F1685, 0x0E1351B8,0x0FD13B8F,0x0D9785D6,0x0C55EFE1,0x091AF964,0x08D89353,0x0A9E2D0A,0x0B5C473D, 0x1C26A370,0x1DE4C947,0x1FA2771E,0x1E601D29,0x1B2F0BAC,0x1AED619B,0x18ABDFC2,0x1969B5F5, 0x1235F2C8,0x13F798FF,0x11B126A6,0x10734C91,0x153C5A14,0x14FE3023,0x16B88E7A,0x177AE44D, 0x384D46E0,0x398F2CD7,0x3BC9928E,0x3A0BF8B9,0x3F44EE3C,0x3E86840B,0x3CC03A52,0x3D025065, 0x365E1758,0x379C7D6F,0x35DAC336,0x3418A901,0x3157BF84,0x3095D5B3,0x32D36BEA,0x331101DD, 0x246BE590,0x25A98FA7,0x27EF31FE,0x262D5BC9,0x23624D4C,0x22A0277B,0x20E69922,0x2124F315, 0x2A78B428,0x2BBADE1F,0x29FC6046,0x283E0A71,0x2D711CF4,0x2CB376C3,0x2EF5C89A,0x2F37A2AD, 0x709A8DC0,0x7158E7F7,0x731E59AE,0x72DC3399,0x7793251C,0x76514F2B,0x7417F172,0x75D59B45, 0x7E89DC78,0x7F4BB64F,0x7D0D0816,0x7CCF6221,0x798074A4,0x78421E93,0x7A04A0CA,0x7BC6CAFD, 0x6CBC2EB0,0x6D7E4487,0x6F38FADE,0x6EFA90E9,0x6BB5866C,0x6A77EC5B,0x68315202,0x69F33835, 0x62AF7F08,0x636D153F,0x612BAB66,0x60E9C151,0x65A6D7D4,0x6464BDE3,0x662203BA,0x67E0698D, 0x48D7CB20,0x4915A117,0x4B531F4E,0x4A917579,0x4FDE63FC,0x4E1C09CB,0x4C5AB792,0x4D98DDA5, 0x46C49A98,0x4706F0AF,0x45404EF6,0x448224C1,0x41CD3244,0x400F5873,0x4249E62A,0x438B8C1D, 0x54F16850,0x55330267,0x5775BC3E,0x56B7D609,0x53F8C08C,0x523AAABB,0x507C14E2,0x51BE7ED5, 0x5AE239E8,0x5B2053DF,0x5966ED86,0x58A487B1,0x5DEB9134,0x5C29FB03,0x5E6F455A,0x5FAD2F6D, 0xE1351B80,0xE0F771B7,0xE2B1CFEE,0xE373A5D9,0xE63CB35C,0xE7FED96B,0xE5B86732,0xE47A0D05, 0xEF264A38,0xEEE4200F,0xECA29E56,0xED60F461,0xE82FE2E4,0xE9ED88D3,0xEBAB368A,0xEA695CBD, 0xFD13B8F0,0xFCD1D2C7,0xFE976C9E,0xFF5506A9,0xFA1A102C,0xFBD87A1B,0xF99EC442,0xF85CAE75, 0xF300E948,0xF2C2837F,0xF0843D26,0xF1465711,0xF4094194,0xF5CB2BA3,0xF78D95FA,0xF64FFFCD, 0xD9785D60,0xD8BA3757,0xDAFC890E,0xDB3EE339,0xDE71F5BC,0xDFB39F8B,0xDDF521D2,0xDC374BE5, 0xD76B0CD8,0xD6A966EF,0xD4EFD8B6,0xD52DB281,0xD062A404,0xD1A0CE33,0xD3E6706A,0xD2241A5D, 0xC55EFE10,0xC49C9427,0xC6DA2A7E,0xC7184049,0xC25756CC,0xC3953CFB,0xC1D382A2,0xC011E895, 0xCB4DAFA8,0xCA8FC59F,0xC8C97BC6,0xC90B11F1,0xCC440774,0xCD866D43,0xCFC0D31A,0xCE02B92D, 0x91AF9640,0x906DFC77,0x922B422E,0x93E92819,0x96A63E9C,0x976454AB,0x9522EAF2,0x94E080C5, 0x9FBCC7F8,0x9E7EADCF,0x9C381396,0x9DFA79A1,0x98B56F24,0x99770513,0x9B31BB4A,0x9AF3D17D, 0x8D893530,0x8C4B5F07,0x8E0DE15E,0x8FCF8B69,0x8A809DEC,0x8B42F7DB,0x89044982,0x88C623B5, 0x839A6488,0x82580EBF,0x801EB0E6,0x81DCDAD1,0x8493CC54,0x8551A663,0x8717183A,0x86D5720D, 0xA9E2D0A0,0xA820BA97,0xAA6604CE,0xABA46EF9,0xAEEB787C,0xAF29124B,0xAD6FAC12,0xACADC625, 0xA7F18118,0xA633EB2F,0xA4755576,0xA5B73F41,0xA0F829C4,0xA13A43F3,0xA37CFDAA,0xA2BE979D, 0xB5C473D0,0xB40619E7,0xB640A7BE,0xB782CD89,0xB2CDDB0C,0xB30FB13B,0xB1490F62,0xB08B6555, 0xBBD72268,0xBA15485F,0xB853F606,0xB9919C31,0xBCDE8AB4,0xBD1CE083,0xBF5A5EDA,0xBE9834ED }, { 0x00000000,0xB8BC6765,0xAA09C88B,0x12B5AFEE,0x8F629757,0x37DEF032,0x256B5FDC,0x9DD738B9, 0xC5B428EF,0x7D084F8A,0x6FBDE064,0xD7018701,0x4AD6BFB8,0xF26AD8DD,0xE0DF7733,0x58631056, 0x5019579F,0xE8A530FA,0xFA109F14,0x42ACF871,0xDF7BC0C8,0x67C7A7AD,0x75720843,0xCDCE6F26, 0x95AD7F70,0x2D111815,0x3FA4B7FB,0x8718D09E,0x1ACFE827,0xA2738F42,0xB0C620AC,0x087A47C9, 0xA032AF3E,0x188EC85B,0x0A3B67B5,0xB28700D0,0x2F503869,0x97EC5F0C,0x8559F0E2,0x3DE59787, 0x658687D1,0xDD3AE0B4,0xCF8F4F5A,0x7733283F,0xEAE41086,0x525877E3,0x40EDD80D,0xF851BF68, 0xF02BF8A1,0x48979FC4,0x5A22302A,0xE29E574F,0x7F496FF6,0xC7F50893,0xD540A77D,0x6DFCC018, 0x359FD04E,0x8D23B72B,0x9F9618C5,0x272A7FA0,0xBAFD4719,0x0241207C,0x10F48F92,0xA848E8F7, 0x9B14583D,0x23A83F58,0x311D90B6,0x89A1F7D3,0x1476CF6A,0xACCAA80F,0xBE7F07E1,0x06C36084, 0x5EA070D2,0xE61C17B7,0xF4A9B859,0x4C15DF3C,0xD1C2E785,0x697E80E0,0x7BCB2F0E,0xC377486B, 0xCB0D0FA2,0x73B168C7,0x6104C729,0xD9B8A04C,0x446F98F5,0xFCD3FF90,0xEE66507E,0x56DA371B, 0x0EB9274D,0xB6054028,0xA4B0EFC6,0x1C0C88A3,0x81DBB01A,0x3967D77F,0x2BD27891,0x936E1FF4, 0x3B26F703,0x839A9066,0x912F3F88,0x299358ED,0xB4446054,0x0CF80731,0x1E4DA8DF,0xA6F1CFBA, 0xFE92DFEC,0x462EB889,0x549B1767,0xEC277002,0x71F048BB,0xC94C2FDE,0xDBF98030,0x6345E755, 0x6B3FA09C,0xD383C7F9,0xC1366817,0x798A0F72,0xE45D37CB,0x5CE150AE,0x4E54FF40,0xF6E89825, 0xAE8B8873,0x1637EF16,0x048240F8,0xBC3E279D,0x21E91F24,0x99557841,0x8BE0D7AF,0x335CB0CA, 0xED59B63B,0x55E5D15E,0x47507EB0,0xFFEC19D5,0x623B216C,0xDA874609,0xC832E9E7,0x708E8E82, 0x28ED9ED4,0x9051F9B1,0x82E4565F,0x3A58313A,0xA78F0983,0x1F336EE6,0x0D86C108,0xB53AA66D, 0xBD40E1A4,0x05FC86C1,0x1749292F,0xAFF54E4A,0x322276F3,0x8A9E1196,0x982BBE78,0x2097D91D, 0x78F4C94B,0xC048AE2E,0xD2FD01C0,0x6A4166A5,0xF7965E1C,0x4F2A3979,0x5D9F9697,0xE523F1F2, 0x4D6B1905,0xF5D77E60,0xE762D18E,0x5FDEB6EB,0xC2098E52,0x7AB5E937,0x680046D9,0xD0BC21BC, 0x88DF31EA,0x3063568F,0x22D6F961,0x9A6A9E04,0x07BDA6BD,0xBF01C1D8,0xADB46E36,0x15080953, 0x1D724E9A,0xA5CE29FF,0xB77B8611,0x0FC7E174,0x9210D9CD,0x2AACBEA8,0x38191146,0x80A57623, 0xD8C66675,0x607A0110,0x72CFAEFE,0xCA73C99B,0x57A4F122,0xEF189647,0xFDAD39A9,0x45115ECC, 0x764DEE06,0xCEF18963,0xDC44268D,0x64F841E8,0xF92F7951,0x41931E34,0x5326B1DA,0xEB9AD6BF, 0xB3F9C6E9,0x0B45A18C,0x19F00E62,0xA14C6907,0x3C9B51BE,0x842736DB,0x96929935,0x2E2EFE50, 0x2654B999,0x9EE8DEFC,0x8C5D7112,0x34E11677,0xA9362ECE,0x118A49AB,0x033FE645,0xBB838120, 0xE3E09176,0x5B5CF613,0x49E959FD,0xF1553E98,0x6C820621,0xD43E6144,0xC68BCEAA,0x7E37A9CF, 0xD67F4138,0x6EC3265D,0x7C7689B3,0xC4CAEED6,0x591DD66F,0xE1A1B10A,0xF3141EE4,0x4BA87981, 0x13CB69D7,0xAB770EB2,0xB9C2A15C,0x017EC639,0x9CA9FE80,0x241599E5,0x36A0360B,0x8E1C516E, 0x866616A7,0x3EDA71C2,0x2C6FDE2C,0x94D3B949,0x090481F0,0xB1B8E695,0xA30D497B,0x1BB12E1E, 0x43D23E48,0xFB6E592D,0xE9DBF6C3,0x516791A6,0xCCB0A91F,0x740CCE7A,0x66B96194,0xDE0506F1 }}; #ifdef PLATFORM_X86 extern const unsigned pshufb_shf_table[60]; ALIGN_TO(16, const unsigned pshufb_shf_table[60]) = { 0x84838281, 0x88878685, 0x8c8b8a89, 0x008f8e8d, /* shl 15 (16 - 1)/shr1 */ 0x85848382, 0x89888786, 0x8d8c8b8a, 0x01008f8e, /* shl 14 (16 - 3)/shr2 */ 0x86858483, 0x8a898887, 0x8e8d8c8b, 0x0201008f, /* shl 13 (16 - 4)/shr3 */ 0x87868584, 0x8b8a8988, 0x8f8e8d8c, 0x03020100, /* shl 12 (16 - 4)/shr4 */ 0x88878685, 0x8c8b8a89, 0x008f8e8d, 0x04030201, /* shl 11 (16 - 5)/shr5 */ 0x89888786, 0x8d8c8b8a, 0x01008f8e, 0x05040302, /* shl 10 (16 - 6)/shr6 */ 0x8a898887, 0x8e8d8c8b, 0x0201008f, 0x06050403, /* shl 9 (16 - 7)/shr7 */ 0x8b8a8988, 0x8f8e8d8c, 0x03020100, 0x07060504, /* shl 8 (16 - 8)/shr8 */ 0x8c8b8a89, 0x008f8e8d, 0x04030201, 0x08070605, /* shl 7 (16 - 9)/shr9 */ 0x8d8c8b8a, 0x01008f8e, 0x05040302, 0x09080706, /* shl 6 (16 -10)/shr10*/ 0x8e8d8c8b, 0x0201008f, 0x06050403, 0x0a090807, /* shl 5 (16 -11)/shr11*/ 0x8f8e8d8c, 0x03020100, 0x07060504, 0x0b0a0908, /* shl 4 (16 -12)/shr12*/ 0x008f8e8d, 0x04030201, 0x08070605, 0x0c0b0a09, /* shl 3 (16 -13)/shr13*/ 0x01008f8e, 0x05040302, 0x09080706, 0x0d0c0b0a, /* shl 2 (16 -14)/shr14*/ 0x0201008f, 0x06050403, 0x0a090807, 0x0e0d0c0b /* shl 1 (16 -15)/shr15*/ }; extern const uint32_t md5_constants_sse[160]; ALIGN_TO(16, const uint32_t md5_constants_sse[160]) = { // F 0xd76aa478L, 0xe8c7b756L, 0xd76aa478L, 0xe8c7b756L, 0x242070dbL, 0xc1bdceeeL, 0x242070dbL, 0xc1bdceeeL, 0xf57c0fafL, 0x4787c62aL, 0xf57c0fafL, 0x4787c62aL, 0xa8304613L, 0xfd469501L, 0xa8304613L, 0xfd469501L, 0x698098d8L, 0x8b44f7afL, 0x698098d8L, 0x8b44f7afL, 0xffff5bb1L, 0x895cd7beL, 0xffff5bb1L, 0x895cd7beL, 0x6b901122L, 0xfd987193L, 0x6b901122L, 0xfd987193L, 0xa679438eL, 0x49b40821L, 0xa679438eL, 0x49b40821L, // G (sequenced: 3,0,1,2) 0xe9b6c7aaL, 0xf61e2562L, 0xe9b6c7aaL, 0xf61e2562L, 0xc040b340L, 0x265e5a51L, 0xc040b340L, 0x265e5a51L, 0xe7d3fbc8L, 0xd62f105dL, 0xe7d3fbc8L, 0xd62f105dL, 0x02441453L, 0xd8a1e681L, 0x02441453L, 0xd8a1e681L, 0x455a14edL, 0x21e1cde6L, 0x455a14edL, 0x21e1cde6L, 0xc33707d6L, 0xf4d50d87L, 0xc33707d6L, 0xf4d50d87L, 0x8d2a4c8aL, 0xa9e3e905L, 0x8d2a4c8aL, 0xa9e3e905L, 0xfcefa3f8L, 0x676f02d9L, 0xfcefa3f8L, 0x676f02d9L, // H (sequenced: 1,0,3,2) 0x8771f681L, 0xfffa3942L, 0x8771f681L, 0xfffa3942L, 0xfde5380cL, 0x6d9d6122L, 0xfde5380cL, 0x6d9d6122L, 0x4bdecfa9L, 0xa4beea44L, 0x4bdecfa9L, 0xa4beea44L, 0xbebfbc70L, 0xf6bb4b60L, 0xbebfbc70L, 0xf6bb4b60L, 0xeaa127faL, 0x289b7ec6L, 0xeaa127faL, 0x289b7ec6L, 0x04881d05L, 0xd4ef3085L, 0x04881d05L, 0xd4ef3085L, 0xe6db99e5L, 0xd9d4d039L, 0xe6db99e5L, 0xd9d4d039L, 0xc4ac5665L, 0x1fa27cf8L, 0xc4ac5665L, 0x1fa27cf8L, // I 0xf4292244L, 0x432aff97L, 0xf4292244L, 0x432aff97L, 0xab9423a7L, 0xfc93a039L, 0xab9423a7L, 0xfc93a039L, 0x655b59c3L, 0x8f0ccc92L, 0x655b59c3L, 0x8f0ccc92L, 0xffeff47dL, 0x85845dd1L, 0xffeff47dL, 0x85845dd1L, 0x6fa87e4fL, 0xfe2ce6e0L, 0x6fa87e4fL, 0xfe2ce6e0L, 0xa3014314L, 0x4e0811a1L, 0xa3014314L, 0x4e0811a1L, 0xf7537e82L, 0xbd3af235L, 0xf7537e82L, 0xbd3af235L, 0x2ad7d2bbL, 0xeb86d391L, 0x2ad7d2bbL, 0xeb86d391L, // I-1 (used for AVX variant) 0xf4292243L, 0x432aff96L, 0xf4292243L, 0x432aff96L, 0xab9423a6L, 0xfc93a038L, 0xab9423a6L, 0xfc93a038L, 0x655b59c2L, 0x8f0ccc91L, 0x655b59c2L, 0x8f0ccc91L, 0xffeff47cL, 0x85845dd0L, 0xffeff47cL, 0x85845dd0L, 0x6fa87e4eL, 0xfe2ce6dfL, 0x6fa87e4eL, 0xfe2ce6dfL, 0xa3014313L, 0x4e0811a0L, 0xa3014313L, 0x4e0811a0L, 0xf7537e81L, 0xbd3af234L, 0xf7537e81L, 0xbd3af234L, 0x2ad7d2baL, 0xeb86d390L, 0x2ad7d2baL, 0xeb86d390L }; #endif #ifdef PLATFORM_ARM extern const uint32_t md5_constants_arm[64]; ALIGN_TO(16, const uint32_t md5_constants_arm[64]) = { // F 0xd76aa478L, 0xe8c7b756L, 0x242070dbL, 0xc1bdceeeL, 0xf57c0fafL, 0x4787c62aL, 0xa8304613L, 0xfd469501L, 0x698098d8L, 0x8b44f7afL, 0xffff5bb1L, 0x895cd7beL, 0x6b901122L, 0xfd987193L, 0xa679438eL, 0x49b40821L, // G (sequenced: 3,0,1,2) 0xe9b6c7aaL, 0xf61e2562L, 0xc040b340L, 0x265e5a51L, 0xe7d3fbc8L, 0xd62f105dL, 0x02441453L, 0xd8a1e681L, 0x455a14edL, 0x21e1cde6L, 0xc33707d6L, 0xf4d50d87L, 0x8d2a4c8aL, 0xa9e3e905L, 0xfcefa3f8L, 0x676f02d9L, // H 0xfffa3942L, 0x8771f681L, 0x6d9d6122L, 0xfde5380cL, 0xa4beea44L, 0x4bdecfa9L, 0xf6bb4b60L, 0xbebfbc70L, 0x289b7ec6L, 0xeaa127faL, 0xd4ef3085L, 0x04881d05L, 0xd9d4d039L, 0xe6db99e5L, 0x1fa27cf8L, 0xc4ac5665L, // I-1 0xf4292243L, 0x432aff96L, 0xab9423a6L, 0xfc93a038L, 0x655b59c2L, 0x8f0ccc91L, 0xffeff47cL, 0x85845dd0L, 0x6fa87e4eL, 0xfe2ce6dfL, 0xa3014313L, 0x4e0811a0L, 0xf7537e81L, 0xbd3af234L, 0x2ad7d2baL, 0xeb86d390L }; #endifpar2cmdline-turbo-1.4.0/parpar/src/000077500000000000000000000000001514221355600171255ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/parpar/src/cpuid.h000066400000000000000000000164601514221355600204110ustar00rootroot00000000000000#ifndef PP_CPUID_H #define PP_CPUID_H #include "platform.h" #ifdef PLATFORM_X86 # ifdef _MSC_VER #include #define _cpuid __cpuid #define _cpuidX __cpuidex #if _MSC_VER >= 1600 #include #define _GET_XCR() _xgetbv(_XCR_XFEATURE_ENABLED_MASK) #endif # else #include /* GCC seems to support this, I assume everyone else does too? */ #define _cpuid(ar, eax) __cpuid(eax, (ar)[0], (ar)[1], (ar)[2], (ar)[3]) #define _cpuidX(ar, eax, ecx) __cpuid_count(eax, ecx, (ar)[0], (ar)[1], (ar)[2], (ar)[3]) static inline int _GET_XCR() { int xcr0; __asm__ __volatile__("xgetbv" : "=a" (xcr0) : "c" (0) : "%edx"); return xcr0; } # endif // Atom Core detection // Bonnell/Silvermont # define CPU_MODEL_IS_BNL_SLM(model) (model == 0x1C || model == 0x26 || model == 0x27 || model == 0x35 || model == 0x36 || model == 0x37 || model == 0x4A || model == 0x4C || model == 0x4D || model == 0x5A || model == 0x5D) // Goldmont / 0x7A is Goldmont Plus # define CPU_MODEL_IS_GLM(model) ((model == 0x5C || model == 0x5F) || (model == 0x7A)) // Tremont # define CPU_MODEL_IS_TMT(model) (model == 0x86 || model == 0x96 || model == 0x9C) // Sandy Bridge to Cannonlake # define CPU_MODEL_IS_SNB_CNL(model) ( \ (model == 0x2A || model == 0x2D) /*Sandy Bridge*/ \ || (model == 0x3A || model == 0x3E) /*Ivy Bridge*/ \ || (model == 0x3C || model == 0x3F || model == 0x45 || model == 0x46) /*Haswell*/ \ || (model == 0x3D || model == 0x47 || model == 0x4F || model == 0x56) /*Broadwell*/ \ || (model == 0x4E || model == 0x5E || model == 0x8E || model == 0x9E || model == 0xA5 || model == 0xA6) /*Skylake*/ \ || (model == 0x55) /*Skylake-X/Cascadelake/Cooper*/ \ || (model == 0x66) /*Cannonlake*/ \ || (model == 0x67) /*Skylake/Cannonlake?*/ \ ) // AMD Fam 14h (Bobcat) and 16h (Jaguar/Puma) # define CPU_FAMMDL_IS_AMDCAT(family, model) ((family == 0x5f && (model == 0 || model == 1 || model == 2)) || (family == 0x6f && (model == 0 || model == 0x10 || model == 0x20 || model == 0x30))) #endif #ifdef PLATFORM_ARM # ifdef __ANDROID__ // TODO: may be better to prefer auxv as it's supported # include # elif defined(_WIN32) # define WIN32_LEAN_AND_MEAN # ifndef NOMINMAX # define NOMINMAX # endif # include # elif defined(__APPLE__) # include # include # elif defined(__has_include) # if __has_include() # include # if defined(__FreeBSD__) || defined(__OpenBSD__) static unsigned long getauxval(unsigned long cap) { unsigned long ret; elf_aux_info(cap, &ret, sizeof(ret)); return ret; } # endif # if __has_include() # include # endif # endif # endif # ifdef PARPAR_SKIP_AUX_CHECK # define CPU_HAS_NEON true # define CPU_HAS_ARMCRC true # define CPU_HAS_NEON_SHA3 true # define CPU_HAS_SVE true # define CPU_HAS_SVE2 true # else # define CPU_HAS_NEON false # define CPU_HAS_ARMCRC false # define CPU_HAS_NEON_SHA3 false # define CPU_HAS_SVE false # define CPU_HAS_SVE2 false # if defined(AT_HWCAP) # undef CPU_HAS_NEON # ifdef __aarch64__ # define CPU_HAS_NEON (getauxval(AT_HWCAP) & HWCAP_ASIMD) # if defined(HWCAP_SHA3) # undef CPU_HAS_NEON_SHA3 # define CPU_HAS_NEON_SHA3 (getauxval(AT_HWCAP) & HWCAP_SHA3) # endif # if defined(HWCAP_SVE) # undef CPU_HAS_SVE # define CPU_HAS_SVE (getauxval(AT_HWCAP) & HWCAP_SVE) # endif # if defined(AT_HWCAP2) && defined(HWCAP2_SVE2) # undef CPU_HAS_SVE2 # define CPU_HAS_SVE2 (getauxval(AT_HWCAP2) & HWCAP2_SVE2) # endif # else # define CPU_HAS_NEON (getauxval(AT_HWCAP) & HWCAP_NEON) # endif # if defined(AT_HWCAP2) && defined(HWCAP2_CRC32) # undef CPU_HAS_ARMCRC # define CPU_HAS_ARMCRC (getauxval(AT_HWCAP2) & HWCAP2_CRC32) # elif defined(HWCAP_CRC32) # undef CPU_HAS_ARMCRC # define CPU_HAS_ARMCRC (getauxval(AT_HWCAP) & HWCAP_CRC32) # endif # elif defined(ANDROID_CPU_FAMILY_ARM) # undef CPU_HAS_NEON # undef CPU_HAS_ARMCRC # ifdef __aarch64__ # define CPU_HAS_NEON (android_getCpuFeatures() & ANDROID_CPU_ARM64_FEATURE_ASIMD) # define CPU_HAS_ARMCRC (android_getCpuFeatures() & ANDROID_CPU_ARM64_FEATURE_CRC32) # else # define CPU_HAS_NEON (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) # define CPU_HAS_ARMCRC (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_CRC32) # endif # elif defined(_WIN32) # undef CPU_HAS_NEON # define CPU_HAS_NEON (IsProcessorFeaturePresent(PF_ARM_NEON_INSTRUCTIONS_AVAILABLE)) # undef CPU_HAS_ARMCRC # define CPU_HAS_ARMCRC (IsProcessorFeaturePresent(PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE)) # elif defined(__APPLE__) # undef CPU_HAS_NEON # define CPU_HAS_NEON (cpuHasFeature("hw.optional.neon")) # undef CPU_HAS_ARMCRC # define CPU_HAS_ARMCRC (cpuHasFeature("hw.optional.armv8_crc32")) # undef CPU_HAS_NEON_SHA3 # define CPU_HAS_NEON_SHA3 (cpuHasFeature("hw.optional.armv8_2_sha3") || cpuHasFeature("hw.optional.arm.FEAT_SHA3")) static inline bool cpuHasFeature(const char* feature) { int supported = 0; size_t len = sizeof(supported); if(sysctlbyname(feature, &supported, &len, NULL, 0) == 0) return (bool)supported; return false; } # endif # endif #endif #ifdef __riscv # if defined(__has_include) # if __has_include() # include # if defined(__FreeBSD__) || defined(__OpenBSD__) static unsigned long getauxval(unsigned long cap) { unsigned long ret; elf_aux_info(cap, &ret, sizeof(ret)); return ret; } # endif # if __has_include() # include # endif # endif # if __has_include() # include # include # include # endif # endif # ifdef PARPAR_SKIP_AUX_CHECK # define CPU_HAS_GC true # define CPU_HAS_VECTOR true # define CPU_HAS_Zvbc true # define CPU_HAS_Zbkc true # else # define CPU_HAS_GC false # define CPU_HAS_VECTOR false # define CPU_HAS_Zvbc false # define CPU_HAS_Zbkc false # if defined(AT_HWCAP) # undef CPU_HAS_GC # define CPU_HAS_GC ((getauxval(AT_HWCAP) & 4397) == 4397) // 4397 = IMAFDC; TODO: how to detect Z* features of 'G'? # undef CPU_HAS_VECTOR # define CPU_HAS_VECTOR (getauxval(AT_HWCAP) & (1 << ('V'-'A'))) # endif # ifdef RISCV_HWPROBE_KEY_IMA_EXT_0 # undef CPU_HAS_Zvbc # undef CPU_HAS_Zbkc static uint64_t pp_hwprobe(uint64_t k) { struct riscv_hwprobe p; p.key = k; if(syscall(__NR_riscv_hwprobe, &p, 1, 0, NULL, 0)) return 0; return p.value; } // Linux RISC-V extension constants: https://github.com/torvalds/linux/blob/master/arch/riscv/include/uapi/asm/hwprobe.h # define CPU_HAS_Zvbc (pp_hwprobe(RISCV_HWPROBE_KEY_IMA_EXT_0) & (1 << 18) /*RISCV_HWPROBE_EXT_ZVBC*/) # define CPU_HAS_Zbkc (pp_hwprobe(RISCV_HWPROBE_KEY_IMA_EXT_0) & ((1 << 7) /*RISCV_HWPROBE_EXT_ZBC*/ | (1 << 9) /*RISCV_HWPROBE_EXT_ZBKC*/)) # else # ifdef RISCV_ISA_EXT_ZVBC // TODO: RISCV_ISA_EXT_ZVBC is defined as 57 -> this doesn't work on RV32? [ref https://github.com/torvalds/linux/blob/master/arch/riscv/include/asm/hwcap.h] # undef CPU_HAS_Zvbc # define CPU_HAS_Zvbc (getauxval(AT_HWCAP) & (1 << RISCV_ISA_EXT_ZVBC)) # endif # if defined(RISCV_ISA_EXT_ZBC) && defined(RISCV_ISA_EXT_ZBKC) # undef CPU_HAS_Zbkc # define CPU_HAS_Zbkc (getauxval(AT_HWCAP) & ((1 << RISCV_ISA_EXT_ZBC) | (1 << RISCV_ISA_EXT_ZBKC))) # endif # endif # endif #endif #endif /* PP_CPUID_H */ par2cmdline-turbo-1.4.0/parpar/src/hedley.h000066400000000000000000002271451514221355600205630ustar00rootroot00000000000000/* Hedley - https://nemequ.github.io/hedley * Created by Evan Nemerson * * To the extent possible under law, the author(s) have dedicated all * copyright and related and neighboring rights to this software to * the public domain worldwide. This software is distributed without * any warranty. * * For details, see . * SPDX-License-Identifier: CC0-1.0 */ #if !defined(HEDLEY_VERSION) || (HEDLEY_VERSION < 15) #if defined(HEDLEY_VERSION) # undef HEDLEY_VERSION #endif #define HEDLEY_VERSION 15 #if defined(HEDLEY_STRINGIFY_EX) # undef HEDLEY_STRINGIFY_EX #endif #define HEDLEY_STRINGIFY_EX(x) #x #if defined(HEDLEY_STRINGIFY) # undef HEDLEY_STRINGIFY #endif #define HEDLEY_STRINGIFY(x) HEDLEY_STRINGIFY_EX(x) #if defined(HEDLEY_CONCAT_EX) # undef HEDLEY_CONCAT_EX #endif #define HEDLEY_CONCAT_EX(a,b) a##b #if defined(HEDLEY_CONCAT) # undef HEDLEY_CONCAT #endif #define HEDLEY_CONCAT(a,b) HEDLEY_CONCAT_EX(a,b) #if defined(HEDLEY_CONCAT3_EX) # undef HEDLEY_CONCAT3_EX #endif #define HEDLEY_CONCAT3_EX(a,b,c) a##b##c #if defined(HEDLEY_CONCAT3) # undef HEDLEY_CONCAT3 #endif #define HEDLEY_CONCAT3(a,b,c) HEDLEY_CONCAT3_EX(a,b,c) #if defined(HEDLEY_VERSION_ENCODE) # undef HEDLEY_VERSION_ENCODE #endif #define HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) #if defined(HEDLEY_VERSION_DECODE_MAJOR) # undef HEDLEY_VERSION_DECODE_MAJOR #endif #define HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) #if defined(HEDLEY_VERSION_DECODE_MINOR) # undef HEDLEY_VERSION_DECODE_MINOR #endif #define HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) #if defined(HEDLEY_VERSION_DECODE_REVISION) # undef HEDLEY_VERSION_DECODE_REVISION #endif #define HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) #if defined(HEDLEY_GNUC_VERSION) # undef HEDLEY_GNUC_VERSION #endif #if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) # define HEDLEY_GNUC_VERSION HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #elif defined(__GNUC__) # define HEDLEY_GNUC_VERSION HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) #endif #if defined(HEDLEY_GNUC_VERSION_CHECK) # undef HEDLEY_GNUC_VERSION_CHECK #endif #if defined(HEDLEY_GNUC_VERSION) # define HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (HEDLEY_GNUC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_MSVC_VERSION) # undef HEDLEY_MSVC_VERSION #endif #if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) # define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) #elif defined(_MSC_FULL_VER) && !defined(__ICL) # define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) #elif defined(_MSC_VER) && !defined(__ICL) # define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) #endif #if defined(HEDLEY_MSVC_VERSION_CHECK) # undef HEDLEY_MSVC_VERSION_CHECK #endif #if !defined(HEDLEY_MSVC_VERSION) # define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) #elif defined(_MSC_VER) && (_MSC_VER >= 1400) # define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) #elif defined(_MSC_VER) && (_MSC_VER >= 1200) # define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) #else # define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) #endif #if defined(HEDLEY_INTEL_VERSION) # undef HEDLEY_INTEL_VERSION #endif #if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) # define HEDLEY_INTEL_VERSION HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) #elif defined(__INTEL_COMPILER) && !defined(__ICL) # define HEDLEY_INTEL_VERSION HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) #endif #if defined(HEDLEY_INTEL_VERSION_CHECK) # undef HEDLEY_INTEL_VERSION_CHECK #endif #if defined(HEDLEY_INTEL_VERSION) # define HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (HEDLEY_INTEL_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_INTEL_CL_VERSION) # undef HEDLEY_INTEL_CL_VERSION #endif #if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) # define HEDLEY_INTEL_CL_VERSION HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) #endif #if defined(HEDLEY_INTEL_CL_VERSION_CHECK) # undef HEDLEY_INTEL_CL_VERSION_CHECK #endif #if defined(HEDLEY_INTEL_CL_VERSION) # define HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (HEDLEY_INTEL_CL_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_PGI_VERSION) # undef HEDLEY_PGI_VERSION #endif #if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) # define HEDLEY_PGI_VERSION HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) #endif #if defined(HEDLEY_PGI_VERSION_CHECK) # undef HEDLEY_PGI_VERSION_CHECK #endif #if defined(HEDLEY_PGI_VERSION) # define HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (HEDLEY_PGI_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_SUNPRO_VERSION) # undef HEDLEY_SUNPRO_VERSION #endif #if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) # define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) #elif defined(__SUNPRO_C) # define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) #elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) # define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) #elif defined(__SUNPRO_CC) # define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) #endif #if defined(HEDLEY_SUNPRO_VERSION_CHECK) # undef HEDLEY_SUNPRO_VERSION_CHECK #endif #if defined(HEDLEY_SUNPRO_VERSION) # define HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (HEDLEY_SUNPRO_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_EMSCRIPTEN_VERSION) # undef HEDLEY_EMSCRIPTEN_VERSION #endif #if defined(__EMSCRIPTEN__) # define HEDLEY_EMSCRIPTEN_VERSION HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) #endif #if defined(HEDLEY_EMSCRIPTEN_VERSION_CHECK) # undef HEDLEY_EMSCRIPTEN_VERSION_CHECK #endif #if defined(HEDLEY_EMSCRIPTEN_VERSION) # define HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (HEDLEY_EMSCRIPTEN_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_ARM_VERSION) # undef HEDLEY_ARM_VERSION #endif #if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) # define HEDLEY_ARM_VERSION HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) #elif defined(__CC_ARM) && defined(__ARMCC_VERSION) # define HEDLEY_ARM_VERSION HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) #endif #if defined(HEDLEY_ARM_VERSION_CHECK) # undef HEDLEY_ARM_VERSION_CHECK #endif #if defined(HEDLEY_ARM_VERSION) # define HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (HEDLEY_ARM_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_IBM_VERSION) # undef HEDLEY_IBM_VERSION #endif #if defined(__ibmxl__) # define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) #elif defined(__xlC__) && defined(__xlC_ver__) # define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) #elif defined(__xlC__) # define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) #endif #if defined(HEDLEY_IBM_VERSION_CHECK) # undef HEDLEY_IBM_VERSION_CHECK #endif #if defined(HEDLEY_IBM_VERSION) # define HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (HEDLEY_IBM_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_TI_VERSION) # undef HEDLEY_TI_VERSION #endif #if \ defined(__TI_COMPILER_VERSION__) && \ ( \ defined(__TMS470__) || defined(__TI_ARM__) || \ defined(__MSP430__) || \ defined(__TMS320C2000__) \ ) # if (__TI_COMPILER_VERSION__ >= 16000000) # define HEDLEY_TI_VERSION HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) # endif #endif #if defined(HEDLEY_TI_VERSION_CHECK) # undef HEDLEY_TI_VERSION_CHECK #endif #if defined(HEDLEY_TI_VERSION) # define HEDLEY_TI_VERSION_CHECK(major,minor,patch) (HEDLEY_TI_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_TI_CL2000_VERSION) # undef HEDLEY_TI_CL2000_VERSION #endif #if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) # define HEDLEY_TI_CL2000_VERSION HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #if defined(HEDLEY_TI_CL2000_VERSION_CHECK) # undef HEDLEY_TI_CL2000_VERSION_CHECK #endif #if defined(HEDLEY_TI_CL2000_VERSION) # define HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (HEDLEY_TI_CL2000_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_TI_CL430_VERSION) # undef HEDLEY_TI_CL430_VERSION #endif #if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) # define HEDLEY_TI_CL430_VERSION HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #if defined(HEDLEY_TI_CL430_VERSION_CHECK) # undef HEDLEY_TI_CL430_VERSION_CHECK #endif #if defined(HEDLEY_TI_CL430_VERSION) # define HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (HEDLEY_TI_CL430_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_TI_ARMCL_VERSION) # undef HEDLEY_TI_ARMCL_VERSION #endif #if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) # define HEDLEY_TI_ARMCL_VERSION HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #if defined(HEDLEY_TI_ARMCL_VERSION_CHECK) # undef HEDLEY_TI_ARMCL_VERSION_CHECK #endif #if defined(HEDLEY_TI_ARMCL_VERSION) # define HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (HEDLEY_TI_ARMCL_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_TI_CL6X_VERSION) # undef HEDLEY_TI_CL6X_VERSION #endif #if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) # define HEDLEY_TI_CL6X_VERSION HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #if defined(HEDLEY_TI_CL6X_VERSION_CHECK) # undef HEDLEY_TI_CL6X_VERSION_CHECK #endif #if defined(HEDLEY_TI_CL6X_VERSION) # define HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (HEDLEY_TI_CL6X_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_TI_CL7X_VERSION) # undef HEDLEY_TI_CL7X_VERSION #endif #if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) # define HEDLEY_TI_CL7X_VERSION HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #if defined(HEDLEY_TI_CL7X_VERSION_CHECK) # undef HEDLEY_TI_CL7X_VERSION_CHECK #endif #if defined(HEDLEY_TI_CL7X_VERSION) # define HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (HEDLEY_TI_CL7X_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_TI_CLPRU_VERSION) # undef HEDLEY_TI_CLPRU_VERSION #endif #if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) # define HEDLEY_TI_CLPRU_VERSION HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) #endif #if defined(HEDLEY_TI_CLPRU_VERSION_CHECK) # undef HEDLEY_TI_CLPRU_VERSION_CHECK #endif #if defined(HEDLEY_TI_CLPRU_VERSION) # define HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (HEDLEY_TI_CLPRU_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_CRAY_VERSION) # undef HEDLEY_CRAY_VERSION #endif #if defined(_CRAYC) # if defined(_RELEASE_PATCHLEVEL) # define HEDLEY_CRAY_VERSION HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) # else # define HEDLEY_CRAY_VERSION HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) # endif #endif #if defined(HEDLEY_CRAY_VERSION_CHECK) # undef HEDLEY_CRAY_VERSION_CHECK #endif #if defined(HEDLEY_CRAY_VERSION) # define HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (HEDLEY_CRAY_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_IAR_VERSION) # undef HEDLEY_IAR_VERSION #endif #if defined(__IAR_SYSTEMS_ICC__) # if __VER__ > 1000 # define HEDLEY_IAR_VERSION HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) # else # define HEDLEY_IAR_VERSION HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) # endif #endif #if defined(HEDLEY_IAR_VERSION_CHECK) # undef HEDLEY_IAR_VERSION_CHECK #endif #if defined(HEDLEY_IAR_VERSION) # define HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (HEDLEY_IAR_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_TINYC_VERSION) # undef HEDLEY_TINYC_VERSION #endif #if defined(__TINYC__) # define HEDLEY_TINYC_VERSION HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) #endif #if defined(HEDLEY_TINYC_VERSION_CHECK) # undef HEDLEY_TINYC_VERSION_CHECK #endif #if defined(HEDLEY_TINYC_VERSION) # define HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (HEDLEY_TINYC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_DMC_VERSION) # undef HEDLEY_DMC_VERSION #endif #if defined(__DMC__) # define HEDLEY_DMC_VERSION HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) #endif #if defined(HEDLEY_DMC_VERSION_CHECK) # undef HEDLEY_DMC_VERSION_CHECK #endif #if defined(HEDLEY_DMC_VERSION) # define HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (HEDLEY_DMC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_COMPCERT_VERSION) # undef HEDLEY_COMPCERT_VERSION #endif #if defined(__COMPCERT_VERSION__) # define HEDLEY_COMPCERT_VERSION HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) #endif #if defined(HEDLEY_COMPCERT_VERSION_CHECK) # undef HEDLEY_COMPCERT_VERSION_CHECK #endif #if defined(HEDLEY_COMPCERT_VERSION) # define HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (HEDLEY_COMPCERT_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_PELLES_VERSION) # undef HEDLEY_PELLES_VERSION #endif #if defined(__POCC__) # define HEDLEY_PELLES_VERSION HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) #endif #if defined(HEDLEY_PELLES_VERSION_CHECK) # undef HEDLEY_PELLES_VERSION_CHECK #endif #if defined(HEDLEY_PELLES_VERSION) # define HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (HEDLEY_PELLES_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_MCST_LCC_VERSION) # undef HEDLEY_MCST_LCC_VERSION #endif #if defined(__LCC__) && defined(__LCC_MINOR__) # define HEDLEY_MCST_LCC_VERSION HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) #endif #if defined(HEDLEY_MCST_LCC_VERSION_CHECK) # undef HEDLEY_MCST_LCC_VERSION_CHECK #endif #if defined(HEDLEY_MCST_LCC_VERSION) # define HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (HEDLEY_MCST_LCC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_GCC_VERSION) # undef HEDLEY_GCC_VERSION #endif #if \ defined(HEDLEY_GNUC_VERSION) && \ !defined(__clang__) && \ !defined(HEDLEY_INTEL_VERSION) && \ !defined(HEDLEY_PGI_VERSION) && \ !defined(HEDLEY_ARM_VERSION) && \ !defined(HEDLEY_CRAY_VERSION) && \ !defined(HEDLEY_TI_VERSION) && \ !defined(HEDLEY_TI_ARMCL_VERSION) && \ !defined(HEDLEY_TI_CL430_VERSION) && \ !defined(HEDLEY_TI_CL2000_VERSION) && \ !defined(HEDLEY_TI_CL6X_VERSION) && \ !defined(HEDLEY_TI_CL7X_VERSION) && \ !defined(HEDLEY_TI_CLPRU_VERSION) && \ !defined(__COMPCERT__) && \ !defined(HEDLEY_MCST_LCC_VERSION) # define HEDLEY_GCC_VERSION HEDLEY_GNUC_VERSION #endif #if defined(HEDLEY_GCC_VERSION_CHECK) # undef HEDLEY_GCC_VERSION_CHECK #endif #if defined(HEDLEY_GCC_VERSION) # define HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (HEDLEY_GCC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch)) #else # define HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) #endif #if defined(HEDLEY_HAS_ATTRIBUTE) # undef HEDLEY_HAS_ATTRIBUTE #endif #if \ defined(__has_attribute) && \ ( \ (!defined(HEDLEY_IAR_VERSION) || HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ ) # define HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) #else # define HEDLEY_HAS_ATTRIBUTE(attribute) (0) #endif #if defined(HEDLEY_GNUC_HAS_ATTRIBUTE) # undef HEDLEY_GNUC_HAS_ATTRIBUTE #endif #if defined(__has_attribute) # define HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_HAS_ATTRIBUTE(attribute) #else # define HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_ATTRIBUTE) # undef HEDLEY_GCC_HAS_ATTRIBUTE #endif #if defined(__has_attribute) # define HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_HAS_ATTRIBUTE(attribute) #else # define HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_HAS_CPP_ATTRIBUTE) # undef HEDLEY_HAS_CPP_ATTRIBUTE #endif #if \ defined(__has_cpp_attribute) && \ defined(__cplusplus) && \ (!defined(HEDLEY_SUNPRO_VERSION) || HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) # define HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) #else # define HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) #endif #if defined(HEDLEY_HAS_CPP_ATTRIBUTE_NS) # undef HEDLEY_HAS_CPP_ATTRIBUTE_NS #endif #if !defined(__cplusplus) || !defined(__has_cpp_attribute) # define HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) #elif \ !defined(HEDLEY_PGI_VERSION) && \ !defined(HEDLEY_IAR_VERSION) && \ (!defined(HEDLEY_SUNPRO_VERSION) || HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ (!defined(HEDLEY_MSVC_VERSION) || HEDLEY_MSVC_VERSION_CHECK(19,20,0)) # define HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) #else # define HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) #endif #if defined(HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) # undef HEDLEY_GNUC_HAS_CPP_ATTRIBUTE #endif #if defined(__has_cpp_attribute) && defined(__cplusplus) # define HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) #else # define HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_CPP_ATTRIBUTE) # undef HEDLEY_GCC_HAS_CPP_ATTRIBUTE #endif #if defined(__has_cpp_attribute) && defined(__cplusplus) # define HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) #else # define HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_HAS_BUILTIN) # undef HEDLEY_HAS_BUILTIN #endif #if defined(__has_builtin) # define HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) #else # define HEDLEY_HAS_BUILTIN(builtin) (0) #endif #if defined(HEDLEY_GNUC_HAS_BUILTIN) # undef HEDLEY_GNUC_HAS_BUILTIN #endif #if defined(__has_builtin) # define HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) #else # define HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_BUILTIN) # undef HEDLEY_GCC_HAS_BUILTIN #endif #if defined(__has_builtin) # define HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) #else # define HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_HAS_FEATURE) # undef HEDLEY_HAS_FEATURE #endif #if defined(__has_feature) # define HEDLEY_HAS_FEATURE(feature) __has_feature(feature) #else # define HEDLEY_HAS_FEATURE(feature) (0) #endif #if defined(HEDLEY_GNUC_HAS_FEATURE) # undef HEDLEY_GNUC_HAS_FEATURE #endif #if defined(__has_feature) # define HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) #else # define HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_FEATURE) # undef HEDLEY_GCC_HAS_FEATURE #endif #if defined(__has_feature) # define HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) #else # define HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_HAS_EXTENSION) # undef HEDLEY_HAS_EXTENSION #endif #if defined(__has_extension) # define HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) #else # define HEDLEY_HAS_EXTENSION(extension) (0) #endif #if defined(HEDLEY_GNUC_HAS_EXTENSION) # undef HEDLEY_GNUC_HAS_EXTENSION #endif #if defined(__has_extension) # define HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) #else # define HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_EXTENSION) # undef HEDLEY_GCC_HAS_EXTENSION #endif #if defined(__has_extension) # define HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) #else # define HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_HAS_DECLSPEC_ATTRIBUTE) # undef HEDLEY_HAS_DECLSPEC_ATTRIBUTE #endif #if defined(__has_declspec_attribute) # define HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) #else # define HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) #endif #if defined(HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) # undef HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE #endif #if defined(__has_declspec_attribute) # define HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) #else # define HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) # undef HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE #endif #if defined(__has_declspec_attribute) # define HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) #else # define HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_HAS_WARNING) # undef HEDLEY_HAS_WARNING #endif #if defined(__has_warning) # define HEDLEY_HAS_WARNING(warning) __has_warning(warning) #else # define HEDLEY_HAS_WARNING(warning) (0) #endif #if defined(HEDLEY_GNUC_HAS_WARNING) # undef HEDLEY_GNUC_HAS_WARNING #endif #if defined(__has_warning) # define HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) #else # define HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_GCC_HAS_WARNING) # undef HEDLEY_GCC_HAS_WARNING #endif #if defined(__has_warning) # define HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) #else # define HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if \ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ defined(__clang__) || \ HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_TI_VERSION_CHECK(15,12,0) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ (HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) # define HEDLEY_PRAGMA(value) _Pragma(#value) #elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) # define HEDLEY_PRAGMA(value) __pragma(value) #else # define HEDLEY_PRAGMA(value) #endif #if defined(HEDLEY_DIAGNOSTIC_PUSH) # undef HEDLEY_DIAGNOSTIC_PUSH #endif #if defined(HEDLEY_DIAGNOSTIC_POP) # undef HEDLEY_DIAGNOSTIC_POP #endif #if defined(__clang__) # define HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") # define HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") # define HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") #elif HEDLEY_GCC_VERSION_CHECK(4,6,0) # define HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") # define HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") #elif \ HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) # define HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) #elif HEDLEY_ARM_VERSION_CHECK(5,6,0) # define HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") # define HEDLEY_DIAGNOSTIC_POP _Pragma("pop") #elif \ HEDLEY_TI_VERSION_CHECK(15,12,0) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) # define HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") # define HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") #elif HEDLEY_PELLES_VERSION_CHECK(2,90,0) # define HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") # define HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") #else # define HEDLEY_DIAGNOSTIC_PUSH # define HEDLEY_DIAGNOSTIC_POP #endif /* HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ #if defined(HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) # undef HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ #endif #if defined(__cplusplus) # if HEDLEY_HAS_WARNING("-Wc++98-compat") # if HEDLEY_HAS_WARNING("-Wc++17-extensions") # if HEDLEY_HAS_WARNING("-Wc++1z-extensions") # define HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ xpr \ HEDLEY_DIAGNOSTIC_POP # else # define HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ xpr \ HEDLEY_DIAGNOSTIC_POP # endif # else # define HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ xpr \ HEDLEY_DIAGNOSTIC_POP # endif # endif #endif #if !defined(HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) # define HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x #endif #if defined(HEDLEY_CONST_CAST) # undef HEDLEY_CONST_CAST #endif #if defined(__cplusplus) # define HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) #elif \ HEDLEY_HAS_WARNING("-Wcast-qual") || \ HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ HEDLEY_DIAGNOSTIC_PUSH \ HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ ((T) (expr)); \ HEDLEY_DIAGNOSTIC_POP \ })) #else # define HEDLEY_CONST_CAST(T, expr) ((T) (expr)) #endif #if defined(HEDLEY_REINTERPRET_CAST) # undef HEDLEY_REINTERPRET_CAST #endif #if defined(__cplusplus) # define HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) #else # define HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) #endif #if defined(HEDLEY_STATIC_CAST) # undef HEDLEY_STATIC_CAST #endif #if defined(__cplusplus) # define HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) #else # define HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) #endif #if defined(HEDLEY_CPP_CAST) # undef HEDLEY_CPP_CAST #endif #if defined(__cplusplus) # if HEDLEY_HAS_WARNING("-Wold-style-cast") # define HEDLEY_CPP_CAST(T, expr) \ HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ ((T) (expr)) \ HEDLEY_DIAGNOSTIC_POP # elif HEDLEY_IAR_VERSION_CHECK(8,3,0) # define HEDLEY_CPP_CAST(T, expr) \ HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("diag_suppress=Pe137") \ HEDLEY_DIAGNOSTIC_POP # else # define HEDLEY_CPP_CAST(T, expr) ((T) (expr)) # endif #else # define HEDLEY_CPP_CAST(T, expr) (expr) #endif #if defined(HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) # undef HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED #endif #if HEDLEY_HAS_WARNING("-Wdeprecated-declarations") # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") #elif HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) #elif HEDLEY_PGI_VERSION_CHECK(20,7,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") #elif HEDLEY_PGI_VERSION_CHECK(17,10,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") #elif HEDLEY_GCC_VERSION_CHECK(4,3,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") #elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) #elif HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") #elif \ HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") #elif HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") #elif HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") #elif HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") #elif HEDLEY_PELLES_VERSION_CHECK(2,90,0) # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") #else # define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED #endif #if defined(HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) # undef HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS #endif #if HEDLEY_HAS_WARNING("-Wunknown-pragmas") # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") #elif HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) #elif HEDLEY_PGI_VERSION_CHECK(17,10,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") #elif HEDLEY_GCC_VERSION_CHECK(4,3,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") #elif HEDLEY_MSVC_VERSION_CHECK(15,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) #elif \ HEDLEY_TI_VERSION_CHECK(16,9,0) || \ HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") #elif HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") #elif HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") #elif HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") #else # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS #endif #if defined(HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) # undef HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES #endif #if HEDLEY_HAS_WARNING("-Wunknown-attributes") # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") #elif HEDLEY_GCC_VERSION_CHECK(4,6,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") #elif HEDLEY_INTEL_VERSION_CHECK(17,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") #elif HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) #elif HEDLEY_MSVC_VERSION_CHECK(19,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) #elif HEDLEY_PGI_VERSION_CHECK(20,7,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") #elif HEDLEY_PGI_VERSION_CHECK(17,10,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") #elif HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") #elif \ HEDLEY_TI_VERSION_CHECK(18,1,0) || \ HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") #elif HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") #elif HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") #else # define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES #endif #if defined(HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) # undef HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL #endif #if HEDLEY_HAS_WARNING("-Wcast-qual") # define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") #elif HEDLEY_GCC_VERSION_CHECK(3,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") #else # define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL #endif #if defined(HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) # undef HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION #endif #if HEDLEY_HAS_WARNING("-Wunused-function") # define HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") #elif HEDLEY_GCC_VERSION_CHECK(3,4,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") #elif HEDLEY_MSVC_VERSION_CHECK(1,0,0) # define HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) #elif HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") #else # define HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION #endif #if defined(HEDLEY_DEPRECATED) # undef HEDLEY_DEPRECATED #endif #if defined(HEDLEY_DEPRECATED_FOR) # undef HEDLEY_DEPRECATED_FOR #endif #if \ HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) # define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) #elif \ (HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(HEDLEY_IAR_VERSION)) || \ HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ HEDLEY_TI_VERSION_CHECK(18,1,0) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) # define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) #elif defined(__cplusplus) && (__cplusplus >= 201402L) # define HEDLEY_DEPRECATED(since) HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) # define HEDLEY_DEPRECATED_FOR(since, replacement) HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) #elif \ HEDLEY_HAS_ATTRIBUTE(deprecated) || \ HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ HEDLEY_IAR_VERSION_CHECK(8,10,0) # define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) # define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) #elif \ HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_DEPRECATED(since) __declspec(deprecated) # define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) #elif HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_DEPRECATED(since) _Pragma("deprecated") # define HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") #else # define HEDLEY_DEPRECATED(since) # define HEDLEY_DEPRECATED_FOR(since, replacement) #endif #if defined(HEDLEY_UNAVAILABLE) # undef HEDLEY_UNAVAILABLE #endif #if \ HEDLEY_HAS_ATTRIBUTE(warning) || \ HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) #else # define HEDLEY_UNAVAILABLE(available_since) #endif #if defined(HEDLEY_WARN_UNUSED_RESULT) # undef HEDLEY_WARN_UNUSED_RESULT #endif #if defined(HEDLEY_WARN_UNUSED_RESULT_MSG) # undef HEDLEY_WARN_UNUSED_RESULT_MSG #endif #if \ HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ (HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) # define HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) #elif (HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) # define HEDLEY_WARN_UNUSED_RESULT HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) # define HEDLEY_WARN_UNUSED_RESULT_MSG(msg) HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) #elif HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) # define HEDLEY_WARN_UNUSED_RESULT HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) # define HEDLEY_WARN_UNUSED_RESULT_MSG(msg) HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) #elif defined(_Check_return_) /* SAL */ # define HEDLEY_WARN_UNUSED_RESULT _Check_return_ # define HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ #else # define HEDLEY_WARN_UNUSED_RESULT # define HEDLEY_WARN_UNUSED_RESULT_MSG(msg) #endif #if defined(HEDLEY_SENTINEL) # undef HEDLEY_SENTINEL #endif #if \ HEDLEY_HAS_ATTRIBUTE(sentinel) || \ HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) #else # define HEDLEY_SENTINEL(position) #endif #if defined(HEDLEY_NO_RETURN) # undef HEDLEY_NO_RETURN #endif #if HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_NO_RETURN __noreturn #elif \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_NO_RETURN __attribute__((__noreturn__)) #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L # define HEDLEY_NO_RETURN _Noreturn #elif defined(__cplusplus) && (__cplusplus >= 201103L) # define HEDLEY_NO_RETURN HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) #elif \ HEDLEY_HAS_ATTRIBUTE(noreturn) || \ HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ HEDLEY_IAR_VERSION_CHECK(8,10,0) # define HEDLEY_NO_RETURN __attribute__((__noreturn__)) #elif HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) # define HEDLEY_NO_RETURN _Pragma("does_not_return") #elif \ HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_NO_RETURN __declspec(noreturn) #elif HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) # define HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") #elif HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) # define HEDLEY_NO_RETURN __attribute((noreturn)) #elif HEDLEY_PELLES_VERSION_CHECK(9,0,0) # define HEDLEY_NO_RETURN __declspec(noreturn) #else # define HEDLEY_NO_RETURN #endif #if defined(HEDLEY_NO_ESCAPE) # undef HEDLEY_NO_ESCAPE #endif #if HEDLEY_HAS_ATTRIBUTE(noescape) # define HEDLEY_NO_ESCAPE __attribute__((__noescape__)) #else # define HEDLEY_NO_ESCAPE #endif #if defined(HEDLEY_UNREACHABLE) # undef HEDLEY_UNREACHABLE #endif #if defined(HEDLEY_UNREACHABLE_RETURN) # undef HEDLEY_UNREACHABLE_RETURN #endif #if defined(HEDLEY_ASSUME) # undef HEDLEY_ASSUME #endif #if \ HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_ASSUME(expr) __assume(expr) #elif HEDLEY_HAS_BUILTIN(__builtin_assume) # define HEDLEY_ASSUME(expr) __builtin_assume(expr) #elif \ HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) # if defined(__cplusplus) # define HEDLEY_ASSUME(expr) std::_nassert(expr) # else # define HEDLEY_ASSUME(expr) _nassert(expr) # endif #endif #if \ (HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(HEDLEY_ARM_VERSION))) || \ HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_UNREACHABLE() __builtin_unreachable() #elif defined(HEDLEY_ASSUME) # define HEDLEY_UNREACHABLE() HEDLEY_ASSUME(0) #endif #if !defined(HEDLEY_ASSUME) # if defined(HEDLEY_UNREACHABLE) # define HEDLEY_ASSUME(expr) HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (HEDLEY_UNREACHABLE(), 1))) # else # define HEDLEY_ASSUME(expr) HEDLEY_STATIC_CAST(void, expr) # endif #endif #if defined(HEDLEY_UNREACHABLE) # if \ HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) # define HEDLEY_UNREACHABLE_RETURN(value) return (HEDLEY_STATIC_CAST(void, HEDLEY_ASSUME(0)), (value)) # else # define HEDLEY_UNREACHABLE_RETURN(value) HEDLEY_UNREACHABLE() # endif #else # define HEDLEY_UNREACHABLE_RETURN(value) return (value) #endif #if !defined(HEDLEY_UNREACHABLE) # define HEDLEY_UNREACHABLE() HEDLEY_ASSUME(0) #endif HEDLEY_DIAGNOSTIC_PUSH #if HEDLEY_HAS_WARNING("-Wpedantic") # pragma clang diagnostic ignored "-Wpedantic" #endif #if HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) # pragma clang diagnostic ignored "-Wc++98-compat-pedantic" #endif #if HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) # if defined(__clang__) # pragma clang diagnostic ignored "-Wvariadic-macros" # elif defined(HEDLEY_GCC_VERSION) # pragma GCC diagnostic ignored "-Wvariadic-macros" # endif #endif #if defined(HEDLEY_NON_NULL) # undef HEDLEY_NON_NULL #endif #if \ HEDLEY_HAS_ATTRIBUTE(nonnull) || \ HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) # define HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) #else # define HEDLEY_NON_NULL(...) #endif HEDLEY_DIAGNOSTIC_POP #if defined(HEDLEY_PRINTF_FORMAT) # undef HEDLEY_PRINTF_FORMAT #endif #if defined(__MINGW32__) && HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) # define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) #elif defined(__MINGW32__) && HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) # define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) #elif \ HEDLEY_HAS_ATTRIBUTE(format) || \ HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) #elif HEDLEY_PELLES_VERSION_CHECK(6,0,0) # define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) #else # define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) #endif #if defined(HEDLEY_CONSTEXPR) # undef HEDLEY_CONSTEXPR #endif #if defined(__cplusplus) # if __cplusplus >= 201103L # define HEDLEY_CONSTEXPR HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) # endif #endif #if !defined(HEDLEY_CONSTEXPR) # define HEDLEY_CONSTEXPR #endif #if defined(HEDLEY_PREDICT) # undef HEDLEY_PREDICT #endif #if defined(HEDLEY_LIKELY) # undef HEDLEY_LIKELY #endif #if defined(HEDLEY_UNLIKELY) # undef HEDLEY_UNLIKELY #endif #if defined(HEDLEY_UNPREDICTABLE) # undef HEDLEY_UNPREDICTABLE #endif #if HEDLEY_HAS_BUILTIN(__builtin_unpredictable) # define HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) #endif #if \ (HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(HEDLEY_PGI_VERSION)) || \ HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) # define HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) # define HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) # define HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) # define HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) #elif \ (HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(HEDLEY_INTEL_CL_VERSION)) || \ HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ (HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(15,12,0) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_PREDICT(expr, expected, probability) \ (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (HEDLEY_STATIC_CAST(void, expected), (expr))) # define HEDLEY_PREDICT_TRUE(expr, probability) \ (__extension__ ({ \ double hedley_probability_ = (probability); \ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ })) # define HEDLEY_PREDICT_FALSE(expr, probability) \ (__extension__ ({ \ double hedley_probability_ = (probability); \ ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ })) # define HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) # define HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) #else # define HEDLEY_PREDICT(expr, expected, probability) (HEDLEY_STATIC_CAST(void, expected), (expr)) # define HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) # define HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) # define HEDLEY_LIKELY(expr) (!!(expr)) # define HEDLEY_UNLIKELY(expr) (!!(expr)) #endif #if !defined(HEDLEY_UNPREDICTABLE) # define HEDLEY_UNPREDICTABLE(expr) HEDLEY_PREDICT(expr, 1, 0.5) #endif #if defined(HEDLEY_MALLOC) # undef HEDLEY_MALLOC #endif #if \ HEDLEY_HAS_ATTRIBUTE(malloc) || \ HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_MALLOC __attribute__((__malloc__)) #elif HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) # define HEDLEY_MALLOC _Pragma("returns_new_memory") #elif \ HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_MALLOC __declspec(restrict) #else # define HEDLEY_MALLOC #endif #if defined(HEDLEY_PURE) # undef HEDLEY_PURE #endif #if \ HEDLEY_HAS_ATTRIBUTE(pure) || \ HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_PURE __attribute__((__pure__)) #elif HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) # define HEDLEY_PURE _Pragma("does_not_write_global_data") #elif defined(__cplusplus) && \ ( \ HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ ) # define HEDLEY_PURE _Pragma("FUNC_IS_PURE;") #else # define HEDLEY_PURE #endif #if defined(HEDLEY_CONST) # undef HEDLEY_CONST #endif #if \ HEDLEY_HAS_ATTRIBUTE(const) || \ HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_CONST __attribute__((__const__)) #elif \ HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) # define HEDLEY_CONST _Pragma("no_side_effect") #else # define HEDLEY_CONST HEDLEY_PURE #endif #if defined(HEDLEY_RESTRICT) # undef HEDLEY_RESTRICT #endif #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) # define HEDLEY_RESTRICT restrict #elif \ HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ (HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ defined(__clang__) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_RESTRICT __restrict #elif HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) # define HEDLEY_RESTRICT _Restrict #else # define HEDLEY_RESTRICT #endif #if defined(HEDLEY_INLINE) # undef HEDLEY_INLINE #endif #if \ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ (defined(__cplusplus) && (__cplusplus >= 199711L)) # define HEDLEY_INLINE inline #elif \ defined(HEDLEY_GCC_VERSION) || \ HEDLEY_ARM_VERSION_CHECK(6,2,0) # define HEDLEY_INLINE __inline__ #elif \ HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_INLINE __inline #else # define HEDLEY_INLINE #endif #if defined(HEDLEY_ALWAYS_INLINE) # undef HEDLEY_ALWAYS_INLINE #endif #if \ HEDLEY_HAS_ATTRIBUTE(always_inline) || \ HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ HEDLEY_IAR_VERSION_CHECK(8,10,0) # define HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) HEDLEY_INLINE #elif \ HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_ALWAYS_INLINE __forceinline #elif defined(__cplusplus) && \ ( \ HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ ) # define HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") #elif HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") #else # define HEDLEY_ALWAYS_INLINE HEDLEY_INLINE #endif #if defined(HEDLEY_NEVER_INLINE) # undef HEDLEY_NEVER_INLINE #endif #if \ HEDLEY_HAS_ATTRIBUTE(noinline) || \ HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ HEDLEY_TI_VERSION_CHECK(15,12,0) || \ (HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ (HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ (HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ (HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ HEDLEY_IAR_VERSION_CHECK(8,10,0) # define HEDLEY_NEVER_INLINE __attribute__((__noinline__)) #elif \ HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_NEVER_INLINE __declspec(noinline) #elif HEDLEY_PGI_VERSION_CHECK(10,2,0) # define HEDLEY_NEVER_INLINE _Pragma("noinline") #elif HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) # define HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") #elif HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_NEVER_INLINE _Pragma("inline=never") #elif HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) # define HEDLEY_NEVER_INLINE __attribute((noinline)) #elif HEDLEY_PELLES_VERSION_CHECK(9,0,0) # define HEDLEY_NEVER_INLINE __declspec(noinline) #else # define HEDLEY_NEVER_INLINE #endif #if defined(HEDLEY_PRIVATE) # undef HEDLEY_PRIVATE #endif #if defined(HEDLEY_PUBLIC) # undef HEDLEY_PUBLIC #endif #if defined(HEDLEY_IMPORT) # undef HEDLEY_IMPORT #endif #if defined(_WIN32) || defined(__CYGWIN__) # define HEDLEY_PRIVATE # define HEDLEY_PUBLIC __declspec(dllexport) # define HEDLEY_IMPORT __declspec(dllimport) #else # if \ HEDLEY_HAS_ATTRIBUTE(visibility) || \ HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ ( \ defined(__TI_EABI__) && \ ( \ (HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ ) \ ) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) # define HEDLEY_PUBLIC __attribute__((__visibility__("default"))) # else # define HEDLEY_PRIVATE # define HEDLEY_PUBLIC # endif # define HEDLEY_IMPORT extern #endif #if defined(HEDLEY_NO_THROW) # undef HEDLEY_NO_THROW #endif #if \ HEDLEY_HAS_ATTRIBUTE(nothrow) || \ HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_NO_THROW __attribute__((__nothrow__)) #elif \ HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) # define HEDLEY_NO_THROW __declspec(nothrow) #else # define HEDLEY_NO_THROW #endif #if defined(HEDLEY_FALL_THROUGH) # undef HEDLEY_FALL_THROUGH #endif #if \ HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) #elif HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) # define HEDLEY_FALL_THROUGH HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) #elif HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) # define HEDLEY_FALL_THROUGH HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) #elif defined(__fallthrough) /* SAL */ # define HEDLEY_FALL_THROUGH __fallthrough #else # define HEDLEY_FALL_THROUGH #endif #if defined(HEDLEY_RETURNS_NON_NULL) # undef HEDLEY_RETURNS_NON_NULL #endif #if \ HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) #elif defined(_Ret_notnull_) /* SAL */ # define HEDLEY_RETURNS_NON_NULL _Ret_notnull_ #else # define HEDLEY_RETURNS_NON_NULL #endif #if defined(HEDLEY_ARRAY_PARAM) # undef HEDLEY_ARRAY_PARAM #endif #if \ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ !defined(__STDC_NO_VLA__) && \ !defined(__cplusplus) && \ !defined(HEDLEY_PGI_VERSION) && \ !defined(HEDLEY_TINYC_VERSION) # define HEDLEY_ARRAY_PARAM(name) (name) #else # define HEDLEY_ARRAY_PARAM(name) #endif #if defined(HEDLEY_IS_CONSTANT) # undef HEDLEY_IS_CONSTANT #endif #if defined(HEDLEY_REQUIRE_CONSTEXPR) # undef HEDLEY_REQUIRE_CONSTEXPR #endif /* HEDLEY_IS_CONSTEXPR_ is for HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ #if defined(HEDLEY_IS_CONSTEXPR_) # undef HEDLEY_IS_CONSTEXPR_ #endif #if \ HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ (HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) # define HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) #endif #if !defined(__cplusplus) # if \ HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ HEDLEY_TINYC_VERSION_CHECK(0,9,24) # if defined(__INTPTR_TYPE__) # define HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) # else # include # define HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) # endif # elif \ ( \ defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ !defined(HEDLEY_SUNPRO_VERSION) && \ !defined(HEDLEY_PGI_VERSION) && \ !defined(HEDLEY_IAR_VERSION)) || \ (HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(HEDLEY_IAR_VERSION)) || \ HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ HEDLEY_ARM_VERSION_CHECK(5,3,0) # if defined(__INTPTR_TYPE__) # define HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) # else # include # define HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) # endif # elif \ defined(HEDLEY_GCC_VERSION) || \ defined(HEDLEY_INTEL_VERSION) || \ defined(HEDLEY_TINYC_VERSION) || \ defined(HEDLEY_TI_ARMCL_VERSION) || \ HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ defined(HEDLEY_TI_CL2000_VERSION) || \ defined(HEDLEY_TI_CL6X_VERSION) || \ defined(HEDLEY_TI_CL7X_VERSION) || \ defined(HEDLEY_TI_CLPRU_VERSION) || \ defined(__clang__) # define HEDLEY_IS_CONSTEXPR_(expr) ( \ sizeof(void) != \ sizeof(*( \ 1 ? \ ((void*) ((expr) * 0L) ) : \ ((struct { char v[sizeof(void) * 2]; } *) 1) \ ) \ ) \ ) # endif #endif #if defined(HEDLEY_IS_CONSTEXPR_) # if !defined(HEDLEY_IS_CONSTANT) # define HEDLEY_IS_CONSTANT(expr) HEDLEY_IS_CONSTEXPR_(expr) # endif # define HEDLEY_REQUIRE_CONSTEXPR(expr) (HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) #else # if !defined(HEDLEY_IS_CONSTANT) # define HEDLEY_IS_CONSTANT(expr) (0) # endif # define HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) #endif #if defined(HEDLEY_BEGIN_C_DECLS) # undef HEDLEY_BEGIN_C_DECLS #endif #if defined(HEDLEY_END_C_DECLS) # undef HEDLEY_END_C_DECLS #endif #if defined(HEDLEY_C_DECL) # undef HEDLEY_C_DECL #endif #if defined(__cplusplus) # define HEDLEY_BEGIN_C_DECLS extern "C" { # define HEDLEY_END_C_DECLS } # define HEDLEY_C_DECL extern "C" #else # define HEDLEY_BEGIN_C_DECLS # define HEDLEY_END_C_DECLS # define HEDLEY_C_DECL #endif #if defined(HEDLEY_STATIC_ASSERT) # undef HEDLEY_STATIC_ASSERT #endif #if \ !defined(__cplusplus) && ( \ (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ (HEDLEY_HAS_FEATURE(c_static_assert) && !defined(HEDLEY_INTEL_CL_VERSION)) || \ HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ defined(_Static_assert) \ ) # define HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) #elif \ (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_STATIC_ASSERT(expr, message) HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) #else # define HEDLEY_STATIC_ASSERT(expr, message) #endif #if defined(HEDLEY_NULL) # undef HEDLEY_NULL #endif #if defined(__cplusplus) # if __cplusplus >= 201103L # define HEDLEY_NULL HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) # elif defined(NULL) # define HEDLEY_NULL NULL # else # define HEDLEY_NULL HEDLEY_STATIC_CAST(void*, 0) # endif #elif defined(NULL) # define HEDLEY_NULL NULL #else # define HEDLEY_NULL ((void*) 0) #endif #if defined(HEDLEY_MESSAGE) # undef HEDLEY_MESSAGE #endif #if HEDLEY_HAS_WARNING("-Wunknown-pragmas") # define HEDLEY_MESSAGE(msg) \ HEDLEY_DIAGNOSTIC_PUSH \ HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ HEDLEY_PRAGMA(message msg) \ HEDLEY_DIAGNOSTIC_POP #elif \ HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message msg) #elif HEDLEY_CRAY_VERSION_CHECK(5,0,0) # define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(_CRI message msg) #elif HEDLEY_IAR_VERSION_CHECK(8,0,0) # define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message(msg)) #elif HEDLEY_PELLES_VERSION_CHECK(2,0,0) # define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message(msg)) #else # define HEDLEY_MESSAGE(msg) #endif #if defined(HEDLEY_WARNING) # undef HEDLEY_WARNING #endif #if HEDLEY_HAS_WARNING("-Wunknown-pragmas") # define HEDLEY_WARNING(msg) \ HEDLEY_DIAGNOSTIC_PUSH \ HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ HEDLEY_PRAGMA(clang warning msg) \ HEDLEY_DIAGNOSTIC_POP #elif \ HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ HEDLEY_INTEL_VERSION_CHECK(13,0,0) # define HEDLEY_WARNING(msg) HEDLEY_PRAGMA(GCC warning msg) #elif \ HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_WARNING(msg) HEDLEY_PRAGMA(message(msg)) #else # define HEDLEY_WARNING(msg) HEDLEY_MESSAGE(msg) #endif #if defined(HEDLEY_REQUIRE) # undef HEDLEY_REQUIRE #endif #if defined(HEDLEY_REQUIRE_MSG) # undef HEDLEY_REQUIRE_MSG #endif #if HEDLEY_HAS_ATTRIBUTE(diagnose_if) # if HEDLEY_HAS_WARNING("-Wgcc-compat") # define HEDLEY_REQUIRE(expr) \ HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ __attribute__((diagnose_if(!(expr), #expr, "error"))) \ HEDLEY_DIAGNOSTIC_POP # define HEDLEY_REQUIRE_MSG(expr,msg) \ HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ __attribute__((diagnose_if(!(expr), msg, "error"))) \ HEDLEY_DIAGNOSTIC_POP # else # define HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) # define HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) # endif #else # define HEDLEY_REQUIRE(expr) # define HEDLEY_REQUIRE_MSG(expr,msg) #endif #if defined(HEDLEY_FLAGS) # undef HEDLEY_FLAGS #endif #if HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) # define HEDLEY_FLAGS __attribute__((__flag_enum__)) #else # define HEDLEY_FLAGS #endif #if defined(HEDLEY_FLAGS_CAST) # undef HEDLEY_FLAGS_CAST #endif #if HEDLEY_INTEL_VERSION_CHECK(19,0,0) # define HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ HEDLEY_DIAGNOSTIC_PUSH \ _Pragma("warning(disable:188)") \ ((T) (expr)); \ HEDLEY_DIAGNOSTIC_POP \ })) #else # define HEDLEY_FLAGS_CAST(T, expr) HEDLEY_STATIC_CAST(T, expr) #endif #if defined(HEDLEY_EMPTY_BASES) # undef HEDLEY_EMPTY_BASES #endif #if \ (HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) # define HEDLEY_EMPTY_BASES __declspec(empty_bases) #else # define HEDLEY_EMPTY_BASES #endif /* Remaining macros are deprecated. */ #if defined(HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) # undef HEDLEY_GCC_NOT_CLANG_VERSION_CHECK #endif #if defined(__clang__) # define HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) #else # define HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch) #endif #if defined(HEDLEY_CLANG_HAS_ATTRIBUTE) # undef HEDLEY_CLANG_HAS_ATTRIBUTE #endif #define HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) HEDLEY_HAS_ATTRIBUTE(attribute) #if defined(HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) # undef HEDLEY_CLANG_HAS_CPP_ATTRIBUTE #endif #define HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) HEDLEY_HAS_CPP_ATTRIBUTE(attribute) #if defined(HEDLEY_CLANG_HAS_BUILTIN) # undef HEDLEY_CLANG_HAS_BUILTIN #endif #define HEDLEY_CLANG_HAS_BUILTIN(builtin) HEDLEY_HAS_BUILTIN(builtin) #if defined(HEDLEY_CLANG_HAS_FEATURE) # undef HEDLEY_CLANG_HAS_FEATURE #endif #define HEDLEY_CLANG_HAS_FEATURE(feature) HEDLEY_HAS_FEATURE(feature) #if defined(HEDLEY_CLANG_HAS_EXTENSION) # undef HEDLEY_CLANG_HAS_EXTENSION #endif #define HEDLEY_CLANG_HAS_EXTENSION(extension) HEDLEY_HAS_EXTENSION(extension) #if defined(HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) # undef HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE #endif #define HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) #if defined(HEDLEY_CLANG_HAS_WARNING) # undef HEDLEY_CLANG_HAS_WARNING #endif #define HEDLEY_CLANG_HAS_WARNING(warning) HEDLEY_HAS_WARNING(warning) #endif /* !defined(HEDLEY_VERSION) || (HEDLEY_VERSION < X) */ par2cmdline-turbo-1.4.0/parpar/src/platform.h000066400000000000000000000253131514221355600211260ustar00rootroot00000000000000 #ifndef PP_PLATFORM_H #define PP_PLATFORM_H #include "hedley.h" #if defined(__x86_64__) || \ defined(__amd64__ ) || \ defined(__LP64 ) || \ defined(_M_X64 ) || \ defined(_M_AMD64 ) || \ (defined(_WIN64) && !defined(_M_ARM64)) #define PLATFORM_AMD64 1 #endif #if defined(PLATFORM_AMD64) || \ defined(__i386__ ) || \ defined(__i486__ ) || \ defined(__i586__ ) || \ defined(__i686__ ) || \ defined(_M_I86 ) || \ defined(_M_IX86 ) || \ (defined(_WIN32) && !defined(_M_ARM) && !defined(_M_ARM64)) #define PLATFORM_X86 1 #endif #if defined(__aarch64__) || \ defined(__armv7__ ) || \ defined(__arm__ ) || \ defined(_M_ARM64 ) || \ defined(_M_ARM ) || \ defined(__ARM_ARCH_6__ ) || \ defined(__ARM_ARCH_7__ ) || \ defined(__ARM_ARCH_7A__) || \ defined(__ARM_ARCH_8A__) || \ (defined(__ARM_ARCH ) && __ARM_ARCH >= 6) #define PLATFORM_ARM 1 #endif #ifdef _MSC_VER # ifndef __BYTE_ORDER__ # define __BYTE_ORDER__ 1234 # endif # ifndef __ORDER_BIG_ENDIAN__ # define __ORDER_BIG_ENDIAN__ 4321 # endif #endif #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # ifdef __GNUC__ # define _LE16 __builtin_bswap16 # define _LE32 __builtin_bswap32 # define _LE64 __builtin_bswap64 # else // currently not supported # error No endian swap intrinsic defined # endif #else # define _LE16(x) (x) # define _LE32(x) (x) # define _LE64(x) (x) #endif #ifdef _M_ARM64 #define __ARM_NEON 1 #define __aarch64__ 1 #endif #if defined(_M_ARM) #define __ARM_NEON 1 #endif #if defined(_MSC_VER) && !defined(__clang__) # if (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(_M_X64) #define __SSE2__ 1 #define __SSSE3__ 1 #define __SSE4_1__ 1 # endif # if !defined(__PCLMUL__) && (_MSC_VER >= 1600 && defined(__SSE2__)) #define __PCLMUL__ 1 # endif # if !defined(__AVX__) && (_MSC_VER >= 1700 && defined(__SSE2__)) #define __AVX__ 1 # endif # if !defined(__AVX2__) && (_MSC_VER >= 1800 && defined(__SSE2__)) #define __AVX2__ 1 # endif # ifdef PLATFORM_AMD64 /* AVX512 requires VS 15.3 */ # if !defined(__AVX512F__) && (_MSC_VER >= 1911 && defined(__AVX__)) #define __AVX512BW__ 1 #define __AVX512F__ 1 # endif /* AVX512VL not available until VS 15.5 */ # if defined(__AVX512F__) && _MSC_VER >= 1912 #define __AVX512VL__ 1 # endif /* VBMI added in 15.7 */ # if defined(__AVX512F__) && _MSC_VER >= 1914 #define __AVX512VBMI__ 1 # endif # else // earlier versions of MSVC have buggy AVX-512 // not investigated fully, but it seems mostly problematic with 32-bit Release builds, so require VS2019 for AVX-512 # if !defined(__AVX512F__) && _MSC_VER >= 1920 && defined(__AVX__) #define __AVX512BW__ 1 #define __AVX512F__ 1 #define __AVX512VL__ 1 #define __AVX512VBMI__ 1 # elif defined(__AVX512F__) && _MSC_VER < 1920 # undef __AVX512F__ # ifdef __AVX512BW__ # undef __AVX512BW__ # endif # ifdef __AVX512VL__ # undef __AVX512VL__ # endif # endif # endif # if defined(__AVX2__) && _MSC_VER >= 1915 #define __VPCLMULQDQ__ 1 # endif # if defined(__SSE2__) && _MSC_VER >= 1920 #define __GFNI__ 1 # endif #endif /* _MSC_VER */ #ifdef __SSE2__ # include #endif #ifdef __SSSE3__ # include #endif #ifdef __SSE4_1__ # include #endif #ifdef __PCLMUL__ # include #endif #ifdef __GFNI__ // workaround for bug in ClangCL < 12: GFNI defines assume AVX512BW is enabled on MSVC // the guards put in place seem to be a performance thing for MSVC, but actually stops it from working here: http://reviews.llvm.org/D20291 # if defined(_MSC_VER) && defined(__clang__) && __clang_major__ < 12 && !defined(__AVX512BW__) # ifdef __AVX512F__ # define __AVX512BW__ # include # undef __AVX512BW__ # elif defined(__AVX2__) # define __AVX512F__ # define __AVX512BW__ # include # undef __AVX512F__ # undef __AVX512BW__ # elif defined(__AVX__) # define __AVX2__ # define __AVX512F__ # define __AVX512BW__ # include # undef __AVX2__ # undef __AVX512F__ # undef __AVX512BW__ # else # include # include # define __AVX__ # define __AVX2__ # define __AVX512F__ # define __AVX512BW__ # include # undef __AVX__ # undef __AVX2__ # undef __AVX512F__ # undef __AVX512BW__ # endif # else # include # endif #endif #ifdef __AVX__ # include #endif // x86 vector upcasts, where upper is defined to be 0 #if (defined(__clang__) && __clang_major__ >= 5 && (!defined(__APPLE__) || __clang_major__ >= 7)) || (defined(__GNUC__) && __GNUC__ >= 10) || (defined(_MSC_VER) && _MSC_VER >= 1910) // intrinsic unsupported in GCC 9 and MSVC < 2017 //# define zext128_256 _mm256_zextsi128_si256 # define zext256_512 _mm512_zextsi256_si512 # define zext128_512 _mm512_zextsi128_si512 # define extract_top128_256(x) _mm256_zextsi128_si256(_mm256_extracti128_si256(x, 1)) #else // technically a cast is incorrect, due to upper 128 bits being undefined, but should usually work fine because it wouldn't make sense for a compiler to do otherwise # ifdef __OPTIMIZE__ //# define zext128_256 _mm256_castsi128_si256 # define zext256_512 _mm512_castsi256_si512 # define zext128_512 _mm512_castsi128_si512 # define extract_top128_256(x) _mm256_castsi128_si256(_mm256_extracti128_si256(x, 1)) # else // alternative may be `_mm256_set_m128i(_mm_setzero_si128(), v)` but unsupported on GCC < 7, and most compilers generate a VINSERTF128 instruction for it // seems like Clang only stores half the register to the stack, if optimization disabled and zero extension not used, causing the top 128-bits to be the old value # define zext256_512(x) _mm512_inserti64x4(_mm512_setzero_si512(), x, 0) # define zext128_512(x) _mm512_inserti32x4(_mm512_setzero_si512(), x, 0) # define extract_top128_256(x) _mm256_permute2x128_si256(x, x, 0x81) # endif #endif // AVX on mingw-gcc is just broken - don't do it... #if defined(HEDLEY_GCC_VERSION) && (defined(__MINGW32__) || defined(__MINGW64__)) && defined(__AVX2__) && defined(PP_PLATFORM_SHOW_WARNINGS) HEDLEY_WARNING("Compiling AVX code on MinGW GCC may cause crashing due to stack alignment bugs [https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412]"); // as of writing, GCC 10.1 still has this problem, and it doesn't look like it'll be fixed any time soon // ...so if you're reading this, try Clang instead #endif // GCC < 10 has buggy handling of GF2P8AFFINEQB instruction if optimizations are enabled // for SSE encodings, the bug seems to cause the operands to sometimes be placed in the wrong order (example: https://godbolt.org/z/5Yf135) // haven't checked EVEX encoding, but it seems to fail tests there as well // we hack around it by pretending GCC < 10 doesn't support GFNI #if !HEDLEY_GCC_VERSION_CHECK(10,0,0) && defined(HEDLEY_GCC_VERSION) && defined(__OPTIMIZE__) && defined(__GFNI__) # undef __GFNI__ # ifdef PP_PLATFORM_SHOW_WARNINGS HEDLEY_WARNING("GFNI disabled on GCC < 10 due to incorrect GF2P8AFFINEQB operand placement"); # endif #endif #if !HEDLEY_GCC_VERSION_CHECK(5,0,0) && defined(HEDLEY_GCC_VERSION) && defined(__AVX512F__) // missing _mm512_castsi512_si256 - can't compile # undef __AVX512F__ #endif #if (defined(_MSC_VER) && defined(__clang__)) || (defined(PARPAR_SLIM_GF16) && defined(__APPLE__)) // ClangCL doesn't support SVE as of 15.0.1 (maybe due to not being defined on Windows-ARM?) // No Apple CPU supports SVE, and there's no defined way to detect it, meaning it'll never get used in practice (even if a later CPU supports SVE), so strip out SVE functionality for now # ifdef __ARM_FEATURE_SVE # undef __ARM_FEATURE_SVE # endif # ifdef __ARM_FEATURE_SVE2 # undef __ARM_FEATURE_SVE2 # endif #endif #if defined(__ARM_FEATURE_SVE) && defined(__clang__) && __clang_major__<12 // Clang < 12 has issues with SVE # ifdef __ARM_FEATURE_SVE # undef __ARM_FEATURE_SVE # endif # ifdef __ARM_FEATURE_SVE2 # undef __ARM_FEATURE_SVE2 # endif #endif #if defined(__riscv_vector) && defined(HEDLEY_GCC_VERSION) && !HEDLEY_GCC_VERSION_CHECK(14,0,0) // GCC added RVV intrinsics in GCC13, but GCC13 lacks segmented loads/stores # undef __riscv_vector #endif // Some environments lack ARM headers, so try to check for these #ifdef __has_include # if defined(__ARM_FEATURE_SVE) && !__has_include() # undef __ARM_FEATURE_SVE # ifdef PP_PLATFORM_SHOW_WARNINGS HEDLEY_WARNING("SVE disabled due to missing arm_sve.h header"); # endif # endif # if defined(__ARM_FEATURE_SVE2) && !__has_include() # undef __ARM_FEATURE_SVE2 # ifdef PP_PLATFORM_SHOW_WARNINGS HEDLEY_WARNING("SVE2 disabled due to missing arm_sve.h header"); # endif # endif # if defined(__ARM_NEON) && !__has_include() # undef __ARM_NEON # ifdef PP_PLATFORM_SHOW_WARNINGS HEDLEY_WARNING("NEON disabled due to missing arm_neon.h header"); # endif # endif #endif // alignment #ifdef _MSC_VER # define ALIGN_TO(a, v) __declspec(align(a)) v #else # define ALIGN_TO(a, v) v __attribute__((aligned(a))) #endif #include #if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__) // MSVC doesn't support C11 aligned_alloc: https://stackoverflow.com/a/62963007 #define ALIGN_ALLOC(buf, len, align) *(void**)&(buf) = _aligned_malloc((len), align) #define ALIGN_FREE _aligned_free #elif defined(_ISOC11_SOURCE) // C11 method // len needs to be a multiple of alignment, although it sometimes works if it isn't... #define ALIGN_ALLOC(buf, len, align) *(void**)&(buf) = aligned_alloc(align, ((len) + (align)-1) & ~(size_t)((align)-1)) #define ALIGN_FREE free #elif defined(__cplusplus) && __cplusplus >= 201700 // C++17 method #include #define ALIGN_ALLOC(buf, len, align) *(void**)&(buf) = std::aligned_alloc(align, ((len) + (align)-1) & ~(size_t)((align)-1)) #define ALIGN_FREE free #else #define ALIGN_ALLOC(buf, len, align) if(posix_memalign((void**)&(buf), align < sizeof(void*) ? sizeof(void*) : align, (len))) (buf) = NULL #define ALIGN_FREE free #endif // read/write to pointer #include #include "stdint.h" static HEDLEY_ALWAYS_INLINE uint32_t read16(const void* p) { uint16_t v; memcpy(&v, p, 2); return v; } static HEDLEY_ALWAYS_INLINE uint32_t read32(const void* p) { uint32_t v; memcpy(&v, p, 4); return v; } static HEDLEY_ALWAYS_INLINE uint64_t read64(const void* p) { uint64_t v; memcpy(&v, p, 8); return v; } static HEDLEY_ALWAYS_INLINE uintptr_t readPtr(const void* p) { uintptr_t v; memcpy(&v, p, sizeof(uintptr_t)); return v; } static HEDLEY_ALWAYS_INLINE void write16(void* p, uint16_t v) { memcpy(p, &v, 2); } static HEDLEY_ALWAYS_INLINE void write32(void* p, uint32_t v) { memcpy(p, &v, 4); } static HEDLEY_ALWAYS_INLINE void write64(void* p, uint64_t v) { memcpy(p, &v, 8); } static HEDLEY_ALWAYS_INLINE void writePtr(void* p, uintptr_t v) { memcpy(p, &v, sizeof(uintptr_t)); } #endif /* PP_PLATFORM_H */ par2cmdline-turbo-1.4.0/parpar/src/platform_warnings.c000066400000000000000000000001571514221355600230300ustar00rootroot00000000000000// dummy compilation unit to display platform warnings #define PP_PLATFORM_SHOW_WARNINGS #include "platform.h" par2cmdline-turbo-1.4.0/parpar/src/stdint.h000066400000000000000000000171071514221355600206110ustar00rootroot00000000000000// ISO C9x compliant stdint.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2008 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////////// #if !defined(_MSC_VER) || defined(_STDINT) || _MSC_VER >= 1900 || defined(__clang__) #include #else #ifndef _MSC_STDINT_H_ // [ #define _MSC_STDINT_H_ #if _MSC_VER > 1000 #pragma once #endif #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when // compiling for ARM we should wrap include with 'extern "C++" {}' // or compiler give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed #ifdef __cplusplus extern "C" { #endif # include #ifdef __cplusplus } #endif // Define _W64 macros to mark types changing their size, like intptr_t. #ifndef _W64 # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 # define _W64 __w64 # else # define _W64 # endif #endif // 7.18.1 Integer types // 7.18.1.1 Exact-width integer types // Visual Studio 6 and Embedded Visual C++ 4 doesn't // realize that, e.g. char has the same size as __int8 // so we give up on __intX for them. #if (_MSC_VER < 1300) typedef signed char int8_t; typedef signed short int16_t; typedef signed int int32_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else typedef signed __int8 int8_t; typedef signed __int16 int16_t; typedef signed __int32 int32_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; // 7.18.1.2 Minimum-width integer types typedef int8_t int_least8_t; typedef int16_t int_least16_t; typedef int32_t int_least32_t; typedef int64_t int_least64_t; typedef uint8_t uint_least8_t; typedef uint16_t uint_least16_t; typedef uint32_t uint_least32_t; typedef uint64_t uint_least64_t; // 7.18.1.3 Fastest minimum-width integer types typedef int8_t int_fast8_t; typedef int16_t int_fast16_t; typedef int32_t int_fast32_t; typedef int64_t int_fast64_t; typedef uint8_t uint_fast8_t; typedef uint16_t uint_fast16_t; typedef uint32_t uint_fast32_t; typedef uint64_t uint_fast64_t; // 7.18.1.4 Integer types capable of holding object pointers #ifdef _WIN64 // [ typedef signed __int64 intptr_t; typedef unsigned __int64 uintptr_t; #else // _WIN64 ][ typedef _W64 signed int intptr_t; typedef _W64 unsigned int uintptr_t; #endif // _WIN64 ] // 7.18.1.5 Greatest-width integer types typedef int64_t intmax_t; typedef uint64_t uintmax_t; // 7.18.2 Limits of specified-width integer types #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 // 7.18.2.1 Limits of exact-width integer types #define INT8_MIN ((int8_t)_I8_MIN) #define INT8_MAX _I8_MAX #define INT16_MIN ((int16_t)_I16_MIN) #define INT16_MAX _I16_MAX #define INT32_MIN ((int32_t)_I32_MIN) #define INT32_MAX _I32_MAX #define INT64_MIN ((int64_t)_I64_MIN) #define INT64_MAX _I64_MAX #define UINT8_MAX _UI8_MAX #define UINT16_MAX _UI16_MAX #define UINT32_MAX _UI32_MAX #define UINT64_MAX _UI64_MAX // 7.18.2.2 Limits of minimum-width integer types #define INT_LEAST8_MIN INT8_MIN #define INT_LEAST8_MAX INT8_MAX #define INT_LEAST16_MIN INT16_MIN #define INT_LEAST16_MAX INT16_MAX #define INT_LEAST32_MIN INT32_MIN #define INT_LEAST32_MAX INT32_MAX #define INT_LEAST64_MIN INT64_MIN #define INT_LEAST64_MAX INT64_MAX #define UINT_LEAST8_MAX UINT8_MAX #define UINT_LEAST16_MAX UINT16_MAX #define UINT_LEAST32_MAX UINT32_MAX #define UINT_LEAST64_MAX UINT64_MAX // 7.18.2.3 Limits of fastest minimum-width integer types #define INT_FAST8_MIN INT8_MIN #define INT_FAST8_MAX INT8_MAX #define INT_FAST16_MIN INT16_MIN #define INT_FAST16_MAX INT16_MAX #define INT_FAST32_MIN INT32_MIN #define INT_FAST32_MAX INT32_MAX #define INT_FAST64_MIN INT64_MIN #define INT_FAST64_MAX INT64_MAX #define UINT_FAST8_MAX UINT8_MAX #define UINT_FAST16_MAX UINT16_MAX #define UINT_FAST32_MAX UINT32_MAX #define UINT_FAST64_MAX UINT64_MAX // 7.18.2.4 Limits of integer types capable of holding object pointers #ifdef _WIN64 // [ # define INTPTR_MIN INT64_MIN # define INTPTR_MAX INT64_MAX # define UINTPTR_MAX UINT64_MAX #else // _WIN64 ][ # define INTPTR_MIN INT32_MIN # define INTPTR_MAX INT32_MAX # define UINTPTR_MAX UINT32_MAX #endif // _WIN64 ] // 7.18.2.5 Limits of greatest-width integer types #define INTMAX_MIN INT64_MIN #define INTMAX_MAX INT64_MAX #define UINTMAX_MAX UINT64_MAX // 7.18.3 Limits of other integer types #ifdef _WIN64 // [ # define PTRDIFF_MIN _I64_MIN # define PTRDIFF_MAX _I64_MAX #else // _WIN64 ][ # define PTRDIFF_MIN _I32_MIN # define PTRDIFF_MAX _I32_MAX #endif // _WIN64 ] #define SIG_ATOMIC_MIN INT_MIN #define SIG_ATOMIC_MAX INT_MAX #ifndef SIZE_MAX // [ # ifdef _WIN64 // [ # define SIZE_MAX _UI64_MAX # else // _WIN64 ][ # define SIZE_MAX _UI32_MAX # endif // _WIN64 ] #endif // SIZE_MAX ] // WCHAR_MIN and WCHAR_MAX are also defined in #ifndef WCHAR_MIN // [ # define WCHAR_MIN 0 #endif // WCHAR_MIN ] #ifndef WCHAR_MAX // [ # define WCHAR_MAX _UI16_MAX #endif // WCHAR_MAX ] #define WINT_MIN 0 #define WINT_MAX _UI16_MAX #endif // __STDC_LIMIT_MACROS ] // 7.18.4 Limits of other integer types #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 // 7.18.4.1 Macros for minimum-width integer constants #define INT8_C(val) val##i8 #define INT16_C(val) val##i16 #define INT32_C(val) val##i32 #define INT64_C(val) val##i64 #define UINT8_C(val) val##ui8 #define UINT16_C(val) val##ui16 #define UINT32_C(val) val##ui32 #define UINT64_C(val) val##ui64 // 7.18.4.2 Macros for greatest-width integer constants #define INTMAX_C INT64_C #define UINTMAX_C UINT64_C #endif // __STDC_CONSTANT_MACROS ] #endif // _MSC_STDINT_H_ ] #endif // MSVC stuff par2cmdline-turbo-1.4.0/src/000077500000000000000000000000001514221355600156405ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/src/commandline.cpp000066400000000000000000001204261514221355600206370ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // iostream is included here, so that cout and cerr are not used elsewhere. #include #include #include "commandline.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif #include CommandLine::CommandLine(void) : filesize_cache() , version(verUnknown) , noiselevel(nlUnknown) , memorylimit(0) , basepath() , nthreads(0) // 0 means use default number , filethreads( _FILE_THREADS ) // default from header file , parfilename() , rawfilenames() , extrafiles() , operation(opNone) , purgefiles(false) , renameonly(false) , skipdata(false) , skipleaway(0) , blockcount(0) , blocksize(0) , firstblock(0) , recoveryfilescheme(scUnknown) , recoveryfilecount(0) , recoveryblockcount(0) , recoveryblockcountset(false) , redundancy(0) , redundancysize(0) , redundancyset(false) , recursive(false) { } void CommandLine::showversion(void) { std::string version = X_PACKAGE " version " X_VERSION; std::cout << version << std::endl; } void CommandLine::banner(void) { std::cout << "Copyright (C) 2003-2015 Peter Brian Clements." << std::endl << "Copyright (C) 2011-2012 Marcel Partap." << std::endl << "Copyright (C) 2012-2026 Ike Devolder." << std::endl << "Copyright (C) 2014-2017 Jussi Kansanen." << std::endl << "Copyright (C) 2019 Michael Nahas." << std::endl << std::endl << "par2cmdline comes with ABSOLUTELY NO WARRANTY." << std::endl << std::endl << "This is free software, and you are welcome to redistribute it and/or modify" << std::endl << "it under the terms of the GNU General Public License as published by the" << std::endl << "Free Software Foundation; either version 2 of the License, or (at your" << std::endl << "option) any later version. See COPYING for details." << std::endl << std::endl; } void CommandLine::usage(void) { std::cout << "Usage:\n" " par2 -h : show this help\n" " par2 -V : show version\n" " par2 -VV : show version and copyright\n" "\n" " par2 c(reate) [options] [files] : Create PAR2 files\n" " par2 v(erify) [options] [files] : Verify files using PAR2 file\n" " par2 r(epair) [options] [files] : Repair files using PAR2 files\n" "\n" "You may also leave out the \"c\", \"v\", and \"r\" commands by using \"par2create\",\n" "\"par2verify\", or \"par2repair\" instead.\n" "\n" "Options: (all uses)\n" " -B : Set the basepath to use as reference for the datafiles\n" " -v [-v] : Be more verbose\n" " -q [-q] : Be more quiet (-q -q gives silence)\n" " -m : Memory (in MB) to use (default is half of total physical memory)\n"; std::cout << " -t : Number of threads used for main processing (" << std::thread::hardware_concurrency() << " detected)\n" " -T : Number of files hashed in parallel\n" " (" << _FILE_THREADS << " are the default)\n"; std::cout << " -- : Treat all following arguments as filenames\n" "Options: (verify or repair)\n" " -p : Purge backup files and par files on successful recovery or\n" " when no recovery is needed\n" " -O : Rename-only mode (skip files that are not perfect matches,\n" " useful for quickly fixing renamed files)\n" " -N : Data skipping (find badly mispositioned data blocks)\n" " -S : Skip leaway (distance +/- from expected block position, default 64)\n" "Options: (create)\n" " -a : Set the main PAR2 archive name\n" " -b : Set the Block-Count (default 2000)\n" " -s : Set the Block-Size (don't use both -b and -s)\n" " -r : Level of redundancy (%, default 5%)\n" " -r : Redundancy target size, =g(iga),m(ega),k(ilo) bytes\n" " -c : Recovery Block-Count (don't use both -r and -c)\n" " -f : First Recovery-Block-Number (default 0)\n" " -u : Uniform recovery file sizes (default is variable)\n" " -l : Limit size of recovery files (don't use both -u and -l)\n" " -n : Number of recovery files (max 31) (don't use both -n and -l)\n" " -R : Recurse into subdirectories\n" " (Be aware of wildcard shell expansion)\n" "\n"; std::cout << "Example:\n" " par2 repair *.par2\n" "\n"; } bool CommandLine::Parse(int argc, const char * const *argv) { if (!ReadArgs(argc, argv)) return false; if (operation != opNone) { // user didn't do "par --help", etc. if (!CheckValuesAndSetDefaults()) return false; } if (operation == opCreate) { if (!ComputeBlockSize()) return false; u32 sourceblockcount = 0; u64 largestfilesize = 0; for (std::vector::const_iterator i=extrafiles.begin(); i!=extrafiles.end(); i++) { u64 filesize = filesize_cache.get(*i); sourceblockcount += (u32) ((filesize + blocksize-1) / blocksize); if (filesize > largestfilesize) { largestfilesize = filesize; } } if (!ComputeRecoveryBlockCount(&recoveryblockcount, sourceblockcount, blocksize, firstblock, recoveryfilescheme, recoveryfilecount, recoveryblockcountset, redundancy, redundancysize, largestfilesize)) { return false; } } return true; } bool CommandLine::ReadArgs(int argc, const char * const *argv) { if (argc<1) { return false; } // Split the program name into path and filename std::string path, name; DiskFile::SplitFilename(argv[0], path, name); argc--; argv++; if (argc>0) { if (argv[0][0] == '-') { if (argv[0] == std::string("-h") || argv[0] == std::string("--help")) { usage(); return true; } else if (argv[0] == std::string("-V") || argv[0] == std::string("--version")) { showversion(); return true; } else if (argv[0] == std::string("-VV")) { showversion(); std::cout << "A " PACKAGE " version " VERSION " fork, using a ParPar processing backend" << std::endl << std::endl; banner(); return true; } } } // Strip ".exe" from the end if (name.size() > 4 && 0 == stricmp(".exe", name.substr(name.length()-4).c_str())) { name = name.substr(0, name.length()-4); } // Check the resulting program name if (0 == stricmp("par2create", name.c_str())) { operation = opCreate; } else if (0 == stricmp("par2verify", name.c_str())) { operation = opVerify; } else if (0 == stricmp("par2repair", name.c_str())) { operation = opRepair; } // Have we determined what operation we want? if (operation == opNone) { if (argc<2) { std::cerr << "Not enough command line arguments." << std::endl; return false; } switch (tolower(argv[0][0])) { case 'c': if (argv[0][1] == 0 || 0 == stricmp(argv[0], "create")) operation = opCreate; break; case 'v': if (argv[0][1] == 0 || 0 == stricmp(argv[0], "verify")) operation = opVerify; break; case 'r': if (argv[0][1] == 0 || 0 == stricmp(argv[0], "repair")) operation = opRepair; break; } if (operation == opNone) { std::cerr << "Invalid operation specified: " << argv[0] << std::endl; return false; } argc--; argv++; } bool options = true; basepath = ""; while (argc>0) { if (argv[0][0]) { if (options && argv[0][0] != '-') options = false; if (options) { switch (argv[0][1]) { case 'a': { if (operation == opCreate) { std::string str = argv[0]; bool setparfile = false; if (str == "-a") { setparfile = SetParFilename(argv[1]); argc--; argv++; } else { setparfile = SetParFilename(str.substr(2)); } if (! setparfile) { std::cerr << "failed to set the main par file" << std::endl; return false; } } } break; case 'b': // Set the block count { if (operation != opCreate) { std::cerr << "Cannot specify block count unless creating." << std::endl; return false; } if (blockcount > 0) { std::cerr << "Cannot specify block count twice." << std::endl; return false; } else if (blocksize > 0) { std::cerr << "Cannot specify both block count and block size." << std::endl; return false; } const char *p = &argv[0][2]; while (blockcount <= 3276 && *p && isdigit(*p)) { blockcount = blockcount * 10 + (*p - '0'); p++; } if (0 == blockcount || blockcount > 32768 || *p) { std::cerr << "Invalid block count option: " << argv[0] << std::endl; return false; } } break; case 's': // Set the block size { if (operation != opCreate) { std::cerr << "Cannot specify block size unless creating." << std::endl; return false; } if (blocksize > 0) { std::cerr << "Cannot specify block size twice." << std::endl; return false; } else if (blockcount > 0) { std::cerr << "Cannot specify both block count and block size." << std::endl; return false; } const char *p = &argv[0][2]; while (blocksize <= 429496729 && *p && isdigit(*p)) { blocksize = blocksize * 10 + (*p - '0'); p++; } if (*p || blocksize == 0) { std::cerr << "Invalid block size option: " << argv[0] << std::endl; return false; } if (blocksize & 3) { std::cerr << "Block size must be a multiple of 4." << std::endl; return false; } } break; case 't': // Set amount of threads { nthreads = 0; const char *p = &argv[0][2]; while (*p && isdigit(*p)) { nthreads = nthreads * 10 + (*p - '0'); p++; } if (!nthreads) { std::cerr << "Invalid thread option: " << argv[0] << std::endl; return false; } } break; case 'T': // Set amount of file threads { filethreads = 0; const char *p = &argv[0][2]; while (*p && isdigit(*p)) { filethreads = filethreads * 10 + (*p - '0'); p++; } if (!filethreads) { std::cerr << "Invalid file-thread option: " << argv[0] << std::endl; return false; } } break; case 'r': // Set the amount of redundancy required { if (operation != opCreate) { std::cerr << "Cannot specify redundancy unless creating." << std::endl; return false; } if (redundancyset) { std::cerr << "Cannot specify redundancy twice." << std::endl; return false; } else if (recoveryblockcountset) { std::cerr << "Cannot specify both redundancy and recovery block count." << std::endl; return false; } if (argv[0][2] == 'k' || argv[0][2] == 'm' || argv[0][2] == 'g' ) { const char *p = &argv[0][3]; while (*p && isdigit(*p)) { redundancysize = redundancysize * 10 + (*p - '0'); p++; } switch (argv[0][2]) { case 'g': redundancysize = redundancysize * 1024; case 'm': redundancysize = redundancysize * 1024; case 'k': redundancysize = redundancysize * 1024; break; } } else { const char *p = &argv[0][2]; while (*p && isdigit(*p)) { redundancy = redundancy * 10 + (*p - '0'); p++; } if (*p) { std::cerr << "Invalid redundancy option: " << argv[0] << std::endl; return false; } if (redundancy == 0 && recoveryfilecount > 0) { std::cerr << "Cannot set redundancy to 0 and file count > 0" << std::endl; return false; } if (redundancy > 100) { std::cerr << "WARNING: Creating recovery file(s) with " << redundancy << "% redundancy." << std::endl; } } redundancyset = true; } break; case 'c': // Set the number of recovery blocks to create { if (operation != opCreate) { std::cerr << "Cannot specify recovery block count unless creating." << std::endl; return false; } if (recoveryblockcountset) { std::cerr << "Cannot specify recovery block count twice." << std::endl; return false; } else if (redundancyset) { std::cerr << "Cannot specify both recovery block count and redundancy." << std::endl; return false; } const char *p = &argv[0][2]; while (recoveryblockcount <= 32768 && *p && isdigit(*p)) { recoveryblockcount = recoveryblockcount * 10 + (*p - '0'); p++; } if (recoveryblockcount > 32768 || *p) { std::cerr << "Invalid recoveryblockcount option: " << argv[0] << std::endl; return false; } if (recoveryblockcount == 0 && recoveryfilecount > 0) { std::cerr << "Cannot set recoveryblockcount to 0 and file count > 0" << std::endl; return false; } recoveryblockcountset = true; } break; case 'f': // Specify the First block recovery number { if (operation != opCreate) { std::cerr << "Cannot specify first block number unless creating." << std::endl; return false; } if (firstblock > 0) { std::cerr << "Cannot specify first block twice." << std::endl; return false; } const char *p = &argv[0][2]; while (firstblock <= 3276 && *p && isdigit(*p)) { firstblock = firstblock * 10 + (*p - '0'); p++; } if (firstblock > 32768 || *p) { std::cerr << "Invalid first block option: " << argv[0] << std::endl; return false; } } break; case 'u': // Specify uniformly sized recovery files { if (operation != opCreate) { std::cerr << "Cannot specify uniform files unless creating." << std::endl; return false; } if (argv[0][2]) { std::cerr << "Invalid option: " << argv[0] << std::endl; return false; } if (recoveryfilescheme != scUnknown && recoveryfilescheme != scUniform) { std::cerr << "Cannot specify two recovery file size schemes." << std::endl; return false; } recoveryfilescheme = scUniform; } break; case 'l': // Limit the size of the recovery files { if (operation != opCreate) { std::cerr << "Cannot specify limit files unless creating." << std::endl; return false; } if (argv[0][2]) { std::cerr << "Invalid option: " << argv[0] << std::endl; return false; } if (recoveryfilescheme != scUnknown) { std::cerr << "Cannot specify two recovery file size schemes." << std::endl; return false; } if (recoveryfilecount > 0) { std::cerr << "Cannot specify limited size and number of files at the same time." << std::endl; return false; } recoveryfilescheme = scLimited; } break; case 'n': // Specify the number of recovery files { if (operation != opCreate) { std::cerr << "Cannot specify recovery file count unless creating." << std::endl; return false; } if (recoveryfilecount > 0) { std::cerr << "Cannot specify recovery file count twice." << std::endl; return false; } // (Removed "Cannot set file count when redundancy is set to 0.") if (recoveryblockcountset && recoveryblockcount == 0) { std::cerr << "Cannot set file count when recovery block count is set to 0." << std::endl; return false; } if (recoveryfilescheme == scLimited) { std::cerr << "Cannot specify limited size and number of files at the same time." << std::endl; return false; } const char *p = &argv[0][2]; while (*p && isdigit(*p)) { recoveryfilecount = recoveryfilecount * 10 + (*p - '0'); p++; } if (recoveryfilecount == 0 || *p) { std::cerr << "Invalid recovery file count option: " << argv[0] << std::endl; return false; } // cap the maximum number of recovery files // because the number of recovery blocks will be // (2 ^ recoveryfilecount) - 1 // the number 32 will overflow the u32 resulting in 1 if (recoveryfilecount > 31) { std::cerr << "Invalid recovery file count option: " << recoveryfilecount << std::endl; std::cerr << " the maximum allowed recovery file count is 31" << std::endl; return false; } // When number of recovery files is used, set the recoveryfilescheme // to uniform, since variable will not always be able to fill all // the files recoveryfilescheme = scUniform; } break; case 'm': // Specify how much memory to use for output buffers { if (memorylimit > 0) { std::cerr << "Cannot specify memory limit twice." << std::endl; return false; } const char *p = &argv[0][2]; while (*p && isdigit(*p)) { memorylimit = memorylimit * 10 + (*p - '0'); p++; } if (memorylimit == 0 || *p) { std::cerr << "Invalid memory limit option: " << argv[0] << std::endl; return false; } } break; case 'v': { switch (noiselevel) { case nlUnknown: { if (argv[0][2] == 'v') noiselevel = nlDebug; else noiselevel = nlNoisy; } break; case nlNoisy: case nlDebug: noiselevel = nlDebug; break; default: std::cerr << "Cannot use both -v and -q." << std::endl; return false; break; } } break; case 'q': { switch (noiselevel) { case nlUnknown: { if (argv[0][2] == 'q') noiselevel = nlSilent; else noiselevel = nlQuiet; } break; case nlQuiet: case nlSilent: noiselevel = nlSilent; break; default: std::cerr << "Cannot use both -v and -q." << std::endl; return false; break; } } break; case 'p': { if (operation != opRepair && operation != opVerify) { std::cerr << "Cannot specify purge unless repairing or verifying." << std::endl; return false; } purgefiles = true; } break; case 'O': { if (operation != opRepair && operation != opVerify) { std::cerr << "Cannot specify rename-only unless repairing or verifying." << std::endl; return false; } renameonly = true; } break; case 'h': { usage(); return false; } // "break;" not needed. case 'R': { if (operation == opCreate) { recursive = true; } else { std::cerr << "Cannot specific Recursive unless creating." << std::endl; return false; } } break; case 'N': { if (operation == opCreate) { std::cerr << "Cannot specify Data Skipping unless reparing or verifying." << std::endl; return false; } skipdata = true; } break; case 'S': // Set the skip leaway { if (operation == opCreate) { std::cerr << "Cannot specify skip leaway when creating." << std::endl; return false; } if (!skipdata) { std::cerr << "Cannot specify skip leaway and no skipping." << std::endl; return false; } const char *p = &argv[0][2]; while (skipleaway <= 429496729 && *p && isdigit(*p)) { skipleaway = skipleaway * 10 + (*p - '0'); p++; } if (*p || skipleaway == 0) { std::cerr << "Invalid skipleaway option: " << argv[0] << std::endl; return false; } } break; case 'B': // Set the basepath manually { std::string str = argv[0]; if (str == "-B") { basepath = DiskFile::GetCanonicalPathname(argv[1]); argc--; argv++; } else { basepath = DiskFile::GetCanonicalPathname(str.substr(2)); } } break; case '-': { if (argv[0] != std::string("--")) { std::cerr << "Unknown option: " << argv[0] << std::endl; std::cerr << " (Options must appear after create, repair or verify.)" << std::endl; std::cerr << " (Run \"" << path << name << " --help\" for supported options.)" << std::endl; return false; } argc--; argv++; options = false; continue; } break; default: { std::cerr << "Invalid option specified: " << argv[0] << std::endl; return false; } } } else if (parfilename.length() == 0) { std::string filename = argv[0]; bool setparfile = SetParFilename(filename); if (! setparfile) { std::cerr << "failed to set the main par file" << std::endl; return false; } } else { std::string path; std::string name; DiskFile::SplitFilename(argv[0], path, name); std::unique_ptr< std::list > filenames( DiskFile::FindFiles(path, name, recursive) ); std::list::iterator fn = filenames->begin(); while (fn != filenames->end()) { // Convert filename from command line into a full path + filename std::string filename = DiskFile::GetCanonicalPathname(*fn); rawfilenames.push_back(filename); ++fn; } // delete filenames; Taken care of by unique_ptr<> } } argc--; argv++; } return true; } // This webpage has code to get physical memory size on many OSes // http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system #ifdef _WIN32 u64 CommandLine::GetTotalPhysicalMemory() { u64 TotalPhysicalMemory = 0; HMODULE hLib = ::LoadLibraryA("kernel32.dll"); if (NULL != hLib) { BOOL (WINAPI *pfn)(LPMEMORYSTATUSEX) = (BOOL (WINAPI*)(LPMEMORYSTATUSEX))::GetProcAddress(hLib, "GlobalMemoryStatusEx"); if (NULL != pfn) { MEMORYSTATUSEX mse; mse.dwLength = sizeof(mse); if (pfn(&mse)) { TotalPhysicalMemory = mse.ullTotalPhys; } } ::FreeLibrary(hLib); } if (TotalPhysicalMemory == 0) { MEMORYSTATUS ms; ::ZeroMemory(&ms, sizeof(ms)); ::GlobalMemoryStatus(&ms); TotalPhysicalMemory = ms.dwTotalPhys; } return TotalPhysicalMemory; } #elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE) // POSIX compliant OSes, including OSX/MacOS and Cygwin. Also works for Linux. u64 CommandLine::GetTotalPhysicalMemory() { long pages = sysconf(_SC_PHYS_PAGES); long page_size = sysconf(_SC_PAGESIZE); return pages*page_size; } #else // default version == unable to request memory size u64 CommandLine::GetTotalPhysicalMemory() { return 0; } #endif bool CommandLine::CheckValuesAndSetDefaults() { if (parfilename.length() == 0) { std::cerr << "You must specify a Recovery file." << std::endl; return false; } // Default noise level if (noiselevel == nlUnknown) { noiselevel = nlNormal; } // Default memorylimit of 128MB if (memorylimit == 0) { u64 TotalPhysicalMemory = GetTotalPhysicalMemory(); if (TotalPhysicalMemory == 0) { // Default/error case: // NOTE: In 2019, Ubuntu's minimum requirements are 256MiB. TotalPhysicalMemory = 256 * 1048576; } // Half of total physical memory memorylimit = (size_t)(TotalPhysicalMemory / 1048576 / 2); } // limit to 1GB on 32-bit platforms to avoid exhausing the addressable memory space if (sizeof(uintptr_t) < 8 && memorylimit > 1024) memorylimit = 1024; // convert to megabytes memorylimit *= 1048576; if (noiselevel >= nlDebug) { std::cout << "[DEBUG] memorylimit: " << memorylimit << " bytes" << std::endl; } // Default basepath (uses parfilename) if ("" == basepath) { if (noiselevel >= nlDebug) { std::cout << "[DEBUG] parfilename: " << parfilename << std::endl; } std::string dummy; std::string path; DiskFile::SplitFilename(parfilename, path, dummy); basepath = DiskFile::GetCanonicalPathname(path); // fallback if ("" == basepath) { basepath = DiskFile::GetCanonicalPathname("./"); } } std::string lastchar = basepath.substr(basepath.length() -1); if (PATHSEP != lastchar && ALTPATHSEP != lastchar) { basepath = basepath + PATHSEP; } if (noiselevel >= nlDebug) { std::cout << "[DEBUG] basepath: " << basepath << std::endl; } // parfilename is checked earlier, because it is used by basepath. // check extrafiles std::list::iterator rawfilenames_fn; for (rawfilenames_fn = rawfilenames.begin(); rawfilenames_fn != rawfilenames.end(); ++rawfilenames_fn) { std::string filename = *rawfilenames_fn; // Originally, all specified files were supposed to exist, or the program // would stop with an error message. This was not practical, for example in // a directory with files appearing and disappearing (an active download directory). // So the new rule is: when a specified file doesn't exist, it is silently skipped. if (!DiskFile::FileExists(filename)) { std::cout << "Ignoring non-existent source file: " << filename << std::endl; } // skip files outside basepath else if (filename.find(basepath) == std::string::npos) { std::cout << "Ignoring out of basepath source file: " << filename << std::endl; } else { u64 filesize = filesize_cache.get(filename); // Ignore all 0 byte files if (filesize == 0) { std::cout << "Skipping 0 byte file: " << filename << std::endl; } else if (extrafiles.end() != std::find(extrafiles.begin(), extrafiles.end(), filename)) { std::cout << "Skipping duplicate filename: " << filename << std::endl; } else { extrafiles.push_back(filename); } } //end file exists } // operation should always be set, but let's be thorough. if (operation == opNone) { std::cerr << "ERROR: No operation was specified (create, repair, or verify)" << std::endl; return false; } if (operation != opCreate) { // skipdata is bool and either value is valid. // Default skip leaway if (skipdata && skipleaway == 0) { // Expect to find blocks within +/- 64 bytes of the expected // position relative to the last block that was found. skipleaway = 64; } } // If we a creating, check the other parameters if (operation == opCreate) { // If we are creating, the source files must be given. if (extrafiles.size() == 0) { // Does the par filename include the ".par2" on the end? if (parfilename.length() > 5 && 0 == stricmp(parfilename.substr(parfilename.length()-5, 5).c_str(), ".par2")) { // Yes it does. std::cerr << "You must specify a list of files when creating." << std::endl; return false; } else { // No it does not. // In that case check to see if the file exists, and if it does // assume that you wish to create par2 files for it. u64 filesize = 0; if (DiskFile::FileExists(parfilename) && (filesize = DiskFile::GetFileSize(parfilename)) > 0) { extrafiles.push_back(parfilename); } else { // The file does not exist or it is empty. std::cerr << "You must specify a list of files when creating." << std::endl; return false; } } } // Strip the ".par2" from the end of the filename of the main PAR2 file. if (parfilename.length() > 5 && 0 == stricmp(parfilename.substr(parfilename.length()-5, 5).c_str(), ".par2")) { parfilename = parfilename.substr(0, parfilename.length()-5); } if (DiskFile::FileExists(parfilename + ".par2")) { std::cerr << "Par2 file already exists: " << parfilename << std::endl; return false; } // If neither block count not block size is specified if (blockcount == 0 && blocksize == 0) { // Use a block count of 2000 blockcount = 2000; } // If no recovery file size scheme is specified then use Variable if (recoveryfilescheme == scUnknown) { recoveryfilescheme = scVariable; } // Assume a redundancy of 5% if neither redundancy or recoveryblockcount were set. if (!redundancyset && !recoveryblockcountset) { redundancy = 5; redundancyset = true; } } return true; } bool CommandLine::ComputeBlockSize() { if (blocksize == 0) { // compute value from blockcount if (blockcount < extrafiles.size()) { // The block count cannot be less than the number of files. std::cerr << "Block count (" << blockcount << ") cannot be smaller than the number of files(" << extrafiles.size() << "). " << std::endl; return false; } else if (blockcount == extrafiles.size()) { // If the block count is the same as the number of files, then the block // size is the size of the largest file (rounded up to a multiple of 4). u64 largestfilesize = 0; for (std::vector::const_iterator i=extrafiles.begin(); i!=extrafiles.end(); i++) { u64 filesize = filesize_cache.get(*i); if (filesize > largestfilesize) { largestfilesize = filesize; } } blocksize = (largestfilesize + 3) & ~3; } else { u64 totalsize = 0; for (std::vector::const_iterator i=extrafiles.begin(); i!=extrafiles.end(); i++) { totalsize += (filesize_cache.get(*i) + 3) / 4; } if (blockcount > totalsize) { blocksize = 4; } else { // Absolute lower bound and upper bound on the source block size that will // result in the requested source block count. u64 lowerBound = totalsize / blockcount; u64 upperBound = (totalsize + blockcount - extrafiles.size() - 1) / (blockcount - extrafiles.size()); u64 count = 0; u64 size; do { size = (lowerBound + upperBound)/2; count = 0; for (std::vector::const_iterator i=extrafiles.begin(); i!=extrafiles.end(); i++) { count += ((filesize_cache.get(*i)+3)/4 + size-1) / size; } if (count > blockcount) { lowerBound = size+1; if (lowerBound >= upperBound) { size = lowerBound; count = 0; for (std::vector::const_iterator i=extrafiles.begin(); i!=extrafiles.end(); i++) { count += ((filesize_cache.get(*i)+3)/4 + size-1) / size; } } } else { upperBound = size; } } while (lowerBound < upperBound); if (count > 32768) { std::cerr << "Error calculating block size. cannot be higher than 32768." << std::endl; return false; } else if (count == 0) { std::cerr << "Error calculating block size. cannot be 0." << std::endl; return false; } blocksize = size*4; } } } return true; } // Determine how many recovery blocks to create based on the source block // count and the requested level of redundancy. bool CommandLine::ComputeRecoveryBlockCount(u32 *recoveryblockcount, u32 sourceblockcount, u64 blocksize, u32 firstblock, Scheme recoveryfilescheme, u32 recoveryfilecount, bool recoveryblockcountset, u32 redundancy, u64 redundancysize, u64 largestfilesize) { if (recoveryblockcountset) { // no need to assign value. // pass through, so that value can be checked below. } else if (redundancy > 0) { // count is the number of input blocks // Determine recoveryblockcount *recoveryblockcount = (sourceblockcount * redundancy + 50) / 100; } else if (redundancysize > 0) { const u64 overhead_per_recovery_file = sourceblockcount * (u64) 21; const u64 recovery_packet_size = blocksize + (u64) 70; if (recoveryfilecount == 0) { u32 estimatedFileCount = 15; u64 overhead = estimatedFileCount * overhead_per_recovery_file; u32 estimatedrecoveryblockcount; if (overhead > redundancysize) { estimatedrecoveryblockcount = 1; // at least 1 } else { estimatedrecoveryblockcount = (u32)((redundancysize - overhead) / recovery_packet_size); } // recoveryfilecount assigned below. bool success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, recoveryfilescheme, estimatedrecoveryblockcount, largestfilesize, blocksize); if (!success) { return false; } } const u64 overhead = recoveryfilecount * overhead_per_recovery_file; if (overhead > redundancysize) { *recoveryblockcount = 1; // at least 1 } else { *recoveryblockcount = (u32)((redundancysize - overhead) / recovery_packet_size); } } else { std::cerr << "Redundancy and Redundancysize not set." << std::endl; return false; } // Force valid values if necessary if (*recoveryblockcount == 0 && redundancy > 0) *recoveryblockcount = 1; if (*recoveryblockcount > 65536) { std::cerr << "Too many recovery blocks requested." << std::endl; return false; } // Check that the last recovery block number would not be too large if (firstblock + *recoveryblockcount >= 65536) { std::cerr << "First recovery block number is too high." << std::endl; return false; } return true; } bool CommandLine::SetParFilename(std::string filename) { bool result = false; #ifndef _WIN32 std::string::size_type where; if ((where = filename.find_first_of('*')) != std::string::npos || (where = filename.find_first_of('?')) != std::string::npos) { std::cerr << "par2 file must not have a wildcard in it." << std::endl; return result; } #endif // If we are verifying or repairing, the PAR2 file must // already exist if (operation != opCreate) { // Find the last '.' in the filename std::string::size_type where = filename.find_last_of('.'); if (where != std::string::npos) { // Get what follows the last '.' std::string tail = filename.substr(where+1); if (0 == stricmp(tail.c_str(), "par2")) { parfilename = filename; version = verPar2; } else if (0 == stricmp(tail.c_str(), "par") || (tail.size() == 3 && tolower(tail[0]) == 'p' && isdigit(tail[1]) && isdigit(tail[2]))) { parfilename = filename; version = verPar1; } if (DiskFile::FileExists(filename)) { result = true; } } // If we haven't figured out which version of PAR file we // are using from the file extension, then presumable the // files filename was actually the name of a data file. if (version == verUnknown) { // Check for the existence of a PAR2 of PAR file. if (DiskFile::FileExists(filename + ".par2")) { version = verPar2; parfilename = filename + ".par2"; result = true; } else if (DiskFile::FileExists(filename + ".PAR2")) { version = verPar2; parfilename = filename + ".PAR2"; result = true; } else if (DiskFile::FileExists(filename + ".par")) { version = verPar1; parfilename = filename + ".par"; result = true; } else if (DiskFile::FileExists(filename + ".PAR")) { version = verPar1; parfilename = filename + ".PAR"; result = true; } } } else { parfilename = DiskFile::GetCanonicalPathname(filename); version = verPar2; result = true; } return result; } par2cmdline-turbo-1.4.0/src/commandline.h000066400000000000000000000205041514221355600203000ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __COMMANDLINE_H__ #define __COMMANDLINE_H__ #include // This is needed by diskfile.h #ifdef _WIN32 #ifndef NOMINMAX #define NOMINMAX #endif #include // Heap checking #ifdef _MSC_VER #define _CRTDBG_MAP_ALLOC #include #define DEBUG_NEW new(_NORMAL_BLOCK, THIS_FILE, __LINE__) #endif #else #endif #include "libpar2.h" #include "diskfile.h" // The CommandLine object is responsible for understanding the format // of the command line parameters are parsing the command line to // extract details as to what the user wants to do. class CommandLine { public: CommandLine(void); // Parse the supplied command line arguments. bool Parse(int argc, const char * const *argv); static void showversion(void); static void banner(void); // Display details of the correct format for command line parameters. static void usage(void); // What operation will we be carrying out typedef enum { opNone = 0, opCreate, // Create new PAR2 recovery volumes opVerify, // Verify but don't repair damaged data files opRepair // Verify and if possible repair damaged data files } Operation; typedef enum { verUnknown = 0, verPar1, // Processing PAR 1.0 files verPar2 // Processing PAR 2.0 files } Version; public: // Accessor functions for the command line parameters CommandLine::Operation GetOperation(void) const {return operation;} CommandLine::Version GetVersion(void) const {return version;} u64 GetBlockSize(void) const {return blocksize;} u32 GetFirstRecoveryBlock(void) const {return firstblock;} u32 GetRecoveryFileCount(void) const {return recoveryfilecount;} u32 GetRecoveryBlockCount(void) const {return recoveryblockcount;} Scheme GetRecoveryFileScheme(void) const {return recoveryfilescheme;} size_t GetMemoryLimit(void) const {return memorylimit;} NoiseLevel GetNoiseLevel(void) const {return noiselevel;} std::string GetParFilename(void) const {return parfilename;} std::string GetBasePath(void) const {return basepath;} const std::vector& GetExtraFiles(void) const {return extrafiles;} bool GetPurgeFiles(void) const {return purgefiles;} bool GetRenameOnly(void) const {return renameonly;} bool GetRecursive(void) const {return recursive;} bool GetSkipData(void) const {return skipdata;} u64 GetSkipLeaway(void) const {return skipleaway;} u32 GetNumThreads(void) {return nthreads;} u32 GetFileThreads(void) {return filethreads;} static bool ComputeRecoveryBlockCount(u32 *recoveryblockcount, u32 sourceblockcount, u64 blocksize, u32 firstblock, Scheme recoveryfilescheme, u32 recoveryfilecount, bool recoveryblockcountset, u32 redundancy, u64 redundancysize, u64 largestfilesize); protected: // Read the text of arguments into the class's variables bool ReadArgs(int argc, const char * const *argv); // Returns the memory on the system in BYTES // (or 0 if it cannot be determined) u64 GetTotalPhysicalMemory(); // Check values that were set during ReadArgs. // If values went unset, set them with default values bool CheckValuesAndSetDefaults(); // Use values like block count to compute the block size bool ComputeBlockSize(); // Use values like % recovery data to compute the number of recover blocks bool ComputeRecoveryBlockCount(); bool SetParFilename(std::string filename); FileSizeCache filesize_cache;// Caches the size of each file, // to prevent multiple calls to OS. // options for all operations Version version; // What version files will be processed. NoiseLevel noiselevel; // How much display output should there be. size_t memorylimit; // How much memory is permitted to be used // for the output buffer when creating // or repairing. std::string basepath; // the path par2 is run from u32 nthreads; // Default number of threads u32 filethreads; // Number of threads for file processing // NOTE: using the "-t" option to set the number of threads does not // end up here, but results in a direct call to "omp_set_num_threads" std::string parfilename; // The name of the PAR2 file to create, or // the name of the first PAR2 file to read // when verifying or repairing. std::list rawfilenames; // The filenames on command-line // (after expanding wildcards like '*') std::vector extrafiles; // The filenames that will be used by Par. // These have been verified to exist, // have a path-name relative to the // basepath, etc.. When creating, these will be // the source files, and when verifying or // repairing, this will be additional PAR2 // files or data files to be examined. // which operation Operation operation; // The operation to be carried out. // options for verify/repair operation bool purgefiles; // purge backup and par files on success // recovery bool renameonly; // Only attempt to repair via rename, skip // files that are not perfect matches bool skipdata; // Whether we should assume that all good // data blocks are within +/- bytes of // where we expect to find them and should // skip data that is too far away. u64 skipleaway; // The maximum leaway +/- that we will // allow when searching for blocks. // options for creating par files u32 blockcount; // How many blocks the source files should // be virtually split into. u64 blocksize; // What virtual block size to use. u32 firstblock; // What the exponent value for the first // recovery block will be. Scheme recoveryfilescheme; // How the size of the recovery files should // be calculated. u32 recoveryfilecount; // How many recovery files should be created. u32 recoveryblockcount; // How many recovery blocks should be created. bool recoveryblockcountset; // Set if the recoveryblockcount as been specified u32 redundancy; // What percentage of recovery data should // be created. u64 redundancysize; // target filesize of recovery files bool redundancyset; // Set if the redundancy has been specified bool recursive; // recurse into subdirectories }; #endif // __COMMANDLINE_H__ par2cmdline-turbo-1.4.0/src/commandline_test.cpp000066400000000000000000001431541514221355600217010ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include #include "commandline.h" // ComputeRecoveryBlockCount // recoveryblockset = true int test5() { u32 recoveryblockcount = 42; bool success = CommandLine::ComputeRecoveryBlockCount(&recoveryblockcount, 100, 4, 0, scVariable, 0, true, 0, 0, 40); if (!success) { std::cerr << "ComputeRecoveryBlockCount failed test5.1" << std::endl; return 1; } if (recoveryblockcount != 42) { std::cerr << "ComputeRecoveryBlockCount should not overwrite recoveryblockcount" << std::endl; std::cerr << " it returned " << recoveryblockcount << std::endl; return 1; } recoveryblockcount = 66000; success = CommandLine::ComputeRecoveryBlockCount(&recoveryblockcount, 100, 4, 0, scVariable, 0, true, 0, 0, 40); if (success) { std::cerr << "ComputeRecoveryBlockCount should fail for too many blocks" << std::endl; return 1; } recoveryblockcount = 6000; success = CommandLine::ComputeRecoveryBlockCount(&recoveryblockcount, 100, 4, 60000, scVariable, 0, true, 0, 0, 40); if (success) { std::cerr << "ComputeRecoveryBlockCount should fail for too high a coefficient" << std::endl; return 1; } return 0; } // ComputeRecoveryBlockCount // redundancy > 0 int test6() { u32 recoveryblockcount = 0; bool success = CommandLine::ComputeRecoveryBlockCount(&recoveryblockcount, 100, 4, 0, scVariable, 0, false, 1, 0, 40); if (!success) { std::cerr << "ComputeRecoveryBlockCount failed test5.1" << std::endl; return 1; } if (recoveryblockcount != 1) { std::cerr << "ComputeRecoveryBlockCount 1% of 100 is 1" << std::endl; std::cerr << " it returned " << recoveryblockcount << std::endl; return 1; } recoveryblockcount = 0; success = CommandLine::ComputeRecoveryBlockCount(&recoveryblockcount, 1000, 4, 0, scVariable, 0, false, 5, 0, 40); if (!success) { std::cerr << "ComputeRecoveryBlockCount failed test5.1" << std::endl; return 1; } if (recoveryblockcount != 50) { std::cerr << "ComputeRecoveryBlockCount 5% of 1000 is 50" << std::endl; std::cerr << " it returned " << recoveryblockcount << std::endl; return 1; } recoveryblockcount = 0; success = CommandLine::ComputeRecoveryBlockCount(&recoveryblockcount, 10, 4, 0, scVariable, 0, false, 1, 0, 40); if (!success) { std::cerr << "ComputeRecoveryBlockCount failed test5.1" << std::endl; return 1; } if (recoveryblockcount != 1) { std::cerr << "ComputeRecoveryBlockCount 1% of 10 is still positive" << std::endl; std::cerr << " it returned " << recoveryblockcount << std::endl; return 1; } return 0; } // ComputeRecoveryBlockCount // redundnacysize > 0 int test7_helper(int sourcefilecount, // not used by ComputeRecoveryBlockCount! int sourceblockcount, int blocksize, int redundancysize, int recoveryfilecount) { // overhead from packet headers, formatting, etc. // fixed: main packet header, main packet contents const int overhead_fixed = 76; // per source file: main packet contents, file desc, file slices header const int overhead_persourcefile = 236; // 216 + filename length // per source block: file slices contents const int overhead_persourceblock = 20; // per recovery bloc: recovery block header const int overhead_perrecoveryblock = 68; u32 recoveryblockcount; bool success = CommandLine::ComputeRecoveryBlockCount(&recoveryblockcount, sourceblockcount, blocksize, 0, scUniform, recoveryfilecount, false, 0, redundancysize, blocksize); if (!success) { std::cerr << "ComputeRecoveryBlockCount failed test5.1" << std::endl; return 1; } int usage_per_recoveryfile = overhead_fixed + sourcefilecount * overhead_persourcefile + sourceblockcount * overhead_persourceblock; int usage = recoveryfilecount * usage_per_recoveryfile + recoveryblockcount * (overhead_perrecoveryblock + blocksize); if (usage <= redundancysize - (overhead_perrecoveryblock + blocksize) || usage > redundancysize) { std::cerr << "ComputeRecoveryBlockCount " << redundancysize << " data limit" << std::endl; std::cerr << " but usage was " << usage << " with " << recoveryblockcount << " blocks" << std::endl; std::cerr << " sourcefilecount=" << sourcefilecount << std::endl; std::cerr << " sourceblockcount=" << sourceblockcount << std::endl; std::cerr << " blocksize=" << blocksize << std::endl; std::cerr << " recoveryfilecount=" << recoveryfilecount << std::endl; return 2; } return 0; } // ComputeRecoveryBlockCount // redundnacysize > 0 // scUniform with number of recovery files already determined. int test7() { // CD is 10 files, 600 source blocks, 1MB block size // Redundancys is 40MB in 5 files. //sourcefilecount, sourceblockcount, blocksize, redundancysize, recoveryfilecount if (test7_helper(10, 600, 1024*1024, 40*1024*1024, 5)) return 1; if (test7_helper( 1, 600, 1024*1024, 40*1024*1024, 5)) return 1; if (test7_helper(10, 60, 1024*1024, 40*1024*1024, 5)) return 1; if (test7_helper(10, 600, 16*1024, 40*1024*1024, 5)) return 1; if (test7_helper(10, 600, 1024*1024, 10*1024*1024, 5)) return 1; if (test7_helper(10, 600, 1024*1024, 40*1024*1024, 2)) return 1; // DVD is 1 files, 5000 source blocks, 1MB block size // Redundancys is 50MB in 5 files. if (test7_helper(1, 5000, 1024*1024, 50*1024*1024, 5) == 1) return 1; // if redundancy size is too small, still have 1 block u32 recoveryblockcount; bool success = CommandLine::ComputeRecoveryBlockCount(&recoveryblockcount, 1000, 1024, 0, scUniform, 1, false, 0, 4, // = redundancysize 1024); if (!success) { std::cerr << "ComputeRecoveryBlockCount failed test5.1" << std::endl; return 1; } if (recoveryblockcount != 1) { std::cerr << "ComputeRecoveryBlockCount with small redundancy amount should still return 1" << std::endl; std::cerr << " it returned " << recoveryblockcount << std::endl; return 1; } return 0; } // ComputeRecoveryBlockCount // redundnacysize > 0 int test8_helper(int sourcefilecount, // not used by ComputeRecoveryBlockCount! int sourceblockcount, int blocksize, int redundancysize) { // overhead from packet headers, formatting, etc. // fixed: main packet header, main packet contents const int overhead_fixed = 76; // per source file: main packet contents, file desc, file slices header const int overhead_persourcefile = 236; // 216 + filename length // per source block: file slices contents const int overhead_persourceblock = 20; // per recovery bloc: recovery block header const int overhead_perrecoveryblock = 68; u32 recoveryblockcount; bool success = CommandLine::ComputeRecoveryBlockCount(&recoveryblockcount, sourceblockcount, blocksize, 0, scVariable, 0, // =recoveryfilecount false, 0, redundancysize, blocksize); if (!success) { std::cerr << "ComputeRecoveryBlockCount failed test8.1" << std::endl; return 1; } u32 recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scVariable, recoveryblockcount, blocksize, blocksize); if (!success) { std::cerr << "ComputeRecoveryBlockCount failed test8.2" << std::endl; return 1; } int usage_per_recoveryfile = overhead_fixed + sourcefilecount * overhead_persourcefile + sourceblockcount * overhead_persourceblock; int usage = recoveryfilecount * usage_per_recoveryfile + recoveryblockcount * (overhead_perrecoveryblock + blocksize); if (usage <= redundancysize - (overhead_perrecoveryblock + blocksize) || usage > redundancysize) { std::cerr << "ComputeRecoveryBlockCount " << redundancysize << " data limit" << std::endl; std::cerr << " but usage was " << usage << " with " << recoveryblockcount << " blocks" << std::endl; std::cerr << " sourcefilecount=" << sourcefilecount << std::endl; std::cerr << " sourceblockcount=" << sourceblockcount << std::endl; std::cerr << " blocksize=" << blocksize << std::endl; std::cerr << " recoveryfilecount=" << recoveryfilecount << std::endl; return 2; } return 0; } // ComputeRecoveryBlockCount // redundnacysize > 0 // scVariable with number of recovery files undetermined int test8() { //sourcefilecount, sourceblockcount, blocksize, redundancysize if (test8_helper(10, 600, 1024*1024, 40*1024*1024)) return 1; if (test8_helper( 1, 600, 1024*1024, 40*1024*1024)) return 1; if (test8_helper(10, 60, 1024*1024, 40*1024*1024)) return 1; // // WARNING: THIS TEST USUALLY FAILS BY A SMALL AMOUNT // The ==1 at the end will make the test fail // if the function doesn't return any value. // if (test8_helper(10, 600, 16*1024, 40*1024*1024) == 1) return 1; if (test8_helper(10, 600, 1024*1024, 10*1024*1024)) return 1; if (test8_helper(1, 5000, 1024*1024, 50*1024*1024)) return 1; return 0; } // calls Parse and expects it to fail. // "arg" is command; spaces are used to separate argv words... int test9_helper(const char *arg) { // copy args into argc/argv format // buffer holds copy of arg, with ' ' replaced by '\0' const int len = strlen(arg); int argc = 0; char *buffer = new char[len+1]; const char **argv = new const char*[len]; argv[argc]=&(buffer[0]); argc++; for (int i = 0; i < len; i++) { buffer[i] = arg[i]; if (buffer[i] == ' ') { buffer[i] = '\0'; argv[argc] = &(buffer[i+1]); argc++; } } buffer[len] = '\0'; CommandLine commandline; if (commandline.Parse(argc, argv)) { std::cout << "CommandLine should not have parsed: \"" << arg << "\"" << std::endl; return 1; } delete [] buffer; delete [] argv; return 0; } int test9() { // create input files, in case they are read. std::ofstream par2file; par2file.open("foo.par2"); par2file << "dummy par2 file. Just has to exist."; par2file.close(); std::ofstream par2file_bar; par2file_bar.open("bar.par2"); par2file_bar << "dummy par2 file. Just has to exist."; par2file_bar.close(); std::ofstream input1; input1.open("input1.txt"); input1 << "commandline_test test9 input1.txt\n"; input1.close(); std::ofstream input2; input2.open("input2.txt"); input2 << "commandline_test test9 input2.txt\n"; input2.close(); // mistaken call for help, version, etc. if (test9_helper("par2 -?")) return 1; if (test9_helper("par2 -v")) return 1; // missing operation if (test9_helper("par2")) return 1; if (test9_helper("par2 repairfoo.par")) return 1; if (test9_helper("par2 -p repairfoo.par")) return 1; if (test9_helper("par2 -b100 createfoo.par")) return 1; // missing parfile if (test9_helper("par2 repair")) return 1; if (test9_helper("par2repair")) return 1; if (test9_helper("par2 create")) return 1; if (test9_helper("par2create")) return 1; if (test9_helper("par2verify")) return 1; if (test9_helper("par2 verify")) return 1; if (test9_helper("par2 create ")) return 1; // missing input files for create if (test9_helper("par2 create foo.par2")) return 1; // wrong options for the chosen operation if (test9_helper("par2 create -p foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -N foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -S100 foo.par2 input1.txt input2.txt")) return 1; // DO WE WANT TO ERROR OUT ON THIS CASE? // if (test9_helper("par2 repair -abar.par2 foo.par2")) // return 1; if (test9_helper("par2 repair -b100 foo.par2")) return 1; if (test9_helper("par2 repair -s100 foo.par2")) return 1; if (test9_helper("par2 repair -r5 foo.par2")) return 1; if (test9_helper("par2 repair -rg5 foo.par2")) return 1; if (test9_helper("par2 repair -c100 foo.par2")) return 1; if (test9_helper("par2 repair -f10 foo.par2")) return 1; if (test9_helper("par2 repair -u foo.par2")) return 1; if (test9_helper("par2 repair -l foo.par2")) return 1; if (test9_helper("par2 repair -n10 foo.par2")) return 1; if (test9_helper("par2 repair -R foo.par2")) return 1; // Should "-v" cause an error (for being in the wrong place) // or be treated as a filename? //if (test9_helper("par2 create foo.par2 input1.txt input2.txt -v")) // return 1; // bad combinations of options if (test9_helper("par2 create -v -q foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -q -v foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -v -v -q foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -q -q -v foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -b100 -s100 foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -rg5 -c100 foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -u -l foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -n10 -l foo.par2 input1.txt input2.txt")) return 1; // badly formatted options if (test9_helper("par2 create -zzz foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create --v foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -s3 foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -m50m foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -rt5 foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -bad foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -sad foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -rad foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -cad foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -fad foo.par2 input1.txt input2.txt")) return 1; if (test9_helper("par2 create -nad foo.par2 input1.txt input2.txt")) return 1; // delete files that were created at start of test. remove("foo.par2"); remove("bar.par2"); remove("input1.txt"); remove("input2.txt"); return 0; } // calls Parse and expects it to call create // "arg" is command; spaces are used to separate argv words... int test10_helper(const char *arg, const NoiseLevel noiselevel, const size_t memorylimit, const std::string &basepath, const u32 nthreads, const u32 filethreads, const std::string &parfilename, const std::vector &extrafiles, const u64 blocksize, const u32 firstblock, const Scheme recoveryfilescheme, const u32 recoveryfilecount, const u32 recoveryblockcount ) { // copy args into argc/argv format // buffer holds copy of arg, with ' ' replaced by '\0' const int len = strlen(arg); int argc = 0; char *buffer = new char[len+1]; const char **argv = new const char*[len]; argv[argc]=&(buffer[0]); argc++; for (int i = 0; i < len; i++) { buffer[i] = arg[i]; if (buffer[i] == ' ') { buffer[i] = '\0'; argv[argc] = &(buffer[i+1]); argc++; } } buffer[len] = '\0'; CommandLine commandline; if (!commandline.Parse(argc, argv)) { std::cout << "CommandLine said it was unable to parse \"" << arg << "\"" << std::endl; return 1; } if (commandline.GetVersion() != CommandLine::verPar2) { std::cout << "test10 fail version arg=" << arg << std::endl; std::cout << commandline.GetVersion() << " != " << CommandLine::verPar2 << std::endl; return 1; } if (commandline.GetOperation() != CommandLine::opCreate) { std::cout << "test10 fail operation arg=" << arg << std::endl; std::cout << commandline.GetOperation() << " != " << CommandLine::opCreate << std::endl; return 1; } if (commandline.GetNoiseLevel() != noiselevel) { std::cout << "test10 fail noiselevel arg=" << arg << std::endl; std::cout << commandline.GetNoiseLevel() << " != " << noiselevel << std::endl; return 1; } if (commandline.GetMemoryLimit() != memorylimit) { std::cout << "test10 fail memorylimit arg=" << arg << std::endl; std::cout << commandline.GetMemoryLimit() << " != " << memorylimit << std::endl; return 1; } if (commandline.GetBasePath() != basepath) { std::cout << "test10 fail basepath arg=" << arg << std::endl; std::cout << commandline.GetBasePath() << " != " << basepath << std::endl; return 1; } if (commandline.GetNumThreads() != nthreads) { std::cout << "test10 fail nthreads arg=" << arg << std::endl; std::cout << commandline.GetNumThreads() << " != " << nthreads << std::endl; return 1; } if (commandline.GetFileThreads() != filethreads) { std::cout << "test10 fail filethreads arg=" << arg << std::endl; std::cout << commandline.GetFileThreads() << " != " << filethreads << std::endl; return 1; } if (commandline.GetParFilename() != parfilename) { std::cout << "test10 fail parfilename arg=" << arg << std::endl; std::cout << commandline.GetParFilename() << " != " << parfilename << std::endl; return 1; } const std::vector &extrafiles_returned = commandline.GetExtraFiles(); if (extrafiles_returned.size() != extrafiles.size()) { std::cout << "test10 fail extrafiles.size() arg=" << arg << std::endl; std::cout << extrafiles_returned.size() << " != " << extrafiles.size() << std::endl; return 1; } for (unsigned int i = 0; i < extrafiles.size(); i++) { if (extrafiles_returned[i] != extrafiles[i]) { std::cout << "test10 fail extrafiles[" << i << "] arg=" << arg << std::endl; std::cout << extrafiles_returned[i] << " != " << extrafiles[i] << std::endl; return 1; } } if (commandline.GetBlockSize() != blocksize) { std::cout << "test10 fail blocksize arg=" << arg << std::endl; std::cout << commandline.GetBlockSize() << " != " << blocksize << std::endl; return 1; } if (commandline.GetFirstRecoveryBlock() != firstblock) { std::cout << "test10 fail firstblock arg=" << arg << std::endl; std::cout << commandline.GetFirstRecoveryBlock() << " != " << firstblock << std::endl; return 1; } if (commandline.GetRecoveryFileScheme() != recoveryfilescheme) { std::cout << "test10 fail recoveryfilescheme arg=" << arg << std::endl; std::cout << commandline.GetRecoveryFileScheme() << " != " << recoveryfilescheme << std::endl; return 1; } if (commandline.GetRecoveryFileCount() != recoveryfilecount) { std::cout << "test10 fail recoveryfilecount arg=" << arg << std::endl; std::cout << commandline.GetRecoveryFileCount() << " != " << recoveryfilecount << std::endl; return 1; } if (commandline.GetRecoveryBlockCount() != recoveryblockcount) { std::cout << "test10 fail recoveryblockcount arg=" << arg << std::endl; std::cout << commandline.GetRecoveryBlockCount() << " != " << recoveryblockcount << std::endl; return 1; } delete [] buffer; delete [] argv; return 0; } // Test calls to "par2 create" int test10() { // create input files, in case they are read. std::ofstream input1; input1.open("input1.txt"); const char *input1_contents = "commandline_test test10 input1.txt\n"; input1 << input1_contents; input1.close(); std::ofstream input2; input2.open("input2.txt"); const char *input2_contents = "commandline_test test10 input2.txt\n"; input2 << input2_contents; input2.close(); // Call once. The results are used to initialize some defaults. int argc_for_defaults = 5; const char *argv_for_defaults[5] = {"par2", "create", "foo.par2", "input1.txt", "input2.txt"}; CommandLine commandline_for_defaults; if (!commandline_for_defaults.Parse(argc_for_defaults, argv_for_defaults)) { std::cout << "CommandLine unable to fetch default values." << std::endl; return 1; } // Define the default values // (Using variable names makes it more obvious when // a non-default value is present in test.) const NoiseLevel default_noiselevel = nlNormal; const size_t default_memorylimit = commandline_for_defaults.GetMemoryLimit(); const std::string &default_basepath = commandline_for_defaults.GetBasePath(); const u32 default_nthreads = 0; const u32 default_filethreads = _FILE_THREADS; std::string default_parfilename = default_basepath + "foo"; // ".par2" is stripped. std::vector default_extrafiles; default_extrafiles.push_back(default_basepath + "input1.txt"); default_extrafiles.push_back(default_basepath + "input2.txt"); const u64 default_blocksize = 4; // tries to make 2000 blocks ... this is closest blocksize const u32 default_firstblock = 0; const Scheme default_recoveryfilescheme = scVariable; const u32 default_recoveryfilecount = 0; const u32 default_recoveryblockcount = 1; // tries to do 5% recovery data ... this is minimum value if (test10_helper("par2 create foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } // TODO: -B option // -v if (test10_helper("par2 create -v foo.par2 input1.txt input2.txt", nlNoisy, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } // -v -v if (test10_helper("par2 create -v -v foo.par2 input1.txt input2.txt", nlDebug, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } // -q if (test10_helper("par2 create -q foo.par2 input1.txt input2.txt", nlQuiet, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } // -q -q if (test10_helper("par2 create -q -q foo.par2 input1.txt input2.txt", nlSilent, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } // -m option if (test10_helper("par2 create -m16 foo.par2 input1.txt input2.txt", default_noiselevel, 16*1024*1024, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } // -t option if (test10_helper("par2 create -t42 foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, 42, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } // -T option if (test10_helper("par2 create -T42 foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, 42, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } // -- option if (test10_helper("par2 create -- foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } if (test10_helper("par2 create -- -foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_basepath + "-foo", default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } {// scope to hide variables std::ofstream dashinput2; dashinput2.open("-input2.txt"); dashinput2 << "commandline_test test10 -input2.txt\n"; dashinput2.close(); std::vector extrafiles; extrafiles.push_back(default_basepath + "input1.txt"); extrafiles.push_back(default_basepath + "-input2.txt"); if (test10_helper("par2 create foo.par2 input1.txt -- -input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } // delete file that was created. remove("-input2.txt"); } if (test10_helper("par2 create -afoo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } int longestfilelen = std::max(strlen(input1_contents), strlen(input2_contents)); int longestfilelen_rounded_up = longestfilelen; if (longestfilelen_rounded_up % 4 != 0) { longestfilelen_rounded_up += 4 - (longestfilelen_rounded_up % 4); } if (test10_helper("par2 create -b2 foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, longestfilelen_rounded_up, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } if (test10_helper("par2 create -s8 foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, 8, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } int default_sourceblockcount = 0; default_sourceblockcount += strlen(input1_contents)/default_blocksize; if (strlen(input1_contents) % default_blocksize != 0) default_sourceblockcount++; default_sourceblockcount += strlen(input2_contents)/default_blocksize; if (strlen(input2_contents) % default_blocksize != 0) default_sourceblockcount++; if (test10_helper("par2 create -r100 foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, default_sourceblockcount)) { // recoverycount=sourcecount return 1; } const int overhead_fixed = 76; // per source file: main packet contents, file desc, file slices header const int overhead_persourcefile = 216 + 12; // 216 + filename length // per source block: file slices contents const int overhead_persourceblock = 20; // per recovery bloc: recovery block header const int overhead_perrecoveryblock = 68; int default_sourcefilecount = 2; int optionr_recoveryfilecount = 4; // computed commandline, but not returned. int optionr_recoveryblockcount = 8; int usage_per_recoveryfile = overhead_fixed + default_sourcefilecount * overhead_persourcefile + default_sourceblockcount * overhead_persourceblock; int usage = optionr_recoveryfilecount * usage_per_recoveryfile + optionr_recoveryblockcount * (overhead_perrecoveryblock + default_blocksize); if (usage > 1024) { std::cout << "Test10 -rk1 should fail because usage limit was 1024 bytes. Actual usage was " << usage << std::endl; //return 1; } if (test10_helper("par2 create -rk1 foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, optionr_recoveryblockcount)) { return 1; } if (test10_helper("par2 create -c42 foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, default_recoveryfilescheme, default_recoveryfilecount, 42)) { return 1; } if (test10_helper("par2 create -f42 foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, 42, default_recoveryfilescheme, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } if (test10_helper("par2 create -u foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, scUniform, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } if (test10_helper("par2 create -l foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, scLimited, default_recoveryfilecount, default_recoveryblockcount)) { return 1; } if (test10_helper("par2 create -n31 foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, default_blocksize, default_firstblock, scUniform, // when recovery file number is set the scheme is uniform 31, default_recoveryblockcount)) { return 1; } // TODO: Test "-R" option (Recurse into subdirectories) // delete files that were created at start of test. remove("input1.txt"); remove("input2.txt"); return 0; } // calls Parse and expects it to call repair/verify // "arg" is command; spaces are used to separate argv words... int test11_helper(const char *arg, const NoiseLevel noiselevel, const size_t memorylimit, const std::string &basepath, const u32 nthreads, const u32 filethreads, const std::string &parfilename, const std::vector &extrafiles, const CommandLine::Version version, const CommandLine::Operation operation, const bool purgefiles, const bool skipdata, const u64 skipleaway ) { // copy args into argc/argv format // buffer holds copy of arg, with ' ' replaced by '\0' const int len = strlen(arg); int argc = 0; char *buffer = new char[len+1]; const char **argv = new const char*[len]; argv[argc]=&(buffer[0]); argc++; for (int i = 0; i < len; i++) { buffer[i] = arg[i]; if (buffer[i] == ' ') { buffer[i] = '\0'; argv[argc] = &(buffer[i+1]); argc++; } } buffer[len] = '\0'; CommandLine commandline; if (!commandline.Parse(argc, argv)) { std::cout << "CommandLine said it was unable to parse \"" << arg << "\"" << std::endl; return 1; } if (commandline.GetVersion() != version) { std::cout << "test11 fail version arg=" << arg << std::endl; std::cout << commandline.GetVersion() << " != " << version << std::endl; return 1; } if (commandline.GetOperation() != operation) { std::cout << "test11 fail operation arg=" << arg << std::endl; std::cout << commandline.GetOperation() << " != " << operation << std::endl; return 1; } if (commandline.GetNoiseLevel() != noiselevel) { std::cout << "test11 fail noiselevel arg=" << arg << std::endl; std::cout << commandline.GetNoiseLevel() << " != " << noiselevel << std::endl; return 1; } if (commandline.GetMemoryLimit() != memorylimit) { std::cout << "test11 fail memorylimit arg=" << arg << std::endl; std::cout << commandline.GetMemoryLimit() << " != " << memorylimit << std::endl; return 1; } if (commandline.GetBasePath() != basepath) { std::cout << "test11 fail basepath arg=" << arg << std::endl; std::cout << commandline.GetBasePath() << " != " << basepath << std::endl; return 1; } if (commandline.GetNumThreads() != nthreads) { std::cout << "test11 fail nthreads arg=" << arg << std::endl; std::cout << commandline.GetNumThreads() << " != " << nthreads << std::endl; return 1; } if (commandline.GetFileThreads() != filethreads) { std::cout << "test11 fail filethreads arg=" << arg << std::endl; std::cout << commandline.GetFileThreads() << " != " << filethreads << std::endl; return 1; } if (commandline.GetParFilename() != parfilename) { std::cout << "test11 fail parfilename arg=" << arg << std::endl; std::cout << commandline.GetParFilename() << " != " << parfilename << std::endl; return 1; } const std::vector &extrafiles_returned = commandline.GetExtraFiles(); if (extrafiles_returned.size() != extrafiles.size()) { std::cout << "test11 fail extrafiles.size() arg=" << arg << std::endl; std::cout << extrafiles_returned.size() << " != " << extrafiles.size() << std::endl; return 1; } for (unsigned int i = 0; i < extrafiles.size(); i++) { if (extrafiles_returned[i] != extrafiles[i]) { std::cout << "test11 fail extrafiles[" << i << "] arg=" << arg << std::endl; std::cout << extrafiles_returned[i] << " != " << extrafiles[i] << std::endl; return 1; } } if (commandline.GetPurgeFiles() != purgefiles) { std::cout << "test11 fail purgefiles arg=" << arg << std::endl; std::cout << commandline.GetPurgeFiles() << " != " << purgefiles << std::endl; return 1; } if (version == CommandLine::verPar2) { if (commandline.GetSkipData() != skipdata) { std::cout << "test11 fail skipdata arg=" << arg << std::endl; std::cout << commandline.GetSkipData() << " != " << skipdata << std::endl; return 1; } if (commandline.GetSkipLeaway() != skipleaway) { std::cout << "test11 fail skipleaway arg=" << arg << std::endl; std::cout << commandline.GetSkipLeaway() << " != " << skipleaway << std::endl; return 1; } } delete [] buffer; delete [] argv; return 0; } // test calls to repair/verify int test11() { // create input files, in case they are read. std::ofstream par2file; par2file.open("foo.par2"); par2file << "dummy par2 file. Just has to exist."; par2file.close(); std::ofstream input1; input1.open("input1.txt"); const char *input1_contents = "commandline_test test11 input1.txt\n"; input1 << input1_contents; input1.close(); std::ofstream input2; input2.open("input2.txt"); const char *input2_contents = "commandline_test test11 input2.txt\n"; input2 << input2_contents; input2.close(); // Call once. The results are used to initialize some defaults. int argc_for_defaults = 5; const char *argv_for_defaults[5] = {"par2", "repair", "foo.par2", "input1.txt", "input2.txt"}; CommandLine commandline_for_defaults; if (!commandline_for_defaults.Parse(argc_for_defaults, argv_for_defaults)) { std::cout << "CommandLine unable to fetch default values." << std::endl; return 1; } // Define the default values // (Using variable names makes it more obvious when // a non-default value is present in test.) const NoiseLevel default_noiselevel = nlNormal; const size_t default_memorylimit = commandline_for_defaults.GetMemoryLimit(); const std::string &default_basepath = commandline_for_defaults.GetBasePath(); const u32 default_nthreads = 0; const u32 default_filethreads = _FILE_THREADS; std::string default_parfilename = "foo.par2"; // relative path, par2 is NOT stripped. std::vector default_extrafiles; default_extrafiles.push_back(default_basepath + "input1.txt"); default_extrafiles.push_back(default_basepath + "input2.txt"); bool default_purgefiles = false; bool default_skipdata = false; u64 default_skipleaway = 0; if (test11_helper("par2 repair foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opRepair, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } // TODO: -B option // -v if (test11_helper("par2 repair -v foo.par2 input1.txt input2.txt", nlNoisy, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opRepair, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } if (test11_helper("par2 verify -v foo.par2 input1.txt input2.txt", nlNoisy, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opVerify, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } // -v -v if (test11_helper("par2 repair -v -v foo.par2 input1.txt input2.txt", nlDebug, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opRepair, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } // -q if (test11_helper("par2 repair -q foo.par2 input1.txt input2.txt", nlQuiet, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opRepair, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } // -q -q if (test11_helper("par2 repair -q -q foo.par2 input1.txt input2.txt", nlSilent, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opRepair, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } // -m if (test11_helper("par2 repair -m16 foo.par2 input1.txt input2.txt", default_noiselevel, 16*1024*1024, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opRepair, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } // -t if (test11_helper("par2 repair -t42 foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, 42, default_filethreads, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opRepair, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } // -T if (test11_helper("par2 repair -T42 foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, 42, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opRepair, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } // -- if (test11_helper("par2 repair -- foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opRepair, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } {// scope to hide variables std::ofstream dashpar2file; dashpar2file.open("-foo.par2"); dashpar2file << "anything\n"; dashpar2file.close(); if (test11_helper("par2 repair -- -foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, "-foo.par2", default_extrafiles, CommandLine::verPar2, CommandLine::opRepair, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } remove("-foo.par2"); } { // scope to hide variables std::ofstream dashinput2; dashinput2.open("-input2.txt"); dashinput2 << "commandline_test test11 -input2.txt\n"; dashinput2.close(); std::vector extrafiles; extrafiles.push_back(default_basepath + "input1.txt"); extrafiles.push_back(default_basepath + "-input2.txt"); if (test11_helper("par2 repair foo.par2 input1.txt -- -input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, extrafiles, CommandLine::verPar2, CommandLine::opRepair, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } // delete file that was created. remove("-input2.txt"); } // -p if (test11_helper("par2 repair -p foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opRepair, true, default_skipdata, default_skipleaway)) { return 1; } // Do we want -p with verify?? if (test11_helper("par2 verify -p foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opVerify, true, default_skipdata, default_skipleaway)) { return 1; } // -N if (test11_helper("par2 repair -N foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opRepair, default_purgefiles, true, 64) // this value should be a named constant somewhere. ) { return 1; } // -S if (test11_helper("par2 repair -N -S42 foo.par2 input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, default_parfilename, default_extrafiles, CommandLine::verPar2, CommandLine::opRepair, default_purgefiles, true, 42)) { return 1; } // remove par2 file remove("foo.par2"); // Create par1 file. Do a few tests. // std::ofstream par1file; par1file.open("bar.par"); par1file << "dummy par1 file. Just has to exist."; par1file.close(); if (test11_helper("par2 repair bar.par input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, "bar.par", default_extrafiles, CommandLine::verPar1, CommandLine::opRepair, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } if (test11_helper("par2 verify bar.par input1.txt input2.txt", default_noiselevel, default_memorylimit, default_basepath, default_nthreads, default_filethreads, "bar.par", default_extrafiles, CommandLine::verPar1, CommandLine::opVerify, default_purgefiles, default_skipdata, default_skipleaway)) { return 1; } remove("bar.par"); remove("input1.txt"); remove("input2.txt"); return 0; } // test calls to print help, version int test12() { int argc_for_help = 2; const char *argv_for_help[5] = {"par2", "--help"}; CommandLine commandline_for_help; if (!commandline_for_help.Parse(argc_for_help, argv_for_help)) { std::cout << "CommandLine failed for help" << std::endl; return 1; } int argc_for_h = 2; const char *argv_for_h[5] = {"par2", "-h"}; CommandLine commandline_for_h; if (!commandline_for_h.Parse(argc_for_h, argv_for_h)) { std::cout << "CommandLine failed for h." << std::endl; return 1; } int argc_for_help2 = 2; const char *argv_for_help2[5] = {"par2create", "--help"}; CommandLine commandline_for_help2; if (!commandline_for_help2.Parse(argc_for_help2, argv_for_help2)) { std::cout << "CommandLine failed for help (par2create)." << std::endl; return 1; } int argc_for_version = 2; const char *argv_for_version[5] = {"par2", "--version"}; CommandLine commandline_for_version; if (!commandline_for_version.Parse(argc_for_version, argv_for_version)) { std::cout << "CommandLine failed for version." << std::endl; return 1; } int argc_for_V = 2; const char *argv_for_V[5] = {"par2", "-V"}; CommandLine commandline_for_V; if (!commandline_for_V.Parse(argc_for_V, argv_for_V)) { std::cout << "CommandLine failed for v." << std::endl; return 1; } int argc_for_VV = 2; const char *argv_for_VV[5] = {"par2", "-VV"}; CommandLine commandline_for_VV; if (!commandline_for_VV.Parse(argc_for_VV, argv_for_VV)) { std::cout << "CommandLine failed for v." << std::endl; return 1; } int argc_for_version2 = 2; const char *argv_for_version2[5] = {"par2create", "--version"}; CommandLine commandline_for_version2; if (!commandline_for_version2.Parse(argc_for_version2, argv_for_version2)) { std::cout << "CommandLine failed for version (par2create)." << std::endl; return 1; } return 0; } int main() { std::cout << "Tests 1 through 4 were moved to libpar2_test." << std::endl; if (test5()) { std::cerr << "FAILED: test5" << std::endl; return 1; } if (test6()) { std::cerr << "FAILED: test6" << std::endl; return 1; } if (test7()) { std::cerr << "FAILED: test7" << std::endl; return 1; } if (test8()) { std::cerr << "FAILED: test8" << std::endl; return 1; } if (test9()) { std::cerr << "FAILED: test9" << std::endl; return 1; } if (test10()) { std::cerr << "FAILED: test10" << std::endl; return 1; } if (test11()) { std::cerr << "FAILED: test11" << std::endl; return 1; } if (test12()) { std::cerr << "FAILED: test12" << std::endl; return 1; } std::cout << "SUCCESS: commandline_test complete." << std::endl; return 0; } par2cmdline-turbo-1.4.0/src/crc.cpp000066400000000000000000000065071514221355600171230ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif // The one and only CCITT CRC32 lookup table // // NOTE: the constant is the reversed polynomial for CRC-32 // as listed on Wikipedia's page: // https://en.wikipedia.org/wiki/Cyclic_redundancy_check crc32table ccitttable(0xEDB88320L); // GF32 multiplication #define NEGATE32(n) (u32)(-((i32)(n))) static u32 GF32Multiply(u32 a, u32 b, u32 polynomial) { u32 product = 0; for (u32 i=0; i<31; i++) { product ^= NEGATE32(b >> 31) & a; a = ((a >> 1) ^ (polynomial & NEGATE32(a & 1))); b <<= 1; } product ^= NEGATE32(b >> 31) & a; return product; } // Compute 2^(8n) in CRC's GF static u32 CRCExp8(u64 n) { u32 result = 0x80000000; u32 power = 0; n %= 0xffffffff; while (n) { if (n & 1) result = GF32Multiply(result, ccitttable.power[power], ccitttable.polynom); n >>= 1; power = (power + 1) & 31; } return result; } // Construct the CRC32 lookup table from the specified polynomial // // This seems to follow: // http://www.efg2.com/Lab/Library/UseNet/1999/0117.txt crc32table::crc32table(u32 polynomial) { polynom = polynomial; for (u32 i = 0; i <= 255 ; i++) { u32 crc = i; for (u32 j = 0; j < 8; j++) { crc = (crc >> 1) ^ ((crc & 1) ? polynomial : 0); } table[i] = crc; } // Also construct the table used for computing power // Note that the table is rotated by 3 entries, since we operate on bytes, i.e. 1<<3 bits u32 k = 0x80000000 >> 1; for (u32 i = 0; i < 32; i++) { power[(i - 3) & 31] = k; k = GF32Multiply(k, k, polynomial); } } // Construct a CRC32 lookup table for windowing void GenerateWindowTable(u64 window, u32 (&target)[256]) { // Window coefficient u32 coeff = CRCExp8(window); // Extend initial CRC to window length u32 mask = GF32Multiply(~0, coeff, ccitttable.polynom); // Xor initial CRC with that extended by one byte mask = GF32Multiply(mask, 0x80800000, ccitttable.polynom); // Since we have a table, may as well invert all bits to save doing it later mask ^= ~0; // Generate table for (i16 i=0; i<=255; i++) { target[i] = GF32Multiply(ccitttable.table[i], coeff, ccitttable.polynom) ^ mask; } } u32 CRCUpdateBlock(u32 crc, u64 length) { return GF32Multiply(crc, CRCExp8(length), ccitttable.polynom); } par2cmdline-turbo-1.4.0/src/crc.h000066400000000000000000000057121514221355600165650ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __CRC_H__ #define __CRC_H__ // These global functions are used to compute the CCITT CRC32 checksum of // blocks of data. // The CRC for a block of data may be computed piecemeal be repeatedly // calling CRCUpdateBlock. // Given the CRC for a block of data in a buffer, CRCSlideChar may be used // to quickly compute the CRC for the block of data in the buffer that is the // same size but offset one character later in the buffer. // A CRC32 lookup table struct crc32table { u32 polynom; crc32table(u32 polynomial); u32 table[256]; u32 power[32]; }; // The one and only CCITT CRC32 lookup table extern crc32table ccitttable; #include "../parpar/hasher/hasher.h" // CRC32_Calc inline u32 CRCCompute(size_t length, const void* buffer) { return CRC32_Calc(buffer, length); } // Update the CRC using a block of characters in a buffer inline u32 CRCUpdateBlock(u32 crc, size_t length, const void *buffer) { const unsigned char *current = (const unsigned char *)buffer; while (length-- > 0) { crc = ((crc >> 8) & 0x00ffffffL) ^ ccitttable.table[(u8)crc ^ (*current++)]; } return crc; } // Update the CRC using a block of 0s. u32 CRCUpdateBlock(u32 crc, u64 length); // Construct a CRC32 lookup table for windowing void GenerateWindowTable(u64 window, u32 (&windowtable)[256]); // Slide the CRC along a buffer by one character (removing the old and adding the new). // The new character is added using the main CCITT CRC32 table, and the old character // is removed using the windowtable. inline u32 CRCSlideChar(u32 crc, u8 chNew, u8 chOld, const u32 (&windowtable)[256]) { crc ^= ~0; return ((crc >> 8) & 0x00ffffffL) ^ ccitttable.table[(u8)crc ^ chNew] ^ windowtable[chOld]; } /* char *buffer; u64 window; //... u32 windowtable[256]; GenerateWindowTable(window, windowtable); u32 crc = ~0 ^ CRCUpdateBlock(~0, window, buffer); crc = CRCSlideChar(crc, buffer[window], buffer[0], windowtable); assert(crc == ~0 ^ CRCUpdateBlock(~0, window, buffer+1)); */ #endif // __CRC_H__ par2cmdline-turbo-1.4.0/src/crc_test.cpp000066400000000000000000000123561514221355600201610ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // This is just a simple set of tests on md5 hash. // The initial version just showed it was self-consistent, not accurate. // I compile with: // g++ -DHAVE_CONFIG_H -I.. crc_test.cpp crc.cpp #include "libpar2internal.h" #include #include #include "crc.h" // Example usage: // u32 checksum = CRCCompute((size_t)blocksize, buffer); // compares UpdateBlock(crc, length) to UpdateBlock(crc,buffer,buffersize) int test1() { unsigned char buffer[] = {0,0,0,0,0,0,0,0}; u32 checksum1 = CRCCompute(sizeof(buffer), buffer); u32 checksum2 = ~0 ^ CRCUpdateBlock(~0, sizeof(buffer)); if (checksum1 != checksum2) { std::cerr << "checksum1 = " << checksum1 << std::endl; std::cerr << "checksum2 = " << checksum2 << std::endl; return 1; } return 0; } // CRC32 of "123456789" yields 0xCBF43926 // according to http://www.ross.net/crc/download/crc_v3.txt int test2() { unsigned char buffer[] = "123456789"; size_t buffer_length = 9; u32 expected_checksum = 0xCBF43926u; u32 checksum1 = CRCCompute(buffer_length, buffer); if (checksum1 != expected_checksum) { std::cerr << "checksum was not precalculated value: " << std::hex << checksum1 << std::dec << std::endl; std::cerr << " expected " << std::hex << expected_checksum << std::dec << std::endl; return 1; } return 0; } // generate random data. // put it into checksum using different length blocks // make sure output is the same. int test3() { srand(345087209); unsigned char buffer[32*1024]; for (unsigned int i = 0; i < sizeof(buffer); i++) { buffer[i] = (unsigned char) (rand() % 256); } u32 checksum1 = ~0; unsigned int offset = 0; while (offset < sizeof(buffer)) { unsigned int length = (unsigned int) (rand() % 256); if (offset + length > sizeof(buffer)) length = sizeof(buffer) - offset; checksum1 = CRCUpdateBlock(checksum1, length, buffer + offset); offset += length; } checksum1 = ~0 ^ checksum1; u32 checksum2 = ~0; offset = 0; while (offset < sizeof(buffer)) { unsigned int length = (unsigned int) (rand() % 256); if (offset + length > sizeof(buffer)) length = sizeof(buffer) - offset; checksum2 = CRCUpdateBlock(checksum2, length, buffer + offset); offset += length; } checksum2 = ~0 ^ checksum2; if (checksum1 != checksum2) { std::cerr << "random checksum1 = " << checksum1 << std::endl; std::cerr << "random checksum2 = " << checksum2 << std::endl; return 1; } return 0; } // check windowing on random buffer int test5() { srand(113450911); unsigned char buffer[32*1024]; for (unsigned int i = 0; i < sizeof(buffer); i++) { buffer[i] = (unsigned char) (rand() % 256); } u64 window = 1024; u32 windowtable[256]; GenerateWindowTable(window, windowtable); int result = 0; u32 crc = CRCCompute(window, buffer); for (int offset = 0; offset + window < sizeof(buffer) - 1; offset++) { // compare against reference u32 othercrc = CRCCompute(window, buffer + offset); if (crc != othercrc) { std::cerr << "error in window at offset " << offset << std::endl; std::cerr << " checksum1 = " << crc << std::endl; std::cerr << " checksum2 = " << othercrc << std::endl; result = 1; } // slide window crc = CRCSlideChar(crc, buffer[offset + window], buffer[offset], windowtable); } return result; } // Checksum of checksum table // stolen from: // http://www.efg2.com/Lab/Mathematics/CRC.htm int test6() { u32 checksum1 = CRCCompute(sizeof(ccitttable.table), &ccitttable.table); u32 expected = 0x6FCF9E13; if (checksum1 != expected) { std::cerr << "error when computing checksum of checksum table " << std::endl; std::cerr << " checksum1 = " << checksum1 << std::endl; std::cerr << " expected = " << expected << std::endl; return 0; } return 0; } int main() { setup_hasher(); if (test1()) { std::cerr << "FAILED: test1" << std::endl; return 1; } if (test2()) { std::cerr << "FAILED: test2" << std::endl; return 1; } if (test3()) { std::cerr << "FAILED: test3" << std::endl; return 1; } if (test5()) { std::cerr << "FAILED: test5" << std::endl; return 1; } if (test6()) { std::cerr << "FAILED: test6" << std::endl; return 1; } std::cout << "SUCCESS: crc_test complete." << std::endl; return 0; } par2cmdline-turbo-1.4.0/src/creatorpacket.cpp000066400000000000000000000055671514221355600212100ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif // Construct the creator packet. // The only external information required to complete construction is // the set_id_hash (which is normally computed from information in the // main packet). bool CreatorPacket::Create(const MD5Hash &setid) { std::string creator = "Created by " X_PACKAGE " version " X_VERSION "."; // Allocate a packet just large enough for creator name CREATORPACKET *packet = (CREATORPACKET *)AllocatePacket(sizeof(*packet) + (~3 & (3+(u32)creator.size()))); // Fill in the details the we know packet->header.magic = packet_magic; packet->header.length = packetlength; //packet->header.hash; // Compute shortly packet->header.setid = setid; packet->header.type = creatorpacket_type; // Copy the creator description into the packet memcpy(packet->client, creator.c_str(), creator.size()); // Compute the packet hash MD5Context packetcontext; packetcontext.Update(&packet->header.setid, packetlength - offsetof(PACKET_HEADER, setid)); packetcontext.Final(packet->header.hash); return true; } // Load the packet from disk. bool CreatorPacket::Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) { // Is the packet long enough if (header.length <= sizeof(CREATORPACKET)) { return false; } // Is the packet too large (what is the longest reasonable creator description) if (header.length - sizeof(CREATORPACKET) > 100000) { return false; } // Allocate the packet (with a little extra so we will have NULLs after the description) CREATORPACKET *packet = (CREATORPACKET *)AllocatePacket((size_t)header.length, 4); packet->header = header; // Load the rest of the packet from disk return diskfile->Read(offset + sizeof(PACKET_HEADER), packet->client, (size_t)packet->header.length - sizeof(PACKET_HEADER)); } par2cmdline-turbo-1.4.0/src/creatorpacket.h000066400000000000000000000031531514221355600206420ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __CREATORPACKET_H__ #define __CREATORPACKET_H__ // The creator packet records details as to which PAR2 client // created a particular recovery file. // The PAR 2.0 specification requires the presence of a // creator packet, but it is not actually needed for the // verification or recovery of damaged files. class CreatorPacket : public CriticalPacket { public: // Construct the packet CreatorPacket(void) {}; ~CreatorPacket(void) {}; // Create a creator packet for a specified set id hash value bool Create(const MD5Hash &set_id_hash); // Load a creator packet from a specified file bool Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); }; #endif // __CREATORPACKET_H__ par2cmdline-turbo-1.4.0/src/criticalpacket.cpp000066400000000000000000000066051514221355600213350ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif bool CriticalPacket::WritePacket(DiskFile &diskfile, u64 fileoffset) const { assert(packetdata != 0 && packetlength != 0); return diskfile.Write(fileoffset, packetdata, packetlength); } void CriticalPacket::FinishPacket(const MD5Hash &setid) { assert(packetdata != 0 && packetlength >= sizeof(PACKET_HEADER)); PACKET_HEADER *header = (PACKET_HEADER*)packetdata; header->setid = setid; MD5Context packetcontext; packetcontext.Update(&header->setid, packetlength - offsetof(PACKET_HEADER, setid)); packetcontext.Final(header->hash); } // Order is: // Main Packet // File Description Packet (sorted by FileID) // File Verification Packet (sorted by FileID) // Creator Packet // Recovery Packet (sorted by exponent) bool CriticalPacket::CompareLess(const CriticalPacket* const &left, const CriticalPacket* const &right) { PACKET_HEADER *left_header = (PACKET_HEADER *)left->packetdata; PACKET_HEADER *right_header = (PACKET_HEADER *)right->packetdata; int left_value; switch (left_header->type.type[8]) { case 'M': left_value = 0; break; case 'F': left_value = 1; break; case 'I': left_value = 2; break; case 'C': left_value = 3; break; case 'R': left_value = 4; break; default: left_value = 5; break; } int right_value; switch (right_header->type.type[8]) { case 'M': right_value = 0; break; case 'F': right_value = 1; break; case 'I': right_value = 2; break; case 'C': right_value = 3; break; case 'R': right_value = 4; break; default: right_value = 5; break; } if (left_value < right_value) return true; if (left_value > right_value) return false; if (left_value == 1) // file description packets { return ((FILEDESCRIPTIONPACKET *)left->packetdata)->fileid < ((FILEDESCRIPTIONPACKET *)right->packetdata)->fileid; } else if (left_value == 2) // file verification packets { return ((FILEVERIFICATIONPACKET *)left->packetdata)->fileid < ((FILEVERIFICATIONPACKET *)right->packetdata)->fileid; } else if (left_value == 4) // recovery packet { return ((RECOVERYBLOCKPACKET *)left->packetdata)->exponent < ((RECOVERYBLOCKPACKET *)right->packetdata)->exponent; } else { // they're equal. return false; } } par2cmdline-turbo-1.4.0/src/criticalpacket.h000066400000000000000000000100051514221355600207670ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __CRITICALPACKET_H__ #define __CRITICALPACKET_H__ // Base class for main packet, file verification packet, file description packet // and creator packet. // These packets are all small and are held in memory in their entirity class CriticalPacket { public: CriticalPacket(void); ~CriticalPacket(void); public: // Write a copy of the packet to the specified file at the specified offset bool WritePacket(DiskFile &diskfile, u64 fileoffset) const; // Obtain the length of the packet. size_t PacketLength(void) const; // Allocate some memory for the packet (plus some extra padding). void* AllocatePacket(size_t length, size_t extra = 0); // Finish a packet (by storing the set_id_hash and then computing the packet_hash). void FinishPacket(const MD5Hash &set_id_hash); // Sort critical packets by type and FileId static bool CompareLess(const CriticalPacket* const &left, const CriticalPacket* const &right); protected: u8 *packetdata; size_t packetlength; }; inline CriticalPacket::CriticalPacket(void) { // There is no data initially packetdata = 0; packetlength = 0; } inline CriticalPacket::~CriticalPacket(void) { // Delete the data for the packet delete [] packetdata; } inline size_t CriticalPacket::PacketLength(void) const { return packetlength; } inline void* CriticalPacket::AllocatePacket(size_t length, size_t extra) { // Hey! We can't allocate the packet twice assert(packetlength == 0 && packetdata == 0); // Remember the requested packet length packetlength = length; // Allocate and clear the requested packet length plus the extra. packetdata = new u8[length+extra]; memset(packetdata, 0, length+extra); return packetdata; } // Class used to record the fact that a copy of a particular critical packet // will be written to a particular file at a specific offset. class CriticalPacketEntry { public: CriticalPacketEntry(DiskFile *_diskfile, u64 _offset, const CriticalPacket *_packet) : diskfile(_diskfile) , offset(_offset) , packet(_packet) {} CriticalPacketEntry(void) : diskfile(0) , offset(0) , packet(0) {} CriticalPacketEntry(const CriticalPacketEntry &other) : diskfile(other.diskfile) , offset(other.offset) , packet(other.packet) {} CriticalPacketEntry& operator=(const CriticalPacketEntry &other) { diskfile = other.diskfile; offset = other.offset; packet = other.packet; return *this; } public: // Write the packet to disk. bool WritePacket(void) const; // Obtain the length of the packet. u64 PacketLength(void) const; protected: DiskFile *diskfile; u64 offset; const CriticalPacket *packet; }; inline bool CriticalPacketEntry::WritePacket(void) const { assert(packet != 0 && diskfile != 0); // Tell the packet to write itself to disk return packet->WritePacket(*diskfile, offset); } inline u64 CriticalPacketEntry::PacketLength(void) const { assert(packet != 0); // Ask the packet how big it is. return packet->PacketLength(); } #endif // __CRITICALPACKET_H__ par2cmdline-turbo-1.4.0/src/criticalpacket_test.cpp000066400000000000000000000071331514221355600223710ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include "libpar2internal.h" // CriticalPacket::CompareLess int test1() { CriticalPacket criticalpackets[11]; u8 *data; data = (u8 *) criticalpackets[0].AllocatePacket(sizeof(MAINPACKET), 0); ((MAINPACKET *)data)->header.type = mainpacket_type; data = (u8 *) criticalpackets[1].AllocatePacket(sizeof(FILEDESCRIPTIONPACKET), 0); ((FILEDESCRIPTIONPACKET *)data)->header.type = filedescriptionpacket_type; ((FILEDESCRIPTIONPACKET *)data)->fileid.hash[0] = 0; data = (u8 *) criticalpackets[2].AllocatePacket(sizeof(FILEDESCRIPTIONPACKET), 0); ((FILEDESCRIPTIONPACKET *)data)->header.type = filedescriptionpacket_type; ((FILEDESCRIPTIONPACKET *)data)->fileid.hash[0] = 1; data = (u8 *) criticalpackets[3].AllocatePacket(sizeof(FILEDESCRIPTIONPACKET), 0); ((FILEDESCRIPTIONPACKET *)data)->header.type = filedescriptionpacket_type; ((FILEDESCRIPTIONPACKET *)data)->fileid.hash[1] = 1; data = (u8 *) criticalpackets[4].AllocatePacket(sizeof(FILEVERIFICATIONPACKET), 0); ((FILEVERIFICATIONPACKET *)data)->header.type = fileverificationpacket_type; ((FILEVERIFICATIONPACKET *)data)->fileid.hash[0] = 0; data = (u8 *) criticalpackets[5].AllocatePacket(sizeof(FILEVERIFICATIONPACKET), 0); ((FILEVERIFICATIONPACKET *)data)->header.type = fileverificationpacket_type; ((FILEVERIFICATIONPACKET *)data)->fileid.hash[0] = 1; data = (u8 *) criticalpackets[6].AllocatePacket(sizeof(FILEVERIFICATIONPACKET), 0); ((FILEVERIFICATIONPACKET *)data)->header.type = fileverificationpacket_type; ((FILEVERIFICATIONPACKET *)data)->fileid.hash[1] = 1; data = (u8 *) criticalpackets[7].AllocatePacket(sizeof(CREATORPACKET), 0); ((CREATORPACKET *)data)->header.type = creatorpacket_type; data = (u8 *) criticalpackets[8].AllocatePacket(sizeof(RECOVERYBLOCKPACKET), 0); ((RECOVERYBLOCKPACKET *)data)->header.type = recoveryblockpacket_type; ((RECOVERYBLOCKPACKET *)data)->exponent = 0; data = (u8 *) criticalpackets[9].AllocatePacket(sizeof(RECOVERYBLOCKPACKET), 0); ((RECOVERYBLOCKPACKET *)data)->header.type = recoveryblockpacket_type; ((RECOVERYBLOCKPACKET *)data)->exponent = 1; data = (u8 *) criticalpackets[10].AllocatePacket(sizeof(RECOVERYBLOCKPACKET), 0); // type is zeroed by AllocatePacket. for (size_t i = 0; i < 11; i++) { for (size_t j = 0; j < 11; j++) { if (CriticalPacket::CompareLess(criticalpackets + i, criticalpackets + j) != (i < j)) { std::cout << "CompareLess failed for " << i << " " << j << std::endl; return 1; } } } return 0; } int main() { if (test1()) { std::cerr << "FAILED: test1" << std::endl; return 1; } std::cout << "SUCCESS: criticalpacket_test complete." << std::endl; return 0; } par2cmdline-turbo-1.4.0/src/datablock.cpp000066400000000000000000000064341514221355600202770ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif // Open the file associated with the data block if is not already open bool DataBlock::Open(void) { if (diskfile == 0) return false; if (diskfile->IsOpen()) return true; return diskfile->Open(); } // Read some data at a specified position within a data block // into a buffer in memory bool DataBlock::ReadData(u64 position, // Position within the block size_t size, // Size of the memory buffer void *buffer) // Pointer to memory buffer { assert(diskfile != 0); // Check to see if the position from which data is to be read // is within the bounds of the data block if (length > position) { // Compute the file offset and how much data to physically read from disk u64 fileoffset = offset + position; size_t want = (size_t)std::min( std::min((u64)size, length - position), diskfile->FileSize() - fileoffset ); // Read the data from the file into the buffer if (!diskfile->Read(fileoffset, buffer, want)) return false; // If the read extends beyond the end of the data block, // then the rest of the buffer is zeroed. if (want < size) { memset(&((u8*)buffer)[want], 0, size-want); } } else { // Zero the whole buffer memset(buffer, 0, size); } return true; } // Write some data at a specified position within a datablock // from memory to disk bool DataBlock::WriteData(u64 position, // Position within the block size_t size, // Size of the memory buffer const void *buffer, // Pointer to memory buffer size_t &wrote) // Amount actually written { assert(diskfile != 0); wrote = 0; // Check to see if the position from which data is to be written // is within the bounds of the data block if (length > position) { // Compute the file offset and how much data to physically write to disk u64 fileoffset = offset + position; size_t have = (size_t)std::min((u64)size, length - position); // Write the data from the buffer to disk if (!diskfile->Write(fileoffset, buffer, have)) return false; wrote = have; } return true; } par2cmdline-turbo-1.4.0/src/datablock.h000066400000000000000000000074141514221355600177430ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __DATABLOCK_H__ #define __DATABLOCK_H__ class DiskFile; // A Data Block is a block of data of a specific length at a specific // offset in a specific file. // It may be either a block of data in a source file from which recovery // data is being computed, a block of recovery data in a recovery file, or // a block in a target file that is being reconstructed. class DataBlock { public: DataBlock(void); ~DataBlock(void); public: // Set the length of the block void SetLength(u64 length); // Set the location of the block void SetLocation(DiskFile *diskfile, u64 offset); void ClearLocation(void); void SetFilesize(u64 filesize); public: // Check to see if the location of the block has been set bool IsSet(void) const; // Which disk file is this data block in DiskFile* GetDiskFile(void) const; // What offset is the block located at u64 GetOffset(void) const; // What is the length of this block u64 GetLength(void) const; public: // Open the disk file if it is not already open (so that it can be read) bool Open(void); // Read some of the data from disk into memory. bool ReadData(u64 position, size_t size, void *buffer); // Write some of the data from memory to disk bool WriteData(u64 position, size_t size, const void *buffer, size_t &wrote); protected: DiskFile *diskfile; // Which disk file is the block associated with u64 offset; // What is the file offset u64 length; // How large is the block u64 filesize; // How large was the original file }; // Construct the data block inline DataBlock::DataBlock(void) { diskfile = 0; offset = 0; length = 0; filesize = 0; } // Destroy the data block inline DataBlock::~DataBlock(void) { } // Set the length of the block inline void DataBlock::SetLength(u64 _length) { length = _length; } inline void DataBlock::SetFilesize(u64 _filesize) { filesize = _filesize; } // Set the location of the block inline void DataBlock::SetLocation(DiskFile *_diskfile, u64 _offset) { diskfile = _diskfile; offset = _offset; } // Clear the location of the block inline void DataBlock::ClearLocation(void) { diskfile = 0; offset = 0; } // Check to see of the location is known inline bool DataBlock::IsSet(void) const { if (filesize > 0) { if (diskfile != 0) { if ((offset + length) > diskfile->FileSize() && filesize > diskfile->FileSize()) { return false; } else { return (diskfile != 0); } } } return (diskfile != 0); } // Which disk file is this data block in inline DiskFile* DataBlock::GetDiskFile(void) const { return diskfile; } // What offset is the block located at inline u64 DataBlock::GetOffset(void) const { return offset; } // What is the length of this block inline u64 DataBlock::GetLength(void) const { return length; } #endif // __DATABLOCK_H__ par2cmdline-turbo-1.4.0/src/descriptionpacket.cpp000066400000000000000000000263461514221355600220720ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif // Construct the packet and store the filename and size. bool DescriptionPacket::Create(std::string filename, u64 filesize) { // Allocate some extra bytes for the packet in memory so that strlen() can // be used on the filename. The extra bytes do not get written to disk. FILEDESCRIPTIONPACKET *packet = (FILEDESCRIPTIONPACKET *)AllocatePacket(sizeof(*packet) + (~3 & (3 + (u32)filename.size())), 4); // Store everything that is currently known in the packet. packet->header.magic = packet_magic; packet->header.length = packetlength; //packet->header.hash; // Not known yet //packet->header.setid; // Not known yet packet->header.type = filedescriptionpacket_type; //packet->fileid; // Not known yet //packet->hashfull; // Not known yet //packet->hash16k; // Not known yet packet->length = filesize; memcpy(packet->name, filename.c_str(), filename.size()); return true; } void DescriptionPacket::Hash16k(const MD5Hash &hash) { ((FILEDESCRIPTIONPACKET *)packetdata)->hash16k = hash; } void DescriptionPacket::HashFull(const MD5Hash &hash) { ((FILEDESCRIPTIONPACKET *)packetdata)->hashfull = hash; } void DescriptionPacket::ComputeFileId(void) { FILEDESCRIPTIONPACKET *packet = ((FILEDESCRIPTIONPACKET *)packetdata); // Compute the fileid from the hash, length, and name fields in the packet. MD5Context context; context.Update(&packet->hash16k, sizeof(FILEDESCRIPTIONPACKET)-offsetof(FILEDESCRIPTIONPACKET,hash16k) +strlen((const char*)packet->name)); context.Final(packet->fileid); } // Load a description packet from a specified file bool DescriptionPacket::Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) { // Is the packet big enough if (header.length <= sizeof(FILEDESCRIPTIONPACKET)) { return false; } // Is the packet too large (what is the longest permissible filename) if (header.length - sizeof(FILEDESCRIPTIONPACKET) > 100000) { return false; } // Allocate the packet (with a little extra so we will have NULLs after the filename) FILEDESCRIPTIONPACKET *packet = (FILEDESCRIPTIONPACKET *)AllocatePacket((size_t)header.length, 4); packet->header = header; // Read the rest of the packet from disk if (!diskfile->Read(offset + sizeof(PACKET_HEADER), &packet->fileid, (size_t)packet->header.length - sizeof(PACKET_HEADER))) return false; // Are the file and 16k hashes consistent if (packet->length <= 16384 && packet->hash16k != packet->hashfull) { return false; } return true; } // Returns the URL-style encoding of a character: // "%HH" where H is a hex-digit. std::string DescriptionPacket::UrlEncodeChar(char c) { std::string result("%"); char high_bits = ((c >> 4) & 0xf); if (high_bits < 10) result += '0' + high_bits; else result += 'A' + (high_bits - 10); char low_bits = (c & 0xf); if (low_bits < 10) result += '0' + low_bits; else result += 'A' + (low_bits - 10); return result; } // Converts the filename from that on disk to the version // in the Par file. Par uses HTML-style slashes ('/' or // UNIX-style slashes) to denote directories. This // function also prints a warning if a character may be // illegal on another machine. // // NOTE: I decided to warn users, not change the files. // If a user is just backing up files on their own system // and not sending them to users on another operating // system, we don't want to change the filenames. std::string DescriptionPacket::TranslateFilenameFromLocalToPar2(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, std::string local_filename) { std::string par2_encoded_filename; std::string::iterator p = local_filename.begin(); while (p != local_filename.end()) { unsigned char ch = *p; bool ok = true; if (ch < 32) { ok = false; } else { switch (ch) { case '"': case '*': case ':': case '<': case '>': case '?': case '|': ok = false; } } if (!ok) { if (noiselevel >= nlNormal) { serr << "WARNING: A filename contains the character \'" << ch << "\' which some systems do not allow in filenames." << std::endl; } } #ifdef _WIN32 // replace Windows-slash with HTML-slash if (ch == '\\') { ch = '/'; } #else if (ch == '\\') { if (noiselevel >= nlNormal) { serr << "WARNING: Found Windows-style slash '\\' in filename. Windows systems may have trouble with it." << std::endl; } } #endif par2_encoded_filename += ch; ++p; } // Par files should never contain an absolute path. On Windows, // These start "C:\...", etc. An attacker could put an absolute // path into a Par file and overwrite system files. if (par2_encoded_filename.size() > 1 && par2_encoded_filename.at(1) == ':') { if (noiselevel >= nlNormal) { serr << "WARNING: The second character in the filename \"" << par2_encoded_filename << "\" is a colon (':')." << std::endl; serr << " This may be interpreted by Windows systems as an absolute path." << std::endl; serr << " This file may be ignored by Par clients because absolute paths" << std::endl; serr << " are a way for an attacker to overwrite system files." << std::endl; } } if (par2_encoded_filename.at(0) == '/') { if (noiselevel >= nlNormal) { serr << "WARNING: The first character in the filename \"" << par2_encoded_filename << "\" is an HTML-slash ('/')." << std::endl; serr << " This may be interpreted by UNIX systems as an absolute path." << std::endl; serr << " This file may be ignored by Par clients because absolute paths" << std::endl; serr << " are a way for an attacker to overwrite system files." << std::endl; } } if (par2_encoded_filename.find("../") != std::string::npos) { if (noiselevel >= nlQuiet) { serr << "WARNING: The filename \"" << par2_encoded_filename << "\" contains \"..\"." << std::endl; serr << " This is a parent directory. This file may be ignored" << std::endl; serr << " by Par clients because parent directories are a way" << std::endl; serr << " for an attacker to overwrite system files." << std::endl; } } if (par2_encoded_filename.length() > 255) { if (noiselevel >= nlNormal) { serr << "WARNING: A filename is over 255 characters. That may be too long" << std::endl; serr << " for Windows systems to handle." << std::endl; } } return par2_encoded_filename; } // Take a filename that matches the PAR2 standard ('/' slashes, etc.) // and convert it to a legal filename on the local system. // While at it, try to fix things: illegal characters, attempts // to write an absolute path, directories named "..", etc. // // This implementation changes any illegal char into the URL-style // encoding of %HH where H is a hex-digit. // // NOTE: Windows limits path names to 255 characters. I'm not // sure that anything can be done here for that. std::string DescriptionPacket::TranslateFilenameFromPar2ToLocal(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, std::string par2_encoded_filename) { std::string local_filename; std::string::iterator p = par2_encoded_filename.begin(); while (p != par2_encoded_filename.end()) { unsigned char ch = *p; bool ok = true; #ifdef _WIN32 if (ch < 32) { ok = false; } else { switch (ch) { case '"': case '*': case ':': case '<': case '>': case '?': case '|': ok = false; } } #else // other UNIXes allow anything. if (ch < 32) { ok = false; } #endif // replace unix / to windows \ or windows \ to unix / #ifdef _WIN32 if (ch == '/') { ch = '\\'; } #else if (ch == '\\') { if (noiselevel >= nlQuiet) { // This is a legal Par2 character, but assume someone screwed up. serr << "INFO: Found Windows-style slash in filename. Changing to UNIX-style slash." << std::endl; ch = '/'; } } #endif if (ok) { local_filename += ch; } else { if (noiselevel >= nlQuiet) { serr << "INFO: Found illegal character '" << ch << "' in filename. Changed it to \"" << UrlEncodeChar(ch) << "\"" << std::endl; // convert problem characters to hex local_filename += UrlEncodeChar(ch); } } ++p; } #ifdef _WIN32 // Par files should never contain an absolute path. On Windows, // These start "C:\...", etc. An attacker could put an absolute // path into a Par file and overwrite system files. For Windows // systems, we've already changed ':' to "%3A", so any absolute // path should have become relative. // Replace any references to ".." which could also be used by // an attacker. while (true) { size_t index = local_filename.find("..\\"); if (index == std::string::npos) break; if (noiselevel >= nlQuiet) { serr << "INFO: Found attempt to write parent directory. Changing \"..\" to \"" << UrlEncodeChar('.') << UrlEncodeChar('.') << "\"" << std::endl; } local_filename.replace(index, 2, UrlEncodeChar('.')+UrlEncodeChar('.')); } #else // On UNIX systems, we don't want to allow filename to start with a slash, // because someone could be sneakily trying to overwrite a system file. if (local_filename.at(0) == '/') { if (noiselevel >= nlQuiet) { serr << "INFO: Found attempt to write absolute path. Changing '/' at start of filename to \"" << UrlEncodeChar('/') << "\"" << std::endl; } local_filename.replace(0, 1, UrlEncodeChar('/')); } // Replace any references to ".." which could also be sneaking while (true) { size_t index = local_filename.find("../"); if (index == std::string::npos) break; if (noiselevel >= nlQuiet) { serr << "INFO: Found attempt to write parent directory. Changing \"..\" to \"" << UrlEncodeChar('.') << UrlEncodeChar('.') << "\"" << std::endl; } local_filename.replace(index, 2, UrlEncodeChar('.')+UrlEncodeChar('.')); } #endif return local_filename; } par2cmdline-turbo-1.4.0/src/descriptionpacket.h000066400000000000000000000076751514221355600215430ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __DESCRIPTIONPACKET_H__ #define __DESCRIPTIONPACKET_H__ // The description packet records details about a file (including its name, // size, and the Hash of both the whole file and the first 16k of the file). class DescriptionPacket : public CriticalPacket { public: // Construct the packet DescriptionPacket(void) {}; ~DescriptionPacket(void) {}; public: // Construct the packet and store the filename and size. bool Create(std::string _filename, u64 _filesize); // Store the computed Hash values in the packet. void Hash16k(const MD5Hash &hash); void HashFull(const MD5Hash &hash); // Compute and return the file id hash from information in the packet void ComputeFileId(void); const MD5Hash& FileId(void) const; // Return the size of the file u64 FileSize(void) const; public: // Load a description packet from a specified file bool Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); // Return the name of the file std::string FileName(void) const; // Get the Hash values from the packet const MD5Hash& HashFull(void) const; const MD5Hash& Hash16k(void) const; // Used to encode characters we do not want, such as "\t" or ":". // Function is public for easier testing. static std::string UrlEncodeChar(char c); // Converts filename from local disk to how it will be encoded // in the Par2 file, and back again. static std::string TranslateFilenameFromLocalToPar2(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, std::string local_filename); static std::string TranslateFilenameFromPar2ToLocal(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, std::string par2_encoded_filename); }; // Get the file id from the packet inline const MD5Hash& DescriptionPacket::FileId(void) const { assert(packetdata != 0); return ((const FILEDESCRIPTIONPACKET*)packetdata)->fileid; } // Get the size of the file from the packet inline u64 DescriptionPacket::FileSize(void) const { assert(packetdata != 0); return ((const FILEDESCRIPTIONPACKET*)packetdata)->length; } // Get the name of the file from the packet // NB whilst the file format does not guarantee that the name will have a NULL // termination character, par2cmdline always allocates a little extra data // and fills it with NULLs to allow the filename to be directly read out of // the packet. inline std::string DescriptionPacket::FileName(void) const { assert(packetdata != 0); // return (char*)((const FILEDESCRIPTIONPACKET*)packetdata)->name(); return (char*)((const FILEDESCRIPTIONPACKET*)packetdata)->name; } // Get the full file hash value from the packet inline const MD5Hash& DescriptionPacket::HashFull(void) const { assert(packetdata != 0); return ((const FILEDESCRIPTIONPACKET*)packetdata)->hashfull; } // The hash of the first 16k of the file from the packet inline const MD5Hash& DescriptionPacket::Hash16k(void) const { assert(packetdata != 0); return ((const FILEDESCRIPTIONPACKET*)packetdata)->hash16k; } #endif // __DESCRIPTIONPACKET_H__ par2cmdline-turbo-1.4.0/src/descriptionpacket_test.cpp000066400000000000000000000236071514221355600231260ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include "libpar2internal.h" // The file separator std::string fs(PATHSEP); int test1() { if (DescriptionPacket::UrlEncodeChar('\t') != "%09") { std::cout << "UrlEncodeChar tab" << std::endl; return 1; } if (DescriptionPacket::UrlEncodeChar(':') != "%3A") { std::cout << "UrlEncodeChar tab" << std::endl; return 1; } // not illegal, but tests range of function. if (DescriptionPacket::UrlEncodeChar('\xFF') != "%FF") { std::cout << "UrlEncodeChar tab" << std::endl; return 1; } return 0; } // test TranslateFilenameFromLocalToPar2 int test2() { // The input to this function is the filename from a Par2 file. // The output is a "safe" filename std::string par2filename; par2filename = DescriptionPacket::TranslateFilenameFromLocalToPar2(std::cout, std::cerr, nlNormal, "input1.txt"); if (par2filename != "input1.txt") { std::cout << "TranslateFilenameFromLocalToPar2 nothing" << std::endl; return 1; } par2filename = DescriptionPacket::TranslateFilenameFromLocalToPar2(std::cout, std::cerr, nlNormal, "dir" + fs + "input1.txt"); if (par2filename != "dir/input1.txt") { std::cout << "TranslateFilenameFromLocalToPar2 " << fs << std::endl; return 1; } // leading dash is ugly, but allowed par2filename = DescriptionPacket::TranslateFilenameFromLocalToPar2(std::cout, std::cerr, nlNormal, "-input1.txt"); if (par2filename != "-input1.txt") { std::cout << "TranslateFilenameFromLocalToPar2 nothing" << std::endl; return 1; } std::cout << "---------------------------------------------------------" << std::endl; std::cout << "The following calls to Translate should produce warnings:" << std::endl; std::cout << "---------------------------------------------------------" << std::endl; // tabs are a control character par2filename = DescriptionPacket::TranslateFilenameFromLocalToPar2(std::cout, std::cerr, nlNormal, "\tinput1.txt"); if (par2filename != "\tinput1.txt") { std::cout << "TranslateFilenameFromLocalToPar2 tab" << std::endl; return 1; } // colon causes problem on Windows and OSX/MacOS par2filename = DescriptionPacket::TranslateFilenameFromLocalToPar2(std::cout, std::cerr, nlNormal, ":input1.txt"); if (par2filename != ":input1.txt") { std::cout << "TranslateFilenameFromLocalToPar2 :" << std::endl; return 1; } // Astrix causes problems everywhere par2filename = DescriptionPacket::TranslateFilenameFromLocalToPar2(std::cout, std::cerr, nlNormal, "*input1.txt"); if (par2filename != "*input1.txt") { std::cout << "TranslateFilenameFromLocalToPar2 2" << std::endl; return 1; } // Astrix causes problems everywhere par2filename = DescriptionPacket::TranslateFilenameFromLocalToPar2(std::cout, std::cerr, nlNormal, "?input1.txt"); if (par2filename != "?input1.txt") { std::cout << "TranslateFilenameFromLocalToPar2 3" << std::endl; return 1; } #ifdef _WIN32 // UNIX backslash on Windows systems par2filename = DescriptionPacket::TranslateFilenameFromLocalToPar2(std::cout, std::cerr, nlNormal, "/input1.txt"); if (par2filename != "/input1.txt") { std::cout << "TranslateFilenameFromLocalToPar2 4" << std::endl; return 1; } #else // Windows backslash on UNIX systems par2filename = DescriptionPacket::TranslateFilenameFromLocalToPar2(std::cout, std::cerr, nlNormal, "\\input1.txt"); if (par2filename != "\\input1.txt") { std::cout << "TranslateFilenameFromLocalToPar2 5" << std::endl; return 1; } #endif // absolute path on Windows par2filename = DescriptionPacket::TranslateFilenameFromLocalToPar2(std::cout, std::cerr, nlNormal, "C:" + fs + "input1.txt"); if (par2filename != "C:/input1.txt") { std::cout << "TranslateFilenameFromLocalToPar2 2" << std::endl; return 1; } // absolute path on UNIX par2filename = DescriptionPacket::TranslateFilenameFromLocalToPar2(std::cout, std::cerr, nlNormal, fs + "input1.txt"); if (par2filename != "/input1.txt") { std::cout << "TranslateFilenameFromLocalToPar2 2" << std::endl; return 1; } // referencing parent directory par2filename = DescriptionPacket::TranslateFilenameFromLocalToPar2(std::cout, std::cerr, nlNormal, ".." + fs + "input1.txt"); if (par2filename != "../input1.txt") { std::cout << "TranslateFilenameFromLocalToPar2 2" << std::endl; return 1; } par2filename = DescriptionPacket::TranslateFilenameFromLocalToPar2(std::cout, std::cerr, nlNormal, "tricky" + fs + ".." + fs + ".." + fs + "input1.txt"); if (par2filename != "tricky/../../input1.txt") { std::cout << "TranslateFilenameFromLocalToPar2 2" << std::endl; return 1; } std::cout << "--------------------------------------" << std::endl; std::cout << "End of code meant to produce warnings." << std::endl; std::cout << "--------------------------------------" << std::endl; return 0; } // tests TranslateFilenameFromPar2ToLocal int test3() { std::string local_filename; std::string expected; local_filename = DescriptionPacket::TranslateFilenameFromPar2ToLocal(std::cout, std::cerr, nlNormal, "input1.txt"); if (local_filename != "input1.txt") { std::cout << "TranslateFilenameFromPar2ToLocal normal" << std::endl; return 1; } local_filename = DescriptionPacket::TranslateFilenameFromPar2ToLocal(std::cout, std::cerr, nlNormal, "dir/input1.txt"); if (local_filename != "dir" + fs + "input1.txt") { std::cout << "TranslateFilenameFromPar2ToLocal directory" << std::endl; return 1; } // no one likes control characters, like tab. local_filename = DescriptionPacket::TranslateFilenameFromPar2ToLocal(std::cout, std::cerr, nlNormal, "\t"); expected = DescriptionPacket::UrlEncodeChar('\t'); if (local_filename != expected) { std::cout << "TranslateFilenameFromPar2ToLocal tab" << std::endl; return 1; } #ifdef _WIN32 // Windows does not allow certain characters in filenames local_filename = DescriptionPacket::TranslateFilenameFromPar2ToLocal(std::cout, std::cerr, nlNormal, "\"*:<>?|%abcd"); expected = DescriptionPacket::UrlEncodeChar('\"') + DescriptionPacket::UrlEncodeChar('*') + DescriptionPacket::UrlEncodeChar(':') + DescriptionPacket::UrlEncodeChar('<') + DescriptionPacket::UrlEncodeChar('>') + DescriptionPacket::UrlEncodeChar('?') + DescriptionPacket::UrlEncodeChar('|') + "%abcd"; if (local_filename != expected) { std::cout << "TranslateFilenameFromPar2ToLocal windows" << std::endl; return 1; } #else // other UNIXes - no need to test. local_filename = DescriptionPacket::TranslateFilenameFromPar2ToLocal(std::cout, std::cerr, nlNormal, "\"*:<>?|%abcd"); expected = "\"*:<>?|%abcd"; if (local_filename != expected) { std::cout << "TranslateFilenameFromPar2ToLocal UNIX" << std::endl; return 1; } #endif #ifdef _WIN32 // Do not allow absolute paths on Windows local_filename = DescriptionPacket::TranslateFilenameFromPar2ToLocal(std::cout, std::cerr, nlNormal, "C:/system_file"); expected = "C" + DescriptionPacket::UrlEncodeChar(':') + "\\system_file"; if (local_filename != expected) { std::cout << "TranslateFilenameFromPar2ToLocal windows absolute" << std::endl; return 1; } #else // UNIXes and OSX/MacOS check for absolute paths local_filename = DescriptionPacket::TranslateFilenameFromPar2ToLocal(std::cout, std::cerr, nlNormal, "/system_file"); expected = DescriptionPacket::UrlEncodeChar('/') + "system_file"; if (local_filename != expected) { std::cout << "TranslateFilenameFromPar2ToLocal UNIX absolute" << std::endl; return 1; } #endif // prevent access through parents local_filename = DescriptionPacket::TranslateFilenameFromPar2ToLocal(std::cout, std::cerr, nlNormal, "../system_file"); expected = DescriptionPacket::UrlEncodeChar('.') + DescriptionPacket::UrlEncodeChar('.') + fs + "system_file"; if (local_filename != expected) { std::cout << "TranslateFilenameFromPar2ToLocal parent" << std::endl; std::cout << " returned = " << local_filename << std::endl; std::cout << " expected = " << expected << std::endl; return 1; } local_filename = DescriptionPacket::TranslateFilenameFromPar2ToLocal(std::cout, std::cerr, nlNormal, "tricky/../../system_file"); expected = "tricky" + fs + DescriptionPacket::UrlEncodeChar('.') + DescriptionPacket::UrlEncodeChar('.') + fs + DescriptionPacket::UrlEncodeChar('.') + DescriptionPacket::UrlEncodeChar('.') + fs + "system_file"; if (local_filename != expected) { std::cout << "TranslateFilenameFromPar2ToLocal parent" << std::endl; std::cout << " returned = " << local_filename << std::endl; std::cout << " expected = " << expected << std::endl; return 1; } return 0; } int main() { if (test1()) { std::cerr << "FAILED: test1" << std::endl; return 1; } if (test2()) { std::cerr << "FAILED: test2" << std::endl; return 1; } if (test3()) { std::cerr << "FAILED: test3" << std::endl; return 1; } std::cout << "SUCCESS: descriptionpacket_test complete." << std::endl; return 0; } par2cmdline-turbo-1.4.0/src/diskfile.cpp000066400000000000000000000715121514221355600201440ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif #if defined(__FreeBSD_kernel__) #include #define BLKGETSIZE64 DIOCGMEDIASIZE #endif #ifdef _WIN32 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include "utf8.h" #include #define OffsetType __int64 #define MaxOffset 0x7fffffffffffffffI64 DiskFile::DiskFile(std::ostream &sout, std::ostream &serr, std::mutex &serr_lock) : sout(&sout) , serr(&serr) , serr_lock(&serr_lock) { filename = ""; filesize = 0; offset = 0; hFile = INVALID_HANDLE_VALUE; exists = false; } DiskFile::~DiskFile(void) { if (hFile != INVALID_HANDLE_VALUE) ::CloseHandle(hFile); } bool DiskFile::CreateParentDirectory(std::string _pathname) { // do we have a path separator in the filename ? std::string::size_type where; if (std::string::npos != (where = _pathname.find_last_of(PATHSEP)) || std::string::npos != (where = _pathname.find_last_of(ALTPATHSEP))) { std::string path = _pathname.substr(0, where); // Handle Windows root path (e.g., "C:" or empty path from "\file") // If path is empty or is a drive letter (e.g., "C:"), the root already exists if (path.empty() || (path.length() == 2 && path[1] == ':')) { return true; } std::wstring wpath = utf8::Utf8ToWide(path); struct _stati64 st; if (_wstati64(wpath.c_str(), &st) == 0) return true; // let the caller deal with non-directories if (!DiskFile::CreateParentDirectory(path)) return false; if (!CreateDirectoryW(wpath.c_str(), NULL)) { DWORD error = ::GetLastError(); std::lock_guard lock(*serr_lock); *serr << "Could not create the " << path << " directory: " << ErrorMessage(error) << std::endl; return false; } } return true; } // Create new file on disk and make sure that there is enough // space on disk for it. bool DiskFile::Create(std::string _filename, u64 _filesize) { assert(hFile == INVALID_HANDLE_VALUE); filename = _filename; filesize = _filesize; if (!DiskFile::CreateParentDirectory(filename)) return false; // Create the file std::wstring wfilename = utf8::Utf8ToWide(_filename); hFile = ::CreateFileW(wfilename.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { DWORD error = ::GetLastError(); std::lock_guard lock(*serr_lock); *serr << "Could not create \"" << _filename << "\": " << ErrorMessage(error) << std::endl; return false; } if (filesize > 0) { // Seek to the end of the file LONG* ptrfilesize = (LONG*)&filesize; LONG lowoffset = ptrfilesize[0]; LONG highoffset = ptrfilesize[1]; if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, lowoffset, &highoffset, FILE_BEGIN)) { DWORD error = ::GetLastError(); { std::lock_guard lock(*serr_lock); *serr << "Could not set size of \"" << _filename << "\": " << ErrorMessage(error) << std::endl; } ::CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; ::DeleteFileW(wfilename.c_str()); return false; } // Set the end of the file if (!::SetEndOfFile(hFile)) { DWORD error = ::GetLastError(); { std::lock_guard lock(*serr_lock); *serr << "Could not set size of \"" << _filename << "\": " << ErrorMessage(error) << std::endl; } ::CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; ::DeleteFileW(wfilename.c_str()); return false; } } offset = filesize; exists = true; return true; } // Write some data to disk bool DiskFile::Write(u64 _offset, const void *buffer, size_t length, LengthType maxlength) { assert(hFile != INVALID_HANDLE_VALUE); if (offset != _offset) { LONG* ptroffset = (LONG*)&_offset; LONG lowoffset = ptroffset[0]; LONG highoffset = ptroffset[1]; // Seek to the required offset if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, lowoffset, &highoffset, FILE_BEGIN)) { DWORD error = ::GetLastError(); std::lock_guard lock(*serr_lock); *serr << "Could not write " << (u64)length << " bytes to \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << std::endl; return false; } offset = _offset; } while (length > 0) { DWORD write; if (length > maxlength) write = maxlength; else write = (LengthType) length; DWORD wrote = 0; // Write the data if (!::WriteFile(hFile, buffer, write, &wrote, NULL)) { DWORD error = ::GetLastError(); std::lock_guard lock(*serr_lock); *serr << "Could not write " << write << " bytes to \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << std::endl; return false; } if (wrote != write) { std::lock_guard lock(*serr_lock); *serr << "INFO: Incomplete write to \"" << filename << "\" at offset " << _offset << ". Expected to write " << write << " bytes and wrote " << wrote << " bytes." << std::endl; } offset += wrote; length -= wrote; buffer = ((char *) buffer) + wrote; if (filesize < offset) { filesize = offset; } } return true; } // Open the file bool DiskFile::Open(const std::string &_filename, u64 _filesize) { assert(hFile == INVALID_HANDLE_VALUE); filename = _filename; filesize = _filesize; std::wstring wfilename = utf8::Utf8ToWide(_filename); hFile = ::CreateFileW(wfilename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { DWORD error = ::GetLastError(); switch (error) { case ERROR_FILE_NOT_FOUND: case ERROR_PATH_NOT_FOUND: break; default: std::lock_guard lock(*serr_lock); *serr << "Could not open \"" << _filename << "\": " << ErrorMessage(error) << std::endl; } return false; } offset = 0; exists = true; return true; } // Read some data from disk bool DiskFile::Read(u64 _offset, void *buffer, size_t length, LengthType maxlength) { assert(hFile != INVALID_HANDLE_VALUE); if (offset != _offset) { LONG* ptroffset = (LONG*)&_offset; LONG lowoffset = ptroffset[0]; LONG highoffset = ptroffset[1]; // Seek to the required offset if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, lowoffset, &highoffset, FILE_BEGIN)) { DWORD error = ::GetLastError(); std::lock_guard lock(*serr_lock); *serr << "Could not read " << (u64)length << " bytes from \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << std::endl; return false; } offset = _offset; } while (length > 0) { DWORD want; if (length > maxlength) want = maxlength; else want = (LengthType)length; DWORD got = 0; // Read the data if (!::ReadFile(hFile, buffer, want, &got, NULL)) { DWORD error = ::GetLastError(); std::lock_guard lock(*serr_lock); *serr << "Could not read " << (u64)length << " bytes from \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << std::endl; return false; } if (want != got) { std::lock_guard lock(*serr_lock); *serr << "Incomplete read from \"" << filename << "\" at offset " << offset << ". Tried to read " << want << " bytes and received " << got << " bytes." << std::endl; } offset += got; length -= got; buffer = ((char *) buffer) + got; // write updates filesize. Do we want to do that here? } return true; } void DiskFile::Close(void) { if (hFile != INVALID_HANDLE_VALUE) { ::CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; } } std::string DiskFile::GetCanonicalPathname(std::string filename) { std::wstring wfilename = utf8::Utf8ToWide(filename); // First call to get required buffer size DWORD length = GetFullPathNameW(wfilename.c_str(), 0, nullptr, nullptr); if (length == 0) { return filename; } // Allocate buffer with required size auto wfullname = std::make_unique(length); // Second call to get the actual path length = GetFullPathNameW(wfilename.c_str(), length, wfullname.get(), nullptr); if (length == 0) { return filename; } wfullname[0] = towupper(wfullname[0]); std::replace(wfullname.get(), wfullname.get() + length, L'/', L'\\'); return utf8::WideToUtf8(wfullname.get()); } std::unique_ptr< std::list > DiskFile::FindFiles(std::string path, std::string wildcard, bool recursive) { // check path, if not ending with path separator, add one char pathend = *path.rbegin(); if (pathend != PATHSEP[0]) { path += PATHSEP; } std::list *matches = new std::list; std::wstring wwildcard = utf8::Utf8ToWide(path + wildcard); WIN32_FIND_DATAW fd; HANDLE h = ::FindFirstFileW(wwildcard.c_str(), &fd); if (h != INVALID_HANDLE_VALUE) { do { if (0 == (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { matches->push_back(path + utf8::WideToUtf8(fd.cFileName)); } else if (recursive == true) { if (fd.cFileName[0] == '.') { continue; } std::string nwwildcard="*"; std::unique_ptr< std::list > dirmatches( DiskFile::FindFiles(path + utf8::WideToUtf8(fd.cFileName), nwwildcard, true) ); // append without requiring ordering matches->splice(matches->end(), *dirmatches); } } while (::FindNextFileW(h, &fd)); ::FindClose(h); } return std::unique_ptr< std::list >(matches); } u64 DiskFile::GetFileSize(std::string filename) { std::wstring wfilename = utf8::Utf8ToWide(filename); struct _stati64 st; if ((0 == _wstati64(wfilename.c_str(), &st)) && (0 != (st.st_mode & S_IFREG))) { return st.st_size; } else { return 0; } } bool DiskFile::FileExists(std::string filename) { std::wstring wfilename = utf8::Utf8ToWide(filename); struct _stati64 st; return ((0 == _wstati64(wfilename.c_str(), &st)) && (0 != (st.st_mode & _S_IFREG))); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// #else // !_WIN32 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef HAVE_FSEEKO # define OffsetType off_t # define MaxOffset ((off_t)0x7fffffffffffffffULL) # define fseek fseeko #else # if _FILE_OFFSET_BITS == 64 # define OffsetType unsigned long long # define MaxOffset 0x7fffffffffffffffULL # else # define OffsetType long # define MaxOffset 0x7fffffffUL # endif #endif DiskFile::DiskFile(std::ostream &sout, std::ostream &serr, std::mutex &serr_lock) : sout(&sout) , serr(&serr) , serr_lock(&serr_lock) { //filename; filesize = 0; offset = 0; file = 0; exists = false; } DiskFile::~DiskFile(void) { if (file != 0) fclose(file); } bool DiskFile::CreateParentDirectory(std::string _pathname) { // do we have a path separator in the filename ? std::string::size_type where; if (std::string::npos != (where = _pathname.find_last_of(PATHSEP)) || std::string::npos != (where = _pathname.find_last_of(ALTPATHSEP))) { std::string path = _pathname.substr(0, where); // Handle root path - if path is empty, the root already exists if (path.empty()) { return true; } struct stat st; if (stat(path.c_str(), &st) == 0) return true; // let the caller deal with non-directories if (!DiskFile::CreateParentDirectory(path)) return false; if (mkdir(path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) { std::lock_guard lock(*serr_lock); *serr << "Could not create the " << path << " directory: " << strerror(errno) << std::endl; return false; } } return true; } // Create new file on disk and make sure that there is enough // space on disk for it. bool DiskFile::Create(std::string _filename, u64 _filesize) { assert(file == 0); filename = _filename; filesize = _filesize; if (!DiskFile::CreateParentDirectory(filename)) return false; // This is after CreateParentDirectory because // the Windows code would error out after too. if (FileExists(filename)) { std::lock_guard lock(*serr_lock); *serr << "Could not create \"" << _filename << "\": File already exists." << std::endl; return false; } file = fopen(_filename.c_str(), "wb"); if (file == 0) { std::lock_guard lock(*serr_lock); *serr << "Could not create " << _filename << ": " << strerror(errno) << std::endl; return false; } if (_filesize > (u64)MaxOffset) { std::lock_guard lock(*serr_lock); *serr << "Requested file size for " << _filename << " is too large." << std::endl; return false; } if (_filesize > 0) { if (fseek(file, (OffsetType)_filesize-1, SEEK_SET)) { { std::lock_guard lock(*serr_lock); *serr << "Could not set end of file of " << _filename << ": " << strerror(errno) << std::endl; } fclose(file); file = 0; ::remove(filename.c_str()); return false; } if (1 != fwrite(&_filesize, 1, 1, file)) { { std::lock_guard lock(*serr_lock); *serr << "Could not set end of file of " << _filename << ": " << strerror(errno) << std::endl; } fclose(file); file = 0; ::remove(filename.c_str()); return false; } } offset = filesize; exists = true; return true; } // Write some data to disk bool DiskFile::Write(u64 _offset, const void *buffer, size_t length, LengthType maxlength) { assert(file != 0); if (offset != _offset) { if (_offset > (u64)MaxOffset) { std::lock_guard lock(*serr_lock); *serr << "Could not write " << (u64)length << " bytes to " << filename << " at offset " << _offset << std::endl; return false; } if (fseek(file, (OffsetType)_offset, SEEK_SET)) { std::lock_guard lock(*serr_lock); *serr << "Could not write " << (u64)length << " bytes to " << filename << " at offset " << _offset << ": " << strerror(errno) << std::endl; return false; } offset = _offset; } while (length > 0) { LengthType write; if (length > maxlength) write = maxlength; else write = length; LengthType wrote = fwrite(buffer, 1, write, file); if (wrote != write) { std::lock_guard lock(*serr_lock); *serr << "Could not write " << (u64)length << " bytes to " << filename << " at offset " << _offset << ": " << strerror(errno) << std::endl; return false; } offset += wrote; length -= wrote; buffer = ((char *) buffer) + wrote; if (filesize < offset) { filesize = offset; } } return true; } // Open the file bool DiskFile::Open(const std::string &_filename, u64 _filesize) { assert(file == 0); filename = _filename; filesize = _filesize; if (_filesize > (u64)MaxOffset) { std::lock_guard lock(*serr_lock); *serr << "File size for " << _filename << " is too large." << std::endl; return false; } file = fopen(filename.c_str(), "rb"); if (file == 0) { return false; } offset = 0; exists = true; return true; } // Read some data from disk bool DiskFile::Read(u64 _offset, void *buffer, size_t length, LengthType maxlength) { assert(file != 0); if (offset != _offset) { if (_offset > (u64)MaxOffset) { std::lock_guard lock(*serr_lock); *serr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << std::endl; return false; } if (fseek(file, (OffsetType)_offset, SEEK_SET)) { std::lock_guard lock(*serr_lock); *serr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << ": " << strerror(errno) << std::endl; return false; } offset = _offset; } while (length > 0) { LengthType want; if (length > maxlength) want = maxlength; else want = length; LengthType got = fread(buffer, 1, want, file); if (got != want) { // NOTE: This can happen on error or when hitting the end-of-file. std::lock_guard lock(*serr_lock); *serr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << ": " << strerror(errno) << std::endl; return false; } offset += got; length -= got; buffer = ((char *) buffer) + got; // Write() updates filesize. Should we do that here too? } return true; } void DiskFile::Close(void) { if (file != 0) { fclose(file); file = 0; } } // Attempt to get the full pathname of the file std::string DiskFile::GetCanonicalPathname(std::string filename) { // Is the supplied path already an absolute one if (filename.size() == 0 || filename[0] == '/') return filename; // Get the current directory #ifdef PATH_MAX char curdir[PATH_MAX]; if (0 == getcwd(curdir, sizeof(curdir))) #else // Avoid unconditional use of PATH_MAX (not defined on hurd) char *curdir = get_current_dir_name(); if (curdir == NULL) #endif { return filename; } // Allocate a work buffer and copy the resulting full path into it. char *work = new char[strlen(curdir) + filename.size() + 2]; strcpy(work, curdir); #ifndef PATH_MAX free(curdir); #endif if (work[strlen(work)-1] != '/') strcat(work, "/"); strcat(work, filename.c_str()); char *in = work; char *out = work; while (*in) { if (*in == '/') { if (in[1] == '.' && in[2] == '/') { // skip the input past /./ in += 2; } else if (in[1] == '.' && in[2] == '.' && in[3] == '/') { // backtrack the output if /../ was found on the input in += 3; if (out > work) { do { out--; } while (out > work && *out != '/'); } } else { *out++ = *in++; } } else { *out++ = *in++; } } *out = 0; std::string result = work; delete [] work; return result; } std::unique_ptr< std::list > DiskFile::FindFiles(std::string path, std::string wildcard, bool recursive) { // check path, if not ending with path separator, add one char pathend = *path.rbegin(); if (pathend != '/') { path += '/'; } std::list *matches = new std::list; std::string::size_type where; if ((where = wildcard.find_first_of('*')) != std::string::npos || (where = wildcard.find_first_of('?')) != std::string::npos) { std::string front = wildcard.substr(0, where); bool multiple = wildcard[where] == '*'; std::string back = wildcard.substr(where+1); DIR *dirp = opendir(path.c_str()); if (dirp != 0) { struct dirent *d; while ((d = readdir(dirp)) != 0) { std::string name = d->d_name; if (name == "." || name == "..") continue; if (multiple) { if (name.size() >= wildcard.size() && name.substr(0, where) == front && name.substr(name.size()-back.size()) == back) { struct stat st; std::string fn = path + name; if (lstat(fn.c_str(), &st) == 0) { if (S_ISDIR(st.st_mode) && recursive == true) { std::string nwwildcard="*"; std::unique_ptr< std::list > dirmatches( DiskFile::FindFiles(fn, nwwildcard, true) ); matches->splice(matches->end(), *dirmatches); } else if (S_ISREG(st.st_mode)) { matches->push_back(path + name); } } } } else { if (name.size() == wildcard.size()) { std::string::const_iterator pw = wildcard.begin(); std::string::const_iterator pn = name.begin(); while (pw != wildcard.end()) { if (*pw != '?' && *pw != *pn) break; ++pw; ++pn; } if (pw == wildcard.end()) { struct stat st; std::string fn = path + name; if (lstat(fn.c_str(), &st) == 0) { if (S_ISDIR(st.st_mode) && recursive == true) { std::string nwwildcard="*"; std::unique_ptr< std::list > dirmatches( DiskFile::FindFiles(fn, nwwildcard, true) ); matches->splice(matches->end(), *dirmatches); } else if (S_ISREG(st.st_mode)) { matches->push_back(path + name); } } } } } } closedir(dirp); } } else { struct stat st; std::string fn = path + wildcard; if (lstat(fn.c_str(), &st) == 0) { if (S_ISDIR(st.st_mode) && recursive == true) { std::string nwwildcard="*"; std::unique_ptr< std::list > dirmatches( DiskFile::FindFiles(fn, nwwildcard, true) ); matches->splice(matches->end(), *dirmatches); } else if (S_ISREG(st.st_mode)) { matches->push_back(path + wildcard); } } } return std::unique_ptr< std::list >(matches); } u64 DiskFile::GetFileSize(std::string filename) { struct stat st; if ((0 == stat(filename.c_str(), &st)) && (0 != (st.st_mode & S_IFREG))) { return st.st_size; } else { return 0; } } bool DiskFile::FileExists(std::string filename) { struct stat st; return ((0 == stat(filename.c_str(), &st)) && (0 != (st.st_mode & S_IFREG))); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endif bool DiskFile::Open(void) { std::string _filename = filename; return Open(_filename); } bool DiskFile::Open(const std::string &_filename) { return Open(_filename, GetFileSize(_filename)); } // Delete the file bool DiskFile::Delete(void) { #ifdef _WIN32 assert(hFile == INVALID_HANDLE_VALUE); std::wstring wfilename = utf8::Utf8ToWide(filename); if (filename.size() > 0 && ::DeleteFileW(wfilename.c_str())) { exists = false; return true; } #else assert(file == 0); if (filename.size() > 0 && 0 == unlink(filename.c_str())) { exists = false; return true; } #endif else { std::lock_guard lock(*serr_lock); *serr << "Cannot delete " << filename << std::endl; return false; } } //std::string DiskFile::GetPathFromFilename(std::string filename) //{ // std::string::size_type where; // // if (std::string::npos != (where = filename.find_last_of('/')) || // std::string::npos != (where = filename.find_last_of('\\'))) // { // return filename.substr(0, where+1); // } // else // { // return "." PATHSEP; // } //} void DiskFile::SplitFilename(std::string filename, std::string &path, std::string &name) { std::string::size_type where; if (std::string::npos != (where = filename.find_last_of(PATHSEP)) || std::string::npos != (where = filename.find_last_of(ALTPATHSEP))) { path = filename.substr(0, where+1); name = filename.substr(where+1); } else { path = "." PATHSEP; name = filename; } } void DiskFile::SplitRelativeFilename(std::string filename, std::string basepath, std::string &name) { name = filename; name.erase(0, basepath.length()); } #ifdef _WIN32 bool DiskFile::Rename(void) { u32 index = 0; std::string newname; std::wstring wnewname; struct _stati64 st; do { // Build the new filename with index suffix newname = filename + "." + std::to_string(++index); // Check path length against maximum if (newname.length() > _MAX_PATH) { std::lock_guard lock(*serr_lock); *serr << filename << " pathlength is more than " << _MAX_PATH << "." << std::endl; return false; } wnewname = utf8::Utf8ToWide(newname); // Check if file exists using wide-character stat } while (_wstati64(wnewname.c_str(), &st) == 0); return Rename(newname); } #else bool DiskFile::Rename(void) { u32 index = 0; std::string newname; struct stat st; do { // Build the new filename with index suffix newname = filename + "." + std::to_string(++index); // Check path length against maximum if (newname.length() > _MAX_PATH) { std::lock_guard lock(*serr_lock); *serr << filename << " pathlength is more than " << _MAX_PATH << "." << std::endl; return false; } } while (stat(newname.c_str(), &st) == 0); return Rename(newname); } #endif #ifdef _WIN32 std::string DiskFile::ErrorMessage(DWORD error) { std::string result; LPVOID lpMsgBuf; if (::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL)) { result = utf8::WideToUtf8((wchar_t*)lpMsgBuf); LocalFree(lpMsgBuf); } else { char message[40]; snprintf(message, sizeof(message), "Unknown error code (%lu)", error); result = message; } return result; } bool DiskFile::Rename(std::string _filename) { assert(hFile == INVALID_HANDLE_VALUE); std::wstring wfilename = utf8::Utf8ToWide(filename); std::wstring _wfilename = utf8::Utf8ToWide(_filename); if (::MoveFileW(wfilename.c_str(), _wfilename.c_str())) { filename.swap(_filename); return true; } std::lock_guard lock(*serr_lock); *serr << filename << " cannot be renamed to " << _filename << std::endl; return false; } #else bool DiskFile::Rename(std::string _filename) { assert(file == 0); if (::rename(filename.c_str(), _filename.c_str()) == 0) { filename.swap(_filename); return true; } std::lock_guard lock(*serr_lock); *serr << filename << " cannot be renamed to " << _filename << std::endl; return false; } #endif DiskFileMap::DiskFileMap(void) { } DiskFileMap::~DiskFileMap(void) { std::map::iterator fi = diskfilemap.begin(); while (fi != diskfilemap.end()) { delete (*fi).second; ++fi; } } bool DiskFileMap::Insert(DiskFile *diskfile) { std::string filename = diskfile->FileName(); assert(filename.length() != 0); std::pair::const_iterator,bool> location = diskfilemap.insert(std::pair(filename, diskfile)); return location.second; } void DiskFileMap::Remove(DiskFile *diskfile) { std::string filename = diskfile->FileName(); assert(filename.length() != 0); diskfilemap.erase(filename); } DiskFile* DiskFileMap::Find(std::string filename) const { assert(filename.length() != 0); std::map::const_iterator f = diskfilemap.find(filename); return (f != diskfilemap.end()) ? f->second : 0; } FileSizeCache::FileSizeCache() { } u64 FileSizeCache::get(const std::string &filename) { std::map::const_iterator f = cache.find(filename); if (f != cache.end()) return f->second; // go to disk u64 filesize = DiskFile::GetFileSize(filename); cache.insert(std::pair(filename, filesize)); // std::pair::const_iterator,bool> location = cache.insert(std::pair(filename, filesize)); // if (!location.second) { // throw exception? // } return filesize; } par2cmdline-turbo-1.4.0/src/diskfile.h000066400000000000000000000117631514221355600176130ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __DISKFILE_H__ #define __DISKFILE_H__ // MAX_LENGTH is the maximum read/write size. It can be OS-dependant. // The "& ~7" is to make it 8-byte aligned. // LengthType is the type required for the read/write by the OS. #ifdef _WIN32 #define MAX_LENGTH (0xffffffffUL & ~7) #define LengthType DWORD #else // !_WIN32 #define MAX_LENGTH (0xffffffffUL & ~7) #define LengthType size_t #endif #include #include #include #include #include // A disk file can be any type of file that par2cmdline needs // to read or write data from or to. class DiskFile { public: DiskFile(std::ostream &sout, std::ostream &serr, std::mutex &serr_lock); ~DiskFile(void); // Ensures the specified path's parent directory exists bool CreateParentDirectory(std::string pathname); // Create a file and set its length bool Create(std::string filename, u64 filesize); // Write some data to the file // maxlength should be the default value, except during testing. bool Write(u64 offset, const void *buffer, size_t length, LengthType maxlength = MAX_LENGTH); // Open the file bool Open(void); bool Open(const std::string &filename); bool Open(const std::string &filename, u64 filesize); // Check to see if the file is open #ifdef _WIN32 bool IsOpen(void) const {return hFile != INVALID_HANDLE_VALUE;} #else bool IsOpen(void) const {return file != 0;} #endif // Read some data from the file // maxlength should be the default value, except during testing. bool Read(u64 offset, void *buffer, size_t length, LengthType maxlength = MAX_LENGTH); // Close the file void Close(void); // Get the size of the file u64 FileSize(void) const {return filesize;} // Get the name of the file std::string FileName(void) const {return filename;} // Does the file exist bool Exists(void) const {return exists;} // Rename the file bool Rename(void); // Pick a filename automatically bool Rename(std::string filename); // Delete the file bool Delete(void); public: static std::string GetCanonicalPathname(std::string filename); static void SplitFilename(std::string filename, std::string &path, std::string &name); static void SplitRelativeFilename(std::string filename, std::string basepath, std::string &name); static std::string SplitRelativeFilename(const std::string& filename, const std::string& basepath) { std::string ret; SplitRelativeFilename(filename, basepath, ret); return ret; } static bool FileExists(std::string filename); static u64 GetFileSize(std::string filename); // Search the specified path for files which match the specified wildcard // and return their names in a list. static std::unique_ptr< std::list > FindFiles(std::string path, std::string wildcard, bool recursive); protected: // NOTE: These are pointers so that the operator= works correctly. // The references used elsewhere cannot be reassigned. // (Operator= is needed when vectors are resized.) std::ostream *sout; // stream for output (for commandline, this is cout) std::ostream *serr; // stream for errors (for commandline, this is cerr) std::mutex *serr_lock; std::string filename; u64 filesize; // OS file handle #ifdef _WIN32 HANDLE hFile; #else FILE *file; #endif // Current offset within the file u64 offset; // Does the file exist bool exists; protected: #ifdef _WIN32 static std::string ErrorMessage(DWORD error); #endif }; // This class keeps track of which DiskFile objects exist // and which file on disk they are associated with. // It is used to avoid a file being processed twice. class DiskFileMap { public: DiskFileMap(void); ~DiskFileMap(void); bool Insert(DiskFile *diskfile); void Remove(DiskFile *diskfile); DiskFile* Find(std::string filename) const; protected: std::map diskfilemap; // Map from filename to DiskFile }; class FileSizeCache { public: FileSizeCache(); u64 get(const std::string &filename); protected: std::map cache; }; #endif // __DISKFILE_H__ par2cmdline-turbo-1.4.0/src/diskfile_test.cpp000066400000000000000000000546531514221355600212120ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include #include "libpar2internal.h" // The file separator std::string fs(PATHSEP); // test static functions int test1() { // create test file using C++ functions std::ofstream input1; input1.open("input1.txt", std::ofstream::out | std::ofstream::binary); const char *input1_contents = "diskfile_test test1 input1.txt"; input1 << input1_contents; input1.close(); std::ofstream input2; input2.open("input2.txt", std::ofstream::out | std::ofstream::binary); const char *input2_contents = "diskfile_test test1 input2.txt"; input2 << input2_contents; input2.close(); if (DiskFile::FileExists("definitely_not_here")) { std::cout << "said file exists when it doesn't" << std::endl; return 1; } if (!DiskFile::FileExists("input1.txt")) { std::cout << "said file doesn't exists when it does" << std::endl; return 1; } if (DiskFile::GetFileSize("input1.txt") != strlen(input1_contents)) { std::cout << "GetFileSize returned wrong value" << std::endl; return 1; } std::unique_ptr< std::list > files = DiskFile::FindFiles(".", "input1.txt", false); if (files->size() != 1 || *(files->begin()) != "." + fs + "input1.txt") { std::cout << "FindFiles failed on exact name" << std::endl; std::cout << " size=" << files->size() << std::endl; for (std::list::iterator fn = files->begin(); fn != files->end(); fn++) { std::cout << " " << *fn << std::endl; } return 1; } files = DiskFile::FindFiles(".", "input?.txt", false); if (files->size() != 2 || find(files->begin(), files->end(), std::string("." + fs + "input1.txt")) == files->end() || find(files->begin(), files->end(), std::string("." + fs + "input2.txt")) == files->end()) { std::cout << "FindFiles failed on ?" << std::endl; return 1; } files = DiskFile::FindFiles(".", "input1?.txt", false); #ifdef _WIN32 if (files->size() != 1 || *(files->begin()) != "." + fs + "input1.txt") { std::cout << "FindFiles failed on empty ?" << std::endl; return 1; } #else if (!files->empty()) { std::cout << "FindFiles failed on empty ?" << std::endl; return 1; } #endif files = DiskFile::FindFiles(".", "input*.txt", false); if (files->size() != 2 || find(files->begin(), files->end(), std::string("." + fs + "input1.txt")) == files->end() || find(files->begin(), files->end(), std::string("." + fs + "input2.txt")) == files->end()) { std::cout << "FindFiles failed on *" << std::endl; return 1; } files = DiskFile::FindFiles(".", "input1*.txt", false); if (files->size() != 1 || *(files->begin()) != "." + fs + "input1.txt") { std::cout << "FindFiles failed on empty *" << std::endl; //TODO: Fix bug and uncomment this // return 1; } files = DiskFile::FindFiles(".", "i*p*t*.txt", false); if (files->size() != 2 || find(files->begin(), files->end(), std::string("." + fs + "input1.txt")) == files->end() || find(files->begin(), files->end(), std::string("." + fs + "input2.txt")) == files->end()) { std::cout << "FindFiles failed on multiple *" << std::endl; //TODO: Fix bug and uncomment this // return 1; } { // scope to hide variable names std::string path, name; DiskFile::SplitFilename("input1.txt", path, name); if (path != "." + fs || name != "input1.txt") { std::cout << "CanonicalPathname + SplitFilename failed on input1.txt" << std::endl; return 1; } // NB: Keeping value of path, name to see if overwritten DiskFile::SplitFilename("." + fs + "input1.txt", path, name); if (path != "." + fs || name != "input1.txt") { std::cout << "CanonicalPathname + SplitFilename failed on input1.txt" << std::endl; return 1; } DiskFile::SplitFilename("dir" + fs + "input1.txt", path, name); if (path != "dir" + fs || name != "input1.txt") { std::cout << "CanonicalPathname + SplitFilename failed on input1.txt" << std::endl; return 1; } DiskFile::SplitFilename("multiple" + fs + "dirs" + fs + "input1.txt", path, name); if (path != "multiple" + fs + "dirs" + fs || name != "input1.txt") { std::cout << "CanonicalPathname + SplitFilename failed on input1.txt" << std::endl; return 1; } DiskFile::SplitFilename(fs + "root_dir" + fs + "input1.txt", path, name); if (path != fs + "root_dir" + fs || name != "input1.txt") { std::cout << "CanonicalPathname + SplitFilename failed on input1.txt" << std::endl; return 1; } } { std::string name; DiskFile::SplitRelativeFilename("root" + fs + "dir" + fs + "input1.txt", "root" + fs + "dir" + fs, name); if (name != "input1.txt") { std::cout << "SplitRelativeFilename failed for full path" << std::endl; return 1; } // intentionally reusing name to see if it is overwritten DiskFile::SplitRelativeFilename("root" + fs + "dir" + fs + "input1.txt", "root" + fs, name); if (name != "dir" + fs + "input1.txt") { std::cout << "SplitRelativeFilename failed for partial path" << std::endl; return 1; } } { // scope to hide variable names std::string path_and_name1 = DiskFile::GetCanonicalPathname("input1.txt"); std::string path1, name1; DiskFile::SplitFilename(path_and_name1, path1, name1); if (path_and_name1 != path1 + name1 #ifdef _WIN32 || path1.at(1) != ':' || path1.at(2) != fs.at(0) #else || path1.at(0) != fs.at(0) #endif || name1 != "input1.txt") { std::cout << "CanonicalPathname + SplitFilename failed on input1.txt" << std::endl; return 1; } std::string path_and_name2 = DiskFile::GetCanonicalPathname("input2.txt"); std::string path2, name2; DiskFile::SplitFilename(path_and_name2, path2, name2); if (path_and_name2 != path2 + name2 || path2 != path1 || name2 != "input2.txt") { std::cout << "CanonicalPathname + SplitFilename failed on input2.txt" << std::endl; return 1; } // check that ././input1.txt is the same as input1.txt std::string path_and_name3 = DiskFile::GetCanonicalPathname("." + fs + "." + fs + "input1.txt"); if (path_and_name3 != path_and_name1) { std::cout << "CanonicalPathname + SplitFilename failed on ././input.txt" << std::endl; return 1; } } // delete test files using C++ function remove("input1.txt"); remove("input2.txt"); return 0; } // test non-static functions int test2() { // create test file using C++ functions std::ofstream input1; input1.open("input1.txt", std::ofstream::out | std::ofstream::binary); const char *input1_contents = "diskfile_test test1 input1.txt"; input1 << input1_contents; input1.close(); std::mutex output_lock; // read input1.txt { DiskFile diskfile(std::cout, std::cerr, output_lock); if (diskfile.IsOpen()) { std::cout << "IsOpen failed 1" << std::endl; return 1; } if (diskfile.Exists()) { std::cout << "Exists failed 1" << std::endl; return 1; } if (!diskfile.Open("input1.txt")) { std::cout << "Open failed" << std::endl; return 1; } if (!diskfile.IsOpen()) { std::cout << "IsOpen failed 2" << std::endl; return 1; } if (!diskfile.Exists()) { std::cout << "Exists failed 2" << std::endl; return 1; } if (diskfile.FileName() != "input1.txt") { std::cout << "FileName failed" << std::endl; return 1; } if (diskfile.FileSize() != strlen(input1_contents)) { std::cout << "FileSize failed" << std::endl; return 1; } const size_t buffer_len = strlen(input1_contents)+1; // for end-of-string u8 *buffer = new u8[buffer_len]; // put end-of-string in buffer. buffer[buffer_len-1] = '\0'; if (!diskfile.Read(0, buffer, buffer_len - 1)) { std::cout << "Read whole file returned false" << std::endl; return 1; } if (std::string(input1_contents) != (char *) buffer) { std::cout << "Read did not read contents correctly" << std::endl; std::cout << "read \"" << buffer << "\"" << std::endl; std::cout << "expected \"" << input1_contents << "\"" << std::endl; return 1; } // random reads srand(345087209); for (int i = 0; i < 100; i++) { // length is always at least 1. const u64 offset = rand() % (buffer_len-1-1); const size_t length = 1+(rand() % (buffer_len - 1 - offset-1)); if (!diskfile.Read(offset, buffer + offset, length)) { std::cout << "Read partial file returned false" << std::endl; std::cout << " offset=" << offset << std::endl; std::cout << " length=" << length << std::endl; std::cout << " strlen=" << strlen(input1_contents) << std::endl; return 1; } if (std::string(input1_contents) != (char *) buffer) { std::cout << "Random Read did not read contents correctly" << std::endl; return 1; } } if (!diskfile.IsOpen()) { std::cout << "IsOpen failed 3" << std::endl; return 1; } diskfile.Close(); if (diskfile.IsOpen()) { std::cout << "IsOpen failed 4" << std::endl; return 1; } // reopen! if (!diskfile.Open()) { std::cout << "Open failed 2" << std::endl; return 1; } if (!diskfile.IsOpen()) { std::cout << "IsOpen failed 5" << std::endl; return 1; } diskfile.Close(); if (diskfile.IsOpen()) { std::cout << "IsOpen failed 6" << std::endl; return 1; } delete [] buffer; } { std::cout << "create input2.txt, move it to input3.txt, delete it." << std::endl; const char *input2_contents = "diskfile_test test3 input2.txt is longer"; DiskFile diskfile(std::cout, std::cerr, output_lock); if (diskfile.IsOpen()) { std::cout << "IsOpen failed 1" << std::endl; return 1; } if (diskfile.Exists()) { std::cout << "Exists failed 1" << std::endl; return 1; } if (!diskfile.Create("input2.txt", strlen(input2_contents))) { std::cout << "Create failed" << std::endl; return 1; } if (!diskfile.IsOpen()) { std::cout << "IsOpen failed 2" << std::endl; return 1; } if (!diskfile.Exists()) { std::cout << "Exists failed 2" << std::endl; return 1; } if (diskfile.FileSize() != strlen(input2_contents)) { std::cout << "FileSize failed 1" << std::endl; return 1; } if (diskfile.FileName() != "input2.txt") { std::cout << "FileName failed 1" << std::endl; return 1; } if (!diskfile.Write(0, input2_contents, strlen(input2_contents))) { std::cout << "Write failed 1" << std::endl; return 1; } /* // confirm write with read const size_t buffer_len = strlen(input2_contents)+1; // for end-of-string u8 *buffer = new u8[buffer_len]; // put end-of-string in buffer. buffer[buffer_len-1] = '\0'; if (!diskfile.Read(0, buffer, buffer_len - 1)) { std::cout << "Read whole file returned false 1" << std::endl; return 1; } if (std::string(input2_contents) != (char *) buffer) { std::cout << "Read did not read contents correctly 1" << std::endl; return 1; } */ diskfile.Close(); if (diskfile.IsOpen()) { std::cout << "IsOpen failed 3" << std::endl; return 1; } // Rename from input2.txt to input3.txt if (!diskfile.Rename("input3.txt")) { std::cout << "Rename failed 1" << std::endl; return 1; } // C's remove returns 0 on success and non-0 on failure if (remove("input2.txt") == 0) { std::cout << "input2.txt exists after deletion!" << std::endl; return 1; } if (diskfile.FileName() != "input3.txt") { std::cout << "FileName failed 1" << std::endl; return 1; } if (!diskfile.Exists()) { std::cout << "Exists failed 3" << std::endl; return 1; } /* // read again if (!diskfile.Read(0, buffer, buffer_len - 1)) { std::cout << "Read whole file returned false 2" << std::endl; return 1; } if (std::string(input2_contents) != (char *) buffer) { std::cout << "Read did not read contents correctly 2" << std::endl; return 1; } */ if (!diskfile.Delete()) { std::cout << "Delete failed 1" << std::endl; return 1; } if (diskfile.Exists()) { std::cout << "Exists failed 4" << std::endl; return 1; } // C's remove returns 0 on success and non-0 on failure if (remove("input3.txt") == 0) { std::cout << "input3.txt exists after deletion!" << std::endl; return 1; } } // NOTE: C++ does not have a generic function to remove directories. // So, this test does not create subdirectories. // // CreateParentDirectory() // random write + read { std::cout << "create input2.txt, write and read it." << std::endl; const char *input2_contents = "diskfile_test test3 input2.txt is longer"; size_t buffer_len = strlen(input2_contents); srand(23461119); for (size_t blocksize = 1; blocksize < buffer_len; blocksize *=2) { { // scope for variables used in writing DiskFile diskfile(std::cout, std::cerr, output_lock); if (!diskfile.Create("input2.txt", strlen(input2_contents))) { std::cout << "Create failed" << std::endl; return 1; } int blockcount = (buffer_len + (blocksize - 1))/blocksize; int *blockorder = new int[blockcount]; for (int i = 0; i < blockcount; i++) blockorder[i] = i; // shuffle for (int i = 0; i < blockcount-1; i++) { int other_index = (rand() % (blockcount-(i+1))) + 1; int tmp = blockorder[other_index]; blockorder[other_index] = blockorder[i]; blockorder[i] = tmp; } // write blocks for (int i = 0; i < blockcount; i++) { const u64 offset = blocksize*blockorder[i]; // Calculate actual bytes to write (last block may be smaller) size_t write_len = (offset + blocksize > buffer_len) ? (buffer_len - offset) : blocksize; if (!diskfile.Write(offset, input2_contents + offset, write_len)) { std::cout << "Write failed 1" << std::endl; delete [] blockorder; return 1; } } diskfile.Close(); delete [] blockorder; } { // scope for variables used in reading. DiskFile diskfile(std::cout, std::cerr, output_lock); if (!diskfile.Open("input2.txt", strlen(input2_contents))) { std::cout << "Open failed 1" << std::endl; return 1; } // add one more char, for end-of-string u8 *buffer = new u8[buffer_len + 1]; buffer[buffer_len] = '\0'; if (!diskfile.Read(0, buffer, buffer_len)) { std::cout << "Read whole file returned false 2" << std::endl; return 1; } if (std::string(input2_contents) != (char *) buffer) { std::cout << "Read did not read contents correctly 2" << std::endl; return 1; } delete [] buffer; } if (remove("input2.txt") != 0) { std::cout << "input2.txt did not exist" << std::endl; return 1; } } } // delete test files using C++ function remove("input1.txt"); return 0; } // test DiskFileMap int test3() { std::ofstream input1; input1.open("input1.txt", std::ofstream::out | std::ofstream::binary); const char *input1_contents = "diskfile_test test3 input1.txt"; input1 << input1_contents; input1.close(); // Hard to screw up. Except double insert? DiskFileMap dfm; if (dfm.Find("input1.txt") != NULL) { std::cout << "Find succeeded when it shouldn't have" << std::endl; return 1; } std::mutex output_lock; DiskFile df1(std::cout, std::cerr, output_lock); df1.Open("input1.txt"); if (!dfm.Insert(&df1)) { std::cout << "Insert failed" << std::endl; return 1; } if (dfm.Find("input1.txt") != &df1) { std::cout << "Find failed when it shouldn't have" << std::endl; return 1; } DiskFile df2(std::cout, std::cerr, output_lock); df2.Open("input1.txt"); if (dfm.Insert(&df2)) { std::cout << "Insert succeeded when it shouldn't have" << std::endl; return 1; } if (dfm.Find("input1.txt") != &df1) { std::cout << "Find failed when it shouldn't have 2" << std::endl; return 1; } dfm.Remove(&df1); if (dfm.Find("input1.txt") != NULL) { std::cout << "Find succeeded when it shouldn't have 2" << std::endl; return 1; } // delete test files using C++ function remove("input1.txt"); return 0; } // test FileSizeCache int test4() { std::ofstream input1; input1.open("input1.txt", std::ofstream::out | std::ofstream::binary); const char *input1_contents = "diskfile_test test3 input1.txt"; input1 << input1_contents; input1.close(); std::ofstream input2; input2.open("input2.txt", std::ofstream::out | std::ofstream::binary); const char *input2_contents = "diskfile_test test3 input2.txt is longer"; input2 << input2_contents; input2.close(); // should time this vs. DiskFile::FileSize() FileSizeCache cache; for (int i = 0; i < 1000; i++) { if (cache.get("input1.txt") != strlen(input1_contents)) { std::cout << "FileSizeCache failed" << std::endl; return 1; } if (cache.get("input2.txt") != strlen(input2_contents)) { std::cout << "FileSizeCache failed 2" << std::endl; return 1; } } // delete test files using C++ function remove("input1.txt"); remove("input2.txt"); return 0; } // test that we cannot create a file if one already exists. int test5() { std::ofstream input1; input1.open("input1.txt", std::ofstream::out | std::ofstream::binary); const char *input1_contents = "diskfile_test test3 input1.txt"; input1 << input1_contents; input1.close(); std::mutex output_lock; DiskFile diskfile(std::cout, std::cerr, output_lock); if (diskfile.Create("input1.txt", strlen(input1_contents))) { std::cout << "Create succeeded when file already existed!" << std::endl; return 1; } // delete test files using C++ function remove("input1.txt"); return 0; } // Testing Read()/Write() where length > maxlength // To do this, the functions were modified to take // maxlength as a parameter. In production code, // the default maxlength is like 2GB, which is too // large for fast tests. int test6() { const char *input1_contents = "diskfile_test test6 input1.txt"; std::mutex output_lock; { DiskFile diskfile(std::cout, std::cerr, output_lock); if (!diskfile.Create("input1.txt", strlen(input1_contents))) { std::cout << "Create failed!" << std::endl; return 1; } if (!diskfile.Write(0, input1_contents, strlen(input1_contents), 2)) { std::cout << "Write failed 1" << std::endl; return 1; } diskfile.Close(); } { DiskFile diskfile(std::cout, std::cerr, output_lock); if (!diskfile.Open("input1.txt")) { std::cout << "Open failed" << std::endl; return 1; } const size_t buffer_len = strlen(input1_contents)+1; // for end-of-string u8 *buffer = new u8[buffer_len]; // put end-of-string in buffer. buffer[buffer_len-1] = '\0'; if (!diskfile.Read(0, buffer, buffer_len - 1, 2)) { std::cout << "Read whole file returned false" << std::endl; return 1; } if (std::string(input1_contents) != (char *) buffer) { std::cout << "Read did not read contents correctly" << std::endl; std::cout << "read \"" << buffer << "\"" << std::endl; std::cout << "expected \"" << input1_contents << "\"" << std::endl; return 1; } diskfile.Close(); remove("input1.txt"); } const char *input2_contents = "diskfile_test test6 input2.txt is longer"; // try again, writing mid-file with different maxlength. { DiskFile diskfile(std::cout, std::cerr, output_lock); if (!diskfile.Create("input2.txt", strlen(input2_contents))) { std::cout << "Create 2 failed." << std::endl; return 1; } size_t midpoint = strlen(input2_contents); if (!diskfile.Write(midpoint, input2_contents + midpoint, strlen(input2_contents) - midpoint, 3)) { std::cout << "Write failed 2" << std::endl; return 1; } if (!diskfile.Write(0, input2_contents, midpoint, 4)) { std::cout << "Write failed 3" << std::endl; return 1; } diskfile.Close(); } { DiskFile diskfile(std::cout, std::cerr, output_lock); if (!diskfile.Open("input2.txt")) { std::cout << "Open failed" << std::endl; return 1; } const size_t buffer_len = strlen(input2_contents)+1; // for end-of-string u8 *buffer = new u8[buffer_len]; // put end-of-string in buffer. buffer[buffer_len-1] = '\0'; size_t midpoint = strlen(input2_contents) - 2; if (!diskfile.Read(midpoint, buffer + midpoint, strlen(input2_contents) - midpoint, 4)) { std::cout << "Read second half of file returned false" << std::endl; return 1; } if (!diskfile.Read(0, buffer, midpoint, 3)) { std::cout << "Read first half of file returned false" << std::endl; return 1; } if (std::string(input2_contents) != (char *) buffer) { std::cout << "Read did not read contents correctly" << std::endl; std::cout << "read \"" << buffer << "\"" << std::endl; std::cout << "expected \"" << input2_contents << "\"" << std::endl; return 1; } diskfile.Close(); remove("input2.txt"); } return 0; } int main() { if (test1()) { std::cerr << "FAILED: test1" << std::endl; return 1; } if (test2()) { std::cerr << "FAILED: test2" << std::endl; return 1; } if (test3()) { std::cerr << "FAILED: test3" << std::endl; return 1; } if (test4()) { std::cerr << "FAILED: test4" << std::endl; return 1; } if (test5()) { std::cerr << "FAILED: test5" << std::endl; return 1; } if (test6()) { std::cerr << "FAILED: test6" << std::endl; return 1; } std::cout << "SUCCESS: diskfile_test complete." << std::endl; return 0; } par2cmdline-turbo-1.4.0/src/filechecksummer.cpp000066400000000000000000000206321514221355600215150ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #include "hasher.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif // Construct the checksummer and allocate buffers FileCheckSummer::FileCheckSummer(DiskFile *_diskfile, u64 _blocksize, const u32 (&_windowtable)[256]) : diskfile(_diskfile) , blocksize(_blocksize) , windowtable(_windowtable) , filesize(_diskfile->FileSize()) , currentoffset(0) , buffer(0) , outpointer(0) , inpointer(0) , tailpointer(0) , readoffset(0) , checksum(0) , hasblockhash(false) , contextfull() , context16k() , hasher(NULL) { buffer = new char[(size_t)blocksize*2]; } FileCheckSummer::~FileCheckSummer(void) { delete [] buffer; if (hasher) hasher->destroy(); } void FileCheckSummer::StopHasher(void) { if (!hasher) return; // Extract file hash from multi-hash hasher->extractFileMD5(contextfull); // Stop using the hasher hasher->destroy(); hasher = NULL; // Resync file MD5 to be consistent with UpdateHashes if (tailpointer > inpointer) contextfull.update(inpointer, tailpointer - inpointer); } // Start reading the file at the beginning bool FileCheckSummer::Start(void) { currentoffset = readoffset = 0; tailpointer = outpointer = buffer; inpointer = &buffer[blocksize]; hasher = HasherInput_Create(); // Fill the buffer with new data if (!Fill()) return false; // Compute the checksum + hash for the initial block ComputeCurrentChecksum(true); return true; } // Jump ahead bool FileCheckSummer::Jump(u64 distance) { // Are we already at the end of the file if (currentoffset >= filesize) return false; // Special distances if (distance == 0) return false; if (distance == 1) return Step(); // Not allowed to jump more than one block if (distance > blocksize) distance = blocksize; // If we're advancing by less than one block (and not at the end of the file), // the file/block hash won't be in sync any more if (distance != blocksize && currentoffset + distance < filesize) StopHasher(); // We don't have a cached block hash any more hasblockhash = false; // Advance the current offset and check if we have reached the end of the file currentoffset += distance; if (currentoffset >= filesize) { currentoffset = filesize; tailpointer = outpointer = buffer; memset(buffer, 0, (size_t)blocksize); checksum = 0; return true; } // Move past the data being discarded outpointer += distance; assert(outpointer <= tailpointer); // Is there any data left in the buffer that we are keeping size_t keep = tailpointer - outpointer; if (keep > 0) { // Move it back to the start of the buffer memmove(buffer, outpointer, keep); tailpointer = &buffer[keep]; } else { tailpointer = buffer; } outpointer = buffer; inpointer = &buffer[blocksize]; // If we already have a block of data available, we can compute the hash whilst waiting for the Fill operation if (keep >= blocksize && distance == blocksize) { std::future asynchash = std::async(std::launch::async, [this]() { ComputeCurrentChecksum(true); }); bool success = Fill(); asynchash.get(); return success; } else { if (!Fill()) return false; // If we're advancing by a whole block, we'll assume the next block is likely to be valid, so compute the MD5 in advance ComputeCurrentChecksum(distance == blocksize); } return true; } void FileCheckSummer::ComputeCurrentChecksum(bool domd5) { // Compute the checksum/hash for the block if (hasher) { // File/block hash is in sync, so compute all hashes size_t blocklen = (size_t)std::min(blocksize, filesize - currentoffset); size_t zeropad = blocksize - blocklen; hasher->update(buffer, blocklen); checksum = HasherGetBlock(hasher, blockhash, zeropad); hasblockhash = true; } else { // File/block hash not in sync, so can only compute block checksum/hash if (domd5) { checksum = MD5CRC_Calc(buffer, (size_t)blocksize, 0, blockhash.hash); hasblockhash = true; } else { // Some issue was found, so defer block MD5 computation checksum = CRCCompute((size_t)blocksize, buffer); } } } // Fill the buffer from disk bool FileCheckSummer::Fill(bool longfill) { // Have we already reached the end of the file if (readoffset >= filesize) return true; // Don't bother filling if we have enough data in the buffer if (tailpointer >= &buffer[blocksize] && !longfill) return true; // Try reading at least one block of data const char *target = tailpointer == buffer ? &buffer[blocksize] : &buffer[2*blocksize]; // How much data can we read into the buffer size_t want = (size_t)std::min(filesize-readoffset, (u64)(target-tailpointer)); if (want > 0) { // Read data if (!diskfile->Read(readoffset, tailpointer, want)) return false; UpdateHashes(readoffset, tailpointer, want); readoffset += want; tailpointer += want; } // Did we fill the buffer want = target - tailpointer; if (want > 0) { // Blank the rest of the buffer memset(tailpointer, 0, want); } return true; } // Update the full file hash and the 16k hash using the new data void FileCheckSummer::UpdateHashes(u64 offset, const void *buffer, size_t length) { // Are we already beyond the first 16k if (offset >= 16384) { // If hasher is being used, the file hash is updated along with the block hash if (!hasher) contextfull.update(buffer, length); } // Would we reach the 16k mark else if (offset+length >= 16384) { // Finish the 16k hash size_t first = (size_t)(16384-offset); context16k.update(buffer, first); // Continue with the full hash, if not using the hasher if (!hasher) { contextfull = context16k; // Do we go beyond the 16k mark if (offset+length > 16384) { contextfull.update(&((const char*)buffer)[first], length-first); } } } else { context16k.update(buffer, length); } } // Return the full file hash and the 16k file hash; FileCheckSummer cannot be used afterwards void FileCheckSummer::GetFileHashes(MD5Hash &hashfull, MD5Hash &hash16k) { // Compute the hash of the first 16k context16k.end(hash16k.hash); // Is the file smaller than 16k if (filesize < 16384) { // The hashes are the same hashfull = hash16k; } // If we're using the hasher, get file hash from there else if(hasher) { hasher->end(hashfull.hash); } else { // Compute the hash of the full file contextfull.end(hashfull.hash); } } // Compute and return the current hash MD5Hash FileCheckSummer::Hash(void) { // Did we pre-compute the hash? if (hasblockhash) return blockhash; MD5Context context; context.Update(outpointer, (size_t)blocksize); MD5Hash hash; context.Final(hash); return hash; } u32 FileCheckSummer::ShortChecksum(u64 blocklength) { u32 crc = CRCCompute((size_t)blocklength, outpointer); if (blocksize > blocklength) { crc = ~CRCUpdateBlock(~crc, (size_t)(blocksize-blocklength)); } return crc; } MD5Hash FileCheckSummer::ShortHash(u64 blocklength) { MD5Context context; context.Update(outpointer, (size_t)blocklength); if (blocksize > blocklength) { context.Update((size_t)(blocksize-blocklength)); } // Get the hash value MD5Hash hash; context.Final(hash); return hash; } par2cmdline-turbo-1.4.0/src/filechecksummer.h000066400000000000000000000134721514221355600211660ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __FILECHECKSUMMER_H__ #define __FILECHECKSUMMER_H__ // This source file defines the FileCheckSummer object which is used // when scanning a data file to find blocks of undamaged data. // // The object uses a "window" into the data file and slides that window // along the file computing the CRC of the data in that window as it // goes. If the computed CRC matches the value for a block of data // from a target data file, then the MD5 Hash value is also computed // and compared with the value for that block of data. When a match // has been confirmed, the object jumps forward to where the next // block of data is expected to start. Whilst the file is being scanned // the object also computes the MD5 Hash of the whole file and of // the first 16k of the file for later tests. class FileCheckSummer { public: FileCheckSummer(DiskFile *diskfile, u64 blocksize, const u32 (&windowtable)[256]); ~FileCheckSummer(void); // Start reading the file at the beginning bool Start(void); // Jump ahead the specified distance bool Jump(u64 distance); // Step forward one byte bool Step(void); // Return the current checksum u32 Checksum(void) const; // Compute and return the current hash MD5Hash Hash(void); // Compute short values of checksum and hash u32 ShortChecksum(u64 blocklength); MD5Hash ShortHash(u64 blocklength); // Do we have less than a full block of data bool ShortBlock(void) const; u64 BlockLength(void) const; // Return the current file offset u64 Offset(void) const; // Return the full file hash and the 16k file hash void GetFileHashes(MD5Hash &hashfull, MD5Hash &hash16k); // Which disk file is this const DiskFile* GetDiskFile(void) const {return diskfile;} protected: DiskFile *diskfile; u64 blocksize; const u32 (&windowtable)[256]; u64 filesize; u64 currentoffset; // file offset for current window position char *buffer; // buffer for reading from the file char *outpointer; // position in buffer of scan window char *inpointer; // &outpointer[blocksize]; char *tailpointer; // after last valid data in buffer // File offset for next read u64 readoffset; // Current block checksum/hash u32 checksum; MD5Hash blockhash; bool hasblockhash; // if blockhash is valid // MD5 hash of whole file and of first 16k MD5Single contextfull; MD5Single context16k; IHasherInput* hasher; // multi-hash context protected: //void ComputeCurrentCRC(void); void UpdateHashes(u64 offset, const void *buffer, size_t length); //// Fill the buffers with more data from disk // Set longfill = true to force fill the whole buffer bool Fill(bool longfill = false); // Stop using the multi-hash context due to file/block hash desync void StopHasher(void); // Compute block hash/checksum after Jump void ComputeCurrentChecksum(bool domd5); private: // private copy constructor to prevent any misuse. FileCheckSummer(const FileCheckSummer &); FileCheckSummer& operator=(const FileCheckSummer &); }; // Return the current checksum inline u32 FileCheckSummer::Checksum(void) const { return checksum; } // Return the current block length inline u64 FileCheckSummer::BlockLength(void) const { return std::min(blocksize, filesize-currentoffset); } // Return whether or not the current block is a short one. inline bool FileCheckSummer::ShortBlock(void) const { return BlockLength() < blocksize; } // Return the current file offset inline u64 FileCheckSummer::Offset(void) const { return currentoffset; } // Step forward one byte inline bool FileCheckSummer::Step(void) { // Are we already at the end of the file if (currentoffset >= filesize) return false; // The block hash won't be in sync with the file hash any more StopHasher(); // We don't have a cached block hash any more hasblockhash = false; // Advance the file offset and check to see if // we have reached the end of the file if (++currentoffset >= filesize) { currentoffset = filesize; tailpointer = outpointer = buffer; memset(buffer, 0, (size_t)blocksize); checksum = 0; return true; } // Ensure we have enough data in the buffer if (tailpointer <= inpointer) if(!Fill(true)) return false; // Get the incoming and outgoing characters char inch = *inpointer++; char outch = *outpointer++; // Update the checksum checksum = CRCSlideChar(checksum, inch, outch, windowtable); // Can the window slide further if (outpointer < &buffer[blocksize]) return true; assert(outpointer == &buffer[blocksize]); // Copy the data back to the beginning of the buffer memcpy(buffer, outpointer, (size_t)blocksize); inpointer = outpointer; outpointer = buffer; tailpointer -= blocksize; return true; } #endif // __FILECHECKSUMMER_H__ par2cmdline-turbo-1.4.0/src/foreach_parallel.h000066400000000000000000000024001514221355600212700ustar00rootroot00000000000000#ifndef __FOREACH_PARALLEL_H__ #define __FOREACH_PARALLEL_H__ #include #include #include #include template void foreach_parallel(const std::vector &collection, u32 numThreads, const std::function &fn) { if (numThreads == 1 || collection.size() == 1) { for (const auto &item : collection) fn(item); } else if (numThreads >= collection.size()) { // spawn a thread for each item std::vector> tasks; tasks.reserve(collection.size()); for (const auto &item : collection) tasks.push_back(std::async(std::launch::async, std::bind(fn, std::ref(item)))); for (auto &task : tasks) task.wait(); } else { std::atomic itemPos(0); std::vector threads; threads.reserve(numThreads); for (unsigned thread = 0; thread < numThreads; thread++) { threads.emplace_back([&itemPos, &collection, &fn]() { while (1) { unsigned i = itemPos.fetch_add(1, std::memory_order_relaxed); if (i >= collection.size()) break; fn(collection.at(i)); } }); } for (auto &thread : threads) thread.join(); } } #endif // __FOREACH_PARALLEL_H__ par2cmdline-turbo-1.4.0/src/galois.cpp000066400000000000000000000020671514221355600176270ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif par2cmdline-turbo-1.4.0/src/galois.h000066400000000000000000000213051514221355600172700ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __GALOIS_H__ #define __GALOIS_H__ template class GaloisTable; template class Galois; template class GaloisLongMultiplyTable; // This source file defines the Galois object for carrying out // arithmetic in GF(2^16) using the generator 0x1100B. // Also defined are the GaloisTable object (which contains log and // anti log tables for use in multiplication and division), and // the GaloisLongMultiplyTable object (which contains tables for // carrying out multiplation of 16-bit galois numbers 8 bits at a time). template class GaloisTable { public: typedef valuetype ValueType; GaloisTable(void); enum { Bits = bits, Count = 1< class Galois { public: typedef valuetype ValueType; // Basic constructors Galois(void) {}; Galois(ValueType v); // Copy and assignment Galois(const Galois &right) {value = right.value;} Galois& operator = (const Galois &right) { value = right.value; return *this;} // Addition Galois operator + (const Galois &right) const { return (value ^ right.value); } Galois& operator += (const Galois &right) { value ^= right.value; return *this;} // Subtraction Galois operator - (const Galois &right) const { return (value ^ right.value); } Galois& operator -= (const Galois &right) { value ^= right.value; return *this;} // Multiplication Galois operator * (const Galois &right) const; Galois& operator *= (const Galois &right); // Division Galois operator / (const Galois &right) const; Galois& operator /= (const Galois &right); // Power Galois pow(unsigned int right) const; Galois operator ^ (unsigned int right) const; Galois& operator ^= (unsigned int right); // Cast to value and value access operator ValueType(void) const {return value;} ValueType Value(void) const {return value;} // Direct log and antilog ValueType Log(void) const; ValueType ALog(void) const; enum { Bits = GaloisTable::Bits, Count = GaloisTable::Count, Limit = GaloisTable::Limit, }; protected: ValueType value; static GaloisTable table; }; #ifdef LONGMULTIPLY template class GaloisLongMultiplyTable { public: GaloisLongMultiplyTable(void); typedef g G; enum { Bytes = ((G::Bits + 7) >> 3), Count = ((Bytes * (Bytes+1)) / 2), }; G tables[Count * 256 * 256]; }; #endif // Construct the log and antilog tables from the generator template inline GaloisTable::GaloisTable(void) { u32 b = 1; for (u32 l=0; l GaloisTable Galois::table; template inline Galois::Galois(typename Galois::ValueType v) { value = v; } template inline Galois Galois::operator * (const Galois &right) const { if (value == 0 || right.value == 0) return 0; unsigned int sum = table.log[value] + table.log[right.value]; if (sum >= Limit) { return table.antilog[sum-Limit]; } else { return table.antilog[sum]; } } template inline Galois& Galois::operator *= (const Galois &right) { if (value == 0 || right.value == 0) { value = 0; } else { unsigned int sum = table.log[value] + table.log[right.value]; if (sum >= Limit) { value = table.antilog[sum-Limit]; } else { value = table.antilog[sum]; } } return *this; } template inline Galois Galois::operator / (const Galois &right) const { if (value == 0) return 0; assert(right.value != 0); if (right.value == 0) {return 0;} // Division by 0! int sum = table.log[value] - table.log[right.value]; if (sum < 0) { return table.antilog[sum+Limit]; } else { return table.antilog[sum]; } } template inline Galois& Galois::operator /= (const Galois &right) { if (value == 0) return *this; assert(right.value != 0); if (right.value == 0) {return *this;} // Division by 0! int sum = table.log[value] - table.log[right.value]; if (sum < 0) { value = table.antilog[sum+Limit]; } else { value = table.antilog[sum]; } return *this; } template inline Galois Galois::pow(unsigned int right) const { if (right == 0) return 1; if (value == 0) return 0; unsigned int sum = table.log[value] * right; sum = (sum >> Bits) + (sum & Limit); if (sum >= Limit) { return table.antilog[sum-Limit]; } else { return table.antilog[sum]; } } template inline Galois Galois::operator ^ (unsigned int right) const { if (right == 0) return 1; if (value == 0) return 0; unsigned int sum = table.log[value] * right; sum = (sum >> Bits) + (sum & Limit); if (sum >= Limit) { return table.antilog[sum-Limit]; } else { return table.antilog[sum]; } } template inline Galois& Galois::operator ^= (unsigned int right) { if (right == 0) {value = 1; return *this;} if (value == 0) return *this; unsigned int sum = table.log[value] * right; sum = (sum >> Bits) + (sum & Limit); if (sum >= Limit) { value = table.antilog[sum-Limit]; } else { value = table.antilog[sum]; } return *this; } template inline valuetype Galois::Log(void) const { return table.log[value]; } template inline valuetype Galois::ALog(void) const { return table.antilog[value]; } #ifdef LONGMULTIPLY template inline GaloisLongMultiplyTable::GaloisLongMultiplyTable(void) { G *table = tables; for (unsigned int i=0; i Galois8; #endif // __GALOIS_H__ par2cmdline-turbo-1.4.0/src/galois_test.cpp000066400000000000000000000206361514221355600206700ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include "libpar2internal.h" #include "galois.h" // Galois are finite fields. (A field with a finite number of elements.) // A field has: // addition // multiplication // such that // + and * are associative // + and * are commutative // exists 0 such that a+0 = 0+a = a // exists 1 such that a*1 = 1*a = 1 // for all a, exists addative inverse -a such that a + -a = 0 // for all a, exists multiplicative inverse a^-1 such that a * a^-1 = 1 // * is distributive over + template int test_field(const gtype a, const gtype b, const gtype c, const gtype zero, const gtype one, int max_value) { if ( (a+b)+c != a+(b+c) ) { std::cerr << "addative associativity fails for " << ((int) (utype) a) << "," << ((int) (utype) b) << "," << ((int) (utype) c) << std::endl; return 1; } if ( (a*b)*c != a*(b*c) ) { std::cerr << "multiplicative associativity fails for " << ((int) (utype) a) << "," << ((int) (utype) b) << "," << ((int) (utype) c) << std::endl; return 1; } if ( a+b != b+a ) { std::cerr << "addative commutivity fails for " << ((int) (utype) a) << "," << ((int) (utype) b) << std::endl; return 1; } if ( a*b != b*a ) { std::cerr << "multiplicative commutivity fails for " << ((int) (utype) a) << "," << ((int) (utype) b) << std::endl; return 1; } if ( a*(b+c) != (a*b)+(a*c) ) { std::cerr << "addative associativity fails for " << ((int) (utype) a) << "," << ((int) (utype) b) << "," << ((int) (utype) c) << std::endl; return 1; } if ( a+zero != a || zero+a != a ) { std::cerr << "addative identity fails for " << ((int) (utype) a) << std::endl; return 1; } if ( a*one != a || one*a != a ) { std::cerr << "multiplicative identity fails for " << ((int) (utype) a) << std::endl; return 1; } // search for inverses int addative_inverse = -1; int multiplicative_inverse = -1; for (int i = 0; i < max_value; i++) { const gtype other(i); //std::cout << ((int) (utype) a) << " + " << ((int) (utype) other) << " = " << ((int) (utype) (a + other)) << std::endl; //std::cout << ((a + other == zero) ? "true" : "false") << std::endl; if (a + other == zero) { if (addative_inverse != -1) { std::cerr << "value " << ((int) (utype) a) << " has two addative inverses" << std::endl; std::cerr << " at " << addative_inverse << " and " << i << std::endl; return 1; } addative_inverse = i; } if (a * other == one) { if (multiplicative_inverse != -1) { std::cerr << "value " << ((int) (utype) a) << " has two multiplicative inverses" << std::endl; std::cerr << " at " << multiplicative_inverse << " and " << i << std::endl; return 1; } multiplicative_inverse = i; } } if (addative_inverse == -1) { std::cerr << "value " << ((int) (utype) a) << " has no addative inverse!" << std::endl; return 1; } if (multiplicative_inverse == -1 && a != zero) { std::cerr << "value " << ((int) (utype) a) << " has no multiplicative inverse!" << std::endl; return 1; } return 0; } template int test_field_many(const gtype zero, const gtype one, int max_value) { srand(345087209); for (int i = 0; i < 256*256; i++) { if (test_field( gtype(i%max_value), gtype(rand() % max_value), gtype(rand() % max_value), zero, one, max_value)) { return 1; } } return 0; } int test1() { return test_field_many(Galois8(0), Galois8(1), 256); } template int test_operators(const gtype a, const gtype b, const gtype zero, const gtype one, int max_value) { gtype c; c = a; c += b; if ( c != a+b ) { std::cerr << "+= fails for " << ((int) (utype) a) << "," << ((int) (utype) b) << "," << ((int) (utype) c) << std::endl; return 1; } if (a-a != zero ) { std::cerr << "- fails for " << ((int) (utype) a) << std::endl; return 1; } c = a; c -= a; if ( c != zero ) { std::cerr << "-= fails for " << ((int) (utype) a) << "," << ((int) (utype) c) << std::endl; return 1; } c = a; c *= b; if ( c != a*b ) { std::cerr << "*= fails for " << ((int) (utype) a) << "," << ((int) (utype) b) << "," << ((int) (utype) c) << std::endl; return 1; } if (a != zero) { if ( a/a != one ) { std::cerr << "/ fails for " << ((int) (utype) a) << std::endl; return 1; } c = a; c /= a; if ( c != one ) { std::cerr << "/= fails for " << ((int) (utype) a) << "," << ((int) (utype) c) << std::endl; return 1; } } unsigned int power = (unsigned int) (utype) b; c = one; for (unsigned int i = 0; i < power; i++) { c *= a; } if ((a^power) != c) { std::cerr << "^ fails for " << ((int) (utype) a) << "," << ((int) (utype) c) << std::endl; return 1; } if (a.pow(power) != c) { std::cerr << "pow() fails for " << ((int) (utype) a) << "," << ((int) (utype) c) << std::endl; return 1; } c = a; c ^= power; if (c != (a^power)) { std::cerr << "^= fails for " << ((int) (utype) a) << "," << power << "," << ((int) (utype) c) << std::endl; return 1; } if (a != zero && b != zero) { if ( (a.Log() + b.Log()) % (max_value-1) != (a*b).Log() ) { std::cerr << "Log = fails for " << ((int) (utype) a) << "," << ((int) (utype) b) << "," << ((int) a.Log()) << "," << ((int) b.Log()) << "," << ((int) (a*b).Log()) << std::endl; return 1; } } c = gtype(gtype(a.Log()).ALog()); if (c != a) { std::cerr << "ALog fails for " << ((int) (utype) a) << "," << ((int) (utype) c) << std::endl; return 1; } return 0; } template int test_operators_many(const gtype zero, const gtype one, int max_value) { srand(14531119); for (int i = 0; i < 256*256; i++) { if (test_operators( gtype(i%max_value), gtype(rand() % max_value), zero, one, max_value)) { return 1; } } return 0; } int test3() { return test_operators_many(Galois8(0), Galois8(1), 256); } // test powers of Galois // 2^pow should be unique for pow in range 1 to N-1. // and 2^N = 2 // and 2^pow != 0 for any pow template int test_powers(const gtype two, const gtype zero, int max_value) { gtype g = two; int used[256*256]; for (int i = 0; i < max_value; i++) used[i] = 0; // mark 0 as used, so we get an error if it reaches it. used[0] = -1; for (int power = 1; power < max_value; power++) { int index = (int) (utype) g; if (used[index] != 0) { std::cerr << "error at power " << power << " was already used by power " << used[index] << std::endl; std::cerr << "g = " << ((int) (utype) g) << std::endl; return 1; } used[index] = power; g *= two; } if (g != two) { std::cerr << "error after power " << max_value << std::endl; std::cerr << "g = " << ((int) (utype) g) << std::endl; std::cerr << "two = " << ((int) (utype) two) << std::endl; return 1; } return 0; } int test5() { return test_powers(Galois8(2), Galois8(0), 256); } int main() { if (test1()) { std::cerr << "FAILED: test1" << std::endl; return 1; } if (test3()) { std::cerr << "FAILED: test3" << std::endl; return 1; } if (test5()) { std::cerr << "FAILED: test5" << std::endl; return 1; } std::cout << "SUCCESS: galois_test complete." << std::endl; return 0; } par2cmdline-turbo-1.4.0/src/hasher.h000066400000000000000000000006321514221355600172640ustar00rootroot00000000000000#ifndef __HASHER_H__ #define __HASHER_H__ // Wrapper for ParPar's InputHasher #include "../parpar/hasher/hasher.h" inline u32 HasherGetBlock(IHasherInput* hasher, MD5Hash& blockhash, u64 zeropad = 0) { u8 md5crc[20]; hasher->getBlock(md5crc, zeropad); std::memcpy(blockhash.hash, md5crc, 16); return md5crc[16] | (md5crc[17] << 8) | (md5crc[18] << 16) | (md5crc[19] << 24); } #endif // __HASHER_H__ par2cmdline-turbo-1.4.0/src/letype.h000066400000000000000000000065341514221355600173230ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __LETYPE_H__ #define __LETYPE_H__ #if __BYTE_ORDER == __LITTLE_ENDIAN typedef u16 leu16; typedef u32 leu32; typedef u64 leu64; #else struct leu16 { leu16& operator=(const u16 &other); operator u16(void) const; u16 value; }; inline leu16& leu16::operator=(const u16 &other) { ((unsigned char*)&value)[0] = (unsigned char)((other >> 0) & 0xff); ((unsigned char*)&value)[1] = (unsigned char)((other >> 8) & 0xff); return *this; } inline leu16::operator u16(void) const { return ((unsigned char*)&value)[0] << 0 | ((unsigned char*)&value)[1] << 8; } struct leu32 { leu32& operator=(const u32 &other); operator u32(void) const; u32 value; }; inline leu32& leu32::operator=(const u32 &other) { ((unsigned char*)&value)[0] = (unsigned char)((other >> 0) & 0xff); ((unsigned char*)&value)[1] = (unsigned char)((other >> 8) & 0xff); ((unsigned char*)&value)[2] = (unsigned char)((other >> 16) & 0xff); ((unsigned char*)&value)[3] = (unsigned char)((other >> 24) & 0xff); return *this; } inline leu32::operator u32(void) const { return ((unsigned char*)&value)[0] << 0 | ((unsigned char*)&value)[1] << 8 | ((unsigned char*)&value)[2] << 16 | ((unsigned char*)&value)[3] << 24; } struct leu64 { leu64& operator=(const u64 &other); operator u64(void) const; u64 value; }; inline leu64& leu64::operator=(const u64 &other) { ((unsigned char*)&value)[0] = (unsigned char)((other >> 0) & 0xff); ((unsigned char*)&value)[1] = (unsigned char)((other >> 8) & 0xff); ((unsigned char*)&value)[2] = (unsigned char)((other >> 16) & 0xff); ((unsigned char*)&value)[3] = (unsigned char)((other >> 24) & 0xff); ((unsigned char*)&value)[4] = (unsigned char)((other >> 32) & 0xff); ((unsigned char*)&value)[5] = (unsigned char)((other >> 40) & 0xff); ((unsigned char*)&value)[6] = (unsigned char)((other >> 48) & 0xff); ((unsigned char*)&value)[7] = (unsigned char)((other >> 56) & 0xff); return *this; } inline leu64::operator u64(void) const { return (u64)(((unsigned char*)&value)[0]) << 0 | (u64)(((unsigned char*)&value)[1]) << 8 | (u64)(((unsigned char*)&value)[2]) << 16 | (u64)(((unsigned char*)&value)[3]) << 24 | (u64)(((unsigned char*)&value)[4]) << 32 | (u64)(((unsigned char*)&value)[5]) << 40 | (u64)(((unsigned char*)&value)[6]) << 48 | (u64)(((unsigned char*)&value)[7]) << 56; } #endif #endif // __LETYPE_H__ par2cmdline-turbo-1.4.0/src/letype_test.cpp000066400000000000000000000074261514221355600207160ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include "libpar2internal.h" #include "letype.h" // test one value int test1() { u16 expected = 127*127; leu16 x; x = expected; u16 output = x; if (output != expected) { std::cerr << "output = " << output << std::endl; std::cerr << "expected = " << expected << std::endl; return 1; } return 0; } // test all 16 bit values int test2() { for (int i = 0; i < 256*256; i++) { u16 expected = i; leu16 x; x = expected; u16 output = x; if (output != expected) { std::cerr << "output = " << output << std::endl; std::cerr << "expected = " << expected << std::endl; return 1; } } return 0; } // test one 32-bit value int test3() { u32 expected = 127*127*127*127; leu32 x; x = expected; u32 output = x; if (output != expected) { std::cerr << "output = " << output << std::endl; std::cerr << "expected = " << expected << std::endl; return 1; } return 0; } // test random 32-bit values int test4() { srand(113450911); for (int i = 0; i < 256*256; i++) { unsigned long z = 0; z += (rand() % 256)*256*256*256; z += (rand() % 256)*256*256; z += (rand() % 256)*256; z += (rand() % 256); u32 expected = z; leu32 x; x = expected; u32 output = x; if (output != expected) { std::cerr << "output = " << output << std::endl; std::cerr << "expected = " << expected << std::endl; return 1; } } return 0; } // test one 64-bit value int test5() { u64 expected = 127ul*127ul*127ul*127ul*127ul*127ul*127ul*127ul; leu64 x; x = expected; u64 output = x; if (output != expected) { std::cerr << "output = " << output << std::endl; std::cerr << "expected = " << expected << std::endl; return 1; } return 0; } // test random 64-bit values int test6() { srand(84395311); for (int i = 0; i < 256*256; i++) { unsigned long z = 0; unsigned long factor = 1; for (int j = 0; j < 8; j++) { z += (rand() % 256)*factor; factor *= 256; } u64 expected = z; leu64 x; x = expected; u64 output = x; if (output != expected) { std::cerr << "output = " << output << std::endl; std::cerr << "expected = " << expected << std::endl; return 1; } } return 0; } int main() { if (test1()) { std::cerr << "FAILED: test1" << std::endl; return 1; } if (test2()) { std::cerr << "FAILED: test2" << std::endl; return 1; } if (test3()) { std::cerr << "FAILED: test3" << std::endl; return 1; } if (test4()) { std::cerr << "FAILED: test4" << std::endl; return 1; } if (test5()) { std::cerr << "FAILED: test5" << std::endl; return 1; } if (test6()) { std::cerr << "FAILED: test6" << std::endl; return 1; } std::cout << "SUCCESS: letype_test complete." << std::endl; return 0; } par2cmdline-turbo-1.4.0/src/libpar2.cpp000066400000000000000000000121641514221355600177030ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" Result par2create(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, const size_t memorylimit, const std::string &basepath, const u32 nthreads, const u32 filethreads, const std::string &parfilename, const std::vector &extrafiles, const u64 blocksize, const u32 firstblock, const Scheme recoveryfilescheme, const u32 recoveryfilecount, const u32 recoveryblockcount ) { Par2Creator creator(sout, serr, noiselevel); Result result = creator.Process( memorylimit, basepath, nthreads, filethreads, parfilename, extrafiles, blocksize, firstblock, recoveryfilescheme, recoveryfilecount, recoveryblockcount ); return result; } Result par2repair(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, const size_t memorylimit, const std::string &basepath, const u32 nthreads, const u32 filethreads, const std::string &parfilename, const std::vector &extrafiles, const bool dorepair, // derived from operation const bool purgefiles, const bool renameonly, const bool skipdata, const u64 skipleaway ) { Par2Repairer repairer(sout, serr, noiselevel); Result result = repairer.Process( memorylimit, basepath, nthreads, filethreads, parfilename, extrafiles, dorepair, purgefiles, renameonly, skipdata, skipleaway); return result; } Result par1repair(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, const size_t memorylimit, // basepath is not used by Par1 const u32 nthreads, // filethreads is not used by Par1 const std::string &parfilename, const std::vector &extrafiles, const bool dorepair, // derived from operation const bool purgefiles // skipdata is not used by Par1 // skipleaway is not used by Par1 ) { Par1Repairer repairer(sout, serr, noiselevel); Result result = repairer.Process(memorylimit, nthreads, parfilename, extrafiles, dorepair, purgefiles); return result; } // Determine how many recovery files to create. bool ComputeRecoveryFileCount(std::ostream &sout, std::ostream &serr, u32 *recoveryfilecount, Scheme recoveryfilescheme, u32 recoveryblockcount, u64 largestfilesize, u64 blocksize) { // Are we computing any recovery blocks if (recoveryblockcount == 0) { *recoveryfilecount = 0; return true; } switch (recoveryfilescheme) { case scUnknown: { //assert(false); serr << "Scheme unspecified (create, verify, or repair)." << std::endl; return false; } break; case scVariable: case scUniform: { if (*recoveryfilecount == 0) { // If none specified then then filecount is roughly log2(blockcount) // This prevents you getting excessively large numbers of files // when the block count is high and also allows the files to have // sizes which vary exponentially. for (u32 blocks=recoveryblockcount; blocks>0; blocks>>=1) { (*recoveryfilecount)++; } } if (*recoveryfilecount > recoveryblockcount) { // You cannot have more recovery files than there are recovery blocks // to put in them. serr << "Too many recovery files specified." << std::endl; return false; } } break; case scLimited: { // No recovery file will contain more recovery blocks than would // be required to reconstruct the largest source file if it // were missing. Other recovery files will have recovery blocks // distributed in an exponential scheme. u32 largest = (u32)((largestfilesize + blocksize-1) / blocksize); u32 whole = recoveryblockcount / largest; whole = (whole >= 1) ? whole-1 : 0; u32 extra = recoveryblockcount - whole * largest; *recoveryfilecount = whole; for (u32 blocks=extra; blocks>0; blocks>>=1) { (*recoveryfilecount)++; } } break; } return true; } par2cmdline-turbo-1.4.0/src/libpar2.h000066400000000000000000000142561514221355600173540ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __LIBPAR2_H__ #define __LIBPAR2_H__ #ifdef _MSC_VER // _MSC_VER is for Microsoft Visual C++ Compiler. // // The ifdef is not for _WIN32, because MinGW (Mingw32) // is a port of gcc's compiler that targets Windows // and we don't want this code run for MinGW. typedef unsigned char u8; typedef signed char i8; typedef unsigned short u16; typedef signed short i16; typedef unsigned int u32; typedef signed int i32; typedef unsigned __int64 u64; typedef signed __int64 i64; #else // _MSC_VER #ifdef HAVE_CONFIG_H #include #if HAVE_INTTYPES_H # include #endif #if HAVE_STDINT_H # include typedef uint8_t u8; typedef int8_t i8; typedef uint16_t u16; typedef int16_t i16; typedef uint32_t u32; typedef int32_t i32; typedef uint64_t u64; typedef int64_t i64; #else typedef unsigned char u8; typedef signed char i8; typedef unsigned short u16; typedef signed short i16; typedef unsigned int u32; typedef signed int i32; typedef unsigned long long u64; typedef signed long long i64; #endif #else // HAVE_CONFIG_H typedef unsigned char u8; typedef signed char i8; typedef unsigned short u16; typedef signed short i16; typedef unsigned int u32; typedef signed int i32; typedef unsigned long long u64; typedef signed long long i64; #endif #endif // Case-insensitive string comparison #ifdef _WIN32 # define stricmp _stricmp #else # include # define stricmp strcasecmp #endif // Path separators #ifdef _WIN32 # define PATHSEP "\\" # define ALTPATHSEP "/" #else # define PATHSEP "/" # define ALTPATHSEP "\\" #endif // Default number of file threads #define _FILE_THREADS 2 #include #include #include typedef enum { scUnknown = 0, scVariable, // Each PAR2 file will have 2x as many blocks as previous scLimited, // Limit PAR2 file size scUniform // All PAR2 files the same size } Scheme; // How much logging/status information to write // to output or error stream typedef enum { nlUnknown = 0, nlSilent, // Absolutely no output (other than errors) nlQuiet, // Bare minimum of output nlNormal, // Normal level of output nlNoisy, // Lots of output nlDebug // Extra debugging information } NoiseLevel; // Return type of par2cmdline typedef enum Result { eSuccess = 0, eRepairPossible = 1, // Data files are damaged and there is // enough recovery data available to // repair them. eRepairNotPossible = 2, // Data files are damaged and there is // insufficient recovery data available // to be able to repair them. eInvalidCommandLineArguments = 3, // There was something wrong with the // command line arguments eInsufficientCriticalData = 4, // The PAR2 files did not contain sufficient // information about the data files to be able // to verify them. eRepairFailed = 5, // Repair completed but the data files // still appear to be damaged. eFileIOError = 6, // An error occurred when accessing files eLogicError = 7, // In internal error occurred eMemoryError = 8, // Out of memory } Result; Result par2create(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, const size_t memorylimit, const std::string &basepath, const u32 nthreads, const u32 filethreads, const std::string &parfilename, const std::vector &extrafiles, const u64 blocksize, const u32 firstblock, const Scheme recoveryfilescheme, const u32 recoveryfilecount, const u32 recoveryblockcount ); Result par2repair(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, const size_t memorylimit, const std::string &basepath, const u32 nthreads, const u32 filethreads, const std::string &parfilename, const std::vector &extrafiles, const bool dorepair, // derived from operation const bool purgefiles, const bool renameonly, const bool skipdata, const u64 skipleaway ); Result par1repair(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, const size_t memorylimit, // basepath is not used by Par1 const u32 nthreads, // filethreads is not used by Par1 const std::string &parfilename, const std::vector &extrafiles, const bool dorepair, // derived from operation const bool purgefiles // skipdata is not used by Par1 // skipleaway is not used by Par1 ); bool ComputeRecoveryFileCount(std::ostream &sout, std::ostream &serr, u32 *recoveryfilecount, Scheme recoveryfilescheme, u32 recoveryblockcount, u64 largestfilesize, u64 blocksize); #endif // __LIBPAR2_H__ par2cmdline-turbo-1.4.0/src/libpar2_test.cpp000066400000000000000000000306611514221355600207440ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include #include "libpar2.h" // ComputeRecoveryFileCount // check when it returns false. int test1() { u32 recoveryfilecount = 0; bool success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scUnknown, 1, 1, 1); if (success) { std::cerr << "ComputeRecoveryFileCount worked for unknown scheme" << std::endl; return 1; } recoveryfilecount = 10; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scVariable, 4, 4, 4); if (success) { std::cerr << "ComputeRecoveryFileCount worked with more files than blocks!" << std::endl; return 1; } return 0; } // ComputeRecoveryFileCount // scVariable int test2() { u32 recoveryfilecount = 0; bool success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scVariable, 0, 4, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 0) { std::cerr << "ComputeRecoveryFileCount for 0 blocks should return 0" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scVariable, 8, 4, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 4) { std::cerr << "ComputeRecoveryFileCount for 8 blocks should return 4" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scVariable, 15, 4, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 4) { std::cerr << "ComputeRecoveryFileCount for 15 blocks should return 4" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scVariable, 64, 4, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 7) { std::cerr << "ComputeRecoveryFileCount for 64 blocks should return 7" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scVariable, 127, 4, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 7) { std::cerr << "ComputeRecoveryFileCount for 127 blocks should return 7" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } return 0; } // ComputeRecoveryFileCount // scUniform // Doesn't matter the value - long as it's zero at zero and positive after. int test3() { u32 recoveryfilecount = 0; bool success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scUniform, 0, 4, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 0) { std::cerr << "ComputeRecoveryFileCount for 0 blocks should return 0" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scUniform, 1, 4, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount == 0) { std::cerr << "ComputeRecoveryFileCount for 1 block should a positive value" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scUniform, 8, 4, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount == 0) { std::cerr << "ComputeRecoveryFileCount for 8 blocks should a positive value" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } return 0; } // ComputeRecoveryFileCount // scLimited // Same as variable with big files // But differs for smaller ones. int test4() { u32 recoveryfilecount = 0; bool success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scLimited, 0, 4096, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 0) { std::cerr << "ComputeRecoveryFileCount for 0 blocks should return 0" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scLimited, 8, 4096, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 4) { std::cerr << "ComputeRecoveryFileCount for 8 blocks should return 4" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scLimited, 15, 4096, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 4) { std::cerr << "ComputeRecoveryFileCount for 15 blocks should return 4" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scLimited, 64, 4096, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 7) { std::cerr << "ComputeRecoveryFileCount for 64 blocks should return 7" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scLimited, 127, 4096, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 7) { std::cerr << "ComputeRecoveryFileCount for 127 blocks should return 7" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } // smaller largest files // 1 2 4 8 10 10 10... recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scLimited, 8, 40, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 4) { std::cerr << "ComputeRecoveryFileCount for 127 blocks should return 7" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scLimited, 15, 40, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 4) { std::cerr << "ComputeRecoveryFileCount for 127 blocks should return 7" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scLimited, 16, 40, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 5) { std::cerr << "ComputeRecoveryFileCount for 127 blocks should return 7" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scLimited, 25, 40, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 5) { std::cerr << "ComputeRecoveryFileCount for 127 blocks should return 7" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scLimited, 26, 40, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 6) { std::cerr << "ComputeRecoveryFileCount for 127 blocks should return 7" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scLimited, 35, 40, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 6) { std::cerr << "ComputeRecoveryFileCount for 127 blocks should return 7" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } recoveryfilecount = 0; success = ComputeRecoveryFileCount(std::cout, std::cerr, &recoveryfilecount, scLimited, 35 + 100, 40, 4); if (!success) { std::cerr << "ComputeRecoveryFileCount failed test2.1" << std::endl; return 1; } if (recoveryfilecount != 6 + 10) { std::cerr << "ComputeRecoveryFileCount for 127 blocks should return 7" << std::endl; std::cerr << " it returned " << recoveryfilecount << std::endl; return 1; } return 0; } int main() { if (test1()) { std::cerr << "FAILED: test1" << std::endl; return 1; } if (test2()) { std::cerr << "FAILED: test2" << std::endl; return 1; } if (test3()) { std::cerr << "FAILED: test3" << std::endl; return 1; } if (test4()) { std::cerr << "FAILED: test4" << std::endl; return 1; } std::cout << "SUCCESS: libpar2_test complete." << std::endl; return 0; } par2cmdline-turbo-1.4.0/src/libpar2internal.h000066400000000000000000000121561514221355600211060ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __PARCMDLINE_H__ #define __PARCMDLINE_H__ #ifdef _WIN32 // Windows includes #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include // System includes #include #include #include #include #include #include #include #include #include #define snprintf _snprintf_s #define unlink _unlink #define __LITTLE_ENDIAN 1234 #define __BIG_ENDIAN 4321 #define __PDP_ENDIAN 3412 #define __BYTE_ORDER __LITTLE_ENDIAN #ifndef _SIZE_T_DEFINED # ifdef _WIN64 typedef unsigned __int64 size_t; # else typedef unsigned int size_t; # endif # define _SIZE_T_DEFINED #endif #ifdef HAVE_CONFIG_H #include #endif #else // _WIN32 #ifdef HAVE_CONFIG_H #include #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STDIO_H # include #endif #if HAVE_DIRENT_H # include # define NAMELEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMELEN(dirent) (dirent)->d_namelen # if HAVE_SYS_NDIR_H # include # endif # if HAVE_SYS_DIR_H # include # endif # if HAVE_NDIR_H # include # endif #endif #if STDC_HEADERS # include #else # if !HAVE_MEMCPY # define memcpy(d, s, n) bcopy((s), (d), (n)) # endif #endif #if HAVE_MEMORY_H # include #endif #if HAVE_SYS_STAT_H # include #endif #if HAVE_LIMITS_H # include #endif #if HAVE_SYS_TYPES_H # include #endif #if HAVE_UNISTD_H # include #endif #include #ifdef _WIN32 // _WIN32: Redefine _MAX_PATH to support Windows long paths (\\?\ prefix) // Windows normally defines _MAX_PATH as 260, but with long path support // enabled and the \\?\ prefix, paths can be up to 32767 characters # define _MAX_PATH 32767 #else # define _MAX_PATH 4095 #endif #if HAVE_ENDIAN_H # include # ifndef __LITTLE_ENDIAN # ifdef _LITTLE_ENDIAN # define __LITTLE_ENDIAN _LITTLE_ENDIAN # define __BIG_ENDIAN _BIG_ENDIAN # define __PDP_ENDIAN _PDP_ENDIAN # define __BYTE_ORDER _BYTE_ORDER # else # error does not define __LITTLE_ENDIAN etc. # endif # endif #else # define __LITTLE_ENDIAN 1234 # define __BIG_ENDIAN 4321 # define __PDP_ENDIAN 3412 # if WORDS_BIGENDIAN # define __BYTE_ORDER __BIG_ENDIAN # else # define __BYTE_ORDER __LITTLE_ENDIAN # endif #endif #else // HAVE_CONFIG_H #include #include #include #include #include #include #include #include #include #include #include #define _MAX_PATH 4095 #endif #endif #define NUM_TRANSFER_BUFFERS 2 // must be >= 2 #define NUM_PARPAR_BUFFERS 12 // maximum number of internal ParPar staging buffers #define MAX_CHUNK_SIZE 32*1048576 // too large chunks are likely detrimental to performance; set to 0 to disable #define LONGMULTIPLY // STL includes #include #include #include #include #include #include #include #include #include #include #include #ifdef offsetof #undef offsetof #endif #define offsetof(TYPE, MEMBER) ((size_t) ((char*)(&((TYPE *)1)->MEMBER) - (char*)1)) // par2cmdline includes #include "libpar2.h" #include "letype.h" #include "galois.h" #include "crc.h" #include "md5.h" #include "par2fileformat.h" #include "diskfile.h" #include "datablock.h" #include "criticalpacket.h" #include "par2creatorsourcefile.h" #include "mainpacket.h" #include "creatorpacket.h" #include "descriptionpacket.h" #include "verificationpacket.h" #include "recoverypacket.h" #include "par2repairersourcefile.h" #include "filechecksummer.h" #include "verificationhashtable.h" #include "par2creator.h" #include "par2repairer.h" #include "par1fileformat.h" #include "par1repairersourcefile.h" #include "par1repairer.h" #ifdef _WIN32 #include "utf8.h" #endif // Heap checking #ifdef _MSC_VER #define _CRTDBG_MAP_ALLOC #include #define DEBUG_NEW new(_NORMAL_BLOCK, THIS_FILE, __LINE__) #endif #endif // __PARCMDLINE_H__ par2cmdline-turbo-1.4.0/src/mainpacket.cpp000066400000000000000000000101141514221355600204550ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif // Construct the main packet from the source files and the block size bool MainPacket::Create(std::vector &sourcefiles, u64 _blocksize) { recoverablefilecount = totalfilecount =(u32)sourcefiles.size(); blocksize = _blocksize; // Allocate memory for the main packet with enough fileid entries MAINPACKET *packet = (MAINPACKET *)AllocatePacket(sizeof(MAINPACKET) + totalfilecount * sizeof(MD5Hash)); // Record the details we already know in the packet packet->header.magic = packet_magic; packet->header.length = packetlength; //packet->header.hash; // Compute shortly //packet->header.setid; // Compute shortly packet->header.type = mainpacket_type; packet->blocksize = _blocksize; packet->recoverablefilecount = totalfilecount; //packet->fileid; // Compute shortly // Sort the source files according to their fileid values if (totalfilecount > 1) { std::sort(sourcefiles.begin(), sourcefiles.end(), Par2CreatorSourceFile::CompareLess); } // Store the fileid values in the main packet std::vector::const_iterator sourcefile; MD5Hash *hash; for ((sourcefile=sourcefiles.begin()),(hash=packet->fileid); sourcefile!=sourcefiles.end(); ++sourcefile, ++hash) { *hash = (*sourcefile)->FileId(); } // Compute the set_id_hash MD5Context setidcontext; setidcontext.Update(&packet->blocksize, packetlength - offsetof(MAINPACKET, blocksize)); setidcontext.Final(packet->header.setid); // Compute the packet_hash MD5Context packetcontext; packetcontext.Update(&packet->header.setid, packetlength - offsetof(MAINPACKET, header.setid)); packetcontext.Final(packet->header.hash); return true; } // Load a main packet from a specified file bool MainPacket::Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) { // Is the packet large enough if (header.length < sizeof(MAINPACKET)) { return false; } // Is there a whole number of fileid values if (0 < (header.length - sizeof(MAINPACKET)) % sizeof(MD5Hash)) { return false; } // Is the packet too large if (header.length > sizeof(MAINPACKET) + 32768 * sizeof(MD5Hash)) { return false; } // Compute the total number of entries in the fileid array totalfilecount = (u32)(((size_t)header.length - sizeof(MAINPACKET)) / sizeof(MD5Hash)); MAINPACKET *packet = (MAINPACKET *)AllocatePacket((size_t)header.length); packet->header = header; // Read the rest of the packet from disk if (!diskfile->Read(offset + sizeof(PACKET_HEADER), &packet->blocksize, (size_t)packet->header.length - sizeof(PACKET_HEADER))) return false; // Does the packet have enough fileid values recoverablefilecount = packet->recoverablefilecount; if (recoverablefilecount > totalfilecount) { return false; } // Is the block size valid blocksize = packet->blocksize; if (blocksize == 0 || (blocksize & 3) != 0) { return false; } return true; } par2cmdline-turbo-1.4.0/src/mainpacket.h000066400000000000000000000060551514221355600201330ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __MAINPACKET_H__ #define __MAINPACKET_H__ // The main packet ties all other critical packets together. // It specifies the block size to use for both verification of // files and for the Reed Solomon computation. // It also specifies how many of the source files are repairable // and in what order they should be processed. class MainPacket : public CriticalPacket { public: // Construct the packet MainPacket(void) : blocksize(0) , totalfilecount(0) , recoverablefilecount(0) { } ~MainPacket(void) {} public: // Construct the main packet from the source file list and block size. // "sourcefiles" will be sorted base on their FileId value. bool Create(std::vector &sourcefiles, u64 _blocksize); // Load a main packet from a specified file bool Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); public: // Get the set id. const MD5Hash& SetId(void) const; // Get the block size. u64 BlockSize(void) const; // Get the file counts. u32 RecoverableFileCount(void) const; u32 TotalFileCount(void) const; // Get the fileid of one file const MD5Hash& FileId(u32 filenumber) const; protected: u64 blocksize; u32 totalfilecount; u32 recoverablefilecount; }; // Get the data block size inline u64 MainPacket::BlockSize(void) const { assert(packetdata != 0); return blocksize; } // Get the number of recoverable files inline u32 MainPacket::RecoverableFileCount(void) const { assert(packetdata != 0); return recoverablefilecount; } // Get the total number of files inline u32 MainPacket::TotalFileCount(void) const { assert(packetdata != 0); return totalfilecount; } // Get the file id hash of one of the files inline const MD5Hash& MainPacket::FileId(u32 filenumber) const { assert(packetdata != 0); assert(filenumberfileid()[filenumber]; return ((const MAINPACKET*)packetdata)->fileid[filenumber]; } inline const MD5Hash& MainPacket::SetId(void) const { return ((const MAINPACKET*)packetdata)->header.setid; } #endif // __MAINPACKET_H__ par2cmdline-turbo-1.4.0/src/md5.cpp000066400000000000000000000037241514221355600170370ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif // Convert hash values to hex std::ostream& operator<<(std::ostream &result, const MD5Hash &h) { char buffer[33]; snprintf(buffer, sizeof(buffer), "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", h.hash[15], h.hash[14], h.hash[13], h.hash[12], h.hash[11], h.hash[10], h.hash[9], h.hash[8], h.hash[7], h.hash[6], h.hash[5], h.hash[4], h.hash[3], h.hash[2], h.hash[1], h.hash[0]); return result << buffer; } std::string MD5Hash::print(void) const { char buffer[33]; snprintf(buffer, sizeof(buffer), "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", hash[15], hash[14], hash[13], hash[12], hash[11], hash[10], hash[9], hash[8], hash[7], hash[6], hash[5], hash[4], hash[3], hash[2], hash[1], hash[0]); return buffer; } par2cmdline-turbo-1.4.0/src/md5.h000066400000000000000000000063361514221355600165060ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __MD5_H__ #define __MD5_H__ #ifdef _WIN32 #pragma pack(push, 1) #define PACKED #else #define PACKED __attribute__ ((packed)) #endif // This file defines the MD5Hash and MD5Context objects which are used // to compute and manipulate the MD5 Hash values for a block of data. // Usage: // // MD5Context context; // context.Update(buffer, length); // // MD5Hash hash; // context.Final(hash); // MD5 Hash value struct MD5Hash; std::ostream& operator<<(std::ostream &s, const MD5Hash &hash); struct MD5Hash { // Comparison operators bool operator==(const MD5Hash &other) const; bool operator!=(const MD5Hash &other) const; bool operator<(const MD5Hash &other) const; bool operator>=(const MD5Hash &other) const; bool operator>(const MD5Hash &other) const; bool operator<=(const MD5Hash &other) const; // Convert value to hex friend std::ostream& operator<<(std::ostream &s, const MD5Hash &hash); std::string print(void) const; u8 hash[16]; // 16 byte MD5 Hash value } PACKED; // MD5 computation context with 64 byte buffer #include "../parpar/hasher/hasher.h" class MD5Context { MD5Single base; public: inline void Reset(void) { base.reset(); } // Process data from a buffer inline void Update(const void *buffer, size_t length) { base.update(buffer, length); } // Process 0 bytes void Update(size_t length) { base.updateZero(length); } // Compute the final hash value inline void Final(MD5Hash &output) { base.end(output.hash); } }; // Compare hash values inline bool MD5Hash::operator==(const MD5Hash &other) const { return (0==memcmp(&hash, &other.hash, sizeof(hash))); } inline bool MD5Hash::operator!=(const MD5Hash &other) const { return !operator==(other); } inline bool MD5Hash::operator<(const MD5Hash &other) const { size_t index = 15; while (index > 0 && hash[index] == other.hash[index]) { index--; } return hash[index] < other.hash[index]; } inline bool MD5Hash::operator>=(const MD5Hash &other) const { return !operator<(other); } inline bool MD5Hash::operator>(const MD5Hash &other) const { return other.operator<(*this); } inline bool MD5Hash::operator<=(const MD5Hash &other) const { return !other.operator<(*this); } #ifdef _WIN32 #pragma pack(pop) #endif #undef PACKED #endif // __MD5_H__ par2cmdline-turbo-1.4.0/src/md5_test.cpp000066400000000000000000000143251514221355600200750ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // This is just a simple set of tests on md5 hash. // The initial version just showed it was self-consistent, not accurate. #include "libpar2internal.h" #include #include #include "md5.h" // compares Update(length) to Update(buffer,buffersize) int test1() { unsigned char buffer[] = {0,0,0,0,0,0,0,0}; MD5Context context1; context1.Update(buffer, sizeof(buffer)); MD5Hash hash1; context1.Final(hash1); MD5Context context2; context2.Update(sizeof(buffer)); MD5Hash hash2; context2.Final(hash2); if (hash1 != hash2) { std::cerr << "hash1 = " << hash1 << std::endl; std::cerr << "hash2 = " << hash2 << std::endl; return 1; } return 0; } // "MD5 of a null string is d41d8cd98f00b204e9800998ecf8427e" // "MD5 of a null string is d4 1d 8c d9 8f 00 b2 04 e9 80 09 98 ec f8 42 7e" // according to https://news.ycombinator.com/item?id=5653698 int test2() { MD5Context context1; MD5Hash hash1; context1.Final(hash1); MD5Hash hash2; hash2.hash[0] = 0xd4; hash2.hash[1] = 0x1d; hash2.hash[2] = 0x8c; hash2.hash[3] = 0xd9; hash2.hash[4] = 0x8f; hash2.hash[5] = 0x00; hash2.hash[6] = 0xb2; hash2.hash[7] = 0x04; hash2.hash[8] = 0xe9; hash2.hash[9] = 0x80; hash2.hash[10] = 0x09; hash2.hash[11] = 0x98; hash2.hash[12] = 0xec; hash2.hash[13] = 0xf8; hash2.hash[14] = 0x42; hash2.hash[15] = 0x7e; for (int i = 0; i < 16; i++) { if (hash1.hash[i] != hash2.hash[i]) { std::cerr << "hash1 and hash2 differ in location " << i << std::endl; std::cerr << " hash1 = " << ((int) hash1.hash[i]) << std::endl; std::cerr << " hash2 = " << ((int) hash2.hash[i]) << std::endl; return 1; } } return 0; } // test comparison operators int test3() { MD5Context context1; MD5Hash hash1; context1.Final(hash1); MD5Hash hash2; hash2 = hash1; if (!(hash1 == hash2)) { std::cerr << "equal fail" << std::endl; return 1; } if (hash1 != hash2) { std::cerr << "not equal fail" << std::endl; return 1; } if (hash1 < hash2) { std::cerr << "less than fail" << std::endl; return 1; } if (hash1 > hash2) { std::cerr << "greater than fail" << std::endl; return 1; } if (!(hash1 <= hash2)) { std::cerr << "less than or equal fail" << std::endl; return 1; } if (!(hash1 >= hash2)) { std::cerr << "greater than or equal fail" << std::endl; return 1; } // make hash1 less than hash2 in first place hash1.hash[0] = 0x0; hash2.hash[0] = 0x1; if (hash1 == hash2) { std::cerr << "equal fail 2" << std::endl; return 1; } if (!(hash1 != hash2)) { std::cerr << "not equal fail 2" << std::endl; return 1; } if (!(hash1 < hash2)) { std::cerr << "less than fail 2" << std::endl; return 1; } if (hash1 > hash2) { std::cerr << "greater than fail 2" << std::endl; return 1; } if (!(hash1 <= hash2)) { std::cerr << "less than or equal fail 2" << std::endl; return 1; } if (hash1 >= hash2) { std::cerr << "greater than or equal fail 2" << std::endl; return 1; } // make them equal again hash1.hash[0] = 0x0; hash2.hash[0] = 0x0; // now make hash1 less than in 15th place hash1.hash[15] = 0x0; hash2.hash[15] = 0x1; if (hash1 == hash2) { std::cerr << "equal fail 3" << std::endl; return 1; } if (!(hash1 != hash2)) { std::cerr << "not equal fail 3" << std::endl; return 1; } if (!(hash1 < hash2)) { std::cerr << "less than fail 3" << std::endl; return 1; } if (hash1 > hash2) { std::cerr << "greater than fail 3" << std::endl; return 1; } if (!(hash1 <= hash2)) { std::cerr << "less than or equal fail 3" << std::endl; return 1; } if (hash1 >= hash2) { std::cerr << "greater than or equal fail 3" << std::endl; return 1; } return 0; } // generate random data. // put it into two different contexts in different lengths // make sure output is the same. int test4() { srand(345087209); unsigned char buffer[32*1024]; for (unsigned int i = 0; i < sizeof(buffer); i++) { buffer[i] = (unsigned char) (rand() % 256); } MD5Context context1; unsigned int offset = 0; while (offset < sizeof(buffer)) { unsigned int length = (int) (rand() % 256); if (offset + length > sizeof(buffer)) length = sizeof(buffer) - offset; context1.Update(buffer + offset, length); offset += length; } MD5Hash hash1; context1.Final(hash1); MD5Context context2; offset = 0; while (offset < sizeof(buffer)) { unsigned int length = (int) (rand() % 256); if (offset + length > sizeof(buffer)) length = sizeof(buffer) - offset; context2.Update(buffer + offset, length); offset += length; } MD5Hash hash2; context2.Final(hash2); if (hash1 != hash2) { std::cerr << "random hash1 = " << hash1 << std::endl; std::cerr << "random hash2 = " << hash2 << std::endl; return 1; } return 0; } int main() { setup_hasher(); if (test1()) { std::cerr << "FAILED: test1" << std::endl; return 1; } if (test2()) { std::cerr << "FAILED: test2" << std::endl; return 1; } if (test3()) { std::cerr << "FAILED: test3" << std::endl; return 1; } if (test4()) { std::cerr << "FAILED: test4" << std::endl; return 1; } std::cout << "SUCCESS: md5_test complete." << std::endl; return 0; } par2cmdline-turbo-1.4.0/src/par1fileformat.cpp000066400000000000000000000020341514221355600212570ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" PAR1MAGIC par1_magic = {{'P', 'A', 'R', '\0', '\0', '\0', '\0', '\0'}}; par2cmdline-turbo-1.4.0/src/par1fileformat.h000066400000000000000000000043071514221355600207310ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __PAR1FILEFORMAT_H__ #define __PAR1FILEFORMAT_H__ #ifdef _WIN32 #pragma pack(push, 1) #define PACKED #else #define PACKED __attribute__ ((packed)) #endif #ifdef _MSC_VER #pragma warning(disable:4200) #endif struct PAR1MAGIC {u8 magic[8];}PACKED; struct PAR1FILEHEADER { PAR1MAGIC magic; leu32 fileversion; leu32 programversion; MD5Hash controlhash; MD5Hash sethash; leu64 volumenumber; leu64 numberoffiles; leu64 filelistoffset; leu64 filelistsize; leu64 dataoffset; leu64 datasize; }PACKED; struct PAR1FILEENTRY { leu64 entrysize; leu64 status; leu64 filesize; MD5Hash hashfull; MD5Hash hash16k; leu16 name[]; }PACKED; enum FILEENTRYSTATUS { INPARITYVOLUME = 1, CHECKED = 2, }; #ifdef _MSC_VER #pragma warning(default:4200) #endif #ifdef _WIN32 #pragma pack(pop) #endif #undef PACKED // Operators for comparing the MAGIC values inline bool operator == (const PAR1MAGIC &left, const PAR1MAGIC &right) { return (0==memcmp(&left, &right, sizeof(left))); } inline bool operator != (const PAR1MAGIC &left, const PAR1MAGIC &right) { return !operator==(left, right); } extern PAR1MAGIC par1_magic; #endif //__PAR1FILEFORMAT_H__ par2cmdline-turbo-1.4.0/src/par1repairer.cpp000066400000000000000000001212571514221355600207510ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif static u32 smartpar11 = 0x03000101; Par1Repairer::Par1Repairer(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel) : sout(sout) , serr(serr) , noiselevel(noiselevel) , searchpath() , diskfilemap() , recoveryblocks() , filelist(0) , filelistsize(0) , blocksize(0) , chunksize(0) , sourcefiles() , extrafiles() , completefilecount(0) , renamedfilecount(0) , damagedfilecount(0) , missingfilecount(0) , verifylist() , backuplist() , parlist() , inputblocks() , outputblocks() , rs() , progress(0) , totaldata(0) , inputbuffersize(0) , inputbuffer(0) , outputbufferalignment(0) , outputbuffersize(0) , outputbuffer(0) , ignore16kfilehash(false) { setup_hasher(); } Par1Repairer::~Par1Repairer(void) { delete [] (u8*)inputbuffer; delete [] (u8*)outputbuffer; std::map::iterator i = recoveryblocks.begin(); while (i != recoveryblocks.end()) { DataBlock *datablock = i->second; delete datablock; ++i; } std::vector::iterator sourceiterator = sourcefiles.begin(); while (sourceiterator != sourcefiles.end()) { Par1RepairerSourceFile *sourcefile = *sourceiterator; delete sourcefile; ++sourceiterator; } sourceiterator = extrafiles.begin(); while (sourceiterator != extrafiles.end()) { Par1RepairerSourceFile *sourcefile = *sourceiterator; delete sourcefile; ++sourceiterator; } delete [] filelist; } Result Par1Repairer::Process(const size_t memorylimit, // basepath is not used by Par1 const u32 nthreads, // filethreads is not used by Par1 std::string parfilename, const std::vector &extrafiles, const bool dorepair, // derived from operation const bool purgefiles // skipdata is not used by Par1 // skipleaway is not used by Par1 ) { // Determine the searchpath from the location of the main PAR file std::string name; DiskFile::SplitFilename(parfilename, searchpath, name); // Load the main PAR file if (!LoadRecoveryFile(searchpath + name)) return eLogicError; // Load other PAR files related to the main PAR file if (!LoadOtherRecoveryFiles(parfilename)) return eLogicError; // Load any extra PAR files specified on the command line if (!LoadExtraRecoveryFiles(extrafiles)) return eLogicError; if (noiselevel > nlQuiet) sout << std::endl << "Verifying source files:" << std::endl << std::endl; // Check for the existence of and verify each of the source files if (!VerifySourceFiles()) return eFileIOError; if (completefilecount nlQuiet) sout << std::endl << "Scanning extra files:" << std::endl << std::endl; // Check any other files specified on the command line to see if they are // actually copies of the source files that have the wrong filename if (!VerifyExtraFiles(extrafiles)) return eLogicError; } // Find out how much data we have found UpdateVerificationResults(); if (noiselevel > nlSilent) sout << std::endl; // Check the verification results and report the details if (!CheckVerificationResults()) return eRepairNotPossible; // Are any of the files incomplete if (completefilecount nlSilent) sout << std::endl; // Rename any damaged or missnamed target files. if (!RenameTargetFiles()) return eFileIOError; // Are we still missing any files if (completefilecount nlSilent) sout << std::endl; // Set the total amount of data to be processed. progress = 0; totaldata = blocksize * sourcefiles.size() * verifylist.size(); // Start at an offset of 0 within a block. u64 blockoffset = 0; while (blockoffset < blocksize) // Continue until the end of the block. { // Work out how much data to process this time. size_t blocklength = (size_t)std::min((u64)chunksize, blocksize-blockoffset); // Read source data, process it through the RS matrix and write it to disk. if (!ProcessData(blockoffset, blocklength)) { // Delete all of the partly reconstructed files DeleteIncompleteTargetFiles(); return eFileIOError; } // Advance to the need offset within each block blockoffset += blocklength; } if (noiselevel > nlSilent) sout << std::endl << "Verifying repaired files:" << std::endl << std::endl; // Verify that all of the reconstructed target files are now correct if (!VerifyTargetFiles()) { // Delete all of the partly reconstructed files DeleteIncompleteTargetFiles(); return eFileIOError; } } // Are all of the target files now complete? if (completefilecount nlSilent) sout << std::endl << "Repair complete." << std::endl; } } else { return eRepairPossible; } } if (purgefiles == true) { RemoveBackupFiles(); RemoveParFiles(); } return eSuccess; } bool Par1Repairer::LoadRecoveryFile(std::string filename) { // Skip the file if it has already been processed if (diskfilemap.Find(filename) != 0) { return true; } DiskFile *diskfile = new DiskFile(sout, serr, output_lock); // Open the file if (!diskfile->Open(filename)) { // If we could not open the file, ignore the error and // proceed to the next file delete diskfile; return true; } if (noiselevel > nlSilent) { std::string path; std::string name; DiskFile::SplitFilename(filename, path, name); sout << "Loading \"" << name << "\"." << std::endl; } parlist.push_back(filename); bool havevolume = false; u32 volumenumber = 0; // How big is the file u64 filesize = diskfile->FileSize(); if (filesize >= sizeof(PAR1FILEHEADER)) { // Allocate a buffer to read data into size_t buffersize = (size_t)std::min((u64)1048576, filesize); u8 *buffer = new u8[buffersize]; do { PAR1FILEHEADER fileheader; if (!diskfile->Read(0, &fileheader, sizeof(fileheader))) break; // Is this really a PAR file? if (fileheader.magic != par1_magic) break; // Is the version number correct? if (fileheader.fileversion != 0x00010000) break; ignore16kfilehash = (fileheader.programversion == smartpar11); // Prepare to carry out MD5 Hash check of the Control Hash MD5Context context; u64 offset = offsetof(PAR1FILEHEADER, sethash); // Process until the end of the file is reached while (offset < filesize) { // How much data should we read? size_t want = (size_t)std::min((u64)buffersize, filesize-offset); if (!diskfile->Read(offset, buffer, want)) break; context.Update(buffer, want); offset += want; } // Did we read the whole file if (offset < filesize) break; // Compute the hash value MD5Hash hash; context.Final(hash); // Is it correct? if (hash != fileheader.controlhash) break; // Check that the volume number is ok if (fileheader.volumenumber >= 256) break; // Are there any files? if (fileheader.numberoffiles == 0 || fileheader.filelistoffset < sizeof(PAR1FILEHEADER) || fileheader.filelistsize == 0) break; // Verify that the file list and data offsets are ok if ((fileheader.filelistoffset + fileheader.filelistsize > filesize) || (fileheader.datasize && (fileheader.dataoffset < sizeof(fileheader) || fileheader.dataoffset + fileheader.datasize > filesize)) || (fileheader.datasize && ((fileheader.filelistoffset <= fileheader.dataoffset && fileheader.dataoffset < fileheader.filelistoffset+fileheader.filelistsize) || (fileheader.dataoffset <= fileheader.filelistoffset && fileheader.filelistoffset < fileheader.dataoffset + fileheader.datasize)))) break; // Check the size of the file list if (fileheader.filelistsize > 200000) break; // If we already have a copy of the file list, make sure this one has the same size if (filelist != 0 && filelistsize != fileheader.filelistsize) break; // Allocate a buffer to hold a copy of the file list unsigned char *temp = new unsigned char[(size_t)fileheader.filelistsize]; // Read the file list into the buffer if (!diskfile->Read(fileheader.filelistoffset, temp, (size_t)fileheader.filelistsize)) { delete [] temp; break; } // If we already have a copy of the file list, make sure this copy is identical if (filelist != 0) { bool match = (0 == memcmp(filelist, temp, filelistsize)); delete [] temp; if (!match) break; } else { // Prepare to scan the file list unsigned char *current = temp; size_t remaining = (size_t)fileheader.filelistsize; unsigned int fileindex = 0; // Allocate a buffer to copy each file entry into so that // all fields will be correctly aligned in memory. PAR1FILEENTRY *fileentry = (PAR1FILEENTRY*)new u64[(remaining + sizeof(u64)-1)/sizeof(u64)]; // Process until we run out of files or data while (remaining > 0 && fileindex < fileheader.numberoffiles) { // Copy fixed portion of file entry memcpy((void*)fileentry, (void*)current, sizeof(PAR1FILEENTRY)); // Is there enough data remaining if (remaining < sizeof(fileentry->entrysize) || remaining < fileentry->entrysize) break; // Check the length of the filename if (fileentry->entrysize <= sizeof(PAR1FILEENTRY)) break; // Check the file size if (blocksize < fileentry->filesize) blocksize = fileentry->filesize; // Copy whole of file entry memcpy((void*)fileentry, (void*)current, (size_t)(u64)fileentry->entrysize); // Create source file and add it to the appropriate list Par1RepairerSourceFile *sourcefile = new Par1RepairerSourceFile(sout, serr, noiselevel, fileentry, searchpath); if (fileentry->status & INPARITYVOLUME) { sourcefiles.push_back(sourcefile); } else { extrafiles.push_back(sourcefile); } remaining -= (size_t)fileentry->entrysize; current += (size_t)fileentry->entrysize; fileindex++; } delete [] (u64*)fileentry; // Did we find the correct number of files if (fileindex < fileheader.numberoffiles) { std::vector::iterator i = sourcefiles.begin(); while (i != sourcefiles.end()) { Par1RepairerSourceFile *sourcefile = *i; delete sourcefile; ++i; } sourcefiles.clear(); i = extrafiles.begin(); while (i != extrafiles.end()) { Par1RepairerSourceFile *sourcefile = *i; delete sourcefile; ++i; } extrafiles.clear(); delete [] temp; break; } filelist = temp; filelistsize = (u32)fileheader.filelistsize; } // Is this a recovery volume? if (fileheader.volumenumber > 0) { // Make sure there is data and that it is the correct size if (fileheader.dataoffset == 0 || fileheader.datasize != blocksize) break; // What volume number is this? volumenumber = (u32)(fileheader.volumenumber - 1); // Do we already have this volume? if (recoveryblocks.find(volumenumber) == recoveryblocks.end()) { // Create a data block DataBlock *datablock = new DataBlock; datablock->SetLength(blocksize); datablock->SetLocation(diskfile, fileheader.dataoffset); // Store it in the map recoveryblocks.insert(std::pair(volumenumber, datablock)); havevolume = true; } } } while (false); delete [] buffer; } // We have finished with the file for now diskfile->Close(); if (noiselevel > nlQuiet) { if (havevolume) { sout << "Loaded recovery volume " << volumenumber << std::endl; } else { sout << "No new recovery volumes found" << std::endl; } } // Remember that the file was processed bool success = diskfilemap.Insert(diskfile); assert(success); return true; } bool Par1Repairer::LoadOtherRecoveryFiles(std::string filename) { // Split the original PAR filename into path and name parts std::string path; std::string name; DiskFile::SplitFilename(filename, path, name); // Find the file extension std::string::size_type where = name.find_last_of('.'); if (where != std::string::npos) { // remove it name = name.substr(0, where); } // Search for additional PAR files std::string wildcard = name + ".???"; std::unique_ptr< std::list > files( DiskFile::FindFiles(path, wildcard, false) ); for (std::list::const_iterator s=files->begin(); s!=files->end(); ++s) { std::string filename = *s; // Find the file extension where = filename.find_last_of('.'); if (where != std::string::npos) { std::string tail = filename.substr(where+1); // Check the file extension is the correct form if ((tail[0] == 'P' || tail[0] == 'p') && ( ((tail[1] == 'A' || tail[1] == 'a') && (tail[2] == 'R' || tail[2] == 'r')) || (isdigit(tail[1]) && isdigit(tail[2])) )) { LoadRecoveryFile(filename); } } } //delete files; This is taken care of by unique_ptr<>. return true; } // Load packets from any other PAR files whose names are given on the command line bool Par1Repairer::LoadExtraRecoveryFiles(const std::vector &extrafiles) { for (std::vector::const_iterator i=extrafiles.begin(); i!=extrafiles.end(); i++) { std::string filename = *i; // Find the file extension std::string::size_type where = filename.find_last_of('.'); if (where != std::string::npos) { std::string tail = filename.substr(where+1); // Check the file extension is the correct form if ((tail[0] == 'P' || tail[0] == 'p') && ( ((tail[1] == 'A' || tail[1] == 'a') && (tail[2] == 'R' || tail[2] == 'r')) || (isdigit(tail[1]) && isdigit(tail[2])) )) { LoadRecoveryFile(filename); } } } return true; } // Attempt to verify all of the source files bool Par1Repairer::VerifySourceFiles(void) { bool finalresult = true; std::vector::iterator sourceiterator = sourcefiles.begin(); while (sourceiterator != sourcefiles.end()) { Par1RepairerSourceFile *sourcefile = *sourceiterator; std::string filename = sourcefile->FileName(); // Check to see if we have already used this file if (diskfilemap.Find(filename) != 0) { std::string path; std::string name; DiskFile::SplitRelativeFilename(filename, path, name); // The file has already been used! serr << "Source file " << name << " is a duplicate." << std::endl; finalresult = false; } else { DiskFile *diskfile = new DiskFile(sout, serr, output_lock); // Does the target file exist if (diskfile->Open(filename)) { // Yes. Record that fact. sourcefile->SetTargetExists(true); // Remember that the DiskFile is the target file sourcefile->SetTargetFile(diskfile); // Remember that we have processed this file bool success = diskfilemap.Insert(diskfile); assert(success); // Do the actual verification if (!VerifyDataFile(diskfile, sourcefile)) finalresult = false; // We have finished with the file for now diskfile->Close(); // Find out how much data we have found UpdateVerificationResults(); } else { // The file does not exist. delete diskfile; if (noiselevel > nlSilent) { std::string path; std::string name; DiskFile::SplitFilename(filename, path, name); sout << "Target: \"" << name << "\" - missing." << std::endl; } } } ++sourceiterator; } return finalresult; } // Scan any extra files specified on the command line bool Par1Repairer::VerifyExtraFiles(const std::vector &extrafiles) { for (std::vector::const_iterator i=extrafiles.begin(); i!=extrafiles.end() && completefilecountOpen(filename)) { delete diskfile; continue; } // Remember that we have processed this file bool success = diskfilemap.Insert(diskfile); assert(success); // Do the actual verification VerifyDataFile(diskfile, 0); // Ignore errors // We have finished with the file for now diskfile->Close(); // Find out how much data we have found UpdateVerificationResults(); } } } return true; } bool Par1Repairer::VerifyDataFile(DiskFile *diskfile, Par1RepairerSourceFile *sourcefile) { Par1RepairerSourceFile *match = 0; std::string path; std::string name; DiskFile::SplitFilename(diskfile->FileName(), path, name); // How big is the file we are checking u64 filesize = diskfile->FileSize(); if (filesize == 0) { if (noiselevel > nlSilent) { sout << "Target: \"" << name << "\" - empty." << std::endl; } return true; } // Search for the first file that is the correct size std::vector::iterator sourceiterator = sourcefiles.begin(); while (sourceiterator != sourcefiles.end() && filesize != (*sourceiterator)->FileSize()) { ++sourceiterator; } // Are there any files that are the correct size? if (sourceiterator != sourcefiles.end()) { // Allocate a buffer to compute the file hash size_t buffersize = (size_t)std::min((u64)1048576, filesize); char *buffer = new char[buffersize]; // Read the first 16k of the file size_t want = (size_t)std::min((u64)16384, filesize); if (!diskfile->Read(0, buffer, want)) { delete [] buffer; return false; } // Compute the MD5 hash of the first 16k MD5Context contextfull; contextfull.Update(buffer, want); MD5Context context16k = contextfull; MD5Hash hash16k; context16k.Final(hash16k); if (!ignore16kfilehash) { // Search for the first file that has the correct 16k hash while (sourceiterator != sourcefiles.end() && (filesize != (*sourceiterator)->FileSize() || hash16k != (*sourceiterator)->Hash16k())) { ++sourceiterator; } } // Are there any files with the correct 16k hash? if (sourceiterator != sourcefiles.end()) { // Compute the MD5 hash of the whole file if (filesize > 16384) { u64 progress = 0; u64 offset = 16384; while (offset < filesize) { if (noiselevel > nlQuiet) { // Update a progress indicator u32 oldfraction = (u32)(1000 * (progress) / filesize); u32 newfraction = (u32)(1000 * (progress=offset) / filesize); if (oldfraction != newfraction) { sout << "Scanning: \"" << name << "\": " << newfraction/10 << '.' << newfraction%10 << "%\r" << std::flush; } } want = (size_t)std::min((u64)buffersize, filesize-offset); if (!diskfile->Read(offset, buffer, want)) { delete [] buffer; return false; } contextfull.Update(buffer, want); offset += want; } } MD5Hash hashfull; contextfull.Final(hashfull); // Search for the first file that has the correct full hash while (sourceiterator != sourcefiles.end() && (filesize != (*sourceiterator)->FileSize() || (!ignore16kfilehash && hash16k != (*sourceiterator)->Hash16k()) || hashfull != (*sourceiterator)->HashFull())) { ++sourceiterator; } // Are there any files with the correct full hash? if (sourceiterator != sourcefiles.end()) { // If a source file was originally specified, check to see if it is a match if (sourcefile != 0 && sourcefile->FileSize() == filesize && (ignore16kfilehash || sourcefile->Hash16k() == hash16k) && sourcefile->HashFull() == hashfull) { match = sourcefile; } else { // Search for a file which matches and has not already been matched while (sourceiterator != sourcefiles.end() && (filesize != (*sourceiterator)->FileSize() || (!ignore16kfilehash && hash16k != (*sourceiterator)->Hash16k()) || hashfull != (*sourceiterator)->HashFull() || (*sourceiterator)->GetCompleteFile() != 0)) { ++sourceiterator; } // Did we find a match if (sourceiterator != sourcefiles.end()) { match = *sourceiterator; } } } } delete [] buffer; } // Did we find a match if (match != 0) { match->SetCompleteFile(diskfile); if (noiselevel > nlSilent) { // Was the match the file we were originally looking for if (match == sourcefile) { sout << "Target: \"" << name << "\" - found." << std::endl; } // Were we looking for a specific file else if (sourcefile != 0) { std::string targetname; DiskFile::SplitFilename(sourcefile->FileName(), path, targetname); sout << "Target: \"" << name << "\" - is a match for \"" << targetname << "\"." << std::endl; } } else { // WARNING - this branch does nothing. The "if" above // makes sure of that. I don't know if it's the result // of a bad merge or what. I couldn't figure out what // the original author meant. If you can figure that // out, please fix it! if (noiselevel > nlSilent) { std::string targetname; DiskFile::SplitFilename(match->FileName(), path, targetname); sout << "File: \"" << name << "\" - is a match for \"" << targetname << "\"." << std::endl; } } } else { if (noiselevel > nlSilent) sout << "File: \"" << name << "\" - no data found." << std::endl; } return true; } void Par1Repairer::UpdateVerificationResults(void) { completefilecount = 0; renamedfilecount = 0; damagedfilecount = 0; missingfilecount = 0; std::vector::iterator sf = sourcefiles.begin(); // Check the recoverable files while (sf != sourcefiles.end()) { Par1RepairerSourceFile *sourcefile = *sf; // Was a perfect match for the file found if (sourcefile->GetCompleteFile() != 0) { // Is it the target file or a different one if (sourcefile->GetCompleteFile() == sourcefile->GetTargetFile()) { completefilecount++; } else { renamedfilecount++; } } else { // Does the target file exist if (sourcefile->GetTargetExists()) { damagedfilecount++; } else { missingfilecount++; } } ++sf; } } bool Par1Repairer::CheckVerificationResults(void) { // Is repair needed if (completefilecount < sourcefiles.size() || renamedfilecount > 0 || damagedfilecount > 0 || missingfilecount > 0) { if (noiselevel > nlSilent) sout << "Repair is required." << std::endl; if (noiselevel > nlQuiet) { if (renamedfilecount > 0) sout << renamedfilecount << " file(s) have the wrong name." << std::endl; if (missingfilecount > 0) sout << missingfilecount << " file(s) are missing." << std::endl; if (damagedfilecount > 0) sout << damagedfilecount << " file(s) exist but are damaged." << std::endl; if (completefilecount > 0) sout << completefilecount << " file(s) are ok." << std::endl; } // Is repair possible if (recoveryblocks.size() >= damagedfilecount+missingfilecount) { if (noiselevel > nlSilent) sout << "Repair is possible." << std::endl; if (noiselevel > nlQuiet) { if (recoveryblocks.size() > damagedfilecount+missingfilecount) sout << "You have an excess of " << (u32)recoveryblocks.size() - (damagedfilecount+missingfilecount) << " recovery files." << std::endl; if (damagedfilecount+missingfilecount > 0) sout << damagedfilecount+missingfilecount << " recovery files will be used to repair." << std::endl; else if (recoveryblocks.size()) sout << "None of the recovery files will be used for the repair." << std::endl; } return true; } else { if (noiselevel > nlSilent) { sout << "Repair is not possible." << std::endl; sout << "You need " << damagedfilecount+missingfilecount - recoveryblocks.size() << " more recovery files to be able to repair." << std::endl; } return false; } } else { if (noiselevel > nlSilent) sout << "All files are correct, repair is not required." << std::endl; return true; } return true; } bool Par1Repairer::RenameTargetFiles(void) { std::vector::iterator sf = sourcefiles.begin(); // Rename any damaged target files while (sf != sourcefiles.end()) { Par1RepairerSourceFile *sourcefile = *sf; // If the target file exists but is not a complete version of the file if (sourcefile->GetTargetExists() && sourcefile->GetTargetFile() != sourcefile->GetCompleteFile()) { DiskFile *targetfile = sourcefile->GetTargetFile(); // Rename it diskfilemap.Remove(targetfile); if (!targetfile->Rename()) return false; backuplist.push_back(targetfile); bool success = diskfilemap.Insert(targetfile); assert(success); // We no longer have a target file sourcefile->SetTargetExists(false); sourcefile->SetTargetFile(0); } ++sf; } sf = sourcefiles.begin(); // Rename any missnamed but complete versions of the files while (sf != sourcefiles.end()) { Par1RepairerSourceFile *sourcefile = *sf; // If there is no targetfile and there is a complete version if (sourcefile->GetTargetFile() == 0 && sourcefile->GetCompleteFile() != 0) { DiskFile *targetfile = sourcefile->GetCompleteFile(); // Rename it diskfilemap.Remove(targetfile); if (!targetfile->Rename(sourcefile->FileName())) return false; bool success = diskfilemap.Insert(targetfile); assert(success); // This file is now the target file sourcefile->SetTargetExists(true); sourcefile->SetTargetFile(targetfile); // We have one more complete file completefilecount++; } ++sf; } return true; } // Work out which files are being repaired, create them, and allocate // target DataBlocks to them, and remember them for later verification. bool Par1Repairer::CreateTargetFiles(void) { std::vector::iterator sf = sourcefiles.begin(); // Create any missing target files while (sf != sourcefiles.end()) { Par1RepairerSourceFile *sourcefile = *sf; // If the file does not exist if (!sourcefile->GetTargetExists()) { DiskFile *targetfile = new DiskFile(sout, serr, output_lock); std::string filename = sourcefile->FileName(); u64 filesize = sourcefile->FileSize(); // Create the target file if (!targetfile->Create(filename, filesize)) { delete targetfile; return false; } // This file is now the target file sourcefile->SetTargetExists(true); sourcefile->SetTargetFile(targetfile); // Remember this file bool success = diskfilemap.Insert(targetfile); assert(success); sourcefile->SetTargetBlock(targetfile); // Add the file to the list of those that will need to be verified // once the repair has completed. verifylist.push_back(sourcefile); } ++sf; } return true; } // Work out which data blocks are available, which need to be recreated, // and compute the appropriate Reed Solomon matrix. bool Par1Repairer::ComputeRSmatrix(void) { inputblocks.resize(sourcefiles.size()); // The DataBlocks that will read from disk outputblocks.resize(verifylist.size()); // Those DataBlocks that will re recalculated std::vector::iterator inputblock = inputblocks.begin(); std::vector::iterator outputblock = outputblocks.begin(); // Build an array listing which source data blocks are present and which are missing std::vector present; present.resize(sourcefiles.size()); std::vector::iterator sourceiterator = sourcefiles.begin(); std::vector::iterator pres = present.begin(); // Iterate through all source files while (sourceiterator != sourcefiles.end()) { Par1RepairerSourceFile *sourcefile = *sourceiterator; DataBlock *sourceblock = sourcefile->SourceBlock(); DataBlock *targetblock = sourcefile->TargetBlock(); // Was this block found if (sourceblock->IsSet()) { // Open the file the block was found in. if (!sourceblock->Open()) { return false; } // Record that the block was found *pres = true; // Add the block to the list of those which will be read // as input (and which might also need to be copied). *inputblock = sourceblock; ++inputblock; } else { // Record that the block was missing *pres = false; // Add the block to the list of those to be written *outputblock = targetblock; ++outputblock; } ++sourceiterator; ++pres; } // Set the number of source blocks and which of them are present if (!rs.SetInput(present, sout, serr)) { return false; } // Start iterating through the available recovery packets std::map::iterator recoveryiterator = recoveryblocks.begin(); // Continue to fill the remaining list of data blocks to be read while (inputblock != inputblocks.end()) { // Get the next available recovery block u32 exponent = recoveryiterator->first; DataBlock *recoveryblock = recoveryiterator->second; // Make sure the file is open if (!recoveryblock->Open()) { return false; } // Add the recovery block to the list of blocks that will be read *inputblock = recoveryblock; // Record that the corresponding exponent value is the next one // to use in the RS matrix if (!rs.SetOutput(true, (u16)exponent)) { return false; } ++inputblock; ++recoveryiterator; } // If we need to, compute and solve the RS matrix if (verifylist.empty()) { return true; } bool success = rs.Compute(noiselevel, sout, serr); return success; } // Allocate memory buffers for reading and writing data to disk. bool Par1Repairer::AllocateBuffers(size_t memorylimit) { // Would single pass processing use too much memory if (blocksize * verifylist.size() > memorylimit) { // Pick a size that is small enough chunksize = ~3 & (memorylimit / verifylist.size()); } else { chunksize = (size_t)blocksize; } // Allocate the two buffers inputbuffersize = (size_t)chunksize; inputbuffer = new u8[inputbuffersize]; outputbufferalignment = (inputbuffersize + sizeof(u32)-1) & ~(sizeof(u32)-1); outputbuffersize = outputbufferalignment * verifylist.size(); outputbuffer = new u8[outputbuffersize]; if (inputbuffer == NULL || outputbuffer == NULL) { serr << "Could not allocate buffer memory." << std::endl; return false; } return true; } // Read source data, process it through the RS matrix and write it to disk. bool Par1Repairer::ProcessData(u64 blockoffset, size_t blocklength) { u64 totalwritten = 0; // Clear the output buffer memset(outputbuffer, 0, outputbuffersize); std::vector::iterator inputblock = inputblocks.begin(); u32 inputindex = 0; // Are there any blocks which need to be reconstructed if (!verifylist.empty()) { // For each input block while (inputblock != inputblocks.end()) { // Read data from the current input block if (!(*inputblock)->ReadData(blockoffset, blocklength, inputbuffer)) return false; // For each output block for (u32 outputindex=0; outputindex nlQuiet) { // Update a progress indicator u32 oldfraction = (u32)(1000 * progress / totaldata); progress += blocklength; u32 newfraction = (u32)(1000 * progress / totaldata); if (oldfraction != newfraction) { sout << "Repairing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << std::flush; } } } ++inputblock; ++inputindex; } } if (noiselevel > nlQuiet) sout << "Writing recovered data\r"; // For each output block that has been recomputed std::vector::iterator outputblock = outputblocks.begin(); for (u32 outputindex=0; outputindexWriteData(blockoffset, blocklength, outbuf, wrote)) return false; totalwritten += wrote; ++outputblock; } if (noiselevel > nlQuiet) sout << "Wrote " << totalwritten << " bytes to disk" << std::endl; return true; } // Verify that all of the reconstructed target files are now correct bool Par1Repairer::VerifyTargetFiles(void) { bool finalresult = true; // Verify the target files in alphabetical order // std::sort(verifylist.begin(), verifylist.end(), SortSourceFilesByFileName); // Iterate through each file in the verification list for (std::list::iterator sf = verifylist.begin(); sf != verifylist.end(); ++sf) { Par1RepairerSourceFile *sourcefile = *sf; DiskFile *targetfile = sourcefile->GetTargetFile(); // Close the file if (targetfile->IsOpen()) targetfile->Close(); // Say we don't have a complete version of the file sourcefile->SetCompleteFile(0); // Re-open the target file if (!targetfile->Open()) { finalresult = false; continue; } // Verify the file again if (!VerifyDataFile(targetfile, sourcefile)) finalresult = false; // Close the file again targetfile->Close(); // Find out how much data we have found UpdateVerificationResults(); } return finalresult; } // Delete all of the partly reconstructed files bool Par1Repairer::DeleteIncompleteTargetFiles(void) { std::list::iterator sf = verifylist.begin(); // Iterate through each file in the verification list while (sf != verifylist.end()) { Par1RepairerSourceFile *sourcefile = *sf; if (sourcefile->GetTargetExists()) { DiskFile *targetfile = sourcefile->GetTargetFile(); // Close and delete the file if (targetfile->IsOpen()) targetfile->Close(); targetfile->Delete(); // Forget the file diskfilemap.Remove(targetfile); delete targetfile; // There is no target file sourcefile->SetTargetExists(false); sourcefile->SetTargetFile(0); } ++sf; } return true; } bool Par1Repairer::RemoveBackupFiles(void) { std::vector::iterator bf = backuplist.begin(); if (noiselevel > nlSilent && bf != backuplist.end()) { sout << std::endl << "Purge backup files." << std::endl; } // Iterate through each file in the backuplist while (bf != backuplist.end()) { if (noiselevel > nlSilent) { std::string name; std::string path; DiskFile::SplitFilename((*bf)->FileName(), path, name); sout << "Remove \"" << name << "\"." << std::endl; } if ((*bf)->IsOpen()) (*bf)->Close(); (*bf)->Delete(); ++bf; } return true; } bool Par1Repairer::RemoveParFiles(void) { if (noiselevel > nlSilent && !parlist.empty()) { sout << std::endl << "Purge par files." << std::endl; } for (std::list::const_iterator s=parlist.begin(); s!=parlist.end(); ++s) { DiskFile *diskfile = new DiskFile(sout, serr, output_lock); if (diskfile->Open(*s)) { if (noiselevel > nlSilent) { std::string name; std::string path; DiskFile::SplitFilename((*s), path, name); sout << "Remove \"" << name << "\"." << std::endl; } if (diskfile->IsOpen()) diskfile->Close(); diskfile->Delete(); } delete diskfile; } return true; } par2cmdline-turbo-1.4.0/src/par1repairer.h000066400000000000000000000132061514221355600204100ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __PAR1REPAIRER_H__ #define __PAR1REPAIRER_H__ #include "reedsolomon.h" class Par1Repairer { public: Par1Repairer(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel); ~Par1Repairer(void); Result Process(const size_t memorylimit, // basepath is not used by Par1 const u32 nthreads, // filethreads is not used by Par1 std::string parfilename, const std::vector &extrafiles, const bool dorepair, // derived from operation const bool purgefiles // skipdata is not used by Par1 // skipleaway is not used by Par1 ); protected: // Load the main PAR file bool LoadRecoveryFile(std::string filename); // Load other PAR files related to the main PAR file bool LoadOtherRecoveryFiles(std::string filename); // Load any extra PAR files specified on the command line bool LoadExtraRecoveryFiles(const std::vector &extrafiles); // Check for the existence of and verify each of the source files bool VerifySourceFiles(void); // Check any other files specified on the command line to see if they are // actually copies of the source files that have the wrong filename bool VerifyExtraFiles(const std::vector &extrafiles); // Attempt to match the data in the DiskFile with the source file bool VerifyDataFile(DiskFile *diskfile, Par1RepairerSourceFile *sourcefile); // Determine how many files are missing, damaged etc. void UpdateVerificationResults(void); // Check the verification results and report the details bool CheckVerificationResults(void); // Rename any damaged or missnamed target files. bool RenameTargetFiles(void); // Work out which files are being repaired, create them, and allocate // target DataBlocks to them, and remember them for later verification. bool CreateTargetFiles(void); // Work out which data blocks are available, which need to be recreated, // and compute the appropriate Reed Solomon matrix. bool ComputeRSmatrix(void); // Allocate memory buffers for reading and writing data to disk. bool AllocateBuffers(size_t memorylimit); // Read source data, process it through the RS matrix and write it to disk. bool ProcessData(u64 blockoffset, size_t blocklength); // Verify that all of the reconstructed target files are now correct bool VerifyTargetFiles(void); // Delete all of the partly reconstructed files bool DeleteIncompleteTargetFiles(void); // list the files needing verification bool RemoveBackupFiles(void); bool RemoveParFiles(void); protected: std::ostream &sout; // stream for output (for commandline, this is cout) std::ostream &serr; // stream for errors (for commandline, this is cerr) std::mutex output_lock; const NoiseLevel noiselevel; // How noisy we should be std::string searchpath; // Where to find files on disk DiskFileMap diskfilemap; // Map from filename to DiskFile std::map recoveryblocks; // The recovery data (mapped by exponent) unsigned char *filelist; u32 filelistsize; u64 blocksize; // The size of recovery and data blocks u64 chunksize; // How much of a block can be processed. std::vector sourcefiles; std::vector extrafiles; u32 completefilecount; u32 renamedfilecount; u32 damagedfilecount; u32 missingfilecount; std::list verifylist; std::vector backuplist; // Those source files backups std::list parlist; // list of par files std::vector inputblocks; // Which DataBlocks will be read from disk std::vector outputblocks; // Which DataBlocks have to calculated using RS ReedSolomon rs; // The Reed Solomon matrix. u64 progress; // How much data has been processed. u64 totaldata; // Total amount of data to be processed. size_t inputbuffersize; u8 *inputbuffer; // Buffer for reading DataBlocks (chunksize) size_t outputbufferalignment; size_t outputbuffersize; u8 *outputbuffer; // Buffer for writing DataBlocks (chunksize * missingblockcount) bool ignore16kfilehash; // The 16k file hash values may be invalid }; #endif // __PAR1REPAIRER_H__ par2cmdline-turbo-1.4.0/src/par1repairersourcefile.cpp000066400000000000000000000063101514221355600230220ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif Par1RepairerSourceFile::Par1RepairerSourceFile(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, PAR1FILEENTRY *fileentry, const std::string &searchpath) : hashfull(fileentry->hashfull) , hash16k(fileentry->hash16k) { targetexists = false; targetfile = 0; completefile = 0; filesize = fileentry->filesize; u32 namelen = (u32)((fileentry->entrysize - offsetof(PAR1FILEENTRY, name)) / 2); for (u32 index=0; indexname[index]; if (ch >= 256) { // Convert the Unicode character to two characters filename += ch & 255; filename += ch >> 8; } else { filename += ch & 255; } } // Translate any characters the OS does not like; filename = DescriptionPacket::TranslateFilenameFromPar2ToLocal(sout, serr, noiselevel, filename); // Strip the path from the filename std::string::size_type where; if (std::string::npos != (where = filename.find_last_of(PATHSEP)) || std::string::npos != (where = filename.find_last_of(ALTPATHSEP)) #ifdef _WIN32 || std::string::npos != (where = filename.find_last_of(':')) #endif ) { filename = filename.substr(where+1); } filename = searchpath + filename; } Par1RepairerSourceFile::~Par1RepairerSourceFile(void) { } void Par1RepairerSourceFile::SetTargetFile(DiskFile *diskfile) { targetfile = diskfile; } DiskFile* Par1RepairerSourceFile::GetTargetFile(void) const { return targetfile; } void Par1RepairerSourceFile::SetTargetExists(bool exists) { targetexists = exists; } bool Par1RepairerSourceFile::GetTargetExists(void) const { return targetexists; } void Par1RepairerSourceFile::SetCompleteFile(DiskFile *diskfile) { completefile = diskfile; sourceblock.SetLocation(diskfile, 0); sourceblock.SetLength(diskfile ? diskfile->FileSize() : 0); } DiskFile* Par1RepairerSourceFile::GetCompleteFile(void) const { return completefile; } void Par1RepairerSourceFile::SetTargetBlock(DiskFile *diskfile) { targetblock.SetLocation(diskfile, 0); targetblock.SetLength(diskfile->FileSize()); } par2cmdline-turbo-1.4.0/src/par1repairersourcefile.h000066400000000000000000000053371514221355600224770ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __PAR1REPAIRERSOURCEFILE_H__ #define __PAR1REPAIRERSOURCEFILE_H__ // The Par1RepairerSourceFile object is used during verification and repair // to record details about a particular source file and the data blocks // for that file. class Par1RepairerSourceFile { public: // Construct the object and set the description and verification packets Par1RepairerSourceFile(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, PAR1FILEENTRY *fileentry, const std::string &searchpath); ~Par1RepairerSourceFile(void); std::string FileName(void) const {return filename;} u64 FileSize(void) const {return filesize;} const MD5Hash& HashFull(void) const {return hashfull;} const MD5Hash& Hash16k(void) const {return hash16k;} // Set/Get which DiskFile will contain the final repaired version of the file void SetTargetFile(DiskFile *diskfile); DiskFile* GetTargetFile(void) const; // Set/Get whether or not the target file actually exists void SetTargetExists(bool exists); bool GetTargetExists(void) const; // Set/Get which DiskFile contains a full undamaged version of the source file void SetCompleteFile(DiskFile *diskfile); DiskFile* GetCompleteFile(void) const; void SetTargetBlock(DiskFile *diskfile); DataBlock* SourceBlock(void) {return &sourceblock;} DataBlock* TargetBlock(void) {return &targetblock;} protected: std::string filename; u64 filesize; MD5Hash hashfull; MD5Hash hash16k; DataBlock sourceblock; DataBlock targetblock; bool targetexists; // Whether the target file exists DiskFile *targetfile; // The final version of the file DiskFile *completefile; // A complete version of the file }; #endif // __PAR1REPAIRERSOURCEFILE_H__ par2cmdline-turbo-1.4.0/src/par2cmdline.cpp000066400000000000000000000101601514221355600205420ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2.h" #include "commandline.h" // This is included here, so that cout and cerr are not used elsewhere. #include #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif #ifdef _WIN32 #include "utf8.h" int wmain(int argc, wchar_t* wargv[]) #else int main(int argc, char* argv[]) #endif { #ifdef _MSC_VER // Memory leak checking _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_ALLOC_MEM_DF | /*_CRTDBG_CHECK_CRT_DF | */_CRTDBG_DELAY_FREE_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif #ifdef _WIN32 SetConsoleOutputCP(CP_UTF8); utf8::WideToUtf8ArgsAdapter wargsAdapter{ argc, wargv }; auto argv = wargsAdapter.GetUtf8Args(); #endif // check sizeof integers static_assert(sizeof(u8) == 1 && sizeof(i8) == 1 && sizeof(u16) == 2 && sizeof(i16) == 2 && sizeof(u32) == 4 && sizeof(i32) == 4 && sizeof(u64) == 8 && sizeof(i64) == 8, "Error: the assumed sizes of integers is wrong!"); // Parse the command line CommandLine *commandline = new CommandLine; Result result = eInvalidCommandLineArguments; if (commandline->Parse(argc, argv)) { // Which operation was selected switch (commandline->GetOperation()) { case CommandLine::opCreate: // Create recovery data result = par2create(std::cout, std::cerr, commandline->GetNoiseLevel(), commandline->GetMemoryLimit(), commandline->GetBasePath(), commandline->GetNumThreads(), commandline->GetFileThreads(), commandline->GetParFilename(), commandline->GetExtraFiles(), commandline->GetBlockSize(), commandline->GetFirstRecoveryBlock(), commandline->GetRecoveryFileScheme(), commandline->GetRecoveryFileCount(), commandline->GetRecoveryBlockCount() ); break; case CommandLine::opVerify: case CommandLine::opRepair: { // Verify or Repair damaged files switch (commandline->GetVersion()) { case CommandLine::verPar1: result = par1repair(std::cout, std::cerr, commandline->GetNoiseLevel(), commandline->GetMemoryLimit(), commandline->GetNumThreads(), commandline->GetParFilename(), commandline->GetExtraFiles(), commandline->GetOperation() == CommandLine::opRepair, commandline->GetPurgeFiles()); break; case CommandLine::verPar2: result = par2repair(std::cout, std::cerr, commandline->GetNoiseLevel(), commandline->GetMemoryLimit(), commandline->GetBasePath(), commandline->GetNumThreads(), commandline->GetFileThreads(), commandline->GetParFilename(), commandline->GetExtraFiles(), commandline->GetOperation() == CommandLine::opRepair, commandline->GetPurgeFiles(), commandline->GetRenameOnly(), commandline->GetSkipData(), commandline->GetSkipLeaway()); break; default: break; } } break; case CommandLine::opNone: result = eSuccess; break; default: break; } } delete commandline; return result; } par2cmdline-turbo-1.4.0/src/par2creator.cpp000066400000000000000000000712711514221355600206000ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #include "foreach_parallel.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif // static variable u32 Par2Creator::filethreads = _FILE_THREADS; Par2Creator::Par2Creator(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel) : sout(sout) , serr(serr) , noiselevel(noiselevel) , blocksize(0) , chunksize(0) , transferbuffer(0) , sourcefilecount(0) , sourceblockcount(0) , largestfilesize(0) , recoveryfilescheme(scUnknown) , recoveryfilecount(0) , recoveryblockcount(0) , firstrecoveryblock(0) , mainpacket(0) , creatorpacket(0) , sourcefiles() , sourceblocks() , recoveryfiles() , recoverypackets() , criticalpackets() , criticalpacketentries() , progress(0) , totaldata(0) , deferhashcomputation(false) , mttotalsize(0) { setup_hasher(); } Par2Creator::~Par2Creator(void) { delete mainpacket; delete creatorpacket; delete [] (u8*)transferbuffer; parpar.deinit(); std::vector::iterator sourcefile = sourcefiles.begin(); while (sourcefile != sourcefiles.end()) { delete *sourcefile; ++sourcefile; } } Result Par2Creator::Process( const size_t memorylimit, const std::string &basepath, const u32 nthreads, const u32 _filethreads, const std::string &parfilename, const std::vector &_extrafiles, const u64 _blocksize, const u32 _firstblock, const Scheme _recoveryfilescheme, const u32 _recoveryfilecount, const u32 _recoveryblockcount) { filethreads = _filethreads; if (!CheckBasepath(parfilename)) return eFileIOError; // Get information from commandline blocksize = _blocksize; const std::vector extrafiles = _extrafiles; sourcefilecount = (u32)extrafiles.size(); recoveryblockcount = _recoveryblockcount; recoveryfilecount = _recoveryfilecount; firstrecoveryblock = _firstblock; recoveryfilescheme = _recoveryfilescheme; // Compute block size from block count or vice versa depending on which was // specified on the command line if (!ComputeBlockCount(extrafiles)) return eInvalidCommandLineArguments; // Determine how many recovery files to create. if (!ComputeRecoveryFileCount(sout, serr, &recoveryfilecount, recoveryfilescheme, recoveryblockcount, largestfilesize, blocksize)) { return eInvalidCommandLineArguments; } // Determine how much recovery data can be computed on one pass if (!CalculateProcessBlockSize(memorylimit)) return eLogicError; if (recoveryblockcount > 0 && noiselevel >= nlDebug) sout << "[DEBUG] Process chunk size: " << chunksize << std::endl; // Init ParPar backend if (!parpar.init(chunksize, {{&parparcpu, 0, (size_t)chunksize}})) return eLogicError; if (nthreads != 0) parparcpu.setNumThreads(nthreads); // If there aren't many input blocks, restrict the submission batch size u32 inputbatch = 0; if (sourceblockcount < NUM_PARPAR_BUFFERS*2) inputbatch = (sourceblockcount + 1) / 2; if (!parparcpu.init(GF16_AUTO, inputbatch)) return eMemoryError; if (noiselevel > nlQuiet) { // Display information. sout << "Block size: " << blocksize << std::endl; sout << "Source file count: " << sourcefilecount << std::endl; sout << "Source block count: " << sourceblockcount << std::endl; sout << "Recovery block count: " << recoveryblockcount << std::endl; sout << "Recovery file count: " << recoveryfilecount << std::endl; if (noiselevel >= nlNoisy) { sout << "Data hash method: " << hasherInput_methodName() << std::endl; sout << "Multiply method: " << parparcpu.getMethodName() << std::endl; if (noiselevel >= nlDebug) { sout << "[DEBUG] Compute tile size: " << parparcpu.getChunkLen() << std::endl; sout << "[DEBUG] Compute block grouping: " << parparcpu.getInputBatchSize() << std::endl; } } sout << std::endl; } // Open all of the source files, compute the Hashes and CRC values, and store // the results in the file verification and file description packets. if (!OpenSourceFiles(extrafiles, basepath)) return eFileIOError; // Create the main packet and determine the setid to use with all packets if (!CreateMainPacket()) return eLogicError; // Create the creator packet. if (!CreateCreatorPacket()) return eLogicError; // Initialise all of the source blocks ready to start reading data from the source files. if (!CreateSourceBlocks()) return eLogicError; // Create all of the output files and allocate all packets to appropriate file offsets. if (!InitialiseOutputFiles(parfilename)) return eFileIOError; if (recoveryblockcount > 0) { // Allocate memory buffers for reading and writing data to disk. if (!AllocateBuffers()) return eMemoryError; // Set output exponents std::vector recoveryindices(recoveryblockcount); for (u16 i = 0; i < recoveryblockcount; i++) recoveryindices[i] = i + firstrecoveryblock; if (!parpar.setRecoverySlices(recoveryindices)) return eMemoryError; // Set the total amount of data to be processed. progress = 0; totaldata = blocksize * sourceblockcount; // Start at an offset of 0 within a block. u64 blockoffset = 0; while (blockoffset < blocksize) // Continue until the end of the block. { // Work out how much data to process this time. size_t blocklength = (size_t)std::min((u64)chunksize, blocksize-blockoffset); if (!parpar.setCurrentSliceSize(blocklength)) return eMemoryError; // Read source data, process it through the RS matrix and write it to disk. if (!ProcessData(blockoffset, blocklength)) return eFileIOError; blockoffset += blocklength; } if (noiselevel > nlQuiet) sout << "Writing recovery packets" << std::endl; // Finish computation of the recovery packets and write the headers to disk. if (!WriteRecoveryPacketHeaders()) return eFileIOError; // Finish computing the full file hash values of the source files if (!FinishFileHashComputation()) return eLogicError; } // Fill in all remaining details in the critical packets. if (!FinishCriticalPackets()) return eLogicError; if (noiselevel > nlQuiet) sout << "Writing verification packets" << std::endl; // Write all other critical packets to disk. if (!WriteCriticalPackets()) return eFileIOError; // Close all files. if (!CloseFiles()) return eFileIOError; if (noiselevel > nlSilent) sout << "Done" << std::endl; return eSuccess; } // Check basepath permission bool Par2Creator::CheckBasepath(const std::string &parfilename) { std::string checkfilename = parfilename + ".check.par2"; std::unique_ptr diskfile(new DiskFile(sout, serr, output_lock)); size_t dummysize = 4096; if (!diskfile->Create(checkfilename, dummysize)) return false; diskfile->Close(); if (!diskfile->Delete()) return false; return true; } // Compute block size from block count or vice versa depending on which was // specified on the command line bool Par2Creator::ComputeBlockCount(const std::vector &extrafiles) { FileSizeCache filesize_cache; largestfilesize = 0; for (std::vector::const_iterator i=extrafiles.begin(); i!=extrafiles.end(); i++) { u64 filesize = filesize_cache.get(*i); if (largestfilesize < filesize) { largestfilesize = filesize; } } if (blocksize == 0) { serr << "ERROR: Block size was zero!" << std::endl; return false; } if (blocksize % 4 != 0) { serr << "ERROR: Block size was not a multiple of 4 bytes!" << std::endl; return false; } u64 count = 0; for (std::vector::const_iterator i=extrafiles.begin(); i!=extrafiles.end(); i++) { count += (filesize_cache.get(*i) + blocksize-1) / blocksize; } if (count > 32768) { serr << "Block size is too small. It would require " << count << "blocks." << std::endl; return false; } sourceblockcount = (u32)count; return true; } // Determine how much recovery data can be computed on one pass bool Par2Creator::CalculateProcessBlockSize(size_t memorylimit) { // Are we computing any recovery blocks if (recoveryblockcount == 0) { chunksize = 0; deferhashcomputation = false; } else { // We use intermediary buffers to transfer data with, so include those in the limit calculation u32 blockoverhead = NUM_TRANSFER_BUFFERS + std::min((u32)NUM_PARPAR_BUFFERS*2, sourceblockcount+1); // Would single pass processing use too much memory if (blocksize * (recoveryblockcount + blockoverhead) > memorylimit) { // Pick a size that is small enough chunksize = ~3 & (memorylimit / (recoveryblockcount + blockoverhead)); deferhashcomputation = false; } else { chunksize = (size_t)blocksize; deferhashcomputation = true; } if (MAX_CHUNK_SIZE != 0 && chunksize > MAX_CHUNK_SIZE) { chunksize = MAX_CHUNK_SIZE; deferhashcomputation = false; } } return true; } // Open all of the source files, compute the Hashes and CRC values, and store // the results in the file verification and file description packets. bool Par2Creator::OpenSourceFiles(const std::vector &extrafiles, std::string basepath) { std::atomic openfailed(false); std::atomic totalprogress(0); //Total size of files for mt-progress line for (size_t i=0; i(extrafiles, Par2Creator::GetFileThreads(), [&, this](const std::string& extrafile) { if (openfailed.load(std::memory_order_relaxed)) return; Par2CreatorSourceFile *sourcefile = new Par2CreatorSourceFile; std::string name; DiskFile::SplitRelativeFilename(extrafile, basepath, name); if (noiselevel > nlSilent) { std::lock_guard lock(output_lock); sout << "Opening: " << name << std::endl; } // Open the source file and compute its Hashes and CRCs. if (!sourcefile->Open(noiselevel, sout, serr, extrafile, blocksize, deferhashcomputation, basepath, mttotalsize, totalprogress, output_lock)) { delete sourcefile; openfailed.store(true, std::memory_order_relaxed); return; } // Record the file verification and file description packets // in the critical packet list. { std::lock_guard lock(packet_lock); sourcefile->RecordCriticalPackets(criticalpackets); // Add the source file to the sourcefiles array. sourcefiles.push_back(sourcefile); } // Close the source file until its needed sourcefile->Close(); }); if (openfailed.load(std::memory_order_relaxed)) return false; return true; } // Create the main packet and determine the setid to use with all packets bool Par2Creator::CreateMainPacket(void) { // Construct the main packet from the list of source files and the block size. mainpacket = new MainPacket; // Add the main packet to the list of critical packets. criticalpackets.push_back(mainpacket); // Create the packet (sourcefiles will get sorted into FileId order). return mainpacket->Create(sourcefiles, blocksize); } // Create the creator packet. bool Par2Creator::CreateCreatorPacket(void) { // Construct the creator packet creatorpacket = new CreatorPacket; // Create the packet return creatorpacket->Create(mainpacket->SetId()); } // Initialise all of the source blocks ready to start reading data from the source files. bool Par2Creator::CreateSourceBlocks(void) { // Allocate the array of source blocks sourceblocks.resize(sourceblockcount); std::vector::iterator sourceblock = sourceblocks.begin(); for (std::vector::iterator sourcefile = sourcefiles.begin(); sourcefile!= sourcefiles.end(); sourcefile++) { // Allocate the appropriate number of source blocks to each source file. // sourceblock will be advanced. (*sourcefile)->InitialiseSourceBlocks(sourceblock, blocksize); } return true; } class FileAllocation { public: FileAllocation(void) : filename("") { exponent = 0; count = 0; } std::string filename; u32 exponent; u32 count; }; // Create all of the output files and allocate all packets to appropriate file offsets. bool Par2Creator::InitialiseOutputFiles(const std::string &parfilename) { // Allocate the recovery packets recoverypackets.resize(recoveryblockcount); // Choose filenames and decide which recovery blocks to place in each file std::vector fileallocations; fileallocations.resize(recoveryfilecount+1); // One extra file with no recovery blocks { // Decide how many recovery blocks to place in each file u32 exponent = firstrecoveryblock; if (recoveryfilecount > 0) { switch (recoveryfilescheme) { case scUnknown: { assert(false); return false; } break; case scUniform: { // Files will have roughly the same number of recovery blocks each. u32 base = recoveryblockcount / recoveryfilecount; u32 remainder = recoveryblockcount % recoveryfilecount; for (u32 filenumber=0; filenumber= 2*largest && filenumber > 0) { filenumber--; exponent -= largest; blocks -= largest; fileallocations[filenumber].exponent = exponent; fileallocations[filenumber].count = largest; } assert(blocks > 0 && filenumber > 0); exponent = firstrecoveryblock; u32 count = 1; u32 files = filenumber; // Allocate exponentially at the bottom for (filenumber=0; filenumber=10; t/=10) { digitsLow++; } for (u32 t=limitCount; t>=10; t/=10) { digitsCount++; } } // Set the filenames for (u32 filenumber=0; filenumber _MAX_PATH) { serr << filename.str() << " pathlength is more than " << _MAX_PATH << "." << std::endl; return false; } fileallocations[filenumber].filename = filename.str(); } std::string mainpar = parfilename + ".par2"; if (mainpar.length() > _MAX_PATH) { serr << mainpar << " pathlength is more than " << _MAX_PATH << "." << std::endl; return false; } fileallocations[recoveryfilecount].filename = mainpar; } // Allocate the recovery files { recoveryfiles.resize(recoveryfilecount+1, DiskFile(sout, serr, output_lock)); // pass default constructor. // Sort critical packets, so we get consistency. criticalpackets.sort(CriticalPacket::CompareLess); // Allocate packets to the output files { const MD5Hash &setid = mainpacket->SetId(); std::vector::iterator recoverypacket = recoverypackets.begin(); std::vector::iterator recoveryfile = recoveryfiles.begin(); std::vector::iterator fileallocation = fileallocations.begin(); // For each recovery file: while (recoveryfile != recoveryfiles.end()) { // How many recovery blocks in this file u32 count = fileallocation->count; // start at the beginning of the recovery file u64 offset = 0; if (count == 0) { // Write one set of critical packets std::list::const_iterator nextCriticalPacket = criticalpackets.begin(); while (nextCriticalPacket != criticalpackets.end()) { criticalpacketentries.push_back(CriticalPacketEntry(&*recoveryfile, offset, *nextCriticalPacket)); offset += (*nextCriticalPacket)->PacketLength(); ++nextCriticalPacket; } } else { // How many copies of each critical packet u32 copies = 0; for (u32 t=count; t>0; t>>=1) { copies++; } // Get ready to iterate through the critical packets u64 packetCount = 0; std::list::const_iterator nextCriticalPacket = criticalpackets.end(); // What is the first exponent u32 exponent = fileallocation->exponent; // Start allocating the recovery packets u32 limit = exponent + count; while (exponent < limit) { // Add the next recovery packet recoverypacket->Create(&*recoveryfile, offset, blocksize, exponent, setid); offset += recoverypacket->PacketLength(); ++recoverypacket; ++exponent; // Add some critical packets packetCount += copies * criticalpackets.size(); while (packetCount >= count) { if (nextCriticalPacket == criticalpackets.end()) nextCriticalPacket = criticalpackets.begin(); criticalpacketentries.push_back(CriticalPacketEntry(&*recoveryfile, offset, *nextCriticalPacket)); offset += (*nextCriticalPacket)->PacketLength(); ++nextCriticalPacket; packetCount -= count; } } } // Add one copy of the creator packet criticalpacketentries.push_back(CriticalPacketEntry(&*recoveryfile, offset, creatorpacket)); offset += creatorpacket->PacketLength(); // Create the file on disk and make it the required size if (!recoveryfile->Create(fileallocation->filename, offset)) return false; ++recoveryfile; ++fileallocation; } } } return true; } // Allocate memory buffers for reading and writing data to disk. bool Par2Creator::AllocateBuffers(void) { transferbuffer = new u8[chunksize * NUM_TRANSFER_BUFFERS]; if (transferbuffer == NULL) { serr << "Could not allocate buffer memory." << std::endl; return false; } return true; } // Read source data, process it through the RS matrix and write it to disk. bool Par2Creator::ProcessData(u64 blockoffset, size_t blocklength) { // If we have deferred computation of the file hash and block crc and hashes // sourcefile and sourceindex will be used to update them during // the main recovery block computation std::vector::iterator sourcefile = sourcefiles.begin(); u32 sourceindex = 0; std::vector::iterator sourceblock; u32 inputblock; DiskFile *lastopenfile = NULL; // For tracking input buffer availability std::future bufferavail[NUM_TRANSFER_BUFFERS]; u32 bufferindex = NUM_TRANSFER_BUFFERS - 1; // Set all input buffers to available for (i32 i = 0; i < NUM_TRANSFER_BUFFERS; i++) { std::promise stub; bufferavail[i] = stub.get_future(); stub.set_value(); } // Clear existing output data in backend parpar.discardOutput(); // For each input block for ((sourceblock=sourceblocks.begin()),(inputblock=0); sourceblock != sourceblocks.end(); ++sourceblock, ++inputblock) { // Are we reading from a new file? if (lastopenfile != (*sourceblock).GetDiskFile()) { // Close the last file if (lastopenfile != NULL) { lastopenfile->Close(); } // Open the new file lastopenfile = (*sourceblock).GetDiskFile(); if (!lastopenfile->Open()) { return false; } } // Wait for next input buffer to become available bufferindex = (bufferindex + 1) % NUM_TRANSFER_BUFFERS; void *inputbuffer = (char*)transferbuffer + chunksize * bufferindex; bufferavail[bufferindex].get(); // Read data from the current input block if (!sourceblock->ReadData(blockoffset, blocklength, inputbuffer)) return false; // Wait for ParPar backend to be ready, if busy parpar.waitForAdd(); // Send block to backend bufferavail[bufferindex] = parpar.addInput(inputbuffer, blocklength, inputblock); if (deferhashcomputation) { assert(blockoffset == 0 && blocklength == blocksize); assert(sourcefile != sourcefiles.end()); (*sourcefile)->UpdateHashes(sourceindex, inputbuffer, blocklength); } if (noiselevel > nlQuiet) { // Update a progress indicator u32 oldfraction = (u32)(1000 * progress / totaldata); progress += blocklength; u32 newfraction = (u32)(1000 * progress / totaldata); if (oldfraction != newfraction) { sout << "Processing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << std::flush; } } // Work out which source file the next block belongs to if (++sourceindex >= (*sourcefile)->BlockCount()) { sourceindex = 0; ++sourcefile; } } // Flush backend parpar.endInput().get(); // Close the last file if (lastopenfile != NULL) { lastopenfile->Close(); } if (noiselevel > nlQuiet) sout << "Writing recovery packets\r"; if (recoveryblockcount > 0) { // For output, we only need two transfer buffers std::future outbufavail[2]; // Prepare first output outbufavail[0] = parpar.getOutput(0, transferbuffer); // For each output block for (u32 outputblock=0; outputblock nlQuiet) sout << "Wrote " << recoveryblockcount * blocklength << " bytes to disk" << std::endl; return true; } // Finish computation of the recovery packets and write the headers to disk. bool Par2Creator::WriteRecoveryPacketHeaders(void) { // For each recovery packet for (std::vector::iterator recoverypacket = recoverypackets.begin(); recoverypacket != recoverypackets.end(); ++recoverypacket) { // Finish the packet header and write it to disk if (!recoverypacket->WriteHeader()) return false; } return true; } bool Par2Creator::FinishFileHashComputation(void) { // If we deferred the computation of the full file hash, then we finish it now if (deferhashcomputation) { // For each source file std::vector::iterator sourcefile = sourcefiles.begin(); while (sourcefile != sourcefiles.end()) { (*sourcefile)->FinishHashes(); ++sourcefile; } } return true; } // Fill in all remaining details in the critical packets. bool Par2Creator::FinishCriticalPackets(void) { // Get the setid from the main packet const MD5Hash &setid = mainpacket->SetId(); for (std::list::iterator criticalpacket=criticalpackets.begin(); criticalpacket!=criticalpackets.end(); criticalpacket++) { // Store the setid in each of the critical packets // and compute the packet_hash of each one. (*criticalpacket)->FinishPacket(setid); } return true; } // Write all other critical packets to disk. bool Par2Creator::WriteCriticalPackets(void) { std::list::const_iterator packetentry = criticalpacketentries.begin(); // For each critical packet while (packetentry != criticalpacketentries.end()) { // Write it to disk if (!packetentry->WritePacket()) return false; ++packetentry; } return true; } // Close all files. bool Par2Creator::CloseFiles(void) { // // Close each source file. // for (std::vector::iterator sourcefile = sourcefiles.begin(); // sourcefile != sourcefiles.end(); // ++sourcefile) // { // (*sourcefile)->Close(); // } // Close each recovery file. for (std::vector::iterator recoveryfile = recoveryfiles.begin(); recoveryfile != recoveryfiles.end(); ++recoveryfile) { recoveryfile->Close(); } return true; } par2cmdline-turbo-1.4.0/src/par2creator.h000066400000000000000000000146301514221355600202410ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __PAR2CREATOR_H__ #define __PAR2CREATOR_H__ #include "../parpar/gf16/controller_cpu.h" class MainPacket; class CreatorPacket; class CriticalPacket; class Par2Creator { public: Par2Creator(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel); ~Par2Creator(void); // Create recovery files from the source files specified on the command line Result Process(const size_t memorylimit, const std::string &basepath, const u32 nthreads, const u32 filethreads, const std::string &parfilename, const std::vector &extrafiles, const u64 blocksize, const u32 firstblock, const Scheme recoveryfilescheme, const u32 recoveryfilecount, const u32 recoveryblockcount ); protected: // Steps in the creation process: // Check permissions in the basepath bool CheckBasepath(const std::string &parfilename); // Compute block size from block count or vice versa depending on which was // specified on the command line bool ComputeBlockCount(const std::vector &extrafiles); // Determine how much recovery data can be computed on one pass bool CalculateProcessBlockSize(size_t memorylimit); // Open all of the source files, compute the Hashes and CRC values, and store // the results in the file verification and file description packets. bool OpenSourceFiles(const std::vector &extrafiles, std::string basepath); // Create the main packet and determine the set_id_hash to use with all packets bool CreateMainPacket(void); // Create the creator packet. bool CreateCreatorPacket(void); // Initialise all of the source blocks ready to start reading data from the source files. bool CreateSourceBlocks(void); // Create all of the output files and allocate all packets to appropriate file offsets. bool InitialiseOutputFiles(const std::string &par2filename); // Allocate memory buffers for reading and writing data to disk. bool AllocateBuffers(void); // Read source data, process it through the RS matrix and write it to disk. bool ProcessData(u64 blockoffset, size_t blocklength); // Finish computation of the recovery packets and write the headers to disk. bool WriteRecoveryPacketHeaders(void); // Finish computing the full file hash values of the source files bool FinishFileHashComputation(void); // Fill in all remaining details in the critical packets. bool FinishCriticalPackets(void); // Write all other critical packets to disk. bool WriteCriticalPackets(void); // Close all files. bool CloseFiles(void); static u32 GetFileThreads(void) {return filethreads;} protected: std::ostream &sout; // stream for output (for commandline, this is cout) std::ostream &serr; // stream for errors (for commandline, this is cerr) std::mutex output_lock; const NoiseLevel noiselevel; // How noisy we should be static u32 filethreads; // Number of threads for file processing u64 blocksize; // The size of each block. size_t chunksize; // How much of each block will be processed at a // time (due to memory constraints). void *transferbuffer; // chunksize * num_transfer_buffers u32 sourcefilecount; // Number of source files for which recovery data will be computed. u32 sourceblockcount; // Total number of data blocks that the source files will be // virtually sliced into. u64 largestfilesize; // The size of the largest source file Scheme recoveryfilescheme; // What scheme will be used to select the // sizes for the recovery files. u32 recoveryfilecount; // The number of recovery files that will be created u32 recoveryblockcount; // The number of recovery blocks that will be placed // in the recovery files. u32 firstrecoveryblock; // The lowest exponent value to use for the recovery blocks. MainPacket *mainpacket; // The main packet CreatorPacket *creatorpacket; // The creator packet std::vector sourcefiles; // Array containing details of the source files // as well as the file verification and file // description packets for them. std::vector sourceblocks; // Array with one entry for every source block. std::vector recoveryfiles; // Array with one entry for every recovery file. std::vector recoverypackets; // Array with one entry for every recovery packet. std::list criticalpackets; // A list of all of the critical packets. std::list criticalpacketentries; // A list of which critical packet will // be written to which recovery file. PAR2Proc parpar; // Main ParPar backend PAR2ProcCPU parparcpu; // ParPar CPU sub-backend u64 progress; // How much data has been processed. u64 totaldata; // Total amount of data to be processed. bool deferhashcomputation; // If we have enough memory to compute all recovery data // in one pass, then we can defer the computation of // the full file hash and block crc and hashes until // the recovery data is computed. u64 mttotalsize; // Total size of files for mt-progress line }; #endif // __PAR2CREATOR_H__ par2cmdline-turbo-1.4.0/src/par2creatorsourcefile.cpp000066400000000000000000000233151514221355600226550ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #include "hasher.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif Par2CreatorSourceFile::Par2CreatorSourceFile(void) { descriptionpacket = 0; verificationpacket = 0; diskfile = 0; filesize = 0; //diskfilename; //parfilename; blockcount = 0; hasher = HasherInput_Create(); } Par2CreatorSourceFile::~Par2CreatorSourceFile(void) { delete descriptionpacket; delete verificationpacket; delete diskfile; hasher->destroy(); } // Open the source file, compute the MD5 Hash of the whole file and the first // 16k of the file, and then compute the FileId and store the results // in a file description packet and a file verification packet. bool Par2CreatorSourceFile::Open(NoiseLevel noiselevel, std::ostream &sout, std::ostream &serr, const std::string &extrafile, u64 blocksize, bool deferhashcomputation, std::string basepath, u64 totalsize, std::atomic &totalprogress, std::mutex &output_lock) { // Get the filename and filesize diskfilename = extrafile; filesize = DiskFile::GetFileSize(extrafile); // Work out how many blocks the file will be sliced into blockcount = (u32)((filesize + blocksize-1) / blocksize); // Determine what filename to record in the PAR2 files parfilename = diskfilename; parfilename.erase(0, basepath.length()); parfilename = DescriptionPacket::TranslateFilenameFromLocalToPar2(sout, serr, noiselevel, parfilename); // Create the Description and Verification packets descriptionpacket = new DescriptionPacket; descriptionpacket->Create(parfilename, filesize); verificationpacket = new VerificationPacket; verificationpacket->Create(blockcount); // Create the diskfile object diskfile = new DiskFile(sout, serr, output_lock); // Open the source file if (!diskfile->Open(diskfilename, filesize)) return false; // Do we want to defer the computation of the full file hash, and // the block crc and hashes. This is only permitted if there // is sufficient memory available to create all recovery blocks // in one pass of the source files (i.e. chunksize == blocksize) if (deferhashcomputation) { // Initialise a buffer to read the first 16k of the source file size_t buffersize = 16 * 1024; if (buffersize > filesize) buffersize = (size_t)filesize; char *buffer = new char[buffersize]; // Read the data from the file if (!diskfile->Read(0, buffer, buffersize)) { diskfile->Close(); delete [] buffer; return false; } // Compute the hash of the data read from the file MD5Context context; context.Update(buffer, buffersize); delete [] buffer; MD5Hash hash; context.Final(hash); // Store the hash in the descriptionpacket and compute the file id descriptionpacket->Hash16k(hash); // Compute the fileid and store it in the verification packet. descriptionpacket->ComputeFileId(); verificationpacket->FileId(descriptionpacket->FileId()); } else { // Initialise a buffer to read the source file size_t buffersize = 1024*1024; if (buffersize > std::min(blocksize,filesize)) buffersize = (size_t)std::min(blocksize,filesize); char *buffer = new char[buffersize]; // Get ready to start reading source file to compute the hashes and crcs u64 offset = 0; u32 blocknumber = 0; u64 need = blocksize; MD5Context hash16kcontext; // Whilst we have not reached the end of the file while (offset < filesize) { // Work out how much we can read size_t want = (size_t)std::min(filesize-offset, (u64)buffersize); // Read some data from the file into the buffer if (!diskfile->Read(offset, buffer, want)) { diskfile->Close(); delete [] buffer; return false; } // Whilst we haven't passed the 16k boundary, compute the 16k hash if (offset < 16384) { hash16kcontext.Update(buffer, (size_t)std::min(want, (size_t)(16384-offset))); // If the new data passes the 16k boundary, compute the 16k hash for the file if (offset + want >= 16384) { MD5Hash hash; hash16kcontext.Final(hash); // Store the 16k hash in the file description packet descriptionpacket->Hash16k(hash); } } // Get ready to update block hashes and crcs u32 used = 0; // Whilst we have not used all of the data we just read while (used < want) { // How much of it can we use for the current block u32 use = (u32)std::min(need, (u64)(want-used)); hasher->update(&buffer[used], use); used += use; need -= use; // Have we finished the current block if (need == 0) { MD5Hash blockhash; u32 blockcrc = HasherGetBlock(hasher, blockhash); // Store the block hash and block crc in the file verification packet. verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc); blocknumber++; // More blocks if (blocknumber < blockcount) { need = blocksize; } } } if (noiselevel > nlQuiet) { // Display progress u64 progress = totalprogress.fetch_add(want, std::memory_order_relaxed); u32 oldfraction = (u32)(1000 * progress / totalsize); u32 newfraction = (u32)(1000 * (progress + want) / totalsize); if (oldfraction != newfraction) { std::lock_guard lock(output_lock); sout << newfraction/10 << '.' << newfraction%10 << "%\r" << std::flush; } } offset += want; } // Did we finish the last block if (need > 0) { MD5Hash blockhash; u32 blockcrc = HasherGetBlock(hasher, blockhash, need); // Store the block hash and block crc in the file verification packet. verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc); blocknumber++; } // Finish computing the file hash. MD5Hash filehash; hasher->end(filehash.hash); // Store the file hash in the file description packet. descriptionpacket->HashFull(filehash); // Did we compute the 16k hash. if (offset < 16384) { // Store the 16k hash in the file description packet. descriptionpacket->Hash16k(filehash); } delete [] buffer; // Compute the fileid and store it in the verification packet. descriptionpacket->ComputeFileId(); verificationpacket->FileId(descriptionpacket->FileId()); } return true; } void Par2CreatorSourceFile::Close(void) { diskfile->Close(); } void Par2CreatorSourceFile::RecordCriticalPackets(std::list &criticalpackets) { // Add the file description packet and file verification packet to // the critical packet list. criticalpackets.push_back(descriptionpacket); criticalpackets.push_back(verificationpacket); } bool Par2CreatorSourceFile::CompareLess(const Par2CreatorSourceFile* const &left, const Par2CreatorSourceFile* const &right) { // Sort source files based on fileid return left->descriptionpacket->FileId() < right->descriptionpacket->FileId(); } const MD5Hash& Par2CreatorSourceFile::FileId(void) const { // Get the file id hash return descriptionpacket->FileId(); } void Par2CreatorSourceFile::InitialiseSourceBlocks(std::vector::iterator &sourceblock, u64 blocksize) { for (u32 blocknum=0; blocknumSetLocation(diskfile, // file blocknum * blocksize); // offset sourceblock->SetLength(std::min(blocksize, filesize - (u64)blocknum * blocksize)); // length sourceblock++; } } void Par2CreatorSourceFile::UpdateHashes(u32 blocknumber, const void *buffer, size_t length) { // Requires: deferhashcomputation must've been true // Update the hashes, but don't go beyond the end of the file const u64 len = filesize - (u64) blocknumber * (u64) length; size_t zeropad = 0; if ((u64)length > len) { zeropad = length - len; length = (size_t)(len); } // Compute the crc and hash of the data hasher->update(buffer, length); MD5Hash blockhash; u32 blockcrc = HasherGetBlock(hasher, blockhash, zeropad); // Store the results in the verification packet verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc); } void Par2CreatorSourceFile::FinishHashes(void) { // Requires: deferhashcomputation must've been true // Finish computation of the full file hash MD5Hash hash; hasher->end(hash.hash); // Store it in the description packet descriptionpacket->HashFull(hash); } par2cmdline-turbo-1.4.0/src/par2creatorsourcefile.h000066400000000000000000000064631514221355600223270ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __PAR2CREATORSOURCEFILE_H__ #define __PAR2CREATORSOURCEFILE_H__ #include #include class DescriptionPacket; class VerificationPacket; class DiskFile; // The Par2CreatorSourceFile contains the file verification and file description // packet for one source file. class Par2CreatorSourceFile { private: // Don't permit copying or assignment Par2CreatorSourceFile(const Par2CreatorSourceFile &other); Par2CreatorSourceFile& operator=(const Par2CreatorSourceFile &other); public: Par2CreatorSourceFile(void); ~Par2CreatorSourceFile(void); // Open the source file and compute the Hashes and CRCs. bool Open(NoiseLevel noiselevel, std::ostream &sout, std::ostream &serr, const std::string &extrafile, u64 blocksize, bool deferhashcomputation, std::string basepath, u64 totalsize, std::atomic &totalprogress, std::mutex &output_lock); void Close(void); // Recover the file description and file verification packets // in the critical packet list. void RecordCriticalPackets(std::list &criticalpackets); // Get the file id const MD5Hash& FileId(void) const; // Sort source files based on the file id hash static bool CompareLess(const Par2CreatorSourceFile* const &left, const Par2CreatorSourceFile* const &right); // Allocate the appropriate number of source blocks to the source file void InitialiseSourceBlocks(std::vector::iterator &sourceblock, u64 blocksize); // Update the file hash and the block crc and hashes void UpdateHashes(u32 blocknumber, const void *buffer, size_t length); // Finish computation of the file hash void FinishHashes(void); // How many blocks does this source file use u32 BlockCount(void) const {return blockcount;} protected: DescriptionPacket *descriptionpacket; // The file description packet. VerificationPacket *verificationpacket; // The file verification packet. DiskFile *diskfile; // The source file u64 filesize; // The size of the source file. std::string diskfilename; // The filename of the source file on disk. std::string parfilename; // The filename that will be recorded in the file description packet. u32 blockcount; // How many blocks the file will be divided into. IHasherInput* hasher; // hasher context used to calculate block and file hashes }; #endif // __PAR2CREATORSOURCEFILE_H__ par2cmdline-turbo-1.4.0/src/par2fileformat.cpp000066400000000000000000000032221514221355600212600ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" MAGIC packet_magic = {{'P', 'A', 'R', '2', '\0','P', 'K', 'T'}}; PACKETTYPE fileverificationpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'I', 'F', 'S', 'C', '\0','\0','\0','\0'}}; PACKETTYPE filedescriptionpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'F', 'i', 'l', 'e', 'D', 'e', 's', 'c' }}; PACKETTYPE mainpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'M', 'a', 'i', 'n', '\0','\0','\0','\0'}}; PACKETTYPE recoveryblockpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'R', 'e', 'c', 'v', 'S', 'l', 'i', 'c' }}; PACKETTYPE creatorpacket_type = {{'P', 'A', 'R', ' ', '2', '.', '0', '\0', 'C', 'r', 'e', 'a', 't', 'o', 'r', '\0'}}; par2cmdline-turbo-1.4.0/src/par2fileformat.h000066400000000000000000000157451514221355600207420ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __PAR2FILEFORMAT_H__ #define __PAR2FILEFORMAT_H__ // This file defines the format of a PAR2 file. // PAR2 files consist of one or more "packets" that contain information // that is required to be able to verify and repair damaged data files. // All packets start with a short "header" which contains information // used to describe what sort of data is stored in the rest of the packet // and also to allow that data to be verified. // This file details the format for the following packet types described // in the PAR 2.0 specification: // Main Packet struct MAINPACKET // File Description Packet struct FILEDESCRIPTIONPACKET // Input File Slice Checksum Packet struct FILEVERIFICATIONPACKET // Recovery Slice Packet struct RECOVERYBLOCKPACKET // Creator Packet struct CREATORPACKET #ifdef _WIN32 #pragma pack(push, 1) #define PACKED #else #define PACKED __attribute__ ((packed)) #endif #ifdef _MSC_VER #pragma warning(disable:4200) #endif // All numeric fields in the file format are in LITTLE ENDIAN format. // The types leu32 and leu64 are defined in letype.h // Two simple types used in the packet header. struct MAGIC {u8 magic[8];} PACKED; struct PACKETTYPE {u8 type[16];} PACKED; // Every packet starts with a packet header. struct PACKET_HEADER { // Header MAGIC magic; // = {'P', 'A', 'R', '2', '\0', 'P', 'K', 'T'} leu64 length; // Length of entire packet including header MD5Hash hash; // Hash of entire packet excepting the first 3 fields MD5Hash setid; // Normally computed as the Hash of body of "Main Packet" PACKETTYPE type; // Used to specify the meaning of the rest of the packet } PACKED; // The file verification packet is used to determine whether or not any // parts of a damaged file are useable. // It contains a FileId used to pair it with a corresponding file description // packet, followed by an array of hash and crc values. The number of entries in // the array can be determined from the packet_length. struct FILEVERIFICATIONENTRY { MD5Hash hash; leu32 crc; } PACKED; struct FILEVERIFICATIONPACKET { PACKET_HEADER header; // Body MD5Hash fileid; // MD5hash of file_hash_16k, file_length, file_name FILEVERIFICATIONENTRY entries[]; } PACKED; // The file description packet is used to record the name of the file, // its size, and the Hash of both the whole file and the first 16k of // the file. // If the name of the file is an exact multiple of 4 characters in length // then it may not have a NULL termination. If the name of the file is not // an exact multiple of 4, then it will be padded with 0 bytes at the // end to make it up to a multiple of 4. struct FILEDESCRIPTIONPACKET { PACKET_HEADER header; // Body MD5Hash fileid; // MD5hash of [hash16k, length, name] MD5Hash hashfull; // MD5 Hash of the whole file MD5Hash hash16k; // MD5 Hash of the first 16k of the file leu64 length; // Length of the file u8 name[]; // Name of the file, padded with 1 to 3 zero bytes to reach // a multiple of 4 bytes. // Actual length can be determined from overall packet // length and then working backwards to find the first non // zero character. //u8* name(void) {return (u8*)&this[1];} //const u8* name(void) const {return (const u8*)&this[1];} } PACKED; // The main packet is used to tie together the other packets in a recovery file. // It specifies the block size used to virtually slice the source files, a count // of the number of source files, and an array of Hash values used to specify // in what order the source files are processed. // Each entry in the fileid array corresponds with the fileid value // in a file description packet and a file verification packet. // The fileid array may contain more entries than the count of the number // of recoverable files. The extra entries correspond to files that were not // used during the creation of the recovery files and which may not therefore // be repaired if they are found to be damaged. struct MAINPACKET { PACKET_HEADER header; // Body leu64 blocksize; leu32 recoverablefilecount; MD5Hash fileid[0]; //MD5Hash* fileid(void) {return (MD5Hash*)&this[1];} //const MD5Hash* fileid(void) const {return (const MD5Hash*)&this[1];} } PACKED; // The creator packet is used to identify which program created a particular // recovery file. It is not required for verification or recovery of damaged // files. struct CREATORPACKET { PACKET_HEADER header; // Body u8 client[]; //u8* client(void) {return (u8*)&this[1];} } PACKED; // The recovery block packet contains a single block of recovery data along // with the exponent value used during the computation of that block. struct RECOVERYBLOCKPACKET { PACKET_HEADER header; // Body leu32 exponent; // unsigned long data[]; // unsigned long* data(void) {return (unsigned long*)&this[1];} } PACKED; #ifdef _MSC_VER #pragma warning(default:4200) #endif #ifdef _WIN32 #pragma pack(pop) #endif #undef PACKED // Operators for comparing the MAGIC and PACKETTYPE values inline bool operator == (const MAGIC &left, const MAGIC &right) { return (0==memcmp(&left, &right, sizeof(left))); } inline bool operator != (const MAGIC &left, const MAGIC &right) { return !operator==(left, right); } inline bool operator == (const PACKETTYPE &left, const PACKETTYPE &right) { return (0==memcmp(&left, &right, sizeof(left))); } inline bool operator != (const PACKETTYPE &left, const PACKETTYPE &right) { return !operator==(left, right); } extern MAGIC packet_magic; extern PACKETTYPE fileverificationpacket_type; extern PACKETTYPE filedescriptionpacket_type; extern PACKETTYPE mainpacket_type; extern PACKETTYPE recoveryblockpacket_type; extern PACKETTYPE creatorpacket_type; #endif //__PAR2FILEFORMAT_H__ par2cmdline-turbo-1.4.0/src/par2repairer.cpp000066400000000000000000002401401514221355600207430ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #include "foreach_parallel.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif // static variable u32 Par2Repairer::filethreads = _FILE_THREADS; Par2Repairer::Par2Repairer(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel) : sout(sout) , serr(serr) , noiselevel(noiselevel) , searchpath() , basepath() , setid() , recoverypacketmap() , diskFileMap() , sourcefilemap() , sourcefiles() , verifylist() , backuplist() , par2list() , sourceblocks() , targetblocks() , blockverifiable(false) , verificationhashtable() , unverifiablesourcefiles() , inputblocks() , copyblocks() , outputblocks() , rs() { setup_hasher(); skipdata = false; skipleaway = 0; firstpacket = true; mainpacket = 0; creatorpacket = 0; blocksize = 0; chunksize = 0; sourceblockcount = 0; availableblockcount = 0; missingblockcount = 0; memset(windowtable, 0, sizeof(windowtable)); blocksallocated = false; completefilecount = 0; renamedfilecount = 0; damagedfilecount = 0; missingfilecount = 0; transferbuffer = 0; progress = 0; totaldata = 0; mttotalsize = 0; mttotalextrasize = 0; mttotalprogress.store(0, std::memory_order_relaxed); mtprocessingextrafiles = false; } Par2Repairer::~Par2Repairer(void) { delete [] (u8*)transferbuffer; parpar.deinit(); std::map::iterator rp = recoverypacketmap.begin(); while (rp != recoverypacketmap.end()) { delete (*rp).second; ++rp; } std::map::iterator sf = sourcefilemap.begin(); while (sf != sourcefilemap.end()) { Par2RepairerSourceFile *sourcefile = (*sf).second; delete sourcefile; ++sf; } delete mainpacket; delete creatorpacket; } Result Par2Repairer::Process( const size_t memorylimit, const std::string &_basepath, const u32 nthreads, const u32 _filethreads, std::string parfilename, const std::vector &_extrafiles, const bool dorepair, // derived from operation const bool purgefiles, const bool renameonly, const bool _skipdata, const u64 _skipleaway ) { filethreads = _filethreads; // Should we skip data whilst scanning files skipdata = _skipdata; // How much leaway should we allow when scanning files skipleaway = _skipleaway; // Get filenames from the command line basepath = _basepath; std::vector extrafiles = _extrafiles; // Determine the searchpath from the location of the main PAR2 file std::string name; DiskFile::SplitFilename(parfilename, searchpath, name); par2list.push_back(parfilename); // Load packets from the main PAR2 file if (!LoadPacketsFromFile(searchpath + name)) return eLogicError; // Load packets from other PAR2 files with names based on the original PAR2 file if (!LoadPacketsFromOtherFiles(parfilename)) return eLogicError; // Load packets from any other PAR2 files whose names are given on the command line if (!LoadPacketsFromExtraFiles(extrafiles)) return eLogicError; if (noiselevel > nlQuiet) sout << std::endl; // Check that the packets are consistent and discard any that are not if (!CheckPacketConsistency()) return eInsufficientCriticalData; // Use the information in the main packet to get the source files // into the correct order and determine their filenames if (!CreateSourceFileList()) return eLogicError; // Determine the total number of DataBlocks for the recoverable source files // The allocate the DataBlocks and assign them to each source file if (!AllocateSourceBlocks()) return eLogicError; // Create a verification hash table for all files for which we have not // found a complete version of the file and for which we have // a verification packet if (!PrepareVerificationHashTable()) return eLogicError; // Compute the table for the sliding CRC computation if (!ComputeWindowTable()) return eLogicError; // Attempt to verify all of the source files if (!VerifySourceFiles(basepath, extrafiles)) return eFileIOError; if (completefilecount < mainpacket->RecoverableFileCount()) { // Scan any extra files specified on the command line if (!VerifyExtraFiles(extrafiles, basepath, renameonly)) return eLogicError; } // Find out how much data we have found UpdateVerificationResults(); if (noiselevel > nlSilent) sout << std::endl; // Check the verification results and report the results if (!CheckVerificationResults()) return eRepairNotPossible; // Are any of the files incomplete if (completefilecount < mainpacket->RecoverableFileCount()) { // Do we want to carry out a repair if (dorepair) { if (noiselevel > nlSilent) sout << std::endl; // Rename any damaged or missnamed target files. if (!RenameTargetFiles()) return eFileIOError; // Are we still missing any files if (completefilecount < mainpacket->RecoverableFileCount()) { // Work out which files are being repaired, create them, and allocate // target DataBlocks to them, and remember them for later verification. if (!CreateTargetFiles()) return eFileIOError; if (nthreads != 0) rs.setNumThreads(nthreads); // Work out which data blocks are available, which need to be copied // directly to the output, and which need to be recreated, and compute // the appropriate Reed Solomon matrix. if (!ComputeRSmatrix()) { // Delete all of the partly reconstructed files DeleteIncompleteTargetFiles(); return eFileIOError; } if (noiselevel > nlSilent) sout << std::endl; // Allocate memory buffers for reading and writing data to disk. if (!AllocateBuffers(memorylimit)) { // Delete all of the partly reconstructed files DeleteIncompleteTargetFiles(); return eMemoryError; } // Init ParPar backend if (!parpar.init(chunksize, {{&parparcpu, 0, (size_t)chunksize}})) { DeleteIncompleteTargetFiles(); return eLogicError; } if (nthreads != 0) parparcpu.setNumThreads(nthreads); // If there aren't many input blocks, restrict the submission batch size u32 inputbatch = 0; if (sourceblockcount < NUM_PARPAR_BUFFERS*2) inputbatch = (sourceblockcount + 1) / 2; if (!parparcpu.init(GF16_AUTO, inputbatch) || !parpar.setRecoverySlices(missingblockcount)) { DeleteIncompleteTargetFiles(); return eMemoryError; } if (noiselevel >= nlNoisy) { sout << "Multiply method: " << parparcpu.getMethodName() << std::endl; if (noiselevel >= nlDebug) { sout << "[DEBUG] Compute tile size: " << parparcpu.getChunkLen() << std::endl; sout << "[DEBUG] Compute block grouping: " << parparcpu.getInputBatchSize() << std::endl; } sout << std::endl; } // Set the total amount of data to be processed. progress = 0; totaldata = blocksize * sourceblockcount; // Start at an offset of 0 within a block. u64 blockoffset = 0; while (blockoffset < blocksize) // Continue until the end of the block. { // Work out how much data to process this time. size_t blocklength = (size_t)std::min((u64)chunksize, blocksize-blockoffset); if (!parpar.setCurrentSliceSize(blocklength)) { DeleteIncompleteTargetFiles(); return eMemoryError; } // Read source data, process it through the RS matrix and write it to disk. if (!ProcessData(blockoffset, blocklength)) { // Delete all of the partly reconstructed files DeleteIncompleteTargetFiles(); return eFileIOError; } // Advance to the need offset within each block blockoffset += blocklength; } if (noiselevel > nlSilent) sout << std::endl << "Verifying repaired files:" << std::endl << std::endl; // Verify that all of the reconstructed target files are now correct if (!VerifyTargetFiles(basepath)) { // Delete all of the partly reconstructed files DeleteIncompleteTargetFiles(); return eFileIOError; } } // Are all of the target files now complete? if (completefilecountRecoverableFileCount()) { serr << "Repair Failed." << std::endl; return eRepairFailed; } else { if (noiselevel > nlSilent) sout << std::endl << "Repair complete." << std::endl; } } else { return eRepairPossible; } } if (purgefiles == true) { RemoveBackupFiles(); RemoveParFiles(); } return eSuccess; } // Load the packets from the specified file bool Par2Repairer::LoadPacketsFromFile(std::string filename) { // Skip the file if it has already been processed if (diskFileMap.Find(filename) != 0) { return true; } DiskFile *diskfile = new DiskFile(sout, serr, output_lock); // Open the file if (!diskfile->Open(filename)) { // If we could not open the file, ignore the error and // proceed to the next file delete diskfile; return true; } if (noiselevel > nlSilent) { std::string path; std::string name; DiskFile::SplitFilename(filename, path, name); sout << "Loading \"" << name << "\"." << std::endl; } // How many useable packets have we found u32 packets = 0; // How many recovery packets were there u32 recoverypackets = 0; // How big is the file u64 filesize = diskfile->FileSize(); if (filesize > 0) { // Allocate a buffer to read data into // The buffer should be large enough to hold a whole // critical packet (i.e. file verification, file description, main, // and creator), but not necessarily a whole recovery packet. size_t buffersize = (size_t)std::min((u64)1048576, filesize); u8 *buffer = new u8[buffersize]; // Progress indicator u64 progress = 0; // Start at the beginning of the file u64 offset = 0; // Continue as long as there is at least enough for the packet header while (offset + sizeof(PACKET_HEADER) <= filesize) { if (noiselevel > nlQuiet) { // Update a progress indicator u32 oldfraction = (u32)(1000 * progress / filesize); u32 newfraction = (u32)(1000 * offset / filesize); if (oldfraction != newfraction) { sout << "Loading: " << newfraction/10 << '.' << newfraction%10 << "%\r" << std::flush; progress = offset; } } // Attempt to read the next packet header PACKET_HEADER header; if (!diskfile->Read(offset, &header, sizeof(header))) break; // Does this look like it might be a packet if (packet_magic != header.magic) { offset++; // Is there still enough for at least a whole packet header while (offset + sizeof(PACKET_HEADER) <= filesize) { // How much can we read into the buffer size_t want = (size_t)std::min((u64)buffersize, filesize-offset); // Fill the buffer if (!diskfile->Read(offset, buffer, want)) { offset = filesize; break; } // Scan the buffer for the magic value u8 *current = buffer; u8 *limit = &buffer[want-sizeof(PACKET_HEADER)]; while (current <= limit && packet_magic != ((PACKET_HEADER*)current)->magic) { current++; } // What file offset did we reach offset += current-buffer; // Did we find the magic if (current <= limit) { memcpy(&header, current, sizeof(header)); break; } } // Did we reach the end of the file if (offset + sizeof(PACKET_HEADER) > filesize) { break; } } // We have found the magic // Check the packet length if (sizeof(PACKET_HEADER) > header.length || // packet length is too small 0 != (header.length & 3) || // packet length is not a multiple of 4 filesize < offset + header.length) // packet would extend beyond the end of the file { offset++; continue; } // Compute the MD5 Hash of the packet MD5Context context; context.Update(&header.setid, sizeof(header)-offsetof(PACKET_HEADER, setid)); // How much more do I need to read to get the whole packet u64 current = offset+sizeof(PACKET_HEADER); u64 limit = offset+header.length; while (current < limit) { size_t want = (size_t)std::min((u64)buffersize, limit-current); if (!diskfile->Read(current, buffer, want)) break; context.Update(buffer, want); current += want; } // Did the whole packet get processed if (currentClose(); // Did we actually find any interesting packets if (packets > 0) { if (noiselevel > nlQuiet) { sout << "Loaded " << packets << " new packets"; if (recoverypackets > 0) sout << " including " << recoverypackets << " recovery blocks"; sout << std::endl; } // Remember that the file was processed bool success = diskFileMap.Insert(diskfile); assert(success); } else { if (noiselevel > nlQuiet) sout << "No new packets found" << std::endl; delete diskfile; } return true; } // Finish loading a recovery packet bool Par2Repairer::LoadRecoveryPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) { RecoveryPacket *packet = new RecoveryPacket; // Load the packet from disk if (!packet->Load(diskfile, offset, header)) { delete packet; return false; } // What is the exponent value of this recovery packet u32 exponent = packet->Exponent(); // Try to insert the new packet into the recovery packet map std::pair::const_iterator, bool> location = recoverypacketmap.insert(std::pair(exponent, packet)); // Did the insert fail if (!location.second) { // The packet must be a duplicate of one we already have delete packet; return false; } return true; } // Finish loading a file description packet bool Par2Repairer::LoadDescriptionPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) { DescriptionPacket *packet = new DescriptionPacket; // Load the packet from disk if (!packet->Load(diskfile, offset, header)) { delete packet; return false; } // What is the fileid const MD5Hash &fileid = packet->FileId(); // Look up the fileid in the source file map for an existing source file entry std::map::iterator sfmi = sourcefilemap.find(fileid); Par2RepairerSourceFile *sourcefile = (sfmi == sourcefilemap.end()) ? 0 :sfmi->second; // Was there an existing source file if (sourcefile) { // Does the source file already have a description packet if (sourcefile->GetDescriptionPacket()) { // Yes. We don't need another copy delete packet; return false; } else { // No. Store the packet in the source file sourcefile->SetDescriptionPacket(packet); return true; } } else { // Create a new source file for the packet sourcefile = new Par2RepairerSourceFile(packet, NULL); // Record the source file in the source file map sourcefilemap.insert(std::pair(fileid, sourcefile)); return true; } } // Finish loading a file verification packet bool Par2Repairer::LoadVerificationPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) { VerificationPacket *packet = new VerificationPacket; // Load the packet from disk if (!packet->Load(diskfile, offset, header)) { delete packet; return false; } // What is the fileid const MD5Hash &fileid = packet->FileId(); // Look up the fileid in the source file map for an existing source file entry std::map::iterator sfmi = sourcefilemap.find(fileid); Par2RepairerSourceFile *sourcefile = (sfmi == sourcefilemap.end()) ? 0 :sfmi->second; // Was there an existing source file if (sourcefile) { // Does the source file already have a verification packet if (sourcefile->GetVerificationPacket()) { // Yes. We don't need another copy. delete packet; return false; } else { // No. Store the packet in the source file sourcefile->SetVerificationPacket(packet); return true; } } else { // Create a new source file for the packet sourcefile = new Par2RepairerSourceFile(NULL, packet); // Record the source file in the source file map sourcefilemap.insert(std::pair(fileid, sourcefile)); return true; } } // Finish loading the main packet bool Par2Repairer::LoadMainPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) { // Do we already have a main packet if (0 != mainpacket) return false; MainPacket *packet = new MainPacket; // Load the packet from disk; if (!packet->Load(diskfile, offset, header)) { delete packet; return false; } mainpacket = packet; return true; } // Finish loading the creator packet bool Par2Repairer::LoadCreatorPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) { // Do we already have a creator packet if (0 != creatorpacket) return false; CreatorPacket *packet = new CreatorPacket; // Load the packet from disk; if (!packet->Load(diskfile, offset, header)) { delete packet; return false; } creatorpacket = packet; return true; } // Load packets from other PAR2 files with names based on the original PAR2 file bool Par2Repairer::LoadPacketsFromOtherFiles(std::string filename) { // Split the original PAR2 filename into path and name parts std::string path; std::string name; DiskFile::SplitFilename(filename, path, name); std::string::size_type where; // Trim ".par2" off of the end original name // Look for the last "." in the filename while (std::string::npos != (where = name.find_last_of('.'))) { // Trim what follows the last . std::string tail = name.substr(where+1); name = name.substr(0,where); // Was what followed the last "." "par2" if (0 == stricmp(tail.c_str(), "par2")) break; } // If what is left ends in ".volNNN-NNN" or ".volNNN+NNN" strip that as well // Is there another "." if (std::string::npos != (where = name.find_last_of('.'))) { // What follows the "." std::string tail = name.substr(where+1); // Scan what follows the last "." to see of it matches vol123-456 or vol123+456 int n = 0; std::string::const_iterator p; for (p=tail.begin(); p!=tail.end(); ++p) { char ch = *p; if (0 == n) { if (tolower(ch) == 'v') { n++; } else { break; } } else if (1 == n) { if (tolower(ch) == 'o') { n++; } else { break; } } else if (2 == n) { if (tolower(ch) == 'l') { n++; } else { break; } } else if (3 == n) { if (isdigit(ch)) {} else if (ch == '-' || ch == '+') { n++; } else { break; } } else if (4 == n) { if (isdigit(ch)) {} else { break; } } } // If we matched then retain only what precedes the "." if (p == tail.end()) { name = name.substr(0,where); } } // Find files called "*.par2" or "name.*.par2" { std::string wildcard = name.empty() ? "*.par2" : name + ".*.par2"; std::unique_ptr< std::list > files( DiskFile::FindFiles(path, wildcard, false) ); par2list.splice(par2list.end(), *files); std::string wildcardu = name.empty() ? "*.PAR2" : name + ".*.PAR2"; std::unique_ptr< std::list > filesu( DiskFile::FindFiles(path, wildcardu, false) ); par2list.splice(par2list.end(), *filesu); // Load packets from each file that was found for (std::list::const_iterator s=par2list.begin(); s!=par2list.end(); ++s) { LoadPacketsFromFile(*s); } // delete files; Taken care of by unique_ptr<> // delete filesu; } return true; } // Load packets from any other PAR2 files whose names are given on the command line bool Par2Repairer::LoadPacketsFromExtraFiles(const std::vector &extrafiles) { for (std::vector::const_iterator i=extrafiles.begin(); i!=extrafiles.end(); i++) { std::string filename = *i; // If the filename contains ".par2" anywhere if (std::string::npos != filename.find(".par2") || std::string::npos != filename.find(".PAR2")) { LoadPacketsFromFile(filename); } } return true; } // Check that the packets are consistent and discard any that are not bool Par2Repairer::CheckPacketConsistency(void) { // Do we have a main packet if (0 == mainpacket) { // If we don't have a main packet, then there is nothing more that we can do. // We cannot verify or repair any files. serr << "Main packet not found." << std::endl; return false; } // Remember the block size from the main packet blocksize = mainpacket->BlockSize(); // Check that the recovery blocks have the correct amount of data // and discard any that don't { std::map::iterator rp = recoverypacketmap.begin(); while (rp != recoverypacketmap.end()) { if (rp->second->BlockSize() == blocksize) { ++rp; } else { serr << "Incorrect sized recovery block for exponent " << rp->second->Exponent() << " discarded" << std::endl; delete rp->second; std::map::iterator x = rp++; recoverypacketmap.erase(x); } } } // Check for source files that have no description packet or where the // verification packet has the wrong number of entries and discard them. { std::map::iterator sf = sourcefilemap.begin(); while (sf != sourcefilemap.end()) { // Do we have a description packet DescriptionPacket *descriptionpacket = sf->second->GetDescriptionPacket(); if (descriptionpacket == 0) { // No description packet // Discard the source file delete sf->second; std::map::iterator x = sf++; sourcefilemap.erase(x); continue; } // Compute and store the block count from the filesize and blocksize sf->second->SetBlockCount(blocksize); // Do we have a verification packet VerificationPacket *verificationpacket = sf->second->GetVerificationPacket(); if (verificationpacket == 0) { // No verification packet // That is ok, but we won't be able to use block verification. // Proceed to the next file. ++sf; continue; } // Work out the block count for the file from the file size // and compare that with the verification packet u64 filesize = descriptionpacket->FileSize(); u32 blockcount = verificationpacket->BlockCount(); if ((filesize + blocksize-1) / blocksize != (u64)blockcount) { // The block counts are different! serr << "Incorrectly sized verification packet for \"" << descriptionpacket->FileName() << "\" discarded" << std::endl; // Discard the source file delete sf->second; std::map::iterator x = sf++; sourcefilemap.erase(x); continue; } // Everything is ok. // Proceed to the next file ++sf; } } if (noiselevel > nlQuiet) { sout << "There are " << mainpacket->RecoverableFileCount() << " recoverable files and " << mainpacket->TotalFileCount() - mainpacket->RecoverableFileCount() << " other files." << std::endl; sout << "The block size used was " << blocksize << " bytes." << std::endl; } return true; } // Use the information in the main packet to get the source files // into the correct order and determine their filenames bool Par2Repairer::CreateSourceFileList(void) { // For each FileId entry in the main packet for (u32 filenumber=0; filenumberTotalFileCount(); filenumber++) { const MD5Hash &fileid = mainpacket->FileId(filenumber); // Look up the fileid in the source file map std::map::iterator sfmi = sourcefilemap.find(fileid); Par2RepairerSourceFile *sourcefile = (sfmi == sourcefilemap.end()) ? 0 :sfmi->second; if (sourcefile) { sourcefile->ComputeTargetFileName(sout, serr, noiselevel, basepath); // Need actual filesize on disk for mt-progress line sourcefile->SetDiskFileSize(); } sourcefiles.push_back(sourcefile); } return true; } // Determine the total number of DataBlocks for the recoverable source files // The allocate the DataBlocks and assign them to each source file bool Par2Repairer::AllocateSourceBlocks(void) { sourceblockcount = 0; u32 filenumber = 0; std::vector::iterator sf = sourcefiles.begin(); // For each recoverable source file while (filenumber < mainpacket->RecoverableFileCount() && sf != sourcefiles.end()) { // Do we have a source file Par2RepairerSourceFile *sourcefile = *sf; if (sourcefile) { sourceblockcount += sourcefile->BlockCount(); } else { // No details for this source file so we don't know what the // total number of source blocks is // sourceblockcount = 0; // break; } ++sf; ++filenumber; } // Did we determine the total number of source blocks if (sourceblockcount > 0) { // Yes. // Allocate all of the Source and Target DataBlocks (which will be used // to read and write data to disk). sourceblocks.resize(sourceblockcount); targetblocks.resize(sourceblockcount); // Which DataBlocks will be allocated first std::vector::iterator sourceblock = sourceblocks.begin(); std::vector::iterator targetblock = targetblocks.begin(); u64 totalsize = 0; u32 blocknumber = 0; filenumber = 0; sf = sourcefiles.begin(); while (filenumber < mainpacket->RecoverableFileCount() && sf != sourcefiles.end()) { Par2RepairerSourceFile *sourcefile = *sf; if (sourcefile) { totalsize += sourcefile->GetDescriptionPacket()->FileSize(); u32 blockcount = sourcefile->BlockCount(); // Allocate the source and target DataBlocks to the sourcefile sourcefile->SetBlocks(blocknumber, blockcount, sourceblock, targetblock, blocksize); blocknumber++; sourceblock += blockcount; targetblock += blockcount; } ++sf; ++filenumber; } blocksallocated = true; if (noiselevel > nlQuiet) { sout << "There are a total of " << sourceblockcount << " data blocks." << std::endl; sout << "The total size of the data files is " << totalsize << " bytes." << std::endl; } } return true; } // Create a verification hash table for all files for which we have not // found a complete version of the file and for which we have // a verification packet bool Par2Repairer::PrepareVerificationHashTable(void) { if (noiselevel >= nlDebug) sout << "[DEBUG] Prepare verification hashtable" << std::endl; // Choose a size for the hash table verificationhashtable.SetLimit(sourceblockcount); // Will any files be block verifiable blockverifiable = false; // For each source file std::vector::iterator sf = sourcefiles.begin(); while (sf != sourcefiles.end()) { // Get the source file Par2RepairerSourceFile *sourcefile = *sf; if (sourcefile) { // Do we have a verification packet if (0 != sourcefile->GetVerificationPacket()) { // Yes. Load the verification entries into the hash table verificationhashtable.Load(sourcefile, blocksize); blockverifiable = true; } else { // No. We can only check the whole file unverifiablesourcefiles.push_back(sourcefile); } } ++sf; } return true; } // Compute the table for the sliding CRC computation bool Par2Repairer::ComputeWindowTable(void) { if (noiselevel >= nlDebug) sout << "[DEBUG] compute window table" << std::endl; if (blockverifiable) { GenerateWindowTable(blocksize, windowtable); } return true; } static bool SortSourceFilesByFileName(Par2RepairerSourceFile *low, Par2RepairerSourceFile *high) { return low->TargetFileName() < high->TargetFileName(); } // Attempt to verify all of the source files bool Par2Repairer::VerifySourceFiles(const std::string& basepath, std::vector& extrafiles) { if (noiselevel > nlQuiet) { sout << std::endl << "Verifying source files:" << std::endl; if (noiselevel >= nlNoisy) { sout << "Data hash method: " << hasherInput_methodName() << std::endl; sout << "MD5/CRC32 method: " << md5crc_methodName() << std::endl; } sout << std::endl; } std::atomic finalresult(true); // Created a sorted list of the source files and verify them in that // order rather than the order they are in the main packet. std::vector sortedfiles; u32 filenumber = 0; std::vector::iterator sf = sourcefiles.begin(); mttotalsize = 0; mttotalprogress.store(0, std::memory_order_relaxed); while (sf != sourcefiles.end()) { // Do we have a source file Par2RepairerSourceFile *sourcefile = *sf; if (sourcefile) { sortedfiles.push_back(sourcefile); // Total filesizes for mt-progress line mttotalsize += sourcefile->DiskFileSize(); } else { // Was this one of the recoverable files if (filenumber < mainpacket->RecoverableFileCount()) { serr << "No details available for recoverable file number " << filenumber+1 << "." << std::endl << "Recovery will not be possible." << std::endl; // Set error but let verification of other files continue finalresult.store(false, std::memory_order_relaxed); } else { serr << "No details available for non-recoverable file number " << filenumber - mainpacket->RecoverableFileCount() + 1 << std::endl; } } ++sf; } std::sort(sortedfiles.begin(), sortedfiles.end(), SortSourceFilesByFileName); std::mutex dfm_lock, xfiles_lock; // Start verifying the files foreach_parallel(sortedfiles, Par2Repairer::GetFileThreads(), [&, this](Par2RepairerSourceFile* const& sortedfile) { // Do we have a source file Par2RepairerSourceFile *sourcefile = sortedfile; // What filename does the file use const std::string& file = sourcefile->TargetFileName(); const std::string& name = DiskFile::SplitRelativeFilename(file, basepath); const std::string& target_pathname = DiskFile::GetCanonicalPathname(file); if (noiselevel >= nlDebug) { std::lock_guard lock(output_lock); sout << "[DEBUG] VerifySourceFiles ----" << std::endl; sout << "[DEBUG] file: " << file << std::endl; sout << "[DEBUG] name: " << name << std::endl; sout << "[DEBUG] targ: " << target_pathname << std::endl; } // if the target file is in the list of extra files, we remove it // from the extra files. { std::lock_guard lock(xfiles_lock); std::vector::iterator it = extrafiles.begin(); for (; it != extrafiles.end(); ++it) { const std::string& e = *it; const std::string& extra_pathname = e; if (!extra_pathname.compare(target_pathname)) { extrafiles.erase(it); break; } } } // Check to see if we have already used this file dfm_lock.lock(); bool b = diskFileMap.Find(file) != 0; dfm_lock.unlock(); if (b) { finalresult.store(false, std::memory_order_relaxed); // The file has already been used! std::lock_guard lock(output_lock); serr << "Source file " << name << " is a duplicate." << std::endl; } else { DiskFile *diskfile = new DiskFile(sout, serr, output_lock); // Does the target file exist if (diskfile->Open(file)) { // Yes. Record that fact. sourcefile->SetTargetExists(true); // Remember that the DiskFile is the target file sourcefile->SetTargetFile(diskfile); // Remember that we have processed this file dfm_lock.lock(); bool success = diskFileMap.Insert(diskfile); dfm_lock.unlock(); assert(success); // Do the actual verification if (!VerifyDataFile(diskfile, sourcefile, basepath)) finalresult.store(false, std::memory_order_relaxed); // We have finished with the file for now diskfile->Close(); } else { // The file does not exist. delete diskfile; if (noiselevel > nlSilent) { std::lock_guard lock(output_lock); sout << "Target: \"" << name << "\" - missing." << std::endl; } } } }); // Find out how much data we have found UpdateVerificationResults(); return finalresult.load(std::memory_order_relaxed); } // Scan any extra files specified on the command line bool Par2Repairer::VerifyExtraFiles(const std::vector &extrafiles, const std::string &basepath, const bool renameonly) { if (noiselevel > nlQuiet) sout << std::endl << "Scanning extra files:" << std::endl << std::endl; if (completefilecount < mainpacket->RecoverableFileCount()) { // Total size of extra files for mt-progress line mtprocessingextrafiles = true; mttotalprogress.store(0, std::memory_order_relaxed); mttotalextrasize = 0; for (size_t i=0; i(extrafiles, Par2Repairer::GetFileThreads(), [&, this](const std::string& extrafile) { std::string filename = extrafile; // If the filename does not include ".par2" we are interested in it. if (std::string::npos == filename.find(".par2") && std::string::npos == filename.find(".PAR2")) { filename = DiskFile::GetCanonicalPathname(filename); // Has this file already been dealt with dfm_lock.lock(); bool b = diskFileMap.Find(filename) == 0; dfm_lock.unlock(); if (b) { DiskFile *diskfile = new DiskFile(sout, serr, output_lock); // Does the file exist if (!diskfile->Open(filename)) { delete diskfile; return; } // Remember that we have processed this file dfm_lock.lock(); bool success = diskFileMap.Insert(diskfile); dfm_lock.unlock(); assert(success); // Do the actual verification VerifyDataFile(diskfile, 0, basepath, renameonly); // Ignore errors // We have finished with the file for now diskfile->Close(); } } }); } // Find out how much data we have found UpdateVerificationResults(); mtprocessingextrafiles = false; return true; } // Attempt to match the data in the DiskFile with the source file bool Par2Repairer::VerifyDataFile(DiskFile *diskfile, Par2RepairerSourceFile *sourcefile, const std::string &basepath, const bool renameonly) { MatchType matchtype; // What type of match was made MD5Hash hashfull; // The MD5 Hash of the whole file MD5Hash hash16k; // The MD5 Hash of the files 16k of the file // Are there any files that can be verified at the block level if (blockverifiable) { u32 count; // Scan the file at the block level. if (!ScanDataFile(diskfile, // [in] The file to scan basepath, renameonly, // [in] Only look for perfect matches sourcefile, // [in/out] Modified in the match is for another source file matchtype, // [out] hashfull, // [out] hash16k, // [out] count)) // [out] return false; switch (matchtype) { case eNoMatch: // No data was found at all. // Continue to next test. break; case ePartialMatch: { // We found some data. // Return them. return true; } break; case eFullMatch: { // We found a perfect match. sourcefile->SetCompleteFile(diskfile); // Return the match return true; } break; } } // We did not find a match for any blocks of data within the file, but if // there are any files for which we did not have a verification packet // we can try a simple match of the hash for the whole file. // Are there any files that cannot be verified at the block level if (!unverifiablesourcefiles.empty()) { // Would we have already computed the file hashes if (!blockverifiable) { u64 filesize = diskfile->FileSize(); size_t buffersize = 1024*1024; if (buffersize > std::min(blocksize, filesize)) buffersize = (size_t)std::min(blocksize, filesize); char *buffer = new char[buffersize]; u64 offset = 0; MD5Context context; while (offset < filesize) { size_t want = (size_t)std::min((u64)buffersize, filesize-offset); if (!diskfile->Read(offset, buffer, want)) { delete [] buffer; return false; } // Will the newly read data reach the 16k boundary if (offset < 16384 && offset + want >= 16384) { context.Update(buffer, (size_t)(16384-offset)); // Compute the 16k hash MD5Context temp = context; temp.Final(hash16k); // Is there more data if (offset + want > 16384) { context.Update(&buffer[16384-offset], (size_t)(offset+want)-16384); } } else { context.Update(buffer, want); } offset += want; } // Compute the file hash MD5Hash hashfull; context.Final(hashfull); // If we did not have 16k of data, then the 16k hash // is the same as the full hash if (filesize < 16384) { hash16k = hashfull; } } std::list::iterator sf = unverifiablesourcefiles.begin(); // Compare the hash values of each source file for a match while (sf != unverifiablesourcefiles.end()) { sourcefile = *sf; // Does the file match if (sourcefile->GetCompleteFile() == 0 && diskfile->FileSize() == sourcefile->GetDescriptionPacket()->FileSize() && hash16k == sourcefile->GetDescriptionPacket()->Hash16k() && hashfull == sourcefile->GetDescriptionPacket()->HashFull()) { if (noiselevel > nlSilent) { std::lock_guard lock(output_lock); sout << diskfile->FileName() << " is a perfect match for " << sourcefile->GetDescriptionPacket()->FileName() << std::endl; } // Record that we have a perfect match for this source file sourcefile->SetCompleteFile(diskfile); if (blocksallocated) { // Allocate all of the DataBlocks for the source file to the DiskFile u64 offset = 0; u64 filesize = sourcefile->GetDescriptionPacket()->FileSize(); std::vector::iterator sb = sourcefile->SourceBlocks(); while (offset < filesize) { DataBlock &datablock = *sb; datablock.SetLocation(diskfile, offset); datablock.SetLength(std::min(blocksize, filesize-offset)); offset += blocksize; ++sb; } } // Return the match return true; } ++sf; } } return true; } // Perform a sliding window scan of the DiskFile looking for blocks of data that // might belong to any of the source files (for which a verification packet was // available). If a block of data might be from more than one source file, prefer // the one specified by the "sourcefile" parameter. If the first data block // found is for a different source file then "sourcefile" is changed accordingly. bool Par2Repairer::ScanDataFile(DiskFile *diskfile, // [in] std::string basepath, // [in] const bool renameonly, // [in] Par2RepairerSourceFile* &sourcefile, // [in/out] MatchType &matchtype, // [out] MD5Hash &hashfull, // [out] MD5Hash &hash16k, // [out] u32 &count) // [out] { // Remember which file we wanted to match Par2RepairerSourceFile *originalsourcefile = sourcefile; std::string name; DiskFile::SplitRelativeFilename(diskfile->FileName(), basepath, name); // Is the file empty if (originalsourcefile != 0 && originalsourcefile->GetTargetExists()) { // don't check size if target was found } else if (diskfile->FileSize() == 0) { // If the file is empty, then just return if (noiselevel > nlSilent) { std::lock_guard lock(output_lock); sout << "File: \"" << name << "\" - empty." << std::endl; } return true; } std::string shortname; if (name.size() > 56) { shortname = name.substr(0, 28) + "..." + name.substr(name.size()-28); } else { shortname = name; } // Create the checksummer for the file and start reading from it FileCheckSummer filechecksummer(diskfile, blocksize, windowtable); if (!filechecksummer.Start()) return false; // Assume we will make a perfect match for the file matchtype = eFullMatch; // How many matches have we had count = 0; // How many blocks have already been found u32 duplicatecount = 0; // Have we found data blocks in this file that belong to more than one target file bool multipletargets = false; // Which block do we expect to find first const VerificationHashEntry *nextentry = 0; // How far will we scan the file (1 byte at a time) // before skipping ahead looking for the next block u64 scandistance = std::min(skipleaway<<1, blocksize); // Distance to skip forward if we don't find a block u64 scanskip = skipdata ? blocksize - scandistance : 0; // Assume with are half way through scanning u64 scanoffset = scandistance >> 1; // Total number of bytes that were skipped whilst scanning u64 skippeddata = 0; // Offset of last data that was found u64 lastmatchoffset = 0; bool progressline = false; u64 oldoffset = 0; u64 printprogress = 0; if (noiselevel > nlQuiet) { std::lock_guard lock(output_lock); sout << "Opening: \"" << shortname << "\"" << std::endl; } // Whilst we have not reached the end of the file while (filechecksummer.Offset() < diskfile->FileSize()) { if (noiselevel > nlQuiet) { // Are we processing extrafiles? Use correct total size u64 ts = mtprocessingextrafiles ? mttotalextrasize : mttotalsize; // Update progress indicator printprogress += filechecksummer.Offset() - oldoffset; if (printprogress == blocksize || filechecksummer.ShortBlock()) { u64 totalprogress = mttotalprogress.fetch_add(printprogress, std::memory_order_relaxed); u32 oldfraction = (u32)(1000 * (totalprogress - printprogress) / ts); u32 newfraction = (u32)(1000 * totalprogress / ts); printprogress = 0; if (oldfraction != newfraction) { std::lock_guard lock(output_lock); sout << "Scanning: " << newfraction/10 << '.' << newfraction%10 << "%\r" << std::flush; progressline = true; } } oldoffset = filechecksummer.Offset(); } // If we fail to find a match, it might be because it was a duplicate of a block // that we have already found. bool duplicate; // Look for a match const VerificationHashEntry *currententry = verificationhashtable.FindMatch(nextentry, sourcefile, filechecksummer, duplicate); // Did we find a match if (currententry != 0) { if (lastmatchoffset < filechecksummer.Offset() && noiselevel > nlNormal) { std::lock_guard lock(output_lock); if (progressline) { sout << std::endl; progressline = false; } sout << "No data found between offset " << lastmatchoffset << " and " << filechecksummer.Offset() << std::endl; } // Is this the first match if (count == 0) { // Which source file was it sourcefile = currententry->SourceFile(); // If the first match found was not actually the first block // for the source file, or it was not at the start of the // data file: then this is a partial match. if (!currententry->FirstBlock() || filechecksummer.Offset() != 0) { matchtype = ePartialMatch; // In rename-only mode, skip files that are not perfect matches if (renameonly) { return true; } } } else { // If the match found is not the one which was expected // then this is a partial match if (currententry != nextentry) { matchtype = ePartialMatch; // In rename-only mode, skip files that are not perfect matches if (renameonly) { return true; } } // Is the match from a different source file if (sourcefile != currententry->SourceFile()) { multipletargets = true; } } if (blocksallocated) { // Record the match currententry->SetBlock(diskfile, filechecksummer.Offset()); } // Update the number of matches found count++; // What entry do we expect next nextentry = currententry->Next(); // Advance to the next block if (!filechecksummer.Jump(currententry->GetDataBlock()->GetLength())) return false; // If the next match fails, assume we hare half way through scanning for the next block scanoffset = scandistance >> 1; // Update offset of last match lastmatchoffset = filechecksummer.Offset(); } else { // This cannot be a perfect match matchtype = ePartialMatch; // In rename-only mode, skip files that are not perfect matches if (renameonly) { return true; } // Was this a duplicate match if (duplicate && false) // ignore duplicates { duplicatecount++; // What entry would we expect next nextentry = 0; // Advance one whole block if (!filechecksummer.Jump(blocksize)) return false; } else { // What entry do we expect next nextentry = 0; if (!filechecksummer.Step()) return false; u64 skipfrom = filechecksummer.Offset(); // Have we scanned too far without finding a block? if (scanskip > 0 && ++scanoffset >= scandistance && skipfrom < diskfile->FileSize()) { // Skip forwards to where we think we might find more data if (!filechecksummer.Jump(scanskip)) return false; // Update the count of skipped data skippeddata += filechecksummer.Offset() - skipfrom; // Reset scan offset to 0 scanoffset = 0; } } } } if (noiselevel > nlQuiet) { if (filechecksummer.Offset() == diskfile->FileSize()) { mttotalprogress.fetch_add(filechecksummer.Offset() - oldoffset, std::memory_order_relaxed); } } if (lastmatchoffset < filechecksummer.Offset() && noiselevel > nlNormal) { std::lock_guard lock(output_lock); if (progressline) { sout << std::endl; } sout << "No data found between offset " << lastmatchoffset << " and " << filechecksummer.Offset() << std::endl; } // Get the Full and 16k hash values of the file filechecksummer.GetFileHashes(hashfull, hash16k); if (noiselevel >= nlDebug) { std::lock_guard lock(output_lock); // Clear out old scanning line sout << std::setw(shortname.size()+19) << std::setfill(' ') << ""; if (duplicatecount > 0) sout << "\r[DEBUG] duplicates: " << duplicatecount << std::endl; sout << "\r[DEBUG] matchcount: " << count << std::endl; sout << "[DEBUG] ----------------------" << std::endl; } // Did we make any matches at all if (count > 0) { // If this still might be a perfect match, check the // hashes, file size, and number of blocks to confirm. if (matchtype != eFullMatch || count != sourcefile->GetVerificationPacket()->BlockCount() || diskfile->FileSize() != sourcefile->GetDescriptionPacket()->FileSize() || hashfull != sourcefile->GetDescriptionPacket()->HashFull() || hash16k != sourcefile->GetDescriptionPacket()->Hash16k()) { matchtype = ePartialMatch; if (noiselevel > nlSilent) { // Did we find data from multiple target files if (multipletargets) { // Were we scanning the target file or an extra file if (originalsourcefile != 0) { std::lock_guard lock(output_lock); sout << "Target: \"" << name << "\" - damaged, found " << count << " data blocks from several target files." << std::endl; } else { std::lock_guard lock(output_lock); sout << "File: \"" << name << "\" - found " << count << " data blocks from several target files." << std::endl; } } else { // Did we find data blocks that belong to the target file if (originalsourcefile == sourcefile) { std::lock_guard lock(output_lock); sout << "Target: \"" << name << "\" - damaged. Found " << count << " of " << sourcefile->GetVerificationPacket()->BlockCount() << " data blocks." << std::endl; } // Were we scanning the target file or an extra file else { std::string targetname; DiskFile::SplitRelativeFilename(sourcefile->TargetFileName(), basepath, targetname); if (originalsourcefile != 0) { std::lock_guard lock(output_lock); sout << "Target: \"" << name << "\" - damaged. Found " << count << " of " << sourcefile->GetVerificationPacket()->BlockCount() << " data blocks from \"" << targetname << "\"." << std::endl; } else { std::lock_guard lock(output_lock); sout << "File: \"" << name << "\" - found " << count << " of " << sourcefile->GetVerificationPacket()->BlockCount() << " data blocks from \"" << targetname << "\"." << std::endl; } } } if (skippeddata > 0) { std::lock_guard lock(output_lock); sout << skippeddata << " bytes of data were skipped whilst scanning." << std::endl << "If there are not enough blocks found to repair: try again " << "with the -N option." << std::endl; } } } else { if (noiselevel > nlSilent) { // Did we match the target file if (originalsourcefile == sourcefile) { std::lock_guard lock(output_lock); sout << "Target: \"" << name << "\" - found." << std::endl; } // Were we scanning the target file or an extra file else { std::string targetname; DiskFile::SplitRelativeFilename(sourcefile->TargetFileName(), basepath, targetname); if (originalsourcefile != 0) { std::lock_guard lock(output_lock); sout << "Target: \"" << name << "\" - is a match for \"" << targetname << "\"." << std::endl; } else { std::lock_guard lock(output_lock); sout << "File: \"" << name << "\" - is a match for \"" << targetname << "\"." << std::endl; } } } } } else { matchtype = eNoMatch; if (noiselevel > nlSilent) { // We found not data, but did the file actually contain blocks we // had already found in other files. if (duplicatecount > 0) { std::lock_guard lock(output_lock); sout << "File: \"" << name << "\" - found " << duplicatecount << " duplicate data blocks." << std::endl; } else { std::lock_guard lock(output_lock); sout << "File: \"" << name << "\" - no data found." << std::endl; } if (skippeddata > 0) { std::lock_guard lock(output_lock); sout << skippeddata << " bytes of data were skipped whilst scanning." << std::endl << "If there are not enough blocks found to repair: try again " << "with the -N option." << std::endl; } } } return true; } // Find out how much data we have found void Par2Repairer::UpdateVerificationResults(void) { availableblockcount = 0; missingblockcount = 0; completefilecount = 0; renamedfilecount = 0; damagedfilecount = 0; missingfilecount = 0; u32 filenumber = 0; std::vector::iterator sf = sourcefiles.begin(); // Check the recoverable files while (sf != sourcefiles.end() && filenumber < mainpacket->TotalFileCount()) { Par2RepairerSourceFile *sourcefile = *sf; if (sourcefile) { // Was a perfect match for the file found if (sourcefile->GetCompleteFile() != 0) { // Is it the target file or a different one if (sourcefile->GetCompleteFile() == sourcefile->GetTargetFile()) { completefilecount++; } else { renamedfilecount++; } availableblockcount += sourcefile->BlockCount(); } else { // Count the number of blocks that have been found std::vector::iterator sb = sourcefile->SourceBlocks(); for (u32 blocknumber=0; blocknumberBlockCount(); ++blocknumber, ++sb) { DataBlock &datablock = *sb; if (datablock.IsSet()) availableblockcount++; } // Does the target file exist if (sourcefile->GetTargetExists()) { damagedfilecount++; } else { missingfilecount++; } } } else { missingfilecount++; } ++filenumber; ++sf; } missingblockcount = sourceblockcount - availableblockcount; } // Check the verification results and report the results bool Par2Repairer::CheckVerificationResults(void) { // Is repair needed if (completefilecount < mainpacket->RecoverableFileCount() || renamedfilecount > 0 || damagedfilecount > 0 || missingfilecount > 0) { if (noiselevel > nlSilent) sout << "Repair is required." << std::endl; if (noiselevel > nlQuiet) { if (renamedfilecount > 0) sout << renamedfilecount << " file(s) have the wrong name." << std::endl; if (missingfilecount > 0) sout << missingfilecount << " file(s) are missing." << std::endl; if (damagedfilecount > 0) sout << damagedfilecount << " file(s) exist but are damaged." << std::endl; if (completefilecount > 0) sout << completefilecount << " file(s) are ok." << std::endl; sout << "You have " << availableblockcount << " out of " << sourceblockcount << " data blocks available." << std::endl; if (recoverypacketmap.size() > 0) sout << "You have " << (u32)recoverypacketmap.size() << " recovery blocks available." << std::endl; } // Is repair possible if (recoverypacketmap.size() >= missingblockcount) { if (noiselevel > nlSilent) sout << "Repair is possible." << std::endl; if (noiselevel > nlQuiet) { if (recoverypacketmap.size() > missingblockcount) sout << "You have an excess of " << (u32)recoverypacketmap.size() - missingblockcount << " recovery blocks." << std::endl; if (missingblockcount > 0) sout << missingblockcount << " recovery blocks will be used to repair." << std::endl; else if (recoverypacketmap.size()) sout << "None of the recovery blocks will be used for the repair." << std::endl; } return true; } else { if (noiselevel > nlSilent) { sout << "Repair is not possible." << std::endl; sout << "You need " << missingblockcount - recoverypacketmap.size() << " more recovery blocks to be able to repair." << std::endl; } return false; } } else { if (noiselevel > nlSilent) sout << "All files are correct, repair is not required." << std::endl; return true; } return true; } // Rename any damaged or missnamed target files. bool Par2Repairer::RenameTargetFiles(void) { u32 filenumber = 0; std::vector::iterator sf = sourcefiles.begin(); // Rename any damaged target files while (sf != sourcefiles.end() && filenumber < mainpacket->TotalFileCount()) { Par2RepairerSourceFile *sourcefile = *sf; // If the target file exists but is not a complete version of the file if (sourcefile->GetTargetExists() && sourcefile->GetTargetFile() != sourcefile->GetCompleteFile()) { DiskFile *targetfile = sourcefile->GetTargetFile(); // Rename it diskFileMap.Remove(targetfile); if (!targetfile->Rename()) return false; backuplist.push_back(targetfile); bool success = diskFileMap.Insert(targetfile); assert(success); // We no longer have a target file sourcefile->SetTargetExists(false); sourcefile->SetTargetFile(0); } ++sf; ++filenumber; } filenumber = 0; sf = sourcefiles.begin(); // Rename any missnamed but complete versions of the files while (sf != sourcefiles.end() && filenumber < mainpacket->TotalFileCount()) { Par2RepairerSourceFile *sourcefile = *sf; // If there is no targetfile and there is a complete version if (sourcefile->GetTargetFile() == 0 && sourcefile->GetCompleteFile() != 0) { DiskFile *targetfile = sourcefile->GetCompleteFile(); // Rename it diskFileMap.Remove(targetfile); if (!targetfile->Rename(sourcefile->TargetFileName())) return false; bool success = diskFileMap.Insert(targetfile); assert(success); // This file is now the target file sourcefile->SetTargetExists(true); sourcefile->SetTargetFile(targetfile); // We have one more complete file completefilecount++; } ++sf; ++filenumber; } return true; } // Work out which files are being repaired, create them, and allocate // target DataBlocks to them, and remember them for later verification. bool Par2Repairer::CreateTargetFiles(void) { u32 filenumber = 0; std::vector::iterator sf = sourcefiles.begin(); // Create any missing target files while (sf != sourcefiles.end() && filenumber < mainpacket->TotalFileCount()) { Par2RepairerSourceFile *sourcefile = *sf; // If the file does not exist if (!sourcefile->GetTargetExists()) { DiskFile *targetfile = new DiskFile(sout, serr, output_lock); std::string filename = sourcefile->TargetFileName(); u64 filesize = sourcefile->GetDescriptionPacket()->FileSize(); // Create the target file if (!targetfile->Create(filename, filesize)) { delete targetfile; return false; } // This file is now the target file sourcefile->SetTargetExists(true); sourcefile->SetTargetFile(targetfile); // Remember this file bool success = diskFileMap.Insert(targetfile); assert(success); u64 offset = 0; std::vector::iterator tb = sourcefile->TargetBlocks(); // Allocate all of the target data blocks while (offset < filesize) { DataBlock &datablock = *tb; datablock.SetLocation(targetfile, offset); datablock.SetLength(std::min(blocksize, filesize-offset)); offset += blocksize; ++tb; } // Add the file to the list of those that will need to be verified // once the repair has completed. verifylist.push_back(sourcefile); } ++sf; ++filenumber; } return true; } // Work out which data blocks are available, which need to be copied // directly to the output, and which need to be recreated, and compute // the appropriate Reed Solomon matrix. bool Par2Repairer::ComputeRSmatrix(void) { inputblocks.resize(sourceblockcount); // The DataBlocks that will read from disk copyblocks.resize(availableblockcount); // Those DataBlocks which need to be copied outputblocks.resize(missingblockcount); // Those DataBlocks that will re recalculated std::vector::iterator inputblock = inputblocks.begin(); std::vector::iterator copyblock = copyblocks.begin(); std::vector::iterator outputblock = outputblocks.begin(); // Build an array listing which source data blocks are present and which are missing std::vector present; present.resize(sourceblockcount); std::vector::iterator sourceblock = sourceblocks.begin(); std::vector::iterator targetblock = targetblocks.begin(); std::vector::iterator pres = present.begin(); // Iterate through all source blocks for all files while (sourceblock != sourceblocks.end()) { // Was this block found if (sourceblock->IsSet()) { //// Open the file the block was found in. //if (!sourceblock->Open()) // return false; // Record that the block was found *pres = true; // Add the block to the list of those which will be read // as input (and which might also need to be copied). *inputblock = &*sourceblock; *copyblock = &*targetblock; ++inputblock; ++copyblock; } else { // Record that the block was missing *pres = false; // Add the block to the list of those to be written *outputblock = &*targetblock; ++outputblock; } ++sourceblock; ++targetblock; ++pres; } // If we need to, compute and solve the RS matrix if (missingblockcount == 0) return true; // Create a list of available recovery exponents std::vector recindex; recindex.reserve(recoverypacketmap.size()); for (auto rp = recoverypacketmap.begin(); rp != recoverypacketmap.end(); rp++) recindex.push_back(rp->first); // Set up progress display std::function progressfunc; int progress = 0; bool progressStarted = false; if (noiselevel > nlQuiet) { sout << "Computing Reed Solomon matrix." << std::endl; progressfunc = [&](u16 done, u16 total) { if (done == 0) { if(progressStarted) sout << "Bad recovery block discarded and retrying RS matrix inversion." << std::endl; else { progressStarted = true; if (noiselevel >= nlNoisy) { sout << "Construction accel: " << rs.getPointMulMethodName() << std::endl; sout << "Inversion method: " << Galois16Mul::methodToText((Galois16Methods)rs.regionMethod) << std::endl; } } sout << "Constructing: 0.0%\r" << std::flush; progress = 0; return; } if (done == 1) sout << "Constructing: done." << std::endl; int newprogress = (done-1) * 1000 / (total-1); if (progress != newprogress) { progress = newprogress; sout << "Solving: " << progress/10 << '.' << progress%10 << "%\r" << std::flush; } }; } // Compute + solve RS matrix if (!rs.Compute(present, availableblockcount, recindex, progressfunc)) { serr << "RS computation error (this may be fixable with more recovery blocks)." << std::endl; return false; } if (noiselevel > nlQuiet) sout << "Solving: done." << std::endl; if (noiselevel >= nlDebug) { for (unsigned int row=0; rowGetDataBlock(); // Add the recovery block to the list of blocks that will be read *inputblock = recoveryblock; ++inputblock; } return true; } // Allocate memory buffers for reading and writing data to disk. bool Par2Repairer::AllocateBuffers(size_t memorylimit) { // We use intermediary buffers to transfer data with, so include those in the limit calculation u32 blockoverhead = NUM_TRANSFER_BUFFERS + std::min((u32)NUM_PARPAR_BUFFERS*2, sourceblockcount+1); // Would single pass processing use too much memory if (blocksize * (missingblockcount + blockoverhead) > memorylimit) { // Pick a size that is small enough chunksize = ~3 & (memorylimit / (missingblockcount + blockoverhead)); } else { chunksize = (size_t)blocksize; } if (MAX_CHUNK_SIZE != 0 && chunksize > MAX_CHUNK_SIZE) chunksize = MAX_CHUNK_SIZE; if (noiselevel >= nlDebug) sout << "[DEBUG] Process chunk size: " << chunksize << std::endl; // Allocate buffer transferbuffer = new u8[(size_t)chunksize * NUM_TRANSFER_BUFFERS]; if (transferbuffer == NULL) { serr << "Could not allocate buffer memory." << std::endl; return false; } return true; } // Read source data, process it through the RS matrix and write it to disk. bool Par2Repairer::ProcessData(u64 blockoffset, size_t blocklength) { u64 totalwritten = 0; std::vector::iterator inputblock = inputblocks.begin(); std::vector::iterator copyblock = copyblocks.begin(); u32 inputindex = 0; DiskFile *lastopenfile = NULL; // Are there any blocks which need to be reconstructed if (missingblockcount > 0) { // For tracking input buffer availability std::future bufferavail[NUM_TRANSFER_BUFFERS]; u32 bufferindex = NUM_TRANSFER_BUFFERS - 1; // Set all input buffers to available for (i32 i = 0; i < NUM_TRANSFER_BUFFERS; i++) { std::promise stub; bufferavail[i] = stub.get_future(); stub.set_value(); } // Clear existing output data in backend parpar.discardOutput(); // Temporary storage for factors std::vector factors(missingblockcount); // For each input block while (inputblock != inputblocks.end()) { // Are we reading from a new file? if (lastopenfile != (*inputblock)->GetDiskFile()) { // Close the last file if (lastopenfile != NULL) { lastopenfile->Close(); } // Open the new file lastopenfile = (*inputblock)->GetDiskFile(); if (!lastopenfile->Open()) { return false; } } // Wait for next input buffer to become available bufferindex = (bufferindex + 1) % NUM_TRANSFER_BUFFERS; void *inputbuffer = (char*)transferbuffer + chunksize * bufferindex; bufferavail[bufferindex].get(); // Read data from the current input block if (!(*inputblock)->ReadData(blockoffset, blocklength, inputbuffer)) return false; // Have we reached the last source data block if (copyblock != copyblocks.end()) { // Does this block need to be copied to the target file if ((*copyblock)->IsSet()) { size_t wrote; // Write the block back to disk in the new target file if (!(*copyblock)->WriteData(blockoffset, blocklength, inputbuffer, wrote)) return false; totalwritten += wrote; } ++copyblock; } // Copy RS matrix column to send to backend for (u32 outputindex=0; outputindex nlQuiet) { // Update a progress indicator u32 oldfraction = (u32)(1000 * progress / totaldata); progress += blocklength; u32 newfraction = (u32)(1000 * progress / totaldata); if (oldfraction != newfraction) { sout << "Repairing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << std::flush; } } ++inputblock; ++inputindex; } // Flush backend parpar.endInput().get(); } else { // Reconstruction is not required, we are just copying blocks between files // For each block that might need to be copied while (copyblock != copyblocks.end()) { // Does this block need to be copied if ((*copyblock)->IsSet()) { // Are we reading from a new file? if (lastopenfile != (*inputblock)->GetDiskFile()) { // Close the last file if (lastopenfile != NULL) { lastopenfile->Close(); } // Open the new file lastopenfile = (*inputblock)->GetDiskFile(); if (!lastopenfile->Open()) { return false; } } // Read data from the current input block if (!(*inputblock)->ReadData(blockoffset, blocklength, transferbuffer)) return false; size_t wrote; if (!(*copyblock)->WriteData(blockoffset, blocklength, transferbuffer, wrote)) return false; totalwritten += wrote; } if (noiselevel > nlQuiet) { // Update a progress indicator u32 oldfraction = (u32)(1000 * progress / totaldata); progress += blocklength; u32 newfraction = (u32)(1000 * progress / totaldata); if (oldfraction != newfraction) { sout << "Processing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << std::flush; } } ++copyblock; ++inputblock; } } // Close the last file if (lastopenfile != NULL) { lastopenfile->Close(); } if (noiselevel > nlQuiet) sout << "Writing recovered data\r"; if (missingblockcount > 0) { // For output, we only need two transfer buffers std::future outbufavail[2]; // Prepare first output outbufavail[0] = parpar.getOutput(0, transferbuffer); // For each output block that has been recomputed std::vector::iterator outputblock = outputblocks.begin(); for (u32 outputindex=0; outputindexWriteData(blockoffset, blocklength, outputbuffer, wrote)) return false; totalwritten += wrote; ++outputblock; } } if (noiselevel > nlQuiet) sout << "Wrote " << totalwritten << " bytes to disk" << std::endl; return true; } // Verify that all of the reconstructed target files are now correct bool Par2Repairer::VerifyTargetFiles(const std::string &basepath) { std::atomic finalresult(true); // Verify the target files in alphabetical order std::sort(verifylist.begin(), verifylist.end(), SortSourceFilesByFileName); mttotalsize = 0; mttotalprogress.store(0, std::memory_order_relaxed); for (size_t i=0; iGetDescriptionPacket()->FileSize(); } // Iterate through each file in the verification list foreach_parallel(verifylist, Par2Repairer::GetFileThreads(), [&, this](Par2RepairerSourceFile* const& verifyfile) { Par2RepairerSourceFile *sourcefile = verifyfile; DiskFile *targetfile = sourcefile->GetTargetFile(); // Close the file if (targetfile->IsOpen()) targetfile->Close(); // Mark all data blocks for the file as unknown std::vector::iterator sb = sourcefile->SourceBlocks(); for (u32 blocknumber=0; blocknumberBlockCount(); blocknumber++) { sb->ClearLocation(); ++sb; } // Say we don't have a complete version of the file sourcefile->SetCompleteFile(0); // Re-open the target file if (!targetfile->Open()) { finalresult.store(false, std::memory_order_relaxed); return; } // Verify the file again if (!VerifyDataFile(targetfile, sourcefile, basepath)) finalresult.store(false, std::memory_order_relaxed); // Close the file again targetfile->Close(); }); // Find out how much data we have found UpdateVerificationResults(); return finalresult.load(std::memory_order_relaxed); } // Delete all of the partly reconstructed files bool Par2Repairer::DeleteIncompleteTargetFiles(void) { std::vector::iterator sf = verifylist.begin(); // Iterate through each file in the verification list while (sf != verifylist.end()) { Par2RepairerSourceFile *sourcefile = *sf; if (sourcefile->GetTargetExists()) { DiskFile *targetfile = sourcefile->GetTargetFile(); // Close and delete the file if (targetfile->IsOpen()) targetfile->Close(); targetfile->Delete(); // Forget the file diskFileMap.Remove(targetfile); delete targetfile; // There is no target file sourcefile->SetTargetExists(false); sourcefile->SetTargetFile(0); } ++sf; } return true; } bool Par2Repairer::RemoveBackupFiles(void) { std::vector::iterator bf = backuplist.begin(); if (noiselevel > nlSilent && bf != backuplist.end()) { sout << std::endl << "Purge backup files." << std::endl; } // Iterate through each file in the backuplist while (bf != backuplist.end()) { if (noiselevel > nlSilent) { std::string name; std::string path; DiskFile::SplitFilename((*bf)->FileName(), path, name); sout << "Remove \"" << name << "\"." << std::endl; } if ((*bf)->IsOpen()) (*bf)->Close(); (*bf)->Delete(); ++bf; } return true; } bool Par2Repairer::RemoveParFiles(void) { if (noiselevel > nlSilent && !par2list.empty()) { sout << std::endl << "Purge par files." << std::endl; } for (std::list::const_iterator s=par2list.begin(); s!=par2list.end(); ++s) { DiskFile *diskfile = new DiskFile(sout, serr, output_lock); if (diskfile->Open(*s)) { if (noiselevel > nlSilent) { std::string name; std::string path; DiskFile::SplitFilename((*s), path, name); sout << "Remove \"" << name << "\"." << std::endl; } if (diskfile->IsOpen()) diskfile->Close(); diskfile->Delete(); } delete diskfile; } return true; } par2cmdline-turbo-1.4.0/src/par2repairer.h000066400000000000000000000252171514221355600204160ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __PAR2REPAIRER_H__ #define __PAR2REPAIRER_H__ #include "../parpar/gf16/controller_cpu.h" #ifndef PARPAR_INVERT_SUPPORT # define PARPAR_INVERT_SUPPORT 1 #endif #include "../parpar/gf16/gfmat_inv.h" #include #include class Par2Repairer { public: Par2Repairer(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel); ~Par2Repairer(void); Result Process(const size_t memorylimit, const std::string &basepath, const u32 nthreads, const u32 filethreads, std::string parfilename, const std::vector &extrafiles, const bool dorepair, // derived from operation const bool purgefiles, const bool renameonly, const bool skipdata, const u64 skipleaway ); protected: // Steps in verifying and repairing files: // Load packets from the specified file bool LoadPacketsFromFile(std::string filename); // Finish loading a recovery packet bool LoadRecoveryPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); // Finish loading a file description packet bool LoadDescriptionPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); // Finish loading a file verification packet bool LoadVerificationPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); // Finish loading the main packet bool LoadMainPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); // Finish loading the creator packet bool LoadCreatorPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); // Load packets from other PAR2 files with names based on the original PAR2 file bool LoadPacketsFromOtherFiles(std::string filename); // Load packets from any other PAR2 files whose names are given on the command line bool LoadPacketsFromExtraFiles(const std::vector &extrafiles); // Check that the packets are consistent and discard any that are not bool CheckPacketConsistency(void); // Use the information in the main packet to get the source files // into the correct order and determine their filenames bool CreateSourceFileList(void); // Determine the total number of DataBlocks for the recoverable source files // The allocate the DataBlocks and assign them to each source file bool AllocateSourceBlocks(void); // Create a verification hash table for all files for which we have not // found a complete version of the file and for which we have // a verification packet bool PrepareVerificationHashTable(void); // Compute the table for the sliding CRC computation bool ComputeWindowTable(void); // Attempt to verify all of the source files bool VerifySourceFiles(const std::string& basepath, std::vector& extrafiles); // Scan any extra files specified on the command line bool VerifyExtraFiles(const std::vector &extrafiles, const std::string &basepath, const bool renameonly); // Attempt to match the data in the DiskFile with the source file bool VerifyDataFile(DiskFile *diskfile, Par2RepairerSourceFile *sourcefile, const std::string &basepath, const bool renameonly = false); // Perform a sliding window scan of the DiskFile looking for blocks of data that // might belong to any of the source files (for which a verification packet was // available). If a block of data might be from more than one source file, prefer // the one specified by the "sourcefile" parameter. If the first data block // found is for a different source file then "sourcefile" is changed accordingly. bool ScanDataFile(DiskFile *diskfile, // [in] The file being scanned std::string basepath, // [in] const bool renameonly, // [in] Only look for perfect matches Par2RepairerSourceFile* &sourcefile, // [in/out] The source file matched MatchType &matchtype, // [out] The type of match MD5Hash &hashfull, // [out] The full hash of the file MD5Hash &hash16k, // [out] The hash of the first 16k u32 &count); // [out] The number of blocks found // Find out how much data we have found void UpdateVerificationResults(void); // Check the verification results and report the results bool CheckVerificationResults(void); // Rename any damaged or missnamed target files. bool RenameTargetFiles(void); // Work out which files are being repaired, create them, and allocate // target DataBlocks to them, and remember them for later verification. bool CreateTargetFiles(void); // Work out which data blocks are available, which need to be copied // directly to the output, and which need to be recreated, and compute // the appropriate Reed Solomon matrix. bool ComputeRSmatrix(void); // Allocate memory buffers for reading and writing data to disk. bool AllocateBuffers(size_t memorylimit); // Read source data, process it through the RS matrix and write it to disk. bool ProcessData(u64 blockoffset, size_t blocklength); // Verify that all of the reconstructed target files are now correct bool VerifyTargetFiles(const std::string &basepath); // Delete all of the partly reconstructed files bool DeleteIncompleteTargetFiles(void); // list the files needing verification bool RemoveBackupFiles(void); bool RemoveParFiles(void); static u32 GetFileThreads(void) {return filethreads;} protected: std::ostream &sout; // stream for output (for commandline, this is cout) std::ostream &serr; // stream for errors (for commandline, this is cerr) std::mutex output_lock; const NoiseLevel noiselevel; // OnScreen display std::string searchpath; // Where to find files on disk std::string basepath; static u32 filethreads; // Number of threads for file processing bool skipdata; // Should we skip data whilst scanning u64 skipleaway; // The leaway +/- we should allow whilst scanning bool firstpacket; // Whether or not a valid packet has been found. MD5Hash setid; // The SetId extracted from the first packet. std::map recoverypacketmap; // One recovery packet for each exponent value. MainPacket *mainpacket; // One copy of the main packet. CreatorPacket *creatorpacket; // One copy of the creator packet. DiskFileMap diskFileMap; std::map sourcefilemap;// Map from FileId to SourceFile std::vector sourcefiles; // The source files std::vector verifylist; // Those source files that are being repaired std::vector backuplist; // Those source files backups std::list par2list; // list of par2 files u64 blocksize; // The block size. u64 chunksize; // How much of a block can be processed. u32 sourceblockcount; // The total number of blocks u32 availableblockcount; // How many undamaged blocks have been found u32 missingblockcount; // How many blocks are missing bool blocksallocated; // Whether or not the DataBlocks have been allocated std::vector sourceblocks; // The DataBlocks that will be read from disk std::vector targetblocks; // The DataBlocks that will be written to disk u32 windowtable[256]; // Table for sliding CRCs bool blockverifiable; // Whether and files can be verified at the block level VerificationHashTable verificationhashtable; // Hash table for block verification std::list unverifiablesourcefiles; // Files that are not block verifiable u32 completefilecount; // How many files are fully verified u32 renamedfilecount; // How many files are verified but have the wrong name u32 damagedfilecount; // How many files exist but are damaged u32 missingfilecount; // How many files are completely missing std::vector inputblocks; // Which DataBlocks will be read from disk std::vector copyblocks; // Which DataBlocks will copied back to disk std::vector outputblocks; // Which DataBlocks have to calculated using RS Galois16RecMatrix rs; // The Reed Solomon matrix. PAR2Proc parpar; // Main ParPar backend PAR2ProcCPU parparcpu; // ParPar CPU sub-backend void *transferbuffer; // Buffer for reading/writing DataBlocks (chunksize * num_transfer_buffers) u64 progress; // How much data has been processed. u64 totaldata; // Total amount of data to be processed. u64 mttotalsize; // Total size of files for mt-progress line u64 mttotalextrasize; // Total size of extra files for mt-progress line std::atomic mttotalprogress; // MT total progress bool mtprocessingextrafiles; // Are we currently processing extra files }; #endif // __PAR2REPAIRER_H__ par2cmdline-turbo-1.4.0/src/par2repairersourcefile.cpp000066400000000000000000000105061514221355600230250ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif Par2RepairerSourceFile::Par2RepairerSourceFile(DescriptionPacket *_descriptionpacket, VerificationPacket *_verificationpacket) : sourceblocks() , targetblocks() , targetfilename() { descriptionpacket = _descriptionpacket; verificationpacket = _verificationpacket; blockcount = 0; firstblocknumber = 0; // verificationhashtable = 0; targetexists = false; targetfile = 0; completefile = 0; diskfilesize = 0; } Par2RepairerSourceFile::~Par2RepairerSourceFile(void) { delete descriptionpacket; delete verificationpacket; // delete verificationhashtable; } void Par2RepairerSourceFile::SetDescriptionPacket(DescriptionPacket *_descriptionpacket) { descriptionpacket = _descriptionpacket; } void Par2RepairerSourceFile::SetVerificationPacket(VerificationPacket *_verificationpacket) { verificationpacket = _verificationpacket; } void Par2RepairerSourceFile::ComputeTargetFileName(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, const std::string &path) { // Get a version of the filename compatible with the OS std::string filename = DescriptionPacket::TranslateFilenameFromPar2ToLocal(sout, serr, noiselevel, descriptionpacket->FileName()); targetfilename = path + filename; } std::string Par2RepairerSourceFile::TargetFileName(void) const { return targetfilename; } void Par2RepairerSourceFile::SetTargetFile(DiskFile *diskfile) { targetfile = diskfile; } DiskFile* Par2RepairerSourceFile::GetTargetFile(void) const { return targetfile; } void Par2RepairerSourceFile::SetTargetExists(bool exists) { targetexists = exists; } bool Par2RepairerSourceFile::GetTargetExists(void) const { return targetexists; } void Par2RepairerSourceFile::SetCompleteFile(DiskFile *diskfile) { completefile = diskfile; } DiskFile* Par2RepairerSourceFile::GetCompleteFile(void) const { return completefile; } // Remember which source and target blocks will be used by this file // and set their lengths appropriately void Par2RepairerSourceFile::SetBlocks(u32 _blocknumber, u32 _blockcount, std::vector::iterator _sourceblocks, std::vector::iterator _targetblocks, u64 blocksize) { firstblocknumber = _blocknumber; blockcount = _blockcount; sourceblocks = _sourceblocks; targetblocks = _targetblocks; if (blockcount > 0) { u64 filesize = descriptionpacket->FileSize(); std::vector::iterator sb = sourceblocks; for (u32 blocknumber=0; blocknumberFileSize() + blocksize-1) / blocksize); } else { blockcount = 0; } } void Par2RepairerSourceFile::SetDiskFileSize() { diskfilesize = DiskFile::GetFileSize(targetfilename); } par2cmdline-turbo-1.4.0/src/par2repairersourcefile.h000066400000000000000000000112401514221355600224660ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __PAR2REPAIRERSOURCEFILE_H__ #define __PAR2REPAIRERSOURCEFILE_H__ enum MatchType { eNoMatch = 0, ePartialMatch, eFullMatch }; // The Par2RepairerSourceFile object is used during verification and repair // to record details about a particular source file and the data blocks // for that file. class Par2RepairerSourceFile { public: // Construct the object and set the description and verification packets Par2RepairerSourceFile(DescriptionPacket *descriptionpacket, VerificationPacket *verificationpacket); ~Par2RepairerSourceFile(void); // Get/Set the description packet DescriptionPacket* GetDescriptionPacket(void) const {return descriptionpacket;} void SetDescriptionPacket(DescriptionPacket *descriptionpacket); // Get/Set the verification packet VerificationPacket* GetVerificationPacket(void) const {return verificationpacket;} void SetVerificationPacket(VerificationPacket *verificationpacket); // Record the details as to which data blocks belong to this source // file and set the length of each allocated block correctly. void SetBlocks(u32 _blocknumber, u32 _blockcount, std::vector::iterator _sourceblocks, std::vector::iterator _targetblocks, u64 blocksize); // Determine the block count from the file size and block size. void SetBlockCount(u64 blocksize); // Set/Get which DiskFile will contain the final repaired version of the file void SetTargetFile(DiskFile *diskfile); DiskFile* GetTargetFile(void) const; // Set/Get whether or not the target file actually exists void SetTargetExists(bool exists); bool GetTargetExists(void) const; // Set/Get which DiskFile contains a full undamaged version of the source file void SetCompleteFile(DiskFile *diskfile); DiskFile* GetCompleteFile(void) const; // Compute/Get the filename for the final repaired version of the file void ComputeTargetFileName(std::ostream &sout, std::ostream &serr, const NoiseLevel noiselevel, const std::string &path); std::string TargetFileName(void) const; // Get the number of blocks that the file uses u32 BlockCount(void) const {return blockcount;} // Get the relative block number of the first block in the file u32 FirstBlockNumber(void) const {return firstblocknumber;} // Get the first source DataBlock for the file std::vector::iterator SourceBlocks(void) const {return sourceblocks;} // Get the first target DataBlock for the file std::vector::iterator TargetBlocks(void) const {return targetblocks;} // Set/Get "filesize on disk" needed for mt progress line void SetDiskFileSize(); u64 DiskFileSize(void) const {return diskfilesize;} protected: DescriptionPacket *descriptionpacket; // The file description packet VerificationPacket *verificationpacket; // The file verification packet u32 blockcount; // The number of DataBlocks in the file u32 firstblocknumber; // The block number of the first DataBlock std::vector::iterator sourceblocks; // The first source DataBlock std::vector::iterator targetblocks; // The first target DataBlock bool targetexists; // Whether the target file exists DiskFile *targetfile; // The final version of the file DiskFile *completefile; // A complete version of the file std::string targetfilename; // The filename of the target file u64 diskfilesize; // The filesize of sourcefile on disk }; #endif // __PAR2REPAIRERSOURCEFILE_H__ par2cmdline-turbo-1.4.0/src/recoverypacket.cpp000066400000000000000000000101541514221355600213730ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif RecoveryPacket::RecoveryPacket(void) : packet() , datablock() { diskfile = NULL; offset = 0; packetcontext = NULL; } RecoveryPacket::~RecoveryPacket(void) { delete packetcontext; } // Create a recovery packet. // The packet header can be almost completely filled in using the supplied // information and computation of the hash of the packet started immediately. // The hash will be updated as new data is written to the packet. void RecoveryPacket::Create(DiskFile *_diskfile, u64 _offset, u64 _blocksize, u32 _exponent, const MD5Hash &_setid) { diskfile = _diskfile; offset = _offset; // Record everything we know in the packet. packet.header.magic = packet_magic; packet.header.length = sizeof(packet) + _blocksize; //packet.header.hash; // Not known yet. packet.header.setid = _setid; packet.header.type = recoveryblockpacket_type; packet.exponent = _exponent; // Start computation of the packet hash packetcontext = new MD5Context; packetcontext->Update(&packet.header.setid, sizeof(RECOVERYBLOCKPACKET)-offsetof(RECOVERYBLOCKPACKET, header.setid)); // Set the data block to immediately follow the header on disk datablock.SetLocation(_diskfile, _offset + sizeof(packet)); datablock.SetLength(_blocksize); } // Write data from the buffer to the data block on disk bool RecoveryPacket::WriteData(u64 position, size_t size, const void *buffer) { // Update the packet hash packetcontext->Update(buffer, size); // Write the data to the data block size_t wrote; return datablock.WriteData(position, size, buffer, wrote); } // Write the header of the packet to disk bool RecoveryPacket::WriteHeader(void) { // Finish computing the packet hash packetcontext->Final(packet.header.hash); // Write the header to disk return diskfile->Write(offset, &packet, sizeof(packet)); } // Load the recovery packet from disk. // // The header of the packet will already have been read from disk. The only // thing that actually needs to be read is the exponent value. // The recovery data is not read from disk at this point. Its location // is recovered in the DataBlock object. bool RecoveryPacket::Load(DiskFile *_diskfile, u64 _offset, PACKET_HEADER &_header) { diskfile = _diskfile; offset = _offset; // Is the packet actually large enough if (_header.length <= sizeof(packet)) { return false; } // Save the fixed header packet.header = _header; // Set the data block to immediately follow the header on disk datablock.SetLocation(diskfile, offset + sizeof(packet)); datablock.SetLength(packet.header.length - sizeof(packet)); // Read the rest of the packet header return diskfile->Read(offset + sizeof(packet.header), &packet.exponent, sizeof(packet)-sizeof(packet.header)); } par2cmdline-turbo-1.4.0/src/recoverypacket.h000066400000000000000000000067621514221355600210520ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __RECOVERYPACKET_H__ #define __RECOVERYPACKET_H__ // The RecoveryPacket object is used to access a specific recovery // packet within a recovery file (for when creating them, or using // them during a repair operation). // Because the actual recovery data for the packet may be large, the // RecoveryPacket object only contains a copy of packet header and // exponent value for the block and uses a DataBlock object for // all accesses to the actual recovery data in the packet. class RecoveryPacket { public: RecoveryPacket(void); ~RecoveryPacket(void); public: // Create a recovery packet for a specified file. void Create(DiskFile *diskfile, // Which file will the packet be stored in u64 offset, // At what offset will the packet be stored u64 blocksize, // How much recovery data will it contain u32 exponent, // What exponent value will be used const MD5Hash &setid); // What is the SetId // Write some data to the recovery data block and update the recovery packet. bool WriteData(u64 position, // Relative position within the data block size_t size, // Size of data to write to block const void *buffer); // Buffer containing the data to write // Finish computing the hash of the recovery packet and write the header to disk. bool WriteHeader(void); public: // Load a recovery packet from a specified file bool Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); public: // Get the length of the packet. u64 PacketLength(void) const; // The exponent of the packet. u32 Exponent(void) const; // The length of the recovery data u64 BlockSize(void) const; // The data block DataBlock* GetDataBlock(void); protected: DiskFile *diskfile; // The specific file that this packet is stored in u64 offset; // The offset at which the packet is stored RECOVERYBLOCKPACKET packet; // The packet (excluding the actual recovery data) MD5Context *packetcontext; // MD5 Context used to compute the packet hash DataBlock datablock; // The recovery data block. }; inline u64 RecoveryPacket::PacketLength(void) const { return packet.header.length; } inline u32 RecoveryPacket::Exponent(void) const { return packet.exponent; } inline u64 RecoveryPacket::BlockSize(void) const { return packet.header.length - sizeof(packet); } inline DataBlock* RecoveryPacket::GetDataBlock(void) { return &datablock; } #endif // __RECOVERYPACKET_H__ par2cmdline-turbo-1.4.0/src/reedsolomon.cpp000066400000000000000000000102431514221355600206720ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif u32 gcd(u32 a, u32 b) { if (a && b) { while (a && b) { if (a>b) { a = a%b; } else { b = b%a; } } return a+b; } else { return 0; } } template <> bool ReedSolomon::SetInput(const std::vector &present, std::ostream &sout, std::ostream &serr) { inputcount = (u32)present.size(); datapresentindex = new u32[inputcount]; datamissingindex = new u32[inputcount]; database = new G::ValueType[inputcount]; G::ValueType base = 1; for (unsigned int index=0; index bool ReedSolomon::SetInput(u32 count, std::ostream &sout, std::ostream &serr) { inputcount = count; datapresentindex = new u32[inputcount]; datamissingindex = new u32[inputcount]; database = new G::ValueType[inputcount]; G::ValueType base = 1; for (unsigned int index=0; index bool ReedSolomon::InternalProcess(const Galois8 &factor, size_t size, const void *inputbuffer, void *outputbuffer) { #ifdef LONGMULTIPLY // The 8-bit long multiplication tables Galois8 *table = glmt->tables; // Split the factor into Low and High bytes unsigned int fl = (factor >> 0) & 0xff; // Get the four separate multiplication tables Galois8 *LL = &table[(0*256 + fl) * 256 + 0]; // factor.low * source.low // Combine the four multiplication tables into two unsigned int L[256]; unsigned int *pL = &L[0]; for (unsigned int i=0; i<256; i++) { *pL = *LL; pL++; LL++; } // Treat the buffers as arrays of 32-bit unsigned ints. u32 *src4 = (u32 *)inputbuffer; u32 *end4 = (u32 *)&((u8*)inputbuffer)[size & ~3]; u32 *dst4 = (u32 *)outputbuffer; // Process the data while (src4 < end4) { u32 s = *src4++; // Use the two lookup tables computed earlier *dst4++ ^= (L[(s >> 0) & 0xff] ) ^ (L[(s >> 8) & 0xff] << 8 ) ^ (L[(s >> 16)& 0xff] << 16) ^ (L[(s >> 24)& 0xff] << 24); } // Process any left over bytes at the end of the buffer if (size & 3) { u8 *src1 = &((u8*)inputbuffer)[size & ~3]; u8 *end1 = &((u8*)inputbuffer)[size]; u8 *dst1 = &((u8*)outputbuffer)[size & ~3]; // Process the data while (src1 < end1) { u8 s = *src1++; *dst1++ ^= L[s]; } } #else // Treat the buffers as arrays of 16-bit Galois values. Galois8 *src = (Galois8 *)inputbuffer; Galois8 *end = (Galois8 *)&((u8*)inputbuffer)[size]; Galois8 *dst = (Galois8 *)outputbuffer; // Process the data while (src < end) { *dst++ += *src++ * factor; } #endif return eSuccess; } par2cmdline-turbo-1.4.0/src/reedsolomon.h000066400000000000000000000425161514221355600203470ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __REEDSOLOMON_H__ #define __REEDSOLOMON_H__ // The ReedSolomon object is used to calculate and store the matrix // used during recovery block creation or data block reconstruction. // // During initialisation, one RSOutputRow object is created for each // recovery block that either needs to be created or is available for // use. class RSOutputRow { public: RSOutputRow(void) {}; RSOutputRow(bool _present, u16 _exponent) : present(_present), exponent(_exponent) {} public: bool present; u16 exponent; }; template class ReedSolomon { public: typedef g G; ReedSolomon(void); ~ReedSolomon(void); // Set which input blocks are present or missing // Some input blocks are present bool SetInput(const std::vector &present, std::ostream &sout, std::ostream &serr); // All input blocks are present bool SetInput(u32 count, std::ostream &sout, std::ostream &serr); // Set which output block are available or need to be computed bool SetOutput(bool present, u16 exponent); bool SetOutput(bool present, u16 lowexponent, u16 highexponent); // Compute the RS Matrix bool Compute(NoiseLevel noiselevel, std::ostream &sout, std::ostream &serr); // Process a block of data bool Process(size_t size, // The size of the block of data u32 inputindex, // The column in the RS matrix const void *inputbuffer, // Buffer containing input data u32 outputindex, // The row in the RS matrix void *outputbuffer); // Buffer containing output data // Get a processing coefficient from the matrix u32 GetFactor(u32 inputindex, u32 outputindex); private: bool InternalProcess(const g &factor, size_t size, const void *inputbuffer, void *outputbuffer); // Optimization protected: // Perform Gaussian Elimination bool GaussElim(NoiseLevel noiselevel, std::ostream &sout, std::ostream &serr, unsigned int rows, unsigned int leftcols, G *leftmatrix, G *rightmatrix, unsigned int datamissing); protected: u32 inputcount; // Total number of input blocks u32 datapresent; // Number of input blocks that are present u32 datamissing; // Number of input blocks that are missing u32 *datapresentindex; // The index numbers of the data blocks that are present u32 *datamissingindex; // The index numbers of the data blocks that are missing typename G::ValueType *database;// The "base" value to use for each input block u32 outputcount; // Total number of output blocks u32 parpresent; // Number of output blocks that are present u32 parmissing; // Number of output blocks that are missing u32 *parpresentindex; // The index numbers of the output blocks that are present u32 *parmissingindex; // The index numbers of the output blocks that are missing std::vector outputrows; // Details of the output blocks G *leftmatrix; // The main matrix // When the matrices are initialised: values of the form base ^ exponent are // stored (where the base values are obtained from database[] and the exponent // values are obtained from outputrows[]). #ifdef LONGMULTIPLY GaloisLongMultiplyTable *glmt; // A multiplication table used by Process() #endif private: // private copy constructor to prevent any misuse. ReedSolomon(const ReedSolomon &); ReedSolomon& operator=(const ReedSolomon &); }; template inline ReedSolomon::ReedSolomon(void) : outputrows() { inputcount = 0; datapresent = 0; datamissing = 0; datapresentindex = 0; datamissingindex = 0; database = 0; outputcount = 0; parpresent = 0; parmissing = 0; parpresentindex = 0; parmissingindex = 0; leftmatrix = 0; #ifdef LONGMULTIPLY glmt = new GaloisLongMultiplyTable; #endif } template inline ReedSolomon::~ReedSolomon(void) { delete [] datapresentindex; delete [] datamissingindex; delete [] database; delete [] parpresentindex; delete [] parmissingindex; delete [] leftmatrix; #ifdef LONGMULTIPLY delete glmt; #endif } template inline bool ReedSolomon::Process(size_t size, u32 inputindex, const void *inputbuffer, u32 outputindex, void *outputbuffer) { // Optimization: it occurs frequently the function exits early on, so inline the start. // This resulted in a speed gain of approx. 8% in repairing. // Look up the appropriate element in the RS matrix g factor = leftmatrix[outputindex * (datapresent + datamissing) + inputindex]; // Do nothing if the factor happens to be 0 if (factor == 0) return eSuccess; return this->InternalProcess (factor, size, inputbuffer, outputbuffer); } template inline u32 ReedSolomon::GetFactor(u32 inputindex, u32 outputindex) { return leftmatrix[outputindex * (datapresent + datamissing) + inputindex].Value(); } u32 gcd(u32 a, u32 b); // Record whether the recovery block with the specified // exponent values is present or missing. template inline bool ReedSolomon::SetOutput(bool present, u16 exponent) { // Store the exponent and whether or not the recovery block is present or missing outputrows.push_back(RSOutputRow(present, exponent)); outputcount++; // Update the counts. if (present) { parpresent++; } else { parmissing++; } return true; } // Record whether the recovery blocks with the specified // range of exponent values are present or missing. template inline bool ReedSolomon::SetOutput(bool present, u16 lowexponent, u16 highexponent) { for (unsigned int exponent=lowexponent; exponent<=highexponent; exponent++) { if (!SetOutput(present, exponent)) return false; } return true; } // Construct the Vandermonde matrix and solve it if necessary template inline bool ReedSolomon::Compute(NoiseLevel noiselevel, std::ostream &sout, std::ostream &serr) { u32 outcount = datamissing + parmissing; u32 incount = datapresent + datamissing; if (datamissing > parpresent) { serr << "Not enough recovery blocks." << std::endl; return false; } else if (outcount == 0) { serr << "No output blocks." << std::endl; return false; } if (noiselevel > nlQuiet) sout << "Computing Reed Solomon matrix." << std::endl; /* Layout of RS Matrix: NOTE: The second set of columns represents the parity vectors present, but this only uses datamissing of them. Otherwise, it is over constrained. datamissing datapresent <=parpresent datamissing parmissing / | \ / | \ | (ppi[row])| | | (ppi[row])| | datamissing | ^ | I | | ^ | 0 | |(dpi[col]) | | |(dmi[col]) | | +---------------------+-------------+ +---------------------+-----------+ | (pmi[row])| | | (pmi[row])| | parmissing | ^ | 0 | | ^ | I | |(dpi[col]) | | |(dmi[col]) | | \ | / \ | / */ // Allocate the left hand matrix leftmatrix = new G[outcount * incount]; std::fill(leftmatrix, leftmatrix + outcount * incount, G(0)); // Allocate the right hand matrix only if we are recovering G *rightmatrix = 0; if (datamissing > 0) { rightmatrix = new G[outcount * outcount]; std::fill(rightmatrix, rightmatrix + outcount * outcount, G(0)); } // Fill in the two matrices: std::vector::const_iterator outputrow = outputrows.begin(); // One row for each present recovery block that will be used for a missing data block for (unsigned int row=0; row nlQuiet) { int progress = row * 1000 / (datamissing+parmissing); sout << "Constructing: " << progress/10 << '.' << progress%10 << "%\r" << std::flush; } #endif // Get the exponent of the next present recovery block while (!outputrow->present) { outputrow++; } u16 exponent = outputrow->exponent; // One column for each present data block for (unsigned int col=0; col 0) { // One column for each missing data block for (unsigned int col=0; col nlQuiet) { int progress = (row+datamissing) * 1000 / (datamissing+parmissing); sout << "Constructing: " << progress/10 << '.' << progress%10 << "%\r" << std::flush; } #endif // Get the exponent of the next missing recovery block while (outputrow->present) { outputrow++; } u16 exponent = outputrow->exponent; // One column for each present data block for (unsigned int col=0; col 0) { // One column for each missing data block for (unsigned int col=0; col nlQuiet) sout << "Constructing: done." << std::endl; // Solve the matrices only if recovering data if (datamissing > 0) { // Perform Gaussian Elimination and then delete the right matrix (which // will no longer be required). bool success = GaussElim(noiselevel, sout, serr, outcount, incount, leftmatrix, rightmatrix, datamissing); delete [] rightmatrix; return success; } return true; } // Use Gaussian Elimination to solve the matrices template inline bool ReedSolomon::GaussElim(NoiseLevel noiselevel, std::ostream &sout, std::ostream &serr, unsigned int rows, unsigned int leftcols, G *leftmatrix, G *rightmatrix, unsigned int datamissing) { if (noiselevel >= nlDebug) { for (unsigned int row=0; row8?4:2) << std::setfill('0') << (unsigned int)leftmatrix[row*leftcols+col]; } sout << ((row==0) ? " \\ /" : (row==rows-1) ? " / \\" : " | |"); for (unsigned int col=0; col8?4:2) << std::setfill('0') << (unsigned int)rightmatrix[row*rows+col]; } sout << ((row==0) ? " \\" : (row==rows-1) ? " /" : " | |"); sout << std::endl; sout << std::dec << std::setw(0) << std::setfill(' '); } } // Because the matrices being operated on are Vandermonde matrices // they are guaranteed not to be singular. // Additionally, because Galois arithmetic is being used, all calculations // involve exact values with no loss of precision. It is therefore // not necessary to carry out any row or column swapping. // Solve one row at a time int progress = 0; // For each row in the matrix for (unsigned int row=0; row nlQuiet) { int newprogress = (row*rows+row2) * 1000 / (datamissing*rows); if (progress != newprogress) { progress = newprogress; sout << "Solving: " << progress/10 << '.' << progress%10 << "%\r" << std::flush; } } #endif if (row != row2) { // Get the scaling factor for this row. G scalevalue = rightmatrix[row2 * rows + row]; if (scalevalue == 1) { // If the scaling factor happens to be 1, just subtract rows for (unsigned int col=0; col nlQuiet) sout << "Solving: done." << std::endl; if (noiselevel >= nlDebug) { for (unsigned int row=0; row8?4:2) << std::setfill('0') << (unsigned int)leftmatrix[row*leftcols+col]; } sout << ((row==0) ? " \\ /" : (row==rows-1) ? " / \\" : " | |"); for (unsigned int col=0; col8?4:2) << std::setfill('0') << (unsigned int)rightmatrix[row*rows+col]; } sout << ((row==0) ? " \\" : (row==rows-1) ? " /" : " | |"); sout << std::endl; sout << std::dec << std::setw(0) << std::setfill(' '); } } return true; } #endif // __REEDSOLOMON_H__ par2cmdline-turbo-1.4.0/src/reedsolomon_test.cpp000066400000000000000000000417061514221355600217410ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include "libpar2internal.h" #include "reedsolomon.h" /* trace from test11.log Creator.SetInput 100 Creator.SetOutput 0 4 Creator.Compute creator.process 10004 0 0 creator.process 10004 0 1 creator.process 10004 0 2 creator.process 10004 0 3 creator.process 10004 0 4 creator.process 10004 1 0 creator.process 10004 1 1 creator.process 10004 1 2 creator.process 10004 1 3 creator.process 10004 1 4 creator.process 10004 2 0 creator.process 10004 2 1 creator.process 10004 2 2 ... creator.process 10004 97 4 creator.process 10004 98 0 creator.process 10004 98 1 creator.process 10004 98 2 creator.process 10004 98 3 creator.process 10004 98 4 creator.process 10004 99 0 creator.process 10004 99 1 creator.process 10004 99 2 creator.process 10004 99 3 creator.process 10004 99 4 ... ... ... Repairer.SetInput 100 true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true false false false true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true true false true true true true true true true true true true false Repair.SetOutput true 0 Repair.SetOutput true 1 Repair.SetOutput true 2 Repair.SetOutput true 3 Repair.SetOutput true 4 repair.compute repair.process 10004 0 1 repair.process 10004 0 2 repair.process 10004 0 4 repair.process 10004 0 0 repair.process 10004 0 3 repair.process repair.process 10004 repair.process 10004 repair.process 1 1 10004 repair.process 2 1 3 10004 4 1 0 repair.process 10004 2 2 repair.process 10004 2 3 repair.process 10004 2 repair.process 10004 2 1 repair.process */ /* Reed-Solomon operations require a field. This is a dummy one, using a prime number. Due to how Log() is implemented, the prime must be greater than 3. */ /** This is a simpler field than Galois. * I'm not sure why ReedSolomon was * specialized for Galois8 and Galois16. * It should be made to work with this type. * #include template class PrimeField { public: typedef valuetype ValueType; // Basic constructors PrimeField(void) { value = 0; } PrimeField(ValueType v) { value = v;} // Copy and assignment PrimeField(const PrimeField &right) {value = right.value;} PrimeField& operator = (const PrimeField &right) { value = right.value; return *this;} // Addition PrimeField operator + (const PrimeField &right) const { return (valuetype) ((value + (unsigned long) right.value) % prime); } PrimeField& operator += (const PrimeField &right) { value = (valuetype) ((value + (unsigned long) right.value) % prime); return *this;} // Subtraction PrimeField operator - (const PrimeField &right) const { return (right.value <= value) ? value - right.value : prime + value - right.value;} PrimeField& operator -= (const PrimeField &right) { value = (right.value <= value) ? value - right.value : prime + value - right.value; return *this;} // Multiplication PrimeField operator * (const PrimeField &right) const { return (valuetype) ((value * (unsigned long) right.value) % prime); } PrimeField& operator *= (const PrimeField &right) { value = (valuetype) ((value * (unsigned long) right.value) % prime); return *this;} // Division PrimeField operator / (const PrimeField &right) const { return (valuetype) ((value * (unsigned long) reciprocal(right.value)) % prime); } PrimeField& operator /= (const PrimeField &right) { value = (valuetype) ((value * (unsigned long) reciprocal(right.value)) % prime); return *this;} // Power PrimeField pow(unsigned int right) const { return (valuetype) (((long) std::pow((double) value, (double) right)) % prime); } PrimeField operator ^ (unsigned int right) const { return (valuetype) (((long) std::pow((double) value, (double) right)) % prime); } PrimeField& operator ^= (unsigned int right) { value = (valuetype) (((long) std::pow((double) value, (double) right)) % prime); return *this; } // Cast to value and value access operator ValueType(void) const {return value;} ValueType Value(void) const {return value;} // Direct log and antilog // base 2 ValueType Log(void) const { ValueType v(1); for (int i = 0; i < prime; i++) { if (v == value) return i; v = (valuetype) ((2l*v)%prime); } std::cerr << "Did not find multiplicative inverse for " << value << std::endl; } ValueType ALog(void) const { return (valuetype) (((long) std::pow((double) 2, (double) value)) % prime); } enum { Bits = bits, // Count = GaloisTable::Count, // Limit = GaloisTable::Limit, }; protected: ValueType reciprocal(ValueType v) { if (v == 0) return v; for (ValueType i = 1; i < prime; i++) { if ((i*v) % prime == 1) return i; } std::cerr << "Did not find multiplicative inverse for " << v << std::endl; } ValueType value; }; */ #define BUF_SIZE 1024 template int generate_data(unsigned int seed, u8 data[][BUF_SIZE], int in_count, int recovery_count, int low_exponent) { int high_exponent = low_exponent + recovery_count - 1; // random input data srand(seed); for (int i = 0; i < in_count; i++) { for (int k = 0; k < BUF_SIZE; k++) { data[i][k] = (u8)(rand() % 256); } } // zero recovery for (int j = 0; j < recovery_count; j++) { for (int k = 0; k < BUF_SIZE; k++) { data[in_count + j][k] = (u8)0; } } ReedSolomon rs_creator; //std::cout << "creator.setinput" << in_count << std::endl; if (!rs_creator.SetInput(in_count, std::cout, std::cerr)) { std::cerr << "rs_creator.SetInput returned false"; return 1; } //std::cout << "creator.setoutput" << low_exponent << " " << high_exponent << std::endl; if (!rs_creator.SetOutput(false, low_exponent, high_exponent)) { std::cerr << "rs_creator.SetOutput returned false"; return 1; } //std::cout << "creator.compute" << std::endl; if (!rs_creator.Compute(nlSilent, std::cout, std::cerr)) { std::cerr << "rs_creator.Compute returned false"; return 1; } for (int i = 0; i < in_count; i++) { for (int j = 0; j < recovery_count; j++) { //std::cout << "creator.process " << BUF_SIZE << " " << i << " " << j << std::endl; rs_creator.Process(BUF_SIZE, i, &(data[i][0]), j, &(data[in_count + j][0])); } } return 0; } template int init_repair_rs(ReedSolomon &rs_repair, std::vector &in_present, std::vector &recovery_present, int low_exponent) { //std::cout << "Repairer.SetInput " << in_present.size(); //for (unsigned int z = 0; z < in_present.size(); z++) // std::cout << (in_present[z] ? " true": " false"); //std::cout << std::endl; if (!rs_repair.SetInput(in_present, std::cout, std::cerr)) { std::cerr << "rs_repair.SetInput returned false"; return 1; } for (unsigned int j = 0; j < recovery_present.size(); j++) { //std::cout << "Repair.SetOutput true " << j << std::endl; if (recovery_present[j]) { if (!rs_repair.SetOutput(true, low_exponent + j)) { std::cerr << "rs_repair.SetOutput returned false for " << j; return 1; } } } //std::cout << "Repair.compute" << std::endl; if (!rs_repair.Compute(nlSilent, std::cout, std::cerr)) { std::cerr << "rs_repair.Compute returned false"; return 1; } return 0; } // Strange that "missing place" is 0,1,2,3... // no matter what exponent or which in_present are false. template int recover_data(ReedSolomon &rs_repair, int missing_place, u8 *buffer, u8 data[][BUF_SIZE], std::vector &in_present, std::vector &recovery_present) { memset(buffer, 0, BUF_SIZE); int index = 0; for (unsigned int i = 0; i < in_present.size(); i++) { if (in_present[i]) { //std::cout << "repair.process " << BUF_SIZE << " " << index << " " << 0 << std::endl; rs_repair.Process(BUF_SIZE, index, &(data[i][0]), missing_place, buffer); index++; } } for (unsigned int j = 0; j < recovery_present.size(); j++) { if (recovery_present[j]) { //std::cout << "repair.process " << BUF_SIZE << " " << index << " " << 0 << std::endl; rs_repair.Process(BUF_SIZE, index, &(data[in_present.size() + j][0]), missing_place, buffer); index++; } } return 0; } int compare_buffer(u8 *buffer, u8 *expected, const char *error_prefix) { for (int k = 0; k < BUF_SIZE; k++) { if (buffer[k] != expected[k]) { std::cerr << error_prefix << " mismatch at place " << k << std::endl; std::cerr << " buffer had " << ((int) buffer[k]) << std::endl; std::cerr << " expected " << ((int) expected[k]) << std::endl; return 1; } } return 0; } // 4 inputs, recover all possible cases of 2 missing inputs template int test1() { const int NUM_IN = 4; const int NUM_REC = 2; // recovery const int LOW_EXPONENT = 0; u8 data[NUM_IN + NUM_REC][BUF_SIZE]; if (generate_data(873945932, data, NUM_IN, NUM_REC, LOW_EXPONENT)) return 1; // loop over missing input blocks for (int missing1 = 0; missing1 < NUM_IN; missing1++) { for (int missing2 = missing1+1; missing2 < NUM_IN; missing2++) { std::vector in_present; for (int i = 0; i < NUM_IN; i++) { in_present.push_back(i != missing1 && i != missing2); } std::vector recovery_present; for (int i = 0; i < NUM_REC; i++) { recovery_present.push_back(true); } ReedSolomon rs_repair; if (init_repair_rs(rs_repair, in_present, recovery_present, LOW_EXPONENT)) return 1; u8 result[BUF_SIZE]; if (recover_data(rs_repair, 0, result, data, in_present, recovery_present)) return 1; if (compare_buffer(result, data[missing1], "test1 - missing1")) return 1; if (recover_data(rs_repair, 1, result, data, in_present, recovery_present)) return 1; if (compare_buffer(result, data[missing2], "test1 - missing2")) return 1; } } return 0; } // recover when all inputs are missing template int test2() { const int NUM_IN = 5; const int NUM_REC = 5; // recovery const int LOW_EXPONENT = 0; u8 data[NUM_IN + NUM_REC][BUF_SIZE]; if (generate_data(873945932, data, NUM_IN, NUM_REC, LOW_EXPONENT)) return 1; std::vector in_present; for (int i = 0; i < NUM_IN; i++) { in_present.push_back(false); } std::vector recovery_present; for (int i = 0; i < NUM_REC; i++) { recovery_present.push_back(true); } ReedSolomon rs_repair; if (init_repair_rs(rs_repair, in_present, recovery_present, LOW_EXPONENT)) return 1; for (int i = 0; i < NUM_IN; i++) { u8 result[BUF_SIZE]; if (recover_data(rs_repair, i, result, data, in_present, recovery_present)) return 1; if (compare_buffer(result, data[i], "test2 - missing")) return 1; } return 0; } // too many recovery blocks /* THIS OPERATION WAS ALLOWED, WITHOUT ANY WARNING ** I NEED TO CHANGE IT TO NOT BE ALLOWED. template int test3() { const int NUM_IN = 4; const int NUM_REC = 2; // recovery const int LOW_EXPONENT = 0; u8 data[NUM_IN + NUM_REC][BUF_SIZE]; if (generate_data(873945932, data, NUM_IN, NUM_REC, LOW_EXPONENT)) return 1; // loop over missing input blocks for (int missing1 = 0; missing1 < NUM_IN; missing1++) { std::cerr << "Processing " << missing1 << std::endl; std::vector in_present; for (int i = 0; i < NUM_IN; i++) { in_present.push_back(i != missing1); } std::vector recovery_present; for (int i = 0; i < NUM_REC; i++) { recovery_present.push_back(true); } ReedSolomon rs_repair; if (init_repair_rs(rs_repair, in_present, recovery_present, LOW_EXPONENT)) return 1; u8 result[BUF_SIZE]; if (recover_data(rs_repair, 0, result, data, in_present, recovery_present)) return 1; if (compare_buffer(result, data[missing1], "test3 - missing1")) return 1; } return 0; } */ // Check that the correct constants are being used for Par2 //The test pretends there are 10 input blocks ("NUM_IN") and 1 //recovery block ("NUM_REC"), each 1024 bytes long ("BUF_SIZE"). These //are all stored in data[11][BUF_SIZE], with the input blocks //occupying data[0] through data[9] and the recovery block in //data[10]. //The test zeroes out the input blocks and then writes a 1 into the //first location of the first input block, and into the second //location of the second input block, etc. It then generates the //recovery block using many calls to ReedSolomon. When that happens, //those 1s are multiplied by the coefficients for each input block. So //the first location of recovery block holds the coefficient for the //first input block, the second location has the coefficient for the //second input block, etc. Those values are checked against the //expected values passed to the function. template int test4(int *expected_bases) { const int NUM_REC = 1; // recovery const int LOW_EXPONENT = 1; u8 data[NUM_IN + NUM_REC][BUF_SIZE]; int high_exponent = LOW_EXPONENT + NUM_REC - 1; for (int i = 0; i < NUM_IN; i++) { // fill with zeros, for (int k = 0; k < BUF_SIZE; k++) { data[i][k] = (u8)0; } // EXCEPT write a (little endian) 1 in a different place for each file // In the i-th file, it is written into the i-th location data[i][sizeof(utype)*i] = (u8) 1; } // zero recovery for (int j = 0; j < NUM_REC; j++) { for (int k = 0; k < BUF_SIZE; k++) { data[NUM_IN + j][k] = (u8)0; } } ReedSolomon rs_creator; //std::cout << "creator.setinput" << NUM_IN << std::endl; if (!rs_creator.SetInput(NUM_IN, std::cout, std::cerr)) { std::cerr << "rs_creator.SetInput returned false"; return 1; } //std::cout << "creator.setoutput" << LOW_EXPONENT << " " << high_exponent << std::endl; if (!rs_creator.SetOutput(false, LOW_EXPONENT, high_exponent)) { std::cerr << "rs_creator.SetOutput returned false"; return 1; } //std::cout << "creator.compute" << std::endl; if (!rs_creator.Compute(nlSilent, std::cout, std::cerr)) { std::cerr << "rs_creator.Compute returned false"; return 1; } for (int i = 0; i < NUM_IN; i++) { for (int j = 0; j < NUM_REC; j++) { //std::cout << "creator.process " << BUF_SIZE << " " << i << " " << j << std::endl; rs_creator.Process(BUF_SIZE, i, &(data[i][0]), j, &(data[NUM_IN + j][0])); } } // The recovery file has exponent 1 and should // contain each base to the power 1. for (int i = 0; i < NUM_IN; i++) { // read little-endian value utype v = 0; for (size_t byte_index = 0; byte_index < sizeof(utype); byte_index++) { u8 byte = data[NUM_IN+0][sizeof(utype)*i + byte_index]; v |= (((utype)byte) << (byte_index*8)); } int base = v; if (base != expected_bases[i]) { std::cerr << "base at location " << i << " did not match expected." << std::endl; std::cerr << " base = " << base << std::endl; std::cerr << " expected = " << expected_bases[i] << std::endl; return 1; } } return 0; } int main() { if (test1()) { std::cerr << "FAILED: test1(8)" << std::endl; return 1; } if (test2()) { std::cerr << "FAILED: test2(8)" << std::endl; return 1; } // test3 used more parity blocks than missing source blocks. // The code should either work or not allow it. // Probably not allow it. //if (test3()) return 1; std::cout << "finished test 3(8)" << std::endl; // the values for Par1 int expected_bases8[10] = {1,2,3,4,5,6,7,8,9,10}; if (test4(expected_bases8)) { std::cerr << "FAILED: test4(8)" << std::endl; return 1; } return 0; } par2cmdline-turbo-1.4.0/src/utf8.cpp000066400000000000000000000067071514221355600172440ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See https://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2024-2025 Denis // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #include #include #include #include "utf8.h" namespace utf8 { const int MAX_ARGS = 128; const size_t MAX_DIR_PATH = 248; std::wstring_convert> UTF8_CONVERTER; std::wstring Utf8ToWide(const std::string& str) { if (str.empty()) return L""; try { std::wstring wpath = UTF8_CONVERTER.from_bytes(str.c_str()); if (wpath.size() > MAX_DIR_PATH && wpath.find(L"\\\\?\\") == std::wstring::npos && wpath.find(L"\\\\?\\UNC") == std::wstring::npos) { if (std::wcsncmp(wpath.c_str(), L"\\\\", 2) == 0) { wpath = L"\\\\?\\UNC" + wpath; } else { wpath = L"\\\\?\\" + wpath; } } return wpath; } catch (const std::exception& e) { std::cerr << "Failed to convert UTF-8 to wide string: " << e.what() << std::endl; return L""; } } std::string WideToUtf8(const std::wstring& str) { if (str.empty()) return ""; try { return UTF8_CONVERTER.to_bytes(str.c_str()); } catch (const std::exception& e) { std::cerr << "Failed to convert wide to UTF-8 string: " << e.what() << std::endl; return ""; } } WideToUtf8ArgsAdapter::WideToUtf8ArgsAdapter(int argc, wchar_t* wargv[]) noexcept(false) : m_argc(argc) { if (wargv == nullptr) { throw std::invalid_argument("Invalid argument: wargv cannot be nullptr."); } if (m_argc > MAX_ARGS) { std::cerr << "Too many arguments (" << argc << "/" << MAX_ARGS << ").\n" << "Only " << MAX_ARGS << " will be processed." << std::endl; m_argc = MAX_ARGS; } m_argv = new char* [m_argc + 1]; for (int i = 0; i < m_argc; ++i) { if (wargv[i] == nullptr) { std::cerr << "Invalid argument: encountered nullptr in wargv.\n" << "Skipping " << i << "argument." << std::endl; --m_argc; --i; continue; } std::string arg = WideToUtf8(wargv[i]); size_t size = arg.size() + 1; m_argv[i] = new char[size]; std::memcpy(m_argv[i], arg.c_str(), size); } m_argv[m_argc] = nullptr; } const char* const* WideToUtf8ArgsAdapter::GetUtf8Args() const noexcept { return m_argv; } WideToUtf8ArgsAdapter::~WideToUtf8ArgsAdapter() { if (m_argv) { for (size_t i = 0; m_argv[i]; ++i) { delete m_argv[i]; } delete[] m_argv; } } } par2cmdline-turbo-1.4.0/src/utf8.h000066400000000000000000000035321514221355600167020ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See https://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2024-2025 Denis // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __UTF8_H__ #define __UTF8_H__ #include #include namespace utf8 { extern const int MAX_ARGS; extern const size_t MAX_DIR_PATH; extern std::wstring_convert> UTF8_CONVERTER; std::wstring Utf8ToWide(const std::string& str); std::string WideToUtf8(const std::wstring& str); class WideToUtf8ArgsAdapter final { public: WideToUtf8ArgsAdapter(int argc, wchar_t* argv_[]) noexcept(false); const char* const* GetUtf8Args() const noexcept; WideToUtf8ArgsAdapter() = delete; WideToUtf8ArgsAdapter(const WideToUtf8ArgsAdapter&) = delete; WideToUtf8ArgsAdapter(WideToUtf8ArgsAdapter&&) = delete; WideToUtf8ArgsAdapter& operator=(const WideToUtf8ArgsAdapter&) = delete; WideToUtf8ArgsAdapter& operator=(WideToUtf8ArgsAdapter&&) = delete; ~WideToUtf8ArgsAdapter(); private: char** m_argv; int m_argc; }; } #endif // __UTF8_H__ par2cmdline-turbo-1.4.0/src/utf8_test.cpp000066400000000000000000000125361514221355600203000ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See https://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2024 Denis // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #include #include "utf8.h" using namespace utf8; int test1() { std::string emptyString = ""; std::wstring expectedWide = L""; std::wstring actualWide = Utf8ToWide(emptyString); std::string expectedUtf8 = ""; std::string actualUtf8 = WideToUtf8(expectedWide); if (actualWide == expectedWide && actualUtf8 == expectedUtf8) return 0; return 1; } int test2() { std::string asciiString = "Hello, World!"; std::wstring expectedWide = L"Hello, World!"; std::wstring actualWide = Utf8ToWide(asciiString); std::string expectedUtf8 = "Hello, World!"; std::string actualUtf8 = WideToUtf8(expectedWide); if (actualWide == expectedWide && actualUtf8 == expectedUtf8) return 0; return 1; } int test3() { // "Привет, мир!" std::string nonAsciiString = "\xD0\x9F\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82, \xD0\xBC\xD0\xB8\xD1\x80!"; std::wstring expectedWide = L"\x041F\x0440\x0438\x0432\x0435\x0442, \x043C\x0438\x0440!"; std::wstring actualWide = Utf8ToWide(nonAsciiString); std::string expectedUtf8 = "\xD0\x9F\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82, \xD0\xBC\xD0\xB8\xD1\x80!"; std::string actualUtf8 = WideToUtf8(expectedWide); if (actualWide == expectedWide && actualUtf8 == expectedUtf8) return 0; return 1; } int test4() { std::string specialCharsString = "This string has: !@#$%^&*()_+=-`~[]{}:;'<>,.?/"; std::wstring expectedWide = L"This string has: !@#$%^&*()_+=-`~[]{}:;'<>,.?/"; std::wstring actualWide = Utf8ToWide(specialCharsString); std::string expectedUtf8 = "This string has: !@#$%^&*()_+=-`~[]{}:;'<>,.?/"; std::string actualUtf8 = WideToUtf8(expectedWide); if (actualWide == expectedWide && actualUtf8 == expectedUtf8) return 0; return 1; } int test5() { // "Привет! こんにちは世界! 안녕하세요!" std::string multiLangString = "\xD0\x9F\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82! \xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF\xE4\xB8\x96\xE7\x95\x8C! \xEC\x95\x88\xEB\x85\x95\xED\x95\x98\xEC\x84\xB8\xEC\x9A\x94!"; std::wstring expectedWide = L"\x041F\x0440\x0438\x0432\x0435\x0442! \x3053\x3093\x306B\x3061\x306F\x4E16\x754C! \xC548\xB155\xD558\xC138\xC694!"; std::wstring actualWide = Utf8ToWide(multiLangString); std::string expectedUtf8 = "\xD0\x9F\xD1\x80\xD0\xB8\xD0\xB2\xD0\xB5\xD1\x82! \xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1\xE3\x81\xAF\xE4\xB8\x96\xE7\x95\x8C! \xEC\x95\x88\xEB\x85\x95\xED\x95\x98\xEC\x84\xB8\xEC\x9A\x94!"; std::string actualUtf8 = WideToUtf8(expectedWide); if (actualWide == expectedWide && actualUtf8 == expectedUtf8) return 0; return 1; } int test6() { wchar_t* wargv[1] = { nullptr }; WideToUtf8ArgsAdapter adapter(0, wargv); const char* const* utf8Args = adapter.GetUtf8Args(); return nullptr == utf8Args; } int test7() { // L"Привет", L"мир", L"!" wchar_t* wargv[3] = { const_cast(L"\x041F\x0440\x0438\x0432\x0435\x0442"), const_cast(L"\x043C\x0438\x0440"), const_cast(L"!") }; WideToUtf8ArgsAdapter adapter(3, wargv); const char* const* utf8Args = adapter.GetUtf8Args(); for (int i = 0; i < 3; ++i) { if (WideToUtf8(wargv[i]) != std::string(utf8Args[i])) { return 1; } } return 0; } int test8() { wchar_t* wargv[3] = { const_cast(L"arg1"), nullptr, const_cast(L"arg3") }; WideToUtf8ArgsAdapter adapter(3, wargv); const char* const* utf8Args = adapter.GetUtf8Args(); int argc = 3; for (int i = 0; i < argc; ++i) { if (wargv[i] == nullptr) { --i; --argc; continue; } if (WideToUtf8(wargv[i]) != std::string(utf8Args[i])) { return 1; } } return 0; } int main() { if (test1()) { std::cerr << "FAILED: test1" << std::endl; return 1; } if (test2()) { std::cerr << "FAILED: test2" << std::endl; return 1; } if (test3()) { std::cerr << "FAILED: test3" << std::endl; return 1; } if (test4()) { std::cerr << "FAILED: test4" << std::endl; return 1; } if (test5()) { std::cerr << "FAILED: test5" << std::endl; return 1; } if (test6()) { std::cerr << "FAILED: test6" << std::endl; return 1; } if (test7()) { std::cerr << "FAILED: test7" << std::endl; return 1; } if (test8()) { std::cerr << "FAILED: test8" << std::endl; return 1; } std::cout << "SUCCESS: utf8_test complete." << std::endl; return 0; } par2cmdline-turbo-1.4.0/src/verificationhashtable.cpp000066400000000000000000000064161514221355600227110ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif VerificationHashTable::VerificationHashTable(void) { hashmask = 0; hashtable = 0; } VerificationHashTable::~VerificationHashTable(void) { // Destroy the hash table if (hashtable) { for (unsigned int entry=0; entry<=hashmask; entry++) { delete hashtable[entry]; } } delete [] hashtable; } // Allocate the hash table with a reasonable size void VerificationHashTable::SetLimit(u32 limit) { // Pick a good size for the hash table hashmask = 256; while (hashmask < limit && hashmask < 65536) { hashmask <<= 1; } // Allocate and clear the hash table hashtable = new VerificationHashEntry*[hashmask]; memset(hashtable, 0, hashmask * sizeof(hashtable[0])); hashmask--; } // Load data from a verification packet void VerificationHashTable::Load(Par2RepairerSourceFile *sourcefile, u64 blocksize) { VerificationHashEntry *preventry = 0; // Get information from the sourcefile VerificationPacket *verificationpacket = sourcefile->GetVerificationPacket(); u32 blockcount = verificationpacket->BlockCount(); // Iterate through the data blocks for the source file and the verification // entries in the verification packet. std::vector::iterator sourceblocks = sourcefile->SourceBlocks(); const FILEVERIFICATIONENTRY *verificationentry = verificationpacket->VerificationEntry(0); u32 blocknumber = 0; while (blocknumberInsert(&hashtable[entry->Checksum() & hashmask]); // Make the previous entry point forwards to this one if (preventry) { preventry->Next(entry); } preventry = entry; ++blocknumber; ++sourceblocks; ++verificationentry; } } par2cmdline-turbo-1.4.0/src/verificationhashtable.h000066400000000000000000000335511514221355600223560ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __VERIFICATIONHASHTABLE_H__ #define __VERIFICATIONHASHTABLE_H__ class Par2RepairerSourceFile; class VerificationHashTable; // The VerificationHashEntry objects form the nodes of a binary trees stored // in a VerificationHashTable object. // There is one VerificationHashEntry object for each data block in the original // source files. class VerificationHashEntry { public: VerificationHashEntry(Par2RepairerSourceFile *_sourcefile, DataBlock *_datablock, bool _firstblock, const FILEVERIFICATIONENTRY *_verificationentry) : sourcefile(_sourcefile) , datablock(_datablock) , firstblock(_firstblock) , crc(_verificationentry->crc) , hash(_verificationentry->hash) , left(0) , right(0) , same(0) , next(0) { } ~VerificationHashEntry(void) { // problem: may cause stack overflow due to recursion. // this happens if there are a lot of nodes with the same crc & hash // possible solution: use a loop // I still don't like the idea of two recursions (left and right), // AFAIK only one can be optimized by the compiler // delete all nodes with same crc and hash VerificationHashEntry *nextSame = same; while(0 != nextSame) { VerificationHashEntry *const currSame = nextSame; nextSame = currSame->same; // prevent currSame from recursive delete currSame->same = 0; currSame->left = 0; currSame->right = 0; delete currSame; } delete left; delete right; } // Insert the current object is a child of the specified parent void Insert(VerificationHashEntry **parent); // Search (starting at the specified parent) for an object with a matching crc static const VerificationHashEntry* Search(const VerificationHashEntry *entry, u32 crc); // Search (starting at the specified parent) for an object with a matching hash static const VerificationHashEntry* Search(const VerificationHashEntry *entry, const MD5Hash &hash); // Comparison operators for searching bool operator <(const VerificationHashEntry &r) const { return crc < r.crc || (crc == r.crc && hash < r.hash); } bool operator >(const VerificationHashEntry &r) const { return crc > r.crc || (crc == r.crc && hash > r.hash); } bool operator ==(const VerificationHashEntry &r) const { return crc == r.crc && hash == r.hash; } bool operator <=(const VerificationHashEntry &r) const {return !operator>(r);} bool operator >=(const VerificationHashEntry &r) const {return !operator<(r);} bool operator !=(const VerificationHashEntry &r) const {return !operator==(r);} // Data Par2RepairerSourceFile* SourceFile(void) const {return sourcefile;} const DataBlock* GetDataBlock(void) const {return datablock;} bool FirstBlock(void) const {return firstblock;} // Set/Check the associated datablock void SetBlock(DiskFile *diskfile, u64 offset) const; bool IsSet(void) const; u32 Checksum(void) const {return crc;} const MD5Hash& Hash(void) const {return hash;} VerificationHashEntry* Same(void) const {return same;} VerificationHashEntry* Next(void) const {return next;} void Next(VerificationHashEntry *_next) {next = _next;} protected: // Data Par2RepairerSourceFile *sourcefile; DataBlock *datablock; bool firstblock; u32 crc; MD5Hash hash; protected: // Binary tree VerificationHashEntry *left; VerificationHashEntry *right; // Linked list of entries with the same crc and hash VerificationHashEntry *same; // Linked list of entries in sequence for same file VerificationHashEntry *next; }; inline void VerificationHashEntry::SetBlock(DiskFile *diskfile, u64 offset) const { datablock->SetLocation(diskfile, offset); } inline bool VerificationHashEntry::IsSet(void) const { return datablock->IsSet(); } // Insert a new entry in the tree inline void VerificationHashEntry::Insert(VerificationHashEntry **parent) { while (*parent) { if (**parent < *this) { parent = &(*parent)->right; } else if (**parent > *this) { parent = &(*parent)->left; } else { break; } } while (*parent) { parent = &(*parent)->same; } *parent = this; } // Search the tree for an entry with the correct crc inline const VerificationHashEntry* VerificationHashEntry::Search(const VerificationHashEntry *entry, u32 crc) { while (entry) { if (entry->crc < crc) { entry = entry->right; } else if (entry->crc > crc) { entry = entry->left; } else { break; } } return entry; } // Search the tree for an entry with the correct hash inline const VerificationHashEntry* VerificationHashEntry::Search(const VerificationHashEntry *entry, const MD5Hash &hash) { u32 crc = entry->crc; while (entry) { if (entry->crc < crc || (entry->crc == crc && entry->hash < hash)) { entry = entry->right; } else if (entry->crc > crc || (entry->crc == crc && entry->hash > hash)) { entry = entry->left; } else { break; } } return entry; } // The VerificationHashTable object contains all of the VerificationHashEntry objects // and is used to find matches for blocks of data in a target file that is being // scanned. // It is initialised by loading data from all available verification packets for the // source files. class VerificationHashTable { public: VerificationHashTable(void); ~VerificationHashTable(void); void SetLimit(u32 limit); // Load the data from the verification packet void Load(Par2RepairerSourceFile *sourcefile, u64 blocksize); // Try to find a match. // nextentry - The entry which we expect to find next. This is used // when a sequence of matches are found. // sourcefile - Which source file we would prefer to find a match for // if there are more than one possible match (with the // same crc and hash). // checksummer - Provides the crc and hash values being tested. // duplicate - Set on return if the match would have been valid except // for the fact that the block has already been found. const VerificationHashEntry* FindMatch(const VerificationHashEntry *nextentry, const Par2RepairerSourceFile *sourcefile, FileCheckSummer &checksummer, bool &duplicate) const; // Look up based on the block crc const VerificationHashEntry* Lookup(u32 crc) const; // Continue lookup based on the block hash const VerificationHashEntry* Lookup(const VerificationHashEntry *entry, const MD5Hash &hash); protected: VerificationHashEntry **hashtable; unsigned int hashmask; }; // Search for an entry with the specified crc inline const VerificationHashEntry* VerificationHashTable::Lookup(u32 crc) const { if (hashmask) { return VerificationHashEntry::Search(hashtable[crc & hashmask], crc); } return 0; } // Search for an entry with the specified hash inline const VerificationHashEntry* VerificationHashTable::Lookup(const VerificationHashEntry *entry, const MD5Hash &hash) { return VerificationHashEntry::Search(entry, hash); } inline const VerificationHashEntry* VerificationHashTable::FindMatch(const VerificationHashEntry *suggestedentry, const Par2RepairerSourceFile *sourcefile, FileCheckSummer &checksummer, bool &duplicate) const { duplicate = false; // Get the current checksum from the checksummer u32 crc = checksummer.Checksum(); MD5Hash hash; bool havehash = false; // Do we know what the next entry should be if (0 != suggestedentry) { // Is the suggested match supposed to be the last one in the file if (suggestedentry->Next() == 0) { // How long should the last block be u64 length = suggestedentry->GetDataBlock()->GetLength(); // Get a short checksum from the checksummer u32 checksum = checksummer.ShortChecksum(length); // Is the checksum correct if (checksum == suggestedentry->Checksum()) { havehash = true; // Get a short hash from the checksummer hash = checksummer.ShortHash(length); // If the hash matches as well, then return it if (hash == suggestedentry->Hash()) { return suggestedentry; } } } // If the suggested entry has not already been found, compare the checksum else if (!suggestedentry->IsSet() && suggestedentry->Checksum() == crc) { havehash = true; // Get the hash value from the checksummer hash = checksummer.Hash(); // If the hash value matches, then return it. if (hash == suggestedentry->Hash()) { return suggestedentry; } } } // Look for other possible matches for the checksum const VerificationHashEntry *nextentry = VerificationHashEntry::Search(hashtable[crc & hashmask], crc); if (0 == nextentry) return 0; // If we don't have the hash yet, get it if (!havehash) { hash = checksummer.Hash(); } // Look for an entry with a matching hash nextentry = VerificationHashEntry::Search(nextentry, hash); if (0 == nextentry) return 0; // Is there one match with the same checksum and hash, or many if (nextentry->Same() == 0) { // If the match is for a block that is part of a target file // for which we already have a complete match, then don't // return it. if (nextentry->SourceFile()->GetCompleteFile() != 0) { duplicate = true; return 0; } // If we are close to the end of the file and the block // length is wrong, don't return it because it is an // invalid match if (checksummer.ShortBlock() && checksummer.BlockLength() != nextentry->GetDataBlock()->GetLength()) { return 0; } // If the match was at the start of the file and it is the first // block for a target file, then return it. if (nextentry->FirstBlock() && checksummer.Offset() == 0) { return nextentry; } // Was this match actually the one which had originally been suggested // but which has presumably already been found if (nextentry == suggestedentry) { // Was the existing match in the same file as the new match if (nextentry->IsSet() && nextentry->GetDataBlock()->GetDiskFile() == checksummer.GetDiskFile()) { // Yes. Don't return it duplicate = true; return 0; } else { // No, it was in a different file. Return it. // This ensures that we can find a perfect match for a target // file even if some of the blocks had already been found // in a different file. return nextentry; } } else { // return it only if it has not already been used if (nextentry->IsSet()) { duplicate = true; return 0; } return nextentry; } } // Do we prefer to match entries for a particular source file if (0 != sourcefile) { const VerificationHashEntry *currententry = nextentry; nextentry = 0; // We don't want entries for the wrong source file, ones that // have already been matched, or ones that are the wrong length while (currententry && (currententry->SourceFile() != sourcefile || currententry->IsSet() || (checksummer.ShortBlock() && checksummer.BlockLength() != currententry->GetDataBlock()->GetLength()) ) ) { // If we found an unused entry (which was presumably for the wrong // source file) remember it (providing it is the correct length). if (0 == nextentry && !(currententry->IsSet() || (checksummer.ShortBlock() && checksummer.BlockLength() != currententry->GetDataBlock()->GetLength()) ) ) { nextentry = currententry; } currententry = currententry->Same(); } // If we found an unused entry for the source file we want, return it if (0 != currententry) return currententry; } // Check for an unused entry which is the correct length while (nextentry && (nextentry->IsSet() || (checksummer.ShortBlock() && checksummer.BlockLength() != nextentry->GetDataBlock()->GetLength()) ) ) { nextentry = nextentry->Same(); } // Return what we have found /* if (nextentry == 0) { duplicate = true; } */ return nextentry; } #endif // __VERIFICATIONHASHTABLE_H__ par2cmdline-turbo-1.4.0/src/verificationpacket.cpp000066400000000000000000000065751514221355600222330ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "libpar2internal.h" #ifdef _MSC_VER #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #endif // Create a packet large enough for the specified number of blocks bool VerificationPacket::Create(u32 _blockcount) { blockcount = _blockcount; // Allocate a packet large enough to hold the required number of verification entries. FILEVERIFICATIONPACKET *packet = (FILEVERIFICATIONPACKET*)AllocatePacket(sizeof(FILEVERIFICATIONPACKET) + blockcount * sizeof(FILEVERIFICATIONENTRY)); // Record everything we know in the packet. packet->header.magic = packet_magic; packet->header.length = packetlength; //packet->header.hash; // Not known yet //packet->header.setid; // Not known yet packet->header.type = fileverificationpacket_type; //packet->fileid; // Not known yet //packet->entries; // Not known yet return true; } void VerificationPacket::FileId(const MD5Hash &fileid) { assert(packetdata != 0); // Store the fileid in the packet. ((FILEVERIFICATIONPACKET*)packetdata)->fileid = fileid; } void VerificationPacket::SetBlockHashAndCRC(u32 blocknumber, const MD5Hash &hash, u32 crc) { assert(packetdata != 0); assert(blocknumber < blockcount); // Store the block hash and block crc in the packet. FILEVERIFICATIONENTRY &entry = ((FILEVERIFICATIONPACKET*)packetdata)->entries[blocknumber]; entry.hash = hash; entry.crc = crc; } bool VerificationPacket::Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header) { // Is the packet large enough if (header.length <= sizeof(FILEVERIFICATIONPACKET)) { return false; } // Does the packet have a whole number of verification records if (0 < ((header.length - sizeof(FILEVERIFICATIONPACKET)) % sizeof(FILEVERIFICATIONENTRY))) { return false; } // Is the packet too large if (header.length > sizeof(FILEVERIFICATIONPACKET) + 32768 * sizeof(FILEVERIFICATIONENTRY)) { return false; } // Allocate the packet FILEVERIFICATIONPACKET *packet = (FILEVERIFICATIONPACKET*)AllocatePacket((size_t)header.length); packet->header = header; // How many blocks are there blockcount = (u32)((((FILEVERIFICATIONPACKET*)packetdata)->header.length - sizeof(FILEVERIFICATIONPACKET)) / sizeof(FILEVERIFICATIONENTRY)); // Read the rest of the packet return diskfile->Read(offset + sizeof(PACKET_HEADER), &packet->fileid, (size_t)packet->header.length - sizeof(PACKET_HEADER)); } par2cmdline-turbo-1.4.0/src/verificationpacket.h000066400000000000000000000051231514221355600216640ustar00rootroot00000000000000// This file is part of par2cmdline (a PAR 2.0 compatible file verification and // repair tool). See http://parchive.sourceforge.net for details of PAR 2.0. // // Copyright (c) 2003 Peter Brian Clements // Copyright (c) 2019 Michael D. Nahas // // par2cmdline is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // par2cmdline is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef __VERIFICATIONPACKET_H__ #define __VERIFICATIONPACKET_H__ // The file verification packet stores details that allow individual blocks // of valid data within a damaged file to be identified. class VerificationPacket : public CriticalPacket { public: // Construct the packet VerificationPacket(void) : blockcount(0) { } ~VerificationPacket(void) {} // Create a packet large enough for the specified number of blocks bool Create(u32 blockcount); // Set the fileid (computed from the file description packet). void FileId(const MD5Hash &fileid); // Set the block hash and block crc for a specific data block. void SetBlockHashAndCRC(u32 blocknumber, const MD5Hash &hash, u32 crc); // Load a verification packet from a specified file bool Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header); // Get the FileId const MD5Hash& FileId(void) const; // Get the block count u32 BlockCount(void) const; // Get a specific verification entry const FILEVERIFICATIONENTRY* VerificationEntry(u32 blocknumber) const; protected: u32 blockcount; }; inline const MD5Hash& VerificationPacket::FileId(void) const { assert(packetdata != 0); return ((FILEVERIFICATIONPACKET*)packetdata)->fileid; } inline u32 VerificationPacket::BlockCount(void) const { assert(packetdata != 0); return blockcount; } inline const FILEVERIFICATIONENTRY* VerificationPacket::VerificationEntry(u32 blocknumber) const { assert(packetdata != 0); // return &((FILEVERIFICATIONPACKET*)packetdata)->entries()[blocknumber]; return &((FILEVERIFICATIONPACKET*)packetdata)->entries[blocknumber]; } #endif // __VERIFICATIONPACKET_H__ par2cmdline-turbo-1.4.0/stamp-h.in000066400000000000000000000000001514221355600167400ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/tests/000077500000000000000000000000001514221355600162135ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/tests/100blocks.tar.gz000066400000000000000000000031441514221355600210420ustar00rootroot00000000000000j:Q~Te{} @x23.qB{F EOo/eSu5vW؆Xǹ-v]9K뾛P>p ] YSW2~wߛikp!qDA@AqDA@AqDA@AqDA@AqDA@AqDA@AqDA@Av^_[;?Ǿ0~8eC}tu]}8=ӥu?{@AqDaWe[~ꏱ_: C釱s[6eltS8=ӥu?{A@AqDA@AqDA@AqDA@AqDA@AqDA@AqDA@AqDA@AqDA@AqDA@AqDA@AqDA@AqDaWf~7Ixpar2cmdline-turbo-1.4.0/tests/bug128-parfiles.tar.gz000066400000000000000000000631701514221355600221640ustar00rootroot00000000000000eTpRZD}iAQrJIi:nDTJZٯx{Wˬͳ\u?um{F -3¿``qⓕ߀ vv ++,,L4'+k-;C_AZ\wsmJ}@dq'+yqC>ڷOȄ lhUǿbRa1@ =4cb2z|?G)mmL~Bu5ʚQWZ30A6.0nd[eOfº2g=?`[dXY)X(L21|̨,,Ow3hDgT]^R'lqKa 3k1}c6gݥ +̆GI3SٴY~7Kw*"sAT(#xe'+m(J<ۺ2袆a;b ڒrM ?cMCrNʼnk KqAߛ#pFp-M;#AX)bN*³4}mQ!//7,ni`J!/嚔1,F nr~#oL4W3ۙW~tN^g#W IҪH(HY4TrLgǪM3O7LI.Č4( .Q7I tIaQӻ"4-76ݛpVUgV.nִNS?'e/'SaJqԲANY'W4CRϷ[H<,vOš̭(Q+(cWw'6;?aF7Х=P'S?*aY6847rsLa;Er"P@}C o8$^6 Lm Kwx z|2 fiݲV' @wȎkbgyӺ fr~GGWG,̱ͨW&'ȏRji]|ߨEU}ۦo&[,#qi9˜w-V_ZrzS;pǧ\~ OE@n}Z`~'a#nQچNZQi>ozHI+uLTk KeNF+:jӸ4^]Uۜjx ?ϖ;u(HmuJ2T?^ Lԧ]WcWK ' :rWߐzl`ę29^du[A\B/7lDXVv8`Qz Z7/F:7ӷ{M/h匁jiGxNk Nv /%m8~JNȾ<^|dn+LW8]HpY'(>h1Lhïhl΂z.yЎRLG0t2 G_ѐ-[uϮmgkc e:Mt#3c;E?uva\ٲUm~ofUYmr=\n$FJVΦuMxGưr/8u[Ai<B]j仾 o? P+>BxJFi:: ~}1`ΟŞՖDK-f8Kצ1«$4eIbVQ5Rr-=j}=IӘ2U1FBH]so~7{܀ew*AIJV쁕 `fogy-3:L^U8्XZ-~=Aʧj7%pCe @1J^dD#lkn$zyj9: z_EO5>3~Xdþo殜Wtt.1b(@6(&zI]qAIy/ܾo4rz\HW?@cf¸YDCWET=-xl>r秩k{Rn1:TbeNl:l\Cakj)q:f~KOꯠo+ŗV4n?p)Ԝ_OfH ?P-2MŒY[r`tոM }\>cϹhٮ&w#8xbLJ*y{ tѡ8Ŀ,a| n(h/Fä_Wޛaq^ :og.0*zڧ=YJt1]y{n> i Uz]p`{4nN7n=:&N[x.CHV/yCes̀<)=ܸ[b+ɏ£1u$(Mm8XZY 1Q<, lc+^v޻۠6*"<(6kT=X(XebpHtȣ G :}0x*nh(_ؔƗwϳKlo/7ݾ!*w&P&7[G>jDkQ?kOۉy@jI#Xi]sk ]s_}%ѓD-skv)ϴ$pm1444wff݄.Df l.jźGy=OvC  > ioQ `U}Ζfq3gE}#\ <4$dcCC 4$doCC 4$А 4$А 4$А@C !DCА@C 4$qCC 4$А 4$А@C А@CBV:hH!!nhH!hH! @!hH!mhH!DА@C 4$А@C k>!nhH!!+hH!D}ohH!hH!D?F„cl-mE6ca6AZI6::H?8f8'ݶfS/0=ϵc4ӘTmׁX@xX>Zr eqJg\})ӂ)2BcEu=6YIDn_awK?м2E%]%` _teU i^˫.c~i #ʧ =ՑE.աd~&[#09uIZaZ@QߥG e;||c!BԫoO32ݕix>~[gi$ꚤ;@^^&Ϙo6PB-"t???C@!m???l ڭss9e /Ϳ%鹠Uqlۋm 8CU~`^˚īuպܚHs@/vsxϪ2/^Q嚂*O<꫘(KLqqwq_W:-r-E(_dd hvf;B2t0N&N|:QAO73PBJixLP~̋%hrʽd/LԝWYywht.N%&mX"}HR:0|k{_XI{יn'aQj8p 4dАAC 4d҆ 2! 2hȠ! 64d 2hȠ! 22! 2 jCC 4dАAC 4dd 2!lhȠ! 2! 2hȠ! 2} XX[YsD/|cU\%F>x Im\Uy&{g-KJT3Э *5Yz-^cͳ^fǻUT2pB+RSYq4\ӛ p|vFexT__deF77tb<-q~OBꃫ)q/apO谊Yݘv\+™2*RRXcO[6E:J[|dFLZֳ%3a||L ʳOSZg!㍰#^A'$iz AA_d}C_wC_Aao迠Ґ6_AyA_duC_AE~6Y鿨nE־s7wsAN`DƆJl8Z|)n~yCVDbub]uH/ǀPWNaRtx&r:#h>ֶy0#H=׏a܍/t2rO훃{*+;yx$;*qhۼ46hErQWD|8*LoW =h\h\wi?;3 O[W,r/Eieb4ϴĎų3fHg8?dz:B}-"xd͛M3!|XĀ5qF;LA nǺ]4U/迠 /4 _uC_Alw_A_dnC_Ԇ /迠 /^_uCYA_{C_ABNqlYݲ mAiG)jeYvl&7}tjV;"ιQ ]ҊbrKw-tiZ /NPn$% k{hz\43P6n*o%n2,&3znށvܩ%o3)6hn#T)uOQvRfb3(cav֓'']絊;U~A[R],_bxSfnݱvʿH'8;ЖScf{3 #lϖ w]DGU,,,G"9$7Ь 4+Ь 4+Ь@B74+Ь@B~74+Ь@ 4+f iC 4+Ь 4+Ь@BV74+Ь@ 4+gfE?sW5"鹠ʵ)80=b[\IBw"vk >fg;ڔsCstiœEmu*6u2=&lVj=%͔v=ThIQ@IZx,y oT013;h)nE_,wvz/ߵ!G7O'z I4TdcCK-TdoCK-TR -TR -TRAK-JCRAK-TqCKTBhtؽpL_uZHz.<6i4'>NOLjGv1ذseӮ4IT}g?Whm9ϴAx{'h!$C &z&NgKc\ LJ\z\gK'l^+ Ҽ55}?,/.+rNj- hY|szZY)ַ2ٸtiƴEYڎ=1itʥ 6RN6JܿE;x5:T:q$d?k4kLկ-$kkM/IB\ ttO}= Z (XCd|z$QVV4cjhzL1DŽzLw -TRAK-T† Z*򺡥 Z*h 6T껇 Z*h Z*2 Z* jCK-YhP+5]/yMks"M <_j0͛D>'#lh9O4~i])N+>gTS[Ubj(WQ+XCz6gb4f:Ռ)Sw!$#R!Xt_o?oߝ\|s?6bWVci|evw6iI9TJء=]8俋oJ꟩:8$o#*^ya"=4uӠFz'2UL))>yDa~:]ߧO] ,_|K. &)9 ꍾ4dwC =&Yk*DŽzLDŽl1DŽzLDŽzL1DŽӿcB YЬ@ 4+?lج˱?eBO[.|A>ltw'q bd3vuGVsHƜ,ק,ܸYF|FS-3rt0  %c Q %"$CHuCALJr%BDixT""w}> \9~Vk}[uoFtopf7 Ho%p욪_0[2 {'4 I"toxk to{ÿto{@goнMOڠ{@7 4+YS6hV@ЬflYbWk'-эCdsӁ4S3?#%=**o1-/(ZSfH!f `gAH{sks5FX'tMg-n&un*>#[EI 7yQ- r;uYy}#!-̪ &jt%lx 'f6s1ܒق̮\.?A%I&VFB'=cT"Zғ^k?>}x鈥#/}V)Ap+!rڮ$Βڃg#Zwl Q((oH݉t:\r8]J to{@'mн nн to{ݛݮ to{@6@7=j tox{ ۠Y ^N@Ь5+𻿳s|xq uyNe+b%rlX8?2`v&!Ag2 y~o'X;h=ST|!Hf{d@Q3;K,<;#0SQQ>݃fMjE3$& :V;U+Ghyr XET*̷ %ދ&-)TFKv8B7? W'V'&&vv("[Juw>(XlGFhO`Βf<ن4-\-[wRt$Sc|{CFo"u8?%ߠ{@woн to{@7 7н to ?voY4+Y ݠY hV@={f4+z->L7w:s呌;wWբr՟zs*RYS N{_u9 bڹ)q /x2?f~ܺt TYdwJ6ԋDYzʹT|l!9#B-r6L>Ϋɫ)2wvɳxK W/F"E4S}'͉ەCSR})P$m\ɬSYmB6f #m.F(._wIu~N#=1IpRRs=NRF>8o.ʝ,VjF՛5#)VYc bPxY/Tn0>ʌf ) dHto toa to{^ݠ{@7 4+Y hV@=af4+uf4+Y hVf~$R]$[\&gQyhϊp-* 1/]S&erO"~!}Dz=;c^K"^%L:xsýd?%ـMjm5ɍbyn7FE/v`r-M[T yDW^MnehEqJc0_wI1sdsҊe6d'YYh'L"ȎuΏxC/'&[43;Z˹#$J`dQ:9dEߊ#mQ_߅T|>a*ANg4tH?N&J nн᭞ to{@7нYto{ݠ{ 7ծ to{[O@7hV@Ь4+Y hV@YzNC ^)g|||4uԵt\ 'Nt\2{oQ+7H=]SWMSOkF񻶖^iji (jV:?^^~ӟC0F?Zِ~VNwt)r3sYT1U>NʊA! ȸ,@ve~jq?~@Ŝ!dvg 3jzUo̸|`hs2]W_$0Eܙ!ʎƩa Tnj Haʹ a; Vcj ՘ƋJ3YNwt>-;d1R0ˑ:{!3,Ffzp q_:tNwGfA9+zItđmA+BƇq-f(-rh󣾈B3zcGaj%Y39avԥ̢hJM)hJAS6hJAS RД RДnД4)MAS R=iMm2oijEH}Odhy*ylCJlTBriۥKF R mHiFv6&=!/w_&+ke5 v1RoJ=BW~v6Ĵp'^VF֔6lzR"H-ǓPgY:bY+u1>BM3Jv|)Y2#4b7~x,wJFu=Ǎ帮u][B +I5dQ&qzd*՜(6E̯XDIIX30c|bq_B_qh~RAS RAS RДnД4)M)~wS o]ډ7;jjszML`3tV$L^l A8 >3[څXVNvQFПA&ّh\ʢECpҝrrO@"lZ_ɝkFڰ9b;$d-g)s Y15MܻUnS035 m Ӝ.I&@tХ.tKד6A_7AtХ.ݯAS6hJV+ RД4)M)hJ RДnДgRy%9{utF -Vnn H%rF9%+/7(̘4{?(ΕKPyrGj%*lǚyf'Rbz2ی\ƅdmy8M[M0Ճ3؄DK [:| Qnv4BVEWI!Lltf*@ű)َ,x7#[IIŕ+JL>FOX>n$w<"{7Vz]IEx YHj2g=AY6 M  %Lk '#îpҚ]:ݠK]:^kAtХ tХ tХ.t~]:AtХ.ݯ<.:HҽI9}&#}f|*IY*hJ #[)}څȔ)9僻ׯNu !k-hfޤ5T~Q=2F8b7-ɾ^sϑ;K 긯 ZMl*\m™3_zAh4v)Ƶ>&\XfQngexu q"SHCq9GZ6X;g[[ÿӿ% ڪ;S*Y.=,Ȯ+G\Pr<2gϲnI|1/ҏ3.izf'%-&,۠,hgA;7hgA; YA; Y΂vz,hgW'C*;=Ea>Ӓ> Aq+FQѼ$i;!c g8wvv}Kn/&kͳ:JW$6l_Ds7JEG?Qn@_v$,3\bg2j2j":q&*JTn%Lil;_td'Ri>\⟌ެ6^m2jEmMg?KuigW̮8q߼_'q1+s/TI}T(„U(gȥbV+Bs]DgsbPeFvo΂v?l΂vū,hgA;6^ig"v:1νk55ŸqzMLt;jQVvIEC#2saRss["N\b1y1#nFZLZrf^Pok9Ch%q,&VV]+w~6.Mq5;פS}BM(#Wxj.a[U).4U~2!OfIzR]Cѭ WEFgi5;Q5+5+zz!읝s45.dnZo 9R(AS^TEfFѬhVo26j&wqN< ',hgA; YIů,hgA; YA;w,hgA; Y Y΂vVڠ,^jgg%Nia Ej 9ݫdQѓbŻׅJZE]]Jjg>ُ$! (:<Ł5wz{ڏ yP)l;Qʨv"htښޒUߥS~#wb:7a9O1CyY.]@0>;x3a$̉ ۿ}> a֋gy)5u7(` H*]nLLDo}W'ofJ|#)ofd[ڣȜ|>>AmpM臭n qd"bvn΂v,hg Y Y΂v~,hgA; Y΂voW%k-KC='eLFj̅ 2*x,ݠ0S)&o=v =[p Y΂vm΂vA; Y΂v,hg ngP5vsš2=Ӛ8x88!(R|IH?L8#b߈4 f*DoM ApQ@H0 lj=Ĥ3}~$!C<8dN$-qcYg҂X/s7fKp9.aJ”s= ԽQ'j0Pk̠3݌k">G@ jj^o5a5% [[fe]YW^:̘tM':U4aS=!ҭR[{2 Fe$WEjyz.Dv熌A; Y΂v56hgA;kn΂v,hgݿv,hgA;6hgA; Yjvej Omd"fܕPS\f1b]0#(9.dFܣ7,!8DpḊ֧ט<UHAy hΜxLS/DS#x9<7NDSB$G壷x%N-Y-]oӑY&یvn΂v,hg Y Y΂v~,hgA; Y΂v5K%Z#j<3ɃgF&23U6N%"o'57y_Ǫ!HhQV0fSkjF:RFIť3Kby46dNF k+EU}ˉt< E"s w*Ys{sNNwvo8>ȳ}F\tGh!w=_O7nt?5H@K/(i5k5Fb?-sO$[L D'=u!ZnM@w3@t+SST&Pm;m!"W ;Z}nA;;6hgA; YܷA; Y΂vo΂v,hgA; Y5RYEA6VU=ӒA$ȄaFg 4*B3e2Wu )c\wV1~ 5/jr|IDa :9(zz2w2BL!slj?HIYeP)sj+>~2%!VVؚ.՗ ?i4{@'hɨ3#Rqd&+N4B^< =0ɷ)b1 .-4wN?Pf>(IP>2Jq)d"{I}9q23]|C0N%/g9^Ⱦqun YCv,hgA; YܺA; Y΂v-,cB{~~sv)μό3YD*7:F)f#f*7qZ/ak*xazs%a|5PA; Y΂v56hgA;kn΂v,hgݿv,hgA;6hgA; Yjvv3;7+2r $73g$-23);[7[J b׊aINEuDu>^hlFsw}ϡGU|N`@>GJ1w*TRs|Vn_0.;SVԢʃ\X#G܃oauQ_FNK)ZrKNY!D3b0Ni a<1xkג߁8!,ΙtfvƟ#IwvQF&-$0{ #%"H]~ydy45F ?Ui =\΁ʰVwv,hgA;khl΂vl΂v7hgA; Y΂vvΥ^>9^ H0:X9~(ji+lzc@9^>7;:~8p復'?X-S[sO~k^:Zz:<:zz:zNS[WWGA!][6 =x}|X}ыI㗊/A'R4{F)?x0LYfN+&󦖪a`R}vg/<Ԃx{  fmc lK=o_s6-hmI&n$? 'M$6 Z5%+M12RGqaa M#ȍ1Kxg>q'88'0RaW1!2[v\เ'f{I6ZNmNN+A"j]sff7q)n+~k&"CRwlNJZf+>)Sv~uٙwlВo%-9nߠ%-9h hab^c@oA:@ gyL)x*19ϾJ|6{9. _%|]a]anIҍ^UL1bnWFiy^##3"C/),"Tc,,nǙq҅٪v5[EUQ7 #/(B0RXFNJM&fC/2BB >L6ȥcۅ9ZpZܵXswns&;D*VGwAԑYgDdicǺu o{pEcB;z e;;pУ zУ%oУp%-9h Zrï%G;|ז-*7i8xf.2WjymJfqUO֒K%OFARfױ2a &0kwuʮ!͵mN+WE]81"hLX `P]n8MIMI͏\#U8=y*;nFfo!zя»BBjG(q-9I| &BSWO᧴,CDv8ařu`8x5W$' oٳ+WYk}{F/A<<LՙzGMQI`6q%M)FL6y3O~+| rnZ[aDX#w6|,F9xdUCar7,vwvI2|,́ሜr3aZ?[lGf1DcEL\Z;Dn@?/dݝyzؚww~=X#:J/_Dwb8JF&'a)6?mҔ{=4ݲf|F rv]&3L%nGz֔D=JԣD=ʡ%QˆzGzG7ԣD=JԣD=Jԣ{DE g$+`\bV21 Y4XwzBIؙ@@:#5y%%o(&M$[jޟSzhj%\ŬHBr)$%#%u85,s5ӄ J+=L ~Zh]ZF-R)꨹MST|#&>hM d5R w p!=5!'19V&[J-1#h3`ҚPరV`DeTS_ pu͌jQnjڢPP6C=ߣ߃w?ha)1Yhc2$'30=?C as/s553m ]z `ov_5` ŲnoUxJṈqԎLN;%KcƴT0$&KNN>sCG:II΂rt%ҟCke4{y1FD8{zg$>9D4? !{$iYjYZ<oi6K"iۨEԗ?Ó(QWe_ YZ0֧qQ{tM01cc#jx׆Cw'6C=c Po8mùǸT)dq+gssELTDRMLjKx| <я Hs'N ce=/ }*ds9?]eEdgӞM;5kBaWzp3\nY@ܱ&w&|\6u8%ݻŴR;҈f2AuL=~UlXGj:BfPm mn.I]25 d}oklN9E4»g{ i`8~4ey$ ơ'jxۆ9{7\6C=C=:wo׍x,AIDA2FݛdH;imp,,swի Eˬ9y" T($R'"e<}⪴[(uIjS pէdE]>ь.VVu9ғ5k0R=bg$<ӳ뫖 rUcqOKCܴaeⷥu!Z7_2EDʁԼ|GE~5VJF3xujLLJuZFgznV z\'y /gRclns FZɦ;K&'C Od 5pl>TyfՀw5RgTMD;ѣ<k͆UZ2-E@20۱D? ]j W*ˉf-ـ=k2fRj۩Ab ?}h)䶍̘%o=Y!KT x1uf?k32`fHjNSS;M 8pᵣe$q e u:!2g~6;R.ㅜtBcQ*̆]rysy;b;bUG-9~G~ҳBD7 /:̭ԙLm>~ m3fa6"&c j {Ϣ&13w*5 ){q^>wP2g#R$GF:&b3K 02dܶXӱ<̬DYmw֫tJd,Ԏ 4/hsh̍170n1d@5HWs]7rOା4Ce%01?6pъ5hdȂ82'_Pbkω0@ɪz U$gz 5%RqD#Pfn[}i _ ׹jc\3mTbftb%;5ӈɵ8E56mMZ"1 <3st:7XR3[,K vL5d2bt@ڪ3о7Ei"m][p S5Z m\>!||*7 5:-ZrC>f#\4pD=c Po8mw5y7-)2XwygyצrփddajIuu?eʥ8uq+&/V\0 R9zޏue) F ;M`r{̊^>}k0SY?<ڡ/q#G?:tX.!qcOҪk[ӆ̝9MD_K/b6l+8(CK }lumu[?WLl`Ly2C͔Dŭ݀/*r~M9ϕ>>-pkR0FR eye5<φCgC OcPP{{{6 =CKZf]3ûԖqaN3\lIƷUJteJBvC?#*_4n8'F#WZC8I@jfM3/nıSֆ{{7CPz{74߃,kSځ9rϬi#GUdޕcj4! P gW PZSbaٝ/po4&Zz(ɶc, 7_@m[V"R "1X󌇑.K8l 5wPro.]U+vط|cv'&ٔh_1O[.xH7Ry"tT_?OdȱG;YwˏyT&$ԩTmO|WlO!q8vvv)5ިד>NG{# +w.%QNAjN;I}+1T{C1pMq{c#z#'L5q Řb6*&"} ߗ齶φ8-ww;ӊ]ODҭgS._5]$hx.4< υw'4<m5<7AOn߃hK.. {f>w#KzmSw ׎%A GOg8Ƹ#̰YiҸQ{XIuSl,iqԌGFnJz_EuT)o:P: :?VTjO8􄓺:HG+3161Ҋ\y]]:Teo$MxMx}Ed flmDF_m?CJ1MsWwd~MpЍJr1%HA3ȵq vu#0U+Ca~a-bD=!-'=A{SPz߃7AOYo߃Mt2bES7{}g3]t~έ C[яѯF&Մ$|C \*UTt\Dd𗖘 y3Y{QVYYJpW̾gkčM˪j" ib2EzЧ%[ژ47~?RO'1aO7#Ogh5P:kܒܒ{{"޵͊I)Si)M;}t̒YCt3Ff"DG+/!}KXQ#ɥRĪ6@Ȼ OAsa9 Oy OA{߃rݠ=) {uZI>, ~!YgЩ~-yf:n W.d ACsϲRsMQQ+r12Ǻγ*HeC^S;i2Snwva hh6${ "" xh{j` Vm~S> 6|,u1r:u}ncST]_w|c."4}_! .\P,par2cmdline-turbo-1.4.0/tests/bug44.tar.gz000066400000000000000000031276651514221355600203130ustar00rootroot00000000000000y8mp,eWֈCD{eP Bd ɞ!ɒ-]}_*D)y9;{t꾮~3|<~|H8ҋ +uU =!  |~'(b|6AA &_mY 0NNn߻oѥ!PI>cAX"ʋƘiͬx^Y|ZϽ[cN~&(C\j}T2{N Z/~Q8 GGYcmͶr)WwKsHt Y[Y'W(\[Ӳ>إop/Q.%ym/Hѧ,7XsRlW< tffIkfk6 %wUE~M{[cqҟ2ęO=\Rzc%d#]J_F.mu?GYXՙ59u6 u#Z(\/޹YJts)xmp˅q>Hq"+BH6b~ִ*OܒtT nGX{R:ҁF!q-,\F] q'iCDoJW5i(9cxaS' aaMs s5\/lsx | _a eM[6目4 I=jw/a튣 ?-w(z}>hQw6YJD-{uu^3*nK>Po$HcQv"a[*Ts:Ywf]3ɸE惭_sHZQ.ϻ߿bh|=Θ77 `xGooeoM$U#ٺîF{($ eŦxP(+y b˅ĸ.QJźB{=aY`\(h4 éJu(ǹYWbՐ \U}W P+~ixT߯hut2N 8WeE/%U_J!&D(p*9_*[׫9|~2fQBnM!22YetHoR?x.3Gk{$4ꈶW7ORE4N߬=iE'ÑH\id\_IVa{[ډMDԫ̛<*Zio'>. Emm^U4.tϳArD/vwGtdZ _^1SX'+l9ּH[ь&.^qEh5ϦQB/!f܅'G%ӽE 똳]Ƒpj ?p!HbcpH2OiK~~L 2R6Cp:d~=~Pީ#ka['3׹ 6bJsg UtEEiބdXlYO 8Y5!#\Q#1HClzWpS;6ixꎸDc/x2P0iLb KsL8nQ[V[4<'ZL\mWS9vԺDH Nv5ƙ`?f-a7"X<0DUUZB`i1jUYFni"6a'qOinyRF9F;fJ=]~ CU=I\1^ [N^wݫVE\ݯ+&N$f:(c3q"7L淺' 1Cg \SYeB$fxI̳vi%Փ4#~CxjI(q `*罬]hHR?93g0u:?_1UkJWjr|nSm߽yP |8-}i{zd+߱Fyk]tE4.Kj˰<y<@mM53 s.R{s̯hq-l ֏jGZ1v$W`S4MTG @y賎p$tX*KBWS z.:7!+jʻ0ȄOYzEG8ԁ;m^9u2]DC͑_tt6.Da ֥UL({(CeJB{Oi&uQR I(!ʁ'")=x}Ÿvkcpй"._js+nFqc]"P$\TӷOxEj#~+)ed6}/:xL 9ڔ"Ve5q_Kw%&2d&*/)Oyg˒]BRO}D\O# fLmUylűݢK3iLHLZRsYzdq|J̌8}.fHγڏU_Lf-b~Zp0πvO_R^Xt%.ϟV|:yqJ95GZ}2^O\OOd'˵nJH !G]u[زRX1\$1aL DwɄ8nQn2'vPs]{jx xiv>_n.Z|9D?Íyd ؏JUBQ}>KZ<ԯѕ-̍Q;6hiwBݭ٩ QuflYZ%`~JͱS6v{!,7*f\7TeƜ):2^I JOoٟ_cUcGk7=j=œ奻9piYd9Qd hblXHC-WNg*oFD׻p$d|Kگׄ޿1OP;;kUOWȇ/I=v)wW,b(v㹡l_vxR{-AwQ[>Ӧq}'iQ^;zqf*0`_I)WWP|jIxy{c6-srދj*C;u"G*2Rq r_;0`HV:fb_7%b[X=X4 0ZI ٱZYw&6V:hsS$̖ӧLCvusLd0Wr7Vߵ5A2u!=> GTՀ#EDtjy45H6=ރlR}Jدie/0+/cgms>:̚DfRam_+k/N ؏Z2b?lE3 Q%-ˠ0,Ie'Y˒T Rɚ]vY*.,M}j?s>81y~4enk?]E?>p .( 0L bm- '],,XhGG4_w:+(J2gjz$wZHK7mk_aaaa &/%,_qoKDr-HG`~=;njðܿ/Gk@, ))l#' F(>l۶TTT7/!9`gwk*.ŵ())AE$3-=)Yr6ony\[JI&D7?>v(V:x Vi|>@.Ri\4i̞NARR XN0G^32 ׶mVSLEG>uv^ 5W7 @QX_D fsN-pʥvLOhɫ`V RՄalX;w  vVi(,:.b#ňK좬{e%_lolRDF]aS6jT:p&vMr5; dGb?@lX=d3 0` `?'gT{b&'tf5߽vدc\Jh;1't_&/fw@G7mVν);uqLd8t^I)iSeTT3AQH}v}M+-9K1 6B=!aG͎93}ePEKGQoUv6ǔN|ɦ)]7c9q @f,J;O1$Ah^~AGmaȣYT͗ 5C ^%+y. UƼOjb E Rgz hM7_ Bױ۾K'Y"ST$:!Y.L:W47u:$E/W}2;Cw3p}yF4&J5? دmPwޤ\mVWJz~*ߔZgYjKK윿<"fqF6֩B NlMkysf3 yaStKN̫F*x27y]r:5uA:^Ik!v9Lc*l"]~w\> mC `g{_1DM}W VT]AX5؍# 2a5o/6v@*jVft쬈Lj<_UDMܾ".qzqP(_Or3 eh[F2S&\i,I+^-W}<'rt%uaӫeL ΤKJ(8v >+@0 R/lZ/PhJL@OoW&d}gMƷA@jzѻVZ_D}U~и,m)U¤4E9)ʊe0D&~~}bu#'Z[fx*Gi Jֳk aV7>;MR %n1A ˏ(ɱG]wd;N>h汽U"99ۦ~᰸6*`g^R#V 9yo9)_`Mh0`[exLHRyC\/̢=w&8M԰[^\LTl `b3nK}F/LFMQ$uIQH`Rd+Mr7v _%Qe7q?IFwWX53NgiNcUR7įO,~K SR@9{0`[f7}M}mTxmq]dX4&W UӇpfT5m8]J43Ng޹Jη} Af~m;x>_]5}&iO/Lڇkg$T^߫P+'s׈o çVLӉyqnkvY4.0ˌ%,\[`!K;l'=' Cҭ ڋV.ztzዧ<d qphD}];0`f4%9B$U^$D"AՉIg*1f5o;>K٦1W\9sr>fwywt?F22GL #|콥8U_J] 2~_ixjfAYkT=ڮ35ceQJWiQ* Va=l┭7TL}|0̷Y˺ޓV+t+*Qta#*z_sݔ'ŒS4#$mmEwLoyA/ `Vӻl+5^5s{D`P|_PJU"`0^wC]AU['[<:rc"j%toWR4hy9r7mCv,-ՊEgC& C.bgOPߔD7!j_h-M":ODoMZKZMMn"È56T5;YWKٝsۙؗGZݗhlZ%cc6%{e H+;ۆKwZ]Yo9n@8B`䧻;zÃUDU5|+0`kJhv912hr"a\m"Y5 njy&M}+JGy[)v$ 3wT=:Z~.)ݢU Ҙ#ofíŹX:dIQg'rY_woKo.ʟ}?H,/Ʈ`'lvC0̲'vTe~Y,:=N3d7ڹgp/5] eW9)!qx\ὣ~GfI$rOYLKG>.f0`CDTjXGWl_kK_qAX1&9<Λ}1-A"r^/\H1%nQNtޣ:|+9桓tbΣ>E2SDNp(L/u߈^ >xV눯YUN7OytbIp ;icDN -p5Aϟu]fx`l%IL0ht)[vuv0:~9) ejhᳰ;FAaNt@a0 ,~Hagp8m|y+ /Á##|r9lae888}}'#?zmy?P5@{&⌖I'l v!a3#]Sk( 1 {5Y3mQm.dYdYD#{E1/6/fd [)ڈ4Tw a*9֤IAL wJ#E\XG[kۅQHbO՛q!Zqʪeݖ}F d2ͧvKGx^$:+]شft+kVB;M6tU-|%G -ǎ ZG*Q~N{R>7#L-P\v- m#5c~maY6L|Gqd"p wLP'0 Max ؆-8VK (W:8J+pɂG֊@ĨW)wUi=^vS[ YQq"{O%=_lm[0<1,8'AE\'eؤ6q\B[*W0`?KNF Ze?Bl xЅkz)W2)-0` tc m\{b.'dn ?دcl0N'-gvBD$d6oVΓSt 1Ru&ܳd.jSo uH[N>T= t& 2b \Rоj=#mos89!5 3Y_Twύ6Թϑ0~-&Z =՗S"!}U OJѵ*M]:Y_.lGf#(ÅP1ԟ #Qý<Ud _ W届=t4`5֠[ n@b)Qp+!'`e}}8f醫,Mܥ^XQ@OY]Nd'{gBqЦ [yn+Ii`-ܘ~2(T)A<>q"}oin[!=A#~FWp'}E#:Vcd4>}9vH[dTW~I#MݨZ Yi m}CX6-ἡv ZSs3Ѣ>Y=g^tQ(ۻ)$~gOSK#s} Re.q\sU7$Dޗz]T-⩙UṊ&պr=Z!*⊴iwdcwOY[_OCa &z\m%޴${mx DSy,-ʟ{NVq МZ&-Q,/[H7= ؏E'[Ict&Sht~3gCcdN 9_4eTENR]&"t)Soģqmr Ŧֹ\\Ǹwa{2ñ(&Ҋn peڑ6AF`㶕CH,GņU|UUj”A{:2Ivc8dB mQkK*c⠡;a}R@4յs̉gam1.V"rYd0^o,H_TǛQaP5V{-B<(*>lT9O?{ZJ]o2/2&NlF|AzxشRtMSYy9xrH1U{F Bߐ6O3t5z>!jW |&À*sޭKjº> 3=68tUӓ!OL9Rl]*hW*fy2S^U4M@E8O!G{9vRḴUqy#ǢfI>W wκBϺJ6P d?i{!ͭ2e`ViVmW{2yzc]to:Y}cUѪΏ-m6~=uO$y(5~,^RC2o߅!:.n5^Cߡڊ=S06V4V,͂pǾcǬtGJbw~;65mFLi'H0fN i6)1ᖷ3F3$S#HKEl/ZdRg0K!)i-m*{B\9_iX`X``Jma4{~UXJ2/Tu8G &\}7HYz`(T0f౛0[wu$l/D4*c^&*xϝ(D4N>6BjZaH-~,>5nOηG٬m?}"NcdK6;:{Lw)>?UgŶ ؆)ۼ[N;^%4:³Lmzuz$C5ؿ$h.yDh+WFL֒Q!V`Rp,ͫW{Q؏v;t 3uopYs;eq.6[P ]iaea "}G6'qBavYܽ!C Zdaᆪn?"EVYE2>D){MVJ22ۇ"3PVɈqҹ};_<{ιZ;/s׸uo؇uKע+>r|Zݕq+qʀbܷJIsh*2s8F ٢wRBă#ؑu,8[ݨ@9y`gp֤[zʮ" |ڬɱz:0Xt+ur+ʕV0ѝ&M&fzӥ3V,lLtrD=Ye8/ٚZtzCE6٫nS8emyrSTn`e$s/ 41 ?xéT)E_G9㊮M?'I.-tر#_Od7a3n5`{cl% &NdZN$T+:l~*49:E X 2/{`mq|*gxD_۹h۟(&EeǗ:J|L-bn|`!t,ُO00R#KG{3=^7ÿ;kNxgPy`W)I1(MA,YXN9-5cۑS5羳5r'iݯ~WF"[ܶN XMTWzƢ_e=!CX[> i Eo;٠x<5/:'ۡi73Q""V"Y\c" BO#O(\(LaOS/oDj0s^=umFa9LaE9v~]* 픗mg$NmBs{  Rr_LD2' B ݤC s+PmZ{܁y$5nKri[[UfCm08ъ)0`۶l ApYͰKx۱>L P1N226J#*^ʩz?3 RaKK[LDj 6/̰X~ ^YNOOߌ5y'qOʐ( 7e5U`~Uwi-%}xA%ΗBri})_Xqyr -+_p@k"z|;s/ݭ~2Gow; D$SymM␡z"ޏkNH}}T/pؔ%;̉V_CׅJT~gKwy\R8kzFq}ȔXTZ43&VrSg,pW[-doqњIcUJv 76RɂxFj֒հmg5I_Q;wy]M${#؁ޖxv=&%AAF9CZr<%ul즏xqHMM"yoK&/!a+ Ռ:_/9iT'@N`dWc tUn~ѝP ])eqp)h^o"POƐ쬣 ۚbiF?/C;1fv^F|hoR!w/Bׅݛ}J9!Uf v=aih2$UpIy*OOzudůC-?T=im*]{6Τ>6ءuҨ<4_VAjUg\"dzKg,$^ۭV Hl2A4MKgv-#jGp=FuiAssi EKܣ~3a~[-KP]2$cP-j"W|Sґ}`3.#R١J-&ԅOДzS` ?i+.hOPGi>eQLPnb`?gnW$0;3ldEonCEN%'UHw@- 4A*\o_=-+"/H? rgV+E&ō!O 2!D70S 3>[Iz [/ɧXb69+3rΙ|w \fzaq')q niaH>Ke^'ޢ]m uȩ)0 ?i+ /[VoFk)'x}n6 ǔoG.Fp0<{d1`U%g핕x3#Xbқn.ٹ]k{=`Sl د`UYe !ͧЩdͫ=& !̓_ٵ~Fip`Rȋ/Ye8jq[\##uXF@4K ضdvSFKKj`$>|:ɷQM)={^z52,*!´Jqc$>hjfX P7Va~!6O]%Һ:#W(v='V1KJGWCv!W TzjH53_>ξxǚzN۩y|ɠsh/).ܕH%X d9Ř T}xN 9';Qc[-ϊ2? lҀZ>g]+ʑ`cS:2gHG *ݶ~½?H#^ب!Tp;1`; 2Ҙڃ4[#bf)R_?6RÀ|ѧ _^ˑZ`MWztG fC3!%B ҋ `e:UD\FVc.F̜cܰ9?rFv@D00`?k/?ث;_ ]%.VB| aaU?m*t,0`f\^<;ON{f*QUs[s'V\ VmxF^q( [m+q|@SMa-1F77LP|K^|{}Ye3QvnwT`V*KS&Odl`Щ)Es: ]|D 2샇]P`I CvʼnNcdqk[J0S4yjɄ1:_psO!V'.fxs>2K .)`~3Ex5`bQ7s͉Jq=Nux[C5{i8pڴ"=^blD6l"\]<n/1Rܣ0T3®V0&F}bNiNƻmFg]z=#d/h/_d+v)Xz[{7[/uHWd mf1k~Cs={ʱǔ.?D;%Nn&ǽ^{4^1¾݈jgxJ.{^6)Ghm`բ[tW{8V6Mn:\י5xEy'7B'Xpc-af'rP9ػpvEVỸCcqpeKdMdMe&JY%SS]W߯~?8s]\}}߂7\"(ιT;՞@W-jYCQ6_6ﳆ.DAsrvC1fP΢_o3mTgAF8,VC]aUNktZ1\WG[|q@_ 3s_A{kaoϿr>k:L I6&lHw2n}_3dӓO&E;,'֤.CHղXi5rSl?/5!_]'qFvh( 7qk(Jw^8'k] N :#'30y=#1cQz o. ӥDNt6М7+Eb PnXZA;8Yqs3_%eR}鑧{?ԅ4-|^GR8`HsJH Z:2Z3:}&h?2cS`sBLKp% f ( uZeť?i+U 5wlNuIR"φ*1Qnk /p'o4҇ ؂7Z<;G5₨iWIJjǿ~rkSܠu/߈fyLl%b.-YazsoQ/I͓~.q%;ʷg9qZ:.%9Rh[F3x& gՔg=d{H~x \fnHLegn`ahnS9mlM-T.66$: _39`@,WpAGsl$6vN8!H6sHTX ~rPQ!XZy}(}ypD*i( M PzJ{\`MŊHJȜ?%Q^J %G}ێ{0m^Rl##Bsk_s/#};̞; ລR\&Y23c( -,o״m;t ߋ\vm8%Dȯa-rG;7ΫH9Kr ݅ٳ+ 17+Møo餳ZƖږKB!*{'=p6q?1DjY'gyXڎ g.8]U&2Ru#[REhk}L$5+9<,]i]^ Ǭ.x4P)Wd3{ RH|$$|J ۀggڛrCV,, /Cr3VεxR?1#݆s)> - /F^idғPE/9@mۖ:ֺ鎕?h^̀I 8?C h7XD"j5e)YgF2T" ;))eځ 'jxC%gpY@oشyꅜ BIZL1 xh}5;Xԗ¸#(z˾m p.FnNU蠵1F/fR <u:?r _(1 hrCBm ]o>O..&a iJ$ڪo>8\74ʮhc[x4}g> }MZ7Xn2'FhShfq՘9{MY'wGZ,yB1zWoaw3  ?ծ8.O`q3L!mCf2s_SnyWxtd^>[xC͍ 3~䊓x$Ci != x $";GvڥȗfU*8ޭ l~: [ƂCFJSϱ O"R" 5Mh>cBX&zTkRV"v mLY_3 h͚^p ' :iy8ȃ~CF,ş[CNjx|LM&>=kd)Ƌv}tIJyAFUK@jl0id锁2҆$2Oxz5 ĮfݓX:\BNMF간V&T̃Ӡ퍽dLLj !pwe$b\u&U QUz{4~dv͔sǵVfQ#o^(ǥ99 گY-f~*pS#:8ktN!vV!ؚ[dn 2/g~WN_J&jF]d-Yݭ l~kfw#ƺhøOߓRštXGU1e&l2H́vMqm緝nI-'iPpc XF6bU怶lhhI/Lά:E+c/&7} bXe-(2{dӌ.*]ޢ("xHi1,hVJ>TOK[A+8E ae}LPSO '.:1 ڿϬ8 -NJ> a'4u)fb\tzPFMm9hN;qPd_~imk?cEw᫇=^_1i{5bjcغʒC>N2'q,8H8QHu}zp_vf S0u\"k 6@= t]Zr M q?ܟk=خT-♞QΝV*J`b%T#ڱ%ba[)c}9@.J7RaRwBLjwsl|?˸ C(u jٖQ$"QM.!]d6aR4~-[ EW7½.~׫7歛ksKt0RQ)\˨DGQUSh:xyEE@++dJ1g NkYYq>k5Sjv}u5eUqI h V^xHr 'g5V{gW#'Q@m,閌j GDp`Z18x#8IAm4өHHIj87% / [ O"vaQp ={U8LƼvKBT>l?# l,H@+FR ь %SF>qbQMt߻e]͈bVl~&삲aZ25)>5ݲ Γ9t(}ނ}~M'<<: 6r8#"(+AthA>dU/O8dg7{Rrix%ߒ^ ndvI]rH5(f1RO]^5DًE-XK)…opgRAW E !@tNNWe zU1Ǫ`[d]WfwR 5KcjkD4$Y򛴻-:\t)RU:@_6{,f H } !}k;)ǃMWfR"`wBh'$$;ve#;Km獩@Nƨt' *Khi"VpFy7;g))) ;w|e{wM*?4\U\U=Wڮ(^mP8<ܞ^yheGRee2fk\0бʒQ)KU5 xbGH 8O*+,! ǘ>j>;O5F@IY"cz. ig)`~3ŘvȬZbNXBp0.;jͽu¸4)QdgPl$L \ux{<ŝC7#orK Ɋd3m{s8v'fg!z u1'SɞSDt-u#鶧~u R0`[!Ig.8Wwt-pm/,ntbZ@`>"r $MwƑ FJT%t-#_&Yysߥ%%`W:%K)ffwM`0E#3WZ 40v2+On Vu&/)|N})(s5IٓzZs ̔WM}H|fStlTQrph( ^` pj_㼴cnOb ň&X_NT^5aҨgdU+pUbD!; |>3m͊ܮ޴jH#{Tv=]s*6,@(,Ĭii O_?`o֧J(StsavGwˡbQy ުr-vazy2丌 ׻u%OMqͽu0ty$ I ፒ 4%F d"8Gڜ=3E7y*96iEmd)QVT=kawO^c%H^2Ei˥tN")m9嚲lk&p"1RD)@v[I'02.Qu '8Y2UB M|.xݲ+Tױ'x_g#IiPI>{3GWd7v÷^u`;c:J3Hl91c")#+4 shSO9g,܏}N}+ .R`lr}*$_l`P"ʗz>ԁKUB0Y)3ga~,L,ƏΙx;@x2 H,m~rjjX'1MI>AsO=嵦b?u%a0r'\;a7uCׅNv¼2c:cek9.f@ܮ5bJ`7OAJn8 iˌ$κ9xZq\h`N:iOKU9`ۦu&iKd ǑHe6Z*AbƧ۲EZ!..&Rw`wԴbeaERE4Vk1%]ֆKh? bQHK^bM'E+]íKܭcgj8r)'Ŏ$6`~WS>qE[-0߰){;:zͪt)r cͤ+_VŋI]  *7q5Δؖn+0e !MqoW?['F /?Xj!yܤS/mKu;<>9:e#}Yץ <\V%bqbu[{EI5gy.Ȕ_ZDw1xԵy*g#Ə{.eS ë'7/N|#0t32D$OOvQcmqھa:ʂ{jlϋ50ol\.Dn}[½_vꆧ5(/8[?/rB)ǴXgP=KKȹ"t]xni.H_f_ `7k҄UK ;&YROkU9_·731bhZK*DTw mi26NrZ36->#aۙ{kʓd jUo hiz#lU r Djig\t 81IAsժгxoW)mܭp- 7: ɿq Ԫ_QlZ8Y3|}Z.t:o!$Ýkʡ<~񜂳vi<=rξUװYzIgN \[#š^8TdC/ܣ`s:Mk*7cULح6$)\˸bq1+rO,Q)wA{1_Ξ{lDLE:jLQƊӧ[H%O'?"ؿϬNXJ l `~p=K|,2z]={Ҷ+{]EZS9#;ɀ9íZOrdSN0?J_*sE!|]2%lqq KL?Q:I8e$t9nE6qj_ѲՍtKo3].FHPϘ#͑iL9m-2έ8y}$Qڏ_E}ɚޗȺ1!D"qpdlEq+>]|"(Dj4+ҪS^K4ڌ1@6_SS!lltEwE8%EM%e 4(Zu/W2w&wfA( cʕceeS] zVhQ <(_QuD9Na-7xUޏg5pAzL4_g5 *\f5ί<o=&Щ;Zs}숇- LˇQr.aQKwӍ.u$! د"7 SMgi1ds<-P5suzlg"GaқkUU0IB+J[B S׆g1UINMS=fS^~;j8<]r ',b+.F7SVƪGh`뱼`ph!jJ>83`~+O-zj8cA/uV579gՓN cFS'/RMt{q"nPr%Ru"4n^.`L;H[MNcO(yXg% 2>%H`vTc)۠(jEl6 UkrpD="pKm'Ci,idv Q>orP~~$>tcyk{ DX0}Vv}/1V?'.!Ûo1?=>t]j2L> wU#;0 Ƨ;ՔY Ʋ"YeY̽pRA.S&jWG3 N!i`7>#b3cS4 7_BU0%N~"RdDdFn+{pmeG2Rdf(dBY%#?'9ssι_W'A_ ]svn/!8sAamm´^*TY`6* 8p)|rHɋ/ο3j.y+Svn=+CWݦt6J4Ww[CߕUp4Ѝx2`Bx%[F~ΐoŽ~/YO6+{monߤ>:m>)%%\26ώ;uTE3j ]|D bǘ#ǀ!]PshI KԶ`Hi ib Vcub$=K!Pd!&)f{˗ec] yP?f )x,#ߨW)\2Ћ : +HxiSc4Df?`C.x{H_Tq`[èՒǠSo{B9u[{uVڨʘY;RђN^@ pD6\K4ݳt [IjYGuEQf>6׳P<SΔ|7b(ݬU &nH`s0gocz[ZO ݺcsF"˧dܫ6Յjv=c#s+mڦL5lK<kX0/&r#BLK4% MQ$Z0}ʊ܇TW:kZ, W|*FmZB^ /*A ,κڠ;Nk%G:FD(adZ&B3b^*gpwD\4);7]cD1LTjs ˌm7gw~zv/JtW^;~3u'r $9\/ݭ6U:Rtj+%XI鴭-*n3Z@p 0`۶l ĎKwˊɍM1zJ;0>L 3N߰22V2*hT}_щ)KKS8Yf~#|&if#Γ=bMXNg`w5MsWt4B.ОǁյOGO߮g0yX"̄0` u)Ktv Goq! cg̥D\PJZ5[ĸp+*:v}%5n?ݼX̨ؐffJq㼃 j?y `90RA Q#¦Gt4wo!}vq1_?^XWๆ\$]G߀ w!핹aZ# =ˇ{`]r5s;nnbvWOUgbƊ$Aoʰ( /i^࡞G"]9 A젒 K!{qw̸r ,*_C23E,zX>]N6~_0VW8 AUG([V뉘(z?_̯UwegJ`^MɐLsq)9X^1'S?C^xe!Cc;-d*[ cɢsotO]Ҧss4pp?+vZ>ž3& EIՉ-mBW[PvkmUvpHz偑Kree[OUv2]g]| 3Ð,8@7(oPPP g3e_L˪cV}KK.66oߔj\.vrp9l:2-msI4Ybj}EŢҢIl]qdp`axT4"Otzd_)+m챷) ] 2ʉF>ՒceT֋qaye_:q^2'e {_E՜N N>-VޔBȯO@g)-MV9rY- TV&1u9}kÓ}[M,26\fMO %> $cޘvv=sa!f ׇ=ީJdz3`E6C_+OL9+1 "TKëHt!DCϐ ֑f\z^ .:#Q!oVtݛ4_x Uw8!C_:!1tnsU踐?&FІP';9$r^ʨ)kwRozX_tϱj ָy&~Iw6-L{cP8ˆGQ -97(ۙ*, z%6 ]՘~z2ҒYt0`{e^L޼8A>KvN'm?l)K6wf{TYjI9q pWvŹ(5 ^YJZebTȺ1J5\|H6'i/0`ۦu3,Fz^zT@K;/R$֐8r*#b3Qc&lƏr=dYvǭć AlhK!rlD&DŇMSgRZW']ȡ9`ۜs}{U:_?rrAC{&pCcчucȠdŸ./[ļm E;Ņ;pGE9VaC6YX!kp_qB|7{Bjx[5sA&/SGakE;m~YG4Zևc"˔* \ 0`?k(r.i  vUs /lDԄ(x%neYeTHe##:Hee+[(d*̬2O}>^}}N}puz _e·|oz;>M3I*s|)SuC/+%*y4ͨT=7]rhqY$8]902qkW$_D훯*Hj:ɲns:r q\^AM}:d27Z9ˆ~NI;TkP7M^7X/ҡ\m v0MzRy^tٹ Qc ^ؤ HûQ+ mcThSQ/n d1tȕ-DDGw0 C9ZȷMMHJ{D$N@& ;]R}pBh6`72:;gP(L~7uMn4tӬG!+͋[1ؕpp^+lIJ8b"}D(nSM\KJo(1@1Vt$^#а}nki\"Q?%{=#WH؁~XkZj1_#P) 9&=%]o#^jbD^#ۅ`=[ۂa0 kSUcXQUxEg-,Gn!L#\S 2 UbEƇflUd"4~C4R.hNmcþg1Z9氬V̷4q bC/ˆs޻V?ǝB펢{5i<# I YONFyӀ _?gޡB2-TJ#Ծ/\xc"`鋕w BƎM_d)Lw]NZNю[4a8 V5㍬bxb}FcDθ;Em׍}X-F L9 9v _ BpBd8$*ܪMp/J@kyoLR"B3 7 ސW+RXIW픚s rdkzd_:Ed$=]`fl& 0totE^6ľf[˄ebсV,Z2WR5~O+XH{t ;kxɠ|µ+HbA e3F(9~u͓ IYȰ]EJ" G_|o27`{suS7.0 8~jo A o*g^nč9gS'zz '{{]ϲ(buʙJ:Ukq;`,=%{m1Hɰfvo1a(oB̍_QA XNWa_N(`lswRje|l{T_ANM!iy䩜|#܆QQk)X|(Uk}D2-NnUQۅNDw8kDdw/BS)NAJi iʡ'9prHx1~ݨ6aH<`}KK~xR +tt\!n0U -_ض-9ՊIמ1/{rʶ=Zsi "Sc"lN.5X:#GJi>bSr?ԟ 6 KFZ@`WJ7L%Ԟ`ssYDYb:I~P35y|ȅmr [" b06`$iHWj!/ha9=?XBDDiNj\ B>=+&_ARVֲ[}Qy{9!GKܮ#IM j>(R4ZT;]Ҝ~~徿nAv|.b{xX͓ܞz#n(ҩ#$8jq2~b 7`GB9{֤Mnir[DB.دC9|6SvT d"} ]1MF׈:9^  ?FnOphq&8ЯXSMaK6$+ED, !q1(~ܿF_Gifl>CT,eF*f5ji?'',4O(2qWJK&bbui6ᨇ#-xHE a" x*גO]n֘wôs,:Nl%3n,Dx@e",@Ͽ;-{']d@C/ka_g&]u*,rM#6f&ꦰH4`F'9S:!yY&# Y)O5V Z ңV>)f 1ZAtO6IXsVN{. z8>%Uyjz -iNߝ:Kc b5f_EÞ;"TZdrLЏ*އ<F؋'LJۭ.tZ;fc7IG:`ohG+5$pi `^=wyHt? !*z##r'zd =\XQj}dS'ae`eCa6=H]]3#B1(܊؏iPD]ܭƄbdIU h sڛ:}T!yT?Rc iP ]z-1Ƅ.t"/:Y!1`%M%{|/tK?g%>)@#~ZgT K~vd^V`]ϪDr͏i|LclսΨ.YM[Va-Hz386guRl#Z^d``~(|,noƼ >brry]5EOal,%HYrtQ>}(iJb %Ǖ8ĤN#|4=m~ơ4;z.-%R#_H[CuՀL[)]5V ĜVWB%ŏ}j-Im nZ UQ;Ve)h/;pLWN] mr,_ۮe%swt.jwqBj^ךQB ֍gBɜT:l2Urs2gT"#:) p"\b~rNveZ^]bS.>a9t]y1r4J'*8-uyKߐ̦q)ߒ0[0.WhU,F-OsAt%^3]9(}ۋPoֈ){oZ/τV'񳦾է ln0% icm`:Mr]"9K!Ҁ{%zxy?IUؘ} q1i-ӧmEGKPfGmM*'Mk±'e$JȎ`_0)O~Zf"0`f' ν|ⷯџY:gF@5gwxiJf=t`#FyƤzNw t9=tz# [`)~No"]/G6PvMu&mkti]nlۘe(t*䨕7l b:k`'9QA|':s507ќu TԮxz) >=۳)Mm: !7617D(Lw\6Pm Iw9`~3u0Sy%FȽJqc=N!u[{C748fQ]L6}jxT"RC)?{( _T`HLf3ͻbdMoi/sV(*2VmLve\AE)XIGŒeΐ{B{ >8|}wS%Dn%id7Ø^@/}hIa@^(ƌTvBQYuqމ^)H/ aX FXݑ5v%@w*")GtmxպK{^g&L+]m֦B75 5~D勾dYO€LkyySUd:Xk/u1TnUe_QYZly:\ȼʇ}=0ص9lkU f$:\p'r`ep# _y\bF !+"Wp8tlsV_b`elɪr=rD#o>085C`Vv>7P-L9s8C&Bza',jDVJR_ǰ|+F^%;N^*ó,'= ]ynh(8>lqyjH&;*΃tp9 _3yRrkdUr!uRWSAUʲrU`?-L`wߗz;wXyۅDRV0)0`?jqBg>\-|`YE!ɽLŒTL~NGy702^G6Y\\eVJлba݋+Q#H3Bw 'b2h,W)Ӽ}M`+?4ڊh?νO/keaUv2\Ò-0j?Kϫpq$oaTgڎ4/;w>PX/)L%~qj%vNmG\=Eaf;G덩;7-`ڋ9[Q)KaaO`n&tiIeIeUH/9Ǥ ?z MD .PP/ =?IM6s!?bT@ iS } C멛=]ͼU VΫӑr~Ge]Rn؅N7pgt~]: 唓`@4$=t.F9k* O6,\6=@11 )$ k\kTzt'r)s䜞F`8t$0`۶l Ka_AŊtf28%8t/L@:V836%yyWYΤv~mgYBx4>!HFfvs3iy.wGr ZaVgXNZb*G'(>a3,7 tk ;T~;j- (F2+Č^hTf O7N\TcEENAhYq?X¨ q3W Fb @M_҈iK7-L;ҷީ?]׏| "u% D_h+ȲLxW nٍ|C<t>[3B 9ĸ(q\B߉끰x0ٺxMQ% 뉶[ƺ=gDz_$0`?]꘺* glݘO%k3̊6;ö$/Vv/Jfؙ{p0{í 8Yh[ک&vD< J' _u>Y$׾\:y8+S} ]:+[撂)|Ǖ9]TIK%tinNA3VuX:7r ŋL{Bߝ]*~G 7Ή"ߐQA|L%@hSTy şYuVd~!}G4%zb^W\SwpS h#7̓y^Dznњ^ *^,|aB_iKVfa/m_zIs5fw[MS{MsLFzMsAx' #O 3'_;`5 nYa;/^[ ֎G-gVV[ś;XyjѽFo[4ɹ?sgu{X'6 nYJ5!M.br:KQW ӸxȻ2TdY!-Gxуw<+txٔ#5{Za"bb(L=ah06ܯ}!@\XX=1~X*ga *2/e [ћg۵[,]*m[Qm% P<'j(zX:휯9^1Mv,/Dv:'6z7o8wW"C22>d@ d\s2/b980ҜoA/Sos2~yE|f+ƧΛդ* CxY$'oUYsۼkA5{@uU;t:uq7_X `vlJci0>E1A&9Tx=nw_(C-9w|KF'1l3)Ǚ1b-HŲIjZz2,2\MTx^2[5sa='z꣑ڹką[ 1.޷E|K-I0T5Sx5^>s΋ T-Qu\|=nLةVݿ?ظ.y\{@ty;tV#> M15z'\rܰy!/CffZu.;ߡڍwx2j*TvLY !L%t,cFKI"vz(sM<#Zg;KLP& Gffq%)rRvvHq2yE~E xЦ]fR{Lx\[ߨH,.g +^FDir(j8ޠHXF/kn8{<]C5#orO ~WENg 30x#6ɀ<|}6j"V 8A{ x"k/['`|nԯ8ԃ:"{&SWk>4԰'"V|*II@DNCz F_>)Y̹3N\*d4WM[Ui\|?t;PK^4^l7d:qLؿτo;>rx9 u[]PE~GޑMTK~կtJJ*~hl8 zU&J\!A&+/ҞjYm%1>'6O*awX bҊk hRiZVYkF=aaS矙|=Yta V'b(8 Iq ^ :j_LJ؉ 4e)vK +IjK&R ̓AՊ\aU4?~/5%^%O{tmsv}r;*V0q1s؟cWx/ #3 1~3-9mGER :zyU{vzHa9y`g&H[N "Z&QYkuJ3ʊJ?7g4&Fr:c9Y%Z"#mrQCW~B`~֢$6-=0_Sw$wN`[\$8n%X)=V vLMx: BtL jcs^7 t:^~ ٩d^)dҢv̸="< > w(Q`%#63>!Мdtq4\v})l_S_!!Jܸэ79gӿ=ᗷ>Q0(K M='u3'u- ۰Bss]9 ~$$O~O{^f$% ݸ4{Z*暏֍mzQl 'xp mE_AK'I`tq -[l \;X <{f!1fgwM+1Jqk`V$UFayF]MlmX egfAJ #6ǖhÍfzc֥VI|;uřZBPu} }gZڴ^#&6N>Ӎykڷ^F@/t'Yrw_,ODj)]u ᭱As;ժ8z*t$6X7S ˿q Ԫ_f!mX8Y=|uZ.T#o‘N`j{ ?Ƌl*C hw5& M3&ϡ7zIvMl[#aΣ>s!eVLCm|`4؋#UYp[Hrf, M`7S&9hq}\,Y%Vt; ϱRzuE{ ~rSNCv $ ؿG0#>arXzA_OM 0`v$q-̽W>V-a<1~<"^R͚qRe!q ڿ R5+D׺Y*&$>Ɨv3:6|#D2l=W' :B*4N]Dv/u4[-X߃hF4(?x'~ 0T'O-q#u?I7H7(ȄFP&!g9aXϡ ozwCr7 ,\8v%?v(~T?Ƅ/E:osV1s7Ӥܬ֊( N7K!4q;wY{}ڈJ<׆?x!50O?_goY!!ݙ|kLuaRwG',rSU,D)[C#8}nwuVbk91 Zq.vԛGjx0/1UN3* kpШ*WlfLlL=n*0`%ij#;\)\#W}BW/ƦqVN`iϬ%Jd `xU!tdB݅6 ZƎ6UVa'DbV|qaml=qbyq1e|}IU0`Ud@ rƦۑ1B@Ny!$#Ga1W*K"XM9үosIXRy>V 1K~AN`0c_km”B^CbP=+3  qkev"1jNrAZSI_Zz,JF X ^jxad\}64>Olg۹9 u[$aj)buD^߭P#zٖzOۦqɠn8M_M&A-{3\b,{b/"xO'c-k  XU L6'WwWܶuxB5flqSGԽ$ M`~ְ.> e E{hʔɫӕqyRLW4 V+D= 'GiG_]o:3L4`IyT fYlӜYiZQ.yࠜF8pfId)EkRZa[ r[9]0vuZ4r}T⽙h9W2Elx48NPvjT`(iLY{f;?Ť92wFF#j:5Q*==G t6;Cgzyr9+}i4#TCaB{pڮWi֮ 6;C׵}x[7}mESă+l#F9 :CeTёwvv?{Yklz'U#8o`-Aϭ X[' W3 'Qutgy#a|a/y`~3F{M`g<3V{)kva}y \hP`K`B50`f/N<;h^v Y5ohL)HvL|jϵ 2#:w9*ľ:Df-EZH-SaI:״JXGj u1z=VVwH#]i#^jfL1ʨtM'|;0`[U`ķz0[82˄+Vuv>i:E'n^҈]F}h? zᚴdIXZRr3?`#F;L.Ĭf6e}V;/S3 0`f3oH銈K5/e y=Z=w+=Dr4&\T3pU2L5ޕ z\kTv0TiCX .u,>5͜8Etkz֡P9q[Xa; 7m5}h%VH;Hi$?RKm-Y<-Kw,.GJj? ..IFDߋ\ptUx:ѫkR3IΫW3ҘtɕB K[JZĤy\d /~;>cwsuS7"Kp8b{uVOw' LkBҢث} =8OG`=vo&9w=.o܂r𷂶{{p=g,5~lkczdjS"W""N $$[PF|jh997'bY'Hi ͛ovi{ƍCתC%V(S=oVEtg: @O(6ֵ t]l+Kv|yZFq˪YOf,)4,;&C()S+=u[\Qo7S{n`qq ϙ2ɺՍL Ӭb*cRyQzHۜ}[/R' Yl.ܥ;)cQ$PӂalX=oȗpф/:-!NCठwe+)-?۬l)lP_ݠYu<(m! 4N`~Wӓ>e)`Q"xuSPhh@y3#"%G7lܕˆ񱪙L{*`Yɇ;ߎ<?>c1' Z4U{X "粲~;nي=ۺ&U>[exD+ŏ냂Oo iun!NR<s.dhY}.EV9WIjn4{*0(2b0`[e۬..&&[q8wwr0¶`ʂ R'wF÷uŸ-6&_ށ|3|yPS_–0h*a+fNZἎ?`).. _+ysş3ƿܖulfk7+O/ xNF_K=a k>:zƺExK'huaMn=ʙ#j"H>1T&C`΋uv{we%;(ٛfQ}ɒ-2(h$ɮ&dd.[}ˮRd)B֒%ԯ-8|Ҏ?ڳvL*=~h0~U9CXħAq 񌨬wU=T I R2^"DYM0jQU޳zl&+PG{+10\{jWYiBBIt7D5L} ǚ:q;%Nۮpo.&dVsUqm=6FS w\gFf`Tl2Lh i:m`6L7˚-oqj:$؆5wWdٳ!ʖ}i:k{[, ?y"6V~1r 2XP, !"Tbpkb&xLyz `-ߞ S#$Lzg#HF򌽧 v[M W4-#=Qɩ ZWn}zJ;8SE#WvzHYh `ں|M;v[6*U59ʋlu~_?S"ꗟ#^w̿ |a_Fi&e0)ɴ_ kAL2{, ,&]ӟӛ)g0Ѿ&~3fҠ6cH2PwVBLl0u~!<5\K{DpK&%1yrVķY7PFAAx99J4٬f6^~P;dFdF%K^Ϝ9a]cU vpW U v5˻DEQ^Rg[} SeCNpZmQoTcv22˾ԪԘQɺ_-Xd_sd)<]^Q%"\HuVle>_(9#N~1zn'U  YP`?NRu5D# mK T,!^ (63~0~-[ׅ=vg5UA~K[GWn-5S8=d9z16Ryx<7ޒrB ˻b,Gϲ/.=oD+F+ .߬$N+ So7r92JUGRS+zرzGJ8(:`,P_!8mFXJҒ)9-E #@ ! cL ft\ $(o:^Tҝ"O}JsyRns!<5E1&3-Ń1`Eze~4nEb tâBG>Y{8NNd ,1Lgۅ+y|]jY5D]\Al% _ԜVdU݆`0UI@:/Ъe- # _O'؏UĂֵgl7R9R^1~Squ/bL3cS /[MW&N$i>l{^*ұqg$ / Pv8ƅOI@.Ms1eQ|]dsɐclk'f2+  'oM5P'BV.8 EJP1IաNR-MT fk4ykIN>~KVUv5Sw\y6pSy)HκGKck[ݿc*>~O0ɰz- cu" 6}]=hNɖ :/800$*H>S,t31 sfi=,2Z[aidvy(sM}tn%^?б7zhCC4clofu?H} O #e,IY_ Z_1B. `NM7Q~؊b{v(XGZ\ NnĨY|Ӧz,θT!h b5 c>ۯwJro f.ci Y@,`u>`imv"Kn1[sk 1:N]Kp _}!tO7~%7bS)q&SWkfkۓ P9{cLJlXӏgwN DSߪ=?{}lP#e6/3=rgkx̘: cuN/V!sO򏮅p賈uަI+aD3QꫮSr{,S2DzVu-{CAU>pjI_g +2)A3Md]953VL ڠ^aYqpSwB",R:9_5Β+nI󀓽so?-Y{tΞmtp/,:@KI[}ᣇƽ ?H9R/n-c0 y1Xs_ ڏŜvy>7e{\7u4"ś_)7sdXN~cÂ%4h׉-hii;tfk`\ U?L:Rk0 ^Eg\))iSa ؟c+aϣ/׾O%1r\4 Ϥ\ÔPW؁Vrn6ovkM!aRYT͉6Q!v=J-3lͻ(^ s<.Rwl;pC^y݌ť*77֙$.K,qʒ\}bz`E JmN[νcYA!Y\$?h/P%7^ 2Ө4}QYբLjN\*RA9v<~+t_gdu^IT=?_E٧/>v#QR`?Tg쥳%Gl%ߵYN6uXc8;`}Suc>BD"µH2ۀ}Ϊht~gcQ0.M');Xv)[\oN"H9[h{cu>@m &R4ԥ$UCgƋ[SY٥z=OF2sH#G[W) $yù36LP9ok\P~׿AgSӃ0n^RxʌL(f60U)᧨#0$`vņad,)ƪ}-DkJMP48:~NnnU=ܜ9ag[.!e3Z]_Q]ʻ[=mՃrǸ)>  z]%ҭ<*i(ww %nJ q4יKF9/'*/ֆ%¨rQoæo=gUTfL2VYEɌnKu4fih)~@f{GMeyu1܈}| $'rgqH`M2-MHzp^,]&m6:jCn 0-=QvR[]h-eYWۆnrT3g0V,[ aS|6l=#^G9K,Oӝ)hE3]mRKP2A$ڷN)1|mŎdN47`%9g~1ův<|dg I06rYBpz>;]~Pރ؟c"?҃]4V <)*yةm A9[>YC=+xZn n~}>qo.F uܤSU]k}m|ju}k1=nM9 ֶJkWvLϲK6U@o#TcΎ2(+nyo;}9&agHS#KN-} 2AEcngBwY< Fl?@__+$y4 nkw8] ۀ/G)븦\8k_KڻhtxCl uZ6v5uayDn,ʟgyOK*PFI&ilͦ qC=qR|d U[Kh8C_߂;(̵ 0L&ċg[{gё38e:BE- i}˒ےנF^#֩+k&f> >Q[]`ؿϮɂ cumZ=-]JV;Bʂ1o`a񂠩z+gDS j0 {Fzr" @ ˂8X9 vCh]^xrFp|]*Ph!1_'"6?DsU`Y¬___lLeiG?gỊXc3߯!".ns;*"/y'uTDZ&P|=Pr򩤊nYA96^R-||W(!2uhkytM͔0띉JхBtm7Su[ѷ/hl4\\5ZFA\&@L^ܴ*6@ W֯{@eꢿǺP5`f/"wA_>{'\X?.iX_ ?<iX/SXܪ%'b"G6`e,bXxv ɭWkp,l3nlIyNkl[n? l$u.O3+eIG*Y9!Ql "`}TKgZ ^Ȳ[Ln"7N̎е8m<&fWc"B}j績 [תMǔ66%9VE`yK^V-ARWtת`ۧs'MIS,'8VE.Lڷf $,"LoiGTLSxuձO9'yj0pvmTr8u~_dQZ}T3/<0 gNUnsZ ͫlEm.$U9a 'Ů,0W%:6m*ƢV('8 ﵔr[ ϛﱕ2< e Asny'pt?gֈiD:.;/Ϭ:0~Q0;ޕ),t~8vč8buȝ2m ~μ"_[Wx( ް'*f^CÊq1sMkG_mGaLI( s$3.aވ4Sγ,9hP:e4bzia6QR{Я 2E>9k֪֘]ŶrH+DCs\i|K :-ї́Ĵ]ʼ[J ט:Q\ jJ[L`n2Yt rq;mF.DNTqFow•p斚={LRVmEhQnυdN_6ƢUd `*+2%@rŦ7^]YtrgXwgY_]ENPb`Ud$4rb9ZLP5>ۜQر qSMzi;a}AkDR,FqbD${"]2Lt9aw"҈. tU@XevػJJ G%AVKP:F~Uj`Jt""O%FŇhcmtƤ=ԖN\u{7hП qOOǣZ+WjJ?nDnWb:X(oe*@r;/^ꕢFHF]ҷV-V-aA9r~#%+1kfjjVdKbKff Sf=48RhϮn^۔N-j~EQͥ+5Z-*\H釯Z]ލ EISa̅A 'ܗqkHvvbf6UzBTHeMnS0fH7^Rrsg~xⵁC;KH,O.^l ̐3~uiqMۈGIeSc ]t(a:⡫nbZ@x8_7 KҬB:c]?PQԃIg~i{,L8]&,ԢτnozG_W ?E3X}5"![a쯴XSKqvn+o 0Lg8-oA-SJU҄Un8 A7qBĽ0741,&tf=|9 V.{mɮfa;5T$,@z'ȇ~3KJ$5n!svqq(~#"&:`kV1kXT(C<mj<ncj|9F}Q,( 0̲l(KC_ 5[RM5S( ⶗uY&:%O6}L|r$?`0db>;S!hA$db>^cf`#{D)g~i:ɎAOZJgؑbode݆Icnn.Y>=S'W8zG]bi1$ ?ܻGg w+Ha'.x6N ;R/~tEYm*E'A iK6"LD4OXV^Gm 4E*具DSہJ?dJyiTm]a=KFFk/#she!h_ݿs{Mry_pDEUq*4}$,(h)~QW#< i@DD@հ˘R9'P@oؔ5q=Fڅ\D4 ? ?ǎKG؝z]%VrZAx/p>hT/"qǡm p^yDip$_vXb fc C jgI~<?-<~[ܘq~᾿NAu{}ƿwu5/'ߚKTOGCF>T&3ٷ@m̽Pӂ7?gJw>c]Btb׽ km[H[g7iEEm<3̍4]?@Wd? 4~WsI򸪖2~1'<>]4D3BVbkIbd},_^zQXTQNMLEM鵍]:eQJdgrޞDzs&j%]nmzk/w\Чtow!ebNA///4vcwf[)KLd5;+._d!E.93| 6K,\.e=񆥗.~ } =kxA Tt%t%=sÇ+=uG[[ce;v~z)uT<^N ôDNl^:O,7MEXOȗ00?akˈptkRm@j-)^=Tn.lkzsF:LYwCݫ׬9Eas\P:;,)Xw5iڟmɧa9#oҒ *3iɳ*^WX/X.o ڿϮHp,LY/ԪmBk-y@*uA {1h5c'O?OM. .Az"_gd9gZ(iW6EwA0<M]^[v 'xA~\k7#2Eu5:cEuayX8-bʊf h$/QExzT1ڱ=^%b$L9I<{3 Uha RJu)f4뛂nkS펍JN fp뉢ub^p;Nv¨qaЁE@ ?imy! 8e|8/V-T*f:DPh?gK 7CjG᫗wW^^#o3Dbhc$/* m؊(㚗8΁g=x1SNҦ)<80LDJ3í<.mo>H%9:(_e*EoO aÙ2FL&XO;M_dd*^!ME# gAslB?iqfظ G`5|e)D r3&FVt9\ʱskdz0Ԁ8M7+]辒ecNDkFn/"-% I BIy@c)}Ua? nq$U2>q^(KT[Вp~Yc9C' $,S~ 4 LQgܢK1(:E(ʈPFRzczt_ ]45}`>dwÑ>"?+ciO|* YZqo WvZ&}ƾBf"rQ߯n|?uהE !O`((Kf3V,cc%qC0߰܇Op TBӼO9fL^д06U)ij1Hr/q9`گޖN\/"F&]*O\I}[fD'hc$c]P bnu?[K#%}pvB4~-ZRG?^b9x VK?3Zo-uSazAsLj.<=~ & @(!8|5Oc*eE@-{ͪÏa jr]]s[Ln`X[˘³yjhUg?L k:B=CɃKB[Pչ~ ԸH@-K V[u_8c.A+*Ϩ/xUe7h!M7=*VfNfX95TF}M_ =^b~v8@5Yc& 1DS[WIjy*^=eHb<hcQmoDM6ЍN@PV}PW'6Ձe ilsM-tw hqvEKZUI!FdrkJW80W0 E5A:+TT^}*-! 9/oNN/Dܷ~暿78nNإ>ڼ0p}\wTluwYŊwBu:?Rm:iIp3vp˭ ޖkƎ;?;IEaNNڍo;襳e;dUJG8qG? 2c8gS >VIo7Eվ릱ӷ~_ `G?8YAޯerEF=y$O2Q~v*aﮃ>SHR] HII (JtHH"](J)? w|<ϙٝ9;us.7q~%:8/X0̷֦*L׊W"aob-pijù[u>™w)nrj;Mb"8ʻԭI4'{'.A$E0J8ؚ8X˹USGfiM-^huYAU̐h'QnUY&ӊgGV2˯fvqeAZmp*4R˩'W-쥢ڪ(u:/44)֍Gʃ7:*a c+PNq2)_VEuKnniH"'/?Gp|m&86죁\81=&2sif9snj>8)ȍ Y@5I30*xvܻTxjЗ>ot4&!$0jf'ڋyljaX֞d"='Lb ,pQCdC@PXs#Vt詺|a tv! }z=׍@ZX4-$٪ =Mtt]bVz7NblĪnKB.|0@aXJ"+axuCׅIRRU$y__}`ɚNGźC5|`鬲TDnȫ,)`xayO{B]ƱY`MzjY=HpKIXU鄓I3Np|100` %@򔕼1+w,J}`,saeJe8zUN+d뢬7IF6K; O?~,8ɿUt@#j|^NRe=#zuv*ŋ7'IKo๒EmMR\ \a 9{\VuaҌ?2ǛK 'O3 R'3(UMLHԧG"q#/[3jVBY<|,߆:Dԧn0LW^z0R>lùNvԜh\fEKk^~` _]lPJx?@gFX=Գ<@E<'`>++:;'X`muLc9+9yR>J"`0x)7&{CspZ{^0 7~S!=uIZ/^&5L8>+] 1٪^a*W:g(xaU8_?6Ka7"?^aKm.vso`tE|u.I["\> `ckW41|}$ 0Ο*!fY8y%Q+Ex|]N7mmdj\+gہhq)`c#<}g0cilѮ 7+)jo0|%f5N3j07^'DVEʦo4دYUÙƠZx> eڠϪ-U21=NɌpYz^i`TgI#܁h=IJOtϟUagUyd;LpʃfS—m ]&hr4pK(;!j-n<#?lq"_r؟mwEVA+Yk2Q7w*?̃a0̲!,H]lս\vg$^E_e-{DV{[ f@h* >"K5R>#S,EBCki|d`-U\@?lLSő?ZHR974ߧTڣٻE aKs}Jt-3{hj>&4FM2+O}NwcWt?`$(MZւ> ]~<{)aΖK|Eoٮ+eCρd-Dmul]fK5=D4#FQ4c#MؗOWq~S /@2At,}/Q72oN~P^}{D 6#)WUtNC7v3XA{F%bmиrSb#g't7ӗ"$&{ic&qh9@C^tз7|ǙѬt<  E4~ h3iw4~~&28866gC{LEn%\&\cTq1AX- g mNhzQ9DC1ydV`OSS8CS-sץ8K .L̺ >]9uivžIJTr|L?%(v&vY=2gR5D)BN /*ƒog<2"miP~tQ5N;8+4)S˨(PaBb_EO=\'7|;qӣn* v7?Ǵ.]N'Kf [W-v"|)Wo.c6mm&ps$d6^]wv78~uy9}jc6ʮ5sb3ʚyF'DBIp~Փ>R˱8 m]SY#0mm@a5b92l!QmQցLx!C-Y.s~54獅)x6mUcBm# }@0 t!8ajtp`ce8Qђ)FIE:~'KT$^GNBNbaO=rIw{o-]GqWCw^+g@'s'vku_/Ye'6c: RA2+{X g `3G[j}2!>w_on+>˦~Ot}2S+qyHqПQkuYrC: دCм~`?[!':s j41MuE]O7҇ȰoAcS 7d& {2fM\f{7j4ܹ'_{ᆹ1q'U:ް%fGʉ%ƄݳqE-GV Ix دګ.4n×/ KP=mVq62V5v*0`fTU5^>Hs8sL=c|gs1oد2Y|1TZDS;syhDzi:XѐOG fp`Ri&y.ߋ?=vvH&F[um=m,N<3$ kcmStG*ٻ|;4M?I 2SbLk k2UblgrRM Q3tuU(f 0Ͳ j0`YbO{zai[ Zhwc{}Ftɰ8SP:a2{aU]F[fp: ##Hl ӌ\XvZxS[FKaégdEdeDq"JGvpD(=B(dddH%#󓫫w]}>ws]wuq߯~>R::nA1YC8`a& 39I %H|h-nkSf $}zPY M)w‰FT/cDN(u:wqF,w b?P8Ō\n}832ch>' pE?znSGkxMQW}Yg΀q["B.dNei5l+MZ0G2vV1$hg#N\l NsVBv=l$}߸.8+gwKע)ҡb< !UX6piOa)X`JʸRz.n ZނQI{ 4NňYҭ:2ü/%b1TRVl1L?deŧZwIOe$V=yE sBÌpW6b@i2䍆(88iءsY0qai*E%@ -IE~Jg\$N p'OllЫSU^ ڟiEcֺ_JxElo˥$&9 {__[ ^9G6Cؤz.7CTZqzɴlX&lWQ -]5=hh[l!Y}̺I+wi!2f%Ei &@ar\4TjUkp bXK,/HȭAoQv)Bʰ)O#Ns &.v# /]0$IкW̧yhJ46rEJQ'GOwrvbo]V \75tyUf喘rZvk H(}".ZOŠNyvg/^KwLDׯEgT9n[X;tNYvyviȠ~Ώh~ѽw&Q=Mk,LX۶lǍo?ȉVQkYXE̦0TYi0GQa@HU)7↶'ow \/48dFȹDfk;zFg_BcFMR-"ȭhп5}sgIvu]ŻTt3d\#_:n]~2ɟeVVx ھ0d Rws2 .DgιWrR^|ҧ]L":xoU$ks }y-.*"Dgn\f] ^3'Ok2߾+ X62AAaKf_Wnn""[JEs^Ǧ%):O [\Im6 f`2}Cs`4Xf;ڰ2 K׍S@Mn}!QNrf 'd=pLKl-=nAY600|e߱;:kmE?]c-n#mgOH BE#'f EX4Y da}fN =\0 _3tBU;OD z$*{ x8A1#k;aԪsS۔Rwƥjʽʷ i%O*/jയ,t{$cQʢ=';37 2IQ|l rYSc .A}Gj7<1!UFw2$%XC޾n heя̘h>7BŐV .}I?ϛG)5԰.3*3Ė"'я>@=>+7rʞ}1b<i9>9 ~)?@)o0AmڳBnt:Iʮ)T iKpK 3sEV6Tƾtkikθg] aJ?F=ɻDVI'Uc'[AWYY A$8&oyΙa!v۵i"d`@:tQ‪NFkP&n̅PEj~cڞD%"Dž 1h)sßޛeghm-aS*59~׳IC:%ػh(NY`DeYS2%NcIB"dWHvc)d-3GNON=u_s.\|%0m~@^)X*{EbΛ.`|&`9>]4 sU*j0Pu:X: _eXTw188;60ɘ6y騪3bo2 ^j eƸ<_^nƒp-]Q.c'h\Jo 5`y duI řSGu d ֗Gz(ɵRq:kQq!ؘ:ũJtu6[yR(BR䫇d !tSĦ_eLoMHK1ȭDq8TJ̀mFޱ}n D<3L֛/2qn^w#>V.CJǨ}1S$:z)JS>F٦:!ā{=PraqivduH'&|Jc>e̾`UWxH8N}N]RbutžO;z ƪs[Cަ~<ئt=ʷJt>dhcFh}&K UedbdmKᬑPQzG>SsָT.ϢUӚmn"{ݎ4krՊɈ_F vYD`zI\zPݹA/I!P2.b&{<0첧TU~=V\9^F3%&OzQ%i0׾*,wh"D+U}B?Ȇ/$Y!uZcna ;(VR4mJ X~zYKEpP3.Н־smX?IOrM%rvimH˄]P'gQV#{Ȫ6xHe2m7)D{ks&X-b>ʦSUx:ZKm&Fl5q]aTد1SIECَ4t6Yy9.TEs}mY?p~m=2Ktbx7gհ8 A=ŧ3(X+IIO&^<(s Zs1:`CcQ;,'h|²;q?l`vdsZD5~_wmK總)=E7\*3N/jfl}YTsTs|D?ݻautQ>,f8f e )uo'`Ϸsܮ{82rߊxx -oVkMw֕NMH_b bgK r| sK_1UW0^@L 8WmS[B<׮b@3;}yc_aAnHg=2':!W9i8F<ذ)'ʩG}-N\Ky]KRqT$al?fKg"ʒzxTlS6 ⮐6Y4 [.ۈږe)^pRV0==v=F+>%[|dX]+)4#Oј3`/#u:xʾOGaE|IU2\#`_޽d|H <]Ǹ4(}![K

]PYx1cGI@C&& M=)U~jC06 㸪`xf0l@C؟c*12wt.&JY-œ9[jU$<$(^IRQѺsήzsb8_mHjGu`Qbz326Ϝ3O{ e\84ļ]:`o6I<= bnO34PՈTl ]}S`ʮ:a ݝ$'%XSG,=roJ `BMj_N9ҥz?",,|2o `yܸ6nQ<_~; Dvb3yO^ cզ=̈N촾J?K砘 ?U9؉x ~ GdBZ`n]n<1arw;1 -}hVWPΥ@1 HBP$C3}JSRFlv> ivR ;V`Gk 0lfDoE+\&ꗑ,&Iyq<-2=l-Y| rwxqکdw)44#;5 BM쪿"da‚|SM;ݺ zWgS L 6%]N"5ecam:$p}zˊ(r#\mĤ2ŜNϋ{eWv 4^]XK 2S9Lwkh]`Jf{0=M9x )_׫{Mhבc@ŎoezQ/CpMbp : umxaCAtV$[ j%nj $/AD5wc+Js/? o%X"}l`wN^nq6nσ5̨RR=z&4;|a ?f-1-1ȔK'':ŷ)O:ZJfμƘ. ^'{}rQ^ ;ǍC#Si 7" ?JtiQ,PW ͏C׈hX۵&6%yLvȦt4`F&R;ArV`dn*IoS4ZAˊ=TM5{{&]Kq"fͪDH-=kOȹ4YPaED0kaWX9OC]3K!PkrfN?{Xqh/;D;1(k;ӹy"mf'G\iy]شfp$G,-fV!⓸coZ[!d^]AIXeO,hDv(}QliFg^$UaXj9t5^gnp8G/+7VYzE@p>`e‘)xUxf̥38 9D Qn[9bl,`ݚ*4Vs U֨x:+!e$ٞ~Nx0?`>stdQ}^;yN_l]M݅t1Dy'm/ zG.eI930ʶ6}[#9󂿦9[ ấ9(r+%e7kСI<km BӉx9oJ3+hO{w ?p$)e+[6!"KK2hHeI]!JNlB֒iZ~ϝszdNwqN3=RՄan:FL{mЏk7e~G 4Ƚ}r}fFFd& O.sF?2ڼ%r 0vEyz 'g Jw4w9:m?UK~uy9ir}qU`VVTI?mF99/1'Z1P-)KWX"0u>1k{`E@\i)t ?0PfFےLq[~F~@!޷HP[sTqd (1K0rT `p ZUӏ~3T]CP R"Wlgd&kl2,Dsƣ*}~Sv)#z[FK[Bf1WnՒDL+Ȯz#.oKNPEj~!ԛ(:Fળ2X3O(C"\2M %dp)Uሙ~q\e@⃹6B[8xP"8X֦yw|vc&)nU\}*}$&MtȨjf-z_=خ]~ӬNdyϻqTh?:G?>*2 7hAG!깿ܢ\} to>i5ג?J *hqo+*B|[i +f99e7L3rQ&'[VAUT QoP+u o_y&SxޝzNlba^mϓggR,гe e,7ܶ)﫻 Gߜ<**+gB)&}箎.m7BZ`@OabZO 2Kj(~z*;P73!PpN66kl7B?3`V.MU!K `ګ#a–t29Cη#l9U-iuuHR[D5o +UB]3w=BH6`iڞ D\#%\Ti6~-_`Sn1z̭T_/X̻=DIn یme$=n)EmٮnTSz'/ ߹zy6:b6v,P`8ĪݧslS?ykvPcb.mz~3&7ǁPu!frb$ٲ ,r;׳h/uN~rvTkYq{U) 9v\i%Xj:O! : G&/Yb9^PxӺ_=Shv էdrr`R%ْ -vԟs4*_g2w֜u7,]m>Q2>J0rM%q{;IrF+|jp`ɔ}Ock1!hCYjo,>s3^C3/tJWT' OCMPsڳ.pn3[0(E(cv0jfm/ڝbop~~A 3'7''7/?rrcpràێ pZZ ~nME9Vh>;by4_Sq';]_#ЕxioGP!6Ҋ>*_{Z, wzoVe(Qaao6EjKjddf\u2Hl4VelpPEQ T}c7b.ow~m8ۏ2Q_]N%(upa0ycm`HHܱ HHSQ D!6S>7kae)ȔizuI>L3yۭ+܅] iy|Y9 FzVKh_uʇCy\ėb S[WD?tN DVl;U (Jss$r$$ɔ֠9\6;Yo{(84Xp^ơgxyC#6ü|&jO3E1%z6S}-E(`lFJRKeSI[`U7cdÊG5@:e4%XE)Hi^f8$6+x ҋa>,T&NQr Ĉ,%j`k> !UMcc>eN,C,E\P߰IgkTugf ?#f-ϱVr!p^M[ DU=:YTZڼ|}m^ގqM€T%n'z“#G^hz>ȏ6ܚԎ'sv><,kH>_,_<}&/On&f.]m*w1(af!w ׏K$E=%mLL˶Y߆kЭ,R:sPv0 pPRL:i:ifi>t ڌV\ T$QQ4(iMT!I|6{>Zy!0iKL#~񈷴KK%Wzx y Ė,&֪GK) :K 'quwuکfuN7 lu9kvjsk:e❸sy@Y ߛ=GJa㏼lj 7 铂Y8+&Arwrx;A6N/A_Wׄ-Y7QG+ngrc wK)/5Ԉm 9 ʤމ{gH`_c%Tf.MYw6 |KF{{f9w6_t1o؉J99DM{ejAjlhn<.xL>.0)>_nOd=JtL=%F`qQH]uc`>c:a{JKV ДXU`nr~s,I=k9lYC Wg{3 ,%G&wv[9- ,hB [kz,gMg)ܔ5rg׼ svsj?Z֔z+ul4!180v`|O W C |.E-?eP]q iPBXnI )IiEQ@))[@RJBAAJ%gge\@w|F ;s1s>9sR༨E7mJҔx ~EߋaZyz )8般,__㋾^ b6xȹmԪG'C|=>|F6 j؆O3[8:y0igIzJU&owj\n)1(UqWq3>S:ֶ-@;LV5"̍&K N~}x["ʹcޭz("rÀ""t1&UTwux{|^I9Ƅ┽dld`ր\rs| SҧՏE& <ԄۅSqqZX-M=SH=HOo ucyug{y|oo1$8+-`zL0ex#u@"gyC$ nebݥ8dY,kb"zƜ6=Y}:$vmlH `ƻaz <7~Hv.qa%˄> H1Nm?Џ̊TPn]]Ntr?"TWWcx=ՑhA4_7ŧ;,7A=vx@ }J8mdL 40evG)όY>ҙ2 >A:fc !)i+6>lPľ;u-q<`iB+ wd͖h=Ω}D(96+Ne`ˆF]QY;*dm"6*4lq$0F,0Q$`d:k(̠T$;j8k&] ɛ1x~\eVऊ_63_ ~:ؿxLOfqSD6k#-&~g3!53(M%. 0~P(^ڮVx- "Q-HN:E:Ef#iA^vNY-q뭼 b~,[98\F ڄCv>tR3N"m]{(xj8nܞo+C#AbГ/1␅ lzDD/bffIl.n J}luZmОgn1O"[O6G^D'=S%%gf^VܯPu^8B\AMg==د $e.*m҂m"^VC%e2KGGa M] iu4 f*7 "UzbUH`2{Qe.f.IFE'fKH|NFN@E e~i<7 .PgVRS#35Lo0 ؟m3ouw?lsv΋L]aՁќ7r)Q^!//~$ڟ$+݄ <AC!Qc8= +6>콞.ISMc-JO 'C}ߵmH~xU;™omK2ܥђnSVӷ0U5Y;tǺwIw>&W`3Yt~'*="x`UxR= Gg ÷)-f+ `ɚ=/;X>[E lY)#Sv]Gtʦd \"!tvL%f] 8uTBmtDA GӇ,{GΈS & ᠕P(i"ӘW.'!IR<Z w}i{}»{=z>CnGJW&u׊4ߗ]Or`R]|ɦb>c}>S Ȱxu(|Sv{;ove84>(=Z5gmn^ߙ$eN=^#P'CUvzŌR@hJ}Iޓ_9vu[M#DФ3$O{ߦ0 j,c[l'7"fDr7;"$Wlv+ƃ)V{ͷ S sRc]P9?sDy̶L||?nridxt;c9eܷkk_>:Qʹ5 M4*vL6ԻoMu a4/Usխ 6ѳ˕F ?ks&`XGD-_: +uRC`6|p`y:VfB`2$Ugg .jw(xe`&Y>Z!o>/b`o fXLT/%($gSlu0ⳉ{vȇ&`:]&3i)s# ,M6zz=gOc'b\7Fk]~- -1P'Ě׫IgTfz: ա髵KsIr ߨ\yz0 Y%M18(S&Υ>%SMtD =ZsvA bХE2nN~dJ+wیv0!Ie6 ۴R-Z8 VTEbĬis׺e?hxk% ffk ɊaM֏4âG4ZMpiw9`{ J7%t%7=sz^Z? u&QBO^g)8lPg_jY!i!D`?kL2iGkj 5I>K>|!7e\NZ K1b5f~¾7!%*$=(XGg>㉬N9- F.'bع-W_b?iÿA05~IJ1JY{7^u֝roK7:̷=Pvl\QO|hf\{*^b{ok^%2ҏHzJ=lvlċ鯖dw~ehhEKFkQD]E}n>,cB ]e|OPe .ԝ`v2W5r$Z 'qE/܊c:֍\7@;LqhE9ܻƭ΄ FAtt>)i1iwTdzgʌY#±wVYh{W2R *;+[}~Q<{\׹ io*ROyѸ8U%$8#R4Wcٽ X϶ك3>+?ldeTUfO`o hk&\d"~~ڶsrTEN߰22NJu1ǣT]_$h.3ځgU;Z䖧XD3ȱ7D|%$=54~Wӑ+~Bj{ X Z{NeANU'OG9cbbVayG 7rSMwF!Ĉ{5P91 $dyt|OD?IvJ "sߐ,dF ;|lJCCZ@4pgkRVV 'ccp,X"adۋ{z|[OKYJYywr3plV/)+ ץe3pΠ4o:<";hut#:Dd+ɯgBs÷U)$ʜ#iXvyxNg3p]]Z|ٺ |~g+m%HSKu^ SOB@mZ o͜||59GB(?σn`^YA@Oy qgfWtx!C"Aw5;'4tϒaq ɣًo^gLʞ5j%0WfS' _+O{s${a>2t3=q7h:vkɘȌ~c"ץ*' +Mwm \~O,wEX'!%̽l~%x c{}nQ-[NzVkNcKqrv!xg=L7Z J2Ne ##1fqܾfeٱ6L!<"E| .AdDɹmwLD._!};* Q?, V T1Ecڝr}S/oŵ25QbYTT/)^ '"2ꆼE%w9М R3f* .ٴKRO vAߋA,/.S'of@ 3. _Su3;4YW2vAׅݕsr)Iܴ6U@#nip2xFpq4*ׂBυԈ 2y䁶՛QEcBA-6HMxJ/ fޭ Q+3퍽钛Hxg(rP#7꤇!K:I ^]3dFhK',$\m \~zf㉑cB)Ƃ M&-#8(:>%1ڟmt"nzn)Z.hQt1, u~ nU!hG4U\Zjf×9w>h˸S1Q!'N͚g20/؇XJ-~ϱrU:!ga\e!|O~N9"roU 7h>*Pm8L^wxLfh?.;3 à4I;|VTtwIyGo݈Z$Zb_Qƺw(W\1hϮD97,$+bNR̾ <Nkd^X7I"gPCG463?(wS[7A$TpmxqIiZ &9eNu(iH̞͞LnEd=5#*|DhȤgq AaPJd-Ow ˫!D}Y " E" 1;e?">hkSW2AS%,ǶBIВV&#[C >aR4I[vs*$=ۓx+/kgBtY1kK1i<4Ob ?:=`mKV/AmI70n-{P\Vʒ;Rl~n}u5c ,:4~{]x"`<?> wNKFOƅ^YmZkgmﺎZ%×0,Nj^'t 4dӵfQ*+c4Eoo})i-ɐ_I ){а[ZZ^r2lyZ#G^,_p@p@݊U  pҕ[G;e|3_τ3o3FC.6Y6n ̈́ڋ۔KGLDY TF: #P| pIl~}y6괆f/]N`-@XܼWܔ"ijdT 9LDm}^Sz*`B(s(ٽֵjB!^]jO*=z+Xg46=aQe_ljL`2;S}{~rMߌ}DNT eA,*5Ywc볺,8+׮lޮvIKj P$ׁ'\u/Ѕ>vg )l"CӋkpz DFϞ1Fe)Y ``4ݲuvDBw%z9):m+-cI>t+^wX/P7;豬ޫƱ(փ a_ŊN|՝D&W)k(h>$y9RSF23bj"0Rc9MgٞsְڡP?y4Tq[lY j:lȮ0Xe)$ɾVdODT e+Y39]{><{qNT{\f vdBT y.‰ e˃4<9ܫ9I)CEF)tY h[Q>ιgz#MHiTՕjo+(pmTsgZ=1 >Ne:piOd!) ]+c6LO0q;ervqDҦ0i+] ] z9׊XV=&7O A) 00|Zۍg;e?Y (a\4B~bկB:X3FRefgz4pRse 0(fpBww\Kz))ٱ̜[f<`mg5U,5WʪxPߵx֓w3 o,F{ծ@<%ggəRgR) wqDWj"wnBbaZk9%%~ퟳ5iGAU=zyz7\jZ0q9-2^Z"h |BKF6?,udžv7p;Yy.A_ M[^FEq%g·ٜU<"0Ò eA,,u,pIfٟ^?exx))%x6κblg 9f a2h*sSN ajkǩN@|P{u]'2j͑hZCNٔu:&X kV]&KgY_SSLp}>IUVS8NmLW~7~7>[yj\m*g0yB& &㟛nnLm[Gl:V&tTdׇKvc捒at;ɱ6%E3^7OdLEmlfcgwg|=+$e%e] ^>H`MVISW+X4~ӕPK[0{$ĹT;jVO*}3핦rha~v*ܑILaY{[wؼ$kx3ڟmL&Svׅ3{0DΓ4qi#ȍ箚 4ڵKQ,>cSuŕ\zNsV/c#boAatvuxr^ZK黾rwPP80җ6˻f?{*37Kv,j ^x0]1M٧iQ1ZaXOFcFV9{^rG@_8 ̀Qf:? >=rO^h~@ogbB^XplZB/!}رu3pά`K:i48$ݏ :ֈV^P<3gA̹qt UԘcVA;:,e^ybZ J!rz|@|z/4~ "3OLXA ,wB hZ9<ٛtY܀+y9#%Yhp墨.(⛗wYq9l!0aU+V(Jt les{o. (hکF684__\"}BAas[=Y '$mV*S$,$cIr}>=F-Cק#D*iEOO.Rs,Z4[Ϯ\8!=\N{ɖ`tYCl=w(!` $xmeCh>D"u2Umbj*w=OY z7v/bzh}}v'rZ0&T97/h%XDdK^x&=:J\!:s AihT]fwJ5伕ֈ"h(!. fܔ1Љu\#f2.ao+#S3wr\K˶fb$J}|N_[sYNQZr| AP_k3Ϧ2?r }*Vߵӻ+T;\uV<т-hP?3.pj_2oyc2S^GB]37yoqm{Y7Ȟ \#6frY]5I-u&\[ w[.Q~tj'@$_# MxyH qn&-g@m3Q*ҦBlt9ٱ! OU)3,BC^{zROת+? 7慶&Omu |{硗$\>(0~a6Wn{4Jh)*t2=KEB N,wO-n^1'AZu:f)o~whd18gC w;5 Nsu/F'ԏc = =~d?f}u#?$?AYklwQP`?hZmI2L\/7JTJʭ>E{&A&ɼⓠ٦[Wx$:0Q[΃WTVmts|ж EZu[Qau"䛚e_Vj;|9m/ Ib!{XhD~ԲC5qN6k "t\5 ?7?=}hP3Ux =%$#]Q|\ |f1ZIM}{kl FaDAퟴ}^Ȼϋ;.{} ]^0;aq=sꣂڛpg)1*:`siPqjn_4T"C3>.7 DtHyB|O>)2C}O/ݔtywmYhQco(7eWPU2al/=OM O(9sh{[Ǝ5O=۫8[g3ܓۭ@>~P}v1Lrjۚ^f0=#4h!$!ѽo"5uq+[P.K2;s9.=~<&'e 1UBL泵FsINXď> _e9 |S9Қ}gg&}\ZXH'+E[`U7C8ғfLjtBI U>]!}Ab!z/.4~%)K-ԒNX; ې-JӐH*8ŋ\$p< 4glKUTsPy!b!ɯz%J!3NqܳBB_!tV^'&^P%*hJ4# GD 5ph?U|>{p:p?\}8={Y*슷Ze1QC A6tu2'_Wxwcj _]i{թHA|q yh]Sū~b ?ܟo,k?~-\hJV<&tvػp*)uD{%JNe%#eu$#cd {$++ |r__xoT{m! |Ʊy3#HF<$WJ UgR#v3qG ryÏx7;%DYjއrZ;;Q[{| %70E_` R#׭+j' ,#˕P޼_5Z&ֶw}{_'qpD[A r}#T!ЄeM?##ܮ.74@߸_S'?}\IVWsb.N^ѯ~*0%p;V> 9 V|SyxCj@_m{| yf:OaG Ϊ{ؗ*ݡC h]?*u_nσcÔqC뮍1eզ&=2@zr%-Q=e@tAf{g^oj} Nd={b\d{i1c}1Ijy.hAX>tr/9%zD -@nk%\޾=XGzm7'U1pN7q=PЬD =^rN3!=Օܞ haJjߥ#=Eڙ$=Y[?L(}FHeáj*ޝ+4!1d&tռI>JsR0bGeڞe &Q|طy,bu4O;81B 1OB@IpP1~HeMh@ ƸFrE{@zCj q%by}Zd+ h38eZ )71.4O8cQ 0<  h6OvL TNIohAej.c\$CW5ZiŒ&f"@%/_z&xmw^%Ek$6@gt:yrsM%{,M(@ʥ\^Kĉl~>K8%o1aঈ8/! ~;=_t={6L \ʸ l瑀6fFPM(4;# d}rwEUGs#s#>c?aPt]I"> ǽ[T5!FYEydMVN T2L e6)k=O3%*Ä--`CSZqQ7;yd߰0nWW.-l2sjbU/];ͧЯ{oOnHT-ڇ,<5}nu\iF; 4!bIin%rP?"v ٛGTBJ (s, ôьWs 'J>ϫkՋ$Iϵ5[>a7gҊX,jHXʏ'jig7i4޻W? N3Td/?=X/.k:o n zP./ m \9ksIԡ#jpf!"TF_O˄$9B͜>2!wI>Ja:lD|bEfTudZJT7)( h5s9Ϋ:׈Xc"nd駕l9S{ T7[WN_OC LRK%. jdz:e!ղ|GX*,qj+8"vWƐ"W7*C\bs~n86"xh\(O`S3#ٌ=x6gى(a$:L{b,χ+P#0(Go7}~R!x%Stу-(QFPz$ޭ!*!kJʋl$EjB(ŏ>Ʀ܊` .W#5M}Q8 pk u.tYz}(i܎nLrLrMh{۽//5g IpLEC v7TX]JHN7ۨU'iO)\2a{?%BrD VGav;?A?h>0iN8[7W c<91w_*flCM%&1gݩٔEDG J=^_/˪_?jp hv?bR:O$BH~4 X a1Rr h6k8xЕ(/Ͱy s”LY6@QXUaJ{h,Ai([2PbP_p hvoVRK_x~ioq0^XY\vG³͉L!,@ڴ8.k џ箨]9>[co>)O)Ѐhc7% u˧3SOh\)*y߶a UvItPsVؗdFd"!D?m*բLm,??aNpoȸ2l$6U)07otzVxg3xSz46ҷV1#6'V?Jxv1 %(5F+͍͍V(-Q1PŔ uJ؊Rzr+HHVQVx#_J4ї?^٪۬*R?/ƦǦzzU_n_煳x1~MU_\D3I"QԨmN3gknF9;[dܛ"|<:"f FKV!@_ǭ}Őϟ_\3XrR L]C/<|6aٟ&NЀmjt KGQ+hNPK|W7NqGt ~)5"}xwtӨwTDz<5Od,y+΁fuf9ǽߔ{t5sPLm6>+VP1e0fN#+%?_q"K=\/udv\N_nKOnkqO+Оg~z[|P#!+ Rb_c8Cub<e4*6Jn2B$w<*O hƾu6zJEpF?2-ē|&ҭfl@$c#&* 4導$b7KK~PM/I鱫/WMWIjܻg(3 &S as?_/osϟ<;1һrq|;Ƈt膠(&SJ~}jD*T]At%h3wH&Y òB7p=J $YnxޏRR0 ?߰nݱB|Q2Ev&Kv aX_idwmf+\9eӎʦb+(ܖb W)ڪ睶wYFz`)t } =)c$&ͪ͊cyQ|{6 n$ ţ~MhfFa:?e'= y q)!3ۍ0g|=hI|d:/؆H-ֽ^\Gcz'ߤ$e!ђ[3qKVżƄ wd~aPnDŽ魯?B7ˉHnQixʊ<lr)4$0sCs`ϊͼ^5'uQ)z&/0Y\? د1UOü9gU kUp)*:9";܁qU`o%A]8vG;zr4BWeG̵wMՓAX{{LjzM[ix1.6 B`dx:yx3`*+4.Ddt9|`.Qqyq1iĮ8"0H- jMK:B@vyQuaۀ,_Erma6ogIXBi!%b2` u`L?-&BaZaH1V;g⽟R]6 րE/OOh:K0EZk.˪h)Skd}'T,H?,:d$]w~EՊ/g -$!%=FWf[G:W@;i%i^ՙQ^ʪ~&oC Sj?eKo6vkg=2uP#{xGOۥ"͇ _`/zcpcdj5nοȦvdֻѩZ |xZ5fPQs7!T3I-)"IC0~ְnڔϝ` pպL.[# {O[wk30~9PEOϰ6(=56I3N{mJLɠ#֤:/4Yqo[e*j+8ANc{He"xIm,^:#< 3S8qmJ$lQ] =*́Q -?'#R@ΎGs|nE=. $%i .#!R}⳧ΗAN:Ox3`u%s{`Sm!Ps~GJۘr%r;z:`Oz4׺ҐFP'?D ߅xwlR$1Qs۽;5\-HoTJQE,}B驨sQP2DeO*Eԅ$]6KD3oە+]}LMamtoF)_TMot0 rF`ng}h J'LYΨbfPt\f=?≔\I2 d֑0OOMyϨ >|{s-֟sB j^;YXl7' e 튙oykWBEe G(ai{͒q(sY}tj-Y#8{+_J.>C[1$P0?6qZ?=p"!%mgg\/;T+}|' 6#vjE+ [C% Ort| vN=вU f %J㸔}lc?&Oqo fMci!h#@,Ft/^J?D!Gc\mc):ߚ >'ss~ʁPrKD\O-w^ ]Jk O51T37PÓx0'ǞTwT:Ö)ܢ3*%au3dcHV/-&wn8BeM13ƅChUçм5#\/- f_Ē+‚3qWBO+\AN-J_+}@)86ذ=6ahLŊV#Bض0 ver/f]<ʝбj]bxB,2zLAo4i6{YyWo~'{ׅ)|L㲭W(!}YC8ߞc qL~o_\vAZqcsLqR./$.V'iG/@hdeTU1Mrx_+-fh<Ғz/AG-ٶ{}m5I0){F{s? AD0!-Cv; ۝ĔZ6+,Qo#,$,ҲmXFЮD$꾻u>Dˀeފ--e0ѣ;t\<Tu/n{TRRulo(,ހ VFYʸZ[{hhI mk++pq]>Wk]1o ))\t|LYP~װcgQSv)gBO;JKQZ}Q,G,`=^-xՠ@ v)H"ԢCRs BaZη+4V&K)! V`LȻQ&|D_m+b٣E. !KT,,&t<=B `;f|ߑ}9)J,/?J_7ؔ5}x)ՖgԵzHur `me\M7Tr 54H* 1O2:R_kPvY?T?gF;Jtp]aI&|`5Sk_l^!eq 0~֢Xe6!,k*%xVQ{H$rϓ2Kj{0xeNge)YxgT_7%VK4w~:yg|U)Bz \IPrV0iկN%~hLfH K|hYI$+mty9tC_BCqSy xgU>d_QJŧJC(48] 6?gc~?NJҧ^XPL5Ays`m~0) aLFϰ`K ^R_ALM)锸;ێUQk. GP }-KQBpƼᯰ̸Nh"@-ՌnI`V0 4?޶%w6Veݹ6#ڄuc3݆ 2NpYHhIu^UO/Ėx`(k%[b[Fuٵ,Z.3Vsa4vk4Ar:KipP;*(\`y'wz[e&xCC}e뵨Ny#+:ߑr8 0T;#qNp)[5>CNG'.P} MYI՟I7-`1v Gᚗp^ǥ.fwc SE?,5ԟ=f)$LpkƱ>C߇50O,q Y@B ٷQޫ͂>Gխm v;rF>.A?$l񌅱O\t #qj! ]zg}tk˄7Z[5.d ^}T;CG[}YMdHdX?;x!{""3#";EfV(đ-$Y!++{edE6N.W{ݟq]}>7FZ>Z>y)xOlh荊Ќ̻""K|6Ha-͜3pi L+#[ z|:[>s̜_{^Ju׋Ӷ49+:v 򋓓qv"N͓"ZBoTe$Z*}}z&J秊%Gń ju/mk1 yCJ*͙.J h.6q6# 5[tE87>~e 9F=?o_*4wmWhQ#ٌ&̴k.K6_[Uʃ 2vZ{՜PƗ\7bQj| _K)^0xxAkӽO%;|/x>j&GMߋ̦[tBphD\T*+ +ky&>GR.hX>[~ M~}dݎr'Bmq޺BBު?xu{c&X+X+pN:'lS]*6D"AA3&{ZŊ}L'8]te^!!1%=ES ;eFu6kb~xwΌ 4'Z+=ÜfJP딍JH=JDR9aZY}4gY2N"OϽ^2ZpNzm.z3-HoSC,ZRڪ.WKJDFY hXԆxg:S}+scS H16h]^L$*zZd1Ӹ FM(ƒI˓#є?"kmځr9rzns}=qeq?h<8ZXn,||U 6'gC'#@wY9j,O)v#fX"Z+ qt4SZIåG4} p-yWcbh͞kU`1 )@?P)hDo7fՒ׳#.CeIA=Vh(Bfhq\el3We21WHtfA1=+S={(_E 0/?De;ӂ}#YգiHdvmJȄoD:'ޗLgZ~XHb\ˆkTnusoqRvXS\>Adu]ݺr(aYHtܭ&T^)j0Ĕ(Zg6"hgV½3HmwVM.NUݮzYGLC@]fC)䁡1@A=,>?1C:I}rtӭhE]Tj6|}/KİȜV@+6l(.2EZdViP/0FX0}~D0tgMW&4~>ۨ٩; DpI41 {dq_?J9B%@;lIPÅQyu>DTx݇>S抖;oTdp:Ӽ}nG`_UsY(poI-66t57 3en)p X Vr`>IH |_n(wЌiln8,oabWGCi] oMb&XqS6ݙ뜁MMxKG .M["-2䳌췟q(Lgu$$.Ļ>^*r#ўl^ hXѪѪ)&& LL3p&dK-Ln˄f*A3!]۽5 ݲR,R#vpC.H.4|& } ˟5I6?Zup]}᠁3j*T6+ Xc_&/jYfKǪ+%M>ᷛ#ʮ[D[(ɣ&^CYeccs _Q^'sL`utL jeG*hEuCH7煺}jxqHN;jm4@;l֧{Ơ49ļj0Z̲ >xc>Kpm f8KiHiuu wUg@Xvq_Y13˃$8Ӂ݁k M՛ Gy?v5yYBvr*ّԍ9YV8 7Mȵ{m$ |p0K^j : Ď`Jf ur}Et_*.SD֚q)]ҽ7k:9 WA f[2ԨV^ |Ǽc 1|.]. s V8DSGKGW%"@ty5yʼn(Rv <:Z [DvU>VCwfNέS==>&kUQ7ͷ*/q*<7E\1X Ez5)w/KX+фߗ))}1ɻ})oJ G[͌ŪfOӏg"/%'Nr`}HG_)>RoU;EY8{fGk(n?z`Ċ;ܚ;n5TTbQQ|"(8{{9L%%" "M%%ۭ)Zez.I:K?=?|PYX필DHq#B)Z& a5ЪSL?1ZyHظ֬mAr{4,AuQҙ%@={ #36nw8q[vfJȱWGd%#2+$2defޥPV~P~+={\:]}=xffl#}5;f>]LJWgCt }).k|WZ PnG^pFäJW;h%iا4z=s j" ffw'+-9N_; ;UgX}fwlq>*'`?2s0cn5yu̓b$0\,-*Vϴܹ1TP:Z$b?\+2ѕ(3; `j$_=Yy`r1ɳjC$yyqഝrK69k"Kc\m'pSS3|>WF+3LF2JdLYL#G%mvL[54zR}\6)43ăWt V֓aK4AEqxd҄+߅Bx}S4w_9|.[HYvu5AB/"]߱gG\H^^rch(k(eElEgkMڨX;jӭhQ3?A\;NOMeqjN 1(dvoԘBIh>ɭfgR\.2+ 1' n8m=R;1m3x&e*̇|ڜL '쐗qtC|M9 f5lWi K^[zFi(a֖]݌ej\!{0;֎h Fڨ݉bί !JTc22'h2Xwu,G%X/F֞`iR8O4FSMSBW#$ۜ<6gU_do#ØKl/MI>æinQiWC31ҡBUa&sA9}{^\H1qܔ;֚{ׯ<ۦ_.gT? 'ء!#$E\wC%O }lTÒvݝ'p⢯JKCո؟-v8sh 2cO,]ىڣ91:c\V^3G؟jqΪh)[ s<|L̴9b= Y!^_NaJpǼߚ}|k{VhJX8KF1 0DkD?\Ӓ-[up{:.>LOV I^#l`c<=&;3v@5ee +]'s;کݺZ -ll?.*eOŁECς daA~6n6=֬‹g踱hXBW`ϲօ2T]L,v+.m}R?qTf?W}V1Ї +4Mpl: %d EOk@},FRwהj@&7mxGx^A:Ko4wyٓf,r'`;TNȁsdY[cb5)6 f%!V1{岠]M[i,s@ڪ!;h֐f4AEt^763Xz}+v@*i^901S?&%T301It {[7RIs\BA BVJa#R>< 4"+jo|tW\׸chqYN2f \B aAm>ci)!{^D^^O[.H^a؟`q5 Qg }AIgY)*T$]ϚzQ5e-^4|%Z֦R2:V%6ݮn@5I'2 X$SSOӖq}} )˥z AɽԢ+Ng:9gx\?S3YG,Cvf9"gqDڢwj2C\fg TfZae}VHaCl,MM}k_04|;ҭH}5$͡oHy!+d~]~]<1xRNNk݊i&XwKMΣFoxF䱲⻥mR Rq@ˡ^Ӗq?΂ܥԥt,XpFp-S8Mu5  8 т5|OiQ}~s !Wһm6qx& @Ôffa ,d ͫɬBݦ:sٛH;f 봺nja8祆Dp.vl``(LѲڱ&Pr^ʍ۴yhq+ٞv-(}gG`>0d}\I;ҽYu#d6Xzq~1ӚV;q"Q]mW| [ MWy<\ܜFد{D1I2 34=}GwgL9NyZ{͘"VBu2ez\?G& "2l3og )2P5̯U١[uf}E42KmϬ]>,g|Ul** ^~܋/Թ.Opl}Vde1_%J;b!bSϺ?@APh^Df$MJg1ɌJ*)ؘB[O ;mR&a"\=j|R}[njF꼌c3o.D'3{]ƛo1l*_im6 %ڏ݅,XA>^e9ҋ{݋gޓ. AB?zi1Xˆ2`m7E Q*bXK'[ѶcAޭ,6X}1^@EUEƳY2IMmWg /;7}V#ST `+>XDܺ>«Jw٭^qbG!o,?Yӝqe8hfͱf'spؠ{L8^WP~Eθfįwmn%!'ů㹵݋}\fhc sv^6XՊƄ;JVJpac'ED4- DY0~ՐXZм;@]iDvSEBeWt[(`9A ?F9$ibb2mZ|$nZT.V}$ /I@TΛ%*=ǥB "blj"p|(d\fF0͌c57]1~%}JkWݍٺ; 9/pyYDr%*qqWg _5RKL̦.Ux__͛ȘI&#_݁C?>DX 3H_[t ǝzo Tl$PH)dGttbzMUT2'(`BzGLLpNJcz+W*=5 4F)0ѻ _LlXA|KegS73Ín,,:ku6;6;QR]i͚Ͳ]Z*2~s[>O㵇j>φ~ZA$Gwh/.X4{hPtdV趬3co0 oEb4s6<ĵHt"jy,PBC0yMsY·(2ijU2,յ:sn]>2PȩkOm"8B Z'u|bUf<V;!hH@d%,Ǡ]'SYȭ-lJW3ZCF ih,w%(>-qD ZKfnцOz ߜϫ,v Lѯ3Cpf.DHKD&jZ+7ixޑïXv V#%9G.gٜUw`\,F!6TQS3sO^^?"<;b;Td.x;m?3#2EO?ՉDmC( 7e2vb5BX^3].]A0@ <8x&6x.\`>۟mB+" Ł @_ʇO6KvF(&&~nM79dh6t`V~{kv<~aq^ S 땾z{Fy˓!;]i_ ـl#5B`h$aQYJɵ|}X= Xb>SbdcFemҥ9#~ǘ&<6p,ЃfieŇb]赹[?0,oL+Ɵ_R_RNY%~s=Ȕc%GyF))~&؄IAфt'zL٤[ZTWv}Eʝs8hzH+NYtϟe-8{_˃RWs1x&~6Zj*pǖ紿OX 촽]M;4IkGfgeIDE+ ƐHz.Ӻ!2p~ l[k!Kl{K+N^,|Dg%cFО/XLibX#{6;Z|!3xrZ˃HbϜbt v ɩg7 s:e'xom:=~6W(Vt-# l`]=HA'HUOn(}m1d>QSmґSJ\lV_5`s ؃g.h"K] ΜӴv=`>ԂwOV3goqܛgg }a3둄'a8P'31Ath'qU jM~`Ff=0("F˜Y5Zbh>S3f\^˨om&\E4;|l%ɃaOң4ЪOۿt~^xds+]_Y7c^hs\rjf{1!^br lK]::q-SWn(f%fTh>re/ޝGC>VDB"Y %1vYBb%U=p%RB![$e e>gr;v?}s4tpF4qlq!]hΑκZďvή*^{S tºA[k k 0,-KX6n5Aj*ɱR1FAK6"mnu4 3uy 5& l lA l kkh9gΆ\S!cJN1bƄ;ַ񿱅?`JA!`c}LY>z%_b \ۯk>.Yo 㿦S~k9ԋڧ [|`94_)efucfPd i 1E6LJO lښ!oPac+Qڪw⮶|to fJߧ]ɪG&sc`j}Ht2Ȍ '5؏' |JjWkj)Bzv&qW`lz'!%\F! l۫r.)3I6YS7xDGqNNA`?m]3tΚ! ]Ģd A.]xj}S:Z^)m%gy$U{N}r8U߽W5[,=΃Ue6H wMyHn3RVkB<$|t*H4:h`gNeSs'ʈ#zMHgM}VeG4s}[qϸl`3BRRZaiGQ&rH\x́.Qm73% qeehJKTl?SP\!&6Rh&iM'tJx"D‡"om-Q4u.oh0峪{fwa _pdι44%Kd:M9Zs- Hjmn.]ղfټA8FwI}P8Hgs%oΛA\v9& dS쉮 {,*eyfY\}Ԓ]!lVWקW42)R;F20UIr:mET/= C)\s^I3i9קq֧ ;%ܣ5Qc)?:SIgtp'sS AR,IM}4X<\;ͪ^3϶ l@_Vjee:QAl 62+4j.@b s{M[Ńn˨(^HZ|h,ӳ̬~]Yh/MUP%{ 1XlFZmn<+gjmg!J!w$5W b+A$*%^l۫=6*5vpMdܣF;tT}ى=M @g?˰\5] ;~(y6΢zd^mܷg2]3w}fx=.I6φ=@3 9aF5o7r23 W^ (?cM*|0"lc+=DDW/y>+P2z? d<|#7(OxbmEዯ7RL*}V C¤%a3fP“ӝf*S4cgxO>~ZrQ0(_'32~V%~_vD"юy[ͧpWZG3FCQ(u#{ lO{/|%8!"~T4ܴn$! GۄzΫ}+u @gXtf ߑ$iZ"^i#9_6ΡKO|I}+VwP7>W`vkS~)FN*H~Sӓyl1ӊwнd~2Y3 O(POͭVIb޾&|jS'ϊKUl;ZSJ}(4ɚ8A>k>Ym[l*6ia˨?rITw 3=M~VJ-bO2aSi+ضWs<'G!:BA5cq+EIHɖ3ba`uM EQ :E8tIMoղH؃4ڸ\ǹ Ktvjfm<6{Ss{ko׌#'K6,<(CS'~`^gΏ5' XkdfkKG7b6dşMފqOMG||Wmh w纳'2n,j"'2seiU mGuEP3筼h -bEYZ6WBWo-;~$~Gٖ9,+ KfC`ЈЈ ^fxt!E=SHRlGnLNkrg}7} +czl}Y i-7* ad _^Pȭe@l_J ܤqP{HpV*q{]ɾ,Imٰx"܅F4UXԀ6cX)I@\D]zڼ)1fS.oE=Kְ ԠhV+ѥn\+j*LANc'#C7OߕDDڟLѼ

^n9+a]8Yk_TTӜ3x%~;Ä^lD V+jz*UuCH>s~h'Gyk!w~jyE]f2IX`/Qm5@)<Y|5v#Sˌ3;ed:%IKAљ<\OgZݱ_|iy͜=FH>n7 :xMAlB jkYYC' wbPͯ؛ }< [4v xZ&1NϸW=|kNN|,x 3"eͺ, Uo0?u%nhh/@ c/DE]XVh g '$G6=\2 P87N(( Q| 7:n%i&M0)mR >\U:,#zti[p7A!^kז5ed'A(pp9Ex/${?yMv5=_cEڝlzXmX8EVOC.b! 4f '@ݗJܙ4^NB;<[ܬlcr9>oT\յڿY\s/Ң+n0RQxJt y[UyqAmNkzu;nRhEc9tw/,T=c8{r"c| [cXϽrZ2nKi[C=$s,­.\5zҮ"VAB5ĝOW*nJ8)G6ԠE(oq,,CFZW^tW&&/ TG$: d]VFA-*]}zN+LovC.&`٬!k+c$[~dJbJBuG36J׍=vsi@&Tkae{&WU YS:[0%~[yLE߂B3C7'P#Nñ.ޛ5YL>̾}t_WW`ˇa2([E;~gg^wVlLw B 4UK< Za{TxsZ=Ib6–qFspuc4[hQ%nOZSN7]1O_`Y~v5r=%XbW`1yxx*nj9RVEx9ɫiP]^McHJ1<`Y:ϊ #GW,A״VnmQ4L8#W]ȏT镽ǛԶkr4;%FVO<҉~H]kQv/ jP]Pv-X-xs#]q֯V&9ϮWͦ49효#F70I6,8vv+A0{ך{ho(Μؠ&za+s\WwhzZwGTUY_OOeJJ@6޺p@dYk\ZF ؀wvk+n ]qpr͖R6HeaՇׇ7Wh|ˌD(1C! 1gg 8 :p;6;{E`~߫ϾDla7DZDMs<n't0N/l|_iϙ[%h;l塁K }j8 h>p MiESN9yHO ^kK5 Er-oZ\p_eBmw=z!f'R4iX̮:;EV=r1J8qCmw5X诲 MY/ %ѫ3dOKY :Md~ڟ ubTVq_>>wx=L-=P5Z`.Rxe/q-$ƺqK4v-Qd[NaA]'↼h}Έ?r G4@5.Rnúٟc{ᎹجGuXn4 Aĺ |iP٭f^wEHDH"AYY:".n+C81sz|Ig$:2qzl[˘}== ֜ќg+ JsfU\+ ZY/?SSRB]3\#"sLg}7-T83›d8GL=>M$گi vg!Xds[? k,s?ZF-%geDjP'ۼ(a7|Bث5p ]]Ԗi5?Zv#r0,ч *ABM"Gj_"X=q"=miƫ^^\L#ʏ3 ~V8?&j T+o˷Kq0W,"G#B_ǒ?tA_ ׶'rM5C j@z|!2\fBOyoMo8v!SJ(yI9;F@ j=sDDC.hFfTاj8tLj[]*3$Hqr^ylۧ^y8?qjjg/[rیE=Ӗ*t]mRg~3Jګbd5^ʯ b;{;e8yl~/ nǎ&ݸmhB/-~ʳ4k~&p.G ޝ^]!0[ DAȩcB<7se\y Sj3eSٜ,"bJfuۻ[F$>hZrC:`@(őA'2BB!(_ϣRFa0 77I4~x'-V*i+|;;/ WS;G8UNO!wX *J rG4:'`հAhh\CCwޅgtk5js,q ~w(.Q#4(Wɕ3j[qα&=@8x^jPm7gZvc^.䩒YUjKrb}ǚ@ j`jvZ2߾s={w縯>ԡ 5IUtE|4ޱ ֣ӪMVԶZ[|G١q+ԇNJ瞯hzdv8xΆja3KF>1ʯ 6 z"r'*hv}aɵGW8Tىhτ6 ٦Qh#zU^twȦ vU9LaAkI;UTUEךL8bgWܱfhggpGf[0t١Ӏ[ XUbal ^:ʊE}k2I )e=Y]G/O`! 2_L N11 }δ*YWڂH%+λ.#06c W䱒gBO}su-z$soVvV6ͽ5c&_&ƍZʻwx251KShRR/!}Wu %}|V_)]{5۬EMfKKͷ$0aildPa>\ ZPyaB+QFca* mKKl:ːpMHe' Ԡ;43ܴ3 .XL3;yJ|PVkGKSf-&fw8_q5~OH,jPjmwT@zȳ7Yљ-x)w\Xo?[?l885%R7>E O9H,}v@>n |*[f״8Si 5n}΃n^7](S,Dnl|J42Un)OZZ"jn6K_?}w9"1Ul~tΪ&5n ֘V^ fA hxK - DVʈ8b_Z%[Mޗ!_L_Ԑ? &ИL,.܁iޯ5fաk-%TrjedӾ:2l0]) wjpkvyT?rzTF!J ,ֻL] )ԶV+!z9^GikgPЗ Q/{YP<+``=PU=ŋ`>'6>2q)9+u@bW~bZ~',湓GOf- aif⃫}1Y!̿L(02/ԍܝ]qIesCEu+iF|' .d[Eu1GT~8~+wNqb ƝsOƸVsؐjuaѬ)9-a;॰PqVI*JHfu>qqȧ!נױ*{>[pe EIǙ婢h*CixO\"/Zw> IEfrlf6a}hXjPjm{"rbD䯫&ǽb[Hs{Viۧ 5m,&({HT* qZ" &6qVWg>)*Q&p[mKiz$3r$ufQd49I5",s6}]3Hߊo4>%L r7I%-PZǹ_Wb[ V[\=}%B5WXO'o@n>Ka[G-/Yf'Ƌzi(1C9/A+%_ ?"dsR%Ro(R+nO˿9h=!xcu2ԀgĔ6R6طVJi@iV)))]DDBYkjǞBd$Qj_Mhw!s~FbO夳85H%EاR^0|2AnXf=6Ɔc)c)+Z±f4d|$Fs1a$"0RQhy ( OuNܗDwGo f~qqطVu33.),),.qF]/_rVRzOQr2/` Z|'Mm~ Zޓ,hAY - ;+Dzd*Xi(Yv~1Wlm鉙,w@{.QQs3/۞kۧE^50 ?l5CO$||Dqּp#0V 6 6Ԁ6cT)CʲԅR-ϣ"lPq߇9'd (6N]ndPե3z[5Hώr6 bfӫW3c+@.\-X+_a@AMOdr 'v?+Λ]\m<)F*jcmcmbvVZԠ i>u\9Irкpٔ?# Cw=;hZ4'V-jf^Q"`Gk| [ m[ٍ9Z 88:_JX77={*۲N[k}Q 3zO> C_G/ӈjJRTO:l6sLԶ~ 0a}}r7{q6myPYW:?A jy!DŽI'H Far9y̴-6@ j]=%PabԖ:5v-?a{|m-a="ߵ]u]5  <+{#R`<3+ =¨C΄D!MgnvT7 fpNRnk7h nc[B*ß߳n^t}y\*v׵<[PMA01<#$0J=JDA^3m/r*ے/媊7Ǡ֞:={^dQ=ŶFލ)XLK3( .n-Ap;T@ͧ=JʡFmY WdP5?)1)ϴ-!Q5(yvN9%CAm5.rxҨ˧ 1llW/D >[/`fG[穜D<氌u–18 Ny OFN,)踞?]*%9ɥ]>=X4Cdfc܂\`_8u`z0]]*E:cm3ݺja%;B8لf3VbE1x݊c.z"tB'm9O| APkpȤ5{ Rm2f3u]nO?մo 9vy;AwhoUNw.;<]Ru. +jW%ן}GQb&ԶVk|rO)wT35̳xG%s2ep(y̜!!Ce&ɔe.:Y=/s>ׅofpZFYpd,}MHqilIsvĵBE12)1o tf}LvZaZπ l;5! M],kqDH UѾ:ZJp3 l? i=ző)}r赒Nw .&n9nw ]1M4؟쿞[AA{N[$a F"cA}ddf'Jnz9PŷCig<111oRbV%-u0\`NLo;d/搉(5ILs-wQg,̹XV+ѝ1c11ˑ D66_~%Zlj+\JBC/wFtFpDEu3vxe .9\6F%[OA+C22+Ϧz? ?iWf ":">sF~[}x?VG`[}W`&ɚ+GKLD1a7DJ<ND3|/Of~1vE8Q9{Fܷ(b& slN[,y4"Sw@Qۣ֒O\_єGgmk zl}͒W1d?*кISd%FW4~gshp{N1F~]QSa;Ί5&q!s1 ZaW&Y#3& ^8͘Tsl бBnM\d+:<}1rO arl ڙM|Y5~^|jQ߳:)JuRgNV h[%[%U&h)_8PBO,MoZ\OA`,ZdS/f".&Ұ~[>z?lWkW|ņ?nNBggNB} VZ71f3f@yFB7/>cV`?>PLr{ME~&qt\o0& @j9?>O$ڌɏyjڽ`W^|$"3&Kc zuzfh|BmKE:e_ycEHS\t" ϱO>l ׊"R9M@\v6l q3!#בIxϓ`5"ɜ`7iRuԢ1'!.RN{A2<Ewy+Q69/ B5;9Gm rr]1껯t+2v AH,˃#BSՆM*~U+ sav3K8cml&r27oYc 6ZYR#Z6[ IV*><S-'pU,c lz)-Uݞ3} 3;,A'*Ϩm;.tޓOl`V}SṳL=N]G8Q`M4mKr;S\J0헵'wt_q a)Ί+jF|X]^E]ˍ9+OۯiEAt#hU02(.+VTȶ۟nD9F%CZ^ڃ?mq*·Ծ_M(|ˌ=|iajmn{;m z׃%+L~m.]2w{20^Immmn-J;aMKAWPoVJb6ƸK!/}2 * vS^UͫtogٳIbm:lfP 4Nq8}8}V(x(Zrl("P /SM t[wE9{/`vR Dr(O|lּ"<>tӜ " ".GjF+]#%ЕZ[ 4lQc}Eޭ SΘ-|݆k '?H0 6lw9ߌ{o>G;ɬj5;h\X,$l F,mǐJVKo=J[}/Gy AS(c902GX((d)~(~3rG6sȗہ;a9ڽ˘N/B=Ls>Ȱ x(#pgEJEDOOBn1< vWt}]]diTηP-⃭I Fn3!gWk6 B/$,q$ާA !Rږ2Q}%d#`қ4FkH4bmpQ/SꁔXb\2$BIZ!${]TN`ۿc9c$uU:6Y}b{w8~l`kj'AIC1מş␯svI:Ձ l{L$CFʩ3B1uh׮wH`=yqÅ88rc0IH%>[X0k?h$"mn}_+g&j+c2;?*8 _&M0l 9S+I_G6gti^.6toz˘,!7+V\-ҕKUI_G眾..gF!tEg9'&g H(-DTo8K0z {ؽ)Nt)s5ʽaOvS[ܧפ"hgTtGGHNH.@:R",֐ $$WW33H>Oۙ GU#^Fb"6ByKsD|tA?F^ *ZEQ'\h[4jtQap"hC@d:+\̷ l@\EB &DYMHOZ@?qwuO}\xO_IM `NМû>:BCX&aLEt&gMvVjiQ"-go 2^]U¿c&qΛLUYkɧ9  l?™(ܩXr]㚝%8n%Lcb$V;iۯ!E2s9"C\I9[BEx6WP ~_ -O`\&ˈ`W~a4g-K?`5M[iJ*#S\P:Xi^:r/Zc6\YW&M]п[רosysErPNa[d ^+vd+i QŲyih'':H:HFu8WWih`r_$"0֛ 6ygUJ؊s6.9)DF(c`0-A6doM]}-_{1.H¸×N)CFtB̻y$n%V#"spxK='dsgDJ_(Hb$ >7I4pr+.a>A'࿧Yc%+)E7g >yB2e3hzjRϨן]C?Z5m *`DSQTW`MdW/`gk@66NCChT;ElUnIB !}"p:["5"5!{q5 -:'a{ T$vb#h=E)^ԼQ)h[΄F*_kÒ1,4%īee\8ɛ{Ҫ{*(|C |[k&bM"R5s39Z9',IkFubvz)}*)!OAO5˅wVJ'ߠt5cU aHzu24? Hͨfh<6si\*zSf%H+MFapfPЖ:Eq^S<.~Ln,e]S׎yjL1jPoRRV2;:UdiO͊w'{0Q^=L7Kh?YYҽ]Yț RmӑdO`| K]#ߵiq^ m Y18j1X:K#O%~!Pj Ь8}C3b`^"xe=jPY-R/էox(Aa.޻- ncu0~pപ2Qlʟl #<3yLV{6)D7t_llA 29}.b,J]4XKD;Dd=>{ >N WZѾ}ڳ&΄ /~۝⟏8bFhM:"\=ļ=zE2 Ny,V^ ^jITT 'Gm[_xf$Pr҆ =X`K0~K7-E_,^N4@F[>ѵ,yD0 k[ZO&Ĭی۬fE 㷟aa_T45<OѾw9z&q&M֏"֔ZP6]d,iFA{6Zޣ'' iNdʸ\'>./Huލ"0CטWQ$NTvX +;(›> ̵ j}5j9ͫFmCndQS9fa$&xtsVxf 9!%^QN%_x,s< zrrnIC׼}$ mcj4ne7众T5N^a#`-~Ɗ7_5˗XP8f "|`> 7xqGI9eʆX w^+TRk:WtWyb&s~p$O_.tLNu~Q묔&U[AwhnqCdQIN193s^%|RnPZ^913iڬ^6ߺf3G6îNC^ 7ɩ=\bH&yYhn ،˱4jٶג&6RϘdWeǨ+Qc}9@5׊̂/5 }\+ >*A:v]±Ȳs)oTw]B?njEc9QQiyvb@UP$vڇ? б xn;ԏ9J)坬 w6ž%cX~l 1s$&&"& !AN*j-pj4^dǢ7nhukLeW1|jS~{yZнӊ ?[%%u\븦՗ZqCc`--Oyy:pSpS3-9垪00q'>s?h~V:[CUl5zo|`m'hTx=h=ĜD(lCW?ׯ̙[}MKc]|{ÚH7ǵMnDg7ug]*_q'cOڨ/Fs2-js *jV>ddϙNTnuTk _"qլsk5$:4SyӖo]f.ߧӢ/X,}ڟV >Ui+*%L.}N"9{ k'rjfijlC%A|LṙsC4E.;Ǵ@(Ie۞Y"q.BZ*n EAlVbQ.GhZOWmh+'܈qv:P19M=.z"Nt 󩛔+MBW5'NT^h~V+ŤÇjtAʢ+bLjQd Awh !Mȏϩrc(|ɭZc|xAt5jn8ԠZmZK3i6$cqfr󒾀ԀN,FvNvXpsr@^D$WyXUB j?=4Qm!l!z9dIZY7nfkAM0yR%Vl)h6&@QKrZGVo2-2Yr 斕[B+wfU_y1ז.9%$zqlm2k|ZYl^/|Ƣx6s6(ݖ;W(p2VU9V U`Shuz/Tsƀ赝$%9wb~m?sH lf<9Luގ~*m[g[$07 l}:kXjn@hfo?=n'0B<\jpkFRN;BmBj)mZ<n, (Kn),lbQyk}~k>rBSir>eLuX.k#wV Y|>) ;NkO2e;&64~M):u۲yskF`u @I_֜[%u8# Pۥ-$F^V|ɕh;aa]mȶ$F5)vz@e@ŋ.rN@x9w}/%BOph9ӂ{!m_tI.sl$YviD_Ⱦp~ sۍ?]rG,,:㠼 ~@Xȳ>عB|| q/p.pJd+m¥HO:ƯHI2/L=dYy+R34ȆT %hr az9Z NhN'xCoH;(I]p.#=ACN0zns6YgM%E}jE PoV~x21ju:J\>N />WKd]ɸGU˒ub>fk4^1h6ګu`;\==S֚]o/*C;v}EWSt.Gf|ANDHuzO_Be!w1 mHҭؕx%ZQsqdc8OZAY3#yL^~ {sLVJmmo-\EHoI7|5ӻMe'޹&(GLڶ7BAx~:V/x̷cǑ5;aoȾha55xdl ^I Ps սV31LbsڡnӾqNI} wCn8"dԕMw.Hz) [DAC4Gs s Tl)ϐ>9md}~ qOCU"kx^8̟M^ypLg%IIͧWiibOz_樣>U@hYCjGeƙ5**ɕV m֥w% 6pF;In9:IlT8`akFcT/cKb TGe0Xv*ot:%# 6׌-ޓRt慲"N4BX*4 oi/P? !<$}げо~ l+i|:@C&HD".W[ߕ9kWaneĮB 8Šq}q-`;\v 4Lwew\cZ.VK`}ѻ^ g2H[ELN-yX j@Q`5MSqJf%pa:0j3v_uf{K5gqO!$E7hsgdtM-ckNs JčUaLn3e3h=qy 80ƽOux/3R/-#JvNnmr6Zhk.{vvƄ+hbƿ%~.k F <\`!?YP^n,0/^p } L7msV(k=s12~` A}\nk쮈Veɜ+ۡ=97?,MX̒v6AFT;r5qSGfkB*0_kmP$ % 'j .hң[l cVP ʥY:WyH@Hf0l6@+kp{%G-T*G{cEI_sH/F^Ʈf֕*Z-yr l@[jd]m;QHZzB }f1#Ԗ<ԯ$#6`K䳞:U~"vmК !MII%T{\ Αi`|!^bz?N891l`kB쬦aƗh,{P>>ߖqx1OԐ3hS4"UR_JL$m_܍Ў8ԓ/DWPe[Xp|qN-ynVw ]_b#) emFyyf_ȷCAʩ7Yk% &.GHn4-|]WqSi@P#6-{2z^YY;="xDmѵӄ6-Qc1q^#(6pe ÷,FpAkW1TN DC FK꺿(%T#-vQLNLf kV-0>9ȥ˥RZ5Uhb$}I5,4sF\v0⫣IfNy'!M6UI?q]FZL~UsB7g'o}\:6ڪDT2{Bz2t2sD1xaKw9;imgx<l`ۑg)[|7dF+&e9 c{:-{a6=ԤKKm-{w}ڳ!>Acs?pݒ0Ƃ{u-\Vё~gcgŠYaclF߯IEԝSf%`7 @=T8LdKCB~hm:5wBp/ tcxYqwxMrˠ3@Bg7z0=y28z-V<]<|Wr"C9aeXj\}onSxxcPnDb(b(DxF((GNNw|OZ?_SJilVϖӢڳ!tƪ1Rwa1=Y)<"c+")sqĵږ`k̖j(0w1a F~9P+ KƄy+΁ l~rԭ5ÏF#"-$HrFd6ɞ}3}fvk3J|jF=JA\ PQJJIQ4^>3u7RZТ@ -*^kYSiRv $ }j۽-\ A++W_Dz g DxMpˆ'`Xx/5l'>t:I\n{фP4|l^H6OC| TuqkCBCBC(FyU Emn ˅y/XXo=S?[Ll3D3WذVCS`Lm"cvW7PBm#9[`;\Ń"A)&ƨ4*deH|ztc[SB.`5mCr7!=DG0-#[n:,I$\8u*q`~٦ fɍd>g噑PFhF}sZ?Z+O8^%ֈ?'B]׀lpczʸkLNAvA8ڛ)noEzf~ ~ܴI.:B\̓"a:FcB鍧(9ձ>Ӹkg-<v|^G'TӃai]RnܚGD S޻`pAk!PX׹tu&R rѱʍLGb+8dp5xhyFxlfR4]&N9s+Gs9c>eF3'OtۿY32N:KY?v ;Q^q9`_< vh䡞AKq*ݡ'KO=KG"Q gI'GiC n&/|i l.tо)[U+2JXUkN \qn4(""fjX(eR LDD,}q'=m[^XZ8"s$$cB:U=%9[V$>\{9~8e: 5l#^&o-4b~SP_(=}D`(2"âN٘pv]FFfOf7@) Joo5)O۱i]Ze׃fL狶QyÞ%cVuӲ&vA MT 1'xfLA?CU^Hޚ1!a[؛k&9)M uL+ʁ4 %*3ˇGN*eIʝ딩y`_vep/EbMzUsZ@ĻJ'TRQG& le[ׄ U7.ċTz{#zͥj:9MNFuk4/",;C1eEA+W=6V)lZgnL]_]M>n]`6ZYZɣ:V{Tiyq~ݺƅVd5|-*l`U-OAl ui&ʼn5 \Qy7qy6iѵRD j9c'م,R{U:'Tr~Y{|W׽,.vKtZ1Cs%qkPVf&u{}}ybVRRő+C+F-;w0ʞ;;5p!:Bߩ| !I젽WwE>[.^N@?'Ns>܋݉*z {H{,?~O#YMY?ܘoŬPpVW~CO~tM3wmk۽*l`qc[X8L2gp >f ^VU ZuɻW( =|ۂ_hdC.OʲQuhE9vwR ]r WfYSy(^M[ͽU0ƀE$*s({*"N\ދ ɻ?gcMߩs$ϖk^LS5 k"DQh<?6g2UmQ.Y&,@ 6Նb>#qTĒ;gx ,g8%`YzHKl<{ϕGu+ |yeS`a/E l_;<8= ig}7XawiL%X 4.~4;#fpT|pa'oZI5G~+_^|cvtm:mM!,]a,*VsVYmc3蔈e OƗDzr),^YdX!шkG(1dablDP@xXs{1g`Na33eHf!`TKQru ğ93wCw̅M=ήِr 4p򭃈 glFD)gqzŚ"Ͷ@x[5N id44hV^,,[ 0Ql2&TZP2~\$YwU/ {3oxվ5< \8"L>'xx]/quvUr#SJeL )ؑؑa$p1`YvsZ [yY\[жC4+;%^p宲jC( X"CwH2y61Pkrk'k'Al~ Ao5[ Yf ??]ۯ޿|'ڙNÜクGh\$]{"j{mzeIxiO@KXX`ֱi_PlF3l`ڬjan6fRggMK]it pZ `UM8_GiX[ lV$UAmNU-yG;C%4 >Hg%-}h'Ӗ߻;fI|n0Fq caqζ ljSp+!agNC+|Fx=f4}Rp>hcLWOfjN=";ޗߧ7 ʸxr^lBNP̹oU ~BnF}v3g`Ŕluɝ9eV={ /q7ch;%-D4 D&|njksb~POLXX^v6h,664X>XnqrQɕĕx5YGb JXTuiiv3guF]̓o 08PNMS}YK(3g>s=!4Q]کQGfΘoF9!SDUҲM>ޯ܇}X6v;Wׄ=©{-JE^ɞm]6 l?Щ?Uعu"(xHې^(nG‹DM?@a`ۯR{N(*!/E3͛6j[̨S{&ˬ+|3xٌ뚭S%iW_lZcOp@4Ls{~fov &lX/!@H< "Z'Jn.fHq>߻:'8Tqn$7dNx ƽvMK7 mU hZ5nl$fG!| .݋hQypc֘ӉӉhC1[[~IiVF 3] Kˊʊ#٫ث[ PK`%bfbݳa^;߹}+x8gpS6+| *aY|Qi8(-3xqъ!CeF >;K1-a\~F%.iIOqq l;q%º'cXY}ĝ}ANJŜA&RRHr^hF< sp[vj sK0Jtd-֏g\[ ιRTKM3Y-\~fU=G8T6&+3yRY`YمIKuB_R>2yH\{Un}K{+ c`9mC|M|R;Tך{kIxANR:e}3`VM@xY1SCYi,} i?gqyXM̓KΚ:\Цؔ`˩0ׯHȾfKd|O@}5ƩVXYx[D뗹ѾUnOowcj@f\:j 4<Y >*D+m_(&IВRY7${3ހߣss[M>}{4׌$@?ٕJ;R;9I+橋]=j`VTj3/W壚*T[n洰ɹ: 1'8+rG6?0M~$~&ܑr&e2)V&|3x0QQ88<8qV"׺Ol=Y34ck}'sֆÈߵ21>_jb X^*7ut&{7쪼gl^z29 UtRŵ.H|_ z{vițtDZjr "5|;sNUq=`Y; ezlPcίqTghf?AK(DO*()W]rpK !7U>䚂 lgCGdGv旴xGE~6mFv[ *囎E*t8Gv>89h atlOx+m c^tQwg)d7;0ci~V+tapO2eLv2aXZXHo^o6 ,(&5^B22?`9O[9SO/ G#CD* oώ ",$2iXϡn僴 l@SO?ܜB!=)=f>ާ@$U4mGM )?EH;2lgԢ~^m(ͮqaBsZRhP:2*xH62GՉ+2 }{7X|[bsKN#&a;%]\o]2;ϊ|znNcB,qr2bS墭7/^@k?~M/_Vnu%6s;Z>Ot&AչrS3^p<!:5nøeS 4sڧG`5d*=ywdi?MGXK0ԠUz%D=t&~IxX`I9aָOzZMP];+#S) ڡ5WSipΐ(W^kjKڷehBMUf%֎c[;"wS9jPU]Jxu0Ay\2v\j2/6ŷjcƩ@ j-$أ*Cdg#y#a; LH͓ kY=)qЕ(u0pAQjdKP$;/(o=ڪ'“x0qm UT),]".mKAՓ%o^tkb(<{];d>OZA-#~຋(N 9W->^Q4H@ 9M f3gOp[(L-(9KAP0jmjD6uy4""]9tP<|DB τw Ur6\գXi&4Lϋ/Iqֶx2L;fWZ*zONz&$ e hxgo`@ZmPڿL|_\A hSr'*WzهQ Fӕ"u(ep<-"ԠOh"_0:M-5Efl#ǾhE(@ jaVpռyqJHnZcaGcxɻB j4O9? f`,sr/hϰ, =tq^O7F|nl(AO>^WUi>u5 Qw07TzҮ? Œ?2ė[ֽ;)=H es*;I4 ۰dllf|}|z"]''C4(Y4kV:Pўz;ŀ"[h=RoHGBD/H% TYV!MU ؐ@@.KLPZjVo$$$rr=<H);"K߬bpDt_j-J'iL tRDM_ CFZ'x{j7K ͼB ~=: O?=Lڻ!ĥE| |_ E,݆t0CNZFr\ztqxC6iU*`8 5 kUrI)Y~r "~Xe$M#dd5)-'Q'b2Q/e7瞣+FVڣ=U/,LP Ԙ قS/ʶX<`BRsG j)i7t]_}^f8^3BWE )_ݩP]Қ#II]=BMo9+.coo9lQ0m gƳ >*48q(U,DG6`qⰋa‘x)xm>fhT%9NsNCrD| fWZ,>ǽF*ӫoNV )aOϭd.!l# M5b|tmTiX^!/[uI#SHNyJ%YQYI֦}>~ V"꫋ \Jr./\Dt#)s;ɁG i[DญzjL5Mŕr >#[&w3v'dN'MBv bgQ_=卭K)k59މ;/(UWҡl#M/MOFss4̫'){/)(3yG})A͠.QPm|oid-~ }xʼYGs_-m3AA h*IZYі3Ƅ[%WgntH3sjPo||ރ*Nat{<`(i%IMAwUIa;فjfy`b@(߾fUccsDI6'pj;--{P/(D1"xkߊo|o9h1Zƙ_fFjPo1OY#jWg+A-$Ӎ989 -.'[rTQ4z/StYZZfd[Y[Ϝ5Jå54s0+]"`П^aưSBuJR$뙳h\hjIDYXԨ}-C;͜;@m7/9''S$G?KISR 8P5LPd;);jPS%,W ֳSĸK3tf8,=?| S˭oPF |k s\=뿧%>ԠOm#ws: ! ;B'}óBx<%e ?S7CdB|\L`^_WKvӋJpEgYVk\QV'eXb$7p;uS1ݭ[Μ_rЎ 1~^9 A( j65 i_a!CaC;]6 4c;]CA6"hւ5䶜bIA590F[aVԕ$;sNf(WՄ9l1UDe4ώ[XYq5.G\\Ƣg6Q,gn5{e?p<+dgF{d*Yݷ*dCFBD2+2Bfvl*{s8n;xz}us͹Jŭۺ.:{!,`,emS['?Fdu~_I9|ܶeIsYf6:Jh-cCQ$:b! cZ_qEHD"mMC{ b%%Q5w\䊽2#w[YKyfD^5C`Qk}]7zdStQ7S@a ;9q0*y!ageexgxwvvW 5w o i iCOwg{VSBO#ǔwSA >^Yޞ^۸(jʊf6sl YD}$z%м t-s)H9hV/:!WH:hc&2*Q5ٝ}jQL-g3l ̀״RIxŐnr<4Ӛ(IMIVK:l7Tp{ dû| t,ZfppĶ 8  0™YzvB[y׳5^\< 1Ƨ^X/G36~j.hb㵔a-LeڷnH溼߻<-U<5]HZ[ƝK縣hQZMěwXva*t7(Y_YZWo08ĞjAtǑ+ qY/fi3,KCǺM`a2gWm*x%_uxZ$Snvyc#k*٬sW4Hkm^aՇIK`j~|f6ēXDWaY ax%q&k#|0o nk x썛W99L1fC1(qAYi X#1!~_U4eq*-H]ܢ>^X 3+Ÿ}?X7ɧ4 嗇\f4,s;C̞94m#Gn\x65b(~ ١((4wk١|f[/4;:*c6ޤҤb_.^q[0Sb4aRb]Ǩ2o?n)ڗ(>[a/jC6X5ŌFb6JZ!Zd5Ng/YVDҷo)Јcwĝ;7Gi !q{k,KBcB|֏n,*Y!aZ 71(' b_~.t¶?JM`޿7KA,9z̃ԂkZ?7~MS& ˺D(EcӒ&!]M!#]b:0? 6mR.afLnΕx1]44? ߧɮtYƍL>xeld@ɧ'/]Y6l~U+bJb NopY]wZX[K9a[d6ZEfi|p#w8J93׋EL"G l%(}:k}r]GLNq%F)P/?i4Lt(a]'$" aKUķ8T`/k= 1\WY|(3"q^y6m6ڏՅ$REW^wL?~M%U{fqpІQ9ޓ!?Ru8dț_-4ub(OΦ lgwYc Wpmlz-)OhUryWL{(Jn?%֐ $yDz\ӭ$C>ԩ4.շ`;Zm/"1"[Nzdqw`\qVT[nT}1ki;\P%qQH Gߏll,/j3SsFĊŠTTfC =޼o6QEzNhŕ2Gϐ$e'=m=&dAIEv^o,4V`F|'$*kP!tLz[>rN~Ź`he3,p}`6ַ͓KulGY nhooŊOeZJR$n /qunA;zv d|q,"2,n >f}eC3GuC16Qy8FR.V4*}$W6wU#HiKc l샌 Oro؎Zm[z4J3UMLghNai-sV Swu,uX$ '҇\D–_@üj')v9ԝbȎ}͕We=Q6g8Ab ޫs $B l&F+)@>ÕXY3&dUEp<8Mۢן]77CÝs(+Î.#8t?_"dUy1;$BZ+kMo_y6@ۡKFkll--p'cU  ]ĶWW q?e%a>lC J'?6^b1l/Hvxu5^.s鋑cHSw4n$$6lL6[F̑ H"ǃR!f\e"`" .[No!wwP%Kb c) [QXd){.K$eiJQd)KMǝ?ߧ_}1Ǜ{p|q>ϝ{M>ep`y ?VSzQBoXžs*2A6TA9-(%prr%'QEJtC 5"%CbG| $LVtץK"K`ˉ29 CS+wЩфsSП;jXQfZ%7ݦwO۟,l;{Yn!ϩSG{O(OGW_m %_yY/g<AehX̺,|3(`Nz[+-蘭z 94 %t-yt1q}S|"3؈WkӖAoiJJSR`|M4YJs|j$ңE:M j N&oMi%hl0p1̫,-&߳ݞ"q]o}F)2S U ޒFl%Hz3W|aTriK4:jT_^#k- _!1zOpdAhg[݇rїĆw6wq\k9c&PT&?ZFMd0m:/%0PƉ`m#`>p]^ڲeC+ҹ͒V1@ w veK sۄ87)$>"d,܇]9hj Q$oE5ֽQ ]=g. 4N_% U y/Sѣ0XR7ߡOrHSZ[07dWj?Y 9SzUp:*0ҐҠ3HwYVʴ9F < Y$*6Fls hWkk\?tt&7зc3+?פ?eGQII"º<1eP!s}_,ve}X3EGT!2C5:,xҙx6pI}U@lg{Vi{{AޝE[փdŪ69WT[bu0-Qxtb80z E٤NF 1 *=6zlܺUgc}38J"S'k٠cWz26-ܗ\\'A+yĆ{ݐ<'HR 4x_}sb;]1I8f sP&{+*bÊ< qFJ6bF[AWzJfD'% R8;?赱w!'S̼@ Xo?/Yvhm;@,T}ՁjI*Vw.(%A @bObj]a~)dl+TTT5  ˴xC1c**6Q֬/Rop@2֮T'S'lR6Β]A5Ȥj?~?y!AIOO-c<6̴n!$rQYz/U|!KFAR\s&wxE1&MaTTtt: >%Lm ˃zX͗9/s"acy >fnl8K]9ڶa엵MC9oGl;4Ć,.rk`>Ң3I_WSr#_rInXcR֊VP߼h=$)9?Nl? u)w"֙w䦒q2uqY]i b#՚zdKsE^Ry6ju^Z+ix50!E]%1j9{|??mlG>IΈpZkŎ/MS ,ewnKe:̫QVW ʂ;-ݯ&֚\mwLC āRDž{Ń=t`jUoMA9y!c,Y[뫕8CqL|6Sek($$̀Yv};r86F292gEJ"cfIdL(C"D!PDL!!Sf NƸ=wk/{o[Z>Z:-k[<8u?5ZNvBgxny5\y;oy]IJG"DǑ>11CM Aec$?ѯ8 HElϔ|NLKQcWzA6N<e a2F8hW}Xe돯'?39UD8=3~;mc`Ȧy`_m``Pˏ'2u7[-oPIrzӛ!7] l &`T+qsv1"B׬?=.F垃0PfGue0J\$nNv U(@V _ ג ܗG*wj#"_T5"ȯϯRN\3d.]VtSSdO%=ܞ`LS1UwN9F l=п_e@mvS!$n繭ҡ:~ X ~O+Ű ?`p526d ]&'zIMBˡC4I3`g7ܝmڭw1ӻӖy᜚⏭h^Ub5V)Vzs٤{U_xWlE'> [* aO 0OH` f8FwCν]ݫ.w mO9[̼uיrKdT冾C4:̂3~/Q?5Je6Q҈r[7 bi70f"4o is2733ĚZí\({N(@*:3K+݃p}K?)S;RkQV]n3;AB$4;MIIG_P}!AFJV&?|2E|EB"!1ttO;;aLga=88\`î\(h+]cHu`cԫB=6\܅oȕ"UOkL28ɖ2ݭfI+Qr,pLӈ*~3v6 6H\! l]]x$}+Y4r7Po ڧk=&i˦IFeƊ}oq4Eƽl`ocy,3i& 9Q 67mwu=>כoۉ>]RWj N~ 64]cw(cF[ l&hzAc疀_gh!I#l|(v{ոjSȲ TI4ѓT Nt< {~~5;A%UJ= ~u7ѐ{"S1 FNö.OM૫a:C1tک pgjѱV[IsU'i])ϧaOn4 f"ii,,ǿ w|B92/6/sjQQާM4".."VZ3:(gֹ ,'դh. 6Ai.g1 Ԩ GVk4Ғboj I^d^78o?$6+iro˨npz .' I6_yqq_>/_\u5 -a*AKvIFz K4"}* N б~v 9i=K5PeP}:`p\0zG+?^RZ`7ic5_--5J}x>|Z9rl)-"t}[˕PQ?-b)ba"g"Aϔ5 ].h:#' tbeG4 ͲeӞ/:}[UV~:ʺsrSuIIͷm뷫&+Ŵ4έ]r[09Xs<ě:4BQg3a ;阍u{皤_;LoڬϺbJR$(Xo@cr@^GՠDnU5|÷gHcBZz.+d1}B !W]Qeҗŧf-?T[=sf9M:o7SjZu1j|÷b*~&yBGD}grh{)%l$8{7C?_L[!aRd`|i<;{=2B~Lz"3gusZ=~+jpJ*= J|=Q 0pP¥J AU`=]]x0kYAm:swx߁ t"~$|)QǥR5t~'x\NMM/5)h6Zo9ad||m|0tt/LvҽTɍI|Q|Q#joƒgr ! d5s9AGZXh-PKvP=_붲+ Y>̙x&'\sE7RJ1̸٬|S%(fq vj E.>\SF8(Ej`EDaCd RR`)w׫DSߛXz5R{^OKg DJ7;zZ` xt(2K$P ])p14NZ]f㗒5H'1n+Q=偊9s{,ᅮpl:(IbIb)y6m>R g*!hW|EO^L:[_dķX$"9(zk5&3R~ TW,>WDz$wd»Ca9i+v됅~߻MRv/NK*V/X2oQ8L@Qin@e6h!o9)>ꥎJV_(d4男$j~M}%Ȟ^*㴻ZP[tyș$v}{\{7kϝs6Cy =,nЬ>ubs^sm)QoԦ'mc-d%|L)zh` }j=_>oh̾F{_fϢ^(Ѱ_~~j"E"'Yaн]]U%Ӓ6_TJ4?F0NeyZ.l?ʨ_` 4=& q#).69Q<ڌ4x 8+4+@e 3H_@8 I"u\F% %:VbH/H- .Q!eQQS;O.84Xlɿvvo@TCHCppj%,Ϝ%mW')(JV^OYq8=0ӨX#֏;^F()y9#Eiz;Vw>'BcԳDy͛7/fY'QYl}v?t9+ Ei @~kp"xDz²rV޽So*c YI? PcU(10voP 1e`Nsz/?%C>0a (Zք=_t|Ėk^ЦR;W }ww;fRxRxzz=7=Su *kNS73x`h9tgGm7WZgEPU?,m$FWYA7?,q2Vx#Wkv&D)f2(v,;*ekϜ{,KW}BUvn~oa}~ĝoroOmi=Mc4$U?4}߯)s" g+lw2|5۩q\xwct٠kev _qں*D%{-o55k`q$V_ŚbgfkȟCx`S5.GbXk*tk#u(ʴ+cyZ0P>&q,LG8h-7^V>axFKw EkBɑ /"G8z]K'[*_AB 1|}0S]mI4I0? Ǝ\ K21EVVCQA|؂{ DDևԇ?g jAn2o|TuȓUMmgUۑ>(Ck.#wųhC}jx ߶ZkH|xմ4oow1MN 8rVnJw,ynǗ}I|Z~J365mYUi!&o2Ԗn*f8ۿ N88)^xuF6㓜v9E< w.Vk0q)d|n5;;0H08:SiDχ1ZIeRY?o֏ne(Uξ/%nMcKX1IX6Dx&eǠ7bp2wވ;Hby)-*{5AWοhUBpq4k!Y-ԛlր9_*\HZ6m @| qIԴ]:}7Lh$xX7Ά1XC|$$V'(B [^TaaGGi9`}kTg N'^^u.t6l_ 9c"T&.0Xw_ л-8:iFZk]zD1yf 6Xomf~3^/A9 ShQ1h4ɢ%"]tcӎD1X-X?@hȦZooك˗Fdݵؙ|yDb8e[Ѷ"Вj(BD6 `Lyoǥ5 /~WJHw.ն0W-3SZnޗ'hܘWك_{ pPM5 X-2}ʚŚŤB"M"p5^nBL|%m&h_g0c2fZY'|&޺{>hԅEG|÷Bs_Otő#juW* Wtj >}"o[0*!8eoSֹUDBuv 7W$='״NQ^dA#il4$稻k-Y 9^U?W|÷jTs{9OQq1ڷ569/RGk!AwB^~G@ i8Up溱p?{HӢZ: #̞p!W`s2hUBU8>4 fkYwcFlmZ [H"GGXG63GK6apr=E(6po!#]#P6|9Fh\bTg&.{md@-* zT0Zf |'u'q)rC 6ˎ!\ FR|P904o8[ LHd ̑THe4!2iS(3me2+"dJODzvuqy]zaZt 5o:ř% ڿer٤]+>} -EʨKKh5!vobhP*?oaw\$P)̴ٛrn3*kc~M&Me8z@ʎJXPH?ҴL6z\Ȍ !l$ yc6AuĹ;IiA[9-'GP5&9*yRe -PW+6)݈n z::)Cs9Y0疹B=|F^Dnny_{bIts$um?w'ۺY ƾpgty>s˶Y2<*9BѶwaωW0QŦ۠=>x?I5k7юn,X_~,-!'ٗ<|jP'z޻e轡#.S޶,HR,>ԠZRɥZSiA#1S \&bdH)bPf0DΨ6<[HV|)ѿDJR@ 5sλRR@9x^M8vhzd !^ Nگi%0{ g#bC?0MVsSPw7' 7;tZr ZN]ӆԓ4S_~?];)hÀyǼ&DH[]^1~l: S~D:@xlQ>m9K҉/_dgb[jm6I"kiIɷ;{X)Tv1#d s"jNA26R,%ꞴLGTF7xIȑFb{0Pv D%rn=@ߠWvXZز6#3-E`̴;qi‚+>@H"@u7N2ȿrPRDdrkJUYfm2En){ai{jo/^j"Ɯ3zc ,{3~ryhX^wۂaotJt.&z{_LCqğ%3vQDmv{=XL2J7DF2GʱK`IB۝=mj`J'9}C{(\fnx6f<جzWuQ'F :eEvAp~:Iz%#2AP-A\C ^ ,/SB1qgksȹaهݦ^N4tP7)6O0uu #ݑp7dJy?_Jrxekw猘{W5tk,킡aẕC#r&9 h]"2Cm緫Y?WVEm'Qcf[)1% n6P ҇/gٕ=O#$/T2?IgXܴ|O4ԠOhS^_3/{$<:+ v A j;kQfËg%7q (ny<*WԠy\Hf(x CN2ήSEL>SF?X n6%GZ`4C+R`ΧIN~1iש[˰> _Kˠ49ˈ71BOףXew;Ki]hC,ӤX GE|*WM^W9okn7& φ=Blw ! ǐ2->t3|b)7$*[Q͜ڝYZ71`=~00 r/1[tZE#`x5P݊=IY[=\,S3}?=wnqbc|ȸWI"n V$HC˙< QT/ zMg<Շ'Է2YADޚ]s". UEW6j]EtC=Y8>aX,3z#jV`)sUNjUsa՛zm %s54Bmg:+J߬nk~ˏkЙܞи*ny1OSnOM>,B#tUJ_D&N`kc*!I$Mː8)d~YrT{Kh > fkhM ''E``j>|e'Hy2HquDD} @otin `wXRlQDuѴ諂 ѕ7RjG W:\C}^3.UrѾVlZòKK5lV+1ݝ޹vf%ytssլ "dpmq3 دQIVT:!'өRqZKᶳ<\{Uͪ,nxhIy?/vNUQ!E/G4J2z/UbtI5`_ Ou͓p<,Ȟj`y'}{'[Nā S=o쳎} lXjrՄoCxeA=%źÖM(U+9Eҏk^%ckrVԙݳDYeW2`|G);I)O|rr^MPlp#>}@ B$3"";Ze:ݠ ~ P(g)u98Ȩcxl&Ħ8qoJ,prr& \\8~ũUW!!ٲrUU3q~l32:?lw&i{Q_AA.dm~qY-UoW]Vܗ}<39L39y{Sqzn֓\+ u[1e% M+iꏮh>yM,!:mBޣ~&߁Y5x^{ݱmFx%Ćx"{zbG{7=n G|?n& O-0U^k;R.j6D}xC%o.+&ضmptSϡqx=z<[@}4Tx p7oLڐڐot-r-rC x3@ 'X˻ |8;Nzȯh7r۝-$>gw8\5gMb5E.s[BɫL7ԃ$$ "j~Io?E^74 ߻Myk^1ƈPTeѢd kD]_3(t19ǶZIeDϗ>a0㐳i/iYg`rs.zGښ3 ״W?9/e.EIi"ˇ,w2*{ڛ`N1uǣO?J{7S/vx,(I"DuS`~WWlE*<,&Fwg1d׫2,V<0~vZ1Y"8/wBvpmUzWg l!Z(*/ 2a.)}VI"VRf 12DPj_`2XPݬqqh$~3eT+M*+gN()}TY _8r8P߇ X`FoS߭zD]ڞCыы+9]oB;C;#Epx<ÌN,<<^^:ay&=9驗$e*\Z;ǀR@:e1G_ szJ% ЇZsѭ}!9UćwnYnS\sڪ!(NnFR5MO2Ḟ ohyQ,eb¬$- sElvV'=WNnP9}Rwe2j0jp @*|d[܀ن= kHbgK8&s f ^'SQ;O%_%_?e :y"y..S@/sB?1ȩy°۰;L|C31ק7 u;ǀyvn;[-xyYFN<2VR U`myxAבapۯjWxÜnf L9)i>p_ B n2Wy9fOK6~׏ܘjLѻ7np6{>;AiO@R4+K¹ъ";ݮ4tͦn)3y=GԎc|tn%w4#&֝#}f㮠 OI?čeu'2W¾-%nMy17L,(?xxY bsuh(HH44YZ.3{:,X*<:!{w5tww(qGgܐRu(QInC~n夻;A _b|DsCUvZ{ej#埡q1ldEN2W茱V2?~IIhUW>H17O9ůjܑrK n'(0#}DZJEFY麜ϲgHSr n;qQ8wUN %'԰q Zxņ~.6-u7Q;àTQBVvk?q*-j1@IPQĮɠ]i$@vi\z ^ӶzʪcLo3R d6;x>Q yGi3" |cZ@ReS**f~ { Sx=J].}(^4+1H;(Ljec,Ϊڧtv+5z [3idըatt=RWzhѬEByybJH$'gk]!H~Aālh}-s8a#U/_^\叻3}--GMPWt>n\sTjRS ZK|JtszS) SkSy(a~m01ZEͰlQP̚<8b.aU'YHа"n`^xc2b`uke8.1؋{=T6w&C~zkG L L G]ġ|f}fshsh*mucH؛wFư77fTL0{1qou띙m'%g,._ ^,>ɇոKSr_.OjFՕ0 ꝟLy8T`rLrL`᫛Pmh]Y!?n^I͋7dt)AT**&eKnq|jt%b )X hD!6k7Z."{zhҙ2%$jH."}p[m5,Զbzknu1u1Llr<c3n!B$I\)o|m棭'kXV&Z'@ jV^.t|TTJWN<樑Xŋ|5x_Ijegd+giH%;,DؑR8e ҵB j@Kg~B!?#Yי.^1nS Wu~[{uɳ)U]jA_%UtrޞěRSix֚qza!C"~9.Th{ҷg>`j[ժ93F gW%iU\S/)z:=٫rTIŏG;{EvCG}y%=鎑WzHq^/[oW5|~2s =۝ϙ(C0mNyZ, c;^cY_'1!a PRֆ" Ȕlϕ=W +lIlԔPJէ tHimuZ?<_ހ$6;b/WmpjcO7`w^$ofj?:P拁o[s=m53Sa*%շ r+A8Et+?/[/n?a:^iҳS۝CF+:7I־ܮQ˝y$IjP_m8\7}=;M&M-/  M@ j3zMsy4r~pJLDPYdv&6/ǰ",ד~ rf@r#'&Oz8WH9-1FVE?;j!6wBr7s[ ԠW[7{Mh𲋓H$Bʣt=MqG*U7<Ԡ*<^=>! clW~E}?u30ޝkf/"m)Z5w/D"ҩ ]"vU懢{E(ʡJu!+w1y4;k]_HJfdxKk^x9sJ[tͱiRSę63/C1bϺPB!t_8&r9 )Rn<`~Ѓ~ .0FsjdO%m86顆HK8A8 [3蝺ޡۡXQp:eеn _Agn^-wgede0G3>^3 W1wGj,mg\pc:N(Π>ժt& fǢ+ktgYZ{Sԕz(Ş1]ٿ׸-UZÌv R5?dNr~kp"}WS6'~J`=r6qSY?@̔=#3ڷ^d9HH垵y5BK.` V5 6>w^ތmP754tS}hz!PG6Kv {T8 61;M5_An-Z} 5)9}'ۆW!Nͽ[юw<://AAd0D31Iq@@hiIG1S&~;W_SJ{ ^ IcZzEsr'O2"篴17*6&%Fo VTmN,ULY5) _ɿX%8<ȵLa7K9%1j{MWWRJ},TH(h.48j`4ݣG}Aj_]n9WJ#8<]u 1hloup9jt{>^fqC2OYHڛj 2N=6B|/>n mOnyvX.C&l׊Z̏SC.hyui+CL.sӏ|P/`S/E q`3.[.ۣRnk" OQŋx fԸ@kA\-%"C,L9 1=&xbSU޳.8$|eӾeZ.CEHn5zPF& ttoDDy-y- ߣC:b0kԀ<7i(zwj;(q Jӯ5ϸj^ =+u@9uwP4<߰7.:9 OTص]=tj\+05ĕ9Ԡڜ/Z"brC=5aNéZg,S242I?Tდ`+Y`J'|bXnU#i%rjP-LzkjuI~y'or/-2:JNWvBj|.X2t*ԋ15Z;yLz+؛"PﵓNO SKVAk(E(e35"^J*fQ&7OjvVʒY"ݎr|Wm_U\ПH/o-r4~_ lhѲ.%߬G=E}. P)HLْ+eo 5e5/8&!cUsF'aa@evy1 5,5 2ȶ{t7jmr9+`٘ h6kk~;pt]PO3sS%C8`H}w@^-5Wc2J~~n3g-ALn _ok`gR7Ņ2Ea=ÂuRSW6ANu%%2OTrszdDZGBN.iFBR:+FĻɡtTs;)l!תPJrcOM#,NNBKh<2oqDLԻz5C<}L;5~@6$yoR\@>6)2&;}8S*횾!l9v[) rWar(A#Pbg¾ pd`8b8q#U&՛[1/OEweiW]f_mTWʯf%щ3rv.YZǺA+K+<H5SLIFSx\۝J;N'Dcܣa7YR(YH>SRR+nXB>tT&bp3$*w<4?n_ݸ&ahm˼f>ާ"W,ʦV[8M4`+'5-9t4BVuŹ+U>8-q;q'.гw\m:m1#o8T GVeݬ=-tKL?k(ͮצ<y`p7i쑜P?iskF Zc:C(:qT@ |/,(Dqe48RK^ᗉ9 raN_x7!c-7z[X,u v~|r Z.u$lkt\΀#%M>ɬ\% \HbR[~C]R]#io#heYtYGi([+[W2JA"AO[:O y ."ЃYDS/YfP{;3%;d`@Sd_+o_rɲza Pbb2r쿏ēo"zaR_7VLt KUUnwuĽ,&9 #RV#ɵ&4D#~!=(ds"U5궻z5şiǧ>tgFg,` OorM*6Jm I]q - vvYq[A<7||I΀*` 8~գ'سуo}@'$̊Ȃ1쑤= jj0ZxV壯v桙j_0z " `cK2Un ZFs/jjDȓ٣g*ܚ|NT˅i"};1Pg; _Y.߸a@dn-yYo輸}Xv>Ԡwy~3g/}v4/qRz~;Ԡw@[7ߞi;D~hA|DZk=C#js*>qW w1%smp㰭Cݳ)L*Jp7ԑ{x==LNa0.*ˮ2oS l;\ڝ2H\3֪-:mVsM[X *i)$)F>&i=t%NsER`s$ Vq$0,@v?HieNp|h4-@!U4XWQ/ۺ@%骑 /7Wb>Pǹn~sLp?>\d)ؙ9OʩqϞa<0 |UDADGE-z[DLygKI*`.R1PgW]߹MOģXx-!3;a򒉼17`P5N G$oiɺ #5p Nʰɨ[rAry_Ja6u'䰓~lv; dzi7eQܼP[xΎ&ed:\J!9JsߕqڿJ ~"С^t|Y%R>BFI_Iy( _qR{k焓 ٹ',oE|7ڔz uOѤN"ȏW~~5tP)Ш#N%jGEQMk?g){"Pps=LNS?XXHn; O%[TިCʗ&=JHPщ}A’䯶T1X!I}ո'EG0aP8'R[8$(/ZY*M\g`bzՠˠ N;NP2 L٪^twvtvWTuUok$RMi/^;0o۹{ƸbW|}/\wam}]0_;D`v3)֟kpȼW7nKkfl()TL>Hi1B՟ѻAe* RXƒ"c| >YjִHHs E 5T@-M;݌T"ȼ Sl_}$$' dV4eN>Aћ2x'䐙P cBB6/tUMLMAӃ M@QR R>|:EAAqY,C,0zk(xnMyT Cg}*nZyv%pؕU5{A5-cN}Ĭ[2znݲ a b<ͯlM ak3P{8Bzk )[@,O3+ s**"-!EAm&X1bL=ml06Fʡ׎D7IYOCb`́Q}%zQ?N*^24hOH sZmdžԉR::xL류RlȟvBf{Lg??d{GM I ݼASsc]Æ}KH.~OD`eeцέ>bK7n@o(6%ʰ<Ojܧ$pOKr`Q٤&eX{IE[|0yKǀ ˸ %Ga;`++w#AT11G˟rG7UGJJ|\~}7Lٚ)WRpNHP qy*BWA_{k6y`)\ŧGǂ_sh(c׾gv,WtjwyXpk(W193t/uzeo໘?n ̓گG[gh㨲Lw_}K1nu|9P_H">vm")Y Y OOZ.}V+ ?iXsJ\Sg9~A>.IsSrt_k P7ڗRt&f$ &@ jFs3Ϋ%Fd8}G.IZ{tC5wNmEz掌| FV<"0EIi|gdFrP20["1t,r`p0.""b%x55,jf͠:ʚȡ]%[pXW+8v؟q~ ];GfU/ kqei,BD|جwa1X~(uExpQ>iG pL¦fCJ"K}01-D>һ kG5+Z gHUH d:"X\ul gGdpKD2s6+"zRۇڃ a D]yXXT@30>O]57I2{2{!?98SB~ߨ%3c pNvhBLmF)E?[J']t",٦bq1I\sXr1tĉ}B9&Kd):/C(Mu)7xz95"c3cP_V UwYP3|.)k{}}CŤR!69MqzeCNH,''To"BJ^_P{z$AXԠZQ f OXb[j鍵ɓ1>PߡGղxY!SLUHƍE@ ']Y$+kc(aϡbɈU`9,N$[sba K3h[[`ZqA j@J%JJkڢx\{cjҗ a 8MNU@j;ݛ ʖ|v~]Ʌ7x~30SX(Qxq@qfVceNN["UUyfg?#b 6;-ϩ{+UG)||]=!; 9rkbBQC;m)x#8S PڟmHoԞAvkW~H.S*l]'%k5jPY͒Z fv$EHI1avR.^ @kcWqҨwVgA|v qjj +B#/F B:(%]_=}L? BG5b!G7D/LY:%l V᭧>[[ՈH7xRoZr<5+5+]>8;3qV䋕q nfa{FY߉ԀFKs=UY2S```f/pyD׷D6i$P;.m憲W|KH%1TnMcqVF'3*PMjmG^ŗ;7IDZr-%TcownסCErFN7h}zD1p ?KS_PQk7:YnM 2+_sT…(6麧|oE~CBsԎZiN /' Ea5ߺIyJKmFkZ,)%_O23~ƽw]j?^Tvr*H|zLm%GWq. #iq^'L4h9# { Krٙ)߿C&Qmޡ5Zs%bZsN~J7՟i.ߏmS]nZ5lrQs|F0nC+yJ Ԡ4-VZtÑs5*y6}2#k kkW<$.їS¢ʯk)Хz;_أլ^+I&;+7SW6LojksMpjͬ vZٙWN)vĬym&٥-4s۹p/}4@N۷ɽ"#I\"%c>g*}R'+ρއޗoGF %ޑHHb%okhqU$ ZP}Dşd '1"1Z7j7M?M88K dwaxZZShCCdQaQaߗn¿3\L|4_я1Rp 1/|u5JZEJ Wq=[r̳\r7&α=xGᄂTDfUK Y`Yx!g;)F~8# EE鼼$w;"vr^a~NB!2dWeV0n0. cU2M,K<xgZ,w I AZEs?R'tu;A)XxCV\V|. ^  FZi?~Rdns2$P#A˜e!)lϺӋ)Mpe:QTyysZ:ׅ~W  [|ot❺6e[Ԏ~S؜,! HѮ̥\3sY 'TTjaࣻœc2/[33pNv;%؝kāԎZt* +C=}}*q W 'IeO}tL]H&ALI4 JCw%P{H o\Q7Ybf1WkI^ď 5tI<>m57tCQ*ƹbI8v"}}q`%P;ZՐ8+4wPdz$K%ٗ(MȾ ]ȾܲeD)D"YdMID*BܖdJ.E7?gtwO>/3UG,.=վjF*gI& 1e1x&\$1]'>n4ߌdT9k?d~°Tko{Jwb$i3s~GP?yIdi|D luΪnfgT$1bhIiOQbэy?'{O3jkJxQζ B~_Lm9>~Zwo4IY=>(=E~=R+$Zm)ʿ{hQ洄&[W$~KQCLeY1 VVo5É j;7ģLJ mEn{V x6Ak5nJ#Z1O_ 5@kZ*pU)l>zi/+3P_;Fciғҵ;pؙAgWhr\UjiwRkɖNOŶc(U W|\{/,R_=nW3DFfrkģlJGzu{=o,ҙvݗ(1X=wv[miHnh%ksa(cz)i_v-т[њXpyjDѨ =S~pF_G{gRƎ\2.Q9l޼oV\+nXwXӸIs:8Cܸkf 'kЏ'W\3~k<;Waj/fq m vMބިSA駑'ʔ8l#]dn'ە`-?zXAr۶?!gn5١rPBb#hGriXmM1uWkRp!1ќ@_DXuaHz,lv*mܙvv<[IVVd&;#6p|$ )"L'ؔ: [/gv%SW2\ٚ9 ؼX ))5lL[9 3L^@I]lcc||/NLzaU(^VX9kYYvл*N̆rU);ulVЙщꞖn{ -1RBHh El7/czdVZL|9h8s@^+b)ec8n`lz߃t F0rʛZZ `yEIN*)y9WuI`B`5{ހ+s@Z̻Q}H6Wa;"2Av7Icl^Ŵ ^c%i{aQ$[+ ԑ-djh}F\cUU?˖˶2e㨙zSE1;k~n|m~SL`c&pOU:b1XxxDvu7KD_@ ܾ%RSRSjA7z@T@Dj(N w6Hl͆g#A}-av<䍤ρ1=qӷl75sddu- h@;QBB2ȤInVݬ hYԩs )3 q٠+.2>ŧ{7)WօꎏU[ 6N;?q;^v6$HUAJkh:6xS̷~ˇkW͚{f3Qv1 sb8i3o~uR#ƚ~tá;ܭ$}Da3'v=WK %D35?ONN>WW2auD]ݨkR>)QjwcڔHWŬXJRa '"XaETPmz0t,&-UMn_}MJ%Bdv*kϥ j(0rMX0E6 =8 9`dNR$Q,d !ѝ c &EH4;he(0~ 6퀓?k1fϷaeYdz%ZI~(C;C;!!b RL#$BPēٰlX*]]jC\ !v 'L;ׂgL(ޯ@mڊǺH|A|~?r;_ϵ8ׂ&k6ZBݬ|_Ӧ Eqg%_k)tr<<_׸SM,?p -0)6!--"OH6\A B^I f1`C zqhiy%@#0![v-$SxyT 0Txx̕@7in7Ϸ?vJԅttkk(yS`bJG7>*JJ6M63j~4#Ch:p xk:2y S* 5jBתrHgc?թ~i.i22J>-̠̠kXD&46 eI>nN=>>Oyj>AbJ)buD+}oFoEd8>Tՙf^w$x-nX j wǸW^8N_oإoHj)[|7)ݓ~60nYK&2Yz^ѪlńUf گ’aDGm͜ 3`+\P~>e-U~ZjzcEA9rayKNnr2^\sB#oq*&i5qQ'9l̢YrK?ĴM pWc p'AS^srkS0(g]6/y_lfblbfWPxpL4J^-s+!!zy"6Ph; a? n9"I|=^MW)-]A aܭEM(u:\qqۣGȳwP!-K!K*e;g߷( RvkH$,aIN)d-r:qs{fs{c)nFJ)#GN-yfX )^,Q?/'f3iҡio{_{\U]E:w;7o7&Nèe}5Еi[8?/,ԟ>UӬ..uO M_L*n1" $x.Xo+Xz62bM^׺5#؋)Xj_kl|TQ'bQ?bZ*2iQඕR@8xKևψց{z[T5󾮛F\25tD3UAqeqeqeg񵣱B7-iP~#E[cIMtm3%y8SRCɝ$-%ˢ،WyuGXtB̙[t h&<-gVe{5w@S! FH\4$wƦ<2kƁoN{giW!1rL*6mNPcN#W<_5zxuG/6P`4[ 11iy8~S+7}]Y%˸H++ |m|mlfaVh`Ym0VzS^qg^E&hE^Awm$Nlek˪OF)&ҵu]}*0k3P7BPd b,M~UsbV&xKj{jOG3n| }zAhXAv'vWJ̶*Y EFBf•{_֭o).|!>$DY%;7}'e lfOחt; !KҮOWv[[):CڰOMtG$5j hn tMq|5b&ʶ750=y랔hK<'~R|L֕jB'm΄K pݍ$\7ֽJ+ⰬL$oEiFfŧ )ɀEu!B0ub܈XX:OF1'ֵۯ={ h H&B%,ay b-/*ae\P{0}W kxjWV^^:G\\ D?sa"ZR5>RC+|/c^)mV@t n~fQ"<Guޮ[ᵘ;owވٓmߴ: chP?@ 릝881Y;Vq&*Kٚio@zVO˰SB}[1J$gGtn(r{{>500ƒ 3|iMhM0*Xe bmgm' C'$%šfqY]+K&+6 zD ҩ]>0G6ظHTP?56:lB&-VVQ Ÿ ^6:6o`)eܔ 4jMRZF4\яVoR B+>N8{sTaLW?DIl`m2-S͞Zf%_1OLG6Fiu 3B/X3Yu`0:Fb0ݠWxxJ )4a.]a{=m׭˰\MCPd{g X z)Dl#FQw4V1{|?%~j;c`ax ΝSm|&.iu#gkt)oJ}{3 FfD g佧*ԸI\mQY"{by[\%z_eet=qPPvI=Xy%=5 Y,ll7GFbHί͔wkr|#vOgߐT#2oxY.D6NUT1%ː3I`t`+TǶ%t6 [fב\.9 <6ă!{co[wPSFx5#u R3QA;RK_+E_8+*u-«Ww^l_{A΢U Ҝli&g:nωn7h6jy'0Xy|CD(G)BvbV6?2юo~vykȇ;"?Η%6;Gug8ԅ|'RoGRレyū >0 UKYVs˫?>skwFhd7^d!y/?0Lˀp@A+uU2[y\].lb2J욀xװ&2 -11-n6,:r,XU)~kHLo^z{bM&ƺe*&ֻ{MR =uj&޻/4^܍*S~ea%$~4ֽ͘+mqЯO +TfS7ː~)QAI5s} JT(!n#,Ǭןl Nyͽg/sqX捕*! *X1XTʇbҨ'V~,ElPp7d[7B>IP{j+% ,E*/KC͍77*96 @@F$FAܧfʚ=6NbaE܎;=κwĄ~lG͗{Z^[)!2hZGZNo87/Uvs5@6E8BL9T3_+{ntKMjŬԼ1-MO)l`mm#g1I2%iaަ"S~/횕=OpkJ]bSmfFUc^xSF3j-3[:Cm\6SYvӍU&jrb`_mGE[sJ]]QMAI[917G(r=h󘈒Gl:e\jI QoLegӜW9#n4ba%lU!nznjͶ^MʂV_rcmBd$>)9:A*v{VK6S_2K%A'*rEM N 8ne>D9D2CXdg2d%2GdS_uqxz<>|:lui pG꣐ƭB~_Exx b-r$1߬B˂V)~tUFܝ)!ґmF9w>tź$t7CB[ӯk#V _ %SskMm[)f)fm%S4D 8< s](XXL\@*NGw41:u+N9}0ĄRs,Q&ߌ.a,UБRS[ ̄rq1'3'μkDC#19STLClݣBnt'_; Ggl!>$5@>̹ڀ5|Ə~5D63hST]&{=/ .y_11G]s/'E]X32mw"o1(Md$zz_%Dḥ%2Y5߇fzK^l6-Z4U]iaWFĴ=ACҐ9İ([a~EoqJ OUtuВ> K,KG{Fbn!^T2 4G]sDM^~2X`d`löjSp릆r`(Q|}ƽKm؆&ް_APOc'ڤQw&cZ|3icNܐ~h%tZQL6WVaHA=PEOǚ(oWQets2-L12 _.,A> `3O}R? m)[t}b̼7vqv@ |BYC@Q9aCJ ((Ҩ3fгɃB zɾqAO{8HgW:mT&:=/{?=g}qQU=B,>X|=* suw'\'ytCnKP==^ȐאwޙzPAy^Ln1wuG.udK|4"lcY}@`O5mFcls!BZF\Mii֛Nf}rQ"~B/{ hvY@Zge :k88w\(F#W19nf͐u1]7x_Ogu.s%֥MAEM@1&yxrÕ\X*$00gJ0 J{%Ga]=؆mII%+THb!; iG982rMJa=̄KgoSN{6lG=\3,1VJ|.e%F{S{ھG.ÛEa;Z~In%_~e$[S <ޘ^tb!QU3_0=˜Rj1(V2("?T*A6bKlʼl@z <};gP&m,8b4~{rLGu i+eHL׏t :mO xqK EP)7+7(`1&Ʃ7Y ~nsurpƝYKMLIl{浹gsK'2-iU^#f Su nr B~B*6`/^mm~/^d|u]R R R6ܪem.}O9MdJEE# K֎h<gP2`Xdp@v@>f{&?mM[^7Qxxb.F-Y,,fށʟN&?[!nPP ''ml*?f4LQ,BU; <;kAgȭ-]Ϫyg$ͱ qBo}hcr<ŵ _)Pj,D$]t_5=Wu0!^@EPlg[|TObӆ$9?%i^h~GxgED ^ )enEavlt}zqza[RcŶ~ 5>!~Gr9 C?oye^^jn;ID>i+qzQZ$Hbp=,=6Z5d;BNk'csSQc[i}n(sW. '`v,8ݍ0É ^# *d|+zz[2GcKTpVG&w6,&F@7}ܯ 7It&+DVD}.&9n$JtĜo99oogڿD2[φ25b>}m:'I os(i_. \:mm8*x` C/d$4p)+z܊#6mwSͪ;(lWbwoPpAVCʅG|~RyVݯsj8A &L%&tF*=Y -Jebۯ`G ^R]nJG4 kނ~ v A93b9*#Wd,x|N dK9nr= D[kp/}͂@,xJ+_Ґ4i KL~Q16lliB۟7*ֶXN:t~!-'Zs 2_ʡ|`,y:%84aN@0} OeTKIMy€8 I[rwHQNΰbPrTI3 0C¼)rA6#Aw1s}O8\-4lgwRylw{UA"?j-:w+w=w(``Igem>-%0-ko dobHpˬ:jNs_Pu7m- ,cfNcMQeT>,Rbُb 0\Y8L5 6;@*w/J]bLS2 3/<_8[!ɰYA6<62f2fVLLcʘȔByg~l>>{?{g}km6Cl>/b>smg@a<U `5=u;|k%vb1\}g8`Iy?+-a37?eed&5ɬĬZǵW'=@[9)Y7YW߯ffQ>!5\SzkV*cńO6\{iOBˮgF>WN{þxX-HPmt:TK1sьYN[Ck^#>]:MBkVym9O^AIlzۄ˓`?vbV?ܛkJR~ qX()Zʷ=>|jP;ۼ8mU,$*nEuw j3%ڿVFEOD$a((5x5bEYi:AW(/.;IQvwo8{cei)iuAiԠOhؠjvP|D %M'r[ E=~UKWX rxЫgI,EE$Īg7-|ԡ9m Ԡ(tN5 s$BrmNP Wu~Y{y㍉0mЌ8n̗U'aAJHV ӿ< Aé_Q-g<]Vj,~MĜa{鬴iK85"E"t{FTU 2%v{ż{wD=8bED5!ʤ\ɒ7:>bZq&|zL0u`^0ߢ)SD@0}KIVO)< uQrK5l6Q9QxO~I䆒X 9&w%ց#SC3C3:*iLs)c;.u.D)b=}#^ L(8a`KvX>;.SeO cze$d$8I0 .z|18!ŦK?|Tnr5uFIW^WuC9n{k.2\9ecD}tTjӽumF\IC$cIjP_mX, /vsy5hSJ|%&?}W`AW5C8u{G%?9:A=vF,euH$wZzD?e'.~k4 Hm{v3fYG©gA.-ε+/\ѥxҢZ5BYKX<ⷾԌ"Ș-[&O ^b9sw 7f|f%fZu %fX78,cی,,&tZhox<І Wn2@5't\<\Y J8 Lj _VLZFfC e2j1ѸSٙVו9շv˫opP8nWbay'I@ j{4M: >fVN c7]mʃ{i!]xݘ, g5$)\>K9A(r}F>ٱe@ϧB5Pdz]X#Sʓ3_tڐ}Q2ta2٤?Oa>ŤmC#T6B|g:0}VP\)ƱMݭzEtjFݷBq6T=7 WnL*{@0feeD$6Al҂*6s@Up^LJ 5 uAN*P#?(Z TpAw;>@bgK'v>~EUܒ&ӯ9ZBugת{HNG^ !!Inj"ܪGwOihk1 Qo_5ÚCi6\Xd|)뇾+{M|`ҥFE/ fw6G&NʖSLMP`k LxC)o^T <^‹0Cǒ;<&Fq9T]3K)9}<{X}(.yU}Rl3* xM5q7lZ16K $(Z=5>˵}ӺJ+[( K/ĵjuDP[ʜWzJAm; :ُڟ4gv M\Ʋtie)-a'|˒--u3( b?ioTȯ led(xfYbS/X H5.-n띏NDžJ1T#n8ov @Dj{).t3J /  S%X+K?xA:W?o'cw3D DW쬤ƕ>8)8!rLWٮyz*}v[} 9>ʄ ҅!CۘrϜlNs~;#ai}J$'.U$i# j.`AW 3p9;7{Gu2 m㙉QL6~ "}]ݾza+I|SCL%H 2n,A i8sЌ\w4SLʕOě@VHKH1hUDeӍӍ?Hj@뭹A-T(b4!Nve;:7JD:xo j2S]Vv;Euik_qDyPVyV91zA SLYI5<<5eJk^}/"2lv{G\!c5_QNSSPe|]ŏH"R6T!0&;fOj{9_V]-r H{ lZ 6e!}5-i%:_7)0Z*i!L 5.$R|S5ϊuZĵ33$,6{{_ x.`PՉP(c3c|_ķ!_!QϑPìpY|DPNw+"Z^}),Ĕ@ݚ_5GH0M:n#)o%$e&bIٿK̽@\]wsџ{ TrP{Ìyv*]fjd|*A #Bꔑ\5&f]2 |HTҬ 0S-(KWaY9. (} r8QWul,g)zx-C. SkΧm_Xɘ/sKuc#߻qlK_b~TfUo.aXKl|U@?(9kt*esGJ_H؈ﳞK64ğ[1N7ژ#iSYO?LjO}F-w2B& z;VUY8q6v{y7d u݀Zc/ Œ\E${e|@Y ;^ݍg NLN6A)J::TM+=ڃG焃` ةPJnᢩ8ZtفƲrp;%)RGL@VgNj'2vd7rq.mhFXxxUUK^k K833+@].,u+6mJxա:e)NE(HP]*.nXzWK~K;ߤYoN6Rf3fwU& ! ?ǐYñqT MY};>"ֵfK` jw^^!ij8M2u7n$t;|`"@OH|1;n+]͸QW$J*Z/>PQ>OwaHD>K:]C s3*h{St$D>n58S]x?wuR#NI횅y%""7WaЋ!Y ' PIVVf?v(OT`cdg=toGIo^Vs..g2h+~T: o=Qk(kpc1Ӈ@R#]22TEiZdK FjWc7Ju|=j-f/}K|g$E[‹Ʒ67 uӄSe۸) T`.}2O7H^,Agġ{#nƔ^Mi8ڳA% )L_,vpW>]fvn똌gۻʞh㰘g"˘ ڳXwevocS04!k@Af=zSOf(>%JsJxQ3ʞצZT}:T!i%] `Kl$FOnXQwss3P0fP{K|e_ѫӫ/qKjtAgg,/KEw> Y DШ8 #n*תmu;L=&%z;4֞ :-i61Qn7T+ j"sՕcĚ# XMkBHd7eF%ghmd7LEI~VŬz*k ("Ie],UXݢ)qQBMUGFOƴ5]k1sQdVU{ȘN#hEDI~&B?*^:!]) ,0`eps6Şz*>}нNJ˜.tXàЩ U j.~SNh`BsSABh^)#<#ِ) SgFOіW*9' g2C 5ţR8}~8 ĨB|s@ݻ8~@qM%ա 7Ѐ)[oQĴ 2^\Ff&setKM KZ[Z9;||]Ү ePt..ү_ܥH=׺F{rne{$ݤZZ {Vznm_w^1HNێe4_s-Uεs`6кjD5߫><ʋ g:jcmR #sl#>6m'fwH$bDL1@O{{2V꼏ׇdYc2`~q1 q֗s& N8<LvFs>S~HZ{7bDm̎i۶@*6 sfO>\!-vFz%J Jf|xpzg9AOhlkMJ|y!("͸R8>^tNY SʵwjdY#9L'$2/\}uL'kjG&B)SDك:aC k~KS_44eL9҂3%1vјlFo*ZJ@d]_3O G: "z='6i4iq"NLa~v22kcɛ;>}j3r;jNdkՕ4~ճ,XǶ]"p^۷J^|yDIj ٖ{t 9e ڭg>Ӿ>5Te8{%WORYѹ4vt ڿ$,8R{wޭ<.}[y}>ӦNM ԆΓ2Js6_xk⸵ ja{o򙆤XfzRk5$ڲHS(G>гju3c͠l,u4+lcK6Gj̾cn0O7%~|xßʁgf((^Ŭt!| `seq]uXo49A|qB@F|!1Lȏ ӂI~ĸP}rvc(gO\?B tu)C/T"U";n %b/b_k[k˜'I f't3e J. KeGc%zn%v%Rp`cص-7*bn&Yo2q9|Y9uzkp ɬr~⫝̸[0/}߽FVwY"6*}!?Y/ +` $Tq6?} n'dx pv3/+/idvhtEzQR[i`4>9o4H55hp# HN:}$P H %Br ,ja~~NxoÞNCU%)81;\8[#[Qax=.zBP GzC/^), 8S { M{e?pˊ̌D2"".{'DY!\dK +lBVIʖ"def&#l~ϕDž{ϫszy9;q|yVD onҙ߱^7xN&ɥJ~2z4uyhGܯEͶO=l Mv\;O i!49DXJMo16ۯirD߬%{C7n5/L+$k;ۂyeppjD~B{듻.u~(Fx`4E9سL#B^/}\:b}Aa0s=Zn;N)룰{KsV4sUtSG,b lVWsHʴrn\""EEM+gp^ { 6PVty~4 ֯m"N+"sBf!\6./l`f mc"E(tگaQ#v4YhϷZ<|슰0헵n\J )Y͋*hD|QYIȜ <~M˃X^  PTA|~Ur 1>ړ`g;iQyX[Rkwo-^EIm\Pfcf'hC3 W|{ō?0H*;gڳV3q}hb%nX@7NPѲ'꟞rqz4~Lé,'͸~gcy-IF72]wnƶ̊Ԑ ⌗",\6آ3KMˣFG@.qv Ok(r1s{2;!rlJ+_M,@-o,5'vj#h%쭱S5'-UwTTTzz"SfO}˝ۑSSbՒ:%͠%@3>]q 6v\Q#YoθHÎuRy~޼x:憜3GM77(a\3tfn.ŞKӵ>gcΘzԪ\Vpе @K]ۢm,)7rIpNxNyzE!-a7v3mJx$]?ĶKFys=@UhC $^;RabbzF 4jm Lxvs& ARUn^ߙ 禊4Jm,aP&udP2(R3;IO%#SoUopiphpHT \S?C5d6&&vU.3"_qO4:)R2s>MsT0 IjY-2K[l#Dv+v oo#r'N*Q,@l+iQt`)lWT4-@ݧg[.Fm"YA\yg%45\!A=ˍ]ca#DUqq~u !%"5dYI;pпZ5%& {Yk.>?'Ȃ l{5CsGud):ԈVJiƄ_#nUWjj(ٲgQVTw[Nޜ93`2Si)V,*@6~_2.uSU% rWuy|KQŸRh_zcA3V*I4q5T0^W,We#M lq%2]XJ&`Cگڬ0)/ۯiZ_-%47`M6#\>Hw{VPcҪ95,nS93@6YIx {Ye4~xJ@,: ;/ e2eP*A3ܸ믓?}7=!_L[>|1EV; z/{YwE!SJQ! fE7Ԝ+p7[[ dk>b[l9 Z9t? \n= \m-f R koUU+(9FMMR΂?遖F8}lXI͎A176_T w<2*  P|nW)1ِ (7i;(s2~N s$"t^  tKTR^R##| 3 t={rM,FWۓS5hTwK_P ip=1b7k:FP[{B&FqT5Nmp[E4&y a~ǽYL31t0 PLpMM& sg6/IȜYYJC2o-7+5 #Ch-䕼IPi翯 U+CN) eQѪDsW a /8e=*[\01&,W u5CVPot V@b#.򃮬Ut@"Hcz,|l.JzTAs^ gUQo7ƥC[ Nf)Z_j3=at--v=i{EkX~/ {LttӒj!gЈkfHPrzgs Y+BofqKnnHN}RT gm ;F]AGwE[7_q?oy=H jPQ-^/WQ˃>Cݱ._S wz/jP6QX?qp7GKɒ+ཅLNeyMiʣ;Հ'=N{6krfqfM]q({寝:?hllٜETʝruONg[v~$KT %LU3D4 ժ0q RGZe^e-@+V}}O \*VDA0 le@i +\/J^[ˁ}B" oNuĆo2`ԟȶ``;o`{5$uu~>-37W*S*Ӻ@Gw T0:t'O*^YYĜY'Ф9|ve2Ilfdbwڳ]_L4>-$0WU)fbÙyOjLŦXZQ=6NІ }^'KVjTqILAզq"N!H4߂*Xȫ/Ԡ7ַ}(^08Dr~to^(:T;Ҝ_%Xz?^t.ӻ/d:*ܳgBrϬݖ˵ڿyOKhleK5h{ '˒RA j?[!I$XT1j(?^>>5i8^~^upŔ}KjXt"A(xai鸘K}'uT$1yOR Qqcت$l|c+kfH>'K{rx.Z$|U~"O1*@f'l⒃:AizxYYɮ ] Waj{O}U+T+H=Ѕj:MV۳އN|%%L#Z7Iuv%O^۰|n`s-q%J||~T+uu]0_;D`r+~\kYZ5jf+)ޙO)4_PvE'tn2KU8[uP] |0qk$ZgzPt9vq0ȻUI|ZV(*5uě@_3i[IVecjbj\'A:JHU˩u.M?^4lFTm-`BG݊5&·w@A4΃Ā r|r|2l hFDqEqQEEڿw$t$4Fm*ls-Y'm^\?)aIjP'4 oEUII:rV2z~ՒPpYrXk~b9E߭7jm&66&Ao]̣ܿNCǴnI7I]M :oLzM'&o2yK5mNԊa3H=a9j_rŞx[9)ѠB,JC`dp sW!3]O1[amas;I"`z^5VgЫo|B6h=^Jh vb4#:^&I-ذg+vE%yn_ &a1Ayq̨9&1"@me5"2a86q1[,[FUC:>1h8+yLLlr BzMe *4A-H`AwJH_n; 㕣Y\\{~)8)*$q;WVqm{ZZzh% ȼձz2z2x˧˧Nyym]gCΔ3MKQ! px_%:A龵[?.d*MF)m<燴Twi;ƛ1IBn:yuE8WF:+/y#w2xj徧j?We~ֲX:}+' XX͆/SܶW<˅BVYdB~ I#4m֢C6wG{% >Cie + )+K_s \]z@-xhwW 7}EkP~YMjlKmFeH1k+LXA jg; Dj)H3yWBҗyb;ȡך{i*/OUg9$dEJ5-uv:YF]#aC"JDM:OL{9#F)'c*rXU99'E¦CSMV RԧAB"+ (iyݓOhh\,}J9]Bb,|vcS`'\_16v`_&^]8hEu U3bcƿG7s {OL09ۋ^Ū=7-?%d6k8O}ssKҥt-qu' :+P`uϿO˷͹%a5@J-W @g/jⱴ4>drӞr@_ȺGјly>2[\( ޒw9L P47~gI[ BzuJJaJM&۶!5r5tqp?OB jg[:vOjr4FG'Vww܄{sK]ݗAi;j8TȾd'k4#"ƒ5KBYÐ]R=p˾RȚ-TW$k"D:ɝ1=uy|99jbgy8YҔEU =6^]ĻxFe$vl`U4ƋC[':g@LzY['8ɗ _VR'˒LB*kI5(?.1҂@^EI/ŗ" 1`ۯjJ\-,KJ(2NJ%?< 6M'52ZnZӁ$I°v_}t헵=Mq9MDIЫh L7' h)Uۯiek z:q!C"ԅ>yJkj?eLӛw(vgZk{^_+q'g^WR\5?*.[3 P߂uvaai<n{M﫭LL n>I^q"_uL:H=[$4__qz|ݙIUtk.y IPaTaTUVWVNqA.),UAeA،i?Mk+S] bZZƾEVJ.x) :OU;6fG+/ G~_jjxr*e$=DPUK4n60" gv.s9ŗ!;` t/܅9/RD*L6~>ͺb.9Rұ@϶w 4K7Ebx%ZJwjA]mNJTm|^>{=2zQn M[;9BN>W>sμa[7@ZKeRƴXC>6{Y8 A$&;IqS& nFHS().l22F.GHMP"[SC8B?{iiAEQ.|(oU&8zf ȿݏ [tRKoup&\ +#g\3?cX%r%>-e<llHsT3rl,I;,G9ヵ+QJfn:+%]hR]uN2:pv7FkR|3 ~>+#>gaH;x旨 /%8D>g5v9&U};l}i@Mk ZtXΦK(/}_Z@~/H$Gemp"ax(<4!1D;Ʀ+FAkFqT%Nk6N?rڍu#|$ve9FvjZ""Yi[UYOYOZZgkD &kkꧾ5f+\SE?C }/v3_( h Ym9%ܸV߳ 0Z%h >#u|xl{1|YQw2\#[Km 6Nj 7ciՃ')sRut<=eT8ɻ`? bNzchZ`\ uD9B{'^\ !mwW߰=K|1o_nv*Sm>D`^k_0|zA a36\zB9y [{q(en e'^%$0_>C:ŁX-&6մ}6SY^*_kgjs \+ Vj/CŢ|>ujTC=Iq;{ y ͧV6ΉAIRH[TmveRq͵b#ĿΔ -éщHj e[*x#r*x05q;hN9$&dhMdy!bU^Gˁu, `ZRǹS G:?a39gúT9 !LK81V˶.өSqգX6g1Ik0 iRkj̉r24y -\*{eѝQ̬SK?m.i&)d^9s*~Cn)~˜G~;]nCv;\H`wxOIm Q 6ѰݳO;G!qBh5(8:e'O:jn"@e,` a&"?=*=:: BQk MX sUKe<֒U,Le~g"Lgĝշɴ'҄i00Իtfа5[&Txz\r.^^n I6<&tU(Dԭ*xklT|Cc^ó*$3擗jR5ʚ0Qj<R37ʻ]SB^ZP 8psMO5v1t'9lvFG]aFQ,9_ӊ r J `{Ibnn;!8yn:Ol{9_Q]xJB[ZH gY5)'ӰPlO܎pib.LFR'37Ș.0||Yx%#ęl`kQqM1\YCD/Rc5/gYr |kuPTvj&ME .:7O$^&Ol}栓]QTlV43ґ&G]~k_z*{iy:2CG"`۟m,2M X)$y֬H^|cSbνӜU5K ~R?L|!E#K~$N=b &?櫤Ēaá-fQ?z1oHJJ5 ϬG/ YQ̔[٤ż=wta,S=0[vVW#Cz}'z뫶<ׯY;޸feIrsC1耞52v:X]gpm||~8pr papso) zoU[zz [++}n_#=JO o~m]A>9\d/";.Bh;g>6,) Rbs}M۝&`6"ZZhXVH)V<7ǩ{Wa#T oC i)7߹:LՒhGY^NP"xQKqebi((T@HQs r z+"m7ݚʕޕk,~,^Eboze~nk #9 TN!f#EuٱIҽsArSN o*y_VCC3jX[i=FS8U(!/48PƊDɜ *!)C*sA<)s- M2$dLƐ E2uގlw7Z^Z{U'2s㸵l?"Ȉ8fe2Xr)h&nDJ$HLRSpEHTɕL0@bks Wmo: \{ pqF4! GOFV>Q>q2?:-,su%[ӀVZ_!f!DҪWzSy @AE=.朆tNCxsyg(COz]mUj ]&Ιc&镣 ?a5bedol{̴`|D2eE"p[ ջ]!5}BXdP =>@x!]';~Qv)Gwic}CX$VmwpbQ[sC|g;umӲe=YCVfIw9T8ToIB13[Ӏ_PF hF9X*;|[+V9G:-whijY߳}l{azғߞW+DFnJ[x~J|Q]sn3;ߘW|Ig&{(Q|AL}ܦZ:!fK @[o r*k>L,; kZMI3qp7|o^F{m^z/VG1u}?&DDr]U{.Z|goFZ-)q4kMGd|/$؞ōowmy c$ORj |5j5UU.PeA;oh.> /T?6+ROS9eGXm'b~+y]Zq-ES[n#$ zC,Epi5lv>if`3g) Y\*+@[1F$R$R{}e7RwG W}hI C8lPujhhmȼ% 5\@.@]FN77difOo 7pNmZ5lkA"v~>&DuB(em4LS4Q4ae>A+ym K{1 V ֑33 ܮ|[Lkl.e3 LTN5={ ڶtރ@$S,x( {Ѝ #fN1F=L+F(ߔ%3 F4 Itnyq{ KI' g%(Vq\~ O $ ll_\p1!`p?AK9dGod6DZ޵h[ꛅDt$Y _uB8mmvI[ccV!1ӕ?I/$*TP [z6?tHnGa{+Үi/MkueZi٘&G&J)WEbu_Gwth]NϹ!l 1Z$:/)lU ?*ڨĶV~F vWDs jg|C##zMYyM R pH/&x켸Ӛ1cP2q޷sM]Xlӈ c=Dbp'O008\ֲ&o 1 $Un]=SgAF%)ijC[= |7?o7n\΋y05)wQSOU#T28yO vUA&0is$?LT]tY !m7|Z0!JUW)){, "w]iDݦ~Gowmn?CcCN˅?o2xָɆgrO^6U3KIX)57H~.¡Lu妎bs1U++17~N&e (XGdLȯޯް V&G)gԢ'939s:b9F'ܘ1TfScq^{wdLATK:A$=`M2d0IEoL,q"THDβ9ۨ%wwn&wƨh΢A0|@mʣ'͈7- ru;D)r*=|ǏdR:?lćχntXjI]YV8-5ښBs+qWs\jN0FҨt <^ iFbpH8Two4e3esdV+gь BAF'ۦxxe!D.dX4Z`>}K{/ $L VÅqgxM(.%L=g\1eeXW6Vc''J!76)q.2R%1$1~V r <~w1!V3EkXil1\];HƌʕoK/T@Ss1L!6wFV72~1ώvy$ȶow q>-3g:D d bp }PKY@rE5Y+:.^uZ\ldWHH`q,yyA;i!T~T%%Z03$ΑΕvs'4SRrSgm3[-(CéSTEػ?Ʒ|p]?AfyO)+g/ű54l**'g\7ŷm{A([Ir@Ee3Zǣ=UB$z׽7|7Lyf3tw4LȉǠhlF shK^~|gdYiImZjg}ݭEX3Ԥ#G!iy8ju̞su:>ʩJ|4oƻ9%rV,5k0X0$&õ>Oe\ȊnAZ.[i;e͍`Vy8@[SiruLNl#ۏ(.\y .4n'ጨo߶(L( |)e7䅀'ȍ@<Y+D$@fLg/~>0t\a,\ _@~􉵑s۞״|{d LDtj&[ Ttzss |>ᙴ u;綻mW l5m+Kn C\Γ2#RMJdXK}Jլouxd1aS *>nB jDCk;F/xݗq|*.YVkQQ- ~'ƾWhTRPy7bu{펥8$^.V|Z]#0Yb P#[!?hCEe89]r#>u2 }mq Dy [-W9)SϏQZ=Sjt̡2{”οվ|ɼ$藂WO^N[>}. c<zX>鷼oUb].$WWrVkf]NO2fc|:Uv}udJ_MՙGA7Wi]J<B/=N%upxIgVN絞i;|÷͚Fls[ENtKcF/stBO379PX{Aͩ,$VhqJ]pC}EMuƀύ,+;IW[3(~3DVHZao9̹=xɈDŽHl}5c)(F\?x ']ݨ))tFShǓ`Ⱥ`\Od2r"~[PD]B L5mτ|>4YH&˥Mb|:M-)6u wľs?!~MGk K"8 +HBK>S]UCbǝjMb7}7=SfgET#_QcK~L hnzͽQ ࣣĽvC4..f|Pٌ،Pm8qXu;p%3lbX=z88D˹"Ȍ_=7CQ-r a'3AY}5.uޝC g߲dIBlG};1fɒ[, IPE" [ DY9uy13sus{NBq pmQKsc^&-}RVW%]v@v*js^C{(%St~ ~LL9qbbP=M)'"byq{i>5P^{x|>&'|◒[P1 u/>Z[M*ఱbNյՅK;~@fDj-`7]8˺a[aVhʖ]9ah;c,0>HvF4 ׯךyjbլ]|"|l6<̢.X*XʨݨxV: gUt0;]JJv>>_K )rh*2vY. WG.fƮyAtOZH቗rs,Fn .~W]]YIcNi~acKR,/ޫWƺ'|HPk Cߟ*4ĵDoe4<צ9vnP@oL.)%Ȩ|R|<{Z2SHd=D^yoY)߾I=ky㔽+zaIcqff'danʳJdܦe.kgcᐠ@!]yMWV~t]O3IC>/++Ck&~8(ijMX40]?)j+dxfv9Txxogu,*k5}|t `8?!-&%&BN cZͰبwE"1JAL| 9c~b뇧բMM.݃ ^bC0L|.vf r DQ1l:Cu\ 9HFX!kuiwliŒVV<&Wн+{WV|̀l kNtTTd ۮ͝uD|+p[ L0$_ko.X2Z2at= P2r<[-?QYFk OO5Կzw_9ea/dqK7x oH40Eɓ sEb}w&j@N66iwFG*@At?OBJlA+=֑ jo{UcM\lӥ`M)؅7ΞzhʼnFSdeؗ5Ne1oD116%kJ3%lW."8}8{KL>S9̆Q*6i}i-T1 ")[ "x~M eAM@m8֯쵓"u NQ(H|ĀUG\Jm # yiUP1)Hx5'5NllV<3 /oU`cJ}Bx{&B/[7Ϭ44 !ebl/NOq8-- ?egJ6j}fy]+܅ĖƱ,%N[3޵#pm;iU TE8 )lT FoX ꬊuo;*4EkZNz9|Q3+2#6g%MƋw"ҋk4ή<Qs %J/Y71]K v/2& 7|YSjF?L;ao6HovTr׻a8it ёQG52칉";')uOS2Ĕ"] CPȂ`í%8oUN_CPx#!PzN|#vĀ=@)ߤth 6lf>uf Fʬ!߰ ~"hӡtd?&xl|_M,t@ee.%%;9y4]))C];191;RQ=MrG?# M{m-Bز_ʫ[ϫ %'t1A.)y5ATAwgn.gDJ̹u?/>H>|0,Oy^l+q^t;׸g + >&舗hTd[]ű pE5dyB3R|βI GH8to6ۘX6v=O&« z~b6r#p [d+;EL$5S4΄xa@s [in]~94yr˸}BuO {s^z|<4']y:LgBud,)).p>47!ńW9`،A[NsEQ;N ;UEt֛b` z䮶~}jjbWP ߮D@d/6Isɘ8*Agz:"lpni,/btFx93R%~E frK17aFНdd&=u:J֋e3xf ^@rln Ac}9n)-r@ĴVɒ|w-6C 6z9r~_/dAz?Tj~3q@=u 4L>SeV(vwN|F֘.`$씽ӡCYMc[;ePO!Y-@ j~YYJ HmB~ĊOñfܹ2AEk_%%&r75G{JFVmweLMxfIEнy Lwgbj~G]jfj&7oFyrrd-Lo289Ѿ0o }7q ׀ũ^Z~QʱCN\0RL_kR qm \7SDsgUWfBqq /oEYOs衢l==5j켓Q7XN\g>sMW| ޭS9qKlpmcyWmrOg jGx޲wہx7*Akty?Q5ZBcu~{'!sfdkWkB,*? A<@T&d{w ?p܈]B4H!iHvEPRd+d *PY˾KQH<ݜg<~<[3s95\|h`xxKsF%?۷qBN yk5 &v]=y?6JToo[Xʜ 6JZ ? QKMlm?Y"T:nQYvf}!Xvafǜ"KRwmA!\3muS)`[bHLIgX{GhIQZ(ߊJ:7z+4(D>NI _u u@{T@HP#U K)6 ?܈ >]:eC ;f2Ai2׈_L#^uQZllJ>* gP2&wªª ,,"&'_,5f{h|$*#Q4wG:J \`gdF/QԳMzW*VsJөC:NbJ5΢gSo܁k4H"*b u𑣫DqVI]=#Y % n$nyDi(08\AHҖ^#q)Ј3&eEy\׫Ht;_T4uN[ri'8pV̷o9Qղ>bȥk'qB!Y\ְdM${z[JӦ+NB7_(_y,`뭺my2`m8JE<,] v[\IRӺuKЕ8(jhś+[1-P!F1=p j9htD)]šV""CNC*Gtxfԏ12er8hDێ3ȟ"vç9t\ۘ 6չn7M5e'U9[Zr4*!!$:$z6}%'zSw__2o{FG_uC(Bz壂Q bI˩`eurU59S:?Ʋ۪C/}?Ӭ9P}hoO}>@`?]yݝ籜@>/~0 EH Փzf$VrojbC#G'(R+Yaِθ]a7iWd9H5ÖPW_w i?XW(u0QD:!~=sgaV2!6!``>gɕ$l j5y596p>|>sJSsis7jh~o7 /v` n}ߟ>zZRoT_ʼp$g:`m"WXrINijv(穇Q>YYǨʀP\R Fykn/We$MUS[pc]>MOɞT;sn( ~gڲpwk AȧEH%F)Zt 9s@YpmfpvSrJyFz%!=˔xC6|eťjl#xG %7vuoye=O7}vީL_9BGpme3=_WWJݤLqlqqa'"qj|WFu`T/$Z]ɏk6I#?ݻ9zd尥ɺG`xx'D>@qW kd -̊T~y'c>eB59[ ' a8e; ڻ(Yx%9@s /\]]E>\_FPLreI71iuwc<_t[ gM.U`,|{S)DMCA*%%;>j)+yLӴjPvv֍||C-*-*>SΊgT>qEZ_A|MfJ/5OR,hL{4q3kFN,r5_%ioouzo`|1>t%N>D%aQ+,LXz~`#Jg/|6{wTŝƟϏ>6gg䒌KtN4L??AM UID;C+t,{*RqvLLϽrLt*':Ha(6sŢ_" Xd* ь8q t ppE.uPs3A"1C b@q%Ik2 gL#a3JVo rr/E!*%oIުS$u"= 5WdR1>ts$<3|ţpƗ;ԡ3U?Fv<VB ILL~tE (n@IL3{yh^ K5brarIgz5#(:L"/lBͶhoO9 $@ O2. dž{ȿy(ڔ6csS6{_ފޛAA4\UL}g.))T(+pf* a*]yn7bf]w:;O[n9IHgd}Yď} =pތ!=5;mjꗴqR}8;".~,2-kxnEoK՞\ cnf_2 Plj Bx9=͐&ˁ'p ׀^/GNQX}Ʈ3ӣg-uߦGrǜq XAurN̥ ߹v0((0/ɩqݬ,Yw]%;mp,^˹(Bbi\QGq MGvx^ຉ؉WF, ͩ?;ڌ; 5 / F8wg_qZG8?ULO<2=lʝvaxx9mPR"DD3N-F`^$rW]^ y*@x FA/XV!묗V P|N~/:K;mɚT 4^RƇy ޝS %2D2\yyFL;821Hئ !23fN%cuֽX{;^:O}Yo~^i 6U.*S kse FlRDdt,zmmqHi (g /( l*u+uCMho|l<7f^v9^RVKEE^ƠYX4SwuBVљҝPcr1 j"N"k\u˄x&m@eϹ%ɩ0Xۭ*jIp9[ɚ_S[iMr ZF%M.M v2.I %Ar\)_}Ar.bvuvW< ('rDw e6*7F>)V&}!7EMfӯWlosp{v^ݽ(}z4D$PpV8@?CodReR5^^=#sʆgpc@g<7~oj~Ym')#zJa wzV Kq vf'x?)0gKy9] WL(|F/pjynu8$ RuXuXkJ1K3Hkd|2Рt8HZrxQlHMkѹnҧgVscAAB {XF )h|P"NɄͭ П6;3=}kX)@vkM*|6Wvll?OSV:(O4: ߷4&V +NMNxKNhM Z` fHd0gGY!,}dV/))?i 5!5!o&,"azUUnQn!xY AAe[5L(_JjV='U^3ub{b[`'Z֒%-j6 '{0 3"-Z2$6iwrs~Lvɗ|FA+'e ]]{: *THr~0ص,x'=9 ڠ q$fe:%X0ׁΝܦe5O5 PzxPP薁h>MnA,RR.:ޔg< yݍ Bs/3[Va90rKQ!`&U6P?󀩃z" wK^ei i[-9z,5ۗ$Jzȿ>sz) -aRYIpʰݰ=Q: 8SrEO;&#\d-AgqCyNJ✊ }K1XtJ_d!㪈"%$x}-`LxeU KOGŐԡ>q ~a2+EK/y 1V|9#ȇOm=< yOVktK>nYfwkKMD($#+5\"eFjLrba-/ט%ݝ5ssHkR?F,N0N0o%ix* $C)CI]pM{d)׭kREZglH72`(!&Da7œ Zn{bҚN׬o_Kgp+{FH'3 04 Lװ`z8pb.I M6u5u{i 7(Ioصɷuq-q-xL{4zkf U \҆ϝ-chTf%y[$qS}ۀ-OI~1!#?#4P/S'tŊA(==bjkKߝ:S}"Nfrfc{,WPq7{5#YnajP+_H`,1-RM6<wzF<5ҝH*QSBDUߧŧ1P(f]TB?8eff;`V2|YyH6tHڣyg#r(+v'mu/*Va(2b9J-J\wyP!2fzze=W?1|BտfKmv&X{J? }:sηӭo\&Zk- 2V~KfW(o@-3i*rs6sG* 9X*&*ſ~Kg;[$"o>vM'67/x 5it*߬QϴfxpmvNlͰOVyqBU}0uf'͗d'՘B_kŖGmcBkX{ |<7n33[S{iss:^64Z$_( P{|}cѐ~ ~aiw7XNhS/Qχꟕ) ޯwpzezeF@q&hQ.:̈́ pqM~4H9e =k@<x:[}F\̬iw nmm+!M:,;,KK@։&b檟o y#\el& ܚ鼴מ,SB!ü~nTD8lS =.{Hw>j7-{˰ b EKpqU]@W>Y"{Xy1C,64f;dd{dzS& qJUh̀ډ r򞛠uUt,%\S̈́) z4/󚣐bN 745Jڍc2Dk֛I)[*탺ۑ˵#\mnFvhY+.>P1+˙~"BԸBF#^N|<ՅoPa.LhT0ͱpDLņLg1EHoćEmEH&9pgzN$$=t;s4w7#Io" D/z4L ~SG܏8Bh(qq7dP1mzB1525' % M֬\qy )f6\F)*mjv%D _ RϛoXC9|h~!uw'f;qHzs C+1;XYm@GAIA%GsQkW5T\&@,yW>ߏM9K&gu:OYʈjGwǔ~dɺx Ō Pokh.R`bI"#$#⠂ۉ<oQRveʒ-k%b![2lE&[[Id"IPDts3?3{x\ /R?/&4mԢiSLm{@o_k?gHyS^OC EPpp..pf۹3f/Z[Uwi~:ؘ:yJ"/+1H< :3Wi಩ (!C}Md 5zcX nUb#4c $~N(%}64dw {@d+h~@6\'\an|w{O/rqNBdBqsUU}yz2I][ۥ((D[?9S L4zq0SZ}NpY[T%شM9\ˬZj,Kw3B}g{Ek_+u͞y*(9#~Ba=T/|h9nNz Ƶ_Q/:7N.fߚ^zLv-b|s3p ζh#Oy4RT&y7z?{{j ڇk9 z=>+yL?w s"Rirx^ۤ,[p ~U{gxS$Ֆ?4.7'7oFk++S{JBq 24!7qo,^ZD~EǪCFqk+ԑy-L>_ٷzL^ᵥxdY]k@3`kKgUUw#hN*>EI3 x/h=S@Đl#W,Hl(V< [77&*}Rh `)Hy%wύJ^^IP^rQ* kۄ'Aʆ!R=J*?&$.Z g7?Fs-1c@LWs]YTb/E*E*m9sp3"pvGh~R錈FFM՜LD \dv_wȀYBd+V zFD|3"u˦G_GWrGݵi^]8Kev(MB!>9e޺lTIʁ @zHItCl @h%oe%KVn(ǐ*n>'5NQf31U4@H/~ؐK"Ş]zsfRtר&MCwt[=&(^͠’ u0\õ-9ud"V]D_S|0>P: Z IU5\ME(I=鹷%oӱPj *tinHdu59ιwjL_ aKN^Ka:軗}yI!%+2;^ xx.+幦W7Ѕ~dE* {!8I؏ @i T r^*6Ϸppa1UwJ!M;98}J"6S6A.HkI|Y/CwEaa&6>ŋ7A3|\W a}|zC{\0kt^jjrBz8\\8V!=||_:<$xHPMS&g|q'Fk`Xvy=' IFn v3uK6z;T ӺFgۏkf]yϘmmv %weۜcJyGOPNq}5X'N*9B!ݻ;ً\Y3N$ k?RvK.N=W=zf,Por>cLSւ>GÚB"G;nWi-;ecc6{t5DD.mYyDNp=9XU59@\ .LG"zoVF)s[c=[(^ Վ%NDnJr 99ձAiW  ,V: jA6bZq#𭛝7K vrަ{w*$rUBg%:rӀ'A켘yOSQo66nn ehtMbDurڃbu>G6]Cz\1)$p%Wgk-=MK\+%x81V\(8G||=xe3n&/Ut.A 7 $Ry`a${Mb|Ϋ*H\˦S+޼!CK Sl(dF1i^~N]fE;r2 W:Ƈ3Rz:yQHjWk3vL&Tthwt7~nJ\l [*>_׫OjQwnF=/A^|qI1;XSeEAuEqR{don~ryKv'V4_]΂t{ xSuCLFjiKPmTzeь]3쟝e~]M )Dy}j6uW)Gtx?>iC~P5B:'Wstk?y=Hx82##`KClٰ31s[J0&$Wmpd : -S6W q: E)L)L/ɓΓ.٭9zv c\V%ƗiޅS% ~~tom^  G˄&er ~87;RRf17S,L'o)|K2qkY++Zwa[}Oot6*[4+k9k{mu `n_nEC^lkˀ1/ӤnMI̱x·Tf۲aFjfzXrDY{%>:麖k#7G«{=acFj6wYrw|#3Hӄty7O4H~wNnӈ g +=:n5s lHNFfW3j@&tm9 u÷\שUxB d }~GMrP$sU%(Ud:*fC~9\9jދDR& ݏ@+/㴾 e cxb-~@L5F3j۫.m-;vumRbG9cA(bffSHt-d2?U(D4":nEmE-,[9 amlg̩}C2 ](x%{z)`zsq9.eqo=xT8 7z+RRۯ@>|q]7]FL'tvvK#YbFw̛RYSDycmZb11i*b ڗڽKK+ԟEu7megƇ+zY wr6ԁgs,>7SJ?g*yϔ$ۀ0cVqB5ـ%/J&+ (>Ѕwwz2X|[ڌڌ\s'/oy]#mKV W=W} T¨yJ^iƌ7!i;̩CA͡g"q4KT bjX'Kk6 \0d[@B܄f`Dա.8bj17?Sۥjn4=0t1r[mŋoL3=la h}v-+*o__%5RU-A,vo\ajEtNZdljeVR#_ծq[9^y;†ўsW3w+i{<ߵ(|8V_w339v?9g~5)hѰ ǝԴQÑGbUC@t::nB? HtW .F7Q)M3) WļӨq5iӥݕGZZx`,)vj=DI#t`&P,JK3yZ^ݠ+ᇊfZ;nP%IPz!΢0& g")& x<`K=ReMltڀrlMߣx&|>8?\e_ z5e~j$` oZ a?;s>l i?s,Azk?әܝjAgLgLd_+tFFF6ݺ LzCCD[D[ȍ\ѹBOrp4SS\w rOJFgp |bSp:7NB.%w2lڿqr(NRn)v]TRd\̧apgk_;~m0u8D$zb}{#yqݒNo=JMhD0YU De3G\ş?8ko_Ӕm~A^+쮘bgJCEk?[㥱{4+W T9qUMP/F2\E.{2 )i۽<:CۨboAݐXio@E^ ٹ1@b/t3Pb{ipNR(w|'Uώzhh߄ Aⓕ\gKRn+6wM~gAޝḌ]Vױ!L 8H+ a]#aG!y%&+fhCGIj%g#`[A\\bv)l) 9]mJZ붽Lբ^RT6 ^sדX%,Dc=͝/eR]v_$N-+ZFL52?YV%ܘ$ _9Lj4`Zy! `7mS%cVx8w0al4w*[/[k.کP%=H7hL7+++"稂o!]I.l<Ɔl~h@t۪T26.vd] >|M5OёQP8̮Ԃa >Z nT``AĊP#gL_G1<t,},qtM^:=ߡ݌oZbeMNaU2؍U:^UD 7svUWUczև%kl%zz>޶C9\1mWa}JZ iXW^ M/Gt|&U4-:C˸k5 hv~SWNN& bU9n~}AyJq"Vr~̪~$>BR=6squ|餾^ ^7}%t[1x(=+ l>b0:8KʹEɕ >v%R\fb L3`'::q읮ː&V8x7.o1IHꀔ7A5 B6@)a'[CQ=.Iv`<6]BüS]C*cwg ڦҦ2tp%]wo X !֗7#<*IZG #ƬƬ(gj3}&*aO(hjcx>SctEq!nP5 ƠOLs0zQ E1Dl`q&} ;J@v =;G8k] t{^99s{F " pV%*aA)&A$Nq46=Jdwܪ e#F88;޾!r4ΧFFāʴ,kxxkUHLЄ_MRv A>a# 2M6_7PCmIz Q=.{vM{ͺGwT7 !Δˀ퟿q^WeO` Y|UGItU&SЌ_L~Gw2$wwv@$/ko:k"9EЩōmPRB&`QEl?ӳmm"!jd18ʣ7#:ė'uL[ĵv=m,b:V[[>E/~$=>UIM\mpp8LպO։\UV2~l%8Sf < '$")2RrX:6E:.[ѲȈ]DrX'\熉j tC&ՔWșAr _[M>zt'bP}AĖQq ZL뢶etvsYt lp vcL]NFn W7Z|Ek]8$|:o]SJ9軜#,Ϻ˨|#GQë30&Bw6JrW^}cm|P\8r䁇|_4&$q@ō 'j1ͩS>kJQ`΄ s0955S'ډ e"RQD :KYOtiAGA9(3 *Ң @@.b2)0E$wc@lOL8A\"&e>e쎊1/R%ǭ22֠RPLC"x(Qt7oWOx RNN4~}x m2(Kj<(L)~mc/tD*d($㯙:}Y1yfIOzaAHdizacH ~ww;AcQD$kL8sޑM#rrB_AY`etو]D:e R [' o}bTɔEX Ū,VqwcjYxqqrîG'!1f ;F 㓘,EPpQpΣ#V0DT(8S FTpo՘k3ݏ;:c*-Z_4B*4 i| +W㘊:_*}lf^1=i5taJwD,F,!Rvn&ȶn?TpPp,!zc,AU*ŒŒ)::0'K>K1w6՝8rX;#rqy;K櫸?BLkG?'<$' ]pJw /_w 3"{q1 z~+lvYW3W ۝C e*%*B5,|wr^Mi8x.h۬V^c!|C7]U-y2yRpn}[ix_Sqxi h |@/$] D q 7_ k/6t,92  Mu1)únA.d}}w #%P 8LEܥo8X1hbO2||\ɝC-oR+[ȳ:?hJavhtWǝZʋ9ߊ[ p&KA2u06=w_N= iF!נ'^D[`# 0j7N( #48N3(;;:ulԧ-.S!&X'Rz0`GL[?T2Si:E;ʉMH"DFGL;X͈=nC]L4|]lt~*w6ĢzH1cy.XM>9l84lR7HԪJV4L;hmHy.aꟊ7Rыhĕsugؙj+"LR4Z12zݛCc%Xy>:PC 5gf݋2b; @PkWt}?Ey]K=hv^s}`w"$kt8 n"#6wgۓ9AmS/Pg◽wzBuS3d;;ҷnϭ|qᲔq+\.tC6:zwWSۧ:A)ޓi5,;nǿ oT~T22^u[NB5x 4+ RCū$ H7TPT!|A>[@Μ\Z7LA e=tbx}dNɜ&٭U78A1p2熣]|Q(e7 }BE͈g TZ5s@G>tZdb.((Hx BUװ??D55zxd$>M)ŶOڀf~['$h<+^p+boک3sF[x E"I< yXD޵LS4LOhBXӰ> Z~~\dy^.U8$TKivg>ekV,PIRm |Iǐd9XaAk$ ͞&j<2XYl_-6`#0\y}kucp(:54K _oQk yI9H 9Ļ8#՜m䛨ImV'BȗQݏ?' $ eGc7{*{hw ([aOZbUR+hcq5nxXyfVݵA]&Jd{l~O]ZV?a֮=I*t@؝N*DVUEr᧌4..Ćs;қVm'a -Ow7N"3r'A'Xzs!}nql>g6XjGRڹşDSp瀧ӯ3)_57dM2rL^=G5;Hb1Qg@vz`5<3‹CUT4jD_}--zڝ.T#Q  Lt$KpzAAΠjU-BY%'wO\bz}>{O;WSlڛVlpeeTƸ^^'.Cmj@Mu=T]hi[7 i iEmg ]т,ӇfJL-'֍!uH@e[Pm3IU-3y@$ө/Dvc0(JtTn3Bp=XN%3Ʌ2 PqafP3p"UW7Iy tp^go &*ՏNT%CIWb'/ag }tt:=PEE55e$@QTk"n&9Keg}qbub&]ϔşߡ=#Z<34=:'Z%3r-Ϩ'M"NUSh:'wꓮY0‚(f=l:y-V1 ӀV`j:au^x~ˮgJhC1_t\Ћckdص7&fpv+a+Zk;J(4f;'8✳NW̷4rt2׳o{mUn0acw-DvZuy_˾Wg« ַ:a=I(`1 ?mR O1fo⢠UGnI}/!XcP;ųu떘6INd̅JoNF_vvqsZ*j#qF܌3+$44yX%щ wcP}m s=qv#T? &lB#,~2in+Iɺq+%Xe0j\acSdaOpjY׃zY=]c"ȲF#JN(S6@Rev0wu!9.u9-.tݭsqr 26WPU@@ t|ܮ_@Y#`d@-a:%6^oK> -߉Iz1t¸;ysD|Nx7xi )}pbY-u-5%lRڬG!aa wʚ 1vty}" ucu4{.g-`O+z x;$!*XMuuHiiʀz07/SRJ0M6}s=5H uPi= z7-mA6|Zbn*̼?>曦y0~ 4L;hm2ptROTHQo|a99v0Wc#ȭooMQaլ5'wY˫Q^EUb ejbf{3? HQ{wPj{ϳe4./rku #>uF,O.|֪w6HqoF =X 1-#:Dݗ+̇\FABb1)s@8¤ amVLbn.x~%rJvMvv:ѸԒ[C<<llkt )QQdN3ozu~yVzV6JRKR5dhgt+STC]g?FP˾eU ~vJƧRyDr]MovZWci6d*Զrjy(:w`S'qO@mB<&K6e1.Z"h+D/{|8EӓM!o1:zh >퀫{y&H.Q&%|z.>D|dVkJZeʿ(yLplAF #(&v\=66peeAris_$"D1}3%EXJQp%XjMӵθȧu""Uk6S aOć[13"^Fe;; 7~Rmi5*9nrf9z^('B,Gր*WßOp(G,A|N>tTWε{Rz!f ԥ ңX bz#J6lT#5ZQ p.A N3'<ɾ bYJ0j6ޢ<>{Q ik! ?rڪmWJZ7IbK B]A3];g$iqmv)xvnV,oqq&gxЊ9:/V*ʝ}[unj}Q5y428. "^?,?ϫ˱8hSy=]ԕ-"kh0 Q~ ƚ\:z5p| BZň<8<S 5jCV/qm}3`91}{26ѵ-67wl؝rm .D!Dab7,5;"ззhNrJtURí:eS+TdPvz'v!p'+\z)xu oƈgZ,`PCCu /@`l m C7zÎU'ǧŢi>!ˇQ33@Π1 cu |}38)d?r}NBjT#opt6̖\cR&;wirr}C|C&چig ڸڬḂge\^xT#ԩ4ƀ>$I>< t㟑E sz6p?8%YlW3N]Jr rABF@zLnw}=gӑsw8lTIzZ:IL*pT d?=T{(ˍEjӂ׀"i6NbPH6 %CO^E&fnTP@(Y@ x0.SсqG|w~@wȎ^Gl'GI5W 7f}٩ǔ(;UwFCj*;Y͊MvbJl7Iҥ"22y[/Ϗ1)֙7WU[2bq} [BkX~2.Z7 Ѿ}k1wWTzMgCICI ڐ>5 g7' Hr? % @o7K$$2npR0yN+{V%CPD붱oy3ɯ%4}/d} x?&~nwcO-7g~S2W3V׊WvGBF] 2' ۳?!IWU-Բd(Y'!#dVejvjƙ%W7yMب$ +-bzw2Bߚe<RyE\梅2?Q%|ׅׄ>tz4p\6@F4u4so6:smyYΉ˂(HD$9dtϝ1L 䄕`&F͍oWoJ5q~ \%ި&(dFc1ay|UhAg8? .E&9dzٺw>=VCԿ攜C-N}JM""e̪[oZoJL^MT+ \ "7]]0ZI8|1X)gAvV%oLZsf;k@݋H7V΁9dD<[dtH/X_ޜKoV]71iX[|\Q\7ŕ~,o \Ķ@3wC&Eu p@R?F Ϋ'7Wê"ˆN Z\X2O7O| 1a)p}8)̊?AsqNd}wߴ㓲ĮTϟE?zI/1|s _翥|K١0ߋEnE): duseNCSCS9Ż^[Nu h[rGʙ[͖]ϢHZ@tW9vH)!gګ+ DS xDRPGug^5ٵ~څeB.'煨 'xt=t\ڧR.)w` ik%d쯖u\o*d|av,UQ ^U}j/"FYׄ[u4r>,џҎCjF! ^,Y0p<уu6;e+2UThEE* !e" .3Rer*[&bT.%{tep!ZU ݒۨejI[{":UX<^O+f~8c@jwg_;P{]dE h9wkؗp SIrb)ǘPBΰ1!iZ1ctĻD$ (2G,yH4x$L6|>Lz̺TD$qG7y8 f=R8VzVS/ȶԇ fǿ_֠+t`A`x\Ib]j\-k 9\XKض7#LVK]44-T~ԚZb̵RY48KX?sRlu0>C]3چ!P[p6p} 2j`lY߉D`h`ϾZ1:Psnc!\ :ڨP aL+N\Q1$C (CĈbvӟѷs^(8Pݺmd@pfG<- =?t+n+6zY>> ᫘9YY?ͯ 3Fݿk3ẉ!p潇,{Jߟ$>=9R߂7%ds'4FstW`X*p',.u_O!ޣALPQtõ_< Ѥ؆Ȕw~B3G+DVЖ4c}~NQ=6}@ߤtgn*YwO4z(huG(k5I՗ڮ (n2ɞ4L͋ `mu^Cͣ6Z+3 eZٿϧX\+:|3O.S饽33NzAc2*nz*3k@q\e7:;.U~CV S$@LI}' b;=GG t3VBR&ݢڗ@%w9PV+|L6fN.Q*{614uAK<#>E3=rfQ0A((|bW<*YJ&X&aa5kWh"tO"z⒗ 3aK{i2%922jMylו.yמ(†B;󍌮"iN첧b S>!gXk֍|}5S[(D3uGĪxNcn T_XH$_*3\`NfmFc;qsƳ5HBj ^5'o>sY'!'/ ݮ)<A9شlk@-!!AؔxaOϚ3`dU2%WB6]]U_Z[{͆xT. z &E!EA7(2wgJtN͐)2x)9bikjJnoki;F>bcW= ܻVV,hvǂ!5щ_y^ p;1:Srbvyk3LrPkhBCjт}}*0+p cZ9 KI9auuFwx2Ok'v(Ͼ'0z}TدOO4 oAI8}LhfcаlwbuE_^zw2tng!7O nn<:e'e'{tIUi3Tg'ף0Ι:]xK ?!T6]vtWEYeuwmL\28){GD5C)مw"..p_yߕl'QyH׬ !P0hE1$Em&a*s„hW ?|i~~HNh({sus1-Lw++AeAaL U7 }Bln[qe6 Q_֕+SNq;v&f/=]!o~;UTfr4慤%-[gڕڕzzgf*scgmYބ'H tval^N9{];\Մm|%B:\Ä_9Jr(qkU>֮_D>4rzW* /̪훃,Sctԣ˥sԱW4xzmm ܏ku/s?IDD'˄&kykT)RHV\õ*| |ӭB2o!ؐ #sTUk f,Tu(T[=YXӱ!DIf=z .pg'!jr W\ƮPU=jp[%hi.\5?hdU^TFԒ\&Qs2kZ.qk?=6H)`!I"ˡy-vkhrA#Sh02\I~N"d#ؤ$k!#UʆF)1>4B?&n(y^.oKבXAG圳!63Ζ4)ꈮw'>KuiHŹ)pxAf&Q׭S4a0@SgҘ`VJ%WәHQV<pPJKk;WGETdA> 7lYV xVPZFÆ[l!SRcs -nto5 n\_XI Vb%%ܠܐlO`b"ؚzA$"cOW$sH@]4/n 7ru?^9e'e-;j,=:F9J(/*CvbKy/tͺ?=<ߏ`(~+ k^nc8c6l.iqF)Ra` W @>`0&-.2/q$$p | 6٦_>f[iY(\h>=  ?l0y o kAɗa3{4CU4XRK&( ƆXŹhؓݷov`7'@*BSS bX4):e4u2KYml]%-#㭥v_ƥ@95ָKL/q6eg#]%Deߕ14.ΰ v0K5~ گ6jD8j$=8ӵCsN_~6йx>MƷp%O>~;qbƽv/xxaP]hvy-I6lKc0U܍BW\EY/#mcFq)<(Iȇ"xGn]@O$@zȜ{(cphgwlFz q{ ٰ.ӟOVWe"gfo]؝qiaádI֐-Rd'.*ط%%Q "KgYV">ws=9*a؆m=15mAR*RChL1STuH{:o%löf&;=0i|3ƕE'C't)mbi ȵ(%$R3Ք |j-ANav pP>=݂m5Vy1xW`f}_2N鯌 n'K<n|N{U-&k-g-y=5.X6ȇM;,{{`WғP79eևz(Dη1Is\׋R8Lq (rH2q~ s5MoHސ? "I/NK"yO!^zhx/~İէ97|ڙ*݁U//Uw];j:&wأߝ5"]9P:$۬z/ƠfJPz۩sSiB'6~MhD;lvh@ڲv=:}@G<0n<jl3m3TL!SCR<1:*Ϻ*V*oQbu`V'6QY/kYt_֎m~*t%MPT!iǠn ܔ<{2{2oּGΔg2itKՅyXk'tօWʩ9D(|Fdc54Ra:䫣oIV`~ZlޡT?{XVK؆m@Tb%}M&VW/1>̷J jr8K53T{WR2Rv$r՞/,T>EY=piZjbYRشϖfG/9pVδ_%k=&kJmV˶_̬zIuԾNG|Is/^lCmViIGܹ:(_rY(7/S]u>iLe,?6GEHmH:T͊`y9#>n%`5s0gjn$xz5L;:,&Gl1@V(ݹaLܹ. nUubMuI$se"<)3Y3Y@ QƉr<`3k&ldC e: s?EU8&bքzyShW,%Zx-wLi CPFF%%S^ - b{1'\?ۻuu+cOt(76$SNt+6SvEqٳɓ{%\gd"j+g(Qp|'jە.c kn ׄ e}L!00htZ@0wt8J1@'!85ڥ;kkeRU#vu_^+ %Ȍ=~$tݽ61ph+kb6rv&M,oב^)Vp p2hΉ?^͟f徿GZ!v=  (s:8/G_FOĈ+7PMu1֌P}msy`sxb'/ёwP@-d.d|=/p^ Ax)q)Q܃9S0b'zp%qt}Nk?Ғk*}H,索5B^aa'8YtCj؆m;5 ;{/.x45JHzdD؁wAv2Mll38])ʌnye9Ñ4Md=@-U؆mh{%ޚRvjol&<܅'5ʟ(¶? *PMԞZo|*hk_DbjSy$׿(t/ꝑܬDfH3ygl4 ?9m~5>] 'fgrw0U 5[y$ n.0;`gmlTkwpYEC )I]!2MO|DU|{P14'{};yyNĬfPj"ݎ [1ЄÄ?Hn'aVknܬb`z ԆR gSEF$/;O+g%LX"pӴlbQdm<1ZQC3b$GLͩ>}@&֪Gz;~ xxO2t#J8{xo u'ˡ7>#1-}֙'k&՞nϏWPT](q޾-s+wڧ6*yX-&i;l0|0,|]||u1PِTe!# զd+G_2po=qpMi ΁L']֝19̦$*8#]#C祝G6'W~D JX^^G +t F&&K]R]R D<%ɾ哦K"v)_3)"*O? X|Y %}7ςY|p&BP싒{#5nX=gl(0J$~M8uEE9^$" Aڭ.37,èt>0OT/1~ xz- :ncjkwfXXBP_8|7@8B k kf6i& &SpH!~:);9ҕ$~-msc쒼&qSN[[ 8^3":; U!pMͺYc:Rwr]JOG:5hjGc1: f:6(&I_mytNl{*wT{Mلe2$32E2eʔ!cD(ce RP!ȔlJ2wg߽{;uι{[{ylQ[<A +x55ɐ@ăcWߞ lrb@C#D緝26v ?nP6I=0']aNهؚN@wn_RQ+;H>*''k#o CMjp6>)%R_.F8L!;]}F .uA2./8zKK@Ezno44Ulڔb߳})suv`½Ezr1ylgY_|a=<^e,Vbl 'k~+m8r땛6ǰ ۀݬ BAΩ6~[ N񓌼؆mVV-Jפzðnk c*<#>-: @3?ђO*+dmnlP5О-TX:RO.m~lök;(*`jO/44ynċwnBSf۰f|8̘Z_-/WQoߝmc._A?_+Avge|,׺.[~oėMci7Al@ FI]EpI`onnaPte|3DBrU &?/8w'Pۉ'%Bh /|,/ZS=88~ rrZG:~&KfU?EK|l8l$Phgws.jy&{.L+Q:Qy>`PPZZ-⠰yt& P$$ĩT}g S%_zˡ~jDcDy͎u+%@.?>ǐUqo0kR= LiYmU{V("u-">FSp zbAN;dhj ae a`NNBNBO v@!A*)  ms8'tIS*k3ЉJT]D(F0c(.ީXN_(Q6őV=4AI>ɲRRN^G3}j.PO{= 7u>?-&uϾ4~wgbT;##C'j]}}mg2sBGQXY}*njie⢏lDvbY-ٴDjoRMt^ #K&Io, F:Dc>ƱGpf lF$fޟʪӷ*+ǜ%ZZyZEM@qp ڮ ߈&m=|3ϼ"T2Nq1p1܏UH9 %$v"C5TO}OnTHr:ޒtc _sXhHTw1^{Kwxtts:'|sz!-r9AtY^1<fѠvy4q [;B ۀ /۴ uAT}hԸܸ[f[V>Rg*hWKquթ?R_L ወv"-  ,_1iJ$|/#¤LS؍Ƴa Zsu[buHӁrbNC]ӼQQ35 :y(ccvuCuC8u2p2OK(gιx*z%r:wIbz9Kԥ a^|f't |Ma&θ^D[ ijCJy RI}wl2$] Gdw7kkˠAitFໄVU7lk!bOf./Jv}eP5eS;RYY?>XB5sA\Z1a_ RdOK ^Igf|mRsdfkgW/\m7jF)ܺ^PKN.X {:@Nyy )5aL~lrUvſlc'YZU$Z-.@,w`@VsXb,E Y RzK5 &Dz/ASj/y:o)ߥYr-wKN`=2xxrO5ގѥ{8@X2.ooq0N+tJNӱ2&k|͔5R? ӂUIRэԙyX}% 4y7n yowÒ[f[fn"mּּg?N:i b\B7;?3sf遧̄dM^ٺ̅^{{[BB4g\[ݐH ?#6O8_zJw+y0VnO^1"cPw]P϶-1K48k{|ҀajBАH 0bɎ<^NpPNV#t|kt. Q QKvFS8 ɐtBatǜ@C,g1ATkvD {ts=Ɍqw ιIn@!znA}C=q$  :#gLuٺܰ~Ř#^)taA}rt+2 NEY?.cš \91\Z5YޥR }gLӅkc_uq57lz6UЦ6|wټ'㮭`۟T&3sމ)^(-$ 4ɦ%zmh#"ans]&LnH^(7O{Ysݍd}xT9g&RKW +MNCGYWoZvMkq:cE!AՅ֨%Npݵ kЌ1x:bZ]Ĝ;ʭHL|vfWWЫSiM:}%r1Y4H\d:4tԭc` ρɧ+(wxk1i4w.{\ oiuotةMxq1aPvv::ݴBst>"APtmmmm 5u1*bq{w?[ s=C\IL3*j}5.k>U#t=ImCu_I;C #eB?ƛ8 G q`S0Vt% }z1C0!䛨TS3ΌgSq?{"@r/CxgiiW]0b`-z?FC~&ÒWےxi`v]d}z^Z58s G꫽Vp=V]= ڻ*jwK>3I˗5Sf}lOΊ@7YMf@d!,/  #w]>e (a lp@::zοکM[0'W7~~q:dž; C$f9 _i8}qk"{=d";Y",II,ٲ]"%d,%d"Ix8963ns\ϻy;4DHj\[٬ĩ>oxUQP^[,HH\| *H7#|nb.WJLGtaa񦪱1b\)yg筸$ln{Ͻ6I9!d:bqR{DX7+ꃫ)|R n EPqך(׻cJӴMs2.In$y]m{}T`WJCitcB~fPY_k a-֫ Wwe ?>.gՑEmyFr0k_=YHS4ŞuuUk#5=Ttf]p̐Um$oRwG JӬGJ&THD A(^4ݎ4,x!)xe4b?/,KA; AcN@u%{LMΠ,"c|9jLrӡZZ$)-p7J^bc;X (yPb鹮(TոBɢAL=4@pD[:>mGoG F݅q1s!1޻QNOcm妵 OG63(h~Ldz~&׊9M+!;N"t">Fհ<ɂ523p˸6löbHlCb3܎g6~L!/O 6lOh5Y剡͜pɲ. <*:/SFѓx؆mRCo/={.қT])UΑ|`KzIi#R\t8C k}c3N?7STL-HA׹n䡣+;8Z_M.O}e u&C?)ۑː<ݎ)"q˔6үo 6|8寨Tԑ$!WJuݺ;@؍ڡp5K (h.z*w@vi6w ;,Ժ@Oc|j_%+0=b]1+( XK|pKJ߀z8ERV1I_&<#yng0pѝU.؝)bźҔR R G7tH`hbIbqYHWtTtSW@ǟm;24VlǮRd|Y2E[7q!_O>RKb•& l6a_m8wm+> LQOץ6T}ߓ3]؆m9SƏ#1< Mҧ~Hh5:D-m*]V'6G!E W4<:øɷimoK:Ank 1 q1E'[OoXKF/|Sc~mW[!]ɰ 2%.H 2<ޭUmضP {4 "9 \o4aWęRcft#(3KcnpiѪ-rv۽]De}icY.o+iFJ-^k0z;%D unc?l̊Tj;mMS>OI9J; :a+0j"hOG4#kOr~%y+6aQ:Uj|#r6[J{3IrЅ$Z@KӃOH̵߶0Qdogl̨|La;o$ gt<@)[_2!n35@7xX!)Ԋ)LlY?]ZG/dMӻVB8_@ /;f)ge=sHETCC r rZ35!#~utMәccO͚̚T ዘ?w3]) }Fw]x4aИJY;k.𓎽)iɤhd\xضu >f66N6VbHi mmp|C]löKc{7pQnRx ?\?W,;>J؆m Lwg7̪^c@4v/ڬ2%c.*o6lkryUFԍ+f*駞psxxEF7(@4lö(/r™?c1b0ۼ|)DVwc-yPܑa 3U8suԡd>KIǎ\:..($*&CJ4gyTl'2ZFuVbbqM >+ pΝB A|1 TJ# *|ѦخR}<J c-^] !Rҹ RLtslFm6`_vv?wwt|&[F%A'A'T' jG'ƖSfHu@"az!@tyug!v[Idq |)}7)G5}S{?y8q2̶yg22%dΘ9Qfs(D8=Zg-{]=tg{kmfYws~_y^8`띈܊JrdɴzϏ냊V^Q;ڕ"=&Ԥ}Dm5trL8cYSdjר2 ?08 GM "fHrrۂ&ao,t H1?LM,uN?$ς S?hbeҪC ]e͚vSC6m۫mCZ00cc)ּA&0o@6sRAQA_ξzS:^:la x1`E&RL@Jd++4d +qL'SpJ#ښߴС$YwScEэj4]:\WG-4 Lܝ}0hk7BcuF|Z^|RI[Qְ8[`|TZ}-q]@APQW'A J:<)e2|mY%sr^$d*-Dщ8=̢ڣw%8?' 8Y IDݏu^2;Sm #d]""|~8JpT0 3D7U 6(>GE Sbxh|SHL@Z|n6te2v U89rW?cۦ=~ZgdXdX./O-ԑ#i11f#b@lB..=rIr1] qNƈު{Tzغ5\@Wz,T2/%mVE+8ӠnƗ~?C+?Man[2zY?LA G3#"ȴ M 3sqY5ύ vSg~8 d̜Ŝ|E6`:E fSz )dĚB4sՠx dNVE߉]|l5^WlWS6fC ŧ%>+|ljXJJ%͔?]0RvxٵY]6#F`VǗ ߧns'-!ZU&ΦBf[HT揥1$9-W~ vB8kCr"dyudxqӷjU,тhıKlOMy7|^PQE5T;Y;pDM+Z|YӫUIYА:=2yZ'gKqM* Tl%h;=i*1V~u13^jSJ^0?r>|~GK9ݿaO.!fFXVgUZuJ]d k & pѰQr x Ǣu,otG1/K}xC-#jf$HfAHƊI*ǞtrkD,Zz4x?lA:kFS&G킘ki2*];l> mHzwg-8ԗmO1 yXl\EԚДԐ\)6F&5oy~i3ݏn/ aNȒRZB¢(=;P Hs]JKm#, "NsJ=Чn?yL0{pnqHݹPPSx*Jn@n`jJp0dt%GPͦͦMMRXRX*Y3uCE=S3;E {qRvYDńd[S|\>Qdd5Efĸ0LLk߂= `AVZ Ph˹4;rlizu,(aY*TDȌ:.W rrI@12_L ƵbR/|Z:p.`_n UG\G\n^n @3K(݈݈dL@L@{QIZs3.S1q52~ =fJ"pӬJ:-hd<rmO/Oᯱ"Eߞlzo`o {UeJJuIj] ݋#`j?mx\2j'#T/CFwcu{yKDo]%z7VK9rf_A\AEZhaک}gX=LAeִyЏ0L-dT*q@ GȾoe ЁSF^F.f5.(3fm]m4Px?˲z'HlT::)E)E)8gҗ=(l\)?lԭV0@oBmIrg~v<;/싓#}4h8lI%(4N8q3#Ȏ+^x ,]$ZH{C_͗ !Җ/,*,~`^FzHZpLST  `<@|>14'N "s ^"όlqJG;p5sBAղZ9M (+&rDgff<}OdtNf* on)X 7;;_鮾G q+Hat}8xZ~í& LJR+Qed*?,g骱OGa|K-B\+6O^»QMS/bWXHm!k󂿱k}qJxڿU(TpQ]h׭ٙp:]E]$ Ƒ"Sw$Oݽ I572'\mC_9YTy~ p 0nր8 g_'SSH-A2 Ik)`H xE_0j/?A9|襴>>`i:'Oo3eS<ʶ@`^䰙9S7ȠႏX}""*N>s5.aDyQw*|9b_1_1 ~_4<nksEީ<#@Z0I_6@יct^ L0vOA&pci\l8SR\iكٺi \,|S [H)9).M7"+j(ylY e e-=lޘ=D xA?(}u?”BpIx\'_ 7&0@Jd\'Ca~_Q1 GPYF32Ҿ;pUN 2\1m?~4bBE2=@#!!7v]垼 A6ٵ$=yF9 $fRCƓi8}q-F>]-KBYJd,Y,Yg YBvQ%K"9vθzkf9o?4U&xb".j92=]Ⱥ%ћg{RWǙPCC 5 7L^P||&/U_%4ģf_w_)hZƠgM߿"b.? KEHCZn1 wU#ϘJ>/s\9)yB?:x5\Z ahχִGZ!qYyd|>p\õ˥my4LŻ2aÖ߲x^~ SRK>3OSޙYlѕ5d(/iܽDFP;)hf+_rR8V-ɀogsh'=Qk\zۧUDęDsBQ>8_DvGP9ݒ~P<eU @b9T^U< +,ex?#lJͿ/ 0HgP+i@:ݘ %%% pfıgiOtճ՘n[y?*:.租s8: zsyl;p]:mŊM+^i"'{<ɦ&F!@ @%s_4#ب8p3{qc+:B:B}[!z0Q=\.UX*nʶ  -(RF@=P '5Ud ecݠF(by<6T)`;o+~-mj>'TYeYx88 `t$i=.XtDmQmpRġeiFM@Qլ$%ʭߣ,_H4JHە77#2 K1J(I@%6֙EOE1PДD'6A>q.xvWt^QKDY\y)(IOIO&m&&Q qnLlKPKK$.]o5'8S: R٢ 6cv-A?Ȃ߅P&AR7k-e(B_{OC15B3 bb7.?ڸ! N)T|{YZNæi"ߧ-ґox.[RS p ~GeUjZjxصuiL24y{Q] t ?~K9`Byo ds[5}R%ʉWaNpS2ѩk^`SuRL TP҂?2&Vڐqas**.*%|p^v@w76˽7kӮznk>u>Unܹ uM4O?Xdj)Jٕv Oh0kY(" i֣~}M=7T>ڏ,!W70&(g"L$`F (= tG)|M0#[ lVe9ƚ2Ad ^}jt+so0Ar Da7F@ h,N;ȅ=]9JzjJgӭA{j~8@Kv^Astcr,TP\E~B0^Jas6Se% QMDl s^pzPӷμ'oB0nmZZƽ.wZZFF;[,AS1@M>VV~lN~Ntj5Sz﭅]6mSTwvڕB?:3O&!{-+qt:Cs>pL]1Y+γTrƵ߫b_o@8jP:]y&qo]3Ӏ IP)\kڧS#9|<FI4dTMz&5dk5Iַz92F㫲elAPgk!6F+| >Wt [Ӓv\?9#9o/A:Z(Izǟ0`(_ MJ@ZB0]Z8!| –{[U"+_ oEVeShV%%9v@, 7@Qa,4*?ƒg OϦTyFOϹNcOBth66alS{m4Ep+Eu. "+;z쯯I&:O U{ m62IDFjw=pVaqϯshjq\ ߊ&&׎īzp4K(d1Az{QQȉM[q&=C 0 yr&aGB#n&/x}C?g27H'edwHq1R5Ri STiӋxͬͱAwڙ))yesBw:SCgwF"г5_Jzç=G?oov,Q@f sew߈ż]Alѓ둯;TLZu g^Xcj婰J'#7V^Cܾj9{5lD (.H8$M`|h|X }rÙ(4Nxs 7}vD!4*-ɤĽضJbwpFb 6!Da>τd)n.]9`{Tmgߋ@=?}wrlT#C*P44ʀgziiƇ1AwNEz7|c} Ms[^e݁hqV4\Tsl[[.p|I4<>[kEQ;%r_GL*beFQ_ BQ0LyBWt-y Q6'-zc9p,]%;s7\f\s;s_63g4M4~[]8JwZ}EdrzCV NU֦tbJofCqkB}M~NR1t0|}}Awq4 '%p锷8[#ոVʬa9U?3kw%y7F~ܢ4ꎻ{3zN[ N+J\iw3k#ܢ"}DT\}?Ba/5o=pg(ϣɒlRVo@8WWR@ZZEZq\P#g|fYE ˢmkɞɭD௮}M \\;CW;d-y)CDp hFi)Lʳ7>z}IS7x*cQq~mXOk<.ݫTYJ8}g2<< c@|&VL{* 71 d{QPDd ۭLW(_Nk[W-r7oJ w[]Iiށg iLw֡+f܃v@ڣxs*/9NaoH3)V-qdS +<_L+P=0`*~;>)6Q2/ At;iN=sEɶãC"{-} ]F E (7F)S)6jv"և:{ڏP^z`)Ds U C=}[cScŒ[I R[j1<0w痷,R>_ga3捬oO2tosO!O#XMEY~ —)_|:4XX$W6w oKC):i@rX&jphXQLrbQ\lb#ֶ|mp0Ev^_Q ; P>2nY5i-kR@["_MKI:^|bl Vce5&|s<Ť;}Jt=WѺ:ĉ)R !!;w;ϴgjIֿ!Db"Ԏf3`~@.JNFD^&Q󷓯+br7 ˼xe㉒y_@"VmMuknҤ~>2 xwL_VPVOĠ+gI@YH"Blv.;a~؈?u1ۭIJK@e[y?^ή5X^V+FD#UmiAEҘȏѠ?idTѾ9S#L)z8A1 b}N@N2sգI TSI:k=^"2;H3펮 }:}EXU?kOk?9*x~8Wqєط7U>5JH~kVUcJ9Qyg09yjy*%:7DNIk5#ޘ> 7?DEPFG7N^t\G3p3'=[Q'_1^2ǐ6C?IpWkt/NՒk/e`Ҍ)ק/1{4x N_|1ֽd?趯]s)P}frDh'\{K>OmNHߥ \(mP$pK*Da*Ϥ p*KA9Ŧc{@td;ώ?x5e ě<-)O]\66<3r*Ex!6h‰ؐطmڰgz +ۜ5W>s^\e&yj<Z.[5[MMd6)1pǏ쬈ń cgеLL f fu?U bGFFƓjkrr|(9S? }tIqWoXy=#wN\;lןg!o6yFB-9p2z po3f Im1 b{(eIjgki܃ӳF{3 %h4GKfpIyY2p?kOhfbSi=fV>{ߛaܣ:_Jv|[$kZk^fWdJjI8ZcQ/{iq ~FHF0D Sn$!++]g Y< xH Wclbc- t*JzNp>y})||@Ixx lGPT$+}yK0ef &ۚĤ`xC!8oRAtV-2dA^P搞 05ۻ&}S@qU.([#@ ෗2pJV>['{W oQsګw$m3fwV UU`_d/C?0}~ ҷ2Vy 㡕 3ij/v`}9mf*2(EWBR7wEsJ8p-5De JL,wk!<(xDɱdpKU#"̉߮%ۉDd飮rg<-+-+!~D=5pzV6b:Vfw:Gcӫ`\ACŅyBP{K[[dce쩤~bD(5u>>dg$Bs:0ݑLa~1aTgӎϹBCuӨ>0M~K[iH`{@%Y+'#m`kt)m2$G?p\ϰ6)|*lX}W34ќ'@6C _Sn)2]@6DRQw;q6$S*2u) )9Iǐ/+QGP-*}0@_?pT`'/Ԉ'"7,/u 4&D1˚&[LҨj"piibJ:{!Bcǰ%cYJ1䔔+w.wtj~fawa7D1r~3GwkJ[;A{(n{tYZ)?$7""Ih_i ZXxttPXj5\J;^Uykņ궂 [G3\ {k{MN欠ah窑 o{mrlh"%m\ZjTs3 GcW+ZR5ǷگղWs~bs'/p*}QAH˃A)tzCpjolh*=x_m WoZa]vW.N|ǕZn2D [Tb=;B&v|/=s˕~6tfKL(G&X 4b @Zd9%Ȍ ~H `z8!8s vZY<ٮil &@'Ġ oj^Q"6AgH^#&]plKAQ5RO`P7Ծ^|=4_$$H@%y|+u(2@FYlL{\_@ fFgbU\O?蟉YBD{%|I 9~m.7f5f.8x#$s:Sl~?qgJ\<#T#T/TUe%e%9{D%@iޘV|tz>>K"1- QѴ`Z= Ǝ`Gd10\]dGIOE\Ibrpټr(]dEs Mo +ni?6{"īiQIXCGϣHpe\7iĵz7'٣w_/3hjJ8m1 zߘ Q fZsnL 0C=F ]aE[QQfŪv=={-΅ `vqsy zY+fR"XDҊ`CÓT4P;ya3^F\@hm%7\,bx8rg3\}"A O O3Sڷ0Q%Ae3g3[ujuj)[gËQ3Dr4-";IDѳ7v;;?]}D tK~brj@!;MxJO_ʨamY}I=UM{N":!0HdG\ud#qfv_{F%Wj$QyzٺlER\#Gj 룸k@[tB6)ѬuV՟zQH2Nӣc.kVQ.߉,a3G%kϊ ါ&2w_ 7٢]p(5<5q \ftbW\p+ؙGKڜhh83TM`f^X5W\õFྣllj lai|\`91g#M~yxtm7͝C>A+-T}]>^~XB1n-.?)br'EKh,>I { fEBT`"b:q;ZZ!o턂ІiP6@dʋ^O2RS\xQXM@l,Cŭ3懋j_Ҧcw̩iH"ڙ B-3

_lWdW*+$IpڬJPpK9//gA} +ݺ-x5nUUc@4Q~;Yنc3ْY9 _cHJp)@^xBf= }G|?W7z"V1)w\Q$rPXO >&9gYG r ľ};-m4T9!IqSG:9} Iȁz\)Q~װ1H5:9"?Շ jZ@w&̼ZgYwҘ<>>av>, *M̛yEGgϞѨvv/kT᫸|ڱ7,026Sf*<%>4dL< k|, %y]f2L(Rܭd〒\Ff(l$D#Ŧ11Չۋl'(W(z YuFBhkJ&@E@oP+"?E`3xo۳j6r67oQN:YÏ33L7 s`~0ͬ,̏@3UHI̜'>fX`Y݊M7w?ky+D,~ Y_=-vh(e]'#I% wAëU9kksSbHuPsQJYE= /"|jSo=\=wԻ Yw;Ja4|gsdC UU M,a,*}w$[}xYYVеS>EɞfwB#֖0 %ц*+j], X:%1%֖ 98e6^՚J$MMi="d o]|niSIUbcO.Uw%hd#$$6쎲t%ݗvxPP\$`2.vj Au fPXŀeajl`|y d8s*6rK!3߈m( tDw9HH/88y@NFB|UM&&xVeG'ǬgJ?$BR̩R%۬JnYO+z Ew`.[͉IG^:`k=1-ʚ)ȸ7q_rr@ꯪ\7jQV^EDZe"I#sPa.Jw)EtEBAJ@@J)9tHKKw"%%"ĊJ=xOSj+*|]d+HN/pڢ͚Lmg~˴+f &K.@_i5~*OC 3ʚgMteE软n~!|.g %ܗ{Uf栳~ϳ~_4 f!zFTUryTUՔO1l(05Ty*>}#jgv+tƚyb4=KޥI[ty}tL`v$ H. jB_묛jvxPu&EP>0aZMU'^ ) )m:`I + XOQ뺭˫'J 5w8/<;:^~?/4-(C?vxPUTgXr2田D3MTP*JV(WA.]:Dg@g0B -Dv &&i]f!Jkcf.8~nMЮ-xRUf(E' ܶE„W,xHbTp]i2vO|KA^lq-x| Q=LX̧KDUeecr[ 0,#M{qcf{Q"܊s ,8ZUs#??e77[ el}2q>Lc(W녿Xi; S";Di!^SJM,b riɴڴڍ҂[BCe An Z'I"  jyR_,V,Xc >S4{gԾP!ϐ#+EC>{o,!ܴ4L'4q/geWic?PeGSGΩ2RiY0 =b>S[#Ӆ|Pwe0n=A~9shddLn|,@U.ݎge Ȕ:8a׬_R\a/enZZm4A"6GVaV0 } ($"nրMs瘧C6 JjH;:)3P<͑2'oQxF"_4$G~paða$_g@I4*@q8ssӯjh+&";N+W쑽w2ղtkدdtX -, P엫MOAd{// 8i"RS{1&l^8eoXm{OWTXJGV8]8mhh\%{ZA ccNb*w*h]]LǏ3uKᱩ$4S?g:_lZ炤 PC DGYQQBN{8UfWZNsbG ` Õ0ښ4%>{ m>vTD}8.b ċW yXx|; PeZ^S~]-nE:P|+>tv`,}T#B3vFG'&:n aE++?oLoDoߟy>S.L^Dž TEl8AfSb(f9Ď|m&Tf+~)MBe9-F+nڏιԿu b ;ئ=$wOPX"'&QC ^m|| VmnZ s ##s?NTེLP1^M(,Xl 9Z'`"SC.eL9U[zƊoxpxq!>Lk XDbTMX.JOl󌖰WzEBv+%igf#˗{S^P.ya/^=L´yČR woЖ}L;RDG2+?<:A-$%iw\aT䭹Oؒ*tf dK; 5@Q k>w>_^#ou6ٜ%.}/K Ʉ;tnvYIqG ($4@Д{b_qS\Qvn Nq1Nj٣٣(]u,)i?2o[s[..LIй>\M݃vXd8%eU2_ZGEĒO!v]>pT0d3i<$fY74)FFn ;o> ؄ꭵ `e C}Lܨ' -PɆ#}&&c{H*L],NúCC:SՓ{I rhGEx_lB's$jɃGb^00V7V72t 00;ɠ|lT#P>d$3V38SzS*7:ֶS -gwb^2-IVj/zqbU-\ш3JG?k[k-}ҥө3Gp[!%)ѩVCq/f,w@Uq_hН_IEh] iǾGg P5?CrҶn CBګ8,SOlg^7;XY9's"$$41ֹ%33 Gu_*˓--l[fZf(V8$ħm>AwD佬vu(O!:of}9di) 1<3cDo/mZc} u?8lxY1~N27D!q=f p(d T=~9EtܥK#2cC..UwP-Yo)YB5R5V!ɮe,,HȒe_QZS6ss13ns?3Ϝ|Μ"@C&8P;(L$ *h5N1N @kO8k$3>]%,Lһ GRy}&Ͳ,VnD-~{vߘ諾h&ş?=9?h45H,D,GGѯ*)?Cfpkf1`%65X7aDȟZ&ϥ)SKifRu n#Fr0h/D?SRAmV(kb4"j.Q~CCsc&YSޘzbvp4.,]Ƹ3%^c{HheI\Rmw5Gk{ R/=}~y$!9C=otg[~*^\Iμ .!S0UmuƊ|qʺ?>K|HKQcyHj{wScfz3w4p`-B{9\sra*V~Уʾv/Fxhnse5`[,%@ܭCH\V2;(? cM'iB @hVne,o,/]ZM KKy1Y5I3}jjq hJٟg(Z7IUsQc.LʏUw2Dy],^p>;E7dd|  [RbM{;K$8x- ۀsy7Vgd؀ie=Q2Y+XiIt^X@%827WRQj=O̯2yȽV>dӬWI~L)r* GLEg*J0V4T'|0E3"c`k6GŒ6{W9fʮ5vflI6zl܏ϾĦ| }%Kc6 "@MVwn޹Y\DQI4(_&I"ɠxb{{{y?D)%AiKP6 ly\n^I#AԂeƟV qi\":Ic_y)ۣ_VNNtɦS'̀j+ki-l-,i))1cu1̙b;b O䟉Lt|TLU)\W+6BHǰaۑKRypRlQfWN K#$\j!$يvvZͭu`4w9hV?P]T?A!!.7TdD"x?Ư\2'@5{GӸ)(.xtfD{SZ UAYL Y 3UBި1LYZvx*"N{tc+L<?j|$2E;+Y]Z9)Ӑvu=Պ; \P=uǬ9; ߀FTCpَgzi\]7"Jm(a+~$G +kgxFWe?]IFٞtlSaG{,&}e.Z*\YJ\suWBn#sV+Y4 K:mwGW_xl,q)yaEYDG ZE5 vWB_埛h,5xKqwt!xojOWL!t?פ;O!ϧgI{Vnitn}n73$a e؄867b`֜l(٣8 Bۨqjq~W/f+NN!lфQi4@DF>x}@DAawY&`6u.)c@AsSkPvgӸ37smאi/$e=q$r$r?I1))f N44h@]v#톃y 6LH&d{ %>Hm¨~(מ3 nɫ%^0ᖈվ,S5%&f;^J@(n =" &6c=Ly~3=S jgbsBΞxUJ0qdb)8mt+=57o+:^I V \{ +aa õ$ (Ƨ$~qD6qɿQt >;_d"d{owe,bB(# yJMfl  kq./8fʖg$5qt錎9g/7C&K<r?COxG$ń+H40-52snZtU`A[(yIwc=!Ӵl%O"|偢C B }6|iDc*ܢoWxy.*2{y)#SmE oP%ojI}8d-Qtԫvo5Gۚu=ե6O^ܗN$XoZfu{…uD*ۛv y Jʏ^< QYK}s 6UnO*l\D@$v2bkL=>d>dG"WWSR25 TJ IwT՗֗^-/-+ .?>o8r\%q|B[͔=|Z*I=dMƠO{0ّ<]KMt64Jcw5v`ߛX |;r%C*դ)J)Yn);%mÎG; 0r,YվW2(7SC|n>Nᰆ^7?%5V䲨,t VT\ yR(#7L7[7;x3ښ84P$vhzV:^:2B Q{3B qc# v\HQKLNҲFky ïqA[kIMkKk!66C^*doR #44I.I.!n͈˦ZzR23ɓi_(Qo/|hjQQk|!!gX HG \ϚBݬ| {\lk95 [LCo z<1?zNWtM ,`DhlOOɨْ)!O, @1Ѷm4Ւ$dA8C{\Ps k 6,lu<[˕p">!?{{1CI֭[j6bR C>6Ya: ǨQi1=-llzq5SWiZQ!(J%G^Juy~9N_W-HZgZ߭q>[ `¯.'=ZaUOs%Iku n;*>ey3CRI$ouWUk\5Ж\j(:v, zlɧbC^,^B<=&[8kh՞uaW[a9f:7uu9)ip,uL 9f}j^fG>J]z{p /n+yL)f2aj;+bMWִsk6s3Hd_abAS5C{pӸϫŸe%NxΔuv*ٛ46;q4}nmoǎJb8wh'> \ډpgʼ8129!E!4؄?p%6[}[f.Nh(l^Z;R̉;|R/#M#M3 .@ګ!L(B5 6|tA.>HZHS l2MٟZxr)wo𨆦*k''1SCO7 qԫM::Hofcfzuk0#0b[fϙjIG&&S;񕽶$R0g=#&O/ĭLb`."-  fEErW7E@~}B13cL76v1#|$$VB ,g&9LH~= i~?RM@/Ϡ[xf {ls"a0 kkII.sd$ sbb}?v3߀gB{c? %*ZPw|8OcHiKŶќQ}i347&DZ1u+S2ƟQP~ g@^8&>É|֎q4Af82Hy QlǞ%4F9 o[ WB1c==l!B:F)PuW`^7lyG'U$a:yT76) ?t1Q9Y%d1__uv }ba[RۻWW9g?b;s_ɒ~ac'ܛkb}>?vloZQ\-aoCIK.GW`J =slB.5hń-u^\ "n5\U-8O3{ډ>U#i?xH+[h=7p ֽ|RǺX:;;0eL6ep>}/"TU-&,&/z׎.F{d2n7S-?g:7Z 98Lb"ȄW!^Sԁg | Ŧx&c3Dİnͪ>}E!pp/?|7,"i.it ?Z̷R)lfEd75gظ.cFZtܷ)lٚdO?**ZǾt_ד;1{ГKv?J5"[MStԱ}@eл`v1=r.ԅa3mhuoV^U^E_{f3kaqyB֢DՇ(kzu6:sx-70u:+p/YOs_iy67=J&s߼y}տMB 55%><""*A)Ag|AӡcI+XisY=%=%Iݸk2Ciiyy5񧵀`f>OAI0I0}]}]ck?~|:S>dU2ўL$4fLL,Bٝ%7Z;A"n QuRaיEXaͻŧ1~#4&վc]9Q@\$Q/֮wvvxƃ@Yߗ1<*E4" r!>CYgr.Ҭ4ԊzUDg'$L0֠࣪GUTn/(Uq \֖֞ 㷛~ESiJRuLqM XBި4XCUh)~)>mx`ݐA$a"Gd >ZZكFA~c'@΅/ #14 Σ̋'kg=FN6LYw2G!Ը=sI,FZRna)[ѯ{hv tI?@h}Iܞ f[s29>a|m0:=Bsb;B#g|_)å|,AVzvE{1bzBk;.)g˖eQbƒ)y7'䭹E^R ;oyCLDDo[0 Uo&"v4C !092IY#=)KiG#fCćՇ{]q!##7oF5pqb߰tzmZM*r|2#3c ȈYOoځmsT^N.\)G-Ժ}υrϱtqqi@nvu T}#ciAFWUqJLOz7 us0N85>LHDw')I5Gp֛[9סuE7'*C⇼qg>^(wu6 Q[3\6(u:?O-OMDY]Y]x{DCVptbB/ݳ̦Dx^uqhL7P2Ж*c*%?p .\܅o Zrtm<-̹0VcY9r PIX<8 {9dP\O7W7 |qey|7=°> :?@&F o0ވKWà@>5$/o[Ew&)y)HdwD<.r3zBZ?^U,]wxD NjgOJo.0oôE-pa(^o zWA(]j](m?LL 5-fbX:jl Ҏ71pN-Uu95,_&IR*&sz>Dh_CYyx2 -+I'өG(msb 4]_\ն' -h2sI9E%nPQo`;q<Ympٖ._RbnLs7,]eӓz+WOlht?KIF&ݖ[Ts"fp.C^mگj!Cl;HnAhgOvtL߳P6Fh &B0̍:o 4ծ]dWDaw}O@#4BUvT>3viVT4xMݖPt~ -Ih4<ζ;,{5gŗ-e1]1?$$4Be_)*a#zыJ:)#P配̑/Hڄk0>h:$1%v[qpzO*ޱĒ!vT*#ܩ^4zӋwo6P{縂2y>[:!&"1WnH%KQ s@[Kz$}΀o\ŗ bQV‡sO,fF4hVmt#}sJk6BS} DF{_Iy hAh,( #2^OY^[QB43%qӜ~$ωJmfs%%-F ߏM^Ƶ|4+Tk!s"s0O|^|>;bdzﮝZۺc㷾}[}Y˫t)Xp֞Wˢ&U6rw@)>A/+A 1%j?. )[RᱛG#*ڠccSR3 ڧ fj ѐ 1~|A#n7z:S)ۄ*WVқ%7}v3S}?gȸ_EUMhUbIسZ}OBhB8軵S et ŊLPN% gKQϠC2Y˙DRڽsyTbA+d '.Й Ih@.T,>u=Y5W8|0dިb}e0\0}? R;pd2EoHohWUm*c2Xz+OsHm~)飴<SSYoNN8b8MX}/TrDrDOk7頸%\\PUUN+0Qf%SRݵs~I}&LV[E(\t˼{_.I5!#Uy.isO5P&&kk|׮d]qK&VլyEPK-qX_PsR5dC^%n ȝ'-e4`}FGk xʶ"%QOԜ쓀k eHa-q- ##Y &Nn!m|wP}YS%d,(c_˾/ST"% lQ(F^4Ew9͸=Ϲs]3^so~{6'8Ȅ0B4"jZ]ПM: dvV(BYX\S".tKԂ䂤u1,,mL8֢2x힛]ySvY+9Ml4n.S+ghF>ǟtVȲd16*'@S eڏ\Odgnh$"yjrG]  F|ip#}y><| ƖxeNA '-$$c@7[l{{^׍Fcp~7MX@"?G. ((@_,\bz -9 ;  b]wF9K؂,| J*0/IIp@.KKĴGfV;wf{[x[,<3۵AZEn7S~ԁ/\P\8aśY#Q!؃{ 0RɅr$P}I!c ++٪Af38RXVT?@.9-9PMQMŃ1 䞏Xyl\L>}nvǴZ:&r9G׻d{d^|!ѝ;y܀BO#ѿ/]n""r}rW m6t݋Zǹo^YҋW[sG0g8pdk,Ga=rҸqPw{N"rv}t-ѳ$_iGZvp;dY1hqʯ|C@ 2Excv?C8)oGLqRqR>K[TQ,Q̣_͔Š:`%b+=]P $RzF2d ʒ=q]'EZMHؔ'z2ˀOTIFtOd|ɬq>Fk(nn iTk*u1-Agg̒ffnR L9Zd&{5WȺ9 GκJ5l~_q9&\3mB:AvՐ'0aׇ[ߵ[ܑ{؈ hd맺)'jΔ5H=JsyF?Z 25y~5إxӳn8T'{š ךWйLYݙ-{?:6N,X}>fObzT@lV+Y}0i>wvz"(dTKx2xFClV{F_s(?%OPofwΓ&*7E-6{2z6?ݺeER-AagcN\0խYWC?xɴ{pfv#Z6Ҿ[՛TQ67Nlug.,Obu&&l&:r[N`4.b_8 hwY[tԟ""kodϥGhB{ӶV"鍵R8I̩&;~ͷ'''.\s}=o ĞRS\P lyrgJYF˰'HOKlF7fj{7w|10JYrcuA>#y7g]c }8rzP&e&ew.u.j}Rxsd2g]v_nTP*Gqʾm\2q N~I`TŻ[8*N:0ITlw[8XB+frHK-Ү"!u;7h2T-= +!݀86P}E%Wi+;d'Wy}Jc ۚJvT!}_`zze˞Ho _sǵu 1tt]'3].WG.,$Yb. {n Ø*m*TsZ]73Q J&CΝ^Sٯヤ1j5aƭ7?/h&X+U>iw6~bI7e,+G.O9F?4*;e|1r_kim՜H؈_m)y?@33~5H!k]daSQ .bإ1RU# ǧCo @V!,};.ۿIܮ'(ncǬb0YBWK-qi3G=ʎygGW'lt+&I2kĉFk1>ZTb3;ݻG2e={XOiRHp\Sz[U)R|s5?/s^m]!8ӱAa(a_PG7X^ iL' sp'Ytu88YdRPU4膗1 K pOe4d'wQAJt Oztϐ֒0 =6[޹] %NF߿i5E 'u;S_3>ns$V3e?ݟC+eP%ܡVg9x[032Gzfk[&G8[4wκaޛUs)DDnmzt%)%oFQ z~`z W5A7 /{~F>?I;S.B_™_I]S^~Ltb8 P9@+f#AHH ,}3[ guDqo`U ;I) ~tÔÔIԼj1(`MXO?"nB\^AHU@-DlK+iBs6Dp_{#Tlll)ez)(U*<"<7giI ")YhKjl]B39/+[ESf$(Nʩ>+|xv@r 1ɠy7\,_WI|i!j~B $qYqz1$8_lnEtf8X$]G@'w4<&2b]:jWCZ?7GBgZ"O%!Ա5N#V"OvFJn-*f'wqt^[({=´vo-Hݙ(%%خ|OЙ(DZ8=/yݘx q~_ݕ8zi}ofihYD -)-T՚t7)ڛKKz zέ9I 8#v'AՓd *]S>AH?^VcT[>dy[dܽ>{}.۹~ g LODVؙԏDȐ4y"{_鮾]Gwy8C#7Bd8RJálVC{f+<&gx栖[]yB$.-OLI]%gGc?{w?p<{lqє}D!CeTdɚ-"dʒ([ȞMl.qg~s3?9Wr]=ɽ/I=D)\̢1%FN5}p[ Es>\5ۿ\y*?T-n]Sނ]ѩV O8/ZR A+V?;)ch!@@褬|d'Ǎr ۥ/*8'j_"Ϯߢ6)r\tHN\õjӑ>}v8)Ms`Xŀ3x5{jSk6-kpA2pnX \%Gk\%!Ixտ \sh3=-ŝv#xv(~b\Cȸ"CZ|  @50UP̂ᯁ*H [2`pMvh]P(nJP7k'?Ry֗Q╀a ։A烑\9جy:3wK?㼽xz2ܡ!!bhR%%~%qj(p(PIZTeݘz^KR֝ލA(ۑwfLT>9XYO<`r@NH2ߍ mnL 6J>nm˝f}t1m.YP^ZϢL6Iǥ GP=а w."h3I\G{3f5]SʡM %%^NЩ'atUO ͫ?igPw̳( k*i_ު Gw PxqHH,6[lFG<[ۯ1ol:èÈbCn?2_'_'aCVPHgs_f[f[uw7d 5S ^BM3?N1ܛk~5~)?b6cmeEXkU`@__ 5\-!IP QAor[~W#_-u9}~:s&6+ Ļh xtrRL۵IY;g^֫_!U]HL;E÷ YSú|SMob0#JWNWe@w+g"4(dOP6wX#G?$@ݿIPVNV!SP=MW䳣UڔDqjpB<#\\W7C7uqŚ(ЮGW"M"e`XY ΔfSerISΓrv-Ag=,mJ*iMd[Ƣc$<;I9;ޑj[(, Vຑ-.Al0˶KlG<앂-i yj)ݤ3ǐÎ(e Ew_Tl߻%f&?|Sr>zJWkygWWyk>rsvs21.<Z7ܡ)S7"-d4'OC]k5nj۔SSI \@y눠a#=uKг~tJH۴ybNgͳk7tAC4pvX ۵`@35L0XgHN 0=QO.|;lZ[-E/E'oƎvPE l>HZ mLҋ2gA*|2% Q.5<?-Pbct ȋ`U;9*`!ui )\"3֝H!hѨ%=QEASs e`.)+{],t%W).e/ Pew4S354U\ZKweFaR12 pG3 (̍jIArXEPt3⫟ U"a`> Kz rCZe0;M03 Vh Ժ{w;#c`oP¶¶.Z y1UUru3~JʪWf.޹1u;3;JJXp/x}TN,giS?ޕ·ٕ eK %74듛, Eۋ H#( I~ տGS9D.ࡻpiM=X֧̀lejjǸ[!2P^s+_dMFʦz :X I`!@PqSPh]lT=5B|RoFX U^ҋV66Wk@ff77g|]mR -s$H 33(#Q3ߟkpT81fC*|])tIڳX \I^سlkWh_u,'$YxYC2k~jvuj3sqjm^bTxD6159^E$6;ZMfIX? ˏq}JJo gzD|s캇Zr2FmW}dq ׶kbS-mrSWhjv [Y8~pS\Z]+Qw_tz=,֗i3柾2Iw&#N`s9j[Ɣh4e,:a`<ڹ1T^-_13J HN~AV>}JV<@A'0:@qdL1󠜇(%&l@"Cl ͘ FFg*%[F6SrAk*yOf>\ jwĕQ :wޑb(9=9=/%Tkz[gw:SC2%a7 ZscOfCHch>=gjmԟ0 ^8PdLfY|(͓P*Khi=mMzUWlz]19M,TXo?E/xMoW]1uVZ^7·FS~ew/)?;rezQIQw&[aLKAh_fz-S]٩,BrVm/˒n_OjOB]=sgUb`BBLL2$$=65ݫi:SHgzޔP 59 w͙6Hb@F)PƢKtV"S]i@4UB 3k4>`\ 4k{?><wʔٔyO[u(Ժ:|kA'.i/g,g;68@5) (O7k&; 1\.Z7- #e'ꔼ'tƪ\)|j@mc׽~t]nگR.;, ?KOh&2HyuV1Ռ d_rd[\EmUpj]ֺû}ڸȜEpC9Nkk'.2l!ckDfٌ%%e)B1C g/ɩ)Lf}vBxzhkMT`\ `|4@?`w1' j|{~_iK[sjA;RFhh;L,uL`4 uFК  I4{ #kb n .xsEvyx5ߜKU3ra%o[f p-;;z 2?$bܺUNIbL@k˔PIqMRTݿK=lt2P~#qɝ2ͦ˦(6xi yۭ}%6H9ќR:j|md< 0~rP'>Q 0yEP,^,[`ohHL+oys[ Uf_'zŠ֢FJ#l5R5Rc+:7ޔȠ0G:,v%J[U+\Fqg8_2 hXJ/ ;ݙ|qJv;Iх9 Fߝgi(R"Cu7x_BkyΡc̗cFhgC?ڜdZ2dcW̐ǂ_t4M;$O;~ ͕ې},LJٗZÆH/Fh?=H o.O"Vseωj^_]͛q!4B'qmB|igM^J00@e*Qj9$-ghjM”K5 &U}>tE/ݾRi9>B#4**`K՝'kϹVNt;q#;)KzyiFh? yxFQ;nA"ctfUvj@h?Փx&%dYJHQcBQGуepj[u)Bw;YWjeDz*<Wi3Qd6niTT)ڕ 3DݟŦkA4f~ƻ깈˿\cjO֢Ө1>V=)S >S|~Imm5Ql`ҢY #f@RBy(ܸnSUY/<r=2.=s1龵Qt~`ɭ_K-{Vy~l 7N8#PKi19lFvfTgTq2;*`K /Lx/GIf6~&l;a/ qUpPY3Vii} bc0/"D.7nFHt3S3h.g5@A gidz @5SWQH.# @[@{ظ%v*PB}( `Pu#\\]քppv뉭zzIT{oFFP4(E⫌іlNgD| һ715/WJ2"¨X{%iݢK!YzQ=U?}FDzgDĹa^\w|yg[_6k>&0&A5ߊp7$ItU sMq8N` !%: ^tݬkW.kucLģy]h-|3lQ,-##8%Q}e zUc ʬ:9*=KE$gzRmLe6iiK_`SVvuvuiSWIF@`rqEpl$|.~)6NMFZw] AHok h>|_dTyOe?O]SyǍj# <5$C&hrfufӸcuaY&{~V%M x{1d~sɸ'92yfǻ jt1>3 ΉCE~#)gGAGܥŨLEhV;%f_HCk D' SJ} S 7wvަ̾J~iudgOWGZ0LGhuGUT0coGlbV0xdxYov3C/8Z\,GYF ! z[Re)qS+ [+> uA *Cu|:(Әj-H2Ȗk@zQ]@f66oF)2Gpl ]@"  @qL|F A *[eҞC\_ᴶ ZZ&U"TT*cV-))0Ѕ޽94 rCW [lجݦ/_l!coS˰Ϲs~[-ZGۮ~1'4BNFgi>4D$+9hYzTf~|䗚R~3Rnw;V`B#B# &.~cd0(+و#պB}QMFhZ{wÂnQ9 vzY"Q2n_oEijmW DisT}Nvm^k2Xt3(puq9]2$/F0C(,|)2ӯ;OoA$԰Yjaޛ'eJ.asNVxg|b1n~3V&V'ebgU`jsҕ1>>ٌ/lK B'ySڗ~>KY| cp팈3"EXA}_]+Vv4D' ~ ~KRr6K6mϐϐ~j%%,)kcTjΞ;_l=W=y#ۤR=;| 9;UU.Qówp5QT(ş[|}I񞵌WMSi76!Cx%yD~o\vC3z^;bGԺa§kN*Ӵt:XΐX$ơ4-bc٘6Qkf;;a7DA00 j@$K$3BCY@s xOXlN;N1K#hZHw;4hԂ-'4'4y6yƃST͇m}kBff{7eo;8nM"Td'}%,cYȚ,I({ȾSB-Qu;_w֧s<3y&A-DQZ[yBg]GFj1ѓY/YG+mҧ~*%,X'gW}n|sgC#dЅo4SldVv?<$13|o,UEo=#9 nI\:d *"-7PIFm(Foeiq ,&~{Y f1VEP &}Wtfp1U o>Qٗ6W6EUJU@n TBB9o9Rp:XE2: ƠVF)FKJ:Й8,f2FՕ;ϼĵL\[QP<1Kly&&&6PbfVfd4@܁{KaPW&JQ9.x ڻ%[`圸R26N"U,麐ؘ Rټ#E"MDcا`~-M}Lig3}k8|#լ/p4 F n@JV8mZ }pPYm'cp#$urH'OkAoc_I˂@-aencS4 ub槾3 YZ)T1EVv@`{)WGG Nʁ%7A[Yuffwcӊk[_v.F;jv)Emu[h=-vql/y_i%K%n_K =yXVUUQhECTmPNM|t%{MA凌Knv~"~D˝L0%*VOmҁoO# vtnIN}{9 8t^=4ʌw7|TuIQ(Ily$=]6RW{^UT 6kv|!ORU 5=dz R6TVWTj_ώ쾋d|Oѷ]n=ˑ:3sfUaS#k{ _m $<ʩ~'VwDhީGC~s=K@'=XpAӴ1Ɖ냗5 wJwzp\CaqH5;_|HsY n p·N3R((ɲY`Szn iFWZO U]M)hfb't^"ӓ~n2[ݯTCQPcЗp?+astJ=q_feT z&F Fa}@ FŽHU..e@v ȯ3;AJ^ T/]P<].nF\;avj\:,ѹܻ;2e3o4aa?U#_#OC6nS v?9/8/@<+(+HLMŖq47vwoXM^fkGsA\ه!Um6^tp?c[Q^:15Jy=6 Wn+O>࠙=gZ@.e [ugp{]7.OXIKCq=79~Ǎ)mgf[6㗒~ϧ\UZZU;@!v9ΚB{?*)*)*3sftR0@تx=LwѾ  ?̧?Fx|0G6MӀF``!2 ?UmiIwōrr56Z H^G1_ j_ydw@7@K =)mT3/pq8ޟظ߂ş 42DkϢԛk/fY~LPss2G@/CGB-8-8א=*|Tv-uZL*5ԃ^u|Vؠ:{3vEHg3L~aJpyԵ e> !  Tw ǥ0\pر],"$4[Bb?AzCp@p:R\2(5iΤdי- <5@U_Jdb:Ǝ†NpBBӞӞމ\Ragbf,{n}ZHwmFFۮ;5qb#M5+N1jo BڵLgKj(C1tkOVsE" ©©^oߚcᛨ\%׏}Ai_UղGY>6c6ٓ{fsXR|6._KX0{ӲyfSAHwחt7Bο0)#>Qmfj{\C<K􃟰ő&2${w#>՜H76="jѻѮ9r8_?U"9w1$R^ӡ !R"Iw9\\nY@čCfn:([lJ9Fioԩks'4)IlѹDy]dww/q1U?&2'8'|]7]g~]/iZdGڏ;EjX@@Sdo@5qߚ 0OvSbs J#,=RX9[xe?ҤV[STށ+-MOuM[NQ۪ܪĐD۞O3p/C o'6:m@>BU X8lMXJ `g뜁'K-bx4OQ 3, O=~E9̹hbMO/ǟX[bvXU=@-YD}Eyy7QD3!Tfgx& Kzs$륆Ȩ( ~Hd9:$A>gqzbg $Q|S(>;B4 )oA7zz:YRI:f(qqa0|hQ=KHE88fqqRp}Dg {FZc /lspgK+0 ^pfrOQOLK*E>? <َGI "8$3Pνt X&PfdׁPtt4Bd!Slv8 sw@RlL2CGF4<6&wql6w'fg|;3^8e,,rٳ C.BBjFGmxx+jz~RAL:DJ()Am!4K6ĎߗgΣ%cf*Ez(y^ߕVAɝKhe]1Bmf}jG rjpg%^c6)=W7a :6y"Xdzbgf;K]Be&#ZԔ.$}AQ ܑt龢Afߗyy MZFݬ.\խ)[;"%f4@OWmfn*HwדI yֵ/+-+Eq@xY>B uGpU*)esXȾrfMWHY,"^l3e?+KA%W;_Xwxt{@ g^{{eщ׉܇*)t-QS.4ufm[ =uO ($s#G-mbJ wE!z?3 ynPb|<KIQ WNEJJ.![,@5Z||4@~BKJ CV})C}[RB^2U:&S$+b}Ss^mZSb=f36t[3S{B[?dX 8f43'>fש;Y[p}fM{UQK?f5=OzۛK؋SpCQ R(N|a^1NÓizAԧ/5Y {qgFU‘̬:u!""ҝ!ǿB|k\!g_>X LbeHY^A*>'Z oHy-|U2C]$դ٤+NtG%\3_3(=Il2Q$$fiX m=^A &T5-I>?֣Im(zV3-q{+%͘;/Zmg2Kcu~OQ<.FX@+;=y#:ag{.Y7|۪IUζw᳚\G7I͉ dmgg5%:xP!k*Ŧ^TH=WNBRSm)jk§2=gZ#$<vE<]>jZNӮ0=F,7fB?-Ƈ|:%kS,n~c8鵴8ZS_5g&S:#kz]lcS4 \Mف{vR--,hͥGHh9 0iK:e!! BO 6;`s4"^# )S\-7UWѤskn aB}5G~_1ݞ=syZb%aXĖ}W3W3--Jq~w'dsxT,~nIAVI+ Gbrig|v?J@ K9 !.جcc2Mpf3hwhQ$|J .쯿Jm #J:UwMi g8ѭ_"9/.-BۮsSC5*d!S_S+_WsG^k G RA@籽 sD޽{16Ĝ)@21ʢ׺FL6BhVM2IuC+)wJhĦOu-Wޛ;6Qɘ}mm{I}!oRe-ЇRuWs䴥ڳWOPyM{Cל,ٕ3iŴ̝`XFGa$\vo.Iy5S>mqUK_{;Vw/Ʒ݈wl_*+KT~(;bx/IBgv{9`Y>S$ WmArgz.; VaS [ =[!x$+qᩤ+F4Ol,=3+~R@A' 2gnuVV R$, r(b m}mu\4"%d%y5u\xrJ/ڟlȶg8H".=|3%%X'S(ӝ"߆s9L==\:ee1##G&1g !%OtJ{f: 7L.Cg B d*آ'oz/ƙ]ZI+h4Op_tv4,0𖄞fh !(w\!qkמ**ʝ4@, 2Q-x3p.Ng!ҕJU!WXp8C9HYgʩ/yS2--”0yMyMŻƻ TB:ck4z2IҪ9#t gvE"n%\&ilwfu(1i8i)sg0ֻSs=]^& 7//&33Йgv2Y(c zu_4Y41yqLAS7H}5M +N^l%__D&Ft˧Q7 G7[zO{3ͷh5(8/!97֖K( * ZC z\;i|($ t.yԀx1r1'3Tzmtm-@V`“MML&OLL`%5:5ZD+o+7}o(|VcUt4dl#v3k]0C-x/p4#D QOCL'cwōbbeZFK = ḴñLfhf(&䧀3 ̟ΚYO 1 I{&#Qђ<?Zr4.42Hfƿ!?"[75G꼥e/|)/b]b^b.<#~bXvSWsWs~^^^"~$L}Nichmbn=GS$a4rw/Cr%t9[L|j Hc3YvYi>J:` (nPPs b  ^HZ b2LL+) (.* qiFa;opq7ڴ dO%!hԞ  g oemmNXj*L:/t~sр,b;Y66`]AT34SÃfY[x('%ݙw⥂v:m{k\-yI|RgG'|Kv_d!W], M]KK܍A{7W_iU6;v< }bv=B\lI |÷g[j+2cbڍ_Z z&nhmk豦O;O僔Â|U?9Vj7Bn$p9n wXHv!YYp`_wi7cu1MTlojT$›]#`!2 wk¤irѓoZm YKw68bOvL^r ۇ#Lo=|22l豫%^_YKP:oZoeU2RDڮEyN-F[Ňy/BEjL{ZHهFǥXt /h74g99t3b nCO:<;G {[p^ÈJL=3{(^ :'9H'ۯu5uHLfXhZ=]AGu|E۴]Q<Ӽ+h泠tY>8)B=țݬZZ'_`N Gpy=d;ؤV2FϭQewxɢآ8&&#L46|&ƞRRO*(cTri+L,[qDRf׺b0ehܐwzlH`  $hd<鬉=p 1A 6P]Tc#v1ѠtvBΑBzNP ?ӘvvLJ"_~21(0č7QGBrgDr2l 5b-XR"=R[-/``k )a{]wPǓRֲer يHBlYPhD!D QNH,hȒlIHT#n;u}gz3s>gu5$$BJiAc4:KUY6Vu x;˦7]Sq=I|pߍ'3t賓BxRL~x[6"kE%?}Mbl]UĈ~n6%>wA)ƴIYk+x1^. \c,9Z*t>/7nfhXe1;q[p @bQRA AԷ!?SJÀUF6.@ޒQi V&\hǖXORakUReeLO5ZZCx@(lO.Ljnj? }(yܑ z;bO۶l7SQpa>GB9)_j؆۽@U"rŚU,q }>0{+.6HDlW7KME߬ FڍfON2搤8-B*懁k 1.햀!l>5?_h陂PlEvMP<}ػXY s@:Yg-.ψu*ΟQnkXgXK\` CQROL^BBXNs᷶.ckdHŮZ%۝e'~cɢude]_vj|CW0;@bО4]=|÷v\=7X"NUO 더#^c~xk= yP'@aʾEi54̜:}Ϋd#-I^uqqװ ?y Ť4 GjJjj˫˫Ĵ< >SbR|O H԰n7S\{= s?>]J1d'OCnL^ ,s1P歸ILlVXϤ31 s&:!wg3S@~[d!I QJ{ ɫM`ՁhhxEg6 ƈdFE*! Ǹ:[4TTB lrhềw_^.eC('1., [._5|]´S{BpkYkO汔;55>7'Pcam,UUo={n;3U}NN^{ߚ nNcMV~%RQmco]-[U#OȮuPѲ*nꉆ^{ϡڇos[1YcXOkWDJʖ0uR`Iy{睿FT!~}?1eJ4L:FGEA^#C: F1?5|÷ F`o NCzS@F!_'7|ZU7ˌyd N5^>'i̎`4eF|÷߭iiʉe*mbfQ1GWow7 Ê[Rw&T>N{.Ь1% 'r?){ ލu>x+6Vn[.aSfW6] |K*P0c .#f@fiiN0G3LjOAK e*GЃh~w. - SNM|x-nB3O@yLv.ի=e:'J :49g&S;:;ܤ夥&&9[ɐCHl7?*~6O>Oש==z<|&8mRoY1l7S|}| gv2 cB]=]T ؟e'6WׄW6ȉ1㸆KTQx)z"f== QEcYV$: #v 4Q/^uC!ΐ4}imSH殦%p%VynЁ.EI??ēsKq8JؚnO 8~~aaVf=wFFw.$($H\\~)gg#%|"ɹM[E^O2Ȧ6Wc1:l@Na{}q%PZmAe 6~-4iHy fe-h=@e[ IO N @אBnweA&oWvyf QRW?H-''4V2EܹN[[q9ԍ˷&5yv[8edq].ןwb_ءZq%r/#²0F9 fYr\Ill ]c$ M?.NefᚱGKz9zR~yvPun7HD^T(Zn@p `ɐJŅt8|U s^F)>\lY{$icMQ{;Bi~2j OOfHB$6pԔԔj6gоFϔ 4S2^l>f/P$> YD6Wà)}bRDǙK]-~a8Իvmo\h uzMn[diGDqήoO+ӆD뫳᪦axS+Z܂:Mu_.ۥvLz).?/.UO`,(Eŷ߫-_fG/<8%e+"4!%K(){/YBdb'{d_F$lZ$J<yA;ԃěuk@Av^oaBz Vi _(qn`NmU8r|=1U3U3m/P}^YZ3Vg5gS&3?$bRU k} I2 x|< A(.kcc 盓Y授4ߘMf՜3{V%v#%cZZgMQQ=2b=Fx8bڬSksUqřdnLJ͝$V)ToMW= }9SI*T0DEnKYgKr40 %{nx__o?NW;0Bs){ !WeB,W7o-f{InfʞAܒS7@ NT}> y[vPPŽ!# perer5OH96_fW~l9{X,BңAf$\Q#EH$?5Q"TMbk<O[h70&q_Xbܬ殎=NK󬐯cD|dry ~M} %D pR&r}cUi / Y3pVD9P}}?> 1 vw.V $, 6z$Үa-TBQ Ep?Wm~5W7VLga :҉ 5H^ףÐ||bp b bQYy ]ǎY/23nSOwϫ8:Xװ(Jy6`.xS2!Qawbw̟JKɏ\\/C:a\H5mU>U!9.yAX$F@+ 8E'!dž|-!U^$ )'n\Z>d^ɨ[2 $#X9*j[ʞ}÷ ߧ:uȗAAQqNY:%}{FOy7[~zFMi_mWmx;V.zG=,96TKUOimyKzK)* 쟟^R_Z).ˠh#z:sZ&L55PFu+~.V:Ov?L&qP]`R߫W~d+VӲ_NJQ*"=P?Ԝ?TȯlRn_*)oe[FhHs3?CuFYEJ!cr;cP᠟[YZ̞|"Zzpg[F +3)xh=/5WzR$VoxNq朥L )^uOhY6~]R\w(=Ṱ*JVHҧՅK \倯^~/P$X[q}ߔN[~!~2 Jc> 2% \_TOJpXY?*'Yֳ˱bkZe,U^NFI!}kAR$;s("@ Hs$DQ w^ 'C".1 DkC*#ܙ4+֡TWo3b~9s5D!Vr Y|h`)c˜x2t1Fb t:!Yt!1p445xΕZK`[8s\ *jQ'=+}4Tͮ!^A(a"Z TZ[3D~]?  t"tb9dH%'Hi}&PXIbhCyƵBẉã#z r$9Ԥ"S歍hx3qxN ͽ~7{!α{Zu Lco§6D `f# p\q-s%Wf8 7X6Vw!E>}YɡCu赴`npV 9ctEЧcb)X#MA67V9])D Hڍ=4Рύȍx3w♋`GHM;u*'6.TT? ΥN}{Ft4Ni9/ao:d| ~.#4&rɏ)bIJw(0j35 d73$8A0Co7 {%*q`iEHKq}5gaZ/=E[_ؘ3ƼƼv Q8wo/\!hFeFez'z'،ی;@7emy#8uN星;8`5{jjG4,լ)>~uU~8onR!wJyPP TsMp ]JSL^FP{UL`ߋCd>B,CަNbAGəcB9NrK稜bJFDDO^U (_^..l~hhX qYwr8QQj/_^[Yͥ|J5ueg[4*ls%3seiVr.Q(%tҟq}skyu?C_d0F?.f]Z+|5j}Dw:(z'|ng[ `l< *$J~P;R5[^?#Wi,}bAUR5gsW#qJHN-Sw8 <&-|q ow BXL5I.A e42Xf:~SYG¹55tVuVY Oݫ:zQ*{N-D]5Y `4qZ zy|W2irMuWaq# b/$U-yۢ"z\pb)ba ,VJ4%: 9iKKNJzO NB͑rNMz4qUe6j_HJˊլЬ‹v֦֦9?s>pSK'dmÐ(}3F&1-|\J9h1!YJJD/g!3V>b_a{;Քʔ*8$mE?s8H*@q !7ǚ%^W.]aBnt2ɖL ]]tkc ަMP788:Z!ɮ r(vndd䕘44M\Te>StPbܶ:GN9l;tͿ"혗ur1} V}q3sRMӗ0(\<o8di,%;E*ڐHwYJ,%{JYG);NJ({d**Be)D=Ϝθ=s93|k>!rU@GPii{֒ btm\L:붬O PꋖeNN?f`CFh}wloY⡭d}.u1RъDtar8 o3ڿ 0;26$QWGK4˖]lTU9shv߻x7EV]mĥ@3c660dMñZhxQT:5op?7m''-flRكͺw>p-1bR]|i~v%@R%p\>Q``|-H>ҔEj>KW Z-,GA`)QqB`D62<9Bf^ L7)Gx|49 oqkŮض6<=݈܈>U{*C/ᚬ{MTLTbby\Luoݤ͔$p/B1)I8pUYzafG| 4QHIOLvc(! 8Md˞˞˛h@s߃\RΘGh7U X0yKr5Y~^|Uw%uәg\ژAUU 'Up~-mgKQywpDDF֞֞$W""hPg鏻ٙOg[7NٝIh`ywI,Yza_X0UGL8^ \tyWb B4F||od ?8m)'-ZI0&VE". 4COBt vxV."*q3 'My)&vKA$moLzd~X7T%V%nqG+)16#å*v%aO{3Uj]OiDAeLIQ L,\Conڢ9fW1+`[MVbV*E g4'`.y݊廚5- L;Z20VG#F=z!2>P(QA'E䖋S7&xe%_ pttr-0a+s*kSlS~>S#Rٽϳ#%u-%7cz]pn0\0viCG3@ӝ[m\N|~#o<1Z M'uXɰss+Η`{3mҕ  IBppN=[ GNN/NSxUf+ RiR=k%eØ/3+wĔ\XZ+'xABZ9\8k8Tȁ\Y ☮9cSBMz']3ڞk8y{U ]pS:>{%hɟl#6a{VZ*qަ?[L\*#1 !|ӷâ 6ax3${V Q$,  .^GbxAc\rCOT*AR"H~x WqL3|pܝ 2HOCsbE$\2ʺE=76y/~1+3ouVeNGfEf5nco~哪-SE2dAT-cf^_'W7ƥo~\<+MKud@Kcb6ҷ_rPFa *ݾ.o2$ yP8.;.;<10:-ѣ!V@ᾝwsx$" "Dq'DKTtvQoyc/"KKrtm{G/5aO na!h| ff=ǢߟLJ9+&XuZ!~=pgeXJ:EAG9oWǚK ƷY(ڳ _@Q "yUdgWM !ݝ*׎qT9i<*z-q j%]Y뇷>9aFwsyw3?s?m% DhtxXj"QSM'3b'vXyrC(ωhnaN.αUSr/>+F*)" jM>mR둔^~KEkF1w?WE5g?_QSElG,&͞eב XqlKĘ#)QEP`p'Qq?m iڮI,6X>C}mM?2<2 v-hdE*C{пI3vۛ{*kṪ2O߻{`y-(1Njt9"| цx#q) LQq,umfn1:ΕСԙO1yČn6Sd ᾎsaD^4x.bJ`ޢ3 .ˏw^Iy&|>si2!FF ݠ(FcDqye-{ :2mmB 1}Č5A4n/ߚ w]_*bٞ)ZFCQ< G(mXS,g1gqZgЭ[c Wfa9OT44eY63dm/ u^M@Z XʍӨHrtLRC)ElYDz(ecW؉dMLoC%%rQgpw5 E E>i{=!4y .|0|KCCeB$i 9 ?Bk_^u-1L~,LES //^%v7Ljv9mpsS|qx'ͺ 8cLz+Ll}D`J٩d[K`!cYz]| S؁i2l::c(:PP )Z$):`ɗΗQIfWO@ `h/>&f ~+&ckԿc6wT}Ǒ},e$!KDJBQd'Y8ZlEPȖlIPIHiqgk93纾{5M|KJ ޥ+U=z}}X"M(8"*"*pZZd@OB\ھ_/EIBaR[_X 'mdջ{1r7DurnBHq ;h̟`s9n(I٨ɡɁ/oyhV(CmLsx^lة]Ϻo]%1p6DAGi dְ"kv>i2+HCn{?.O{Jggymۜ--srpdd?+!v:8rE-Jy[y[o D޽>ݺp)ՙ$7s/?d3WȾ'sMC̉uȁ:O3Y| 떥եXLR?WkV ߔhNC7!>.=6;war ߦq^}7=6E7/LhɗŰ# k59NjvH) ڂ$(]ֶ>aVfr9zd=g?ars6He=%mde'u#$l`QnzQ!`B{ (vOcMޡ k tmdn>Xte6 C1 2VdžtïObxBS^) $;o5S7zu-ܗy 5_^(b4{X *0YzG! z-ڕ#fbrN{{mBFoѼPZ8OVޥR(sߋv|聰B2tNjǰqO靳?/Mñ!qә,F&f5åUXXB@^V.gO>Y>Y^}8&.WkW.#}4H{4cK2lMW"ێfJc})S~{4aXF LO K{1}ĭVA 1FތC./c#^n:ѮK)[ޙ!ƇvhJƮ3dC.BwsQ Z6ŧJ:gdMl+U,:wf:SypJNDh&ی`R:]HHk|= 8/xp-Z{ș:G͹EEDEmD61bƧ YɀЪEJhBG!-gQtفa`;6Gk\Kѫ l|"iYEC](ǫ|/Cdo(;={,;:v4a*;Zn罟U,eFdF,Y>a5SLM0tZm[_Yj?'&hƺ9즘wEj?;!d0jLv[4Lay\g7Że}k^]2v~QiEaSb/["\(މ8]>< >1JߕwgC+ yo5SZ޵yWQ|MޔK &o8/f#)@_0= 7kܛ9_ ڄ6Ғ._ Hط$џyv @H*#$rE"Ȁ@eUlD"CVjcccoR%3;+gHgs496df2{*rrD3+Az1LJS(V(zWHYYD!!_1",cı)fJ{}3pȎ/&7i1ҩ`p1Y{|4x_*M e* +vtWo6:ggn & 8φ@ Iq4$Pz{d?6Yi(+u0*.K ض{Da{t7wsxU Yد _HA``N|]ӻ $$ w7gv:>e$Vo oluf'zB<<šdsgje k][$y<4-XV6/rYB˙(N:ͮ9*kKYOm5:$FLZd<3rJ:Jɶ]7t~r5qa@%p4M2SF?lTk/tjBj5Obf~B){)g}2Xt߰a:EPo{."MdJ2?m!*P^#9;\P v 0\=QQR=綘nEEZ::&tMOgUf*)E{Y}ј$3;|bKxr¾()]g Zۆ56z`Ԫ%~ynJ`g׬Y^< e:lٱDpt=& |ҡ\wȉWP~=zJC7¡ɣkgrI)mQtu^_z`ڥޔ[z?_oH}#ST_U7jĬO_r8x*e pw##ox,-4s 9MVy!p?TG{P0IP|,|}?mfoqkeF|k Xq3j*6ח9J< ikYŜ99jGfxrϸOu;-%i$v;*JSr F#uh3\(B6; غ+4Sّ4@_<֮{;݃@wTd Vd˞d Y5K)ErTv!;Pdϖ=XCHJN399nYzf3s}j6H~UJGVt5@RT+Ȼrk;P"xD6-QxR]r; Y\ gRYx;g%5'yx:;򛥚& PFhjRvr['}}UU(IIQ|׳N]9%|qnBvۜ-SVVN[{H%O x:ij @$$.{^?ˣ1ggbg]eh4 q*vE[)G ! %y!g@1MiA%\{ =w)pbH!CVjSGkmmKc|<$C^8\Euui$u*lKM})s)¾='\c3{\TRJl gCM0WR V/gxg?}A=6 ?6{ Sede$8MLhPDU'O nA )[8QM|cԇK %vKw t2Qt/~^g=)W%)L籸\lkgeItw~d0$VSpuuT\\άN f?]|!bY6K&hUW=+@#P,L=CWo'lW qQwnq.wW7UAԆj=`,M7d[ A/!aى_?g%]Iʘ<^s'`ߟ?ҏ)y F!n$ ln̎$U9C9S%l܇ D,!;Nf 6.Cه+ܤDڋ>@`o<]Sj l'Ӳ5ڪgd Z}w)YdN:K'3vNgZOʘ[4pۭbH2D] ehcv^ 6H ^kBgPM)84Hul҃T@+t.% ls ":P 3)*~.خSsۭprhތz*由2DpbSsL'rY] C`]]J)maE7(aP-y(=,b+ QyG "=Zp%U?.o _DlQK?O?s2.3zꔤ.soT{1w1Ob44T5U5O G? A665GgGg &R&Ra^(Ѻw% t[~c[tg$cԟӱz.V+G* Qk{pXIl[\Lcijޱbd Xsiu\+58 {4#e%IW]7H_ceVo*YRǭЌT pc8a:T&1{(\|,ߌs~S'٠v;BrB-*j>*\l/ N-o!/2&ABՆRqE߃eښSрC+6y8LL#A#* [ cloF.} !/!o/[3T3b-S 8\vocAÝl'dܟ:f~R*O[xt0פT}DTwCf#k/Ʈ^{@2g;gzB'#=z l'Ӳ~#/_-^H<59Z1+qԮ}^;NeT]?^ߜ czu ŽfR&<㉞3+ޤr>T?r[Vz6ODsz;u&s",p)khdIJ^ga; 9O0wap@ǎFr. etzLYYb+1m ԸvTZkC/L:: z5Hx/(a FK@^ i. #?~}ok--kririiڬ6pA&#QOlf_BC{ibm[c#Q UK2-5L7|Lvusw|-jbMm8ȸۏlKPgNTR:/"&4 Vi.v !}߱-},y6l nu͹jb'8&?+k(RLb]@ľ)H{%oq % )HwPHEL- "~{!5m3bx#l#c խlh-|{wa2D6 hGGK<9[Lި^]"]6N٫0dw;I:]7!9i&C脿jj`{F~%_z:Zjp"@!3 U20ɭ spY YGĄLE;knC~2e&C ?2ϊ 1S)cƄ2̔y̬ %$o !D{kkwk}.9γ~@e2?\`0Ӭ̗ܤ̨(aA|'lA"yF6]^LڀI$eGYVB$1)?rR&|}:-uńqkpipU|q$$:+yN%4>:gp22^SlSl`G`Gh ;EX?g*?J;'_~[e|>x@~UGm@5#׺i3= 5IY0R)xpbP66]9inDᖬxaL] jdYRU~4 (z 3PX0Ǥ YH> ɻr<ER܉ۣ}XgأUHy W@#*,Llp;KlV*d BUU#&iNi΍J/,ع!:eCu)8E)wNDfEi96͜T`1O,gV:Mim 3ɶ^FQfx1(bǺd ufS^ mq)m”at17oPMi3\=eԷ4$؛ep)]2gj\ߥj9uً 1+twc7Ls+nZ}eMe$ r ke.ۥc,#$/V١&wS8eRvgG7 keCJޅYzI}!ݸ~+W줷*9M[bhO3# ?^SӥjM[2=xsqSiR,i!q:*3Χ P;%,V\]/?` "mi1J%zV 0 DUTAR!4C qTxJJV*,/d | PfbrWHަJ8|AIۭڤ+ؘ.gqτXm\Q|QOEw0Oev><+8ُHHiNUPB/Y8b8Ps"ztb܊,a]`ߙ*AdڵF82=2=EE3CRtp~,~,nBCׅ""Os'喊ri<IiιʙlxUr6m`7Pr],ɻml&EvZi)ßp['5ijPRZ.}<;٠Sل&j"o2Hg09.S  ^Fp:* (j}lc5wmdfeBD|PAaБ :m30X+B sEcҪ#=bl0OE5*]Y}L`ӛyWj6Mp,yy1tUtU~h簚?u$AȦ~NYWX' ˆEmSą‡ Pu7>^|$U1>SL̘KuT/WCFFG!N4iϟ->[l? itC8$'b2YCer으1ii:o7{*:FI.~f5moɪU_#|*d%V\|2&4 lK9V VfL qzl]SSPC&}J6]|ʷٔDgP’Ar>k%kٿOVH2B"N%4\ {$N/&6E:bwu r\Ş~JRUJƩ8n pn}ΨJ-7x^[cLCr8?aAWg) e FofSX?i0և :rdn^ZtUԤb$Cydl%SUffp.}R/u v7eVl쯕 {N_V^*7N{rMӺ]\hZkY$7d]&z-GJXJG&>ba35,_] q NfMS)iU;N2v6ۯSǑρWTtUJ]y&п|9㺀%+`$U('T,͎ KK?ЉC'NG DBn1{':%EY}ݡw>z34=an2 kx~v! /,6cBMtk3ڱu{biOkW*2_&X!:*l(]_>3C%)}&[D6SYcp\+hV]IN63c 9p@$^I7XxmeMW-w&.JNlt@@ۅ7GyCP> fr+W} $6rʩWzpũۍM0(rRW|s6ET Y3b|'Yo>weWḍttچO^/{aR[zHH,K5K5100ߛ+5SALR_(;wQb¯o-='铫u$6oH+#TaTT4?_߳}k.RJf,*''u0iH8vdm\=ӓņboVK\\YhF!-WEq @[tnF>qc_ы|6xk'e䏸'Zr3m}J>z.Lo~W~j+**L+YQ9?"\4olR\" eS1jԜ+:(ؒÇkwmEƊ۞b"{!f,n,*ϐdOp5 M]g73^2]~LGoke*w O )Nj]i E+F\:lkaAyZD1o$I3v ncIGI "ظUE/`A vscsc GcB?w_ FU7jxE\֢Ϣo2B /<<2+=l.¦Ьnsk؏-w1O^Ԓ|JQYH+޿XTTNH:o11Ƃ|LL;bc<=S=S}^}LIX8/L1 Ћ:6S[俷I~dLL¢i^1 7`N͉B v1Cw;`(y90V}1> q)!sda ra %_v4pCsWa3Пz,˄eiǻD=n#x ;z$..22LSJiHfLg z2G6-UnƟVxK|igwT.tpY +|u̴ZAkA{c&?×Mϟ 9Jtx6N[2^}?zݻiBLGG>3W1eޅv|2,PF͗x]v.h(nn!Y;0mHHԇ| F~Ry(_)P~13`sS"D lo 5%1;;bLu:2.\(SUֺW֛pKJ}JP8xt-F?(%Swy4 p}00}`fE 6IE۽~ ?ѳ%{UZx fIץ77E5!m99uH3kIʑ*Phͥr@H]slt;==T`_m%RQ^{$trWL ?7~\BkIkݰ&>SC'%ucNn9L]ZWKtN35u;JqLGFD1QRodfHaSEh=&]]df@q"ΕCoyBf# /$|z I}wNk.T4Y_3&Ɔ´ѣcc !_*|$w;걺u 4^܀JJ:|uXgM $+&-Q8eeSd`ёLl#rO/RO^- 4v>9-:~y'ȶ驨+q4U !,C̎܎5\oy"G"\OIXSt1P~s\$wz~ڟBs9xO:$P:~靃"zbp F“nZNDaEC #h9, K蟻)S/\GLT| lŧx;| Dh_5ؐUvo伵l\f_=چȃa#ԅ˚7he DxAcƗh oHڣ=(v:6ķ009r.DG_dEZ )P$-6`1SFDЄ|f76UPԦ޿6"SJUsgH}}O= %%My& ]Y\'{S%%˳3R-yԛQljn3岵.TcOH0SH>L@ L a.B*y 6{;xD!gE s CSB\=2 2gǭum oH/DQh9JF9@q0Ɔ/./xx+/ Rј6+7;H evFD/LtV_$%GF~q&*B`"~U̫<3r{t}x}8=ȁ:UtÔ ?9ېq=p{x]~")ʄ.]?%P?mk- af"qA՝>6Kͺ+椊&@D<݌p!&ހ1$y>NbBk%s֣vHevXޣ> =k|#4'{ÄՋ%?$i@g Uu ɓ8kU{JWj`s|ϲAm̆t,KΦ> F8nMHȻsaVH$'l?XXn;pᎊ1|ܧk8tv&m#8n\PmSs|_1dqXV#䎜II*1hnƦa?PFۙO$*x!uNJ# ,,@BL< %:( o" ׹r\rZxl|RT͜~ۦJ!;{(üy\^O8tt%?mBF1=\s((hS>Sd8㑍3sq"ԕu%8mr$(%F,W)^@v*crEyfOϢi؛a oW5Z5faC4RZj.H^#g%7W bwfR'qJ"IF$k6"]N˯bc^Ѱ`lwњMg?LOk++Wi&܇7PR6} y)9N_Eacd\5CO}'kWkS<-{;)]rDYd\z{6BnIk|HMU$sm9k/ւs\p+\psnІufDž39tI),( R ՚ѤxG̉~akEơ&v үϟտ7`b@nnWMSn}TY4dzrn܊7Gn)€Ĕ~^`](јL_acz0BmAA+g;_ QI#}8,;Q;nȥb@Q#O/\GI'u3/0d4aQMMLNu۱7$j(<M.;c(ԲԢx$i-'IIq(w$xw%҈88&&&~oR+ߌ\k[\{ h c .1qTJ 6 ךb$Y$N$Ѵik '?ػh*hSvdȐ\x 3<)SLI e3fLdHIeP1s=g_{;^}؟wߵ~kk(ԲʍR M )1yfusOHJYh tO)IH\T=Hl{Uv'5 pJsJsFFgܢ=qr n~/ o]NN';.qKUfӁ?@}@c~{`|йsv\ENgNC]oŽ(BL D B(e*V[!Sj) 1r8}#Fd>m\" ::QTPT0!u4?4G#Ggˤ\\ iNQ5.ͅgzХlw$<*"e8bN'd /hY{>`UiNRT \[p>'CYγ`'ke,dOwu:"Pýv[Me^Z=HwY u? 5H6JBޢg0@2d[e*mΰ[5N+չNڙde]a4d'ײ P:ۦ%\23n{/:׫sb}hrR|i?bs̾"fkJb5*PphIIJѨ qɫ HcbaF`D%h_=Y2+!! ! H*WB *& pa .ҿovJe݆qOF>{Rs3Yּ5nՃt-t-)$,""~6킐}fͦuߙDF :95jW:97x\> wz='Hɷb42g~4=&\v1L000y0eaFGQpae _4Bd2(X~A~T'C\3E 9 ԥwp ," Vʞ 9ո\.s$ M1c}clblNuEG5h`{wBMMI;1nn2?_W>~_iv9WYjܡ hK"/as6/4H JaKU$4ܫ&Т^,"u14+2+Bl3E y.@=8ۅKXEHj[  " f R\]x<ijˏiBk\XyV^Awn-c_ܷo j~%|;rg-}TsTsYiik~ϓ_+=36hOΌp<N0"A}'V<(;nd6K~ϓ8OFzkzZhpȎ`ڮ^sp{јgT\-3bk6uMzuP^n ާGsůEDRyͨRRLxy& c 0Au^^:&<\Ephqp f1Q0]vŝ+ԗLYP!hlakFYy֔𧧎M+.#N\ee$sLaH(iprrCC]D8vީ`.6:]zgUdz$S% <|]OL@ĕ!j;a%Z'4AT\5RLajaj:r=0ujŠpuҎL :;\\'aQ }!ȪG=[ !wQ5/pfr.,вzɨ%`qgk"S=5JJ>Qp'%E}Q'Ƕ1̢=p((jEmEM4^)$)Is];Oߖ&Fb'dq8TiZI!=ڮ[c\-XHdLG(ayf<-*2/E"+ZY=uYLY:?'fIZx >ԧr|6f\i~uHfO_m0IV?=L\<ɖ}ƖKYupaST~ALUhR(4  HsJ猏}l6'u[Q ,@XΤ .Tq cYigy\D/Q.}) pD20|^8_??!7EilA%{̔"P@;UE瑘wbZvݜk9=0}Xy .H7|Y8h*T}$3_\jELch-P2؍|yQַBB2r\ $w"D%CੌX ν)d>zJ Ön^Үܫ6;;:j[zڛNV۬۬iOO6QI=C@@qAK\y>x- o|mخS~sNipL=BQz&ObxŢX9$iCG@Pٲh9)qCE Xɤ~ۃk?&\x(jmc[UCȡYo|}sw 2@4D-J'JO)PI.kV~~垰BߦHE4\4,H2IlrӪ^-]3:c7#ʾg2ZkdX͇Bb!9sɞ˞{Ěۚ;g!ީfB$a]7-i"GTOE8 \>:PIrRn @i"P E| \TL4VaD[EMq0F0zGctt))ٽe/WթpZSFC_XmLng+]E>7.hSm)g}D+Lł5n eWHv?/{bo5-ϒ@5~&f2"BxwOv?T)7.܍lo?+WD?3dfl'M2}2ɺ dFO_^_Lu_ >w~cO<2&ꄘZaΣlTITRچ)d $62dLD<Lɬ(lCk>ގs{sz}ﷴ}] -eg/wYx3Պ" S&^LnYufF'~eS-؈ߡ?A5z`OoWv8rQ"mY١qGFlWKxGPݞIOj=B-=Fl駲Ify<3yS34!vV28H?IlgxTP ɐiڡ_\<](L:RcV'脄5a&a:tQFs"s`Kn.>or_궞vv%~O~U[)fP9] פ|[{9;ƟsĻs 4nWf ~-&H$ |SyJyj秺xrOp{[ځpz͇ ~ck55蘆8\0C*E1&lQeBGFZVHg y{B־|JPy\[1@=+fAVpMiMi ʨ]l}l EtSiRivzEg/|h4SM[ܔD\VlHX+Xa 8 Q{s`4+a][>:[ݜm~4ǵ Y)PN^Hʥɥ9bK>cj$ NulP53 "yR_/\Ip(*wfzSnB -);ʹ5c#s7W:cQy5/=Gr$&~kq^_gg/gn?CQ p]]•°$t4Vyodwh@ ͕l|xkQaYh7FczVxuF\-waEuHx(*[8!ywU?qUTZ_ƅb= ԑTC\rZD*x x t@(!k!Bt2 ?2VB5~2THao%IY zp䏬ZWm=WߑDsEsOu"u&&kE;A~˲=WVVPZZfDsDS5~Q=/ariXK*qEfV}6D)ӤKb6f{);IN} le+ipP7}їM9)C]TXKnnI%/zcOܢIvJ΋&@IHP_&BlOMKpiEiEs~#5q#ϦE 1Pgdߎ@g$eC!eB=pB0Ӵ2 "դ٘iJUv2{2qRqi9s001JlίϯO[jLC!~|s'EE'FF{G;F宙ydvemf;uo}n&0F.EˇyT&~ID?ë*s.L9oUԿ&xwaeJQ4N<^sk2}Xaq7!}QG9G96Bz 'K ): TN`uEHsϋ?$HDowOH}٢|zcFZϵg ,!Bmv T:kׁ   H);z2JS-NJ??X{WSȗG 7eVdeA,k?ݭ̙T*|(T&ۿڶܳ*@.jsvP|QX:p_W샯fǦLȋR~_ #_Y=MiUo(8۩~&߬@fVDCuU2}־4sJ{eE/kݗwXb=[|l̇!m]ȃFljNQG6$)(R@GLfn"KtI$Fl[[[+JY\ei;գtޕHo:?b,}pX' ] |FloyS 7pDy$~Kt͞_Zi ׹ k̾kx|fEj@p4|frbbqX- LRuJL$kU2,D ׎1")]% Iw| 8DboSGͭA:XrUB\$kM^Q:%dc.I丒{(GhJhW*d>Ep3&3&*ܔv,;57Yr; }aw[C-1zҳ}㤗0k}R|^|ԥm-FOބs|]Õٗ+>3 :yVW Rbqvd $89_4\%Īh}B80$5FvBKW[FvA[?$tcgi{<4@2j4~$7S4օ*-= Nk kCvJngϒZZ5|Z (>DbќV3 oV<%s*֡v}yi8璱$2&.y OJ=W|V@]#;ЯO1e\!?DHROkHdϚЀj;Cli|_ݞJ:V]d~߻wgEXyƤS-}>2A;4I;HRLըH<[yG5|9\Ck>ugruiqjұX&?@c 'ö&*i"6bu#,9I w~Px9Gqii8/aU, 054n/wet@$ m sZ>'RQs9Ѷ|Rd@*|mVf 0XCO b}U1Nkm!7O75&!BBdovLÊ;Z @BeP;X_H2Ɇ^cEBm?aHsy@z(U !i@|˝-\qy H.ԯ0~HuIp.V.Vj>Rry`6?B*͋?@y/(X  /wSGҝQ֖+ºNqtõpX;buw}~ݎOSRXNi0T63%xeb"J#{oh\VNENfַ,7;%GkE33ݝ8QpBmJ"`$D8]˝AAB Zf%9M&Q]X 8Q%M՛*$TA,Dp$:$:U6hwrĴ1ϖR~ƢmVwT}Qf)3IeHJL Hƌ !2HD2S!2LN"dȐq}u總k=k>Yg S==Aob.cIG^! ^]6*mY Q$wna՗&*d.}?wz\}H.#)%%>ȪbF!l} WilAWZҧchf\|6x/SHh]h9(n"2@a:$eMMIJXHGHʴ" <å^gj)VS K|ͻ‰mdRtiɶvs`:q .>4>H>JW 4mludd݉|PǖOǙ*4㼷Ѹ=:5Wи̲˦` Z>zJ݌83+@Gbe~whajm)61?Ԇɷq6򂈇$W"prJ몳K2 nwp/ o@4b .<;_916WԔ4oǏ كI8gm]BQBa.\Q[f@~ +w2wd]]/Sʲ_8e-ԩTD18hJ5o]%&E $9zH4up L9M*bH ~),,z`&S"O:CaWl7!dEܖJHUC<}y,0|K8pJW$dmF!\HV"Jlfm>|4?+˱>pndM%%֙Ĉ"rlAZ)vVV`I]㙛mB#:E /~ \$Rf.),Slcμ4JC-֗l'K8J$o(61=ȔqĎbF pOopC" Ol U"iζ"T*՛m|lDY$$oТW"DGZv22U4&Lks >%Jsw%Kp KRvTo#rd4z5alcAj+=-rU6?.:Ԭ?0g>l`P@v pQ}b`~z7A,A2’u+8\mAIʕZ\nYi AZr"'&RnJX T=B 7C9CPP"吺eIv5)5雌((7ѓDMCi4'D"!5/vZ{BYpؘg/Hg%L 33"#'Hn: ^.D XYO WT*u~{j!P4yݹ=KBB~Fpa>NN|Hk0`Ӷ ;/jDkNd)]X-U2rvhDà@2]w$ O] ~濛3gCJ:tVY{!+kLPiԶd#8?ΣOKvޮ@C`BGaZinOL}%~BAxq]rkM k:\&wl̚7]Bc|U WDtc :rޔW^ofxk/UxIY*=ˑ8Cd9ۀ(7Sʮ1v?fn)&.k jMJG&t숋/^Y{#iu^]FJ3d'~ٵIdR]v\v&rBDAw`:LjR焓m,+idWedM]L^fxխX;^[Rk1.I n5Y@e["|Ù;=}t0zIu)NkQ[s&kl&ь $ Ԟcpri<M^i}|~'J4KJ1!ɊSSi[](>Ah;AA yH3{ $O `谯A $FȩB Gq^ʚgn #O˵n9D**+f/:: Z(>2W8SƁƱ99#.^N?_xVnc'ESb[ VɶeH;? LeFdz :/uŸg6m%3 7g}%hLididtc=Ь,Qd dӍUe_B|BM/AH!FH` PVrGK.?\;Q-]@23s{\D כC"'"'RRt4 wy$^}7{q"=Ҿݾ݌XN'tթɿFJ.3OqtvRy|_];U$dFd0ē)0FшnIgJ#?JoeA~}BW*^DJ}},WO\ωMVNd jO֜%>ER4'[#f%h+,ؙDM3lqvKNyz`Zn&X4yzZ@mFHR2<_B{{uQ%U!ĩu`NeXmG2t`-tVo*h$=M W,.^Px- ώnjw//>U5S7-λ0(ӎ~JܩS[s^| Ds3m!æºxL~[&4a[i'%PZpӁgaQ>KfJJ[jORۢcβ mzJC<%)@-)cKs/KqFc͚eP.F%;EUt?bx9J`{2*U0{BޝCO"W( K,eB(d-e)5k9}ODJ25R?~9y3}]>3jό n8#i-i2346sm٤w.].ߟ= bxj& F rx,t{G~:<4Β yئFvZz ܚaVaV >jxV(׫p u u$$ʥo} } X3{1ӦD_pb0>hZD^LX񼹊S )]5;^tjRHt[]^L-bK^,tz1qV qy^Oڨ[2#hd[8eJa<5b)'t[1?E ` JMiXmD6{/B/s3JhH@0h}FdeYc2d2]210w(ңi1mr]q\ҫnSN jM\" "uwQr<,ekkqDD'ߵ5*U_$aGG9MM(KabToxOQ@K::&ElSuQ±Ea<Ȱ\ 26mhХ`ٴ1" <['گ`G٠o@βf'c0٢й4z#qIN儩P $&>Q8ID٠hAv\u_!U^ED9#C%v`om@nظq~ ~zbbHodcCg6™|3VTZVZEhZFX|zQ̴vߟ7oռNgd ٧FVށ[:x7Zp3r읽MKd>F:*-}Yy5Bedx#S{n!-G4<7U_ҙI2N<3*W]D99,@iu4_ B;O PW\+iWq=%QgH+CL\E '22^FG흊~zkNʙe\QV>ޛFEkٙ邑͛W')$QAMGNqiLE3>K^x+ k/+kcw'FJ]|P}kHXXPe jDUI;"rc.ٰn0d~7%v쟓V,}H$%kN|nG}jG aCmMol@{X/fBh}g }ПZ9W#BM=٤k<&lo#]v39$?4˫tp&dݾ0j?&BL-p,tA\lqx,t~=pVt7֒*bb&pYY'.v6C3z+H ZT鋦Z;:im{wCF݊\κM  R7ɐ3[cgWr ذ#_ǟ顴_0]pũ!dnYuu,cy.oG!O^_-薥 )yo(It c ¥;q#Uޫ>+ݗGhk^壟 gtkuk%$Bw]lL2pb> v y y && [Ԭ&T8^ǥvOG/ws|1SWdEujz3zFSv*z+Y=Q kǿIo{qF]A A8lV)#+`AP`K_j>/&. J\RWY` C]XG||NOϳPH|$tCZ3` 4 rUКc.\j1o%$9K$$] Bk [s>sg';Og'\\y\9#G-GQVlñ;;gsz ͵?I"J7mwv=uo.[ ާE}`{)tҊ8Xu= ʨb,1#*qq36b~٬T;b]G[~PA|m*lEah4dP#<mJ ӆ8 M s[,9{r(KhÀEu*_lqk o`73zu`n]sU(5Hȍʈf=L5{9\&iδ}>͌C.qYkeWxJ$ݨ8jfЉЉfל׼((,ѓbRx [B?+yssF2rd~tO5~7;nI7[Ç{=? @&|.lx49!{NZ"ǫä㥁 Uw EAp9SsyVO9WJ*_x*t>gUb'RƻIob?"w_yyYg83E>gG^NmM{ݷKPnY9Is*Ѵ<] l{@z:G"}1gPϘ]O=gpG*#n2S̓aBp#?qcwyu7?o?i6]!#ޖBqknpS`X+peN`mbIbs+MޝE q 7Xft6[wXC;z@32&qb_O9DI0VuHjĥ1tUG f\vK0#2Nfidb%qYqYrٚqCp~~o͵bE <-1*))NjEiE-U2EoS6$)r?bKu?6)!9- ܳ{j"B H݌GG7:f1=ha< ZCzu/v~FF!^Z$74 b`wu,./vng>$ 0*ƥE=Ʋ[l9J}ˏ"Q7zWW,?|ǎ>w-ұǒ8ggnklbQbQd:ҐE]Oel:Ǹ\GAJrg `Wjx9&0C Sdl bvٹMi7@9K3ڐN3Z!!N!N Be/G q/'Gξ(7D^Y>#>A>NikU[Kk:k8qT ,[Կ᳿[rCY\fwaGs1gF΋>P슶g,CdDGV L߷}j<_DοS_-X V4sg{ BO.9;DG&$: 6C"4gǭ ´qoDTx x۹c p,V[Q@j[4SW*45dUXGl5]1.>qyHO7_`81՜1U׊ ZM7wkV<|Tx.\DG9Gy]z7jXDYDhHCN)!Thg2{rӍ9%3*oe9U"kĤ ejFƖΟ\ب7k l {OgOZ*}p=<G#,P6cWFVTkQnĜ{ts!8~,ŕ~^>t*/ѽ LkhjhWkmM:1@W[+}+4ЦPwD[CYz`ah$Ռ!˜;I7[5 *T;܊>'.̧mR{1YS)~I8{AEB<|zp)NWH޾TIwĤWD|18F UX:7?~JVV.,оZ;~ GHHuuw: Q;3;33 '3Se^!(?q4ߪSsMcpZps歔:GWA@P Ni H@•6@BY`>EEL|i8g&/ ݗw k{ =Vbu`?MwP3Z\1X@O82N723Dޥ5)ye>Ċv<|bTNL>*OA^{QCx# eg+k 䒡/%59d[ݹ*:9qtƭuٻ+_lSy\?9CL:W=;gcf!RjsU緳lLF[sG%T agg#uLGm8KPb߂&.aOHl P.&/ȝt'&8pȕ&_IFIF0qU,s&%dm`_'R'rMRpew+7LT$]( izBnTJlIky[(7w#Y;+-+^ 7ppM[K[K镊$Bb2ΟWt3m1;d%fs _B;eY/j/#iG:;b lgy%+'6tV>Y}=,U5.̈63a\l;I^Tt,iw{=jAZ`+s© -Qv_˼cE]ms'xr-Ws,qC4U{lu17\ډ}dlMهb୽nTQB) Pf 4Ջ¹D7>%+u~`i+h$$"GȅWa9V}-X"@9`jL@D3F+da#oTשI/g%.+GnN%;puu4Y4Y!RɣQoxSyS~橰$Y.E6[ޣ17LC 8ɘIRXѩ:L(SwxR L:q`)(|͹EWgԺw21m|F= oZ}5=5{V=zB~2?†:G]'YF ZzGPCMu(8ĩl44A]`7pqb ŔOe *BCWUIIwff3&w iͣjmU7տ`3HmZ ($'XY*#m@5(sAHQw ]E")d'4dT="LR @y1Twj!{ϰsMA01Œ:y$K:is*6eԄHH;>=sp'HH. 3ғ-\7w4wԿkPqԝaz.K1ʣ",ZzCM*tsmœ}tIh#K섡ݱMvvL,( Coum؎9 Gk1mqjkBK̃{mhZ&j:c8O4c}(sڊomխMg->6X+_S28,`o( qH{97$$dWFXj%8Z0/_;n]3dDDp(c߻4Vzw<\>EE26J]JݶJD2Sb|⟹2{'"oթWsp~ζ 8t )1g pNvQ tX\ޝGC $K,]$[TH%2}m/l!d-k֍d+PRɒ"c)tgYy{|;߫2kjNg)_ʿzҢeg'_4STn{W.^=o'a1Oʰ NR6e g;\CBپ q_g!EKEK$+jd&2S'Lgpֹզԓқ>?hĖaRKzQN8ݳmէ9ì% m"U;>m-K ^(tc#~׹It`v| G.1Rrʡ!tȳ=&Uz~Z2kG=r#-wUnN|3vǤmKԐo'^4NQ9Ln9J~,(yv^(Xݑ00Ln 杓? 7rY &i~!mo,bfB~<w==>0 yG_WmY|փx';sטaE, +$UR0bsjoˌO^RZ.0~_zKzKc>L1ΕkXNohܠzĝn% )WNW 哠fpX\;7v]#e;*,sϽ\{m7)7*:^"z)TMLُU1'uj+!Q.Q'X@JM`@B66!{-&gv}CH`nkTulp:n1-ۈ}~XOr)ye{_Rwv82ISJSi<.{ +nKttl  #Hu`GPmO % Т:8f.b.mM)'z_*KؒzvzǮǘ͔kEʰȰHHP;}NLI gJȢ̢,BQ&M}+tYvM3Mޠ$NxC4V?BGu{#_qc}H9Bo+`e+>3&9 _B&5@]z\KvjjH- L3x]` M x%þ~P'*JRR=X+BG=T6<'?Xgl$kkɣ > ;'yNG<%1[=~jά6;1;aO8S'3)薰#7j悔ݿ6^r 1ck'L7'>*ͷas=8*_HmoP- :n|e8XyzwWs }A :^*F 8ed `>X#E7C]%JwH+i|#SDvvBib+_]mu=:#gm=7ymA!m]m ɛU62$]b/X1&:':WȲ3`gJ'Ԛvn^yA,-4qw#j[FU/08Hfg24"7rw5DzgȆuW+i1U Ӌ-K1On0~~!ՙ)ȔSqO2lD&M{ _ۯվ Bg O*bר.InagQPj _YJ VNG a̳MɰRTbJC[{7ɍ D%EA5l4Կٕd{FYgwQfjmAmz*[CzWђO ~rU؅]7M߽X?|s0ُVyf^_'7Bcy8ufs=&hI>E,` A'˕jYR`Fy/c31)ŠJ`Jl= Zz{Kf88E8(*hc/|+oZx{oJ n4ngu-){ hZ]پVO_a$ԜQ*Et"kh"AmIJ5 e|t%%5qݯؔ8'r_OPn6S|_Ψ)BȎZJEW)^08e7,f8igH J`ϩͩͼ>F GP JuU*@'"4M_ lr0}H t(RuT>Iʈx+GhUB_qiͮ#_ﻑ,,>UaSB]E]rr(e惁&%* аob 3{gҞ>dBn9c_ZTl :E1@9WDT3caHK4|_#g,,Ӻ?#!uU ?l`vB*b+Ҿ>BBg=@$8 : cd cee}p-g6w \-7݁lm1P@鉀:t^N^GuA3CBT]tk[ۑݼOpumWf:}1HB 8F-jscA"_:ƯǷ&{rpN1vl">h|;=8'ٮ%޿ <8K2Z2z4w"i *TQ; @Hn XkWi֌ݮcr#2k\JƊCvAծt44vJZBZ Z D.Z؂.O=!40hhCHUPZx7rtf3U}X,}Kvţ[C,j^ N8S WPG #lM R|g>*"X4>mKAf.y@hvXȬa̖q̦Dm :"ō:0KSpD5)cw*m՚Rg|d0 E_=jk0߼IU 8ۧڧ bd[tH 2kA_͟' 23h#p@/vc}9xEKf{lO.Q{ 5[afGo!7rhBگ G2%g!u+b45;9o.BÅ3CYSM?n+k.wY3)&Fn&\OTǦ/3ư<> \5ɍ 0t^s}B^d*%jߠ#7r{^ ]E :i{u[_i/.E$[Zp+ۯX:&xvۜsXyZ:m&NoM ܈nB{ŔߣJev==}ZY4 WbGnug 8\9VKu6nVn6L,3yRHӖ=PI=-ghi3>c"]/XN: *{_y4UpDHd*S#c%9Ȕ!1Cgi0d dy)D TLe}\Zw}k|{i_iiH[{ R^2 {+ЮdMjtScCBB6l"ޓzT>GOMa[ﺳ#oߪ yRߵdϜ}./ЀՊJÎS)x%25U֩ʭp?'p7Њ5n(Zy}zT@t`iH6 ؟Icst;v8q|ڎMZ EI\DdOee8;ȷ.[e(y Oc@púMlʅ?8-p4މ1l 133W z&@O:&&.D@I_ŕؕ8xީXNF֩RGmp-hT5fR7maۗu0ׯpqj 3ݠD4]HD`cL*xlߤ9Fs=0ZWe6c65ȩ8 _--ʑ  Xz3͢@#d6N< ri>C bMYhhyXcp443]!. &^8 O~'$T_#\._xqy"6J|Z;?X{:9'ז\lHż7|JdSOz}Y ~~tRLk98OAhVz:xZR9h-LMY5>ç6Rx>#_S[ݷy=DsDG^MFN B*!7!< q(*tS56xL"HyާQJ=!$=㉤Sܙ1Ƞ̠L9#RJJ]ՂKLΌ}p['!wXGG+ "QIu'srg^Gn}3R ,={)FD?MzUQSN2;_c^T3άrgLX dxfcyZȵ¨8(G2;e}=59F!7rJCmǫ%Bo`|`ΈQWډj/ke֧V<H^ۿZ?*"vGCdW[IVVUgIY*ߨyV䳟Oغw6: 8{*+_3dM|mc2xD+ _}WTu108\`n[joK{DI崄Պъa.VgjBBw~ݕM=))+bw*\SD2@|x$unk| x4.CÄP4ʅ/oĽ >B|UjڰL,d@@x0uh~(rT2pk0nGd?P p@M-4Fe_!ygqnΝ[0Ǧ]A8s߬d 7bY7og넽:pJJzzvu1昨q|xU::iwKdHq ?!z"`c') +5/4bJq]&Zlxqoo;KW#|;d{)Ճ}?YEKr,Qybd&&S^h+p㩊n@޸"%v;hǤ}6tt=zES@IN4[)ڟY3G$AUC)!?YPN聣Q6شYITrGH\lf)l.ιxI !YuJIюy/ .6 E &zR%%NfjqJrJ޼= _T!򳰕BuakOc.[-K?(1E'|}##D!Yi >pҠv87$llvJUq`!2L!AY{VyHQ+:(7eHKZWx9Y< *lZEPAxSeu/vVXBvT:%Ԩ;Ԇk1[Xx~2))Ćc\]rhx{}N5nyZk шD2'/bJ{*$_.x@8(y(wX+R gb~;CǷ{3fwUBI B払S~_CYCYaH-)Du'OZNwLU:6 #CL_8ԁ,%1]oT2i~Y1D ŝ-Hdd5{f%u0` HOupxxFk׎SS{<~5ԩK8O9ܶ'{OkkDK6 +7|c/ V y<|$Bqv/Z"/ODT\k T)s(UnÍ9c".E+'<аRls2#\-z5(eRgN}]O̷k}SBe{_-a`s⤲Hʁу9\i֧5_ܧhe{N5OPs|+znu "ڼϣhxt:deYZGÛ%wsϱ?%IY-Q0>Gel}\L&9WK }j6z_J8@f+X%7Vڱ2~u\,lj ? x88%:H- uoOOj CT}v,&Ƌѓ!x"<^❽'UΑ2L:<2U5󼖘S%=Ϋ^Vq«[7R?;pˏc=j,QFoۻ״59!\Oڙf2o߮LcLDe \0QEtD@[ؒ#l.]:a-a L RvC[ttbeGէR(^7>K(6Rb l,Uץ})3Mٝf, `LmKA~ S6?},~֩歹yd\ F@ݘl €Βl1.(ByȽKҺ;MQ7$P17œ\tNסe !\W\א׺!?!Y@ 2|fQvR*le9a.ktx-gamR(V0aO}:))+)+MΙ 74T\^\! IIκܺ8Љ!+ZZg啀=GchDDȽpGdN)[Sz"3#EhbIdi?fV=Kb@mB #m:{~o3~B o_,yE8}ICQ(g 9_4k)6ڋiٚz6@Rr,"x 5<ro2-obRvik[V+*ETb?^FO,{x06&UK x<&$pOU')J {w B}vY'2"{b# ;(O#e-<3vr1)s:";~E;T\ rpupuYtx`Hö1 3pGOXh*N: ߋ:-rیݾpq-?O sidQ]dM.Iȥ}[Sڋ)^qUV|Yv>u9ˎӂbI:eь'rA11A4fn/$"*l3#NB%%Crsx+=\}񉾐W{{%I{ Ӏ|5Rz~+`h uVb5AR;%bXP>%vGwŋͧ$݃Ƀ_)q .Y~~ WG ^S'PAPŊbEO4pw*elo)C&4e:յ:B&ܬ[šMɨ7ĢFm(7EkS^aSN-{R)(XeL_]EZ(-˷.Ql,sf0nn 57j@͉]5QdNatu4v:uЁxʢsWegήoΛ" 9|iĻ놨SMNa[;odlI;U1ᚗaD‡/:ji =飛G?mdlשc:Ϧʥ2z eK6f1XB7 d_$7 V;YyT=e;Npld#+)xBEշ!o ]ܔ| 9<6vvޡ΃ S{BtN)n+٣;C,N|#Et>AEzЪgĆ]I9snd#F ^9pź~vv?'=㣚p4ঔ~nEWN{}'J1v9th%r5|mƠOC0DOSbi+K%`Ng"8'5V_PPW)#FpݺƑϩ!e[Dut!߃1oe8_IXf6Qh8<)VO%£}5,Hʹ=\61 ;y!p<<D(1 "'@lVVBVVrs^D j+$.~[^IyGKx@JX}65gڼ.nc),-,T`NnLn,_+_pP=D'1k"pLʉ Oηlw@1 R>2yl:ջ:F.6oM11loH@"v*#C3xqI;`e`cEEk~ Qpe.~ ^1vZ\C,ݶ?AA{wHGONE̝P4Um3?gb] |k巜S׽yfm.:**yX}k}}MM؞|d[H!)|.##!!,Vl `T:QMSLeezt=_w+o~j̎=g׸3hIa`r %XYyfJr3`z4آw7~KB]#`AF_8> I*RKm"  :JH<Lk.[ 1l4ۯMmMm3 )&ʥ\ْ "up#)f7d"|xܗ"#'Gg>`qTksUmuĬ\r2z =)Ðe-b\pE;5NN.NxF.;NK[SsGό)M0MϼaǦ9(E/Sm$fokwr!xե 3< \۩'C%/ gǭ'7}狝 !:Qn0F )))'k$-Q"BYt/v}?(PmqmYVYNKB8YDžNT} \NhjGRR{Vpjlv-ΔzXw9s#ς?"'̩I#*}QaQS&ʮ6* ,q;)z:w J=҇!ss#,6O*zc])xշ24]g!9n#RVt7P\<>E}:m%KFYlz";*^5! m$uQW{mIȦ̦L+HNn< 'ٓw޾#6Cl! Gp!&)d&{LRQAyـ# [uU/veIdSak9XOa/ܞsNOpGlrxtSnІQuu \S7_]%ډe _>B؄ `&c{%o^+z^ AƉ=ك22mU B,[oGEVz mCq} )Җg:NhkD{\ETk`6I b\|inHvӛ`w_x跘O]XWP1_qĒU:+ee]{U-#"4hhT,R _kNӎHV+M9TJl_)Qoc3) ԥ\/oeY˲dš< CΥꊩ7ILk^&fBt|ߓ{Y]wm#qj2EvQRA/zW;Y6u gnTwQ1TBv8[!Vn\vI%q۴S=vh HjNj--merx܄˜MZ56}JN)S *ʶ_-ulCOJ]R@yyD|pM%(sDj->꼛=^+2as0UZוJI3'슱afXRUE ^3',ẟ}?;.%sX?a)>X(:A. 3˸&9iWԨ2ØCw%NB3_!O@J' G)hxٻ6s~ʪ"1ݧōˉŪJA@0mݷ"?O8 ..c;k;0I>I ]Notr9^zsЪ ^}CXzn )^;zվ*OwWC8Ʒ0a]4!(Kŕii}_W7+PPZҮA4jww+PvRNzM)$o~3KcB҂dc,Ө@Q+ĿAnc_US߁zFf޵^?7/UŘA# K;+en< +]"qԊb} p&ənnuu..KG.ؼd,E6/_ZZ RDJ@W_BXU%hQn ?IExuڧRPX2'|l4z#vu7vA%[2[P <))rrs%)6-2/26K#I#P!s"se17'lCbpDkȰx}sf|mU8lw z0cMgs?RhlO?IIpq) W+]+]Jb0fX2ǎvʐ Zz߯B˜Ѐt2!hKA" Qb7szOk gI8 oL@љ3%eVή̕\ `Jj)):}ʰ1/1o(?ZG9t~wdD)o:z\$V^Y [瀽DTI>T2)@#{l <ܕ (:DD(<‚+| "`Vpo I_}|l0}_(ȮD pS%v%$_@Bnj0[B%QhǓ*5p/ƒ]Fru^dwQ:i.$>4rF NRgxx~ 4ֽ.K̙tk2YЇ<7{W//le[7|W7SSϨ4.-duC:WR}С5P-~%h]&٨ܬ66)i2'l쵋K[N6AyT3-UBqX]~KV)i_YvSoe[Fͪ%ѵ̃grO ntX^Q.Ph F<&Ex\T-'Ҡ^ß303S2 *R=ܸsFzhY A&#,](ﴔX? F} zPSV"q]22tZ +F KA ڭkkiR&LӼ p]!Y 7͞ɧJ J ;19*66S޽8(G4I4dd&{ґt\c%΄>Z%=)u0cF%]ݬz&MJt:?$A܇Oѣ ScwK(: 8v;VRBJ\.*(V`G? "JVV5 3kAHà&(!\thzrٕ$NB_@$\]=l2Dl1ſ%G&GvwHuz ʐHF1 ~ӬҾC %E7;ɍbNZ!śog_sl ? *e`o`m1(f17iuL!#%rC>v1M_K}%[ccdp#p~~==N5An^3 U  AZZ{@L?`lLf=5^w*\֍X$N } HQ -Tf;S+ ?JH`jv-o__xk˰C 8B +c(4R;w;,>yHG*)\F߾ZB* ׇ֥eTbں Mr36ccl!Vv "ǁs[ n\y\9(>>~O{jg?|YVjf=5^]z$KA\}&@|$ _R-wDodJ)ɞXD; ٱx{DZޘ{}GawtƐ%b7m411ѲQ$dW>"z w2`1P#d#B=rCFlvB kcx]r,W6u)zvv|sc--JTy" >xz+d'oMt"ßH(Zk>( =u(x?y96QFݛ3 {j]X8*Mt*sʶˌ!M-W>ci;GWOyjHYw}sNP 2Veo|-nݴ(,TL_2sKGj(v\:,gh2{+/ ÐLM#&P[S0y M4ZnTkﲲ;+/!/m/w3*XEzwQq-Zii$_\@c=ҏѺM!* wmXz awNWQlU8UYuu|n_qsRssYY!'AQ<-Z=,ln?1xO}ExF<+T'FlSuSpMsXO$lvEmoNjK pq* &sY0c ԏNJpM;tS s,n$DZ"Z x(BY*X2GG{UC!3H:[E90(NLI)W+,;~M$=b}W{cbfBBE_6P3e1e<0"իC^^ޝS$=)ueٹJeעKe_Sd_8lג%[ʚ)k%K>g|t~/9y}|pIН]L1/ FuhvvY <" 1tg/ c 4v3]5W;=G=׽TD , mg' ;;=vv8Dc';7_w qumu 3sj,QoDP44z?o$8bo;iooQ2]\3G EgΜΜ=iHCrs-;EDlܦ$D$@초Cϝ< #ynh%BW6ڤPZ^Z" URrZѐ7Fk~4i`ź̺ܖ."b ]t\8hQs`ϾwM+RzȌ21`*e S:\(aALC_ear}(ʟ91wEWFL]DWǍbwӀv*m8>oo^2퍡۾AzEp+lQbSHu4r2=n&j;ׁOLL.f ٽ oo}ZZ,*,*,Щb{X55ʏ/[TzoytWBJsTb$Q;OΫa2203Wӟ/m#ó1=ZP?fEܞ^r΄NEK ;G//<& P#ظU}Q*Y\>} 2R!MkCnz߳xpyo\ ,yU_?%>%Nw 4i; ʨѨe{m{BoZjZao2L13%qH$SV}Yvvɿ-1)→^5bn- %(_e)v:lk\+e=-<o蔖 ,Hh*ʵOrcKݸ6YILMT TtDNy%@$g4zJSF:Pz`OsKn9|yK͞}O--AL:g&iHjE@ѩ0lUT ?n΍ilRz~~r7?6?VooEf9k9kpaPUU܁ꍷErAL#5YP;c|M|MVɕ3u^ׁ_kyo:c=Ljew$^,۬L76ݛk%SkCRX$SSR66qZPu &o=PYY0Igttгqq?Hs56L;~x}RRs0R7 Dd0 m'ҟ\mt>b)lSХ?HS4oHSޛ'"iN[՟33$: Xm,GoD?cs P?[ԅMՑ(W3)B-x{[pbbDJ=f..iih.sAt&u~(mm gδe7x_R7ޏ)XNˇѶbk.sa;~~Sa:xljwQmHo]M+B9k6<r?ļBi:V,lVt Ub|LO8j̗ y^Έ /qmv3Չɓ)ڽj]kol_q/jD& ue=@MDy|}r'ZM 2 MP]2}k||߈A~[Wnk9ipu> QmAMDOhWau#)oԪjm@@:4*JmZo7㆒lRcӼ7JPR3! 3_Gdˍɍa 3'HƇH@\*V  [E!БHcawlFWA4*U2؈ԬGt3];W8^u|&2&kP֤֤.(4&`aӂ  uu)Μu YiZ9ߡ,#Q*"bgͷ1 k:ܨ21]ci(/AR~n>`B?QP iۨ7\@A$t=FG铥@+y @6Hs}oɺ[qw44٧CfҪ y&J𿥦8y!X>< dr, zILaTCmm22 NDPcKa {o>Bvlk5,[U'R): 'zD_o:Ncc2b7eJ PQ;L} &ͥ| )PWhu3V`O1 uIhݛ6UkJ+%)lVLL:tjBtF<= so{V"{Ԏ5*S2P1(*iӎAA7  LDUkK 0oc7owϹBݏ @YHk0cQNPJj&ͬg! V i ҙ@ĉWND3i2aްBQSt\Qn" [-Õ@ mFK%t#d(#9 B%lB'{ X c#KE!ShY8NvR fʽNoNo}cgVl__5\hL3ەs4 FHn最_bR0tSz~m=If4 iCM鈓$ ֿCT[O9"q|m7>*D ΒY%2o[ )+4s1Y~fu'G-n4ْ?|r S&Mk\iQU*yULE H5icc"+i./ؚ|ǴOϬ.'^]wIMS~%nyU>>X:9\aH_Hxr0ǹT|X4`bNNlG3n ]ӗ(Zބ,D [8=^ N&,cfd>m@!YA@ 6!ULAZC(*AS27+ rmf::=p ۮtіcZ͊G7xyrVV744.+Ns*&Ft׎,[owzCё)FYP׷r&~[f ̰˘߇ %I1۱RH|w{j@fP5kVANh*ɫAY+.>a8٫ܫ̙ZsQ}CEܿaF!l|nOWf.7^N3,Cv<[&{s/Ivv@GAq?KRH&6ovtDDJJ=P"(Q; }~~<`ˋe;0;3:"$/>^Q9 hI4LK(o g \I@}BwT&kHuN#5> !Y,9Ȇ5<TPَp 6M @qܧn|qgmO'?Ue-lޛ`Ϧg4o4w^Q&tvv saNttFF .;}N$[l~4.Z}wv1C١xӋ5K?j&1w$UǏIfw1)߿.ܽY*<Cv}ewY*]Rƹ|*{Z >z9VzZcz (БSWFZZ,Ć, =ߔT"ϋgJW~F"<~!pHCqkVx8Ht}P/cjSfQtcB l,˺s[M?SʐqZ1`oѭVV9;QM{O{O8r+Kg#L&G.B">`xPSII8c>N=PukѧhOĺ\_}Y:nX+%;mr㩺&Kεd"H۟Y)Pl֐pz f GaeXA2crkk T- 4Y0za#?E6%1z)5+W1 a3M*iT$ºoL[ Τo[%hɴځk4N2*-Ոon~{T|-5ip-%QO2 cLm Uu3MQ Z^\N65bŭñQjF֠{FލmϷ{oQ:a~4 il ŵ?Uޤ(+'?iQ^V|6C敦0N8B'/%MW(/x;AC& u-l>Gw*0cp{rLK;bJ.bz( `P,#i9>|MP]MfaTlw}[D9P"} -dTU5<\$3yV2D*9p¦ Qu\eh[F vA=׳A>$&lBL09sum4] }})R=i,iڛ+>W|L^.kTc@^l)FdZߝݾ.տOx$-/8j$W* 'tڟ\Ư+PԋSf3 8tϭP-НDcd$PtUwhҥ/e<Q}FbI.; 'M3騐Zn^BΒM6ÎjlUXwb.ZX 4z Ɵ쬼1dž~vLzADaSIoΩ@wÎTnM&w S>XggGOdBHLب-]lʢӈCeLt AZ̡pdt;Oa6:OݍJf 1PV>@,6~vUhPͩrZ<6. ~F߷LMBUHtm늓S!&>p]S[SyyOtg~h t#WɔiہpǸc.1̺aOifN-|{87F1R3t~;wK/`A wS=ITÌj^ G7%+Q">F4aղ^ު=;ERRxd}E4쟌`_oy8mp0YDBȒ]e .l%{Rd+JJd$IS1m$y\5}wqyt/z,퀻<$S@W5TIШĩWyjy VGw,Ͷ~O||ϕo++3[ HB662 |WFF4ls=8NC($_ZS:VnEpN%.Ml![&sat?bQÊŞI dċLWlf֒*VꪵdPdɢ>h߂9DY_(J;-b 8Ŗ:>VUCM!IFյ!y$VpBB X댫ʵ E?*L}Fanbtž*A|C6{jaX.픙{!9mms!; ,vC"1;vcy͢|bwAD!s$)uĐZU{"HDS5 Wya%;&RࢭmmTR_C ֪UXVXH)V82e9?z&PX:EBNB2n,&tID]r~!wk;P^xVC/|mW'q gV'+yϣAI%u2}Mh XXbMG;È e\xflu{fyzPv޳=bm=\8&svXCL"L"|w /q^c]xh7'uM7QK{Nu)H gg籄o:ԄbGg߈r*@ۇY( z Eb4#ה|1l c#`n'/l`lQr?J)\gGI1|-D.ArI>|$f׍V-&Txl[.!iޯO1b9s)T*Ǚo }P ۳7m>}RޜƜF72i{6vv Ѿ9ڶ;zdd(;k晒\&EꠚuKIA웛F '-"!Qֺ؂c=GtQjږvΗzxo0'j İhܵq%"4unCi ~hNBPi; )Tfbu~T '͝;{Da_ uP 2keK ;+m;uŬ}/07.L9 9"LM$cmb_vO=j:=0.t|}Ζkk'5-JuݯW3p߁X5RP_ocnc6{T|8G1A5tuԙ훃7yWd~Y=^vZtV>=qz܂[mn`gf~ȪbW=ɮ[C֗<**?.-eFOc6v6Ck8CS:ov==Iĭ cܶZ۷mf [jUWY}Nq5{'/)>O-{ﵙmfFf}20_cu9Y}45y 캙3,9TYAT@M4GË(l3f&G8'"ʴt<1aWtsmf;l3OdTNq>^(k0Q$osg4{SRg(Ik^Z5S0FR|PN]t:NBl'?mv~JKvek+(qw!1͊'Ԣ 88/I~c1#Yh Y |J܍3FLc# vt•-ght sȐBR51£? ȥ*IqG&5O\_9F"T θٸ92{FkD.rsZpb_ľ|6s-5: 0*wj>GQС`kNboKurS"QpFuqX@k՞M(Zeτ{EbI/Y }(4e}-RLL{uλwSSZ撇dY [].kǚVL)h>|?;b@hjXkDuӁw#v+vVӊN{ :k|a n= v}Qש׺8yqO,(z)_NO'd.Nmg $i W𹻸 Kڛj"]}9Pi]8B82 lׯuNj81g#-b"k wi܂Ixf{g-.aN[H+:W%V%VVƹԠ9]Fe2\1.:R 8~D31:|^8nXACd.@H cEM!g!H30w ` cў~j6نC=x[Qr!5_O'ʿzK>ΐYYxlZ?3xk֋A7G>dz/!/pߥK+%'-$-d^͉T۟w(Raj|4w5h 빙gvזΔ$c>)ȩ}ƚ:Յ^6DxmnRfUYp4gDgdѴSiYj_bX T/7b͋-_}ve͖ޛ }qDjyqt= l3f"'3n0op]zKMWWyS{2fr;#+"I=ꀜW*FY ldx\ȏ-=6z6Y{6Xx~orGY9^ ][B,@7"jpx|b7N9abkmZ[g2U:xHdw4x|Kb̎a`%A B~^iǺ dSKM" PhOE| I,Rp!+̡#p z*1UR;kW}p(llrrotB ;/ppy^_ϪU]8NԝEYNC%t2ϷoB֩EBp>8a^_L9 yhwaNu}o d2Ǐ/pJxb"<./M X|8wI0HZ|ω'^\T2@4Ra)[`'\bL}U-MW3D,QT- E&[LXZ%:4CBː#͆ o 89#9rdd?F}Ex ьwQzACMunSU.zgt4 =]OA(~kPls*PWo=ab~R |Ɨ[c=E,q]n1yAL1x . Q8 9k4-h~J3)"'۞%#ۤԕ3,5k z jѧ;Zj+K)KlFx7_y4]pB"$seLɔ "$P!BPd 2!!DLqy>wo_{..iA]uq4ȓ' d>펠..? ;iNԔym8zتa";wfę+^˱$x?b^fȑcfZTL;eSlt4 m*R =P X_+*TE`y0A q._ 㛱>c}fCִ A,nE @Uk $% 6HH8^W4c!@s*g Z| hZE0W:<҄XƥV-Wmͨ(m>'T{9J>B>BG [ Ip 4ҺzZ$$SC6s-ԞNc} #.u󴐄/6TtPG}ͼt6}?nm @<E}`fG]IlqW @{r4(qts!O!^x%|8~Su-Z7` |K j`)/ f̼duw6s:T*;;d(O)9:hMAR_c\ﴁ##5VRru*v*v|?SLIQBէMFm7~Ɛ#Cɲtӗg_ rHm Ost wOj;nZ~O׆ds1D32>~~J3FHd'b?l9KoiIm,{fYWw^UϟtQt.3|=f@c֨OgԨvO˄h&$K<ͫ??lmiznkK>}?anA ˎdC+ۭ)7L)p'4s0Hh4b&@mt[tb(9*0ȓq)}̳ i|rC&dOFEB TsBh{u@4_ S `s!5rq]r #t(:<)9~rr+YjkWF Ad%WXXw_S ]UxXc^u]}ug`nv:kg Fz"Ql@P\&ú 66@By #S.Aj)/ZnII^=!KYQݼ+!T 55B CE 0!d%7@[H -1CR~,x[,O8VМpZ_23131F7oCC=BZ]̀sWpW`ffPdb Ww0)ɐ0hvZ.~A8"(T`[e0P|H`Qء;u=lf?UUlC'Ȧ-ei9 yHoHBBe8' G.*Q@.\r!>(c'p!hpMflqi(ɆB>/b%VRrteȭ-lreŁhF@W]:GUƙIJ]KBdL~ OpbZvOvS^pxWI0Wvk ` Z(7ը' T[$,pQ nfaѡȱ5vB b^jA; Lq8.D WX3B ,R5Rdeޣ;Aܞ7!/mm!3F`rp8Qѻ)ӥG!|.Qr"$Н_m\2ޱyƚ|+%Qd='t} +G2vvvgLlO/sD.C"U,7K?|֥5}"晹YY,M k UZO[UN }V;4UOOInyzM,"6(%v2S7vX%J]|>l$3 RwUT~B ;)w~s;HwmOڻ)Cp{:M 4X{.-)1PtA/9l@X@=' ZB?ڴXCn#iP6FJ(Fc1MZבFEd ũIcHk4=i@HS=!{C+lt ~"^1qҌ'}t5/UUa D‘67W|6XtZb(Ձ[5Z5!YhoR9RD!69v?`}OS^uLzl=ūJHs ?-f%N@Mx/oot}Uo#80z]7C([z?a;~c! mPJ P6zxI_6Dž8 m̂§ߒbtF+FBߛUN;6vrf XD%Fy ղ:5H:|%WC.|Qv?$9,ᢗd_{k[lyǬ,#޿\u\uT:L slΈyx($$ [ ;L@>Iz;uvbT- cqT&XE[gm{s1شv ?)%$ hiجX{W0"z/CO' 9APt e G\Z2o6s/p֍=.S78e6y!!jv8cCcCuR:#)lTp͊Њw.//xfdͿpݮS:Q RLˁٖj† k1`_YR.`fSx/~ D_0^88]aYtti8f!Hg4VٿRBn'DK<:F@Nɹ-pa&9fp) ܎{BD=x=D2j))G# i@5k~!\.nu.jA0c0N5[SDnlB5NSA:2LhN8+.glvdV?-~N=47fj.$47ڭ0bbx;vf"{qPf(SI 5+łPֿJrv?+dzxk~$t^ގISil͏-T(U4k'2Qd AI)*>nSf$CmgsB'?JrCFN:KX*j,<%y;?/s>beyM3=TI/fP5sê*k~qvˌ4Jm\_ wi|;)TlR}BE>d^WxUP}84,'S.+0{Tof9ۇ(\}c|ziClGքGyذ*{ [Eu`;o8>ֲEBHdIQHYB,=dZ%e_Cʚ!T#l$$g$k}g~̼{9\C!5)}9kUV28U߆\9W 7V1FE1B o!G@`^B‭Y+c+4o SCA}]y1H%zj܄StPtSq|Q7mXREDM9^$0CA3& hS6Y4A.'N7xlcr>R \=_b?C$>rLq y:kjj:9,T&N}}oE\3h[rU-U-{rVugGD<@gP7@BR口qn<0|,.̕h,P0dȖ*a} 9YRW"TuuF^1Cn:CEtV^f!Aokw,$g,nر@rQfZ:\ ZLCmFls7=BFa:Q%z.l=5c4okiƷ__:N ]wɳ_&WJzV' Qg"JH?PH01 a?E⛻CNqe{iIwLƉ9g%HiijyNɅOzwS_{ZE-S. ?ُSsN&b3N2Bz = ga^$DlTOYnNyEHӶ_9|^8It$s}*_]~[,_78h;b;ҎQF@z%áPJL{\\_RR o* Ӕ9^ H 8 ۮ/*'sG"}+RoB]j%M/ _ ..fB#mBwbuWS0WP.JRԂ>&ӶZSUi{ɎT$]NwMN"_xϟ&]3’ #VBѩmW/HXPRIxr}ڏ/EټdWERPlC8}Ier9/w-UX@J7G?dB?!jِ4jG /_.plNƆ%fM8G U1i$k*++3 a1ՇKpJpz\|}.| ǵLB\'㹦&IMQ-e jjihS=AvUoJ=kػ\"?ߒU)X[&DR}gZ#GzgԲ17X~@'R>Ƕ}2iP:u:$b?pܐnDQK'$2yB#e-^ܖA['kXq8prJ+?'y)y)ww.Hd-HzzFN+ʖMH<|\7q L}Ѧx~mJ8ԎVWxZǽ/ۻd&^jc9At|؁cՋY_@fZl?'Z%w=t5ܧd%yA)KH V5?oCC$zD5ɭ9# Br}rh9cH?pKK>AA[, 9ݔ>S-j]OC.&e'~s]#r a/m?tGW%NL_q 'Sяh gY*>s:` qϐSYyC4 =ZC~WT# >{trp 9̖Q\ÍWI&)k7۟&"8'ndQOAa3*LrUv)Mg5C%]%-Soc"ԍ޵|M.ѻEݩw؈_m]X-:ߔ>j,d`%bjv[jvI b#?-3?;HF -,rZL^m}oNII}KΜA[wVzo{1_]Wy^xʛ~upU827E"+QHv9Wpjh-q"(xx^R~5+CG@]TBdӻ;y4^[ GwR pGҢW_f'g{IR10'Ȣ~ǎ+g ffrWs˙͙}Tڱan'|U`l}ƒ*d̽ۏ|'b hS|\^p #q0_|&d/$9X̈N d8 Ur|ƀf@Wf7EӎFgls"t_$]м!rűi؉ϏU1uTMj;wYw]FZu_[&6 wg]8R;zw!$`IUBfZs$<@Z/_0KU#+Oi ܗ{wqX2d E!IF,"*RPvgɮRY&Kswq89w^|?~ull,lKHQhD-"?\*:`ꡕY"drY%]OeZ=Za\Lƽ^ V>|ip?\ts39;L.fu2xN}[7 `0aZj7iZp3xq VcMԡpss$cJhD X[ s܉ZMm~sz`7YZOiӬF_܍vґOo%6j4W_bNxn$sS[[ف,Y~L+=t@dvczQ76y&?(TWŴ6jZω'Ӣ\*cm`*K*Es=EۯڨOkZ~ Uݡ9YҸ6&O݋BthQsmFћǵU9.=$f AjvHB_+\i̳^g*cɝw)}@Ӽ\"%ŒIxwSk ] ZX00¾J7$hˋ_V}M3ewvB2cƯ_;`"6s\vxvx:w:w}(3b:Ld6"E?oK3K{4Ԑ>wfͅ?ݓQ +>x`fٷt|d]Τ6(*FD|8z{~c}ȿާS򃜗N=6\mo03=c].8Km^]5gNuq3f #詠S۽IhzPhJ6 0RSg).w)$&H6+bE\{馧MO !] vb D6w/\ 7,GBbV(QM#ljBiG,VFBUBUA#N^V,> 2c2c^U"p%< Mu5.Y N6amJ޳o-{/%)Tf, I3塇ExYD`T1P$Ik3j"8l/cT$h!rMx4yAvjS,$ "Z_43M7>Q t{ h6GQ"ێ(ص+rNv{5 +Yk֒ppQ΍gF!IFhb1dd+ (lF) 3)K1W_U\lm4_l)SD~n."#mAecìsZX~:4 rLvu;VaRF&0djacLCU!Ͻqr.gDK9$ IOCS?:;BJ@TR _#Z44o)M I :PaF^iV|swlW$+_kӳ UAv|t)m]֯4w Il 9ղ|R!K \N0_dϩ DOD"4g ׅx\IjQ*6X([<ҿ~7758W %n)6 %I !6qپɪ}F5TOp$9Djejcce-Šc77ZV9Yr|pKOOLLߛHr7~)gd^Q/loMǰɼ(Gº&a~Cx=:I'd(s8RI:i(IQ \ˉ44A'"kUNU=+!},iܕJ2 "|j6zތL;PЏDJi噫YSKNi+ikvO'A. ? ?5uP2rOdȮ1z~~KK})NhS{O )Fs`=/i8"\5bT(ijiHu 75iɿ(115~`5jeS(ɯe>x c 罅].گkG[dl3hF{%W9n:9cƪ\"6:"r,{.O 7m6j46x:>F{w )-$3]vWWu3!jTXCBѕC6&u56 hkk˽<}\fLmhjf-UI ]ZǯNM$~aN4Us +Q?bŔu2~-LT믑(놭n&/%ӿ*k?z#t80ӯ'jWu{]-#c$k}ofU"vݩLۦ^zL6FsR'ߴ4kI7C.Cs J$V3m79TW!GmrޝC}o!k"[LY˾dMRY(lEBٲ5v!IJ$dH$qDI<>3s]w9y=s/^9uM)_]|zfBٽudu2#fDN=#!"IH8Sq"<|MP{HD}زx $`kk"\׌JU*͉f/nHaaf%t;@jkB90(_@ўl?B.O{ b3lGA}JY\Qͦ+s<XXoe'׼z * ?=[[3dLLxU4 Xw/_IW\߬SڛRV-Ljt׋ԏc +RYb%4H'o w)qO#B9osc<گr&%wcIoI7+,!_ rڒ $9MC~p=$&'@6t`Ҥ€LBc$bbE¥̦E)jć &oo*k*1|uD;OS+#f!eֿY$;9)jZ +l1!7%dfs: f2 dT"L0/qcp-,e05Qym9qZ l(I:oۤ4"bzXrz']%!'"3$hmr5no!<1i}""{TT}.$Ͼ"Oʢt a/^oUU_?g4hѣJ ,vkuJ|.-4/r)cePigN6&ExmӎI7)NxYg'8#NlS_2Um7ij[*]t &|?# +qrB־m^[G:kT6~_?O)w)62/ndn[HI8 >$X| _~iTCSVZsn:]P_E%N%n]tW VR3ɈA' BZx6ad󂔘Cr^T(BVB'\=0ALHLww,%sNHda5 `DLʠ2Ag*N<3~rD51 RI xRtUV}/ $S宲N Q-ZZŸ,\h,Y[>pΆηQ 9Ğ9u)ygO,~Jw݋}W6+DۉՓ=}ع9qhnNz9Dʙ nu[x/`e#>-ׯňn `˔ er/X2]݄~Fpu0U&ljQm(1@$j^fԢF:QvS;fc rB`é8HM79Cqw޿)NSwe׽]])ƻkL5o&ņ/]0Onσ={0 4 TX8әi!ins7<)N4~ v?NdY;@cF*|'A_4*$V'"j&u?,'vm"QPV|Sz66<PPy!o+(1EBҦyp@,}1%!߆- yRﴚ؀D2^^kTG.D.daqT@f7$ >j""Ϋ]ee u;vK;Uyi!(ȸ;lL4wN\J^]X6=kC?gEŎ~ą,煀 @V9%!;OspYY9177Z PsN@>ۨV 7C zˀcpLv-B:!Utßń^$/M2}vtg7F%GLٺq=+9DDX[E[SvfDK_72>qe3DDZaN3AZVcBRpG4nfwMmdž=Jȡpwξ 6ؙibf~ƢV+mqyY߸4SRBNzM5STQcs[?;3sKe..~м28iLol{`+@pAYg]5*sѰ#(o%]=̉x:ʶG\9[1b-ʕa]߅8Ivr9½ߗ촕sW)N:tj,Mۢ4'L8d^x{ʶkf"t8e'm/G%Y:~"~"r1b?Y(eʶG6fɼv r~Ti[?'wN;̕Ը<.ݞN?LJhg2$DD5p )^0}|kRA`+^QliO|EnY|A~{.ջ/٬dO+ۘ5| *Ӹif&)btX>nu+CMȧ#FWZ@Fߋ9g6XBg~ $*' 'I66[;.n,1}o)r5HROBT̜r\6䅗+dS@`H~`J' ~ EB|n]JV$"J}xUfƎt''~a赧 &}OsNJSJ^?r x;GL9T[c2uJ~Saf%p_`BQoit` p(+)8=\`_<?}7bbOO;v=UZ)ƃLAGA`TN`o:$~Ўfq+>k2 o vU ^Q]"g4ĝDRsHϭXM! +VxyIIu? ߷JMZRyyTӟg+xvinҽ)'[῟ʹ[~dsI}}gLEh V|p{ٍȭl+2KA.7Mlr&R;XJ;ϕ0֭l+PSuO:I0v9-} $JZnɰ Coe[5N:fvɉVɈydJWH t^UqjBィY_s\6ʛ)3Da2;ԝԶt߈{I@eMvޏ#_JP'ϝoY<7FHeY@8.+*W(854ZWIy(5WF9A rӟ (q2;#!^hf,i}g?!6=6Bu *zמ1rK~~JT8O?=G,,f|'L9ʝV,GMO.wʶf׼٦w ܬSz. W8`թu2>d=L3Oh|ޝC{Ϯ,IYRQ։JEٲ/V-Du}NPQ$QZlSLh(k8~5\z^~_߹wsRw1paD1^-)/_brr,L3}i鄼\OJN]{A 1꡴$ (RG2.Hڮ|HL*z&RD7WXW.q5 [o=RXYDZ-O!tX66 )1)1G<-,_LkN3s?࢓GQ'TW)U|Zrv#}ړ @y(Rd2Ɂ℆,ɍ㍉h? C |#>abɄL|9 V†9 DM$Xpn mC Qb'%n1&kfXc׍h [>+ .4S=No=)RzZg"͘7]} |qc`}!V#RTU;ϥ-,?? aKKţ΍~~p=d0'N3Kn E,eX}BmR̖ +7mD(WүVgK }9qDHy3 lָ˸ c 2Ph Rވr*_>ИCZ0vPqd99Xޑ}}G8>5rձ wI뒖M Z e33057Sʼ+Yf'h۳~7ZH8O!uFQGX$ ɢ~86|0I5'<*HDIOp2֏FF뷩\e۳]g2j2겻­Ȟ:?z72\ɣri]%l4IۑK =J|}gtR[b\e_/UMyxm~3˄ݝ)Ҽe0\/ڭk87 x:*-b ~ -mf9;/^  bX>ncyDd3Zm8лF5e_ߌ'UW>./):mf~hԘÔLcnE'mׅCr:>b& iS;mf?-;d1zS/񴔲0X0$e4<[cf >'GaxE %~c?mrONM$)zi91,[h#cR+@R-.! 9dµPa yoE/xS 妁B~ A\cqo %8|;N~]RVkbr>uz@ۙgssx\H#?CFnhyseLe vv#\3SC*=ö V V7Z:5_ +0q0&vxp=-gHӷH$3=k`G"97ȱcJBx8q*Sqm}h́B2= 8M( 4%wM E 2ڠU:΢*$x*G罅uGX5?BwԆݥ'pU ,(BM;/ÁT,==vvAbD{^[t֒~˿9%֩@pu%x&}O)s'#{BG]S_x#c4H(n."$"("rb ^!N=yHrxBbws@:Hr8[><^Me()ok&l3C /t=U*BTm<᧳p"E`KǍQso;bM} p:vS30ĵtݩO>rsA=ÄFw:oRJ=Kާ2vĀKHI&sd#ΝΙl>g\ÿ"z6Z׻Znd]2@;dG^81B-`0>p(8z6Rn/dn3 |!tcϓ g! vPm??a A_o8d' g:pQiVէɝɇbLF1Aߴ_4+Yw{Qnup""wQ  ˋf5f5=[>B쨟-9xp҄ΑnNɭuj6PU N4Uﶥ}:wc@KQ?x[k9|; ߷'%wm矩 ,5CȖ/=ay_cN6%?M j$MfV"֙F1iė"lZIj}l3[/vn9^Sfyڂx:G?wÄnf"upLX=JRJVsJGEFCsf9=gȯ=g|TpV_3Vg2?tp,W%Zb&@mKf!O=@ZBv#v#,.% 8 I%n2aj05C\ȓ^k88aE )LKҚ,shh}]c'⳾UXUKb=adfdVW}*UU̱cVh=HT{v8ɀ[=/Sh;%d:W;[ F֩ءKpdAxjYUR_r Fۦ|"Z#Es,ܦHP͡@}CeBTf*q oBs 6d}ՈjĮȋ;&Y)DW8e`$CJPi!rW#;)o$iBTWYۃ/yu^_;w~-/ U U-޺g%JVB0_b6j TɕȱKB .(o)NYu݇76m_DCjK~ijKbBCc@XS^rYjʇ)f!v>Bxq^R"4iemǠ,+neZH d߳)8* Լ 5pšF 9⬊.()E1dg|?/21u#ttgT*Kzz },y#`+ [q?qřyrq*m/cSѶɍ`4&Z;K}oB:Rf{Sy:d)~+ v]qIMpvV>}PSRY"ficQX?beUIdӯ@O͐HI:~G/0(?&E]aGVOm^/S!Gh_j=z@|T \~K~005H(1@v>K܊܊9YYZ\N&egaN)uᠥ,uHn (z͜n*N˾%[?!"yx2( 4⩿X K=P~D@@qr RWO;N,)W5&MNq\yA%cGם4ɼ|yUe TNr8mNmPPrH\n:u|LݘbZYd jFdy4qK5}m썬͒e2IY+%Cd>R%$Yߔ,-#g ϝ;g9};=pμJj_kh?;v+ (Dۙ;CFahctHӄn&8zoz_n;+&d 1N^R֗گ޽":cF{;f~8i-bG{m=bBȍ[\d$esoaRg䲹yfUć?{ƕLߧ-՜FN՗GuO8 Gԉ+Y#=/p7=uNx'r1̺mӆRt;~VDiālqɇ'׼tfM(fyWsJ:'?|Z/ -E]u -@?lA-վY0452|UD׿ᱭq8I-/^m*-RPԽg(k 2/e\9 i+IUa,\mLyF(ydZiݦ476[ k"G(956z.`8)R aY7K5@fSSkvca;."ֶAZpZwЅ'wA 2n@ZF|=qrUܧ@-U+n3(/g;=;ݬLԶוܕ| +W,EEB.WOqUlOO7Ml/3XFBm *PW"UO23CFGrvwZ& xIa;V;e >ʗMlfm\(7aNݜ*WHE3A3.TjL `^} e#w9>~$4ivuu8Z4\|X"wޝ_ȒzAx 3SHs.xP'hhGOAʴ*aaV}Ƥ-߯9+=+B_Gio٩Tn:^%7ǖsp@%L$qf'=D8yq[ˇSn@%Ś^juD\KZ!]ZQlSԷT/E\OVyy$`g,R_pnvy\zT0xe$Apߐsͭ14cncn$Ld4De7|C19;tA  XR#+=3Tȩuks\%u>:?}gԙ@E'qʆCy{Pz0=#m=u>vϯjT',sMiolꛢPٔ1w Z!"\ Fm悸Xe3eXҪ}nqzbb莜͈D`d|UAsrH#9b<(9yи0j }пL T>C~%U(,Gm;űNmcPWS"P]]_缌SXp1ǖBu8^ÎdG2UUbXMɛ 9G3RAFh3;_~cKhYAă+B ˉ#$)qFh?ݎR\FQM82gEy/e*FE5X ~Nqt$kLk lˌPtv+\^RP+܀ v'dHݣfi֟Wfoaz*L2`!ӪI 3,N%~a+;I7~SG(ԉѢ݋,-&.fK|C$;G-1YJ}ȋ,3N,A/ h㤙Mvg:xz*!1 ˙*[Ȳw33<8 }@ hRZ=Gp~}3F6OGTX_+)2tstA! ڠCV kS 0X[@rAJꔺvCe& -C[dG#]rqYxd;yH#ףݕP1bUU5""ĬMK3H?q #:`mJwcS'dhX ,Z\_ ;q{z=wȵ dd.̑#  Lڬ}{-Ûqae>PH}rԥ<Z[iuaN, wԍPk_[_?g@/%M?vfHkI/_OF) gDZnz[BxkN(Ϗ .U /IcHb!\c4h@"녖>va ::2mUq.~ 9z__R|]Jܠ`G^ΥS>һ+++<;?Eqm6iUWErkcD}nqPcc2x>Kf6 0\TQ̑P;4vh|]]lWWj5njepʊ$77y+τؔN~lU k8̧&gRt.D̃z6e.q}M#m@m,ҺO/@h'F%@ u̿{1gI^T_\iTLzO'.TeM#۸ځk Y[c0|4hμ|@L<Ԍ͌MƏA͡- B0;y7&,Krf"KO6k)BV#hH֊񗱄Fh? 4{α><ga3m<>B'k4ۨG~6IŹ:`(1H]%(fEv휥QemIhV;şb3Ėg95,GmLlA!8G B#? ec/yˤ+v2?[fƐJ8KWk?kK(wkUi g␥I5b (x5 jE[JsRJŋЮ~/uo6C?{ Y)hQqqSux^N9qáoc >@wDWW/o= #rĮWNJ# ?s  u0>g mIJ8fH13=煤 ÒOoi9 l}6J=0&v7#Pd@~C5czZohR f(J.+:x*.>.\)K5㢯 M<2ἤ#/- 52 ҔJ{c{+d,NHc 8  , [ +gKdĪɪ}pbiJ0XP~?oo:5=/JUμ!z5zÉޔߐcqL'cdPPLc,97l;Oeqۼǡ- l:^˦l4n(ax*#~pJ 4>LוmwlR:zGU:ȫ;suSJ|'e$!Nݍ=8v 𡑁:e)t4p;$8tO9)' <'R ,u˹+*9Li 8 =4og;KKV_ew|%nh[sKӥ@4y RAG3Oـ3Z)TdKFa+L~ 1׳#:Eʝ"XY||Pe2 b[Y[N#5dN:R:\FSFs)S~Nn}NL&KlIEj~:ʱQ :eG%{NIo: gd-b%P.pv"x$7jL̛|f|cc>8G`UldÓ; :WYCZ~@!H\ BbDΎ,EH0ǒ21Zm9s)݄.NEMW[{5WVVV\jbTCO8ݞjHЈ[hɹcm֮^7G5|VE!{3]_2սZ1ѱmf> ٧E ],0#넩sRϻ3w%$1fi]Lpfx%d{1V]{L l5^z:r(>۔LNiq\ ]/Dθ u8Fs #rX//V e5Eqth7v閹%!]T,]`$f}?L1zfNF(wdGAgk\Xl*\t45{d_ rR+8^3m3m#>Wcλ&S g';=YM`0=Fm&cԻɜ9Ϻ]d^Fj Xf~tѻwOr}u^{mj)+ڨ&IAp}YNӄT2ZV,2?R 4CId`#Fg,х}6=;0|Ju=u1׀Rx;Kxx d.)A I#Ú[JFy;Qf|<1ت#CC;vUaV2Gt &/1pFFhL-MBOJ%`0N1C*}&FY'¾utS\%qZluSl#>wP psKzACe`PTǂ08ĸZtK=Y-#ZI= 2J.IX ~joSuh y[V?T x87PeӹV41>òCOʾ>*Z:/kງ22MAHsr:w   yNh#ᖵڨSf78'4(f. 54kiI賡MAh|#0mpx\fs})11dceͨY§t'5,LXu˅"D_0g4gLhA^k1 -Lm5R@Y?. Z{C5r 6 j'+dQdQA{Y .d j 5Fy)RO&78Xlm`mV5}5y;*liN_S |Ư5Cw},Xʲ1[0,u<&ة!f@4~")P?iÓK|u-|f ʵ=^Q5rwҖ.t¶`Z1Gf/C1y許Kz1ejNDE9䙳~\e,љ?eK*%i,ZSuvҿͿT,;ÃRLٟ=u< 'K+{dZ\(7~}GmٟUg|zE7XjMΤx#N{3G쓹ƻN4y 3?9 *:r:-ON ɇNo |P 9`x2>Bڻm׀'NŏDbxc]>0 㬉:V|3`K9CYI5v8ȅDY5$rgj5֜ -h9PKLPp7k(M Հ_ÜWW3v% O;;(&;;L#EB6Zqpa԰S,LNuӏ]1Z܂|NYu]@65g!QC:fB m|ş-%.s.T$C1 8(ɶOᚇ_ݿ-=yBsڛ P!A  $ҝn9%OWIr_*JM꫍H6XG_'('/UݛGՓd $nWr=_g>]dj*dH8{o[u p Wp'g_*FB.Қ.wz,LrMmbK4Q(!X~,@ Au⡪ bș3"hVy> IÛ& V@21cF|:ugPK>#+ ac7tQ`="l2G._U|raP:66]CfE.\EYYg>2vۓjNYuF( [9g1>y ZbԪ"yЪ+'Ihg{{s?}.WqO,'WJ#y|23Ms2Z .vf~;m-:e{#\E!#dc$gc9c߷Iy_lķ~x,qX+`Y!#deNmCOX_lUnҖ6tHdJ"li3/#d(jw&{eҜ5 9Y%׹&J{su*vr4oY{mˏi` V\ZX@RϪ|EUMZ ݗېj5(ky,b(\޹L *Wrq/ِsHZluRJ;:~J>KoRZơ؎+NfKaQwя9IW/9 鷧huociOֿXr!N Bb<iջ{;R""f?[HDt&<)^wU\ $߯V@A 6|&]4  &P[`⊦R AF3{x@@Z cJN'jp$LJ(2>pRI4g(u_XĻ'L6Vxnο\L]Y4]Q#;{hlF0םApyyq/^)m,m&wJ+2:eSOOH߈0p?8\g\wz_ `Qr̆P]CՉ/JG7=pE[[Q[Q+]&o %b58m[p55awW̊4 :ӾxV*(:$CGvtJ./ln_mϭ@fڝpmmɃŕ!abÁݎ_|&&Ohh{yD;uh~C~V:u-銠:S yp>74>bLITpcqJ91V-$]"ʭb[;v@+(Uv ]K4ܸnu #A!θ%p;ĈBrM=mx[423@ ҫiAM﯋ 'yqיbjy7+߸591n'DDֆ, 1 BVq|gbhkk@@ݚʲݩ?f4(w(/0H8nl)gw}?;VߧLcJ`2iedXb_ #Jn.%g^"!JT9H,-lcd̶&|Do (+^ .cZ,S6Ԩ[=?EP8}#ݤXWJ &bʭQ%MZ |2UKDTDT" Uykp1ld@6DoCԊrr7?Ct^B;M@/)@U8>t2aH4Dz>ʨeDɔ|BouUg,-e8e8ݬ&~Cj?\L@'&'3l6Gf O֬I|V;THM&11E|H8~'BT1V &Tn| fR 8~)3}FT I:#Ufê8۰հju%9ݵFwȔf!u-DvjBC5Qg!!{f/k3d9^ҽ >[rQ#9BξPaȕ+GWk/9T|"^*`kfx 厰] w#d d /EhVzxhk돞N@ՑTy'1y6qoBFYF'S$!Ƶ#^l?H8jjr.qw#dt3瞑NեûdÛuN˰>9,}[~U_!#dDIT86Q( xc{Ik}FJEg!OdܗU,[ ߊ[~+Ӡ;}!dNkNR| [ru0/3Vv]IƢc gFS5];k*J6Ӌ+gm8~Y&|%}鳑ؒ55R5*b?C a 鲉@&&,,r5(pS[٩1 Hn}w P04NJ1AbN(Zc/F哢o G;;ӚӤU!Džpr#&++z1h)֦`.C <= [u̯N%|b0&d۽*HH܀yμ矾ᢌL2P4B@TE|v[=foOA{7$@iϓK?4^II1[0Y}l9ѠEt%瀠 G(?RG[S5|~M}<6y9rg>eal2R6Y÷Fv] [Z}UI HI}^۸:evD!/usI 0zxn\u؁Ot4;Pii7Ӄǰ] FS|T!R?;vǜ BvA^Kݒ5N9@$>>{>V \&GtkK.iԱ9moM(8++\PS!Wx{{Ͻod ܳԻީ[oղlsRꃳVrթf -tXy:-4ϐ2<8ae,Jc'\ ȱN(b,2F& y3РU/-xxjyjBbZI!~hj}u < Tti" >>QǧBu>1ͤmN?xo)ttC=|NΜ11Q9( Qǵ_Y~NLYLU%P+Pky_ީA9kC,ǯN0KapR{ѾORΏpgLЕQ\M1&AkT]W1|RQO Ch?&b ƶbJeԿ-$~D4qƽ]\ rGNE|-0|{wNMȐyI2'SMLPBf!eJBf٦X:D$dbDi`}z׻lskX\~uZrV_Dr9ĵv|UBTL/}k,;`};=qmX's/|S ))UD=E=ye""h1z,rO]v~ƹ$f3=|3_s'gfK󴵙3\ZvY #9|ϧ޵"`|O)PGTُnP_֝l'bozgl麢mGމĹ-7Quh'S3̡cZm(v;eJQϖXԭdi~"ߝe#SߛZU=_N6ovk#͵Q=i7H쪭)'6g? ֔lxҚA>X*zSHV=)**5埋q'1@5!4ААuaH6WQ:x kƾ3B(M^̡v %ĔJv^)^kfŭ(S3sq 1 #̺ͺZһ8, t(8}}jj,SSVSxJ rXThLHD\f.䇫bzS#2AdTf͂O\ uR]Y4Κ $#=P3Z+x,x*U .t;Ba뛯ijf݀D}0r} ÀdZ.(]%dhH%-=swG=<-+^T[S~G..sp8QW*) ~8q{q{ ZToKg*3EO܍B ط"UpUyms џ~s:Lvbv]w캿y.~9O7:7"}X*UwG1Zژ:)ٸ&WX+k&2XܻOgAi/ӻd)q%&+PwZk?h B?g2۟eȔ [|&Ɓq xz#!3 8!ڟ2F>"Z| n&o(n G3AO8-EERcd}[|{Oágѳ_20F❍4EE;9i]nNx<\~,eB7De;T",'IC̓ԣx+e hz=\e./5,"!)Gz[ q6ssI#FO D H#ǜrd3CS:;!,'.PG5TP}5r55S-߂I઼I;I;ys_ˑ@Uu!, z z,o7S=g2 )d΋ab9DLFk=QvۜS)|u*>O_ZqwJJaNߗ)Xf}g'DJ\QL&O+_h'nmX_'E<<f!/OVFKp^{mcw/mtҺ< z9 &+ja!9s.V/._x&pbͅbDEUz@.ԙG#C6 lr~=>pB Ws9J„{zߺ.uvg{|ښ]wSHSTm_8Vcf.B3ֻssuBeIe G xDȕXJJ;rdv)$L" z>$;;3Wj wݲX ²pQa%kge^j^Y V-FJQ))SJ_ ސCԪqR Q{b e+BQk2*Džw>$~ ȏ[ JtuQ(liKH'g;p dE[ G G~p.HӹKO2}k?l~5nO~,2kZ_F.|pIvy}Uq^<4_)mC_HLq&Z9tX~c8[E;,gKQ?鲄2 nLڰ46\X`$ L O{k:T? )܃1$ZQ8uaÉX˅x9BMATD'0,8eiR{!~ӌKhBJ;Z$e t!dG -FjձU~!.XsYRwEq1s1:ffZW;]D^Ϝ^O&wtnNnv:WẀV?E) /Gƾ,c|NoKȅ͐D_h=2Pa00o,[> e4q; <0k5B,0:T*!ñ'f:Au(*حʼn^ h/֣ s&H=9ڙCV%O[,^v=(kt5gPD2˷)'{4 ' _w^YRz_ 3E7=5I-$fVYҲw|Axu:1( nW`HQȩKTcxN 'x?ATC)XUW%jWWSe ).6$3B_D!cjbh8T?$]R#}ғXV{׈f J gNȐrh#_Š!ç5N%Ԥ5[?f7XD2HMIMI ,>hs|BΞ֞VŪ*^ѽ"WaN_QCdgM 7}ۮSTnk۳uMԱJ5OU>|b b`/aП\@p;,Xʿq5060fiqEbmcJEQ]d \\&!RW tE!(dn g ( y6()2/y'B!l MVo)*+Q7[l -ҌƋp+n+nELEL՗unpCJ!(͍^`ii{**!%~ݩL$KH&A:u{S/~ҜRT8P<Bk`jd3h f{ y׉ٚ,RxѸ&`6'BvGy*(ȼр @ r1VS=`F-}B(a7Rm D&X?\E5ccb{FjpF֐VLL=EP(ȮȮԨ^|'dYY%VyWyKKK[ɍ'T^h{ >x8<M0rѱ+Z+\~ooCe|{lğϓE':nL ]tdfDbH~JzW/ s8_v@#a;͆5XMH^N m# qqGoW"Ruٕ!!7pzb"Y9Bco7;jo8n2$א!2%B$3 E'rCL!':dT!"<<߾ss,=9mll7M}ʌJ{0[Ejk~"9Uv-kMIs&:a%ٝ@ٝw9# ?wo:JXt -{HϩL$g˒x.> HԮRrݷ8 Y婅́:@/^,ӏ Gs#WLXw\~5MNά;i C="P$Y[}Pa`2oA1¦ænKK8nj%kJ&K^M-{m }ST>~PaCh~u/GP+ӘrO]b)3I@$z BُfM3WN}7nnĿt~6 %C֔pAH=}m~i#P(R†XP& bL;eZ6~9iHF c0egQ^\W CA _Wb)NLL`5 !@nJKǘ[7Hzool1/̭K2[_qDYN@HK4r9 Va w/ćL%(hU:̑P>& 2y0AD7NuDԱ-7Q~VxӾI \õ\gh}2GnQj&%Nzm{9Mۻ@O?R,\+ԟ,Z>ւM\õՐ0>I {W7-`]Htj>2ũ"6 pV$G{Yw bTTo7e6Y$5\Q-=T]agq> Ery5N+ ܆N2tb.{{cʬGcegGD3ѺSJ joWgJo#M(Ϯe82b8!B&VA#bv164s.~`1=AKƴ{fSn R t=o;I3}2teQX0/= r CXuyoIh,u`C O 4g2S7 (G|8i1eBP8,yYlJzָGtJƫqq?^Pikg%G71ɁL=zzt22>h]g3njp|쵩;7%1Ś'u-VS-iL57j)ezmڮbU`u3_#p# w"Sm5APew\;&00 $_*(ov*$ 1ڀ7+K }{u@ 3Q[ܸ5۾&jCZH`>BAxL*6,jZݨk%,,3ss WW hpCesL踰%DDӴKagt徨p.K 39|5\x|k}lb<6~ͱ[)ĆZa Nh~z^W(b7h\JK^kKzL6l16{T?颲}R?XN)(&(&(~l;''R{]==DލKdo@ºU^B uaFk @gtiUqTMy=e|3Țd$;>u &]']E:gB)*ɋ;@Ə0s8 &EiSAbAG^T$b=6{Z~-[l䷲VhN.a BA--mzEE6Yk{S<\#V#f{=~]sY=x*@>HmvNGn˿e(5\O^C؂A w,⭗+M'"4B`)wQq ~TsaU!o +Ό'~Jz㌊M3ߣ݁k?W>vjB^.%Z™iJBa@y hAbtjR$I{D51ekTϖ{g\USac/ϨS~}%=Ol O SUZ?6zzy])*Nሖݲ[oq%T' fe)lxOi1¡rϮ^+'T#3(p UHH{z0zQb/ɂċ 6CyO''XaQVM| 2\򜏇6[ד#쟔ܺ\0b8OL6'm,hSUք7&wy:_/C/CbҪ+aaEyLyg >#B3ky(8(8fGJ>B癡ecC7p z99xm*f;6=SԚ2GiKsWtlVOd T|"C7r8&I^@ʭX?lf}Ϝ*Z>4kʾ鸭٨8NY o+ɛ:mABVg2ᶙ/V?|l"a>t_WtL9*ԾK.Ho+/d6$A3$}6 &[Q1,'"Vzj+~\mM^k]uaBX}ۓ15oe[1v|kO\]|TͪuOS+Ԗ;kQݙJ=ed"guTX@KxT{ o/g 䎡7Őmv*|'j)4򄸸+ )eV̢>c8򷈡.Fw{ZI N֍ʌG1$X}洳?R(h ahex%Ɵ ^&@줗5hϣޭzpxTd2_~HEʝBxx ''9;ң">sTA#T>b4gGw$$K!TJθJz}6Tz>5bn'ٸ`Mj@(ȘGֽ^#~Ĉl(}c y H%*]Ҁȅ`J@~YW#0MYv=YYޕss֔3ڙz)-ܒْ'WY_!t`66!00D)#$\#\twˆ20YilVlS;?sgTdXN15ϾCZf*6V6O_C>,ڬq]v]%УKC^jL!$K)V张d/=_Bhͭ?P Z)S^i!/8h؋/T)SAW/cA1'wGA\]@)NZ01m j%#/}WLAQ'Z"|'%>ۘZN}p*2𕑶g W3q1w1?g4<XC8gT J!m_{.íOx Nw.#T#k:T<4IHB(?g GG vT.ǀa ɞǼg5kr &RIF'!4!Lr# ֮ Vt(xo;ZI;^U1o185RcIJ*'`Zz##lx4l4lvHoǵwpVVHk &Jjj! SY~9{h&^il֩N):픇k73R57G]PB9mAXNW1]!_q44X@8k+)њA0Z6|)Bm + 4 1 <󃨘lUFYͮtF Z'z7vykzWjQBϘy2E&\(8W3C h‘/{y:M/lsû$BOv/[zdg?V{e] $$juN%Xeݰdf ¶Q1#&UH;zXZ=ṯהi@JJ{e1 N4S4;;HBⴃ><'!(*8PPN_RQ;ewP'j2wdyMgvf ki‰s e̒Pjmb+NH{ p )C$&^0'rRYa|wpjy{IzS9DKA>Fg@V) a(kq ؝BL+&2S05SJ EOM`;&N6=nսM~(Vgʋʋn] qFHK CCs/dɊff̊|ZX/)w s7O+ "W1j3rn/ *[HjOB$4X˧ NHYŗ $=Xs^/l׶N!T~ӟS=P,s`TJ0B>g&րeQ&+s4Pxd|ƴ8MXu@$(gJޑt55(DV0v^+.QR)$:K(Q5b_qXBr8=&{vcyij=$p}}ޝGS{G QPB2 ~u* hw>0F”\ԸB炁$ԡKĸcCpi*:c?rkaC8{՛N3` a ,Bv ^ITEWk33"cZb ? 3\b gȷi]+#Ltb%QM9Ց(SwBd$Iȳ'Y˺@ާؔY_X]^|:BFnٜsGfi(v 4I*5Y!2\?<ͪGFЕW"ʎTiБ:z%@S ~*B|%Z4|Kn@7Y-Qu<m'JU#uٿzުEVI^ߘ9L1eZɑJB* m:z'P\~-Տڪu5$Sr_B<‡g2ï^%jrjμ*?&=HbAJpvùgbX纭2F+b- _^SÜq!>Fte. +jߢ]peLPRTOw1~f8'|𮝲 [B`)==_" 7mu3=~x8 -F y54 @@I{@|RHGwZ:u|bw{kL#,j䩺1Ezzo(76V^Nu|$dvWy8~N#lr_(8qC6X^ifN%>R;, ^gq)>(->5]`Ed }¨g>W-ے䒤YSؔ(1hN)9O"BBXꈯd+:5ghCh.+%چo]ÍOgxϵ)Qk{|7m@ie.lҰ=y_JA"?)ns:HmepCD;TP΁$8)}ӂؖßeO ׈6-@G0 uu8w8*L_285צ6%^@GG϶ux^wҌypvNN"E?>9k@O*HN txh? a|K/ y16jjlU>hci A$oZ$$X !' Dw!{Qh%J1p-~|OlL_!Kv= !>>7tWmP+2U-ΣڻW'?1+v\MAg; M;XuJaVSm ީ)d Ɍg6TNʍF¥f`<Ι{ᒕ#0m[oW2)0l85Kv >_x^;bc#xwQb{ NKOQYȉ3Uf%}XY >; Oh䷀u u ˭鐷 j-]*|b"%K4|J U,o,99# {N"O"IImY\OD+"")wl|׵uDc>5-u =5גmfM5lg8:e̞ݲ$h;QMZZʿ f1%7 3v <]vJAhbZ fbZjP9\Cõ7a,;5k;b& |0|¹D51غE>F610 4zx  E!4WJ8֫}>R@rSԷ/i!kxs>5#Z'di7vf"7UѡPGyhݺ: ɫĥ W{:|qR}ٷw|ީi⨤< {l֩ߝעk3|`P1CvS[W귺I7H+`k\ĶRv ]o]I!TL#&,ϼC>Mo`ZF߫{#T $fu̠ @w 0bǿɌP8Evc3mx2c%̾ 2-p)pQoiE VVKb :*8tt oVNVN``P6t.3@Z< <]4i9Ͽ;o٥No##@^1K-*i_2>zhyv/A&QQA脶6EA18mWoJ}bDt\SxD pԅԅjM.L԰Gkݶwoz&԰lI'13^T+1C~e?.J!@[(T|g/T؄ZZ9kbĎKYFHE|c]w]Viw>lťd.౩>Z@?B2 h>/*Ne~t~k=۞o ʧgGNS>paa/nAZO"x ۙaa01ou8ZNn&UCBn }ګI7k?RtdXm˅{g2 &d*~-rd}KiA4f]P4 qp"r׈n ^Mߛ}2!Lh8"}`6p 2Ap#l1%1%w>և`ư@3HB1121~ 9R&$oE\wWөz?B;ooOMHCs"RVn9ekjjYrb7\MtMt+EW\A8NN(èȽ_jM;g_w8p[J!T^ J$dWd0B$F̒Y.d !?}s=88:}gʣ{#X$ԩĭNɎ $(KPV: Lb:Fi3Ku`r~ (P콉 9L3#('(x.nZ%09p4=ɑy;INNㄸ8C?+mA:g'+ڱqV4i%%k sLJe`dœ~P_sin5>.S S Qe䃻r:BaII~w٣УPsM*STipdQ8BouvŐ#\bƓv{m$zcw?oUșt%84)B @HcR S?S[U)Γ(>\H~=v }JJxde6d3H=y]e LVcq7:F@|y Wl\u,Keâ n;/?|̷]{O'eu'hQc:-VYX(P{ܞլnC.PO/6i MWaOGǻ9USt$گ] ek|9bnWn2\]"S~z kddk2ַ.^Gݺ;Qn2?FlSmO͗=:"}|5s`nfѩ4U xFP# 6헩 VG;Y:k"g ᙬ\Ӳ)>hS*չYrxz^Nc> pD Q~ﮨ$1L%>HT ݺ-xPS3d3: _Ӿc@y[&"p_TQvRv"8C&@z#&dXJ6S!D3^F}Wݴ/asJǫ_HyfL ۫ t`@ci99RRV уBC L LF66改HHqH*z3r0:sNJT!U+йw(1щ=SK~wqT`h)S(ܫZ8 BGD؎4JFpU=0?j֐ cYf$"Bu= G@3($=+?+;AWa]Rvڂ*f.?KߜuNs읬6!\&vxk$-뙻9pmOLW)坺pg P*I}TZf|s@Y'̲=cAtfPD9.aN D /6֥K"$͐+JAI7@ ,wf<ֺɧ;ky%m nϑ˻OB$JkNnS-'U!pͽ?,(,(yVWUE¹Mnr_52CsNnuX-':$(ݭ< ,!bLKx 1+ 苲?O^(mɭ1 .%HG S#P]}vxU`I󋂄T]D2Un1Z8]ߟ|C= }9B5j߷-a^Kn_q]^l*tZy N ;'rx7bg,ϦsLvSҶ:%g B8Ef0KB ϽaŔt{XSQ?b#b ;;vv5mrEG 5MpO ss L0aI,Er,Ł7tUV6׈ Κ+h_&M2ew}ǼA3O(هƑ 1?dyt-0Yz844uusW! _1W^ގ_TlWl2==+ <uJ=\0Ut¬ȝh`$lk9Sr3wbu8:ϕW'I8UyI^xuQb񳏣^@L#6QY.[Hr-ہ0gq"kc9U(gh^.x=[q-gWV;z7=_-L[.H=VazTMPwћ 2'ŸXX$N m%Cǿ# ӷ$4 B&Ayc0b>6Q)KtW?C7%RICAEBhdWzf9j&?$iG?M/@. t%c%cd>!;6L[ cOl$$_w< |Y FX!)LMslDI^]oHp`S a665ֿU)Yȟ,jeJprrK݆ővuXl?*ؐſ)_hLI/Ptp9Qbm}Q0r8RF)]0\Ubb%BiōsNe,rV}f`Nelu4w8j(-)/Ʉ 1YkJ HrcStN33F(xDب&OVV\F4!w!5 ʙφ))ؿCe"5W$~v@uleѡ} œ㲒بj?6 ߚĵlr|U=Ϧ>'%_>xs{5IߥK7v=ksisO~ dkĄn^t^>i7d:ddE/ʘOĻӚL֑qno]Yf*_nߐ9DW/~xRo`qEޒD9~P^MVq^Y#憲>dvU7xVژμ}F"ѯ?}KHjԹk+ښ@4s9 ~))eul7.;sxI I"#1YϮ*3|&](#?xd?o7ײ׆&5~c=nyZRZ*Ra3ýY_&wgdm3&8ZPt.6{M̌i|%ЭI$ W'j%شWTJWKW )MrХ4ܾ? Q"2 V$ȧϧG107A(⑞"h<@6^gP >B?;CM GNx0?2(޺{6P<Έ׈`lSŴbחۗ;J۔Дy)?IUxttԩN5rs@%̽"Ý~QػhǏv"\)[5u,lYEd/K!SN$Je+%$S}"d ~}s3w{~߯33\UU]퀠 ø zicje0Fsp$hWM"=uaXm*tp#g%K |Y>PSA'e.PIvRn=|bB\ܒfC-/kl->t:u#ObʲwB$: fv܅?T^^โso'XA'7k\hy4uawQr~);r_3y擽~uǍuzgϓ%xPq~⁠ >hsǧ()^? ~iNR5+2] `s3.ً'64;J+Jz 4Wtc}Ƙ{=xhx`hHJ wI.1f\"]SXVw X"4>e2fC6X5wLt̳i*󅮭tlZZQ ;AAQQ_M٪$}O\=TxxsgϊejfờLrm aͶݦ^z{0r^P+SRc 8%]LEU-ahI"@@f%yy "9׃n2!iA[5#_1MlFAOKn~A?B 4e 4Ճ}7Fr)4K(i#xMwkJ#J#% ޅGãao::JBoʓܔV % CQn=~~ޫF"'\gvùaAY['*ڿfV8Y}g ۥu]~6:=hl."V$:k%vv}Yz{F+7n摵\\9%^<Ikmz}G=g)<1}tBr1dVs)&YFhgcq@҃Μnw,=Z .Rzrḣ]k>%\mQ"Y{^Ozz,ǃIAmz="%"Eޞ2kьain0]A9F" \wǭ Rٚlw`̊ϊGeae@cpO=X8'(Hbiy7#?|$t:wƝfz743:#E䎘EXTML,I|&H>>x" 1Xqs)S8 *q=qդI'G'Gr!nӈUV%!i)rodv)5GEBOܳ[Cg]$SEC~J  h (0BGfPpU 32c#B/r878ylSD q7[ U/̀T|.h0mT(ۈkpG=8oEa2{Cc5~v6jvvЙ ߊ@$TH+,PMئEE"nݎFqVb9  ^v|ݦ^AK;(ʷv#7>Ӓ lUD/)^Ü h ۾}g!C^ XK|p_LkZR?"xB@C L\2sH/˶OϴҥA { {4[k`<%EKU~Q`QbrrqykQ3 !+F¨h]q+a 88\zIY\B !{6cmJB9>HoLJ10ΑH1Lg'Qj@JGAcvmc*í ԈԊ曚m%DG+,Y[0_7ۚv{*"*"/TYb _>Ry)KUU咟s>|OܐMi+BM)~=wx_z"۝A??Iw[dy۬':(vm HwӦ[{ȑFLh_+L"1B~/Ir6ajW;.L< _N1~F![sS©%^ԻJO}':M"4B;w;%֚=G#r6M]pϧj:"{kQ_U*o0D-N4U0_<@:^jRL-j4sQE$B+M-zRםx/ma;NFF#޾_N=`$&j:ʫU;}gy J:Э٩c<$YЀf-Zs^syC<LW̏>ChZ;ᲤC8m6 <άaE֋,h_ӊH""[= ڐ~aUVdw Bw7Ƿr鷾)׸U!-Py̪!4`6o+?,r&s^W"3Fj. /T㓕V`mvfl+a Zǘ 6hT!+L++ %23o9Tֵ#\T&-18g9KX?ٞqK;e Nzl5!/x 4 4]u+bf^I^ID{yZȆ'?TW@a.a;_Ƹ}MnJʛG- ÇCm~|3y!#.0ݰ#TX+ QW렎7T?&}# >|*laQDN16 ?F%?@NR ZeeG7àdZYSg4i@E|?/f /SS\X z鍔[-\^[;qqz 88>)=)nVqPO.g*..S ʦ(WeZQ"Ow墫BZR$(>T>Nb?]6-e-hq#v[/u(o7` TrqM-#eo㊗^P]q8dѨ0G l׿68az-A s %XUU/A'$IJN]"P *&"@m=7 /#N{;S3$25uӵZ:6 @6>aBM[ӗFqg?.|KMtS$b_qqnPnSE?7)w*a㌛.cwq] 좬W({6abB{lqoј;8nƖ BR.!eɾ,e)$=e֔=k="KQɒ,s7?\9׹xuuͼg, C'NihbpB|wN@FXcdYyb?7.PB6tIKCږi*˯m{m'۱>m]QSb!-#..B<ѱ\T;8nk'u7'xZ?/v/lDjw3ef FPDuz\`-TtDߣ6 4 -SW V;ϭMھNmfӶqάܛ}8wgDΑ{-+czl5N, G{PFB=˨r| RT=@%%2|mHN,m\v:pwb*4#In{=g{ޔ@ҥUJ9(?qf}<[Hwk,U *¶Ru%p/pXᨍߩ&L.X0u"MnEt닚v;賗ǔdd|TpK8Ȗ[l5N{ =X)S~!.5Pۀ̩5 퇎+NP b.attT-.+5c7` yEvػUQHXc~*֧T 96_AP @i. 1vt4]:TjBHy}͵BDDO&X¥ʥu;vq%T|.TTօ >#*ygB)E@Rݝzz-;J8LcZ>s|aG_'֩$D16P*/{r%QoddFP?YX66هQfBbP *,Tp!'v3=#٬7TMxn+\8ʯ_7mA3n]Ѻ!@tAۿ(^Ipw-|QRIӥIFRp.&@ ;f̽ )uOsQX%`u21'|3tQ2얇B~q,80us[[6.bȥɥs¡cѱ55?jlIM%u@@PHH֪_)Q#?6-xsnR4ݹKmN~moԤRtH.i~iolr r94|^D)yrF[u 5=p}"?$]2-w1$2nY(U7|3QC `MQ+0ùaS :jn"|O,/6YPH`uDFnqSb o>RfO !]bЙfH49=<ۦ|>#j-jBf =qjhLpו@L PVjJtO3I3^ ng*DgRRD[+2?Jbhc\ \CIF\8_˹ z3̶z${%BPdShpu +)) Wh|D)79:r`\BǐŐefC<ϰca^ջT6bsJnYē'i{/8;WzN"遼 "1b}bL2±00,-%dlvgVb:Y! {E?+!y2j?$|7TME[zR HuEԛ)T?ML.p!@Fc(xulH ny|%R%R2e`~ۣкBd9DDCڣ!#w YA@`b'y5B}i̸'ы!91*?uirNpZ_ѪBB8?b|+܎ "t $ϕ 0+uuG#j|)?wzw $''=7TݯMsPBR[c pg[OيPr]v!)|MP!|<q$MFRiG!"c'zFFoAb!: w8ұ"Z6 Qm@##h915e?Z}QI-A'v}5jN=}ZCC}Nv<{w}ufd32^~sC.,rˤ7ٺ7M XpM}+0; ^+c yܨ~gO\Nу7iOv{_YS;=ե~(Ghںkm;qtO<gQBG\4wYj yd"ρ^~Ƙb*X45}xgXblݪZq9Ǜt/OvUxEp 0Uw'&u ݵW RMWcbt"B \`4ё7C kk%+ LϋQ:,[6`y1Xu! HwsJbJ>ֹƅ2{ӗ)~Mj2Uxcm.Y/j$j 1.ŝǸ*א I_ך4jE~Aϴ0tO Iq+Tc9rgfRgѥ{E4/YPtUۡn/j`zRZ]&n~groKn6%O*xY9Om޹f\-k{0% K߷t|gTHa?,"sF[a- ޷lϠ=풾#5z6Gj5' 0:n^\OV(sX Gy{{S =8}/BLC_! )m-VV#! #&1XcA{9 1@I#}_^|[ȎoUɋ}zh)N_={kA5eNt H;zwɣpRx dDgD3j\"i;7 }R( 7TMݘ7F8*hP?wR4;tS1u`0wƻSN~._k+{JőDbzВ8R3]|%8.XٻS};&cNj> %dJ2Ds2fȜy.nSʐC!SBȔyE*m"=]k:}NjYwu^}^szJ"J"O ` S Cy/+a ՐFMu #橰=l48zpҟ@lՋlarΔ >~2dO mҶWc\cSYi3AbzW׳Գb{~nCFNg*>nŢf\' (Q?kXxAò1&\ֈWWޡU\ =hS1ޜFШ*S%[cOzLfrO7¢m8rLiM㥼"֎MmeU&ǟVl0ݏ~U2NLORG쾍'~ γ{>n?|P1!d2 c'&3ZfEoPi 6's6=9$\qf MU %,dhh$'' !_M ZA؈[7= !I';2!'T+)ڧvzwCbCbҍ֋={J?:B>bj~H GQNQnؼ<95u=S5&m*k*KܭS;U|NF7ZeKŁ@~Exˣ,4no+F"" Q9f,||WqFq7NkFl,61, M/NR7s'WUI:rD/޽=¤+<61{&I;Y6'?-NbƙFdcɏ=DmEV;DA<׏WIB1Wvt~dh DkD;~M2=ULl}zo`b2Z;*GahՇ^lm7:<:|5Xv|%g(Lzy/*4ge%+JBIv b(6ʟ@R>DU~>7l a!^*ftʲsRIRLsݼk,WW[}4 GDTR2iIB݃SSz̊7DZ;ebYqQ鵴uwt@R׈ YXҶKB;]0Ԝ曃oo7{RQѦRmle;+xfHǞP8C AQG;4c G~:F+pr OΔc8 8 io10|iOKYDSܹorpK.K.{efxfxkjrnwjϐzDeӻuw]v7BHϚ-+Y$*( 0>䲖E5PkP'Gcm&x@܎NNvh`\q1TnAA H]8 Y1m`> @:89 L98g/bmbl]iI.M^;lS׶ 8B“'o8v !_hmm5]lGQ |-fʜLJ kIw[ hyy(C5ro _4#MN'zu#k1ZLb!o$Y!9Nq]حݿf _6A멢&F?F &@ a8 lZnMvmk_$onB;S lh[@~3u 6ieCgH!s5(9a@9 ΋$5H=kBdHS;hmdf-pV\{9W3K9V?r9ѱ =!Mw*$PgO=^00KUI3|-f>泩LCu{ތ?5麛` 1HY ]?&DLiK|E3>}!Aoe? K&MC#%l!qUhҵKmv8$R ;zƖƥȘd1K7$85a=Pol)g&p[T9,Bv:-mxy_,+0nmПMr#V{-g# ʫO@fq5)8&''X@ֺz[aީ%jdճ_ӣޱnשN9S#O4PS{p"> 3)"rfa7.6eBF9)8hRK.0~( VV_ע;0"=0Įod@v ?g>,IA{z8jr`1uC.ϽJ9W$bhJƻ׍m?Wʅ$.pu,u,N1r+!5z7SfSf[e;ϻ+)}ګ"v3|?sNB쓗4?:imQ>dKuhNkV.!1a32TKHP1DK}nvd;ٿ+d(hr !JוxGgqҞ$ߟV)sw'S3}g/1)fg% a+|ҭAbhtϿ:+\H%rZ\RAI62GdZ14<~Ux}QH0&.JQ C+Ѹ̺*y?cRqݡ,]FvH!ULTLEt``PE#5J8B,[Vi/:_4qT4ly"V"I@>GN0Wv.OՔ~G}qVqβlZBFg]^'  ~,n.ii%MRN0vL{Y{1WrN!]Rd21‰$U] נ@< Nw+.# @1o@0 xJ3> :@5lrz9/Lb|lf|t7O!.m-Y-jAA={l^=T5Q ((|OB@Z3?l_5}`YHfWryŭ !}j߅\J'`[W]-#%To^h߮7h .kGFAWI.4z̙x{\{ˡP'>,t{ְaDpe/ذ!4Sm'whh܀tAzZz !iV#h;`QfO"@r]7@y>:l[ZFd56+3SSS{2eܫwwHkXЅEeEeE=HLD l.4x%]kufԔ>zL{#N|D WbdOCw9p:֩)ؔL1t"yodC?־BHU!d]3@qUhj|JU/eK3<`msGsOk )]T)5j%ag%y5曼$da]pA$A$E&&Y!Y!\8< &9QlO]>nv*l6G/F-B>B\%7B?Fcy]1\O3%i bvL3NeRa~BO1PhLF-bNn>f$n&d\r@x<Z`7G+O+|J|:˘ og+ u4|5?W]4XmBRBRt}5w<Ù2ߕ_&k,kPQNdK\Uɜu8B.CQ)ɫq1|h'gm' <=󞂿rsҘSeM I&,κd{)QfrN'3&_Ը%W%4xt'n٫6o>(bH.Ncگg^^.N'AHx'ҏ &޶K\5Ѝ.E"-JAQ{H~c-ZrrVncrc.l\"}7[m"Fd0R"N(E~otVq+}h`EV+ w:6fLʱcwD <Pxt~p#%B: I91TG S  ҹ<9ϕQ5Q~oX,RqIT|JẍYE!I,pͦͦEvwTTwҗ!i?)4/pA!1#)Qs wV޼!OӥĶx'N=;Owty_Eɝ&e UQqQqa5a(|sƔL֤/n{笶%>QLد=jߚDrIw:I2y[o1ӱp- T"cQmwۮ]OՖש붂Á~5+mƤK}8* A'ȫkvC(R8|o <6070gz.i6V* ڣhGԟ }&ڛXA!A}h.[(<\o^X9WOZsB8SyVA|.8 KN``vȀuad99&&^22Pb&)45}i߮S OJ)6axbɏ)/iŧ/,Kp([)>W7ԩ@(l%%]=Ű@[ ?zUL7knGMMbs%[r_G4,HܹpF*/ (>W4')JW􆌘s[͊uuQ69}}ll쵠?(tC#̉\I EUR-S3<){iخS1X. RDxf(nO\j}ި' On*ƛ|MWW[İZC`ӏjdVk!4eɤܥ 1u4}d[FSQoYO_ %QxB/s>?.&I7c?wNU^RI#fc/^F~BQy"=11.Q? gfph11IT|7@:S)^Od=9hƍ7ja/s=hYkվ(U?s]lV $1Ilkw#u=hUsA|E!1z#0y$}܋EԂr3vNM6L6T@#!J2d'1. ? 5ҡ4N.&( DʐS/d梹}X\1j)˖j383Ѻ/;n/ƶ.+0 uE"b !'r!JTp#??9 Tܽ%_AgKR|g/;U TEK#gnNFXGQOQZ~f lypͪͪsLsLnO3@wbƖOB-r}{=V35kL *C3NJlkBUN/O`  ]ц5r j.`?1寢}A_Zd}a)۱G㜆C6ѦEB&Q:ٯ…N?b0.ڟ;,t\aS]΢mi )-"Vj ybA1zhY^|k.BV,HAFy]Qzv{ɟ8M*||%Oή<]q/SޒKz^}BP3/ ' !RwۿFv YU쮚=\t n8$aN.l)"٧uB"60҇@XLdn)^^y)\X OxxxWI1I裍Ι!VV [~Ȏ0UٯF?eqL%G0= 2j)uQ8WmhZ^iQZ>xh̸5љ}65Ȼh2G\^2\0 "1y)Ai̜!RѷrH$I$俄`Dȕf\iyM.@rɆk֕ݯIȈH-w[:T前h2i 7=F=\r速Jo%0hw1$>iJQRJ 7<<%&$Yiq. hdCY@ گnr%. 5gZJ</(_tۜga#m#5oN\{;X D$D]p||zgrK~Pu/Lq_3Q!jZKRڰ p]/7{ga {ћ]Gs=]M!L7|]-'RjO < Dw)즔S0XJ;vϱbq|7ЌXn2?5nMҙ~W&=+sf&;%=S ~[˿llBc{H=Q:b5:BfI鱓ǘlzPMlSv'|{7!բ<:}Oo+KW8#}IxyĩbHG&[{.χWUɩ'+n`M.a3ZUaae#J?a (ش_lB)?N`}P:L+4>!Ջi'?/ Cd% 6sCdd"d[莃/QP\x2r/Eh5_W$aczg )Uollo#Y#YJJL!r7 { qqsE"D"Dngv]]xx8.fïd GmUV:٥e zzEb֗9iVlrεaNW|\rwhe0 fRAE12\l8O!CbM - -f!?!Ff ? ;P^@W^Y\ Ar^ ~}~jEۇUxlS6'+æ5\TMTϺ}'Q{l3ZsZsksks;Ug<5Ӟ^ <㸝di edHJJ=[FH+ED-KȒ%k}TEC2vo_߽=w<{:3a_'ݻl?72ko\Ɋ˲wӞPK"?)ulYJgh?m/鱪g]6-e#m56-ٞ>kpI[:c ߊū s⓴OQ_:OfJUa?^G[0}?p!diiΘKX|}V?A:MzX[iil'2~B: Y!S#w  K ;RS=M'$9Q6lvR(சӧ5_5鰴µ""%%uY6هO8Pu[[`ttmU}ƷufvY`65kSpX_ɞXGt(]w\ CgiUnb4M[%DlݜIfƢ@r;ƭp4F/JsέB8nWH@&!(DGkR6KBWBjPHi~>!#ǮH_ ]`͋tnki1>)Rb 2<÷ic j A#-Y Y͉|N 㮀u6G[_jIn{G1.pi=Z5&J7lc=nޏ7hm3ΤuF\Q`OXmk٪ <߅ ,Ix8Nlj?5:1g3y-Pb#}inihuX)mLB, R]LS{դ)g~R/E[ܧ@6%^*C_ٗ=HC5: S2ӿ2@yF~5!\q5Ѧc^:}2 ~K1 )@]SF?w& 7 ϫ4@v= $K.YB{2Rd8l(fu1:²./Cn ɵ^Y=PCxB5*1' /›4o=I\%KyVGKř]4 3 !rDk'.n99iGnS<;2"zOOV|3XVtawSc圬悓~ SOij;>.>~#\ 0,@M'rtx([M ;^`pF0>@_Κ*:{з&JRR!@+iV*BNH+߸:8٘ Gҍ[Cnmo-\ම$$6Y6Y GCnL3".(XU,.jTFYyDӻ}OצT-8wW 3|:$Wُ9}I q&kޫLHhB̍-3+3!4`y X4I-@TdIHޑfț& *w@Z6p}I9#@ؚ8G!jSoh&in?wwr)dkgnqc^[)0 ҄RU[5jXf*U3Fk;x!L׊ Y;cC+F̩Ƃs59,.>=Ƨxɺe, F6Wxgl[D%ȷ <ǵm{n[ƌ1jO}O0tu(8:iw;@W FX%!KҺnl=2G l {6Q:pOT =$4d  +":%Uh))6?-NmPl;阀8CHdǭEIC57)L("i"iwwe'lyIw OzCzBz  =v㝱Uu=|ܧD"pMڔly2b07H?PDFNwf%j4a <\9LPY%S̈!@?4j'3(o}}qHͅh//048NȾ{egrr2;) t0љ4KRs>NN.[K%ru&MVބTCĖZ1Y1Qgdψz۷<ٕ5iz89^h=sE2?n37-ύ~ 6bh<Ꙛh"RɎWJFJ1O^2xs[omHtũMP<̔1qTJj kk,j@l_iHuR$&m -+gww\2>sHy_hr3޳}ﵿȅ]hݟ\uxT@g`4Fl[lw2-ʹeb "gB/:OQG3z7nke8dힼ?,칬7.~bS|mym}S?UT#.4@PL!wz;!/vG-جtr,sin 5%5d \}}Ao6 +E++/w~s8ύVee-Ḃ-EEn&gT9I\NWFKE,iv]bbi}KLFf4`m}Έuke vi7Ɠ ֯uB]+N}n=1Wa,INbs¥^hb0"AxfԓYp*yzzZ<9 A7O[T Z Т)lRҫ  # }bHj+!k]^wL0~.aCS }|22AN g.l.LQn- v9DE>q~5ݦP6%]ӈǎcQae|*2:|^@Nrxy0o&!U@"z[hhibK?S}M ~9qrr΁x@[Bx-#M@̄I2!pm@(|:rc.[!i~]1ڍqR]jlS"NNT+Oq~C=7e¬M 1SA\Vss)"8[z܇oW3qx;j*ஜdCWOW"771L4s\kbl2'ud&߫83O7mc{ɻ?9\)P5) ahm#߫;2jp<ؗ<"{RvK-K=D";c/S!HY+k⩐QBl޻;{~>pu_}0-MizFГUkg]<4 1*1c<@hd!}|_#W2~`rm-n_،_J}xpaBAfp5~d,|MqxIw G ;6,b8<<Ϛ4V0|* ɾ(+tạ̇9iy-X7눼'<6ѡ?j;p8e]sB'%aOFdd11xnV4?##dOmd RNݴ~rUR uorN-w|9?O8/\nsl%t:G:aMh:kP6HR<p-qg8lFX-R@ 4ѩS;>C=Rz!k'l$bM)ރ~ڔ^"bGoV4_=a;1_VجP3q224H4HHKw! 9)}`44wx݉eq1VtIyW+==k& фvuA_DպmoܯU8W]494jchbuZf!c!q;IP 'щ<R /Fo 枏 N7߬]zZ.ɩ;ѝ^v[- Q@B2U 7 N,YbN(hd[ZRǐ3y'@r;}V{)RA@l; (]ktÏ6 Vȉ~Ǵt47/1L:bԇh7f`0&r r@ȾSU pNNmҐPa׍ll3n,ܸ璭dtlyC7zD꼅^ q:PCI !v[@S4E y@ΟKq:|2S2Sf(Gbj#6##z\HV4 BPُ0txH} g[0'7т0wv$giͥcxn {VWML0!.3玽sZi);p66m;»غب$N90BR::pq}bʻH}i38n8HMz\mne9Y2tTPLZb CCdVG|[|ǟEmeYJF-V><.!ud[κ϶4<Ƥ`F^ y=-5:.:/6lVe_/OSR<Оe{F6X~+YYŹ+tCXU2~|caSL][ٿV9A{fze^ۆ0}A> |2,|i;~29|2Zf+=jOcjiHT*3`\,VYNkȪ;3 /Y=_f:Ho7H h:_+ՊW:6I.9zl/:VyA]䝳Tݯ!pKy8IW{k2</1_ctPv_)YY K\%)o/'vK< XR>ݙ吔"sc Y6Xʓ:V U#x`IS&˄s\>#!Z.U3WGRTM ~vmaM)X)xeCNWwnΉ_FHspSBSB>#Wr(-;YBrYU$l)}2dX6NQZJzhI[ [pmc$]."S`w-5|fqʇdP`6Ca|t8XLZ@ W1# hx֩[̐$ n{*$ 湲S H]!?I]G1\E (l8ߗXSPN18NFF9/5l\>qrsrWUFOvK`;xamJHگ!7ꔮz_]NVݣI͔B; ANY47Fjq$螥8;F0m'J>;@>Q4|d.8i>XIQ$Wòrv{ݐ9=ȗߪ;!} sXu,zZoq#FѳL8nd^VoU{Z@7^F ee|LCH~U\ra|" :);)Yh"3ǎt\mU$Ǧ)Nմk={.6 J"\w+gB}yo4\&&75O.>5Z|qqV:rxY1¶BwKR*Ax\<}K c @8ɚt[kJ"FvWjkz|ˤ͟6, i4u-@F I H # V r( hhHݡ #=&N&pl)N}Tܩrlтdx1|'xVB1)' ,N(01u@>u|kk }i<ƪBZ!g(N6yr dADwYPALa}eHaM;b mY | \iS;1@e/9?nlLfrrqPvˡcc)#"%R #g?d?4%VXx.0<0 %L-`ʳ!KToiԍcC$B.~J,^@@UVjD."bwe1O5 x ѧI+%~G0@$㠱\2}2}?ػp(ed,}y{e($kֲx5 )Ke"s͙{:Ϲܯ{;瞏nPփ #E%.p/'#W+qwjf7Ϧ<ם.%:E)ԳpA4Lv>P WS^4@0@(P꧀/-[l}}֛a{AM4 'z2V/WC#BD7f WBNFZ@V(ڴj"D@1n?w|h>Xope+-'s,B1JuԯFF[d~e=BqoӦee?Н f:>p_ I۽?;7Q3 vg]/TFP:/.?daXtuN>p1\~e.~F_ߍ!kך׭qlQ:?VCv]i5oK|z$%ۧ2WAXy80v!&Ѧ:@HeX ˆ/V.ŧ}Z  G\r[l‰,hw>wCH| # udChf?I0b>+pxH/U<ʆvU|ǜ]m Y[*W! \[T[$$q dv9B``)dQQ˯s} q/{tDmש]XJ)ԉq!d&.ĀIDSR%8 9)8RxLOµl$675752~Yl/1\ VBc(]ZoN9bfbfsKi%w?@xPO?(^Ozn饠kB6"=cٷDʒQ lrr(]4WpBq}쭇``y`so 5;etঃ&m°(6:E٩{}paz$^-I\7dNƺB5˜'$E&XqL9=6X WhaRsWp~)*^$$(A-!{vZ1%[|F{djT Vhz~|ع}eiVbf*N&*XcA^$$LNHA"A͹8\#lp(j1\\#% %KY|&J#-g"h^j95>ts#n/,TL3QG<ϻ~luj=s_FL_4zEc"\qOtye@n,^>>>CsbvQ7I`ֈJY[[NDMSmUm]e_e E:55``qbJ  RN$DvԲE AoܡᱫW'pݐ >TgjFF.̙T:ƹck~D`$69ƥj/BħVx; )p@J"j4ΉP*!+>zDm=Whҟ|pY.)ŕj:1U uVDZ?ݰQ"/b#hoAc?~NgiBO]! & v,Af&>AnpE#zk͔lHvʧ?BT%EĽǧVIgUr%PD،҇K)",<܂n`P;`0/ib55.M9m$F!/4 ?>meݜ}' {\!9#Q)XOl|"Gʰ!”A|swnW~>4}xxkKβff'[ ק ̭8O9m"Gܚnl$=% TC Bsǎ l)N *vT8iDtf<.Tp[axGӧ!\ 쀐 Rg#F|(>]~/fevl++4>k-hk1=!oEfP3',!DH RֵFV7 HO;;}~p<%[#Y^|ȼJTSjIQ!ʭs Gk3vqtq`$ěVdd_x&L߉NTL /g\K\9L$ٽgl i3f=iy"8!=)LUd;ٟyI;e>H5GEt.Xh$+~GƏNY}gy”"E{eeX+GsԮp4;NW}_R8~ACFhhC "5AH|חJOÄܒû!;iz ;UiW(`=㭥sٳ&(\.W.8gvDIiTY? E}}Ԙva0%rvޔwOch{6Ha?/sTSBNԂ[Q5\@CE)8Gw1n$+)9˾+֝f+ M8]6pWoqMHE5e*VU Hn#įa*lެ6&Ja[Fg%dl\&$|knb>>8&&Ծ-Jr=ۋ\dC66{BVCQqPq4Zv(VxabH3NHј)8D($Ki#E/^3dPsÿ#5XxBzh\HHҔ)KLb!=LVN5ݴA( %j $B NBvg~6~דnW֑h4n%2q,(HY$h.9u9gɆ]\g~``YxS%IRSlfSp Gpn]Ynnم$q)nx1f  hgfBV4|YQў CܯW?B)L>! 9'n}@W !&2[6RubI >Zl_lu^q8 ) )OxL!Hp 'ńń|<|<7NS*~L]SM]n "MGP~%-d,M"_ʩi`<]!V NTYBoJXMo;.Jo#]ə I_/dd:ux7Rr3kO(uOo|)ba0٤ ̒OzSdTDέ)w܊zJO fas,߃N3CbN!ikioi_uJ(ꬓMGnll|{ \hA?NgrC=^+(՞@SUS}ubW٩ <_i{9b<ܫE%/jGմ@*U ]B~<ed}RʷX.PH|$ OSNFKH\Nb&s}"CӇj,H 6[SțΛޑp\K՗"$!NH]u]bX݀s+!?w؟APalYwk頲tiTtLZ7s4'hH ~|Ó q9Di'O%Dk4R23e^4vvMqn6U88aqŹ!{u&]NjZIQ@";elƚ%|3F-p\]wAUi5]ձq 7 <!d'.@(=`QN8>U&)z۔O:k9-_QU)tbۀ.]2 pzzSO{(@h /5XX}ځq3\%P63_ڽxXc0?m[sR @S7ayZSgޅ]fHxf9zq(q"gs|8XsOQ )LO~x{-{-KA: CF4ocQJV=B%ً&3ޮc;RmɂcjUGGtRޗT6P,P,2oQQ6OqMYX:Rhaxx*G*Ǔ)(.Pφ7e>8xîfRBnfSDpfJ:ɩ49nȎHaf|@땊K_Z !f<:|m+m+'=z u`89Y}ڡ6l/HK:C=Hr#$1`U ɂ,!v|?~Hdڪn$\n|i,}$٘eo߆\G:T$(Eepe0]/|kV^1] qo{~fSgE(ϐ8*ZۨeEr~J넛FRпѾn%b߀:[.#G&"nRᓋu'\TfC-macۙoś5XZC[*8x/Z2VĿ$JȐoR0{!Ɩ'7LA] USɉ ߑ5DiMm`W9}7nG~}DZ+%l!5`7yԿߑbGw|*vu'FtYk{rWƭ8e)]_afHjhrOk8Zz,Gb+Q/~9t N^J`R񭍉qdΦCEOXZ I@CȭÂk;顶g-<lC!o1+ˏ1n[kdL 6bIaIn"-Zaa(99ѡko*md; vǡ[k4%Ew .LW*@1~{Wg 2Yd.!\y2$6ԖTfL-+S!!L!F(T)}uֳׇs]ֺ:0CĽ  (\d\rfHxFNRv׼MReNqnwʞΐLMu6Jc6 VN l)g>6E.Eb(:R1ׄzf.LhQsdODAk44 fHԽ:HkRߩ9``M!8ζl*.Jd9sR];s)PPm2H""xѽ!6ˆ-C.fffVx85k-DN~;TbS@lpmwj% .!+\y7F5O)*)[5s KO| Ehalx=gJҗ1nnmu/-b+ ܣ녨stG OQ|i(]RDKF`@{A¥@݅\8O^ +u WƤ'YJ(ٛJzw;77?\*MM m8Y 9l,*Dzrr1:N;N;Pǽ6O,yt+%[x32y$=3DmH;f򑭹e{B0sROG53Y~JKh/ \T}`nك5xD絒_06Io m|K`8KV.omn/d=b& x%ķ}Lx,j(hZ)lLVjh@l ɫ)&_:΍b}dt^e1 \FG^+|$}Nc#p_/rc;_Sv~Iz. rHq΍>^de@g_4Bk@n0rUg[2Q2Q>"wjb8Bs8KE.KZ@d U^xV|Wɮ kޞO QҦ ,{j4 {SSr>QM@Y8;/wlg\Plb_dԟ!".nyQ80dS쐔O=!נ#exƟƟOTIVPɱRuv4$Kbg %䭟 '~!`}g_PDnr @̴w2%..x8 8 Bt1Y}EQnwΥ?yT/ `kẻk@Md֛K^.o75x36s52#--|Yc_,_ii?! V[II _c|5&dfLLn@b"-];xtq'1$2ޭSǷ;zDO 6F~IR%2u7l q GҡԮmNqhKQn8^Ff[L::+b2CkGYjgTjfGPWWn4BJ!^kd0zPlLAג2J$rwNϨw|) Q=3eOٱڱ11T5NwBb ;GIJIǃiJkei y\N vmؒ1 Ƞ ڣiv yBh"JhhA:-! 9 y -0ʼn=M'qj%W-劈n^*}pK;۸% rKߋ7(Q$QBRnCR Gu겑#M+MdI%S%}6+jKW-%/?JF"iZ+ /[% A{\P6M"k Z9.6\@.._ZWB>LH41409jyĽE:.Q.QU Dz-DWi x=gtD !X\Θ=4Zw`۾]k~hq pp0- -Mј\:ьə;\XX^rr8{TT Wn,S7$;JRz^wSin~p!hY^  ¿gSZO,8gkNg}C.)YObUTPWv1K=zwetA I2ه?WÔ)κEc>L~A:lJ7]YfN4*9O;iT\a>̆U2F##-<ɁC#֍.ߤ _< WwiprrƜ66y^bn%){/)NssDҫ#ۻGUHr…~lסZkN""p) 04z%LT?dQ.fQB&fs@v,)[#G{N4t#H@jFSYNak8<'W\ĭ)e/Sq;\ ,?Mbĸ3<򮇤Hep(;)x:7W\Unԟt?(G[t)NN9xrJ_e%Ł-*{j: ؘoc;E2ڏ$ \RiÝ33_ Ub@wB%w6~‡\rt$qʇ韇֘FӸAG=Z=\u͔'MQfNp[Fq ]@q,r6~~TZ:a*^UםGzN lw*.by5%sLM)l XDlfϮ忡 Z qQxnx;bܺ0i i:[_|@A?'$]]9h "T c(L,<.%HM閃qqXuVcmBQ@Ț)uupu)1yWT ߼r}7R i>梕ԧ-O[\TP:%:w*PUbZng>sf+h$;}/M{ٿ'LI#a=>,]PwTL/b#eo{wf]e]qUeifÏ [#r]Z_elhht-ǏFGj~C{>)ZHl:712+uv|>fr9ʖOM~QYSu/V~}Ub7vM8)E+^~1{2C<=݉C"{^vM™U $voz ]dM\&ɸ T(qό!U-_!ӣb"`Nj5Ҡvy;i-KD+nն*$XB_6s11*& c GCîˮK. 7PIhq~yyrDbd!r< ;K*)b7z%2yY0 0^w1('Bs À#N՟pj[>"/< #{{vFܱRqAtV110\t6 BZ&L q 0IJ܉(1`P@̵(cBÒcuAi\Y:&;;[ʾPi g;l;Q4D@r, uH!Iڳڗ;z%[I.i.8ڢǐ T89U,U?7J1n8fRZ\ѽvӥ9uƌ.)`D|_f$p3P 3Pt_KV`26zו%"¹[7ݐZ8J(ѥ`7J3OH~{ `UAjÄ1 )-)Z&tEX?܀_oD=">#> i"r2wЧ ԟ5`QCj.+Mt;'e~ƒk7*oLL {R8J %PPìV!5+vYY^ȯ_#;%0\nC9E7ԑNU"{>rQKKrĤbPn `fo??)1pˍUNqW}wb5-;|Q8b8?<Ղ@P՟^ >;e Eq>B2o}yA@x{jb܀ Az{D<8+W6Xs|cmI1$M0MdRT\A~AŴVn@(} i<*LL8ЖіQ Eb ѴwS<&aCΉRYu a6z wጋ&NM8NBN$FPO @Mb"1NKqp>LBE_M i&5(Wy7r_yky U0Agb4&ڏ6-yDt<1،b#A27iؙayvb*P(^O2O2n=ޟtBžrcVW4=VN1/[HxŖӅp)IGw8K8rVH酿b LmH:B ׀2T`+'NǕ/Y9”Ôs/hQQJ01 k EKZZ ">1`A1ԣ6bOe>m'B;-ɩ e.3Qlh>Pg7㰑xj5^ \&u&՘ˢbbb-=/<1Au$fAMpԦ;9RBOӌe3Nl$/Tܕ63 P"dLf}`Qk#&M{q|+;S|B ~BYwP8pT8D-Q >2% d6bq>9JJ9bJy9\^ra$°{&yUsUsNjڙehlfJHo' N;@;@䎈qhhUFS1PN X z=tOA^t(w,ʮ,1CUQ=^ k[rv`_娂]n[. F^*!1v\zm݌?9P2Pv6>}5-ɴ{&8Šڙҙ—T M}ʶdtnha9rD L I 8M{+t $KԤ$Y*2VD|p}l+Ifd2BIٯC}S0ss:= H5a ЁM;rRs,-&}BB*d|E!$ cRhdho!Z![Y=[},Y3yf׹~g\y}]=sBu52]:-QNSS~%(:y>j=j=7i3xFhiiiyÏ!!~!Hgg)~ji؃lEVB?Qc}V{pO&_tM͠Wd܄&ksj7w Sk`]=H80049\R,. b746~JKIKy%i?Rf]jȳ5o4,OvG=ĴcW{ف7 j| 4t_˟T3h=@M?ϸv:BIism̏ qԘM8׼Y^ߣ?3Su# \~b#K:KD/[Cz+i;2C݇ NK,Q?FN[kF~!lvffdOD)7r 2M{h@\9Qvi*}Ye.ͮGlZeHzc*GP9w.pӪW ?{3_ﭔ䏕7l2cE0a[8VC>k1W||qB.i|3%Xv>P8Xn_q21kI} 7yk?o ן̑̑!Tʃ 0"*ПK Q'^{.*v:|#ryh©I$ .G#*1ќg3c?˧Q$P$U]4Z4bwj-GKW-f0\RˣX_w[]QO]hDjxv?Wl *n?^ԅu ! @^C؇22{'F p1$RN_nʣ~drTOWc(`}DHh 4^bCr/H<6Z.aGEE.xzz22ѤR%EHpS"  L!sxLٻPÒl [uJ~S<+ԔpDX/[L1 *{wD7B 4Op*`~LȢ+2tt|P \w#0bS_3%!;'c3jԶԎ@&h 5U3@'l6sSJT՛9R.32+0NBo4v}.0vPns "NNmL5L57`n5{ *9qiiLxzߪb^™zyN)ltJ"N ;C~, l3OhRm2=7GmDVXZ>儼/ d322J,)HUZ>Gv^---Gr?HFr$\=JU1qJ-h!>]2!${k# ѷ4Xd~tnTrzSW%pϼyebr|_΄sk'oklBoW;qq$r$.녥) ~dcWW'J<ӀrQwЬUmV]?u2Y~Ub]žl;|N/FHB%S@ոԬO \j#I  #_] LmjJ~ܫij3BYɧ+=~j:)іZ! ӕZO>.N/Il͉eerh 2 JJsG*AT]72f]iWi--|dީ'>^pR~rN)ntp@, ˶(|=@;ߨAil~`Boym .o o`.Y2;=/3]cwI/Q9Uޫ$0ij_o51,E`@a>!M蘘b.F(Bjݎ% k GfGGd6CgWUHìY%c$c>Y-TA95XeÚZZa|N09x,Y"Ne+NiVLM`ܪSJcjܮj]O=~"׎ķ+]NĤTvKN1B$>hq]R\GV"aP&!u(R>8ᒆJ zbl@dդ!UԎ(o_HHd~daB^#?$`am3Z^O]b2ۜ&BULQK/y^J+=47;h~l$L14Ak_mT=hfyȋY2nn;KWs8};'2vͪY Ev ®t>[%Nk)c }ٟYdwaƸ&P-5N8]F%6C_P58@o;+PԴoEiDF([[K3DXO}@v̤&ݧ;,k} kt9T&_0tãve&<楩U  fj)qun~' G&+mgٟѴ;dbʎqȒl L˽I}n 7uZ1_DGO݃a3l3Rix#Qws$? 0oº 6_} .Y[= g(+pJcƊ<2(x%.pw[*.X1K,s.HKޕQ1O­­E@\֒< 37ajp/!.^[ 3wȨW弥RT%|6~,I$DWxWx(KJL288k>燯"QFvuRҢSy!N5\fן2,UT]ISpi|X'(.)"Pqμ Ǵfy+X˅ h jqIKWu *A~B![!ZFTH69^ p8 cu~z"\/b^ː}Ib k?|Jڻ8.2]^&%_pŅŅR((} rG;Ƴeᦔ}vNntZP{"X% 0~ۃaxkp#w8Ic fъu=ػp#B eyQ4M){t(dJD #(BRs?uܿ;^?8|'97,flּVC-Wcm3Ȏz~Z,IKKMIC>AOZ@]u$O%>nI' *QQl)Laz"A3344?WVV(S(W륆5dRR!pJ%8FZ]sSq2;3xÙ+mE;\Sa@ܲ [O2g1y 9n<26q' Ē1ZоŊ$ovƐ{k GlBCQHȼ;%3Jm?П b,7zG5quwEj V؞Bt\BB>c>#Bz=FWK\WDEO..994\ AuV;޻Tq\&7FBq)9drn\LQ[{µDsMͣj""¦{ & ل )mζ7? tS˃8:zGQ@U>| H3 R|X'sMO>ȾSLB{{2*˨vvq$s$UWA^mPNaMaAA& ߟJyb,b߳ޟ0.<1ľkz`eaEI'P@cv~56JN77]![0Q \nOW<]Fk2 P<)@{hjخqmL /W}a^i*ea Nj%ڞ4O$O _ZpGt-^ Tp95\SWߗ+D7 F ]MQmPNMm5e/>7ہq}8Y˂gX.v4u麗7@#Q]Bne 6?60ɧ`(YTYw_B1^-Y.Y>S8 &ix|Ic!ٚMԂc)8\L$dzj"xݳ**do |#fʳg>c!OjhuvNʔKKm.7 |ƄxDNM!\#n2ňq>1uI%5[|pG{|5cs`R,`x#M>ka=z@$$@ h][E+I|AAIB"nh J)vc:Nc*V"6X8OY%;r♩,=::0~/z[8M1kn.\rE:[A[ zS鍰&lvjJ)v s-f2N7Zr܁Z+=O>}ନ )8Ft>*n;2W0WQ>@@ZT?$.[npLL**d M] LV ik"D3i ?J/h&Tr/̦ϙ]3"{hV'PuGFzh8YzwwfZ(2/Q\zd"3=&Jo.i/nX g2}o&Q_F:Z1+VlNIX2Z2;\Ei3h"2Z¿zo,'ˡRO\/kj*OUDQԝBf[隂J Xw~ ]JmBЗ;,rT:3眭<Ͳz=Sgg˚Z^d vzn1znR#PrHKN<1`^Y[^2km5 E`(X'O&8Я(T1rhߴEϦ+XYC! @FkX! yW>@'C4$Y-|<(&"}S+phI؞!KnqGp||)9)9'sL!%TEGFG.&^`K7;c}NMn5U4GdsmUh.GW( \c+M@ %!@Ay>$]T4t5t%6Qᰀ68e _15 l7ffV0ۿ޿PvW@{qr;bh_LLӤ ĊbP\Q;ԏvJ=4bw/b|xY[8gMDU=ڞG<'#'#ǝCp11_j"0nB>GLLs2Xαnn9TÛqYm;ޜSS[M!EF5cw-W1e5Qk)\Em_Ǡ%#sd.V-,ٌRI[8GZonׅ3"^^!&/@7+@Õ9Hvf/ݖ\ y]Ɉ3 #rpP+j!";_h;%)s).e6eVߺ\Or٥]i֥7eT%XT7iSS&`/Aw#NT}$s_I\ %@UQ.ao?߫b.~ɆOx,XΦ7 ` 2oΏg/BOC * UhF*Lnv{SXB-™]Zz8o}J9.s]k3bTTq II  ( (2(DL"4C5CǯVP2v]Qޔ[y? 8נĊOO{,e1$YIfV"+qA*y0=?y?:z;3bnX}8S[ǭ%qQc&W'2`x~Ϣ4 6]@9^3T@U?mmG+,h 6HƢ!g!QYR'<PX/ FR6'XLI S6WEBVB6OuRplN4l4l+׀@iLGPss"#K d_qWl) opťL.BH|pmcϤ4L~5tg|L°zw >OUo]@G0bvZ;]`{OLHA߼F %U|gvB5lĤ$#dC4 (ƃ72%؞˓"wEp~U,|[A۸=,,, u'LMMÛR$OB%U}ک) }Z׆0׈cݍr=p=T.|WKp C;}C>z! l-o[0ef!v\W||20)>>8 1L@,/v"6t7ZG>i@cud!T|9KL٤\̒w(E [ol Td{wI !!]ECYHc\\Lpyy+H}YN7xSy#F#52tzXwjb9ubΣ|>#Gv-Yʚ}-[K,PdM%٢ME )۔s=w=9y~_1xszu0Qq+jt&fp}5M4!7F;.1?8ՙLOSSE:Hp0`<hBHt j NuJ5p -1@FgF}r3Qaʣ3b0J._n,!{;bVg@3&1&1pQ17Vy1;Jk<ּA^׳BxxDMGgJ(Yl;u\{vͶ;a/e愤-bSn@H$Nb:5}-L1_ɋ*}c}IT'I1PPO@d r*˒(~ K Fk!?X!>4tRh9jƔɫXXbU8_FFR{!Ƅ4MM'q82^~VWlvz2 /-øD3N"kP2 Ƚ5RhZgw#l+(sUa\ngLPF5;LնxIã:gV9d'OmwޮԺ f>5zщFō!ʌ[V?n=Wh粙aV(z3D3{uϟ {iwle^QKR=eO4rFDF ךg^WeSVS$fZ*;Yo/\%>d3-H祟By=< $$+<+<;7ZBw?ÍKstPFNU/!TdI0z˳Yl;5־7\)R>ŸURR9u3}gus58Ǻ$)IWJZJp>#KX{mϚ*̇ <4QNyN:O $#|hhJúP vd5W _^@*2Cݙ̼N*M{gv0mlbMRRvfFE.Z1Zߓ삂:P2&5gϖjD2/_ G6 Zo~٬SvyJwU_Qm#~LxqKeLp*0)R \d4)BFLcPY5~~(jK N$.ݵ[c fMÉ %! !&"Zf^%^6@ .tZEbs{ZS 4R%c;൱Rtt5c[ثص8i{K( 1 Ym 5*ll0Svm]rh6[GڬS띪.J+1$r?]v/jg'+W㚄# Zۄ(1{l** /s:{:,9Th-2-@*OA<'Ubw@%bLys<ѕ;QRDjLZG00ρ((P5.wÅن.!`V#8;eIɟrVdVdGD jZulrd.ku . Nuڨb$B4YRx-FHe~v80k${y㍬;–}JGG?&#IVXMCy7R-DQ9|C4C$M%NIjm{8?'/wwT*iU\I#tҽ8*C>Lh^p+0`g+;b>@@NOJkq8u "C沌e ;Y!wPA>$c 2~b&6'rsBH#3~5(Ghj# sW: 'sm4CvƽB[M>}4曀U&29wxa~QpxXCTS 0}73ZkuqSaIp+ʚ8o S풜jё'ZX 'qP,v] t1k ¹l>O k ̈́ tu{SII^<&Q4VH*JFTU_@Ks8$儠_ŧApsG7>^9^ź:)5B=SpJ)e)q\ܮxl|+ϲ9 }Y;%([ex9PBhUI.k9>BcOkcx-JKT8rbbK5b gu}+>K/Q./L\,Hx`:Z4*!r_|A ZrrꍸU]{87;{RRzz&h(*;;yP)L*L*#$#$#l{4|^w9狌~ۗo"'wA9$qϤH[]u/,s4n4On=x<򽋬YW{VYOw=N]'g2#R/B〜]"w*gn#'&82-ĈjttYRJzw4N],4Ye R .d0/ ]c0{3eڴΤ$ļ Ԑv}kN.Cdyn}fhi9y _2_2B;rP''GsL#?Lѡ=4ZUD~kBFueSaQ|p6s_vXqET. 7`T\#>ӡ`n5p\%mE ļd#G~ )8T&<`ŠPf |3Pv6ssB1AL?<ܫu-ȑ#% h* R%OA,1=y]Q־^slz8Q4[aYVcc&%&%~q.+݋7~gʐ\.[-kѵZh_#_x8IӮhOY\;Ֆgϛ]8D ]@X_cEk-BQ MЏ@i8Mp| .ІK0~ ,J9]u:l1H`G h{ ‚aK)P UmH-ۑ@COV駇kg .!EB4 27gbl1G;u_. Qޟ[#/ˌKwޝS}7e( I$DP!lCRc(ʔM`d8Hb1Ce,C$3ic(Sɔk_{o?y{{#w9_Le4=&br@j)/6H;uz-H\~CU.0V.I"h.;|HӴ  @wENh%2 !ȘPZkp]fTyzv$C/!,A 0?WQr&0N%5T[$HIN¥hF-"FoVf}DBWPMN9yUճ?NẢBo2!E$F$15}=J-٩O"yjR-g7[S:c)w3I昫'8u$o_G`Զ[NK`JNR4dYД z`E+94G+@d$zfy#?wyAAܜ1 1u;B^RBm@=#?%-Z# JŹ+x AQIF%|d*xA_zee(?sQzi:d:+5Al٫{zJ2ăŃ:;Sp\-KÌ_^mv8Xf'vbO*!+E!r䙓j{Aed;؈<)'3Kk3ZfyO:U%q0ee;"6v6U2;}4b\8[| =3~q|ֶxŅz^K3ð(g6`[l-̥I"K_({k][lu3QXZ'qK47q./xأK][[`V7 鴖3{3fEۘZ!YXO|̛4kZwN= `n;,,1z8n8lChXq*[/kZ~jgL_NeYLjqwgs2Q{(W8}7:^wȽ]M*g܅_moב i_oÖLt]N*͸>rK7@8o<%!Cd3ZsO]OpuZ΃;zС c3\Y 'R'_cXtdE);4(bfnȭ&5|h75Qp{I RZŠ沠ȂOnP,m1 (bD_TbJmB%'T.$8Rȹnd϶-uewluul/ C))/)>uR5!tN$FFNK/uNe|+S:܆GjljSqHW/{QWbdb7jɔ3Q֩o1-XQ.J3sR!wjb$#ζׂ/# +*C Ӕ3rz26|)$ uxR 8#? kGs5ۤ2E!h 3%'A, tdPr#1T;iF0We~.Ŝ1]-MMRFGGFFbe5<=7`Gc//ZooLLթez*rͫDQ i_dslPŎϔ nKnó1-ȃ+-gGemw -٠ZםuݴU6Ro_~k[o!JnGo |J!ɌErJ{Kz=Q{x$PkK<,X{}&eBBgIφk-&x!}䤼!7.6.nXcc9 폪iV_)i2&B&Bŀ/VM{CiK ѥ>#+DT<>%/u[}L7Iyy p|!u6cS(?u8DS#|# v^hG$hGe w Zo kC0#$C%Ooy(Rm!#)rꕂ]W=EVOK3x0o·; pvvTAgU9r#yYyYt0w07XTI٩$w;Z 9ٮ֩K:dcghi<r E٠ϑeAw"a%@=>RiiA)H"$<O ߅b($)Flp rp2YݺTϛ@m*yJxva])ˏ8J ۅ7&RKM͕׷+7}6ڟ愤YYkjк0JROKHMM&,55c\tniv?d,c֥yLUS 9H5cDqq:߲2*+:݀=+]?Bi1%Oj*mmOZ/ I+>JO;)df/], A;?C?0FI+=>}W/ԚI0ũ7#ܔztyoꝻ/4X7[}mj_դ883 Fm! vkwkw{&Adz &6Z\u=vD&7PyDi{ʢrTIMKrםo4x*VQH"%oSxI RRIUi.H:nzN@pv5<^}qpL~4sHx.< CT3VejuANW;\;_E^g_b$[5vtvc"͕!]C_,XֲVgAo@KP"d˺˹;y2ϴäײ쿕Ⱦ:1u~Aiw ^ -~_w`CZ]3Dln}]'F[EѦ"V%|Zsw,C8e; ddz/V$A+RWY/haRDm*gwsKJ1X RVd?PaWr/+QR0 +%!!s{6DK 2a*~ms/cl-@ F6"1'~u aP*ObAC,{pi_[q*6k˼LW܊G+$ /wFlKo炴g)ƏKN)~, pZu|]=RYݿ1ܘxFh*aXZNO_ M.Pu'mO0=TW6K"uނc@0 ĖI̗ݗe@ޅfٖ6U4A X+p;t%WLwxu ?2sʺҥp 璊\xA^pSr"o-jWe٩ɜ[S:u$j#DO* _}!gQ6`Dl%a Ԁgz!G]DRri u w wŴ{,L4.^8aܡM[*ؙ@π&J̜dl죄\%|zZrxvS;,;mrYCŁŁ<6Aϻwaʵf+MnoHi8.%&+(dedϐ($:XXI !eȈ$JJdϒ쑽%}}{s~߯y>4tGFFC.80ZmNylNDyTҥppƎ;"8%A̯5u!s9@gpEv¹Hr)H3Q#Zb= C%0J,%K'K海h@,G)!S#聆4Hzd) ϝu= ٞSCGkyޢIP#2+רqyc~.\\C_CiIjI**&Ѣ&ob.77nʸ¸b鮴|iS?0G=Ho߮9=^w>JW*rSUMÔ.s[Jv"@h=\.. T86kuRpU8qMJ#B^׽ ${MQ8bzHbo=vhhͺ՘Xam3HқC.~<;mB!^x}YWWGCSj^'HX=ҥjlt\WwN]]DPu"IiΨA#~~nSn5V;JөPu5Ԛ 8v9v:qݴ^k$GpFpV^t"!5!dmkGB 'J ̥8+w*,\"UW՟3G ǀ+eYuқ`yNvvk$B AP:tX"a6XJA~% #91_ X8w4"ݪF1yyC2Fp NM=jR j2DMpHsH[ g4л^h˖.6kzS)w5\B]ƀ K^.,m{ChZpz0X8 HK0Yͼ8,WV'O7k\ ct^~.. Bx<5+NX"7V#9($o}(MhlBB+,^զaRY(ab=$'hxxggH]v^)NW"Dq\q~q>9bAZ~Z~֮xStlS$r5ԲH7kzSlvQWXFqK8Z z? M[nyvaJ PА9c>8HJ%67%2| pHk >ʷAj"ܬ(!Ɛ 'Z& {?4p7W s xf~(s:IU:7(I*iX,l}CO:V:V44J!u5c1Yk22Rnbnb"s;Ow|ܮlWwr0M_%{~kb7-׸: `V}ΓzlQ&Dn{b0W\!鰅"`9__]sKv> *bB7"d@QB*}D7] ي锣S"aVOwSѲ#$g2MrPs$g1[;H1wugug8I; w&L@W[T6@boGGiinHHDa~rd ͚^oʮVY eŞJ&1Nƨ%BO 3L, e_E'Lp;'X,,_疊#U\̶ƖyՙWPV˫@:$dTc@& P2Fx[ִn$*ⱣsyQe$-b<ƶ>x6Q8O\ZJ{̮6B5p""*(NԑF}Wשh' )@Lm ~6Ww\%{0Dx0oŖJ`\^\\vE2i~}['M Ĉgnh_$ zѧae-N$3G^-KX!%'+LK+g SBHR`|]Q]nL<*71`~AApGŽ=<7?+0&暚4)̦3A3Zl͚[o}] B1GVEwir{!w>d/pN ;CA]DvU`4l߲ײtf(4^,ϊ@@HZg}l8[:[zn+DN`m ^jr3s0"ļŬn Vh$'T}L (R/9595O\59uFo)%:HNkdy9oD:&RlfQa$NM#WkVk%\X~,l7PRFcN6*sgz"^вal7.x5ߎ2P䲙*f=fP~6sՏi4Yǐ?؄^OPWj"`tI$ }߾;='.}sYHXi]=t3gްPt 0GgOK.@'6}R}.bP^F{<=wDG\fQf!9URRrR漢v;-C_ E_k&TNe4qgs|<1]<nGgFlO҈δY4_g#'':so+|Q cc8;[=]=j9s&ft.S9ڒW$/%Xxߝ:i$̃GA1| %>2 m?>sS:5|ZP*>t9Lz@_'II&t~a2!67(u2t2K! _;huZ;4EZAb+/%FS°i'@mEݫWqUl99UH8<^"=.[n/n^3/.Ǩi$1t=@vԬ,ja%jlhʼn#gWQJ!ҐwR7+HCA yKY܁ tH)sÜpG~CRQUfbQkcnd$lla=_W5y/ܡ[.c c` o)%(%q\w*ȭ9OjN wBY\aod[^~a88ed):Z5?Tkz/XBUcq3cJ"?ͩĩgS8Ah |qSFXV{N X]@Ć/ -Ե{E0It1;6L+>J|KZ&tYUݞ=0x,=ۋϩ?6Fཤn aQՃ$뀻z;T Z ˿uW OD@xN ݩH_rnbL6>`/싋P5N:ku7C@Rmӊc1TOMUPsALm #G86!**pp^yZ]ִKK̀>?rJL蓮HGbzN $ٞlu r%rvįY@sKD$ : ~=l5@.l8'+2鋫H=˜7LQ|g`@pʔa !?NTTic|t xu^r|4:*됌l>wXoa$31/o^˨e48ggOƟ[]@W瑢1>WSTS$OF9ITLaA#)6syNQ ©c/RMQfUV))1w[ZB\,eZivA>|I,@Cl'O^`"F,-`=9I7Dm^sOHȤ K4}#@xξ#a|UpA!9q|, ,HZD|zNg%y!{o/=j../PE""Yz A|a8Úo&oM236163 O&Wfhw%WvԝߝZ<.( #9+vw\w_OuhRnV(4qjIJMFG4ZhϏg>Anebc N@t΢ݽ}%3  W!DFQ.Y^Wp`\BջF(i!!*c/=!v %%&}Mڧ#7)oRƙ?pRX2mă;u*wn c[QC}>W~o׵rY( TEՠjPlo>u0fP.Le ?fDjaskyA ѕgB"lGNj|)<ɚ7=S<LtQ}d,&Wyp::ؒXVxYx&p,,ktIIlhН:ujNeF,*l11jcy{_Z~%ƓI }wM6poNq6c5ZJd@E2n`u*YHn=o(:&]Yvlԫ'dDe-[ޗ`7>|D`{*dف_tx>_#y2o7NF< #.֠8ċ4EJ#zm弗c vlBua95W*Qk>{'%Q}8އjN0-_ϟ>PRFOyLqnHPuq77C4nrq=!޴=#+ ;2;f\{o𠈻u@}4cլ`XuJuROZvL;D{suhHKxc1ff}qv h7s|}2 Q1o%bT_Ǎ^ǖQQH׍m<Ӻ5`V1946.6? +HLd:mM@ 0g10eH]ŕA_ Igzεƣrp?$[ffV;<4ٻJV=X=-s밂:S&a'`"~ܽʾK c*L K4P($TbeS}KU`[fPaPD9F1 ++351 դ!Zقq$dlY @a lZCҖS@\#Pv Sݧۻ/7G <<ʒϒ/EYIA k\Ȥ.:>:^zkw7?8&8F9)/Aoިo*ީSޱ% 䌶r@.%U}@P}<e0os,:z_&<=r^^j¦ʌF 0Ě )?49I' Iܸyf2$]E2.$G@E?kG0Y~΂ÞR6>2bj{|++"nhÑxx].|/4.p V||U\12̨w굇ԘW0 gvTN ݄&3Ǟ%d!}\W7^$_ǵrЗX+D1|܍tp8s63l'7Kq޻Ͻ. ֺNC >D54qOONU/_i"3F bJvi3~ᙧrr˽GLJK\*S}SqGV6e&#MRYG{gPBVY"d2J*2"QY>s>.q9}qݟ;Bra?_Q*$Tu>DW)  ~@勝@=EnhiQ.~햾A>uwo}P4 ЖxRLo3Pb(1hJt /ɸD? ܬ1."<8!n]ƁѢSH\S !-:Hl'@c34 ~'dT t3082;g^7/BtROZE\Z\P^r.:koQĮ݅ ڜ{o *JT:O7PGڻvmlGj*3%p-t-t\FX!#Dr}V7{uɉ/_;uߖF(,kh7T!Y'12dbWD(up1]HoC)40g [BdelrMi|r^Zñ>eA )=b"DReEoE6 (T31A3,2pE1!W} <ԉa=|Rx!aF;m}}2d l;%2t4xxVF;2T!!$0G8Gxvɯ'S!Vκ8.5Y;Epe@s' BJg5b~7;vNqhئ a!c%'DD U EMv|58YXcЉ'k{BOh4Ky0 XGubT4@X$ !2;!w>&NH́7<ecPeW m'Q4yG+*vC bSmJJn86 =y?)f̮4߇7Tztk[U2=+J@͋iYCaq:xebZI]W⧽X!Jn6#8jfΡA[MT t\L_+i'p-xb(]X"OF_hHo#df^̪Tim*]Ǧ6&yތR_YX]NENꚯJsf#DO*,*,*llh\\`;uym+Άwl6Wߴ֜{t;f}aEnH Ƥ2m;i ,'{Sle.RؗHXf8MQ2Uܾl_3oneOcC2>DpZs'fWBs̈́.)Z,4~H*3c4ܴmeW<1@)RBmz-EJ}IoF/gP=pȠN\y=08vh瞇Ngӣo *ȣmR"^)`3L7IlCMRef$Ib@ŘR/B44aJ )=a\i؁85r~ Ax_>y>R2>=3X<ww܋'F,- v)9Vr0(`iDCĭO&$Z#43]Kr|XUse5 M&pbh1oo '!萦}ALJvG;8wJ\ީ .Ҵ<ǩuN%w*g3ݎom۱)fWp  PQK"pv72: LqHHq_ b/O> $JZEF+3gZ9HDݷk€LG,BKoG} ~~񄌤1 EfƝ*w~p`c3Q 1a0JY_!Hk~m&'m:$%YN,X 9&M%&vX#iu*ySdJpZ[qޥ#?SO.to z Ʉ1]oaLx }SU|춷tefP;c5Kn1טl皥 N7 ʼnNay6=̈́,˨k_x 쪤4 Pk`l2"_suum5˚33^BL鑎\f+VV`03j: Nzvnhfzs5y!T,ݴbT|ߦ,@(%+WOǪC|TnĪGܴM7+`^f:uIE*ɢPQS2H耼K@Z H!0;@!c9|!!SpdRvL!.SQJmGA!UMS*`F^/ ޚ2eno\܈FVoIM~Ɋܵe1Xv2zy:ʴ\nUA\fX0o#=qqF7C7ChfWĝ!gMJ߬Sw; nL"'0E`dm`һ\!$I1ϖ;X bw07}:~pb8 %d(JM$d9&sK{ 0  d8dk?A9=Cq4!s+^^`|Rs0V1YkV!:ۼz.;{J|JHDB)=8YqKWꗖzEm4HOBB`;O2) {bUZY'|&i^0=aBgmsҐ;$ ʾݘ~*YY&uϏRRҺ5@UT||¹|s΅;v,އvho֩NYٛH9[r:R'jD/$dp:ʇ{9,!U6iˤy|vvksaf{qPXW4m<nRG"F>F> *w X{~4G$!WrK35 1VD&| ;om^G1܃DԨZ!3Į[՗֗~ dd2ukP0SFֹj m4٬Si띪:g#({@*p-en(ir`{LSXUMKB a >C2C2+ʼnZ&s.@Xs'd\#QĒD,L&Єbgz {ޝC}&YSƖ쒥EOd){!I"-زPdȒ5jq;~p}}_9Fs^h4r.3]zL֤پ ggsTxiiqLm*~Tظmmjԡ#h;wX69p-9;_kd8{ךA5K~tZy 7wY*B9dnw۾\|A?;XfX5lz /Ԃo&ܝv*2aM2>&Aѩ_6;OU'JHll*}*=_9i1%^a.HQ~"*JEcWSZw?+3_Ӿs}pC1_SvOc b9dnֻhyUUX7XQ<m RѤvjV᧨d;ٟ%ۣ@zcz~Ys4FͥIbDl?佯C}9T~UqHT C]^nw[I3;T_WPvs^k/`~}Nݿ c&u[hRbpa#< _dc#@J4K(R?zsSd ^NJJz1'|bqmqA_Iu@ϭϴëd9pб:)\[5,%%;w]վΓۓkkq; Ī }>ܪ٪m}_}>525&e%3b1.uS.0l+5WMjrrQ?^>+M滢pE  ۓS6=&\c P!]ˏ[G/varA6Dk2AJxFF',+, ! n~M_AC^ƅ[o 6Z=gh싋hѻVU|2mWlvmQQ\WG.80Ͱ99E| 6 p͊jVoT.׆ H~wAvw"oU7Ou*ԉ 2ڗΙړlL?*2*mZmcgY~:n}8 ^ ,t#ڝ3:>wF h!j>IXXK 0wB .bM)HNWL*;',>cʐ]wS9AJx}U%J%J/xNc32hx n" 7׶ضxm)f)Fz}}SS,ݻgTNDU^AkueQӣ&Ui'.g06^ȲhE}q: | XT[Kak 3d!vB|a|_q]q!!9. ќ*EaHKqQ4~X>ᴢ.?L)#z&ݸ=Z\np~ыʟPcܛ$7:ZCxspg;)v{{?m3DH:-kSmvmv'w)1PAʑ׏CzTNES}Ԅ&"~h.!Z{ 0͟tqbA2$v"0ꑵ`F".5 Ͻ4\?=|z8bD dUTɡ.u@ *$J* '.92ԕ}kb?) Lڝɠ .Sp+Z+Z=Ls}QR~9qɏ,i'u@2`,UoEinUٮSO~ujP;1f(Kwwa٤W'M L:|خ1|v|<Z@N%Da1D _r2|Ϡt8CLz TYUYJҶyHEW; TO3 B `s#O"R9Рz}}|4g\<It3kkzBFWcȻȻݝ`-Kn _ONr6j w/]2~u)8oeJb1?h YܬqOZ XCK|I" 6%0MMdD؍v|FÀ8Qi|g NaL[Q(b@6@ɉ˒20&|y-s&i!.˪'kfx i xl,тsK؛ffffє4;\  APP*վWzD2Ko~pֈE:SWՎyE|Pi>Q(1*y =Gx6!a% Kh&7A#ӤEvA TBy''fSӛh4yO@m6sl  Ȧ.q.~@lwe]$PU,_ظ˚k[+Uw?0_И¾5&555HZHZ]%!$OU{W{;č%uu@u7\5nf ہ|w?"˩McHhNOdlJg 5ɇyF]`S4qSN,;ٟٲ}BܠKi?U'9Nt7SN! g"Buw쿒qd'J?V%c;K5&iN,%}f3{,/֪ +-JaNPZ;Nef=V/Ң/,['[IK1)\IhƝl'2oMYl5p0~/_6!5Zyߝٗ%1!A"-g,/9E#J myJR LL@dg<щ]v&ϊ@ibL#4F^" .d\/QNq!o Q, CƯڢt%%@%.\Tf.z HCl'A\ 1ʨyݡa$|5K _#F\>).=})d)D٭? ڃN'C'#Uoo{m8D;et+Ã|u*W*D ?Vq?"@J+"a3lDaA#Xd&[Z\BDOν>z&Z`2MM |u>իll wߞą߁Ruʍ )5&"U)Uu*W.=Ň1 G[L#ve}2mڜK4wɺ@ 4ţ+8r-`Ui8l8̼YL,hMPd_-UUD 6mQ_j!ij@0oӫx /lp;=qjEbz&̦c JJn^ʹ((~~Mo2ئU9^rB@N::B67/E],ޮS9:uKy>BbCh$)mոCm5-mǴ';m0c+Km zYc!ia Xk,EHq␘ƓMHd 25E dƛBw KBN H{T{C w H,P,?!w/Jr=ė&b'bii B5al'QQ'$Դ5)yGNsW_fvBJIf}/&"hLwb fvB 砎 Ly@ZҸ&}K#1]uBwTd4e%UH&*e=3SFF"*┕Q;$""D(dDr{s܏~?x\>%:EY1DW!َ££\&E 7ŰJH0kF xJU 0xd|3 ()!9aw&2v^\wۘhnNfXĘN6Q6Qs6ׁrE|ff,ۭRW_wLas*TzbvbRthf4}on8,igK1#g=V뇤]g0wXNoׄLd 3"r-}WR@XYh.A(\ BRNw//? I 8忏e@^dBv³zAD~!L.r2gQmv]Bմ?OWC!|m sssqɖçǜ̜,aͩYT>>m)?=WutIJn?U)2_O{mDT>=?il%{ZZ)%#oi~ټA˓]r;!+l+v JB/)$Xuv?Q+c29XއвpE;?7=X1MFW߯eM "Ҽ8!r,Wͼ1|_ރ~+{eO|?S5p}Qӹ_z[,$a;گiWVʖznfO9Ir0Prɠ7wu6|_6y|T>*o&›ͮ&I߽h<@f--,kp~ZE>|v?keʑwI/ay6:n'0 ǥ |y|~ɇf!cCL<#T+u*u,q|JLJAHtl_B8Wɰc&D|ʬYyDWzOR4L[3;.;.;ĻĻ7QFRT "\Egg922I~Kx_>3j7쑛uhS\8 < (NF~?@ankb,yXx„8#u׸}+jrqcab—%!QDgl.5/5/q]BIN4++ ͟9MbMdj[ k_lpGw} MTTn6vx]sNF<Ze*C*CvxnfVk>뾩f*^TO0\uY,ZD RIN;.ԱY; `fݎ5πR9^\\sH)- yqI ,X{4#vGQyDu.|;M %'$dpSV"Q"$eqiRfINlLyNӝ&Qs-p4MMVW@[Tw 4ô3!>.x:4w0p+n֩띺s · aH,stJNH5j H|KO/fXylx!ϿVpij6ž5@+d[y̤sg6m |;C._\f}B\N\~B220uj|qӲiS- *ñeu;?J}Gv=9HEll!!N wJdP屃;9/~(ݬS/;EOo KoJ9)|}SπtY=.C_V X:/ ,q$ X#ť⽍!˩YCrtK'B bJ+7:@dqрD-j3č#tvc_^=9Y?`#Ƶ"\)ufN4E 8Mm]W4 48-b3:ݙ;"}үQf~2n f/^/ CT&+f/.ף-+ ?g&W8/? o0o6h+K?Y_w]5U [Ou\Ц(IaT}WB_EXn:~hrq**Ҩ:d؎|vCe[ԝx$ m-/]CE 6!X @8" sH%қw^ٯdm%4.FWǸ!.1yyBְ;. ˥p!> |Jsi}DksJ)V#"ғ*iudSEpRvaEʝ,C_ƻ*nXO}-fQ<¿K%1*-w\jjոFӰfc2՞\Z۞qCZ& ׾yBb21$ l!ݭkvQ1H2,2{p=1 sڨ*(JZkc9| O qpτvv?p'<2VE֋[S3}3}#N+X;YE"TyĆs4LJ;e _br'I|w(4+/D>|}`Y/&$|2bk{*2)0/x;V4]]OȢ }{z [TLZB4ִ_ xg6r°c Ŗ ruDk|Ԑu^Kwè4ꚭ;on6ϔe';_.\'G9 ڪ>\X0bMՁ!%Fm}t,ǹ /! 2>uyE#u3 axhK8!Zv$iT.;#Xp"Εu<$xH7ZVVN|@EGp V V==*PE^jJxDOϒx׻,Sj:N C%U9SKI|&v(y|Ǫ9Cz Xn2-XflSi*~, 0hLOnhXy?AR@Bk1ξbWz(B-O$BjzިHs8—U?ܒK\4:>2P3H33uu䌍;_lȟnp^^޺u5Pb)SS}ng޶Ti.6ԩNM:aYP<슷_w &zQɫ VƍS$2j0ai`18tRTl@ϯAyqΛQVUU7g,[#{{:tfsYr$dON%\WcBJș-S-q5s8c Y. ]yd''סաE3||=Dp  j9f_"*wsRw4~Q%FWS:')uxYf:g|4~r4~~lbRRXM 6Z9zE)\6X|0*϶BlO <}֏D/H(g'9 !6w&!@uQc>wˎa.TF ;?SmIIX*G]ZITT͆CG5ۖi8Kbb]""-w9qvN5luʇSn7hVbϗ/;QYwFo8[? rE ]GhAu$<q1:?嶁aY`A9.$:v6j5 Z;`hS`wr" }H2R&η]A'* '䲱| n rVY]]Wp| gTdTBB}^r_,+XĤ< ?g^hEdH%딮3x=?]W'XըeR%+`Ib1dJ>0?74NHFēqҜJΜ[,hv+zL-rKg$;Q3-Tj\d3rs{@s.#0O% wz~ߣMUp;2F`d\oJEn-2H{D@=+W駬n ) c#me΃l[J*6DU}0E"Š9g8g T,=w7{ODj.ԩNi+x\~:Yd^I4?pKOIwv'H .w:Ga\ic_1L7CB!)B&WC&jwky4e=>bCJ;ؓ adL&d%`57'4J]U EJI4>,kk{1!|rN_K8E*E*I WiWiwD\Gx7;WvTVk^la=rL' bD|](rwU|Bs 7p$!u]'pbU`X~N gLRuv(H硊 LU&G^ 4!L'䮧~ݧF^zhC7O m_WDW$Qwxxg>!0ieeW;H(H(9!0ʯ/ܩS[z 7X5Qw^܉LjCgmHQ tOE-Vaz$!:S:.W5ZCC^PqM'O'/AEADW d*Op`/Bg@3ScDyU+T(!rƽ@JGEmKʾs7x k=p9|NcO֯E::gPs2J"ko63Khw]V昭yq3&D1|D+^] 4' ēWa;~znz2;z#1k >9ڻ@[V|vЦ(m@3k`!w2فu5V Zu^ F%2` FBp.FF-uA.@Ľ n0ǥIMF,7ȁ[:# OQiuղBmO3azsS68U"\j*bwNs1IR%/=>RRQ1h~Qxo_]1=О+S^luj"\$yz2g 7N||5C, Fit4XJ$ E q=*K_1&JǀR7_'*$*$JC!9 '7f0DLtEzEPRtH步!r1EԭIt\9|މd1}墵HMqܺܺ\ZZMoېZGf(g)g N ]@(^,Ȑ`e۩Sm[bFetֹS(>SHYQ̀}; ^2Cʻv\SSӇN_űg"pƾj zy:q@ bCr)z-2P^dhqfCP~YufYްs&$d11ez:Ԍ\SR55xat- n-ɤyyxw-DFhfr%7 uԩv0.\ND kF^+6W4zdV 4W=N. Gay"oy)L1]N<IR?$Uq99 LZ2H(y A7*Xa%L#FG Q@v+؞`[L~L`f((MZZm #ߦz!T߹s$ot*ԯqDƛ!Rd7v;Zg"MVtYgqƛ붙iFм:mrؘE31Ŭal7}D s׋RV9[RU#e'#f<[ģH}>JJ{V?#l%w d.h B{k9kC[${S!lyvBPl7Ȫ# 25]nETt?6OB/&_;\47]k;`+}yJ̄zodc\gTyogTmn:I6 xd$jvsٖv?5;2XR&M TOwQ[!݊m)B6h@#g rHqI:I }wy-'Vx-\е|Xw12elfA|rb<6 % 'Y*RuNxYOJ/Cb#=#79k* sܕW_QRJVi \Wa!)׭ gU#fTJR'I%HʭwӒVCڿu_taBt,bT~Oҫdb=I J|+3.0.TCPo/+Uifƙ ),RiCVQq+T^w=iRn"_CFt;H~YIޚsc~o]Âےk`fƸ`\O =A#$v 8 8@Z-X-|9sީ]ct/|\S+ʣU ` uVok"uOuO5 cq9ȶdK@00E? l$`|$ivR ?i;63M569ذ:"r!ra)p6،xn38]AzǬ̬`ehX;&)'.}S+,Z[ݑ[Up_1此˫#Iڸ\=_<dG{)zt ON^VUN XZZPV*O-pee66ll;T[B& Nm*Udc.8Ә4:]BG(l+*_k&~S&M;Oþ,^.kST? oY Y:+_i`3D>k 8( 1w 1Iaۊ0wtXy IRZ܁2!gg(:S|?do 1Vro &m RrH ϸwGѫkNۋ ɖW>FYWi թN{F)8]$PF;8\yD1Ru<.҄o m')6ܔ"n>b,S1&7)m$!_\yrɡtE^hH.EKf!Qzw );m( mU:zS# j ؇\[]i yy:pa0Tssҋ3oHBj)(_jjNgj.Jx.ēʪkucӸ5F`yHr')]5l/XR@ ,dA)Rtiɺży `52G ;-:hMt$*"o;n|,:dWTC䮡'srPpuٳj5j:r=x/ګp/R;S9SjN őû~X%%1Nyq(_xNԹs Z)) Dx8( U\.Bºj nFةFn_oLK.ēmW* }M[&aGJ[Pxdni66FiAAtUN}bXR0 !a8 ”))Q~ SJY;?J_~~_Rsujd[<cٺٺI 7 JH­h%=b{Ďrrcc떫wz ԩکƌ8hY2 3| ~{8_ٟ6d=[S3΃g/azrmDY+&ECĠ_:KJ_⋦3=>=d^YN6: F-+k#>.ϸQq{qDPҧmޝӲ"7?vR ץN`NFq3F:}zQ!MڨڈNCOx/42@6 O('ꟀC>(@^dJH,տ)ѐ>?I.}з!Z.:]TT Q"pR^`Z0Ju!Hӗrr2*Tv4x)X]*KEW|uShIpɫ6Jvĩ7t"ެ0yG!վ9k,B =eHy2 P:;'9ZiFE-oSEȉ9m;u@6EJV QFIRXC=7}IS^)7aַzun489ƭuLCp} =ooҢ=;2WPܚm+ x,HAt_g1,"%`Qhiy4)ȁNbmmf,5d' gο{R ?( R2 Ix^vSPOܖD(DW>Y9yyo )6\.PڍY~gJn^]~f={ 4 C Ci_s I:Hug|9ݫwt NQ,MjZ`9{)bN0L0!! II1/khL~*Ha,N4-D@ry7Ȳ*;BTQmƒ%Q%gTquZݦGr|r|R2$Oy&z&VVq6C4lR.bj՝5[.ǃhf wvqx.g(4sP -lM6t[Em73«lCMI>2cmV?t=! iʺhqsܺgAKV+-ui~z lK"≽ ~Ǡك;xEz}aʐFTsrOHdhdl4@h( vJ?.X|)q}.e hdYR3T01ϴ߯ Y 5iV0ڷm2{I=Dʱ 6kC  rr0󇴺R*3hޯpdrraaki"$3:m/nl֩Nf2¥❫°>5X^,@:b |z1}t L҈tKCODDWץ4Ĺ¡%$ASH=.r2qsOkÐ$~911B 2&FJ6>& c4{1D4q1QG048t'j&2`)2kcou *$HH >< ?SS?ɳفFܹ,!2oVS2O7;S ]Iܕm{AGn^_V?%k,Tpct:% ұO1W ejC%+]=|@e%;Ʊpw\3 >l+eo;쑹S~+c,lذ9'&m[,Fx*c`,CEo}?uxgf+_ɶ{2s_"mj4KVM5Ge.?'rU>nNV2>/BUDb=6lz_J@DhţdS},b{Á%|XfYC@RCF!5 8лQc$bZ ww& n3\McI5++Vn=[>{% 8 8+0u]60FPW:ߊ ;; QQA>'*.^x-z~zU7]VsW=Yz;6@'355(ql? R2-D P);dk B(3n:·F<$F6Z.[qۣ~~a-!3U,P1^-pnzLDBnhv11jgĩj2"T*N{l?EOTtލWaa4l/ER WRRk[cq?K4{iѵ7Tz*u_3mpYE Y<`[E¾{1C@W=~J1eͮq@;zB44c4nxR>Z x9@v6Q-Q NBsp;dtiiK`:1]\QġOxQNb^d gLBu[m1э)p2ߦdcf)pSr6$2^|l>{E̬l7.U)M;yun֩N&]+׮$-+d.\ H1jxnY `9p;1}%uqs{+_'4&nb,:!%20RϿu}.-̰7e"8]Z0g1WԁΦa" UC+ uEOp($5*knxY+NqMqʂe{)ܾ{pFOƸa&J\K\uWP32n:$W%_XlvFxDwů>z .0 QH32 x/0U7V 7-Ҟ۬]P#_2a8KYңI $Z)4/ Xċ^"'?Z@~LSEȅeC RӼ;J_+_+7)Ꚃ?~jD NwfU%\$:_LEKnMcVAwcJ/GM811*$$TPwA{̭Css~^wjom#6zJT i I%QFrfߙ(:\֎tp<`):?7rU +2U{9~~!cBH)qfӝ<^x q=m2mk:Dȶ _p:j Lلq{`)R^\-b"tF0i9S4'o %%uAAp8njZZz06B]T/pp|lLaL/6/6N޶klG?zՆ;UE8'w/q~HY1~b/5J$ZTjA<cRB;n =BOg)\h8?r]HHkdd!r8!mJbh Ĉ܅r22!g?侔 iDI(}ߧrRRk&p[dvS5FpҙҪrCy^wJQ#RVI0>iMχw*2.6Ιn۴?+V"9Mz3ZO/n 6/!'1U ۨjCB;BvFM 9ǛCL,JeqP4ee!W%S!m8;ٽ7eiIl|+1>h#d2OdvڄiuX}=j=_NpGppc!_hp'klSŧe̷3^nwe)}?}szSZWujdSG 1})'"I9}H&SD#7MxBLjiq N)$dOaOB3<9vST3L϶lz+O-`z@,f8ᐋ}HJ |թĕ@R~wq?J9Iq͌03;H!#\3e3屙c3XKRoBe: S-[F>!=!>:?hnH4e_DfgdW[_%,2㗎\ʶ KT^#]qGe9rE^2\ (뒵fgItȆ-MU#k3Xkž_o?ne;P8ow\b$qHKfqw}&~r*F-Ú^[VQvVwz2\㫉UYU6RR?" A5|ᕏ*֓je?&UEb[l6guChx4n@jv|?Y"%4=M92Wb&UT/f8h9@> 41ؑԍg88hB8!Id Lj8. 11OmQG ;# 2(?;*vJ? !NƚfD٠lrVFf3$8BC#e}Ӱr=[hn\$։6MLbNо?Fd uX)_.WϵllA~PjG#t>@Z3J8yV5@~f=5hFߵ'qI}A:_Ҝ]7z0Wqml[[Um:$+\0bc~ODG'llj`P3l<='q0Lf}ݔ&oh%T)oM6n`s܈e~] %ޟs BB*h1(k@ͮM8 \hC6 1p0 ųP&qy8j *.:3A 1B[MݳZwG/n.|.|!'snttG&.A.4/ΤL)cxٔ߄Z /KZ"ݜل-RsYXC|Pϡ`(C7" ezZ2,G< qL8WTjnHTnۊ`*]h߸%Ui(z(K^fP[.u$V6pLѪ|^CȦVO*שϭ= י٩Me"iϠMK=kI]V-o X浠J`bJ/~*J9 $#z6G^iܥv+J@jJЫH'eb_ا'=ٽ/ʊ)M$!Hoփ$(V,H'yX]E``u | TB2B2}N||&Ӵ}QXQX!XQUE|4-06?ќr}q9݉YND| ^:G»r0鳗`}[u{ljmv>w#*td^:S3[96[[6k)7:uCI]A9"La |P B;1=}l0ڱӱcASX&PP/ٙU InASk^^"ױW>HF@A@'."Ћ0l2\[Jj7yyCV{=/++-s Fz@ t(kon`sVlz|Va/\*Mh_aM?zSx*ڱo(6D4OtMd};}@=F y3B:ߕP1@]>SB G^Z. JhY2~=Z6 ֚Arvwv}4(̡;b-%; wBү_iԑM=T5PtkԋfC$;550 zI/W5z˾; ,~m+橂k0&@.Z{8.Ӽ^i L\DfBQ0)jՅUOs":Ѭ/E ،=%å(I+??={v Ażhۋ ]NƸMGqmDʨBnŎo(e炴ͱ fP *Nb2r4 p g]]< m%l78؝؝'KHE6EUqN_LZZRQL!hf/I)r* %ch ^LƆMS 6 p]6F?/&%|%|4@LݏzdV wL:.3ӈ"~ɸzC6(ls|.KVVIZWTϐOcwZr! XMɞǚ+p=b]V[5 S@“aEUZlt! f0ggV?Ӎ-un&8]q%J#$D [!mP]4J@@2/iHZMhOt1.Nz? ?i0ľ,OCy*UH ,螺A'rW ͋΋S\.05%٦lj_|!!3,mS;5KjD^A0h{BK,&&tR%+T[\%Pf2`r[:ɶŌS3JZ̋QYuPZC:F ^w|7%C4"W.Q k E4?U9j=ވ^yzUZ6:鿰k";;?$$Q n2ƻTG'vjjwS#Hk%E;+eql>q {Ik39M4od\H置w^dhx 4~2z;@3[ʦH'\:\3\w; }a 3]@a?p-srB ){z?6aeYiEt['VV>wDA͑ȖȖUے# = jj,`kOr*Of!1XQwwځ,@&t%.ӻZZ䊃?aB|U붣E#6'r~O;P(A%UK-Lo֙*<D{̬hCzF=Q6&A@:Mt k .$w0vo0}}3g#nRRҐ^sr`~pD֯Lhǯj r rTy=E+|B4ϘܓșW{ )_z #KXR>[T{Hd ʾLF1,rrG?g55YIzu?|.."y>yaz\f\f쳟Mq[RUMgmslS ־~nrcgdqB\`C(WsEǭ/q3iԧ$te[u%Kz̐|'j?I3LaQ'c;/ptDG*ÿ́N{%&#\[m{~lLlPsS4Y?nlHw8ϐސ~Ij{?\qje2B:*p]]Bm6dB#4l 9t\(dJ2u=5NyqP:/kջpQ&5OS;bn^5}9{|4l>VEmr \̈cm6ll<<0^2LȑsWBn!$Lz =T %ɠM̸KS꣫(}prTXL课NHo!d#K}-mۇGm7%2"Y[mE}f3<5dH!?ևz8>Q&<ʼnў'I3+ؾ~m.r퉀";9j+lz$čҮ 69[> LtK]=S @k  ;ގWᄐƜw N N0֨K>}{V bsWڒڒIRҧHbBeBedUL7>!Nn6f軣n .&sR<>`zF\]PyRCP|k:TZ@P~ Gzq  );_z5y_>LJzě 6q:bOd-)c2E5Ɓ`eg>96vm욒z3UY8e9eVVv@)J .pqA=A='e&e^myU$,N mM6]k ¨E8\< L;!\L{1$F;G\ >w!@ڬW|ZقMr)XI yQV ǿ@&^ ~7c4C5y3!΁ ?dX,k%|*5N)2pR2%[/*Sf60)))\|5R _;ܧ\Pp)\܅ cbOszݍ@mlh(­+b,xq]*58&t<+xxN?ГaAٽR6APN-6h sm-qά#EE=bp2rx 2فJ+\O1)EwCC x0qWE1}|ck_yy- ^t'uIbDf\@@~8Ka|SWS# jG$ZElM)8 Pr`#nyj9Gi\u/S)_[^?fj|;voBh}rhAln(w b bRlD o 9(-gVNT7oG)OJ͍ŷUPTPDc;}$ Y쉿)n>\bb,,gl@@;Y@(l@t %M=a,l+A3_7I5Hw. JwyfXf3 lV]C+m4o[ߓ& W R dEhuF[FYڼ*)ӘLw y\M|sq7b +bz<>ijbt(Y/ET?)s+7F  >*|=nLJF ]xԛʗͽx56ּZΪccyOapyyƧ=N{jsⅤ*ZXU׆3nꦎ yX$ )G3ԙjSwlS?7EC.v΁c!i(U'9\UZ-'psz":\]  +}>c$Gi6Atl1@B[>TU{*"r_r!ސ@{]@`F<zBRZXr9څϞ >7fG KD٘Í]SXX%R fSP\;R^)j0۪#9f26j}Sr½قH}AJvR).]6rˣuV:Y()‰1jO7p3ZZu(?H,4RYIW,.ZEZo-}hN, zY|4Wdጐf&.iqwgO:$-aY'sdcEBujl _ *U+UPF}YlϕKZl 0(c1sM(sӛ2 C|yrM3*Y!%\R h?_<=| [$!@箽j2f-[U'G4)N@meuaIRIR \9PLE{S& L[uL؉ZIVG](<0v\B5ՔoO˲vώ}p{~ۜ WWO4q9( gd祻cҝxPѤZK`PbMo&VFl`@2~b|$8~%u7:`dɣb/56'Bh|6^y=$۶msCֈ,ȩ)b:;fpE4 P'2' A2wSCYP/yKEqwm v>d4Y yÕxxN-M-Y$%;*CJ㢦vrr 5n5q$sߍWο9ċjzKΛw5:epb*5p{ L͎T8jT|bklBh6XjYēG$6 V|`xP}% IFh^y͇֚)?o2.稂pxŀq7^m)^'}֝%JMS+.AP9*ND>˨͢?~f+j<̕ _ubȚʵo)0L 4BOA*WBN)T(gINPg5ϸ8Wwkk,5hh 14gSa0ʨ`T(&$pdQIwj\_BKb=h1 /Cw47"z6?A erVU[?pU8Hj`O9vHOy"b͊g8Ϫ}.0n@AAUGl`>Avu%] ++?;ճֳ>zFLAg)n$"x6ssuo퀬-d.ߙ/_ BS H1(3[P#CSX罒({(wfcHp g3f^WײԲ$ p5LnSŀ70<<ƍ߈ߨ!!;io J"{5UC5E%8ݬ v0'ȱ"C1s3H{ !3;= h<3 p'v)R.SJb" ^p^a\ Mn6N(ZWW:!=סQ[`%ah{'e±<ť *4 6O{=ޒ'bٿؗ:IK5M%!hOCz@D 1$vybd xΐ>A5wwkCn:At!"޴dR@[4̽&fI]r\M䫟/?sY0P>|$hcXR1eyMzMkpA`Ⴛeu˪9sT00Ԑuh} %͚YoJ7p9ՏJY3+ /2dx2R˝&[F6L?>R*`wL VSsR +sAhh>BRu/`B ojEӨ|1 Ż1T䢱0vwn,qw |@zgq} z/}pTT&M8A~!:VǤcϕjk%>GÛyޑx|fMѮ7U&[ wĬ+BN+ζb$GMpH_q' ZUNrcGq ÒJ1إb4j}6 eECvV?l)2d6oLV̎< M9Xi4caXJD9Ӌݪhv^xKok#7Bm+Mɵk⩮pRtRtBB- [B(NnΠʠXģG-͎͎m.R9DC_(Q.b߬)X MB.1UzQ"5WCDT5w XbbEM'߶Rg3<Ӧ:+d{5\\BBs!ծx-^~;W*R+pn D߸.xO4rôfz?75Hٹ*zpޔY;p *Y(YlZ>0x "ex||tJȥ*fa,:H,aL^<*^=RA^ ySi燓lhR c>U,.N]<@{z96qc$^^eOcOl쑇#qDddžņ}rQޥ+37%f%ACyx֛2&cJ$`(\@a~'74v Sx+y+ X' +/./..%cD0Sgs*v5t ޖ#]FFF!aԂ7CXJE: )"^1`3V.a )DcQ%n˩B܇?vE)lLG1$8% %AA WONۑ\[3^ggL.A.+rTOj Yzֶ͚ڲ.-O84dVׯDqBׯ݆D>4ݵv8d[rIG,/CY |30axvS՞wrT2#d*=V]ϣ P~pX E;F#cא9*ȔNC7V\Րհo*\G>}%_X!SQHVdowozͯ/Qo*oq){Gp'H*%Fr X!D]5Np[lBnSObij7D#ӹ(b'ÈiNkN}=#E] 作@ޥlwȶqp.O$Os+|q #Y,;sH<<<ںIIa|~?DlS쏉'?uk-E?Ddl&g~v~_G=L umIc]2qb) f]D/S۬uֵH*)%0WJ^#^2FZ-9 |Ƞ1gϞ"rhRq 13EY4 gH#WTҬC%>=jlɮhbG(!Zh* c_|03$e^Iɝu ~SpAA..RR"~Yp t t1{G\D^~//9rOM͚¬7502djmO?"vG!o6qAZ|]\_MPWSAG!KQBrZwqbjxuzֺ>y珳4q " ?z=A}ȅN'sWm֓~gBC" FG/E/]l ;!Ɣ[V6\2_!O ϥB^EEjIu 9FCĤ*ғ#)b\kكpRR TI3[&zzdtkMF:oӟ9Qgun&rwm)?z'|upsWﭯ3z S9H[[XvClX IUE{+3=8@?G8lu Zrfz|יjѵhd%D`%ғq̷3Yo!;|@J"sC)wNnedLYyy/Yhihhآجq> 29(r83H  8SKr?Q` s}MBw~p#!efNP87Sb<<]Uj()t~5557DkdB|si @@J3)+$.{ Jxkz!զl*"8!n%Ws)Q7$4Q"]7<-.7C )e4!9of8ad8[(1A}A䆝c#y|CٛU.z]t "+ \?ۅGQT[mM#<>8Jb};Ej__@|{ C ĩ_@'1'7B,4j~n)ݸ \v3 2`H'XC(wHKֻ.RvQvIdBBƦb!º@0_BBح;@b2:P)Ik>DKjyAA!G h4;_9v{C/{1\s^^~]Qr8.Ոa2ouOYd{ҧ")UBsq?EJ+.)Z}I4c1 &gHby,YJA\<OO9U-Xs3Z/2|Z{[E扃;Ѿm> ͩ:g.JHKx*^ v .[i,I*y&=}:D`V]V]\)D¨P݁*BPt86vU!44|v2 vaD{ʸhso | |=Z:-O@BRҸpLU\i)"аذqR+F+sn|SzmMD.}ŢӟM^`8wBLL>||o>aV/5w|'+9h|6\㽕%h!T7%RYSYK̚qec8cw%ޗ ,.܎3d cpz4!@Rl Xi,B~#:4ygϹɛdLJ΄\\I؃q=ŗ#{AeepZ„L;UgxrqT)7d)*p|~XC:30V~L 2}g=v @z Korl'D`s ssc8. XLfي@XDjRsGIIDQhRHUsOH ģP8y2.W_<%zE)TC_W`3;XgRgR?Qz9T00BN+@>zv_;dcx.VOQ|B峛 GrN+^)@v(4OA~v/3*3"^ 8LrzPtz_\9Q"+Xs"4ZN#%m~.rړ3׻LDhnOr^ WUb`Sr`kn긙Wѓ7Hz-^ 7zu%n(0 >A_KečgŸݟ{2""S},ygu8){E͝~j/=uvs#:!XrpıfSh\ZTmX0N*/N@NTY:"z B>Y) II<$h9Je2!V.bnb ѓPp zԶ :6HHBbjwN!(l-lZny bGڑ' ۂEVpfpf6Mo(cl9'6gS]sq)p? Dz& ĐJP?UErH\J8x {wMUT2ܦc2FNrL2eNE !Ԉ<pdJ T=gϱ?/{ٟwe-/|^6Y#QC@̆߆?Ld/1A/^ab7_aꝹX2CYYJvCSf0HYoN/rU’AGǍ Sˢ0Vݵ}a!r1IISp++FFG=W!.:od #'jozǘ^B/٨Sk2Oldd7۝,w6FRRRO2*\'B&93 [?ꬉx}&+i:A)KUU%^@m%  @mXar\c!/\vX5h_{C)Oz܊듞g(o*o ܯ_ullmYq]R.TíXX33wt3II;Y'uJpSЊͿy麟 NHR7$Wj{R!R@PQ W$O0ACZBa=boEފ'=K@Égr_=ͽئ_{nrqdCޅ!\7GrZD > 76զ_re¤;5ךD JT.=~. Zop1rߒ09;RqۂD= ZOu* qϝ9m" /JR>a]OO~7V٥^)n,||9{YZ lB8sn+g?[-nt?= !?Q4=gD'3K@k5b&a;xyjiNмdB@Xt;6u*vzX3#Uj9s@-QuXuF2s xx^>gMjl!Pt&$Nw%Rrd䶥Ps%+wRRe;Z,Z,PaP%U~"c m m9ؒAk ۼ?i58IKm:%֩smR:pƯ_ysEE^C͝-ȩw642B/ʪEh;b_FYV+"=gxUR XSvX"|4)zWC@v;0 l(=9_k( 7`o-7㠕5]hcg4t>>bhxSu%͊{j9'xf^ „jC+sѵo"<)BUu+]\x@xU5U/?H)z.Kz2O +F;;\pJ ީ[1sfȽ6ujZjVB?t=UcP^eZQF? 'h*k*X IM )K4\o33J6(dU6F]-p #enٞ(}ttrBZ3R%#8i S#ƟSȹ~$f>GxFCN$.ZQuuBYdQ^qQ#J9C}/jg/NMt*6q=lFuվ|y:*f g sJ pLǏŏ}&Hs4KJ镨;w:Z˜QR|m-h[m=xw/yUNTfYR\z%Fu8B{ك'=/{Vo*&|9C"'+Ƽ#U`7kRZsnK}vO=,D(<&{)dlngt"樂 C,RO25 $vwO=n7`Q ifCJE _bLN&}1ꪪw^}<4& \oOLP ONhr%3E70Pk[NT4joKT ½*q,67 G4Iv+AmN9+îBJv}f1m9m9pܡܡ':ʶ]UR7gxXX¢('3g~ NO%7ׂ\;2bNIuW7y]')j@!Md )k3>]al  9L@I byNMIJ :nc @H7W"!{B!DRѵel$")B|02zd3ab٠NJb ~yG&iF{yƷNG*=蘗L=:*=Nov8׸S]%GN?]fgi\ofcmUN3 p6WW^ZCw-5t5~ټyg '(l{,>a1Y~3@֨!˲!ʊ=pq M{RC+5Ql3od Ǩ5%Pӵ$C竧'(? fBCڋ{Tc%Zf#W=fkIO\KRr= A9a"y"j#1:]+Q/[>mfl_$R$K˺/Em鲉=sn И}M/Yx m?`TAoѮu8ѷ]-{o) xKIt%g9 47EbƬ{N;_ ƫ&+s3!6$ۂ'f]V<{؝BJ+I+*rS)4śTDCtrJ vΐir^ dK3xi֕8+d{IF54p) {RySyeBdBH=AQM$ØI$BGLS쨦fzuV-sujZB5JQ' /%[Br- ڦY{w )PTh|$6?v!G>Gs!s#ZXs{PA;tgc_=aq؞l_)CޝGS <6%J1SР2g,YQHTe: E2eL#2B2\sߺw^gk} 4eyu`t˒r=.wŠї}poD2w?Yǀgks'&ViOv8g)g#lG2o}Jb$m5?Ѡ w2wPD )8LaǪh{iкDz YKH7ƁtT]v#G6B"ݴ@[8Lb.q[X@T=LL~6ABGL^iW !O)<|h!)I/8ed4G;f̔˄q,|.kU@jUXڄ >{\d&*!zHAoctiʛFF)7u{mxϭhF%86/x__$8&EE\?bKҷfi`86O /F4TYB/z2bozq.{nhaޗ'ׂ9 i7cT ^HoRͣ`}B=Ao ҋܓ= v$j5m.9[OˈYU~p ߣQ DwPҪbKy>] ];;ڗk'+F3mՔFSy53ߪ/0i HoB: ({rR:{\H@j >8!tpPh4V@BK.\7ET?]]W+ w ge9n&1&j{{o kH E,8q2ppC3Nٹ12 M[DQq\5RT`E'h Ƨtz'Rj 㪗kk{9jFv'cᰯURu:&(~ZURH+yӀ鳀@5O4{>"44״݋A=:#wjE}G]%%DAZb*U4݌M{#2zA"ȬייG#."Rdžx'oOS(xK[5%єC$[i <_)maf`ɼr>sM{I HuTǫAJf0@!PTjTKbIai7 g=+_[w}24"qXP Rmp= PMvqcԄWpF! Q}^㦯 ri 9 6pn7Ll5riD6 nyyթ)~ŋ'UM X<\߮4USM<[ +{I:a%G/fN`QŝpJ%3%@q1@]ӧ/~F4N _sZnB^r(/KAR& ZZ bŕw6 v4yaHx?"4݊4Nu;sJ{<9֜eBpRERE۾QsA譐rS#))䰏ZZX#/(/Y]/!rlՔFSsflwR?U8`҂T/k!?'5R VG{ 1 UsD:wXD=i g q[a.v513NN棐?\" h`=sN+ !t <MbY]+6)ShX6'Sgaqt~  9 s/!Q3H11Lz_qkꨡO#oNW77\o`wqHy7l,b=<|2=%kx꣮3MwR,ww5"=8\-/E姹E>:Cww4Ȓu휶&i[pJ߼ oSl_Gh#EVMIn4wN eOǟݵxj7T[\[T1BoO*NoPm$ĜBͫ͛F  |Š@fMp:;]43>@n$õbtZY +":C<}xsdfVApjծFK9@d!ff"kxqxr,|@uN`oPK>V;^ur: b۸*E*k4ojwWFz@tuASo܌4&"ު,3r{Ή,R1`E{$WyE6g>9}P9E(1tD06˲N,\:9@@c"yHIfBHr:l+vaE ԙׇT Gg\4Cz=b+3wfL66%%U&!&! nW .H1j hki+]*] j\\Ȋ{M9$iy ~+\bm4uP,y f+@|;z%@gP.6i(x(x{q}rҐ.vUDCR!18tZ'cJ@dL\sHERlƔ3J)ϨDuMba)/x |zqz"0H5dR#((y`[IIllЫ%zn4M1>p3WКuo4UT[ gG>Ip{.EA?ׁ"Q~ .h<_EhJZjʄIIr|ͅs-=} N {wI5M򹏞8duo͓g4FHr?F2m YJO\7wAcdtMsR>.""bwP{C-*KӢ/ٗQɮFBI?$'Yǖ$4IPPʾHt\>̙{y~b3}}ᛏIC\;+9gb@+%{&&BAN;=+ %.f'cN)/uJ3cG&Tv˰Hm[S`˙԰tx%.,Py6vJNN|J;2lVj O*H`vlo& Q "B"iuqVZS bz_cʺˮ)bt&P&}׾esIvw;k:kbcUzTz'$Z-xV::{^x_]m;Oj%~ ?RT:57q"%vto{HގdrvE&;I 9U^m! wRc#g-%fA2)"5wXsLL;;G\#$ܫd&iwGGiMsO\Ԗ-I~/'F咗ka >lC}iKJ֒BJ!U☴!u\Fpj(5}f}f&fM#+Rr(yءؕΩg وi,B.y٫|v'Lu w ~br @:jK4Ɲ]tZ2+8d?fyَ;5CW}7>.*!~կW-Mi bo6PY\N4r+ffԔ|̽+g0Ѷb,I@JHc$] YPzD&,eN:OTxֳj=?t<%&y &Y`5[21N֗ IHQd/,i|'ܼ4kUڽg":ᬵvٜݕf}եY_38 \OŐC-$+pO@Pk6J> Λ#g]: !Pg[=YyN`8uDǁH7'D޸+DS>I:cJwrdcȦ[7#Z|ϝh ym`$"al)*vf=\/]d@τ(Rh;Gwdd*aX?Tȹ'$|݀Qia%cͦ 泬o3i-ޯ; .ogq7lTs@ߺr |?^Yob1km?_f5:\X.Nʫ eh #/ɜ]o/!YJh:j޾^Ӑ@=a-1V^ Gz6Jd$VrJo`)IH ж 57ܧjɛ\<f2 h`MK ~ζalWyHiRVMksބVRR#\zw6TQ%Er!yy՜9hК??# aʠFJgdת?wu0Uq2TfaPjCH,sFK_<_ŷWs+u]cc %ɪ q\ܝޡ> JQCXvY$vL$X6Ӣ©lIɾ'"(='S#17k0fނ44<1㭥$FUs?m-8#]0udezbqca2cUA?$E%tp\;vpW~F徿F1.%ۖ-O(9l\^ߚknߞQKڐ h29ud|t% FMK5LMVF 4:HbX Ct ,00e+FXlԙX!$/# *j'dCHPt$^,$>~ Wh a}#Á_Sq,Yjq7!q0{_jn"a?a'O Ƨ%!G*nA яv; onn^׷ jҋ4)ͥN &Oro=7Hg=_2?tB7[{'N ;&v&v:_! {5?˷ɷ1X!![NgֺX* R0K{W '%k}&5D!/|6K1Բ1c@a=zo7n&Ӥ{x 9+=ICGӷ(K& ,~VA/ ,_g_;!}!N;IKi@\->@׺r?ZPlZ6f34qD{o e"͐fRp3Q3QҞk ϛ!yԢox! /_4@ M;umv~ƁQ~idJ^Top w<?x_4BKb~)|X5Zn[7P׽b39xtȡ#[,)$S?@fl/?815Bzo-Krp7`ei:&!8\fAxekLV2<0Fo̿-FoSvCno"գG14JC,^ Km2[|TK_##h˗ߏYa@N9>ȴ٘yxjA%;0ػh*߽2)C93 )CH ,sN(J$̄$[cP9Z{{{xzY?~Zbp˧ύ0߸HqLKj5f_HRpT90hmD'"w/؍"y .}zgbyu_͍>[iFq֏5 Ғ $>[Vj&NnCnCw>qS vP^}bݦd} KZ65Iy:]qϒ5jr={*yk::6|2o@x8#EFŮ `ZM^̯m"xuSKܝVWMa{RIh< l&sWȉ8 9Kۏ'{T@B?N`{-1ؔB~pp+"Γݕ~wc% $Ǿ: /q޷!3H7ӣ'e3 E~ 5Uj*Brtlmu/hqW|5l鈹əT8,֘NENθ8ZZ75K̐n' JBXQQY"vQ_=oLCb֚b:qۥñKd4+7jI88&c 7KÃ.c EBZZDOb8ϤWO.YB.,^VFFQ2]RΟɮν a9ԇE<ˉDn&ee?Ǿj͢.} iRRp;֫#<3HlG< hJԡ61Aw5ckNlԔZS?"=ë}S|N!P/q> @]ei `ohIMh˓Ll0zzާańyiq-Xt@K$}TTF^v#(OΣ 8 {31 RC!+hLjKd /6ŽRe}a.!]H_36858q ^ߣ9Z!u9u9 4 4UvuvV053jE(Q9<Ä$i/iDI3%'2+<5Źc$Znѐ)q3d(tI?y˂ʞϻ{h;,*q4wiKS^WyC[g5"9opkOOfʳNjk"XDr6cx?T~J{]}J36jfe\bǺ4<gsfKV)fGIF>o[ٹ[ķ;u$K:hIsMj;&KV.ci?ggm Q`8,h( Z|j:p[$o3f>2KB)Ðr>|~ӵ4Z_#lzg9$$so(nHi8e_,F \R%cWS d`DҙQfiBSS.:[ I>+m4 tĉGMJJy i@%F@ I8ALQm!;5+dg51]|.!pOۦn}+T㻰52wv9#)(،DDP}or'r!MM}$]%]mx7n짊dS-o>ywL޲`QSGךV*+C9-8gZIYu7wUW+?$&--pÊ^lIQcvAFʳ]112?$Sb|oG;߈ LPZ_Ӷ"~{URrzeJ0!$$I5H==Z\Ѵva(m3D#t70HA.6|Աל6jpâRHBZ%S6eaL4>IO<Ȩr y 'V[BZ% O"TUu)3tN;sfW_Yf?er; %N>ŁF:3$uej@)Msم֏N8=\K }>\܄dWkW2Ď{܎dhX5}:sFv,yGMI+'i4FM5 : i! 79CEZUC_@5O^Rh\LB^<񿪉|VL H8mm4u?&w`,668t )ZdZzqpaS#pS+N=&g)pcj#OtkHm_A>9%sH'UOzzwMg7FS.l9Hߗy* |TPimдD3z0vWC!A\8a| k# Bxp$4vp+ ԋT'YATP{".s,pr21\Y#vex]LoG ۴76!Rz7[l侺=ET|V㯌Xnm.GHqm5w]/&L[lle\]{=kZSEYE H՚RxS$Mԝ :t(i1P~n{ $!7%l|@'PIϬ+ZV#fa *PY}mPmUmU=!3Cw8AR 'Pc #gOw-!+Ǒ %~\k$J<jRWh] '9v'tIiv+s;k43P(*yVnnIIXZZVU VLIICBn!۽0̲ib֚ aGP[}AU2^jǸJlaYex)~"ekE5-x>!8)\fHӂTv\v#~0 &x\0 (GD1=I/m/MJo~ZHh5{weq\$ke,MY"2NTА%ۘȞ(lEvEBe'!ԿqxGws|}ŘqW:]ưeWpi癑q/k4k'&m09GjOyS* <MǪǣ :n\L6q1 [- /d%ݒ ^W%.j1t8o>YR#,#Ҝ#қgp&7M,6FVAjЪ2o`EB6peNaeb]ji"CQ{0y5R#n~:|݇qٲtVEg|Vt}mFj}5wԇijhCCk3\7emN 5Rm-'i(<1_Ux &Ol)Mk`h in&1R&a?+q=0&bnTVWw}$f#5j۹"Y6{M+'D_JCktPЛ4LG:K]}yuZ.8"UN',녃<\l 2N՝ r ' ݭK9'rxNVD=*ʏV3jë\m/GLI ?T?Yq('5(B?KGPx503B7 H<x YGLڛw>rל|:s]FC>y-QQ>b'U;@j IMN_vb׉]ʵO7t 9γHz2 ԰9s%Y0 e2oPqJ `B9EYPwƉq64p, Z|r} 1w3b/+oRoDukw/ @"LVv8ՃC oܖa;* I$4!7e;ekC;v)\J%Մo*&l31Qu6uצd}O >gr<+P!&+@#)5S<-.YZXm/^e!kDTʸ $ٟ 4K5 9*U!U!& jiPx@c885˫ n:wG![Xvav!C@ha3d7dϕ̕8 iL'Tlԧԇ@JժL]co yS)'Bm4y6ekSuԾpj*c;m1XBGnŝH^+jMbu,je7NSЅC9B-gA@D{@\gEMo bb+˨h!yᑉӺ}vx}! uԆpUQUQ-6*U%V%:|NKqq*$ @'OWp7ISq1ŏPsdyjׯ UsXۆ2*yW8~8prut BH,E>8?s?:: à]L$|SC}{Y&)_NSZ1{կ_ wI=wEb>#B'JңfNxGv@mQXm>mW Nj?Sv+hEZdvݙɨ,@:qV@ :d 4eH9z%㌎,o9<)^ӷgiBBsEmp##-jBj;rz%]FFe)*k7|S4'd oT$=E[zMYFQiSF<]Yv͌i L]ExTv漀w(1n!ԐGjk<$8>8>ݍg+"J28mmjF@BBvO+4hFބ 3OXoS66<Or~1e矄w&-bV6ۤ&xf)ļ}z pWkq_ <{vՎOIhb0گaac9)-| i ޡr= .Ӆ4rdjиs;ƋaaFUnkCZt9v9R\y1c&pbm|,VB^pNe26U6ՇlyM ~TnzO{> KyO ls?u5n,*.ڏ?\b?W-p~<ЈH?md.2fW?EP`^7>˒^h~ Y:Kj'ŵ?{Nq sLOnTZ 1m7|IjV`XaI?0\ +s@l#,33YHґUE-qw?UZ>ݣB۩+Ij9{OV 29o)uFaJv6w\C2i3ڐMjeRzw!["2_6%Sc0`(@'5/ƾAm}?iRhH>O)|Ŗ쇹KÞxD <$2sB;UR, mt{sbIX(bbJL5]ӺDyqQqo@ 76~rUB.VOBm| g$4r`3 )d5\ ȓ?mFLϠ<\_fm'Rjam%u$G%G?i͙ݘݴ ICNGxi롒'Vbh esNp);}dfO-9=Cw&Tgpo/!Ư^KB5pƘ9:y7b>"&3W ' '޴ڥx%"8dON׭q= ] ]؃r/8|΀r'{?`ڄ(7L#rQ1QML ]_w"梬NN3K3 q q xJ dPPaC.|FE"YMMt=\Aj[Yٮ6ekSޢ p;Oz-9Z]L"z:z//v KTx716@tjm%KY")тS}O$-*UB$l YOPv٥q9s<33}f:3|#~>9pjpP~@Dw- ra"n|PaLh4%[ L1ޚ Ո,,?G:sp)02ɞ0j ѢG:yY U%Z zDT˪k_YE=k+:e)|pע/brdI i7E@`eܸK[$R* f80`?ԥpH#.RRmXNV逃: zn"uu0;kV&똩%'Pt 1 (ʜo]HLlgp tF:kN٭ϴ08A}+#NE%JK$#pgQgQ}}t\队@ g_H߅vn7rc nqE٧}^Htzu[{*qD5~6TPF%-fB}4 vJlmuͮXEE󖡰len꿋$k2EO'!ҺTn^#iXϓ@l\ŬŬ$ٝevq1 <z'!?,K<# N_Gq*,*WJv%qѩ"rIw$'Fո:(h W\{J4¢\@L@L3|N]]A;m$3+07ޮS6;^g6zniU;mdh˄t9|  Xf Ē*ۡOMs@vq7. 92\BX7_Lն HĽd J97L֐)i Y,ƷPV0py/UT/lZ+;^t.KY%ljuP{ć6-/\8 F!ݿG3E3uN툶77tWwI4Ui):ۥw'N1vuĢu?Ys5Ym.X1k(C,ˀ0>*շVZ1/T D\c)b|!WjCC Rem l  9e|4s eFwxbg(2po-}5:$s߀=ˏHiƩpMMªLLcqqhN[ltڮS:g@n5a%$r U;_Ғ!]0a"pg8 7. W ׮;|rn cd7j SJ4m>&s%sAfC ;l?`F_ʹFܜ! &C],2e9BK8ۭSL" 'S}Qk(v2#CssbAbA򕦕&Y xKە5LTeڮSv,q8f3,J;?W"&js\C m@+V>|rT@2Wqa $apAA07BZiyPCrCr‹.=Z I`8AyQ($E/ׂg;dzȞAѸ{o','gЫ.Ep7u>u'tFYR*̤mdAYsX36+ 9|P$s VŇW*A|d4 Eث:UU#;?mvۚ뗦117 {<&&ĝ5q>Ha$.uA :z_B­pbR8Ms g_ݻ]6;e"Nr#'\ȵǐ}VZDNRX–!v҉b b ZN#,% rb$DJHfeu5n@Τ @N+a0o)ˉ{E!I f_|]7;%[z}*V#NppUD];i`yX%eHX2j>S]Y\:\+1lt`~ȝwvOYՖՖPp$H1`d q|Pu ~c;.e[\6ZTC'o-) LGullˣ.L_ z+I秜;uN"[Ը@TF~|n5BI\y9W%\~MpTsuL#@< mBm>7J ֬>5L>m\l`ڼv~I91qbpDQQ]B <#ir]sgWT_>?F+}fj3z4NLCN0Ka7y X1\CJRtt 2f2r x1v.GSp&B 0e1AxG(Bt]{>5’CyyE-xd]8QI>YvraZ\f'VI8Fji!). L3ė,4eڿ7Pe\qq#tfs0V\C!Lt]6VV!y AQ}4{ $2@fW'lQg>cĆb&})Sk/xcc_'AA-c'I@e ~xFjN֝{֮;0^i3BAK+eSשH)s"fJ߸~3"))-ѫwA4xF``,J%=v|z&EX> "\aGrp}®*Yf(B*@MnȲ;hc;l*Fzb\0qϵROjJkw # xqR6TnREW,=j{77vэuI R #pp^^mCCk 揄]CzQmujy\Ŀ2TbP|+:"|n.|xSK-NNٺvil#͊.^1ɾȼNvdD[x7q9:yld2 tqCh v^6ք5*w.n++B"˷э_ZeڷM56Q#EMc,npR\Ջc#\W;Ln=|RձM]^NcAc07+wa 5^t.üa}7]ongfi'&-?irpɐ nnZ)kzu_Z8Ul~ȯ9'&BR3{z΅˒Dl^1] ` %böHORG ũE@Sd@D~> J@PCEIw|:ϫ(d„*zm a'|O8 {Pp;T,|.$gV BU_#\mS7~hSZbZS~u JDKڂ+/hH"SFg{;!/"=FJJo2W3WW "}H$j"!Z ۑ LHk A)m28J c)b/Mtp"EJm]Q=n gM זh:OgOgOg ؓYᒣv9N_I]S^:=[cUV{7++I.N- Ik = #:&n ؒ,r$)!Bgm b\md5eotz a`p6fOE`ϕEd^N&JNQW0> ee;)X0^r܀ŀŪ2@)u\Pv8fWfבINNWLL6<St^n>\i PFyljlc:B{6ދk2'ՆGgV&>Ӏ'%7x G(R44SAnEOz*V2*e"ʴDhsCݕ 7r0P"# .|f IZdXn)dLk [ɩgIܹiU׶N;,=pԻ$8FF0*ifi!MORu܁[ngmPAa,~!툌fUL-YoNXGfdק3tJjSN`˷Cܭ5=Y#YJ߿~@/uc [+u򯮟i|wY84+hɱF8l"y0m؛|P=B*Z>E663?TN ?Q zzCBrV q/ ߎ pL[yW)7DAPㅩ%)9*,ӒrrA&cMZDD3Fii\YccZNTs59DNm_e_4n22E$ˆx|{V 7rbn{_vxh&gI5hmS%^`G0{DwK@X99)$)p;L**wȣ~MjQQ2]PEXLsS)t DZۼlG+0ۯ:yrmy?`g۔̰ĠT='{ PnG,8 8 BO~bĸP$ S;]ڶ% HL)NGWd_LŃw?Ɇ #y@C9s{LN:߸ 3NMMKPB 4]>N[[25`TV A{ L =O˘˘C! ֻ=C037D7j`\$;_]0G!Jn=Dj^rkBBtOKKe]KVJ-ɭc3<3 LK{qКީj|^+a%eUpVX8\0wL-<Ѧ osWӭr0ϸ"Dqj}:1,,l$g~x5~JDǁp\&9=䋓}kt bi'mr.]=#߆W%>0Cj\b=pp~ ~ q\;whG4SNP<z0rjbz_ʼnFQ#ܳDmd>Y"la Ue^nvafFbaN.!+M!Ϧ=E6wR[6w˾9e7UPڬa}ߔ_xBt1UrĻv@{[`#]6%<:nPדwL3FWenx#OS-Sux_M\y<-uSƼt;~ u;?֍l#gM?/K]槚"1;v4Z? ^3jM|z]vo^zft I{N!|#ej҄-Fe2tԫ.cJlŕ=wy4=6)w͂BaWy ]j/|1-5Jc1N*vZZkdcD{JfM4uw>TTq0piZDk8Q?0܎xOv-{ b_7Kxl\B@ w QXMJd?ФLLq8RtAA:] ILQhr0B} R#U? I'J\q˴sFVn$UW5?m1h}W:eKۘzfb{b{b}_;}Ǐ{KYFA:3 ;4shFDZȒ]ՒSܣN/{j|v\O_r4kX0 Kfxc9JX 70:84CnKtx d9BSM1pL-"%N%NucK)B"\qrٔ74bE {!~q@!pG ez W9D Rzĥ [|ǖ!LS섩ھ8%,qƿW.*2*bb5 t?kjEV"+jNj+H;X缐e:S*Vp r՜5,6a"9n@pY ɔ"?Jlҁ"nz{koI&&RXlb(dNǫ}im;s;5Hmכ9BQi@rcΜ-Œi%ȝ}@%%t㵶s2Xk;\b@bO՝5!7}xn''<7;l%5[?BbNx7="#e]'vZ0h㰛\[^/ Trܡ|<~ BJQfɘԝDXė`N?g Ȕ!2yJ$ByH2cH湐 d&RDDBQf)2}u϶?/{9wu^~:{eSHJIhJH=ֱ$;9Mr bx̐HzA* 2S")>6i;^V?uM'Ĝl[[..[ 좊r T"xiO+%GJ kw7eF]LKwԝN]2D{~(gIlgJ]+G} (wH3|1PKuL-`FKB5 l@\]']XxLB'ΠH0TY*ab$dr?ՃHכ9">\\vaRe~{Nɴʴ";(.#/#/+k׽DB|JCixjk7$:5{Ա'ՉI/µÿ])"X`R#^@|_4P !-̗Ϗ-[~k|ЂJx̾{wh.o5V;>NC\']WE8M ?KEPA-= SDcH _?dX^|N׏sihb]XbRtE:b: bXԞݘ|%FlݪK`Z[)vm! pH?rqu9 {mx!8EQ Z@j1@7u5r$f݅{%m<$ߞaܣ-]paî+7B9XY B8]>]>e [ĒĒJN;50"W8[PۉȄaNouŏq8Ma=hކ Z}4(JqnĶA3_ 4U\ZhUߐ2$-1>vChknPO6XJm""+$y?/0P1sER  8Dt;7E|KUK~Kh̀y{*-&&Vڝjgkg}L) ށI[˺˫?8Vx5;eHG,kPgz4`}Nlu*R6\^- m]֜tUl 'ȞDw+r]enY t"^:,b *[VdцF| ERK-%0#QQE6B<$cMĻ(s$lI`\.qXkq #˳L^ ?}LmG. Tӎ6OіӖ lf{n@ĉ1ޔ__(44l5d" hnj:թ5p>Uh7Q"9]5%v` 0(4_HNӠ< ܠ8SDUpe~eK azk>ɨOmƪ]+hHL;$Dž%mHg`G{7\sLxxŧ[|d{3xsBRtGV i~URON#j.@yK%ʞH7]{_[^Hv[ϝ<~hOh<,ZrxVVy*;u=h{u~`SB,nJ8ׁ-]ogf+͈VpHs6 F XF0Y>cfA~'C4*~!e9d"!/f}pmpmB EMNAfwh r˫H\&X ݿ!GWKA8GQLHqC%śۋB##kka-鐩؜xU\:>~8Kv ީ5 wh;~ԩN}\,ӢF#O5m͙H\xo2Y@åXdeDơ2b,!2L]w@\</rb"3TXذ tl$4C{w2p73UC.̌]yl܏Y|xNsy3狫77~!ӀڀZ{w筳q-A8qq0܆{.cr8EUV%*|&¡INYg=![ J5zCwTG{ar s^h7 a6o dnf^6ڛjv/6 %RͣN`,1H}?IaQr~c@%;E U u(x@"m 5wFJ-gE{H K5rVA8|:|$A^:6>28``:w󖚖MNm +lu;u*sBӮ[V=Bͱ略z P"_ hR_% ֳBt#=3{ V ǙřUw?!0!p~>m>STP~QyK A-2P~hFqq6st(<"kipw>8QsAM]Aa^of]ufb&h.C^dFtYoJaZ[[`\d% 4;ٴBrK_2.N. =viui'GM}dqֵc4CRԟ0MRTۙI5i8% w8p`uNuNxD6 6+Q8XX/rrwG[u|N)p>h|I#>h8b˦< \h2) 0=l0 ^Ĥbw s9e;lV{ $}H0w+J\>@1 Q![kVC!NxU${"7 cNé\="b2+mM(S2 E!S ̲c"eJBfeȘdHy}ug-ۇ^Zo/Nu4s7Xyq;p%%ܣ5yx!h9?;?X--Wk~k|_>Sد. Dh ONޫS;Z7]E,5NSۗK|2`~ lϿ17%}@-Ju8.EKTLkkA0r@W Xt\gZ#}WDD=Ð7;~t= s۵b=>ČƻDH;Ӵz/ED 8Ĝ{ @HYGN^99c77}H5Q&ԍ q*+ ݜC9_eؙvn3;Wa(B;۳lUۚ 'Ml#^޺EƐ5F?7lc )򀗖!ў\ƅGj&HHROL_h+&(l Ӏe^D !FeYYPYP"HW)܆`sAYAe(\n'X/C^U|k?TKc]Ek_'56nRuE%_pn99uu4RM81J:Ga44 x ӕd$?![Ww:urPnzd74e2d2;&@F7K+dC c{5Ba4B~MP4旈s?p ,̳fзH<->VKR[#Tt*܄Y6OzQCh'ƆW' l'0 F,vR<.Ľ@ lf=}L)h(/tmKoӞNm[_eet\jͲBl+JQ%@ܷ .AFX`.GTfdunnd6IݱpjC'58''PPXǑd%D+X\`A`AےRÖ́DQxr lk, ܫS;Qč{$5[lc]/*.x4{ !wb3.@G#ZLct-den4<3['Fv@ 4/! R ؏3'KpYHxߜdD#T$.wZ)CS/AG+@;ʵvvܐȐM/%%׊\~T~RMTINKj>NU?ө聾^H9ٿg~3/|HMω vh)\XȿozGl?WeZb {9)vx.vF!O.v !g~Ι]?hN!>EZ~&q]ozr-=wgXa@POD(VXOFc/EbU[5I,Z,Z+Af-6d 5u>U@ݞܞA |pVYLYǰHV/\h/ CX67rB|F>uNGɄWNҍZ.\k]kJJL ؾss ܈D@"ީf:?Kz\ɍ٫SQ;{;+,9v0#%Rr.'w҆qd9R>jV O~Z!\˦ڃv433#; 1QC[ E6i[I#m$}HHKB`y_gB.<J4;Rα1WXHC# d\pmm7L^*`mmM:ީ)"?[թNmé_Dcs!Ow/Q*6wa FDϚ^Uv_(E=,cĥUKDG?`2R{oK>>͈HF {13{ h6z>p|RI<&e-$/Qj_&qXALv.;;ډ|;$ . B\r]r2rs v4σɿ$P0q&5:өZGYeh>e[̊kԱm 9$TW}*2]Cs^ǣ Ű"k9W*ҷ+K3,$_**$P܃|o[@Nx/gdI{.hC6>(|&zՇmZM095htw9ɱǹ̹``FAio=*M †d;yhĢo:GwwO;N^|;faۆ{u*aSj)p:2htRܣK^ؘr$%ۭJLlUWt4r>fI[c4N3?^p)@bH+Z)Z.$$Asl4g-MvkS?őH%ɱS8%o{70k?gic Df !yXs_zuL`kL6Ö~Z'{_ZrO5{1|D9MY~5|~na++^?6}^&٨nrH3~w(a94Zx%1=DsorQGFކ~~ OKGVk2}ѭ.]3*_y4opX4Sʜ1!C)sTT2 bL!2FIeP{>^zZ=kuKL=qZIt޼^q鮗 y5"VA^1&_#>֦-C)$+IʽY?aojtZ.eI!aS:\IQGJ%6~@ԥlTsA{4y ;pF,0E~(c]D r+,%Z@`g/xw#tt84 y7E2@E*/)%sy:AIΞdnH 3X-C:^pǥX#ip޸e?bH`] .(]-:t;ұcm;8:8f5ĐR{MmP=xݹI@ )C /8'Zt mA4\+AB33+PR+d-@P0Mp 뻱 ^*k6^)EdWnb-3yS6P1!g"-Е >t9M@^L#t25eN}QE`c"9+P>_Huaיd, &kmllWU4E&k`p8`ԣZuk-t] Mv 5,6,\}!B6]qHq,D?N֜A;|$?ɇ$#jyKNPh}2aaA1&_"G'H'X!)wC]xvZ?^RW3U:,(!cmiHi>UgVz&1Mi(ksO"~&7טd&d&ꍱ fݐJ~nw% ]nOXmQ@ mR@UcKfwfr RNIZ>_7a| 0(~r,ea0dUH|lK׾W@hW2##G/_nU@fgO2Σd9HE ZTxj$ bDt00+8=χ8E9L>I,YZ)۪OjW8h@tXaM->.2-6u5)witB/ _}|I|Lwr4 y&5gM}Wͩ&1<$\??%V^Y+rAs~ij@6``:L02S]&Z_۲G >iR!Iv.j4c Z?T6[wmggȪ)DRR:E{>R&OBr-q-9􎥔N.|X5˥b#4`gNBeCI B?kgTN Kܭ#>JN+,3RggZ|x)Q _R?Wٹ*a_ OnWoSuD?1CDsDjw/͢et, ̫f֪kN6U\ưYqza9]~3p jVvuAѡUz(}rnD3$Vy[M[d/Bf-U)-OŸFsXp@fV%lw#6?O|Xe8{*_#}P{,0|%pp\}2 nb9u1tsvalШh`azw<)v(0**ώLT/$~um/陷5C**2n0n@~HLRٮf1Oo-Qw+Xݚb.6X/S髙 ?*$(6z@5kmMF4ax%sOq;]Zg/#&P[R&%Sy>ڛs-erO2 'X ~߳N=5j;J އ3 D_##UϺV;ECЛ˿l3.|0gߦ+K)r?*{TFsfXhY}6 ;Z>H""73̞ZzP}d`>H Oe*c5Sw\#,:D,ֻK|\\s>Fyw:_@v?)޲@O}Gb˯_=AحR/wG טq+J;dCUwjA'6`8H`~YO:@3pi:WI]#=qㇱH?$ :$^ ׳jYܻbFBZ]=8UllAd*RB܄Mg48 ]/SL9"]ӹFJ_ZM2Dk .p{Xt ŏef;ubȷW&v2^bG0_O۽pHON"Xaw0,xb+ ԤܦN)R#"毨'%m IwBAm;BQi:<}4 QH [m=@+D"t:RQgUdUr-!Hjaj!&՜vpQ&d&XWKQ Y{5ܵי*J*W!މ?/|]йgJIDN,(լ9K!1""K1%N=+#!Mv?PGoP˯$8%evNR9OH$A2ޤ8s«eLǎG]OC~O7<_@f u=)rtjs(˧KѲzN޶w{u'f [bfmsWssHMDX3swv Е24;fA$.sˬ.-hQ<%}S7~GeBTʪl)lJ""I(3UMGFGI!#}}:ǣ<{7\s]a{8JwِltﴩM҂Lr]k_#U\oqh0ڡke&{7;\D`ri;TH)}i ! ;"=Ħ C37MUuKJٽ+C DJXphqV,g-llh# wSTZښ]-t'deyyxy|KK~z힊uyʤ-ַU6x֦h͇"~t,>xO@ ;`-S)_IX:SQ, ">W{oHCBC$MŧE! $||7 %7?#l*&UnT7Ht9pz. >qt6 ݱ{#p-A-A7; :  (D(DSЎ8M"[A^Ӧr6kj"|YW?ҀKƨ!i:mj@R4LրBPΉ*eAiDS f xMuH_IքA&S@:"=Q82&QKF_NcnXAX\x^ y$G3o[)3h=ddGq|7ò[pdd;hk[܏!i.iSy[{j7n$qIOru@ U^ t1Ǻŏt&9&BלW7wY XQ)g"C6\=|_()~bcC8D%_Rq Sj ЪDŽ2z "ᳩ;ɨkZ 1r8Tnϑ8Ͼ[alBBPCTe{ |%((/--ǻyˈ\Clʝ8f:w- V/:,jE?Z"{w,'7NBE%\٧BU$wOG$͊ShYa6+VB6J }aIR `&]@QK *ccg1"+..67K !c!c!"En#9j `ĤIx̸ LjS^e'V}.ՙSLӋp**55 6蚟-"zgtTr9;΍hǐ5#R }cXeMkc^}#)9Nwօ; !'0mL;\Cj/D5|VHN}7Q""h+K6! jEghpip3 v3B{o`W)@0IOo3[$DQ.ܕzp£4sk{\? _qj ԯY?%J BwɭnlL|$I:nгg_o@#אRwRtA:;mpkSEᤃdBBtDweHn6:y&1M7"n0yg^^?d$ uܣ%Ą8~>>ΈW6tͫOhhNܫ:Wu.Ѐҳ ҋ5kE nMs gDs G#Yn*+pm$~nz #Z`!pL'هJi*V$AG~3h=c="9qJChiҽ194 T;w\n|8"} 8kƗɱtmZ?e]r媄yO4y@G6<;$*‘ND]}Of?_ŰӦJ631ϗkTxjCS0mc⚺P*kQRT5`y LꉾCDUڋۋgQUK""8utCR.qPIf4e4U B68ud]Yיg\5pm#Эj5xEKu=xc|%zJ#"3.3.>z?G[ti6CpS◹/ƴ4 Q=;Ӧ^lm+)s .fC)͹b9Pjr<;ёqodk)ӏHtY Qi@孍0_ JڳBNJNqxG:=3c˫!Uyjkٞ@N7ŋŹb梚sʙtJI #]u3IS/t4ttơV8**o9ut{&&y`{gj3%0EU(ڟwQV}]̱#3aWzf5N߾ha;gk6)bty'0k{g(> dX-Ujc%w>QNg&Ka۟j/b}yH$Ʀ6_[]rκ4믵Yr$PlöVY(;*>3ohh4J^$I~%/6 ۰O'!k d^snF+ӑ)h2#NօS7؆mfuQW6%)D^k"s-53 j)]Xlö?|̽kmNs _V`1VqnShR6VgM#d 1rhh8nػp2̬dȨ&du]ddQF" YE?~u:?uWKzw杛\pnb|S(?ҬeP.ڵ8@_q" |yN4ױ562b@~r6:K { {i.@T!Fѐhb02H>n2!o)Ȕ6S3(2xulOnټ<n)hYDB2[+5CC<[y&v>SKFש͕کS[[' 'AVWI?pD0 ̋_ahp&%#wG !fjbt_pt*t*u٤p.|O-%R~髄+W u!!F2! 4^1} ,x1(z)P@WyQ[Eg$Ļ:اo""fI ") l ~B\q M MFk/N,N8;oQMyN%\pNTJf7"LTz_pn | p)D$'1:|xfSԑ0_H&X D%7r) 2U2(%߆Z]X'@GOML56F$|I "Ӵ )T>zaeWpQjQj?k sp8]WW|S՛__?Vީ'4 ?HAMԩNݱ'%؜z?2alp*i\<@<X_~5մt|!6=Fd8ڴI>d niv#uu{MO #Zjp405aЅO:uZ彜O V7M#ljUtpq)7i{c O54ܘ2]?AAH;[w8Z)Z)d]]ՒՒx`yvM7|Λ.l<+rN ζ={[3b W{1#Sm#'F&ԩڭNQ{Mиa鱒&K.j7,(kҗu^McV<ѯ} 33#J`wCKm23tmWFF!'V 7}A;@Dcg ^ 5HA}ߊ&jO|" 8x³_}{dp; l/χhxeJdd7|9(#r)/'%4B88!Yyh_uX%d|̀ZJ}S~gSط7YMomlfjjvsa nbBQi<*ø%ÁebJbJ]]ZƎgѝJl6Dwujyk!ue4ޙ |CC4!_VMOtYK 7P)A v':;bVՇ=ek_%<{NB [99SNɰZpcNou-t:%i g_.4ep:bWD!@*oo4`TçPMh`;y0`f_oB@|hXx3;3!H-E+Q(8슼E5к\n89ZsRW{wmJ8jOU1ދXwsrp# md?`REREOMM Ԝ(RLWy6+'1ꙝ:zSz ]:هѴGATνwTR#QˎtB/V|B~ڿƶZZ4!r:05+@sR4ueo hT<*-/~e~H;$];e(`09 kuj*0>~LbY f~X~\'Rv{w\Q/̩SQHao:94+ދ1I;Iʬ\sީF.utUWwԛNpק0jezKYpzDWӵ+~:0/$NG.`zheOE 3`¶q[(l,uQw)Clf;YY44)IBȐ}MZ q O b+v#kv3( ;E6>/90ٳ=<<3*cpL -7eiReL.','7q㐐ccx/?#Ω]ֈS:gAAK;_A?WW 42e`$>[3ٲ P>܁qy6 =#H[J$VL4L1' >",Ǘhfp>{÷҃g UzibNJ5njP9~J;soUzU^+Ć CTMH՘? gh*gh׸AlMlm~r>au-Q Cf|9w˳y;WϤkdKGu}Dvٳl g$5Ri&OgNn3d%jC%92AXxh7nu]T#;9tUI=ZZp]b 04H"\'Z%Z3=ʋ }><У4]~ҋT@sÝ˅&&[x*ip7˗GKr%`/[(bl;ZZsٌ'}#W蛿l8.7a9aIZ(n!x!HHTf\2 Ztzo8؆dԧ ;җZwntѭvif ;po7S%/v"ccUJ:}^.@6RXu6=t("cXh2 k-RRlqML15Thf'i~e(%D  @[[bW{)fX wm7جBO,bCOHiwx\Aw7nY#&Eq!˦ U߈F= 0" y@+VfGXr0<ΫfLƿ ;|`r>L%eߝ=M{(@^9v΃:)Z;g=zk g~·btru=Z|B'O9= MRIl~U1Ҫs*Ho7FQ9H4y'a YY $ <$W-$(  ?M`98^dsJ*KQZG{Vdnmvmoޠ_iJ SṠKAF0}'r/h,hCDODoV_)*  Rү;uq#Ik!5-A9f1B(-eӇu=J`Fc&z{UnCql_.(( LDU,*HU BgWQq} y" ?\ 9Bu y\رQn) 8 !f˩g.(guG(XS$X))ZZB+&pssF*uu ~Sq)6/{5^;uiS ApBQ]$mνPltŅu?Or˰S7`(IEFHaCԍ ܱMFAA(zKHꥂVC]/Z %tH C. 9=6 $Vڐy˲79Ex4.\IxS'f;KL* ͣuS<s{bG!ŢߺcB@RspEd6r jqo $SVRn'O^Г׷ZX6nc9:RԏIFЫKklfA+NU=w3Lse+NjYD&A9JyYLGKvWU0QKyi+fVl? 늬8q.'Gw+Ͷt`Ml6""Kf2vx*W29--a plZ=eNy8y҇Xo.oog~~&*&'rr Y=8?~U.VǰduPJ ll/ڸ|2aSOyd}eqȝ޺u pQ(Y=|R {0ɄJ o)dtv8xhmBR΋;:G`8 )J4tQh|qΩrNRF"RN-N}>AH䍟&,'i` = CyբI`ӴPdfZ8Xl/gde ~Nv)x4&U'pJsNziFݞ#?<簺XΪ|yNmuJgc\reWQUiƞ/Oҟ?̩/ˠ/cQuhi5 {>O{XѰ4).aa`ѓ= T 3Ab)9IlBȁQ!&eNܝش,l +;*g{{̑j4OÙEF>9JQ8zc$.//^A'OtF1S d+ ҎɞeSڷ:5\wM{wJ0p^HLǽIg=m@K#aY^ Ts+(SCG探'A<p/JB h&!MHt#_ 36Ok*,kD7?cTS)8W?=؊̧=<>c{n?_VҶSp]G$O ˲"Ż8c9c i iE8^q lZi>];;A14!M3K>W'1L`:n8 ǤYL4nPD`ƬIW};29LCx oxsz{s#C!/B >"U ,nt6 BER{ a^B "[2uD{?_1]}3۳ϠXMIX㉷ Ya&@^ݥX_tg**x67>qd(Nթ57pW_X螲jYL4d dԟ*Ntk(Z4o.4%s`NDO[\,.nA#rI'vOGT]чlBqk VcH_x?Y9l+n%a#\W=vJp#і=h~>{{Cz ( (rhEhEzz&|B\0IK|5JÕ"544q5&}T;ƕ3k4?;!8*>ujpڿM}S8o~jmlլC͏f#rye)rn Jxb{J/AkD/̢L;:#GS 8f4>:sP3ck*?*5YM$KvfM0]}}:KT&#Zc⸖4+{gɷvoEύ&.cAbO,8n&{0Eņd+Xu#5Q}/숪&kܐ7mهt~Qf.qgt'yiF@x*?v:tn[Z9cAChyS & `&"`Σ =2Ȑ!2dm"eJBBP2$DH2 )c$CEs޳^]s~cQ[QΰVj 8k `Vb?]C; I|+ S hyMkX'Tse1nͺY 0Is(P mV%&gVyh۳jF$_Q[Kt!ͧvvi> dqg̙|=D3II=lala*'pd΁ˊcVnmשJ38 GfBa?$ .{j\^Zg+ ]ז+M ^C86ccxX4(hӛN wזeVVvD= dY$`+sjHmuS$}tMBR.Z8a:gއ%bFeeg5I//XQ,#H5KV>~~q٩S^>h?A'p.{~2%'x?x@s@m)B;N=\4qaUx?ѡYQY֭e4e4X$4cc D; @|i- _Oygb?ML8nveqhr `*|U Ѿ"hCqD=e1p4Xi=k hbbysXXcc4RkD3QQ]IYS_>_>cNvX=AK6mN -h2zv}Mpr㢩N̄ՁDy]NU~0fd?N`##;F-  -<+t eeNN׮Z$2' 7{Мj {~]sP'd_r%>w-USi-.*ޅݚԉO?;y4jTl "0"m*m٫%WKDO;"Յ5*Ϟx]vJm{˩<#HCp}k%Qɜqy_K ZDUߊ^FbM 5ӇՌYl(XA6uV6sT.>!lBV9N8ZZ&JY/IzĬ$TM8M+6Rrm te 0 #LνlH,"jp1 H *,%+m.*ZJ .QVV&@JJĕ=*R1;! CTUG!vS8sZ0LON?]0ttj}+@*xM3+7lYYT&_Ƹ-Pȭ{au[n9T A~|MM!h[S}hyĐǢ@WZpJ|9Kzp~OJM٧ا<,  JMpk-S$85Rڍ5MFQWt5+|g/ KOQۭ;͙%|mYdk21iwn?*z!xN72ss"ۣvJo}9X~7uuH'%0~dq Cњe[V'p6i4#lJ.h}ȷѐ\sęg6E߀XB^w ݴCODü$|l{#]!ú9Ɇd+A<[}TRR5x +`*`PP'en i+5~ GNNgy&T;%2Iwz"v)ujoO q{w0BWn~j\72ٲB* A1@fΉT>~@6b'bUؠ!G8owi7X!*/nD\Wy:Rݼr- xc+MEC+*<-0{[{vóӊt|}98\!+r~eZc&3&3&-I,IFwj_{^PP) .2!ߪSϻs迺$as\w7-yD%[ \-Fscį $%htjϻ9-9-|DnD; pxDYjLU2Ur#~Ȕrw"SW@pr1fXUY"3& $~uI.ܱt+pn*GC]VcRWzCp|KFRF\{$;5{+II"/fձ;e1Ru_멷L}+JaS1SNx$YL\JS ᢘkT^! yT=d;I4CsnB(-ii mԐ,"9I,pxy*䋾z l!BI)H)C"kc@CS i3Az>7K[;q=3ضHnƠ lVĩnڣpQQyyB $rg9]]Zv >Xs8So/}`$PA:jZw! w ݢ8Ji^g!B pdRGsX?9Opqx: ?;RJ5\rk % I^|YԼ_s)9R J0tLk5'w$HHrggIZSc :aY5Z99 ЂTyK`041@xHwm4!r;`UȂ:ӹ~淭,#7tK׽_ڃp^ǽ>C "pq nn7}7})>KX^옴BsKFsg27h*|[ٲZj}AױF;<5{0U6_;wvB;x+qFK{HQ=/Zׇ.WɱT,_wHLXJ :')aH m 0Hh$fSbc㦾\$XLbh8 P<2MtLLzgOQe~Q## pΪآHx2MAsK2!rC-9j`rv$jLD_qm*θ)A)whlpҙ Tb$: c'nZ@%~ sц٢R\~.Rx+ /޲в𳤳`\;e;~~]O9)ٖΟן+M9r;e9N6^-j]O!:Hm$(c U>Ž)ޱ/~Hւdc=R8\ZLˆO:_ WN†'NXm2!}OvDxxܥ|Ut0 n$zHيf;`_7t$r,44N-Wz]l{eo=On~v)}Ŏ ^vRVf'MuVVzq/^.2wjWvHN+ݵm5 %ujlSrpGo/ѩy^JB ݺbtA2˖yO}Iy\G H~K@~9 CѺ#:͂=8A*>6Dg@@*D4~j87RPS৚+tSnۙ7!ͼ|ў=DMnuvvuUwٖDFJJHJ:uG[;uH/ʬRTtHN ,ykQ &Lelժt2sC<#"9agu6Ո sl1wZr-[@6Tw+?S1gAXKsT-9ҕ T XLZ'mN[+)ԯ, 8sa +b)p]J갖3[ocցQqyyՄݗJ|Ls}l-o %DL B#mJUUG *sl3Z.ܚub-[USηFo3ͦs5XZ=Fbj+ɮ 1)/Cg6>#M rX>v;ڢwE#A\.pcMT@J,֋s?@m 3ȿD9o4 },%:rB>J ?"7dAyP3"ኡPP? 1^ @[KAe-hl \-Q6C3bk$0^#y k+cŜupS`2#ZZP"c5N71RĶDڴڴa n\хw껿': j‚ [Wr|b6ÐS~_jP)Ɯ xtNH9}JqfE-DokQ(TNN4L4,C8I"l~"QJBլG)y_IxF 1B!Py 5s^Ft0#& Wv}{?)c'ơơ޹^[!`;!|BX"t/_<Do@aj=vYj$}jf ~a';oFr%K^&p--,EB}ŘANBJB3")^A< K3]OILIܕyAx>RzQ'@s 8)AFMk`b,Q.)0p7Í1!s?7^`zRR{aa'rEBֹp>V8izi vK4"ߧ%4I?䂣z9N.wðdU. .q{䐾=H Oi'HZ"8}|>.1hnKcaNǑc|@¥I F{ i3'P[!_B7B>VA3}Sh}ۗ2"oQe_%([^gsΥzj3224mhP"I5wL8x{ql/rS|DZ-Q=R:,SJV\ܰL$|WI~Ѿ6^o5ߺ,L 3"v'LoJwO-wC\@<5i{-ҁ@f')'%פfг04y.=[ +89O&!Դe^ ތs<+/_ z b;:"Q4@pzOe ['&m:/;3Dd56rif?Sqg}&||:W0 )#&f?~8p?ݛkc*0Y w [RoK.:5)SV9**ec(' #65 n8rZ/^ Cb !m$:hKoujqޝCݷ}GEd.Yd2Eܶ 22PB E#I!R)˕ɒl1\Nj>z|}__iN[p(D!)MFA眒d=LhS;ei*AA{}6YQQ/?!]=[r\9SyyO;էŵyXN<m2݆Z `$׊ ){V6ډ7l7:R8Ԝ?]zX=tԇp,M *"n3SٕTnqTJ.xIh `o9Z>}3SzUbQ홲${6 Ob[u8ɾm77j+vq6ژ_,wq ys߫۫wP([jE2zYq[GMl&BeXe}z6w?7f#;pqg =2ba|Cm TM/\, vく<%vLJwog(i@9#vm]R?`>`~ Bɖ,øbyoDBFK!"ی>"B9TqGhCsuU2"QQAyN gOE;!j9Xp mw*#@9{GPGyᇹ6ϵNBHb$м#N>qoU+-XELu pGm#Xbtn==X~qɋ2XKpϞirruLC2g>$)h<)IP? 0)E}彷3MRz :kSspQ ̽ 2.;gd#=p Q'#l{ 4AWz OrJ xs_5<<5QT6^=) @N`?x+q泬8E)?N 'p8|$pD;;l)ýf5Ni/tOO׆dz_>>i_skJ^Chh}AA&MM%ހ%)ݵa<&4RU&W}vj7gb~ѨY+$vt W[*[*{dkGĖ IsP Æ1~JbaAwx{QF NM>㬂 y%ؕoaQM]L.@\Ej֝Rs\˙!%o50;lI,mex--xKw!mY;L[e@ZG!j2{lhve΃z, <+[!va?{ RxtP5zoff%U%\۟R!|H pεεvd裂G# 3A($'ܨS+kb9 'ZDյ쫇ty7SL'm̽X DI{XfI9.tmy _`h& I LWe Ĭj8_ "IV󝂄໼ e(WJ FP =|{\\wi:)23~)9\wOwOTᓰK${|p[{d=kwJ^&2Cd@r~ݍ:{Su>apcOfݮ! ;i(ۻdvq0F' vW~J]]8RE E`q/pT(yѵpnϲ)du:v ' h}$% x0 <͍A|;MJ~Q}Vۼt) d?\g݉/}NÝd:ɴge}ݾ' 4H̅Pcn:yPL+\Κ@kh=/>mIz7Ghbʵ2uN= j3$Qd]jpQ?u -)‘1JW[7t-M"^VI;ޘVJ t{wf;_B! DRF%FKKQzV%_-_>=v~Zօf_;h k&*eM0I-6[3S?)krBXE'ǟM|糀xO@kIxLhfյY_!a+H# ۥ' R}9 f6"`1'9pE,&d݂GA7ދV;i- >.vH. ^ F,L#@obk~3VfZψλexG{}[_C1nn&[4Wk~-!Wݫ+%N劼I:.Ożن&"#K4)k%ߟ)1{] ^83cVҝK@ #-E?)6Y@}@bqyqyrfb4 J\&I4D2{mqwxex1$p?D4M &f %}H9Y]6!uNSj7Un}kKy`b4~ 83F93ggHA1u^id)9ܐҐhp-u-RdΣA2e4HDP2Ce*Cᘓ9')ip )Rfek(~Ͼg}\H3ݎ4j%8su{ ;x:x>Z܁d~]] "֨nH-Ztv1} '"\#$1e7WίVkAޡp9U(] 0sՅ?V|tȆȯGĹzSU ^+#zNS|ZgXi>߉8ail6XFY u\nD!T IJI0~ 5rC<ԞԞ3Jv0nY5m܀[d,U?7F QXM*|R ^aF+̂d_ulE5q>x{ .ϊm:g]]mAF/֨\4-=-=i4m4E/S缐·33 Z<hnJSrHJLaE3KggY$6 L;Vc 31`34[Ɲo fw(h $MljG Zd,4-4! ?Bdw'ːi[6~j.y8GBn]HFj0:DSR?n援bYC<={wRN Y{Y{yyU' B3DR\R\bufb ./g5D6up;YE/qBZM14eԁ䩚;o&h ϪôWbM0 @s6D=womJ6(XQО5+S+=qGMeBBe/-d )Hk-^: "CS??=7~$GWT!ql(ݴ^%3즲Ӫ]'mOmieyw\7k6 1C=m6=#*:l7Yk5n)HR4ҷ\A[h4z&1 &h`d"4k +יpk,J[`XoOCZ=6]]&!uǩ\pc D= A,+RCS9hw{FFޫ3A 6Dj3gVAcB~>eF11iiʡ__Gns7zC8bQ~=2u!9*o_?Y~~?Sjf{+Ĉɿx@QIr_l|qYshO2k$U?l篺yʫzz{OdfA߳P;vL>MRrL ir6e:3U}߳߳fuO55?"a?Lk=lu<7$`Sfqz"gY}Sm !;k4"3 wJ;$V\ l}|ӹMh;_H$ [9dk.?'`~GgxwXMJ!6!6m J&0.2Kv>/Pbg(o344÷E I"#}`HͥL`n|?x^wL'N{Y'7݉KƘ I>OO VMUBٽu#F wcL s $pA>iQgQG@nn&dS)wvHiE\k~ccYi<$zBgq%~wVZk +%@f2t*`({7 5t3LtnT*D~'++h.`deƿ-A 1Wl#SZ85j.z4 ʱt!dGFI~˄1 \mj +w%^WԎG;㖂DAkB4vؓ(()f$$AȦkve9.9=VS+Mp@:( M51is0h+0I֘{v05{,a4tԸzRrpvbc백YNWxzi 8"d_{aDʂL9{>iJeG ax q$75 6'Z'+Zvvw:">O) b8v sJ` XھlJE"j ^6ĝ+ek3Z߻ q 6pe H\#`1[, W8gi4e] +ۄ:S,PW im5{R3[4~={f3v*Ȧ*k!y Cf_Ŧi cq.ަ_.IC?%pf ft!tS'x,++e[t}?cٺ-}86EP_^=7\0҅齟yUO}l]hSKV+ ]Iy<{)u5sl13@d|AHJ1HQ3:. L0OSKaid-=@ڮ g[sdω|7ٜɔl!; Gq N> ٫/m8FnͲ]sX".,.D{erTG;F;ίw:Ԡؠ؅OoW魇T= xrgAн_iC{}ab+p[p8V* (TM TZ&'5kZCG-Qj3hU[XC05KbzVly#%))a)h‚Ȟ;:AOx?q8cXA\&<'SQF.NԨg!;^ߧ5FuF5h";5)7+Vٟ$KreeW!$*L3Lsd\4RPȊeeO706>)!C6AB1{jjJSDZOH6 a~5-_.۠}DDm Ԥ p1aMjĥZo_x?Tn2:Jr7aCdq^֥A)ݩrȞlȗ_cLe=d< o15Amj6yKcZd'{xZž8?1s|xf~SPoĐB0|=22[XaLik%wۛaTldys_ bun˭v6? Xk;+4dwgWhSM鑺IH HL&Qc5{~Ys\.#ʥ#αolLˍb't\#$4/dݽk@AC\Ŭ;Ɖ2$=[08⥉T& &EVtp3o4vwqɯ5ZVBӼ*°@~RʕUa3t׻/p껓|Ђr0PIb #<甆-ܮ(4 |BYbt8ɑđ. e3e=?)QϒCm6V0ׅ8LAڥ |(l Ȑ.qBREtdT]QV8ILU?oydڕ<jOViUUW&M2TU WSRC!3=F=FlbY+~w?+}6EkSCV8H喦Aݽ[7Hf؊o[dhtp1o0Ж`(A;y%nHa>)Eg'Ecbw(d[=E@u/>m8-7V=&shDJS5SSqq=hۏz>6 8p/KFF5"UrOJMajrmztW;U(j!B ~g]]ʵq,ZP] uw~ń&1R[ܑE@lqJbcS"GL>PXv[g!phoK7O_v3ڿ_EYt,V~:DSmݜ0pabbpDvԏP|/z%iȁ%4ZȬp:J81IEYNf~RGYdȊ[ڜ5isqr֐kï^I5W^NIؙ݉zYšm^.NRҩ!c!#:::)֧mwV{OknS6 W҃D{7Qv_lwd$2cp^eb$mp(^p9@4cjTiTdQF~>U AL~90IIA! Ʃ׎,d"@.K;6 S҂ƸĹ42Ktꌲޭx7=Gg2y•\*5k nrY ǢpTM´´3*DS+|So2 `VJnS{m=8Chw~+$6͝<%Qh,6JE{d*.V'˓1T0 Q^~@tBqK^֦V(De?.ݬrtq ޶~4 )(8UAD|=Adjܑ ~ n@nMG, @0qCvFgyeyQDB fW R@"p!gY{J%Op . !S߿>whn- yH[-O6p^d李OCQQA2vol9Qb"p{>Pyy.`.`x UO[iZ9 *۞c)$k{@ӕȍ]}ؚw8cnhv*.܈ DSґ:P<}ײַ֠=B~Gtx^N4=A7 v(d 훈2ń{"3:ru"q7ǹ+fZG)o(ӊۊDЅkyp2f2f%j ^FsީwqqssKCM1CR06?lq5]QNh3Pol -lʓr;4ۋxb5,ٞQ!MS\I)o_A:z}xi*IQrEӗ:}v ~#E4-گ'w _ԤS)y/&eɢfS'Rk\I秴 ζ;U3?IH M{e&n7{ī)Kԉ٢&6H?[/6*]rRwtQY*ŧz|rN_1|T[Hi˨j('b6tsG >7;?aڎ徴81ѱvg(dGC8\uWueAB?3 2 7#~sRJ/TVPA/p}av6NT(5Q(goιHM6~Fl $@EEe NtSifȁij:;-H<1JM]A ߍffuy {/@m=< @>9LoC$b.`>#~]%*bh#6K|?s|Hgog/A7OF+4lYB 0ZV힑xA$ ◛cPdOahT*w ~TwTR>*ٗt^uO-dajbzŐ ;=@M*:[ctD5W Lʉ͉MטT)en|$"+|\C55od-$%?*G@BN;̒'z~yr5836j/2^|jcl]wgqʌ_TLLː@l C:j NjEUyzfV+W+75EKDc`+ڹڔ8!f^ǍknGUEqO#thQH% 83 7"AM$/o|ɊY$//.tV~wJQZO}\|N2F2FjI^ :ى;qTm[ dE. Y!r\b*z-%p쭍e<妽k!;,;l5i#$ĽFr!d) #]'l lЊ6Fx-!qܰ:=o;X]/ I])զ&;C9@sL!SX46k9dI["ޕ(( v7r GE9'g/m\22ob巼[[ӊQԾ nF:98??z?TT;w^5#T=X^SV5׽q"U`+G{f)[ aܰ{&Wu$3kQ7SX>TC; d LRx"JRM,<܅R1-`p3A `sW‸,Å,$G󫳛O3 UFy[V|~~P_rjb-GHBBRvalcl;! yJv GN_הjS*ඇ@G!? }1p &{ZȖɊ" !ޡ`1{iOʈֈ2LR QO ۥ,2_ А((fXNkD A  ܸMԿx(s@^x\9blogceS|\NPR(}{UYy8J4%Z_, iK6/TT ƇM~G+Vj?Xw7kvLKxn &{^haD5??wFR4 Csh6Qwa r6 g!hN]s5_!53.blY6ߣ&![bPaއn5 D Iٹ #;"Йaw]vJ܅2L8▛t5%Աf8aD|Mg>)qAjpbE}<bĉ]L#\ݕϹ}tkRqq&O%LC\o)T5쪢MqY2Lyճh.玷~{}|S 3rT} k_ŝZßk-tf/fo DD1ca^ etP_Pp^X\Q\3)Ǯ_;trm?)09=ݝ}'@T,HPK!-gϐ`w{&jONdv+pGCRqH[Il:4P7JS7/C!wzơ ݀vr8@T]fen9+.S?y])Ǧ˦2B9.*ƭ-±9yσIJFō jj\-hn4wՁspϖ-1_.`[܌ɍ?'9"%ίB;y>кjIJMpZl:>yHo}[ _q!<#,Z'E(8ErL..r!\I%FFˣ㣶 ۧ(!Pj"6 Q 5UVj;3GYep㓾ܾ/]CE!6#{^Aʫ%w<"5XK]]Yye,M#x N eqM[}=?&>%>5E oaOCbgS矜u8푁CBcr5>dTAB*&/z`|>2]MXgbk9^:(T&RR>>Ztj)|rpO[JS P &99"c:_3KC(=}iM{ l7?[Qsf_/Y WX/`oEu$! #92a\55 /5qc!1R7)є_?K~UU !]l3)A#̃W}e 0[\4LVCR?Z}J=gj`|@āwTN/@Z"5tp0 )؅ݯ\١%Fzv')D?cj_-\O`DA5 _" FnX֓D%B1 T77iWm,QY w]5HC}COM!ݍ03 #!@'!*\ fiB&*20B$.~5`3<}@ik7xxHX*ނVL ѡ+``$||oh^F^Ƽ;So^\S~;/j7\~{nv3b1n,8.wtߙu/|s JTtp5w KCB<,=+440b+cx8  F(xKJ!uj1H%ua |*PɖO͆T &lo!!%"ؕGA]E=AX1bh%e{)!)!5LHIfߝ$_Ua [ ׋DŽ,:-:ީ"Ӽ;01l)N͹n4?:<~WsFi@-\[1, =$(f"Mq`naWyB67b]2ei ~q?ˡ̡%2 Ajpyynii:Gw&1U陀 uJjSC&dPU\ L7NR|les 0> (QŚd_&V q3qo`e{,y4mPW}}UHe$I+]ؼ$t)%􎱪 Oe#ҪYn-9>k*kJAT<[[ҲֲDX >[pɡɡvkˊˊ]mTvλD u=ҪﵓG3n~ ~_d\{Xdfl-kɡvYo5 aBv^jv2n禴ɬæ|GV)v'Iv~Jd^_v&ױ9[k"_\䪜"`Bz,d{YjFE˩i]tcq<ժkz,Uwru}Қ_rmN 57=~;Gj߼֍=dd'kq!ϴ&tB?I3|ez#Lqڃ&G:l-tU;Z{n|nq7 nKcZW]]lǵ"T8oeu"ktwZ]fRZΒ$ 0e7@bRQp$n>.*"*29 i> @zc`Эtk(Ƣ?Bw{\.%:ӥ{z~ENτı@&&&ȼ_ 74fB3gZ@?_CBڈeyS z 线snשc[Cwa kc6![Hi|| @œ(MB\/9l4 \ߏWN7V/hZi*FgIWdɝM!' HfW-!@Υx4޽8Hkψ'$^C#V[1Y(XżdR%xtkJJü&ŠcGˏ;0rC&g憄 =nQQ<+|IbyQViNlv.}{:Y? 0&on3,&śx'>PK ad!]N-•ޝjBk Z/. "+(% Q?8H t!1" ~ !H=ͭ/ot 1r6ӸzM%U۠ޜQofkG|%D&D'>ZZBIo64{C4_ =O}RbNQk17Phٶw[nvʑ_ \àVpD#Ԫ3>]Mgt|}uZ+~i4pCː$'c2&16$B#!SB)2eY %:{oNj9/wž]iK_]8 %8bcYgYwu5*NhHKG\ݐTNN ֬VA[Z8z ᑄk"ŷ=Ԣ<};s7=ERoƫ|D"n[9̟?iiMV͋@(RA4o>eFd#.]b1[5noc]f%H6a4,(erLs煽P7-cF` ,$asawa_hҿ̪x "%s~A };UPVXD5\@p5s[[\H@FEj(jS{zĐ=pG"<6եAdd126EGOH@_sLsq9*&Q7]ܒLÜcޅ#3l*T,&>Z-i&,?ds]4VXT ۸/~,9|O`yWesyߛu]foD&50*RKnr W=mBXrq^t1zL&Gͻ uTT긌-8#H %nnTJMzF錒 $ń2r|-9(!':ny:}]ff?>y#ׅ-Eg*OHlM$$ґ)m$$ii4BuW\ MK^ϕ>҇}t JLfUp).jwe߭1*+䋼xxnzhju?5KKJ{C`Cs0O O`I׷`x. PL ٮSJo GH"YWKT:r\1@Aֺ! 0|{N%|j _ƶA܆B+X78SrnNBQCe|dgvAΞRo Hl҉SBcnoͫivva0w/y#DW9)eeɃȃNyoȓpBOIUo)N>;uL#^z2HKVNs*9E+y-(:obxZGc=xo0hh WlkhM@~ݩ&{HI !*!47D {̡IlWn}TN’w#gtrsg~pZn-[DDW7..pps E3]<6x7x[*?U~ڒz(ݩPxʟ'FNs; Mu߾pəs2 Ĩ* .G^n3%*{gdDs]:K'6;/ѼѥqDO md9_TcgҒ9>>m7Fګ7@]r*Hބ|@q dzZ( .7@£=|ڤ_УMگ$s&MCk]q*x%ÕŧŇ~ȟ&z T+0)0PrMGGz'x=kK-U^:yϤ2}N)mvyE1\5) 4?aYZFFO[; ԥj:0\[t!GGy^;Ey!.& rF:=a_ЅgഖeY gtVbbc{Z >oQA5.n.nsO쫎u5>wט)69gOVQ۞!Blvu8 id~ȟw> [`9 vu`XCx%_kYҾ`.ډ6F;.Q SD8\\,9 DMj+ Hހ &9JtT#|8둕9ּrZZ$iAAR0+9K;X 3uEֽ:qdp勑fD: nl~a#{hcRM-EEd@5-9-ل6U @x_:4$f#]|L2urk7Hʘ˜*?4䣊 8 g0PynԃP~[~O&1:IubNGj2[r~gZÁ|bsX-RD\ffԦԦa~>8}8ौRHyqg6xk%>_ؠ( .k?4&QN5{~'mddB6Iyxku6*B_}Fyǹ=(T.ʳ%lO|3r"dRiR)mi[x9H 0i09<&?33 ȌاE)\g€aqb͘n)_9ʟ38R$rCXѣHM$URg4ǚc-2>¥yy;nqT21ۅ|e,yjh6xԄy˨0T 7Zhf>i_]yRwz ;9$;?^ @9i鶁Ĕ v^!$) )5OO/ zE6rXqgqg'H=hn  p;D`zT bMR$ҝyL wM\Ago^6{l=ީ?Ik:|,oN_ώRe|<v;CAH94OÜ$f&Mo /k#7`x[[{S JC<ʽ|){2b!oBZ Bl1\8;;f~b4ZVډːi'Iq00hJ` O&1]ۦsɈ{vv߷'棴!$Hȭ%osqB3N5 Ph._ɥ0oorT.L (JLu>2j'Fo(:1$J CvO֗ٮ[Ue7uWaa""wg-^fp ss> ^ӢGQ?7ONeI˄d%G$HWD7f?⥓Ȑ*"RM}qAbcd?~ 2!mfZ TW}䪳޹>QѝgԾ-uNՂs`u`̪̊0zVTQ GK틢HH}P迾BN-2^+0 E;qq db!Sg"QO X^Y$FhrRu?v*)wWf6X_|N=EuN-v7+Ods֚2>3?;m _AAW(pbN DdҎ]3E 0[cȞ"].; }w9 \ 'xYZKmEf!pvB>!AG܏RLj ?.jmE4įy2z`U|' 0\\#2YHq&Sّnwδ~^(pNqaqa)L Uw|Fz+m '-НΜݜUXPP%He[UK**L$|GūB?mՓu[nk3Jj)ˑ*} tt?yx3~CFBouݵcK#83 .K#>PFv)hbw{m;G #FAgn>uJoSZ45pҬ(xx[\rpHt$FdK=d.â}=}h|mm七J;#bw@F_ԟ1A令xd̾<'$,;}dHmfnxK%߉q3e 'LO̍ɕ,։`r;b 鵅$4kA*O2PC1S[!!%Đ1:i8BQ"jG )"DecXLƌ -S$9"CVX"sBg=ZZϹε9|^wܽVXg^Ofʖ̖̪b?Y஥\KadoEA'y.. EEG"xVM0-ReoI,@lՔFS,0x.\_|K,P p5S)=M,DNrJN KWYPT }& 8aLSQG`">> 10?~QM89ꍡ%%_$8C!FGxTG/hS,y".$<)NF[F{|ޢ}9ULbfjp?hye%e% J!F*)fIlS[Az:[݉?E[yҒ}NN\?ߡ!.$' s@6B]6YЎASb{g\E90Ϝse-ToWV|W6]iilwu&|{ l{4'm{wܒNgKA^6rt܄Y"שCMHD?{{̞΃=d)-ۦ)h z W/׭Fgt˃*Tx|-cIhfbdaqmWbvQq${p%{1)Rrl~AB|#:=^ce]L^d>n:ߵd2@$ ύv`@EBZ8.XGOΤ/KO'r(ש6dCBvd3yh44}%ӥe!Vs\/^b8qcyѻ)~P_٪)2rl,d5~+ c/px k&HGf`vxeRnGL&ƵuP8coŵY{ZoIIeJw1丧9+8K@S<)!H6ҞZWRoO+YIޜAy o$$FRFRp'?遀c`d``bv׌hUhsOjoՔFSw_s5r&9KYFlU'6v9-0@%%7z OR/M4> D!@'℣g@_dܺWxn7I9$"[[>xDh T Lbnx՛Wo>1k˂P:9H ɣF L*n0$=.5'f͙ W7,U^^_N`Baװko̽s $nqq;/M:ʳei#nlLM&|FS2lg0B.}ˆԳ.xr\t郪ۉ͊%{#4 mxo-XI ?>'@% y}s8,AMQu$VOʼn) R}3rd|Rp0NCJB0tPDx+)ma?0f..y:cuvsAF?=՘p^^B5B5*iUs!5Ĝ%_O|8,#,X::~y-_^2no+ݪ)LRBY/ |4kgαT ,b?D;QO9<&~ZRd c_-7ox?9>ihڢ" 5hYv@ж|8KG ˯O00jg7$uR9N%ս@#77;5{9 //]]$|T({)tJgij`BU2=xܞ)\#Ve0ŌK^ݙE,bxVO`SS4k%EAL!4xsflgn4=Z*E 3;#ҭQ ˦l6``1XRV2ybHy 9N2(}AJ]QUy*o?댫lhdǤ**xe*R]#gp-K-K^N0G TV7Q[n){jp).wOpQxd:VoΤHn!e pd{ʊ̊Tm #t/EZ8 zYܑi8_^kpr aY8@Z-)⃭! +XqRƬ)d]*^t" E)E)wOɔYY^,6UaA,˸%Bbkvkcc;;PCMiK rMM6q;CwV3ϞPp,x,1lY'UJrPq?:P]L[bΡ_vcK;\zE;XBx%CY߆W'ﶽxݸ'6'XAfGguwZoo[]XF O[E%T*߅T#:UW V_Vdn~N:V/ݚ ʧϡ+޶GkwRvZ$=59bv ̺r~waj87w~H7Vg}ЗdzJ6h\i NL{~>u'!,:N_z}=264 RY&-ЂhC:o[K 5VޕPC1$g=qMSj sA4)@}4CY{#٤|o45 !rFUup[Zעh+cׅ; w'NDx^BzTU@)w /$wʒCsL6lVkRs [wP rlIwRI.*$6{κ)/EZ(Z=HYN`ZIHLRL2,ږPs^o(iE{s((TٖyC\)* ʤ \\-! Hqko8(~u5ʙihso'|e>ȋȋ#1Ɗb^spQvu}*y(y*0›beL߳㏻]wk统g=+W^H5jQn)NS yH኶VR?t ;Б3ƥ}59 dcP=.ߠ>#bô[[8+c@c1 $/2kտgOU=伻%)__Q,B-^(S2:'*A{Y>>7y \_RJ'ψׯFk/ˬLY2l^+Z(Z66S;ͤ!ES$IYebe٤){Wi 3=ם*igLwQi&Qg3 ޫݺ%2Kg7[(cAwpf@v?q?s[mv}`o}0mqY+7c,4pLGɨf~ y{4oıhhV8A mT|pk=Жʼn<P Ju-h= *~|kG_A>.~^,Op7:7#͒j]l~ߚkJ."{EAs b*IV~ .##^VVkbbGN-MbU^b$:uySpJX0ZV I"4 ; t{*̓2Ẽ4tjtFB45|! ÛuJ]Ea;=X l \TJBKFQU.f|ILgC!f.9BaH`"Gh~ӛk,,+^'MM;.{\VEyb5zI80o]cY(ssg6zA[SW22ըV ck`k`g?1_@W$ZQ||НݍˮEd nwG# ʓ8*X!_vv w2j93sT:`Ucy]R vW!ЀxJW]74r [:;$6(D Ol>hTֽus}.%.OQ(^ASȨ>h' dqD,:#p5pv/|=qKufq9?iЙ{^X!5&].]|ptttd:JlO-0 dqJmJm\VO* N;uV\@}6 Ia+]l\7{ᢑϳTZabC\ 5WNRS1+ԨۈU`RR>ma=iRۃhkM`AA :MǤv$%Z`0E}nTT;``GX,k}J#ډAQ[[~Wb5rVorpJ%8'ѕ=aOs;\ISIS*SS"ˆLCx$"̪LUڮSvCȥ=DR)ēj@|# mT:p ]' @/%[fC>OX %خ_8h_} :9-9B}}1M$'R$ɀ67|5K$by5} OݓCѻi,4M:힢YNN^w5H##obd2M!e( 7Kꦯ5o;yjrcv+xNƧAao:ew>챙GfzJ}/u">Vj@glV7m`]F#fZM߄l8E.XOO4U-ܯFC9#|-$7$8gԡ}PfF"kb%d|ƻiia-ssԕJ-QP%9cHQTvURWEàՠՋy=5G->{wn99*jq?ޕx#ѢNz@bXEZl'||4ˆF} ,U=c8ggQ2ߏ 3,νq/ߩFf;a̹~!z؈2:x$MD\Hqbpp OΝu7o9-DKl0ׅ.ymk7U+ƼKI1I1e|ز]/K,g8rgi$ 2eUpJ헜y϶fĻX]n v0gӼVz W,j (T2.qGG1@8 ,bP) =p6dKQ8f'{ S Sk& ) yv]sgbܶ;Fʹ ȏk'fkNdl8%v-G87Ã5 k\pKy)CPvf' 6z׭AsL$7})jvW7@`wή[8*[+O-әceܓiksdL(> QMq^ I3Y)8Fw_+w*隢 #j΂/EZjT@rew>!_,lipP%/$S Ǝ gɏ^P+vwP}ٳfId-)D1o'٥)&KBFQY"K5{"h=\3/weȣpFn҉k;:oۯRe-j:udEK%u3mf/nYI"'Rs4xA6_9緑^CLObj ߴGxcr`._W~w|mIqV*r 9:nOIh <æ]W^˗wL2R;88!Ð)!/4qR @`W)Ӏ{W7HZɜYE$ ),b/(-JCum3==zzlJgT}: &.9[y*88^5;ז$Q(] K])AQ<:u6St n ^_<۶㱚p4ppjV.XId6ܫ<ӳ& Ls.˽N.=x. 1@"G& ͔Ŕ:!䅐!Qo-5' rQLGI.; VY1)K}g*#zK[p_,ێ=! %k>bGBC**nZ66Yi;X\<6~ϵNZbǛ띚XfALȜ.vAT&NRiX3cq=*7ʃ˃CmK穼 :[!AY aNNlݶ09dS@[@/')w;SOɦ E'ҹV8?' B")!Ouuv"w(uFns v 񷡹5}b;DQXpD۾,??Z>NUlmղ:YS#/vx-;Ej{Z A4k0vbJV81A(Ej t{y+g9?P6p~{u=0^%Y3ĎS'#Qke9㹴xHu:`%>y}r-u{4yIRk?ssߎ.9@{aJؒؒtL^I6C67=)ϟ̏䖠S_۱Q|:U}n,^~f7ډ qwaZ%X\IO0ǣNCx}E܂ЌwV^kH8cy&jȼB'I 셭7N'8Q1p=+{@iyvJ.4RD) &:ePU9: { ҬҼw)A8 EbVb_Dcc''|[Vzo02wJ'Z jETF]{5xq*6H;H^0Զщ9# "AW Kq*RKGjz1LYTMA'430&y1$Ĺ5S@i ",x4$4lE(舴 n9LC`k&SяXbv+V.V~ġe Hlj ؂(cBx3hZ}.'tN]\WpD>6;"]o;?'fU@WI8q!1!ov %LخҤ'P1ѳ̳Li^ SGm#}&<ݶݶy~2ߞ9B7[oN&kDBv_ Hv%c *cDr%}v}Jb!b!Shtt ؚޞax XNppCiim:u+<T4')5ѡ6[?RՑt'fqE\ٍҳiM0Bdl9' 7Zí=lCS1Ulw pɎ3/y=ۑ[H^Qr4۹f̾shkRCԀW'/"s{2CK ńzPXjT ۝ SmfweIAn$=hƄ,YHA4\]]qf'd9Q"ar.ƽh>!I)Ҥ?H6+K?ZO{iq-kYi5Xb%#=hFr(plf,rx;p&|5r7DsGlg N rL^IfeV/ui[PQ)cs*F 9n4K!8NlfOV@ƩY[ħ|~bLZS U^_ӣowíg67/-XNUuȮh(hFݳ{ƪ'?-U8 ǩ@M wt F8uSpJR>k r6hycAaU!slF%#̝u"ďǰ.6*!I٘QXHe喍:uyS-~W0Tg!]q cV_s޼;weFR cE1W3W3Eh3-FkBHtD$"HFC>j2@ 6ǹP 0tXb[):!WN*9zRK2qdos#ݧ E..zXp{lbg3γ.irm.) ]"" ")R" !(f ()K%>f3as6k?3s_ yׄ)0IYr8"QwZ>U{e$mJo.^Z[޻;ʈ}KaIcR" htט#}$]8gJ}?*l. p4_{K='Mzxt@M?Q}B){0QMґajX&|jܤwK 8MgRt++S5辭TG#QG ln#/!.]7*oT:f9ؙ^C <5-.!(+~Y>nYƎzg+.eq/3ZA9CGGg M&M&M\Lr;.&6y8_u -Br^vM%zw݃xNw߀y=뎲IKӢXn"p7?1An@W'0ox k}~9\95:ITke mQ5* tD]'dfJD7ṾBT'q(=uxRTԪ;d.BM]ː--4z(sbbvo șQh. ñEE0Y2Yʥjj\ZwϒUů]CNdWxᮕLl.|fW&cN@PgFH6 o@ `0BJu Qsh-,h3Ftَ pqDi|@W8n*d`*)` 0(%cS/pqCV$p?ZF鶴h/}[rNRN^;5Zq_BJlo<(<`Cjo˱R@6xm'r&6{?9J:Jy#s(ԛHKbȕ4ź#SS8A3$q@UHdf:< 9&P68iVޑ*sWn|qVYs6rEn(.qp7yorݰ0`,,dKuHu"ssj9cm1OCvHcNt|\-$Jm㈦L̰&ڒ"'+X aB+ %gtVVq&P Hcwnyː/]6-RRr;" J co㐺M'Ye lnxq 29 Ӛi 8oLA@ИrX2_cb_G_W9-p66CC"G!.)DXZv,rwWtDv]6Ū##/6k6Ȝ"p El wkݯV+w~3:_|$Ml?Oyf))wz)~fSŋ%1ceTdjc,s =;nuƧ\oB))M" $NZ:He >@#93pF+bd=7 f0_HÀ/׀<>ؼ l%|+8Vuu#sw^N*"ʧ-wGmm= GG."آZ~HMiji\ttFNN٩lDBDDDu\nS;bMd3ج2{V!n=!`Z`*9|{36ްuloM)mqmqA)"DiND\lD)pT**u hF~V^Zl$t*aX\^)lH2FzcF Odٓ]bc˭ʭ1V:˂Qi[q^^p{NRNj 8.(: ZWw:5yz ʖ7WϻϦa ,;T]ָ 56ǀ yt&&UDI\Fr8*w2($jw(1+1k$ boY a.G~VJYOڎs^I<&d-lf{1e>q!YTc;wTz֣p?44904'A1EåSSg 8QJN4q4Lٝ-8EhWv:er4zSOW$ 1MOۿW$j0)E*%Ӣ0Ovض?bf$tKExp`WE%s(UHZd@zH"WUc(t#uT5ZF46io6PU7IL2;)R9$ҤbbɶE#8zccZ66AFsF%00Z33K/}.7(ԣ]Ҳ'ۉg/8&X_8m5Ӗy9v1QT2bfIj3KҮ+K'Ԝܷۉ ;nhY+Vӊ)0X}!&%]|!`d@ʅSŇY[2 BGIY&mν9No8 Sb>KJf"WH03 p` < yM (x[F`Șo;/ gF;N?%%؜8{JJN/tzM+Lѿ>~2`gKf}^ISeMԬ}Wt8V,N; #Y=ʡZ;*mSU("p]J ^G963@;dJ t(ZHUUA/33锴*"Hê=R-OP 0@ EjZܔHhb!c3*|;W=fy16Cw!bʘ\p:thag~d cJZ,O8&ܫSv:%[ 7݊r/xְ0L'Ud81mo_/Ql'x(yx %%E=_L[x'1 MuY]5^C&{ ӁY%`66@"Bă:l4j&iD}J%ΝD{ב*;Ay[#Z#""zW$tA#22AF6moD ;՘%J>c%S.WBc^pe(R"r~ᘖ*tG-uL.*壀[R 0U)ҢIMǓٗ@p:..f'lQiTUM@hhy%cy Ňg/Dqb|ؼ/[.@#l;U&Yr]9lQt|%Jͦ+(з2twrUb:.9BBk bْ]jnթ;: ?x<|" EL_I}4}]LTZ(xjYY(K9BwN:W |`$#A`$?8HZ?G|sQQd "&g d FX p m'O4 !)9Ny+;j3ٲ%yPxP0+r  [r[r~]ۂ a55qôߩ8d^>4VA?p6PQs3Kw.Kw,N*߬΍=gJZZfHf?W9Ln>@Ȥ +[t[ImU*񱼨 m18Dw?'H^Pk2;rnX2CR"Qq!>]j(C>'TCMeuiJꕫ-JT 6:X,>I޻i/iM|Xfb5/̖EWviãFk^w3CDbF+R 8SVRa殹~WRefv`]6MD_ YZ J{?.Tf:@n85WS@an}`4^9sROSUl|O^bø m̷A @,r@*m7PWPW(! ۏ3ʁOg0xQwdz6>O:teތR|L{)~u'JJbL*U˅R\VsxM))lq9{|UW|wc0?UG_d-W .;lfL5\7DPA5 w |N4㇏aw[U.4.S(reh{^3rCj6&+l&TMF nZE#x輖$+WaTj) 7 ;PZGGP*VImeA*\TTl`1U1sR~_;2߶M%,VeywmkNT$2g-7xN'5nA9`flccnjFBB|/2ַ^Yc+(栯Sc6qxS>׎p!omdAi48V~9'wP@^`h@?;>|sħ&El8x9gvƮQ,|,|Ȗ$U'i-\ԅDn4,̚}}pSmXu<t2i֩_Z}'F c)ZSu).Hl׬ǍP\"@\12H2,'(:&>a!k *4lBd)iiAqrd]92̎8hKw q.;i|y5"Ė)%w e2d,5^z _0X0 Ț! yCkN=+[[vO!9t2|E%Sf4hmE q캯S,pѴm+ROoҕ50 $:CWXv@Rl$I>rhQaӷ`}v\ @{͂ӒВ ꂬ" /A$;ΡCQ_|V ÐD5jP|.6e&V PXYDʹLGs3Gs+qjp)d_߸l $.ޑwpo&'RS g)/n:9'/Mռ=]JsvBCDӰbԩpkcd! }u؅77@!ֺoCYo8>r lEɱl FQ).XnjbQhYn}Q]Fڧ~("YY2~cFnE'Hx.Yl!ٖٖsfBfB~Nibz-qm|N~ujDy&KyxwtxO\*Qw;.NJ^@`PB l3H~hh4(w؜N;7-Rl**\uEF0k\|{ .+ˆDttzz&O2y#P5lC- Vo\y<;-w׷4}2%.wEOCQw{}u*W\]cD ?:7@_PpMJmT_.e74<\F`C'b*e#H[|с[nY3Y3'!sC !z'{N*%@kczSMIbNMQgcI0=>ii/\h(3 3MBpzYIOW{W:YuYNN}oIcx8ѱ}nzSupGM05XWmZP gPx"<3q Pj3^i`T%mso͏|WWlhf +Fs /- &g 7"&gR Sbhh>q[ <n6i_Q W^hЦ3$-qcR,,-5cjDMMO;eE uҿ-«ũ=Z)R idz .3T':"i@>Ff{;_nOv"V dD?;d_+) /׊n=붍+~$K5t/]`xQ8SNNpBcQ+3m :+Z{^ְޡ.I1dVGSIBc1N9s4'$2e,zn7e뛯+?nHkw:O*+oTpЁf쟕UWlD=/cZoFv%w3 Yާ_rL1!p0ZuX nїdOcSN&e5fg$֟"Lfʀ{~Uw!Ӵ*1]ef&RNX Sa61RG\˲ۭSQ:Er|~N UZQ~J? L3ͨ$.Kh`>-P(jQGBV))KEűVc͡!_q(/śʟ2jjt/ bVV`( okنv_e_Gln,OHZI5~|u뾳F8pFWY Jal5̨5>>3W$rwJ㷠;np3֩g:7- q1D3vgp ma XvK_"11m1}# ]y P(⠁FusbznW9Ͱ0fĴLBk7]gĿAq_p&'B3(!uug[. UNNH뉂ڐ\|]DrKf͂L"o>.ΖGajMQCp(Y5򻪖pW~go4PeԽ}3ݭ~^ETSDWEIM[8L"S/vk%q&I/fE GG,FS"lj~JE/+dtO{wed,II%bB)ZKHD-Y+k%%kBLdZZ$*YʒH\KvYolyx}8?Ǽl[+]o6a"TѾcr>MΡK 0:~+ɜVR p]fDi)*@钱n];Tl! bFC8""<#o9/^ ȾD~(\ٽV 1ZхiJ?cz#yVr*h Pyh%8d2**oSmP$ڎgpfG4o߸jTSKO(⎺թ+Bl=hl9r {p!MӰrfhK-A59DL,exiHEFbYej@Xpmk D {A9`MdM(iّD )A3@S… TߛP\Aɜ󴋜Xu߷iO3}w.vZ,D &vp̡̡99F%Fr:p}"}"y0>TT{lB++mn5xuN%tE3>u(i{r] u0ϡƲ]/!97h%A?灝?He>W9x?AO`0y8D5hQcӰ'k eMV󝛷zBZ={ |(lUR jz\@gc  }_Ŀt+hh= 3Bsu^x@ Oޚ5$Sr[tQm]s7zJZ(M"RV./'g y`|~\{ɨDܳΔPE\&M&Pqm'šAtf, aT?}j;(IPP2+ ٯvl T RBF6CzM'J(^R/K=o`uiRv̰FK NR32]0#l5@ζF-\BSBy+iT>)KNSJV=.T&#O97j~ݡlq?'{6P[17UTgm4!ȱauٿi8\y5=ˤ"gލw* 戧}[ͺ?'CŔ]v&M=3C~xHO :l=w-B~((2Wex>=l;^^4Ogj%;ONj{֢5Ί^JI0JGT^1ƕ,ZfO~P-4=8S@Ѽ,OŐ9c=VfnzO}ճ?]HZUI]:09ɲDWچ+p5Vgɣ5RY*"hH5"-T ʍ_ֳL*2CHمP}J0=n)jPRX`Tzh:?A;@er7;'UzVWjOzV!YR{٧ٸ7dz_6#P}>&\z̸~S6RQm6 +L+(9vz=9J@H|>gˆag@ #!%~UR--;}I\J/u@XU'B(ɻ*ljLLSX:1f' >s.iDJvzK1td?deIJ}jH?o18Z;:k^NFbf{Mlн p~ybK'SųuU~\^Si+܎1yrf; Md/kGly *F]C|5W>w''xAetG S#qͥuTJj qUQ/Z?#W&' z^aL}(a> 'pD=> Oy#߈ sW< C%sػg2$], 85<@]?fEoep NִR7&MKƴ,79^ҫ?5Uy33\u5~?s/q2DցPj?xFKdg^QE0d䊾{1թ+(/H!B[[*}"iL 9M73m DcR)aG#-IыӋ}mb#w"|)A9E % KRi$$v"q AO8" ܖFRR!9 'DZQc3%[gcWiHf:kcFbFzk"pC!LbOpx2ϒ`1f99QEw &.YyP/_ByբgT'XXSYtN&ߕJd8Υ'KH49-U0%^,;3u b"-FSjUI$ -ÀaȑN"xxZ 8Z] DbLvg}^6*qqCRgFT}$dtj6ݤI$3q5ZZ&@ps#P0uV0]|q p~aw̖. }Ĥ|@V 9kj>V7u}| &C?\mm _d BTmY̷Q>>%&&qa;+sLk=P\S$w mAש4#| TbNj>WM!C2$JP$"%)BdR)!1%ÎL2$[p2Squﳟxs>}ߋguvY{ g<5frR{ݔI0G^_ve"@!m4orʑNd biDӚdM+Wh .o%88ƖQO(NKռ8?>vyz MuF@VueqZ0@o`GIk&K+r8fnsbބqjD^qo{7O6LWuQR:Ȟ""Ix34#j8E2>p^.6CfK۔齱􄣂,JdPg?+[cbmԃ(VMwN/r);U:$䦋z.TBUG.$1ø^W;N_cj-;$us?ep:7׽bP֛ӈ.ѦOm9XvfH{9$VKNfJc]32y5oJx0< ''0}wpVwYU;dv"tXѩy77/R7<bCm4o"md TyLg/m1kb/ ˕\XWYl#OdvL5(.Y(biYc!a`sRQHsޙ;ŌESVq~XhI"n鑰#9Bcdg6jvA^Dvp X{G|/q7.=%\fB?mdląKAZ|*3Mk2C'3Wq{ *lW-v+;\Yogd6}}B- ߪnHCe@ieʙnc[$slyq g<@eGI7,U۱Gx 7!/+4b촒 dC<e O)uq9]]O7²j`J¨v(g4{W9QNڜlG^tma+*/  E*mP{v m00&?BWc1?7^s>GuNaV;&-/$ $+F"oV Hˬ!͇Fǟx59QisX9iN>}~/P<j.05.!6b""v t GGNj {Qcnh&b~G/wʎH+| ))32_[dn~i=dI8#a#a0wHBIlrEsEiҜQQŹީn.G)l#ԫNUQxl?+%AWfI߇(pHd@m'p" ׺;1 „>|cж)*3oo~vohBДIhU#> 8GL|HuMR1ڲ Wб*9Yq}mˌviyyjgiBmڙee[QB* GEE{j%x%KGәmשN=Ҡ;64R7i$ qVxt@!W݀=ʎ@쁡`^m_Y#Fe҅ˀvI\H:9!*$ooKjjYWߛ?1TbkkC1dԦA9E$h:2%1;>fvXa5 qCbIňMSjOG /,&,Lf' z8ۥ窕EQA誐Bp9L9LWY"Ek_i7NԱoD稸Ul>Nf7#kX/e2d:Qe VO\cM~$7YisQ=KzH7?~l m՚]Y-K5sn~q'y Ie6^xb=$t'vBcKx18݌-Ԁle4$_a퐸f4 $Pnh~p2Wt7^{%Źym˚+,>Çkcw:>N* ܁ll,FLpVV{D=+*!*14i [ @EggTjf͜?_=tdjҬo 0lOBw&RqaM{veC_  U}ӊ ?i=pQ<%pKa͐Af᯦ na 9ϗ!Fgq XaCL8pfZnLk$(y-S+iN$T4?4 턎z8pnffgΒrvH3'UOw^LaC̉jbj* ˼)O"9-)i},ddSؖٴlk`IN^#gr )ET ``4NBABlh`hQ~ )w4y+Ru*wSx y*?=dFl~DDl0 H. F#e@U)|L(, 5t6tRR$7q ֝LL{!t*Ē8|Pb1;,!J.6ib(̿EˎX?*%\P6՛yy ÝEpwGFTkRmυo ߢfc\ ha]Xj~}z[픥7E3ϕ<|3H,`d:cfҊNg ‹r "XT@Mht43PN -;C9OnNHHt>idt(dyӿg`Wm@#=O%A̴@=+32芬d>:Gq|k[Rb1PPnA~#RTT?/)?:$ivY"nd+v)Cw5cȎp.17jS6mdftr>͋g)>w Jgc5(aMuxݬL!gqk`˿;{?ػh޾B,CB2`ΐH$E|"T4e e;cH2z:ۛŽy}]g>}]BҲJڕQpn7ot-tFl6>n܄h޳|eCF8]N"NG8?sH<0 ªpĴ:Ӊ ocn= ,ɦE])i7]]pi=d i|=2п8 RՖmOEa]ch5W=JE e V;X_7o[lmlf/ze88ss6ʖY!Y_>GId! ɒGGV\ $TU~'eA=udu*S'u F\aNY%\AKE{l;pexq2Oy ʾ >=FhhZ"f.bQ7 eI:~Yȼ$+9!! Ȑa&n``?ao>0y"b !{2c?4qPR$#]}RVP~;*=p"gD.._^#q{H"q׊[:WvΘީ: &cdnԩµN1g`Sx\qJnHuU%YC!Yǚ̎AhX)]xM|0>4ļ _+!HT|r_;Nh-IOz,f4WU =Xsg,Wk`]#O.WyhW+uuo  Pcn-. -aNi2{]]٦Spb'[8Ӝ"fhc1F;^;>a-; 3]Ph&6h{Q Rp_pҟ "aHMM G%Lx<~"k*HMLb;:_ .+u~]I x[!-&y 0Dcs0QxTֽp}ƿnЗ⚞ypl~l J3Z`Ge/B5$}zp""66ffhE4?:~\pp:U) z=vdD( sH`DH$ü*-cH#94vwĴ cM#)yog_TT"!{~@"BIchgY4O$Y0&/ o4+Q_z,supNu+^ ZS幬 ieeWQQ; B!̏NnTXi]lIڨS%kq99VӗWܡ6|{jJcqHoizGm|H:sAR.(sD<_+i H Dg-^AC7lRh7Od~_Ybf x77%V#~bIJ[hLjGiiF#@Ө!!}( gݒSƈ3r:op%y|,%IQ_tPK&sreA8sNsN\ЮCyu($ 8"jt!"!"A5⾤٘E'a_qvWبSekqȾFXSR}(PF+8X 4DsK'& H"/ģrBl!Ouu9H{]@1Dt<4@# l WMաl"{+h6gHcn79|uJ">ryIqJqfd+ld"77@7z0`d5hN%ȥI;$;DoSS/%Sh?՝7Ϟ]}fFOT+g*o)r9qXct!/rr1"KS=Vo]颼F۱v0xl;2~UݢZV5cWoNa\ӘHVc/LD=k:i#"7K/9LjjЍgf%<)u2lKɥ̥`ut_SE2gziZs~^ߊuJruʙ2 ӪL"Z&4"V6~(!>@iiûf%b-*mU>>XYƬm /#" $׼䋝>]pbKHO`6wrn齀yrw(]{Êl:!o9}Q|Fw#B,%yW"uL{)-_6=LxL!P]Du|k6#;>#v\Q ^QԊ+ŧO] <&Ej#]<MC30|tt>ХkŤ1a$B5>orޜɜ-Q[h^^Ĕ4kDeAr^ g-n-njsIRսNIPa@=VZw9>yNUuJVb6s4d]fw$ vׅ[ .HKޑJӖ^G(ꝍ KjOj?UܤI . 3x5,@iVi6b&b&}7^Tc`(4M E.|ıR/hu_6qߜjӝ{s41pjjQQX ^HIUhh$E+q6  G9x_|Is)k|nNUu4\bCw32Vr̽;fM'9f=4ݬ<@,bزXXIX {]=1%j<3@HHLу k v {N,C\̲L~[m'4l:[9Y')7}Y'<7㚝lcبחI[[;vmT\|<#cc~G.RnyN'N'R~=wdmIr_qRwd4hG~1b7I;5Go/{ 4 bʔwdّ Vmk]UIa{BZV˺ϑK RXH}rD bV^\@(:%;*aK]ͻĚ* 9ӖZk =e-l5Lh 3"MsM|gZ+4Te(oȾ*ifJhjW37q=* >OJ/v cP,$"Ree)Vih22F_۩##fyR>-ިSصNe ppR=ㆴwa*Avul[& DpS!Q!ɭt"r` \@$% r^r̼2IJG2IqFΠ 8Vq$r;-6k1~' ‘6M^fjjƯ̊p++JJ0Q]\ Ρܬ(La8Gq-7>% *MR2TTY DLh, fwz|eU}H\T=9Y|FDHFF\%PXJSk80fkKE>:43Bb`g3rX,ӆ{M#><:5tUH(ĩm9%֓ӓ3[p5==TChipYZXZ+pMl$3}5)͊DTԜj,#9ۿ=f xDwΩ|G}F^5^rV:6Wu_4i!DO[.0N08WmI@0]ŠW{WEnvlޤޤէy Q`d2R1Zu !NSdZhmTew>TUA-qD4A>v?I&sSeI!E!!jLW 'i#i;;an<Y#**p=2j8&=f/#K{mTVS,( oXq=䶔Z@,Rj@0CPbeK:@aolW(PX"as -Vi=  1)ZYy2Bx4Z5<61s5`!!so4cg2HrX]}NNEQ$ń:p{lH#BG~>9s,5 Ӹ2H4j6k6!}RĎĎ1 x*_P|hpOzNM}je \s<AZSqU=mJzi]=>VnXCx;kAs]@V!͙kלtَ?" vjy28iQ O\5ڂ>&*E2!&5Y[̷ķRֱٱyHT n4Z%81$!oKK!]$gRhiLAQ> ?'!/ZPpwICKŘò3vvFWߗEtv ii>}DWͲ3!q}+n\ppB^,X\$7tzJpt}шNMl5,4#7/F㮩&,Z\74J^, 른O)WsSUk*9d5#Zz'qva؟BpX1`Tbʧo,gBJH /5gE^$DߥY!Rfo"9'}:>mmic -3KB@xd2GEkkTR Tŕqq5NVvNptt=#/=/]0 NSSZqhD[WTVS$jp3>ˎ[`HdmE1P@Pcc/C">9&@m Ֆr+\1jt!NGQe:Wk6@8 /}!K*zh_=`q:t0׾iJOǤkXğدҲhcCȅӨҨ6G5&5&-8@99pӼӼ" # Ͻff& ogzJfN?jj7p֡?0S}Z#_LSeCS*~u[ꯈ6+T ԙk0>՟-^\ؾQbiof+pQriqwR,:ٌ%sM8GZ 2%G㣥6.:A3JnF uM">74U4!ĺ3Q{%CDkMDM8~~G*#)g=g}3h `>.xaƌ= (5#=Jnm$&f=Ta֡Ǘ덒Nk Pu1}d( =woTcw.~;rcQzu;(` nٻk$0հ@m-dYvbdqc4M闂w&$25>^1i#vݓtݐu^ ͞{BdlinШ%;E-Ssu n6;!5JF&͈9mCUS،(|82}kw77/Qۯdw9:61M5ͦmvos =(lj\?MgJ/cZ+h^Ȯcz04އL]AhJ-O=.Ζ&=`i zMa5<[z:bk&wD/P/4'ZW jρ.@&f~-5'w)>@Po^' C"|sᏠ$1q Q׊I_[K9t! Ć؆XZpB20;ʅʦܑܑ?*ߨÛrt 9VUuہD=1Iz !ys<=xrxrBڑϪ]hz>@C,>@8I HDw|6䲓X.Umn> {. - %n˼ٻаHkfgD "+=C]+B3BU}={?:q=s{z_p{R_L|ݒ cU' WdZdjxnrbM^h0jSwt6\%ґZn}4m2բ aNG/6pM^T^/w=DhfRQƤ0׍k|| ^A&,_BlUx/%0^|ڀ{"joB䊖Ū>B$@S^1)?o޸Q'K>ʇNN+k 6""kKx87wZ7PE;)e2j0e:7=;50w&k Cm1QJ},E0yPSVXq7GĪHi&q#A1* M9 S#457w19~j)36/PPP@PG!^1o)@ntvW᫴*&H-΃~#*)`kLVh{rQ Ld ZrZrâʺk 䱸f2_;696l(%(%d4;7[sBfh$#emݩ!^p7O6ѶN޺%2@ wvqbx..,W^lYTkS&=fPc(,OWDqȵ̤0dp.CR@@fĶ@6ee?vz&KaGw!R\r⎆fu\2  yb1_ր黄iid>7aM&ȟ PCz|H<-blb M0<;,!2kR}!Y7OjYޕsLymOcLwLAmC)ܠ ߋ| y/ev{G!BKwy(w4NS UW~3|xA_7gKR#mSeuQJ#.-~zE4xIfn؞=wZ 6яᄶz+PD\18l@̢jo_ Ir-$([xt} U6G+VhzLbo~IҘtt" Y>5~ϹI6X\U3N_YˋG%&WK1YDL}۫}5L2LBpo.$;;c0j5%8cEcE ?:2UU w >bVpNݩ#,Mp4BvfkpUg =$^ y[,jݧݞ DQ||9.l55Uxy1R{D=շZC()ᮐ[H1O5$%Cg-78 Kts|?Jɷ3l;WoOD"?:YDO0u!1rn@pQo.C K\722} SQPLFt. r,n4$Ko{W=ߴݯ5z Vsë"5/{Xq&iRDRwn*ظOxjzAɬ\hʗr3+:Tl7g2mT -j1oHطE$s;_ǩ|@2n'7o(55_j3Sx`t7/>o[g" iwL+!;q ~L/2)Q_VilR/5fU!)*Jfiak E30P{- \~Q uKK ͐OhR@`/O%o:ѥʍ:4@].=! AƵ=_#Od FM2U6T6 VC^*+N-NmWEY,R_wjvyoڹ&;uN]zkΛԠqq{5^Y\ )ppV8"om#x,$n˱=}L;k%-='wp 1/_ *'uoDW˶yvkl̡W3YRY&JB-nmH70;3NNAtB'RR= < xh8&aàH/ZIrLN{vЊSNݩp95FZdsb 'Q6h1!\,y}ϡd߀K<:AtOUQlm/f`sz|͂J)`ugr ؋lnA# ci@2^[`fTTRbxӥ8v9h]silrZgnn§96pͽͽYYI4\]vH;vf}x9aꤏаC-0{O} ߝ('/4ALʂR; E-l.Cu@/g@cρ^>|Tā9}l`[I !5ç5c"1U$2!"*dUW7 9Ə8E*z2فƶe6ީ;zf:py@0((]s3s@x=3!_m5Fay}yZp#6N\g_ u>cȱ++$1zc3lňkBΒ~yd?88~tr# r'+41`yӏ3e{$؟2/>oxxX.Qѩҩ^z~mK`HL%.6:={ȩPG00ت;2O^rBU|d%p!kN})pe8SY"y'q6J;9+1 t[x<0W0Hkf%;il33<zGqb]G#\|{hAr噅6 \!5m{1‹.h퀙!`rۿػhD)*21rǐy(H3B LeHߤdBB۳s>ǵݡY^ٟgȫWCM ,u!=հd CT1+d.KlL(pb_^/yS3ADԀke߫YސƠ"R N6O6OBƳU!UM[.[XX ;ccfESWG{RUts6Nw:Y‘4G6u2 tx4!dp.[ Opy%@+/PO]ݏGN VkNewRl%*mtfV#'$8"XD5,0 Ki\PFݶpųvm4{D|lRgc2{aδcSSЫKNbRB pg_;8w)eCֳ7 6:R/7{վFf, ; ݅w2m[x].Jk`b(TVIS"K\jugG7#=4"˯ ʶ̕M,:'V~o#LcwG4LF=K7նYHQ5FYt+"4}Vɢ GmALv֭'/(z/J;[;H׿\<0_TaG:7f+լgUC%r i#.g9eujՒfOVkPSi| qfr>}d^`#{e⼮wg_nyl~8NJFl2D(NOo #9Ɩ9/K(Ɔh'jFCw@eGv.д>NibLZ'P#|zt'"ߋh:2!15_B: ,_!:^H}/ArMQH0E[b.[+NJ xKIQ",qēp?DZDZxv`þsPߜ\o+{+{G::5kx(^@ެS_~w*X\2a\*fa:0^(!Dy.LOSIў w9} D fppc 2%I ]]En>y$ A},@ދ$r`Ngb:xE=@FoxンÕ*Ro5{%XqPPJjwb8&>&o+I}p Q &B~6\12yJsϕ l78/|7N)jKlߓzs~2M(4=OQ 4) p:KT705i$Nk.` JF]..g' ɉq^b<<9eb[!ѵeĤJM+1Ɖ+ z~]iߘ^kEabp)(D8JY4 ,,5ދ99Qh9x$LJZ+, 3GH!A9BF߇dWwܧ ?ܞs?qխ׉IڧP {NNY͉wFoXcI.R?irU`sj91)|tO?7#0Ӆx{C[>C.=hr'Ydѧb$֍;گگI٥g{&M)M R2*BIRůΎyyejuIީ4Khۉ~)u+c[. vJ){1ҡ5@IAB4/+-ɦ1YkQ;SScᔻa;^ j9Gi.]^`PUC"/a}.hź콄t Z;:IeH [׳ֳغ^E";&t!T3mmu߳gFuw])CSbn> Īfݩ R~8WY9!xi! f{DƳwNMt\@iڦ<;BJ T]QDOK?ԁeO3Ո/:)DLLw‡y㯅U! 1xMVxAZu(w2#  ޜ}B̫;wKڋng2]XSqIlrh ?|ZoLJcN%F]F~DUїN^:9^\5 A iy`U$%%j/SjLIZ8iE"\}|NMђ1G|j8˧t/D~4&tB 0SrPPWw ؏8Wu~71VQg*+ΰzāCVf[/E8>)KKU;( ysch4JCs[>vYV+NLKsrrj tN4d5ŞS;9~9''ؓ!o mkW\\ǣ=d>OdVkkr$%,S^~(3䗚OHN,ۀ y>ǃAWVSOۖ"`P~G,\ۍ'Cƚ^1HHγ˴..)a44d@@v?^*bTw /ڬS3p:prsy_%*w~npZ*zwT)!B$2g*IlCd,vLIL9DɐB[frEJD LQO7^Ċ . }tVF|՘<U% AQZ+vd {{vAL~H끇q.Vwrmآ {;J$K$Kyy$@!uHL;|VY,)i_%x݃GȂm)zlLVDz} Pu61ӯSV}wD.:V+ΌeAWϝbR(v]hg 8b A@;=ĂQoJjHl9r~=u7^ PFi<^ bS:l-Ѱ+K'7\zCr1O8JJ*˪K3B۸!-HZpZZX:X:HpWW~rQ\q\G!=C{Sk1)?Rg꫏qfg\Ե#j̔xܳ??%T P_6ie}f=2&>br 4[ FR1~ e]*>q]4@[H t6(,`HOROPm$F^lͅb!))/@V= nԐwXf8 ½oLc ݤ)8ըt{UebM9-Nur\xc"ԋ#m"#}|\z@SÅ  &?@i'{qv*H=c;Y~gJ8 \Y9i2N"݋g|ˇ4LH&L AQ') @_F1WEuۙDjer xDGiyn5!\$(<s&,"e u6qe:#_gb9+T>}b}`xKf 4<^Bgຶ9 DP6ӚC^XC(h3 \]4퀤I@4V7╪D\E˖Jzk7ľE<ԇ ~5C/R7h `44} #HZXĵ}hxbo1KVZ6zwsٵ=$ȭYc9nCs2[3{K(+6'TaVx7E}ܾXt$;CkXuAN6J`8re-)<#@mrD 伫4W {Mv)|K/Nejj-S2W"9#9WޭsP)2^4#| ҮҮ7JJ=IǓiRʓ2bD.F37zR0p6j_@Rt2jcc:OV %klL9A-jRL?P  n"S"{%@W9 爭8pa8*9>d9lo(@+!ЧW >X٨tR3 QYhhrbnco( "Pp[]F#y|TE(S}"O׺$yoi0l~ ;],'1[{DtrEhëYCVz]K#;4x=d}juKmږ?TW%o5<|h*Jĥή ïx-uv{-1 Oߴ_2m}'ԼߵZRZqҞ3u:~^6!Ws"2g/ez|4˿_޹*Kv]~ iO ]~kY;2ߌ n]Y0{QDօm;`]xRZsO<ӽ^ѽr4\/TUkWwȇnLJNy""Sۄאn`RӁ9+:OcR>/b;4}iZ5p{%)/jVe7ڟmi/n̓7:W3mH +?^?>`)r!> uR"2+hj xp)07~߰tctct v1;)~R|3iT;ZAR3 G&O&ŌOOhHnHnZE6}_#nnL?˄ "Vkč"+ Z"YX+}خ&78t~ g81j[j?hw&xA7bj KLlS!!E iSxvg `N)ovʨSۑ RDe8/:i[^aS؛ccQ๱ ٩٩T.7 r8GlXT1#ԩ#s_g+%ҭb^Y`7 SٿsRďL|A: z;-T[$CJmjU`Gl+V22b(Iy ߙ$32ONya6!fz`7odB4Z) =T`5/QH^8CWIdx%ϸ-vD|ycBoo7~2;Hlgr}v+Ru1Ռ!'W|offqHV %m3\coY <.*KHT@?spٴTl`Jńhj$7v)fhf yY:mŨ&nLrEJ-lx6X={$B4q‰,,b2737W"'('b wRY7=zgp#CN+37;͖_GfY=p*#qSY>'I#Yvc7d74L_q B=đ33BZ{ R3,0C@& <Ҳ1 HHıϥ[H\q&±M 7qcC[,/7SQqrϺ{r0kA!k.e.ETTU; e]e5AJi7wP}m,ْ-])$D,YKYKHH%;dQ/k D(==|9}?3\ZTEBhKP2Y9k\dw GǙ6zi$̎yE-s=xzq( ]ͽmfE(P/q8B >ΦVe oL/DkӀDs 1t EVcct1zǠXf\a]-_\_"NNc MSpJJE@^ JiZ9{ٲòc@::NEutmy֖:EBEBh'5oћnG'pEF_7ҹBJ;TئVɄ7#FK` &0040!=<y~̓+s\ 4ʆL~šME{`1@o~MH̨Gk/'ŧ㽼-Ɬ_;z/.2sE\ffލHy_B\ǤSaPayRVU!SvP/^*] =\.s}?:/0s(P\YѢ'{}TDUy]]@y1kB] ב??1?2Uǒn9x֓9H-Vdez׍N{2k5 Wߙ3kdNr.pVC@@c8I fOfma4VT;~jyy-3yh76y߸oðR l3[/uՖZvMIV&gmӒϲv4= k*7̬lfdʫ[VD/0VqDAU"f_d! ӂpM_;7r2^s(g3geOj`)LSi<"hbqgg:ڋHyߗIy%r/295U?zԊn=2+~yFp|fONi<*QsVPTq6\7F~ozS@fV'Ujg6~IBAA'J| 5OgF}h=) I"\5C ^-LptOsjVފYnίor$ j;Kv„cH݌pEE;m)<^^r*x8^jh'lfOvF"[T6E'5s h13l)zH9U b@kFGI_$vB?szDg3D(}z:>kbp@+sH Fܘ;S0ѕ 17~@F98YXs_teo/7ccTep vvqQQdgل(wVϥQ9Wkus:eSX7^V9uEz][RM)6H쮿「}N;xn5b!< 13SShv1رڐ+Q6,vGî'CrCҜt_j тK=)59]ѩ9:m uYzDzK'jlty; )b\oOo˫VVYE_Rx/bfÚ=رQ:U+o p$Ey RA 2{o7I^]@8^"'qb\tS B!!|xi4z_xuK@kr~2_P GZ4Ik}<;6 1O0j$ߐ9I!;-FCgp@v}=2M* 79iixͅ  t/pgyL򎺈ñcߋ2ܒ?Y7SprFXT U?yp?ޓ#>']cGhQ@OBK(Ҥy PM'6E [p p ->B(212q{!!;n<8/[~c9vɷvv_!7!1 Jp*'؉QK. ͟[VRlUViާT]T$}ϑoѕl}_-titiں&X¡G#aάENH4Ρpaagw&tt9_{kB>VYln{:u3>]['XVneKsԶ;b; oaFgHev[ڰƓ~IY>63y)4"״+2ˇ{uPUy]O%o qC$賛4D-9s?4%NqElrs^'JpZB\Psi֖&[|˄yK2&jBh 7r ڃP^ᶁ,5,5)H ݈)%$|p2o7qtk#SjI%$FZD?/Ѭ#x~D#ٹƷ-:`X} sΑtt\GmfxpPY(E @;%k2!Yya?UH߁'D̲0JSLzrFĈ-糑Ư/EmWļV^Tq@@Kg>Ԩ艌NY1sW.c{8V?V-ffv[Fj?|wҰE᳟qF^ Ct ƻ;# 8Kf?EK%z*ٓ?ۨǜW.2nppuiuIo/v Hm2HޱXX=^^(ՈՈW_sgҦ 9x+h~URͱ瀵 EqN)I4_dImZY?' ɭ34b@ 4'Ɇ2h׺>S Nѷ[˾ * $@iAH+~5`׏)bSA\MNb1q+eC?<23>CjjvUp&&cmy1Dz+wTv d̰1ʐ)S1e!sfR*<'s!!cʐ!C Ș#3 9ֹ}g{{}x9<}ZwoZ+HȲQQNmil"8tXD̬YulS/\. B#/.cX $n1ē+ H|fHQXM^b#Z@檱u]`0 (Rb[ `cmoBB i0XAҸhˀ~AHn6IKn&AN&3k/asu3If44G~3M+vg,5Gy8 # #Z:C!F09=,[HHPP~1uީ9Fb*OkzvXA9Ӛ,PՖɀ)})ejwyneN:J4oJ% lسfg <X.4CVDb \ !4ʰҘvJHgEp}\ѯjUlrZJE kГ,lldH? uqz>4l]o0n]}xήޮǵǕqIDO )٤ m-Y?$~h_Tƈb,n"37v;N2d#;gn)=R'l8"?`˙NOI6JM]FiW5*[oZ9kG?;QKZCX̀8ql~/{g3doҙdJTY{5₻FJT?8҉z5^lɹANbVrw =6QF{^UTVmT0veG|MMe} םq/{g.^iS^<(iȦcڪ bAd<1|{^W͌/FĊf yT?p2uokyJQDwvosLM&u sFKU>ȫHU _L-ЧY/Bnkpp_s4~BɄ ?ke/B)\@w$WO@ٜf_]xNjAOcO]E"gg޺;]&~f{m\U$wvHL{ dE00ו.;lwpS+ E1|Q1 76ufJ22_*[`Ej[ȋ\C!2_ )6A"CgXA VU16-@Pd{5.YS;bx2d`CfXZ..8(8($q9f*a*Uӝ>)b=FFQ ;;,t4n+~hfUyӿ[(;Uf<Φqs~TSdNqf2E2Sݘ^ D R(޹3 zHe| <(!H>oЍH9Y:n2I ٚb H Yh;uFPV~'2cB׼>nɧɧ1dΞׄccKMǭ*i 8oFoF5GDNT>[s.$4}qnrpT l(~?p fp6j1 4 ,`bMڽ #P*'Q֜z 'FccL  R Q o$񙈊J ;i Z02BpT8!بL^J5Af'b8MA a&[4 Slf+1>pU-U-> >b'arQ%dkApqۖ闄`39v4+󒥑@Ne4BKBWB7 ǧn  '@YL111Myyꥁ͆52N8o?SbHP{}Nlwmq Hvq´pHL%!^cl$Y=Ne؈|ƗFEDTkGgEj%_g8%'9j^&R& ':{+t} H<&04/8i f#浴/^7]c5vYa%<~4\mmJNJQ_:ELQ~pg=Nbg?ݍ-X=)ީ4) )N)}|g?24&dfuhi&'r"QEdpc^MAu|= 3g\3ZM!s~5\n}WK#W"W" nhNvA4-S}r6&@׍_U%h#ᓯS-|b/}-TI/}=0);ܻ3/)O*O)PMVs&w&wUrUNx/ R^@@|w). .M7+S]Brso)2֩C۝#hx EI}*ӣ4S@iFݐ)L)DÜi?5V6l]0I#nvuy>4,Ž2(8*r(BMuF. el+bm ^a3#-3?4HɺC;G=󂓰7r2rʱ<PT|]n}==yys w";D?wd* 槯9'^?KXP5O^t9.C50ζ^1J٩W~{^<\f w$|>uepդ' 7E 쿕1勾tӴ##aXA"75| :eVn=c#!Qh7w܈n{n3OKLKq9~fkQabxa+/FLYr(&΁КFit&?L~8b,DAi_;brχ>>x+gzoJ*ze@iǗ Q% uX$76>!)iYQ?Hee"d~0 lnf |!9I3G/4|m\%4V6SOB|?3?[C^?`ued;, ώYMnʊ sJv.9) *PPߔćIԐtELYp88ÛccFNI+1W;XX" "S ۝*Bgo+20?Qs@DϹΡ*@L >PTؼ'<$+vOtF(BHY7TC$gg9|;&&p^B9  o fklȀMs*lN/dJtdظќ\3:C(ogn4_̧o톋? ,#d©GȐF$3Fh$cʐ2e*2$S!ĝ1,i2gnZoާ98:X;ZZee'aa;|T컔0ڨ)浦bDR30Yw.Ч|*X21ɱ}$bTGL[BUUZ!D=Cp`>m844)4IJԀ*j8'̀,xS1ހ\H i>+Ej[Ai%EVtN3U{&KlI-@-jAȹ |2 y_nTOmz3mDZW)媎QdYoZST>nH}6Qn=f`:wSrBfhym_PY$xL<\ȥP|+t Nn#`F%mA*zou-utC9[Fx 1/lL3ČGDc&cbd*{?dZ44~à<~힐 йGvߵY0M0rbjj.E)ɋW^97 Q+ب)ֵj˨5>\+G]*.`պOa}kj ޏ*{bmbFdzGæ)gRE9T*W*1@'Bǐd(/cF4R@åo2ˆ^ &mz}z[F/s|Q?pGs/H"}Ycj:@7C3i@jjRHP W CȦ&x2tr85ð.]`@^JZlchD{o0IVdӃVX$~/*}x@ݺkmE N6&9d#FmMuJpi)JUte6abecb\8~PI~ܸ"p{8kף}VrG70ǣ$:yrZaHmm FVq4Z$aIaJJz {!Joқ Ih2ooq.#Z17p=r:7mL41 q+=n^D[a x]Ĵd" de9jeWHDjyVsХ*' yp 섪hxP 99 j&Rr]m:={lyCb|uYzcicI'e$ FSFS#tW!khoJ6!OvO_؆[[]P0L 3q>T?FMXk 4Xt~rE}3יV't%5QR4yV=Ϯrڛtyq$ԙߙh(!=s|8@[}-HdJ,NT-t@XPq6Tv@jb&$l y%1M6s=5oXuڱ(aDRxeӜr'C16.jɫߢ#QPF{{hxE{EgZؔfܱt "ŸqK1' 4Mt xO[N3%9em1 O.2Y,o]wOIOg'6ja֧eLvHݫ>gg6l#%F'*=o^RIN} 33=\L1LJME#a{YI @ Ŭt2n<rG1o{WlTg (jxJ靊V;(gg6b|>"n\:.&OﺎxՁ쏪$yqY?n%uY2rR+H-EyD^r=ꀠG 4X -5Yk8BM~#,p^V ۥ˲&ߡ}2Oj_[_( MH?GHk=VY1҈ӈk_OO^jGѢ;R$_ 5ŹT^B x [{-ˉ|v} sק|, R`-b"Xyl!xxׯNJ0$&LhBN* := tc83h6 f%8" :*U%SCʇ|ю]}D>Sdd_?]EʊɊ(4 t,d ӄӔإة={wͪXTU ,9(FKmZS% H|xY?T)-~ln."k3#`Ռ8&Hd.1b$&@3BKWY lÓ()-AdRZ' <')}тIB<L8<-1JWdhc^T4ִ6 {z3t։#nupmi:VĠee ^$\&ǽM1dd߼37K$Cv+V'-On#|LWiיQ;+朖$|/M1:fHwdّ\5:a3|$FsD^!%|ZƓ͋~M~uREV5={VjxJ w^K,xXO^h%-@ddH ms"X455mDυՒTX%&J [55XX>ywW*[6RpYpᤙ(jm1|) 8 8uuHcv``xVdSnzMىyi8QSJGh>򜃄ov\Q-hEt:jfvei00rjۈI%.2?>JH"|Ri9ܔet w qwi[Z|V|$UFJ<8cL!QdReHP99 *REd23OU/FM=b4LmW mC#:u6TZffՄ!9>nSOi`0c o§I~4_<t;h<|fGK'ox_YơlM&I?䙴PZDȈ.Ec{/PP  J}?v!d%݇~F~@vロdH_X sdj#kXՑqGT lVNy2>*GlII_=X´`\b7@z8ή| "(?RmMJdMoVWW*35.lQNë1$go׶lďr ζ3H.$'1HUa:Cܷ5jײ`ؔť;@<-K̪Fk-q9 wFFJchBpCI&%o78,Bxsi8к ֨L$#m |ĭ6q2йY VP3D=D=&Q/HH ƤV O2qK>;tZY{8|"i4i) oj[Co8wkJp^ C]blb}(ǥTg7D>J\Mb]+O]>B`^9ÙTna{1E>ӹ""t R_ 9'Vs RVXLn `/trڟΙ5 JJyaRo l"/_w%hhC[dC{LF(!dBS!DDԚڬ99'JxS*Yӽ[SBM5^.8tGC#aCi]QW~&:h)Wq `{UAU fHC@MW 6OqOqrʐs؄0*"aVn lDB_SDQ;AF"Lr}+MRӲ#Y"Y;ʥƥ&2=r~sEB*CX"6|z[SM26=ЛgJ3/xei,/^&B1zyCϞ|vS>7l4{NC:oK,C{- [~B;$NW$rcO2ůSg=ȟЍ!QTIwIaˬeϩT\\q/k%7P gg{S}&&K:&^C ż v];yyW7cNܨ9{㰡BG0t# :9>Gp>/i`BEVJmnRa8'1О}M<٣kJp d՟{h< P&b<ˆ}ڝ;7cӕ8@h3I!޴p~~M  9yr:bV\Pk ?[!SK$p}{.-]_(+YwmtNepOϴVB0kk\X>}qP,K,wi߭6cd#zMqqdFˈr(:7j ֵt? OTøvtPglIIHltC9Z=9A u湪}ݢIeSB4hKNPzwuqJ!Jq M(>hML+>Yl=cg5Eg&;&;^pŐYZZX#ӇN@P渖oñ..Գ^w1;<;BOxS(L{:֔vSmp Qk.M-.,^)ǕRB Wuo pJ$>d?Z[)#Q1~N:tOC)INދ}G}T|KBy]w@g2*jס-W!n~Дӹox9:Q|m_N3K . _ `1!NQԑx يl -][ oJmRG3zf]wkJl39Kp.+Ç 5D$Lp>hCâ@D@(կW.6fưѳгc;: M=)t؈1HH+4@$?Ch#Lέ ,>wHM򯗞m3hZN}Vduyԁe)N"SSxmZ[S@& &&>HHl&T)TY2o@A}/}$֔vSKpTc/ejU.jl=ݥRq=\nr,[~.r\cƆ|DrX&@x:|Ga`|+EQb!&{'PΚY 2/UV++zy]aab:g:"#P@MϟelJ)] Zs|~0itA\-ű+\+6v',Q&w#Z']G|7Cݩ 5tuAWCd"6)6/{k!0l+ZoUʒEz`@nz[w7#iSEmM4 t_e 1ASw>I'hE)Hސ ^sҫыځW.!|+$MT "-DVb~6$*MYs(cLkm ) lSpk+Mem WraJ; Ƽ еE}J&o.Q"~o׶>G:g3F+y<e H nr8>-NGDؼ,dk5cx}n8B[c0!k1`Be\%@yhs(!-hjqo #pb*rޙ;/Jn܃ &&V7/R㆛[[ KHKS oʢ`p^,#bUnMIl7MNBW- jr]ʃYM?eΟi99[T0/8nc\P/gH xUhcmb"\[4ƺ=Z{8ܰ5XOurt@ =hҀ-풌i੊eeqGg,`h*"m/"}tz+^!_!O9.sAgΐ\Np?28K:KM7n>6o*idaj5u|)Dܘ<o\ F`0Ksf9ZR 7b%e 9>Q c@YCŢ`6==7v&c0=Fs kGN}* i0^OҢ5@.>.[yiY |ZZ|*xx:!;S]_(ܶ .*^^(re隝dl< n;8Xk*-aoF]Z\@M{nMIn7ahu#) LE"\?#$!}J7669cdUj]zl"%K" P,0Јx[os Wj(}5;?Y 2)E-x!Li@{>-,lB/71'! [wn,!ݐ젼K(S)SY3/;ʿMFFq"![)22qr~jc%aKf2HV<}9~ϸ\u>=p uu<~ǣm?Q&V.:GJ:Vj:e:gn!SKutطhi)ߨS ;E3!׏!@'Lv*h+g^`9U/)7]Ȁ3Ӛ$$>{ z\|+06"?+bC)C *#_B.8E68Rje?1CX 켾&glQT(y^޹>] Iq Fp/^r)+E߱{ 1f"&/K7Mg 2ҍ|W1p1{BFsu92Ҵ"%16"vȶ6\\3^x>uڊ5CT\+G_dsu2\:suW4=Ome߈Ȯ~w{}3$ W Sxmehm䜰U.\\:- ZemxW;pqHBD^fPsAEsϭ=Q<8.gYonThC~Ɉ55ѧrM0l"Q2:u٩ Fą>@; NK:i9 Tm{;hՁ+* ba[ >A]##wndedE|{K(SL* <17AZGZNq(@Jk[ @+${Şabe"_rޓ#~-b x"JaqoϷ(Cq_ =]K]K Tw!^=1K-W#v0 R&`Es|?o\ޝr9kZ;.]3:ค}ѾXLKc0愣:Ju4P`>c1]?] Vx|՛F7N->s{D)N[U0wq8ڲ;J=bs<@`pDOtX+)ՄWʯ#Qr WX%rgtLX4<I<n . dU*oHdT%SBX=>&()r>/&Cu/b 1` ݨW 12$1Up66QCCډRyz uJj(׫jJ@oj[Tabf~c^E^h3s8ִur9A!]_kǢɺnf?n]B@,s]=W+R^(9ss1k"kcl3`177g/̦7O,-e1qmf LFhL)7m_؏}zYHRWl3KsNsۺQ]ogEF} f2K _}d)WwkƖxpWqa퀄WfmكAUG(= O|\k.O+(E1i=Y9ilje|ʟCJ\Mñ ڣo3DމKiaE;^cR0DɼA rݿ?=09o6anJ~ƉS3p.*x= E2-pS;eHu ^bOj>S8R~hM 1S r@/xFzء-̓G jCH BD"]ÝPp|ޅK7/ܺF /KRH4ܠgw(wB+uRkFu2_hǎ/ y uu/ Fg,,P%yBHM2?.@N +M:M:twGX;5E+8.̆wVFR3V Ɠh62ͽH̗H+TQӞYbi5y`gxV,͘\KȰϰOVx]8%, ɭSr~sHSăӴ _#+l)Y¤\|kN }HK|몱 4KRnT˒)Z.\=' W`Y`M& $oM '"r,3ӹOXx!)>_ tEd /uNiu ™{uc΁T%b3W WRky%tI9e_9!pPTUT67T㕸~IwA*4wjpp҄$Ax!9A@cl ),PUR)3*!BH[-jmȠ2 Y~t+A# 좜~հ\ )G>̣JwN·)]ŵ\QTw*~v\aEFy/"f3;4dN۳!(ZtHq]%[u;j^N-8,aV҉dnA&e&!_E!)BtFpZ*E pd)F@+ِk5HA|2kG?>ěs[\?ɻOUU[=*Xt=eHL5** TgYwfڨSj;U^ nz;5Ӭ=A*ュ.ĤYUEqA1? < K'Ec;q{rK8!#V@@:j/GreN?8|>?܃以lY~ `VvOR >r}yZc5!fȝkƚd6Z@F "r}3#0RaiJ3HL 18;NN(g ʛԅ]NT$$0wy11={z9!:S(gKx;tO w.UfOO#aa0*t"ُ]G7.g B)MEA3xP,oP- =]8_/@H@1[.g@>>.. ,3g1M}?*b?^\Eh n;hX5\6qͼcWkssev[)RMi޶w# {)2gpgF*{>G[}EoPbQ9D6p}Iqьw'#jJ߭ݺY}a\yfs3tJBVI"XFG:GD')e{6e [YQ'f4~DޛxT~'{W[~=ȥ7Wo.!e"1~5Ș6C8Mm{m=}AogJҧ:ZwvBkN u]#Z zx;jhjB<'!sg/dDDlp&BX=H.EQȲSL[Jɠ)r WWW" .Xa ^@bG=lꮲȾHz7d b^1'l<N!o,P <>"/=YhtmsJHZe9© T Tz4,!L goW+RR8։wG\Q%[6єyz5=,^V35|N+B\> DO XYH؄q#qԸv`bLrwΕ͕x> bu|R#TqpU%D@?DSs2^BNE=rlrT lۜ-Z疁P׈0ܖ\ 5y|Lˣ8֖֖Vkk(7͔ymܯLi|^VM)o4E3 GM͌m|>3U;cVxѼ*IFȁX1݄MrH'q!ՑAYjF>5t[ *3P)I)4 AwڝH`(z3g kn@օ_V%.h?1aJi9J$ rI5&[%[= )d_3;*{NTt F%mm+z| ު-JՎi=USg6 jaW~3pԽX&| {u> k^ҍPy@A0ꁮe,NjY.P12+k:#M{)0t##.27)x_X&ܽ<:-8i=wRZ0!\#݄twoxQlcIIw9LC<*9rw ߒ˃~8[*[*R<O.T_1U<$5$e=^NNUv|otᾙ}3dd[YzfLjJ>VgNi_=JTXoKiːiY"j(4V,Ilrf)zOXi%#n-[Wh| 28/V~jEN| ,鯣y4L%4Xw/$$0U;PK"P" 6q, `4R_L&ԨEBb iҐD .Fa"$MJS87[st,!}.WiP/}Uz{usm*opۨd9(_G.A'Agd/ΙH)cxSRk8Nbߪ)7y5.ֆ4?;{7="$\OWP sj"Êżpk3tD"oᆳP1VwŇ <"d=>g$ XY|V Ȑ*Z,\,,cŕu(0#Kn`Vʴ$ d BMKb0@U=B^\Ct+ 9,e_! ׌hFx{vdA D$wFX[Z۶'*+ѻ]v7e= ;7[5(,b'Ly_ZO2~m4 xi._c}Y,QMgiUۏ[#Q__16f\9]=eCn%m={+?} Ɏt#ehρ~z'@o@7-1$a5CA0V(euq7gK-y}"r 8Vhd42u'*Εpˇ?g|XX6/c޾O+,ojFSZ*"p%D[O8 }1NTCByq v`R6VG2yBDɲ!29Ljd-C!iBUvdr L(iH:Dc -u f~JH?p㬎=$/L?'*!͉z/b}܊>:h8aƼO..7Rk% vߵN RO3 JJWjk7D,(ij"ڱ |%+\\ǨHhAj;WwfYSlԹ^/Ӄn<`wAIdIK| ;}Gc$!222*BH22W!22Ҷ({dd}I$q{:}}>_׿y/ غ6 }סv>[K«d)KCE\ee.rr.^u~r Z.Z($k9PdA f>No([g4|HEɽ Jhsĺ*zZ͠q+QO➠Mv ieewWDP|mȯm^W_OW[[)MvJvN^ϋ?u\yyr:tgbOEP[(3-W:tO.Fxړi*w?dctI_]>WZ^[BwcɹX̍ZNO @SNI/>VS|ņ -_5?_^nkko@l1;}Gˠm?9,PǖA;բWx!vݥϪ)V'ڍDD~;&ELjDKu#Ʊeyu!6Ꞅ3RP2sbK'HԾ('rfoN۪)!IZ?j>PoCe"bG~hW]Δ(@e !%>|N(v[ϫ9=XMs@"pU1ϰ\\k^^N71c |g}I&V ϵsQԳ1!+'p#_/`s>9VVуNNH--鹗r/ǩCfh; .!9%#~hc77v-m#mݳjxNHD|4ՙB[= H t/FR 4LOrJȫ*lpss?R V3 #2͂v\@CR cd`$26O'4Qhyg-HjxW **O 3¿ǂǂ<|/o..}@jlW%xZzZYOkD6*Kxza[5urԫi(wܱƖZ<<@N0 "_, xt,F#1X~zm+2mqxx7.x1a& /Y`iu SHŒcZ*OI%>eڞ$h0JYϷq||JM7[w;[9jq3]gGi !tKǘp%I&I^N:W+#O];z!cA6{&2H+1c!01_ 5SMb;vSS6sܟ{j%<%]qM_Hjj\d]? k3XHHtUUvTTwb!M+~YFj)o$koRz-f]MV黤PPo7 dX~Rٟio{TöĬNj_+S$ut%#(mhC-k4ٛSt.m-dy3m"ǷpnVҦ:N3ll{IBN;.<+Mֻߛ :ywW ?s{VQEq y1G0e)foTDC%+PsZӻmE^OH|ch:7-ASc/`u`[y悖 2 +32hPjxjl',s$Iꄼ5|2,1/bX$|tdϺTF|Zr((8[',/#0Bzo4?pEEEwvudSeK6Hڪ) DZ]]U)w*y]6eUC;hƔR{}~V>#D߻F=%\>1b]P~~9_,EE2pB<#Bە1lv\ӀMz]|x (Aeu aodCD{;s95^ΰG܍4<<9Y;` 2A_;jNNosKTTȦvdDPڪSMTAqAnhcW%rڋXGnvߋ,|\,L\o9l +C5`﵌n͇i0 CB%?X|:kByOjϗw_/Yݿv̺<ⰯT昈]\ D4"++d @g{կ#ggri^4hئ&v,rK3:LOç[J3K|:AP}e)}qF7L&"ǭp-&@7Yzi4dNlV[lG(UE^$nxSW*5" vwU*;?27Pau)|k>Sp }kemt m ٚ^ԻdX 9Cv O^ g#)|V%u>NȃSn_Wʺ6HRCv?\pҢ֢^)vs$"ff*ʉK^25"3󈃛i+Ȧ(~ݿ}p1[5uzW,HOnc1/Z0_DkrT Ħ\ql<OL9LsNeNaՙ֙{_d9]AăUacFhYiYuZ 􁚒6Xwso+y+@BugJͨf=>S'y=<:8$*JS:rS2Z޼0R}H}iuouro\\㡩=$1w"%Saߪ)ˍ8- (~n9aE5tyh'*!HL^R>EոdeՃs⪵Z!QO%=lU'}\F͍{ I~d:_7|Hkvg, ѺYʹ57{dS%[N~{9&yIH,,i'SBɭhWUUSc c ]+\+ I멾>gGȉҸ Nf,'Cֶ}6l 'eWu&{JJZ 4s7BjJJv=0ڝU߫j F&4$ 8HNGW>Zʆl*H[ٵt6ib-ɜh}yM^l' ȶQO7@AKjN+={fq]yaV.@l0~E b6󚷳qgg 4:Ԗّc4 i|CdzdY~| p9SZkCksR+V#HvE#u/.3KK~ 4eORtq{w"e!N2T=[KPYvBd) [I(&*YRIϜ=\9y.}͟q͸ϷIٴwfBMNRmo**}K;d#HUT;b=&o'NvKj2KOZ,Vs.,[LVWI&ϽvEc+S|.:ˠYS:Q2kINoYʶl8ݤ"q# /_L,?-T*14UWo+6]Ec^ި ׫07hUm9l"fnrL٬Ǵ f+U|&Y~YyvEa/UL{z5yl+fG4^^eP''i-D%wU|n] `E7[ٿV1&dKy63euS]kAU\3?*L*Y&Ԟԋ$K5W^jHPWw23?QCgDó$9w a67Hne OZ#ٟD"'r27m~|.vQw1YJ`3N'0#Ehs~z#5@tG0rx2`=o٣t MM&|b+o/ EUR-^xzPzP1+)чd ɉ~,X"F$⤺˫LݐjM cj 1*؄ާO{x6HP!!A6螒KjH둝Jd;Rf!é.pNYwQOO\zi[\jhp-d11!h3 LƇ==v)){3gm2\ݦn{T>4ud"_ agm<7xnHqqE[#;5)t@1-w8ǛuvS %HWaeç}_Kd`-~jaPscf3@ olՠ=H6cO>T* KgсƟcW9jG\]0,>$M3 'lpN@fY dѾ817ȯ.?2]*,~mfcR"#}B3~9+WblX#ǐҎC;-Ǽ\{5٩ ;vWb ~en)NmCQb SH`1ƶ_l\Տ%蚖N-v6,жVl=KB;)ON\NaR]YZPj etܻꔺyԚ$ ):?DFr>:+:F&Q2JJAhTi)HcofN}9$1]J"s̡2dvk:k O. 瞨ݸ3p1K&q{DO%O!nbcTK$MtqaA;c8Z߳$#2`| Y^RPFl5.vM2rs= %ۅ%PE[Iw$oo+ no_9 2 5r<>&;@GQNqVr}px0YI p^{{,)h EU^NKhQ-Nㆢ!V Нֿܒ.tA1wĀ[ gN`|;$3h^b*I>} JԼlWݾ22կx.+\Vff/ax N-9}}>#OҟϮ]];ʑ/ys #xf:)L}gDǰ7& >aEnF #mxt@2mG,1HL.>c6eg¦YQ %Cj q/܍[YU<꘧0>w͏- Zz=EBI)9tvRgtԎ2/6Z>$DD dxE{^ğHlƑ5QѬx C-{ݞV:ީHݯ:X]$sZP DI%pW@jPsLD^me8M;Ewg]1DSV!+U^(rx?$J$j8FL`ojij<(ct˜ZiwSpbƷe#pddEyDD˿_XcBYHYY/98Fml^zL0R@lѺBWddN9wj{}iXRHvrz @zz̀okǰu4FphTTz<ÙJC= yMTc?:*NɂLap< hZ 01n#&)󺃦=#AѯJr-ˋ36V#xxݏdeu6Bfr!ǺHnwwz*L1C29tl,*6㌛C~?igg|s eQM^YXVtI}zݹg 3xE>Pr.@eYםֻ.s9,'kȣ!#ݾ sP%Zľ*w`Vq/ߏ9ho)kkE6Q>ߊR%k$w]?SA Z.W6Zpg*1ݲy}Tw*&,)3Li1{T䪼(X?$1GGxC=k""`MsUh*͚RRk2OFcF(yvyv~dY3S<Ĝ=6z|kdF#`QUO/_W0$ԋth9uȅN@J[f1) ddHb|a8*Ư-m1:h1Uu=%HJ˚Þp6m74GMBx_(YU!SVIř~ʥ%o`^v~';T'gy[,ZSHvvִhwkYnYR&`Zh=EرY\;yR#(śpd/BŮ-vsm":a![&fٮ9ƚ]wnNVV؟f;+%  ;X 'epT, .+C= Db{6n7b&]"<;)jE6??=(^Dŭy rm!_䥝7h\AګW.]mmi&3lp9RU-#ZmV;h# 5ؼ,cK5CC[?'㻦nPמ\eBrnX/nB uc+۵$!YCYeT?} ͈ %IkgANjgᥱJ#p,x^ЋMmx8'/!#ycnG#꽔 A3LJ1Ai@O=1a@_  [Y,ϯ'Swgqޘm﷣LLF$;y54hP# a/|>ĠĠGJN9綳 ,.^xaDgYl;u._aYɚ"L b=I@sa'Ry{ Vhr`[S+c#(B`C(a:aZFBdx28Z;; ᑃ4j^ /FHt|@ Hy$ 8w$njcG5dRK12gƸ"},,ru0XYa]؟XE i|(50̣ˣs2'wߣqnb<unS)tMK"*i\ٺ}ߙ$]e>o&PtGڧ@9OZsb꾼X4δ||)wФ+ N+۟XShldi?$;'ׁ}.t偽If>.6c{>O@)|Sk. IpTdpNEpKKv|XӧaHmM0ϗQNbRLRGjŕI~lcbXeOoܘSD0y EǯDUI)jj@(ʟox0 )_ zdKYnwjt: R_ƨV:*&)Fr(#U]Hmŀ3c*@/ܧPb&mdPc8):<#*?Ce8$ʏ<{a^˜'!{T!S`FME (z1k㴂-Do>ǟ>1su'?w;ɉ g*ؘj$M⛺8RRVVSyn?!7DjjPM&3*+*+ Qs&Zo~mH;**6;g Bߤ睿2ssrb{IU,WX}׿mQ]`xTwu̾3Í/EðZUET>N#6A[P50Wjd?S t&,,YAHPo$Y:&5Z fkR%7'7 R[ QρReLL9bwL[dەk}+1ԯք˜6WptcEzz3bפl^ټx<Ļ uQ;Áȇv'4&4g r-"vS=ʱ;t]&vl]Ax mGSR(q=?Ț '*2w ,JYㅚNc's SpN0I$9gWM ek-e8nOז!1=3 E%{fiHx+K)b+o|tM}}W?b '!:He8^E^= י׽wjm,(bɱ.8wNk )g<݈F R0-cZ`#+R kF$Tx*_M53Y,!6BIxpt=5H?, M_ <8:A 68uG\Мټ;GL"AJR]JZ|WzvׅeNh UX9g,f͖%_}GSS_e?-٦zOntٴSEjRju-M boz7zH<+ N݆gW!8D9h- <d%cb$ FxWt,BU5;N*Cr8#ʙ'dq}=) ILd;N<0OL qot 񭑔u#D)LTC9]zm,u(eɇE 7^odʲ!vH*͍,OiT/i6שP)KC25.:]2L$ۭc軫܈g3JF=!mo󙠪:]men|BmYȡXQ-Tq؋@ m[Vᅯ#U˽Y=bVKC0{c]& +r+guk3YQQ9_kBUܮGxH:N:&rBj.;OʢgzND@͞\֦֟<\PKx,Ywu?#:{z6!ՊEdrv bV)쁉0iBcǶH q. ġB욇d26T ](PYȋ`PbyS9513NWiU0Nl mu;v8BCnnq""uhYk Ri8Rgc55с))jn DU.ABD(۬S;uW+ENۃoՁǑ|H܀KΠy72!kqxR<+wz%1\ ^L/c!!]~i᧦ Jlқ7k/!w+iO\p\SP Pw)QHe賿&Hsx1cޚ߿b}7v=鰄dA+\JsJVV&uNUUEMkp޵b99t4c ;:2yE?j)W4\)|*Ȼw =આd]0 k$E(RMhS熲 MeR6B`3T0Q0y~1<(:_g'F-d:$ ;LcýUc32F0Oo.l:0`LK[[⑰#a,}r6p""99o$V=@J>M3u/fr[C_ pOդrkS.2Ǒ)iop/ (ksT}@ M1ÁPKDKDKiaIq4 9E!WCLLz`~Pި^ѹ r,p]Cʏ(4aɉR(RK&y5ŞB&qJvڮvjjY/w:ts[D恛;󓫮x;LnM!Ѵ#X,fީВe=8}\s<6 kvHo,tl~ɃѳH'v.o%^z!1s]!~ ITXL?"kd>]w-'~W>l,Y$9cz6YYНI7'3$g~-kϋ/N}, F99ݢH':#u!PW/@!;cEln!ߪLh쑼 j|0`} Ѻ a?6rÔ`[25BD98ksCidTNStYכobҩZ1˻xX}½,*9r,W'b' W֣aFy8W\ݦZx" }we׻K$)_g_>p3륡7HUY&^U#Hm:.[_γ]{ꈯ'烈 ÒL*44My rFQU*i4 :iOѮM3p`NT#a2 U1"18sAZMܻx%?_T^^@c$$ mvDDbmcS%$wp<< XUi}Q3-;`ݗeM7`x[a]-;Qik ?6 9h!'pp|$ 8Ơq m$ݥ%bἝ?!* ׅSD LLIRBBnRșXQ</@C@pչbȑk JəO mlᰈz1a$Z^Aѓprn0-1H~epo.gSS mwJ)~jƚxlufT6- !@eW?r|vv򧭐BS5or 6JȮ9 !37j5 KVuZT6z@uO]h8L"&QtTtk+{'nHbbﮉ/`_Q2<&S3U wʞxfJ3缔Q1Eκ(Qޥ}vcIeGӇ 0]h>dX!Ӟl¹h帚e^Φk24pΪp1r|2{^'B [$*iPz5[nӬPo 7uEmuH.ۼE{Je3j+;Jhdw07fSɝQ֯ K[9f}Y`|9~ x{y݇[5g6HofL1{lgw;bb8EEčb{?ܼ8?뇳^̾Ї~|~ة̗_ '0K$-hGrdsHq!(؁D}o.z 2.6.FHM!=\#vLچ}~C6C)k ҡ?=G~jMG ^n";֙[@MNp ;)kYTdoY޷]sy')F,LA-P㌤J: gfƀސֵC:*79=YЩNPr&vh.4+TZkDos `fh T1]TDa $)jHqqv9 [VCe쇌!}-;bjy!>mr:{^xaD3t#hU@(Ifuzաm殺.{R0Yh+%$ pZ-Կ஽c<:R.=N7+>cTk9R:ՠ愆:@tINb'K6<7ѳ=Hou[2ɡ5W)RUbb8æB wH&gVY7ݟQ—w|/A bwgd'*%r5P(~?Uk|l9/(K=Na5̸#jgsvyZ]]xv78  @i76$I{\i`Nٝ,]WHmx7Hbe44Uݫ2+u*xST>‰imghEWz^$\= zQuzR+ h]&TF$CGSyںkRvD$Vz[Ѱ,@!ݐiw6{s2I+k?y@xo_f9Ev01ڟV,clRRqmk?' 66O[L[|etiD?iޘpIITfgnMM6~T(GA#).o7a#WԕN}1!Ft#zHߟ)u"H=]njd8*⽟]49/R38I^CвCMݰ(&5kVC(BZlLu?stV)emuZ,9/RcO?5(.-oqKK O~c ` 8[3gA:l"ec# &F혥7Dgd|6Rxii}Ǎ+wL\PsU+s=v&DO(Q9Igdd-Wꇡ0u5l-8@"t9CH9vH2=nW !Z8QOlE9}Q@lRFFӕ6qTy M=N8!'ߴ,B+ @7 צ.ClC)P 'A{ONCFY{rSHhr@!&8př {9!/;4hUuTulJ ]ԄyKyATC/W W0_Hr7LE7N LqhЗvq@b&Ka RUD $<rq2k ss222u3'zaB^R`uN'("}A>&E ~bSHV lbv "تeEp_xV oϐ#ET^EƧ4(BMm. & f7}b/w@ q17+6m/ L b..'nȬ4w?;t+s|_ص\A֬8Üj;d)phFe xKP&q\9w1h'7!Wk V~)u~٩ ~}Ӛ{fN4s#?G  S0꺺\vί l+ ?Dc(EϱKi*,@@?lU %.3).,`(H3;BSm1[CSWǼNM^[EY9~3CD/nCT̾N?:V'V5;z!;pN,sbwNJKZ㮈ĐӦr(i|S6 9 ;խtwe 妼0̛sSpx0-Fgv(jQ}@eav 3hSfS x< i~VJrx+a%HOb>KǺk|0wl`)G)GLh+$l(6n" C)A Y8y˂ۼۼX(jE]Cr%ßCy3+u*bSDu bXWRIGR!4 ;z(41  r'amZ0Ӻu'L  ڽ`rTrw2ĎDfyA p>M6 jz T$)=<{wOO*9lyޛ7ʧxJJ| hn~H?mtz\:tة7!~Eh|[(Jk E'{Bg!zŋ30pm]yS9<~u?{5:jX,ؖR#>Z cөigFPϨ:yʣ=Vdq$S;swK'Qlm9i >m?OEbki6WBs 7 ^n É*@ "$ %ɸqq"<]CVH CjmV%HKR;#3*Zwqql*ܰD1F*)H1D_ޒJY]ޡmo:[%W|/FR.-`:Uec)+4pl~tt<Z =*M; fG{nHJA*! !qx*M؟E=d wnc9$tv zjë,6!-տ#D]ת}łłjOq#11m랊j#}ڗ]~Nq:;yAfJ^ꔛ`Rzk5XrsSaL{4Fk} cyX[{񾀾dOtT*#:̗ѝ.d^iC9,ٛjxL/Qy&Mfk{cr r}6oBeT^*(H wm?|!MfYG+yaa̱JU&*IdG#9 r(!,~˽W_vIdE˩1`2`F!}9r -STD l#JǐCzjrvQݤG~maJ]gvH,3yI?>E.Xh*v.+ɾ?LXWb1w Ra{g\&fXwW,yԯ/s=s_ Rv^b'=[4 >8+c^b=znM{iww_H? {BENJY [ ZTvBhEuQ1SG9 DpXAC}71̶xoAŵݒSui~vmcǿ}3='MMIl8 @ 'e/6Z5WWPu׌gztύ=Ҟp]jN%,ut8Ҏ Xr"߾aPĹ-nZy5" oVs ZyqZ\\'=]hKM7]g;!<׺ʪ*5BSBJ%)Ta)y0-Dg¥f dOƈ?j%~vHNLV>'x4N[G6['ص03 >ķ=Z'1`5T.aaqM.7?sL}4.NzKGv] bIEg=w#&,O!27oFk %IG]X2Yw MSQѓOI1kɇK1a;9lWTRD؏'[6,( 0SPQ>X`fM}-fCf7ZwQ%FYZz!L|`?/HUFk Bi1Zf'Cn!pJ6=*!};zo8 "E(VnHdI([.kkK5ed˒5*f)eIZNJx|Ιg~3?szz3?s37y a>SKs|yp6ՎizizI99!Du:l~ ɋH6)GpKRh];xڲ^Q0Y3HX ײ==* H JGHp^(IGB1a= X[w?̷M:O$eڽe$- =bgߗoB3ooZW}nnBYe';8!{Y @YMXM8۱ڱ+\)E $^o^޹ݦlnj/Z!hP#;'y #IxK=Rl@:W\ д" Tb~ezef,0t\d $S^J*1(1.iӀf}Nl*KC;B!Z zţW5ǯ蠃f&*b۲vYWuӳгRRO,: ;F̽ fsSœœ٣NM ? Cu_u fG4_ vE-s.nOUpE`!]080Q(pō+r\r\~ XHqKYCa@*Y5l?T#!4Z*TRR^g ײ,qnͧ!/Q6QӅ^ ån^%%" FZ Ճ Jjk%d16uh- ea1Y^Zd{;J/˖2ې/ iGq+tȘv'U~Csx8UX^+}z,j_S1>cڹ9[Lj4=_]₅L<Ϯ)*ɿPEi8t;Fok2;/U\8!-+N8S>Vu8D**%+*+A+ %UZ0'Z"&T*{ެcs`>|uwq/cM^;ֱh0%dv\22術 PXgl׃pH4r<#ҫ 9yS,EwcxEB$\=KhLO_>2IC= [cE->y2R8X.$ք}}魕^Gá;=cc#-;;P%Mewz5~;OobWݦ77eGIwLXQ\·_wq2 ֣m lX:  (1̏0֕7/q||1l WkGRnR#KITUaL<4@4>2[AEO7w82?v{f-%ii(=5__y?4%;:.V8VL$&ƨs3JM]v.7r4a7M Km.8s7X5I~,IO|Ȁ;1L˓{a*ABo4Rv%姃6(/$ )۳Q{ՓW@ ~3 Q!A ^]`)$v[rr+r+vw;ş66QK ґ/9W_).>]j&qb-=yDzn}sa^Rpy& N廴dbs #^3w7y2syK ?n[nHgnˮ_1 o2tla%|+TPj-15_'0KQQG -o1I_ Z#EWYYӧ H-y'.kOHm!POHFr( acD!&G&M\F7t hjn-T'%55:/[L^+Ts˛W!r 9fhw>|SbPѻFdMenn*j =  IIcdp |QNp+Aȝ5/R"EӋm "6cbۥR=@*Ohree'!pZǂdWu%@EO4!!i_LJ7h=Kۄ8 cK,c5g>NTJUUl/GX -<2DhT!JҜ@J(.~hV\~X%cly|2qT\ܛ-{Ao@x?IPʄ< B܎YfI]=YfMzDD+oGp%-hfSla2Uo I* n;'8'ػ~)#_{ݡ ilnmM=B &m`׳81"o#^XЄr,;@I%&B2;%r\5gk>pMW1ֵslҾ2s2҅sQ#Bun"y #2<| BSv@W蚫RX23hSn,{0Z$iL֗Ja4445k~uBG1|%5~p״!k˩ck|QM|T.}-_8#KEM{6w KdAao_R[8]6{&CK؈?m;nUJ,|N!}ލ@a{]F)ӯ~VsR%@m{c4޻Yw^:OG֦HeLdiuۯ>3Ug940K', ,!rLM.kI:!=tGY-SͽQGFmૹ.lԊ5D1%nj(ɬ.?t1ܓ٭UCq؈WkkLĨi4< k-؇Jc 1=ɻWjI^ߏ8ҿB-E6¿{i۹t:5kw}r-y n͢$KOȱǢɤ#H׀o>|2ZKchՄ GN\jSjS.GpaEvS_y0q}+-ʾ$}Kʒ8Kt YBN^G֔%;-"y|~ߙgyfg&3sb(.Dϻ~[\3ysZHA黝",kJX_ƿhsz@ 䢎x{Q2!W|:Ѷ4I6]g˚&{r:3mNCN8SS(FMfYMr^8p%=qIu))#'M겉guu7v++]SٛM"j3y LBZXB`'HC `5+bsq)䏕I['p'V&8[hNJ+y^Er 2 )t`@+џBD0LO"8<5gBۃYd]~[4Z4.0{gΐL7ė)R!\PPttEδ(K2R; s:;n4ݺٮͦޛ Pm-r=Kq (ה@ t,?+'"KKզEbRh~A+8 _9(P%l/^oH 9jf,@G)VqRy#d GT%)No H|ChmiYWĝ_ QYCboz͋FF74sVǽ$t>QRvM=Mݗp#/8ceA2HY5W Ǯ+cNZYygLsBz)b BB~#auLOgޯLCnhC c}z Ћ7 B a-x$jC لcL!b5=rPV޶;[ 5|'&N_^h=szds/N6N heRy~k%[$,-y#Wa7wMQQkgcOni),٦JʇygI~%L$EECYkf뜹#p8L O@oBP7vq$q Lx;p#Ec_ix$㻗iva ު1A.pEPI~N!qs&d-nkxXj((t$n 7=&ZԦi@cLaLpa~޾ȊҾ[MYU8(ʻ'WoTfS:pɱ.8ҽK%H1BսζJH/ *jXWBriHbߐKڏCZdx Λ$EgH{y%:x9x CDx7d 86D  8\w^L2 D sy#WeDpIdkwo=[K#3C"'ϰ]FR Y}PPZcc4}$)C/R;Bv}6uk&Cra~GB"R$SyV҄?u*3X S %(<L??$^ph\}H(lHzOR\qKhEY:c ҁ3Zu^VHR}e>EkWROMGp0V ڌ[Yt&:$B8i/_魵z$pG8t;X~ˀ7ER8( ×TVZڮ|ДeKww-:p:-&ڜJwsumgϖ;*P-YW 8s7E]Toa +"`l] EeˀDRtuLs\jS, ybçB }W<fCwFw6>ɾdp (v73ݝoJT38xxw%M{giрuC] >,%`E6S®M+?B/f\'k,[:b}\ұgCyk>3,RirS;CHik5~jgg? X'T` >NQTC`R1mOl>'?̬刧}~gZ_1 o8şZV2rxGpV %6dٻMFsd:鲏EtCdG2i4fs͞Op<% 8̈́(Wnzaoϸ^+B Ib?xa0cաGa??sr"[,pq7.GŹD{.]]@#)_X(tw8Ě/xK_M@>u/,9_ r/&+ S׾\"pW)mѿT.7*UyW8& <t3]2$TT]ݑC:^^Cݶ萋ɽt*pbb9OSJx#-J uugfZUla1730A6GvvMljc_p)Dek Th/$Ljh#Glv<ON0'`Q{-G+ℱa 2'Py,j5 R3ѐaJF@bPdJ UU"BHfꦚ0:skG8{W)&\BXaLiQ@E͖&&ޖ&MGVoj"YFcoTfS&]Ynax]3Gb: 6 PJ<`D>`\tvDZ";x {u˩ө3)>&X3쇀U&Ϩ%'pH$ `H!D8@&s%/BZ\kT z-Åt>p|+R}ڗ}{;"C"C;%JA,=* |6:SSL12^^vv7HHa](or'kp)lEz.T8?n$ F<jjh Bb?\(9P׺EqȬ?r60:Ro~X+24 $0Ai պhڡd#blRq ` 0:$>,|T!~aNg>;S]5 :ZmŮ)P/_?8HaE_D YFx bb_77p@"} i9oX:]є!\@Ij rV4SBt˪O'O` xei=^5/rKõvvj`40u-o1@z,•ӋьLreOS,ۻ[nN v* dNm.wC)/{ 튉#oyfʰmSS[Oi+]+lC9l埛:)]śWoX4p7a(憤XDǽΕF2%"RĦn tbf+8q.I K_FP)[9>qj4)ݘ2`DR})LLI.oܰuH\]2 ֿ]8o!dR "G+ yF9GHC)qPEbַ&QUf̬W s,p,ȟ̟Pmo6*AB{eeˮ{Jcֺ(X9)}fcJUFzap\TST;S"rmܬ ޝGC}dYJ CT"-h"k%dDdM~:ֱSv%[!ܮ==={}溼5b?zhIQ &x %%R ]YuHc fփQqq!Q Ԯ18`!~T;=B|C컰'nh$G,+asv2Xd}5~\||ΰn=T {0K>Zmzz 4 40wG6G{pai΍:U֩Kp1R;~%рW @Y+wI&ɧ_`V_SE8`Ho ŷ"$1!1Q~4t< U[@#}[L]ffddnYHTE'6'Eʮ@Dm#Rrr; Xx pxR&A~sNyTR7Nksq&ų(f3kD \\Ŵ ~NN{b&ܓ RNH޽WWhZˏS$/~{o^Vy\:U֩Պp普xG݌oUJ̯miAD,suS/#)@4lBA^]%t#@8:]bA{ҥ:үϯ7TnO9GFL >o%E[vG Rxoh,[%n޿iq}Y9h$jYhYp+Be=uO{!!7m|Kuo /Sߓ>yRԞsYۨSk}grk SQCjb*zhb熠)p,c8 k`0) %r J_?@O%<8 6G(T¦ͪ9rh]Q|͉ˇ3:Ë8 vRg B veH 1s׆FT5WNyˎɎGҁ .P&Pfrrt^Gk-5,5rp!cDᝒFL>YՏkGglԩN5eq:<61^x؎1/]b}ـ0]A< I0@(3|-!lW8YRN@3H($ٛĊD  AK < YjHQ(FyE Ü)]H_eڹe#sb _Ȯ 7xopFF(.xHl?1&D0<uuM11_"!qԒAZ(uF\T޽-pmf?L$R٧6L̅ 8U/RVӀ „$$Z ie9 &7i4 ?z?ezK&HMDSG(~ xbjM;t6)$wP5Ff?C7q}|Qyr;;l.\YLiы ('..!,܃( + ԑŢ<$d|o߿8y-U)J(_g+k%mw!Usx\g!v:WWB8_˗Y!Y#p^[}Y5wL=#|U%1ހ93RV'$%UxggfFB[o1=K!>Gȳ̃8NK *|Oܺljizvn-|;C+ pF:949G= P\I1/BM 7OΏI!\G=Eѽo&_rƂV`^`Dx0 KKsӐd$*^8#7#ϵqG?v}Z?sE'GN?'ڮQj:xMd ) F׈bL^o*/wG87-:sO*ĄQ=II K؟NYBs.I?a6R ~?|3mr6PENSSoy6xV<725.RဒXVV>eON߈7j V(,&=KnbS O+$^@뤇ls2qUgVA~ԕwAOk" To;fݲo>InS`j|ךG'Gd;phjmfz5Cw[Sѷ /y6+b5$L%^`OnšUVUnf738ٟ{)3{KҾң~aˮj>(]b65K/՚PF.?߿3"ժ{k3{e".*,TuNr}eL萷[[;R1ٿ/;.󀄚ɟbYlJî!:7 Dqdfz*}g][W}^1+rkN93>SѽfnȡgG>JԪ35vw/zK~3dݞR'~ҽn\ݢQG>fY^ulzc'J59unffZخso+ ]?:cY;;nťggG>;rrw*~[sKp[٭6w%Ror/<|wzdF:}#v[ۦE_y/}n׎^zc9朅}'{׺=|nOumkgk]~w93,Lݻ#$o~!;UN|rAg35«%&^Q'?znղ9~Gې'J/#nS|w_־?/qkw=>}sӾ9?й5:ZrZ{7oiM#R|Z']>[U rO<-uSkl(uΥp&Rc<屣p x?dحm=ro}W~鍳߷vkᬩ|5K,Y ^ڿcu|֌Ǻ~Owaˇ-ixGYL{^έ;jv33}\~,>ppC.tؐ8o=qOIz4m l|Cޙcpon^k/Fn:?~ĕ/{ˮ /ߴٿvy* Zv?aƮk>ב:h晓Zwz|S.lɗgSc{r 4|jSMBs˩x, Xfo7{nxQ6nE]guHřLfſ#t}IZ a.F--U_:wWQ,rnϿ?yٕ8Jul*-&3ltcWcJԤ8TA@(IЫhEY-ZAi_MnXc.UcxQ&jEg"A6$t6Yhk L'#x56h$nh]5j6N:bֆ|PaS!KsaS݈eRYy&Hd&_T6WcJZ a.F--U_赶zjʎTN7 lԆE+bֆ|PaS!sa{ċ2T,<x: Mf2l L#x56h$nh]5jm @ ճF-V_hmhl~6ºT1檱G(J͢3&Hd&_t2WcJZ a.F--U_e@|qmj$'"t/646=G(N͢:HL&uAoH4s_RBG|KKuר/z>'g[lKE#bֆ|4/æBX5\5eRYy&  MfDq]NhWZsh4oi 75e4'f೪>"F&V_hmhl'J2l*usa{ċ2T,[Z tF ' ݮcP#v+ew^gzԞPK"?w0Mpar2cmdline-turbo-1.4.0/tests/build_unit_tests.ps1000066400000000000000000000106771514221355600222330ustar00rootroot00000000000000# par2cmdline Windows Unit Test Builder # PowerShell script to compile unit tests using Visual C++ compiler (cl.exe) param( [string]$Configuration = "Release", [string]$Platform = "x64", [switch]$Clean, [switch]$Verbose ) $ErrorActionPreference = "Stop" # Get script and project directories $script:ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $script:RootDir = Split-Path -Parent $script:ScriptDir $script:SrcDir = Join-Path $script:RootDir "src" $script:OutputDir = Join-Path $script:RootDir "$Platform\$Configuration" Write-Host "========================================" -ForegroundColor Cyan Write-Host "par2cmdline Unit Test Builder" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "Configuration: $Configuration" Write-Host "Platform: $Platform" Write-Host "Output Directory: $script:OutputDir" Write-Host "" # Clean if requested if ($Clean) { Write-Host "Cleaning previous unit test builds..." -ForegroundColor Yellow $testExes = @( "letype_test", "crc_test", "md5_test", "diskfile_test", "libpar2_test", "commandline_test", "descriptionpacket_test", "criticalpacket_test", "reedsolomon_test", "galois_test", "utf8_test" ) $ObjDir = Join-Path $script:RootDir "tests\$Platform\$Configuration" foreach ($exe in $testExes) { $exePath = Join-Path $script:OutputDir "$exe.exe" if (Test-Path $exePath) { Remove-Item $exePath -Force Write-Host " Removed: $exe.exe" } # Clean object files $objPath = Join-Path $ObjDir $exe if (Test-Path $objPath) { Remove-Item $objPath -Force -Recurse Write-Host " Removed: $exe\*" } } Write-Host "" } # Find Visual Studio and set up environment function Find-VSEnvironment { # Try to find vswhere $vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" if (-not (Test-Path $vsWhere)) { throw "Could not find vswhere.exe. Please ensure Visual Studio is installed." } # Find latest VS installation $vsPath = & $vsWhere -latest -property installationPath if (-not $vsPath) { throw "Could not find Visual Studio installation." } # Find vcvarsall.bat $vcvarsall = Join-Path $vsPath "VC\Auxiliary\Build\vcvarsall.bat" if (-not (Test-Path $vcvarsall)) { throw "Could not find vcvarsall.bat at: $vcvarsall" } return $vcvarsall } function Invoke-VCCommand { param( [string]$VcVarsAll, [string]$Platform, [string]$Command ) # Map platform to vcvarsall argument $vcArch = switch ($Platform) { "x64" { "x64" } "Win32" { "x86" } "ARM64" { "arm64" } default { "x64" } } # Create a batch file that sets up environment and runs command $batchContent = @" @echo off call "$VcVarsAll" $vcArch >nul 2>&1 $Command "@ $tempBatch = Join-Path $env:TEMP "par2_build_$(Get-Random).bat" Set-Content -Path $tempBatch -Value $batchContent -Encoding ASCII try { $output = & cmd.exe /c $tempBatch 2>&1 $exitCode = $LASTEXITCODE return @{ Output = $output ExitCode = $exitCode } } finally { Remove-Item $tempBatch -Force -ErrorAction SilentlyContinue } } # Find VS environment Write-Host "Finding Visual Studio environment..." -ForegroundColor Cyan try { $vcvarsall = Find-VSEnvironment Write-Host " Found: $vcvarsall" -ForegroundColor Green } catch { Write-Host "ERROR: $_" -ForegroundColor Red exit 1 } Write-Host "" # Build command $clCommand = "msbuild.exe -m -property:Configuration=UnitTests-$Configuration -property:Platform=$Platform $script:RootDir\par2cmdline.sln" if ($Verbose) { Write-Host " Command: $clCommand" -ForegroundColor Gray } $result = Invoke-VCCommand -VcVarsAll $vcvarsall -Platform $Platform -Command $clCommand if ($result.ExitCode -ne 0) { if ($Verbose -or $true) { $result.Output | ForEach-Object { Write-Host " $_" -ForegroundColor Gray } } Write-Host "Some unit tests failed to build. Check the output above for details." -ForegroundColor Yellow exit 1 } Write-Host "All unit tests built successfully!" -ForegroundColor Green Write-Host "Unit test executables are in: $script:OutputDir" -ForegroundColor Cyan exit 0 par2cmdline-turbo-1.4.0/tests/commandline_test.vcxproj000066400000000000000000000047741514221355600231710ustar00rootroot00000000000000 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 Win32Proj {e2e11769-76c3-40bd-bf1c-a9198481bd6e} 10.0 Application Unicode v143 Console {d0a94f83-495e-4fb2-ac33-9a3ec2cc263b} par2cmdline-turbo-1.4.0/tests/crc_test.vcxproj000066400000000000000000000047521514221355600214460ustar00rootroot00000000000000 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 Win32Proj {48aecf41-79c7-4739-a80e-b4cd22cce099} 10.0 Application Unicode v143 Console {c4657dcb-7b83-4608-a1d5-df38d37c6fcf} par2cmdline-turbo-1.4.0/tests/criticalpacket_test.vcxproj000066400000000000000000000045761514221355600236650ustar00rootroot00000000000000 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 Win32Proj {862b6aba-d1c6-4db8-a893-747cc8b5d0f2} 10.0 Application Unicode v143 Console {d0a94f83-495e-4fb2-ac33-9a3ec2cc263b} par2cmdline-turbo-1.4.0/tests/descriptionpacket_test.vcxproj000066400000000000000000000046011514221355600244030ustar00rootroot00000000000000 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 Win32Proj {4e77ea22-b1fa-4fc0-8e13-74953de245d7} 10.0 Application Unicode v143 Console {d0a94f83-495e-4fb2-ac33-9a3ec2cc263b} par2cmdline-turbo-1.4.0/tests/diskfile_test.vcxproj000066400000000000000000000045701514221355600224670ustar00rootroot00000000000000 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 Win32Proj {6612248a-ab2c-400b-a879-89ca63dbfcd1} 10.0 Application Unicode v143 Console {d0a94f83-495e-4fb2-ac33-9a3ec2cc263b} par2cmdline-turbo-1.4.0/tests/flatdata-par1files.tar.gz000066400000000000000000000054021514221355600230070ustar00rootroot00000000000000mlT" d/o^6RtpWn WbKQJ;RDk*J@ $(P% F dV[o_V7A@aU mg}/_rNdg2si'wD*6-V6j f‚H(?D#H$FCm;{*80=;l -P%E[RVt>3jȟ-lĈ.pkWኧvLjYg|et-7DYS鲢3jvd7Nr)cN]EzMoeAU  7DsGm?leK?3ikz>1f~լ/5=xsnTIxD[^kao\Y{m =:o|M^s1:sU݂D[~j캹Nn_sgm7yÈ!K־;D[ASAޝ3Кyj*.yg^~I&If-*K_s؂ᆊWWs֘O& ;O{* w3i˓ūޝ7ض|D[$I 7_s}GZiq}Mks,OLҶxrCFlq sʻ}0t;}}F}=h9I[xstC 7 Wmێ-6Ꜷ.  mXH'>:sW Y:Tgo@UKE/0p R%IJz;_zo -2L=45 @G:TCy"o2POjRD\n@KyF_,՗@N+O<Qt 2>T:@/JvT&~o[.`>@G-@zO :T#7T.!hW@-#d.m)KnnU~oT\~h8NrF@& NW?j*Hشh,[omEᳳ ҼH$?RʋDP^~hd8j9cUhAٱd]hy*)ؒ{h__}bAy+z]qdgԆ+15e-ᖙ%>gވYh?kjV<]VyF͎ _]8e󿋼hpS-, b ӂhs퇭yIg2mM'&̯~n߭S* ohKvC8l ~?kxG獯Vk5Fg.8[hOҖwc|V]7xk]y<&o1$t{wh+Hx>һsZ3XCME%3x/U4D[8I۬XeIӲsw[P}"PjOzD[ay'i]>޷Qe{&myxv[h$i{x;Bu6vpUțY>->{my}剶I\OCns` w|ލX}(t6h[ᒼd2UmRhwrI ZNL/U?tTv .Z ܕ@; ^z@R!v 3u# c"|ޱ@J *J @/% ItbStPAJ厈UŦEc܊hel#,r6/G CyH/ fwXU,Z9p`P9{v,zZJ& }描\7w}o}fE+ĺpu;s=[-3K|r~qYtY5;j'|9w1'.ɋMvĂ*ÃPL 9>;ݼ3_GkWRӃW?)O7H%ixjEK5У{~53Z-H'i˻1oƮ{5<~~7^djaH$ijqnxzE' =gyd0Iۼwˮm}ߨp=wD$m{!90hȆ;>0N׽'gG;)󉶛;M>t߸pӠ} 6{mipar2cmdline-turbo-1.4.0/tests/flatdata-par2files.tar.gz000066400000000000000000001347371514221355600230260ustar00rootroot00000000000000y TpȾ%B,.diOTB"!(%VHIE"m~7٘Sg޹ύ924q5w'!D})(J~o"N"$# ;'t< $:[~ݛ{1Ucrz{)Gޒ7h2]i;EH69ZX{Xnԅ@S!+ދKG$zĎ>(']f* zHB2=Dp%D` ~IF]ZJMA}2A״NZAi՘T53r/JM'8 _a\VVvjjpfVd׋G-w. )Tt)5dd_%\XF:dM9Ivgj/|^Mkŝ N:es= YNv/V OHNl`wlEUj#n 49nb ĖhwkԨL-MOݸYW0 w~n\WYg~_Չ&W{p^83*zɃUV{wP ,V8j6GD&{'GG\Xeעmrn6d_ߊjЋOɛ_?}$sdPxEaUFq|ٳAy+9G'j~:sfbz>>_Do K$z^{\[6֍`͖y9*.?d{sqԧ݌*:zwz;I*o|(n?=Y)$z -9xm_NJQe]ɫ[v7)v2kzvѠU{vMNyc\ՖꪆFȨZ(rL>LK%eK%w&^}Q`Iat0*r1yczaS:L5r%\xwuv,3?HV+MmrP6WԶ̟PI B(gsUOJ4w )sfͬ3}PMڋ &:<Җ,CmaI5KrG.|bIcK(\H|<$$ؒ'Fjl(Oڙ9w噣ENf1ӛn)-<0cp/W7]nv~>ɱYu+M]b&{^}zÃmmv12mS8d_5^t赫 w YNBM8+*͔57bIV+˨@<%Rn) 76`:B-ݓJXXX,vF7/etm ;Β`S+1"yu,cݫlP6)[yEɕ}1#_;q a2'oUFgU YT> NNbܾ6/]򱅂fVy O*nFxJ#||#Ӭe nE-'>㞽utcw_5ݵ OznFM/^RFOnTpP\{~L\ɺV/7Vm٫ܩLJ1U}fo|*e6zڧi$gX`Vx"euQtʇUj/2V|t^rҊ~{AJÅe7EWN>d3gi.aZja9 Z͝G/"Yb2\zkeBqald 0k}8k.2$f_8 ,NM/\=Dh,cdﶸx@qaYM蒟mzܾfy~2А ~V-Yd9:$_Thݟ;=V$N=C;Ds<7=,wꔄqX)ǣ {co=K ^*5>G+,t>ccGˢ_[^`lAѤ3wi<{e ?EK*}uEmgG۽׉23`Z';.p"6d}t]SU* F-{#l^PՓnso:uTX'{2eu0*@1}e7"c6jMX"-^'`c`郝rrE8 [M8X۲^b*'-cPJad]AYהkM_yӮʺ+9%NkdCc/]!U ]vJN.$3Z)e]R:o9'Ԍ\Dfg$.x($?d97E9"E1޸&NuI\C!˟?,.Iу;+x${zO.+DdĘli=UDfP(eHh|nk7ե a9J~։[<EMzRoVV+L qնoѲЋ J7j@q7<ޛi~6&Nە9isĩ⿓ZVF`i\ dgO\H[A[m?yF$XHn{^Dś~V= I dZ.FGkk9Q,W#rTO'|J.Szȣ[}JvLI@נd{$4tVgEV|L4X|y᳏>f?m%ͷkopSCׅeem19ST=Aժr2"'8烂 񣜝2Cq(L>}L4Y}Ӎ{Mm3T~^T5qsYiIc=wT/5>W(r}xo^S|ty'Y悞6x؀MūǶ*Z/Vh|pH#^79m'"XsSw$8t۟So^=r,us-r'dKK.5J7b_Lju8Dbl̽[Evr~Nq'٥=ŻhW;|Jo۵+ d7>3I6w~Lƕ{Ylx_[z2n,r EܜRDL2H!-y_>yUVE dG\c;&F4bǡ4/ZERpBѳ/7Om<&$ݗ}Sk(y~WHyJ!5$6>֘ԯkLEѥcG5iܸ mǴ m㎵-}猅Iz^yfmtIg΅޼qD*HǍ&{㙟KMOb 4*|"gl٭][ #%'cBSgӂo$CM-ބ3c'{cZ_ E;[ڟAJlTٞߓ,agR+e asWh?sJ)z MQշl56wioEznF]ZAVs]\o`;zw􍔄̕F1"TJN ":W^|,UIpMdc*j O*'OI5czҕi,wډ|ɂ!KԦЇ80^mkڮ6ZCZv`ɏ H~F|=2ğD|q"~F|X ~ᐋ#GźAP;ڊ—k;o y8 AA A>-?o^iDulums>ƶwif@Xn<ѿ?m*8@/ u,f<  AE& [q u̱6-T@*&p"\Pۺӂ&ףAtO1[09u< {{ڕY/>gG1%'ccD}!dps;< 5%zgY0 [hr!33@=P Cz(@血=P Cz(@=P Cz(@C @s8@0|k&NVLﴬOwֈJͳLtĬl4 v aPMPYk a-U ޜ$q<8(#ʼn6t֔<\o]1{IwK9d,IQD*> 8%3g7I?cG PL_h)5:H%e!+]xCt*̕*t-騄a0(:NUdYxALuஐ*ؚ}0C=g7Kz30=$e4.ڊaB`' @?@?@1_? X,4*|?UE.vx?*MGt>`w%j˳q`La>*,dPSZ;fLpRnNnlDjFL/q{"ΒD|{IfO:wi<5ȟ^rK"Cܨ<@ﺫw̑~iy> Wi~l†!,"[( g!yPo#ӻ,P9ȗMӽ5+Q)Zq1a+v҉?gF_i~9L/`֌w5*LjSH N.J['۳b׉o۝ f=8,gRhZ !%"IpPA]8 u.ԅpP PA]8 u.ԅpPA]8 u.|ԅ w A1[(;#]IU-awcX}gCjbfv7pq˿2=w# S 'jM7aT/nܩ7;rqW^Y)GؖD>vG5{LL+ )B&:gcbx= F߾I:q&0|l|tOYqqo 3Y'F)Eh5K(?CZ& !&ccWN%\kngl \?¹~rMů=Q5cL`ȡ{/Qyu8xUzNb`w~L 뱵D\uj&v|+>Cc&k2FZԄ0>-,^}y><@}}y><>\ OmEY?U3F}η=)"Q2b_ιPl pG+/ۥ wL.|BD%I0)< NН" n>P ,H;6YmFvtjԂ^{˅ι{`p {U"愎\qC&TGޣ#Cث70;ݟE?qSZ,m&%y?ʼnpУiŦܑ~b!*adde1m3.iD\gQN-wî5;/uO;Ál\^i5\0($.HlԽ{o t9bga=l5g2Vp5ۥ :ۛY9`VfY9`Vwu.ԅpPA]8 u.ԅuSo3 Mb> MSm$m ws|!6_c+q/ՁH| [h>"kP }ECkc[A5T70..~Mһ S7|봪1w~9C$g_:m._BF[+1ұA% =10& Ջ` ?UV l\.1?% = 8|Ǧ cƊBhZ@Y/RfqNtp+]ScKʈIYtv<;i|_4*L<Ȱ*Iv "3AKܕjc뢬߫iL"f$Z5K8_$o|bi`Dfk`/u.ԅpPA]8 u.ԅpPC]T#E#:?`\}AzoD }$:)%unI05J+:L BY- %u¸Z+7}NBM8ڒ٠/ʕ%U¿NnBtNJQesZmOY} 4dkй?,ɒKQG/Q27U3ֹ޾Eq6<8g[%g⧐S/w&vRܝFp}ռ@%o2gH{Y86WSNg-NW .cZ+L^iB㘴4b0KTtɈ[\ XEczGbij}f}`IN^ B'ϼ'Tl 0[5l ;A]8 u.ԅpPA]8 uw]c9rq$Xx=cUv6tqZ[qYrmt1w Q \E] Ih!=~_oŤAy"N"@IE0?O^͎ԳuTW AY=UI})GuD!1t51B;5`Im`G`|4 %-BaajtNt1;8F.yIM_F>B| ٞA7wEցÿRSCR,HFMnDL&Ce ) dH |dTƵ^.L߿MS){聬%8Ղ¡LMMRnIoS'ރ-%, h |~c~ ZOҡM3U18 ;yjl5g/|$b&DZʍ*tPf%; 湀y.` \@/E" =WYN!-!F㏘&(o#3d* N 6>lflD wN H]npn0LFBbʵzB(WWC Pla߽Dձy>{TJ]8{F&]f-?%:;} <ۊΞ KL$lˡRЀ^REh0⧏m0lTp:~OaCχ.5z]qqxIn"勌;_';y⪹J4U:[Z5:J'|vVU^i:K]Dr%F"PHeFk-;ҿL7g3D3Y2ZZ&_I$T4x}"0s\<0s\<0s\N`*\\'nT"1UnrD#8ʽd&aD#ijQzk~ giNrTS\U:ܑ6bέܿWLd&:ӱHm&qAmLžp|z5l 0[5l 0[5l 0[o!1rvN':ϒoqT#B}O@@"[&.=kCa$g`RY*;6bCqBQ8ggY&?=ElAyr О*%L "/2S{~{r!s>: ^T.ߍ$%zOșkyj7t>r) ?>)wiLGU)qi]H;&SQ CFQPQx(}JfJRtޝGSW4tSWAJEDEh)SD$ҠKҤKL TDP dHQg=s8XXel9D f gan02b&^TE"JMEzuer.^"w_oVU-l.8M?#6\!ϓZPH߃^2m m m m m րhk}ͷ?U=o|빉Y'G]TQ7 n($D)ZFF>9LWwsɥaMD2V?K{Y@mC4æOKDz/Y,/?Dߏ9DlE3-,F"MB=ѲhȂxloc] MÒpcq_tѤBhqFR6dw4ff<8_ۙ9:rKi=^Wʞ5w߾]Zu6~Lwu:Xîۨ.6;^jՇh֯md}h,_ YD0FݵaP% yX[2)Q0#m m m m m 耶FikSz7cCkio ˱q'E>լD==EsҞ`[ 4D6$T, fHf5 ]qNF2$q5b׀CH^Cmpȅ4~yQDQ >cGˌndFo4B!X2OoA]E}L1Uȝ}H峉_MIpŃ?\~Ml`n~NNQ'Cf$ɩDD9б,Fݭ_-Gz7q(ӆ H_GgګzImK]m, ̝%=rӠfGYndU9'sgE? #qTt}URW骪* | J* $$˖o$z;gό<]^k>eܚ/YsHr-؆j =n!s=-Iy 4sȱH,Y"qҏ.wUPȹ\2fJ%V6ǖ E _O;j;\MŋMD+>_EX^}Xʍe2~O^hޜ%,?YBDBJyͿ%RD!kШT s>Ӳ,4YkKh$Zܯp1bỸ9#s*Idm&nz#zzzz̵"o^բWG!>kH uy;h~LqD-Ƅss  nF4C !˘H$m#AL+ܑdx!NseVd%VX;Tx_m'l'ˈV2}v+a,%ބ|tӋfA4¢fy}"][^TۧQapOg>DWN9N苒x-YLkqkگgt:7B t]8NѾwY#֑$6/WHP+)vI {s.B39/)W~8REXq+idwɰ<}nez٠ zݣ Z/+SoYZW85+膩m`ռA0V__D+|@R!{er!ɳ٢!y6ٜvσ Z ixBM5ct_מCmj3,Zphn^p%ۡ{y ~ѹ +q>&7a,ZJ )ڊi.?ȣ>|xSo#]H8ÀJny4ݎ-4-6i|R>Ɖ޾ d(Z$o3OŦwb]]fFtͼNCqBWFᮔ0O%ҿ&co\ő)S v]}6ɂ kr&_XsD;fGFS͏{hFcɹ=UKs,}"YR%DBˮN dEc͊J*ZB+O4ݚ4Ѥv ⭖_AdmjoLzXfG+M! CCʋ:%ԉQzA ֮neniBAxtvr])27C?Cڇ=tvZF'_g2p8@7#wKyM%xMmB=FITPskZr,;!@-Bn3~>z,dbdh$T(F` 9vsAAAVڼš~:>Úsy V>C#f2+zƍBbhTȧ;ޅdZdQɯK8, I+h "hjkLm69:S(K!ZKme!?$s]% 7_:YhJd :Jtd@)^ ϋURG x84Ⳍ^Շ={39]z\N4G>$̄1ל~t]&8txeS”5xW@+>rۍX\2v[O!J\QAi΍؈WWY%m Kls3Z=,&umOF`y;@A9[L.s1I.fwvx[R͙b4m?Ϲ=!EdydIGrm:94iږs%pǗ&,l)+ԧz8xia4{|V4t =8{> Kd*F)gIzW _)ߋ$f sf|r$=}^~eh@c}JfxBwz.  Fzypۼh2<)س03j> 1c|gWcW rM _Dҩ)[ƻƯ!VA31YǠ<)'ӑ}|v0oJD.RUKDk4.Z>2 2$8jŘC7㍕oO ĠO ĠO gZ}sC,Y/tyd+#neq'z rŪm$/mh'KX$/CF箆+png#JiRcE,>AE3H\h{gez292x|YtOZKhaY~&y;C CRZd +G)Eၿ)c+KZ'm͸Eo-<[tqb2U2|df.h]z}inȮt;c̃YFk*}iXԛ񒐩Df5eF+w~T-9(x|Gٛ 3E~:;!IND%c~v>1>1>1SOL5Zo1ȱ-eXF;yލ*FU q4n`3眅COQB>pTHdb+Y8 'ywr_^֕wDOL&]+`MxU|7IPWJhL[T}f`1~M<t7FΈgFC[Ģ K>AM̃4;OEKB 8<4G*r7tܚ/]+8> ?gFr3vvtL;1}9Lw8^g=[W] Um㫱W̬bHJr)(]1>YTpʐsۜ4*}RhqV.WEf M4=Tvon>1>1wg˖Y/JwUS-AX3\;7SxO)#?9p)MlG97'PV:e bC7fH^@]_ZѤf`x ;j8NR[iBBfD*JDHҠq`)݊RJ)6H~ly>k,,e9_wOҜyD:Z &9,դ>o{7cDkx p2SVQILo!ɋ ?g rl+*CZoܣ[KOJ(}cbT &xىQ +3p>f/?9$9SW@GThTfk6Ma:vk Tu Uba@Z~`#P˘oq:L>6ŦoO İO İO Ă'# 6s0}@^U摲 wR~6m%\}gg6WL3bxf$֘4>G]}gs|Z;j@v۾DĢ@SO><֓1<rܝ7N@^&2xAN %fǣ}-Bs%5`oA`8P!B29KWW2ɓOMb1]8i/q/{ؠ1}ijyk,?Kr޾؏ڲe|?eOo >h&ܷMi>1>1>1c_Zkޱ3|_Ýϭ+Me_`DIN}]!6aQWB9QLI;'޶K3 l֌EN3Q6!.p2D[,"/vjHq7+JĿ KvZK.]5/I M$sHϏ b!nn@?[)H m*'AŠi-ڌAo#`X:ʕLD`cy&ilSxkoO İO O̙ygt{MSc]tr w {6em!rk$tD?V!"eʀ-x~RΌw46<}F~d›$zwXpT; Eb׋ˉtpX V>QD2Yebj݌!ʦ y+?KN"jRo<ۂٙg}G?q}C/fX߆:{!p>X41=}?ReK^֡>r.\u^?}4Ǧ`7cs{#uZ8K&RqӀ֦aA;U>x7{Ц[2ܓr/+ŗ)Ä }mmSpĎ7'}b'}b'}b^څELR8R\9 {,Gq_XRLėS;]ڻ!o¿O] &!խ1rTʢLjLRD8&xMvɋyܪ̲߃c%JTM~3M1Tphe6yx<~dW˯#m?V8}ATyZv)JI\a V^Ry |!w1'Wj xk;`aam9ϓQ\"M紷d?|'x̾_Oljۈr􉥷A#/F~AA 9LY{fTI?h}|΄"T=#`Ɂ'6;)L#"l1"h[Oﲌc2uK˝ɡ?D]/%9^j_e2SDd,'6Od B'y :7l0S҂P9?.uŚgxEk@7P1YYl̄3]eKzz"?kI/yNj՝*noq/6 D b{)8GQ1Nt;UרlǔyԷVIvbmLcgX,唩/3Ft\eSlİO İO İO mO<߫y Z}lhu%.nr[k9-qWUQVA!֔׹T=BM[y75Chjh4~𶖦ѣՄTGQSWEk~\\܉>z>?Q<~g~_Dz((6Hl]}#B$ĝ\#}sgǣV@S[0%7pϸFl.%5Gmwo2vS=b\T4-w닅dfcؙyyLdvy]2 vE B`">dDS3e1!ƈ)[Y!$R b߲ՀPwڭ1_TBbKȼ/r2K~FEv~Lk>Y]naT9UnYؚ Jk&US_#^`+Α-U!WڮUXǟ5\ٖ,K6Ձҳ)_EŤ7_Wۇ}O( JYd|hHYInrw@{KO4 ؐxdT1o؃ƮW;IeNI5i,Ӓd7.׬y3'on5eu#<&bedL9M0GbM~˶W "^Fx5!toXZĦB&[k:N~/ѱȢz?+)㗈8G 7Te~}DU,+w/$C~f (7 ѧ#SMU.wڮc98E"V-Cc2V|PU˞ךEE wGW]ӕ1y, bPÝƵ#lc^dҧUv"aӉlΔx~\bFt)wʱL`8'9GSަ3vtr%M>@i.GF<ӄE;j@=.;‹b"^gU\nSb2óCuNhTP.š\Zd;%jSXL :ޥbmexx0I~,֤'!-`U*'sHyt.o\%rhmVd]ʎ?9Rv_ϫ}eؠ)]z'CamL%|vLro/wM eé?[E,Ȟ-2BR!Y:fVFdlUw߭pN\_u*a>8}9*G`vP&&WiZ͜qo58+'5CU۰/g~_u_.[Bhs~Yo8α\Gv-si#&7ЯYqwpL9VY6:HۚU9T*i;| t8V̥U e1e{V!m3d?3}] h8ů1fI@rGˍX%ZaM z*n&nI !(C$nS9*q=k>Q>h(HòYS㍐ xmC"n.2k >'3}Wkd82τ B;;'.ND !L⬞uZ0~aN ˻u CeV/_jO8=ڜ!*,gEer72!YAvvZ<yHK DT@fY.39z *$2܁_qI HwG<=AEcJs)"c,1V1Dl& [CKW ] }K1xU*0VIh_Eo0F6f9&7_[ ̔y47v|4 Ӌh%m᪽־>iVT 236E9iU'IzcW]$>1R9*mIͧ8./Xwݐ͆+pQMvݠ='u5mK!XS>^qҬ pot~zR2BS%q"+W"lP71 6;\a=# Bo/Ds7!4َ"hHU ckt )8N 5ꝿfOX.~j{U @yΈ[:EL7oYBFoɾS EaSʻ`G)A1SdrzIY3Yj;>{`m4io2^@F%YUp;q]fL%(BO}h;y$utE [G̻MxFڗ)Huxn zHXT샶;#:T]We')0G#-q+T_xfmtYp>P$UZRaG 1` *ۇpL|YA{Ajgퟟhkj>bRL 4$cD+ o'<; xPN?0؁$[k)RB 삵tҜBcE(ΰqb=z2F#+9KC{Ƚǭ$6+Bh8ebvs$XͷRo-hScFHY B8j. HO'^鍕Z=ĝ>KwB&݋X^o}}bHt MvDI;B{1\ȭak"acMqJVu,kRrvRz^ZW#j)=ׅv8&K( (ss<0⏝>wU*B<hY#uf}t-aN8=if%yjOMH^,n" ˖me0͸=WYoMȩt|8$` ǜ!}[c_/4 ;n+ɹ%}v?hٽ  }slXnL?C;i3TQ-.XFH^) 3[sBANWi<6P[^@`MpCpN!T2'is=\APЭ {3`yR .$ڋ 聶b2uר =p\Gj~DT/Q>v&E#cv#uZYE6j8ڮ;328OHLa yăX$<ԯfh })Gm-Y2W*\#6(g9.>cHq &5JHnEKonRf9ƍ}I?ZhifbOuars_: zX Ij*߀o<^61Ih'?πrZ<րOy]ɍa"~ݴQ^ףe5ύב4lͬ^&,QH=sf OS/I͹K3ގ2U7?yM7{F=cFb@"3rh/hc-n%}{t5:7ִM-6&bw y7{z&Wa؞':45ȑ_ٍ zʕk듺ӐLOndW:&Mkh`!ϑakWOxy{.oT&NC]ݩQq^75."ͨZl-"w/``)1Mxa2xN mLkZ  _(r+&1K/^e/!Lw}aPs3 ]0f+R0 2Bx fgpLO@+ԗ 5JKz]U@n'$%;Ʈa -A1#P™" 0lI3UHj9uNTH]AfUQu}z}"ړdiAn:σ!e(O9{,غh75 ӎUy2JLUCn?eHj65K)ԏ\MLGukRG*Rx;ehH-dG0#if~+8&eF*RƽY^^rpbzRD&a , 8!". тU!Gy*$|}.\VŨASEܺ/ZP2p&.M5O!`~B֪| o 3M*F`TOt|TZ<> = YGeA+ !P{>N0넋AeGe>W_owy" j[ 0Wrhm=і#4(JnAP^Y]kXnIѼ;AWKm 5%uq"}E 5+ s뾻/ 1maXaH#LUfl.;BʉƼ&-1 ѓ.؍;*W`BQ8 @-ryUTy! 3\ꀾQgا$7rbHQ|f=@H;7t^v;}ł}Yu:@ĭ.AY*NGy9?>)2g5Fc tP3gddC eAt1l^#zHZ[)|)?(Y9!ZӸ\!SH R<TvaB{sA N0Зw<.4g\ N.is9yOZiQ{,PŒRR#ԘqSq[93x zIB}A{gA(r(o\fs;Nkj=&k.r]5Qk= VFܠ W!ý k  ǟ#Gk)?/`Li N'M?vػpHEeo=RHdc:f]FFv!HDF){S("[Q$JH~'GYq<<{_׹rr2d. Ǻ1*Z^GE!rNmEVA6Tn[R \͟2 岘m3__ۜjx"`$8ؽ*>qF4PU>xgw++,OKTCUsCs ޜԥʴX``3蹶~;HQlzN ֝\6ZC?Bs9ʌ=1gQۘzF ٬ lBs nWI}s|&1{Ϟ-$_g?)׌5nwZn6lVSE]8/=~vɝc>fvö\O}%bz#h?;fM~S9ai»Kq|CN$}^sdi|&sAwbwY/ 'WB'[Hr)h">D„~J ' 4Gko MQ2Pl gؿE@F`۳w[fG䝛T=-"Sm薬3{,D>##Ƅ~GiM/Tbن:χaID-u,,qzxF;8 aX)VDi2AEǁmKJV )W"v~ .%_k/Fm*x>88O@pt%''*jJᨊ'MAqߓ#|b^j]2Ѩ2t cZyrnz)KG4^`bэljy݃:e#`{1>[!4nxvHŚzJ8HY7,O6-8.8{ 5l EF@?lEX~Ca@TXWK*uq,}>ԺmD ^c(3$4UZ̝WsQpp~ ;P2=EgGnd7fZ< nvۉIsm~`0&3B-`ϰÆ"Sl-Ǩ-0xV,#k,7:8k]q yt_O!ơ:t܊^r]wXH(ԣzS+D7ܻR qB4nJZcZ"W5J꓉Ǘ8 էð2B.l'ʾs\YztY!:Rܬi^54g>㯓 +cf^ ,"k@*#*İo%5sGV5{!X0~gwX>Y)YGWիPͦE(^XϱKܹsC/wUldLP%7K6'쵲# d &{)$nn+6gDb2z>PNЖ;Ԥ}dQ//֐/%Gqo/=-ё 2ԃ3;m8 (xs|PVNV*ʆ.4YlWPl_"䱋b[S{ptT߻J{|bAh/W%ct\PXYM>Kg=__f⛆RpoZ+`biʔ<1xwB[U&]Rs l.&] T+x+9A.C;P2t7 kWpFY]@ܸ,-t˧>($štY"1%Yzye9= p)_>'kf/!֮>u[LT􎬺 g_.ᤥ9aCCm`wBjlypV6qφJ10|_ e4-PWTG 3ۛZԜW*u>Q!Sҡ;5@{$fjzH_Ζ VвJrj--* ߗBqYH,ɎKLcŜmAwyNu_]:|oW@O'ae>|ZGp=5hyfwDs:}^Oh%']p!am[h\Ov8OسQ5Lzf7,E )fH}TO' D"ۃ%A!6DάA,fD3=wYgHjd}+]Ƃ99}=wr4Zq^@~>'> gydR1BHPsXY3Mݛu9|kLa=s&盪d֡)ܩYs^NVR$݊Jx*q{N<~'pՌ+a\?z,Eׄ_>/nMr^-rН .Td z m21 LYe]_[_⤃=XBY#+]&SIn`|]);Us.YlG(> &ñcs՗a̞qDU/<ܾ)Djn DntJw- ?+PMi2w@Z07aPӌX.~vEO/? s?y3#Ѓ^FKx6+"v@V;kv 6f ʰ;M7 P aIʟTGgȨ d8uM{غNaeiaI[鐡U7z5 ?1vr٧gz^TQ=?Q6Z_">ΥNȓ l,Mv).b&Vxa7{ǺAcCJMf;  k͊P(xちpALɉèf&g.Њ:{M)$Bd_eo-HJF"[(#kʞ%++*#Jvt}7o|9{?uPz|2A@E-QJb.I=j qpXO9mŽLpc_L%>/O?<,t>I#]O["TZC̗.q0Ȼ BwA jtc±S1W=rگ  <JBcc;Jkrڡ?Z/ݎ67 mB^Xݷ(ojI, R9lH?,y{~:o2 ZohXv}ʷj@* Y@rFyZTĴHuw坕I+7ca  K4:c~xKsloBHףy6$4Nj(ŲJ.`"[$ zՠ¹S() XdH12z'%\x~ԲW5N)XhH93Bѵ\11uMJ8 mx%Pv}>$Y&9c`":ri< eim 9;#ZFR|Q5"Q>UИOf<iLbS/IM@6LeU625IPc{`;gőѽl(4~Nɘ#x>iUaqd!vId md 1fH A=fD_*ŪCC +:$h:aVveSYg mqS=>O5M [b |Bѫ D|&t9w5̚$fy!{3ݳcU*Y]̡P|=zC&qzQ9qu6/ Ru9P)aۯ=w? YplBmWw1qB<G"jp+]qGDnIѴم1cIgKwEJJO0DÛTP :J=$`ILZwR04Jl/&=p\ Qqi,{sGLT3cꥒTYPqb@]gFT#!7A&cruyԘRT1P&J3٤ţ" 7\.v!EB51UW~=H-Aꃊ1>7mUG."v]¶ak01@T:9T'/ (3xLdKʓU{u2md!O9]]`mî5M\>,}%@Z7Ӳ 5}1v^ps䔤cZl!x)n-Q6*~|$#bҷg LVC*$n׃](Q܈*Y,p"NjxA[g-A["Cu+.Y$OHL9܉:~ߛy1IƍS @ 9.] + O#`'kq_Vv,ioN"L|ӆ}]KVKc$wIMMo> > eWwF.c|R+?b).޹8W6;lX}5X d)q>n6/&VX^Vo>իI3WDZtVxOtri@ց2ȿ`ACAE-yvۤ=^^($>OLP@=ފ8ׯqq}z(Bjc+hxO}bZF?$e,Ϝ9˒|~>yU Rچdv{ w2 Nܹ~I U.%FSE O%veOq(vp?̊Rȿ8=H[S6xEl,o)ը.Ixm|I{`Ń۷՘'t5!La ~Nf+7ys&|Vcp C^jxy3}Z+׹1עB%X=L{|gy!c(z"k=oɝ63 o lfF׵%6 fO^BTU6".8'ЙP (뿠ׯ~yz* hpt y}9S JDD}?uSGX,Zw-!m\{ΟJ>e%,L5J#MʴیJP}-iJ@!;p]/y#>LhHSXy9ggq=s WL՟WMd#5gւpv{:zb08j] = /uzrVkoWJXVm \h+KHvޗ: 8EՙFĠ!xrnJ?8{04"AoǁfD[+j휿k~/d(a HiAC!><[V׽x]t :'ĸH(iLQn3"%ʵr~sQIM4>+@Y/k4G%ӓܟO@m9Lfn%qGB*0~Đ%n.R'Hc211,MF%pw< (E~,BX"ɷEZc2K:<׾8Ox(۩Vs%韫eVrҞWS]Ӝ(y2C/]ŭa,G ӫ1C6\,\5\Roch mʮs L>6{}4GTȴ{XO |;N_duO`;cΣl?zLY#Je/nȒYdM.$*KB e$kȖȚ-}>~ƘO<}=>>kYЮ`y |g~`@-ƙe>;.,$_buks bU]E e\ i ;%^Āl{v<|wXI}Ow (XX/bmA ? ,;3.$b; i$9jceej\i_xe. jᐙ ~%ukу=Yx֫33+1#.oy~:PhOJ'<9C4-: cTA28ziA16xQi_:'|+Zv=ZXyRAGH"88)hC!0n[ Z=)puL|iQeb L;:-u y)m/#"DqQj>%AΘD,,e'(ZcﲨQFGk\cތ3LE qKyوTǙV; $ocG2>x/-5un_ D3dagC>mrLvԜѼu@Q}7DCif.i oke1<]; U lTEh@_J_ X1u&z`]zUGqPyu) \ mͤG4]8f᦮($KpZ@xc5-Vk|0o>$IR`k>6؄$vp@D%hYA2_ 3*RU QGkuD>/h790_"b>.y0p4:Y1j#XD3_1R[h O9VK)T zla -<YQ3a_Ԣ߭)> /S[(Ʒc=?f*JwDDG{*DG(a}@4nz(a.pzw}S&RVVm\AWmoX"﵄J|ZI4mpF*mh0leGiy4#ڎBFlKM Ըk9晗݁8&h\ NʨԵ-Wgɳ4T0nG?N"Dpw,cJyqe G`#pڈj̛Z?f6`6JՇPWϑa.۾r TqL1k}jk\/º٭ƙSѿWjTjG&7WkE>9t{! a%3n7$ÛCqUi[3({d K[eF䯞JL9=[Q`P3yli_0 !FzD|jSI!91 }1(>``7>KDU6Yp;o0t>xNOp6Wiߌ(װK?uyP8D?<ܩI>:Бk#V ZD +og] 3|>Minsc쭻1?9ɿMRlvGQ[6JJ30mt'\}wX \~~FTW& ZFVCKnDY3HI4&n*kЍR Bڭ 9ZPB0}U1WJR<[M&NZ3ݥf4n&-63~ K3 C%IJ*=!B6N #{XяYR;f >(s 3 9OnoaPEQM 0Qh uT= /K" ~O$SHYX`Q_9 G6hYP\wĹѩݖ\3JPޒ79a2[{ gٓcjl1k43v}w!jrJ>ލ/'Y%9Kj6nә~E=qAtA`j135Vgڲʝa!-lNeWC* (YJ@>#4q.xr%%x =꾚5&GnG=nfXvXAe\RtգPN2j=6v /I&oE$ySBhAu"N3]wPp:],di[ KlrSךbQEzc5IVl2g_b͉&K.o=/aщF͕G6)I 14_ߪ IJ 4V҆||}o)?Av_uGk?30|K8g=I,$U3++%Kfu QH̶r^Xi 8>Z%)`X$[t8AʑsN 5זH?F:1bok}vT9x-뇁>;Iza=䨣LayXl-ns;-lGy?#c3"3w,X?e6╜ua l˴GAnZvp>'ux5ԕR=UC}m(k!ښǮ>V(q'2ŲH$~5Z@4L⼥\;o"' ec~%E13d`.^s&6"5x v#C }Gb*YrEg)%ǎkb=4G}4XsjMRh;J4@3ScXh=Qȉd=Wh{_TX_;sZ$ӄ|䯇7܇3>R%4%d Tn*w *&@_g?B)5q,.xtH#'= ,FR|NO"GE.Bt|R1rjҕ9]bE#{]Ld\Auwqnd iLgPĄVveŞPtڕI6Aޭ1Eq[D`Sc#3awX3`OՍв2VS.=)C]y} 2^8MHB3qd~?WZFRWp8 BE&W,+)JcΣ^8#"PJ2D%!]"Pd*Hd)al-IhdMNEeɹŸq}3{7u(wCmnl=OkD l-4 ^^ő<? y_k"_=DYX62脒c&ɸ| Íh.QqhmݶQ.hˎ˧{`%0coTc/ތ$N_'\l]: 1m=&|"yaa]@:W'`ET ׊>5,J1PF܎ushBOCOoQB4zaOTvLb%ۡp__K~^BI"S8;<*GA0<'ɢ,?4N? qPxgmG~ځ|P(z*)MYɈwsB.0@uQ' lɿddQ\y1%8h_Ye{5eG7\yoP Tst9wr'3&xO;6vՇȲ@a+,CAэ0ioH̓i>֛.&!T.qTp/OAp"7n 7;vgbC7kf!ҕwHF^%C␙S|*>Q3TR}8(i ~/A1 r²#]PF[\ODUw VjGn"Kiik ˭Cjs:b0Z=B^O$As[G#R(QSfeT|WB_TC L%&W7t:>&tKDŽ#u`W{7S%9჎5&_@,vC-C(3'c@ԵOoa WZ̑?O1@o؜9|VXWT~ (|vɗJ<)~ke7nyi_1G4I\~Ҟ\ oЧD^'8{k<3:3|7UVa0.|Zk?u_YVup:,@ S׎FpFR}k4h#kM2bv6ʙ,:8,:˷VPԀa~K[Bʆ| @s.խ^A S e۱v8#ŕ@;;>g.+,%J*ɔHi/k65%N'o6?}k^i7 [OeC62y{.uڱ-pgAPB^ps ̆sW@$^4h@)2ϢcA28Ge" r3\O x{RDƗ:Hr͸Tb'bLJQNnbv:9vDd nGZt_;c sw`#.L 1vjq=hqFh#c Ζ2l#ˮ:-T904rЀHED>Y@"8 #|me},g,9=1}/ńns;KWc?RWTB,gMzIP5 5Vw!]F.(ާ< wEȹ$x7&{T$O :hT6#ŷ2zMB͛'_>Eybی~ ceH|) P̙V5.tfTruosEKABh,:{(&wp@^y`u;0ǃ A:C8Hjbͅ^A"C8f&*Z$kf ,usZNП@:s&k,GʼnO EiEd uF//$ V5q=LeՃkp=n]k¯c;-.sT5$dLW|{*=J^/5! CbS6 e@ ը e\bJ,YN0<0#XvsSV)S< 3A@e-pYަ=5/KvMD//c啜8`"yW$a<01gIݯ mzq_z[V6pB6W nc!c){XHR^Ato8 љZSXl^@? #3} ] M?]y04 5TE'O{SL=#[{2&O NحG@ hۥK>w;r YTIŬ;S~o|bL]Kt)|hQȲky"c)Sv)"Λ\8a:||"y_kDakSJjcFΌfF_`7"'?gnY髕r*WF_|S~__Ksx%=p.muszQ\{$"t<3B aoFdz4V'Ao{CpNwעԺݖōBlTh|?s.L+ 4.TWLV cֿnbQƠQY{zbg3Q_ q_., L!=kuJPuFV_{%E-EBXC<TKmmMgr+0=43ij `@ c8Ñ&|!LsߋKp SK (>j,R'CC׹s+Y7S4&(`{ >aquUmEDN3kúWCB|yKTRU#3k+:QS~cP E[.(+Qk3} gQ14cW/y#NxJL~0ru*HHjٖߛWw@Sk0Gp18D$Hfj?8%y3:W2SԥdU"n rl<*$=0}.k&~m\Vv+nU:<ΘO/p w[8Yaf4_Y`_mYVÜ}κޓ{~qVp6k3eEnGadfj*(k-rZ_ur-:gWB ?IzB- "ȺW} "I}?yyl.6r@g(RRFo EU7wFl7yBV G%^ҩXH3ʼn*弃=5hJ@0^6NM𗻽٫I 3j7&dkf'~ | SG/5WhfhwQ,:cr*8[-z^u;R,|Q:wyD a=>J}nTDhl2p ̽C~rzdItmP\ '[Yf}Hݡe4SC mL&Uˤ+3i8nph{s:J5ŔTQB…~bO{#77ӎWL(%YRZb{S%0Ib<̚qZgw8|6Rv1Z ViHD[ n^tTyui :sފ2GճYoe!>ʻԏ>aTOMaT3.L>{K5'pR;PU@%i~ޟWFY, -W:aK''Pb$>#kOKBbQ6zɿ@b%v<]VW c-!% jDa>XLˈfP|sz@͛#Gޤn^ңZ0:+;O U`yk4KxHdث EßAk*RѦPz[}QjxPg(C倈QvYsp=BG*&xi,-Xc 8 &PD4 塆d~صzVVDVtZUJ[h ,x:ve-Q޶#Y46)j8QDŽtt\W(O펑 3f巃I֣t$PZo=ֿh1cB=ؘpB*ٍs;TpS^KichjCԍC3=^3hfz5B9{emI؅ðt'w*6rɅ|q5\}eE]Sة8=O3|Sg$V}ZSۂ?/gYǞ f>5t#*E".Tm\SWL1V0vH՟QO>zpx:RL AUd.WZ:71_-FA {y *^&靷gy5|C8l6+@$'Pfp7nn-[~Q$ u}貢DZ%œ|_F.9`af/t>d!Ńb<$_r-mv.0Vš ~֐hpF.? !+ 8tURo'=\D6NDRPO>ª,T]kjfY7K+e36F,E * Sڦ΋ј{j˹~/T)ٟy?j_@ѓw0eh+y6g%;&@쮫uU^ޖ(u"ӄ ^Qz=YmTZWd3$TAWVra2yw@9aBWtdq]|} =-ZmYIUZcaһsFS a09;@4Uh#k[U/ʝ9ogY^IW˛heIvv~W0'1n`s?̾mԩUVƌ!:'ߡ{tiB~2X"w<]]{-ZsNx Y>sv= L'ݑr.&)ZD}??qvy墙B 8TM9ʛ7_-o5 4 ]`2`[%8,ۥ¶}<"4ѭeGh $mo*Pj[Q^rt0"z^x$w!oyzecZO"Dpv$ RO`Xi`mGw'9ߕEwd8sZqSz O)|+u~f@i[AC(ht5no&yV囄,)+;ѳ9Z~;W_1jͼ՚D=,JFɧn˦qv Y#a/1BzbX\d}t+|BZPkuWr^`;wkq#b|il<ؐsRP=˥"Ƃ 2O٩)NHiRZ8K{`аƸ9"7ej~$Ns@hLn.8Jmf'/)J 5#p`)O©jK^I%4 #;VP2ou$wmq,=G,)ObIșl.-4Uz$%6yb8lnYYKF d3ڮt1 рӧiK\5=Ic> NM y'9OJ$í'Ȼ 7Qlz.&iy2sNTㄮ9O@37CzJ7ʳӈf<hj2=C>E/(Z>.0"[ \ڶauԻnHᩭ0[UoA(|2ꚶ7cIx-:PƽuA =_eBp(9|e" zDjh`9 ^|AzgJy wጃ +VwįR+#<ًcB:Uu,55_+ѤoW5pYݝ6sw.wWAD,bHX6\u]:װ.?}֧١~d?RzܜM<'Ӈ;ӂ&+X?GТG.(YpϾم'KVJ\L5a=oQCI ή4No6N?c_^^z?6O(!b}wctB^^<~+qSd5N={y'P}λ_ [tW߻]WDRBik}㝱I<.zY|wea\95{{bl򦳬K7u+Mⓞx-AѫPJ stW<߄N_8ebًS57,_zxI5<Q]2{E 5{=iCeSǵ[v C8ߑ j>}xFXƖS~>33.]>j͉^cuk^7OثG6~aS8˯P=/)(+~r?pٿ˻-$t{lbjI:7=0dϯh:o+PުFÕ|3`C?LC?c }^~'{h/<){Lqjʽ6kԯM}vfKB qչ(5$B($U*f&g$)0Q0 F(`Q0 F(`Q0 FPJǟ0par2cmdline-turbo-1.4.0/tests/flatdata.tar.gz000066400000000000000000000036051514221355600211260ustar00rootroot00000000000000Yi%]Ӧn3NmKpl]:j!_:`. sy|&.waq朓y1:6z*bU1".iPa|wmRO^`>/܇pm>yS.w 'i9~W:ئTfuUVU(B̜ Eϗz+u}[,x %exot|AD1on3$59' MJ&XUmCg4n(0}Ի6_[?i&*_}7-(,m>y+]:#NRӜs2Ϧ7 MJӇPą9" }/W֏m<=`%,~C#(E$ئTO}3 3ss7E>_]ԭ$#˯xo[Vk\Wk O"pgm>yCΈ4s6vzߦTO}BvW".iPa|wmR~"|4^@mCk[{x> p'm>yӖ.w 'i9gnClSҾI!VU[W 3ss7E>_]ԭ cz;X#rc޼]-tF9duUUbR=M qnBg4n(0}Ի6_[_t''. 7^U1o)]^N9dj*6z*m E\hӘ(R|nW`u .?x!xi5 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 Win32Proj {263deccf-470b-4bd0-b21a-d349b563cf6c} 10.0 Application Unicode v143 Console par2cmdline-turbo-1.4.0/tests/letype_test.vcxproj000066400000000000000000000044261514221355600221770ustar00rootroot00000000000000 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 Win32Proj {73cfcb2e-19e6-4adf-b825-d2fa87458f3c} 10.0 Application Unicode v143 Console par2cmdline-turbo-1.4.0/tests/libpar2_test.vcxproj000066400000000000000000000047011514221355600222240ustar00rootroot00000000000000 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 Win32Proj {bf86226a-49ba-401c-b6e1-dc52a7d7965d} 10.0 Application Unicode v143 Console {d0a94f83-495e-4fb2-ac33-9a3ec2cc263b} par2cmdline-turbo-1.4.0/tests/md5_test.vcxproj000066400000000000000000000047521514221355600213640ustar00rootroot00000000000000 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 Win32Proj {3c50aa0b-8b10-4bad-85f8-953861327d0a} 10.0 Application Unicode v143 Console {c4657dcb-7b83-4608-a1d5-df38d37c6fcf} par2cmdline-turbo-1.4.0/tests/par2-0.6.8-crash.tar.gz000066400000000000000000000206431514221355600217600ustar00rootroot00000000000000 v}{G7(RW&+dG_%?VB\GQÒhd~~,~fd):+22-^otH7SgfGc1dj{kUkWY~پ'䁓;Crf*S/72Kk#Qi)Pu i}x]*&z2w/'j\8bّILULzkt7e==/$|LHӖX]_`|]{~D(GYhؼtNj?*սJYi1/0$"N(3JeZ[1"E͆c91͖{e>Zwv"9W)0j&ϻ15F!7=7DPt>s6nH.=b5qC4 sKZrn62n=iQ"Gy)G 㡳ٛEJh~ɍr¶߻oHi>fvvڋBγFv:HhSpgCL{zPQ[)޶xVӫuCg۹i54j,RjS|N|4AGpUXʂin6$7c7MVqwt\?ᢲ-,/WX򜲁uRɋK3Iʓnv"@Q3sDXZbns~:K}hCC%Nr#Ǻ.+Tƌ<>"W\<>(kR;w2=wCZY;xygc8΅UiVc62;Ūӫp=@qcپglTu:,&FS{Č 4l dByu;GQ~TaNcJRU\n5/xb#4~Tc"):z5rs-YYq6&/boeJP|S _ 'NnMiI9i7vOK6M Yd_Sb zl+t#{mډ;ǚ`|^ġ/_h>ͤK&aűLY|'mpf:YKY4~Elaaj wod9$Fiѕ;ZM|^ ^[SPF/ nKS![+ިF\bpgx蚨5\6I:w?0WDd zb4NCy9kJ[:}/g=CFڇOorg:Ddrz8^QKE\a5,1rN'8)=WsZMR XϋIjz[S=^3U='&qQ4 hU{7 h@Ѐ4 h@ЀmftS+"ƗTǶx)zr3.n!p+TrVn|#*_FDDŽ,(R]!X BDIUt8Nᅠ?ZᶻSvUN5s%8DrcΞ J_h]Pŷ]ݤCZXnH)vϲE mt2T \۱3uTa:-Ap;C)G|d}Fm:Xqgt2M6לHZ0a.|n˶~_@&jl9:tkRq6wsrmimDf- hwkW#fo= ʿ]w'{ #`$%q`{/ϯo$淲uPnim|YyFJ9oRQ6*櫴9H*gYx-별(ؾ*8˱C yL2Pp ɖuZ7"$.g-Aͺ~ygygQ,_1 v`JBAIn8'v/#3[A 㒁$ 9nꊹs;->sxW/U(F⬇Rfͱ.Hvs&ݽ}sdy,cߴ0R7@ i.8 |URK#q6B3p\-3E;Bs-M^^JsOZz y̛f_9()PCz8\Kug([!˧ł0yMplE k8<'5 @?_CE|DԨ y$V̍4dr6mly'Jt2iX,m})H^c-a1 1 cz'Wg?VDx<>x8Ǯ"^A?$fSX"{@L棱ޏ<ɷY^4"Yr펭2RHT sǵ2Qk{<2j9җ[]'?9wIo2scx*U%6+$ qD "x%H{ 6HQl*c4chh@Ѐ4 h@Ѐ1~>K+i97"edCnc1a1Αx~I:${;ᜨ&GtF7>vSۇ!Rb,C{`5S<*]${nzE2UG1d\|đ&%{Wg[Ư!ՙAu}i } C3v{$88SEs[m A$!<؄`7Ϲ#CJfx&8ǺAjUB_G ܾV3crC68ऌ): ҐGnK8'>@.{u+C>"gHYȓ9cuh c`%1*y%W98ѯG JBٌߪU!qAt 1 1?J>I9BR jH'R&yhԄ=᜹խU<Ӭ58 "%K붳H퐧<q!3~uFg3[x ( ɜK+3ut@(B9{)C _g9pf%}Câ0,Sk1ʤIh6^`\#VƮcf $(Eh=(SO} ys^?`ץl9 أ@>h@?V 4 h@Ѐ4 h@i c NWMInҸ4&y 9̉E鴜Ek!qvA ʒpN!W*:,LGǺISlq+ʨ0UC>V O%gQJG?"G]u:Wc*$"o(iF_=0Oh+ڜ/NNgфً?~Ρ[T˔1D/5<.ſ| ǀ| h ͭ#3CҼ~XˊsQ~K5Uf9cy>=,rܙyQ0{;h*׾')t+eBg LcLQ8d.c2EJL*Rᘊk?:qx!\TOoƺ qF[r?F9߭Wܶy"ݡw.(SVR"vב)껛2p}(aE(kWk<&[:cYL>HX^ɪjD%|+ Fx<8;Δ1Џ~ c[?-!3!dEC#霬I{1FL^)(3D[g6=Tk=\s4kُ2v<=XGٛBTsLL>2ԕqC$2^E,~LD(edٕȜg~ e9XAx[Ľsczx{̉^K}r[y礋4%<>(SZ|:詳 KUжM8AN a J`k6s5g_Ɖ\{B`!cDx9՜ʟRՎŸ@gʤ1Rmz`DUS12yQ&6z.QI(W{,ƠAG4Օ]RCW8O,ʔ!;]K"cݫ(r}0còyf 0` 0`}o 1_ō`ϰ&oaC#nM8e.o>EdLXM<1ryi:vU1ku10aSteeju=~FL|NaPF%q7Osni8BH{(]ilYuZΓD~Q.dFϜ <[:]R'e$n=_yYHFqS].*<)(S8ӄ Rn hZ|!'e~e8BǴ,ͨ@aMv v4,IZ2ѓV?7lgLm}J]֫njUTYV O<(`o@Һ]gXȮ4se,tBNKjVxS3|p3(8@?1umB>HAH]Ga,v^,oJ00/:SWfC. er=VEq ,_d̐n#^rtcQpi@:,W:7?]4t#ztk3[X@y-Q ᄈ1BVCB&{ڥ G_mۢM̍vt4PmVUic se.&Q~}ȫ,;Kjb_0I,)sLt?}m6<遲~o);w{7TO 191y1;bⴇ-)jmCuTJHW4įtR22RtRRrrtp@G/k2N+F[OMMnb|#3xخ> {:LB^~GԦP&K1G΢ҦUkf.t(㶱JYmu ˠLQ4 MPKtoCY曭Q%^%{G;#:4Α~nWFKu%Gvm}R`fR{-CkRs&|㯔)Эc1ݼQ@xoso)hpar2cmdline-turbo-1.4.0/tests/readbeyondeof.tar.gz000066400000000000000000057240731514221355600221720ustar00rootroot00000000000000Խc-M.l۶m۶m۶m۶m^FD;􈨨U3gTx3P3]L]L ] ?ȿYXYXYY9@/obD@doOEA?[5t $) --# ((|'?n> tTUWRoUrVvT$#~+y/| uׁ< a r5pX\~QE5"jv*eh  Ao@:nV(//-w0?X ~߀ӹ[Y2Wl]gݘjm_kXr| {IMZqiiR;.י=F=5^RY*+ϛZ-Ngє-ஒ9[N)z)4M&~e%SJ~~_#JTŎh#:[0z OIeMԅy詒-HCDdWfLfS,mt3TA5h74 uQh6 \NзO28y3&7vgrmKݍBh޵LsЙ{x3X.xħp a_.ͭg8 2ٲ`bhlC;sw%b8)&JK6RF3g* ][%k =*rpHڹrPpWNAYmp\NVq6t2&ɢs6_X'nD>L@(ϔ֋rf@|*޻Q?H1'WbWh6Y>kF7njZaEDns71| qmH+1#;dWR8,j;SQEJ RLX4mb*n;3rz;ʡ֡-|7/p) Dnz/l]y^]j$~^yCBsBȟnNZ{&'໒NS4mjo螓+ Ż lS!p{ZPɅxJzpЄw=l0oC')(D Jqk7]=nЉ!lu"3%[[05Ec[1/O׉p?bϤQF!h^t1aH71*pD`˨u"7\ [D)" iٟ}E0$Ҿ'AgZcPȧY7ϥ%D?謌P:8+TE6uz(SQfɡxr7D];_";:6xCp]}x0u!N~8[SOIK:/E$$DsXB5Q욨x֨5#. ĒpcAV|z86 k|,M"Yio'h ɭ^""Rzsdė,^9SJcCO Ӳ"[6щrOE4! ,=|Y{wd$ABA 扛g4"6!!"'̋M$,[CO#AAv|J4]'x$$4p[͐y lf!hbvMK$@cブ:VWVms&#ڤ4lnGyG5RϽD#2/jxw+܇3δ4Mz}7+Yf> s)(Z@jϢl6(Wh٬C<9&&tK2W8DfFy&~BÉSܯTS< ~V<7 RF!"VVDC\u_C-P yHЖܽ1Ѥo}L s:,&iq0xOqHHz#OPީ{ (Ii I&a~"I*TtRMDV& Ö ĬZP<'e,QƯR6 p*r\މΖJ0Gw0W8*. s6̢]iGBM NHMў9YRr |ǼTt7ki@5`,vT!x m!7d>$qsa CPeʷه佦IDSsí%@:jZ'9 jy)u}0 n<|C#7r ElX9gn>YɁ+i! m#ZrKo,!x9-*(G&5`q|AԂQu]<'!b/}z^)`E)Цs]jO)fa *Qz=nhoKIL?!yI!(ށy6}SltK ,%dI]3M:7GB#DyPagְu >{~6܍)BJ:[K%ebzy% eT,x(CIq.nֵH^d˫/X-(08{ ع37@^9R+ bWCl W&gGؐr:.Z~U}ÊBZ;v<ϊwsXU"Jh`Ld= 8{^]^m{cf1brC} ezu2NG+ e9 C.ޘ%' aE] Uď tONTMD,>Ú!mG$z I0Pw>Y"ھ0[#`,GՈة"ϛͩK`.WyjAtf0඄n&QHՙ_ekDA7)D/y`VxZa,}{q-J>r鉴Asb a^JMA^uuE9T,")(0(w;t;;1GP^ "qN˟j$= \#O%H+ fzo.kL[|ԣnxϊxbPm)mG5 86+6lGkz}92&/}ӉYa"X9aN‹XC 1b=Urւ| KմnWvx-ZЉhx<ˊ_ kӥfrHzEM wAoGªJڌ?5ƇW؝eF$-N*dJmG]oY2 %\^F@]}SLď"@,fͻzݡҍ Xh;ćbS 䫺@\{aLZ`*p&Rej(V7O/ -K+V2'*zo|k9zi۷>r4.nfO XjY9r zr;ʽ4;L(g6Sߞaž\Jb'4:nʛI'W~];B^E "+qQ.d#N*+Fŷ (u55}.q_6;7$,o )*üA=( dE@3FܗtEzl4 @` DGCAG/e JUq3P)iT٭7g5%,=Ꮇ;%0*F#^y!I0ԉg9;0$u8ؼ#w}w^\5h+e|q,/ MsSU||%a?fo;ތ㡩gԌ#u ! BHr/,O#L9ˇ;\NvZ9ꞁ5,D<2o#n Eϯ='sj =3'>1#>]Fy}"E?ϫ?(s"JDL!pylhO?Ke>}_fg_Id` _p+j`fߊ+DyH1_(OFS݌h!9f4/L$Μ93n>X&{~U4B9N+ǿح%n?Z :7_Wl&OE>b!jW B{i Xuw!`K1'.cbhJUA(='qUiNBhjlw_]j^[O ?/̱}o A똼y4ns6C-mthwxcޛa%wbZr[caחjs1oۇoYp`b}_nu2pG;4gY;r}kweoLcΨ"vxߩwXc9 6>wKBZnIbMÀo-i %?20/%0>#WE1 iPkz+ UͤuSyx4j%8c+2$B#z~Ks{Jz e#M諣q%f׶Ij)q}f k|8ey$I& ]O]w xB.ۍKLj '+dLKO*ðӯ+J?u˘MBZ"#$yӤzO%s!3;َ_nŞDZiˈj[wf sSq+;:\<}N> | Mjf?1v{5}FiGU!0]r`o;LS7 ᄹ\6˾Cl?Z|*'NEɣ)ק?q'o1CTQݧb=ֶ{O4jHilaMRty$-8Ewd)^=;{N;sޤP?dK6mk$7^>(Cb j9ڷT~sV3L~RY I쳪C@ه'.|-UnJN>ܗHw,5d4M rϢm2n ^6 q^J(p/?u kFz@IN5651|Zjʜ ax(6(cw$$u^skō!fgcAġc"/ ZKs kS3"gچKLZZ hji^k)ϋBbl74+%y*^TQ=+Cri8.gAW>l_WMM!w =M!Rwޜi\MږEXmp+`8>=%M&!B$m:}3{| G:-m 2ë<`KV. ~_+{(8bq9˚S2<ׯH,J7yˎ dahXOIXC]1\{:' 8|ws@΀32NW|_"cHtqr,,gPT{UWI0n|-rIY`-U]$BE7%{E(|(@Qy!q,`f1 V(6}> Z zw,Q~kHȆD}1.0 lH_DCҢgxeۓcO}gc^^}£!7N\s\WhLW62 wm_$iڡAT'#uCmF} -8(i\%&mnwR7DPy;^62ybf);W6YUg!4T_/EZs E<,JKV4Vi`JUmT}YYpYJd%1R?f!7e@TWB 9h6%cdSgn?UtXfDEdEW\K=WCcxC#MzHzVUXRyN;u{ G~[ݩf"zAJەGU15uZ ˃ŴQ|7@C쑺PMEI3@i<33&M\5I-Hrh)ɸ:@fqET;)QX\,;;E42lv7s(=HWCP5\ o绎^+>Oc>}͸$1.订"D.=0bc[˃ qQpąU-*1 7@򫹴\5SО/Cď)գ6VᏱUG޷DxE"߯c4@$i*$?\`)&5¯PBm-osI3x@ OPa,k{D5MA &Y1 nY;? kk˖V6RwЁ=*u) =AſH&qp9w!6D#,hɛf e+prt$ 2kѐ1!U .M*_A9-tᝉk= 8q ֣M%9;]`15 bW`H5ZTS0s'&p]hl$C$gKN .5p[4H kx Yf[Wٚ2%=g>%T!CS*VqAvcAw|VAh̗J"kkNqDw!;)"@LhV%ʴ (Nzvn%u`=~A s__wi@@rD &-;nN9~gif >ԲYgBE>Ϋk_{ez<p]YՊ+][{0F"vfxa5+goĸnx88#ơ?Q@a\̲I3-I@"pu(c# &z\6l)%9 7b_#~<|z{1e&eL5vos6}#J]-pHԞ@;M=.9rRiUK9J9J@R,ĂF'Y@])!R@,2NslN G!؆N& [̮N7Í)Cn[CAS$TI:lX4jۗ(@zz)?`UIau>t@t b5Ŷ|PAL[KEg{˞4wgO1`_z8x~=vWA|r oAf8Rv}G6Rdi;<^()@ZTV ]BLw6ݭTAo`kYp(%㵉@n7c4LK":G_h jXS2 E#r1bIcz:C5$;mFM찔f EjM(MR5 6S4(LƮ$PʖK@\}Kq_]7,QԼ-uIv d,Ov&>݉xA9̵iy(v {j B^F(K~5x̍NFx gmxF5>@_eOim& }dn."q$,s+GQ= D0-YSEdy.K3n %ڂ iK'LH>RB><)$5rdkGj(7A]Ng_t{LeѸA89*_CRkp61j 2cyӊx퍉}T˲*!P}kzqGS3@ Jv4B8,@OˑA_W|K#H, hs$PۇUH 1IƏTn2?v?a NOBb>,|crU<_oKί;dȁi_ ʹ*Ѻ58NlJH w T:cY~$!KG+⓿^<\,hƚw(wpϙ̈3; V_j'p#v49GBw@=aQ8jsc85Fj\&AWoW 倊54тcs+7z&bk&Zg>%V|>-:BOhNXRs>B]03?<Y #>ke`HCF,,X&_wRYQ߭](UM(p**ͧ5!'uYwRŀ:̓wL| 3Qas>|4 wp:@D|{ uSPFĉ 0̲N.u#[@A " ʾׅ?%6c5- |# (LJw{6jfZ=֑:?Fw?7 tmi%ci,@7(%u}L*WN\ũ܌ǪpBI'$Qi83xKK P>0zY3Ha'ck"b8Ս!%$ع9u )qv_-|z/[N^au{t;RkNlV.df SuS;2 V(fWE~=|0WFc tMA8/gM@ow%ڲEOЇ@`ȶX Djzs$3vW03݆;|[ !4BmD*Ӻ\E|~䉆g[W"KSWZ¡hIܣmdND\JǏg/[/?w_*f0n8N.9N/:?$?O? ]#/˂H>Oa7#9cT YRGV,GUn QߧrJ)#wI을ѫRtކvJe)^ xBvlp6]`,@ 2SIG#Y5\H⽁!L;8cbY%" >uAi%`Ut61s,{cd}Lj= Fqu>0)kl41P މna]YNnzߑ,'>Cp \ھ&ḺS:Sa%r\2V(}h GʺybN߆],`^{*j@V&xLAX}2,d`8x/,9g)>ջoaơN/v<ʧG`C4 kW!YC@a𕒼5J4lِ~E(ׅw(gh6 )@4 vmyRSߘ=Oo tNBkg}+NxYܭ(miי wXTba´x#P"8稜ZT6 Kd=Uhؖ-ոJ 7V[iVg7;uS)-IW z1fʫG]51+q?u]X͡OיN~mqrwZijPX¦gӵ3羨B&o=vzvnHϏ֞vo?bUHJF@X1(?D&(&DȎ0{jShaF̐xKmn:y,[SOSa6;eצ[c5Tjɗ}Ύ(keh̰`#Ħa4qH4<!qp3:=c kAs_ߜ82d\Z=S@2JΤUc4؉A6]vp@L[S@H8d*9E(ܨ7p->v=u<3u"zV3^?Y%gL2XS7JA_;.;b.OQ'Z*2e5#yYA2a@Za6܋2T\1ī<[2ND[Ǚ{Kl ܡme8$>4EVO.-r *A JP: 1;ki-þK\nu8c}ȄǤ:۟Y]Q22T Ef<6Jab,'$Y6xp5N 2m4B 3 - S`8 y<3-LX#))U z`:F>;jɂI; SO6@J{' Z8J_V K(>K3'N ~KB"Η4()Lr_` +#\Qm VuIbZ%L_૦$*'d!P+׸EgxMy!,/Xw8VKW65OEt 6T*C]-UR0j? E4rF.(*W$Q4ʑg|h ~D;Z!*J T_(~'*~P AT֩0,a GМrW&2CXpB+ӧVC"O# оF2sX`>`^: {=i,Śݔc|iG[U䛷-RLٔ'7n VtTm9F-Cfqg&B JZ^?f_?]>D:Y4kQ$׽A~2]h@?ڱr)OVW5r_gh 4"~-Eŝwƾp(f_Ѷڑܐ^fPNUҔvyy?rR%+ltPEOVwCGH߮[S.Ҫ~/h5@~,?H7̚SJ@hƅf"w,QųU%,)x$nY,`>woy+wnٳg#KFa-vW;Ymwxw#z~"qSʿh A+>M ivXN(K 팅^@޹ԗ!q.)kaiR<yhNF v5 ~xoj.+YNK-x= ؒEe_Ey}_q&s cDp%H_xiʹ7BIIgޫ0mq1ˈ[r_M/dR˙bx5WȢQL+~-88V(GpCX>!NA%y$z?yd,4EHQfm+[ahoZ nJz9 ZE-,k0t{7#Z,YEBW54ƍi8Q6<> ;:Y1WH¢"WN^xK.7*B[Tu5m'v&٩{pOh̩@持5rтl1 HȳpV[Rt7b4 w!Gkb/go m7 * 9]xӽ89lہy`OnͪZsf{"Ko=ZTbIΠP m(F̮Yl:raCW%f-V/vFh!>l*Œd> 5|͔ =@YJE^s{C㝥fY׶,)0.#6+47-@) MlV\Znb ]jBÃ23&Ne6'-gJ+vіSF](2ѓ@ Ca\b!hbskE } #r`_lH5fu]a`-v _r~cUR4%LfX:x [{}#Ghcm{bHQ0Cw4A.`mNJɋsqc.$,qrtrۍ-uZ ގXeg)H OΓ'V'u5z.B@q~A0W OXO[ 9CSV4$^:cjFZ6[ȅG"c~hhbfe#~*j)){{|4x^SN71CsUPL Ztml+u8n2(}ۃj&.{NȯAVb9QU@ܓs׍@JTfo@k{;ꆼ0XxЯ~l!2rQ:}l5aegʦjq\|A/ ;BY;u6> v {7zFvU[z.Uc\ 5$h{\3ke02 P"mN'#v1HE%N; aiE!fwu0ut!FAYC79֮2e+ʓS{T1|r*O;N3ZkyI7VsW+ijcH`%`1qB)^ׄiUصR: gnl ]vߍ[4 ܂:!;"I4Kbkׂr7JoURXևrzdc*o֪=+X-hl}U,"k"6i$6/J:d_i̙>{|Pi86@ 墋 ;caj*iy)EZ&sO~wXdщ\Y:;(1%ީC vZ;y[ .f=BcZ%WX:Zxt.57݋!hGZU@F"{0nw\:G箑E1yߣf_Vv?LY0͞6_UP]-;3wW"H`4 ;Q>؅0Bqg U/%@V^͚3 YZTC G{]}I)EڂIz=*M[Ks78'ZhZQ47L0?^ C*/cWoǓ1fߟX79ҺGÑLc7Zd{?2ৠǚ;\;sY_DHi!Z=?n7ٲ '%Vjh6c3YMD0JһP""@GUUL.x,PgϘfWc7+x؏I XJXdRsy9 )͚ƞ;p뾲]Yݤ'M":h[S3td}B+:x j5$~K+0lQ Jy9p1G 08g$Q۶`"f,CnKd(9d:3Akk.}qL诉T6gf4]f` Y eK* v[;KŎ;_˖KuĦ]2;Nѳs`#c\CrGd6_c@{?Ϡ1P Sp>CY3:E {b{ %Mwu.&XRVmz/k='9X#rruJ+}A}j wbcsXÿ-+?2'h(c$H^YClJzrQc=-Nf\1cr gQ\sC/إɢ⮺-qpl ߔFe;,]W]A6`07u·]APhjga.1(N%l7DEdcB[OM1ܶnNWX4 Ws~i" f8\pȇ@0NlX5b4B Mf U.s)/~ȉw]x!џmz a<}Q}dmB('Y8UD},yOafIyh~T¼D/ RĈ58)A D]]!\TgJ!t9(8jIjAtucg1RTxehv:}eQh*8%A7AK%ð7kNO/ /ؤ&X4~ET N$laSܞ܁C4{#7nd~L Mc$s^.X Z2`󣍑 Y:r?sEW_+/>VqC\(m@kX)i:Bh9x!08$Simz-DB.D`2t5,.-p[Wt1 Bb{8t$ьHqpAu9TC =<)Pl6k̉$G3cnzi4CUi(P.ćX/Ŏ6`  efK+1}@Nc%62UD#5~RpqL'X5$/k]ΊB.r&n)[{EDMqE*PGj*64K fDLli\Cb#;ysQ ]gh"N3B_G\L0pH%>!1O* tUĦbvJ olr6^a/ǀymR*XYԿAcA X㓥 C, Pģ*e-2 CC+qk&jrNḱX&9ט6n~ٺG wF~9%nR6׎y²ڛܛ+Dh CxH"lS6 0)g7`qb3WesѩN?U8%^8a5Ui+,!T5.䟵^ߝ0:XN K,wC[* xgh񨃍„8՝PMnZ(ӵ/Ύv&X x͈"i8IjR(Ĉr ,?3Ԭ(n xa+ dsG8=Xa,*ە@F$ԉU'G,҅n.|DO#{&B$ rl 4KT-xpd1cD35ݚи" ~ w"k3XH0EP9LT 3-\Qj&V"Ni1&(, gq0rܠkU,%[{9y5{e^/In^-o}ç E8\]"GT6넹p]TJ8y!mrU/",RI@y *4p*2B}$y'i~52 :=Mʀ[Bʔh:s b(%ңJEVZhjpOyus?!>..;os+]Nˆ1 ~0@ȥi asX>uƬԲO2愓\E>{_7>^Ыg[NpMث9xU` ۺz8 BCsf%$<9{p6mn_' .X'4iO[Jf_ur+FMwtͣm֗D6¥&D4Qوγc6FB6X3{K;,C[@Rbrw6d$rU1 qH{.<m:ն{@s3E ]EU]5]i7 <<}[ZE# 8vix4ޚo-퍋ze85x-q5bO;Ƽ#i 59Ty+ 8ݸ?RLʬnog^?EEscsN0L ,CUȮ=xvxiH1z^_ɸ}6gLy7³r26 tysSfNȆOܶl7]p `ŝCUu|c\Jυ$`}MTsfպ_V_ ^Ѕ O[xO9 9@$Oձֳ x8Y,@ZJɌNY dVo`  IS jnBB.8HT4yvWvϱM0BsDaքl]A'bċ5 cI3 p@Z$=O4DJ3&irVɊ L}|5YCL{Vs,xH߇^"9O:Hź! ǢМ}*Bp\ieANղ7牲'nـMՖ/Z!ZƺWтz|uy8$ß- ]mT4-Eחsp#юSRm.[F?j |1ʣ/![ 2sh }xeFz *M\D1?nuv}(`VC6wKAV?DUu M\L҃%<|tb (zCO+Xjߺpc 1˜>|f]78uk*4/QB 4 )hH{_FB yJ-fzlUh\d` 7Q Su>gwR}aΖnf!8L^'0)]gD4,mK6ɍT+^No=S~R][dwݶ bmSk=vuS3q]=ԅlVD.騔uRQ ]`}E[M,$4EGW;:r[=W~b^5ك}w~|bދ/}E_tѰ4?s ,uIl1n?J.mQ*e U1?HZ\aZ !Y>H%*5FڱfV܍֝鍷YpPj%4FI4HN?ph뜇!iD%bh#A$DGˈix+%sID~BC{J*)Zw1t/ЩP˩̠>\ 6\K%Π`jBh v{isYNFEy8VDaI|dWTC4+2={r X`b21|o23x]BgCk[3 H}~`i :A X+XK.O=R 嬨:z%nP m%&Wv;bl" M%gOaE|U+6МP) Ve rIi/<~v^^^?[GH `87#>שW«Zsҩ^-ڣHu Id_cyW=xJIo@^hJ)fml.g"zE4bޞ컰sƯ..hy;ćO+΅7J 7{7h0VW3a9pC] %l3-~ cQNj5Gnd' ynr+ E&‹ =x#,|WB9^t,?gSWaNO5eLZ灎M,cLXpPb`RL1!"K&@84VFH%ԙ@hT y'gj?FDʷ H:$tf?kNdc=re`Xe0CWT~&Tx|,A9"Q ﺽ( 1;1$Ch#RF#bMz\ל,0@`N[5Q CuA4]Cq־fcmqw{}۝ƘjSXWRRu36pb!.]YTVJ4{ :/[&dgI IC JQk$r:߈b{d`2A/EmLanޛk9쿷Ӑ{}Rlb߰G4i2[ڠ=iDUI wuYu [[ܐ*sc=~5s2i&ۉ}:#n0RN`AĚ́iYIQCϾ&0BTA{eЦ%o7m~񙗯@ȌCram œ@X-*d Ŭ^3H2Bv01&I}lmPiѤ5 PW_.Z~M<-SrŹbӪR󻌰)w$Б2uq\s(M}}#FŭQ KOT?UkuAVgdC쯉i{'3sj@rcЮ) ,#Wx/t:b8#}鍔ݶJN{PړH15S*qgԞw[jګd ^ K󢱣xͷřB ''5^ iߖ4OhfXI0a_}PvK6G>$GJQc z!%O6EgFR0'cLL%׃Fϩ}iqXt\C7`X4v4ޱS|2ȜC2ʠX_\fre1PmT X amea,hզuYZkֆV{WcwiF.XHcL([>Q-e8LOz]xqPLD@ tVr:tEjshz [XC]A33@ ~ܣ 5m1pM>lW.^B~0(X8dizFۺLpB&TFElaVy1U$eF(0 c 5P0-FZ4`q2(^o\S|zѭ" xazXjyDbۭ_k+UYPLAFaN:3FfL퇉n̓iZJȐ6NnҾ W"Kfl2W^66^zû:(7_fKAd#K a =.Snr-]AB`!1Fr@E4;JAD@V-]:`Jw?͍o(F\Qx&$&=Eh?/_g ]Xx?eA\X9pYuAoCcߌy:!L:A#yZ)Vʼs @>˥!4HކO@+D%` _͌7ofv Wc 6mpYfl}[SZR%H@_2tjڊZ6uN_^D$e kU kzM蘱("(2}7s"NC#Bcq8(v x\o™WS0I⏺ŐTdeZ3.$ vfR(i2E2Z; \ gjK5 I]tdZy6䝠jX&-6 K?~_3H!Kcvu9R|n_]u|u_3]=TyirZҹdeɜz,~_Im5PzZ m:}lykRr.>1t沶72z2\r6>QDBUm[o_?00:=F^xҦ u:;RQ}{µ|0R(u8v[q]lPdkU,pwn||+Ѳ2URt2jzdT*5}l,?Jqб#ēJ]iXCReE?R$FYAVWd_t;G]?c[ӗR7~//h!3-8sn/o0 !SZ7?8bg_5,"DKa#\#R\8:faZ1t"%@DH 68+SCn@szx Ӗ3NH֗GC)v"Ia5?jޖ sȑ{. 0\0=4"cִ@KK?~4f7ycGƌ8~0+%w[( ElSP(a+JF8|z4 Xt3@xCK^@ g"a ^ >j9o?d!p)@R0n/qqqA_vw:dVFDb2uŎӭKXǂ-8F]o  {  'ABJZ 6!`ΓR6JQ!)[ĵWX:2Eg MP9¶MN8?201[7HѭCZ4fgé 3@fGq׭vl_{+zohH WR}S$#X׫oP@Hv>gw B3vEYj)n6p,5A>GBX[g@&z]E{(T)xT$ۃcVK(#Y}n B.$%F@vxSQ7 n &ݘC-?brm[ΐ5LS C]a[isNQ([v"$U2V>w>c&꨽W\XHi6l'\TObC=5 [K7q5v0!+=QrxkrQ)i\oSmTk|$J9Fs.Պ(m}}o%W~?8'/şjjWmϵghWs=7awGvwḴW9!S/ ~?vgZk,Tv-'sY Zt 7}T\U.,]'b^k܅syF~k+PǏF|W KhǂoG P . cXmXG`|,b3m-M6¤埘})2QbTTa]DX| VQ+ЃZs4bŒ3!)ҤY7P2͢ xy+vԼP Ð\juBѓOe o|1{玮3a΃)J?_/.sSuedwD-Hq`t:t} 6y־D):m=Ojh%>. }0¬|hPQExl'u PZpi?0'rH#8 ̛Uc$( XM7L(6 ɠ#I^d TbIRb7n3gKC9 p".6LxvrCzOLi{'S8㬕 rHz 1޹>|]3XXϛ1vAP$i/qZKL0L)_kf= q(!uC_;NmJtE66?&F=KJ[Zd;yw5fj'$w0yjx%17E翹t!:гHמ;^0yK[C?Qek8TjaEJNCj%Ą yY9VI jGBg2v{4y=CF,sM1xAŏjNk `GFv~0OXDܭ8o`P:rNǣ#a_Em[{wm]%F5oF z2suܷG 5R'fz..i;Šf*,YRYQ5psg'۽xZ X, 66Vcl)[۾7SǞ (όZx;=`(`3sKmbWLu )g#;wG|^!}`1}ҩ“ʘ록u!VyoUt<&Ǘe ;~Jswpz`;&xّ2S'?6@s}?H4fK6jPMA?2ݚtN.E_2gt9]\h$l ҏ'q $`i%lԴ YvfwDA$BM6fQ+dsSt2rXWm\I7E8*fʲeEE"G9*1Z2&Iq"HoT5mIJAHL ':Q4$iRe 1E(`BU^ 4| e̥b<3F8fSQg'ĒR"3Pm5z#I?+T UDc&JZl>6Wcsgg14 D@Ys-@mѲzWSն5\#?oٝLG6=סr]=XD|S3 ((q$\d_)\l'$ GV(iTeK31|$0-r'ZGrG7/rOg1R\iy&HQ_r}\jŇ{-u_|f/"§p ,cŲ+3(4l+a3QzsjV(qHVehCaJ5; ?sCTYTz׃\M^gب7s-m@N1*]_t2湯YY$F[nIR^%]aY& |q:WSaTMW_7m3C5{-s.guuնrU 6RwzjtTiZ%ltQZoG=.|FMmZC J} ߈N !o~PũI`H=WI-lD5;% L? }5AO͜Dy C-9SDZJWT4zR M$lUI1Ji7aާ,t_8qjcWpǢi} Hc-cg+Tin! *;\tUmyX+oI)Z LT@R394Pnɰ* 6Ȯ=kc?&Eը^ݥ )+9IqgID$<\dǚ93`~3{ŀ: I.g8>Of$Ujɵ T~/-2B-M(M``g dLOk/tڤlc􌥄3ʗd bt ]m۶m۶m۶mkm^m67+*꾣*":2wA p>L$1}q]QLG})2?Vu!lMOJP7ǵn2丧,NI2:R!gN .Xo;>(2Iܖh@u2ʼn I$G\%:;Z17|1+hAy >QTM2'L:Ev`W Ғ%d{M?\=2ׅ wz7O@sq(:#-5ag \@P P7øP٬6;^cW !ׅ$جMdq1E Ke rg,{/)1S0BW gTy^ 卍-?њyR:q1z~|> erqv0'Fʢ^4F!Eˋ pIH4]᰼݆푼|~G4s'==n Nv) "kgn2cd=8 ch/A)`!)b+ǝyk脞C3h$ ^xc(HGXYRSi{4hസub,Z+D <@dB,P5'GI ]t d9P'pCEV,enK&<4z4 1*5?nsx.qc]B|S =vmrq?b-wh V` f_sbt0KhS0Ng3rAjPd ȖWD8.}7zT-~VI"Q]~H&7ו^wlN0GON; LVr͛dWXtʵi%r4lpľzHu%)L>,Elf rLi9mD ;) 'Y2F>gϢ ?nrfzlx=D} I2cY?Ey xmbłgF&\ݕ~ !Y6xM&\N[pogH ޖO <0G6~W(F/xS^DPvP:d;K"rl#UOiS*;ܨ Og\1n&c &p6> S2'WHRËXjlm&cXRO<#9W#.h=^3IH M~ےجcƬv֪W<87C6x[[Ǐ)2mRI/Hz-rJvl?o>Ud2ŗ$ iGOo}-q?/7u$HδvcaοS}]=ԣ} yIIL{rjYJ?|m(g%m Iwk' IXDџW6Qh9 ^͔ǧBё !KjZΩkEZ蠽 9Z^OJ}r*>h7XezuF$&" Sc&É.†lt%%|K-$8=7Aepe暝(k}Kr!vƩX<ñh]m. [ uGV,<}S½C!Q'(FڢZ- \me;>h38wšѳ`k,,К"I/gYe>X;pdԕ#5;OjejIL„QN dN1On\ˋM61`ۘx+[@# 5?JN\4Pz # Z 4[DZ9jzT6/98F3'8YQ)hsG4@ I`_<"|TȻh#:ON XIj,uBTܹ[4-Q֛pE<ʖP H ]MK$wUIrdY} H|RT^_w~9BmC%;TbYAQ67o RƽUTzГkDUȊ8B:" V}ɯ8~|Q)+ .r0h&lN"U*ddQ-JRzƀvs3D[ ?Q缾5ט,:(U8WD$81`巏W$BZ,~ ߽r|fӓGKΈ$*.͙2,^O&WV)LM^EiUJ% TF=ķJC:5{<D^.D "H*fKYleY͡l=Sa(/{ViƽǠfSgjL@N77=OO0\RP(-J jdZu(髴sU5CLxqG5;; ԞaWhͿ }jk#V-Φb9:.ern@9[;= Q rS z~{8pzq_{^-w8TF'u}T_'ufRZ좃Q@|8| 0(|s1݀/˔?jaքv8/`YxWҏŴ7?8JT}(`2 ex1٩Rc"֙GIKڪ_{m'`{u@=%ўŷNi$su/LWK/MJu l{YȄ7}LwoP ].%"\;t3hsyO]?;2d|ȪP + ; ~?'iTjgyGzX7W~$kpGޡhsZ9lr76Gq8;-ڮ7=ެ8&ٹy {3`~҇uO%h|V?wm^|a^E/IAhج)oˋ֢*7œ=H^g]xUwOCޏx;<[<\kl<i͠_Y{jcU xԬA 0E $yuq0԰VGQZY1:Q%Hen̊lK'h]5j3-_JMTq@KpϚs;*Ita(fFXg@ -Iw+Aek pn84cCxpSkF2KX;R 8z7NmL7jK!ZI*-9\ d]H~1Rv b4sP#M;d:2͠pxٿ76gP$@ES-0h5Dz\O~†$L۠gg>5;77h092<7eI$ZNn#XY$8y3OChbӐrFd`U v(Gg!Xb5~sr2 Hˎ!-4+A\:jbQJd#+Y,?DD,5j󜱅,jS#IYP/$@:\^.Eh1tT4c 0g}y H/;0CSs0R&:yF,,(WDA&JAXU 9.%ZbSD͝ 1I*h zl|¹ۇ5C ҽ۬NߒvYAq\&ތAr.O l&$OF0Q $ rn卑P8:?6fVAtH!kuR~,طc chÜ(]ῪU ՞z::V`9y9y_E&  sӧ$^7:g7ʙnh8:S):(jsSխ&l$B特y}><=5'wҒ:0?E}!2dm:b=eQUh(~QOx]XḚER5 ma :f+4ڧM,IoZcEx1mqjZf0-=O;V[P ͘Z6$[N`!й놶~|Ϣ>u}{0%&o2iJOu?_?eW FsxWaYRWŮ6IsʘɊY!0B:Vys lN /w(xEB~ >#Uޘcmt;2@]BHrm Uv ~Y`BI$:qRjIcE.~N@}fK݇o3s;/8Cnf0ibv"9γ cL |NG\#B2p'Fci9!sGd$v9v}SVCf螺&;V_6r6|X>~##FG4UTh^v&qFrj7ue+L'uiʦI')F݅-{fډu}Hv xW˧5?[DF"ރ Bx7δQjm3yWnX&0rHeuq~6T:AM3nu:dU-}kv"Rm=YK :;@h1W凥ObgQ[BE<⛴s*)Ei%HpY>rg705vv5uH幠@SmnX /n0; "RK ]mjC"5=j\]5]_B`{ 8W C {l?peY5 l-t0q?#z}?4$6~}to32ҩgrϳob=u5^P>4.JJPP,1&"Ɵ3݂ ]G7Se-4WM2GK:TY9=uAʹ1JV\(c=ʪ-hJY/%y74zho5/ҙkmj(ѕ}"s5l"wTC!khi éIĺUks<[:l/@svxSiC_y_>51r8Xcc\pX48fݻgͺP rE^ iJN>v ĶCn.byH#},< d7)ڵZ-[A6K0%Z=T SO55# PcV$~>wJE9]f_Y"-AW{.>5164uP$E͕n%mhkxF&~d ›% |jH7`3}QG[u1վil>ymͤͶׅbjyAlR SUCR֬ e*M[\[%gRm҉Fbr>Tz^*7 7.kR[>6UwWroIbu8s9>8 `G@@x4WE L28 TTQ>J,Q]HCI i]V ~f:kH'ә"@?3 8%ELKxBK:-"+ bT\;DXЦV0c HF֏XqкYbGhw!P(&99>T a>@RHHhBQsqW N@ֺB^babFiBJwHFx[uLb$tȨP&j$Fĉ~bd@JbD?p $l=,, aLq#Q|$! 18`QCFF6Ae}䗲GN!65+|dJ=a 2u 0CGDÛ?rwR8rv NJ#0,*DeEǀ#Q3Épi;Ue4D$ȃJj얆+|c6R*KCN2'ɉ3Fq[!8Uմ>~~}vsp{{Pn{~2mOr)E7PE&(cr8:&)b28@>v ?UA^A& rK`H[T'g.X@2)@'N2R%UU>S=\?4)5e.lX0U=WR{HTStmXiz7JaU2‹6 ޱhm=:v qJUW,_:q<`qqK:)4aa -HeԔS''=^<r9_7M &ؼv%q!<9mVHBRP\H6`9zfjIz{Qٸa s\v^z8fUQ@{Wxw-],eGkI} meb X:b`q[vi6a$ ;'p(W0 1jj aUaXE*oI1zRmHyEC19pbX%ԇ u 3E5_j9Fͧ V?)`U%NdF ^bALSD#͟aHϡJ-G؅qm<2WXЬK/ɕ[c+`dxӥVB1Jc Rt("k#>b,Pd1V9wVUfd@;αқB/5C 7t! Ղ' j%ҩ/"P#8g>tZ>8dKOK:y*a_biW+Eh98̱* OXƍ0-d#3'Y^v3ŕ.7LSo\'oN1dvv7aTDVE^bm}%|0ް= H-$-ZﭻymtGfS'yٕ6[h~떇"T o}Kq~]7;0.A'Qu׾&67AAEY7 (`zYYݱi}sDcnd9,E(JPb7m.՛$ i؜uGq!j8+wvѹiy ҩާ TUpJ*Oh;tlh^wf tkؽ3lؓfYM@@VR!I(`c6D΢^굩tت?3IRdݴ;94âG u>k|քiԿ|՗, 1W&_1=:? Z9>۳?T.1{ڳ! R럨UւWPeQؗ)9К1嚙W)3@!&Ļ+ylۅ~q"sK޹Vs9g("0(Jsi "6{{T&b;V=C)'Pb+/~bH%rT2P: y iRa̸s;NFRI y̑:h^#?)m`Q8+k{)6u'1Ţ0HD'Tl=0ʵ=©m՞P.˶o =iqz/0S<ܵ@ƢPf;+fY]bE 13OwUT`ejFLijUE{"(v0_rn≞JlԀ83c.KŲif8sl !6zz!Ƥ8lF42tƔ0 }V B٤HǪE:#p3"b|tv |B!KWB`L 3:XD|=,CV?G 5X# <v/`vd֢^ ~4(BS7 !o\ow^qQ|޸5 ]O8A)yv| ׍Ow O|/^@]k6˝?P勂 A7HVVf\"/1agA#k]7Ѵu+`X{3} >qb8}#0JA!Ib:!Rdi8C b;^UqgJ0["4 H|dx>O|"Ě!QJ njoj[ipojs(u^߅_VUհ o~\k~ol$,")v/$9RhbLm|W>=Ɏb$1pyXL,I CRdg j*&!ȏ5_7gR<123炊5Qk TDrkn)~nĝk-JgWVl]sWb.ْՏ*sVm*JͤZvĘyZ@(0qyצ$J^t)+Z<&kM>j̄u)ʾ|6yGKsmpis{V/o,ڹgOGt/[Z Veq2'3:pRIujZc,Jޯ[F7}]N"B`E⋹+6*SG@" ;b p4~Y~/aLG4ƺ%RfR"7JfŽ4mU+m,(T؞$*yً{ўYJ3#3N'm?|.1¡)c؝*[S"sQV,QWJhPER~R5o(c_rY3M抺͗zuts$(dFꇣ]"=4^?Kd~4`k64^?sQ|,u'4}mhm֯hx&a;d!b:VWı>A8os) sÿZ"u!RQnű*%2D] !,Z,X҄bp6r{(PX,6N}}z}h XH}W$XG!1fm:@|K5|/#JCE 0/e3Ԡ+VK0BͿ2Y i㭥rv- /عʀ; U J 3>DI³ẍ́dǂ[!yz$A@EJ~ߒmA._$QK )%-oIU;!$ 9OŌ2e@&Mrq#r;2vY$ۄ:BH[` "!3_yψ;WG-S#G‚b%oz<ۇ { o){m?|qlVb\L7,2raz!pXCñ=Z=ѲBj>U:{sx6/GDu;Dђ9_<e;6/uniД lpt^C7רPӞ3ԟ5$uUV;^'CKgà/ c! }moj7teNKXZZ>w{U`>_H5:j@Ø˴voɻi Vf4 bR&B6rMةpQ_UգwiRc&kWy3rmZk_4)o]E(O-dw7|wܖ;[{)tl51Q 93uڭ5~I%E1N?["KcnCq:YU }2hnw wniOXNrX52}{hl15͓,5߱_׍o8Aq2Xf9Yo&Oދ2PØwvS=BWYn$߷5<ѭ=F糆ʫD<Ɨ@:2ɏDIgklNjn o6wǮME܁@_?pH?xwS) nALŬz# ^RޯMc`9փw'랞fn,d_zL`O_88`CrwGF GR ‰1#??zLҘp+SMb= <^Duc:?@dE!rsqU9WZ:l хJ)o(^uS(<[jFiV*K(B !,/$N^o>ӨD-w9I$i_~D͒zQLY LLX8eNit\=a?6XQ HP(ĺA,Xi*V.A^Z\_iUV ۾4ĀFzF hFe{gG: 3^F1å0]nkqCZ% g8Tyոɉ^>W%F+)8mh\aդBjA)tZg8LqF,-ҙI>pqc!5򅷕~ =<#7pL5ๆ*os2!DB5 feg\K\hRG Ƴdص# ^LB!Ÿp >z]SqQ[XX }l!zv˛>É5J?չf{% k06"e,2B\p<;@gMeBrygv и> ۄ<؂BtRjJ'5B4]UhOpq+W}$Jr?I//U dѦt1ƭA J;"*Tu*R`0-&iu–C$b{,!h^mh%>4n4͉4GP"Te4zJ䶅'^6,B7<:*{GKW;7ִw5],2RXy;!l`!tEH$ϻ svI[z90 ٶ(i6s 2'媉 l :ϗT|)wͶ~vL#+z1:r AOp7x-*D4ѦT G3-KUB3o9aIVLChMȤwb3eѫA{,0Cυ+71G}*H.d3жt"l(Q /TvUʙSY{A*[ʹ꟡9n5kI_29ͧp}jtOvF4[mi|WHk><+۝!`MawN~.rnq\+Df WlW>F4֩rb9 C?֢.#oS) =N-]N% \n? |YsA :.4(%I+G'ׯOjnHjs2 }>~4͒HTxfӧ+_.uBZ>OD8/`tuݲ7#ٚT KZ7мYyzckb粦IQ+ĢHst5yǺ[~ceucŇ_'heC|.~j6ߺ db9Sa uxYSȋk}Ԝ&-ohlM%qےieA 漼lj ~.39?_k+7vyj *`1noƙchmք֥r;o_Qf ǃn QR]voċ5C"$ŤBZ϶]c~Rsy:KHI.j* cҽ̀` nNP!"QK$Uކhh &G\gC\5-O桼ѾLUanP)T H iK#%=l" !=y[ѯ?i :tyc,d'?^&㧠D5e׀z009K>:jdP(Wxecfu jD+mّRA,D>DKC+֢[LsMBJ`Ɵ.`5!)8TR"Z"0s}o6~xXGgJ!Zb.Y^WT"?iY_PY*6^V_ -_ ]D;u&4P)}`9{xUϪqQ<*uk({9J^r/q? o'ղ?'BY9`,gbʴ z*v«F>+3V~rz܂}J-RArB5U>H`#'b=@=d}u 5UV)HteEmڽAVM_,GU" }ىbhI-.CԵFKƐ%(sSwGFgsmvA̤ Wp &MQj'jH{τʦ9;>**^p!%TןWҌTmR,xm.㎻@| X*CM詂b φskߛ {sƁށ<ǫ:Qq+p:4gkpW$Gas CE`qT6b7nbyI4V&$|/z:oEN}{AU8*n Fm2,Gr#ק^5܅iY:bFsMٌ BAދ20r:k+`  Mx9)h,nl."?$v;1q٫˴x, ȨݵV^z%9CߦjO%EͶy,jX`n;w̌T8kp+Sl^XP7{cl}utbu; Ѽ!Ziq] WOzQnC rSI ʬP]HL:'55 AoD~DYϖm,ӂu2iQE+#g IQİ~<5B4B 8M4;-N09LkYZb# lgW!&ET ñ D"/{*|) M0d8C(KX3S0%F"Ԟh  8:@9( ,2HEI;R1.ܥD ҂.a?CRjqnK;7 B2ȘbCež&&}d V’ Zl=0j'뱱K&ae WCCcvDg#BFP=XzcHY\9\q - q@LlM7J6tӾc =ЁsD3 /Ue )ĶAm Z* -堦I&!um~{u;f\n91.n)V1/O:ҟ98ʍt9V^َCݿzIaxq*/Wvt7'·ܧۆ鴢}#B&& l3XeǞ336hY宗z!Zncq)F0}Q)tw"6Tfi6 &*԰TUE2ې{USŒĻdsoa7 ̝ Ӿf'[ǥ=k<>:-/gPf"Ds$Ơ?vY[o.0Kz7*9IڍJ3vz/Å=8c#i{5|I>XT\.n}hcloF_NqEs] 0=˵oU AUu41PvT'DܺauTa[dynv٭ ,iaf; By&D Vyqb$GJ!%_R4=:Ae9^Dr[JN>UOy&_vؒlfB\fc9V%5_܋=/zX-S QIHJgq鉯Կ+VR 69Y8nҚY$/d~"@d>G7b25bP#ImSE{(2G#>SBBᛂ7W%a7A G FBGR/Q*i )n(e22b^WҲMUA 5=ص?lGO5LLh1枰;!O-Cl$=E&!nMWFˌ`7BBSY4JS@ԀI Kh3e>LBcMC+B,pcn J)"e7~Z":?LƂDMǫAG|Hsq5Ȗ;:2(k^eJh:_/6X+8)$C)2By`IQaߎ|^ Fi $ A1-KnP\1čw)Y>p:h >L[/hFſG],*;`J0Q(Ǝ0P%x0*᧑woG?@sCNv 8-4FakgEh'4ܙr[.`7d7[5{7=7&O,#<\V|Bo0bз,#FER>Qrlz†y1u؃H$:BPwLl9e҉Q;0`fӤKYͅV?uI@Ğ]غW~θ^ Mʛ|_`ωtCzt)(K?{X9턀@Vwl h}dKK Ua%j٬6,Va”djpF1= Nɽ6LvҢ>RBt= $xRFHQY{2"̍^]ɲ^RhlO."gO ;9+'*hctTW{yBY֔eX/GO.㺄۵]>1˕nj/| 9?%n޲0`ZW'ܕustEZ!'M՚>+IMBR4,ҘE5dk:)Xpmٞd$G_2GYf јy8lObGXY{ 9 6 R+},[ ~kuF5]*Or4X9wR7O?hAk̳+\bRֽ3t[Lm 'K֎ &Q`Vt?@5F1)R((pzQ] m aGs(* P*f̴ ӊ1FP*ڥ1A!NC!WxUj\yܲwwoFZWƵbm܍{~8j+btԦgvZa-_[o:1!CU-A#Vo]Ui 1ׇS=:!MMU%C]gyw%74>4m+٢X"cߟO NNur]Tf=JYxnqd1EK #%i< R9a_pA)9µEtΊzLJNrM¦?ƪnŇ`")i0A PY / =GrvEj }2rS+r }`ӹ+A*!rl Znd[k_Z7":r8|/Tc^'U<O0IK jCpz@ hW1(+[#N>>f/ ܠC/D9IpadNf>-񜜰ΙY_OGy_៾6֔яv>k/ސ-vhU?γd9% F $#@Q<X[*i$5x` DzG:1\ ū5@TI'941˫=Ye,AKg=|_ }˲c`l/=T{g$"bZ$3^ְګc)3|3SȢhâݾ} {R+ r]A0>}9 H53pFu29ޖ-Ө9 }ndMdW_Þ4JYߊ)g,(/̀1:Gύ Ǭ)^<8 Lic#42X>5c޶}m(Mz3?} x;:wP#%ނS9[nEno_cs0m3j k'dKQ{c%1크v' ^*Ďg`9(4h *E#L c9烴24XoKV!޷ףwG\tʨ`Ŗ^@k/BM5Ew|O_Uzl_dTA5ux8Dȋ঻??$9{G1'SC$^ZoJ$H5hƙ7: Ṃ"JJϘ}>7}JʱoeHvUvv7.5%Hc|力DAb͍jfW64;4;2fzzgȐ[E刾h=Rjhv%^zwA+Zz%?U)NP%-58*[9!Gf[Dm^Y&qQ?2:6Q/2DWf,)f¬60>Q';j5j)/ԌT1݂sK7,bp5~(arkm\-sLRHz+"3v#%4W爇N+Rc8`):g2jNmԱ*|QO{TfPB[i8 b/} l'EmNv5IQw x83x:~>q> rq~-kej{d 4(!P . i=C8 -aVQ) My9x=9 5};2BAM $[er\8 ȿot.r4>{sss}7]EՈIRD74JcT8C=p}u3w\:]FJz3iƨ>J[;?圷UHwcC<^ߘ,&T`x m+g Zm!eR{d'/g{$-m!6x;N'5lnQ%]Uۣe8CK|.\UII-VfÒ-DuVf w(a>E-NLҚ%51,f?)qp;uU '|j * \RYAi_[7R3}HiUv@EQo?6)rTQiWI joؒMkSplM]ȲS :̇(4d/ְ~ <[p<.KQIj؛d ͩila;6&2{:L~.tJ"srWѣo76e [q :vT ڢP4=QdIDQ A @JӁX-hX^6k;C!PTQ5sSDM[!ydL+ b_ vG$9t5Ǡt__q]8'[﷢GCDvؙMZ,nnmntwgxZ#D7A gQ6D_7-WH@ 2]~jTB\e@Ȓ"P|I|z?fAcØ.Ǭdn" 'ܘ0+A. nsBmjȦ!^C5y,9.^0M\|;P:\M3{ګ`ƴG$6bX$ǘ\<НV I*x=7nG׊WwZ N:Ddȩ=y,cf4Z.Ԥ&qƂ5d;EXJL>gRa9!T g$3Ӊ ÉQd/p. 2 \3L R; ڜ|cnV9y#G3(, s'i&@|;\K[Rbh``np.Fm0e @`+`Yda28Gec ڪ2!E3 r0%͢0=7>K0E)w1nYE2 KB2Ɉk&¤F"iU7f15ϊӻxNg J?A0w;L n'7Ԩ~E<_t? hLcۧ35h톖%33kk?1WqiN7 t%} %FkCv_S "p_f]GNj[弚[1z[?vvRƪAgƲJ)OBAB./KEN,Ghp7l_B~:RߘThM3=_1ǜJ"1QLTPH}QYiXz"5hbdD2  L31~ftСGJq,tmgBٸlb $kxs tȲs_a>2kkj7 UCAR(Hb5[b4[YnxA'ts'^aB'$Qb4ih"(æJb{Zzb:aG~easRdԼh̗MPK- -6a&j`EL" l-&^)pi"As/ l6p0}4zŕ635Z`$ _rtMl*=E U8Pm8;`[=*s0X݌>i~J>9`W:fEЦh­{lz)@d S{7 >iy'9}bX@b?cբ̒Wʝ&8ayC7e8]0٭U#mM'xpf'tbƋP'z~cL6mǦnT] K@Pm_Z1˴wHZpƳ>ͿV]Ch[~|o(n pS[yqϋfU63$'SJFi Ad z5Bb5ۛ1o^/A]|Tըk9&8V|n4zs[(,OQ¯a 'sg@|8 YYQ$zϪRɪ+%8nr\Cn[46{X%:Sˆ? 0}" >.?li3t(Z";А_0M sc]UGehwu]}Q=VW/DqX~oܾ#Vaͅp} 5qiŅ_B1!iV9es8ӰWK}/S0Z 3-u"AܼTGŇyg{ 1b?1ܔm^zۘP;?z-6Oz9{l!{ٻ²r p\ܥ`zیX`A`E-PNGw-yAo$ըI?k,#k:^5JݱYxKʆkI(xq{;eJ5?~]$P-&$@Zv$U1OILf"ƩF`%FȦDH+"ߡʍ V]y34)hO ?m$[udo4.qz=]ݔta(lybz=YMgw[05qC%q\~Lxj<@Z{%Z {SgGz6G0,>ˏ+E滹jIpiQ-QGWyfK;@+沟%D 2YTܼ!T|e+h WV!XecUBF)[zd⠆k~] \B9y\VX|B9i:7|{M)/nݪ!ڪC8ewxmf!;{.j$-LגדAͶkŮ t}"fogyK.Hkkwp{"s5ջ%y<6ڷ~MfwͶZttlUj'cfH54ϲ9n+IV't $+z3IrM'o#ueWteK|RSذ@Ai`q(Fc:ym߾~[I !~Jyׁ&׺EB2j/r^\RGLljK .ޖ5ףuzΦU‘ ~6۽-T^.K U$3CuݢES_'w m2)JŰlM@xQn bPd]uR{=R]dOé̂b9sk%(kQ u$θ|go ZKwm[.'*A Wj v5R"F*2me;&&W;&ue|#%ŕR*WȟaU\u4rstE-!˽MT LB3-2 ⩭T ZQ Q($e(u]tr7"p6wXL.xX%W!6K(v8AmOQi8!B!amȆGErǕi92* 0&iȍ1' ,Cr9JisU'}傾a#Tdr^4N7b1I3eb2+܅X<*rf比"g$%tT֟1,iȖsJbỦT:#h#P)Xi0SBN ܥuPTRt#IS2g `]^$-Fb(jڿkf!I5+xUxI$F',G}< LZʨ [Hw$u"0>'igj"JxWӏZR8 tӉ{c ^z+%Ng@nTLNO~oCL36! `u[ݎ0N@q,pp1D $~F7 '43VHGgU-Wrp팙ݻ?2ʕf=T>K39ս_h.KWi#s&e,/p\2Jrp >hqku ܊zii\6Jwiq8BK =+B+hbaC׸*e3ĀQ)ƍEwWB ɣ-do3 pHk'yBeȦ(k{v5vr8PFI͖xrJXztf6`]{$'KkW#z!V/+.}W„rA_e@*dus u"Oо%Hrt@.Y=ҹ)l*U~f&c4*}. k戱faT@ g/҉F}'#r3`:sz2c+k):cG*ǕC1˒8#I2H~!96+euτoF|EV~oY7{6nReg4 h4_ͱ !\LR?i%+WTEˆnuT.^NbTA,6nE$Uo5k6#A񸾢LrdjiLIR|Iz]ĝWWo@N3pׁΘ9[>BRH.T_bcwUtĂA$D7P/s{1?gęMag@$!=RL QMS^}A2FcrD}H= L"a2Gq~xQrr81~qV8h( oh(T3pS"sf5E1bń{//I1@W?g L|gH#tq&٣Ҁ+75WnNN_*ȦC]sgT1q$S҇g{L6>>_]Oo{ԾNo6.riVLf nױ֚ u3&ʷH YIy-y2e|ҊQ̱ƥۯQt9Ʃ(N祊p ' zxY{q XøDJȥJŮmnY4,|,QgnƃS$;~!ܚICė!J٫EK{kCMԏkukx=cviY sLr:8^8q:gT71gGz 'U%4ɐLѨ/I A!>ZFCѷcAd>#&8-EJ89)!VңOyybr@5''2ț"N2%aa S`)A3],Cӯ&gq4Oق:CZ 2.r R>'8 ( qrZiM},孊z:ˡT(c5hX]j۲k:NUoI!:J N5)psfpfpԵy2H/d17hA󄵲Zbk~}N}Zi cˊiṷv }>M\J7V"ļL()jʜR\SáLy픑^f3xڈcCO}膄>K/S C\q@{V㛇fnDfBj?1A%I٢X22f[XC׆iv[ 3rI=daW\t7$cit߃%rm_,+L;2#d6͒ڂ?ia(&jUWO)Fm/IjJIQeN1*Xg~v@,&s 9bcE qbLɬa;cOcq3ׅb|TV(?n3%wDzҸ{i =Pey򹾄 "vĿa5K! - -WnfcfqqasH\&H>qtՌtȬ=R RIFY)qYmk}pJ4jɦ;Jb{!;˲e:0U=+u>ŊwXa Q5ίҺJAڧΘ ӓmU 6M 4IC<?2J5 vȄv7Y Ir>ir.k0LjNYƤ.~-}o:x4*l͐AU}Z T,gHf ֥g<"Ϡ$; H&kzn2l]d&dxn}`_,PMS}BwmZ#Ei#&M@# % ZylA'v^Ѝ`՝@-&oҜ2:ZQ}C +%TP h uE mcUyvDf1X{c7ܹ1v|򔅭PFpFNC{QKM'v+}q0TsmMM)Uge*ddATW"ϊ}Wr8# Y0&-V&}ґvU5-RSg%XN26&3aHUw{ɻ^pɥl0nEV9U}FbRY >^ᅳa*QPh_>EƨnIjN c!&Ї$j7>&L3x.U*0zzVʪp ?46+F{ /R g+TKI2x&AҿjY,K/RD0׮&8f$7^hVXkd6w$(sE Q6`[3m(Rp9+cNd,f?-ũPq&|<5b©Z1 ?^Mw8b:˫C}(] &`T@Щ/UMH6\c,5 %5R= 1Ӎ+t$mR*7Kx!&նWlg#MnSP¯p$)9Y}"Aߟ{Rg{ny7cIfSHiZDGN! fC7,Tmɞp)cͭ]4ȃ:!^xөk] j?܃o6j{+c]Η6 b|4 @$ 8xP122k՝2d~\ߢpFtY@=^a9#5MHJr:k>r:}qI+K3,b'߉5y%y3!@4 +:4ÒZ#dazj}ޜhI|]:"|ܯL *f1OHw<¿7'Re98TO3&a!ƞg)آ#'r(il3%:Z>hhޣ*emN&GEvŀc=wS7?P,THh{O3zYW'-3٩*jF)o7|2MJu<Ygcr(+^1IlrwhCjT(X͞#a9P`}wvH:8yVQQf*+!h*ګ'уp #Rяl|xpV9:(?un{O }bS;GWe<h$ij*M<ҘZlH_1pkQFdvԃ˟^dXpu?;μH)Ď O:dũ-bP.ɈNm9l΀6$Qҕ:EMHoi2W&>]3lUCM_حH>97̠4k0\Z^urUk>0]qMѥo-D}f7p!,qwݣՆP^}('-ۋHHR&iN :Zr]kvWecZQ}$SCuXz|;Vz-I>'$-iTl|þ%]>va.Stgn>| H|yv"uQj#W' MuSˀOUTZp%`~H1d):;CQ/tj׋b@Я{Y1L^z|;S(Мo]6Cp_+Y6h&_L[ga\`P0%bw2H>Fד 8,\'+Z> 䆟Gǎ&q[V^3?- u^.&+=tƺ][8[ ߻4bz4&}$!yYPb ѧXmJG=m [ܶhm)ɿ5\r?+5L@c3"QJ[tigC] a\{zGL!;QӠTbTmírý ^T1K-S\QPTl,q?:PSծ ~>xC#! O1L'=AE sMoalxC/$goT_GF_w kTl^:?sLq[c@#=-EMq<ݛjxbNz-Q "Ylʂ/psbN11fƆ6eiKĂ9;aԁҰOG/c} {!b3]Y iE*"TᖓsZ  fF7 *ho.!7eikaHvȞ Қ}$$Jwȉ 9z:y:=t}d)v'lX;I:iڨDSpJ&;}稻1JAC!A@)F9@ MBPRHMf2. UL|7o,n̔;;`]( wc$۪[Qygx , +g)OQjl/b!GNTBo@7)1 OoThB5B>fuY4d+ vex A'Vs AYphׁ.beࠛ3 nM7xwm@ZC %2+.biH1wkޛ2+Qq8 M"b$AX>QSqdPAZ@T7lT/C_Zee̮+$aے!֛6m[VNc7kdlwRo1686B8$ h4l}SvfXzim4<^BSgz YiB9t'ޱVKi*aXAZP څ@c q ;X5 u DeLc'j,obDkq5<; ҫ4R&8nQOr/\&{Vq,'=쓇;Js/!W n۹כ..RƉH׻ Z '2`]|Zfd)rNTx9QCJpz>.Z7Ч. 2u6"nĴQ:F9nakYte,mMA,7 Qm\)^y)񭢜ZIVr%D U+ЪEH'vu) 7)&FG8ۏ0<7z Y$736UE`b%B|9%A,Tti>M*go])UBE@9.~"dw]iWDV SBE}.OiŲP;D|O#HS<:Sߣ`-e^^=13^Ꞔ:f:F ؊y A{Ū_ @$|gƦ^ص t4 J#S܀ˮr2SGreM& obx^ETϣ:)"32/˘G`RZq aᆴ9N`+1UX _`K'j:8/LdsvʺBgfٟO٢oz{Q»Y72∴Įʮ*j͘jHIiJڰEpĖ K/%Dм~&wBt47p4o>VN[o'|-.8M%Jd-8ixjd'⃙YY_[mxO$#cno О9,<GUQv{Jk'1]xh.J(fQ`,hgkriM+:QItTN'I/g .M%N\J b< ws'DO8MF=5*|?5eG}InQ4f E=UI/բ83_y|~<ߞ/=4f#/uRf2A_/dBO?TEFհ }Ұoda~>?V.{_\c|%>Q#ޮvxǚ,~WQ~~^ T؞W{-+Ez4'J޿cyG&lR)Ac̐ɄV6g_i>Q)/;u %TN3Ў3uu}!gQeԝ̨~8k}t^P_Eм"b4}. >6w@qkGY)rz1sOF|ki"Oz+u` ,2XbXx%mlT>ˆW|g/ &ׇZy[4h M9Y.鼰^911pIG-8&4֠y3SpL^u/Ô_ ?Ii}OpԎT / ߔMQD4 Y^D4BfYoA!<l `ʿ={"@j*o uBh_$_G\DQ@"~mo4F s<ňE(0n$㵞ihKk[h0M.:WUYI3Agwl"Ī:U;t_@{A ґF BxB>O!LCH>vq ;i0F: 3:JM :Ј9nZ|CLa: ;4|\#{OMFҩ/B Ȭy%[Q#`)C0-E -9y#ޠ~a@z^ 9*쓘1!R|^L4 ?lI0@q&s`Jx~I`CIٴRe!#~w4rCJ3(UbYO' Y논&h3YyDհ([]^3‚ cuާFuk c|cvywc[- B;J#旡3"OxƸ|nއ)nƆ?XmggSS=s[sFne;6UVu idS2iD&ːڜodܦƚǦ*p6,GzhH㘬 T8M?"qSۨ;|ׄ7Ì!`~ J`mX_BշWB@N <$~^zDyUvăCuBJ˰]>ky/ԍ lyHuyqKdm8O Re1V>(NgO܅͗X+i,og1sYxOHDOAmE;X_=RgT8 |}e')+x" "DA+n!΋xZ b_L>&'5\Fq) fU ȾsJʡKOV㠋إFSD.q/lme(14f6fIXK@BxwJg;+<oHE. n_\uς_`=Nuhj|]YEͼQ9(sGwkSXՓ҈df}h^-tVthM/ zhHa[TBˡ AUhii-4M[~sԇSށ֊VjT'83!V!ƬY,2uR${77ӒEjA_ fc*r-><+o>>b.-'MTwM볳N ǿy+.d8{.ֆ\[~e6[7p7(#'`5:"A^$poC5zfqLd$ϙ`fA43HvA fZ.5|}89f\ nqpA s97!:烰ʏ@t#:4L,^+%:랹ڀj_ĽvB~5raq})P_f^K">>}-KvMftc1@𛍀ڒNam ݍwtf <3Yw!dY(~d@ lO,<ѩ{"% S"E ue%&fաǍzM@DN Bݵ=7+RsO90r>`ጲ&e[e¹tCX[>m6{3}E@5]jCt$0OpCGȇd .y2U|Kd a-;9B4ZQXMjyz====⽭$Ag=*?em ӣݗy75 0#}H la,׽˞TL*zۃy3}?}*,%а@E7*W-'(j~Q>s} j1r3y^`< aP9DKO>e")c<8cAO|Y*DCd[Q1A9MK޷!&wr4ls̥Jj$~.Fqb |d6sVXY Xժ*Td~L.ez\l Z!F@y EԨ#ep)i>lqiCƙFye *|t lﳍjгI5n#UInI'tNٟ@r g+leNhIX5{#Sk ПRMfAW2NÜAM6w`0EO88khAˊ(JeYD踽غ6uVr<,)k/Pwdc"Uq7chT& ( xaLj/ sMEZ*],Z/pIYН.(TF 3Ѧc'6xUO^M:R`hl b g|D7ac4a=%.[SHñ^BS8,mvzE셭ȵa5RU3t0Q퉳0C[}1ږ^iw8zW'sMYr:+4p#z.mB~qD bRV7Me1}ǚmH _VxQfxֺ8T.|kނF5o9ƪ^X<γF B/T$A_xܟJ5(;-sHj !-R^de9cO**'=HOKEIor+$f_&eW{ElØqaeIz)"YN?I,Ct`UWlHM/%X JdL#sjv`NzhV1:MR?H6bB(:8s%\cyQUdp`Вp&c" f]` Fe("|u8q`P"'YO7lg-3jm:K;7*$D(qMݯe8 tiYvN  AZI} J9|Kxf ȹH}#bFu}?3q*O –#R"m^mi&'G?`lzw\2(Z&<~$0f7hT#7z6/܋6v p62 DE+J`pިȗ|qX\,[InyyZi{- CCnCdG%@ƪrW1Px8\>Eݙ=fPn *pA#XXMҿ2t,#'*'RE}&LI ]AtVr)oD9*-++,.Iw;!GAƖi,|ֵۄVmPq$%ͱZ+/ ›񋠬Tv Dz'~hF3RbZZH輟ڊT68!tXaxT uJ4?S2p |8NI>b^Sr#ڰ'_rZ.-ܮ!|^[Cbɕy=3.U3  _wA,ܧQ1ጴzM<]wY*3,9ByY/1I0@B>u!Y X!RW,M+?7:ROQHa.2<ԚiB4N5h{WsR=5>OsBIr+Ո[r_}:me\`9ƫ9ju&=YcEO)!HuãwHP6%ߣ;2 =~Sy:($y\H 5RM6좪sfطfлIૺP1 J8h-ԯ]HL0g?y x.%BᘶLwzq".rPtɰ WST*Sʣ&ζO9<&Ye7晰4~-J7G<ųp1Jv*o[Z-slZV0(B2Sܶm&vJӘW]1Y=UHF(,+Nk;!`;?j,-l>I- -UM L 1G`gEf>qBf6jPʧ0T]9BVv#ŋ^{0V/XJT6?F.+9KC8j[T0e_랆QsODqKCɍѝ3d06џhÅѾ5Jtbfb+:ٱ=h/x 0ԲZ JBu D<`f 1WUMFYr % hfQps**"6pv–"K}ܻ.(vu qF!VT,Q[k.?&Y'P2sD"7Wn1G-a,8˂5Dc-i"٢вO'Iw(=JZX8""ߓ A2:t\Xzex.dWC_|:G)fßn3PQt@ 8^$}]K*.{ŏ](xwuu c> EPkJykX+ fBA5(C1r%ȖkiQG7ǹn"d HM&A{_֛ZR*u*169# 3#\<8HLkNO{H`k|DGY*Pca~)PEk6\PyFV8'w +sC/FPdprQx?߻$r-8DͿ^8)!hLo){^iQO7hW+ 9Ԏܒrڳ Zl{ |ʊ[|Gՠ'&6@0M`XLO"0'w~&( ]6m5GOv.:7,4;kثYʼn/η^jпٸzN'9s]D 2@_$2צ>je :\V7~"k`Ϛ]6*Q?VQp[{ }طc U$KvKGw8 DB2HiĊ"\BBNp >Ҳ[M=L L:8+W;c}m]Pfxv?Bhbc[ڜn zk|Y3܊LmVn.3f~%<b@NMr雿ٳöb*{39Nv=} ӏPɂ5:ϗkRd^[N򜴙1e}Q)ŭ3wu[64. rv&? ņ.!HoA==j ,& fգi72Gd% Zxl{ƀcE/<5ҭHKu$Á3ϝ'Juș(5,x eNAt5hmPdވ+f$@FSW0Jgόxŧڰk 5c/qKu :ݛQV<M?3{k!`WBV*5;4/PxlĂHNe[-HSܑrei5P!UDa-Z бnb{qAױ+A{q#mU-ư ^ _aխc q |ys+,9HZo9"+ީۍ>j7Y(GJgXMmf:?څ8h($FK=زwi6b$M^l挞DЀw7àڑ3%Mq JP2u*Il;N6 #;^gBLP MU bs#6h쁚XS!Oz(&{R-#XZOu%-;U+%h|#0nfxM!TR:iH=~Lt6"#[nnV6% pQf&#&f,iD"SM顜eZQ/X^4qA0,/DWҞp XFYK}2gMpQCi϶2Vߢ.`jƣ+ S" j]TsJjcRO-V^RM8n?6aQM?"PO"oAb^.PjTED BPS^=Q0:.Zo T)iXEht_ku=Մ(|f^tUjqP G%vYKKw1<RXQ-~UP u\Y TV~xn34TMCpi@!r编|\cyOV{c$ 90 0}E5QG zpYl9q)5VJ &k19O#YzwV9qEr8e@lue)sP+N|c!574b, mB-lY'l6P`'f|eݹ,HٗjMfYN ­>LHU p<3E4)mJ%j*\Vuk7 edZ@{dI 9p>h +$= q#Pv&7-ALZ[RK_Gu3Tr m b&Dট'h{_WUoE[jr~B*]T{!ZR ] dwg"Pf&!zJanlnDvo⫦:+7vJ6 3jjKY*B ԦpTmN]K]5ϖ%i(Qefx_$;4,҇ sUwm>.}+MfL%`#Z/'9wBd!X1Hm֝Ip4RDA:5('4.ڊd1 pY=V#+՜":PpQ8$t[>PRB*/ |$90}_ƿTU&*Rqɝm. }b]du\OU`E>ϋ@!JrrdRF-途94G" JGC'$>$URatYp@CԿ +|y{ն)/}$ Je3 Tj[Oi߲W-!B!b9qhWV)&]$" ZaX~C)lN(5\ E"]#))7If&.e­}&0uב)ͱٔ#nܾ֥vtpy0#VFyt({b; fH|73pIJ8GB&&ک'cC {v%arO|LzKi_TK<#<\ɕnCU(l|7ׇߚ\T_^Oq?~2<9n_97IuR6#axRB~ÕpEvwb| nDDkgT2fΈln_`>V#֓X<'vKne%+ZiY/»lIy<" @dZT:*eh9IڄIɜp-ooN[%)XԾL6xUsFրL\޾}2US틁ql,u'3HX},@B{>U3! NF`>wEоwwxĤ?1'`SNhc!]opx?aNQ֪O'_Є/d0 CPZnDgF,Rk焑J%w=$3wM9 [ *`+\ha.eoج?YMWJ2ǝd!=0MY|MCa@vzH!#_9xBz/2j v"\$5R|yuQ׵S,FSu~:;(󸯺A^Bﶁq?eV;ܚE 8FDòw88YY8Ҷ2X:D[օ~襞wc+ث5OIMZsJdTB쪓!M[R8Pש 2 IfzݦOWF՞}q:}vZ;~ xD^?3kƾi~Òi^gV7(3+[ʅcj UiHWX_XHU !SzptE uT37#w=Xcbi E55,0Ufs_{2 [ju%`p>m覙} 7_*|1!b7Ś4>f-}מSjL)nI@n[':ڙV$Xo_ 8擀|s}&BU*ޢaefosZtg 3XEʃ4 /ė}O&\&t9]&̎(ILWK}{aT+x*J*9p#H.^RQ.I%Af~yW,gԏ3(I ?7a0oaOBOJ✑D`c fp.%gfw hrFHMGmSDM/WUȟ.E 2F "]d[$OJ3U(0&"\ot "RG=ϡ\28ySwc/`xhJ:0^HD} V۟642-B1R/'T aF}hG.]?}_nkTKUf&U :zd捲}Gc͚^R?~quޚ C7 mK=b/ě_`'G !L! LżH(qwhʑ#+zxvՅFC o#ü ={7|$l5L?񠣷%KIcfGO>oM_8LL2_[͒W~lF!X.7iX_ 3@11:*U48Pʨm лHq\nEp`_ђ 5E4I|uGp=חͧKK[vX9:l'Q}_bK{aoTe7G4,o9TjϺE_܁hP~jHat?i񦵽OH|.&S |E,fQONkjXg%rB!b? #E @ꂺw\{Kd,2K^fJ".psH Ӆ _#F B6y8'ip–IY~TоX`8%dY!QB7lIhFG\U\,Nr,s#!xz~؎.,.ljOqi,JE02[t=$J)VUyPfS655ifFu\syk6:kXZCgL~ x22evn'$ON;B@1@n2=SQꦯZ ñ(U!ϿEc *9eYtu|c~L'Hr2tIۭB0K3{ ($U^`QD]cD9iLA] ͪTq O˧L_ŭKl|S‰lK @RLJ  %R^kbπ ςݸʴc6\GGyBe{#0P!Bme\5{xC1?C vB}#iO/Jo&"y*detK4hw-x!2GNCt{~慨HCGhvr]G ůW`Yzmh,A`A6 5&jQ!j nLbΣUScƫ-$uL$*Dgp'b]ϵM0 HTiIJYs,LD9X{E0ۗ$B@V.m 9ݪYW+UTz(Z ) D\Qk'p'꒞!߹Z]̘_ Ė}p\Σt7ĆLJ$&0nY0h?:~h103h~}}v0{Fv]v}>w%VnT:1*d&+:_%8& _ 7<&)(e[6 !pTůX fcBhm&rW!]Lv1ZRCA;A~_7Bo'EA\ZNY9$l*1QWʥ"CJQTq/)҅P~]!ÂVcEˈ!h@0DHESDqk<*M^kg7rMS܄,Tw=~xK 69 ] w(O`2(]{hZÂF>gS|*%{0J|Ahh7`2_fc kw Xޚt ڔkT8\:YXDXX7L -Lr T]XZp= ?m^AtsIEY(0dz$@"д(*<4B>{=YXya%nF<"/T{{^Ve DU~m8j4v/pH Y`(΂f,=䄐N~QN>`dIr'd$8{& "s;#eZS{ qFkttYD1}(I@ )'l[80)v?dHm7ur>aC~EUxDe \j,+@*wxYZp~O81LS{Q>w6. ^ǽ C"WfP &52f8nY93wFZ:tiTT#㈳KlNΌYm;p(,!ngh(Mq~WMxucTA[P!Hލ|[҈=!@iCi!lM̻0 6p=mk9DY Ⱥ_Bjh^n>sW!K+al.=!bS}sX$ϸбn S1XLf|(HH?mk9J>qf}#g/}p7)m1s\Ƃ&?[Z4x8BvML`/I{WwY3[ V6qQ~ުOa-0;X[~} ܲAM4*ؤ`b(M +6m+@&B%ZŘK9K cs٫c%c+J=3i 6<ȵ;7'̺9R|Z#V(c318s>.[|V%w3;e)i풁 SƄ^hKIZ~ٞx3ۚΰ*MHOſi.x&[ȟPԦ]nQM?&wjD$Sqw9lY%FŖSq%zvy|GkWg$XBx(IR+\:_[q۾O{~AEVa-^V8l- _H(頍b"(|!1Ү޼Dm.ZnAђ.h}H"w_ϐ@ `Lu޾}gwl%\_+%f6?,~lw,Azع۲Cry\3)9eDi%s={}#? 46~{gSTc;%O|@ >rU4c&2Q+Un(Rz:SPO9PƟ ؃HӥjHA7-djWo۱^6r {B7:\y7RyM/ =p5J܎lK$zomk~wN:?[TTQuT)I~$xڐeH:Lz&rbJivO ,=|a"ǛN!\MfX@(65z LdgCmtS~QQR)i2ڶ9ll5Bvnh ͢逥A2Qq#|.=et22q]Y ZL{ӷ;a< Ƈ,eD@$z'CAS`_v/eIw+NIiz \!   <+fsѡ͊~! ; r)|JW w$ r)‡GaJ$smg WP`I'd, &U\<*Eq|?QL?B{mWTKBi\5ojke5!8Jjx.⺆$}ܳ?|~ xɨ:Z9 [5v_FK`Tpa7|=}_K%cȥD,+q]BCوߠ[Hp$F/J(rnGAT)1 XD,ɗRtKze`JdX*韱C!ffG3uI-iֺ)\@5r$`Y95SsdAyO/Ż0vׇ43XR :[+LȖ(K?raʔ"} [K:**p!Lr4}O&7u NM38Q4GϘHk"8)65ܬ'%2"!w#SVZJw[ w4^N\^.J mo[ѻ]tt=k$l!%]%}ҩ]+o*8CLV[mM7:(t|ⷝ@VF#,9!]]NG_v~gj.,vV,WDžNeP%=2Pl=O>_68HCfzVwU> E%NSnhtJo崐[-&MąU)f?n)p] C4]qݜ]zt M >V*Qg|!k;ɓK&Ӊ *Ji[4)۝D$ZLOt.= eцYX$oVvq|MQ?WqT{s_s-M599X  I \rp)Mzoܰԃ>p{ŇX[IyAx1}W G_ ;ٹ8zYנqC!;[/٣IAR!(;v{8h}J<^\Ȃǹ(o }ѝF쯰jS 3 Vxx̯ O0£w+qDo)ya{Nt`h J<|DJQ_Kv z)yt(dઔx7p–`m:TrMIÀrښp`w,IȼqJdNҎ|$u^2ѥ§`%8IzA!uJ^zZ$azxDT k.mضkȝ+ر ,qtuRE|V2 kU84e,h%.h0Z_c 46sɲ] Ѣ,=`*`ylCeqLN#պ%A#o۲.+=S&Š;\+6ýkm|CDzآdda6J9j:CGEEE?EICyIFu o9>غ5'kQnBz2O{G֯iŧiA߆lAAzi8#g[3fAD麯1u7#iJ."D-aOVl ! Mo/޵7'1CgGsl½ ^LQxLU$d(@mF|cqXzuBکX tTёoV.m)CdNӷiTZtދ%JCB,1'l'g<'B3|; o7ͪ_!_Ϗ#_٠ky+O9[Spir`0@N0uֳc T|1$1\gH(Ȭ[^=hZ]*#Hle AOv/5Hi^ebMyeO5hN]Y-W Eڈ{د,|70>>OUpt0㨅EeFa>+zqYH0v[ipue&+ۗEͫ6P 2P-dX̶%>e[[E2Jg8')V똧r]豒?Nr l*g.+褾Sҥ7 CuXePGE ښQrm(vCQ9L_ȝ[[T%Xo@JHPT_+q Dg,&.mkȱ*N.i$L'b@.zg[܌iw(ho h ӑ[ZX @%] yZl4?=gMx=>Xk/jZ?fԈx?A8W;So'Fj懤'^5qKwmDȸv㪯/Jk+;/ekĐL&֋Q]?3Ճ3ffl:m9`Yςy@[";E8Gd0OT99N98I*K=~Uh WLk@d \Vg.D 2C[ve o~˛6h/Gxچ HB`q, J 0N"0PH D "xS+MOh_V`RhBzJpьkMTCduda儢cVm԰/i ] M`ga\&}%KzA J7ހJ˱7#D!rhU!dkw ueUSj1Q,"^:t&ID\`N#J\h o.Ǖ9Vع5bc6VTƐn34d`5שjJ(v"OC\ WK)Q!v O~T鿈_i:k}C>[|D$jՆ$rsXJ;-oTE NҬCqY(8E"opQ<x^1gh_65m!tpG-S=ԜVR/g/ F/)zT=_;&N-tG0в¼*QqDYSjRׯ!LGzTɯ|:nl]Yrjj<*O0wF[*? xuKc~g!)@wyl#ds=-bZʠ}qExj#G9L&;-~/Vfm&2,3R*$o&(%X¶_.eWXkD_8~Ffv;@14i+{H- [{PPp?rJMkx o):),W<r};;+}kv>>xg:J&(}>=H/\b݀~#:s]72c|[{lن>P [gn?#=5rě;Yl], 'L׮oM 5*bHFIqcS֡kAW֤ʘO۵=gf.`mw_S_~oQlDֱޮtP_q!3 4B2i/ZxUXhw LƂdw ̛kյ;s2E(; b2Ƣ#p[ڶ+|΀tE⽛Dh21@yzi,c:0JDV4YipoT Hk (֧3M`\%$GJzyk HM֘_0Hıĸ9 V8H#,hxF恃U:ē K|ԃ)2"@d,Q\-۳x߸Qtp@eR|Ɠ ͡d}1utu㋖܎ˊ܀xc! !aGdk!f&>Qqzc1-<3&* 9ftrzrSW#e_APڌ#s; 1r!3*c8@2j _I/jONj2FaxsM57tk"H$<"ѭ{kn"HK*ׂk}b7Mn|zɚx#;3KJK;|ֆKR+z!>}@@d.sr|D^],sf5Ԫ\l7пjW4H.GL^oM^ Rdh}@6F ]D+'CԄNdR\%ĦP SM Q ;?kV:j'ׁ=;zȪKZZ_~$W4 /:"Q<#204#?Uqr(;dD ξޞ6U5F>7!~xe?ok\=P\ (ΐ3/gO#BN1q TIX;ªpT,*tW RST6̧)y" 4!HT%J`% ɑ,cFa%R”q'o%)]p>X#o CG |owW-s E2;*c72ƭ#)7i.  zaOB>qF0%xk_k?D]4 hpd{FjRUIEHj; }^v$P XAO[.*{^:vx.'tqs>f/4_K+>8mF#I_cD\lpp#m/4`?X20uwKB5zVz-#oRg\/ÔzOhJ!вLP P&}oH-dWi%W rrUkDfqNv|p,薸#a>j^c.${)7$"eG z+NH0B3)pϩT mvGtej9MUr<~KG#bOǰV~#(/Ĺ}5d(=h<ԔUyrQR`L]Ax2^BqRp}[r LJgO=ƪLG{vrk=n#duOkD9{+$c%=:ggk%D}hҍE/zSES]>fA[z<'T<=Η6:w]kG}t6}C]FXT@N'Mj N8= {·7=C&ls ^c>B%EJ ^#oo*(B'h\&0s[R@J}(au,ی"P( ȿα%Xe '[#"QP" tO{jG ?|?djKQY.|UGJ:Q-Wz |46VT-pЯ~֞yڪ5|pQ/Yy;^xP78 ;1Uc|ugpL[N9b5/^S ԵU0vQ#؁QFx{^+--^=fjha7I,$@o}yT]SEVs" cD #o,A9=`eoZW0bN/QE4QAL+@V5䬐,զ2GJEʭ_vmXބk?bPzsG,ǹ={n x ?w'sEI;3,[H[W9ؗɨ[p ͆&X~ns:t#ԐUK`>)E`[p}#Fs|Z䀇[h {}h=R>O@a#>hZ,s`؅FS;Eq=MR!y2l6TBfB+33lGX)6R2Bsݥs-sإjMk)a#"nJIm~3P:(@7"5k["6/T#wPF1⢚hkkzqgINi'©Nv0F/ѵ!i)w5WQ&QOg{ 8V4*YmnFұL3g~8~êjIp9/|S5l I"$idXТ||3^yAF$S%ž{q/q6D|3ڪAD)yg|TEiA|{ ӌT|D4I0vZ,sUkr D+U|픅{ ǵϰJ0"Y%rvF6C9An<ƿrE*z#>|9b/Ϯ(q>ꕈPW4;nH^5&4^qGE \.ʕV`lbz͜hňm:&O݊^1bMPώU*-p J]Nw\JqU$i0"*ei~CDگ?W3zñS]9HٱT:{tsb;em ;27["“7 :H{cŷ<F>]|,|c*W T}vCCO%|6h=XA-"_Ey zN`CmQ[ҋt{p†sUM@brj gFZjeFEI<<mjW m%Q?eoRXnW*`t@E"v8nN}ѥp i4LÜp@&գpF]SΤ  殈JҽG6˧69pIG v(0Щ[9K<7Pmmp ɘ:i3 Q'q?#*R՘;*U K/''7mʬ^VH_R[Tm"Wcԯ Ѥ*W=c S+ Z OBo3YEAouJ4ѣwnc'A 2&db}C9',V!y< j.@}LeOs$Yi@hL!z2Ma9J͹Z_<˃ĀU{iLytU2YS"s+fc&U>c^y0M5?L~m,$M_O9pq&kؤ\brJIeOcؿ16Ԧl/; 8ε2nialq]yօ*CN8e 3?.ۡIEک QRjIWaݜٜ9[~V{?.o,0-_FgQEڗM+k޾|55a,Q7$ Ay_ugPR)gܴpkհmb&Rƽ߶~]efc}`wAx|Bxj~־Hy d V5~[験zp/AWZ] d؏I廱Fvv.y /V\)ܨф2~{vvOA6xA`GW)@L3(,Š6, W!`h1M[AB,%m'S2'庁/[^F: ȵׄHAOQu}v/im~(/s 6A#+c,Gԝvj=wn R -Dy\i+W1 g1SQ}^ )m&gyS {i?]+78a|1/h Y31Դ/Q6e d u6yT3YW*"1AHg񍝸@fgXĩͽi +g:hMA\3Z׏!>"c̚Fm"LS)Ik0A?\oA+LkL~ybU\:'7<clv\(a"[ %a3'q+n 5IĴ#s|@,IXcg]|t98EsXnOUA%7 wPHquJ;ց Y4iPLJaGW׮xpΠr;7H./N:}hek\dCb"Ic|s'2Cu9pX-(}Tޤ$z#N-?|sk tuoI~TV L$]2Bq0pؽyt^ |ܧEm(jv3v:BnpS*RܝƃC̷mUo Ni(@Si*+qkj*2t\SjG_B A1iKHjI}W6U,0-z"cAx߳YWCr__ jM >4 5;w{G!J %kdRq) l:곒 ت8[4uG$dB~pE:ټ~G^$Vv+5O'x>8^:Yf7<`⪈V UHjsdZ uJU~V~[1]SDQ"*xmRf5G#TZNh!?4 z]+H:\FN/r+hVƱgfbqdËI,sf]HMƆj:l1rkkDixFBP$++OV" VQSG2qL[ڶ`pȭ;Q]86Zku@9Uվ $i+Lg褗8Cq iK()09l~G^$EXrf'< :Yr6FIԘ(eaj)5 kB+ѰQ@ɭ4vR⻶m,ֻ ي  A|m8P]i"_G{w.bɋǟD2h+ Rn0HPvZ C $)v{DMz95HT@r\U/=nߐVk)Q- +?% m >'v#]~Μ^CO?z{r+{~F`gr_(u :}f3/#6mN1:^_ ]X+3uI~ů+0h(τ2Bڪ/@x-t=<kJiHuiR #O8AEYo2--l8q%q0I(rH 1½!p,_-RWE9^:!bC=]C[?Bb 􈹮IIG_ڐ} [Tr UR+H6ևzHM*X6 S]>x:[?I2#* xW$~kBzWDhXV<MIP`RHB> }@P˰y)FL̦m َw,ɗH(=Iއ>'3=S9r_8=T~& 7/V&zY,AJހ4w|sLIg-kovrY=+’TIUS){rv9Ip/Z`gZ%~F*WݡPW,dH1}c({)fmأ %X)NDU_v_gr-0$FN_f6mn \/ST5g?-tF ܨ=60ZxHj] xpAFW)b@sʓTf%ǰliY'@v=Pºln IVB*ܳd ofhnGNѡ 8coRkS5l,+tqNg)BKx)g$5!22m* [s[` OM ֓:.,c h03߼r-Dtֵ?!H) 26{=uÒ> ]]k? 4R)"Lvlkf_ZP ;Ga44+ޠ!L7_VuP2.d7Wcl]ְKO|I Nɞ!AQ=He|3T{ =%YSݽ}#j:iCy$dU}=vϘE TejyM `4k)rْ1vdKM߸ksWnGl.DD"ۙM<]1k;YLĿP.YK;0k˗9JM!ܻ `QQ}B•ό[lI: ;mQL{Ӑ#,'ȕk?) 3PDIj $7F w *+۵ 0EKc q*&1#i2U+ W+:?b(lcZ>ANS҅W,%7l8dNq`)6Hd+XKM[&CSP2evqY%qi҄dC՘_W/\1{5Bxb6'gPŋIcz 08y`>[6I" F { uV/6$i^='{"3]/7:84c\.5 K:7Ŕ `oM MQpuCt2r *@% E0@A y7̛LQAFicKQ>g\ icD(߸1j2of1t@ٓ7z̐Xd!ⵛn%3_GXQ],Reo4 ΠКliX}9b>oj~cY#ض͹Շ3/ܬZX'MCi$_ zO1'^[~6w1cL22}ϧI7~[h$׫vr&*m\!ϑ4M{Tթ>)\` :.Q^yrZ ҟWwWkl'kGu пgu.'k#VNvIlE _io d?Z`rG,x;7I: ֈ āj :=|59.UGFkXN<,Ugߒ(UbCLv1s[N-jX#eWx_u4iǟX`WGSw4HCfaE_Kip#o Xa ?9>ʱO$f:s$5o$5uazx3WCrzXN{&N_'VMKNڬ _o kZۿ.+iiȾu5]?):y_}f?42,z_\?^n7A~pκZ`YX>1>> ! !1-?Yo LLllL L13gG'}[[?jRA{{ P4\GE049ynE?'`3Y4k fj^̋ i/.ihv'{'ZL&_Vki3zY:ntS4H* ՠC=G?6)DOjbl\ 1C@ob6M,LtBT <OyjxV^H 7g.2a ݾK#!A+-QC8n`@እ(ZaWlyN b` T$L~yNzɖ"$ZM[l-!6$ѡM+3c)g;Zyz3زXsAmT[ (@τ9"*ck<Ю qܨ+ V w*K?qm x6% тCiAx I#JaAT}RO_A DSRHRI r31'< q4,h(GW=x_S2бj,Q?,4D KX;ytH RNiBi:+3׶wť1pcG)3W X*5!;ᕠnJ%yw( ܈`7$1S>ksVRX9^V4Ua3EϏ F5J{OwXp&F8+,{ٓ6?kbl}tӗn[j6F7ϭ b eIym@Yg?+Κ(=vq] 4bY\FG3p &hrr#.~?}J곃e @dnYHԟe!RܧLv'Oj^vi9>+>3FN=?g,T :dL͆{7u,.< =D3.DRhá+36޸4~+Z;foLpPmLS4#RZ>%%i6*e0jT WPP -x {Inrp[BͩعE;ԏ51~M,?aƞ1+ GY8Ƕhb0jol溧l`5Mu1v ӎ \B5 //=kX3~pH&jw߯qw#}S&hݷ[D<99`}=EX Qj/ bB\F[ud,R˫ܡz҂0QV&v[`:qwov]):*ܨk5]\2VSUJ"Cyz0g*O^ ݢ- iD94Hc6}Oe l8H{-x}Mg?g[BMP9~}d'&wǬB8`;Y\JO |RI H/]VֻvD8X_KJg.`ph)"9N Ϫs崷c:HPגg5llj֊+#Ec/T:/uM}BGCJjh}@S'ӄ(niWO[)e͌ _` P|霌(z%箊Rh;Uwjtn웓z 4'Uzvbi_t3O@(1EH|W ɔ;)z H{].ĸO {O̒! Av?L8:{. /4X܆Lλs|HxD_ xg@3p:ڵBO:aIoI_Sdr>VZL㟋ΦhޠоlCuAyt:h.i@JCLR$ui/|{eȨC/ȽVgH']aUL# /L(,hYMϻwrN1k!Zx`mHa5g9|OB ?H{ WlxWj7:`>4Wbc} ǬHb'O$c\)z&7g-dbxԼ撑 ^̪/FY뭭 I"G[9N.q61[, ({=[ɍGf(нQ72cUjE8P@l ]Jo)O`D9J'}JEp>CfJKl@鿯r7rADh-J %감Vgs3u'\:,[w8 SLZgqT o{ӈި\{ h\1JEQԊud'-z0vc;'Ic4N>:K1v;ӥIAmȤX 0z !B`d{B1Q8h) 9Wa츑GZ ]:>GĢNw:s%WIfbÁ>B-`&@}:> L т[o-&XAEZ&8x {iJifp+f@_AP22i_g{n`xsa: wp6nex|s^RE%UHa|ȟ9/~K6H(.GZ-}CːҦme&/aL{>YD4 ۦ =HizGwZ-͚Z=SOHj:x[ 7GI'}$VzDxX%U66ş #iH!Z^۪&ZfI_xf] u<CAZvrTWrYXxx׬gj݄'4v܏֌T(z5S.FiW-p7`m. %K M j;=a]іC lP%*lL70W`7t*$ 6fPL|s7FK; .xTQeoɻ6xGw"9z]0ʙd8Oop[S "Pr`06"CvvyUz09+(\ů~]Bw O9εʙzPUĈ)-ta`cC]y=oYNKEP2SI-3XcF^;H7!:٪"W7;UI&'k/Ŵ?I׺;4C$.0GjTZa'mNABbGSމlh'i/&RI4 oM;_&#U^ WFj޷U*uf]7s<rb1Q,~-ew6 lȖ@k3ƻR`C4j-5m]F X$@J&~A3T&Qw~3>"ȕU|gg[Y:@A|ɐJ@ d{7baFZ=*.[%Oa>WZ(u͆fD޴%XsK OY;~dVm/AxQMW%Ooj%eVD)ޤ cД\*u_좦by} O̤E}lnf$ԇ|KN&;ÜF`  h̥fdv:IS9; ٕp{c] "3@ ͧۗ:SHGzJ?ZH%kFʚ,2=l!6b$cv""LmQ1*v0F( /G(W#ѥsQ N[l rEk+9I\$0"L=`Yaɫ]p'.HĠ]*5;< nOjZwxe܆ 1*#O=QBUsrW{{3G$eغ3 Ëw]Իp%!H WUKISb5|lm]7`s+f?"~/%~ @ ]3Z6iz-Z9ܛ|vI;!0+6&[U_a܀v7Q̎A8;?,,,wAW1j(kIC PO#]un?s"]*Z(t4˯y_!&Bmp );(PlUj =i@2TIrzqéSrW )VKk7ȋ9=10_] $J 斓PӐR7LkY@:AkhY  pBݺ7q'$T,5F}G0^)P/;.馐g(&(}"] LC$9_Ze8ep_nݔN;T0>%3;T,iЃWdĩk] z#pQU~pU&LЬR LsSFGCʎvW|@4$۽kݔ ]!cat,8M;2k-4Ȋt0n8!`a" ӨQY: ? ϶h=A,GeFM q:t" 5OʚTKD >,rV v+CC6f Ӛy$5?hߓmWsVT7b򕐘~YFx8 $ܘ>V)n)""pPi+NmB=ifS%^k-r!iiόeY NBj 4c;gr ok_\`H/:RU!LBj! ?Ó\z655<3B 2GE rL;:C2eUHV{nF8@;/n9fŮ_.*zڸo(#tJqqN\jk*:܁-u^, wPfͱ,Jk\t tk/'Ϲ7*-tbf!2͒ubnc}qMq_8U ʟ@bdJgZb'XzTԍQXkrcr̍6eJ_V[,_}o=erfc ܚbMHN S +r iy>޿};eef#}X~O~r/6W9?$}+h =&߇j"pԻaQ J؄0 "~ux]ݤJPzJ$/xqچ9NP%PUA˹C; ZFS~fG.aEgŲ &GDɳhWىy6sd (apTJt$Xa18Zж[;Қp3[pf%H){ņiïpY{,ߝ qٴ&B@Wq4Tމ'\(tޭ[3_]J8Xi=Rh +: YP~k %T+%F5F{SMp^a/b0p2aKi`],a^30HsjZ{fK1wIXשmw)ѧx/U,QVP w5bmW)X}j, qSNG h9VؔG%C/D'W}yx:ϋX"N˩9IOWT[39#8G5ktf|VUl=iī^\D 0~`YOW([{Ra6T%!@ygMQ.H9}S﹊cT7|߯Fec4"*̦?ec*5I`v};>4 Gfą U{>$?  q03A!T1@S>@1|k%x͔: KCwN AW5 \Yj¤=/@z8|V)aEgN`7O6*lҌ:ilo]o/,XtWa1[adHS Bٛ`̋S*p1{Y,^0J<˱voVb[BBJ(uzT˓Vؐy]ۿ9Nj^Zhȸ(~̿Ǧ^g1aD+* eq0?+LB:&/V lQ`Y<O?XsxqQ2KN,`'G fFfVMf_cnXjɐjXלqG&!ClP-EM^`1%)!R~KU.[0rQJ `p5?bLvEx !ANUwٟ1싢s&JTTA>{FI|j6 YMM䰖+%CQiZG]`^GoTrɯS?wra1,n‡L0@黥e2lQ'1ѶO:W_Uye#vMKpV7Eң? ĴͿ#a9ܫ y.'gۮe5ThRLCZKw8ku/weFΑaW; 0TT0ZIȫ&^֑(r…Fo DxuR>p -`vo<"$M'@'*i=NmKn# #ܪU<Ơ1=bH#+4>5[n*h$p`u@x g)(fp)uOU2X@HTF"ȼBd4jcc\s,қ8QqAeFgIexTiݳLu!Jr)׭vq A77]*e~m\Um hD"čEGխ۵f\:"=ORQ ʽx Y*U3ilvi!i[$$*!b"/%N9dSwECDjPf344Q=jM{D'c)McK 2J[5<;6wla[F1Υ}viXOTv6p+;>ԃY feJRϻ;8.$o\@;̫ڌ+|%&ĐXHoR]DUqǙ_w|P,ㆡOZ00ax3/|l\{'΂4)3_ְyyɖ ^u,8qgX-Z;L*/v"pd0Z3 XZ/L;Û׽AnQ V0 Ⱦ3[dɸ4g3HP)1{D&}Me oA^P{.M3^瘑ǰrAN-zڙuė%UkO(gr b)cgԣ"P8k%=kOu(n>sA*jԽA~Z`C0꡵\\ztHTXt0tc[UXϺABrZ+3&wc*ȕPDdJl?(F;v^]'<5Oi"% Tޏ y[p#KDxGѯxh+Q8=F:IL t8+K CQJ>+e "AݓYc-pheY^?{SOljYib+6^&FO<#'f}7v6jaTFaqX4@8#khzm M,nѿd' U Q"}V°hCRi q{@\IB.sWgkH)Qx2sM1 Ux @.O(do/}C+hIl4B&CfTo8pbӓzż*R~ӈC8yWjx(zk 6aGݽIeDDeo-CTP RfuTA{ !UTly/ryS96mѣG."@dh3N혭LPY9r+GEsL] (P@A9^Ѷ B~ғ!nVF:$0A;Ӂ_rr6[fϭhilٟ[) Ӣi!xxei.4wk0p 㚚\UO +2zȟ S YU`gpKM޺&Ӏ$oD5nH@į/)r,2QGuwl 4p%!!B ~s٦,kM=*hsf}d%֍LUdryJ;v*Bz ߠZdKl) M|2%eVK7bK;@sV}s bg끾݀!j9yBNpM22n$Eb6𷻟.[3ϱ4!5'e D{ J%ZZv=߶([[E\B G]Q k^õ]P#G-{ BL#m~oL;#raO%MLVp7pͩ.%Θ>nQg<abvVVxH7DyojԬcV˴-\dtz 8h< `4;e1E^Ȟ@^J  &*!T_ R;d/콰]l.j3yY]v"6"p $Mc%X|BOH*ڹ)I+9OQOk)12G$j|lj- R.oط{)򎅩~ʉ!>xove4y5!0?_S-o_bN *nbV}i~Ԗ(qSR'b^$t?qŘ+iތQ4>EO9\F:F⌯n7.2m]e%@z#]}99KV*קknM=`Iڞi^XoY 4N哏I5ѣ&U7Xn.< 7ָ_j]ߕc-}:X~>p穄e'd?9u51uޙy9K_>RvJ";7z]s A][n )^ѠVx]0][ت-4Q8<ک[3BYx\{r+ 0_sa^< FDC Kj5c Ճq~>BXYO0ƕJd-κ3ikwNv_tUXJ/$tA!"E㤘$L腖;A:kz:1MẰE.ՙP d$`vU(?ojnP>'I{)R1_܊A 3Jx޲]=RΎ %HKD]¢1 /8sI[^ ʮC^tzfáNK'] rŸ?!N !S W3^v^ۉLD\k_JS6̆Mw|I K AESUpPYvD R*CKqďaoe q7o(םmƧ̵,[|¢,w{&XG+])JRxLHF)/}r@ֽF{1Cs4; 4o٭TQ|0Wns: 5vy[jG $n\7Ü3S/MK{&˹yeB.%hY]aY+1B8#_g3{b1YdUp~x}hW]9?'SO_̠޺fgrZ}ܞ~4F%~©`sF.* O(o@97*,yRN/Cc'#l]EѪe@*c͕2V61W‚bshY}+m(ośxrU~SۓdJL< Ъqw[ָsrD1māQ@ ~D=& ƂσU,kT8^Gk5x*Dݲ U߇ϸ}ELA@B`Oo 4)m񎕾YsTithvA$^$rR0QӖCu9 ~Y0 6#kE,@># be{Zhfm``Gt}K.Ar/[yhfi \#0Z0)^㶟SYvE8duӨ3(,v@k&Qg"whG>P`G Xӕc{ud, B>+`S0rMUZn *g)v',M 8c.uHuF9]&qHJTZW&G$Sݍ/cpq*u O0.-Ys˙|aYR:=BťO;21n&qWߏ7gm!& u]74r악={aݫP* r8$!Bk&ꄵL>ȞWE桏$;v إ&J }plʠ^"I9J )7WĪxGawc9'7>.RZtfB5q@xN c\`Xh&o@E?Ck=4)X]D.rAէo*d뚙\0nVGˉQ&L[Hl]C;1`}k6cFV`|.2D&|p CbiUW{IܿkC9(*q4^G)::qK9eDŨlhq3d ػUe$f^uw2Ã/}^v>$Ζ8hLqoݙ@Kv8+Z4>O8U@e2f~0EAitZ06|t!;AŗsFxeTaZ -+mԲib7OA7 \y- b6-ٵo B&&756{~9W.'\?Ilt2 "BG>0k)2riUfs]˔>%3>Ah.bf]Scwr-aZ?3 dAs+ 62~Òc~Jf4yu"pMؔ p@)7lleGbw詟ҥP{pX #x-ƦjfԱ$o[/Q%[=7|bܺlEk"\Q oQj,yˏIbz9 @X&C*j8,sΑ*vbs6R&K SU~ňPJ[+<*sŵ\ QOBS5rh3RZ0:73Cػw+GbuЫI v:Yyl;%%B,>AP揄‚xh!=5ԙ}R0^L 29jnΰn^Zih!uF;7{71zˊ>69KWsbvnT!1XKyA؂Pͽ 5c\za2\weS8d3*LIa VorpKLS 0:;Gش8Wc5޽3%}F9'bB"E”ATՑWl [ 7C`'+צޭ4eG/cq"7jl a}!#,O/+lI *1t"[P'l"3KDBf]rSFM{jዦ❔%BO8w  KJYis;a̳όfqUD<Яѡɗ/:g_I _>NVO+ v(shLf̢O9cJCsJ,ߊy`ǃ(T쭑<*fsQn> ?I$?z}6T/=8eyyaq( _L!\32L8[*)]+"FTZѕV;U-D`Ů k24n PֱRH@ݜ^ 8M7|>O'rnrWv^рSc+TIVzϪ z䅺SF}DFaYkXႮ#=_a#RpT+ծ$l$*኎B#K`L|%]S0껊2 ךhj^Cp_^c5.oEy&3*&F[ֿhgFӯRpi#TfX,J֞>Ěl@VR)D&GG:<rftF,֝/5'.7b5aQhCiUw8AؘA!Qaaau&#ys;7A&=iJ4j<;_e>n k]-TO\,6Ҏ #&SNd"v ׳HM2<űԓ`.Ոu9u؜ I`p1R|ʚ3+E^DH?D%A)i;IKsJD>"idz$[ ;x]Z]H?Ytv =f7f$vOl™*>$'w i6Hx7e˱eߋKy5T-} 'vhnyc)aN.|a@,o]&ЄLAf1BThY:>%.(D*\eܙYSt<¤oQ),_k,QF3yG;Ic7?5f8Jl:'XG={TL3z"Oc զ~l{3'h4c:L )Fv) N\{nzFOE0qY <+DgaSڟͻ[~ACX]9a n{^ǟ,ֹ>0F[he^$<1h"c K_+eSf˿>iECĆ=|goYbQ;}~> 0r7EQ[r3r&6U!(a_3+,?+ 9f?d~S{ε'-OM" HS"Pr&†ID;EDHZ- 6n~#ɚe$C_7@8)<;Y-~Q5?9",(YT +Ty/ xZWA7M1xMׁ;(>Tg\oCm"Y=tOKNmZ'zmtM=w.F@-6>Ǫ3nEV"HaU2C>P"'jOCghu/ݺK>jq b)' gda}m[%2a 3y|ıƙD{& a%;pk suhҍ mvbd*T=[dWR|B{HIaR`7 sԔJ0jԟQ.e^33c< Pa=:MD^:Î~b b%h&2I벶"RyЗDIA]f0]ǂfQͩ,ؑFFA5(՞1v{#:C>wJt?o*/HfL?GǂŷŚͬll;!Yo1(9G|g].\r1mn{v $l2'AzQWB▪j{$~8U4I=4[1 #KmcW: O޼@~T P.`F&2 ;uȴI\軞Ua9JJh6k)cr a ނU:rh$0 vC/љ߇Y֡XE'STMP yz~˟n8h4ĔmA4/y[ c߷1V秆n'G}]RVykvT4ѵ9ئ,wy}]c #tPP@vKB:̱ ݀άӼct;Bl,'lyK#4aϩ.+5ntS%)~#%%\MnK_teSnv"2:]v}mȼMbcyS{4xk)v[ i&YP" #ۮCcr yڽ!,"@ 1Ŷ?-,iR ޣ$LC3Gp+ֹE":7;D-ӊ~Iۘ ^s j/ā]jN&"C ,(Dipk. d5/@-%{~U_Km=qfGuU؃僰eUUBr q2ua$m1N%5 =Aȱ_7VԤ3͕t72dWћTQ5kSEXo7"z6/5ݧ~l5zcQq eךP+Wa90ۼLKر` dN6A95UQ5MvI!1j DsOuF ,Slkm '[ 5Kn.uwhjн<%\hQ9b]l IZdt pM<2a*uH_Rb6-J\(/m9؁DO fٴW2eĴ I>} b?%r)Ůj%p$Nǻo*±xigAGm=tLS٥>f^+F0h"^*@ڛ-kv@:- }h'PMd-iKO _ 29K0];uuU L`Ô]5\|V͐]C)q.0UdKT!ڠ- s|1:Izx>.c9`]ļƎˆʇ{KиQ%xMt=Q|\کE~%#-/:m-C Lx 4SĎGh~ppV@C/xFJW`rtFKXTk NZ^a!YFSa#`-19F̐ЈPe >_w5.ŇCߋUVD]JC~=4{NIۋO^p.:r*/&cJ+p#XSvmn{ Ё%rbG/sLq ֍\U ~r!~ Q)BH8TX;ϥ^61>PPܥj H3юfXX*4;1]]Wfs/X;h6֏l=?]Ƶm˴'^} ∤<;~}ؕl.L<J"P?*Gl$l.Է:f+Aհ>5@?G:~JmG}V`$/xuө倖mhggi؍;!o~'PB:KD%A% 'TK8!M#5#9odI\o?Aaef[bɏFkp%n@H!kez=TӼX4!!o*A|P_ڹƷn|NRdqhM˯iQ3ge0±v`)\W <1"XEw0GW%?g~xxkY|ֻfrw:;chs?!&ys(_'*]4`@~1g6N*{jȊ ?Q-U#94{y ܳ9{i #wfv:?Q |FdGq *}SNU HGi"Ȥԭxzj4ּzv˿T<BE5'~15 T,R1V=qR3o՞pc< GRʚ,Rjx ԩ " :FzU2}= -.nʰ {yjr"*$, c 5BeapZ!<&i&Y|t< ZnƱy*Hح7ZX9&d' _o}83[} FRvB~SD!c%5/Y5?{UBuOVXiox״D /-[ضT킽"; 䭱p{"֮L>}`6@l@&\Ok:[ҸY^EG;!{sX}6ko헸")9A ?$ D\0d>?Tk3M`MF*K\Hc Zdf2j-+a Yϡ6.&Jo0M@@tÆb(j.WҳR}A0P_W8EU2F׋ئ k~܎;RӸ#{EH_Z;JJ EJ +1vncecėcHPس1OxAzZ]zj`qU>u냢K^fvqe$&OպN|R=5v KL#ZlB$ b* 5 6~^w2#&D^vpeI%DICζc4]aжlTrjo d{T rdŹp73ߒؙUq"|=b ?%E)66".^ܺ գhB ^F;IwuDoߥHqAh7'J0fɐLE1H+̋)¿\Y M{f@עI%z%~ a#r\0-Vt.ۀY2C2n}:.+_",YZ⠅t.u y6Q,=wJjIix0avT}ꎻ{U8n9AӀ5/nn07ZK7"y[n24ޏ@tgS'ɨ%9ՠrEڊήE'e1D g! Zn1OhdXW,Ya886em;g,FF@8YK]N` OtD&'OnF$@bϨ˰ң$=Ia(Rq*ѳS'p ?mWt>#`!hVA<$lC`冂57j܅r}3 R}D-ѣ@93SdL޳YK)b4 )[JQ1s9޺:[% fg JJ-J,~oO0-E3\ 3]pR/q6>(J%y{q TjRlMa۠jDgQP) J}#EMnOmuiۚ6ˋRwgc&<,nm&Wz$BvWh6\Nu]OU6p\>X7*5J4\Đ< twj^cG%Az5LuZ:VDէq:#2Gv z*mJ /=at]ǣXf ؁ƢsRMQz]w٘ +7M)kt[9Tov7.j}(& O楳 j9u*=rkn㮛2 p`EK?-Uэ"fZz=Lu/H@Uρ\AFILUgǭⱚB[4G+S|zd+"牳x6׺Qk ],+(^C nf*(d)3(_szz`X茧b7.3ƽ,{_A2 1)/FyAH 4N] Iu>6Eroz兩t;5} !7c{j?O䇷O#f 9vGʓ d&ƞcd'`2 쭎ɲҔ`J-:] Œ{Rd^L$t( [@ \րa|BvYGa/}o)`4Ac#l~o-lZqרb,*}ӦRTKMq?ŹɗHE 8/0D%70yb*]y)y 5K_|.gn Ca"t"?P c}yU {Gu/1 _xlxe, p1Nx=, OWloc})d (mocK}TZ/j"NS~خN|$a!%$2SϨޯ#Z0vhh%gln<:HԀaF0H-x>, 6rzκA>,2ux`qn/+YޥI-1 ?/VͶSv~$tEFJaVgKyrީrU&}s'Xp~z*[ 9[3YW51ju$GN$̊BEGcD8glM3>B!(`=U^[OMz d5,3!K@6]2 6׏w?`\>-Yq)*I 7S"1γpF5aXr+?F%{dP6{5͍̄iP+o{ZlHLS av~4m{i?ʒGmU0.u9U(sC-G{%,YWYq_:&اuPI൪0[g;`v[ LKgEl|J%yZgOS#G_cP0gIu]cHhsm ,n( 㒍o W}0ـe^yii?'\$ 4^^n#Ϝj/qp~0W?)ْb[T'b++J͔e8a1zQP=&}i#VonhjA%_ZY01MZWL"6Lzb>'4iF3:lj{rgsC ͑Z=3Mw]Ɂ: gO Y)c3;\F??ڬ[ޘ0voVM*\5d"[rl~NuYU+(pÄp3SFYf, JboӖ; *s?ωuF3b]Mi;ouh lFx<l${ 5rslbШ ɚI;iaM0pPJL\De&h*[p|pGU -V^{؆>D {8yV;i>m m~.Vi{>߫b]Ng`n {ggNb#,dWA GP;"i[-(]ۗA̡ 0F9R8&BVpqW]KFx''B+p|)a<&3t80"4D+z00dAMm.|9Wь s=6J|J,I2< e(dޟV.%2_^S~=,74V'1dGB1mqG„?& ZZD ,zw 9]vRcO/%FZY`\;A1:b׏F~x } |X;W4(C}h.Sy& }FUη_axn%L~$uO ~H :D^Bﰈ1{2֕MYnۿ "E$֔c߈[`~υGt [luŠd-4>zᩅxVy MR$og-A~|hL&5lޡ AF|^g`\EfK݋ #3aȹCHNX̜ 8∳۰B*]0^< G6N˅"Z*EF2@G?n,E 阌b_1XTqS5_  Q]ŸvnB;km_p@J ̾urrK&egh*r܃]qeG>~N=9|2T&i)ZMV?y>xh2GeB&1vPp` 1j{e@Lt b y HY@' ,UɴCϖ܇3-q5(t6BrRi$:yw>D7I1.pclXwsEN> ~/1B>bNsm!H Hlz4M/DdEa2B(A #=s)'(~=+aZ`"˂ MR n,haWm5K>-ʚ̿[o7 }IG~ϜԺQ5JeBu$SΤ@T6Fë]\Ԭ]Alߏ(F(l˱ d̾t~N8-c@$1UtLA1g0AAuDՋIsP67 Tg6;qgfY3 cFͦ~Ptӎ#NK.$b!ytj^\Q!s ϖoy?{j.18ݵpV \˭B E$E_l39Q?Eb3>Nz ̄UnQo)-ϐoa(anCUPo'h5:9t? |U+; Mb\ t_B3sэ2ɺ[ȉVG{9\?W- ט.^1=oRB&Qbz&_m%Di;t5FӴk]膀n< ZqmcMFfr1Bq}Կv #YCv)`*(v? rṙ:2gAZue]\LҝC8hf)g,Hx;78k4k? }@]i2pU2sÃW鶏jg &}13*n)W~#E/`DMQ!>L^i/,1(1>ё!awˉkHeG\+B֣\3!+(Z#|zJHi2z]B )qSa=gjQE+S~(RE?i/A)ZX7YVzաr* n X_ odMhys^\P1{$с-6Sx߆AU<3EE&*GZrtF x{m]{DMŇIr r=X3/x(T6 U-[40x_s | e/ӄ0 ה$gifA|3zYƊS_nKRӋ-mLo~W n+n}p1<m_c1Y >ܕVLY2Ϊ,x\cI2\ {iV7Rn3p:H v{:uSЙ"Ms[#nhL!\ _j:Ӊ)HFΗw9Z(z ryw߾!A& Ge걯SjQJ,{A4Bd\6240l5ohrRD8n.9.g`1<3{-k(meL2hkX:S =H=ߢ,*bl\f6$ӥz~z/X^ǰ4iRWrDf\x`>6V(5)8aD6$&"i}KhoUs^.GxSPAWgލ& `v459g j[`ZvF|צ0 t;=+% 5vf9.jvmm> .zH^Z.!i~bI~b\; f2DkB,RȫKhtńĿSC.XQ`g`4y讼;@Wϛk33++}mE{{!I1c$DYJ\S1?jhj꒤و^UpV CH.8^kkwOJһ$nvogEqr s+<&v`'SN1s15.{iݦTw@hҗ^V堞N!}\㿔5o~@g9 WfDJ$"" z:;fSěOɇ6ea%~a+^fH]xEc"QPMӻ}am>OЎEs NYU3ticgiL YDN#^8N];j8B]L5^H7J"["UÅ>d\;9h:KzW_thT@i` h]U fs8yY~8-~{n:I}μ[FE,}[Qz=$6@ɏ<̽gC7oB3lf|t+eDx JAo׊nFc{(~=7yؼ3jrYXY]=bOTF3v0G -=:aHs穩@qE3,\Fԭ4Se[n)Æ)cG5KLa{C4ĺ?6^$zyt,TrtcB|U=j76OÒsQSJ200G\/*XJkVjhQ03LN::|{9eS<4kʥ럝|Y)]U0}n^Q sؓ-eV)>|GDjX?D\v_=wdQ> -IsU<`h>>pjr}0 J %L˧a`0;ZhjNסfߎ=$[H+P˸}psRbr0mx\}Z4{ 4&`ijZi}[hJl/Y;@4OE9{↰K?Cek6LūDFI叞=6 /6L(vWL~:߈ vRkkWے(5rV1W| IN:)^KZN`ՠ7?>CSUC/~^h=9 ^RgjqZ&&gkf1Nݫ`_<fC+LI3rNfim%&[0;ux%z3M8&+za~¼):`  8'4X걲ZV2 yH|ء1"_JXo"x7jbìҬf:(CY[#6S@as Hڦf[:ob`L<J#QKylV*:$wf+hFJI;]d ȉ>!W);x9=+ D*2,k̓AM[ ȣOr\bڔorMtb# vVvwf 7h\ŠiM]1o'WjVUj'<(>jiB3kX 6cܷNoc ܩpc8S.IizNwe`!CXF;cbM6{NBZf` }ll+AL唉ԕez\rdUx4qP.5 8Vk5*z&Wy}lAxH^'x#vEIϋ1pBy~4<2޳_ף>\$}mnd"j<   9щuˋK"1,$$AglڣҴj>"'T'eGFF% |6ɆH;Zg4U\N \  ز!rR)q ֔V''ǔp ILU!Q&s^ȼ, `ڪkՈa%1Ú,B@@}Cv,GC(!^:u\(*Z:Sx4^(} 5Ė33 >( q/nB䋌؃]7zg p5MtVi8 x!62p,koĶ<8cήo Q)!ܑ+iHiy{HLo9iG|7]wSg@@LۥCw/ά<.;"B6SSjdRsm9g hߟ}' BZUgơn)&pb*DXn@MPd%8kxX@P^N^%F^aF>e}1 LRd"!Tno[%ʈaqE]@uz[d< 8kv|ؖ_ 3.J>ŸA%B$#?KŦ3?eN 2U`ĖZϺAKB[ xY+cd)]4r3ိ]3ɡiK;y*˞Y$Dъc x6%FkW!r( *i, N$9q; k}ଦL0e6J^y֊}v&-XDH \id'n5/O).хGړ--]lASTbPVo@:w^ tb@RC5Y/Z6W @yz}GvJuȲm904K2ON.N͚y8:1ۚXڙ:9[0бt..tn6 te'Od6jedde#33 ;;3#3#;+yЉ7Q;ǻG|k?&h+c;djlci_|ѹ͓#[΋ F{|Q i]V HI7ʯ9R׺tQ%^i ۨg1>˚,WnnsD3a, !0G7rG 6Ӊ蛷]﬩pR~Nd#[5{,Ac_)@䥘X6-)?僠nN5`((3ʄ ,9W!wwQJX%IN"Cq ^-FPoXd!F$+"oٛ.g8gfjcrן~_6 NۭCgVgS|[2 9'X(6i `ם@S3>mBd.` z(E^S'Iz^)c*pHi&'Փ΍ofpcȤ%T2|Ε5 c*!2=\7.LJti 7'>Ѵ4q/V7nvhJp-DsJ S"#Z.MG=ݐT冴&M8fTwH{p,x;Wy~ִ 02ԛc *%Կ9: 5jx`mGm{9;yvsؓyyD4␙gp[d71^ _)l9hThZK ~GOl<= t? *qݓG+9Wo:cH*Z#&w{/U:4`&P1}uk+dd>UNM uV\.ƹLzt+d0-_t:w踒pu}#p{m@Iۡ?j6$-j3Ufsy ~C.r!`{A 1\ 57cWѽHZ =BuNpl⪗q[(e:FX;R8pllxcp֬5o FA`sD.f~(J# ޵W* ѬHEoFjy3lM&gڋt!gjBdl\Axۖ'Eyo Nk^Tع;0(Y{Ex h߂%!/Yr83T;_]%~D!5uCphPRiÔHQt 3=&G1F('+CAS 6E+)]z5{xOi,IZL`{4MgYY! >"q6/ o$EgN!Z@gpuK?a.՛)HR=A ;q]xuuVVʄ {$D /.O=aW\t.cHF^W;'΁dpq=X.int+"^v@2zicN>WP$Ԛ%%q<-8&zy5:q=]gi\sř.ZZJG8Ԭ[7{Һ9/;xJ*cX +y"V>F}XVψ'%K,5Q !Y*ҪZ)p w?^(k2hlҺa|>e2s7JlH'>h4:[eOֲwFj= Ԗ;UH4e+=EwHʯ9IJOJfrdήh.{MrVC.w_œW74&2yOvSIjbUlDT@{wKye K}.nwF::u z6Y Iں.S;-ڄVQWeKu+Sp`&Dm26j7VrXBQ@hAвUdHDߝ;3f2.7LYEF ʼn\`&M f vɅԜ,o;*TSC!?ZN'*n|!ЉmQ`i|kC{bY9̩ڟLpx+F0N!{w'w[eo%*c0.CrC.B|HؖD5pS(W15s ̤8[fR'=nW?kU$gH䬻qe̤Gꔅ?ijAoL/CeML9  K2#L{1 J{uBV8.O+y}>fIGk_kɱh :HӋئ*xH,Snߝ 눤9*5z#='W ErӷClF/R*"_K7U̿\ΕSkeϝcb p{L+4fNX/!I 3YbwHߋGCmZvSn,Q↶uYrPr(B|i \uZ{9˧KЯ$iESi>Hsg@r i!.^{m|TU,Cb]m  $\c%!BË=`6U@;ǯrCz~MLZ _m6X3@#w%:f8xkGZYAHv`"ؐx(4E{X-睇e*œs?Nkax P2rJt oKՌ?tI5ShEI?hlYn]E@[rƟ$s--!#Fo.~HeQ .Gp-GP]! @U%fB"TLUZ t2k%"} ]Q>P0"\5 GhN9 %VON3 1=O{%eyj% ׀xڱpF@zH6mxRngÈH/b 59_ y#Vpr ihmP?9#GNK$;b"&k CpnÊ۫ PG,0SA lbsrG8Ne$_IT!usU)} f\#i9ABN>6Nao}lL(P ޜʘ{& ^[U':\|MU 0BQ.2tz̭JACEq2˻xO88,R 7p)"l@p݀W~EsBh!HtE\VQUr k{;#Ff称^ >Fr%]Je)R:rM|6 IîrDo{_jjnC%8jSŶtH{FBñ$ c0_"W'C~U.;`g<N씟MΙA'O2ھM8f}gƔ3|H.K{gƣU!7&#]. }ƞskݠyvר[ }۰mO0PV/2Z&"wċY*Íu=(!ZtU=V[nbF ?~XjRhzъ&C++%/p7 lOp% CU5lc\4 yP&\I7|f/V[T>NF޵'XyH̄YaQ.ExWR'-J=j$eSD-ۥ KSnqLc5jl~QqSW5\+áy7c4POsaH$; )&0ro)oiYCz]%b9~]IrҜ(` zOGy7epL"ۃ Ɲ9[!Wo].8֣!~ןkqXC_72o_eA-A0E]\Tޓ^zSd'n ǐ~a<]! &/V G쩕_Q55/ z>*Ћ!xI|y \F*o}0Op#yyos/7<=%9l/Lj6pV"[BQCq7(*ÔM^ҿ'AHi&O`O-UL' Q߃>v&@T[O:Cfje6W Iq TPak/Mc)>?-U=P0CL0)ڋmpDžs EjOR_[ E)i%ijP s]s Ŕ{.5mCl>V(d2<myFK=TG1~LmPEɝ5{2_cZFm-y!C_^Hx; Ճ#g^7>9pGnӔc0WVݩ6C"} c$%4mxbJ+JY8_D5~Js^j?(#+ֳTLk2 v7r:d Z耋@4x A3J4t`á$y(#HijovHOe ې!D7rl<4 3BShOӖ3q) ^:؜z] %o>xU۩l"kS69;Wjc'lazmHUntդ|ĭovdS~B2-!!&>e qj]>F EгX(͙U9EJ?3%6YGx~F)ݘZ +@5edE$l. }^X?i J=v21_ HS*rތ.d)Ēum;RMڽ"خVYμlQqDq~&e P7ƨ=>64,'?_HK$GXO"<8hqi*l eRIp>R8I?Xר߳oK˟V)PJյN&QƵm0>WELs4i+I)_o&s1R:vp" $q =~9L};ge'W%*ػK2۞ M#R7Nn 7v⊔tTK&`+\JFÁF_j ӠeT2۸Qϻ`xap׃Ĩay;K:s m u/aZX-J2-Gvw0f. uEBOJ\I {}7Ys:Š>X"M==Rf^87nđZ)k_Y-KeO8 4Ďԙ͠o*4W37IH@aԥ;&- O6 g8)֟&p~$v@k | kjPRx/~J꨺w| [jK6q#yL D& 'F @Ԯ/V{)\js `XPӴTbmOJ|˳ڙ W*[}V*[t bS-uЏ@Ө ;jB[0={m XTr8'o㇉m*9!\\f ط|sœ%ܡHϖm귱sVިZ18f )hz 7(r-㐳$ &z2FHr'l{ %qaHX,Geih3U:w5.AٶĠ[Y;?fmwo|rXrGXيR ~ `뜪gUԦ ;2{y({])Ū W|XY6SFRQa.gUE9,4yOQ}np̐"Z%xEӻ EOK1E9mN'VceKc qS{nKt#!{f(>҉zY󻔇K!ko RRl&c`'Hͪ=fm`Ԕ܆B-s2z S/ESB3];&BC-Iw{2UC^WM#*9oEϷHp_ȸ(A\Z l%6֤逸QdWP7DjsWBZmVeCw[nd6\1w` ys4,'MQС>gdWenk7@5xŬ6U? ~sv/zb_Nv juTТ}a b7,\jZx{7\_hܩ7~3/j>bU΅!dQBY:l|&mk=!8UWMχU@kV՘ goE|lg,ܸMpH!X'38H*ZHZ ǡŅ6MR3_$PueqdpΎ״ 3Er/e1p=XDޮ)`۶w *Օ ʵ~u5gFq9@*ju]\5о;ξ{EC[LJ MDrD?vDbtW5rX!2Dc'ذ_2$c2H(+,H>c>vS`qk\ȴ\Ʀn4,h$ yC/Qḃ4Z][A+-O2yg^ gsZ73[1bV]_a".1ɭ.SLkɽkG2`NG?N06CǽIJ$Xq33Ի*lޏup5}H0 {gEEk8;maѾFɲY!z/ iPO090x N*B ,ׅم->M8N XL1 "xeEAu\j]tj >09EqC“7= :V仮Y<5J&13@nZ獿ytҠmDzy[{Z D=쉿:؅g5?ϗ,0^l1HUq1ZW#ପ JK+| cBg)#VJVɦ>/dE\Vp z}qB_D!1䉢ȡ7$#UZ;b s y_2/*{BϧFaqsQ @ɕ~je/Ânt |{گđV{`8` [5mFLk Cv?s!oO!2wԦ fXtYn+Nt:AL7 ưgL5+dlڇSpkiε\1/J8 @elCh7OluHE,Hݣ;S߫SsOFI[B-I'UKYY*ƢM&sST`YF I`{g M Uh 6mPOW須ȔBy\P祭q..x[ށWDk3|90| ^=VIhֈI/n%kΓFv `Yp֒b)5U6=ʏ뚸T!S_ ph<>TEvlȯBt`Y;2BTtɱ:)"57 cx &,A\x}4hi^&'.##6]wrݖmvpkbp9@ÏnW1匫V=#՚wz)/'BCcX =Ps˒N]< F"F;U_׃T}oRDI-ٖ4cX4pcodrȂ^ Jvb<)eUh,Oek~hS. t{SaL nH.9NImSy4N"+ d^QJ!^k#-28Z[X&v\^lz,OCr 4a[rlɫ=L?敏mXkODPDGr1$79QJTOƧFòqHwj.hrmhn0v6w>Pʾ>/x "4m.ד{!ْ.t-&RZ:FUXƛX^[H(+B>9DaU7oVuUӖR\5Vr{sص70M~ m@Wra.v φ,`'8sL;]eڶ ^qԼ7Vӊ[B8*R!jk 5gzB? `BBeM ,DUP$RfwVE|l. ,9d0 Ӑ| ~&m[qSWI%ʆc@Ъ R@Wp#L<XmŀC(&xq"ԚZMRgpjٞ2ʄ̓:s$}91&`r E ```fTuF\Y ̑ We瀨޺KC< \`sJ;c֘8ʇj2GdyU8Sܕg˷l};E{twY{ bdH$̾~"YҢ5?ppBU[d޺@ R-N>0~{gEٿ{v3; vɔBo0e<#@ =bsi$!xN͸(+~ 'S#DlƫQE> M,.M(zD);w^y~ +-VKIe@dFÕr4:Pz6$M8{i\\ ?1W;?*zF#zLam@_Fa2?D^iݻj_e]Т3#h~42Śç/5p=)BerOxNwDv;DV.6E$);5gKXJmѱd9rxx(7 Ι-M82/V3μܥa0T2)\H,7vOatjKy\\@/ ,2H1\n^V iāFmZH1p>kI-YU:FR8#Cpjqr۱8 5ڸM^e)CZƣ.@cW:4H7B~=I=b%>bݺRC5pAd(~eW5FXwұ{FzJmyv"o)@~wPne 6ynl ޚt  j$4r1BT5$e2r㙝vpt o 8*x d ^z| 6QeɈ~lRspde؀ ַ JF(5D6T|2=5ªZ`4٬hEm=;b{%=9gη/#%xy-ljlJF \%w&73lc.Z?!uaVnk孼fb8 8JZlՇ!Nw/kU/d^ k_dE kr:5siNͿu|kִ0E;#>&R@d#Yߢ\ʹx']:E ۺ:բ=;y}v32r'Pؼ.n2{3 XSP1T G2ŕ6p [ ?7;9t7`E5#3>k&X&r$vъ 'qD&#ӏ [!Ǟ-_]BKRaq.vWsSixXE;* 8@$r9V%O% b/+ {ֹ͡p>1 t5G{ }~ S}ܦ<"]+5UC%?2| M2&`ͻl@[3=zat/ij%&Fk'@u%M!`A8Ƴ6Pg!(s1.:H!D>WBv}"Q>)jvhm۶m۶m۶m۶m(ٝt*>ݧT3*0416py<|xflϧ23|@Et|񳣨>lS sJ49$Ϊ)08dcfB4!ya38ɋvbboJvA+\A3ۭA3Gh.Q8LbSy:l\s#}/ԙxuP:K+-N^Y +guz;Na4͉}PMd70Tn8)=偙 ,`0E*IHBPO2=^Xd SN!A5 [S4n: W7>c`)t8e5(J^y+TR/ i%kb+M"gP>t`vXnt`s {$odqiN-Kzԯu(*%0P75=뫞*ɥr=n8yڦs Cׇ'!9-!m8Ow$f*cZ$) *L*XŖsE?T Sw9=a?wup)m7o <6RT oPOl"駃Jzd ̅e-o\Wy ԅpi\}4! ,#K^S!4Q~>?@UE {90zs!m,^=eX5n >qF$/r)Ϻ~N+a% zdR{7:? Fs./]_By=kAu[mhHEnT.=Z*=S[Yո1g^UB)b=¶}ܧu'_DL5 dj5K\ZFL!1$5 #Oߝs_>Ŷ T"UF !b/Vѽ М*1FC~a$"Ѱ_GDIN5iCErұ@l8Yxύԫq ۓ; 2k+S|4`:(GX.Wzԕ3"bv-1S{<&ܓښ#/<`|W[){|C(w7IkLFW oʻۤ l2{dG蕉mlWr^V%,W;ʂ?vK ,P$tRLб XWFL9^-<0^m G6ra]@0f !-۩3h,a;o@kH2X0WbAwʂDDfrhfr骷g TORi9t$s?JfZ==_$M,zݥާz6m \.$[oD3~.rXVv~h/PRⶱ2:>Ba?Mc 3\8q&߻#$Տ}AZ7O3 NRPY;Ը[!oq ‚%b! .f]<ڀkdO#̼ANjgǞ Ai 1BM[ܗͅ'v( y+TcI۞U`5fkW wQ[݇mdnJQbcuO DnPEz{|SV蜵T\N"nucD}fJvnkVFzk_.x<)/PFœ;8Dp-i9jJ"7}T qH²Sjx4.@8o4Ko)G,_IO𽂦-Z+gU@}DR%_k7<=G*y&a*gw /S,/:VGLLK*vw-Uz2Y*,/"1J4h=nReZ͍Zчd+S zU>V?wVXck\.acjt"G&d}T<.ӳ2Q;5}hǭ#m4Q:}^\KSM9 4i( yUkT ȇaQ%tm+ݚ!XΟ59 ] w~4s@V,x DM&BTzR.,UjxwR\&c0߶'ҟ0M/ɗ=\1~m1a-Eto_N2y㳄Fr<`Q;,C2Vgˏ36/Vئ#{r";N)Z3 SmW[!ؚ9A_2NqY# Px`lC$Bey=hd'ˏݹH,+ xQH )i`\ mmV>2iyu簥@O7cOvAu][njK/KI@a} [ᕡ?$6=l;g%c53?Q4\[muzŐ2k_Sg\'%%s:``ߜ)۔ѣ|GDN8hJv3Nie#, 8淾E{O*] ? 2N;1^Qqtd_C3C7`o>2u$dv0֒}8g^~qveT#v#n PMf5^xZ,p0uGP,:3p}x٥R$7 # tjbF8:P-`KΜP0Tu@݂Ul]<'m )&w}oj&~T ߭UU R)fT[!1u(!Uouj@;E|IuphisFn؆}Wr"F6RF4$ΡJ*)NvB}9TGXX=" EQx4&DX1?xo`B~x|I blBa|[rA"AopX&Z#}#n>l& DLĈO7"'e:w7OrB Aڸ9jW- an4찡[;߅_q .Sq4.t nH샷|eT<+,j 4ˁٸ_> fS;eH[)(?lWw)H Amz^OM3 !I h^jEE$mcXy^1hkPbaNڦ^0,aY :!R-p}凂SheJg)ROy`@g>Ю]ByA%;hۺ NRZ{l%;UG=_nVn𷂚(w/k(U#15FOf3wL@ʑ@^1&dDܯi'wc9Y[yKA7m8-Z~K0[Cua 3%Ъc\FJ2p_ƞ9(RM!>a_[UBJhyd3p 0WxU"IaY~> ,݆nE)ꓣ1&(wSƛr-s&3L^Gz&'vM.ŗ3@ԙh/۪:T= 31^u'2qEK,")\U h`Q*/)kou$W4X޴kP3`˸ if'iMH.M?==2e_oZ `Mw y?R;ťr-)}^E=o<l!tXBwoB6yn<4,̚m\+`d-7|(ѩAEUO^Lk9qgD r'Q8$AGqκ5I,m-@ea 8J֊w`*lUs{#̎^+)PqPB~.V$1-f Jl%xnR A2Xñs*'S|_ zO;kCߠ$'6d+ ʼՀӨ Ӏ;ʸ5 |tAK'wVJA= O` zW@iXΔʳ [) k_ԌE9?nK\')4&n(AePK_udgX|/,nj\pºZ~{*Pt(70I0&}9t&OH/r&'W\Zfg[GVC{z񗶪ꋔ5BqH`  GZMu$pk'yһ?Nȇ*XqUFq@Sˇ?t$%7,|d.vP Q4<ؔ% 2OpP?|{ĝ֜ X =h21P+g+yLkY|@ YQbAgh5(MD ID-bBV.eH̱wt>snJ^Q5NIԽM? k e M]VC{DwV9[Xlij pM|S4lV¿s,j - sj̍E_T޶sЊ)k<θږVȣH,FYGcF;+0v2WpjB6NCU n8/γA9}Zkn~V!PEVU2AN"!eQƌ|lTEt2y07ýXAf%YU- ǎ s`D"<>Ij !)hR_u QF~zNFĮPaa/ahDyHLjvN.Wr 8f]4|@Tl_Obm;2|,7OÑ \SEBٛ~x =JR!RgS٘}" ĚrL+킧`K9t~18Z:>aO( a9AT8k(]a sqv5V,kLRݒ+fe &.8W (ϖ̵$;i sXs<ēkC4>/ ^tWwnf~McM㚀SӄO+1KQ]Z0پ%0}0gLq\-I92֍ěrAT?2v,yxJ M)̅X,]Md9v-=뎧o}[*{떔4QLi[>DU,)|{ qdac=$ތ塙UikD]# %88K3(Yu9#&rz>[9M_2+l7nܺ Oq V+scWu{eigFSQiv({5Vfu584@V&?A]ma IUA-UJ<͈_f3N}'KG)$ %ᡬQnn F!sr@"jm&{ԅOA!)=J+%0~E0; ɥJc%_IW!L;Ǯõ%Z{c׆ט久('[Qaی'4Gr& ßȁR-B{Ղ˕d,VQ}3j YGtS`(IbHӠ7ġ 7K'5cOIjnjі/\Y+A>R&.yT `J"(ё=]+u5 edtɪ+~MwZwsZ`6f3EHӌ8C<ތil2?$.͘eC9ӣ-;l>@J"] H¤޷ۤSL\Vp %Yᔢâg@kP=ŸouNX#ǧ,o'+u. v:ZɃ4O ZDXCxWjٔOZ@nD*b8a_Y[XN_<̇.M+hTeqMU3pW >JBYǏ#o5dT}smwISҔ4$/ħaQ8/N7)}K$_HFE퐇+97N =ώuVK,Ǫ_kainJiօ@e:5U m $FfM@^sAuJ!4Y8%כP[HVlO$ǡS։^hD~ϝ˭Pt$[e>RE 2y6+ aZņSZjݠn.ojXJI(YX_Vɩ mlLy^8qq&$!ў@X}uZ\v,tq||!oC~[y9 UpGkY,բz:}g{Md4?;5e8Lym׌Ǧ0 ςzXN'3{ΰ߃,)D0L/뺬c9%G:hR{P䭮B+LױYGs* 9vngQP JL |f,56!9 )Ӹϑbmmn3+0[z>0Ns[ 8ʢ=y4:YK0WcrE۱^n@3 $bzătI^9-E-_*gM!jO{}=buwR*$S7//Ղ1?X㭑j]0@ 2mW(3g@XR= 3Cʘ|wCX`w:7 )PKtau(/bԟUcTFBb0[4N$9" v91bw#A7vaK)!1z UaۉueWuU'h)>Mutmkr)! F8i-%|߮b,ǺkX"=O0Hr(6Ɣr3ZWZt*$. w5/i9\2k(Bgk|S_acX˕@Ĥ a"6SO0ݶkiկXg:^@)$L%KaE2 1冷xzI<2Sh ||2@LYM$F YwQ%HT'=м7r-sl‡c734/1N࣐;Ki0[yg0MgsjJ|{IHI#<}zɣ!#j[u;]Y5mdgN-G_[*. 2o|ӨXoD3?wJk o=` ȶPmbyg6@p<47Ż!CnvoH)7&KbLkv Kn#H5 2E6unHFV}EcZQ/#iAkнA 80KpD$@{P(; vr%dL6Ŝsׯ c8=Nb쨮R {|J]H!BN" B6sکĸFSr69vFߊ";{nfVB zsC@;fmTh8[d[o%pFo0 ;WZ*{_r*,bwiX "> ;A6 &=&>r2.25v3$^푿v|hr;c࣡vm^dնcy ź{mRQ _Gkb #*gZKBWp;~ >!S,XWt4LeKX5ҦYX >U9#墭3';(iZ:b 6JFqv!'_~/dZRY.=%U.|"x SE81̓|Q,@3hcQhBTNIv*̠k(򃲾&M^ \UH{SV`.BY'Tc~!mE7m)KSA&grƼkδ*z l擋Ϫ12 h?<%R`b 6De5`x/0s#;_4IDžt,l!uq]po 8wBZN'4*K;(o{4E-~HAr#cI>/w6bfsرsu NjB#|wh#߹Rj TΈx jMI +9;$X"h6y=l%}=?GȘ1p* dѰsCFph1{1*j:k@ioGОto7 5{IPBN%>$c,VinK~ιMmO#:D_eΐ^60W \ ُ_CN;~:@6a8*E0鳢ʝ;`]Bkf9fg"T ܀d0ԗcsp ь Ku7fy Rj|=EqUk@B3@8͛YFz˸vmd U AAXgϠsGCId#Q VWlp#ҸH'Omh es:Uв y̠=S; <^{3$ dBHyi$D|0D#` gѕV8_`dFb$-4 sԕ'(8,lkR=1Ө8ym`x )dLbw7+}0d7D<}C%;А^oK^;Wr֍VT[ u>:iU2-jLݶ S8,\>+ #='F(&̯/b"˂zLZ.bNtw8Babc<9<}KĠXAž vŠXdlnWjjWs^{v1nes_E@>غMԻe49Q]˺[Aq[ݟHݏ&Z\1dvN X. p"Njm(?HKnXXķ^Yi @|C*չ650$^ںoyQ걛M* j!sLiR%ãBmF򋵙5T|EE)ԱWݐV̿I!20 .Q?LfIeOP YQ'=#b:x{S"z1>Man׌C^'Yxȝ(֘Au[o۩Qp)(m^O/@3/r%4V*{ڪ'^5\jڳnv ru(V_YsڞE>Z 2:u_OWH^FQ6{=>cu"CQMNT%5aLsF,lQk4X7co;h"kQϼ;r 1/iѧ|MTWR_ę ON #uL{2uhOO.gvyp6!^b!JB1%u>G=HVa jv He i>bl\!zB‚ڌ7tZ&ԱYe%z!>9&څD  p]ŠB*XzV 2 JWzC!01pGj7`p5WKs'ZI\\(~Tɷߺ=ml r  {6o2[/ T-w cj ݫGΪ2V6WhH=*']KOR];'||3~Vt}}Pbv?,}.̒VZw߉Pn{Ir$]U^_#}d]UPa!o+ӝNfĄo1W:(ih`~vƢ+ږ7`]XmT3#Cᡛl aBWu8ݠU5f[R;*nYd'!C2$(ef_Fw[K[7zT]ӑ\<~6{w'.9:Ϙ}T). WVU 32I(i%Vy1WK#v!wQ RcoaڌZ!X!lD &3N}e= Ufh֘Z& :X>[']C\peK1]_%W@x[+_.Sޅ.!:&G1NWT-; =̮Oqw -'"(by&z\K=}WB oKM#r3ȚN=lߑ89Ƕַ[Xn(Z[7qգd,(,aS3 ~s`yx#+ > 3g>shEMT΋?wƟe9ݦm猅Ȩ[H9<'qixº@˩,)uQuHdwˆ]uRz']1 E*Ue?zVtC cP~΢g3 Թ<$m̻PF^ݰC4z&AR%7z(bf{4ki81EYU_I{0<#&>T'pX!FRSYV#G<=8E=YưpU_iI"Mwz\dh |1mڽ/W[:lP<,^e'0x%' '2Őӿ5u@QcZ(^G"~llM¬(T)}t>@VB=<Ûd d%(isxwnꀎ˅ӂrhbtrC0e*iSZ<i$X߈.Q cTbNPGfiWL(Oҋw09fW7I!q޶,yVŎ bZ`P"/:9r8WBuc}Z'劙8k ^: Uxj ae:^{VTu&a$8>{0Bq5sN/hmPWh<56ԉ6V +1 2ↂʀ=.F!I{# [%`oc|uKJQ196b~D*Zcdud= bd's!I,a4 ϖf'Aq6G)8`AS}^:$~gAٕ+r+.h2}q\0edͺjfUrUCV,U)6P78T[!|UR7LHo w8Iq?:%neri)۹_D. J0mC`?YAXGQh4c~/fdF][*jfmʓ殚Hw R#=&H-VډicE^mbO{-|TY;n mcJD0Ю#gȟF;}7~jy*^ ~Vޭwv/1R@&_|`|c=?-{[v%Ђܵ}TcӉ(EʏcB-X)o wP뻛ٵoP}r" *" ɗ3`r}7IW_ #bICʒ*Nma8&!:a/u(Cl!G&.pA?>kY2lFp`Dit)>;x+\l.tlɠcØV|YJ"u'W42<ƈ;ϵQ1vd91we\ENhz*j:u0>T`w 1,*\YRZ /ή;<22^D.68H՗˼.#Qlcqa񼇚d#9JaR YGnT|[qV8$gJR3A1%#W0!o]TN X@+^DbML;hy'\zzDGe˶XWX JJsZ؉g%* gzHIHi $eO|&}TiLJ$kr8yPAZtjwuUd&Խ12Ǝ K~1>ŹY̩?` 8 +ԪBkU8plt\(R@=k$ۙx43Ƃ_(ӁN>am>UzP%ٯl&7 t,  A].S'\s>@~0ÜC!mX= M,/ n,-h-Vr'GZX |+>al4^ө;!Iֱ 6,vh?sa * ΰ~z(p?v`?BMm@TlLe  w>aӲOs߳Rjf#*wd\vBH-7I=( ؚ?؅W}s ؓ'CeduY1=# EKJQu7G_4c Ë/*sT.DpL`cvp/W&D ֐:[lOppB(q\%ݜL{X1;lz}8b]SBg#)'Fgp|SqA7Ɔx7W琰#$,f0rt)ĖGdҼADfPO\yF+98_*4Rj*8҃:|R׳V y(, T!Ƃx[xXYK{|{З}MQT&T LOO>LJ_m Dnc>euO*}a]&RjbL}@8A28oH2sZ_ m?@+1sTGT9]nsM J{VX`'!6lƟ5 o>jIlE7x:"0m,LAB!rG篆(%{2Yl#]gu Kε* _do)MRf1zSd-6L(Y FP6TvazXC ~NXA\LI |$Ƶ Hw%43_=(c[O勜iudwЯXse狽p nv+5-.To%GO.qknp\Kđ6ظCG\S*P{}Mn4MFnp𭅀^6֤nd&# {(gGoή05mWBa3"'(/|ج+s/@[glL_&I% )9x;j68bzy# ojLF@xԕ iz) 7k[u})C]=?<|nۮvܰhaR3r1RbFOHl(ao1 CtfCA"_*]_vx&\|ĵb^-d=1n"rnhu)`?*xqiP|TFkm.9IJg$Kc Du/Lj#C V&'Eb{˞r?/z66>sZ܂f+Q&tޫ,)633y-ʢ"6`ƥmfC>] 秷IB/E} ;+Q M@F u%Gdv|o5}`;j(oʌRP>#6IjCk" /gD^5;`pt<]~18 L|~uf zo&oJS3O sV@a? eW0mwm c^@Su;~t(D'ӜH*](X@y6XZvJZZ]^n_kWYi#mPf݆z`䥥Z.I-6D[oO!걳`&hLT*"U*FWLI;5&pFx=Σ|\ˆM}R*`²ﱨl_}дC:e(Ʈ+yBmWY<t]q-Tg+rŔL1ᖼY•pny9%mR5„74<6)Df{Lmnj u]4vʬة*Pt<,A˵J HM I$]䛏s+ulZ'ts^)^".bޜGsqHp?v˭ZRs)U#h}W]{WFgj{&ksIӼ\.rU'7⫵V&Rݹ$jT4ߤL<9/ԥvxTSYjz 8~9 RzB0&\i慩Jteq);pܿ63=/j҇V_) r X=>F@BuE0UC.I85P`?dˆ56nvދ).KaZFkz_w/7@12̃kbf(I?x2;S貗mYAu&}IU`['υ=0K ^zpjFD""8;Z{hX9O|hQVr\ 5ϡK`vD7[ā=*Eh 4o1~ƻfS,z X9׭U5CW?vδE(?⥎qԵ3.&#uT3ya-%:W0\sJ `L当d={M'~ xPq؋I DV`0Uepk67gR?GHh;ua_]΢׹SNba{6Q/8}f/4F!lHRfL)0Ǡ4Fxfk=Ygg-pmCҢ5RO[Z! [${Q䁊l{p &Z=AqЯ}~F7%V*Hp}V!R?l`u8 0J9[r4UbVl)RLKiqmbů?_[]K("Tg >lEQeYN$aDiۼنuU}WJYݺ Ymidd7#|cMFYm&)~ `֩PDߣ MBv+[Esjbt,ڲ'vЙʘӅxb<]UW[W̿g'*cJ62n`c2pf}'vt[>xқm Ygxߞ3F|I<115 !@+(K`Ř'-W'?ЭPʪi_x_H)΍dbb]Kle`,29_nc%q9 !:KRyaJi~ea5_^@y\>%oN,!?^JJ ԥ8>E "H۲#5QkoĖ+=T4L}ATm4Rlg]\@< s IK=&倕e~(Dum4k's@bѣ$Ax:w 'Z4CeDJ8Up~>l2FJd_4\_8OCcE]wJ@uY |H?&g_XգvcC0M4,95 ,:)cp2=`5:_:T~l3Tbw䗈Zqc`' v}ջ-R),gHs7 "!ͯ4$ ^ 1x3;Q8vPѓ#ⵘ*uF'ebrvahԽ qi6_4/d&6Vb(_ ȮcY)Pig]^h`8ߔʁl&K' #[< ۩C(zBUȡ+uzm%Ïސ'j#%hm?́.&2wө&-6*+j9e5rl ih[;E :jСЍtmjneIn&& *$˓-,4AE𱁱G@`U\}MB}wn f$E rcxP@;(Ø<t\ԽZp\<$'%M&|OD(6`gUogy׽m`vwUW &ߤuXp&|refU:~xr]⣆8.$;po3} Jm$;V z[ 93咔GT{ywX_62;e#l1&/k${ǡeIj0@l{~TNH]YU,G >]GLb]8@_㙀cVQB.0NnRyg(ؗWO}7bW$!G#=k|pՉ=E'݇H漁-Ɠ1_A3kzQ ]O$RBB(1pƦ=h M(~BUoxy_&zd|gDPxQ Pw|l8jlluKS,~؛7J-;i~H*'e7NkMkurrL ȐTej2+ɢ 櫭KqZV31 "'o8ԍ?lr4{~eSgΕ(ػYժ3uW_@rp[s[Ol 03C2 BV:iQ NȨ=uCqCi%P]@plR} -26ȟmuؠ|pŘ|xj7G Jz;m8!-;yPe-+L+B셃a<ݨ4̗HWI Ա`#ʀ$٬)L-}ч-I]op/hOOt$?Ga( -2%D| xL/8ÓG]l~Z;7{RRtS}Tc޺*}1M\yCR,/][ zhjľnA6|4؝f=s%i-pl5,[unk)MĞ_IgPDg~OiXJb#2NFl[p9y0 ΃A9֩|@rȟY,'Cf,f`OE~dXFOo$)߳tA0P{nGy/L!ҕx8lx>ٛ}N2^0m9J߃k,Zj \"σyTj4!:ڂ(tGb/_t6#xU͠G%jF鼯Y#i B(gs^Qwwֽ*Bģ1 '0 D}&Q>Ґd _ɄফY3GY%JJde-TuyweܼIsOP/RϮT0wtf lH M 8P#QН2Ʌ_piߙx7u$]8T{7}Nb̊[c#"D_m3e;5ѨF/X!<7v}Vi/'*{ _uyyhꖲ}n*ê(Z z 7 >Tō8HЁfѭN i@eUlC =jpX4G)H#,9lG Pnс'/Ag[akսJ)}̯FM $gYR<11Ɩ$]MF*6 *ߎ:Bw^1ICtmA^{}Y*@7PB0Z{GRh'.؋Yk=Vsl$DPCoG&sɐ?VV>90$;`3g_$7IlP B[|d ;!p- kCCVvٱllAVdž&%7*\,=izF?t=Gj{Bkß g`y"dL uꚳ/<8;'c #FKz Eum6,S8D{j1cK0X^/4:כ@ӮBEvj1 EVY'{K oUbn0aS]pt-E&">BpA^b8kqIXI9d̾Yk^I0QJqcb|5c)Zۉrc?ye=Lw35CffY.AEw2l5(wv4YMΓމSfn.ȇm@0mKj< Zy!9X"TL2C^^l*j9#_0!SFlſkx d/dȊ]-c \48G'f]J6N&5myGn~3DeH۶P jD*'hsy &[;>.CYa3k^M4L? t\n9OH#=5Ø7EBT8pwkS" hvW{j!Pbf^On:L#m&ji]SƽośAhggق5XOa0Fv"0*[PRXmb]8x(= ;Iu4A%&iDs, @'$194^#%e~%0 d - 翟Ϭ@nYw[w Rdna'SC{fMM<  mMl,L L-3 :zSg:7{Fj&56jebfbga`dbff`aggfd``dbgb `-\] ]xo&)(o>_ŐKpG}.jG#q-H̖ oKyPp DK3<*^C?3<YтM Ue₿(,mLEL 6-д\VGۑIjE4&J9 _u71t1?N7v:a|[僤>MҸh$.MDb %n٫ƅָCdc,0{Lv#Lj]2]ֆgt_  np=;:xu!9uMTQjD܊ g,#'ںqw?ɬQ j03|XIZd{ ޑhxwّqdžYJ$"km,NU_lkN.`a)TW-tyS1ⷊNmAB@$%n3b<&z] ;`jAKс*EUAJ be*}2?Ced\8#@gbΤy h2[~RNnzy/p#ssKAJuyzIY=ݧE"}fcOY~[/iJB7.y]Ȕ,ŔAh9Եnon-]eGɆתnZ6g̢ϲ>-=ۧLKB ƀo®t"mW;k*ԵHVq^D8KWE)P4>y)&MgKn ąS`c9 2!8 r怢땴=go]VSȐe-0 /W}9[ /Y?mdo@GNya>: vkkYaVL+|ɽ$֩lk>M|"H8u'PԌdu%}+@+ n|ѱIrc BdشJ1\d%R=I$Oi]v뀹ٞTXꎏs"0=2i m qspeGn<ȘJE>Wf )qt,͉2r4m"M܋ՍRl%\ ?<ÈKSQO7$d!8*I|Se8Nk~aR*'Kc@^δǾ|^5-!R!qE!~eUEXJU%->&XC8v|5}q%XQnf^\/_F8d)YuLW} [y*چ҂Q68Oϥ*2]Oc qwJ:=՛JֈK) ⟉;2 ~>a_ݟ>Z5YOSC=|ݬq.lym'L"ݶc9:$?&ܤ{}\:^[F2cev(Ooa = i߸ڦL=x7^X{2\`5k 7QG$4؜*K߳2.`XNpILrJmEcƴc(m@9_s&bݳ}Fx7b%5MJpI%<&^Bsz 55͎b, ƤfYo_ar^s7kqz;w4 Q*p˻,/?z}AHv !0YhBX]!qS$Miم{ ;Lf RItQլ/l܄4vUBU\cJoSWrR) 'ܜY@Xq{[b/îb/ L=zx`IH? 7";J/h?] _}lAyъu}zhoh_2A*Fɼ6N2q}p- j`U-L\T%\.c[Rm%iJ۩hTNo 0lYAkY2(BHnxg!r,廱ꐝi:!oŸҨ}?RzF-\CT*P_W&>;(h>߈dVwhF8kOMT8/X-e~w?TYNϙ>\O xFQ?<qfJglzRTON\^]#Ձ2a%v"d‹ SONlj0vs 0\l4w9컢`Zv|H9GtdGA]0 Alس2I:-Eq)g iO ^^N`\izu}e,tq,35 nN3pxÊ3vȲ_;t3AAR$ "|M9H9nj$l8ܦ¯O7Z )nk<vO r܍?E}9i1 ;VZ%}14=厨jw7MJg@ݪ:4Rk7R*+iS5#K0t I mޓnEb&bAFj3/E^mg߶ ۝uG^=F#m|$=DB~n=6aUUfeJp.QDfLc9b<捷V( :6Pof(ZEqD9Y=wN{<E 7aVѩB%mq"XIS#b?iBǶGY}:qr65gF=K} PH鏖GɁJ?l;tbcX!{ОXf`sjA4\3GJ%nVy} K8:olƐK_,% 3Ժ.Uy͜ 9{83:#YԉCϚ~1)9n\E3:elCPo٪en|a%o3UrU?X;uWABd!p,P_EՄˎ+;gsDP짶ɓ/>{2:Yi_1 _p&˒uwƙhwȍɈz˯=F_E7`5D6,x i%+ƱՋ̼/bVp#h]D@> l9]U|񖡽$Hd_* `"?P J:܍<ç<ۓ4cSNPPa m 6ʄ6o5a}jiX Ȼ+Oɑ0+/,jżHjY*EɺGm1Lyʐhetai-_plFmO5*n&kw8T=Ƹsxz fi. Du :DF-?Q:m9-+rHTl5¯ø+ INs%"BD@2/&lΞIb{P{AvԸ3g_|!d2jmK%[z4v/s2 xQFB 6?e43BCBq{Koʝ #GU+aŊAB=5~2EAGEuzq4/0OdHW P|> U.@;`">2>m冧7-eIͦn~ \Dc ]R01zg]<)d7ȓdT.1Z@)@#45_A=\A>w >SF Y?Vw)A*j7"N1sm@<q5ITbgY\:My[L}y[ :t)i|s3r`lEu#"sZ2'^t\QϞ5wg?n3EŖ~RK$i2v#1IlUj?UM՜ ʁA:?8.** AQX=i,EfP &@4 S{ p.S!V"|(T-)Akr(?$Xj[a=kDRCr~uϥ#uͧ L܁ߠ-½`G&n T)Ҡ"2&܏ `4 7<^rOku @(%/dK )0tzx?;Cm2vlJê;Ն|YcHaځÞcw Oy]sYI1 Oi"K [ໟE}T|z֝ =z[oMF.FNl0S p;#hF Rfsl8Ԟ\w?Cs)7m_M ѮA}_a4Rz2F׳>A@Bu[xj1Xb `r&0Am۶m۶m۶mxm۶fYLLWDEd\dW4?u28.ϤAdž&% i8Iĕw3.M嗝 Qj' GWS '5\{m1Wb*BcPJDr+ʸ 3缾Ȑi &m/)K!-:d9qCJn)NZ$xNIN_`h1/kC[sFfJpZz[o+DX(ِD ;R+~}4x]m;H,AGdҚ}!˥4a89hEV`P1 ZI,~ yQ.x-H9Y;'~oo(nk;8__Ղ!{~d2cV´*a R9X/%}3QeשY *Qp%TJ+e酣~znYOA^0ݒ*ފ[V8Ð!OSLp4Q|OVv}5ZԲlF]1gےdPJ㌝pNjGBl!XΧ{}AηlXVZhx : tdBPo dAr1ʏmw(828ǹэ5MXLOő!֦ķ}@0ڧZfG6QvԮ`{z6&WPxCܗXe̮Rh2EK׮jJ_`zE'ˋQF'k'3.Yn@Y-؂K^;4"lK 8J9I·;<=;,iC=wb͋~om;WlVNӜH1.X xiH؃#)aXuZ*Lr0@ْOq 7$'o㇉m$펨*9!\\f ط\s%졄HDϦm귱sVZ18f )hz 7(R-㠳$ z2FHR'l{ %q0apX,Geih3U:w5.*AٖĀ[?k!w~r*ap e$l [ YUϪ ]vd>?0aR2J,;l>"hG%\ΪrƥCůqhB}󴫟<£b[VAYE: K-qw^ , b6"r .O2:22^H 3.^wGB.IA8*Q|ȳw)!GuB$M m,51<)$NiC{4IuqZ$ye ww@_3fr[ӻv(1MR%FZeۙ[du ѓl%Eev7DAZ>FTrߒo/*1qQ,uq0@zJl ~q8oZ&ϯBqԯU8:RlibJq]_hXNF' C/}x ojz;--3HrW^9ыdu̪u`L6R j v|x MdJܰpjۈQp&FN=(yyW21_u\ Ld>UwöZg۶3Q{eY|1\faXp&F_Gtwur0o {ᬆčl~^\lބ +5KUXWNno{MzOC=sY!RG#*V>1Mmy1pG1*)}tkΫcֳ>v`q\ȴ\+Ʀn4,h$y/Qḃ4Z]A˙-O2yg^ gZ7ӎқ1b;V]_a".1ɭ.LSɽS߫G2`NG?N06ǽSIJs$X q;*lޏup5|qa0ow>nB]Wqr!?}IeB64ׅ##wlFNBk%CoF$#lW-ff61VLƶSтQf$nكlz77T5H_yRCE<]""S pC*:>r nz^ɯ"LDg}_+x?DXl%Z#''RL&Bk:O%M `R`g ZK1~V=v(?iR\Nv|u)TjdxPy d;! =&gUo 9R% Ԝ42S|hKtUs)AK2(8,柲^qB˅->3=|ω3wjB6p!SXN+n yh{)5õ酚3=܀d0!q2&v?TLR(b;ʢD6QIpsZ_vLYiHf~qą ?ٶ-8穫De1V hՌ^A~+8&^y,b@}!yjMUwA_eI38l{ueBo IA澜 Bc"h FY033wLҺj{#΃U`,RE+2s@Toޏܥ![ʆQZ9%kvpZCO #<ߪSX)~ROH[>i"=>{2 $od_Zljњ88!킪Oe2o^AVOFyGb'GKp}=³= ̈́NdJ!7y_2 sbxl2ֹM4S G~Xjƙ" \>Z7kPI !&֮)l|n[i_8`)k Ų[´Ip@0~lv!8ҨM[0?56#u a5%0JH `$?qC2Nq; ~! _{vܭ:(^V+'xeϖpW'WG9 \1T [Su`* vвUL>]ʵ}]2mN:uHT-ON$ bS;(T<دnpQ- ppA&m[N4!Ga"[I^F;fquģLF}<^Ry GW%/ܺ?R2tӷ] VF4J3;QV.X? g@V 8g} i8dR#!AhC7*U[--!Fzf]ͿC(VWم 8؟}f|;2xN]DGڔ.jjO\Ȧ$l ]wh}=6A梬jX \fEXklNdQV/Pa> !rs_Y0|!j X*'C(meX[{9bBΥ= 7Ym䎴ךO"_ՏW7C<ኧ!.BldmM?HK^'ռ;qvqٳ+ݞYҌVpo8逴(D ]HX)u͡+ɞY3z6#SD+.ǭL?:d^{6䧅 4=cT]2qhVF-t4'! O&8u1mϝmO}P*'҆h]h\g3nⵚ7I[2d^+*nA[ze߫o̫&dz)I:|C7 :ô ]jH @ۧдwT2ApIsIsJ2JĴ_Vg͛C%sa}}b̞T5G{ .}~ S}܆<"]+5UC%?2| M2&@ͻlJ[3=zaT/%&Fk'@u%?q-0aC8uiӽ8Ƴ6Pg!(s1.:HAD>WBv}"Q>)jIpZ]&Cn g<\2Pٌ^Tf#N^9~v|4_\#3M~Pc"$5l=oov(Dfh?A -'F/Onxne:$hX;ӭz<_P)ΤA: JIzU/8b7B^g s_i25{X㜡&Kب7@ DJ }#niK覊CKrCo }1BlQ_^^ě 2TMD P!0uTe:Ec.pu#?qOw/.ZV0k~e LA%oyV&&ߤ|n/2=➆Ӷrt#0$U`#yENwjY֏;>-CP)(9ή8X_$V9L.uÉ65OXx}{l iCyj&C;J22+Ul9YC0XxG*޿Cʣ_\[ MhOJ9vB2'imW7vJbOy6MuJCk`1NtjvsmԹ%|@X55ס3*lT^Zd tzr,ry+D0nUwph\9,%/3]@x{}uc ?SdCF c+X'OZyKp8\rv skm/ATIi'9tbS)]sfV/ՆZ4SwW Q+fn뾗/(re5dk3]|<؆*ۧq{2bglfS؅(/؃XZh_|,ag" rQ[9(<i(0Qn*tOt>sf41yL6qD#0֫}|jCM>yQ3EqJHv;LDaqnx>|`䉜9|6áj:׊2ޕx6飡Q lv :E&+'nw; MgtMKUY͞7/w̍F@[BI`o#euDD&KoEy.NZ}uj>"RNYRAx%VxD,ӆ , Z,rՅQ^?pyv??87mjLPoNj`O(2}'&Iʅ $Zi Ӹh6BXGay'Cni T }3~O㫊s~!!:C,0Y,e{ʰjy}H^2HuS5`x eW´K4zo(Pu~$&]_m#  m!9Q\5 NneQTcTbyjB4W ;/# [MS֝~1ۿA-|&145M {X8lgUYP0 { 4:OK%ݰw"\Gr2M1T$' >Tz ),;HZб=à/2:E,La {r5E]9#2}-d3%c~=:=PyM|U޺%̂O^ϣ>1r<\}7/ |$cqf=M>t(qlCy4^XVHϔ(eU1[1r-h7" ERME(몝ueĔX{R΁U,sm!Cl Ҳ o>F> A(ֹ#,y9x.qKI^1eȸ<_iھ`GS9n(bR46Sedy*[(Vqx8ټ#7a<=C#m=?*酩tL:9# I10?58VVxqD 2{I:S[|KTj1*&`9,z~pПHJ$HWMB!=I`5E2G]}gӦBF;R)`aj/J-%n<(97i (X;IӴ :F>˅gr{W{5[{#HWafS7IVj^#kcw+"-yAXH;ׅLW'_p 07C 8 f]qy#ٴ}IX\/~9e'Ah@,L=9QVczMP1p}hFt.V:Z9N{6giu >ST ,~*X7Y~h.YK!H^1 HOLN^W"}HBW2 ^x qtPr;[ 4k"*c|kcj- /*avJ܋Rvk7n&ɪebE&  8 gi~ތ1f1ֆ+b* :3@)j(>ڲ `| S1`f GM5A[ݫ.+:cOqfnjQyDw iqRnT.)(cצ{4Ђ3aH)j ion]7+қ"Rzێ(ՃGJ՘If  {.Ԭ09(E1 5%}Pb8$a©5ZE)c/5[uÕUpq\ 3שDO+ku&&zy^|;S;[*= ExX%mOrw7)2nFAZ胲 ҩ=K*Q+;me,ͱMZl..W`cjt6"e}T<.ӳ2Q;5}hǬ#m4Q:}^\KSL9 4vi( yUkT ȇ`Q%tm+ݚ!FY[3&%#:ï.$Lhn3i@"ϵz|Б(U `1pGeHpXqBx8ڦ stzYput)%@kFaw`M*֟pNEk@㗌SsxxֈT_Di[0PbYZ 1C~cw. e9RzjuJt_C%SjB[ݵՅϨL{#pFj9l*SؕW-+cזc&dfxPX[Vxe( $!qTe~a9+" plɨgdԌ"o-+!,XBŝH-x?s:V)q-35͊Mj=wDdiC@ȁTom5l瘆W6’c~K^侷BE'Abg<HG68$q83t3\ 6_Xw|Z+r'ήjnčԳɬ`[ /Y+ל7|8vfڹEg&Vݠ︗]:a* Ib~9HX)K&fC NޯO D-]vѥs¦b\xmfvmO53NnUFNگMBV1˥9\@ JkgWҗ)KԅCMs͝3bl%t6|CB{/CHYHQА L8*8 yT0P:4c~E)uӨaǬ7S,^{"NoT  &3 wٺw]mCC9IcTZ`hYӲ0B1a#/<" ;xkhO!~sƯ3S.haCz?_O8Uvi\H}+nwnOܟ >> So K$X7cè/f~$+;O#:ol1"^mE"I*7^=]{-5,X&7'4ikM5 YO`XFge by$BYB9u:9k{AðMg$ІHj" ^NŮ*H],?m)g3@v9n68Jj]Tw|[F^8 jܽpXnWP,z=q=+%I2 )GWOx٘ibK\TlE)dgvu*湇[Gh6qݴPh-ElxRD4[8X)V bV 5qև -7x̹Eo #8pՈwP*7D#^ئw«:I?U(PA-Zr4Fes`txSPn%b:dk݄{b[kR| <+HjHpZN9eXw"gYT" ܙe^QՀ򒢺&]Gr% J@sMU1c b KuWvlFv[XDh D#jÓ}uItǐ;/;\*Njxܒ_s#BGN,t&dӞwӍͣV88֐kWaƃ¬FϵRF)P[q}ǎ\^Ċ;+Ŵ<|N9!wA*y9J*!K]dv1?oo-]@za;dxV%07B8_!U %JW蒻jO8Sb;K\'&%${p6N?*$C}җgEջYKr%U>!^QF]Q\#Z83RBtwfoxJXu>;VtLCrT}HWh\#f(Bܤ(̙Qt}_=I!pe0 pF .]_}*& ?CaA8f‡è}[+XT|`pH1CDyBz9JBr|0k%? 56?R,g,S󌿴UmV_"h,Hxv>roʯk$c|\snJ^a鰂Kk'Y4nV:z2sN!=t_-,XSUC5Lq^X&>IS?T!~6+Yw5 Y[ASnƢ/*oٌ>hŔUg\AemKwo$[nӬV|Um|AeLu;x+8h|TG'U*1D癠Zn~V!PEV+U2AN"!eQƌ|K{6"gk|'9XzH{. A0 B k )tZFn7-b%5wp@e;0=Ps,m9ƀH~ F\'hjZsd;`9qաth2ESi* f N91}>8l\%5D[ImRCF/ݒ b8DדkfH4{pUYN*W*^lj4ODXUc\u"{~i09"/GW @G][R; $,& g@w+,aNg=gjk ' #ezk Πʳ-1}- Ne|t\!$"?O ]Gy9k{Ӹ*`I${T-4Dfӊ{a%RTW/ּ,A108goIl:(Y3]WKEu#ww<pLtu3Cdxs4BSm s!V#KeWS'Y},`-]D)[Ö "?޺)(M$WږQl?C ^YF@&7-myhfUFQlH~C )&+visV}ΈFmd}[Hn 7au|UnM8F1+޽ODQ;=BJS:wfbv gm ~3[FiOAb3vj u/l>*h'J<yiki _q<*qdG aB'mx(kwԳ9QȬAqCN\^MD/)(72ǀZvOf'a{ 4V WtL_c+5ѿ*p9if֞:$աU_3yn!2ʉVijFT6#~qMv *'r fTPbt@p r%$2dkFZB'Ƒ*Y r4uq(ȡzw gQbb%K1o4:(6BEh֊rOh{ dl.QHdr/=:ҡG7w4bx;Yu9Pq@˵NzNi b] u P69T5)MPBoW1Vڭ/1XX#D2h~¥d9fvPО1t" Sp|U@nI>/ Ԧ.sFL(o0L{S֜)"D&z, J1D7mN qM^tS :adUM#sL%9آo(ԅbDrzKn(]9490 \~bDDFP ̦'9{(\`EN˴»a/ T^Mkn^\5Ĩ GjIܭ|=U@}U: zvk /,+ơ/Ԟ$mKa0ΔsgJ ,՗)G%eI764w[Pwlł_-Js ] 'qID60Jsif>(CdQGܖyxlj8̐A,8@4q2x?Z_k =9ʒ2 I#$q;KڊH1 _q&UuJ!tE!z4 GvՠTTbgfؙ﹎Pl)|gfM)U,5;I]"1.^Ҙ8dP ,H+:!'r1*H(C0(˭I4Dl`T;mS9Ҫ_[c屪%:tH0R9 ̗jC>>e`F߹ ox/!e ا* {^VT%.$Ww Syl Q9Tb\9t;i~oEJM=\I7y3+!CvT!ȁN猀[6ĈPO*[4ߊvR-jsx\Z\^}_VׇǪ^v{/B| Cͻ4 y V!KdĞSM\U9UdTcv_ BD;>tP5 1Pb;6g Fpj[Sb]=Ax7 /#Y5AΉ3-$+?\ةEI@ WC,C˫Y:&}QWt*jiAV?',G8lE|h̉'}$J~7ͦ-#l]H煟 Xd7U.iJO.vT+%)t:Eh~TcuL_T=K>g Xt>P45h!`.-13Z1솈GwW8u5.ޔ6E69j65] @5,e?ك|i*L.=Øv֘TC/2 Sv|rYu>7Bfw@M' DOTL,aS5Vi66ǡ,}b3?ktIN|\GFs&؉y_ ߕwϡq~*J.4՛4zBBSvJ(I#Z^t[$=2љ#rg"}i&9G;W1ЀOԭ5wƀ+>_X *f@H?VL9žC2%:&!lO†;ZGcs!@ ;E~}j]nB"A3Vzi@wwb@_tn[]u,T-^L2Fc۱4?'4v5b,KĎ^f es@5䄻C*$`懢R #?>+*pۯ pܹ$YK61>PPܥj H3ЎXX*4;1]]Vr/X;h֎l=?]ƴm˴'^}TIxv >=+l_x4D?ҡ }U\(I_ov~T 7a}.i~N w2ږ tN.H:!_ƹJ1S-ОOۃ9em͸Ӱew8C‘ O4*t<hKPoI@;p]ieAx Dk$Fr|1axM=.H]y<2̴&ޓ3v1J܀B,z,yw#i CvC4~=T ;uFsU!wmnF@A]К_E/ѢfNm` 0cmSb/3ҳ:a xbD0ocRHH~pO+y!>uM'+"mw:YMuxlr#>-|(6CL|PtTjhx |j/ՎM~XXԫv8ב hO{n36[6ekYDMiQ\F%е ughR:cJf `㑺梐 W}ѝ+LMan׌^'Yxȝ(֘Au}o[Q3p)3(m^O/<&К^QVV`Tg3Th>@/r%4sV*ڪ'^5\j3nv ru(V_YڞE>Z 2:u_OWH^F#}Q6C=>u"كQMNT%5aLF,lCQ4X?YW*ξ>(z1Oe?ogHbD+X;ė)Skn7$t9_Eͦo/tίAl'X@Fşm3Tnبiȷ@t1!Gv*+eL* J/߼p{D d X:V,G}3FQx&ڣ_#sU]/}iU ά[ DLd nxsƕWjk: Awn0x$%QG '޾K5o8O|e5Z`J|?!󙴋bcR~4azg0*5vw&聮EJJ †刹`"Z<٧\۳PegebܠUzĻ k .;_|)닰d oierʻP%QDso(_*e'5RSw=qˉ/yAt{p{!FO_<)A.?u~dS<<);#MQUts~3zgY}irΘ/ãq' t\[;UWMNZyǭݨ-H\ŞVa!GIzPXQgZKM7~ڪ96',}F>Czo`N̓xhIق knE ? ;@g$Z4|Gr<+N+fȘg)OUhRVerB;uqJ0k Z/C zY$g]gtggh^%,l|P~@Kh6oZ˥؀{J&(AЈ" , <, &S@ʕF2=fݚ.6P÷5i9C;ϲIx99<44?X(#MB9IH5Аm(4 l:4n{}n(TXm9h!y-֛HM)j4($tb7|ͫO=t g_sGje<#Uڐ_|| G@ E$ z"[(**-1VnT& f QFM'9GKgT)VrTzTy k]3e22)~ЛA8J,z2z9#l;%a;4?7a?$VE[;c5{,hVLVDglu#XRW26PB<%̖UQRgPW)`1]Ksok'o\tg{K94Y_A2 1)/ByAH 4N] Iu>6Ero|兩t;1} !7c{j?OO#f 9vGʓ d&ƞed'`2 쭎ɲҔ`J-:]nȀ`IP0:@#X1-ڭ"V턻}_ߺ%R§4iFZ¹ZغZqרb,"}ӦRTKMq?ŹɗHE 8/0D%{rMJ=X0F^YoD(1ľ"M}؄s=#:/1 _xtxy, p1Nx-, OWlb"PrZ#6ml;U_$&xLs]0HB?CJHeQ_F>`4@ `?J8@l!yYuô3aOZ\X"m$uo5|ӃYԓe [ ^?dz&Ā|6V͖Sv~$TEFJaVgFKyRީ:RU&}s'Xp~z*[ 9[3YW51ju$GN̲BEGcDgl$M3>B!(`=?Gj'gx3C:?llde:m R1}Z}Ra T.@n ]E>mcJkg8kإ\W"JAc==F%{dP6{5ۍ̄iP+o{ZlHLS av>4-{i?ʒG-U0.i59U(sCMG{%,YWY1_:&ا5P ժ0[g;`v[ LKgEl|J%yZgOS#G_cP0gIucHhsm ,n( o W}0™%^9)i?'\$ 4^^n#Ϝj/qp~0W?)ْb[T'b++Ji͔%8aQzP]&})#VonhjE_ZY0Q ZWL"6LzbN8}/iҐ%ftT7((eLõ>軪ϋg5h=;rau@Ξt%-\Ƽ/fv ~Y1a^߬UGčXN-ekŐqB -w,T4x1~(k( f/̳;,8xn#* Ywo=Uxl`"JYpv=d<+}@4 66]]+=U_Hv'{ezٙH: iUPԎma@BJe~PQN')? \Bnfރɉ('_ʫπ x$]6~)% F46Y&Ab{۫hv %>i%@۲C_Uu2O+\Tђ EZG፯jQ? b*#!XrVc6D]ay֞|Ah--"t仅.;n)1'#,0bkߠZS1G"?,+M¡B40!N2jzpH1Ҵ>I4&YɃ 2ЅS#w{p30"3E7v\HE'q,MZbNcqiXVZ.`n#wgB-^%H| ƣQ՟V7"tLF ,w*G@ ks+{|ZX.~e;7/IMf%f_H:p9<Rdl|Ei3adphbyTqcnAk+_?a&>:lײ[ gӧٚBJ,0LmaSE#hŇ0GlWIpÅD\ր/n<#l|jjSb+e*1?PL\ y}R[47V%3MBjIzAQ\.츲G>~N=1t2X&i)ZMVxh2KeB&1zPp` 1j{e@Lt b y HY@' ,UɴCg{ Ù8Oz:VN94<; ">:$ƻ,'@?M?! `1m}w9ƶϐ K$T=& "2mR}"{|8JecpH\ IJ%_JXX正2ov+`S ZXjn}k[`eFOKj?~-t/)ӛZ7-FL.|$}ʙ H}x#Ú랚U0MsŒ-x9Z`Nx!x%jm8vSĬkoFPPQbBTv 6E0H+U?Ya͎j ٰLtg'@Q$yiݴӒ=0 &Xi)zD\Bdue[ޏ 8sNw:%H,;r|4ILNah2O،^3dG_[J3A}JP 9aU@gN,9bOq6B0%N$yׂ"̰o\tmn-/rՑAFcyϕm{{M31 70Vj*Z]$JL217]8#mhv+-[ !mI5L.FXP(G:p`]p 9 8\0aNYm11~`hf&qӃtA> 2ٸ*?ox!)ogz@g-6f4G+3M#3Aab4n֖R{N_x Q]̹a,Ä/fF-4bH )Q6$<6sQB2Ї+%܃E"}U::^vx&l|ĵb^-d=1N"r@xXOX^;MFY/T@v~rY:"ŠR2s"l\x=he ۏE]'m3= ,%5%[*6`pFܤJGm1Wyѻr~bi[)<+\͌Oȱ-aT_3ST)Rrxm@+)IgĀYIбGT|dߊ*'ɪqBA,ɬ%?g׌He3PղŌ{L ʮ)yK9P2MpΩPxM`@rvj99a7#%x)[`?*xq[KiP|TFm.9IJg$ Du/LjÃc CVc{"qpeq՟=t_t-nAXi3(f:UϔAXҙRE<eQ0`c63!t.sS$>_܅&IÐ2۾7K>p0OiuReFIq($r!A5k\Dsr0F8:X^STd>:3n7nx ͷ'9+TV[S+~Ew6 ;61l/h[᩺?y:i{}`4y讼@W}ۊ6RwUw?#C.!b+F HΕ&cVb%I{z\qV,V{8ťwH0T({M_(g?xcM,b$)"O&7Rb`gcj\vM" 3pѤ/)JvA=BsaR׼_9$)dv6Ό$l7VNo<%@!\5.{q~R>]"QvEq`c?GDgC5Mhig ]{o3)H~R=v,ҜVprȪEM=K{g`U"bwER8tW:ehBQMPn*9|\"AYӽ G;L|8ŤJO+Xo0GU5p}ip%$uL;ya_]΢s34J(6 l^Zwa0_hVC \ٌ᳑n(R`Ai >JC2,6Iމ0L~z (O$Z竍oRƈ|ID2Z*+is` pID JldeYh\(ۨBNΡZX4@+!Ϋ,0&D1OS7uo3c3W; v?+siS74^^qo럙-]*+No[]y,8ut><VznZS<1ꁍ~GQHCpYj}Jm$;V zK 93咔GT{igH_62;e#l!&/k${ǡeIj @l~TNH]YU,G >]GLbMo(@_㙀cVQB.0NnByg0ؗWO}7bG$!G#mn.Ŀ" Wlcsm8s VINX \șzQ ]O$?$Qb {AVQDLψdAq™8i[l*ˉ7oRA[vr:TN?7nvA֚A*$ʢd. WAW[u-<?$fbXhȿOpC؎hEy;6_]Μ+%w@BEgrMoIz{ɮGzz3&P(QXƵH5xd~g5*M3:̫8bsڡٓ*hE EЗ-ki`{7LGPyΦ'u x508y ԯ.YpMoc+cǬtK<`]EHir'DHB8":+4xRNkRZ?gq57bKɃlp1g׷Nٳ!*:?=p% )mX$oO鍇0X4 ̟Zߛ )۵tF0YW{nCy/L!ҕx8lx>ٝyN2ƞ7m9J{߅k,Zj \"σyTj4!:ڄ(tGb/t6%xU͠G%jF鼯Y%i B(gu^Vwwֽ,B1 '7 D}&Q>Ґd [ɄফY5GY%JJde-TuywaܸIuOP/RϮT0wtf l\O M]8P#QН2ɅrIs8⋴rD<[b*e,ь:}1pfŭ1toT#ۗZ(h;D>uhV@+'*{ Wuyyhꖲun*ê(Z z 7>Tō8HЁjѭN i@eUlC =bpX4K)H#,9d_(B bDꃟRrۓ;+S9؃mcW H%merypCD[nt6e: X?((7 o_;0yL]ѵyAdxv^ HJov;Pc/fՎ BhsΑ8A @wC>ΦT|FQf& (bͿzFQ4):[TfWfZ=64qO,bMAH[3{YPػ4Zlf< DsUL!c HSל}ၱ8O/Y[1Z)k!tA޻Wi[:7B?fk:TH@_ovO 9,PYbf y"/e7W/nWWaOuY_rG]@Uyy2bد-h!D$aQ2$\Qf9'F+rߏ NJDkn'ue0]kqs̔ gpv݉ bYOdݢޖ6:OBz'Nml: W ًOc/UCY]@w̻ Bg¥bWQKa{„L{Y60(@u0^Ȑqk!;ZJiq.N ;MmL4j$'m[s7}gK2mrUN웵8M;w.@":f0:ϬQz50TsLOryqFg>YnJW"vM8`CW crhN޸JvnQi=ޭM08=D_֋CEz=e4 SA0IAvšp{8)pw*Lwn^b g Va=Q`ىok@Kao)KtQv%|'| {K(UtA:w^ tb@RC5Y/Z2S OOD khiy'[%y<$s5Zx}K0k[M _3m|J7V0G{ P F8!50NND? OFVfHU/J`'1KSSgo? ڡiMނ MKe|x_D+$S w5CPIaHoS b?3U_֣z;qRI&J$J0W1 qƘ'`0O7Q#]#G:'q) $պ0e *! HKo;@&l]&}!R%㊨#" H V~R3F$$Ƥq ̔zcÄY0j-)z{;O;;I^ 5J^zN!zH.kfF=|kkZuDKE 6ҟfuzL).?]> „Zr؇똧MUk ^c5N7ol,cI@%>~/ |}۪粣i]oDx;T|u\UW ic#J6 P54-d ᆂs8 .Im0ൽJETNSJ1ݡ`'hVE#_Є D ݲ)0{.|Ô|ܽ8/ļD2+ in[v|\޴\19GQ#\궲){㍑ w TY5vDȮ -€\oFB|KpN24ja5"R,y'm v \ AU8һv:~',adM tnu(u7ʐV5JDrv bqe%ލ]Q!ցJ/ICNQWa)%Y4%N#`'xي nh;wuCM$|`.Q27>_HOԯHz(Gj *ձ5*B ;C7Bq!6:߷.+t[q[nҿ`n Yg^ߞ",G !w&Yc쌅zP6*9IgwZ~6GeLYy3GfPdRB$\"ڱVkmvB"NXQܖ?TUt Q~@@:?{Ԝ)`'1۾vs4y#IT4JԄp*f9bRY5x;g뿟7$=F>]4W[a/ex< s%%Np#؈1gΒ - t;os}m_=_]|--Ňg&{@}iV?aff UgidtUHPtq7omI,3*,zTamS4S4r(R%>=iOs, +|PċhugJǮv/*Tp(,##6L->9ғeT9⫿4iLprUNW462up3=NŞy^̤l@Uuuw{SW IA"LG7Qs0[Q# z? 0tqߕpuDh]bDJs70$no7!70IrҊ:NPBXA6EZ {baӬ9E0&!³]}蔵M1 85$ULa{ G1M6#p1N5t F (Aȧ]VpHBq٥DXՠDp׎˳MjnSdXdud:<#o&>NsnZ2pՇͪn&|6%kƒ0U[.mK .Uq~)u?Yò4%*vdRg7QgM[*#K3>ĖKw&]|%`m [3b"/ ="m"څBn#5΂u㻋|U %LN56ީbW&J\l9M(dVqvc^Ƕ~Pqu(٘P j 8rj,/|x6btwC5dz.&.U 9iK)Y ^jr9:JԏnB(J_ŧ'NHvwoAWpg1E 8[]03rk-Ia c;A!C{ܝTINGCm$OaZ좍Q$d$E\_ȪD|d D绱dJ4?y^^8Uڭ6NK5! IbN{b[uGmC.q~]sόrRW6:Muqb{f qIww!z/lU&Q.w#w$s#5eK=>* `Hf˹}_&ˬG,>iJ(ߴ@|4VdNLߝ@۹ԠEޟ6ytv#n58Y1u]QޫzZhYFj 'MjJ!{˫TW@ 7>14SJXw˗(2"_ySsHiY9L«}Sy9 ~._:k!6h,S֜D7G\9d AYՖ%"jP}V+8OxbzFFr ۼqv X8 sXZ?Yܮ`'쵲.dj0$|43;ztdLIV*B }P*57e0]tEfEcʧ@t@ !KhW-oR KQe" g+';cdBzWS/9I^>4T;P[(@dwZRL!aaUn}a/?Y-F}[b fBbaR.3{qL-RL4MAl|맷"􁧾2㍄ MML x>0 u*D衜|9w`(~&I8~P pt#՘IL$S0ePWm @\HM;].1m'1GDZBb{ V S<(:g Zx]B4<6U {!"kߕύ2Z]).`LE+Dк͊|@ D:c(HWE mr0j@Kr=)%?V ?x[ba^l}JvшF;ЬA)pAj]iA [N0wM8 ߛRo9Aį@I&V>!vJg7 oB^&i[Q# mêZ9oLBH{7%&U ]U)}t,9. ;|m&d(r2nWaU?ȸvt} eJ1UR7!۲J\gjR'E>n"=88Pbh0(>l vREr!-${B\DuT` #/kB_JĝHqt֥ NgM)ΔГwd|62!%슜^VE_H:8fFF.\%ڿZBrąI졌ɅQU xnSd3󶆏嘶"od&.8K}.S}2$Zg1NjU Ɉt`1}V^v <+۠|zڟpKmj陞>ƉG5Nb ]2y>(N]UxGj KF ۪4˂n,p~2cOHamc-ʉr M%I P}a0o`lS!l`k7hSшV䖍{o ?x /雿4V=AVTo%]㶌%2S =.Ss96=bK[TvWUd֡*c8^0fQt0"%\"(bw{G\N-%*6CrYΈD-Bh1"GLȚm$ApV;}_Ll[{IƼX@R^n<+_Ąo`ك+MX Qr1Z+S7rn@p`CN07cU?;K OFބ#yZXŠEE]րXJ#Ls%B`|7>Bp3>J͔u-@ 鋧AA*,21]xCU\*ys*>u-7X>Koۓcس"nڑȬ) wPMsNq1ѤK(:ZG՘ dqysߏR=;?M[#'\â+f/͈2!jv|,w)j Ft㼧J3:obO؞I09R lb)["`;gDtJ䪾Qz+WplY<~.TG+{`#"Vi~iUq3,UY+rݯsД͚ Icz<˔bSf*僜DA<֔G䨯?7w>oh\ʊ*6┠=%A dH2y'kZzkshE7Lu4сRl\ y~gʫ*S u8&|_ّHQHօC? xcJ>pZ)^ 0a@"J<8qLDVC@ZMA%}mQJP_RKQcg g9$є$:V[\J>Q &3t[O;ұi!q6Ge?Fv T>ZtCVtDSM6hQ^?ù&}j-1DZOBa}# @ΏUs_=v> |%;e϶lu#5]ʶYꯌa"_a7c[[#Y ( Vx)gtL+ ~}}+R5绕} k&#XھD'.` XUAf Dla·s\^<) No Uىpx%[bĥv' $*]hbyeG"[34=.|!YQ ]\{>7mʛ"XC/^ l,)1SN=w 06"%}MSL]-%`^?@T,>Be-Nn+$<%5 6;^8y3v .%3 >''{#_G# n&hʫ~)WBLAdM|ճEP+/V|Kb梇 '}OqٳsHQ{Rӯ 5Kl+`u~bZp w&_$6tz8co;5oy0RѣnLЭʗgTWNO{1tG&(w5#IJ۲[WDIuu jU}s=X6=E)"c~d- Oc|l,4ң+$?Du<9T76qJݒ|]YJ5S҇Hj(XU Rrd_iۊc |y=뱢a2 ,ԿOpdsqӸJ\nӘ)KDRNHCsҞxB;W8e}LA KH7CBa>?dJZ.oV܎S3+s\U k7ъzt䓇gNS/ޡQ M;" rU; <3R "ׂF+ =⹥s#^1܄#}n~h7jIa3w?1UU!'= HJC{ogbNi:pfツi˂>u)q_؎$iޮR&Y_h .HgU\pa&Įe"ۭRܳ2ײHH |Νnr76AʂձnCrRݯNU9u} gYL?YX(a*Ǵ{I\Uw/AGh MZJׅ9Gx̉ÐA ԣTm{gG&S [G8@i`+,njaOmHO4ngBSkl5ŅPOh8 , B>߫h.>SVͻ%8Nߢ#bsp{-[c5[CĂ*o%>hVuڡ~8`[29bvhI{A76*IeZÇ-mF RƶS(_t֔~—Rh4NPcLbيH9 X9+s&v @t^K( 6 c| 7OtPw= / %Qb0ki`zo=hOd{Wh_eVZۜ`rD>jNe^D q%!Pר SO<F; 9O0g}ҫnv9=dm-EU4g!,\ N}Y$ YϩsciHDUHImcsRV0Ӵm/sI>04hSc畔T 8ۆϿq+O imؓYw5K{29~t) RFЙ13P@S ܫoe@-oOw3j6_4 Wӹ WM$#O!"Ć8 i; HD6k|Bd?{SSHxRl(4_w9|].&<)hd*`F>`2"qRR91aQ !Wg!mToLMQ5֮]@Grx=T(h<6('\j&qڮ&qx=ipf:1=wcݴ!7IAY*EWJxBqA 5=Y0VFgJ9Kaoل-ND(OS( ww_ׁ2(Ђ3'b{ FY%R1P-SHWHxE;RS,S7v*pz{l'&D ~-GE< !N֯T teGbɺ{-Aȕt1PZp.lݥLɛ9\Σ;'G]is 9,bMWBz_!oꝵ#8nZ(b5 j'Lu[8ב&uA3U2lJPV_on!>Ѳ&ER; UV{'Ŏy)=0B珡 j(RjKek|Q&E: jw^XHBis@`"E{'E 5m$I !ӓ`ZܿF%CLTދxBb͘~V@Av._>[#jލ3{8Ѭb2@n K΄s%L+efY.vrM .\20Ec =2=c_%/W &)mu65[a(֑s:kR+H6964 [}bڈBmOҚTcE1dΨʍ*-)bo {!glKeye+ )B: a襵SMq8t_t]{"OE> >PzM}7jkEg7+v} [sbn/ˣC`/(AkIcF1?ul]SY*EʸPo<ݩXyᝅ|*yns6/B.LBq*.\ @D'0#(7[Heud4:Q,zdYt!Hglg #Ni۶n[`FxvC1j=#ęYq^BhA0k7 Irm-K4cfVQtFl:K21ƀis3_&ᜡ/bX mKo'&7yd3s+Fl5E&Ep˚FI9PTW~ņ%ܭ,m,݆6 V = 2Y[lד6~]hJ6@T5N_Dz4cHSQ]pΪ$dwcE"-&5+vL뱫;{9n&0bƃB7-:ѻC7͇ǒVs9p뚕um&8G|-Giu#ZzR|"^e2]2:2}ɡU(Q"U5sUM*H;9kE䎬7j`'XI8C"mn,sWT$1ZU Ps!-x-<D vz!Z˫Ֆ JW0rzb Ov"J eC%5t[A6h333T -u[{Ӽ)N_S~-3O8iM)h5~r^6Xθ 9j Bg5W(jR+J<4Ɵi[NuGt񁜂7y1@'ٛj$Z-foprWo˙3 ʊUN{o|cI;4CvfB͆vJ{e1 :N>?mx1v$&o(zy鮧+LH2&sf=TX' #GC/Eׂ/KU}:~覉]~] KF!'^_`%2f/:2˜K(qνhsH6ޭ̟ORu v,E(`zVl%" bt5{'kea۠QLgVRTt5>Wߝ-גhT=~C]Nۏ$E-N])S[(!p%7+9OFԧISg0lFySNJ GZ V 4m,E=DZ! JCeUD&'{#[o|Kշno@4N njpcK\ڳt%zXN֏ĨcaCf&_u3z-d!Xf.1'pj^-cvG# 'lqީV%YX]5HqW%DQ.мѩihXƴ{MwT&1NEV@V{tRipr,'ݺMKd l2&Ւ+@XI:K4uuEЊ )n[CQCB:AU$ RauH_n,WA [bgSlIT+h:0S 5\1 V$$mb,EV֡'H͚5[G]ToR8 |>0b@OBJ÷ȐIћ)!yh<wx =Lc,:) ~BҁC5kڞ?Fx1Q(9S/@RZ6#rpCXa&z &M0X2,g[\}ZE;jZȔ¾M5t:<#uZ@D#bab6V/˗x+ =}'Aݘ6z홼K5S TjqępׁGR F> {&1Cz8]w DSZY75:Hݫ6-GC~>;\/j~Z{ ō|bS81fU+BLԘK&A<8 o߾w7vWJypE8nEVa{$>IʞaN?]jC(Uk]ؘ)}qI(VM$W0?P7WZIYq. ŴKyvRcTI{თPs sumT>T~{p-sa(o A˫ \}kl4/2rOO."+*?=S;,O, M/;WiSZˢ=j:u:@Hk,t}ZCaWKeߎ1sBYtFm.Z>0s!7_"E>JI0-P0&,0t!;.x e~=O8uಝݞ '1mAFU$0Z&Ȓr!g*Rt/#8t֙][ fS/p9Cȼ?RV|3HР zu5a6b->)hIZ+Μvb|716P P} C9W ۬|,y~/@ hѷAL72&tW)L*JCl-Hg,Pw KwZ>s4ddaq>#hel ^Bm S GN`-wx:=p1lp8©waW3pcyzzT>9568]etw|XXCfy%UMOGc\y[.`.5=4x+57Kے^䎟X; -")olJfR#}p$ ^SqUc# kIВ f`s uiZɥM>{rF`4v7AԪP"f)ħb~/R#ݤZ+oٙL{pS+4F Ah'G2I{7)C-KHL掯6! 4ra;K `b!f?ym먤w?BN\לx60mPM$o)x+#=&S@WeȤIvuŜg@e0@ͪ<=xNP&p|Jh.6w 51rϬW!x:׊s2ћ#275NVr*z5]Oue,ew0:l$W:=TvA͸S8 xEdLjKͧS2oj0&" :.[4 %B*ˈR]V kyޭ)md8ٿA!-6C\dɡChYj(G2sR|έA-v^4fl͂ ~eMlnDB{Gy!#.O&S3=1r VkyPB;!EWnHQ#1`#K*yɪf[ 8u88ڃe[&v{Rp7FƮ8x"v` o ݢA^OQ>ʨjwG9]*:rDv'"yqQfRnO]OovjbH]^<) ioG#¹Ũ9޾!B67Է6A65Zyq-,=saՎ`ӛ5'jdYibzip>f!rS1P7T|EZ'SwIQ۱yxG#ĩ=Ϊɑ @#q:/`"iڗ޸׮dd^Їz+>hT0w5΢iYO7N-y#Lv7gbZMGK TcpԦ4Zp6tП ]wb{Mo{Ӷ|TxSY|-rQklY|:ue[8akXb޷,) =UU`&@' ?+KH\Smv4x2Jp_qQx´2jH@l%5&$~ij:'(i.jqўäx9{Udү`D v5dj~Zf 7~D [WQmv/\ߪ?~w(a+0Gz7 eW{DҟQ!}^ GeBZ96gp2Q8_BKc!|B/9v75_s=BG7Sb7b;645Y3Pm=DYNt|eq.fO_qߛeJ@$g PQ!M.VFb\# {s9 w*ߓ_]ZOv7Umtyl(C}ƭU4CLON"~A(_Qti1gF R_ں=X }4rKet8[vYw9Յm^M;ߚ&N)=kkbSB[Na C* rAWk %Rxb",H#L\ݴ6|dSHJ35쓚/Ca6C{$l"W*uh? NP4n4WFn05[l&n V*I&W}ӰQlOCj ȧwΰN}V$a[}f+k؄4erIMh=[Cc!1)଱G'EFtF2| ;ABΑeEFDF8dmNu6:e&8Z1+)jcZq-;ȋLhW#{1:G_nĪUj F;Z;6aXp{gPW $[|4k +I^YF N,b k'u=Q. vFu\&ayi!AF'J><%QoMV8ZruʷDx*]#DH0;Az`gOIE]odNRq}`8qS0e1FMĮ#kn6bIn"1)K"śV c]?gk. ]:T;IK+[cŃhBWr4.d&y̱/j;k _wcʃC5;bJbk{C_ ypĿqHX-:(z#Q7-i8<>mr({THWRIwx (TX5Ӻɹ&K{ns;H!i_8e^ 1ΆU&eCj C7BMX/RIV@Fh5 -3gpw1:OQ7d5$XqlryʙT$gÿK[N`|zg`9b`7c:ZQJB.ia}pMfR8) |mj=@S.?2Ne*y;G!:?Ef%B;%{3.ܿS[e  bGtOST;d0Gކf9CemkO*>M%As"%̞B^̜RB?N> PlJ4-IxH4 +1-gkj%5(QDx4n|KNS m$>:3<0uΩ{}^U1F#똤c(rt?n S`);@M]o`[4" :M)Lu'${Obh@TFOdPd}${W[QEc3/9H.n}={T|߽PPdEVt fޚ~@XS3Y]lD\a K~DNm ǣߵUQ6VP=c>e(,6jC48VHPVAec3*{mΘ QlUSD]<ه{c5.CH4Uh_㡜 wWDx ?p7${ ; C(,\oQŻ~^7>jz~Fw -E<$g2d2#__`8t.X|>y!S^ʧ]t~Ä`PiՕܳ6N. ߳"T8$ p4_1v+9фFqI},#UTXeYEi%8'pr:nXF 2\O~W*ZvSAFr.2vOLsLl=#߲֗Wh_;j<ZVEM;n36E䛾emk ZMYuLBRZnul0Zz<[@xIw*etXFsDI!&9qݍb\?@d"M2Gr!a cDQtFVIX(fi*oḄ$'HSʎ0#["gǙԔ,e FM*n~I GpkK[2/=yM6 O\JQK"Lo%w?䉡/0&IKifj%n+)G4MJ36l立1)_>vbg;{P"N5 ;"BxS,zw(G2 9:Np&o"K}Q^8Pu\ֹ׭h ,;;W؀_5@shw}ڋQ*0_9Ÿ Hyaժ6|\񹅥k^}7n@4Տck+y$I9S ca s)ψX@RHH04!753ExOl,w9ۑ /6h ARϐa{"l R3s}f|&y;i6Vp3 4,LޝZ0_SILђ)ř$Iu[Qg H%d]ᮑR{WY( C.Y^,MYWnkؘ**pFfSKq<0z {IԋVSp*z )cq=Ѭٚ,KүB (Fhc%s]:RXxΩt[qo@j`Y&x'rA/E!BGiUV`$~y,a,@Qk{Ҷ\{P{='}~*c t cș{+] &[$u f/T 8礂^,2fPG7z)wa T !۰b.,H݅34 4z#EɋUcd<0V9N,X7NFRwlbAv7Y=JBa/pȁ}uWP=~T >YW[k186BhݱWK<>}#qu3^y\'{ۈ| p61>f-34ᬉݱDwytzK A&k ѡs4\ NTy} ciԵ,C"6Iy*ݶoKUǸUq@\.E J3VQ||bSԏ/IW@kPTk0<ӟ&;MY!E1hMw4EO`P\P^'?Zg!vjz\>limO<Ys +"X\AI)â1Ib_/It+dAWn]MOJAsVt{}m*R&{eA=OYZ:[~ݤ){%\PI!xo2O͈},9#qw7~WjX¨s5bRN.b%co*Pe8rZz#Hb m3׆Ĺw?Kbc3  aE|_tхspj" R¿~ռAo,ݍr+]zwt\hNTohdƍRķYnsXvX0i NFa2oM@Q0k:3~?XNC/Ѩ~I ÊBa13 *w;HQ5;z٧NwJPJ0 :%C qB44jYgutR՞Ϝ飲5@@,fؖnmבyt[Km[ɖA*&o}`Ǝ"KyA^|7Rldx.blRIl!WڹͩCVXĹ2mAŻ} Irnv=]J3*n/Nl-b_orw,7cOws 'WɰP襢D[I+H q"T9Z d{X9_/L2{$>uJzG M0͵'2G'Ǯ1H% LxTі{5.~XoB /&wBWq^}:F|ڊL -e-̂nL2 (u1/6ar@y05E>+JI;n<=/Q~{b4}׺̓Sܹ֝rjb˸{;G,FQg<w)vu[Y Byv< E[}Fɯ͂ݡhhկUs&@ޯ\[w_{ۏ0iZhՔ+ `jg3ÄL %G Pth;e2Cf}[ 'X!U]Q{/ a{F\oʹD^|!v1p ;zU 7pkڥSە=U 8z%X)]<ʱdM Yzjjs*WX*E|8} g#+ee-}CNYW(,jTe[Uzq:Vg[͘v1inN?jU[yS|Z/L\I')_Wzboh%ClI|ƮDŠr'*i!2΅b46p]_GÊֻ,!o'<"Ysӳ;o8ݮšBaCP_k\U= 't_#w r(OoZ@~_I샗W!%V1Ykְ=eVcp~^#mTv7e{ hanhnfMo.wFS=2lg^U IcsOE\Pf-䰝7¥'GpYgZ+p:7OB3\Z/͌ #+gĎ,N^lȵL.#{1﹟[˼әx;'B.=-H\ύ 2I#G\Z@LJGB[y M- lǰi6j֚z^pmnl@~@@^8l>" ΝPiyC70AaO>!?N=CUIBz#l v1n3;Yc ;Ƴ\b\KW;jKT$QOv$}8OCyM0CkЧ!@&4tӻDC-ZBUgTu2-[WJQvWK׽*qF 15ը(_L$E5kG)WzI]8Dcf>\bm4>;GmQU6+,']1ޛékLd/R{3zqD*nM`5ӸձVf i}<.ɷ2Uu}-Uaû3>Pj3 D&dL?RKJco.:='S ;=1>IwWIc "Yqs9}MXR f} 2[ԍQ3єC:X 1_?!X f/E1^~ N#: ojy.A7tLc? Lv U{\tX]C57sQ_D4}@CZ?G.9Ot++l/94Q]RV n*Wqv/vv; !ZCHDoEvL,ه{7k.W^&*I8ZV vE;#DɫME:ka `^Ⲵ,L5fiq5 88y_ 39 3WVPlʙvw x{b1 ݲsС N,rjGK!ʬt^~EWT}O٭]WL4%/JFQ*8S~/5*א;ƗaCۨL`?$X5ΞY݆}82A%*a/ q|3(4kK}QBS?kUJ)3:rBvqojEmAa~AIJH]!1ҊO1ulC2G48+Ҏ!9QHx{`N;@Zם(&lX#)+&PÝJe5ɏ^EܦbXVmdͳOD+FoݐagѦF#/x EE }gXMɦf!,ʩ:` kPְH< $O@MlM[<}^ψoΒ z`ɢ!ir7a;}r7w1!\CAzRH~@Ĺ뇏Lin9!**&-xlgCS S!X._kpٻetC%Z4DS"Ş5ATUZrRT-;Y>gM2@:bdJl^.⚗izʻDJb!  $6Igd5_'Uώ5f hS){̛QG4yv+X%BWRc`R !M=PLSLQG0Q_T4c֏9xExCh\ǖw'w1~\dxl`d­fG ip keux9 k(v=_]aAބ'f7G;?o&6^y+p<;H% I4{&>03'm2dmNDjYK0Z+־jP0ѐi!p,A\<;DֻX\4Ir0?]D>X}2ĝd6%9ൡߕ>WThK<;5xKPt+(?њ9T9y>"|$\ Ý?@r WފE 3hʗA3ґ3G!\=.Q(o!N7sK* {SQͦ7҄׬q$~_-x ,HkT ><*ȎYLs/n^'񔄈A~bo~k_0lΡ'Kze`0\|bVϐ8j-)+kOpi6 : `xBtVeҊi N[]OI˹x阬Lo"ZCO3`Bn'Өoj^p)`ӭ/3y@B_$vU1'/&` +#jQ PR^9Mz/N_OojW 4 4fa.~@.SL.[:{u\N-_f;X;2]Q>[c)k\\XkFӝu")Ac^1R6P4oVmܙK;Oj^Av-o@e,C7SdL$soKm)yU:4ZNx2U G',qXRMuMI}.2{$:(籃֪DmSnx벃|75|ؘijQ>RO-s$0 bq}cs(ۗIxWPcdl+\g[Vd`š; N$_!̀|= @7K~1vL ==;S5Ϋ)G]?u~JMHg˕8r#jjkY!C8$ 2 +c8b5{; +}Lz}-M2h1u3"62#Xr#K 1f.*H3'sgC2ĭ|UY#5Nsy(.M:c=qfn10~-=zi5W@pXq嘭k^$ZZq$x~%xDRW-e{Dʼdq F\9}>d~IֈjwoR`Ek">t9-z-u02B.COW+}af O0BL2:Af~%/kAR&M1^pk'sTa>iu$j4:<IaT2) "j l(&'s1|yֈF ʷ:Q2S3! ?HV\( |i?*ﰌ% 4T:gJhYZ}su<LL%zG"%tl=tSĊ6qgo=Y&EyRJ [v*>mjH$RJ![-Y+،W/p_} *rwm Mwz"F2`scw:^^"_m0RkHR_95pyZT ^ ;$C>dkH*7c$N Zg].V's8w`c&d=+ Lcq9~(-'|q31qݰ+UBǪɿx1'w6.=:EJ/ $ۙ# ,U_WcA Iqoˁr|px|ϣ^ ث?6d-ko핐.4 C7ѐt:|S~ 9/1e30ux4oAPv489jN3'+Zh>ԇ|_6_,z˕܄ihe-kl4nU'@Hg?GTs ~^ ŋXnC!i@BòS*62gP|MJVѹfgT=&M:namBPY_o'ΰ)¦AuiE&y0Rkݮ?i,}s;ӐWcT*`t"2Hn;umd#"k A/\p6'BCK:92(m0|9!u͈V)Rċ}ϕKr^qF=NF0//Ihn[ʷ660])FӫşvMq{DntFxS>psP)Zl`uPq/s "*O_5!ց6{\QtfLlozh{]mjk t-"oߗ{~XF_ID?yE\V)S!WCB9մ1+O5w]=09 S 4Aƨ?99Zc?ct$ͷ>Df>s,AAWUDMӶv AC_.P6n*$ af@݀L*Dٲݿe9i<: e#Br?&BgHDi{O~#+]5tFiڡ{'3𴥒Q}4fNZ)Q5f>9/X||HE{JZyW C;aUr 3j@\ĮN(~N AnD#h'q!faEئ6ɮא>Ȭq7p_ Xu6}Pt qzgpJˋKBdF^/)C=dw` "8ȁ.#@/b-(av<3ZƲ]bfNC]Bc+nW7P̛n'Txn1PNANC3\LZG-cc#A#H:2KIpfU?=Ȕo8i%=؀9YK"C|u3,3 ҋUWWw.na0Q}) D ^Ee[ 73ѓRe,iPZ(/JW߆NGYIΙOQ^ 2ݝWQ%J9/c <)N_He M3~FX{0|;U|BJ aM`0-YtlkSC!>\GR0XCZR'$ֲS^*@ws_JyhsB5F6{=P$/-MrI4?l$Rr| X.U3Fc5wW!PU% u4bBOߩ!d(03q5W[6l[`Rx~E5Mg)C@6v]ɋj̠k=[+ $f Jgsˁ, l&MO!?d6`jkmv,Uk>Uf NmUy7>d)2\Uj@jZO""|l^){gӊ?i\ wxJv<CRI3]nuВ,Sl䶢=HiKȤ1s%.pX5uIlā^*^!{$Fq^%Nq]7 ;7^׳p{aʍaX;0EIHTع?EnӀ ; \4KJRrPO>}._JT? Գ+DV3"%TFʩ~ͧC2ްK0_y\K$ʮh"Ut(Bl]}?x06crd'ch"͹n 'Z4wM V,"v'XD/usNqq5ySv/$%@T _l-ՁBSJe2.h% =ݫ/n:kx4^Ld 4ey4*[[Ag,? IW=BB[7Ϥgg #"m@vέ(=rGD޳ pziu!7|6Z pe3>F2gHY <7kEn7[1=`?˞l<1Kڵp ZDY+<הo1ݵ:88 nWVu,/N B"G8.N-un\ Z2`,gi-Ԗr+wV(C\X ,_ SHØ.S +<-YD$'wr-g}uORWRzoT..iY.ZyFږuZ{3''Ķ_顢 ` Hn4``>#] IKHxl^5,,F'k^;F#0$ybӹT 8qТ.#Vũ2-kǔaÔ1R#r=¡UbM/=T:C:1!>ªڇiaɹ^`IA#A@D5](@ҙW&y߽@n5RENNyV>.Ps%qJf3a\?fAWã0b1TݘSUm)FsP)Z8Gȼ~z>+~r5Zfd6;`u$j >6$ Z dK#3)j`=iOn=βBGb=^䁍a*`>7fg9ɖxp J]u./ɞ;2aXzͤ*OB 0zK U89R;llG&0LEOFJ-4Az5'PSoG]-|(se>9)y\Y9^6>-cV4~5-4A>-r` mgEK z"=qCإPϟYʲ5EGW"_S# a$GS caj+&D ˆso;ٿmILa9D`+_$ i~A-'Q0jЌ!ݩơ? T3Z8a-5CU/KE&x9'3̭DBfDv:L?K[<G|Te0YzV?a^q0NBUBXYk+~<$>YP[/HFk]e%mu7QN5haVYiV3 ̡,eUHCT)R 9TKn[mSsw-C Otu{71IxVi&Ylna  ( 6`Е`VrD2=e92Lo*6G2 lpl5鄅 …YԋZEx%p D36FKiZDzÓ2#??#‹zcLQ gdCo].'.g ބQJ lM DR9)[޸wڅXkJ_cJ8G$(V\/d^I]0_mյXjDaMP!u >yán a;!/|u:sGa@EVU/xxOۚzbWR8TJI!pEFUA֮x=3J+Are_PVKp5__]^hqYDMoIz{ɮG{z3&PQ<CLh,i&"k|UǛgzuuH,8bsڡٓ*hE EЗS_ki`7JGT}Φ'u x508y ԯ.YtMoc+gǬtK<`]CHir'DLB8&:+4xRNkRZ8gq57bKɃlp 1g׷NφC|•4E`=Y$74c1{Z,#"_770z$IY8̆]s8{a v;as"1iwQTtXc!rWc抷A|SR !4dEK>"{B9īj=*YUk5LnwU5rO}I[G!=Ӝ 'T)p E1f8X s7$cpFb=L&7]͚9"X'(QR"S/kʭ>gk.O+~z6r}v۞W3P`FXiH9ąz Ao4՗I.?}@(ǸX4#H.GĻ~ &Rơ]ػsgVJC!j)۩F5}r ɶc@d3PvoO{>Q!˳CPsW1Vf=,xEZ g(i8q*nđe'G5n%v"dLHjP.%o}Ut UJfoQ¦9H@ama>P`fw+ň?y} 'wVΧr?Bu _UJe~5mJ%)?<.I0&Jmt7VPPoTAv*Mk RZ";*@8wv^̪]#e %q&0zz}-??0Li2P0 ŚzF14i:[ɓTꥉ<삍t$emNٳPzHkbY;4u3hX`D6A'< 9_csQ3iH7G&H$SHqA/;§K#&' =]'˚LD.7T"@!+W72?\D}_`W.) N/_FxsY̜ ~h s|FOC,5AL'م!9"ټ9Hҥe^`#T mNw_b+/Ȏeӽ~fWfZ=64qO,Qb-!H[3{9Pػ4Zlfl˯ls%u^ҿ}UY]@w{ Bg¥bSWQ˙a2 *0bK-g]ày%x!CVĭjq<+MH抧 98I4s0Rb4uq2Ѩm[^gt;2Ftþ! ,@ڶJ W &W9@o՞kH6޹Dpqu7RctYj"aNoG+0,w㌕1}ܔF9pV߇дqe,hű{[ap@#+xȿS 9XoZ4zhpai5>pVSLzU&2}%/޼ Bk>;z $4QqM׀j'STEǣDQINS6)*1(H7 ;gi:1 )ɡ,-+iOD khi_<?>TK#G;ydYBĶ e ˥tUla'SC{fMM<  mMl,L L-3AGbBfoLB_?hYXYXؙٙ?.NN.Wa_ ?NI!+mPM?>n,{^)6ѰR~(];bt'Q N,кj_#ddE j6T /fic*bl?+aд&oAdžb>ڎv`ORk/1Tiv t2[ СS4#4$Ŕ A}EoEK%ql"S((u^4.:'c.qo#0ۧ~zIkUVZ-q)0uG|dn_r(D饮u{s#o*?J6VuQ8c}5Yo>fXB`4ȍvm֧7oYS1ȴGk"Yƾ*RN/-K1Tm:[RttA'.ܜk-PPf AXs6]9C~k(JrET-naAyZ;ϡ&~BH9L VDn%x7]>2pN/?Hx1l[#^˭ #be _!vPsN%Ne[QlA<;ʧf|$ۄD]6.3-XZAPu㋎اPOS"KǦUr"+MN'yJ:[` Rw|9ԇQǐIKl[d+k,;rATBd.zxZof7k\HӠdoN|ii^nДb+Z pEF\"z!%& i?TM*qr(\; @W>Y:jw85Zi yЮȕ 3/ +/(6V*?|nad7!L‰UKst@k.ڎ vKsv0z'0h!3Oܷ5ocBSrШ8%S6ܵyz.U T㾻'Vr!tƐT}FL|_uHiLܡc *WȢ}f\sdWls=aZ?7uT[q%q7&tG* j7,CIx #lWIH[6-f8機'Z]B@OE^9`%c<knR{z#Ʌ֝8"T\QzF@kT2?MeYAӑ>ʋތg"٪*nMdWϴC>Մ "m۹@-O*$l+Xּ^sw` !'P-ѾhKB_B;[qBgvĻK@D4C/Jk |C?*Р҆)[(ge{,Lҏ ^cPNOV+ylVShj8Yz!&zbi: \3/ϲB(|XEl_0Iz/wϜB#9 1y>4wJI NC1?sIy.;i{\1svϳAum㕬r:_NBf"0@T¥n(0GiKfj3ŴM$6»${d-ɮnRLڐ-U1ҟCW(KivK?@gif0&uF7zV Z^ӷ%;!]W[ey BSBBf"oJX.$HKQlUS Q$_$\gZڱ,m^U)3")%{xŴ(0C7 ߴxgא'bc|Y ȫBm*M~~۲F[*JT)LQdWߟ6 7%(l{+/\YA6 ,$~hi G97!ZvepA̰7ĬAҍIu>zH|++!ϫ3Rχj*y,^њZr mg4+JCՈf}a&D4 蟷Ư''Pʅ}Jn0O8tû L"rZ"@e;pc4t1mt՟N+’{٭vu{lGdL@.P@aVz9E Wjfʋ߄VC{C R1fN- w[hYoxgVkjnf*rvےno+ISNErzVfxg̢ ZSΒDDw; f)ߍULyFsz?s6ob'=^FGR2AAFL& tE6y5\jZyXy2m)T.v|\+77S=cs# zv魬 (Y 3(H&^\(z®\Ɛ8v:N 텱ɀ/u}e{a]\#3WD9 ; "삁d bǞ}h诐&Hҩ5m)KNa?KHxZpMjtJ {/c栋3]d,pYnt8 _T4Ejo9۝+){l˞;.n vq{y({թ'^Z(bVЃA1VRSȡ5*ћ8P@|,}R`LDU2WC_Gʞ3ϥ/p~t\wOcl4#k9"@Y:&QxiID=V j`EF^bbNxEk'=n'! Q7)ZE7;u7+{S׭M}t?+&V i$>Q,̝'^.&C2@+j`gT)B+Y ÇT۬ X.7f m뢳S.P!JswO1_IIҊDӠ}πA&B^]yzY|,*7ܩ &HZ-J2iC{mv_bFmmfBFJtp׎2J52,(6>GW!Dρ}iELG!P5i2| 0-=%G'[8;/T"9>@~ѡd)uqe咕~ 0.ī -2$ʛk Uhk:ٲܺt?IMS" [ZBFް]j<\Z85xGB,?j;E!;+mќrJ.bkg @x)czH+ϟ^K:N!KVWA=ڵc1 ;6m|_#Ά?ۑ^.jr?)Gt-!0d9ڠew'w+s F HvDLޓJ 棛݆] Wcġ٧ߏ 齑Yai+c$قWipÝIxf#6$BR` Gs|m, НM~.Ot=CK5v1*^al7\JeN ?[)=ewpp(Yn2>SD%/ـ@(6<;fCx 䋸6^A#>w[GOaJ |6J8Bə.RFϥt,$cL3!Emޓ]G婉܇J6pqԦmmh 9cI^`nDnO4*&\v]nx)?3'b?Od}=ٛqJ]ό)g3h]ܯ3ΌGCnLFԻ\~0=-AQn ay =o;I۞`._X5^dLEUvA2%zQ`Cz ČX $AJ'BmWLOդ9M1=W WJ_n>ٞ36Y+K* k`hl\Ln:oVcn_ܷ1}퍼k_O B¢]̋$񮖥bOZ{Iʔ [KřkԖ^!T⦮jV{Cn1Pi֡HTw@ SLDa q#SޖӲ)@JJVs,99Q2-B+i@.*o"D!dG;sŷJB& \ۻ$;\qGC,@iaR?.㰀En$d޾0`3Z1O`Z1#ĉ1:T.Tg'N@@! =xTBfM^d/$S+|]j(j'(_|TTw-GC AƹtU `PDF #._nxzJ}s_֙l Et9`م Uk yeN_Qո3.c="߂+@OkXOvFs VD %{v2#b 46#j, sĿ7%D^n~نXmbrjn&+7(wʆ $gS룞+I.{#hĐ/juBM:?:/*-!3#WD%j((~U#  4OԗNGa )YM׫_a ^8<)F(ёfQT'9"aH k_R|Zz0 `5`DR< 2"j%§ZOղ)+AR JҊ՘ƹA$)5$Q\j:R|Pdx. K xˎlMbn( *|Z{.©j.c"lA۠z;kÝ!/dVp"IZBĽsHw!@Gn|sP>ܦ)ma6a1Sm(˗1D89H'KxiĚו8Wpj/.20~ 9QGJWgݩУ5d[}"+otp1pi3f"h6CI%:z9#0@:K |쫶SDx!pHz)P64 1r@ l) DEa렟2qzhos3A@*4Q|Bצmrv$< bYO`ٸ8< 0`I ɽ[߈,(Յd[BCLd }V;D@|z!@NgɱP|73!r$~gXK~m2.0 B]S>1V ZkʂH\"~b9.X=.zv3dbjj'Ubշ]ī(SX%ov{CE%]]!T}۝y5cyEZ)NLhAnQ{:|lhYrO~éID\ypZ{'8T~v;?|t5pXQ˕g%?R(4 UkL-k`|CK|KT-w% e=HaaGj}}o,o)#1S96%訖LVZ/Fy&'ܛ**Ar8veqw0/)%Qv:5u t0 !NA^ ´`?Z4dv[ď^]a*=U]W%LA ĕē^o67:u0A|: Dz{@ qt bS-uЏ@Ө ;jB[0={m XTr8'o㇉m*9!\\f ط|sœ%ܡHϖm귱sVިZ18f )hz 7(r-㐳$ &z2FHr'l{ %qaHX,Geih3U:w5.AٶĠ[Y;?fmwo|rXrGXيR ~ `뜪gUԦ ;2{y({])Ū W|XY6SFRQa.gUE9,4yOQ}np̐"Z%xEӻ EOK1E9mN'VceKc qS{nKt#!{f(>҉zY󻔇K!ko RRl&c`'Hͪ=fm`Ԕ܆B-s2z S/ESB3];&BC-Iw{2UC^WM#*9oEϷHp_ȸ(A\Z l%6֤逸QdWP7DjsWBZmVeCw[nd6\1w` ys4,'MQС>gdWenk7@5xŬ6U? ~sv/zb_Nv juTТ}a b7,\jZx{7\_hܩ7~3/j>bU΅!dQBY:l|&mk=!8UWMχU@kV՘ goE|lg,ܸMpH!X'38H*ZHZ ǡŅ6MR3_$PueqdpΎ״ 3Er/e1p=XDޮ)`۶w JJZGs?ݺ@~# 5̺. ?w hdRĝUgͽv\ڂ&"C; 1ID 9ZMKolدno 1s$Qs$j1bRc5.NydZ O@cd7P 4Lȗ(1CN-'EdӄW39-SJJ{-cBGr.Q/ְ|l{VߘV|)޵#߈nxã'TAAnvp!$wEøvJ ǺG8>$_\`EĆ콳["w56İxȏh_R#d٬M=iGދA(Vci'PWmYmwq 'vsW!t_[Tk&VK,^ز":.s5R.Eh5lL梸!Iś+}]M,V e zs vʘTzh 8/ZgdXFtDQHP[Iªd~9vҼ ahsSٌ09(dxJl?2aAT:=kHX0foVŚ6]E!Q﹐7烧;jSa3,MbZpYh: QcX3&^}z 2J[W  C)84Zk%j2 !'p"bT)hߩ'$b$`[,c&Fv*Z0,#$3{MXQB*K6OSjtPDdJ{(V8Y@A}<-y"5Q k/YH}$SkDQ$D7ߒDh[I#CbcVzYL ,8[kIc1Ɣ~Ϫ uM\*ѐًܩ?84{J σP*Ϣszg6Wz!dwB!G*XX@ӛ \<@b?m SWj.E>4/B͓J‘B\sD;shbnK65m1 r7rU^jMw;Hf!ձ,AkeI.[Xn_uVqN/jӾDC)lKΊjzM8۷X?Fg2[SfdAWgIy;y*EH5c?s}I)` ޽0&oA7$'Z$۩<tW2(o5Z|-h,afj .bm/6\LwEcS'Wd9pF ð-9eUd &Ob6C'd(@#WYb( *'~Saٸm;W4y47a;xe_ljukeLp6ɽl{~Rlom-*rHM,X$!t0HLzݛ7E+SqߪiX[d9c&?߇Ƃ6 S+Nh0gcǓ9q⮲R]m[8}jRi-!\sY@QsNfvǦ~2꛼/L_0mq<f>nI)j3*=/ʩB"H 19hTB og&^J6r5E"Jn25Cp1^MIS9|Z8#WH4OL+C}"шSx}<~z/WZWn(ߨ)ͩlLfz \O nPݎ"KMI@NY/R!R~[t,>l`#:-^5v< FsfK ̋Ռ3/2wE *%|t o נ*pA6KM]kS ݶngx R$ 5ei` #>|BqQ(?Ra|k(lFEkRKaVNH~oܫZG{dv,@C@~6nWuPV5A:-UN: $8MЃ_rvXiclb9\P# ߡe%t|km)lpg4t,랑R[H(':v (Px_[lt3HM޹%ۂ&hBnDD&/%v*U Gxf$x+##J^}٥d*oG3.hs;fDF2[b&Y6`-Hᨒ*Jv| ߨFtO lMb6=uQ[z0jO7Xs^cw&bH9v 9yF^hK*uq"Cr13wgݣ LۘarOa)p]csnkpy+gGu'#bg2}([aӝC˚p WW9Bl+ڿ1j/&\p/mߚ5-LNHOp>وbַ5r!IW<wb#önN6xhtDe^_:݌36g̞=T,f 8h<{+`ǑL@q% >BENak] Xe-pMdHϚ ֳIlA~o|:cVHg O~Zސ@3>FU-WȌfaBG2+dw<9/<Jy:,o? 8lM@V3]'iK擜+2׵~EPŵ-HyKȠ{yuBLP=% UbW8aWВ|qKmuT1VJ~8<N?)Pp.ɀvpUIS ysu.o5O '8] kzb2Gwp_T#/HJDMPϫE#_B ;رx.2LϽ^gu?] :lu%!g]I;&l}.m/ YE23 \̶N>&alչ]HO ZVl.&;4 Ä T6e3|>C*WE9_/HLv`˅oh8cUA!vVM!!䟷7; gןQN^'p7<~Sb Z An 4vhwo=BwY/gKr$$=ae}LƳkҹ_r^ nGtwʺPX=kqp nNl nj"%{RDtSšN7(̬q>8me!/V//MUL*pEz" Kװ"SFp QLaފh t Nց_K;wG-qG5P [դoҐ|I+Y]YoR>?➆Ӷrt#ߥ0$5`#y%NwjY֏;~-CP)(yή8AX_$V9L.Yu65OX>u,DR`*rfB/-KņGhBI=;`rL"MR8ۮ. m ^lmFk]]|;Zes9nOF>Ul {P 0œ8M@e35>4Ӟ~a-ۗ^KeؙȂ\(e*3mi*$Ai۲ ݓA1ݳGX:MrL #k-LmjF2lSz^G hs+<yw"Pmp8* ǘO#=s{T8GNYf9tRMZQ]ܻϦ|44 =BIcUdču*`hM[w_)G?]훦>-;"bJyfXW'SYܿ(2b y͈I^')_U}ۿA-}&145M {X8lTYP07 { 5:O&J%ݰw"\Gs2M*bgxy=V|n^-S؞aЗY["Vk1W G=rף>k1Y?֜q&?Yu㋿Oo]ߒMiL'P@l@IZc0ʸbx3T&Af;߃8v <ELdn+g˿B*јȈ`VQXaH"#buN2b =j)AmMV8 !P6 iNytE# ` |ZC@iƂ< T$Z/3d\ίUq4m߈m0ͣrK7^1)m{)\VKQPS <׉El^0yX[!ܶÈQeAq|ƥT58VVxqD 1{I:S[|%-5C0HWUW?8kO w`j|HA&{Pz0Ӛ"ib#..>]гiSr!z#wl~Zv[×D[|q%@UX,4N [$iZS` ‰39!)~DҺ~@!wء݊y{^,Alu|6ӕ\%{RabtV84n#sVrx`+x\E'=v7:-k?W,G㛪x?rGեrqC$$t S4S~וGw}_2 'DA;# ac]igdaMDtm~LEr2.\{1\g [;"2YӴLq\tPݤqC8bL;Mo՛U<,t7S,~QCGz(] G[q˷} 2f!X rR߻B Bߥ<&Hm6yS[AN}I^'UQxTOꒂ2}m7YK-8ш$֏SXt"Y(B^$7@/Bvv^3,!_tyOyم54o(!ԇkyH+QS2pѿ (f(C-UcAlUT|PEplj}kYzN9 `2Mo-5xgt4m]aFT=7#UB>.X\~ݸ!^@myJdyױ:^gb^WOͷ;kғ:WUayQҦ)X̾$GywB,֊nnՊ>$(^ /Jгe׶R6T_wٵS>i=2!k܆pC8niZҝhao}aNCIWϫZ[m@&@> [O"-l[ 1%zᰣZ灋~5”k,Q'`CJvNnkENa{QKg:s7\5<ڞ9Q =jTo.sSzja姕 =O*?Q]]6؍D__m; !5CxLw0{"izL8R~bT!6aFo^vr:xbMP'g?dE}Ricd_OzNaNѴ†(0tT?ibp,x=`.gYW8$jW7Qדre_oRû_67s<ϖ 凉lzI!*k oo){;t1%t7ʰuX"ܑe :_~=x6C^)\qJ КQjӾ m}  h h?pjQ*+(`&J,A&8Y~t`EbYc,BJWPNNJ Vhk{Oγި@͟V>-=]z { װeer[ׄvV[z]O KX 8YH 0DD8dsU2jnCuT~!FhnNYr\k8ږ̙pK_w~;Ӄ}sblS2GY:9r*[F;9w$m>}_3v$T:YzE-ґM-I ׃ 7בBNҟ/Y ZKVyVٕQ؍z6BYXC4lCpxE4k嚳\}FǯL;AÑߪeJ=`$@((VҩC{/9sBøSKnEw fWti)*^]R}'|&#_w'wOTW&H?Rmyrh.ETյ;Ϋ%}j¡91a>w^!/CHYHQА L8*8 yL0P:4caE)uӨaǬ7S,^{"NoL  &3 wٺw]mCC9IcLZ`hEӲ0B1a#?<" ;xk?=d"QlYLspa yx.BKtiۥqY#M=u@:0Ą7dU-Ca]XaAWHnǦQ_9HW4w:)GruJAacDڸLuET(ozp/l zjY QM%oN0hFTk,( $nID[[s ur6=a Ϻ70I֡ _ <&hE,?B/]U:KX6~S:kgv[ r,A+mp:ڻd/٩:rCppD{\C蕯Y5zFW4K)dRj/v1'#%~Mc8ظSɊTs`\i;ơh[ȔIޚ [8X)V 2bV 5qև*-7D̹Eo #8HըwP*7D#^ؖw«:Iv˚ ^(ȐfX6tk-JQ55Fټ+/"]64 n8n6ej:c7?mu)U E{V5P$98-W2;3,*]b LA j@RyIQ]{#?%@yD-_*GqXT呺obI;6=Im,o"EFpi5)o:~Jkck]ݗ).%['o̐ <%Ilw? sN2gio;n7߷.Kt S =Xƹ]UVSaavZ!Lٯ*xԄz}t='i1 VȵwPZfsݭewF.yvtVp6NS?*$җgE{Yr%U>!S^QF]Q\ Z8RBtwfoxJSXu>׻VtLrTSHWh\f(Bܤ(̙Qt;_=I!pe0 p F .]_*&C ?{aA8f‡#۫XT|`CpH1D3yBz39JBr|0%? 56?R,g, UmV_C"Sh,Hxv>roʯ$|\zKGLD\Z;'JgP66У5+5umDtZ  ZElaƋ*Ea*$N5!L"gR Y αpI(̩ w37}QyeC+8 *k[R[}#"rUf flO^F 2>x8ZU) :DPXEkY@wZW];hf ʏV۷/;*AuVw8c'n|I})WDFe;):HBu!1g x0a-8%^яi^7u)RpT+ծ$l$*኎B#K`L|%]S0껊2 ךhj^Cp_^c5.oEy&3*&F[ֿhgFӯRpi#TfX,J֞>Ěl@VR)D&GG:<rftF,֝/5'.7b5aQhCiUw8AؘA!Qaaau&#ys;7A&=iJ4j<;_e>n k]-TO\,6Ҏ #&SNd"v ׳HM2<űԓ`.Ոu9u؜ I`p1R|ʚ3+E^DH?D%A)i;IKsJD>"idz$[ ;x]Z]H?Ytv =f7f$vOl™*>$'w i6Hx7e˱eߋKy5T-} 'vhnyc)aN.|a@,o]&ЄLAf1BThY:>%.(D*\eܙYSt<¤oQ),_k,QF3yG;Ic7?5f8Jl:'XG={TL3z"Oc զ~l{3'h4c:L )Fv) N\{nzFOE0qY <+DgaSڟͻ[~ACX]9a n{^ǟ,ֹ>0F[he^$<1h"c K_+eSf˿>iECĆ=|goYbQ;}~> 0r7EQ[r3r&6U!(a_3+,?+ 9f?d~S{ε'-OM" HS"Pr&†ID;EDHZ- 6n~#ɚe$C_7@8)<;Y-~Q5?9",(YT +Ty/ xZWA7M1xMׁ;(>Tg\oCm"Y=tOKNmZ'zmtM=w.F@-6>Ǫ3nEV"HaU2C>P"'jOCghu/ݺK>jq b)' gda}m[%2a 3y|ıƙD{& a%;pk suhҍ mvbd*T=[dWR|B{HIaR`7 sԔJ0jԟQ.e^33c< Pa=:MD^:Î~b b%h&2I벶"RyЗDIA]f0]ǂfQͩ,ؑFFA5(՞1v{#:C>wJt?o*/HfL?GǂŷŚͬll;!Yo1(9G|g].\r1mn{v $l2'AzQWB▪j{$~8U4I=4[1 #KmcW: O޼@~T P.`F&2 ;uȴI\軞Ua9JJh6k)cr a ނU:rh$0 vC/љ߇Y֡XE'STMP yz~˟n8h4ĔmA4/y[ c߷1V秆n'G}]RVykvT4ѵ9ئ,wy}]c #tPP@vKB:̱ ݀άӼct;Bl,'lyK#4aϩ.+5ntS%)~#%%\MnK_teSnv"2:]v}mȼMbcyS{4xk)v[ i&YP" #ۮCcr yڽ!,"@ 1Ŷ?-,iR ޣ$LC3Gp+ֹE":7;D-ӊ~Iۘ ^s j/ā]jN&"C ,(Dipk. d5/@-%{~U_Km=qfGuU؃僰eUUBr q2ua$m1N%5 =Aȱ_7VԤ3͕t72dWћTQ5kSEXo7"z6/5ݧ~l5zcQq eךP+Wa90ۼLKر` dN6A95UQ5MvI!1j DsOuF ,Slkm '[ 5Kn.uwhjн<%\hQ9b]l IZdt pM<2a*uH_Rb6-J\(/m9؁DO fٴW2eĴ I>} b?%r)Ůj%p$Nǻo*±xigAGm=tLS٥>f^+F0h"^*@ڛ-kv@:- }h'PMd-iKO _ 29K0];uuU L`Ô]5\|V͐]C)q.0UdKT!ڠ- s|1:Izx>.c9`]ļƎˆʇ{KиQ%xMt=Q|\کE~%#-/:m-C Lx 4SĎGh~ppV@C/xFJW`rtFKXTk NZ^a!YFSa#`-19F̐ЈPe >_w5.ŇCߋUVD]JC~=4{NIۋO^p.:r*/&cJ+p#XSvmn{ Ё%rbG/sLq ֍\U ~r!~ Q)BH8TX;ϥ^61>PPܥj H3юfXX*4;1]]Wfs/X;h6֏l=?]Ƶm˴'^} ∤<;~}ؕl.L<J"P?*Gl$l.Է:f+Aհ>5@?G:~JmG}V`$/xuө倖mhggi؍;!o~'PB:KD%A% 'TK8!M#5#9odI\o?Aaef[bɏFkp%n@H!kez=TӼX4!!o*A|P_ڹƷn|NRdqhM˯iQ3ge0±v`)\W <1"XEw0GW%?g~xxkY|ֻfrw:;chs?!&ys(_'*]4`@#w8Xco)glFCq̂yjdRV`=\gwRLa -wKȣd9Q̿U:UNHCŃikSw;qˉ/yAt{p{!FO_:)A.?u ~dS<:)w7NF+yα/VMvv(=gy0w)k9c02.RɪG\.r oxʥs]oes@T]%79Y~zv0"qG{F]%IjW CcUُ՟j={,PiB E:u.% d.7Q%x7,dw.쳞I#j'8"c<"~. :ԫ`҉ݰ"5>)}PG8 ?\ЫWiS"oW~ s *<E2140jl֣\XnJ\Nߺȡ~9p`tET%XD5&&hxbe4/UPXU˩SQ%[sw\I+ZʦAon0+ֲa{AD猰:z 2zH`„U<[m>nܢ9Z2{`&[9Oų֍ZTbY]@  mp0[8VAFY&K\AQ_xw-CCx?Bg<ͽqɝ1/Md& AWpIy6 FPmMw8eM) ~{/ťi=`^vVy $?}@7g(9w8R)$K7YD7_4&3?Fj `9VsֽO`gQO1lã0\ksSx9}XB.Mj9mm˧ 9B]=IN""Ba"f!́Y/ {+S ʑ't߫LOd!$7UƷrf(jbL+HďI"·LjBqBٲ)x/fz9} CP"{>GjY'gx3C:?llde:mMRq}Z}R T.@n ]E>mcJkg8k\W"JAc=Jl , mj; ܡ)Vz߂n?௑4ʟ&i2$Ҷ%۪qa\,3ZrL0QeS84[JXtL ޱO$١\1gkUa"vWM>>Vkϊ|= J$ ןǧ`F(Ơ` -&6ԑ:ja%&ACYPPr%($@8)c`s>m~OnI2Pi/ʑG9sՔ_`~R%qөNVV͕W3)˄qc$P]ms{LP'#FԾH%2^YK`Lc̹DlL}N8/iҐ%ftT7((eL#>{Kg5h=rau@Ξt%-\SƼ/ fv ~Y1a^߬UGčXNc3-e;ŐqB -w,T4 x1~3(( f/sܛ,[`gݭT }L(fڕqdwh3|@2Fw]7z$lYdyl@}UQȼ?|s]TDK*di7F0{YohOb:HccY u _>MAXZr츥ع_+KwhbthkM5X8wh6Q x„p|;ɨ#9Ht$ˉ RRϮ41qDJ֙Y3)k|[(uϸ}[hd\ъ+jMURYUI5L$D',N;bh $t.hGzGt6`-8TO&ͨlH>=b;gyOxc2-twlӪ”7K CddFCF|x\1*7Վ5X^;0=H _^%OMǒ7 !O+\BS+ҷu{BFKg}Ey$3~lc4>ls/P}]4R)L $Kȍok3.2Jl_IP^t#:c#FacD &d+ )ݶ hE݋H)i -ٿd YOHl Aɺ?ZBiN|ӟ!S ;D$ R)< )-I/ޤZ34ИdM.'*HkؼCNý̄6Ffؑsp/"DZ879k9qgaZUh` x߃=m ETzd0q;8FUFX@1žb:ܩ'֧j@jc?܄vھ&5.@}1!xkHf~<~c~_Paԍ廅sJ~ㄙ]+o%'8LMf{k:u'IJ´3:N\qB<A\%O%rq4ZnnPG@hMy @!0q'lZr{VJm~b|D厗L6U&E[sGʾ|{rdLеR<+3yI:jrfיb}}9!~xeB}Xe LbcA^b) <N%AX:i+b-C5gZkQlZ9Htv |*.4oc\ذ抜}6_c$|ŌT;>C..erh_7ȌJ?he>K!PJ->`RZ!P܆ N0Vk:#ur` ~r8))Wv"Aϛĸ鎿felu |550+sl[`|1] pczn߅M)sӅ#xM K`8&wkJjÍi( "x ƚt?ZÍbu?F*STP<&@ue΂h댍C33$;%qpP&Ty'R8^Y"v6oMqbiH/242d6M/Ecfm"emuΞ1L*bfTRC Ft_ 艚iC-`3%-&c}(N ^Xb(=hQ$bK}\# CoD˔V,ЫG]@Y WE0 =]^y^ oٰo9VJLXV=4k26H Yu%/VS*g0+ VlE2i2&ܒ2+U-/6M&Z4FǦ?hLvσ٭TAfNT5;Uu {"pVi?D|=VzΞz"M+s.p]+KdxCB̛cn0I'vՁBK"xN4#嵪]|xkh0LmVdm.iӖpABT|d[X ;]q}w@'gj*C @ϼ^:!^>ZOƄ{:Ѽ05|@i]ywl3.7e'fgE VVۊ6ST>#C.!b+HΕcVc%Izz\q-{8ťwI0T({M_(7>VycM,c%)"O6Sb`bj]M" 3pѤ/)JvA=BD)kRPrY͈HDD2;gGRuvCk+7m xJ.|W9t?p).(f8VEұ#ͳw54oxی}=僟T42fjQޙ65X؝`Gq<9vqNj۽V "f1&l8,Q,{^nyȳBZ_I㣻cTk :!3zbd!\#kVk Elć(ՠ쵾2K։$ (3;{ג>pS9θJ)k[>u2˺a;lӝfo)ڈc= d3ŏ:߀{taIخ4wE|]{NMԀ%V[VcT:ScP/w:ⴲ]^ǀ_bcߠZc7Be,1SF2 T-lLa W0:nПOzW4a5QU8  `ƈ/iײ'&5Dhe $_S xBwd3_Y#Գ@; / @8%ԹqUBLLkɀ >c l0S[&mc0Yݣ$!'#~&/蛪#FӀv5 Vׂ'a|.!yg,/һzŞfxd{a[,zt!OSSAfX[i.ï~S>ԇ SH鏌k˙ Viul6HSX. DŽ znlh韆%碦ze`B'a ^TvѢ`_Kg^ut~rʦyi'֔K!?;9Q[(R xr_f@ P\ăm%2*͚rQrКe\ ˆ b78. Clx6>}a2ܼj'[R|‰.:F+~tn׹o${У}2g` @[60<< yN/6-}P/|V2 H96aLJ(O&g3=+av #ԜCM;{vINWqTrexa$:=hZ{hL>Դ=(۷>т%^v/hbۋs aB=f)m>WK_|M$='{lLi_'ЏmQ쮘1P #u줲d֮z%Qj^0ɃcFA 6uSDܫA3o~|t6^*zr$@Sh"ℵLLΎ8 bscWx<.͆V f̤JL23 a:#ٕw,0 /m+K gR9pMVdYyStdd;:@pEOhV 9ceCddCmycD$"gw9օDFn:$ņYeYt6P2,F.mV! P}kHBP-:nMͶ 1< u$Zdy%F4>60UtIxWЌ6wB}BRpwrz{W cUeYט'\γG䕹Ĵ)F,ﺷ n A4uUc N$O լ^ۯOy`Q|0=܅$bgְmUoA͝D}JASo~/Czv-xm`84I &s؂m@WσY)+ʗ0iܡ\k<p<*jnk{-_ff|HQ@P!^(#J'- UYn4x(ʱ MC9[/|}u]zUf\ OCӂOͷ 2HG.6?AIo9e'8,%|}=?iEqp0lӷp6R*:նRLxD@D56E/06%i %΀BG: 2@F#;Uo!շgx⨋YkfOVJ΢yop*cwZ@[A_O9I3oHJ‚Kް+W{^~Rm:׍>6ȆQ3u>Rd5 fVc{KN-u !ɝ}0 jk$m!D(Ls+*.κSZȃx1ƘFcqP?JG0t7k(P& 6BKc9eB!%e@Pa=pqF`4|"=Gͤ-#u sN&-`eN]}&y;qj`-f]}"{IU g!w=R;1=?GK IF<~ ӋM_E-g~˜&d-umQ YAx4!Q+&h$KDFrҶmy:}ӭx(,i*\-7^d;U{#!d{axH7KU?yfҫ:]c|˳ߍ3V>rSixg[}fCvU=HJnmJ֮C+ G<€8j,~͌:on0Ec>IK) dG7vH㢥6 )LHifbZ@`n1G,NtNR,2HuatYUB БƗ~5LM8x ffIh5  (RFBQr+2̞\hr!D&"F-I̔P8b%ejݓޓ, zG-۶'fG5f)q8Ѷ :U~s]8m- P_MHjRF*: ޒ0[Xă msv-cw쀍a/e67wGGDzUAL+ q !NqE2V؟T9e7plEI9,_-*yR%G%Agm%v~ {^S?em5*~X+- Yܸovt:uq#S>HI7ʯ9R׺tQ%^i ۨg1>˚,WnnsD3a, !0G7rG 6Ӊ蛷]﬩pR~Nd#[5{,Ac_)@䥘X6-)?僠nN5`((3ʄ ,9W!wwQJX%IN"Cq ^-FPoXd!F$+"oٛ.g8gfjcrן~_6 NۭCgVgS|[2 9'X(6i `ם@S3>mBd.` z(E^S'Iz^)c*pHi&'Փ΍ofpcȤ%T2|Ε5 c*!2=\7.LJti 7'>Ѵ4q/V7nvhJp-DsJ S"#Z.MG=ݐT冴&M8fTwH{p,x;Wy~ִ 02ԛc *%Կ9: 5jx`mGm{9;yvsؓyyD4␙gp[d71^ _)l9hThZK ~GOl<= t? *qݓG+9Wo:cH*Z#&w{/U:4`&P1}uk+dd>UNM uV\.ƹLzt+d0-_t:w踒pu}#p{m@Iۡ?j6$-j3Ufsy ~C.r!`{A 1\ 57cWѽHZ =BuNpl⪗q[(e:FX;R8pllxcp֬5o FA`sD.f~(J# ޵W* ѬHEoFjy3lM&gڋt!gjBdl\Axۖ'Eyo Nk^Tع;0(Y{Ex h߂%!/Yr83T;_]%~D!5uCphPRiÔHQt 3=&G1F('+CAS 6E+)]z5{xOi,IZL`{4MgYY! >"q6/ o$EgN!Z@gpuK?a.՛)HR=A ;q]xuuVVʄ {$D /.O=aW\t.cHF^W;'΁dpq=X.int+"^v@2zicN>WP$Ԛ%%q<-8&zy5:q=]gi\sř.ZZJG8Ԭ[7{Һ9/;xJ*cX +y"V>F}XVψ'%K,5Q !Y*ҪZ)p w?^(k2hlҺa|>e2s7JlH'>h4:[eOֲwFj= Ԗ;UH4e+=EwHʯ9IJOJfrdήh.{MrVC.w_œW74&2yOvSIjbUlDT@{wKye K}.nwF::u z6Y Iں.S;-ڄVQWeKu+Sp`&Dm26j7VrXBQ@hAвUdHDߝ;3f2.7LYEF ʼn\`&M f vɅԜ,o;*TSC!?ZN'*n|!ЉmQ`i|kC{bY9̩ڟLpx+F0N!{w'w[eo%*c0.CrC.B|HؖD5pS(W15s ̤8[fR'=nW?kU$gH䬻qe̤Gꔅ?ijAoL/CeML9  K2#L{1 J{uBV8.O+y}>fIGk_kɱh :HӋئ*xH,Snߝ 눤9*5z#='W ErӷClF/R*"_K7U̿\ΕSkeϝcb p{L+4fNX/!I 3YbwHߋGCmZvSn,Q↶uYrPr(B|i \uZ{9˧KЯ$iESi>Hsg@r i!.^{m|TU,Cb]m  $\c%!BË=`6U@;ǯrCz~MLZ _m6X3@#w%:f8xkGZYAHv`"ؐx(4E{X-睇e*œs?Nkax P2rJt oKՌ?tI5ShEI?hlYn]E@[rƟ$s--!#Fo.~HeQ .Gp-GP]! @U%fB"TLUZ t2k%"} ]Q>P0"\5 GhN9 %VON3 1=O{%eyj% ׀xڱpF@zH6mxRngÈH/b 59_ y#Vpr ihmP?9#GNK$;b"&k CpnÊ۫ PG,0SA lbsrG8Ne$_IT!usU)} f\#i9ABN>6Nao}lL(P ޜʘ{& ^[U':\|MU 0BQ.2tz̭JACEq2˻xO88,R 7p)"l@p݀W~EsBh!HtE\VQUr k{;#Ff称^ >Fr%]Je)R:rM|6 IîrDo{_jjnC%8jSŶtH{FBñ$ c0_"W'C~U.;`g<N씟MΙA'O2ھM8f}gƔ3|H.K{gƣU!7&#]. }ƞskݠyvר[ }۰mO0PV/2Z&"wċY*Íu=(!ZtU=V[nbF ?~XjRhzъ&C++%/p7 lOMEV8AAʒJBu!*1c.`'T}Q<˗tu&5rH+s]-Xv!HZ@Ĩ!|z>tWD5KXȷ A{'mӿ|hHd8QsCy 垝 ' ȸX!M!~91>b/M _!4V\gh~ڬ~60͇%4ʝ!. Tlm%h|Jj9H~#OS hkrV{ Ӏ;|QfpE*O11dZP;Ώ (= nK85̩$Q%eqc#s7͓m1Sn3|B l:lӥp؂9tʁ ctYIp`"-jZʜxqE}/?{``=8҆{(śT[aJ&/ߓ ؍4'yuۧWusē(@AkbREGM-Ƨl!_T5Us2d+ꤏf⸄bZ05Gaße ?!BM&рO68¹LZ颀S`Jqb5fqn99gbJ yy=!6b+2sK~<R#{%#)0S$J;B֞pڣ˘r?l&[E6pgxe=1\-HR㶖/q/\<FA3zT#i|[رM+q Te}!>ak{1^6c;,2C[J+ /lre:烌n- f Mе)md+51k?XX60=϶Bf*7 XjRf>rroV}7h;2j?Ju!aقe2j85Ю?~dpi#ӢpoYr,xyLg" EozV_̬# jPW?#ÔnLzz`2"6>/,wGc_ V{OK ;/ZکtIXm9yoFj2}Vbۺ&^PŁ|IlWWH,Uvg^ X^E8S|?2ZycԞtܓ/pj#'W 4θ4_v2G$]M)$krٷ_O BUp(Z'}(6T"C9N/yd9C)?J8iwJrθKyYBx蝳2ӂҫx ]%Bmφ|&RXؑZt_KAGj;qEH΀e :%Q^. #G#/r{iвDN]emܨ]0훁͍,NbPC,榃PB\)3O/7Hf~-Ȋ5r,G%UVu޲'P ~bbGLfЈ{·kv龎֛$$V|r0ҏ?ؖ Vg3vpU8]|?b;5e?>55~( [v?%uT; r-` dڥC8b+GCz-V9>.xĪ]V64Wb3 }\zd[cۇXg~xW3%BBk]_OtH7`l-¤O&}[VpoeyySO>>ƷJ@Dhl A CXB%U}\uSii%1eL;6/a +JC%@*.,9[@M8PbZ-Agwr%!`7'B봛!qi;~ENiu#rOX;Vm%oB A|3˯4 PB8|{[,身7{fI3p9=}|d7O^$=:;t{Bhb#64j 'բMSTZ}%nqh3qaGYHz ƱwmQKj}UǍ>4يU/))St-w*Y$)lFmmJp¾aks%Ms#(E_th|~F^bM{ $X3!@):g>?IvQv="X%-U4b赞[5z@結7N@yQCBm7T3pIx:E 3i^_{p$'PXXIS([<g92]>s@m0񟷭Ue1'y^լ#9A~֜t.5\Z;)QْM6v UB+}|P8MOa:[nrcgP3;KMS?w<>Hքp?b.:bğwYU[dd>WP޽8ݩFG4Mݳ˕vn_ebCg܄QO\Qm"y#NA#L" h, mJ[#(ۖa+2yL-:PQ+:[QJ¶`я ySJ5aGsk$UbT;U}(EV=ixVR^QfwJt«>iD%Ǚ-  ];!( |\Wƚt17*{ڟ Hmnr`PH-ԪLpnKZY-љ+l!׵ui4}R:'\Lʁ߼m "Ӣ5f=j'oqXOˉ^"îsdV`WPZO#"UↅPT A| "?;o|x^X#>#й!$"@}"] Nmǥ-h";$#9LĐêT鼔!>Ɔ! q=x@BXE8g@#8-:[GX 46Mve@#!_ʄ|ic>ђ Z nxR;KlM:Mx?2Uq<)|m.Eb guInwb]N^;w: 0ymJw:o#m%HՓuOGزTp?g BgaOEY.5ͨÎBJS+xtK_+~ &؋a+^iXi3eZ[H% ys>x !6I6Ҥ*v[q= ba5=c5Y'mWπ ueg>4<͟XKLsyVƁ*`B{|b G:(b)F:AE^}{2J/8ފjIF6h(Ť΂L4cLwzܰQ~\ĥ 韽ȝSC ݭ< ,8wfC~{LϪy)rKՁH9i#d c/Ж0u5` RAK2(0Gۗԝrf~ݛ cf /tCrqLnj̣qJYap%R 0ZSiѪ'bn6"b%4?pW80e̬zEvSGo0 ےc+o_H^Eƞ`O ,Vn3Z}"J$:p5,&ωR0~7>5FS{uAk;@s󁺈W}VGKP ms Q͖w9োh!5ɖ*1*4o@BYB! s$߽yX <M檱ۛ3Ʈ h}h,Xh?e№ s[8}6o5`@!&LdjN㜧J XU3z GxC{NS(PLE5]ݵ}?/Բ=eԕ 'u6H$rb*L :-fܩ3E퍸VH#G,Qu?zxRo)Ff甬w81qi>%d|Nq`TI=+2oxw':< (H}jEEk6k~k?ɼuY<Z.79bs|a-#fw*4;6)P}ax>F(21n{X61pHBfOVqUQ}QWNOpG鍉ٌWE|5X\x>0Qj=S:\w,VOVZp\]ˀɌ>+itjl:gHq¹:@9 bjw&T^эF+P e~{ҺwpEqgFFMiNe{e5+O_nkzRp\v< (?]JemHBRvjz ؕۢc)es1O(hj Q0o,3[pGe^fyK-`U),ᣫe SU YrobZ{v;&^AX,eL+c ܼlk҈/D c[C`32}Pf/ZZB tp: F~+^=:'cqkqgǽʮSL RMG]ցl Ǯzui xoz{JK|C`u^kP`7-+QsU\kKek> cY@D F9qS@B5 %b۟Al.5Dt&$5 |I.icWWхjHs| o_FIK3B[RE ٔK<;Mog243\0~B5_ K¬(w[[yp>=?q;(8K0F٪C7^ք4 _ȼ־d[=uUk|1!을i֬iawrGZ}k L FE ୹ĕs OiuuuEO{ Rw@- ~5fdNy=]dIg*g4cA<[;d: +mAx,"'0Vo<&v {]so(kk"#Gg|LMrHVc N ։M|GB =[xB1U.hhBf4+P :]ɐ'{O}P*fySh]h\g3nⵚ7:I[2T^+*nA[Fe߫o̫dz)I:|C7 :Ì,]jh @ۧwT2ApIsIsJ2JČ_Vg͛C%sa}<}b?jht_=# &MyDV"jJ~^e/dLśwٔfzm8Ke"^Љg+KL6N. 8J> -0aC8uiqgmB(1.Qb]tZ1cC| %D|RՒ d{4M .g퇄 t1A00y<&d, 7PG ,r(ziDFg[.,TEá EIj 9 X%١MH^=̇6*pⷝ?] W vkaLC{ھK|A8Xk')%AלEĦf. ufz5F]+}Jgp8WօŠY#`^sS,vsbf!t3T)7 g-"*-umFy`f-.)n+ E|  )zyo*`R+L@EW_%5SbzP VFceopՍh'X >ݹ?2NhY; Whފ&}3 HZɚʲyN*00[QθEoU_2[kj(1W<9oo2A)ƒ88ѩ˵A#,RZsHc!&<הj܀~ϨS3ziq>gD_~'.6>B Ly˕eRUm}v尔Hwt>G.Lu{ey $5؈bi@C:j.]poE[~$ϭe T%a"Hs 2R^>s'pYˬ/Նx4SwW Q+fn뾗X((re5do3]|<؁*ۧq{2bglfS؃,/ kؾ"X*E䢎D)sL%UQy lH;P9X$aJݖU %}|`쉜9r6ˡj:׊2ޕx6壡Q lv :M&+'n7MgtM 1KUY͞ w̍F@[BI`o#euDDwեxjZML`-6nT0c^| j^8Y*)f6ƾ/Ru@d7@y9RbG_tF[Cmr)I|  %XfO^ $~su?B8u!D+-!sWFv~5׼d-M`j_Aci{|UQpw/$5 s1\v` E2bOAxOK)nʳlJvs&#ٺT=;Ndœ #cb!ק|^AqVRacuOV3JVE5nL%W(DshغrO9j4=im݉Sjͳ40ú:Z͒EWSkF|L:Iyˆw'j3>yie-`lUt/a342ez=ǐepݼ8MQ6)x4ۡ ı(ze"cu[!=[Udo@FN@*I5tv֕SN`WK9Wmhѿ\X /HHv*c+,KXL;4L>vP]x %z9ϼ!p~#iFliMۏ/\IiLS?xmbZl叢XDžnN,djPGR F* 3䌀0.Š]"K#rڊ `}_ch03a ǢHXPUMR.1@D8doni.cƘڂ r";L"8B7r}*UkӽZh͈F$4ό~¢B")~mGҿյGJ՘)f  {.Ԭ0y(E1 >\{DZyÇM_(UE1Cylԭ3 f* P?Nd[pQi}k>+|iK@ 3edeƿ!8vPQ|eq O:J޹D8DGJvٝoT'˭Ց:ҼJ/|j] m wֹ"< ˋH6Mb%9ZϻbVDvs V!EhxTe(cݿ1ئZ-6ˮuؘ]-H#G Y{6D巀 LxMZ'qHMNp4גESm~ vJz^2m2a2~j|i eJf1&- }G:\\f:RVuw8t_+.r ߋX:hGՙżBN9Uzp%7V +?foW~"Paeꊶnw':jc)P :c*!WLc EQ 3|V;a#oM8>!+rc~'J#EsE Mt6GqMNCd0M@'s9z/޾!Q /˰=7~c݀24),̷m?Ie|g(?LdKeGv)Wu_[L~{K aWӀEo,#QOb,ːᰌ qMNj?HSJ֌TUh_pNEk@㗌Ssx|ֈTXDi0PbY^5G~cw. c9RzjuJt_G%WjB[۳ՅϨL{'pFj9l*Sؓ]P-+cזc&dfxPX_Vxe( HbC f!.sV2V:E@JQUɨoE޶[[W )CXQ!;eZ~urQh[2gj-}ML͉Mi=wDdiC@ȁTom7䘆W6’c~K^侷}"pU S d3SHG68$q83t3\6S,_G ;ILfg h-YчsZ=gWF5b7F` ea dV _Ѭkr 9 3\wŢ3G~nЇw]:a* Ib~9HX)K&fC N/}@_O D-]vѥsҦbBx}fvjg<*Kݚ|ݝ=Q_ bKrXRV8/ Sė_) ț;gjm؇y,,nJ^k.k#e!mD9@C0Dh'1ؗCuЌ#RALjJ%ވ~N{A:h1 &G'π)Vf!vɷ%+b~ '1qXkeA['o̐ <%Ilw? sN2gio;n7߷.Kt S =Xƹ]UVSaavZ!Lٯ*xԄz}t='i1 VȵwPZfsݭewF.yvtVp6NS?*$җgE{Yr%U>!S^QF]Q\ Z8, ti^۶m۶m۶m۶m{mw{Q8'3OJ Y^9޽)LqPb]<^Z] (?2 ˙RyOr#^s򋚑qz0gG)k$fV-% w)Nr 1| q BX_obQʒF^! Ƥ; eV*ݗ aW v+HJ~yH`OCS/0V[[}S(L7{ H})Cؒq3t1?UzwGd?8 #beW%mN-.IvhݏA+,D\cS#<V<Cw6Zs*`E6 4^|@2`1yxgc$ޫ{Fw)*zKGLD\Z;'JgP66У5+5umDtZ  ZElaƋ*Ea*$N5!L"gR Y αpI(̩ w37}QyeC+8 *k[R[}#"rUf flO^F 2>x8ZU) :DPXEkY@wZW];hf ʏV۷/;*AuVw8c'n|I})WDFe;):HBu!1g x0a-8%^яi^7u)RpT+ծ$l$*኎B#K`L|%]S0껊2 ךhj^Cp_^c5.oEy&3*&F[ֿhgFӯRpi#TfX,J֞>Ěl@VR)D&GG:<rftF,֝/5'.7b5aQhCiUw8AؘA!Qaaau&#ys;7A&=iJ4j<;_e>n k]-TO\,6Ҏ #&SNd"v ׳HM2<űԓ`.Ոu9u؜ I`p1R|ʚ3+E^DH?D%A)i;IKsJD>"idz$[ ;x]Z]H?Ytv =f7f$vOl™*>$'w i6Hx7e˱eߋKy5T-} 'vhnyc)aN.|a@,o]&ЄLAf1BThY:>%.(D*\eܙYSt<¤oQ),_k,QF3yG;Ic7?5f8Jl:'XG={TL3z"Oc զ~l{3'h4c:L )Fv) N\{nzFOE0qY <+DgaSڟͻ[~ACX]9a n{^ǟ,ֹ>0F[he^$<1h"c K_+eSf˿>iECĆ=|goYbQ;}~> 0r7EQ[r3r&6U!(a_3+,?+ 9f?d~S{ε'-OM" HS"Pr&†ID;EDHZ- 6n~#ɚe$C_7@8)<;Y-~Q5?9",(YT +Ty/ xZWA7M1xMׁ;(>Tg\oCm"Y=tOKNmZ'zmtM=w.F@-6>Ǫ3nEV"HaU2C>P"'jOCghu/ݺK>jq b)' gda}m[%2a 3y|ıƙD{& a%;pk suhҍ mvbd*T=[dWR|B{HIaR`7 sԔJ0jԟQ.e^33c< Pa=:MD^:Î~b b%h&2I벶"RyЗDIA]f0]ǂfQͩ,ؑFFA5(՞1v{#:C>wJt?o*/HfL?GǂŷŚͬll;!Yo1(9G|g].\r1mn{v $l2'AzQWB▪j{$~8U4I=4[1 #KmcW: O޼@~T P.`F&2 ;uȴI\軞Ua9JJh6k)cr a ނU:rh$0 vC/љ߇Y֡XE'STMP yz~˟n8h4ĔmA4/y[ c߷1V秆n'G}]RVykvT4ѵ9ئ,wy}]c #tPP@vKB:̱ ݀άӼct;Bl,'lyK#4aϩ.+5ntS%)~#%%\MnK_teSnv"2:]v}mȼMbcyS{4xk)v[ i&YP" #ۮCcr yڽ!,"@ 1Ŷ?-,iR ޣ$LC3Gp+ֹE":"QE8ƴ_,F6f{ q`rḓH-á(#K% QwK.*lf͋9!Pr=^=_iq6?s[{`%Q]z` yYU,\C܅LEIDm Sqdl-rWEv(5Ls%ͬ UA憀":՟7v+o#B=l|+Mqȶf-Kq hryi;[9`Dwx?T UX' 6"ӰD.|A,v,Xl.M{NM7}pUeT @]dkRfH#%A$wGC!ۚ3F%mM)uڤ&t/`'d#1:'FTδXv|s9(|BcY y3\ -fhE]-TXkM |=i9a&/rFE[gN<#v QӴu<żl6핌l1mBO |_8"sOrI\zJvZ \(ID.Bp,c-*Y(tF2Pj= nkR"-p+0 J}ŋIX H;s۞F,e?t`\}˜!l`@"u#Wpwbul"|pTagEc5s7.;w"+s׿r̤D49w).Ra/ A!J+ohwW0ռ{⦫*fp87#;DOq(i24AA2{߃>8"ώApv% G:%= ίJFP5ϥq NRrnߩI'q87^])t`;z9eA#}{0Ywvcy,gH8<T"mIm` G"=΢+qH￴zHN[=/&i=+/OPp؃GY֤{cQq.:FRȚ}^4n$V:? {`nyJ 2w!uG(ԗv*nӭ(|uZeZ̙ mApX| WFz6C',OVQ>LQ)U_;^z?o#Dߧ.d\N'+-nq: g=xosxxʗA7}W!O7AoQ߅1٠¯ zծ:2z)4}cfl-|uh1}w57hru΃ ?M1gbL윖1 zfs ZScTI谐j[Qbm#Bb1_QojJuU7䶁9op(`o "8,KԼ3Ïi?vRT@Vjyb7=ȡس#^Tȱ{OS5*f^(3r';5fP`]Vrvj,j04G,8JF&%n T,@s0_Ǭ/0(G >/91)\g ͼ鞶W#yl](\?jW֜gOVLoN]9e1ҫ뱽iqpwTd{3X]HPT9U%atgi\* pZ7 a}w69O3O͒ӤM}r37S1Gn]1!<`=\gwRLa -wKȣd9Q̿U:UNHCŃikSw;qˉ/yAt{p{!FO_:)A.?u ~dS<:)w7NF+yα/VMvv(=gy0w)k9c02.RɪG\.r oxʥs]oes@T]%79Y~zv0"qG{F]%IjW CcUُ՟j={,PiB E:u.% d.7Q%x7,dw.쳞I#j'8"c<"~. :ԫ`҉ݰ"5>)}PG8 ?\ЫWiS"oW~ s *<E2140jl֣\XnJ\Nߺȡ~9p`tET%XD5&&hxbe4/UPXU˩SQ%[sw\I+ZʦAon0+ֲa{AD猰:z 2zH`„U<[m>nܢ9Z2{`&[9Oų֍ZTbY]@  mp0[8VAFY&K\AQ_xw-CCx?Bg<ͽqɝ1/Md& AWpIy6 FPmMw8eM) ~{/ťi=`^vVy $?}@7g(9w8R)$K7YD7_4&3?Fj `9VsֽO`gQO1lã0\ksSx9}XB.Mj9X5ۖO0r{Hӝ52(ED Z?EBxv/_Vz#O˵W )` BI$no1od]PĘV-ב9%[0+ EJz㜅eS^7rKE3V}xm=7),PO,ft~@/t2t\?:r fť#']L|Ɣ::q 7b˹DzxؾY@Al77v2 CS~hA_#3Li/ٕ?MdHm(K=U¸Xf<>Taģ˦qi5d]e%}@cICb&)תDl'<}nY#|620-מzA*iI? OP}A) $=Zc?Z>wMl#u͵JL 2 ~K6QHpR^g}x ۦpݒd.xy_#X?s)u\dK%nۧS9 +f4S 9HaGBOG;9X3}Jd.|ie,6k]1s025p_Ҥ!K³%ɁoPQ>/6Gj}T4Ykzv &j뀜=)Jd Z>y_?7phnyc¼-Y7xrՐlfu ;u'gUT қz|܏NefZv.+!㾅L[/Xib$gP<'Q͘_䋙v7Yyfy5 ZE۟f2h*aϱ RCr0v'kZ&5A)1qamXn=xksKUa/|6κ[y{cQ*̪++!YюgdD8x貟Z>x|tB;Uw+읝9A _5AXOo 4wm_0.t"JP 1V[=]!fv-=jʮq򥼆 \_̀GhˆX hDh!7I=C^}F3o.'H()%O$,ؖ=yZ溨"T,:o|U{Ma,< >XٟĐu Ƣ% 3|Bhi)%-wqKqs?Vigs]֚k\?6='aq\l vQ փcGrH}AZ!]GTi'2c #V3鋳gRr#1Pdqxi_  WԚ(ϥ$vk6εINXK:wA9AH䑉 \ǏlZpG<ŸL6Q/!:|z99@0w57Ɗe#[20U)oj⇌|"1"'WTVoË .8韋 *R2oHgظ"h}_<&ȻhNR,HU9~-f\e0 ٢>6!-:&GuFz "ƈL[Wz7Sdm$ЊXSӎA(Zއ#n>ё>(oٲ-uҜ>?5CvY d#Nw3{88/h=P`vq<4c2}=t`S9OXǭO|1<+"Fu +۹ }Mj]6 (0bBPׁ 0'c+2xH)Vs~'`ˋ¨w ,Z\) 39aV6JOp>>4tNei=-ful *F>y?bJ3l.$Jh@p)apS0[)SB`Oش,bĄ/,mRMs>vaǕ}\C9%Pkh5YxVLgCunї5͘3rBࡱ A7싫 155 gS@<y<*J8t8WI7'V ?[kpδBԣrIT\i('=Ǹa1919$lI;̩v6}\#]& |7Yǿ4o;|Η CZ6y (Υ0DTUkl:. f{26UH;^'Vh.D+k3o4vĞ.%Ez=szSwDu( ՅoӓO9W[@Q~wYsS ty_@9 ~n~?XS/*1F9{8ᴌG\W#Hjg 0E̺ƜhU/&EAe`SRI g@wwdE7vAM;8-yL# Snb! {qɞG%Df,?[v3t׮Y]s- /[J}YG)#Y850JV}|$D궫=7,cT̨_A5E9<*҆d?[f.J[LP2zP{ТHĠJWGG-'ޮ"9)qXW Yrȳpȣ7h +Us"k:3ev U.<@@w2GGTJuNUѶ" GLaHMs&>sW# h`dYEVʩ$ h`}1{/\A77sG{rAqnv@DXV O W3r~zWiTirJA1eNum t.f7&ٷIjP:Bw9+;|cx*'a(54RL7Tl)c^f8Ӏp>㥲k{|R+5Ll{?sz$^S>ZNNA͈yd+^4O}-IM/VZ߷EX2\z(4໭rpp}Df5+\sWo:Z3e8s&`޳s-UEZhJyáFv_#7T7Wn%NAgV;<6R/ϙnՏ~11ipQw~.VL'r֧t"9_:XkOu6+؏=`}Fr4ѫǾZ۳KNE)r (и°I8`޲ϋM\A J 3*gʠ cL)"b| qiِDO-bP`zQJ/BФH]ߛr?epg8ڴ:[2Ԥ8Mڐȋp.QWy#,OW_i NqBB*Si2_Y{7‚ޛ7۩ U*PmeliU?̢;d]4T·< Q4+.qyE,YO^qӉ_ã&>?TbR%ѧ,7iwU*ܚ >eYTOںx&=;o]ok:(unFa&?x$2McK.A߼ Ͱj+9|6ҭ8E b1(M^+rmqVY<Yf ag(dM;ԓGwV@t;9{Cf>&Cy";1>àVfe#k_|M粕&x(4\no@u}ɅXbdAL[ؘ ü`z߉uKkaLΗXaGIBNFxBH`qNg^xv@tbXi%Ph[E$"9k9KF74k~:{àjpu)N#rµ3ҶHMڛ9i|<%JMhS_7UGrij(O\BcRϨe9`eY_w7=Q]6)X!C-ΝqljpQ(N9\fo_>|, 3lzaұL? KEM(N q bB *!X E¿Ημ29Mt`O)*Bvr΋Qdw䎿̀`/KdT5 \P5 ʸ xƜz0ըʨoK1JM!8BYuѢe6#ܙmC'Qd!QMW xlP$\IQ; O34|Jmwoq]6:Ç"l| Tqdy5G?3aO[]uVbݮs~IܑG d(l&Ua xx^m[^©eȑrlMgc,v<*?'0Q.Lf*z2Wh [G9 p_};쒜n!C;H/H΋Hupi{6И|;i zQo|Kh;+-d,^;<Ŷ.z R)|<2I['?zNؘҎ7tO 3U]1%bF#&Ie]_nKԼ?` Y%\H'A8mH,x- j9Wf NmT5yTHx-JѪE kq8u}#x\ (2$89Iͷdn%g2tF +Xa _YW=7r,ҳ 1v*tЬbrj^[7!b>ƈ|ID2Z*+is` tID JldeYl\,۬BNΡZ:t(t#jmbx IƒJ3ds K(hPG|l`,XWtPߝb)m$ v-#'\>X4@+!Ϋ,0&D1OW7uo3g3W; v?+siS74YYuo+ݠsbk7ivݫ<I:\YU_)FW?a!{ Iάa5ی*"r߂{;pVyL$jo;|^֗ Nc[ɋ56E; qhiL./2S&RWq/ˑazwWhǹCXx&XyUx 뽛@^ e yf'=/yѬdw{:Z7\|u_pCɫba>9o`f$DL',W.ڧ^ԂF'f-/~.ǀ!J i6Z"HӪu Pt!^,`"Z8'"xkTp996p9_&R*h`NnZ_ Iƍ.2$1UDYz!J(jRV#L kuc۱ (oxԙs> "n(BhL]zx\=3|*֜[0 |ǡBP2GNZ/2bvi)PZ 2c,Tjrz_+B"j~6(*E~1oAd͑8\mN_"F-?4rHyNpTYbKJ{~"ӊe{`Oe!o7* &%m 2URu,m::26j6+'m 6Sp_alzKnKv=7I2ueB dI3Gw6_B?<> Co?dA%Q͞T!E/:h0U.Jb_sL8gސKKaVB8t65<}l _ţ dvga\})~uɢk| [y8 >f[BJ;'`4YyrZ#,4A WT]PrY$,y쫢cbU63~C\'6EJns[Q+0[)Ft>+%GK`=3r>E=k=6uRD.oS,Iq_Vq7OzLL6IWjSѰ } 㯼НWd n][W^D'j Qhlj 5X)lM6]Wt0&/䍫d/{fF+-ڔ!]qxCG՞Z*|80ԪXٮSF8H `/ bw׫2q(yeZ+ٙp` uX i$ vpm▿ԼV?*D;%jOwҷtqMAP @AZEDs, @'$194^#%e~%0Lt vZ9(#;whd1tU[Jpې=};OLݔm,_! 2>vz>NבV)8y`5G!ddE j6T 11u6 6-д\VGۑIjE4&J9 :Йj]7u1? ), 3S=C-HPLaDM3{Ӹwltu?bursb)@rX SbЀ4t!d– n#~gXO5c6N:D @5i~P@5*Bmܝ[edB[;'01jAMfv+)T랜l/`;mO߶=`0; p6KDy1ש˝}il9|!,*.o=FRC7|_VQթ-H^h٢"ԍPxFWl#~GDk!۾clT ;x)cа;:>B%ң b"X DXl 5s@Pgt+~x(T̙ȡ-͍p(ZMkF=YYggvt# cI8p3?zMصYND߼gMŀSs"i*>ًg :H90@'/RlI-ps y,s@AW&aAPt R*OrR7j1<| % !0"Q05XyM t>)>3S;#="LGǰaPvn>:{-:+ۊ)]@9:՗mMGO[ *n"uٸϴ`hC׍/:bB>ICrLA,V5DJG49)n0W߃:3Kqn\~S4SGC&-m#.vȍS jݬwpaR";Nnſ9QFM{qCSk'RSgqri8醔,7GP6o =6r/,Cj]dI| ߙvk%C"W/$μ(į*VXa٧ބ0k'Vَ/!Qk;jc4-kŞ#:<#^l|k 9T"c'fm ۶<,{L}`wZzE݁(@yH+ӷHG|- ~ l mR..7m& (4 %@j|JD[7oEn 0I? .`x1B;w>YqT Z).ZLR׫{~gL{dIbD_݋iXf.p}$< &?\a~ix &)=s ڎ怄q,ܡB(-&žS&D;E6 j$3wl]Wn~: q~xUS m0h,ØvL-9kΤB{6|F@$I N3iCdW+VH]P/ّB,,v[K\4Wkb{{Mߖ8\o$*w_ny套G2N!& MH =:$n)c1"}}/GIPUN%DU0D|4rq iiDzп[yUI ψ㦔E#" &pVo|2Fua^C܊e5P6 63nm(aRU?0ŊG^_)^h ,ܔ!Ȳpe$_V| e5~ބhڕ3.~l^I7&yg!^zpA<9$v3 '01k|k2TJgѴ͚&U?; ;'K6zoKevQŲa}GL3GܖC1Fcy[\' _!o>(/~2ZO K&Hh89&06I&oNeYeمclK*$M ss;-Zݛf22hM9KE) ,D|7V3Mw>_SB@HtbEk9ZCpTx}JCg1q .m6@sRrpi au˴ﮃS껜: 9ӇC\ sh2/LI]@* R؉«{:PV&Lأd5Π _$Lxqx sC6"81.t$.}WtL pݎp\)( 3M{vBB< Iִ(.9,!iq6˫ +-Xx:OC嚃.tђrWp<¡fݺA1I||ƓwV@Uâ?oXqY1b;zF< V (1^dA_@]5 !RVՂM۔\@YA;em N)APbGH>/A;-ap*~36R뱤O0U@)[([UG@JW~&}R*7[%svEs;m斳btt.򾡑4i{cmHR[D2Hmf#zػU^+[_vt!t3bֹ h+Ghtς\O?po'&L,#u^[)7%lw 5Gǀ`QcԼRDG @>:'G q6(Uwa־f:8*2:U-N0iwD M(=|7OS'N.ߦШgxQ )r(<9P`7&0E"gݍ(c&=R,! zczy-[lr`ҍ/iPXFar޻F?_`Utw~X1NJ=l\XKEFh%f@u^6UQCdtogE_G$ιUĬۯ?9)Ҽ|T/fӝd5x4|yRZ|Yޯ Ld lwvB_-{%c=rh2*UxiYhYA%`/ZIM-"z?#LWԪ Dob@2 *K}jOS1Wy*;KXjsʌ_ U){rB|?T]Ӎs X?YЌ(̋e5ފp@ Fi⭦%X)Qq{x튉8`Tn<:C&hDݤls@8kLjEv~jݬ]Bƀ~%%I+OAS?sOyu*msJg|p*hSL j%+ɤ ^>~kbb?j +1{_;*}Ȱ D]c=1ņCf|,z0to<<.Sq_ SFŕKVbsl}+\8fK4Ȑ(o>1*DW},NA H|fru.2ڒ3$7M}li!7zveC*Rp?ky< aevVB/y5Z$K e:MOXxM3]8~,\蚍(2AoT=z쬴Esa(8~r܏!<{-/S8,iX=_k׎ŀ32CڴmÓrC|]8FlGjx^th|ҵ ӖctOChݝޭd)q4vvVt5^M d~?*FfyyH Jf ^;\;lw:(#$NJڐJ[5I r4@w {cGfBr}TTƴoV6iڪ? m/ŨNxr)94г`nT J*ƗoYE{`fZɀLd;,z@E A-/ڰz cU\ @ܓAo6"4??=W(50+rV %g.H=ұ3nöτIxOv&zRSss*?Qb&v.^FCߣ'4%y]= 𫨚p1w=pblrΜ?yEgoBF1+Kw=3 DuYrn831Qrg6[̳Fݺ]ކ5$m{b8z21#^*Wn˔G "걂/2w3b5), ]2=dWBV7j\ _)~Ygg{26Y+K* k`hl\Ln:oVcn_ܷ1}퍼k_O B¢]̋$񮖥bOZ{Iʔ [KřkԖ^!T⦮jV{Cn1Pi֡HTw@ SLDa q#SޖӲ)@JJVs,99Q2-B+i@.*o"D!dG;sŷJB& \ۻ$;\qGC,@iaR?.㰀En$d޾0`3Z1O`Z1#ĉ1:T.Tg'N@@! =xTBfM^d/$S+|]j(j'(_|TTw-GC AƹtU `PDF #._nxzJ}s_֙l Et9`م Uk yeN_Qո3.c="߂+@OkXOvFs VD %{v2#b 46#j, sĿ7%D^n~نXmbrjn&+7(wʆ $gS룞+I.{#hĐ/juBM:?:/*-!3#WD%j((~U#  4OԗNGa )YM׫_a ^8<)F(ёfQT'9"aH k_R|Zz0 `5`DR< 2"j%§ZOղ)+AR JҊ՘ƹA$)5$Q\j:R|Pdx. K xˎlMbn( *|Z{.©j.c"lA۠z;kÝ!/dVp"IZBĽsHw!@Gn|sP>ܦ)ma6a1Sm(˗1D89H'KxiĚו8Wpj/.20~ 9QGJWgݩУ5d[}"+otp1pi3f"h6CI%:z9#0@:K |쫶SDx!pHz)P64 1r@ l) DEa렟2qzhos3A@*4Q|Bצmrv$< bYO`ٸ8< 0`I ɽ[߈,(Յd[BCLd }V;D@|z!@NgɱP|73!r$~gXK~m2.0 B]S>1V ZkʂH\"~b9.X=.zv3dbjj'Ubշ]ī(SX%ov{CE%]]!T}۝y5cyEZ)NLhAnQ{:|lhYrO~éID\ypZ{'8T~v;?|t5pXQ˕g%?R(4 UkL-k`|CK|KT-w% e=HaaGj}}o,o)#1S96%訖LVZ/Fy&'ܛ**Ar8veqw0/)%Qv:5u t0 !NA^ ´`?Z4dv[ď^]a*=U]W%LA ĕē^o67:u0A|: Dz{@ qXuWo)vfls`+z, oH&n7J{tvHͅц-2FfEmiNE ާ FGTKr*(1f4qÎڳcOaۢ4}h_S*kS(w_Ki+[JTHdyS01*،ĕ}IKF PV6蒥7"N2>HfC0R P,=x+ )rdT}f疁Ua?o[y3E+bNH-WYGs߃9_\j<0g >w(!R*1%im7V q ubK_8,b܋gw5~Q3x@}.i9b ~Z!=]tt 2X鷉?J '|* {qSdi#@gX+?plņ R .D F>$GD=cY x]FP-1(Vg#dΏ|[]upߡV;.QVtmCc+>:Y:kŽy3 ;1BWJƃ@ijBe{'VgAT0rYUQθb53 MovGxTlb*3$ȺV;)0z u-BeRFQDreIUGXkRaŘnԞH%)G%ty.!(RȚ[[y İ&f' R*mh0B5%!2P @$LAKԻtLntz}z%IĨw@,1u;Pp.!z񞭤DF)ضBĨT P{4ӭ;?7[RAPȬ /#ǀM-EYu+z:> Nmǥ-h";$#9LĐêT鼔!>Ɔ! q=x@BXE8g@#8-:[GX 46Mve@#!_ʄ|ic>ђ Z nxR;KlM:Mx?2Uq<)|m.Eb guInwb]N^;w: 0ymJw:o#m%HՓuOGزTp?g BgaOEY.5ͨÎBJS+xtK_+~ &؋a+^iXi3eZ[H% ys>x !6I6Ҥ*v[q= ba5=c5Y'mWπ ueg>4<͟XKLsyVƁ*`B{|b G:(b)F:AE^}{2J/8ފjIF6h(Ť΂L4cLwzܰQ~\ĥ 韽ȝSC ݭ< ,8wfC~{LϪy)rKՁH9i#d c/Ж0u5` RAK2(0Gۗԝrf~ݛ cf /tCrqLnj̣qJYap%R 0ZSiѪ'bn6"b%4?pW80e̬zEvSGo0 ےc+o_H^Eƞ`O ,Vn3Z}"J$:p5,&ωR0~7>5FS{uAk;@s󁺈W}VGKP ms Q͖w9োh!5ɖ*1*4o@BYB! s$߽yX <M檱ۛ3Ʈ h}h,Xh?e№ s[8}60~{gEٿ{v3; vɔBo0e<#@ =bsi$!xN͸(+~ 'S#DlƫQE> M,.M(zD);w^y~ +-VKIe@dFÕr4:Pz6$M8{i\\ ?1W;?*zF#zLam@_Fa2?D^iݻj_e]Т3#h~42Śç/5p=)BerOxNwDv;DV.6E$);5gKXJmѱd9rxx(7 Ι-M82/V3μܥa0T2)\H,7vOatjKy\\@/ ,2H1\n^V iāFmZH1p>kI-YU:FR8#Cpjqr۱8 5ڸM^e)CZƣ.@cW:4H7B~=I=b%>bݺRC5pAd(~eW5FXwұ{FzJmyv"o)@~wPne 6ynl ޚt  j$4r1BT5$e2r㙝vpt o 8*x d ^z| 6QE~; 4Q׶m۶m۶m۶m۶]Nfv_eөtTNR5fl#яB]jqu΀ p8p\FBh;>؆oTF#VX[B^ &1ͺ-~=P`ıpq0ve$t<#/%]:8!MI`ij{&mL09EY 'Qհ.̊m9~5̿/Q0ɈAL_:!5V}B_]BKRaq.vWsSixXE;*08@$r9V%O% b/+ {ֹ͡p>1 f_pZ#=Ԇen>ɩF>n_WF&vc]6e{[0RY~tي͵K CκwB L00N]t? YE23 \̶N>&alչ]HO ZVl.&;4 Ä T6e3|>C*WE9_/HLv`˅oh8cUA!vVM!!dۛ ф3|l'/~ۉы)pp n t;D,T3iNvRq XMlj沾PgWY`ԵB/9t#;ye](x5$g8l7'6j B7C5=|S[|R)"Rhf8lb궲[g bª &"a A=TdkXb)L]#8(0|oEh4NXFK/\Ə}ӝ#-qG5P [դoҐ|I+Y]YoR>AqOCСiqbSeZJ0 ϡ챼 r;,yS֑|sg`@}u,DR`*rfB/-KņhBI=;`rL"MR8ۮ. 8'zs|ۺ% `c\Y یh03(ϣ-v $rܞ| O|wa9q'!N&fkp}hO=Aka-ۗ^KeؙȂ\(eN*3mi*$Ai۲ ݓA1ݳGX:MrL #k-LmjF2lS{^G hs+<yw"Pmp8* ǘ!:G.{"xyqce H5kEwqJ<(6O &Um7ٛզ3h :Ϧ[fexf*ˬf[N;F#-!Ō 2C:V"Fw@";[U^uS5~&&D_^HBDI7KX1rt /,v3`Auc?DE]P j2)N1iS:~#-ڡyJ̔ vPfi~ ,8 /b~`z\NWFv~5׼d-M`j_Axci{rUQpw/$5 s1\v` E2bOAxq@K)nʳluJvs&#ٺT=;Ndœ #cb!ק|^AqVRacuOV3JVE5nL%W(DshغrO9j4=im݉Sjs40ú:Z͒EWSkF|B:Iyˆw'j3>yie-`lUt/a342!yq5&N}+7cAmRxC=c2ȣQDBz+D9/ފneE% T(j:B)&DW+#sr6d. e_3h,a;o@kH2X0WbawʂDE3;y]yd{5+# ]=^`(hK{qqNъTκ{}`B!l+l]l3Z ߏU4@S؅q/Cٝݸ!`G[V&k[k_.x<)/PFœ;8Dp-i9jJ"7}T qH²Sjx4.@8o4Ko)G,_IO𽂦-Z+gU@}DR%_k7<=G*y&a*gw /S,/:VGLLK*vw-Uz2Y*,/"1J4h=nReZ͍Zчd+S zU>V?wVXck\.acjt"G&d}T<.ӳ2Q;5}hǭ#m4Q:}^\KSM9 5i( yUkT ȇaQ%tm+ݚ!X91J }?9"x&:EJ  nڇQ`~\|7Y[3%%#:ï-&Lh0i@"Ϸz|Б(U b1pGeHpX|qBx8ڦ styYNput)%@kFaw`M*}/[s8'ߢ5KF9<>kDWmD(,#?Cdс;e? )W^A:%:+5XgT=8~z5Zt)f.\Ökm_23}Zmvi< (/a}p+2ĆQB]yd&"t'&Q3pmԷNR` 1BCpCw"̕ZѶdԀ[\ۙ #eҔ9~W2n1 l%֗}oBE'Abg<HG68"q83t3\6S,_G ;MLfg h-YчsZ=gWF5b7F` ea dV _Ѭkr 9 3\wŢ3G~nЇw.0z$1IQP$S3!j ^rJqOnEw fWti)*^]WMLGe['ПSrw^!˽EV!Mkem,(hH&CTR<&rb}{L@:iTMTDc)Wp=cB'7; l];.v^!xE!؃ُߜ1&k-L"G}G|iL rI}nlDN?tXn<5hO!qsƯ3[.haCv P=@8]vi\H}+nwnO< >1 ozUЩxTW7VX%iq|#:ol1"^mE"I*7^=]{=5,X&7'4i{]5 YO`g`XFge by$BYB9u:9k{QðMg$ȆHj" ^NŮ*H],?m)g3Pv 9n68Jj]:rCppD{\C蕯Y5zFW4K)dRj/v1'#%AMc8ظSTs`\i;Ƒh[ȔIޚ [8X)V 2bV 5qև*-7D̹Eo )#8HըwP*7D#^ؖw«:Iv˚ ^(ȐfX6tk-JQ55Fټ+/"]64 n8n6ej:c76_B*Rgl(S+h|T{֝ęG|.w rنWT5GIױ\IŸ ԶCHD-W=>.*{1$?P>['o̐ <%Ilw? sN2gio;n7߷.Kt S =Xƹ]UVCSaavZ!Lٯ*xԄzt='i1 VȵwPZfsݭewF.ySvtV}` 8'ϩ̟Ljk~3"=e 9`*ؐ)|(VN.Lv(j҂-EY)!: C\?ٻT7<)JX, G]K^e:Ga9S*)\nq+4B\|W~Q3R!nRoL(/r힤2 @vqA.~In!ϟa 3sTS[kU,`>BYҡ܈k8$a|"ҙnC)Zɏ3 i`h_ڪRsk/R| !)f4$<;i7uh[q>f2WpJ.Td?8 #beW%mN-.IvhݏA+,D\cS#<V<Cw6Zs*`E6 4'^|@2`1yxgc$ޫ{Fw)*hf ʏV;/;.AuVw8c'n|I})WDFe;-:LBu!1g x0a-G\uѽӼo1RNyJ؜;9U$sl80+.O3_jN3\2,~s7љ  0PM~ [5gbضc w)b%q‹霦%iz:Gf0]ZaƟ,_TZ:[Pp``XA磼sLώu?pq"/Z~@Tl_Obm;2|,7Oñ \SEBٛAx =JR!RgS٘}" ĚrL+킧`K9t~18Z:>QO( a9AT8k(]a sIv5V,kLRݒ+fe &.8W (ϖ̵$;i sXs<ēkC4>/ ^tWwnf~McM㚀!SӄO+1KQ]Z0ف%0}0gLq\-I92֍ěraT?2v,yxJ M)̅X,]Md9􉸃v-=뎧o}[*{떔4QLi[>DU,)|{ Idac=$ތ呙UikD]# %88K3(Yu9#&rz>[9M_2+l7nܺ Oq V+scWu{eigFSqG=Pk)N]ɵkqiށ*lM}A]m*%f@v/C3OSĥRxTcMȎ@yNPg77rr N9Q9 d_^*u\jڕ?q"wX%\1Qhd~ ɂ@㐃S~ F}WUcZP=uHαkk̿fBegmFBEW#9TO@Wo\=jAJ2IdWר 'CUW ,NU:s)0 $1${iPÛ%jғ±ק$`cFh huP .mЬ ZgX @ȼ ]*c0%C_tã/?qnFJib)aq]wr ,_h$,(u"ϙ%?h1aRߧpR@&ǝ&`x;nW$=)  }ڟͻ[~AMxs9>4dy?Y^sA/|`1ʼHyb*D&*"WT˦̖}v$ Wǩ {߲8/Ţϵw}`>po4^A*ӷfMmB4QÁg0VY~Wr:~y #kOZDD x '>M .yvnL<[2$9lzG5-7*H6h<~7@8)<;Y-~Q5?9",(YT +Ty/ xZWA7M1xMס;(>Tg\oCm"Y=tOKNmZ'zmtM=w.F@-6ҿ}'$Ug܊( e߭_E.d1}γEX OC*6|*R=_vu|xPRjODڶJNehe0@g3P% LKvc`Ѥ y@;ιT;zX b.8äD60JCiSSf>(dQǹܖyxlj8C̐A,8@G4q2cx?{Z_ ;=9ʒ2I$q𒻮ڊH1 C_q&UuJ!t E!z4dA4ǎ72 AOĐ̰3sy^f֦`#$GVՔp )2kPy .k6i `?g)~ŀ,ړ猯Iu>r5=*\ڭ. 4#N#G<8 ҋ"'c[b\ݒկzV$l)[x',Mb^z+7QG3A'F9ϝjǾle%@*qy!9:TXޙM0&)MMn7ۨRʍɒ*Zl’&=z&Uf8Ȇ^-Ua?(ҪUcL+jb$mc6(z17&w;Dh12TE{N=䂬fּ"| V}a,g3$+ ͎*a˪dх.d-H $jc 8Jk4 '{`nc'mo(2@Ig+of%dȮ 779?y#`G֬M1"ͷbglmr$׀.v~W荑CFAt*^k#~K_pPl.2 /Agbǂ5!8$WU_F4E&nث=Wюϡ?MB.g |4N9k1l1\֔b,/AXOޡM*aB vH:#!s G&S]"TL,aS5׆h&,}c2?kuIM:w:x;.+-CD.V\hY7iFqiPzF)Hzd<ڣ3GF,Lq~;vNccSЈ/w.~a+0T3"5^²8ZS0p {$0zǼ6 ֒>ß#md 8 Xhع!uWr[|44XluJѵ 4#hOԿHu c!llb17Ҏ?eܦ`X"W/v2gd/+`Uǯ!'BXw ߿0"dYQ~MK.Ȋ\굿Y".U7 YE* ؜\5v3ho攵=Nn, G~>ـ|^Z$- B-LQ8HXYt=i6Yɩ~K'ń}}7M\|#u% {(0ۚ{O~4*N^؅;^(qB Y| 롲ݍJa/t };OP D"4}U-6u3o+-E :gִ*z5s&n[c)k.{ K#U~zT }Ud׎^ex h&Yy-h;f[f(YlC}b>{b ,UPFӍg[T;waEv l~6hb^L`u~^G{=M{l;ٲ/[" Gl&ZLh]M2(e]8íOGd-S2;ek |},T_`q'5zbt4FtŶ!}0WnZ;?5$Ju-Ml IWu8[^5>zfs ZScTI谐j[Qbm#Bb1_QojJuU7䶁9op(`o "8,KԼ3Ïi?vRT@Vjyb7=ȑس#^Tȉ{OS5*f^(3r';5fP`]Vrvj,j04G,8JF&%n T,@s0_Ǭ/0(G >/91)\g ͼ鞶W#yl](\?jW֜gOVLoN]9e ҫ뉽iqpwTd{3X]HPT9U%atgi\* pZ7 a}w69O3O͒ӤM}r37S1Gn]ha嘐a0Br~= jPoi0Kڽ~ )L|}@_,dup:Vz ac?Zcs gZi^Zm{Ew?[cE]ש}*m؀2/M jtB/q苎Y w^IC.;P;/x&y̻+N}li5 /q%Ey0H.\]?0^D ]<.]gtOrfwk%Sc_8:^Y Xh:j?YW+ξ>(z1u?,}.̒VZw߉Pn{Ir$]U^_#}N.q?*fݰQӐo7N'3bBTe7 WVZ˘T+B44x_0yl;LcQmFe.w6XfMG0!G^nЪw3-)Y,2L!V+I/J`qM(@st$ M`2IKΠO}j"9!:pj*~&C3i *2/ vsi$n d.0b6!Tj"LB]2#$4sD[yf VOlg C>A'"zĻ k ;_|)닰d okerʻP%QDso(_*SNHCŃikSwW1 @_X< Cx=%WB0oKM#r3ȚN=ltXoWc[[ -,7^8QdyZHp@﨩F ?{|y<ґSo9Rk 4E Wb*} v{ngy0w)k9c02.RɪG\.r oxʥs]osPT]%79Y~zv0"qG{F]%IjW CcUُ՟j={,PiB E:u.% d.7Q%x7,d.쳞I#j'8"c<"~. :ԫ`҉ݰ"5>)}PG8 ?\ЫWiS"oW~ s *<E2140jl֣\XnJ\Nߺȡ~9h`tET%XD5&&hxbe4/UPXU˩SQ%[sw\I+ZʦAon(+ֲa{AD猰:z 2zXp„U<[m>nܢ9Z2{`&[9Oų֍ZTbY]@  mpa,/pL"8JZ& AWpIy6 FPmMw8eM) ~{/ťۿz^}]Cǽr37I~x42n6= Psjp< RhIJonh9Lfx! &1[9l,+M鏻آnSеp+̸'@KBI0nϵn 8/0j'ܝuKB݇m> \L46΃B FudinVUK\Ǟ6leXj ,NOD",.l\%~M'(}؛k|gtAD\5/q~^1- qT B/aGUi*&; _i$d ԓ!$kƘ;=+(jo>o\ųٲԾA%Gl-E;%vR k;HL8M*a:=LA?r{hk}hM B i/Sg #+DHa9j'Q.5ǹ)>dz&Ā|dm˧ 9B]=INC""Ba"f!́Y/ {+#O˵W )` BI$no1od]PĘV-ב9%[0+ EJz㜅eS^7rKGE3V}xm=7),PO,ft~@/t2t\?:r fť#ا]L|Ɣ::q G7b˹DzxJl , mj; ܡ)Vz߂n?௑4'ʟ&i2$Ҷ%۪qa\,3ZrL0QeS8G4[GJXtL ޱO$١\1gkUGa"vWM>>V'kϊ|= J$ ן'P}A) $=Zc?Z>vMl#u͵JL 2 ~OJ6QHpR^g}x ۦpݒd.xy_#Y?s)u\dK%nۧS9 +f4S 9HaGBOG;8X3}Jd.|ie,6k]1s025p_Ҥ!K³%ɁoPQ>/6Gj}T4Ykzv &j뀜=)Jd Z>y_?7phnyc¼-Y7xrՐlfu N:u'gUT қz|܏72c9ʹ\ȯW"C} %!^P,IϠxN(413poHy#̮@k~-pe3?sf2h*aϱ RCr0v'kZ&5A)1qamX x<5%0>[`gݭT }L(fڕqdwh3|@2Fw]|W ~!ٝg`n {ggNb#,dWA GP;"i[-(]ۗA̡ 0F9R8&BVpqW]KFx''B+p~)a<&3t80"4D+z00dAMm.|9Wь s=6J|J,I2< e(d|s]TDK*di7F0{YohOb:HccY u _>MAXZr츥عaS2*Ԋmxqv9Ğ%'"sYE|_mvQbs7X=${y)T >v*:ŌKL&A?[W:'?E؈Q/wX |Jtt_Zu"k`1Epo-0v?=B#:-[źbPPgwN<+Q<ԃ@ OBJKm&){7i쳖 Mc?92&YɃ 6ЅS#w{p30"3E0v\\HE'q,MZfNcqmXVZ.`n#wgB-^#L| ƣQ՟Q7"tLF,w*G@ ks/{|Zب.~e;7/IMf%f_L:t9:Rbl|Ei3a?dplby?TucnEk+8e&>>jײ[ gӧٞFN ,0LmaSE#hÇ0GlWIpÅD\ր(nFj# 4Ԧ< DVTb~~6-t9=+iv?1a>rK&egh*r܃]qeG>~N=9|:T&i)ZMV?y>xd2GeB&1vXph 1j{e@Lt b y HY@' ,UɴCo{ Ù@z:VN94<; "16,ƻ"'@?M?! `1c}w9v K$\=& "2cR}"{|8JecpH\ IJ%_JXX正`v'`S ZXjn}k[`eFOKf?~#t/)ӛZ7#FL.|$3)}4G~5=5@tq3#J9%[0ropNx!x%j}8vSĬkoFPPQbRTv 6E0H+U?Ya͎j ٰLtw7@Q$yiݴӒ=0 &Xi)zT\Bdme[ޏ 8s Nw:%H,;r|4ILNax2O،^3adGl@[J3A}JP 9aM@gN,9bOq6B0%N$yׂ"̰\tmn=/rՑAFceϕm {{]31 70Vj:Z]$JL217]8#m"qT>hv+-[ !mI5L.FXH(θ#N]a$k(ۮ8LngQDNP._YW,H_ؘL043KASr0wlBq" py# ojLF@xԕ iz) 7k[u})C]=?<|nۮvܰhaR3r1RbFOHl(ao1 CtfCA"_*]y? [N]Er.S>Z@ n"rnhuFJ2ȲCSIpb^0x#nnR%@ˣ玘+傊 9쀈lɭf'6j)*4V9<"3b˜k#]o*>LoEGdtsVwTOòQx3kFinjRp}Ke4Wk({&A8 H(|0 9K;E0 2Vh'=u[^h!ޏ'odz33(*Phw[q냳嶏ojW4ft(g2qVg[ MgZثHєt!C~#7T7WݬKά4)vxl 7^3ܪE`wcFci:]V˭ԙNOD2r%t˱BymW{< 4i(>*WŵgRRb3 1"S:P&oqaI8`޲ϋMC\A J 3*gʠ L)"b| qiِDO-bP`zQJ/BФH]ߛr?epg8ڴ:[2Ԥ8CMڐȋp.QWy#,OW_i NqBB*Si2_Y{7‚ޛ7۩ SU*PmeliU?̢;d]4T·< Q4IV\v`b?k(KXNzR+S|W,pUVȣD#8@ٵ5BX/"yiiKO#-w7_Y0l4&*^swe*@^]PG+&$Bvz8#\Q{Ul>.eæUxZ)X0aYXTt>h!c(Ʈ+yBmWY<t]q-Tg+rŔL1ᖼY•pny9%mR5„74<1)DGf3=fRٺ.^s;Ue`Vizc(q: J Z$.X9{ºp6Fu9y{wgxz/i 1o#8"U8T;V -)U#h}W]{WFgj{&ksIӼ\.rU'7⫵V&{Rݹ$jT4zФL<9/ԥvt\SYjz 8~9 RzJ0&\i慩Jsteq);pҿ63=/j҇VzP) r X=>F@BuE0UC.I85P`?dˆ56nvދ).KaZFkz_w/7@12̃kbf(I?x:;S貗mYAu&}IU`['z=0K ^zpjFD""8;Z{hX9O|dQVr\ 5ϡK`vD7[ā=*Eh 4y`1~ƻfS,zX9׭U5CW?vδE(?⥎qԵ3.&#f F P?7[dKCu௪=`%e2NZΒU75<@QCQ/&U2}X2?vW­ܠ3N^NK!gR߳3օ|vA;r_VhFO9 Pl#A"s8F͛a @g#J3Q,D"gE0cekM=yVHBִK=i|t|lj @7dSoYl=D*  ` k`Q9BiW1ߔx.[h"ArafSQNQr bLRgZJ˦ml++~ڪZB=[,a-ju5({̒u" %N浤6tN3R֭%i̲nsN#t'1Fk6XjC6LN7 ]o+]*r&fj@'-1*b1];qZ./f`Ac/1h1UuoPh-ϱz{P]yvr2d#l*S6&0o+wbgOUcρ+*| y0cϗkS"Vy)bؼ3jrYXY]=bOTF3v0 -=:aHSSAfX[i.ï~S>ԇ SH鏍k˙ Viul6HRX. 䂏DŽ znlh韆%碦ze`B'a ^TvѢ`_Kg^ut~rʦyi'֔K!?;9Q[(R xr_f@ P\ăm%2*͚rQrКe\ È by1C(ϏfG&=k|pթ=E'݇H漁-Ɠ1_A3kzQ ]O$RBB(1pƦ=h Mу(~BUoxy_&zd|gDPxQ Pw|l8jlluKS,~؛7J-;iAH*'e7NkMkurrL ȐTej2+ɢ 櫭KqZV31 "'o8ԍ?lr4{~eSgΕ(ػYժ3uW_@r?漷`fe;⅒9­tҢ@|Q{>^OLJ`٤W;\aWץZ\el?UAT(=1-| "3oyr}lSv:=1jq#[v["W7SV( x* )00}Qi7/i!Qm+ԁGITY9oSZc[v^ў I*}Ga( -2%D| xL/8G]l~Z;7{RRt~AGB?v'Du%o*}1M\yCR,/][ zhjľnA6|4؝fG=s%i-pl5,[unk)MĞ_IgPDg~OiXJb#2NFl[p9y0 ΃A9֩>6k$m!D(Ls+*.κSZȃx1ƘFcqP?J0t7k(9i|7]wSg@@LۥCw/ά<.;"B6SSjdRsm'9g hߟ} BZUgGơn)&pb*DXn@MPd%8kxX@P^N^%F^aF>e}1 LRd"!Tno[%ʈaqE]@uz[d< 8kv|ؖ_ 3.J>OŸA%B$#?KŦ3?3 *0bK-maPYnJW#L8`C crhN߸Jo Qi=ޭM08w$ ԰y&lkKn7?cGY 98-nxP>E|[goСS4#4;}I1ecG7vD㢥6 )LHifbZ@`n1G,NtNR,2HuatYUB БƗ~5LM8x ffIh5  (RFBQr+2̞\hr!D&"F-I̔P8b%ejݓޓ, zG-۶'fG5f)q8Ѷ :U~sy]8m- P_MHjRF*: ޒ0[Xă msv-cw‍a/e67wGGDzUAL+ q !NqE2V؟T9e7pbEI9,_-*yR%G%Agm%v~ {^S?em5*~T+- Yܸov|:uq#S>HI7ʯ9R׺tQ%^i ۨg1>˚,WnnsL3a, !0G7rG 6Ӊ蛷]T 8ek?'2ƭ㚽pqS s di|RL,UΖAЉ 7r8G peBpE+i{ΐ{ڻ(% $\!U˸[a@^zcz#sɷ@_#uSDހM 3331O?/D}|eiֈr)XW{IS}D}xEp8N6!2QK0L VV=pc/)T$=/ұi\cJ{LIjs==+7EN3a8z1d*V>bJˎx1}֛Z}&%R4YehD7;4J*E9%y)\-nHIrCڿq*I|Se8Nk~aR*'Kc@^δǾ|^5-!R!qE!~eUEXJU%->&XC8vr5>GF~o?g`3n{GtH#yF},|^:&K>-S<m]kiiMR1A8{a%Mg I%Wkn| q[ Qj?Z\ްO}|Ś,Zۧʩn֊8IN6~Op<6 spn[GWwnҽOwrЁqv{#12{;^0~՞o\mblny5ao:E. t4h+GDs}0WG6B}X~st/_Ϣb$Pݺ'e|J*m$$߯>=x7^X{2\`5k 7QG$4؜*K߳[$$%+Cn'yjKKZH@3dp p7 q*m)nݼr&X$(y5EdQPw(zjhe0:K]fc1%Bi}w/c}?,++RЛpU$Φ 㡘r?v6$<68އ=Bi1)6!)zְ9gnP#v6=7/ `dxe8Ujwpffa&. .g-4%T4^*n 0lYAkY2(BHnxg!r,廱ꐝi: oŸҨ}?RzF-\CT*P_W&>;(h>߈dVwhF8kpU_L[:8U˩"3}:%2ʍ-~x͔D".Gz+ee„=JV EɄ+.:1$k#+'΁dpqO]_,ir8pE7r`/Ȏ` z=شg'd:8+dtjM[S88g҂G34X9L-Y}-gx% #j֭=Oig<}`%T1,gt 2%$r~bvZ G'k;a#KchzjQ*oUuDiK tnb9 r3U2gW4Ӧjn9!FJ;D/a+IvOڼ';)$5hL*f6"*_;Y%% MϾmMM;#f r:{QFG,Hzʅ$m{bmd߫2R%˺)8|]6zPsx v5Ko+ 9PXAttm (P h=㈪sz Nx`3rZufoì"SJD. |G~҄Ǐm~ɅԜ,o;*TSC!?ZN'*n|!Љm( 4>B5Ρ=Ԃ`O{&igfIG@kɱ :HӋئ*dH,S 똤9*5z#='W ErӷClF/R*"zZEDNf?W`s E^5۲Ή[1]C+&"^ujS/- M1+ ?Y+EBgjZuMRx( vVFAeY tW0&|*Oeo KsN@ceONҗU ?Qoa'|±C6 嵜yv[]Nz(Mմ$R+_50"y#]1C'"̵힇_G7Spmg P"O؝=g֦>p{L+4fNXS/!I 3YbwHߋG#mZvSn,Q↶uYrPr(B|i \uZ{9軣˧KЯ$iESi>Hsg@r i!.^{m|TU,#b]m  $\%!BË=`6U@;ǯrCz~MLZ _m6X3@#w%:f8xkGZYAHv`"ؐx(4E{XӃ-睇e*œs?Nkax P2rJt oKՌ?tI5ShEI?hlYn]E@[rƟ$s--!#Fo.~HeQ .Gp-GP]! @U%fB"TTXUZ t2kKrE~*f| `D)$>?j;E!;+mќrJ.bkNg @x)bzH+ϟ^K:A4^zkbv!mI!.F ##5]p/| ~R:4 >XZ Ci1`rAgw'|w+s F +숉8'S & qÕ8G7; +do&CO{#<T`6L\l뽌NoGOi$4K0s%ru{2WQ5c v{#N9-,~4ބcVڗzfL9D~ݼqfYjez:Ȯ&o9ԸAR"w/)?Tdį,$TgBa=fls2M[5EArbĠZIZ,H7}?S/Grd& Ev Zw1/ ƻZ=iQq[L')S2$o.]Xrg"Q[f{SIZaUϻ1.CY C"Q!)N1{|g7xNy[Nʧ*)[͉+0 H\DɴЧc{8D-3Clj/Ȏwo>L_ wIvdkXX.^]Q5~Hȼ}afcbFctu]rQ5{OzM0C={*?w4̚X1^HV~FPOP&(Z@/%2U&x}>r@G]氅3ٔm@Zrl *,21]xCU\*ys*>u-7X>Koۓcس"nڑȬ) wPMsNq1ѤK(:ZG՘ dqysߏR=;?M[#'\â+f/͈2!jv|,w)j Ft㼧J3:obO؞I09R lb)["`;gDtJ䪾Qz+WplY<~.TG+{`#"Vi~iUq3,UY+rݯsД͚ Icz<˔bSf*僜DA<֔G䨯?7w>oh\ʊ*6┠=%A dH2y'kZzkshE7Lu4сRl\ y~gʫ*S u8&|_ّHQHօC? xcJ>pZ)^ 0a@"J<8qLDVC@ZMA%}mQJP_RKQcg g9$є$:V[\J>Q &3t[O;ұi!q6Ge?Fv T>ZtCVtDSM6hQ^?ù&}j-1DZOBa}# @ΏUs_=v> |%;e϶lu#5]ʶYꯌa"_a7c[[#Y ( Vx)gtL+ ~}}+R5绕} k&#XھD'.` XUAf Dla·s\^<) No Uىpx%[bĥv' $*]hbyeG"[34=.|!YQ ]\{>7mʛ"XC/^ l,)1SN=w 06"%}MSL]-%`^?@T,>Be-Nn+$<%5 6;^8y3v .%3 >''{#_G# n&hʫ~)WBLAdM|ճEP+/V|Kb梇 '}OqٳsHQ{Rӯ 5Kl+`u~bZp w&_$6tz8co;5oy0RѣnLЭʗgTWNO{1tG&(w5#IJ۲[WDIuu jU}s=X6=E)"c~d- Oc|l,4ң+$?Du<9T76qJݒ|]YJ5S҇Hj(XU Rrd_iۊc |y=뱢a2 ,ԿOpdsqӸJ\nӘ)KDRNHCsҞxB;W8e}LA KH7CBa>?dJZ.oV܎S3+s\U k7ъzt䓇gNS/ޡQ M;" rU; <3R "ׂF+ =⹥s#^1܄#}n~h7jIa3w?1UU!'= HJC{ogbNi:pfツi˂>u)q_؎$iޮR&Y_h .HgU\pa&Įe"ۭRܳ2ײHH |Νnr76AʂձnCrRݯNU9u} gYL?YX(a*Ǵ{I\Uw/AGh MZJׅ9Gx̉ÐA ԣTm{gG&S [G8@i`+,njaOmHO4ngBSkl5ŅPOh8 , B>߫h.>SVͻ%8Nߢ#bsp{-[c5[CĂ*o%>hVuڡ~8`[29bvhI{A76*IeZÇ-mF RƶS(_t֔~—Rh4NPcLbيH9 X9+s&v @t^K( 6 c| 7OtPw= / %Qb0ki`zo=hOd{Wh_eVZۜ`rD>jNe^D q%!Pר SO<F; 9O0g}ҫnv9=dm-EU4g!,\ N}Y$ YϩsciHDUHImcsRV0Ӵm/sI>04hSc畔T 8ۆϿq+O imؓYw5K{29~t) RFЙ13P@S ܫoe@-oOw3j6_4 Wӹ WM$#O!"Ć8 i; HD6k|Bd?{SSHxRl(4_w9|].&<)hd*`F>`2"qRR91aQ !Wg!mToLMQ5֮]@Grx=T(h<6('\j&qڮ&qx=ipf:1=wcݴ!7IAY*EWJxBqA 5=Y0VFgJ9Kaoل-ND(OS( ww_ׁ2(Ђ3'b{ FY%R1P-SHWHxE;RS,S7v*pz{l'&D ~-GE< !N֯T teGbɺ{-Aȕt1PZp.lݥLɛ9\Σ;'G]is 9,bMWBz_!oꝵ#8nZ(b5 j'Lu[8ב&uA3U2lJPV_on!>Ѳ&ER; UV{'Ŏy)=0B珡 j(RjKek|Q&E: jw^XHBis@`"E{'E 5m$I !ӓ`ZܿF%CLTދxBb͘~V@Av._>[#jލ3{8Ѭb2@n K΄s%L+efY.vrM .\20Ec =2=c_%/W &)mu65[a(֑s:kR+H6964 [}bڈBmOҚTcE1dΨʍ*-)bo {!glKeye+ )B: a襵SMq8t_t]{"OE> >PzM}7jkEg7+v} [sbn/ˣC`/(AkIcF1?ul]SY*EʸPo<ݩXyᝅ|*yns6/B.LBq*.\ @D'0#(7[Heud4:Q,zdYt!Hglg #Ni۶n[`FxvC1j=#ęYq^BhA0k7 Irm-K4cfVQtFl:K21ƀis3_&ᜡ/bX mKo'&7yd3s+Fl5E&Ep˚FI9PTW~ņ%ܭ,m,݆6 V = 2Y[lד6~]hJ6@T5N_Dz4cHSQ]pΪ$dwcE"-&5+vL뱫;{9n&0bƃB7-:ѻC7͇ǒVs9p뚕um&8G|-Giu#ZzR|"^e2]2:2}ɡU(Q"U5sUM*H;9kE䎬7j`'XI8C"mn,sWT$1ZU Ps!-x-<D vz!Z˫Ֆ JW0rzb Ov"J eC%5t[A6h333T -u[{Ӽ)N_S~-3O8iM)h5~r^6Xθ 9j Bg5W(jR+J<4Ɵi[NuGt񁜂7y1@'ٛj$Z-foprWo˙3 ʊUN{o|cI;4CvfB͆vJ{e1 :N>?mx1v$&o(zy鮧+LH2&sf=TX' #GC/Eׂ/KU}:~覉]~] KF!'^_`%2f/:2˜K(qνhsH6ޭ̟ORu v,E(`zVl%" bt5{'kea۠QLgVRTt5>Wߝ-גhT=~C]Nۏ$E-N])S[(!p%7+9OFԧISg0lFySNJ GZ V 4m,E=DZ! JCeUD&'{#[o|Kշno@4N njpcK\ڳt%zXN֏ĨcaCf&_u3z-d!Xf.1'pj^-cvG# 'lqީV%YX]5HqW%DQ.мѩihXƴ{MwT&1NEV@V{tRipr,'ݺMKd l2&Ւ+@XI:K4uuEЊ )n[CQCB:AU$ RauH_n,WA [bgSlIT+h:0S 5\1 V$$mb,EV֡'H͚5[G]ToR8 |>0b@OBJ÷ȐIћ)!yh<wx =Lc,:) ~BҁC5kڞ?Fx1Q(9S/@RZ6#rpCXa&z &M0X2,g[\}ZE;jZȔ¾M5t:<#uZ@D#bab6V/˗x+ =}'Aݘ6z홼K5S TjqępׁGR F> {&1Cz8]w DSZY75:Hݫ6-GC~>;\/j~Z{ ō|bS81fU+BLԘK&A<8 o߾w7vWJypE8nEVa{$>IʞaN?]jC(Uk]ؘ)}qI(VM$W0?P7WZIYq. ŴKyvRcTI{თPs sumT>T~{p-sa(o A˫ \}kl4/2rOO."+*?=S;,O, M/;WiSZˢ=j:u:@Hk,t}ZCaWKeߎ1sBYtFm.Z>0s!7_"E>JI0-P0&,0t!;.x e~=O8uಝݞ '1mAFU$0Z&Ȓr!g*Rt/#8t֙][ fS/p9Cȼ?RV|3HР zu5a6b->)hIZ+Μvb|716P P} C9W ۬|,y~/@ hѷAL72&tW)L*JCl-Hg,Pw KwZ>s4ddaq>#hel ^Bm S GN`-wx:=p1lp8©waW3pcyzzT>9568]etw|XXCfy%UMOGc\y[.`.5=4x+57Kے^䎟X; -")olJfR#}p$ ^SqUc# kIВ f`s uiZɥM>{rF`4v7AԪP"f)ħb~/R#ݤZ+oٙL{pS+4F Ah'G2I{7)C-KHL掯6! 4ra;K `b!f?ym먤w?BN\לx60mPM$o)x+#=&S@WeȤIvuŜg@e0@ͪ<=xNP&p|Jh.6w 51rϬW!x:׊s2ћ#275NVr*z5]Oue,ew0:l$W:=TvA͸S8 xEdLjKͧS2oj0&" :.[4 %B*ˈR]V kyޭ)md8ٿA!-6C\dɡChYj(G2sR|έA-v^4fl͂ ~eMlnDB{Gy!#.O&S3=1r VkyPB;!EWnHQ#1`#K*yɪf[ 8u88ڃe[&v{Rp7FƮ8x"v` o ݢA^OQ>ʨjwG9]*:rDv'"yqQfRnO]OovjbH]^<) ioG#¹Ũ9޾!B67Է6A65Zyq-,=saՎ`ӛ5'jdYibzip>f!rS1P7T|EZ'SwIQ۱yxG#ĩ=Ϊɑ @#q:/`"iڗ޸׮dd^Їz+>hT0w5΢iYO7N-y#Lv7gbZMGK TcpԦ4Zp6tП ]wb{Mo{Ӷ|TxSY|-rQklY|:ue[8akXb޷,) =UU`&@' ?+KH\Smv4x2Jp_qQx´2jH@l%5&$~ij:'(i.jqўäx9{Udү`D v5dj~Zf 7~D [WQmv/\ߪ?~w(a+0Gz7 eW{DҟQ!}^ GeBZ96gp2Q8_BKc!|B/9v75_s=BG7Sb7b;645Y3Pm=DYNt|eq.fO_qߛeJ@$g PQ!M.VFb\# {s9 w*ߓ_]ZOv7Umtyl(C}ƭU4CLON"~A(_Qti1gF R_ں=X }4rKet8[vYw9Յm^M;ߚ&N)=kkbSB[Na C* rAWk %Rxb",H#L\ݴ6|dSHJ35쓚/Ca6C{$l"W*uh? NP4n4WFn05[l&n V*I&W}ӰQlOCj ȧwΰN}V$a[}f+k؄4erIMh=[Cc!1)଱G'EFtF2| ;ABΑeEFDF8dmNu6:e&8Z1+)jcZq-;ȋLhW#{1:G_nĪUj F;Z;6aXp{gPW $[|4k +I^YF N,b k'u=Q. vFu\&ayi!AF'J><%QoMV8ZruʷDx*]#DH0;Az`gOIE]odNRq}`8qS0e1FMĮ#kn6bIn"1)K"śV c]?gk. ]:T;IK+[cŃhBWr4.d&y̱/j;k _wcʃC5;bJbk{C_ ypĿqHX-:(z#Q7-i8<>mr({THWRIwx (TX5Ӻɹ&K{ns;H!i_8e^ 1ΆU&eCj C7BMX/RIV@Fh5 -3gpw1:OQ7d5$XqlryʙT$gÿK[N`|zg`9b`7c:ZQJB.ia}pMfR8) |mj=@S.?2Ne*y;G!:?Ef%B;%{3.ܿS[e  bGtOST;d0Gކf9CemkO*>M%As"%̞B^̜RB?N> PlJ4-IxH4 +1-gkj%5(QDx4n|KNS m$>:3<0uΩ{}^U1F#똤c(rt?n S`);@M]o`[4" :M)Lu'${Obh@TFOdPd}${W[QEc3/9H.n}={T|߽PPdEVt fޚ~@XS3Y]lD\a K~DNm ǣߵUQ6VP=c>e(,6jC48VHPVAec3*{mΘ QlUSD]<ه{c5.CH4Uh_㡜 wWDx ?p7${ ; C(,\oQŻ~^7>jz~Fw -E<$g2d2#__`8t.X|>y!S^ʧ]t~Ä`PiՕܳ6N. ߳"T8$ p4_1v+9фFqI},#UTXeYEi%8'pr:nXF 2\O~W*ZvSAFr.2vOLsLl=#߲֗Wh_;j<ZVEM;n36E䛾emk ZMYuLBRZnul0Zz<[@xIw*etXFsDI!&9qݍb\?@d"M2Gr!a cDQtFVIX(fi*oḄ$'HSʎ0#["gǙԔ,e FM*n~I GpkK[2/=yM6 O\JQK"Lo%w?䉡/0&IKifj%n+)G4MJ36l立1)_>vbg;{P"N5 ;"BxS,zw(G2 9:Np&o"K}Q^8Pu\ֹ׭h ,;;W؀_5@shw}ڋQ*0_9Ÿ Hyaժ6|\񹅥k^}7n@4Տck+y$I9S ca s)ψX@RHH04!753ExOl,w9ۑ /6h ARϐa{"l R3s}f|&y;i6Vp3 4,LޝZ0_SILђ)ř$Iu[Qg H%d]ᮑR{WY( C.Y^,MYWnkؘ**pFfSKq<0z {IԋVSp*z )cq=Ѭٚ,KүB (Fhc%s]:RXxΩt[qo@j`Y&x'rA/E!BGiUV`$~y,a,@Qk{Ҷ\{P{='}~*c t cș{+] &[$u f/T 8礂^,2fPG7z)wa T !۰b.,H݅34 4z#EɋUcd<0V9N,X7NFRwlbAv7Y=JBa/pȁ}uWP=~T >YW[k186BhݱWK<>}#qu3^y\'{ۈ| p61>f-34ᬉݱDwytzK A&k ѡs4\ NTy} ciԵ,C"6Iy*ݶoKUǸUq@\.E J3VQ||bSԏ/IW@kPTk0<ӟ&;MY!E1hMw4EO`P\P^'?Zg!vjz\>limO<Ys +"X\AI)â1Ib_/It+dAWn]MOJAsVt{}m*R&{eA=OYZ:[~ݤ){%\PI!xo2O͈},9#qw7~WjX¨s5bRN.b%co*Pe8rZz#Hb m3׆Ĺw?Kbc3  aE|_tхspj" R¿~ռAo,ݍr+]zwt\hNTohdƍRķYnsXvX0i NFa2oM@Q0k:3~?XNC/Ѩ~I ÊBa13 *w;HQ5;z٧NwJPJ0 :%C qB44jYgutR՞Ϝ飲5@@,fؖnmבyt[Km[ɖA*&o}`Ǝ"KyA^|7Rldx.blRIl!WڹͩCVXĹ2mAŻ} Irnv=]J3*n/Nl-b_orw,7cOws 'WɰP襢D[I+H q"T9Z d{X9_/L2{$>uJzG M0͵'2G'Ǯ1H% LxTі{5.~XoB /&wBWq^}:F|ڊL -e-̂nL2 (u1/6ar@y05E>+JI;n<=/Q~{b4}׺̓Sܹ֝rjb˸{;G,FQg<w)vu[Y Byv< E[}Fɯ͂ݡhhկUs&@ޯ\[w_{ۏ0iZhՔ+ `jg3ÄL %G Pth;e2Cf}[ 'X!U]Q{/ a{F\oʹD^|!v1p ;zU 7pkڥSە=U 8z%X)]<ʱdM Yzjjs*WX*E|8} g#+ee-}CNYW(,jTe[Uzq:Vg[͘v1inN?jU[yS|Z/L\I')_Wzboh%ClI|ƮDŠr'*i!2΅b46p]_GÊֻ,!o'<"Ysӳ;o8ݮšBaCP_k\U= 't_#w r(OoZ@~_I샗W!%V1Ykְ=eVcp~^#mTv7e{ hanhnfMo.wFS=2lg^U IcsOE\Pf-䰝7¥'GpYgZ+p:7OB3\Z/͌ #+gĎ,N^lȵL.#{1﹟[˼әx;'B.=-H\ύ 2I#G\Z@LJGB[y M- lǰi6j֚z^pmnl@~@@^8l>" ΝPiyC70AaO>!?N=CUIBz#l v1n3;Yc ;Ƴ\b\KW;jKT$QOv$}8OCyM0CkЧ!@&4tӻDC-ZBUgTu2-[WJQvWK׽*qF 15ը(_L$E5kG)WzI]8Dcf>\bm4>;GmQU6+,']1ޛékLd/R{3zqD*nM`5ӸձVf i}<.ɷ2Uu}-Uaû3>Pj3 D&dL?RKJco.:='S ;=1>IwWIc "Yqs9}MXR f} 2[ԍQ3єC:X 1_?!X f/E1^~ N#: ojy.A7tLc? Lv U{\tX]C57sQ_D4}@CZ?G.9Ot++l/94Q]RV n*Wqv/vv; !ZCHDoEvL,ه{7k.W^&*I8ZV vE;#DɫME:ka `^Ⲵ,L5fiq5 88y_ 39 3WVPlʙvw x{b1 ݲsС N,rjGK!ʬt^~EWT}O٭]WL4%/JFQ*8S~/5*א;ƗaCۨL`?$X5ΞY݆}82A%*a/ q|3(4kK}QBS?kUJ)3:rBvqojEmAa~AIJH]!1ҊO1ulC2G48+Ҏ!9QHx{`N;@Zם(&lX#)+&PÝJe5ɏ^EܦbXVmdͳOD+FoݐagѦF#/x EE }gXMɦf!,ʩ:` kPְH< $O@MlM[<}^ψoΒ z`ɢ!ir7a;}r7w1!\CAzRH~@Ĺ뇏Lin9!**&-xlgCS S!X._kpٻetC%Z4DS"Ş5ATUZrRT-;Y>gM2@:bdJl^.⚗izʻDJb!  $6Igd5_'Uώ5f hS){̛QG4yv+X%BWRc`R !M=PLSLQG0Q_T4c֏9xExCh\ǖw'w1~\dxl`d­fG ip keux9 k(v=_]aAބ'f7G;?o&6^y+p<;H% I4{&>03'm2dmNDjYK0Z+־jP0ѐi!p,A\<;DֻX\4Ir0?]D>X}2ĝd6%9ൡߕ>WThK<;5xKPt+(?њ9T9y>"|$\ Ý?@r WފE 3hʗA3ґ3G!\=.Q(o!N7sK* {SQͦ7҄׬q$~_-x ,HkT ><*ȎYLs/n^'񔄈A~bo~k_0lΡ'Kze`0\|bVϐ8j-)+kOpi6 : `xBtVeҊi N[]OI˹x阬Lo"ZCO3`Bn'Өoj^p)`ӭ/3y@B_$vU1'/&` +#jQ PR^9Mz/N_OojW 4 4fa.~@.SL.[:{u\N-_f;X;2]Q>[c)k\\XkFӝu")Ac^1R6P4oVmܙK;Oj^Av-o@e,C7SdL$soKm)yU:4ZNx2U G',qXRMuMI}.2{$:(籃֪DmSnx벃|75|ؘijQ>RO-s$0 bq}cs(ۗIxWPcdl+\g[Vd`š; N$_!̀|= @7K~1vL ==;S5Ϋ)G]?u~JMHg˕8r#jjkY!C8$ 2 +c8b5{; +}Lz}-M2h1u3"62#Xr#K 1f.*H3'sgC2ĭ|UY#5Nsy(.M:c=qfn10~-=zi5W@pXq嘭k^$ZZq$x~%xDRW-e{Dʼdq F\9}>d~IֈjwoR`Ek">t9-z-u02B.COW+}af O0BL2:Af~%/kAR&M1^pk'sTa>iu$j4:<IaT2) "j l(&'s1|yֈF ʷ:Q2S3! ?HV\( |i?*ﰌ% 4T:gJhYZ}su<LL%zG"%tl=tSĊ6qgo=Y&EyRJ [v*>mjH$RJ![-Y+،W/p_} *rwm Mwz"F2`scw:^^"_m0RkHR_95pyZT ^ ;$C>dkH*7c$N Zg].V's8w`c&d=+ Lcq9~(-'|q31qݰ+UBǪɿx1'w6.=:EJ/ $ۙ# ,U_WcA Iqoˁr|px|ϣ^ ث?6d-ko핐.4 C7ѐt:|S~ 9/1e30ux4oAPv489jN3'+Zh>ԇ|_6_,z˕܄ihe-kl4nU'@Hg?GTs ~^ ŋXnC!i@BòS*62gP|MJVѹfgT=&M:namBPY_o'ΰ)¦AuiE&y0Rkݮ?i,}s;ӐWcT*`t"2Hn;umd#"k A/\p6'BCK:92(m0|9!u͈V)Rċ}ϕKr^qF=NF0//Ihn[ʷ660])FӫşvMq{DntFxS>psP)Zl`uPq/s "*O_5!ց6{\QtfLlozh{]mjk t-"oߗ{~XF_ID?yE\V)S!WCB9մ1+O5w]=09 S 4Aƨ?99Zc?ct$ͷ>Df>s,AAWUDMӶv AC_.P6n*$ af@݀L*Dٲݿe9i<: e#Br?&BgHDi{O~#+]5tFiڡ{'3𴥒Q}4fNZ)Q5f>9/X||HE{JZyW C;aUr 3j@\ĮN(~N AnD#h'q!faEئ6ɮא>Ȭq7p_ Xu6}Pt qzgpJˋKBdF^/)C=dw` "8ȁ.#@/b-(av<3ZƲ]bfNC]Bc+nW7P̛n'Txn1PNANC3\LZG-cc#A#H:2KIpfU?=Ȕo8i%=؀9YK"C|u3,3 ҋUWWw.na0Q}) D ^Ee[ 73ѓRe,iPZ(/JW߆NGYIΙOQ^ 2ݝWQƵO|TC؈)ӖK >*bxQֶth#MX$"eS}2{VPjDC΍􎝫..v–ͩf\h)KJ[lXȨPMQtŋ^Ha z s+/z_ yjRiG2jh,LYl0@[jT uuPjo6uL/ILd;ILԩyHsM l UdZ ,bae6d+k[7jUk>vU3X1[UO\>0r“J&:++-"E}xԼJseKrt0JLp.Ԧ`s"@0RȂ!Q;vmb9h~i@o<'Pq%UCrs{ 󽦍Mxl r1O "[\#>G6ExQ ۖD3ej9'3ͨʤfnDs-)ijtj?QŸ{!DBmPGn(WV oZ"H`)k֒ӕ9::)R0K\g bmrbF1~k(#ya6MGp0@fP/] $ 6YD}'tdfVHմՌTFṕ1ܸZtf{ ^ޣ? nNLi&ę6Tʗhld a{F彛oY YT9>a𷐞 #-֛@XD-AM j1IF<MP5WWzo_"q:9HW;Ubdd{&``mKZϧmj5wH Ɉx H9$xe- g⌺ejZ̿)>Ǟ ZMjrw'ZsICWYWr%/{ [:Q:-O1-\JȭofrX6K¢Z+/;bģn7b[XS փ | '=+VNpi闺F*b=nM P#h\O#?蕕;G 4:b+,QʦpvWa%/cç2,#]lz$^.4*N.B>oVl-/IΊʀRL A `:T RjV ϥ2r\\>ޜʁ<,SQ 9G>(< tGlGHfCn*~ALua#`@ƌx]x2ULWJ1gJ-?q >j?V﹧V-W\fW + >6*˨-@{(L fJ+s^C0@Jۻ9걡q#^l=|; vzc0ɴj|>"Vũ]":nI&|̀ /`SْΪxyo<ٯi||VN%8پ˛ eR񏐤2) ͙Qy1ϩ Z`A/kVr@;efq_$ gpK JUFMN= H:Zcx,޵O,d?pVZ!Ax)7H[sR6,p󍷢cGa ҅y)Ql2ե"}ebHCk#yɼle-ǰgl2n2~\onJMICNj'7>ŁKrK?\єzZrEw|]ت~zᨼHzѕJ-VCZdd8afr< ͅdA%̗-mH3"1Bfs"n\2A??e*p9ɨ ^jz(G `rp8BY5Bp Ԙ~/a<$|7 {bp5퉊=rjj̹)34ѵѦ٪*[J/ *pv%ڶʹ0A.޸!팒 jdy6vWA1Jw+V9%,BA_L {4Ÿьcdzٽp J;O|lm ojeNS"WSLuOL2wT/À_X 8QXNF@<ƒ}2m=^@$ 7=D%Bs5W.FnOD@, fx`ίy% _u64-_x_Iv5w#y "秙~y`S RF3Lre_b6 9sɂg7_k|l<" 8Ohu%@'DeјF`h_}$it` Pn/@"]H@(:ce{O Ӻ{B ,Rd!  tx*_7Ёsf'U/&+ |$x"/2sT?tys]ʥZ(c bƁ Mh ?R[XI1Cѯ ;.g<ƹ"25Tu_9s@3oqӿ2Mۥ1 Y-cs5CBjEdhSjWto2JX.QZ0@M? XDgy58VAp0ƦJloM %`={-sgDG}`#]&w e޿3u3`V׈'6^6G1pNCG; m5/ϒ[ lt-l?3 `0gUtBc( uCǎz]Lm F- 5J*fLq'hm.vǐq~wߕk %;?{*XnD4VqdM[3H9OHÞ# jCE&ySdo*@TAS0Lyl۶m۶m۶m۶m߱mwgf/&;N:鋷IW%bU4g}?& +;KP=f𯅻(700WXty'1SZ3d6jr~9Yt'Z02~.[`>]@uz[d< 8kv|ؖ_ 3.J>ŸA%B$#?KŦ3?eN 2U`ĖZϺAKB[ xY+cd)]4r3ိ]3ɡiK;y*˞Y$Dъc x6%FkW!r( *i, N$9q; k}ଦL0e6J^y֊}v&-XDH \id'n5/O).хGړ--]lASTbPVy5HI7ʯ9R׺tQ%^i ۨg1>˚,WnnsD3a, !0G7rG 6Ӊ蛷]﬩pR~Nd#[5{,Ac_)@䥘X6-)?僠nN5`((3ʄ ,9W!wwQJX%IN"Cq ^-FPoXd!F$+"oٛ.g8gfjcrן~_6 NۭCgVgS|[2 9'X(6i `ם@S3>mBd.` z(E^S'Iz^)c*pHi&'Փ΍ofpcȤ%T2|Ε5 c*!2=\7.LJti 7'>Ѵ4q/V7nvhJp-DsJ S"#Z.MG=ݐT冴&M8fTwH{p,x;Wy~ִ 02ԛc *%Կ9: 5jx`mGm{9;yvsؓyyD4␙gp[d71^ _)l9hThZK ~GOl<= t? *qݓG+9Wo:cH*Z#&w{/U:4`&P1}uk+dd>UNM uV\.ƹLzt+d0-_t:w踒pu}#p{m@Iۡ?j6$-j3Ufsy ~C.r!`{A 1\ 57cWѽHZ =BuNpl⪗q[(e:FX;R8pllxcp֬5o FA`sD.f~(J# ޵W* ѬHEoFjy3lM&gڋt!gjBdl\Axۖ'Eyo Nk^Tع;0(Y{Ex h߂%!/Yr83T;_]%~D!5uCphPRiÔHQt 3=&G1F('+CAS 6E+)]z5{xOi,IZL`{4MgYY! >"q6/ o$EgN!Z@gpuK?a.՛)HR=A ;q]xuuVVʄ {$D /.O=aW\t.cHF^W;'΁dpq=X.int+"^v@2zicN>WP$Ԛ%%q<-8&zy5:q=]gi\sř.ZZJG8Ԭ[7{Һ9/;xJ*cX +y"V>F}XVψ'%K,5Q !Y*ҪZ)p w?^(k2hlҺa|>e2s7JlH'>h4:[eOֲwFj= Ԗ;UH4e+=EwHʯ9IJOJfrdήh.{MrVC.w_œW74&2yOvSIjbUlDT@{wKye K}.nwF::u z6Y Iں.S;-ڄVQWeKu+Sp`&Dm26j7VrXBQ@hAвUdHDߝ;3f2.7LYEF ʼn\`&M f vɅԜ,o;*TSC!?ZN'*n|!ЉmQ`i|kC{bY9̩ڟLpx+F0N!{w'w[eo%*c0.CrC.B|HؖD5pS(W15s ̤8[fR'=nW?kU$gH䬻qe̤Gꔅ?ijAoL/CeML9  K2#L{1 J{uBV8.O+y}>fIGk_kɱh :HӋئ*xH,Snߝ 눤9*5z#='W ErӷClF/R*"_K7U̿\ΕSkeϝcb p{L+4fNX/!I 3YbwHߋGCmZvSn,Q↶uYrPr(B|i \uZ{9˧KЯ$iESi>Hsg@r i!.^{m|TU,Cb]m  $\c%!BË=`6U@;ǯrCz~MLZ _m6X3@#w%:f8xkGZYAHv`"ؐx(4E{X-睇e*œs?Nkax P2rJt oKՌ?tI5ShEI?hlYn]E@[rƟ$s--!#Fo.~HeQ .Gp-GP]! @U%fB"TLUZ t2k%"} ]Q>P0"\5 GhN9 %VON3 1=O{%eyj% ׀xڱpF@zH6mxRngÈH/b 59_ y#Vpr ihmP?9#GNK$;b"&k CpnÊ۫ PG,0SA lbsrG8Ne$_IT!usU)} f\#i9ABN>6Nao}lL(P ޜʘ{& ^[U':\|MU 0BQ.2tz̭JACEq2˻xO88,R 7p)"l@p݀W~EsBh!HtE\VQUr k{;#Ff称^ >Fr%]Je)R:rM|6 IîrDo{_jjnC%8jSŶtH{FBñ$ c0_"W'C~U.;`g<N씟MΙA'O2ھM8f}gƔ3|H.K{gƣU!7&#]. }ƞskݠyvר[ }۰mO0PV/2Z&"wċY*Íu=(!ZtU=V[nbF ?~XjRhzъ&C++%/p7 lO[Ʀ"+ ~eI%:r| 1 0cm+ mR 0j-#ղb@w Vz=#3aV/_XԺy1ղTIub:I!xvҔ[8x2+jTUM2 {pzލq9*:\HAt([>#7!nģtrZV>hWIj_1qW@4"JEh>-Qe^Md"=:qgξVCdۼk{dK6h(-_eP׍FlP>iL+f81FjQׅ*U㬱ޔ; 81߃GqWHìɋ{j嗯kT E e⋂hH!^a>8 5|c>\(wD|d} OO o[:M9v.,j-ab>T= +oyxtƥxG[p蠽 ` N>4h$JrNRuޓCpd\,ƦzZ^E1Att/ېyML3\4?BmM?DcCNdu \u*r}S>z%5eo$'Pũ]xgcD9RFij(T3{{|"|\է~E`SɃZGUo^%DcۀxjD ϲJu1ÑIUҩh>l!u6kRz 8l g:1:R,$8GD0 v-eN踢= kli=~Mg*-k0`IdFbɓ<غS˫r:9xB ]Ƶ e1P#զSgį|~9G͕luGt3q\B1Ub-Tˣz~XO2qU&Lh@bq\BDDtQ@ZS08HQJAZIZ38z~ל3d1<ƐHð=Id /MX bQ㥟EfϷ|w?'H;7zߚ8Cc]d卜`.:". ^C5wFЌ@1p=ɹDG~2 <Rnھ]5y3ӃtYh6dэ #!g}L4Ԣc(7LhJ{y·06'B^qFHb~}v*O#$XI6&>b1F\w!-|y]H9bAF7@mm_`3H&Oڔ6MD, ,wg[!3R,]5)397qqT`K0~rljsq5}hW{Oo?D2iQ87,9ofs3DUNr= tɯMfAaG7VC d=Pk{@Y0~ ~B@O;ǣ/w'åRbqLB-RTJ7x5ž{ +m]ێvvz@$+Uo3},h~T"eѩq\I- 1jO M:KRq85 ֓+Nkg\/;A@nb\N5jpRU `*8Ruvqmg*oUc!Ab'MJRʗC[tɜs nrr%H;%9 ag܂᥼q_nuS_|<Yi ghՁoJa̶gC>~),HoTӠۣ "e$f?gՒ Jk(/҄p࣑{ZeB4hYn"'ծ 6n.%D\; 1jXR8fFC@q;!=)H5v}K;~A'Va]ݫ+Y3)H`ГxRkFqr\f1OG!sA|(!q$3{uJdŚ?9Wy#tx:oY(Æd?M1#uC3hD=۵J;LMP+>svuN l˂SAvNJgr  2?T-K:9߂aZRMwSC3 m Q8+*?ޡ@dXG741=GX[S,vBֿ6{C =SI}ߺ)ⴴ2]svB&IKJ~͗0%i|Ŝ-&Ir(tbՠ;9@~[?u4?D"p9'߇z, 6`w iL TW@(rtխti|}ʽjݤ8Xr>2K›'/ۍFhs!jz YQbjG*HQ->Ւ\ 8J 8zܰ,$ӻwض%M*FFiol*a ڔ)]RZʖ;,?Y^e 66q%8a_05qR9妹բ/d 4HS@L/lL˟$(|,c\Ò*1Z-Yx=Zțx' ּ[ܡf!ܶ~vim8͋$~MO<"Zw ˙4}@=8vUg(W)-˿DqCr3.UٹeuU|s6~VLAUj֑ ?}kW:O.-YJJ lIڦ~;g*`>@a0|~b`-?9K1X(@qTl;PCZ؂n$kBGHO{1] VrmϻR*-d22+ (`^T##d&O?\2[n¨'cT.w¨˶_ &arX6^sWbmK Yc&ߖ{Waow(Վ(KxT(%aP؊ΩzVNmꚰ#wq7鎽wЕ PZP-gމe0EaD;*)rVU3.*~MB]۪ *N ^g]do;{P`YddQtY}9nՑ9yZF:Tq1 7'D<wI jQ-G5Ky9 n& %Vh1`?I&qԬJڣnFMmL P<'+S2ER4.1z޵Ci*1**KL>"Kx4pgg+)/(Q%:z{4\|w@U.bn>O?իVbcM=Ovy N67|0~~u(jU&O8t~,LfssWZ:Gr4E>_z|F&o^sTiQmAZ57jr'D/a׹~2Vׁ 0H+-'_FN -6*qEv GyLƝz~7P><fc,_u\ Lf>U.töZg۶3S{eY|1\ faXp&F_GvwLur0l{ᬅčl}^\lބ +5KUXWNxMz@=sY!RG#*U>N1O m{1pTWbT*(R=֝ʟǭx qdtIpϿc@&":n =l'4UI]&bao*t^ |c~ vsːoɸc< ?"t QMŭqq#r]xR&рX/ eBG1rhwmdT<)%&{&iP8VRKo z>6[u"_|ecس$ dL1M'NFtˀ;8 t8N$)`-PﶫPUx?>™!+8`&6d}!C~D2)'fliN{׿,=^ BwO?¾jD̺o-c8s Ѧk,^fnǧZK48%\b20Ŗyq!їw-B)`gx4 O*,XngL(S(eD֣wKSһEk7IQɺ灧#lY iik3'"c@<_Blz! W1xu㮗xh]mGZ/+--l2 XQ(YK'Z(q^[%_iH }Y: $Ŋ6x'B"ےHV-' h5e˼E{ =fTaG!%Wb /i^GFFZUł0/xrl ,ִ2- $ ٍHυ9 ԅ##wFNAk>N=%CoE$#lW-ef61ULŶSтQf$mكlz77T5Ho\yRCEo >]""S pC*:>r ny^ɯ"Dg}/\+x?FXl'Z#''RL&Bk:O%M bR`g ZK1~V=vn(?kR^Nu|u)VjdxPy f;! =&gU 9R% Ԝ4M2S|hKtUs)ѠyDjT H#tߙCu[B ouiy ?V@ _~3ZdVk:Dbܟ4 YF` 2X@-K:utK''|srT}Y\VSmsן%RFJ5'g[ vVU`}kݾr6:9Ÿ5# z:/(MۉuV-B<]1qKN3 ?OM1y3!8r&qN580yE)w\~{)hoDc1KG7PU|Npk{of+b ?qf ";)7_mɱ޷/[$"cO\0axr+b>%CqD)UPQ? m#ݩ˵ @]+bP#@^%(`҄Ӷ\OfK޻|EdKxkkUaKGobyog 9WEb޼a,ZAVM[FJ]w &sX cH`4>4,柲^qB˅->=|ω3wjB6x!SXN+n 8*R!jk 5gzB? `BBeM ,DUP$RfwVE|l. ,9d0 Ӑ| ~&m[qSWI%ʆc@Ъ R@Wp#L<XmŀC(&xq"ԚZMRgpjٞ2ʄ̓:s$}91&`r E ```fTuF\Y ̑ We瀨޺KC< \`sJ;c֘8ʇj2GdyU8Sܕg˷l};E{twY{ bdH$̾~"YҢ5?ppBU[d޺@ R-N>0~{gEٿ{v3; vɔBo0e<#@ =bsi$!xN͸(+~ 'S#DlƫQE> M,.M(zD);w^y~ +-VKIe@dFÕr4:Pz6$M8{i\\ ?1W;?*zF#zLam@_Fa2?D^iݻj_e]Т3#h~42Śç/5p=)BerOxNwDv;DV.6E$);5gKXJmѱd9rxx(7 Ι-M82/V3μܥa0T2)\H,7vOatjKy\\@/ ,2H1\n^V iāFmZH1p>kI-YU:FR8#Cpjqr۱8 5ڸM^e)CZƣ.@cW:4H7B~=I=b%>bݺRC5pAd(~eW5FXwұ{FzJmyv"o)@~wPne 6ynl ޚt  j$4r1BT5$e2r㙝vpt o 8*x d ^z| 6QeɈ~lRspde؀ ַ JF(5D6T|2=5ªZ`4٬hEm=;b{%=9gη/#%xy-ljlJF \%w&73lc.Z?!uaVnk孼fb8 8JZlՇ!Nw/kU/d^ k_dE kr:5siNͿu|kִ0E;#>&R@d#Yߢ\ʹx']:E ۺ:բ=;y}v32r'Pؼ.n2{3 XSP1T G2ŕ6p [ ?7;9t7`E5#3>k&X&r$vъ 'qD&#ӏ [!Ǟ-_]BKRaq.vWsSixXE;* 8@$r9V%O% b/+ {ֹ͡p>1 t5G{ }~ S}ܦ<"]+5UC%?2| M2&`ͻl@[3=zat/ij%&Fk'@u%M!`A8Ƴ6Pg!(s1.:H!D>WBv}"Q>)jIpZ=&Cn g<\2Pٌ^Tf#N^9~v|4_\#3]-~ݢPcU"$Y5lP&$/̞^C`G8yN^̟M.h+tq5Hаvۡݽm% gyRIC,uʵ OkN_pĢobS3:3J~y3e+BaN/9)Lf9QoF3JM:嶇@<0Q`S">c [7Ve0 [@&ǫ/]La)D1={+B1t278]Z~G4~,_\`pB+4oEWJCJ$dMLteٿI<^d {ڇLێ˝ҍ.ВTTL`|dm,n;mީe[?^C?9a}ՓXC3Tοg 'OԼz.>ay`$=$G% DLv ]d`=]eV Rr.j=a}rU3>'쇔GQv7n/zwq"B]'Zsm-(g\OѢ7*~Wۯzox55+77m cBbڠsL9J$ֱkkJ5n@gT؂ș 8zk3/Q?v &orXJ^gF#`˲<lVT 4 O~g.q7-?A2چ_ 0iM$9tbS)]/| seVjÀGm<);|p O+3uK,{Ѯaf>wuQG[@HjS=T36)A +s4-OCOl|O{ї X^l_z,ag" rQ[9(<i,0Qn*tOt>sa41yL6qD#0֫}|jGM>yQ3EqJHq;LBaqs7Et0\DR 9ee H5kEwqJ<(6O &Um7ٛզ3h :Ϧ[fexf*ˬf[N;F#-!Ō 2C:R"Fw@";[U^uS5~&&D_^HBDI7KX1rt /,v3`AucDE]P j2)N1iS:z#-ڡyJ̔ vPfi~ ,'/b~`z\N9f#DE?xdk^_p~2&0B5ʯ 1=(qo;й[{c;aS"\ _ <'Έ%SP7Yڏ Rv%L9AOl]wU'Gb2Aaх1Sh>g n ȱՍ座]Avv+7̫TUJh4El}|TvoԶD)a]Lfs+WXˈ)5#>&yf~UaD黓vn\Jļ 42Be06*bSeB9F#{/dT$u}K62X@}pcyn^&iIS_(XPyw} 1tsjlJc.c+T[>]t)r("m|~>XPT^5bo]V˩C ns 1OLN^W"}HBW2 ^xqtP(uKm5B1>1yʰp5`hr9s7optdM2q"vqiAuZ]34}UoVIDBZkӕLLFp}v-mŁ/14T 0rcQ$,QG(zKMV~ &t` )~s "S4{1cLmA9C&yHZTFAqP>K Xd- fD#`ZCg[?zOaMfADs`x}Ķ# #{j\ć|M =ejHӼWHSR=p"5JO;\VE$FI|b1ӒM [+"QV+xe4t*AϲG_JKslS}KeB:lLZңȄr[~zV&juM&Jk8kIwj6a^; %A^=jm0l?5>m[3CÎbh.P SUD )+:; _9E D,UK#bp k{G!'RMIV?3hط+?2DvuE`7U~1(C 1^} +1"(KqQ؄ zɝ0S7BZlL91J }?9"x&:EJ  nڇۣS&ox&zTjeo_ᐨ ~^DU ^OʅeJ n@KDsa۶2>[3&%#:ï-&Lh0i@"Ϸz|Б(U b1pGeHpXqBx8ڦ styYNput)%@kFaw`M*}/[s8'ߢ5KF9<>kDWmD(,#?Cdс;e? )W^A:%:+5XgT=8~z5Zt)f.\Ökm_23}Zmvi< (/a}p+2g$!qTe~`9+" plɨdԌ7"o-+!,XBŝH-x?s:(q-35&vH٦4e;"ur! @UvrLC+aI1%/r|Pg*Iu[#Z8{o)#A?_&A9 +q#lh2+؆֯‹h5g_v;bљɇ#U7;.0z$1IQP$S3!j ^rJq>ԇ"̮ `;9iSTH1!{3VO53NnMFNگMBN1˥9\@ JkwWҗ)KԅCMs͝3b|5t6|CB_YU\7%5B6!ApURIq"`ˡ:uh)R Q5Q% YoD?Y\ý Ex 4ޘLg@x+du ے z͇1`Bg?~sƘ82ъ )+Wq9e3'`ȅ&b.'F$}xE9/wAt ўB2?j樍_g,\&ѰÆn<<~~{%p4NҸ,ӑV&ܞy |bSo K$X7cӨ/f~$+;O#:ol1"^mE"I*7^=]{=5,X&7'4i{]5 YO`g`XFge by$BYB9u:9k{QðMg$ІHj" ^NŮ*H],?m)g3@v 9n68Jj]Tw|[F^8 jܽpXnWP,jz=q#+%I2 )GW_xŘirK\TlE)dgnm*湇[Gh.qݴPh-EldJDäooVx- ,ΔCq +ʸ[{"ZܢH7xKPnqjT JB (P/l;\U$MχeDF/dHD,vOҚlޕL.o DU72{u豛P6_B*Rgl(S+h|T{֝ęG|.w rنWT5GIב\IŸ ԶCHD-W=>.*{1$)x(E7pf]JDz$AJ9rd'z賴s[K~:),\خ*Y+ށU 0;zWBqZĴ+;(-ֲ;# IIz:+?` 8'ϩ̟Ljk~3"=e 9`*ؐ)|(VN.Lv(j҂-EY)!: C\?ٻT7<)JX, G]K^e:Ga9S*)\nq+4B\|W~Q3R!nRoL(/r힤2 @vqA.~In!ϟa 3sTS[kU,`>BYҡ܈k8$a|"ҙn)Zɏ3 i`h_ڪRsk/R| !)f4$<;i7uh[q>}f2WpJ.Nȇ*XqUFq@Sˇ?t$%7,|d.vP Q4<ؔ% 2OpP?|{ĝ֜ X =h21P+g+yLkY|@ YQbAgh5(MD ID-bBV.eH̱wt>snJ^Q5NIԽM? k e M]VC{DwV9[Xlij pM|S4lV¿s,j - sj̍E_T޶sЊ)k<θږVȣH,FYGcF;+0v2WpjB6NCU n8/γA9}Zkn~V!PEVU2AN"!eQƌ|lTEt2y07ýXAf%YU- ǎ s`D"<>Ij !)hR_u QF~zNFĮPaa/ahDyHLjvN.Wr 8f]4|@Tl_Obm;2|,7OÑ \SEBٛ~x =JR!RgS٘}" ĚrL+킧`K9t~18Z:>aO( a9AT8k(]a sqv5V,kLRݒ+fe &.8W (ϖ̵$;i sXs<ēkC4>/ ^tWwnf~McM㚀SӄO+1KQ]Z0پ%0}0gLq\-I92֍ěrAT?2v,yxJ M)̅X,]Md9v-=뎧o}[*{떔4QLi[>DU,)|{ qdac=$ތ塙UikD]# %88K3(Yu9#&rz>[9M_2+l7nܺ Oq V+scWu{eigFSQiv({5Vfu584@V&?A]ma IUA-UJ<͈_f3N}'KG)$ %ᡬQnn F!sr@"jm&{ԅOA!)=J+%0~E0; ɥJc%_IW!L;Ǯõ%Z{c׆ט久('[Qaی'4Gr& ßȁR-B{Ղ˕d,VQ}3j YGtS`(IbHӠ7ġ 7K'5cOIjnjі/\Y+A>R&.yT `J"(ё=]+u5 edtɪ+~MwZwsZ`6f3EHӌ8C<ތil2?$.͘eC9ӣ-;l>@J"] H¤޷ۤSL\Vp %Yᔢâg@kP=ŸouNX#ǧ,o'+u. v:ZɃ4O ZDXCxWjٔOZ@nD*b8a_Y[XN_<̇.M+hTeqMU3pW >JBYǏ#o5dT}smwISҔ4$/ħaQ8/N7)}K$_HFE퐇+97N =ώuVK,Ǫ_kainJiօ@e:5U m $FfM@^sAuJ!4Y8%כP[HVlO$ǡS։^hD~ϝ˭Pt$[e>RE 2y6+ aZņSZjݠn.ojXJI(YX_Vɩ mlLy^8qq&$!ў@X}uZ\v,tq||!oC~[y9 UpGkY,բz:}g{Md4?;5e8Lym׌Ǧ0 ςzXN'3{ΰ߃,)D0L/뺬c9%G:hR{P䭮B+LױYGs* 9vngQP JL |f,56!9 )Ӹϑbmmn3+0[z>0Ns[ 8ʢ=y4:YK0WcrE۱^n@3 $bzătI^9-E-_*gM!jO{}=buwR*$S7//Ղ1?X㭑j]0@ 2mW(3g@XR= 3Cʘ|wCX`w:7 )PKtau(/bԟUcTFBb0[4N$9" v91bw#A7vaK)!1z UaۉueWuU'h)>Mutmkr)! F8i-%|߮b,ǺkX"=O0Hr(6Ɣr3ZWZt*$. w5/i9\2k(Bgk|S_acX˕@Ĥ a"6SO0ݶkiկXg:^@)$L%KaE2 1冷xzI<2Sh ||2@LYM$F YwQ%HT'=м7r-sl‡c734/1N࣐;Ki0[yg0MgsjJ|{IHI#<}zɣ!#j[u;]Y5mdgN-G_[*. 2o|ӨXoD3?wJk o=` ȶPmbyg6@p<47Ż!CnvoH)7&KbLkv Kn#H5 2E6unHmHCT"1٠5GޠoBܥf8d"y{=p(RB; J&YbN\bWWZD'1XIhvTW_=X>{^VU%.$w!S'xlF!Q9Tb\9t;i~oEJM=\Ix3+!CvU!ȁN獀[6ňPO*[4ߊvS-jsx\Z\^}_V7Fǫ^z/B| Cͻ4 y KdĞSM\U9UdTc_ BD;>tPg4 1Pb;Ŷ ƀpj[Sb]=Ax6 /#Y5AΉ3-%+?\ةEHAw C,C˫Y:}QWt%U,iӂ}`OZrتɋr֙OH4px1oM{%#[FL8/?\-o\,]V?JR>{t"A J{| (}4|Qij*CH'$;]ZcfPb[AY &r/r*hz])+Bn!z 1? w6pD֢d 3 c^۵sZgZW `6LUg ?4ɟb? SEj0M\^ 2ښn|K<͇s^ۯNwBN?:6N:ml|~{XQrgDǥZ[B=BӖr? N葱h$;K39Mع:}䎁|n5_;4T\ŠW`Tt5 *GgDjeq@`I,a4 `ʼ6 ֒>ß#md 8 Xhع!uWr[|84XlJѵ 4#hOԿHu c!llb17Ҏ?eܦ`X"W/v2gd/+`Uǯ!'BXw ߿0"dYQ~MK.Ȋ\o33 E]n@AT˱9jh_J3<L5of>骊C @ ,m#mv=e\;JڶL{2M*eϠz,HdzgPA9܇]f{ģ$eèzFIrφpB}+hRT si\szԶ\pwjF yn2΍WW9*؎^hن<}fH)k{֝X/˽O}U!HD[z[">p‘H}J+{/m0^#1S~O nxuFKQa5)iT6 wQfy_Ce1ͻO^>v>DhHQ7 % [l|gt+-E :gִ*z5s&n[c)k.{ K#U~zT }Ux׎^eh h&Yy-h;f[f0YlC}b>{b ,bU@FӍg[T;waEv l~6hb^L`u~^G{=M{l;ٲ/[" Gl&ZLh]M2(e]8íOGx-S2;ek |},T_`q'5zb{wh%hmC,A,[,ܴY wO`H:w[&$Ы:P[-/]Q=v3IA-d)1MpxtXHר\~6Sfa1Әs5%}:rʜ7i8xPT[%jǴIW;  + \D<^WǛ`[Y/p~SX=Ƨ)vD3 /3(M9c;5jW5#e͋T#PgDZ ۹/RcV M#x֜ԔWX.TSfJtO[īƑKͼU{6Í.DJ5v+kN۳H'C^7P.T2\Uhނ@?*F2txbS${(ɉĒ04i.zAW_|8jܾ;hfIiyz&>j穘#aha嘐a0Br~= jPoI0Kڽ~ L|}@_,dup:Vz ac?Zcs gǿ^5t??l=bR 0S( 6 ;X3YT:e^&s=ծC^lI_gy\v͡w_`tMdsB~9^U_/bz(7{s;HMB#|iuD+++ŏ*[7|۹A_!BaYW&mGEdku驑^kUoOՊ^.zŕY< V;J-|/3]ėkc koP,c|϶@7l4 xeɌգ{MÕ2&J% o^x8ێ="XTvC۲QYjfcc(A'"zĻ k ;_|)닰d okerʻP%QDso(_*e'5R;VDELּ ==pDk /ޔJ m`Dn{?YөGM;'EXxDY@M0+5mz̺="Smok2,/rH1*ve4zrxhh~X6=&FܛH_r$* ّk^!|s9YivAH3?U\uhp cPְ{(s9C(( [`- {7b?Rh0YIhnXW{ξxF#م.ի)7+X9t"c`IJ5FENQTTZt fc. 7LxэFo]P}ݜv80j"\|@,XO4<1z2*RԩB˭׎n.$ed-eSWE7pkY0ս "esFW=r=Kv$i~0aªWIڋ6~j YnL=0'\FG*tdlz 6uJ-  ,.ΠRb<!!a3ڋ N޸shɠ+Ǥpb#l&;Fv2&?տuʃz꽗W_wPq/܌ ;E<{> M3ag;)OZқ,/{|6$xVN:&JS㮃A+-z0t/ 3I!{1С$`lFbR'[s[NE 9 wgP!u[Owi$s_j!|p_E47Ǫ%cOK2RUx,5b '_"ONl6p.z^?؛k|gtAD\5/q~^1- q}T B/aGUi*&; _i$d !$kƘ;=+(jo>o\ųٲԾA%Gl-E;%vR k;HL8M*a:=LA?r{hk}hM B i/Sg #+DHa9j'Q.5ǹ)>dz&Ā|&VͶSv~$tEFJaVgKyrީrU&}s'Xp~z*[ 9[3YW51ju$GN$̊BEGcD8glM3>B!(`=U^[OMz d5,3!K@6]2 6׏w?`\>-Yq)*I 7S"1γpF5aXr+?F%{dP6{5͍̄iP+o{ZlHLS av~4m{i?ʒGmU0.u9U(sC-G{%,YWYq_:&اuPI൪0[g;`v[ LKgEl|J%yZgOS#G_cP0gIu]cHhsm ,n( 㒍o W}0ـe^yii?'\$ 4^^n#Ϝj/qp~0W?)ْb[T'b++J͔e8a1zQP=&}i#VonhjA%_ZY01MZWL"6Lzb>'4iF3:lj{rgsC ͑Z=3Mw]Ɂ: gO Y)c3;\F??ڬ[ޘ0voVM*\5d"[rl~NuYU+(pÄp3SFYf, JboӖ; *s?ωuF3b]Mi;ouh lFx<l${ 5rslbШ ɚI;iaM0pPJL\De&h*[p|pGU -V^{؆>D {8yV;i>m m~.Vi{>߫b]Ng`n {ggNb#,dWA GP;"i[-(]ۗA̡ 0F9R8&BVpqW]KFx''B+p|)a<&3t80"4D+z00dAMm.|9Wь s=6J|J,I2< e(dޟV.%2_^S~=,74V'1dGB1mqG„?& ZZD ,zw 9]vRcO/%FZY`\;A1:b׏F~x } |X;W4(C}h.Sy& }FUη_axn%L~$uO ~H :D^Bﰈ1{2֕MYnۿ "E$֔c߈[`~υGt [luŠd-4>zᩅxVy MR$og-A~|hL&5lޡ AF|^g`\EfK݋ #3aȹCHNX̜ 8∳۰B*]0^< G6N˅"Z*EF2@G?n,E 阌b_1XTqS5_  Q]ŸvnB;km_p@J ̾urrK&egh*r܃]qeG>~N=9|2T&i)ZMV?y>xh2GeB&1vPp` 1j{e@Lt b y HY@' ,UɴCϖ܇3-q5(t6BrRi$:yw>D7I1.pclXwsEN> ~/1B>bNsm!H Hlz4M/DdEa2B(A #=s)'(~=+aZ`"˂ MR n,haWm5K>-ʚ̿[o7 }IG~ϜԺQ5JeBu$SΤ@T6Fë]\Ԭ]Alߏ(F(l˱ d̾t~N8-c@$1UtLA1g0AAuDՋIsP67 Tg6;qgfY3 cFͦ~Ptӎ#NK.$b!ytj^\Q!s ϖoy?{j.18ݵpV v 4m۶m۶m۶m۶m;d'I'}V=VwV"}Ki/6(e"k'f@ɪD7؀g700*(ȷ sÚHXs:ł\>*l`JʕH&1E/aFdz-_DM#~ .+?_ukpHd/ps[hwz(1=zt^6wX":Rkpi5 @tC@憃o-&ݏp#3a`C8>ߏ|;uvl r0 F9A|af_ -:ccb2L. .N|!d4T U~Ɖ3׃HhyS{g`5D>4@MKXY۪H9AtGvEs +?"C0z(GEڐg E {X@JS4 pZRW5Q$2# j!Q.NwsysFc=a `Nz4Axf̮S مNJɸΩ0[䞳r5c(⢕)l?B֢ |gaa-,H=P9A /f72&U8[nn/ܱȬpzEkJcMRXXz,cgU|UP1${`ꀴHM)Ob8 $Uj=ͺ)JbǦP~C9_v7f4&.֯eJD^N$#XBǻk-iNنQoވ\N2zW\k{v)(%= Y!2U}eVW749)" 7[[yѳ ~@=H56[2a^eL5x,)]]oQ6.m3HR=?EL ,~Q/cY]h4+9"{S< CTVGy+UfA}I"W^y>%J9/c <)N_He M3~FX{0|;U|BJ aM`0-YtlkSC!>\GR0XCZR'$ֲS^*@ws_JyhsB5F6{=P$/-MrI4?l$Rr| X.U3Fc5wW!PU% u4bBOߩ!d(03q5W[6l[`Rx~E5Mg)C@6v]ɋj̠k=[+ $f Jgsˁ, l&MO!?d6`jkmv,Uk>Uf NmUy7>d)2\Uj@jZO""|l^){gӊ?i\ wxJv<CRI3]nuВ,Sl䶢=HiKȤ1s%.pX5uIlā^*^!{$Fq^%Nq]7 ;7^׳p{aʍaX;0EIHTع?EnӀ ; \4KJRrPO>}._JT? Գ+DV3"%TFʩ~ͧC2ްK0_y\K$ʮh"Ut(Bl]}?x06crd'ch"͹n 'Z4wM V,"v'XD/usNqq5ySv/$%@T _l-ՁBSJe2.h% =ݫ/n:kx4^Ld 4ey4*[[Ag,? IW=BB[7Ϥgg #"m@vέ(=rGD޳ pziu!7|6Z pe3>F2gHY <7kEn7[1=`?˞l<1Kڵp ZDY+<הo1ݵ:88 nWVu,/N B"G8.N-un\ Z2`,gi-Ԗr+wV(C\X ,_ SHØ.S +<-YD$'wr-g}uORWRzoT..iY.ZyFږuZ{3''Ķ_顢 ` Hn4``>#] IKHxl^5,,F'k^;F#0$ybӹT 8qТ.#Vũ2-kǔaÔ1R#r=¡UbM/=T:C:1!>ªڇiaɹ^`IA#A@D5](@ҙW&y߽@n5RENNyV>.Ps%qJf3a\?fAWã0b1TݘSUm)FsP)Z8Gȼ~z>+~r5Zfd6;`u$j >6$ Z dK#3)j`=iOn=βBGb=^䁍a*`>7fg9ɖxp J]u./ɞ;2aXzͤ*OB 0zK U89R;llG&0LEOFJ-4Az5'PSoG]-|(se>9)y\Y9^6>-cV4~5-4A>-r` mgEK z"=qCإPϟYʲ5EGW"_S# a$GS caj+&D ˆso;ٿmILa9D`+_$ i~A-'Q0jЌ!ݩơ? T3Z8a-5CU/KE&x9'3̭DBfDv:L?K[<G|Te0YzV?a^q0NBUBXYk+~<$>YP[/HFk]e%mu7QN5haVYiV3 ̡,eUHCT)R 9TKn[mSsw-C Otu{71IxVi&Ylna  ( 6`Е`VrD2=e92Lo*6G2 lpl5鄅 …YԋZEx%p D36FKiZDzÓ2#??#‹zcLQ gdCo].'.g ބQJ lM DR9)[޸wڅXkJ_cJ8G$(V\/d^I]0_mյXjDaMP!u >yán a;!/|u:sGa@EVU/xxOۚzbWR8TJI!pEFUA֮x=3J+Are_PVKp5__]^hqYDMoIz{ɮG{z3&PQ<CLh,i&"k|UǛgzuuH,8bsڡٓ*hE EЗS_ki`7JGT}Φ'u x508y ԯ.YtMoc+gǬtK<`]CHir'DLB8&:+4xRNkRZ8gq57bKɃlp 1g׷NφC|•4E`=Y$74c1{Z,#"_770z$IY8̆]s8{a v;as"1iwQTtXc!rWc抷A|SR !4dEK>"{B9īj=*YUk5LnwU5rO}I[G!=Ӝ 'T)p E1f8X s7$cpFb=L&7]͚9"X'(QR"S/kʭ>gk.O+~z6r}v۞W3P`FXiH9ąz Ao4՗I.?}@(ǸX4#H.GĻ~ &Rơ]ػsgVJC!j)۩F5}r ɶc@d3PvoO{>Q!˳CPsW1Vf=,xEZ g(i8q*nđe'G5n%v"dLHjP.%o}Ut UJfoQ¦9H@ama>P`fw+ň?y} 'wVΧr?Bu _UJe~5mJ%)?<.I0&Jmt7VPPoTAv*Mk RZ";*@8wv^̪]#e %q&0zz}-??0Li2P0 ŚzF14i:[ɓTꥉ<삍t$emNٳPzHkbY;4u3hX`D6A'< 9_csQ3iH7G&H$SHqA/;§K#&' =]'˚LD.7T"@!+W72?\D}_`W.) N/_FxsY̜ ~h s|FOC,5AL'م!9"ټ9Hҥe^`#T mNw_b+/Ȏeӽ~fWfZ=64qO,Qb-!H[3{9Pػ4Zlfl˯ls%u^ҿ}UY]@w{ Bg¥bSWQ˙a2 *0bK-g]ày%x!CVĭjq<+MH抧 98I4s0Rb4uq2Ѩm[^gt;2Ftþ! ,@ڶJ W &W9@o՞kH6޹Dpqu7RctYj"aNoG+0,w㌕1}ܔF9pV߇дqe,hű{[ap@#+xȿS 9XoZ4zhpai5>pVSLzU&2}%/޼ Bk>;z $4QqM׀j'STEǣDQINS6)*1(HtĀ#&kP$_l߯@8n}GvJuȲ?5jB>HB.:qNJn6Ť2ӄ[D<̆{\Okʣ9c-=ۧLKB ƀo®t"mW;k*ԵHVq^D8KWE)P4>y)&MgKn ąS`c9 2!8 r怢땴=go]VSȐe-0 /W}9[ /Y?mdo@GNya>: vkkYaVL+|ɽ$֩lk>M|"H8u'PԌdu%}+@+ n|ѱIrc BdشJ1\d%R=I$Oi]v뀹ٞTXꎏs"0=2i m qspeGn<ȘJE>Wf )qt,͉2r4m"M܋ՍRl%\ ?<ÈKSQO7$d!8*I|Se8Nk~aR*'Kc@^δǾ|^5-!R!qE!~eUEXJU%->&XC8v|5}q%XQnf^\/_F8d)YuLW} [y*چ҂Q68Oϥ*2]Oc qwJ:=՛JֈK) ⟉;2 ~>a_ݟ>Z5YOSC=|ݬq.lym'L"ݶc9:$?&ܤ{}\:^[F2cev(Ooa = i߸ڦL=x7^X{2\`5k 7QG$4؜*K߳2.`XNpILrJmEcƴc(m@9_s&bݳ}Fx7b%5MJpI%<&^Bsz 55͎b, ƤfYo_ar^s7kqz;w4 Q*p˻,/?z}AHv !0YhBX]!qS$Miم{ ;Lf RItQլ/l܄4vUBU\cJoSWrR) 'ܜY@Xq{[b/îb/ L=zx`IH? 7";J/h?] _}lAyъu}zhoh_2A*Fɼ6N2q}p- j`U-L\T%\.c[Rm%iJ۩hTNo 0lYAkY2(BHnxg!r,廱ꐝi:!oŸҨ}?RzF-\CT*P_W&>;(h>߈dVwhF8kOMT8/X-e~w?TYNϙ>\O xFQ?<qfJglzRTON\^]#Ձ2a%v"d‹ SONlj0vs 0\l4w9컢`Zv|H9GtdGA]0 Alس2I:-Eq)g iO ^^N`\izu}e,tq,35 nN3pxÊ3vȲ_;t3AAR$ "|M9H9nj$l8ܦ¯O7Z )nk<vO r܍?E}9i1 ;VZ%}14=厨jw7MJg@ݪ:4Rk7R*+iS5#K0t I mޓnEb&bAFj3/E^mg߶ ۝uG^=F#m|$=DB~n=6aUUfeJp.QDfLc9b<捷V( :6Pof(ZEqD9Y=wN{<E 7aVѩB%mq"XIS#b?iBǶGY}:qr65gF=K} PH鏖GɁJ?l;tbcX!{ОXf`sjA4\3GJ%nVy} K8:olƐK_,% 3Ժ.Uy͜ 9{83:#YԉCϚ~1)9n\E3:elCPo٪en|a%o3UrU?X;uWABd!p,P_EՄˎ+;gsDP짶ɓ/>{2:Yi_1 _p&˒uwƙhwȍɈz˯=F_E7`5D6,x i%+ƱՋ̼/bVp#h]D@> l9]U|񖡽$Hd_* `"?P J:܍<ç<ۓ '(_YRI0\_6{E` euy̷kvbňAX,lo]j^~Lb^$Aw,{ҢdݣNRs 0g6wrSΤfS]?i`.˱.Z hu=3Uχ.t[=q6\:h5_z7'jn(/ܳӷdK0)!/V3ƠgQL=7#ſ)!r6dj ͏PuF0XмĀFS6Y%A?C7W-\^IM2poI2Tq*Am-Qjo A`p/  HW)F#xQ;j`~aEGWm Ƙ96 $*QCD,RyLpdnyRռ-t<ڭw[HAZm4^[9N90BA}T6:  CAE]K/:gB[pxәbZ?L)%{4f$n\tx=cqmB`Y ThHف<k6jQls [LPLX 濆 (4sӢL\Ճa3D ⩽p\8Q+>]zL _9RRПVV 5έ05 "YL!9?R֑:SlB&csoЖ^jy#\vd7osDiPyGsNU{tQGd|UԛY? A~': eI }֒2%GR=hR*b=N̈́&#kYy#C6KP͝4#D}L6jOr.ѻ硌9&hWMތt ]־0) Bt#CHY :-M}<,1ʍ0m9!a`!͉ЬWҩX_g_& G҃MϾXgQ>t]f`KixD? "_^-RX|э%5Cx R6l&99}- zVȌTKWMLGNMܪ/FmGf1UG.$#2b"[!z\@M .mrZ =K/ٜ QS$\=*]kuwyAmg~эUYZPL_D悟hl˝vjpԳXa'PT;n>-'"^MFJ,y[׶#ݤݫ*8/ i̫v+պHGtjgRF u ocCΒ{TNM|$ʃ;vP(ᣫ)u\=+iB1X T]doe\Xd4GIҟ%l2[!cG '-NIN_`x)/kC[sVfZpZzu[o+DX(ِD ;R+~c4xCm'H,AGdҚ}1˥4a89hEV`P1 ZI,~ yI.x=H9Yc;'~PNHq m]_Ҏ_բ!{"~d cV⌿*a R9X/$%ڧ}3Qe?שY *Qp%tJ+e酣~F̯^YOA^ݲ*ު[Vð!OSLH Q|OvN}5zԊ\F]1g۲TPj㬝pnjGBl !x{}AηlXVzhx :LdBpo dAJ1ʏmw(828ǹэ5MxLOő!֖ķ0`TqO$׭B?m&N7= Io8-*`ISQڇ/[E=6e2}=VN%O9cͨM\ N< `{θdineh.Yz/(Klc/k>(C'. G?ﰤF sK?^& 5/w(Y_jZ9yN"ɷ_O|V`]r?z"kP`]` k0ceo<уǁbܐ !GKgvnh]&7S,4:orqիu$0=O`ߚΥƓK ss"%?[YA{Jh89/j) ߠXg-CΒ| ,ʽxg)`*Pga1#ɚGEGL ~.Jyb 0ػ;HI={Au-Vlh,0!˝0A$/`#HID#am0̀Wո{ke"le}>B&ɷUXa#`Eg+JI6,1`c#td<&T_Ywbe|LDюJJE#U/_0ЄiW?yGŶ*2CkuYN*X6C YOD g鰭 rTi^Y6=r WqFYA8Vc*Ia?yp6& `̄#"#^8k!qk14[l7J͠r@!բ;;^3P\q5JcϪ0zx̪㓂m-D ?\9Օ ʵ~u5gFq9@*ju]\5о;ξ{EC[LJ MDrD?vDbtW5rX!2Dc'ذ_2$c2H(+,H>c>vS`qk\ȴ\Ʀn4,h$ yC/Qḃ4Z][A+-O2yg^ gsZ73[1bV]_a".1ɭ.SLkɽkG2`NG?N06CǽIJ$Xq33Ի*lޏup5}H0 {gEEk8;maѾFɲY!z/ iPO090x N*B ,ׅم->M8N XL1 "xeEAu\j]tj >09EqC“7= :V仮Y<5J&13@nZ獿ytҠmDzy[{Z D=쉿:؅g5?ϗ,0^l1HUq1ZW#ପ JK+| cBg)#VJVɦ>/dE\Vp z}qB_D!1䉢ȡ7$#UZ;b s y_2/*{BϧFaqsQ @ɕ~je/Ânt |{گđV{`8` [5mFLk Cv?s!oO!2wԦ fXtYn+Nt:AL7 ưgL5+dlڇSpkiε\1/J8 @elCh7OluHE,Hݣ;S߫SsOFI[B-I'UKYY*ƢM&sST`YF I`{g M Uh 6mPOW須ȔBy\P祭q..x[ށWDk3|90| ^=VIhֈI/n%kΓFv `Yp֒b)5U6=ʏ뚸T!S_ ph<>TEvlȯBt`Y;2BTtɱ:)"57 cx &,A\x}4hi^&'.##6]wrݖmvpkbp9@ÏnW1匫V=#՚wz)/'BCcX =Ps˒N]< F"F;U_׃T}oRDI-ٖ4cX4pcodrȂ^ Jvb<)eUh,Oek~hS. t{SaL nH.9NImSy4N"+ d^QJ!^k#-28Z[X&v\^lz,OCr 4a[rlɫ=L?敏mXkODPDGr1$79QJTOƧFòqHwj.hrmhn0v6w>Pʾ>/x "4m.ד{!ْ.t-&RZ:FUXƛX^[H(+B>9DaU7oVuUӖR\5Vr{sص70M~ m@Wra.v φ,`'8sL;]eڶ ^qԼ7Vӊ[BsY@QsNfvǦ~2꛼/L_0mq<f>nI)j3*=/ʩB"H 19hTB og&^J6r5E"Jn25Cp1^MIS9|Z8#WH4OL+C}"шSx}<~z/WZWn(ߨ)ͩlLfz \O nPݎ"KMI@NY/R!R~[t,>l`#:-^5v< FsfK ̋Ռ3/2wE *%|t o נ*pA6KM]kS ݶngx R$ 5ei` #>|BqQ(?Ra|k(lFEkRKaVNH~oܫZG{dv,@C@~6nWuPV5A:-UN: $8MЃ_rvXiclb9\P# ߡe%t|km)lpg4t,랑R[H(':v (Px_[lt3HM޹%ۂ&hBnDD&/%v*U Gxf$x+##J^}٥d*oG3.hs;fDF2[b&Y6`-Hᨒ*Jv| ߨFtO lMb6=uQ[z0jO7Xs^cw&bH9v 9yF^hK*uq"Cr13wgݣ LۘarOa)p]csnkpy+gGu'#bg2}([aӝC˚p WW9Bl+ڿ1j/&\p/mߚ5-LNHOp>وbַ5r!IW<wb#önN6xhtDe^_:݌36g̞=T,f 8h<{+`ǑL@q% >BENak] Xe-pMdHϚ ֳIlA~o|:cVHg O~Zސ@3>FU-WȌfaBG2+dw<9/<Jy:,o? 8lM@V3]'iK擜+2׵~EPŵ-HyKȠ{yuBLP=% UbW8aWВ|qKmuT1VJ~8<N?)Pp.ɀvpUIS ysu.o5O '8] kzb2Gwp_T#/HJDMPϫE#_B ;رx.2LϽ^gu?] :lu%!g]I;&l}.m/ YE23 \̶N>&alչ]HO ZVl.&;4 Ä T6e3|>C*WE9_/HLv`˅oh8cUA!vVM!!䟷7; gןQN^'p7<~Sb Z An 4vhwo=BwY/gKr$$=ae}LƳkҹ_r^ nGtwʺPX=kqp nNl nj"%{RDtSšN7(̬q>8me!/V//MUL*pEz" Kװ"SFp QLaފh t Nց_K;wG-qG5P [դoҐ|I+Y]YoR>?➆Ӷrt#ߥ0$5`#y%NwjY֏;~-CP)(yή8AX_$V9L.Yu65OX>u,DR`*rfB/-KņGhBI=;`rL"MR8ۮ. m ^lmFk]]|;Zes9nOF>Ul {P 0œ8M@e35>4Ӟ~a-ۗ^KeؙȂ\(e*3mi*$Ai۲ ݓA1ݳGX:MrL #k-LmjF2lSz^G hs+<yw"Pmp8* ǘO#=s{T8GNYf9tRMZQ]ܻϦ|44 =BIcUdču*`hM[w_)G?]훦>-;"bJyfXW'SYܿ(2b y͈I^')_U}ۿA-}&145M {X8lTYP07 { 5:O&J%ݰw"\Gs2M*bgxy=V|n^-S؞aЗY["Vk1W G=rף>k1Y?֜q&?Yu㋿Oo]ߒMiL'P@l@IZc0ʸbx3T&Af;߃8v <ELdn+g˿B*јȈ`VQXaH"#buN2b =j)AmMV8 !P6 iNytE# ` |ZC@iƂ< T$Z/3d\ίUq4m߈m0ͣrK7^1)m{)\VKQPS <׉El^0yX[!ܶÈQeAq|ƥT58VVxqD 1{I:S[|%-5C0HWUW?8kO w`j|HA&{Pz0Ӛ"ib#..>]гiSr!z#wl~Zv[×D[|q%@UX,4N [$iZS` ‰39!)~DҺ~@!wء݊y{^,Alu|6ӕ\%{RabtV84n#sVrx`+x\E'=v7:-k?W,G㛪x?rGեrqC$$t S4S~וGw}_2 'DA;# ac]igdaMDtm~LEr2.\{1\g [;"2YӴLq\tPݤqC8bL;Mo՛U<,t7S,~QCGz(] G[q˷} 2f!X rR߻B Bߥ<&Hm6yS[AN}I^'UQxTOꒂ2}m7YK-8ш$֏SXt"Y(B^$7@/Bvv^3,!_tyOyم54o(!ԇkyH+QS2pѿ (f(C-UcAlUT|PEplj}kYzN9 `2Mo-5xgt4m]aFT=7#UB>.X\~ݸ!^@myJdyױ:^gb^WOͷ;kғ:WUayQҦ)X̾$GywB,֊nnՊ>$(^ /Jгe׶R6T_wٵS>i=2!k܆pC8niZҝhao}aNCIWϫZ[m@&@> [O"-l[ 1%zᰣZ灋~5”k,Q'`CJvNnkENa{QKg:s7\5<ڞ9Q =jTo.sSzja姕 =O*?Q]]6؍D__m; !5CxLw0{"izL8R~bT!6aFo^vr:xbMP'g?dE}Ricd_OzNaNѴ†(0tT?ibp,x=`.gYW8$jW7Qדre_oRû_67s<ϖ 凉lzI!*k oo){;t1%t7ʰuX"ܑe :_~=x6C^)\qJ КQjӾ m}  h h?pjQ*+(`&J,A&8Y~t`EbYc,BJWPNNJ Vhk{Oγި@͟V>-=]z { װeer[ׄvV[z]O KX Il{U,wJj"Bg~[i2*5 vK}!e :*|#47tq,R \N5JmKL ;ux91R)MG,mq9|-f;FXpo}ɋ6/TA~d*A,vcȦ$gnކ}eHP!|'ϗa%+pμBʨFFH=l,! !k"rY.>#Wa뎠Xtfo K?L0Io G +eČptZ½9aܩ%"iȻ?4xNRLތծSMLGe['ПSr<~Cb4"PzCՀew>E5uvys_ ݰ ;EV!Mkem,(hH&CTR<&rb}{D@:iTMTDc)Wp=cB'7; l];.v^!xE!؃ُߜ1&k-L"G}G|iL rI}nlDN?tPn<5hO!qsƯ3[.haCv P=@8]vi\H}+nwnO< >1 ozUЩxXW7VX%iq|w\RP66"S|]}$ .½f,CT@  Ѵ.,'030I,2|cV,!y:zM(aX& AuhCWC5 Zf/rbWR.c3ڙ}]VeKv auF.Kv;{Эh#/o5Q^8P,7zFbk5@=l͒g $#k˫/bLH4_%N.*6"Ɣs36-#4nڎq(Z"62%"aҷf+z<gJơUǸXBMieܭJˍ=-snQB}%`8R5j%] faEòfE}"y 2Y" ZR'GciM~Q6 H7 ["Mfiڽ:M(O~|]/gt3C^U  tN˩z4>gb Nd#>JXD;Sl+T^RTޤH$O hѱiנQ9f4qA,3Avyغ}hOn m2\(~4{@ zd_@yQ;vq5k~iA肖"N!{RG%,x݅ϣ%2#Ӱ)g.8R!.>+)77 sNrvOR\iL nu;Qˠ|W$ϰ^X-u*U0X,Pna5`Ls>L^fmLN}{`B́͏ Ǚ 404/mU[)k>Hp :4-I8>3OHG+\w~uC\*8bѩş?:}XـM@>Q@;(a|( slJvj'@s={ƀSkNȆ}xߋ(\쳕<&~{U~>_ EǬ(v{1a 34}}eyu$N@\1J+2B$ػ_:9C7*^LNŝA7 U-L@v LoAZDn([]LL4ng|f q0S.7r7G%gp_a鈂Kk'Y ^z2sN!=t_-,xq߿SUC5L ^X&>)S?Ta6+9w5 9[ASnƢ/*o۹~hŔ5g\AAemKwoQ$[nӬVұ|U|AL ;x+8h|TG'Uҡ*qD٠˾h57?(B|NU|*k'h[ɈL(nBcF6"g|'9XzHSN]@0 4&=`%)tZF[Mic[’KMtA= rS_V@͙""{AsI𢪢a:)jIΑLeW{С'˄#Nث,$V:yG(/_/ӳcs\pKI} *BŠ'^K Dt['XH]O~"! y?fVbM9vdpFvS@H:H]-w}Y'f *5Ϯ9iM8zA_OӵA&GH)Vn32A+gK[cZŝ4LC,9ID!/+;rv7H3?1qMdHZi‰ |Ky]ѥ_LyERbahlߒ >u Pg8$FM y*̟ X;fǽ81LoF̪5خzSMV :9=Y-Ȝ[Hn 7am|Un]8F1+޽_DQ;=BJS:wvrvg}w ~+[F VŮg6꿰*%f@v/C3OĥRxT#MȎ@yNPg77rr N9Q9 d5ѿ6T§\jڕ?q"wX%\1Qhd~ ɂ@㐃S~ F}WUcZP=uHαkk̿fBegmFBEW#9TO@Wo\=jAJ2IdWר ǙCUW ,NU:s)0 $1${iPÛ%jғ±'$`cFh huP .mЬ ZgX @ȼ ]*c0%C_HG_݌.ߕ҈źSš22dBY&;J-;y9g03"$J^47,Bd$otn]#(6Rդ5MC f^gYkM^`a=ˠEJ q?崛Y"܆w[QbCA{zt ӉL$NziI|86Qz2̥1x'4ziyN62GU5L1Vc¾ RcgZkSq9.tlnpM~R8A%05Ȭ;Fh*X-Kǧ%=U: ;3ksY#*k % phs&(uzGs:tNL*g,jQJ=xOh>3L =&QbvN2A &C3T~uy]gYڇYWSMIٌ؀i\XpX6MS-T}'9K-eўҩ/01JJk b0'n[uxCyje㳀D/LTHڰOO`qXwnrJ<$Keuu4C>r>z w&;Ga“h9IH96CұЙ{|w_~'Qȝ4<3o`i395eF׍N`ʽ$pc>ѐKɭ`鋺|sԍ6ZDFKC-RP7iT`|,o7|fOtr;%Վ} 5 7JdU0[^Crdu63`LR 8؛!oQ7%U1G%Mj${tDVivF"Nx:H$W]QQU!hǘVHlP#toP7|!LR3w2=b8edD!N\{pY%ͬy1'Dj@.ܫ+X"gn$4;TB,=/Ek<#iv*1T Ejv&i[!*P@FbYbD'-oz)l|I<-\./>pg#U/{ޗ\! ᄡ]d_ȅ<Ŏk%Cp bϩIh M W{!C:3\h(db[shc?bd)X^rt CTTÄ,y FDÈʙ܎o.eOH" $;yk!, SپE*kiA>V'-G9lE|h̉|$J~V7ͦ-#m]H YdT.iKO.vU+%)t:Eh~TcuL_T=K> Xt>P45h!`.-13Z1솉GW9uG4.ޔm!Xk=nor ՘liD8j"kM[~Tə\z1/93 ^0ee|lO1Cvԟ"k5X¦j. mmM@7X>%C9\Hi'M;q!'tb'}t6v\6|W>[B?=](Vo N- -!hyioR\'Gg˝X&v\>r@>eSЈ/w.~a+0T3"5^²8ZS0p {$0z?e^kIuϑ62fF J,Y4w->h ^ Etvo%ZP[ۍu}M^~nswԱS x1UZisn@wӈe,;z3dWdnaא_M_J2@alf&rXPd~.Y".U7 YE* ؜\5v4/RiymԿ73s|QtU! Gf~dg2%m[=&H2Hf{gP=G$N0Am۶m۶m۶m۶mǶ}ߝd;/ު'UT|{aW0h(C0Qܳ!P nU\d)-ZtB[sՕbN Z=O4ҷsڞua7rp#ASl@Ux>/-іޖ&(p$Ry,,4KHߓb>&{.s]=xemM'?f' c!YޗPYLFbӰh{"sRw>B}kBߺ;݊jKY5-^E͜ ۖd ځ˧p^ag3tĈ``B_>W6BdY}:ZIV^Et2G?}[Pll߃6wϡ|t <t$N]XC; -jXW*p#X߫ўrOێ=fml.ֲ[7zWz&'JkY|<(pѤ:s_+Ɣi#uE!;Wd3|I͢]iv #:b>}+ 7-vVȝ$Rַ! 'ֽxˋgWT ~nRaP c vL*)Rm5*_TuD@X4+-\MI߾62gM-A$Vpx>~1g6N*{jȊ ?Q-U#94{y ܳ9{i #wfv:?Q |FdGq *}SNU HGi"Ȥԭxzj4ּzv˿T<BE5'~15 T,R1V=qR3o՞pc< GRʚ,Rjx ԩ " :FzU2}= -.nʰ {yjr"*$, c 5BeapZ!<&i&Y|t< ZnƱy*Hح7ZX9&d' _o}83[} FRvB~SD!c%5/Y5?{UBuOVXiox״D /-[ضT킽"; 䭱p{"֮L>}`6@l@&\Ok:[ҸY^EG;!{sX}6ko헸")9A ?$ D\0d>?Tk3M`MF*K\Hc Zdf2j-+a Yϡ6.&Jo0M@@tÆb(j.WҳR}A0P_W8EU2F׋ئ k~܎;RӸ#{EH_Z;JJ EJ +1vncecėcHPس1OxAzZ]zj`qU>u냢K^fvqe$&OպN|R=5v KL#ZlB$ b* 5 6~^w2#&D^vpeI%DICζc4]aжlTrjo d{T rdŹp73ߒؙUq"|=b ?%E)66".^ܺ գhB ^F;IwuDoߥHqAh7'J0fɐLE1H+̋)¿\Y M{f@עI%z%~ a#r\0-Vt.ۀY2C2n}:.+_",YZ⠅t.u y6Q,=wJjIix0avT}ꎻ{U8n9AӀ5/nn07ZK7"y[n24ޏ@tgS'ɨ%9ՠrEڊήE'e1D g! Zn1OhdXW,Ya886em;g,FF@8YK]N` OtD&'OnF$@bϨ˰ң$=Ia(Rq*ѳS'p ?mWt>#`!hVA<$lC`冂57j܅r}3 R}D-ѣ@93SdL޳YK)b4 )[JQ1s9޺:[% fg JJ-J,~oO0-E3\ 3]pR/q6>(J%y{q TjRlMa۠jDgQP) J}#EMnOmuiۚ6ˋRwgc&<,nm&Wz$BvWh6\Nu]OU6p\>X7*5J4\Đ< twj^cG%Az5LuZ:VDէq:#2Gv z*mJ /=at]ǣXf ؁ƢsRMQz]w٘ +7M)kt[9Tov7.j}(& O楳 j9u*=rkn㮛2 p`EK?-Uэ"fZz=Lu/H@Uρ\AFILUgǭⱚB[4G+S|zd+"牳x6׺Qk ],+(^C nf*(d)3(_szz`X茧b7.3ƽ,{_A2 1)/FyAH 4N] Iu>6Eroz兩t;5} !7c{j?O䇷O#f 9vGʓ d&ƞcd'`2 쭎ɲҔ`J-:] Œ{Rd^L$t( [@ \րa|BvYGa/}o)`4Ac#l~o-lZqרb,*}ӦRTKMq?ŹɗHE 8/0D%70yb*]y)y 5K_|.gn Ca"t"?P c}yU {Gu/1 _xlxe, p1Nx=, OWloc})d (mocK}TZ/j"NS~خN|$a!%$2SϨޯ#Z0vhh%gln<:HԀaF0H-x>, 6rzκA>,2ux`qn/+YޥI-1 UmT}#_+'4qQ#cRXDU(,Yl 90kRE\aowA9n\{I , DCNL`EMiڢxS5 PQQ7Y([6~L/P!t XdJ/[45>S8B]$My^s[;.eFk]΃CFұ% cZU[f)a֡l^(~w/#|<"F sڑKkǘfߕqq ;$ SX#R5BseKZhjE68`Oz"U_/6(D9~Ə,هmnjz^*IdiQmWb%^[I-+IjҢk|QglĨ;,b^ uwS:EV/`AȺ{5e0}87s!B-b]a1(YGK(Op^;Dxja'DAAC '!6p= ś4YKP}Ai wBЩ;=pWRL;r..P$Ң8&g-3t1΃86P }WO{0t7ÑrJ@ &ng>CѨϨ ~H:&CL;# 9xT=B-blTgZܤ&e/&uOṕ) s16"b0w2z 6 *|pp΢Xɕo0SkY`elO#{MXXb&Y6ذة `4CH#6$8vBD.Fk  7Z>~ 7)Q2(&Mb>]=|J-vOLbpڦ !$=(`kcv\5SbO vZVՁgx&0/)YGM}Y^ӌ:s_O/'/l^OQ1Iqa̾^ ]XC^< 8|Vl== #Kǁsts2aPeLy\ tM= P+TnOFrs \cϟC¦~Ӿ;jgcgȅ?ҥl[wuKC>q{|8JecpH\ IJ%_JXX正`v'`S ZXjn}k[`eFOKf?/McGB_R37nzGTGRP]60==I(3)}4G~5=5@tq3#J9%[0ro`Nx!x%j}8vSĬkoFPPQbRTv 6E0H+U?Ya͎j ٰLtw7@Q$yiݴӒ=0 &Xi)zT\Bdme[ޏ 8s Nw:%H,;r|4ILNax2O،^3adGl@[J3A}JP 9aM@gN,9bOq6B0%N$yׂ"̰\tmn=/rՑAFceϕm /5f c2n`LۭtPI=ebnpWq ,GD`qM@5}4rW ![sB{~XGk\0PqG:HP]p 9 \0aNYm11~`hf&qӃtA>2ل*?oD?C$R4捀3Zl0i"ePWfFf æh,ܬmA uu Unsâ9IE_̌[ahH!=QSã"mHl梄d,%өW K -D ~tutaHr(s׊z(L<ʹ}߱R0'ovs<^fשPz'stDAd\TEm-rp1zq!Tk{O>g3wŋ0@PM~@UzouJ uts*Z>w\.TaDt`Mnp53?!﷡GP}U+LQI)w$^^[צ@b6Sa}+:$&#tC7F~xr _3J#tCU˖2Fe3 ( '3^*,圾_C4!̶a8GB5Y/dߌGE8ׯےb@ ~<}[ ӛAUBۊ[-} 7~XLdVC85w1C)U,=S*> ު?Wp(h ={0^u@ZՍ[ 1jd?xCusݞf]tfHcS(!V/3vuHZnt"/g}J'e,]^lÿ\ݷoooDP~.I@Qz+=Z˞i,*2y + [-{˭x O?kq JD0yx <ΔB..yϷ(؀ It)"& 1D.4Mԕ)XwxM*3JMC >@$ j_Z%[z՜K1t'/2~&ՙew#, pkh*M?>!xYV&0],CQߵ)a{ANO|ӡELs.׿#wjVLpc-q`“^k)][jejO vuz»9@| ]eoJS=\iş4zۻK<{{ O|Hysq !wz|.:PhI]d)fV?]u]  㛚%Mwbp:U|TZ~ Kv環Q8S~2䬿PQMez7=K@:$KG pqO9F(~+Еmdrrq J6r[f~g4t%dRLce QֹT} z8j$i6@@܃U/= #ڸSx/.jqeYQzܽܰ0~e$E@fS \L͟â^ci@da.%Wn9SH>/%x[Y•C"Hfgh^Ύ{ha{Y`T?S DoXɥ_sq/<.%eW4lتH:sDy6T|_ k`Q9BIW1ߔx.[h"AvZ9ZcHU0)('(9sHlTY]H3-e6bP~mՊww-Pݞ-EWf:AefuoZgrj:'WU])ewGZfY9l{ތ#5Eqg!lYB}.7 ە殈oϩбjjʟAg*crL*NGVˋXP~K _~wU]Z sl^1Tw\%f*H&TE=̛ YmUsIo && g!z"}{B%ZvԀ@`,ckʷOh_l@B+:bzh}~!#Ė:7jQw-sLFpj|m;{!d.,/Iy慇)maL)LyYq xUD";dpzCSȾz+)7 v P4G,-\<#m˺D[bۯPфV0}Su$Hq]0{vѮrZ$%$y<6/\VeEzWO{յ lo#̑qEN<1yj*w8h  u+Te~cʇa)q|p9S~Ъ? ͦvqa*e\!}aUڍ 4Ӱ\TR/ L ׋ V Հ.ZL |+S^NT O7 Ěr"g''x+E_JapO (x DFY]0 UZQXn̩S9-R#d^?@=]g-Zf3zΝ:T~5LtE%ꑙs4@çvgec#1| /ǰOw0LfWq3CdKYUQ8хZh%֏.:]dz԰Ol,rf\`}!f*Zf)ft66̂iwRC|B il'#|%̎ uuٷ~.R >2nX/D^W^|oXaGe{G9Z"K%MSl{qΞ!R,eٚ#Sj+uqd)xC0S5_"jaĹ7bTvU$JkU"y0{h/tӆ4bҠ({5h͏ϐFUЋWAEObZDYlnS*?ǥЊ"|AҌCt|[IV|z|!3Lg"e@-uExޣ >~S*βɊ^,=0/xl8lB *V!za赕 ?zC,v-cȗD$쮲6ֺ(MD04JPŲ*o)zXCB7ҭٖ!'A›$W_1(v|n׽zc雤ɕAUp1lt?F⸇DVýؾ""-([X)wm58ΔKRSa}pP4юŘXc^&dy[JyP0+S9e"ueW7pw1v{;uq>}gZE 8޻IU`_&[^=Wh ވ]|b P*LuW'I?ğ*vf[#HoOBtB|Bά}E-otbv"w@gSU< @V`wƚyz^BW,׌{locI%!4{~|}&!AMgy<)5Bc)-^ 38m%s68N嘳[gCT u!>wdJR"Dޞ,a=-i`?oefC.q0^;HWBdo9{;(mE~*:h1s[ >)Rфh ХӃJ|!lU5no&rg-ă壐iyEYrR y"3`v,9@GIHC1\8&feg()PU35Y`r'y?A GK>RmO+әIB(As#]G,4YHtc$B GAw$> c\ODN_Mgf#r?v)Pf9q3+n%ˎ}͔Dپbd۱G 2C;ZdVkq[ ܫ jk-HH3G4|hR7Ȳ#]SCʚEF;~2&5k(E’˾*:*Vk? 7uba]d 6eE_(B bDꃟRrۓ;+S9؃mcW*H26%meJypCd[nt6e: ((7 o;yL&]ѵyAdxv@ hJov;Pc/fծ bXsΑ8A @wC>ΖT|FQf&4 (bM_o`4`[*DvFhxCz6Y(=5J40"l~]1⨙eΛnj#M$)dSVdu㞮eM&["*ɎpZIXdqr>/]KK+[XG]'ЗuV#<ù,fN?9>o'!JO ₚ vBmďC&CbXYQ͜}l$ҲAa/Dw my*Vնx'ĻAn YٕdDz^?+zy[8CFl~ܨp-|ҽsM] 63 A&"?z*1$ԩkxVr輪--յٰLmyݫŌ-xc{fk:TH@_ovO ۩y,PYbf "/e7W/nWѦWOuY_rG=ӵ@Uy2bدmhaD$aQ2$\1fy'F+YrߏINJDYkn' 0]k s̔ gpvɄ bߩ9Odݢޖ6:OBz'N횹l: W Oc/߾a,.Gjq;潇h`P3Ɉybz)̏S„LmaPYnJW#L8`C crhN޸Jo Qi=ޭM08wTK#G;ydYB LFe]Sn%Sc7eKcbƥ@%0QhG%=,n(Kxg*KL_ 0V u/-/~3<YтM Ue₿(b6"{= ڡi#Mނ Me|x_Dc«$Ӏم(ǽQPD(U[#8lA dm;j[d?{`ڷ$257{c ChG i:5O|I1eaMҸh$.MDb %n٫ƅָCdc,0{Lv#Lj]2]ֆgt_  np=;:xu!9uMTQjD܊ g,#'ںqw?ɬQ j03|XIZd{ ޑhxwّqdžYJ$"km,NU_lkN.`a)TW-tyS1ⷊNmAB@$%n3b<&z] ;`jAKс*EUAJ be*}2?Ced\8#@gbΤy h2[~RNnzy/p#ssKAJuyzIY=ݧE"}fcOY~[/iJB7.y]Ȕ,ŔAh9Եnon-]eGɆתnZ6g̢ϲ>-=ۧLKB ƀo®t"mW;k*ԵHVq^D8KWE)P4>y)&MgKn ąS`c9 2!8 r怢땴=go]VSȐe-0 /W}9[ /Y?mdo@GNya>: vkkYaVL+|ɽ$֩lk>M|"H8u'PԌdu%}+@+ n|ѱIrc BdشJ1\d%R=I$Oi]v뀹ٞTXꎏs"0=2i m qspeGn<ȘJE>Wf )qt,͉2r4m"M܋ՍRl%\ ?<ÈKSQO7$d!8*I|Se8Nk~aR*'Kc@^δǾ|^5-!R!qE!~eUEXJU%->&XC8v|5}q%XQnf^\/_F8d)YuLW} [y*چ҂Q68Oϥ*2]Oc qwJ:=՛JֈK) ⟉;2 ~>a_ݟ>Z5YOSC=|ݬq.lym'L"ݶc9:$?&ܤ{}\:^[F2cev(Ooa = i߸ڦL=x7^X{2\`5k 7QG$4؜*K߳2.`XNpILrJmEcƴc(m@9_s&bݳ}Fx7b%5MJpI%<&^Bsz 55͎b, ƤfYo_ar^s7kqz;w4 Q*p˻,/?z}AHv !0YhBX]!qS$Miم{ ;Lf RItQլ/l܄4vUBU\cJoSWrR) 'ܜY@Xq{[b/îb/ L=zx`IH? 7";J/h?] _}lAyъu}zhoh_2A*Fɼ6N2q}p- j`U-L\T%\.c[Rm%iJ۩hTNo 0lYAkY2(BHnxg!r,廱ꐝi:!oŸҨ}?RzF-\CT*P_W&>;(h>߈dVwhF8kOMT8/X-e~w?TYNϙ>\O xFQ?<qfJglzRTON\^]#Ձ2a%v"d‹ SONlj0vs 0\l4w9컢`Zv|H9GtdGA]0 Alس2I:-Eq)g iO ^^N`\izu}e,tq,35 nN3pxÊ3vȲ_;t3AAR$ "|M9H9nj$l8ܦ¯O7Z )nk<vO r܍?E}9i1 ;VZ%}14=厨jw7MJg@ݪ:4Rk7R*+iS5#K0t I mޓnEb&bAFj3/E^mg߶ ۝uG^=F#m|$=DB~n=6aUUfeJp.QDfLc9b<捷V( :6Pof(ZEqD9Y=wN{<E 7aVѩB%mq"XIS#b?iBǶGY}:qr65gF=K} PH鏖GɁJ?l;tbcX!{ОXf`sjA4\3GJ%nVy} K8:olƐK_,% 3Ժ.Uy͜ 9{83:#YԉCϚ~1)9n\E3:elCPo٪en|a%o3UrU?X;uWABd!p,P_EՄˎ+;gsDP짶ɓ/>{2:Yi_1 _p&˒uwƙhwȍɈz˯=F_E7`5D6,x i%+ƱՋ̼/bVp#h]D@> l9]U|񖡽$Hd_* `"?P J:܍<ç<ۓ>cSNPPa m 6ʄ6o5a}jiX Ȼ+Oɑ0+/,jżHjY*EɺGm1Lyʐhetai-_plFmO5*n&kw8T=Ƹsxz fi. Du :DF-?Q:m9-+rHTl5¯ø+ INs%"BD@2/&lΞIb{P{AvԸ3g_|!d2jmK%[z4v/s2 xQFB 6?e43BCBq{Koʝ #GU+aŊAB=5~2EAGEuzq4/0OdHW P|> U.@;`">2>m冧7-eIͦn~ \Dc ]R01zg]<)d7ȓdT.1Z@)@#45_A=\A>w >SF Y?Vw)A*j7"N1sm@<q5ITbgY\:My[L}y[ :t)i|s3r`lEu#"sZ2'^t\QϞ5wg?n3EŖ~RK$i2v#1IlUj?UM՜ ʁA:?8.** AQX=i,EfP &@4 S{ p.S!V"|(T-)Akr(?$Xj[a=kDRCr~uϥ#uͧ L܁ߠ-½`G&n T)Ҡ"2&܏ `4 7<^rOku @(%/dK )0tzx?;Cm2vlJê;Ն|YcHaځÞcw Oy]sYI1 Oi"K [ໟE}T|z֝ =z[oMF.FNl0S p;#hF Rfsl8Ԟ\w?Cs)7m_M ѮA}_a4Rz2F׳>A@Bu[xj1Xb `r&B4=N =ϧ"\(z 7^q9*HB[gzU&3 0S!a=,L? ';ޓRgw8N& }v*|R%V}[NޛEb=XmGjIW=Tdq _*KշݙW>W4?u28.ϤAdž&% i8Iĕw3.M嗝 Qj7 GWS '5\{m)Wb*BcPJDr;ʸ 3Ȑi &m?)K!-:d9qCJn9NZ$ߝ3nR^ָ/:/>?z̴3D%{WbPf۳!v7WiQN\233`sYjl5bKiqr8Ƚ\2}^b,7jWY`7y _]" .z5,ags]^@wN#޿!흐_ L EICfE.ƬSUr^I\K<)aOf`s89~STKW Gƍ8_˽:%b͟+ e sUU aCؑ:ӡ4Zj& \;cp'e᩠Y; '39\NߏhsMᏏA~M |COIUoAq ذYv&N@t;!ԙȄ(AȂڕbejP sq2eKmqsKj񘞊#C-)[oyV;s!Je_=ߡρPyUg$-*

^n m,EO_%͓Fi#w5pXìM1I#AT|`hjI[~%LF=nQ{ނq;l[TZ_q#_b0{Jcme.]{k)~eCJɟ,/r 2FQ[y8qrj]_)Q`^ 6֌}&PٿO]yaIKz,g ~?Py-Mk^-P{nMb;մs Eo'pLD>IA 3+`ǔʖxwŸ!9B}2к*>9y?Lm+ohwEUY itޠW}5H`{5+K',%DJT%~$mSFЊq0s@_ 0NAS>A1Ζ[l%Y,{RST8*b!-GlA75!܏X#A+96]V)a2Baw/Nw2Mzhr[WY7aԓ1B*;aeۃH^/GGb9`,KCҹq1ʶ%E}L1o˽7;jG%<*Vm(Xcxlx^T=R6uMܻؑ8|Ft;FJx(-VM pʲ "0Fs9*_ &`f Ӯ~mUs[edYt'F/.ŝ=xU(,2x^(Qnt>?:j *'RlwKe&9gt抹[+u-a9i"d/ W>#r7/s[9(fY e{\{9rȰ\?Ul퓯#Da"T;cwߣFN=(yyW31:t.|Im@&P*D:a[3m[Iǩ,mz>Z0pT8~/~#f;dmM&@ :9 G6DFRpBFbh> /.DoBA%C,E'kwv}g,k|)#*U'`vMU'۶[~~s+1*k)tksnB]pvr!?}IeB64=V_{/X񻧍ß@a_ar`"fݷ1Tع]h5~Yla S%Z|Xq.xbDb<Kٻ}~3a%L]M:XhҼL" 5O*]FG$ qm ̡-!:ִ <r+ ݄b?WzxG25߁R"_O,Vn#{%licyץZ >D99w,69O)#Zȳ-ic;+ƪ5inb9\nO嚑^&yR:!Yʮט8%u\'z˜ݐ\rh9nhEV\ɼ;.CL֔GZdpjIM*>'rI31LY83?^i/ öJۗ-W{.0<9S F8\MgbIFos*lOe㶑^]`m|.}}@_ E0ii\'BT%]>"ZHM t%F#7b3PV}rœ"1Iwo0 O}-#c;ouj挱ko$?`0| ڀ@OYGg8\Nc YORpęvJum!yKIoBpyh{)5N酚3=܀d0!q2&v?TLR(b;ʢD6QIpsZ_vL[ iHf~qą ?ٶ-8穫De1V hL^A~+8&^yb@}!yjMWwA_eK38lOxueBo IA澜 B`"h FY0o03wLѺj{#.U`,RE+2s@Toݏޥ![ʆQY9%kupZCO #<ߪSX)~ROJ[>i"=={2 $olf_Zniњ88!큪Og2o]AVOE{Gb'lGKp}=³= ͆MdJ!7y_2 saxl1ֹ| 4S\F(v߉dD? u9M82l[NQ%#T `*QZaUo y-0l{4뢶`ԞnB 悽L3Csr<ЖTtQpDBb6%ag.ώG 1 e-DWR0+J5V^3?1vOF he %Qi;5* 2r2"V9Gcq_Lȹ4{'_:5kZܑVZs)C} ŬoQxk.q\qC<ēxZ"FmFljP˼u_kg(l^h7={Y(px*OoV#HJb8|-ޅd ^"Zȑ5g\9؂hŅu"ߑdžc!fFc}@ 9Z! Tèe~W2y8asS_x*DuY-Za/qٌtxf NҖ'9W~e(ǯk%Dk[󖲑A긅(G&{Jͯp®%08K;Ѓ+94ɩF>n_WF&vc]6e{[0RY~tي͵K CκwB L0 N]_YfegmaV}L$"ës !>(F$8MY!a7]L3wi~A. lF/f|*3Ç TH'?;s^/. ?np(1ǪDhCBC6V?oov(Dfh?! m'F/Onxn:$hX;z<_P)Τ!:IJIz5/8b7B^gQ s_2u{X&Kݜب7@ DJM}#nK覊CKrCoQY }qBl1_^^ě 2T-D PUaE0uT:Ec.pu#? Ow/.ZV0k~IL!%oyV&&ߤ|n/2= CmGNFKahI*k* &0>GK7ȝ6ԲwNZZ͡RПQ y]sq؃Ir!\*߳ꆓmj^=<0t}x#҆{GjB;J22+Ul9XC0XxG*޿Cʣ_][ MhOJ96C3hmW7VJbOFxLuJCk`1NtjrmԹV%|@X5573*lT^Zdї tzvr,ry+D0nUwph]9,%/3]@x{}ub ?SeCF 6b*XЧNZyKp8\rv skm/AUI&~:ᩔO\9\2ua6͔>8'zs|ۺ% `c\Y یh03(ϣ-v $rܞ| |wa9q'!ˎ'fkp}h=AkAZ,//ʰ3u-QSIUg4TI(`e'BIcgi9t<&F8[ A>>u5e#ئ@(_@"Wyp%$8E&o08NqlU1":G.{"xyqrN̻w%Mhh'{NƪʃyMjgӭtx *l7M}Zj[w"EĔZ? ̰NVQ+eNR^30"I;ZlLB%b^ik`b !2i p ͩ #an=@2* kuԟLK޻aEd\ <1T$' >Vz )쑅;HZб=à/2:ELc"{r5G]9#2}/f3%c~=9=PyM~U޺%́O^O,>1r<\}7/~$/`qf?M >v(q@y4^XVHϖ(eU1[1rӭ,h7" ERMG(몝ueĔX{R΃Upo#Cl Ҳ o=F> A(ֹ#,y9x-q,HI^3gȸ<_iھ`GS9n*bR46Sedy*[(Vqx8ټ#7a<=C#m??*酩tL:9# K10?kn%pcp :t&iӷKT[j1*&`9,z~pПLJ$HWMB!=I`5E2G\]}gӦBF;R)`aj/J-%n<(9X0i ,X?IӴ :A>˅gr=BRZu+d0 B$H+ 5mC ,X"FBl+œ 7K 8 f]qy#ٴ}IX\/~9m'Ah@,B=9Y VcvMP˱p}hFt.V:VN{6oiu >[T ,~*X7U~h.YK!H^9Hڧh`'+lvfe$K mi/N P @Xߗod* B9ȱ(#&h+wՅ`:0~KgyL9)ۃl 1 !$-NM 8\J%e,toZpf3I0M3m&yEzP 90HJo_bou쑽R5fY.C 5k$i+JQC)H8Vd!JUQP$,[8uL٪ԏAr2ekZj +h߁%Œp٩zFoN]GT(<_}\E&~qCxwj2@8.ё+|v:r+}cudDo!4 owz~['u"MS>}iI&Xƭ(H}HvQ2^:gY#asml9Vˁͥk!m6F}p -{dB{ GC-?=+^ӇIq:&@Ӈ5͵;5Tð@h WLuۀL|EZBGٶҭbŃ wKaGQ1j)תYN`/׊"Ϊ%hur1okx=s{Nd\c č$O+4{ەTXsm݉*~w!CjJ`Evop8ŨBlŒ߀Uu)tĈzS|-NvsȊ BoȄ>BџnKHoa*SE#2$@8,cu8!<zmbm9,'R:5;0զ}9o%#5UxV+Q6L"X׃FM摟!p؝IJX+ZQ ƕlou3*Sg?QG?|[z |3dTaصد >r4֗>^ 38YH 0DD8dsU2jnCuT~!FhnNYr\k8ږ̙pK_w~;Ӄ}sblS2GY:9r*[F;9w$m>}_3v$T:YzE-ґM-I ׃ 7בBNҟ/Y ZKVyVٕQ؍z6BYXC4lCpxE4k嚳\}FǯL;AÑߪeJ=`$@((VҩC{/9sBøSKnEw fWti)*^]R}'|&#_w'wOTW&H?Rmyrh.ETյ;Ϋ%}j¡91a>w^!/CHYHQА L8*8 yL0P:4caE)uӨaǬ7S,^{"NoL  &3 wٺw]mCC9IcLZ`hEӲ0B1a#?<" ;xkhO!qsƯ3[.haCv P=@8]vi\H}+nwnO< >1 ozUЩxXW7VX%iq|w\RQNMm۶m۶m۶m۶m׶}m}T:}O#U.?lWw)H Amz^OM3 !I h^jEE$mcXy^1hkPbaNڦ^0,aY :!R-p}凂SheJg)ROy`@g>Ю]ByA%;hۺ NRZ{l%;UG=_nVn𷂚(w/k(U#15FOf3wL@ʑ@^1&dDܯi'wc9Y[yKA7m8-Z~K0[Cua 3%Ъc\FJ2p_ƞ9(RM!>a_[UBJhyd3p 0WxU"IaY~> ,݆nE)ꓣ1&(wSƛr-s&3L^Gz&'vM.ŗ3@ԙh/۪:T= 31^u'2qEK,")\U h`Q*/)kou$W4X޴kP3`˸ if'iMH.M?==2e_oZ `Mw y?R;ťr-)}^E=o<l!tXBwoB6yn<4,̚m\+`d-7|(ѩAEUO^Lk9qgD r'Q8$AGqκ5I,m-@ea 8J֊w`*lUs{#̎^+)PqPB~.V$1-f Jl%xnR A2Xñs*'S|_ zO;kCߠ$'6d+ ʼՀӨ Ӏ;ʸ5 |tAK'wVJA= O` zW@iXΔʳ [) k_ԌE9?nK\')4&n(AePK_udgX|/,nj\pºZ~{*Pt(70I0&}9t&OH/r&'W\Zfg[GVC{z񗶪ꋔ5BqH`  GZMu$pk'yһ?Nȇ*XqUFq@Sˇ?t$%7,|d.vP Q4<ؔ% 2OpP?|{ĝ֜ X =h21P+g+yLkY|@ YQbAgh5(MD ID-bBV.eH̱wt>snJ^Q5NIԽM? k e M]VC{DwV9[Xlij pM|S4lV¿s,j - sj̍E_T޶sЊ)k<θږVȣH,FYGcF;+0v2WpjB6NCU n8/γA9}Zkn~V!PEVU2AN"!eQƌ|lTEt2y07ýXAf%YU- ǎ s`D"<>Ij !)hR_u QF~zNFĮPaa/ahDyHLjvN.Wr 8f]4|@Tl_Obm;2|,7OÑ \SEBٛ~x =JR!RgS٘}" ĚrL+킧`K9t~18Z:>aO( a9AT8k(]a sqv5V,kLRݒ+fe &.8W (ϖ̵$;i sXs<ēkC4>/ ^tWwnf~McM㚀SӄO+1KQ]Z0پ%0}0gLq\-I92֍ěrAT?2v,yxJ M)̅X,]Md9v-=뎧o}[*{떔4QLi[>DU,)|{ qdac=$ތ塙UikD]# %88K3(Yu9#&rz>[9M_2+l7nܺ Oq V+scWu{eigFSQiv({5Vfu584@V&?A]ma IUA-UJ<͈_f3N}'KG)$ %ᡬQnn F!sr@"jm&{ԅOA!)=J+%0~E0; ɥJc%_IW!L;Ǯõ%Z{c׆ט久('[Qaی'4Gr& ßȁR-B{Ղ˕d,VQ}3j YGtS`(IbHӠ7ġ 7K'5cOIjnjі/\Y+A>R&.yT `J"(ё=]+u5 edtɪ+~MwZwsZ`6f3EHӌ8C<ތil2?$.͘eC9ӣ-;l>@J"] H¤޷ۤSL\Vp %Yᔢâg@kP=ŸouNX#ǧ,o'+u. v:ZɃ4O ZDXCxWjٔOZ@nD*b8a_Y[XN_<̇.M+hTeqMU3pW >JBYǏ#o5dT}smwISҔ4$/ħaQ8/N7)}K$_HFE퐇+97N =ώuVK,Ǫ_kainJiօ@e:5U m $FfM@^sAuJ!4Y8%כP[HVlO$ǡS։^hD~ϝ˭Pt$[e>RE 2y6+ aZņSZjݠn.ojXJI(YX_Vɩ mlLy^8qq&$!ў@X}uZ\v,tq||!oC~[y9 UpGkY,բz:}g{Md4?;5e8Lym׌Ǧ0 ςzXN'3{ΰ߃,)D0L/뺬c9%G:hR{P䭮B+LױYGs* 9vngQP JL |f,56!9 )Ӹϑbmmn3+0[z>0Ns[ 8ʢ=y4:YK0WcrE۱^n@3 $bzătI^9-E-_*gM!jO{}=buwR*$S7//Ղ1?X㭑j]0@ 2mW(3g@XR= 3Cʘ|wCX`w:7 )PKtau(/bԟUcTFBb0[4N$9" v91bw#A7vaK)!1z UaۉueWuU'h)>Mutmkr)! F8i-%|߮b,ǺkX"=O0Hr(6Ɣr3ZWZt*$. w5/i9\2k(Bgk|S_acX˕@Ĥ a"6SO0ݶkiկXg:^@)$L%KaE2 1冷xzI<2Sh ||2@LYM$F YwQ%HT'=м7r-sl‡c734/1N࣐;Ki0[yg0MgsjJ|{IHI#<}zɣ!#j[u;]Y5mdgN-G_[*. 2o|ӨXoD3?wJk o=` ȶPmbyg6@p<47Ż!CnvoH)7&KbLkv Kn#H5 2E6unH"QE8ƴ_,F6f{ q`rḓH-á(#K% QwK.*lf͋9!Pr=^=_iq6?s[{`%Q]z` yYU,\C܅LEIDm Sqdl-rWEv(5Ls%ͬ UA憀":՟7v+o#B=l|+Mqȶf-Kq hryi;[9`Dwx?T UX' 6"ӰD.|A,v,Xl.M{NM7}pUeT @]dkRfH#%A$wGC!ۚ3F%mM)uڤ&t/`'d#1:'FTδXv|s9(|BcY y3\ -fhE]-TXkM |=i9a&/rFE[gN<#v QӴu<żl6핌l1mBO |_8"sOrI\zJvZ \(ID.Bp,c-*Y(tF2Pj= nkR"-p+0 J}ŋIX H;s۞F,e?t`\}˜!l`@"u#Wpwbul"|pTagEc5s7.;w"+s׿r̤D49w).Ra/ A!J+ohwW0ռ{⦫*fp87#;DOq(i24AA2{߃>8"ώApv% G:%= ίJFP5ϥq NRrnߩI'q87^])t`;z9eA#}{0Ywvcy,gH8<T"mIm` G"=΢+qH￴zHN[=/&i=+/OPp؃GY֤{cQq.:FRȚ}^4n$V:? {`nyJ 2w!uG(ԗv*nӭ(|uZeZ̙ mApX| WFz6C',OVQ>LQ)U_;^z?o#Dߧ.d\N'+-nq: g=xosxxʗA7}W!O7AoQ߅1٠¯ zծ:2z)4}cfl-|uh1}w57hru΃ ?M1gbL윖1 WSҷ/c! ̙CG{ e`Ia\-~L|ʞ05OTu{aF Şu:Ec|ݮ`OT1B!;Q1Jߔ3SfyU8RfQڼH52)u+^xMd5/?+/?>f5~D}8g}a͉_LMi}B5?K(hTLUOj[g3-BAQTc'=}2euB%o)^սF_-H 2l$C{|!:EȩJ, k>KØBPYخ'tŇֺi+.ɱxvj4&7oq9vV  ($G[,B VCԟ:Qاh $E"KV^cUf<1@hZ? v^k5-QC7sK#-Uq`3ykl3ް:1Os Pe0S9NŖ4u}Q0kk}>ne*}'OwD6:ϣywxb:c^ЩӢO>[!x%3ۥFh'eў)\nm>y9C ?=?B.㥝йcJ|z#/)9 }ALoX~}ظB􄖥oLc+<*ZJB|sM> ?L;#AUfdL{5#NQ,"rC`vQ J"bʯo7F"BV>,aB.$=е(c1BR ^@1AC؈1LDg6`56`{Ь1?Lt*|}"r_N b8ŗbKV08h!]& u]BuM='16~bŝҩZv@j-L{]#Ui[NDP4 }a ۃ7 M0zM)nrAޖ Ff#;5zI#q2_smo5(xQ"nⰳGjY Q.kYHægV36KGVO}f} H/5^L71؝F?˰sMY Qp1rx4NV?uCS5=XxS.z+3~*ӫ︵+?32,(IORbT~T cO2$EgH-թsy-I w`͍(a!3wah`LTQKo(P'>qi,ipc xB֪R5s\NhBnVɢ7~-قRehA/KysKQ6?LW,-Kqe#hImryUZpxC@d6QYGEqd ùRHQӦǬۓz[ ;eFj&"bhYػ O'e[xcbĽU(0I7ef4S5\m0 k <#01$6]ݲڠ׺5tX$_pwQ/ u[EJ4|Fr1MH+_8[ [b\5X$KpzJ_?)`-#URS\O.fqpz%dtf>". ̼n:gEi& g<~Jia^Je^CM'ߙP@Hh/,ݷH"fzt^>l¹ÞvKLBG=/^"Kf#^n( 6SU<-)@d_ YJ@n~[q&_-C_m*VӔiAp#I@{H 3*gݴ@ə+$|>25`yF:L|0R OBFxzp;zak"X3xJwinRA 7Oƪٶ|/ԕߓDȨ?@),"*,b6{)"O_;uؠyB7XʤO`NKNOO"qSe|!k& 0&ƴBmQD)ؚYQ(R|x(,-iG_:,Gki|TOfzby73zȦKPp, ݧ7+.@>`U6yH?̿]u%4Ǩp ffP *b-xC i)`Js!̮o&Cm/mGY蹭2. #E^6usHqx *+9KN3IqVu&bDle\i}5L'8FB]; =u@}bI%/3~s=aGFO)1~%I&gWU7EABf1xkjCg%$H400 -(QWpD@KHu]/n!'ˎ[{尲H? ~7(FT_9sf"ɷ"]طKJ8@DQ5|.%QU^ôq- LBt_Q/f B2@'L\f?~}Mgւ8Jdٌz9։*S|w$7V,\<ؒAwdž1*-Lx0DVPO6h?d;yw>kb9xS\scLʸ8?pUt)`|,{Sb HYT̥ -4"}^]wx'ddpIĉH\lpV/ߗy]G??A6y5GEsJ=/¤`Aܨ+l1/ӭ$pI i5Ag>36bK1F}`Bߺһ"+m VdݽĚ2v B>q OA!~˖m%'8Y"<JTA" !“AIʞM%>C؏Irq򠂴;t!ȝ8 Lx{Qacd&9b(i}sSAqvVU־ ƫ'=qxPDKzH3hTgԍ$Q!˝}+؞PQJ9b(v2^p<.FSN*Dg7ΧH#F99n nȉ1!aE?FHXi`N3R6-W:yءT=VspThԲ18sUDqu.$ׯg%Z ,@dQtY0;ޓAB č-,j>ѵ-F#pɧ%ZY~#t/)ӛZ7#FL.|$}ʙ H}x#˚랚U8șMsŒ-x9V7p0 e<p d< AR;~W)b57 F#(z1)r*"$ܕ@fG5NBl،?k& s|L(ٴntiE`YvC,4_ Q؋K=*d.!6cٲ-G|O 9v$؝kU}A|Rb&0 %m m&3R'N'W !re'IkA~Khfؿz.Q6Yp9Q~_2ʶ{]31 70Vj:Z]$JL217]8#m"qT>hv+-[ !mI5L.FXP(θ#N]a$k(ۮ8LngQDNP._YW,H_ؘL043KASr wlBq" !)ogF@g-6f4G+3M#3AaR4nֶ R{Nx*Q]aä/fF-04bH)Q6$6sQb2Ї+%܃E"}U::0$n9vMɹLkZzc&\uEܾ@xXOX^;MF9Y/T@vr9:"ŠR2s"l\x=he ۏE='m3AX (E&? *R:TN%mD{: I-^;b *fcw":&RxJW#gHӤZH;VRΈ/srkShw10ɾUNUC!XYSY?<~F9feK2Ù/] rN_ef0#,rr 2oF\#Xy`xmIjzr`z?^-BtϠCmŭΖ>C w,&2!^皻Ҙ}ӡ*)XYo՟+8L4I=ka: -FSӭ~5{BIUnOGt.q :@)ߐzytp~ݍI;tY-Rg:>2.Z }ZS_~T.۷77"(?Ӥ^=ڞ]rJ-Je4H@L|_@F MN=V^ll<}}bҵa VLWYPO9_Th5eV+&PmTI/씮-2'xE:d ׮7G= Bͮ 'zEKK%\>[l$ļ98T;~R= >i[(.2O3R^G}hMM撦y;my1\\ QN>n*DWkLv@sIըh)wtIxr_J KI𨦲J=ps H`L S#Mf?ݕ{6ySv299mf{_`eh3uOu}3R:2)2{|(\`>Gm=m5VM]4qk Ʌkxmb)AS\z N8(\=^nXrcce?̲?vQ"R dj3)v.ae/4 0Mҫj)Ot {`-,g!ՌHDD$qv4A/Ug=,rx) 7ү8pkC+o{lU$9"<izWsOc/6wا#Y>IڱHs[!!j5M,iSU! Q~Kөkg\\MG딩f F P?7[dKCu௪=`2q{'-ZgzBO*N 0*> ``MVln'/oM3ٙw~?~[ ЁEs+4J(6 l^Zwq0_hVC \ٌ᳑n(R`Ai"Zh{z"1ϲ5[&۞<+E!k4> >JC2,6Iމ0L~z0(_$Z竍oJDφy`cا;&ͫ8v{,*Ň(BcGvˮK =j's6De3i c0Ӿ mgN-@co:f;Q)!>r4`r6SѓlfGM:BI:Qgt }Azn@Ju^,WVF O؃fU,AM0MУ}=-XBYi%kg)8gOv)ghlM㑩xHB:sƔv|f/0\1N*;@omw[5SX*<=o4D: iC_g1kiPI̽4cgHwjq+'Gk1UV-"NXx,67Ʃ{lhE i!^L:m$s+Q>c=3]yDzSϺ"Dp)gdE/LOy1C(ϏfG&ۿ{ց׺zԇ$O^ͭy[$7['!b:a!p!g>7:1ny;pI>$QbM{AVQDLψdAq8i[l*ɱY7oRA[vr:TN?7nv!֚!*$ʢd. WEAW[u-<?$fbXEhȿOpC؎hEy;6_]Μ+Q%w@BEUgr S漷`fe;⅒9­tҢ@|Q{>^OLJ`٤W;\aWץZ\el?UAT(=1-| "3oyr}lv:=1jqC[v["W7V( x* )00}Qi7/i!cQm+ԁGITY9oSZc[v^ў I*P,Zd$K<2_&^`]R}q' *!vhod ,~AB?vǬDu%oTcĹ<$,X_ z'v}cl*i +;c< {^=/H!K][kYh=1$XR܉=?>Π3< g/FezMضs`9'rS!*:?;p% )mX"oO鍇0X4 ̟7IRֿg2``8^B/C+!Nq|7e`❇s6"X-DEhBuYQA^l6GAJVZ7F ]yUy_9FAQH4缢 {9U \4}\qdɑe"[mH?5"ae_Cҵلz:i.R2FXtsز/XJ1O^)9B_ɝ)϶P]{R$p_|`I6z< !xcb2-IR2퍆UlUu &cp.ڂ T#L(3y LC޷Q0aj iVpu-lzi"`#4]m! =FYS,RX t %gM 6OCW.BcqL2RcƑ R\ NjHa+o ≿qOɲ&-w dG-PJդq ,8W9jB.إ%KJ#KxW\@3'ZCѓ%'qAM;|!!1~} svaHjwfξH6oti٠0";<+vBj[ X7 ctٕ@m=^{9&.ͅֆ? l=@EȘ5gw_xp<+KwNF@lX6qbƖ `<Ͻ_j5Tk*i$w{u 7U;ק]Լc v1g O j2+ݫh`+ ˆç/CZLD| *m p^160"( rsɘ} Uּʓ` ,ǤcVrx jǬSpF~ z.59fJkF\`8G;dQBk1Qe ݧ hnQoKL'!v6\`EI{'ⱗo_0xr#Cs4DЙdㇼpi1ؔUrGؿ)aB RY60(@u0^Ȑq!ZJiq.N MmL4j$'mۖ7g2mrUN[8Mw.} \\捇tTg(i~;?& '<8csL,7F?x{&k194i'o\%{74Zql֦Da;?BVŁV"v2) u$G n{aMӸ^ {Fɋ7/Z΄kI#a+D`Tm5楰7U%p(QT{v딍#h JL *: @ΫYN H=brhF5KEˆJ>a01Z>O@nYk[w Rdnma'SC{fMM<  mMl,L L-:zSg:7{FVjF6r56jec`ecb`dbff`aggfd``dbga `xK::8ۻO?>i^^0Ń1|xC}x9- ; ,lzRoyi ʘNՙ~Oଟ#S\vvdM=O&Ov"2mʊuECvdJ[cn6SS )AOѓ!]Ȋl*E /fic*bl?aд&oAdžb>ڎv`ORk/1Ti_0EP[};@.Li H+xCF? bKuP? RB m0zKׂEr8Vo3~o!t$MN_RLY;P_ѣ~;qRI\&J$J4W1 qX `0O7Q#]'G:'q) $պ0e *! HKo?@&l&{bJˎx1}֛Z}&%R4YehD7;4J"E9%y)\-nHIrCڿqUnqc3 *‚;=UOǀZi}V?kZB9+rB̋Bʪ j>.J[}Mӱpbk_K<6FCR̼f^ɿ<<q3S8-yMP/4*NIT wm6qKUdŸ#tzȫ71$`_߻=*mR0D?wej}p{þ?}fk2hm*&zY+.\&=:?2{O/E mջVst\I~MI:>uBƽd $Pҟz5@U{ҖqM*Ny<!`dv0=irѠWeal+^$m-EHru'O86qTG-2#|U )MIH_A}6{nȱdxk7n`Hh9U"r3gy^j+}SzلhVPt7#u~m9#|oD0v'^%A~Br;"7!!оdTy lcd$ZV՚[J(]8ƶJҔ0?Sxޢս^a*(֔d:Q( BYwc!;t|C?Q\*܁D'[d=IE׹QT8ġ~L}vP|7ɬ]Ѷ`qm 4.-%pV_L[:8U˩ 3}:%0ʍ-~x͔D .Gz+ee„=JV EɄ+.:1$k#+@{aG@2`8K]_,irwE7r`/Ȏ` z=شg'd?+dtjM[S8g҂G34X9L-Y}-gx% #j֭=Oigt 2%$r~bvZ G'k;a#KchzjQ*oUuDiK tnb'r3U2gW4Ӧjn9!FJ;@/a+IvOڼ';)$5hL*f6"*_;^%% MϾmMM;#f r:{QFG,Hzʅ$m{bmd߫2R%˺)8|]6zPsx v5Ko+ 9PXAttm (P h㈪sz Nx`3rZufoì"SJD. |G~҄Ǐm~;umj~ zZj-'̓vs>Tw6( 4>B5Ρ=Ԃ`O{&ig3ԣvϵ/XdVb_$ PGElSu͈Zμ;PVx d`&jZ)Qu׮؁^Ivï @H8dFM6VdwMfNʞ3ukSu8ʽICBT3 s'f,$Њ,v~P1;E֣B!U6{-)n7ˍYqCT@(z9!B`:%d WR)Q4h93 9wqWz=}6>g^!K wꮂ6yVr˱L`*W!=&&-X񃯶Q[Pa3 G JOUy;FH0s`y_ZlQlHQqd&(GoNeLvOae_ n.}ЦR]JWM(Rx:`@=VJOࡢ8`|v]D' Fa |K6 Pn+Jâ N_$B:" *9PŵĈ=`#BSc/}R #.kPr沋s) X9S&>lLHQaQ9zyj/557 \)fbb[et:[=Z}BN#Xׅ1/ۓ sW3'v&̉Oma'm_DO}&dtҾt3cL$Z%3Ѫ._{jcϹ5znPmX^C[AҶ'KW(Vcy_|-c;ŬrFкL|-r+-C{71#VIҟbPU,Av5)4=hE~LϡuyOy'MEV8AAʒJBu!*1c.`'T}Q<˗tu&5rH+s]-Xv!HZ@Ĩ!|z>tWD5KXȷ A{'mӿ|hHd8QsCy 垝 ' ȸX!M!~91>b/M _!4V\gh~ڬ~60͇%4ʝ!. Tlm%h|Jj9H~#OS hkrV{ Ӏ;|QfpE*O11dZP;Ώ (= nK85̩$Q%eqc#s7͓m1Sn3|B l:lӥp؂9tʁ ctYIp`"-jZʜxqE}/?{``=8҆{(śT[aJ&/ߓ ؍4'yuۧWusē(@AkbREGM-Ƨl!_T5Us2d+ꤏf⸄bZ05Gaße ?!BM&рO68¹LZ颀S`Jqb5fqn99gbJ yy=!6b+2sK~<R#{%#)0S$J;B֞pڣ˘r?l&[E6pgxe=1\-HR㶖/q/\<FA3zT#i|[رM+q Te}!>ak{1^6c;,2C[J+ /lre:烌n- f Mе)md+51k?XX60=϶Bf*7 XjRf>rroV}7h;2j?Ju!aقe2j85Ю?~dpi#ӢpoYr,xyLg" EozV_̬# jPW?#ÔnLzz`2"6>/,wGc_ V{OK ;/ZکtIXm9yoFj2}Vbۺ&^PŁ|IlWWH,Uvg^ X^E8S|?2ZycԞtܓ/pj#'W 4θ4_v2G$]M)$krٷ_O BUp(Z'}(6T"C9N/yd9C)?J8iwJrθKyYBx蝳2ӂҫx ]%Bmφ|&RXؑZt_KAGj;qEH΀e :%Q^. #G#/r{iвDN]emܨ]0훁͍,NbPC,榃PB\)3O/7Hf~-Ȋ5r,G%UVu޲'P ~bbGLfЈ{·kv龎֛$$V|r0ҏ?ؖ Vg3vpU8]|?b;5e?>55~( [v?%uT; r-` dڥC8b+GCz-V9>.xĪ]V64Wb3 }\zd[cۇXg~xW3%BBk]_OtH7`l-¤O&}[VpoeyySO>>ƷJ@Dhl A CXB%U}\uSii%1eL;6/a S(yM,Lle40NJCmkڗTpؒ n(ǥyV!9Ս>>DcHX-fa(nN e2t,҄/B en}8OSU&WY4OePA:j1UQbig!-Ǟ޽öE,iU972Jx;e+VٿT0֦LQҵ翖W<ީd"`,cT+ z,7͍@m|%KoEez 6}%`c؇`xX$E۝`zTшznr՞B;EE7 +SMk;'i^$kz ,kX4GOy} t :Cab &}L9lY'z8WS!w-޶fvWTŜF Z.zW޿ [sҹxria|PBDUbgK69+hoT 3B 4=lŖ޿qYB,5Ljb;,f\rt#Y>BzڻdoeV)Ol!\!T@{t!4Gv>.W~|ي MpF=#rF]=8}I0{$, Ʋ4*sol[bPG3ܻ ~7C9vD9\£lE) ۆE?V|0uNճ*ujSׄϽgLwcbՄj+>N,π)#QI(`0qPkfP}<'V>7UfHVuNwR`<"{[݃W"'류6Hq̱̃2Œ1ݸ=i%瑐KRP3JlD=]CQp楐5))FaKM1O 0fU `t360jJnCd9I^]=)◢w!茙JLTQ!VYbvY]C;=[IyFݍ*!x֫g"[$˯vaxd\\tyB. q^kt@ܨi+(ΛVp"C!P2y¡-k f~Tf2sFg;R\9(BIBKp32+~25Nbh Ҫ\Uc=/'z CYlF^AMh>ڎ0rooLT.BS-޶At* +ҦC*h5+jL37">B`3On&dpds@d$ g-$nt-F`B`M&TPXT^:IJZt2vgkZ{"Ɨ8QiYu"FoYu|Rmۻ?JJZGs?ݺ@~# 5̺. ?w hdRĝUgͽv\ڂ&"C; 1ID 9ZMKolدno 1s$Qs$j1bRc5.NydZ O@cd7P 4Lȗ(1CN-'EdӄW39-SJJ{-cBGr.Q/ְ|l{VߘV|)޵#߈nxã'TAAnvp!$wEøvJ ǺG8>$_\`EĆ콳["w56İxȏh_R#d٬M=iGދA(Vci'PWmYmwq 'vsW!t_[Tk&VK,^ز":.s5R.Eh5lL梸!Iś+}]M,V e zs vʘTzh 8/ZgdXFtDQHP[Iªd~9vҼ ahsSٌ09(dxJl?2aAT:=kHX0foVŚ6]E!Q﹐7烧;jSa3,MbZpYh: QcX3&^}z 2J[W  C)84Zk%j2 !'p"bT)hߩ'$b$`[,c&Fv*Z0,#$3{MXQB*K6OSjtPDdJ{(V8Y@A}<-y"5Q k/YH}$SkDQ$D7ߒDh[I#CbcVzYL ,8[kIc1Ɣ~Ϫ uM\*ѐًܩ?84{J σP*Ϣszg6Wz!dwB!G*XX@ӛ \<@b?m SWj.E>4/B͓J‘B\sD;shbnK65m1 r7rU^jMw;Hf!ձ,AkeI.[Xn_uVqN/jӾDC)lKΊjzM8۷X?Fg2[SfdAWgIy;y*EH5c?s}I)` ޽0&oA7$'Z$۩<tW2(o5Z|-h,afj .bm/6\LwEcS'Wd9pF ð-9eUd &Ob6C'd(@#WYb( *'~Saٸm;W4y47a;xe_ljukeLp6ɽl{~Rlom-*rHM,X$!t0HLzݛ7E+SqߪiX/1V&Ҽm۶m۶m۶m۶m~3?&;N:uT%n2WՀޜ1vd FCcBh)\i1 Ij8qWY-dW>5o)U%b/%P  P'ݠ_ &$.T&.ʀBԁQU E"UlxgUYϗ&* zP"юIy7 <0g"V%pmv,qMrPXv˘V @p=y:[.^rk#'fd!^&fUItVzuO1n'0k6ώ{]0Wʿ&H55uY%IDzQN+-y ֕zX1 j$C:D.}Wr-e_ ,4ZNd3Uj~D Ty7([88 Ҡ`wvɶI'.0&Q hKrI#.DUCQ&#>i /+ GpnߟBv)JьLJ h#%*HFcPZls݄3 +N4W2BE ڎшVIfG.j_F!`.+q@>3t <'.!# mIE?N$tH.fS6zX.{4` LpQ A|5, t[clm .o5|{T' ~2(vPpF+ӗ`(iUO;9o Wiy5}!ɶ2?wUk|1!읆i֬iawrGZ}k L FE ୹ĕs OiuuuEO{ Rw@- ~5fdNy=]dIg*g4cA<[;d: +mAx,"'0Vo<&v {]so(kk"#Gg|LMrHVъ 'qD&#ӏ [!Ǟ-S6ݏcXh-rHUSap*fB4!ya38ɋvbboJvA+\A3ۭA3Gh.Q8LbSy:j\s#}/ԙxuP:K+-N^Y +guz;Na4͉}PMd70Tn8)=偙 :,`0E*IHBPO2=YXd SN!A5 [S4n: W7>c`)tEjQw/BVw5雩4-/_@JDWE&oP>thvXnt`s({,odqiN-Kzԯu$*%0P75=뫞*ɥr=n8MͫOCr-!m8Ow$f*cZ$) *L*XŖsE?T Sw9= -;B,sx*Ӂ0zb̪r]mG3ezNz5b{X|/WVC6#5.>hI'#*vf6=qaNeIuȲ\iOZ?r}hXRv. u%J*CaFځ" sP춬B$pP(iLc->NdG4>b S!hǧqÞ(q9Z9D0-F)->wC1Ƒ˞ȹ=^^*|#Xf9tRMZQ]ܻϦ|44 =BIcUdčut5/8u?rKWDxڞ\U7ܝ m Cܭx=ױ0f)eLSxP/bgD)G {n)]򜢧H.ջw~G#1 l0e)4׳vD\ՆTXFrӮLc;eEQS}U* \%4"S~>*l7M}Zj[w"EĔZ< ̰NVQ+eNR^30"I;ZlLB%b^ik`b !2i p ͩ an=@2* kuԟNK޻aEd\ <1T$' >Vz )쑅;HZб=à/2:ELc"{r5G]9#2}/f3%c~=9=PyM~Uu}K62X@}pcyn^&iIS_(XPyw}vX]x %z9ϼ!p~#iFliMۏ/\IiLS?xmbZl叢XDžnN,djPGR F* 3䌀0.Š.K[Y%93|0At,L"No  cTM,rT#]V]ଡ?5܁=I*#-BzBLkZ狤eZt1CϦM˅dw/ޥRKjn_mŕ*ZJ6VyhQFǧs`:Y(l~itB| '{W{q5W{#HWafS7IVj^#kcw+"-yAXD;ׅLW'_p 0?K 8 f]qy#ٴ}IX\/~9m'Ah@,B=9Y VcvMP˱p}dFt.V:VN{6oiu >[T ,O^5bo]V˩C ns 1OLN^W"}HBW2 ^xq^PJ;[ k"*c|kcj- /*avj܋rv\{DZyM_(UE1Cylԭ3 f* P?Nd[pQikS>+|iK@ 3edeƿ!8vPQ|eq O:J޹DC8DGJvٝoT'˭Ց:ҼJ/|j] m wֹ"< ˋH6Mb%9ZϻbVDvs V!EhxTe(cݿ1ئZ-6ˮuؘ]-H#G Y{6D巀 LxMZ'qHMNp4גESm~C vJz^2m2a2~j|i eJf1&- }G:\\f:RVuw8t_+.r ߋX:hGՙżBN9Uzp%S7V +?}3hط+?2DvuE`7S*~w!CjJ`Evop8ŨBlŒ߀Uu)tĈzS|-Nvs}dE}Ricd_OzNaNѴ҃†}{:4t18MDy tY 0+5ϫ QI /s7V H/sIh9|V<ϖ GlzI!*k oo){;t1%t7ʰuX"ܑe :_~=x'6C^)\qJ КQjӾ m}  h h?pjQ*+(`&J,A&8Y~t`EbYc,BJWPNNJ Vhk{Oγި@͟V>G-=]z { װeer[ׄvV[z]O KX Il{U,wJj"Bg~[i2*5 vK}!e :*|#47tq,R \N5JmKL ;ux91R)MG,mq9|-f;FXpo}ɋ6/TA~d*A,v^Qqtd_#3C7`o>2u$dv0֒}8g^~qveT#v#n PMf5^xZ,p0uGP,:3p}xSLEJY:51#xp%gNtw}]><-y`vUGIB ]ߛ}xT;5;{jj7A 9,j˓; Fs.7*q^ H_p/.n#o㫡czYdUrݔ]FBڈra9TI%ʼnNc/G(֡ k߷(JFDK4x׫ZNţ:‚&/` Ms`h?u +eGj.2IR:R/if2D5 9MRHȂ|s82:-l+k>&mmȒ7Y؋%l?$YG6D5ToPr -׿,vU,Ebi;O1v? 謝هڵK(oU6ȱdv[PjTkMdQ|gϗm䅃& F|H }͢7BYL$ rd-P{yW ?)&jE]ĘrNW| b{{wM1DRFD8LlP]/g¹L8jR)>Wi'e-SwOGFUРĸ+R!Z 5¶#^աHҴ|X֬_O%oBA4Kb[kQh ɯ1]yqK\uq =-SQ ץ:xHW:3e[@@8ജ\As&˰D&<⳨tE$36 ?J%EuMJ˛| 8cLcR1dGꮾ' ،$)åGԠGu͓^+N vu_*wTN%+G-ձ YMfK{9pK!tYCp`2΅ [ `~ Tiģ&+_KA< GLYB2;n-3r+BLk8v8yNedT@\K_!^Vig-cdVĆLyDpu`cGWs.h)J Y^9޽)LqPb]<^Z] (?2 ˙RyLr#^s򋚑qz0gG)k$fV-% w)Nr 1| q BX_obQʒF^! Ƥ; eV*ݗ aW v+HJ~yH`OCS/0V[[}S(L7{ H})Cؒq0t1?Uzw>pWG>T*ˮ2J(0>Z>\'ч (d#uQ "VX21ǦdG(y,)x;1۳'l 8TlXAۗ9AhNe\>[c^kHW-RTx̊b/ R`pHcab͑V\"xBg忨ԝf0%,d:Xn3a |FjΜmPSKU 9MQKt`r,'.:?1X&4yu*-^e $3=:&8Gyxz~D^&` پ +vx-5dmYn`!cA@t=@7/Z_U{BpB ϦF1DZ5ؑ;V5YgO!#s8."bptt|eP@rptQ>6ѓ>S[k~Yl#s.o"eV.0n܄!!T!uVDz~zءRZ=ֹk״8U[ٚ4J Zۨa IUA-UJ<͈_f3N}KǸ)$G %ᡬQnn F!sr@"5ѿ6T@!)=J+%0~E0; ɥJc%_IW!L;Ǯõ%Z{c׆ט久('[Qaی'4Gr& ßȁR-B{Ղ˕d,VQ}O2j YGtS`(IbHӠ7ġ 7K'5cOIjnjі/\Y+A>R&.yT `J"(HG_݌.ߕ҈źSš22dBY&;J-;y9f03"$J^47,Bd$otn]#(6Rդ5MC f^gYkM^`a=ˠEJ q?崛Y"܆w[QbCA{zt ӉL$v O{ql6dnu9K5bjByN(6a3b c͙"/AhǒCtNФ[IGD>"idz$[ ;x]Z]H0Ytv =f7f$vOl™*>$'w i6Hx7e˱eߋKo1j9HZw*_OOpȊ_ : ]䲜SwI@n,2_çu SKe4rYrgfmN8 Ub:xD|D.m5NhnRgT$tҚ (:ꜜcVɠ?u# {reX"@zRM3<3TC͘/Cr]|ЌIXP&8DΟ3=Jb0O!"ڥ$LJ;q}M9<e5w( ܮH{R,ZN)>?w^Bu%?zavY5;r|iyR[^`coy3 CwP |96.d:Db{"錟p_E=WԵND ${]nZl?OHθQ3˾[ѿ ]T c '?gC׫Ul>U: zv /,ơ/Ԟ$mˆa0Δ'gJ ,՗)[Ǭ%eI7''64wsۉPwlł_-J ] ѧqIl`懘||PP\,-ִmfefK/F ~R~GY'_=F;r)}j{Uh;v [] hFdFLxq%|ENTT3%_I2DiRNYj[ļVx"Z0rه5,"'q2zV 8+ *գa0Sa;J?w7%x W!>$n0zʡy@ EDg~~fYb"NyP56AoD.Y`)@,SED)`S*v1tcm2}KXv,Y]vUWHyX yRFޛTG׶f,`mT[j rve.?M$~ڎr`cL)k8C5kJ|Egb9mLp\sƜ# %B\,tfAZɼ9%QG2 ck1A#L&j-c:ZZk{hr>z w&;Ga!“h9IH96C҉Й{|w_~'qȝ4<3o`i395eF׍N`ʽ$pc>K1ɭ`鋺|sԍ6ZDFKC-RP7iT`|,o7|fOtr;%Վ} 5 7JdU0[^Crdu63`LR 8؛!oQ7%U1G%Mj${t~M"4D;#pt 'b[$(QU!hǘVHlPctoP7|!LR3w2=b8edD!NÝ\{pY%ͬy1'Dj@.ܫ+X"gnI VUW!`žUUɢ 5] [AH@pN;h*@N"N~h;[Qd`Rz4W-JȐ]Don(rFbYbD-oz)l|I<-\./>pg#U/{Gޗ\! ᄡ]d_ȅ<Ŏk%Cp bϩIh M W{!C:3\h(db[shc?bd)X^rt CTTÄ,y FDÈʙ܎o.eOH" $;yk!, SپEIFڴ k+qؓj"gd\uS>b%?M+\'^̛f^Ȗ6.$ ,2W[*4˥.vU+%)t:Eh~TNcuL_T=K> Xt>P45h!`.-13Z1솉GW9uG4.ޔE79j4\ @5-e?ك|i*L.=Øv֙UC/2 Sv|rYu>F6CvAL!D"k5X¦j. mmM@7X>%Ce?~$w봓&q!'tb'}t6v\6|W>[B?=](Vo N- -!hyioR\'8xGg˝X&v\>v@n5_;4T\ŠW`Tt5 *GgDjeqH`I,a4 `y=l%}=?GȘ1p* dѰsCFhh1{1*:k@ioGОto7 5{IPBN%>$c,VinK~ιMmO#:D_eΐ^60W \ ُ_CN;~:@6a8*E0鳢ʝ;`]Bkk33 E]n@AT˱9jx_J3<L5of>骊C @ ,m#mv=e\;JڶL{2M*eϠz,HdzgPA9܇]f{ģ$eèzFIrφpB}+hRT si\szԶ\pwjF yn2΍WW9*؎^hن<}fH)k{֝X/˽O}U!HD[z[">p‘H}J+{/m0^#1S~O nxuFKQa5)iT6 wQf@Ce1ͻO^>v>DhHQ7 % [l|gVT[ u>:iU2-jLݶ S8,\>+ #='F(&̯/b"˂zLZ.bNtw8BQbc<9<}KĠXaŁ! vŠXdlnWjjWs^{v nes_E@>غMԻe49Q]˺[aq[ݟHݏ&ɘZ\1dvN X. p"Njm(>{wh%hmC,a,[,ܴY wO`H:w[&$Ы:P[-/]Q=v3IA-d)1MpxtXHר\~6Sfa1Әs5%}:rʜ7i8xPT[%jǴIW;  + \T<^WǛ`[Y/p~SD=Ƨ)vT3 /3(M9c;5jW5#e͋T#PgDZ ۹/RcV M#x֜ԔWX.TSfJtO[ԫƑKͼU{6Í.DJ5v+kN۳H'C^7P.T2\Uhނ@?*F2txbS${(ɉĒ04i.zAW_|8jܾ;hfIiyz&>j穘#a`rL0OD!9RXgqN5(g4jܥ|^?ЉB>>EK /jY_j~:8B=0江TB9cx״D /-[ضT킽"; 䭱p{"֮L>}6@l@&\Ok:[ҸY^EG;!{sX}6ko헸"oWfIbD+X;W(Skn$t9_Efo/tϯAm'Y@şm3TnبiȷAt1!G++eL*!J/߼p{D e X;V,|3Px&ۣ_#sT]/}7hU ά[G ELd nxs&Wkk: A&vo0x$%gPG '߾K5o8O}e5Z`J|?!󙴋bVbS~4a7zg1*5v&聮EJJ F䈹`"Z<٧]۳PefebܠUZu]5tυYVٝ/EX|A 29]m9YN{/te'5R;qˉ/yAt{p{!FO_wS+!\n%&CdMy6uZ7NF+yα/VMvv(=o <۔휱Pu )GdU#. OX?t9UӃ7<ҹr9w(雜,?[Y[=.BJ$+HűGO=n(]!sbOYt}ޢZ:В y ܨً2{pxvāY$HJdFxWVL1yRf- ;0'o*EY3v({6ho,j{{ a8-()(;PDw=8%js-t%KwKX8*6m.='PK 7:LQn>EXxLY@M0+5mzº="Smok2,/rD1*ve4zztdh~X6=&FܛH_r$* ّk^!|s9YivAH3?U\udp cPְ{$s9C(( [`- {7b?Rh0YIhnXW{ξxF#م.ի)7+X9t"c`IJ5FENQTTZt fc. 7LxэFo]P}ݜv40j"\|H,XO4<1z2*RԩB˭׎n.$ed-eSWE7pkY0ս "esFW=r=Kv,i~8aªWIڋ6~j YnL=0'\FG*tdlz 60[8VAFY&K\AQ_xw-CC Bg<ͽqɝ1/Mdcɠ+Ǥpb#l&;Fv2&?տuʃz꽗_=`^vVy $?}@7g(9w8R)$K7YD7_4&3M5o`>ɳT H R".j8a/]b8EB{gE*~0֣4Q/t_b?xY5c̝zpCY77lYMj RPrz#6Ɩ"ml;U_$&DLs]HCJHeQ_F>`4@ J`\!yYuÌ3QOZ|X"m$0uo5|ӃYԓe [( ܔ^NVPKsZb@fFN;PW~Oi"FDPX糈@os`<}JrU&}s'Xp~z*[ 9[3YW51ju$GN$̊BEGcD8glM3>B(`=U^[OMz d5,3!K@6]2 6׏w?`\-Yq)*i 7S"1γpF5QXr+>w=2 (HNfB4w|^-h6k})I0gI 󶽴eɣ*v\ˌֺӇ*xy9͖ѽ/w:Iv(W$YcZQijUS;-kFڳ"6_O>H<3 '?#G_cP0gIuϣ]cHhsm ,n( ߓo W}0ـe^yii?'\$ 4^^ncϜj/qp~0W?)ْb[T'b++J͔e8a1zQP=&CSi#VonhjA%_ZY01MZWLC"6Lzb>'4iF3:lj{rgs# ͑Z=3Mw]Ɂ: gO Y)c3;\F??ڬ[ޘ0voVM*\5d"[rl~NuYU+(pÄp3čXNc3-e;ŐqB -w,T4 x1~3(( f/sܛ,8xn* Yw+o=UxlC`S"JYpv=d<@4 66]]SՇ=U_Hv[ezٙH:K iUPԎma@ BJse~PsQN)? c3\BnfCɉ(_kπ x$]6~)% F46Y}ptۃK _1g4#r{_Rb4KL ςm#衯: *\Uђ EZjQ? b+N"!Xrb𶸣D]ayן}Ah--"u仅.;n)1v'#,0bkߠZS1G#?<ֆ>,+M¡B40!N3jzpHN0Ҵ>Iұ%N cZU[f)a֡l^(~w/#|<"F sڑKkǘfߕqq ;$t)`|,{Sb HOXT̥ -4"}^]wx'ddpIĉH\lpV/ߗy]G??A6y4GEsJ=/¤`Aݨ*l1/ӭ$pI ߏh5Ag>36bK1F}`Bߺһ"+m VdݽĚ2v B>q OA!~˖m%'8Y"<JTA" !“AIʞM%>COIrq򠂴;t!ȝ8 Lx{Qacd&9b(i}sSAqvVU־ ƫ'=qxPDKzH3hTgԍ$Q!˝}~ 7)Q2*&Mb>]=|J-vOLbpڦ !$=(`kcv\5SbO vZVՁgx&0/)YGMY^ӌ:s_O/'/l^OQ1Iqa̾^ ]XC^< 8|Vl== #Kǁsts2aP2^p<.FSN*Dg7ΧH#F99n nȉ1!aE?FHXibN0R6-W:yءT{E+98_*4Rj*8҃:|R׳V y(, T!Ƃx[xXYz=i=]K>{MQ* ' ELJ_m Dnc>euO*}a]&RjbL}@8A28oH2sZ_ m?D+1sTGT9]nsM J{VX`'!6lƟ5 o>jIlE7x:"0m,LAB!rG篆(%{2Yl#]gu Kε* _do)MRf1zSd-6L(Y FP6TvazXC ~NXA\LI |$Ƶ Hw%43_=(c[O勜iudwЯXse^W wtL7w y~7ӣLM85js78)%Hlܡ#)7]@ D7tan8B@{/okh 72=3ȷSgWʶN!SA[`Tf>ji9 36&/ $bz<AFs@5PmH?x=ܷ=7,cT̨_A5E9:.҆d?[f.J[LP2zP{ТHĠJWGGCoD˔V,ЫGsWR n"{CTA4 ȠT #抽pb8v7H; [,mr+t 9~ =Zyf4MUHh% 2'ں6:v[Q$Y5A(;1z2g^LQlZ1z/3i@a8iR5=`)^ a}9= ) HQ-'' f</ f~ݖ+Zf",D J=Vlcy4۾pb"}+7Jab陲eUYVCDd0ك"n46V(5)8aD6$&"i}KhoUs^.GxSPAWgލ& `v459g j[`ZvF|צ0 t;=]@Y WE0 =]^'y^ oٰo9VJLXV=4k26HJ^PU`]W\ ي\1e%1dL%eV*=p%[^e l`TM0a/h OL Lvσ٭TAfNU5;Uu {"pVi?D|=VzΞz"M+s.pN]+KdxCB̛cn0H'vՁBK"xiFkUZUޕ`0\4/p-/8\Mjaw. <4)OKiu)TVy3tC} tyaj䡻~]f\ªڇiaɹ^`IAcA@D5](@ҙW&y߽@n5RENNyV>.Ps%qJf3a\?fAW0b1TݘL52RRSpH9-y|Vjwjhm:wfPI0|lHT+2fRz  R{e!z6>U0}n^Q' sؓ-eV)>|DjX?D\v_=wdQ> -IsU<.6-}P/|V2 H96aLJ(Lf*z2Wh [G9 p_};Bj_2w^탛#R˕ㅑh2km19KwP"Llb(G vVZd{Ywxm/27] ,[Sxd*^-}%55N".1o~1@?fFbK@-8ױFL[[ݖDy J$f NpڐYLZrsҝڨjz @;ɑZL:U219;Y04͍q^'x\ (2$89Iͷdn%g2tF +Xa _YW=7r,ҳ 1v*tЬbrj^[7!b>|ID2Z;*+is` tID JldeYl\,۬BNΡZ:r(t#jmbx IƒJ3ds K(hPG|l`,XWtPߝb)m$ v-#'\>X4@+!Ϋ,0&D1OW7uo3g3W;$v?+siS74YYuo+ݠsbk7ivݫ<I:\YU_)FW?a!{ Iάa5ی*"r߂{;pVyL$jo';/2klًv=2$5]^ cc =]i? fe*L,*_##nsr.G?L\T!'{7)3d «'@ >+Oz^Yɶ{:Z7\|u_pCɫba>9o`f$DL',W.ڧ^ԂF'f-/~.ǀ!J i6Z"HӪu Pt!^,`"Z8'"xkTp996p9_&R*h`NnZ~P Iƍ.2$1UDYz!J(jRV#L kuc۱ (oxԙs> "n(BhL]zx\=3O9'|a!ECxdp+('_dTd8Sࡴd(. 8X6lWXuWEs6:lP>U8~bL ><5߂̛#q\@%NDZ~h<Ė T!B LnTMK@HeDTJu0uemlVNlݮ7z':n e~ueB dI3Gw6_B?<> C tA%Q͞T!E_tЁQЏ kQo]}۾Jb_sL8gސKKaVB8t65<}l _ţ dvgQ\})~uɢk| [y8 >f[!BJ;'`4YyrZ#,4a"{B9īj=*YUk5LnwU5rO}I[G!=Ӝ 'T)p E1f8X s7$cpFb=L&7]͚9"X'(QR"S/kʭ>gk.O+~z6r}v۞W3P`FXiH9ąz Ao4՗I.־> c\ODN1_Mgf#r?v)Pf9q3+n%ˎ}͔DفbdۉG 2C;ZdVkq[ ܫ jk-HH3G4|hR7Ȳ#]SCʚEF;~2&5k(E’ˁ*:*Vk? 7uba]d 6eE_(B bDꃟRrۓ;+S9؃mcW*H26%meJypCd[nt6e: ((7 o;yL&]ѵyadxv@ hJov;Pc/fծ bXsα8A @wCΖT|Fqf&4 (bM_o`4`[*DvFhxCz6Y(=5J40!"l~] ⨙eΛnj#M$)dSVdu㞮eM&["*pZIXdIr/]KK+[XG]'ЗuV#<ù,fN?9>o'JO ₚ vBmďC&CbXYQ͜}l$ҲAa/Dw my*Vնx'ĻAn YٕdDz^?+zy[8CFl~ܨp-|ҽsM] 63 A&"?z*1$?ל8O/9[1ZC)kat!޻W[:7B?fk:TH@_ovO ۩y,PYbf "/e7W/nWѦWOuY_rG=ӵ@Uy2bدmhaD$aQ2$\1fy'F+YrߏINJDYkn' 0]k s̔ gpvɄ㲅 bߩ9Odݢޖ6:OBz'N횹l: W O/߾a,.Gjq;潇h`P3Ɉybz)̏)aB Rkx d/dȊ]- \48G'f]J6N&5ӶmyGn~3DeH۶P jD*'hsy &[;>.#Ya3k^M4L? t\n9OH#=5Ø7EBT8pwkS" hvW{j!Pbf^On:LcmÀ&ji]SƽośAhggق5XOa0Fv"0*[PRXmb]8x(= ;Iu4A%&iqWVeJ.xj䋖 |4'`cP25vS4FOo͙~N%D]:6bd=Ï95|u'y5 t)YE rzk[( Uݴm3ΘEeM}[zfO90#9߄]Dۮ?k*ԵHVq^D8K8PE)P4>})&MgKn ąS`c9 2!8 r怢땴=g=h]VSȐe-0 /W}9[ /Y?mdo@GNya>> vkkYaVL+|ɽ$֩lk>M|"H8u'PԌdu%}+@+ n|ѱIrc BdشJ1\d%R=I$Oi]v뀹ٞTXꎏs"0=2i m qspeGn<̘JE>Wf )qt,͉2r4m"M܋ՍRl%\ ?<ÈKSQO7$d!8G$2x'،ʵqt%1V|gځc_j>ϚG\8Zm hcÇFfzt!Xe;oB\vvhh[3l7׋=#:<#E>i|sU–F)i᮵w&sʀLWӘB=y찒Ny35b{7GRCJgqPoWVybMF@SP_7kbˤG'?'r`@f x9ȿAzj+ɏ 7^Gߧ;WNV; ĘdJS[aHjOB7i1Si67O<њ07d"go\4蕣vY9>{#f, 9x[ ~gQ1\n݉M\2Q>n Lk_BJSWP/r=.0ߚ(#lNȥYWiڻJ%^6!;飼H]-y&DvUL{1SM-Ҷ 4o(M2i͋;w@Bp!9kO"{-P[!7؊#R\Lc0Ī_zsYMy 9|r+6FΗ@ـ* ܦ*T׻-kMHU+E6{A|y npS¶"•d` @Kk8 2+ Hg]=!fؼ nL#"[I^ y^z>VS7ͣOfŒԒNl3?Y!TFD5 $7!9 E4~Pdռ'E?8R.$'ԕTtyrI>7gO`bIגe*3ށi5MV1n|v^w>݅O4lޖn؋e>$S,gҏt-bķ˹$<(OhBW{g0[}P^$&d"D]ڗLp0s2oM`lL\߲?D*5~;8Z3XUs0U  ؖTv{[Iv*/huoWy,ʠ5,N!J|7HxkXu4a7Oi>ן =w`#щY!hayRunqT*q_+x4o l2+@Wm#|w[͵KpKLT8/X-e~w?TYNϙ>\O xFQ?<qfJglzRTON]^]#Ձ2a%v"d‹ SO@{aG@2`8l4w9`Zv|H9GtdGA]0 Alس2I:-Eq)d iL ^^N`\izu}e,vq,35 nN3pxÊ3vʲ_;t3AAR$ "|M9H9nj$l8ܦ¯O7Z )nk<vO r܍?E}9i1 ;VZ%}14=厨jw7MJg@ݪ:4Rk7R*+iS5#K0t I mޓnEb&bAFj3/ޝE^mg߶ ۝wG^=F#m|$=DB~n=6aUUfeJp.QDfLc9b<捷V( :6Pof(ZEqD9Y=wN{<E 7aVѩB%mq"XIS#b?iBǶGY}smj~ zZj-'̓vs>Tw6gX!{ОXf`sjA4\3GJ%nVy;x} K8>olƐK_,% 3Ժ.Uy͜ 9{83: #YԉCϚ~91)9n\E3٠7̡ ޲U&&Jx %k&kV=z!n+Lx꧋>3ԣvu XdtVb_$ PGElSu2JHwNv^uLR[Mb+G`"avp9ۡ@6]#G×g)MR-͢~U`"'+ٹR"b/ms-.n!CUWh`:ߩ&`Pr 򟬕"3rhqtJ@&F)<;_+#K࠲笆t:+Q~󷄥9PWﱲ''sK*܅?ݨ70>!͈Zμ;PVx d`&jZ)Qu׮ء^ivï)@H8dFM6VdwMfNʞ3ukSu8ʽICBT3 s'f,$Њ,v~P1;E֣BU6{-)n7ˍYqCɺT@(z9!B`:%d WR)Q4h93 9wqWz=}6>g^K wꮂ6yVrˉL`*W!=&&-X񃯶Q[Pa3 G JOUy;FH0s`y_ZlqlHP0"\5 ǏhN9 %j@Kr=)%?V ?x[ba^l}JvшF;ЬA)pAj]iA [N0wM8 ߛRo9Aį@I&V>!vJg7 oB^&i[Q# mêZ9oLBH{7%&U ]U)}t,9. ;|m&d(r2nWaU?ȸvt} eJ1UR7!۲J\gjR'E>n"=88Pbh0(>l vREr!-${B\DuT` #/kB_JĝHqt֥ NgM)ΔГwd|62!%슜^VE_H:8fFF.\%ڿZBrąI졌ɅQU xnSd3󶆏嘶"od&.8K}.S}2$Zg1NjU Ɉt`1}V^v <+۠|zڟpKmj陞>ƉG5Nb ]2y>(N]UxGj KF ۪4˂n,p~2cOHamc-ʉr M%I P}a0o`lS!l`k7hSшV䖍{o ?x /雿4V=AVTo%]㶌%2S =.Ss96=bK[TvWUd֡*c8^0fQt0"%\"(bw{G\N-%*6CrYΈD-Bh1"GLȚm$ApV;}_Ll[{IƼX@R^n<+_Ąo`ك+MX Qr1Z+S7rn@p`CN07cU?;K OFބ#yZXŠEE]րXJ#Ls%B`|7>Bp3>J͔u-@ 鋧AA*,21]xCU\*ys*>u-7X>Koۓcس"nڑȬ) wPMsNq1ѤK(:ZG՘ dqysߏR=;?M[#'\â+f/͈2!jv|,w)j Ft㼧J3:obO؞I09R lb)["`;gDtJ䪾Qz+WplY<~.TG+{`#"Vi~iUq3,UY+rݯsД͚ Icz<˔bSf*僜DA<֔G䨯?7w>oh\ʊ*6┠=%A dH2y'kZzkshE7Lu4сRl\ y~gʫ*S u8&|_ّHQHօC? xcJ>pZ)^ 0a@"J<8qLDVC@ZMA%}mQJP_RKQcg g9$є$:V[\J>Q &3t[O;ұi!q6Ge?Fv T>ZtCVtDSM6hQ^?ù&}j-1DZOBa}# @ΏUs_=v> |%;e϶lu#5]ʶYꯌa"_a7c[[#Y ( Vx)gtL+ ~}}+R5绕} k&#XھD'.` XUAf Dla·s\^<) No Uىpx%[bĥv' $*]hbyeG"[34=.|!YQ ]\{>7mʛ"XC/^ l,)1SN=w 06"%}MSL]-%`^?@T,>Be-Nn+$<%5 6;^8y3v .%3 >''{#_G# n&hʫ~)WBLAdM|ճEP+/V|Kb梇 '}OqٳsHQ{Rӯ 5Kl+`u~bZp w&_$6tz8co;5oy0RѣnLЭʗgTWNO{1tG&(w5#IJ۲[WDIuu jU}s=X6=E)"c~d- Oc|l,4ң+$?Du<9T76qJݒ|]YJ5S҇Hj(XU Rrd_iۊc |y=뱢a2 ,ԿOpdsqӸJ\nӘ)KDRNHCsҞxB;W8e}LA KH7CBa>?dJZ.oV܎S3+s\U k7ъzt䓇gNS/ޡQ M;" rU; <3R "ׂF+ =⹥s#^1܄#}n~h7jIa3w?1UU!'= HJC{ogbNi:pfツi˂>u)q_؎$iޮR&Y_h .HgU\pa&Įe"ۭRܳ2ײHH |Νnr76AʂձnCrRݯNU9u} gYL?YX(a*Ǵ{I\Uw/AGh MZJׅ9Gx̉ÐA ԣTm{gG&S [G8@i`+,njaOmHO4ngBSkl5ŅPOh8 , B>߫h.>SVͻ%8Nߢ#bsp{-[c5[CĂ*o%>hVuڡ~8`[29bvhI{A76*IeZÇ-mF RƶS(_t֔~—Rh4NPcLbيH9 X9+s&v @t^K( 6 c| 7OtPw= / %Qb0ki`zo=hOd{Wh_eVZۜ`rD>jNe^D q%!Pר SO<F; 9O0g}ҫnv9=dm-EU4g!,\ N}Y$ YϩsciHDUHImcsRV0Ӵm/sI>04hSc畔T 8ۆϿq+O imؓYw5K{29~t) RFЙ13P@S ܫoe@-oOw3j6_4 Wӹ WM$#O!"Ć8 i; HD6k|Bd?{SSHxRl(4_w9|].&<)hd*`F>`2"qRR91aQ !Wg!mToLMQ5֮]@Grx=T(h<6('\j&qڮ&qx=ipf:1=wcݴ!7IAY*EWJxBqA 5=Y0VFgJ9Kaoل-ND(OS( ww_ׁ2(Ђ3'b{ FY%R1P-SHWHxE;RS,S7v*pz{l'&D ~-GE< !N֯T teGbɺ{-Aȕt1PZp.lݥLɛ9\Σ;'G]is 9,bMWBz_!oꝵ#8nZ(b5 j'Lu[8ב&uA3U2lJPV_on!>Ѳ&ER; UV{'Ŏy)=0B珡 j(RjKek|Q&E: jw^XHBis@`"E{'E 5m$I !ӓ`ZܿF%CLTދxBb͘~V@Av._>[#jލ3{8Ѭb2@n K΄s%L+efY.vrM .\20Ec =2=c_%/W &)mu65[a(֑s:kR+H6964 [}bڈBmOҚTcE1dΨʍ*-)bo {!glKeye+ )B: a襵SMq8t_t]{"OE> >PzM}7jkEg7+v} [sbn/ˣC`/(AkIcF1?ul]SY*EʸPo<ݩXyᝅ|*yns6/B.LBq*.\ @D'0#(7[Heud4:Q,zdYt!Hglg #Ni۶n[`FxvC1j=#ęYq^BhA0k7 Irm-K4cfVQtFl:K21ƀis3_&ᜡ/bX mKo'&7yd3s+Fl5E&Ep˚FI9PTW~ņ%ܭ,m,݆6 V = 2Y[lד6~]hJ6@T5N_Dz4cHSQ]pΪ$dwcE"-&5+vL뱫;{9n&0bƃB7-:ѻC7͇ǒVs9p뚕um&8G|-Giu#ZzR|"^e2]2:2}ɡU(Q"U5sUM*H;9kE䎬7j`'XI8C"mn,sWT$1ZU Ps!-x-<D vz!Z˫Ֆ JW0rzb Ov"J eC%5t[A6h333T -u[{Ӽ)N_S~-3O8iM)h5~r^6Xθ 9j Bg5W(jR+J<4Ɵi[NuGt񁜂7y1@'ٛj$Z-foprWo˙3 ʊUN{o|cI;4CvfB͆vJ{e1 :N>?mx1v$&o(zy鮧+LH2&sf=TX' #GC/Eׂ/KU}:~覉]~] KF!'^_`%2f/:2˜K(qνhsH6ޭ̟ORu v,E(`zVl%" bt5{'kea۠QLgVRTt5>Wߝ-גhT=~C]Nۏ$E-N])S[(!p%7+9OFԧISg0lFySNJ GZ V 4m,E=DZ! JCeUD&'{#[o|Kշno@4N njpcK\ڳt%zXN֏ĨcaCf&_u3z-d!Xf.1'pj^-cvG# 'lqީV%YX]5HqW%DQ.мѩihXƴ{MwT&1NEV@V{tRipr,'ݺMKd l2&Ւ+@XI:K4uuEЊ )n[CQCB:AU$ RauH_n,WA [bgSlIT+h:0S 5\1 V$$mb,EV֡'H͚5[G]ToR8 |>0b@OBJ÷ȐIћ)!yh<wx =Lc,:) ~BҁC5kڞ?Fx1Q(9S/@RZ6#rpCXa&z &M0X2,g[\}ZE;jZȔ¾M5t:<#uZ@D#bab6V/˗x+ =}'Aݘ6z홼K5S TjqępׁGR F> {&1Cz8]w DSZY75:Hݫ6-GC~>;\/j~Z{ ō|bS81fU+BLԘK&A<8 o߾w7vWJypE8nEVa{$>IʞaN?]jC(Uk]ؘ)}qI(VM$W0?P7WZIYq. ŴKyvRcTI{თPs sumT>T~{p-sa(o A˫ \}kl4/2rOO."+*?=S;,O, M/;WiSZˢ=j:u:@Hk,t}ZCaWKeߎ1sBYtFm.Z>0s!7_"E>JI0-P0&,0t!;.x e~=O8uಝݞ '1mAFU$0Z&Ȓr!g*Rt/#8t֙][ fS/p9Cȼ?RV|3HР zu5a6b->)hIZ+Μvb|716P P} C9W ۬|,y~/@ hѷAL72&tW)L*JCl-Hg,Pw KwZ>s4ddaq>#hel ^Bm S GN`-wx:=p1lp8©waW3pcyzzT>9568]etw|XXCfy%UMOGc\y[.`.5=4x+57Kے^䎟X; -")olJfR#}p$ ^SqUc# kIВ f`s uiZɥM>{rF`4v7AԪP"f)ħb~/R#ݤZ+oٙL{pS+4F Ah'G2I{7)C-KHL掯6! 4ra;K `b!f?ym먤w?BN\לx60mPM$o)x+#=&S@WeȤIvuŜg@e0@ͪ<=xNP&p|Jh.6w 51rϬW!x:׊s2ћ#275NVr*z5]Oue,ew0:l$W:=TvA͸S8 xEdLjKͧS2oj0&" :.[4 %B*ˈR]V kyޭ)md8ٿA!-6C\dɡChYj(G2sR|έA-v^4fl͂ ~eMlnDB{Gy!#.O&S3=1r VkyPB;!EWnHQ#1`#K*yɪf[ 8u88ڃe[&v{Rp7FƮ8x"v` o ݢA^OQ>ʨjwG9]*:rDv'"yqQfRnO]OovjbH]^<) ioG#¹Ũ9޾!B67Է6A65Zyq-,=saՎ`ӛ5'jdYibzip>f!rS1P7T|EZ'SwIQ۱yxG#ĩ=Ϊɑ @#q:/`"iڗ޸׮dd^Їz+>hT0w5΢iYO7N-y#Lv7gbZMGK TcpԦ4Zp6tП ]wb{Mo{Ӷ|TxSY|-rQklY|:ue[8akXb޷,) =UU`&@' ?+KH\Smv4x2Jp_qQx´2jH@l%5&$~ij:'(i.jqўäx9{Udү`D v5dj~Zf 7~D [WQmv/\ߪ?~w(a+0Gz7 eW{DҟQ!}^ GeBZ96gp2Q8_BKc!|B/9v75_s=BG7Sb7b;645Y3Pm=DYNt|eq.fO_qߛeJ@$g PQ!M.VFb\# {s9 w*ߓ_]ZOv7Umtyl(C}ƭU4CLON"~A(_Qti1gF R_ں=X }4rKet8[vYw9Յm^M;ߚ&N)=kkbSB[Na C* rAWk %Rxb",H#L\ݴ6|dSHJ35쓚/Ca6C{$l"W*uh? NP4n4WFn05[l&n V*I&W}ӰQlOCj ȧwΰN}V$a[}f+k؄4erIMh=[Cc!1)଱G'EFtF2| ;ABΑeEFDF8dmNu6:e&8Z1+)jcZq-;ȋLhW#{1:G_nĪUj F;Z;6aXp{gPW $[|4k +I^YF N,b k'u=Q. vFu\&ayi!AF'J><%QoMV8ZruʷDx*]#DH0;Az`gOIE]odNRq}`8qS0e1FMĮ#kn6bIn"1)K"śV c]?gk. ]:T;IK+[cŃhBWr4.d&y̱/j;k _wcʃC5;bJbk{C_ ypĿqHX-:(z#Q7-i8<>mr({THWRIwx (TX5Ӻɹ&K{ns;H!i_8e^ 1ΆU&eCj C7BMX/RIV@Fh5 -3gpw1:OQ7d5$XqlryʙT$gÿK[N`|zg`9b`7c:ZQJB.ia}pMfR8) |mj=@S.?2Ne*y;G!:?Ef%B;%{3.ܿS[e  bGtOST;d0Gކf9CemkO*>M%As"%̞B^̜RB?N> PlJ4-IxH4 +1-gkj%5(QDx4n|KNS m$>:3<0uΩ{}^U1F#똤c(rt?n S`);@M]o`[4" :M)Lu'${Obh@TFOdPd}${W[QEc3/9H.n}={T|߽PPdEVt fޚ~@XS3Y]lD\a K~DNm ǣߵUQ6VP=c>e(,6jC48VHPVAec3*{mΘ QlUSD]<ه{c5.CH4Uh_㡜 wWDx ?p7${ ; C(,\oQŻ~^7>jz~Fw -E<$g2d2#__`8t.X|>y!S^ʧ]t~Ä`PiՕܳ6N. ߳"T8$ p4_1v+9фFqI},#UTXeYEi%8'pr:nXF 2\O~W*ZvSAFr.2vOLsLl=#߲֗Wh_;j<ZVEM;n36E䛾emk ZMYuLBRZnul0Zz<[@xIw*etXFsDI!&9qݍb\?@d"M2Gr!a cDQtFVIX(fi*oḄ$'HSʎ0#["gǙԔ,e FM*n~I GpkK[2/=yM6 O\JQK"Lo%w?䉡/0&IKifj%n+)G4MJ36l立1)_>vbg;{P"N5 ;"BxS,zw(G2 9:Np&o"K}Q^8Pu\ֹ׭h ,;;W؀_5@shw}ڋQ*0_9Ÿ Hyaժ6|\񹅥k^}7n@4Տck+y$I9S ca s)ψX@RHH04!753ExOl,w9ۑ /6h ARϐa{"l R3s}f|&y;i6Vp3 4,LޝZ0_SILђ)ř$Iu[Qg H%d]ᮑR{WY( C.Y^,MYWnkؘ**pFfSKq<0z {IԋVSp*z )cq=Ѭٚ,KүB (Fhc%s]:RXxΩt[qo@j`Y&x'rA/E!BGiUV`$~y,a,@Qk{Ҷ\{P{='}~*c t cș{+] &[$u f/T 8礂^,2fPG7z)wa T !۰b.,H݅34 4z#EɋUcd<0V9N,X7NFRwlbAv7Y=JBa/pȁ}uWP=~T >YW[k186BhݱWK<>}#qu3^y\'{ۈ| p61>f-34ᬉݱDwytzK A&k ѡs4\ NTy} ciԵ,C"6Iy*ݶoKUǸUq@\.E J3VQ||bSԏ/IW@kPTk0<ӟ&;MY!E1hMw4EO`P\P^'?Zg!vjz\>limO<Ys +"X\AI)â1Ib_/It+dAWn]MOJAsVt{}m*R&{eA=OYZ:[~ݤ){%\PI!xo2O͈},9#qw7~WjX¨s5bRN.b%co*Pe8rZz#Hb m3׆Ĺw?Kbc3  aE|_tхspj" R¿~ռAo,ݍr+]zwt\hNTohdƍRķYnsXvX0i NFa2oM@Q0k:3~?XNC/Ѩ~I ÊBa13 *w;HQ5;z٧NwJPJ0 :%C qB44jYgutR՞Ϝ飲5@@,fؖnmבyt[Km[ɖA*&o}`Ǝ"KyA^|7Rldx.blRIl!WڹͩCVXĹ2mAŻ} Irnv=]J3*n/Nl-b_orw,7cOws 'WɰP襢D[I+H q"T9Z d{X9_/L2{$>uJzG M0͵'2G'Ǯ1H% LxTі{5.~XoB /&wBWq^}:F|ڊL -e-̂nL2 (u1/6ar@y05E>+JI;n<=/Q~{b4}׺̓Sܹ֝rjb˸{;G,FQg<w)vu[Y Byv< E[}Fɯ͂ݡhhկUs&@ޯ\[w_{ۏ0iZhՔ+ `jg3ÄL %G Pth;e2Cf}[ 'X!U]Q{/ a{F\oʹD^|!v1p ;zU 7pkڥSە=U 8z%X)]<ʱdM Yzjjs*WX*E|8} g#+ee-}CNYW(,jTe[Uzq:Vg[͘v1inN?jU[yS|Z/L\I')_Wzboh%ClI|ƮDŠr'*i!2΅b46p]_GÊֻ,!o'<"Ysӳ;o8ݮšBaCP_k\U= 't_#w r(OoZ@~_I샗W!%V1Ykְ=eVcp~^#mTv7e{ hanhnfMo.wFS=2lg^U IcsOE\Pf-䰝7¥'GpYgZ+p:7OB3\Z/͌ #+gĎ,N^lȵL.#{1﹟[˼әx;'B.=-H\ύ 2I#G\Z@LJGB[y M- lǰi6j֚z^pmnl@~@@^8l>" ΝPiyC70AaO>!?N=CUIBz#l v1n3;Yc ;Ƴ\b\KW;jKT$QOv$}8OCyM0CkЧ!@&4tӻDC-ZBUgTu2-[WJQvWK׽*qF 15ը(_L$E5kG)WzI]8Dcf>\bm4>;GmQU6+,']1ޛékLd/R{3zqD*nM`5ӸձVf i}<.ɷ2Uu}-Uaû3>Pj3 D&dL?RKJco.:='S ;=1>IwWIc "Yqs9}MXR f} 2[ԍQ3єC:X 1_?!X f/E1^~ N#: ojy.A7tLc? Lv U{\tX]C57sQ_D4}@CZ?G.9Ot++l/94Q]RV n*Wqv/vv; !ZCHDoEvL,ه{7k.W^&*I8ZV vE;#DɫME:ka `^Ⲵ,L5fiq5 88y_ 39 3WVPlʙvw x{b1 ݲsС N,rjGK!ʬt^~EWT}O٭]WL4%/JFQ*8S~/5*א;ƗaCۨL`?$X5ΞY݆}82A%*a/ q|3(4kK}QBS?kUJ)3:rBvqojEmAa~AIJH]!1ҊO1ulC2G48+Ҏ!9QHx{`N;@Zם(&lX#)+&PÝJe5ɏ^EܦbXVmdͳOD+FoݐagѦF#/x EE }gXMɦf!,ʩ:` kPְH< $O@MlM[<}^ψoΒ z`ɢ!ir7a;}r7w1!\CAzRH~@Ĺ뇏Lin9!**&-xlgCS S!X._kpٻetC%Z4DS"Ş5ATUZrRT-;Y>gM2@:bdJl^.⚗izʻDJb!  $6Igd5_'Uώ5f hS){̛QG4yv+X%BWRc`R !M=PLSLQG0Q_T4c֏9xExCh\ǖw'w1~\dxl`d­fG ip keux9 k(v=_]aAބ'f7G;?o&6^y+p<;H% I4{&>03'm2dmNDjYK0Z+־jP0ѐi!p,A\<;DֻX\4Ir0?]D>X}2ĝd6%9ൡߕ>WThK<;5xKPt+(?њ9T9y>"|$\ Ý?@r WފE 3hʗA3ґ3G!\=.Q(o!N7sK* {SQͦ7҄׬q$~_-x ,HkT ><*ȎYLs/n^'񔄈A~bo~k_0lΡ'Kze`0\|bVϐ8j-)+kOpi6 : `xBtVeҊi N[]OI˹x阬Lo"ZCO3`Bn'Өoj^p)`ӭ/3y@B_$vU1'/&` +#jQ PR^9Mz/N_OojW 4 4fa.~@.SL.[:{u\N-_f;X;2]Q>[c)k\\XkFӝu")Ac^1R6P4oVmܙK;Oj^Av-o@e,C7SdL$soKm)yU:4ZNx2U G',qXRMuMI}.2{$:(籃֪DmSnx벃|75|ؘijQ>RO-s$0 bq}cs(ۗIxWPcdl+\g[Vd`š; N$_!̀|= @7K~1vL ==;S5Ϋ)G]?u~JMHg˕8r#jjkY!C8$ 2 +c8b5{; +}Lz}-M2h1u3"62#Xr#K 1f.*H3'sgC2ĭ|UY#5Nsy(.M:c=qfn10~-=zi5W@pXq嘭k^$ZZq$x~%xDRW-e{Dʼdq F\9}>d~IֈjwoR`Ek">t9-z-u02B.COW+}af O0BL2:Af~%/kAR&M1^pk'sTa>iu$j4:<IaT2) "j l(&'s1|yֈF ʷ:Q2S3! ?HV\( |i?*ﰌ% 4T:gJhYZ}su<LL%zG"%tl=tSĊ6qgo=Y&EyRJ [v*>mjH$RJ![-Y+،W/p_} *rwm Mwz"F2`scw:^^"_m0RkHR_95pyZT ^ ;$C>dkH*7c$N Zg].V's8w`c&d=+ Lcq9~(-'|q31qݰ+UBǪɿx1'w6.=:EJ/ $ۙ# ,U_WcA Iqoˁr|px|ϣ^ ث?6d-ko핐.4 C7ѐt:|S~ 9/1e30ux4oAPv489jN3'+Zh>ԇ|_6_,z˕܄ihe-kl4nU'@Hg?GTs ~^ ŋXnC!i@BòS*62gP|MJVѹfgT=&M:namBPY_o'ΰ)¦AuiE&y0Rkݮ?i,}s;ӐWcT*`t"2Hn;umd#"k A/\p6'BCK:92(m0|9!u͈V)Rċ}ϕKr^qF=NF0//Ihn[ʷ660])FӫşvMq{DntFxS>psP)Zl`uPq/s "*O_5!ց6{\QtfLlozh{]mjk t-"oߗ{~XF_ID?yE\V)S!WCB9մ1+O5w]=09 S 4Aƨ?99Zc?ct$ͷ>Df>s,AAWUDMӶv AC_.P6n*$ af@݀L*Dٲݿe9i<: e#Br?&BgHDi{O~#+]5tFiڡ{'3𴥒Q}4fNZ)Q5f>9/X||HE{JZyW C;aUr 3j@\ĮN(~N AnD#h'q!faEئ6ɮא>Ȭq7p_ Xu6}Pt qzgpJˋKBdF^/)C=dw` "8ȁ.#@/b-(av<3ZƲ]bfNC]Bc+nW7P̛n'Txn1PNANC3\LZG-cc#A#H:2KIpfU?=Ȕo8i%=؀9YK"C|u3,3 ҋUWWw.na0Q}) D ^Ee[ 73ѓRe,iPZ(/JW߆NGYIΙOQ^ 2ݝWQƵO|TC؈)ӖK >*bxQֶth#MX$"eS}2{VPjDC΍􎝫..v–ͩf\h)KJ[lXȨPMQtŋ^Ha z s+/z_ yjRiG2jh,LYl0@[jT uuPjo6uL/ILd;ILԩyHsM l UdZ ,bae6d+k[7jUk>vU3X1[UO\>0r“J&:++-"E}xԼJseKrt0JLp.Ԧ`s"@0RȂ!Q;vmb9h~i@o<'Pq%UCrs{ 󽦍Mxl r1O "[\#>G6ExQ ۖD3ej9'3ͨʤfnDs-)ijtj?QŸ{!DBmPGn(WV oZ"H`)k֒ӕ9::)R0K\g bmrbF1~k(#ya6MGp0@fP/] $ 6YD}'tdfVHմՌTFṕ1ܸZtf{ ^ޣ? nNLi&ę6Tʗhld a{F彛oY YT9>a𷐞 #-֛@XD-AM j1IF<MP5WWzo_"q:9HW;Ubdd{&``mKZϧmj5wH Ɉx H9$xe- g⌺ejZ̿)>Ǟ ZMjrw'ZsICWYWr%/{ [:Q:-O1-\JȭofrX6K¢Z+/;bģn7b[XS փ | '=+VNpi闺F*b=nM P#h\O#?蕕;G 4:b+,QʦpvWa%/cç2,#]lz$^.4*N.B>oVl-/IΊʀRL A `:T RjV ϥ2r\\>ޜʁ<,SQ 9G>(< tGlGHfCn*~ALua#`@ƌx]x2ULWJ1gJ-?q >j?V﹧V-W\fW + >6*˨-@{(L fJ+s^C0@Jۻ9걡q#^l=|; vzc0ɴj|>"Vũ]":nI&|̀ /`SْΪxyo<ٯi||VN%8پ˛ eR񏐤2) ͙Qy1ϩ Z`A/kVr@;efq_$ gpK JUFMN= H:Zcx,޵O,d?pVZ!Ax)7H[sR6,p󍷢cGa ҅y)Ql2ե"}ebHCk#yɼle-ǰgl2n2~\onJMICNj'7>ŁKrK?\єzZrEw|]ت~zᨼHzѕJ-VCZdd8afr< ͅdA%̗-mH3"1Bfs"n\2A??e*p9ɨ ^jz(G `rp8BY5Bp Ԙ~/a<$|7 {bp5퉊=rjj̹)34ѵѦ٪*[J/ *pv%ڶʹ0A.޸!팒 jdy6vWA1Jw+V9%,BA_L {4Ÿьcdzٽp J;O|lm ojeNS"WSLuOL2wT/À_X 8QXNF@<ƒ}2m=^@$ 7=D%Bs5W.FnOD@, fx`ίy% _u64-_x_Iv5w#y "秙~y`S RF3Lre_b6 9sɂg7_k|l<" 8Ohu%@'DeјF`h_}$it` Pn/@"]H@(:ce{O Ӻ{B ,Rd!  tS0Zyl۶m۶m۶m۶mm3=N*򥪒**}D @g9rW zTֺ=1jʫj)5"Bz9~Oxg˩RJ-A<cp۱@oY (%# IpzLn5sENPD^B[W}Θxg] =W+l-Js=ͯLg&! ͍td!эs .:i/\HPq>9iG|7]wSg@@LۥCw/ά<.;"B6SSjdRsm9g hߟ}' BZUgơn)&pb*DXn@MPd%8kxX@P^N^%F^aF>e}1 LRd"!Tno[%ʈaqE]@uz[d< 8kv|ؖ_ 3.J>ŸA%B$#?KŦ3?eN 2U`ĖZϺAKB[ xY+cd)]4r3ိ]3ɡiK;y*˞Y$Dъc x6%FkW!r( *i, N$9q; k}ଦL0e6J^y֊}v&-XDH \id'n5/O).хGړ--]lASTbPV QPm8,@fIhp_'L|&:%Sc7eKc4f3NfŔsVq)O^& V:ԥjiZd7נ6wf`Pj_ΝWó4{O|YCK;P- e 6r!ڰX~}XLQ0Ǟks@>6BRݛW*?)ftJ2aF!G:f+/~3<YтM Ue₿(b6"{= ڡi#Mނ Me|x_Dc«$Ӏم&%8kqd°af*@1GV=[?ԫ~*n11z|; ed07{c ChG i:5O|I1eaMҸh$.MDb %n٫ƅָCdc,0{Lv#Lj]2]ֆgt_  np=;:xu!9uMTQjD܊ g,#'ںqw?ɬQ j03|XIZd{ ޑhxwّqdžYJ$"km,NU_lkN.`a)TW-tyS1ⷊNmAB@$%n3b<&z] ;`jAKс*EUAJ be*}2?Ced\8#@gbΤy h2[~RNnzy/p#ssKAJuyzIY=ݧE"}fcOY~[/iJB7.y]Ȕ,ŔAh9Եnon-]eGɆתnZ6g̢ϲ>-=ۧLKB ƀo®t"mW;k*ԵHVq^D8KWE)P4>y)&MgKn ąS`c9 2!8 r怢땴=go]VSȐe-0 /W}9[ /Y?mdo@GNya>: vkkYaVL+|ɽ$֩lk>M|"H8u'PԌdu%}+@+ n|ѱIrc BdشJ1\d%R=I$Oi]v뀹ٞTXꎏs"0=2i m qspeGn<ȘJE>Wf )qt,͉2r4m"M܋ՍRl%\ ?<ÈKSQO7$d!8*I|Se8Nk~aR*'Kc@^δǾ|^5-!R!qE!~eUEXJU%->&XC8v|5}q%XQnf^\/_F8d)YuLW} [y*چ҂Q68Oϥ*2]Oc qwJ:=՛JֈK) ⟉;2 ~>a_ݟ>Z5YOSC=|ݬq.lym'L"ݶc9:$?&ܤ{}\:^[F2cev(Ooa = i߸ڦL=x7^X{2\`5k 7QG$4؜*K߳2.`XNpILrJmEcƴc(m@9_s&bݳ}Fx7b%5MJpI%<&^Bsz 55͎b, ƤfYo_ar^s7kqz;w4 Q*p˻,/?z}AHv !0YhBX]!qS$Miم{ ;Lf RItQլ/l܄4vUBU\cJoSWrR) 'ܜY@Xq{[b/îb/ L=zx`IH? 7";J/h?] _}lAyъu}zhoh_2A*Fɼ6N2q}p- j`U-L\T%\.c[Rm%iJ۩hTNo 0lYAkY2(BHnxg!r,廱ꐝi:!oŸҨ}?RzF-\CT*P_W&>;(h>߈dVwhF8kOMT8/X-e~w?TYNϙ>\O xFQ?<qfJglzRTON\^]#Ձ2a%v"d‹ SONlj0vs 0\l4w9컢`Zv|H9GtdGA]0 Alس2I:-Eq)g iO ^^N`\izu}e,tq,35 nN3pxÊ3vȲ_;t3AAR$ "|M9H9nj$l8ܦ¯O7Z )nk<vO r܍?E}9i1 ;VZ%}14=厨jw7MJg@ݪ:4Rk7R*+iS5#K0t I mޓnEb&bAFj3/E^mg߶ ۝uG^=F#m|$=DB~n=6aUUfeJp.QDfLc9b<捷V( :6Pof(ZEqD9Y=wN{<E 7aVѩB%mq"XIS#b?iBǶGY}:qr65gF=K} PH鏖GɁJ?l;tbcX!{ОXf`sjA4\3GJ%nVy} K8:olƐK_,% 3Ժ.Uy͜ 9{83:#YԉCϚ~1)9n\E3:elCPo٪en|a%o3UrU?X;uWABd!p,P_EՄˎ+;gsDP짶ɓ/>{2:Yi_1 _p&˒uwƙhwȍɈz˯=F_E7`5D6,x i%+ƱՋ̼/bVp#h]D@> l9]U|񖡽$Hd_* `"?P J:܍<ç<ۓ '(_YRI0\_6{E` euy̷kvbňAX,lo]j^~Lb^$Aw,{ҢdݣNRs 0g6wrSΤfS]?i`.˱.Z hu=3Uχ.t[=q6\:h5_z7'jn(/ܳӷdK0)!/V3ƠgQL=7#ſ)!r6dj ͏PuF0XмĀFS6Y%A?C7W-\^IM2poI2Tq*Am-Qjo A`p/  HW)F#xQ;j`~aEGWm Ƙ96 $*QCD,RyLpdnyRռ-t<ڭw[HAZm4^[9N90BA}T6:  CAE]K/:gB[pxәbZ?L)%{4f$n\tx=cqmB`Y ThHف<k6jQls [LPLX 濆 (4sӢL\Ճa3D ⩽p\8Q+>]zL _9RRПVV 5έ05 "YL!9?R֑:SlB&csoЖ^jy#\vd7osDiPyGsNU{tQGd|UԛY? A~': eI }֒2%GR=hR*b=N̈́&#kYy#C6KP͝4#D}L6jOr.ѻ硌9&hWMތt ]־0) Bt#CHY :-M}<,1ʍ0m9!a`!͉ЬWҩX_g_& G҃MϾXgQ>t]f`KixD? "_^-RX|э%5Cx R6l&99}- zVȌTKWMLGNMܪ/FmGf1UG.$#2b"[!z\@M .mrZ =K/ٜ QS$\=*]kuwyAmg~эUYZPL_D悟hl˝vjpԳXa'PT;n>-'"^MFJ,y[׶#ݤݫ*8/ i̫v+պHGtjgRF u ocCΒ{TNM|$ʃ;vP(ᣫ)u\=+iB1X T]doe\Xd4GIҟ%l2[!cG '-NIN_`x)/kC[sVfZpZzu[o+DX(ِD ;R+~c4xCm'H,AGdҚ}1˥4a89hEV`P1 ZI,~ yI.x=H9Yc;'~PNHq m]_Ҏ_բ!{"~d cV⌿*a R9X/$%ڧ}3Qe?שY *Qp%tJ+e酣~F̯^YOA^ݲ*ު[Vð!OSLH Q|OvN}5zԊ\F]1g۲TPj㬝pnjGBl !x{}AηlXVzhx :LdBpo dAJ1ʏmw(828ǹэ5MxLOő!֖ķ0`TqO$׭B?m&N7= Io8-*`ISQڇ/[E=6e2}=VN%O9cͨM\ N< `{θdineh.Yz/(Klc/k>(C'. G?ﰤF sK?^& 5/w(Y_jZ9yN"ɷ_O|V`]r?z"kP`]` k0ceo<уǁbܐ !GKgvnh]&7S,4:orqիu$0=O`ߚΥƓK ss"%?[YA{Jh89/j) ߠXg-CΒ| ,ʽxg)`*Pga1#ɚGEGL ~.Jyb 0ػ;HI={Au-Vlh,0!˝0A$/`#HID#am0̀Wո{ke"le}>B&ɷUXa#`Eg+JI6,1`c#td<&T_Ywbe|LDюJJE#U/_0ЄiW?yGŶ*2CkuYN*X6C YOD g鰭 rTi^Y6=r WqFYA8Vc*Ia?yp6& `̄#"#^8k!qk14[l7J͠r@!բ;;^3P\q5JcϪ0zx̪㓂m-D ?\9Օ ʵ~u5gFq9@*ju]\5о;ξ{EC[LJ MDrD?vDbtW5rX!2Dc'ذ_2$c2H(+,H>c>vS`qk\ȴ\Ʀn4,h$ yC/Qḃ4Z][A+-O2yg^ gsZ73[1bV]_a".1ɭ.SLkɽkG2`NG?N06CǽIJ$Xq33Ի*lޏup5}H0 {gEEk8;maѾFɲY!z/ iPO090x N*B ,ׅم->M8N XL1 "xeEAu\j]tj >09EqC“7= :V仮Y<5J&13@nZ獿ytҠmDzy[{Z D=쉿:؅g5?ϗ,0^l1HUq1ZW#ପ JK+| cBg)#VJVɦ>/dE\Vp z}qB_D!1䉢ȡ7$#UZ;b s y_2/*{BϧFaqsQ @ɕ~je/Ânt |{گđV{`8` [5mFLk Cv?s!oO!2wԦ fXtYn+Nt:AL7 ưgL5+dlڇSpkiε\1/J8 @elCh7OluHE,Hݣ;S߫SsOFI[B-I'UKYY*ƢM&sST`YF I`{g M Uh 6mPOW須ȔBy\P祭q..x[ށWDk3|90| ^=VIhֈI/n%kΓFv `Yp֒b)5U6=ʏ뚸T!S_ ph<>TEvlȯBt`Y;2BTtɱ:)"57 cx &,A\x}4hi^&'.##6]wrݖmvpkbp9@ÏnW1匫V=#՚wz)/'BCcX =Ps˒N]< F"F;U_׃T}oRDI-ٖ4cX4pcodrȂ^ Jvb<)eUh,Oek~hS. t{SaL nH.9NImSy4N"+ d^QJ!^k#-28Z[X&v\^lz,OCr 4a[rlɫ=L?敏mXkODPDGr1$79QJTOƧFòqHwj.hrmhn0v6w>Pʾ>/x "4m.ד{!ْ.t-&RZ:FUXƛX^[H(+B>9DaU7oVuUӖR\5Vr{sص70M~ m@Wra.v φ,`'8sL;]eڶ ^qԼ7Vӊ[BsY@QsNfvǦ~2꛼/L_0mq<f>nI)j3*=/ʩB"H 19hTB og&^J6r5E"Jn25Cp1^MIS9|Z8#WH4OL+C}"шSx}<~z/WZWn(ߨ)ͩlLfz \O nPݎ"KMI@NY/R!R~[t,>l`#:-^5v< FsfK ̋Ռ3/2wE *%|t o נ*pA6KM]kS ݶngx R$ 5ei` #>|BqQ(?Ra|k(lFEkRKaVNH~oܫZG{dv,@C@~6nWuPV5A:-UN: $8MЃ_rvXiclb9\P# ߡe%t|km)lpg4t,랑R[H(':v (Px_[lt3HM޹%ۂ&hBnDD&/%v*U Gxf$x+##J^}٥d*oG3.hs;fDF2[b&Y6`-Hᨒ*Jv| ߨFtO lMb6=uQ[z0jO7Xs^cw&bH9v 9yF^hK*uq"Cr13wgݣ LۘarOa)p]csnkpy+gGu'#bg2}([aӝC˚p WW9Bl+ڿ1j/&\p/mߚ5-LNHOp>وbַ5r!IW<wb#önN6xhtDe^_:݌36g̞=T,f 8h<{+`ǑL@q% >BENak] Xe-pMdHϚ ֳIlA~o|:cVHg O~Zސ@3>FU-WȌfaBG2+dw<9/<Jy:,o? 8lM@V3]'iK擜+2׵~EPŵ-HyKȠ{yuBLP=% UbW8aWВ|qKmuT1VJ~8<N?)Pp.ɀvpUIS ysu.o5O '8] kzb2Gwp_T#/HJDMPϫE#_B ;رx.2LϽ^gu?] :lu%!g]I;&l}.m/ YE23 \̶N>&alչ]HO ZVl.&;4 Ä T6e3|>C*WE9_/HLv`˅oh8cUA!vVM!!䟷7; gןQN^'p7<~Sb Z An 4vhwo=BwY/gKr$$=ae}LƳkҹ_r^ nGtwʺPX=kqp nNl nj"%{RDtSšN7(̬q>8me!/V//MUL*pEz" Kװ"SFp QLaފh t Nց_K;wG-qG5P [դoҐ|I+Y]YoR>?➆Ӷrt#ߥ0$5`#y%NwjY֏;~-CP)(yή8AX_$V9L.Yu65OX>u,DR`*rfB/-KņGhBI=;`rL"MR8ۮ. m ^lmFk]]|;Zes9nOF>Ul {P 0œ8M@e35>4Ӟ~a-ۗ^KeؙȂ\(e*3mi*$Ai۲ ݓA1ݳGX:MrL #k-LmjF2lSz^G hs+<yw"Pmp8* ǘO#=s{T8GNYf9tRMZQ]ܻϦ|44 =BIcUdču*`hM[w_)G?]훦>-;"bJyfXW'SYܿ(2b y͈I^')_U}ۿA-}&145M {X8lTYP07 { 5:O&J%ݰw"\Gs2M*bgxy=V|n^-S؞aЗY["Vk1W G=rף>k1Y?֜q&?Yu㋿Oo]ߒMiL'P@l@IZc0ʸbx3T&Af;߃8v <ELdn+g˿B*јȈ`VQXaH"#buN2b =j)AmMV8 !P6 iNytE# ` |ZC@iƂ< T$Z/3d\ίUq4m߈m0ͣrK7^1)m{)\VKQPS <׉El^0yX[!ܶÈQeAq|ƥT58VVxqD 1{I:S[|%-5C0HWUW?8kO w`j|HA&{Pz0Ӛ"ib#..>]гiSr!z#wl~Zv[×D[|q%@UX,4N [$iZS` ‰39!)~DҺ~@!wء݊y{^,Alu|6ӕ\%{RabtV84n#sVrx`+x\E'=v7:-k?W,G㛪x?rGեrqC$$t S4S~וGw}_2 'DA;# ac]igdaMDtm~LEr2.\{1\g [;"2YӴLq\tPݤqC8bL;Mo՛U<,t7S,~QCGz(] G[q˷} 2f!X rR߻B Bߥ<&Hm6yS[AN}I^'UQxTOꒂ2}m7YK-8ш$֏SXt"Y(B^$7@/Bvv^3,!_tyOyم54o(!ԇkyH+QS2pѿ (f(C-UcAlUT|PEplj}kYzN9 `2Mo-5xgt4m]aFT=7#UB>.X\~ݸ!^@myJdyױ:^gb^WOͷ;kғ:WUayQҦ)X̾$GywB,֊nnՊ>$(^ /Jгe׶R6T_wٵS>i=2!k܆pC8niZҝhao}aNCIWϫZ[m@&@> [O"-l[ 1%zᰣZ灋~5”k,Q'`CJvNnkENa{QKg:s7\5<ڞ9Q =jTo.sSzja姕 =O*?Q]]6؍D__m; !5CxLw0{"izL8R~bT!6aFo^vr:xbMP'g?dE}Ricd_OzNaNѴ†(0tT?ibp,x=`.gYW8$jW7Qדre_oRû_67s<ϖ 凉lzI!*k oo){;t1%t7ʰuX"ܑe :_~=x6C^)\qJ КQjӾ m}  h h?pjQ*+(`&J,A&8Y~t`EbYc,BJWPNNJ Vhk{Oγި@͟V>-=]z { װeer[ׄvV[z]O KX Il{U,wJj"Bg~[i2*5 vK}!e :*|#47tq,R \N5JmKL ;ux91R)MG,mq9|-f;FXpo}ɋ6/TA~d*A,vcȦ$gnކ}eHP!|'ϗa%+pμBʨFFH=l,! !k"rY.>#Wa뎠Xtfo K?L0Io G +eČptZ½9aܩ%"iȻ?4xNRLތծSMLGe['ПSr<~Cb4"PzCՀew>E5uvys_ ݰ ;EV!Mkem,(hH&CTR<&rb}{D@:iTMTDc)Wp=cB'7; l];.v^!xE!؃ُߜ1&k-L"G}G|iL rI}nlDN?tPn<5rB Aڸ9jW- an4찡[;߅_q .Sq4.t nH샷|eT<+,j 4ˁٸ_> fS;eH[)(?lWw)H Amz^OM3 !I h^jEE$mcXy^1hkPbaNڦ^0,aY :!R-p}凂SheJg)ROy`@g>Ю]ByA%;hۺ NRZ{l%;UG=_nVn𷂚(w/k(U#15FOf3wL@ʑ@^1&dDܯi'wc9Y[yKA7m8-Z~K0[Cua 3%Ъc\FJ2p_ƞ9(RM!>a_[UBJhyd3p 0WxU"IaY~> ,݆nE)ꓣ1&(wSƛr-s&3L^Gz&'vM.ŗ3@ԙh/۪:T= 31^u'2qEK,")\U h`Q*/)kou$W4X޴kP3`˸ if'iMH.M?==2e_oZ `Mw y?R;ťr-)}^E=o<l!tXBwoB6yn<4,̚m\+`d-7|(ѩAEUO^Lk9qgD r'Q8$AGqκ5I,m-@ea 8J֊w`*lUs{#̎^+)PqPB~.V$1-f Jl%xnR A2Xñs*'S|_ zO;kCߠ$'6d+ ʼՀӨ Ӏ;ʸ5 |tAK'wVJA= O` zW@iXΔʳ [) k_ԌE9?nK\')4&n(AePK_udgX|/,nj\pºZ~{*Pt(70I0&}9t&OH/r&'W\Zfg[GVC{z񗶪ꋔ5BqH`  GZMu$pk'yһ?Gp:x.VA\vQbF1ϟd?>l@ɍ& jo>H96%;Bca5L 9ԏߞ=qgc5Vdjھa Bs. JZ{V?F*gtn/cV0xaw>JӾ2<Ѻ~jcQ ' PK!s/!OgqNrCw/_Nߠ_\&Xp|xhdud;ͷ v-l`{PO7}T&s>d|L)Ag _\38WtDDTͥjpҬtu/k`O`=ZC_SFDQݯUDa_멪![ZBq|,\”)r|*0 ǻB Ȝڭq7sc\F?b3%7(-7QiQ+hX n&̆tLiZP*㣍eP8 lP@ee_֚UpQy>~*Lеc4-wdDY&@d71#?Ueݳu(po4VY":CohU} ±4Xwk1(~3vZHngi WB{EnD_^vb$/+TwxK;QzmڢS⪋e}Yr: >e,=$ɩr. A}~ C k :c-O4-a%k&:S w`ѯat+{Y v=A$NxQU0$MO &wrⲫC=eBӑJQr U@+h*|S#ܘq]^Y/Tpᨇ}} !Ճ٩k;;vM;>;PIOAb3vz_BRUn`KO3y '`D _q<*q&dG aB<'mx(kwԳ9QȜACn|ȿ_^*uSPo.e wARJ 8n;N@ri(42y dWUkq)?*pyif֞:$5_3yn!2VijFT6# MvI*'r fTPbt@p r%$2UdkF̡ZB'*9 r4 q(zwI g1bbeKqo4:(6BEh֊rOhA{ dl.1Hdr/tã/?qnFJib)aq]wr ,_"64vdrzvN%iM* d#4L,QSₒJĪe\˝9E,LVRl9Hx:IPm4vXqKk6fsruXѳǃj֍ɕaYC9#K54'4Pm*Ƕ7cyvA3&aA9(A>mk0)->ĵ6gT#ܡ.p"yAHIh}o8м+AO1n=e!˛uJm zhVE V!2Q>!-Z6eP?$Q=Nl~8x)~W7! xS UY%w0#'lbnS">C󸒐n[Mq8Un߼\~$€4%", ,w_8i"ltA΋StMdaߒ a;iQQFA;!{ D΍}ϳc˱Z#oC*b[Ru!P`٭}MBE%t4לaдxR3Aqp&;!Ig*q(Ԧu&Z@ фs'rob#>! |:VDY,/nE*vQ%1s /JxB_VT֣DɌj}-3 (Kʠ/&Q"f"K.k+"Ř7@}IđT%*ykP u,hќʂhYodTRS9!ag*K:3xͬMzGHD)slFl@4As},X|[iʦ)̖^hO3z$wR88*svh@2Ќ&8 ?q%|EN!$nzfpѻwKWYEd^_O#o0Զ <6y D G` xkd"Z :L۟ʌY%$TiLA+2&-]sp)Mc m7}eŋXt2:Ag9LIH]NLHЍDsEJ0}k,a~jv"|dwU]!a/lI!{oOS]ۚmJpQ=Ngo ߷˱5HS?8:71i;1 ֬)8&r1 K"sKsG08 qЙiZ':@.F3e%r51ikcFMj-c:ZZk{h7QG3A'F9ϝjǾle%@*qy!9:TXޙM0&)MMn7ۨRʍɒ*Zl’&=_M"4D;#pt 'b[$((ҪUcL+jb$mc6(z7&w;Dh12TE{N=䂬fּ"| V}a,g3I VUW!`žUUɢ 5] [AH@pN;h*@N"N~h;[Qd`Rz4W-JȐ]Don(rSy#`G֬M1"ԓͷbglmr$׀.v~W荑FAt*^kC~K_pPl.2 /Agbǂ5!8$WU_F4E&nث=Wюϡ?MB.g |4N9k1l1\֔b,/AX}OޡM*aB vHb%?M+\^̛f^Ȗ6.$ ,2W[*4˥d叒O^:"4`:yЂ/Ҟ%sm,:JM(_Tv 0NeTzV~Pwvģ {#^ioʊE79j4] @5-e?ك|i*L.=Øv֙UC/2 Sv|rYu>F6CvAM!DOTL,aS5׆h&,}f?~$w봓&M:w:x;.+-CG.V\hY7iFqiPzF)Hzd?ڣ3GF,Lq~;vNc[Mhė U|;U*]͂/aP)r8i{=gdKduM=B2tH3CB#NC%,vn| 4|/"VE:;7[Agt-( Ӏ:&o/R?zùXȩ[d*ci~O9ii2XC2 +2X7rU0k w/V]H&/ GF~ }VT0_3wRsK(bp?z,LLDCCqPb,"rlD;baҼƌ6wuS_͛ϹG(nbHhy3KH[?]Ot׎-ӞLJ{$=3#|{aW0h(C0Qܳ!P nU\d)-ZtB[sՕbN Z=O4ҷsڞua7rp#ASl@Ux>/-іޖ&(p$Ry,,4KHߓb>&{.s]=xemM'?f' c!YޗPYLFbӰh{"sRw>B}kBߺ;݊jKY5-^E͜ ۖd ځ˧p^ag3tĈ``B_>W6BdY}:ZIV^Et2G?}[Pll߃6wϡ|t <t$N]XC; -jXW*p#X߫ўrOێ=fml.ֲ[7zWz&'JkY|<(pѤ:s_+Ɣi#uE!;Wd3|I͢]iv #:b>}+ 7-vVȝ$Rַ! 'ֽxˋgWT ~nRaP c vL*)Rm5*_TuD@X4+-\MI߾62gM-A$Vpx>~1g6N*{jȊ ?Q-U#94{y ܳ9{i #wfv:?Q |FdGq *}SNU HGi"Ȥԭxzj4ּzv˿T<BE5'~15 T,R1V=qR3o՞pc< GRʚ,Rjx ԩ " :FzU2}= -.nʰ {yjr"*$, c 5BeapZ!<&i&Y|t< ZnƱy*Hح7ZX9&d' _o}83[} FRvB~SD!c%5/Y5?{UBuOVXiox״D /-[ضT킽"; 䭱p{"֮L>}`6@l@&\Ok:[ҸY^EG;!{sX}6ko헸")9A ?$ D\0d>?Tk3M`MF*K\Hc Zdf2j-+a Yϡ6.&Jo0M@@tÆb(j.WҳR}A0P_W8EU2F׋ئ k~܎;RӸ#{EH_Z;JJ EJ +1vncecėcHPس1OxAzZ]zj`qU>u냢K^fvqe$&OպN|R=5v KL#ZlB$ b* 5 6~^w2#&D^vpeI%DICζc4]aжlTrjo d{T rdŹp73ߒؙUq"|=b ?%E)66".^ܺ գhB ^F;IwuDoߥHqAh7'J0fɐLE1H+̋)¿\Y M{f@עI%z%~ a#r\0-Vt.ۀY2C2n}:.+_",YZ⠅t.u y6Q,=wJjIix0avT}ꎻ{U8n9AӀ5/nn07ZK7"y[n24ޏ@tgS'ɨ%9ՠrEڊήE'e1D g! Zn1OhdXW,Ya886em;g,FF@8YK]N` OtD&'OnF$@bϨ˰ң$=Ia(Rq*ѳS'p ?mWt>#`!hVA<$lC`冂57j܅r}3 R}D-ѣ@93SdL޳YK)b4 )[JQ1s9޺:[% fg JJ-J,~oO0-E3\ 3]pR/q6>(J%y{q TjRlMa۠jDgQP) J}#EMnOmuiۚ6ˋRwgc&<,nm&Wz$BvWh6\Nu]OU6p\>X7*5J4\Đ< twj^cG%Az5LuZ:VDէq:#2Gv z*mJ /=at]ǣXf ؁ƢsRMQz]w٘ +7M)kt[9Tov7.j}(& O楳 j9u*=rkn㮛2 p`EK?-Uэ"fZz=Lu/H@Uρ\AFILUgǭⱚB[4G+S|zd+"牳x6׺Qk ],+(^C nf*(d)3(_szz`X茧b7.3ƽ,{_A2 1)/FyAH 4N] Iu>6Eroz兩t;5} !7c{j?O䇷O#f 9vGʓ d&ƞcd'`2 쭎ɲҔ`J-:] Œ{Rd^L$t( [@ \րa|BvYGa/}o)`4Ac#l~o-lZqרb,*}ӦRTKMq?ŹɗHE 8/0D%70yb*]y)y 5K_|.gn Ca"t"?P c}yU {Gu/1 _xlxe, p1Nx=, OWloc})d (mocK}TZ/j"NS~خN|$a!%$2SϨޯ#Z0vhh%gln<:HԀaF0H-x>, 6rzκA>,2ux`qn/+YޥI-1 UmT}#_+'4qQ#cRXDU(,Yl 90kRE\aowA9n\{I , DCNL`EMiڢxS5 PQQ7Y([6~L/P!t XdJ/[45>S8B]$My^s[;.eFk]΃CFұ% cZU[f)a֡l^(~w/#|<"F sڑKkǘfߕqq /1V&Ҽm۶m۶m۶m۶ڶ~3?&;N:uT%ӝUt)`|,{Sb HYT̥ -4"}^]wx'ddpIĉH\lpV/ߗy]G??A6y5GEsJ=/¤`Aܨ+l1/ӭ$pI i5Ag>36bK1F}`Bߺһ"+m VdݽĚ2v B>q OA!~˖m%'8Y"<JTA" !“AIʞM%>C؏Irq򠂴;t!ȝ8 Lx{Qacd&9b(i}sSAqvVU־ ƫ'=qxPDKzH3hTgԍ$Q!˝}+؞PQJ9b(v2^p<.FSN*Dg7ΧH#F99n nȉ1!aE?FHXi`N3R6-W:yءT=VspThԲ18sUDqu.$ׯg%Z ,@dQtY0;ޓAB č-,j>ѵ-F#pɧ%ZY~#t/)ӛZ7#FL.|$}ʙ H}x#˚랚U8șMsŒ-x9V7p0 e<p d< AR;~W)b57 F#(z1)r*"$ܕ@fG5NBl،?k& s|L(ٴntiE`YvC,4_ Q؋K=*d.!6cٲ-G|O 9v$؝kU}A|Rb&0 %m m&3R'N'W !re'IkA~Khfؿz.Q6Yp9Q~_2ʶ{]31 70Vj:Z]$JL217]8#m"qT>hv+-[ !mI5L.FXP(θ#N]a$k(ۮ8LngQDNP._YW,H_ؘL043KASr wlBq" !)ogF@g-6f4G+3M#3AaR4nֶ R{Nx*Q]aä/fF-04bH)Q6$6sQb2Ї+%܃E"}U::0$n9vMɹLkZzc&\uEܾ@xXOX^;MF9Y/T@vr9:"ŠR2s"l\x=he ۏE='m3AX (E&? *R:TN%mD{: I-^;b *fcw":&RxJW#gHӤZH;VRΈ/srkShw10ɾUNUC!XYSY?<~F9feK2Ù/] rN_ef0#,rr 2oF\#Xy`xmIjzr`z?^-BtϠCmŭΖ>C w,&2!^皻Ҙ}ӡ*)XYo՟+8L4I=ka: -FSӭ~5{BIUnOGt.q :@)ߐzytp~ݍI;tY-Rg:>2.Z }ZS_~T.۷77"(?Ӥ^=ڞ]rJ-Je4H@L|_@F MN=V^ll<}}bҵa VLWYPO9_Th5eV+&PmTI/씮-2'xE:d ׮7G= Bͮ 'zEKK%\>[l$ļ98T;~R= >i[(.2O3R^G}hMM撦y;my1\\ QN>n*DWkLv@sIըh)wtIxr_J KI𨦲J=ps H`L S#Mf?ݕ{6ySv299mf{_`eh3uOu}3R:2)2{|(\`>Gm=m5VM]4qk Ʌkxmb)AS\z N8(\=^nXrcce?̲?vQ"R dj3)v.ae/4 0Mҫj)Ot {`-,g!ՌHDD$qv4A/Ug=,rx) 7ү8pkC+o{lU$9"<izWsOc/6wا#Y>IڱHs[!!j5M,iSU! Q~Kөkg\\MG딩f F P?7[dKCu௪=`2q{'-ZgzBO*N 0*> ``MVln'/oM3ٙw~?~[ ЁEs+4J(6 l^Zwq0_hVC \ٌ᳑n(R`Ai"Zh{z"1ϲ5[&۞<+E!k4> >JC2,6Iމ0L~z0(_$Z竍oJDφy`cا;&ͫ8v{,*Ň(BcGvˮK =j's6De3i c0Ӿ mgN-@co:f;Q)!>r4`r6SѓlfGM:BI:Qgt }Azn@Ju^,WVF O؃fU,AM0MУ}=-XBYi%kg)8gOv)ghlM㑩xHB:sƔv|f/0\1N*;@omw[5SX*<=o4D: iC_g1kiPI̽4cgHwjq+'Gk1UV-"NXx,67Ʃ{lhE i!^L:m$s+Q>c=3]yDzSϺ"Dp)gdE/LOy1C(ϏfG&ۿ{ց׺zԇ$O^ͭy[$7['!b:a!p!g>7:1ny;pI>$QbM{AVQDLψdAq8i[l*ɱY7oRA[vr:TN?7nv!֚!*$ʢd. WEAW[u-<?$fbXEhȿOpC؎hEy;6_]Μ+Q%w@BEUgr S漷`fe;⅒9­tҢ@|Q{>^OLJ`٤W;\aWץZ\el?UAT(=1-| "3oyr}lv:=1jqC[v["W7V( x* )00}Qi7/i!cQm+ԁGITY9oSZc[v^ў I*P,Zd$K<2_&^`]R}q' *!vhod ,~AB?vǬDu%oTcĹ<$,X_ z'v}cl*i +;c< {^=/H!K][kYh=1$XR܉=?>Π3< g/FezMضs`9'rS!*:?;p% )mX"oO鍇0X4 ̟7IRֿg2``8^B/C+!Nq|7e`❇s6"X-DEhBuYQA^l6GAJVZ7F ]yUy_9FAQH4缢 {9U \4}\qdɑe"[mH?5"ae_Cҵلz:i.R2FXtsز/XJ1O^)9B_ɝ)϶P]{R$p_|`I6z< !xcb2-IR2퍆UlUu &cp.ڂ T#L(3y LC޷Q0aj iVpu-lzi"`#4]m! =FYS,RX t %gM 6OCW.BcqL2RcƑ R\ NjHa+o ≿qOɲ&-w dG-PJդq ,8W9jB.إ%KJ#KxW\@3'ZCѓ%'qAM;|!!1~} svaHjwfξH6oti٠0";<+vBj[ X7 ctٕ@m=^{9&.ͅֆ? l=@EȘ5gw_xp<+KwNF@lX6qbƖ `<Ͻ_j5Tk*i$w{u 7U;ק]Լc v1g O j2+ݫh`+ ˆç/CZLD| *m p^160"( rsɘ} Uּʓ` ,ǤcVrx jǬSpF~ z.59fJkF\`8G;dQBk1Qe ݧ hnQoKL'!v6\`EI{'ⱗo_0xr#Cs4DЙdㇼpi1ؔUrGؿ)aB RY60(@u0^Ȑq!ZJiq.N MmL4j$'mۖ7g2mrUN[8Mw.} \\捇tTg(i~;?& '<8csL,7F?x{&k194i'o\%{74Zql֦Da;?BVŁV"v2) u$G n{aMӸ^ {Fɋ7/Z΄kI#a+D`Tm5楰7U%p(QT{v딍#h JL *㌡rv/,D1,-+iWODdjlci8LivίE0I2Zϳgr7JL(x3Z\ܰ^itHu'y5Ó-RU&./fic*blaMitlhZ.+n$"^M%8..t&.z Cq,52W?ҲeK_+gs],Ysw8kOY5S淡fh;]7u1?%Ŕ?fG7vH㢥6 )LHifbZ@`n1G,NtNR,2HuatYUB БƗ~5LM8x ffIh5  (RFBQr+2̞\hr!D&"F-I̔P8b%ejݓޓ, zG-۶'fG5f)q8Ѷ :U~s]8m- P_MHjRF*: ޒ0[Xă msv-cw쀍a/e67wGGDzUAL+ q !NqE2V؟T9e7plEI9,_-*yR%G%Agm%v~ {^S?em5*~X+- Yܸovt:uq#S>HI7ʯ9R׺tQ%^i ۨg1>˚,WnnsD3a, !0G7rG 6Ӊ蛷]﬩pR~Nd#[5{,Ac_)@䥘X6-)?僠nN5`((3ʄ ,9W!wwQJX%IN"Cq ^-FPoXd!F$+"oٛ.g8gfjcrן~_6 NۭCgVgS|[2 9'X(6i `ם@S3>mBd.` z(E^S'Iz^)c*pHi&'Փ΍ofpcȤ%T2|Ε5 c*!2=\7.LJti 7'>Ѵ4q/V7nvhJp-DsJ S"#Z.MG=ݐT冴&M8fTwH{p,x;Wy~ִ 02ԛc *%Կ9: 5jx`mGm{9;yvsؓyyD4␙gp[d71^ _)l9hThZK ~GOl<= t? *qݓG+9Wo:cH*Z#&w{/U:4`&P1}uk+dd>UNM uV\.ƹLzt+d0-_t:w踒pu}#p{m@Iۡ?j6$-j3Ufsy ~C.r!`{A 1\ 57cWѽHZ =BuNpl⪗q[(e:FX;R8pllxcp֬5o FA`sD.f~(J# ޵W* ѬHEoFjy3lM&gڋt!gjBdl\Axۖ'Eyo Nk^Tع;0(Y{Ex h߂%!/Yr83T;_]%~D!5uCphPRiÔHQt 3=&G1F('+CAS 6E+)]z5{xOi,IZL`{4MgYY! >"q6/ o$EgN!Z@gpuK?a.՛)HR=A ;q]xuuVVʄ {$D /.O=aW\t.cHF^W;'΁dpq=X.int+"^v@2zicN>WP$Ԛ%%q<-8&zy5:q=]gi\sř.ZZJG8Ԭ[7{Һ9/;xJ*cX +y"V>F}XVψ'%K,5Q !Y*ҪZ)p w?^(k2hlҺa|>e2s7JlH'>h4:[eOֲwFj= Ԗ;UH4e+=EwHʯ9IJOJfrdήh.{MrVC.w_œW74&2yOvSIjbUlDT@{wKye K}.nwF::u z6Y Iں.S;-ڄVQWeKu+Sp`&Dm26j7VrXBQ@hAвUdHDߝ;3f2.7LYEF ʼn\`&M f vɅԜ,o;*TSC!?ZN'*n|!ЉmQ`i|kC{bY9̩ڟLpx+F0N!{w'w[eo%*c0.CrC.B|HؖD5pS(W15s ̤8[fR'=nW?kU$gH䬻qe̤Gꔅ?ijAoL/CeML9  K2#L{1 J{uBV8.O+y}>fIGk_kɱh :HӋئ*xH,Snߝ 눤9*5z#='W ErӷClF/R*"_K7U̿\ΕSkeϝcb p{L+4fNX/!I 3YbwHߋGCmZvSn,Q↶uYrPr(B|i \uZ{9˧KЯ$iESi>Hsg@r i!.^{m|TU,Cb]m  $\c%!BË=`6U@;ǯrCz~MLZ _m6X3@#w%:f8xkGZYAHv`"ؐx(4E{X-睇e*œs?Nkax P2rJt oKՌ?tI5ShEI?hlYn]E@[rƟ$s--!#Fo.~HeQ .Gp-GP]! @U%fB"TLUZ t2k%"} ]Q>P0"\5 GhN9 %VON3 1=O{%eyj% ׀xڱpF@zH6mxRngÈH/b 59_ y#Vpr ihmP?9#GNK$;b"&k CpnÊ۫ PG,0SA lbsrG8Ne$_IT!usU)} f\#i9ABN>6Nao}lL(P ޜʘ{& ^[U':\|MU 0BQ.2tz̭JACEq2˻xO88,R 7p)"l@p݀W~EsBh!HtE\VQUr k{;#Ff称^ >Fr%]Je)R:rM|6 IîrDo{_jjnC%8jSŶtH{FBñ$ c0_"W'C~U.;`g<N씟MΙA'O2ھM8f}gƔ3|H.K{gƣU!7&#]. }ƞskݠyvר[ }۰mO0PV/2Z&"wċY*Íu=(!ZtU=V[nbF ?~XjRhzъ&C++%/p7 lOMEV8AAʒJBu!*1c.`'T}Q<˗tu&5rH+s]-Xv!HZ@Ĩ!|z>tWD5KXȷ A{'mӿ|hHd8QsCy 垝 ' ȸX!M!~91>b/M _!4V\gh~ڬ~60͇%4ʝ!. Tlm%h|Jj9H~#OS hkrV{ Ӏ;|QfpE*O11dZP;Ώ (= nK85̩$Q%eqc#s7͓m1Sn3|B l:lӥp؂9tʁ ctYIp`"-jZʜxqE}/?{``=8҆{(śT[aJ&/ߓ ؍4'yuۧWusē(@AkbREGM-Ƨl!_T5Us2d+ꤏf⸄bZ05Gaße ?!BM&рO68¹LZ颀S`Jqb5fqn99gbJ yy=!6b+2sK~<R#{%#)0S$J;B֞pڣ˘r?l&[E6pgxe=1\-HR㶖/q/\<FA3zT#i|[رM+q Te}!>ak{1^6c;,2C[J+ /lre:烌n- f Mе)md+51k?XX60=϶Bf*7 XjRf>rroV}7h;2j?Ju!aقe2j85Ю?~dpi#ӢpoYr,xyLg" EozV_̬# jPW?#ÔnLzz`2"6>/,wGc_ V{OK ;/ZکtIXm9yoFj2}Vbۺ&^PŁ|IlWWH,Uvg^ X^E8S|?2ZycԞtܓ/pj#'W 4θ4_v2G$]M)$krٷ_O BUp(Z'}(6T"C9N/yd9C)?J8iwJrθKyYBx蝳2ӂҫx ]%Bmφ|&RXؑZt_KAGj;qEH΀e :%Q^. #G#/r{iвDN]emܨ]0훁͍,NbPC,榃PB\)3O/7Hf~-Ȋ5r,G%UVu޲'P ~bbGLfЈ{·kv龎֛$$V|r0ҏ?ؖ Vg3vpU8]|?b;5e?>55~( [v?%uT; r-` dڥC8b+GCz-V9>.xĪ]V64Wb3 }\zd[cۇXg~xW3%BBk]_OtH7`l-¤O&}[VpoeyySO>>ƷJ@Dhl A CXB%U}\uSii%1eL;6/a  JC%@*.,9[@M8PbZ-Agwr%!`7'B봛!qi;~ENiu#rOX;Vm%oB A|3˯4 PB8|{[,身7{fI3p9=}|d7O^$=:;t{Bhb#64j 'բMSTZ}%nqh3qaGYHz ƱwmQKj}UǍ>4يU/))St-w*Y$)lFmmJp¾aks%Ms#(E_th|~F^bM{ $X3!@):g>?IvQv="X%-U4b赞[5z@結7N@yQCBm7T3pIx:E 3i^_{p$'PXXIS([<g92]>s@m0񟷭Ue1'y^լ#9A~֜t.5\Z;)QْM6v UB+}|P8MOa:[nrcgP3;KMS?w<>Hքp?b.:bğwYU[dd>WP޽8ݩFG4Mݳ˕vn_ebCg܄QO\Qm"y#NA#L" h, mJ[#(ۖa+2yL-:PQ+:[QJ¶`я ySJ5aGsk$UbT;U}(EV=ixVR^QfwJt«>iD%Ǚ-  ];!( |\Wƚt17*{ڟ Hmnr`PH-ԪLpnKZY-љ+l!׵ui4}R:'\Lʁ߼m "Ӣ5f=j'oqXOˉ^"îsdV`WPZO#"UↅPT A| "?;o|x^X#>#й!$"@}"] Nmǥ-h";$#9LĐêT鼔!>Ɔ! q=x@BXE8g@#8-:[GX 46Mve@#!_ʄ|ic>ђ Z nxR;KlM:Mx?2Uq<)|m.Eb guInwb]N^;w: 0ymJw:o#m%HՓuOGزTp?g BgaOEY.5ͨÎBJS+xtK_+~ &؋a+^iXi3eZ[H% ys>x !6I6Ҥ*v[q= ba5=c5Y'mWπ ueg>4<͟XKLsyVƁ*`B{|b G:(b)F:AE^}{2J/8ފjIF6h(Ť΂L4cLwzܰQ~\ĥ 韽ȝSC ݭ< ,8wfC~{LϪy)rKՁH9i#d c/Ж0u5` RAK2(0Gۗԝrf~ݛ cf /tCrqLnj̣qJYap%R 0ZSiѪ'bn6"b%4?pW80e̬zEvSGo0 ےc+o_H^Eƞ`O ,Vn3Z}"J$:p5,&ωR0~7>5FS{uAk;@s󁺈W}VGKP ms Q͖w9োh!5ɖ*1*4o@BYB! s$߽yX <M檱ۛ3Ʈ h}h,Xh?e№ s[8}6o5`@!&LdjN㜧J XU3z GxC{NS(PLE5]ݵ}?/Բ=eԕ 'u6H$rb*L :-fܩ3E퍸VH#G,Qu?zxRo)Ff甬w81qi>%d|Nq`TI=+2oxw':< (H}jEEk6k~k?ɼuY<Z.79bs|a-#fw*4;6)P}ax>F(21n{X61pHBfOVqUQ}QWNOpG鍉ٌWE|5X\x>0Qj=S:\w,VOVZp\]ˀɌ>+itjl:gHq¹:@9 bjw&T^эF+P e~{ҺwpEqgFFMiNe{e5+O_nkzRp\v< (?]JemHBRvjz ؕۢc)es1O(hj Q0o,3[pGe^fyK-`U),ᣫe SU YrobZ{v;&^AX,eL+c ܼlk҈/D c[C`32}Pf/ZZB tp: F~+^=:'cqkqgǽʮSL RMG]ցl Ǯzui xoz{JK|C`u^kP`7-+QsU\kKek> cY@D F9qS@B5 %b۟Al.5Dt&$5 |I.icWWхjHs| o_FIK3B[RE ٔK<;Mog243\0~B5_ K¬(w[[yp>=?q;(8K0F٪C7^ք4 _ȼ־d[=uUk|1!을i֬iawrGZ}k L FE ୹ĕs OiuuuEO{ Rw@- ~5fdNy=]dIg*g4cA<[;d: +mAx,"'0Vo<&v {]so(kk"#Gg|LMrHVc N ։M|GB =[xB1U.hhBf4+P :]ɐ'{O}P*fySh]h\g3nⵚ7:I[2T^+*nA[Fe߫o̫dz)I:|C7 :Ì,]jh @ۧwT2ApIsIsJ2JČ_Vg͛C%sa}<}b?jht_=# &MyDV"jJ~^e/dLśwٔfzm8Ke"^Љg+KL6N. 8J> -0aC8uiqgmB(1.Qb]tZ1cC| %D|RՒ d{4M .g퇄 t1A00y<&d, 7PG ,r(ziDFg[.,TEá EIj 9 X%١MH^=̇6*pⷝ?] W vkaLC{ھK|A8Xk')%AלEĦf. ufz5F]+}Jgp8WօŠY#`^sS,vsbf!t3T)7 g-"*-umFy`f-.)n+ E|  )zyo*`R+L@EW_%5SbzP VFceopՍh'X >ݹ?2NhY; Whފ&}3 HZɚʲyN*00[QθEoU_2[kj(1W<9oo2A)ƒ88ѩ˵A#,RZsHc!&<הj܀~ϨS3ziq>gD_~'.6>B Ly˕eRUm}v尔Hwt>G.Lu{ey $5؈bi@C:j.]poE[~$ϭe T%a"Hs 2R^>s'pYˬ/Նx4SwW Q+fn뾗X((re5do3]|<؁*ۧq{2bglfS؃,/ kؾ"X*E䢎D)sL%UQy lH;P9X$aJݖU %}|`쉜9r6ˡj:׊2ޕx6壡Q lv :M&+'n7MgtM 1KUY͞ w̍F@[BI`o#euDDwեxjZML`-6nT0c^| j^8Y*)f6ƾ/Ru@d7@y9RbG_tF[Cmr)I|  %XfO^ $~su?B8u!D+-!sWFv~5׼d-M`j_Aci{|UQpw/$5 s1\v` E2bOAxOK)nʳlJvs&#ٺT=;Ndœ #cb!ק|^AqVRacuOV3JVE5nL%W(DshغrO9j4=im݉Sjͳ40ú:Z͒EWSkF|L:Iyˆw'j3>yie-`lUt/a342ez=ǐepݼ8MQ6)x4ۡ ı(ze"cu[!=[Udo@FN@*I5tv֕SN`WK9Wmhѿ\X /HHv*c+,KXL;4L>vP]x %z9ϼ!p~#iFliMۏ/\IiLS?xmbZl叢XDžnN,djPGR F* 3䌀0.Š]"K#rڊ `}_ch03a ǢHXPUMR.1@D8doni.cƘڂ r";L"8B7r}*UkӽZh͈F$4ό~¢B")~mGҿյGJ՘)f  {.Ԭ0y(E1 >\{DZyÇM_(UE1Cylԭ3 f* P?Nd[pQi}k>+|iK@ 3edeƿ!8vPQ|eq O:J޹D8DGJvٝoT'˭Ց:ҼJ/|j] m wֹ"< ˋH6Mb%9ZϻbVDvs V!EhxTe(cݿ1ئZ-6ˮuؘ]-H#G Y{6D巀 LxMZ'qHMNp4גESm~ vJz^2m2a2~j|i eJf1&- }G:\\f:RVuw8t_+.r ߋX:hGՙżBN9Uzp%7V +?foW~"Paeꊶnw':jc)P :c*!WLc EQ 3|V;a#oM8>!+rc~'J#EsE Mt6GqMNCd0M@'s9z/޾!Q /˰=7~c݀24),̷m?Ie|g(?LdKeGv)Wu_[L~{K aWӀEo,#QOb,ːᰌ qMNj?HSJ֌TUh_pNEk@㗌Ssx|ֈTXDi0PbY^5G~cw. c9RzjuJt_G%WjB[۳ՅϨL{'pFj9l*Sؓ]P-+cזc&dfxPX_Vxe( /HbC f!.sV2V:E@JQUɨoE޶[[W )CXQ!;eZ~urQh[2gj-}ML͉Mi=wDdiC@ȁTom7䘆W6’c~K^侷}"pU S d3SHG68$q83t3\6S,_G ;ILfg h-YчsZ=gWF5b7F` ea dV _Ѭkr 9 3\wŢ3G~nЇw]:a* Ib~9HX)K&fC N/}@_O D-]vѥsҦbBx}fvjg<*Kݚ|ݝ=Q_ bKrXRV8/ Sė_) ț;gjm؇y,,nJ^k.k#e!mD9@C0Dh'1ؗCuЌ#RALjJ%ވ~N{A:h1 &G'π)Vf!vɷ%+b~ '1qXkeA['o̐ <%Ilw? sN2gio;n7߷.Kt S =Xƹ]UVSaavZ!Lٯ*xԄz}t='i1 VȵwPZfsݭewF.yvtVp6NS?*$җgE{Yr%U>!S^QF]Q\ Z8RBtwfoxJSXu>׻VtLrTSHWh\f(Bܤ(̙Qt;_=I!pe0 p F .]_*&C ?{aA8f‡#۫XT|`CpH1D3yBz39JBr|0%? 56?R,g, UmV_C"Sh,Hxv>roʯ$|\pWV<ڳ1U=;t |ń мkQ-S;Z8qń*\c~|<y OSФ+v#[$yU]^މ3<0m W]t4/p̺iy6(c!aNN;u X6XXshpާkyo5um K._s7љ  0PM~ [5gbضc w b%q‹霦%iz:Gf0]ZAƟ,_TZ:[Pp``XA磼sLώu?pQ"/Z&}` پ +vx-5dmYn`!#A@t=@7/Z_U{BpB ϦF1DZ5ؑ;V5YgO!#s8."bptt|eÞP@rptQ>6>S[k~Yl#s.o"eV.0n܄!!T!uVDz~G=Pk)N]ɵkqiށ*lM Zۨv[x e͙"/AhǒCtNФ[I9 sOV42=TZm~- H]imM.AL$gv;҅GA'6ILDdlzxbVL$`Nزť<]GnRxDݾS\;b< Aaku \vj.IhEW` a`B,V"VF.2X̬):gaҷJLV5P(e͙F£M j[Z%6CGs#=TwnDpO 2BQY}=1pjSyH?etHΓ]1 DsGZ v|i#D\Iiq'oI=#"euc"@JE0|)EEπ-נP] zqS!,.뜰pGO7 Y=OWj\ tD#-2/i J祯nղ)_!Uqb,K(s흾ryh9\"WШ-9yes fᰯ}ǕpGj2?Ȉér=&)aiH(^pe‰Oaä p^o"S$-LI7ޑdMˍ2 !/W rn{묖`XU(yWҬ nuk*ڼH<-+͠ BipK7١؞H:'WQC%U6u}6:&;a[}#G Ic"}fyw+W*dGl(Vz ߧJ3ԺAߗn]%o58ړ3QSx0 Йp>XLTIB=8e눵:X4Bކsn;12-XE)u> $$0)0ԛFi~9wj%p 5(2M a1&"Nf g/VkaaG1GYR}1a4$^ruY[)ƼrK"tФJ.Q[]3VcA(DTDsHz#jA ;SY=בu!kfmj;Brd%:_M7$e3bRq #cbMfV6MaR}`,pE{#it.aQ\嘋c@f@6I鈓 (+r? !qK[L5޽[U\*$CԞz-xUI+@oXo^ _x?c([#Ժ`Ad$Pf]*g%Az4Lf 5u^釔19o* FO9t4oShȗ,P,^Ģѩ?&ȅ<= ?ajhHr4ErbJ6Fn -RB[c` S#ˮ )5~a;O*{S|֌lSBq<[|K] Xu׮Ez:`ѹO>Pl)|gfM)U,5I]"1k.^Ҙs8dP,H:!'r1*H(0(+I[4Dl`Tm9Ҫ_C屪%:tSH0R9 K̗jÊ>>e`F߹c o+x/!e ا* {^VU%.$w!S'xlF!Q9Tb\9t;i~oEJM=\Ix3+!CvU!ȁN獀[6ňPO*[4ߊvS-jsx\Z\^}_V7Fǫ^z/B| Cͻ4 y KdĞSM\U9UdTc_ BD;>tPg4 1Pb;Ŷ ƀpj[Sb]=Ax6 /#Y5AΉ3-%+?\ةEHAw C,C˫Y:}QWt%U,iӂ}`OZrتɋr֙OH4px1oM{%#[FL8/?\-o\,]V?JR>{t"A J{| (}4|Qij*CH'$;]ZcfPb[AY &r/r*hz])+Bn!z 1? w6pD֢d 3 c^۵sZgZW `6LUg ?4ɟb? SEj0M\^ 2ښn|K<͇s^ۯNwBN?:6N:ml|~{XQrgDǥZ[B=BӖr? N葱h$;K39Mع:}䎁|n5_;4T\ŠW`Tt5 *GgDjeq@`I,a4 `ʼ6 ֒>ß#md 8 Xhع!uWr[|84XlJѵ 4#hOԿHu c!llb17Ҏ?eܦ`X"W/v2gd/+`Uǯ!'BXw ߿0"dYQ~MK.Ȋ\o33 E]n@AT˱9jh_J3<L5of>骊C @ ,m#mv=e\;JڶL{2M*eϠz,HdzgPA9܇]f{ģ$eèzFIrφpB}+hRT si\szԶ\pwjF yn2΍WW9*؎^hن<}fH)k{֝X/˽O}U!HD[z[">p‘H}J+{/m0^#1S~O nxuFKQa5)iT6 wQfy_Ce1ͻO^>v>DhHQ7 % [l|gt+-E :gִ*z5s&n[c)k.{ K#U~zT }Ux׎^eh h&Yy-h;f[f0YlC}b>{b ,bU@FӍg[T;waEv l~6hb^L`u~^G{=M{l;ٲ/[" Gl&ZLh]M2(e]8íOGx-S2;ek |},T_`q'5zb{wh%hmC,A,[,ܴY wO`H:w[&$Ы:P[-/]Q=v3IA-d)1MpxtXHר\~6Sfa1Әs5%}:rʜ7i8xPT[%jǴIW;  + \D<^WǛ`[Y/p~SX=Ƨ)vD3 /3(M9c;5jW5#e͋T#PgDZ ۹/RcV M#x֜ԔWX.TSfJtO[īƑKͼU{6Í.DJ5v+kN۳H'C^7P.T2\Uhނ@?*F2txbS${(ɉĒ04i.zAW_|8jܾ;hfIiyz&>j穘#aha嘐a0Br~= jPoI0Kڽ~ L|}@_,dup:Vz ac?Zcs gǿ^5t??l=bR 0S( 6 ;X3YT:e^&s=ծC^lI_gy\v͡w_`tMdsB~9^U_/bz(7{s;HMB#|iuD+++ŏ*[7|۹A_!BaYW&mGEdku驑^kUoOՊ^.zŕY< V;J-|/3]ėkc koP,c|϶@7l4 xeɌգ{MÕ2&J% o^x8ێ="XTvC۲QYjfcc(A'"zĻ k ;_|)닰d okerʻP%QDso(_*e'5R;VDELּ ==pDk /ޔJ m`Dn{?YөGM;'EXxDY@M0+5mz̺="Smok2,/rH1*ve4zrxhh~X6=&FܛH_r$* ّk^!|s9YivAH3?U\uhp cPְ{(s9C(( [`- {7b?Rh0YIhnXW{ξxF#م.ի)7+X9t"c`IJ5FENQTTZt fc. 7LxэFo]P}ݜv80j"\|@,XO4<1z2*RԩB˭׎n.$ed-eSWE7pkY0ս "esFW=r=Kv$i~0aªWIڋ6~j YnL=0'\FG*tdlz 6uJ-  ,.ΠRb<!!a3ڋ N޸shɠ+Ǥpb#l&;Fv2&?տuʃz꽗W_wPq/܌ ;E<{> M3ag;)OZқ,/{|6$xVN:&JS㮃A+-z0t/ 3I!{1С$`lFbR'[s[NE 9 wgP!u[Owi$s_j!|p_E47Ǫ%cOK2RUx,5b '_"ONl6p.z^?؛k|gtAD\5/q~^1- q}T B/aGUi*&; _i$d !$kƘ;=+(jo>o\ųٲԾA%Gl-E;%vR k;HL8M*a:=LA?r{hk}hM B i/Sg #+DHa9j'Q.5ǹ)>dz&Ā|~m˧ 9B]=IN""Ba"f!́Y/ {+S ʑ't߫LOd!$7UƷrf(jbL+HďI"·LjBqBٲ)x/fz9} CP"{>GjY'gx3C:?llde:mMRq}Z}R T.@n ]E>mcJkg8k\W"JAc=Jl , mj; ܡ)Vz߂n?௑4ʟ&i2$Ҷ%۪qa\,3ZrL0QeS84[JXtL ޱO$١\1gkUa"vWM>>Vkϊ|= J$ ןǧ`F(Ơ` -&6ԑ:ja%&ACYPPr%($@8)c`s>m~OnI2Pi/ʑG9sՔ_`~R%qөNVV͕W3)˄qc$P]ms{LP'#FԾH%2^YK`Lc̹DlL}N8/iҐ%ftT7((eL#>{Kg5h=rau@Ξt%-\SƼ/ fv ~Y1a^߬UGčXNc3-e;ŐqB -w,T4 x1~3(( f/sܛ,[`gݭT }L(fڕqdwh3|@2Fw]7z$lYdyl@}UQȼ?|s]TDK*di7F0{YohOb:HccY u _>MAXZr츥ع_+KwhbthkM5X8wh6Q x„p|;ɨ#9Ht$ˉ RRϮ41qDJ֙Y3)k|[(uϸ}[hd\ъ+jMURYUI5L$D',N;bh $t.hGzGt6`-8TO&ͨlH>=b;gyOxc2-twlӪ”7K CddFCF|x\1*7Վ5X^;0=H _^%OMǒ7 !O+\BS+ҷu{BFKg}Ey$3~lc4>ls/P}]4R)L $Kȍok3.2Jl_IP^t#:c#FacD &d+ )ݶ hE݋H)i -ٿd YOHl Aɺ?ZBiN|ӟ!S ;D$ R)< )-I/ޤZ34ИdM.'*HkؼCNý̄6Ffؑsp/"DZ879k9qgaZUh` x߃=m ETzd0q;8FUFX@1žb:ܩ'֧j@jc?܄vھ&5.@}1!xkHf~<~c~_Paԍ廅sJ~ㄙ]+o%'8LMf{k:u'IJ´3:N\qB<A\%O%rq4ZnnPG@hMy @!0q'lZr{VJm~b|D厗L6U&E[sGʾ|{rdLеR<+3yI:jrfיb}}9!~xeB}Xe LbcA^b) <N%AX:i+b-C5gZkQlZ9Htv |*.4oc\ذ抜}6_c$|ŌT;>C..erh_7ȌJ?he>K!PJ->`RZ!P܆ N0Vk:#ur` ~r8))Wv"Aϛĸ鎿felu |550+sl[`|1] pczn߅M)sӅ#xM K`8&wkJjÍi( "x ƚt?ZÍbu?F*STP<&@ue΂h댍C33$;%qpP&Ty'R8^Y"v6oMqbiH/242d6M/Ecfm"emuΞ1L*bfTRC Ft_ 艚iC-`3%-&c}(N ^Xb(=hQ$bK}\# CoD˔V,ЫG]@Y WE0 =]^y^ oٰo9VJLXV=4k26H Yu%/VS*g0+ VlE2i2&ܒ2+U-/6M&Z4FǦ?hLvσ٭TAfNT5;Uu {"pVi?D|=VzΞz"M+s.p]+KdxCB̛cn0I'vՁBK"xN4#嵪]|xkh0LmVdm.iӖpABT|d[X ;]q}w@'gj*C @ϼ^:!^>ZOƄ{:Ѽ05|@i]ywl3.7e'fgE VVۊ6ST>#C.!b+HΕcVc%Izz\q-{8ťwI0T({M_(7>VycM,c%)"O6Sb`bj]M" 3pѤ/)JvA=BD)kRPrY͈HDD2;gGRuvCk+7m xJ.|W9t?p).(f8VEұ#ͳw54oxی}=僟T42fjQޙ65X؝`Gq<9vqNj۽V "f1&l8,Q,{^nyȳBZ_I㣻cTk :!3zbd!\#kVk Elć(ՠ쵾2K։$ (3;{ג>pS9θJ)k[>u2˺a;lӝfo)ڈc= d3ŏ:߀{taIخ4wE|]{NMԀ%V[VcT:ScP/w:ⴲ]^ǀ_bcߠZc7Be,1SF2 T-lLa W0:nПOzW4a5QU8  `ƈ/iײ'&5Dhe $_S xBwd3_Y#Գ@; / @8%ԹqUBLLkɀ >c l0S[&mc0Yݣ$!'#~&/蛪#FӀv5 Vׂ'a|.!yg,/һzŞfxd{a[,zt!OSSAfX[i.ï~S>ԇ SH鏌k˙ Viul6HSX. DŽ znlh韆%碦ze`B'a ^TvѢ`_Kg^ut~rʦyi'֔K!?;9Q[(R xr_f@ P\ăm%2*͚rQrКe\ ˆ b78. Clx6>}a2ܼj'[R|‰.:F+~tn׹o${У}2g` @[60<< yN/6-}P/|V2 H96aLJ(O&g3=+av #ԜCM;{vINWqTrexa$:=hZ{hL>Դ=(۷>т%^v/hbۋs aB=f)m>WK_|M$='{lLi_'ЏmQ쮘1P #u줲d֮z%Qj^0ɃcFA 6uSDܫA3o~|t6^*zr$@Sh"ℵLLΎ8 bscWx<.͆V f̤JL23 a:#ٕw,0 /m+K gR9pMVdYyStdd;:@pEOhV 9ceCddCmycD$"gw9օDFn:$ņYeYt6P2,F.mV! P}kHBP-:nMͶ 1< u$Zdy%F4>60UtIxWЌ6wB}BRpwrz{W cUeYט'\γG䕹Ĵ)F,ﺷ n A4uUc N$O լ^ۯOy`Q|0=܅$bgְmUoA͝D}JASo~/Czv-xm`84I &s؂m@WσY)+ʗ0iܡ\k<p<*jnk{-_ff|HQ@P!^(#J'- UYn4x(ʱ MC9[/|}u]zUf\ OCӂOͷ 2HG.6?AIo9e'8,%|}=?iEqp0lӷp6R*:նRLxD@D56E/06%i %΀BG: 2@F#;Uo!շgx⨋YkfOVJ΢yop*cwZ@[A_O9I3oHJ‚Kް+W{^~Rm:׍>6ȆQ3u>Rd5 fVc{KN-u !ɝ}0 jk$m!D(Ls+*.κSZȃx1ƘFcqP?JG0t7k(P& 6BKc9eB!%e@Pa=pqF`4|"=Gͤ-#u sN&-`eN]}&y;qj`-f]}"{IU g!w=R;1=?GK IF<~ ӋM_E-g~˜&d-umQ YAx4!Q+&h$KDFrҶmy:}ӭx(,i*\-7^d;U{#!d{axH7KU?yfҫ:]c|˳ߍ3V>rSixg[}fCvU=HJnmJ֮Cwb]TrxA&KBLAlѫ3,]rD\qC!yݒ/~3<YтM Ue₿(b6"aд&oAdžb>ڎv`ORk/1TibBgbb2dcZq 3_pplًĩjIlŬޮ?&J]I`A:=l uCP)Ho_RLYc+zto4.Z* `DDf*q59  &SĢH$."S`ZLY%Ġi|WC:Ȅ-ācGnΰjlFt&AjN] :"kT(ڸ;%"Ʌ.-wO`2+bԂ$L9.VR=9=^w$"m{"av$A\lm bS;څ rBX UUK!]{(@oS[- EE<6G ^kB:}ظv;Rhasswt |JGQDP @j~Jb-$W$c%yHP٩3)x^Ƹx! V^ ܒAzЬRzG/e^r^tf_bp{kG>S֨KZ"臵ҢlKfGC^=2#ts(|C!ZN/uۛxKWQᵪ0z賬ru_6G4ƒp1|#g~k>yUΚ,uD=ҸU|\t0Ur an,O^jْ[>:qXXh2L‚9z%mr[{DU+2jw ; Ko,@bDy5KB`Daj"v(|S|fvz9&wGzEaà,}0tZnuV9ŷ+Sp? :sr/u/ۚ(b/q T>5#Y&D&q fi _t>}嘂Y:6k YvhrR=SZmס:`sug`U78ܸi> G:LZBe* G\\)\cّ2"sûz3Y¤DJw s⣌MHbuf[ O?$0+0q )I5YnH7mT<{lFAځ_XpԸ ɒЁW+3}5gMK#vE_HyQ_YU~AQ6RU p #O a:N_rMkB\vvhh[3l7׋=Gt@#yFx E>i|sU–F)i᮵w&sʀLWӘB=y䰒Ny35b{7GRCJg_oWVybMF@SP_7kbˤG'?'r`@f 9ȿAzj+ɏ 7^Gߧ;WNV; ĘdJS[aHjOB7i1Si67O<њ07d"g~:M.,ќ}=P_s3z݋D׳I.T &z(RcJ[!) +xލ9֞ oZÿ l 6JRn,ҫ4R]{oJ/ ʝQ^f<~VTqk"*|Nr&DNimyRY&`^ E?P !8򐜵Wo-@[!7؊hX -qܹx?HT .K/^eBL V:0{tH xSbv!1D^dJ`$"&i;ҎelLYyM)ËF.EF1bU/9Le>ü>#jl@^nSqm*gݖ5RQ&~HaϏ"ǽ RAX)AaC\em} Iȿ` @Kk8 2+ Hg]=!fؼ nLC"[I^ y^z>VS7OfŒԒNl3?Y!TFD5 $7!9 E4~Pdռ'E?8R.$'ԕT}tyrI>7gO`bIגe*3ށi5MV1n|v^w>݅O4lޖn؋e>$S,gҏt-bķ˹$<(OhBW{g0[}P^$&d"D]ڗLp0s2oM`lL\߲?D*5~;8Z3XUs0U  ؖTv{[Iv*/[7+<[eeКrL'%R$λ%S"3iK]w9usW^Ѽe_\T ׅWWHoeuLGjAAH4BvE2dmuEqbh/]H +ۃ%]"F7"RQa $Cgv?CG 5y@NiKQ\r YBӂlWWZ3huFx5]%˿ dxCͺu)c'0$2Eް⌝'lcwexrA.APbнɂ_忀jB" ")q5V+v&&=矝S&\1;lcI` MOm#]ŁtMSPt( ~iM,Tn&JwT-g5Hq8%9]|}C#i)CdǺ>e-XeFDe wǫDWqD[ٷBvgĬsA3_GѠW`c>HIQ2rOMl{YFdYR:6oK&Sjnzym~!' +!ś V-}QuNV4Aݩ>lFQ.C¬}tpUdtPI[aҔAPzoV{`N\xM/QAR+v_-B51RQyrnyEG19'uÜZi$ ?Q+oA 2wwrU^__0Λ>$1/W>1mI_C 7bc^3'HΞLHEkV/uCv&_Er}nL`DκWQLzNYC<9T[j_X 0$c>w~z]!mBtcƝze]J $ݱ?mT2݉HsY[_q0sRLqy  ^$.;};Ȧkh,)T |_\)H=˿f[9vqK ư{cdtUD9ثNm==ҲD J _ZD ~F-VU(ǁbkedTpNxc"ׯTv< =R䄞~.}X~';`Q^˙aj/4[MK %RU.0;p+"\;yut8 LЈIp֘Պ.)ԌݩY3x&nmY7ɴbH#AjfaČ>r1ZQ;;O*XyzT> Xf}e7er5+nh;^5*pE/"D̗UWڟ;|JJV4%*4~$26RUL\;d)VNUЦ0@r?Jn9VI"46lS}*7Ĥ+~6jk5 42WcvUaD)?*o &z,K-b: LX+`i.9*>yyxq]9/9t6+%K+C-NVqp!^Chh!Q|Xc0UVXD[Б̖֥\d %/gIo9B2oRT2$ xD; `an T^k&$BH>t_@Ig s&YqJX+W5#P#eL!Qߩ.zYiPri[+p,<KCZyZ_q YҰzz ׮gdiۆ'q6َ"vP%IА'8bk) -ǀ鞆&/;i[ɞS0hx^D#&bL/0 W `0|6j >~TL N[; .(6wJ+w3tPFH5L!R7Wҷk9f$#nci6*̄ͩi[)lUuŷ!t_QU c)R*sOhgJTb6L\l뽌NoGOi$4K0s%ru{2WQ5c v{#N9-,~$ބcVڗzfL9D~ݼqfYjez:Ȯ&o9ԸAR"w/)el* WT3 !Wװs36Bp&uƚ"lXo1bP-8- $y׾ڟ`婗#92f"EI]-KŞ(Y-)Ol.,M3ר-BZFM]$scOҬC?ͅ!D=3rGL_ wIvdkXX.^]a5~Hȼ}afcbFctu]rQ5{OzM0C={*?w4̚X1^HV~FPOP&(Z@/%郌sP?>r@G]氅3ٔm@Zrl A&F]L0'qOg\zDW_ >iװC@"ĉK(-Y=9dG il .G˩Ռ1Y@GMoJ :E#fL1Vn>4/(1Q AVwIUbk .A+G=WR!\Fy UJЅw6F[K(H`Dܡ"H5+.RU}ш!^6*W3',D~e\X*Z>Rmn1>uv`<A秪s!\90V'}G7%S%B!< 7(W`@ j2xj/9e*DJOe?S:H}mW1Cs+̡w9HSjHϣԴu[ɘ;\AA,M* QT\S]Dd3",A&w;+C_|hEB }{!0BT#x`}HMSŽm\cXuP4/c 1 [;psNҴ5+q +)f <^)]d/za||sXϺS}3GokɈ34EV &bj.5TsgD*lnӁ ړKty(c !U7#>=H+FJoCȱ0z'Ht ASO-:Kr=L[!@G'Xx|cs"4G`tt*Wm4BB?`Sl"h(/cx?a~2R^AH}e.+A?dthI &f0ThMi#_IyN_ HAIJqqx2#UaU2{ꋿ?@ۑYLUQ  ',6w)PǩvC$K~Cϒc;n6g:CTI(z+,CϰJdf]`P|tcj;$@ָ'yaDľs<r]{2\*.g|/"NOĪo{3WQ컧KֵHm7i,KbBZe;jG.Rт @ tФ|!-S7a=Nqƥ$9J&!vjJ$`]+Ͼ/J,ZPh C)U:[nG׶x_52$vҤ'|;DE':8.wH-gQISv-^V0G|^[`J,l{67ŽJX: =*8Pۉ+RFbsl.KQ-f_r)M9N>7U/+TL&"pR*ˀlF=a^RK%^=,u#kΉ;`7Ct[k׽i~b(i߽˜Uz8JA =+q'%i ln'gu` tb07Jyz߸G2kWDVeA8Ã.ZzCx+d =0n&Ee2i۲t bS-uЏ@Ө ;jB[0={m XTr8'o㇉m*9!\\f ط|sœ%ܡHϖm귱sVިZ18f )hz 7(r-㐳$ &z2FHr'l{ %qaHX,Geih3U:w5.AٶĠ[Y;?fmwo|rXrGXيR ~ `뜪gUԦ ;2{y({])Ū W|XY6SFRQa.gUE9,4yOQ}np̐"Z%xEӻ EOK1E9mN'VceKc qS{nKt#!{f(>҉zY󻔇K!ko RRl&c`'Hͪ=fm`Ԕ܆B-s2z S/ESB3];&BC-Iw{2UC^WM#*9oEϷHp_ȸ(A\Z l%6֤逸QdWP7DjsWBZmVeCw[nd6\1w` ys4,'MQС>gdWenk7@5xŬ6U? ~sv/zb_Nv juTТ}a b7,\jZx{7\_hܩ7~3/j>bU΅!dQBY:l|&mk=!8UWMχU@kV՘ goE|lg,ܸMpH!X'38H*ZHZ ǡŅ6MR3_$PueqdpΎ״ 3Er/e1p=XDޮ)`۶w Iu%Fr-ߣny Y|z Gf]@W ;o2ph)Ϊ^apBn;.mA!яQ$Ua"V-rH 76`7 ɘ9 Ê(9K1i1X<2-ׅ'ji ( BP&K{!'|~VJfpKœLYbki+j c%xLnsU(kXv>=oLr@vStrڑoD Xя L7;}qG; ֢a n %[cm#ypM/|"mbCǭ|QhNbXn{&~+Ö́2RFd=zk;e P*[yo!4h(E{x:–98k{/:v{ ı%/ ̦B Rzg\=zi戡z$8 &ØY|at hAա;\B^DЗe@Qh#@Hw:y($r(덿-$arNXH;i^̋^ǹlFEv2iz+ۄ?kiZbs-W̋5P @]8AK1q*jܓQ~1VPK0扭|RVlh\Tl;-eH=&w(yCZƥi)5TU:("2Pq^=yirc ˠ w<( _Nt µ,`${>z5y{"uoo"ڭ]!tA+=@,&v`cJ gc熍n&.hHETW=OnFAng`ƹ]3+c2X|VλL#]r,@HM.y 1{IKP5"^ ZIDIȀD!9MAW94\%v]ܚ\@oDpU g9U/qHf;^J$IАm 5{ܲS-m,@7/~||BQ8'N՗`5?{i["eQsR y% blgX5w|& [,ǟk3-\3׫3Ҥ@4tt ]v^.i"Ʊ)gf+B8z#aؖ[}E*2t'{j`r!Q2 \ `1}NRqmѰl6ҝګ \ ݰ͝E/ :5^&M8m^j?]D ITQVh}&W,{ʊOQsU$&iƢD]੸oմedun2WՀޜ1vd FCcBh)'\i1 Ij8qWY-dW>5o)U%b/%P  P}'ݠ_ &$.T&.ʀBԁQU E"UlxgUYϗ&* zP"ю㸚Iy7 <0g"V%p9qc-:F2{ڌkr苺r꧐2;HoLdf*UïWA᪍\~e~bz?_LMf9\)GLW`9;@ms'lS{3JPn4^j6heDF.ìKֽU-;327jJs*.SY9|rh^ד-T&GxtGjHDiR*kSD: Ss˿TŮKO)3؈y*GAWkM@}c#~?,b5̋]jCeJa ]-5$\͒{k6>H/5B b-cZ$8 Hel5_Fxi&ʭŏT@ 郺0{њjUc$a08[?'Ǹ?_㯍?;Uvb?Mjj<dK8vիN+N-ף݃m-VZ*ۭ+:XcHwhY*]&zZ[ʾ.\Yhx'˺gԖ'`'1ʉ Wy7([88 Ҡ`wvɶI'.0&Q hKrI#.DUCQ&#>i /+ Gpnߟ@v)JьLJ h#%;QV.8 g@V 8`} i8dR#!AlC7*S[+-!Fzf]ͿC(V\W݃ 8؟}f|;2xN]BGڒ.jnH\̦$l ]wh}=6A᢬jX \fE\k'Q0ɈAL_:!5V}B; }ο>wv OR}7˛EO8E?:q. AIڒ$/ u_(Tqu R62(^}c^%(dOIBN!$5fgR;`G[zp=G>gU$O K2 \mUT f?k*i [ )` NWC|Z?ذ\09m#ҵQS5T*sȗ$cv,޼˦ 5soY*kON<[q]bbvr dHYWNh fĩKcXh-rHUSap*hBg>qT ߔV8@g[ kg[]p *ř4R\;I)ItظG,&65sY_3ӫ,0Zt뗜W:[ݝ.V<|w3di5Ho`->oTqhSn{ 43kqA6N1u[Y-3`KˋxSaU \d*z5,BӃj"4Ch,#|ӥun|D>R]qEjQw/BVw5雩4-/_@JDWE&Ϡ}()݈w2 -IE}M%@HvZΩ_KP9T 3Ja>ok.{W=U? >K{VpMͫOCr_9[BpzHt_Thе^ISUa9 U-~K(W7sz~Hykwkwp~-)uU>نxp-z|&ފ^SCQy~Ӗ N=vh-,N^ a:Dh 1Q湦T{F-K9#>qaP`R#XE.:oƭjT+E{ O~Pl!\ۓO;c3`᱂o80'NӲ$:dv 'h}?hXRv. u%Jc*aFځ" sP춬B$pP(iL#->NdG4>b S!hǧq胞(q9Z9D0-F)->w1SDeO//p>ΑSVY݀TөVyij) adOiX6Yy0(D/t*KHWKRY>oM19\7}AXԕ &3ˑ#>?87jLPoNj`O(2}'&Iũ $Zi Ӹh6BXG'Cni T }3~O㫊s~!a:C,0Y,{ʰj}H^2HuSu`ۭx eW´K4֥zo(Pu~$&]_# >zА [ݨ\{ڵ Tzll(qc*ϼJ5XE!FSݗ{GmWOKmNRkjc*q^D9=vW:'weVFi UuQd\+gDZbxLO'5G*_V]y[׷dS9)<UoS؃Z)"?rI( )̴{zHX KO3l\Hވpg]*4!V%_\ɡ"mcet|: &M'VA;g8pL}wGHJ>Qs7nz,f6uAȝi~56vq"BKhC](texsfɞ?Gyլ+0=b:?/ EO0$Q@! WǞ3A='j֮ j9\88MܼJ>9"<~щ"bq wCOQ#*9ku:D6<C͔u.B׬$txz)-ʼn:G+{P9NB_WٺfXQ!<_ShixQ + W^ &;YqCGȭL4-*bxT7eNX8N[fD$?,56] _lTѧJaBVhoCLŀA#9E‚umlBƯw, ?"R?!{{pMwA3d9daIiSe_M@ lF4" 5~f#$HoD4I Kl;=WL0KE|40]xS^vf9͛}E)q4)Z'sԔ >DoB*㐄e ni60[T\q"{?hހSXLx[K ]{M[;bW.#;U(3 )j+K(פo7nxz!TUM&%:r%Tҷ|_^=Y^nuי-UzSNZod0aUX^Dbi'/<-zݤ˸"i.WFK,|D;46j9]v-䯭nE!=zL{/!x(\gev_SpQӭN۶m۶m۶m۶m;m|K'9:fg'Ũ֟S>N֑6(>\h%ݩF{4yedðe(:ʶnc,L[R;uWC-LVu6d$p~閿V\5tV-ю@3y%XÓs&K"K07'n&AV~Z̠c߮DmNtUƸS R3tTz}-,Bx,)Fbf4e'wL'Fԛ kq};0CVNz+6F&t@fM+/l(!(joCO㚜.;a9SN r^}C&yuU!*x{=)^a{n*5)e.i`Sp1YoʓlP~ȦˎR0ANs3y827R[( k&mn/f\sKUi#(|8[u>SLEJY:51#xp%gNtwH}xZ n. 6E뻾7cT?S?QYd)\-_źސvy5 }Y"OQM]84G9#WC7l>+deUuuSZ#tY) i# P%'B;! ƾXf,}(ExbUU",FsʟU|NB6[WK-ݠ|^s t7g8iZ ,b|u7sZ6q\h"&zbDR{ԻOw 9!SH Gm+̖07vЭ/TaNƩ]e:R_݄S7OLxC[^2t*D5xfplul/{E3~G2|$WG6Fī_W_$Iw¶Kp$PBf4mK"" I L _֞7 :RvJ,d7! iĠo.ᶆXOu7 f6zZ0NGЇto>vԠ'VE]e/8u Rɓ(PQv P 8g]$C}sxC }kO0҃e U%k;0*fG A8GM(WjG܃P+x`\{e6wZvgW/}y[xY!oPY2ejiie\_Z>;+%Dgya {jT0Q@ ^wh{ku+L4,gJٿ?-{F5/jF"MœE\ݓWf[@` 2(ߥկ:m23,cy.|8jj a-EV(K:qx $>OD:'Y[9t_._-^3حPs`#B+q!= MK[VjnmEʚO8$R0Ƃg#-M`K8ǵQ Q@;(a|( slJvj'@s={ƀSkNȆ}xߋ(\쳕<&~{U~>_ EǬ(v{1a 34}}eyu$N@\1J+2B$ػ_:9C7*^LNŝA7 U-L@v LoAZDn([]LL4ng|f q0S.7r7G%gp_a鈂Kk'Y ^z2sN!=t_-,xq߿SUC5L ^X&>)S?Ta6+9w5 9[ASnƢ/*o۹~hŔ5g\AAemKwoQ$[nӬVұ|U|AL ;x+8h|TG'Uҡ*qD٠˾h57?(B|NU|*k'h[ɈL(nBcF6"g|'9XzHSN]@0 4&=`%)tZF[Mic[’KMtA= rS_V@͙""{AsI𢪢a:)jIΑLeW{С'˄#Nث,$V:yG(/_/ӳcs\pKI} *BŠ'^K Dt['XH]O~"! y?fVbM9vdpFvS@H:H]-w}Y'f *5Ϯ9iM8zA_OӵA&GH)Vn32A+gK[cZŝ4LC,9ID!/+;rv7H3?1qMdHZi‰ |Ky]ѥ_LyERbahlߒ >u Pg8$FM y*̟ X;fǽ81LoF̪5خzSMV :9=Y-Ȝ[Hn 7am|Un]8F1+޽_DQ;=BJS:wvrvg}w ~+[F VŮg6꿰*%f@v/C3OĥRxT#MȎ@yNPg77rr N9Q9 d5ѿ6T§\jڕ?q"wX%\1Qhd~ ɂ@㐃S~ F}WUcZP=uHαkk̿fBegmFBEW#9TO@Wo\=jAJ2IdWר ǙCUW ,NU:s)0 $1${iPÛ%jғ±'$`cFh huP .mЬ ZgX @ȼ ]*c0%C_HG_݌.ߕ҈źSš22dBY&;J-;y9g03"$J^47,Bd$otn]#(6Rդ5MC f^gYkM^`a=ˠEJ q?崛Y"܆w[QbCA{zt ӉL$NziI|86Qz2̥1x'4ziyN62GU5L1Vc¾ RcgZkSq9.tlnpM~R8A%05Ȭ;Fh*X-Kǧ%=U: ;3ksY#*k % phs&(uzGs:tNL*g,jQJ=xOh>3L =&QbvN2A &C3T~uy]gYڇYWSMIٌ؀i\XpX6MS-T}'9K-eўҩ/01JJk b0'n[uxCyje㳀D/LTHڰOO`qXwnrJ<$Keuu4C>r>z w&;Ga“h9IH96CұЙ{|w_~'Qȝ4<3o`i395eF׍N`ʽ$pc>ѐKɭ`鋺|sԍ6ZDFKC-RP7iT`|,o7|fOtr;%Վ} 5 7JdU0[^Crdu63`LR 8؛!oQ7%U1G%Mj${tDVivF"Nx:H$W]QFV}EcZQ/#iAkнA 80KpD$@{P(; vr%dL6Ŝsׯ c8=Nb쨮R {|J]H!BN" B6sکĸFSr69vFߊ";{nfVB zsC@;fmTh8[d[o%pFo0 ;WZ*{_r*,bwiX "> ;A6 &=&>r2.25v3$^푿v|hr;c࣡vm^dնcy ź{mRQ _Gkb #*gZKBWp;~ >!S,XWt4LeKX5ҦYX >U9#墭3';(iZ:b 6JFqv!'_~/dZRY.=%U.|"x SE81̓|Q,@3hcQhBTNIv*̠k(򃲾&M^ \UH{SV`.BY'Tc~!mE7m)KSA&grƼkδ*z l擋Ϫ12 h?<%R`b 6De5`x/0s#;_4IDžt,l!uq]po 8wBZN'4*K;(o{4E-~HAr#cI>/w6bfsرsu NjB#|wh#߹Rj TΈx jMI +9;$X"h6y=l%}=?GȘ1p* dѰsCFph1{1*j:k@ioGОto7 5{IPBN%>$c,VinK~ιMmO#:D_eΐ^60W \ ُ_CN;~:@6a8*E0鳢ʝ;`]Bkf9fg"T ܀d0ԗcsp ь Ku7fy Rj|=EqUk@B3@8͛YFz˸vmd U AAXgϠsGCId#Q VWlp#ҸH'Omh es:Uв y̠=S; <^{3$ dBHyi$D|0D#` gѕV8_`dFb$-4 sԕ'(8,lkR=1Ө8ym`x )dLbw7+}0d7D<}C%;А^oK^;Wr֍VT[ u>:iU2-jLݶ S8,\>+ #='F(&̯/b"˂zLZ.bNtw8Babc<9<}KĠXAž vŠXdlnWjjWs^{v1nes_E@>غMԻe49Q]˺[Aq[ݟHݏ&Z\1dvN X. p"Njm(?HKnXXķ^Yi @|C*չ650$^ںoyQ걛M* j!sLiR%ãBmF򋵙5T|EE)ԱWݐV̿I!20 .Q?LfIeOP YQ'=#b:x{S"z1>Man׌C^'Yxȝ(֘Au[o۩Qp)(m^O/@3/r%4V*{ڪ'^5\jڳnv ru(V_YsڞE>Z 2:u_OWH^FQ6{=>cu"CQMNT%5aLsF,lQk4X7co;h"kQϼ;r 1/iѧ|MTWR_ę ON #uL{2uhOO.gvyp6!^b!JB1%u>G=HVa jv He i>bl\!zB‚ڌ7tZ&ԱYe%z!>9&څD  p]ŠB*XzV 2 JWzC!01pGj7`p5WKs'ZI\\(~Tɷߺ=ml r  {6o2[/ T-w cj ݫGΪ2V6WhH=*']KOR];'||3~Vt}}Pbv?,}.̒VZw߉Pn{Ir$]U^_#}d]UPa!o+ӝNfĄo1W:(ih`~vƢ+ږ7`]XmT3#Cᡛl aBWu8ݠU5f[R;*nYd'!C2$(ef_Fw[K[7zT]ӑ\<~6{w'.9:Ϙ}T). WVU 32I(i%Vy1WK#v!wQ RcoaڌZ!X!lD &3N}e= Ufh֘Z& :X>[']C\peK1]_%W@x[+_.Sޅ.!:&G1NWT-; =̮Oqw -'"(by&z\K=}WB oKM#r3ȚN=lߑ89Ƕַ[Xn(Z[7qգd,(,aS3 ~s`yx#+ > 3g>shEMT΋?wƟe9ݦm猅Ȩ[H9<'qixº@˩,)uQuHdwˆ]uRz']1 E*Ue?zVtC cP~΢g3 Թ<$m̻PF^ݰC4z&AR%7z(bf{4ki81EYU_I{0<#&>T'pX!FRSYV#G<=8E=YưpU_iI"Mwz\dh |1mڽ/W[:lP<,^e'0x%' '2Őӿ5u@QcZ(^G"~llM¬(T)}t>@VB=<Ûd d%(isxwnꀎ˅ӂrhbtrC0e*iSZ<i$X߈.Q cTbNPGfiWL(Oҋw09fW7I!q޶,yVŎ bZ`P"/:9r8WBuc}Z'劙8k ^: Uxj ae:^{VTu&a$8>{0Bq5sN/hmPWh<56ԉ6V +1 2ↂʀ=.F!I{# [%`oc|uKJQ196b~D*Zcdud= bd's!I,a4 ϖf'Aq6G)8`AS}^:$~gAٕ+r+.h2}q\0edͺjfUrUCV,U)6P78T[!|UR7LHo w8Iq?:%neri)۹_D. J0mC`?YAXGQh4c~/fdF][*jfmʓ殚Hw R#=&H-VډicE^mbO{-|TY;n mcJD0Ю#gȟF;}7~jy*^ ~Vޭwv/1R@&_|`|c=?-{[v%Ђܵ}TcӉ(EʏcB-X)o wP뻛ٵoP}r" *" ɗ3`r}7IW_ #bICʒ*Nma8&!:a/u(Cl!G&.pA?>kY2lFp`Dit)>;x+\l.tlɠcØV|YJ"u'W42<ƈ;ϵQ1vd91we\ENhz*j:u0>T`w 1,*\YRZ /ή;<22^D.68H՗˼.#Qlcqa񼇚d#9JaR YGnT|[qV8$gJR3A1%#W0!o]TN X@+^DbML;hy'\zzDGe˶XWX JJsZ؉g%* gzHIHi $eO|&}TiLJ$kr8yPAZtjwuUd&Խ12Ǝ K~1>ŹY̩?` 8 +ԪBkU8plt\(R@=k$ۙx43Ƃ_(ӁN>am>UzP%ٯl&7 t,  A].S'\s>@~0ÜC!mX= M,/ n,-h-Vr'GZX |+>al4^ө;!Iֱ 6,vh?sa * ΰ~z(p?v`?BMm@TlLe  w>aӲOs߳Rjf#*wd\vBH-7I=( ؚ?؅W}s ؓ'CeduY1=# EKJQu7G_4c Ë/*sT.DpL`cvp/W&D ֐:[lOppB(q\%ݜL{X1;lz}8b]SBg#)'Fgp|SqA7Ɔx7W琰#$,f0rt)ĖGdҼADfPO\yF+98_*4Rj*8҃:|R׳V y(, T!Ƃx[xXYK{|{З}MQT&T LOO>LJ_m Dnc>euO*}a]&RjbL}@8A28oH2sZ_ m?@+1sTGT9]nsM J{VX`'!6lƟ5 o>jIlE7x:"0m,LAB!rG篆(%{2Yl#]gu Kε* _do)MRf1zSd-6L(Y FP6TvazXC ~NXA\LI |$Ƶ Hw%43_=(c[O勜iudwЯXse狽p nv+5-.To%GO.qknp\Kđ6ظCG\S*P{}Mn4MFnp𭅀^6֤nd&# {(gGoή05mWBa3"'(/|ج+s/@[glL_&I% )9x;j68bzy# ojLF@xԕ iz) 7k[u})C]=?<|nۮvܰhaR3r1RbFOHl(ao1 CtfCA"_*]_vx&\|ĵb^-d=1n"rnhu)`?*xqiP|TFkm.9IJg$Kc Du/Lj#C V&'Eb{˞r?/z66>sZ܂f+Q&tޫ,)633y-ʢ"6`ƥmfC>] 秷IB/E} ;+Q M@F u%Gdv|o5}`;j(oʌRP>#6IjCk" /gD^5;`pt<]~18 L|~uf zo&oJS3O sV@a? eW0mwm c^@Su;~t(D'ӜH*](X@y6XZvJZZ]^n_kWYi#mPf݆z`䥥Z.I-6D[oO!걳`&hLT*"U*FWLI;5&pFx=Σ|\ˆM}R*`²ﱨl_}дC:e(Ʈ+yBmWY<t]q-Tg+rŔL1ᖼY•pny9%mR5„74<6)Df{Lmnj u]4vʬة*Pt<,A˵J HM I$]䛏s+ulZ'ts^)^".bޜGsqHp?v˭ZRs)U#h}W]{WFgj{&ksIӼ\.rU'7⫵V&Rݹ$jT4ߤL<9/ԥvxTSYjz 8~9 RzB0&\i慩Jteq);pܿ63=/j҇V_) r X=>F@BuE0UC.I85P`?dˆ56nvދ).KaZFkz_w/7@12̃kbf(I?x2;S貗mYAu&}IU`['υ=0K ^zpjFD""8;Z{hX9O|hQVr\ 5ϡK`vD7[ā=*Eh 4o1~ƻfS,z X9׭U5CW?vδE(?⥎qԵ3.&#uT3ya-%:W0\sJ `L当d={M'~ xPq؋I DV`0Uepk67gR?GHh;ua_]΢׹SNba{6Q/8}f/4F!lHRfL)0Ǡ4Fxfk=Ygg-pmCҢ5RO[Z! [${Q䁊l{p &Z=AqЯ}~F7%V*Hp}V!R?l`u8 0J9[r4UbVl)RLKiqmbů?_[]K("Tg >lEQeYN$aDiۼنuU}WJYݺ Ymidd7#|cMFYm&)~ `֩PDߣ MBv+[Esjbt,ڲ'vЙʘӅxb<]UW[W̿g'*cJ62n`c2pf}'vt[>xқm Ygxߞ3F|I<115 !@+(K`Ř'-W'?ЭPʪi_x_H)΍dbb]Kle`,29_nc%q9 !:KRyaJi~ea5_^@y\>%oN,!?^JJ ԥ8>E "H۲#5QkoĖ+=T4L}ATm4Rlg]\@< s IK=&倕e~(Dum4k's@bѣ$Ax:w 'Z4CeDJ8Up~>l2FJd_4\_8OCcE]wJ@uY |H?&g_XգvcC0M4,95 ,:)cp2=`5:_:T~l3Tbw䗈Zqc`' v}ջ-R),gHs7 "!ͯ4$ ^ 1x3;Q8vPѓ#ⵘ*uF'ebrvahԽ qi6_4/d&6Vb(_ ȮcY)Pig]^h`8ߔʁl&K' #[< ۩C(zBUȡ+uzm%Ïސ'j#%hm?́.&2wө&-6*+j9e5rl ih[;E :jСЍtmjneIn&& *$˓-,4AE𱁱G@`U\}MB}wn f$E rcxP@;(Ø<t\ԽZp\<$'%M&|OD(6`gUogy׽m`vwUW &ߤuXp&|refU:~xr]⣆8.$;po3} Jm$;V z[ 93咔GT{ywX_62;e#l1&/k${ǡeIj0@l{~TNH]YU,G >]GLb]8@_㙀cVQB.0NnRyg(ؗWO}7bW$!G#=k|pՉ=E'݇H漁-Ɠ1_A3kzQ ]O$RBB(1pƦ=h M(~BUoxy_&zd|gDPxQ Pw|l8jlluKS,~؛7J-;i~H*'e7NkMkurrL ȐTej2+ɢ 櫭KqZV31 "'o8ԍ?lr4{~eSgΕ(ػYժ3uW_@rp[s[Ol 03C2 BV:iQ NȨ=uCqCi%P]@plR} -26ȟmuؠ|pŘ|xj7G Jz;m8!-;yPe-+L+B셃a<ݨ4̗HWI Ա`#ʀ$٬)L-}ч-I]op/hOOt$?Ga( -2%D| xL/8ÓG]l~Z;7{RRtS}Tc޺*}1M\yCR,/][ zhjľnA6|4؝f=s%i-pl5,[unk)MĞ_IgPDg~OiXJb#2NFl[p9y0 ΃A9֩|@rȟY,'Cf,f`OE~dXFOo$)߳tA0P{nGy/L!ҕx8lx>ٛ}N2^0m9J߃k,Zj \"σyTj4!:ڂ(tGb/_t6#xU͠G%jF鼯Y#i B(gs^Qwwֽ*Bģ1 '0 D}&Q>Ґd _ɄফY3GY%JJde-TuyweܼIsOP/RϮT0wtf lH M 8P#QН2Ʌ_piߙx7u$]8T{7}Nb̊[c#"D_m3e;5ѨF/X!<7v}Vi/'*{ _uyyhꖲ}n*ê(Z z 7 >Tō8HЁfѭN i@eUlC =jpX4G)H#,9lG Pnс'/Ag[akսJ)}̯FM $gYR<11Ɩ$]MF*6 *ߎ:Bw^1ICtmA^{}Y*@7PB0Z{GRh'.؋Yk=Vsl$DPCoG&sɐ?VV>90$;`3g_$7IlP B[|d ;!p- kCCVvٱllAVdž&%7*\,=izF?t=Gj{Bkß g`y"dL uꚳ/<8;'c #FKz Eum6,S8D{j1cK0X^/4:כ@ӮBEvj1 EVY'{K oUbn0aS]pt-E&">BpA^b8kqIXI9d̾Yk^I0QJqcb|5c)Zۉrc?ye=Lw35CffY.AEw2l5(wv4YMΓމSfn.ȇm@0mKj< Zy!9X"TL2C^^l*j9#_0!SFlſkx d/dȊ]-c \48G'f]J6N&5myGn~3DeH۶P jD*'hsy &[;>.CYa3k^M4L? t\n9OH#=5Ø7EBT8pwkS" hvW{j!Pbf^On:L#m&ji]SƽośAhggق5XOa0Fv"0*[PRXmb]8x(= ;Iu4A%&i!l6r^@k_?GK4jg4^#%e~%001()X3n%42s>_`XpvǎǛ:!۪"]I[뉗׀Jg:tĀ#&Z|[%y<,ֶ904K2OL ]G45!0$p0tb253%p3ur#`c]L]mof0Gl,,=G }_Љ_K_u_eFUUZ0ZT񴊁*EZ+dGE 5,<)B9 ;s  %E@5W>{nR* VOO.ST6hCLJuU4dRFeBnfʋ!Z"yu_;O>Ó-RU&./fic*bl_{vhHcCrY1mGv;'5јj* 431t18a[cT? ߇?oQ2]##a,*e&WL3.JF.\տnLVݤeO|x (kX Zs,$He3y[81'BvGٽ.㜵շ> =;{c ChG i:5w|I1e_@}EoEK%ql"S((u^4.:'c.qo#0ۧ~zIkUVZ-q)0uG|dn_r(D饮u{s#o*?J6VuQ8c}5Yo>fXB`4ȍvm֧7oYS1ȴGk"Yƾ*RN/-K1Tm:[RttA'.ܜk-PPf AXs6]9C~k(JrET-naAyZ;ϡ&~BH9L VDn%x7]>2pN/?Hx1l[#^˭ #be _!vPsN%Ne[QlA<;ʧf|$ۄD]6.3-XZAPu㋎اPOS"KǦUr"+MN'yJ:[` Rw|9ԇQǐIKl[d+k,;rATBd.zxZof7k\HӠdoN|ii^nДb+Z pEF\"z!%& i?TM*qr(\; @W>Y:jw85Zi yЮȕ 3/ +/(6V*?|nad7!L‰UKst@k.ڎ vKsv0z'0h!3Oܷ5ocBSrШ8%S6ܵyz.U T㾻'Vr!tƐT}FL|_uHiLܡc *WȢ}f\sdWls=aZ?7uT[q%q7&tG* j7,CIx #lWIH[6-f8機'Z]B@OE^9`%c<knR{z#Ʌ֝8"T\QzF@kT2?MeYAӑ>ʋތg"٪*nMdWϴC>Մ "m۹@-O*$l+Xּ^sw` !'P-ѾhKB_B;[qBgvĻK@D4C/Jk |C?*Р҆)[(ge{,Lҏ ^cPNOV+ylVShj8Yz!&zbi: \3/ϲB(|XEl_0Iz/wϜB#9 1y>4wJI NC1?sIy.;i{\1svϳAum㕬r:_NBf"0@T¥n(0GiKfj3ŴM$6»${d-ɮnRLڐ-U1ҟCW(KivK?@gif0&uF7zV Z^ӷ%;!]W[ey BSBBf"oJX.$HKQlUS Q$_$\gZڱ,m^U)3")%{xŴ(0C7 ߴxgא'bc|Y ȫBm*M~~۲F[*JT)LQdWߟ6 7%(l{+/\YA6 ,$~hi G97!ZvepA̰7ĬAҍIu>zH|++!ϫ3Rχj*y,^њZr mg4+JCՈf}a&D4 蟷Ư''Pʅ}Jn0O8tû L"rZ"@e;pc4t1mt՟N+’{٭vu{lGdL@.P@aVz9E Wjfʋ߄VC{C R1fN- w[hYoxgVkjnf*rvےno+ISNErzVfxg̢ ZSΒDDw; f)ߍULyFsz?s6ob'=^FGR2AAFL& tE6y5\jZyXy2m)T.v|\+77S=cs# zv魬 (Y 3(H&^\(z®\Ɛ8v:N 텱ɀ/u}e{a]\#3WD9 ; "삁d bǞ}h诐&Hҩ5m)KNa?KHxZpMjtJ {/c栋3]d,pYnt8 _T4Ejo9۝+){l˞;.n vq{y({թ'^Z(bVЃA1VRSȡ5*ћ8P@|,}R`LDU2WC_Gʞ3ϥ/p~t\wOcl4#k9"@Y:&QxiID=V j`EF^bbNxEk'=n'! Q7)ZE7;u7+{S׭M}t?+&V i$>Q,̝'^.&C2@+j`gT)B+Y ÇT۬ X.7f m뢳S.P!JswO1_IIҊDӠ}πA&B^]yzY|,*7ܩ &HZ-J2iC{mv_bFmmfBFJtp׎2J52,(6>GW!Dρ}iELG!P5i2| 0-=%G'[8;/T"9>@~ѡd)uqe咕~ 0.ī -2$ʛk Uhk:ٲܺt?IMS" [ZBFް]j<\Z85xGB,?j;E!;+mќrJ.bkg @x)czH+ϟ^K:N!KVWA=ڵc1 ;6m|_#Ά?ۑ^.jr?)Gt-!0d9ڠew'w+s F HvDLޓJ 棛݆] Wcġ٧ߏ 齑Yai+c$قWipÝIxf#6$BR` Gs|m, НM~.Ot=CK5v1*^al7\JeN ?[)=ewpp(Yn2>SD%/ـ@(6<;fCx 䋸6^A#>w[GOaJ |6J8Bə.RFϥt,$cL3!Emޓ]G婉܇J6pqԦmmh 9cI^`nDnO4*&\v]nx)?3'b?Od}=ٛqJ]ό)g3h]ܯ3ΌGCnLFԻ\~0=-AQn ay =o;I۞`._X5^dLEUvA2%zQ`Cz ČX $AJ'BmWLOդ9M1=W WJ_n>ٞ26Y+K* k`hl\Ln:oVcn_ܷ1}퍼k_O B¢]̋$񮖥bOZ{Iʔ [KřkԖ^!T⦮jV{Cn1Pi֡HTw@ SLDa q#SޖӲ)@JJVs,99Q2-B+i@.*o"D!dG;sŷJB& \ۻ$;\qGC,@iaR?.㰀En$d޾0`3Z1O`Z1#ĉ1:T.Tg'N@@! =xTBfM^d/$S+|]j(j'(_|TTw-GC AƹtU `PDF #._nxzJ}s_֙l Et9`م Uk yeN_Qո3.c="߂+@OkXOvFs VD %{v2#b 46#j, sĿ7%D^n~نXmbrjn&+7(wʆ $gS룞+I.{#hĐ/juBM:?:/*-!3#WD%j((~U#  4OԗNGa )YM׫_a ^8<)F(ёfQT'9"aH k_R|Zz0 `5`DR< 2"j%§ZOղ)+AR JҊ՘ƹA$)5$Q\j:R|Pdx. K xˎlMbn( *|Z{.©j.c"lA۠z;kÝ!/dVp"IZBĽsHw!@Gn|sP>ܦ)ma6a1Sm(˗1D89H'KxiĚו8Wpj/.20~ 9QGJWgݩУ5d[}"+otp1pi3f"h6CI%:z9#0@:K |쫶SDx!pHz)P64 1r@ l) DEa렟2qzhos3A@*4Q|Bצmrv$< bYO`ٸ8< 0`I ɽ[߈,(Յd[BCLd }V;D@|z!@NgɱP|73!r$~gXK~m2.0 B]S>1V ZkʂH\"~b9.X=.zv3dbjj'Ubշ]ī(SX%ov{CE%]]!T}۝y5cyEZ)NLhAnQ{:|lhYrO~éID\ypZ{'8T~v;?|t5pXQ˕g%?R(4 UkL-k`|CK|KT-w% e=HaaGj}}o,o)#1S96%訖LVZ/Fy&'ܛ**Ar8veqw0/)%Qv:5u t0 !NA^ ´`?Z4dv[ď^]a*=U]W%LA ĕē^o67:u0A|: Dz{@ q}@008ڧZVG6QvԞ`{zWpx(CXe"̞RX2EKמZJ_PzE'ˋQf&'k?N=g\47ZE,w h%6@5c scdow#exzwXRE#^%YT{^ yĚu;,v|ӯN5<yۯ鉧c>\D+@a`9=(GRq. 51`e7@]1nHNy#sߥ3; O|Nyʛ)]QUsB7hU_:x'oJRɥ9KC U-Ioc笠Q%bp5SoP [z!gI>x ^<㿳03~syH[ЍdM#i#kJNMyUJXk!w~r*p $l [ 9UϪԩM]vd>?Q0ݱR2JU,;l>"hG%\ΪrCů YhB}󴫟<£b[V!YEֵ:IKmqw^ , b6"r .O2ǭ:22^H 3.^wݖGB.IA8*Q|ȳw)!G B$M ,51<)$NUiC{4) Z$ye ww@_ޅ3fr[ӻv(1MR%FZeه[du ѓl%%ev7D!Z~FTrߒo/*څqqQ, 0@ǵzJlIqɮ8oZ&ϯB ԯ58:RlbJq]_hXNF' C/}•x ojz;-Ym3HrW^ľ%2:Af:&z5Ek;)A&2Q%nXN(o)ѸSoʇg^Ռ}>= BR!,' хγtVkLzzCq4p,K98֬ 1Τ苰$_\`EĆ콳["w56İxȏh_RC#d٬M=iGދA(Vci'PWmYmwq 'vsW!t_[Tk&VK,^ز"ϓ:.s5R.Eh5lL梸!Iś+}]MЃ,V e zs vʘTzh 8/ZgdXFtDQHP[Iªd~9vҼ ahsFaqsQ @ɕ~je/Ânt |{گđV{`8` [5mFLk Cv?s!o·O!2wԦ fXtYn+Nt:AL7 ưgL5+dlGg(Ӝkb^q @$؆Оo8‘XNGwPQ#W|枌!N"Z6Om嫖bTEM*b۩h(@6A6aF l7.M0Gۗԝrf~ݛ cf /tCrqLnj̣.JAp`<"UO8,lBmW9E&Ki~ஈq,`ԙY10H`a%Vl=s9Xf6T Htj:+XL2zTa@Eto|j4,t&/v`7lsuBy,INr=-yrOBj-U୭cTU.1Zi u@BYB! s$߽yX <M檱ۛ3Ʈ h}h,Xh?e┖ s[8}6o5`@!&LdjN㜧J XU3z GxC{NS(PLE5]ݵ}?/Բ=eԕ 'u6H$rj*L :-fܩ3E퍸V{H#G,Qu?zxZo)Ff甬w41qi>%d|NqhTI=+2oxw':< (H}jEEk6k~k?ɼuY<Z.79bs|a-rp#fOh6owl'S ”|+Peccmc঑̞96ګ3)$r_pG鍉ٌWE|5X\x>0Qj=S:\w,VOVZp\]ˀɌ>+itl:gHq¹:@9 bjw&T^эF+P e~{ҺwpEqgFFMiNee5+O}?5p=)BerOxNwDv;DVn_RY"^B,vD}JٜFSu< ZZkGd`|̖?Acg^dR0*sU KjAU$lܛXާAm~PbݺRC5pAd(AeW5FXw҉{FzJmyv"o?@B5à %b۟Al.5Dt&$5 |I.iWWхjHوbַ5r!IW<wb#önN6xhtDe^_:݌36g̞=T,f 8h<{+`ǑL@q% >BENak] Xe-pMdHϚ ֳI} Zqd7>nwda+س'?-doHX*Pfq+dF0jyߕ i2wAo>wv}B>OTMѢ}~Cfܤ k5Coud>ɩ ,C9~]+W$ U\k݂ WߘW-D8 5$A Z3t-=ҞnJ3*QG %*|*a3~YYxӟ5o΅a0:逸6,s{q%LN5qtDT _4%4ɘ7)oqZEV\m\p֕4}.Z`†هqҦqgmB(1.QbG]tZ1cC| %D|RՒ d{4M .gG t1A00y<&d, 7P ,r(ziDFg[.,TEá' EIj 9 X%P&$/̞^C`8yN^̟M.h+tq5Hаvۡݽm% gyRIC,uʵ OGkN_pĢobS3:3J~y3e+BaN/'9)Lf9QoF3JMG:G@<0Q`S">c [7Ve0 [@&'/]La)D1={+B1t278]Z~4~,hY; Whފ&}3 HZɚʲy'GQv7n/zwq"N*900[QθEoU_2[kj(1W<=o>h2A)ƒ8<թ˵A#,RZsHc!&<הj܀~ϨS3ziq>gD_~?8 .6>F Ly˕eRUm}=v尔Hwt>G.Lu{ey $5؈bi@C+]:xДI[h~)*7JߧE6tAeOt}:BOᲖYuwP h @)<_W}/PPjfDGym!Uv@=dȘ5Xx[;#̉Ӵ> Yv21]C}2 _G_. kؾ"X*E䢎D)sB%UQy(lH;P9X$aJݖU %}8r9K%s+,n@_+ʼ{WٔF}'X4ij<Οg6AKy6*H7+//5kTYf5{߂vRI\07m QG.f$e"ֱ1*I! LTު]k51$BR"RNYRŒAx%Vxd,Ӧ  Z,JՅQHyvf}MSnSfLr'5KX'`y ԅp̿h6BXG'Cni T 3~Oۓs~!a:#,0Y,{ʰjSH^2HuSu`ۭx eW´KS4֥zo(Pu~$&]_# >zА [ݨ\{ڵ Tzll(qc*ϼJ5XE!FSݗ{GmWOKmNRkj*q^D9=vW:'weVFi UuQd\+gDZbxLO'5G*_V]yoɦ4sS y y\6 W͋$1?p e\1*ndzʀA;AW&2Vҳ_!yYhLV dD\t+ ( ,iBTJ1A'j`]1֞{ xն&+ȅu(lytE# ` |ZC@iƂ< U$Z/3d\ίUq4m߈m0ͣrK7^1)m{)\VKQPS <׉El^0yX[!ܶÈQeAq|ƥTe `++Bz8"s=%IZ)->Azʡ E}ު5;SR>IE~ UPHϽ@R(=iM|4Ltz.fٴ)plŻTai6?X-CحZK-CE BK*-tLB' -O4N(wprę~jV?|joiJ?Y lf; JBMkdmPnE=/ r 6P>vkgɞ?Gyլ+0O<b:?/ EO1$Q@! WǞ3A='j֮ j9\88LܼJ>9"<~щ"bqO NwC+FMUYsRq9u!my:))}?#ۻ]YIRCA[ڋu"V|r# ac]igdaMDtm~LEr2.\{1\g [;"2YӴLq\tPݤqC8bL~Ъ7$"yBg!Yo&Xxb 8D> P @Xߗod* B9ȱ(#&h+wՅ`:0~KgyB9)ۃl 1 !$-NM 8\J%e,toZpf3I0M3m&yEzP 90HJo~Pbvv^3,!_tyOyم54o(!ԇkyH+QS2pѿ (f(C-UcAlUT|XEplj}kYzN9 `2Mu`-5xgt4m]aFT=7#UB>.X\~ݸ!^@myJdyױ:^gb^WOͷ;kғ:WUayQҦ)X̾$GywB,֊nnՊ>$(^ /Jгe׶R6T_wٵS>i=2!k܆pC8niZҝhao}aNCIWϫZ[m@&@> [O"-l[ 1%zᰣZ灋~5”k,Q'`CJvNnkENa{QKg:s7\5<ڞ9Q =jTo.sSzja姕ov'V柨ܮhFw_WN1oCO㚜.;a9SN r^}C&yuU!*x{=)^a{n*5)e.i`Sp1Yo'ҟ(M/ɗ=\1~m1a-Eto_N2y㳄Fr<`Q;,C2Vˏ36/VĦ#{r";N)Z3 SmW[!ؚ9A_2NqY# Px`lC$Bey=hd'ˏݹH,+ xQH )``\ mmV>2iyu稥@O7cOvAu][njK/KI@a} [ᕡ?# 1p*YXMDOc+MF=W%fyno^1 aZGb.Ej+ɵFmɜ5q3=7'F6)s(#!Re cz_K/y=cW9OL%xB+?nlkqDpffmѧXw|Z+z/ήjnčԳɬ`[ /Y+ל7r4~fڹEg&Vݠ︟]:a* Ib~9HX)K&fC N>ԇ"̮ `;9iSTH1!{3VR}'|&#_w'wOTW&H?Rmyrh.ETյ;Ϋ%ԅCMs͝3b|5t6|CB{?CHYHQА L8*8 yL0H:4caE)uӨaǬ7S,^{"NoL  &3 wٺw]mCC9IcLZ`hEӲ0B1a#?<" ;xkhO!qsƯ3[.haCv P=@8]vi\H}+nwnO< >1 ozUЩxTW7VX%iq|#:ol1"^mE"I*7^=]{=5,X&7'4i{]5 YO`g`XFge by$BYB9u:9k{QðMg$ȆHj" ^NŮ*H],?m)g3Pv 9n68Jj]:rCppD{\C蕯Y5zFW4K)dRj/v1'#%AMc8ظSTs`\i;Ƒh[ȔIޚ [8X)V 2bV 5qև*-7D̹Eo )#8HըwP*7D#^ؖw«:Iv˚ ^(ȐfX6tk-JQ55Fټ+/"]64 n8n6ej:c76_B*Rgl(S+h|T{֝ęG|.w rنWT5GIױ\IŸ ԶCHD-W=>.*{1$?P>['o̐ <%Ilw? sN2gio;n7߷.Kt S =Xƹ]UVCSaavZ!Lٯ*xԄzt='i1 VȵwPZfsݭewF.ySvtV}` 8'ϩ̟Ljk~3"=e 9`*ؐ)|(VN.Lv(j҂-EY)!: C\?ٻT7<)JX, G]K^e:Ga9S*)\nq+4B\|W~Q3R!nRoL(/r힤2 @vqA.~In!ϟa 3sTS[kU,`>BYҡ܈k8$a|"ҙnC)Zɏ3 i`h_ڪRsk/R| !)f4$<;i7uh[q>f2WpJ.Nȇ*XqUFq@Sˇ?t$%7,|d.vP Q4<ؔ% 2OpP?|{ĝ֜ X =h21ɁP+g+yLkY|@ YQbAgh5(MD?;Z8qń*\~|<yIj !)hR_u QF~zNFĮPaa/ahDyHLjvW]t4/p̺iy6(c!aNNs ,i,L928{KS茵 ƶ%L MtA= rS_V@͙""{AsI𢪢a:)jIΑLeW{ء'˄#Nث,$V:yG(/_/ӳcs\pKߤ~l?WaœX_"-,p,'T?vh|uCjTT(h6f3C+;2q F#l)D X`sE_ 𻾬v HXNC΀?gWXxÜ&zgjk / #ezk Πʳ-1s- Ne||\!$"?O ]Gy9k{Ӹ&`E${T-4Dfӊ{i%RTW/ּ"A104 gv`Il:(Y3SWKEu#ww<pLtu3Kdxs*|S#ܘq]^Y/\Q;=BJS:wvrvg}w ~+[Fi_Ab3vzu?l!*h7J<yy{i0q88w#(Pd6<;͍\(dNTHx!p7t>Yd&f @](72ǠZvOf'a{ 4V WtL_c+58QUtiguּD3TkOzsl<v}+Y#*lP&@H$|93U[(1W{O: Z}s*25jIPU@!hΜ| B9I ^s8f ڻp3 XQ11ڲ%@87~K"4kb9ȧVj d=2ow LI$29>hOܣeRXwJXs`\FFܼB(yׄqGi`z'=U ac1SD˞]](ܼKwFI(̫8 ,TvvR ?r"NGv3KnK;J\l(hONa:?p|5@n)~/MԦ̭.sFLC(ϩo0L{C9RE?MXbnޝ)tk4<62GU5L1Vc¾ RcgZkSq9.ݎtlnpM~R8A%0DLTIB=8e똵:X4Bކsn;12-XE)u> $40)07 sԔJ0jԟq.e^33c< PQ=:MD^:Î~b b%h&2I벶"RyЗDIa]f0]ǂfQ?Yͱcu;덌jP=*133Le\GuXɑ|5}o*/HfL?GǂŷŚͬll;!Yo1(9G|g].\r1mn{v $l2ӈ#NB▪j{$~8U4I=4[1 #KmcW: O޼@~T P.[#Ժ`Ad$Pf]*g%Az4Lf 5u^G19o*Ӈ FO9t4oShȗ,P,^Ģѩ?&ȅ<= ?ajhHr4ErbJ6Fn -RB[c` S#ˮ )5~a;O*{S|֌lSBq<[|K] Xu׮Ez:`ѹO>Pl)|gfM)U,5I]"1k.^Ҙs8dP,H:!r1*H|QaQb-WZ6&h$yQeL___K~mw<ǪV8> AN!dJ4.1_ +D}o)7K⩿TX)bS`['COΔ$@bug_2(TA=DUxy#/ 2&|H:q:zO/}>~Cx ,t=&:L.a4ӧ<r >6,}QӕoN\F@vVtih~R  7MFL:㉮QNsڱo!fC,о bk\kHl&wfIdC{Sb0M6jro ƴhI6DzޯIahg.WsDru7;D-ӊ~Iۘ ^s j/ā]jN&"C ,(Dipk. d5/@-%{~U_Km=8JBJ*A*Yt! :c06iMؠ[Iۯm+ PjRJśY  EOQ5kSEXo7"z6/5ݧl5zcQq eךH+Wa90ۼLKر` dN6A95UQ5MvI!1j DsOuF ,Slkm '[ 5Kn.whjн<%\hQ9b]l IZdt pM<2a*uHw?bHdc%.{rVM^̗ΜxGPidy3l+2bم$|ApEj~KfŮj%p$Nǻo*‰xigAGm=tLS٥>f^+F0h"^*@ڛvf gPᖆXK&ݴ'{/Mɥgܮ:Ӻ`SV&aʮO.>f.I8dH0UdKT!ڠ- s|Ǐ~v$x>.c9`]ļƎˆG{Kи'Q%xMt=Q|\کE~%#-/:m-C Lx 4SĎhԭ&4wǁ*>_X *f@H?L9žC2%:!l1tH3CB#NC%,vn| 4|/"VE:;7[Agt-( Ӏ:&o/R?zùXȩ[d*ci~O9ii2XC2 +2X7rU0k w/V]H&/ GF~ }VT0_3wRsK(bp?zoc&}&ȡK (H1@p C}96gW  TZi^w|cF翻)#_7]Uv$4Ѽmٮ'z~kGIۖiO R TIxv >=+l\x4D?ҡ U\(I\ou~V 7a}.k~Nt2ږ vNH:!-_ƹJ1S-О ۃ9emϺӰew8C‘ O6*t<hKPoK@N8p]ieCx Fk$Fr|1azM=.H]y>2̶&ޓ0v1J܀B,z,yw#i CvC4~=T ;uFsU!woۊjKY5-^E͜ ۖd ځ˧p^ag3tĈ``B_>W6BdY}:^IV^Et2G?}[Pll߃6wϡ|t <8p><t$N]XC; -jXW*p#X߫ўrOێ=aml.ֲ[7zWz&'JkY|<,pѤ:s_+Ɣi#uE!;Wd3|I͢g.4mvE>E|땅;I TrzUj^EY+n?701;I /f:"L ,fS}o_P^uCnX3&  b+D[#w8Xco)glFCq̂yjdRVM:Oo_-7s<s$V  ($G[,B VCԟ:Qاh $E"KV^cUf<1@hZ? v5𚖨9eۖ]WzGX5noYuڧҹ (2vA'bKW:ˋ(~>4co;h"kQϼ;r 1/iѧ|MVWR_ę ON #uL{2uhOO.gvyp6!^b;?F.㥝йcJ|z#/)9 }ALoX~}ظB􄖥oLc+<.ZJB|sMgj%wp& oÆb(j.WҳR}A0P_W8EU2F׋ئ k~܎;RӸ#{EH_Z;JJ EJ +1vncecėcHPس1OdAzZ]zjpqU>u냢_`,ILhu}G zj|.Gݵ _57ݾ$ b* 5 6~^w2#&D^vpeI%!DICζc4]aжlTrjo d{T rdŹp73ߒؙUq"|=b q}Vm/{n݄q 4wMGr! /#ߝ 1w&R\# gFLg2d>vQ J"bʯo7F"BV>,aB.$=е(c1BR ^@1AC؈1LDg6`56`{Ь1?Lt*|}"r_N b!8ŗbKV08h!]& u]BuM=' 6~bŝ?$4Z<0F>uݽU8n9AӀ5/nn07ZKnJq{%DDdh0"7ýف#ϦNKɨ%9ՠrEڊήE'e1D g! Zn1OidXW,Ya88gEU"}Wqk7k #W ~tgeXHQv08VY +dN @I:nϐ[4XS Z@!0rC5{QwBf|Ž8>I> QO}ӊ)2&YӬ|{1fU(k昹~oFEmob3 Z³%%tЂ^'疢]mvwY8Zbn 8G^%В< pbs)6&^ҁ0m5@K)sMOX'@Dwʺ4mMfE;FγwFO fĈ{P<`DE!;}+4do.':.i*k ^a v$y.G`bHd leAy|#R璠CJ &: - +"_Ӹ}ݑZu#z6%rfp뗞0G:߀Qd,3@c9 Cߨi=J;l̅apᦔ 57ӎFM\IYEQkb≆'FQFYUʁU:U^Bh5qeLlFo-KD^q[@ OzI؎%'LX*ɳU{֏X^!-)S>fYj¹ÞvKLBG=/^"Kf#^n( 6SU<-)@d_ YJ@n~[q&_-C_m*VӔiAp#I@{H 3*gݴ@ə+$|>25`yF:J|0R OBFxzp;zak"X3xJwinRA 7ocl[>U_iI"Mwz\dh |1mڽ/W[sؠyB7XʤO`NKNOO"qSe|!k& 0&ƴBmQD)ؚYQ(R|x(,-iG_:,Gki|TOfzby73zȦKPp, =7+.@>`U6yH?ʿ]u%4gTbNPGfiWL(Oҋw09fW7I!q޶,yVŎ bZ`P"/:9r8WBuc}Z'劙8k ^: Uxj ae:Y{VTu&a$8`k L^ yklbCmVbd= !{RB2F80K0/6-$sˍy3WM%'%[R,wk>Dle\i}5L'8FB]; =uH}jڱ-L;hA@iھ*`]1T"1bzBZ7{(>9]Ky 0+/!ZA0փш& Bnn{p !0f\nO둰QSJfI Y-{=UUG!_뢊 ZR!HU55!سX~@`eCI$Ds^ZLw+L8o" rEҺ|e-=rXYb@uMCC[k*qhŹCpEBP87_\&iF-X F֧#G_Ni\zv-V~pPT#*XU/"I_ˍZ@ծ{>mҢ=ApE+5QT KeIT'0mk :u苡ds6 #ُ}}ـ,Ry?l6^Cu"4srak< n.6G:d݉aL x S>,% :ԓ+EcDOryZĨdT;2לb{2.O"a'4=xľB=%?bo*]CL) >WTVoË! .8韋 *R2oHgظ"h}_<&ȻhNR,HU9A-f\e0 ٢!>6-:&GuFz "ƈL[Wz7Sdm$ЊXSӎA(Zއ#nѱ>(oٲ-uҜ>?5CvY d#Nw3{88/h=P`vq<4c2}=t`S9OXǭO|1<+"Fu +۹ }Mj]6 (0bBPס)0'c+2xH)VsA'`ˋ¨w ,Z\))39QV6JOp>>4tNei=-ful *F>y?bJ3l.$Jh@p?0V6a *R2C;iY ݧ˹Y)N ;^2Y .;CT!l}Ž+>sJӡ2ANKj:"%%:/k1_g ŗ #c9*"8&0ÂC;n8W+bkkȋAzϊ-xx8!Tpa8pnN=}[kpδBrIT\i('=Ǹa1919$lI;C̩v6\c]& |7Yǿ4o;S}ܣhe>K!PJ->`RZ!P܆ N0Vk:#ur` ~r8))Wv"Aϛĸ鎿felu |550+sl[` ט.^1=oRB&Qbz&_m'%Di;t5FӴk]膀n< ZqmcMFfr1GBq}Կv #YCv)`*(v? rrG:2gAZue]\LҝC8hf)gGHhyS{g`5D>4@GMKXY۪H9AtvEs +?"C0z(GEڐg E {X@JS4 pZRWaHr(s׊z('LvsysFc=a `Nz4Axf̮S مNJɸΩ0[䞳r5c(⢕)l?B֢ |g1@PM~@UzouJ uts*Z>w\.TaDt`Mnp53?!﷡GP}U+LQI)w$^^[צ@b6Sa}+:$&#tC7FO~xr _3J#tCU˖2Fe3 ( '3^*,圾_C4!̶a8GB5Y/dߌGE8ׯےb@ ~<}[ ӛAUBۊ[-} 7~XLdVC85w1C)U,=S*> ު?Wp(h ={0^u@ZՍ[ 1jdCIUnf]tfHcS(!V/3vuHZnt"/g}J'e,]^lÿ\ݷoooDP~.I@Qz(=Z˞i,*2y + [MN=V^ll<}}bҵa VLWYPO9H*](X@y6XZvJZZ]d ׮7G=$Bͮ > zEKK%\x_l{?X,΂g1Q񚻫(T:]1' k$ޫbqA-6u0JR< Ǣ}MfAfC@6v]ɋj̠k=[+ $f Jgsˡ, l&MO!>2y06U5u ۩*cNCaaP@.*5 5-`'hwo>JS=\iş4zۻK<{{ O|Hysq wz|.:PhI]d?l䶢=ՃHiKȤ1s%.pX5uIlā^*^!{$Fq^%Nq]7 ;7^׳p{aʍaX;0EIHөTع?EnӀ ; \4KJRrPO>._JT? Գ+DV3"%TڣFʩ~ͧ#2ްK0_y\K$ʮh"Ut(Bl]?x06crd'h"͹n 'Z4wM V,"v'XD/usNqq5yL5^H7J"["UÅ>(,#ɸwrТu'tį*z1  Ѵln]q,pZ*'] mtV "f1&l8,Q,{^nyȳBZ_I㣻cTk :!3zbd!l2FJl_4\_8OCcE]wJ@uY |D?&g_XգvcC0M4,95 ,:)cp2=`5:_:TDφy`cا;&ͫ8䙡v{,*Ň8BcGvˮK =j's6De3i c0Ӿf*Zf)ft66̂iwRC|B 0LEOFJ-4Az5'PSoG][H+P˸}psRbr0mx\}Z4{ 4&`ijZi}[hJl/Y;@4OE9{↰K?Cek6LūDFI叞=6 /'6L(vWL~:߈ vRkkWے(5rV1W| IN:)^KZN`ՠ7?>CSUC/~^h=9 ^RgjqZ&&g'kf1Nݫ`_<KE&Gx93̭DBfDv:L?K[<|Te0YzV?a^q0NBUBXYk+~<$>YP['/HFk]e%mu7QN5haVYiV3 ̡,eUHCT)R 9TKGn[mSsw-C Otu{71IxVi&Ylna  ( 6G2 lpl5鄅 …YԋZEx%p D36FKiZDzÓ2#??#‹zcLQ gdCo].'.g ބQJ lM DR9)[޸wڅXkJ_cJ8G$(V\/d^I]0_mյXjDaMP!u >yán a;!/|u:sGa@EVU/xx 5Ė33 >( q/nB䋌؃]7zg ݛ}N2^0m9N߃k,Zj \"σyTj4!:ނ(tGb/_t6#xU͠G%jF鼯Y#i B(gs^Qwwֽ*Bģ1 '0 D}&Q>֐d _ɄফY3GY%JJde-TuyweܼIsOP/RϮT0wtf lH M 8P#QН2Ʌ_rIs8拴rD:[b.e*Ռ>}1pfŭ1thT#;Z,l;D>uhV@+=Ux~꯺<{<2uK>7{aU}AM`ÂWy `pxțsWFYvrkj@YV[b'OƄ}f HXv9PEPŪtmg65N,l  V(avR@}WJЗp{rg`| {-T0{l^>ZW#ߦX𳍿^)n cmԦLg{aFo_y;ܤ! >,O(!-ҽR) 4Ў{g yŬڵA[+`9VP_g"Snס79#9d!PmLCs\l{ <@^.MW/yHBQ= &C]7C!@;DdtӐկ:6OŠAx5!֍!+rX6gve6P[ acCgtȈϒ.e=Oמ#xN Asf3Dd0G^1-Dq²x;xҚ!Q3, X΢;p\A ;u;g[&SIHĩ]3Möj 6wQ^x%W5 H-xǼ ,*t&!/\ZL/69%LT[jumQ YAx4!Q+&h$KDFrڶmy:}ӭx(,i*\-7^d;U{#!d{axD7KU?yfҫ:]c|˳ߍ3V>rSixg[}fCvU=HJnmJ֮Cty.~-!OyC5cbEQcBV~rr%hF 9;-j|_|Ds, @'$1_tP- e [ǘQhs'v+)74W-b+I3y.4k+ 7ðH́4hͧϴNM#8Okpcʒ__z ЂL-U&U\ӯ#)2oeYm'*@"\]rg7$S04 OFVfHU/J`;'vhHcCrY1mGv;'5јj* 4bBgbbƆp#2$T4 X(6b?BSx39Ps~eBؤxO.c|euCP)Ho_RLYcWߎh\T&"A1R7ULBkܡs1M=E;|ɑI\Ea.L.kóJA:ۯt [~7a<Ռٌ:M՜AtCEרPqw"JnE3ٓ m]Z8ĻdVĨ5Ir ] R{r{DHE<}D;Hc,%56\/w7 RBI Q }[EU y![fxP7B]mAU֮tlNqS5 xwौB@H*b%aA2>@!2Z~I2HJ S1gRqC4Nh?)'7勹%Y:_ʼ$͞"x? ѱ}ꧬQF]EЏjE!ق͎].{dG)@uQ B^Z772dkU7a`3fgYSmi&%!HcF7af}:}Ϛ,uD=ҸU|\t0Tr an,O_jْ[>:qXXh2LÂ9z%mrZ{DU+2jw ; Ko,@bDy4KB`Daj"v(|S|fvz9&wGzEO`à,}0tZnuV9ŷ+Sp? :sr/u/ۚ(c/q T>5#Y&D&q fi _t>}嘂Y:6k YvirR=SZmס:`sug`U78ܸi> G;LZBe* G\\)\cّ3"sûz3Y¤DJw s⣌MHbuf[ O?$0+0q )I5YnH7Q6o =6r/,Cj]dI| ߙvk%C"W/$μ(į*VXa٧ބ0k'VN.ר]⁵1 `5bO0i!3ok\DŽz)|էQqJmk--?m\2 4&}wO;C^!jލQT!nҀ!*C-@T3UXEkT951Zq2 >{´|!|.om=JnMU:0`o$1&YfoԫFүړmZTqS?O1 Y#ȅ& zh]hξjFKoE"ւ_YT$[wcWLuB)1WҔāgc{ƋkO f?x6 FS%r)7{GUR<0MfNG(/z3RWsl?gn5]>^l|k 9T"c'fm ۶<,{L}`wZzE݁(@yH+ӷHާ} >Ԗdv 6P|w)XC6h_vUA5>N S"Eӭ"7PX0H;8 *VPO- RGwq=?3=$Bh1M4u,3g_eeP z.t4пa<^ڎ怄q"ܡB(-&žS&D;E6 j$3wb<-b:+0k7g .c+Yn~: q~xUS m0h,ØvL-9kΤB{6|F@$I N3iCdW+VH]P/ّB,,[K\4Wkb{{Mߖ8\o$*w_ny套G2N!& MH =:$n)c1"}}/GIPUN%DU0D|4rq iiDzп[yUI ψ㦔E#" &pVo|2Fa^C܊e5P6 63nm(aRU?0ŊG^_)`^h ,ܔ!Ȳpe$d?+>XH2r?oB ҙaoY?6/3|HVRWBWgnTM(Y05(LkDiV"Dh%Q6Mh@NC?o'_%Y5:FO9N 6u%'8ݠaqh͙5ERdE*l%āʌwhZfMc|?ߟWOw %G%[;8bٰ>ԣ&˙#]nˡ{#NsnZ2pՇͪn&|6%kƒ0U[.mK .Uq~)u?Yò4%*vdRg7QgM[*#K3>ĖKw&]|%`m [3b"/ ="m"څBn#5΂u㻋|U %LN56ީbW&J\l9M(dVqvc^Ƕ~Pqu(٘P j 8rj,/|x6btwC5dz.&.U 9iK)Y ^jr9:JԏnB(J_ŧ'NHvwoAWpg1E 8[]03rk-Ia c;A!C{ܝTINGCm$OaZ좍Q$d$E\_ȪD|d D绱dJ4?y^^8Uڭ6NK5! IbN{b[uGmC.q~]sόrRW6:Muqb{f qIww!z/lU&Q.w#w$s#5eK=>* `Hf˹}_&ˬG,>iJ(ߴ@|4VdNLߝ@۹ԠEޟ6ytv#n58Y1u]QޫzZhYFj 'MjJ!{˫TW@ 7>14SJXw˗(2"_ySsHiY9L«}Sy9 ~._:k!6h,S֜D7G\9d AYՖ%"jP}V+8OxbzFFr ۼqv X8 sXZ?Yܮ`'쵲.dj0$|43;ztdLIV*B }P*57e0]tEfEcʧ@t@ !KhW-oR KQe" g+';cdBzWS/9I^>4T;P[(@dwZRL!aaUn}a/?Y-F}[b fBbaR.3{qL-RL4MAl|맷"􁧾2㍄ MML x>0 u*D衜|9w`(~&I8~P pt#՘IL$S0ePWm @\HM;].1m'1GDZBb{ V S<(:g Zx]B4<6U {!"kߕύ2Z]).`LE+Dк͊|@ D:c(HWE mr0j@Kr=)%?V ?x[ba^l}JvшF;ЬA)pAj]iA [N0wM8 ߛRo9Aį@I&V>!vJg7 oB^&i[Q# mêZ9oLBH{7%&U ]U)}t,9. ;|m&d(r2nWaU?ȸvt} eJ1UR7!۲J\gjR'E>n"=88Pbh0(>l vREr!-${B\DuT` #/kB_JĝHqt֥ NgM)ΔГwd|62!%슜^VE_H:8fFF.\%ڿZBrąI졌ɅQU xnSd3󶆏嘶"od&.8K}.S}2$Zg1NjU Ɉt`1}V^v <+۠|zڟpKmj陞>ƉG5Nb ]2y>(N]UxGj KF ۪4˂n,p~2cOHamc-ʉr M%I P}a0o`lS!l`k7hSшV䖍{o ?x /雿4V=AVTo%]㶌%2S =.Ss96=bK[TvWUd֡*c8^0fQt0"%\"(bw{G\N-%*6CrYΈD-Bh1"GLȚm$ApV;}_Ll[{IƼX@R^n<+_Ąo`ك+MX Qr1Z+S7rn@p`CN07cU?;K OFބ#yZXŠEE]րXJ#Ls%B`|7>Bp3>J͔u-@ 鋧AA*,21]xCU\*ys*>u-7X>Koۓcس"nڑȬ) wPMsNq1ѤK(:ZG՘ dqysߏR=;?M[#'\â+f/͈2!jv|,w)j Ft㼧J3:obO؞I09R lb)["`;gDtJ䪾Qz+WplY<~.TG+{`#"Vi~iUq3,UY+rݯsД͚ Icz<˔bSf*僜DA<֔G䨯?7w>oh\ʊ*6┠=%A dH2y'kZzkshE7Lu4сRl\ y~gʫ*S u8&|_ّHQHօC? xcJ>pZ)^ 0a@"J<8qLDVC@ZMA%}mQJP_RKQcg g9$є$:V[\J>Q &3t[O;ұi!q6Ge?Fv T>ZtCVtDSM6hQ^?ù&}j-1DZOBa}# @ΏUs_=v> |%;e϶lu#5]ʶYꯌa"_a7c[[#Y ( Vx)gtL+ ~}}+R5绕} k&#XھD'.` XUAf Dla·s\^<) No Uىpx%[bĥv' $*]hbyeG"[34=.|!YQ ]\{>7mʛ"XC/^ l,)1SN=w 06"%}MSL]-%`^?@T,>Be-Nn+$<%5 6;^8y3v .%3 >''{#_G# n&hʫ~)WBLAdM|ճEP+/V|Kb梇 '}OqٳsHQ{Rӯ 5Kl+`u~bZp w&_$6tz8co;5oy0RѣnLЭʗgTWNO{1tG&(w5#IJ۲[WDIuu jU}s=X6=E)"c~d- Oc|l,4ң+$?Du<9T76qJݒ|]YJ5S҇Hj(XU Rrd_iۊc |y=뱢a2 ,ԿOpdsqӸJ\nӘ)KDRNHCsҞxB;W8e}LA KH7CBa>?dJZ.oV܎S3+s\U k7ъzt䓇gNS/ޡQ M;" rU; <3R "ׂF+ =⹥s#^1܄#}n~h7jIa3w?1UU!'= HJC{ogbNi:pfツi˂>u)q_؎$iޮR&Y_h .HgU\pa&Įe"ۭRܳ2ײHH |Νnr76AʂձnCrRݯNU9u} gYL?YX(a*Ǵ{I\Uw/AGh MZJׅ9Gx̉ÐA ԣTm{gG&S [G8@i`+,njaOmHO4ngBSkl5ŅPOh8 , B>߫h.>SVͻ%8Nߢ#bsp{-[c5[CĂ*o%>hVuڡ~8`[29bvhI{A76*IeZÇ-mF RƶS(_t֔~—Rh4NPcLbيH9 X9+s&v @t^K( 6 c| 7OtPw= / %Qb0ki`zo=hOd{Wh_eVZۜ`rD>jNe^D q%!Pר SO<F; 9O0g}ҫnv9=dm-EU4g!,\ N}Y$ YϩsciHDUHImcsRV0Ӵm/sI>04hSc畔T 8ۆϿq+O imؓYw5K{29~t) RFЙ13P@S ܫoe@-oOw3j6_4 Wӹ WM$#O!"Ć8 i; HD6k|Bd?{SSHxRl(4_w9|].&<)hd*`F>`2"qRR91aQ !Wg!mToLMQ5֮]@Grx=T(h<6('\j&qڮ&qx=ipf:1=wcݴ!7IAY*EWJxBqA 5=Y0VFgJ9Kaoل-ND(OS( ww_ׁ2(Ђ3'b{ FY%R1P-SHWHxE;RS,S7v*pz{l'&D ~-GE< !N֯T teGbɺ{-Aȕt1PZp.lݥLɛ9\Σ;'G]is 9,bMWBz_!oꝵ#8nZ(b5 j'Lu[8ב&uA3U2lJPV_on!>Ѳ&ER; UV{'Ŏy)=0B珡 j(RjKek|Q&E: jw^XHBis@`"E{'E 5m$I !ӓ`ZܿF%CLTދxBb͘~V@Av._>[#jލ3{8Ѭb2@n K΄s%L+efY.vrM .\20Ec =2=c_%/W &)mu65[a(֑s:kR+H6964 [}bڈBmOҚTcE1dΨʍ*-)bo {!glKeye+ )B: a襵SMq8t_t]{"OE> >PzM}7jkEg7+v} [sbn/ˣC`/(AkIcF1?ul]SY*EʸPo<ݩXyᝅ|*yns6/B.LBq*.\ @D'0#(7[Heud4:Q,zdYt!Hglg #Ni۶n[`FxvC1j=#ęYq^BhA0k7 Irm-K4cfVQtFl:K21ƀis3_&ᜡ/bX mKo'&7yd3s+Fl5E&Ep˚FI9PTW~ņ%ܭ,m,݆6 V = 2Y[lד6~]hJ6@T5N_Dz4cHSQ]pΪ$dwcE"-&5+vL뱫;{9n&0bƃB7-:ѻC7͇ǒVs9p뚕um&8G|-Giu#ZzR|"^e2]2:2}ɡU(Q"U5sUM*H;9kE䎬7j`'XI8C"mn,sWT$1ZU Ps!-x-<D vz!Z˫Ֆ JW0rzb Ov"J eC%5t[A6h333T -u[{Ӽ)N_S~-3O8iM)h5~r^6Xθ 9j Bg5W(jR+J<4Ɵi[NuGt񁜂7y1@'ٛj$Z-foprWo˙3 ʊUN{o|cI;4CvfB͆vJ{e1 :N>?mx1v$&o(zy鮧+LH2&sf=TX' #GC/Eׂ/KU}:~覉]~] KF!'^_`%2f/:2˜K(qνhsH6ޭ̟ORu v,E(`zVl%" bt5{'kea۠QLgVRTt5>Wߝ-גhT=~C]Nۏ$E-N])S[(!p%7+9OFԧISg0lFySNJ GZ V 4m,E=DZ! JCeUD&'{#[o|Kշno@4N njpcK\ڳt%zXN֏ĨcaCf&_u3z-d!Xf.1'pj^-cvG# 'lqީV%YX]5HqW%DQ.мѩihXƴ{MwT&1NEV@V{tRipr,'ݺMKd l2&Ւ+@XI:K4uuEЊ )n[CQCB:AU$ RauH_n,WA [bgSlIT+h:0S 5\1 V$$mb,EV֡'H͚5[G]ToR8 |>0b@OBJ÷ȐIћ)!yh<wx =Lc,:) ~BҁC5kڞ?Fx1Q(9S/@RZ6#rpCXa&z &M0X2,g[\}ZE;jZȔ¾M5t:<#uZ@D#bab6V/˗x+ =}'Aݘ6z홼K5S TjqępׁGR F> {&1Cz8]w DSZY75:Hݫ6-GC~>;\/j~Z{ ō|bS81fU+BLԘK&A<8 o߾w7vWJypE8nEVa{$>IʞaN?]jC(Uk]ؘ)}qI(VM$W0?P7WZIYq. ŴKyvRcTI{თPs sumT>T~{p-sa(o A˫ \}kl4/2rOO."+*?=S;,O, M/;WiSZˢ=j:u:@Hk,t}ZCaWKeߎ1sBYtFm.Z>0s!7_"E>JI0-P0&,0t!;.x e~=O8uಝݞ '1mAFU$0Z&Ȓr!g*Rt/#8t֙][ fS/p9Cȼ?RV|3HР zu5a6b->)hIZ+Μvb|716P P} C9W ۬|,y~/@ hѷAL72&tW)L*JCl-Hg,Pw KwZ>s4ddaq>#hel ^Bm S GN`-wx:=p1lp8©waW3pcyzzT>9568]etw|XXCfy%UMOGc\y[.`.5=4x+57Kے^䎟X; -")olJfR#}p$ ^SqUc# kIВ f`s uiZɥM>{rF`4v7AԪP"f)ħb~/R#ݤZ+oٙL{pS+4F Ah'G2I{7)C-KHL掯6! 4ra;K `b!f?ym먤w?BN\לx60mPM$o)x+#=&S@WeȤIvuŜg@e0@ͪ<=xNP&p|Jh.6w 51rϬW!x:׊s2ћ#275NVr*z5]Oue,ew0:l$W:=TvA͸S8 xEdLjKͧS2oj0&" :.[4 %B*ˈR]V kyޭ)md8ٿA!-6C\dɡChYj(G2sR|έA-v^4fl͂ ~eMlnDB{Gy!#.O&S3=1r VkyPB;!EWnHQ#1`#K*yɪf[ 8u88ڃe[&v{Rp7FƮ8x"v` o ݢA^OQ>ʨjwG9]*:rDv'"yqQfRnO]OovjbH]^<) ioG#¹Ũ9޾!B67Է6A65Zyq-,=saՎ`ӛ5'jdYibzip>f!rS1P7T|EZ'SwIQ۱yxG#ĩ=Ϊɑ @#q:/`"iڗ޸׮dd^Їz+>hT0w5΢iYO7N-y#Lv7gbZMGK TcpԦ4Zp6tП ]wb{Mo{Ӷ|TxSY|-rQklY|:ue[8akXb޷,) =UU`&@' ?+KH\Smv4x2Jp_qQx´2jH@l%5&$~ij:'(i.jqўäx9{Udү`D v5dj~Zf 7~D [WQmv/\ߪ?~w(a+0Gz7 eW{DҟQ!}^ GeBZ96gp2Q8_BKc!|B/9v75_s=BG7Sb7b;645Y3Pm=DYNt|eq.fO_qߛeJ@$g PQ!M.VFb\# {s9 w*ߓ_]ZOv7Umtyl(C}ƭU4CLON"~A(_Qti1gF R_ں=X }4rKet8[vYw9Յm^M;ߚ&N)=kkbSB[Na C* rAWk %Rxb",H#L\ݴ6|dSHJ35쓚/Ca6C{$l"W*uh? NP4n4WFn05[l&n V*I&W}ӰQlOCj ȧwΰN}V$a[}f+k؄4erIMh=[Cc!1)଱G'EFtF2| ;ABΑeEFDF8dmNu6:e&8Z1+)jcZq-;ȋLhW#{1:G_nĪUj F;Z;6aXp{gPW $[|4k +I^YF N,b k'u=Q. vFu\&ayi!AF'J><%QoMV8ZruʷDx*]#DH0;Az`gOIE]odNRq}`8qS0e1FMĮ#kn6bIn"1)K"śV c]?gk. ]:T;IK+[cŃhBWr4.d&y̱/j;k _wcʃC5;bJbk{C_ ypĿqHX-:(z#Q7-i8<>mr({THWRIwx (TX5Ӻɹ&K{ns;H!i_8e^ 1ΆU&eCj C7BMX/RIV@Fh5 -3gpw1:OQ7d5$XqlryʙT$gÿK[N`|zg`9b`7c:ZQJB.ia}pMfR8) |mj=@S.?2Ne*y;G!:?Ef%B;%{3.ܿS[e  bGtOST;d0Gކf9CemkO*>M%As"%̞B^̜RB?N> PlJ4-IxH4 +1-gkj%5(QDx4n|KNS m$>:3<0uΩ{}^U1F#똤c(rt?n S`);@M]o`[4" :M)Lu'${Obh@TFOdPd}${W[QEc3/9H.n}={T|߽PPdEVt fޚ~@XS3Y]lD\a K~DNm ǣߵUQ6VP=c>e(,6jC48VHPVAec3*{mΘ QlUSD]<ه{c5.CH4Uh_㡜 wWDx ?p7${ ; C(,\oQŻ~^7>jz~Fw -E<$g2d2#__`8t.X|>y!S^ʧ]t~Ä`PiՕܳ6N. ߳"T8$ p4_1v+9фFqI},#UTXeYEi%8'pr:nXF 2\O~W*ZvSAFr.2vOLsLl=#߲֗Wh_;j<ZVEM;n36E䛾emk ZMYuLBRZnul0Zz<[@xIw*etXFsDI!&9qݍb\?@d"M2Gr!a cDQtFVIX(fi*oḄ$'HSʎ0#["gǙԔ,e FM*n~I GpkK[2/=yM6 O\JQK"Lo%w?䉡/0&IKifj%n+)G4MJ36l立1)_>vbg;{P"N5 ;"BxS,zw(G2 9:Np&o"K}Q^8Pu\ֹ׭h ,;;W؀_5@shw}ڋQ*0_9Ÿ Hyaժ6|\񹅥k^}7n@4Տck+y$I9S ca s)ψX@RHH04!753ExOl,w9ۑ /6h ARϐa{"l R3s}f|&y;i6Vp3 4,LޝZ0_SILђ)ř$Iu[Qg H%d]ᮑR{WY( C.Y^,MYWnkؘ**pFfSKq<0z {IԋVSp*z )cq=Ѭٚ,KүB (Fhc%s]:RXxΩt[qo@j`Y&x'rA/E!BGiUV`$~y,a,@Qk{Ҷ\{P{='}~*c t cș{+] &[$u f/T 8礂^,2fPG7z)wa T !۰b.,H݅34 4z#EɋUcd<0V9N,X7NFRwlbAv7Y=JBa/pȁ}uWP=~T >YW[k186BhݱWK<>}#qu3^y\'{ۈ| p61>f-34ᬉݱDwytzK A&k ѡs4\ NTy} ciԵ,C"6Iy*ݶoKUǸUq@\.E J3VQ||bSԏ/IW@kPTk0<ӟ&;MY!E1hMw4EO`P\P^'?Zg!vjz\>limO<Ys +"X\AI)â1Ib_/It+dAWn]MOJAsVt{}m*R&{eA=OYZ:[~ݤ){%\PI!xo2O͈},9#qw7~WjX¨s5bRN.b%co*Pe8rZz#Hb m3׆Ĺw?Kbc3  aE|_tхspj" R¿~ռAo,ݍr+]zwt\hNTohdƍRķYnsXvX0i NFa2oM@Q0k:3~?XNC/Ѩ~I ÊBa13 *w;HQ5;z٧NwJPJ0 :%C qB44jYgutR՞Ϝ飲5@@,fؖnmבyt[Km[ɖA*&o}`Ǝ"KyA^|7Rldx.blRIl!WڹͩCVXĹ2mAŻ} Irnv=]J3*n/Nl-b_orw,7cOws 'WɰP襢D[I+H q"T9Z d{X9_/L2{$>uJzG M0͵'2G'Ǯ1H% LxTі{5.~XoB /&wBWq^}:F|ڊL -e-̂nL2 (u1/6ar@y05E>+JI;n<=/Q~{b4}׺̓Sܹ֝rjb˸{;G,FQg<w)vu[Y Byv< E[}Fɯ͂ݡhhկUs&@ޯ\[w_{ۏ0iZhՔ+ `jg3ÄL %G Pth;e2Cf}[ 'X!U]Q{/ a{F\oʹD^|!v1p ;zU 7pkڥSە=U 8z%X)]<ʱdM Yzjjs*WX*E|8} g#+ee-}CNYW(,jTe[Uzq:Vg[͘v1inN?jU[yS|Z/L\I')_Wzboh%ClI|ƮDŠr'*i!2΅b46p]_GÊֻ,!o'<"Ysӳ;o8ݮšBaCP_k\U= 't_#w r(OoZ@~_I샗W!%V1Ykְ=eVcp~^#mTv7e{ hanhnfMo.wFS=2lg^U IcsOE\Pf-䰝7¥'GpYgZ+p:7OB3\Z/͌ #+gĎ,N^lȵL.#{1﹟[˼әx;'B.=-H\ύ 2I#G\Z@LJGB[y M- lǰi6j֚z^pmnl@~@@^8l>" ΝPiyC70AaO>!?N=CUIBz#l v1n3;Yc ;Ƴ\b\KW;jKT$QOv$}8OCyM0CkЧ!@&4tӻDC-ZBUgTu2-[WJQvWK׽*qF 15ը(_L$E5kG)WzI]8Dcf>\bm4>;GmQU6+,']1ޛékLd/R{3zqD*nM`5ӸձVf i}<.ɷ2Uu}-Uaû3>Pj3 D&dL?RKJco.:='S ;=1>IwWIc "Yqs9}MXR f} 2[ԍQ3єC:X 1_?!X f/E1^~ N#: ojy.A7tLc? Lv U{\tX]C57sQ_D4}@CZ?G.9Ot++l/94Q]RV n*Wqv/vv; !ZCHDoEvL,ه{7k.W^&*I8ZV vE;#DɫME:ka `^Ⲵ,L5fiq5 88y_ 39 3WVPlʙvw x{b1 ݲsС N,rjGK!ʬt^~EWT}O٭]WL4%/JFQ*8S~/5*א;ƗaCۨL`?$X5ΞY݆}82A%*a/ q|3(4kK}QBS?kUJ)3:rBvqojEmAa~AIJH]!1ҊO1ulC2G48+Ҏ!9QHx{`N;@Zם(&lX#)+&PÝJe5ɏ^EܦbXVmdͳOD+FoݐagѦF#/x EE }gXMɦf!,ʩ:` kPְH< $O@MlM[<}^ψoΒ z`ɢ!ir7a;}r7w1!\CAzRH~@Ĺ뇏Lin9!**&-xlgCS S!X._kpٻetC%Z4DS"Ş5ATUZrRT-;Y>gM2@:bdJl^.⚗izʻDJb!  $6Igd5_'Uώ5f hS){̛QG4yv+X%BWRc`R !M=PLSLQG0Q_T4c֏9xExCh\ǖw'w1~\dxl`d­fG ip keux9 k(v=_]aAބ'f7G;?o&6^y+p<;H% I4{&>03'm2dmNDjYK0Z+־jP0ѐi!p,A\<;DֻX\4Ir0?]D>X}2ĝd6%9ൡߕ>WThK<;5xKPt+(?њ9T9y>"|$\ Ý?@r WފE 3hʗA3ґ3G!\=.Q(o!N7sK* {SQͦ7҄׬q$~_-x ,HkT ><*ȎYLs/n^'񔄈A~bo~k_0lΡ'Kze`0\|bVϐ8j-)+kOpi6 : `xBtVeҊi N[]OI˹x阬Lo"ZCO3`Bn'Өoj^p)`ӭ/3y@B_$vU1'/&` +#jQ PR^9Mz/N_OojW 4 4fa.~@.SL.[:{u\N-_f;X;2]Q>[c)k\\XkFӝu")Ac^1R6P4oVmܙK;Oj^Av-o@e,C7SdL$soKm)yU:4ZNx2U G',qXRMuMI}.2{$:(籃֪DmSnx벃|75|ؘijQ>RO-s$0 bq}cs(ۗIxWPcdl+\g[Vd`š; N$_!̀|= @7K~1vL ==;S5Ϋ)G]?u~JMHg˕8r#jjkY!C8$ 2 +c8b5{; +}Lz}-M2h1u3"62#Xr#K 1f.*H3'sgC2ĭ|UY#5Nsy(.M:c=qfn10~-=zi5W@pXq嘭k^$ZZq$x~%xDRW-e{Dʼdq F\9}>d~IֈjwoR`Ek">t9-z-u02B.COW+}af O0BL2:Af~%/kAR&M1^pk'sTa>iu$j4:<IaT2) "j l(&'s1|yֈF ʷ:Q2S3! ?HV\( |i?*ﰌ% 4T:gJhYZ}su<LL%zG"%tl=tSĊ6qgo=Y&EyRJ [v*>mjH$RJ![-Y+،W/p_} *rwm Mwz"F2`scw:^^"_m0RkHR_95pyZT ^ ;$C>dkH*7c$N Zg].V's8w`c&d=+ Lcq9~(-'|q31qݰ+UBǪɿx1'w6.=:EJ/ $ۙ# ,U_WcA Iqoˁr|px|ϣ^ ث?6d-ko핐.4 C7ѐt:|S~ 9/1e30ux4oAPv489jN3'+Zh>ԇ|_6_,z˕܄ihe-kl4nU'@Hg?GTs ~^ ŋXnC!i@BòS*62gP|MJVѹfgT=&M:namBPY_o'ΰ)¦AuiE&y0Rkݮ?i,}s;ӐWcT*`t"2Hn;umd#"k A/\p6'BCK:92(m0|9!u͈V)Rċ}ϕKr^qF=NF0//Ihn[ʷ660])FӫşvMq{DntFxS>psP)Zl`uPq/s "*O_5!ց6{\QtfLlozh{]mjk t-"oߗ{~XF_ID?yE\V)S!WCB9մ1+O5w]=09 S 4Aƨ?99Zc?ct$ͷ>Df>s,AAWUDMӶv AC_.P6n*$ af@݀L*Dٲݿe9i<: e#Br?&BgHDi{O~#+]5tFiڡ{'3𴥒Q}4fNZ)Q5f>9/X||HE{JZyW C;aUr 3j@\ĮN(~N AnD#h'q!faEئ6ɮא>Ȭq7p_ Xu6}Pt qzgpJˋKBdF^/)C=dw` "8ȁ.#@/b-(av<3ZƲ]bfNC]Bc+nW7P̛n'Txn1PNANC3\LZG-cc#A#H:2KIpfU?=Ȕo8i%=؀9YK"C|u3,3 ҋUWWw.na0Q}) D ^Ee[ 73ѓRe,iPZ(/JW߆NGYIΙOQ^ 2ݝWQƵO|TC؈)ӖK >*bxQֶth#MX$"eS}2{VPjDC΍􎝫..v–ͩf\h)KJ[lXȨPMQtŋ^Ha z s+/z_ yjRiG2jh,LYl0@[jT uuPjo6uL/ILd;ILԩyHsM l UdZ ,bae6d+k[7jUk>vU3X1[UO\>0r“J&:++-"E}xԼJseKrt0JLp.Ԧ`s"@0RȂ!Q;vmb9h~i@o<'Pq%UCrs{ 󽦍Mxl r1O "[\#>G6ExQ ۖD3ej9'3ͨʤfnDs-)ijtj?QŸ{!DBmPGn(WV oZ"H`)k֒ӕ9::)R0K\g bmrbF1~k(#ya6MGp0@fP/] $ 6YD}'tdfVHմՌTFṕ1ܸZtf{ ^ޣ? nNLi&ę6Tʗhld a{F彛oY YT9>a𷐞 #-֛@XD-AM j1IF<MP5WWzo_"q:9HW;Ubdd{&``mKZϧmj5wH Ɉx H9$xe- g⌺ejZ̿)>Ǟ ZMjrw'ZsICWYWr%/{ [:Q:-O1-\JȭofrX6K¢Z+/;bģn7b[XS փ | '=+VNpi闺F*b=nM P#h\O#?蕕;G 4:b+,QʦpvWa%/cç2,#]lz$^.4*N.B>oVl-/IΊʀRL A `:T RjV ϥ2r\\>ޜʁ<,SQ 9G>(< tGlGHfCn*~ALua#`@ƌx]x2ULWJ1gJ-?q >j?V﹧V-W\fW + >6*˨-@{(L fJ+s^C0@Jۻ9걡q#^l=|; vzc0ɴj|>"Vũ]":nI&|̀ /`SْΪxyo<ٯi||VN%8پ˛ eR񏐤2) ͙Qy1ϩ Z`A/kVr@;efq_$S0\Э.۶m۶m۶m۶m]mkwN?g2yQ#OR9jAz n@JuN,WVF O؃fU,AM0MУ}=-XBYi%k{)8gWv1ghlU㑩xHB:sƔv|f/0\1N*;@ouwK5SX*<=o4D: i]_g!kqPI̽4cgHwjq+'Gk!UV-"NXx,67Ʃ{gqq&_4/_:m$s+Q>c=3]yǒS&Ϛ"Dp)gdE/LO66F2 lpl5鄅 …YԋZEx%p D36FKiZDzÓ2#??#‹zcLQ gdCo].'.g ބQJ lM DR9)[޸ۅXkJ_cJ8G$(V\/d^I]0_mյXjDaMP u >yán a;!/|u:sGa@EVU/xxOۚzb˗R8TKf 7I!pEFUAVx=3J+Are_PVKp5__]_hqYD}Z@(iiH2 g/dBp߬,u%%2ܺsƼ; 0_n\$'_ah)gW*j~e:3 Y6h& C\w(NS}BcB9uD9Ewfv9"M-1nm2hMӾG8VlWLNM4K-kN{"c:+Kʞ(PrY$,z쫢cbU63~C\'6EJnq[Q+0[)Ft>+%GK`=3r>E=k=6uPD.oQ,Jq_V/q7OzLL6IWjSѰ } /НWd n][W^D'j Qhlj 2OŠAx5!֍!+rX2gve6P[ acCgtȈϒ.e=Kמ%xN Asf3@d0G^1-D̲x;xҚ!^3, X΢;pT6A ;u;k[&SIHĩ]3Möj 6{Q^x%W5 H-xǼ ,*t&!/\ZL/69%LT[jumQ YAx4!Q+&h$CDFrҶey:}ӭx($i*\-7^d;Y{#!dsaxH7CU?yfҫ:]c|˳ߍ3V>rSixk[}jCvU=w@JnmJ֮M"йJxr/khi_:n}GvJuȒ-]{8lWf*F{}cA< ?YF}c^Φ߷6=!XUD^?# %ijGy2$DZ?<( YHVXz208dapG^b8JZ @`g,yx\2~l?gx25@Q,mLELg=CFʊh;ہ=IƄWRI0Sg:C/>zŞ!uB/Ȅ@fd݊ٛBe,;#aa0ѻJ|̥ IB?`j?s!Ks] M{yp_ӠV`Up.&Aj.%UsqVߙ77v:a|[ǗSie=C-yHPLaD 3{Ӹwlytu?b5rsb)@rX SbЀ4t!d–un#~gXO5c6N:D @5i~P@U*Bm툒[%dB[;'0e1jAMfv+)T랜l/`;mO߶]`0; p6KD9y1ש탍}iyl9|!,*Ő.o]FRC7|_VQթMH^h"PxFWl#~GDk!۾clT ;mx)cа;:>B%ң b"X DXl 5s@Pgt+~x(T)x^Ƹx! V^ܒAzЬRzG/e^r^tf_bpG>S֨֋Z"臵ҢlfGC^=2# t(|C!ZN/u[xWQᵪ0z33ruK_6G4ƒp1|#g~3>y[U{gMŀS"i*> g :H90@'/RlI-p y,@AW&aAPt R*OrR;7j1<|%!0"Q05Xy;M t>)>{9&wGzEaà,}0tv[nu9ŷ+Sp? :sr/u/ۚ?b/6 p T>5#Y&D&q fi _t>}嘂Y:6k XvhrR=SZmנ:`su s,vǹqML}u ʶU@عRƲ#7dL%D梇wfvVIn; AG96& M)ŸHNIa WaD˥)⨧RjܐmT<{lDAځ_XpԸ ɒЁW+3mñ}5gMK#vE._HyQ_YU~AQ4RU p #O a:N_rM!Rk;jc4-iŞaЈCf¹oi\DŽz)|էQqJk-?m\2 4&}wO9,C^!jލRT!nҀ!*C-@W3V/[EkT951Xq2 .>{´|!.m=JnMU:0`'1&YfoԫFүؓmX4~hcFv a5?& zh]hξjFKoE"҂YP$[scWLuD)1WҔāgc{ƋkO f6 FU%r)7{GUR7MfNG(/z3RWs~VTqk""|Nr&DNieyRY&`^ E ?P !8򐜵[oG|- ~ l mR..7m& 4 %@j|JD[7gEn 1?I? .`x1B;w>YqT Z!.ZLR׫{~kL{dIbD_݋iXf.p}$4 &?\a~ix &) XCυPZL}Mv*5lH gtIxcZpuF`n.]Wnn: qnxUS m0h,ØvL-9k֤B{&|F@$I N3i]dG+VH]P/ّB,,v[K\4Wkb{kUߖ8\o$*w_ni套G2N!& MH =:$n)c!"}}7GIPUN%DU0D|4rqiq۲п[yEI ψ㦔E#" &pFo|2FuaNC܊e%P6 63nm(aRU?0ŊG^_)Nh ,ܔ!Ȳpy$d/+>XH2r?oB ҙaoY?6/3|HVRWBWgvTM0Y05(LkDiF"Dh%Q6Mh@NC?o;_%Y5:FO9N 6u9'r8ݠaqp͙5ERdE*l9āʌwhZfUc|?ߟWOw %G%[;(bɰ>ԣ&˙#]nӡ{=J! uyﳃ󍘸Mf|芶mnsvn19ԴHeRwC]N!.Wn4o W_I?T WWHoeuLGjAAH4Bv2dm5Eqbh/H ǽԵ.}WtL pݎp\)( 3 {vBB< Iִ(.9,!iq6˫ +-XhuFxK5]%K dxCͺu)c'0$2Eް⌝'lcwexrA.APbнɂ_忀jB" +")kq5V˃v&[&=矝S&\1lcI` MOm#]ŁtMSPt( ~iM,Tn&JwT-g5Hq8%9]|}C#i)CdǺ[>e-XeFDew+DWqDٷBvgĬA࿎A}Tё? r!?I[7ejX0*w+{{ɲnt l8d)"M11FRJBN+]C(72"Zv8ꜬiSc'|=،\V܅Yƛ0T69 ,ä)4cۣ߬v?N8~_BZ~Gjjc(Gɣ@%:EG19'tÜZi$ ?Q+oA 2wsT^v__0Λ>$g1/ė?1mI_C 7bc^3'HΞLHEkV.uCv&_Er}nL`DκWPLzNYC<9T[6k_X 0$c>w~z]!mBtbƝze]J $ݱ?mT2݉HsY[^q0sRLqy  ^$.;};Ȧkh,)Z|Yޯ Ldr:; RDO!vٖ<]1vX94]uQvS[OO,4QĬc0dCkUj70JqZY>g4ܧ)^<%,59eƯ}=9}?T]Ӎs X?YЌ(̉e5ފp@ Fi⭤%X)Qq{x툉8`Tn<:C&hDݤlq@8kLjEv~jn,]Bƀ~%%I+OAS?sOyu*msJg|p(hSL j%+ɤ ^>~kbb?j +1{_;*}Ȱ D]c=1ņCf|,z0to2*DW},NA H|fru.0ڒ3$7M}lj!7zveC*Rp?ky< aevVB/z5Z$2Wi'̿&Pܮ~VR?UwD@pSH|~Ԡ7w=BvVڢ90\Z K?9܏!<}-/S8,iX9_k׎ŀ32CڰmÓrC|]8FlGjxth|ҵ ӖctOChNV88-숉8'S & qÕ8G7; +do&CO{#< l9]Ue|ݛ$Hd_* `"?P J:<ç<ۓ '(_YRI0\_6{` e59̷kvb待AX,loj^~Leb^$Aw,{ҢdݣNRyr@Gݍ3-@Zrl A&F]L0'qOg\zDyW_ >iװC@"ĉK(MY]9dGƅ il .G˩1@G 爽7%D^n~نXmbrᢹjn&+7(wʆ $gSy룞+I.{#hĐ/juBM:?:/xܖqj#WD%j((~U#  4OԗNG;a)3YkM+_a^8<)F(ёfQT'9"aH <)EY]}`|o:SQTlYk)O&c7LmZ^+֙Oa}2M, J-6u:۰u ~sST9ʐmd+>_sR|Z+z0 `5`DR<[2"j%§ ZOղ)AR JҊ՘ƹs;A$ )5$Q\j:R|Pdx. ܋ xKlMbn( *|Z.©j.c"lA[z۫Ý!/dVp"I[ZBĽHw!@Gn|P>ܦ)sma6a1Sm(K1D89H'xiĚו8W3pj/.20~9QGJWgݩУ5d}"otp1piD*lnӁ ړKtx(c !U7">=H+FJoCȱ0z&Ht ASO-:Kr=L[!@G'Xx|cs"4G`tt*Wl4BB?`Sl"h(/cx?a~2R^AH}d.+@?dthI &faD ]FRAIJqqx2#UaU2{ꋿ?@ۑYLUQ  ',4w)PǩvC$K~Cϐc;n4g:CTI(z+,Cc.:»<6u3B?LԪwHXjq( &/"asO}x4N`dTY(ΰBEJ7TUߒft&wOa%kۖjUYȗvuRmw`͏j]#:53)AA7FIg=BZ,&o>zqi@KSeg;H(sIBՔIbF .Wy_ XJ,TRu2ѷ܊2mLj,2d%HI[OJw~N6?t-p\ұ[ ޻Sv-^V0G|^ěgJ,l{67ŽJ__< =*8^Wێ+RFbsl.KQ-f_r)M9N>7U/-TL&"pR*ˀlF=a^TK%^ =,u#kΉ=`!NA^ ´`?Z4dv[^Yf*=U]JA =+q'%i ln'gu` tb0;Jyz߸GrNX'Gʂ /xnI\oE-~aؐ)&vtQ|OVv}5ZԲlF]1gےTPJ㌝pNjGBl !xΧ{}AηlXVZhx :Ȅ(AȂb%jP sq2eKmqsKj񘞊#CM)[oyV;s!Je_]ߡρPyg$-*^n -,/̒ɋdvGgno\mB,caVԦFmZ}* a>0`TqO$B?m&N7] Io8mM*`ISQڇ۹/[]6e2}]ՔVN%O9c \ N< `=N]g\47Z,w hE6@Uc scdow#%xzwXRE#^ET{^ yĚu;,v|ӯN5<9ۯ鉧c>\D+@a`9=(GRq. U1`%7@1nHNy#s3; O|N9ʛ)QUsB7h_:x'o͹JRYKC UMIoc笠Q%bp5SoP [z!gI>x ^<㿳03~syH[dM#i#kJN 9JXk!w~r*p e$l [ YUϪԩ ]vd>?Q0ݱR2J,;l>"hG%\ΪrƥCů hB}󴫟<£b[V!YE:IK-qw^ , b6"r .O2:22^H 3.^wGB.IA8*Q|ȳw)!GuB$M m,51<)$NiC{4э)u Z$ye ww@_3fr[ӻv(1MR%FZeۙ[du ѓl%Eev7D!Z~FTrߒo/*qqQ,u 0@zJlIqɮ8oZ&ϯB ԯU8:Rl:܁-⺖Ѱ4FOB^˟_9AvZ3F[gV 2=ڽk}9ыdu̪u`L6R jC vS|x MdJܰpjۈQp}Sgq ϼ<+ }GW:>6C YODg鰭 rTi^Y6=r WqFYA8Vc*Ia?3yp6& `̄#"#^8!q14[l7J͠r@!բ^,k|)#*U'`vLU'۶[~~s1*)tkΫֳc>vS`q\ȴ\+Ʀn4,h$yC/Qḃ4Z]A˙-O2yg^ gZ7+)7c =v˭D/XòEY]~c[]zW{W|#e~PamsP{?I iWd~}3C VqMl=/ -bw]nC m%e<0RN ӜX oO{b56}ՆɁq›pRa^; 0ymJuGH' JD'i*~@>Þ]x^q| ņ^8yVu1b Ϊj=O70&t_2B`Ed,ljB&ZE{mu~zק!'eY,l@+N( zoK"I0BXl/0 N!:,q.|j4Q7 \V&2,J?WؿxMiU V< ʱ9Pfô 0d7JP#=|B!sGm @=AFi6Ϛ}hx?3G\b@T&6|6PtPRt=9;5d_ q!Ԓqb+_b,h2[1NE Feqf4JhP`#~iiJ >tL)Tuex^ڪ'2x5G$*/':{ZKw0`k? Q<=ͷd7Z{y.lXؠp; N0ZX1sFGqMJ4"wO͞'tR# 7ԇʳh0ܮޙ ^1,>z]Pȑ.9VP Eal<⏽C[դ%K-$Pepd@Ѧ+XbxͮnU@. hM*qŪ}$ZN/%Yhbe6KjvIҩ˖6?}Mz.A>ըCh~zj}^Hy(EԜBmQY1V _IG>vLp*׌,ꌿ4)o;6ϓRYTv|`/;0D˙v+՘G.JAp `<"UO8,lBmG9E&Kinஈq,`ręY1 Hc~%Vz߾l=sބ9Xf6D Htj:+XL2z}Ta@Edo|j4,t&/v`'lcuBy,INr--yrOBj-U୭cTU.1Zi uo@BYB! s$߽yX <M檱ۛ3Ʈ h}h,Xh?e № s[8}&g5`@!&LdjN㜧J XU3z GxC{ NS(PLE5]ݵ}?'Բ5eԕ 'y6H$rb*L :-fܩ3E8VH#G,Qy?zxRo)Fj甬w81qi>%d|Nq`TI=#2oxw':< (H}jEEk6k vk?ɼyY<Z.79bs|n-#fw*436)P}ax>F(21n{Xc঑̞96ګ3)$rOp[鍉ٌWE|5X\x>0Qj-S:\w,VOVZp\]ˀɌ>+itjl:gHq¹:@9 bj_='1Wڷ)|쇑0"yu)~VA?@5Y})֬>>ګf ^?Yڭx@,QZ=]JemHBRvjz ۢc)es1O(hj Q0o,3[pG%^f9K-`),ᣫ% SU robZ{鶕v;&^AX,%L+c ܼlk҈/D c[C`32}Pf/ZZB tp: F~+^=:'cqkqgʮSLe RMG]ցl Ǯzui xoz{ͅJK|C`5^kP`7-+Qs\kK%k> cY@D F9qS@B5 %b۟AlΝ.UDyt&$U |I.icWWjH=?q;(8K0F٪C_V4 _ȼVd[Vr:5sivOͿu|kV0E;#>f&R@d#YߢV]ʹx']:E ۺ:բ=;x}Ww22r'Pؼ.n2{v3 XSP1T G2ŕq [ ?;9taE5#3>k&X&r$v؂hŅu"ߑdžc!U*w4sC\!3Q .gH N߽p ~?Jy:,m=q~u6&] ^z%IN_`Z "QZgPƼ:n"JQɞ1t+ChIj>sKmuTg"px~R\9j̧/+ {ֹ͡p>1 fOpZ#=Ćev>ɩF>n_WF&vc]6e{K0RY~tي͵K CκwB L0 N]t/ YE23 \̶N>&alչ]HO ZVl.&;4 Ä T6e#|.C*WE9_/HLv`Ӆoh8cEA!vFM!!dۛ ф3|l'/~ˉы)pp n t+D,T3iNvRq XMle}ο^gQ s_ekBaN/9)Lf;9Q3oF-"*-umFy`f,.)n+ E|  )6{yo*`R+6L@EW_%5SbzP wFceopՍh'X >ݹ?2NhY; iފ&}3 HZɚʲyay`$=$G҆{GjB;J22+Ul9YC0XxG*޿Cʣ_\[ MhOJ9wC3hmW7vJbOFx6MuJCk`1NtjvsmԹ%|@X55ס3*lT^Zd tzr,ry+D0nUwph\9,%/3]@x{}ub ?SeCF c*XЧNZyKp8\rv skm/AUI&~:ᩔO\9\V3ua͔>8'zs|ۺ% `c\Y یhW13(ϣ-!$gsܞ|v |wa9q' ˎ'm|O{ї AZ,-/ʰ3u-RSIUg4TI(`e'BIcgi9t<&F8[ A>>u5e#ئ@(_@"Wzp%$n;E&o08NqlU1":G.y"xyqpN̹w%Mhh'{NƪʃyNjgӭtx g n ȱՍ座]Avv+7̫TUJh4Ely|TroԶD)Ǻ2Z͒EWSkF|L:Iyˆw'j36yie-`lUt/a342<'r뇹G_ɨH4yQ2QR,y:qo.hPt,0ez=1ǐepݼ0M>Q6)x4ӡ ı (ze"cu[!=SUdo@FN<@*I5tv֕SN`[K9Wmhѿ\X /HHv*c+,KXD;4L>vP]x2/%z9Ǽ!p~#iFliMۏ/\IiLS?xmbRl叢XDžnF,djPGR F* 3䌀0.Š.K[Y%93?At$L"No 6/ cTM,rX#]V]ଡ?5܁=I*#-BzBLkZ狤eZ8tM _Kfղ;8ݪ$+9T"mO`ޤ)tPb$O*{ /NɹIic'jFM̦n& /ԴF5VD[`-wb 峙lO6`voI;#p< Zͺ 3!1M!Fi0_TsN$OxрXz*z9#> 1tsj lJc޵.c3T[>]t)r("m|n>XPT^5bo]V˩C nc 1OLN^W"}HBW2 ^xqtP(uKk"*c|kcj- /*avJ܋Rvk7n&ɪe@E&⠃ 8 gi~ތ1f1ֆ+b*:3@)j(>ڲ `}_ch03a ǂHXPUMR1@D8donicƘڼ r";L8B7r}*Ukӽjh͈F$4?on]7+қ"́Rzێ(ՃGJ՘)f  {.Ԭ09(E1 >\{DZyÇ _(UE1Cyl3 f* P?Nd[pQi}k>+|iK@ 3%dʌCp *>ZE)/5[u#Upq\ 3שDO+#ku&&zy^|;;[*= ExX%mOrw7)2nFAZC ҩ=K*Q+;mec,ͱMZl.}]k15hFH#Km>*oY>N֑6(>\h%ݩ F4ye[dðe(:ʶnc,LRuWC-LVu6dk$p~閿V\5tV-ю@+.K'_3< 9!gUM E0anJOXM0R̠c߮DmNtjc)P :c* Mc EQ 3|V;`#oM8>91J }?9"x&:EJ  nڇQ`~\|6XKHoa*SE#2$@8,cu8!<zmbm9,'R:5;0զ}热9o%#5Ux+QL"XւFM搟!p؝IJX+ZP ƕVwmou3*Sg>QG?|[z |3veUaص6د >6r4>^ 38YH0DD8dԳU2jnCuT~!FhnNYr\8ږ̙p_wk~Ӄ}blS2GY:9r*՛F[9w$m>}_*IuzE-ґM-I ׂ אBNҟ/Y ZKyVٕQ؍zBYXC4lCpWyE4k嚳\}FǯL;AÑߪK?L0Io G +eČptZ½9aܩu@݂Ul]<'m )&v|ojT?S?QYVe)g\-_źސ7vy5 }Y"OQM]84G9#WBm>+dȪ:)y „sJc_QC3WoHQ"<^1*ixz#9*a,[d4=0`R>{X!P%nk>9{:34aHy_19-8C.4vp=1"Ӎ-IݍN!3e29=6t;x>.ӥq*nev7ސ}W uuxc_M ^"F]90K !^ьߑx esj.2IR:R/kif2D5 9M[kRHȂ|82:;-lϫ>&mmȒ7Y[ %l?k$Y6D5TsoPr -׿,vU,EbiO1v? 謝ڵ(oU6ȱdvPjTMd(3K ݊6 VP rWj$ھfQ!_,yNiH9ƄH5]b.bL9'>P1=܂=BEKo)b#S"b&}r¹L8jR)>Wh'e-SwOFFUРĸ+R!Z 56#^աHҴ|XV_O$oBA4Kb[kQh ɯ1\yqK\uq =-SP פ:xHW:3e[@@g9ജ\As&˰D&<⳨tE$3ֽ ?J%EuMJ˛6} 8cLcR1dFꮾ' ،v%)åGԠGu͓V_+!vt_*wT%+G-Ա YMykĠo.ᶆXOu7 f6z7NGЇto>vԠ'VA]a/8u Rɓ(PQv P 8g]$C}sxC }kO0҃e Q%k;0*fG A8GM(WjG܅P+x`\}e6w\rgWA5Z +%ʍ8 CIv'"'Y[/'W\Zfg[GVC{z񗶪ꋔ5BqH`  GZMu $pk'yһ?Gp2x.VA\vQbF1ϟd?>l@ɍ& jo>H96%;Bca5L 9ԏߞ=qgc5Vdjھa Bs. JZ{V?F"ktn/cV0xaw>JӾ2<ѺvjcQ ' PK!s/!OgqNrCw/_Nߠ_\&Xp|xhdud;7/v-l`{PO([]LL4ng&@a03=]/|o7r>nJ^a鈂Kk'Y?ݬ ?uk e M]VC{DwV9[X⾽SUC5L ^X&>)S?Ta6+Yw5 Y[ASnƢ/*oٌ~hŔUg\AAemKwoQ$[nӬVұ|Um|AeLu;x+8h|TG'Uҡ*qD癠˾hU7?(B|N|*k'h[ɈL(nBcF=Ueݳ5(̍po4V":CohU} ±4Xwk1(~3vZHngi WB{EnD_^vb$/+TwxK;Qz-ڢS⪋e%}r: >e,=$ɩrݽS>?=ĚCD>XӨf0E,d:Xn3a |FjΜ-PKU YMQKt`r,'.:܃?1X&4yu*-^e $3]:&8Gyxzm~D^&}` +vx-5d-Yn`!#A@t=@7/Z_U{BpB ϦF1Dʱ#:XWidm=_tΡ㸈~ז{B i ]G oDLm7e4]drb]1C/sm0qb@y6<]KS ]w֮e}aK|yoݔR&J]/mˇJ!q?,l g 'myhfUFQlH~C )&+visV}ΈFmd}@$ ƍ:Dt>*|S#ܘq]^Y/Tpᨇ}} !Ճ٩k;3zM;6;PI'ȠU;6TRӌxneh扼i0q88w#0Pd6<;͍\(dVTHx!p't.Yd&f @]›KcPTR'[=\+:& /A^1YthrpO*ʴ3~:\kNZ{ 9~uxL[rHh~B] x$g b.*-'\-Ⱦ\I9b58sVʼnq@gV>B$D~9 z] rx^_zRC8v̨mIqMJPbS+5Z{lkA|7[Kr $|rtx4'2])X;%9^0.#kNV]nN!"64vdKrzvN%iM* d#4L,QSₒJĪe\K9E,LVRl9Hx:IPm4vsXq6f訳ruX3ǃj֍ɕaYC9#54'4Pm*Ƕ7c뼌y vAL‚2Q!rQ6 %}W/$aRZ|܉kmR&.GCY]vD؃`? pJмw+AO1n=eV!˛tJm zhD V 2Q>V!-Z6d6P?$Q=Nl~8x)~ӗ7! xS UY%1#'lbnS">C󸒐n[Mq8Un߼g]~$€4%", ,w_8i"ltA΋StMdaߒ a:iQQFA;!"gIرj Xb!A|a1-@)ͺ(VFJX{a ik0hZA)f8 zjɊ퉤3~}8^r\ujSW;j-shvjNY>Vq+,--nF*vQ%1s /JxB_TT֣DɌj}ͯ1l (Kʠ/$Q"f"K.i+"Ř7@}IđT%*ykP ukќʂhYgTRS9!ag*K:1x[ڇYWSslFl@4As},Xx[iʦ)̖^hO5z$wR88*svh@2Ќ&8 ?q%|ENTT3%_I2DiTNYl[ļVx"_7rك5,"'q2zV 8+ *գaZ7Sa;J?w7%x W!> j0zʡy@ EDg~~fYb"NyP56AoD.g)@,SED)`S*v1tcm2}KXv,Y]rUWHyX ~RFޝTG׶f,`iT[j rve.M$~ڊr`cL)k8C5kJ|Egb9iLp\uƜ% %B\(tfAZɼ9!QG:E1F\n DL\&b<i˘αVx(U,q|Bɔi\dTVx= ,.+0_Snx[S .S8>U`['COΔ$@bug_4(TA=@Ux~#/ 2&|H:v:zO/}> ~Cx ,t=&:L.a4ӧ<r >6,}QӕoN\B@vVtih~R  7MFL:㉮QNsڶo!fE,о bk\kHl&wfIdC{Sb0M6jro ƴhI6DzޫIahg.WsDruoEZqiE XmE9B5|.5q'[!CQFJo48εU2ȚsB{Ͻz\Ҫ/%l~{`%V]z` yYQ,\E܁LEIDm Sqdl-rWEv(5Ls%ͬ QA憀":՟3v+o#B=l|+Iqȶb-Kq hryi;[^9`Dwx?T UX ' 6"ӰD.|A,v,Xl.M{NM7}pUeT @]`kRfH%%AIB)9kx6-KVۚR%7H ;AE5L^PNɒG btN4i& ]rP6$N-2O g*bZ^0틺RTPkM |=i9a&/rFE[gN<#v QӴu<żl6핌l1mBO>/|_8"sOrITzJvZ \(ID.Bp,c-*Yß#md 8 X`ع.uWr[|84XlJѵ4#hOԿ@u c!llb1 7Ҏ?eܦ`X"W/v2kd/+`Uǯ!'BPw ??"dYQ~MK&Ȋ\굷Q".U7 YE* ؜\5v_J3<L%\=EqUk@B3@8͛YzڑZ˸vmd U AAXgϠsGCId#Q fWLp#H'Omh2-|(6CL~PtO Tjhx |j/ՎM~XXԫv8ב hO{m36[6ekYDMiQ\F%е ughR9cJf紌`㑺墐 W}ѝ+L>nfVRwF]ˆv;ĢO"MUrg$ Tshmbk`H u/c7TBfӤJ GTzj3Uj&2z>WRҷ.c! ̙CG{ e`Ia\-~L|ʞ05OTu{aF Şuw:Ec|ݮ`OT1B!;Q17Kߔ3RfxU8RfQڼH52)u+^xMd5/?+/?>f4~D}8šS ˅jPYjx8rjd1ZۅXN~ej{)dh5<ԅJ~S?]#{[7~GeHM:Oo_-5s<s$V  ($G[, B VCԟ:Qاh $E KV\cUf<1@hZ? v 6𚖨9eۖ]WzGX5nYuڧҹ (2v;NŖ4u}Q0kk}>ne*}'OwD6:ϣ.+t5}RLK\I}g7<95 <C#We3*COxי=S>|rxA~z"\ GK;#s; Tǔ YG^S"s2ۙ&*#%.~ -K 2c3iPWxTf0Pg|jj%& waw1F 5`Y)> j(G/+*Y\#ElSx57xniPH=\cs/Νh%rBrQ%~~;>H1$_(٘'{ l=_N(@R..?3{D+t:g9D[\]#xv.=5r~Ku"vAɺRuAыyڥX/yvQ r"Bʯo7F":BV<> aB$=е(c1BR ^@1AC؈1LDg6`5`{Ќ1?Lt*|}"r_N b8ŗbKV08h!]& u]BuM='1~bŝҩZv@j-L{]#Ui[NDP4 }a ۃ7 M0zM)nrAަ FF#;5zIޑ89ǖַ[Xn(Z[7qգd,( aS3 ~s`yx#+ > 3g>shEMT;,`NS֖s|ad-\ U폸4I> QO|ӊ)2&YӬ|{1fU(kf~oFEmob3 Z³%$tЂ^ &g]mvv~Y8Zbn 8G^%В< pbr)6&^ҁ0m5@K(sMY&6AD˺4mMfE;FγwFO f6Ĉ{P=`DE!;}+4do.':.i*k ^a Vv%y.G`bHd leAy|"RgCJ &: - +"_Ӹ}ܑZu#z6$r />at]ǣXf ؁ƢsRMQz]w٘ +7M)kt[9Tot7λj}(& O楳 j9u*=rkn㮙2 p`EK?Uэ"fZz=LuϋH@Uς\AFILUgŭⱚB[4G+S|zVDglu#XRW26PB<%̖UQRgPW)`1]Osok'o\tg{K94Y_A2 1)/ByAH 4N] Iu>6Eroz兩t;1} !7c{j?O䇷O#f 9vGʓ d&ƞed'`2 쭎ɲҔ`J-:] Œ{Rd^H$t( G \րa|Bv^Ca/}o)`4Ac#l~o-lK-ĸkTWHfXioXa Jڶm۶m۶m۶m۶k۾tI'uRVv)ZF \\QK$"}E\yt"Pދ{rMF3X0N^YoD(1̾*M}؄s=#:ݗz<6^2D8zsGPxEmͧx6[VSڷ1Ⱦ^MⷱH[~NsU*a}ɃI5)\E?lWG>0q)gTnׁm Pi4436X7WH|V}$ej0tSa<aHI9L=g[ vd:< Ef87偗Ӈ,Tܤoƪٶ|/ԕߓDȨ?@),"*,b6{)"O_;uؠyB7XʤO`NKNOO"qSe|!k& 0&ƴBmQD)ؚYQ(R|x(,-iG_:,Gki|TOfzby73zȦKPp, ݧ7+.@>`U6yH?̿]u%4Ǩp ffP *b-xC i)`Js!̮o&Cm/mGY蹭2. #E^6usHqx *+9KN3IqVu&bDle\i}5L'8FB]; =u@}bI%/3~s=aGFO)1~%I&gWU7EABf1xkjCg%$H400 -(QWpD@KHu]/n!'ˎ[{尲H? ~7(FT_9sf"ɷ"]طKJ8@DQ5|.%QU^ôq- LBt_Q/f B2@'L\f?~}Mgւ8Jdٌz9։*S|w$7V,\<ؒAwdž1*-Lx0DVPO6h?d;yw>kb9xS\scLʸ8?pUt)`|,{Sb HYT̥ -4"}^]wx'ddpIĉH\lpV/ߗy]G??A6y5GEsJ=/¤`Aܨ+l1/ӭ$pI i5Ag>36bK1F}`Bߺһ"+m VdݽĚ2v B>q OA!~˖m%'8Y"<JTA" !“AIʞM%>C؏Irq򠂴;t!ȝ8 Lx{Qacd&9b(i}sSAqvVU־ ƫ'=qxPDKzH3hTgԍ$Q!˝}+؞PQJ9b(v2^p<.FSN*Dg7ΧH#F99n nȉ1!aE?FHXi`N3R6-W:yءT=VspThԲ18sUDqu.$ׯg%Z ,@dQtY0;ޓAB č-,j>ѵ-F#pɧ%ZY~#t/)ӛZ7#FL.|$}ʙ H}x#˚랚U8șMsŒ-x9V7p0 e<p d< AR;~W)b57 F#(z1)r*"$ܕ@fG5NBl،?k& s|L(ٴntiE`YvC,4_ Q؋K=*d.!6cٲ-G|O 9v$؝kU}A|Rb&0 %m m&3R'N'W !re'IkA~Khfؿz.Q6Yp9Q~_2ʶ{]31 70Vj:Z]$JL217]8#m"qT>hv+-[ !mI5L.FXP(θ#N]a$k(ۮ8LngQDNP._YW,H_ؘL043KASr wlBq" !)ogF@g-6f4G+3M#3AaR4nֶ R{Nx*Q]aä/fF-04bH)Q6$6sQb2Ї+%܃E"}U::0$n9vMɹLkZzc&\uEܾ@xXOX^;MF9Y/T@vr9:"ŠR2s"l\x=he ۏE='m3AX (E&? *R:TN%mD{: I-^;b *fcw":&RxJW#gHӤZH;VRΈ/srkShw10ɾUNUC!XYSY?<~F9feK2Ù/] rN_ef0#,rr 2oF\#Xy`xmIjzr`z?^-BtϠCmŭΖ>C w,&2!^皻Ҙ}ӡ*)XYo՟+8L4I=ka: -FSӭ~5{BIUnOGt.q :@)ߐzytp~ݍI;tY-Rg:>2.Z }ZS_~T.۷77"(?Ӥ^=ڞ]rJ-Je4H@L|_@F MN=V^ll<}}bҵa VLWYPO9_Th5eV+&PmTI/씮-2'xE:d ׮7G= Bͮ 'zEKK%\>[l$ļ98T;~R= >i[(.2O3R^G}hMM撦y;my1\\ QN>n*DWkLv@sIըh)wtIxr_J KI𨦲J=ps H`L S#Mf?ݕ{6ySv299mf{_`eh3uOu}3R:2)2{|(\`>Gm=m5VM]4qk Ʌkxmb)AS\z N8(\=^nXrcce?̲?vQ"R dj3)v.ae/4 0Mҫj)Ot {`-,g!ՌHDD$qv4A/Ug=,rx) 7ү8pkC+o{lU$9"<izWsOc/6wا#Y>IڱHs[!!j5M,iSU! Q~Kөkg\\MG딩f F P?7[dKCu௪=`2q{'-ZgzBO*N 0*> ``MVln'/oM3ٙw~?~[ ЁEs+4J(6 l^Zwq0_hVC \ٌ᳑n(R`Ai"Zh{z"1ϲ5[&۞<+E!k4> >JC2,6Iމ0L~z0(_$Z竍oJDφy`cا;&ͫ8v{,*Ň(BcGvˮK =j's6De3i c0Ӿ mgN-@co:f;Q)!>r4`r6SѓlfGM:BI:Qgt }Azn@Ju^,WVF O؃fU,AM0MУ}=-XBYi%kg)8gOv)ghlM㑩xHB:sƔv|f/0\1N*;@omw[5SX*<=o4D: iC_g1kiPI̽4cgHwjq+'Gk1UV-"NXx,67Ʃ{lhE i!^L:m$s+Q>c=3]yDzSϺ"Dp)gdE/LOy1C(ϏfG&ۿ{ց׺zԇ$O^ͭy[$7['!b:a!p!g>7:1ny;pI>$QbM{AVQDLψdAq8i[l*ɱY7oRA[vr:TN?7nv!֚!*$ʢd. WEAW[u-<?$fbXEhȿOpC؎hEy;6_]Μ+Q%w@BEUgr S漷`fe;⅒9­tҢ@|Q{>^OLJ`٤W;\aWץZ\el?UAT(=1-| "3oyr}lv:=1jqC[v["W7V( x* )00}Qi7/i!cQm+ԁGITY9oSZc[v^ў I*P,Zd$K<2_&^`]R}q' *!vhod ,~AB?vǬDu%oTcĹ<$,X_ z'v}cl*i +;c< {^=/H!K][kYh=1$XR܉=?>Π3< g/FezMضs`9'rS!*:?;p% )mX"oO鍇0X4 ̟7IRֿg2``8^B/C+!Nq|7e`❇s6"X-DEhBuYQA^l6GAJVZ7F ]yUy_9FAQH4缢 {9U \4}\qdɑe"[mH?5"ae_Cҵلz:i.R2FXtsز/XJ1O^)9B_ɝ)϶P]{R$p_|`I6z< !xcb2-IR2퍆UlUu &cp.ڂ T#L(3y LC޷Q0aj iVpu-lzi"`#4]m! =FYS,RX t %gM 6OCW.BcqL2RcƑ R\ NjHa+o ≿qOɲ&-w dG-PJդq ,8W9jB.إ%KJ#KxW\@3'ZCѓ%'qAM;|!!1~} svaHjwfξH6oti٠0";<+vBj[ X7 ctٕ@m=^{9&.ͅֆ? l=@EȘ5gw_xp<+KwNF@lX6qbƖ `<Ͻ_j5Tk*i$w{u 7U;ק]Լc v1g O j2+ݫh`+ ˆç/CZLD| *m p^160"( rsɘ} Uּʓ` ,ǤcVrx jǬSpF~ z.59fJkF\`8G;dQBk1Qe ݧ hnQoKL'!v6\`EI{'ⱗo_0xr#Cs4DЙdㇼpi1ؔUrGؿ)aB RY60(@u0^Ȑq!ZJiq.N MmL4j$'mۖ7g2mrUN[8Mw.} \\捇tTg(i~;?& '<8csL,7F?x{&k194i'o\%{74Zql֦Da;?BVŁV"v2) u$G n{aMӸ^ {Fɋ7/Z΄kI#a+D`Tm5楰7U%p(QT{v딍#h JL * ՈUPx.!F5Y/Z6W @MҘ? A^aDy,gd t^1{vHhkE r57B%)6?@ΫYN H=br+Y?tP- e Z+8.1jg~~O%O f0B`Z 3Eꅤ?F/Ó-RU&./fic*bl? 6-д\VGۑIjE4&J9 q\L]L ] { 4ֱ^Jw-<`OT(Qmgy_je))"1fxHEx8 I 1R>˭J:Rh(];>C Wv9#=WB=<)AL&6oC{N~)+a;q:^vw3o~o!t$M/), 3+zto4.Z* `DDf*q59  &SĢH$."S`ZLY%Ġi|WC:Ȅ-ācGnΰjlFt&AjN] :"kT(ڸ;%"Ʌ.-wO`2+bԂ$L9.VR=9=^w$"m{"av$A\lm bS;څ rBX UUK!]{(@oS[- EE<6G ^kB:}ظv;Rhasswt |JGQDP @j~Jb-$W$c%yHP٩3)x^Ƹx! V^ ܒAzЬRzG/e^r^tf_bp{kG>S֨KZ"臵ҢlKfGC^=2#ts(|C!ZN/uۛxKWQᵪ0z賬ru_6G4ƒp1|#g~k>yUΚ,uD=ҸU|\t0Ur an,O^jْ[>:qXXh2L‚9z%mr[{DU+2jw ; Ko,@bDy5KB`Daj"v(|S|fvz9&wGzEaà,}0tZnuV9ŷ+Sp? :sr/u/ۚ(b/q T>5#Y&D&q fi _t>}嘂Y:6k YvhrR=SZmס:`sug`U78ܸi> G:LZBe* G\\)\cّ2"sûz3Y¤DJw s⣌MHbuf[ O?$0+0q )I5YnH7mT<{lFAځ_XpԸ ɒЁW+3}5gMK#vE_HyQ_YU~AQ6RU p #O a:N_rMkB\vvhh[3l7׋=Gt@#yFx E>i|sU–F)i᮵w&sʀLWӘB=y䰒Ny35b{7GRCJg_oWVybMF@SP_7kbˤG'?'r`@f 9ȿAzj+ɏ 7^Gߧ;WNV; ĘdJS[aHjOB7i1Si67O<њ07d"g~:M.,ќ}=P_s3z݋D׳I.T &z(RcJ[!) +xލ9֞ oZÿ l 6JRn,ҫ4R]{oJ/ ʝQ^f<~VTqk"*|Nr&DNimyRY&`^ E?P !8򐜵Wo-@[!7؊hX -qܹx?HT .K/^eBL V:0{tH xSbv!1D^dJ`$"&i;ҎelLYyM)ËF.EF1bU/9Le>ü>#jl@^nSqm*gݖ5RQ&~HaϏ"ǽ RAX)AaC\em} Iȿ` @Kk8 2+ Hg]=!fؼ nLC"[I^ y^z>VS7OfŒԒNl3?Y!TFD5 $7!9 E4~Pdռ'E?8R.$'ԕT}tyrI>7gO`bIגe*3ށi5MV1n|v^w>݅O4lޖn؋e>$S,gҏt-bķ˹$<(OhBW{g0[}P^$&d"D]ڗLp0s2oM`lL\߲?D*5~;8Z3XUs0U  ؖTv{[Iv*/[7+<[eeКrL'%R$λ%S"3iK]w9usW^Ѽe_\T ׅWWHoeuLGjAAH4BvE2dmuEqbh/]H +ۃ%]"F7"RQa $Cgv?CG 5y@NiKQ\r YBӂlWWZ3huFx5]%˿ dxCͺu)c'0$2Eް⌝'lcwexrA.APbнɂ_忀jB" ")q5V+v&&=矝S&\1;lcI` MOm#]ŁtMSPt( ~iM,Tn&JwT-g5Hq8%9]|}C#i)CdǺ>e-XeFDe wǫDWqD[ٷBvgĬsA3_GѠW`c>HIQ2rOMl{YFdYR:6oK&Sjnzym~!' +!ś V-}QuNV4Aݩ>lFQ.C¬}tpUdtPI[aҔAPzoV{`N\xM/QAR+v_-B51RQyrnyEG19'uÜZi$ ?Q+oA 2wwrU^__0Λ>$1/W>1mI_C 7bc^3'HΞLHEkV/uCv&_Er}nL`DκWQLzNYC<9T[j_X 0$c>w~z]!mBtcƝze]J $ݱ?mT2݉HsY[_q0sRLqy  ^$.;};Ȧkh,)T |_\)H=˿f[9vqK ư{cdtUD9ثNm==ҲD J _ZD ~F-VU(ǁbkedTpNxc"ׯTv< =R䄞~.}X~';`Q^˙aj/4[MK %RU.0;p+"\;yut8 LЈIp֘Պ.)ԌݩY3x&nmY7ɴbH#AjfaČ>r1ZQ;;O*XyzT> Xf}e7er5+nh;^5*pE/"D̗UWڟ;|JJV4%*4~$26RUL\;d)VNUЦ0@r?Jn9VI"46lS}*7Ĥ+~6jk5 42WcvUaD)?*o &z,K-b: LX+`i.9*>yyxq]9/9t6+%K+C-NVqp!^Chh!Q|Xc0UVXD[Б̖֥\d %/gIo9B2oRT2$ xD; `an T^k&$BH>t_@Ig s&YqJX+W5#P#eL!Qߩ.zYiPri[+p,<KCZyZ_q YҰzz ׮gdiۆ'q6َ"vP%IА'8bk) -ǀ鞆&/;i[ɞS0hx^D#&bL/0 W `0|6j >~TL N[; .(6wJ+w3tPFH5L!R7Wҷk9f$#nci6*̄ͩi[)lUuŷ!t_QU c)R*sOhgJTb6L\l뽌NoGOi$4K0s%ru{2WQ5c v{#N9-,~$ބcVڗzfL9D~ݼqfYjez:Ȯ&o9ԸAR"w/)Tdį,$TgBa=fls2M[5E~rbĠZqZ,H7}?S/Grd& Ev Zw1/ ƻZ=iQQ[L')S2$o.]Xrg"Q[f{SIZaUϻ1.CY C"Q)N1{|g7xNy[Nʧ*)[ͱ+0 H\DɴЧc{8˿,[g_^5*}x7pmpƱ ]K\jy(j< iŌ'P-Pj5қr';pa9{0QU~8 i5ybFG*EQ! ܢ֮̉Wg!v ݃-mُ[LEQel= BHL3y[}jyUZg:OX n*MwW ܓZ2$>nk B #a49GA>r̷ۄǰNh._cv#i,ik^W\AVRA"yS_R/DQ)_ufB[ghl!LB\¥kT>'9PFAMW|C&oF:|z.kWކ !ca$OP>Zt F{CMi0ODh+T,y/Nei~ #@DPg_,3(~.3d4/)]V~>ђM `D ]FRe=el+dFrÀ&e&#'&n##Tl O1-XYm=.SSjH69- %Bwlt)PVYa.:»<6u3B?LԪwHXjq( &/"asO}x4N`dT],ΰB_EJ7TUߖft&wOa%kۑnUYȗvuRmw`͏j]#:53)AA7FIg=BZ*&o>zqi@KSeg;H(sMBՔIbF .W}_ XJ,TRu2ѷ܎2mLj,2d#HI[OJw~N6?t-p\ұ[ w$'/[05!˭aꋏ9+3-8 -:-QޕX",lo"Mjtt{TpWL \Z2[i;R0r|4ro"WL_W-MDU?ƍzüWKt$F {YG|׬1Нwo(n{'8׮{i/ӂjQҐ=l?{u1Tuq_0,zWOJ{Ӿ(Nβ,(8an:%ĕ2Qq#drNX'Gʂ /xnY\oU-~aؐ)&vth('|ViiIBjg..܉3mYpx*`qN8IiLW5#!\Sfc_Sߐj{iSRGս [P6@V]j=S<cju&2m87JvYǶ;\LR[\Ҁu<kK~V[\Rfws r4TGb`"G@J-i;ee.iC^Hs%6Я7կ*Q[@Nv5:}؏qV5]"$TM qVzJa?"LdܷeU xV̌0:qc|[۬JHf0tiގ%^P5XMAǵ[7ZVSyn_!iCoú&߀=T4²3ф8:)Y%QNղt{'h_RݯvcK~'NYh^V7.]忿#ayچQZN:-7J%Y\÷z}@008ڧZVG6QvԞ`{zWpx(CXe"̞RX2EKמZJ_PzE'ˋQf&'k?N=g\47ZE,w h%6@5c scdow#exzwXRE#^%YT{^ yĚu;,v|ӯN5<yۯ鉧c>\D+@a`9=(GRq. 51`e7@]1nHNy#sߥ3; O|Nyʛ)]QUsB7hU_:x'oJRɥ9KC U-Ioc笠Q%bp5SoP [z!gI>x ^<㿳03~syH[ЍdM#i#kJNMyUJXk!w~r*p $l [ 9UϪԩM]vd>?Q0ݱR2JU,;l>"hG%\ΪrCů YhB}󴫟<£b[V!YEֵ:IKmqw^ , b6"r .O2ǭ:22^H 3.^wݖGB.IA8*Q|ȳw)!G B$M ,51<)$NUiC{4) Z$ye ww@_ޅ3fr[ӻv(1MR%FZeه[du ѓl%%ev7D!Z~FTrߒo/*څqqQ, 0@ǵzJlIqɮ8oZ&ϯB ԯ58:RlbJq]_hXNF' C/}•x ojz;-Ym3HrW^ľ%2:Af:&z5Ek;)A&2Q%nXN(o)ѸSoʇg^Ռ}>= BR!,' хγtVkLzzCq4p,K98֬ 1Τ苰$_\`EĆ콳["w56İxȏh_R#d٬M=iGދA(Vci'PWmYmwq 'vsW!t_[Tk&VK,^ز":.s5R.Eh5lL梸!Iś+}]M,V e zs vʘTzh 8/ZgdXFtDQHP[Iªd~9vҼ ahsSٌ09(dxJl?2aAT:=kHX0foVŚ6]E!Q﹐7烧;jSa3,MbZpYh: QcX3&^}z 2J[W  C)84Zk%j2 !'p"bT)hߩ'$b$`[,c&Fv*Z0,#$3{MXQB*K6OSjtPDdJ{(V8Y@A}<-y"5Q k/YH}$SkDQ$D7ߒDh[I#CbcVzYL ,8[kIc1Ɣ~Ϫ uM\*ѐًܩ?84{J σP*Ϣszg6Wz!dwB!G*XX@ӛ \<@b?m SWj.E>4/B͓J‘B\sD;shbnK65m1 r7rU^jMw;Hf!ձ,AkeI.[Xn_uVqN/jӾDC)lKΊjzM8۷X?Fg2[SfdAWgIy;y*EH5c?s}I)` ޽0&oA7$'Z$۩<tW2(o5Z|-h,afj .bm/6\LwEcS'Wd9pF ð-9eUd &Ob6C'd(@#WYb( *'~Saٸm;W4y47a;xe_ljukeLp6ɽl{~Rlom-*rHM,X$!t0HLzݛ7E+SqߪiX[d9c&?߇Ƃ6 S+Nh0gcǓ9q⮲R]m[8}jRi-!\KG5Z^J>Dp-@zLOh7 A8LH\L]DجΪ/MTܥ֗E7q5VoفyqaDvK4y*$QpZ5?AWh p1W+@_ђ9o9y,(w9nfBacn?RMc\r|/s8[un37$di^uO!d w$ޘȜxU4ȇ_sŅ3E[փ?U}g"outaj 7~ H!sRFJsv)zO>-ˑ$'jgG\>hD) վmHˈ`?\Y+{WM ZwfdoԔT]Xr^'7[L nbӥT֦$t@ e欗])-:lR6gTn֚Q;#Xb9G~Xjƙ" \>Z7kPI %&֮)l|n[m_<`)k Ų[ƴIp@0~jv!8ҨM[0?56#u a5%0JH `$?qC2Nq; ~! _vܫ:(^Q+ xeȖpW'W[G9 Z1T [Wu`. vвUL>]ʵ}]6kN:uHT-ON$ bW;(T<دnpQ- ppA&m[N4!]@a"[M^F;fqu]ģLF}<^V} GW%/ܾ?R2tӷ[ VF4J3wl#яB]jqu΀ p0pTFBh;>؆oTF#VX[B^ &1ͺ-~=P`ıp?ve$t<#/%]:8!MI`ij{&mL09EY 'Qհ.̊m9~5O ݣ:aG3ZtC@Ik0x!|eMJ̫` Ha}CQ\r.Io͚h{'w'DPl~1[ޚK\9W+Va[Q'Z4t"u2/yWnFF 3MfϞt{fkrJ34[Hzdw!Y"rccb5&2r${g$Wn? Zqd7>nwda+س'?-doHX*Pfq+dF0jyߕ i}2wAo}n r8ҥvJ{}* HzG%c?(8d@;ڪ$AeeyOּ9T:ַS'H~xO=aۣ;/arGk%jU/IƄXyMkV/Tֺ.xhsȐs6>S6x,Yp.f[zEU06\BȮO$'Q- @@Gr~HM g_aB*˲>!|yD+ώFk$~&KO[4J|̱* Zd;Uۛ ф3|l'/~ۉы)pp n t;D,T3iNvRq XMlj沾PgWY`ԵB/9t#;ye](x58g8l7'6j B7C5=|S[|R)"Rhf8jlb궲[g bª &"a A=TxkXb)L]#8(0|oEh4NXFK/\Ə}ӝ#􋋖ո _(yj7SiH[^,7)ǃۋLAqOCЁiQbSeZJ0 ρ쑼 r;,yS֡|sg`@}?ӯV^E8\ݼ'HZFKAP1AU>-;B,sx*0zb̪r]mG3ezNz5b{X|/WVC6#5.>hI}'#*vf6=cqaNeIuȲ㉙\iOZ?rаˋK/2L]dA.xK2TR6E4 mYIPҘGZ|#,&9&Ɇ5h|6CzO]#BM)=/#Pr9s\ ;`ap[(6S[C}çƑ˞ȹ=^^*|#lS(.]gS>fɞ`Ӥm`x:7{S`t-At ݬ,OԬQey Izqh%DA6RfXGJĨxH$}00Q]zʫwqƯ֒k I}PH_8fI 3UΗZᅓ|Nb,hsnh+U DMfx#)F}~5MqJGoĻ%Z;/O)W2ʝ,bPeOL7WC/SI2qlh7,]#x NO@F8fW%8 wBr[:w9cul8 Y` xY$+a1 'dʑ<[:Aʮi<'i2KݳQHL&!90<;F v} m!9Q\k5 NneQTTbyjB4W / ۮMs֝~1}w_~>PWa&XB FZEB,6Cs,B(ȿ~yd=DÚ|'%Œnػo9} IϳU<r +{d>7R)tlO0ˬN+5ꄣa\MQWΈLٵL񘬟rOjkN8T^_էoɦ4sS y y\6 W͋$1?p e\1*nGʀA;AW&2Vҳ_!yYhLV dD\t+ ( ,iBTJ1AǺj'`]1֞{ xն&+ȅu(l[Azʡ E}ު5;SR>IE~ UPHϽ@R(=iM|4Ltz.fٴ)plŻTai6?X-CحZK-CE BK*,tLB' -O4)wprę~jV?}joiJ?Y lf; JBMkdmPnE=/ r 6P>vk͒=)01:AYW`{&=)u~6mq_4拪`NI)B3KPE=g'!ƃn{NV-՘]ir,6Eqplwjy+G9<}r3Fr,9Eo ]u!؄_!Y~D~C6f"D5~, &ڷ<3 wk Į0#\FvQfS`W*J!Wv|Q,InC 䝫L=#KtJo6N%zJXY31[/ͫ§۝޵V`pg+ê(iӔO,f_xZI!qkEd77 jE]N%YV2vX}k[)cimr`sZ_[uB4Bz^nCQP~ ODuR Dz Gs-N_40>0k$ȫU-S6  -GQtkc`ݒzhpwT Ejaʵj!e%;Xx'K"jvZ\.|m(S5Y7X)=qc5 °Jgv'V柨ܮhFw߯6ƝbxߐC<;oarŴ]=[e)?N107`U/;sf ]<1T(_܁">w[12ᯏP'=[0DhZ~aC AQ?{:4t18MDy tY 0+5ϫ QI /s7V H/sIh9|VSTgKD6$_vdr_xń鷷 нFu: Ș[V:eغTA,FȲ X/?NπGtXcy~!/ˉ8h(Li_o`k[4~85g(@^Jv %Qyg,?:0v"1G!+VDuTq+=[]ԿybOoQO+Ö=ߌ=Ձkز2vm?kBffO-.'%nW$6=l;g%c53?Q4\[muzŐ2k_Sg\'%%s:``ߜ)۔ѣ|GDN8hJv3Nie#, 8淾E{O*] ? 2N;1^Qqtd_C3C7`o>2u$dv0֒}8g^~qveT#v#n PMf5^xZ,p0uGP,:3p}x٥R$7 # tjbF8:P-`KΜP0Tu@݂Ul]<'m )&w}oj&~T ߭UU R)fT[!1u(!Uouj@;E|IuphisFn؆}Wr"F6RF4$ΡJ*)NvB}9TGXX=" EQx4&DX1?xo`B~x|I blBa|[rA"AopX&Z#}#n>l& DLĈO7"'e:w7o9!SH Gm+̖07vЭ/TaNƩ]e:R_݄S7OLxC[^2t*D5xfplul/{E3~G2|$WG6Fī_W_$Iw¶Kp$PBf4mK"" I L _֞7 :RvJ,d7! iĠo.ᶆXOu7 f6zZ0NGЇto>vԠ'VE]e/8u Rɓ(PQv P 8g]$C}sxC }kO0҃e U%k;0*fG A8GM(WjG܃P+x`\{e6wZvgW/}y[xY!oPY2ejiie\_Z>;+%Dgya {jT0Q@ ^wh{ku+L4,gJٿ?-{F5/jF"MœE\ݓWf[@` 2(ߥկ:m23,cy.|8jj a-EV(K:qx $>OD:'Y[9t_._-^3حPs`#B+q!= MK[VjnmEʚO8$R0Ƃg#-M`K8ǵQ i4:PgkS[6Q0='V>SD*[9x\2> f甠~/FQ׫pX:`" 58iV:5'}\ᯩk#ju(]*"g 0^TU--B S!q8>a?>bfJw]MBdNVԸv.Z1etW~PYےyɖŨ4t,_h_`7fÎx:^& 4Z-_(IղtJmy6( 2Dz/Zk * <j߿J&I1?;d2, JИoY:Ofq+,^D7KP~*ؾ~Qa;zH;Iw$74MN"h7߯^/;IA_Ո;L<% (y=S 6@m)qE~\NǬK9 as2FTS>?`!5gXq} QVSwؖ`5wp@e;0=Ps,m;ƀH~ F\'hiZsd;`9qաth2ESi* f N91}>8\%kR ПImRCF/ݖ b8DדkaH4{pUYI*W*lj4ODXSc\u]"{~i09"/GW @G]_V; $,! g@w+,aNZ`=3|tmRղ[r ̵g𘹖Aq2 sA>xux|zȟ'ċ#w֮e}aK|yoݒR&J]/mˇJ%q?,l g ě<4*#m~k޸GgiafŴ9>gD@y|s62V [fMX":B[W)jenLʸw,h?*8peԵΝ\YF֤Q'ȠU;/l!*h7J<yy{i0q88w#0Pd6<;͍\(dNTHx!p7t>Y_Md/)(72ǠZvOf'a{ 4V WtL_c+58QUtiguּD3TkOzsl<v}+Y#*lP&@H$|93U[(1W{O: Z}s*25jqPU@!hΜ| B9I ^s8f ڻp3 XQ11ڲ%@87~K"4kb9ȧVj d=2ow LI$29?:G7w4bx;YuyP @NzN b= P6۹y~ 5e۶m۶m۶m۶m~m{wN?g2yQ#Oj23*UMzZӤ?ixvտv|*; ZDp~'#YN%mx%.6GL@0DBg dxʷߋc's\SsF!9 c 5gV~KR Bۻ88wBn&j#3}D>YUSINk=( "u1v610Jzn9H'3T}IN3X2mn˂9c˚.;t1j9HZw*_OhOpЊ4R]®Ձ rYN۩ٻ$M 7^寃S̺c% Ѳt|J\P҃[XU`36xI*1h$,(u"ϙ%?h1aRߧpR@&ǝ&`x;nW$=)  ?w^Bu%)Mxs9>4dy?Y^sA/|`ʼHyb*D&*"WT˦̖}v$ Wlj {߲8/Ţϵw}`>po4^A*ӷfMmB4Qþg0VY~Wr:~y #kOZDD x '>M .yvnL<[2$9lzG5-7*H6h3 wP |96.d:Db{"錟p_E=WԵND ${]nZl}'$Ug܊( e߭_E.d1}γEX OC*6|*R=_vu|xPRjODڶJNehe0@gc3P% LKv#`Ѥ y@;ιT;zX bԃ.8äSo"!f4ߩ)3a2Ԩ?ϣ\n˼f<65!f xzt81[ueI$JLd8x]emEȡ/8A*݃D%ou Xa͢=SYͱ#u;덌jP=c*133Le\Gu}Xɑ|5T^8͈ H5o5msY4KwBbQsWκ\JGsc.ڎpVHd'##N0-Uo1 .znIWqp=h Q{icFV&1tby7|\oLdxwPBiBw=r0m)װxR㿫tNn7=ѼI`LA"_3?\CxNF<7",0??ݢq"iˉ) h_H ƾoc%O ;N.+<<@#dMik[3sM Y6Il-v5c9]2yG&F{?mG@915W!֚5Ң3TD6&w`CxxIcYC!.: ^d^Ũx# Z"&mmLIzQeL___K~mw<ǪV8> AN!dJ4.1_ +D}o)7K⩿TX)`XGc0-瓡'rgj~ 1ZȺ/|F**$Yy}>wXON|?!Fh:ÞS]Vjt KR0FJK Q 7H l# ;Edt44ny_?R)uyF~#h&hD(SRط@^ӐMDh_5.5$G]j; $2㡉) 1&p{CJ7YRE@cZm[XҤvA"GWIahg.WsDruoEZqiE XmE9B5|.5q'[!CQFJo48ɵ\U2̚sB{Ͻz\Ҫ/%l~8JBJ*A*Yt! :c06iMؠ[Iۯm+ PjRJśY  Et?o(Vޚ)FzR٢VlmV[ΗO w1r(8^k}⫰NmEa%\̃XX\2' o˨&פ͐{GJ"9:IB)5gx6-KVۚR%7H ;IE5L^PNɒG btN4i. ]rP6$N-@g&bZ^0틺Z/bHdc%.{rVM^̗ΜxG@ixy3l+2bم$|ApEj~KfbWQK]7LXf.I8dHi*V%lmЖts^o>ԿǏ~v$S<rrѱI.vbAocewý%shŊ .ԢJ҈!uBG{t&Hو_)ob#w 4Sv?88u q|VJY0P9:#R%,5S'-`LbGHSNQi#cfHhi2E ѿ_}áƀPĪHgf}P"茮e~A{нX'פE'][V`8w@ 9``Xv,);6 t=X~~9C&{8_pEF*f?~ 9 ?D!ϊ kn\*wu EVRIh(r(R5p R \P_UhG3B,,Vߘ.`Jy39MWU p4ofiiGv뉞.QҶeړiTi/d}cqD??z>J6& %t(F#6J{6[_AjXK#L?傣ݾS+0Nv qnRTvr@63F`NY۳4xYΐp7?x !E"ڒ D*{%EWZ&i{2_Lw$|{7RW^⟠ IL䵁]u7 52*iHt~B߷y@d BCzQ/y\Ub[7>c[Qm)2PP8W˴3Av2LX;p+lNX |;ԣR諒3v<5~F, OG]@3k;NVF1[u@4b{<9/n`0B n<ߢ) +bchgA_7]ud{:Sic ͖%ZMan׌C^'Yxȝ(֘Au[o۩Qp)(m^O/@3/r%4V*{ڪ'^5\jڳnv ru(V_YsڞE>Z 2:u_OWH^FQ6{=>cu"CQMNT%5aLsF,lQk4X7co;h"kQϼ;r 1/iѧ|MTWR_ę ON #uL{2uhOO.gvyp6!^b!JB1%u>G=HVa jv He i>bl\!zB‚ڌ7tZ&ԱYe%z!>9&څD  p]ŠB*XzV 2 JWzC!01pGj7`p5WKs'ZI\\(~Tɷߺ=ml r  {6o2[/ T-w cj ݫGΪ2V6WhH=*']KOR];'||3~Vt}}Pbv?,}.̒VZw߉Pn{Ir$]U^_#}d]UPa!o+ӝNfĄo1W:(ih`~vƢ+ږ7`]XmT3#Cᡛl aBWu8ݠU5f[R;*nYd'!C2$(ef_Fw[K[7zT]ӑ\<~6{w'.9:Ϙ}T). WVU 32I(i%Vy1WK#v!wQ RcoaڌZ!X!lD &3N}e= Ufh֘Z& :X>[']C\peK1]_%W@x[+_.Sޅ.!:&G1NWT-; =̮Oqw -'"(by&z\K=}WB oKM#r3ȚN=lߑ89Ƕַ[Xn(Z[7qգd,(,aS3 ~s`yx#+ > 3g>shEMT΋?wƟe9ݦm猅Ȩ[H9<'qixº@˩,)uQuHdwˆ]uRz']1 E*Ue?zVtC cP~΢g3 Թ<$m̻PF^ݰC4z&AR%7z(bf{4ki81EYU_I{0<#&>T'pX!FRSYV#G<=8E=YưpU_iI"Mwz\dh |1mڽ/W[:lP<,^e'0x%' '2Őӿ5u@QcZ(^G"~llM¬(T)}t>@VB=<Ûd d%(isxwnꀎ˅ӂrhbtrC0e*iSZ<i$X߈.Q cTbNPGfiWL(Oҋw09fW7I!q޶,yVŎ bZ`P"/:9r8WBuc}Z'劙8k ^: Uxj ae:^{VTu&a$8>{0Bq5sN/hmPWh<56ԉ6V +1 2ↂʀ=.F!I{# [%`oc|uKJQ196b~D*Zcdud= bd's!I,a4 ϖf'Aq6G)8`AS}^:$~gAٕ+r+.h2}q\0edͺjfUrUCV,U)6P78T[!|UR7LHo w8Iq?:%neri)۹_D. J0mC`?YAXGQh4c~/fdF][*jfmʓ殚Hw R#=&H-VډicE^mbO{-|TY;n mcJD0Ю#gȟF;}7~jy*^ ~Vޭwv/1R@&_|`|c=?-{[v%Ђܵ}TcӉ(EʏcB-X)o wP뻛ٵoP}r" *" ɗ3`r}7IW_ #bICʒ*Nma8&!:a/u(Cl!G&.pA?>kY2lFp`Dit)>;x+\l.tlɠcØV|YJ"u'W42<ƈ;ϵQ1vd91we\ENhz*j:u0>T`w 1,*\YRZ /ή;<22^D.68H՗˼.#Qlcqa񼇚d#9JaR YGnT|[qV8$gJR3A1%#W0!o]TN X@+^DbML;hy'\zzDGe˶XWX JJsZ؉g%* gzHIHi $eO|&}TiLJ$kr8yPAZtjwuUd&Խ12Ǝ K~1>ŹY̩?` 8 +ԪBkU8plt\(R@=k$ۙx43Ƃ_(ӁN>am>UzP%ٯl&7 t,  A].S'\s>@~0ÜC!mX= M,/ n,-h-Vr'GZX |+>al4^ө;!Iֱ 6,vh?sa * ΰ~z(p?v`?BMm@TlLe  w>aӲOs߳Rjf#*wd\vBH-7I=( ؚ?؅W}s ؓ'CeduY1=# EKJQu7G_4c Ë/*sT.DpL`cvp/W&D ֐:[lOppB(q\%ݜL{X1;lz}8b]SBg#)'Fgp|SqA7Ɔx7W琰#$,f0rt)ĖGdҼADfPO\yF+98_*4Rj*8҃:|R׳V y(, T!Ƃx[xXYK{|{З}MQT&T LOO>LJ_m Dnc>euO*}a]&RjbL}@8A28oH2sZ_ m?@+1sTGT9]nsM J{VX`'!6lƟ5 o>jIlE7x:"0m,LAB!rG篆(%{2Yl#]gu Kε* _do)MRf1zSd-6L(Y FP6TvazXC ~NXA\LI |$Ƶ Hw%43_=(c[O勜iudwЯXse狽p nv+5-.To%GO.qknp\Kđ6ظCG\S*P{}Mn4MFnp𭅀^6֤nd&# {(gGoή05mWBa3"'(/|ج+s/@[glL_&I% )9x;j68bzy# ojLF@xԕ iz) 7k[u})C]=?<|nۮvܰhaR3r1RbFOHl(ao1 CtfCA"_*]_vx&\|ĵb^-d=1n"rnhu)`?*xqiP|TFkm.9IJg$Kc Du/Lj#C V&'Eb{˞r?/z66>sZ܂f+Q&tޫ,)633y-ʢ"6`ƥmfC>] 秷IB/E} ;+Q M@F u%Gdv|o5}`;j(oʌRP>#6IjCk" /gD^5;`pt<]~18 L|~uf zo&oJS3O sV@a? eW0mwm c^@Su;~t(D'ӜH*](X@y6XZvJZZ]^n_kWYi#mPf݆z`䥥Z.I-6D[oO!걳`&hLT*"U*FWLI;5&pFx=Σ|\ˆM}R*`²ﱨl_}дC:e(Ʈ+yBmWY<t]q-Tg+rŔL1ᖼY•pny9%mR5„74<6)Df{Lmnj u]4vʬة*Pt<,A˵J HM I$]䛏s+ulZ'ts^)^".bޜGsqHp?v˭ZRs)U#h}W]{WFgj{&ksIӼ\.rU'7⫵V&Rݹ$jT4ߤL<9/ԥvxTSYjz 8~9 RzB0&\i慩Jteq);pܿ63=/j҇V_) r X=>F@BuE0UC.I85P`?dˆ56nvދ).KaZFkz_w/7@12̃kbf(I?x2;S貗mYAu&}IU`['υ=0K ^zpjFD""8;Z{hX9O|hQVr\ 5ϡK`vD7[ā=*Eh 4o1~ƻfS,z X9׭U5CW?vδE(?⥎qԵ3.&#uT3ya-%:W0\sJ `L当d={M'~ xPq؋I DV`0Uepk67gR?GHh;ua_]΢׹SNba{6Q/8}f/4F!lHRfL)0Ǡ4Fxfk=Ygg-pmCҢ5RO[Z! [${Q䁊l{p &Z=AqЯ}~F7%V*Hp}V!R?l`u8 0J9[r4UbVl)RLKiqmbů?_[]K("Tg >lEQeYN$aDiۼنuU}WJYݺ Ymidd7#|cMFYm&)~ `֩PDߣ MBv+[Esjbt,ڲ'vЙʘӅxb<]UW[W̿g'*cJ62n`c2pf}'vt[>xқm Ygxߞ3F|I<115 !@+(K`Ř'-W'?ЭPʪi_x_H)΍dbb]Kle`,29_nc%q9 !:KRyaJi~ea5_^@y\>%oN,!?^JJ ԥ8>E "H۲#5QkoĖ+=T4L}ATm4Rlg]\@< s IK=&倕e~(Dum4k's@bѣ$Ax:w 'Z4CeDJ8Up~>l2FJd_4\_8OCcE]wJ@uY |H?&g_XգvcC0M4,95 ,:)cp2=`5:_:T~l3Tbw䗈Zqc`' v}ջ-R),gHs7 "!ͯ4$ ^ 1x3;Q8vPѓ#ⵘ*uF'ebrvahԽ qi6_4/d&6Vb(_ ȮcY)Pig]^h`8ߔʁl&K' #[< ۩C(zBUȡ+uzm%Ïސ'j#%hm?́.&2wө&-6*+j9e5rl ih[;E :jСЍtmjneIn&& *$˓-,4AE𱁱G@`U\}MB}wn f$E rcxP@;(Ø<t\ԽZp\<$'%M&|OD(6`gUogy׽m`vwUW &ߤuXp&|refU:~xr]⣆8.$;po3} Jm$;V z[ 93咔GT{ywX_62;e#l1&/k${ǡeIj0@l{~TNH]YU,G >]GLb]8@_㙀cVQB.0NnRyg(ؗWO}7bW$!G#=k|pՉ=E'݇H漁-Ɠ1_A3kzQ ]O$RBB(1pƦ=h M(~BUoxy_&zd|gDPxQ Pw|l8jlluKS,~؛7J-;i~H*'e7NkMkurrL ȐTej2+ɢ 櫭KqZV31 "'o8ԍ?lr4{~eSgΕ(ػYժ3uW_@rp[s[Ol 03C2 BV:iQ NȨ=uCqCi%P]@plR} -26ȟmuؠ|pŘ|xj7G Jz;m8!-;yPe-+L+B셃a<ݨ4̗HWI Ա`#ʀ$٬)L-}ч-I]op/hOOt$?Ga( -2%D| xL/8ÓG]l~Z;7{RRtS}Tc޺*}1M\yCR,/][ zhjľnA6|4؝f=s%i-pl5,[unk)MĞ_IgPDg~OiXJb#2NFl[p9y0 ΃A9֩|@rȟY,'Cf,f`OE~dXFOo$)߳tA0P{nGy/L!ҕx8lx>ٛ}N2^0m9J߃k,Zj \"σyTj4!:ڂ(tGb/_t6#xU͠G%jF鼯Y#i B(gs^Qwwֽ*Bģ1 '0 D}&Q>Ґd _ɄফY3GY%JJde-TuyweܼIsOP/RϮT0wtf lH M 8P#QН2Ʌ_piߙx7u$]8T{7}Nb̊[c#"D_m3e;5ѨF/X!<7v}Vi/'*{ _uyyhꖲ}n*ê(Z z 7 >Tō8HЁfѭN i@eUlC =jpX4G)H#,9lG Pnс'/Ag[akսJ)}̯FM $gYR<11Ɩ$]MF*6 *ߎ:Bw^1ICtmA^{}Y*@7PB0Z{GRh'.؋Yk=Vsl$DPCoG&sɐ?VV>90$;`3g_$7IlP B[|d ;!p- kCCVvٱllAVdž&%7*\,=izF?t=Gj{Bkß g`y"dL uꚳ/<8;'c #FKz Eum6,S8D{j1cK0X^/4:כ@ӮBEvj1 EVY'{K oUbn0aS]pt-E&">BpA^b8kqIXI9d̾Yk^I0QJqcb|5c)Zۉrc?ye=Lw35CffY.AEw2l5(wv4YMΓމSfn.ȇm@0mKj< Zy!9X"TL2C^^l*j9#_0!SFlſkx d/dȊ]-c \48G'f]J6N&5myGn~3DeH۶P jD*'hsy &[;>.CYa3k^M4L? t\n9OH#=5Ø7EBT8pwkS" hvW{j!Pbf^On:L#m&ji]SƽośAhggق5XOa0Fv"0*[PRXmb]8x(= ;Iu4A%&i(,XdLȏA,-+iWODdjlciFȋ^͟>e8iUpP=:8RcKn// Kʕ1Gh}{RhF'O|4`hd#(V3c] VۃW$;"g\d+ <7It_ΝWó4{w|YCK_=wC?r3P:G%4go`dY_UXӤa%3G-?(_yNxrK`Ν6ۇ6!=IxbO%º* A/a[wfUF1gW8KI3yKv=LN_ pA JM@@c?/>Ó-RU&./fic*bl?dMitlhZ.+n$"^M%s\L]L ] {Ofgн.>b@jOP Q iQxZ׿0<*:9iUN2W]7u1%Ŕ?fJ}EoEK%ql"S((u^4.:'c.qo#0ۧ~zIkUVZ-q)0uG|dn_r(D饮u{s#o*?J6VuQ8c}5Yo>fXB`4ȍvm֧7oYS1ȴGk"Yƾ*RN/-K1Tm:[RttA'.ܜk-PPf AXs6]9C~k(JrET-naAyZ;ϡ&~BH9L VDn%x7]>2pN/?Hx1l[#^˭ #be _!vPsN%Ne[QlA<;ʧf|$ۄD]6.3-XZAPu㋎اPOS"KǦUr"+MN'yJ:[` Rw|9ԇQǐIKl[d+k,;rATBd.zxZof7k\HӠdoN|ii^nДb+Z pEF\"z!%& i?TM*qr(\; @W>Y:jw85Zi yЮȕ 3/ +/(6V*?|nad7!L‰UKst@k.ڎ vKsv0z'0h!3Oܷ5ocBSrШ8%S6ܵyz.U T㾻'Vr!tƐT}FL|_uHiLܡc *WȢ}f\sdWls=aZ?7uT[q%q7&tG* j7,CIx #lWIH[6-f8機'Z]B@OE^9`%c<knR{z#Ʌ֝8"T\QzF@kT2?MeYAӑ>ʋތg"٪*nMdWϴC>Մ "m۹@-O*$l+Xּ^sw` !'P-ѾhKB_B;[qBgvĻK@D4C/Jk |C?*Р҆)[(ge{,Lҏ ^cPNOV+ylVShj8Yz!&zbi: \3/ϲB(|XEl_0Iz/wϜB#9 1y>4wJI NC1?sIy.;i{\1svϳAum㕬r:_NBf"0@T¥n(0GiKfj3ŴM$6»${d-ɮnRLڐ-U1ҟCW(KivK?@gif0&uF7zV Z^ӷ%;!]W[ey BSBBf"oJX.$HKQlUS Q$_$\gZڱ,m^U)3")%{xŴ(0C7 ߴxgא'bc|Y ȫBm*M~~۲F[*JT)LQdWߟ6 7%(l{+/\YA6 ,$~hi G97!ZvepA̰7ĬAҍIu>zH|++!ϫ3Rχj*y,^њZr mg4+JCՈf}a&D4 蟷Ư''Pʅ}Jn0O8tû L"rZ"@e;pc4t1mt՟N+’{٭vu{lGdL@.P@aVz9E Wjfʋ߄VC{C R1fN- w[hYoxgVkjnf*rvےno+ISNErzVfxg̢ ZSΒDDw; f)ߍULyFsz?s6ob'=^FGR2AAFL& tE6y5\jZyXy2m)T.v|\+77S=cs# zv魬 (Y 3(H&^\(z®\Ɛ8v:N 텱ɀ/u}e{a]\#3WD9 ; "삁d bǞ}h诐&Hҩ5m)KNa?KHxZpMjtJ {/c栋3]d,pYnt8 _T4Ejo9۝+){l˞;.n vq{y({թ'^Z(bVЃA1VRSȡ5*ћ8P@|,}R`LDU2WC_Gʞ3ϥ/p~t\wOcl4#k9"@Y:&QxiID=V j`EF^bbNxEk'=n'! Q7)ZE7;u7+{S׭M}t?+&V i$>Q,̝'^.&C2@+j`gT)B+Y ÇT۬ X.7f m뢳S.P!JswO1_IIҊDӠ}πA&B^]yzY|,*7ܩ &HZ-J2iC{mv_bFmmfBFJtp׎2J52,(6>GW!Dρ}iELG!P5i2| 0-=%G'[8;/T"9>@~ѡd)uqe咕~ 0.ī -2$ʛk Uhk:ٲܺt?IMS" [ZBFް]j<\Z85xGB,?j;E!;+mќrJ.bkg @x)czH+ϟ^K:N!KVWA=ڵc1 ;6m|_#Ά?ۑ^.jr?)Gt-!0d9ڠew'w+s F HvDLޓJ 棛݆] Wcġ٧ߏ 齑Yai+c$قWipÝIxf#6$BR` Gs|m, НM~.Ot=CK5v1*^al7\JeN ?[)=ewpp(Yn2>SD%/ـ@(6<;fCx 䋸6^A#>w[GOaJ |6J8Bə.RFϥt,$cL3!Emޓ]G婉܇J6pqԦmmh 9cI^`nDnO4*&\v]nx)?3'b?Od}=ٛqJ]ό)g3h]ܯ3ΌGCnLFԻ\~0=-AQn ay =o;I۞`._X5^dLEUvA2%zQ`Cz ČX $AJ'BmWLOդ9M1=W WJ_n>ٞMEV8AAʒJBu!*1c.`'T}Q<˗tu&5rH+s]-Xv!HZ@Ĩ!|z>tWD5KXȷ A{'mӿ|hHd8QsCy 垝 ' ȸX!M!~91>b/M _!4V\gh~ڬ~60͇%4ʝ!. Tlm%h|Jj9H~#OS hkrV{ Ӏ;|QfpE*O11dZP;Ώ (= nK85̩$Q%eqc#s7͓m1Sn3|B l:lӥp؂9tʁ ctYIp`"-jZʜxqE}/?{``=8҆{(śT[aJ&/ߓ ؍4'yuۧWusē(@AkbREGM-Ƨl!_T5Us2d+ꤏf⸄bZ05Gaße ?!BM&рO68¹LZ颀S`Jqb5fqn99gbJ yy=!6b+2sK~<R#{%#)0S$J;B֞pڣ˘r?l&[E6pgxe=1\-HR㶖/q/\<FA3zT#i|[رM+q Te}!>ak{1^6c;,2C[J+ /lre:烌n- f Mе)md+51k?XX60=϶Bf*7 XjRf>rroV}7h;2j?Ju!aقe2j85Ю?~dpi#ӢpoYr,xyLg" EozV_̬# jPW?#ÔnLzz`2"6>/,wGc_ V{OK ;/ZکtIXm9yoFj2}Vbۺ&^PŁ|IlWWH,Uvg^ X^E8S|?2ZycԞtܓ/pj#'W 4θ4_v2G$]M)$krٷ_O BUp(Z'}(6T"C9N/yd9C)?J8iwJrθKyYBx蝳2ӂҫx ]%Bmφ|&RXؑZt_KAGj;qEH΀e :%Q^. #G#/r{iвDN]emܨ]0훁͍,NbPC,榃PB\)3O/7Hf~-Ȋ5r,G%UVu޲'P ~bbGLfЈ{·kv龎֛$$V|r0ҏ?ؖ Vg3vpU8]|?b;5e?>55~( [v?%uT; r-` dڥC8b+GCz-V9>.xĪ]V64Wb3 }\zd[cۇXg~xW3%BBk]_OtH7`l-¤O&}[VpoeyySO>>ƷJ@Dhl A CXB%U}\uSii%1eL;6/a  JC%@*.,9[@M8PbZ-Agwr%!`7'B봛!qi;~ENiu#rOX;Vm%oB A|3˯4 PB8|{[,身7{fI3p9=}|d7O^$=:;t{Bhb#64j 'բMSTZ}%nqh3qaGYHz ƱwmQKj}UǍ>4يU/))St-w*Y$)lFmmJp¾aks%Ms#(E_th|~F^bM{ $X3!@):g>?IvQv="X%-U4b赞[5z@結7N@yQCBm7T3pIx:E 3i^_{p$'PXXIS([<g92]>s@m0񟷭Ue1'y^լ#9A~֜t.5\Z;)QْM6v UB+}|P8MOa:[nrcgP3;KMS?w<>Hքp?b.:bğwYU[dd>WP޽8ݩFG4Mݳ˕vn_ebCg܄QO\Qm"y#NA#L" h, mJ[#(ۖa+2yL-:PQ+:[QJ¶`я ySJ5aGsk$UbT;U}(EV=ixVR^QfwJt«>iD%Ǚ-  ];!( |\Wƚt17*{ڟ Hmnr`PH-ԪLpnKZY-љ+l!׵ui4}R:'\Lʁ߼m "Ӣ5f=j'oqXOˉ^"îsdV`WPZO#"UↅPT A| "?;o|x^X#>#й!$"@}"] Nmǥ-h";$#9LĐêT鼔!>Ɔ! q=x@BXE8g@#8-:[GX 46Mve@#!_ʄ|ic>ђ Z nxR;KlM:Mx?2Uq<)|m.Eb guInwb]N^;w: 0ymJw:o#m%HՓuOGزTp?g BgaOEY.5ͨÎBJS+xtK_+~ &؋a+^iXi3eZ[H% ys>x !6I6Ҥ*v[q= ba5=c5Y'mWπ ueg>4<͟XKLsyVƁ*`B{|b G:(b)F:AE^}{2J/8ފjIF6h(Ť΂L4cLwzܰQ~\ĥ 韽ȝSC ݭ< ,8wfC~{LϪy)rKՁH9i#d c/Ж0u5` RAK2(0Gۗԝrf~ݛ cf /tCrqLnj̣qJYap%R 0ZSiѪ'bn6"b%4?pW80e̬zEvSGo0 ےc+o_H^Eƞ`O ,Vn3Z}"J$:p5,&ωR0~7>5FS{uAk;@s󁺈W}VGKP ms Q͖w9োh!5ɖ*1*4o@BYB! s$߽yX <M檱ۛ3Ʈ h}h,Xh?e№ s[8}6o5`@!&LdjN㜧J XU3z GxC{NS(PLE5]ݵ}?/Բ=eԕ 'u6H$rb*L :-fܩ3E퍸VH#G,Qu?zxRo)Ff甬w81qi>%d|Nq`TI=+2oxw':< (H}jEEk6k~k?ɼuY<Z.79bs|a-#fw*4;6)P}ax>F(21n{X61pHBfOVqUQ}QWNOpG鍉ٌWE|5X\x>0Qj=S:\w,VOVZp\]ˀɌ>+itjl:gHq¹:@9 bjw&T^эF+P e~{ҺwpEqgFFMiNe{e5+O_nkzRp\v< (?]JemHBRvjz ؕۢc)es1O(hj Q0o,3[pGe^fyK-`U),ᣫe SU YrobZ{v;&^AX,eL+c ܼlk҈/D c[C`32}Pf/ZZB tp: F~+^=:'cqkqgǽʮSL RMG]ցl Ǯzui xoz{JK|C`u^kP`7-+QsU\kKek> cY@D F9qS@B5 %b۟Al.5Dt&$5 |I.icWWхjHs| o_FIK3B[RE ٔK<;Mog243\0~B5_ K¬(w[[yp>=?q;(8K0F٪C7^ք4 _ȼ־d[=uUk|1!을i֬iawrGZ}k L FE ୹ĕs OiuuuEO{ Rw@- ~5fdNy=]dIg*g4cA<[;d: +mAx,"'0Vo<&v {]so(kk"#Gg|LMrHVc N ։M|GB =[xB1U.hhBf4+P :]ɐ'{O}P*fySh]h\g3nⵚ7:I[2T^+*nA[Fe߫o̫dz)I:|C7 :Ì,]jh @ۧwT2ApIsIsJ2JČ_Vg͛C%sa}<}b?jht_=# &MyDV"jJ~^e/dLśwٔfzm8Ke"^Љg+KL6N. 8J> -0aC8uiqgmB(1.Qb]tZ1cC| %D|RՒ d{4M .g퇄 t1A00y<&d, 7PG ,r(ziDFg[.,TEá EIj 9 X%١MH^=̇6*pⷝ?] W vkaLC{ھK|A8Xk')%AלEĦf. ufz5F]+}Jgp8WօŠY#`^sS,vsbf!t3T)7 g-"*-umFy`f-.)n+ E|  )zyo*`R+L@EW_%5SbzP VFceopՍh'X >ݹ?2NhY; Whފ&}3 HZɚʲyN*00[QθEoU_2[kj(1W<9oo2A)ƒ88ѩ˵A#,RZsHc!&<הj܀~ϨS3ziq>gD_~'.6>B Ly˕eRUm}v尔Hwt>G.Lu{ey $5؈bi@C:j.]poE[~$ϭe T%a"Hs 2R^>s'pYˬ/Նx4SwW Q+fn뾗X((re5do3]|<؁*ۧq{2bglfS؃,/ kؾ"X*E䢎D)sL%UQNͺm۶m۶m۶m۶m˶?s9ٝtI]T4TI(`e'BIcgi9t<&F8[ A>>u5e#ئ@(_@"Wyp%$8E&o08NqlU1":G.{"xyqrN̻w%Mhh'{NƪʃyMjgӭtx *l7M}Zj[w"EĔZ? ̰NVQ+eNR^30"I;ZlLB%b^ik`b !2i p ͩ #an=@2* kuԟLK޻aEd\ <1T$' >Vz )쑅;HZб=à/2:ELc"{r5G]9#2}/f3%c~=9=PyM~U޺%́O^O,>1r<\}7/~$/`qf?M >v(q@y4^XVHϖ(eU1[1rӭ,h7" ERMG(몝ueĔX{R΃Upo#Cl Ҳ o=F> A(ֹ#,y9x-q,HI^3gȸ<_iھ`GS9n*bR46Sedy*[(Vqx8ټ#7a<=C#m??*酩tL:9# K10?kn%pcp :t&iӷKT[j1*&`9,z~pПLJ$HWMB!=I`5E2G\]}gӦBF;R)`aj/J-%n<(9X0i ,X?IӴ :A>˅gr=BRZu+d0 B$H+ 5mC ,X"FBl+œ 7K 8 f]qy#ٴ}IX\/~9m'Ah@,B=9Y VcvMP˱p}hFt.V:VN{6oiu >[T ,~*X7U~h.YK!H^9Hڧh`'+lvfe$K mi/N P @Xߗod* B9ȱ(#&h+wՅ`:0~KgyL9)ۃl 1 !$-NM 8\J%e,toZpf3I0M3m&yEzP 90HJo_bou쑽R5fY.C 5k$i+JQC)H8Vd!JUQP$,[8uL٪ԏAr2ekZj +h߁%Œp٩zFoN]GT(<_}\E&~qCxwj2@8.ё+|v:r+}cudDo!4 owz~['u"MS>}iI&Xƭ(H}HvQ2^:gY#asml9Vˁͥk!m6F}p -{dB{ GC-?=+^ӇIq:&@Ӈ5͵;5Tð@h WLuۀL|EZBGٶҭbŃ wKaGQ1j)תYN`/׊"Ϊ%hur1okx=s{Nd\c č$O+4{ەTXsm݉*~w!CjJ`Evop8ŨBlŒ߀Uu)tĈzS|-NvsȊ BoȄ>BџnKHoa*SE#2$@8,cu8!<zmbm9,'R:5;0զ}9o%#5UxV+Q6L"X׃FM摟!p؝IJX+ZQ ƕlou3*Sg?QG?|[z |3dTaصد >r4֗>^ 3HbC f!.sV2V:E@JQUɨoE޶[[W )CXQ!;eZ~urQh[2gj-}ML͉Mi=wDdiC@ȁTom7䘆W6’c~K^侷}"pU S d3SHG68$q83t3\6S,_G ;ILfg h-YчsZ=gWF5b7F` ea dV _Ѭkr 9 3\wŢ3G~nЇw]:a* Ib~9HX)K&fC N/}@_O D-]vѥsҦbBx}fvjg<*Kݚ|ݝ=Q_ bKrXRV8/ Sė_) ț;gjm؇y,,nJ^k.k#e!mD9@C0Dh'1ؗCuЌ#RALjJ%ވ~N{A:h1 &G'π)Vf!vɷ%+b~ '1qXkeAx׫ZNú:‚&/` Ms`h?uSꈿƈxq$Px^v4`Jߜ`ЌuX$PdA> 9Ibݖ?65 e )mzE6uo` C"yL7X~(x9_t"ul t%*XVt(5wɦ_SunEy+r¹b+_5Cm_akčh5(P!mZ>9#HkkyW^0Eli(7@1Wpl2COuԡnBbR| <HjHspZN9SeXw"gYT" ܙe^QՀ򒢺&]Gr% J@M[U1 b #uWvlF{vXDhDj#SuIktǐc/S\*Kxܒ_ƣBGN,t&d3#w3cV88֐W{aCS¬FϵR F)P۞q#ǎ\^ĊŴ<|N9!wA*y9J*!]dv1?oo-]@zsadxV%07B8_!U %JW{jO8bk[\'&%$S5;l<22UI ͇/p/1 J2|bC[ 80 ر^K Gqrg,/ q}dS 8 (a.|mw.yL‚p8υGPMm!W eIr#c҇Hg2o+grr˅0`K~jl~Xh%?<$XiJͭHY)D =X|ߔ_ס lIyB:\*|3}4ܯ|2U]eQ `| :|'AO+Pr Gv?hE>e@scMɎPXX "SwcgOpjٰͩڃ/s{˸}֞Տx[ n/& ^x]3Ҵo>OڡD +&TiRF{K1ptYES{=\;݋i7z?ꡅ 8^4Y(ο5@-](ԓe{k)"-ld<.3sJE?y#W NU8,Q0Usi4+A@PfԵi5G.|kE/zxȖ8k ć0eJ1L3f%;&2v+hjXEm;2+h?mInXӨ;`lKXrtT8H2Xnjk9sCĶc@$C}O#h.^TU4L4E-I92r:4`trԩ܂{3 Z'Ϝ>eezvl y5D[UX$ֶk!#nr 1 5Տ]$y8G$+J@o|65'"J)ǎtܱ:.x =4Cq#/D30'-gZCo>hi6)j-b^`3hyŀli x\KS ]esuy+-vq&  y+`27&ze\׻Wvh4v8aG__CHi`vZN]ӎO#xToek(dЪFTRӌxneh扼=4wWjJ| ّBrP2IFN.i2'*qP<:,&f @]›KcPTR'[=\+:& /A^1YthrpO*ʴ3~:\k^Z{ 9~mxL[rHh~B] x$g b>*-'\-Ⱦ\I9b58sVʼnq@gN>B$D~9 zC rx^_zRC8v̨mYqMJPbS+5Z{lkA|7[Kr $|hOܣeRXwJXs`\FFܼB(yׄqGi`z'=U ac1SD˞]](ܼKwFI(̫8 ,TvvR ?r"NGv3KnK;J\l(hONa:)8>^ m7o&jSOVT#ԍB`s7&=@J)kάy"@=wpp&Z/M:)Ff|9j{lQ7DbLkumju1b" g=%a.<< r .?O g""#f=g."e ޗs*/ǖ57/.]vP:bts#R$U* >ڡi : ]䲜SwI@n,2_çu SKe4rYrgfmN8 Ub:xD|D.m5NhnRgT$tҚ (:ꜜcV?u# {reX"@zRM3<3TC͘/Cr]|ЌIXP&8DΟ3=Jb0O!"ڥ$LJ;q}M9<e5w( ܮH{R,ZN)?,j4nJSzavY5;r|iyR[^`#oyU: zv /,ơ/Ԟ$mˆa0ΔgJ ,՗)[G%eI764wsۉPwlł_-J ] 'qID60JCiSSf>(dQGܖyxlj8C̐A,8@4q2cx?{Z_ ;=9ʒ2I$q𒻮ڊH1 C_q&UuJ!t E!z4 cGvՠT{Tbgfؙ﹎< ^3kS#+jp )2kPy .k6i `?g)~ŀ,ړ猯Iu>s5=*\ڭ. 4#N"G n0zʡy@ EDg~~fYb"NyP56AoD.Y`)@,SED)`S*v1tcm2}KXv,Y]vUWHyX yRFޛTG׶f,`mT[j rve.M$~ڎr`cL)k8C5kJ|Egb9mLp\sƜ# %B\,tfAZɼ9!QG:E1F\i DLژ&b<m˘αVx(U,q|Bɔi\bTVx= ,.+0߀Snx[S .S8>U`['COΔ$@bug_2(TA=@Uxy#/ 2&|H:v:zO/}> ~Cx ,t=&:L.a4ӧ<r >6,}QӕoN\F@vVtih~R  7MFL:㉮QNsڱo!fC,о bk\kHl&wfIdC{Sb0M6jro ƴhI6DzW*3 ]d X0miw*Z15b11 _ ǝLD"oqEY*Q`'\rAVd3k^ Z>K?qJ0z$+ ͎*a˪dх.d-H $jc 8Jk4 '{`nc'mo(2@Ig+of%dȮ 779ЩXyk֦Ie[nE6[mn9_k@ KO?+j x^5r%Wr8a(y r3bcdslsj郫*/j"_j7Cb+AhП&!3> Yl֜5O.YmkJ1 ]'&0{A;y$K&690rź$tA;{?@xehy5KGT/jZ#mZIQ[5y32_.:s)/`iddˈig|A~-KS]JGI'b/w0UcX:#!s &S]"aZ &kCA[F[ yPb3?kuIN|\Gr&؉y_ ߕϡq~+J.4՛4zBBSvK(GH#Z^t[$ =2љ#rg#|i8G;W1ЀOԭ&4wǁ*>_X *f@H?L9žC2%:!lOF:ZGcs!@ ;7D~}j]nC"A3V{i@vc@_tn[]u,T-^L2bVFڱ4?44b,KŎ^ es@5䄻C+$`R #?>+*pۯ pܹ%YvKmc&}&ȡK (H1@p C}96gW  TZi^w|cF翻)#_7]Uv$4Ѽmٮ'z~kGIۖiO R TIxv >=+l\x4D?ҡ U\(I\ou~V 7a}.k~Nt2ږ vNH:!-_ƹJ1S-О ۃ9emϺӰew8C‘ O6*t<hKPoK@N8p]ieCx Fk$Fr|1axM=.H]y<2̶&ޓ3v1J܀B,z,yw#i CvC4~=T ;uFsU!wonE@A]К_E/Ӣfm` 2cSb0ҳ:a xbD0`RJJ~p+y!,>w$+"t:YMwxlq#>-~(6CL~PtO Tjhx |j.ՎM~XX֫v8ב hO{m36[6ekYDMiQ\F%е ughR9cJf紌`o墐 W}ѝ+L>fVRc.4mvE>E|땅;'I0R;@˭o[CUN{eϮܤ 2&UR8<:,kT.XP3iLW[}A{ m`e4< [(H*5o}c$l櫝TAAZMG0rh-g?)rFxv;u~ Ɏ`X׿U5˫ Ǒ2 EI [3h" yee\ Fy1 L&< kNLbjJ+,YBA3obzUȥfު=hyn "WǏb;5Y쓡@/SS*yENtue4zloAZ\ a#: 1V)=DNUbI]Y4jv=ᠫ/>MCXwynߝMLS4>EK /jY_j~:8B=0江TB9Zi^Zm{Ew?[cE]׉}*m؀2/M j!tB/q苎Y w^qC.;P;/x&y̻+N}lI5 /q%Ey0H.\]?0^D ]<.]gtOrfwk%Pr//|,4PSRsԃd5vxINAa|~ ngzT#',-,ȬxCeB_QZV¬ⳞCm]Ma Q *\g0 `+<!pdqeЯM==n wqC!FXsՋ :w˕ G|W>c /ǐ|gc&By8Krw0fVн`y䬊+mesuzԣ"}۵z/յ}7'jEE/i`,ILhu}G zj|.Gݵ _57?I1>ng[ U6jm2dFLѽJkJz} 7/Czo`N˃xhIن knE ? ;@g$Z2|Gr<+N+fȘg)OShRVcrB;uuJ0k Z(C zY"ߞ`[gtggh^%,l|Py@Kh6oZϥ؄{J&(AԈ" , <, &S@ΕF6=fݞ)6P÷5m9C;ϲMx=9<44?X,#MB9IH5Аm,4 l:4n{}n(TXk=h!y-֛HK)j4,$tb7|ͫO=t g_wGje<#Uڔ_z| G@ E$ ~"[(**-1VnR& f QFM'=GKgT)VrTzTy k]7e22)~[E8J,z^2z9#l?%a;4?0a?$VE[?c5{,hVLVDglu#XVW26PB:%̖UQRgPW)`1]Osok'o\rg{K94Y~adcR^8򂂑6T[hz#h|m}l:k=KqvZϫk(XBn"~=oFͦsN3'A -RM=ǀ O>ddQ]"YcUMۥh*<r1sEӓ/'D 6q8q_`uӉ@={/JM5o`>T H R".j8a/]b8EB{gE*~0֣4a/t_b?xY5c̝zpCY77lYMj RPrz#6Ɩ"ml;U_$&DLs]HCJHeQ_F>`4@ J`\!yYuÌ3aOZ|X"m$0uo5|ӃYԓe [( ܔ^NVPKsZb@:VͶSv~$tEFJaVgKyrީrU&}s'Xp~z*[ 9[3YW51ju$GN$̊BEGcD8glM3>B!(`=U^[OMz d5,3!K@6]2 6׏w?`\>-Yq)*I 7S"1γpF5aXr+?F%{dP6{5͍̄iP+o{ZlHLS av~4m{i?ʒGmU0.u9U(sC-G{%,YWYq_:&اuPI൪0[g;`v[ LKgEl|J%yZgOS#G_cP0gIu]cHhsm ,n( 㒍o W}0ـe^yii?'\$ 4^^n#Ϝj/qp~0W?)ْb[T'b++J͔e8a1zQP=&}i#VonhjA%_ZY01MZWL"6Lzb>'4iF3:lj{rgsC ͑Z=3Mw]Ɂ: gO Y)c3;\F??ڬ[ޘ0voVM*\5d"[rl~NuYU+(pÄp3SFYf, JboӖ; *s?ωuF3b]Mi;ouh lFx<l${ 5rslbШ ɚI;iaM0pPJL\De&h*[p|pGU -V^{؆>D {8yV;i>m m~.Vi{>߫b]Ng`n {ggNb#,dWA GP;"i[-(]ۗA̡ 0F9R8&BVpqW]KFx''B+p|)a<&3t80"4D+z00dAMm.|9Wь s=6J|J,I2< e(dޟV.%2_^S~=,74V'1dGB1mqG„?& ZZD ,zw 9]vRcO/%FZY`\;A1:b׏F~x } |X;W4(C}h.Sy& }FUη_axn%L~$uO ~H :D^Bﰈ1{2֕MYnۿ "E$֔c߈[`~υGt [luŠd-4>zᩅxVy MR$og-A~|hL&5lޡ AF|^g`\EfK݋ #3aȹCHNX̜ 8∳۰B*]0^< G6N˅"Z*EF2@G?n,E 阌b_1XTqS5_  Q]ŸvnB;km_p@J ̾urrK&egh*r܃]qeG>~N=9|2T&i)ZMV?y>xh2GeB&1vPp` 1j{e@Lt b y HY@' ,UɴCϖ܇3-q5(t6BrRi$:yw>D7I1.pclXwsEN> ~/1B>bNsm!H Hlz4M/DdEa2B(A #=s)'(~=+aZ`"˂ MR n,haWm5K>-ʚ̿[o7 }IG~ϜԺQ5JeBu$SΤ@T6Fë]\Ԭ]Alߏ(F(l˱ d̾t~N8-c@$1UtLA1g0AAuDՋIsP67 Tg6;qgfY3 cFͦ~Ptӎ#NK.$b!ytj^\Q!s ϖoy?{j.18ݵpV \˭B E$E_l39Q?Eb3>Nz ̄UnQo)-ϐoa(anCUPo'h5:9t? |U+; Mb\ t_B3sэ2ɺ[ȉVG{9\?W- ט.^1=oRB&Qbz&_m%Di;t5FӴk]膀n< ZqmcMFfr1Bq}Կv #YCv)`*(v? rṙ:2gAZue]\LҝC8hf)g,Hx;78k4k? }@]i2pU2sÃW鶏jg &}13*n)W~#E/`DMQ!>L^i/,1(1>ё!awˉkHeG\+B֣\3!+(Z#|zJHi2z]B )qSa=gjQE+S~(RE?i/A)ZX7YVzաr* n X_ odMhys^\P1{$с-6Sx߆AU<3EE&*GZrtF x{m]{DMŇIr r=X3/x(T6 U-[40x_s | e/ӄ0 ה$gifA|3zYƊS_nKRӋ-mLo~W n+n}p1<m_c1Y >ܕVLY2Ϊ,x\cI2\ {iV7Rn3p:H v{:uSЙ"Ms[#nhL!\ _j:Ӊ)HFΗw9Z(z ryw߾!A& Ge걯SjQJ,{A4Bd\6240l5ohrRD8n.9.g`1<3{-k(meL2hkX:S =H=ߢ,*bl\f6$ӥz~z/X^ǰ4iRWrDf\x`>6V(5)8aD6$&"i}KhoUs^.GxSPAWgލ& `v459g j[`ZvF|צ0 t;=+% 5vf9.jvmm> .zH^Z.!i~bI~b\; f2DkB,RȫKhtńĿSC.XQ`g`4y讼;@Wϛk33++}mE{{!I1c$DYJ\S1?jhj꒤و^UpV CH.8^kkwOJһ$nvogEqr s+<&v`'SN1s15.{iݦTw@hҗ^V堞N!}\㿔5o~@g9 WfDJ$"" z:;fSěOɇ6ea%~a+^fH]xEc"QPMӻ}am>OЎEs NYU3ticgiL YDN#^8N];j8B]L5^H7J"["UÅ>d\;9h:KzW_thT@i` h]U fs8yY~8-~{n:I}μ[FE,}[Qz=$6@ɏ<̽gC7oB3lf|t+eDx JAo׊nFc{(~=7yؼ3jrYXY]=bOTF3v0G -=:aHs穩@qE3,\Fԭ4Se[n)Æ)cG5KLa{C4ĺ?6^$zyt,TrtcB|U=j76OÒsQSJ200G\/*XJkVjhQ03LN::|{9eS<4kʥ럝|Y)]U0}n^Q sؓ-eV)>|GDjX?D\v_=wdQ> -IsU<`h>>pjr}0 J %L˧a`0;ZhjNסfߎ=$[H+P˸}psRbr0mx\}Z4{ 4&`ijZi}[hJl/Y;@4OE9{↰K?Cek6LūDFI叞=6 /6L(vWL~:߈ vRkkWے(5rV1W| IN:)^KZN`ՠ7?>CSUC/~^h=9 ^RgjqZ&&gkf1Nݫ`_<fC+LI3rNfim%&[0;ux%z3M8&+za~¼):`  8'4X걲ZV2 yH|ء1"_JXo"x7jbìҬf:(CY[#6S@as Hڦf[:ob`L<J#QKylV*:$wf+hFJI;]d ȉ>!W);x9=+ D*2,k̓AM[ ȣOr\bڔorMtb# vVvwf 7h\ŠiM]1o'WjVUj'<(>jiB3kX 6cܷNoc ܩpc8S.IizNwe`!CXF;cbM6{NBZf` }ll+AL唉ԕez\rdUx4qP.5 8Vk5*z&Wy}lAxH^'x#vEIϋ1pBy~4<2޳_ף>\$}mnd"j<   9щuˋK"1,$$AglڣҴj>"'T'eGFF% |6ɆH;Zg4U\N \  ز!rR)q ֔V''ǔp ILU!Q&s^ȼ, `ڪkՈa%1Ú,B@@}Cv,GC(!^:u\(*Z:Sx4^(} 5Ė33 >( q/nB䋌؃]7zg p5MtVi8 x!62p,koĶ<8cήo Q)!ܑ+iHiy{HLo9iG|7]wSg@@LۥCw/ά<.;"B6SSjdRsm9g hߟ}' BZUgơn)&pb*DXn@MPd%8kxX@P^N^%F^aF>e}1 LRd"!Tno[%ʈaqE]@uz[d< 8kv|ؖ_ 3.J>ŸA%B$#?KŦ3?eN 2U`ĖZϺAKB[ xY+cd)]4r3ိ]3ɡiK;y*˞Y$Dъc x6%FkW!r( *i, N$9q; k}ଦL0e6J^y֊}v&-XDH \id'n5/O).хGړ--]lASTbPV N{͠|} IfIhp_'L?Lt Jn6ќ!hS.ݣ.+7X3ƽ@g5t¶b#JvBfC}ct*oXUr0}Gxw|HF<&sfc{\+ igu\UTQ߰PY%OFg=RKzy5Ó-RU&./fic*bldMitlhZ.+n$"^M%9..t&.ywbFت='fsH}_EʗlYAz0_(_tG*zj.qo#0ۧ~zIkUVZ-q)0uG|dn_r(D饮u{s#o*?J6VuQ8c}5Yo>fXB`4ȍvm֧7oYS1ȴGk"Yƾ*RN/-K1Tm:[RttA'.ܜk-PPf AXs6]9C~k(JrET-naAyZ;ϡ&~BH9L VDn%x7]>2pN/?Hx1l[#^˭ #be _!vPsN%Ne[QlA<;ʧf|$ۄD]6.3-XZAPu㋎اPOS"KǦUr"+MN'yJ:[` Rw|9ԇQǐIKl[d+k,;rATBd.zxZof7k\HӠdoN|ii^nДb+Z pEF\"z!%& i?TM*qr(\; @W>Y:jw85Zi yЮȕ 3/ +/(6V*?|nad7!L‰UKst@k.ڎ vKsv0z'0h!3Oܷ5ocBSrШ8%S6ܵyz.U T㾻'Vr!tƐT}FL|_uHiLܡc *WȢ}f\sdWls=aZ?7uT[q%q7&tG* j7,CIx #lWIH[6-f8機'Z]B@OE^9`%c<knR{z#Ʌ֝8"T\QzF@kT2?MeYAӑ>ʋތg"٪*nMdWϴC>Մ "m۹@-O*$l+Xּ^sw` !'P-ѾhKB_B;[qBgvĻK@D4C/Jk |C?*Р҆)[(ge{,Lҏ ^cPNOV+ylVShj8Yz!&zbi: \3/ϲB(|XEl_0Iz/wϜB#9 1y>4wJI NC1?sIy.;i{\1svϳAum㕬r:_NBf"0@T¥n(0GiKfj3ŴM$6»${d-ɮnRLڐ-U1ҟCW(KivK?@gif0&uF7zV Z^ӷ%;!]W[ey BSBBf"oJX.$HKQlUS Q$_$\gZڱ,m^U)3")%{xŴ(0C7 ߴxgא'bc|Y ȫBm*M~~۲F[*JT)LQdWߟ6 7%(l{+/\YA6 ,$~hi G97!ZvepA̰7ĬAҍIu>zH|++!ϫ3Rχj*y,^њZr mg4+JCՈf}a&D4 蟷Ư''Pʅ}Jn0O8tû L"rZ"@e;pc4t1mt՟N+’{٭vu{lGdL@.P@aVz9E Wjfʋ߄VC{C R1fN- w[hYoxgVkjnf*rvےno+ISNErzVfxg̢ ZSΒDDw; f)ߍULyFsz?s6ob'=^FGR2AAFL& tE6y5\jZyXy2m)T.v|\+77S=cs# zv魬 (Y 3(H&^\(z®\Ɛ8v:N 텱ɀ/u}e{a]\#3WD9 ; "삁d bǞ}h诐&Hҩ5m)KNa?KHxZpMjtJ {/c栋3]d,pYnt8 _T4Ejo9۝+){l˞;.n vq{y({թ'^Z(bVЃA1VRSȡ5*ћ8P@|,}R`LDU2WC_Gʞ3ϥ/p~t\wOcl4#k9"@Y:&QxiID=V j`EF^bbNxEk'=n'! Q7)ZE7;u7+{S׭M}t?+&V i$>Q,̝'^.&C2@+j`gT)B+Y ÇT۬ X.7f m뢳S.P!JswO1_IIҊDӠ}πA&B^]yzY|,*7ܩ &HZ-J2iC{mv_bFmmfBFJtp׎2J52,(6>GW!Dρ}iELG!P5i2| 0-=%G'[8;/T"9>@~ѡd)uqe咕~ 0.ī -2$ʛk Uhk:ٲܺt?IMS" [ZBFް]j<\Z85xGB,?j;E!;+mќrJ.bkg @x)czH+ϟ^K:N!KVWA=ڵc1 ;6m|_#Ά?ۑ^.jr?)Gt-!0d9ڠew'w+s F HvDLޓJ 棛݆] Wcġ٧ߏ 齑Yai+c$قWipÝIxf#6$BR` Gs|m, НM~.Ot=CK5v1*^al7\JeN ?[)=ewpp(Yn2>SD%/ـ@(6<;fCx 䋸6^A#>w[GOaJ |6J8Bə.RFϥt,$cL3!Emޓ]G婉܇J6pqԦmmh 9cI^`nDnO4*&\v]nx)?3'b?Od}=ٛqJ]ό)g3h]ܯ3ΌGCnLFԻ\~0=-AQn ay =o;I۞`._X5^dLEUvA2%zQ`Cz ČX $AJ'BmWLOդ9M1=W WJ_n>ٞgp% CU5lc\4 yP&\I7|f/V[T>NF޵'XyH̄YaQ.ExWR'-J=j$eSD-ۥ KSnqLc5jl~QqSW5\+áy7c4POsaH$; )&0ro)oiYCz]%b9~]IrҜ(` zOGy7epL"ۃ Ɲ9[!Wo].8֣!~ןkqXC_72o_eA-A0E]\Tޓ^zSd'n ǐ~a<]! &/V G쩕_Q55/ z>*Ћ!xI|y \F*o}0Op#yyos/7<=%9l/Lj6pV"[BQCq7(*ÔM^ҿ'AHi&O`O-UL' Q߃>v&@T[O:Cfje6W Iq TPak/Mc)>?-U=P0CL0)ڋmpDžs EjOR_[ E)i%ijP s]s Ŕ{.5mCl>V(d2<myFK=TG1~LmPEɝ5{2_cZFm-y!C_^Hx; Ճ#g^7>9pGnӔc0WVݩ6C"} c$%4mxbJ+JY8_D5~Js^j?(#+ֳTLk2 v7r:d Z耋@4x A3J4t`á$y(#HijovHOe ې!D7rl<4 3BShOӖ3q) ^:؜z] %o>xU۩l"kS69;Wjc'lazmHUntդ|ĭovdS~B2-!!&>e qj]>F EгX(͙U9EJ?3%6YGx~F)ݘZ +@5edE$l. }^X?i J=v21_ HS*rތ.d)Ēum;RMڽ"خVYμlQqDq~&e P7ƨ=>64,'?_HK$GXO"<8hqi*l eRIp>R8I?Xר߳oK˟V)PJյN&QƵm0>WELs4i+I)_o&s1R:vp" $q =~9L};ge'W%*ػK2۞ M#R7Nn 7v⊔tTK&`+\JFÁF_j ӠeT2۸Qϻ`xap׃Ĩay;K:s m u/aZX-J2-Gvw0f.?S0Q N۶i۶m۶m۶m۶m۳sѫ>+oȋ75rdM&#@O_I y~5ZqڛF>[ N>>Pd^85ǒLZ*id] .he֍4F ך Num(n7W15C-{bԦ;}Ƴ. M4 ;*ԝs|ǴCiH}| iP5{-|Jhg ÆhI4r#޹ONGķF @,/)Ty*\jcXSQEw`mMڈ1ؚ W(YZ~Tw˙<L~\$rkV"m9/& j,dv_%hsDŴ 4̨p㯤ԸjkF\d3;0i^u<3%L?N2-w)beT RU{DvIl4O\,jW]j'Ϡ5X 4̼= ވ02!Q%^Fټxs' hW\էvcCv'FQhVZ; .U巿-nqڊQRo-eJ0#wz:l7J0 wwj!^0`T1OD_b}-FnW]s /XmMJ` ͯ۹/ъ]V%R=]ҥEOYy#i \q\ `=]'\ҜTWLwrhEVشїU#oqsc$go7#%x:w% =V3?vl*n&ivDTߠecV|4jI`5*J&g-gŅU6%lR2wGъq0A_ T1NAӒ?A1Ζm$x͕zR˷B -m@ד4 ܎X¥ A*8|7W(`sE3C`w.N2LivksW,Y7`Ԓ0+:`dI^/ӆCc[a,JBҸp1wW JYK{0n˼*7~ڕBk ϖəm_gU<*S&7tِyݺ8GtFۇiKH(W)2qDKxÉ0 s8*^ af R~r lrZeXVku&G.3/yɱӹ3_X Pn;-?I6kIJy,"_M9x܌w :%4b)EO"Κ%a^ Xyc*n4跱TEs%9@W F6i"'eׅjȟ {Ϙ OoNڠD5)2(O<"sg)*-R+R& xyX7_| CV`q>f=)V`cM8>Otz45:0|~˧hV$w|Z/HOdqLkq4UX:ErP7<]z.F$}em\TjnAZI7uh%fG-b׺|0TՂ3ZKɫ .%]FL-4(r…tl#GzOĞz|S< &Hǖ\T0CnC)Rhok[ldT aY᜹{v,vCƒ|%$ɕ~>w0-R鐃ZS.s3p9#IA:,%4LlVD%jڡNjSX,Qt̲S%4+[؎7:?+ygu*g{'K,U@ ڞ.gg#Qq_k vzjM,Wl.H6>kV0eg14-#ÁRLfƔJP|[#_~הQ(L6L;2ṋ M/ uAVV\x3w# c%c?0^Li\q`-'*] ['QH@pWY$rˍ 0aR>vh,Hۿt!(sSÙPYHdx l_R!Zyނ]׀k∈0&y/VA7wT& ICXEˎ52дځ7"F+;֭u4p)Ef.r&ۿCv*=rB)= mX_i2ߥ (ibaOoGq?M\ۙ8zR= 4N* p;̠e, ZmVE!\?V,yۆ34ށt#_OWFoñغf$;mhb~פ[ ? 8+̯(|nE˱.c;)Ĩ6lb9\InO昒^$nzP8)"YʬU&v%_xސ\h:lqF_I ,ALVEhJ51(<;XpJ12OZ81=\d6gHە.Uy3}<9SYD _Me`JDsȏ(lMfۮ˱駾 @]+bP%@^!( gԀӲ\KꁨbMڽxA4`MkmnQfMCkd~o5WRyĜ$1NskZ7)'O})%e=oqj∶mk ?7| \@K^Cg+?\Je KTpLqi&b~KNkDpqTYM0 bwpΈĉP?Q?B@"H)URbuY(~s[=6gٯO.*LdbN픫BPY=r'GpMs F] (PDI9Uչ }7'Fؼ5iؙ+y:@$ rb"e9DK ՜iCfE q;;fg;"[60ROǗIj~.B&b՜%1IpX}2mR V2VjbGۏy4ϢO+)Ve_y! ;̓?l}#ՒdeStiTmn*i "h2RLv+b;`(J"3;e9[Tp&ogt'C„|3@ec}uԎ9&ڳ#ꢶ)8bO`[鍑ɔGYl5XLh.0Aj-S*Lg4VWVJpL]Kc)|6gH>T6ylɧSrx?+ntވ='+1g?4R"C!E Jfii@UY}Il^^׀Ġ&sa?_Yaٚ8@,= %-B{lTur/1emJf֢J-cbU?#`kXg6}a"ߏ~KRbDΡ= 3^iלO /by[u-&pl&64h2`WJsGjZ!jZIOϙ8Ca|FޕjgIQDi€S~z˿vΰGZ S$]l* X@gZLfq[V@F`Kg9ߩc J {ҎMxGD 9D6UoboBf0W <]N'{fucsS*@Y)\{c 8hM@V5]#iM╘-2ӱzE"WOyK^O{~uDH%W9% Q?aSДxvMiiƕqT6RL~8<J;ɗw*NrwQNU~q*k1#KHzxO9fݥ=/ftCm!Q+N„h_yI{kR+PҼ*huOu2:U2ًe8ksDѨv8)9լ@xDu*&d#Ĩ'ݥnw>k;$碍ty5v7&IaKa8"`cCQyM%5;!tf) !>f_)OQϦ*bB4&ya3[?*ɍrddoLrF+XFՏݩF5 Ch.?HdUy:lXu#y+ОQq)W9Y4A~!kxsQ38mU冡œp+cOU i-Nz}J'8' RXd |b.,fxN.zjZri*0A>N,g7]7؝ VJAoQ[4[m/6W89o4F.ƒ88ѮͱF#,T\sOe&&8אlX~O/S5|i:g@_z'.2:B H~ˑ5aT)ƸUiyraHsp:GLq}a ~ _dGEu#fw^ͨ/т\4US V+bjχta1`k5Yx82߆,ݧv}2dchb߅(8)i\RZ,=ު>+߃5_Zh[|(fc$ tV[>,/8n(7n,pKt:uf06~L2v@%67֭y|lCI>~QէAvuOٶ;HDaǝdu38Pe:E7X@T>eaaO1kAsvL8VW2O *#'fw; ExEKYQŖ7/{̅F@SLAog-iuHgD{٩pbZEL`%:5 fX>?Vt bV0Q"!ό6ʶ/\y!OhOq9\lKWxFSMe|!eAt  %PjO/(vsu?B0y!H#%.}[Jv~9״6d%EjWNcas|U^Vw'(9}>\zCo &/I2lGN?pOCf½lBz}*-ѲX53 Fh̔-mb.+t^N~Z\nmyOZ=JZI9fD)[ H}`زrO1(b85ieՁ]bŽ?Ͳ2RŜEW&]cJ|L:Aq̀{'j=6yae-CodYx/n=8<4'r당KW ɠ@4q^w2^\$q2~ohK_xir=>K ipݴ0M6Q :!x4ӮȾ  re,my[.5SYhgIOJF<UϜ O0r*\1:XG3=cbxo ?eOThPG$YB ǿ%8dfp4޳mt!@y^Rri-]ZGV,2_֩dCº/n +tDE*5McX 鹹R u!QU7T-U g aL^aMN2X`6˖>(/ΤaъRZ^yƐ!󵓤? crm381F߻#$ŕ}ٚWݒOfSڙ@jk[[aA/1 EbAuN&<kٽ'%a&@GhU3cThMK<)[q`\5}beʨD8-%j+-yHv͠CVRW/H6ђOy_tc9}H;clº`ݶS92]CK71tJNvaWDڍQ@"^S[99d;׋UKCq] (h ;11đnT^0B`V6NiTMǔJjT Ir삕9.%ÑM2KU Mn{ *i7YpNfӴEacZs)" >SQkAZtg|e{j<ǐ@ Q`&ˆ@mMUݫN+ZcO1Ȟn&Qyivw )]Ju)PF2ƫgST?^z\V Wn 5۴G"=UDOr3ÅU70^p{S\vf:Ι~E* 7*Z'ȱW>m@(ᐄf lVs3YT\r {=h܀SXLx[I^{NY6`.!;VM+1 ɳ)zh&)I(Uo5{"WVN$&8pWеl_^=Y\n ·VxSLo)wӻa[\D`j%?-x$K j,UDKt/)Dٯ.274hڳ:^v筮FW lE"=z y-"z++_efkx8*YEXktxsw-hYQSԔl QHk+T6A23nJ>\o9(Ժ_ 63X6[c!=Xx'M$#lrZ\*xmw/U1^X)9qe1İL3fy -KznSS r;Fܫv#G3ob_\6iSGY̋U,&L_JXDK?3`l3gCV`|+2B&p GbI-/Ͽ!(lCqIJE`v7I.G'Qs>|/ں!Q, +M97zcY2зοb,ȳi;Nax/;L`MaCv .Su_]z{KV'fSUEkq,w[[10 Q.n`wl75i1ݎ:#7d/wV'j8 ܿ \oȼ8b< K T(4|(^q฿J0Xoks^4G 4:i}ݙ++3 [Zʦ%vMdiө/%JOAGE&w杣Љ(FjRZh5B*|P œ7 ]19gl*S:@@?I GL-l1YdJL6c14d_C3W /^R5$`ޓ$6Pe=8'>16%TC6C.uf` &y`ܺUxa٦L0ñP5P,ZS0䷪z=x~Lxay ZUQCm8f0E'47ʽ~=^v oIƷs[AHWiQEetwl+ź?sˤ!@.]7(ѱ3<-SCt(kѯdwP7aMY&j`5ܕB&B[`"W43K\}=yؙT>^㒐:/Қ3 2>pW?TEˉKҋ[1>?m&Ї)e#uP#ҡ91G'e)-ɹ:0۲94g Y@ۖ؍@=y¤]>[Ȣs^kFIVv M c.=BrG6g[NmQpk+5s(…:s-ϩ<_.}Ej5^ujKQuB@ -mYyE倍}HঁԴJEb~ϧ78im| yb+qszKET[:&LKQw37|ѣՕ4t[  }[em`ƊzZNUYSU1yby&M2PɇY-~gUAfUoL (lfGVp,-Gl +N3Z@KFT q3`mcLTSU,J+]p^f)-z4W]|- ;,W*n'K3"% xUu׸#17¼RY@f iT F `Gݡ?Hj! ȩS^ PF}u{xO,=}+EP``/ahDIk O+/bghսy=Nt0r&&tNA{}1 b{;b,N#VRXdhaΔIo\K8EA$C|Né/_TMf5D,gc9r֢2iFfQj( #L92¹~>I;Iwo[_%5D[r'Դk"#lpq  6 ^y8E,'+C66% Lӈ* O߱R8{>jCr#-vQoDѣ0%γgXAoMEՒr9|e}Gjc%44o0V=;oa ?)8֖6-K" [P#ۥ  ~VL֐D!c݈)5$MƂџ!k^.e,'SW\PVJA[Ii2n{u@ hlJ*H,IAT&v;(h'84,^l@~C)*#zi}Vu΀Fehu%O$ ƅ:Ht>*t]-Ԑ~]VQ'wu} !ٍ١c33zM36_ A'@Y3:X\݄xe`ꁼ58{WbJtٞLr _ZU!%|A@&UF/'ΐ$o h r`Wް*Ҥ~:LsNJ k )nuhTKbP~\?h8{b.  *-; \5ЮLQ9|58cFّa@{V.\$Dn)z] rhNOjB](f԰mIaMJ޹P| S3%Jklk^l'KSr8 |bHO죫eڻb*hWrhS@lzzܝ|Y׸Q{I@Z7e4aC:1cxˮmغ]čsW8zԔq__֗٘ ,!TV;v)b1RNGVsNsJlLhw(vA)8>^* MФO'jcwfd|@S7&Fr)K$Y!"]#8(+W9pPϸju)zF ۰\9J{La7DBtKuMJUbG%a5r .O2G"8f{=GE0ޗ9S _.mV`bTS#RU >%i05:5Ѕm=)䒬-cWq*#n 2_;:U05c9s$Eؤ;"ri;RGFMv3eBxx\x>.+M5vHNbGT+$\?T5):ꬬC-VU [REh> "@Zbu3ȉTCڱͨ:CR̂m\qh`DJ_Bw+!"%Lrq}u<%e0W3܎p.[ R",ZՇN =Wn|U)MXS*>dYinS~|@Էp.YB D**"Wd_]<VӇ g!5[گ;ϵWơ](`P>O^~ 6fĕum|ag00VY^.g"~,Y׬KOjx(x! XŎ >u5y!vNt[%$lZ{ *ph^/?96m`Y+u>3XL3sP Nr,ە߯b( xWT.zb^3 fwPr|YV~Nٞ$ZD";"0]vyl횔sl${.ul='$'Hs%ͨ_N$Q=XqJV|J퇎z]?65~<濕8* %h%P@'c3~PEq zfªK6#b  9kj@ۊ;٭;:s"fŔ׹NXĀS/"kŹA&TISo!R?.&*#v;1 =V=0g prH V3Od=N}P`Pb,[7ǩX%N=(wZӧTbT*i!zE&`2$A.U_J sGߖ$^*JO0GH2d4E>eܘE? "QPtAs5Ͱ ],S= cGy:-&l丿J5`ϩ.+;o%( "$ $oJ^lud䚒%oVT;w6}l(;HM"bxR55ϸ((!IǁwQ"򵮂X"ZCXD x ۼ %]-(A 1D>-S !ޣ@X  9j]#\tGp73H%݂vAӐ^}j/ȎUbF*, M , H֡c.1/h9'@%)svYWCeտ8BBB*A"Qx!2m0:aE PSAӧew+ lPb\o'2xGы  ToA%sCYHw'!j XsOuZ  4Ctsm'K%Sv6egpr-,9LhQ)|Mt I쏍Jxt `U,"~2uPg/|Pdc96{bFUNԇ֌x[@qߓi#t3 "|ə$l^`YjnSzbGQS]LX,F$ZEEʣyVY[RCCB*&ɱ\3Rvx$k~7`CGiX?MIaoJMqk#V`S]4l9JTYvB-ЇT#inqqME 13 yG;Ecx#xv8oi S4TY6Eciu&pps97'j/0c#+O_)3h6؄}\'1뀽Ò"94qBy:Z#zwHd/VJN1ּAy5BG;tFyIob#7 4S6_885q1<| ū0PYZFXv&쎚Y톓6yLk`i#n|RFLЈSP "_wzηE !N~MDK}+vԠ;1}[ TO:-pn΀AvA/1pmXN讻1}Ųu}G/v29a V L|B]t  }CɂH8 mL]n\k ,X\%{e h((npҁ+Ў+,5ۿ1]Vr/*YLSS׎lm<>Ǵ"lJ&R(S_H|~zFc~<tü/#iQ> ˯%v /6;g*@U>A?;JlF|&a$l/c]yt$(ldiӦ 3fܨٌ2ڝ n~&Q۟K $@ w9 4 R""8ovHxv]n$<>AaegZcɎGj:qjo@AV/K+/zu! AvA4|>T9vDqV"wmxnF&KCA]И]E-ѠfLnIc 0am呻`/3б8b{`ӳocRHH|pOr)Tz"2<uM') mw8ZN{Eor!Q<.|(4CL|sûgSthhxr|h%/֌M|ݘԩt;ՒikMyn6Y4eiqYGNj_F6 u hP:aJde7ScsA'\E{Ƨ?񸝒IS5B> ֕az #2le>u#7%zVȕO`P*K:$*PK-7 MA-f#Q^5x1!U`hdHPǰLn&Cza!Ès%9m* rKҌ7q(x@,s䜟3ÏIVbT=@f$:߉JYB𡁗#^d{wc(5÷= f&^3rf`@mfɛRVJ J4{ 8J? @chOOHKor`0ʿKϳ_`J4Do i||X3b! br9Ke]-jNUtWsZ>KY-B%t:/20cW5OÑc;EB }!CYdQ՟%!̬:zbC]Ԅv]YdXMD;?ՋONS7AAWKM\8v?OE;{#xB IzuVG p*ك*n]ngחH| <re7腬I+ڄ0ffh4cUz"f]i*Ժ%\դ&z(VD?>YV;Ͼ>{0O;?ogHsEYĖ)Ro7=%t^Lyn/ίA l&AFyĞmUnXɶ@Ju0!Fv**+ .  c~8`2{f0*ww&M逮Eu}Ce9a›,=hئ2Qgc\T{[u΅X؜.3}4@ h38䜩js#8.tOURQk`Bm({vvO+q\ç _]\`o4OoJp{ąr7E\1"6¼ـh"&OJtH)%ٷ4U\CsDZ CϮ&d0D8 f uo1OYLƖZ-X`=`8 1"Ծ@U/z'3\Fa_ܽf ~`gsv3 ""o!e-k~Ĥ k/'&;t?}DԔ#|Vqk6j !~tդѡ$>Ib(P(ұQs=mKt>#`!7TB<4'lA`儀55hO܅}1 \4xCr8-J-b"Og. KUTRetD;uqLH7m\/E |Y$g]cpccfoZ%,hxP~GKtoo富\!߀{J&(FP$ ( 8("GʑF29fٚ.2TWŷ1i>jE;ϴމ99<40;X(%ID9tIlO1+Wi(0 h<Կn}}/_9o&yͷ֝ OM.lП( pd3(xͭK9t c[sCja8#Q[|x CHGA z$]q/,,)6RjT"r2"\ءLåۆf&ih%i];WI;pwkQ05/,isF [5 r5wCz$av0n̢wWA҃6vb YfD?+TJObIMH_ m`0K8F^ZI:S\^AO9hw5]]h\{,խaэ!7Ux}I3۸`| #l:;F v"&?ůeҝjGOg@q7̔ ;Y<@v(7j:6r(X# %_C^˖p1O?p1뿊 n5e$)n.K\Vyl%ʰ* Lee"LN,ǡE> GH=؛k߀mQڼM r1ϒ}@B/!G)JF;q_n)ބDd?u?ѱADk;]ⵠҀ+(*o^o\|Ex䦷jlM;Ew6q+;nLX J!Z]a~ _2;v(+=z( k~q糪@)O}igÄrCՠxlvKD!Y*{'h֖NUIw^$:FlDTi-;)?G-PNi5YwHsCM{E,1ZF5 򳆠Pag[ o6[ptL gl< E9'zOSC#P0'| 5?#ck(3-b@L.(to W=0%o9~))_'\D T.#*/10>0?_I"[vGbKK i%X!Q:]F})C/.(*biϋE0Q L"V :b^G⾗T)bSZʰ,*{2'3ҏC ƍo]3 wjКZ 'rbfΏI#ύyS[\z_?[P6o J{UdB2,}>5mljK(ep03xoS&, ~ "Ȱox; fJsyh_i5S" f)?;/5nh@|f,8\=V7 cjYYV*2hTv$ mw8(EFN4Rlc e=Fo<ezgo3s̻JP< yAkR_(Yt~HiNrwWkp@T6/Ugoz<k{d(y9wCjL^ ڭE/h¸3>3]!GOIQ>E Fg֬aWmӊ7e&Qxk*gEzDT0Pf@MH0j 5O.AG[{҄?s >7(֖_Ys{&c\0o`'LLn+]cTMO;upjY4XyA&^“0Iڳ>8 ƅ7o;n{طE v8@A-"VDռő4[C4.􌂴Br_jT>#/rY@'?{fpHet탬ȦН r&y-\Hpn,q9?Ұ% [f(`,נ_Hɶ.r=VGD!&ې9gJkFfޕpq~;+Tja-{R(A3)sfIkhFԿ8ls`KK#lTu47:fksA>.]q* uJYƻ_npf)L~(yO~H:HVLg1s<֙E Qf7 ,I$ژθ׀ovωKt [duEh%8>ropV q ZER$gk%Nv|hD* 9d֮AJtVoTIjCՃ -=n1M@NPĔ0″SL"?V<΀G:FÉ"R"I J2@G7f$E ᐄbW1PXj~]9W ^U̗znL3ceO J Ķy|b/ ?1l)[~DFutئi»ǪG59lonRGhSolF0!nʇX/嫋 07c_ p Fb/8ت4DZX bv v:%t9=#az?>n6|C*ik`"|܍"]~eK6[vN=1t2X*a!REZ 4L_跨h9SwseqM=u67q6xhz`1bse@Lt by @mY@' $ YMgCs ͑8Or2ZF1843 ">:$ʳ,+JO7M7.o>muw9ʺOC$T5*"+[.|e+AkNvKhjзr.^:Q1^voW <º 烽&  nfv+9%.Xg)KG&~c\ KĞ:аMK\]_s}Mf8EJnT𭉀:ڨfh*-s(kKg&?9e[Ln3 "+ 'tؤ+}'@SkdD_*A) !1p;fj:0lwzgqi?wrLZ#_hԅ qj1 7sKem)]M-/,|v˶fܠp~B3b1BbFWP l0~w! CdzCA<O*M y/ KVMEb6C.Z!_t'GM[)w$*;V2DVt{:*?MLdH!uY9 .G#^<6J#Xr5jWILlha# p `dIYVұ8m!k7\^''s[{r^qn _XHVO W#r~ zGif<6ˬk$#]o >LOy{D8TΠS0VWdwÒa6x3kzINJbȽP>]KEؼWk[~ p\ sE4!VH.ukn,hO -dZ3( `wkQ˃ŖoJ0gԹƎfTGQfg[2#uG7j6HT}^?/.aKTI6xlr73m 7#B]Rz:,)KR dx-r-[`?ʗxm[BKRP %%2g$ D&J>/^ÃcCc{Bp1=ϱ9Uai_9ͮX3(XR߅ZEy楑0`cR3 sS$!bu"_\ƠÐ:>7eKNp0tO5)($5ma~kӜ"DsI08X.Sbz&^R޿3l=7xM%)|'+[JS2+w;Ѭ/h[ia):?"Nea{}-9lE?oo-ӊc,:qK,M,_ѮN/y6*-5̐G6upPjoC ^psSSu-NBN",v7&d^u٘1x_svdA^CQGuN ;a=G .^s<7޲`SF=,-,ͫ;Vw4N3k_,'Q۔`\] -X ]0$0S$+<*qŝ_daU51a/  M~ Yw΃,3U@6y]OT:Tx $Iq9Wh>D }ݗϞz$Ls/pޝ+$ߋ䂃͚ro0|M=pFdriDiV9{q XUܕRc-3X_R7s.-8\N+h73bw, x\"F^չ G;Lx(?LK_ןCꬴ5py5i.p%$qH}vۼ0+gcV .#IzvO`9ǨRYy3fQS2l].w"W|#Vr15By~Ŷ& nϜrx SR .S(sQ莊l {p *Z5N~ЧuvJ;)Z"@P}Z.\7d`y80Bw9Sr4YlZd!\DCaqmlɧ?WSUC(,Xk%/6dI^ eQF$HaӴيyYuWBQݲH/Qeahh;-tcEJQe*!vlOo!_D׭MBz#SI}jl t,Ҽ'zБ݉xb 4 UY[ S[ķk+"mB: >.@# C`ɐ+- W '%_ʢ aWpW@ !ƅhllUC o,<1Wfm)~9 !:KRqnBivia9WVOq\> )gF$.V7V USa #mYyM[rQ5Wjp\3ꂮ*p(6PpG])`o5h,jZU6+""ApVHټ[;Iot<%+V pӐZa2k?1Cm yݑQ\Pc.~5Ώ'.vQa -el!- ]Ae5Ӑl䤿b4LȄVt?*eZ$L\+cќ6nvdw5RgX /y_rAP'w4(Xй5xziЙm e/ZQ((h;N;cږ$$ R6_dn@%]G* ZFZT^"L:/+T9 :y%ʑ$S=V.GeC1|5rǐw%WLFgQ3},Dsie7QхjH֏mD)zwƌ?,>RFl%ƀ]F7 jF?ީ,ly.GżbFP  mM}4:pUG~oMe|(3{%^YI9YVv^M#wzjT>{UMT^M62 q-'֗K :B]1UGƢW"Cq!DGnkqc!*+F Ԃsmox[,뫞- װL!Y +ޑ_Du)>dMG0*Aʆ_Olqυ3X!McUvӘhǮ/=řBc|x'i4y6L-Dy uNDeKڌ?|k =GC|&=0?Yb0YQ,uX-CЫt$ޙlP^/ HYŭ,a7< h1L! - HZɒ tٔ6['gfhkw o`-S3=X]CKPJ~EmWf;\vf=ʩKH:eCɈ>!WȹWy8= Is+ ӍH-Ы͒@Lܚe TȢNsK[nrwb"WtvZgf ~ש{]~EXۨ\v0n'WTV&Fݱ|?c3Y v7bz+ 6tfOnyc$!ܨTp9/Qw <~/ IÆAx2.D{nc7O4J$qy[ۀmCẂY*)IwȕfK3)sܡ\ ?/?(?ĊM#p0ڀB;/yѨt`ݻg *:C}H!Q0\ΘӷA}Q$tbU+l^@ǙO!N߰C)n<|Aԋ K<@  +L7C- rrn{F)зa#3WۯENhS|nd.yJJ.fD(\It~%Yp|Qd.EJd"5FYֆ 1m_|WElqȾrQ}7 &T\q*S9oV*:jm})[fxHV@cW&/%LW'Y7*p_0E%6mOo)Q#wPRܜSΠo3GkN ~Bu: Ǖ.&К#]u.^UK*|3kY3֦fk*Br'@"YYRj 4…pAc!&\R^v. gx' MZl=;h QLGJjb]ut80PqGLSԤ` սu> hS6+o'T!icJZ2g!t2hk(Q88h>zǀ #RoԍH&bwO`O|:o >o/'Bh" co`9J jwAvgo`ٴ@_ ES 2|f ,(?Ɉ5e? qmO3;ֳv2 y iRbƅ: }uIKܾ ۗLv\UɲYktY>c2eG6!mXf Acќi`C7#T0!N]wbz>=ufm`DiX$)Mkz^ͧmh\ |RZjQEpUzQ}ږ 3N9dh1sTsg3z*᭾|qپ4 `<9욬&K@6ˉ~mA[> " z )u<S6c)? D3\?ΐ|L;o#&8_ u9Rc?`'-3 ^;c&KW=GI?~ щN]E.e|eL 2cĔg^[à&x"CǮh<+䈥 :;;7P`4vr0RnY\w90Dt!/ICڴ#W$W:Cm֜ȩK49sr5P atYh bLEm3p/u጖2~2ߔD:V݇DgSqf,RkƲ[aC#)lȽQ [捭X 7xHk'sbi6u!Z(MKYX\Lzr41psĘЃքΖ?碥s6qrugebe?QN+3@L D@@@#]O_[1Yږ}KT)uc=0"t*qh&V{J੊;G/:%P߸TZKӞ hTh%s ]5J Y߭<4:~5Uuzo8Ѩ:®[ !\.` K0y4ŤR?2Fv aaZ]BXe %IxSe/H,#Xn.|zO)o$aTeϙ>}_n. /OB w2шK5<(^-pkٙ"%'aÝxNKXTڏP'5IjaHv/_BTXNmYoYS9al<S((eN$6 :; c,X}Xg ĹHx, V„&,Կ=7 jPw8`ȵ CՈհ8Pձs D8PjP w;Vx(} -ƹ]LzYJ@1ۿ`RR?Ū;{;_DKçuW8=Ԗ$s,&B qNu}^e*r`c_`J?eNBP_҄{+1%a!}EU = w͢<؅1ul_UzB\mpXn=Jc.>!R%㊨#" H V~R3F$$Ƥq ̔zcÄY0j-)z{;O;;I^ 5J^zN!zH.kfF=|kkZuDKE 6ҟfuzL).?]> „Zr؇똧MUk ^c5N7ol,cI@%>~/ |}۪粣i]oDx;T|u\UW ic#J6 P54-d ᆂs8 .Im0ൽJETNSJ1ݡ`'hVE#_Є D ݲ)0{.|Ô|ܽ8/ļD2+ in[v|\޴\19GQ#\궲){㍑ w TY5vDȮ -€\oFB|KpN24ja5"R,y'm v \ AU8һv:~',adM tnu(u7ʐV5JDrv bqe%ލ]Q!ցJ/ICNQWa)%Y4%N#`'xي nh;wuCM$|`.Q27>_HOԯHz(Gj *ձ5*B ;C7Bq!6:߷.+t[q[nҿ`n Yg^ߞ",G !w&Yc쌅zP6*9IgwZ~6GeLYy3GfPdRB$\"ڱVkmvB"NXQܖ?TUt Q~@@:?{Ԝ)`'1۾vs4y#IT4JԄp*f9bRY5x;g뿟7$=F>]4W[a/ex< s%%Np#؈1gΒ - t;os}m_=_]|--Ňg&{@}iV?aff UgidtUHPtq7omI,3*,zTamS4S4r(R%>=iOs, +|PċhugJǮv/*Tp(,##6L->9ғeT9⫿4iLprUNW462up3=NŞy^̤l@Uuuw{SW IA"LG7Qs0[Q# z? 0tqߕpuDh]bDJs70$no7!70IrҊ:NPBXA6EZ {baӬ9E0&!³]}蔵M1 85$ULa{ G1M6#p1N5t F (Aȧ]VpHBq٥DXՠDp׎˳MjnSdXdud:<#o&>NsnZ2pՇͪn&|6%kƒ0U[.mK .Uq~)u?Yò4%*vdRg7QgM[*#K3>ĖKw&]|%`m [3b"/ ="m"څBn#5΂u㻋|U %LN56ީbW&J\l9M(dVqvc^Ƕ~Pqu(٘P j 8rj,/|x6btwC5dz.&.U 9iK)Y ^jr9:JԏnB(J_ŧ'NHvwoAWpg1E 8[]03rk-Ia c;A!C{ܝTINGCm$OaZ좍Q$d$E\_ȪD|d D绱dJ4?y^^8Uڭ6NK5! IbN{b[uGmC.q~]sόrRW6:Muqb{f qIww!z/lU&Q.w#w$s#5eK=>* `Hf˹}_&ˬG,>iJ(ߴ@|4VdNLߝ@۹ԠEޟ6ytv#n58Y1u]QޫzZhYFj 'MjJ!{˫TW@ 7>14SJXw˗(2"_ySsHiY9L«}Sy9 ~._:k!6h,S֜D7G\9d AYՖ%"jP}V+8OxbzFFr ۼqv X8 sXZ?Yܮ`'쵲.dj0$|43;ztdLIV*B }P*57e0]tEfEcʧ@t@ !KhW-oR KQe" g+';cdBzWS/9I^>4T;P[(@dwZRL!aaUn}a/?Y-F}[b fBbaR.3{qL-RL4MAl|맷"􁧾2㍄ MML x>0 u*D衜|9w`(~&I8~P pt#՘IL$S0ePWm @\HM;].1m'1GDZBb{ V S<(:g Zx]B4<6U {!"kߕύ2Z]).`LE+Dк͊|@ D:c(HWE mr0j@Kr=)%?V ?x[ba^l}JvшF;ЬA)pAj]iA [N0wM8 ߛRo9Aį@I&V>!vJg7 oB^&i[Q# mêZ9oLBH{7%&U ]U)}t,9. ;|m&d(r2nWaU?ȸvt} eJ1UR7!۲J\gjR'E>n"=88Pbh0(>l vREr!-${B\DuT` #/kB_JĝHqt֥ NgM)ΔГwd|62!%슜^VE_H:8fFF.\%ڿZBrąI졌ɅQU xnSd3󶆏嘶"od&.8K}.S}2$Zg1NjU Ɉt`1}V^v <+۠|zڟpKmj陞>ƉG5Nb ]2y>(N]UxGj KF ۪4˂n,p~2cOHamc-ʉr M%I P}a0o`lS!l`k7hSшV䖍{o ?x /雿4V=AVTo%]㶌%2S =.Ss96=bK[TvWUd֡*c8^0fQt0"%\"(bw{G\N-%*6CrYΈD-Bh1"GLȚm$ApV;}_Ll[{IƼX@R^n<+_Ąo`ك+MX Qr1Z+S7rn@p`CN07cU?;K OFބ#yZXŠEE]րXJ#Ls%B`|7>Bp3>J͔u-@ 鋧AA*,21]xCU\*ys*>u-7X>Koۓcس"nڑȬ) wPMsNq1ѤK(:ZG՘ dqysߏR=;?M[#'\â+f/͈2!jv|,w)j Ft㼧J3:obO؞I09R lb)["`;gDtJ䪾Qz+WplY<~.TG+{`#"Vi~iUq3,UY+rݯsД͚ Icz<˔bSf*僜DA<֔G䨯?7w>oh\ʊ*6┠=%A dH2y'kZzkshE7Lu4сRl\ y~gʫ*S u8&|_ّHQHօC? xcJ>pZ)^ 0a@"J<8qLDVC@ZMA%}mQJP_RKQcg g9$є$:V[\J>Q &3t[O;ұi!q6Ge?Fv T>ZtCVtDSM6hQ^?ù&}j-1DZOBa}# @ΏUs_=v> |%;e϶lu#5]ʶYꯌa"_a7c[[#Y ( Vx)gtL+ ~}}+R5绕} k&#XھD'.` XUAf Dla·s\^<) No Uىpx%[bĥv' $*]hbyeG"[34=.|!YQ ]\{>7mʛ"XC/^ l,)1SN=w 06"%}MSL]-%`^?@T,>Be-Nn+$<%5 6;^8y3v .%3 >''{#_G# n&hʫ~)WBLAdM|ճEP+/V|Kb梇 '}OqٳsHQ{Rӯ 5Kl+`u~bZp w&_$6tz8co;5oy0RѣnLЭʗgTWNO{1tG&(w5#IJ۲[WDIuu jU}s=X6=E)"c~d- Oc|l,4ң+$?Du<9T76qJݒ|]YJ5S҇Hj(XU Rrd_iۊc |y=뱢a2 ,ԿOpdsqӸJ\nӘ)KDRNHCsҞxB;W8e}LA KH7CBa>?dJZ.oV܎S3+s\U k7ъzt䓇gNS/ޡQ M;" rU; <3R "ׂF+ =⹥s#^1܄#}n~h7jIa3w?1UU!'= HJC{ogbNi:pfツi˂>u)q_؎$iޮR&Y_h .HgU\pa&Įe"ۭRܳ2ײHH |Νnr76AʂձnCrRݯNU9u} gYL?YX(a*Ǵ{I\Uw/AGh MZJׅ9Gx̉ÐA ԣTm{gG&S [G8@i`+,njaOmHO4ngBSkl5ŅPOh8 , B>߫h.>SVͻ%8Nߢ#bsp{-[c5[CĂ*o%>hVuڡ~8`[29bvhI{A76*IeZÇ-mF RƶS(_t֔~—Rh4NPcLbيH9 X9+s&v @t^K( 6 c| 7OtPw= / %Qb0ki`zo=hOd{Wh_eVZۜ`rD>jNe^D q%!Pר SO<F; 9O0g}ҫnv9=dm-EU4g!,\ N}Y$ YϩsciHDUHImcsRV0Ӵm/sI>04hSc畔T 8ۆϿq+O imؓYw5K{29~t) RFЙ13P@S ܫoe@-oOw3j6_4 Wӹ WM$#O!"Ć8 i; HD6k|Bd?{SSHxRl(4_w9|].&<)hd*`F>`2"qRR91aQ !Wg!mToLMQ5֮]@Grx=T(h<6('\j&qڮ&qx=ipf:1=wcݴ!7IAY*EWJxBqA 5=Y0VFgJ9Kaoل-ND(OS( ww_ׁ2(Ђ3'b{ FY%R1P-SHWHxE;RS,S7v*pz{l'&D ~-GE< !N֯T teGbɺ{-Aȕt1PZp.lݥLɛ9\Σ;'G]is 9,bMWBz_!oꝵ#8nZ(b5 j'Lu[8ב&uA3U2lJPV_on!>Ѳ&ER; UV{'Ŏy)=0B珡 j(RjKek|Q&E: jw^XHBis@`"E{'E 5m$I !ӓ`ZܿF%CLTދxBb͘~V@Av._>[#jލ3{8Ѭb2@n K΄s%L+efY.vrM .\20Ec =2=c_%/W &)mu65[a(֑s:kR+H6964 [}bڈBmOҚTcE1dΨʍ*-)bo {!glKeye+ )B: a襵SMq8t_t]{"OE> >PzM}7jkEg7+v} [sbn/ˣC`/(AkIcF1?ul]SY*EʸPo<ݩXyᝅ|*yns6/B.LBq*.\ @D'0#(7[Heud4:Q,zdYt!Hglg #Ni۶n[`FxvC1j=#ęYq^BhA0k7 Irm-K4cfVQtFl:K21ƀis3_&ᜡ/bX mKo'&7yd3s+Fl5E&Ep˚FI9PTW~ņ%ܭ,m,݆6 V = 2Y[lד6~]hJ6@T5N_Dz4cHSQ]pΪ$dwcE"-&5+vL뱫;{9n&0bƃB7-:ѻC7͇ǒVs9p뚕um&8G|-Giu#ZzR|"^e2]2:2}ɡU(Q"U5sUM*H;9kE䎬7j`'XI8C"mn,sWT$1ZU Ps!-x-<D vz!Z˫Ֆ JW0rzb Ov"J eC%5t[A6h333T -u[{Ӽ)N_S~-3O8iM)h5~r^6Xθ 9j Bg5W(jR+J<4Ɵi[NuGt񁜂7y1@'ٛj$Z-foprWo˙3 ʊUN{o|cI;4CvfB͆vJ{e1 :N>?mx1v$&o(zy鮧+LH2&sf=TX' #GC/Eׂ/KU}:~覉]~] KF!'^_`%2f/:2˜K(qνhsH6ޭ̟ORu v,E(`zVl%" bt5{'kea۠QLgVRTt5>Wߝ-גhT=~C]Nۏ$E-N])S[(!p%7+9OFԧISg0lFySNJ GZ V 4m,E=DZ! JCeUD&'{#[o|Kշno@4N njpcK\ڳt%zXN֏ĨcaCf&_u3z-d!Xf.1'pj^-cvG# 'lqީV%YX]5HqW%DQ.мѩihXƴ{MwT&1NEV@V{tRipr,'ݺMKd l2&Ւ+@XI:K4uuEЊ )n[CQCB:AU$ RauH_n,WA [bgSlIT+h:0S 5\1 V$$mb,EV֡'H͚5[G]ToR8 |>0b@OBJ÷ȐIћ)!yh<wx =Lc,:) ~BҁC5kڞ?Fx1Q(9S/@RZ6#rpCXa&z &M0X2,g[\}ZE;jZȔ¾M5t:<#uZ@D#bab6V/˗x+ =}'Aݘ6z홼K5S TjqępׁGR F> {&1Cz8]w DSZY75:Hݫ6-GC~>;\/j~Z{ ō|bS81fU+BLԘK&A<8 o߾w7vWJypE8nEVa{$>IʞaN?]jC(Uk]ؘ)}qI(VM$W0?P7WZIYq. ŴKyvRcTI{თPs sumT>T~{p-sa(o A˫ \}kl4/2rOO."+*?=S;,O, M/;WiSZˢ=j:u:@Hk,t}ZCaWKeߎ1sBYtFm.Z>0s!7_"E>JI0-P0&,0t!;.x e~=O8uಝݞ '1mAFU$0Z&Ȓr!g*Rt/#8t֙][ fS/p9Cȼ?RV|3HР zu5a6b->)hIZ+Μvb|716P P} C9W ۬|,y~/@ hѷAL72&tW)L*JCl-Hg,Pw KwZ>s4ddaq>#hel ^Bm S GN`-wx:=p1lp8©waW3pcyzzT>9568]etw|XXCfy%UMOGc\y[.`.5=4x+57Kے^䎟X; -")olJfR#}p$ ^SqUc# kIВ f`s uiZɥM>{rF`4v7AԪP"f)ħb~/R#ݤZ+oٙL{pS+4F Ah'G2I{7)C-KHL掯6! 4ra;K `b!f?ym먤w?BN\לx60mPM$o)x+#=&S@WeȤIvuŜg@e0@ͪ<=xNP&p|Jh.6w 51rϬW!x:׊s2ћ#275NVr*z5]Oue,ew0:l$W:=TvA͸S8 xEdLjKͧS2oj0&" :.[4 %B*ˈR]V kyޭ)md8ٿA!-6C\dɡChYj(G2sR|έA-v^4fl͂ ~eMlnDB{Gy!#.O&S3=1r VkyPB;!EWnHQ#1`#K*yɪf[ 8u88ڃe[&v{Rp7FƮ8x"v` o ݢA^OQ>ʨjwG9]*:rDv'"yqQfRnO]OovjbH]^<) ioG#¹Ũ9޾!B67Է6A65Zyq-,=saՎ`ӛ5'jdYibzip>f!rS1P7T|EZ'SwIQ۱yxG#ĩ=Ϊɑ @#q:/`"iڗ޸׮dd^Їz+>hT0w5΢iYO7N-y#Lv7gbZMGK TcpԦ4Zp6tП ]wb{Mo{Ӷ|TxSY|-rQklY|:ue[8akXb޷,) =UU`&@' ?+KH\Smv4x2Jp_qQx´2jH@l%5&$~ij:'(i.jqўäx9{Udү`D v5dj~Zf 7~D [WQmv/\ߪ?~w(a+0Gz7 eW{DҟQ!}^ GeBZ96gp2Q8_BKc!|B/9v75_s=BG7Sb7b;645Y3Pm=DYNt|eq.fO_qߛeJ@$g PQ!M.VFb\# {s9 w*ߓ_]ZOv7Umtyl(C}ƭU4CLON"~A(_Qti1gF R_ں=X }4rKet8[vYw9Յm^M;ߚ&N)=kkbSB[Na C* rAWk %Rxb",H#L\ݴ6|dSHJ35쓚/Ca6C{$l"W*uh? NP4n4WFn05[l&n V*I&W}ӰQlOCj ȧwΰN}V$a[}f+k؄4erIMh=[Cc!1)଱G'EFtF2| ;ABΑeEFDF8dmNu6:e&8Z1+)jcZq-;ȋLhW#{1:G_nĪUj F;Z;6aXp{gPW $[|4k +I^YF N,b k'u=Q. vFu\&ayi!AF'J><%QoMV8ZruʷDx*]#DH0;Az`gOIE]odNRq}`8qS0e1FMĮ#kn6bIn"1)K"śV c]?gk. ]:T;IK+[cŃhBWr4.d&y̱/j;k _wcʃC5;bJbk{C_ ypĿqHX-:(z#Q7-i8<>mr({THWRIwx (TX5Ӻɹ&K{ns;H!i_8e^ 1ΆU&eCj C7BMX/RIV@Fh5 -3gpw1:OQ7d5$XqlryʙT$gÿK[N`|zg`9b`7c:ZQJB.ia}pMfR8) |mj=@S.?2Ne*y;G!:?Ef%B;%{3.ܿS[e  bGtOST;d0Gކf9CemkO*>M%As"%̞B^̜RB?N> PlJ4-IxH4 +1-gkj%5(QDx4n|KNS m$>:3<0uΩ{}^U1F#똤c(rt?n S`);@M]o`[4" :M)Lu'${Obh@TFOdPd}${W[QEc3/9H.n}={T|߽PPdEVt fޚ~@XS3Y]lD\a K~DNm ǣߵUQ6VP=c>e(,6jC48VHPVAec3*{mΘ QlUSD]<ه{c5.CH4Uh_㡜 wWDx ?p7${ ; C(,\oQŻ~^7>jz~Fw -E<$g2d2#__`8t.X|>y!S^ʧ]t~Ä`PiՕܳ6N. ߳"T8$ p4_1v+9фFqI},#UTXeYEi%8'pr:nXF 2\O~W*ZvSAFr.2vOLsLl=#߲֗Wh_;j<ZVEM;n36E䛾emk ZMYuLBRZnul0Zz<[@xIw*etXFsDI!&9qݍb\?@d"M2Gr!a cDQtFVIX(fi*oḄ$'HSʎ0#["gǙԔ,e FM*n~I GpkK[2/=yM6 O\JQK"Lo%w?䉡/0&IKifj%n+)G4MJ36l立1)_>vbg;{P"N5 ;"BxS,zw(G2 9:Np&o"K}Q^8Pu\ֹ׭h ,;;W؀_5@shw}ڋQ*0_9Ÿ Hyaժ6|\񹅥k^}7n@4Տck+y$I9S ca s)ψX@RHH04!753ExOl,w9ۑ /6h ARϐa{"l R3s}f|&y;i6Vp3 4,LޝZ0_SILђ)ř$Iu[Qg H%d]ᮑR{WY( C.Y^,MYWnkؘ**pFfSKq<0z {IԋVSp*z )cq=Ѭٚ,KүB (Fhc%s]:RXxΩt[qo@j`Y&x'rA/E!BGiUV`$~y,a,@Qk{Ҷ\{P{='}~*c t cș{+] &[$u f/T 8礂^,2fPG7z)wa T !۰b.,H݅34 4z#EɋUcd<0V9N,X7NFRwlbAv7Y=JBa/pȁ}uWP=~T >YW[k186BhݱWK<>}#qu3^y\'{ۈ| p61>f-34ᬉݱDwytzK A&k ѡs4\ NTy} ciԵ,C"6Iy*ݶoKUǸUq@\.E J3VQ||bSԏ/IW@kPTk0<ӟ&;MY!E1hMw4EO`P\P^'?Zg!vjz\>limO<Ys +"X\AI)â1Ib_/It+dAWn]MOJAsVt{}m*R&{eA=OYZ:[~ݤ){%\PI!xo2O͈},9#qw7~WjX¨s5bRN.b%co*Pe8rZz#Hb m3׆Ĺw?Kbc3  aE|_tхspj" R¿~ռAo,ݍr+]zwt\hNTohdƍRķYnsXvX0i NFa2oM@Q0k:3~?XNC/Ѩ~I ÊBa13 *w;HQ5;z٧NwJPJ0 :%C qB44jYgutR՞Ϝ飲5@@,fؖnmבyt[Km[ɖA*&o}`Ǝ"KyA^|7Rldx.blRIl!WڹͩCVXĹ2mAŻ} Irnv=]J3*n/Nl-b_orw,7cOws 'WɰP襢D[I+H q"T9Z d{X9_/L2{$>uJzG M0͵'2G'Ǯ1H% LxTі{5.~XoB /&wBWq^}:F|ڊL -e-̂nL2 (u1/6ar@y05E>+JI;n<=/Q~{b4}׺̓Sܹ֝rjb˸{;G,FQg<w)vu[Y Byv< E[}Fɯ͂ݡhhկUs&@ޯ\[w_{ۏ0iZhՔ+ `jg3ÄL %G Pth;e2Cf}[ 'X!U]Q{/ a{F\oʹD^|!v1p ;zU 7pkڥSە=U 8z%X)]<ʱdM Yzjjs*WX*E|8} g#+ee-}CNYW(,jTe[Uzq:Vg[͘v1inN?jU[yS|Z/L\I')_Wzboh%ClI|ƮDŠr'*i!2΅b46p]_GÊֻ,!o'<"Ysӳ;o8ݮšBaCP_k\U= 't_#w r(OoZ@~_I샗W!%V1Ykְ=eVcp~^#mTv7e{ hanhnfMo.wFS=2lg^U IcsOE\Pf-䰝7¥'GpYgZ+p:7OB3\Z/͌ #+gĎ,N^lȵL.#{1﹟[˼әx;'B.=-H\ύ 2I#G\Z@LJGB[y M- lǰi6j֚z^pmnl@~@@^8l>" ΝPiyC70AaO>!?N=CUIBz#l v1n3;Yc ;Ƴ\b\KW;jKT$QOv$}8OCyM0CkЧ!@&4tӻDC-ZBUgTu2-[WJQvWK׽*qF 15ը(_L$E5kG)WzI]8Dcf>\bm4>;GmQU6+,']1ޛékLd/R{3zqD*nM`5ӸձVf i}<.ɷ2Uu}-Uaû3>Pj3 D&dL?RKJco.:='S ;=1>IwWIc "Yqs9}MXR f} 2[ԍQ3єC:X 1_?!X f/E1^~ N#: ojy.A7tLc? Lv U{\tX]C57sQ_D4}@CZ?G.9Ot++l/94Q]RV n*Wqv/vv; !ZCHDoEvL,ه{7k.W^&*I8ZV vE;#DɫME:ka `^Ⲵ,L5fiq5 88y_ 39 3WVPlʙvw x{b1 ݲsС N,rjGK!ʬt^~EWT}O٭]WL4%/JFQ*8S~/5*א;ƗaCۨL`?$X5ΞY݆}82A%*a/ q|3(4kK}QBS?kUJ)3:rBvqojEmAa~AIJH]!1ҊO1ulC2G48+Ҏ!9QHx{`N;@Zם(&lX#)+&PÝJe5ɏ^EܦbXVmdͳOD+FoݐagѦF#/x EE }gXMɦf!,ʩ:` kPְH< $O@MlM[<}^ψoΒ z`ɢ!ir7a;}r7w1!\CAzRH~@Ĺ뇏Lin9!**&-xlgCS S!X._kpٻetC%Z4DS"Ş5ATUZrRT-;Y>gM2@:bdJl^.⚗izʻDJb!  $6Igd5_'Uώ5f hS){̛QG4yv+X%BWRc`R !M=PLSLQG0Q_T4c֏9xExCh\ǖw'w1~\dxl`d­fG ip keux9 k(v=_]aAބ'f7G;?o&6^y+p<;H% I4{&>03'm2dmNDjYK0Z+־jP0ѐi!p,A\<;DֻX\4Ir0?]D>X}2ĝd6%9ൡߕ>WThK<;5xKPt+(?њ9T9y>"|$\ Ý?@r WފE 3hʗA3ґ3G!\=.Q(o!N7sK* {SQͦ7҄׬q$~_-x ,HkT ><*ȎYLs/n^'񔄈A~bo~k_0lΡ'Kze`0\|bVϐ8j-)+kOpi6 : `xBtVeҊi N[]OI˹x阬Lo"ZCO3`Bn'Өoj^p)`ӭ/3y@B_$vU1'/&` +#jQ PR^9Mz/N_OojW 4 4fa.~@.SL.[:{u\N-_f;X;2]Q>[c)k\\XkFӝu")Ac^1R6P4oVmܙK;Oj^Av-o@e,C7SdL$soKm)yU:4ZNx2U G',qXRMuMI}.2{$:(籃֪DmSnx벃|75|ؘijQ>RO-s$0 bq}cs(ۗIxWPcdl+\g[Vd`š; N$_!̀|= @7K~1vL ==;S5Ϋ)G]?u~JMHg˕8r#jjkY!C8$ 2 +c8b5{; +}Lz}-M2h1u3"62#Xr#K 1f.*H3'sgC2ĭ|UY#5Nsy(.M:c=qfn10~-=zi5W@pXq嘭k^$ZZq$x~%xDRW-e{Dʼdq F\9}>d~IֈjwoR`Ek">t9-z-u02B.COW+}af O0BL2:Af~%/kAR&M1^pk'sTa>iu$j4:<IaT2) "j l(&'s1|yֈF ʷ:Q2S3! ?HV\( |i?*ﰌ% 4T:gJhYZ}su<LL%zG"%tl=tSĊ6qgo=Y&EyRJ [v*>mjH$RJ![-Y+،W/p_} *rwm Mwz"F2`scw:^^"_m0RkHR_95pyZT ^ ;$C>dkH*7c$N Zg].V's8w`c&d=+ Lcq9~(-'|q31qݰ+UBǪɿx1'w6.=:EJ/ $ۙ# ,U_WcA Iqoˁr|px|ϣ^ ث?6d-ko핐.4 C7ѐt:|S~ 9/1e30ux4oAPv489jN3'+Zh>ԇ|_6_,z˕܄ihe-kl4nU'@Hg?GTs ~^ ŋXnC!i@BòS*62gP|MJVѹfgT=&M:namBPY_o'ΰ)¦AuiE&y0Rkݮ?i,}s;ӐWcT*`t"2Hn;umd#"k A/\p6'BCK:92(m0|9!u͈V)Rċ}ϕKr^qF=NF0//Ihn[ʷ660])FӫşvMq{DntFxS>psP)Zl`uPq/s "*O_5!ց6{\QtfLlozh{]mjk t-"oߗ{~XF_ID?yE\V)S!WCB9մ1+O5w]=09 S 4Aƨ?99Zc?ct$ͷ>Df>s,AAWUDMӶv AC_.P6n*$ af@݀L*Dٲݿe9i<: e#Br?&BgHDi{O~#+]5tFiڡ{'3𴥒Q}4fNZ)Q5f>9/X||HE{JZyW C;aUr 3j@\ĮN(~N AnD#h'q!faEئ6ɮא>Ȭq7p_ Xu6}Pt qzgpJˋKBdF^/)C=dw` "8ȁ.#@/b-(av<3ZƲ]bfNC]Bc+nW7P̛n'Txn1PNANC3\LZG-cc#A#H:2KIpfU?=Ȕo8i%=؀9YK"C|u3,3 ҋUWWw.na0Q}) D ^Ee[ 73ѓRe,iPZ(/JW߆NGYIΙOQ^ 2ݝWQƵO|TC؈)ӖK >*bxQֶth#MX$"eS}S0M5N۶m۶m۶m۶m;m|:/jIU:@F MN=V^ll<}}bҵa VLWYPO9_Th5eV+&PmTI/씮-2'xE:d ׮7G= Bͮ 'zEKK%\>[l$ļ98T;~R= >i[(.2O3R^G}hMM撦y;my1\\ QN>n*DWkLv@sIըh)wtIxr_J KI𨦲J=ps H`L S#Mf?ݕ{6ySv299mf{_`eh3uOu}3R:2)2{|(\`>Gm=m5VM]4qk Ʌkxmb)AS\z N8(\=^nXrcce?̲?vQ"R dj3)v.ae/4 0Mҫj)Ot {`-,g!ՌHDD$qv4A/Ug=,rx) 7ү8pkC+o{lU$9"<izWsOc/6wا#Y>IڱHs[!!j5M,iSU! Q~Kөkg\\MG딩f F P?7[dKCu௪=`2q{'-ZgzBO*N 0*> ``MVln'/oM3ٙw~?~[ ЁEs+4J(6 l^Zwq0_hVC \ٌ᳑n(R`Ai"Zh{z"1ϲ5[&۞<+E!k4> >JC2,6Iމ0L~z0(_$Z竍oJDφy`cا;&ͫ8v{,*Ň(BcGvˮK =j's6De3i c0Ӿ mgN-@co:f;Q)!>r4`r6SѓlfGM:BI:Qgt }Azn@Ju^,WVF O؃fU,AM0MУ}=-XBYi%kg)8gOv)ghlM㑩xHB:sƔv|f/0\1N*;@omw[5SX*<=o4D: iC_g1kiPI̽4cgHwjq+'Gk1UV-"NXx,67Ʃ{lhE i!^L:m$s+Q>c=3]yDzSϺ"Dp)gdE/LOy1C(ϏfG&ۿ{ց׺zԇ$O^ͭy[$7['!b:a!p!g>7:1ny;pI>$QbM{AVQDLψdAq8i[l*ɱY7oRA[vr:TN?7nv!֚!*$ʢd. WEAW[u-<?$fbXEhȿOpC؎hEy;6_]Μ+Q%w@BEUgr S漷`fe;⅒9­tҢ@|Q{>^OLJ`٤W;\aWץZ\el?UAT(=1-| "3oyr}lv:=1jqC[v["W7V( x* )00}Qi7/i!cQm+ԁGITY9oSZc[v^ў I*P,Zd$K<2_&^`]R}q' *!vhod ,~AB?vǬDu%oTcĹ<$,X_ z'v}cl*i +;c< {^=/H!K][kYh=1$XR܉=?>Π3< g/FezMضs`9'rS!*:?;p% )mX"oO鍇0X4 ̟7IRֿg2``8^B/C+!Nq|7e`❇s6"X-DEhBuYQA^l6GAJVZ7F ]yUy_9FAQH4缢 {9U \4}\qdɑe"[mH?5"ae_Cҵلz:i.R2FXtsز/XJ1O^)9B_ɝ)϶P]{R$p_|`I6z< !xcb2-IR2퍆UlUu &cp.ڂ T#L(3y LC޷Q0aj iVpu-lzi"`#4]m! =FYS,RX t %gM 6OCW.BcqL2RcƑ R\ NjHa+o ≿qOɲ&-w dG-PJդq ,8W9jB.إ%KJ#KxW\@3'ZCѓ%'qAM;|!!1~} svaHjwfξH6oti٠0";<+vBj[ X7 ctٕ@m=^{9&.ͅֆ? l=@EȘ5gw_xp<+KwNF@lX6qbƖ `<Ͻ_j5Tk*i$w{u 7U;ק]Լc v1g O j2+ݫh`+ ˆç/CZLD| *m p^160"( rsɘ} Uּʓ` ,ǤcVrx jǬSpF~ z.59fJkF\`8G;dQBk1Qe ݧ hnQoKL'!v6\`EI{'ⱗo_0xr#Cs4DЙdㇼpi1ؔUrGؿ)aB RY60(@u0^Ȑq!ZJiq.N MmL4j$'mۖ7g2mrUN[8Mw.} \\捇tTg(i~;?& '<8csL,7F?x{&k194i'o\%{74Zql֦Da;?BVŁV"v2) u$G n{aMӸ^ {Fɋ7/Z΄kI#a+D`Tm5楰7U%p(QT{v딍#h JL *#M \QtS@j_dϊkP$_l߯x&:%Sc7eKc242dq f6TɹqV_g{H<,U^vg-Ta=a'uTϔYP-.OqD*5|2d&7ׁ?7᤟5~yXAab=)2fpTx|2HGRԲuZvMzDm“2Q?Ќ&+W-fk_@ΫYN H=bro5{C?r3P:G%4\w|Vw#hsIydfjܜJ:DCT.U]e^l2kLGv?Cy>[~5K_`Z Aa3ṯJUqNhPE+F_ъ̣ ƞ)*b9zL=Lͩ08jf%oRVQ}S6Ԛ8M1!{شrsGCSB5?>Ó-RU&./fic*bl?AX;4m[бi#ؓhLx5s븘:Й?%⤿}c[2k ٪YU$2m;]VS 78^Q5IR[c?-RJ4ӷpd"e1Y$Akn2!XTUZt1:6RL:mu3iJAn<%m'9n=re83ۂ+ؾHNaS'"!"(IՏͻ@kĄ;{c ChG i:5|I1ea?gZ}EoEK%ql"S((u^4.:'c.qo#0ۧ~zIkUVZ-q)0uG|dn_r(D饮u{s#o*?J6VuQ8c}5Yo>fXB`4ȍvm֧7oYS1ȴGk"Yƾ*RN/-K1Tm:[RttA'.ܜk-PPf AXs6]9C~k(JrET-naAyZ;ϡ&~BH9L VDn%x7]>2pN/?Hx1l[#^˭ #be _!vPsN%Ne[QlA<;ʧf|$ۄD]6.3-XZAPu㋎اPOS"KǦUr"+MN'yJ:[` Rw|9ԇQǐIKl[d+k,;rATBd.zxZof7k\HӠdoN|ii^nДb+Z pEF\"z!%& i?TM*qr(\; @W>Y:jw85Zi yЮȕ 3/ +/(6V*?|nad7!L‰UKst@k.ڎ vKsv0z'0h!3Oܷ5ocBSrШ8%S6ܵyz.U T㾻'Vr!tƐT}FL|_uHiLܡc *WȢ}f\sdWls=aZ?7uT[q%q7&tG* j7,CIx #lWIH[6-f8機'Z]B@OE^9`%c<knR{z#Ʌ֝8"T\QzF@kT2?MeYAӑ>ʋތg"٪*nMdWϴC>Մ "m۹@-O*$l+Xּ^sw` !'P-ѾhKB_B;[qBgvĻK@D4C/Jk |C?*Р҆)[(ge{,Lҏ ^cPNOV+ylVShj8Yz!&zbi: \3/ϲB(|XEl_0Iz/wϜB#9 1y>4wJI NC1?sIy.;i{\1svϳAum㕬r:_NBf"0@T¥n(0GiKfj3ŴM$6»${d-ɮnRLڐ-U1ҟCW(KivK?@gif0&uF7zV Z^ӷ%;!]W[ey BSBBf"oJX.$HKQlUS Q$_$\gZڱ,m^U)3")%{xŴ(0C7 ߴxgא'bc|Y ȫBm*M~~۲F[*JT)LQdWߟ6 7%(l{+/\YA6 ,$~hi G97!ZvepA̰7ĬAҍIu>zH|++!ϫ3Rχj*y,^њZr mg4+JCՈf}a&D4 蟷Ư''Pʅ}Jn0O8tû L"rZ"@e;pc4t1mt՟N+’{٭vu{lGdL@.P@aVz9E Wjfʋ߄VC{C R1fN- w[hYoxgVkjnf*rvےno+ISNErzVfxg̢ ZSΒDDw; f)ߍULyFsz?s6ob'=^FGR2AAFL& tE6y5\jZyXy2m)T.v|\+77S=cs# zv魬 (Y 3(H&^\(z®\Ɛ8v:N 텱ɀ/u}e{a]\#3WD9 ; "삁d bǞ}h诐&Hҩ5m)KNa?KHxZpMjtJ {/c栋3]d,pYnt8 _T4Ejo9۝+){l˞;.n vq{y({թ'^Z(bVЃA1VRSȡ5*ћ8P@|,}R`LDU2WC_Gʞ3ϥ/p~t\wOcl4#k9"@Y:&QxiID=V j`EF^bbNxEk'=n'! Q7)ZE7;u7+{S׭M}t?+&V i$>Q,̝'^.&C2@+j`gT)B+Y ÇT۬ X.7f m뢳S.P!JswO1_IIҊDӠ}πA&B^]yzY|,*7ܩ &HZ-J2iC{mv_bFmmfBFJtp׎2J52,(6>GW!Dρ}iELG!P5i2| 0-=%G'[8;/T"9>@~ѡd)uqe咕~ 0.ī -2$ʛk Uhk:ٲܺt?IMS" [ZBFް]j<\Z85xGB,?j;E!;+mќrJ.bkg @x)czH+ϟ^K:N!KVWA=ڵc1 ;6m|_#Ά?ۑ^.jr?)Gt-!0d9ڠew'w+s F HvDLޓJ 棛݆] Wcġ٧ߏ 齑Yai+c$قWipÝIxf#6$BR` Gs|m, НM~.Ot=CK5v1*^al7\JeN ?[)=ewpp(Yn2>SD%/ـ@(6<;fCx 䋸6^A#>w[GOaJ |6J8Bə.RFϥt,$cL3!Emޓ]G婉܇J6pqԦmmh 9cI^`nDnO4*&\v]nx)?3'b?Od}=ٛqJ]ό)g3h]ܯ3ΌGCnLFԻ\~0=-AQn ay =o;I۞`._X5^dLEUvA2%zQ`Cz ČX $AJ'BmWLOդ9M1=W WJ_n>ٞwp% CU5lc\4 yP&\I7|f/V[T>NF޵'XyH̄YaQ.ExWR'-J=j$eSD-ۥ KSnqLc5jl~QqSW5\+áy7c4POsaH$; )&0ro)oiYCz]%b9~]IrҜ(` zOGy7epL"ۃ Ɲ9[!Wo].8֣!~ןkqXC_72o_eA-A0E]\Tޓ^zSd'n ǐ~a<]! &/V G쩕_Q55/ z>*Ћ!xI|y \F*o}0Op#yyos/7<=%9l/Lj6pV"[BQCq7(*ÔM^ҿ'AHi&O`O-UL' Q߃>v&@T[O:Cfje6W Iq TPak/Mc)>?-U=P0CL0)ڋmpDžs EjOR_[ E)i%ijP s]s Ŕ{.5mCl>V(d2<myFK=TG1~LmPEɝ5{2_cZFm-y!C_^Hx; Ճ#g^7>9pGnӔc0WVݩ6C"} c$%4mxbJ+JY8_D5~Js^j?(#+ֳTLk2 v7r:d Z耋@4x A3J4t`á$y(#HijovHOe ې!D7rl<4 3BShOӖ3q) ^:؜z] %o>xU۩l"kS69;Wjc'lazmHUntդ|ĭovdS~B2-!!&>e qj]>F EгX(͙U9EJ?3%6YGx~F)ݘZ +@5edE$l. }^X?i J=v21_ HS*rތ.d)Ēum;RMڽ"خVYμlQqDq~&e P7ƨ=>64,'?_HK$GXO"<8hqi*l eRIp>R8I?Xר߳oK˟V)PJյN&QƵm0>WELs4i+I)_o&s1R:vp" $q =~9L};ge'W%*ػK2۞ M#R7Nn 7v⊔tTK&`+\JFÁF_j ӠeT2۸Qϻ`xap׃Ĩay;K:s m u/aZX-J2-Gvw0f. uEBOJ\I {}7Ys:Š>X"M==Rf^87nđZ)k_Y-KeO8 4Ďԙ͠o*4W37IH@aԥ;&- O6 g8)֟&p~$v@k | kjPRx/~J꨺w| [jK6q#yL D& 'F @Ԯ/V{)\js `XPӴTbmOJ|˳ڙ W*[}V*[XuWo)vfls`+z, oH&n7J{tvHͅц-2FfEmiNE ާ FGTKr*(1f4qÎڳcOaۢ4}h_S*kS(w_Ki+[JTHdyS01*،ĕ}IKF PV6蒥7"N2>HfC0R P,=x+ )rdT}f疁Ua?o[y3E+bNH-WYGs߃9_\j<0g >w(!R*1%im7V q ubK_8,b܋gw5~Q3x@}.i9b ~Z!=]tt 2X鷉?J '|* {qSdi#@gX+?plņ R .D F>$GD=cY x]FP-1(Vg#dΏ|[]upߡV;.QVtmCc+>:Y:kŽy3 ;1BWJƃ@ijBe{'VgAT0rYUQθb53 MovGxTlb*3$ȺV;)0z u-BeRFQDreIUGXkRaŘnԞH%)G%ty.!(RȚ[[y İ&f' R*mh0B5%!2P @$LAKԻtLntz}z%IĨw@,1u;Pp.!z񞭤DF)ضB_S]Q@\Kh[w^(o$<ƑY%U_G> Z컹W4u|ێK[D$wHcGT!&IwUs!UKy)C47| 5-CA2&zB"pҁD3F pZc7u )LuIhlFbFB7 %|I%ߵR(wؚu ~&>e{C=XIi/%x,S(\n%|-b*2Ի6;v-tx*(n@;z;4?Nh73CۮBVXwg\Ӈ+>yؐwq+_ZӆKx`,9\О{1j=m 1뾍?ν~x*Db {]]j-"Ċp [?_yTDžxF_E[ \7$Þ]x^q| ņ^8y^u9b Ϊj@·0&t_2B`Ed,ljB&ZE{mu~zק!'eY,l@+N( zoK"I0BXl/0N!:,q.|j4Q7 \V&2,JWؿxMiU V ʱ5Xf˴(0d7JP#=|B!sGm @=AFi6Ϛ}hx?;G\b@T&6|6PtPRt=9;5d_ q!Ԓyb+_b,h2W1NE Feyf 4JhP`#qiiJ 1tL)Tuex^ڪ'2x5O$&0×p% ;cuޟzj(jĞH][2vY|r,4d:v%`5,eK .= jT!b4Seq=XM^Hy(EԜBmIY1V _IG>vLp *׌,ꌿ4)o'6ϓRYTv|`/;0D˙v;՘G.JAp`<"UO8,lBmW9E&Ki~ஈq,`ręY1 Hca%Vz߾l=s9Xf6D Htj:+XL2zcTa@Edo|j4,t&/v`7lsuBy,INr=-yrOBj-U୭cTU.1Zi ߞ1"C\IZo{hQx*[5m+u1xUc5 7g]{#X::{ -bplxx='δCUVm O[Jzc:%FK ԇqH/t -D7' 72`u`TBH5YU%J3H8f}j MC2;=#.Lȶn 9O]%$*Af'H _08c苧P͋Pkk5 *^Je{˨+2O~mH 2Tu%D[00͂ySfUq;'vg'2G.(J_׏Yz~. R6r͂5)Yp Xc*}JV*NzrWǟe,߲O;)Nux@e!0'ݓQ |c3ՊduKl iTm~:y 2x*J\n;r;`8Z23'e9GTh6owl'S ”|+Peccmc঑̞96ګ3)$LFk.46}`hztj,W#yYa..X-!&)S=|W@>t4E\ɧs9ruDs^L='1Wڷ)|쇑0yu)~vAΌ ҜkV>ګf ^?9x@,QZԜ/b+EǒS 6bʭQZ?jǣ`X,;g4ߏ˼X8"sZPRXGW@p " dڵ>mv,qMrPXv˘V @p=y:[.^rk#Ƿfd!^&fUIt'VzuO1n0k6ώ{]+j_]@"t =('a`[ɩ ,C9~]+W$ U\k݂ WߘW-D8 5SPu n~vu-I͇Y\iQOcIdà aW[d>0,<Ϛ7JZVy 0:'6,s{qG%LN5qtDT _4%4ɘ7)oqZEV\m\p֕4}.Z`†qҦ@P4+c\>lKc"Ɔ^KD0%hh\ b<`aL ryo"@:Y>xQT҈~qdw \XvC9V%DlgrJy{C!0{F{mUo;1z1w7%v  Aڙnv#}(Jq& )NRJ҃<69}M\j< V(%qDw' ߳F:| 0YF;Bf&Roq[*ED7UZzZ\G탍SLVbA0Rl"TXW$l!'t K,2k)H7tiO|sd~qѲw] %м]Mf* )y51ѕe&xp{3(ih:0m;J,wJ7] CKRQ_S f09=]Ay%oxs:olϛ욋UObR9U708q=Eh_mePb{xr0#ߴeSrZ qpSkFXε2}(ZBLTy)ոQa "g&( }ΈD,O\l}+Գ+gˤ[!q/Ca)y“;}\&./H6j[=P)Ҁ0/>u+]:ߔI[h~)*7JߧE6tAeOt}2BOᲖYu߷_ h @)<_W}/PPjfDGym!UO=dȘ5Xx[;#̉Ӵ> Yv<1]C}< _G_.by}ET,EoRJ<@ئvrHF-= J=Hy$10fZkD)s6EzJ\΀1ǽr΃+!w< .2y wc p@}8r9K%s䔕mC7 teŽ+lGC@>,t4VMV OXϳfo WΠ%ky}ɐ[(pnj ǽ_HnkBn=c칎6 LA/drŞ2,ƃ7c8#L9R@ݔgvk?^'Hٕ0=MFu޽{v? Ta#9GGv.CO ⺭64"V7*vf)ۭ,jܘJ3R VQ*urQaվizRۺ/"Ԛgi`uu2%\a-#׌uUѧNڹ/bgr}**L#_[H^fhNex^s#!QhX󀯣dX {-u4'\Ѵ"9X` ygQNae,F8})bef0pUp#,x=p}1)y=SImkUW0-ٔf|zb=>!yq5&N}+7cAmRhC=c2ȣQDBz+D9/ފneE% T(j:B)&XW+#sr6d. e_TxHW4YB75$dvh,}l1젎@eAJry=CZGN<*_ιtSҦ/~ ;lTE: 5sX鹥Rm!QM/L=Uga\A_s,leEHGxё3I8'h\ڂP3@Q94at[up`V'ȏDj H 3i/&i=R =6.7"xJ9,ewpUk?|IWrDh)qXEISdIU1_.8s]ǾO\ ^' Mlr'AZ_i{jܭa1Z_g3].|m5ܿY'ef^ F'h53cτ4Ϧ-K‚|Qi; `E3;y]yd{5+# ]=^`(hK{qqNъTκӽ>0B6וv.ADT-O׆*Z^T ) ոynܐe-r+5Mā-ǥAMjw9p.δVY%c: ibMW~31U!:tgRصP|| S1`fAE G-5A[.Ё+]:cρOqf]nj1DwE iqRnAT.)(cצ{4Ђ3Hij on=E7+қ"́ERzێ(k?ng앪1S<p5/L<]Y#aNf_Qb"MAJ}5%Pb8$a©[5ZE)/5ۍu#sUpq\ ;שDO[{#u&&z y^|ӻ*= sExX%m OKrw7)2nFAZCҩ=*Qm+ec,ͱMZl.}] k15[hFH#m>*oY>N֑6(>\h%ݩF{4yedðe(:ʶnc,L[R;uWC-LVu6d$p~閿V\5tV-ю@3y%XÓs&K"K07'n&AV~Z̠c߮DmNtUƸS R3tTz}-,Bx,)Fbf4e'wL'Fԛ kq};0CVNz+6F&t@fM+/l(!(joCO㚜.;a9SN r^}C&yuU!*x{=)^a{n*5)e.i`Sp1YoʓlP~ȦˎR0ANs#V #]d/ByuЃ;xaۥ^Ŝej(!|sA3ץZc@$p&etv[؀/V|L"*%#oXG? K؄ֽ1Hmj1A \,bZYYŲvb~&Y;kPުlcZ!nӡԨ%~NQ|gϗm䅃& F|H }͢7BYL$ rd-P{yW ?)&kE]ĘrNV| b{{wM1ERFD8LlP]/g¹L8jR)>Wi'e-SwOGFUРĸ+R!Z 5¶#^աHҴ|X֬_O$oBA4Kb[kQh ɯ1]yqK\uq =-SQ oK%t "uf(˶"qi9ULLa݉LygQH0pg "mxEUKڛtɕ$)#:7mTq8*nj32.b&.]}[OAImc!-2RKŏfAL'VXC^TNq/qKJW|Qk[c;%ݛx4b7Yap[CfO^釁M fr=J-Yx CCm{:č7;JtjrQx+ZNr:QxgI(Ib{Qh.wMv>K{9pK!tYCp`2΅ [ `~ Tiǣ&+_KA< GLYB2;n-3r+BLp쀳qT&6<-BZƐ7(ɬ 2o544`ǎ2z/-]Rɝ0s{O5{S*(y޵Pd3ߟGB#}5#E&FaR.I +3̀ ĭn[ 0JwRWy6b 1<>A5^Ţ +%ʍ8 CIv'"ˬU/௖Y/V9b8`^`j+5"eͧP)n@cA³~S~]&% g (cp}4ܯ|2U]eQ `| :|'AO+Pr Gv?hE>e@scMɎPXX "SwcgOpjٰͩڃ/s{˸}֞Տx[ n/& ^x]3Ҵo>OڡD +&TiRF{K1ptYES{=\;݋i7z?ꡅ 8^4Y(ο5@-](ԓe{k)"-ld<.3sJE?y#W NU8,Q0Usi4+A@PfԵi5G.|kE/zxȖ8k ć0eJ1L3f%;&2v+hjXEm;2+h?mInXӨ;`lKXrtT8H2Xnjk9sCĶc@$C}O#h.^TU4L4E-I92r:4`trԩ܂{3 Z'Ϝ>eezvl y5D[UX$ֶk!#nr 1 5Տ]$y8G$+J@o|65'"J)ǎtܱ:.x =4Cq#/D30'-gZCo>hi6)j-b^`3hyŀli x\KS ]esuy+-vq&  y+`27&ze\׻Wvh4v8aG__CHi`vZN]ӎO#xToek(dЪFTRӌxneh扼=4wWjJ| ّBrP2IFN.i2'*qP<:,&f @]›KcPTR'[=\+:& /A^1YthrpO*ʴ3~:\k^Z{ 9~mxL[rHh~B] x$g b>*-'\-Ⱦ\I9b58sVʼnq@gN>B$D~9 zC rx^_zRC8v̨mYqMJPbS+5Z{lkA|7[Kr $|hOܣeRXwJXs`\FFܼB(yׄqGi`z'=U ac1SD˞]](ܼKwFI(̫8 ,TvvR ?r"NGv3KnK;J\l(hONa:)8>^ m7o&jSOVT#ԍB`s7&=@J)kάy"@=wpp&Z/M:)Ff|9j{lQ7DbLkumju1b" g=%a.<< r .?O g""#f=g."e ޗs*/ǖ57/.]vP:bts#R$U* >ڡi : ]䲜SwI@n,2_çu SKe4rYrgfmN8 Ub:xD|D.m5NhnRgT$tҚ (:ꜜcV?u# {reX"@zRM3<3TC͘/Cr]|ЌIXP&8DΟ3=Jb0O!"ڥ$LJ;q}M9<e5w( ܮH{R,ZN)?,j4nJSzavY5;r|iyR[^`#oyU: zv /,ơ/Ԟ$mˆa0ΔgJ ,՗)[G%eI764wsۉPwlł_-J ] 'qID60JCiSSf>(dQGܖyxlj8C̐A,8@4q2cx?{Z_ ;=9ʒ2I$q𒻮ڊH1 C_q&UuJ!t E!z4 cGvՠT{Tbgfؙ﹎< ^3kS#+jp )2kPy .k6i `?g)~ŀ,ړ猯Iu>s5=*\ڭ. 4#N"G n0zʡy@ EDg~~fYb"NyP56AoD.Y`)@,SED)`S*v1tcm2}KXv,Y]vUWHyX yRFޛTG׶f,`mT[j rve.M$~ڎr`cL)k8C5kJ|Egb9mLp\sƜ# %B\,tfAZɼ9!QG:E1F\i DLژ&b<m˘αVx(U,q|Bɔi\bTVx= ,.+0߀Snx[S .S8>U`['COΔ$@bug_2(TA=@Uxy#/ 2&|H:v:zO/}> ~Cx ,t=&:L.a4ӧ<r >6,}QӕoN\F@vVtih~R  7MFL:㉮QNsڱo!fC,о bk\kHl&wfIdC{Sb0M6jro ƴhI6DzW*3 ]d X0miw*Z15b11 _ ǝLD"oqEY*Q`'\rAVd3k^ Z>K?qJ0z$+ ͎*a˪dх.d-H $jc 8Jk4 '{`nc'mo(2@Ig+of%dȮ 779ЩXyk֦Ie[nE6[mn9_k@ KO?+j x^5r%Wr8a(y r3bcdslsj郫*/j"_j7Cb+AhП&!3> Yl֜5O.YmkJ1 ]'&0{A;y$K&690rź$tA;{?@xehy5KGT/jZ#mZIQ[5y32_.:s)/`iddˈig|A~-KS]JGI'b/w0UcX:#!s &S]"aZ &kCA[F[ yPb3?kuIN|\Gr&؉y_ ߕϡq~+J.4՛4zBBSvK(GH#Z^t[$ =2љ#rg#|i8G;W1ЀOԭ&4wǁ*>_X *f@H?L9žC2%:!lOF:ZGcs!@ ;7D~}j]nC"A3V{i@vc@_tn[]u,T-^L2bVFڱ4?44b,KŎ^ es@5䄻C+$`R #?>+*pۯ pܹ%YvKmc&}&ȡK (H1@p C}96gW  TZi^w|cF翻)#_7]Uv$4Ѽmٮ'z~kGIۖiO R TIxv >=+l\x4D?ҡ U\(I\ou~V 7a}.k~Nt2ږ vNH:!-_ƹJ1S-О ۃ9emϺӰew8C‘ O6*t<hKPoK@N8p]ieCx Fk$Fr|1axM=.H]y<2̶&ޓ3v1J܀B,z,yw#i CvC4~=T ;uFsU!wonE@A]К_E/Ӣfm` 2cSb0ҳ:a xbD0`RJJ~p+y!,>w$+"t:YMwxlq#>-~(6CL~PtO Tjhx |j.ՎM~XX֫v8ב hO{m36[6ekYDMiQ\F%е ughR9cJf紌`oXnv۶m۶m۶m۶m۶mtq=9+dY9X. p"Njm(?HKnXXķ^Yi @d>I!Zn}@pBm݋(k|vE&9`4a!ն^rLUG Lc*ޢՔ ثnm+sߤQBDRqXlyg~&a3_'( r Bx]oz?CCogG=Nc0{ko!nU,PgNvk̠7ԨY^`h8Yp6/RLJ@݊FYhO/(+o `0KϳY_`*4QD ||_XsbSSZ_aPM y+=mG.5V 7FsP:~+ɯ9m"e z@P/rpcWu/c{  gN졨&'rK0P#T ]}ᨵnŠslr,fݟ%ͧIÛfnbݺ:cByx ɑG:pA9C'Pk.k)'0N9)Z ~QUX)Y7je1ȟxMKЍҲmKU.+L`,A 7,bNdSoylbTfz%E_tZhZrٱ7J4͵g^ΘtgcOV~+/'Gvil&Ze≽w:w{'3<[O^/1OO⏐Kxig%tncꘒ:$񈿰Kr BuCCv_p;dą_16=eiaAfm:- ʬֲflOBmNw8l.ƈbPr,=+^A xST%+c~mq_#5 0Bǚ^ttnչ_TH_?oo6V6I|9 =d7 X\E1ghbU#gU\h+ {ݮեF{W>?YW+ξ>(z1Oe>oWfIbD+X;W(Skn$t9_Efo/tίAN.q?*fݰQӐo7N'3bBTe7 WVZ˘T+@44{_0yl;LcQmFe.w6XfMG0!G^nЪw3-)Y,2L!VS^2n/j-%ϭP^=H.A?ؽe仓tWAgL@>]䈆|~+ѪSj Ϥ]ȼ+ͥջ?A؄P0 mFt-XTWPL6"GboM'X> ؞*34k- _خ{.|z2|񥘮/’+ /ZH)B]GlErߣqtZfHէwZ1 @_X< Cx=%tzS+!\n%&CdMy6uRHoWc[[ -,7^8QdyZHp@ﰩF ?{|y<ґS{o9Rk 4E Wb*} v{;2`nSֶsBad-\ U폸4 \L46=|*<ܬҗ=m .EHUY+*|D>Y$:]٠ùK3N{Q`oZ ~-AZqP uxw0+)K>K/R0ٷW`p'0~y]5QƋWȒGc׃Ϳqfj P6ٗBkI6iЗ<`iJ%U>"y0&4e~G@RB".3:0j7mPr& 3ϪL ft|~*Ԃr8i#){#΢,cZGa  6f^ʿӗ+N6(G k2S<ӓHTbߚɺ 1P[#?rJ6&aV*>:# 9 e˦o *Agzo*S YfX ^e费~;\7Ku@BiA͊KG4PO!2t)uu4obs](1*}'(#٫nnd&HsʧX ޻ЂfFg _+ɐ8oKQdrLR5U*O%\@r(G~US~IɖKݚOr8[YA4WZ_h, s4DÎBub1APwsLzsC?gGS"x]d-ʂ1Yl2ֺb2ad2k9IC0QgKT3ߓߠ8} _0m>/i֠L9{RЕp}L8~n.22f y[~o T9!+٪͔dw>NΪZA&7;$72c9ʹ\ȯW"C} %!^P,IϠxN(413poHy#̮@k~-pe3?scsWe$T;Þc+F`XDOִDM1H kzRb"JG/6AT'z=䃃>_,mnuS61%TUWhWCƑͳOiɈ>pnue?J[}^{/dwO?w_V;;sIg) / j0>ڱ-L;hA@iھ*`]1D"1bzBZ7{(>9]Ky 0+/!ZA փш& Bnn{p !0f\nO둰QSJfI Y-{=UUG!uQE-Yu Y,f yA|?!8" 9E/g-&o;J&g|79"R`i]K[c~r9,1 溦ߡ ѡ5׸~4cm{N!D!(/D . $Z#MKӑ#/'C.JI=+?8OdF*YggϤȯFm-bj=mҢ=ApE+5QT KeIT'0mk :u苡ds6 #ُ}}ـ,Ry?l6^Cu"4srak< n.6G:dݱaL x S>,% :ԓ+EcDOryZĨdT;2לb{2.O"a'4=x?5{ K~T*Rc?|,sB MH߆g \R/q"?Uef(6qE8xCMwќRK0)X,#7r [̸t+ `E%|BmCZtxMЙ ꌍzE}+n*@Jw,HYw/" PgFc}.d==#}P߲e[+,%h 9} Nk|O-ijx3H=h$tn'ax>k 4Cc5\< a]:5r:*2^^T cG%DZtreԟn0yG݆jU*|~Ff8rpq:^.R)z5xh<Uuc/Idzrrd6[bxWEWsYklPań9p e?aN!Wd6SNW&}AQ7Y+Rfrv- lp0}6}idԝ zZ$;U4r9| 0~qgn?=\Hh BS0V6a *R2;iY ݧ˹Y)N ;^2Y .;CT!l}Ž+>sJᓡ2ANKj:"%%:/k1_g ŗ Cc9*"8&0;n8W+bkkȋAzϊ-xx8!Tpa8pnN= >i1 Gj唓J#38 ҈QNzqcb+rb sHя3vwSll GM@bգni "3v('.b@Y  ټu]̎dlqcA Kw-Otm \iVgz=i=]K>{MQ* ' Er&1R^溧f0.rf~D)5Bd >^U c spi?$DGζ`u 9 #^L. w=+,Q?6Ϛ 7S5$o6v<qZpG  9ͣWC=E KX~xSug]%vZnz_/2߷&)bi= RF)qk`& Ht zKiy|#C s|;0G[= !?S,U F\ىa>oZ_;nM֭'ENԴ:;hm}^W wtL7w y~7ӣLM85js78.%Hlܡ#)7]@ D7tan8B@{/okh 72=3ȷSgWʶN!SA[`Tf>li9 36&/ $bz<AFs@5PmH?x=gyDټ7wY Y#MQ L Dq4>J}mW;{nX40苙QqK9 )}1'jrxT ~\d:J3xaEA/q/ [N]Er.S>Z@ t7W]G9o4:;VDNtg: ]x\dH1뜪mE9.W3^".Z#Dj-zOIL|xFJ2ȲCSIpb^0x#nnR%@ˣ玘+傊 9쀈lɭf'6j)*4V9<"3b˜k#]o*>LoEGdtsVwTOòQx3kFinjRp}Ke4Wk({&~8 H(|0 9K;E0 2Vh'=u[^h!ޏ'odz33(*Phw[q냳嶏ojW4ft(g2qVg[ MgZثHєt!C޿GRonݬKά4)vxl 7^3ܪE`wcFci:]V˭ԙNOD2r%t˱BymW{< 4i(>*W}ŵgRRb3 1"S:P&oqaC"qpseq՟=t_؃t-nAXCi(f:UϔAXҙRE<eQ0`6!t.[$>_܅&I##2;7>p0OiuReFIq($r!A5K3\D{Kr0F8:X^Td>:n7nx ͷS'9TVӲ~Ew6 61l/h᩺?y:iWx$.Z sفՊI?ne.eæUxZ)X0aYXTt>h!2 dicוXM, *XٳbKbɘpK^ˬTzVJ86ha^Cf3=fRٺ.^s;Qe`Vizc(q:J Z$.X9{ºp6Fu9~{wgxz/i 1o#8$U8T;V -9ӌתv+@3=Za|Si^N[^ .WqT QZ+oa)\v5*yoR&RR;(C +k.\|0Dƛ-[I~"4φjܷӘg ]o3)H~R=v,ҜVprȪEM;K{g`U"bwER8tW:elBQMPn.9|\&AYӽ G?L|8ŤJO+Xo0GU5t}it#$uL{vߺ0/.t`gQ )'M~0Hd=ǨV]y3aW6sl[)3p&cPz#Vv5޳Fy yö! iQȚv|'Rvr|-M(@Ewb|8 ߇A{ 8G> j#e+MP$h>N+G xf6E:%gɿ9*1+)ulƶrWʯZ%۳RۢVW,Y'0^m^KlYNM:ʾ+nHPbA,64}Ow2ڛc[h#6d?v`0Tх&!`"w951S:XmYQ;LeLBExvy1 ~Aˏ+~Bk|{+߀γ L%dPJ1y3\;xjC<Ͷ_AЄDU,DO3oOȃ#~]AL%b̓|M M]V(~~eUGP//$rRU 2 11%6x2}Nm/xgu8|%)ϼ04?2ŰҚ//K<{σђHDrr'rNohWT/uz%A?RFH墅kgmYב7sxbKl* o6)N f3.P. X_ $楞QrʲLHio{6ᵓm9R n C [<;OM-!`2nQ*sv}LP6L#?2Y.g /Z!".;Lcy, >/Q} P 18zQ @TZCPE /yer*) XS.U\DoK) %@q>^Ȩ4k>˹FCkq5< #6@Ս9`QQߖb4CoqW,WElFVoӹ3<VOcC^٠H$X=2vfh,l,t$ѳEsj6:~fÞl)J; 'P%]#Cɜ 7QnL /9C۬AYS,#ƆY0xTO(a\> #Td2@Ps55v%9Bj_2w^탛#R˕ㅑh2km19KwP"Llb(G vVZd{Ywxm/27] ,[Sxd*^-}%55N".1o~1@?fFbK@-8ױFL[[ݖDy J$f NpڐYLZrsҝڨjz @;ɑZL:U219;^04͍q^G4ZQd/Hqs2No+1JX/d@dWޱ(ÿų/{40oJY6Y gOё-cT!=Y*P: GoC}dUVZ~ŻTff5@ʲXY44@"B CtPF65w72$HWAxkfPѠ"X#f *>W&;7^A3RH "[FN J}ޱ i<`_V( BfWYaLf^c :nr-g8gv@~WӦ| oi60;WA*+NoW]y,8}t>2VznVS<9恍~GQHCpYjWUD6w}+NÙrIJ#v*;/2klًv=2$5]^ cc =]i? fe*L,*_##nsr.G?L\T!'{7)3d «'@ >+Oz^Yun>Ŀ" Wlsk}$s VINX \șON̮[^d'\g!! B8cmDUAQ?7<Dp6N6D:ۥ rrlrMTܴ@}A$O]x:99c|dHb h5BdQV]8F+eZ7c9=?@QWש3}F D,PPjՙ\/G{gT9'|a!ECxdp+('_dTd8Sࡴd(. 8X6lWXuWEs6:lP>U8~bL ><5߂̛#q\@%NDZ~h<Ė D!B LnTMK@HeXTJu0uemlVNlݮ7z':n e#0˄ɒf"lW~ x|XWT~ɂJ.6?gڛ=Y)B:_tоaЏ1kQo]}?ž&q.ϼ!) .y^=yqIljxb_7 G@ X3Þ׹@ RHE48qZ}-}:I5&wbϏO$3iL?O'FYh, xf^S}#-<xɠsv}>lxJ9,\ICJ[ۓEbz!L33"?2 ,}#{7MY l(=7 `JS6<>'rc/xͿAE5-w5ax A}Z@(iiH2 g/dBp߬,u%%2ܺsƼ; 2_n^$'_ah)gW*i~e:3 Y6hn& nC\w(NS}B/`rIs8⋴rD:[b.e*Ռ>}1pfŭ1thT#ۗZ,l;D>uhV@+=Qx~꯺<{<4uK>7{aU}AM`ÂWy `pxțsWFYvrkj@YV[b'OƄ}f HXvWEPŪtmg65N,l  V(avR@}WJЗp{rg`| {-T0{l^>\W#ߦX𳍿^)n cmԦLg{aFo_y;ܤ! >,O(!-ҽR) 4Ў{g yŬڵA[+`9RP_g"Snס7#9d!PmLCs\l{ <@^.MW/yHBQ= &C]7C!@;@dtӐկ:2OŠAx5!֍!+rX6gve6P[ acCgtȈϒ.e=Oמ#xN Asf3Dd0G^1-D²x;xҚ!Q3, X΢;pTA ;u;g[&SIHĩ]3Möj 6wQ^x%W5 H-xǼ ,*t&!/\ZL/6/sJ#~ֵ < G]22dEzȮ҄Dmx`D3.%FSg'I۶uFg#cM~?{ m[r`"x4V9d-KHWy!,U0F5J&rt~N I.b7X'Mjޞ l}aLM_W^"!*V[ǻ)4BX=UFq`UH]fq 7H&ɑ6A^@Xng54We)޷Q V3l'@HJ#;w- y)6M1EUt.}wCFˊh;ہ=IƄWSI0=:Й ōf4HtaB;d݌8D@Š,4' rX`pC-5f8WqP@ṙuSߙn0Ec>I%ŔzZ}EoEK%ql"S((u^4.:'c.qo#0ۧ~zIkUVZ-q)0uG|dn_r(D饮u{s#o*?J6VuQ8c}5Yo>fXB`4ȍvm֧7oYS1ȴGk"Yƾ*RN/-K1Tm:[RttA'.ܜk-PPf AXs6]9C~k(JrET-naAyZ;ϡ&~BH9L VDn%x7]>2pN/?Hx1l[#^˭ #be _!vPsN%Ne[QlA<;ʧf|$ۄD]6.3-XZAPu㋎اPOS"KǦUr"+MN'yJ:[` Rw|9ԇQǐIKl[d+k,;rATBd.zxZof7k\HӠdoN|ii^nДb+Z pEF\"z!%& i?TM*qr(\; @W>Y:jw85Zi yЮȕ 3/ +/(6V*?|nad7!L‰UKst@k.ڎ vKsv0z'0h!3Oܷ5ocBSrШ8%S6ܵyz.U T㾻'Vr!tƐT}FL|_uHiLܡc *WȢ}f\sdWls=aZ?7uT[q%q7&tG* j7,CIx #lWIH[6-f8機'Z]B@OE^9`%c<knR{z#Ʌ֝8"T\QzF@kT2?MeYAӑ>ʋތg"٪*nMdWϴC>Մ "m۹@-O*$l+Xּ^sw` !'P-ѾhKB_B;[qBgvĻK@D4C/Jk |C?*Р҆)[(ge{,Lҏ ^cPNOV+ylVShj8Yz!&zbi: \3/ϲB(|XEl_0Iz/wϜB#9 1y>4wJI NC1?sIy.;i{\1svϳAum㕬r:_NBf"0@T¥n(0GiKfj3ŴM$6»${d-ɮnRLڐ-U1ҟCW(KivK?@gif0&uF7zV Z^ӷ%;!]W[ey BSBBf"oJX.$HKQlUS Q$_$\gZڱ,m^U)3")%{xŴ(0C7 ߴxgא'bc|Y ȫBm*M~~۲F[*JT)LQdWߟ6 7%(l{+/\YA6 ,$~hi G97!ZvepA̰7ĬAҍIu>zH|++!ϫ3Rχj*y,^њZr mg4+JCՈf}a&D4 蟷Ư''Pʅ}Jn0O8tû L"rZ"@e;pc4t1mt՟N+’{٭vu{lGdL@.P@aVz9E Wjfʋ߄VC{C R1fN- w[hYoxgVkjnf*rvےno+ISNErzVfxg̢ ZSΒDDw; f)ߍULyFsz?s6ob'=^FGR2AAFL& tE6y5\jZyXy2m)T.v|\+77S=cs# zv魬 (Y 3(H&^\(z®\Ɛ8v:N 텱ɀ/u}e{a]\#3WD9 ; "삁d bǞ}h诐&Hҩ5m)KNa?KHxZpMjtJ {/c栋3]d,pYnt8 _T4Ejo9۝+){l˞;.n vq{y({թ'^Z(bVЃA1VRSȡ5*ћ8P@|,}R`LDU2WC_Gʞ3ϥ/p~t\wOcl4#k9"@Y:&QxiID=V j`EF^bbNxEk'=n'! Q7)ZE7;u7+{S׭M}t?+&V i$>Q,̝'^.&C2@+j`gT)B+Y ÇT۬ X.7f m뢳S.P!JswO1_IIҊDӠ}πA&B^]yzY|,*7ܩ &HZ-J2iC{mv_bFmmfBFJtp׎2J52,(6>GW!Dρ}iELG!P5i2| 0-=%G'[8;/T"9>@~ѡd)uqe咕~ 0.ī -2$ʛk Uhk:ٲܺt?IMS" [ZBFް]j<\Z85xGB,?j;E!;+mќrJ.bkg @x)czH+ϟ^K:N!KVWA=ڵc1 ;6m|_#Ά?ۑ^.jr?)Gt-!0d9ڠew'w+s F HvDLޓJ 棛݆] Wcġ٧ߏ 齑Yai+c$قWipÝIxf#6$BR` Gs|m, НM~.Ot=CK5v1*^al7\JeN ?[)=ewpp(Yn2>SD%/ـ@(6<;fCx 䋸6^A#>w[GOaJ |6J8Bə.RFϥt,$cL3!Emޓ]G婉܇J6pqԦmmh 9cI^`nDnO4*&\v]nx)?3'b?Od}=ٛqJ]ό)g3h]ܯ3ΌGCnLFԻ\~0=-AQn ay =o;I۞`._X5^dLEUvA2%zQ`Cz ČX $AJ'BmWLOդ9M1=W WJ_n>ٞ16Y+K* k`hl\Ln:oVcn_ܷ1}퍼k_O B¢]̋$񮖥bOZ{Iʔ [KřkԖ^!T⦮jV{Cn1Pi֡HTw@ SLDa q#SޖӲ)@JJVs,99Q2-B+i@.*o"D!dG;sŷJB& \ۻ$;\qGC,@iaR?.㰀En$d޾0`3Z1O`Z1#ĉ1:T.Tg'N@@! =xTBfM^d/$S+|]j(j'(_|TTw-GC AƹtU `PDF #._nxzJ}s_֙l Et9`م Uk yeN_Qո3.c="߂+@OkXOvFs VD %{v2#b 46#j, sĿ7%D^n~نXmbrjn&+7(wʆ $gS룞+I.{#hĐ/juBM:?:/*-!3#WD%j((~U#  4OԗNGa )YM׫_a ^8<)F(ёfQT'9"aH k_R|Zz0 `5`DR< 2"j%§ZOղ)+AR JҊ՘ƹA$)5$Q\j:R|Pdx. K xˎlMbn( *|Z{.©j.c"lA۠z;kÝ!/dVp"IZBĽsHw!@Gn|sP>ܦ)ma6a1Sm(˗1D89H'KxiĚו8Wpj/.20~ 9QGJWgݩУ5d[}"+otp1pi3f"h6CI%:z9#0@:K |쫶SDx!pHz)P64 1r@ l) DEa렟2qzhos3A@*4Q|Bצmrv$< bYO`ٸ8< 0`I ɽ[߈,(Յd[BCLd }V;D@|z!@NgɱP|73!r$~gXK~m2.0 B]S>1V ZkʂH\"~b9.X=.zv3dbjj'Ubշ]ī(SX%ov{CE%]]!T}۝y5cyEZ)NLhAnQ{:|lhYrO~éID\ypZ{'8T~v;?|t5pXQ˕g%?R(4 UkL-k`|CK|KT-w% e=HaaGj}}o,o)#1S96%訖LVZ/Fy&'ܛ**Ar8veqw0/)%Qv:5u t0 !NA^ ´`?Z4dv[ď^]a*=U]W%LA ĕē^o67:u0A|: Dz{@ qt bS-uЏ@Ө ;jB[0={m XTr8'o㇉m*9!\\f ط|sœ%ܡHϖm귱sVިZ18f )hz 7(r-㐳$ &z2FHr'l{ %qaHX,Geih3U:w5.AٶĠ[Y;?fmwo|rXrGXيR ~ `뜪gUԦ ;2{y({])Ū W|XY6SFRQa.gUE9,4yOQ}np̐"Z%xEӻ EOK1E9mN'VceKc qS{nKt#!{f(>҉zY󻔇K!ko RRl&c`'Hͪ=fm`Ԕ܆B-s2z S/ESB3];&BC-Iw{2UC^WM#*9oEϷHp_ȸ(A\Z l%6֤逸QdWP7DjsWBZmVeCw[nd6\1w` ys4,'MQС>gdWenk7@5xŬ6U? ~sv/zb_Nv juTТ}a b7,\jZx{7\_hܩ7~3/j>bU΅!dQBY:l|&mk=!8UWMχU@kV՘ goE|lg,ܸMpH!X'38H*ZHZ ǡŅ6MR3_$PueqdpΎ״ 3Er/e1p=XDޮ)`۶w JJZGs?ݺ@~# 5̺. ?w hdRĝUgͽv\ڂ&"C; 1ID 9ZMKolدno 1s$Qs$j1bRc5.NydZ O@cd7P 4Lȗ(1CN-'EdӄW39-SJJ{-cBGr.Q/ְ|l{VߘV|)޵#߈nxã'TAAnvp!$wEøvJ ǺG8>$_\`EĆ콳["w56İxȏh_R#d٬M=iGދA(Vci'PWmYmwq 'vsW!t_[Tk&VK,^ز":.s5R.Eh5lL梸!Iś+}]M,V e zs vʘTzh 8/ZgdXFtDQHP[Iªd~9vҼ ahsSٌ09(dxJl?2aAT:=kHX0foVŚ6]E!Q﹐7烧;jSa3,MbZpYh: QcX3&^}z 2J[W  C)84Zk%j2 !'p"bT)hߩ'$b$`[,c&Fv*Z0,#$3{MXQB*K6OSjtPDdJ{(V8Y@A}<-y"5Q k/YH}$SkDQ$D7ߒDh[I#CbcVzYL ,8[kIc1Ɣ~Ϫ uM\*ѐًܩ?84{J σP*Ϣszg6Wz!dwB!G*XX@ӛ \<@b?m SWj.E>4/B͓J‘B\sD;shbnK65m1 r7rU^jMw;Hf!ձ,AkeI.[Xn_uVqN/jӾDC)lKΊjzM8۷X?Fg2[SfdAWgIy;y*EH5c?s}I)` ޽0&oA7$'Z$۩<tW2(o5Z|-h,afj .bm/6\LwEcS'Wd9pF ð-9eUd &Ob6C'd(@#WYb( *'~Saٸm;W4y47a;xe_ljukeLp6ɽl{~Rlom-*rHM,X$!t0HLzݛ7E+SqߪiX[d9c&?߇Ƃ6 S+Nh0gcǓ9q⮲R]m[8}jRi-!\sY@QsNfvǦ~2꛼/L_0mq<f>nI)j3*=/ʩB"H 19hTB og&^J6r5E"Jn25Cp1^MIS9|Z8#WH4OL+C}"шSx}<~z/WZWn(ߨ)ͩlLfz \O nPݎ"KMI@NY/R!R~[t,>l`#:-^5v< FsfK ̋Ռ3/2wE *%|t o נ*pA6KM]kS ݶngx R$ 5ei` #>|BqQ(?Ra|k(lFEkRKaVNH~oܫZG{dv,@C@~6nWuPV5A:-UN: $8MЃ_rvXiclb9\P# ߡe%t|km)lpg4t,랑R[H(':v (Px_[lt3HM޹%ۂ&hBnDD&/%v*U Gxf$x+##J^}٥d*oG3.hs;fDF2[b&Y6`-Hᨒ*Jv| ߨFtO lMb6=uQ[z0jO7Xs^cw&bH9v 9yF^hK*uq"Cr13wgݣ LۘarOa)p]csnkpy+gGu'#bg2}([aӝC˚p WW9Bl+ڿ1j/&\p/mߚ5-LNHOp>وbַ5r!IW<wb#önN6xhtDe^_:݌36g̞=T,f 8h<{+`ǑL@q% >BENak] Xe-pMdHϚ ֳIlA~o|:cVHg O~Zސ@3>FU-WȌfaBG2+dw<9/<Jy:,o? 8lM@V3]'iK擜+2׵~EPŵ-HyKȠ{yuBLP=% UbW8aWВ|qKmuT1VJ~8<N?)Pp.ɀvpUIS ysu.o5O '8] kzb2Gwp_T#/HJDMPϫE#_B ;رx.2LϽ^gu?] :lu%!g]I;&l}.m/ YE23 \̶N>&alչ]HO ZVl.&;4 Ä T6e3|>C*WE9_/HLv`˅oh8cUA!vVM!!䟷7; gןQN^'p7<~Sb Z An 4vhwo=BwY/gKr$$=ae}LƳkҹ_r^ nGtwʺPX=kqp nNl nj"%{RDtSšN7(̬q>8me!/V//MUL*pEz" Kװ"SFp QLaފh t Nց_K;wG-qG5P [դoҐ|I+Y]YoR>?➆Ӷrt#ߥ0$5`#y%NwjY֏;~-CP)(yή8AX_$V9L.Yu65OX>u,DR`*rfB/-KņGhBI=;`rL"MR8ۮ. m ^lmFk]]|;Zes9nOF>Ul {P 0œ8M@e35>4Ӟ~a-ۗ^KeؙȂ\(e*3mi*$Ai۲ ݓA1ݳGX:MrL #k-LmjF2lSz^G hs+<yw"Pmp8* ǘO#=s{T8GNYf9tRMZQ]ܻϦ|44 =BIcUdču*`hM[w_)G?]훦>-;"bJyfXW'SYܿ(2b y͈I^')_U}ۿA-}&145M {X8lTYP07 { 5:O&J%ݰw"\Gs2M*bgxy=V|n^-S؞aЗY["Vk1W G=rף>k1Y?֜q&?Yu㋿Oo]ߒMiL'P@l@IZc0ʸbx3T&Af;߃8v <ELdn+g˿B*јȈ`VQXaH"#buN2b =j)AmMV8 !P6 iNytE# ` |ZC@iƂ< T$Z/3d\ίUq4m߈m0ͣrK7^1)m{)\VKQPS <׉El^0yX[!ܶÈQeAq|ƥT58VVxqD 1{I:S[|%-5C0HWUW?8kO w`j|HA&{Pz0Ӛ"ib#..>]гiSr!z#wl~Zv[×D[|q%@UX,4N [$iZS` ‰39!)~DҺ~@!wء݊y{^,Alu|6ӕ\%{RabtV84n#sVrx`+x\E'=v7:-k?W,G㛪x?rGեrqC$$t S4S~וGw}_2 'DA;# ac]igdaMDtm~LEr2.\{1\g [;"2YӴLq\tPݤqC8bL;Mo՛U<,t7S,~QCGz(] G[q˷} 2f!X rR߻B Bߥ<&Hm6yS[AN}I^'UQxTOꒂ2}m7YK-8ш$֏SXt"Y(B^$7@/Bvv^3,!_tyOyم54o(!ԇkyH+QS2pѿ (f(C-UcAlUT|PEplj}kYzN9 `2Mo-5xgt4m]aFT=7#UB>.X\~ݸ!^@myJdyױ:^gb^WOͷ;kғ:WUayQҦ)X̾$GywB,֊nnՊ>$(^ /Jгe׶R6T_wٵS>i=2!k܆pC8niZҝhao}aNCIWϫZ[m@&@> [O"-l[ 1%zᰣZ灋~5”k,Q'`CJvNnkENa{QKg:s7\5<ڞ9Q =jTo.sSzja姕 =O*?Q]]6؍D__m; !5CxLw0{"izL8R~bT!6aFo^vr:xbMP'g?dE}Ricd_OzNaNѴ†(0tT?ibp,x=`.gYW8$jW7Qדre_oRû_67s<ϖ 凉lzI!*k oo){;t1%t7ʰuX"ܑe :_~=x6C^)\qJ КQjӾ m}  h h?pjQ*+(`&J,A&8Y~t`EbYc,BJWPNNJ Vhk{Oγި@͟V>-=]z { װeer[ׄvV[z]O KX  8YH 0DD8dsU2jnCuT~!FhnNYr\k8ږ̙pK_w~;Ӄ}sblS2GY:9r*[F;9w$m>}_3v$T:YzE-ґM-I ׃ 7בBNҟ/Y ZKVyVٕQ؍z6BYXC4lCpxE4k嚳\}FǯL;AÑߪeJ=`$@((VҩC{/9sBøSKnEw fWti)*^]R}'|&#_w'wOTW&H?Rmyrh.ETյ;Ϋ%}j¡91a>w^!/CHYHQА L8*8 yL0P:4caE)uӨaǬ7S,^{"NoL  &3 wٺw]mCC9IcLZ`hEӲ0B1a#?<" ;xk? ўB2?j樍_g,\&ѰÆn<<~~{%p4NҸ,ӑV&ܞy |bSo K$X7cӨ/f~$+;O#:o!b vA/۶m۶m۶m۶ml۫>;}W27OjTjfTacDڸLuET(ozp/l zjY QM%oN0hFTk,( $nID[[s ur6=a Ϻ70I֡ _ <&hE,?B/]U:KX6~S:kgv[ r,A+mp:ڻd/٩:rCppD{\C蕯Y5zFW4K)dRj/v1'#%~Mc8ظSɊTs`\i;ơh[ȔIޚ [8X)V 2bV 5qև*-7D̹Eo #8HըwP*7D#^ؖw«:Iv˚ ^(ȐfX6tk-JQ55Fټ+/"]64 n8n6ej:c7?mu)U E{V5P$98-W2;3,*]b LA j@RyIQ]{#?%@yD-_*GqXT呺obI;6=Im,o"EFpi5)o:~Jkck]ݗ).%['o̐ <%Ilw? sN2gio;n7߷.Kt S =Xƹ]UVSaavZ!Lٯ*xԄz}t='i1 VȵwPZfsݭewF.yvtVp6NS?*$җgE{Yr%U>!S^QF]Q\ Z8RBtwfoxJSXu>׻VtLrTSHWh\f(Bܤ(̙Qt;_=I!pe0 p F .]_*&C ?{aA8f‡#۫XT|`CpH1D3yBz39JBr|0%? 56?R,g, UmV_C"Sh,Hxv>roʯ$|\pWG>T*ˮ2J(0>Z>\'ч (d#uQ "VX21ǦdG(y,)x;1۳'l 8TlXAۗ9Ahe\>[c^kHW-RTx̊b/ R`fh%֔cGf:Xhdm<_tΡ㸈~ח{B iv]G oDLm7e4]drb\1C/sm0qb@y~fjIȑn$ޔ[~ΐnfon3WRhMa. wej $ˡoy_w݄H!9 T( ezvs#'4 8(p OWk3K.| ͥL1V])-rIH.UF /,Jj 498`w]e?v5/- ӽ^[6k&-]F9yߊxֈ f$T4pu?.<3I1D jJޓd_$Dfu{9TuUP8ZE3'CPNC"ל!9YB/=!{}LV;fTL 8_FZX=}65 >t̛ݥ9SL>Etx4'2])X;%9^0.#kNV]n^!YUSINk=( "u1v610Jzn9H'3T}IN3X2mn˂9c˚.;t1j9HZw*_OhOpЊ4R]®Ձ rYN۩ٻ$M 7^寃S̺c% Ѳt|J\P҃[XU`36xI*1h$,(u"ϙ%?h1aRߧpR@&ǝ&`x;nW$=)  ?w^Bu%)Mxs9>4dy?Y^sA/|`ʼHyb*D&*"WT˦̖}v$ Wlj {߲8/Ţϵw}`>po4^A*ӷfMmB4Qþg0VY~Wr:~y #kOZDD x '>M .yvnL<[2$9lzG5-7*H6h3 wP |96.d:Db{"錟p_E=WԵND ${]nZl}'$Ug܊( e߭_E.d1}γEX OC*6|*R=_vu|xPRjODڶJNehe0@gc3P% LKv#`Ѥ y@;ιT;zX bԃ.8äSo"!f4ߩ)3a2Ԩ?ϣ\n˼f<65!f xzt81[ueI$JLd8x]emEȡ/8A*݃D%ou Xa͢=SYͱ#u;덌jP=c*133Le\Gu}Xɑ|5T^8͈ H5o5msY4KwBbQsWκ\JGsc.ڎpVHd'##N0-Uo1 .znIWqp=h Q{icFV&1tby7|\oLdxwPBiBw=r0m)װxR㿫tNn7=ѼI`LA"_3?\CxNF<7",0??ݢq"iˉ) h_H ƾoc%O ;N.+<<@#dMik[3sM Y6Il-v5c9]2yG&F{?mG@915W!֚5Ң3TD6&w`CxxIcYC!.: ^d^Ũx# Z"&mmLIzQeL___K~mw<ǪV8> AN!dJ4.1_ +D}o)7K⩿TX)`XGc0-瓡'rgj~ 1ZȺ/|F**$Yy}>wXON|?!Fh:ÞS]Vjt KR0FJK Q 7H l# ;Edt44ny_?R)uyF~#h&hD(SRط@^ӐMDh_5.5$G]j; $2㡉) 1&p{CJ7YRE@cZm[XҤvA"GWIahg.WsDrukiw*Z15b11 _ ǝLD"oqEY*Q`'\rAVd3k^ Z>K?qJ0z$+ ͎*a˪dх.d-H $jc 8Jk4 '{`nc'mo(2@Ig+of%dȮ 779ЩXyk֦Ie[nE6[mn9_k@ KO?+j x^5r%Wr8a(y r3bcdslsj郫*/j"_j7Cb+AhП&!3> Yl֜5O.YmkJ1 ]'&0{A;y$K&690rź$tA;{?@xehy5KGT/jZ#mZIQ[5y32_.:s)/`iddˈig|A~-KS]JGI'b/w0UcX:#!s &S]"aZ &kCA[F[ yPb3?kuIN|\Gr&؉y_ ߕϡq~+J.4՛4zBBSvK(GH#Z^t[$ =2љ#rg#|i8G;W1ЀOԭ&4wǁ*>_X *f@H?L9žC2%:!lOF:ZGcs!@ ;7D~}j]nC"A3V{i@vc@_tn[]u,T-^L2bVFڱ4?44b,KŎ^ es@5䄻C+$`R #?>+*pۯ pܹ%YvKmc&}&ȡK (H1@p C}96gW  TZi^w|cF翻)#_7]Uv$4Ѽmٮ'z~kGIۖiO R TIxv >=+l\x4D?ҡ U\(I\ou~V 7a}.k~Nt2ږ vNH:!-_ƹJ1S-О ۃ9emϺӰew8C‘ O6*t<hKPoK@N8p]ieCx Fk$Fr|1axM=.H]y<2̶&ޓ3v1J܀B,z,yw#i CvC4~=T ;uFsU!wonE@A]К_E/Ӣfm` 2cSb0ҳ:a xbD0`RJJ~p+y!,>w$+"t:YMwxlq#>-~(6CL~PtO Tjhx |j.ՎM~XX֫v8ב hO{m36[6ekYDMiQ\F%е ughR9cJf紌`o墐 W}ѝ+L>fVRc.4mvE>E|땅;O'I0R;@˭o[CUN{eϮܤ 2&UR8<:,kT.XP3iLW[}A{ m`e4< [(H*5o}c$l櫝TAAZMG0rh-g?)rFxv;u~ Ɏ`X׿U5˫ Ǒ2 EI [3h" yee\ Fy1 L&< kNLbjJ+,YBA3obzUȥfު=hyn "WǏb;5Y쓡@/SS*yENtue4zloAZ\ a#: 1V)=DNUbI]Y4jv=ᠫ/>MCXwynߝMLS4>EK /jY_j~:8B=0江TB9Zi^Zm{Ew?[cE]׉}*m؀2/M j!tB/q苎Y w^qC.;P;/x&y̻+N}lI5 /q%Ey0H.\]?0^D ]<.]gtOrfwk%Pr//|,4PSRsԃd5vxINAa|~ ngzT#',-,ȬxCeB_QZV¬ⳞCm]Ma Q *\g0 `+<!pdqeЯM==n wqC!FXsՋ :w˕ G|W>c /ǐ|gc&By8Krw0fVн`y䬊+mesuzԣ"}۵z/յ}7'jEE/i`,ILhu}G zj|.Gݵ _57?I1>ng[ U6jm2dFLѽJkJz} 7/Czo`N˃xhIن knE ? ;@g$Z2|Gr<+N+fȘg)OShRVcrB;uuJ0k Z(C zY"ߞ`[gtggh^%,l|Py@Kh6oZϥ؄{J&(AԈ" , <, &S@ΕF6=fݞ)6P÷5m9C;ϲMx=9<44?X,#MB9IH5Аm,4 l:4n{}n(TXk=h!y-֛HK)j4,$tb7|ͫO=t g_wGje<#Uڔ_z| G@ E$ ~"[(**-1VnR& f QFM'=GKgT)VrTzTy k]7e22)~[E8J,z^2z9#l?%a;4?0a?$VE[?c5{,hVLVDglu#XVW26PB:%̖UQRgPW)`1]Osok'o\rg{K94Y~adcR^8򂂑6T[hz#h|m}l:k=KqvZϫk(XBn"~=oFͦsN3'A -RM=ǀ O>ddQ]"YcUMۥh*<r1sEӓ/'D 6q8q_`uӉ@={/JM5o`>T H R".j8a/]b8EB{gE*~0֣4a/t_b?xY5c̝zpCY77lYMj RPrz#6Ɩ"ml;U_$&DLs]HCJHeQ_F>`4@ J`\!yYuÌ3aOZ|X"m$0uo5|ӃYԓe [( ܔ^NVPKsZb@X5ۖO0r{Hӝ52(ED Z?EBxv/_Vz#O˵W )` BI$no1od]PĘV-ב9%[0+ EJz㜅eS^7rKE3V}xm=7),PO,ft~@/t2t\?:r fť#']L|Ɣ::q 7b˹DzxؾY@Al77v2 CS~hA_#3Li/ٕ?MdHm(K=U¸Xf<>Taģ˦qi5d]e%}@cICb&)תDl'<}nY#|620-מzA*iI? OP}A) $=Zc?Z>wMl#u͵JL 2 ~K6QHpR^g}x ۦpݒd.xy_#X?s)u\dK%nۧS9 +f4S 9HaGBOG;9X3}Jd.|ie,6k]1s025p_Ҥ!K³%ɁoPQ>/6Gj}T4Ykzv &j뀜=)Jd Z>y_?7phnyc¼-Y7xrՐlfu ;u'gUT қz|܏NefZv.+!㾅L[/Xib$gP<'Q͘_䋙v7Yyfy5 ZE۟f2h*aϱ RCr0v'kZ&5A)1qamXn=xksKUa/|6κ[y{cQ*̪++!YюgdD8x貟Z>x|tB;Uw+읝9A _5AXOo 4wm_0.t"JP 1V[=]!fv-=jʮq򥼆 \_̀GhˆX hDh!7I=C^}F3o.'H()%O$,ؖ=yZ溨"T,:o|U{Ma,< >XٟĐu Ƣ% 3|Bhi)%-wqKqs?Vigs]֚k\?6='aq\l vQ փcGrH}AZ!]GTi'2c #V3鋳gRr#1Pdqxi_  WԚ(ϥ$vk6εINXK:wA9AH䑉 \ǏlZpG<ŸL6Q/!:|z99@0w57Ɗe#[20U)oj⇌|"1"'WTVoË .8韋 *R2oHgظ"h}_<&ȻhNR,HU9~-f\e0 ٢>6!-:&GuFz "ƈL[Wz7Sdm$ЊXSӎA(Zއ#n>ё>(oٲ-uҜ>?5CvY d#Nw3{88/h=P`vq<4c2}=t`S9OXǭO|1<+"Fu +۹ }Mj]6 (0bBPׁ 0'c+2xH)Vs~'`ˋ¨w ,Z\) 39aV6JOp>>4tNei=-ful *F>y?bJ3l.$Jh@p)apS0[)SB`Oش,bĄ/,mRMs>vaǕ}\C9%Pkh5YxVLgCunї5͘3rBࡱ A7싫 155 gS@<y<*J8t8WI7'V ?[kpδBԣrIT\i('=Ǹa1919$lI;̩v6}\#]& |7Yǿ4o;|Η CZ6y (Υ0DTUkl:. f{26UH;^'Vh.D+k3o4vĞ.%Ez=szSwDu( ՅoӓO9W[@Q~wYsS ty_@9 ~n~?XS/*1F9{8ᴌG\W#Hjg 0E̺ƜhU/&EAe`SRI g@wwdE7vAM;8-yL# Snb! {qɞG%Df,?[v3t׮Y]s- /[J}YG)#Y850JV}|$D궫=7,cT̨_A5E9<*҆d?[f.J[LP2zP{ТHĠJWGG-'ޮ"9)qXW Yrȳpȣ7h +Us"k:3ev U.<@@w2GGTJuNUѶ" GLaHMs&>sW# h`dYEVʩ$ h`}1{/\A77sG{rAqnv@DXV O W3r~zWiTirJA1eNum t.f7&ٷIjP:Bw9+;|cx*'a(54RL7Tl)c^f8Ӏp>㥲k{|R+5Ll{?sz$^S>ZNNA͈yd+^4O}-IM/VZ߷EX2\z(4໭rpp}Df5+\sWo:Z3e8s&`޳s-UEZhJyáFv_#7T7Wn%NAgV;<6R/ϙnՏ~11ipQw~.VL'r֧t"9_:XkOu6+؏=`}Fr4ѫǾZ۳KNE)r (и°I8`޲ϋM\A J 3*gʠ cL)"b| qiِDO-bP`zQJ/BФH]ߛr?epg8ڴ:[2Ԥ8Mڐȋp.QWy#,OW_i NqBB*Si2_Y{7‚ޛ7۩ U*PmeliU?̢;d]4T·< Q4+.qyE,YO^qӉ_ã&>?TbR%ѧ,7iwU*ܚ >eYTOںx&=;o]ok:(unFa&?x$2McK.A߼ Ͱj+9|6ҭ8E b1(M^+rmqVY<Yf ag(dM;ԓGwV@t;9{Cf>&Cy";1>àVfe#k_|M粕&x(4\no@u}ɅXbdAL[ؘ ü`z߉uKkaLΗXaGIBNFxBH`qNg^xv@tbXi%Ph[E$"9k9KF74k~:{àjpu)N#rµ3ҶHMڛ9i|<%JMhS_7UGrij(O\BcRϨe9`eY_w7=Q]6)X!C-ΝqljpQ(N9\fo_>|, 3lzaұL? KEM(N q bB *!X E¿Ημ29Mt`O)*Bvr΋Qdw䎿̀`/KdT5 \P5 ʸ xƜz0ըʨoK1JM!8BYuѢe6#ܙmC'Qd!QMW xlP$\IQ; O34|Jmwoq]6:Ç"l| Tqdy5G?3aO[]uVbݮs~IܑG d(l&Ua xx^m[^©eȑrlMgc,v<*?'0Q.Lf*z2Wh [G9 p_};쒜n!C;H/H΋Hupi{6И|;i zQo|Kh;+-d,^;<Ŷ.z R)|<2I['?zNؘҎ7tO 3U]1%bF#&Ie]_nKԼ?` Y%\H'A8mH,x- j9Wf NmT5yTHx-JѪE kq8u}#x\ (2$89Iͷdn%g2tF +Xa _YW=7r,ҳ 1v*tЬbrj^[7!b>ƈ|ID2Z*+is` tID JldeYl\,۬BNΡZ:t(t#jmbx IƒJ3ds K(hPG|l`,XWtPߝb)m$ v-#'\>X4@+!Ϋ,0&D1OW7uo3g3W; v?+siS74YYuo+ݠsbk7ivݫ<I:\YU_)FW?a!{ Iάa5ی*"r߂{;pVyL$jo;|^֗ Nc[ɋ56E; qhiL./2S&RWq/ˑazwWhǹCXx&XyUx 뽛@^ e yf'=/yѬdw{:Z7\|u_pCɫba>9o`f$DL',W.ڧ^ԂF'f-/~.ǀ!J i6Z"HӪu Pt!^,`"Z8'"xkTp996p9_&R*h`NnZ_ Iƍ.2$1UDYz!J(jRV#L kuc۱ (oxԙs> "n(BhL]zx\=3|*֜[0 |ǡBP2GNZ/2bvi)PZ 2c,Tjrz_+B"j~6(*E~1oAd͑8\mN_"F-?4rHyNpTYbKJ{~"ӊe{`Oe!o7* &%m 2URu,m::26j6+'m 6Sp_alzKnKv=7I2ueB dI3Gw6_B?<> Co?dA%Q͞T!E/:h0U.Jb_sL8gސKKaVB8t65<}l _ţ dvga\})~uɢk| [y8 >f[BJ;'`4YyrZ#,4A WT]PrY$,y쫢cbU63~C\'6EJns[Q+0[)Ft>+%GK`=3r>E=k=6uRD.oS,Iq_Vq7OzLL6IWjSѰ } 㯼НWd n][W^D'j Qhlj 5X)lM6]Wt0&/䍫d/{fF+-ڔ!]qxCG՞Z*|80ԪXٮSF8H `/ bw׫2q(yeZ+ٙp` uX i$ vpm▿ԼV?*D;%jOwҷtqMAP @AZ%&[tvؿ>VgVhF5KEˆJ>aYODdjlciSّԟ5pFRϢ5kwzGjJ"JrZBru0?`5 s fLhSqAf; *yhn[pJ6>ê]vx&,[*ɟA!cΑ?ɐ؝Ts_9B(TK#G;ydYBf1'Tլ#~?M? s p -Fjƒ}DVlllkՅ 8 mo HI7ʯ9R׺tQ%^i ۨg1>˚,WnnsD3a, !0G7rG 6Ӊ蛷]﬩pR~Nd#[5{,Ac_)@䥘X6-)?僠nN5`((3ʄ ,9W!wwQJX%IN"Cq ^-FPoXd!F$+"oٛ.g8gfjcrן~_6 NۭCgVgS|[2 9'X(6i `ם@S3>mBd.` z(E^S'Iz^)c*pHi&'Փ΍ofpcȤ%T2|Ε5 c*!2=\7.LJti 7'>Ѵ4q/V7nvhJp-DsJ S"#Z.MG=ݐT冴&M8fTwH{p,x;Wy~ִ 02ԛc *%Կ9: 5jx`mGm{9;yvsؓyyD4␙gp[d71^ _)l9hThZK ~GOl<= t? *qݓG+9Wo:cH*Z#&w{/U:4`&P1}uk+dd>UNM uV\.ƹLzt+d0-_t:w踒pu}#p{m@Iۡ?j6$-j3Ufsy ~C.r!`{A 1\ 57cWѽHZ =BuNpl⪗q[(e:FX;R8pllxcp֬5o FA`sD.f~(J# ޵W* ѬHEoFjy3lM&gڋt!gjBdl\Axۖ'Eyo Nk^Tع;0(Y{Ex h߂%!/Yr83T;_]%~D!5uCphPRiÔHQt 3=&G1F('+CAS 6E+)]z5{xOi,IZL`{4MgYY! >"q6/ o$EgN!Z@gpuK?a.՛)HR=A ;q]xuuVVʄ {$D /.O=aW\t.cHF^W;'΁dpq=X.int+"^v@2zicN>WP$Ԛ%%q<-8&zy5:q=]gi\sř.ZZJG8Ԭ[7{Һ9/;xJ*cX +y"V>F}XVψ'%K,5Q !Y*ҪZ)p w?^(k2hlҺa|>e2s7JlH'>h4:[eOֲwFj= Ԗ;UH4e+=EwHʯ9IJOJfrdήh.{MrVC.w_œW74&2yOvSIjbUlDT@{wKye K}.nwF::u z6Y Iں.S;-ڄVQWeKu+Sp`&Dm26j7VrXBQ@hAвUdHDߝ;3f2.7LYEF ʼn\`&M f vɅԜ,o;*TSC!?ZN'*n|!ЉmQ`i|kC{bY9̩ڟLpx+F0N!{w'w[eo%*c0.CrC.B|HؖD5pS(W15s ̤8[fR'=nW?kU$gH䬻qe̤Gꔅ?ijAoL/CeML9  K2#L{1 J{uBV8.O+y}>fIGk_kɱh :HӋئ*xH,Snߝ 눤9*5z#='W ErӷClF/R*"_K7U̿\ΕSkeϝcb p{L+4fNX/!I 3YbwHߋGCmZvSn,Q↶uYrPr(B|i \uZ{9˧KЯ$iESi>Hsg@r i!.^{m|TU,Cb]m  $\c%!BË=`6U@;ǯrCz~MLZ _m6X3@#w%:f8xkGZYAHv`"ؐx(4E{X-睇e*œs?Nkax P2rJt oKՌ?tI5ShEI?hlYn]E@[rƟ$s--!#Fo.~HeQ .Gp-GP]! @U%fB"TLUZ t2k%"} ]Q>P0"\5 GhN9 %VON3 1=O{%eyj% ׀xڱpF@zH6mxRngÈH/b 59_ y#Vpr ihmP?9#GNK$;b"&k CpnÊ۫ PG,0SA lbsrG8Ne$_IT!usU)} f\#i9ABN>6Nao}lL(P ޜʘ{& ^[U':\|MU 0BQ.2tz̭JACEq2˻xO88,R 7p)"l@p݀W~EsBh!HtE\VQUr k{;#Ff称^ >Fr%]Je)R:rM|6 IîrDo{_jjnC%8jSŶtH{FBñ$ c0_"W'C~U.;`g<N씟MΙA'O2ھM8f}gƔ3|H.K{gƣU!7&#]. }ƞskݠyvר[ }۰mO0PV/2Z&"wċY*Íu=(!ZtU=V[nbF ?~XjRhzъ&C++%/p7 lOp% CU5lc\4 yP&\I7|f/V[T>NF޵'XyH̄YaQ.ExWR'-J=j$eSD-ۥ KSnqLc5jl~QqSW5\+áy7c4POsaH$; )&0ro)oiYCz]%b9~]IrҜ(` zOGy7epL"ۃ Ɲ9[!Wo].8֣!~ןkqXC_72o_eA-A0E]\Tޓ^zSd'n ǐ~a<]! &/V G쩕_Q55/ z>*Ћ!xI|y \F*o}0Op#yyos/7<=%9l/Lj6pV"[BQCq7(*ÔM^ҿ'AHi&O`O-UL' Q߃>v&@T[O:Cfje6W Iq TPak/Mc)>?-U=P0CL0)ڋmpDžs EjOR_[ E)i%ijP s]s Ŕ{.5mCl>V(d2<myFK=TG1~LmPEɝ5{2_cZFm-y!C_^Hx; Ճ#g^7>9pGnӔc0WVݩ6C"} c$%4mxbJ+JY8_D5~Js^j?(#+ֳTLk2 v7r:d Z耋@4x A3J4t`á$y(#HijovHOe ې!D7rl<4 3BShOӖ3q) ^:؜z] %o>xU۩l"kS69;Wjc'lazmHUntդ|ĭovdS~B2-!!&>e qj]>F EгX(͙U9EJ?3%6YGx~F)ݘZ +@5edE$l. }^X?i J=v21_ HS*rތ.d)Ēum;RMڽ"خVYμlQqDq~&e P7ƨ=>64,'?_HK$GXO"<8hqi*l eRIp>R8I?Xר߳oK˟V)PJյN&QƵm0>WELs4i+I)_o&s1R:vp" $q =~9L};ge'W%*ػK2۞ M#R7Nn 7v⊔tTK&`+\JFÁF_j ӠeT2۸Qϻ`xap׃Ĩay;K:s m u/aZX-J2-Gvw0f. uEBOJ\I {}7Ys:Š>X"M==Rf^87nđZ)k_Y-KeO8 4Ďԙ͠o*4W37IH@aԥ;&- O6 g8)֟&p~$v@k | kjPRx/~J꨺w| [jK6q#yL D& 'F @Ԯ/V{)\js `XPӴTbmOJ|˳ڙ W*[}V*[t bS-uЏ@Ө ;jB[0={m XTr8'o㇉m*9!\\f ط|sœ%ܡHϖm귱sVިZ18f )hz 7(r-㐳$ &z2FHr'l{ %qaHX,Geih3U:w5.AٶĠ[Y;?fmwo|rXrGXيR ~ `뜪gUԦ ;2{y({])Ū W|XY6SFRQa.gUE9,4yOQ}np̐"Z%xEӻ EOK1E9mN'VceKc qS{nKt#!{f(>҉zY󻔇K!ko RRl&c`'Hͪ=fm`Ԕ܆B-s2z S/ESB3];&BC-Iw{2UC^WM#*9oEϷHp_ȸ(A\Z l%6֤逸QdWP7DjsWBZmVeCw[nd6\1w` ys4,'MQС>gdWenk7@5xŬ6U? ~sv/zb_Nv juTТ}a b7,\jZx{7\_hܩ7~3/j>bU΅!dQBY:l|&mk=!8UWMχU@kV՘ goE|lg,ܸMpH!X'38H*ZHZ ǡŅ6MR3_$PueqdpΎ״ 3Er/e1p=XDޮ)`۶w :Օ ʵ~u5gFq9@*ju]\5о;ξ{EC[LJ MDrD?vDbtW5rX!2Dc'ذ_2$c2H(+,H>c>vS`qk\ȴ\Ʀn4,h$ yC/Qḃ4Z][A+-O2yg^ gsZ73[1bV]_a".1ɭ.SLkɽkG2`NG?N06CǽIJ$Xq33Ի*lޏup5}H0 {gEEk8;maѾFɲY!z/ iPO090x N*B ,ׅم->M8N XL1 "xeEAu\j]tj >09EqC“7= :V仮Y<5J&13@nZ獿ytҠmDzy[{Z D=쉿:؅g5?ϗ,0^l1HUq1ZW#ପ JK+| cBg)#VJVɦ>/dE\Vp z}qB_D!1䉢ȡ7$#UZ;b s y_2/*{BϧFaqsQ @ɕ~je/Ânt |{گđV{`8` [5mFLk Cv?s!oO!2wԦ fXtYn+Nt:AL7 ưgL5+dlڇSpkiε\1/J8 @elCh7OluHE,Hݣ;S߫SsOFI[B-I'UKYY*ƢM&sST`YF I`{g M Uh 6mPOW須ȔBy\P祭q..x[ށWDk3|90| ^=VIhֈI/n%kΓFv `Yp֒b)5U6=ʏ뚸T!S_ ph<>TEvlȯBt`Y;2BTtɱ:)"57 cx &,A\x}4hi^&'.##6]wrݖmvpkbp9@ÏnW1匫V=#՚wz)/'BCcX =Ps˒N]< F"F;U_׃T}oRDI-ٖ4cX4pcod/1VXmm۶m۶m۶m۶m{˶g3cgN&;WinO嚑^&yR:!Y]1qKN3 ?OM1y3!8r&qN5GYap%R 0ZSiѪ'bn6"b%4?pW80eyp̬zEvSGoq0 ےc+}`_H^Eƞ`O ,Vn3Z}*J$:p5,&qR0~7>5FS{uAk;@s󁺈W}VGKP ms Q͖w9োh!5ɖ*1*4ߺg 9WEb޼a,ZAVM[FJ]w&sX cH`4>4,柲^qJ˅->=|ω3wjB6x!SXN+n FK ԇqH/ -D7' 72`u`TBH5YU%J3H$f}j MC2;=#.Lȶn 9O]%$*Af'H _08c苧P͋Pkk5 *^Je{˨+2O~mH 2Tu%D[00͂ySfUq;'g'2G.(J_׏Yz~. R6r͂5)Yh Xc*}JV*NzrWǟe,߲O;)Nux@e!0'ݓQ |c3ՊduKl iTm~:y 2x*J\n;r;p8Z23'9GlشOB})2WƸ cM# =sZmƵWEgE]9SHLFk.46}`hztj,W#yYQ..X-!&)S=|W1@t4E\駅s9ruDs^L=' Wڷ)|쇑0yu)~vAΌ ҜkV~nkzRp\v< (ܾ.6E$);5gKXJmщd9rxx(7 Ι-M82/V3μܥa0T2)\H,7vOatjKy\\@/ ,2H1\n^V iġFmZH1ɭp>kI-YU:FR8#Cpjqrۉ8 5ڸM^e?L RMG]ցl Ǯzui xozӰ{JK|C`u^kP`7-+QsU\kKek> Y@D F9qj@ .J@ŖN?4(䝻]-xk҉& 6LdIk\a', QՐxȹgvAˊ=B/\;$]J`v4c+x0ڈF 8C`6*ԥ\7 ʰoA: ǕPQj$$mFe4{j`kU%hYѬ׃Q{w* J{0>s| o_FIK3B[RE ٔK<;Mog243\0~B5_ K¬(w[[yb8 8JZlG!Nw/kU/d^ k_dE k9ǝcq_Lȹ4{_:5kZܑVZs)C} ŬoQxk.q\qC<ēxZ"FmFljP˼u_kg(l^h7={Y(px*OoV#HJb8|-ޅd ^"Zȑ5g\9lA~o|:cVHg O~Zސ@3>FU-WȌfaB2+dw<9}n OR}7˛EOvp͸Ijޠ$m|SyXrV¯Hֺ?o)1[qjHBN!$5fgR;`G[zp=G>gU$O K2 \mUT f?k*i [ )`u>}SOmX.Kj6Z*yhKh1a;oeSߚ鹷 ,xA'.11\;2$+i\|' ԥM@P4+c\>lKc"Ɔ^KD0%hh\ڏb<`aL ryo" @:Y>xQT҈~qdw \XvCO8V%DlgrJ١MH^=̇6+pⷝ?] W vkaLC{ھK|A8Xk')%AלEĦf. ufz5F]+}Jgp8WօŠY#`^NrS,vsbf!t3T)7 g-"*,umFy`f-.)n+ E|  )zyo*`R+L@EOV_%5SbzP VFceopՍi'X >ݹ?2}qѲw] %м]Mf* )y51ѕe&xp{4';.]%3.Y wڼS˒~9ki7JAF 6 sva'grϪNkS偡Ó/gKHS +z { <' V\bzb)xf|NO8)~n^vm.4EuhUraa>08q=Eh_mePb{xz0#|дeSrZ qxSkFXε2}(ZBLTy)ոQa "g&( }ΈD,p@\l}+Գ+gˤ[!q/C{a)y“;}\&./H6j[=P)Ҁ0/>𥉉W>wu)m7o <6RT oPOl"駃Jzt ̅e-\W(D/t*KHWKRY>oM19\7AXԕ &3ˑ#>?87jLPoNj`O(2'&Iũ $Zi qlh7臏,]#x NO@F8f'W%8 wBr[:w9culG8 Y` xY$+a1 dʑ<[:Aʮi}w_~>PWa&XB FZEB,6Cs,B('ȿ~yd=DÚ|%Œnػo9} I'ϳU<r +{d>7R')tlO0ˬN+5ꄣa\M'QWΈLGٵL񘬟rOjkN8T^_n]ߒMiL'P@l@IZc0ʸbx3T&Ag;߃8v <ELdn+g˿B*јȈ`VQXaH"#bNtN2b =j)AmMV8 !P6 i o=F> A(ֹ#,y9x-q,HI^3gȸ<_iھ`GS9n*bR46Sefy*[(Vqx8ټ#7a<=C#m??*酩tL:9# K10?78VVxqD 1{K:S[|%-5C0HWUW?8kO w`j|HA&{Pz0Ӛ"ib#..>]гiSr!z#wl~Zv[×D[|q%@UZ.4N [&iZP` ‰39#)~DҺ~@!wء݊y{^,Alu|6ӕ\ϒ=)01:AYW`x&=)u~6mq_4拪bNI)B3KPE=g'!ƃn{NV-՘]ir,6Eqplwjy+G9<}rK Xd- fD#`ZCg[?zOaMfADs`xĶ# iu쑽R5fY.C 5k$i+JQC)H8Vd!JUQP$,[8uL٪ԏAr2eZj +h߁%Œp٩zFoN]GT(<_}\E&~qCxwj2P8.ё+|v:r+}cudDo!4 owz~['u"MS>}iI&Xƭ(H}HvQ2^:gY#asml9Vˁͥk!m6F}p -{dB{ GC-?=+^ӇIq:&@Ӈ5͵;5TðPh WLuۀL|EZBGٶҭbŃ wKaGQ1j)תYNp/׊"Ϊ%hur1okx=s{Nd\c ԍ$O+u =O*?Q]]6؍Tq߯6ƝbxߐC<;oarŴ]=[e)?N107`U/;sf ]<1T(_܁iYC;ATG(-GmS4}xΟ59 ] w~4s@V,x DM&BTzR.,UjxwR\&c0߶O*㳥?CQ"^/;{Hʯc2u$dv0֒}8g^~qveT#v#n PMf5^xZ,p0uGP,:3p}xSLEJY:51#xp%gNtw}]><-y`vUGIB ]ߛ}xT;5;{jj7A 9,j˓; Fs.7*q^ H_p/.n#o㫡czYdUrݔ]FBڈra9TI%ʼnNc/G(֡ k߷(JFDK4e_[UBJhyd3p 0WxU"IaY~> ,݆nE)ꓣ1&(wSƛr-s&3L^Gz&&_K ]EPmUErpϙj/ú8Ϣ%`D.40(յ7:+ISGt,o5pTg0e\KL]&n`3ړB&B[d &P27OZ{;}ܝR9Y㖔>֞7 :VvJ,d7! iĠo.ᶆXOu7 f6zZ0NGЇto>qԠ'VE]e/gD r'Q8$AGqκ5I,m-@ea 8J֊wh*lUs{#̎^+)PqPB~u.V$1-f Jl%xnR A2g9RMqm>/}y[xY!oPY2ejiie\_Z>;+%Dgya {jT0Q@ ^wh{ku+L4,gJ0-{F5/jF"MœE\ݓWf[@` 2(ߥկ:m23,cy.|4jj a-EV(K:qx $>OD:'Y[9t_._-^3حPsh#B+q!= MK[VjnmEʚO8$R0Ƃg#-M`K8uQ Q@;(a|( slJvj'@s={ƀSkNȆ}(\쳕<&~{U~>_ EǬ(v{1a 34}}eyu ID-bBV.eH̉wt>s)S?Ta6+9w5 9[ASnƢ/*o۹~hŔ5g\AAemKwoQ$[nYǭcF;+0v2WpjB6NCU n8/γA9}Zkn~V!PEVU2AN"!eQƌ|6"gUYI*W*lj4ODXSc\u]"{~i09"/GW @G]_V; $,! g@w+,aNZ`=3|tmRղ[r ̵g𘹖Aq2 sA>xux|zȟ'ċ#w֮e}aK|yoݒR&J]/mˇJ%q?,l g ě<2*#m~k޸GgiafŴ9>gD@y|s62V [fMX":B[W)jenLʸw,h?.ᨇ}} !Ճ٩k;;vM;>;PI/ȠU;TRӌxneh扼=4wWjJ| ّBrP2IFN.i2'*qX<:,_k3K.›KcPTR'[=\+:& /A^1YthrpO*ʴ3~:\k^Z{ 9~mxL[rHh~B] x$g b>*-'\-Ⱦ\I9b5$sVʼnq@gN>B$D~9 zC rx^_zRC8v̨mYqMJPbS+5Z{lkA|7[Kr $|rtx4'2])X;%9^0.#kNV]n^!^ m7o&jSOVT#!ԍB`s7&=!@J?֜Y)"D&z, J1D7mN  M^tTAt#ɪFJrZEaP3׵8LnGQhv`6Ab7&?)MOrrP iw#x_̩[tA%qT׎-X@ؠӰ _ص:A.A{;5{ 4"ӫu0|jYw0UD!ZO Jzp+J#up,wf0[%GTJy+(KL^#Q&ux@A`M-9`EϞ SZ7"'Wep !䨇,>ӌ8C<ތil2?$.͘eC9ӣ-;l>@J"] H¤޷ۤSL\Vp %YᔢcQ3yw5(TWøouNX#ǧ,o'+u. v:ZɃ4O ZDXCxWjٔOZ@nD*b8a_Y[XN_<̇.M+hTeqMU3p8P >JBYǏ#o5dT}smwISҔ4$/ħaQ8/N7)}K$_HFE퐇܏'g:%c/F0U44B[*a6OKpJ3i 9ài:tgj,MvC$+' UPzqթM]D_MA NVF$[e>RE 2y6+ aZņSZjݠn.ojXJI(YX_Vɩ mlLy^8qq&$!ў@X}uZ\v,tqrr!oC~[y9 UpGkY,բz:}gϛFi~9wj%p 582M a1&"Nf g/VkaaG1GYR}1a4$^ruY[)ƼrK"tФJ.Q[]3VcA(D,رFFA5(՞ v{#:CwJt7$e3bRq #cbMfV6MaR}`,pE{#it.Qq\嘋c@f@6i'AzQW} !qK[L5޽[U\*$CԞz-xUI+@oXo^ _x?c(}X㭑j]0@ 2mW(3g@XR= 3C#ʘ|wCX`wC:7 )PKtau(/bԟUcTFBb0[4N$9" v91bw#A7vaK)!1z UaۉueWuU'h)>Mutmkr)! F8i-%|߮b,ǺkX"=O0Hr(6Ɣr3ZWZt*Ú$. w5/i9\2k(BgkS|(0(+I[4Dl$Yy}>wXON|?!Fh:ÞS]Vjt KR0FJKS 7H l# ;Edt44ny_?R)uyF~#h&hD(SRط@^ӐMDh_5.5$G]j; $2㡉) 1&p{CJ7YRE@cZm[XҤvA"GW$LC3Gp+ֹE":7;D-ӊ~Iۘ ^s j/ā]jN&"C ,(Dipk. d5/@-%{~U_Km=8JBJ*A*Yt! :c06iMؠ[Iۯm+ PjRJśY  EOQ5kSEXo7"z6/5ݧl5zcQq eךH+Wa90ۼLKر` dN6A95UQ5MvI!1j DsOuF ,Slkm '[ 5Kn.whjн<%\hQ9b]l IZdt pM<2a*uHw?bHdc%.{rVM^̗ΜxGPidy3l+2bم$|ApEj~KfŮj%p$Nǻo*‰xigAGm=tLS٥>f^+F0h"^*@ڛvf gPᖆXK&ݴ'{/Mɥgܮ:Ӻ`SV&aʮO.>f.I8dH0UdKT!ڠ- s|Ǐ~v$x>.c9`]ļƎˆG{Kи'Q%xMt=Q|\کE~%#-/:m-C Lx 4SĎhԭ&4wǁ*>_X *f@H?L9žC2%:!l1tH3CB#NC%,vn| 4|/"VE:;7[Agt-( Ӏ:&o/R?zùXȩ[d*ci~O9ii2XC2 +2X7rU0k w/V]H&/ GF~ }VT0_3wRsK(bp?zoc&}&ȡK (H1@p C}96gW  TZi^w|cF翻)#_7]Uv$4Ѽmٮ'z~kGIۖiO R TIxv >=+l\x4D?ҡ U\(I\ou~V 7a}.k~Nt2ږ vNH:!-_ƹJ1S-О ۃ9emϺӰew8C‘ O6*t<hKPoK@N8p]ieCx Fk$Fr|1azM=.H]y>2̶&ޓ0v1J܀B,z,yw#i CvC4~=T ;uFsU!woۊjKY5-^E͜ ۖd ځ˧p^ag3tĈ``B_>W6BdY}:^IV^Et2G?}[Pll߃6wϡ|t <8p><t$N]XC; -jXW*p#X߫ўrOێ=aml.ֲ[7zWz&'JkY|<,pѤ:s_+Ɣi#uE!;Wd3|I͢g.4mvE>E|땅;O'I0R;@˭o[CUN{eϮܤ 2&UR8<:,kT.XP3iLW[}A{ m`e4< [(H*5o}c$l櫝TAAZMG0rd-g?)rFxv;u~ Ɏ`X׿U5˫ Ǒ2 EI [3h" yee\ Fy1 L&< kNLbjJ+,YBA3obzUȥfު=hyn "WǏb;5Y쓡@/SS*yENtue4zboAZ\ a#: 1V)=DNUbI]Y4jv=ᠫ/>MCXwynߝMLS4p{ 6d`5sЋ-i\,/`B|Ґˎ9TN ,luG=.'tƼSE>5}ZBK\I}g7<95 <K#We3*COxי=S>|rxAT(vVBv>)9A ?$ D\(d>?Tk3M`MF*K\Hc Zdf2j-+a Yϡ6]MQ޿1T\KJaAWPCy>B~9^U_/bz(7{s;HMB#|iuD+++ŏ*[7|۹A_!Ba<ٓMapB2v paٹcZX{>YW&mGEdku驑^kUoOՊ^]2K+$1yw+5[^g/w"f3|շ:HߠFw,c|϶@7l4 xeɌգ{MÕ2&J% o^x8ێ="XTvC۲QYjfcc( `T9MQUtsy7zgY}mvX(ãq' r\[9;UWMN^}ǭݬ-H\ŞQa!GIzPXUgZOK7~ڮ91',}F>Czo`N˃xhIن knE =8 ;@g$Z2|Gr<+N+fȘg)OShRVcrB;uuJ=0k Z(C zY";؞`[gtggh^%,l|Py@Kh6oZϥ؄{J&(AԈ" , <, &S@ΕF6=aݞ)6P÷5m9C;ϲMx==:24?\,#MB9IH5Аm,4 l:2n{}n(TXk=h!y-֛HK)j4,$tb7|ͫO=r g_wGje<#Uڔ_z| G@ E$ ~"[(**-1VnR&$f QFM'=GKgT)VrTzTy k]7e22)~[E8J,z^2z9#l?%a;4?0a?$VE[?c5{,hVLVDglu#XVW26PBG-  ,.ΠRb<!!|a3ڋ N޸sd챏~adcR^8򂂑6T[hz#h|m}b:k=KqW_wPq/܌ ;E<{> M3ag;)OZқ,/{|6$xVN:&JS㮃A+-z0t/ 3I!{1С$`lFbR'[s[NE 9 wgP!u[Owi$󠵰u?>Q]"YcUMۥh*<r1sEӓ/'D 6q8q_`uӉ@={/Jt70yb*]y)y 5K_|.gn Ca""?P c}yU {Gu/1 _dlxe, p1Nx=, OWloc})d (mocK}TZ/j"NS~خN|$a!%$2SϨޯ#Z0vhh%gln<:HԀaF(H-x>, 6rzκA>,2ux`qn/+YޥI-1 ?/VͶSv~$tEF!JaVgKyr? ʑ't߫LOd!$7UƷrf(jbL+HďI"·LjBqBٲ)x/fz9} #P"{>GjY'gx3C:?llde:mMRqZ}R T.@n ]E>mcJkg8k\W"JAc=}F%{dP6{5͍̄iP+o{ZlHLS av~4m{i?ʒGmU0.u9U(s#-G{%,YWYq_:&اuPI൪0[g;`v[ LKgEl|J%yZgO`F(Ơ` -G&6ԑ:ja%&ACYPPr'%($@8)c`s>m~OnI2Pi/ʑǬ9sՔ_`~R%qөNVV͕W3)˄qc$P]ms{LPԧ#FԾH%2^YK`Lc̹DlL}N8/iҐ%ftT7((eGL#>{Kg5h=rau@Ξt%-\SƼ/ fv ~Y1a^߬UefZv.+!㾅L[/Xib$gP<'Q͘_䋙v7Yyfy5 ZE۟U3wI4N@jʡQ9V;ѓ5-Qv šaࠔ˰M6U[q|pGU -V^{؆>D {8yV;i>m m~.Viz>߫b]N30|n3'|1t2Aૠ#i´-NE)R~j!Jyg+Z̮%~#R!PQ8N0닿HmRKc=hm &@b{۫hF %>i$@۲GC_Uu2U.%2_^S~=,74V'1dDB1mqG„?& ZZD ,zw 9]vRcO,%FZY`\;A1:b׏F~x } |X;W4(C}h2q 7 X (Gf3;['OO;'NY޹ƓX bs|cKݝƴ0RYC=ټQ8_1F$$xEAM#s)׎1=̾+0d-vB׃WI SX#R5BseKZhjE68bOz"U_/6(D9~Ə,هmniz^*IdiQmTb%^[I-+IjѢk|QglĨ;,b^ uwS:EV/`AȺ{5e0}87p!B-b]a1(YGK(Op^;Dxja'DAAC '!6p= ś4YKP}Ai wBЩ;=pWRL;r..P$Ң8&g-3t1΃86P }WO{0t7ÑrJ@ &ng>CѨϨ ~H:&CL;# 9xT=B-blTgZܤ&e/&uLŕ) s16"b0t2z 6*|pp΢Xɕo2SkY`elO#{MXXb&Y6ذة `4CH#6$8vBD.Fk  7#l|njSb+e*1?TL\ }{R[40Q%3MBjIzAQ\.츲#kh?Ğ>*t&ϊLa(_Rܭ9u8X@_N^|ټP<2V2p!c;,8Ø}q2A &x@qb{ { BE G*dÊٷeLy\ tM=P+TnOFrs \cϟC¦~Ӿ;Ĝjgc;`ȅ?֥l[wuKC>u=VspThԲ18sUDqu.$ׯg%Z ,@dQtY0;ٓAB č-,j>յ-F#pɧ%ZYO{|{З}MQT&T LOOǙ H}t#˚랚U8șMsŒ-x9V7p8 e<pd< AR;~W)b57 F#(z1)r*"$ܕ@fG5NBl،?k& s|B(ٴntiE`YvC,4_ Q؋K=*d.!6cٲ-G|O 9v$؝kU}A|Rb&0 %m m&3R'N'W !re'IkA~Khfؿz.Q6Yp9Q~_2ʶp nv+5-.To%GO.qknpRKđ6ظCG\S*P{}Mn4MFnp^6֤nd&# {$gGoή05mWBa3"'(/|Ԭ+s/@[glL_&I% )9x;j68bzoyDټ7wY Y#MQ L Dq4>J}mW;{nX40苙QqK9 )}1'jrt\ ~\d:J3xaEA/q-'ޮ"9)qXW Yry„o7W]G9o4:;VTNtg: ]x\tH1뜪mE9.W3^".Z#Dj-zOIL|xq# h`dYEVʩ$ h`}1{/\A77sG{rAqnv@DXV O W3r~zWiTirJA1eNum t.f7&ٷIjP:Bw9+;|cd*'a(54RL7Tl)c^f8Ӏp>㥲k{|R+5Ll sz$^S>ZNNA͈yd+^4O}-IM/VZ߷EX2\z(4໭rph}Df5+\sWo:Z3e8s&`޳s-UEZhJyáFvo?tT߈n%NAgV;<6R/ϙnՏA11ipQw~.VL'r֧t"9_:XkOu6+؏=`}Fr4ѫǁZ۳KNE)r (и°䤈p0\los\nE&cx!g. ][Pl%ʄ{3etzwv1{EYT ظlH"ݧK1_(Eag%w h̎M3 }`SmZTQjRq!&\mHP{ME (ҫ\r/48~!4ϯ,aAM^DTij)s*(ն26fMa v{xnO(dsy~ Ea.;Z1 m OzIetnU>+տKk8*+~sC ] P\@4]B@%aŖSrz,|6 H .az !`F .^O*6߲aSG*s< ,z,i:dv4m1 dicוXM, *XٳbKbɘpK^ˬTzVJ86ha^#S[k[eZl]/𹝪2k0vj418%KDrRRvvc{=JaE8VI:]3WJˇ7`*~O?rEfӌתv+@3=^a|Si^N[^ .WqT QZ+=ha)\v5*y=hR&RR;:R5=gxH{)Rh=%.4GHS9Cw͸xޔLN8_5XYFn+LS=~Li# !:W"!`Q[G[US$F{eGra^X{ EP%qðS-5}=/ WVܘX5](C +k.\|0Dƛ-[I~"4φj<Әg ]o3)H~R=v,ҜVprȪEM;K{g`U"bwER8tWT3ya-%:W0\2q{'-ZgzBO*N (*> ``MVln'/oM3ٙw~?~[ ЁEs+4J(6 l^Zwq0_hVC \ٌ᳑n(R`Ai"Zh{z"1ϲ5[&۞<+E!k4> >JC2,6Iމ0Lz0(_4Z竍oJ<1Kڵp ZDY+<הo1ݵ:88 nWVu,/N B"G8.N-un\ Z2`,gi-Ԗr+wV(#\X ,_ SH.S +ʓ<-YD$'wr-guORWRzoTS..iY.ZyFږuZ{3''Ķ_顢 ` Hn4``>#] IKHxl^5,,F'k^;Fc0$ybs穩@qE3,\Fԭ4Se[n)Æ)c5KLa{C4ĺ?6^$zyGt,TrGtcB|U=j76OÒsQSJ200\/*XJkVjhQ03LN::|{9eS<4kʥ럝|Y)]78. Clx6>}a2ܼNj'[R|É.:F+~tn׹o${У}2g` @[60<< y]m[^©eȑrlMgc,v<*?'0Q. #Td2@Ps55v%Ծe 7G :/++ #цէeA*@crE&GپQ,x DS^d'n34KYhTZJkj$!lD\9ccJ;b>~b3Tbw䗈Zqc`' v}ջ-R),gHs7 "!ͯ4$ ^ 1x3;Q8vXѓ#ⵘ*uF'ebrvahԽ ųO4ZQd/Hqs:No+1JX/d@dWޱ(ÿų/{<0oJY6Y gOё-cT!=Y*P: GoC}dvUVZ~ŻTff5@ʲXY44@"B CtPF65w72$HWAxkfPѠ"X#f *>W&;7^A3RH "[FN J}ޱ i<`_V( BfWYaLf^c :nr-g8gvH~WӦ| oi60;WA*+NoW]y"8}t>2VznVS<9恍~GQHCpYjWUD6w}+NÙrIJ#O{ywX_62;e#l1&/k4{ǡeIj0@l{~TNH]YU,G >]GLb]8@_㙀cVQB.0NnRyg(ؗWO}7bW$!G#mun>Կ" Wlsk}$s VINX \șON̮[^d'\g!! B8cmDUAQ?7<Dp6N6D:ۥ rrlrMTܴ@A$O]x:99c|dHb h5BdQV]8F+eZ7c9=?@QWש3}F D,PPjՙ\Ы/G{gp[s[Ol 03C2 BV:iQ NȨ=uCqCi%P]@plR} -26ȟmuؠ|pŘ|xj7G)Jz;m8-;yPe-+L+B셃a<ݨ4̗HWI ԉ`#ʀ$٬)L-}ч-I]op/hOOt$>#0˄ɒf"lW~ x|XWTAJ.6?gڛ=Y)B:?T޺}ž&q.ϼ!) .y^=yqIljxb_7 G@ X3׹@ RHE48qZ}-}:IC5&wbϏO$3iL?O'FYh, xf^S}#-<xɠsv}}}R#c WҐ%dxӌ iȏL|}I=Kp qz"] wӽD.c<ܟ=B o!<(HFBh-ȊBN*}D @g9rW zTֺ=1jʫj)5"Bz9~Oxg˩RJ-A<cp۱@oY (%c IpzLn5sENPD^B[W}Θxg] =W+l-Js=ͯLg&! ͍td!эs .:i/\H}}@(ǸX4cH.GĻ~ &Rơ]ػsgVJC!j)۩F5r ɶ@d3PvoO{>QS!˳#PsW1Vf=,xEZ g(i8q*nđe'G5n%v"dLHjP.%oUt UJfoQ¦9H@ama>P`fw+ň?y} 'wVΧr?Bu _UJe~5mJ%)?<.I0&Jmt7VPPoTAv*Mk RZ";*@8wv^̪]ce %q&0zz-??0Li2P0 ŚzF14i:[Tꥉ<삍t$emNٳPzHkbY;4u3hX`CD6A'< 9_sQ3iH7G&H$SHqA/;§K#&' =]'˚LD.7T"@!+W72?\D_`W.) N/_FxsY̜ ~h s|FO#,5AL'م!9"ټ9Hҥe^`cT mNw_b+/Ȏeӽ~fWfZ=64qO,Qb-!H[3{9Pػ4Zlfl˯ls%u'^ҿ}UY]@w{ Bg¥bSWQ˙aS„LY60(@u0^Ȑq!Z'Jiq.N MmL4j$mۖ7g2mrUN[8Mw.} \\GtTg(i~;?& ̧<8csL,7F?x{&k194io\%{74Zql֦Da;?BVŁV"v2) u$ n{aMӸ^ {Fɋ7/Z΄kI#a+D`Tm5楰7U%p(QT{v딍#h JL *p\x"-F5Y/Z6Jdpgl2lJ,8|?IΟ([?=5qn(qb-^vfAӹ?V?9y[\c&dBaקϨ [^(wqTﰩV0}#;gi:1 )|YCK3C?r3P:G%4{o#T)c7LVJ]0siN-s SV &X>TC&wqPAIo_SHprMAB{OxJ} W,=wBGS!اAHy}(ӏ1wek%o"K(>̿tX_kw(Lo5fЗ)A|7jB9>U dd43<YтM Ue₿(|1KSSgOaд&oAdžb>ڎv`ORk/1Tiq1uv31t_gTM,j$[yK :djޖe Z_i C+8*:߷^}7}Г_׊(WTрfpӫ0|d6k|񤰮!B3C>γ3d bé XxpmۏodN(>fy|dn&zG="L,ajgx#Ɂy)>2i>:~ڡuCP)Ho/), S+ztoG4.Z* `DDf*q59  &SĢH$."S`ZLY%Ġi|WC:Ȅ-ācnΰjlFt&AjN] :"kT(ڸ;%"Ʌ.-wO`2+bԂ$L9.VR=9=^w$"m{"av$A\lm bS;څ rBX UUK!]{(@oS[- EE<6 ^kB:}'ظv;Rhasswt |JQDP @j~Jb-$W$c%yHP٩3)x^Ƹx! 'V^ ܒAzЬRz/e^r^tf_bp{k>S֨KZ"GҢlKfGC^=2#ts(|C!ZN/uۛxKWQᵪ0z賬ru_64ƒp1|#g~k>yUgMŀSs"i*>ًg :H90@Ƨ/RlI-ps y,s@AW&aAPt R*OrR7j1)>3S;#="L'aPvn>:{-:+ۊ)]@9:՗mMDZO[ *n"uٸϴ`hC׍/:bB>ICrLA,V5DJ49)n0W߃:3Kqn\~S4SC&-m#.vȍS jݬwpaR";Nnſ9QFM{qCSkRSgqri8醔,7r7UQPv!5}$>tՊL;pk|_Yȡ]+g^WV_PTqmTUB0SoB5l'\Sst@k.ڎ vKsv0zyyD4␙gܷ5ocBSrШ8%S6ܵyz.U T㾻'Vr!tƐT}FL|_uHiLܡc  *WȢ}f\sdWls=aZ>7uT[q%q7&tG* j7,CIx #lWIH[6-f8機'Z]B@Mr4.K4gxs5xd#܌^7G"ok/,*F խ;qx±^?maVHiJB 곱=ރwE'[q}DB#ͩ=ˣ*T{^d~&Dr#}9DU7UܚȮ i/65} xEڶsm[VI&ؾW;yQ"`BN<$g[$|ӾjKB_B;[qBgvĻK@D4C/Jk |C?*Р҆)[(ge{,Lҏ ^cPNOV+ylVShj8[z!&zbi: \3/ϲB(|XEl_0Iz/whmGks@ch8}hs!b)i a c~5jg]v;1ؿ\1svϳAum㕬r7m8?D`<*wϩK6Q4aL;&ɏҖԜ5gRi!= I>]mw#H Z]sݤ![c+?^QPSi!~`L nv{%.+~1wýoKwC~^AKwd&$Յ7EޔƱ]H >$(٪@*I"I9δcY_-ۼ$SgDqSJ𢑋iQ` XKo87iz0!On(WT\ezeTRbq/0/mnJPWdY[y_|l,$~hi G97!ZvepA̰7ĬAҍIu>zD|++!ϫ3Rχj*y,^њZr mg4+JCՈf}a&D4 蟷Ư''Pʅ}Jyn0O8tû L"rZ"@e;pc4t1mt՟N+’{٭vu{lGdL@.P@aVz9E Wjfʋ߄VC{C R1fN- w[hYoxgVkjnf*rvےno+ISNE 3VE%Ӊ"D w"wRٙ;/)S!~l$:"5-!8O*z΍>J! ueﳃ󍘸Mf|芶mnkv n)9i Qu˴ﮃS껜: )9ӇC\)sh2/LI]@*)Rة«{:PV&Lأd5Π _$Lxqx)sC6"ؿqbh/]H .WtL pݎp\)( 3M{vBB< Iִ(.9,!iq6˫ +-Xx:OC.tђrWp<¡fݺA1I||wV@Uâ?oXqSY1b;zF< V (1^dA_@]5 !RVՂM۔\@YA;em N)APbGH>/A;-ap*~36R뱤O0U@)[([UG@JW~&R*7[%svEs;m斳btCt.򾡑4i{mHR[D2Hmf#zػU^+[_vt!t3bֹ h+Ghtς\O?po'&L,#u^[)C7%lw 5Gǀ`QcԼRSDG @݃>:'G q6(Uwa־f:8*2:U-N0iwD M(=|7ON\xM/QAR+v_-B51RQyrnyK#]c˺ aN-`ПxוX7q ޻;*/{/Qq Msr+טE¶$}ZE1$gOg&]'xp$ݢ5:q!uYU߯"9>7&0E"gݍ(c&=RXC<9T[j_X O0$c>w~z] mBtcƝzd]J $ݱ?mNT2܉IsY[_q0sRLqy /`Hf˹}_&ˬG,>iJ(ߴ@|4VdNLߝ@۹ԠEޟ6ytv#n58Y1u]QޫzZhYFj 'MjJ!{˫TW@ 7>14SJXw˗(2"_ySsHiY9L«}Sy9 ~._:k!6h,S֜D7G\9d AYՖ%"jP}V+8OxbzFFr ۼqv X8 sXZ?Yܮ`'쵲.dj0$|43;ztdLIV*B }P*57e0]tEfEcʧ@t@ !KhW-oR KQe" g+';cdBzWS/9I^>4T;P[(@dwZRL!aaUn}a/?Y-F}[b fBbaR.3{qL-RL4MAl|맷"􁧾2㍄ MML x>0 u*D衜|9w`(~&I8~P pt#՘IL$S0ePWm @\HM;].1m'1GDZBb{ V S<(:g Zx]B4<6U {!"kߕύ2Z]).`LE+Dк͊|@ D:c(HWE mr0j@Kr=)%?V ?x[ba^l}JvшF;ЬA)pAj]iA [N0wM8 ߛRo9Aį@I&V>!vJg7 oB^&i[Q# mêZ9oLBH{7%&U ]U)}t,9. ;|m&d(r2nWaU?ȸvt} eJ1UR7!۲J\gjR'E>n"=88Pbh0(>l vREr!-${B\DuT` #/kB_JĝHqt֥ NgM)ΔГwd|62!%슜^VE_H:8fFF.\%ڿZBrąI졌ɅQU xnSd3󶆏嘶"od&.8K}.S}2$Zg1NjU Ɉt`1}V^v <+۠|zڟpKmj陞>ƉG5Nb ]2y>(N]UxGj KF ۪4˂n,p~2cOHamc-ʉr M%I P}a0o`lS!l`k7hSшV䖍{o ?x /雿4V=AVTo%]㶌%2S =.Ss96=bK[TvWUd֡*c8^0fQt0"%\"(bw{G\N-%*6CrYΈD-Bh1"GLȚm$ApV;}_Ll[{IƼX@R^n<+_Ąo`ك+MX Qr1Z+S7rn@p`CN07cU?;K OFބ#yZXŠEE]րXJ#Ls%B`|7>Bp3>J͔u-@ 鋧AA*,21]xCU\*ys*>u-7X>Koۓcس"nڑȬ) wPMsNq1ѤK(:ZG՘ dqysߏR=;?M[#'\â+f/͈2!jv|,w)j Ft㼧J3:obO؞I09R lb)["`;gDtJ䪾Qz+WplY<~.TG+{`#"Vi~iUq3,UY+rݯsД͚ Icz<˔bSf*僜DA<֔G䨯?7w>oh\ʊ*6┠=%A dH2y'kZzkshE7Lu4сRl\ y~gʫ*S u8&|_ّHQHօC? xcJ>pZ)^ 0a@"J<8qLDVC@ZMA%}mQJP_RKQcg g9$є$:V[\J>Q &3t[O;ұi!q6Ge?Fv T>ZtCVtDSM6hQ^?ù&}j-1DZOBa}# @ΏUs_=v> |%;e϶lu#5]ʶYꯌa"_a7c[[#Y ( Vx)gtL+ ~}}+R5绕} k&#XھD'.` XUAf Dla·s\^<) No Uىpx%[bĥv' $*]hbyeG"[34=.|!YQ ]\{>7mʛ"XC/^ l,)1SN=w 06"%}MSL]-%`^?@T,>Be-Nn+$<%5 6;^8y3v .%3 >''{#_G# n&hʫ~)WBLAdM|ճEP+/V|Kb梇 '}OqٳsHQ{Rӯ 5Kl+`u~bZp w&_$6tz8co;5oy0RѣnLЭʗgTWNO{1tG&(w5#IJ۲[WDIuu jU}s=X6=E)"c~d- Oc|l,4ң+$?Du<9T76qJݒ|]YJ5S҇Hj(XU Rrd_iۊc |y=뱢a2 ,ԿOpdsqӸJ\nӘ)KDRNHCsҞxB;W8e}LA KH7CBa>?dJZ.oV܎S3+s\U k7ъzt䓇gNS/ޡQ M;" rU; <3R "ׂF+ =⹥s#^1܄#}n~h7jIa3w?1UU!'= HJC{ogbNi:pfツi˂>u)q_؎$iޮR&Y_h .HgU\pa&Įe"ۭRܳ2ײHH |Νnr76AʂձnCrRݯNU9u} gYL?YX(a*Ǵ{I\Uw/AGh MZJׅ9Gx̉ÐA ԣTm{gG&S [G8@i`+,njaOmHO4ngBSkl5ŅPOh8 , B>߫h.>SVͻ%8Nߢ#bsp{-[c5[CĂ*o%>hVuڡ~8`[29bvhI{A76*IeZÇ-mF RƶS(_t֔~—Rh4NPcLbيH9 X9+s&v @t^K( 6 c| 7OtPw= / %Qb0ki`zo=hOd{Wh_eVZۜ`rD>jNe^D q%!Pר SO<F; 9O0g}ҫnv9=dm-EU4g!,\ N}Y$ YϩsciHDUHImcsRV0Ӵm/sI>04hSc畔T 8ۆϿq+O imؓYw5K{29~t) RFЙ13P@S ܫoe@-oOw3j6_4 Wӹ WM$#O!"Ć8 i; HD6k|Bd?{SSHxRl(4_w9|].&<)hd*`F>`2"qRR91aQ !Wg!mToLMQ5֮]@Grx=T(h<6('\j&qڮ&qx=ipf:1=wcݴ!7IAY*EWJxBqA 5=Y0VFgJ9Kaoل-ND(OS( ww_ׁ2(Ђ3'b{ FY%R1P-SHWHxE;RS,S7v*pz{l'&D ~-GE< !N֯T teGbɺ{-Aȕt1PZp.lݥLɛ9\Σ;'G]is 9,bMWBz_!oꝵ#8nZ(b5 j'Lu[8ב&uA3U2lJPV_on!>Ѳ&ER; UV{'Ŏy)=0B珡 j(RjKek|Q&E: jw^XHBis@`"E{'E 5m$I !ӓ`ZܿF%CLTދxBb͘~V@Av._>[#jލ3{8Ѭb2@n K΄s%L+efY.vrM .\20Ec =2=c_%/W &)mu65[a(֑s:kR+H6964 [}bڈBmOҚTcE1dΨʍ*-)bo {!glKeye+ )B: a襵SMq8t_t]{"OE> >PzM}7jkEg7+v} [sbn/ˣC`/(AkIcF1?ul]SY*EʸPo<ݩXyᝅ|*yns6/B.LBq*.\ @D'0#(7[Heud4:Q,zdYt!Hglg #Ni۶n[`FxvC1j=#ęYq^BhA0k7 Irm-K4cfVQtFl:K21ƀis3_&ᜡ/bX mKo'&7yd3s+Fl5E&Ep˚FI9PTW~ņ%ܭ,m,݆6 V = 2Y[lד6~]hJ6@T5N_Dz4cHSQ]pΪ$dwcE"-&5+vL뱫;{9n&0bƃB7-:ѻC7͇ǒVs9p뚕um&8G|-Giu#ZzR|"^e2]2:2}ɡU(Q"U5sUM*H;9kE䎬7j`'XI8C"mn,sWT$1ZU Ps!-x-<D vz!Z˫Ֆ JW0rzb Ov"J eC%5t[A6h333T -u[{Ӽ)N_S~-3O8iM)h5~r^6Xθ 9j Bg5W(jR+J<4Ɵi[NuGt񁜂7y1@'ٛj$Z-foprWo˙3 ʊUN{o|cI;4CvfB͆vJ{e1 :N>?mx1v$&o(zy鮧+LH2&sf=TX' #GC/Eׂ/KU}:~覉]~] KF!'^_`%2f/:2˜K(qνhsH6ޭ̟ORu v,E(`zVl%" bt5{'kea۠QLgVRTt5>Wߝ-גhT=~C]Nۏ$E-N])S[(!p%7+9OFԧISg0lFySNJ GZ V 4m,E=DZ! JCeUD&'{#[o|Kշno@4N njpcK\ڳt%zXN֏ĨcaCf&_u3z-d!Xf.1'pj^-cvG# 'lqީV%YX]5HqW%DQ.мѩihXƴ{MwT&1NEV@V{tRipr,'ݺMKd l2&Ւ+@XI:K4uuEЊ )n[CQCB:AU$ RauH_n,WA [bgSlIT+h:0S 5\1 V$$mb,EV֡'H͚5[G]ToR8 |>0b@OBJ÷ȐIћ)!yh<wx =Lc,:) ~BҁC5kڞ?Fx1Q(9S/@RZ6#rpCXa&z &M0X2,g[\}ZE;jZȔ¾M5t:<#uZ@D#bab6V/˗x+ =}'Aݘ6z홼K5S TjqępׁGR F> {&1Cz8]w DSZY75:Hݫ6-GC~>;\/j~Z{ ō|bS81fU+BLԘK&A<8 o߾w7vWJypE8nEVa{$>IʞaN?]jC(Uk]ؘ)}qI(VM$W0?P7WZIYq. ŴKyvRcTI{თPs sumT>T~{p-sa(o A˫ \}kl4/2rOO."+*?=S;,O, M/;WiSZˢ=j:u:@Hk,t}ZCaWKeߎ1sBYtFm.Z>0s!7_"E>JI0-P0&,0t!;.x e~=O8uಝݞ '1mAFU$0Z&Ȓr!g*Rt/#8t֙][ fS/p9Cȼ?RV|3HР zu5a6b->)hIZ+Μvb|716P P} C9W ۬|,y~/@ hѷAL72&tW)L*JCl-Hg,Pw KwZ>s4ddaq>#hel ^Bm S GN`-wx:=p1lp8©waW3pcyzzT>9568]etw|XXCfy%UMOGc\y[.`.5=4x+57Kے^䎟X; -")olJfR#}p$ ^SqUc# kIВ f`s uiZɥM>{rF`4v7AԪP"f)ħb~/R#ݤZ+oٙL{pS+4F Ah'G2I{7)C-KHL掯6! 4ra;K `b!f?ym먤w?BN\לx60mPM$o)x+#=&S@WeȤIvuŜg@e0@ͪ<=xNP&p|Jh.6w 51rϬW!x:׊s2ћ#275NVr*z5]Oue,ew0:l$W:=TvA͸S8 xEdLjKͧS2oj0&" :.[4 %B*ˈR]V kyޭ)md8ٿA!-6C\dɡChYj(G2sR|έA-v^4fl͂ ~eMlnDB{Gy!#.O&S3=1r VkyPB;!EWnHQ#1`#K*yɪf[ 8u88ڃe[&v{Rp7FƮ8x"v` o ݢA^OQ>ʨjwG9]*:rDv'"yqQfRnO]OovjbH]^<) ioG#¹Ũ9޾!B67Է6A65Zyq-,=saՎ`ӛ5'jdYibzip>f!rS1P7T|EZ'SwIQ۱yxG#ĩ=Ϊɑ @#q:/`"iڗ޸׮dd^Їz+>hT0w5΢iYO7N-y#Lv7gbZMGK TcpԦ4Zp6tП ]wb{Mo{Ӷ|TxSY|-rQklY|:ue[8akXb޷,) =UU`&@' ?+KH\Smv4x2Jp_qQx´2jH@l%5&$~ij:'(i.jqўäx9{Udү`D v5dj~Zf 7~D [WQmv/\ߪ?~w(a+0Gz7 eW{DҟQ!}^ GeBZ96gp2Q8_BKc!|B/9v75_s=BG7Sb7b;645Y3Pm=DYNt|eq.fO_qߛeJ@$g PQ!M.VFb\# {s9 w*ߓ_]ZOv7Umtyl(C}ƭU4CLON"~A(_Qti1gF R_ں=X }4rKet8[vYw9Յm^M;ߚ&N)=kkbSB[Na C* rAWk %Rxb",H#L\ݴ6|dSHJ35쓚/Ca6C{$l"W*uh? NP4n4WFn05[l&n V*I&W}ӰQlOCj ȧwΰN}V$a[}f+k؄4erIMh=[Cc!1)଱G'EFtF2| ;ABΑeEFDF8dmNu6:e&8Z1+)jcZq-;ȋLhW#{1:G_nĪUj F;Z;6aXp{gPW $[|4k +I^YF N,b k'u=Q. vFu\&ayi!AF'J><%QoMV8ZruʷDx*]#DH0;Az`gOIE]odNRq}`8qS0e1FMĮ#kn6bIn"1)K"śV c]?gk. ]:T;IK+[cŃhBWr4.d&y̱/j;k _wcʃC5;bJbk{C_ ypĿqHX-:(z#Q7-i8<>mr({THWRIwx (TX5Ӻɹ&K{ns;H!i_8e^ 1ΆU&eCj C7BMX/RIV@Fh5 -3gpw1:OQ7d5$XqlryʙT$gÿK[N`|zg`9b`7c:ZQJB.ia}pMfR8) |mj=@S.?2Ne*y;G!:?Ef%B;%{3.ܿS[e  bGtOST;d0Gކf9CemkO*>M%As"%̞B^̜RB?N> PlJ4-IxH4 +1-gkj%5(QDx4n|KNS m$>:3<0uΩ{}^U1F#똤c(rt?n S`);@M]o`[4" :M)Lu'${Obh@TFOdPd}${W[QEc3/9H.n}={T|߽PPdEVt fޚ~@XS3Y]lD\a K~DNm ǣߵUQ6VP=c>e(,6jC48VHPVAec3*{mΘ QlUSD]<ه{c5.CH4Uh_㡜 wWDx ?p7${ ; C(,\oQŻ~^7>jz~Fw -E<$g2d2#__`8t.X|>y!S^ʧ]t~Ä`PiՕܳ6N. ߳"T8$ p4_1v+9фFqI},#UTXeYEi%8'pr:nXF 2\O~W*ZvSAFr.2vOLsLl=#߲֗Wh_;j<ZVEM;n36E䛾emk ZMYuLBRZnul0Zz<[@xIw*etXFsDI!&9qݍb\?@d"M2Gr!a cDQtFVIX(fi*oḄ$'HSʎ0#["gǙԔ,e FM*n~I GpkK[2/=yM6 O\JQK"Lo%w?䉡/0&IKifj%n+)G4MJ36l立1)_>vbg;{P"N5 ;"BxS,zw(G2 9:Np&o"K}Q^8Pu\ֹ׭h ,;;W؀_5@shw}ڋQ*0_9Ÿ Hyaժ6|\񹅥k^}7n@4Տck+y$I9S ca s)ψX@RHH04!753ExOl,w9ۑ /6h ARϐa{"l R3s}f|&y;i6Vp3 4,LޝZ0_SILђ)ř$Iu[Qg H%d]ᮑR{WY( C.Y^,MYWnkؘ**pFfSKq<0z {IԋVSp*z )cq=Ѭٚ,KүB (Fhc%s]:RXxΩt[qo@j`Y&x'rA/E!BGiUV`$~y,a,@Qk{Ҷ\{P{='}~*c t cș{+] &[$u f/T 8礂^,2fPG7z)wa T !۰b.,H݅34 4z#EɋUcd<0V9N,X7NFRwlbAv7Y=JBa/pȁ}uWP=~T >YW[k186BhݱWK<>}#qu3^y\'{ۈ| p61>f-34ᬉݱDwytzK A&k ѡs4\ NTy} ciԵ,C"6Iy*ݶoKUǸUq@\.E J3VQ||bSԏ/IW@kPTk0<ӟ&;MY!E1hMw4EO`P\P^'?Zg!vjz\>limO<Ys +"X\AI)â1Ib_/It+dAWn]MOJAsVt{}m*R&{eA=OYZ:[~ݤ){%\PI!xo2O͈},9#qw7~WjX¨s5bRN.b%co*Pe8rZz#Hb m3׆Ĺw?Kbc3  aE|_tхspj" R¿~ռAo,ݍr+]zwt\hNTohdƍRķYnsXvX0i NFa2oM@Q0k:3~?XNC/Ѩ~I ÊBa13 *w;HQ5;z٧NwJPJ0 :%C qB44jYgutR՞Ϝ飲5@@,fؖnmבyt[Km[ɖA*&o}`Ǝ"KyA^|7Rldx.blRIl!WڹͩCVXĹ2mAŻ} Irnv=]J3*n/Nl-b_orw,7cOws 'WɰP襢D[I+H q"T9Z d{X9_/L2{$>uJzG M0͵'2G'Ǯ1H% LxTі{5.~XoB /&wBWq^}:F|ڊL -e-̂nL2 (u1/6ar@y05E>+JI;n<=/Q~{b4}׺̓Sܹ֝rjb˸{;G,FQg<w)vu[Y Byv< E[}Fɯ͂ݡhhկUs&@ޯ\[w_{ۏ0iZhՔ+ `jg3ÄL %G Pth;e2Cf}[ 'X!U]Q{/ a{F\oʹD^|!v1p ;zU 7pkڥSە=U 8z%X)]<ʱdM Yzjjs*WX*E|8} g#+ee-}CNYW(,jTe[Uzq:Vg[͘v1inN?jU[yS|Z/L\I')_Wzboh%ClI|ƮDŠr'*i!2΅b46p]_GÊֻ,!o'<"Ysӳ;o8ݮšBaCP_k\U= 't_#w r(OoZ@~_I샗W!%V1Ykְ=eVcp~^#mTv7e{ hanhnfMo.wFS=2lg^U IcsOE\Pf-䰝7¥'GpYgZ+p:7OB3\Z/͌ #+gĎ,N^lȵL.#{1﹟[˼әx;'B.=-H\ύ 2I#G\Z@LJGB[y M- lǰi6j֚z^pmnl@~@@^8l>" ΝPiyC70AaO>!?N=CUIBz#l v1n3;Yc ;Ƴ\b\KW;jKT$QOv$}8OCyM0CkЧ!@&4tӻDC-ZBUgTu2-[WJQvWK׽*qF 15ը(_L$E5kG)WzI]8Dcf>\bm4>;GmQU6+,']1ޛékLd/R{3zqD*nM`5ӸձVf i}<.ɷ2Uu}-Uaû3>Pj3 D&dL?RKJco.:='S ;=1>IwWIc "Yqs9}MXR f} 2[ԍQ3єC:X 1_?!X f/E1^~ N#: ojy.A7tLc? Lv U{\tX]C57sQ_D4}@CZ?G.9Ot++l/94Q]RV n*Wqv/vv; !ZCHDoEvL,ه{7k.W^&*I8ZV vE;#DɫME:ka `^Ⲵ,L5fiq5 88y_ 39 3WVPlʙvw x{b1 ݲsС N,rjGK!ʬt^~EWT}O٭]WL4%/JFQ*8S~/5*א;ƗaCۨL`?$X5ΞY݆}82A%*a/ q|3(4kK}QBS?kUJ)3:rBvqojEmAa~AIJH]!1ҊO1ulC2G48+Ҏ!9QHx{`N;@Zם(&lX#)+&PÝJe5ɏ^EܦbXVmdͳOD+FoݐagѦF#/x EE }gXMɦf!,ʩ:` kPְH< $O@MlM[<}^ψoΒ z`ɢ!ir7a;}r7w1!\CAzRH~@Ĺ뇏Lin9!**&-xlgCS S!X._kpٻetC%Z4DS"Ş5ATUZrRT-;Y>gM2@:bdJl^.⚗izʻDJb!  $6Igd5_'Uώ5f hS){̛QG4yv+X%BWRc`R !M=PLSLQG0Q_T4c֏9xExCh\ǖw'w1~\dxl`d­fG ip keux9 k(v=_]aAބ'f7G;?o&6^y+p<;H% I4{&>03'm2dmNDjYK0Z+־jP0ѐi!p,A\<;DֻX\4Ir0?]D>X}2ĝd6%9ൡߕ>WThK<;5xKPt+(?њ9T9y>"|$\ Ý?@r WފE 3hʗA3ґ3G!\=.Q(o!N7sK* {SQͦ7҄׬q$~_-x ,HkT ><*ȎYLs/n^'񔄈A~bo~k_0lΡ'Kze`0\|bVϐ8j-)+kOpi6 : `xBtVeҊi N[]OI˹x阬Lo"ZCO3`Bn'Өoj^p)`ӭ/3y@B_$vU1'/&` +#jQ PR^9Mz/N_OojW 4 4fa.~@.SL.[:{u\N-_f;X;2]Q>[c)k\\XkFӝu")Ac^1R6P4oVmܙK;Oj^Av-o@e,C7SdL$soKm)yU:4ZNx2U G',qXRMuMI}.2{$:(籃֪DmSnx벃|75|ؘijQ>RO-s$0 bq}cs(ۗIxWPcdl+\g[Vd`š; N$_!̀|= @7K~1vL ==;S5Ϋ)G]?u~JMHg˕8r#jjkY!C8$ 2 +c8b5{; +}Lz}-M2h1u3"62#Xr#K 1f.*H3'sgC2ĭ|UY#5Nsy(.M:c=qfn10~-=zi5W@pXq嘭k^$ZZq$x~%xDRW-e{Dʼdq F\9}>d~IֈjwoR`Ek">t9-z-u02B.COW+}af O0BL2:Af~%/kAR&M1^pk'sTa>iu$j4:<IaT2) "j l(&'s1|yֈF ʷ:Q2S3! ?HV\( |i?*ﰌ% 4T:gJhYZ}su<LL%zG"%tl=tSĊ6qgo=Y&EyRJ [v*>mjH$RJ![-Y+،W/p_} *rwm Mwz"F2`scw:^^"_m0RkHR_95pyZT ^ ;$C>dkH*7c$N Zg].V's8w`c&d=+ Lcq9~(-'|q31qݰ+UBǪɿx1'w6.=:EJ/ $ۙ# ,U_WcA Iqoˁr|px|ϣ^ ث?6d-ko핐.4 C7ѐt:|S~ 9/1e30ux4oAPv489jN3'+Zh>ԇ|_6_,z˕܄ihe-kl4nU'@Hg?GTs ~^ ŋXnC!i@BòS*62gP|MJVѹfgT=&M:namBPY_o'ΰ)¦AuiE&y0Rkݮ?i,}s;ӐWcT*`t"2Hn;umd#"k A/\p6'BCK:92(m0|9!u͈V)Rċ}ϕKr^qF=NF0//Ihn[ʷ660])FӫşvMq{DntFxS>psP)Zl`uPq/s "*O_5!ց6{\QtfLlozh{]mjk t-"oߗ{~XF_ID?yE\V)S!WCB9մ1+O5w]=09 S 4Aƨ?99Zc?ct$ͷ>Df>s,AAWUDMӶv AC_.P6n*$ af@݀L*Dٲݿe9i<: e#Br?&BgHDi{O~#+]5tFiڡ{'3𴥒Q}4fNZ)Q5f>9/X||HE{JZyW C;aUr 3j@\ĮN(~N AnD#h'q!faEئ6ɮא>Ȭq7p_ Xu6}Pt qzgpJˋKBdF^/)C=dw` "8ȁ.#@/b-(av<3ZƲ]bfNC]Bc+nW7P̛n'Txn1PNANC3\LZG-cc#A#H:2KIpfU?=Ȕo8i%=؀9YK"C|u3,3 ҋUWWw.na0Q}) D ^Ee[ 73ѓRe,iPZ(/JW߆NGYIΙOQ^ 2ݝWQƵO|TC؈)ӖK >*bxQֶth#MX$"eS}2{VPjDC΍􎝫..v–ͩf\h)KJ[lXȨPMQtŋ^Ha z s+/z_ yjRiG2jh,LYl0@[jT uuPjo6uL/ILd;ILԩyHsM l UdZ ,bae6d+k[7jUk>vU3X1[UO\>0r“J&:++-"E}xԼJseKrt0JLp.Ԧ`s"@0RȂ!Q;vmb9h~i@o<'Pq%UCrs{ 󽦍Mxl r1O "[\#>G6ExQ ۖD3ej9'3ͨʤfnDs-)ijtj?QŸ{!DBmPGn(WV oZ"H`)k֒ӕ9::)R0K\g bmrbF1~k(#ya6MGp0@fP/] $ 6YD}'tdfVHմՌTFṕ1ܸZtf{ ^ޣ? nNLi&ę6Tʗhld a{F彛oY YT9>a𷐞 #-֛@XD-AM j1IF<MP5WWzo_"q:9HW;Ubdd{&``mKZϧmj5wH Ɉx H9$xe- g⌺ejZ̿)>Ǟ ZMjrw'ZsICWYWr%/{ [:Q:-O1-\JȭofrX6K¢Z+/;bģn7b[XS փ | '=+VNpi闺F*b=nM P#h\O#?蕕;G 4:b+,QʦpvWa%/cç2,#]lz$^.4*N.B>oVl-/IΊʀRL A `:T RjV ϥ2r\\>ޜʁ<,SQ 9G>(< tGlGHfCn*~ALua#`@ƌx]x2ULWJ1gJ-?q >j?V﹧V-W\fW + >6*˨-@{(L fJ+s^C0@Jۻ9걡q#^l=|; vzc0ɴj|>"Vũ]":nI&|̀ /`SْΪxyo<ٯi||VN%8پ˛ eR񏐤2) ͙Qy1ϩ Z`A/kVr@;efq_$ gpK JUFMN= H:Zcx,޵O,d?pVZ!Ax)7H[sR6,p󍷢cGa ҅y)Ql2ե"}ebHCk#yɼle-ǰgl2n2~\onJMICNj'7>ŁKrK?\єzZrEw|]ت~zᨼHzѕJ-VCZdd8afr< ͅdA%̗-mH3"1Bfs"n\2A??e*p9ɨ ^jz(G `rp8BY5Bp Ԙ~/a<$|7 {bp5퉊=rjj̹)34ѵѦ٪*[J/ *pv%ڶʹ0A.޸!팒 jdy6vWA1Jw+V9%,BA_L {4Ÿьcdzٽp J;O|lm ojeNS"WSLuOL2wT/À_X 8QXNF@<ƒ}2m=^@$ 7=D%Bs5W.FnODMoIz{ɮG{z3&P(QX&H5xd~g5*M3: OUBu9kJIY4O S~Y z"KTcĹ<$,X_ 7z'v}cl*i +;c< {^g=/H!K\[kYh=61$XWR܉=?>Π3< g.FezM6s`9'rS|lxJ9g,\ICJ[ۓEbz!L32"?2 ,}#{&Iw-]F aQ Sst%)OvgM[p^w 0h"M6!+ ]:=f!^U3QɪZvƨar+{:+|VIB

fU]P' CKc9eB!%e@Pa=pqF`4|"=Fͤ-#u<94A"B zIM>5X)lM6]WY+ed)]4r5ိ]5ɡiK;y*͞Y Dъc x6%FkW&r_/ *i, N$9q; k}ଦL0e6J^y֊}v&)XDH \nd'n5/O).хGڕ--]lASTbPV >MRkL8fHy4^#E%~%001()X[ۨ3Y3͡DVV'u9sj#YbHKҵ5FBOXΈqm)X)m9 Gu/({ & Π2K'cPl~l:UScp%P:O$EpFoC30|C^j5mPqjxJuo?"йJxr_sP- % Ypb90D%5|GVϩsJ "˛5YQZ"SV#wY%Ko+?3X\ $J$5`o@T ޾QXX<gGeRCTzѳ*:[?Hb$i*h2I:ԓ1;h, [ {Ks4Gg:/Xf 5<Z13?3<YтM Ue₿(,mLEL}2k4y :64-vd{Z} `N<..t&.3d;$gpR'W?&܏e+-cgJOM&D^wݎ2ѯ6Ժ$g `)i)ު|o~q!NZS]}f̱/AoIJ|7X;ITb?kxs~\K84w{h#N1Kh(b~qhy8=]ό{%VA{\2'>X#N;_.^{c ChG i:5b޴!JB~bH7.#!Ja/~&$/{KLQbz(<+͑~?ȣj교m߱6n61ZhQT1T",6_9(3XFo:IXc^?ȡ-֭pū(Z kF=ggvt# cI8p3?z ND߼b)K]Yi4n}U_[ K㓗bbtN\Y:-:ˌ[)]@9:՗mMGO s*n"uٸ3ϴ`hC׍/:bB>ICrLA,V5 DJ;G49)kn0W߃:[`U9ܸi> G:LZBe* G\\)\cّ2"sûz3Y¤DJ s⣌MHwcuf[ WO?$0+0q )I5YnH׏P6o =6r/,Cj]dI| ߙWk%C"/$μ(į*VXa٧ބ0k'V؎/gW]⁵1g `4bO0h!3O74ocBSrШ8%Sݵyz.U T㾻's!tƐT}FL|w_tHiLܡc W+ϗȢ}f\sdls=aZ?tT[q%q7&tG*wjד,BIx #lWIH[6,UqQ?O1 Y#ȅNr4.I4gxs5xd#܌^7G"oi,(F խ9qx±^?mamVHiJB 곱=ރwE'[q}DB#ͪ=ˣ*T{^d~&Dr#}9l?gn5]>^h|k 9T"c'fm ۲<,{L}`wZzE݆(@yH-ӷHޣ} >Жdv 6P|w)XE6h_vUA5>J S"Eխ"7P0H;8 *VPO- RGwq=?5=$Bh1M4u,3g_aeP z.p4пa<^w ڎ怄q,ܡB(-&ž]&D;E6 j$3wl]mw#H Z]sݤ.[c+?^QPSi!~`L nvϭ%.+v1{ýoKwM~^AKwd&$Յ7EޔƱ]H >$(٪@*I"I9δmY_-ۼ$SgDqSJ𢑋iQ` XKo87a0!On(WT\eziXRbq/?'mnJPWdY[y_tl,$~hi G97!ZvepA̰7ĬAҍIu>zH|++!ϫ3Rχj*y,^њZr mg4#JCوf}a&D4 蟷Ư''Pʅ}Jn0G8tû L"rZ"@e;pc4t1mx՟N+;’{٭vu{dGdL@.P@aVz9E Wjfʋ߄VC{C R1fN-w[hYoxgVkjnf*rvےno+ISNErzVfxg̢ ZSΒDDw;j)ߍULyFsz?s6ob'=^FGR/A;-apԙ*~36R뱤O0U@)[([UG@JW~&}R*7[%svEs;m斳btt.򾡑4i{c-HR[D2Hmf#yzػ^+[_vt!t3b _GѠW`c>HIQ2rOMlYFdYR:6o&Sjnzym~!' ˈ! V-;}QuNV4Aݩ>lFQ.C+¬}tpUdtPIaҔAPzoV`N\xM/QARv_-B51RQyrny{#]cK aN-` ПxוX7q ;۹[*//Qq GMrטE¶$}ZE1$gOg&]xp$ݢ5+:q!u;YU߯"9>7&0E"gݍ+(c&=R,! zczy-lr`ҍ/iPXFar;F?_`Utw~Xks1NJ=l\XEFh%f@u^6UQCdtogE_G$ιUĬ[?9)Ҽ|T/fӝd5x4|yR{->,jW&r2{[)lK.n vqy(թ'^Z(bVЃA1KVRS ȡ5*8P@|,}R3`LDU2WC_GʞK_*V.F ,hFWsD؁o vt8M 4VҒHz|8=v0WO*z~NB!4nR85&@"o ?5cnw ɧ[~M2H|Y;1cO\Ld$Վ'ΨfS1!}/2XkMt\nD͊ڎDg\BˡB-pig/.!c@MʧA ͩɹM{9S%WYU>oSw)L&s[d҆ /v T| 51i1Ċ|`̈́ ߕ཯eh>jdXdQ"m|ʏ1BbbCdkd3d>a= `ZzKO7q^\D s}8ݯ ) CRP%+9.a\W2%ZdH7VLի>l'Vt$>eu:mě>DA6aԲ!yF) µ?j;E!;+mќrJ.bkARV?uB4\zkbv!mI!.D ##5]p| ~R:4 >XZ Ci1`rAew'w+s F HvDLޓJ 棛݆] Wcġ٧ߏ 齑Yai+c$قWipÝIxf#6$BR`sA 9[_;y#3@>*xs*cڷV} +4zmUps{6ݗjbTUb'j Eʜ0Rz*%˷,"=P0 H-`}K^u^Vmxv = ͆ qmXGU1* F|Iџ{+lɕpt9^-3]MOʙr7agB ' S}lM1;|/!r ǒ.z\ٚ hUTM옽pblrΜ?yEgoBF1#KwoL9D~ͼx;dD׮snނT0*uVxf~C "UEf^_Xdx1\ew.Sg 6D2xM̈@g|/Vt]M M0Zsq|De_STdį,$TgBa=fls2M[5E~rBĠZqZ,H7}?S/Grd&2Ev Zw1/ ƻZ=iQQ[L')S2$o]Xrg"Q[f{SIjaUϻ1.,CY C"Q)N1{|g7xNy[N )[ͱ+02HlDɴЧc{8D-3Clj/ȶw̋o>L_ wIvdkXX^]a5~HȜ}aFcbFctu]rQ5*{OzM0C={*?w4̪X1nHV~FPOP&(j@/%郌sP`P<DF #_nxzJ}s_֙L Et9`م Uk yeN_Qո3.c="߼+@OkXOVzs fD %{v2#B 46#J s^"/7lCi61pY7La J h;eC]D3tsթKQO$sz$Ctᝍ":JwR2TqUb4bځM&vjVQT{ nK85ƿSoI5K?*Go'UbKΣzgԙ5ئKI/y/s#He(0$D[ՔY^~,{puP>q7(*Ô ^ҿ'AHi&O`O-L' Q߃>v&@T[Om:Cfje6W Iэq TPak/ c)>?-=P0CL0)ڋ-ps jOR_[ E)i%ijP ss {.5mCl>V(d2<myFK<%G6qS`J17Hw>]TG1~L-PEU{2_cZF--y!C_^Hx; Ճ#g^7>YpGnӔc0WVݩ6ͥC"} c$E4mxbJ+J8_D5~Js^j?y(#+ֳTLk2 ;v7r:d Z耋@4x f"h6CI%:zs9#0@:|+SDx!pHz)P641r@K l) DKkEak2qzho0ThMi#_IyN_ H bYO`ٸ8< 0`I ɽ[߈,(Յd[BCLd |V;D@|z!@NgȱP|73!r$~Jdf]`P|tcj;$,Cָ'yaDľs<r]{2\*,g|"NOĪo{3WQ컧KֵmKm5i, KbBZe;jG.Pт @ tФ|!-S7a=Nqƥ$9J$!vjJ$`]+ϼ/J,}ZPh C)U:[nE׶x_52$vҤ-'|;DE':8.uH-eQI)I ; /e{rswL N>CK |KTͳw% e=HaaGj}}گ/m)#1S96%訖LVZ/Dy&'ܛ**Ar8veqw0/*%Qv:5u t0 mm u/naZX-H2-Gvw,3f.W%LA ĕē^o67:u0A|: Dz{@ q}@008ڧZfG6QvԮ`{z6&Wpx(CܗXe̮RX2EK׮jJ_PzE'ˋQF'k'3.Yn@Y-K@;4"lK J9I·;<=;,iC=wb͋~om;WlVNӜH1.X x0iH؃#)8aXuZ*Lr0@ْOq 7$'o㇉m펨*9!\\f ط\s%졄HϦm귱sVZ18f )hz 7(R-㐳$ z2FHR'l{ %qaHX,Geih3U:w5.*AٖĠ[Y;?fmwo|rXrGXٲR ~ `묪gUԆ ;2{y({])Ŋ W|XY6SFRQa.gUE9ҋ 4yOQ}np̐"j%xEӻ EOK1E9mN'fceKc qS{NKt#!{f(>zY󻔇:K!ko RRl&6c`'H͊=Fm`ԔܺB-s2Z S/ES|3];&BC-Iw{"2UC^WM#*9oEϷHp_ȸ(A:\j l%6֤逸QdWP7DjsWBZmVeCw[*Nd6?bJq]_hXNF' C/}x ojz;--3HrW^ľE2:Af:&z5k;)A&2Q%nXNm(o)ѸSoʇg^ᕌ}>] BR!,' γtVkLzzCq4p,K98֬ 1Τ苰™!+8`&6d}l!C~D2)'fliN{׿̷=^ BwO?¾jD̸oͿc8s Ѧk^fnŧZK48%\b20Ŗyq!їw-@)`gx4 O*XngL(S(eD֣wKSJw:o#m%HՓuOGشTp?g BgaOEY.5ɨŎBJS+xtKӟ+~ &؍a+iXi3aZ]H% ys>x !6I6Ҥ*v[v= ba5=c5Y'mWπ ueg>4<͟XMLsyVƁ*`B{|b G:(b)F:FE^y}2J/8ތjIF8Z̊R1mb4m2I8ـn%4oBk^4m4^|JED* ງ2Y|r,4d2v%`5$eK &= jT!b4]eq=XM|?{K<"jNj!϶Aפ#usmt&s?kFzu_PI)B[dy*Vc>0Kۗԝrf~ݛ cf /tCrqLnj̣qJYap%R 0ZSiѪ'bn6"b%47pW80e̬zEvSGo?? ےc+o_@^Eƞ9oO ,Vn3Z}"J$:p5,&ωR0~7>5F]{uAk;@sW}VGKP ms Q͖{9োh!5ɖ*1*4ߺk 9[Eb޼n,ZAVM[FJ]w &sX cH`4>4,柲^qB˅->3=|ω3wjB6x!SXN+n _8*R!jk 5gzB? `BBeM ,DUP$RfwVE|l. ,9d0 Ӑ| ~&m[qSWI%ʆc@Ъ R@Wp#L<XmŀC(&xq"ԚZIRgpjٚ2ʄ̓X7~{gEٿ{v3; vɔBo0e<#@ =ds1pHBfOVqUQ}QWN'S-DlƫQE> M,.E(zD);w^y~ +-VKAe@dFÕr4:Pz6 $M8{i\\ ?1[/T^эF+P en{ҺwpEq Ҭ־kV^nkZRp\V< (ܞ.6E$);5gKXrmѱd9rxx(7 -M8/V3Μa0T)\H 7vOatJKy\\@/ ,H1\n^f iāFmjH1p>kI-YU:FR8#Cpjqr۱8 5Mne)CZƣ.@cW:4H7|~=I=B%>bݚRC5pAd(~eW5FXwұ{FzJmyv"o)@~wPne 6yNl ުt < J$4r1|T5$e2r㙝vpt o 8*x d2^z| 6QމdD? u9M82l[NQ%#T `*QjaUo y-0L{4낶`ԮnB 漽.L,3Cs"r!|yD+ώFk$~&COS4J|̱" Zd;UhBg>~T ߔV8@gS kg[]p *ř4R\;I)ItظG,&6ﲾP_Ƴkҹ_r^?25{X&Kب7@ DJ }#nJM:嶇@<03Q`S">c 7Ve0 @&+/] La)D1=B1t278]Z~G4~,_\`pB4oEWJCJ$dMLteٿI<^d{= CmGNFahI** &0>G7ȝ6ԲwNZZ͡RПQ ys]q؃Ir!\*3ꆓmj^=<0t}x#{l iy#}5SCz%XO~opWdT*,ZOz_,e\ߌ !Qԯݍ[Kޅ]w\ȧP׉V%f惻!S荶_+^x^M %F' #u,DR`*rfB/-ņGhBI=ۏ`rL"MR;8[. m^lmF]]|Ze߳9nOF>Ul P ;0œ8MK@kejp}h=Aka-_KeؙȂ\)e*3mi*$Ai۲ ݓA1ݳGX:MrL #k-LmjF2lSz^G hs+g=y"7Qmp86* ǘO#*l7M~Zj[w"EĔZ? c]Lfs+WX͈)5#>&yf~UaD黓vn\Jļ 42Be06*bUeB9F\#{/dT$u}K62G}pcyn^&iIS(X{Pyw}ST ,~*X7U~h.YK!H^1Hڧh`'+lvje$K mi/N1yʰp%`hr)s7o ptdU2q"vqqAuZ]34}UoFIDBZkÕLLFp}v5mف/14T 0rcA$,QG(zSMV~ &t` )~s "S4{1cLm^9C&yHZTFAqP>K Xd5 fD#`ZC矷~‚|)~mG#{j\ć|M =ejHӜWHSR=p"Bџn-=]z װeerׄvVz]O kX Il{U,ywJj"Bg~[i2*5uvK}!e :*|#47tq,R \NUJmKL ů;5xY1R)MG,mq9|Mf;FXpo}ɋ6/Tv$T:Y{Ȧ$gnkކ}ekHP!|'ϗa%pμB+ʨFFH=l,! !"rY.>#WakXtfo {٥R$7 # tjbF8:P-`ދΜP0T{H}xZ ns. 6Ek;7ck{ɟ,ww2uwrD~oY.Ֆ/wAb]JoHU];,N_ݧ.n#o+cz{YdUrݔ]FBڈra9TI%ʼnNc/(֡߷G(JFD4x׫ZNú:‚&/` s`h?uSꈿ傲ƈxq$Px^v4`Jߜ`Ќ5X$PdA> YIb?6U e )-z65o` C"9L7X~(x9_t"ul tE*XVt(5wɦ_SuߙnEy+rنb+_5Cm_akh,/P'7z C%b[Э(E}r4Fes`txPn%b:dkC݄{b[kR| <+HjHpZN9SeXw"gYT" ܙe^QՀ򒢺&]Gr% J@sMU1 b K#uWvlFv[XDh Dj#SuItǐc;/;S\*Njxܒ_sƣBGN,t&dK{9pK!tYCp`2Ά [ `~ Tiǣ&+_KB< GLYB2;l.3r+BLk8v8yNedT@\K_!^Vjg-adVĆLyDpu`cGWsk)H Y^9޽)LqPb]<^X] (?2 ˙RyOr#^s򋚑qz0gGm)k$fV% w)Nr 1| q BX_o`QʒF^! Ƥ;ˬt_._-^3حPs`#B+q!= M=K[VjnmEʚO8$R0Ƃg#-M`K8ǵQ <[_"i_Pe< .(1h@tjpOΟDV6F~E7|Xf$ʀ䱰D ǀoϞ1ԚU+am_09 ql%{=#^5OHQ1+ZH0H ͻf~idyh];C1WLʥ9c'ͳ8z~!vS|qoЍ.CC ,q8>i4:P{gkS6Q0='}T&}o q0S.7r7G%gpzWtDDTͥjpҬn:z2sN!=t_-,xq^멪![ZBq|,\”)r|*0, ǻB Ȭڭq7sclF?b*3%7(-7QiQ+hX6 2n&̺tLiZP*㣍eP8 LP@ee_֪UpQy>~ Lеc4-wdDY&@d71#ҞYOFq+ ^D7KP~*ؾ~Qa;zH;Iw$74MN"h7߯^/;IA_Ո;L<% (y=S @m)qE~\NnjK9 as2FTީ HP`pHcab͡V\"xBgiJv3"\2,^s7љ  0PM~ [5gBؖc w b%q‹鬦%iz:Gf0]ZAƟ,_TZ:[Pp``XA.磼sL϶u?pQ"/j^>@TL_Obm;2|,7OÑ \SEBٛ~x =rR!RgS٘}"?ZUؑw4ζBu/ F:q\DjkKj=4Q |v7Il}Zx 29BJZv6 Z^1<%APܩL\.+4r!D$_ 8#gvbt0ocoWL6dş& Z`7D]*Ԛ%5(f-S>~ՒD#cH)$OŁ3!k].g.T\RIC;XKk2n{B꼷nJ)J.CT%ϐG63I<4*#m~k^GgiafŴ9>gD@y|s62V [bMX":B[S)jenLʸw,h?*8peԵΝ\YF֤QdЪB O lRiF<d724D^4wWjJ|ّBrP2IFN.i2+*qP<:,Wk3K.| ͥL1V])-rIH.UF /,Jb 498`w]e?v5'- ӽV[:k&-]F9yߊxֈ f$T4pu?.<3I1D jJޓd_$Dfu{9TuUP8ZE3+CPNC"ۜ.9QB/=!{}LV;fTL 8_FZP=}65 >wt̛ݥ9SL>EGG:<rftF,֝/5w'.7b5aQhCiUw1)BeW.|.MFFvvΥ;b=UMzZӤ?߫8֗ ,TvvR ?RNGv3KNK;J\l(hONa:)8>^* m7ojSOfT#ԍB`s7&=@J)kΌy"@=wpp&j/M:)zf|9J{lQ7DBbDrAzKn(]9490 \~bDDFP ̦'9{(\`EN˴A»/ T^Mkn^\5Ĩ GjIܭ|=U@}m0)->ĵ6gT#ܡ.p;"yAHIh}o8{X h Օ7 wtӐMs:dyNG4Bs"yA+t^_Q-2I((\E'6?k~Kp B?a)x,NߒۘW61 `Fj!Xgy\I7kq&s8*o޳n?iyja@F@W;/46L E)&2EoTqskIִܨ(#٠{@sXgrE f]Xv+~_S%P0@i n_i4 |5g4-^?LPm\rudD?ᾊz @/9:k9H4 ;H?OH+θQ|7A A9φ`%>% k*9 `) "5@$$3X/فS6XKˮEn\,-ԴndefK/F ~P~GY'_=F;r)}j{Vh;v [] hFdDLx8 ҋ"'a[b\ݒկzV$l*[x',Mb^z+ AN!dJ4.2_ +D}Ï)7-K⩿TX)`XGc0-員'rgj~ 1ZȺ/|F**$Yy}>wXON|?!Fh:ÞS]Vjt KR0FJK Q 7H mo! ;Edt44lz_?R)uyF~#h&hD(SRm۷@^ӐMDh_5. 5$G]j; $2㡉) 1&p{CJ7YRE@cZm}[XҤvA"GW$LC3Gp+ֹE":7"QE8ƴ_,D6f{ q`rḓH-á(#K% QwlK*ld͉9!Pr=^=]iq6?s[=NblP {|J]H"@N" B6sکĸFSr69vFߊ";{nfVB zsC@;fmTh8[d[o%pD0 ;WZ*{_r*,bwiX "> ;B6 &=&>r2.05v3$^풿v|$wGC!ۜ5F%mM)uڠ&t/`'d#1:'FTδXv|s9(|Bc'y3\ -fhE])KX5ҦYX >U9#墭3';(iZ:b6JFqv!'~/dZTY*=%Q.|"x SE81̓|Q,@5hcQhBTNIv*̠k(򃲾&M \UH{SVDn!z1? w6pD֢d 3 c۱sZcZS `6LQg?4ɟb? SEj0M\Z 4ڜn|K<͇ڋ9\{Hi'M;q!'tb'}t6v\2|W>]D?=](Vo N- )!hyioR\#Gg˝&v\>r@>eSЈ/w.~a+0T3"5^IJ8ZU0p {$0z?e^ kIuϑ62fF J,Y0\w->h ^Etvo%ZP[݉u}MZ~nswԱS x1UZisn@wՈe,;z5dWdnaא_MJ2@alf&rXPd~.(LLDCCqPb,"rlD;/RiymԿo.骊C @ ,m=mv-e\;JڶL{2M*eϠz,HdzgPA9܇]fkģ$eèzFIr׆pB}+hRT sq\szԶ\pwjF yn2΍WW9*؎^hɆ<ߠ=S֌; <^{3$ dBHyi$D|0D#.` gѕV8_`dFb$-4 sҍԕ'(8,LkR=1Ө8ym`x )dҾLBw7+}0d7D}C%;А^oK^;WrfT[ u>iU-j -ApmX| WeFz6C',OVQ>mLQ)v<5~F,OGk]@Ws;;c6hs>w!&ys(_'*]4`@Man׌C^'Yxȝ(֘Auo[Q3p)3(m^O/@}a͉_LMi}B5?K(hTLwUOj[g2-BAQTc'f=}2euB%o)^սF_-H 2l$Cw{|!:EȩJ, k>KØfCPYخ'tŇVi+.ɱxv~j5&7oq9vF +DŽ A#t!TrxOV]WSO` (sSf%fӱ*SnI 4c?;WxMKЍҲmKU.+L`,A ,bNdSoylbTB'bKW:ˋ(~>7co ;h"kQ{wxb:cשӢO>[&x%3ۡFh'a֞)\nm>y9C ?=?B.㥝йcJ|z#/)9 yALoX~}XB􄖥oLc+<*ZJB|sM> ?L;#AUfdL{5#NQ,"rC`[']C\pyK1]_%W@xK+_.Sޅ.!:& G1NWT-; =̮Oqg -'"(by&z\K=}WB oSM#r#ȚN=ltHoWcK[ -,7^8QdiZHp@ﰩF ?{|y<ґS{o9Rk 4E Wb*} v{vg y0w)k9c02.RɪG\.r oxʥsMoy߁Jor;nFmaD2ď.?uRz'1 E*e?zVtC᧭ cP~΢g3 <$-̻PF^wah`LTQo(P'>qi,ipc 3xB֪R5\NhBnVɢ7~-قR ehA/y[Q6;;?LW,-ԋqe#hImsryUZkpxC@d6QYGEqd ùRHQӦǬ[z eFj&3"bhY; O' e3x[cbĽU(0I7ef4sS5\m0 ;<#01$6]ݲڠe+"牳x6׺Qk ],+(^C nf*(d)3(]szz`X茧b7.3ƽ,ya] tNl`$ }ވ.Xߤ:@[gNyp~ZOR\ >agpOۧtyS;lp#IBKTzEtEc2`2Óf0ʉ`VdYiJu0huSf[a=)2`/$X:#VLp|vkH~!U;n0|_>ni0bV6pb\5X$KpzH_?)`-!URS\O.dq.pz%dtf>". {p"Pލ݃i& g<~Jia^Le^EM'ߞP@Hh/,ݷH"f|t^>l¹ÞvKLBG=/^"Kf#^ n( 6SU<))@[d_ YJ@n~[q&[_-C_m*VӔiAp#I@{H 3*gݰ@ə-$|>25`tSa<aHI9L=k[ vd:< Ej8;偗Ӈ,Tܤo_Ǫٲ|/ԕߕDȨ?@),"*,b6{)"O_;uXyB7XʤO`NKNOO"qSe|!k& 0&ƴBmQD)YߜYV(R|x(,-iG_<,GgՇ~SY2 ofH-M ,CX:.O oV\=} ȧmLi` #JD)hǨp ffP *b-xC i)`Js!̮o&Ce/mGY蹥OkM΃CF< bµAwڶm۶m۶m۶m۶i۞};J\ȨU6usHqx *+9KN3IqVu&bDle\i}5L'8FB]; =u@}bI%/3~s=aGFO)1~%I&gWU7EABf1xkjCg%$H400 -(QWpD@KHu]/n!'ˎ[{尲H? ~7(FT_9sf"ɷ"]طKJ8@DQ5|.%QU^ôq- LBt_Q/f B2@'L\f?~}Mgւ8Jdٌz9։*S|w$7V,\<ؒAwdž1*-Lx0DVPO6h?d;yw>kb9xS\scLʸ8?pUt)`|,{Sb HYT̥ -4"}^]wx'ddpIĉH\lpV/ߗy]G??A6y5GEsJ=/¤`Aܨ+l1/ӭ$pI i5Ag>36bK1F}`Bߺһ"+m VdݽĚ2v B>q OA!~˖m%'8Y"<JTA" !“AIʞM%>C؏Irq򠂴;t!ȝ8 Lx{Qacd&9b(i}sSAqvVU־ ƫ'=qxPDKzH3hTgԍ$Q!˝}+؞PQJ9b(v2^p<.FSN*Dg7ΧH#F99n nȉ1!aE?FHXi`N3R6-W:yءT=VspThԲ18sUDqu.$ׯg%Z ,@dQtY0;ޓAB č-,j>ѵ-F#pɧ%ZY~#t/)ӛZ7#FL.|$}ʙ H}x#˚랚U8șMsŒ-x9V7p0 e<p d< AR;~W)b57 F#(z1)r*"$ܕ@fG5NBl،?k& s|L(ٴntiE`YvC,4_ Q؋K=*d.!6cٲ-G|O 9v$؝kU}A|Rb&0 %m m&3R'N'W !re'IkA~Khfؿz.Q6Yp9Q~_2ʶ{]31 70Vj:Z]$JL217]8#m"qT>hv+-[ !mI5L.FXP(θ#N]a$k(ۮ8LngQDNP._YW,H_ؘL043KASr wlBq" !)ogF@g-6f4G+3M#3AaR4nֶ R{Nx*Q]aä/fF-04bH)Q6$6sQb2Ї+%܃E"}U::0$n9vMɹLkZzc&\uEܾ@xXOX^;MF9Y/T@vr9:"ŠR2s"l\x=he ۏE='m3AX (E&? *R:TN%mD{: I-^;b *fcw":&RxJW#gHӤZH;VRΈ/srkShw10ɾUNUC!XYSY?<~F9feK2Ù/] rN_ef0#,rr 2oF\#Xy`xmIjzr`z?^-BtϠCmŭΖ>C w,&2!^皻Ҙ}ӡ*)XYo՟+8L4I=ka: -FSӭ~5{BIUnOGt.q :@)ߐzytp~ݍI;tY-Rg:>2.Z }ZS_~T.۷77"(?Ӥ^=ڞ]rJ-Je4H@L|_@F MN=V^ll<}}bҵa VLWYPO9_Th5eV+&PmTI/씮-2'xE:d ׮7G= Bͮ 'zEKK%\>[l$ļ98T;~R= >i[(.2O3R^G}hMM撦y;my1\\ QN>n*DWkLv@sIըh)wtIxr_J KI𨦲J=ps H`L S#Mf?ݕ{6ySv299mf{_`eh3uOu}3R:2)2{|(\`>Gm=m5VM]4qk Ʌkxmb)AS\z N8(\=^nXrcce?̲?vQ"R dj3)v.ae/4 0Mҫj)Ot {`-,g!ՌHDD$qv4A/Ug=,rx) 7ү8pkC+o{lU$9"<izWsOc/6wا#Y>IڱHs[!!j5M,iSU! Q~Kөkg\\MG딩f F P?7[dKCu௪=`2q{'-ZgzBO*N 0*> ``MVln'/oM3ٙw~?~[ ЁEs+4J(6 l^Zwq0_hVC \ٌ᳑n(R`Ai"Zh{z"1ϲ5[&۞<+E!k4> >JC2,6Iމ0L~z0(_$Z竍oJDφy`cا;&ͫ8v{,*Ň(BcGvˮK =j's6De3i c0Ӿ mgN-@co:f;Q)!>r4`r6SѓlfGM:BI:Qgt }Azn@Ju^,WVF O؃fU,AM0MУ}=-XBYi%kg)8gOv)ghlM㑩xHB:sƔv|f/0\1N*;@omw[5SX*<=o4D: iC_g1kiPI̽4cgHwjq+'Gk1UV-"NXx,67Ʃ{lhE i!^L:m$s+Q>c=3]yDzSϺ"Dp)gdE/LOy1C(ϏfG&ۿ{ց׺zԇ$O^ͭy[$7['!b:a!p!g>7:1ny;pI>$QbM{AVQDLψdAq8i[l*ɱY7oRA[vr:TN?7nv!֚!*$ʢd. WEAW[u-<?$fbXEhȿOpC؎hEy;6_]Μ+Q%w@BEUgr S漷`fe;⅒9­tҢ@|Q{>^OLJ`٤W;\aWץZ\el?UAT(=1-| "3oyr}lv:=1jqC[v["W7V( x* )00}Qi7/i!cQm+ԁGITY9oSZc[v^ў I*P,Zd$K<2_&^`]R}q' *!vhod ,~AB?vǬDu%oTcĹ<$,X_ z'v}cl*i +;c< {^=/H!K][kYh=1$XR܉=?>Π3< g/FezMضs`9'rS!*:?;p% )mX"oO鍇0X4 ̟7IRֿg2``8^B/C+!Nq|7e`❇s6"X-DEhBuYQA^l6GAJVZ7F ]yUy_9FAQH4缢 {9U \4}\qdɑe"[mH?5"ae_Cҵلz:i.R2FXtsز/XJ1O^)9B_ɝ)϶P]{R$p_|`I6z< !xcb2-IR2퍆UlUu &cp.ڂ T#L(3y LC޷Q0aj iVpu-lzi"`#4]m! =FYS,RX t %gM 6OCW.BcqL2RcƑ R\ NjHa+o ≿qOɲ&-w dG-PJդq ,8W9jB.إ%KJ#KxW\@3'ZCѓ%'qAM;|!!1~} svaHjwfξH6oti٠0";<+vBj[ X7 ctٕ@m=^{9&.ͅֆ? l=@EȘ5gw_xp<+KwNF@lX6qbƖ `<Ͻ_j5Tk*i$w{u 7U;ק]Լc v1g O j2+ݫh`+ ˆç/CZLD| *m p^160"( rsɘ} Uּʓ` ,ǤcVrx jǬSpF~ z.59fJkF\`8G;dQBk1Qe ݧ hnQoKL'!v6\`EI{'ⱗo_0xr#Cs4DЙdㇼpi1ؔUrGؿ)aB RY60(@u0^Ȑq!ZJiq.N MmL4j$'mۖ7g2mrUN[8Mw.} \\捇tTg(i~;?& '<8csL,7F?x{&k194i'o\%{74Zql֦Da;?BVŁV"v2) u$G n{aMӸ^ {Fɋ7/Z΄kI#a+D`Tm5楰7U%p(QT{v딍#h JL *Svޜ. CekP$_l߯Lt Jn6e>#Yu93X+BPH5h +,V@do'qzY3u5~mΌa\θST)bOmMy t߂7;x]SQ̿l5[Vjvr7˨F2DX3o,2Ț̔}#;gi:1 )vO>TK#G;ydYB-ο.S 62ʊ%$y %LcDu^z'3ڎv`ORk/1Tiq1uv31t1@(GrdIл/g=ܮ^ Bb_r`@ Xd*oA {rn2ksfKFހlT;oc c}4U՞Ns;ByRbO9@K@JAev9*Kۑo}; ;Z 06aPQ3Bjpc\X*k3{dùZl#uCP)Ho/_RLYϙV_ѣ~;qRI\&J$J4W1 qX `0O7Q#]'G:'q) $պ0e *! HKo?@&l&{bJˎx1}֛Z}&%R4YehD7;4J"E9%y)\-nHIrCڿqUnqc3 *‚;=UOǀZi}V?kZB9+rB̋Bʪ j>.J[}Mӱpbk_K<6FCR̼f^ɿ<<q3S8-yMP/4*NIT wm6qKUdŸ#tzȫ71$`_߻=*mR0D?wej}p{þ?}fk2hm*&zY+.\&=:?2{O/E mջVst\I~MI:>uBƽd $Pҟz5@U{ҖqM*Ny<!`dv0=irѠWeal+^$m-EHru'O86qTG-2#|U )MIH_A}6{nȱdxk7n`Hh9U"r3gy^j+}SzلhVPt7#u~m9#|oD0v'^%A~Br;"7!!оdTy lcd$ZV՚[J(]8ƶJҔ0?Sxޢս^a*(֔d:Q( BYwc!;t|C?Q\*܁D'[d=IE׹QT8ġ~L}vP|7ɬ]Ѷ`qm 4.-%pV_L[:8U˩ 3}:%0ʍ-~x͔D .Gz+ee„=JV EɄ+.:1$k#+@{aG@2`8K]_,irwE7r`/Ȏ` z=شg'd?+dtjM[S8g҂G34X9L-Y}-gx% #j֭=Oigt 2%$r~bvZ G'k;a#KchzjQ*oUuDiK tnb'r3U2gW4Ӧjn9!FJ;@/a+IvOڼ';)$5hL*f6"*_;^%% MϾmMM;#f r:{QFG,Hzʅ$m{bmd߫2R%˺)8|]6zPsx v5Ko+ 9PXAttm (P h㈪sz Nx`3rZufoì"SJD. |G~҄Ǐm~;umj~ zZj-'̓vs>Tw6( 4>B5Ρ=Ԃ`O{&ig3ԣvϵ/XdVb_$ PGElSu͈Zμ;PVx d`&jZ)Qu׮؁^Ivï @H8dFM6VdwMfNʞ3ukSu8ʽICBT3 s'f,$Њ,v~P1;E֣B!U6{-)n7ˍYqCT@(z9!B`:%d WR)Q4h93 9wqWz=}6>g^!K wꮂ6yVr˱L`*W!=&&-X񃯶Q[Pa3 G JOUy;FH0s`y_ZlQlHQqd&(GoNeLvOae_ n.}ЦR]JWM(Rx:`@=VJOࡢ8`|v]D' Fa |K6 Pn+Jâ N_$B:" *9PŵĈ=`#BSc/}R #.kPr沋s) X9S&>lLHQaQ9zyj/557 \)fbb[et:[=Z}BN#Xׅ1/ۓ sW3'v&̉Oma'm_DO}&dtҾt3cL$Z%3Ѫ._{jcϹ5znPmX^C[AҶ'KW(Vcy_|-c;ŬrFкL|-r+-C{71#VIҟbPU,Av5)4=hE~LϡuyOy'MEV8AAʒJBu!*1c.`'T}Q<˗tu&5rH+s]-Xv!HZ@Ĩ!|z>tWD5KXȷ A{'mӿ|hHd8QsCy 垝 ' ȸX!M!~91>b/M _!4V\gh~ڬ~60͇%4ʝ!. Tlm%h|Jj9H~#OS hkrV{ Ӏ;|QfpE*O11dZP;Ώ (= nK85̩$Q%eqc#s7͓m1Sn3|B l:lӥp؂9tʁ ctYIp`"-jZʜxqE}/?{``=8҆{(śT[aJ&/ߓ ؍4'yuۧWusē(@AkbREGM-Ƨl!_T5Us2d+ꤏf⸄bZ05Gaße ?!BM&рO68¹LZ颀S`Jqb5fqn99gbJ yy=!6b+2sK~<R#{%#)0S$J;B֞pڣ˘r?l&[E6pgxe=1\-HR㶖/q/\<FA3zT#i|[رM+q Te}!>ak{1^6c;,2C[J+ /lre:烌n- f Mе)md+51k?XX60=϶Bf*7 XjRf>rroV}7h;2j?Ju!aقe2j85Ю?~dpi#ӢpoYr,xyLg" EozV_̬# jPW?#ÔnLzz`2"6>/,wGc_ V{OK ;/ZکtIXm9yoFj2}Vbۺ&^PŁ|IlWWH,Uvg^ X^E8S|?2ZycԞtܓ/pj#'W 4θ4_v2G$]M)$krٷ_O BUp(Z'}(6T"C9N/yd9C)?J8iwJrθKyYBx蝳2ӂҫx ]%Bmφ|&RXؑZt_KAGj;qEH΀e :%Q^. #G#/r{iвDN]emܨ]0훁͍,NbPC,榃PB\)3O/7Hf~-Ȋ5r,G%UVu޲'P ~bbGLfЈ{·kv龎֛$$V|r0ҏ?ؖ Vg3vpU8]|?b;5e?>55~( [v?%uT; r-` dڥC8b+GCz-V9>.xĪ]V64Wb3 }\zd[cۇXg~xW3%BBk]_OtH7`l-¤O&}[VpoeyySO>>ƷJ@Dhl A CXB%U}\uSii%1eL;6/a +JC%@*.,9[@M8PbZ-Agwr%!`7'B봛!qi;~ENiu#rOX;Vm%oB A|3˯4 PB8|{[,身7{fI3p9=}|d7O^$=:;t{Bhb#64j 'բMSTZ}%nqh3qaGYHz ƱwmQKj}UǍ>4يU/))St-w*Y$)lFmmJp¾aks%Ms#(E_th|~F^bM{ $X3!@):g>?IvQv="X%-U4b赞[5z@結7N@yQCBm7T3pIx:E 3i^_{p$'PXXIS([<g92]>s@m0񟷭Ue1'y^լ#9A~֜t.5\Z;)QْM6v UB+}|P8MOa:[nrcgP3;KMS?w<>Hքp?b.:bğwYU[dd>WP޽8ݩFG4Mݳ˕vn_ebCg܄QO\Qm"y#NA#L" h, mJ[#(ۖa+2yL-:PQ+:[QJ¶`я ySJ5aGsk$UbT;U}(EV=ixVR^QfwJt«>iD%Ǚ-  ];!( |\Wƚt17*{ڟ Hmnr`PH-ԪLpnKZY-љ+l!׵ui4}R:'\Lʁ߼m "Ӣ5f=j'oqXOˉ^"îsdV`WPZO#"UↅPT A| "?;o|x^X#>#й!$"@}"] Nmǥ-h";$#9LĐêT鼔!>Ɔ! q=x@BXE8g@#8-:[GX 46Mve@#!_ʄ|ic>ђ Z nxR;KlM:Mx?2Uq<)|m.Eb guInwb]N^;w: 0ymJw:o#m%HՓuOGزTp?g BgaOEY.5ͨÎBJS+xtK_+~ &؋a+^iXi3eZ[H% ys>x !6I6Ҥ*v[q= ba5=c5Y'mWπ ueg>4<͟XKLsyVƁ*`B{|b G:(b)F:AE^}{2J/8ފjIF6h(Ť΂L4cLwzܰQ~\ĥ 韽ȝSC ݭ< ,8wfC~{LϪy)rKՁH9i#d c/Ж0u5` RAK2(0Gۗԝrf~ݛ cf /tCrqLnj̣qJYap%R 0ZSiѪ'bn6"b%4?pW80e̬zEvSGo0 ےc+o_H^Eƞ`O ,Vn3Z}"J$:p5,&ωR0~7>5FS{uAk;@s󁺈W}VGKP ms Q͖w9োh!5ɖ*1*4o@BYB! s$߽yX <M檱ۛ3Ʈ h}h,Xh?e№ s[8}6o5`@!&LdjN㜧J XU3z GxC{NS(PLE5]ݵ}?/Բ=eԕ 'u6H$rb*L :-fܩ3E퍸VH#G,Qu?zxRo)Ff甬w81qi>%d|Nq`TI=+2oxw':< (H}jEEk6k~k?ɼuY<Z.79bs|a-#fw*4;6)P}ax>F(21n{X61pHBfOVqUQ}QWNOpG鍉ٌWE|5X\x>0Qj=S:\w,VOVZp\]ˀɌ>+itjl:gHq¹:@9 bjw&T^эF+P e~{ҺwpEqgFFMiNe{e5+O_nkzRp\v< (?]JemHBRvjz ؕۢc)es1O(hj Q0o,3[pGe^fyK-`U),ᣫe SU YrobZ{v;&^AX,eL+c ܼlk҈/D c[C`32}Pf/ZZB tp: F~+^=:'cqkqgǽʮSL RMG]ցl Ǯzui xoz{JK|C`u^kP`7-+QsU\kKek> cY@D F9qS@B5 %b۟Al.5Dt&$5 |I.icWWхjHs| o_FIK3B[RE ٔK<;Mog243\0~B5_ K¬(w[[yp>=?q;(8K0F٪C7^ք4 _ȼ־d[=uUk|1!을i֬iawrGZ}k L FE ୹ĕs OiuuuEO{ Rw@- ~5fdNy=]dIg*g4cA<[;d: +mAx,"'0Vo<&v {]so(kk"#Gg|LMrHVc N ։M|GB =[xB1U.hhBf4+P :]ɐ'{O}P*fySh]h\g3nⵚ7:I[2T^+*nA[Fe߫o̫dz)I:|C7 :Ì,]jh @ۧwT2ApIsIsJ2JČ_Vg͛C%sa}<}b?jht_=# &MyDV"jJ~^e/dLśwٔfzm8Ke"^Љg+KL6N. 8J> -0aC8uiqgmB(1.Qb]tZ1cC| %D|RՒ d{4M .g퇄 t1A00y<&d, 7PG ,r(ziDFg[.,TEá EIj 9 X%١MH^=̇6*pⷝ?] W vkaLC{ھK|A8Xk')%AלEĦf. ufz5F]+}Jgp8WօŠY#`^sS,vsbf!t3T)7 g-"*-umFy`f-.)n+ E|  )zyo*`R+L@EW_%5SbzP VFceopՍh'X >ݹ?2NhY; Whފ&}3 HZɚʲyN*00[QθEoU_2[kj(1W<9oo2A)ƒ88ѩ˵A#,RZsHc!&<הj܀~ϨS3ziq>gD_~'.6>B Ly˕eRUm}v尔Hwt>G.Lu{ey $5؈bi@C:j.]poE[~$ϭe T%a"Hs 2R^>s'pYˬ/Նx4SwW Q+fn뾗X((re5do3]|<؁*ۧq{2bglfS؃,/ kؾ"X*E䢎D)sL%UQy lH;P9X$aJݖU %}|`쉜9r6ˡj:׊2ޕx6壡Q lv :M&+'n7MgtM 1KUY͞ w̍F@[BI`o#euDDwեxjZML`-6nT0c^| j^8Y*)f6ƾ/Ru@d7@y9RbG_tF[Cmr)I|  %XfO^ $~su?B8u!D+-!sWFv~5׼d-M`j_Aci{|UQpw/$5 s1\v` E2bOAxOK)nʳlJvs&#ٺT=;Ndœ #cb!ק|^AqVRacuOV3JVE5nL%W(DshغrO9j4=im݉Sjͳ40ú:Z͒EWSkF|L:Iyˆw'j3>yie-`lUt/a342ez=ǐepݼ8MQ6)x4ۡ ı(ze"cu[!=[Udo@FN@*I5tv֕SN`WK9Wmhѿ\X /HHv*c+,KXL;4L>vP]x %z9ϼ!p~#iFliMۏ/\IiLS?xmbZl叢XDžnN,djPGR F* 3䌀0.Š]"K#rڊ `}_ch03a ǢHXPUMR.1@D8doni.cƘڂ r";L"8B7r}*UkӽZh͈F$4ό~¢B")~mGҿյGJ՘)f  {.Ԭ0y(E1 >\{DZyÇM_(UE1Cylԭ3 f* P?Nd[pQi}k>+|iK@ 3edeƿ!8vPQ|eq O:J޹D8DGJvٝoT'˭Ց:ҼJ/|j] m wֹ"< ˋH6Mb%9ZϻbVDvs V!EhxTe(cݿ1ئZ-6ˮuؘ]-H#G Y{6D巀 LxMZ'qHMNp4גESm~ vJz^2m2a2~j|i eJf1&- }G:\\f:RVuw8t_+.r ߋX:hGՙżBN9Uzp%7V +?foW~"Paeꊶnw':jc)P :c*!WLc EQ 3|V;a#oM8>!+rc~'J#EsE Mt6GqMNCd0M@'s9z/޾!Q /˰=7~c݀24),̷m?Ie|g(?LdKeGv)Wu_[L~{K aWӀEo,#QOb,ːᰌ qMNj?HSJ֌TUh_pNEk@㗌Ssx|ֈTXDi0PbY^5G~cw. c9RzjuJt_G%WjB[۳ՅϨL{'pFj9l*Sؓ]P-+cזc&dfxPX_Vxe( HbC f!.sV2V:E@JQUɨoE޶[[W )CXQ!;eZ~urQh[2gj-}ML͉Mi=wDdiC@ȁTom7䘆W6’c~K^侷}"pU S d3SHG68$q83t3\6S,_G ;ILfg h-YчsZ=gWF5b7F` ea dV _Ѭkr 9 3\wŢ3G~nЇw]:a* Ib~9HX)K&fC N/}@_O D-]vѥsҦbBx}fvjg<*Kݚ|ݝ=Q_ bKrXRV8/ Sė_) ț;gjm؇y,,nJ^k.k#e!mD9@C0Dh'1ؗCuЌ#RALjJ%ވ~N{A:h1 &G'π)Vf!vɷ%+b~ '1qXkeAN!e29=6tk x>.ӥq*nev7ސ}W uuxc_M ^"F]90 !^ьߑx +ej.2IR:R/if2D5 9MRHȂ|s82:-l+k>&mmȒ7Y؋%l?$Y6D5ToPr -׿,vU,Ebi;O1v? 謝ڵK(oU6ȱdv[PjTkMd( ݊6 VPs rWj$ھfQ!_,yNiH9+ƄH5]b.bL9'+>sksP1=܂=Bs;Eo)b#S"b&}{kr3l\`qdZ5xˈUX)ԔVZ2E)Ļ'\ v#UVhPbZ@-lxa[ P$i}>,k/P'7z C%bЭ(E}r4Fe`txPn%b:dC݄ץ:xHW:3e[@@8ജ\As&˰D&<⳨tE$36 ?J%EuMJ˛| 8cLcR1dGꮾ' ،$)åGԠGu͓^+!vu_*wT%+G-Ա YMfpWG>T*ˮ2J(0>Z>\'ч (d#uQ "VX21ǦdG(y,)x;1۳'l 8TlXAۗ9Ahe\>[c^kHW-RTx̊b/ R`fh%֔cGf:Xhdm<_tΡ㸈~ח{B iv]G oDLm7e4]drb\1C/sm0qb@y~fjIȑn$ޔ[~ΐnfon3WRhMa. wej $ˡoy_w݄H!9 T( ezvs#'4 8(p OWk3K.| ͥL1V])-rIH.UF /,Jj 498`w]e?v5/- ӽ^[6k&-]F9yߊxֈ f$T4pu?.<3I1D jJޓd_$Dfu{9TuUP8ZE3'CPNC"ל!9YB/=!{}LV;fTL 8_FZX=}65 >t̛ݥ9SL>Etx4'2])X;%9^0.#kNV]n^!YUSINk=( "u1v610Jzn9H'3T}IN3X2mn˂9c˚.;t1j9HZw*_OhOpЊ4R]®Ձ rYN۩ٻ$M 7^寃S̺c% Ѳt|J\P҃[XU`36b uMpٶm۶m۶m۶m|˶mVԩuzMG;cF^xs̑c,LVRl9Hx:#IPm4vsXSq6#f訳ruX3j֍ɕaY9Ë54#'4Pm*Ƕ7뼌y vA&aAc9(A >m0)->ĵ6gTܡ.p;"yAHIh? pJмw+AO1n=eV ˛tJm zhD V 2Q>V!-Z6d6P?$Q=Nl~8x)~ӗ7! xS UY%1#'lbnS">C󸒐n[Mq8Un߼g]~$€4%", ,w_8i"ltA΋StLdaߒ a:iQQA;!"gHرj Xb!A|a1-@)ͺ(VF&KX{a ik0hZA)f8 zjɊ퉤3~}8^r\ujSW;j-shvjNY>Vq+,ܧ|7A AF9`%>% k*9 `) "5@$$3X/فS6XKˮEns5=f+G]ۭ. 4#N"=OE _0-Uo1 .zwnIWpp=h Q{i6cVG'0tby}捡\`73 ;uȴI\軞Ua9JJhk)cr a ނU:rh$0&!vB/љ߇X֠XE'STMP yz~˟n8h4ĔmA4/y[ c7G1V&nˇ|\RVxk¶Tw'4ѵ9&,wy6}]c #tPP@KB:̱ ݀άӼctF;Bl,'lyK#4aϩ.+5ntS%)~#%%]MnK_teSnW"2:]v6}mȼMbcyS{4xk)[ iƁYvQ" #ۮCcr yڽ!,"@ 1Ŷ>-,iR ޣjAXe l8"\uFpsEZqiE XmE9B5|.5q'[!CQFJk48εU2ȚsB{Ͻz\Ҫ/%l~{`%V]z` yYQ,\E܁LENDm Sqdl-rSEv(5Ls%ͬ QA憀":՟3v+o#B=l|+Iqȶb-Kq hryi;[^>`Dwx?T UX ' 6"ӰD.|A,v,Xl.M{NM7}pUeT @]`kRfH%%A$wGC!ۜ5F%mM)uܠ"t/`'d#1:'BTδXv|s9(xBc'y3\ -fhE])KX5ҦYX >U9#墭3';(iZ:b6JFqv!'~/`ZTY*=%Q.|"x SE81̓|Q,@5hcQhBTNIv*̠k(򃲾"I \UO{SVDn!z1? w6pD֢d 3 c۱sZcZS `6LQg?4ɟd? SEj0M\Z 4n|K<͇ڋ9\{Hi'M;q!'pb'}p6v\2|W>]D?=](Vo N- )&hyioT\#Gg˝&v\:r@>eS׈/w.~a+0T3"5^IJ8ZU0p {$0|?e^ kIqϑ62fF J,Y0\w->o ^Etvo%ZP[݉u}MZ~nswԱS x1UZnsn@wՈe,;z5dWdnaא_MJ2@alf&rXPd~.(LLDCCqWb,"rl@;baҼƌ6wuS[ɛ˹G(nbHhy3K[O[;]Ktӎ-ӞHJ{ =gP=G$3 îdaPYH2aTq=l$kC4S)895Sj[.8; #=`o攵5Nn, G~>р|^Z$- B%LQ8H XYt=i6Yɩ~K+ń}}7E\t#u% {(0Ӛ{O~4"N^؅;V(qB Y/롲ݍJa/t };GP D"4}U-6y3zՖ"u@kZ~D9d%1 OኽHf%*I=""}xP}y#3@-;,V;v6Q?S~caRU\G&2[=鞦={̀l\re#f-o4FqMN@ײ&yPV'RIu<W)2L5 G>B*\EwƯf0񸝚E[=B1 ]iv #:b>u+ 7%vVȝO`P:[&$Ы:P[-/]Q=v#IA-d)1MphdHHר\~6Sfa!Әs%%}:rʜ7i(xPT[%jǴIW;  + \D<^WǛ`[Y/p~SX=Ƨ)vD3 /3(oM9c+5jW5#e͋T#PgDZ ۹/RcF M#x֜ԔWX.TSfJtW[īƑKͼU{&Í.DJ5v+kV۳H'C^7P.T2\Uhނ@/*F2thbS${0ɉĒ04i6zAW_|(jܾ;hfQiyj&>j穘#a`orT0OD!9RHgaN5(g$jܥ|^?ЉB>>EK /jY_jn:8B=0汏TB9coh״D /-[ضT킽"; 䭱pk"֮L>ν6@_&\Ok:[ҸY^EG;!{cX}&ko헸"?Tk3M`UF*K\Hc Zdf2j5+a Yϡ6.&Jo0M@@tÆb(j6WҳR}A0P_W8EU2F׋ئ kn܎;RӸ#{EH_Z;JJ EJ +1vv}y}ėcPPس1OxAzvQ r"Bʯo7F":BV<> aB=е(c1BR ^@1ACذ1LDg6`5`{1?Lt*|}"Voxv s1v/t},_-`~qBLNy꺄<`(zNc-;]S$4Z<0F;U8n9AS5/nn07cZ鋧7"yn24ޏ@tg'{GzcdT߿[ZjPnahmEagW"Kղ@\ֆ3zM-7Ϙ'42fcm00̜̑__)j 6n/޳{F?Kϳ;MY[Qp1rx4NV?u5=XxC.kz~*S+︵?2,(IOR;bT+~Tk ܣO[2$EgH-թy-I [w`͍(;a!Ӿwah`LTQo(P'>qi,ip 3xB֪R5\NhBn#Vɢ7C~-قR ehA/y[㌳Q6;;?LW,-ԋqeïhImsryUZkpx@d6QYGEqd CRHQSǬ[z eFj&3"#bhY; O#' e3x[bĽU(0I7ef4s5\m0 ;<01$6]ݲڠW5tX$_pP/ u[DJ4|s1MH+_8[ [R 1.{U,yY=V/q{]c).'8U8=|Ht`Ar_7ԳwD`oZ ~-&AZqWQ txGg71+(K>K/R07W`p1~y]%&!k/%\37^AQ)*͔[`_ YJ@n~[q&[_-C_m*VӔiAp#IG{H 3*gݰG-$|>25`yF:L|0R KBFxzp;zak"X5x,TܤoUeT}#_++4qQ#cRXDU(,Yl ?cRETaowN9nT{I , DCNL`EMiڢxS9PQQ7Y([6 ~L/P!x XdJ/[45>S8B]$My^sK;.eZkM΃CFs]aGFO)1~EI&gaWU7ABfQxkjCgEʾ$H400՘ M(QWpD@KH5]/n!'ˎ[{H? >7(FT_Ysf"ɷՈ"={|% V\Qk>ʒ*N-!8&!:a/u(ߑCl!G&.pA?Mgւ38Jdٌz9։SS*|w$7V,\<ؒAwdž1*-Lx30DVkPO6h?dyw>#b9x\ﵣL3ʸ8?p U{j:u0>T`w 1,*\YRZ /ή<22^D.68+H՗K.Qlc qa[񼇚d9JaR YGnT|qV8$g JRA1% W0!o]TN}X@˲^DbMLhy'\zzDGeKXWX J}JZ؉g%* gzHIHi $eO|&%>C؏IVrq򠂴;t!ȝ8 Lx{Qacdƍ9b(i}sS@qvUV ƪ'<qxPDKzWI2hTՍ$Q!˝}{MmQ* ' Er&1R^溧f0. pf~D)5Bd >^V c?F28oH2uZ[ i?@+1uTGT9]jsM J{Vg#!6lƟ1 o>jIhE7x:"0m,LAB!rC篆(%{2Yl#]gu Kε, _`o)MRf1zSd-6L/Y FP6TvazXC ~NA\LI | Ƶ Hw%43[9]/c[KiudwЯXse^S wtL7w y~7ӣLM85js78.%Hlܦ#)7]@ D7tan8B@{/omh 72=3Goή05mWBa3Ψ"'(/|ج+s/@[glL_&I% )9p;j68bzgyDټ7{Y Y#MQ L Dq46BumW;snX40苙QqK9)}1'jrxT ~6\d:J3paEA'q-'ޮ"9)qXW Yrȳpȣo$:;VDNtg: ]\dH1뜪mE9.W3^".Z#Dj5zWIL|xa# h `dIEVʩ$ m!{7\A77s[{r^qn_DXV O W3r~ zWiTirJA1eVuut.f7&ٷIjP:Bw9+;|}x2'a(54RL7Tl1c^f(Ӏp>㥲k {lR+5Llk?sj8^S>ZNNA͈yx +^4O}-IM/VZ߷EX2\z04໭rpp}Df%+\sGT(gqVg[2MgjثHѤT!C^#7T7Wn%NAgV;<6R/ϙnՏ~11ipQw~.ft'R֧t"9_":hkOu+؏=`}ֺzR4ѫǾj۳KNE)(r (Ąp0llos\nEcxg.][`L%ʸ{3e:tzwv1{EYT: ؘLH"ݧK&1_(Eag%w h0̶Mp3 }`SmZTQjRq\mHP{ME4(\r/8~!4ϯ aAM^xdij s (Ֆ26fMa V{xnO(Dsy^2Ea.;Z1 m OzIetnU>+% F?5vf90.jvmm> .zH^Z.!i~BI~$b\; f2xkB,RȫKHtŸީ!d03q5W[6l[`Rx~E5Mg΃)C@6v]ɋ$j̀k=[+ $f rgsˁ, l&MO!2y06U5u4ۉ*cNCaA`@. 5 5-`'hwo>rS=\iş4zۻK<{{ O|Hysq !wz|:PhI]d)fV?]u] 2㛚%M j#e+MP$h>J+G xb6E:!gɿ9,1+)ul¶rW̯Z%۵RۤVW,Y#0^m^MlYJM:ʾ+nHPbA,64}Ow2ڝc[h#6d?v`0Tх&!`"w951S:XiYQ;LeLBExvy1 ~Aˏ+~Bk|{-_γ L%dPL1鿇y3\;xjE<Ͷ_FЄDU,DO3oOȃ%~]AL%b̓|M MYV(~~eUGP//$rTU 2 11%6x2}Nm+xgu8|%)ϼ04?2ŰҚ+/K<{˃ђHDrr'rNohWT/uz%A;RBHᢅkgmYӑ7sxbKl, o6.N fg]\@<s IK=&倕e~(Dum4k's@bѣ$Ap:{ 'Z4MeDJ8Yp~>d2JJd_8T_8OCcE]wJ@uY |H?*g_XգvcC0E4$95 ,:!cp2ݏ`կ:W:T?Y)9\Y9^6Z4 4&`ijZi(7>т%^.hbۋsv aC=g(Vm>WJ_|M$='zlLi_яmQ쮘1P #u줲dVWz$Qj^0ɃcFA ֥uRDܫA3n~|u'׫_*zr$@Rh"ℵLLΎW9 bscWxgB+LI3rNim%&[0;t7y%z2M8&*za~¼):`  85X걲ZW3 yH|ء61"_VJXo"x7jbìҬ;(CYZ#6S@as H7&gfZv;ob`L<J#QKxlVg+:$wf+hKI;]d ȉ>!W(;Vy9=+ D*2,k̓AM[ ȣOr\bڔorwMtb# Wvgf i A4uVc N$O,լ^ۯOz`Q|0=܅$bgְn*"r߄{=pRyL$jo;|^җ Nc[ɋ56E; qhiH./ꇮ2S&RWq/ˑazwWhǹCX x&X~Ux 뽛@^ e yf'=/yѬdۻg .:G}H!U0\Μ3E}x"+rf^ԂF'f-/v.g>! B8cmDUAQ?7<Dp&N6D:ۥ rbrMTܴ@}A$O]x:99clxPb h%BdAV]8F+eZ7c)=?@QWש3}F D,PPjř\_@rw?Щp[s[Ol203C2 |,f:iQ NȈ=u?^OLJ`٤W;\aWZ\el?T(]Q-| "3o9r}lvھ:=1j1C[v["W7V( x* )00}i7/i!ᮒcQm+ՁG~ T9oSZc[v^둞 *=G!(q-2EDߙ| xL/q'*!vhod ,~AB?vǬDu%o{*} cĹ<$,X^ 6z'v}cl*i +;c< {^g=/H!K\[kXh=61$XWR܉=?>Π3< g.ĆezM6s`9&rS|lxJ9g,\ICJ[ۓEbz!L32"?2 ,}#{&Hv-] aP Sst%)OvgM[p^w 0h"M6!+ ]:=f!^U3QɪZvFar+{:+'}VIB

fU]PrY$,z쫢cbU63~C\'6EJnqY Pnс'/Ag[akսB)}įFE (gY\<1>Ɩ$]MF* *׎:Lw^1ACtmA^{}Y*@_B0R{GRh'.؋Yc=Zsl(DPCoG&sBp^^b(k qIXI9dԾYkNI0QJqcb|5c)Zۉrc?ye-Lw35CffY.AEw"l5(wv4YMΓމSfv.ȇm@0mK_xr#Cs4DЙdㇼpi1ؤURG^0!SFl~ֵ < G]22dEZȎҸDmx`D3%FSg'IۖuFg#cM~{ m[r@"x4f9d-Ν?.C!3k^M4L?թut\nYOҕH#]UØ7칛BT8pwkS" hvm~W{j!Pbf^On:L#m݀&j)So䅛hgg™UXO!0؁Fv"0*[PRXmb]8x(] ; 5a4A%&iSbW) w_PN hF5EK}J>a'`cP25vS4i){BY~ѨwNR_Vr8c7^P:->-Qagj2-}uJ$&UN;FE 1LjR=mMD@C]P*xF?oHwSe m0# }*;Wݪ( uff g2#UX} Jyވ\OnK۫'[:tĀ#&Zx[%y<$mbah$h/e&:wk ;; ')Qt..tn6Llt#Cf0Gl,,sFFVV&FF&ffvvfFfl}ЉS7u6P6^HuA{Kˀ뿫Mf^ p=2gס0nێ((vIliW~WUs*k , >=FS LA !v/l=:2a{ |dfeUƮʟ4e}!Ƣdsm15_/ ZIhc*$B%VOO4Jhs*o%W ;7f0rSY€bF|oӿN,i~雷7{V L&}S_._|'#+ZPqL\%_TCӆʊh;ہ=IFWRI0'VChAOz%v3O ?⢷8_#6ayiu׹vU\k,)w+6פ o4 h Wʢ 0RvP;"߶YV+AT~}K㍍ӕnchnW(W+.^_krBx{cnʗPjp-ڟ0yRȮiiD;ARy"Tsw͉ MZ .'7v:a|[ǗS5ԗi\T#A1R7ULBkܡs1 M=&E;|ȑI\E&a.L.kóJA:ۯu [~׹Ga<Ռٌ:M՜@tCEWPq#JnE2ٓ m]Z8ĻdŨ5Ir ] R{rzDHE<}vE;Hb,%4\'/6 RCwI Q }[EU'7!y![fxPC]m@UVtl;`jAnKс*EUAJ be*}2?Ced\8#@gbNy h2[~RNlxys#ssKAJuyzIY=ݧE"}fcOZ~[/jJB7.z]Ȕ/P͂Ah9Եnm-^eEɆתnX6g̠ϰ>-}=ۧьKBÑƀ ot"mU5NZϊLyqf/ %`"YKզ%EE|t*e\9gs@Jڞ3.H?)WdH2vXޫňj̇Dݟ`E4Q27 {# LOnrL<0QÆAYi5`,3ro)V~bwu>^T_5Qz&^>m$|jGe<ӂD\7؋} U$I:1!tlX%.)zڮAu\}lM s,vǹqML~u ʶU@عRƲ#7dL&D梇wfvVIn; AG96& N*ŸHNJa WaD˥)⨧RjܐmT<{lDAځ_XpԸ %1V|gچc_j>ϚG\8Zi hcÇFf|t!_a;kB\vvhh[5l7׋=#:<#{#f4 9xK ~gA1\n͉M\2Q>nLk_BJSWP/r=.0ߚ(#lVȥYWiڻJ%sߔ^6!;飼H]-y:DvEL{1SM-Ҷ 4o(M2iՋ;w@Bp!9kO"{-@[!7؊;6Lf RIt6Qլ/l܄4UBU\cJoSsR) '9 xƷH,H-8PqM)ݬjzr/^C}d^vk]^,O2r&} Ht( F^`0O|+MƒvA+~w EoBF+B顽} 3';-IR735U5 3qQPp9pmIe)anR9E{3̳UfQ)gtQ"A㝁]ƪCvۇ JTHNr 1G {ΓsRpC~]젠|#&n`Ymo][L;5-R<gpuK?a.՛.HR=A ;quuVVʄ {$D /.O=aW\p.cHF^S=#΁dpqK][(irwE7r`/Ȏ` |=ذg'd?+dtjM[S8cǸ҂G34X9L-Y}-gx% #l֭=Oigt 4%$r~brZ G#k=a#MchzjV*oUuDiK tjb'r3U2gW4Ӧjn9!FJ;@/a+78&2qOvSIjbUlDT?O{wKye K}.nwF:: u z6Y Iں.S;-ڄVQ[eKu+Sp`&Hm26b7VrXBQ@hAв#&;5vg(ep]X3f*T&'re4;"C&>~l{'ة'oSsKhTгrWPMm H9y`D[C'(0* {˩N}q<~kbb?j +1{_;*}Ȱ D]c=K-b: LXᅦ+`i.9*>y~xq]9/9t6+%K+C-NXVqp!^Chh!QxXe0UVX@[Б̖֥\` %/gIo9B2oRT2$ xD; `an T^k&$BHK e:MOM3]8~,W95#P#eL!Qߩ.zYiPri[+p,< KCZyZ_q YҰrr ׮gdaۆ'q6َ"v=_%IА'8lk) -ǀ鞆&ݝޭd)q44\/pZ"1~^{OL+p0n>zvVt5^M d~?"FfyyH Jf ^;T;lw: #$NJڐJ[5I rOw {cGfBr}TTƴoV6aڪ:?!t_QU c)R*s OgJR=iuT^~KM}@Gmع{f_?!p,̭ȕP_EՄˎ+حi;gsDP짶/&dt3ҾtӣL$Z%kcѪ._jϹ5z nP<;ԭ[>mX^ϛAҶ'W(V+y}_|-㣑ŬrFкL|-r-71VIҟbP[U,SSAv5)4=hE~LϡuyOy'MFV8AAʒJBu!*1c.`NFޱ'X~H̄Y{aQ.ExWR'-J=j$eSD-ۡ KSnqLc5jl~QqSW5\-áy7c4POsaH$; )&0ro)oiYCz]!b9~]IrҜ(` zOGyϲE8{&uAVΜy҇y.lm PA˵8,Fѯ/2} Rqbբ U.YeI/)wp7cH`0Q㮐Y+ #/_Ө =] ѐB$<}q.#]j7@c>(wD|dy OO o:I9r.,j-ab>T= WD5KX7 A{'mӷ|hHd8^sCy 垝 + ȸP!M!~91 6b/ېyML3T47LmM?DcCNdu \u*:|}S>r%5eo$'Pũ]xgcH9RFij T3s{|"|\O11dZP;Ώ (=b^%DcۀxjD ϲJu1ÑIUҩh>l>u&k Rz 8l g*1:R,$8GD0 v5eV踢= k=8{(śd[aJ/ߓ 'yuۧWus(@?v&@T[Om:Cfje6W 1 TPak/ c)>?-=P0CL0~)ڋ-ps jOR_[ E))%ijP ss {.5mCl>V(d2<myFK<%G6qS`J17Hw>]TGQ>L-PEU{2_cZF--y!C_^Hx; Ճ#g~^7>YpGnӔc0WV6ͥC"} c$E4mxbJ+J8_D5>Js^j?y(#+ֳTLk2 "otp1piӂf"h6I%:zs90@:|+Dx!pHz)P641r@K l) DKkEak2qzhoӘA@*4Q|Bצmrv$<{'lazmHUntդ|ĭovd~B2-!C!&K>+e qj]>}F E3X(͙U9EJ?KӬ%6YGx~F)ݘZ ː@5edE$l. "~b9.X=.zv3dbkj''Tbշ]ī(SX%oڶ{CE%]]!T}۝y5cyEZ(NLhAnQ{:|lhYrO~éID\ypZ{8T~v;>|t5pXQ˕g%>R(4 UkL-k`|X"N==Rf^87ǑLZ)k_Y-IeՏ8 4יNo*m7W35IH@-aԥ;&- M4 g8)ԟ&p~$v@k }| kjPRx/|J꨺w| [jK6q#yLNG& %F @./V{)\js `XPӴTbmOJ|˳ڙ W*[~V*[%Y\÷W`A]~OWm4gKn $y"(١ =`6Fy)Q[8}8hj>t b S-uЏ@Ө ;jB[0={m XTr(BzʻdoeV)Ol>\!T@{t!4Gv>.W~|ي MpF=#rF]=8}I0{8, Ʋ4*swlKb@쟵;?fmwo|rXrGXٲR ~ @묪gU䆮 ;2{y{0])Ŋ W|XY6SFRQa.gUE9ҋ8 4yOQ}np̠"j%xEӻ EOK1E9mN'fceKc qS{NKt#!{f(>zY󻔇:K!ko RRl&6c`'H͊=Fm`Ԥܺ8B-s2Z S/ES|3];&BC-Iw{"2UC ^WM#*9oEϷHp_(A:\j l%6քiQDWP7xjsWBZmVeCw[*ND6l1w` ys4,'MQС>gdWenk7@5xŌU? ~sv/Z⿜E2:Af:~&z5k;I&2Q%nXNm(og#q ϼ<+ v:t.|ImA&P*D;a[3m[IǨ,mz>[0pFU8~/~#f:dlMA :9 7DSpVCFVch6? /.DoBA%C,E'k},k|)#F+U`vLU&۶[~~ө.ǨT P{4ѭ9?7YRAPȬ /Ͻc@&"n8n >l'4UI]&bao(t^ |c~ tsːʸc< ?,"t QMŭrq#r]xR&рX fBG1rhwm-gT<)%&{&jPO;VRKo z>4[u"_|ecس$ dL2N%N}Ftˀ;8 t8N}$)`-MOSﴫPUx?>™C VqMl=/ -bw]nC m%e<0RN ӜX oO{b52}ՆɁq›pRa^; 0ymJw:o#m%HՓuOGشTp?g BcaOEY.tL)Tuex^ڪ'2x5G$*0͗=% ;cuޗzj(jĞH][2zըCh~zj}^Hy0EԜBmQY1V _IG>vLp2׌,ꌿ4)o;6ϓRYTv|`_Rwaywo*ɛ| %lj3V1)]d+J)kMyEGt_ qX:ٸڎsXۋM~;@/4\]h3cAN!lK}y{ Ǔs=5X km( TV.d>'JɖHX6nvM_m? N^Z*/AY&Zr/D5[.[[[KLj \b>X+~ޮ1"ClIZo{hQx*[5m+u1xUc5 7g]{#h::{ -BpLxY1cM:F2{ڌkr苺r꧐=m 7&2g3^*\hbq- D֫ OpFF[]D?]XiZB rt/R&3z#ҫ}m i6ޓO r6y%{OD7c @oR42"#!VEJS*íi@5Y}I֬>WkI*ü~³r[X$r{Ԝ/bEǒ 6bʭcQZ?j#`kX,g4}ߏKX8s"ZP+RXGWK@p " d3ڵ>m+v ,qMrPXvKV @p=y:.^rǷfd!^&fUIt'}VzuO1n0c6}ώ]j_㤚]S@"St=('a` | o_FIBREC  ٔ<Mog24\0~B5_ k¬(cwKy{b8 8JZ#lՇ!SNw/U/d^ _dE k{9Gq_Lȹ4'_:5ZܑVZ)@d#YߢV]ʹy'\:E ۺ:բ=;x}Ww22r'Pؼ.n2{v3 XSP1T 3ŕq [ ?;9taE5#3>kY&r$v؂hŅu"ߑGc!Gb}@ 9Z  Tèd~3y8`s?JDuY-Z{ 8lM@V3]#iK擜+2׵~EPŵ-HyKYϠ{yuDLP=% UbW8aWВ|vKiqT6VJ~8<N?)Pp.ɀrpUIS ysu.o5O H~xO=aݥ;/arGk%jU/IƄXyM{kR/Tֺ*hsȐ;&l}.mx,Yp.f[zEU0:\BȮO$'Q- @@Kr~HM g_aB*˲>!|yD+ώFk$~&COS4J|̱" Zd;UhBg>~T ߔV8@gS kg[]p *ř4R\;A)ItظG,&69}Y_3ݫ,0Zt뗜W:[ݝ&VoTqhSn{ 43cqA6F1y[Y-3 `KˋxSaU \d*z5$BӃj,4Ch,#|ӥun|D>R]qEjQw/LVw5ᛩ4-/_@JDWE&gP>x`vXnx`s {$ohqiN-Kzԧu(*%_75=뫞*ɥr=n8qڦs COCro-!m(Ow8_dh^ISUa9 U-"~KW7sz~Hykw֒kwP~-)uU>ٺXP$-z|޲nSCQy0~Ӧ N=vh-,Nn a:ԝDh 1Q湦T:{F-K9#">qaP`R#XE.:oƭjT+E{ O~`l!8'zs|ۺ% `c\Y یh03(ϣ-v $rܞ| |wa9q'!ˎ'fkp}h=AkAZ,//ʰ3u-QSIUg4TI(`e'BIcgi9t<&F8[ A>>u5e#ئ@(_@"Wyp%$8E&o08NqlU1":G.{"xyqrN̻w%Mhh'{NƪʃyMjgӭtx *l7M}Zj[w"EĔZ? ̰NVQ+eNR^30"I;ZlLB%b^ik`b !2i p ͩ #an=@2* kuԟLK޻aEd\ <1T$' >Vz )쑅;HZб=à/2:ELc"{r5G]9#2}/f3%c~=9=PyM~U޺%́O^O,>1r<\}7/~$/`qf?M >v(q@y4^XVHϖ(eU1[1rӭ,h7" ERMG(몝ueĔX{R΃Upo#Cl Ҳ o=F> A(ֹ#,y9x-q,HI^3gȸ<_iھ`GS9n*bR46Sedy*[(Vqx8ټ#7a<=C#m??*酩tL:9# K10?kn%pcp :t&iӷKT[j1*&`9,z~pПLJ$HWMB!=I`5E2G\]}gӦBF;R)`aj/J-%n<(9X0i ,X?IӴ :A>˅gr=BRZu+d0 B$H+ 5mC ,X"FBl+œ 7K 8 f]qy#ٴ}IX\/~9m'Ah@,B=9Y VcvMP˱p}hFt.V:VN{6oiu >[T ,~*X7U~h.YK!H^9Hڧh`'+lvfe$K mi/N P @Xߗod* B9ȱ(#&h+wՅ`:0~KgyL9)ۃl 1 !$-NM 8\J%e,toZpf3I0M3m&yEzP 90HJo_bou쑽R5fY.C 5k$i+JQC)H8Vd!JUQP$,[8uL٪ԏAr2ekZj +h߁%Œp٩zFoN]GT(<_}\E&~qCxwj2@8.ё+|v:r+}cudDo!4 owz~['u"MS>}iI&Xƭ(H}HvQ2^:gY#asml9Vˁͥk!m6F}p -{dB{ GC-?=+^ӇIq:&@Ӈ5͵;5Tð@h WLuۀL|EZBGٶҭbŃ wKaGQ1j)תYN`/׊"Ϊ%hur1okx=s{Nd\c č$O+4{ەTXsm݉*~w!CjJ`Evop8ŨBlŒ߀Uu)tĈzS|-NvsȊ BoȄ>BџnKHoa*SE#2$@8,cu8!<zmbm9,'R:5;0զ}9o%#5UxV+Q6L"X׃FM摟!p؝IJX+ZQ ƕlou3*Sg?QG?|[z |3dTaصد >r4֗>^ HbC f!.sV2V:E@JQUɨoE޶[[W )CXQ!;eZ~urQh[2gj-}ML͉Mi=wDdiC@ȁTom7䘆W6’c~K^侷}"pU S d3SHG68$q83t3\6S,_G ;ILfg h-YчsZ=gWF5b7F` ea dV _Ѭkr 9 3\wŢ3G~nЇw]:a* Ib~9HX)K&fC N/}@_O D-]vѥsҦbBx}fvjg<*Kݚ|ݝ=Q_ bKrXRV8/ Sė_) ț;gjm؇y,,nJ^k.k#e!mD9@C0Dh'1ؗCuЌ#RALjJ%ވ~N{A:h1 &G'π)Vf!vɷ%+b~ '1qXkeAx׫ZNú:‚&/` Ms`h?uSꈿƈxq$Px^v4`Jߜ`ЌuX$PdA> 9Ibݖ?65 e )mzE6uo` C"yL7X~(x9_t"ul t%*XVt(5wɦ_SunEy+r¹b+_5Cm_akčh5(P!mZ>9#HkkyW^0Eli(7@1Wpl2COuԡnBbR| <HjHspZN9SeXw"gYT" ܙe^QՀ򒢺&]Gr% J@M[U1 b #uWvlF{vXDhDj#SuIktǐc/S\*Kxܒ_ƣBGN,t&d3#w3cV88֐W{aCS¬FϵR F)P۞q#ǎ\^ĊŴ<|N9!wA*y9J*!]dv1?oo-]@zsadxV%07B8_!U %JW{jO8bk[\'&%$S5;l<22UI ͇/p/1 J2|bC[ 80 ر^K Gqrg,/ q}dS 8 (a.|mw.yL‚p8υGPMm!W eIr#c҇Hg2o+grr˅0`K~jl~Xh%?<$XiJͭHY)D =X|ߔ_ס lIyB:\*|ӑ}4ܯ|2U]eQ `| :|'AO+Pr Gv?hE>e@scMɎPXX "SwcgOpjٰͩڃ/s{˸}֞Տx[ n/& ^x]3Ҵo>OڡD +&TiRF{K1ptYES{=\;݋i7z?ꡅ 8^4Y(ο5@-](ԓe{k)"-ld<.3sJE?y#W NU8,Q0Usi4+A@PfԵi5G.|kE/zxȖ8k ć0eJ1L3f%;&2v+hjXEm;2+h?mInXӨ;`lKXrtT8H2Xnjk9sCĶc@$C}O#h.^TU4L4E-I92r:4`trԩ܂{3 Z'Ϝ>eezvl y5D[UX$ֶk!#nr 1 5Տ]$y8G$+J@o|65'"J)ǎtܱ:.x =4Cq#/D30'-gZCo>hi6)j-b^`3hyŀli x\KS ]esuy+-vq&  y+`27&ze\׻Wvh4v8aG__CHi`vZN]ӎO#xToek(dЪFTRӌxneh扼=4wWjJ| ّBrP2IFN.i2'*qP<:,&f @]›KcPTR'[=\+:& /A^1YthrpO*ʴ3~:\k^Z{ 9~mxL[rHh~B] x$g b>*-'\-Ⱦ\I9b58sVʼnq@gN>B$D~9 zC rx^_zRC8v̨mYqMJPbS+5Z{lkA|7[Kr $|hOܣeRXwJXs`\FFܼB(yׄqGi`z'=U ac1SD˞]](ܼKwFI(̫8 ,TvvR ?r"NGv3KnK;J\l(hONa:)8>^ m7o&jSOVT#ԍB`s7&=@J)kάy"@=wpp&Z/M:)Ff|9j{lQ7DbLkumju1b" g=%a.<< r .?O g""#f=g."e ޗs*/ǖ57/.]vP:bts#R$U* >ڡi : ]䲜SwI@n,2_çu SKe4rYrgfmN8 Ub:xD|D.m5NhnRgT$tҚ (:ꜜcV?u# {reX"@zRM3<3TC͘/Cr]|ЌIXP&8DΟ3=Jb0O!"ڥ$LJ;q}M9<e5w( ܮH{R,ZN)?,j4nJSzavY5;r|iyR[^`#oyU: zv /,ơ/Ԟ$mˆa0ΔgJ ,՗)[G%eI764wsۉPwlł_-J ] 'qID60JCiSSf>(dQGܖyxlj8C̐A,8@4q2cx?{Z_ ;=9ʒ2I$q𒻮ڊH1 C_q&UuJ!t E!z4 cGvՠT{Tbgfؙ﹎< ^3kS#+jp )2kPy .k6i `?g)~ŀ,ړ猯Iu>s5=*\ڭ. 4#N"G n0zʡy@ EDg~~fYb"NyP56AoD.Y`)@,SED)`S*v1tcm2}KXv,Y]vUWHyX yRFޛTG׶f,`mT[j rve.M$~ڎr`cL)k8C5kJ|Egb9mLp\sƜ# %B\,tfAZɼ9!QG:E1F\i DLژ&b<m˘αVx(U,q|Bɔi\bTVx= ,.+0߀Snx[S .S8>U`['COΔ$@bug_2(TA=@Uxy#/ 2&|H:v:zO/}> ~Cx ,t=&:L.a4ӧ<r >6,}QӕoN\F@vVtih~R  7MFL:㉮QNsڱo!fC,о bk\kHl&wfIdC{Sb0M6jro ƴhI6DzW*3 ]d X0(ҪUcL+jb$mc6(z7&w;Dh12TE{N=䂬fּ"| V}a,g3I VUW!`žUUɢ 5] [AH@pN;h*@N"N~h;[Qd`Rz4W-JȐ]Don(rSy#`G֬M1"ԓͷbglmr$׀.v~W荑FAt*^kC~K_pPl.2 /Agbǂ5!8$WU_F4E&nث=Wюϡ?MB.g |4N9k1l1\֔b,/AX}OޡM*aB vHb%?M+\^̛f^Ȗ6.$ ,2W[*4˥d叒O^:"4`:yЂ/Ҟ%sm,:JM(_Tv 0NeTzV~Pwvģ {#^ioʊE79j4] @5-e?ك|i*L.=Øv֙UC/2 Sv|rYu>F6CvAM!DOTL,aS5׆h&,}f?~$w봓&M:w:x;.+-CG.V\hY7iFqiPzF)Hzd?ڣ3GF,Lq~;vNc[Mhė U|;U*]͂/aP)r8i{=gdKduM=B2tH3CB#NC%,vn| 4|/"VE:;7[Agt-( Ӏ:&o/R?zùXȩ[d*ci~O9ii2XC2 +2X7rU0k w/V]H&/ GF~ }VT0_3wRsK(bp?z,LLDCCqPb,"rlD;baҼƌ6wuS_͛ϹG(nbHhy3KH[?]Ot׎-ӞLJ{$=3#|{aW0h(C0Qܳ!P nU\d)-ZtB[sՕbN Z=O4ҷsڞua7rp#ASl@Ux>/-іޖ&(p$Ry,,4KHߓb>&{.s]=xemM'?f' c!YޗPYLFbӰh{"sRw>B}kBߺ;݊jKY5-^E͜ ۖd ځ˧p^ag3tĈ``B_>W6BdY}:ZIV^Et2G?}[Pll߃6wϡ|t <t$N]XC; -jXW*p#X߫ўrOێ=fml.ֲ[7zWz&'JkY|<(pѤ:s_+Ɣi#uE!;Wd3|I͢]iv #:b>}+ 7-vVȝO`H:w[&$Ы:P[-/]Q=v3IA-d)1MpxtXHר\~6Sfa1Әs5%}:rʜ7i8xPT[%jǴIW;  + \D<^WǛ`[Y/p~SX=Ƨ)vD3 /3(M9c;5jW5#e͋T#PgDZ ۹/RcV M#x֜ԔWX.TSfJtO[īƑKͼU{6Í.DJ5v+kN۳H'C^7P.T2\Uhނ@?*F2txbS${(ɉĒ04i.zAW_|8jܾ;hfIiyz&>j穘#aha嘐a0Br~= jPoI0Kڽ~ L|}@_,dup:Vz ac?Zcs gǿ^5t??l=bR 0S( 6 ;X3YT:e^&s=ծC^lI_gy\v͡w_`tMdsB~9^U_/bz(7{s;HMB#|iuD+++ŏ*[7|۹A_!BaYW&mGEdku驑^kUoOՊ^.zŕY< V;J-|/3]ėkc koP,c|϶@7l4 xeɌգ{MÕ2&J% o^x8ێ="XTvC۲QYjfcc(A'"zĻ k ;_|)닰d okerʻP%QDso(_*e'5R;VDELּ ==pDk /ޔJ m`Dn{?YөGM;'EXxDY@M0+5mz̺="Smok2,/rH1*ve4zrxhh~X6=&FܛH_r$* ّk^!|s9YivAH3?U\uhp cPְ{(s9C(( [`- {7b?Rh0YIhnXW{ξxF#م.ի)7+X9t"c`IJ5FENQTTZt fc. 7LxэFo]P}ݜv80j"\|@,XO4<1z2*RԩB˭׎n.$ed-eSWE7pkY0ս "esFW=r=Kv$i~0aªWIڋ6~j YnL=0'\FG*tdlz 6uJ-  ,.ΠRb<!!a3ڋ N޸shɠ+Ǥpb#l&;Fv2&?տuʃz꽗W_wPq/܌ ;E<{> M3ag;)OZқ,/{|6$xVN:&JS㮃A+-z0t/ 3I!{1С$`lFbR'[s[NE 9 wgP!u[Owi$s_j!|p_E47Ǫ%cOK2RUx,5b '_"ONl6p.z^?؛k|gtAD\5/q~^1- q}T B/aGUi*&; _i$d !$kƘ;=+(jo>o\ųٲԾA%Gl-E;%vR k;HL8M*a:=LA?r{hk}hM B i/Sg #+DHa9j'Q.5ǹ)>dz&Ā|Tm˧ 9B]=IN""Ba"f!́Y/ {+S ʑ't߫LOd!$7UƷrf(jbL+HďI"·LjBqBٲ)x/fz9} CP"{>GjY'gx3C:?llde:mMRq}Z}R T.@n ]E>mcJkg8k\W"JAc=Jl , mj; ܡ)Vz߂n?௑4ʟ&i2$Ҷ%۪qa\,3ZrL0QeS84[JXtL ޱO$١\1gkUa"vWM>>Vkϊ|= J$ ןǧ`F(Ơ` -&6ԑ:ja%&ACYPPr%($@8)c`s>m~OnI2Pi/ʑG9sՔ_`~R%qөNVV͕W3)˄qc$P]ms{LP'#FԾH%2^YK`Lc̹DlL}N8/iҐ%ftT7((eL#>{Kg5h=rau@Ξt%-\SƼ/ fv ~Y1a^߬UGčXNc3-e;ŐqB -w,T4 x1~3(( f/sܛ,[`gݭT }L(fڕqdwh3|@2Fw]7z$lYdyl@}UQȼ?|s]TDK*di7F0{YohOb:HccY u _>MAXZr츥ع_+KwhbthkM5X8wh6Q x„p|;ɨ#9Ht$ˉ RRϮ41qDJ֙Y3)k|[(uϸ}[hd\ъ+jMURYUI5L$D',N;bh $t.hGzGt6`-8TO&ͨlH>=b;gyOxc2-twlӪ”7K CddFCF|x\1*7Վ5X^;0=H _^%OMǒ7 !O+\BS+ҷu{BFKg}Ey$3~lc4>ls/P}]4R)L $Kȍok3.2Jl_IP^t#:c#FacD &d+ )ݶ hE݋H)i -ٿd YOHl Aɺ?ZBiN|ӟ!S ;D$ R)< )-I/ޤZ34ИdM.'*HkؼCNý̄6Ffؑsp/"DZ879k9qgaZUh` x߃=m ETzd0q;8FUFX@1žb:ܩ'֧j@jc?܄vھ&5.@}1!xkHf~<~c~_Paԍ廅sJ~ㄙ]+o%'8LMf{k:u'IJ´3:N\qB<A\%O%rq4ZnnPG@hMy @!0q'lZr{VJm~b|D厗L6U&E[sGʾ|{rdLеR<+3yI:jrfיb}}9!~xeB}Xe LbcA^b) <N%AX:i+b-C5gZkQlZ9Htv |*.4oc\ذ抜}6_c$|ŌT;>C..erh_7ȌJ?he>K!PJ->`RZ!P܆ N0Vk:#ur` ~r8))Wv"Aϛĸ鎿felu |550+sl[`|1] pczn߅M)sӅ#xM K`8&wkJjÍi( "x ƚt?ZÍbu?F*STP<&@ue΂h댍C33$;%qpP&Ty'R8^Y"v6oMqbiH/242d6M/Ecfm"emuΞ1L*bfTRC Ft_ 艚iC-`3%-&c}(N ^Xb(=hQ$bK}\# CoD˔V,ЫG]@Y WE0 =]^y^ oٰo9VJLXV=4k26H Yu%/VS*g0+ VlE2i2&ܒ2+U-/6M&Z4FǦ?hLvσ٭TAfNT5;Uu {"pVi?D|=VzΞz"M+s.p]+KdxCB̛cn0I'vՁBK"xN4#嵪]|xkh0LmVdm.iӖpABT|d[X ;]q}w@'gj*C @ϼ^:!^>ZOƄ{:Ѽ05|@i]ywl3.7e'fgE VVۊ6ST>#C.!b+HΕcVc%Izz\q-{8ťwI0T({M_(7>VycM,c%)"O6Sb`bj]M" 3pѤ/)JvA=BD)kRPrY͈HDD2;gGRuvCk+7m xJ.|W9t?p).(f8VEұ#ͳw54oxی}=僟T42fjQޙ65X؝`Gq<9vqNj۽V "f1&l8,Q,{^nyȳBZ_I㣻cTk :!3zbd!\#kVk Elć(ՠ쵾2K։$ (3;{ג>pS9θJ)k[>u2˺a;lӝfo)ڈc= d3ŏ:߀{taIخ4wE|]{NMԀ%V[VcT:ScP/w:ⴲ]^ǀ_bcߠZc7Be,1SF2 T-lLa W0:nПOzW4a5QU8  `ƈ/iײ'&5Dhe $_S xBwd3_Y#Գ@; / @8%ԹqUBLLkɀ >c l0S[&mc0Yݣ$!'#~&/蛪#FӀv5 Vׂ'a|.!yg,/һzŞfxd{a[,zt!OSSAfX[i.ï~S>ԇ SH鏌k˙ Viul6HSX. DŽ znlh韆%碦ze`B'a ^TvѢ`_Kg^ut~rʦyi'֔K!?;9Q[(R xr_f@ P\ăm%2*͚rQrКe\ ˆ b78. Clx6>}a2ܼj'[R|‰.:F+~tn׹o${У}2g` @[60<< yN/6-}P/|V2 H96aLJ(O&g3=+av #ԜCM;{vINWqTrexa$:=hZ{hL>Դ=(۷>т%^v/hbۋs aB=f)m>WK_|M$='{lLi_'ЏmQ쮘1P #u줲d֮z%Qj^0ɃcFA 6uSDܫA3o~|t6^*zr$@Sh"ℵLLΎ8 bscWx<.͆V f̤JL23 a:#ٕw,0 /m+K gR9pMVdYyStdd;:@pEOhV 9ceCddCmycD$"gw9օDFn:$ņYeYt6P2,F.mV! P}kHBP-:nMͶ 1< u$Zdy%F4>60UtIxWЌ6wB}BRpwrz{W cUeYט'\γG䕹Ĵ)F,ﺷ n A4uUc N$O լ^ۯOy`Q|0=܅$bgְmUoA͝D}JASo~/Czv-xm`84I &s؂m@WσY)+ʗ0iܡ\k<p<*jnk{-_ff|HQ@P!^(#J'- UYn4x(ʱ MC9[/|}u]zUf\ OCӂOͷ 2HG.6?AIo9e'8,%|}=?iEqp0lӷp6R*:նRLxD@D56E/06%i %΀BG: 2@F#;Uo!շgx⨋YkfOVJ΢yop*cwZ@[A_O9I3oHJ‚Kް+W{^~Rm:׍>6ȆQ3u>Rd5 fVc{KN-u !ɝ}0 jk$m!D(Ls+*.κSZȃx1ƘFcqP?JG0t7k(P& 6BKc9eB!%e@Pa=pqF`4|"=Gͤ-#u sN&-`eN]}&y;qj`-f]}"{IU g!w=R;1=?GK IF<~ ӋM_E-g~˜&d-umQ YAx4!Q+&h$KDFrҶmy:}ӭx(,i*\-7^d;U{#!d{axH7KU?yfҫ:]c|˳ߍ3V>rSixg[}fCvU=HJnmJ֮CaODdjlci A7#AF(nM ;9I6Kkr&L5ghCo^ X64M 8Q-1 楧2d: `奎\ܠ4f 4",3؆>"йjxrɗ5_yw?>TK#G;ydYBYbѡmqF?-?3.ǿ* iJ$RLa0(-չE*\θ;\r)>x/I6u%*rx'??=ɨf{-cS1_c !n#B\nQBvDB۞N"V&I ?Zx++q'?yzc= ,0?XM:vomt@*^i2O4s">{%Iȕ3B4=ɏOڵwC 80i\7OH fv ӄ|/_u1MȲTF^<9CddE j6T ,mLEL>1k4y :64-vd{Z} `Nߎ  ]˙9X5w13OdWM_xϰg 1=lp ;m0/7#֥ U&VxMLZf__;d$!%\rt~jw:V3%jI@VXRQS+a #):Zf mV$2P_W&#.BM7D_5TL(N']AW_27v:a|[K) =C-HPLaDM3{Ӹwltu?bursb)@rX SbЀ4t!d– n#~gXO5c6N:D @5i~P@5*Bmܝ[edB[;'01jAMfv+)T랜l/`;mO߶=`0; p6KDy1ש˝}il9|!,*.o=FRC7|_VQթ-H^h٢"ԍPxFWl#~GDk!۾clT ;x)cа;:>B%ң b"X DXl 5s@Pgt+~x(T̙ȡ-͍p(ZMkF=YYggvt# cI8p3?zMصYND߼gMŀSs"i*>ًg :H90@'/RlI-ps y,s@AW&aAPt R*OrR7j1<| % !0"Q05XyM t>)>3S;#="LGǰaPvn>:{-:+ۊ)]@9:՗mMGO[ *n"uٸϴ`hC׍/:bB>ICrLA,V5DJG49)n0W߃:3Kqn\~S4SGC&-m#.vȍS jݬwpaR";Nnſ9QFM{qCSk'RSgqri8醔,7GP6o =6r/,Cj]dI| ߙvk%C"W/$μ(į*VXa٧ބ0k'Vَ/!Qk;jc4-kŞ#:<#^l|k 9T"c'fm ۶<,{L}`wZzE݁(@yH+ӷHG|- ~ l mR..7m& (4 %@j|JD[7oEn 0I? .`x1B;w>YqT Z).ZLR׫{~gL{dIbD_݋iXf.p}$< &?\a~ix &)=s ڎ怄q,ܡB(-&žS&D;E6 j$3wl]Wn~: q~xUS m0h,ØvL-9kΤB{6|F@$I N3iCdW+VH]P/ّB,,v[K\4Wkb{{Mߖ8\o$*w_ny套G2N!& MH =:$n)c1"}}/GIPUN%DU0D|4rq iiDzп[yUI ψ㦔E#" &pVo|2Fua^C܊e5P6 63nm(aRU?0ŊG^_)^h ,ܔ!Ȳpe$_V| e5~ބhڕ3.~l^I7&yg!^zpA<9$v3 '01k|k2TJgѴ͚&U?; ;'K6zoKevQŲa}GL3GܖC1Fcy[\' _!o>(/~2ZO K&Hh89&06I&oNeYeمclK*$M ss;-Zݛf22hM9KE) ,D|7V3Mw>_SB@HtbEk9ZCpTx}JCg1q .m6@sRrpi au˴ﮃS껜: 9ӇC\ sh2/LI]@* R؉«{:PV&Lأd5Π _$Lxqx sC6"81.t$.}WtL pݎp\)( 3M{vBB< Iִ(.9,!iq6˫ +-Xx:OC嚃.tђrWp<¡fݺA1I||ƓwV@Uâ?oXqY1b;zF< V (1^dA_@]5 !RVՂM۔\@YA;em N)APbGH>/A;-ap*~36R뱤O0U@)[([UG@JW~&}R*7[%svEs;m斳btt.򾡑4i{cmHR[D2Hmf#zػU^+[_vt!t3bֹ h+Ghtς\O?po'&L,#u^[)7%lw 5Gǀ`QcԼRDG @>:'G q6(Uwa־f:8*2:U-N0iwD M(=|7OS'N.ߦШgxQ )r(<9P`7&0E"gݍ(c&=R,! zczy-[lr`ҍ/iPXFar޻F?_`Utw~X1NJ=l\XKEFh%f@u^6UQCdtogE_G$ιUĬۯ?9)Ҽ|T/fӝd5x4|yRZ|Yޯ Ld lwvB_-{%c=rh2*UxiYhYA%`/ZIM-"z?#LWԪ Dob@2 *K}jOS1Wy*;KXjsʌ_ U){rB|?T]Ӎs X?YЌ(̋e5ފp@ Fi⭦%X)Qq{x튉8`Tn<:C&hDݤls@8kLjEv~jݬ]Bƀ~%%I+OAS?sOyu*msJg|p*hSL j%+ɤ ^>~kbb?j +1{_;*}Ȱ D]c=1ņCf|,z0to<<.Sq_ SFŕKVbsl}+\8fK4Ȑ(o>1*DW},NA H|fru.2ڒ3$7M}li!7zveC*Rp?ky< aevVB/y5Z$K e:MOXxM3]8~,\蚍(2AoT=z쬴Esa(8~r܏!<{-/S8,iX=_k׎ŀ32CڴmÓrC|]8FlGjx^th|ҵ ӖctOChݝޭd)q4vvVt5^M d~?*FfyyH Jf ^;\;lw:(#$NJڐJ[5I r4@w {cGfBr}TTƴoV6iڪ? m/ŨNxr)94г`nT J*ƗoYE{`fZɀLd;,z@E A-/ڰz cU\ @ܓAo6"4??=W(50+rV %g.H=ұ3nöτIxOv&zRSss*?Qb&v.^FCߣ'4%y]= 𫨚p1w=pblrΜ?yEgoBF1+Kw=3 DuYrn831Qrg6[̳Fݺ]ކ5$m{b8z21#^*Wn˔G "걂/2w3b5), ]2=dWBV7j\ _)~Ygg{Tdį,$TgBa=fls2M[5E~rbĠZqZ,H7}?S/Grd& Ev Zw1/ ƻZ=iQQ[L')S2$o.]Xrg"Q[f{SIZaUϻ1.CY C"Q)N1{|g7xNy[Nʧ*)[ͱ+0 H\DɴЧc{8˿,[g_^5*}x7pmpƱ ]K\jy(j< iŌ'P-Pj5қr';pa9{0QU~8 i5ybFG*EQ! ܢ֮̉Wg!v ݃-mُ[LEQel= BHL3y[}jyUZg:OX n*MwW ܓZ2$>nk B #a49GA>r̷ۄǰNh._cv#i,ik^W\AVRA"yS_R/DQ)_ufB[ghl!LB\¥kT>'9PFAMW|C&oF:|z.kWކ !ca$OP>Zt F{CMi0ODh+T,y/Nei~ #@DPg_,3(~.3d4/)]V~>ђM `D ]FRe=el+dFrÀ&e&#'&n##Tl O1-XYm=.SSjH69- %Bwlt)PVYa.:»<6u3B?LԪwHXjq( &/"asO}x4N`doXa`r۶n۶m۶m۶m۶m?7'N:ZYbBQt| RdلrږX)[2 mmVVZB eO:} H^dE,ѩQlq)  jw5usbQ _֓ 7NK8Gl/A<@N"b;\߇$N"5jPbدҧe2@ 8bUVQM+G OUC,ABur_b}[TߡS4nRR0Hޝnbn昻/>znj83Jx<[gB`F/$v7WiQvl՟`SiJ8lBsIqRًl]nB* Jgi:@+y' 7Ӣ_m .Z(,aGS,]n{Wv Pvp7q2mmoo 9ۉ傄[~D'_2Cfɩ´ a2R;X8%]aE穽i4*QPTJ E酃^z,n"iOޕ9An0풸 ފ[f8Octp4Q\wVv}Z8Բl-Fm1#guXtw֦ܤs zկ.KgLH| 3;E"&hXk.Vl⺜rL[oOomUU]6 |ً9 ]#w%bҺY7)%L4*bGhnlD+`vG[K;wV[|K:͓>_d卤 7"77p9`_s18vpIsR] Kk3u^ɡQYaF_W QپNxa(X.Zx>{#Vߡd"ܶ|tjne; '~M?"Zw˚ 7~@=8#J_+_IU (]{8䐣>2Y:uMG,i0Qa6#~ZѨ%9F~֘p*1X=.VٔI6r QD+}|P8MK:[j{tेg6W3;KIS;c/ s8:]OҀp;b *,/cXz1P&T7K%<*Q jQ`Yՙϼs|wQ&2O|)b%/$C $uج%u(u籈)7dHFq3jlKi~$dЈ[y;&.rRϕHJ`4Wu`݀J3[|O-[I3?z<<&&MPN<. 7Z [5l/fX?E#0.ZSY4n՚ GktS"=1=[LV!cA`.v!xG oqHP^gI>criYG&þЛH]!RU h-Y'7`tmK1;H* ҡ?Iv6{_O-K|K|ծ}Mzd"\d, J.Φ^e*gu,UaKf 2GoE|8LG ܘuP8HX;p 'jpj45ǡMb_PUEQDP甚43y,rEphųʸ? ؄@W3Iu9Zb5ϽviYlj Gz_Wql' 8q{gcpa0(> &Hǖ\T0CnC)Rhok[ldT aY᜹{v,vCƒ|%$ɕ~>w0-R鐃ZS.s3p9#IA:,%4LlVD%jڡNjSX,Qt̲S%4+[؎7:?+ygu*g{'K,U@ ڞ.gg#Qq_k vzjM,Wl.H6>kV0eg14-#ÁRLfƔJP|[#_~הQ(L6L;2ṋ M/ uAVV\x3w# c%c?0^Li\q`-'*] ['QH@pWY$rˍ 0aR>vh,Hۿt!(sSÙPYHdx l_R!Zyނ]׀k∈0&y/VA7wT& ICXEˎ52дځ7"F+;֭u4p)Ef.r&ۿCv*=rB)= mX_i2ߥ (ibaOoGq?M\ۙ8zR= 4N* p;̠e, ZmVE!\?V,yۆ34ށt#_OWFoñغf$;mhb~פ[ ? 8+̯(|nE˱.c;)Ĩ6lb9\InO昒^$nzP8)"YʬU&v%_xސ\h:lqF_I ,ALVEhJ51(<;XpJ12OZ81=\d6gHە.Uy3}<9SYD _Me`JDsȏ(lMfۮ˱駾 @]+bP%@^!( gԀӲ\KꁨbMڽxA4`MkmnQfMCkd~o5WRyĜ$1NskZ7)'O})%e=oqj∶mk ?7| \@K^Cg+?\Je KTpLqi&b~KNkDp/yUky*z55醘3>܀3"q2$t=TO+`9ȠD4RsqX]VOYKd|dž =ٴ.S;)Df1#hV\A~;&G\/`@}q !zjNUuB_eI;6o{vf@mIB漜`rAg5gZgbPqB+YȖ SacڼKE< X5guL=eVm?̃j4CdyU#?Uؑcͳhy; A}ts^}d`O [Ⱥ~$YԤ1;wDU{[`ڼ@T)N:X7z}gEٿ{r5; rɐDk0a8%@ {9qdui !#NI,/󈺨-z ؓ.Gzc$u2QV$ź&ڢKLiZ Sil=/8lAՁ! AhJ͙|8_r6 $E0siT\/ϊ?>[;7"rJ-rLm ynkªg`+Ya{ZFUqVyky%3Wm/5`-1\ybWhVgXf+DFvOBI<GKhrmDۤRX$X873 u_VΜ&aTƊ$ \p VwAÃTJ?sYl,%H=\{?nnfӵOոr#[ SR=P`&OBu40Z >K]Z'hc1k1g݊SLeղ:,YujiPNB6*, l{0%B]5-E/}Vܫ>p9J15GX_V* ^H= VH eZV:FbԿswO̾}W51E:",?5gǓ{AdX"Vc8c'\4߅ Z ;XUڡx|jVws&PX=.n2w2YR2Q0-3 Dqx! H>::p/U @HZx%&s. dt,^Upt~ޒ)^}_6%bUeNIBO 4%4fhSaGq+h Z Ba?|SNgwb.c7i[(+x助0aneޚ踶 b-4 y@ǟ-9/11Z]:8*{}5 bդLbZAQ4*?#EmJ;i5#Fy_ Dx%1$iIw haasMsݍIAe{7R?n"@:?xdPTtS~q dv6(v BWE mfTؠI^<<֏qr<> hcwamMBzZr}@%9kj&($@VED'/ {ԟG\}|rKqK٣CYa_-Lwc"g!tU(6 Ħ,ã-lFaf/#'o+ G! ~7{xo-KaR7AEW^:CԃQ vGinp:5|Ԍ}ƙ?ݸ>O8iX &|2IZHiK~ݹ^45B$?#r8:gc`}Yd28Ͽg &N[U=/>aah{$%XSrzz'Cuӱ} +I(m8B40uKyF8߿"^\Z L:8iO,Mvǂү'ilV<6=w cN뇹7Cj`1Nws 8Ak45$֡meM_e trdsxʅ1nU$vph\-$.2Az|tc?S\d؂HGc+x}'OZx䲊q]svr rjl.@ETHz5I )OCٝW3*{t`#M?8ƻòUZac=]X XZ iV13;;y)Σ̷!$4Kg] +Xw }v`8p& Kǧq<@j|a͗_J;I))%2h+ @[3 @0ݲ4y; "xMuk:3l_di*f9xC"6Qq'7+z FN"ײdeL,QFS`G*Epli*^rJK,ֹu|E!1#3erlئ 7ױ_քgDpӬVa;fFRQ~V*R_7 S|>o5N~ZhYu GXq?OLT1}ÕɯGNP\3*3 zI9}恚oyO @%`^ic` "j 2[Ϊ0 #b>tC2( iםIܻb%d_?җ'%/08/UOa p6E^9!2~-d3&a|>:>PzN|}U޺%PςO\ϣ>rZ<\}7- |D'crF=(N>ʹ+oC7\K[ޖK͔}+fV1Yғr.h3" W,|zpRכjHL'#% $ƤnWIˤq^gbU OS zŢ+(Ͳ83)D"uW`޸1d@|$O2{ /NѩIqc;rFUԺv& 7ĤZ5VXK0-gb峉tO`voI ;#`<Zժ #>1U>Zq0 ODsV$WxAXj2j9=.1tcr tJ}ޥ.}3DK>Md)b(<uN|n.X@TN 5|wM Fӱ]vc!WTVNG"}P\G" Nx qtP0uSu%B!.1iҠ`%Epb)c 7o pdxU"P* r8$YB)ܦLE^F%7#a/S$V'WS~妄KȎUJ plʠ"dyJnc/ Ei[ b< pı tm3)DOi+kƺa%T;S;[n,xXŭr ElO4w7Ү-lf-2 bQR)KF+;˥M1u5νyk:nhH^2v^KJoi4 cV(ޜah.])y &Z}Vꆻmu<*5*[dC|T(RJ6M-z !W͌9MXH I|,~-9-~/!bh-V/r&^2 8 fU1anJN\Y1,}5SLbޮ|BKl\N*|kQ Ţ+8\X;DRK o ۆP`h(\RE|6M< d2ߋpHT>n"En'dJ|΍X$v!ž$og1 lZNS-Xu}݂ؐ˔Wޒ @wh!oZ?i(V `1sE[hr@xw;ؤ[w zZ'sw*CkDbcNȷ~Y;ݢգID?zkS4j6 h%֓yoBE#Adc83OC65?$?3p5X 2[U([C =I{Lbc h)^փs\9cSB5d3B^g af ` ƭ[֨mt> 5XsŢ5 C~׃w*0|$1W U5&j ZtJqKjAs bS{t., ZYSILGe['{ כUt8~Cd0$P|CQaw :E5qfr~s[ Y =/$lB2TB4 ʦL,.JvDy9TC I_=":iXETHm!w[p5cL+ 7Έ3dU;&z^!pE.Ѝчߔ>*kſD,â0B1~#/,</+WAT ю\"/r樕O{4L:ݚv<,n~9`$VҨ4ÁV&̎? || Cᰶo ?S8H'}ð/f~$3;O #*o9t! [)7x#ypF]rxAZ;Ar9RD)zȳS[s^; .,l i ށe1z W0Bqj|1b>.j?_9 ;(  qqZ C;dOr ĵPn!eծV,~qFwLg@Y>a'>vQk^I~ȼNΌ Ņϟ{b${>,x%-#㐬 G$.R!.>+?A^(L~}_JeZ݉p&0Um pbrJ_}ʯ\ƃ?Cb8Bè&6V}r[+XA`2sӆM fVLg(ޗ bU¬ tWXH2H|3cOCS3TYY~c#WΟnrGcF³~Sz]&! gn/w{/}߯ 0^c| :68%BM)SpGt=kF<,3CscN Sp[ s u aeYsh*Z؁-P{I}E֜Սx'+sl-\'{^3Pm2<ڢ)WjP G{I1tZSy>\ދ*jո=ꢅ ^q7Z(̽6)C΋^ԑMeyi) .lOo?.1qF=z!Wf+xL :t@ LLfoG]+1iX k@#J"(b'5"L eQZ 27̪ u15~Qz̦A+$r; (iYP[~# p1Vfn ,fOI\D 0 MSj)t!Y$zV^^܉p1?3nnW^t-f7-8Ѫ{z6e+a"aLL:b 7$T=`&wXFl7.b&5u)@3TU3>Pqd/n9DH| S_ǿ(jXKKc"r䴭E9hWeDӖLVt QHGo8sܥes|v޶?J!^MkT 6כ-NiTEF/ْ?Dוmf0}pQYN,W,^ll0K@_Unc\q$}~7>$+GWGG][R=$,& Gw-(aJg95xpTѴ]t ̱wAq4 sF>x.Sqxx|ț'ċ%8sى1KiiX7$9`{w$|3iA=0~^Sp.-UmZP'E3ݷFKB.!$CƺSj;H?C& !2 ]XN2-ؑ(>ҪeyfM|~oٔT"JY+i̓LV wQJGO*7-eqhjY5ضjSUF*9=Y5Ќ릷KHv 7~u|UnM醻(Z!3έOX(A;-BRC*gfbf gm ~3KZqO^|#fj u/t>2p'R `'d.Ix:z@M‹S}@DB'K-L 3*:7^N!IߊaUTI?u朔xTKwZ-SL*ӯ v)} Y*l`a?p%\U[1g[w j]s" 25j¥$Y&pס6ؘnQ~4"cqSp|U@.I>O N{3L(o0L;SIBDF:,q QDW-sq 4S@Za$ CsLEY͕o(閆뚔"2:K.H9490k@\>bdDPq'Y{(z`»a/sM+.\ڬ5ȩ@G* ܭ<]@}cB)9ط$JH2عD+.T,v9Ƚ8/_ٱ@slXg5,z1V|f > fX+#_,PQf=0@bt_]ļg͞nb?P=I5EvDR?a> @/.5)k5 Ha\;\j5H?z,OH+NnKK>Q I }?zgy ~+ޕ!%V~>lk /y+qF!V/%T$ u5JFK0NsgmUlɛG,LA"7rԀnw[ )Pwt1E_͊)sm Q'9^D0sLN>(CG9\Mx8L8@-uңx?9_kz݈J #$;.KZ Hf_+uj4ah5 uOe@4Fl@)w)EL3s_=W'a%T)=q )2Qx~/[g72 a?gE#ےfE9p*~V:k9tY^ kdO ; Љ"'aYb_ݒ֭zTR'vQo*YE{/N`^iz?xRA9mg{SBj@u>+R3ʋUxRDg+tLn>eRcLB _3=\1A1{LD<7 pw?=ޢq RɊ*Y h ]4K ļoaůMߎ ,'?Tn?)C#dNjkY1N 4Jqo.tc9^0zhE$Dy=m{C2$6!֘6&Ҡ3}T2TG4$w cAD{:{JaZA!.81#\kg\F+{! X, &nS JzQO]g]_K|m ?ŨT;$Yz~B5xd*1q#]&CQBL$k?αQ4ȜuD }˹z\Ҭ+!hz{`!VY|vg ~YQ(XE܁LANDmUv` )tSA(1HuɓQEt7g Z֒!JzRѬVdeZSƛG w>| 0V cwu#FmIn%\̍X䐿 T<'/gˠ&@רEsKJ,9:A焁B!9kp6 %KZӒ\);@3AI9D_wFK bxN4a&]|P:$F%eLG6/|_0,}G|ITrJzR\(AD.Lp,m -"Q<ʬшE`KS!XziNۂQd eug;D<5¡4yٟ`&LQ+Xmv.I;e!C]N*k|4cƸ"rlꝃϢ1<h74)\wYol:Hi9]op}Vӧ/Qۙod4l>ua]pw8wBJV;$2S+wk_xvk^{:\^L$7Cԑ)/z\`GS ^a ,?E,UyvGM` bvGHShl)C#&HhĀ)Ru_ﯻj=ۢ'{&}P"%>ߕn;jݝGľ'[`87g@msY e6,I[Vut]z>bٺ>ѣYF;X_0zJ&_>uY`rk?dArk.\J7.5qmϽ24Y7j~yIz|BP_MhG~ߘQfy.`}+sEW,MapokG6k cZR6Z/v>UDR??al[u(C֊քj_3B XcꠟS?%6e#]>0c𗱮<:LiAt@K4izv`[3nlFrxnNpd7?xj%Z T[qADﻀWYc^u zt_^wS$E.K7Wb23-1dnj#bd5c 7 {ܕRXh} or*H@t|"Q_r8+D6oGO7#[phLʮhP3m1ða]X =0YDx1F$W$>my} }:Vs;- ڽ7jsE>v!&)^\]4`A<\E}EkF&fw nWT:jIWv봵&RFnq,4E׬D 'U5/Ȉi:]kk4(GVc0%F=S`՟xNɤ!c0i鲏A:A|둁=+/I *WpYTÐxVj\FZ)l=7*ӫ29J -d8 T#,dQ~$m]P\uAn[1&H Ŗw3:~1c2J,}Ȍ S';Q)Q#>4}~ ص>}n %wfj 穈=~`obT OD>)BH{aN%0{$j̹|^/\..Y?7rYOrn:( \-0j)C>s4gj=I*џ/^j.M[yW$[Cֺym物] [ﭿhu4J!t|Qկȋi3wnq}`;/Xy.+'ܮӼv&]LI2/qEYplm0nחx4m]G޶TwRF;WK%`r/-͌\ 4P-cbsDv_Xqv~a |^ nGZ+d# ,ȌX}E|-_Qjf'ⳮ}Mҩmu$a*?65(Q4*lG@+/!,PDQE'u+=n- WQ}F0hS勶 W˕2G\ۭ+Wc\GC|Y!?)sRW0ff@YČ #MESUZ}뵚ZÊ}7'Jyyi` ItH9U]{ݝ2ZJ\&ę{ݵ4o57ݞ3(/سB +5~=^w&DnV_PEqDq}Ӿӛ'ΖCmALd&>RJ)ŨO( 7D[d #rDξŹ:P<ؙeQ0|0)b4ߞԂQ]>V+m!'{NQ54Wu{R 4ȞwRl Gۉz"LgdVa4rBoz:Bf< a#B)еCBb1n/@A},1'LxG@۔`[&t̃+t |]rov3bιŗB0sЖfhm3Um|.Uu$1~BŝjV"@*j L[mei%kvx` ]A s? MԘzM n0rA45FF#ZIޑ)9*khNZk!qգD  ac3 4)~s@YX ; G>8Sh*Eud(;,`NcS|AD-\4,e͏g3fu\D-̻iCv4?:Fot(P'ޱEELeaVRp>= 3xJ|V*ճLh#vI"671CaY :h/߹[ r .;;?B~Wb1T  ïhs^m\1k9px@ieę%GTC9HS&,[q "ۥ]63Mr‡T#hv;O#'f 3x[= t(0""i>f2M7sM՜׭0V; ܗ0$r2֝q]2Z>غsv% lu\>alknH- gBa=پӸuOo@Waq(1!Sok4%`fBJ 0pJ]hzVU]M#&BE4P㈆GQFrXˀU4;_i4xpd9 $t*iG3n-J<&% _qcfA {HX$ƍY*ȲTzbXL_!̛(?sŘ=j\ ^,)+^C lfK+Ig*+)3]q ck`5,1ľj/a]X#wuo`ă T~܈.XݤC[gLs|ZM_<ag6'sMۥtzSoss!`" žd'Ig4쩊δԐkL)<] Č}^ G(\RfxBr^C+yk.Ug4FcW±RвRq۠l$"u*\TCE~7ɱ@I 8?H){|M4pl>&̴˴%Fا>J=P0FVYowH(1Ķ"E u؈s=-2՛z<:VVM⻾@SvF}U"n}I9!]TI7d[K6 ߏ~!kXfێeGPa 4؏=6P;[@|Vu(iO?tSn4nHA1D5kS `vd24I8;鎗݋$XԨgĪ޲x+Бە@pH;@)($,,d蟱})"K[*=_~B_ʠgJNKK$qUax&{k"ׇ"ƴDmV@.^ߜY//Txx /()iC([<,CknxTKbzb~75zϢMPjw, ٧3-*VG>`LQ2~U?̻Yq!0cPdGfiSXΈϗ Kԍw7>bS3N&qڲ(~R d\ugP$+<9tP?WBqeyZ# ጞ ?kZ< Quj fe ^OǴ<^}VPq"$8>{0Dq1s"J˗poiPo<12Ў2R -6䂂J=.^F!I}#]!gkexuMJQ8b̞ s).5kJaw$hH^"bo%!l e::1>g2d~N@*\c5`qd9 b`#Vu!{I,f0 b'Aq2C)8`FUy^< ~gFޑ͟r -o4yqܘ7eɼlfQ௴QEV(Q.W?P]&|QR7Ok ?v;:%narn.ݾ[@. 7iC`;NjF_Co0ez+b`BY]lbi{̕cuSa P=Şe-Fe_@OҰ@MO m|Rd$JC/6FPj9ksMQ+x63μ[~{cV,Ȭ+-&e^k֊d@?x贛\:x|pB=K,ӽ F^K rApk ʳP+Egt<!Oq-ᩚ*lyw5 LKJ@eS@a?RZxL+# Mf@,?qg0f΄녛X&i:3z$l^`yh@yUіϸ?xsYP@K,`n7B0{^oKIF0_\tl%ؾ_ -M3o{bomI5p_0?.o2 VĄpx;IƱ%9Ht qW. UKXa'*lj )_ g%7ƒ# [" E1or l"!<;c5|D2o sVfa] 'nR|OUۂ'>:132>gdfO64?O18+HUKW0oi]n6}X@2nD!hN~ Fn'ȺDGzK6XWXV}Q⊳z~, g gHaHi\$OBq V┟!lLJF$98PCf:dNwnFN\kKqC /j〇FʳDp`\p3/.6WD Vt囬Oqpdqp(aBR]X{6=я.)G!3eQYM8>s) RC<˲tyHtQӶZwm9G:@KU#y2^ Ӷ(U'΢{QJN .(tN%0Drzu,^ ǻ֕H1%v۞m^':6Vhy4D˫v{i^oۢOz=|=)S"ڪJjBiiQi+͠ TW?r;,9n):̦C# ,Et+]`"^jCPB\* ߎ0LQõ`Ai8+Wĸdi}+"륬k-lz0˳.`{>kטaΐIfoSQRuƑt 9iB<_nŰD ۴%5taS4ȝLߚ:-aB:FTyjlÙYd`(k? yBLM g~b4FF!ĝ✌Ry8h*|< vg{Hx:s'w8j4?򅖼A]p3TܲWhlkf g'|0o)#.atELP 汙 w>M&_.,040$?БBd4P$f3¯uk P.NwrrzGc<`%aNzl5@gg(WvĦ0@P ֌@%zn)Z!vur+=?E]/MG`ao%p5?!Q}T*LPi#-PlZJK8!ʾN=Eo$W:JTJ :cezOfp=,f?ӽD(ꄨ.KeLإTtNa[:~j@npL k1_T K#r>/aʼnrᩭ]&ȂYxz4 B5A?P <8Ylyl1KsxFkHaN[)[x$/aeU}.P1R'ytcj4KN*M`W=Sl=ɼLd&WzC>ƭ[r3b0.T/Wb2݁)@Jƛw9R r |y{߶.N*Ki꾯]b^B,sN8 BdT:<8&?d9781!, 7SSyѽv@=@5:S2n^iL5p,!]Ug^6&e=@=7IL,vQ'mQU` 8 #1,sSܿ CTZKq+YjA}A"[Vy淸6)B1'c T׷-wbZCJB;8ƢCN2gOR yhc\5Z6k=@875UbQ4/t$Bb|oH6U3Z}5gG>@U9u$|\W|s 8γ|q^- 6e0R1B̟rۼmUzAz锾?S rMy fe٥@ҎSN3UژKZz£Wܩ@QXmx=KG$KG PQw'z7(~. 6ЕMDRqt?4r/6rkFʮ~gt%dbtCE( Q"'d]4ۏZJĿ,JzA;$gϡ1ջE1W [7׳0{!yُ!nc[0 ɍh?.t; \4K {˝2P^}N_ _ Գ+S"EaጎxmöӀ>⍧CkRKߦPe9v\K$(Bv4HB,Ʃ}?8PV#2$'chB.tGiTѳw uL"6GXDaO5s11U9S6O$E@vslMawʮ6S %RNl(Eq]/.Zkx4ÄL$ T%z9?4J[gL_sr ]BBWmg' CB-{6fގ͐H]2kGnwqp:)5?A7d~ݧƦ@++) IѝY/`yA/hy^EoPh̀1uE|P߹!)ʓi0o˘^wgOm'=Yv m0ϗ4YAa4 2 ߢ! ;pp2]Eϯ,jjvzDp j\*^5љ6RA)se6і,n⇐XS$e&4fѝ&}eռc02~ HndNBi}jj%`PN8:?f8ih״%kL?XZ 5." Rmz wԕ V&`/!yUe32-.:D( ylez`䉛ͻC NgSRb@ 9 Z&K/6î]?Ԇ GU/1 QXoxb2?P^ TvXKW$Lphd(C rR\`}^i+43] (]Uxd,Z)y%11J$.{6z1G?bBbM@-?׶F[]ْ@~ {L b Jp\^H\tsMԙ\l|G;(\H;^e70ɉvZ#x\ )/4~}2Fg#>BP/h@d[־ ÷ɽ/s?gBiQ:Qk Oޞ% X`.5Q"h_2KgMudYZjRvɳXjj9QO¼PQ4O, B Mxh_ournyIv&:28Ӄ54$^ E౞䷏[ELmU\mgvz$Y&?r} è߮_;0݈, tLĭZ`L,$;9U&|WX(&B{Ew{iǭfwEW:ϸeX`&|byzE:nhb]8.8;`w#mJm$;FjK9#u`[ΐ4ldW*H{BtnFI0}4L@WY V?t(t\i4|2zŚ/]ߐ3sSl@h? ` .?+48Op޿" J ֽ{ڹ:ԇDOeӍ͵9}$7'AbZ!A0A'^f7ZQ.9;0 ^zDb ;bDʞؿ $︘zc1ԂX`)[,Jˉ|7o}62|Z?7.6AתbAJ$•$N WgW5M\j?D&bXchmȿO0?(<9[Vǎ+EwaB'2^ n~S֦ܷe`&d%;veYʹ8y#v <F% ٶAA2u(g+ŸB./4J+6aסr)Bta)yD^ sezz'(im4} Trc@Çl6D4o'-``z#R_RÝP"Z*)(h3^&`5F&$m=A=#Qp(s{䏨CP$ ҿ3y_zqq`풽':|5hov%Ә5N}SlYzjz8=r%͙^: 6{t'Tw}\by!˱;bL_g<.AU/7ñU1omaJ&"$7{||}$"AMez?)6@c).\1I;n%es; LfYyYs@˝ .i_$kK0I_HǞ7 țZߛ )۵pFY}nCy/H&'?>ٝyN4ž7i9J{ۅ m(Zl\$˅~Pl0&:ڄ,/ppCb+p2%xUIC%l骸B^%i v'B(cuZVswҹ,D#6 #7 @}&1Q:R`J[ɀࢭY5CY%JL`a)Pu~saܸNuW+T˪7spb4 XO$(>q CK-3Ee7jawבx7y(U0s7uNbĂ[m-,HWe=i39ޠJ/P.4;z }j-Vi'+"s" Wyyqh⚼un *͢Ϥ Zra ;6XɅ8@XߎjޥF i@iURdM =pP8K!@-$1dK,_fގ+'FNkSnkXյB!uħJE(kwY\ \4>>ʚ(UEF" *׆:L{^ >AMtmNV{}Q"O_L0RsG\+&؃Yc5ϔ}d(H U_MgK *}4EM P[ ?98Ek#2:qJ8:V!sB)qM,cf3Lv*.W.y x0b*e>@d)&{'9QToa(;v]WhQAr-T h)j0Qjx` vܜlx)֞Mp]4O/.lpr8*_S6cA7ZZ693?(o=kW(; oqI "+,+f\jKG?X' }dɅI_u-,FM>=&/SvDHlk҆~.^k9:.͙ƚ/ O?t=@YЈ%{g_h`,+SgVF@tH&a|چ`,ޡ-/ _W[U߯eקm0C&1V8G5wּ=3j+H@+ °Cjtx\ m3༜Pn)1" Rs]1e?֜@4ʕ DŽCf=rX1j?P#:F~2Z5ϸ9frKzl6@8{DQ|?1Qe33hNaOst!#V66-\7ҫ>?`:Eq['Ⱨoox&r'#=CsxD㇜PIURG^Ƥ!c9FL~5< {m'2dyZ&;xMX@x;FcG+#*IuzGȜCM^{"4Mk8r@x>4f9D Νs/H8'gY! eFǙJr*Tٺ^vΏ1q2H-&ɑh#nGgE af̳L*p(r%W-_5Y _MyeT]wJ$宸τOAOE0"#?uV'aQ=Tx  C5'`P41rU0xD#YmW42 Ftt{@J@ZfBՓ|MmVi |~ E!a6v͟FJvq;DvKhະJ#URQ52%gȒm>mLm]%?*)ޖʵV]??fjQ9Z{# fW`#Dk1*d4'8< y*SE-S-@&{=^b|)t%ID ׆JHO,tH_Z]TcScFša*[(6[e^ߩc8N\ gK˳ ڿ{sxqnhJ"??ZҀ#Uf(E-MM{ i7z 87.Ҵg{"ZI!<&Nδg"Ԋ{C r/Ǵa0"&R!Gy83#2L[wpퟝ(nfNIGKd% eGOEx9qm۶m۶mm۶m۶moҤMd ; X7dcU~OoL֨[WxDLB } K[KK1>2{-D~r,ٲ5f(1'^>E'~>}ˣ' GZfI,Us{9y6X {ς7Lji%sDg)%f&cD,ZݦvFdu]#Wm9)ۮ*Q>3"t?~5CIaHvo_RLYe=C-yPLa 3{ؐjw,y4u?bv5rs"IrX S˚bb|imW:}ȄͿGn}ΰ<Ռٌ:M՜:@tCEVPqËoEҗٓ l]8ĻdŨ5I XIXwgugy ޑhxڑrńYJ$ iϋNV]nlkLca)TW.wzS2ⷈNnBB@7&򠮇3b<&x^ ;`hAnKс*EAH b*}2?C7_;#Cg`N'y d8F/ ԃf8z)#6{O@]=H+BF^qA?,c jX3;: p)^E rzmX[J/닔 Qݰm3NAaMy[zfO97#>߀]@ݪ;k,ԵHVqZ@8WEW4>y)"IcKj U`e9 weBpE#i{ΐҳ % $\.UøSn^-ZPoXx>F$+2.m`XNps ։sSLrJܭᅣƴ(I@M_&5b]3i}SUFx7bY׍Jp;<&^1BzMbi,M Ƥfo_⢡=r^k7[ızw4 9 *pK;,/=?zHv 0hBXQ$ Yic;6Lf RHt6PԬ/l݄4sSUBUs\cJoSS( 'zݜY@Xp{c.Cb. {M=y`2IH?6J.gh?] _}CmAy u}ioh_2@Gɼ6N2p}Kp,+ j`U-CM\T%\.cSm%iۨhTNoj 0smYAX2(Hnxg rV-廰jiڿC!ožR}?R,2]BrjcTbQ_>)h>߈dwh[86jNM U8.X,e~w>TYNϙ>\bOyFrP?<qmRTOBO\_]#Ձ23`B%"dŠ SNKGOLj0v!0\R֖:]E0-uÌn>sD"!B/H_ 6~  s$ZR#34D/F1`Ѻj> җ:9D~_^tk|ϓ[6Fe'Oa8XIUe a;NdFKY]Ġ{(~u$7JDZ4W7Dnsb栗ªdMZ4L?;\&MyA"侜촘VPg*Țw|HG?ZsU;'mnUQR(]5YILneﴩϪҤcq;zSݓ7Ɏu|J#H m1J!#JaWx"lb6CҲn@ӄpΈYg]῎@F}TБ?"s ?I[6ejX1v+2{z2otl8d)"L&11䎵SXFttm(HW 'h刬uz Jx`3t\qfkì$SJDη |G~ԄǏiz;umlz |Zj)#̕ru>XTw6cX&}_b`ujF4\3CJ%ORy}1t!9 ؄!v!|Y(lK"ku]1NrpfyKzR'=vG?sU${P䬫aeԤ[ꔅ?س^oT/CyMLᅕ9 K2#T{1 RuBV8.O+ym.zI[k_kѱh #;6;@-=Me*Y"ݾ;xIsNe1kK+FZv)4/=To>T8 _TE^s7U^VGr~)n5ے[<1]C.+&"anUJ鉗&@`R4ҟ3rHQTJ@F <;_ #K猆T+Q~󷄥9`W''sK ܅?݈7>Ͱj;Pfíx d`&Jj")Qu!׎؁^!IVï @pdFMt8VDW!Mfv3uKcU/8IC*Bd sf $в8 v^P;EB!տ-vS.,Q522(B`He .G0-xG!B,P0"\qGhN ŗV15ON ݤOj++׀xm1pF@zH6xRn gCmHM^.竳?)st- 0Sd9Z?9ÏN%ۣkpGnC ۪ bQG,0bS@ Bm cr*r9Nd$_IT usT)} \#h9AO>bNao}l#cL(PߜJ{BK' ^[T\|{M:U 0CP.2ṱJ@CDq2:xO88B-R 6p)m@p݀~Ds꟝Bh HtD\WQU jf&G_z ?s$]WKe(f瓰'M|"7IC#rDo{^oC$8j6KtH6zC°$ 1^"V&BU/g`=)?3'b?M~=ڛqH]O*3h]߯3NECO׹\~0>T-ARl#by >o9J۞`._X5\d}5FmUtA2%xQ`C4z,7D['BJ%BmULMU't:M1=#WH]>ڞw&#ʝ ~eI%:Br| [1 0cl˕ lR 0ߪ-B{ԲSc@w z="2`/^XԺx12UI tZ;Hr!xKwBS8xZ3ʥUUM2V zpߍq9f*S]jHAt(?#6!n#uZ?hWHت_1pA4gËDh>-ʼg"=:tlqg̼VCȤUۼk{gI4vkSA˱K?Fѯ/4} RLvb Ѣ Q.YeN+)sp7cH`7Q.Y+ %+[Ө =]׃!xI|y \J"o>'Tu8Q<󼻱×x:y&5|H+sU-Xz!HՒOĨ!|z>x[D5KX7 N{WO5o+x)n3a=+mSAyWqCKBrr%}zms9|/M _>4F\gpnڬ~&0͇%4ҝ>.Tlu%plJj9H~=WS hsrF{% Ӏ;|AzpE*踲W1 1xZ@;֏ 0-|^9XcۀxjX ϲRu1ÑIUҩh>t>u&s Rz 8t g*1*B,$([D0 f5yV𸼮=slq=~Mg2-s09IdFbɓ<ȺS˫b:9hB ]Ƶe!@#ŦSgį|n)[ՕleGd#aLB1Eb5DˣjnXO2aE:L_b q\\DXtA@JS08PQJAJIZ 38znǜ3d!<]GQ> -PEU㡎`{2_c"![ZB=qHw@ Gn|Pa>ܦs6Ѭ(KuцD-8H'xĚ8W3pj/}.2=0~eّGJEWg])#dm"otp5.R5TsD*lnӀ ux(# !T6">=HˍKnȱ0c Ht @SO-r<L!@&Yx|c5attWl'4C>`li(.x?a~ْ_AH}d C/@?dtciI f1Th"Mi#Z_IyN_I bXO`ٸC8<[ *0`H ɽ[߈,&(ՅdMd|VEKc@;|z @N gȱP|72!* %~ꠧYm22.0 B\?0!Zzaļs<r^{2\*u,D LOĨo{3WQ'KֶnKm5j,IbBZe9jE,P҂ @ vФ|!-Q7a=qƦ:J$"}jJ$`])ϼ/J,}Z%Sh C)U8[nE״x]52$tФ.%&;DE%:G;.K-QI)I ;-fyrswL >CKKTͳw& e>HaaGh}}د/m*#1S96VX/Dz' ߛ((Br8vrw0/*&Qv85we0 nmw'#Vv-nbZX-H2.Gtw,3f.L&#BOJ\I {~7Yq:E>["N>=Rf^87ǒLZ*id_Y -Ie֍8 4F ךNum(m7W15JH@-aԦ;Ƴ- M4 ;)ԝs~ǴCk }| kjPR5{/|J꨺g [jI4r#yLNGķF @,/Vy)\js `XPӴEwbmOJ|˳ؙ W([~T(t[4}يU-*叶*St&. u(Y$})oDnnJp¾bkqR:夺֠-d7HS@L- ˟$(|,c\Ò(1X.Zx=Zțx#V[ܡd"ܶ~time8͉$~M?"Zw˙4~@=8vU+¤*.ɿD qCr3.Vٹus6|VLAVj֒w#?}kU8O,ZJJlJڦ|;g(`f>@a%3|~b`-5=:K1X($@qoF9PCZۂ'iBօKOy1] TpmϹP*g02+(`\nW!#d&v O?\70[n'aW,u˶_ #&apX4^qWbnI Zc&ݖyWboƵ+ +Ix-+%bPG؊ΪzVLn蚰#wq鎾ӕ PZP-}eމf0aD9*)>,/c\z1T&T7O%<*U jUd]՝ϼwzUH|)b($G $sܬ%s,ൌpx)7bLFu7nlKi~$dԌ[k$UbT=UyȿEV=NRR^QfwJpī>i@%Ǚ-G-WI;?z<<&.CPN<. ?Z[5b/nT?M+8ծZSU4՚ GgtS"393[L^!kI`.~!tO oqHP^gE1cziUO.ñЛH]!ȬZU d#U?`tmG17H& ڡ ol$ᙗGx%}ch]‡fd"\t, Z>޶At*+ҦC*֨%3jT37">\`#Wn&dpxc @d8 g58vd5F`B`M&DPX TN*زJt"f{kJ{"ڗ8QiYuގ)؄`w3R@G P@>f=Hy@#&KG>R컱[8y|ӆKH$wHcGT.&IwU}!UCq)C47t 5-CN2*zB,pҎD3J pZc7y )LuI`lFbFB?;)|I%׹\({Вu ~&>esC=XAi/)x,](Tf)|-b"2Գ:3z%tx*(n@3z34ׯN`;=MӦBVX{gT݋+>yP{q+_R݊Kx`$9T2ߖs1j5e >㾅7?νvx*D1b {]Ub-,ȊA/4o8 \nZM.#0vPxB{^J|ut?z\B)=\䵍2z(-J7N"EO=< aRUpWK[}=Yg ̽fË !)3w$CjcP=Uz]iqo~aT, yB X:GL .>UWC"V˲Y ɀ(F 8;z_"5y"{uًoo]f@44tqx6.iB'!WdpFCٶ di&Oqbe65Q'd(@WS9bQ(% c*'[~c#Y5W4947; x_ljȫLp6kI=UlI~Rl)o#*r QHc,X-{ʊO$&=}MƢD)oUdԵ-n2W Uޜvm d FABhk'\ ]©3acj8SW)dW>oi QVQ8\3^>Z n/N:*oBCſU E"Ul8gUYϗF* :B 9~oYD-v4ι*)$YpZ?Wh p ѴWˠ=m@_<"E/n^ZSU+W?sR N[^FgnDR9/'¤X.! ڂl*0ވ`1?Kr=~U#w'u1~NIzl0VyPmB戬?ϷVJ5;R<,y-wx!HrC.ف잌 YׯV$+Zf#NHokS̛hAers>IQoٹ,(wnfB3;Sn?Ru\r|/c8n7 $dϩ5^eQeO{2HoLdf*C9[ -Wka 3\ ~m~b5z?_Lf\GL#%W`S@='eU$ON+C}"шSx}<~ z/WXWn7+nOߨ)ͪlLff^%5YL lbR*kSD: Ssɿ,KM*3؈y*EBWiC}co!~?,b5̉^jAeHa ]-ի_͐{k4 2 9N;PxIV8[h7d[]`SULft/H=.LF(v|DF2[b&Y8[NQ#T @+*Q)jAeO1y 0L{낶@nB 漽.̿Yg/#E8iyM–vɅ,J<og24\50~B_k¬(]cwKM{b8mU8Q%JZ#lUSNwa/•/d^_ed- k{GU_L94'a_:իZmVZɃ Eox.e\<xZ"F]FljQSiPK䵂_M;9g(l^h7ݻm)ʙ(Mpx*OoVHkJb8|da U ^"Zg\;U{lI~ao|: CVHau1gO~Zސ@#1>F]McWȌfBGs2dSw<{n{OR97KkO{v}pMjޠk$I|eXrV¯Hֺi?oyѯij$! ] 'Zӎ3t)#ݸҞ#nJC*ޑIAi' C\mUTB!23>*h Z (Bau>|SNlXfwc.c7Z+yhi0aneޚ蹷 b- yAǟ.11Z];%+hz]|ǵ bեMbZAP4+]>#lK;#F^ D0$hvi\ =`asMsxo"@:X>xQTR~qdw6]Xv B9V$E mg١MH^=֏qr㶜?\  bwaLBzZr}A8Yjk&(%AVE&/ t{4F\˕}rKqK9ׄBYa^,vc"g!tU(6 ŧ-£-ulFx`f,.#(&o+ E|F! )6{xoʭJaR7L@EW_:$B5Q vFenp:ԍhY?ݹ>OhYi &|3IZJ~x^B?#r8;gc`}Y2Tοg 'N[ռ/>ay`z$=$Sr{z'Cӱ]V Rr.h=a}p3>ERv5l-xuq"B]'XsΏ+_OҢ7*~Wٯxo-{6+7n c@bTؠs-O9J$ֲikJ5Cۂș 4;zk3/R?v $vo?Y0`ܪ6Ku 5f!&Ct(_C"fWzp'n;E$m08NrlV2":G,y"t{yqpK1kAsqL86O "Qm7ٝզ3h<Ϣ[bef,ͨbJl;F#-&jŌgo#euDD{٩xjZEL`-:5 fX>?mV| jV0Q"!f6ƾ/\y@hOy9\lG_tFSMm|!At  %XjO/$~su?B0y!H+-!s[Fv~9״6d-M`jWNci{|U^^w/$9s1\v` I2lOA?pOK!nʳlJzs*#ٲX53 Nd̜ #mb.׫t^N~Z\nmuOZ=JVI5fL%[(Ds`زrO9j85im݁]bͳ?ͺ2RŒEW]kF|L:Ayˆ{'j36yie-`lUx/a3824'r뇹KW ɨH4y^w2^\$y:~o.hPx,iz=>ː epݴ0M>Q 6!x4Ӯ ȱ  ze"cu[.=SYdo@FF<]ϒ @*A5xv֙]F`[C9Wedo _F !-۩3hij,a;ᅱ@% " хЃZy)9tkU9M7bL(>|9 WLJ&,;Pe+ŪX.65b'Wsz8:L"HuGE7PPbT.PI_'g1)懽6Y؊n/9Qw#Igfq 0}O^y&n2hf2ʷg ɡvORi94Ds?dJfZӺ]=_$M,:yŞiz6m \.$[oD3~N2XVˮ`v>(/ъR:>yƐa?Mc m38q&߻#$}ٚAZ7O3ڙ@ӶjY;[!oq EbAuN&<{3dOA̼NjgǞ񁏩 BܗMy'Sv( kTQqq[K`fW5wA]ڭdnJb%OD1nsPs;u!zme|V5T\N"nD!fJvn!VFz<=APЖc#ݨ`BlmllӬ Zqqߏ)4@Γ+/KYs]K`#V& 1<2r'\,i-z3J"tҚEXfb/6t%!h4uy!@b  ":BQjW6!cWHq;tDŽ)=ݸMY; j2 0I @ ]4 u)TRPƲ&g6Sz?ֻ nW7"C ŶH{+?ngѓ<p /L<áNsf_b"J }Pb8$Y)Zyc/Ei[  pı \m3)DOi+k&&za%^|;S;[* xXŭ EOrw72n-FZ-胲 Q)K*+;˥,M1u5Zl.ykպhFHK>*o.23P5}hǬ#litpw-jsYhP֔n SHK(V5Ax0nJ=\8l;*z_ 63X5I ؐAYZqSX^TCY5G9L_.c Me{pBxϪ,`,ܔbXiAǼ]@'h?+ߝ UDSR3GWx}/@.G{,*F`[JXDO?3dl3w`CVz+6F&t GfM-/Ͽ!(lGqMJE`0M.G's9|/ں!Q, +M=7~cY24ɿ,ȳm?Ia|g(;L`KeGv.Su_]z{K fWӀEk,#QKb(Mᰌh#l7kr<+V)Z3SmW[>Ț9^_2NqY#P l]$\ei-pd'ӏݹP,_({TS*R.|zE1̧7Pӧaswgoj5li)5!3ݧզ^NƓ"+C>! *;YXEDOc+MF=[)fqfoV> aZKl.Ij#Jmɜ5~=5;+F6)s(-!Rimr_K/y=mW1OL%xL#?flkqHpffmޫXw| R+r'ήjnčԽɬ`[ /Y#ה7|8vjڱEg&VU︗U2a* Ib~9HX!K&fC  N֯ԋ"Į `;R9aSX@1.{3ZRu'|*#_{'wOTW&H?Reyrh.ITٹ3ƫ%uj¡9>n6w^.˽IV.Ekim,(hH*MX\<*rb>}{D@:iTETHc)[p=cL'7/gɺw]mCC)IcTZ`hYӲ0B1~#/," ;oxs?9!SH#G+̔d3w7ЭϗaLĪ]f8R_݄S7{C[^0t(G}5xslueì/{E1~G2|$U-6ǩ]W]$J¶IpUSBf7nI " IO4_,?+KH1Gܰ\'enc/ ycdUSa滾YC)4_Uen?E3vdh,U ǐ@mAQK6Μ/w) [Aɍ\jESY'E;IB eZj /~2R"MW7t1e,̮B@E?ws v &la.% Oqʡ^OqŚqhU1.!Vb%SSZwi}8rcGɜ[&x0r- WZAqkpC4?j8nz+CjZ@QH腀 jlAԠAZ_cι)eMAfzZt>v oIŷ u+˶"ri9UeOb݉yeRH0pg"{EVKڛt)!:5nTr8*G10΋`. ^}[MAJma!-0RIōdAO'VXC^TLr/qKJQkYc;%ݛM{ M7`7Zap[CM\ꇂN fp=HY'{ CCmy:5;JthrQx+ZNp:Qd{gJg+Ib{Qk.uNt?\&Ї (d#uQ #Yҡ1G'e)y,)x w aepjͪڰڃ-q{ɸ}G֜ՍxY $l-\${^3дm<ڡ +WjPD{I1stZS{=\;݋jx?ꡅ 8^4Y(ν5@͋](ԑMeyk)".lOo?.1sJE=z#Wf L( +t@ LLfo G]k(3kڈB#J"*a*$5!L"eR Y βpI(4̪ w17~QyΦA+&8 (k[R[}# rUfm ,fO\F 0>x8Z V* : ̶Zu .,0Z vy/ 4f[ڳQU=K_6Ne+>hf ̋R۷/=*FuV7*qNR] , y?,Wg9\@zl>fVbU9fxpFvS@H:H]:>ڒaw a6Adk]A s/ ^T-WwNf^"MCMê&SӸO 1KamZӲ,پ%0}gtQl I$92֍ěrAd,?2v yXr2M)̅X,]udv-=붧o]7[ {˦4QʒL_IkDe )|{5qDAC(=$޴塙U)ikx9] %88K3(YU9#&r8z>[9Mo+l'7n ܚ OQ V sCWu{EIGFcQiv{5V7fu5o%f&Ҟ Vg^|beN@sOy'ִ@x _q*qd{2aB'mXkW䳛9QAQCN\^uԯD)(72ǀZvO,fa[4V1WTt$_c+5QUTi?09i&Z$瘺աU_3yn!RʉijTV#~qMv *'r fb4@p@2%D2dkxFʫ'ƑJYd 2hݦTuq(ȡbzw gQ"b%K1o4Z(VBEh2O(z{ d,NQdr/=:ҡG7˴wTbЦ8ι;Yu9q˵zNi †tbp⧗] u 69p5)MBoW1V/1XX#Rhb~¥$9&p6ؘn14" Sp|U@.I>/ NsL(o0L{S)BDFz, J1D7m qM4S@:a$UM#sL%9o(閆뚔"r:KnH]9490@\~bdDFP '9{(`EN»a/ ^Mkn\5ȩ@G*Iܭ<=U@}Vq#-ܧ|7~A :@F9`%>$ jZ+8 Ca) 5@$$2X.ف7XKDo_,-TndfdI/D ~P~GZ%_=F9r)}hyVh;v Y] hdO {; ҋ"'a[b\ݒխzV$vl*YG{/N`^z+r0)WxRFgΎtLn=eѼI`LB _3?\AxND<7 t3?=ݢq"jˉ) h_6K ƼocůM9܎ .+$?Vn?@#dNik[3qM Y4Jl-vc9^0yE$D{?m@91&5!֘5&Ң3TTG4$w`CD{xIcYC!.8 \d\Ex! X. &nIzQO]g__K~m ?ŨV;> AN"dHe4,2_ )}Ï*׿-K⩿T X*`XGa0.呡rgj~ 1ZȺ/|D(*$Yz~_VT% /$Ww SylS Q9T]9u ;h~oEJLr2C]`kTbH%%mA$wGC!ۜ5F%iI.qܠ"tϯ`'`%1:'BTΰX.v|s9(xBc'y5\ )biE])K,_1ҦYX :U9#墭5';(i\:b2JB qv!#~/`ZTY*9%Q.{|"ip,c -*Y/g&|ޗfsرce Nj\#lg h#Ϲ/ߨBj TΈxjUI +9+8X"h6y-t9m=?[Ș1` *dc]ZpBbe>(tz`J= Nkb-p 0 J}ŋIh2psۮF et@\]ˬ!l,`ί@"uW%pw0B??d!Èrƶk.\*wkX%^{e h(r(Rp R \P_MhGB,,VߘQy.`J}+sEWMap4ofkGvk .cڑҶT/dUD??za>J6[&ut(CF6J6_3 jXcSL?%e#]0NcvnRiTAvr@K6iF`N[34xΐp7?x %"ڒ [qAD*ﻀŜWZYc&u 2_LwS$|EK7RW^⟠ 3-1L#5c7 K2=* ]Ht~BA] ߷s@d Bz]/m\]bc7>2PPy8keWQKӁv[2LaX۰pyˌlNX |ۘ#R+ӿv<~F,OGk@Iʫ9}NVFSћ@4s9b  m<ٔ/]n`0B n<ߢɾb5hgu3;_m7+uεd+su:ړޣ[q MMYZxl&մ(.ȉ:jDj4GWc0%iFHGsPH+ &S2iF(AػA#.aDGmbЧbzd3k>IA~Zn}j0Bm݋Hk|vEFY T₡!!2 UGq c*žϕ䴭 ꘫ.-+sġ`DRqXl9g>&a3_'zH r Bղ8]noz?CCogG]chP{koAnULgvkھ͒7^ h8pV/R J@YhO/H+or`0Kϳ_`*4QD i||_XsbSSZ_aMb 9+]mjG.5t7FsZ~+՘YmBetz/r0cWu/c{ Ң|tɐngF'rbK¨ϒP#Tq]}.s,r,&Eͧ 積Û&n"ZQ!\RR|Mhz^b5t??,=b PSH Víu;3Y:bʼL6zӇ=ؒU/: fʹ߹-9%iZ\Bg:Zi3_S'UxoCCñ;4r}z]_6-2tq;wySeK-M'/g'BqG%pt3:s1@LϑUx_}a:y!Z;/?i2RBگWв 36clGV3^zIv!6~z6|DѨP9  |rɪEB^6uPn}_sv/ܣM/:F:7 V/W*$W/mn_yxi$ž  ead,.K]m"@˜y3GZ1B3*LMURInғk=T׎+bߌ+g_=u2 3$ѹuuw˔)qշ^g /w"f|U:GHߠFw{, |϶@7l4[ xSIU#Y}C2&JMo^x8["SXTvy[K+ jf(EE,eap=3xB֪Rճ\NhBn#VI71C~aYR h/߹[㌳6;;?LW,1ԋ ïhsmr1Zk9px@ide6ę%Gr"LY&6ADK4mMfEG3wFN Jg6Fň{+Q=`EE!|5dn.'J;.i&+ [_a Vw%x.aIdm:dAy|“gK & ;- #^sR}ܑZu#:z6$q0;ހPd,فƠsPMQz^w٘ +7M*kt[:Tot5λj}(&TG 䦱 j9u(=rknᮙ2 p`EI=Uҍ fZx=Lv͋H6CV͂\AFIUgŮⱚBY4E)S&zOg)"犳x6ոQk ],+(^C lf((d*3(]qzzðKuok#oXtg}K94Y_A2 6)+ByA0N] Iq>6Eto|t;1y &7c{l?OO%b 9vCʕ d& žed'Mg2 쩊δҔkgJ)<] Œ}R`^$t7:@#X>-ڥ" V儻}_ײ%Ŗ4iFZ¹RвRqנb$"}ݪRTCMq?ɹɗ@I 8/0D){|MJ=X0FVYoH(1ľ"M}؈s=#:՛z<:VVMⷾ@[vFsU"a}I5)S\I?dWG> ߏ~!gTf׎ePa4؏=6P;[@|Vu(ej0tSn4aHA9D=kS vd24Ej8;遗݋$TԨ/?cUoY>U]iJ"Mux\Wh|2mؽ}-[:S?,^e3'{' %2Eӿ5u3&ƴBmV@.^ߜYV(/Txx /,-iG(_<,GknxTObzby73zȢKPnw, ٧7+*@>`JU6yՈ?̻Yq%4cTbWGfiWXΈϗOҋw09fW3I!q޲,~RŎ bZ`P$/<9t8WBucyZ# ኞ8kZ< Uj ae YOǴ:^}VPu&a$8>{0Bq5sJ˗hiPWh<16Љ2V -6 4䆂J=.^F!I}#7K0':%(sˍq=[E%'%[\${k6DleTa}5D+0JBU;=u@}b<|ޱ1e E;T,uK+ 4jXɜs@ĆTGC* YhFG%Q|O|lRq(|1\ヾxIZֽ#?ZS IAW, 1i1ofGyfͪ!PXh6]egpܡC81jnpF~tJ ˌ4:\}! V\-o|xB%~N3'49`"_<"s0跘(Ǖ+殚H{ R-=&H-VҁimE^mb'r胃>_,mny]61)TYW`WMƑųOiɈnyi?B[u^s/dwYw+읕1A//j .ڱ5T;h^@iھ,cӉ(Ec| h o4wP˻ٵo`}R *" ɗ*3`R]7Ig_2#bq}BɷB=ۢ{|% \Qkj>JN-!Xf&!:a/uHߑCz,!G&.pA?^Ƴ~kp2,Flp )t*9|wq$7V,\<iؒwdž-*Lyx30DVkPO6h?d w>IeCRُIVrpru!ȝ8k +Lx{Paeƍ9)h}r2S~@qvꗩUV*'<qxPDK聺WI2hTՍ$P ʜ}B[7ٞ= !Kǂswq2bESl2\pF?̄QN( Ge5MΥH#F:9n,ˉ1"aE=KӾ;lccgȁ?ҥl[suKC:q=RspThԲ18uUDqu.$ׯc%\ GdQt7;ޕAD -(j>ѵͷF#pɣ%Z^K{|{ЗyIQT&T HKK:LL[i @ne>aqO}a]"TlbLy-G?F28o{H4u\['i;@1uTGT9]juM LG0fG5FBl؄?c&O阪A$iqݴӒ-  &Xi )jD\Bduy[ޏ8cNw:%P,+r|$QLNah2OȌ^3xG _[J3A}JP 9aU@gV,9bOq6\09N$yׂ"̰o\tmv-/bőAFcyϕm{{M31 7 Vj*J]$RL2!'M8#uhv3-[ !=uQ%L.ZXP(ָ#N]a8s0ˮ8LngQDNP.OIW,P_ؘT0$#SACr wl\a< `i_g 6f4G+3M3AabnR{v_X Q]̹a,Ä/fz-4bH )Q6$<6saBЇ+%܃E}U::^(vx&l|b^ d1N"rNHuw'T>̉[&#,όu T~p?,b` 9U!FZs\f4El2G"jԮ6ϙ]F "bµQsڶm۶m۶m۶m۶i۞YTRΕ1*yreue[*6`pFܤJG1WDZArbi[)<+\͌OmT_3STiRrxE@+)IgĀ9ֵ)GT|dߊ*'ɪ B!,㩬e?g׌He3Pղ{L IʮiK9P2MpPxM`@rvj99a7#ex)`?*xqiP|TFkm.9IJg$Kc Du/Lj#C V&'Eb{˞r?/z66>sZ܂f+Q&tޫ,)633y-ʢ"6`ƥmfC>] 秷IB/E} ;+Q M@F u%Gdv|o5}`;j(oʌRP>#6IjCk" /gD^5;`pt<]~18 L|~uf zo&oJS3O sV@a? eW0mwm c^@Su;~t(D'ӜH*](X@y6XZvJZZ]^n_kWYi#mPf݆z`䥥Z.I-6D[oO!걳`&hLT*"U*FWLI;5&pFx=Σ|\ˆM}R*`²ﱨl_}дC:e(Ʈ+yBmWY<t]q-Tg+rŔL1ᖼY•pny9%mR5„74<6)Df{Lmnj u]4vʬة*Pt<,A˵J HM I$]䛏s+ulZ'ts^)^".bޜGsqHp?v˭ZRs)U#h}W]{WFgj{&ksIӼ\.rU'7⫵V&Rݹ$jT4ߤL<9/ԥvxTSYjz 8~9 RzB0&\i慩Jteq);pܿ63=/j҇V_) r X=>F@BuE0UC.I85P`?dˆ56nvދ).KaZFkz_w/7@12̃kbf(I?x2;S貗mYAu&}IU`['υ=0K ^zpjFD""8;Z{hX9O|hQVr\ 5ϡK`vD7[ā=*Eh 4o1~ƻfS,z X9׭U5CW?vδE(?⥎qԵ3.&#uT3ya-%:W0\sJ `L当d={M'~ xPq؋I DV`0Uepk67gR?GHh;ua_]΢׹SNba{6Q/8}f/4F!lHRfL)0Ǡ4Fxfk=Ygg-pmCҢ5RO[Z! [${Q䁊l{p &Z=AqЯ}~F7%V*Hp}V!R?l`u8 0J9[r4UbVl)RLKiqmbů?_[]K("Tg >lEQeYN$aDiۼنuU}WJYݺ Ymidd7#|cMFYm&)~ `֩PDߣ MBv+[Esjbt,ڲ'vЙʘӅxb<]UW[W̿g'*cJ62n`c2pf}'vt[>xқm Ygxߞ3F|I<115 !@+(K`Ř'-W'?ЭPʪi_x_H)΍dbb]Kle`,29_nc%q9 !:KRyaJi~ea5_^@y\>%oN,!?^JJ ԥ8>E "H۲#5QkoĖ+=T4L}ATm4Rlg]\@< s IK=&倕e~(Dum4k's@bѣ$Ax:w 'Z4CeDJ8Up~>l2FJd_4\_8OCcE]wJ@uY |H?&g_XգvcC0M4,95 ,:)cp2=`5:_:T~l3Tbw䗈Zqc`' v}ջ-R),gHs7 "!ͯ4$ ^ 1x3;Q8vPѓ#ⵘ*uF'ebrvahԽ qi6_4/d&6Vb(_ ȮcY)Pig]^h`8ߔʁl&K' #[< ۩C(zBUȡ+uzm%Ïސ'j#%hm?́.&2wө&-6*+j9e5rl ih[;E :jСЍtmjneIn&& *$˓-,4AE𱁱G@`U\}MB}wn f$E rcxP@;(Ø<t\ԽZp\<$'%M&|OD(6`gUogy׽m`vwUW &ߤuXp&|refU:~xr]⣆8.$;po3} Jm$;V z[ 93咔GT{ywX_62;e#l1&/k${ǡeIj0@l{~TNH]YU,G >]GLb]8@_㙀cVQB.0NnRyg(ؗWO}7bW$!G#=k|pՉ=E'݇H漁-Ɠ1_A3kzQ ]O$RBB(1pƦ=h M(~BUoxy_&zd|gDPxQ Pw|l8jlluKS,~؛7J-;i~H*'e7NkMkurrL ȐTej2+ɢ 櫭KqZV31 "'o8ԍ?lr4{~eSgΕ(ػYժ3uW_@rp[s[Ol 03C2 BV:iQ NȨ=uCqCi%P]@plR} -26ȟmuؠ|pŘ|xj7G Jz;m8!-;yPe-+L+B셃a<ݨ4̗HWI Ա`#ʀ$٬)L-}ч-I]op/hOOt$?Ga( -2%D| xL/8ÓG]l~Z;7{RRtS}Tc޺*}1M\yCR,/][ zhjľnA6|4؝f=s%i-pl5,[unk)MĞ_IgPDg~OiXJb#2NFl[p9y0 ΃A9֩|@rȟY,'Cf,f`OE~dXFOo$)߳tA0P{nGy/L!ҕx8lx>ٛ}N2^0m9J߃k,Zj \"σyTj4!:ڂ(tGb/_t6#xU͠G%jF鼯Y#i B(gs^Qwwֽ*Bģ1 '0 D}&Q>Ґd _ɄফY3GY%JJde-TuyweܼIsOP/RϮT0wtf lH M 8P#QН2Ʌ_piߙx7u$]8T{7}Nb̊[c#"D_m3e;5ѨF/X!<7v}Vi/'*{ _uyyhꖲ}n*ê(Z z 7 >Tō8HЁfѭN i@eUlC =jpX4G)H#,9lG Pnс'/Ag[akսJ)}̯FM $gYR<11Ɩ$]MF*6 *ߎ:Bw^1ICtmA^{}Y*@7PB0Z{GRh'.؋Yk=Vsl$DPCoG&sɐ?VV>90$;`3g_$7IlP B[|d ;!p- kCCVvٱllAVdž&%7*\,=izF?t=Gj{Bkß g`y"dL uꚳ/<8;'c #FKz Eum6,S8D{j1cK0X^/4:כ@ӮBEvj1 EVY'{K oUbn0aS]pt-E&">BpA^b8kqIXI9d̾Yk^I0QJqcb|5c)Zۉrc?ye=Lw35CffY.AEw2l5(wv4YMΓމSfn.ȇm@0mKj< Zy!9X"TL2C^^l*j9#_0!SFlſkx d/dȊ]-c \48G'f]J6N&5myGn~3DeH۶P jD*'hsy &[;>.CYa3k^M4L? t\n9OH#=5Ø7EBT8pwkS" hvW{j!Pbf^On:L#m&ji]SƽośAhggق5XOa0Fv"0*[PRXmb]8x(= ;Iu4A%&iI1j0]yySy"B5Y/Z6W o=qTr]MoD4oI܋)g.x jaq<` P'L:s\&~Ź/$M %Tq Vo~G  4T \knHghn Η0% 7]=U :Y|V2[2No~,9IO`DznlQ^W:WKV7gdLH\H3A|Ds, @'$17_{C?r3P:G%4޼L"=GP}Itt|&8G6 W~[5d$T%Y{ɞ#&z攀@C2 =Hw֗ y:.]{骯#kr4 Hj8ρ qT;Ի_C#ZY" la%'ow~~\1>"@7l.'ښbLQ*P?s0XfMTc+R*DZ>lBǃU53<YтM Ue₿(b6"2k4y :64-vd{Z} `NSg:CSj}vΠM،N8X|3ei LJ({~n9P4UB.Q-%Z-6jO=(7 I0>RpʚNr' ͳMKi•YA ˰mr?0V}`17̺W¼Ey,2xѦm({ـFń,R7̞#&: jq7. cL\|(f;_h̟d凨ξȮ{tы8Xv F'wmod~o!t$M/), gMҸh$.MDb %n٫ƅָCdc,0{Lv#Lj]2]ֆgt_  np=;:xu!9uMTQjD܊ g,#'ںqw?ɬQ j03|XIZd{ ޑhxwّqdžYJ$"km,NU_lkN.`a)TW-tyS1ⷊNmAB@$%n3b<&z] ;`jAKс*EUAJ be*}2?Ced\8#@gbΤy h2[~RNnzy/p#ssKAJuyzIY=ݧE"}fcOY~[/iJB7.y]Ȕ,ŔAh9Եnon-]eGɆתnZ6g̢ϲ>-=ۧLKB ƀo®t"mW;k*ԵHVq^D8KWE)P4>y)&MgKn ąS`c9 2!8 r怢땴=go]VSȐe-0 /W}9[ /Y?mdo@GNya>: vkkYaVL+|ɽ$֩lk>M|"H8u'PԌdu%}+@+ n|ѱIrc BdشJ1\d%R=I$Oi]v뀹ٞTXꎏs"0=2i m qspeGn<ȘJE>Wf )qt,͉2r4m"M܋ՍRl%\ ?<ÈKSQO7$d!8*I|Se8Nk~aR*'Kc@^δǾ|^5-!R!qE!~eUEXJU%->&XC8v|5}q%XQnf^\/_F8d)YuLW} [y*چ҂Q68Oϥ*2]Oc qwJ:=՛JֈK) ⟉;2 ~>a_ݟ>Z5YOSC=|ݬq.lym'L"ݶc9:$?&ܤ{}\:^[F2cev(Ooa = i߸ڦL=x7^X{2\`5k 7QG$4؜*K߳2.`XNpILrJmEcƴc(m@9_s&bݳ}Fx7b%5MJpI%<&^Bsz 55͎b, ƤfYo_ar^s7kqz;w4 Q*p˻,/?z}AHv !0YhBX]!qS$Miم{ ;Lf RItQլ/l܄4vUBU\cJoSWrR) 'ܜY@Xq{[b/îb/ L=zx`IH? 7";J/h?] _}lAyъu}zhoh_2A*Fɼ6N2q}p- j`U-L\T%\.c[Rm%iJ۩hTNo 0lYAkY2(BHnxg!r,廱ꐝi:!oŸҨ}?RzF-\CT*P_W&>;(h>߈dVwhF8kOMT8/X-e~w?TYNϙ>\O xFQ?<qfJglzRTON\^]#Ձ2a%v"d‹ SONlj0vs 0\l4w9컢`Zv|H9GtdGA]0 Alس2I:-Eq)g iO ^^N`\izu}e,tq,35 nN3pxÊ3vȲ_;t3AAR$ "|M9H9nj$l8ܦ¯O7Z )nk<vO r܍?E}9i1 ;VZ%}14=厨jw7MJg@ݪ:4Rk7R*+iS5#K0t I mޓnEb&bAFj3/E^mg߶ ۝uG^=F#m|$=DB~n=6aUUfeJp.QDfLc9b<捷V( :6Pof(ZEqD9Y=wN{<E 7aVѩB%mq"XIS#b?iBǶGY}:qr65gF=K} PH鏖GɁJ?l;tbcX!{ОXf`sjA4\3GJ%nVy} K8:olƐK_,% 3Ժ.Uy͜ 9{83:#YԉCϚ~1)9n\E3:elCPo٪en|a%o3UrU?X;uWABd!p,P_EՄˎ+;gsDP짶ɓ/>{2:Yi_1 _p&˒uwƙhwȍɈz˯=F_E7`5D6,x i%+ƱՋ̼/bVp#h]D@> l9]U|񖡽$Hd_* `"?P J:܍<ç<ۓ.cSNPPa m 6ʄ6o5a}jiX Ȼ+Oɑ0+/,jżHjY*EɺGm1Lyʐhetai-_plFmO5*n&kw8T=Ƹsxz fi. Du :DF-?Q:m9-+rHTl5¯ø+ INs%"BD@2/&lΞIb{P{AvԸ3g_|!d2jmK%[z4v/s2 xQFB 6?e43BCBq{Koʝ #GU+aŊAB=5~2EAGEuzq4/0OdHW P|> U.@;`">2>m冧7-eIͦn~ \Dc ]R01zg]<)d7ȓdT.1Z@)@#45_A=\A>w >SF Y?Vw)A*j7"N1sm@<q5ITbgY\:My[L}y[ :t)i|s3r`lEu#"sZ2'^t\QϞ5wg?n3EŖ~RK$i2v#1IlUj?UM՜ ʁA:?8.** AQX=i,EfP &@4 S{ p.S!V"|(T-)Akr(?$Xj[a=kDRCr~uϥ#uͧ L܁ߠ-½`G&n T)Ҡ"2&܏ `4 7<^rOku @(%/dK )0tzx?;Cm2vlJê;Ն|YcHaځÞcw Oy]sYI1 Oi"K [ໟE}T|z֝ =z[oMF.FNl0S p;#hF Rfsl8Ԟ\w?Cs)7m_M ѮA}_a4Rz2F׳>A@Bu[xj1Xb `r&B4=N =ϧ"\(z 7^q9*HB[gzU&3 0S!a=,L? ';ޓRgw8N& }v*|R%V}[NޛEb=XmGjIW=Tdq _*KշݙW>W4?u28.ϤAdž&% i8Iĕw3.M嗝 Qj7 GWS '5\{m)Wb*BcPJDr;ʸ 3Ȑi &m?)K!-:d9qCJn9NZ$ߝ3nR^ָ/:/>?z̴3D%{WbPf۳!v7WiQN\233`sYjl5bKiqr8Ƚ\2}^b,7jWY`7y _]" .z5,ags]^@wN#޿!흐_ L EICfE.ƬSUr^I\K<)aOf`s89~STKW Gƍ8_˽:%b͟+ e sUU aCؑ:ӡ4Zj& \;cp'e᩠Y; '39\NߏhsMᏏA~M |COIUoAq ذYv&N@t;!ԙȄ(AȂڕbejP sq2eKmqsKj񘞊#C-)[oyV;s!Je_=ߡρPyUg$-*

^n m,EO_%͓Fi#w5pXìM1I#AT|`hjI[~%LF=nQ{ނq;l[TZ_q#_b0{Jcme.]{k)~eCJɟ,/r 2FQ[y8qrj]_)Q`^ 6֌}&PٿO]yaIKz,g ~?Py-Mk^-P{nMb;մs Eo'pLD>IA 3+`ǔʖxwŸ!9B}2к*>9y?Lm+ohwEUY itޠW}5H`{5+K',%DJT%~$mSFЊq0s@_ 0NAS>A1Ζ[l%Y,{RST8*b!-GlA75!܏X#A+96]V)a2Baw/Nw2Mzhr[WY7aԓ1B*;aeۃH^/GGb9`,KCҹq1ʶ%E}L1o˽7;jG%<*Vm(Xcxlx^T=R6uMܻؑ8|Ft;FJx(-VM pʲ "0Fs9*_ &`f Ӯ~mUs[edYt'F/.ŝ=xU(,2x^(Qnt>?:j *'RlwKe&9gt抹[+u-a9i"d/ W>#r7/s[9(fY e{\{9rȰ\?Ul퓯#Da"T;cwߣFN=(yyW31:t.|Im@&P*D:a[3m[Iǩ,mz>Z0pT8~/~#f;dmM&@ :9 G6DFRpBFbh> /.DoBA%C,E'kwv}g,k|)#*U'`vMU'۶[~~k+1*k)tksnB]pvr!?}IeB64=V_{/X񻧍ß@a_ar`"fݷ1Tع]h5~Yla S%Z|Xq.xbDb<Kٻ}~3a%L]M:XhҼL" 5O*]FG$ qm ̡-!:ִ <r+ ݄b?WzxG25߁R"_O,Vn#{%licyץZ >D99w,69O)#Zȳ-ic;+ƪ5inb9\nO嚑^&yR:!Yʮט8%u\'z˜ݐ\rh9nhEV\ɼ;.CL֔GZdpjIM*>'rI31LY83?^i/ öJۗ-W{.0<9S F8\MgbIFos*lOe㶑^]`m|.}}@_ E0ii\'BT%]>"ZHM t%F#7b3PV}rœ"1Iwo0 O}-#c;ouj挱ko$?`0| ڀ@OYGg8\Nc YORpęvJum!yKIoBpyh{)5N酚3=܀d0!q2&v?TLR(b;ʢD6QIpsZ_vL[ iHf~qą ?ٶ-8穫De1V hL^A~+8&^yb@}!yjMWwA_eK38lOxueBo IA澜 B`"h FY0o03wLѺj{#.U`,RE+2s@Toݏޥ![ʆQY9%kupZCO #<ߪSX)~ROJ[>i"=={2 $olf_Zniњ88!큪Og2o]AVOE{Gb'lGKp}=³= ͆MdJ!7y_2 saxl1ֹ| 4S\F(v߉dD? u9M82l[NQ%#T `*QZaUo y-0l{4뢶`ԞnB 悽L3Csr<ЖTtQpDBb6%ag.ώG 1 e-DWR0+J5V^3?1vOF he %Qi;5* 2r2"V9Gcq_Lȹ4{'_:5kZܑVZs)C} ŬoQxk.q\qC<ēxZ"FmFljP˼u_kg(l^h7={Y(px*OoV#HJb8|-ޅd ^"Zȑ5g\9؂hŅu"ߑdžc!fFc}@ 9Z! Tèe~W2y8asS_x*DuY-Za/qٌtxf NҖ'9W~e(ǯk%Dk[󖲑A긅(G&{Jͯp®%08K;Ѓ+94ɩF>n_WF&vc]6e{[0RY~tي͵K CκwB L0 N]_YfegmaV}L$"ës !>(F$8MY!a7]L3wi~A. lF/f|*3Ç TH'?;s^/. ?np(1ǪDhCBC6V?oov(Dfh?! m'F/Onxn:$hX;z<_P)Τ!:IJIz5/8b7B^gQ s_2u{X&Kݜب7@ DJM}#nK覊CKrCoQY }qBl1_^^ě 2T-D PUaE0uT:Ec.pu#? Ow/.ZV0k~IL!%oyV&&ߤ|n/2= CmGNFKahI*k* &0>GK7ȝ6ԲwNZZ͡RПQ y]sq؃Ir!\*߳ꆓmj^=<0t}x#҆{GjB;J22+Ul9XC0XxG*޿Cʣ_][ MhOJ96C3hmW7VJbOFxLuJCk`1NtjrmԹV%|@X5573*lT^Zdї tzvr,ry+D0nUwph]9,%/3]@x{}ub ?SeCF 6b*XЧNZyKp8\rv skm/AUI&~:ᩔO\9\2ua6͔>8'zs|ۺ% `c\Y یh03(ϣ-v $rܞ| |wa9q'!ˎ'fkp}h=AkAZ,//ʰ3u-QSIUg4TI(`e'BIcgi9t<&F8[ A>>u5e#ئ@(_@"Wyp%$8E&o08NqlU1":G.{"xyqrN̻w%Mhh'{NƪʃyMjgӭtx *l7M}Zj[w"EĔZ? ̰NVQ+eNR^30"I;ZlLB%b^ik`b !2i p ͩ #an=@2* kuԟLK޻aEd\ <1T$' >Vz )쑅;HZб=à/2:ELc"{r5G]9#2}/f3%c~=9=PyM~U޺%́O^O,>1r<\}7/~$/`qf?M >v(q@y4^XVHϖ(eU1[1rӭ,h7" ERMG(몝ueĔX{R΃Upo#Cl Ҳ o=F> A(ֹ#,y9x-q,HI^3gȸ<_iھ`GS9n*bR46Sedy*[(Vqx8ټ#7a<=C#m??*酩tL:9# K10?kn%pcp :t&iӷKT[j1*&`9,z~pПLJ$HWMB!=I`5E2G\]}gӦBF;R)`aj/J-%n<(9X0i ,X?IӴ :A>˅gr=BRZu+d0 B$H+ 5mC ,X"FBl+œ 7K 8 f]qy#ٴ}IX\/~9m'Ah@,B=9Y VcvMP˱p}hFt.V:VN{6oiu >[T ,~*X7U~h.YK!H^9Hڧh`'+lvfe$K mi/N P @Xߗod* B9ȱ(#&h+wՅ`:0~KgyL9)ۃl 1 !$-NM 8\J%e,toZpf3I0M3m&yEzP 90HJo_bou쑽R5fY.C 5k$i+JQC)H8Vd!JUQP$,[8uL٪ԏAr2ekZj +h߁%Œp٩zFoN]GT(<_}\E&~qCxwj2@8.ё+|v:r+}cudDo!4 owz~['u"MS>}iI&Xƭ(H}HvQ2^:gY#asml9Vˁͥk!m6F}p -{dB{ GC-?=+^ӇIq:&@Ӈ5͵;5Tð@h WLuۀL|EZBGٶҭbŃ wKaGQ1j)תYN`/׊"Ϊ%hur1okx=s{Nd\c č$O+4{ەTXsm݉*~w!CjJ`Evop8ŨBlŒ߀Uu)tĈzS|-NvsȊ BoȄ>BџnKHoa*SE#2$@8,cu8!<zmbm9,'R:5;0զ}9o%#5UxV+Q6L"X׃FM摟!p؝IJX+ZQ ƕlou3*Sg?QG?|[z |3dTaصد >r4֗>^ +8YH 0DD8dsU2jnCuT~!FhnNYr\k8ږ̙pK_w~;Ӄ}sblS2GY:9r*[F;9w$m>}_3v$T:YzE-ґM-I ׃ 7בBNҟ/Y ZKVyVٕQ؍z6BYXC4lCpxE4k嚳\}FǯL;AÑߪeJ=`$@((VҩC{/9sBøSKnEw fWti)*^]R}'|&#_w'wOTW&H?Rmyrh.ETյ;Ϋ%}j¡91a>w^!/CHYHQА L8*8 yL0P:4caE)uӨaǬ7S,^{"NoL  &3 wٺw]mCC9IcLZ`hEӲ0B1a#?<" ;xkhO!qsƯ3[.haCv P=@8]vi\H}+nwnO< >1 ozUЩxXW7VX%iq|w\RP66"S|]}$ .½f,CT@  Ѵ.,'030I,2|cV,!y:zM(aX& AuhCWC5 Zf/rbWR.c3ڙ}]VeKv auF.Kv;{Эh#/o5Q^8P,7zFbk5@=l͒g $#k˫/bLH4_%N.*6"Ɣs36-#4nڎq(Z"62%"aҷf+z<gJơUǸXBMieܭJˍ=-snQB}%`8R5j%] faEòfE}"y 2Y" ZR'GciM~Q6 H7 ["Mfiڽ:M(O~|]/gt3C^U  tN˩z4>gb Nd#>JXD;Sl+T^RTޤH$O hѱiנQ9f4qA,3Avyغ}hOn m2\(~4{@ zd_@yQ;vq5k~iA肖"N!{RG%,x݅ϣ%2#Ӱ)g.8R!.>+)77 sNrvOR\iL nu;Qˠ|W$ϰ^X-u*U0X,Pna5`Ls>L^fmLN}{`B́͏ Ǚ 404/mU[)k>Hp :4-I8>3OHG+\w~uC\*8bѩş?:}XـM@>Q@;(a|( slJvj'@s={ƀSkNȆ}xߋ(\쳕<&~{U~>_ EǬ(v{1a 34}}eyu$N@\1J+2B$ػ_:9C7*^LNŝA7 U-L@v LoAZDn([]LL4ng|f q0S.7r7G%gp_a鈂Kk'Y ^z2sN!=t_-,xq߿SUC5L ^X&>)S?Ta6+9w5 9[ASnƢ/*o۹~hŔ5g\AAemKwoQ$[nӬVұ|U|AL ;x+8h|TG'Uҡ*qD٠˾h57?(B|NU|*k'h[ɈL(nBcF6"g|'9XzHSN]@0 4&=`%)tZF[Mic[’KMtA= rS_V@͙""{AsI𢪢a:)jIΑLeW{С'˄#Nث,$V:yG(/_/ӳcs\pKI} *BŠ'^K Dt['XH]O~"! y?fVbM9vdpFvS@H:H]-w}Y'f *5Ϯ9iM8zA_OӵA&GH)Vn32A+gK[cZŝ4LC,9ID!/+;rv7H3?1qMdHZi‰ |Ky]ѥ_LyERbahlߒ >u Pg8$FM y*̟ X;fǽ81LoF̪5خzSMV :9=Y-Ȝ[Hn 7am|Un]8F1+޽_DQ;=BJS:wvrvg}w ~+[F VŮg6꿰*%f@v/C3OĥRxT#MȎ@yNPg77rr N9Q9 d5ѿ6T§\jڕ?q"wX%\1Qhd~ ɂ@㐃S~ F}WUcZP=uHαkk̿fBegmFBEW#9TO@Wo\=jAJ2IdWר ǙCUW ,NU:s)0 $1${iPÛ%jғ±'$`cFh huP .mЬ ZgX @ȼ ]*c0%C_HG_݌.ߕ҈źSš22dBY&;J-;y9g03"$J^47,Bd$otn]#(6Rդ5MC f^gYkM^`a=ˠEJ q?崛Y"܆w[QbCA{zt ӉL$NziI|86Qz2̥1x'4ziyN62GU5L1Vc¾ RcgZkSq9.tlnpM~R8A%05Ȭ;Fh*X-Kǧ%=U: ;3ksY#*k % phs&(uzGs:tNL*g,jQJ=xOh>3L =&QbvN2A &C3T~uy]gYڇYWSMIٌ؀i\XpX6MS-T}'9K-eўҩ/01JJk b0'n[uxCyje㳀D/LTHڰOO`qXwnrJ<$Keuu4C>r>z w&;Ga“h9IH96CұЙ{|w_~'Qȝ4<3o`i395eF׍N`ʽ$pc>ѐKɭ`鋺|sԍ6ZDFKC-RP7iT`|,o7|fOtr;%Վ} 5 7JdU0[^Crdu63`LR 8؛!oQ7%U1G%Mj${tDVivF"Nx:H$W]QnHCT"1٠5GޠoBܥf8d"y{=p(RB; J&YbN\bWWZD'1XIhvTW_=X>{^VU%.$w!S'xlF!Q9Tb\9t;i~oEJM=\Ix3+!CvU!ȁN獀[6ňPO*[4ߊvS-jsx\Z\^}_V7Fǫ^z/B| Cͻ4 y KdĞSM\U9UdTc_ BD;>tPg4 1Pb;Ŷ ƀpj[Sb]=Ax6 /#Y5AΉ3-%+?\ةEHAw C,C˫Y:}QWt%U,iӂ}`OZrتɋr֙OH4px1oM{%#[FL8/?\-o\,]V?JR>{t"A J{| (}4|Qij*CH'$;]ZcfPb[AY &r/r*hz])+Bn!z 1? w6pD֢d 3 c^۵sZgZW `6LUg ?4ɟb? SEj0M\^ 2ښn|K<͇s^ۯNwBN?:6N:ml|~{XQrgDǥZ[B=BӖr? N葱h$;K39Mع:}䎁|n5_;4T\ŠW`Tt5 *GgDjeq@`I,a4 `ʼ6 ֒>ß#md 8 Xhع!uWr[|84XlJѵ 4#hOԿHu c!llb17Ҏ?eܦ`X"W/v2gd/+`Uǯ!'BXw ߿0"dYQ~MK.Ȋ\o33 E]n@AT˱9jh_J3<L5of>骊C @ ,m#mv=e\;"biQwڶm۶m۶m۶m۶i۞;g\I%}Q#O:%m[=&H2Hf{gP=G$3(îd=paPYH2aTq=b$gC8p4[)49=Sj[.8;#3ho攵=Nn, G~>ـ|^Z$- B-LQ8HXYt=i6Yɩ~K'ń}}7M\|#u% {(0ۚ{O~4*N^؅;^(qB Y/롲ݍJa/t };OP D"4}U-6u3vՖ"u@kZ~L9d-1 OኽHf%*I=**}B*\EwƯf0E[=J14FtŶ!} WnZ;'I0R;@˭o[CUN{eϮܤ 2&UR8<:,kT.XP3iLW[}A{ m`e4< [(H*5o}c$l櫝TAAZMG0rh-g?)rFxv;u~ Ɏ`X׿U5˫ Ǒ2 EI [3h" yee\ Fy1 L&< kNLbjJ+,YBA3obzUȥfު=hyn "WǏb;5Y쓡@/SS*yENtue4zloAZ\ a#: 1V)=DNUbI]Y4jv=ᠫ/>MCXwynߝMLS4>EK /jY_j~:8B=0江TB9Zi^Zm{Ew?[cE]׉}*m؀2/M j!tB/q苎Y w^qC.;P;/x&y̻+N}lI5 /q%Ey0H.\]?0^D ]<.]gtOrfwk%Pr//|,4PSRsԃd5vxINAa|~ ngzT#',-,ȬxCeB_QZV¬ⳞCm]Ma Q *\g0 `+<!pdqeЯM==n wqC!FXsՋ :w˕ G|W>c /ǐ|gc&By8Krw0fVн`y䬊+mesuzԣ"}۵z/յ}7'jEE/i`,ILhu}G zj|.Gݵ _57?I1>ng[ U6jm2dFLѽJkJz} 7/Czo`N˃xhIن knE ? ;@g$Z2|Gr<+N+fȘg)OShRVcrB;uuJ0k Z(C zY"ߞ`[gtggh^%,l|Py@Kh6oZϥ؄{J&(AԈ" , <, &S@ΕF6=fݞ)6P÷5m9C;ϲMx=9<44?X,#MB9IH5Аm,4 l:4n{}n(TXk=h!y-֛HK)j4,$tb7|ͫO=t g_wGje<#Uڔ_z| G@ E$ ~"[(**-1VnR& f QFM'=GKgT)VrTzTy k]7e22)~[E8J,z^2z9#l?%a;4?0a?$VE[?c5{,hVLVDglu#XVW26PB:%̖UQRgPW)`1]Osok'o\rg{K94Y~adcR^8򂂑6T[hz#h|m}l:k=KqvZϫk(XBn"~=oFͦsN3'A -RM=ǀ O>ddQ]"YcUMۥh*<r1sEӓ/'D 6q8q_`uӉ@={/JM5o`>T H R".j8a/]b8EB{gE*~0֣4a/t_b?xY5c̝zpCY77lYMj RPrz#6Ɩ"ml;U_$&DLs]HCJHeQ_F>`4@ J`\!yYuÌ3aOZ|X"m$0uo5|ӃYԓe [( ܔ^NVPKsZb@m˧ 9B]=IN""Ba"f!́Y/ {+S ʑ't߫LOd!$7UƷrf(jbL+HďI"·LjBqBٲ)x/fz9} CP"{>GjY'gx3C:?llde:mMRq}Z}R T.@n ]E>mcJkg8k\W"JAc=Jl , mj; ܡ)Vz߂n?௑4ʟ&i2$Ҷ%۪qa\,3ZrL0QeS84[JXtL ޱO$١\1gkUa"vWM>>Vkϊ|= J$ ןǧ`F(Ơ` -&6ԑ:ja%&ACYPPr%($@8)c`s>m~OnI2Pi/ʑG9sՔ_`~R%qөNVV͕W3)˄qc$P]ms{LP'#FԾH%2^YK`Lc̹DlL}N8/iҐ%ftT7((eL#>{Kg5h=rau@Ξt%-\SƼ/ fv ~Y1a^߬UGčXNc3-e;ŐqB -w,T4 x1~3(( f/sܛ,[`gݭT }L(fڕqdwh3|@2Fw]7z$lYdyl@}UQȼ?|s]TDK*di7F0{YohOb:HccY u _>MAXZr츥ع_+KwhbthkM5X8wh6Q x„p|;ɨ#9Ht$ˉ RRϮ41qDJ֙Y3)k|[(uϸ}[hd\ъ+jMURYUI5L$D',N;bh $t.hGzGt6`-8TO&ͨlH>=b;gyOxc2-twlӪ”7K CddFCF|x\1*7Վ5X^;0=H _^%OMǒ7 !O+\BS+ҷu{BFKg}Ey$3~lc4>ls/P}]4R)L $Kȍok3.2Jl_IP^t#:c#FacD &d+ )ݶ hE݋H)i -ٿd YOHl Aɺ?ZBiN|ӟ!S ;D$ R)< )-I/ޤZ34ИdM.'*HkؼCNý̄6Ffؑsp/"DZ879k9qgaZUh` x߃=m ETzd0q;8FUFX@1žb:ܩ'֧j@jc?܄vھ&5.@}1!xkHf~<~c~_Paԍ廅sJ~ㄙ]+o%'8LMf{k:u'IJ´3:N\qB<A\%O%rq4ZnnPG@hMy @!0q'lZr{VJm~b|D厗L6U&E[sGʾ|{rdLеR<+3yI:jrfיb}}9!~xeB}Xe LbcA^b) <N%AX:i+b-C5gZkQlZ9Htv |*.4oc\ذ抜}6_c$|ŌT;>C..erh_7ȌJ?he>K!PJ->`RZ!P܆ N0Vk:#ur` ~r8))Wv"Aϛĸ鎿felu |550+sl[`|1] pczn߅M)sӅ#xM K`8&wkJjÍi( "x ƚt?ZÍbu?F*STP<&@ue΂h댍C33$;%qpP&Ty'R8^Y"v6oMqbiH/242d6M/Ecfm"emuΞ1L*bfTRC Ft_ 艚iC-`3%-&c}(N ^Xb(=hQ$bK}\# CoD˔V,ЫG]@Y WE0 =]^y^ oٰo9VJLXV=4k26H Yu%/VS*g0+ VlE2i2&ܒ2+U-/6M&Z4FǦ?hLvσ٭TAfNT5;Uu {"pVi?D|=VzΞz"M+s.p]+KdxCB̛cn0I'vՁBK"xN4#嵪]|xkh0LmVdm.iӖpABT|d[X ;]q}w@'gj*C @ϼ^:!^>ZOƄ{:Ѽ05|@i]ywl3.7e'fgE VVۊ6ST>#C.!b+HΕcVc%Izz\q-{8ťwI0T({M_(7>VycM,c%)"O6Sb`bj]M" 3pѤ/)JvA=BD)kRPrY͈HDD2;gGRuvCk+7m xJ.|W9t?p).(f8VEұ#ͳw54oxی}=僟T42fjQޙ65X؝`Gq<9vqNj۽V "f1&l8,Q,{^nyȳBZ_I㣻cTk :!3zbd!\#kVk Elć(ՠ쵾2K։$ (3;{ג>pS9θJ)k[>u2˺a;lӝfo)ڈc= d3ŏ:߀{taIخ4wE|]{NMԀ%V[VcT:ScP/w:ⴲ]^ǀ_bcߠZc7Be,1SF2 T-lLa W0:nПOzW4a5QU8  `ƈ/iײ'&5Dhe $_S xBwd3_Y#Գ@; / @8%ԹqUBLLkɀ >c l0S[&mc0Yݣ$!'#~&/蛪#FӀv5 Vׂ'a|.!yg,/һzŞfxd{a[,zt!OSSAfX[i.ï~S>ԇ SH鏌k˙ Viul6HSX. DŽ znlh韆%碦ze`B'a ^TvѢ`_Kg^ut~rʦyi'֔K!?;9Q[(R xr_f@ P\ăm%2*͚rQrКe\ ˆ b78. Clx6>}a2ܼj'[R|‰.:F+~tn׹o${У}2g` @[60<< yN/6-}P/|V2 H96aLJ(O&g3=+av #ԜCM;{vINWqTrexa$:=hZ{hL>Դ=(۷>т%^v/hbۋs aB=f)m>WK_|M$='{lLi_'ЏmQ쮘1P #u줲d֮z%Qj^0ɃcFA 6uSDܫA3o~|t6^*zr$@Sh"ℵLLΎ8 bscWx<.͆V f̤JL23 a:#ٕw,0 /m+K gR9pMVdYyStdd;:@pEOhV 9ceCddCmycD$"gw9օDFn:$ņYeYt6P2,F.mV! P}kHBP-:nMͶ 1< u$Zdy%F4>60UtIxWЌ6wB}BRpwrz{W cUeYט'\γG䕹Ĵ)F,ﺷ n A4uUc N$O լ^ۯOy`Q|0=܅$bgְmUoA͝D}JASo~/Czv-xm`84I &s؂m@WσY)+ʗ0iܡ\k<p<*jnk{-_ff|HQ@P!^(#J'- UYn4x(ʱ MC9[/|}u]zUf\ OCӂOͷ 2HG.6?AIo9e'8,%|}=?iEqp0lӷp6R*:նRLxD@D56E/06%i %΀BG: 2@F#;Uo!շgx⨋YkfOVJ΢yop*cwZ@[A_O9I3oHJ‚Kް+W{^~Rm:׍>6ȆQ3u>Rd5 fVc{KN-u !ɝ}0 jk$m!D(Ls+*.κSZȃx1ƘFcqP?JG0t7k(P& 6BKc9eB!%e@Pa=pqF`4|"=Gͤ-#u sN&-`eN]}&y;qj`-f]}"{IU g!w=R;1=?GK IF<~ ӋM_E-g~˜&d-umQ YAx4!Q+&h$KDFrҶmy:}ӭx(,i*\-7^d;U{#!d{axH7KU?yfҫ:]c|˳ߍ3V>rSixg[}fCvU=HJnmJ֮Ca'`cP25vS4./pr 08]74c 0857y9[57v$POK0,ReA7o}__!aTK/^0f"NM_eݫ,1}}Yr}n6$a56%]4s7UoA!l&a*Fv#v33ε$}X^Dy5/aYƝ-聾6ضeTբmg34l:(ˏuȏ$.;YBsnpcKn\s? OFVfHU/J`o ڡi#Mނ Me|x_Dc«$Ӏ_مSPgHT] $rAN767O c/$w co'DSM@#*5?}Rx UW¹Jiɯͯ/cea_Qogn/%  Y/Hol%(>,Y;'-=~į;٘117'\:JֈD!˰~fN慩IcHj"$662V:`R;ߙn0Ec>IK) 9+zto4.Z* `DDf*q59  &SĢH$."S`ZLY%Ġi|WC:Ȅ-ācGnΰjlFt&AjN] :"kT(ڸ;%"Ʌ.-wO`2+bԂ$L9.VR=9=^w$"m{"av$A\lm bS;څ rBX UUK!]{(@oS[- EE<6G ^kB:}ظv;Rhasswt |JGQDP @j~Jb-$W$c%yHP٩3)x^Ƹx! V^ ܒAzЬRzG/e^r^tf_bp{kG>S֨KZ"臵ҢlKfGC^=2#ts(|C!ZN/uۛxKWQᵪ0z賬ru_6G4ƒp1|#g~k>yUΚ,uD=ҸU|\t0Ur an,O^jْ[>:qXXh2L‚9z%mr[{DU+2jw ; Ko,@bDy5KB`Daj"v(|S|fvz9&wGzEaà,}0tZnuV9ŷ+Sp? :sr/u/ۚ(b/q T>5#Y&D&q fi _t>}嘂Y:6k YvhrR=SZmס:`sug`U78ܸi> G:LZBe* G\\)\cّ2"sûz3Y¤DJw s⣌MHbuf[ O?$0+0q )I5YnH7mT<{lFAځ_XpԸ ɒЁW+3}5gMK#vE_HyQ_YU~AQ6RU p #O a:N_rMkB\vvhh[3l7׋=Gt@#yFx E>i|sU–F)i᮵w&sʀLWӘB=y䰒Ny35b{7GRCJg_oWVybMF@SP_7kbˤG'?'r`@f 9ȿAzj+ɏ 7^Gߧ;WNV; ĘdJS[aHjOB7i1Si67O<њ07d"g~:M.,ќ}=P_s3z݋D׳I.T &z(RcJ[!) +xލ9֞ oZÿ l 6JRn,ҫ4R]{oJ/ ʝQ^f<~VTqk"*|Nr&DNimyRY&`^ E?P !8򐜵Wo-@[!7؊hX -qܹx?HT .K/^eBL V:0{tH xSbv!1D^dJ`$"&i;ҎelLYyM)ËF.EF1bU/9Le>ü>#jl@^nSqm*gݖ5RQ&~HaϏ"ǽ RAX)AaC\em} Iȿ` @Kk8 2+ Hg]=!fؼ nLC"[I^ y^z>VS7OfŒԒNl3?Y!TFD5 $7!9 E4~Pdռ'E?8R.$'ԕT}tyrI>7gO`bIגe*3ށi5MV1n|v^w>݅O4lޖn؋e>$S,gҏt-bķ˹$<(OhBW{g0[}P^$&d"D]ڗLp0s2oM`lL\߲?D*5~;8Z3XUs0U  ؖTv{[Iv*/[7+<[eeКrL'%R$λ%S"3iK]w9usW^Ѽe_\T ׅWWHoeuLGjAAH4BvE2dmuEqbh/]H +ۃ%]"F7"RQa $Cgv?CG 5y@NiKQ\r YBӂlWWZ3huFx5]%˿ dxCͺu)c'0$2Eް⌝'lcwexrA.APbнɂ_忀jB" ")q5V+v&&=矝S&\1;lcI` MOm#]ŁtMSPt( ~iM,Tn&JwT-g5Hq8%9]|}C#i)CdǺ>e-XeFDe wǫDWqD[ٷBvgĬsA3_GѠW`c>HIQ2rOMl{YFdYR:6oK&Sjnzym~!' +!ś V-}QuNV4Aݩ>lFQ.C¬}tpUdtPI[aҔAPzoV{`N\xM/QAR+v_-B51RQyrnyEG19'uÜZi$ ?Q+oA 2wwrU^__0Λ>$1/W>1mI_C 7bc^3'HΞLHEkV/uCv&_Er}nL`DκWQLzNYC<9T[j_X 0$c>w~z]!mBtcƝze]J $ݱ?mT2݉HsY[_q0sRLqy  ^$.;};Ȧkh,)T |_\)H=˿f[9vqK ư{cdtUD9ثNm==ҲD J _ZD ~F-VU(ǁbkedTpNxc"ׯTv< =R䄞~.}X~';`Q^˙aj/4[MK %RU.0;p+"\;yut8 LЈIp֘Պ.)ԌݩY3x&nmY7ɴbH#AjfaČ>r1ZQ;;O*XyzT> Xf}e7er5+nh;^5*pE/"D̗UWڟ;|JJV4%*4~$26RUL\;d)VNUЦ0@r?Jn9VI"46lS}*7Ĥ+~6jk5 42WcvUaD)?*o &z,K-b: LX+`i.9*>yyxq]9/9t6+%K+C-NVqp!^Chh!Q|Xc0UVXD[Б̖֥\d %/gIo9B2oRT2$ xD; `an T^k&$BH>t_@Ig s&YqJX+W5#P#eL!Qߩ.zYiPri[+p,<KCZyZ_q YҰzz ׮gdiۆ'q6َ"vP%IА'8bk) -ǀ鞆&/;i[ɞS0hx^D#&bL/0 W `0|6j >~TL N[; .(6wJ+w3tPFH5L!R7Wҷk9f$#nci6*̄ͩi[)lUuŷ!t_QU c)R*sOhgJTb6L\l뽌NoGOi$4K0s%ru{2WQ5c v{#N9-,~$ބcVڗzfL9D~ݼqfYjez:Ȯ&o9ԸAR"w/)Tdį,$TgBa=fls2M[5E~rbĠZqZ,H7}?S/Grd& Ev Zw1/ ƻZ=iQQ[L')S2$o.]Xrg"Q[f{SIZaUϻ1.CY C"Q)N1{|g7xNy[Nʧ*)[ͱ+0 H\DɴЧc{8˿,[g_^5*}x7pmpƱ ]K\jy(j< iŌ'P-Pj5қr';pa9{0QU~8 i5ybFG*EQ! ܢ֮̉Wg!v ݃-mُ[LEQel= BHL3y[}jyUZg:OX n*MwW ܓZ2$>nk B #a49GA>r̷ۄǰNh._cv#i,ik^W\AVRA"yS_R/DQ)_ufB[ghl!LB\¥kT>'9PFAMW|C&oF:|z.kWކ !ca$OP>Zt F{CMi0ODh+T,y/Nei~ #@DPg_,3(~.3d4/)]V~>ђM `D ]FRe=el+dFrÀ&e&#'&n##Tl O1-XYm=.SSjH69- %Bwlt)PVYa.:»<6u3B?LԪwHXjq( &/"asO}x4N`dT],ΰB_EJ7TUߖft&wOa%kۑnUYȗvuRmw`͏j]#:53)AA7FIg=BZ*&o>zqi@KSeg;H(sMBՔIbF .W}_ XJ,TRu2ѷ܎2mLj,2d#HI[OJw~N6?t-p\ұ[ w$'/[05!˭aꋏ9+3-8 -:-QޕX",lo"Mjtt{TpWL \Z2[i;R0r|4ro"WL_W-MDU?ƍzüWKt$F {YG|׬1Нwo(n{'8׮{i/ӂjQҐ=l?{u1Tuq_0,zWOJ{Ӿ(Nβ,(8an:%ĕ2Qq#drNX'Gʂ /xnY\oU-~aؐ)&vth('|ViiIBjg..܉3mYpx*`qN8IiLW5#!\Sfc_Sߐj{iSRGս [P6@V]j=S<cju&2m87JvYǶ;\LR[\Ҁu<kK~V[\Rfws r4TGb`"G@J-i;ee.iC^Hs%6Я7կ*Q[@Nv5:}؏qV5]"$TM qVzJa?"LdܷeU xV̌0:qc|[۬JHf0tiގ%^P5XMAǵ[7ZVSyn_!iCoú&߀=T4²3ф8:)Y%QNղt{'h_RݯvcK~'NYh^V7.]忿#ayچQZN:-7J%Y\÷z}@008ڧZVG6QvԞ`{zWpx(CXe"̞RX2EKמZJ_PzE'ˋQf&'k?N=g\47ZE,w h%6@5c scdow#exzwXRE#^%YT{^ yĚu;,v|ӯN5<yۯ鉧c>\D+@a`9=(GRq. 51`e7@]1nHNy#sߥ3; O|Nyʛ)]QUsB7hU_:x'oJRɥ9KC U-Ioc笠Q%bp5SoP [z!gI>x ^<㿳03~syH[ЍdM#i#kJNMyUJXk!w~r*p $l [ 9UϪԩM]vd>?Q0ݱR2JU,;l>"hG%\ΪrCů YhB}󴫟<£b[V!YEֵ:IKmqw^ , b6"r .O2ǭ:22^H 3.^wݖGB.IA8*Q|ȳw)!G B$M ,51<)$NUiC{4) Z$ye ww@_ޅ3fr[ӻv(1MR%FZeه[du ѓl%%ev7D!Z~FTrߒo/*څqqQ, 0@ǵzJlIqɮ8oZ&ϯB ԯ58:RlbJq]_hXNF' C/}•x ojz;-Ym3HrW^ľ%2:Af:&z5Ek;)A&2Q%nXN(o)ѸSoʇg^Ռ}>= BR!,' хγtVkLzzCq4p,K98֬ 1Τ苰$_\`EĆ콳["w56İxȏh_R#d٬M=iGދA(Vci'PWmYmwq 'vsW!t_[Tk&VK,^ز":.s5R.Eh5lL梸!Iś+}]M,V e zs vʘTzh 8/ZgdXFtDQHP[Iªd~9vҼ ahsSٌ09(dxJl?2aAT:=kHX0foVŚ6]E!Q﹐7烧;jSa3,MbZpYh: QcX3&^}z 2J[W  C)84Zk%j2 !'p"bT)hߩ'$b$`[,c&Fv*Z0,#$3{MXQB*K6OSjtPDdJ{(V8Y@A}<-y"5Q k/YH}$SkDQ$D7ߒDh[I#CbcVzYL ,8[kIc1Ɣ~Ϫ uM\*ѐًܩ?84{J σP*Ϣszg6Wz!dwB!G*XX@ӛ \<@b?m SWj.E>4/B͓J‘B\sD;shbnK65m1 r7rU^jMw;Hf!ձ,AkeI.[Xn_uVqN/jӾDC)lKΊjzM8۷X?Fg2[SfdAWgIy;y*EH5c?s}I)` ޽0&oA7$'Z$۩<tW2(o5Z|-h,afj .bm/6\LwEcS'Wd9pF ð-9eUd &Ob6C'd(@#WYb( *'~Saٸm;W4y47a;xe_ljukeLp6ɽl{~Rlom-*rHM,X$!t0HLzݛ7E+SqߪiX[d9c&?߇Ƃ6 S+Nh0gcǓ9q⮲R]m[8}jRi-!\kG5Z^J>Dp-@zLOh7 A8LH\L]DجΪ/MTܥ֗E7q5VoفyqaDvK4y*$QpZ5?AWh p1W+@_ђ9o9y,(w9nfBacn?RMc\r|/s8[un37$di^uO!d w$ޘȜxU4ȇ_sŅ3E[փ?U}g"outaj 7~ H!sRFJsv)zO>-ˑ$'jgG\>hD) վmHˈ`?\Y+{WM ZwfdoԔT]Xr^'7[L nbӥT֦$t@ e欗])-:lR6gTn֚Q;#Xb9G~Xjƙ" \>Z7kPI %&֮)l|n[m_<`)k Ų[ƴIp@0~jv!8ҨM[0?56#u a5%0JH `$?qC2Nq; ~! _vܫ:(^Q+ xeȖpW'W[G9 Z1T [Wu`. vвUL>]ʵ}]6kN:uHT-ON$ bW;(T<دnpQ- ppA&m[N4!]@a"[M^F;fqu]ģLF}<^V} GW%/ܾ?R2tӷ[ VF4J3wl#яB]jqu΀ p0pTFBh;>؆oTF#VX[B^ &1ͺ-~=P`ıp?ve$t<#/%]:8!MI`ij{&mL09EY 'Qհ.̊m9~5O ݣ:aG3ZtC@Ik0x!|eMJ̫` Ha}CQ\r.Io͚h{'w'DPl~1[ޚK\9W+Va[Q'Z4t"u2/yWnFF 3MfϞt{fkrJ34[Hzdw!Y"rccb5&2r${g$Wn? Zqd7>nwda+س'?-doHX*Pfq+dF0jyߕ i}2wAo}n r8ҥvJ{}* HzG%c?(8d@;ڪ$AeeyOּ9T:ַS'H~xO=aۣ;/arGk%jU/IƄXyMkV/Tֺ.xhsȐs6>S6x,Yp.f[zEU06\BȮO$'Q- @@Gr~HM g_aB*˲>!|yD+ώFk$~&KO[4J|̱* Zd;Uۛ ф3|l'/~ۉы)pp n t;D,T3iNvRq XMlj沾PgWY`ԵB/9t#;ye](x58g8l7'6j B7C5=|S[|R)"Rhf8jlb궲[g bª &"a A=TxkXb)L]#8(0|oEh4NXFK/\Ə}ӝ#􋋖ո _(yj7SiH[^,7)ǃۋLAqOCЁiQbSeZJ0 ρ쑼 r;,yS֡|sg`@}?ӯV^E8\ݼ'HZFKAP1AU>-;B,sx*0zb̪r]mG3ezNz5b{X|/WVC6#5.>hI}'#*vf6=cqaNeIuȲ㉙\iOZ?rаˋK/2L]dA.xK2TR6E4 mYIPҘGZ|#,&9&Ɇ5h|6CzO]#BM)=/#Pr9s\ ;`ap[(6S[C}çƑ˞ȹ=^^*|#lS(.]gS>fɞ`Ӥm`x:7{S`t-At ݬ,OԬQey Izqh%DA6RfXGJĨxH$}00Q]zʫwqƯ֒k I}PH_8fI 3UΗZᅓ|Nb,hsnh+U DMfx#)F}~5MqJGoĻ%Z;/O)W2ʝ,bPeOL7WC/SI2qlh7,]#x NO@F8fW%8 wBr[:w9cul8 Y` xY$+a1 'dʑ<[:Aʮi<'i2KݳQHL&!90<;F v} m!9Q\k5 NneQTTbyjB4W / ۮMs֝~1}w_~>PWa&XB FZEB,6Cs,B(ȿ~yd=DÚ|'%Œnػo9} IϳU<r +{d>7R)tlO0ˬN+5ꄣa\MQWΈLٵL񘬟rOjkN8T^_էoɦ4sS y y\6 W͋$1?p e\1*nGʀA;AW&2Vҳ_!yYhLV dD\t+ ( ,iBTJ1AǺj'`]1֞{ xն&+ȅu(l[Azʡ E}ު5;SR>IE~ UPHϽ@R(=iM|4Ltz.fٴ)plŻTai6?X-CحZK-CE BK*,tLB' -O4)wprę~jV?}joiJ?Y lf; JBMkdmPnE=/ r 6P>vk͒=)01:AYW`{&=)u~6mq_4拪`NI)B3KPE=g'!ƃn{NV-՘]ir,6Eqplwjy+G9<}r3Fr,9Eo ]u!؄_!Y~D~C6f"D5~, &ڷ<3 wk Į0#\FvQfS`W*J!Wv|Q,InC 䝫L=#KtJo6N%zJXY31[/ͫ§۝޵V`pg+ê(iӔO,f_xZI!qkEd77 jE]N%YV2vX}k[)cimr`sZ_[uB4Bz^nCQP~ ODuR Dz Gs-N_40>0k$ȫU-S6  -GQtkc`ݒzhpwT Ejaʵj!e%;Xx'K"jvZ\.|m(S5Y7X)=qc5 °Jgv'V柨ܮhFw߯6ƝbxߐC<;oarŴ]=[e)?N107`U/;sf ]<1T(_܁">w[12ᯏP'=[0DhZ~aC AQ?{:4t18MDy tY 0+5ϫ QI /s7V H/sIh9|VSTgKD6$_vdr_xń鷷 нFu: Ș[V:eغTA,FȲ X/?NπGtXcy~!/ˉ8h(Li_o`k[4~85g(@^Jv %Qyg,?:0v"1G!+VDuTq+=[]ԿybOoQO+Ö=ߌ=Ձkز2vm?kBffO-.'%nW$6=l;g%c53?Q4\[muzŐ2k_Sg\'%%s:``ߜ)۔ѣ|GDN8hJv3Nie#, 8淾E{O*] ? 2N;1^Qqtd_C3C7`o>~e۶m۶m۶m۶m~m{{N}~ήd&\5SQeHP!|'ϗa%+pμBʨFFH=l,! !k"rY.>#Wa뎠Xtfo K?L0Io G +eČptZ½9aܩ%"iȻ?4xNRLތծSMLGe['ПSr<~Cb4"PzCՀew>E5uvys_ ݰ ;EV!Mkem,(hH&CTR<&rb}{D@:iTMTDc)Wp=cB'7; l];.v^!xE!؃ُߜ1&k-L"G}G|iL rI}nlDN?tPn<5hO!qsƯ3[.haCv P=@8]vi\H}+nwnO< >1 ozUЩxXW7VX%iq|w\RP66"S|]}$ .½f,CT@  Ѵ.,'030I,2|cV,!y:zM(aX& AuhCWC5 Zf/rbWR.c3ڙ}]VeKv auF.Kv;{Эh#/o5Q^8P,7zFbk5@=l͒g $#k˫/bLH4_%N.*6"Ɣs36-#4nڎq(Z"62%"aҷf+z<gJơUǸXBMieܭJˍ=-snQB}%`8R5j%] faEòfE}"y 2Y" ZR'GciM~Q6 H7 ["Mfiڽ:M(O~|]/gt3C^U  tN˩z4>gb Nd#>JXD;Sl+T^RTޤH$O hѱiנQ9f4qA,3Avyغ}hOn m2\(~4{@ zd_@yQ;vq5k~iA肖"N!{RG%,x݅ϣ%2#Ӱ)g.8R!.>+)77 sNrvOR\iL nu;Qˠ|W$ϰ^X-u*U0X,Pna5`Ls>L^fmLN}{`B́͏ Ǚ 404/mU[)k>Hp :4-I8>3OHG+\wNȇ*XqUFq@Sˇ?t$%7,|d.vP Q4<ؔ% 2OpP?|{ĝ֜ X =h21P+g+yLkY|@ YQbAgh5(MD ID-bBV.eH̱wt>snJ^Q5NIԽM? k e M]VC{DwV9[Xlij pM|S4lV¿s,j - sj̍E_T޶sЊ)k<θږVȣH,FYGcF;+0v2WpjB6NCU n8/γA9}Zkn~V!PEVU2AN"!eQƌ|lTEt2y07ýXAf%YU- ǎ s`D"<>Ij !)hR_u QF~zNFĮPaa/ahDyHLjvN.Wr 8f]4|@Tl_Obm;2|,7OÑ \SEBٛ~x =JR!RgS٘}" ĚrL+킧`K9t~18Z:>aO( a9AT8k(]a sqv5V,kLRݒ+fe &.8W (ϖ̵$;i sXs<ēkC4>/ ^tWwnf~McM㚀SӄO+1KQ]Z0پ%0}0gLq\-I92֍ěrAT?2v,yxJ M)̅X,]Md9v-=뎧o}[*{떔4QLi[>DU,)|{ qdac=$ތ塙UikD]# %88K3(Yu9#&rz>[9M_2+l7nܺ Oq V+scWu{eigFSQiv({5Vfu584@V&?A]ma IUA-UJ<͈_f3N}'KG)$ %ᡬQnn F!sr@"jm&{ԅOA!)=J+%0~E0; ɥJc%_IW!L;Ǯõ%Z{c׆ט久('[Qaی'4Gr& ßȁR-B{Ղ˕d,VQ}3j YGtS`(IbHӠ7ġ 7K'5cOIjnjі/\Y+A>R&.yT `J"(ё=]+u5 edtɪ+~MwZwsZ`6f3EHӌ8C<ތil2?$.͘eC9ӣ-;l>@J"] H¤޷ۤSL\Vp %Yᔢâg@kP=ŸouNX#ǧ,o'+u. v:ZɃ4O ZDXCxWjٔOZ@nD*b8a_Y[XN_<̇.M+hTeqMU3pW >JBYǏ#o5dT}smwISҔ4$/ħaQ8/N7)}K$_HFE퐇+97N =ώuVK,Ǫ_kainJiօ@e:5U m $FfM@^sAuJ!4Y8%כP[HVlO$ǡS։^hD~ϝ˭Pt$[e>RE 2y6+ aZņSZjݠn.ojXJI(YX_Vɩ mlLy^8qq&$!ў@X}uZ\v,tq||!oC~[y9 UpGkY,բz:}g{Md4?;5e8Lym׌Ǧ0 ςzXN'3{ΰ߃,)D0L/뺬c9%G:hR{P䭮B+LױYGs* 9vngQP JL |f,56!9 )Ӹϑbmmn3+0[z>0Ns[ 8ʢ=y4:YK0WcrE۱^n@3 $bzătI^9-E-_*gM!jO{}=buwR*$S7//Ղ1?X㭑j]0@ 2mW(3g@XR= 3Cʘ|wCX`w:7 )PKtau(/bԟUcTFBb0[4N$9" v91bw#A7vaK)!1z UaۉueWuU'h)>Mutmkr)! F8i-%|߮b,ǺkX"=O0Hr(6Ɣr3ZWZt*$. w5/i9\2k(Bgk|S_acX˕@Ĥ a"6SO0ݶkiկXg:^@)$L%KaE2 1冷xzI<2Sh ||2@LYM$F YwQ%HT'=м7r-sl‡c734/1N࣐;Ki0[yg0MgsjJ|{IHI#<}zɣ!#j[u;]Y5mdgN-G_[*. 2o|ӨXoD3?wJk o=` ȶPmbyg6@p<47Ż!CnvoH)7&KbLkv Kn#H5 2E6unHFV}EcZQ/#iAkнA 80KpD$@{P(; vr%dL6Ŝsׯ c8=Nb쨮R {|J]H!BN" B6sکĸFSr69vFߊ";{nfVB zsC@;fmTh8[d[o%pFo0 ;WZ*{_r*,bwiX "> ;A6 &=&>r2.25v3$^푿v|hr;c࣡vm^dնcy ź{mRQ _Gkb #*gZKBWp;~ >!S,XWt4LeKX5ҦYX >U9#墭3';(iZ:b 6JFqv!'_~/dZRY.=%U.|"x SE81̓|Q,@3hcQhBTNIv*̠k(򃲾&M^ \UH{SV`.BY'Tc~!mE7m)KSA&grƼkδ*z l擋Ϫ12 h?<%R`b 6De5`x/0s#;_4IDžt,l!uq]po 8wBZN'4*K;(o{4E-~HAr#cI>/w6bfsرsu NjB#|wh#߹Rj TΈx jMI +9;$X"h6y=l%}=?GȘ1p* dѰsCFph1{1*j:k@ioGОto7 5{IPBN%>$c,VinK~ιMmO#:D_eΐ^60W \ ُ_CN;~:@6a8*E0鳢ʝ;`]Bkf9fg"T ܀d0ԗcsp ь Ku7fy Rj|=EqUk@B3@8͛YFz˸vmd U AAXgϠsGCId#Q VWlp#ҸH'Omh es:Uв y̠=S; <^{3$ dBHyi$D|0D#` gѕV8_`dFb$-4 sԕ'(8,lkR=1Ө8ym`x )dLbw7+}0d7D<}C%;А^oK^;Wr֍VT[ u>:iU2-jLݶ S8,\>+ #='F(&̯/b"˂zLZ.bNtw8Babc<9<}KĠXAž vŠXdlnWjjWs^{v1nes_E@>غMԻe49Q]˺[Aq[ݟHݏ&Z\1dvN X. p"Njm(?HKnXXķ^Yi @z>I!Zn}@pBm݋(k|vE&9`4a!ն^rLUG Lc*ޢՔ ثnm+sߤQBDRqXlyg~&a3_'( r Bx]oz?CCogG=Nc0{ko!nU,PgNvk̠7ԨY^`h8Yp6/RLJ@݊FYhO/(+o `0KϳY_`*4QD ||_XsbSSZ_aPM y+=mG.5V 7FsP:~+ɯ9m"e z@P/rpcWu/c{  gN졨&'rK0P#T ]}ᨵnŠslr,fݟ%ͧIÛfnbݺ:cByx ɑG:pA9C'Pk.k)'0N9)Z ~QUX)Y7je1ȟxMKЍҲmKU.+L`,A 7,bNdSoylbTfz%E_tZhZrٱ7J4͵g^ΘtgcOV~+/'Gvil&Ze≽w:w{'3<[O^/1OO⏐Kxig%tncꘒ:$񈿰Kr BuCCv_p;dą_16=eiaAfm:- ʬֲflOBmNw8l.ƈbPr,=+^A xST%+c~mq_#5 0Bǚ^ttnչ_TH_?oo6V6I|9 =d7 X\E1ghbU#gU\h+ {ݮեF{W>?YW+ξ>(z1Oe>oWfIbD+X;W(Skn$t9_Efo/tίAN.q?*fݰQӐo7N'3bBTe7 WVZ˘T+@44{_0yl;LcQmFe.w6XfMG0!G^nЪw3-)Y,2L!VS^2n/j-%ϭP^=H.A?ؽe仓tWAgL@>]䈆|~+ѪSj Ϥ]ȼ+ͥջ?A؄P0 mFt-XTWPL6"GboM'X> ؞*34k- _خ{.|z2|񥘮/’+ /ZH)B]GlErߣqtZfHէwZ1 @_X< Cx=%tzS+!\n%&CdMy6uRHoWc[[ -,7^8QdyZHp@ﰩF ?{|y<ґS{o9Rk 4E Wb*} v{;2`nSֶsBad-\ U폸4 \L46=|*<ܬҗ=m .EHUY+*|D>Y$:]٠ùK3N{Q`oZ ~-AZqP uxw0+)K>K/R0ٷW`p'0~y]5QƋWȒGc׃Ϳqfj P6ٗBkI6iЗ<`iJ%U>"y0&4e~G@RB".3:0j7mPr& 3ϪL ft|~*Ԃr8i#){#΢,cZGa  6f^ʿӗ+N6(G k2S<ӓHTbߚɺ 1P[#?rJ6&aV*>:# 9 e˦o *Agzo*S YfX ^e费~;\7Ku@BiA͊KG4PO!2t)uu4obs](1*}'(#٫nnd&HsʧX ޻ЂfFg _+ɐ8oKQdrLR5U*O%\@r(G~US~IɖKݚOr8[YA4WZ_h, s4DÎBub1APwsLzsC?gGS"x]d-ʂ1Yl2ֺb2ad2k9IC0QgKT3ߓߠ8} _0m>/i֠L9{RЕp}L8~n.22f y[~o T9!+٪͔dw>NΪZA&7;$72c9ʹ\ȯW"C} %!^P,IϠxN(413poHy#̮@k~-pe3?scsWe$T;Þc+F`XDOִDM1H kzRb"JG/6AT'z=䃃>_,mnuS61%TUWhWCƑͳOiɈ>pnue?J[}^{/dwO?w_V;;sIg) / j0>ڱ-L;hA@iھ*`]1D"1bzBZ7{(>9]Ky 0+/!ZA փш& Bnn{p !0f\nO둰QSJfI Y-{=UUG!uQE-Yu Y,f yA|?!8" 9E/g-&o;J&g|79"R`i]K[c~r9,1 溦ߡ ѡ5׸~4cm{N!D!(/D . $Z#MKӑ#/'C.JI=+?8OdF*YggϤȯFm-bj=mҢ=ApE+5QT KeIT'0mk :u苡ds6 #ُ}}ـ,Ry?l6^Cu"4srak< n.6G:dݱaL x S>,% :ԓ+EcDOryZĨdT;2לb{2.O"a'4=x?5{ K~T*Rc?|,sB MH߆g \R/q"?Uef(6qE8xCMwќRK0)X,#7r [̸t+ `E%|BmCZtxMЙ ꌍzE}+n*@Jw,HYw/" PgFc}.d==#}P߲e[+,%h 9} Nk|O-ijx3H=h$tn'ax>k 4Cc5\< a]:5r:*2^^T cG%DZtreԟn0yG݆jU*|~Ff8rpq:^.R)z5xh<Uuc/Idzrrd6[bxWEWsYklPań9p e?aN!Wd6SNW&}AQ7Y+Rfrv- lp0}6}idԝ zZ$;U4r9| 0~qgn?=\Hh BS0V6a *R2;iY ݧ˹Y)N ;^2Y .;CT!l}Ž+>sJᓡ2ANKj:"%%:/k1_g ŗ Cc9*"8&0;n8W+bkkȋAzϊ-xx8!Tpa8pnN= >i1 Gj唓J#38 ҈QNzqcb+rb sHя3vwSll GM@bգni "3v('.b@Y  ټu]̎dlqcA Kw-Otm \iVgz=i=]K>{MQ* ' Er&1R^溧f0.rf~D)5Bd >^U c spi?$DGζ`u 9 #^L. w=+,Q?6Ϛ 7S5$o6v<qZpG  9ͣWC=E KX~xSug]%vZnz_/2߷&)bi= RF)qk`& Ht zKiy|#C s|;0G[= !?S,U F\ىa>oZ_;nM֭'ENԴ:;hm}^W wtL7w y~7ӣLM85js78.%Hlܡ#)7]@ D7tan8B@{/okh 72=3ȷSgWʶN!SA[`Tf>li9 36&/ $bz<AFs@5PmH?x=gyDټ7wY Y#MQ L Dq4>J}mW;{nX40苙QqK9 )}1'jrxT ~\d:J3xaEA/q/ [N]Er.S>Z@ t7W]G9o4:;VDNtg: ]x\dH1뜪mE9.W3^".Z#Dj-zOIL|xFJ2ȲCSIpb^0x#nnR%@ˣ玘+傊 9쀈lɭf'6j)*4V9<"3b˜k#]o*>LoEGdtsVwTOòQx3kFinjRp}Ke4Wk({&~8 H(|0 9K;E0 2Vh'=u[^h!ޏ'odz33(*Phw[q냳嶏ojW4ft(g2qVg[ MgZثHєt!C޿GRonݬKά4)vxl 7^3ܪE`wcFci:]V˭ԙNOD2r%t˱BymW{< 4i(>*W}ŵgRRb3 1"S:P&oqaC"qpseq՟=t_؃t-nAXCi(f:UϔAXҙRE<eQ0`6!t.[$>_܅&I##2;7>p0OiuReFIq($r!A5K3\D{Kr0F8:X^Td>:n7nx ͷS'9TVӲ~Ew6 61l/h᩺?y:iWx$.Z sفՊI?ne.eæUxZ)X0aYXTt>h!2 dicוXM, *XٳbKbɘpK^ˬTzVJ86ha^Cf3=fRٺ.^s;Qe`Vizc(q:J Z$.X9{ºp6Fu9~{wgxz/i 1o#8$U8T;V -9ӌתv+@3=Za|Si^N[^ .WqT QZ+oa)\v5*yoR&RR;(C +k.\|0Dƛ-[I~"4φjܷӘg ]o3)H~R=v,ҜVprȪEM;K{g`U"bwER8tW:elBQMPn.9|\&AYӽ G?L|8ŤJO+Xo0GU5t}it#$uL{vߺ0/.t`gQ )'M~0Hd=ǨV]y3aW6sl[)3p&cPz#Vv5޳Fy yö! iQȚv|'Rvr|-M(@Ewb|8 ߇A{ 8G> j#e+MP$h>N+G xf6E:%gɿ9*1+)ulƶrWʯZ%۳RۢVW,Y'0^m^KlYNM:ʾ+nHPbA,64}Ow2ڛc[h#6d?v`0Tх&!`"w951S:XmYQ;LeLBExvy1 ~Aˏ+~Bk|{+߀γ L%dPJ1y3\;xjC<Ͷ_AЄDU,DO3oOȃ#~]AL%b̓|M M]V(~~eUGP//$rRU 2 11%6x2}Nm/xgu8|%)ϼ04?2ŰҚ//K<{σђHDrr'rNohWT/uz%A?RFH墅kgmYב7sxbKl* o6)N f3.P. X_ $楞QrʲLHio{6ᵓm9R n C [<;OM-!`2nQ*sv}LP6L#?2Y.g /Z!".;Lcy, >/Q} P 18zQ @TZCPE /yer*) XS.U\DoK) %@q>^Ȩ4k>˹FCkq5< #6@Ս9`QQߖb4CoqW,WElFVoӹ3<VOcC^٠H$X=2vfh,l,t$ѳEsj6:~fÞl)J; 'P%]#Cɜ 7QnL /9C۬AYS,#ƆY0xTO(a\> #Td2@Ps55v%9Bj_2w^탛#R˕ㅑh2km19KwP"Llb(G vVZd{Ywxm/27] ,[Sxd*^-}%55N".1o~1@?fFbK@-8ױFL[[ݖDy J$f NpڐYLZrsҝڨjz @;ɑZL:U219;^04͍q^G4ZQd/Hqs2No+1JX/d@dWޱ(ÿų/{40oJY6Y gOё-cT!=Y*P: GoC}dUVZ~ŻTff5@ʲXY44@"B CtPF65w72$HWAxkfPѠ"X#f *>W&;7^A3RH "[FN J}ޱ i<`_V( BfWYaLf^c :nr-g8gv@~WӦ| oi60;WA*+NoW]y,8}t>2VznVS<9恍~GQHCpYjWUD6w}+NÙrIJ#v*;/2klًv=2$5]^ cc =]i? fe*L,*_##nsr.G?L\T!'{7)3d «'@ >+Oz^Yun>Ŀ" Wlsk}$s VINX \șON̮[^d'\g!! B8cmDUAQ?7<Dp6N6D:ۥ rrlrMTܴ@}A$O]x:99c|dHb h5BdQV]8F+eZ7c9=?@QWש3}F D,PPjՙ\/G{gT9'|a!ECxdp+('_dTd8Sࡴd(. 8X6lWXuWEs6:lP>U8~bL ><5߂̛#q\@%NDZ~h<Ė D!B LnTMK@HeXTJu0uemlVNlݮ7z':n e#0˄ɒf"lW~ x|XWT~ɂJ.6?gڛ=Y)B:_tоaЏ1kQo]}?ž&q.ϼ!) .y^=yqIljxb_7 G@ X3Þ׹@ RHE48qZ}-}:I5&wbϏO$3iL?O'FYh, xf^S}#-<xɠsv}>lxJ9,\ICJ[ۓEbz!L33"?2 ,}#{7MY l(=7 `JS6<>'rc/xͿAE5-w5ax A}Z@(iiH2 g/dBp߬,u%%2ܺsƼ; 2_n^$'_ah)gW*i~e:3 Y6hn& nC\w(NS}B/`rIs8⋴rD:[b.e*Ռ>}1pfŭ1thT#ۗZ,l;D>uhV@+=Qx~꯺<{<4uK>7{aU}AM`ÂWy `pxțsWFYvrkj@YV[b'OƄ}f HXvWEPŪtmg65N,l  V(avR@}WJЗp{rg`| {-T0{l^>\W#ߦX𳍿^)n cmԦLg{aFo_y;ܤ! >,O(!-ҽR) 4Ў{g yŬڵA[+`9RP_g"Snס7#9d!PmLCs\l{ <@^.MW/yHBQ= &C]7C!@;@dtӐկ:2OŠAx5!֍!+rX6gve6P[ acCgtȈϒ.e=Oמ#xN Asf3Dd0G^1-D²x;xҚ!Q3, X΢;pTA ;u;g[&SIHĩ]3Möj 6wQ^x%W5 H-xǼ ,*t&!/\ZL/6/sJ#~ֵ < G]22dEzȮ҄Dmx`D3.%FSg'I۶uFg#cM~?{ m[r`"x4V9d-KHWy!,U0F5J&rt~N I.b7X'Mjޞ l}aLM_W^"!*V[ǻ)4BX=UFq`UH]fq 7H&ɑ6A^@Xng54We)޷Q V3l'@HJ#;w- y)6M1EUt.}wxj䋖 |4[ODdjlci\_,T '*2`8W"MG2UR>@p*ܤ(Bt W__f!΀ #0ȥƒrL/j|fIDw0Ql'p36">q@ war__t7@M0=HGBx/&|d9~l:osqh3)l:s1%:zka_&r~t*G~OsBB"I󠂆u1R(ɈutĀ#Zg=Շj ~hgt^7,Kh;-ڦEi˴Za6v]YQi+0]qe'0Eu#I!yYc҂vA↝SԮMzFp%=m$Q o/ XTJ"9 nlXq$|pAd,րy-2~?sp?lcP,^ }o/3ڥ-IHPɢh++N% ?BgV2!$m)!gx25@Q,mLELdMitlhZ.+n$"^M%:..t&.PFsp*?Mo{_sA[[f@n\Ke4R,:[6oؽ ? }zj*sx):sav_f cm 3)YTQW3|Lώ7\#b2+5>sP.g-Ub&%z7n_fC9@7dU(ƍmNX&3y )0qԮt׼X M 3w3o7v:a|[͗SsWi\T&"A1R7ULBkܡs1M=E;|ɑI\Ea.L.kóJA:ۯt [~7a<Ռٌ:M՜AtCEרPqw"JnE3ٓ m]Z8ĻdVĨ5Ir ] R{r{DHE<}D;Hc,%56\/w6 RBI Q }[EU y![fxP7B]mAU֮tlqS5 xwौB@H*b%aA2>@!2Z~I2HJ S1gRqC4h?)'7勹%Y:^ʼ$͞"x> ѱ}ꧬQF]EkE!ق͎].{dG)@uQ B^Z772dkU7a`3fgYSmh&%!HcF7af}:}5NYωL{qf/"%`"YKզ%EG|t)e\9gs@Jڞ3.J?)WdH2vXޫňj-,Dݟ`E6Q27 {# LrLӏ<0QÆAYi5`0ro+V~bwu>^T_5Qz&^>m$|jGMLe>ӂD\7؋} U$I91!tlZ%.)zڮCu\}l np,uǹqML}u ʶU@عRƲ#7dL%D梇wfvVI8 AG96&M)ŸHNIa WaD˥)⨧RjܐoC$2x'،ʵqt%1V|gځc_j>ϚG\8Zm hcÇFfzt!Xe;>GF~o?g`3n{/#F2},|^:&K>-S<m]kiiMR1A8{a%Mg I%Wkn| q[ Qj?bZ\ްO}|Ś,Zۧʩn֊8IN6~Op<6 qspn[GWwnҽOwrЁqv{#12{;^0~՞o\mblny5ao:E. tt\4蕣vY9>{#f, 9x[ ~gQ1\n݉M\2Q>n Lk_BJSWP/r=.0ߚ(#lNȥYWiڻJ%sߔ^6!;飼H]-y&DvUL{1SM-Ҷ 4o(M2i͋;w@Bp!9kO"[$$%+Cn'yjKKZH@3dp p7 Q*m)nݼr&X$(y5EdaPw(zjhe0:K]fcɞ1%Bi}w/c}?,++RЛpU$Φ じr)Dk;^CDZhCs NI=Tk37T; 貓߱ǿk3gYY<_pt Xv0^ ,SEo$i&T{N%\ c1I~dv术9ZL _Nj#A@֒&%8ͤ ْ]eX!9tBfGN tfcRWht}n/qѰ^95}[s; E]^ $;,4!A.t`萸)4Bb%AV:UHEL%wB*^oU%)>#R\Lc0Ī_zsYM}y 9|r+6FΗ@ـ* ܦ*T׻-kMHU+E6{A|y npS¶"•dY"0@◁pyehW {~Cy$ݘT筿ᣇDzͷ:s;-|2o %'@f X[&JB$:D[(jInBDry;i*ȪyO.1~q\HO޷+9 䌓HW}n,ĸ-*%,Ra+!Tf7FJ7kcKW$b| _h ,8-aWGˆ}I=<0YΤr[osIxP.hܯξaHMhE>=7/ `dx'e8Ujwpffa&. .g-4%T4^*huoWy,ʠ5,N!J|7HxkXu4a7Oi>ן =w`#щY!hayRunqT*q_+x4o l2+@Wm#|w[͵KpKE*g,Ӗ2Nr,l'Lq 'ry˨z3%36w=@'Ha' @Y0ap;Dh2Ņ'슋e 늀c^9 .RWK]E0-u;n>sE#: .H^ 6~ ه j$ZӖ4D/F'0`gѺ> 2k8EK_^u}SZ7e'Oa8XIUe a;OdLjX]Ġ{)&u$7KEZ5V 6EnSrkeVMZ5Lz?;]çLbwxF@"侜VQgZ|Hǒ>rGTlnUQ)]5XILneﴩ[jҥqKsFҤS6Ɏu}"I Zl1 #aWy"l~BӳoAӅpΈY\g࿎A}Tё6> r!?I[7ejX0**{{ɲnt l8d("M11FRJBN+V]C(73"Zv8ꜬiSc'|=،\V݅Yƛ0T8 ,ä)4cۣ߬>N8~_BVZ~Gjjc(Gɣ@%6:1,ctshO,v309 XރI.B^Wrc% 7dxnD` %7}H6cȥ_|_c ےȿnj]*ƼfN=tt֬^ġ\gMT}ܘu7Hx6!slղɁI7#aBaI}y<UiC ':ޅbE;nj;)s˾b-9{W@:Icû~zTEe*}T;VEFo`礘JQA>H#\NwvM׈YJESDkfQ{*0+ٹR"z Ͷs@aʡ誫r8Wzze"f=A~'k%5@Z\3]R.Q 78,9>N D_-ay)3~5T{ =\R wO7u7 `O8vfA33/x+^iY/@JcF]!a+&vWDvRpu=1]SSwgL>uTNroiŐF܉}b?$# v<vF5"T{1|H^nʠۍr#jVv.:k iN Hd?m"եޫ|Oϙ*ןwRyÝM!`2~r$6Dhxl8ئ hUnHϯI!Vmk&hdD }(CQ#"+iS~tUގLXޗ[t%[#&>pW]rT|L%r^s~m0 OWJRWZ.Yαp B. "C9`^h;-#-˭K׹hK^$4!r d ۥ3JeHZw+ÖY LHj/7|>4߿J 8?a]7@vM:$W䷯rk6G F˘B!S])*VX xr?i﵄,Oa|];ȰHiƷ O u1l9E{&Kҡ!OqJR0N[= M _vwx=`HidGLy=_0a`>mX {{56A*}A"1v*(A\-xQlpVg=頌k?;)jC"n*o!̟ r$>'HG)3mT QSӾzSX٤kDoC0Tc;vS(ʥT48@S)(9x(_]fy QEj&3E_ hnóS!m6Nk58JTq-1sOzu؈ЬK_gHZ)"e\JB2VΔ>R&=iuT^~KM}`Gmع{VVHh8ua Kd(@ïjeG9s"(S[XIFS Ǭ/̘r/8ey;x;dDמsnޢT0uvxV~C UEf^_Xdx1\ew.Sg 6D xM̈@g|/vt]M M0Zsq|De_S '(_YRI0\_6{E` euy̷kvbňAX,lo]j^~Lb^$Aw,{ҢdݣNRs 0g6wrSΤfS]?i`.˱.Z hu=3Uχ.t[=q6\:h5_z7'jn(/ܳӷdK0)!/V3ƠgQL=7#ſ)!r6dj ͏PuF0XмĀFS6Y%A?C7W-\^IM2poI2Tq*Am-Qjo A`p/  HW)F#xQ;j`~aEGWm Ƙ96 $*QCD,RyLpdnyRռ-t<ڭw[HAZm4^[9N90BA}T6:  CAE]K/:gB[pxәbZ?L)%{4f$n\tx=cqmB`Y ThHف<k6jQls [LPLX 濆 (4sӢL\Ճa3D ⩽p\8Q+>]zL _9RRПVV 5έ05 "YL!9?R֑:SlB&csoЖ^jy#\vd7osDiPyGsNU{tQGd|UԛY? A~': eI }֒2%GR=hR*b=N̈́&#kYy#C6KP͝4#D}L6jOr.ѻ硌9&hWMތt ]־0) Bt#CHY :-M}<,1ʍ0m9!a`!͉ЬWҩX_g_& G҃MϾXgQ>t]f`KixD? "_^-RX|э%5Cx R6l&99}- zVȌTKWMLGNMܪ/FmGf1UG.$#2b"[!z\@M .mrZ =K/ٜ QS$\=*]kuwyAmg~эUYZPL_D悟hl˝vjpԳXa'PT;n>-'"^MFJ,y[׶#ݤݫ*8/ i̫v+պHGtjgRF u ocCΒ{TNM|$ʃ;vP(ᣫ)u\=+iB1X T]doe\Xd4GIҟ%l2[!cG '-NIN_`x)/kC[sVfZpZzu[o+DX(ِD ;R+~c4xCm'H,AGdҚ}1˥4a89hEV`P1 ZI,~ yI.x=H9Yc;'~PNHq m]_Ҏ_բ!{"~d cV⌿*a R9X/$%ڧ}3Qe?שY *Qp%tJ+e酣~F̯^YOA^ݲ*ު[Vð!OSLH Q|OvN}5zԊ\F]1g۲TPj㬝pnjGBl !x{}AηlXVzhx :LdBpo dAJ1ʏmw(828ǹэ5MxLOő!֖ķ^n m,EO_%͓Fi#w5pXìM1I#AT|`hjI[~%LF=nQ{ނq;l[TZ_q#_b0{Jcme.]{k)~eCJɟ,/r 2FQ[y8qrj]_)Q`^ 6֌}&PٿO]yaIKz,g ~?Py-Mk^-P{nMb;մs Eo'pLD>IA 3+`ǔʖxwŸ!9B}2к*>9y?Lm+ohwEUY itޠW}5H`{5+K',%DJT%~$mSFЊq0s@_ 0NAS>A1Ζ[l%Y,{RST8*b!-GlA75!܏X#A+96]V)a2Baw/Nw2Mzhr[WY7aԓ1B*;aeۃH^/GGb9`,KCҹq1ʶ%E}L1o˽7;jG%<*Vm(Xcxlx^T=R6uMܻؑ8|Ft;FJx(-VM pʲ "0Fs9*_ &`f Ӯ~mUs[edYt'F/.ŝ=xU(,2x^(Qnt>?:j *'RlwKe&9gt抹[+u-a9i"d/ W>#r7/s[9(fY e{\{9rȰ\?Ul퓯#Da"T;cwߣFN=(yyW31:t.|Im@&P*D:a[3m[Iǩ,mz>Z0pT8~/~#f;dmM&@ :9 G6DFRpBFbh> /.DoBA%C,E'kwv}g,k|)#*U'`vMU'۶[~~TWbT*(R=֝ʟǭx qdtIpϿc@&":n =l'4UI]&bao*t^ |c~ vsːoɸc< ?"t QMŭqq#r]xR&рX/ eBG1rhwmdT<)%&{&iP8VRKo z>6[u"_|ecس$ dL1M'NFtˀ;8 t8N$)`-PﶫPUx?>™!+8`&6d}!C~D2)'fliN{׿,=^ BwO?¾jD̺o-c8s Ѧk,^fnǧZK48%\b20Ŗyq!їw-B)`gx4 O*,XngL(S(eD֣wKSһEk7IQɺ灧#lY iik3'"c@<_Blz! W1xu㮗xh]mGZ/+--l2 XQ(YK'Z(q^[%_iH }Y: $Ŋ6x'B"ےHV-' h5e˼E{ =fTaG!%Wb /_Sp.e۶m۶m۶m۶mm>ݧbOd^ԛ'5*52fk5qddU^,3X7H+bM.ڢ@(A\țSi)H&]VۊS-y,4N 1,q͊>iz+ۄ?k)84Zk%j2 !'p"bT)hߩ'$b$`S[,c&Fv*Z0,#$3{MXQB*K6OSjtPDdJ{(V8Y@A}<-y"5Q k/YH$ֈI/n%kΓFv G`'Yp֒b)5U6=ʏ뚸T!S_ ph<>TEvlȯBt`Y;2BTtɱ:)"5n&p) >%L]M:XhҼL" 5O+]FG$ qm ̡-!:ִ <r+ ݄b?WzxG25߁R"_O,Vn#{%licy֥Z >D99w,6}oRDI-ٖ4cX4pcodrȂ^ Jvb<)eUh,ɮט8%u\'z˜ݐ\rj9nh0yE)w\~{)hoDc1KG7PU|Npk{of+b <8uf ";)78Xmɱ>/[$"cO\0axr+b>%Cq8D)UPQ? m#ݩ˵ @]+bP#@^%(`҄Ӷ\OfK޻|EdKxkkUaKGobyo3PV}rœ"1Iwo0 O}-#c;ouj挱ko$?d0| ڀ@OYGg8\Nc YORpęvJum!yKIoBpq\UC8jzxąP0Y:0JH*D%]Zj}Y$qsW3>o5`@!&LdjN㜧J XU3z GxC{NS(PLE5]ݵ}?/Բ=eԕ 'u6H$rj*L :-fܩ3E퍸V{H#G,Qu?zxZo)Ff甬w41qi>%d|NqhTI=+2oxw':< (H}jEEk6k~k?ɼuY<Z.79bs|a-rp#fOh6owl'S ”|+Peccmc঑̞96ګ3)$r_pG鍉ٌWE|5X\x>0Qj=S:\w,VOVZp\]ˀɌ>+itl:gHq¹:@9 bjw&T^эF+P e~{ҺwpEqgFFMiNee5+O}?5p=)BerOxNwDv;DVn_RY"^B,vD}JٜFSu< ZZkGd`|̖?Acg^dR0*sU KjAU$lܛXާAm~PbݺRC5pAd(AeW5FXw҉{FzJmyv"o?@B5à %b۟Al.5Dt&$5 |I.iWWхjHوbַ5r!IW<wb#önN6xhtDe^_:݌36g̞=T,f 8h<{+`ǑL@q% >BENak] Xe-pMdHϚ ֳI} Zqd7>nwda+س'?-doHX*Pfq+dF0jyߕ i2wAo>wv}B>OTMѢ}~Cfܤ k5Coud>ɩ ,C9~]+W$ U\k݂ WߘW-D8 5$A Z3t-=ҞnJ3*QG %*|*a3~YYxӟ5o΅a0:逸6,s{q%LN5qtDT _4%4ɘ7)oqZEV\m\p֕4}.Z`†هqҦqgmB(1.QbG]tZ1cC| %D|RՒ d{4M .gG t1A00y<&d, 7P ,r(ziDFg[.,TEá' EIj 9 X%P&$/̞^C`8yN^̟M.h+tq5Hаvۡݽm% gyRIC,uʵ OGkN_pĢobS3:3J~y3e+BaN/'9)Lf9QoF3JMG:G@<0Q`S">c [7Ve0 [@&'/]La)D1={+B1t278]Z~4~,hY; Whފ&}3 HZɚʲy'GQv7n/zwq"N*900[QθEoU_2[kj(1W<=o>h2A)ƒ8<թ˵A#,RZsHc!&<הj܀~ϨS3ziq>gD_~?8 .6>F Ly˕eRUm}=v尔Hwt>G.Lu{ey $5؈bi@C+]:xДI[h~)*7JߧE6tAeOt}:BOᲖYuwP h @)<_W}/PPjfDGym!Uv@=dȘ5Xx[;#̉Ӵ> Yv21]C}2 _G_. kؾ"X*E䢎D)sB%UQy(lH;P9X$aJݖU %}8r9K%s+,n@_+ʼ{WٔF}'X4ij<Οg6AKy6*H7+//5kTYf5{߂vRI\07m QG.f$e"ֱ1*I! LTު]k51$BR"RNYRŒAx%Vxd,Ӧ  Z,JՅQHyvf}MSnSfLr'5KX'`y ԅp̿h6BXG'Cni T 3~Oۓs~!a:#,0Y,{ʰjSH^2HuSu`ۭx eW´KS4֥zo(Pu~$&]_# >zА [ݨ\{ڵ Tzll(qc*ϼJ5XE!FSݗ{GmWOKmNRkj*q^D9=vW:'weVFi UuQd\+gDZbxLO'5G*_V]yoɦ4sS y y\6 W͋$1?p e\1*ndzʀA;AW&2Vҳ_!yYhLV dD\t+ ( ,iBTJ1A'j`]1֞{ xն&+ȅu(lytE# ` |ZC@iƂ< U$Z/3d\ίUq4m߈m0ͣrK7^1)m{)\VKQPS <׉El^0yX[!ܶÈQeAq|ƥTe `++Bz8"s=%IZ)->Azʡ E}ު5;SR>IE~ UPHϽ@R(=iM|4Ltz.fٴ)plŻTai6?X-CحZK-CE BK*-tLB' -O4N(wprę~jV?|joiJ?Y lf; JBMkdmPnE=/ r 6P>vkgɞ?Gyլ+0O<b:?/ EO1$Q@! WǞ3A='j֮ j9\88LܼJ>9"<~щ"bqO NwC+FMUYsRq9u!my:))}?#ۻ]YIRCA[ڋu"V|r# ac]igdaMDtm~LEr2.\{1\g [;"2YӴLq\tPݤqC8bL~Ъ7$"yBg!Yo&Xxb 8D> P @Xߗod* B9ȱ(#&h+wՅ`:0~KgyB9)ۃl 1 !$-NM 8\J%e,toZpf3I0M3m&yEzP 90HJo~Pbvv^3,!_tyOyم54o(!ԇkyH+QS2pѿ (f(C-UcAlUT|XEplj}kYzN9 `2Mu`-5xgt4m]aFT=7#UB>.X\~ݸ!^@myJdyױ:^gb^WOͷ;kғ:WUayQҦ)X̾$GywB,֊nnՊ>$(^ /Jгe׶R6T_wٵS>i=2!k܆pC8niZҝhao}aNCIWϫZ[m@&@> [O"-l[ 1%zᰣZ灋~5”k,Q'`CJvNnkENa{QKg:s7\5<ڞ9Q =jTo.sSzja姕ov'V柨ܮhFw_WN1oCO㚜.;a9SN r^}C&yuU!*x{=)^a{n*5)e.i`Sp1Yo'ҟ(M/ɗ=\1~m1a-Eto_N2y㳄Fr<`Q;,C2Vˏ36/VĦ#{r";N)Z3 SmW[!ؚ9A_2NqY# Px`lC$Bey=hd'ˏݹH,+ xQH )``\ mmV>2iyu稥@O7cOvAu][njK/KI@a} [ᕡ?$6=l;g%c53?Q4\[muzŐ2k_Sg\'%%s:``ߜ)۔|GDN8hJv3Nie#, 8淾E{O*] ? 2N; ~(E:ÙzaFb:Tie2;kDkɊ>3y827R[( k&mn/f\sKUi#(|8[u>~vԃ&M䈂"a,qx׫ZNţ:‚&/` Ms`h?u +eGj.2IR:R/if2D5 9MRHȂ|s82:-l+k>&mmȒ7Y؋%l?$YG6D5ToPr -׿,vU,Ebi;O1v? 謝هڵK(oU6ȱdv[PjTkMdQ|gϗm䅃& F|H }͢7BYL$ rd-P{yW ?)&jE]ĘrNW| b{{wM1DRFD8LlP]/g¹L8jR)>Wi'e-SwOGFUРĸ+R!Z 5¶#^աHҴ|X֬_O%oBA4Kb[kQh ɯ1]yqK\uq =-SQ ץ:xHW:3e[@@8ജ\As&˰D&<⳨tE$36 ?J%EuMJ˛| 8cLcR1dGꮾ' ،$)åGԠGu͓^+N vu_*wTN%+G-ձ YMfK{9pK!tYCp`2΅ [ `~ Tiģ&+_KA< GLYB2;n-3r+BLk8v8yNedT@\K_!^Vig-cdVĆLyDpu`cGWs.h)J Y^9޽)LqPb]<^Z] (?2 ˙RyLr#^s򋚑qz0gG)k$fV-% w)Nr 1| q BX_obQʒF^! Ƥ; eV*ݗ aW v+HJ~yH`OCS/0V[[}S(L7{ H})Cؒq0t1?Uzwg"i_Pe< .(1h@tjpOΟDV6F~E7|Xa$ʀ䱰D ǀoϞ1ԚS+am_099 ql%{=#^3OHQ1+^L0H ͻf~ib}h]gcQ ' PK!s/!OgqNrCw/_Nߠ_\&Xprthdud;ͷ v-l`{XO7}T&s>d|L)Ag _\y38߫pT:`" 58iV:5'}\ᯩk#ju(]*"g 0^ܷOU--B S!q8>a?>bfJw]MBdNVԸv.Z1etW~PYےyɖŨ_q+hX n&̆tLiZP*㣍eP8 lP@ee_֚UpQy>~*Lеc4-wdDY&@d71#򾍪Y:Ofq+,^D7KP~*؁~qa;zH;Iw$74MN"h7߯^/;ia_Ո;L<% (y=S 6@m?⪋e}Yr: >Se,=$ɩr. A}~`!5GgXq} WSwؖ`T8H2Xnjk9sCĶc@$C}O#h.^TU4L4E-I92r;4`trԩ܂{3 Z'Ϝ>eezvl yB-g*xkԐKDeyG.޼£nh}UqV %7>`fh%֔cGf:Xhdm<_tΡ㸈~חՎzB iv]G oDOLm7e4]drb\1C/sm0qb@y~fjIȑn$ޔ[~ΐnfon3WRhMa. wej $ˡOoy_w}&3*&F[ֿhgFӯRpi#TfX,J֞>Ěl@VR)D&ܧ# 9{t3LWJ#N k蚿UWe10( \紪; l f(yzӰ ߰ eѹw鎠HU4%vy5g27y5N.Q*'\_IHnfpmiG )L'2P׳HM2<űԓ`.Ոu9u؜ I`p1R5gV~KR Bۻ88wBn&Ff|9j{lQ7DbLkumju1b" g=%Q.<< r .?O g""#f=g."e ޗs*/ǖ57/.]vPuĨ GjI|=U@<#+vA>"64vdrzvN%iM* d#4L,QSₒJĪe\˝9E',LVRl9Hx:IPm4vXqKk6fsruXѳ'j֍ɕaYC9#K544Pm*'7cyvA3&aA9(A>mk0)->ĵ6gT#ܡ.p"yAHIh}o8X h{ Օ0n=e!˛uJm ziVE V!2Q>!-Z6eP?$Q=Nm~8x)~W7! xS UY%w0#'lbnS">C󸒐n[Mq8Un߼\~$€4%", ,w_8i"ltA΋StMdaߒ a;iQQFA;!"Iٱj vXb7!A|a1-@)ͺ(VGJX{a ik0hZA)8 zjɊ퉤3~}8^r\ujS:k-shv7j>! |:VDY,/nE*vQ%1s /JxB_VT~QGrwxmN;i?<rrѱI.vbAocew壽%shܓŊ .ԢJ҈!uBG{t&Hو_)obcw 4~ppV@C/xFJW`rtFKXTk NZ^a!YFSF:ZGcs!@ ;7D~}j]nC"@3V{i@vc@_tn[]u,T-^L2bVFڱ4?44b,KŎ_ es@5䄻C+$`R #?>+*pۯ pܹ%YvK71>PPܥj H3юgXX*4;1]]Wfs/X;h6֏l=?]Ƶm˴'^} ∤<;~}ؕl.L<J"P?*Gl$l.Է:f+Aհ>5@?G:~JmG}V`$/xuө倖mhggi؍;!o~'PB:KD%A% 'TK8!M#5#9odI\o?Aaef[bOFkp%n@H!kdz=TӼX4!!o*A}P_ڹƷn|mE@A]К_E/Ӣfm` 2cSb0ҳ:a xbD0`RJJ~p+y!,>w$+"t:YMwxlq#>-~(6CL~PtO Vjjx |j.ՎM~XX֫v8ב hO{mߞ06[6ekYDMiQ\F%е ughR9cJf紌`o墐 W}ѝ+L>fVRwF]ˆv;ĢO"MUrgg$Rַ! 'ֽxˋgWT ~nRaP c vL*)Rm5*_TuD@X4+-\MI߾62gM-A$Vpx>y1g6N*{jȊ ?U-U#92{y ܳ9{i #wfv:?U |FdGq *}SNU HGi"Ȥԭxzj4ּzv˿T<BE5'~15 T,R1V=qR3o՞pc< GRʚ,Rjx ԩ " :AzU2}= -.nʰ {yjr"*$, c 5BeapZ!<&i&Y|t< ZnƱy*Hح-2QHԯǷ>YS > Z#w?_O!v?)tϱOHE֗NǪPO!̺y'U+c,д~A^k5-QC7sK#-Uq`3ykl3ް:5Os8 Pe0S9NŖ4u}Q0kk}>ie*}'OwD6:ϣywxb:c^ЩӢO>[!x%3ۥFh'eў)\nm>}9C w*\ GK;+s; Tǔ YG^S"s2ۙ&&#%.q -K 2k3iPWx\f0Pg.&J(M@@ Q *\g0 `+<!pdqeЯM==n wqC!FXsՋ :w˕ G|W>c /ǐ|gc&By8Krw0f1Vн`y䬊+mesuzԣ"}۵z/յ}7'jEE/.zŕY< V;J-|/3]ėkc koP}I1>ng[ U6jm2dFLѽJkJzC 7/6em;g,FF@8YK]N` Ot|D&'OnF$@bϨ˰ң$=Ia(Rq*ѳS'p ?mWȜt>#`!hVA<$lC`冂57jޅq}3 R}D-ѣ@93SdL޳YK)b4 )[JQ1s9 ޺:[%fg JJ-J,~lO0-E3\ 3]pR/q6>(J%y{q TjRlMa۠jDgSP) J}#EMnOmuiۚ6ˋQwgc&<.nm&Wy$BvWh6\Nu]OU6p\>X7*5I4\Đ< twj^cG%Az5LuZ:VDէq9#2Gv z*mJ /=at]ǣXf ؁ƢsRMQz]w٘ +7M)kt[9Tov7 .j}(& O楳 j9u*=rkn㮛2 p`EK?-Uэ"eZz=Lu/H@Uρ\AFϟKNUgǭᱚB[4G+S|zd+"牳x6׺Qk ],+(^C n#̖UQRgPW)`1]>Osok'o\rg{K92Y_A2 1)/FyAH 4N] Iu>1Eroz兩tWϫk(XBn"~=oFͦsN3'A -RM=ǀ O>ddF3X0N^Yz`D(1̾*M}Ԅs=#:ݗz26^2D8zsGPxEmͧx6[VSڷ1Ⱦ^MⷱH[~NsU*a}ɃI5)\E?lWG>0q)gTnׁm Pi4436X7WH|V}$ej0tSa<aHI9L=g[ vd:< Ef87偗Ӈ,TܤoUmT}#_+'4qQ#cRXDU(,Yl 90kRE\aoar `*>9,8Y@H?=M-+ E:#dckfEH1PoPl ^BB~yƪQ=e̐%[ .@YNǻuTt\.D߬{D )CWOۘZY8N#(F,v9וRXoQ;A^vsc'3@;T>J/[45>S$B]$My^s[;.eFk]΃CFlܲ/{۴[T//7r1g\5嗸n8?lIDܭyt*DsՌf2a0G At=(TW[(!yǴ77sFp4 RE֒/,Ә&c+&s!F&S=FqK4d UxD5=9 9JǑHҙ&;k ZϮ\@Xm'],pA ǔ1悙.#m-o@W7@ObLNZIں䬪 aBzS]O?FYf, JboӖ; *s?ωuF3b]Mi;ouh lFx<}6w]FMŽ996AjrhTNdMKԤw8(%&.t2lM}C&I%/3~s=aGFO)1~%I&gWUo*hI"cWԨ`b1K ⃕I Y'i`a,z9k1Ax[Q0<χ 4ȁK^BN~;ae15oPmkCs& y@P|!OpYo`=8z$'iZZ$w~9"pQJuXA"S=&0bU:s8|&E~-7okEVطKJ8@DQ5|.%QU^ôq- LBt_Q/f B2@L\f?~}Mgւ8Jdٌz9։*S|w$7V,\<ؒAw'1*-Lx0DVPO6h?d;yw>kb9xS\scLʸ8?pUj:u0>T`w 1',*\YRZ /ή;<22^D.68H՗˼.#QlcqaGd#9JaR YnT|[qV8$gJRG3A1%#W0!o]TN X@+^DbML;hy\zzDe˶XWX JJsZ؉g%* gzHIHi $eO|&}Ti'G$kr8yPAZtjwuUd&Խ12Ǝ K~1>ŹY̩?` 8 +ԪBkU8plt\(R@=k$ۙx43Ƃ_(ӁN>am>UzP%ٯl&7 t,  A].'S\s>@~0ÜC!mX=  M,/ n,-h-VrGZX |+>al4^ө;!Iֱ 6,vh?sa * ΰ~z(p?v[-ڔJ@|¦e1w.g;'>xd1 mSZn{P5W ;)'O];-Ebz+؞PQJ9b(vmz}8b]SCg#)'Fgp|SqA7Ɔx7W琰#$,f1ru)ĖGdҼADfPO]=p/@)l \QAKa>@Y  ټu]NdlqcA Kw-Oum \iVgo4vĞ.%Ez=szSwDu( Յoӓq&1R]溧f0.rf~D)5Bd >^U c spi?$DGζ`u 9 #^L. w=+,Q?6Ϛ 7P5$o6v<sZpG  9ͣWC=E KX~xSug]%vZnz_/2߷&)bi= RF)qk`& Ht zKiy|#C s|;0G[= !?S,U F\ىa>oZ_;nM֭'ENԴ:;hm}b+\c@:&x;JMGK ՛DS& G|qM6ה ^_ӇMӮQ wE07~k! Ľ෍5~ Q~۩+d eUxحL0* 354ʜi? ffwIp1=HwJ 9M6N؟p[!v6oMqbiH/242d5M/Ecfm"emwΞ1L*bfTRC Ft_ 艚iC-`3%-&c}(N ^Xb(=hQ$bK}\#!awˉkHeG\+B֣\0!UWQ G*9~d2N*d ;#R *%:*h[o{ΆՌףVQZ&9+^A)ZX7YVzաr* n X_ odMhys^\P1{$с-6Sx߆AU<3EE&*GZrtF x{m]{DMŇIr r=X3/x(T6 U-[40x_s | e/ӄ0> ה$gifA|3zYƊS_nKRӋ-mLo~W n+n}p1<m_c1Y >ܕVLY2Ϊ,x\cI2\ {iV7Rn3p$Uj7uSЙ"Ms[#wnhL!\ _j:Ӊ)HFΗw9Z(z ryw߾!A& GeqSjQJ,{A4Bd\6240l5?49)" 7[[yѳ ~H=H56[2a^eL5x")]]oQ6.m3HR=?EL ,~Q/cY]h4+9"{S< CTVGy+UfA}I"W^y>%J9/c <)N_He M3~FX{0|;UrJJ aM`0-YtlkSC!>\߿#wjVLpc-q`“^k)][jejO vws_JyhsB5F6=P$/-MrI_~bi~b\; f2DkB,RȫKhtń?C.XQ`gדZO Ƅ{:Ѽ05|@i]ywl3.7e'NfgE VVۊ6ST>#C.!b+HΕcVc%Izz\q-{8ťwI0T({M_(7>VycM,c%)"O6Sb`bj]M" 3pѤ/)JvA=BD)kRPrY͈HDD2;gGRuvCk+7l xJ.|W9t?p).(f8VEұ#ͳw54oxی}=僟TO42fjQޙ65X؝`Gq<9vq1 {!y(rXblxUu |\&AYӽ G?J|8ŤJO+Xo0GU5t}it#$uL{vߺ0/.t`gQ )'M~0Hd=ǨV]y3aW6sl[)3p&cPz#Vv5޳Fy yö! iQȚv|'Rvr|-M(@Ewb|8 ?A{ 8G> j#e+MP$hV!R?l`u4 0J9[rlEQeYN$aDiۼنuU}WJYݺ Ymidd7#|cMFYm&)~ `֩PDߣ MBv+[EL Dbe5FO319 r#N+ ,(?x %/?ƻ 9^};N.T3l$ Be*"dpN鶪} 97~AVU==!fv-;xbbj@ \CV0Q1O5[ '4qwN6[U1B= ӾB SbKW5(Ļ 9&YZ #8er6 =Jr2Bu<Ô(Jk,}<FK*"ɝ\Y2B8@]S镔U Kq#}Ee]GjI-WzhB+:h8 .HhWC`-x<zFM.+2"QhNXŢG' Ioyj*w8h  u+Te~cʇa)q|p9S~Ъ? ͦvqQ*e\}aUڍ 4Ӱ\TR/ L 1׋ V Հ.ZL |+S^NT O7 Ěr"g''x+E_JapO (x DFY]0 UZqXn?Um)FsP)Z8Gȼ~z>+~r5Zfd6;C`u$j >6$ Z dKc3)j`=iOn=βBGb=^䁍a*`>7f㨓g9ɖxp J]u./ɞ;2aXzͤ*OB`h>>pjr}0 J %L&g3=+av #ԜCM;{vIm!C;H/H΋Hupi{6И~;i zQo|Kh;+-d,^;<Ŷ.z R)|<2I['?zNؘҎ7tO 3U]1%bF#&Ie]_nKԼ?` Y%\H'A8mH,x- j9Wf NmT5yVHx-JѪE kq8u}<.͆V fΤJL23 a:#ٕw,0 /m+K gR9pMVdYyStdd;:@pEOhV 9ceCddCmy`D$"dw9օDFn:$ņYeYt6P2,F.mV! P}kHBP-9nMͶ 1< u$Zdy%F4>60UtIxWЌ6wB}BRpwrz{W cUeYט'\γG䕹Ĵ)F,ﺷ n A4uUcN$O լ^ۯOy`Q|0=܅$bgְmUoA͝F}JASo6ȆQ3u>Rd5 fVc{KN-u !ɝ}0 j)Rфx ХӃJ|!lU5no&rg-ă壐iyEYrR y"3`v,9@GIXC1\8&feg()PU35Y`r'y?A GK>RmO+әIB(As#]G,4YHtc$B GAw$k_1'"'/3nlIv{q`W3n8™eGDfvjQ@j\Cxn# ԡ[^OTTAH58-eUU5Y ^Q$#o>4}\qdɑe"[mH?5"a@Cҵلz:i.R2FXtsز/XJ1O^)9B_ɝ)϶P]{R$h_|`I6z< !xcb2-IR2퍆UlUu &cp.ڂ0T#L83y LC޷Q0aj iVpu-lzi"`#4]m! =FYS,RX t %gM 6OCW.BcqL2RcƑ R\ NjHa+o ≿qOɲ&-w tG-PJդq ,$W9B.إ%KJ#KxW\@3'ZCѓ%'qAM;|!!1~} svaHjwfξH6oti٠0";<+vBj[ X7 ctٕ@m=^{9&.ͅֆ? l=PEȘkxVr輪--յٰLmyݫŌ-xc{~j5Tk*i$w{u 7U;ק]Լc v1g O j2+ݫh`+ ˆç/CZLD| *m p^160"( rsɘ} Uּʓ` ,ǤcVrx jǬSpFA z.59fJkF\`8G;dqBk1qe ݧ hnQoKL'!v6\`EI{'≗o_0xr#Cs4DЙdㇼpi1ؔUrG~0!SFlAֵ < G]22dEzȮ҄Dmx`D3.%FSg'i۶uFg#cM~?{ m[r`"x4V9d-KHWy,U0F5J&rt~N i.b7X'Mjޞ l}aLM_W^"!*V[ǻ)4BX=UFq`UH]fq 7H&ɱ6a^@Xng54We)޷Q V3l'@HJ#;w- y)6M1EUt.}wwEz抢=Tl-늟 W?=ɠAoP5&LO*{æ5rLw(^틓ޏܗEě%tJg>(?+;Xٰ,eݻi\`W4/1o{AB9/CddE j6T ,mLEL<CFˊh;ہ=IƄWSI0oم?]YBPP妞ČM}'}93HBO`䤍;`O[VsCwN~/M7d4{: l|3ΗJ u?^SjB#_s鋛c=S"I}f+i8[|mMX/=07w8B.dA?t!O:/cgpeΐ@=F_.Mrا:Zç#)pWuCP)Ho/), sWߎh\T&"A1R7ULBkܡs1M=E;|ɑI\Ea.L.kóJA:ۯt [~7a<Ռٌ:M՜AtCEרPqw"JnE3ٓ m]Z8ĻdVĨ5Ir ] R{r{DHE<}D;Hc,%56\/w7 RBI Q }[EU y![fxP7B]mAU֮tlNqS5 xwौB@H*b%aA2>@!2Z~I2HJ S1gRqC4Nh?)'7勹%Y:_ʼ$͞"x? ѱ}ꧬQF]EЏjE!ق͎].{dG)@uQ B^Z772dkU7a`3fgYSmi&%!HcF7af}:}Ϛ,uD=ҸU|\t0Tr an,O_jْ[>:qXXh2LÂ9z%mrZ{DU+2jw ; Ko,@bDy4KB`Daj"v(|S|fvz9&wGzEO`à,}0tZnuV9ŷ+Sp? :sr/u/ۚ(c/q T>5#Y&D&q fi _t>}嘂Y:6k YvirR=SZmס:`sug`U78ܸi> G;LZBe* G\\)\cّ3"sûz3Y¤DJw s⣌MHbuf[ O?$0+0q )I5YnH7Q6o =6r/,Cj]dI| ߙvk%C"W/$μ(į*VXa٧ބ0k'VN.ר]⁵1 `5bO0i!3ok\DŽz)|էQqJmk--?m\2 4&}wO;C^!jލQT!nҀ!*C-@T3UXEkT951Zq2 >{´|!|.om=JnMU:0`o$1&YfoԫFүړmZTqS?O1 Y#ȅ& zh]hξjFKoE"ւ_YT$[wcWLuB)1WҔāgc{ƋkO f?x6 FS%r)7{GUR<0MfNG(/z3RWsl?gn5]>^l|k 9T"c'fm ۶<,{L}`wZzE݁(@yH+ӷHާ} >Ԗdv 6P|w)XC6h_vUA5>N S"Eӭ"7PX0H;8 *VPO- RGwq=?3=$Bh1M4u,3g_eeP z.t4пa<^ڎ怄q"ܡB(-&žS&D;E6 j$3wb<-b:+0k7g .c+Yn~: q~xUS m0h,ØvL-9kΤB{6|F@$I N3iCdW+VH]P/ّB,,[K\4Wkb{{Mߖ8\o$*w_ny套G2N!& MH =:$n)c1"}}/GIPUN%DU0D|4rq iiDzп[yUI ψ㦔E#" &pVo|2Fa^C܊e5P6 63nm(aRU?0ŊG^_)`^h ,ܔ!Ȳpe$d?+>XH2r?oB ҙaoY?6/3|HVRWBWgnTM(Y05(LkDiV"Dh%Q6Mh@NC?o'_%Y5:FO9N 6u%'8ݠaqh͙5ERdE*l%āʌwhZfMc|?ߟWOw %G%[;8bٰ>ԣ&˙#]nˡ{#NsnZ2pՇͪn&|6%kƒ0U[.mK .Uq~)u?Yò4%*vdRg7QgM[*#K3>ĖKw&]|%`m [3b"/ ="m"څBn#5΂u㻋|U %LN56ީbW&J\l9M(dVqvc^Ƕ~Pqu(٘P j 8rj,/|x6btwC5dz.&.U 9iK)Y ^jr9:JԏnB(J_ŧ'NHvwoAWpg1E 8[]03rk-Ia c;A!C{ܝTINGCm$OaZ좍Q$d$E\_ȪD|d D绱dJ4?y^^8Uڭ6NK5! IbN{b[uGmC.q~]sόrRW6:Muqb{f qIww!z/lU&Q.w#w$s#5eK=>* `Hf˹}_&ˬG,>iJ(ߴ@|4VdNLߝ@۹ԠEޟ6ytv#n58Y1u]QޫzZhYFj 'MjJ!{˫TW@ 7>14SJXw˗(2"_ySsHiY9L«}Sy9 ~._:k!6h,S֜D7G\9d AYՖ%"jP}V+8OxbzFFr ۼqv X8 sXZ?Yܮ`'쵲.dj0$|43;ztdLIV*B }P*57e0]tEfEcʧ@t@ !KhW-oR KQe" g+';cdBzWS/9I^>4T;P[(@dwZRL!aaUn}a/?Y-F}[b fBbaR.3{qL-RL4MAl|맷"􁧾2㍄ MML x>0 u*D衜|9w`(~&I8~P pt#՘IL$S0ePWm @\HM;].1m'1GDZBb{ V S<(:g Zx]B4<6U {!"kߕύ2Z]).`LE+Dк͊|@ D:c(HWE mr0j@Kr=)%?V ?x[ba^l}JvшF;ЬA)pAj]iA [N0wM8 ߛRo9Aį@I&V>!vJg7 oB^&i[Q# mêZ9oLBH{7%&U ]U)}t,9. ;|m&d(r2nWaU?ȸvt} eJ1UR7!۲J\gjR'E>n"=88Pbh0(>l vREr!-${B\DuT` #/kB_JĝHqt֥ NgM)ΔГwd|62!%슜^VE_H:8fFF.\%ڿZBrąI졌ɅQU xnSd3󶆏嘶"od&.8K}.S}2$Zg1NjU Ɉt`1}V^v <+۠|zڟpKmj陞>ƉG5Nb ]2y>(N]UxGj KF ۪4˂n,p~2cOHamc-ʉr M%I P}a0o`lS!l`k7hSшV䖍{o ?x /雿4V=AVTo%]㶌%2S =.Ss96=bK[TvWUd֡*c8^0fQt0"%\"(bw{G\N-%*6CrYΈD-Bh1"GLȚm$ApV;}_Ll[{IƼX@R^n<+_Ąo`ك+MX Qr1Z+S7rn@p`CN07cU?;K OFބ#yZXŠEE]րXJ#Ls%B`|7>Bp3>J͔u-@ 鋧AA*,21]xCU\*ys*>u-7X>Koۓcس"nڑȬ) wPMsNq1ѤK(:ZG՘ dqysߏR=;?M[#'\â+f/͈2!jv|,w)j Ft㼧J3:obO؞I09R lb)["`;gDtJ䪾Qz+WplY<~.TG+{`#"Vi~iUq3,UY+rݯsД͚ Icz<˔bSf*僜DA<֔G䨯?7w>oh\ʊ*6┠=%A dH2y'kZzkshE7Lu4сRl\ y~gʫ*S u8&|_ّHQHօC? xcJ>pZ)^ 0a@"J<8qLDVC@ZMA%}mQJP_RKQcg g9$є$:V[\J>Q &3t[O;ұi!q6Ge?Fv T>ZtCVtDSM6hQ^?ù&}j-1DZOBa}# @ΏUs_=v> |%;e϶lu#5]ʶYꯌa"_a7c[[#Y ( Vx)gtL+ ~}}+R5绕} k&#XھD'.` XUAf Dla·s\^<) No Uىpx%[bĥv' $*]hbyeG"[34=.|!YQ ]\{>7mʛ"XC/^ l,)1SN=w 06"%}MSL]-%`^?@T,>Be-Nn+$<%5 6;^8y3v .%3 >''{#_G# n&hʫ~)WBLAdM|ճEP+/V|Kb梇 '}OqٳsHQ{Rӯ 5Kl+`u~bZp w&_$6tz8co;5oy0RѣnLЭʗgTWNO{1tG&(w5#IJ۲[WDIuu jU}s=X6=E)"c~d- Oc|l,4ң+$?Du<9T76qJݒ|]YJ5S҇Hj(XU Rrd_iۊc |y=뱢a2 ,ԿOpdsqӸJ\nӘ)KDRNHCsҞxB;W8e}LA KH7CBa>?dJZ.oV܎S3+s\U k7ъzt䓇gNS/ޡQ M;" rU; <3R "ׂF+ =⹥s#^1܄#}n~h7jIa3w?1UU!'= HJC{ogbNi:pfツi˂>u)q_؎$iޮR&Y_h .HgU\pa&Įe"ۭRܳ2ײHH |Νnr76AʂձnCrRݯNU9u} gYL?YX(a*Ǵ{I\Uw/AGh MZJׅ9Gx̉ÐA ԣTm{gG&S [G8@i`+,njaOmHO4ngBSkl5ŅPOh8 , B>߫h.>SVͻ%8Nߢ#bsp{-[c5[CĂ*o%>hVuڡ~8`[29bvhI{A76*IeZÇ-mF RƶS(_t֔~—Rh4NPcLbيH9 X9+s&v @t^K( 6 c| 7OtPw= / %Qb0ki`zo=hOd{Wh_eVZۜ`rD>jNe^D q%!Pר SO<F; 9O0g}ҫnv9=dm-EU4g!,\ N}Y$ YϩsciHDUHImcsRV0Ӵm/sI>04hSc畔T 8ۆϿq+O imؓYw5K{29~t) RFЙ13P@S ܫoe@-oOw3j6_4 Wӹ WM$#O!"Ć8 i; HD6k|Bd?{SSHxRl(4_w9|].&<)hd*`F>`2"qRR91aQ !Wg!mToLMQ5֮]@Grx=T(h<6('\j&qڮ&qx=ipf:1=wcݴ!7IAY*EWJxBqA 5=Y0VFgJ9Kaoل-ND(OS( ww_ׁ2(Ђ3'b{ FY%R1P-SHWHxE;RS,S7v*pz{l'&D ~-GE< !N֯T teGbɺ{-Aȕt1PZp.lݥLɛ9\Σ;'G]is 9,bMWBz_!oꝵ#8nZ(b5 j'Lu[8ב&uA3U2lJPV_on!>Ѳ&ER; UV{'Ŏy)=0B珡 j(RjKek|Q&E: jw^XHBis@`"E{'E 5m$I !ӓ`ZܿF%CLTދxBb͘~V@Av._>[#jލ3{8Ѭb2@n K΄s%L+efY.vrM .\20Ec =2=c_%/W &)mu65[a(֑s:kR+H6964 [}bڈBmOҚTcE1dΨʍ*-)bo {!glKeye+ )B: a襵SMq8t_t]{"OE> >PzM}7jkEg7+v} [sbn/ˣC`/(AkIcF1?ul]SY*EʸPo<ݩXyᝅ|*yns6/B.LBq*.\ @D'0#(7[Heud4:Q,zdYt!Hglg #Ni۶n[`FxvC1j=#ęYq^BhA0k7 Irm-K4cfVQtFl:K21ƀis3_&ᜡ/bX mKo'&7yd3s+Fl5E&Ep˚FI9PTW~ņ%ܭ,m,݆6 V = 2Y[lד6~]hJ6@T5N_Dz4cHSQ]pΪ$dwcE"-&5+vL뱫;{9n&0bƃB7-:ѻC7͇ǒVs9p뚕um&8G|-Giu#ZzR|"^e2]2:2}ɡU(Q"U5sUM*H;9kE䎬7j`'XI8C"mn,sWT$1ZU Ps!-x-<D vz!Z˫Ֆ JW0rzb Ov"J eC%5t[A6h333T -u[{Ӽ)N_S~-3O8iM)h5~r^6Xθ 9j Bg5W(jR+J<4Ɵi[NuGt񁜂7y1@'ٛj$Z-foprWo˙3 ʊUN{o|cI;4CvfB͆vJ{e1 :N>?mx1v$&o(zy鮧+LH2&sf=TX' #GC/Eׂ/KU}:~覉]~] KF!'^_`%2f/:2˜K(qνhsH6ޭ̟ORu v,E(`zVl%" bt5{'kea۠QLgVRTt5>Wߝ-גhT=~C]Nۏ$E-N])S[(!p%7+9OFԧISg0lFySNJ GZ V 4m,E=DZ! JCeUD&'{#[o|Kշno@4N njpcK\ڳt%zXN֏ĨcaCf&_u3z-d!Xf.1'pj^-cvG# 'lqީV%YX]5HqW%DQ.мѩihXƴ{MwT&1NEV@V{tRipr,'ݺMKd l2&Ւ+@XI:K4uuEЊ )n[CQCB:AU$ RauH_n,WA [bgSlIT+h:0S 5\1 V$$mb,EV֡'H͚5[G]ToR8 |>0b@OBJ÷ȐIћ)!yh<wx =Lc,:) ~BҁC5kڞ?Fx1Q(9S/@RZ6#rpCXa&z &M0X2,g[\}ZE;jZȔ¾M5t:<#uZ@D#bab6V/˗x+ =}'Aݘ6z홼K5S TjqępׁGR F> {&1Cz8]w DSZY75:Hݫ6-GC~>;\/j~Z{ ō|bS81fU+BLԘK&A<8 o߾w7vWJypE8nEVa{$>IʞaN?]jC(Uk]ؘ)}qI(VM$W0?P7WZIYq. ŴKyvRcTI{თPs sumT>T~{p-sa(o A˫ \}kl4/2rOO."+*?=S;,O, M/;WiSZˢ=j:u:@Hk,t}ZCaWKeߎ1sBYtFm.Z>0s!7_"E>JI0-P0&,0t!;.x e~=O8uಝݞ '1mAFU$0Z&Ȓr!g*Rt/#8t֙][ fS/p9Cȼ?RV|3HР zu5a6b->)hIZ+Μvb|716P P} C9W ۬|,y~/@ hѷAL72&tW)L*JCl-Hg,Pw KwZ>s4ddaq>#hel ^Bm S GN`-wx:=p1lp8©waW3pcyzzT>9568]etw|XXCfy%UMOGc\y[.`.5=4x+57Kے^䎟X; -")olJfR#}p$ ^SqUc# kIВ f`s uiZɥM>{rF`4v7AԪP"f)ħb~/R#ݤZ+oٙL{pS+4F Ah'G2I{7)C-KHL掯6! 4ra;K `b!f?ym먤w?BN\לx60mPM$o)x+#=&S@WeȤIvuŜg@e0@ͪ<=xNP&p|Jh.6w 51rϬW!x:׊s2ћ#275NVr*z5]Oue,ew0:l$W:=TvA͸S8 xEdLjKͧS2oj0&" :.[4 %B*ˈR]V kyޭ)md8ٿA!-6C\dɡChYj(G2sR|έA-v^4fl͂ ~eMlnDB{Gy!#.O&S3=1r VkyPB;!EWnHQ#1`#K*yɪf[ 8u88ڃe[&v{Rp7FƮ8x"v` o ݢA^OQ>ʨjwG9]*:rDv'"yqQfRnO]OovjbH]^<) ioG#¹Ũ9޾!B67Է6A65Zyq-,=saՎ`ӛ5'jdYibzip>f!rS1P7T|EZ'SwIQ۱yxG#ĩ=Ϊɑ @#q:/`"iڗ޸׮dd^Їz+>hT0w5΢iYO7N-y#Lv7gbZMGK TcpԦ4Zp6tП ]wb{Mo{Ӷ|TxSY|-rQklY|:ue[8akXb޷,) =UU`&@' ?+KH\Smv4x2Jp_qQx´2jH@l%5&$~ij:'(i.jqўäx9{Udү`D v5dj~Zf 7~D [WQmv/\ߪ?~w(a+0Gz7 eW{DҟQ!}^ GeBZ96gp2Q8_BKc!|B/9v75_s=BG7Sb7b;645Y3Pm=DYNt|eq.fO_qߛeJ@$g PQ!M.VFb\# {s9 w*ߓ_]ZOv7Umtyl(C}ƭU4CLON"~A(_Qti1gF R_ں=X }4rKet8[vYw9Յm^M;ߚ&N)=kkbSB[Na C* rAWk %Rxb",H#L\ݴ6|dSHJ35쓚/Ca6C{$l"W*uh? NP4n4WFn05[l&n V*I&W}ӰQlOCj ȧwΰN}V$a[}f+k؄4erIMh=[Cc!1)଱G'EFtF2| ;ABΑeEFDF8dmNu6:e&8Z1+)jcZq-;ȋLhW#{1:G_nĪUj F;Z;6aXp{gPW $[|4k +I^YF N,b k'u=Q. vFu\&ayi!AF'J><%QoMV8ZruʷDx*]#DH0;Az`gOIE]odNRq}`8qS0e1FMĮ#kn6bIn"1)K"śV c]?gk. ]:T;IK+[cŃhBWr4.d&y̱/j;k _wcʃC5;bJbk{C_ ypĿqHX-:(z#Q7-i8<>mr({THWRIwx (TX5Ӻɹ&K{ns;H!i_8e^ 1ΆU&eCj C7BMX/RIV@Fh5 -3gpw1:OQ7d5$XqlryʙT$gÿK[N`|zg`9b`7c:ZQJB.ia}pMfR8) |mj=@S.?2Ne*y;G!:?Ef%B;%{3.ܿS[e  bGtOST;d0Gކf9CemkO*>M%As"%̞B^̜RB?N> PlJ4-IxH4 +1-gkj%5(QDx4n|KNS m$>:3<0uΩ{}^U1F#똤c(rt?n S`);@M]o`[4" :M)Lu'${Obh@TFOdPd}${W[QEc3/9H.n}={T|߽PPdEVt fޚ~@XS3Y]lD\a K~DNm ǣߵUQ6VP=c>e(,6jC48VHPVAec3*{mΘ QlUSD]<ه{c5.CH4Uh_㡜 wWDx ?p7${ ; C(,\oQŻ~^7>jz~Fw -E<$g2d2#__`8t.X|>y!S^ʧ]t~Ä`PiՕܳ6N. ߳"T8$ p4_1v+9фFqI},#UTXeYEi%8'pr:nXF 2\O~W*ZvSAFr.2vOLsLl=#߲֗Wh_;j<ZVEM;n36E䛾emk ZMYuLBRZnul0Zz<[@xIw*etXFsDI!&9qݍb\?@d"M2Gr!a cDQtFVIX(fi*oḄ$'HSʎ0#["gǙԔ,e FM*n~I GpkK[2/=yM6 O\JQK"Lo%w?䉡/0&IKifj%n+)G4MJ36l立1)_>vbg;{P"N5 ;"BxS,zw(G2 9:Np&o"K}Q^8Pu\ֹ׭h ,;;W؀_5@shw}ڋQ*0_9Ÿ Hyaժ6|\񹅥k^}7n@4Տck+y$I9S ca s)ψX@RHH04!753ExOl,w9ۑ /6h ARϐa{"l R3s}f|&y;i6Vp3 4,LޝZ0_SILђ)ř$Iu[Qg H%d]ᮑR{WY( C.Y^,MYWnkؘ**pFfSKq<0z {IԋVSp*z )cq=Ѭٚ,KүB (Fhc%s]:RXxΩt[qo@j`Y&x'rA/E!BGiUV`$~y,a,@Qk{Ҷ\{P{='}~*c t cș{+] &[$u f/T 8礂^,2fPG7z)wa T !۰b.,H݅34 4z#EɋUcd<0V9N,X7NFRwlbAv7Y=JBa/pȁ}uWP=~T >YW[k186BhݱWK<>}#qu3^y\'{ۈ| p61>f-34ᬉݱDwytzK A&k ѡs4\ NTy} ciԵ,C"6Iy*ݶoKUǸUq@\.E J3VQ||bSԏ/IW@kPTk0<ӟ&;MY!E1hMw4EO`P\P^'?Zg!vjz\>limO<Ys +"X\AI)â1Ib_/It+dAWn]MOJAsVt{}m*R&{eA=OYZ:[~ݤ){%\PI!xo2O͈},9#qw7~WjX¨s5bRN.b%co*Pe8rZz#Hb m3׆Ĺw?Kbc3  aE|_tхspj" R¿~ռAo,ݍr+]zwt\hNTohdƍRķYnsXvX0i NFa2oM@Q0k:3~?XNC/Ѩ~I ÊBa13 *w;HQ5;z٧NwJPJ0 :%C qB44jYgutR՞Ϝ飲5@@,fؖnmבyt[Km[ɖA*&o}`Ǝ"KyA^|7Rldx.blRIl!WڹͩCVXĹ2mAŻ} Irnv=]J3*n/Nl-b_orw,7cOws 'WɰP襢D[I+H q"T9Z d{X9_/L2{$>uJzG M0͵'2G'Ǯ1H% LxTі{5.~XoB /&wBWq^}:F|ڊL -e-̂nL2 (u1/6ar@y05E>+JI;n<=/Q~{b4}׺̓Sܹ֝rjb˸{;G,FQg<w)vu[Y Byv< E[}Fɯ͂ݡhhկUs&@ޯ\[w_{ۏ0iZhՔ+ `jg3ÄL %G Pth;e2Cf}[ 'X!U]Q{/ a{F\oʹD^|!v1p ;zU 7pkڥSە=U 8z%X)]<ʱdM Yzjjs*WX*E|8} g#+ee-}CNYW(,jTe[Uzq:Vg[͘v1inN?jU[yS|Z/L\I')_Wzboh%ClI|ƮDŠr'*i!2΅b46p]_GÊֻ,!o'<"Ysӳ;o8ݮšBaCP_k\U= 't_#w r(OoZ@~_I샗W!%V1Ykְ=eVcp~^#mTv7e{ hanhnfMo.wFS=2lg^U IcsOE\Pf-䰝7¥'GpYgZ+p:7OB3\Z/͌ #+gĎ,N^lȵL.#{1﹟[˼әx;'B.=-H\ύ 2I#G\Z@LJGB[y M- lǰi6j֚z^pmnl@~@@^8l>" ΝPiyC70AaO>!?N=CUIBz#l v1n3;Yc ;Ƴ\b\KW;jKT$QOv$}8OCyM0CkЧ!@&4tӻDC-ZBUgTu2-[WJQvWK׽*qF 15ը(_L$E5kG)WzI]8Dcf>\bm4>;GmQU6+,']1ޛékLd/R{3zqD*nM`5ӸձVf i}<.ɷ2Uu}-Uaû3>Pj3 D&dL?RKJco.:='S ;=1>IwWIc "Yqs9}MXR f} 2[ԍQ3єC:X 1_?!X f/E1^~ N#: ojy.A7tLc? Lv U{\tX]C57sQ_D4}@CZ?G.9Ot++l/94Q]RV n*Wqv/vv; !ZCHDoEvL,ه{7k.W^&*I8ZV vE;#DɫME:ka `^Ⲵ,L5fiq5 88y_ 39 3WVPlʙvw x{b1 ݲsС N,rjGK!ʬt^~EWT}O٭]WL4%/JFQ*8S~/5*א;ƗaCۨL`?$X5ΞY݆}82A%*a/ q|3(4kK}QBS?kUJ)3:rBvqojEmAa~AIJH]!1ҊO1ulC2G48+Ҏ!9QHx{`N;@Zם(&lX#)+&PÝJe5ɏ^EܦbXVmdͳOD+FoݐagѦF#/x EE }gXMɦf!,ʩ:` kPְH< $O@MlM[<}^ψoΒ z`ɢ!ir7a;}r7w1!\CAzRH~@Ĺ뇏Lin9!**&-xlgCS S!X._kpٻetC%Z4DS"Ş5ATUZrRT-;Y>gM2@:bdJl^.⚗izʻDJb!  $6Igd5_'Uώ5f hS){̛QG4yv+X%BWRc`R !M=PLSLQG0Q_T4c֏9xExCh\ǖw'w1~\dxl`d­fG ip keux9 k(v=_]aAބ'f7G;?o&6^y+p<;H% I4{&>03'm2dmNDjYK0Z+־jP0ѐi!p,A\<;DֻX\4Ir0?]D>X}2ĝd6%9ൡߕ>WThK<;5xKPt+(?њ9T9y>"|$\ Ý?@r WފE 3hʗA3ґ3G!\=.Q(o!N7sK* {SQͦ7҄׬q$~_-x ,HkT ><*ȎYLs/n^'񔄈A~bo~k_0lΡ'Kze`0\|bVϐ8j-)+kOpi6 : `xBtVeҊi N[]OI˹x阬Lo"ZCO3`Bn'Өoj^p)`ӭ/3y@B_$vU1'/&` +#jQ PR^9Mz/N_OojW 4 4fa.~@.SL.[:{u\N-_f;X;2]Q>[c)k\\XkFӝu")Ac^1R6P4oVmܙK;Oj^Av-o@e,C7SdL$soKm)yU:4ZNx2U G',qXRMuMI}.2{$:(籃֪DmSnx벃|75|ؘijQ>RO-s$0 bq}cs(ۗIxWPcdl+\g[Vd`š; N$_!̀|= @7K~1vL ==;S5Ϋ)G]?u~JMHg˕8r#jjkY!C8$ 2 +c8b5{; +}Lz}-M2h1u3"62#Xr#K 1f.*H3'sgC2ĭ|UY#5Nsy(.M:c=qfn10~-=zi5W@pXq嘭k^$ZZq$x~%xDRW-e{Dʼdq F\9}>d~IֈjwoR`Ek">t9-z-u02B.COW+}af O0BL2:Af~%/kAR&M1^pk'sTa>iu$j4:<IaT2) "j l(&'s1|yֈF ʷ:Q2S3! ?HV\( |i?*ﰌ% 4T:gJhYZ}su<LL%zG"%tl=tSĊ6qgo=Y&EyRJ [v*>mjH$RJ![-Y+،W/p_} *rwm Mwz"F2`scw:^^"_m0RkHR_95pyZT ^ ;$C>dkH*7c$N Zg].V's8w`c&d=+ Lcq9~(-'|q31qݰ+UBǪɿx1'w6.=:EJ/ $ۙ# ,U_WcA Iqoˁr|px|ϣ^ ث?6d-ko핐.4 C7ѐt:|S~ 9/1e30ux4oAPv489jN3'+Zh>ԇ|_6_,z˕܄ihe-kl4nU'@Hg?GTs ~^ ŋXnC!i@BòS*62gP|MJVѹfgT=&M:namBPY_o'ΰ)¦AuiE&y0Rkݮ?i,}s;ӐWcT*`t"2Hn;umd#"k A/\p6'BCK:92(m0|9!u͈V)Rċ}ϕKr^qF=NF0//Ihn[ʷ660])FӫşvMq{DntFxS>psP)Zl`uPq/s "*O_5!ց6{\QtfLlozh{]mjk t-"oߗ{~XF_ID?yE\V)S!WCB9մ1+O5w]=09 S 4Aƨ?99Zc?ct$ͷ>Df>s,AAWUDMӶv AC_.P6n*$ af@݀L*Dٲݿe9i<: e#Br?&BgHDi{O~#+]5tFiڡ{'3𴥒Q}4fNZ)Q5f>9/X||HE{JZyW C;aUr 3j@\ĮN(~N AnD#h'q!faEئ6ɮא>Ȭq7p_ Xu6}Pt qzgpJˋKBdF^/)C=dw` "8ȁ.#@/b-(av<3ZƲ]bfNC]Bc+nW7P̛n'Txn1PNANC3\LZG-cc#A#H:2KIpfU?=Ȕo8i%=؀9YK"C|u3,3 ҋUWWw.na0Q}) D ^Ee[ 73ѓRe,iPZ(/JW߆NGYIΙOQ^ 2ݝWQƵO|TC؈)ӖK >*bxQֶth#MX$"eS}2{VPjDC΍􎝫..v–ͩf\h)KJ[lXȨPMQtŋ^Ha z s+/z_ yjRiG2jh,LYl0@[jT uuPjo6uL/ILd;ILԩyHsM l UdZ ,bae6d+k[7jUk>vU3X1[UO\>0r“J&:++-"E}xԼJseKrt0JLp.Ԧ`s"@0RȂ!Q;vmb9h~i@o<'Pq%UCrs{ 󽦍Mxl r1O "[\#>G6ExQ ۖD3ej9'3ͨʤfnDs-)ijtj?QŸ{!DBmPGn(WV oZ"H`)k֒ӕ9::)R0K\g bmrbF1~k(#ya6MGp0@fP/] $ 6YD}'tdfVHմՌTFṕ1ܸZtf{ ^ޣ? nNLi&ę6Tʗhld a{F彛oY YT9>a𷐞 #-֛@XD-AM j1IF<MP5WWzo_"q:9HW;Ubdd{&``mKZϧmj5wH Ɉx H9$xe- g⌺ejZ̿)>Ǟ ZMjrw'ZsICWYWr%/{ [:Q:-O1-\JȭofrX6K¢Z+/;bģn7b[XS փ | '=+VNpi闺F*b=nM P#h\O#?蕕;G 4:b+,QʦpvWa%/cç2,#]lz$^.4*N.B>oVl-/IΊʀRL A `:T RjV ϥ2r\\>ޜʁ<,SQ 9G>(< tGlGHfCn*~ALua#`@ƌx]x2ULWJ1gJ-?q >j?V﹧V-W\fW + >6*˨-@{(L fJ+s^C0@Jۻ9걡q#^l=|; vzc0ɴj|>"Vũ]":nI&|̀ /`SْΪxyo<ٯi||VN%8پ˛ eR񏐤2) ͙Qy1ϩ Z`A/kVr@;efq_$ gpK JUFMN= H:Zcx,޵O,d?pVZ!Ax)7H[sR6,p󍷢cGa ҅y)Ql2ե"}ebHCk#yɼle-ǰgl2n2~\onJMICNj'7>ŁKrK?\єzZrEwg4p5"ZjS{kĮRjjE%.U[TCc=vj/Ԩ{sܗ>;ϛ|Pmۭ&* MhmR:1jP򄂍]tj[8)pkwt C-A|$JN,TcuF׍= +N<,v*Ä]~tubh 2](T#{-dU]RvPO|Gy_SM/D|11!/E,l3Ҕ[I< :1%[}t׈gV:hEL/难v=XfTZ$hWh\nxhB2 'R2,6G"_ ҙ[VO*}B9._7׶չ̞VwUIcxb3$tA {Ucz>#slֻu`0bRtI%W+r+.g]o&KM6œ'J>K$Exbvdz'ঢDn=<֡7czc?J1qOI/lX62?I.t$*-7go;wFSӆ| N>J]a&ڭ˻ƐT.XSq{& ~Ak@V`_j5__hI4"bV0;/+#]ؙtbY6Ye'zN/`Zs[͂@=m.~;mAwkے.c=Bc>^aT+RT~IЖ<ma[ zx 횋M99K1Dո!jYѤc.Sl7DFwֱ-lyjjD 3 2wPZZu/O ~ u:16+]ٕKCg\>O2F,~T=G,fpcs[FDUiMkѶi9hзi<ƚPG%a e{}d0XG .L!_## =_O=>=Ֆ@JϽ 3J$f3Yl]u/䦉n i4tmطW^9sK|sUFjQX jޯq OD`'簼y-8NzuID~eQf ;Z[Hpb7ߖ}Vv|)i@GZI2P*Wl^a%.54eAG0D>8nP`g/Z}&W1cS¾5}3ťݿ+{ҹ[^+dl#eb;y?KϞJjde&XK`w=wBF6xEuW^Z"qqfٿ؁F ^z,NݥTU+,&?= CjLv)hĸ0M§T7ls`b*ט:$Λ- }c-$$Rz33xpwۅ@N}JSy|sAlͲ}̥ڙ QފeK*!y6+qAʚ0I?g{N(%}r,OOcXsUih:* FRwɆܷ>7bVYF =mgV E< QӘQphlf#4y'PfU  g.ODž#ǔMfFu_xņʥV=.$w("JFvR)/AM7 .6"rDZss!V1Gw1EN i٨W1QcE3q豢Q/9.*}eRMAq.yY{:NYzi|cp%w9K/?EѪ8 y~yR֍p-Ҟhr-Ttzʓ.lpUC ȜKS+Uk?7DGDSc,~RkwٰW4U+  p 1+RU_(%bʻR3㰁rl2RkB_/rPO9U9g=I߯{Y(6 \R chҟ? ze ii:x@[;d& -R{rx?yhs.e\+75}hӟ<>{.=!__] eKV^ AЪۘb~q0I Ae݁ Z_PڶjzK^Z)#7"so2}i#Bc yx~txՔ dT?kfuş}.)1 "Cm̪۟o$gR,п:Q+77@*ŐR#^L %k8ۋPۦ/# 䀣'S< )'`=5>^p疙UfcG1jsF87ŮE]B]PPa]r[4U(Kt_L>_љYT{ncRF\^֑QZUT@wK1'viԤ8 ;G症XV(/cm$ј']l}^pvׄA^rظI¡N%!&]rʕ+W\rʕ+W\rMڙpar2cmdline-turbo-1.4.0/tests/reedsolomon_test.vcxproj000066400000000000000000000046011514221355600232160ustar00rootroot00000000000000 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 Win32Proj {4f6accce-6d7f-4c47-b57b-8b017ab383e1} 10.0 Application Unicode v143 Console par2cmdline-turbo-1.4.0/tests/run_tests.ps1000066400000000000000000000175221514221355600206750ustar00rootroot00000000000000#!/usr/bin/env pwsh # par2cmdline Windows Test Runner # PowerShell script to run tests similar to 'make check' on Unix systems # This runner discovers and executes individual test files (test*.ps1) # All tests run inline in the same console for visible output param( [string]$Par2Binary = "", [switch]$Verbose, [string]$LogFile = "" ) $ErrorActionPreference = "Stop" $script:TestsPassed = 0 $script:TestsFailed = 0 $script:TestsSkipped = 0 $script:FailedTests = @() # Get script and project directories $script:ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $script:RootDir = Split-Path -Parent $script:ScriptDir # Setup transcript logging if requested - captures ALL output including from child scripts $script:TranscriptStarted = $false if ($LogFile -ne "") { try { # Resolve to absolute path if (-not [System.IO.Path]::IsPathRooted($LogFile)) { $LogFile = Join-Path $PWD.Path $LogFile } Start-Transcript -Path $LogFile -Force | Out-Null $script:TranscriptStarted = $true } catch { Write-Host "WARNING: Could not start transcript logging: $_" -ForegroundColor Yellow } } Write-Host "========================================" -ForegroundColor Magenta Write-Host "par2cmdline Windows Test Suite" -ForegroundColor Magenta Write-Host "========================================" -ForegroundColor Magenta Write-Host "" Write-Host "Started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Cyan if ($script:TranscriptStarted) { Write-Host "Logging to: $LogFile" -ForegroundColor Cyan } Write-Host "" # Set PARBINARY environment variable if specified if ($Par2Binary -ne "") { if (Test-Path $Par2Binary) { $env:PARBINARY = (Resolve-Path $Par2Binary).Path Write-Host "Using par2 binary: $env:PARBINARY" -ForegroundColor Cyan } else { Write-Host "ERROR: Specified par2 binary not found: $Par2Binary" -ForegroundColor Red if ($script:TranscriptStarted) { Stop-Transcript | Out-Null } exit 1 } } Write-Host "" # Function to run a single test script inline # Uses Push-Location/Pop-Location like a subshell - test can change directory freely # and we always return to the original location function Invoke-SingleTest { param( [string]$TestScript ) $testName = [System.IO.Path]::GetFileNameWithoutExtension($TestScript) Write-Host "----------------------------------------" -ForegroundColor Cyan Write-Host "Running: $testName" -ForegroundColor Cyan Write-Host "----------------------------------------" -ForegroundColor Cyan $startTime = Get-Date # Push current location - like entering a subshell Push-Location try { # Run the test script inline using the call operator # This keeps everything in the same console window and transcript # The test can use Set-Location freely - we'll pop back after & $TestScript $exitCode = $LASTEXITCODE $duration = (Get-Date) - $startTime if ($exitCode -eq 0) { Write-Host "PASSED: $testName (duration: $($duration.TotalSeconds.ToString('F2'))s)" -ForegroundColor Green $script:TestsPassed++ return $true } else { Write-Host "FAILED: $testName (exit code: $exitCode, duration: $($duration.TotalSeconds.ToString('F2'))s)" -ForegroundColor Red $script:TestsFailed++ $script:FailedTests += $testName return $false } } catch { $duration = (Get-Date) - $startTime Write-Host "FAILED: $testName - $_ (duration: $($duration.TotalSeconds.ToString('F2'))s)" -ForegroundColor Red $script:TestsFailed++ $script:FailedTests += $testName return $false } finally { # Pop back to original location - like exiting a subshell Pop-Location } } # ============================================================================ # Run Unit Tests # ============================================================================ $unitTestScript = Join-Path $script:ScriptDir "unit_tests.ps1" if (Test-Path $unitTestScript) { Write-Host "========================================" -ForegroundColor Cyan Write-Host "Running Unit Tests" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan # Push location like a subshell Push-Location $startTime = Get-Date try { # Run unit tests inline & $unitTestScript $exitCode = $LASTEXITCODE $duration = (Get-Date) - $startTime if ($exitCode -eq 0) { Write-Host "PASSED: unit_tests (duration: $($duration.TotalSeconds.ToString('F2'))s)" -ForegroundColor Green $script:TestsPassed++ } else { Write-Host "FAILED: unit_tests (exit code: $exitCode, duration: $($duration.TotalSeconds.ToString('F2'))s)" -ForegroundColor Red $script:TestsFailed++ $script:FailedTests += "unit_tests" } } catch { $duration = (Get-Date) - $startTime Write-Host "FAILED: unit_tests - $_ (duration: $($duration.TotalSeconds.ToString('F2'))s)" -ForegroundColor Red $script:TestsFailed++ $script:FailedTests += "unit_tests" } finally { # Pop back to original location Pop-Location } Write-Host "" } # ============================================================================ # Discover and Run Integration Tests # ============================================================================ Write-Host "========================================" -ForegroundColor Cyan Write-Host "Running Integration Tests" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" # Find all test*.ps1 files (excluding testfuncs.ps1, unit_tests.ps1, run_tests.ps1, build_unit_tests.ps1) $testScripts = Get-ChildItem -Path $script:ScriptDir -Filter "test*.ps1" | Where-Object { $_.Name -ne "testfuncs.ps1" -and $_.Name -notlike "*_test*.ps1" -and $_.Name -ne "run_tests.ps1" } | Sort-Object { # Sort numerically by test number if ($_.BaseName -match '^test(\d+)') { [int]$matches[1] } elseif ($_.BaseName -match '^test(\d+)(\w+)$') { # Handle tests like test5rk - sort after test5 [int]$matches[1] + 0.5 } else { 999 } } $totalTests = $testScripts.Count $currentTest = 0 foreach ($testScript in $testScripts) { $testName = $testScript.BaseName $currentTest++ Write-Host "[$currentTest/$totalTests] " -ForegroundColor White -NoNewline $result = Invoke-SingleTest -TestScript $testScript.FullName Write-Host "" } # ============================================================================ # Summary # ============================================================================ Write-Host "" Write-Host "========================================" -ForegroundColor Magenta Write-Host "Test Results Summary" -ForegroundColor Magenta Write-Host "========================================" -ForegroundColor Magenta Write-Host "Finished: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Cyan Write-Host "" Write-Host "Passed: $script:TestsPassed" -ForegroundColor Green Write-Host "Failed: $script:TestsFailed" -ForegroundColor $(if ($script:TestsFailed -gt 0) { "Red" } else { "Green" }) Write-Host "Skipped: $script:TestsSkipped" -ForegroundColor Yellow Write-Host "" if ($script:TestsFailed -gt 0) { Write-Host "Failed tests:" -ForegroundColor Red foreach ($failedTest in $script:FailedTests) { Write-Host " - $failedTest" -ForegroundColor Red } Write-Host "" } if ($script:TranscriptStarted) { Write-Host "Full log saved to: $LogFile" -ForegroundColor Cyan Stop-Transcript | Out-Null } if ($script:TestsFailed -gt 0) { exit 1 } exit 0par2cmdline-turbo-1.4.0/tests/smallsubdirdata-par2files.tar.gz000066400000000000000000011505701514221355600244130ustar00rootroot00000000000000uP$;%T:FQD)IP R`ADDs=?;gf\u?ffnfd%_W(/xP(7?)gq+i,wW7S::u~ҥ. +i}*[|M>a3fEG<1B|g9񿎧rrC,d,\/VJFuSG jBV^1%xU&*{i3$څ7?gaezm[ ҞU4*MIOYJ>~oc唈|dUEXߛʞ  ]՟Q \d<  @,/{ QJ:Z]T1]lwe/BsžL.y؊J/tɿ>I8fZ=ikoQ)O~mNPmtO?9`: q,"KR)؏G-jsr_x΢֬'f`> - UޯfMɾѢ fy|䭋j Q^I|<`k>9Ϛffũ]H}9bjpmjֽY|衴jD vpS3aGESܖ艼kV} ^-h=0`?jGԙ+l/0?[~ru,Eh(`Y!`~Dy?$yL&9>?|M<]Sϭ^#;X[E1GF^TpDK!grGOŬ[Jv vxj!'? BJ5I[yC}e$`~ԢA%, K[uG]&!|?^h{)I90`͚/Z19W$& NG,4%o# $;ddXI\jA׽}@A8&@", e%ޱJזi 8㉌kX"EOvyng90ǟ$ZQtVJYpiuyh,kY 7=5As"6cE1E1=+THgB,϶ 4׈h!w7{jvI/5/-{^?ސ`NZXfNbAwimI3lJw lE~9%z=Ze6^Po1D%KC'g%iK) 'ԸPUx3ŜC JpR'1R_vXKeb0WN-g2h IcwKXvZEƍa;ަl\+D‰v>m\_(K4G'Ӟ)B۹_izFBx{FdDJ6'GvNAy~=~pŧc74]JCx%Q {159+RLZ&ɒ7L_%`Mk0Mp'R`YWnUiNYsq$ Ģ/]*@ޠ;XfeIU!&ob]~֑]wntF; (ͧ{LHJೂA #t s era0%sA`iJ N5XL{gnpr5xtip~pm uر.vKDn t{>ѓ[GS0#R# zxfE FI 7{H&͆fdT uQrH>;m>A ؿaaH#FdnG3B-EG;^p>MߛOEHDi)Q-}::QNJFP%u" D3K)0_65VW#O as6d8(LEVqoM(e佛7&sJB*)qXKdz'Gp+དྷI:; Sc %]bne,Tcď2'6ͻ6AM]vΌ] V٥Ç#\H1R;g0I JϪ_m9'= >:yAzgO]Le$um]L_ WU#[=R|v%99C,pZqﬗ/>cOjoΰ7Aaj&A;"蛞] *XS/ iB+:[|*颽H4vATlxi7$h84Acq7̋#U묦 8$7D#tpze*A?H`~Mƿiul%5<&ݜf%_r1y`u`ruAx 6}wv2ͪ2|%oT,!퍊(sY'## Yz$mU-~`>hJQ R;AO$qG_=U8\j 2<^坛(eMKvS&ilI~XT v2xֽJ9F4b%;<|kAU7>CN6\=f,^X(D"}9b^E2?_X):tWs!jGg{Ӷ߾Z#:BMpAL,kuUݻoM/m]ju8_+/gA3Fmo_mo&d(m(Y?Vy6*wI%kdbWU*vJNjyV lEٴT3,e=ͷ$\[ü]^m/0`:'p꙼q9VM]JLN7?3RQl4EQ N1zD=&*Yy(J"Mj?((߉Ce<+qjJ#ʵ2#x EnF6R\/vl$ӫ-ubQ`ilN{U Aw=EM K69; ,0[t$8xHKF\ Q4ٙ=oN?2jrc1ȋ8UZQLK%ϩ.N/m/ZVeM`zɽYuMQ_]8;?޵>t""D؟gZ8a6v!>n Y5D 0=(YY/;;Xƶ%WB$uxYv,<#D>h` v="x߼գ]\pk!T^cV`H~&Kaq|u2VBsk9U99!rS:5Ty nL,o_c@nT߉5leT šhd`P`tFZ9W=w1w11(&5+(+k[{gg4Rr˕dnPbSZa /uU=CWtg\1/T} ;v'ܔ\hQDv'w愂{%uf$_s9X>2%s9,?LV`>>顏H^ ^aB<5%:d~3qLOIH%JR&'}ۙˆPɮwkkdk+Ha>m9j>r2{,)6)MܸDz?CQO)kF`WNjD< q:}~kPQIeḷ.:|J8:8oݚ{;9ؙZٛѻ9X9C9᜼e86enP( q{B||_n?g-7/r@ܼp^59jLOqvppo=?~~0FfQoZl~ -b=8{ 55P?Cp7Cpo<Ϡc~ɾK%f[Q/Q̉Dv3[lν[p/AA?"NmHBeϱz3{|7)qEV7Xgj*phM5Mf:8t [G3n:+^Al^q5Fk{^gbZgz9ޏ+i, r'Yڭ΄ؖeT}yɌY~ o*:M  kOETpY d8GN\_<8҄?3`oԌTz)iY+þC>RH WKw7m_e&M7{\rݩFK}-mڔV8Gd:g14mgj Y%BEN^%ľ\(2,[r୳Bta4I^q|s,ƞtV07 gc\@CP@mQkUlv95.q݀JE.4uQW=vSۧRnlc8gï&)׆W} v2.=km=S_~{ٝ`e*Ӣ3)_u7S;- Usk0 ߗop?1%7i ]z]鬼e@䄼Nfq$T\y!`EuzC"i[eGd.g7 l%.HlHpƒ٩<5LO dZ{x!xH!l8 8#jnO} յ4h;=QC/鉃Nr[I[~2i fo|uư؇Q jK^k"B7۲^~+ Ob\3`1%MLD=`{57=cj1z$^U*exf9gyۇ*їU++0^IGUeI#WEK;lvY(Zd4+H^&LdF Z=呯"vܭ6_ҹBaw{ͨ6܇P;a|d`p}p_9faTxrf_`>vh0b[~mZW6; Ő_xĊh.ݞ<&aLqy_>ߖefs _ܔ@4pM[b:%t?sV` &]HXy, Y`ڋRM~}D$;%FdrvH߹~)k09?ʥW漛'u 1$5 QXsZdƘKZLJw&!nl/wTD-W/k$ P$~̯Aݖo%ZkOŚ (&׼ !A$l%g/Vg@ײM"&&:}}aT1?e"[A^ghjK%wVsvgr$=٭΀db/ZS?U!*5wv+J(Jm hBrʁ8y 1yx >n[B`/:@S`f< ܙL_2]&Y2%Rr}g!WqC6[Ӯ[+nX>auv5%-G:-%c6MFx{%*'Y~D~s4 *ݘUbQ0]!0`{(&)j`Qvy+n|lA1Lg^«{YmHSs ™F#E Z4d84WY|DlxĮ\f=BzN1/zMzL&d}hOUnYۚ;Z*lq YFؗ\p82tocr}~M*U+m ۷Q;̀<ʧqi`jz+͘c"EX[ mX; k/9)ysc/fYvy5kL7{vWC0Xgw٣!͐fx4 Q@-WGFjwW- =f/h!%B87 X^ >\d֚vl$ Cjט=H"\;bDA});%%CoorS_>]? X-ȴW4AX]_A{5>YIzu/ 1D._BꬳmL[S\c& PC.se#] /u^Nj$4ply.p޳/K!|rܒpq]$B|-E1=<2 z yf2='ONYՐ8RL<5ʢ 7zV(bM&5X~D,owU}?(X"<@/*0`faB'LHHbQg,vkDu RAsB"Gĺ@ƍL"*Ǥ*gZv ѰbRKZ ζW>0h;U飘 JC].LՖs:UoDR#V@l 2_=ldff -({gyCZJhې3X]b:sO2P\ӕۦ2>iǎ4ĸ<0׳C~4^+:,Ťk$M/.@=TEzk%0`؛(mUC岢C5$'p(潂qAީ4q% `&mj ՛浳->x4WG1 >l=,O"]Z}JDeVsb>RU{3},LfX}~)5h>ᙵYX1B+b2$myRcCiwR*Ttn#ڣOȋ<ΗeHo.5?Pn8~}BZ~dOVk;oI-zw U'WP#G"-\ZI!W L0@L}eT92C)0z`,Dt >gH#("62"6Pѻ+`T%`Q_X$5鍕Cń*q:]2ξ+-!Fqϵli tF$tdz-KSJq|2_Džn؆E3;CeؐUi* >rÞP"[ ||LGw}f_R<=zg{UX1 IN؝m NV}^_Ƿ\P쯟9`\N(i A\vtw9:z|;E,xҔKXz2 sƆJV憻jo.bFbq[QYUԸeU;V V h!'T0t&Bu5x"bA&9rstьFfZfuv7OTޒ5"I !˧G6y;VVkDB^9G'|NUu[U^[nL`'**rrimj"]^{^}k<,)A 5ߐʤ~ٚ"__Ho/􂳿GX\-BY3( q~ A 1 MM@-.kk9ZLߚ}!çaEҴMi!`ۆMb5S8,._{,3)OV+nH̛ *vb Feƹ:ӾG } `?ǖE&74HR_03߳WxEq!`}>b6"o4jmcL.&AڈC'k[~3*-cV3 OqłF"Eu_ EA3dk:1Ӄ"lsoǬ_Bk_ ˿ߘ_uoa?GTƳ\ve-Wvç MEIO0~3Xj2ZW]=~"0kvxYd[\Wg0~=${J6>;w ~;Y^c>DS4Sٯ J=QVa:/rq!=(Bj 0䲁VO3rA~8mtjQeVPG-r˾:KX$DYGxd\>f)..oX?}M7kfNqT+,*%W@L0>bzh+ P%`Xati !yinQ>gc0պXa| "4;:jY"0C~d1c_[\!,c32F#1dDOβzO"͓R txV7F7Q,y#ejOiSJ#-/K.2 0d[r% !bVf5q@[=*xh4{2ٙsT0$֛Uk%Q@W$:s)+Lk`h03^\+^hc?X Rѕ/?؏)6-HƛhCBqت"mp]NZ ,#[-Ѭ ';ԝK19yCޫ7rQoP\ffBU»!k^dK6p/258ZiW}{JHJ 6%!Eq>}(Xe 3λNDŽv2u .'ۯ7!}.O)w\FyVQ;<3$+>aͶ/9`>cvcϼt~aZ~k]A^m(m2'ɰ l'9 Q(x I!{!K0.fW-@OVNKc `?j&7  ʍ:@pҗ: kw5%& `Zx:opQ` &['gYtH\ 4+ah Q71/O<+w>"&j `4Ek0NU4)xR_c'Rx̫FՖOM!J jOtp5zdwm>gYPgk1>F׍ PQYfPuwMyNGpk834S~noM9lkA,k2Zm'PbnȕtȠJ4(_g Bi* jК/1.".z3YB }-s(:+B6Uv@ C6ҨYo^4 yHQЂ-idD9ЪA^ce3+bQhߜK2WzڪI?Q v^/ _iws6ju rlH:)NhL0Ϝ'=Ev1ެ.P{.FmPqVi70~9? ) D/~"g<,Tt']Q WmsG^5É7GO UL:'rN @ykޜ"}YZ%e]ܶVt[\)9InhJU;u1 د32G<6 7ɦV',QAu`ٸY S*f4c{_k(u+D~2Y)IV(=vin'0`]"> OGy$Sv#1:f CEιNC6[ Ismr!omUZ;zԆ|5J`w,mĹd}=l5ֶ~sIĽ1GFu}]Ü|0¬k}JB=<% uv~Iv2Μ~sIZ>?^Pw`u|2>(B" -4ў) F3~k Ͻ[$%nnpR t)1G#{dFdMjbw '䇻KK9T|熿tx@)a}B7Y!t9m飍 KZ9GٺfLK7U(y in+}б FB0& =f*Gg)~pgtc;/ICd:  ,}q_& T6(F{Q>K7 L i9$E ۮ|g1 sP/O\t?_ Ιktb=<wcBy`'.eɗbr05dѷ){^YNl ~~>wU2:d@R5Lf&*V&m<o'_ܐ HXF?#W֜ޜ;zdvDrA9)?'kLua_Gg`A&DY!mRQ襤og : ۸$1Ie֏[X_iHMaRxϱNKk}TN9nj;:hʔ͂\M"!U*S2/8kԌ;!#1`KO6gqR܈="-g!- hGשׂ~;gr.ۡS@q-l9пQpasV}6GS:kL3`Qqz˯ESEW]jG+{mLt [Vyռ@&3Ad6!W@i)$OZ2<ҀWzr wr#`eQYԃ[—ku'Tp&.ȶ}pyybH{-ǔ9!ryYWnͪ6e54OH<\eoD 65" [SR!]dpLxE*F%]=u ~4d`ϥwtB,zwmVïn>H.y0xLhZɪ)jNCe&:u'1B_UT?˿ԗePc~iט+KAPFhy_Dv`M6i >Ob+B8jIK<T8ϳܗc hfd1kHam*뽵/tD{ }ùY%}'zGy<6Er  e*v&dϴ_o䵴f#עVC;}!:%倕:yUA)rPxPhM"s *(9 S$s\?6SOjGzBY~'Qј7+Ns;'<!A%xUÏ{,}4n{9\2\|f豽:Z$ReҳO&WDYR2*X_1%;j8'(7E/; {N@%gÒh$k*EźC4PZ<.iݼk)ؘDm7d'hP ׋ eQ`fQnUNwf<2kX§pduϽدiwsZnoW~<'@H+2)>j toL5S_$f/~pwL$aS}^R$w}X 05S,qKiǖ/w.D ]EY쑽Me ҜMt͑{Q]ɮvYQ01s;#ʂaoB$KIDd%,FҬ(i ?7b5 {%[C*5pXoBýn(&x6hxydߏHa|gQ_9LgVa2zd80X~]XO7ل_Lh>㡨zati#9=`o[%tv'<"cMxYJM3 j擂i"Ο!,>Պ%H\ -LCg,%I3F踬3ė3j.yhB6O I:+c~7.խGVG*K~\]TB҄7 6B5\ʚ)񭷦ۧ+դE 0Iԓ3r[u1j:'^dU8UM=L/e}S҆~dy?E[AAy,f-TcZ]{ƚvX~,tůh5:8B;nV=w*RoUUTyT餶DqNl -hy/:J'Pd؏1וX<7z9;˖Dj7DFqJ?@C4nܴ+mޞdu5q]V 9.m̰j`T/0nG:.zy'.Jސ2`~ĽiҎ?۫6r4EX156$E `v_.Ba{\*(|dHGGe\%&˰S ؏pׇԡkY D qYZT2tTY|Bܞd:M(m8/~~- e?l%r#`~7[Wt︡ߕ1*Ε!3_52Ncyh 癐Y3.7][k4 +mj!M$ rEd z}l£҂ ={Ly1LU `Ưh c-kcOu5gkNt[WjyS`Į'ckz57rB\߮|ˉBs; 4.lǂHl +&ACX"VzU)Y)Μt|V ϳr :sX=js<+/ Bd|\M{ O$h=4L د`38 Wز6 ε^,[!Ri{+h-} >g/Խ$; #H6XiVғv& [!oq4N{[`+vѳ.8_>i0`f+ęd6BˏSS߻W-5^Spx?d󠽴#2CYWaՒ NL`١AgR^oאYZZ2Yhv5e;WK/VKi6kL܋qkchU۷=r9> 1D혵. ?Uqsmt{.0+ bƯa P03ݰX²uH Tiʙ|L1;>seu]GnYosT)rݨER!i[lq#{x*D ʕSew؟gwRdGn6p%룬7qz,ĖEUrE ,4(B(?Ry )lvbWDF#D6 n&o~ df__Q*=at@s*)w%jOuK9UZF|6]+gwP{e˾Y*1ɾ](;c="k-%Bd߲,%Jq<9=9/3g>~T^sG_/O]f{ A!k9H EGfeKj?)JP XOx 4u`+u5>K4ݝxj P 6q,X(`<* 89s/At[rg9Iic۲V߾ &vZ#^d-u'xO"- +{`b:|1g},K BFm+}/., *6ǯ^Z߀c*fU l5""y `\_Hq&UրzD0Fv~战b:`|p:+CsW>ƳhFPLSѰ&ɶ)b`FBb~}B7.1^DŜNHYa2,Bdz$l`4 dP2:m{ yֳ֒ľ#ѝ˺_ٓok|'StMtsB _jҏe+r>3`.;u) M&B .| "ĈFe y5J`,cυj+O/60.>/ `VgP[X }eztNJ&}BS;c۹o;`Ѽ bMNBM,42s{E7Ҕܤ%:$XY}7(>]Р%v&(5 Ʉx:}qg5Y)$6} Yl VۏzδP[} sKCZC[;+KZ0+^.V`0'3źU?׿8v?rsr98 `vn.s 0;'㚝=Ԗdkeeߞ=WOw#T>1ڝAĸ59/wbVѤ7;ǩZ23gmE?k+g3"9:~\|o¾Nj Z Z+I򒒻vrI*AvAk)ЭneE;e` ]r^﹵VB/6mn0BԬ7؊tLy \h^^ F.!ON{6_.^.~N%A4IdܒH5Ar^ #E޵2s.!~wްd !þ݋0}ż`$g8-׋AxEkg<=ܫaI 3_ԩ`@ QgBJL/x&=)OZ#Oēo()'f?K8$-ly9(1Y޾NNh(Mnּw|p\=}8s~k@#@oke}]L~2hB*z-cVS1J1Fe""뼷UX 5")k:161ك$\nCcVoX*,d߅&7fEWav̇i'4/"!tKoBQh¹g`?j LzfL/*=ξ&E1h] _iWBSR?iE˽Cٝq^$n7Ug,ZLfE@MsDY> {L㥹0is :*)>gփH}Bd0hU.r!~HG w45"d#~X#d4Ux)Yf+c:vЬ1Ma[RRW2^\t^vUMr~2p*qIzS}cx< '}6&H:7aQ{;};$"?B,sEkd[?7H} F@w+feoI$m.Uɧ3E4= Ȗ<GgC~G ">_nTY({!ܮɜ د3 X'\lKLγR:!hݘœԄOmοSHZ`ه{1®̪cAgƲm% ْ,kMYhD=u1 ohgY̥} %{,Ghyޡ\B)BY:R{7gac`Y vZ0OkS+#^G{_^J*WQR)j =jso50{GPx><~c`ZZODzHcMWgCӇ!W] Ѣ:f5I<%$_9v8/Wu̖Й1BwLՠ؛b sp̠łdr;`6zcQz}.U!-)'g7[i`(esֿKJ5櫉bK0fQqYAyG} 0g*(j7A1̚- :u7!j6:;F OhJz?:1TcN=#+^~J* 57$4ywc0|x`&3j1ǵ(,d3Po\'J,a:*OTn qˬ`x-LdU*`ˌ( y>1)XV"@SxzqzrX9VJ@ﲈL#$u=H$3駜Y+a{Z@R(QzÈk*{<p|gH8&D%6 \l,Lj^uҾ0T腞WzzÈ51l˺ې,d#Z^!{3@b5tF٨;"CO.띦*~4n)5Zriȗx ؏(_[yO 3#N6&NU0 ri귯Ȅa"-=.۟% T,ᰄM~%N,O8M `Ͱ_PLS/{%liDOq[ 3.ѧTmvLC"Y7bG(7X UpL?6\VEBٖwmA S*ٯxtH]d 1~Dxgog@\M(*,eo;˺%-^ \9$y{\ƾL]Kዾe娏& ׫Y3 rֲ8VPσm?چIQ],d,$nКD u6W%d2RG8|\t9p~{=oK~9E992KC8oFDCAlJO{X=vlCEn%'OLɩY|a!QoUJ n/1iQHB> ]Fq씥xGk@ y؀+m2zӪ TCeXBxCvd|u>xa0 d2޸]Η2rU?{n&B]Edaϲ4AbWOe ը>Hj.>#Etؖqe@鳫$yW=~V;븿ҬS֌T|>0~]K2I/hpDɿ+QmL$3zg ڪy. _gv8Ep5~?{.ō ˃3֦ lmz&"1%P{H H#B^,\LZ,ϫٙ1D7kgzN~V]L#S#G5|4baw^ MrNxPƟ Md \E\N 2y\Wa$y@5K@)wSXgh0Xۤqv 3%eEPőEM.AalrZ5Ņ|`K2cWg6 7ϳD"ɾHdQn>^׌Ngkf/d뮭 fsINv#d-f L U t9mP"C8ﱔ2X9o.6Ȩ o'!o!4&Qz*7sIsɝ= #uV–ex]ۃ)Sy瘎zj'MϛJNe^һ5prfRpSpnA|Vܠ[ +2'1i HrO}mމLM;l;O'Z}GcCVf"#[ʸ̬2SdY[dEH6eD2!+Sov}U9/ޯyͽ{L C!mKdݱ((M'r"rb`,OfYa7"N )Z'K#vϤ̘6P7P/Y!x?44FzRzR0rfWX~XrCߵ))nqɥ'j7/fM mҽ?^vth}? 5slW}=."U4 p/ Ӱ ]Z1tu9FFE tj#'5o{OC-kxi mn-fJ^"׫̝%rJ܅>׻dѼZp,A~ g%oTR`7oҥ)^#l J6Xj/XWTrA ,4(B4?L)) ;?. Ev\KXZB3eޑe~m( ?uzr}9SK:T3Zi|g:jwjnas~sU^g3t!7:!u͖2nߘ3 ~=gJ"TmjOeq԰+{kA~LӍq0B  `j.fK:,_5.< !lc-'Kқx.ovq@%?N0,>*ɬo40j8r59}l0`v UA4Kс _ɖ#!tM ΕMb0)ԍӖd4 -fb„$OscS^$& {~rXs^e>KfL:ZŚB 'W,WDnNRf 7B_KV^⍞-BNJ hS?*LrԺ2z` -| gfTS<'?59j܈{e1#x9ix_i[2*CL Fe^8Z{ΰ^OeB%<ԬitV7BHo}Y6I\ {kN\ 탞;L1aIs'h[?a9CG DU##_3MMոj b"C Inڈ1kT|=I|(;4,WY~$.A)af6~Yr@sf8yn6BuW-P*C$!sNQf*&+i~RެS_z۹}Mc?1=#weU2h}trגa;^^ r,£3 ~v=|Y;mgw#):^?kC .ךkCV'%}-4g S)}ZS|`gH@`9._4۷eBey`~ ӝ P>Cte#dPy2,PVh4)X٧ d+ǽ!Fp;wR~-I--Nxwn@u3WƲvH\_0&}-\^K7fTmv8[6bstfTqEYOh#R!G"^'I(ճ"yFN*od3G0$_8[Eݰ\U=ZvaŰ@[&{ȵs6EKiύnmQpoo0`kTX+:Mm>ΝorMk0>2FCLӊp@C%]C&167c~yαNWa xiZ*=*z){1Zn\ӊO]eG*Z6pUv6nH ?bJ^vDt|EQ4`עIfYuE⓿gzJQr`PZ.X+P=@Jw e>m}9"?C'(䌼 5m#]:yle'^1U2ɳ]~dY]\q8XzyقuP\7{koٸkt|->2&sE1e `?WXk\<]nhs?mT9E'InZόR-eC"y3" ˌܜ*f=6meE-M=~DO8GgN n߲m3 ~'GOJsO]cԼJ.00Բx 삇BGH3`\: Z'ȲDbC}fq*%םՐ9(=gbSJL&"e>=UZa:O'#IKũ{R/o4J\|VC]}?QT 1?b08+.uhADco}gc VеpaqAW=ر]z:b>eJ^5M$1@{։%Z/t l=,!EljN`=uIfp<(_˟7q}# [_pR[n7G#}= _ZVw΀k,5FGlz;xTU^PMIS>苺 272Y # \1d>' Es9/W1}-<`,FDӮF:MZ}͏B Gwe-w$ _Ν$n;>peQj؄2jsjFFsDxqCO4/V{$GJH;XLT#=Êɺф2՗^4&WH5 =RIo qzk:Dpm&iJoKPۄEKT>X3y2QZ& 2`7 NOk2g7)VUAMnv\ACOIo}I)$kK`|n} { X$כݍzz nŒE00=gȲS~`n?ŽS "9e`L5u5\[[X4XOҖݖ}큵Z4r,G3 @9udw\\7ܿj#ĶVȵҷQ~Ecn"Ntȼ\\zf $ϳ~NswIg0:BE%( `B m1vi.֝ȕl)&e^Z4!oHr~/0`f+v𖵑3V YSsnsChMBtaU~#zАt0 Dc5=ZA&/HSb@)0`?&Z.V8#Tf ؏2\MW[2&v *yW&#4UD+|`?Dͫ :ٮ iTC)u KJл<́>68¯uuh浃3wL7u)De "ho6ίhf/Y/BL=ڟw"9[c6.<ﵯ0{am$5v;Voyj +ޠ99zaQ-?,՚06- uiN=3 є{Y!{|7;̐P&EVɊ#{S ϩ;9^u]/GN<3QX); >eUCMv}%mEΪH\ޅ|Ҳͮ~o o#ߥ+ *Y,[^^B#C`A*А>Xg-v;1Lm}e0`f-$F9&[ռ{/9ۊX%$D03>l-Yk%e3GocxZv0*3۴>!>6B,ky6^\ZZ2fۥ<FVK®uiƀ\'I{}ݳ 23@7@rL ؟g:JX5fkކ~mݮs J9 1K|PFF˛Vc1$+Q穰t$S If_KQ|Ya].Z24wh;& 8NM }q:dŝEwr@W6@ Z2 *a6<+ V$O].r7d*,P"0`fAY)ts^ fe.v{\a(T^kV,l_ i!KeF #M0:H]Hɻ_J@GށPUg,Ϸ}xkTܫpM |dk! Bċ( &X_vi~C;:*0-Z:Anjqd0` Q΍omYF5; ;9}}7lM,k{evdj ؿY§qDvOSxku'=<sk ok /5IAL!ִ11<'j ص,esӤ{7H.u](.;(AِEV7L`^%M+| m|:|+Dُ%O)_S&B_d ϱenz- (zUbwu=Hoۥ0l!I1%Z}(x!Nb4S{^ڍs{Vӡp+1d۳~SX 9$)5lc<9 ~e>8{V/hmZoO;l _u.@A *DӮHWC]+6xw#)q ;hc[@_a`~U+!sӤchF-[^:CJS uy%Hmx7YO{}nw5˓Hg({[lej*)dbmt8&3m l'@ugdsفDd1ک.pC[͒>a2S@4V)ۘ$B,/{p{ƮKoֈsÄζ4=s>$t{*=ҋW4SݧG2yxaxL>@33<Vkْ)۞P4-Mf":uy~{;mڡi'_6~nj} W#k`Mg#|>t>de:%.yઃx7H R!"m56T6TS䬜_sk}k|γ u*IAOAOH>b$b{OLMopM336S%Z5b\\6jdJM[ȥM XN%e`KzuN8Po=$6S2&;dd;;twh+?k_EW|j+JtϽ =Šq G&5*vx,{xב(O<Օw LjPDkJ-hp-2&( {ΝQ7]-:-ȯ6NsכΘS҉m,F}0q63R-cQ‰rXLαJXV*̚%l*y,102kdǤtMK֮X㡪 > Oi{zwow,~`?*p]1ЂɍmpiD#9xYS3k8;9cpQ ` *6s>Ls̏:N.a5@ቓ.-FOqWSnѱBrv`?הH9DtmUxl;T& 2jh d)i+UmF.OSF -gb1 jMèQq>xe!u.[ůhuVʷ *4o -k)4j_Shdys\a}ص(G*4iy,v@QL;Dυȗ2D'ޤ81YR K!T;yGuH-liMZ w"{!嚿sq>lj-u=E $!,՛{1{7֨Y,hz! i3R ]fq ([6 r`aOӯP.MNV٦I3x!.kɯ _iSыX͸cZʃ=2H,~2(ɴ$I 4nم-jhRx SDdvyðʀYX@T*D-v:{"$X!:M IԊ#պ\x ؁qOԼ;pfK>/΋<~mʜ7st|>0`?T2MJڷKTrfHO\[xߦ˙Ĵfg יQ=6o;MSRi D:L \?N7#` vR~߸64;mc6EEnӨ~Pgj\ دm1ibKĝzrМTJd">]Uތ#J}e g$bE 䊴fz,аG+ϣS:g8uia+$Ls* ˘Z+WPpɋ*JwJN^6'^n26r#U՛I% $⫡3SZDA}Zi?aX:v=TJ0nq!XQENtR+OYCci:ЕqX#QGLEwȉ(ÍӂL?F"+<%Eڏj`ԛx΢iU'jX/_d'9[Cm 0 F#ϙPIm2\^?n+,=#׶>l=G|mOcy’HjJK*0Kg dH;CݸaD8']f=5`?Ӕ/?,z>8)=OB}yD%&˷+"[(|_sLA Qcqo(׋as j#*KT鴈AM~ľ2=}FO@"U{g&wǶs_7AtSFsIyϻ}kl+g`a|ܝpZ<<%Ֆ%# Fynb8lͬ&"lkMSl LlgSIjߓ ;ujN*4(%AY%M wM l-lM]L,lA$8 ǿgx;;k sc#Bp\|H .DJq=c@WAHcMku.P61rQ05{ڿz7G޻鬆Jk3Xzsqq'q'qi؎!t;*9!~κ׳0vA>O"O">j2i7()-߉Upo'5?5Ox7K'N03:;kt)V,{m:v۝TI!tg4O;YY%+hU=}[>V> t֓7*eCeCN{8u} UR6 ٴ_^Kԯ_&^nP2$;%x]&;3Wq@{U,_UoqEZ#D$Wr5'߸tѬ}y` _|iy# >Z|~(#~hѣ.ߖtRѫagku9郤b(~D>ҍθbǀ[X\|=Ӿx4Ey< ]*M h>aG [}֘ɌRU="'ܚ*1nm51DɨB ɐكƬ{zbgJ򈣬&t#Vsw*uŅ {y:(I},Xg]=*C_r:_ 5 EvC+#<9s5g(#7FM_IϴKQ($+hMLcݤjya0IJ'1Pwt/Ve*9UsFj6C\/ACeC,ouUzրDF5)FQ~) En2`}8<&j7K:L!B (7qeTJ0 ͙g=+zR žO53'B^8MwCr-o>3TϬYڜs' 4-WI^|t@Pϲ%P-_G^oPhRIX|%ڮV3jDK,Z3s"1pDyhb7;W] l-%A8h41gb-ܒRRј!a2g K.W' ?N;Ub7?j'jÿ2y10Ʃ0ڻVy˛+49F*3r} ̳w ISKNkıBBkou~'O`\?$_axj3cFKS2_T  &ilV:R3ǪT$ H{H r,8L@ucU' ',(^I@I)35hvS,A)`HҚa> 7\6#'6n5|Oy#-S,@ܼJPn`Ǧ=֒Q$'Q Y14Z/7V֏ݾ^CpAsBh[ykO{96x8["ؗMGؾ%}%^4m0IJ<$lT3\R6ooݟ&m1 CqZ1 ܔ]ZϢFݘ_)Qcc7+ƌȖ~`C$KM{|p-"N$?y07lW-\4{0 {;\PmkJ.1tye? >Bn-9ecWe_c>{0|-?b۶W!>m6 %%argU{br$cjL 0"]H4ĆgB9`;YZ(?z `ҝP⨳'`y' Yٗ|̈a0u1"1>~ Χ) 0٣'He2* s%uu 0D3TjDԄ oDLicohb^Y~_VJ|!) q`ԒJԢ?-{i4Di؋*/5R٩2i!WmI#EOPdtCG~M9ݵ^\G5)kzpؾ >Mn|LHhE6)Y=^HHy۱m;5ہGl'xmu'|LEH~4UndpvӘmhSݥfY6Ȑl9<2'O;=́>68̣GTy TY4 Po:h faL/W &V-vjNքLu-}1s8{1 )JNkW(Fmv!}Ok9VkafM }5 w6狦#C4}Xf8DH8x"9d8u~ ؟g0j eXf3< 5_5$~z_ڧ,pp)̳;26Ɏw7C>)R@|O)-BX!ϰz^Ab#3`i#U&5+5` ~JY7Zqt>Y:l)GO.`S][FZm 湖kuO_;j$ LRpw,|/k'X`{zI71F /`au% sG =JRBҝo<_Gv4;ݹR3pX/ D:ٴ UB'1'9ξ_}gg1/^C^4C癁:NPuƥ>fmI΂ /YTsK+XW(XXloC,sfݏÒxE‹HCk"=lx_46.qL0IJ:-7>w]d,SsQCcR 7`e6< Ԃyk f(ʐVcWʲ׿ ϳU@ o{6pzLc+Ex#h.x.\IiG?ŮPu04t܌CY]߅ >e5Q-<+RFz\ d% ӌR3[Ppe59U9Zft7|_?KKwCRh؈as {x?[$a X+[>GUM뵟~t-3دiNĢP'O7Ul7L3FH#GFy&Ztbj d]=fJ_ yaH7љ-;6&j MHQ@ƓT[Jv&i1ɾSHрo$wId`D˵7WuQ-v%ESC6 w}GaPM׶;gʙT,yD<8i*^M@aWAR.WB*0` LQR`pji³{~_"xeʼ.ձ P8zڸt؜P{%"Wܯ_cX5Sv%א 5hk'Ɯm3z_\~ra= =oC|t>h:.J$4aQi5̉P~\֙HQ>Ow㩂i.D`)Z`oh5g 8h#U}J6. vP+GTOB3`?V%WWw|!N1YСZ[ֲu򛂎GM'ۅޚ+bݳG7D~S^V2#0HamO&a _J  ̗ Sc_UlVС{լkY߹zEA9Q+!kl|QԹZŜq/ Tabc2sש䁡;c4- v1T%7}F Pd~iS)0IJ49J a*Qu L2|.ӂ*^X6raK=XBzNp#/UvZ2YޭxdlE!$`";"H+>nMޓm L%eԇkO_+! ̄k k,  |(Dtm,\'E"l;W$r2M^(g3jGc4g>W"-p{tHP؟gq%hfdQ+nHcj.ĮvV0f̱>1ۂ|+0,1njBȽ?8]RX~?V#=|G9BT:^Kkvrmh'ebC|`d࿘PR}^]ntV{T !T#5^x'85R 󞂯%sv4ƽXa50RW7?=S֌TIq'Nń7iVÈ?%XpWDX>޲a50Zu2H7¤GD&?,MtocDw.j xHMU5M0_kʑԍ1B ol!ɹrt]4!`8B=)9h9èB#(/Vd)0D;ʥ!#RagX ]t$ܺ @Y> ?TW> 2cӐXoV֔z!CІLe|ї\;F1 <bC7Љ S&*xGռN{ ΒJ(QxoX$ äQv3Y\WŒVk!rěC-DOKhGf.DyݢEv{IXfiAYʿ&ghUe )m8FORf6X% *v4f(~o B$KL%.Xg 2Kc ;[Իs)VEs6?k%ߜbeT[gGqk$]nU54GDjfQMƳ' ïG ؁u]oGTU%'u &}4,G'x mC`)ĎǝBęlt m-!c Ř`[EgOFhg%Y\kpɅd+8u3^x&礜O ׬aݝ'VHkހ 0D3o(W,#okc\٘}[Z-1_NWJI֡=6(~5W:Ia|P~5-D6񮱦Rڧ+ԢC7}ய}VMZf-.ry;82a,u2L]U}ߔt+UCB[CiDT.A6%ݠ u2{z.wܫ?}Fg0N}LhjYQPPG).m< o2o2q-q-}'_ǡܡ ޼ǖ棬G I>8' agAɇ)NOfqaX)UZ؏׼ީvJ p7m\׋g,0K@ޣ`|w}sXrp0@?xR[y)0U-Na{>;k^n ieE}:܉ k$:V1KzyX8j"'>8kUCnHeb46źs\Ki|;g܆ ǚOO-W‚Pbԥ |a\ ulhǦYi n}jH{uel )d #FB.]LZŭ:Ͱ>cNW~l_=VH.?gkz@8Z Z.YRό!h7Mv0?~?JA:#/*+-r8tDm߼cj]#(JX*s02#U{qk0~6 @tF';ϐggUSCפ{sPk\>_|?$Ei bwHe!-ѩ|KrBe j+5Tp i0UI-R Hp_\d+q0K.e{u7`5[Muj #h3LLp|랾@6+.c0!<,1x^O{>+1[BT#(Rx;A)ϵLP`?=ЅB>A'1d@-F/`n!=W^( 'FL%.-s&vdMD[ZDc܃^WI>KYDk${Ol „rגvDq>A${1o¹'xcheJ4q~mDX`}>enYu XYv#lV\"Yd+{[q6TWu>R[{ y$T0oMmh_߫1X/P B쪊V7;')齐SNm̹x cY5W4o_FXћ̗5>Kz\CG9ܠ#sB P,kBNB ٷ\wetG/7:ck'٨iFGnb7=H6hd:Tt{+9f\*<&TB^v .]ȓ#Y:o`0.⽖j{K) 0~VdUX% ~ah=)ݠ;QAd*[=͜7sR.~z0jtVJߣ~\K' ud)IAu[9vIAzP46UIK&cԍ ڲl! /wsu7=e]*d3^Y v?t_<?'}$1K'η´`LJݲeay-zE)(G[sjVhcMmyYI|)B$Oe/?AH8W?F]>JfQ@c Up C"W;VWaf]MrpOjVs} U&CV*<{G ]0*7G6kփc,08P0`HSu~ID \u)GLZ cklߚasSzٲÊ49ҾVbMWcJ,ZQm[ia-jwTfx!#7G:#AZ~sz$7 `] g:KG0HIW*/)^tHG愈N)`5kp7-/*ʭY[">Yv x:h& ;=tϞũhiMdΜ=>zu})I9]ގ8i] QH;}{"*˒XZ~>z-󥞴}\;'R.&(Qº(pi9x8D֗ɡTB,mz4/ %ۅa.s~95[`kH>(ܬg m`?jlb2Ri8/6 \F8xc{cI6uke  1yϳL؉a3ZԱ1*G \I7D: jiE> 2οMg[_oYpkzs6}I~wRi50D 䝺gq[-0UDYCd"߀?^8x-ZCC}B 11>q)VC[,X>~)xk!D8g}j;mCĨ 5u<^bUzʎ̙2B;fzXhB37C9e4Mrrv:_[݈b=Ip1fB|9 W_Y7_lŵPk`g˔ [4.fp*aM})rl-]<`eJpj1nxjNSp7<*eW=ti3>)۹-TG;K21S3vj%`]Ü3C&,Id7vYyY|KlQI+5(-c3DVM4iƀ+b!Z# 3۶p7!qs1`gL8tLK7U'8<:؟•"X jR01Yz,Yb"Zv:mpa~&f,%l`S d%>?ػh>W%{wdoّK]$#$ =*Y!xyn~~As+;!vw1M|Ճ}VnIٓMٽsʝZχ>j&~Yv-b-nh -8n Qoz\ʾRRŜ~oD+TPYϳFlWEf9.g *zAcၨ%v/1汘`.ٽ91Zc 'e Añ٦~Ӛe}-P}'ҿLQ}MߥOSدc85LFǐN 쿔vq03?Pkrh9`LIzGN0aث5|<]X55Y|Y0@)`#%oP àPnJEGm^+&N5`(x~'۰ fuRMO$%NENka{ɧU؏2~MO[2fN|KK̺Lx2X?`LȲ >ϳfZdP36۔J%"Z0,̝Wmsܬ gBE9Lu 2@Q42 QeMgc⾚-Q':SS⻎I!m62p5,#;c.[ ,awߏ9l9"1{pj 8;,f  }5ZW6'Fi:-r9ElVH nTl짜'Yzs=%ëX=Zy_Di{XӦ=0D`Gg#pq.q䬚:60*3Vk׶x=cU> 1!&'uaw:r-LX8]%2O\MxlFEU/5aElqN;eA; Z6"AxIn!-r.|e]NCTӍOh`o2{CG  K(/اKF}?[/w24i.D^twWa'|r Nmp" 9Hό5q"l?Yv ?gQ,\J^*das2w,6REZZe,d;UyZ7_v`c8rԚS7>mjUn|ϫ =FNMy u'@Gmo?%Dޛ' d|U899Y3Ȧuz57 <᷍QVZP}װQG<YtxPabK7R2L?_.SO0Ċ^KAYY8=0k,]&[ss4Zt j}CS0@ r6li'`URcAJL<W1bMpv/L"pr~ϸ.Okw;4g"Nb7o2RKPͅ| Ԓ _Y>l `p *jt[7IPr41I2ҩ A`W;>K1廧VQJH}KRYƩ!Lk ; U"W/lѦcaJQ%DK0Jr~"'oh5ce+原/ fB Q_|Zf:M~_W6kasY6A\?@:JLZ14aa,gE4''9HОض)y'AIQc/i2FLou3'GxY`19HK(AsBZ~0t?k KI[jBp#2,Rye0~{,A_TGg*e?Fa&!M2%oS`(; 0..T}>r`SbT{lgcaЗ G*HGF4~KN bd H}wݵu `a."C^U2q<;9s\I.SCx,8r̋[mPL.'Z A*yc~`"'z.V7G\w#Cmq4ۓʄGx}渚HC?74kǜMƭݜZZL+ri"o"wGkDk<'<+?6~7IډB1[uVVs7殂yݸ-B^@^v*DuNb{593ͯT=UJE"c$j>މ12g+&/uUj]uE; _b6KՇ 3*0&\~؋9Ї/;FEOit'!Oɘ6T&܇J̇4/h㒂Gtr&L+}؏~/$Yϫ@"S\t3 V mV*]",~֤ 5NOb.͹"eމf:;{1VxtĄAkV{WոY(<ȟyG +jRľQխ>HEVZ+TB kq.z9 +NYƆap4υ|1 fW]R|әLGd)LD|j>~7>@ ʪ6[;{k˭ϸ2i MT ŽV $αKJا?$\Y(RkBexZKY:q CU#1[@{Z$06/F*:$ohRȨ^^5TGV7"r GÊS*,H,Ð&p[c4Wz^BxXKyus{^KN} /.Kc.[k,Wx2Y*GęУюG;s&:`YB*5'iΙ ("%:2e3oY*g6%\=-7q/9p;y_jV#=-<ۤjNxJpk`k`N-QFÃ"R' jFe{u,ņ~}"]8&7+XgKy=a}2rv4M05TwΕʛ1^-8yiDm*AѿػxǨ!2*}M2dfP#lgIFP u>x<y\+C5u(]njn, _zd>T|g=8OTSQ0z[жo1`!()|8$aQ7{+Ӕ:=Vz` ;*,auU;M,^{;WvPf1_GNhd'g)[EJooq]/i'i<<8DF𼬄i0rL 9 )h*dcnln !5^e;1IyHA3<]<]<6 >"FN\9c>|9?gPtRtEb}J/;аbJa TU(gZ@pNpNx6@EdBJiX>ԩ"r'7x"GYSpqy9BAjwDi6Mz&Q{ nI7F׈.ܮ>TL(vuWJӾX.yS2EzbmpOug[zfL1Y7z-:wW̰yoTJ\5|E*h2~R]>ĺ _9r*kX5c mgp+tPD؏FL`H5F_?w3knnC`|:fsmwl^6:}Z531˙S.]ҳ^O:"ժr$؊ ,̽!I?1 $l6*HlD*9r&2c#ϟYɅJuc+?-Ifr=QgAXk2vPy劯a@Kr#t)E\lHJHx!`e[NMV6;.ұ:_NZ *-+o`ew/B}Q\=Xtf/pFm s/fWf϶ղuE@8o9ok-]ƒCWtA 0dSO5R~V\+:+W uːh ˟iMh *Ĥˈg 8aH.m7V^_IJJkyU\:( TYk"uuDx_جp_"K ޫ.d:G)~.7YpC-mRzݶ}<31aOAwBv;&M2ig[ =Rj.>]gp80` =:>A(E'K>PAzm )`%7AʬaM+l/WD0yktX-բ10qZKɟLяnYT=M2.8j>`(cqj ?6 do]xR-k.:g._m%G wl}Jw"ŏ8ϠVwLB2.vgl;e'F59RCy{c[y=3k5R&DC7=Z|9[ݱx%ǣ_gv}'դ;f8-qimNV=k̊X3Y?y&kMC#msAn'qkU>ͼC?ט/?k݅aC7D:0V(^eۉ mv¹g`/mӷ0̥Ϛzn40o}jʨI`LD1Wo{t>Ņweq7*A[ rVyJd:$UwݪVae,쑧RrΤENX\A9XtFNp?f+3\Pؿ~/gYjPtc͢cװIXuރYxǯJYIw"OZɀSTxLAp ʇ5XAx{V깒h]W,B}I >}kOʹ($>eV\J}>q-X{6r ό;'S`d(Gȏ:GL#xqB406+8TξOäD%Q'931kF,)~=˘]nJbv,|%6(^z Q/bF^Ӭ~f_ms 2+ [457lU3yׇ;\%斞[s/e챽3vŠ\b8)Y aX(W)k؎ g|` %:2ei"$|wNT;amfNMSz|ͺ?Bl62"HWq2Բ;I}[?&MM{$=c18 TqM_mfk7=cCpHȺ^оT$zQ^ҔӔa= ŽWފߊx_77˗g4+UJ huN2Tpk)}G397I<zPoc/b/"'Olpp2,aRSTyI\D!^x>hOxtTcyK+(8OޯS5 {TPݵ 2J'$x'qО q-_;GȢ-gRâ^w4Èz.{R؏iB1~ە%.a} JN椃]S//\L{,wh/NG!t}R辤1cqbvB=(R6p2,/M|iGsXC> `e]Quo:_ };7ޚ\ l(p_ّmN~LJ~wdx|3Rtce«_~L) kX4 Fź0a&t,kb ؀.1Ҏh8A"}M@,{uhh\u*`,)KV -W/\!xw8=9ji4 }} "%t]ĉ ْm &D158\,/v0Oz;Z;(E4F=|6M{5$l|t"iV *kr[`[3ICXxtPn&BfR ݕa$if~\Q61`%Nd1~s伔#;H[Br%a';lB-%k1psjX:&G%F H@#w%X:UAcEj#79@xV[Ե1(ۓvWԫڊJiMw)U_ qΠevECAs3?ۥJT }\lb C΁UymH r9C Z`fV5"[.\Q9Z(?pm)06,;Sࠨ!0G&ܤ G,.\OvZhE*Й©BN$a/4z<뗀}>B`.YVrťDA\g=qz٦neXyHAYS%lQ#QpoӥJ (1ܲ)R"&uǨqSV) ݑ q{d;W G:5z86%a>6o'g0~;6%sZ|EW6m2?IY"@n)`}+^/ */@'z*g,CU(a˓r 90s4>|k|w}T=N9w/z8ط bݖNEˍa3'ۼ6n3<Ⱥc3a2>7^`X Pnaa} %WӋ ؍JvPij5;-hH¡ y`` 1:eoM& !#ÆHrXrx]˞opwt82t/ZSAi?ۇg6gsEhz[M8E,*IJ~C!vPd`?a7Z[ دgE*Msox^j{\7$yr0~kct6Ñdlo[]hxHv$_jT= |CK1_u*fR)V}885Oߒf دgZT?䊄fL;O׸F;[+|U_`HR[FZ#ZlU |휾@W0>[!L 2T$t7lV*,cY[ԕMo.'o\`nd&31qzN s -{_MM~~8L|:k_k9Lujڄl'qi~˭3 qYs7PL9P@7*fԯ-Qh9F.|vuA 7m;\Ⱥ-SH-.X2i<|I6h>7.\3dAִuTf5 }9.<ճYEqq0W 9ަ(Vʓ!Y*ơe_ϊ롛jT ކ3@]lBs]QDS uB fAiԼ&eJd^^YU(}F[Ѩ+)i Sjs2 dsFX0M)-p;ʞWSu k+'"ݣo@jH8\\ Omv2(CB0tZZ$a Z=V׷xTq%K-]nk9F}`?e8Ħ O7Rh~0Q`*TumLuJ#zv'd}|v=fJ AU(J9 y#6&* u0~oSTlMR #|&WajG6d j1ho-Y~8.Re: m8˽,-\N딗7ic01EA$ڻv󉓥TW !5 c')u bB Q&,͋zz kYSOsT4:2- nʟapv0:W m vAؽ]v"!zQvWpzƤBN>f|LpAPA;+sB?TߺXdNGv\x݃m"w[rK2^M{ns/)`lj^?L>~15x*Mކ5ٙM^**taY>.{Mx˱ۋZ7}W0Έr} 9L3J  ;KٴlңV cf iNFes]HAC2"GB8YX oe0~ |lZ!M1SӉ>)v(Gn^1x`f9؏j9(;WiX3oޫK Fcet\RU} Գo}޷3ᢙ-!uJƖ3ʥI$|Sb|Mc%RQvػp!{dTv !2"{cgeGduc!9f(3+%d(vn}]S}޺y>xٟkhf]eKlAf>g6[7_v*6"=p@Qy`}ɍu-;+3\g<ޛQyQ)޽&udː>l@^˱2Pg}欀%H" ` -4- 7 LSQ$aєU=thr⧯C}[e&bf7a > )KDaGgi_^;C޼Gp>ྊ׬vQ[;Tx4u0kVm=ơuўqߖ>4欼xV@&_!eQ{Vmf{ 9gߩMiBn2Z Ko؏ˑԲU)]&ωо>-4AVFg0:_1b!.&]q>#,{;Pˆ;/} u~F; Vvbԁ,`ע}rtȥ3, J[DPQK0p׽",.KX3. 0TVN }RҙڃL[is/N*f B9"n@{3B1O.42=t:*6n]bO&y[2.9GZHs[]Y"qtߖ7 nnynShֿoX1l NIJ_K'N']\b@$Оe*2_'}OVwUF}b2gΟ"J^H^~,iHON;Z;q}ǿZ }Q nCz' WB1ULs;{ɠ!QÀ@I2IkfkfP6rwF?~ڔV5 sUۢMa<)u1XXt+GC}!ye~э<3ish_ a 3 vr\~#m `ijTp53SeՍ)kI U*z&xtDTϲ߼O(2-yƆ5imSR_Y؏3Z<+lsJyPWd1;/in8[PX lAwV~Bg2tzA+n]1RX'ϳFaew!x~YEM~ FsL|LtI G) yN)Hf$m'6} $,ԍmhU`jfNsLL[K{̱"EQîr{u*y}*;*!fcQPW ؞9}]q=JWr% o=9M41Xgڙܓ5R-V` 5,}ϴ %n~hw'0dG) l,Kx Y#:0PǫG7-dd['\v&}Ů گE-@0ʴz 1twnMDsLv5Km}i8Yͷ׈7)E< n aފ?JF2cE*]cZ"a(~;G|b%kC-IIj<{B]plq,E[螙tPˬ,i˅W>_J30:I.f xu󷃀}"$Z&ŽK!uq!Gﺡ%%mQ}-Z (F7j=8Χ\I%7x0YsUGb=-;cr+L轂_F[k`'td]3j} zF3jZ'J|7ɴl> k&qv .#qZ/C}Va!z%g@~'^,Jfm[#š!IT95";ʒA;}Kir$Ó7.ihdvMPmV G\ǧ%|Cm&w7пvVCۧO؈”bE#jQ6PCdI*Kv~y+H8-FG//_.v^eJVKVs!*4"߯JAAq㨹NGO/Q0=k {ً֞7D{X;?~ӂٌHf a+I=~O̓5c?Y/_zؾo޵3Z>K010Φue=t4rvGG`r~I;kd6bw"b=SߎD8,R`cn,RP=*D4"1B𜼨뎫/A(A?`mկ7+ZE=I}5䵠l 篌3R wgHPYVR?\:8J!57͵ ̿4Ru3!cL}8`?uMYֳz^X1uv-1f=br~n7 \# _ۂ?8&Id˗^d\EQyB{v'v4=ϴɥ3ѪTq1n?_+>&\9ϫ^C5HhsY[mLL p/AQ|T'8l_ b#לVfE *]\CIC+b!ե]s1Mtt7"}oOG;#X%.t}䵠M|4K5/(`hjO=Kǰٸŀv|!Ma, m<1WImLmˑȣ*c~v?Pf)(6y@i٠P4J tEalv+CXg`^u e0<&6 t2Yqvijg,_ -r ƛzYØV"[>1ũ.VBmt QH,6!/6ϰU%uZ P(? 'z8uRJ'5OQ]⋙c &({up(+mg2!##++{n3{6 &㶥22}W'sN)9?~4?F!i3Q\?}_=`-}U(`u?7v}>L#|5K}M =>^-r|d_꼛g:nA h`ԦBSџc7f7~qFx0,X[ZqZZvƷOOl<יU7F;y0}1C#27'/ 88NwОО5#5#}oD,wlw77IsX(--Ζ:ǍYћw>:-27}^|f?ݒDJ/vR .١Feax;ue,ҟ# a=h~,sg4`>j۟JׁftSG͝#kt$HOf|Sǽ 2f.8cgMwMeM% B[Z(5XNBsh2*د`[ox#o W%+^¥,֚cϗ)  鵄5ب|Cϼ/E/|Auo\i`?ƖDz8';7h UD20@Y:Nr6`}>3r~O܈D=,0!ܺ>փ(笢!S1n4gSgk_HCAZr6z9ч(\a>8w1nN悂i)*?27q=AƳvs^ruKұ]RfSfiW\kK=,LVXXM36cwB ![$= a[:yp~s,)II`uw[a 6Gj%m%= e2;44ˎEjad~ud]tNa?2:./Q& #\&:J>CZe>ebnߑ#EYu `.= ~ߺbPOAnҀAk ݄1GĨdvW'wh3U)Zb݃&l [0P;̮k1<˚ì`y4Er;s r`UdK\[k0~8=Hx?RG֛qoI?"w `>W&LS٬ JWi*)da^i]w4_ze펊lf3rI|B8`tPVH|E##`k6uYj"i'B0 F(Yzq!f1kڥvج1My[Dnlj7U)G٧4=R#|vBYg5:MdafDYpI1bk.&~QpT$>Bg 9}yy^6o] TS֕OǿūS2EtH7yyOsUy)j`0=;}N(i;p]NO.^-%+1}r5QK!~9}r//>qĢsvӕK]/4;u yf[cmAyV*v0j/0VCE YF;\-욑Ww'x$[R=)+JQ;zژ\w.􉬒d8'25;F^s`$N*P}/oN40A,CF}JǤl'Cx"Ҽ`Bk8`g,DO,+4~vX>Q ͇`!ޗ ԯ=]j^Ekv-Y3}bQpՐtKٗ49qN+9h `rLtm+`;Ƹk/hxsN#}Vҝ@ *E7Y9AVc9LgVAće/@hi$(WrBVi9`?sx3ȭ64gq#М7{؏1HgG:IfT,k kַocaM HӇK&aI*P0?b&@ xMӮNY cXS '"G)$*p=n <a[`?&(Zp4 ʇ6'aA_3[j2 _.Ȁ8_ʅCMKWz^~>V`( vxDX"zuZ06w"8H!*MQ%gUk'~籄Ac,˂k/` :q[q)F*X0~4f vl:"WBڸ2& _ks%2Ś`L&ñfA)܊֬ p:fZr>GH]>6V|ۼ:43em:IMҡ4Ya'.'З+>Qp0óE=V)Njvxm izS~![,NTSލZxl& h>8j5X;c9QghfϑvL0&Y;w}dH6Q?F~:2{5 Hvd̤,'ǞVb+O7 4=0:_sϽPrwzRŨ\ _:&靱n̰-Ziӟk֑-/6JF :`~u~[rZSҋA>O>^: %ﳫ!i\nDCPB^?Zz|irކn;l6Go7Jf`g1>^÷9}.Gy,.! rEz%,%mxg ڣ.ztL[el? >lqLs*3YKr]K|8Rg{7 Q3>%r+=r.vcPg3 ٌsмHszWXj"3-%,zLUo}=.N+*;jm8nBy L92 L<EyL1XfL9ݳwsn]w޷Իr<ϳ<٘9k'NZ{v X<&rV p qI_\xŀ,~w{qKCLѣ|mT!#>vk 'gWvY(#-L.eYODg%ʴI~r}ބ 㑧ta^.yKr2ahv,/D0'|j$9K:+wCaKޱKi&tJ͕Q̼o] pD1k9l[TT&<]RPP6I9}e]wQ4YQ#&ɖ~)͵ B˲4weܭGU4v-pΊF+d-d5_[FPG$;Xv΋̋_䃱bBD;}j2%IUFdd4mH}Z}:aBapz'=Ut[0p&VGϨ̨l~'ٹ5.7[gB?†W!8:*mq3EF=odkcЄv<^U>L}l_sCcizΫaVTv 1(<)ʤm,E#O3us%U >ߞF)ԗWu"{`h?E\|3 ` +ZLZo2#~ {^+ÜSl2cAq0{E^$ tM+čL8,,J$-$J@"ƣ7ě}аnfŹ`AW$|&V?ܫ^rFN =$gwǶ3w}8':Źd<眾57A=w1𾲇_, 2!ښ}UVx>V;G=lR(<^Ԩ꥘9 !~0׈|fw}n9Edm똍,W-) K>>hr3|"7ycoiY d+8c圂kgNwNyNv_ez\ؑte9)m=j\1z_a`mF iMW(sX1z<S쥨# 1"K/zoK4`jd̳u aKpӿbCHr%Gb'RLE=7^Í[!!`}6q1̜S6Q)Z㴄[E3D~SYS5tMfm-HqF%; SlPRe~]π]c^HպW@΁>{ɯ4u w9}Vn >Ħ|q$qK#8U₝7mNtU"Wt{ KH;y ~w * 8I uOI=~R_x>V Q]Y\m7_?Fjx=`٧tI4d8T)f53;Xxj 9͐qkx e rQ{-;6[T,T!Ɇ(k_e[~+ fD7̙KcQRHWvsZ3jX< ,Pi&`x"H"\P]?;ͳ:ք̼C$i6usKv*MzQcC;}œ풬L|SU.aSQD"4N"Ha0'(06y⑙^~2g1,ԓ&Jd^[M^KU[)kF'N cS{d;}KXH!轀~c`kw},":,3(;Mo vT4TSHdJ~Mخr)ufPc/>Ÿ́: [ُX \WXO+籟eH`$(N)k1 fwKc %.fg Ū;~ԊץF}`}: 5= c1vҔY`D}̔oz#~nHd _^PʹPA˽}ۀ&nlXcXc$pk֤7;tW4c4cAnh"3Z6j;ӝҚᚡ貽pLn붣mbt:FDW=_|9![@UUjJQQV ,eA6tf*j'rHʿj]-lcſix]Na9d$$feٸ++=2ƽ#\Y}73VS\|~ CX[+52c"9OrP#M{ S;AL<:r9V, xO(j?jK `}=Iqt܇4&y٫Juq)` vQ^>"WNRU/`}"Me/O9:A3j47"vt;W? =&,-?$%i _utK'>m}]U5 'D8:$ I84H)-ҠtR""%uHAR+- \=/}EZk\(rvWg=1g{J]PoiNIۖrqx^Q [8Ѽ;VM1qynFP:y9Bt5|"Ǐ}:\ :gWzq`fl9MxY5qE0_OhrM[-5 ۽=1m6s/y,f;RW,ݷ#k>8jU{r$0ϱV (&Y=g04OO؋bYtB =s$O[bcźJ-#9sotn45sSz 0~k7 s"1ÔgkUDNApin;qbF3_|['Ek /;u|dӳ^HKT_UىMl~hgxE龈/dА>%_e.v99u^,b)swN;j6`7Lb3>;}"9x%^"xG%{$m8g !n^=DJe$ĸL# ߖUw}DDi28;w< `㩴T>g^F|~-E"C"C iyh%WUdQX*U.OsJ vXXSIGXa{Gg957&> SSUJq,p,ΠP7ccRAMjhi kV 2 ϾNKV {#ڛl9oH67onNo픝4KH'DgܐNi"f@YGOC֤.g{ JyE›<$ ;Zfϸ.QHqwr !B;BexL_3%]McgD6)$tyk9J+~h¡c_ ]e^BPJ[&ZX`?ݥѸ߁NկM- m{l(\rc0k`]o~'l+gjhJY2>ǔHȎk[UWiIogozk=}2(yxr:MAS.cȕ|ݲr6/w៻M6wXnezӚ,yؘ9&Rf]4$3wWChsy3O-}|\>c {c !1ku gp%Bon2|ȗl~͏@VV^ӈy )ɫti--B(,t-~X*3BMa{_S~?5^0hP~5bz"\+Wb oG5$O@& KL^ P$3`?WtrK_eD%%d/dÏ3c4b1bABo002cn^e n֓"Hub!В^0"1*}5-:óaYʸg0~9?m&v؋6>`}˶Az5YFG[3i~P"3$rNal+HwTo҄M@/ 9l ?FY_1,&>hk<6ރ(Ti{ c [xC| "|{f ǝDvo> w0_:wdz YE^ K0/m77̣5Q~} ,!=`?Tœ vwFx5vQ0iwvwM"ry>htifRU~ fa%e,̑j & s,;Zmunt6#7'lR&zZ%x$92|o-ze:+Hfi0? a" VGuJY Mi{BaB ^֓DGiOA*տ~zx@P&'83G#и̛4bڜ3ax ,lNU0-F n$v8q'+y|A$YK_=\oS1(Ӗ"!q"C8ZXyT fG+ v|E#@{|]rU:| _ژHIw}߿1ڝn))T$Xc`[y_΄Dw? QRڤU 5R'`!#G t_oekUUwŶ?7@lo/;%{Š[/#م,DX=Z44 Zwx-{:r7ڊÃ/sZX˵#NO{S{G@T!ac*{X>a)lXΐ'ջ jtǬ:5n'^soN-i]*ؾqr34Re,muJLa˜{2>1BO;xgY\iBP[CLޚ8[ܱu6-e,Uy-S,@V߹]g%OG$΋Sc`?~x}wۧtTTP5ot;֗l1ᒴGT՛r3mByE1(7 R4s-Jj'FPR:`G! D: L1E93-wHvĈiǫqQՠ)o:51rY]`^8/aDZZ`sݎ%&bvmn~` '5؛:ʔ EX9lg}auqe*Nv ɥJP}󾙷ihO*fz]9lnZǷ' f]-Y=+Cׄr"FPM۪d!5|Ι-স|ޝGCKƾo cc+Em".{Dֲf'Q1!KRY0R32O=~<ϯTpΫq|ӽ;^<.Ų"Xz OHKwPo$?@#0 lGSܤ $)Z}F|2.GMjwp]_VȨ&{7eH`͈0hNP0Ͼ~.*[e]ޏ#`Lm3oltۆ/_:Anbozm) *R|\slٲn-ðe,(6}m+zfU.f 6.Sfd{%`WNrIa{s,0l/}#Yߎ:oP٠Ā#m< q}3~f1՘tbԋtDuY`trt5]]w!l~kL /IEU+,ʵ%OfY۲^Il /86MF|/:Xq&jVL"ƖVF㺹*\P=eK][tvA^f:և vW[~ո?5`5I?>^O,+7K)ڝC3-)m@YӲ];y5g+] oZVzغ:lCWP܇X?T} jï|~Z Z=81C/ &Zl'Μc !j5TPW¥rVfb8aq5`?[>ATS0͓>VC:,]3X۸E(q- )y'FLW4{k[`+8CyN@ r% FGPZMLJs?YRX){HP$rgu:l u{K]5ȬH'`'b.'J/<^֗ybN^^{k'\!V`_&D)&͘"6eV/MgRZKKNN$tl(\}r(t2R`L>Eˏ1h$JL9tRXn=LH"H"zzf|J!߿υq.{q875O9ՌE ȝzH tu›ZKybybAƇ}QR2nsӪǞM4t۝p(2C&Jm5~CFGܩ\vD_d}R{Dk$ڛg#!M`e;]s8StgaTk gPqg unR+}<~?3 ʵY7 BuyL?9&QH:iU!A vm/s9L$WD#3mpVv$ڑ7 Շs$5#z}^l{Y޺kߥ !P2 5l 'zu,{=gÛzh=H0Yv hU n|{~?+ZVcJ QHR?j7+\+⫝q f!Ѣ738 4MHpfQ^US׈hxs +s쓆j) ڙ֭CLLe-""+ ivEi(5ǎN;`;Tiώ?/®o,f \Qƕз` [(gAÐ(*p iIehU00&ͦmڧF9s*7w^÷^Irۛ2K\EL5 دm(p.RB-bOm-簦LVDV, ތxswxxR@6ZT?-It=N 4nߗ0Ƙv񭫖]SBi#Yɚabe>[-0&4gDQUFl2rw_='o1g%g2zKaDv|3::zM#6ʯ M@yfY)$x"*R{9!Z3'?eӹ7^<1uwHSgJ}BQ+W inƕ3 2}Ta8Ɯ1Iܷ^2^ ̰Hlec祛f: Fqo*Xڡ_|WWHEyV8};D?'LvA<π mW^Q( B!GA^>Y!sv1qbe9ۻtþn-"&B8VlX)͡ kj2cL%{V0.UN " V@sqrRd|Dq ]Nu.BHts:L(f+)s$bn`*(;9J5ytssw35ebcN vސRS@Z6:Dxd}aK\A.| \ \ =K)Xrr5 vr,,o3Slds,A!QHS-KʶI˹̫O';a Tq ғsE09X;%םcp/Ns'Ӌ}`qPk֝ai 7ŭNkSƀB]eo?i ` *8!ZLw**CJ|Rqm-,$ P` ɑ5a 9)eVgWZ؜\t.uٓN:&_q!طl)]9-2:a;=gW1Lt^id? og;=JNx(LVs|N13x2|0܏ynIcD8՟삾rT`>+x5q^V&<z c9;HAH¿l!13N=g y: ; e&"l"lۅn]ʉ{$"!Q/tc 66g)L'20Ioɒ`]IqqظV5&ר#GƾcưKis Gn$+2Ѵq pl/z^?Y!D!B}FKB9B9))┰[ķǷgɪJM-2y朗Й;a~>߻áUXu6!8NIzoV@ؿ?Z(݁ŝtFV%;n:pUŀ䴈-`S۩Y>_$oȫIHa)'?Y#~v:}Bm=S;!" <bG[ۿe1Y/Y[ߋ)Tih?Sl&?zEae}HSKa:Ns @4DmRpn*?>Gy:P*XY}`!:qGȲ}ߏvFɯh%iNT{@I}fZ'QKv89$(= 3o `}i-W\9 ut۽j%81/;ܖ;%2:y.\rGp钕zK_6q*, ֍̔3۪rNzMz:h:b:",{d#2Cmf ;UADV 0JPq\֔{τ2BBudJJ"0Pm$3'3s2r2_l!v  >*r 4w8m;E|qy$ H G `G>%`FO`?Hھyubw\IxC\1*OH.OS{~3sZwrt$o!p/N*Gtˌ='k xgo\>yH}`ț`vSK'l9sx*r-nxkz84$0":i.PlGY˙F4f?^ů~Vzs3߯yu=0~sMH 3Hnn߂쳨anQbQ²6TZ%B;"d9ˏ"#𻟻\41kZӏ:hD袲 HiL~]'9QHu#+ TaNpبCfoWZUZ6Bq74 ԷSIg:L-$nTq~Y ~bɷϷeM8tɩɩU_Ze_G3luZiwyVͼZVk{?=G*h0n3Ewݦ*&!Z!Z9ǭ4?f;!8ݲr=y&={l C%{HvGB!"^}m&ޓj%#*dJ ܇t!SO[>^o_dh\S OSyߖ+mbDҦOQ cŏ`0Iet[%i=n3#Ͷ) epLѲ:,٬^$@8e9V60b; i|S6 q[L; 1aޯBNzN)'?4 eJu䗵c=)_ex-TG8{8_t8Fǩ8ΪkG}Sq@ƻzs/eVFa7쵯~/3ә#CL!T}/,HP|8ĈB7Lr3t\;>׳0͆ju+5C?a 3/Mµ},:Im}GJG Pp!KS̜jJL$LOi綠9K 2 R;[KP%*Ȥq;zC.x֢ڀ^ʰzn7{.H<=Ϻl EGk( 5»=/ ;VA .~?2vdcEib˶~{}n)lJxet,uh9u8vs3|̯hR(RP"gZh3Ƈ.Mr˄P֖빷LȕTۉ9&`~*rZ9Pk` kI 5*ͰD2*Ǐ%pkîABSȣm7Stlul_ +^vzq}33y5""~[q~_h'*ᢝOwsGdFB'N`67 ;;-*WWj\͗ ϯkAvUAV=Vz⼦P%ĭ fk{?='W7צ;]' jolHUI(9c&53c-phmJEp[ZsN qلyb7ZNN!{?N$PjIa3ˏgbZ V.ˑ֎y[:makզMs|~50hk#|_y X^lc2ɬfh@ ca<Ի/WZ][5_i!jxbpuil ,"$Z,?MR;YyJ;2gzΔLs/[UAд(^s{O6b-ha%$b^XRod}cF-FTk+x~Le5H$`15Pl^hr0Hu_$6_/>&54kQ  ZcUQ`*y pXڂ5)TӺҎ 4Tu06e0#pc`/Lqp1sv~ Ȇ~e[j*0)| Fs`:`7Y/SsOW'Y P~`?ͽJpzsQYP*HaW =Vkm/v`ȗ0FWV[ls[~>mG6g虴;uy(-҄.|$* 0j{8?~>'t891 o kN(2 GjGn\鰲#atUʸENVF Pv]»G7ќ_f)km $<ڑ߯@U;%zE_m, *󪰪uGyl_%xhhDPH82w6A dI<8$4VwRXAuTT[|)nw բI$*SN+Jz,bn  a6""X'Pv7@ *[H<ӫGܽÓZ>;l}}C=ppqM'=w]]R""b|llJ.e˯BOGwg^(W RG ;N{}4u=rt6%N"*yFi;…2^ S/9 `i껛Vt!ހ#'2> 3e}-!j1`}/E/BHʆI֥,,^_m \rĹ? H%@;Թd@L(YxZJ6r}S? [F^*Ed=cw-M++{"D}ΩnuUy::7s M.|ۂب{Fsr]tw[Yc `քtGV~xWGpTo*׉fE[{ Sv|ogMSIqЈ?NN;={X]* }x&{eb& ? NE/?"s ?=~ksy5b*8&k,X71 U[>8? |ߪF_>f &Lzv^r9r_Lm;^=HmvװUeN]Ws雲0k<IGШfZE<%gjc2ɼcSQjp_ewrO4*EvU*p٧')3TVru*֫*YX#5dZx6.-IOvlArc򹌜Fw蔌=FZEk2O-|ݺ6K@$j P_LaA*W8L=G%Y3uyn>MmmaЄ.@+[X5>$JѧMÚѬ׬_NfjKÞ^|JdThc]] 5>2՗B,,'+l m@ Zh6!@=𫨂O%? [}mba_`kw9ᖽ|c.a4D _%?\hoyTy)lxa7W4S_$aUw_5~)Z к˺Qi ~=SqBpm`"(vgO ~-z`n̅g Z⡙GT |%Q,9_}vl˝"}b3O.dh: /[wXo ݕ,Γʎ5'>qdoʲDb.;l$_?$nٸ3ӈ.NжDۅwi(`}Tƻwo9T1''ţ(Sz ɟk ׻l}gxV#e .vÛS'$H|A<د`$HeH{.jNKC$ oV`( / ,L#~*g,$XNQ,bfD#BdW/`AhR΀ ":9pqf;.c~Js^yR %:L!K8c$BR3Sj`lB]3{xcL~?p^RN64O6! (\g.q96Dow$GU}m7`6[ Lsl&i3mV~9}G"- /"pyF_+y=A&Ǖ34Õz_͗%!+c'͏DVnGmnJkJ4%@h 0 N7AKtr8`)Ʊ//4O~JRV{Y-"=xWWB\\/Z5ĊOOG<~r-r-[{k4pEíc j˧U:ʔ,Ϩ9z0Zaih306>;++r[[)L·UߜڜJ6hz>n 5o3l__9D#BҚg@=ֲ^Жb~7) }ZQiG0O B^C̏^F$rEn`1{\&>߫y~zY'fQ̬A{k9L ل.\q[ޑ~'㝳I҉stA,L"3-e,:L^z>zsc1 ϑ12߲pػx*72BdedfPABV!RLjd=+ιv?}ݺGqq?纮k;qgK0gdFPMgIh`Ys߭ - ,<%|.7+n?Sb ϱ]|;z` K2,%|R\Jﳢ 5>oک]hu^t M3= ϶^l݇cp.ܟxm/tk*I ?UhiW y rjP@nJR&_4-+:`/&fы 9z\w(lfkjgkҙd `p^%.-T5no0su,{ML>3LʒKshls}ro;|"y{ 2 8,d/~ ()["$w{T>L˩*Kh+Hs]SJtt[kA0}!}!GTp0N44Bug!o~Y q|,?M{\;rϰ*1Z,4a4M+zUKL#PQ6;M]L;l20&Xtv8.ʰ!kd}GVx\ˏg/LPR\Տ/26MLƮK5|̰Gu@دjD\.P-T &Zu^5yc)-Vq/vumCS9n)NomNJ{߼7{c!U>\y\EҝHhY{Y/C16MZ99B:xBm듖Z=l7;m~DL"Kl,Ýbح3l*<ʚʚreP:S:7T >C0?O zbިnn: Z7 jOӛr "㸗 ;۠пM ;rE<ѪԪDO_f1><1PSfް ~3*},y%W-)S:JԸ1,TW饨& L* +" ^R*Fe֙j}n?ȓp _3;sl^j~oγuI,eI +kB[}tO*oǬCƀ=A27l0RnccqO15 ZJw-yOk7!@RG$I+7!h >:.Dʦ ID]q5B/_}3Щ/7 ΅1JX&}NEsh˖D{XGy{o[^h|M.؏*kqKP֗93xo_* Tdci+u~?`v!R.Eet!sBmlV(Z^,yI)rKCL9*6 PxcfڎOiׁȩ|çq[JdVQsEQG-ӡ*GT2RE[?=WU'=Cq#2EvЬ1r|(3߲ϧ_0v̦< Zyk-d9\} O*G=DG, P"'|\6H3۸`c?{gF Õ$:i[?3ȕ|qWD璍lI&[y+塉 4M9.~\ ӧaxs2!.u銠N1p4L#jNm9 e[?ª3rRYݞF#),=pvګAx;^ jDK4:6uuԙ2];\ڤ@몎3xM`w;|rr/SB&B-Unu!s%@?k]uvMǨAzH/EZܦ6942V=UԘNSC"*,{lWaBfJ=GY&Sm8;VZWv#+\s/ꂧ4au⊟#~t^c̬j `k[cݳU 9ʑpgW XXqQi_8*{, oX6 ~3 Er]vC# ?#ќGq2a*O+q]ip~3~$, +%(eZëU_0@LubLF{,z}}l]bR~X]9"Βie:L:wPLpl Doǚ)VL۴)rHL _DiǢWb\zLA.8m0Sva<ur񠎃p_8"G×(~$Q<߾Md`4R7(msn^HuGUF@&2,o=oC޾BXbt9$qi)E@2֛CP9k`ame Z&-^?|W>9_?XfHk$ZF)` ǡ?&!؝) "NDMsO`;G+ɢ4/݋B1wdMӗ"oܢКvŒXhChb,ȴDgQN&*8&=+#&'rNj5'&>H m RIOZ׭+QSEC]ee(SBQHQuM{{*/o9ьb/rV0_3G׀|nw:=[Ply((yUR<|2U3avmY1l.ё ۍqUd9) mS-{47؏ٰH|-3GHQG|uA2^*Ĺ+E b&ѓ7I_;Netr3b|L!(;}20`4I9E,MC47;!nmFI` rZ*$(uH?!%i _xue21h+P6EI`镐v'MKۂ VJ; 0-F)nzJVxc-ۛI 5t=on0@ltwuv zDgtTېcO#uWԻ3}ٳr  kWR]:!|DQj V;x*QT&d9ԯ )*ﳙ*1 G{$^:0eC$^ 3 ?~ 10 $cmaΣ#eȞɾFdg"le !kB)L$KHf=ʞP~ќ9-xΙ?9=<C0Z-2H`'ˠ`o[^6*gyO^oEN͚dDU*=_ʧF~-u!Iqg^8Š#mkzGF;4\vViL#$M`/d0~#!V !rˁ?=6ؘɏ {YKMxi"pƉH vov¸}л6ĶvT-lIcl1ny3M{V]XoevIۆ&{dl&MZxn:%YLȬ=̵ms'+AuGu슊;<ul3\̔G\'ym,IXW"X3))iPc"a\ޮu_ ܂Xf/}̈́Pߕ7vlD91Wt\=wweg# Dbypσ'3B']^{t@Osl z3Q/*|c?׎ 4II='OEt5Fɪ.BHn kmw}dVIdX.ж3/BtBQD11lwHI%a'aKˆJ]||u[mPhu82_@J ݎH f N0o,,Ց.sD&&՜#ڧN ?UA`~:V{^ Fc+>^~.&d0M0i/^i&2gI:YJV O+\e}_~vS͗i_v1 My(G`?MXԞt?Dyi=cѮIF3#S[ejf\~m O1_w*ed=)V 4z/8=)(y)`i36+ {Hvؓp*GYM'`7'Iw{4Bb 2˵Xw<0bejТsx@]ﱓu̞,4kM"Dt! yOf WFF6܈1loS4HhXemwDؗgcŗڛ 7gK~;MBvsܲx*E!>|7<Nh 5Bj٬vy8 60 k.$,P03Cl}_̲'St+upQzi [|i6(L\3/r8Fg aWjPEܲJu~ϒ;n?;-w/vxZä<˸IU+ݷ7g%Ϣ6TR{lx`ޟE|E UW?to-}\F+)飌,t4>9)W ]a7|Ct%қMu3 S"pI2GSqL)O{Y .7TSu bi{ ojUՙK'}e].Ŵ0B'^g?_}wg֒Q#i`dkaU::+[zMDIDI"K ZbDi ʎzr1ђR,Y'mQ33p8  CrJNߎtڥUx&/9/ DܭܭR59@ vpfطs~([C ai&Wdm]4IW o]5DZY36;o]@ZNJNO]H}]2![ާ  Ȩa֙Pac|4c˰g/Rz_xÉs5zxt0cM/LeҞq^zq:`4`> c?Q&xAߤY2@Br&p5 M]cm0ԗ(cYEfK1"/Q"꽒4要vqZ*&`/L^Jz91شWta}on*/TEkp?+ڦlhO13&[/yIgIn|釫+jAuC=xc:6cgs}1a%OnY:h$qmzth3cFS'#Jʷ0%DUMfQnPם$jqq,:Ȇتq K&y cCX4ҪL4owYw߹PݨG8Xr<@M`#QL޳!kѷF(xŞOTSVv;t~"X!lD#ԡԦJԦF \=Q=aޜgAY|pv״a6a0Z#D6675ɿnzݴ7%vIIO̟ 7)i0ZC SoKpI{Qе[}lIji{r:.xFƻ*)׆ɯ:-1w3%k2'Wiaoq *S2Oԛ,mޢ1T؉# N*5fE7ZqTY9si CY /z*vW<$gn/k\3S{8?_igبE} 2dE̅CC  n s>#FsGz!a`DfVۍ_FFwbNɽւL$$geNRlF43x}!dN]]D))œC ގ= Ab;Kq umVU QzAzq8wtt G!;rE"Q"K` ipp6~-*W n5Bs8:ɰ)dz\?wF5bPu@E-`WRa,mZelȚ6W?E)1C3c6\bOp0~iE? ;6c'&,y i*9G44F(6C>g\Wl@{0GzQW.xmOBWz(`b.;dpKd\/>n}dLp&<'ݘQ `7k5v:ٛYtr`[@H5`!lV`騬ceh T9No7ʱ*NM`NRY7z DyP(:1JVFѪTP0aD`[Oǩ!3ӣ2L^ lQAF Bl;؁ }4jF%* i3^&\Q"/9mAR^UPHV3\ ձ  ,ښP+lfphLmbc>v smDž l 7x`=_wՒ}v9g=}ZfǎH^8aThbRi1zctNG$7FXẌ́4tW[ۃG׃,zCg0:TTY50KboȺ7'3:y"y3+JOb>X^~lu+챽nڎVQ'Y)Hdbz W#G;6nѠ;%{6X'Nz-괡)j{;؏KS1.N1P rSszvh >:znuV%Ⱥ ewxVxi@ؚa-e<.{5W1IorE&}EA;(B_!0M}:@9`ЪCzQd0j[i״N/K򮪣>;]Trt>yN\؏KXBJ"NϰJ ŦBݒL=`h$M^[;q#y9f>nC''ɜ&TLG٩;2]VKNxE7MB#,mFMn;[yX3[S[[ps{ϖj^ǽSewn9`?9 k.xeo=±?ٌy4ARO oZ3h;=>e8UsUԍ\1o/wu"6{O}b߾N "V X;WzM>S[ϥ Z ~ ,ŹMd$W~3ۑGSgz%YHa* iKE82xRدgExKWHZ`cSڪ`T֞ttvp5 9ڜBcNz%ZJOd4s7IQFsmƾiw0*W~T9:xzb5E<دgidi~8k3-T|˖hh#_E6j6E<WYy* vŏ+? Byo`%:펜,0oB$\2Pⲟ꨹ᶤkϧrLn0xϯ d چ:q7+g|[F vK\_)3 bFRe s;RR!t,tPst Y,t]Ji. f<>ɭ´ 5L3)J܏ GWQUQUÇс %4TdUdDtWz|Ůxt0e\ 5`9ߎLEkvC iw`' - 83y v*OQCsA !௛6:'tf&E6E6_~%;p~hEd섯fkc{+٨d+kDYS˸Sf; H Pmi3 kJjwu|OSR+x1CRp}WZȳG\NdGnn\X"jI6 YnBU] |iIPvC ӕ*@aC{G BXV,|AgŝxD@p$sA[`Bq~,{szV\ M?sv0[*00mPL·h⪸. 05u+[U2:,F1KC?ۙ.J' )iK\ a|pY|CrݼXc5f`~nCeR !> ^MM\/v+8u0{]PyUYTCՠڗ$&s{=\0Hҕ'y VO]t^Ox@6eMk 9dQ ``h=&g^'msTyͤlN(=HeCcL٧=Ⱥ8fM^x\tסZmcm>4|SQg[ 7ۏ&gޜd$ v;)L8 f iYb>6lƯ>ە.3lqt޷Lr^=`) 2z-vT}HGeg٠C Bb s1JK֒ E ; w,tzp 1&#EM3>>p8[)L'h?-ױ1.._w?vP /k5>r]+G_ 7[wN{}wdO7AYgFK%LICל`l5"^ؿi'Lʦڐ!/GRD?P p]Gdߵ#_( erF=aK,~I$ _Vhyjc,gۮ6@ɟsbuEhqZL`Tel7`8ԗy[˫Znnjed_X$ q4V\ت{jGb_'1 XX_M|(jzȳcHbGZ}"l+vOcAw߻ Jy"2V:l6G{Jm|j|pN]ӗ,wM'Txk|i3Q)ƆyzȤ@i TN4)Ք#]? |[v0OKMhVdr_h?w4dkfTvȞIH3EY!${+3;B{|~{wV|ys 8|a ҊJ_FNKZ~AO"CF) 8Ġl/}lElecɣ{V(հ2mSu1w:G/k:%4+vlly͕>Sohш] ]G{ hWmaQBk}@%Y)a0 6,専0헆q,:;4:ͺQpX||ħE1+W -@C hnӜ/Gejs%лECAGE0g7МUymRqtyvM/ ]ZNF"^M59;0hn=g_7,@⾡] bޓ49vruK5lZcJʴuex6/ lg ur&1oجfZ$xkٲH_-bö>SP4HIO,1a*='$t Ѭ mE[yT͍$LUf󕺅g}eF3(xxL~ n39sx׵)T'LK"f8oƠe=8ǻyyJ`0Xv۲Jx+{1 lNR&dR(@ A$dW0 0-5K ^Яח˦WA+kLb\.p+>>" w2^ɍ+C5 2$^v6ӤzSBSW('c~Ĺs2h.ꆢ$Ϳi{aI>(`ek] cLZ4pSDG=-I %eO(I[MPw*f .ӀE L1{Mßm_$F v63tVpa=w nh8Qҭg[nR@II)sĠUBm%1͏>iSݶ[[ha eB\3㤿~RfN7Ԡ,(B$=1+ܓד-+o79>9-Xs2b[=诜0HYqM+e[odT+ePORONvvZ1@;WW ?S&祰wS7{8Ξ=l()eJ'6j2<\a hߞeFLyp %_Kc؉ iP̣^wtiץ(vқVl #kC#< r/y+*7gBAE/Z]4}+Y IzV3xY1"I+!b%4;J$a9S=OԱp]8b3<' cL+PtM>^GS~ڙvwwg2AU㹭`ŠP[o#"%&bm7 ֘C ;=w ␭p7 9,AC]1u U^n.ϣR+O1r߆?ߛ|X^\q==`P BdWP ;x?$ .ȡ]*j1z|`oRKx ep$ =QDE񈷑[Rg'`W3O+p* 7T 1w(C·{ȡ6?0~S=o1;9/-pTOv %|zRj ^y"gWC4n&S'[^z-bmDu6:X)NEQx!A` vFS"M5_bniD\ 2[8fYȻRvWث֌5n d7wse-c%CgovgCvm>o/e}[y~ O5S߫Z{NetpB(4h1+Z1E)HR҅ECe]8O E Xd,`PxӈZ#o#Q;,OɣG) 2m))㌠<fNN47s տ纞:PMT^TF<_w||l K{٢GhA^@߷: 1 CˤQxؾ7;+C)3 `+[5_ ⦆-U`A\;g[ȰyRe˛X[۷_ "}g*M&>lA2-gnXryHrI-(G2*qX/NyQQ#|3RJ JN !Ou Փ#1HB+%$F}~mW `?ҦcQa7qZk(phIa Yer 0~#ysV8:itNQk^ԩ<ORcWPB01e9! 4udQ+O􌷑z jw53Dpŧ3:W\e2L>첮-gH* i%zMq&h6ݹRi*dP#\ܜ<B?wC~i:^.}FhL{! ettrW }7ӎvns5{34w+oݝ3f.LU=wճJ< "}pjk9Qkd8;{ s5 W6#BTGl`UmJȄWYΜVg%ƊOQnav(p- Rp27S/ M]qYVjuϵ\(~MZدmomuί^N|FvRҲ1C L|QRuRFtRuoXh;h\`{0؏t sM6'&j #QVBr8!Ƒw~TF07S^$/Hq2l7|_ kmB+Z+:/0+3,3B66[-48h6-Q)oEq \"Iof0,IQXjf s:4 DDy\RRdi%X&-&-..nJ.0P?m~l %E8tA^U;IhgVVʼnJw}g:2l1l*11kѢVp;_`вjk&ly ;!փNۛk+'HG4qݫXg|\C^ؠZ{H,zexW&Mz6eE{Eۛ_ "}'wwzOػϱ(&^Jb\ʽRd=ng:J f :r1 ˘NdLL<C,s`ދüXJ}4iZgYb" LE6xy{3ŀ!5h9 2[oQUcuUKH=}CIW!~_1^Bs~S+Hν#C쩼,mk~?+{@/;|q6?AeɛTg/dC4 N5T fxFAu^@ tP^ڢȆWaؿ7G aƑ0&޵wT*{˨>.du2RdoeM!;{B%#snuu}\oWRd $dJZ[>xĔJ#y^•SbR6=Fvz 8o ]'=:;7).|,iΟack $#9yuzz)TE{'e {]S:;:rIuW&#.SD*zONo埁25P P\ FU.'A;>p0ח(A9"GTGE;ThO|'Q0a 8ˉ2Ĕ1n,j[ lKG+/~ cQX>?z6a+.\BjNKP`͏ǹؿv8#KE~ع1=x0FU<'q53Yӣ۩sԴZ\42TPx#ay*1GgYmcEhhkЗ_e{L!GŝM(0"ٔPD88 &JuBqARMSӪ$It:xfuBXm$>\G" $#z~YE8n'N/aR“OO讄DD`jjQQ7 tvZr;ۤ?S"v`7* McUAIYl&%rWFL[:5K./G^wl`:հߺڴ[ USOƉsp{sǤ,w$U ֫}cw 5:_l"d[I7׈!} _w#9wlc2h>jMo@Km||цnmҼǻDh=͖'bL;Sp)\Ow-!!k/Fr!PB  9i3V>O%a_S^7<{TNx$ و~_JjƚOzzq>1Q&XpQZRF% QŔY< >K,zK;MwO|onҞ .OV:K@U$?OV{,~;Ą + ^BFuD>tj1V+GIQpDvZ=آbdg&6Iً(f*-I;7 nj{cΔɂBHkuAbsjԦѬmyDԍ[g ɀS!o`U`ΓUoB,;,&GtAܴ}H{-* A n%w͊|, kT/g: ~hZ~Cj-NzG5 vXD7En3Rl1x)-m?`4B\RKU<1<\G՝z²SeIaӴ=/>Wr莨3d(4m8KkV Rͦg4R ypNT1Mq层 *` x:<&O5}r²#.0-n4XcŀvЬ%CI;212wJ^cosjf3Lw:7A,xXobsu׆fTǬ6wtk ~OiT:R2E\4(9]B<0h0ؿz_:byPzs:A8Bb>¸IZW^7v_о2[c`)Ĩ K4OͰj8N|iCٺݣ~{Ovr/52 foIc4\ʧUsK``~Jq͒kHn lQ{GVH) 5iKR,Z&H!7X+gBy"q_M\`Y6Gfe;TEp IˬpX;Thi,4*`@.\"xzݝOl JcX!)"nԑ j B;2уOAm֠H)LkNо:nr2(Qֆ]|``_>3-i%zNtŦ*oŵ3$w6B޶aKQP$ (BnnLCs^rQ;xeYvp{k.>>wm{l=EhQxnrm88D.[+^+,bgeqs?'JLxOvM}1iijmv ɇʙbO'5>$d?JXiR؏($D' X|IA+(7}-`Z<r:Ca$Ty-t3|r%A>+`4{AP`kQHsxZW>L-uP`\:ҩ%SUMcOTG<(؉ԏZ+9kavICܥX+ۃnynh{~-\+C~>f;.?2zhFT#GtGGe\=3 \~Wd,d,ubbBY?8 q@".NTe.q)U?D?DRb>GH] B][ .*<*<'i#.XX'q%qAʞ}A݃IoZAqLe-J7EɲcYw~PL Adx !9D<VKoھ[N[t/={iHZr{&ڞ}9G;o㴨n+7)zMnrQX<%/~A|?=׊U0̔t.ⵙh>Ӑ;jNbu~>tl/]'3qZfB 3 7H+Нl*Wlۯ_9\='C?@1#T|D~ղˆF|Iӛ&`&hQF kMu8a%Cj&1Z圸l`mmLOَg5bJ|rS*q/,V-+BS,j,joD(`b/6E-%YXrV0+&!I`#iꦻo %'ff=MHqSESE67lnHKf["q3R7MIm7$YTo!ovͫ'Y 7ĝpQǂM{Ӫ~22QQ1&&_I“Y/nh. - EnOWIZ^oED>?Ӝ uM;; nTL8eX^c];S{qU2E|߄‰*.2NncS4^o+4N} 9;So/Hql_rx/h8e&&)tHtJr YG-64HYVQ;Ѝ ?FhgYGl`_4qF}o6HQ?Kx&˂o]Dﱷ J $`>[ozq5 H7'ҟ٦e_a`IR&m1#+ l?k%0(-JN\n[zbLJuhLyZ,h](}w^&2YZ:aZHѼ؀ƻhLRYghZtulwbmĜnBZNsgwS~$6dV\+0-ɏH:cwmVSR iƴ~e̻}-p߯mP_`h^P'7 qC{嬳==~nI{Ra+!}N5|zB>1R(ǜZZh[rD|_?Y^Z^]v6{ q-n>Gxm[X(c.#GGl@ۢhi8Z>x+VHTe I/ _m66Ԓ/MBpXr י綰k R %l p#[ [b6ЦSG$H$HT81 C )+K1ml(R$vJXavQg'My'mn%Uf Xb6 "P2m)n 3.4su]kU-/.vBϵ2!O3}Rrñ괸a9*~L"#[m e9-NKunz֯ҔfWiM(%gk5mjĦDyaú\w'팉Ϯa >$-D牆| &e>WSB}$LZU@ٺV vnquKq$DPPX|H|$hߖxD&yZZJ )IwIޚ׭m%S)|ጔKmp-n;mX>)jOGN`w)S$ZV#JKddWpq^DDs¿$ؼ]&>xؑ%BvBEeqR~-?)%trK4Fyj?ġOy!b>( ?07p} u PAYs4hG }J5c~f]S`ڰYY<ɚxIaibz`?NEyQSJ?xueϴCp[mO,JzйJfEjt]S8t5J)4Wdg3B.Oڀmw7eNe439ah^*kKs/_P\ 6-cMi(XNQACBt5]7'/v[k `vBKwr7;kMSoÙG!Gߢ:΅d{UN3-'i.$="7f#WFUa6$ʅt$GPMDgq%((c#~c@8A `XFf/Uoyq龊:*JWƧ:L\f@XKEbox  Cq.?T#۬TT/yi'`%oFRV- z/ #%;1;3#lT~EcF.Q?roAR؍;j꫎'攳`}S?HFȖ="{f̌l!8Bdd=De^Bf2pTWyz<|0W[nHI-l86vI3n8.V:iH fa0\QFCndN!!THyz12mAEKʫ oVԩqu8$ve ȍ΍NH][RZw|=PGoEHKTV?R1#4m B붫/M䥝–08"'I3nL l>|E(MI07)kN Mf Ÿ~bwn*Gh'erN55zYt,\m q"MT^S BbXhAgHӭpKG\&GWYyggKАI=9Cq Of2ggD\O;Swq+V ^95Bӻ~V,k"BjiRRCgsxP3rl^mvҜչD4zAXu x97= TtؾOsswe2}IFvlZPk)]lq1UHJ Nں^mi꨼U4$.y]krjM1jʼn zT7;4,KGQEOgt'mק@t딿P 6m"~Ǿ`\sPUgQը'/Ye9@-σ l?B;ZL0&y|AiJy]&?Iu`$nZK mhe)zƂ7 9T%v(1^ h܇Rc7GY4{ %Is038nE `jZM{+:]ʣejKx&L񤳿dU_`uk[3^A4N%N)+) @{iםyT(%^}ie8&Yi drzՆE]GࠍWl07Eɧ'O^,;y5l˄ T};r"MMehkGX#WvxY-vݎp-""6 VW6}lvS<9`iwB/(:E V⃰Y@~f K64.F"*8*xM3= d7mlHYn& ݹh* mY_xҎ>-򵽟?s{0{axP6WITKg(9(u}X_[7գ4|;(DS᪋){.Qw>nGhAx,;ʌO>9zv'yzn~!-FPb_9C rśFƴ S[ok9(i-Mh7B~~8Ry5=gJfIZ.i1+\ ~=!b-dPE xH}-g=vr3f }.R{&"ZD&Eà5:slύ8y=йlJǒg¾0=Yu˥{%e?wWk&Tg4_j5Zټ5 hOpy̦&Q5]"Doc՜ٷekLå3\D5M=P>+weWkv1dIɦFW*7Sn9/܅;=﹡v(WͩFҴ a;U*~\=~V(|]*-Pi^ӫXCf_8J\`~k"f Nɐd禕V! S)uP{۷Tcbb͆]˄7Xw*;<7F^ qnȆ?1p~HZ!)g(t(b>$]]DXaIfI@@@6z'xG5lala#j9"f:1@Yv) 'wM $B`55]dSduVVH03~?W\mۑpoc-Gn|/]4g==ˁϰY'2yy7czz2KII ==ϭ럲tٍ_7ל \G{ggq_*#˻Xj=зA^y`1[0~ _C<@KxܕG\/\:qXyl^;YDz1Hs-~BQTjHP=VZ l@k+x"kUOqi bD?W1ꃲIQvy;tN1zDւb!ݵE܁BR}+* 5`p?NqXڸDw&٦% T$( l?jyaQO,V'iNQZ)b96?eOâd+G-$>nmx9T9u+V`/~ ] ۄYcSm ܙ؇]K@ꆿl ^sS(ʑTdz^ҀPGۗޗ;?p8*2s]{FD&3;"lKV$e]3nFFY.$Bfힸ W89>|,>L#={\))tz 8ZrDJ@10"2"ʌ^7A'y$ZD(eĈa֛%WQ$ RnlϹÔk1|z$Q !t$호#BuG/ʹ'0L}!򤠵}:;S\\#2϶&i+ەy23םd[-I iGĎԦq_݇gMFr,;6(M(E44C,}yxtw6!z\;;K9;EwbK N^Oۀ>^bN5Ikc:NP}l3ᵂGs6.j+xv{`4Q~b=L1[!WR>O5!i|LgLdM쵧 dG,6 :KG}!xNє+]j[5xjꊟۺPM`9 μ㦘[稼9*Ϗ~PU( nf3xf}'\sv)m܀T˵'+-GqYo2?{8`W,cR]cQ8KW6S,^ LV `*o& AT8C"J8!C֝\kkp-__!{7Q@K)+83CM9u@D.a.a4L]N6w 5XOG|ڤOTqOOV0;fe5}R$Τ;ymz8uoS,S9%ˮm?m EO{Zx$Zj/E yRᔂN@Dm a_UL!A?c 1^qGv<?feggFZVΑ%7F%9Tʚ~0Eu]BSgmwo{3FK*UMwlw7+MujE֌0Gs^qMWs [,aoCp N4B=46H#/"0 |=ׅ)Y}:s 8Q 鯿y#IBN˿^- Gm84G27B8:^ iߣJ=w[Iyl)G׿]CgxgRz[Ȱl`7VcjJ't# i:y[4Xuwc zmwe )T ]/iĕ+442'NZq>9l59qKcɴ[T7O]k>S )Ke!&w[D ]>R CVa7 Ȇ(%v^^7 l='bu*~O!EXwr7t3рvtZ!Z&ضs{\qC/dPlwX>[1~B `Grd dR" 뤕OSx%ŲŲAսq6R,;(R"Alyz w_Ğ U| (,o܍ll’g*~VV_#7QA?!zk3#]F ƙiBdrJRrv||EP;n*nJC˸q*Ziy!ޭŶK9 = E.$!w˲Ɨ΄G; /L=-)m PdA+.*]RXXqT,bAWT|Ha2#=/::^d=>" {FfӜJ5C}%VGKftAx݈B+{yZf" PLErq7v)#Nc ^,oS GJ/U7. U''OJP<Ê(f~.B%B%ğJ Đo|cDQ HMx ?!%TLw,vOwOkk>g>7¹Q~Ͻm۷33ϩsxZԇ{]iN+ 5IZ1/0PpQ3|99U(^nu钐χiN~/? ;-"ښiy2T.sPB!7+8QzȽ; ߸>۟Dct6+.Д=[WD$+M5q0ThJ ;_\honKi~'qA^[/Y*:Ym6Z;s;oPΎ~: J&ZOvk]DK{5Ѷ=לKM߆ys菼J=,f$RwCH{zӥlfHkZ"QJOv5_MFupKwdUJr9wdŠUpC$q\w&c|$fZ6w9\dc 2ߑR&Ü;rė\0 >-I2I?iKؼ9{ܥT&Us8Dᣟɔ+ 9 l6Pk@fH:FZy2 Ź4:TLTR.xDF m['"[/װdbq8̐5.&Ǘ:p4'_ *Z t1ǻ3¬ښnk+ i fPt$\#wu_us6=5|;<3TiK+;a\ekXU;;|{;ަg!\ jX S  " 3 hY0qι9bmMY۞صiBN~8a]mϭ~E EMU7o_LL+N.ߞ8@dd..|&sTTt_v1<:eʚ#dY(gEE9ZO ˙{ G$v>$c*ɒsu}Raa6MpkFJat417=,JU(_j G5NeO| \P@]Ue}¢L(kg9~^;hSI)=߷,;'ݎr)/ 5\JJ"m>TMnQNƧ~IdSI~FϩeM8h bDե >'e WŷO{ػ/mv,A] +.~JI&o<jVt~ а%0?x07xkGrG4ޜlg;yNUz@Q،m6`E^w$N5O銜F&Om6]ۿ{L’)U; gKaTÊhZ5n4>˻ aLC\uh xGGpKSd|ġ<<".`޵2xc:aL4+Hu{Iue _ XhZ9a_-7= a3qv{nA0o81*r ^U֖ոf /0eFlOJ7%n ' |^\PGo>3Q Q)9\;Huoo[ u|MTgoL#R RNL[Ɲ3^Q0Aç-͙ 1dXO'xVџC#5s !gٛ v~_3l3Qc>KQYgf\`fs+O#ȼe9 ClMzU=PR|n!'$ {ӜP͍/]\1sk6!}M;z(*r~spX8l2|>O_%}1=ֈ&C5I"t **Tqw, K s{*,Kxa[,+Q`⫢ &.B29[OKfb?{T`}8ll`شR2K6u~U%π.fMpL u_ 0"ވ\U!Z{HI1nxhg2_V֣H pЙYxJ/` l6W-PAIX&z 2 x` =LL' uDDF$/s cd2i+TQЭ S|{ ;!ek1z^iz p.m#vuCIr7;KϿ zʉB"+yųbw;. }3 O0w9ڮ)W3Bn6w,<\)%bq8ɪ۟>IxbBw%IťaMk~q֞#EuΝ~f55@w _@ 戔hbI͹_{1Fλp l`QM)"gzKCe;dPhu'DvqԒ\~6Po }ňV$MG׃j0`w8$UK-'=oKqf*BZH}wsnoEr|&}\ `g,u^~ Mբ[IQˣ/!VͦmbLb.+FXۣ<<{w(Y`z$YayftxL[*+vx`\}\}N N L&u,u|%&j@ IǒkP hxAX6Xc;]H{.ş[=t\& & xxqv22 GA`P#]OOa,÷ i(}m=iX8Oi}#ٴrqǟWS=+b&}iIVwR'Vy$~t8T+0s5?&3Rq>wڬX|iΕZ7ۣ?rNaah#ER"=0CrMiK{T˿ss }85fcDm!d(}$H__{CRgJz7x|>wRؒ,,7f:-P@yGG竱Ʃ7zʕX_gFUhw\_/X(:$+ժvUzdߢO1>.?Ph6d*He9snCxC)RUFk@k\wyY;^; s=IV>%><ƞ#Vl恻ˠB4ЦUV`VDE.F4C` .U3BQE%Xш!Kw0vIF6QM|)kyѹ2RXOSANG%uFۏi ӝ'><*uY}L")IS,eRn; IZ/8^t`Sz05Cifާ7NH[bw'L "y(zX}=٬yaCBr |/= Ɠ% 鄿LX4R#f{ 1݈ fzw 9U<<[+]CN3|Vy XX  \Re xKcf+U2e`+^U0),̩SiYޫ&GG@@FKrd :rvojvsf6yg:q}WO0vwҴ[3nLϋ-^vsFsFBB%WcoOYsc1l¼ޒF[fmm$=$n-Sq7/XdFl]?w][t0lyyV"lo۩}1[dL( $k#fhe\M h ƨ8aI'rCL BU{lgҜwX/+[F6=o#!E6 EVvF2BF^!~\+WCsߪy%`_mND ]O Úrް&cc9g]Õ@ LJs9A2J)vzryxS*(`?m^Y:<@;H)8k .\[5,& ev('$Tsb?!vN:cJC4BNs!,a +_[3"5B_]j~} 蘚e1JN_Ci1X_ )y:h0Y{=#=9cn) 4 P/C1k6mNx)cIj6N4f鳬 SUQ=߯eύ+0%5ZrGY!hTyz6vꅮDw(`c}[> RSPn ^ZdmՆX όZ\` $\P+NJ:HIc` n []Z}ZCvx^|UH"6ErxI [8~wl:|CԩY{GjuMn֐q4Υ2Ijqf"g5R88 Pe$B"ooe>p, `j BJ*E uC_L0(ppT""Fo>N>÷ʹp}Y-Y 3ؠG&&KѝOpf̥<<1 [}ݨݛאB!)K,i Ư?c F{\{29w=qc7ũ5{=S!(~C]ug(՟1Ꮺc?qg*T <\@DZDcg*|O<8KP!qW^ 0Lw7Ì;O-[0l?gioW9R3G{yr QN{PK%:U7W,\.OÏIeihD}csmejdy2"qZį6!=Bkh`۷h:W] ˍ >7֚IP*Kq:s6Z̭k~e(?&53|ьݖWy>l`hJM,U:֠xݤp(cI{<%kpZ.Wv"61焢 gI[r:=}lAͩ˄V1 -Kߝ#Җqc{k<Dc/+Ƃؖ6~>/X=%cw{(y&{.WJ3kGL#cܪ*BABuB_tq3(=_KzRT oug?u8v ++hRQ5B͢pxݺ%2BXTƫW2jeKdK4HH!}F[IK#V8v-^qh|yO.8iZ켎#P<#88NHۄ!dž ]nf|x8^e_ JrhVQrO4aն밎, s~> Q_yKͿK-*bqge^c>^Z6uzpQ ;TKd?a4,g0.[S(`$^&:yY\(i;?CXB:w>(mm`?m#"׉¤fFm&b%<:{\kA}l`9IbV1+&2b5+;)UDYog H]4e'$io3F6>\BPV%5򀔆l`ډ*]ٴe3OGՆ/Q_$$z"Nݞn7ĽkhNj3L?;U8U/myy1n|_:orH? ά_“+\8Yr?Rp^ß1\1\nmgqdq^d^kvl!ϭNB"d8_u}242Mbzz86^' .s+Ȟ=jЬF[%uO[!e?3J]F!|Q*MW[xy(e1IWԌdk ⾩ $h-"E=DŝߡotF /4hwBW&I;7JTh$ķ}|$!l`om|Ma-Hp>eE%-{;(-$cyԐ : beK!Z2W;AMp%Hcl`/xk:F^gD#z^bg"OR,ڋ xCQLQԨĢrW)l96{>EƤ"SRyK(l`ZರҎ包Cbth$KڵT[|D؀ƃ5{ccL=ci=*.6Ր%e]ھV|KҸ7Si|z¸͐7;xK&?o|0O)M } щY{Z7׭Jo,)~=+ j)jހp2C G GO/CTxx4eq~cz3p 6[] iJiJ4^)b)b**lLmfsӻ6R6&&>n^,Sk6|osL~6aB?׉uswWsrK0Q9Be:A+=4~@?*)-YdMML#{1v EM-Ws>#W@=>h i"L,[}$CRfdWq>%Zkx]~j uWxNsۯ,[xۢ_4\T ?ܦ$YΓy{v`ZM5k SQ= hˍ>螯rWTՔ^Fz lf0+iN%cI:C')JwFѺ^Pm oiFY_7<_%yΔ.4m V(-TNкל)x- ˔ZRODdҝ湰FKSCn26T7{?pJܢ왌2BV=e+7%+dDȊT$2$ vwJ|~Gu=9oxe!q9swQJƣRfX;͙3÷UfV|*f/ uhE?9\sԷ>PG/_2`~e Pc$=VMҞg ͟?񹙮SQ*C:]&Rh~ !jRZu@knk-OT":":z9z9W ~NcJc*kD!hwbEL}8pMaC{ :!}c~~v9 ALT.iiks ame X"ffG$Bz]H! i΄w6$k/4@Nn}JD5Sao1; +lf@{ OKHWy |l6ԇs"Kqg2Z&M"?S% a9!7=OmcnbpKBjE9gХ⯉{K=XLwس?17Om#ljx(>P>M7;wh%-^B ){* lO?*\{*QhRRPbz #@;hic8UՍDKm6 ׼gFRV=eTˬ~FXG)NY3uu)UhiH 䵸sTpTl/5<9u ,GLgZho|W;!JBu3Bia8|ccdcG11J{NNg**247S(>!ۮsP)%uzR2~:#)<$`^j^ rߊX+& 0]^_ 9_jF"G *C^<9hfPz?sD8=s|0eۄ,/-)r;QQ} )] u4k =*'bʱQ\/;4w2p]PYmf4tV5R|lv!#"G d dg7n{ϽnnDV?̲tT+~He{d:y#GN )U"0Ȫ1'͍aGKH-^@es"xFzڄp"0 ߕ ;R^ʿ-M;HHֺَԺ{k|+Veq<m 0u'Ճ$hWCcztS͞U119=Ԗ27C~E) )Y-VKMnۿ}T?lJiZ-aҌG)QiGct]l*^{>r:d&XbwU :{ `zB б;wh+Z&7K@ GJtF F 85VtˏmmH=L{d{L0KIHBo_{u"͞RCx GdF/GGBC O 쭱$$$ ll/WΘ*Ew2&zO#rU;&.(dxKQ;9- `Pw[݇SGGI{Hμ DB]s]6aR:ke~oO3dyp5̿c:Ws`_m009,{C`k#X3fMd p/ :63c^uʮ,<,xij)QM ztٸ#N)>zz.#4R⪶v*֣̅FL)^nѪYMܣ(68[H"\e[D005.E6QRm&MTr0Kg߂r(UzT5>/0%6#}4r,Q-aSW셑_Isͩv|na8fI*Bܘj%ߜ^jKO[o$yf=>syI=nҚ\\8,c$&vdd4TPY]nxF1zP>9<y2%qb 5`]-= u('xM"Q=ay{ojKrV 0ӢR[n\e|om: U{,a[ؑz9v&ngҏK,Xie|8#=.GM {%$$pgF~B@$D)?s݊WjHUKhbuQXI7>N EU.)ɚsBeⷲ \ zŪ ʥT,kak VQ#t͵׌buX[vnաLƇm@yY0Jt\w- l@iEPU%'u8"CZˀ&eSTKkW l-?y9q6+-E{ l& /< "Oh>c|}x&]N ׼<9AZ~ƀl`m 4%7bT~It1W1/.O ;8>B!0}YBY%BH-CY cRd߲{$K֤BybJ3ޗs]3u3η!=K@6 B ADYhL>l"7&a xvM§ܚAeÌp6C_sn5}dohƵGq^Bͭ }VGV/bsm3_4IQ411a7`˱Xrql|'o |#So"dEFr0NFc33aqX/d) ,Gj+Bae2snueP{UJRvM8P6`1e6ʝԕt9):=o(VD"9?:{Av l?[[խZ>}7hu r!mCkڽ T{!tf]%`}Q$٨!I0^z!3G37i,^D7&G3 `l`ͤ-'~,sl/qq {Ny.?wwn Og 3R<3M喠ZyOU,j*=~'`ZFMoaVS N-%/6rL1 {;_[Ur'0ͅX2>oWgxHpQF+mFZ>~!!ⴁE ~%|e)*jO#oJ5۟״) *5x!{T {Lr@3qwUW l?[Jqk ryf1˲0:Z.xDډZW`3o񵸅Sp9c|۾vkɰ')>? #'e]1k"> ?hUY߳]5 hNpckf^ qcg:m9ވ!/m#3a`An4yi>-{-ֆE.9(τȱxݓ)S?ʥo?VWa Wk^Fw1^Z&TƁ) ~v=0L,#bfK=܇[=D(ꊶH#}1*6&QkоL~%{qy sΔze@<&PL=l9aQ P tZ6(\MpLpx6gص5&@4Pj!V/c=< !TG,m#mÙǙݛ<'='E|}~f.eז"(M!quNR8lyNKOU|'{HKt;8Ta\G"\=>}b@n[ ?C_Oq鲎ROt, 7C3a^??X $a X<>Qo}R>,hØK`55zw4iӌN75UTsaHM0ayMqwЁ0Nћ,gEWXjT;=d؀ЛY/oUFN?nX/W`_*v&h}$q\dݯ>[PWb֏c)&} sQ)9|lw#G" _b#EJ4X#%1Tʹ`ۯzJ9pTN!-"R"HPi15pl`_4qV\bmP="^C20jF5"O l`_0*ǹsgc,U A͝%WhP"4,՘WJfil,,l&lF  " 03{ZVKi^X+*ݶ ShLhn(R"vY'll;ъ-^yzeElE,S;;߹}(Dc(Ӭ1ßigs/P{V{CktWW@֖0uwp;'l&UG8)̣Qc۫w6')l7~HURu ;ǚPUP%r)lI3^̃g̃ubp?"{Dܕre5*+k䏃/)Yt~.R(*pN\w^ V$c.8a0G]E,,vR,ߖtIiǪ )/`dFr-wdW\#(7^KlVWzYvZ8xNj4km )M9[6Q?lKS}`Ji}%qIR=LwAU}IiPAPJ$TBA$.H )*Ҩt4"s8s߹wwfg߳g2אAўh|Y 9(&oflt>_E\X5ꘚq~L5%u˲k/7- -Hp.M/MO04F$֛HoC@!VV?`58uф8)ރN^o y5ԕ4FyyK6(<< {xj!bNnB h[m""U;U{r(nMLgGfG4uO55aƆTT8m/B wH/,y=֌v?h{]FnK5F~+ulEnퟶ#2Znez6Yt}$?*մNo঳ƍ>W6Ňvd$"q\#]7T< H=1J# 㮒K>W}D:E@n+YƢ6xM& kT=熍lW1Wm_lQH>[GDpBReHt)xퟶ^G4eV$]hJA^+30o̅{.䬦ΓfSU匞Džq\H5>юLškj'\*]vl{l#$5YٯU=zs>J,J]=`+@@S6")/Y]-luGJ8$"!y@īWQ`UeH ..;<_vł| Z5 V3kBs2ik`;\m9:!%s 1|Dj}CfRe*䓗Qvi}&cI٨WuO&\=:u:OyvOe4lq@g;S>#bYX1rL) -j-*S}{OE&Uv΢_:@A{*לO@^+P}v&&K"Y(_*s}-550sg)[ @g1IM@`9ZB1.?3mLP*c73atR:kGDEDE)S8WAYYUTnZ>~[glQG p DKc !ۤ /GS..zY8\3ܓܓ0l||`11bb4\vuױ@YO꽽] |Dzpq湯]9)qLHyN걞 Ue^"\1R;]îȎ>D5v ܌U$>Ѥ9.Y3 7K{8JUjQMIAp4Y+L.vڃE~D1S#-ޭ7myv*`;p"Qb lk o#*eSy/ܙQm1GCпbFN#s;`=%ʧW>8nEIUyPnS F]t-oH,iÝ'B]狘@S\~;i:Nb9:WdASѯxPduiƙ[|A]Nvkz`;\M_kYLà`E6R㠄hk{O#L4hǺR<"\E0T ="%Z7ߺF\PsN1ާ:yl^o l@krl79++K2Nlr4`?+(NfHN5n#xv۪X \:-6P {75 lqGy>:r5.+G[KUy^:;x#{zqEΐj/`3J&}Ahc6LaOwan,T9&G`>vQNP6!~3;seInM|3';xL/f|lÏ \rJ1L \nH!{'GV4sytu> ;R;Ƀ5\GVn)2J~ joiZVHE=˘(Ȟ?j KΫka LU1E9hn{/[`c+Q2CecТp)"G7>ڧ VP_f+\-fk.cpϝ4kstFԨlN ñ@$wR_iAVƎ#\'f'vږJINڐA03 I)b?1S‘iRHoy?aXf^мe,ZȖ*1J;g<٤Ǹbi _xt^Xbs5Ӥؔ_3}Ҩh*bJE)iv;Ba.J(K[f-PtK^Q|߱]&8jNޠA´PRЫS@AC߯I ڡN| W31ϑ/* C@FƒC*C*ҀMv3`^ ǻBjl!x7xד%U:O-m #Iͬ}vv#\\ł90 Dƪê#5ޕDڳ[\̿y>ݙB-nAxکU/,b!|!H##W'S'#3ppPMM3aߌaaSjl]k]#ګPeˀDD sZKCFSa ]4:m؁ӝ',<~2=WzrV4Mbi˱ r SׄabY u&A細r&V1{kʓ Q;`l:a1{+Ldgh͆kyzr/tŅޢ0>ۮ h݊g4xsʰXuB r 坛U5XG5vң{:WnG lP.qq.O$NN-Sƨfh& NN "bq @8l|}pI?Vc$quP[WH"gLuڦK耨zǖ H?C%T%:P&P=Vqm|^|^ ɿewdw&:M9gtow[ 4xXs]BcgLGy4{q^d;Dv31DYEք"kY%"d-NɞlDDߙI<}9oΙg7_al7g8VY<I=m0/n&P_Pa7}7g[lgTW#{@~.{w'kd+e OM?9:Փ6eIh^SWmlalcSVKǝ{zU z5Vgߌ“.yIbǠ7#5f[0Wu0I\=ڻVyVفu,EʼD7*nx+<$?=5]uFtrԟV Cפ/Rě,Y,Ʃ%Z~M M|/uCpI'P upGZ J?r:D9c.gF^zqIpil*c(`v}Hh_H^<(5@S,z"*'g(w a1>zόn)+ti4l(vUF diA)a*^KbKTcD_&wyYXuFp+E.!Y%23.JIKۿz+C;[k2em,0q؊e1efK],rCRXq  PLY`s[mgGdOw;[cIS()#iڪ_(#ll\yX)@b}Nݻ#Jrr>i@fzBe@@5:*:j?§gy5xU#8`E&׃MUx%r Vj+:u#iGZ(&-%/%ߞ@A͑Dv̀y:R= g*5]nxtdswFKc.v\cP۟Ӿ=6Hjc'3%J]d|Di(]HΝQ~lSmoQG˽O83-p.>Ɠ {*00`^ΨFXǵǵhx627fTwܘ rЊ|cvs7 'iNeZ|E+?a`DӻO=,OUm<q7@O?) y a{Kbj28vȯ "+O:b+Kk(@OĖDzDPnkQn5rɱ(?f%ggyGk0iś>.ג4QM]iKSJ8Mw-mlJSZV1>ޜ&nԈ<b9y{JzsCU+B6okK\I}A{L|HHl**;bF#@<7ӬJ(Bէ/_*>Ȳb"]TG[mӆ+u+1(}FK% @:~!B 1[a]aGǁkFUpc~L82(K ߠ=WA+=u=wc+3W\ `ܮ. ,͹ֹv>T %ƷUŭRRҐg I!fmB) =O0~Om&!tUl{}!g ᯿Cb(<Wu0{[uY{ =k;M}Yc8a]->{ny6ٟvl` Kn:k]Au㔾AA&$Vܥ q*:x@X{J@d:.TGV, M|Em(,WƥFM)e`|tTq+p拚]dR- 9WȘUf͢S/h B`mlhJ$^e6k ! {sPy QCR`^kDnfGl >PSȔvK4d{(VNV1H \F):-w',av?˟ᎣmK(4bNa4ѴS,"g9߾1>yJƘuÒvTzviȎmŠpa!EE7&A__ַd{e?ӷk5`9C5]>xF!FaBnj J/*39"8KAV+Vs#:㺸mA#צu#u# h?FlGlwU1JND%ܸ3Av!7cqEsC9E&e4  ؀6)8%]^JG±RsW(x\!)aגG}ң1Lޛq A_Llkk`>,{ͮ&1ӤlK`<1>Z}agVlOL+/ mo#\ҟC؆dHg!fZg1:rl{a/WT~3 w&#Q`0N嚺"T3[k2FWL'.ŭT?:_ԀZJRlWoJOH~Mf*slm|ȨGpD:ee%EKeQf|f|MS}R}H5Eno11;h4p(a"2˙̙mN\2X? rW,-IQX"(-nnaYd?EE)"9k# noW[̅=2}h|-a-аE5..v9\YٛNЃcOPPݭtcoZZ"`2h$DajTNiQLK'SD#+o{ڗM['u;rd:lho͍2;B#ag*0=#/IN+HKqH8c;?uur碷q%y9]}Ӌ$?zXM+lZRaJ@I+fꨳv#_QV};,O l?jmYZ7 D7װKZժĚplhדn/yET{0Ez 8ƜsWz9l`9.aΣ-X”% !KيHiY#{)K}!$koeKYT=n8ʝ-Y= P&1^"M<kbhm}z#h*^Wrܺޭ0??c% hXּjj;h}G×riuGfF!M{Lo Y(gr)XWCo֊Qgs 2'giF2-c lf:,|80k,/ÏF Sοѷױ&ό2 l`4eC^}ǎ8r,r]]VJ~7O 6mF^s̘].x3ޏ%-Cv Qa`ۯt2d ^W?Y6Ao$|$~ 5HXU֙ȑ&`έtnwRsjh{^jnpsiPwu?7=ku|OZYs&zԽ M,iļ+Vs{N{B0j=EiubX8b5!٥e`t_yә)&y{RCœ]VR% \sst+3b9%6xJ BBD?šk .mEkc!W ,vq%rQ󍳴h4I=GbvJtƩ1T0=Dϝ (&6;Hm#ŰnfQȆq3v| _kLBpTwi}H l?;1^-՚c{Ex$6 (YƆ?ݶ3VT{74~>ae:UevsG^vt/Kb|کlNgrF惷GwXL޿gA ; d9j$Y=W 'yuJNvw>7Q@mMRԤ(ہVcфr8s0,_ѲGF ?Y47)ڴa`Edef/w;t L|2=BKVE{MǛa^%FM{kndh- '(zфoӸ]z٬j&(n7ZM&2*4F6ɣffS4>ad|dĥ EQE\BXTŧSH KgvCB6YnP?]R}XJFC%Ja)hMR&6e"5-*idFVeRJ=+ww*q'bgypفj;֦]v6־WpmD͎b>x:lD^sTpٌ!V /@M6Ý(fh-%h_H'ٯ~l]BB|yyjm@ɢP/Ri9y ^%ڔgbb܀7Z%F#žRViF4 @ZIZ9F``m@-$3;FȚZud4DQ>Ca./S'=cމԗ/. O=^p''E`2?nn#EDFEFeaf1gl^+4iatxDDcE1SqC%|a+l~.T~vxD ^OdTfB1c_^\yʸj>`vkW"rS3Ѩa<͍QZ8,1+! ~]wHpGHd*;Oa"xL C)R)uZU  j7J8"b`00 U+U<#kx:<]Ҝ:Օ1JG=(R0M)x$EجߤvW5H(#zR%%i1R? y}wa-0n&JJ3gʳMK!O;ݼ՜3p#ڜXTtI5+CDUC5U7s99S< u/^u*K%zm5g¼t>h#K)8B"W(=3/M馇eIuٽy|Alf a۫Nܫ!LeY"G# /=>@A2?IVvFr^c5j<'#0urs h-=ZT;,E<{A8 VO$ok l`o4Y5IQ>g4Շk.Yp;s$$``/-df1P-zX3ĕ,8C7OZLaE`ۯf_ĹK؍+ JӦTA T:?G lƐ]0?S~u{3ጚg2گeZu4s(+xjRrsNhsmČ }<՜Pr #VTO>GіmE҈܀َk)5;vkH_o}cN(xLƲ5Cܷ_r͜f)&oun  K""ɩ52&O{À)(GN'ޤpppf};&Uo&;gjflflZAnbvp{}AJ?K]r YbVv[t/IH;m iAA0ЙP?"<_+8РРHesn4岙l9@y!kO;_!O&UK>탎#kaeVsDX$Cú n2'v#AlΆ1ȉBG?QQ^b G)'y_6Ƈ/=!K:EŊlt;l>9.`ۯچ)xn$_o_bԁ u7)g8"Qϐ@-Oqs6۪Y9=c~c)Ԃ2ak'=0ϊXCH~nk4lIZէH(/ǃtM Y0[j^q gZbρ;"ݘυjY!mF/ ։[=: n FhтX Y ܽœ(i DmFmV5 X3NFhеCYZEcպuǩػh,Vg2˖q[ᓔ$#+"vfGɈ2E%"3)\߻n^={sszn1/@'Ԇpz .6Za22,Șژ55qoY^>Y9T*\tCK,[*ԱH]:b commʴ_fG=g{>G؃߃рـHwYoov;Vg/rmN[\Ϡx՜$CZ%ז? k\#6ʓ0@>QF8{qQeIw[" ])UOs6-t9Xtˋ`6 |jMW$+s b,9;H*VX'$J?V'yc)v +`|r&[Efsڬl;h{Y6ǚ2AӺWEm oO0)C%V lJSzJcH{'%/MB͠ y ܖXu"`@_SICJfJnBiIMfUKdGaCYg/cL.{!0WA¡VazLB7232Ɠ/Hdy8ڸV>d~|C|C+K””X~?cc axwN@l7EmEODF[xmx6Ur+;@X7 ͻ^~{]| ^R`R۝tܐzYb}+Ȼ]I<%o3leur؞gǭ8e lgQϠC UO 4[IKY )yvvߧ] 8:|%ڡ|47V!nRFf>Hs h"= <1PbՌ,}3X w~ H'l{-cN\>C2#ԅAa *7g*_iK^ ۯkUqW pϱi;M*d7cvHӒEmWQ]9Lr?V[m}S%=P<[yTA#QRX^o&\'*}|ZUj`A߼UH}Kџ(y"6MMie jNMV,PF.7@DLSh_~;mÃ78靷K+yw[9}MӳؐnWˍ r`g'$[#@I՗L M 11=d% H@X-IҠݤ$T[*GP+ Bp69,d&hh*t*tQt-FYL|[?k?BHq5ݗd!NvJO;g9c3 ?u5`Ryqۋr]?4[v/ےEm`^kהn*Z}a"ZnK``iI7H1*zjusBBQ`W!ª6 HJ wʶ0ݥUUYl!3i}png$0 ]} ݮCC=0jc|4qЏx4h~38&8- 'z__#C"C"-)1.$^pj7VQ%_Uc,5 ??% h-<!ޏG×% "\*a4Oߖ?d\w"Y& c"ȼvS!="s js3oh|bQQı$9Q}3Q|_xWHߡ"6kr>KhpB0>jJ̏dN`^kxϩR|x#imLu)*Xait]zl`o\L;ec⊕\=}l>Eݒn^Uf4[ ~+"ϭtxol'%P*rmn|ply8Mya Yg;[s牮<}F ߂+ߑ .%LK{ͭ\NXa X e#L_E^>pLx*D+D^O(hbj|seUV n @>%u"=Ww^&uqPV%`5p50cn; *+,&/Љ}nWSp ˗bscVmkGP8yN"y_0K5KkXX 6@RšѨDdDdۮag"UH w/|4t{߰ KgBP0m1*uGVhJJew<,ԢQ2K'>j5֒L7g=ZӂH ŧ? I$~N>Ɖc5Ƚ I ]D>٘~TZ贄oRq2/fmj}\Qmf_Fc/ iS=n=Y3.GZOcwh:CEt"QKc&Md!.RX7㕃 l?_.-97McGMӤ)HKP 5 l`߁^Agoל>&MwX72e8@eJ­b}g5LɮvӬҖuGrM ,7'r.ś51v-LUӪ0K,h(qZV~hm.F`sZoTb;kC/)[ دGAgH:ӱ~9dz|ٴQyΌ &fSSgiĆaG҅dx[b:Sy\p 45af`kŰ׍tđ9}["=V Ͽj,?<X"{"Eb $%kI$Z(J Y*kwELS9t9=s1׹szZ;?z!)VPERjFlBѓOO|"^ j_;N)(N)>NHXyxeAlg;G ۪[֦ROvu.AΘΘ cҀ{uBڴchmuqqC|s6MgA_dМU ;tX[w62l{?{TωI DP_MtM~rkrz2j͛ _!)ɠWK$k66]RY/`=}{0gvrrwl6Y@3@)cù{4-QF89lqb5ޫFrK?MM}zLS:ҙ fDz;S{N{;VQ+eOueybOԹHN@kV6s>f IBk8D4\&q9L`՜9Wʖ<ŏ4Dd8-3xiZ:Ͽz*W]Cz-kW4[YER !MrJڿeX#VϞ2zD G"]ꉪ8R=~VPƐek83>p\Y"E\5hWolAmA·o8eY>*%7g^ ED1\Q~ahe6l%339gzzDda͙<#?;L=Zk9EN JQISIk(,PÞ9L8L RR0D*n c'gWڥh>pPwJJvk.ZAnz eZ>byrweZ мм[Tq !\!\#o7, /Q/4]v4,W2榉7^(=˪2\{~FFDbEbŋbLJ}}צ/8^pTnYKtp=:.9SʭcjGdo6goF/}%Л'giZmk<^|Ns-OS ̙Lc\N5)BADKg'm:)F-0ݧ@A j@D^Fh̼2}̹sѨ3/?</tLWI}-LP"?(b."lNuGlojRҫW|;![h uY??^xU=GVbbw_6n*E{b{5bc$H$Lυ}rM]һV:wn7QPphɨ"4bG,G1Lי3?))6Vb;W9,sg#=M|oo6ww#MnCi>y{ 2xmKiLh<2iYpܹ O#\D~[.fsgc)`6!OsI jI_y-.EkWpNS`VgΦj\<Ւ;S&@v< k-J&=WD\9P_xr9"UW0- {JOq mc.tm;9F}rd_\OQҢO;)bg۩/Zܡ:wm϶J&(?uG{(%SI>טqNAKل~Lp2/aϔs e)Z>*i})m^'b>za~߫:O'y;/`~*dnl~57c2=`8ikf3gr1w:cs!x`;n\" B!&!|{x$#"0&6Μv6qbb9;6?tÜҧK= 1Ple_Ǵh3/u>y>~ci'ŏg4VV$ 'W/ɋazslnfjm-,PI !}=ϊ 䖉 @QxAxA9Rkh;ns)<(?[О9y\CX||1&7H[#tٌ_HlB᤯ai|+)i;3k}YY Waοd:(^YJ߽!Yn^\"*?>)}ŏȫ:ޑ[3Z;AϿ?~FrjmH$ __/V"Z@.<iXy Mcb]!ߛɫ$~b̺Uu6h}<#ʦȦ,娈񁰸кG.~DD0?/{c*MA %o((%%f"^Vɱ#OE}L^ BX!@ S6]eq_$!!TIPjQQ;f`'WÜDR䮔sS1=t] wx|j[-j9 5 rFDo"1 _tQW m{]1l[(3!ZnASY2tĭz_j 9Udd;X{-%227vܹݹ)2A ٌS%:#"ڻc x } h!Vg\⥪pf[]odISHS5Hu h1 p>|0rظ8e\BGG1Ra,ceK(nƜOgwR}}&4$_C^v,Njv?kjdKv_Um/_o{yNqpB@ߏ1w 8z'#,*_ vأ!@ZZ~~)¤ 'Qǻ9h-R [+;@R}RC)3gBF"㸠yeUUD Uա.ExЌbǜA,.PH&B @]GOϦ47WQQb* .O}LD$zZצѦO"_txOuvIrgfkjsN-4Op]hÈD`O+&⹸Nm![A?mxlM'OddLy Zg0ﮗ\12Φy!7F;&Hs/ѷ4Xw`ɇjѩ" Okj1g*!\MG)@mkWOaN_ Ut!ld):)[i0ԠJS'Y6GQӈ2rʽkuu~PƨjPjcIj j:$4[~G?j>)`~"ߗ΀<9BfhFl.ytm<1^#*+reE3x|B? EԲPЏ6fxo6zް`[ fGM;w ι~Ϡػp,!Cf[5R4%Eg'kvMa[YBbHȚToYrHQ E>OL~s^?9gFGGo?-.nܺ0y :a*572=89 1d?ȶE;WWy2G0hG~ V붋HbC48-u8[2dOZY|H^E?AJp|N*+*+DNK\K\7(ZP^/&|"Z"Z'׿8y/|CƫJg.@RQྦྷl)vV] Ko+x+~(;A h/7z3Sы#+kM =\(BoTvmLb) 7Oh5.5H6@xG ܀ڿk] ńx ^*7G=DSǬ\:+;+8kKI@`m@/|5bЎ9HC' j?#??\vGES54̹^L/^ a:s8(:fYMnկQ6Z~ܯ \+"H$3xӖ-f ]T/ϽϑjdEy `9ڜ *@Xs$xEf\I)F-e$8='^,^,ۉvU0Mimb01XjK.1}H?q 6ϓ2z|Wz8@v݇3Q>#p,iԢĢtvR%[ϙ `ThXhFI+HIWMv5:r9ӗIbMu ]ƪ~G5, B³ꨌ6mOg+&z-ZF{Л}QQc]z'_Oi쉏/_6{ NtZ0r(,zm,ZJ`nv=S_twS@&A&^c#H$$ܽнy jk]}SmA }.xNg Έ܇-$|v|wi(55IM/_M\?"= &nrP퓈͈> k){o8PX@Py`SǏ{zdh%57ZjO+@$p/;4Y{ zpGGJ݆گ$uWkY*xbNԒ&8<^1pR8=sv4jOmv`Nplf ]TAv eUI[Eo>Wtyݷ(}wcYgbREPr <n_f_vچsK _6[~[{̀ʷFHo[4pQm#s=?H. iaTT$ m;%%d!m!2ߛmܵܵ=t/γ]0jP!0xѧ&3[[Ad)懲p53wͫTT7{))1<|'kD]@~ÄpOuPgkVZ~MAM25کL cw _9T]IXdX=+\>hUͣۈ^F@~Qҍf;*X/cG>}6<=?pN_Po,E洊ߗ"g1gw9xyQ Q8l@RLoln,H9!Bgv/ x- XZ z&+m#mr/䞑Yqq^^>2})>EQ8F;U{Y|be00n4n R Djj{:'A,iXbz"áá<53آo^3 7ş[b6Y/Am"Lbny5HP/[b r*{2W&u'pK+ycCvzp/?OƹѺ<5ƕ^2$6ҞKFZT~ɓ %jPڲ>'l m2 0ngz jOz;m@.nMz_zeۭ,Ix:PC jf栮={bءxsZt1#So{M_18X仅X~q@nUΌfUxV$"+EVq t֎dXoB\=\ej0jPؕ!Ov-5R*we2 vxkbr)ԠO5\doM^Y۲ qijkSL[E%PAkRUh$ڸs\j5Ӭ찫&"'+0n'p8/z8?0vtpl'She,9i#3R=9bCT=3a mҬj?=gKBƋn9mMW;3_/pyn[WX׃S70}}z_61DHۑ6= 9ngT֛qP]%@jSGT:,חL>:_Ni6]pvC@-°߰Z\6"E68 F6s,ox7l`vd˴4nj TN]C]#:mX$PM;}<2{\fq5 {y~8TP x`BԉmWDb[&QjSj>KaQaQ_~zս4::pjq^<|a+F3pw|p@#Kr*[Xa3$C+TXwmsxrj{J񲥪&C=ϟ꠳LԻu jPZۨu5ǍyB(kU[bsࡉPEۙ*5rP_++1zF;r3-=~λcJ:qt٧t,'}I88nP7NV?zpjP$UsC-vZ}6M#:z0B:_4c$lF[kEOŸ~@{5 wg-C #V I;s@/nR\TTg @ LPMǹL $MNИۃWmNf%'/q@ZZpS0If tgć+o3o>byi*#e|ۖRbrg1v]^^O ~չ:#:#""]Ei (PDA)Ei A * !Ew=WXQy<;g"0-Ioqr-qUc- ~M5f]\}mN\]G?SSPlhA1T71<+vW\f̷+(y?cKxKN87)"OFRR./lѳӱxڂk˳YbD:ܡW _kօ,ڤ!]qa2I>{l?mEB}MW7"򪧱o[J=*onC\<~5ZVD V.hq)3e`dj|lV޺ 4#|׸ީVAp1C cEYc|ܞw֨[.,zoT8#K1:sdKT+(^A_+ _SNy==㟤PɆ<#rf  ِWk!1囨?:ȜzÉV_8OYr- |J5*tl B^s+}ayqΔ)8+R(.=_N6׺ל)dx~c*IX3~7 ԝ|&*\.24wT?3u.s&LWzEs.YچaТk 5gL;kY#r-܎*W :nn{^D=U9HgP]Q]K((%/bz{R2r8 St'+#C,QaPyD%(6N,P_?07Ó ++1>* KHۗ?L9G۝bQ߷*A?yT K`\+i44z+>jF5"sG#&db`]+`SDbzrެtYbL rk΄S1Yql%E{~6je_#?S hDtLB@AAbE0> n/յ1@ˍZ~a/etZ d6ī}J@aeeb7 ll(LU>7?7?0znh(6@DYa"C4j8ll5l噮}*? R%  ;,ęߕ8Pڝ;wo;`0 ا<!:tS2ES馏2.=;*55L}Ln@7LCO4x2Wj~+W/½|ݝ mG"sM&sgrg~@{-7N U -Sgz$pwٽ-A#I--B0Lαl*[z!&pVV\dGNEN =ZxNqq_&1շ]pD6ncp /Ő_J&nLDL>kU ?ԝER>GAN,8z?2|GW-֛{G66L~NX9aƭaWuFT"#븎D6d;I40`x}樛,,^ⰼ{lW5Bn1(/0 J4{/ۢav3z<ݟ{ِFY)j?N5+57z?ZAf.)TG̑!<~o dyG)s{m ̭ǂ $TRiM5g٬݇y{_1ۨffHb!6۱'JYC.\6Vlto5" ߥ!~F{$G5❸n}ak,Kw2aE4ٳGPM0ݔo.iQ&oD.OOzy\K8?G/7 d*8I*hWc:M42h} ~-ِ4qhDx22rο.1ҋUP2#8s'Y25dN ijSqr 3jjƴDDDohv5,-4-fu;Afm@y4K݅*Z\˦xі*!V~ٻmH~ȧN ~ZD3' nygl1D]/0"מqd%3.yZ #mMAHȣO־|k=kdj2X]>M쵶#?_Ycyx:Y u*[*["bya-uud@8l`,@w 1 >Jq%7JJJ}y P T 4I g|шD*z`nAhh'Dr/^:^:^KoTz}+I]44Ǔo¡Z }@D}[=p lkgHK*%" PA?{VCn;BLddz-V>z?}{;f>q wRdg:GEr'q[;w5 ]G eSs^A qivXxSEXr5B|c[*{}P|1"ԨϽH2Le.ij\Hj<_Ӭ({_BQ޿wXljn{ޕ|lɊBJ#!ي{$$#$#{>fVY~}nwz\u_iCJRzm-,8B_R_P_XXD_޽x}АOKbളo}x$ChRUQBw/ ,4,T@Br ,#i**6CmK_P}ZE)be:X&sP\9A} aZbg-jf()vDk9 =Yz8+U1a0Yq6>*uWW1Ӵ^ݞ5eE#1V"OT.<xPƜn6?D:Jݚ{VUn5BgeYPڷX},͍D%nFjEu$jo j>3_S/ys< W7xZH]9ʮIeP_mē& HɂpdsWoMZgFH[ăղ q5bMj4~PD~yZ_vM.ǔZc\ĈXnyӢӖ$0 -1vN*(k/{$N 8A$W6}:OWY|vݴx)/T5X5yoكe s.G!lL{jx($"xv:k@KL5C4Q!,"Ӯ~ _=<2<#@K,99)J1z+j?67SD  6u~oɃȃBv;Ĉψ/a''9/9oJ&8rx@J}5ub^>ur<{XD&ţY򫙌!Iߖ$Vo6FU9Rh53P-a9b@ j Dܒ:JvՃ\ A YF>lcL2AWhg5s';&cI+V#ۄ+$҆;B j?[#xFDx~U]D&vyl!aq~8-i+Ԡ+4r-dݹœ #QR^T]N,MkU*m2k0az9WB+z ڵY#">}cٻ'xxտ MJ"g% E `H0PP@ tצYAѱQ@t n64 A ;(1 RM:u7ʝʃLBCX((w1.ۤRR pg:2@.xՆ'J(3bRgz4W 11i ~DMQMEZZ":lϹtYnFn&Ꮽowи2=}&j<>]Rxg:S膝AggzKWleZuB1԰U{6P]9Ŭw]g47^T T{$YhԒNa5]&D*6'(lqF\f" ƙoRdLI`i/ƉXFsj{o؈]O7*K9k%Ԡ+4ʉp:e7xs HW|D.BP5]-C`-3LbF1$k>Ml*5^ kԀƻ76|_s\%)IzN~Σ>r-ԅ^.iYԺjU*[̗v|hn_ҕhS|sM԰ P+"վrN5Ų]Qgr(-uٺX{Z$ym VfPj W~vLJiE^i#?}]sX2[zǁMMg9,Yjjk;&LSryȃ#x[(W@5z!Avnz&{]55n {YAtVʞ-,Tߦ""tEYGL&|Q.َyDfbR̝vTGX`wD<,&E磜$$4mU |;c7oy$@}Q%WC<[v:EownOf|(z R)LK^"rsrZJm֪Bxmj3]:j] I\4e~:fKPt$#yQڢ@yPq6F'\k{aGI;t<ӟR-TԩxLqFDIlS} ّd/jq-sW:@ܷ9o%UgMUԠxC!&GF*.mv*`,HrwPģX#2vM. ThOxtk1?iaqvo|Dy_RIPIfSdS$XdzG*|7]PX@44c.D yeЈM>ˋS5kttz$\ υkjtmCC>i+ztIH 5;xd(lr${CʌɞeRTFJfɞQc^/NC~SM:biH3bLPs.{5t!YvrvS\\CU\'>6D/^hL_=5Xqn m!Js8#FMJT4xb?u}:oXDp-QܾB52EpWl{q(p ]Lw6 ϓ'z6lZtx}Gm`y pע>Tљ؁mNz-?^Ѵ}:(}*5eIS)29%d=vs!,"kf{2qmM5ތ,b6~;b8iSZΜ 9DAoQDDQ:LLHlelgSf: _/ 6cECrr% xdWz?RL3kMM2N]|{A3\ ]${Y ˧O=JJ-[Xa?@P\x UXҢQ8XjεWgro__:+ԧ'o6`JM=֒ВNd+rp?eҠS *ߐ9&)ظL{d~ ueIrA}zbcN*v2mz[ծ9L:2]sg ˰.ps]zI e77ZU{4ZjS/xzT1i.!l5ꮈoz-Q%-)1ˊ1 YѪ=G۰ h-=7N0Z2If mbbgvDnNE|löG!ZiVeyB4k߇g/*+s`:nmr{]*>9F˨| QJtm[a:RlökB_|PnG@dU={ՌՌy"o4J_PС,%qZϷ*}`y?(`s }ӯH9rŰVVؑJQtQ4M7 %Ƕ*p)!1\^q $nDFlCzOZOusZT{8fGɶAAHR+wc~9Fcj97}ЃkmӸf}N 咠664ZrJEkݍ Xd&y, RGh ugW;BcAaPL$ʼn87@fyfX\J\Qgv10v/0Z9q!vMlOF{:sHf"7bOs.IH6l+1COJD{7{J}Qqyӊo9,6lڡ2iu'W PdG? %+>_KTmwmT7t)?0\d3f85j0]!xRAbPc,)6j :cae}հ8>ٗ3w@ҬȡLƮ`u>4Rbעj |U?qudh|'80/ s7P~1Khv¬k(l. Ow3 jPގYou":Ojj؟'4%&~"hB@|PB(rt^I)IZbnbJJMr@}[r*|?v^6ڇ%I|\8>&GD~5)+lJyJYsh@W>=JÄZ`mb3[~MM`<)ttNCul7p TG 5YYY@miK3C虇gq"ޑ㶨v5 [w{aYA60{jIAݷdP:q5q5IR@w?+$0b @[OձqAcc8/HށbrVwAdA6ȚK CƵv箵^k5n]1**u?gnNh{4fOmJ{X)gvs=8hcSiS)^mV=bldl |Ot%F AF.;}{`QmXmXlBY@9hLEwoLn`c {&+KBF+**O.?Zi;(V%%w>P,+95A,VV\eZP:"&(&HgSޣщU GDa壸'/QWVVL9C*2sK6S,3nt GKwG3ؙ:b<`yVK0UQ/%t/|qQVvIvI!_-A-!F@B%95`\#VJ'PXԴyhBBK4pRR_fc^IRHRඐL1g+g+I_[XOyVyX*Fӯ> 6íяNNggkuh e[2[f϶B_?` zP $ܻ!o dݙsoUH"Q@|JsH{3*]xЁW;aB/dB5khD;sǔe1̝ cM8{ʜ<Ʒl]ϙ>J=0q?VR]<ȋHPt¶߫x~Egʿոc7cHVʐO%p זV[h D+gk|w02!)6Nke7?V4mfʳOUy-w{M_'o"^q@EOa΅bFQvmIoɥͅ0k4l{Nep^Y:F2D(!2ReeMVHID)12kdˈsu}y>}>^0}VpOb>Y3|YXTgS9UU).?d$$fc ?rNvsFM,#w-z/|p5]}u}!~Л]CnAD֘+{OA]:I!lg 5mXmW]cC .HZA Mt+(ͽ ]O>{#TU嵞rp4` &ƋS(_AA'n#-.H!ktvPoJ"R>T',\҈[άjUC'x_хFobҩHgb3Vs;L#MlT Bq)rp8􀋍&푷D_++j3NtH0Ox<@.4zaT3-LO,{ܲLbWJ0Pp@e2>@r+KPwi"# z b$$ee*AoDU&/h (.Xnxcu=y ^Cġ#jqkqMlH $C^lT&%lsJJr6z#ٮx~xwLFTp]ٽY;Ԁs\+55p{ʡe =89Lrh%~&X#r0G56R0,Twg6=Mi$I<%ە?Skr_w=Wi_-};\GǭR?1sݬDn6ɼh37SP,jfrRٻ}ZZuTJ zP"T j 5%;#bF.bR鏓@UjA;M1h9w)xVW/ggZ/T>,,$*%*n;omk k YD/{ouFK?byRt#n, vJ-`` {AڏPU|nȑ#MqÍ nyqu5>9h/]ޫ]qc}Pdx( ~bQe{sa/dt|T, εA;*f{kc=]fp9i{3{oJ@\ qFj>'>Cˡc:txo1[|DӜ!Xz qW{+9>h9HQH 4ߟ{.r=AׯkĕՕ5{4{՘o}}Eڻ^ů0xMox|+ +,yi wHHKii {T[$<633vՈ\4nVsgKWle:{D),bęW[Ld㪠x-zNGϵǒv4Ҏ7;T&U؊My8b8Bh#Anx *ޞR ߧQكe9䮚 K3nwY AlCQ3luBaεa/V@5#8m#毷m5 #Zɗ饋Bf~b>:{톥nGDC)J8>d B>vvC"ew`H$NT/5↷v@ j@;-6b['Ͼe꓾U2;p 85C=xjPڙtW Ȗ7 )1L[<}{99&n\zR9gzF#ex8 c1mlFZ݌kj+o{_X !PIe mYYZT_{C77)Z`^m&殎iZl6a'3<۾5}4#^L3\%¡*~Nt{ynwɦ[Ӧ#Y2Naleg[A֎y2<9!@D "+݃.5o%?wuѢȓj2dl2H4ƅD e%@U#l4]&@!@ACٳ+6'骊ϠuX@e/FS@-"vyZs3bu9~~tfD[^[a*2NM9 q ֊׊[l}<

)0iGqh?A3ri[|ԅ xYp9%"xtO1iI}TD圗(Y6=Zi,<ّZE l/_=2NQB0Ο2g`g.:RwFWR/l&.\K =?'r'*)( _좉mo_;Kw4? Ev/MWpk1SǸ!B >wBxBgc걓{xiwf`;ȺQ-xPRnSѮ,];Ϸ,~ ݙq7A /"qQ /k󺠙ŜɜIG\X;m_ +fRR;;IWDvMVf(iʌ!BǖhXί:ݿ8%]=OO9ޗ_ܷũš&9tӅ 4;x;[Yϥ'"R+vm)!ۛL X@@Z,统S \ \""k}+\$\.GE%۰UPvT YW]i%wlЛ=;>u6IoIY-908dCKN{^@6YЏg]=,3gRq 8GiFiF/$$Ni w$沸!f fF3ۍg@8@ /\ ?ս$oq2@vv**~򩦪shZ9Jh4]= C["K1Φ jPB +'?)`,O?PCpٌیR*-h;u8f.;>-ur}n.;=gƬB?2x=wMV1c M}L01y1&qKC=34".Nռ+)qR&f0lyBTieE\x[jW1{îaC]LSs+XE?t|~C%!Q6U6`laꅮb?L7 ` EOOGh29lض=Kؗn!œƛ~s4Ҭ)1w3!mTJޢMޣ? /Jw/P}izS4xZYGF/υgBPٮ2ӆWW:x]?F|K3uN=ڿ<gykV#qIJI6HN}b sK0Sj@5(:Tx.^ ;A~*Ԡ7بj&Ϭw.eAўfzSQ 7NV ڟz I^'s;]ײDoiAȄ(SHۘtkje^Wo_ZAE:v,TCGMTR];Gv)z$aF[viP Հ&6LZQoh[WTHcnn_lW쇌$hx6k 'c3 W{lnl'  g,֔èmQ̎! hL90Wj2$eq3Osvd ,I NNNKY*؝{M7d9SSϋ' g!gbVNpøȸL-q(v>a‡v;S#ϐr<4?O#<&("vh?~7gC~psW ʼnY`JTwۏ%l/l0z F\S+:zjrwP4.VMM+(lp2U9NuFE@YY?? qIt.ܩĞ BXcKWxB%p`SbVS%$9fl)*@81Ukfѱj5̥oFe/)GFw˭E\s&.4 2'd35iov=#}6dcsUr_ h9eJ/j6M'VN&On:,Zbm-YnYcϖP{JW8/H Q]ه[OuF>2~/Oˠ~LYJɠ}¡jM"+>ʓT'Qj0βnG)퇬:4Jq5oWL'ˎ /'aI-v꾡\ョbC (yH2!No}V0/A5TZ)6'>kbwT}ǏyYtQRf#(2ϙ$sDJ!2DpALDo{wqRZ^uߵ߳}7n6j*9TYaυR$Y53x\]ٳ3RHi]idoGn0ڿ\Ƙ!j^Bws|fs/k\֣`.sv:-; èA|}5}5Oagg\$% hKbXг(\H%-3~ ~hu(w+DfhcK!c+\r#@ܛ7IhPk/o*ޔhNk֊ \Qgr[AI_yf.lk,VVԭ~3 q qK"Z]=66HZYY]z"pyKfC~WZȩ)t(SʉBB{}wORQ>x5 ]vr Ih Ԁ6&1R^CĩM<) =RtJoڿ%{{9=:ߜ΂9ygčQ% &{5RhL]c*i5ơoeYi"V3OZ:+?y oGT +@@=hebWmتbq?XU=+ݷtrnnl@e@?e'dF eApp᫇'=Abi eWȍOdօCC/>(˵SSǫǫ5]"M41u=} 0𘊵ao{[-'ȱ˱V(kixx$++Y9Gss XX/B0aGGClTS6{F%~\ UH{4%1A$zL_WZ#P5dLjs;OR|3FJKwЕxalb~֊r'bgwO S&(OnSY+눖+ԶZ^ե`/\54Z"ZˉGzrP&$+ Ԡߘzѕ,$wA/ިpnU74~Je@m22Yz(E6P5F5bMOs0wq sL\0a)bHٻKZ:Nu}tokG椎ҽ3 oœ"ԶVd.?bl? 5NG|X+}|Dڠbjz*7Xp/Uʞ&˻UM?#&G gܞH; TQ{%f_3YzA jYHܻB{S$?vӋ&u;[QB jBӖ}ݷݧ򴲂^FSrtGP|5suԶZ#{Ť9DL5=9 눴dqUB򂜦=Xh_ NMr(:q|o-9:8f< UgEmMepuHhl6+=xL ZD U1?җ` ,T5^Z|[QQP[ Pt:*IzL0ʲ.f"f]f],qδ]`%%'Ưw`evʘ+ EekCX;a;=[X vc_YIhH $>>?ggv!ÈĈnz&p3W5Wu|z-ZaQui)yOzw?sPdW( e K6'2z.06\jbWm"?L;\(狩lҷTV]%Nf+Dj`ptzzw/DR ϵ2 N:+iFqQJg9%Sa]AxvP{D;+ G8EFZ3d:e٥:{bH[֖*r{mnB2e2]e1 r$hxߧ=5S! Uz@& A:}f^0St`.ԠOSdUgjz К$ë>0(hjPð"C1 B&w· NXN?{)wgE<jPZc9Bon'yżЧoGiB5d](@s 2#[[ FGNe$7L@U\"Mr XXrĩ}[h{3)3l ڗgtNG{Jun U햓E]^eo0 ȃUQm>m]w8} !Pv"H|4ZZe-Ɣ UYH0j_~9U-hW"y&wUD1lvu$WWd5e ]%QBNqv~=Sh#w )gRt-fO{ ch)2&}ΞI{& [:Z'wjvRjx^f(QxS6r >΃d\| *ߺZqDSnCn c@Sci h&As P rԁ 0~0;MI֨44VuʲKc3rGB_!P&PV,^dj@jng[dPF #Y-Y-yJBϓSYzI7[0g{%|[)ߣ~Țdu-ᆄ¢!sU^X<(wiK_ D`߳]cgӻL̂K7[QA]3= aHuHrl5H凼bQFu o#p5s_Dm6lZC`gve)ӢA }82W-qI8lpAm؆m&Ϫs6S95df3( YN,SOI)#z I2vv"Zz>y99Qg1 MsT4y؆m6ٟ!f?,$C{gg8U2ķbZW 19]ތ؆mƐr~IăuڤB F&i!{=Sd`m4i%ʧZƇZ^UF9azF2k娝i̟%vj[W6 FB ifi/L(=U%R%B[|ƀ j2,uU._hDKC/ns]dE-BJo.: Ȍό;ϭq]L$@&&-l7nɊs$慉MbQRhp ZуЯFYfbV G4z[zzF0! 8HN"#oH|3NQQr]M}DDS[ǐsOUzY|v$FUۘ9+s;1DC|-rn) xqV5k6QDZ[w* 1=c-`ʐJ# o{ @}.REn5ڇ!-&K+-ɸIFЁWGA{'P,(lZ@gՀ&P~L::2;r4eVWrd*6h=hBk_%>b-_< 99.}NNת1Qrl6㋼^PtsGԛ]2H]]_ѽ!p4"\ } ~&mC|5͙AY 9mIO]CUz Kdx҄CA>k_VT !.nlög;~հx1=*\EMBkjo!a_²4g+XPtS,K\c ݧ0 ۰z)O욡jgң*N1+k܁42+fT,車Im' %F_>ʦTrg[5#K[ߓ|9cHEpQ5*G1eKL ^Xt1qЗ"~;:ھo<Η3'bA\xk@!e_ߥ'++[ K?_L&ZW@vR~~QZ>OӳM˾gkh P"8h8I~h+'7Pç~1\3(OI4'_]i0414UGW(J$$--Eqn:Lk shq|ґ^4{O *~ ?{z쯼]a|WMHB#O# {ut46AC Tnsc+zˆ5̝GU {W[Gy`~{:'i}E0J1\)c܍;΃;(Bh[x;X矯1(Lw66!NPsFH w˵>7Mu'__?bpޚ 4f)(\OA,dңe*d*';NWgfS@v+hzSsJ+J+P@ͦTޑ}=llƚ§]~RR7g7s9RNJ'Flɾ{]aWS!޷[}}bk+~& ^/:?O9\9|l32_#ۍs-Pl[#Blkڴk`W'34f5Eq卮Nɍ ~V/3ҜN1ޒS۔<`_ OwԢxa|q9$$")q:uPС VKH(s7įfc${բ:x|u>.PP祎sWWgi&{ɵvDE]߈gf·EƯ_aC"GoR*nu!euԜVJ%W8,Q>5|W'|@gjݔצ4SJK9ﶚ8Ro1 d#(9( uMcѷRQۀ|K@v: .1Zu:"??c wragB9zqd[|OENNcc/ xL7N7ngH? f2ύN: xyh WW0lb&/[I4L,²Gj*oF6͒]Myy -C3C3 3'.(pb,!5{o<Saasxsys?RRZZYZŜdc{ZUC^H,%w"(ʥm# dQIG?bbT}.5**r%n'YY'׿޵U$yxl͙Rg+gC1$yq鰗<9,%(;*}l`N=ϝ0N VzXd)\Z༄b9uܜ,qZ+4i3:|l^stHp'7JsH.!r{Z+rK`]C]kR|kkkyվ}@T%xUT!R]J;;!3UB Z,a\9$efff2F|~a*J5//YE[W_[c5ƃ+HTnHwSEXfC:93Ui 0Mg@HjHa?b4i@BwyVĜiL)L)gthJ~*Y}ʼn?*wzFG'Y~i)u&]7qIf9~>D➱]hgySwGdyEv/k;fښ(B5SC(#S S tZ%'jܿ!PP\Pa ̈́kaV8&N[;6dWR⿘butfp=zԭz,lJ\pp k}NSNS:?!7 TeyH(}o6UWyEE՞([=ly"%'u~qI8\=ႪOd`C;5%v䵞ˏ{i/(3cu.=۟58:Ssj)_]P1IkKClILSo27]k]W`")g :n͝bAC̍$c;WTeMVa6C~Evfbfb. O=oo)e*e3mmKI2m@xn'`3Fil%=-;-~I-EAaEXWBi#."D2''{Rۑ#iGE5hzLeo<<ׇ̑6>J]%d+†(OOZOgk],T?+iXXt)y6|*xЎ$\چa;탢&C|i$},H:f]ݭycҨ9#l( yZ`}+Hew[cPw4 _nmkNDi( k (AT7n0R#b6i*[p{}8Ƹ"YIpep!a®3 漢:5G0JuSYyY9Ҥ6; O{$>$V-.m:1Ћߋ?'>Rp0#ȊjH GHj'y;V -|F5W mOK)z3\U[ZJ,nkm%#I5I7Jr0,+$&ڿ7oYІ}z=N|>(l僚TĈ^f-Io2N !֩|S%7o2׉..ۿVKqyYG>ݤH"qp LQ;F@ iH.CƙS#N`>9;"OdWuAuo—`nk5l ꗆN@c0ݣ)gM}{bV`Ő2>lw9mEV\L]!wp<|c͎U)I{~3[d\ |"9-}OR`9;6~Y(t#YEfOhxe63V>Voossf$1E[qp_5m # Gs"`DM襼~?nID#ܵ)Cٍ.F,F0Fms4ݚj*Gq- QBig{7/M~)nfH&4t˜Td뀳:7-г ZfSUSg| :o4 Qi'dJ0xtl`ZU(Q 1iތy3'BJ%'7hJ5/{۱ge;L2*ht:,)^; [Y~Z^G 5E'Y[ʕuVw lL/-Bs E7 JxY8uCX$77 7^;ԔzFoR&KM 6mPKGȭȲ9~P ]s~0Q (SS:'GgTUݿ8^:^R|`ؔAe-$kD"ipg[qQ>,[g!H쑍r<(84~*eGG6} 4@+Dkk##Jiorpr!گʷ.USs(gX$[E~14p=@Gw7,LKßg!ːp͚pp/LHX/ P-Q- NOާnVx.0HC#w3m|JQMڤ"}6h%lbBGD:^j-*Bcݎc ! "0ٮAOG>jͱB eZDlc.$U´_yrh/l>DдI1@NUbF5ΉKVi@k ۠h]pq0$'Ww8飺YqLAJ8"agڷڠhIf/AV?|lqs$$Øn}d =$[/ֲXD994)[S Qb]DOw 'aYt'ϰ#enǨ#c^ x3qb/cÚ{pk'=D^A-Zx&3a?Qd`i4yۏ%ZDf|s"ܝ~5%wQLĶZmiMcN׎1EW~};Y2x&s4stoPc^̯MMUԮ89Cu']Lz !7AgAgkidž@CAr(+>j kyy 4(ᵪoSeac)FB<((x\_Z_ӭGðalD O:OؾXKS3S3|\;v@t<`ybE4J(auuB@ [ N<]5+1}`-h__?~F:@En 5+ Ñw3SFvR>6i~oz %MNN.Wh$$>ݱսխj@NEB.CjzH|#v}oWp[(!raqz9Y C&}6Y[Ɔ-wp#yZQQœ\HLôi &4#SE*SQapDL.4L@}; 5k,,.o XHwZ}JiwmwOcXd9KT.vϸ׸H9te\/`Ks )~ԆBJlnj=37ށt,ZxJ.|ӫ-甗2]6kDF!K%$Nmf^of " 3 HKqYUWWBEiՕ55>>{NR0ȏʫ|?B)))gM{N'ײ7^(6@+ Tq :{luҺH7QN)1>@<=RwT֟7DԬO.l;#Qs)4퓫-gyEFz=i7P~fm_3 C^_gj79Z{yhPh'%&&V:;[l#@ L&&JJ7ˏ~535s'yɏ؄*QP|m}C\Uh^2hT]Wx-sgH$I$96*+ƛEI9iAd"]"]vv(,-crX%`8n3}!C_ݣp,w*o昆i!R$6xVUDE r6Uq>g#ͩ2J0o`I)xUYόXѢ`ZH ߭.xM)9m|xݕUvseiNEg5½B/EjiNmƂђ^Þmt?i̬0Yy%qd\5kA?w?U7.a˞?iMNSёo3b ]ppѹ+4;jjd Λ_a!#%Tl4oPhRVRV6b: e4AmިNsg$ U;@G8tt!C 4}^r~&EE3tL'ߔxŤ!d&k 7; VVFcItɰXaHŕ+ +`kWW L}NlD A(TJ#:eFƋ 'cOdeF o~gho܏)!Fgt(Ko @gO?TKcj5 >x:g?bh@@`Ϊ5P!h4Lv{h;ǝr$$[-suW nvFmJ"2^qېEDsWr?׹GG l=akrFERTo_^y]^b.W[D}M0T3sdx9 s\+abm&\%q3PYRn 鎛͚rӵA{Q0q G}!z!f햾"'ģ ~hq^0%~N܈H_T3$?f &B7% X0;R-KnLFQg韃AǮî#gwJұsL2ʍɍUP ٺpnh2xm3h.4vaj{b8rLa59]mw8hhԋ֋uꋐ!&LJ}߼W1~^F`=Ve&2LXBp+Ŗ'T^$57WVZtԈK=˘, !m KH- cZ $yry}!_?賯Kc#̷/jp]ek>B& l`Ɇfz%EO#,.'!S|b;%Ոk/r ә0Dϻ.Q6|*5S~'Ld_Pez_T`[mZ9 3 ES?~)j mc95dbyJ4)-uX4d A ߈y?:~Zvp_&FzF j8$/g{#c=XxX @;:XhwE1ɼ1sZZιgܵz-- - ຬ,M@Č@y\ݵ^ц&wrɥz)"h~A c(.fPh%GV&]ʹ\u PP^OUhhF؁![~~'TВR@z6 \z{n$JxÇy|wVkt}/.'}ykY nvhxXt->o"ѭU ^H&zl?FF!%6[ex'f _?V ;[aa:n~ ! :)+rhzMJnn#j&0`KxaQ$kRhN"&'St+t+yZ1 rt|L}p<8v8j$Et4e:FGhb;( ]#c2Z2ZJ\R?#*~DF)Ƒ6n?-&֩]}@Yqrb)=O!}}<]2r!TsZcfk ߧB,ؗP2/>Ή`Bӑ֥yPҭlf寽V鰫č+Tolυ~2uHv[2*Uf~4lOnq%#$)(qmF<}}y";t)5y(ykWiuq/[viPzH,b&ؾ4FyP;34S?KTZsElī鏯.Q|}S9޿1MS?l51)`_mIszRCEs\2q㤚wL6vy]l۫2!N0o8(O`3vSK"0k鴱LDWᣡN1ؠ \\]I0Rhh}_lnlc}PCPh*H[*z+\FFBN?x|ZScScɶɶ[;w_Oy 7DGRJUrguxVg سw-6#ȳ F A}ӕGa,lNsQulyDwےR>;,ȞkOj2{Z{nk+W`HQ=49Q{ GetTFCbJ%WB2Nsqض[x̤1N- 2%.ڔ'=ʒŽCCt06WhBz ruϖL=jCdOAiE6I߮u*Se SJU͆o讯Wtj\ nWh/ϰzmG$6Zx"1#qf0Fܘ'[9.r'${)O;OyxdA #{=9%mX!~%L: %!j 0929"+ --h2Q*a_VQu6QzPC.2 ~0l\΂oJ00x9S٫htH42 r`M{o;bCC\omW1Ʋ5sihąf97od::*6a88>nw).*.ʘ%ve1@rm\ZwIb>ԄqBFbmt?#8=8U99YY c{\`zfG6|~}:fZTh阷6kn5o=._q6JPXޖb"^qh7M$SU+yǛzgF4Nfq 6[#1@3rĞ]rD5@}Zuigcܨ\8Ϗ%]+㖟\H駰ͯs; l`nmEnѦqyE$N}Ʋ|wV膙q[ua)4 ~.A['vU#5_D6m Eȅ'3H>O'i$1Wvo|ρ lBcPdRIv4EWm9v5dqQY ת Rja XBK٤Sf]+HPe0}eҰ@$q;[`皔I#D{KD3 {p'&uUsl &`}Z_o艕wSvیx\GKw8J*Hu ʩv5xim;9%SbO]7c: t \M<t!%kC@Gyˑwߠ mVpn8( /nKꜶ# Mz]8Mbu=?U 2ti Bug{>;ިxjOp ȓjxP0&geYmW; A'<  DD ;z@RHH$ÿoTkTKF'W DKhh4ddrEx\4@; vE}LD L2UKWKsf#]@ff(㴻8Ԇg|PϏ|Vس~˿sS5FKBw<=& Xԧ+u<Jd#S3;kvh({^<dypl`ZۨyE.)C۪"M O|ng,=ĝlw󋘱sVv晁jjf-"u?G kMR'ۿiVy@yIns=I2pg`vkt)-A OpLr?WIlv/m;Ab ntH,HB5kk+(ԭ}SȖUF9V"MȎ";+[$+3;+I" Y_SֱGl!+>Su}w<{^}Y_!)=,vM5Y#'#'&)"gv(߫4 Т/$*M'˱h-pN"4|u"YG$7PD5hPU`|=D= 8,|X8^\4S2S4idXݴ(OPJiBƾBC|IJI0hu4>eT?+ԊԊ#VxdzdN+2SoQoi8vJ; к׊<ﴑ{ϜoҚ`|zYWds1S%lA}u|u+Ycf{4 a?uA<2' 6v♝ km~9m|;a/d6,2>rl E ^ti^SNSgb-2 plBc\d PD !uu'9@@({afR-uY]:Yʧ)ꠎsu u RRY>;sI<C:m cBĢY(v7.84hZ?WTVf d xVPiP)Y"I4 5 cm}7K\{_SA f]kwzҿsckOc&Br6TO׾)#4:QZ'{;72iDU8zs}=$)Z[$~{3wzefKM5g eK15n/kr @1Ηy*F cl<َVL:us&ϙRB^S-a 䍥tr}r+p,VsTC5w4\vXэ g hc{(o} P$U,5ʘQ`_΄c nH>!^y$b٭BYyҭ{oS3YXnB>A5TDSvcUW ֢ǻ[yM˂jh4>%5.k:'wsnJzzp#cNC+UȅM+eh>^F5T'bU<#NXIm땊'ȳ^mNU73z5r;5PLz"<5*XX{:tB:G:'.@sI P`8^Q yi*b ?E=(I@̧{ނނ+SOO䍂7++ƊwG?VG6 0s RVrhA1:cItԄ0ᴩ뀮 /;޺00RS*SnSn;r̥ݑaדG .6GxZKLj61mh7PLME |* 5KŌG$ (\{jwm'rԉ{_-]$}$`bg|׸K2RUݤ߶S_ma~"\t^@ϸV ~$4rw=۲=dewO/hy-RQC pXӀ4U%؈MpKCݍ/ 崌"|s^/`Qd7Tn؋wlt\Xu9= ӍbGjԨLܯ'W¦rֵI>: jx $%:0UH3c덮?hݕɒɒ77|01ٵ\n;x^@x;5B71  ӴӴ#:]s_azIWBCcEc["g>|V]N؄9fffG#`%7ߴ5@(vڊ ayˉyз@=p&r83 %B)C |57gY#tzI2AwCO &--:++q8G{F'zw"9"9 ŻjS'L#GL}7Gif}3Bg=aFi_ѡס__܀֪֊yXh?)g;摝$uv/֒7(IBym)iIbm7)]B5T;MqiɈ򻪜r > z=j HF^"((@߿-0R릤aVgX[LA A-IOQisssG믶1j5i(caI/Ғ4 øJu]J&nm1e5NkeJSZ=GkL顗ޖuJèyn֐HQ"_{ܫh}sbw:Jhح8?Ҟmޥ xV"kS?6h?fe)8z ߭{5] J41L̔"5)9Oq}}XXBvq|QQjjI|1ss)agX $Sj,q ܻ6eq_AFLF,N77?ٵocC&:]:]H#p 1;Y;Y#%C"%VI kO,>Nt5G=#!s*Y\(^k{9;&M"S(2C HDp=bؐ 4NWz#sN98N3-1 @мpGiia$1=GC7i5i}^TCiF=8|Gmxk9QAMǃc ]輞|Qj[eiQTt_Q8E0\l3ߪTF-wV{wXQv^%;=-2${EvqDDMH8Vث2:W$__<)]]s{⏏?p=2!OPȩL)t1V6WIl`-rJ}/dA1˞;7RӟFQo:. )Zk!dR7OqK/A:f8B\>fő,Z!gu!lank36Q[B~rKO2}aїjV%z6H-zNJDc.$w"xY1 ~~BCUKƝ !nn5BZq1dw(1`ˏZpTJҟa-WW݌;YV 0)WhF5rr,&zw#FS6FSW  wts}b+SSP[d..< EMELwoo Z.J-]>_ZAߺlr||`$=.ޛ]Dy!7 ARFNv^a?#m6 4+8 ֛Ece'*WSz<QۿeF^\)eJزG ԷcOZ5b_T?՗6x9:ƹq{)>hc>Ñ8MG]Ԣj6V{+ᕙW>I;u#I*=z,`; ݒ|Қ ")eF e7"3% FS7 Ht*w 8Bo79=W&0w $[XhaR2mVWg[ZD3"aK~_Y .YPim  )Ο31L1@\|?τ4rPy׊i/Rn1{hpCG F jqq~DL7GV0jee)!Ñqc`}YY{tdn'P|ݣ# 麖1U_Ejn>1;Sh_ߣ}[" ^o'5`&pwJU K򸖸z']6V0V 1 ga6k=Y*5)ߧ|p:&x=F L\Re@QmmF22^CjrJwJGl/soy}qh7]m IfGlLP }}RT%DNeNeEU}U}]djddriڄ wh ") >O!lG D4GGk _SY1ͅ䎈 qIPe9}=g KL7G4tǭl2ۨ, #Z/?҇L׭5ذ={AUx65>_ә!2Gx#d0!$65'>KM}@9>r|BCS ˆHwC~_t׶E)z)@(>>yiRƃyM::p⇩^4>j|nLkMk9kIH^`>mfv߷le0ܔiX}qE!GTD]Cj=*{ZHMH!2MM 5XL aNXݦސ7D_aWzpf"ż^lg l@ӅjǣF9jHqկf"|M"l i㨢FFĩ`;mUx1zQlN~$ 7ET{Dj3LT|Gx&I^"6roGP|dY璂 l?C9KlRpwU.*L|ΈOdSy lA5E,CCJfNuKeubw>ɱGd$]`NU˦K7LִǕ^6~D ].V'MWZ*b ,eD iVd{ M0EBeooX+|*(DՉ}~!hhLS1xo kUV2q^҈iLU5[$rJvJv9o uu`pms `!NeI²Љ#ﻊ_Dz&Z+N+N9S@#[#~k/@"gNDsc@_UvJgm]vNӕo R9 ( d(f(_3mu: kO&MwѧQ益58C(V`¿ߍӚ֏L!(2)X%XE;SnnL4T1Oy*{wVv Z[Etx"g/JfmvJ>J9[Lu[Z0rsm^?IkA7dP|&~[" a&|`{B I9ųWğב֮o_A:6q]ѓsVwQB * 88POM2u^M ۯ*YW=A[:g~OMU> 0z5Sr`1O%z'ڈ zq#}F$ MR.:BC-QNQ*`Qy{P~Q]1t!Eִ2˖P^wpGwuҙ GIף}$iϛ^ib3675kk\=Q.`As~Gg lWnΖ-p;TrWIQ, JEW&IY?D/W: u1e.i&Y:0B>7sQ9ѣ@`uЍ|j-N;ZORuOTp.Z& 3,zZu+w#:[t`6F/o0Or g+C[% uN{qw칈xa{n覾1H@!3}޽iըk5[ا/$^߂ا| tj Id8vZG#gǯ6;^Ed}MM=u22I~KJ>Hc287 qz9Od x>yަ``.\ف΀JV귭gm~2)}IO<\݄|`YVo/.ArKDDO/JJuŭŭZ[A lvDc&S^+׹!aΣlC5ړ&ٵ 0dB5d'l-$dI5 eKt?1%;V tE (\1<|QiڽUQWQK$$)b4oTjc4=q/%$Hm1ZgX>h6m3B> ʰ Az@N IexG7 ɏ#?YHp2Vb9pW}@{RӀx͹ [öŷ_+C1f_I $ۘ\(!HulY|75vdժXU^X0dG* bx4(P({py&.֪X2NUn:s*gC>KB^wR̶oЧ_jsk);^BRӤoLw7/2&s$Il6kXOY|.APrz M 39vq3r.ޝ i{jslUYr,]껱}A[<XxeE}ר=&&/>`r@,] `1k~=720՝ a[R]5V;ih[>d9e95^~~AAON&nc'GnXlf8"]Kߌ:H '<44-.>D֐9iܒ9cnnfAkX^o(0{3.Cʯ誽ڳ|BCNlwm!s^~@p9i)?w"R[xdCnNȱծ_7ϖֺqZtir6.h)"м3,PS]E a1$<6C J`>j y2wi.47X={מ̯ <hӼc~9u%vS_qq{3!+%يzhܟљo 6yWh gۣ&VS}cQ cI# !+Met0RYQʽu (=q@vvGRVšAN"'I ;@q#= 7 XO !P@$ aK–{@l1JJ3')寮4"ciz!LDSFhuC!r39WZ}ĵŐE55$Wm  uf`o,AC>E:gfk!kgcMTd; KTqߡ- EOʜ -MDx=8 u2UmP3cH)xІo{jx+I* rZ  hjYxoMQyh^=Řl8PPqRăo$hKo͙,Y`6;G) 6Iݜ ~e9]}/v`flZ۔];-tkv-!̣,P x<.veu !b%ueSm&Q;) |+_!3#33=q`pw-.YZ;ۖ@YBnO+l$R@߆w˃{jRywT}J DDzӝc-z(фm}=-߫tynY)-~=AcqdOOvP~U4jyy~K)@3@h. / bǞ177C*P|IAdQfA=TO6H[1xx$а# 1;ʰdv ?O 8܏U/X9Oem1/}ǩQigOB~ř3Zcc\ '.f\Yz~x \O2_ݲoԜaٞwm~|nt~  te;F{K_w Xpp iB733Ȇ߫$r_8nfdc&^!3TQFfvV"[B=vQJʌu_w~qw=z>CsuYtAUT 3 k򳉾'Z_Uug4(T^AgT+[ASvFl`-je:DѥC&x䃔DIyWnD/ l{@E@!'\y)7kObm?rj _q'Mօ58_}NVUB7J!cgad0 `flعօ>~yf|c rގB6ډ̗lHsn'*.P"9 #45 gj'#دڇ0+n2.d-E.11j @\w/wLL#^ԍ[M[M#,s9B܀nip~e9b;~E*OэWloGqCq+WDuDCcUTTv;bQo-f PKl*.<]Bҵ}queQ=ZG!ckfV4 iɚz6=niP†ԇ\S eg|Bk)o_Έ.4 7 wqV^MV]c`MuC/z@N\ctaYU>];ʻ2>ִF9'gT `ζ9Iʙ`ČJB89h0~b1a3?={Y]ʙ|Z>8k@Bύ$V%11LQ_GӜx`Ⱥ?1q"c4%\e5\ؐS!<վk _Ɉ xBJKzWf{v'Ʋ!rHu*;T+ PPA~G7} Im;ёI'__yjxW1p}~w@9ǜ/Jykfx^Nз\CH)p-؀精L) Xv!ӚmuB=^v$( R7ɱWpZw4- JщxxDYDYeQ~LrFr5op?2VX`uSgL GInu(Q[v\dII'pp0dVfTGc۵Ktp%d.d1Oyf:CLNHewtZihSƕ@Q3bE5E4(;eO:>لb6lha1=z&K'k:$鷘: ?u7xPGs q&{cUHczQ,-˷ MW l{U,R 6?V;b"T}y ʺO*2~GMu-]}=Q(wKo_֑Ws_bܦ lg2Wѧ*(ko3mDy:ܗ;^ޱP 6F! hs*?bSoV;| /n2e8LpaiaɆ܍va;J''}P䁝_r *3;؀ւjD9x1"y~EJ7,7{c|2d.݆JaT6>"I4詖0<|QڌbIN&;\GIqMƫ׶<Ø$ l`Eq&7{oK۲S鉔/`l/#N]&wZE:N"MCZR;Ioq̹[[O<P qBp\ُĭē1Fa#JR MVp]<5 ]]b]bRRP~`= 8QuaY#U?'?or h [q]%U3Wz| hOgv)61ᷗ~RRaa[^dQa,,O!RXv t;nimi!^2mkMFĢ:(2H^I1xtͲqp9s|suPJp} w/v'HvgF lvvMkLLi7eX}KN*++ $Exa-<#PK`A=..8,Y>{Z7]Sf 0_3_LL-e 11=X{0c..f~N/T U[O'RRw~1jlo lJBbTq_/ /jƐ7373Cɖlwgjk1Oy3|ճh:oZS:(l糧TVyVs05ֺiQB(b&bs䃛 UQR ϵJQY)FB'̉ `(>W:DG ݅<ɟC-վP߲,|m % l`km]E^ʐi(J@UIۃlt(c ZK4 Ct"Nx=j7QyެICԇ%&m;l`'H;zcV C`}ClE΅=cYr7~F9!KY~ƓQ>=QCmG}`53&,w݌]Q9邠O-g,]mn}d'ye R Àō}(9}Z>;[ 6nJӮ}y-|'udZeM `^!@[2g*_bN~-jj-~ @J3C)F7c0s9k|rӰhֽ 786@_ .(x@(ppڅ1+*c?pDf>h>T%.X2T 9|ƂfSQAt2tt*V0FvX;U0^2@S9>|fY0bjza5O<{'E} ݸMI2-b4kgiبYU k^)^)j:;ۡzh=`ZFL|񫡎4vS z6:urE9Z[T N8`WȌ0um6/*jقt^ʟ8Kטd_H&AB 6Seg 6l[-t"Ηm`^k aá#Y5$ ƾ{D%CR"{QHeC;s1%<ϯ{돹z稹^Ws>8Y[{Ba,w~u#Vi.y+;T:5 5;Y>_R C&@G1E*$Z4Z4WVܻqa6àpPBR {0ldϘaGuϪv7apHM7D775_1)>) wRRSISI/֊<^@Λ͛,QcM.u'16ua>K6d|m{ܼu- R# 9q-k&!O9LX_vXé-I2#''#^uR::rgD[(GwItR}՞xtvR.< +< k0&A(k>%ҁk:f38눿rʇ؈NH=F`ޅ4R<Nk vT|dXu@$:jϳk[ Ȯi[VN#@iVuDA[<\vUHjTo\Ğ3%(ޙHhe?7l6g ef$s͖LTtK+E>G¡&*^s?3!mLfL&ہ_QP$I=e;;}-irkX׫uu) &%`"z63av De<ˮ2W;du[y-H*mJISZNIƵ?]3CW!pQ@OB%j(k%!{;"p^+2ʈ0}*%P9=۹u-Qjf{:d]dh|,X?/!pp'kmhGC΍M$YmbL^LNчkAs&Dz%c(ĭT a!"hsp 6󐋪4}ǥ"O[IoGi]DD}c 5\o4x'\{8ЎZ{c1yEH֥LSvk@BUMjikI:98 dtq [fKǬ'z.r-=YЊ=[ꔨB ,Qu#MMrha)Q?T4e|z"r"r%e̶8#CfT^VW̯cpj귁=% y chͱ:^WWv²x.*cWS$?nS7!(x.suu[  $bWp!<>8? o'\!3>֬Ӭꢒ-m1)%)%5ğ }p(ܶK J{ zOP@˭9u2 ::]9tt%l99hɑ۾R^O,`PN{&Zp%TDBf:V{ gO/"7ϕ-iv& D~<4Zߏ}W/¥/ֲP`Ee2v|7WW`hHbS[%S`MFgsvaR@|99Jx[ܮ<6%-bAȒJyl uu)*ëMMkIRR|"(/v20zY19En芿&'`DUi~c5Dʯp u eE] 8-,dm];;\O_gotI5$gQyLo6^{ ;ASjUmZG _Z m <"~CDq6qJV|5A;e#-h-I&(ǩOMx!(+BS$,͐ ;.4^k=7mLnlbڷiz6Ѫ0 순Y /9QT s\õ" aͩOlNlkj;=Ҵt-wyx|FRϼ5PSC(7y\f! Rn=Hʛ$l>kHڃy*š޲M_zC=2=2O©q7rr('-Coeh :V :TxCKE[ s==Et !ȷ54H6FkHksb;A6x$d~r9Tz׾a_8~wAAyء_1="2e2e9z\dwGGGZݢ\$;RZw7)IJHC7;ƞ1gp=8~E)o< @sg@7i+T# Z5Xϒ: ilm~끕TϮ`S[Z.`PׄY\]P=r2s+)Cg㤔 S@;=AZ8 ~a0ߠ>3gft'A˵A30?'jVI?dP\b(1TU8]]X~ܐ- !e$7U[_mn>ۭW%+l3l3frUTNŦ&f*fz⣍EZ[WQ(ad?;qAYegQeEf*-{+Dʎ$DDV6!##)}q{=s\>x|v Š3{j| ;}Suρ*ZGA$dKp>guV,UKz"6Pm؊lEMFVƺl{+<Oּ-ޏ0n]bSFBԠ;'4TOG; +nѶ6 Z=j;}.I Mch_;[ƭVUI_qJ*BC򍐳ﭡZkʊjډϝQy'FF+1 PmϑZ|N$h㷗-8Ñm<d ^_`/t;Svj'݁!׵rbyF!T:A!G{]^hdȝn- ;V`#:7f " }QнI2Bys/\o"t/G_zP. jxb $9jaAeCٰ; 4uv.@u)<M J] _Xkcsn?GrP2j3)1HC|Y% gUgL!D=rUU;VH@ ;: RX'мқ6lJ0#1plj-p݇N@m7 .볾b S=nvQ܁G&6JDi8s/1s!y C*I)r䩛I2I|Ry͒ |&D/6s&bW+qWbxKx^xL!U@^8k`pBKR`R`J{T1,ʡ0T%%\̗̗<xY*'?Ls"gZ(|(a  _-QZ`sNrl80Zfw8xff7 |Y˭JcjӅӅstxBV"O#iM'џ/NqZN\JRY>Z )fw"*'y91o~K^ww{iT{eڿeQFÙ'߭M 2 wmӦl?3jf(s-ra W)yGjD$#E~_|͌Pr= M'7%YN>ե1qv"fPNkE)eI[L=ܛj!R@nź߮F&5EjPQSjڸ .64l_"lŜZSP3=ΧڛK>Ek#*(|kĻ?F t9Ǟ>QfA)Ԡwɘְ^Q<{!#$i|Jܛ-LJj1x/!>N0a,@pj;Y >/\THٝzu͍O\XxWM;ϴ${ަ3'bAzr?zgaic ~\͙!mD͊ 9e^4r/-^6 &a"gYt6# N%M%7UvQcgë11yX䙬3Y3+ip[\uvψ Yu4ƭv9}s {T:*HֿJC!ڭ/Lr6G.-YY²OA?wEQ~4-A䃧(-D115Z\J_1V4zt55= D:~sltTרg#6mxPl Kήj@>RJ|v5]y#M4ӔfVKfQ3u"sP3{bꗱXݳ*x68_zt+dMltI\` -/=VU]ب$n@@*d AL)(Np~_ euuUuvG(Xff;̫ͫke{J}豳iF%+KJ7ciiez~.ed a\[#JXc#x){G\A\6-]KWood..JSSTp/ ވۈ'v3ܭ*| "/26.'mw>l5poİQRzGyPD:哎 Ax^YW66iv~[:L:Nrp֫H.41P/2s5ԠwYHLBIS2$aH՞TiNjP$e.nb#YK pB>_F\@pvZ#Q%%0esyic¢tdPڟxtdv?P46n @kG-ꅔ5qhMqrK:όyL z kEfX1~ւ֊Riˆkv P 11P~{lkOtntؚڛ 0zNȡp63mQ\@c1sVUk7eTI J 8L5L5166{2Pl88̺&,(޿,,4z(<&R(qrY㾚$[ ~-}ɿbtrta1aE:448,]&5&5Ԏ%m''eq>bU|A(]Lw*$ OMEIHI/ţ99suз7~xx_ƶhV~6s 1h ğp*TO˸*U8j]S(v{C@JBA r߆A:FFv JMwg@(g (^w559<<΃';)S^eۏW:%syshLͫ(yJL\p8 HNN\ieǠ_qR^//fa]CCH6$%ff)[T<4upS= j z>f1JO_w﮾pH'~gf _Y{O%1З]#2TSv" PڟШޟ*&¥nuȫ\7``/HgTrMRPA1ԠO`\;R3Lbɇ2o"rN=X_bh#jPߞO:7/S(U8yb n*^{b @Jt Xa2y rCTfSÆ8c{i#P#s |Z5!RL؋cc YTnP.KG'_.bhn4bw*wӖ!mO%y`+^ζ }o'~:]ΉF1`H_@QUhePappJXE#-Eǻx[!x`PY=Z70#QB9U$ YhUAT*[Sd_Ͼ> <ʽ81 )CD±/e+0Ne˞8DJ8d %ىlٷ-E?לS?kޗ|gwdY1д3d2e;8l}LxRh JG| #Jae;ږ_A MY}5F#]';8/#׳u@ppb3ԨԨfw4050;C[`{l;>a}@'&|;N4YC"7)qW_p{qZ2>?{YgR'ə _иfﵶ>.Wcڎocmu {{_tauݓ0򎯹++06ܹȅ5 Np K*׻!}g߈|>W|q:@k(ZS A%_tFFss2.MqwGɎ٘)~ߍ!StF Me c51!Bi\F?}**=V%H-HmtysppȢj;_N<m++zzBZOKrLX'tp[ G_?_V R UkJs4L#Տ1kEuLt} ېC5佝旇z;1 YIq7RɌwO nxfaVۡ2fw-k۔-;h~ٌ{42E GlQ$?Ĵ߯+ȸ(lb5EdS'̳%zQ0gksTςX`瘭>ֹ١1<ւ2O#L8g>;p`1$d5 ª )|4(P4 ; L ǖCfGsq-#ʬQ6ܖ<r)-X8$WETIL=xPVTV6dPXX44YN]__v|U֦2G\PZ|5Pep(.pE`Wy+E5ʄ3aba#哋:&S{4?nuc m66Z֎wxqkM^B!&W#f_cwpŋ"J]n?M7f];g]#l7T?lLvnml;ai>K| Xrp TOô߯!IjZ.!t; 6Us o hp^+kL#V"LE9ļh2ՑL1^Ws-A7;49ʔ]|03>=M~ս`{ՠ8íe\w%+EOͲճ߯FЦ*_!;bBX" _Lô]v\.X餤;܏pы *A#z, lT&0Ǜm@H=cJ⾅u9㺿gRQYXb'|9N.,˩Νtn% J;u%ƅ>ۥ׉MJq6r6RtJHl|EWvG %E~Ԫ q?'.ƻQߔ8;=!FGB{QyE8k|ᑆ3bpcL馮zG<ynQYy !6368KK\$u;; 8I"%zJ :4")C[mKx' _rm&GI\aE}Bsj~dCQ] KYi|LV;>uCu e=Sk9-b"cN?::i.Ŭk,,G`+UPȹg54LZFK4bh독~:kmw({0$USVeL)iVpeA;L`]\#J$$#k>=dq$Э.L|,,V(7Lôk{iW ;y1t'j9q+f6g2- .aR|Js.i:drݢN*%]ϘhIgXa,>QTh2p}Bu1.|M+3rW5%ۉlv0"R8|VhV&3T\Zk]HmdJ&D= ೔1pd!A]f f IslA:t(iy&+&+ȗ/0P,])])3lE\V&!k*s% a=B3jy^O}q88F--׈$#22x22UfӋthAdO 1%FMk ~1N/O'IQXR7ٴ$oCü`2WaTzf꓃4ŢC} RI SoqZzNN7[ImnjN=:/ N+Ǹ=6!/8Ιcگ>:sX z@Jm 6/ d/SYIǾV}CnȝXC$"KBDoow< nTT,^#_S펝i|eea9:G tϸ\uH3ΕRLoz> _;St;*9ˠˍnr#le:Y$sQ||AC?? p'D0PdRB3ON>r^˶Tx@ =蘭`AgRgu8xx-*EEyz}KAAd b֢~cu!^\>߾WIkt|""o'_{}o|ɝi ghҹvmt5CC>p,kq眯ꈮ~t_Mv!ӆgh?3Wcl"X-y#.{gw5|1Wjv!$͗ngu.ޥ؍{DxN3^ Z;3^U߫q]ŇF'Q,QA3M3m h^Y{ t4}hhAЋSr?H4aJ`@8$S;f` 4 Ӽg&oLpwi 1Z.B!oT:E'ۼ`,<+Kwhh'#+@ȶoFݒx& g $ xaJ{EjK9>|Xb9o] |A_LkF,ֽfW-eZY@NM@֔^ *zQĔ@iUWkPIHDf]8fN*0MHO;ee^y|bjwP $l7pB-ӸZ::L2{q\k(_~= AH` ʿIU ` jHkJvl=l=rGF/!S[XnVJRnppD|cڻȳw=o{(Le56ݩB-񾘛\41\u jazLmΗwo=}&41øh>XbO(tb=kiNwO .^j&{V#AtA]T_K}v|c:5E8q-mBsJ>˿ SCFk~Ҋw{_юFI;}DNVSb3訆jŏ8KYbO1N-x2[ԵITC_bsC;n~t?4le ?0V@/t .7׺!~j] :knI6tq kn,Sѽ$^^xڋU%Kݬ"8eE555~4 {#t$//:F3 .]"I5h>a,kmcֱ/)Fnaa}p$: ʷʷ x,Xޝ##2֋0:sw2 mDbbg*6 KX^מ^wf3Vr":3n}=~V7}<ϲXr~gțQ]_v`5۔_n>PzWW#Z⣽׿(j= %VgSgS(ޣ&1]u;u8ɀCymm,qԟT0@ICf=ˍ>o5+WV\&U|~~>[HH'mXAiߟ.2֙E5TKÝrC|L[LUkfE.*é*jF!X `^0N|2AخݩP_W9}͹1I%vCϗ %1oZw|Vx;=E;`$dO6A+4:1ʲao؜Vvo4h 8O+8W̑)3QX,pޱA>T4|Nݎ&X&bCےDxmAkffD1WHyݾfEfE"CQJ @| ;,o[cgulu[JR[4) H@2Cw99gyR|xOuB b?WVv˝4' nFV,NA,7fqO†rhh#6~b)%K-#2M?.aGBrg'TsKyYeټ=GH]7uFG~'C:czM'7񰜌mS3Lle~u3C#?o|xp٦:ǗwӻSĆNC v8^Ђb}:,,w .;ijw \6""2b!w@Gt ‹ $\@ աՙ is}0kk11>v\箮_]K}kjG8Yଖӽ%Cd_O8uոָJ[tO; @X %2zlcW0Mts7yuiqt48O1y0:\ABy?ʨR?.^+b9vRKC}Jg2r/<P izpg:'u&ˣR j$%9cyJfzP >vąҦ0@x[ 1(߫ILv셼l@5To-JY>&m'~-UǙt]7F657 P ~ִg1p'31L:?Օscu\BXo=zQ;'+D6S0lO{ܐUKlz9_m$ Vl&%<MY'h0KR$ W+I3˰zpl餪EftPZ(lZ>Op{#2XfR* 7V>{# N8! G4)uȩko^9^9v`eѭRh&&SN}89%¨M2uRjZ܀2i`tFv7ƞH8rrBw< ǩ{rIZooP ha>FT.31ŊjKI]^d1\Ez[`&}SLº( E! S-eî*FՃ^aOf1I 'd'+#R`e G&KS%i19?銓tL!6v9Nc"ҴM7B1NRa'~kst$޼sL`|>]BXUD%=Y7? e0o_-6vNQҚqLz8t9vMԯɆ^*=&`,/#e何H]Czgc`{hѿw3B&zDu瘏eQڄlny()*9/>7ma2v [BBe cX j m$gd3Tj @:A:As"u;(H@V::qihHZR؀ Ac  so\}x'ͪ<<>LLYdUVƾ\}yfIn:uhP<_UXߓrbN-jV-]Cf[p&F7H$ NVQVks]nA$Dw)ኪ V^Mug3vӭEvr'#i?*,,yǓ11ͼXXs6Ss6N:'R'@0Zdg‡x5 6ѯ-j!Mj`~քׄnYq8 V>MV0'7,=^^$#t4tTҲҒ٥UtOϭ봒ɣw7qA@llp"ʼchU+;i{Ag{?k?M`~{ݺ"7j*#9#vDډh50T\gz-bչ5{^r#\emOޝ7;FeWiA~sK[>tf3?H1 ^^1 /TX.bμ]ݻ<-o;n,&MW/ȽMJDLn1'@>{{0.FjrwT `7g7`2ݐnLLSz2ۈ3V,"U $Kֲ{Mh?IZ*|?|2{VZ `ek[[*6W6Wp*ybzpŽm$D[cWnUJ2t5KX <6*9'.='bK  u?QV'Wu3o#9}nAgh-V!pԾ;\RL+Iכy(plD3jv CBr8P5% BJxq{:oS͡5JdaJNyBj-ޥ ^{d>QS5hm*٣1%m-=_8?ꀽB5NdMjH}C^)[p`S3M`[D%TrfS$Njx Nff],t./ȘCR0XOxg̜A &_4a^U' @h xf !Y08u06262=iuaa*'mfU  C){':>a( !:޷eS/S/W-nDxpΣR]W]ľS7tnP$lq䔅֗Z$+mv L}a׵˻.p%;VU%*[6_DyFrj f/PG݅C?A.s0kMM;A| HHf#\<İ*$|}U9r5@85cqgx@ [#;-\29_\IMO6Hv:l;0&L >=0Idj%mFeH֩3~!EVwe/` Sx9CRIkm< h/A Bζ68hƌlžޮ߭1^h#`rz#58nv0^v&$Lj_3҆lO/5'txO<ҐԾG8us(MKj?Vs^EPI8ь\x GYiaݾEPJݭRٻ5cB*.!Z U:+گ2;g- Y?g<rc5fPyWP\űC|g5+ G{.}-|o_kQ| q] *ugҽSկ9R[=YǔN:x 7WƎA)ޫRuF n.bvEyKs'ײp{Fγ^u1ڹ1vu*L5'D 1sևNj X\Wppz{!Rͦ$[|+)CWh %7OY F?950$#;?*Yhx{ n!n";K.vjRo;z0zVXE|"qr2yz)DBxDLeuҶ WBj e"Ѳz~6hu,ɄOD:4'O*LR=FڡN!>W svmDhmQ4UȡsEסuQWIU_Bk!{WVyů! Ld#p˨p/G @nh]]7u#WXbErr:FlAhԠW^#4ɻ*`}H(U{9EcH{$'[,w:MÝ)uSBO xjP{DfN"< Gh;ÔnwfŁ73Ke܎Mv#"_4ܖږZQ7q:92s !z<Mb RL( `8KKX4A͙ Mɦɦa\|Do<`#0쀻b jLAgmd|Z{)EDOBQk2X*`}X"B(E5}d:=(ڏzV׳RLfLNoe"&YTV. ? #ނB, G0+,u_\= IƲL XiAgJ.W`0l,< 770n}?<<>,;1Di[HՖ>p# np@ʸ9+n3ҮR0g#2?E님ff Tdcdcz%Թ88LOK)pW]u(eeF*ݧ2{P59xhT_$19}vC;t5^))Bq 568 Ef]$CB|c6_Wkoר榦Ze{e¨Zʸgg*#ީ11:SKNʑ|0LCzNi1X: $8470֭RVix<d tY~zzk-]^VcdI퀮a$~&:Wu$Bm"r;p}&$䯦aV`xub/W&UT SYY^H MHǤ>ͣIsg{P5Z9Oc{ۋWc]hAl-v;{S߿עdRk/C{m/NJ6^mguue y56t, - TJdkk-H:N_y^"!1+8% J*$ hѾ< E}?@ٚG&A::TTRvۄmo1yĽgHN jx_- MdU*A g5 ~Z~eTV 0t߱t{;O( :#![*fWmW-1b7S2+ C9v2M4># qq:+ [[lyJJOPI,,he%UWSW?]{NZJJ3ո $AnQy,^_҈Y QaV҇V1͜a};}FW;g%OŠYVػrSvBۮN$3:ũ©–'BH_e++DĄ7.UGkkc7c]j;ZaFuf,v\Þĉ!좲kP⽆!daw  ]?6mCf\?s?1 r7WÜDRDRBL@E E ú\(*Fbb^^GӦ#cy G'mKTX.1ќ^3:ۿeQF^eZL^`X3 kB)/3\n&塾UfGS Q.ǽc'1qFsBBHr+{Zv] n<ׅyR)::a> CN}ˍl'ìϬlEؘC\k[q<4Vuh><~~HR|zNX&07i0R*xJWk:%^Trg?# 4Y6Y~ L۟~e;<|p9Iw旱9szރc+ԳW hn`Zxj@ݗ#+bEU3 IDߖerHfӉoktF$V˭b)d@3i6 F[^"EnLSQnTsx0`0ݝqlG9 zeSJ@ S[LG}aHCPG5("͏{$$nlm/qk})QTj[Ȝ``:;=5R@6ttg`QS}ݱcDW-Fh 0b,y*+O9p98b'b(DD琾YF}}a/a/%8ng~ӷwAoZ\nDIzPCW#Aџqq$eo /g;dҢ>nY@U6~{Vaطl`[˝ؼ Hw\Af#ޚeY "L| G6{5cawJAj$24紳3X'IES=6dG`ACO48ORw6IԹE3G1 .ޒ$aM PAGkk:uom VGNYހEܧ-oE;ujt?ޮÐq jN#.X<0xC-; 2#u^A h{Rb7tpSO; #AtX@"b#Ajgw>[ڱU|CܨM0Gä́Q;B׊s%wi3X<۽z^㖹=u9& Io:aZG9Ʈ jN"iepfjPl7m1XBg 9#=?cfS1yѸ3lkUD?9Ktb6s0Rn紼08NQ\I8l ϝjz:qA+XXKnn> X*4 LP)Aڳony:YN "geLεj8\uuCq[@4 g#A!!_دččk64_RJ3($fwL>!0lX|U҆JVм(+i~DΕ0x9Vĭl/bнjgg6׆^7wW(]/]؛ o`!YnиqmoDoW*j{j{R€Ui[.[.'P:-.ױ{лf.9U!إ O ?z?IX=ŋjv ~s@zyNXZ0)Q͸~,~MR03"E^_p)n}{=~6GzyfgqtZK{aۉ`DJ/>@8Vyk_1<} xT&xA~ 7n?I?I3rKsC>G@k齩9`UlޘI aTG&Vv~%Ml>kA rIFN v(v HKhh**NLu 5 5gHI%7F{ǯ1^|2D.0E"CdVV=:$ g=.ng PP}cwQdiM>7v]Q-EG|Cf+x,#3D2ã> [o!XkUNU>|!,,*tv:S\f~⸠.rյ0~&Xq]lHvnm˜l>4_Hhbr.\/1Lw~fA|*f%$=lA5 <7;xPj?VsJ /o':Ll 7i.Hվ EPZ@s wMg W:_cT>0yjNycN cӇZruvp969ǪT*:Ckkj JI/c{B 4a{{K0 5h-"4tSy5/:"$-B٧zʦAT% j\JrFI6t%:nǟ^]tj1)-YS:ݏ`sHIfuV*V}3>bbF /(ɘHgy> g^; 8X~E{.r &bv;thg %i2!n6nrDyZss^$O ba:~PF\jBI$pSb$':(ۛhПYZxͦ&tMOnN~5wΝukfϾG[fd`2T+_xL=׶2b`* qX "qםeU ݇Mp:|N›;aUHsD_[|yoY^ӫ,f[-UN2Ϸ(-c7^]ؔ{,XA,_ D}]1#-].%`b4"C^7C /-8*XuuP"_"/Ԝ 2\\38vҵ񴛝L/zAH,ze3%G'(?z\g]gߍƻEEvX8NEE6&M o {]w?ga+zԲ8X웃U]acM"d6]WhOZ= =Q{5G=Φg"O$s~&x%C .Ԡ{MUtΫ1tGCeX+g"ӂ1VpF;W۳Iޜnl2},* #PP?QR^8-b܉)k̉be fBTJP<&3H+{ o(w 1p`C jF_Zdx-Oc:_$s L2µU/[B j@{cDPwi]SEwI;yө>€t%vz3 A'7DDC P*AApLh1h1{M}M3]A}HzOZ}PLXddQxee_>}&kNꝀ1\céǏg9]HDZI[7Y7 BEfǨ(D{}~/NIu:}.^s>1O4"8AP3u i2S "5됎csCgez-p۠qe44B=/ k u 1z \SH#r-ٖ͹߾3{)>~Id+jʨhn)l[.fiqO /3R~l*vWG1{Ggֺ)a\a E<0p.?C7tO6\RSJǤ.R=[`zgsbnom$t=MAks;_^,b*-^'9o8ܦů+k.{8tCFq~s#ovyU1*o|X{HAoFGC|WXC>L~+&S;TZLC;G:'0 iR٘ӂ3o"ipd^_a _$'>˂:RS]0\0&'|||*я˚܌@vnFR50˞8+׃UҀhh8h åu#]Q8ydzdn\_Ļosn>D\O_;Wc|}sgG| FX!'?~"tHX ;ϿEUp.2z]Yߨ ԉȉh tH[Ҍgs EӖjtB:{iIk:@ԏ!M0[44,~\NPR:,;,7o"BB>Ǐզ5D oT!]IƵ6*:2\bU--/mlQQc!tR#R# Voww@x-nhH>6Nn=BSjOs74%W7QޗɮMܹMqm ZoB B[__h7 w r@9~,~,ؗiZ>>8L,O,_ 8,V=}#O <Ơ4VҊ1A\p+ qd I@%V%֍Q@EE)'#:;:eoCCoILI/ @FDnB%Wf+~{ubqqtKw7;;'qTC""f|yYYwnMV)ؾ졍 stC02(>ަ0'y(lG7t7-8,%K2"'Ed-=̣Ewll~ddM\AO""rrttZZB _.")fXGؒ.mE1~_ &OߙJ+3))\[yy}Q]ϡ44s(CJJήnھ,YRGヒ%%(!ɔ8LDgJy>T62-'HC cJ{}g#%Y=SM[g5[B_n]aݨJ;Z=55jĮ`xзNa:ng[ Ð#OQȱ&7&7,< u;bAuʭߎ,dd+}}8ރphk?SUT౷66:˟L#F\A҇N-!U/3ߵЏkm't{SrgDKDKP=5{*Tس3d Q!;mDx@oIY )x ӍoF]}j8bu9Gŭ~-j6Z jUf+hԻ9U4ɩw>Ao$et{ vs:#;_-'ɾCߖ?ʸ-GZSQ\h RjSϜ{ _}:7;StZ[;UuI5TAxP5Ka9LtCB]-+Ry)$btBO#h#INv3jyPF'Wk$.ƬQSԧJ;xJqvQkU|yķ|_a5^̍2Hee$I,Ŕ~{1˜.]@Eft*H^`4 W骸p[r6iff-(C x;`'*HCOJDD^VgW<<|rKL1ΌΘŁݟ[rW?&:ҼWZHBW#>wڑ|E0n1# (fl[3pqi60QMͱغ,,vGqh׎G3S4M<&ógD=8[[}iyHd菫cMv FM"({YSik"tR)*SGB}/s j u9~X3, V8vw'BBmRheWUW =xSpW(~P.kýiQ84ޘ5*AuVM+|52( <ä|jN0^r Uu3)>tˆXSRZAベVr\M8iաOe C_7>azk:&\y _9\t\ZwVrAxdxLE3Henkcf*j1f}6 Npr?J!v!6SveyOCw%Woo+ >a`^×< QJ mzq(>,, 6eed5R#>X89yr8%(h>ǰ8mq`ަ֦b&,]  L4YULG6Zx { {t΢'gц*(>A}gbx`|<4|y^La ԙ8FFwbqf*}K'X[zt!zVR: nD%ַ3nY{g䳐~ֶ絝ַBKdvuς4JJu鏕|AAb#нUjPh"!X,=<ՆToAtV/ 4cDz4j`d^`F0#fA$HnY}ٮ-"%"EΏYmQ3SmFEƄ(8RRkD.apEW"`f,їϑCgPRCwrܭnP!^!`Jz `obxFJAXmum9iQWW&?FZ "&5E5drFJل.mZ8x":HM)&i:%1IhDNxcILJ 2S6Uuq~`DEdnZT#0Oڔf&l,\ulxմcç<2̑n{#͙nv$wCDt$^b+䀫l7ۿJzLr`|`9W7X&`}YֵnNS@Y%sV>DE\X|}_^Hsek4hCK_pRʘݻ(=eXhM?4,ĕFk5? qtTNwQh'i \ F;i%dTsHFd$>-l>Q8GsM0}MgA҆1ˤQYf > |8ݨ/D%_D;, C@eYnnzFx1FF3$ NQD8&q"J2$2$͛\ :,e'!"'@#78~=HGF(3!2"3{ddEV$'YYه!+"E}s8 ]wwu K)Kz@0e&D(D@@_2o0)ADT4>JhsV"c6& >GX*Y*%DBX現vC,XR1_bg71DFآp+ ˪ʪJ̳+7>TgJuaή0S'Ӈxgɴ1$2ώ%*n0\s +Uʔ ׳Hfȧ /&c6Tg@!9BuSg|)O,Oc,@~Ѐ =`*ObRZ{P)~5~5 C rmЛ, |f4J'r,m? ^)SS$~[rr}8m?&F8O =-?azw6p*f*f[=BBa갓"Bv8C߇G3K{q sO ۀׁ(k,[`X2 {B! & :v+=B43Ԝ>[k%Ke/#p۱ jXFz$5"N3 5wN\ARHK ʭ&۰Wh:4vm~QN(`4':A2)#z/lö71iO(2SΌ8ìXnr$D =(w ߧg6lGJY uEF^V5$h ]d2զ[j%NcnQv,{8gU1wEZwB-.In+@vmU Sf< T {^C:C:QFe΀/::)xzu8Ϗ<BÝڭDbbDD b܅fHA" - )4)t oDDy߶TaUԵ h9TuYkL@XC' }U= GE1ki>|#о$ل٘{PÃI6]atTƵl~m[=^\JD1ؿ4v ~}S)^|)&FnЎ7 8*[.(zi_fuoF;Rg@ƆO@%g*Ldpq9hj𺳄惺t]nniyH]I]9-`e7"wg8Mf;j_#ZqA|FռڸW..>r@JJ=<55ϖ=W8W@f:d[qdMXOXʄ'eS߲%YG|7ۃbt7* bk)LDVp qJ8Ph w(ŶMJghOf7N;O!q1@-Ss?Ŷ]Ex~2CbJ|S ]qu7?HJ8YaV)b,/cdK[a0|Ѯϒy~F^>~AVGBnu[8\nLOPsɟ Ecm,-yaCvx?7)'Q/v8K/ؘ` m]*Č^'[uM8db1+UZJ^mO bJtf٥" Ic.$y#N۰ hB;RakQܩRw*F^nHӯ< obSLn?:V=J)UۄsvY8 .ް%öIZRupxTJv8Ͼ@U+}]N"},i077O&Ơ.3nϱ2,dOj c7B̼mZ:1F4XtUϗFlTѯߊdxB@GوA ̽:EO᏾$l"l2X1zWMC4`C B:@bZhın m>q.]H{rAQzzZ)@ J JVEt-l-ΨH&ی tthx @M|' t$/6 3AC,,w &LGL3L38%N퓈rCCә\ߦld@'ەٕՎh{9z;wK(}F=)! OKXܚJA;c0 dASKz|Ih59ʑ{b I;v紘gz-xʹ[8C/iE4ࣳeop\O11<ZfCުiK~,4v$=8֍m+1:./ f)ySj1ב'&džJNnt2ix`B8%vfytw'TFt֯hR[-tdY8i- xXWJ|('8+_*𬾶 ۰gkC!6FFžvq7~ZXI;a&N7`E8'D&Ũx*IVehr֗ޥcͱ!)T "_~XrSPoۅDAY4vDI >@KOPۧ?q!q!To**ח>蝅Ҳ .4$r[!"AmeBy=gF+Tkkk &| ""9 x]]4}VTT Dc^s[' n"!l>ni%ebL>4l|*4ؓ.y3AÍLKMr:g.e7!$8ï%,y蕋f[Coe,xn:=: 툆EÔ{{:;"+5|a+EE3%&Oz/ahX)i >Cgg8};@a@a]PlTXTwz'GI}wjl[n hͤWk|F>?\GRlhh/ tG99>f}&44-iwee f_lx {>|nٹΖ󳚇.BNnœޮZ7c {$Jwav8dJ4w OigmfEQghڞ&GCJd&i8!G'lF2ъ杢i{*dZU.'Αվm?W3ظ܆zɚV`5G$YBUziy=s+BN8^g4#\n)URWs>{{U p pOx\c ϋE2| x\löńIgp(ԪPPڹ?[ӛ]MVB6O~tHsI7u-%-~ uFjnШlQā@*Rb&P}Bnhŏw vqz%IcIc =DlZ 06oHs=e.B+lmomyǨ^PF}X ](^;wI>tt pPyyaWIWȚǷ@D W}ߓTQڀhk 9T >F6%%Vi5{@婋GLJ{R ^^Jl>4ր-cnno3“FlxfC&!1YT%n)BСS~T>2Ǔk￲<%#V  m%N[[N uy;}W_T򋸊B Sd@@"q 4 ƏC%*3Z9HIC/]AES1G`ԉDmX#0 d@A٠Wm(e8ۑSnŒ8f&Z]_s9| ~ĈISl &#qN[{.<$yh.Vz338O7itI`'dIv%vv{K&EPŏZwO]CmdF 1!q{'UD#"`!78mf3HA}SrݱwRUFiRN$DZ}/NCu\xz[6tC9n쾭Z1x1C+bl HrY7)Iѿ^֭Q?ҏiKwUVdB#>hAX&+J ^څih,Gv֨K1y~< |uH+H)1H)B>yy5u=)YR+IbҺiގ&u!H}J [Vvʆpty.Bl#dWYy}|>T Iru✨zUnG.p+@ak^*eLjxʷbbJppĊOJpB.zU4RP q[|дtT6bB˞ a*r9KuѫII]eDqq=>ٽ'NgXƚ PV8իg2jKLn؎؎toj'~3gn-aFDx5fgW)W)́2mkT/+Cq@@ Q\zN1䗦N_1uEJkDNj'73#t > V 2M'71~s߻ŠyYsPРNs繰wi;w30tc[u&G$GX"(6Z2 $%.1>=ЭxЗпy :0>6kl(DR2CCHCCkbhl)-Jzvb+6噹:@sDX؁D'EEx007zR`xsrn}0vë\O\_<&xI䰞#)}.灾Ia l\Ѐw-k?i>O}#k:" Ś}udJ1J" 0o,tijY0]:|U} Q12>auE{uu1nՖP*AHhي0ii(bSh?s?3oǂ݃ks.•PYxd+P:Fo$Υ}~pj7-5`Iҡ4 _ADt\we%5'RRI,,<ͭnnG ד?қh ΏN뽴Lwo!:6Q=:Ò"_x`.LIs2CiWLô?87z3KI(aBkgJk--0 fWJGsd諞V( we |U܃-oXiWTvA;~F,Y%`6;ʒC:HEY|G}9iWo10,κр68敖7q+Q؇d?d1 lΣ6XxެEL "0|aLô߭5Rux4CH}FʿGwz1:uk f3KzВ?4umFrp35ҳ]:#AUO9AC%%7==VN?-ZNZN@UQUQQaȳ^@9Ȓw&h>(:OI4@og%H:03 rrh)@agQڞ]]s { fX%#7OrŁ@V-xInFU[~WPP]Aa*8;Xmp鲰0gϪqS 3WNw q6؄لU6%$lu|Tu3x<\O_Q]S.џ))ci}m?-v"Tb7Y pbqek2ii'}M<{Ui@PҜs+BvuR1i]|LkcȐܤ]Tv4B Ͽ4Uh Ǵyָ'X2ָdME !߼g[8F/Gte~u3Y"yF= O׉98C wr`x\4/ͦTԺO 0Ig ! sJǧxsu(mx,vbPNs9gi!*t[9ɀ?t8;Wj^{م`ẟ׷jjŞ33]vې/مd՞^WFU<ྭ@A3+ ӓ9ĺ`+f+&VMM*;nik#z#L]@%bGwmz,GCbWDF:ο8dATNB'**׾E }6V rXIJgt[yimL d^iFG~,PJZ]ahY3v%QhLص,3B8"gh*a-`[iN3sR1YVޝGS?H32eN W I)L!cQB!e2qe3L ݿsAk=u{q?kY9'C$z~z/<ٺb'yRSC"\} ]{r9wdUb$Mބs3WzoQv¼I;gL,)?-g,<jPi|QbГ^I#Ӊ&QlH.7V}T\5ա'X=HKJ6*NA继k6=kI5>Eݓ zN ,9N?Y+KP2K-g#5{aQĝ#z#$"cg񠶳Z}C\?PHYSM@Nꋅe`:%.Cmg5#"ylӂe+=]BXhQ7 ;RRJY`zcRK^–۰ʇP~L agypI$Q軆J|ik5-1lkǀR¶Z2 AWًAxZ ?iO%ݐwc!Zwy0oo)@Z5  Cl6H#+mSvґc$ )(j|r--kp] dCun}s߷U-c'8jdP\gQ$g\H_nڈ,!G:3\3\{Es~rr~Vk8rf[a*Xw vJ#5M'*PaHЧ!%0njrTro2o/jk<\}y3۝m?c+׹>lh5[J⤯\c0_v]ᏯabnonňN6J{慭+Q|2@|r}}$=)Uؠby=5PÕ(9NxRRG&e@ZZwo\\\-r[{KO=Mj;-dN_uUB"(sh\F]F~ Pڌ>?,#l-!oʣ{aR>uvƤiPگN!]uu_>#iM'Z>g$)')khUGI%HfŭÍ<h칶8(U?jPgR *G^ D+]s!S%2X/E<6sՙ1qZr賢4Qo/gmznfE0X VWgc4nV2!s^]Z61*K-5,,§Oq 'SBnWyV(Ɏll[^*Q]KTE4lDP=R2gyy[dRdR\|q0lh,, }cB&^pP}k{2QlV}lb@y`bgr^g +;d1x& e| ;>t8į&91`ٽR ef걃n=/GX4CDkVkVkuܪܪU*}E~x[(SN{tq^M PF`K`{uz\*Jg;;FgDg 0዇\\&6k۪ӢSaMǂJG/n~wݼb+d"4_ݳ`d.|&|ѡ{L{Ua)EP 16SUdXk3'oyzb9y5f?Df0Ujބ8F8nB+vh_6AmՋKgL‚hhb׸(`GX.?n*αvKFb0a}t t]ؕa6i3weC= M}&*UnI:P{B@ffNSxR @LVQ%C#(Ey!'9M6@tk,lwY.Ynt 43 !! Lyy@լׂ֌ `E,mƺo-kմDmmn^&-3s<Q|Sч^= ՘0 o-KՎ4H2{E8<-7ugP|c2U#KBxM#SuUe] I4`l~8dGXDDwm'aNƲeXw;N,d-PNkSlW`&ʅuov󮰣/0BDӏDRzET_E h,%07+!n"y-o65[=dV PgGKJ Sehh=88C#hO}V}vBbaa ~L,Ò@,bzD\ϛ5>_|_/ 4{j|"豐XX^XfFJC%Kl<9T9Rg]m2W'ٳ ":On7]'r.>lh$_G{ۑ<Į;c: ]ӷ1: nMuWgo]˽7sv߲7'qٺwzn9zh3e^KXP5mB dOxCld Jt ^qZA wKfǹ'])&B&xLnlQR [#yPYBsþ˿DŽdvMBş֮~9AP}Zj۬"%Ɔui%;.J O)ȥZkYA *CVc}uyUiƚ6c.EPLAm "ig2 U^V a u:6 Ϊ@I*H*FR[#ۙ>r h>iiDsg9{uk@E7 Fݞv$)/3^FصdگEtj+Oy qչW'TGC.~ʮUU<⸀*0@ `FOx!a'@F33Khy=3ZCʑnNf Ǿ w~(>Wi]i]PPT=|s.6>Hn:'lE~,r  uPࠀڥkpـ_qLEPP,Uɐǐ' (oK4k20(gAXAiB)򩦑XT3wc:7ϫ:|vMvk[󡻄ABY$FCSeS"%ò[o۝bbؙ>b@+lQicB;INDgQB]7&ާYF(-" ##\:_Zڍ  =z굱%,;!R#[2mht'@Vp~x~X% AɯZҪ@[LL9sϼ̼lf3ٶ灋5_lBOazcRC<;\>u\;~@ 9mIO"]Vh[S" '03{R}l!kjx`C7t_6u]G㒩V0jHߛ$B^x1;#c3[C7tQa{sORY53{&;rs?ɫ 7 g$yOi-~s;]=OK9-cIREFyG\<8e#D{D%uCVOV#QdmQ1 c cZ[=xO| ޑ[٢ ,,^@ہ;zCAO z,qxC'xF1] F b5\:R|K}h|IB,'4'T Ojj: \Fۆ(@E EuY|yB+bPY) lnf_UUïxQ"Q67672TT6leO7C=w+k)`XyΉ[>Ny3XT MdnmN11'CJJ_99?+o 1nH.CDfg:,9r* Đwhze^*<7<7EilCbChhz~^^6H=a:0 -C8/!9 |zJn"/ίu`c[/[ŒJHJHa[UPoUxݯV=#}7*OS!!Ψ{ Cc=ȕ Pn4>R6`K[s|c,u~ѲsWs#݅%5vkgmU3lf!2]2Kim_$}OOՍĥbYv;1;xGHkN)S//5 iB@ӾNv)\ `s|I \\Q l8sbr\Iǐ$ʲrmjT]0pw2,6O+gg*gjfjU~*gk?~&}](*x@J _vQ@u&e_nIbԨk\n?VJP'd#'yaip5^4 :%hn]̏ZN[g3?˘"`{[;F&:K7a\|K%\DM)6yhkxW% "֑~wEGA^WmtFIbd*8]s e 2 |ˎrS΢zO.i\(MebU|@Xw)S=Eniu"O=ŋ~Ja:k ~VGfqͷb.'\)w|i,)WN>axvHU: =,(vnJG++b/¶ fDp~3ԫ w*SfelE/-9O9O( g0K> -!E4KDAҴ$I;pE5螾luȃhgewP{M-@bc//1 Ɵc(ZgY{EaH܇O)g2~޺LL\Ta@+ll{ v'"ZQQf v22sF `x/,jܚzvsF{>p&#R|#rD*Y*h }릊\Ef8>%cZQimwz)fß luwZq '{k4{{|%KCSr) ײįF:Z=^7'?#c,#md2dI#7׸UwwL !}|?n ||a+? ]F]n: " 0h!:)JfKfK'"&r;wMǍǍƄDŽ3lѾ)8ܻ#,B-p-Cma1vsFԵ<בN,ÀcCNIX4aa{Nu?p !2BHHR2#;H9FVfF6ʌdded?|4t]O:\|(F_z#ša5M O6):ȻWhGb4>3y<۵P_Du/}-eOFt )EĜKk:Qe/t% ݃س5 +Xu] ^% z|c3jWs(IxrF@P" 7vFŠRN3\ K(~fa}ݽgl gsIޒ'7~/22o>0fɳs!}atmu思"P)Slrd ?&뉕r9yc""|Da @_D* VJ( uDNЍՍuUҊ~jBh7QXVA*!`s~KYh % [oۃWFО1_H,"%ֿ"C27YKb/ _WvpH+K+X pF1G1e`0T9E9E Svv4Tݮd'=@]<]<2>OAPw|TFG}'N6ʃ͘FR*LnF5cji^v/|__ݔp>_ynml);bvd= ^؆aBeD3_ZZJUY/8.8^θ]N qFFpIt[pD"gv[~'jO$dX M?:6{~s+}JW[L\K)~&㭵U@]~3Ǯ_fQzÁD~#mP}Zw'Uhwn:GdP%YL/g%1Cy"SfPUNd Z$Ot h [ }ŅԾ4F:0n8rg?3fQZbڕ{gClX?zqW9̻·^o#ʛf=/V%P_mDd8t,y+,w^G\L?ad;Fz)R|>\-;G+΢VTfYܴIa3]Vh̡s5 3"ELE˂vXZ} a[b/om`(ƹf21'%jEڷũz7Sy| unf/EȚf@͂O&B@in+v7^`#+;vuĬ" )?n?yM/w}ih~h~GGkPE="@ЖX?wi2HDbZx' d~ 44AIm_@$!nXXK Eq:Ryjj->4<4,A件NB:pJe=hb&'9?)1 grv.POSc͡ZZ!d} ' '&;tNwN+bNj%%QϮ-{z]}D\2kήPS*\cJ<-ǿ@!q Yq'+/yvA+cuN`9vhwU 5[+ ϩSz7GW$ ҙa25fY{ 3iDL*QGbIus(4s5fZ$&ޏ(/S2IF8S=aO2W^NJ sPگJ[5Gh$Ii$V:2GK.PSi%&0uad ~QnRVAWher25&O ^2Eo$AWXќt0W| } Dp{]8nV`};vGeٳ AJrj*Ft!fy/\99Z@J yƿq,!DO1!Aϵ VT7TӶalJmJYX ?wbwbzo?Wz5#lZr{*mHzkJ[}ȽW QoQo>#s*=§?v܎z{ .._}sQ:Z|sm 7Zein]H1I1{^GqL5YWs\&$yQt"qmM*RP":1?["ڗ Ԏ.1%''"hhXQ6XSgύ"-5AFXXG?{W.i:b1lWGyo2RRdfen?޷To|>sS`@qlgu9%KXw.+22FlƙYTDx]*_m'ӾMq<5Q!)y%9k1 탢t7Jt7cE֝-wf{Ak~أ"s]'AVR-$FE|.k"v瀯 /"]MMJk.ċVȮXrt~Sh[Z,1V79HhaTc -:4P}q~4ZY f1xK[t 0]E8yA jd;KWgH\FnZXޱr3"K]Pگ()*T< );]st| ܷ>wS 75!͊@[Y T,\rdvS{7|~[0[pzS Tū##ڂn WܮxtL=opMaRbє*XbȽ4F:4|b gfT >BB9hwg~~A&^ޣVVoL2v+cLt8봔8Oxy AL]=-v:?g+ߝ%l-NnSGZ,0~Ij*NFeP|sru3s7h{ap[CI;Oz:_X«aqvJ\0r3`7T,FY߻yM֗Z 0:J>xAG%[qMI&PrJ7O`>`!Q*XgUPޑ"4ݺ4H|fa64T!{o 1B K"i~u?$l$.d:P ;rs4lrx'R$k#t$tDk)Jߦ R>O>O% '!'I/&@90b5 8;z)ffjil APah 9 IY[|<ռ`.22D!hBaAaabŷSSRV0ꌗ˸m|'722?;q[PmU(3#J*{YEJ7-+'&#-)ݟsi躾vy>ޠr!NSV ]c jYhYKv14YWi%wE +^r;ͺml%-vLM[@y>rD$p#Htm)k>Yg]Rw*ڶЙ۽_D{p+!~nPm "InK5MMVVRuezp.m_0f?{{!ǻ,0t9Z=KKVݪ%rj?wȬw?[N.̵+?2=q8C"@%+m^rGS|l5s]ҘV0ȾA3⋝$uK$8 r~vUź{E3I_*dYgD*-&֞Ηы z Ƕ_lz|۵;_)Hr9,Cr=Emݺ`bD0#۰2C[J9{٤vl=wI(a>E7؆m@;JĊoMkG-̹sq bʾ<(ӿTm#.vv;aʌ Ce/j \Ziki+ s N&Zk@<=֓Bξe1rW9S\4.hWH8?tt(I0e5eMMZ}wzd[Qb 슮hk%4|* #HWVd3{c?brppy np6:eOMqw̎i,J1*gB?)BG8VO>)UQt#bu$D̥ߘ;/t"%<+EvдyqIU.t t80pV{Q _..)((y @Rsw,Ä Uׂ43}B VKDc)\sPf6Cɹɹ! [ee11014N01,Tu4nIzĻGU_(s4+*~J b\<77n iYe)M슷tùj_SWUW#IK\Ƚ,N|H:y r$$$ls-szՃ&6@Ѳu|y~3zs(T8}||Fjأ!&m@4!}0kf|JR/956Cu:p#i+'c-a,t U!׭ i3JܦrvƜu+Ww`ƣ,fhD9-WQ;uѫQ¯*:C@=C0P#D&d6!wZۧ8v7(xcZJz?p@@׻Q"t  ۻNe,EO}mV νzBOW Z[ʫUCs刮nxC $<)@=I/ξ8[WXW))} ?Vc* 0DV}CE&HV4٪" 1Rl,SS1ҰҸLxk}ؿvdրMttp=q}==8-YiWn ]czl7R3p ۰6DD/G/K?Os1FJvD6:͑ʪ!lkVYF|Vs>HTW1=b%ۋ˗:t x*:(8ԏb5ޓmYs{e+ pq|r+löubȰ kv.D}.JD4tiJYb;rnIKz9ӾwmzV^6O+^9:{9=izí5 jOYRHR]8`$" $ڬ' YY{:*"6D 䢙(7c:P9P؊XX(lFmFS_{ c>uu=F\/АJL{_* .f RgRgW 0Iy"OG# Z.Pן׏cOBNI\O]~,fZu=5 l41!Y“nWf'Iq6NG~{jr|0uɬȝ@sv1x<)ސHaqN-A|]ޣg] Fui Wg)E>:A4sYTa'ށwȃ' #- (* E;퇪FpE êjYs1 0Ti3c-$nRT}b)7&YmVygqa1f\^sI ^$ kOS{]*mAͅF4&EALI16QU:_};`Qm.@{NW_+5JV3P{޴:3ȮQ'\(#z#ԗiҜ ˷U{we?p|(˒-kH$""X "cȾfIdWdkaRd\OLy|sFWNQsɨejs? F" 6|Ht]hD44`N}Qt えtTW)`xXQod66__tV>vnٕWdpwUa6]n/hQQu{Eޟa2C-? =z\/U;MrMXTySb3Oss#-|io[(n.̓LJ0뽴W|4'_^Ho̅mS;OҙޟI_]^nm_"c[U;bS܌gʇtrr\~ye#::RR22[ p3)j^6>A{S]tesU^Yo{e᭎=(oay$^h 4dlS AOŸ봗;rO#ED2QaXÛÀY&sy)sF;6 ȥ)Qj5I13ΣYԳԃN¶[siciɍqT)7 mWRד󿟵>$~Fx5C}~9#_G[qNd+c?K$) 5*n 5 -:^Dd׫iTcŮIP4# \S -}E@NL >*ԌKdIzHFlgRC51y8t]' B '#X8|شZAZm->Z--Hp]:!s ^jmi|VE0ӜJ*ѩ3J6/E;΅doT( gdދsyacC4-,`>>Vw/l ]! yDU=s^Ceenԇ4,'Eف!b"DU|K'xc@>Pa0ݐ3Ϫ h n nx@"ψl_4]+ 8MUu/4|"6s 9Fҁ zѕW*Ը qYSWQ\ϠH9M2MbйsF^TVLL{tdbC팈0ooWmGǾ xg.Y+vpSJ|߬S3B2Os~GݩI*Jqֶuv+rZ"l-SH Y3G]3[$ۯihp F`tuG_ HAKPPgLZ_ vN,Ƒ&>єD225m9HwÆņeQQe6G(q(!FkKjnFm$'EB;_mK"--S9/7H99aWؚT<22ؼ72݊85ìz=%e#;{mRi1HEt\֠wG^u-Dvu%t()rbϮ?-uaШ/W#NuIL99`=si,jl˝&ƛA/?8Amz㊒xbjSAWkѧoY 6LI7ѲC~jvk,MA jkcx^N2 7΍$=f7 vu`-1hW?čj &h>8EeoTճPH)v1-Cz!f|0AkLˑRgvCj9|&&'o:¬}- =W຤.ҙAj榴"JȒIAk#xe1<_Bi R=!tfJ.˵+1' 7l^])HqBq*ҽyIni ;|߃הћ* T{l7w Zr ̷4",%,%i4CkxyyR..YInHeLed92<MRXT僞l6:&ӾdmA3b:e-iL n!DT- w^imf:$x14'})ua~O}rYPFXx]s6# ا4Ä>'AWkč4or3ӯ.L# ./ORZ%3T jPC]:<׏cOt1%$,~0-݂$,T6gp/$`ϊS#isF3[nVEdm ö>=B$.k #_=5iWiWdu֏-&_G4ku@,E K*\ß bA-⧩@b4p2q((Aa$$ԅ:q(h |.YCc_?zM,!7AM,eq.7u_W{9=Ұc>jYQ}~sfo)4ix&rڣ;ioޥ; ]bxq'd[{pUsqTKFb;eBkS5w4Φ֓M5+^"a.zmE*U ,k jMst;fLY4!S5(9@lSM~jijPڊNݼuaq-؍<< jzka4[uCӈrġxwNii~V]Zx?cw.kƿjP;X;AۧC:4=$Ÿ!{}PjnoPШ*PUKHr80Xj< xw(T)#^r4\-'lڱi?hnd [$^#ܳ9 &ıUclDE\@+ | 9kWev# 6 6/l :/*%I6j&rդ„2g'FI%k e99;CGŧaUHHJ I }7oV㽡w2n|C\N+hg!x"/NIS$'?j *lPTGOOü氠XPv{SHm%i> (@|u_ނ.]jswTv紁<%z9& g7]5D! gEgEGWVv㾊L Xi}vy-vkWK:n@Ռmf"HSFޭ\#vһ;W9WbO- LP01~8Nҍ*ÿ(vQ+h8҅C7}o{t6ll% D`62qK]]b#HF!##dFdH2cd$n*3&!8QR߮)qx|ץѭ3;9N G^셷#LudrgEGyl5FHu~6MɜAq^g&<')̗x+ b۩:yZn^+jWhzVKDk uѤs!VHۿ=7 $=꼟wҥ+Տ G% O?ןl*hqG߻“"t :!~*iR*TWTFhڈ'MlM7A?iKcV$ .{[;\J<})^"K1Ow]M'ۯ: S>a\6[C w`kQ| fx։C; 5'ך[;}3&vQUn >w;;;W\Ώ y[wh?9bg-$z"DΥoitN96,j,l>иi:qBmx6Jk̇|g]\tHE0V+9nn3?mW7V'@ £ԃdpO*/TFmqR3GY*ב&A0tei}=(~|,K϶xMqV@:^ ;+PtN \ǎj>1`#eJw@j-p}DD%S[>,@F^^)[ȱc ͏V.4GŽ ivm€3!b1g˽Y"[l4C _)B_]Z0>Ӂwoޑ~_MOBJ ݱ04-۟ 4k-2-WMے^؋4rkf,;PspZyYۯ97c ˛ &11,!RL<~1!1~V84dFfyVR5WX)uGߧYf }M/Xյ/SJM[>\V\Q6)@ygT䉞= ޼خco _nabR*f_֬;ĖG/%z,OMѲ.>n?h_ Nl0|Cg:nGڤߡ5k7vbb`\(^DNƭMA$$ӕT89|jm#  Ƣvdi H8 ɕSk$!ŕ K[<5=5 bb5m 8by|go)[O,VfKtl'TZ9Ja+!k}m9>jE}JC'ui2^` eI mU$n@!u:m2 "))Mj駻 x}NNNG#]ZdZ${,{l';[pXp}'ct x` iS)gPkOs<||fXNFrrBzlh[[@>&sC %1S7˭IWϐ쁏][t +@gʢAد)La4rg졉[rØP,n_0yM'y?c>mVK-4"4VB1ϖRtYe7C7tZ_Vyݪ~XtK0{k4Hva8 2[NZkS~gbȺL߱gʉHCGn/ 3,C6OvY%~`b+cU#k3%?^ƹJ8'lHQŨ"+Bsu/b&{Г hě۞wsڤ/ٿ6GK|8i<[stC7 /ޓt6_ uRu9t eg8vkLZ&oUn[y  [xXpݰ[:+pc fvC,F.$ǣVC)%5>B{ U:`i< a",787Btsxg䞰J-* --R|ïDZ+}Q@n}U?X G$ڝR%;]4]j&vK7󹦹1ePZ|.oݮcH}ԒܢD#gl{Y/(!lՔ(E Rw}:C_Jfy).ms㬸}*ﶧ=jnO{+jϜnoyVMӦ-;2=&DuCnMUò΂ C)I1G csZLrG7tAi|jORIH>J˶.±pt^nom#$Y,Qv _577D1:YGƗ&1znDqՍd&L{s_|xU8tA>uJ +UUR17g{O~wj7|`x FړF4G!!ikJR/10B-K-KUrO<0×wޑAecNfqZz(0-}ksnJ G@kns$qaFf|^'ZMg3^ XU25>cPaiZ/Kb'7tNIvRQ)Emeg ZN={whj2+6x+] KRReyV/ ]@-E J AoT;R| Ӂ鬶1 I\>vŨAJIR))v,.X#X#`+M(֛ԛf}jKVm.z<+.{B)cyF[wǑ;fڴ<-/kzfKkrCty+zU{դ8Gee8gv? ~FoͲ5d p0ؕjE-rA\Ux m6k6 fH~w/q4̩e4=0[" 9hlil 焆N*ojٙԨԨ]tӐߌdnc|؆W"w,t88>c=; 345G` 0HTI-YSyl5i0&dc'/;1“FdICCmY'2@{^v')yBSy,I S3&N+(F_aOLƩΐ5a z[_Dpc!x"Y?e_7x1]W2 vj=PYf(Y+;(LߥIkBj׹?8%]u>tIn<_f !.`m 﫻iXiX)6ٛy?ު]4(_DHQGS&^@$pTq35{TQLiiual8a^tɪ K0;*(l)>piP]K#by7Bđiq3(4]л0lA=)yx+)JiKi$x(CH\?`\0zd$HHUĊe@ͪ S3l)㾍!!Q55+ ^}i})K2KB-urr\ i?'YNAa1!RۼğPL ȑIIQw~6~6UUĘ˕ʕ1r7̝Lw`WXI]IE3 fwFΘ FgD~vmeF~k"qUR ]>d]\1~gڽgtY b!42ޖUB[zrn;%k2bb1 hMHDmNdi99W*?bэ)kNU,DhHT5İn|uTы1+j㖺^Iv^ ;Qj\w^fsSynӃ'yAVƆlT;Xm#)N%+@X͹#K PS.j]z()r,8v;|bYsD;Għ(1R^Y~/ a/5#W[<ȫ/O&ߙ뿴ikd$B[YGp8atL-ĜNkW-ua|| 6GF$"rXLiB$ {A/ ~gGhA@t=[^TFԕj S$|m߯nF:.|OTnáơ5&Z7}{-q-qD }XV@!Ea dn0565V9dLXȵBoygc ++L:54(;(;>-^.^@rWx!`,D# J{ZO@sǼ[_16,-Zޑ̼Y͹s5a*ݶk))<#)XpWBX! ;QȳIEGo´6y4"!8Nqr{NLלE ْsPDtlh˝Yk|oFR ˝pW>w^.rYXmG/IDyb8g5FՠF+X M>$LHmOrug8'5fE<~T{3R [K1BZRZmtoc)m9 &\j¥t\/\K\%gAXԤQ4n*>4~xn'm0IK^oߢvÕ=DIۖܖܣcGHs1V,Y9[I ZcUDth2v<AXX:HB$_肨hh? }:[+h7FI#GBi2߼J> p!k>vƙ"4˔8иX=-jAA6!N㝄w$hh#veg?̧XI)7jgWw){< r|f,2#K~O#Y疞WFy@v:;+%Uߩ_lC߶v!MG:rE||X]%&n޳ii}6DeL=&#|z`lleP3@k$m,T<s&s'ܝY2 E[ouQQ5{ hBHs94jPVy Of4x`7\+#%%`",_yABBxH,Lmf`Y v>?;O/:TRf9srϮgP h݁'mGݑ1=@\Èw-HvEsws9d~t445R?B6US4su]uwJ^nI(n#: 9 A(rQĈMMtaٳyI11 MP^U0mllVgHs61:7z7 /xޛF0T0sHJpOQʭy܅󑗟8ܰ\ܛ7)ܐJJLjRY9]?~)Yw7>'8 yz7ݟ]bARv4 v= E疭eS>I3z^*%?x{^?u2 ^6oF]7 t=>bX3]2]zOfa\c77qh@ D2 `cIa9,TԦl1Iqn zO@Es!? Ńs,::N?V?6,%ao3~?'%yApܯL I{nM t7upzzDzӯPP=E| IIOx//Tq|'̇phCFKG&n ۃ"A* ̿$l:tGc(+>`N4]Vj\Iќl:2&gM$*TלD g$7&EF !H:j8N H -] *HJJwwwH(HIR)^]+0;33Þo'FVݭF7mzjP7l %v UzHO{ȶd쑗b7 kC j?C#'⇋Nr9ʻ@mjnr7+?ƮXHZq^y@M[iI"V:FۈQCP'DU9EϼyL~_fdX %:P?Ք =rB.#VSő)1KllB[#q. j^;kPi:évE!܊Ƭ kRĖ7z ё{Nmc s֦gH;VDc \C^%<;36"Z}pU]WN Qdw'odstk=l4oweI )C/CDSiՅ5|Y[ȫonn=&܉ъXܦʣ& /a$}^ 2Szd%Eqx|&suħG&8v-Ñ###W WHu²²Do-`+j@ i91+9oB.MS*KH3-ߣEqD%"j8 l">X2T:_f PyvBk+ ľܿ\( -_MucatN \~_][қ $O?#Hd HGɘHOZO5>a2_Ngy H@~|_gAĞee\8@B̳0Y}#.dd'1k#k#"'(=mL!##W ;0\D/ܴAezӉӚMdM 0[VЖe-::2IIJȠ;88U%Z Yv<'Z`ڞ BKR'@iكkF>.,ZA1)Ch*3Ȟcʶְʚ·^i~1x_yŗm&+(rcN>HPڏB#検gP13=')eÇe/YȽB7iA;?C^c~o'1eb_[ :ߙB]]RV^DsCsuȭXsG(( H$^s麰uu̬bZ: 7!u?3S;]]V@kkgS[m009hudd=6BV0s@F<|%O˯^^OHjh R[ ]**/);ا!ȃc԰S~Qwxf[_+_ Z:h2t!]{1?rOek y'SJwkaml=5Fbq~aߒemM3Isu"Jm`K4J{?ky!=ݪ=AՍҍ:(ORAR|r>b@ 0zmAIUUAx f^ lC)ЫЫ}?#P9aÊϪUMvt*|]h}h=_.#_zBs|S`fᑫ$̕oގ"jP=z0{0)FFὓ5@7@`{m^c/)Tvߒ & :h~m$ְ?g߱mJ(3"z}Wn\|&!$4V[%bey䑉QݤQߋybD$C[m[rL%&V(RPP`Щّݭf۴:d~"|d 2UG.nC j@1&nRCjE:yv$W&nVrI,:*ԠhMMJ!k2FQEDޝYŮ&گ&F2`(cfH}fACWIwOېBAgm+܄_Hp >kDhLfZ"C,ѻ!?F1qy(1v9jZo q'dL5񮙽'*=nl[]׋;>eL6hw뽅 3CIZف+*a)\ds##NSN8@;P娳`pXtj&&slA O])? o<t t v@\Ѯ**$^Q_nqk۔s#kKi}WzJ?OJXa Ѯ<ϗ1BI%!kV _>HuG9>W|xF'2)@AkuR{J6O*iS:tӌт◄vQnSO睕}ajP75'ʥ)e!EҦ%FԠO5"nٷ ԩe[g~n `*=ykmtL, ԒH>5'rkiUwKFzIYZW7.]J_Hۦ+1EheQJy#n|dHҒw֐Jn[}5б4Чuֱe׳774r43bef`AhhhhhhhhhWx !par2cmdline-turbo-1.4.0/tests/smallsubdirdata.tar.gz000066400000000000000000000040201514221355600225110ustar00rootroot00000000000000YPfq}sYIN8Nbw~'G3"%1sG* (@o]~82;jַ>g;*CmVze7alnӱS*^?=L*6C]C]/_6fy?Op> |!hE97c,7m1v mlܞ[ݔ1ϳqMNWÔb3a/5Unh8:Oy`qu/jqp#2.LQN0 iXxF5f&> 1}UB>˫8h7alnӱSn;\w|'g.{d崻gOc,uWq ulcW qnJØٸݦc}+|,9}'__L_`;\ܖU:7|Je܈v1aJw iXxF5f~B m_w]]{0y6nXߩJ=/ | rXmpބ2nEQN0 iXxF5fYW}ݶY=ԱY^-A) cgv[ 9~pNX_~x)uE97c,Ôb3aլk}Ξf7m_e!PǾj=Mi<tT}JU`^j ܢe(qez;fH3Y4=B,mC{0y6nXߩJ'/}*Sܮ 9_@par2cmdline-turbo-1.4.0/tests/subdirdata-par2files-unix.tar.gz000066400000000000000000011430361514221355600243420ustar00rootroot00000000000000w8cʨ8G($[{I!]22MƱG]~.Ϲz_煋݄7W/ xyO|BJ}碛+zGE "{JmQѥ[ST>D*jWiT95Zx< p3SL#KFuˢKV5),yom|O}oC1S>+_߾SPO_Š94YȨŜG.侭弌OrٙϛĄl]xSk `;sq!" ܶ(#) $C2~[e1HIèCi4f>G9N ܱI%}[=ib%OxVzߥVɍ7W\}A~mN܊Z]sQKl@0dD&>B`fK̩| S9f^0_&/m"y|uPj2f![ 5~!)?>pzԎ)'P[s=׵3q !7J fVm՞^TgO|8fݟ˔k2MDWmTx ?oKfc#"!Yܢ6kJkT27%ZNSǝ'Bs-ڶnsYFar=$t3X׌u(~#75WǺ#VsE{ԕm>8~m95FPbbfSRkF&v$)Y~?"( Fyڦ،v#{hx/<'Cw3M 6["ju갃XXoMN 5%@CXT#̧$ՆdP"EyRZKQ^#f>쬧sYZq|H 6sYh3F $o} #un=?Ӆ@ڶШL^r>OYC0M_J >Ҕb17W dƞ\%d%%S.B[}4`6fۿ 4LqZ|Ʈ*^|wC#e{ 2)K +ғ)*p.L $4n ϖ;\֨1|cԚ}z&3RFys#)oA$_rVQ/>֩&[ >["ΥlmUs!Ɇh-Ptu<tUhT`D 祳/Po;+MR&R9o_|Zuo&EFD[ {I&?p'KPQm;KZfr7@⠹oU=2M{9|[[Ujz-0u!Zy!V҉^2r.D9.a-fFYTRAsY0m0jJ`M)Œwg6nf/hVPAUtKQ~N|R/.@0(?vg_Z#Q~?{2zbe]tJ6I m µw(}61٬dC264n14Ȑ@ɖatFZiA3(07,ꑷ[2Vd4ob1\]E [ ~<3c ~5>I_ƐxDo pIr_fpHF0$ fbMP!$#Wx jo}ż_%(3j!ͣb{7>b F.ӱomI6.C0=ʤ35t2{´yZa 'X$.S5}n9 ]Ec" /rGe!9 2LOJXaQP 4kI"ʜP9߿9RErt9hN?K5I>w;e8.< J~r?eE2ƊF$-ѶyG3M޾gcG]O㪥љtrd$a8(Ƭwsq/{O1aHۧLӗζpFż^Ѐ j\jDk4#=H,b*FOI>N& O Ҭxj#%Z?D _g ToM? ~<PFEUۼb%UIjm~BcsҚ3z*|E%Ά}yi>g.,08PI@|P?ahz-;4X۞6ݖwP Q+S0hBr+8[j pXj&]@k?B$td~X4$Xh18o"OfYƴ[% (ՆkDGɊ>CC;l۸Pܲ1q$u[꣜;G!4"7rTص>DU'zDM~X׼|`~Ŀouj#>.ݚeZv 6c ejbːOgZ+#pe+j0d̟g='pj23 'Iֻ9vjBrRPc oN-y.%L9G|&ktIqo KN#{m8r#bBxֽQgetҮҌG*ᕮb!G=~F^wS~ndav;0dV6d}1hsj g ]WSTFIj )R|m(Oh E5c2ʽj䟳[x:lW۟k;:VH`ô)7&?%V\Ϣ.E@tK'~NJ ޭqfQ;4rU q>B;},Saae&دcA8~4 B܈籞fIa\h.OL/qZ-劻Ǥy[𲭅4]I7 !іLc!OJSMFWY+9u1wzvk{k[)[3L7J:'[CF50`jį)pY$K6IJM\p7q8v1NY«|uIkXXFb"퇸A7JcqJWZiE\TmJQgz<2z㰿ǟ{Gnafn eBBswp mJ?|]mxԜ- jF>u7m0Ucщ*o˹]P,y 'RYr sr (-"]y'm؏3:|kzދrύ"qy>PSr9÷1pzsW]q_DAx8CWYR F-fs,~b洗^J*URpdŤ"s&+of^Z쯁 ;Æ`Q-g܇K/ݑP::ҡ|9W8|ͮ縎4#j/Oas !'+5=e e$)OxF8jpP ? ' BqRPDnQ>g5)ndD>${"&<۾s8DuSC E[rB( ⒨3'1'Zق(ٺckmH+<2`ޝ@Cg'K7ˌ%k(eɚe%%e%lYle !)ըg|gƙbSGMD*97I2MA$4-Kq3q:X>uH?$#k `^x F7o眓 wou>͟a4؆lVBS,5Bзçaf.jVf[?CwG~ _j _8D+d[!~n?7Wбs0_((m"V-[(THKmGXXHH}RhG$QQ[[[k_bGc烲g8} |+uvmQ٪T+uÉ#C,y:0'|4 ERWLQv9xgm&cD3s=;#`Mos_sE97%fl: ѝ)KV>EtPp\PP/`j^a'`oK4 Iiǫ }e2` 7qGv[?؂BxUf]dX]IǼ )o /R_طzks j7m[)~T30LhyzALô q?B"mYxC k9+bdun'#ڮiy;hrk-Dn˜w眵Xn4ͮ}*suc^sVı S_dfE(qrRZ- K Q8)r=0 ,NTQֿ$j1?(PCycN2nOPNH{n+unWo*Vo|r8`jt8v5Cb"YUԌ4' 䃯URњވ%MhkgZ$R}Q'>WFXH\=TA4:x1w'l6K4Dde[T >\$[b{[K*ݩڅ`-ZJɺ< X;qd_ǢKT d\."kQ^h۫.8d#l%4 *d@I48i]co/c?-^|.S u'7z&=p %R3]c7y~GRv2rS0Xؖ5d՟qZ6:wYp ݊iQVA8{ _*h؏2eut55=g:'=gY.%_JKG Ћq&Vq-q3OP$޼o>`L~@PgdXM/Jx}A9a_w]ޖƀjU6uF]w~P:d u0Q!8TJkI$phH((:UmI;RO(m݀ex }zG.SV:.yauQ?n9rq&L&C^Q˚+ZM2&tq3s/Mnsw1X` skmPA5b^*P=|ױkkOO9XpG#S1JesʈyyF9V}3FPpyeG@Q 沜Z}G3v9cC*ȧ̯y8vW۹Bu,g] * jb{!e`:Ccż<"}oP:# +mFOGD»S&_c joiY#*1|nK*U!JsOLU?;8 دcݍpOonga4߰SXO{1R<++wh:`f7o=嫝n)\ '[muiz~@ ox4K%&fA9G3 Vթœpkl=zZ]ū傪(|k1}K7wR;ǔ2^#yeI*&h$ҝ0H LsQ !esp,kxeNDk Fݛ&o1 N~w023łKFVEI'7ǻXdF+`cI)H/GaYQz:l#SxdOs8#a9qHbXk6eX'soUڸԠ>RRi~2MO#C-.U;_ֆ!;k"FwJ˴ob+(,^}Jv0X~VISa:ifK1(Ssyr:oY^Ta+n51f˘ů:v/Xxma(-cd`?4CPI?-2:=mYEw3β}0ҤTW ]D˹53N(<~t=5&?e rY_ڊzLZV`-j褂 n vIJ,=c(wI3,BQ2p=$caDƨ0/X:Ǽ-%B\RSqrtPA'u<8Lb<bkp)} ?,G,?'1q$:6jU>v բK<: {uuv2ݻDO43(sNG˿͛Kx5O`->Kq.+WHsL40lFUMo,E'XugO+S!Vk1E0yk(KÄn-o5x3xC}~E6=N\q>z2j꫎T2C.[i%>.KƝbP aЌ8|5 07,vKN[yٸ %u.FkG&2IDi2/Vx{8qZcSP`o(gqYxD%T,'j2>l/2CVd*p"w !IQc -#WE5Fˏ`[,XUjȀrxwAQR`UZXb)I DSZZA %A$%VrEtHw=toDMrVOYkAyI  `g"\T뒔C";]b<}~i5`=6WjA4׹Ƈe:=B?DU檋I7Kx͒4vs",[ƒmm>pe3]J>%ZSOtG_.#)cPgwbamMLX(#*UfHɱUHǞx3GκVgIF|i݋MA)(;xN$g9gx{];<+}Hxa:n$=&_d TEHWdC4~&'|JkqIR,jb{DžÅzVǣ,l\U[ʼnJ |D 7y ;u)N iZf.PMdۧox>עW ]+Ň]>/_ "+6jjoH*JLL~I+YEN ?ԍES4ޒj,4J}xRf&m/ccQ7ɅB;n"S T,8/u7Zgm3z.o؛0Fri~K .Yz8`-ZV(b69V  LkTY?O?SㄌD䈃9ZNj-!9phT'7|psE|~jN`Y(!tA}BZ~d+`o'2{bB煩wPCMtyg#"AGWA|O+­4ROCĄ!qHcw=f[- eE6 ej"9($+ ڹdl^vse` د3N|Lw*wmNYW"IĿђ'-, @4! gnͷgg쭦czin YH #6.s3(-Z8f@zй=%2]R7%q"UQj䳟oP0<`ƹs0ϵ2 M@I:W[3},,6&3*3)zlOd+* $hT;R3D+3h7,B 7S=m4S]>6EN,qi֠g I"sufhv[G6\M , 'm:A|( ǎLu5WrC[w|Xt&Ӟџ mX£Y cۙσW6ܪ# hA'4u'zxN'ŧJQ#xcAI V.!jD(5ʽCicERkC,cO0Hh`#4[3K>g,9}*9`efTe|+G-K?*k ݋LHrٞ`Sc^1y5ϱ*BREXPjsB[2/1DHӻE`%4F4?q 7l]qLʝݩz 3;o\,„0ե7?_vTX<="%{>V?Hm{Â_$zYGS9OdngTzӷE]YWM`)0uY1>ϞǮ>i"0T`s ftMlGR_o0~?$:vsZ~m,.}%HP/ v+R#3(Q9 VTQgI]hz~>#(Bv0IJWuM'Ts!M R \:i5AVr.A ȡٚbf:OXt&yI.DV߇(;~u\R0͚X08ſ%%#G2uź੠S2Y<|NxEфˬZ(o>" Z]+)v4~$ޙ EH\73̻Db1ݿw2L>.^ڃ~Ro|Fa\v>"uA(?Xz wwf9)t6aIԹą'O讏#Z;mYiJ5 <}RZ!RiIS]y)gIqucv 2 9kd<2x5W>X:CerX}%mTvꞌBEmi Af _ 3uOf(cW2q8yǔ+I$#|zr|ÿ 7WsZUă&֛rE\F0ra}[U9`B8eyx2u-Kfnj6fmRCL޹Q5&&j,Œɢ |[3U3Ubb}H wqes::*T{>|>i 2x׃ܲb. _ff~\kNrpנ }ےqbN(Kn.{pωݒ5Wi0#O`õý3t';*?))"|C _@n@8+EE§)Z?+6A|2'?v8+byK˗{41OH)X9L2cv@ۚЛ~3`~^]P崽@pLp" O@28ʌD R6 m 5H}&CN ˱^Ї!tq &>slUjuugŷwQ4â2NGC4_B}B|mNnEy2f]xGDzOi) zg]0킯YSV^H$='mo_8fYN8ߊu8MTeq>w&vRёH S|[~:[w>U{P͝qX D]'\ò]00vCNQr5ZJ($.T;N>RKu; `k.0 Ӑu3#O)0B7 `=!M̞xW9LKڌOT,RV^g͘׎i:kF7rQɖ*D4ۺ8%J|hgZ$ 䚴fLae\M 쌏] l Up>o l4ӣ3eɢz{j^{übd#E # KUZ#6?OZ'c;oT}X/ Duo40xjMUH; -ltVؿ qCls?u x -b{]&H02WDlv2>ez\z÷2S(橧+.MfKhΕC,kw&hUdT!p^xw iOA(?V6x=}8V.FaɑSPs4g#wTi҂a7 py(ʏƸhv'8J8?Bz㼔 CN_WO4SFD[ ׉n$ $vHbK6poUk'%M* ]p6^f+$ߙ ;H&/4~,KZas aSLӍ1QmŠ+<7~Ts%} K5[|q]ȵ(BNa3@(>):<1Iuul](| `> 91 2)>-ͤo!K()HxC$jR ^0SXwHAM˦A,Մ048@d͖iQViz,#7LQ軴6Y+70ofЬ?Iaϐp-n5.Q$?ơ89ⱴp^fM$p%QےT 0`Np|$'AIP_+؄<6$}?VOR 0` N^(I-mcǐNW(\mv_tס㞫/RJ00`f;M!-l"yDYȖ=mt{[&3 ؟cU ,kGǑiEވLLR妮֣&y6_i op[\ ӀuQiKDžΝXq 2z?om `  3iL2%wq+YNE[ӑH Y:4OK:d^27 ؁MۅN:Iv)I9ӆ1Ֆp<KMYٜ+><`_޳ZzWu&G~rIљl\lp:rav~IjfWt(< B z˕WuuۇGY&θ~D[#FtќR5[`+"Oa\~:#oIOGDPDg ӄj+K`/VSx\LCkDMg-! fVFʚ YdM1y,igLxƋcC,+LNn <%_$Z2M]p b%u9#jW0ab2SܼBv)Q+sk'3Ɗȁq`v#|"X'mQA ?d)eq~s0[yFUbgLJD;~jf {Yv!aog"_FHqλrNt>ڂaa]vGo ?vњuߕ|`Vxv:Oe`~UX*zM YZsqNnY ڙĤ[uJn+M3)t }EcH8`w/Vad>2`eU1Y:6Dj9'5sq'd;?_X VXXvWRq~J]ւDFy3j^ ^c!&;u9q].-t4=0H­dE҇3(r7S2A.7Zg `ڒ֭^8@8%MY%抜+T0mv fDOieͽp1gMd=ib[j>0U˥n6M ""EמO[o5&i3F-OC:Cҵ}?Du+}om C=p==v6J*lû(jl1f]%V<\.}U ƙqkl_L6 R=@1qSJcI-f~}@0-2YjH1'H/W\,.9ҥ=ul,8ս%y,k26V#!0;1Es(,%+M Y,촖Dߧe5 ^ew?2nK`Jw遹kXKB^K|qj#'͑XκĸXg$XvSO^"ttz y>ﯵi~,IflyYH ό4j3m>%w{!ud`j7]Y}j\: )S'ua1n}_^ ~~ꝷkc< dX'48@ .ӥylK5)>7TUkp?a&$DY/S&f0%c+gWh3^/^:*$RIkkACoE+w-G^g %Y˔u% c7%:Efnk*qjò6 s鄙"g?۴V{>a1LkKwǍ ni5TX%2NAG6;ۛƀ4ҕ7BQ4\aX wO<Sl90g<*U6!)3w#bvdYb%k,7dY/-[= n1z~*].G`,Jq䍭J>jؓ ܉Ji,>~USy[Qy;lɒר-r"?E c8Ax푝j 6CNCj6fwR]:B8%+ rg1>Cj.㯵a׵s/eM+Aym#}ut99upZOq-->tF2TA d?OBH4 * (ZQu:z'<==$ÀNxu;YT[YpᐨBEx.T&';!)Օ]]SVWV7~$Ic zJV/kYT5Il OO(ѫa_33`NWnޛ !,JF{KVDFFFfD{ddgFHEq;:sq]yF39&cubb/OBHtȾ|;$"QeA꨻ؿ]juܓ~rt*,=zUJ@VuU syvsk vW- ww'.vH3M5)%D5%}zP̳惥ÈuR`e)CTvdj8'Up&/v~p!` lYi[9X)rFnp )ŭyY':Kx$s}Ez5dipj0 M2A.L0(;^0`fmLvqܥj<靄o4tJ3SԼs!+.kD-ۋ(Վ `*-v0*\kkL-|C;|.4,*HLȣ#n.H+`mY`N*e?r˰P>I##A0\ YSޘ<?2g^΋+C5&1!^s9rnZ>aܬ~ƹ<`r]m C!‰cw~U!gk|fsO}Q,HTGz#ܒBV5J %t`o,l2+ eWl&'HhH`M"K,:+$xԈ+ȕX'̎ϵt{g s~:bBurtEFt;ՕsМڧ3gl/g`as؟>RBYѓ!#CN<7}szjlǠzWjD[RlzťaߛpT ԇNo~ē;jWX5!ˮ8kl}䍻ʳAhV b80WM6 x!s#{ Q3ص/vr'0dmH 7W=yޘU!ͥTE7KetgdFE6(uRP27rKF-(}NK=Pl؏ \-^; $&1j5.yE}!0`d1P\_Q_;}*]L~21nr,kOAa5qXl\Ql{2=+xe=3՜.Yn?|3np{7 D{Vu8ٺkم{䇻GYaO݅Iկ  cv?&!B"vBFy`]mE5׾ɏk2dz\/XEɸ{.N0`̚42RXbO0+ciӿ4Q^pKQq5 m"_&GimݒKzFyD-˲SjK1P¤G|'=gD;Cer~`VH\:&ȻLw]4؀&}YvprΒ-zJgv\_٢]>HnW2$gߔӱJZ镪u] =nՃ]oj` On`IF rٶ:ׁyVH ;n|4P@.z/'L&,X0`f!rR.(7_$e~z0A=0%|z,\E8͏s'G8=Ŏ DA>bG`lp0AQvL27_>BJc9< n~M3C/i1\}=eC1CtvMg"`1Jä*Sdk}7X# _UV8?DOB w F0jt\Z. Q(is`$E-ÖN9hx˩`m9tXYw7"֛䝶fĔ1b.Q[X5*°I`C3 `dL{~-i7.9N۔D 0`wq}i' #KïhDh~1$ J#Wrޘ753 0` JY,?N)u,cVۄU&a.O`VJ&O )o5'ױ e$_ؙ ԑ 5!ƜO[`T݅pv {a5']teg\m0܇ccxKTRR$D$D(s_ߑY;ҵO9g^7)NQ=J@(w0707BQ"DYLEE阒D=S_'a޼QQ= TI?]8#QFhiCMs†ADy-_Bkb?Z2tn,(@<̵wun] <7Vcx}٢46|>|txc*Ye~4٫?j&ZBd W0dM Z҇7 o؏1W|gF9P%Q*]Սc86ąu7lܼVԲ5yBcǖ4l{l$S4g5V} ryĘD8"k[)gGQ<sq6zv(VBd[ t$!s}`~#_H^'wbrnQ}}T^jTw1Y= `~9= )F/~*w:0Bl6\i;+ >"F vh\XI{ZAl^MlW^;J˾.K %(Sm)uF.'YePŝ)=KnjҠ[;x l`?ΙT:0d8׽V~`f}UB36bܢ\>͓'H}&~vQ}nlir#zɢ^1!YtuJޫ'B9k}hQW}&I"i+ejOEb5z> FCH3WgYqZɧ:BH,EW]˷:>Og&3.;%t zL߲&ϠdKj` S2E)ա>vaR 7C5u<]vښlz/dž;0<(|8h92#.?@ETU?(Zl'\o Y|}Őq kGؙ悍q3=C*Wi'S/@ؿ_a`r߇U{+濘+Xy5V]FVXzR£SOA].D=V 9)-xh{/1T7&/EpGds݈M^_od,rD\LQIײp<$G[fM7Q,)]'DW}ڝ7@\irؿػh,Vޫ !"G[L e&Gfd~N=߾眾O{ιs}$$%p[B6'%?Oo| [g 7͍ f [WʌWUaaط3W/&c!s"m e8?4CڱCllX[qiWژN AK-;=*\mNj^CUd*46ajXĮqM,:ͼ`莗0`?B]QWM똘7@/G՛ yd[܏.__J1GJe!(SŮ3J3V{AwYj15 Lf|`J3FR ke![1HrYg^ "M5):nzd\qAq `͚[0ڗF!!s#we-څS='Gҩya.bQϥZ,W?G/`c5øh}GMޓ{JE]>λĩ]lпq?9;Ǹxuj7r]<[?3ЋO`'[̄KѸ̪ yHy \jB\%O:#,F(@y*qZٓ[@$8C$GGf,&05\d5Ws6w?.:^ 'h~aMF٠.gG5%xV鐺!<[#i+/1"}|3t$ oH 4 @̡D>K]=)E?<A 0 LrAƻy=gW`p(Ugũ^ EvucjGt,_8%R<+~9]4AixH]+% xV yXQh!'}9/~3 kA~5I#wg{9CKۃ/zL,2+ѣ#CCFvo8;wcyݏ;Gjv[FpbS2CP╨d`/{S32 !{[MxrT`>2>H#w6>FָdzJh=7z^q7XjF={s/a3ZPB۳cӼWޖu+ o|HEb)(Y=.11_jAq/0u*zJ#5ii$ϼ+(yE{n@{dol3`?VVVz=;wHMʢTkyIqK! (}oL_S(\FooeO.BmBisFm=k+]/J^\ֳ#}'nٞ&r\u[ڈ$zYYkp>riqC Ӯ$kGi1L YrT钔A ;SR>n7 ؟gEwtnavN,TfEhuc6ZXj_:,p]qγ:3k68Z;IQv) mo"4DV}R3S^( կޒW%o}l3ubT?ʄt;G78GfY:Ze/e}Q "IKk(y5ix ߷v D@l/!fs?$6{/*>gJpﰳ$} 蘾2]a#!tSn4.Y-ïRY0̟;e׉^+MfKLӿķyy_ybQ6_)w;t= M_I0/ ;x)bUmUͤv)N)NWD_">WJVJ%tZZbqKgI s N>kXXr2rdlǯrʗ$ʺBB54&v==A==;=;eج񈵷v?~Y:NdD;333٩Vx&`iџfo/tst[<͹hp J؟gz*8V6MzܜUr0.c]df||~4慊ԄEZZed$~ҥYDT xN/޿6xg؂`yO[L f I]|.IP`eopy"z/] ;֮(^ƕ.V(&i{؟g%/Bveo6Dr!4zsOcˢ(I;{!oz6+Q*TnFL1;ګUqSCBz넷W?Ι9G)9l9j W n~S#Z9;%b\1gzKy^5n fq>:a- FIȼOe^ZQIms0`7|qS7r,[d Q]$)GoZNsbʷhoh X=?2NaFkxQ8DS^]M;THnFC" دjo'bϹؽT#4 `&Fuo~XÄ́ èn<ύW2Om 1-Šb%L#!.sBCkeB+g;U~~É\qa{s^I5C ";1'qXYdv!O愰|>H`o[N'@Һ"cQ5ﻠ8ЏU {9< ϴKZyK.lb<@3` jm#ezyYkSJ vML&!.eCtW1leUʡ4Du|?bq?Ը`R\W>_аBggiZ׎߳l=^e!&0/96ĮuVɳ!-D.qίVO^UxX6B.B 7imT#2JJ<[IqP]x@4z^Ll pQψ =+`?Ʈ-F;3L%#,FS[M±%Ϋ ٮV1)AO(ʟXQeMc%?@9M߮EY1YCQ#GߞM1Q⤪lצ<!!kٯiS4r_i!R tT oNN _ea د`kd})z\ Z4{}M6*Ove )GYȵ|P1e:ԓ!BR5EmR@#:Iz)^ާzw']rc Q֐!cUұ\t_°;]zla>w"{Kl`?Κ;uЧ9׼UI4rҺ t.zNK.6?cN3n]5Daj|krɢ-7BijFx'wҽlEu-hk jzJJvk@*RW#> ͕+di&]^JXe;{_#/*Jb j̀7PP)鈸 wct h5-]>U\w 䥣XNu?Ex8D[ Xx5kQ>t5(^Z |ҝcjIo ~xG=u(Yj;59 R Fͷg0%̇qGNdr]NX_copػh?{fTȼƵ"$Q{egH"ddFd+#$[v&D*Rur~~|sý\sC,qi6V?0g}H8reu*3M)WQ,J?(ߋLHtvݙb'G2~1$X1aTf6vx4#,) wc*KXY58FްU:3VAu5ÛaA~%\*Yn1ď îG͚:rԣ24JG,ў{z,UJvWYo")s8 ^ jL]VFyLYKXO|^5͓ nM, ? inedgSXG}Wx/ ټdW|^eJd:KgkB2*YO$ex?_J%3o |p҆rCbFnHZĜ3f'z9F =!=I[_p}{4tZek!auCLRݠ"h շ- ,"g/tluƠwnjv`>skg nMR'z10 zy \‘VEC$Rކus5bJ۲I #Q,szz^\)1 Y[8MaJwkts<ǙC]&-EVp>LnjBJʛg^g_ؙxG##$y!Gr=ܒ\F5J9[h1Z,%\P'sM H ފy\l;~=4d؈+EG ʛrmj}*tۥlЄbe\Cg#1!Z=̜ײӔ JqGMî{d 0yw"\+(:̄䐖 D2 L mMu-mquX!tw j/!g`L'/8s<0/x IH[ZߐSmA` &J`=73gxP 뭙=bj .noL2Gp"vѦԾ QhfKp {&Sv`Z(t_ִN+{R\a'T|_Q=3߶`aZK*=ZBu::r>C͜[m5e䑽Ҝߪ ,l^PB  R򔑂{_okcyGxBZ%aT>+XoX⹘TH^z bM>x}oVq'[1%ck ZgG [Z0;f`x?pA[ζ`0 { q88?8x?=D 㲣--w@unUbtrT7g(9+Y[}zN@ߗC⤃M?R yiLM]!F5 kjՙ`@#^ͫ)ƫhЈ_gaA632.D')POԁ\؄j\dj7C&11QencIRfRK<<%̕ӗY(U<;q9e|_Q.,FH\;*dD6ݷy㮫Y!z!W_3+ӼF2{֯]R\ i7=F3oLk!ͅTtWJdtg.FJIORq^6@"})F s`PpP;Gt_Be`ǖŗ{f65SM+bhxM~U!0efnZ/^BmcOUGe*c2X·^U av6ϴ.B۳[Ƙ;\رa#x t=\}]޻1K>[I bݯYܳb-*W2۳®&dhlD&t>0 Z %+1Հ~VlXezQ ?f,siognKq843Xyv="=- t+31Z+( ]krYO)$e®lT[؍"HzvԈD '5#)^׏{γ?tұ=SP`fKiςڭ@&y曓_c4CCgv-.1o#ZZ>+QӨJ#{i>bj_J-e- -ss̹8}z%Ւkg0؍~%x`b_eb/I&1P!HR t-3?t$; .63+)aY˜X0%ZI Kشo%&Ss0'2DUnz_V<|ceGG\!υrC״#.)cSnC:ݿlkɮZ1(  Lyvl;[~?+mRJ8F[ŒEVtE 0xS9L%Hs˿]>9!ܤ۳Jp5iyu/D&NXb+zX_;]5nT=&<(jR5A7[8@5f/@MHpzI XԫZi}t$^2 :ۦ P`(dM D(`ZݘtCZ2mKy4/O5YE9ӏ En b6h\!b1yaԓI u(SC~bM'B6l buԊSg>Kl4#xf0ؙAtbWaOkV&޻\ 5l½i/%me1U2v> L{v{a/ %Më5bލYZbB Q&_)}`a2GPo91e LCVpWǏ`TVJ&5ew78&m5l0/3)1joGRKf@&0Ҩ'z+}B~y?mqD%'4l!}>!ZlX6衋KX:MٲfHM?wJ֭IQ_S$::[*q #aB)N6Y,^Y+[ #)}~(x4;D} uٮ>{ H-:~1|`ȨQlqV&v+ܫ#E}ơ6Q{9/sHL}Z kFA.\)׌jܞM>4AϓY߅f }?]BGGvnv[\AB; 9\* yШ""*CSUbyqEao""Sֿ3G94o:N87q7kӠ GQEQ0z# 4-2-% V6s4 uA4 ̀6g+©2ay(;4&9'S`MR}*8۞Q%3?O<\6صѪwOQmXtBpdlZkhPVi9`?ΖT7. =;gC/W4o/R  Eȷz17<YE "U+wGm~u%bٖf¦A+XS&OR(8g;4º*u ri٘1j7+pR>ТFiE; ( i3ohm'UDz; G`͞ {m'\0~#$E'u;[LS}>.m*/ae,- ^&)49Mp7w10\D7;f ;r6l\L.fǀ[YIozpA(kh5Yl^M@$>jf?=+Pg0l[)i\Ī",4g.5>ɓCDj8fI'clVj }Ij}S Z˅7 >KmB{n#3V]1 HSΩ;5X}@M} G%B˞&/RoJV+POԈ]oص"`tp܎E1rJ# gukNJҫh8͌<:l^̆'Aw78smVr~K֤F`A5c9ЙD!$tt`$wfC;7j_QK%ഄoخAO,1j'R~yw;Vz w6&ՓxZ靁n{>lchKr`8@ l\B l\ C-bZZtHEiOY* sA#҈y:=UtJ_(|.vsf`\zwk;nAsyfQmee^nY"E#U9.z 9=+oeط.l茍pHɪ= z0c!˒h^20b&V&xU$ߐ L$VbnȋORr}Ϟ|\y\)*B5w3 /f )mقX 8 Ypl>'A3]s.<muIlƕE7z@wR[P¿r.c0Q( sDN730d-?b`׹.,Ѹ{Lж@a@h&-:*DAmRwhOcQ=MzҞOA]0&>.BFӌ}ރz{s YԳ}Ȫ:K'0~S1jw<2uM&oS!dB%v%u|;6Co"Q!^{;+`GC槌tkf$Wh" 0~X"$\wz\UdKqGMWf\ m7H;is6":W~j&uۯ|3d"NWLJ\Vo/ih {/c7J;%!Cy7Ws24+7e}cLâ ߛ.KJ"^$|Ҏ& xU& ל':=QhM)PLft/B&pcWT?rDy+o/2k~m63:/>OArd#R u{~ji6` c#-YkT3CuB>xI}AmR=;UrNK4"Bה~0~,}1Fŀ[^_eitڣTAOt1ז؟gxI^R  ɷX_:`.0QXe^)a髯 0hD6_qvc,jίGrf^$p K>RM4>_uR/ yD5>!va/jTӪ.MB7|d?Kr<%Cf9QFI$d-дOY-2n3C .IƦ_r[тɌi ؟guF&u:{dyPFZs00pu5 ̊0 UBN&:wK$DB\`eu*G ?ܢe-aòq~H5&.uvʎkLa5`9ӡ/JPʖ*\%#,nv?ϊe}?hΊ0vAVb$ 0d$]ҕ%OfaY{UO;w~$QgAGȹ^'VaBM?T7*k"#&e?թXx#6Lu.}hݯ8bpSnF`*:u4v'`zv?;G^EŽg$()fn|R5͏Jl7*Ր<'qítճޟhfy:Sj?+}ifs/pЪjzq.y~.LJf=x05,fXEbF23.xΟlGT+I㉚g3NÄeOk#Y}D`v0G+L艹HM{Z*ֻLEtwS(9/ӗGv_ _Vmf%@XՕ\u0 Oäz?-g#H]^e߲fd=$)wNy,KOn]EEPXH;W~9!JC]לʖ5G8xVMI5Ҡ!+sBN_~?mxZ!ç0pIsBZg$`o;*V4wRzcVImu,qU'xdCBAFg6wVzgad$py1 ڽص8"7&YԻ' UkȊk]} lWa is\RcǶTS'!QeԥM1,k'%F gzy  7RsJ|6}-~C~ 7UH%%:{ZrQ01` FZV!C6Ү>Șo>ִ2KLy? 0~E\+ +Za8?.t?C䡸=wC{G䘞A;4n=^3.?c}k2HmϽpA. `(l+uC}zO-7Mi|yƤӈ!<~t!br})*FF)LQO )ߘ2* 0DNo@b /jAnBJDz'[%R[|8wJn4Ϩr<C$ܶBMu9~`Y#JKTCN'Um"G*eXpޏ9xϝw":ï?6 otn=XuV bH|!2p'+p`ڢ|#ByHǴT9Y'-a(ׂb5pXP?ϲFnė[PƮxF^'hު`h_[`?Ϝkry,E`#ŏtaY%ws0IJƮ^Y0։"gRt-fQ!aZ_\J,QR\"dEEaL<ۚ;og{Eÿܗx=TJ~-ݕ-/N<w^:h\C\hUZos^˄ÿ7#lDzW7#3X_G񿇙Kx_I d2?xm7/3 M3>6aEiuC/IhGJzBKzV{]S޴uEN\ .8Uwޛ/9˟>hRjRbesuiyLOlj>,> )BBkk-2393Yj8c8S #9e9e?([]I:؇A–ĖqVh /ݝjގ&TO"c˚ۊ^3lo/.hYHw!*MVF2@/iFƟ) m 96B!'}TeإtC(B;8c>تjtO.yEsE #'Hɝh>T7M#6ڀsV+*+X}Ŕi) Sue\0߳&-3|uqԞ5:pv\cYznk?z7jrw7{Vryic;kAdF@8晜 5j 2%v3'_TSK57q5fy^j\3!) _=h.pd8>1bi18V^lRV'6`݊~c(FktK1#S[F5[ Z qPLs<"d"?OiSlBZzlakv"W /{ l<ݽ=eɢzgb[աh1`{A+z|Vϒ3;g׈Osud7R;N:qW ƷY\K |gIޯϒ27GϚD eOhT}Y~-uٌ).^\sIQL.q癡:~pm恝)}ͬBVp P]ab+AOC,k;3bMѲޫUMvSZ I(`Zmp#xxs-_ :ީ*U-Q"ʡh؟ge/#vUiS`7 by QWм T冣P ,,(|^lzK&'U]*m߄H&u\T$HKMBpĎ OʃWK[)M.cnF+o $~!oqi5_ "u'` 6M5B -,i`C 5j߻X~M a:ԃU2(k7Ub.Թ&HB1<y>|ri[|F0IߤM&@\fևd*rYMik;ijM S[F L#@ I0Ki"~n Z! WI"mMn*[ w&Օ,s,,* %/w'9(Zqd@ abGła_Z4oMʍ6N؆@ƀ>ƿjERUcj~NhBhFbkQAQsBsdZ5,qO4n0E9!?\OjU¡}\l>G =}!m 4f2 '.д3NWz >:`?.W.BfTFʤad$ǍYr<( eg!|By*^0O]h xqW`sQ|#K䷰}ǃPFJ.IC"sWE*XN*OĿ#T_}TE8/=_"pC%N-L[|)|!g" ..1<9vo,e !!B!kȞ-.[!P,eɖCdMQ{Ts3[83?f>O`A5"CTw9M)y))S_2KNFNv4^`ԅ/Q+KOAccF"ShSh٩U_Rx80,ܙ٘T$ϙYG9ާ3jJ_Īclq("ee]o˚87yBZGVGVFN&D y=Pyy1J:H}at~Fv6p]A֙&p L夘ќeH] clGF%t"+STN@t@G0ybW g̝z&m$=#9(G Z0x;cq 0~U#¡s)j6CQe- QRHVuVsQ$9g*2Bt']oJCACcǷ(/$6;#q~tT/[ނܽCQј%6g]ֵ^r[ʰ/Snla}wN <;eZD(w]Qonsٺ ԈO*E<*OS~̯¼ wl/*A(@b^D5<,;캑ғ` XQ<×}C1WC~/)I@#a F+T,)߸W^-S-f#3DzA50 Uݫ!r>dZ^`|9;gQG["OTK'&W-wgQ;tyJe{X¨Lz7` ;zp; snb?|VU0(Vl#Xg`fJwU$m33-5Itv =7їߙLo>uW C㰻>SXNHD4`ЮZs4:]xs&;rauM7EG> vwN'*^#yeAK*Qzhƃ[|v$yĉ%Z˝`dxMEcA`h.{9`efr L˲wIFDv&xNq2QW^5y9؏'$kq8v/= Tzs,)#CGDi޽MxVٰu6;;~Au5 AE?.'}]x`mφa_φE>xwحD]yfȵݥևORaC4\w{#pT CWs# 5,en#kc\%:㯛#w&pLG%} GgFc*'t.aC3Ot^6[a^ZWDSh:lWs뎫3eLI+ |p]J%?ojH{PmExNh?jNA}F)0`}E\s%H7-9bubJ8(;~sRʠ 0PZZ:ǿC9'R] 3:y4E aB[)e)ejaeYPx< +aY􋛹e&Tq7M"ZUT?Ǒ3ZxGl:r9xb|td1{Tն,+goZؕxW[>(CƝf֦׈d>z 07lv N{e5d'gOn"#pU+s3x{GX1ogs)W6?8ԡTP!S=qm)uF^1Zk n7BH{?e 7tFG 0]PA}kf"IJ!sX@g/50 c> mSt9gx[|),N} 7:E0|<f6!p?8Q8Ync~̋ckRmε+8Uǵ#Fǩ Ejױ 9oSȧVd]Чpu 0T3f9RI(K1X3:{L1!W6gK ˋ$& )hɺW:Rg0闿i/Q1ENh]N'+m!>V EZz_zs[mX5OSGɝ^#WP%"Te#|7+~9:Z|:ךdc[2y<#R%dBߛB".KI];%My`JfKڪyDh4$r}D]hPuLM"B;"$swMϒz藜^2g̼%JFG-Sk J . .B$`gPMŗ&}53gr!S9oeG]zڧNAC>4vzRIœA.m9%M̥xskkƕ\DBbP |zD y!+DE§7&RњPy'OzZݨ̚\N3EAgVk}ͨ]OxŲWzf{' D3+gZO7/ru=!3+r-w6%Q@ ^I(rZ<ՀyVb~}LI ^,sPuk.&=V ڥC}+X׌EA;!W]mK +.<4{mtJ?2mt&y8 /6.]+pagnj3~!Œ?mtT!ͳo.t>!Ze$`o֧B/4aZ1Nd\ a i7oj.vB-i7N8KPD*(x`8syE4rd޽`'.!՝w]Gn/K͑T;Ym.GJ=N7O{;ԩך(e Vr%\E{eBn?$Y͝Ή/{E?s #b|?n;j;jh)]⍉P]_ܐ0y5,1,c3<.f3p+a OhM^#lF FrU]iMf~ЛLCXe/ 3BnУIllDƒ{ R^~:aaI󝉿MoMp;o3OLAK,c^YdMr$:I/疦#if&fo''O}8pT117O3828r0z()| [Fxhҙgcӛ+lF-XO:zՒ\\)P~ۨ L}Y 'eҝaxK7/ZDp߬XNէk'hO>C6ۺUvn5`,,cdQ9gou~aѰQo/>xڣX]h%잇`?Ҧ7pu)  r jz?8sFrɂ]C` F9wk鹭?xj1j9_g / e6|.+-3$ND OP}DČ+޼j;jHW?`hL3goFp$rFCIOW|{r:_[an8ԑ.tIװ_$b> ++|<-gſP`f>٣ KxɆ0 ->5Gk0ݝsߖ¦6WtASį76^j.~N;hAp jr#i'JV_`jį qE߆2F(; 5!N&R rYc= 23J|aidm :Ai2i C.3ԧ){)iTQΜ[yYP\{G;ۓT%8t_ } ^?Z3 Gqds,&wO9BE2ֈ#{&eG_g˩`=}XoNO\xTw0ՠqx"hxF,Rl# FpP*6Q |k#*)ռ唳O`Ɯaaĵ٥&G\xMMc4b-LL7=\mB[ KSDG-1e웆9*O?#tzC".bÀ#M1Ry-j\_dNȫFނ~Tk`eO^X HVp+0W_bW)`eCr*"trmp;KB6IO~0Ղ,kRT$4 #%f8GMiY=Cf7\tO—LIbpF iɴ|Vu< QX㋍s㷲q)4 Cqt/7Ձd X^5G fu, $v uuw kSϓ;g^zXzf [fIXs0 crW 2 0-]?L.]-Ƭ]ؒ0Y@#FaY4k3 n=^Nc֮BiEAL(BrkH&HF|j%Y ;@wmtT1\zط8'.J"9D|ƍ|(؃A c30qA!"kgUӻdq)5l ZaDxy镐Eq(ƐG*w֦ s{DƟђv`b k14_T6ODˎ9RCH?T>NO#A*cZ]z\eTw'wեk}ߴʢ) ίRP#3>ˇ(yJqHsGxO<w^# Lg7ګiU[Q*%텎g5^{"7YM?bd=vֺz6k[@a\:uMm`VFf`9k. 17sB ?owNeeV񺓠u؝vW!埳ù^<߸گ/hk00ell:W9V]잸(m^lOOekϠf~ltO'e˞нl9Px**ڶ:ZTEVE33=ט0jǝf/{G8 _P$mm FZ5;ݬ\|#v``Yx x oL [ y66bϘDYPpwrSa=c"%nunhmU6˳Jb=6 |o䝔dMTrz=;1ɵiU mI` ;$pk޿,N1IiAZ>c| !aVyYtV],܇BQyqF}lJtS&-8ƒ0g*j`8QK" 8}ot) oi6 @~^$OK֑pv5zpxLitst؞e;6;KR6Z(V  \U{kmm\֎y'kAYW/ yR&rR+J oЎYݠa>{bE`>7Y_/vSWag񥦷n}Jc ٞ<(a 3dؤuu8G}nfR]~m{"6DECx[TB +~4+7]P $iMgJDŽsQc[ꏠ?.8w cg̶V7NT[W `fdv^Vcm&(zU|_}#bop_ ;  RKl3t_%>S~X-x06)wvj4΍"J7|PK%?giv5=@̸P@Zq-4{fi=rx tS5>#zfq0ڢ6M:`>Ӑ5N6sךUV@>g)b7<5++f!YF^)k&`.k,MġƜu(Nù9$;4rYCⵦmnE&p *ƇLSϹĥNNڟױԾ5.3]{GM0jbL%\ɗ+e@Raéc)R=dg.D$&#:v%{d ̲wK~w]quzkY߳M8/;85al]^H ,cX !nv+|zILCI1b ?kb(;FNj/߉BZXނݨȋ8uEVHdž-QڟEV%|8)_&]UJ^ԐDDQ% Pbqvz_-;Ӎkrd% Xg➰.u n597,%o9Xv-[eWaj$h'qrnDDO )R/c_uXՅיF)m+4<9&x:{EuedNd܆(-E. HVPYXW֙83^n/p<Um3_a #l ~.:'oSj-'%V  P.1%)ACGvW %`,犸KB5˵svA2Cxof`z(<)~ D+&Bڇz0`ٓQnU+||P-/嚌~Nq(0RFcBhw4}&[B=h"汋m+o8CSڛWnBG4Iدjsol$9IK@5]GCeWXCg2oQ#(n̊1Q$ſ=Ǹ+gno0 t-Ɂ_'ԣ1W73,{lUZ;h~~qI+n,cNrR=Re-_Y'䂛}|SLt"f>W̃ {vmMc^3h;M_@,?E'G+v!#9N i4tfs߀9/bD ҇D1=1bZ egs9y^BM+Wn4z,%Y#sDOs _ՆLxm4 *9c <^ݑ"SQdIln^bI썱*m/φS"ZHmr%v51ZvaMwyl }ۄŕej4_E5k[iC Waݾ>~>6w. R4?h>Ee Y Mۘy۱n6ΫA)'Ʃ_x=vJm) N*xDX-g@5ی'Xw y3=33XeJ(]X<6 #;l/WtPԂc([5ЦW23 -SosP/ҍj:B5w 8+P8kQmh ݂c~Ğh"=)Gzc.J^O* 5[b Bysa' EMb,?f ӳ^ [(?LhC &5gĮNDrO1 u"*1`L( QuL?*Zᣩ= Il~gg{BTqʛ~o,`P[X 3,=|ݱE&j: `^~iRz9ڃv5bHROY_ 5$&>`i<6p At{ x?kMzE_pe)ֆg5Ǽ{>Nu4a[=CHJpTx<}ALY[gNylSM5 mh4 䤢%GZF'uQ+r):Sζr3|ލeBRu5QجJ2_RRbW\n\wMpY_⚒ ho-nФ#-j9֢eӥ#ZHMe:+PgMڂa Q1Uv=22"`n)zG"H,^͊KzIJV̼{wJPrr?CdŠ'5<0 g, TOzlpϏ*>Cmp$L6ncGsgiag^th D<"v;t牢҆{TA-״F 8C3+19N<'`N\01c"SR Dx2 ohM-Wԅ[qK y⳿FD bO~^ Sd{=KKl p~}$m_KOg5+P x^C#,\FXUl`݂6d/[]*CU-+o>72cԽս)&a''k%A+A y yyzqn|g +}e7UF݋G^هwow`6,W e;dXXˎJJxޅϜ~qeq%EP6m/ ڟ[C!7ѓHUۯhu3wǦ 0ux \eQP "w/\H'yA ͭ1}o+QR9hys/iʥ"nt0׌yO@ 2COԃOysh,\6ڗgد`f\Gӗ?C[ Ch80~;[),RDDYH3!rL8f.?dhI͇ϭ܌R5^ A -gVӃQ^tHֳ%c}^cm&qL 츴*Hßk\Of!m jykX>T:MVųYt^ vŸ_F\7[?JV+s߇,ThQ8(={SS`cg=5E|Or65:cq^RRsl;ZScl1{q\a` 9Wc:8QLLs\/IW!WrN-S@ȵ&C=B}xN93&`?D:Xk][7 DulrEi#᱅طl c2íyOͮ+Q^!u֎`?2m8%4RnҒt ҥtHI#  >g!f|EY?{_i+A=kۗQ=sqgږNg'7d2N5xmUwauge9zZq-;7>U981{V赜Y?I/% v1L֘j+ʑ_`5_ϊ5ܔD6Tc[~"fo؃cJa_*g9i/ Մ*oG`1o3дSH[F>8f/v;ZN]ۋDM ['TI:K_Gam+yhyhz5L5F|F Eaya`SH"ې!=ĥbeիw\aś~~YuKuTνx@[AKYp !"ҸBZ_iI?")K.KJǕc/i i`j_w A+^A0{#go<} %bcs\-xMcce99Fн}ѯBrmxgGЏ)~0s0(~MNj}VԴSD k4lրwİʾKùp00MLGÿ4OgbR "r.|{.bZZ[.ۏAj#`GL\& YL3L#Urm"]P\`5G7Zw{U*X<(QuOk#H^r籬U O }; oG r`ɸZ em دgσwIdF~?:u/^~ً e:w]AQj40Nd'SK)7*J@H,voh_N:1럠ϙbw_\-ѿ)x9{ݕ-DND\Flg*%_y@ Sd,s&LqAaֲ ,bRt.4[0A2<I@S'ՖI(VӞ]3C 1>ETaeNt=O#/NJIzt{JY-Z+~=mf8`'2w( 1o!.Ża$7G/ b>C9b&Ķįx/nZFi'y~8` J>qU3$W > A%Q<mX1#Q0pfS^JI'BSIy _0~T{7h5k-.JjiD}.ۻ%|)wԝ0FQ,/Z>nqWߞ cf[8U%mF6MfJ8Feݼ9!,V'EǬ FTtvsNyp&h*~>&N>7 5E;m[ްiz/E_P2?I#B-d.bhsKc=uYdR#x8(Qz4aY OL6[s!N>og)إc;ӮW_xOC-l@r &rֈG{>i6pq}yǃ7&+)LDjpYR_CgsWIalO?W)?>zWHE0Yc]kζMqkءš^ lEpM|w탮2$YS$('} N5:/#: I+) Bg/s66TeVHj\.7 %eCy$}aU4۷$cn=,1CjbdbdAhhГ7yRhwh$ǚš2҄g6uNރ|N۪87T4_gɎ3)NH68|} Rk%%@3L\L\Z-,-/|퀣EAzʌzx=\Oyڷw>i}my ?UiR5sb#8tLy'+_~)*уsT%&g |)6*iMg)c xy"Pr噽ۘrEPVѢl#Q z{vn0~vlz I0O*Oܥǟ<CE5KZx2G1Lh1Xm ؿ =_Y4k!Kt6 5 .OA/!Tî`oc]*%y+tiU`_k 4f7riZ2/i' ޙ@&[4i i!NeĄ"qvM\C*|@ҟ>/eUʤTv ^FW*b2/8>:H.JϛvlAiK9@æd)='nk{.r qX8lXZ qn`JU|⋎+0=fcRT0NbcހA.5wY0 O$gjR[rK(g.JgrpB)W!򧔛cy ,)r@Q),ɐ5&ϔI1l؜\^q@T$1+Gg]O;٠EΝ/~>z-홁t9:~WŻҪPùPs QwҲpNĖ$l  䳩5"J_d> `Z;cr#YonLC|#ˆH_ׂڔW{j^j ty=MU65M%6`~Ts{O*dmvl+>Z%==v.cj#;ؔHL5\EqP$N!тͥ}Jk%,^Ms}͜B\׸ 3 ƞ33EjNJ4IBPC)%|&ڝC$j 0}1}. %Q  i aRFZ>AIuaMM+ml'Jfl*\wzASAe``!ď۠VUUHawlV:V:#sdڐx2K\rPYˆ?+^Xn͖0EEb["S"b"b0|Y|Y1W\}2lAXE0G0SUw` o;iOSvoWP|\s{OPCِ+ Q5 ^v6tնZcT\Ps??JQSӂ9gsҞ z-bf$`A߭ wAQ{))X:VET$$TB@Z@`)ii%DAAV@B EBCRzypfPya@V,& 7Yٿ}Gŵ0}*$x:3n}dٱEIF$ uj!tHF'ȣqK4u ui^m^ц(BV?z⃃:uL66h}˦bi  |ӪʀrC|d5$IM3I 6ޠȜS>㒰jWVDpBt9sڠk>rnn<7T6E6E11m;;V:: ᯳dOxO~ s[7K)\&NlBn#,ٮT"m{*HnVIZ53չC,v}@VÕEOqsWQ^+ }jnvy>YE:˃EٚS{-דq~{PU-e6}xk*|)3=D OLD82^o[`3$F8RkǛWml6Gdm=Hhaix[}1B`P9B `2ta=IMő[G5]jE}I3pK|qha{Iĵ ?ui̶bvfP3ڹ+\O9],y]eb]ϳ)цoBEl[^3go\fsv/W-f=֢p"^9Oɢ4CŠ~w'C.R߳}o^QhSbd+c's@pKIm9%M>fkj(~-(0(o__Asү)i=@\ 26ѐK7A73Kꍦ ]T8Bn`kIV\dmm+ on^1 >H 3؟g_vz >ĨS/A!K=scl))U^ӆoMuFT뚡T=^"5'}i6dtM1ԏGD!G?}P-Ƣ܋aȻ-ry!Bv(`z@)`8x{Ɠ݊JT)Gd@I*`-D9]&O"(Bƍi[fsa s-|mddu~O{.RcYk@3僂p>`?Ӯi̵厣=K.%!ʼn@f ~e-0~H.o d 3{Um̒~p#:+h}1︫fp `yD71;${Ew4>>}}ƒXW!YV=AmutNc +ɉ{229] 0G5#x̗f$e* ޖ4C|IˠM.!u%u՟ggFq@ exex!oJ9pW+.jfN FK~?a!Ssa%Ou);Ml$iKLKLa*P7)U)UiY{22$!MK,OWYLY ⾍W@ThR'S5x=:]p6^]Rw6%Cl_>%%3C&|a6ĵ <_JkxLwbZ B@hE#\M PR y6J#JqNi5 zeK|߅NSP~+ǀ7`?l&ȟj)- UdWlEuٖZʭܚF!(S<7ea&E:?iRIo)1)}b 'Dɟ~٤NM{3JUڢg`LXë0i* k# \3qZbނf `QLIPTH{`S;\MmPSY҆'r$: K + e9tE'GQ"49"fI %:m.7a;>Cϭ]QyI9N#^7fA3:L:U(y=?DT'#Er`t\ ug%<͐dxIR4jt䴚L&YN'Z>` 6 =ia}S+uV]U@̭C<ʸK~MT-.r8zn>9pzC\֢ǺO{0ڲUw]un>zvVc1 #?TĿSYnJ]V~MIY#_XJ2Ϛ ۏ=i{/KUL* 5!)S!8J+Of8t&o -U\kX}< eܴٛ].3C.34.Y9rjT9JĦ[Gݝf%zq$n~$";.±i`DfCt2Yl,^6fY015p)nؤ V (japGoⰝLRd{`%|94.!oj6ڏۉ*T0ݰ6b72py1unq-?kv9mX#v;mOgKoXc~ގ(dw 00嘊ZtMnjd0զ΁*]ν¦m& iJ/(^4LvhcVQwo5(BrVq/ l$!N9M@m& Uƪ1I޴:2 Ɔ t2Ҷ*ŗ6dVҤQ{-TUvFq A A:WOs3M\"׆šlVGxeQ{rar+e]tO·LYgZkȘwIɁnk}vRtNdkUr`3-4hi[43IdU '4Nz ,֚uAM- mS3ElwDu rG$(nt.gPJ,fI?ǣYyKGmw 0MD )y~deDX|33R`J3~76ZRÖ֎!oR(λa':W:<5 -י=S2y5ʰ)B}3_5>wPmt™Ϣ)Q"./qn6g*_D3lTOݼ{֯q3쪀 8`AM*ll+؊hRجTLM2rUq $}\X XnIy`1q@̮eי(|_AT-ʟؓV|csssB KƴvUIO[]zmxAFa `?Dl80jv%}3g']f[?dsz֭3*<~!eMIdf|L|]ɽTpQ'Y+RN99ALMW?y2 ={5ӻ#4vm?}g<5ZXF5HhNƙe5> {>Eo^fM`$k3VRNhpi<1Av~xň;l&DfZDvbK1*Wc `du\L[yK-,U1{X k>C3e]so>)ShQ6b0% I}-`ȖhVGĻYQn̏GsJG]l6ǧ%sdW42,3 P7 #pb$ 9\K^Q7͕js W^}hBt2gM@$9LrPQKKF~}Fir $sEF{IlY=fܹadުk XP8.Yxs ې3^ӍO@es(@^YB Ӆ0ҜNNYO_6l؜"uM5w .:vvH.ntNUN>㜳-RQ8@% ` ޏг9; ﱖߧlEcBa g(קdꯋ]ͷ]}s&x1J%luTЋ&~"yyAtsӮCХ&?B2}-x4&9Hb&LAL>|\k@ؠ((шш|l={ hd쏣¥W^4G7XK4Z$$zڳܭΛ9d~~)PF(BgV;GasTySji_;8UG#xۤI6:wnm~[4s bONfjZrҊ _iޖ3]%AƳE+|BPG% [r_u遏wg`|ͷ9r*elm7q%- |`mU:PC9j~$&f3g?C ,GjBғ6qIpΥ{?|f߆=]*9%:vO7DڼU\a M;tM/~Y[K^Q٩гSmw6Z6FV{~wB kYZLL(Q;ς=߄د׬d 'M;P$^MYdȉ} !{-l+Ë# p`ݾ%-Ɔ,9e2[WgɪqőUϲwY/Wa[0-J5b)&s3NB3CɵI tuǂ۵x%{5j1kkYG{SBiu7W^f}>ʼnca:F!PJtl(sd';¤i#IcRۚ&bS/W|Gվ/5 `&كjߜlس`@~V/ ~XH2>k*RGeJ;%f(ʫZ\fo9Jԥ{P-z>Ͳ؏Loyo*+WyMG[9YE#.pW=$ 4XcGʋKU.? ieB}5?Ryo)ޯ{&a_Խ/^\%JwK$0o[ZrHOK#1Y,[$n/Xb/F 4IjVʽ$CFw36Vq$K/A/n,%եGLD{E[xP9Q9D׉{{3}ΗLy_* ܍vq#{q ZL?ki@} +Rxx8^4 $!+!K<~d{酥ܔ2{k͏iis~۴L nc4Q%zoY8waPڪNLa)WzףEYN _ `[CW`*i77fz>[v]4w&͌QK wqXLAv)uo&u{`i [֔.SM"4u:v+R Zry$j]~X,f;$dB4l5]9n+:Eh@xuI;RQRg N2D~&ݷ&WOQ顇TAjF !7aAͿ}C"u fƒ<c<(tfN>1s[7u}$mzϹgQԔ1#[AT %Gcw# I -_hTz^JCM=`?l"r4Jg5btapQCBs7OWH^ l5恈sZv #I{B-Čj(Qc!a0ZxFU)Kv[9),޼cq5Ӵ 8XOzs$_hKTpd}[v^#mK܋tPkCi Xa^~~ٛKT`@(50|NV+dZvに"=!Y3t%"GO>̰Y#@ L[hΧ!q0]Ꮚ ~t! >yP}y*~ɣϮ*ۘ(0~S%2kv}}ϭP_ r=E | pm{GC4{A2Et<~B*2{4}%k+Å%b#I 3 3h%o&+G# ](nAT>p35mBUfT;鐼 ❴db3*C:O^Vq?/ {<&W@ viMWrC/\On!gl WySk-Tvv ]zC&9:$7^zQHh5ڋكTb=^Xuy?"GH|cKۼ9jI uZoNڶ@e%{9eKR;d,iNϝ)SWw+ҙtjPwAo%%#Z2f݋PH|k7 q,* fP/,eOeu?XvY{uGvEvM$M$ Zuss-; ˆ`a&`ȯÿ[kpB?f9abՑCO9Zda|   ic/:?XD8P9O̵$ ]ۇ(Z<J; g-)p3削fR`2}bAļ)]!-SSh&Uf9/!O3{XWb-YXwm۠W'I4zQĔd_y9mTW[6iW !Y1̖V_gו ɜ"XZVoż%>R2v~5`s'hS30;mƢHq~۽/f7 >;M2fD+dЀ 7iva*"GI)2%+ʼn#V#*B]%ey͛m*ܳtT] F^* ZM|j9S^aKYԳzsLAWb$OkzBZ`ygzI[tҡs[6%Ź:kRGfb_h~Ӹ&:L~+Ӭ̲Nw= 0DYd>d l*f9B-  fq=8a|~/y_V3Eek|렽$; %āvVhWp4w)^g/^; SSY)f]:K_ "lBvöo8zFf->zzu:}( ff+7-C,cى>Wx7KG=/>l{K$Hj5v44l)w2r*p߬6SRkG>~^0^tDqtay';p8s)3 "lVZWy"U[uSa\1( c˸QBݑ w@f1l#嚩3⇘RY~\_ŮQu X& \qbH/A $w$3FkU+sKT7lUG9s.{ϦLԕy$J,y<8=[?F8Pá2dR#8Jptp{|]}]7,޳¹z,O5X. 50kƶJG#Z.T50:z^,g!ڍj HHE ,|Cn\\ "1]iK:,n휽#\(_'>g&dzaՏl%MaNA eeXdG+jIIi_Vu'`f{!ATB}hW 9Hoto#,aM (<|~9dk>C&m5s!fP>`?2ՈiǦS%O$t챡KskcCmqdLix_f `FMsb|3?"ɗ K97 0xf53] ?G&kV9 0v^g)n113 [ i&|N~+# ׶>܇K=XjxWWb7hxO11E*@Z.CI? ^G9 ;XMXKCEI *jAuݢ8CfQ1(n=ˉ6VC] دc kD兊F6Oß;Qՙ+U *t ?/U92]4 Udzd|u*:[!;{|SL2ҀN!W  mMߎ!U,.L( zVa5I?-zy0~]P-ȟ)O):OL4 -lm%0TXzF .o8乔l~'ѽ30~Ve).R рD=}uetg*#z!)Sl>AriG>^µ- 1h|ֲ.zQ qZC;ogӟzbZdk]VhVZg_ "o\b+Fc޴r}\d$q7i-T@E%8LQZAy)aOtX9Jϗc cg  'I3̈ k. YVUo۹bq lSvJ~ꈔ}н\\3rjָ-VQjIu䀶2~+fZ<2p6|ZpNvB(œO[4( ўl2罞؏9zMv~W;AdaΣE;}؅JٲY, ,IBc+Ő"DBv%{vB\B5_fn]骞ǜjx4|q%mf{TӾetؾr6qZ=1//_z݄4קwamK Q&vmcc ZZ' Ttky5%%WnU9"T)^.Af_W9I/[\V=#DF -#ͫ֔ױ\*-T|V;aqS);)z i AzePb/Z؁7z& ɆxsIkױ @ 0~&7q~xP1]ؗ)K9OUZ^kNjoj?w|1Na#^;%ЌҩC-9˰0e:#m07/z6jLf0Q-_3 i ڰek*ᕔA/Yz> LTRxz0H&l(eeF 6wjNd`(=8 |'X%P8;+5I8e. ]W 5`{lEMlr"ڔ~>lscc cOI)I틠&uI<9,9@GՇ򖹉$0vu,5\Wy{H$?xu,?|պ8okpp:YX;CW5n8oAA *1"JwEweu;.2*d͓"gPVd5bS ۶[b;?ӷӷett %d.892~;--Ʈ B]ҙkK"x>]>]o2|7HeHeD ^xpIw  {~grצ֦5ؗcP"xXV}wxz|<g]{&'bR 0e1V8gzܴ~߉.]׹ιnIrsj8d5\Wk,FK;!yDŽTϕvLmp)s06sҏw8MH>I˞5^/TcZj D^O$οFב!y[kQrA/e[OnǤL0u6й,KkfTT{)ql&-4Zue=MZCM@5̬W&`k1=.2iVmZ (uLlY K1lJ[k0^OLWFs֑*}¢s8Gv*8tyW,y\]56].DC1i֭|qr/ө2؇أqe4XT?,=IPɾ*A=~ oh cyqԭKccGKmȜflh 캫FZ%İv /?b?CX{o3Y&u[@Ht1lhrgnWXu?!޷e凬Y$ֽ=䁰ܒL{oИDVP&^OuQCpH8)!|. vȱ&" teH}Dz͎ɻҘJV,DlQd.MSxx^:ct$socYo[/$,oȣ'k  䪰O 4/К^#![j=/9߶0) ;z-f|= [ԑ3eE.a&]E[ꦶ|v1=Qr2.y9S낪2:GCu qMnanӺO:LlVsy@&= dvo ^>|K)XD||A-Mݜۏf^ΦPY [GKfwK#3uZۏ7Rswr p|g>mBB{xEDBh`XAh̠osu323AyO~:G1sfM"9fMw0}zMQ(L:d>ug~x?auZ{M[P[?}^ۯbgV-S⹛1kD=fM}%:ZMVӳ~8h_9fYSnu(~Ψ+:*v~0c]L3XM*v/Bs|i PNJG&G(?4j)9 y_.L;7Y k3V@5Icjqݤ ~K.4z>+W AUD<܌첏wT?k f3w=B],sl+Dvn@ŭGJf ޤ :6q]0MiT&h?h k%DaX`gH1p"Jo?`5 Je?clLN#Z{WsRQR/I/0? ô74_ʑ6.]'4^4mbBͺO.hFZ'ЂV^cq_"'=o"o]n,|znPHod$8l.ZƁ@'NΚz{kń̄(lQ4I "Ix /!3[}}fyM)w_YvI%^hʻ{4xxȰȲJ;ҥOޅzpJ{;8B}a*K)Ⱦٲ5;VIlط=["{NQ"-s4zt?1\ιw|8'nJ7EAP%ҢxS:DOuuf 1:5}/ SXWXGOqd6RI̩ڹӜ M5 [6%'`/X<ᥤ>fN>xo5Fݔl!`f8H+96HN9jy< 'W $ddCxdT[,[fϺ}΀(l4C :du|q&V?㌅ cLR"Qdi/џ +e ';}JvzF;C<*8O휷MC[]Ю:^J֔EX=g:h./r1젹o Krt%NUЃ2^4*KΒH"/{_Vy}MG=VFݯstĮBCsA,2"WQdET۹׷.:$طcՀ%TMjl"qqD65֌+@rCUaoow[?ʕacq$S)K;)ޠ}.gښK-d>:d7,,J!C`Y

OeX ~ T8w@`N5oTͬ/h |J[>ʦ?n9*onZD0sDt}̊`'Fm6OiL8!UN~F=)ؕ̇4yCꕌ ~cu`kxtRX~-ڣ?r1`7|Lk&3:Zڔ=E2.=M0Gsʞ̅%\;Ґ %lKH١v9jN( =(_ &ʒرx"NZSmvG,Z90Dʚq>A`}=ĪuƆo=s=SCtںD\nx!0BA 7Xˏim9w{=&{edk)}o1 ~p;-Ql-0~H^S1H`;,O}1%a:'I&H.5Ӽg uB( Hx4E>۴9X.W]fnJ[ƿ-oQrWs0%,=8;zOy U~~=$$q] DgFjv"MgX&wx`hPIn{ٰU%>S.G Xi6;u?YVܔ^md=R<XköNf{1Ūm|0 C?<[n jb'Nz;>eeyZyQ*nQIK{fD2H3=ee̊QBVwɂK?ڪQVV>9~^%s2Dkͣ9!}4h|n*sTx33DDk ߯..VJhK%n$*1%nRߕh3(:+:\_'W'̔2.5,5F@E4V.,y_&C*O\@\?˼z#04)_M.*\Z M*7o{uQns%R`?j8JeO.a}1KPquo>iVuOi}!RJC%>ceߴN-1mO:S>LW1`>V$Qbm{/,Wq$p53i~rSր.lwRYU в/e4bPR yyFX,ŬB6xͮ.C\"!(S0!ϵy?&_`͚ؕżdAT܍oB#¯H:\< G4̲NC\h#?[JqKޅM9 @B0^(tz;muЉ/BΟPԾOSl=Z=(= gwNzD* ߞ~r&hn 0+CڌfJS׌X"qKZ28PА"`{>1|+'ۘ„*b%(Q㹠fΪg:bO  7#r>q% 26~de8K2y&;Aī#<amCO:+ ].)dv6ELN~-ZҼogWG`2 xxh x]IŃ9]J5Nk̞//4wU|P'PX5U.K}t?*A ШҨRd*Lkn!s+~$^DEv269|S,mldOl,0ă,4!4ÑU%L f WH^^/ʣN(@:= nm7 ǡG(h6*F-\L/'GOZ7@I\[\[jN,K,Y,5f?fQ|YC _Ok*%1O@CX&1;S?cRCZߵ̋Tu^Ո,pi_X63A8t 4oe sY$6 QdC i3P/2ulo 5Q$E!'r5ySBӵG0Ilp1#" 4I-ۮ$6:#xP-.{(#\rIҴ `6#|BTd~nR6”3{=y1Q[b0~ґN+{y,swԈ,ZiNۑog%GFb&υDW]yԻ짹ս;Y-8եaݲ\{7A>rU<:%ޑ.'~[Ew"NkYBK;kߡF߿}`rѿFYL.;3 gt""wƩVwѡ}{%ʭ{c#/޷VE0q3:O| |.gQ5P4SA%4`t6 ?&uuRY?i6{ZZ--IDF{ W XX =Z8shiqz{~i23Nj!,U~xwm3 Ds T =Ws>:8MX;hBNc.8J;[b.G3An#p4QL[Skj-َob|"V=/ ;.c݉(m"Fj/$ Ĝ*NVz:Z{؃A-.ebYN<;t:ƴPs^uXz %Ҏ-6yyPcm .ŜBlVZ7S* !g{Q<8P]ƲqI 9#aG3% ߊ0%~=?ݤ`hZ&> { &no9vD#ʈ9ǽeCڨp=WIPQDžCn<<畕,5솬ھ%|{hKXS[-s rx>QŁ""7իt_\\)<.ZXGϹxkLݺpU,+hdJ2Z}#}6c9X_WbJJµ4\;_&14xL~y~yϙ"E _=r>9{%*ȝȩx ܺzLxTQo,L<3E_ /P<۝=gB\PL0ND1)sL%LmF\W؏j .VR¤x張^a`Ϝ3YnTL0;5Q;Q/<Y2_#(}!dXhS9UaK~ŵ"ǰ7}yD|f;Lw^IGI;WB?'})Rw M焈g7j;gw"YjuO)Xb! 2=3a[ oegxDs3d?%o mhv?#0~TgT{O1F' bh-gA뎔0'L|~ )yDOQ~o!(?3&bm_ϰ_,^Sz[dڑJ88ǀ ExFAHżݭxb/cMM/e~BO1u\=m-kv]ʵ G3Z(vϒusJ泪mg9Le~D+`h-So:v%>AF)bCk;J+,\gҜjr=4JEt|z- :AoQx_m~. ρ`xci!Rmq`*m;WP7N/`6T]װ:,iy33rS[GAg&ԩ:Jy \2~@, 5nH3emx#u@!6^`6@n8{XS j/N2[y|u7 Ȧ$*xbw;If浨~֥sߦM/Ip¸ ط24DU'%Q=LRg)0Ɖx8|zD>gcz+x8fOKEط24nӌM'zygSɌ-&tƱ~4yۣv8c'KA8$"ީKgY>bc0};.OSVf !]i1RAW }g){X#nZr k_ԃ#bQT9rB5+VV;n WW'=lWǪXL{5ij|mG!`?eFҭeX9>^7DPW'qϿ4T؏i|^6EN Gu_72"{BK|go72DBqJ dҩh3xOj#v#ISSffZ *Մ0O9LO~dtcnJɯ#5cPK8䖗3,[KƗ+x9,Jw^Lms ѷYroyP`DǹX8y n3M$l%\e2B |9Is_rɠY#Ćd!N65)/uu?˚3Jg\4f2t{R)(nX_MZ}ػh>H$pN<$\DᘇHy LJ,KDQTyۯν9u׺Um^k[ky~{{7umׯgWï:# L[XqP;%p\mbf7PR.k72F/O.C'J F$^ O1G?6iyX=3uQ2໳Qg>]з|6:$KQ|PBTT7O[h1| ??8G xԢ,Mq4ZPzh}X䁌y8(iDXubXb~J PRZA+z@/232S؟ﯵ_7NNTmx-sBrL"D7??:u`8'a4fJ(Y)Yi])B]ZPQ53󥬵#kffkY-WL:+oʻX䃸3-*sw72rJwΫ<%;/k*F}O0_!)k3e՚ ^~ R h]^m\jj#zN>Kb"0$ k̐pH;B\.1{pS\ 68Cpkb:El3@V6QķÎ2fAó cxI]jA:U曓䒂 l?C'jzry~X;p.![:BCo듹ض[4PkL!~5L>lw,)ܳG/-" l?C;PG*CfMxP]jZm?+-ݮxWZ*k1`$)sÛpV[~J%{k{%#K; >wǭʥ)d)m7Nx(Ȣ@Ľ+pFb~֊5}GNW9, [,q HuK}OTKDdQ冕=J[}6XÊ~:bKnyHnc~[\TP7+vz$d$W^L~!˧ko%Ύ.KGP8WQs@8 +T?~MhPhP"[ 0w Pm5) DQ:!9!c 1@IMD~Af:~U' PB>$$k(Dyx -c1c7hvkEa. Zmsz }Wp2-=hw|~}#F93SkkC_Il9_dz|I&C go97PmGl+F$|{_v l?C70'<'Tđ%xwc\ lImul?Kwq{´RMJc+NI]~EuQLc{=vT*Z#c~2NtOem)a܂>-1Q ? c5k;v\V2,Zilϯ%`c Ľy T ٸ5.'WB^l_>5 ݂\zGV ELF;~!~vx]Ŏ53 A^Έ#qu l?Ckto$.Ȩ 5&_WP!_4$4悿y0Lz 77EXVil݌[ti_z2߄ѩ88̻[k2jBY.QܵW YXl/؁m{5'8IzWRcXDv?xG5&nm{uC?wd*U&rP T[VisH:-w91ZuK%+T8rNk%kjI &yzv@[M~0 l`nj@PnA|SII)rLNkv |Cr1nLNqԚ$wj sT;4ˠ[ z7NS:3{WX1~v Hu~,C MO)\grۣ4߯> dG 35IJ<RN%4sl`Z_^e^2G@qAqŝ`I_'6JRƥ85m!^h?[XWrI(~6$&zݷ<O]ymE9Nj%S,~I3y8E:QC^QJK﹣f2c lFt s!G"l-ZvY-hllb)_.D8.,'+' B'&7 "K]]!nAR` 1hйDxPPĄvf&? AP_ r,N.NRߧ#Q9ӌӌOCH} m- Khc9Ɓ8 k<~JRgz slJ8P|Mz%U>UݐRAA, )!K&sy%Fn@EUQ? ߖu3 VY "! 4!G.l,_H-<~el`7qmògWSJWHIb1G #pܴ xW6vOBP8Wu5,dxM x): ǿɻjW+QbD ԃt:*~S1iRg8VeyL"ʝ!:n6*>xcV69mJCiix};ڐ[7ۚ7% _*BVBF4gl:Lׅq\,# T 0UM@ogP#F}{mJ7ƬVffBS6bw:0|a`1y6 b!%"s1r "G+0 lۭi׼-_ĸz^ldc{1OZiC4Hc1Ø{_^J2W;LUh`gFW~!`ۿc_AyN7vh Gx9 F4@( z[ l?CTݳ\^qCGT>ZTwqŃ!`ۏjEtWIjb8$4D#6mQw2#Q}`и? \=ľD~z#.? 81CZ\>ɺb}]%mk.;DJdˏS<3j5If]t$ۿׄL `Y./ՓmN.¨# V*+*p7ۯNjk/O[3qFwnQ0 (B^aY4۵e$q¸"O8aoucYK)5i'xK0Pkp Q5GW^fƹϷ߇<-0:@eΩއdt {I)/AO-$EddjZuGu)wlʼȼ0pY AYEܣ>=SJQ#"^]>g"R`C fjn47By#Gs9W>98)..:n5{5livv߇Ht*Ćh6h6)p<0W08irlw%`$1Xbsb'@ז>bTǂ*{h~NV/&-P+LUON2h5دc}﬽HbY(lxXRî#֤_@PWV;'oPu0NGf_5԰-͐65ĦiԎO#:29*`Ȕ&;aUlJtnF3j$j {җ0rV!,us9^j:փ3%RF{ =&Yٽ\`԰ )7>D=z3|sY|إHi {Q +(.bGV7Ȍ/j6X+*<~m>W?,d.3CZ̵/mTEQeYxRk}q&jbؒdg~{CLfcNd5zaq,>kp+g18'1t{ &eHb h9Ν;D!`z5f}LT0N5a9Y, :;ang2U 5ԓ]$8Be>y \lܜG|<~'Kw}0>vǦ:DWVF2^;x)̂_>`Kw3YdئD9'd Zw܃C17 ODMDqM+DsspA^T mj2;;l55Q+pKw5;>(T: J{<u)p:3˹ B$PעT^^3' 5 |7\\ak4k|}=5h@E>oLkL'ow(=MSIBc};c/vq$8g%g(:N~<*V6&fq~1j]tQEoӟK8Eb"?G=^hܬQޭ.~=c6`YWZؽbgYW_lAADJAqʲ\:0`SQ9ƲqYWj9 U)nFF7DC&SI'E2#c<\\i*BŸx& '!ToRߵ f ejb/sҜ*3lvY $#T0Ré+ȑ̩ΩOV)iя9wFȺ|]ڠXF4 m<*&C6_ ϤWKug `sq_`8OgzzK8;ЭO(OS?+4Y4M8ֆ\D2y`?) _pҽ~_*"SdR9vy-|[{;mH4ser''G 1Gspg)"𯴊+b GHtܘq0?㺡Dږ6~Jc2J<*hO4so A/"LTޜ 9ߣ}gmɔ$s ;+8IC~׀߱)ޭMS tc Ƚ13Ԑm@#lVW,* ndb~pꁉS\ s+P' |}Ƶh=W ['?.Z!s܈.o.onX×R{A:?'-]T'J׫Q ówi)@ Q"pRRx̋;O|pJ8eg.II3d䝷/!":,٤9_ڄ&@l#%8Pr!r!Rښk*2N::;. ¨@h G>xI\c)Q_ 'l ǔ>iNԫ fXj龷.gC U9l]czt>gmszOZ-gHZ3/=NhZ0Py0~YS)Kg1MV(5);@X%z+%ok$WjX(E/.B. mayoVDVV&\ڷKYU}Ђs!N .2\QR#(_d3e8 (@-w =-ln^`>r5Kc0tVck eU}1s݉G:杕=c7rzSS4c̕ȣ@ B,:M2;N3*Y⣂jbolmi 33A1z \ oMaŽ6Z['cjVy ܮ/a hpe@eet)bQ܆g8/_^z7u!lp8#Dĝ = 0B%T:n)O vXX}ll׵}4*P~~T~85.-#r >i9O]W-!Ci{OhZ_±wWYњr~?+0Q_sGVÍ ]Oi}[;@I^ej~.m`j7^oSIZ"džeVIG> &.}vܞ{_2zW ͡의oFiu,aKdΰԴyAOK>qZ+ pYkhRpC0v,'E,}Q,սϿ#??ߠչ .p/FqOn?IK+:۔KdԠ.CUm.y\v +i .>ƵN:p3gYZԿoFep:P:Pus:[D|sJJ=ܹܹJA2wϜ2du%$ܧC"xHE y6p0H>Kbc@zzwZuZ} SS-wva.V9Vͦ iyѲOΤ LEUֿ;;ugd|(ʪʪFS|шA{[OOp/Y@QŪLq:>Mw6ŐC7,'Ql:MWo,HS{-'?:!s?9E.[껒oM;%ֱޛ`q5SzL\ y+F%M:Kl>uAV&1 $#> 6uSk*$zMB G$fGsE-uΐ{WJ?gDzqBFmx- SA2:ATrYN*e,eR9;RڇXG;)+ !drZB(O#;7v"\H܊j^>1y?ػp&3"dRHvY)w(!#:NVW#}.ߝSJ}?קqz}J,gв:iEfp7秏QmIm̫JbwF?rXX̰%vjjcjz Ó;Ⱦ,2|k|rnvq/ Q{m]OąрMFSxX]G70KdGsE"#CϴsF]W Րbxqh#FlQLp+ ePA)l7ϘMI3k[|ߤ9c@{z>t _F\9$+C?jm<zr&ki~2tרŌ+74a%S{}F3!]Ndigy}}>8딞@e!w~de43J[f~_?Ӭ ;P{lyȞG !c/\xYĽ -re**1:#jXB5 xNp dT]RV"g&tv_s 99m=X 2˩S**V ߮.6矋h#)B*?ȺLV7qpS1U1} )ꏇBCu>sw0p q S+yJr£v"^6w{?͗BhV(-ΦuEmt<4cqn#҈elV7Yc^A= F'"fSv`'0/<Y8`?ƮǺ5dJpF@s%Y|о()0m֭^6[<)dCyobD;ܮKYcSl+ch b~-=mף#z~㜽_H`?& Z#`Q^.bQM &P/82 _d|qisoқxTb$, eW3FZbK?3&Kס,eϗZnl-`ÿ5wk5sIuʾ57s,P}և-oF!1`(Ӭϒ.txr[Ɵ0Am`ʸݐ?Kw y&j^~H9õz| tP=t´68"9̔Q4W{ꨊUp8[úU6vO倃c0^紨ݕθv.UΚ@Y SZ<u&gs_9}NN,-'\ 6{>fXU5lt6\\p|cQ@{sXج.wL+&r_{ҙԝ5Z7at*8'C=4tqi"Z}&xs3pgG<|^Q|g4I&2[c`BUJBv 2D4I:@DB0[1ѕ}Õ4$ޛX[󣢆Ra78uj`ƀx_8J&ngc_O 5%#Tb axf"c'.J[>\;xw%x(d.Ni Bg(fMţA`eVtBXr;hF']񸵽5LQwU?`?ƞR= bq-I:n;,ݽK}*GXy^;p+ްm.;;AAe%⑽Lm?iVI8Jԙ i~)ÎYz$+7Ѿ|ox sӑ*7< ޝӂ] jʆe2krhhoOw\p2  W\}}JHO Dt:"ss;&"X;wES#;BiYeND'k/U -%ADL,<\ cgO2,3,semDe;]p>t(zGߡ;iυ!:ll8kY;GM&!&!1Ԓ))CJ<"DDIfIfا.eE/X~?*f靘b%5GjdZ߻ϧE*NlU}|s lFNp/ҕ@]VE ﵨA/,>rt O["(^R3r<^Ox7*cX `͚HY:&Q򔫼GLi) +>c>Ϥ/Ĭt0["ԦJK׾f*_wkE1xOVg,xD,:f-jRIn 㒌ʾЉBnf(> "|.Ly]$X^xggPzslsKaHƮqUc '9.e `hs25QfՉ)nJ9[IUQ!Q2LJr(̩ccVG΅5/*_Ivys̹1 BNa8NI~2G7N-f _Zߑ/陑[ aw#N_Xp0jF:r5o8VvUz_P2dgވLL?@ 4-$xД/K^s CŷNZtlhSP4ZoNRGF` "ϐ[n‹g`ᯟ.~R:?}zopQ~&ywo-P2fԜԝԭ}aʼ<\"DĦ#bzn_GvQNruVC [v)2#,Rnȫ9'I<44Df ϑogaNFHl'^ #UkT%}Q9GAuLؤ{'WL f mI0%=~e#0,FD$.8ŒA>K ]dZ|\s-IW06m^c'K1^!⌜HR z;c@`i5^JӁ^I:X0x'2GneQ)[j1=Y` ['~AdUhWiopiټm:{#忶[6g?"j3&{E?[ ͎l*YV=|cB*ݧLIm_R(zgyw2:NF+;Ԩ[P^ܪq*'1[qʼ AuL[\|&tz}ٽPRev}JW$7<>&h NsK}?{tlΩ+^zDCמ ,hOdiZQ],2 __ .΄Wqȃn)L P IF CzJL>VuƩ:C2Q'18tt+;'Цc˧&:7u)RbRU:X4)Al`߲xsf͖4*qo:~Sތ^GG~5 6Գ+y3hKN,k|wnKaf}262V+Ledw1*Ծ=!O2\ *99>-Dz16jŏ=5@ a rۨ:zM&Ed8=%;kSHnB$s Y|GPGIطLLxm*gB}fK[n$$yB1>"3R6UUu~p7|oj|>etwdZkkQ@I /N&N47BZ,[Iظah_yDa55P5γ6o~pu>>l5٪̛‡=^ԧ(̡_R=~qY%`{\}ƦʅB:4&_k(x]1\y(AH0~똰 "cu'<Õbl_]h*dteiNs-x5x/ػˠֶtK7JH!%qhD$)P""%HF A@P@}sd9GQfz6Go{ t|TaӖ¤~~Fw`i oRlGErugMߦb-@~;iD:Nc g^l=9,HBҏᣅnXށ%( X;Wr, {B;>ay~!tXr=AS -%_%yΒ97G\-e3cq$쳜]l6Bry141q`ǟ!^xnia "`c}aқMh)z1rwA2ff>,sc9HAnxF{*s`Ej.YhW&5n_dY;֘Fz&G f[ }H\2gGyppr+˗핰DX^):KM*blNzVJnK M7@\3]*7`'"Cc.ܿ([lxS;o/&E΍ƈt ¾`)^wm 6'wۣyΤiTNL5BK++) L$A*<7]].BZSMWp7= "n#n_ Vqtk] yPxP@}Q TcZcDNߧίߚ1JUC:k&T1pYU{ mHi,NLk$&88TN"ݦtto+rVrWr~ڲѪ@zCNÞ3NQSD8=@s&6zR ?תUwR*Ι3_ 76(ؿXP4gT'az!yz}/_rosuґ \POΦ>>nBm0ٔs毼iƒ w47'm4ґdEdE6KK`Ke˚ʚl#ab)4ϣtgvn6nڞ. mo)1Lc Q{nidrT6 QLu,|-{1e/inp6U\d QHMZnrgesk 5Dr1943bf ~K\mFdY1{9 vQN6B)$- ="qCȋ6),;YfeA^+,ch]A*{Stu)h~f6ֈO罊Ea@봏;qaq,#.uI Fоt0e)Vڭl|UkǞx, jNn@Ts S*L}&zLO-ac44Jhgz- eh0 `4cH6Rf <Z0:Ղ[y|SCWo<dA}v\.nr aGzt&Z>z.v 롶|1(48fN9պ˼[.Z{\%r-XbnbP=ͫ,O٘2 f~Vε)<!9Iiɍl),> {A/OvS\J;%Sk W9~ 8 lcAWiJgG6uy3΅)UjY%P\KXR"xs;uqk  ^>n}A"P)ϻ~ ~p|Ӵ\"qE7#ψd\<6QϨRu/+cAqC^Q!SzV0=v8ٌМ$a+&vǕgjɫG/uY~YIkQa47%V~ŷ*x6_vlyzO~c H2V^+6Rmjxqdˌ(|CabYJiRRt˕s+kB3o-f`FwSiL\%x*"|h8e%}ZQ=:hb8~?OY}kBoBϾ Hl¶ L=8[s$'}Ōfh +ЌXSCǬfzz ҬȬ6kQ<@ <߼&ſo0\1?T",:a4-ðrC ) ^wakz<{ ~H׊\klJLWܮQ2s%9t0y< *;&L=lq-8xvzT)jo kl>޹#E$2T-#ؑYU{+Jz9pvBC#޾T@}+6 X-nxS_ ײp/Ew(^ f{/.v뉒t*72[hWQԻ2o25̽Vw1o#8/7W2T-,)ȿQ LD vv%Tm #S_u-PӒךHkBoyKcm?3,d|rj㙖< b_WFj0|&CVDJDJ2'ٮf)p8qc**9Q]A0u-PչYPf2ƏbbiiBYt 7 y,.^lTaa -Gеfd쿴Ʒ#t!tR8n5lID% fǾYN"[ىx?>þj!g)ł^hIA ΃=xx=A A 8F=yðHEPʹ;Tut;0#sSX~u]3%#gqi M>;%0˥"u$M D&(҂UP VzcU&|́G?t\-[|gI[oM~z_s}n~bS+ڌ&i -.H7aONh{AQ 62.虾ZRhɡJ! @W;~ nke]m1<-uet aa14E}ePP88'*ZL?e].TKei,4SȆ~X쥬0ƉnU0:)l3Dyy|ey  ή`*Rӂy? °rWQQ T _= gi)x)8@@Y'B!0]JJu=v Uq}^7>̕L"oIjZlMgj6jxemogw"u6l?MU~s {{th`hWvX lNrҔşN%x&J%r#`\7ꩅqӦMf2-S*gDH\{3V* }6uOh_`:l/v൧(vYu,vc 1i_ gna sJYAB+Ӝ r:O:vQ'2 @X뎴ʫ=V3l3P|9_\8$y(_BNs{Mg}Z6O7۷GD64 c{OΤwk"}_/B^o#`AjCfk*= 1_>Zc{0Tm_$t #Wb75(0ˈ2L,F`Qa+y-rʚ_)ؖӟ]2G e\Vs42M0}?0&˪I>S5%s$G s[?6nRx|]b/O"!G9J94((TGޒN4dOE@}]7bڗb's{/2r.=zb|-M<_&/+$0kWEx<ǜ; 1ϋ8|}:OeHi=F+[c*B͜hQBi(vzMkM63[ M꣋p'E58/U'wϥ ) )AN݈j:&1;n4xdltjxߡ#V?J; S.1y_`އ*BX473736v o44:G'5qpGnY@p켒u."fi4#/^OT9T9p`NN,UUedx-B4!_qkӨ̾/;d҄#;>;͝1r1`ߖ \n!,q)I p˳FCQQ/9ߴfS G += Xfщwyٍ~|`ˎȘ lU]x~"6 .wR`ra*-;ɝi1μ<$[֥~Qd /mP_€ڷSPq `?%?ua(<U?ՙ^ٷgFjY瑬V~nt?O*ƇR{~eK:MVZnkʼnakoݶJ:B>Z !|30e:#_r'+Y%rnx.n*^}[Dwzݣ }FԆ|$ ([rX%u`hZb #帊o\˕2~kCtHpi&PL4iʹ(8x v%Rm%RɟKЅdOP5MZ@/^e<5K,1Je_G$Y,5]5w+)d_ќ'{ս?wsu^fmfzHdPN׬qj"ޤNn+Z acN-0CxJw lzUj"iOk4m HgWvRʹ-6[:kiWN])~3=w[M]gHGlwDlG\vزVy}.#F,R:_kcFBn;0$ymk=83Ov6hQIQxxF|[[]WSxcN5qKk_ Zsn`^c'ƈՔ4rN0%RxKՕ+J_p ZUUɱznc!r!r{(MAYy"YK,rB{z[LvI~,GQ6YN!nVT37RĈxע{izT(Ԍ$*lHB+eGQ>S߯默SHh56ywvXH'$ZCdk1.fNO %$!Xw\uT~m6[r3Z=%r+2PtΠr"B"mFmc7q숪\Lw9o J?%Nlw($rP!2cTy[w抯H5jI+m6ƌ3x(AŅ2kXS_S}>7}8W uaL-3<㘍~>➑`:<[p**t>ew? O֋ȭb=ݓ: leQOݶk=!r/4SM, |N67 li[^؁])]uvҢXaB}l`$ypW/aG^}3D ;RE YhI= \q4z 6Kyrs`scPco{`9\LXFSt^.+DJ.ej^-g/ w{UX~f շGZYYÜla8 ⵵7wi 0j;X" _~lOuH$D#QHOB!q`!|;o8}Oa_C)|GJg&ǎO٭lzLK>^;u u Z8gW.:l״c2>7 a=U5I-KK tNr4`c\>KFFa7`:n:no<֬6Ε  {F?wy*<5##uxׄ;ǻ^xO!NXwu] 3Ofd)¦UFV_[e0Fڼڌ=;+_N8Tc_~ 'SnB15g-^d73Mg\3S8cudhv},; {nțt-n7~"4&?xD>X(*UQH҈$tmY&6FU:|0?p$,O`8M|:o?Ӧ;5l'Ƶő\\Ivex2O4ؾזIL\כ0([7_LϵUGUf#zw=-Yg4a| e*pF.gH>F녺0vOK03{^a;{:{"fUd 堭_ (A2E9 k`oI0>ӱa'*Y?hpvƈBg^J !..?_} 5WRWZ/ q.s.{%οbUzUb}򑥭K[eg+f+2ľeF @{Oi)ɨ!EQ@س~]VyK.q?l_k)v Tk 5P9ڱ7N,.?tvzcFk cL\ńa},tOInә JŨ7x4Gxyw>*MU/V:'BjBpnQ,Fp7K3Q*NW 3}{<]uz_[4nEhH@u/(%>b<rAljdulm#~MF27$ARP9D@;K,GmE|+&\,p3LĖ 7J}Ѵ>&WgC^BIұ:&>#x=$~71O~B1C֮q&_ PP.b7S%*tF `ge1ah;ᶧؒTY>;An CZt>|GIHI8FǜmuX2>㸹@,qKSҶhRBe? - !::JJA~lf5533Kv|)nE; ͏ǞM\UJo~!b7mwF ^Xu.qЄ:ZmBbR?;لDq'QȚwRhl4>t߭=7`x+_5يpʹ=g& p)͵5U*;|:^rS "ve:g cH>EmtdbyLT |ժNz/n[Qm?(>r>WvuD )sUlNK6Cz[]=ຓ"h p/Fޑ.;*WJն .0+&SA>1KH",l`l޻3U+Ǥeino3C⻟R]ƞ3$?G=âlxFsaھ^[ j/)1Vm0H݂l-5 VXD2et{%du gH8@͌hY ƨaaD- $5Kg0r l\*&hʻeȤȤpޅcvUUx[z[F\(}MMF,?U:58N|LD Pڙ}ਘNC7|TW ? @\}=\~L8XB%hՑyys&'GiH~ Fs&j}P[.j,HآHI`:Vj.1gZ?gY;e |A$BlFs&cLD'SA1YPfcm{%6 OITR$lGC rFڣ*wjYHGcęaJ`]=aKټbW82J2_wmwnS h~u#Hհ1/(Y$Fi'~ MMe뻸ź&4oOL%0]:5Jy!9u ~CvSJHur:'VK)"\bBOC9`_wbN%IwT K4>B}~:3hLq e0;t}O7M;qฑ}{eȸ=fRDw=36Bu /X?P7h4~d_NPgBfa4MN넨,l!= YFdL6Vӈ ԩJuBé^"` á B̘PlZԑkAj %s's2xSƜRBiB%tt޽~#!L^W=\lHxiimz#}C4B&޼te@%X%8?H3]c]c||-& @8}S#~ YPfЍڳC9D:dh搋Jރ//{jv.t9j_uP_a w֝QϜִD%YtDHo'ȁ\u$SL94z ;M^lGs4o)ȮEqEd[L@xVr_tr3& Ko^JY6m "@2yP寸ǤϰNo尹è^r#/Pm"0wc9j|'w*]ojtOM]yЎcZ^JVT[m U$=ߘUem߉.V HI'/w.w6`>x t=_zRf󶯝T@6piL0? X+GKkjiF ɗ>ߧFa: LJřs C"CcR>E>+.+y*M,#`[-z~h>`lQlHU ŕM Ll` *yob\Nj{ɜ `PzTcC8l`U-RS`IqyG$Т4lm雾Go؀Op'b_CTgn?V>&NX먟ʌ)k[6V&iQԺ,)S, 1zߢ+ϵpČ[YSUƛZ,@PMLi*d`|_6,LO^gMLf?mD>dż)̦ u6)ʌrˎiQA#rin 6WНe ydx+_ߧP3LdΝiV; TܛAusd+9c> ġp܄h~i"c"w;g;JJ>e [m@z>PFшψZp 2RF~7S4h|ng|6H,UUUDz?E9a(I~zn< l2]'/(oOIGXWjˋnܖ,.:Sm6vtIr`ΐ@\2$Lq=ؚU+ں_* #:ޑyNRoɥ=ڜP.+T7g)F5ܦS;ƈ Ւw{`T>,}FbeOyFT6l`F:eF!U*D W* ]:C^ l`nWO!d]f |F#SEe2"[6@faW(\~btך:oMz~P*>Q\a0 wz'>FLJT[ea#j A**eePs7]r;#%xxQ&lAw@\K G' 661X?/#])B " d@0f ЧЧXww`!\鹵8޷)N}n{ng&?BY|fܫuPr%ry^OD,9v9vpG#I 3.3V>-J22PׅXbI,ḩ#Qi]h=[eAsawr{F &۽IusuNufwjjM~;ͩd?fs;(TO,jq+B:Pm۳Q}Kjj nn5FIg|F (@2nյE{tr)¨`D&@2U25ݗK@͗}(ZgϷyA JپՀ[x>HvZeLd.''`֙My07ŧ;H>H3$6.6哩K}}[nZ@I:Hyxߐ?cNslGLO7H mmŸi.`t@׆RE|eN%}N5iݻ7QYQe 㘝T+δ؀ފn}XYyjG=aю?f1&VrY2HhLㆲċ',i53.2qGoi~ ފ -9/<3|_o*h!4wM^}Ud`۟ԏY4ObpRx"wS io> wv[#|BLt~FXk8WƟ= *Σd oW%t1VNƑBgYy*|nքu&Ye$ ayu++9tM\Y Le7vZvDZ0 Ebmcdw4[a;ߗRSa c Zȥrqxf~y*2=R+IB~Bʥg~H78 PSC`^.%tR*hrkSrHuS[-j8N*H4BD@%%TqnQ$$[P@Yw yA]|άbf~ {x?h{@xJ5Aް|&V넟ѱT/='x> Gf"\eiXz !ag- sL_9ۖ{oJ~Px"祼Q߱?ל:n` `>MfMfI{Kf}X gNZ'5 Hg-@<`"NJPz(R܃ ^navXYNLGF-m(m S99W3Ȯ'ֿE";~x8Xugh5ѵѵd?BYol޽Ԃ))Kyv fXX1y4ICRW.#tk&o lUNߖ{6&FkIU`ۯж9g8|CjRY%ʤ'e}C rgu74dZ)YY ba\o(_P&B_b^VDVV&xQ'L5zZEOfW\&RŃ&}>chH̄T -" o4٥J$nIѧd\PoJX~!1rٳށ߱{N ݶ>_jr!j갞f!B}ǂKPӥMg /i߹]|d0n<;B'@Yɰs*VL l`IS?:Wl+Ktt/TjV)d5_E|m"Դ^tdLT*GnCBzLecT?~bK!rT8X,ӈPvla gQ<|%K㇝`AkK.-~#$Fy깿vVCUk^!|gcnïak2[y8K?$Vi5,iZ*Ta7hO(׬֬`j13Q뗤OHhgHKxg lZ#n U_A]TG a O+(ៀaMp9׫EЄIC Č%#A9uf[hŨaa6 ^@)0p Ǔ$׍- |*J-(Q<;\HPP#UUjjݞû&.d HVQW%, ~6.6m yPo6|zdTG&!o[+ ͝trϧ*2`;* ;l1n2frڔ>ŭX{Gw̶r!-#(0k"kJX~ ٖnBڒ:.Bek$b0؀m7#sX׊ 9{C k h-Iw`c&}h79Ja+Y(<[RG˾ª݆1qW{qLUvr_X%nfQqsWJ lj{9&b;e'!D[qGHrL[Bޠ'= hI^.;ŝcs!>lO^)?̨!ž~̤U/v*)ylfMHWv*Tt?O>jd痢D_YCyi*ogjtOl4`L':&:fRa(od=G,8SÝC~fU)225gXXɟy}5#~cPʡ1PtTz*Tp>15R/#.z_~vA"-xǴ1fwC !`lœ~GhonTWΧ~?6+zRcbCv&)?ԓ 6*?'ܙnOa(&b=^vۆL~NH%`CMsw<``ͦ[Y< 84Y.~(~z4.}XL=iLBg3Ŝڅ|wVѤm N\i^ME:tCWmx̹;Qj<6E4USEeӫ0QwbvV> 6LCqS`Z Nyr셖D%H_r< dQ4"k?t[ՉRk"-_?/n-0ki40|]uuLծ ɤǤ3j8NwD44Mj'5@+1P0`6 E$\^"l07:jF׆@vP~[JkJkju'CQ\k*yf.Nj-gjMJ 2F)B)‘zrqUrDfRңnxRAAy/fu~/&%vj*yj!>OyZWf8M5=h >HF 7v9ÛfH I^T8ʑWkK핗T3 [.arX v x^GɷxӜ\qi SsJk/NIJ~pGG_M7VOG%V3'=ƔD8v 4M:Ezp{5~U=Kh01>)]qgk4qP#D})6YfQ0G>i1e[^۞릺`ۯHfI꥽qH\%r[U\HI_&$M} l`Y-ĩ22TD5_`~ 5daktj=-Ĉ^ hlGN)$4\~p^an㾊 70]%K.7-ev3riX^qlIe)6ɧ9ęak猪d9׿TJ69iQЈI4;.Ob]TAp3o9o.65eY+ 8,2E3P}rVkrf䣟jWJy܇O#ã[X s+nb@]D]L-^n?VwaS!ya4t T4oڞ`}Z>qBa~Ή/9XPS'-Vrqʵlf㍃~oO^JXg3(2W-:!=hn2lOmFw'B˒#qQv(ޅ`AkA1âg,gbf6^ǟذ|ۣƽ| l`VS,@V[?w8q#3{pTvJ7Yu#&#EɮddggG E\u}s]z>1HoM{|H22+C@sk'fvc Q@uO \d|jgT lۈ_YR0Hc\4qR͙+qƶSķŋ\j7qtR,ĎrKL]/Λ!o}m*J[斵Jg,V-a/[ָH3֖iselO" v_ǎ D4ޱZJ ~GKQq' VY.Tba8ƎwnѬ\Vt蝃bL(I^Iuv. q{Qū?~{ٱa yM#5|~NnهFfn Gbo׼8& bzp)0cJCđ*HM$Pr8~3G[[h)>iioa -{<hKL=\?f{2r*O:qEEn`ׄ@E^||Jd6jddT]Q.\.XOǿ,1VC>WymSw_p^x˦DWtGePntMhiw*kES9:#O^RGlb3̘>+md86t[9 ɪǧfbGhSڇW նfRb}Jkr\[DS)b\f`[mEcM3\סuv~ ꀘiEgvF 4,?C' <bRv}K)Qa\LC\h0de-ۦ< wW;5vJe,ӢKzuizTFBg3g}y95ZD;|܋g^7Ec ſ鎆|`[m=9A#~?X,)w#~K+.j]$0˰\oX얜 sIe5ʚ#c¿sw1iR%[w"[N"G:>|`r+Ꙅ[#U??PЙ0Cd2zE"<|l{[G0gboC١w8MMA,ETK5pNb!` W~ya-O &t\RUu2==+>@/C/#6>wDO\\۩/9 {N8`ŧ{pSYկ&|u[݁{b9b9B_>HHd[eeyuU3U3g)pĉ͉ yOU5"|Sn{E7%T-;>RoNeDWoϧ6-\=0{gh%6'B,ǝ`кCQmxԨF (j&W(bQ^+~97PIVd2lI?Fu^Eyer\!8!~#4IZ/MbH)AEq=r:vЎVΔzqǰTMX}OK\ZV `pҶ[o#Q5ܪ4&o=KlVj93zDwGP NJv!o"ӜRP[wF\ ݧFP՟ǽ߀3WF%ζhXl!P׏@H^yeĀRG {HpB⛊P)ebcbL"""eY?Rh> B,\H^YŪWreV-ֳ OE {=$#ʽ E|E|gAΣzz~Awۉ yII̭R"`NԔMPvzR2>o }/8W󿔌ٞ QݷήquZ[iG"#'> 乔Z[SXӬaUBNAg=DqtDE"ǩc v!I¬ +9Q 1H'o+"1+I=FnӫOX̓NC AFs VG~+ko穕%'*+{3=M<`8V7$Jp7 (;3 eﻧ2ϧ!Tnd e aXKXKz"!͡zzqMd͘͘ڍ(/Zo(Cq sHjYv뽯/$1Cƅ:u!K˴Lfc { {&󚰳3v;X׫#L4 ~W={rfkf-7)ۯiQޢ l`7Miz3ѕijVrĐK *$cLɄ2K{-AJ*FmG܌ұpJ1Pr$Xi R 'a5Rơo yS_q&4~>p5jVnz!n/T5̨< o^h/2iE%?9b5?n{ =0{!M#$UT]VVOwIMyD(@[J3#A[ "OOY#G\a1SbA@ h)hi uK!POOT`*i?v\k8)ϙJnpj׹"5M )5Y*d Ȍb8`8 Ο*N*NI||"F_=zZ /޼ v~;,O6N6"S->|=E>m0XJ3uh=j=Kwlݜq`>V,nQ<0Y42GeZN#FVk-j7Ω_0Ӛ{f%lIb3˜[* 6Ze#7ڿf2lkKXĚʌT5xA Snqݼk(He}591O$)O5m2@Ԫ-J Vx |Td`q}y (jp}mE,NգFM𳸊<)6%dHVl< 22jwdߕ,鹿F~ ZFIm&v&¿iz=?Ӟ@ڪШTg$gKwEi ]NlzGW<}Z[7So x͙sZ飃k!rnv;ndW;Y30 ;%[ı{#>[44#y8ZS%`[љ-+M]y`P"KLAe n.X@}mQgkXjX n$q]m;Y;ى,QsSE8 Wb9:4PQu ak?TNCdj:ځR`!}u;s&s$/3hSzu6gB,{ɿ-[򛤞F>^XN>l^ w!^9}GcGF.̈2rLBdgg=2"]Y7IBUsy: lLG \#OVp~Ië.sOl2 =(3v׏qr Tw@'g)=E)VCe+Qb l@n9k\CaI1"8!t$ꝌnVr=IUtq$n5G3goڇvvȑϢŗKo( 155ٓ"0 2\a~HjP񧚧Lc=w+dGs#1#6ee7JJJ.1 6m55Yϵ^v׹ɦ wZ(ItbBB@6&@{)T8"*f{O G#B#0qĘUe$!!ϒ›»uΩ kĊ؊ؔޔ~Io\{m΀9r; x߈:LS kwB >WHDD.~wQDu}I}I3UUxݟ[ :)Eܷ TJmL)umxoCS<4}l-Km-5 ag=x H Q|ۇkbdhA]uCڷ!t_ѯ*ՙ̶)07D"˖$њRB,)k =SlTPY ,5u!yuG5}Ӯ-D;80]wArrEݡv7|q{6VaMRHAyy&[cH\jT)O+4Ye!BNy1碬P$De]66.AvBr-Y%i lB#=KV{ yT6MM孯66m2/nE [|ax`ۏj!)/  FIUz$jbVc9l`7ݨAQc$.z֝=|L3yWa1wN:Hl`QM1S8s(BR~"Cz`lJCD۪m1.~^ԯOw}u9i% R#.ؗj2dzt9i>v<ZL2qDu{ڿ2^%ai^Bҽn&mqh Dk>]k6+ŭ?AP` mӅFډEAegh>}nɽfʟ1M,."1kN29?K7E)o5RUqUq aG6@8`n K+W2tqׅ >qYHrH3iy``aw"^^ҷ\Wgm i{8x8mvG5v4Ơx[fK{ax KBr-{ >V>QGU!6 ,]*]A ?(ɩ洈kkwi!Y/y'6^M njWWR#q.Q(9T_ ylWP91G*Ԣ{{Y!NWwJ~za|z2)~fBAc'&Y/E$n,AЪK`l`olf'p3bl'ޙu"m*;.R ljn:zAwٮ'?f nRƫZ_Qcn7xm#npǛjl1ygS*mF$4̢믢#cgV'΅ұ<ඏI|]{lpc5j'O┶NeՌ (V%KV, 1U'w$\5NOZ}T?~L"$\LeHhAbM0O] V؂s ̈JL,NGq Yl^k7zZ@덍rdQT(y2N EްFrZLGo'RϚmwUmJ)ӳHȽBg.\;IR~.Ef% 2-|LUCWB}pGȄi?zN5u]*!al]iRgj>d]5ew'zHȠE<t8TJI+@ys~kŅNQzE+K-9[7.By(հHL4W΅N~\2hK^BmS*5FjjÑBVY˾$'_H;:*- @}7WWgg٤gڊ '-Vٷc}36RzO*{Թ΅};[FqdG Pn 1vW7؀6qDҦMGY̪ј%O29dI B beL Ė_%'ZrA^oܪpuW3u!'MUc2ӼtvV"'".oXYYV;8<˷$Hx529NJy90LyԈB|I˒#cs/}&Y@Oat$ q&;.I(z;|rG`T .Rz1('IP[ytSǀ]m!a!Whʧ Mop$Е;F#T6|r5$&^3 8%diqJ*ʀ=>2@~mr #:l`WDXzfֱ" D&o.`eL[{ GZ ^u /MzX3LlI]\{K>}SƉK>|.Ǣ٪wTǭdoe$#{戬Lʱ;, c$dWJVBPBQ/NC}nzw/Lo6"Z_X_c# DZAX `[W B9zŤ㤣{QagrSha_^>VW*ۀ``*K@^}v AKkrQgXn!x# wDk\*R޵sv@r *.4::|m5RIRIdɟg;s8&dZFÀ#**oc*]M1[vP#Ǽ׊L>y@s)ɥ:fun, +ɫcsF'Xi # Bt}4f3:̿O!)]^^d.6Ge oT=[3>hϫbKVh;fOӶQVζyLcG#m@+mh ,:hwZ4@k̡lJ>J>: W9mO~!B噸zr95A hRfhC(F;ittjTTqryi&M&M''5u i<~Zl8BA* ErMcQ[={ŎśayP>cQQYn FNhHLJOKNSdnT AH&)|dfɭ~m6h6KDoKEXC'ۯi"O[v-Ҧ[ޫd桯M2:En6} X{ѷY3h9tǻl)M ^Fcy3]{>&ꊧmm/6a8t?U硸Ayd跒Ek=ȷ@pzzHSD?Kc^큌GFMr'{&6 Ȝw"|I|WW:`j=^ ?Ӧ=sS(; v֔%%=cI(|-|DYZR喙r'AКGkq֓{nf %9-;y=KtO)W6&A/X%m8&_M<׃HϨ#&^܃ SS/9!T#T3ލ̻EYEY멖CXxsmoR pyffkbBIޣtqU?*oe5E{͏&s,=o% ;,;:02b戶HVR[R[HRHNGY5LGD0wɜk%8 A‹5M0M=aTB^3^[b#K ni%Ԟ_/j#+?WͯomNN$޼̓dJv䦷 6M`t֞Y0&\hpfa_zRiuTl_3! ivx{HvzyoN>"w޽N`Z͍w*ruq$+)ɌX:,VKxt4KN7|D)ضVk>|AGS-{(g׸1^'^30kN$ {WthHXԮ0%NJlŻGd4s\m`d͓qI&-|I4F=p._ޟΥoS? u.N3\i1陧:wv~sFsUv"-Dr͙0Uu.sRl[~tCpV.J<c$qΔH:XoSӝl.YٓH3#؆To2)qNd$Βpxr&r ~vs4 #ɐe]Un/5 bJ|\1vf )JTy2S<ŅZմ8ދ<6`:Cyʶw]cGoƼdi`_6ٵߤ,*[0nz~0k*0ķ5CZMJ9 ?=BdS$''^y瘼zMrw#Vb`^?sI5q9 ;FrD="/GFcJ/o(H=~wHs.Y`sU-m" 6HS5Řπa,=~˰rgL>]3ELN[z4.TU- , v8])+ߖ˅Bf#TM@ᮡ#Q&Y\8&9-RVOGNV00T(y)y]zNKm[-T/XppNQ3XSsH>jW mF)/Nr0g oߵ}ޝ2>ߥ?%" bm4 Xr..\\/FMoւsgsfcTvwoS^Ylyn5gAY'cJOZ|L74tRc,qFBlus"B2g7;c~M(W~?LCF=j}%GnD]pIt7 )Wda3o2\pJ9 r<ߵR c~HDh6b=(s7aZ$$?!ܯDKnf6O(mtORŴٜy611TL}zנb蹱scĸvXq 0n( F닥AfWWm_򾀫 XZG#X%(!e TTCK4}F3F3XS?Vo תٯZFiP戾O'6^+5k<6۹s_&W]45&>ry#.@! "m^F<~3D{~{j}9+#Ca vujN3Q;"V䰣woܷgD}|SG}m :(7۷a~t^ت-3n_ʈ!*֪IHgu эúQ^!e3N" 7S$ԾҩJٲnGx>掌M唾Ů6ћK!u)},VR0ڊacI⨪m ֥BB;|)hvb1 Ow2gDtUiUSsG?)^kL5P&mMXqrg।`ۿٞ&f!mun5&>&}$51D?"7]1~F?L_HN륚Wr`!،=#kEU(JWHbKgؤ3=J<\*$|z;{l`qr܇aQG)7y|9ςje`ۯjz7d{ms;W7,w*.tXiÈ 71WoG^L=J'S F0 jN]iJ]Zր>٧kkkS΁t&&YcZ/%Qi F=tZ/B"DRcƞx, ;[8@ ~_N,,,\o8ne%{ylJV'eed d&މ̐;Dݟt]oq?]y}.'|N K00`q]x4ɚVKQQk?:Vjaa圖o9m(l;\ U{#+L3ҙC 0x+cFWG[P'H^1WNM=U%JuJZۿ@pnPzpۉpcդJ&++7z_ML ZJIZPΐ%_|*^Hm&e;n |y`t-wR cIu̢R!o Tao9vltXi +%*U2sU|1 l`hʌE(;ⱓ8 |QDyGK$,Vml{q͞9N&c5qWK(:?suH<͟&)ulД-ӿRA*1vrDR}{瀭1&*XozgD΋x!7lGӝ(D1w=$JXٰE6i'6rV {Ԥ}*E{Rl4GA ׃ݨ>Ά!-uZ>'[u@H}= [9dۉ_k?x !6£uja1ߊ__apB<]G"Q&aY \m!rCu$s*̜a cu*y :ɂɂd.PF9&1&1_7_7Y4}VU%7[Ա~ZmHA#8ck01EihxxEr LL((2O &ӦbbZ_SG;IW>-uZëG݂_x+Ymq[4%%5 Mɗe68=;J]jTe-yahat!!cʍs-l-R2z`?4Ü)% ML_¬"Jyy`U؃ Yն>QMwخ)s]>qhfs0pŷ`_A}\f&-+;l=n;v,drQT~W ^R)Ʌ#faxdÔǫSYŮ l54c-p+K@ڭi))W0u0N+ t y.Ĥp8DJyS>5^s\ /='NQ8F us])@Pr v\HpUƵZ){ͅ Xv-k.%((<y hߒ,,z8*W ε8}`AH%$S󇓊p9+X:uS8,,8!iiQ֊k^o^Oc j 2 i}fErD B)%@A``1)Vҟ;XXY{{_{)3$k;i<=3^ϧTk>l&wyDpBv_?sك[d=Kcl%h7QX{I!mlš#,6O=k"v56Tdщ*jH V*u. `_f0&L틺<_6I=fċK|9`&1?ӌBV~(VɡȭmOΫfmc$\ҲBFsr$\)֋3T:~e oXlf>sqE亭GsI&I) ދOz#艹qԟUkO|MIU/*nEz;{/So9 /1YJ[leO%Q Ǔ0яW{xNg*~FB+(s~/8fUba9ǿЭ}%o<,io(.)~cYz<\9#ŮŮ;t'#\"Ty>8,,ݘY 8kaI`I@yYZLLkxd1n9\j!t8m |a׀po33WXW~eX3jԜ0[D[D5Bg]+T$TPvjWތg^)E jhcS!`:\Q(/RyVJdRDd1_hҗI Vr3n7Vep@RS}4qCmkj3O$4ȣDǼ!症P\e=*M3I_iI KqRU.aso)&e>G6vZcXhaM GSdBN<2ZvWh<2SLMT4 ߠ6p?No{Ymxu 4Uf fD1Jt}[g7c(^{Mѩ󽽽k}1ǭʥ oBngPCwEig"饵 ̵Z< 9Z[-]@ݬS.!UG2Du[>O<|Ś^s1?^S~\f}d_c@( 4EJM|#-" o+6F' 4U* .QQl|O#I"lqmi1l''B؊؊>vX6~[uE`;-U\l:ci/(W6zP7i<8}@BAgXZ؞##Ҳ@x T`ɺ: g\sUck #"*B:3g+I W;b#!.wr~kۃ3l\!E}mY|qLiK J_ lB=/v~:,I\ѽPQo\_Hv, l-0Q_p2dLE8QZ➘i;y]_3'KPʚl_s+-h! i֘ɔJ E~T>5tKޞyBð3Ww;&HJƛf ̊b> ۴$~m(V᫝~m#LoϬOgU&wV2#;n{.j,ʐ9=Z$~ߌ!B|Cld_Ȓ~طG^^k:ChFژ/ݪpt\Bnn T!B{2Dև(S)%ǽPWmmfoZl=Ŕc4}ZpF >Mu{|'FC pP鑧&,?)p1VdINў=,m`/j/ =N قz<_O- sF)&~ kӳMjhh@6Enq*8ŕ%dY4k4ԜGlFjkgWo\p}a1?J?PC޸m!VPl`ZߡZ'|aKP','/^|lZ3;b&!RGOr䥾,tb~ŚQ>Tcd>YBsltBL9vͳNpuU[#Kql6Ljr9l}tRh?1"!"QCŲb=M &(]M,+k6|8<ǹHj7es )-]f&iN3H]jlلIw۠_SD(!yOh({eZE`0yhW)iLM` ZO%ȣsvfs68Yp||-QuqI)XLc^-h#ߗ&&(L cw;Vhp!U  p/tN2xlnzp @"& NIeO\PAA' [ftAYYY=lC89ŗ1"/k6ӳGk!Lk.S=#}^W6?<6ח}mq `5sj-?x%t:ۉ4ƙY͵fcrjv۱˚! s dFЌ9}J2qΎIjN<jfD+^j2s{Nu?pٲ7tP2sdf&#{Yٕc̬$#dc"ETVuԝ~K}?=s]?>vZ 1~G17X?~vgږQgIx<ɒ|,vs`k!*I =  ׎az-(`x l{Ie=gV;%)Mɖ."}PF% KQ.] UPy*Iv([|}&c(ozDFC3׮Yy`!}&3&:ߞ}e~,unLioiA8 Ñt HMttSOg褰`A___W%Sa{1+ 'lf d9Љܯ&Hi4ixgJm"C V1j^xD(E n4 SÖ`|re:/>MP;ts%)>h l& U_LZk }N&Qd;hy BҐm x" Wl΋g3*Q( lj{3!Q_* ViK<ӵ'r6o =&htIWK[9j\qݢlO\Y!gĬ.թuوFaQ'eY0?=*=hq3ڈ6Рn N}g΃ W/}M2n=ۊЅ?2 3"D隷\1o:AGNf~f;bX>U<8*P* ' F1d szBG5b B4Au1Sq/ɧ>iX~Ykq lj!9Jv=.Y@͏o`8Z|JJQtOo*:.-LA\^077Q ߉o#*cksFj*OCq3575_MrdwV9%-;-;7g* *^MCj 3f `|Xͼ# =ދ‹Iĩk~ڶKlgkeel" ddHgnnS׿I{5 μMsm|YvƔ%n<85k:cRvv==S:EH#etB u6'\ѹ`zuv!6Yq <;5Ŀ=GuR2_-=/޹nk~s=- jnC}4Wl, 'h]:"ЕєG']e%PCK\OVF F~{=$b%!oL(,~L4Fb:ɜ'՝{3bhoEj W7/\$ l{-/n\9e .)/aLerOBtnuiloHE 1mMW٦KGW ;#~f~HkdDS~rxs aaiBz- _QOPW+t-nzodJ7.OW` #|6r1,TRsY8PX-Ht6}QO}KG^ h< n#˔g}jNJ8~c_uk||FCXoUNi FItQq+okFm rL7jl*! /c_ɧ+*E0~T_5v`w9ӨXL"Cؚ!V-vU"N"\aZ}zFE`+PK|/I 0A'A/%%q&VJ̵mx2O>Eihmv H ,H$NzlcmGqi;)=ޥ@t%`u򌢌" 牷IYhshsB܉o눓CE϶i LlKYh&5Hð?OJLm ɱ~]'>_mr]@]@t=ҧzNdτz#cn紨M&Je^ZNY)Z%V>A:ec~c+n< ~-vsĄ ѹG֢ӢfiTV!7/WͰ:guu(Kߓ{V _ jmV9ѕFV|YL:0p~q"lOmqb ĕv qVԾq b:fkT*Q ;$e: R.VÉt|ŶߣCk|ퟚD] ͫ.Lc9ZSYNHjC@k'~ƀFځhJF?f_~xgi`6Y#Ebp5n'̈́&I\KZjy\qfZ G)OFk FPΚ^ILo,XW'BOmK1 xel?UR/Dܢ t$Kw쥖Hjp\,?I,+YCMhA쪗]ng ֟*Ӣ&QTOc>4I'{:{uAk/ʝ;L>4PtZ|\90X)5pbT[. "M),ȫ-Խ&&AA<k$* fO5ߔmɪyH6-vŜm.+>vMמ@~rdm.̉PF|S!*՞7QfiOYJ\Jz`S NPXUng qӛOqISgic6\6`CW}o!%b|Mj˳g -yǾ+a zr5ϚmDQ56XˆxWm U7uc]8_C< KD/){ʑW4Gpg-k`lnh*Ǧzg j{^4~OkVyJx:Kƞ:§ԡل#52 OV]Nͥj6 mtAJS ڙġYQ86}o G\v!H7+9hG@1L-x9qFq͕`^k^eĩ<<{y=._k+5݇䇲QPEXieOi5w[ǯţ|פdiX caA#щ1INutΣbQ8_Y]PMF/ZUWWwI.ˍ58j#jM6a&9"Z|AAJB-xӾ1@qmrZ-을D̡5-IO^e/`#q#OOTC$!??-?AA]k*6mRm!=BfJ08gFn $ N=r AHhΊFkJG::Lj|NWU2}2}:j5(/mgzssMeHbWg)+D ` șy`"6`fa/P9\65VaՎ.}tlLU~!%,ﵬA}fy(?go=QYz+aql?9U;ĺqeu}bO0r@Umkl!О#摙W>zI+n(6FCa` ݒ\|#E5Q&̅/&";Le4/(4{uMU0 lt>4t]﷏=uܮ>$FHHJlx =ᏋTu+*AؐWX]:q(P>}glebPidզa} !|lb~w gMC6 MWLh 8l~7!MiHS=6FD=E*GGs){}o}hn" zvWh< 3d|QxR]9N !h!\= %{`9mNvnwѻcQ"ͪ"sq)Y+X4~.YD`۷:VWԏiE8 _Ѣ4N=^qARۼu d0vi%=P:k֭=Q~θ )(4I2)3)8OmVKmCmC#P3N2>kGAY I[%U~p?8RI0U7Ug?IB:SSy #A鷍mm v}ޙl@jg[kG9ԩ^عYQ`9S{R{U?g>#uK9%x0zϥm +&J|T:E=Ǐn faer xf꼬w툙ŵ]h_Yn] f^uփ @p!AT}\T(*^aұrt}?Zk4`V3$a䪶e')(Cܩ2`ۯ'm #;3Ù1ol6=6zhzB#<~))cy.ZZa&5ˊ3bHdZk-* {0\-nԓh{>WO.m58EǓ`QV;40:fϱA6gNwIFWuuW֒_ HD¸xbӖIGl @%9Jz$ {dHR7o4|Xno:[l4;4;r%NBE@UTr`LL܆+؊H\uHpSg#> uGQ::;zUHx<<3>>u çzٮxHFt pK5$&: = 0xخWGDFzy夣jQ`dṱ,ts[E-zJUKUy?yj-7ÙPCdRlI{i9:s|76௽i!t"+| Mb ?kAn/Z%wwM]\+7"ll`YfBcJ]p7iIJsp4ƲC_mg:M+6$7t+Nc1aU{\s"`}ڝ9>Ps^&5ck3*Y{['#Gy>|M>9HyЏa*7(sїs"o_K]i(ڵÊyVZd)Y2[. }PDq_:ݍٿUw5b:^ d?$ʰTC̭Kus9곽2m|Fd؉{`@K&``KDqz3Bv7\"TF5`ɁB̆f- t2"t3jUwڀ l@k mVu]2A;*6+x* <$H4}l`_4YFuɫ 䴳lDe;4| lwM:GlIkY B,,q'NS( lj})YI@*@aW7J:.ҳ` lF|n{~^M{!49eҪ2u\sVu._bF`I GrC\ky3~6̞=1.?lgTPtT;W_; ؊q.\_G? SQM9> @uEq[<휠Jn#mx͝Qdvl Ɣi::-k t%3dS~^ml`7YJxWs׆'VIĢD޼Ùyl`YXPƸPkl%{!h`6\/l1gBFQ^{N4 ,FvOs;<YfRCHW̧H @V>y% l;-87.)F]˧̇eO`ߋFM>=lO#f?8$3W|V2 t:]u vZKΛl [H ;"OF6 `~$hl`5LٟhM9'zL}>0 қWyJ+NP6^Y73> @8E_"Mw|Rif~l`/^RƠA+Fbã28#x> RBNCT_mNyY(*VJԧ_?7k3K9oYU 3fjX;@k|R_.5( lOƯyqhf 2ݵiO%Սp7h}S7}7BX!2*8-DvFd&doVVQfw:8%][\yޟyHu ?Br?;CgL}g-\ްa29ҳN 3?{|A\A\ɖ.`M~o)@SΪ˪pʔB*}4.~QHcØÐ%AOOdQbQ~Y;\[_\t$s©ֹ $`|*WX" PPTA[S<<-rS6l迳ZoXoظQ@S(l@icGrTp9>-y1bV7j#Wϔ5S)g=X{Y )Ӟ޹Ogf 5/J(DhkwӲ# Vg27rѿ^ U񫐹?|eW߳Ez(oUXR1Wax):2Qf]PKw`K7lGv:+>T O0ɞ=)zd7[6w4\7wHt| ^_m%I;ʦ |ø`nMa+udžLS7܅Xk³g5Dd*cU߲c~Jm^!t/ݠ} [MQ^`chmKh'`_mDdt"V߆2V\N :i!uPBWbV1<7}@' c:-|: .eFlQ,cHjA<}{+%zYΫ>=~/ ˟F،|pd?ZL̡y,bQ+D{  ])0.{5kӵSVx8y ަWWQ8\R,{NNْ'L/|)q 3/&_Lv<s*o8%8L3ZSG>R!$RP`auIpZ.~"~8:џqSgnx98CS3ͦ_[(- tt!5Y&ddzCL\9ItI87()x *P+71`y;f &˷kJmXGu‰ƇK%FN4O y*Ta^t^׳o>t7(ځ)ɽzW1. LJc65Xțzϵ*g")WLZײOhpP 6M1ЮBpOdՄ~=i: zzQʮE`_di-+׆*0>/̵nsa.+Hzl ȯ]YPϦfԣQNi?\&]D]ΐ9 6//C~;;w+/<_HZ#N<+hh=EwQJ  m␻{= .\~#./ς옜a!j닝n6lB°~09NN|Ňmd|{ͅ\|c N90rCۘ9y6N7IZUu)Yܜ1#[Ag_%xw Zݬb .E;#W:+ u\w 8͒ YVGa_dRl:}PV sϬmp!Ԭ\/h$ {B-F"q\1la2H{EW^*lx_yw $,=;%iB} ->ڜ.8O—i;,_׷^r~xַӦOjf&~f zFfm(Ur;y|A?YNJ(-_E ﵾGָȲFPX}z6?'7|O]=K@ U d hjK/Y*D7S1U1E7Yݰ@@F8xߊ.6(dƤfgg]GؚwwMI\z;99H, Ocrh^Z2 ?9 5"O+Rai=P*eleUdέ[7\L[ߎ~[AAsuݭ/Iɛ8n^k*㝶`кnrQ [VN eg@RPKן{8lH1gtk9 Ig.Q~8g6զ7wQc$Y!p#,wov' ~$Llz{yP나IJMEL|֠ 3;mC T bD_ vbaZL tChHߡPOQ/~]4Q(7HyD} eVvp2|@C;^M:j>8^Y6N-ujx_gLvŲxtJ˱=]QZq:M)!"?> 9Z[5 w$=C٬E$}zWD.,1ջWrح/؟߉8l٨vqT3 ֮kkݖB.}44OD. hT'$z'+o2A]4ek I IQŖŖYYQ^kn֣y0L4 +:;\(J(0PPlt,Zdȴ k kFF-nnS(i*i,ޮ{s1L'3_2ej1`ٽf/DLܞ PXqW"4kζ9e8W破uzġrSebHum\Kl`'I0y~' rFIO\' &I.(o Tj1Ujُxs/.{~ ۫='Q 9QcAPĔhQP ռVm?,S G/P4S^2jof7KsWit(c&; *h"r䄭_Xa`[56;D ΋!iӘo ZpQν[X~KIinֆ`1JoD3ZST4^ m!(l{`-OpLqLS#MMNEddTl2H^j1%%J8Q})r{ (j<<V[Wf22DFAA,BlM(B(2c')60y =\Ag$MM]KX!ه_$q9D(λʉKCΛ1Q ?Xq\Uߋ_Kܦ'6ίB.szOIދӪ.@nQ)A#yQ`kW*7.]Ãӡs ۷t:D.u lCk0:ThOm=ÖiiVHQpiʲ+`w77!0uE. wT 5*.Ȍ*lǴ) ${6a):Q;\@0ʐkfyYM$ɶ0E1nhϏ?5?_y:U>llm+)ncl+S`սf_Sى RfXX'ksقrrmmg* a%?[^t WȈ\\g0%,p,p>TP O:.v\ltGpcgrAeSivWScNHH4pkNw}.bBp1x}s`0f>f)l2u 8ݵRBK$QJR iQZ@PK[¢ҥ (i}l̹Wѳ}Ϭa}Y~VeLL:j>FP7&fXsh64Ft6 퇽-0v)ܟH.lglާP-w%+mZ]*O*eZk[J7(|ire}ڧ':jb׿2ApTxU7^(>hcI̳a3 J ]|w||kBb3l _ =^++`p㕃 +V{Ғ-w\\D蛦 :<7kuHI ߌDA9byyĶ/zmSSތUKKI] GRZGGۇih+&h|r5ia㩴1!ǨԷ. "nIg&Ra ?plw{98]`7s24~JhE6#`ۯގŸ:ZO$65c<Z {3NɈ6?EGXS1,\$eoCiKkU%J] m `zfeaΰlZa/1FI##jVm'YsA钐̍DvhN͝?^־B,,ύ@ ػB;avþUeF_"""={b0lJ$J411/+;@b3IWe%u=@aV;[@v@\f2>T'ҿM" m{o։]ˮ Z.6f-һ ee$nCgGjЪt,[&99JĞĞ7>Wܴ_d3%55;?1T$ diy'vۚ=]YZMH@(]G;-ќJML`l.9VwN)O$JCe dd6!l`QD!|,i!ÐM1qϽ'[#{6e: ljg+#\$h04ߋҬ;ZIi*)RO": &(W%1ֹ]6s{vɊ@J?t:.,ciCNiTɿJ\}N|nw%lݭn,6t*Jsԡw\_;JA쫜(``ccMvcrZTA+eguOZ,v|X7C,cPed*SXQXQh (),R4h,-܇ f6jjR;푭 ce/\!H6eԌȡni;"XXh˵w鳢ԭ-9[la6oMt"Uq=/I)I>O/?Ѿ6j r{}_JEvIrMP,\xr꫸Cp`ymA+Vu^#`Z!ޫN8HǴNxS4I2.{ 2mYO_c.l`;lmREaYP8GdȎ&l#?1Ev29lOîa|wlH2%ᷢbNPTiR "7…f-za l?MDnzT a/4{`ItZ[^O]Wh3ҤupȺf.u~eym` GTAiSgE\,x aɕlRx >l`f|&U q^$]2&cllZsX_iVibqT^a+wI6,+v1X`ya@;mC[WW!5cF^%,(Ft1|y6<]6:L{I#W0 d|lBֲ6%%f =ia7/v}5qZ*Osjr\c{d=% \07$w3zpW f-D7݌kо4 2JtBk{izzTA/ )& +0s`311#.ȷ8";OCthbcʔ  SPQo#z BRhh[p!ꢬ&pk~0|zgnֆZ[_P: &lcr,X Yhx 5Wvbʯa}{TgLɪO3u* 95(-3le<> /Va YOߖK˴jesB9^~֮ns' HQ)2ŻT)[bsQU^GMl` ECV㓌yzw7-3dB#hTs/$667RWx'D>x^W%`;\m21$ls5ZTTi3G?[w8;|n*WlsP^SwѶuZ8bAcSkT]A[:l`-dMCE.B#c&A>XDHEzR sݣ?J锂 l=s3a)~C"N"SDžKz.;&_1_qٗ|vs!$] ? do\C?h.]Vaw~.a##T"Y/qRrRMU|Z,j<[(_(uaX`g73ϫ= 4&2&)'ssE7?gg0.`sxv.D+x4E4Y!9As< ~VKѯ//>mLo{rLɴ>dJa0Ÿ],2+s!s!O;9/6e  ⊞9h.{/,Bٗ}Ĺ1җ#1!1s l@{-8xǨ<53d #K} MBDȾ/ٲE$[,I,%!*K%Dd_w's秚cy60_wN)a8QviF*H#8Ӄm[}jH[3-1P(g}'*T !+z12{$¶߈8m^+(SPh퇉/ΡRf5(;)>3gsO5/1"?Uze=9(ty-ӠPs+t:yp93o [[ϽL(rN񾸀e>xBqPȋT;I~rie,B_YXvXv@9$p[|ZxYƨKXY {ͱ~fs7{Ғ0z\lAgM"N"Y~L.}-VՃkD10xbbsOU.4]}Х]e=UF.)kԒ1ݍNuw%B_C{u')?+#==Uk9) Oɻ̑0㞶؆m@kՀkǧF׫O[6>#N[0ɩ`踿m6B9NV|,As[9Yؤn]g_ ˸؆mkOȍ8ȖFVC퓟zp.vgZRSc MKҦk@ûs!ƒDdܑC+4GRRܙzk@~۰m5S!cZ0\c !r'+i{@۰Whun"*.{֏S=5CIf3\T]8!nR>6W`;rqL=9CbXzdAf%2._z-{e\Ý(`>'C՜Lm4V by;œ#؎?K#Z(,n>N !-W?]C8%c^|>+ jn39 s4vڙZ̝ۛa|\ӄz <9< L컹}ɼ--2^o#1!!?o裰٧sQʛ1/&-9:+=?(&8Ay &yn6-J-'Hq w}a1f1u7(xYTyT-ZZVVzvИ^"t t]cSAEPRy&gm(@hUh@tZtoD٣gg f Tu 6``JcD۶OJ|BI t(E,FNىrK~uԭ=E@!--;&+Gqx:NM-}TTiNG'żVz+qqzA$#wiz]ƒ[3d۰WhK#±c'߆(R0_?b0A[A&*aJ-z3'۶~ONXv€ %2U@L'rՖk6qx3|ګq$MXstA ĝM 6j$gߛ=W#>븕49]ftIq\ o?iR4u#tޭǚپg& 0eG%ڞ=4V9rZ?M>l[blblʋ^Җf Y"K]h3eAwց|{y/kȺnbO[xɜ&TשLMMƴ$ īmds)cDvƒIIZ~LrV@ 4unvI bC m!A IwIwb b bj?L64::^IŮz=f@ ? ? :aL܋Jg lƜ/p9]XOYgsUΒoLy/$ná1Ξ=ꜗgں*}N\lmԮ7y3SȺ#dP j%#;AZ%qk~[zfzfP 8@enR2"J&1+Z(a'( Sg0Z32NDwD PPOQOfrrTù@KR:N-,.؜}W~L{a F/J-/I~q'c_>\~o/`Pee^ۼ)1<ǜ@aoco㥃OOT Xo\ttz*S %g輇l\[sSx4j%H۶C}bx* }Ob۟,ի,T.no4}ifƻWlgj6lZÐeH޳d oynN;ٗ=Pct([J#}zZ b{Ǥ &Ë7<rѰlVh̰kZ297pKjB*z<_swѷ8XR ۰mQ3/;՝1ϴ/sB<b-Kf2s6wS~X*`b_of 1kpnGp;RT鱗Z*caߙM~6{Z{uSj^7<2L6Mæk^Lw*!@|5`Ssz1xa;䭤Уc hkSnZ; 3qs!6 lШt K?<<

y@rg_fwO9$52I=M )lw۶VsZAn/ySh[( 3(ןg?@(D/MF3k8۶V]q 궢Yh:W| B9K{>2Yl}qi=sS K!h!hs< o{J)ggl G GRћCjz\㢕̽#P6.XwLWٯ&e=Ï}3FލQ{s L$\J0kTJ\kQC`Ɨ..9tsFj,"򍃷l6F2 PTRNℱ7UYşRsKcʥO|2C^x3 \W!*YM,ɵ Ad%;@(Uc>:K{IT|;;EE϶Hv˗楌Khy@ޥĥė)twXr?:;Bt$EttpF F oSA(B R[NpczzDU3;d&u ֆ&uuWtt䛔ZvA~xDujdj$}(&Ҹdd}kZ78Q=: kTtL(86O斘k{+=hL Yd?RouzD-Mqg9k`= /3whҧ.鬍|`pz%Hgr^lځlC& #ke^ZB1ӹma3jˠ31^@_=!fMnk]Lm,jI5O`~': esO1ǩ~@(n=1FnKZ*]B,^TŁ<;-3*HrT(U(7콲G>J%#oso W=ϹpuO:6DŔ,iAeũZ>츐s]kC2F{&+R6u#it5{kuBEl]fZ{'ysgz%"cń;# kZg"Kn\bfa%٩z-̝>w~hV%g;WL:h5 S?e_{ PPJF9RN_"e'enia ȅ+l=K!n;E pf[llJ#K1pHpԔ`;v b"bbx}q .G?m3m3#ϣA@蠭.}:߄i0 @]zTttj\ҘϔR Qf两g~4~t>>+Qܩ 99or>i'gENf熚?cᨤ{ vsCo5wF?[W>=hE ^lhwxi:c,#`l.i-vO!)LI"! GfHc6e_~ULWx[C@X4B)|ڋ[b-no,P0<{]ك lj#dP"xC1ܹBN\ͯg+^Z=^Fi$}>@̝(4TډB-/zVo#^J솱t "5GЪ wHيs7#eHlH d^Џ~x@`p-aEaE N.> mpAp\=0=Z^6옮]鋖Í&)%)|DDwG!qbaT,;`>4=Q~-U>;7a19~8j|w݋WfTbqcq.KRCŠbovۭP88%S."Ksýxv9T^&)ai9^pyì>3wNB Pvy's?#F$"W۞֐(I݅Ht'd8Mx8;cc5ݨ)zoޣ 1pz:*.0jtpB1)% pwsU튀{e ٓɂ>SBǝHn.hLzkbiI 慑5UA̪Hr+o[F[ۼ\|=amL(7,ۘ-v854}ege`4Y7 aԂzحE]` )*}ɽl`m2 ^y FNmkAx$^Zks1F6+IVyn˗yjӻlQ;e+~Uͣ- D'x4cF'jƝ`Zc9l`7΃Э!uc bjϻU=|\3ㄧ]\y:`ۯjFEv%uxRd'_Nkı܈2knw'UR305?jRT |.,/Ql64a}Bq秊Y IFЮ.4RFk!#̍KH7Ze͞!&bpV ߵAV|/ QMēmn>RZ;QڂRÉZ]XrNsXZuNi n6zEv{&~]2TV[ʗhiS P [#eG\fl\d0)2Lxie?8%.0AGayy$ q2pO6lFEvCCU|kS!y.s F_0`gE[qk6x$S4YΔ)ˡt{mn W0?OY&^x{HXhsZĽ;iYFk Pau. ^JHO9<۟/mWXQ80(UE ͕)f$;ď&d9 ljDAc-(Y2 (j\QYzƗւKfn-±,vr%ꛁSddԂn11 W WyIL@F/Ve4<8gBhFN ͩƘT8cWp$aiI+P$Q$󞟼/RJH%$2 2#jj9O~" Fe!I\ yϊy*A)M뭕› x1TqQ'F,R" ^`! Wl#yv#:'9ڄz6ޟָ`& ̬lIVp̊Bׄ*G[ڥLɝ̝,33[= ;]) 0| mAN9!-!M{SXhC!8M0}KSld{iWG66kkV&S&#ʗ}s\2[SU3S0o]$de}vzCkkQ~Iޭq}ʑXDu 8R9Rm¾L$⠛QFZ0GVBqZaxԯ2c{aR]dJ.k?#O'O&ˋzƻ= ~TTφ~W '0ǻ[՟JмSz=%kߑL zFdt۩Cv-Z ;DbߟGafn+K娗\Tw;۾{I nj#ᲕtlG!51o(* xTT}ХKC, |)25@d8Bqhݰ7tlHxW4P:V:6гճ]̱́>v?Vd*Ugs[K?n^;Wv3P@5Vg`&c&eeU=B CZHZE6',#,ױ=* + ^9ax=h:ag߸ِ hvMF,d}NC~Is;SQ43iuҰoKbtG`ua<$i=!0"jc#! I/x4(5h I (v"w뜺KR ~Oh`˾rI؜\|ܞC$E?ܶF6dn5(0%]j`/=KC}2[3H ~vHP93w#ŵael)4|SĎblO7/^*MLEZTW6Rl?eoP5Xq225PE=o<WG0jKI6^SFnbmS#h*X kth om/v3u۵FDU?l9p4^ȷt+t+? Clk>Vp槤M@q  ("3A{;sI[)/$$;d_$$nYNsr5ȟ-D@4qnSc6zSQ+^>,@\yy{ZlDz%Y'YO&gJ GGg0NPF@=;-@}^yEiKjC2]2ېo tQ1%ji\E9?d~Mu G鐶,\dwڬl+P]*2INu~(Cb%!Mjv Ȇl;uv=ImZ=r#Ơ}JZJMS)-<qHЍ](7̡"`VΎ@RÇ{@W(@9FFKصrfårߒ'O.)ѱ@4F G(Kfz֚"@T&T&OĞ2} y6 hwN [e ,),9b`a!V2Y5YUS)bݙhh*̶W8'Ei'gBk'-:V5K@EnUuU~P!S YKC!I{py#4t V ].x38SGNj(ovyq{1a!Ő/^)jٯEa}gfLHSMvN0}+lgAHoAJ}@(([.=J y򭍱1Ǖ66=Ƞ3KvtpSh Ϧj013^ W#J]-v v ^''{gަF|rs櫯N}Cg P4\8g؝Vg:t  Fa~???fͪ˪˶2CC*[I6mhqsS .F|KiY}\e1fmvگ h”/unE6dZJq9M3,|5U6lpnw'MZJ%<.}.qzK Bi#3\Q *=XU RNU jm$ުVY T@ Nf7>'Zw>샃NzA ).Ӯ&WCׯ~e?SL?}* ӾM}72RcVw= nijnL Q V1 JGA+Օ+p|hV``}'I I)1YOKi+@FU:A!'t5c|G@t3@jp{Qܖ}퀚 ?T?Kdd+u/mb .D1csf _+s f@*ĖnRM g@ʍϰa EgWWI 02bj3u;yejjxՌҜLIy@Ġ O=[qa+C`X:\778$"J6Ac3PAG](7H2\tvmn1?7Q}RT3{-)N0r0F_ + ;V .ڌ@,vx^2\"oHZe(RՖk,Yo;y7bdIyk݅lN{>himˬ>-pQ[Ϛ$T|SUdZNߚ,UdѐjUN,ԅ=ѐ ~ϓ{RQ! N_4 =Uj- a 눻%!Vг%v>?T, p̭рSA(0MMI&&uiɷP:P:|)p 흴ur@~Y+\uu뼾ز)qL"\>~C!h$GGVYqY ENP3ǜggxp|vp>\Ǵ_BOrD_E ks~%7ujZW73Shvϸvm)N/]/}#]۩ӱwtt·? ""Ҝ8lQ^-\P]LnMN­Æ٤P`+KWsRO$j;{K-r:gk!DBC=MQVeIԠeeZ9>[Jytu`#ÀtgQpa|*(ÂmLmLJQZh*,PnyZi+ +;01K^$$ϱͱ9%L@K@R%wV08Zp0:\Zi%¹Υ_<#fH29bqB.cϐmmz]4.FC/nmB] y - - JC*/eGQ^Ԋ77Tΐe.yöJK%%ŜU+>\)ny|LeuBݝlQqϐuջ*X`C]iۈ|_dҪIpCiv{;[=𽉩sX_Pm|.U@IAs88 G**F節rxDi33 m6 =u+PDC|ȋPiD"ߍ6pYkCr?AAJddGTtA55́ rrXOB|ٲd? xhCnh׭?]w!0bJC-?8r8r .PZ Z R^bBCC~jASznΙ'U,jHȷ`t?U7 ۬Nj!\,;̒oaT*80ِjD`eU@׵: dGYTNqœ6R%X~^XDrC nd.&̉A))XY׷%mk) |+ޏjPimM7hh$eBM'wTAӈ&9^D$%vŪmӣ#m5SL 565H(`|DSc`Oegq4/E} zYB jF3)Gs_ɛDvQ?CLIB窒.Qڿ.F\ 5r1"w}2޵G<1?DS q%FPj 'ݖYek.8%7dZi^<3j3)֒jV9ܟ;}Ǝͺ ED"\B΋P~_gAo&y#vһ$?*&C!MX-[[l;0iS̐Lxj:0[2iWь]|e }zQ2sZ/ `z}#i?~U]qL9QTAB¢ؼ ̺7>A"L"LcHBBAhb 2̕ңA㢷j: xVxV0,,%d,&#="=kkpo~bbIo:H/l kUxGC4Ux)~R:SIafrE=+p 0uбwtUiznI)JS:iOZ /!bxEy|Nק^n.*5vZ RV h|Jn! Fqv5^ K8/@ j_k]8:UF{q돵&5Hҩ{jƀt}Z(ZjF_f>q"?~\ 5h#aAMȸ_xy.b!焚w"l'3Jxu7vV5%Ծe㡒=?%Y5E:m,OlezsfaFW!+o`YVn/ ?`ũ!0y&wEILo~Nzl'[,rGȘ-ǜX4fNyO|? V_4SY,辟 hBٽ[_ϞJ5e0g0 u u=.pMWXdbL]6hcfr?r+c9 ~ 34͈/KL ^/^*??|atnlFFKS^P^5f685m>E46q*6Oӯ=~K! eL#<%^Tͪ+ϮX H,4qV昮lvg=S{F{zJZ#+!eKMEpuuo꽟-ei)'4* nD fkC8m>:U G&ۗ9_[((9v'$RR:7 7 o O4'1r DZӟ0ٽ۰۞rꊬtRhy`͊2S*((/gN(K({>33նYpV塀P԰0p?6gSZ]+oa&;ۿ= h霴2\֏t1VWqNJ9!=5hS8gmє1KTypq/XtmqtEzK Ԡ+f[9F.mO jɊz:Ϛ,zJBmg-tK5` Sz$l vVhsQ_9XN7ЈEM{-҅LڕǑ+'`Pڏu:)f%DTH0Z{,A j;=$P3?(_s ?)NzKMKEveܿMa8^H s.Ģ:/O2ªA\h-7ri[qÆCy}bcj% No5NH/̅y.$LRfǛW3qP^V 9D6=MGphr D Mt>SSxŦ]]E1:ox B^J IW8ǖ@n%*dbIڴȣƀ\\g9*@“ Ҡ  \\药ŬX*2hNzn1&LDWp~/f)yF^MQMQ.HM{VV <L6Zž=WJF (nvV+Ժ$um"ԞǷ a^ܽg&1{piW`{`dFqh>Ԁ6-L-'w\9:e2n:Y/p/GA_K:SkBeJ1 [!i<ܬ汞TWw>~]so'CmZ! gyWT\jhi9u{\ɶ -xhu5oe;? j;sYQ+|ZQ&yB9IfѼ sq[ Z6$OKrZoqMV9o1ݻٵSy@Bؽ5?705} aZh0^}I pڟT4 K""f-u_ &WT:c?ھ𹤃8A)8˰kj%(0404PUuoQi<|?y8Toq""cle#DJ҈P}Ok-_ۤ-KYJv(-,cR)I_7'&=ku}9?ιyg567XB}z\EZ_y9pA-z:ًgUU:I;I[/͞Ş2j7ovIt-T"%qqw^|%X$NΩF{B %;U}\mw)5eZvVf;TjD]]g.ۂ>Zo[1Ȳ$y[k5mkq]Ȓ"E>[D}Wnwg~΀]]`@(&EzTՠLL+qvau-䆀dXq ZЪ$NZ\vԒa}&Y?Vscf ˎTsV[ %7j|Uh@bJcptl{9 : J#gg3¸ 5jka6=Q_M; )o2hw3ػV>|1(':@Qb7C@nۯq@&iv}m~{T܃ [Z lx|rMaܮL\Įzح33 s!P|÷ߡi@G}=$h(U=? rjq=:7|÷_mgx9Ln!f;8[ StӱжMB%x)t,{>婬{/dfoͱV29 LJNo4vZ+Sx"օ{?5\LK!0>ᦫW6%}~f:1\noVD6]@4̷K#QoFD5Zjđr@>YckލԸ3A]# BVGU_ֺ~S" :{jŷ=F::N9$/\@<)WEA=nVv,+RS#2_]-7W X#X 薲-ePAaAB'{(2vP Fwghee剚G}B><7Xj3 \JJ~f>dMY#/VnԸkWOnJ&XzțM2,tao6AO03 pT׊jDTu|%L4&i`b.>ډo;g[QnT{ӧ  6*r\-;R$4m%5LE\Gb9$6e42遄IUjIBŠw <_s9Zȕ v=z؁o 4צ`ǡ OVKwwZ;1f\!aTuzDNt˘E4 o`F U 럢 paKS/yo2|ل |-ײx|Y6MI^딝W'j àۃ "??(r?Sn׌o!.]ޒt\61m_zj*75vQAxmw HяKn^EpFQz=Ię+'))[}/_< @s/` Tp;8\0aWLŀdYwY$?,'g\ma]".[P!8tf̿o^`I?O`E_lwmJgm;=^ 4V,%*@zm0"PKmCo_Se$%iz>z.w.H2ۯ՜đ+V4H&ضSx$Q XY6pFxX!Z uvB_Ȁ\Òzi."\|9BFuET8G$紬)9?;7ϽjbQ;[t폔똁\#Yĝ0Q=~Ucf;iy{iBR-~\lWBll0HKL TTLYMo{R44s N8%cybPr?斸ݯG21tHmnռy" A8Gp\!:sKo槴`Q=:6Z韟Q۵),י˫B:%KV/|”{fB Ƣ#'DݴFDr@ǻ554;|Z@wGlxkw9#Hy:mcٳ!$$yBy{dcz\ Ůc/'/R3+~=L? dOipϰA٠:+zTLT DAd ڰZy. hMndjhn Q)e؝ťcX9@Vy҇B MGHx- ߀؛ӠhdrE0 @c_,J xFa3s)'-4(Cla8u&BrQV껭[!gA4m{c4Үd=O{b)-^b}-fbt ~v$㠫`2 i۪$Q2E~b|÷DcN-`y="+ iSHE1HPX,֧ VRmR꘍θq;G5e8rGys.%k5ebZiי;K?w;8V=LqEV#T72l^ɞBv&;AD߮~o G<_:OaIфbIH:nItF囵h+x;vɓ$7% +J545_9h`XJZkET%^;SSPޏK~L5ٹhKGn!Z6޾&DFF:ۺ-Ϊo+k+kT }hO Ȼ"tDjg2gCp,VW/yL@c:.#2j3j + +>@A"Iss1iAtR2ގSl-Bvf ~[9k,9'L~Uk-LKOy@ j)jε珣) I;D?}H%0,=d}%jPU#Wr`Z WGϽp{`bBC j6DĞDD&I o$鯘Jp))cqDIoybZwB..][)Ӛ|ƏJw;oPhSKKNw6%+f)R>k^| IصhLG*?k*rfN /Z:"fz.f?Pe;ʖJ2 HChh8ܾo=b}2o$q<t>c!o2d+{^\|)υ҄GMMwQhtp`K]]VNwUn!n!gdGsmOv;@}Gxi:n7#j(p皑=5nyңo1%BNq#[T۝c.l{ Xss|.e,11&lX$}T9 $JPֽ;wLWխEA5"X-nx&ppF3Ř1v[Ё<̧NhVxPlc tx \7^ i e#;q\|ނ]]7HjP'4)I n834y2"xKE/0o@ jS]dL<1+N#fΙpTWP8> Ӽ?tURŨ:9b\84k5-Y5Oya^rD^~3Wٸ^W _ƸP}M%c]Ղr5L`h_k\"7s9@z=l/m6?=ea2t2}/%F$,h"M&V{hCz,S7\,vO:xjz]< ;cLht^xX6ޟD:ԣr~ &vd``pڏ44))?Gm&&}_.B*Fp!\;gaDmYP~d#KK;”A#kk89T"p>T hIc!)%)'հo̎@ˢ簒n{koCӲWgxGx{;`h j"$ ʡlj[VTAJUna=7Ͻ.U HtL}r,jme8$ dص)=wIE GXTB j{=/3(n0b_"]3{٭ȷ1OsjPVSkPߺcGXw' 5I@G̷ޕWk~˾ jF uPjRaýR eP6F VO/GOpOq"V<a_Q0a|!Ӈg}*搴J} j{R-cJt[/>0%͑%@״rbRopCp|9Yl`{=z}2oAV Q83>S_EX }UآniָtHUv@F~Z3:i/_eeazT2E $ :2HA"Rr2saA:̇HA]VAA_"uU/埔1o=h~VLx( I4d/|H jn{^0x7[o(}|P=8~=o;非5hhevm(}Sһ]5]vrl*kՑTMvx2% \:09ElסmX ,ŭW2YVcaӗcnڠIl!sjP \&i]jT])<όBqRsy >&Rn\8eTj /x۝K 2j}-vJkVOf3H)O^=^Dg-vq9:Ԡ-eM:!DC#FmC.D^Eez;|eXBjPkوٲ8|LQ'^[*ӻ&W=S8qLf0\Q~6K8y>%nB9.{>.ppyXKKƅrmnv|\υ>t.va0qssOkVUw FE߷]P:+={ˏJOLn`u˓˓Y]5 @wĚ?-Nz )=^W 8q$mD/A.hJ"hm1^ EE f7HJ66TS|[A7_l$!=19[# sђ_!~T ={| `et2ڼQ^Y^clh(ZjJ/h XPUo7Sb`@^OAOPOk9Cz?@Xjs!Խ&<=8(gV\33/F_!V3Q@SA h3g^>'ꑼ_/ά]"5 ܛOx屠xda^=l@⁾<ߠ\)M[EH.-jYUYgs\btxu@/h+[IyB* V[&☜YsLJu8Jd:#v u:x(1)ljtL}.RNY?g~fnE>$E2EcY !PcX5%èܚDvf%p'M^TԶ%,d++GE ۵ǴK}Ɍnj'UJ*X뼢i[ԆTBiЃD`9Vz^r%PEQFfLz 2r@011 򺼂8qYYY hH9'N@rR召sHJw(jpǂ[eeezчی?"Aaa~1T8766NazNeKyofwTǭ=AFBH"lYENl!B!ddf=E"d]uljx뿏?>orVKZwnsFV/SutЭ Iϰ` YHAԕd|H򦏞R }Mvg$6 xǃ]z@2J0QHH"B[;Dt,X>, Y\fp Cpt}Hax B{=AƻA&S²$qEE-ҩҩP7psd]v._ۚUUDhx™^CP!rSC e9T)&EI9*O5`īū}_z#Q9h4D}{wý;~>E,kq'ڼ0xi5i]K揧tcSb<v~,J>Fƶ_Q~C$G9L9W^nf8$Лi$[2PZ)> `8Iݮw.{lDFn;4UqrK=νOZsAtDfpQŕ=}Eewm5: dS[꣆gyl0oOaNP6ExKԥOT*|3^Z78HrBelAH"~4'}S*1ȯ1\d{xCSO~[E㎖,Bdy똘Om,ѳiF*Kf >:;g5Kt4 oک %8-sJNxq [%mՖwt]O[gQ0o 1gS2>VL'7Tt_;_n1j`nߑwaZ:7B"(}`^Tcc͘ý½<#i͑36/Zk}cH"ZFKF>0W`.(ý+7Xt@wP 9#>) 'xpzsm1 D}|`ȋTQ<HwQA S)tS//54HHJAzF6SܨP[ƫ( nO:8fu'pŜT;?9/EbPim|O!1iI˃[_2ި#!Tcxu"@Jq/deTT=O`K1|5Dj]_ѧѧ|#|ʐt?/ssG}}'"/_FFlMMLM9Gp01˧43}-ITB`rİX"P/55ra9VwZzǔ7`5јр멌j&inF];.?<a"x-Jc2SIxEy,='?C%\#K .B*ھ*N-0ALmnCk3"举+bO fi^koF?~o%>CѪ}[>Qtڥ ]Cݐn_SI|S/7og\לO5a钅Un;MR%7x]}iapfw 06. I?r(u6"(8.d1Cvc*x,C牸)-vj%;Hd6lb(OZG_&dkn8LwFo&QKms!b} GZ6򛪈taT.00F; ]Ui *I!7@B=y$nbW"t1 "Z{?202yokY\Y\9kLVFN Mô 8dy˭BPlͯk<$$;! lJ)yS/ q{mƫvb`z)Xvޖ Եn+];Z9qWY3Y9- dXJI`5-ebJXʕ9W}Ƙj*(?=u Jw𠠙9f}h-@Z7fYkZj8K?C0mPStYR}Ͷ׻ߘsrv9>R;2VoQMl>FC{i|1׮;֞Rب+K9QzaT ͫ%E3n{^L|vq} l_-,tC<q~l\>qBz{Xf6,z/PZ[62lH!ԤX=?γgS]eb &\Z߶t2ݪsrV? 29I HȠht:iVx%#9l9\̴FwqPA׫P)|m󇿂r09GDDi.E.E+S'W)ƒr&'HAin €fw6HZATc: IŇ8nfl%Bdf!E^ID"+#*ٲe2O*ɝ}|4x{}<{=}y\ןV =Ghjx̊LZZ%*_' ӞӞo`/ճWqΌ.~n%4\2 CqQꦫ-W?`z3/;ޓ #n;&$$BQX%XDa#)d4 {I4Ӈtwmyl5w\? Զ[֗tٛ~&{,$nZNjx!f4CN?5vJq|Yk,ྑd+=;bpGܺǟc3Jʿ5&X &o'}_]G*Uqժ#>Rpv]Kx"ԠOmTYLl4bq涿}nӆ3C`bP4Bi2c{%9jGQG%Wηx/+feb3jN?L.pe5e=p+3Jp~998;w^ .:@ra.+<8!;(*bzp:aеY8=!M=))PLvrrdGdJo EpFZ$24畇wι0|v6 hI##.ٽÏx;vY__;LIѮѮeNofL z/b/s !mRBa87P d|9= e,f)D|.97R?;U]$ώg>N;Noqvɧo*7["=@! ahX^[_3ȗQDLL-ϋQ@dHeXEаRAK66m©?{ J}$eAZHSQQNTT(k7u;;Cto͕ߕ4ML&#kEB,Ql"ˮ--=]Oy#tktk9"1=~04?%(F7? ٥K \O-L¸ANM9Vs̵{' }dvkRLԛ8jjc춣Ռº[\ צka>N: 4-J _Q!|8$mC4h7J>Q 'e+zkvUxbԶ[[ի_8Wи4l}+8<dž/qvIpM%5jFV>=W!UzV7IA6}fXQN ɠFbf7S  5{H"<]#$uWdq3AƏ2TP2v)gp{}wdw9U5p2AWP|Ұ#siYBC`X1i,wRϷXԀ&D֣}p%S'|j:`qUUҋٓ*qE&PگjZ we:"pn*V g0Xؽ6i35)~_2/w&o@TϞciwӶn"vBx6}~j"rr͓6#mwI~S3 n:{ɺEž6-BtU:} ץ)ZDʚ+tիx#sZᯟ"oIf /muN[d͐bHLȍaɜqsȅ~f 3G D> 2J|Z4 A"%RGG/r>29тf ` #Xw=Mޯ-jvdۀבmh/VD<`wE T(\4=5Ӥ'퀄 +;ƋNj]]o~T a?gIУO%R\7vYF e5xǠh":<#8D''L(|ڋ/'--lE`yp}r}ʶi^U,VgWӃ( :&Usߓ{#IqqVD"nYĖ`ٕϮwrKWq.(Rl?}t$9A|ZuuA՗,;HSd*i*IPvBJ= 0g'R\oorq}jiuKFC,^FXYyBz΀%uuvR26aa ti,O tZ0>E".e??*vۏϪ*o##S.Ǵ_fB|I|3zv[Vj{ q|+^xqVgWkwubK+Y]Ěܳc ?]=-*bH%yjPٶC㬭Ӥ5^43HMV46~g s+abPd#jk̎ʂ/2;D_7Yx؅-s!ԶWX8qQXiMd5GUViZOBctS TFVP#oؘ+qn2%8JaC j?"z<k O0(c '`׾L/ȶ]R4.Զ[{Byي˱4 ;ʣKa;[ҽ|fc$S?ELs.qܮ26O ( ..=,t"2-Z{`[^ʑBI"H4K$lL4WB>:|`tVsO^l1(uS(F@Y5P~TgYgYoot 0Tr>8DsWId={`n4Aq;A'W]*T-sAl19CԜ[? y㩐"vͅkNsDݦT f!\8]~mK=A-h0PF m ?.V>W8Զ#;7WiK?nEqvz9w#=⭾m]0I#>lַĨ\Lwj؄WkT,ztco:]vGts}Y r]Oj ~&\l$}l?Gwhwg1`Уz%q8y5\,Y&Di'k_քXIRkr$EyЃJ>k-wn̆ʆqTLY<bbL˅b}N \64yG(T[  /:emm\ "Jw i b bcScS^QjO<1~ֆ66gnMۃը*hR Pdb* q::gIjHj pDנk{6-ᖻH+XI IA;,8.#yֳub.Fs/ \Fԡq 4:isA,.xؾCO. cgB|ߌ\mQHӃ_`,V"~+jSSk\:*n==1dW@&U9@1,ge5x-Wi[7SHuyOf`1黌11#vz)"r13%3Bm**VQM‚<(uʏ}.T"7N;;kw$9A;O.:.:k7\-.- X%dN@'z,ߨ y/:}-45 - ~(6 <*m8^-hw/WݤOu ZB.8bDjVnm8>&4*EO= f" t+.7m\a|Aܧ!cUEA ϝ3YpKMH|/JM17|[-P܊ar$qSfNrIB'̫p|om皹Ŏ/^tJqV/\$+R r@z6Iw8~O-@"hiXX@3,A KED`LfLlfF s/4"kWni[m#۬ndȬ;yl[?f)({U4-lX7P5qy `èf}^?s?}<}6ayg>q-O(E➩o62D;S΀:^s X ZW@€袮Artrr+4JeGfG AOa*0 n[}ּ/ *.jsΞBdCw@tg.2M?D8%D~`h/aQQJ%}MlVj aQBf9˹ۦ9^4O\J_j Ø*Ĺsk=Unjo]$=%e0\:} &(DT~ t&p? i<7}siK)rFVM[}6NzSRN.tyz/kV*8\l=hx %xxZ/bޙǾ00c#6и 5@@ )W'mM/+hbH(2D )Hm ۤ Vrk=1O1O!&ڮF_u&濾:v`]n\_z$;}rݛLr)S'I'I hF+Nw mn{z};9Uӽ\2bDHUv?{wP3JvM4&ptwޚ#5$ [:(~/5ÛcNpT1}D*iw᣿e`uBԻ >mi c,||b~j.( Ld dL;2~OZC]iza->k5ђkۥ7tӨEcBGDhk{IjrxH۟ppthH̾k۫O\V*?rP@i|ڳym`emB<^/D՛1ID[=ۯ֮GP%&aS9nrIq Sޅ1fo7A~Ysm%؛>\ˑ6n89Hm?g+|::LLDWQ X)-A)c)cn/^6W1AAV^BXTlf(RH#ۢgA䌒vbЏ^a[^SRU, У2Fx@jzzߐm?òвP+EN]N=G==wf̶.Rs&esvCY(斸۫}^g`fGZdC0p-F*sKwQus\>-i9~/kVԈP\-[]Ur9 F. riJ{7o2MN޼Vʨ4HxIӪKH$@JғޓGdt\@/XL'K[8:Jݟ..VUFbNmRm6/8nFb+ '?5FUHEޅovhtv&Ba۔F4M qvx&ҖN d7H>`~ W '`9 > ݧ4ɨޤzJ鳀AU!   Hcqz>1` }{X%}XWZ[3VjC{e7p^MF-JedBE !#"&QFV$٥޸:ޣ<{=_?<sHE f Jgt6R+R+5pU#͇n0RaRX4}i{;xۤ܈;_yj;"rwEHH36}v `Ky>sdhh(AOSg g: G1FJVvDarEg3#޿T5&X(vX@2[}-^w,%iC ]0 ?s{>, Y@xPZtSpU* /.vt|-|-| wx7wJ{bl:9~H|U닙.ŢDĵSX8=y3ppnKΠhhԫҫBݬx1.@aa@B\q75kޥo#M Χ7GW.bD>ib%@Q Zŀ>>:mTl,ހUBP!,$K; sR) shY oGN g*ɞ?[/9򤠵(gd9f9VVWkH?yS-qʹmF0??zѻFMl^M)vs{7 ]FqO/cbD%jI֯YR?fKyhh5PNb[޼)Fbx%r-C4HGr{ clwh&e0+5Ij W7avZ[?SpqyHb8F`}y9=_'>a& 1xXJp{9; <{AL&Yi&'+tp]0"l缋H?FfYH/?:# s*tw+o/ C{#GWIOx_Ck|y!%O$Bݒ՚cLsUHFT~a+-L?bFvC j@"Og2ͩ:Ϛ#9U vȝ<W|jP]M!Sbvɳu{c [3Й=f Ȼh{j4.%O=hjyyEF1D\:P﴾^"'ק'\1IJT@{{ y-ǭiSLDYwa9-{w-]K֌]yA;]VyшZ 7i}N/S4婂4Yo~yUoj j =楿9Ã1--k22<p=XÈ( <ՆՆʼn >aCyό+7'A4i4iU7}&%?ݏUh N4% @1g6p 癃܀4e"F=ИoϺ""6Rl, ?a?a;͘ b59`r\O!ԨyI߈=+Dͥ;E_ߺ3jKs<r4{PJڬho1^$rJWG {ױ"QJ;"[{ʆ̑z5gt~ىr5{麎IJGRۡi-6. 1,Wޡx=Gqh+Jbe S5&PNkAe'Z 7M#0Ch~b c[&"A{Mc#[g/{yϾjM 3N0*qj"KZL»jBkL}ǬAZp?KcQZ⑅ٓԭSFRasӖaPȲL|Wbɢx}aImvN^^ii(GLw[Ѻ\ IovIGՂƹqK1\}Ѹy 0ˤje taY'HhiLIVw68X"AA[#%OaUgg7 }}<[A ~GTY(Ƴ-Y 6 66lf;HXoPjPJ-^-vg=Y1Sj 5QR&j'G|1plq!3_,_,fbE{LgHJ^{4!L$LD$χ>Bo!h2c2ʢIq/qAUήbl6Ԥhp6eQO">*|kUdJ}2UkVޘ_]~ 10]5:_rӿ1}dt6Q8u`H' ҙ0nYJJTT6li@En@ڼ*ʮ~ !i#k "K(_2eϕXqVH 槚7Y']O^:Ɣlj? E &^IH-/9"Fqnq-}%8?Ô̔,]XtoMͰͰ%~+y@md$$y36=r+78o;B_ ";m^e:l[~G'iVzKPگ]je-p?ɜ!o o0b}eҸ1~AO6[ tG+ş4UC^ؕ!.:ĬA7nX*ˋF%Ug_րg )=[WIz%vVkW_9cS8~/ָ3V+X J1_ma}n֏3u-f7C!~I'ŲqyZac%PNkMf\e1Ӵh\vһejZ{WTjj#7;̀ۢυ*sqS̛ ȵ z\Z~PA`T0T/p ǚ૓1TT2Qߘ \woRsmom)NB-jh;2VA:CO@qqG<~=Mzk & &:F4k{^q\T*ʌm)HbtY?"bT#LJ0oJR`0s0Sk`GTF(OPaRa22<5'=Ow%^;ʐl]Z0==w^_5kk.-F{3ݢ&&X]/rxsp lpj+w+h5L)cͬ6 \2iBkNxw{^9$1Ż,+rbm+$Z*jPڔDCo3MUt"Jf&L*W8#TwA_x=S{ژ.b1saTpz;ds]vCa|fS}%!O[qf.Ba&Jy׬UYj esηQOzW'jf^E!=\xP o G*^Xfw+ )%)h~[l> //i>odhנCedednk ˃˃x]^@]6`s7m)נlh5 TW*?!?QO`YL8rv0lտp%ΏBd RsvbѼG pgJ DrcbCL7zU->>}^g$t >R3enklUu0i_c̰aR<V`»GأK|{S>S9gNSvk 1n8Hv3ZPC"HT++$9h b5kv톓T*jERsǹLՅ# ?v`@_A$* ȇpIe)an;=! MVv*!en )#QF:{$3IfdTƷx|Ou<{=\]ιT$7@D $椘"&J׃/=\nEnͱV;E[YClT+G]dKv WErRsoxU-+Qvڹ^|RR;FWTToؾQ$ ؾĹw|vóWUat5 us[ML44BmEtGXrFyH6|ۂR.;d:%'Ǡ芿hf#,6[8 7ih4LwY;Cd9R39j(=Su^b{»m2aT4iBӖw<ڳDFcwoѪ&>~̀ULôȪ^Sa|%ea==< p_yQ^LZFLô_ <ѕOCeA<Wsb2v"x_'z4a~t t/`Q[;>{皾x2 JY?x4ڪ"^EI̘+Nx_IU ](Iǒ;4FKT,w^/5ێkP4_To#&؎>5\,3wJ.I*Vsk$6"]2]CgG$NI!*#%$w#w~'N WWkD G*Ī{̍LP O@=u )"47造$s(ȃZ/ oμ9set#劗/._垹9t'/m~C;1fj'l,JӇ/˪6_tUY__ {ݔ㪈NN7zrG00z7I7a@Fhh|OKJN 2,UPj+S| {o]זE}N |? dW5sWȆf\Ѹ,ӢǏi0{Dö;]WQe..΅Xu?}}xteȈilɄ&Rwn4Աӱחe ğUֱq/Uݐq0=^N1Ur0Wh+"E"}'%tƂK,) T w#Ee|C҈&cosLq xe)H>|Dmr:z0fffZ4}dסMh28ט|XˤܑW}QX'z`;KsG5<#. naЎ*yRp>"zAFeh1]eg @?0Q?* c"H,60|!1 ]+ ݛ(ץbV<6<6^W:C%%Ok2k2 Qira~ dɾ"<#jkjB&4Bxp+@+?++ǻHHhک̶Y׋J(2e[[~l;C/OUox"hFvS%bڟ״i!kժq v.#)F[dWV`m6A{ϡrC%ֹLe{;-nEqB$X"`>SKl5~G]l*l}>qz:zZ[cv>3لg 4|iЧ[&Z?Hͱ^ 6SN̦ޒNi"vvb@3.` ?^`]VbȡR+e'eGHVqZW-XBV{eFRvcDX> Jc%5%5^o;JG/T Ȱ7;ՏjIQ 5kuP,S8eU螸`ȵ*vvRs;bKZh-42gws/_V-wڊ(Lk P>b"7cd8XXC/L gZQR:1NckW)lꥺوtYtp?P?!],Mܼ}oom`\$GD|`Vjy:S:gS C0l{tcٺ5EStU{jÆ*9%T zf~$Bvqpwɧ79k=eĘǼ!Ki-sc+F<~8S_ݾpg{Y.$Oa7:l SnjNJزllOQc^vEiQf)z[W W 3j!ڣ0 ~65x+ai{;ʞ!z;,. |…ihnObۜKa>m-L}Ui: _s`KϝY,5,!QF5Ih.m5wFmߤ3ƃY60vՄ2cUZaߙ;~,,&g_eo|hs5}.s=AfPg[[:%ܿD̹o RX)])]MQVe^;2 (+ǤAosEE̖n}_FE"(6oʦdN. no^7RdqqKuK_pnyr eX]A)riA1sgy~pm UD~F8<^9=2=b̃NНȗw Ck77s@w*?o"|n{>Ԡ_}\z4~5i 'Os .zuп?jP!r<-Lu‰}^&J^[9q=&Jm<1FyWB.ݎ 1í<@# \ )%dF >fMiM pwGX܋I 0WG ya /7Ukd8L_XN`wH ֦-hL$"$"= /}u [?%t p ebh;9 Y˜G_ ? 'гg d ̐w?4?3:#;# ,, ge dYVVhUAvŊKtts1I*%{W;VN4@G\E8U,>nNM GQ,t}syCt\WUVU*E cϟˤnflth  7P{_*Xr݀̆(*>?Dexc0363|A dbAUɦ9ױvwZw ".8OfP"^}Veƪ[I.3VPnk75B 94 βoHFcJי=>KXo3c/am\ 052iMm<(a,tP^- ;#zKdW)R5H!?1 z&Rs% jP l~IŻ#bL&sJߞ!j<, tmqSt-ʝl1F镱` lEqm>h9EލJmKr>r"x~M{{8?znzl7dO+b, 8^q24G~`~Wg`Jط/(MPlo; nKHY}mefe13K`Bo̢4԰9*j*O=}ʹzKgr9>?"c3 J5YYu:*L]44ؤ܏ic|QzºTQK.&"`|aK~v)zn͎?cos0q&q #ϒ3mnPQVdI_X @?g:L|NgW62Br֦94=u*xKN@sE%#z &]JgW=!\ai፽& cv2 s蜶A]D@GԆt/`:*~MHĈDn ZvJZ= 0Zj@q&0ɨ(3jρ=ϋYr @EE[ `aəq!q{{55I#OWwO\ǒ(ɥ!T.$STË_h_ SCVţ?Y32muIu tdvdva2]tt%[yl&0۵\O͙䖙z/42{Ja+:',% HǺ\UZ.\ :*}=P?mCfd}ÚM2T+\xCq6jvWCyh}<xH?ҌAE{9bRŝn)0UsU$`P?mQ uzdӭ KtDSH1q׽"(*KX>i\5 kyjjjn~RnٷRGڙzֱJY ¢ns!V5Ϲё4ZGHhtui+dqi#=#% @}qߘ |{.Dc}-8-hX55טH3nݚmf;vN; E©»ٷ5. ~\{\OAN;MOةBkNx.<+"#I9X~<0gPa~1Q({A h3bzQ) z/ՙr=N VPO\jZhp~B%M̜Wl0lqhOf3? MA5DKg !gx)Hnnj^u=-<^$Kd PIsocҾ)~#j94XXB0?yBrBtN%cumb촏~XAۼp7H1" tdk~Oc)ٿx& `$"  rɋ)?$?9e!A-_0 //j/aII17|mۛZOW=R@ʫKrOP0 ]؃_9Dg@a^h^xJ۟88I(@$r,E:EѕpJe #4?ۣ=@Ժ z;bU/G"a%݆ǠI̗B*2=&]{`n|5ש)-bwZۃnћڏ-v+%2^c2/_w4;w3[03L+['#. (Txn i iU6dPpd'Ӧs"gzzԧڦVՕU@ă% % RhϠʠ8揕h9eX|B!9tm$(DUIGCu}S*{QdERfQٛ"ز7!١lDt?qz=u.\sjTc̠JRdd&DDb+--+ݯ]|xV]E۝jdee=Q_~?qALAAN{tĵO lN_VT3.! [L(U G{"oK9 "O1Ϗ1$ \ܜ{/jK ! $>ŘJXOt؃A' F7"ˈ8ِwhĦN`}RT:B^n$Ifclȶ^iW'-" \c!Y-/~LFKҊl;4)U{j/oy0HAoI@Q 35E\+Li6 sP5ԗU|`FHޱ•1NitĹ~1_ߟ+N"{-ٱ5MQF^(DRx#x^J3Z\3;nAel6.>.1ɀnb4k榙Chgp/4|fvw H@(AfVdVds4W5gg }~ 5B meRg@y?\R2( 6n]Vy 7{AK Vp&A*9]--\7wQ@ (YZZ,r33ӣ9*yn6lpٱ+:r>*~1ίX]}Gj[rDS jj|m&ݼhh4qY&"2,prc(N3_ hS^O.ۯx_y.֙/7|/]v$i #Ɠ܃tyU;g|l%g+W: ;
v:/ o&&&&p@^6-~ Lc %CF AqAOG\n 6 ܌ءE̠#'.I~5(xz`yRJ?.ŝ4Z 2ex9%כ7_7y"Ra>-'qj+Agf̀|d_'si拸PZ2BR祍zf; ~?3-|mpiK0*|1z)ȶ" Z#>iOE*MNz/c t㓣6sFt"?:y^:B.%Gܽ[~Q\oFÉ]2@ِGm >ey3̖+>?LO頙uQ*͈l ˖V|iv6n:T* 9f|XjvS¹0zV`݀>9,c"p$V@FFۚ,Ճު*HBká|LLMbz{.eeiiWYddj]+0|Hу;(_6QzL^ dU+6QǫERR.,~ g5$q5Otete0Cz99oKO<"zDj&)z0DZӞq4h}{ݜ)vo :fΟi~gO=g3ˑY3 e+#z޳UD (O :""W`<8ݺbb*$  Y.8= 9Qv6^7O]R@V(1pyQ\o(( q"*U*؃{ { ؑPs/.\ЃKdž-.yJXQTUur@ӘkksḲ+<bԋȆlCk}iWlOc#e^Ҽ"OD١mTŊXg7w 5EI:VkJmH٦~z֢VCQ;w=>qsn\cY 6hȆl}<#Y*@9}#{XfE9"rHf"{ lǀ-dIK+fFNWϜ]-D[RƘAUZ[8Į`W]#&0[U[UO>L@ESU@6a/ g8n2/3/*[)0ۅ]M>P_ϺTtllUKˌu1ws,ZSIȋ0Å3+#Bz]0 c]YuTM F6JMK: 99 tr,s,%*9ks3J(N*񽽶r*;*A=%/BPk$~ܒs\qJGimgSvoViR5PII,Djb8۷6L>4xh` ' ([?-a,$eL..Qw2]7éJd\``&EE ǿdd?YbcffcLu?@Wjk|>t.,01{+rRTUG]` IIMYY?//ovXou.u.p=Œ|9??~9?KfNsKĵ΂'c>$mLt~AG2^KRH=c1{\d=?ӡq 0eTk2}ac%Ǝ^ s@nvi/KmO9B!L:gh<iz~F=_J&eܷ1!Z^d֟1)N=t&V{(vE\7E+JCIK{ MZƈzrܥ> c5S/3D+#F? p.Eble)1yC^2Ȇlkpui>Q{eU(5=؉rk}8w֡DljP^H&p{tFeF$>9RRFEK4;sgϝElDNU(QyxwkޤgW^$p Ԥ79Y('b6X)p -yB:\;$ 7e{y8]EY++Q*0QPi^P1b֊F@j*a,zbuUaݳwϲ3SЕZv4*¡}T$ya*6$@BcVzV(fЯ8pe2TA,^affC[]Sn _2Q)uriqimmBP0ة4A7H!#YBB ?%풩W(^&GJ:tc)ِjܹb9 bᆪnٛ%&``zQ5[ّ's=͟)cH]zL]T3Z[T @>lŪŪ再Udici^^_zP V2 "4 I۬ެf/ˡ,v=`DKjnSRpw)xy;#Z <0O5{꘹ d-ڞJ-x]li' J$iVR% 9.& & E7si|: }} +^С`$cÑÑLW~%%1銤B3Ar+k+bS#aYy~|O ;#Y,,[ڈg88u[n)BDS-rRӟ^2$bQl}M=0zqZUG&Ļ=qG(7j͔üg4)&1mEdh^}{A3ΤoޡJCL]ԘY|K5U9oU8 kv-002 ׃u} hG2ҢO j*ϱHF3xaqQWtXU.hpdz>"AO{jtջ N(=$z{HvS=AШOSP"iu}ՠWc@ b l¡DPjW3懜:7t" ڽĭx MÛ;5܇wk:H[y֫LB|aNy:OdC jiT?h5{z3T~V#_;QW|qqB{b)/ޫřXX4/@&:NmFu#wZw-~=6GX-]ҨX {kg.]3kljB=Mz`,p;Ӗ1`N紨uߗLp2A#zV3?Ry(wiNBd+m|TbbN紓_h?nm0*+[IӀn^1$oʄveEBČŌ*j}qn+(Q;쾲16 1~o޹u : ; pҴ o+`Ap@Dz=@::QewL8XޔUEуf2cAm;ʁ$-j%TFiؑ/YF[mW'.jPQSiyy~#iA`Pc|q]˰m85CpSjHWߓ ,^rh[N,i#0cdG,Ei'< [%K! |;OiɣGIYwN8")XXmɋ&P]Ԙ._xKF,59fwBXث7ևi0 \ G;{Z19%ٵA&VT.6 'j]ZΜ K,E\:A6zzIȥȝFToV*PŐ儃);(;b 0I(~+>\;;N tc*XXi@\\Ԋ~=1$A j H4)U%LL K̒̃̃iBk?>>hֶd}WO$5:@T.$UIf3^`-]/]a /t^^x#Fv ϱss\[og@NKC=2/e8AYRnNgWɁ/f*KTgKh4&O8)'k1d]d۳+]WƉl`(f ۲D4_0r8mPinB: m]!}4&CTTOzzCfj $$tیwUr~~k΢"#t&U;{C @Oٿvv zm1%7%g;i;^c'9O;?9Eo&#azYXIm|p4PU++m+ZeMebwT^U=fDgD'9RvјUUY -qfqtAퟶn7Gy'd6"O c6_:6Rzr@gv2܉V*L/9Ϩ.uP-jprY䖞Ui#NTVfF;ݿ8 ^=^sߗ{)2`^:IGp*'QHVyg:.e6;F9B_Q!0kDشnC3{lws'oLjPͼ_nEv{UZ{-.tfz1(\wﴇYPPC jBӖ5\yj{^zc )OrF9KPfk$5 Z)0RL 5r$ޅ9d_.i~Vl& U#Ԡ+4] oJ"r.3Qif;n7%0&U7N8髂gs(z' J;۫[^3&{4odo}Pa{$ [cmH`ݣ?29S&bOG ڍ{-?0Rh=ێ~?5\?rT"T>;{&>#\Kt]kfBcA|]mZ~m 'jjHX a"]WyQgARZd/m3C=8=a.@dÙDD<<]& . .)mc,݀jLG?c^V3=)`y<1/ gee56yEqJZK|( f>/8!)9%Bb0F3_ lGǖ^1杖E"/kP$W򅇙gik7y =lISJ~"Z$k\Bg8(VHꚫl _?Df5}-`?4jJWgAq:n3#cLt\LCO|/$+J}|6z`s_+GWVa[QƠfxQ\6;6 6 8Դȁ'%>H ,,IlQG q6 oYNi@\\_((\F!""  w׾ԏI`y'Q<˄PǨ*dGSisS(R}=iMzɍ/ٵuDC ``Ñ%m|H>ff[XqG̫wDffYKJ{R05Pht*UP̣=͊]*I| &&NpV 1%))[2%tauI\҃҃kɸ'h '8 j??[:\tё⭍{{3;ONvK0{v6&KZM n>îj^˱8VUd~DUTkhXfJ>/[_5"-I*v^"e0̗0e|4fWc7 ]p %[gZ#5o&}n7cQo4z~7WXNPfu nb^l^LLowd;3PZ^e:SJ5Ҫס{{3vvL!VVWjPl+pJN43Ͳ [wa]]݁-3"≫opf⃐X;;Rq6~ZɞvtяZIy^$uua[VN)8|؄aÙ3g%-X#rO VQ9.b ÄÄė `f83%>Yj w(wHW@PBP# kjzM\KhheW>'c8%PG"n|!> P}#u ?C-O-oxxV$ݫƌ5188/Zv[@+/-/ }n`O^/Ok|Mf%s{M5Z4j^Vgl(Pȷ X,Uoh _hoF|{otOZ1~Xص ?>YR58'k\}WkDӋwQv[6Yܑ\rBlZHʦ (3(3h1$U$U<+  MttѴj cλ=.3 AνEṾd>D'y0?=̟e ::n>^j8~~D"]O85{">?T/|n6KFsKo&DĬ|+W[>.>s@=Iݠ۴,&'fBNMQ$#eۃ/߯0:w6f֠>$m;wpN|0Gߏ5@o/7*#}XXs%6?f}c@#;\-fQ's=; B`]:1FH舕sg( ?D3َlOgg-[m=@gR iou FH^!h6PmAWܞ=Ѩ*y+rLvy*=AA»{g;sѨp%QdH.jJ_p~#;|aC|sPڿDd 2$RNSP,I{Lmjt+Ov~OA>YL$R߸T@!TB RJxn;k8nn1deMHd_"QƒA},ed-{R"ٲs1-|^׹s8'(A~ܗ1ku޽ZPOD+Lj8aqR!oտoY&&m d_cSg%DŽńesj-\x J_ Ģk%_y:Fv[JH^`M8e"=dΩ>::RWSd]R 0e:X[;33NQ4̪h]owno؀F f)BjCIue5uV^Jj=M%WBHyaM+߂&xP{څPgaΘ[ԼQb#hnM+"oP6Z[ԯ:[3{_sQifQ;XlEP4XnЗRŴZ 4tU] OʃA j 9WGkآEtQJ-i sO齭 wIAИF3+{R2:Iϭםqᾲ5v/iQee@CAjr80LpJ%lOƻǖ-k#r ?jP0Ž.^]s#+U<+netӵ[JQ|Aj)2ViM(;& {,[41nE ?$͋ĚIqhKsUS2A?dEjO(LAng]ipnS'x.V J6͸ Q[o vݴ9-{.,Tiω.n[N鹎wN;e V3"[d (g{G}rTk.p[~[~Q@k3@r %#&AYDJi]>PY/ڮn76Rwֺ??D<3b̓h#[/MB_2&T|xqEV 궠ǀjݸd̲]\]]  @Z$ ȱ7ۛra @29N9N2({c͏AlNǰBb3yA""oJ,11Oa}?wj{< V~`V^-cq۴rIO#T.Yr~Eg\gGXG7xTϑV/[33T-Ԡ-ݣ=Ͳ ې-S2b&17elqԠS7$~Xz]i#[IcĠwvl{p*N["cN謷/M4{Bf?5J0~VdU>~аJ'jqsen}!۳iKG:s"LvW/eLZC/pd\h$":?aRnRgloED_d7htZc]@L\Ӗv+~٨3rF&88r1 ##SgL~,>ɾDoPp6a8hcu Yy&E=^*[*QڏF; *nO\L'+&+ipQ6V6۔uin5CD0p>9u~"#Fq;wXH7)=ҍ_s g/aWfNF~aq3PފeT8|j^(IA]JR}BQn Ub䁞S L!Ρzjj}h)}n |=`ώlf_nv5p~^k@F.`wZKm6U&㚔Bԏ/^^ C(nf=DxI ?rQ2M6λRqM&d?J Ϸt]ǰth5ϭY2FZ q96v!mֵvK:C:HX3V3Iu~@ zؑHwt_DDb~6us`-HdRf1ѩpIŅu7N) ddNҥ33sU}Xȫl`yK3cзj˞Diis~'K2^2^pWZ~CC%򺲺2Վ묥P]$7v YsбCe5zzŶ\j[kT/Ju ;YVPE&9 (z,;kt%,sИir3Pʗ1YWBlԹZ{ 8cTHTꬴl4Ԇ 3 3t_뾶&(.>z/;z& JJ,?Kl[;B2"|A4@K׫HH[!԰**c`n`.r r ]D%Ս%炥= :5g) sC.ZFF=?<+23G<=pm%L$L䷉ys-jkK8jUUN_+@?V'0GFǝǛPZ/5TWgzuQ60'/lJ;t28Ӣ3.j*GнOZtU~$ɐS3Z*QAC.& oFAwEDxMڼ3y2&O{w e?p|,!k첥d_B (uƾIJdRʾG&dKMR"zbJ~_\?ws8皙o?$@%)ǭ&pWhz4v-/${{wb2&{J:Ø17(hPOQptd2Ũc_Qt* ӳҶ MJrV6%j1 "(>4K'IdiK[*;VUm%xaW|4sV1yqMDÕw懙wE7:4E'g\?t*k+~m9:~yt^N/=g< {j!+[C7zhm S鋨Z1>PPwr,dnfȇ P8AG#Grx`+5!]}y1/?SB!5!a s>K1b4 5[{MHsMHׄ΄#tuI n-"7[ېX\o0@Z S'rrRփiaaj.h.h hD (s_c=93NL݇,x80?}` f3 ɭPb<ލII7O}ycJ \ |m1j>" xѭuSB8jWY@ ; bs~V)ӦְpZ)Q99mKYD;6t1mvM{mfiߓ̕0 Fak+eW#H׆کQsaR8gA'YRօk?{lzuKU־jc(GyxEԕ|oڔT'e]@qtw\RyL;cS2.YT渆khk7LZH ~5IՒ8-^(/¼CK57\*o˖;SvM>9Ѝ 0)V)VgK7{ )H 朰(M(`\O>((ȌD\5]I*ެfx#(iiPE~T렟RRǧ1z} WuT0юDlaxЩ?JJ0k[v.D " Z/gBSaS}dec㸸f3~~4Ke};/KO!S 8}ZYF`Qe=~ _Pv9>ƵA Nj$d ךD@7>3b [v(h][[`u 8 Rj|)MDQV*PWW(/!C!C<<9ɍ  >R34 ۻw]0su8"ȯ :IOdgEvN RYRY1||fOefKaKYYK0uF%@fxx(0)~bbӋ1pΩϴW>.#iu<#o\2 q; >~k^+>R[IxB0H1*6$|Bc8xL{G'گ^DRTdqΜ6sAU2ٵTuvG|BϿqJxXq$T331R&jCzc+{hR[ڟK25i֒.c(R=PKZ._9Q&3#][!V6 CCUR[1ڇ8J#X= ۃ/`:BP TWm6 @:ˢ]>mjn-Kx3gޒSS͸r~CYNY| @~/`sB##փy"99W4MAb-Fq;G$xqM@j8B5dbs3 7Yxݝb _*4ҁ*p ۩BЌ0̄?~;;hA MͰMMGIʑDz7@n@|@{uA0SuT&l٬ Ĝ&:B!^tn}!N[bd,Jzz|c%Y'RM9G~ ??tRt|_[nJ@ST9|-;/;QQ.QilQ3.${it00u;"&TXn:  ZWPPnqNqNe}}ֳبHݓܓ(t&'tXA `p+V?)nx??әRŮʤLY ڈ9kvW߿D Vm%SSr&z '!G pkƺvai>n2r[&Lf@pjn*MpPpO [ RDtµUzذqWa6Ϡ*ӼFŰ/{pi)돰(Uo?aG"/űFޅe+WVu2rĵ?ݮ]fM=EYtD1T]p{r Bm'q ~v&l^.+GUwiC|w9H0~ {QN&6/,t<{=Ej׊UE1:DR%jYw{fuk -~YL(5y9I ^{ xx/llxx#P$eD]Zt*N..`-=ui̢HO*n6xJ&o&i+-mzp5C*kU>}cn)~nLy.ǩ*ţ9.ocٜ L{E/ЬUS i"#>cުv>9Mr~Ż )gSzoӅb˃~WkD܇%/?ys|LY%iX< z|5^">\5u7dv3< T3u^eioWz5%\õFS&I[h8`M11ǩY6KJ?pkMRTP{ȳgk+W"L/~c, ><(Tp k}Mכ"-_u- S ϔT &ΐI5]*7.52 2]Ok)䥴 UFfll_%^U4t1p|`=/tV'D cf7llI}><"}S?3rN!+;!BgȈ"+Ywޜ(2DLʝ}8 =y\sIH+\HU%-ROh?3u (",?;&#%;:;:#P00{0{t1:[!|; g[$8 y4@=?O j ;eZ/~ S O,_$6lUhkԠbA3wDjdK;7%]:v@q'C|7AچɸnjoϨOP0QW57`汔{C%>l%iu'NJfmuE1A5/7/(.v\-URddč Ӏ77'|}gJs2oj"-~Tʸjj 'ohWOv Aү1jὍ #u}H7ממ7ovb-(4}Ϛ41a2)ܞ W?KW z#gg߿qGԈֈ~l6DuvfxʪBl%GWWਨq" 'w@[*[7:\E'ķ"K_VcC}}=߻xZ 9GiG{=mOgo;[~K^lT BkBBKbb gcLGrY:#'!Hxư7'.(N5!5z+]v10\\2O\Utqtqilj|55meTp!c=Izm!ԣ,na {y9D8x"<*Z4P~Flw >?kC܂[/L xZֶtzwCRAf#Igղ,OBt4ղ)>L6$ayeP}m_YzicYܝ! d9Z`jƸ{Ŝv&ZbDvwjV7aI]Pnkzu -(p0e +amP4Iη1h if 'Wӓa=E;"%~gg$jF6l~ YT-1~99᫆uZA jF;)O[G&=X|7HT$*~U w)b -^`ws4&qp"RaW8ႺsnPw6'!Kek*R_m`wuH[1%P6) j㻫`05lPnA"VBZXP ' Qni1uRV'<_~T|Pg8| iBNaHah+U9*T rYx4J @\ ͨM7v+4'9O$$G%> X'xacHV!e=('͗߿.SsNE/8+?& fz647*Gll.u[-inD]YW2Ȼ S]8oUfvSb< 9:N8!'?g>6߅&Qfwٕٕ߻9r9.F&`N*4k@]~׮k8Nڣy3 W_mp>M%l 5lV,k;kmbpI,aC& 2N5I~gӊpOG+[Ő*::=m]KZ|3p'%h:KNJ=S]MD 4O +?\u=>4iZ\5V4:ާɹyF?bBIJEԠ-<]^J,3{TCb\;I //B j5zYp/M#IPKB}i+ڎ2hh'iMLP+(%c;3.ᵛg3$y8^v !C,r֘X5ԠSO{><Ѝ)}zgD5͝HU ܊/:GeJ8LܠT#M{ }6#p>mXVS}FSpN| > kb}kRGέFn;*Ppgڽڣ˨uCk1Hs⠍* ]vC#LGfJ1 fwS>tJ/Pr<>I>=dN,6/ V% dແHEnEnDm޺*xm~ U CdI@2 gw"s.z9giuIWoNm1@yTy!@S4D)Zܕ+0f~ػp* :].[DTFV8:F^'##2"O#<!%!u_}Ϸϧs={^.}޶.S?57z&S&Ӿc9#U83g1zCjOj_?;nݨf[1RVdd1ՙaæQi8ԡ#>Z>dN:vFAhWXHuu4Sq ;{#ߣu5'-nm|i= 1 7{jzb RbZ*?ͼ0-{ {)FFgLjq^A;xk oÐÐ6Zm(еah7.^!a`\c\ $9 9F9[ Łx9vCzDӺ!(֠8Q@e,QT>`Y}n8gLP PxyyQ;׃OLlf³'`wdT.PНSCIk'lG\؆m@CxukjҌPj'#IRCճ^@Em[Q,璲yD%<fM8\G3~8؆mvћ)WHd2cZH9ξx)ud؆m+Mz*g$?Dk,Q'zrR%4͌!%lö߭5Lӓ.?{ zfX)qEQ̓BJ.löB;\sN!]~]ÊS %ct>:İ7bZzI<]*[cP`WNJdn9+Jjģ.Ҷb!%v"RdV-1AYx "@@b?7K'LޓaLl22]ƀ/Q e'BL p@ /rXE }1U| @~Wjm;bįcc]TWg9ۤפ+yEAHAȎf=[yfsܗ9hRDntۮ ژ>?ZOjVeWަ~.suݳ+"uR(ί(t#0 aΥlW;0셇>'0|ѰBb؀r_/l2Paua2h;dVN 6C^sق [ Ud-8jQ̮n#PtݽH @ ~ jii[i0;,@) >uE\ R Fd=HBeeT!?BBq2猜J]5h7mѼB* 3|RϪ貱:huPQځ%_G!F1TTRWLOnfՄ%|W')9 )j!`/;d*mpGU\I}xQ$!X/BhϽc&8!F?C &DMCFT (=Zhb.Let<[Lee Be)a?}8;YS"e)U1@QɄDR`^C n˔7c R}SFPJL Et *cӖ䖖&?@R7WcҁZl_/K&i"nDO+[ \D0H zjܛ]>^o-YoofdDN+n6\0p Nq22 sI--xaV R&>q~o]bb:xQ9fd7d7:NI"߳Vw4cZhG=>O(~,6Y}>+ e2e ໐8HY2 o C1@{+&[[_ nܳr!>]`ts aY$2e,.:=ݡO`۟J,k.hvL ({XΔ* 1aZפ%1 oźb#c-OU<覭ls'w O=c(Lkx@U%qݙxYR{yMB(=:MJX) Xs=,Ooa/a(︵#h:!ľwvZXG-?ĉAgq s ufHınrB@vJ$}̘v7KM&žwt\M&:nvFNP{M'&٣ƀuUIInzAiV33Sw_l%wE$f={YY"'AJ-J--V~Mե 9},NMF2̜nm3j7}*/.I]ʹrd "?p;.ϺJd>³Kk[T/-ظMCLe76.]9{QWM'AOmd\9'h{[g),~}[]35edvSfH|oS_Βٞ'q=׺w66kQ2YρNˁzxPQ3xKmѾw1-Me!ʄ44A Gk_{H ?߉* I(It&7y  /xЭXa~ 6ȫR<_gٻ{~mgkqqrd~01 #~0b1;;qABh^kͭʹyAҋDa#,{/>a {wdm2*Tkk?syוQMG,n>mXz}1eDb8A|dB,>"ZPPEۋNT!1 W HWDyY[T+b$"OR ZYDӳف@){dd'r@BLN8F~\i{lq )S4W#"\ "O%B%BNu]>?uuɜ.ɼ}YZvR-dted멁nUp3չUDB>L\Vc(-޲Yqz$<=ɞLXY%P}MO- $,"Bţd[|/y̳x_οq;0_ Y<. +?x"KlUbq+Q'ԠڲV٢EH[;n`ؗq -K}zhKF\6W6 6%{8A~6V][bbAw)FZ>zVw1'׈Q#{Lڈ}2ՇF3*GSqԇGbNQ%}UTAeRG? ߃~U ;_\z[f9CaX)V e&+.A hW?h_سDRO"].&rWJm5ɒj7.i^vr;pAQ# aI˗Mz|) fDP}MԨ̞D%ӱbJ-zE96V Xֱ<B?VW#=l0wtf4jQ}auoN56(Y`FxcHȉ6ð$)k|2)elߍIIJ(݌I/gU_YYdD9 79(+@Ab(À wgpWm78 #5Sf~UgEn3VVɝQsyȶ;;WlwX7E;Ѹ&P;q_-bǍg8;Gſ 08 ~;i#v_Kzdg$sl,ʁ(ko[ _5v$ y(@y\b?֝P{ZuDlv)A旨E# mM1-* /Cl" ~vtQ'+p}⺭{dj;g=v8jOj!pft W{t[חqyI`dMD1ԠZ8~YKD7LEv/,/u md Ծԇhz: jL}&'ZsfUVcf9oXJ*Zf8?ASgPڿmdcTS}3i|V#d7yH&OWlj Fȫ5<\ώjeS6Nv>OoLJgpJfOZ!{"Rλ\^8eǖ{ _vC״Td.D1&Vn6tfjWP/pF%Αųcއ,VXs@^y,ZeZ 4g9d`]Bk/vۘDF8n8nA\\WAC8c, X X߬[$44ʢAy/QvS7]%^~\Dp`Oe:ۓzTEȧ l^r3yѷ&`J۳+6a̻JKg~f\2`D}l%5YCCqVW/'=gPׄ̄F-.!/lV>'!Pssd&De 8}Bĭy@MU5$**mm:EDŽcj;(-cѢreYЙH]ZN&̋X@}sdzp _-_+xa8)A'5Ϥ@^NN\ObdyӺcGpxΨF+̵α -lr(Y~@-j$Pڿm[U8J[N[zCTuK"F= K^NB j!8ߥϐ"* mq.asմ?sqF< z%PfkOLmK_SKQ*Vyc0>5Zx F Z[i×,L^A5X &o0ADX\^"h@szbyyaqA4hJlC=?>KKI&55\ ff rr0z#?"A|.`n*=)/P(A[U[Uk Ӂkn7qqGV q+q+͇4*K^6CȢvgVREpQ SMp'\c,mK*:jy4mq2vn[vRv%}w5dTlY*k(dߗlEd H~}cJ:ysy>Μs\5sbFOuSZwJ݌QtoF ҹ&F# 9~j{o1{W|-(X[2K\l%.axA m>nY&*'ͪԳb7g&ϨS?x 2Ƈrq,(-ib޹a.kF6Yޭ/a%t!5ʋqnJMQ{H 1??8f/t0T~gzN*Cr{v 5PA=&4دGsp_PS\k72vH?%*3\־{*kƐ~2=!ҪT_-uqdj *q(@Ա^FFѓa_ ~!!׀k >lxMA tb b NY#N T@R5HD펀ЉмA@88dY|zLL8"İŰ?Zܐ2)_P`WW]qw|Yd77|~{p`W<>N6ee)\E?FGy -r+rCS=Moi>`.x^n{| ֌>%t}’눞09TXeu8]a?{*w)7whp|#nkKo0@Ք6h8 D!$Ķjq-HHSS0ax=+ T`@0YO#@'7( HOE<pp`ff73mmqqC/wFCϽ (@N"sM8Y J|NJ3 SMX$gY'Whk۩EEtrms{:553v0B@+c_nw#82eum/@7tZWw;-͈!_DԊTl6 Yx~ UC\t#7Y=#$(3̉[lkC7tOըP҂k2sI9. cx_iw̱&ˢ@7t'4]i2XK~3E9'*OlR*'f.QF@=E&GG+1V wXaz?Lwͣ'oG7t'4r}TF%ӓMcq#^yɵa-G5sӵ'[o%^uIYf@DCUY>3I+dE5͊X110M'FE9մC'* q99VܡwջU?(^ .>.+MOINI>ZSZ S<b*Ǩ,O?7II|TM9<̣V\6΅?J* h?~A3eFo,$ X X~ !"Ţ*UUS XXä[]|tmb9h|DfǫvZO쀿' jI'"?{gRV@Nҳ+dL7,wPu.uaj=S392a\n~ =JٛӶ7 e6~v?B T̲swyMuƭ^H\`X==K $%0->`=$۸Yvwƒ'[ A*zlH쌨+& ȇݠ%%{3{\ٝhh$knH4H7M }He#{ ?ISS""p_z,/UlB/BVoM ''P!𕞕fc4ds~3 X*.Rk+DF#Os?BO4AqtN>vMAu[c/-M}Q#S/6kEwXST ]meQp~; Cf TЭ(:( n.>}6!<;o,-{xIH ^&xEDS= = h\9!,iAr YB$z\H$mm1/00)Y58#j3;- a0Fyv%.f-s f!.#.?K*^+H,I,}߷oaM@e86v@9zj``V5_ZtuES}-}sdiʻ۞u4{!چS~۩'AgSMm}q1(~ܾ$BuOO\h|&q P^͛c ;j'I}_nHY âpTa8Z]n_+֬طnnp Inpm%oN2ȡ8"L]n>aBTNL<Û0u,0f?hrj͎nW.&ύfVӞ,Tb\2;9,%~K!4Yo_hxBOknNlڪn{Ssk qi=ac66VyGw{`áv'0e ݢ򳾁:Pq4a;yf_e۽{*S[]g!@YG:?=u{wmlBTV݀9޴qk 'QҚD+y$ `IMq"q5$#VV:ϴZh-@a .L@. #aWIhj8)VQg_12b@Nkx%f%FXQQ^Vo"KhMF$F$&nJM Dr6@=yd !:[EPE([{t> 'tD`6!#De-gf??amms|yƂz;HϯdOd@`@*J7T=y` @_4]@w ઐM{@>LP򲷣#3m#cD5H$\SQD< 0C`.m.~M[>J>jBu5ܨ U^_385u+;՞X1.:۬ um糧~ʌ>hso?@{܆h904y!0}5#-Xƅ~m ]N \Ǵrzw-e;t[f܁O KƲ T*M֎x=Yn{o ِ~hz_zZr%K([Ǹ!v*Kw~t }07ݽxh _gl3g,̐8-ūz_ڨu `⺩F=sU5]} Z|M"+]1RjJV#׽>L5&9xMܾO k7\ߛE"ͮSΌO A%1~=U?<m[[5WQe(a@zqGcm ѫR;a1頌7yHoF?$'JMZZg++);Q1+I E*fpGz'QR=M}ϭ"zVBk"k>k<<*`*ƗP,|r+ol֌֌}S ?<d#:eE!#DEE8deK.DGDȈEV&$ i޿}y<>疎-6+"ܽ޹%5A Y;$DZNт ԛKpʹ-vFrt 8%!}loэ[Jhŕ.r2X mfBfєµ9 3+ٲ&{`3 89+?< %eej6T^e) P)֖ \!+b$11X8'q$M&sa7HŦyl0Ëy+4:ƫ'w==F# 9=QQ慸^*7)7 F22KveZ]]<%>r;A9b϶wn;ۿ?=55I]S1ͭ8b&SM}ls:lixʼnN82]Q)S1uOw6XDQS&S8`˳pm\M ,>r,caxGB j@kξ`vnN'yPK"CVi:oa|A jJ1pOdT ^BSr{g6y /ӆ An=.jp穏N'-s$)Wk1W 5mj8MA݌JZX}Wu}zuσjPo459f6 |+%wAio,>Жt7 s{{5 $qz`%rwD᪙Z?(<ޛ~_ѓvqLFNۻZ647Bmfb;ȇ!!/ow'WHz*-ߴ2%={;+@ZdcNKҌьyy\vGߦ!!|̌r E[ #Fcu5 My|.{ic説rrj2c2?UzdMM9\!Y!Y e[--aX #ꄇC1Y-h͍ĵ:a=7D_V1Ŀ0ܸ|c{]x?5fGצOj3~`cާz׿N{1` N Tv7tax:hKiK%o5ꪯ@W܁9v4kpȕ3{̀YYiۧgo:QAF@ONMgh,(ynK{P6>S(Lm)+r3P]¿rJQgm˺56T͊pj2@@Z$++]*pNEWEWQIΖNکՈh =إ_~4/@}dts{ ZKX,2N]%u;jgUV&cKE$ߒ9?w =hf~:*ڹ-T4=~}b+Gs5-.K1,:FR;r76<%uz4zm6qW'V}ĵz1fE*ǖ4a>(GP9q"ujd&1pL+}p U`)C j[[Ԫ}wanY$,8YkdO^cAhAiQcvMb' TYDuh۬~pAWgSgh{).ߥr3»iZOw汧Ԡ'4Q*ejRCZ'fP)be)n#Pja7كo=zӮ4PiXl&+rV©gM>EwA j@H^E nc8zON^ tW{E3v0>ԠFuٮfYŭN"<ؑqsWf(뚔Qx+*ݩj3zJ/0Yݼf\Q]776jmK w4F#x_ \4jӦ T!RuKX.x0"uIwN\Tmu9t"pק;b[xH,i̾J؛ݛ}`0BH+Tj`M?> "7X7?"h9*\b `/8 "I< ,?;NzQhZeZUo\ol7,m!!%]g?#+e[oO(|FQ;qhD2d|RGW8ɧXwIwkf5MkM.5+7+iӒE9+{xٰ_$\oO{{|fFœHi^"*5q!iw|B-BS,7+;֛m3kM_E2?"t9ȚfFpFP!SRkk JcGU8£ڕ0@S"W@AAI N̠GZ] ~ Vrp bQ; cDk͙;7K>p9}R%f D4;/`Csj t8>۲Lh0aHQۥ1qg,SNG )~m, jOj!sg`H8@R-OPW^lujcAwk!Gnǩ [hFa*YaKe+l:{I6 A[MYGy'Dap':.)]zoR>1jevd)1=؃ ~ 6I~c}㜨]h>jP);\T4wdt 2wW"vD[d ;&"n$H2NHq񘂬1 /uyw6j߫G_׬xV -ɉޫRZ.;-/ dgO=i 4o(cHCYMhݭCӝrSKJ?8/4Z('=M<7~Oa4uȽ .,YB( ?$"+"uhBj_؎ 3- W.]dq `ŝa4i8 `QW6%c z /ְƘƌܭܭЁю1c4|[[^[Ŏscom1aLv*tݱ3MhI@.227ff#G4hqi1 |^^.rZZZqwW$l .[)++yl8]%._ L:U^K)C;E;ř<''[`J뻫fF3m,gc+כm!`=d~ PTp2;-(-h@fb:9Gybe-@[EXGoii+'|@ >Tt' &!MM ^ ^Jl911{46RgR1S2/cbHbͷbQ[7h,>2TІFi^BB'^ˎ,3퓄URUmL럻^/ۥgy#86vWαDbw||j^ ?סҐ< 5FsPO jm HP!R1f}'_4]J+خ#Ftf>o4,u?U߫a.!_?nfyKR@{::|ZAIlDZ@wPENnc IȖ-kc cRY6Y"P}dIƾDȒtŔt}9뺾jjM. zAFBɤg]̾acȚ9 w5ԥ j pL1WH1k $!T:1'3㠒YVkuԗx03^T))JŴY38Y|2kݾX_23,{lXj`0u 'dY(ڄ7.tZJ@iAe+w32x?-4Ky#*+*xa#YɣڀI3:3,nkOJ֙3A9=Ȳf64.;q y9Ke\O`ufmگk4'b9ŵi0ZqZiF:=mJ{{a5_;"8#8oD܈w)A gŲvfwf)^Zg֟П3riβGzPa6lθEGv*o~Z>G.'*!yᣚ>.K Br"wde/uY_'MG3U.@>BJ@ t|NcW~4c:c*|YMA"n(Ҁkz@|*)@rZa!Ɗʁc4+G{W@#N/3f OTl{r|6P PPww0ؘCh5(k@-}#OzV1{t#ы>> UU0whƞë{902x&fj^M<k< nqqR.ZTOɕ%DD/P7=|zUd FjX:X q93I";%wJ;+8U/UoYg~~&d"YhaXX=ziRCcpPhè.9m[AUdҦ<-c즶T=:l!S1GB)xcdA-& m5Z1#4ӯo4"9q?(]a]3ߡQfX!h|~0*O>%}XAv @irR!~RwnuC';0mͭz1\ɵ:MɃb ꫿fm>6'⺣18 PB_@gja-KǂD߿+t5Մ\7QJaANO9SP,T,?X `>1q\1S"8;{dTUQsO0e 58}0pќۜR|N~' '?tEjܦ%%%''D [/3Mۛ6p=M}[ņxN<\^0~쏍֓iw~[\jNѽ{nŒ{ BgQ Bp}fws3c MOvн)(b֭BA%%y-'ygaDԸ׎kǻ`=r\ɽkʂ\TTwWWC57Ki ـӇXAq.w?PCʹl;1(VGdЫi~87PH%(hr(SCا>uDn(9ZqCkg`g"?/Bԑ;|k?zqma_?z56Za1Eȓ,-,-n+S\ 9)#5Z5ڗ33٣%R'(WWC NDKHzI5@a }*,{G?>|(YV]ArrbDcD5+6FwXlܻz!.vkŋ+=;sƻMMg1Yѿ#cjEf:.{ ߷ZOyp%f!mLSh+usl;f۬ mY|L<2`Zx"q0BfW|D XE)fE/߶~gb9{{E*0R8}i> @]@t͗ѹB|ߴo߶>YU8~t٧agUϪ&w n'*%CKYϚ@򾀬L{I<b!!շ急L_I_o/粍4Wړa=<^,gl/Fz.Z/!ܰpjI6;itvxqa"D.UQ_x{OkiUw@L叻~!uh~*ٞw[v8@[*pU0]YϥXb'{jџ繁-Շn lmK~Iwh`ns&h!QM-w7wvXxŏۖێСQ{̽B8jcUa;hm}w}w, $FzaU ͡d d ddTdAt}_33N*ƴ_xq`r`I@'V*.ܘzx%!!F߂efʃcQrQT{b_bZכRՐP$\OP;d屋+(qV:+TLYp0v5Z' 2);P }CH*rqiZNM9ђY c[iQF]o/ʄә}}|#IXX]X!-!TH|`?KLb GKNX5R5hR>s uKz!"%"%Aʨ_Q{;{{tCCB]ms \Zκ0Mm_$٬ '=f;,qE8n=KnճV@{:'.o ֍q'8Q*!^Rn6.[,[{e?ٺHG2,V-&u&2%DG aJoף@W!|bt%5Y˯-A:}{,$ ?Y/|Z %Evq.()N N`ucEi:I!]ػp&N*)'efo"=3#;+#Bf^ٲE(d̒cdEH }~4t][\hoByMBJ ?}D PPsZN{n N.:En B\>V-/q:Jj{!dE!W(nDm&O.# $ 2}Pگւ|CErSH*=?&EŒOK ]CbBŚ;ȯ-bΙR[F[ N[͙D>=2 &AVVWj:mrBxpՃCdRh#ƠDdr %8}٣nE#owܵk30+4$@s/WWˋ%ٝ͝U{}|R1hS%.ɢ1pU +h?s{ -e@R0nneVLDԳԳĕ%%nu-lpǃQQsdo3NiMmG3a^{jZ'ZN!C^6J3L͇aՍR_3oX{&gzv=2&Vg[G϶m Af#wkм;xuD=ŸK՗ Ȅ;n/EpTp[9"uRexZ$%^9y"t8b DPcL4Àն6?ԡSԖZZxao##Y M'sƶKih-] %\,11Hr1i%1^P%NՈRԠV/eP5wv#t*wT~; .ԠhW Ldzp;~N}&8gD^uGa>g_!6nazglLPC_5}iY Fvd3.c^L+w6Z\K>ɖAx{g%OJO&6;9(CPꇔAVTq$A_ UP0eLiGxf `'DoWtxб~t(SA4`h0m6me8y&2L\\\Ǔœ2qRT/^ >Qoh]Ahק.fL٭m%s~BяtNCa:ze.] #n/ĂÂ9ի);˭IV0e\>lkјF,󨆧^MM/H/{P@>?i>Sf~=8$$5qi*G8QND. a2  't~n~/z#2]NNyzD>OZ^hֶn'jyJ$7_K#Xq0m7J ++?||GrfY96@+FyZ5b L/$nw|=p ̞@4h'Rޔ9XU[,Y^s%tC jZ[Q_XҴ8(m *,@-yrhS:Czp{%|f_d.q.xZ5I{\`Q6>pAl13{M<-dʽnv)pA)V>:o=LtߡѼ쮏VodW{7Uҭ{E/ԡ$3lAsь.v@M ģ!⿕``tіNW;8WE:nY؄_szT_=g#&222˪“^`[ 2 ixxm$GG?#و?>zO V{%O> ˋ\SIY)/V`ݣS-Z/U0WJ [i1A?PǙ2-NH|յlH1&NT {P=p}r5x^=}&*4R2d.ZfCixyp &j*J(5~v]@cC1.fC@@JJU˓o v} ɽznP@5ߊrp,gqk Gx?6 5)} Rowpxj ؤkk1w fU;")fy]C̠ΔqMWF4ȿ ?J2#Ƀ^kKwH4>\u2.\m)gCSiirٙdrԧWz~6A|]tmiYfW%a_Nȗ2&"M*rQ8 6Br8not$úd݀vH$涻GHq]ړ0021(%uF_|~}Ȫ:tO"Dc.ٖȃ{Ƶ=F!KOn-5gsÎxFd#u;'{F9d3 2+8l+|{tb {ٿq\H]D)E^`- ( p 4*|o$15A!j v{j@ '/t6.Zɯ ܪC'eLA>Y5non﨧/~ +D%Q*1JT*yWG@f54 %Өw00DC!d3auau鼾~TЫ>L>Lyr槓I!UZaEA$3f >`g+.%;5*ai)d]>s}Ǽ̪4 Rje9PKQ 4\ {a.LֹO}= с\ҚL/F{iegaGOdJowR**NҠݪ ΦDcJcQͦ3?~cC# ev3:1i6l'h n,KSFdaəDְXg'boKJ9{y\kJ冲$'2~gڊigՐ蝄+{LȌ:ذ,/&~LktJLôCӑ&Gx{ 3;.|TbW*e{S#\0 vZ#}-u]֨P= KcE]2C(0 L_!IfwQؼt><pRfm-T·u?Y. vy8|[_Kل7Z^2Jѥ̿lfj{;&{T _ VR3. ɓc^$q@_B_s=ɪIF-Z 5RRXYN5Dɞu'iE` 2ٜM:j=]EtOf^^*oDPyP< ]A̷\7غg;_b] k#4+# j|)P/I(6H$wƝ/?jPrM5!Cow{tfff6J*`if&CqhlLM8]8= f6 "@Hah#Ns>g&j0xW:H{2.qsh9q3\Wdd x85*t<ʅNT =|6S]kѻ)~ wcgqY%N"eW1觮! 7IY H1!0{(>А[=w 1ƞyz`Y_['gz./ˢR fi[O[M[4Zj8FEICSGm {e']wY!dGķ;die!n>) s_\yL]>}bgl04I 3$TǵGj99?to!-<`}[Iќ{ 9΍8::zzFrmm2yn~QD9vmyI_m!v/>557>ȾEa;HW.G7/ҮҮ(Ov#É>SvKxSKQ48nF p=?x.(R>- wkxXTF?~>S>6s;z?1)DζʷGo̦o$ 7C EFּ83T $#{(>BlR WOV`-\"9hhԇU 4*;D'P<T2|lQ^a^PUJʏ3g8m&.~=cKr(i+rӊdQZ1(pWS+y'.|wܿkY;۟|̇SuY87K<S <]kY3TJg{'OnJ3/ř{X$JP.RCq(ϫs9pv }-d]9Ȟv n0ErX}]i6fBRFJɓ`zα1d`Qv)MngEI/Pt%VS`\r\uX%<9}MR:7)^Z|%<7k!"C0m?\CҴaYfڬW.0]QPGQcU73Dŗ;r8> cM%lYEQlbd)d)K"BrȾ+ٲe-{}BI~ϹOL^t~?<ד^}]rc(&ܨv(aA I6G;եgg:u\Mx(eƒ?>)|S&RpW7"`֨, ^`D݅SQ3(퉎?"+!9`u܍܍?0%>%FuHsdo{T1(*@aWH1bf_% }auv)zI I a"jQY`TY)ʁr3|I$@u*#FD-|84Bisouv}=>v2Zfn}a+@s )lNk[x7vL Pt[v`@] g=|~~sN@>@J7޴$p?rhY\\ւFSzToK_b8 (-h̻ϻ'm yݓ[o0Щ5(:\(*o\$QXϸ)Rw=W[kbkbτs(ZxNNwy! U&&?eJOH,w5cf]kg[ֆ&]:}lZn~p*:s3{Kri'BVOԷ!d5$<&b QK \́{>ZJ1LDLsrئ*>MWK>sc$~Vuǂlfޢ_*Ht{ӠnNLuZjۼ ֦:2 ؀cw-L>^zW9N}2x]>_˛jP> /~\%B%\5;Nؘi?[ -P! ^6z\!uOԯ.ӷKqvߖz߲>倇]djZ/ vƂ@( ;Y5uK ʆdq2ղ< a2ß[]OeZl/7D+(A:O<@f$X]R./2gb@,(8VF ')8"E::]Q JNZdvX‚@w&G't3ӓ{ Y`aܨN11Md{ww[VO-݇o'ltSz%wME%4.4-bf]ZEnu Xj %Ur0>"hLHܛw3nZለYdfԳzfNtns鞌w;&;nt3s'\&Pé\SyJ˃_Xݵ&QŴ_; :yx=x;¨X* WVvsZ`#e ]^N/lk6ƉzZ|4L_,jŘ5'ΖaIµ sg9\ehopM'a2b;yfp+IL987#Q(bS@gዊT U@zڤ b*~;}0bgFY^ttޤz}:Mtjmoc5cZ`Kr vWθ1*XTi;Y"btY[YVHRR|G2IcK1qdg'au@Q[pHBQ7}vz ^IPs-@~2!D7UԠtdg, 9o,6XI_AWqJO|@-+O6.?eM;@T2ccƊJnBnWgnp7qk8 ЏC;WYi=oV,I<Fz.~c\)XwF/̝ECd[JrpU̫ۑ0.ea Шz2Hju#lX/,ڗSqsHn޿p@!enu!`{?uK ~b.{jx+NJJ.AFcafů[^Nm7 "f(uPoM,Q3)I"5 7/W7W2Yy{X(jjdVjefОK"d&d۪\x;hhÿ5K4"V~)Nk/ ~+а|R =#t5θA JaNLô8M CT{E Nڷ7?ϐND*oBޕLôDds$TY z֏YLݳ /0x5Hh%/5mwT1[Jw &E4etzAhOқ+_kdѥ;obg>}3sE{rm%sucuc=ūũB Sl| $H:9l^ue6$1*t/J O MM3`YKKѹU!:VNV~o#G^' b@vPUGKuy3 H!,,{߹lFMML:A {WJߊnv;`@LҨX`6R4#M7pUboSGrLKs2{ϻ6<*寗sИ)PNlz؎1:nխH@M""$jYU>78;ss[cIYxQג@0 I3 M-lIn__;;;`cF  66]]Df~] M CoZi\uC:{*bIO<7ofԍԍ7ڇO?oQ..!PtGBS/:?O7 IݩH|a_szZzJл<*fz@L}mL[. Q {֝,%bdb< t=HJSLtO¥3'pO^$f??vfO\LWct s-X RD6唾'l4LںNŒuދ>PH`MQy ~K}BPS:hc@l,#ZE =kХ#Uw)m70lC^֑!tAx8Hީ#ᔣ5Xn04L'4ԥJ>$x4. y:,E荋]nb옻?Zs̒lARij26 EF6cMF>C}a<뀿Ƥsb##Lô4RZ4;< +6{)8D}n°QP,[HKBLY=h#KS,<-U16rr&Mn/v:>U{b;߽鞛QR 樞_|Yhӄ7qs#gg-?lAwkN{XH vLnU %!NYOYND՝tT\ٵkp>9; D ((b>g ȟH@ k F#  %U$Uo淎7P 8ξ<9S3?:aRUPh=݈@@bVtq{= 4໶vdK1焙vܝ^*Io܄mK&#k&9ƽM3rVXV;vr)/ }y߿(bnIՖ^Z XNkھ{i;d (:|@sà**kT!wq^\}}UGD5?@oVp*0%X:Hqppe9M|T(hPL@IëТ`+33y&ePsxx ^ X X<'7ʀ[}ʰB8yGOs` Pw]N){k#11C/#U=ZRRڪwˀ@ZZZp=S&׹!'=]0#;/iiNˤoɲ:,FJdpg;=a͓g%u(=_j28No"%z_/H"R􎴥i b(J**T(MR .y}n|T;{a2q2'9~a?I' Ku :x4uNWjO[fli BBpqʄi^>-JO{"=QamضZvIεe'њtL{x}wSJHlö5\|0R kq /dW_)0 ZЂmLQ@udڵ xv88SׅDKbF=fOCʭt w#wo#Dc,\Dݞ-'ö %ؐ$WuuZ26?{0a%x+3foX"9OjvO? wR{ pƓYĶ=P{ϒǒ\gS;vSKN۶J7=FAu<7ե_ Ӭ.=Sw2 66gˆÈ ]Av -d2bǸ""? ~)i 74Z,ֽhed5'sIp-V0My_j{W;[:SF""㨓R44*/٣>\\gWOIy畕O*at=J+ o==,9TRE޹"aqvqʖ]Ϯķ]z7gGF <I;5`-N\yJGqz [oq׌%6S" r&؆m)it2S$C(}u>qzfo M?ҫNsdHǸ9QU~Lfo ;p} mw'wX @8Jb v|2->mu7tO]5t/fȹZCC9rDe"tj]Z/8Bd2IHz` 5LXy5WNB_x*-0Nn IJd %TtWtP|oveiei >>*3S5@Y{#45A.YUh AѺV%*E3k3k*ƻ@xξffP*(ӗSW9v,dYMZ#+D̿|G:>˿BEEqpdS{{n Th3*`Fy11BZfX+q@3Pi_h%64tscR=t9[5 [ٟf"(w+;BLnH@L;_^Nu QL}N"_=9vظظC r ru8a W8 "Z Ǡ,9ě9Q2=fJ[@aGa9ꅛ03s8X@m@hU݅UnS]]Q@cT w{ҹ ߟݎv'VK^ffRP;&Qϫ$hk"Y˭<|z;ϤSPVg5JH?<>#OY.crhB*sWI~w~vF>)A(Gl6MHUwhT;[VZjgsB\=8YF],NVZwWZCmbfbQԜҍm){m$lxC?{ͽ5C1{'udbsS.lR}GiZ(Ia1y(grvGgѝ/sLV֓Eүl*;I).<",{*<~ř*+()E`\$`31=-6F5[$'#C {h"rٜB[d~gcF! `?2hx fOqOq6 ט}s4nKh</`=x% +ΥU 9jj:66V;L㓋96ſ~Vg}YF}õV"5Z;9/&Ž}A; KZg ӆakxT#;^bO,9iwftXOu~5P/;-ﴶ rV !G0۬ &3]EꓓOQ!9UIm00T4=Ȗ>9a1- yO'R_KV&~smwL`-Կ ] gi;rp̣mr wm<T65OīJ(R@=pԜQC>+Aޡ؆mI'Q?Tw?7F<|˯0[HQߦʴ ۰Єˌ)U>4-~/e(OSIdƦt ,w``7`Dž[xhbUMC2rGvw .NV7q:S˾Q*SQ[kk6"zp&^\ViBP.<,(X:#8 h)OrF=ԓQ/SPu-e|D ᙉ>X'G+?DknE;P@rvosƺ  ݮ7~E|b|b9iO/_i{ƺ7#7jt`Z ɯوWDϋԄ4-κOuhށ*\_mBb['-e`^ς`~ 0F+=+_a?e?ªɪI0\ttEyjRR{oņ0#! !r2Sރ~D?|{k7SmpM|MsJz-e徒!jB\psfkt;A *1|O[ܝpXI&~gsT  $MiVٕ%mn ۚS>QOW,Dwƛ))G`)> HY(pLĥ;8>ƒ5d  !kdT2R%&KdD!{-;SUH)K)Rsu^r^g^߯4kh"h0f3)] 23-̩HbU#4O>#%k7OZ[b&u҈oSL vxu`Cȥ_a]R;]/qm -@M@URdR}McbbQ慣G=8qvvlV2z!nqrd9*8v›fVǸ{0>Zw}~V=sl_-2s,ͼ]rvmwwj^+ү$X9 m4|":,PͲc#7{B jBk`>5-1tqac_bYM7Cn.?wN9b*?OivN;=&R͈sfKZ#Fc{$7gZBkF ;)M$Bon{#T;TE9%Y2Sq]M'Ԡt!;|w##U_;-4aJ ^LޞxVѸ-* A5=HhH֛KڿV!+&hwAt+Ʈϱwwq]];@ & I3[lyKt!\ 9֔`|00v5nE#Ϗ ;ph{v)h0%qv,wN]#$A@95n*X9r&7/9UaI2z8i[ݓ,]UWuEk#2d[HXSѮ *CfwzF7E:!Vә}®K˻. §4E3Qˎزlw.]aԽJ3 tstt ýSy@'&%dn2jmtD}AJ3$E@7R 29+b1ӬNNQB3_ccmzj .VQV.AɅA~389ɾ3/$cJ7ut&,X? auz*=hjaofRLo -j^35f/ jMyx%aAnL;w;9Bj(ի*bD)3pvYkɻ4nnΒ7 yrj?W5^u0`MA!r]*w* )1&1J@m%9G;>-GVl{x{2Ψ &DXDގnbt`a!dڨ٨ijzMQD?:mYG4 .X [\z>Tv^hW%W WЭ^>as$0 2jy)xttW?jPZkPO^M%[! CY-2LJ7A jĩGQŊ=bY}֒{^s1-P`/y6B7Uf#8jD#,WuiZOЈi@ jj{9y⤆UKWH^TTAlG+f:0ݰke*}[B7 ^,}B.jPQM-`Z,p3lzErRSB3>ycAWmԂy;ٌij%{՛Tp역76~2~Mīێo/\K䨧::ߣ2Hs99+&99ٞmUM[NV@W|#V)ТqiCi'K& jtzl D7q!RR NҩcQJGG11!.-o6MZI@h s_V()2 MOĔT8ޛ=x?~o]h]itP[E};k-p (1=2=&)hߍxtkj6sGe[K?c} uTY]b{kkS?RvUK<{FmlOF3kĭߏ&Q(u=sF;~\ t@' O\?*(}~0dttբ#[KjOGo`5=eI~ew0]1]S[ HH*G:Nyf.e@bI9 "Wv+V,"`g% OrQ=JJ\H=1\BKz~]hb4qoZ4{8HNS3v MNMy[# [)7t~0<%WmOGKc7^vj^ Lw19YO3MЬzSj.{jX`?!d#6Ic#-!ƴ|l?ܱj#L5lmh+6Ѱ­[ |c~z%=jOWLo;MsiO$ 3i5fT ;t5m<%&aDae4x>eRAf͡_1N0ViRl}詞n>zu9Gk,׼v|C j?Eݾd4!7! 8R(\z 5M,ʇ>|'VT`ezhgg= 5KPڏju?-k[q'(V -{5O@I_tM!ڿפWRHqfW4Hw^}zTѱ$8~{%qzvgΟ鞛1X2 D6,N۴4k8e}: 1x_"K腍ލǃX aNeҼ(hdT<<(W͌;1@))l:pk/ M.[\Vf*zb |KA܁+y},q|7`ʠ˶ `ſ֌!X\rD!!+R]~lV ̩//s,EjY6G?=U1e)cI%x;cF~N'fӂ? | 7 {}x?"nkn`p]5dcC%t Oq~4Q3QA L_h}Q\7B u cWf' =NJ1#нؽx#PQd UooOO=ٜl_6C>[yU{Jz5ͱox맇RǙ3Y+}z)T_aaR[`'po S-j=mX"]/ܸR+JsIf?ٙJa6KSQCbc V,z T13`'*q:uxI_*LVkRg娿uƴ6)ȋ,ɟJИ?D#)+=Si{<]qoHNC.Iز]Zȋd⋸XcΎ5sG$|zHX.7W̠Æ[3 Ejt٥I#du+ُ0 n#b=,W1WҰ "݅1o)M#ƴՊo[Z7U'PVc\:v7=w Vd_S1ye*}dVlfgGƗQ>\8ALTts$ VT^W6(ҙr{ӯ.F+Z$/R0R7SCU6_qۑ1=Eg+ BU/rȲEʼ~<,7.>c Nc;o;:5SͶζMs56\\[Qȳsz|Q&d=sHk!CR+~GLQ拉% t; M&TôDD<{[RNV {:;5jޝ\1)SmvSg !`7ZE7*˘Εk|}g:t%1 n65?_R0CR{tCH0!{ 3U%ciɦ^Q|̒.5:1&|n7- `z<63bs&Y_XT"s~S7 3_Eߋ9s14m/ 8`;SGq!&t{z)0x}niE Zӄg{-N![ۀ S55! T-Rz9k.? LN F#D9 :}UTTJJ}Ίe$ZBe/5`!e9}1fD gWԬ6볾d}id;&&uflnXXa~ʾd;fquq5>#d|Kmez3#4=ȼba@kAGrQ- ط=k49V=Ǡ ?gc>۷4?KFޠ{SW\Fy}!_}ƀ? "?F2YRy*:,a4LڔdNiY  ޟ/%)yW׋a.Z@gc/ 5kG$BMya9]6tR+j3"xN f&۸sfQfԕ+n/3rI?ac qx`o<|v'gjQyzMRF}|;'I +q{tl:) ,v#mxBb\%n'Hy!{j#$Q))ύN"kKU'bA9QyUL67Se̠Aɒ zBl8-C7n GFr 92 ۴UUYp@I_zB;r%IodvwZ3 orr=H\xmW梿B'9ƶe88(%  0A=G E5;jvOUTLՁn}HE VZ0n6PgQ_Sk%=ϟ ʳ _ ]-mm~}sۥ'k#+x=h@ZZ쥀B3g|Wheke[qHJB&[1X(IX H$"ˢ[ű #zU96&{ M*088%6-AΌkk uh^h@0K [9C׌k nVhn%%}rFZw4F5Fhh Ew\ܒM8֭裶kC!ܱonfpQ=±vgL`Oz%h!}@l!,'얈{TYۢJ)ՇBJ5ϛ)>Q ;X`q06a顔\yq\GD- a( 늭 i0 MOR_$Qud|1㛴'3k( >7uu4Lk"7+,qa.~@VU'7| 0k r5|[6zhw,٥ĵ S2-LڿM62}v`t ׇ T 3'im v<µ:jZc+ F!goB[VbO6C@^ ϸBW<`oSNݓ1:CD~>Cpp!EN%q.u#q{KhpYxb6Uf;TGp5ZPPG!@F|20+N ''Dy\dzz' O&@w](Px_&&(?aww?y4wYdKdϚDd)CH5%[%$zBy|tcZt}՜?\gι>}SS'IG:CH Pv% ]tC_/]X X'K!KeX`em4o4;Df6 -0fBW_55p=q}С3*Ns=ouwB{ZfKiQ6*8i+Cc"'%Ƒ c䬗 T zhkIDeP&<Ǵ>0A U XHPrr=?yt6-?=LpmM:qq=܊&צ q=/;b}v=1W;ja]Ē#06rӇIk;4{TL\bН[;l?;l^WN:g#hh ~wOy |GG,Wg]g] ^oL%EqZ3*"~f<|o :H[2ྨ1&5`#`# g1 u v r{}T>!RhYW=BW92KEGqGWE{99jɖ!?AɺBB _t]vvǶ;Lٍ6c&;e"G( ?yVo=5 %w5^2p;49rw{j{JbԳh~If]-d~)ZrbwTO0@yj1k8+g`DwG&:=W,Q `~i+ĵ{HfLl*n4>Q6b͔&wLەwva ef(;&ݯ*ܼbTx=w5]*$?&z|kL=H=t4@<{0m DZ f˃ߺ;-7LF\ )mPD=]ĕa-21DM}6۬e?aدAUrSH`ANb.))j(j0>_Yy{ 0 xv^A 5zNQ %D_A14"rl8;G\-\-E&wAʷ꣞`?'yȇ·Ln $; ßh:/hcffSvYr?#'%ZȵHR79ᱍwm6А劅>U%IZ͐{l*;Y_ $]uB:!蝼لзDjr_1>;e}tVkGu0vFzsvng@MO&7]d$5EE} }@0@?%#¸ i!Ps^7^"}jPb,i%\ĝx033ps+wOOk]\&& ?VR 22 ?N *=aEY\@ccZq=9Zgxx,ڌ،b}3~"PKWAW!)`ڊJ3ii]۟386 ɞ9Qf]k{;SƫowK{W{rMJLھM>R fEPH>sw[CR+`]bPnC.`jףv)>;Bb`6.t́/51mH"y4юrFZص@PPb.haÒRIQIQ15. / RX&P8(C%T4b<}<sg]]1Q`X^^hh7D'rֵԵi.|,n,.e띛Vѵӵ-4) LjnnbfBxOuK.HЃ`k<7?rusCߞ[Ffe=jm JJQPȇڌ4 ϑU - 5 uLċ9^8gWPZw{,ih|ˬ?DE8)@GtPT T8|{S-...Լ y {).(fkZvrǿ k#0QppMVQ55J""90untC݌aadnrUU h#'ȲPA&K{}%Nw mpB4琘e`C:2n(FtݐlT\L5u=1X{DSWUzBrJD´?.  6] 3iRdc2R+>лcaZ^/F.\LôEcTXmi|g Au0iVL;"]rgvᦧdƕ;!:bA DAu1Sبe\ݹ/z]lt< E inc4zJ%;d(w:@or Cx8{A-+(5c(& QQIf@|IPLL?o!2?]({J3F!J\k@9Ou_EZ/,į1I`'6on Ӟuuˆ`Ddmmn,]ތ``  ~aXouaVQD~MRkqs +usNTZf 0tOvPق ïGg@6R?Rۖݖ]0PUXN ΍bOg7ȜV"=q5uT! `T8i,e%k3k3&'AE!! YAȥS:tLEԏ|9UX 6xcmƼO i8ŀW}}.N?~g3j k y:9[8[ '&JWg7ߌ=o`gw|~ߚ%v ͞g[< :@_0mE}qGdDJk 뮸7)aln 4L7qDGD=8PR/E A$ր1W4,(|6a?5̖LWaK$/'_Ç.7&a︚ӻ?0Wm|N@)EYu7,aT|A)tx }wfmj9o+9+Env0͹]MA h@6l|PܮRo_ۆ ZUvZ<9(_Qj'q.̉In&DC- ʶaSwLB@#{uu_r<}33#t~l?\8ׯ6L7!hgSMn0no.E2f8  _QrX\\\ʥA56g6lZllD275lhsyݖ^$|W"Ӧ7=QgcOr`sZI9=\P3~ \;8Ox)o w'sNk}6 yv2۾MI!#J.n",#![%oӈVkh%(ƃEJ,V8*+B;a Pr.r.,8=[ _KhK:#'`PhP pOG+&j. noOq22ڛ))Yg} vB fada(t'>n)  F?Yǝq Yu<߻j=-$Ίnm6L*kPo{>ڷ}0b:ftudߺ>ڿ koW:P2FU~<0py~<j=˃<ΛQ=?,Ƌ^~ĵOJGŊ:g͡n)Ǫ J]⮛ONt[Ԇq[;*Ǩ+%`Z0 d3u||/ S  ?n7_]]䳫O00θ`WV29*~j;5QCCHda4)n O m'v Aj5R . l>Vz*[f~l<`iV#*[*) : <1؅ILP5I7]V"*i\HbWkӀXXv`` ?&"Qs:a T ,^' *>ʍP} m}m~IO{O7P&2 .$vA~jZj(ߍ PWQΓ_w&^I˴ma:`Jv+ιxO) -WaN>QӉ"hpTS2Ԡ ]y}8S}vשwuSd7o n c̘7K|62ɦZFN**mtgEW:g;zˮO Wif UaJv!u #g>9K [Ct(fӲCP'Ӯ]GU\RA j"=gpJ&"a{ޅZs+nuDڨjPo%}y;qwJ.t\ߺR NztJ>+{1̡c&j>9VV]^; ebq%5F>^BZV.1ȋPf_5:4}nŨQڔ1XIVƋ3 -}M<"3טѺsb;*_뉵{q̃?\ryؗ\g0qSUSlz^%gmj-lKsO??2 KfҚLF3J c`d$?PLI]fh܃RH ܭECN ADRxZ=PL+k![rWrW*! `uqA/`DSVNy4z5듾* D<}I?.L i8 0W6W$ ~`ګvJ.gZSGBobHpB{l} լzMkG*+d<1Uo F2d*^(%bA0Qע)\kF<#ͳDGa}0MvVwoo#5`ҸzUTTq'Ԡ@6eUU!GgakbY$e= (Q5}y۪l>|މ [Mqυ2XyiS>Ґ1w B j̇㓂OH#Ghc%M]Dks[gB j%o9GW?Y)Sd&UKGY#$O8cH 5v\c_oa]9w]T񡒣兝DmPڿqVIH5,x8W:Anr@fsژX)ڒ-XĻʚRcڴ0InVAFzk{2[(z#2G@<+N t<<ftگ#Qy)EHWHO_2ghnOucS*NT{/;( >蔔;ApB-U7V^V^[hhJHP}_X_-?~J JRŰB jn+\tQ;;[^Vi+]S^XɊ,˖KZ ӢnN3ykޭ&gecdr?'E+C^m 5yY{Dw&u\;bu 7CϿCޗ4ZM\N1Aɠl+Qk ِ.Α?;CCvZ%+LM!-8Q7F>Ā;j8n˞0Yʚ5kZlYD=CCdMa"K YIdN~i~۝y94E11dcQ 8O+D&''XDP~B2`/ 4}/CI(wrRܮȈ`"ijuA C ui1iܮܮ3Qvdd5#BF91d}=χaV5ّV`(ӅDlt=TT{--G(D_HI{3na|NMDb\jZFnBTTD؄gʢDbȉ(Z6g""OGN;rynmsV9oxa>oZu6ēĐ=:P\Nc/]Z\شִ6 xC8A 3g,Qa8FL:柛<7qVxBf +ah'FP80022Έ4g66@xo3 7 l>g=i -1zL 5Oџyս33~o;?DDldh}h=)[MƍZ\:=:=oFn+X#Enq\O<5$fw#ЍΧkv(5`2;*yFӾ}Y-ߡKJćNʚ.'נa/>j1fR -YT܅k?cbLMQ:\g!PBVAEDZW}״igÍVMn5r+2QoAtaϥCgPlp ׾ȈϿ1>ߣBg:oWi:0F~6OCN즾aF st;>; Bc#}YRHQQ2)4yǮɗ::;91`To䋧$1(dmmU;7w.j5w`Prz{{R#Fo߈ Ukkx"^зEG̛^X>[W M+=Q'0B5r#V|2MjOሠB8 #\^LLlݵٰVzRR{NńzHg@/9τĬ VýԮ񕷇5j*1WԢ_SJ ,.VNO [ہmc 6@Z^ š٥YT4X2XR6D Ss Wހ s s@)' 3)`e7Fgnd0WWG L9L9/cd@{a66>hoW>Sh1[Z!GG[/}cJdJ, dQݓݓn'%'a3`tM1@3#77؊Pzz'f=E4jV~΋7^?YQrS}+C%$8#\yLnwmPk7Tc/E3贌4Լ k_gM4)x pО;>OV0sk}Knf+{'l);j&õ_]F;gb8鰼S U}pqٲjIؠۏh?`kƻ2+ZH #.?{ٓåB]:p ~6AG{?'<;+R_;/қ(N#hټcuQ\& `An7ުm$ajjzˠDDouc,_JٙLְC$D=80^|hmmh7z t|%~&z{8 z"٢Cv:l$<]GndH.r]OI_"LJr"B:!_~G;1F^XQXІjeo(ZF_Qi3_ޣG=2ϔkr- nօ [>*W}AwDPod0]lR2~1YYW䟿letRw,>lo6!}Ƅ{A뒩GSnNL7'ĵ^0Vv=>{hOO){P>> W\YDri;Y-ToR5KsɎ _k?W3|@мna%\+J}ο=m$Qǫh\uiW󾂖Ůk5T)VUԺ})\絒U=c/.Q5^;bA8(L3tgf/\ָ֮cghTY'Kuϯ< 5Zt^J$[Þ3Eb9UZ}ݜ)6w"Ul%߃kAb4f+M_RG#*Ut'u'ehE u&;3 `M699F7PrQ+(१qyU[P#--P>>?mo$>. \Y ґ̗? d\@ژۘ{Xf19Y%LXUm^Ɏ֎@LNYIDX9݉z-ʐbLؿk{Ti;Oj4ŋboiëjWIZʜITUL %u:WRdm[ۗ?:\w+~3[7ۻ=" ^`>Kp̉V   VNe&/_6@(($UVA} Ps xmtw+: >_Rja$>g5  JH&[/:c?[k<ܑ#`#\7T Y.VToXdȟߏU%ɺXO-l׾{mLg mpA&݊PB1n{dz(Ca~٤1El\RR{PVfkG_}=$)bvi?WtqclV enݢ$|4N9 b^\5?kVu/ˠCG2/idZ&4~4?pє(fVUwCג^6E r^S:;ipn/єԳ=ɌwB6ntI]E&II4z_Rc41>y:@Vo o+IAAߠ6o)o)3[.J#xB م@+;B9>Cs8<6!FgP10X4}[-irD1͂-; (yE95s>-ܮwmK= )iWww|Lffm0T} s ?hQiLZ8R~tvk;ӭPuAN@:::84"/#Q%!|x= NX #P®mN7BFJOAAy3$OzJW 3 9  ^5gwr+W8Y\m~~eD'ƍo xRϞkmPE.J@Ӈ\K"3gSFFΑlRrwf㗷*$!e3[ޚ#anC0c`$\ے١YVh8"yByËsޑѻz^pَz8QOJMi#xo G(UMHBll-{MIͪ 64q8+m/\õkӧϳAI$K9m ;Jx͔+NW4kGPmW W7S5t3 ̑S;@S,(TBsځv=~OOYN!H!ȇ;g,|u`8lџw kXXAÝgJA>N,cIuˡS ӏF ; j81eyV!2"ɘ!$ y(c/ֵ_oǺP^(oGG+V\Չ;T_\(rtǺQNrd"Myp5%ŮNZ2W\,Xj##t4z\I\IYy|jB vp,9Bhu8?Hcտdls{ ^* g0yH`~Ѧ!a"ٝN jVTX{0+PU-*Vim}]2([fP\OPBBbb=O=S/雝 y9<(g@S,,7'Ȁ@CV{.T)5$ \Π"7>Y((=W9uQZ:/@X$ Y%!:mOd\L4Måя|rZ2ќzW  Pr yi1A!ggf 6k3 " S+j=e\:&aWNL9M' "DVtkRbBUKq%gYXζgPѸ܅Xt}c*6f@Η*e_UT|ovְΛ۩pfLq/]gd @V JP3vidkG`֠yBox1 ӗLXY@Q[Q[v::`B[B[Jra@o!+.9k 5!N7 A+ UTjkKd,~fpiҔd2zzpG㈓y'o5X Y}}}L/۞9gj3>ү-%?Ԏ_,{= QvI(1)QO.33 gBWvӢ}=-:΂mi"Iia>Ċ)~t] =*.BQJLg6x*RW{ո'?e_pMHS귞I{iKA$ƛ ~]F)\?o :&j0 v[Z?pJqaX}KZ`">LôHg>Mg2qhP[svqC2na Lg6F[~L15T/ o\Nc%06T2KQw_Kƽ'О`@Yh{1mwxS RǎRY9/ kpz_ۋ \pߞG!MvY-(2!W!?-AfAQǓ659^4ʿ¿r;0w4h LPJ)@-O݄r5=Pg %fDd0Ǔ'9NZ[#𔥔'"."@szڀv(9(KGu|fk{_-ԍՍWTXXxLae*eQ_lUMB5XVaw",t>Ӭ!~lQh pM{s')اdV_e:^~*]j~ 1X1fc+ Cj ﴶCW/oe'ډNfz = %l&l&^@=aѓW"" *" 0҉xv,HwD0$yj"|2A!Zz-3,4Lm9}iG U=$4}*5ul#AC "g1+lSL jkYޚ;Ms(ajr܁2D97yLLȳi5lF+.o x xF4= #Πn\2nd8>)W/vX8<%As@^>@X߸/ȫTŌÌS J+蘟;֤C8|Eaʺ> O:&{xH{p&%×]o""=??2]55PG9r7bg;=yysF6<+-p:;35Z\zZE`\4?/ מD|HݢNym厣1{~pxr*m46J Im;__-pfƃb463z A@?4~8$4On {ff~ QE)^eOr=G9҇{лt)f+_%H և׭FgbñXXT&&ޫ9s_ZZ+Ǿ=nv7hP̫yEtgU}g՛@ShPhIKv#~E1GYG7:E}$`m&|*}*4nv|+ +\O.#bZvQ*O\;3;+V 0&^b/-Fgj4Lڄ臆*^j/>Ѝ/~7\E]V"D/iZ뀜b\JS q㹰َ?.H2 Ǵ\y8/N(fw` j/;)4i7LvOϩ'6A7f^QZ]f{'p渃isXL(~m-_N!B6#IlWef .1e>&>Ƃ~+FGR]e:C\G\H~[7^ k^18@,MeG`[/̉'Q AUw\,zz"eeĝzMDD[E[b5h5hPbPG(ͯd\E!79{:77&(BxxWS:Bu9:Z*{jo`qqEߣW #xezUvڣg;\?vû6U,^S 7rsXBJ -~GKMm1+;֎g6k/[fӃ^9iFbs{XL 5k@YiwL.w֝g5 +^Ҵw\F9a#i77 -b<6* ]-C֢+uL,}vWNsFF(Ko5{AN*˅NU9)"f"]:M7KMZ-2*6*6=4YK ӥDW2Gv>Iz/'8QQ&{B*O 6Q^m|;7_vITFبP3/?`{5R z¡|Y_XEpӑ>bݶd|+lPdlK^P ۿ[[=4ۣ;~<|W713:۵Pc+k=}yH9^Y}s{ϿCGyA%w _McA-+=W;5);X*e^;>_RK tjBRBAbQrnJNf^BYjQqf~nAQ(`Q0 F(`Q0 F(`Q0 F(`Q0 F(`Al !par2cmdline-turbo-1.4.0/tests/subdirdata-par2files-win.tar.gz000066400000000000000000011527651514221355600241650ustar00rootroot00000000000000eTk[BsNEADPT$E@JZAIQ@RP_rjYduKߘg7,d>0KNRD8tP]ѮQP5{-u tjݩ,]ɞ4Ss8nhA_H\:Ky\Y8W8߶ +=A7o1.'gj2 ؏40'TմBSϣݡԪtGϿѲʔt9vjb5FZڌY*mӃÕ;Z6~VꅌV*nXzg?#g"w-bש!KJ"niTrª;88WCâ<+q&Lˀ;jSy[rIro(>u j-WtϹ9H*rHQ2]gg{O)eΒ'_/@)φ iUnuZ]_y̓BW?/:a]1{;}$%>^>Kj ƝIYviӻCxi-k7l Ny޿̦T^|,2?wo楡DyP>~99yøX =raIFr^Rςm;ZywFn6m Wl(t$QOaGv ͊N9p1B$p){oR>W:'^_6&Ve Hc+A9oYU--3[(l>Y ?ah*Gݩ=IpLjJӉ߄U!USq5G72eG505p(,kg xu'-f)i 6ei5Yl8^ ױ<8kͅP1qeWC,c ^yV9M$>Poƙܓ9\jAJuœ CԱt+N3]~hws WDoEN\U?y_ر[dfwi~<&k׺'J)NǙh]'d;_[-60`e3=QT/ܾn4"ITn5 vz=n`?ѢfܤA;ZfeI].`j]pZDj}7[{S(q_ >VMDF赊qAdOG0|q%(U oJP 0DOwe->1 fl89X0ߟT#:l6lE$Bufbڨ}N#ۃO-`k =0c k1q~׳?s8eU J[x}l`DTaHTim|7R` DȣA1nL_;)Md " "s>r1Qo  On{ik%gH{(Ec^H]OIX&}2873B2L16!׺\Q6 0`?Jټ2L`(I [C'UN!ژ7!uS*DԄ+K$-in_03^W^+5QHJC `5r2M&s'J4}Wl6څ409;S햸_ >=ARd/cՃs~+ARo$V%g -ņQe.4iޚ^.- kƄ=҆;oK8LrnhZ`6g8A߬_ n*^|`U2 Bv|VtD;UVbxgE-(gEJtCƂ1 پ}m1Ɠ|!y *d\zO{A856hZXԺ9h OYoCIˑA:I e0ypدcoXNƌ!ъ-kK]aHM0v^ ,GL2n+CQ0vzijܡYV9%G@Ӎ~? FFN%S1@o#<}Ye P8n0`B n0ʙ +Mק)L>ph#vlPa[dKK4=tn#o^) 2,^mwH*x L %WSd.8~ZT u2f-k%#NV!CZp T( -:6r4vYYsMR5|AMPV[yyOPpC0Tf\ 2rd,ԳD;):&jҳNt-eu}\N|E{ vt_ yXx h@SYt 4wFd;7<_X|sX\e4&)/K`_ `b CR5m *̨ 7 I] J/*vm81.CɄ>P.Bp %-<2ȭ} 6O}[Y]δvm1ZgS wvAj[rQ |]&Rb6Nu1Z&-Ta5,G陻&[񛥜@{ll_2@!8.r&FXn12ߍ0%DqpGy-r\'Ҵ`m v8NW 3. g2SH S_VE9j gy&o]KiE5.:/z*棪YinIJ {T` a~LAB܈Nm c C n.Q_Ɔp2 #b0uqzbv} K%K:q%1TՖO{8rR$@PW#iJR܁F-\a9 ?aIWt OviӻCxi-k7lBJ5̸{[4z8W8?+6ؐF{؜~ibm)%J8LC99,sLn܈<) msZ3 OV˯լ#JA_yC&[U˭)'3== l{X`ͱw~9;oyxa|n>/? rC&|;g]\!q{ݿO׿;RdK:}Hf[zV13qS2Z{wPjjϡ$###KpC/ؗh_r/3۟ϑ= ۑKd쿅;0 #)#_I966Xor;ϟ7e;pqי 3!q}>t:L{ՙ3x笴~*gEW#_cm~N$5]ن3|[ݭ*\tySt:|[~ZQ`Phdoo I1`+Wu~կE祘!;`+ۘ<ϜpmeM!L)F5_)>`~ |DE!6QyMה!z5A E]Hs~E˶ʒ(':VDNWp3fUk}tI ;=a ]Z#:7rnb嗲䄑$SD<\ekA Q:'w\Y2( Ѭ%SI[x)in!Az)XPaف  8~Rt*фϰdf.l84_+./ _xD٪)|\}$͢C,: sqO02+Aсz1%'͔Ԍ"r3 ^Z+^鈵=̹1#sEsZ6AF/-1nfښӉYL`eKwny<9:Y3~jiAEw&8Sa0X-mOW*Iw 9"oȍ MZ^\,c6^5kW~EdjZMʙUT9(C#2fC{wg8K[(N9XLUy2߫ ߖJ}+ɬQuOG3i-m2מO ܬp.dPB'1YD<&g74籮FgsT1tBa2O3%.=+/#V*4]m  Œ Hɰ9L4ة-2K{oX͋F&o鎼L~$l ~K)L}vӣ4ZV.-{, h4=qNFVZ ?5IK2,:fչH[1o,$LFُ9%P ,3 K3޲7x@T~i Ws=XC,4.XB6*~@ٛt;#%Ji y=&"#Z¼IcAdO~q`C$mD1offfz޲ܭaEjk/یj}PKo 'a ]zE%V.# keIl3'asq^a|m[{O32Š_XȪXl&aD~ yt`o+yDYֽo4<&d2$|2=WRS0`̇b8Nq?4C{i#uǵ*ǂ(`lj_\%0t=Ji$R6Ez:0syJ0D3To$ g_9F1F\826XW.>`:Ri5h%/c Q] Vdz^nPČgnN효o5=A&RI<|nRmo#^${ j, 0M2Vr[# k;ėgKV4uF/m1"hF5ʬF{ۂHg+G+aݸ#Lnbz Y>bɑ脋}ꑐ`?f:hޭ%Bi<(_0iK(V Eeo 1(Ow&q Đ,&WU}t  l]f.nuHz.eY5%-{;0-{I&fKp&XIF ъnMI9Hs|@.0`?Ƣpxt[{('u 0X4l?I*9|LvOFm@CFMdqYq9 0R`=^*1 v8{$($:Ma=OCc'.`@|Fε#GWT(W~U@i?SqF}/;S)nUܼRxK(%ok&D.V8~ 43Wͩ%Z:)GMLjߧ0 c=W\ֿpcll6>1V"-2O,ܙIє7 X]ʹ }VL7];0`,pk=Nq-9W07<5X7BYo"/\O|Jud56zLx~ZZLE+\5\Opv<{  iGc KYpn{F@,N>A Mgq ~,-=ڛHvzO}$9 C8Gי=HB\BnR_x.;{c$αd׵3'[a[ྣ̚k, DZkc+ӭxOru oYmǟsLOyc . _pj3l4%{ M?gUQ=&H h ̕m+d 2(z =.p2iْ,,7} !l^$\Pk;UC~*S˨Ci#=~׹`ky`V%(/&`)&R J%1{z~=+~L({ M$og}:?.Tќ%9Teپpw_d`ͮ g%3䘩=y#!ErGmYa#}umKDbP>RdO>s/<4:ͱuѰE l(NTR[gr3n:bk^`1W4o-L?8fK]kg]Z/L)V(33UooT=Mټq~sm&ۀEΊhO'+sZ2\,:?C^' jB"_ mJ/> *D>'}T8r1V+kxdkA/9#; > 3VӲƨMl4%)RK>%!t|c`eP;-<ߚRTleH.,8QƔ[?!Ac/z"Q|)\uS{Yق)6id_j){^III&hƇ֬QvN!YrRYk!q>+lg5K[*\쒨%2 go "=l%8&h|gz:G} ?a; =j:N6}h*}.Q:~=;3XOzQ0&]S߇ )"xlۚOZUREP  oofQ? I0*xV҂j)^TKO'ٗqqB6ЃA " KxWv,e,1JM|rۯ67},8y*AU` 8vb|'霾 4,}S?VFBI(}3KHFd YJ$u$e!Ou>~W?\׹Ϲ}h|'X9ifئHx1NKCg^}f ^yO\Uc̬!:ac@БXï%ؽu(sedfJ'*!ڐ45}qQ%m#꾠^fz{6Nę{pz.I`͏8 ФVm%#Q;@›q՗-s?ԥjtD6Ŋ۞Z<q]q)h$?G9_?t[9U3sV&;rDmLzM}ɝ%%G tofIFf_.54>4~~;Üsw{SpnS Gx>Ev EU g''D_8 Z_00bkB+ź |{Յ3|물y*wMO c{ŋ>u].$y/ڴ 9+3\œ}{Յ֖"Es"}xwX!{9W{x$]5D^ۧZilc4\kX3fCR~L|HM).YcUѾ:¢4Y</Z\/\]A5MJ*~KoLM)obwyxFH׶ˆϽW v~H3!J AۨgsP^q%0+9hK6F)AYKx(R*Lְ 0df‹,Ns)/P"ӚLSaٺL!=Fh]gfMW-)B#ւp 8MXDN p Ͱ┌e/8G+VXä?}d5Gl˿=?ƏOt= Xh#E6uBԋARFv6^6جt3r9灓>J9,1e-ɡ3g)   *Y֋2 S23Ϙ q&՛-vU?ו4 cscG?B30^I>:sjN'敳F!-7v܉̫/p vY+~AҚJ^#8S,0,9lOjΪُDgNh+3l%~},߰ɭݍث8?"Q&m-e!ME #j| яxQu8S[(BN8-f#k=zFln{},"ފim6L&gTBAƗ=0cfuuze^z>k@jgrOsqӕDv N~ou@E*ʕ!8k@ ۰;Ŏ]u!P*S~TƖi.nhC3>u|oUy}'ioV@MM>a3x]PnG_073^[%R~ Vzy`SǸ@ZؠG©~ѵ?WF4mIM"U&ֈD+l崤h=1I8nak%Z6$)ʙ!k5t&02Ksre 9Cw1"Sy]ڜ`BǾhr؏GD^1X7i-).8n#wdx=)>.ߌ1OX%gA od89,؆)UF\t.f[cڲKG3`G2?BauZ &m-s<ث6ؖ @VQӧp [̡x 6*j`48 ۪\!|ѡ -bdwESC7 Aio{|\vU2I5 Y^itͳ !W0m>Be\RAk09"&NVe{lZ KIhs:kژW'Jˋzђt+OltFAT3X|j/':JV"MGm$jil3i1${A3Yzx<@rkޙt:?2a!6Yi؆ު^. KD't mɋwuS[d-_~aI JW-tүhUMxwĨ17ܭ:o*(Me%VdyHwVkm$Ak=$*TϖH 0$o؏17x';c {3(UKxu\} wqC6iX+jZ^@UY`MEǑAPL1Ц^k֥21(to]I1C]pHsB`10`?&c59uqWlN:"K9(hgi د`OR*`Qxh1m1 WzWdGYҚ s'nJ@b3i$K;VB->Duc7t9Fzl@ G "zOȟQH(L1[*cl/Ka|%ݐ/m+\`?*];2kߨ%[:B4OOɧ  cCA*sg^;ZO2sEwSu񘬈qבE[ Yɔ"j$˪UZ@yxܣ${WC}"^R{WCl 2}MF)qLrIPX-qWU̡>zqq5?J,Tk6̕|q:Mէ C|L/1k+XǤe^'>Ό%}m!˱fSc;*T3[v%r1)JI3ُOyŰ潼"Lsf+f8)?4IeH4c( ~>Ci<7J5i* R{rz=/)ӿyIME 0`m  י JUᲖ'b-=m04Hр/,nB*QT mZ?16DClJv)skk!m\ѽ [^\l\g!K5{ZʚTT- ݇겨s&10`߲}s,o8"Ӿ )?њRz$ŲwϸxLݸ Ϛ/P* S+=Wu=DkAa6ʧ5TT"fΦ@n05tBj-do{|إ*u5S[\ǨRhZ gEl 2# R8 MP*4wAUw;_BARI )IEAR i7H -!tHH wuq˝yQ5>Y{0goзʯGu|=5;tgtt Œt[O>5~u\mplQ` >=5nUpV NϸEO|cTAsc }k i&sЏ"k:sⳢIafq `&ArCf% [+:A$+m1~uXe L1` `d|öA{6-u9 m܄ O;4u᭣(q:NM=qW=%Jkc3j>w0` #OY"/Q5}8vvF]-J*iuk`o3^E [+0wʧ}F@AX :X*~a}YآRB>9 SrW0V'ǥ-{vSkHI E'䃛ly\$C#a}BT0>KpDžʑÛ > ~d4`뢉py鞙3xJDD#00`Tu܌/t( OJ$ 3b{̚o3ebB^ЦnM^hXCSjc9pVɆ:?R&Y}Fiح%c93ο]f)q=xU1:~>~{9g='=F^?? AU͏zZ˙bD>n&ǕjgFS?wwUfٚ!cDu4iI^63?&[+>[ͫc+=ss3SHAo=EE]whON~z#rSc?ﴳ4!'bg|U1 GKkNQ6M'>0h0h}n%66O8Ox߇""c]YN5~Ltw0 O8B|*T@HwOX( 'ttǡ.aw*}?eUI,OM<<U9NREwX j41DI.1/YnqPMfX }ZZ6ka5*Z~s'v;kNdv=4dpo7k}, SCVf(05exQ{Ҡ{[cTs惭b>RCJxFF"YI>o0'K>(5-`GFfR;~bUSVh&!?ch`ۥjwr6AH)Sk_ Z䠷c]6'X^)@ Qa*ֺGy7?KvԬcY{bQb/k}$G~A)7b ک3r#|uE¹Snu^Kd4xrs{*UA~#u14rbAe'Hl uxogW*֭Q^lncgVï?}V+ctn=u&uz^~6&N~IH[`mqSIR6v JiKV]9*g`!L85?y>_" YbK)=;VK2b"ß&I\7R7j$lp@+ɴNq+D}{`a_B|!qr>ƽa{`e`[ṅIfNn.Ҽb9-C#U$֣4*'2K5' I>Ẃzd( ]^vX_5x6*6X@,ʋ~D  D:k-;jTdDҎiF@}gvVP:'EʓOu=A޽Snlì7:NP^-rf^Z]N`(Qv`/|/+V.()Fxp;.b|=Ot͛/SÝ"B+X?,Mca\W4cΫ1"+5%i%Y5`%<S=GzT8m\GYY񭝐1.x ȴwvw&XhIѮss#%=5]4(YRf65{ђyp{ûðu+g:ƕ QX0Y9ٮۨ* Bʫj)v@&˛zk۷`vevQ}bꩰ lg}9t z'x}$S@{z+%N0&EׅT|-56{8Z4O}E>C:^PFE' i,4{flߘ%]`GTt3h?ZɾŘP;3!WUq3(e9VKći@ eͺ">RT16&z4$ د:at'GshwBF|hl9Fg6MJhNZa"W+.Y^A}1Yј%m]ھX|_¸5WŢ[6C ~ ?wk,X]9c[i Q#4c.*[?8LѱEw0OG; H!. P)G 1\xZn?W T9-$Ss50v ߱?Wn~!3P7!\o&fq8\ ~-GMvtdH*#6abU*}rYΞr{QϳbKj- %|f o yu.8XU0f=9֌Uc'"CFF g:HUa-h~o|m e诘LXagYq͹ݘGf<$-V&(6"޾[B~ {Mx[OC]0`G[GI^Bs6kz]O({K:hPW}Z2EGfr0Y~WZ~ %[t~a.~'.K'RLb$owAQ#-1?f4VK¯( E|'_^}c~{;:1,w$r;?] m@PUCٲmcYCYcQd-=;ٷlc'ȒWsqX*PD۽B+O!4[G8 %PA'fC N 1ۤih毿 +Vzn"0X-l:rZ-k7"DžěOӾW3J3(I; ` `R>EڜM&ß+# q3F+.ULk0n yn֋ [ qgNUU"4#!/ joκ;pH%h5$E(n~F,\=u0Fx!/Q5}KS"-VZO&7Y4ͺ%`{Re#VwB:a CkГUs%ed1rqWSy&F1w(#MjbH#6GW #ShB;΃mb;sv)v #E+~{#rUz/cų4-G82M )=ee i*nE/@JGgRʤ? Hc8fq97 Gw~pZS*Q(іɴDZƍ~f'XYvXm3*H;z'|?,a&JFHm 4˯}/lRsaB׷U[q }qӊNzܯ~c2s䵡 ~,!wy/T^5;D"Eާѯj^MCoTk1iKwNp{ϯ& F p') 'eojo!ν&s?]߾qx@P(v  チ \]vv;10]!~ΕmVے@H2MmϹ%KcGxB!G_a56_>{{T3^)`-`nzwxw%^@1J3;*8wamh:0}NnO{/yy{Ap#-ɬl?^SWЇ#xI? |SJ~/?2)|j-c  KpAVO[[ǃڲ !-#-[{^zl&x&8qoӡ!MMZh1$^cgXg|P-~j tI8QLQhG}΢#e>׺WyOqsTAnZa}Pa/%&T۶XoKrCWv 5 SFƺe._=U] ,ySDG_)R.k!F=a n,KW4JC-ۭLOhqPå;Ɣ7Q/-Mv1-(+Pnޫk9=YH nS]})ŠYq}ly!0`] G8>Ԃ K6N)BaQ؍I)2C5k!`q,}M!F%SseJ@Jp3 fNʍe;#.'cȥ#_.<$9M)[g/mpFGa3?8˜ ju&҆OE& 2#埞> 1G?̔{ }o654o :b *>-n `{ 04J.4$cH1E+ -3)2PCQ`DP2GoƖ5!3\~;߬rRJOsnC#~99ĺpXi5ёE_RxnI)gs 0ԲM]cDs6I+nŠJ^=B,aaYp\X/*3`7> wa$Q`/ ̱ O߲XZzb*0j&>0t䏍6y*Ԛy\yBx#^s{m8s[(W9./׹0;ܖױ  %ʂ^M\ ,+[3,7'lTQSg"d%]NSV19fv.e듹y3jĒ4DK/D"|"nia^ޘ///($% `5:RiGfk['cKǯPïq[hb%ptvKķΊVaH`䩧\g0Df\r"=΀NdEmul/OgU=s:~J<6AN-p2/N>޸'<΀T:0Zs3?}!j-7n{hy`rk 6#j̲r~->OuR[|D2~IL"ђX&<s] tc6eLCUٜvm@C&>Z/1ym0 QZH;GiWXe دc.<[[ލ̿ra2buq֖B_&H:FhB1r2Ӳ]ήy굉>a$=WC>?bmӍm+lU=74mn%`93s:/wOڲ߽JjmAl2c#8&D(,W!5zdH/pP>0Pg%*M[fqMfy[]g* `]3Ax,49VNvu,g[ ϏPIo׶#VIz +e6PK\{ݠN0qBC~? +J W')4a .ۀ4x`zgs^C5[$KuorZ|o_`#vO!9r?{c$miDm?Y*Lƒgb2O\=N2p4n*gKwK|; +k%9UƒmAu0Ѓdpb_mrL…YùhF N+]t~?W jʰygЙ"MYX5{)fU`e.컱8U x R)h& ʳU& Z֨늸9jAѲګ P]c=w*p|C`_DzV7v]+K*AQpg|ߏz;)2\lnn􌋝 k2^#/v%{jsщ䩍BRSV\4n}$ˬ}^(;omi[!fv;gؿ4S]grT"(A蘭˱sڴ>D岨W'ڼ.@j<=sfsnamad"E b*{(~pgv oslүb.se_ [SC1! fl[o\ 3~ܨf  }HKS:VI0b6 iQ9O,DYy$Mm6H$G,r L.2pKП6 D.>Os=|iM\Ȣru$ALP۳y09Ųv;hQЊU.K & xaWI {;F OLFo08^UL> ՛PhcR= HŃ@lg)ݶu2tt2C,nU7Ļ|H5PT59R]Kh5d :hΊR|;&[;9~ 4*PpvRbO!Oc0GKsvWx\ihwiqB.PC aVDŪ&\ݯB]+ }OH)Wc'F[П+!헓=)wiM̰^ c F C'DO6"pxb΃ Rȇ]s1V#%`m ;zlu3D$̟ns9b@*40`JSq6*.x R L(i(+ë%" g<Y] i'ZN#,&75%Ⳛfzܯɫ `U#̾LB5NN Agܭh4^p*-V3@̓~v<ء^tx&ڭa?*Cm?aswӎJzޫvu$9 ^L܁Kkyr+ 4-GW#?3+{l*!u:B%^bjq1g MW߰ms0vxՖwvM!ԛYX3,\})s:qW$AMgTC˶wy;]`ՎvX]{2s֓c=&݌`䖗`JB6Iͻ{3;߾-GῩ] ᲽtiVjU\zLܱ -/ͧ˿p`M mԳ&ݰw NZe7QײDPZĠ]]6d:DEW(g]/+ϸ2%vج]il~|lo\Rhb`*Cb@ aog2!a`n"aRgŬQҒtfF<9?q~114ݔԌ"j5em I-宪׺F^luf_cǵ¢:+0[7r7L5v<w=;:^+f’RJ:Ep aZ['T+ٌ E+2/I ON{M"ρ9-V*d7G/X4F>[K9m$o5^+kA:֠bUfC90sP/cX `7o>Ws ry,2`AY#b[ o=0fuEuȼze eߺVv4M8Wkʞԕ9r O!rlkHе%N~oIE( Q1 ~EEICB.Z`6\lDښi1NpC{MжG#Ә~{ۙjpޛL"!wXk/tv43eG[ג61 Ҍ].{EռV`-Ȱ% 7 Žúh&7f ύhG5؀.{ohxTT`tA iU~{OQ]oИvB]*.k-p:`jDJC'oheqXLsm'3ƜȆ.a0Yb"n5(iq{ûðuf̌{D #7D$xծ j7?LvU5VwBzw{'Y;-J;@G+[쯷FxO0сr팪*Ũ>wdxl&QdA8Q*`>恉Omo!dgH},V@G7-0AƄ?#~yCn7ܱ9Q `Ӕ lxs9H`rIڑXf=|rT>0s}ƀ0ᾤxG$MMV6 B ƸqrGr,0qWʤK5NOU+RIe|XrUPM 3#mb^54{Z (W,l긣v['9he4VXj>n6 +uU  T_bL*ղN1dÊ*1A c9LhEejĵP c I{\z:mzk!(PfI)`|57Ϡ^ -|gM(^:ʱz 1hWg&)O gQ* $ Ԓ|u= f: k 6.As)s-)i}f'QqOuhB:} =!Zsw{ ԕ/n쳖| qWd"n1.*ɸ2 ¾}^Fv|LK:ZF`]w ςe,).4LJsA41Mh(&]&?I҆'zkJdW?`!DbAdj|jKaqYo"cYv1SUұV@¸7Cn`ָݐ7C<_g"fx`s͢zuF TP#tܴbM~/j` T? ͼd092Y #D,ltq4O^'e F>6X!6YOuH>a%q͙h8QbcS~I"QRePMt]"HHH4( Hi) R *  H Jè93[]v̬qukݢԄr':wX'[v'YR\u!͓:Q@̲-7'&jN_*TNLRX_ }Z2IGnj(Ma%a$7 q 8[~n$#~hR_&yB$dЗŘR,V+o%ׂH4{eBs{ /'A?= f_C7q[@7:{b3VU1lE<a*VV7!9bxA&8OKg(;l/_6;2bYНK;ݚe*7j0vN)t%u^;=w/N(d=xӥU$8_YIc*MbM7|} P}Q=WqQU#\}C4 La:m^FZϚU q0fU(K}Tͬ׳eEG"_om-/?U֯yTq_Wbsc[0V2 S&H 04 rCEFIzkj=/|Yޢ}Y%`~|# 3Ze}F yd@%^`o T h50.:وk%XŴ/AqEZ?clIziwٻsA4YY3Cjmi; P)o8M;BpgJ‚R2f}({DbiL)< :];ag Mj̝=lϺxc+] ofgjJwJna3~^St ! y:!Mqnس ~g'f'1-'۳¯f8`)`l"(lDQ2Jr?*L~g%Z:fX_2}ЅM?.D 3Vj?o*}ud H?NU 3 g3n9C')L={C(r)9!tK` j* }(՘`d Jv&i>0i#+XPǠBޖ`_V^ĭ% { N:J?EE\ǯR_^N؂ d.gU!n7''UWq_9 &O%n4N*uolB粇00%9V.qln{Oih5ӺxW^l߄l>Ubpu0kuṡ66bV/G'A_WB3//mL N?d„숃z/nr 1p i}9fۤ*g(HqyX~-'>xc::xӝG]㏌aOQ?sCgD0-(: 45WW3Hƍ#bsZ\GOAXJ=C*aᖄ5T{d>u݀Um32pZ]:x!31[RD9=xu1oک7__Nw2g3PJ5Vn|^\.䵣2qͿ ~6׋;p.07\!>w=rKQwFS?Ṗ_^(]c<_cG刳)8(2|ȟG3\c,F$6+mM!mKCL,Yf.!>j {6GnwS`P;]p9АM;=(mx"^CWa.4,c:ǻ淔D1ig͆Z%7}d BhPY#ZtNW,{|!'GQR;)Tq鍩k0dמQlzn>-0\-0IJv ՃR!d(`wi( ZĐC]6/d^1ˮ8cⴜu rlǮOh%Ci;rU^t (?|\=t` tA!':~Fdڼ|IU"\H,8%0O~[ǻ}9RIfՄ9LR]=~X٪[fAIj0!_0 kNGos3M/xԙ(g2iGad$q*Uy<QFY4SR3EHxj^\Yz+i3X\~ef]9|Gq7T27F!6uv@4lzm)n2 jע/u5(u[Eג7ߦڎt?YSj0`k3dXL#Ǿ\}8il1(ĥl`e\qfXZ Mx8Ie/(U9K6wI<ldG4,ͩKV J>\B50"ݝIvJXk$"#X‰¼GcAdGeK0Hsp{?孲݋1ˉ_'e?k9\1c:љ7LtY;E\Qi/#w +~-Ɗ<ྍ0[/fvZu@w7_n_ˏxw ؾzw ]VU'wݐ$64Л>A'z m].h!gHKl(i}#ݶZH}%!, :#ΆNG&: ;\mAI د`OYyO>D`rNƙXf=|zT!0cmր0D3gTod ^IGgƸ>=VWZ#9OJC د`K[:H, $7!SJhnSɮ)~Y0[JW?ϖ;<Z>ŘTUY 3V,k@ cժ<>rr)Kt :[aHHFI)gu~ZgQZ:1,|gK(Q Aeαy ǘ|;+kDžHIøv$y5v!pC4iX\м1~>$8<ך({;Qkv{~-˼|lX&jS{Ty+u  L~Owp\KcHJd, "k}>F F E>&eV#]AӊL;Be( uɧ . H/v"k28$:M&?IҎ7Jk6}=3{wcLAyZ)ɿ8 "xN\q˙/2GfSDUfIǖ+J} 6ti W|Ilp<5+w`ּWOr¦҄C6;Cai[}ko=Ma6]$#H,Qٔ:lba*o Fc.~cGLc1aje;x;lǎl$kEm+Ɨo%CH Z /n]ٻVs5 lrFhLq[-sW铖 C&q:ONq~ ؟gEjM([z`ᆪ?_+#{2*kddfd+!2eEdD"d>~}_}|up9qXm {&bAhym6VD `m8uTg mO7)}NQ 6Zzg:2SJӯZӘَ'Lhyh 3 3iHO1PKpԍ{esֵ>[0D3I[K++IX5^{F I"gsǐ݃K&iKO $ѲmP0o. :ȨS n!Ch!6/R]{)q~`sIs{A1ragO[`|z[\/O7F7g;0:yU۱K$?*2†gy؟gz8u֟|[<g_2+ `cP23\X̲%UDs(FC7/9lp_, Se4ڝ>"`eu:;.AC+[ J\׫'tQ  }Qς -Ҏ69(h֋ \UJl uyV"dC:1@~nt$wO}=Tω%lyTepWod`n e0䘨NK2=dgUwe.vkL`H^XM@zL ÑS? *VnyX#{Stki#=d}by1:}9ZJߚOӸ(k0`xy愵gB*S-e讗M*^"O+,DEOV~N_2o5-\RȭWEO[_Xlj_&I1yYJK0 At<#`~d=fɛ(m0ZYE#ҹ )>R{%F]=f ~̚S"0`?8 y򅳔>>O%("56*1nZGnT%`˾H9F7g܅`BB*+-8ĩ&(]X&?*Z>u=ƹqՊJzҭr}2{C!1Trdr#ܩz|X.?.i*5;}&!OV+õ5!u%?z&v_Mii .[[ؚк8:YrqA@;}w;cscgsWB#7 pprqBh9.]vr6t89ھ}(}j,Uk/Zd+2g% cgoR-||8R(1O%y{M}- ;r|܍}?*%%ǾZ?9MM-yr%v֊L^Cw6mlqnj2%pjjSOSOPHff[?(u6L{|IT oXl>S_NۍKRP-O$$<[nU_hr~p% gyxz|_~՞~BTN㙮[g:CT^ӅJYW]kڍ.]az îv}[43&-bޒzJSuBI.Ht;TMnRN˻`jYCVXʒBZCmXc7IFEhj>2&_ Y#UsFdg<]#GJ{l]'d3yΓ$]-M1o_^o|L'4lQ>uu8ͬM4: cy~ B/䋁`fan]=10|_:scb-WgC$Ex'?ҽl1%M-Y$)iq#.T P"NqONwLkXo/XovgAt0}h~/*Kw֘hvG,Fvް5wC sXuG‰56F@' ܑ&;P^rqU !}7Yxjmu;v}+|ѲkHn҉ lSş~9kI1iŊV?D2T_z8ih;ErhB50IJ8LӜl$6SQY3$f/^ AvJm ܔ MZߢ;Bt́)]DMK,w#%Lǯ2׆LO)Wߓx'}&ۮ#ZE:NpBSf#syY\?q{q~a=ZWW 2.OdȪhٓlADai!V oxi"dK~(QU! 'TS@V` %=Nq/)O0Kү٧߹:nj0`g-_y_7p>Ea(\6C" syF0D3jӄTsoD#MxGB29R[V%6Z@BM A0g:iuhze寧bGP^g#Rõ\nWH`jK했w%-~&$]:Cǽ}$ڞH'=3=&QBhaSSɶ>~Q 󬎏4ߚh5hZ(ŐDymZcŭHw4DahDZ޺/DjnzY1bxj2``"j0͇D3(-pXw@|Q[s`9Z@|jH)Sp/vS aG~E,^Pܦ،t;:gX';6ֹ+8z5@$=ݵECf̜P\-i|7-!G `}v0,ߛPqlULy:)Ar,k4LkV>_$,p܅j5֐E:Y۷>p tj=RJݥo6[H'}:f?X&dj*df[!CQ5+Z&>Z# Z?VxoYP}-C"g?r#=Bttc P@twOz>>y& +Oq U0.<#ɨg&C/!H51AQcc%`#p UƪiC?rLx0O!zS$0V_4 # `?L^k%yUƂC#%5XE*q~m_dzchfyj66A,Sxs"^Eڇ 8=įl5cD+B7v}aT74]XvdShhgt]:a^#Cf`0X$*S4ှ0k ws.6 ThD.5'A__N7//iRv?|  ^/u; uouR,]#QsBT0|\LMd دcBΧ ZCt1Q!3u"ңV|we0~=J/4DL0n^`}0qK䨬4?$(!35!5ѷ= 0~V'`ϽL9F?Q{?xh)hmwƹS4f+s3toնVvz_!u..crR#a]?? EW++N[ϝD)@G},^HQ{߱]*!vdV"Qw|aw-<>4FDPy'Ո$fMC,8& vCb|[X5ްĥv?YY!^)8:|r?IU'qRL!Oz3Ci6@ϭi7~;22sd5`Y'Ku3^jAičDXee}זwpo"6.6xalwl&> X XlN|۟nz0Dptt*Da5EƣzTlG iHR<xG\,&G0jy`*\vK]W,3{Q=*Z~w6XEPr4*'Yš醬UoARns `PU+Ll1?ϚǮ9E=qtY[҆À#M3 %TFwz3<-GWݧQ34}/Ej85pe.rTxg KaڎO a- R.+/m& *\F kQC Yz'-#IEizįOHqVL>^j5faƝ ̣x}Yr ϾvK;cE! ?1_ h `lNpy「Ir6-+%sV ް2*`eNŗ,Vq[]JW71`? x9us_b ‰RpI}J(`V;ĉyBLӽd!.}^ iB 8RU3E_C6,9T 6qqTF`tqFʡ5fH>*Ox Vb-4Wo`*Wĩy<*eӲ;R)^ӑprӶܧ)p 0eSH_nW_˪=Ľ[̕ _ܺ9gu?bԓщRO %^P{a`]#$Ša(tD4~Cs5=y6'-A6uXR&`80.'Y6b'6VK\3K>wE46U j YSUZtPzTZ/_S Uc0}3jBR%l0=j B;2//dII8=;d WYH'3φ@ 11tN(5?-|gɀ1mie:D(QҋZC;I.ˊN`-WG͆=]zB;r9No=eIE7љ>F%Lۺl!=Bmϻm $Yl|t)"Wtۋi(>"{ tt5q%OAO'YyK :Y`Zޯ I90[Fe%QH!Z5 qu f8)5 ވǘMF>5V[Q#1^DBM `?qJgH6T̼}1_:Ei|Hze$6δYS+_C⡯?g8@pc9}#R!6jNv͉~*%|upro-$m`.ݝP)I$C* "tvHHJl:%H)y=>z3罊o`f}&deȸGɂbj0c#IIç}w?~;i߼vuk\Bxeu%-g'-Y}]Հ:[=Ǻzjb("3zRvP >뚷0~MǿmszE8nح5.ݖ eXr 2Y1 (d)1({KuCL3i̫3}%Xp{YcHie1Sd}qxt)Glj3]Ę/vjސq3[d)͗/Hq8dbWBr`}/nɕͫD.{ ScV/GajaƬÄ/c-gnxB`φNL5H-8dʅ!uZ$؝_m]œ43wle9tU, 2h?g4rf >ϽO~Gp<saG!Qm>ҿ}J)XUxB'@'`3M7erXaqrP}s18q"9ѶΠ m(fJ9qBZbXdYdY.^M{;QΕ}}2nogk@˼=b;ͽj?WC8“JL%aNɽ{цiu?Ϧ)>ܞayĕ lOwuˌ(ooV&@0%aP~6D`%ysWLc9qiie?xR,{MwnևQ7vRHr7 _42tp}Nc9I˿5Bх`x-NnSgDZ%25'ՙSZyJV,?IE7ݍ1DrA^s&.Ъcm*K(|0v;=cUʠgTeoWY؏?3RPbm+ ;a崛Ժ77eCwFl74{yTy Qwe*'M8pxqѭD䕵]}lمeر[\ DfGezHU9>DUzHlo &_%,>o}_d{i J}2qc ґϗh[U_s0~|+$Qm'Op\xZBތ'$yDTZɷ5jF* fcn sto! }ۗ)`VWVzy[I3-_TűfIyoGyKo`߲]L~kw/)UbtIx*IcAkY ~^5l$P.6o]Xtܜu#}\[]/))iz-Q;u]Gv Z&W欂GğRf>Fu5Z:U SqsV;Mlgb ۆ+>7)uTK;OY iw R B`S]šW[]xԹV7H|1~?;_zziSrF$,u0DR$y!46YG`~m=ۖQ ՘($$@)棲9$H,#pq0&ͨ!mM֚3t[+!] *˸5D6 !0oK|h&0}9jDU+b~Rǀ$^3.  u-#X1=[17hXQai7+&ۺh{$q z'?Ai@agidm fB+jհ̹{^Z6Gx>'l 6bVlGO88dl؀)'B k> 3 -_'O˳mmz2*^o Rď+WNr֏`}4q?ba =Ovrؐq@T-*`HS4 Lg V~"! ÌG7eNxUd7õY`Ζ J~xƘMX1B#aa1u]Ulg$>f&G^8!W*pq`[srtx} 1/ù-<ܭN|T. -ߚ*:~yYX_NA/?/i2?៟`؇^4(_u"!>wr.+qŸS-_Lts1g|䊞*3w_т\k$ Wf2^H^HN B/!mqmlcaġV^]U놭Q,Fϩ56 y/iDH;*;C?OUNֻkV}V}gr,Zxcx; u]Vܵ&8y3rXR ܾx2s`68yROET,gj$ ՂԒ"v;.qmUK7]|ŀ8smpN*^e5^Tiއ%GK_raI5kek-) &lpbW9blǕf;.J=:ITK,Q耝|CgtP &W_֔w'崙%Vf 7[ 2=^f? ?>sff=ø%jS/=2^!RrlV5},|GBz)j{90Xv2`'2n-F\~/0d׽h'O'DnƻDt\+Yb4PU ޙ .Ӡ.~hw ļFM. G3x6󚊤'"VvJeZxE֥896"͉ڮDS:vB|6h[5SӓJx?̢&R[nVq5-{us-Mە+73&L@/)6]`m0Xley0"S{-+m!/qqi+ X,+NMi9TlP\i+7P y?ܢdumņPVtfGINFJD3}%"#YøOcMH~y`$u,5wS؋ɛ,{#u'K wX}&{~Feon!elUVJHed Y{Έ[("y܏9<9?9^Exlz\ˇ)Hz!| [c2/0>B,sX*u^5F\8w`p _RU%7 s&+aIG5Y ?C#LGzl;I tI"O^P|@wv5 GIaOgFn#`'dꮏnZf`Z6ޞ5o*{D>G"RK`H`pZ)5g /P,s8OuWR܊>Bj.VO*] 5߰b~d\ Jo"M\5m6Vܣ/Ҧ\N0PD3 XyD4z=3 7u2:6,Sپ9f@{^Woи:(ńD}kV̽SM0U k9NV"cX}պ"-I9\Mk7 *A?Ih#.;X `k]Ƹ2%'{Z 6KűN\P鋋q `7mZRԲ9!k OQoCq-qd${S:Aw`T1N^yvr#UnN=*7@N+8#m:= N v ͑^."*m83RBF>Vƀ34y?ElNQF1~jӪOq]pJ^z^E HDA:ԅIõF3Hfly]$D%teFhYw}e)ю% D0Rfaw#uyz^խV -|{ɐog1#4-q(皏%runΔbΞ\{ֻg?eD8lP'&Y@gbb+F.낔,,W;^º%]š5\LA2K`T׼?5S &#o>j^bTwf2@)Q<]p_ǞZ~0ZV S?D'SNECU~?+{J0<;yŗ s*w[EUn4 0 Na5h"ezUuߤ(hha:_gĹ@ڥ%'g6'ߟ ܴrc|!'mTٍ2R״wqIAw^qF۔}lUrugū{^$ż2)ձNG$!S)`}Dt_2#iI|3682ab,k׳4M_!֬R)<pţj ֠;Y;~UuԽ|]ZU7RtA۸Au͖R>RY㉾tJ2df{љjVĵ7M~&c Z=a`_s5yS5Clag-f&FX`?e8nXC"XH{T̍\צζF9Ss5ALw ؅Mڥ%n+W zl1[:rڤ(7 (Dž}%Z΄Po)ڛh$O?$UO%nI[SoD[A,q~m.Vy0t_Ɔ3,c0pK{}%a'Y*8N ">BYG9Ak@oı{7?D-ޢfB Q%z5[;^um,s:FS''DE4Ն5`M`vT˔ySSM!✰sPCF8(9yIJR A9ISrWVMOJqD\OnB.8ڬi$EtȜP=o{۬۴\%tb }8" OR~Gn/Z%~r9x&.ZA<U۞3g"}\af7#MMո [rt bX1Sç63 {waHZ#}A13kiHhw|0j90ў4zD mƓUt\Dbx׶ou6[,䛕U4=swYӉN*7@>>Vx ql(HC-H6?*sr|^>՘:oF)B|/h$=2,z.lNQw|O> b^k9cϽYOF$3ھ#?S <ޡk7c`54 }C"x?6Aogbx8.pn. ֨-ב*ϹAΏχ?]W/+<؜ ѳYo˜ʘz!Cb =UnSbSb LfF6&dcScq-?;w {ѩfV;!ks1$t00Ut^ej͂b?ž2G@hgf|CbGp0~] s/$Ps2I1sTݐwV` mRU%GS tP9~Ф™x.U.?gNW-rŌ)YX]ڕ![׳w-b˱!KX"BUOlqrQ~'Ey>NJq6VqKdd27yȏLKnxKb#tPLj~g>~^d!2q%\Q}<\j}W2MA#[UpČRqeXl !}ك}۷5č>Oe#W: cx[7Y4* nG ,ǙSC[>'I}Q.Q(z?I"/ێU6;^gFn =gN2DiM+ KQg.2v ŊN>_V!3`?>I+wO+"%R#3YXg)FZ3l>Y ?FܫMq}3y)ңMvp v)&SHt#7+ҿkPk!] rZ.ֻ8F\a,XY5b1Qt>,K˼s{wT|! ! AE$6!H(tJwI*% *!!  nC$=ιWAgzY%/q>v Qՠ\^&T9q8Ejbf2_zm1nEy2S0C}C^ՖE=̷ySm`˖WM[#_~˒U?$?59c" B>⏚7k9!\ ,h X:V&BQHey–7C{g!:nRvؐk劶IU+|4.;4HQ1VO粌ߒ.a:0Yl,v'9 E lE Asl Ξi?6 G=^_cqٮ㈪( 5b'0rc%F7ޏ[eés%mݕ G 0u!X˓h{UNm ff҂uj 'vmJ bmMQ/x 0rPrWoɣ"]G2tGhm jB5WzEi) 7KOvUvY 鼤%ǞcNwX34}.p֌{jVTga:(Z:m=c^2mGvXOdK#<AOJy;ViA%`  υs<'2 S׀sXpu՟NO?/Pum%۲DNՇ]{7c?;lVjW9*nt/,۔1npD:MӪF _ǰfVN D #PI2g%hU'@ X i侮٬F5:ɅJ%Y^4 `$b$URI3Tth8?km:IIڦG4Yiǧ&DE?FL+O6Dװs1iil>^g=О&gv_}q`{0\:occא$c~ugVO=33z|[`AK'XjT<½}`)'KdW#m帅YUPfUL}_b6,z٪`'KCUEE:Ǝ~2~rr AAuo D 9w #C^E.+[=?a\i;;+XL.+︣~X};D _?>k mB=a[JA>*?^Tf~b的oOɐ3?e=9`)U"mjxpa+M/Tps7uGQuQIc욳87RVtm- ҹH^4R swN;l@o7Bb2286V}]8,y^ e="2z?W21f'=z\@4QXiNy[YK&woRX$s/kѳ.ч`*a'͘;h. ߗôw)=;o:Oڹα{+\|NПu qAtT ϴkL׽}[\y4SX|Mv%`08P`%ѱDD% ЋJܦ;\V0pwoՎb5ZR9G] M\S:޳lDsɤrU#Pֵ&\)'$nigEAq~Rqfzk!-{ NdbI!K :z"a{~gų _ѥ23A5O?_չ; iy|] Z|icAI!+N<&.=B޵ވR 7]>qFQϾo6L@ut_dkԏmN2I~VZᠺ|_Nf@^H+`Gv8g8#M= %&ekpJ+*9 flJR3W4ZF!kp(9;&ߴc]z4j ttI6yºwi!汅 ' #$F">.|P'qՊdh{rK@I~ (80[Ac5 .۹jOpo5T:Rtr)ݔITݧy3v1+c}y^o7Bf@.}&1+|_?֍9tБ8M>4ήJ%Ð XOxѴ3;0H:$N_PrcBYݱ#~&Pz?Xo;"v @WF%GjHN( `GލG 8YOHp\$Nƭz]깈&ݹ &kН0FQi5h>qL1Χ *%Ք@VJbk&3e-ByL=F 'ۊ\:aHaپАȃsoYJ[]*9&:dcMPEK=X}a7\pi}y\@MS*̻tP/ΰbtSٰeLulKtN):Â='v4cY!!gNBgHd"g؇:tҙM6rGkV_4xECxۂ'[#|`GF2⮐ vS20gk_+f1ᯌ#m f̅jFfFƽ{?!3QrFWYXHʂevT~bMw2v2QQǗژ8ܸ=MNԟ~bȟc 2A)ƿ3oSLrwRRYƔF#%@"ν<<_sשz;"$$|D2A|gs|]3O@CU y:f.j&}Y.̎v)?qܹP~6].$EH&5 *GWҮgv]#V~xGѩ >zjk ϴkd$"\m3:ոyɻX9 {8nxvl,!4tfO9ƈj1& )!.huj[LX"" rl^؊F^͐E jޝGS57fA8Y1S$Td1X)*1eHDL9Y{]oWu{?~v}#mAj4ӫGCAtRǫޒXތ]R0( f 7c?z(_$;He+ȯ_NVCF:)g0:σ&/)@[ǣ.6'3?}cO&1n^6vFk?.:u>ؖQ.QWe췎Go.,}î)L74؊b`fn  0M!_(9(zVekG5>J P-}\O\`eˎ-5EκEa. _R\{]k's̡.<ۤrk`'H"6\j/7}r?`Zc=UDwrRmj,k3kF;7<.3+ߩE/+u %$?PBge?s¼Gf3&JQV́H8p^N^y,T҈Y;"RsP8Is MVv5o> c9әR"#3"+&xQ퓺ڒ:4o ޻cxfC{ĥJ\xLm Y]s}ֳim<'cvCū9CS[Z7/]`.>0S+QDt:qxMWV5. R%i&"ivbkoz@xs_nNe0_w[DMv~Lc@ \")^=|%oTz/7+wb܌]`y&=Tͅ'"6cN$`Y6_ VY) +QI#wҗa,33e(]U20/rɖOaXJK{0$QPGgQ^$ > VRٙf#4v sPhu7gKmΡɡjPD嫻żN+2- VvS䗲x`&/8Q@ AA;,''qbrb%vAAT^*݂5f*&J"$Bl,weGcMO;)7x?ӳӳHHD<gEE)22}?ű6TSS6!.ۚk{5F߳sj7KiwyctVg:4 rmо5#\mfqU7k=k@8W `( lŴWWУuU1,I;, CZD~ )2C-)=PNgR][%z#̚$.Y9 -S̖f:(t D(*;h'F8CgSҩ2FI `?]h>Knx/=Tr'tt RNhqB`H^5>Pf~/mi3N<#U+`&P&_Dx|Plu08bE-֔JyWZ0nkb%멢3tUt<09ݧg@R` 6u(B;to]zjES;c~P8vHͼ:|}{~D+e%ڈ㭹c`sF'zXiDb<**쟳*)xƔVzv{Rɂ]V҅~oX9OE&ߣ}\0!Y9$Gbiʦ'F?q`4[өYί_ɝq e- 佸1"2g} H쟴fW5z<$/5$ 8} ͮ؏`t*;f7h-_xo0H9Ǿ!JR`˂_eL;5|_ #:M1/IF0ն `($J ]_sRN0~aK]s&B{f#N̢5pRh~ Q:iI_̉L#h+R9s9 fe{̲ fʺ\ },3)>Ifnft>)Zyr=P',j <`#vzVXWfvKXcLhAq;Xgg{;GQv7R&Cl* O5\;3PTV}ED;`?}K֗0QM{T V,TcP칡+QHʄtc~=B-YبZ}'@GHE\1ڪ;h6Jum pk06[;}A860*`ɬ14`k$$n[gDu`1ܕ:7ؚV*WWNc-){-sΙvjT彶xsf~)PߎܝqJ?z득dpMdn,F2b6?`Uǫ5ibQ)e؊"AuUy,zS𝦢xtyt Jܯ)7ZJ{'{Q4 26^C%-RS ##QxTG]GǸi#.~:]|[66OgekUeRI9BN6?OOX\\-J@!qf Ұ;QTC>l1)90sB g̏=Mw~5{o7َc{ 28s }kq2؀z$"-cߖNw'W\꒗ cf :Dl;XĽKXFP P qrq&Aƽϱn9ygF԰qC W%[ۈNa71$`?=- z`n]?n0}1 J(p 6 UYQm:cۤu"Otc^ >`Hd6\A^#{g;8B;(ob-3jfMԺE&-~s-P*4exy\lnWWzu}]$O<8O)C=w3q~k$3jgdY//>E`dK։ĭyeXLI`!A$ZRg"4E,3Q5$m hZ|[`q+r5|, a'1a $ h|9`:G=V!:W%Q+wfqKy:A>X~sV; }16L-"fRg6Lp#?$َƷ"/#鮫6+]Omde#Q`* 8: 1\{+vڨcV :{ 0Pۓՠh^v\֋H o bjcfw0>0&Q'ڠTܞA☉,F͒Ps[?8s7, o,q'9^p^j1 fg(nm ^W-U FNtwn'=0"vb^i?팥(23iiZ, F TM|R PyOCuo<}uCDw 'oةveo':Sv XMP}E6474LR[|%Kƺo,@qQo'ePU[H) ^BDR$ .TDC4 I%H <sg{~Ù63{^>g>[goK3aAɠ1-V˗I5H+V+6.Fօe2Z88) %$%پ`ަfg˧-vzԼ3]t nf=ywkf:g&IbE*hw>o`GI;T4`?՝tK]@~Cq9I=Aഌvjb gȲi2Ho 4e+I`\mbJFr }:`oL&,WQה${Fƨ/D4$zK"&g=&េ=6Y)]X7H [:ZXƋxg\He䪯Nss} JZ]QZЯ h"I!{Pj-ff侮gg-Mz ;V{F]~G}A<<+OI۷sB'^9()̓L`i׺oA&&& B]%!%..gAo'(81b5cYE n`5y>xv]Xބ:a$OjmS8 _xl[Sqq~ |?J@p*5<`"4ݘK^y2DPP'ϓ|Ri|ϼUm)\CWzE-KOpN\Lױ[/lT|lARoRPז>{.g7_e`fO",8RLj"Ul'6-A;>8n( NطLy+ EmnMfָZ:~ 4rEjk kF|b^>Lrы;E}u.b_aF`?ݸo>~rwW_J@ :K>ոrԵ9m"'C4n W[Zx'ljt#6HNQnh%A;` v\Co^]t iI nu5]Z=8J8K2Aŀ,1ʯ(P@APxeNG­=OH!}3ѡGm+>UuYsGn>=_IG2TnӴ'`RVIRwzm:힙b߰6٩rQ)YRdPl䬺+RoN7C~T0gyw' D}l+tZ kbJW ɣBv6a9H++K2itWWޡE~ ]R}X!W>fWyU?4jFyŝJmj!!o ,y y HWAX##z^]CaC!4+W;8,k5F>׈}F ?jj̕|ae0g0fC^pp0xx{$.c F#5C<&~̄έG=3".GW6*A*Il$+ZD Q;m=*Pv40P₾y,hЉovw:R@0~k4ŮwpcM\ѳӡjԕ)֚ +VLnv׶1!jڗVt&!NŦ؍0~tK` T,[Iu|3,YZf闋zj Jk7Bj20=;}O\p[_F H 4Uf)EFv4#:ױy9yYXO rC&xeo1F&2#1ƒׂH7{Op;YQ_2B!w E֓f(~[cb-՞oIA@)s!bxEr&&{V!3GRD̍Ʀ^C6>-]8|IiT`e5LwT؋9pU>:!){z"_^.sxX!fYP M8[1Gh0Kk[~VX@#3pn:6r'1U,c ҃`'dC4 O0T'fHbcQg*@=&8(Xݵ?ey@J\qSS  ŭ8K7 s]=&RlU<,upP) ,K>!_ 0|9 6Ѣ\EQG!9-36ٳqEڸw&` 3 >Pf&GsW'&!obnA`oZ/P+/hQ{/bP~C zgcˢ۴Dq a 6jzM>`߲Oz]6YbY#w<~n)QiDŽ3g%RofM.8V/8uNN:n* 2wTn !3d&3SȐd ɜ9(!2%dJcy 2A=K֭sk-u[l* A6r_T-| Vr xf5tg,UfRhd2y'3+XҝF[ `c؄=Rzfd_C!4r);n0ޅ2iA2L 7=6WMy{qx]pBa}v;i/ QpL4i'<&(UWԃpT:6fs.Ӳ $])KqqODXJ>G OYVӬҠKv$O4px:^ZԇyT>F=Jn.a)Æw*d3?~ |Q{e2w˳fVVK=IՖbrQj~(2SV%|dRȌg)'{u'$$,ܠ)f`,%{ugԱTp0 bzqWױuCQ7&} Nмy趹+Y\1gB#Mä|p~sp }=|.ӽ:7Ghumߜu,?jݑ@X4<:Ҍ9+8uj'vX nfwl.96NQE`Cf,7 ؏4pRjJJdhɞ5Dh2څ&[w=AջeS"ө!4ĕxki3f+7 8=4eV3Z.49 rIew6zSB *ﲕZdsm>?tkxaJ wmу<FK K֬ΰR> U=|^طZ3[8ɬ8I+ uEOg V+7G]Gxo יP쵆M1ZUx, -<~*&zfor2X#{o?~Hu]\jfvS~c0}>y.z._[·''$)MW#;6,0Bf*QwnwYjj&fxUUwcMY8~K71#)$RVR>]#⩦.hNSї\ء;UXXE/gK.99Dž-VVKPsπg`C_fv$ƍF=y~~XIjӀ=@VY9Jߺz?gFι=S^f_ c1rIDfE^+՚e`4e%ΨTQx gcrK0ы^1ލ4j~y&n ^P?L`?\=y fs^auJ БGmp>9%6w mw$}2rk}@sa^ږJ%|5 oX\JAs%OUGEBRIR Eib+q9ձ  kb#S'ᾖQ&XքVIsT0^L(-'_T_ϩ;c\9hi-ZB2nBwk@?` ox# +Deos}ݒEXvle5=F@o7kȃ1Hoюy&R~~`S#&ǁJ-Dy4!m!ɤj\$Xݳn8 &VS=n=/s:M.׃5Z+Y[0mJޟq$OLm}c{5#FY<%^U{/`ߦ"qu&9H$wsc;ѲyYQ׆3 V-X?6zGCp^ ma}9e!A Oqo#r󹙣ְ6P_+~kèc i!~mhx54((ܮ>[̦(aɘD3X=:L $L[Mb,.!d"+-Zz?5фE'2Hs^ e6]sε"& tHl[^OdvZx2W'Rjq9`4N=I"KC8?!mIuC'N`Z6P.]3lӗþԦWI lWzhRvC¿ږV=QZdo#unk;^ ~ i- 4O1 vuhH\6"8ixU]h=h|_34S,Ӵ>)|h)~ c9HVaŷߺ+Fi]-M tޞp@;U\D}9 3ͧ EMuKACE^}}.Ƌȃ C-E3ht%j ?nuA3A k#mnSRq%mqG@|o[ hn/1)1Cht 'S:-4h*[ i1]GxQx,\Hz7rNAƫ&3{.TazZeRJ6'tx뇋}朄t= fs!Ô-v \j^ >% j%_&#G~\2@a?Sii*V%Q b1>w I<c&qp *sd,PBg2O2G$Ȑ!Cf,)CQ <)%ֹZ}Kݽ}k?'q#v*s }{e{ u"6`>UȰZ1kIts~Ϊ$3{I `ٶcpT4a&I$=8'WsqvJ%seuZN}̈W_l*w2(S&r^pmLQWA8`euϛr{ۀ]=8f/DnѾ#^Ɲ!Y(!esԇl(Q'A6?^mXyt%pgoT;h*$k4s:':{wԝ#,K$7b>#RUs&LX\ $ E4,yEM(|o쑑GRc'-#3PÍBag|%n* nk},fޜg̫)t"(>P1LӋQ{;Z I2(ZLXqƔ3,>t3{ٗ+IBmAChz.V$s9s" 90~U0.bNy*QM`J@)ӹ^0%Tbz]|NUHkEcRuj0&0oX אɸ.žܹ~KBSX8zs$hxV} P+x=yFL1hj( (TNk1w@:˷lt'83?t^oN|-x1V܅7qi<.XG-!ǣp X}ݸ1.w #Jaa|,3~LGŠd3HIDgq[Lq؏2B~ lg %FFc$}SmQ1ov1z?r _Ն9r.-Pj [Z{GOKVGmgًjogOe]+{y!{k[ys}wu.yA4tr|t9^c.)yO. \|)Ch7U 2^!L2LU5ø3g-+.MF@;`|tl$^nǙ|>S|ٗT⑍÷<)m3-~AE6y#$E >hܯuT"sie\3x^7Ʀ2{)-`?TC]PRHU$ Ǭ%* j{ܬS 4$mK*UsUC* õg7rI ; e}-Η>*ѰB5 ˹ӈBn+zYKAO-i7D#f CO ,bVGJ kfLv1OTypR259 : fP 8 ɣdK^F%omI1+dꩃwg rE:~~3e:g:' m o 7D$)(ϝDVZY;mfyd((@HV2qU48ɲQ(qqvS ůuw,|?9Y9KKHU"*vboe4+G'7SRFnǜX;U{W{ ; `*sRKmllB SyKM1 !ϥ^ԟH-#U2 Af!\s!/T,7`>\y0c{u uIƪXZz [PuL406-8o"=iP!I# 93Ή)*'|B ,clVnL Ę"r5hiݦK۫^k{=}mK>/\N^V8 ?b+YL޵A-5w>=:^̙j3_gfՠU8q,S`߰XحĕT_P'ߧEނ $'Yz#-T~`2YK o i-i.E5vTIy7Nѐ/{.6?y]@vb!l݌tAKQײvXy9tT(~7OXQZ"d]E2o\+5M(O}ʞԕ)R +90-svim>ǃ2ƛQ&,W &Y}EIHxmt `4 V(pPʊq6/L.F՞dU+ʆ *,!w,v'Ɣ[CҼ |?d曪q">Bi/}3GXJc[Q [ 7 #_ˏw` lYPR!wY&È"QXGoIUl1O!|"邋Z i+>&nf2,x(Q87 b_2 ͥAO>pǚ\SlRL+ѓp>"CY)P,mȹ*)]+`4NDxnm wCDbNGjp+` U%&Y[:9Q4Xiȃ:XbEuTk ߦ#*T]NtӑG3tx tړ7g@V =n6 q :a|y Ȋkfn+9jФܚoi[b:ûҋv}9i{Ňo dZyBT#ȉi.|#~3;W8%%):.:5K++v2Ӈm֚ `򋈤ӥjuvB^GΈθ0r4QSavuGἈw Lu*t N6F;G+t{rn{&BܫW,,ra~!$!$-_O@Dj 33%dlVXБ%VVA:7W -F|xaJ g6{x y RtV*(%_yHT FͤNX%.-*>zH9"|6J3vJߡwAUm I .%TRNC  JH xhT@JA$<A^˛)709Z{<^%aO1|27 H%H|QI\[5?xR%sQ% b`<ʠQEjsYR(ذ\o)3mÇwN&D eN_SsY{s.92qFJcPoEfqۖ3b#rV4IywFo 39Z`ߴ\d}U9\#Ľ|M^/B)` 9r(o}x68;mk9IIf2Y -FVф8|("EkM;sZTh3~ۣ]REG=a笽_Ȏ VK?'+Fnm%#?n6Vc~ym܄{ݎ"G=Wq&U:<(@O܋jEbRy glG*Xe{D} P]ɤUVte` 6iYOE~c9uަ+Cɑ\vRp` `/ipW>5LP] i1~&~A _,߈x/'Bw4,.t7vq3aKKIiX5aib&=]K>֖xL$-MJr⨵$읁(#h}\|lK!RDCa`!kIג_>\f@WafN7q:;c 3>{;  l1 3."`a[YyIrs{ɨ,6Ku `b%_qHPaR/%#$\)DS ?*xYΞ!sOv:в*8߮G>&%fܕ_2z@ؼl\o2̫Dr~(N^ʖ&Z$,awϊ)`5`G\jfEr^ŐCR sB fA!t٦jEʼn蟲0A5&0"ؤ߹w'qiuv_ JlisɈo&/heYD- Z4 A|u6ݶb4p,Y͜~};zc=,09Ii9>8sp0Ʒ/땄Eú^8Hӹ@ yT(=$I5d(ɕW :;K$sm}?RG).bckp?FJ/6(M/]k [`Xn2%7fryx%wv2)8ՕpC=;׷oQ#oY]qecs#{ TJԼxʬR`AgjHݘi}}E>aCb_-8t?}šyńGzeDm\]2ϑM&1"lT7~Ԇ>!l_m3mRRӔ %t1O;rsk;Ehݎv=,ulCS~Kp'u8%W{.HMuiƲCC8Ga3Rd2W@(QC gS̟r{|.\L:!-! f[~ݴ^.(.s*2`~-WjX5`UT>[s[ȗy"{3-C57>ƗAF+'n=ēy7`Þ5FUy',Ƀ xU>tIa4-EV3 W"î) q)Q tiZS?`^i9L2cQ]3ǐms4hb^r;9ڛ+ XQ_S@_w7lSm0 'ݺϽS"b)gdd.?iІcРMVmm TBv! .TT?Cg8.A-"ގ[B^EE+MEղAgS,S,XX/v44I\.KycmIť gEzA[D{ΡF``b**6P[_a5ZZ%uaP, ϓs]NOnfq~ga19j Q&N$A!wOw ~bTj{=.D5V!G=+| %vc/!5.|h&ȷ5J9k:"%n?`?Ү #8m}i9߽4.缩]񲽲TʞrO,#j4阣5r\!ր/x㬺e|GDz7作zf$F,j?<`[ Mb#I5-0ӐP :'3F/4@8*aG. d`V_5&Q>W(Rq"Hu0 EE/˗g+>Z4moaYSf?KxFuڒ\8:.ā>j7lW&ì js3$SU۴<α> ƶh!wpvI ƱR,)6K̤ZW…I҅v /$JAMfd߼/_hQdor5 ;p||~TjiuQs`g b`'Wb9J+4sXp+sF|Ȅ90s`,HA@sX.]g͎Îױ `aA.w]MQ";t ޹Vs_n=y+]j5OtAљ2%,kpEv~mAIB FKCKP^;/T`7Wc`(D+=fofWsobú n˿G˝-S|{jPxkr-;L\Z5'w]:9O8wK6OG6Q lGRIzETR`pg̉(D " ƼG$q6=f 7FPR5:`˲YbM4/]d&69K:wU0=lק "KX^Cx&yR4w}}#u[ˆD}H³!i3zu;NٵM9waiN':ؽ!pGtڟ)7 {GVP_>S]-qejNNa*Cޏ=LRiV.AQfN7EeF5^s[ԥv1{YQ-Z-,m5/]+h^ˉ no ' ͦ5594^<\u;mX.ffs8~AEq{eAfdϬc̒H"HdfϒMFt~O}|꾟zr꺺} g||4Z"D#D񏡗LMMv7rҗm_`bZ?}3Zibk }|@ TEfH٧r4dU"awx&+H0"ɬl~[9L6Azy1F2)^|2G 0 ܉"->Bqݗ?U{i%\eNA`?iHY>z){1gNXG&|biڻI}JWwmFV 1:ҹf.2G\92:TDT!5GIO`?T&/|6S2tț\]&]]\eIH?>xC fqinc!V& 2Nj2Ɣp:`R\v!^ж"Gg/P\A`6Ch.ׯ]ρXBqcn֦OZدcel E K0CYoϓ Hnj9B6=@XkH:ۦ޷7YL9!w]f^]e Cf8Mi_b\H&mw̗?2& %zJ>!FX `B\h1%d )N֢ϋ%5#ں iw#7t sR*VKK|p[\l`}/;[&(nAbޒ-7Λ*7Jc6cڨـ7Ȳ8`?DM˜ZS\fͭX1@uT1 4yym:{ՙO1հ?9no9i1fw-wӖŜ#JUɄ:b Iζo2m3Q5֊wNᮃWC7l9A-ND.oyq.6R+k_W"_#6:ǥw/W</@+(K0(8h9?BsպJL~ `u9/62KC1ZEz:gQ@CDt'm m0<1T7 )UMi6){Z ~ ii3\;uzDccT/ݦ&~A!~tQ3(h vГxg=L'5l2ɭ)i4Hמo+b~"{dW}G7|NA`t.f аNEItJi(ʹ$sLK !u;KnwW߶wvo7ǥOf]Ę6r[ba`egOeܞu=|XeG6c?Ǿ7HȨ?';ar_$Ou8ERFX`ju6],HW6ϲBKa$UA`'tױye[.(ͺTJe .t{=;~?+~Pp QݛHޮ8}z(竬%X*rᮗ1lPьCLjR=>U<}Lj8{x~ٕu|AF۟՜:y5 *eT>趠{rzrx %⭆1/_sw_VV@Y~d{^<&ACd+G*ZTԆ XP5uLj[ #xnh2䃬(z-.兩J"SS7N8Nٷ%aL`+l ۺK[xS/Jo<#EH/;-C,gYFsQyıpmmlCJJϙfM>q-0:jx_<,qMC$1߅%UjcćUu7Ȭ&88j ʜ }yg&f*̱J14gBߗŴ͙)> r]g 7Q3bړ!*ܖHGzkBQ%umVnžZ}nFpwxE /Q>-|yhWfmDӧQll+df WDS[e>eBd|*~7=W1j&4|sc="WFӓoOtH ׬MFZo6ӢQvA1QtTYg.{WYƒoKEMLjKNǬ6lo`) CE#nZ<.W/s5V6|^5!Hκ|_Wy&'/$<%Xo7Xaώ> <8j c5jt4y5"|cg!nuW أezjRȣwVOg)ynu#f7z-?";`1ƈFN@V WBm.db..mR~oϲF^3%`8|`k)~3v(]æM'r],Q'Gat{kD(ȂÆlӹr,Vr{O~Xc$Rɫ2IW;Y/঻jä\gHhۖtkbLښݽ Gʘc=oPǶӟ92&0/6Iׅ~Vn~ϟeq..SrZ];0G~~1HW5TNհ쇊a9BS'*?3+ulQ{5 yR_Ԩ -{q45rsz55a4vg7r1ۚ2::l\\P.}ύy> + 1B{###o=ioR\C;tOç<=f2:+J?nլggϰeE%Gп6[4[4%W,Y"[٪*ψ/ݤB`V."ggEIęUQESo2l% àΚDp"}d:ɛ{P77]D>d}Xב5YmAp|+WH6n?W*@bU( B9/2yf*f*^`סmw"aيƦƦ/dKqqs'etHF;9^֝Nǥ,:*/H$<+o>.D[/NW>w2Mq qu4wBߗu/^?Ѽv:'i! Ӂ{.GtV0a+[b{6MP}s~pS10i$Წ~?`H;أ}B-/tObWߠLiԢAӧee ɐ &:1 n!5u4޻N lwY_5`-LJtoS2AT7][5KOPX8-  k}^5iZ0 d3mW (hj:Ig[]Y5( f $-9LQKR{ #M}4z>f `LrB?P?gXN[.hxY\hCnNKx!8|LAl%x^kxIј)\vQ1~ΥOwˤQ7D2QsYLKyy!+kނ> d!grx1fWAU}]: 6J j>TNLK6@wtt.o'$h1j޽rSDqt:5Hyfo "2]Iw:?mByZ[e""{wMpg,L,eL!$C$4g" eYQpLE{u}߮jZm-o?{?_Ʒuz{w'#9T-Aa$7 7g7B|9rGhzk1zXzI[DG%#l aSaF2%^ogf,z1XMRnLU{<{nə3V;Xk'噙m<'%bt:AkztQY±;niw*`:9nazX-9dN{b+15MʙY 13v$eNX(oB}$VwL09^?^S\ BbipZ|-{c㡌+SrBkB X1Szd勔ݫ1M,!@wډ̝%l-\C39i.eXO?x_n:1̀peYr+KJ9vM= fw4n 7˲v"]kvk{|B=`2<4-|9 GZMdB`X9bluc-f招U,dF\0-Qot镀O5Ll4K8ڋ'!.H%v,;G7v8XIҖ=WsQXvڒLdCZ|ցK^DZ]W[#h8^㽀}""#Nj"'E0trg oo|(?n]'o-qZY= 0uy\Р4"w ]& 5*ca'|Fl£w⟉>dH/Ѡr#0Y%Cl\+dwN?=)s8"s$ۣIb`}/ӆ=f_F+gQX؍CZMS8x{&nQ/Ƙyv\1JT }{}G<"r;G1fN636h\g }y9bLșɣބy@[UۑgYPZ-!Y>GnmsUظ)FWvнqrz&x;Ch˞j*Zj7E&,iTV#DZ.((5]@@lQ†HbDGO#]?SJnέnLnL|AU>vd˼j'vrv\*DoC665e-+gSϦ8<]`c`CFCwr Q\N[C鰝 hE/C t<䇈T mm8dݳPQ-sxZkD?[[z˚uT }? w]ɸ>H?)=>?/AHރb~[SmDZ.45'j~=+҄ՠ[ඛkML2jAdsu>^D 0~kױS_<q-2a9Tq,v;eff`?]g,@ir]-s*S!MjVUu&bDILD0#*I |O`wy.F69'˸_? fӤnA΃- \Zx=J^JJDU$l"E_i/Ԧ2% ~ sLq3wpp; }3wM|=؏OE@q)[K"_ANff47:8{kmC7N}6 B6i?lcدgAu6Yv~].bS/5$滊R ]` v%5wQ8`?=잷k~ɌfP@V[̞|Y5g=t4r׳u @վk݁wx=GsT5T#.Aہai,ʹ瞽'cG'-N{FٮQX-oh%$;ţ/n ҵs&&/NXZ|w.tO2ᐿSffH,,J=`Mܲ+@)@d[CdW54e43!GJ9~/Tg;؏`Bobg$'C+4m |JNEdΓd"5#QR*Fm:cnRW7@7Z_'<ؼ|Xw>iŋV{ kd!ޔĜ2طl9EHǃ]ԧ?14Nh:eւ4,= kN)ZX/h'x~E ;%MFNŕi;/yA50+{^%\Ku ՅTj٬cZ05NjF e"'6l-hj&$ (T**LnQߏA^Yav\9LSS}MJWRy. 7F9 ;6دg'™ !M 2ԓחCQ+IQ=V0٬ a9/5T+FQT@?chV2LbtSP1a>&T"}Em^?ZtCd/CL:mp,=bpt_]HZϤOf`?CjI,@Z`?͎$xps"(MNP[-ZMR\JÊ`N)}(zQ5m< F4 ^F-]^Zj%(`!LdvD׌Ѣ.ͩyNa+,3|8v9(C3FJIy{TdTz!#N ˿hn+B!2l#cN(^<aΣ%60e'keEB1ҵGDv![dߓRs4{R9^9y>|$1DivVПy|`g~Y*P400`)D$N}ξ:: Lx62)Sd 1Wok`` e%*Q}7PI8]h*|dUJdƳ iPYv TV^ǟ~EEƞ_zLk{dc/˜N(G0h^+ۿJ&'}y;zz>$q$++6棈7>ML^7oiߚ]hΝ KG}Jp1@J;aw[wF.8,F1~3%>S6>'|(͸&%B28q+v>d4[\#4; `Lz2^k9'Q&Q϶6bꀬLkM|V>ϱ~%ݨĒ.,BPeﮃ4s{sÓoW甜 |ө@pi! d﨡߳>GU!Bxg-ԛ`m>FN&` `~tt|5HЍ!Uf;vQ%>y*j6ncYM\ ep:;!lY~XZR$g.ؿ5u6x l2D׏i [c1oY72 igJi*'MTfkz̪CbmKfnb,\ɣ? $>(E8&"Ҡ7huѥѥd3-3-Ⱥ!SA&Ž}2tȽUKbib)!@TE2?ngs}Zy>o73^ bb VC~##nޖRx-vQboWXtCI4x,C], ?:YY+ASS1p@q;ZߙLƊׅOQ$  [(%..4)}KcEaڽ\)S-۹".tt纐f x >f.>On~G\߻ơ ü cRpy%v06+:]ZjYPHǍ34yn%Bbj8,ՠov}fTH_+_I{p>cCk "lV .)᛹.S\loR}h)B G <)v uBV6tLg¥> l."SZ vã̢|Z$* ɼ;i"Kqg#BV"lCw{F߫Kr.L`i#FNA;u"j!o81~ B=E$U,v{F˷c941֐!IWkoY^u~4Ǎlӕ8G%_Vدib>: p4t>^2ut=XPKm>>ƓY''T6Wn7DZ f-lm5xFУ!9[4 +3Vm[A~$"Xs {=Kw YiP'XsSVq#?J9uq6]5m?F[dQJA> ,}G\N*{FTR`VLP•л1gfB"L&/GJ%,T,'_'ު7ʼn"Y3Tͳa=1ai+-`,S0~=9NrXR˽+n67Ƹ tX1^c@hInGsald/,>Ǽ%9ep  md^sE͋7Nvކ[3wh.w~ FTlTmK#K#kt+O&74).O!O6aere 22ҳJD;=I^ޮ' ܄3;;iÀҩ TGl֨nW0VVedO`7a$ff!!{y*{*s-Pdȝ%˄`8bP+\EH6{||bUM`Yx€XLʮֆֆeL&O&'~YL#u ^`zKm[j5ץvxPoKI9˰" fP‹'8o#sa{X9ڬ౭55G`HhŸȫA;l@|xȊTMIOCVqxAE#d-+Ao7ԈSer $Jk.`}0Y& ?N~9݋Z(C޻fsU%y%` vVܦch~rGL$qIBǷ*g \`׌$x~C~8H-]$N|*FVRO>Un_!|zɋј"&7)Il7hs1&햤g 3Y i*:C^sns3U4BY2]}ܢ8Mu<[oi۹D=*IA58Lv[g@^7Wr=bۍ{.m)w0r0Q 7mI#@,h:ɜ$x,]Z!J\ `?B3ȞgH=p,$X˘(cǟbTfb #0Oѯwk 潺S^e1 p;={5gF)0~ϔzܺUtWܤY CibI@"Xk"~9B]j%ZP '>Cknc}΂orgk=:^A$qNkB;W4V53gnMJvmUZju~yp蟿|0zE_vի&T %eyeyܝt@TSSX 8޻Y[2l|dP( t6PI(fD(f]6TMUY=8 !9?K&FODT})7|z*@%GBd#j}֢Nk"T~NN"``!fO"%%"22W@oH**|NKZY#{ vzRG[kPX-mޏ!Cw6쎐Wڸ(oI~7drz<wGܧ"|+u.)ncKgSF H04HX/VP t:EHm%`=+ԬnCL{B(P[Y;ab^Nuʟ #a<ÝhhWUh=0<`jT3 ۼ8$J/Rtyfҟ(R6z2zvf3<) .HHhT ,޿[7\N:yl`8iM`5CP6; 6_g[V|뻏nBvb'j0B$ DSߴ>LАƟ'Ȼ"J!|`{˜9bNI@03ˏ&aQgkKk(loY֕A+ 3d.7 m TD}A2*ќ`cNZy.B!d̐PA7cʐ2L!$N)C91%c%CzWgݜtݵޫ>oe?<ϳw~,GOt=}.lM!9t/qD4owmCn,EXj.`^k!9 y5ҲTл\m<'ETzU0A 4CpÎE2xu= 0"õsC1!āT7\w۳<TL:} F )mijK2M&dBs%w%ZPQ0JyQz+z I"b C22~/pdF? ɹ0kS؆Iy!;!;/@TATQ'%SJʘ.k4czoIfҒ@bo*6"AѨ*/   ܌;s,,-nHBQי5s_Oչ<zyk;u4W-\n,2;:?ӳ$Vk[ x n:u&oF6zYULS{"|vu&s9XLhMd4.(Zv-86 @L ݩT5.REJ*Zb!ɀB.H3HJʔ5b\*/b|DLݕЦlOoj[R?+B:&7by8e7~-fh,iU.ܲ*MevE09Aeq f1kщ&SDӧv. zd=~VIF!l·ݪL.MH6zKוƬǬ[Rl:Dw!^:빿ёƬc]pgé. .y !+V)x(X?qHW˔膍yقXn'`F f+=5Vh&Rۘ`mC`5'݁kD"XMI O]RN܇s l`Zonu%Aݐ("GH0'e{ XG)~G{(gIA).nfT/s;r"amAEge {_^HZdJ>ƫXQ.~v4CQ$u:IUp-Z@I;QI!_óf/=SRD|:S=2yDq0A@'PN9g\$`jۺ:Ƃ^j֏#_isYȰl甈a]Hp{OȖ)Gg6G=on\Q/i`\Fc`oa؀6-r5l{Yq)&WQa+'&ȕMId{d=:>dP>\~n̜]nc`{I2SVk4ʞ?Ӿu/VՔ?)d8;c`ޝjR.7vo}_cv 5E-, $97$3"".1u/}O<\<[V9 -W\t,tt<~7]o48VNٝdcwpuEOJ.ܼO[aM[4-cGk[n[-ag88'~"֬9V#``Ʒ; B^Oa[B@Y*ͩP흾+_ݧ,O `~\1Ǎi9K%PM$_{‰ԍ5\_\CcUL=9gi=/=⽹/peG(>FUW^0r[!]dl`kJiZȃA;ކYV"NdcZ5=>b6iw<ٸBRfr>$Λ]9EH2Z.S3,#ɵՃ!T יF^ lNZQ3J>\~zk@,~_s1-y*75=b$ZW9vUqNJ cb9S"{ܠp` լ,K卬?s KLm}=h(,٫^= \ɫ_1!/ɱ ?v2v$1ũtTI%:.dZ s:"~DgBe_)@ygV Ӧ &]^Z4d||{cRM2v04g0|O7K?eZ5uWz*RVCV* r9 9lYY #_f]ixIDbb۟Úe1V3'ϘomylnƐtq{w=}j[c3Qa v< m4~?UXZ-{v:N7( fa>~(@w5`XJHx1ba4`yɑT6٢ ,1ovFk?`vtqK"gJ:1\.i.}:Y0]Fvή7xv$[1WD(BU~?+ P­>E[Dw#, ` e$0dVOHH0=`mwT$ Uʳ9<U׈sHn[Qh 6י.f*u?vusq:S_ȃEguLxw7=c:u&gF6E^)V$*3Ӫ.]RfrGӥx5.ʚB;/`[||šSA 2Gio+_&O+J: C)gkt9h|$CN/bPK#; -H,,tq{i'7G01%UhH{K3 &:eƈɔ\ fkeAzǯbTFdnmOϨBfM-3MՄ wͺwu#U? wV{IC 9U1ϡ64AO*L%9㚕o{iO W*nӽ(_]mC r/3vE0@׏lܘ򤂍K?4Z^^.8VzW^Y2$=;"9qhx$8wBY c&hhp^ppt0"M&Xfzכj,X.bwrw+dsˌPbտ\gsscp K̎#7YiiqȵkoӾɎ$v&v"S4x*7O)Gylc`>ɮ6&O<-l#fh|OjurʮP`)Bl`6hXJ* `6 0f6JWPدQ zP)X&E{, I)y(eJD10ͭ(ޓKR0A0` 3RnIz}%f#i(%VBb\Ymng7bI5v\B&WtV *&t_e—r9oFLQQi$w\:ṳ\r0ƿ`FPZmd\lW\}G)[`0ruw(ó:3ħJ؜ð`0-7"9`kM)̀LNMO,pug~>`]ȧ+rZfoZv*v=As_ִxgI. ;CٸhdOM7%A-/3`HSt/u)O+%"Dg yΞc}`}/;'L8CKG7D%'٠bfLw Նg^_X"7g])4S"+:d}ƻih-g낿vm``n }{1E>/hFV#[`51UrzerWqe8 [eKϯv!y2{ԐF?ΣHazVF~G8Lȥ|PDB (DANNMsy[ul p Pt{xrxҞ)vX+Lx7pN O wRLFdSVQV!xdЬvDB#wz96xқ5xl֣֣n`R*@J%+tkNovHc䄎Sd * x3cc 1鿥dFmmAI55?,evUӔsa?+`AKԇks37GEH{or.gOnqԏ<6iNM!(>{Iϻ{*9.XKSLG=7ayPn'aJ{4-ΠD= 6N{uru(x0[Pb*j7Ԑé6S?I(1I=e m_ i]^PYv,mtgJ ?>ewv7 f=(/yvl$>$5vEXC{HU. <\%%I@n eV kv/(S2A$䷃'h.]{!C^0[ zVip'h0CD 7$km}۽~RH(#Ar]Hң=Mkx7o\Dd @@Ο_/zGZ[vz~/͝8YC1N.P[i29t#4GoOOkk3h^KK*̓}wsbzx Ɠ W0^nm.<~xB3u}O:'{΢P ȇ%me8&b `pxWD"m+aXmq9-.sJ ^~AE ~?KUxȈؘ,|%ʇaz :_k{q>Ǚ]Ms"˴lfwT}Ǎqtp2TdJyIQ)3!c%%BcU2$s32dT{{[kYkؖ{E EM |p^eəܫ-[ksC<98B`.yfѤfD~C56cv^5E!y̡Ԭ aj [Mm<*?pT&ORI ee%qvYsE 7m, ˦dH]@uaI޷L3zK]#Ÿ ENKɼTn⊌2^X@\zGpڱ7!E0iP$JXsڼ(7cw8Glibb[%;W{a6ݰ@7< s'b ubݝfY%kNJW6s+RXq%,ǘq=+=©fV5nis̢ l"zBS)ºzU7.Oۚ2Gt@[ G"5HD#gP6"raլu}54(!CrIwݏ殣v~w6'͝e#+S> XÀ?Νs$,YTZ4`fպ~pߎu^ѳv2Y8*T^XNZs99XA8ldu;;ȬX"/0iqɢ:| .3hFJҍڑexwq*2V&VUUKG}=1:gL„^dO+Eow.V=vIa֨ h\\tMO{'7wrt׬5k5A / W,88_(lrp>-f{<}i_Ɵ>*H{Cg(YalGlVCj5emFjo_)\w4jFkt؏`ZF"+' kEeKuƷڠeM6!\CW|CG)-)L>z/NnF_7׳ |1+cS|vp>A<[ճ'l"~8p N} r < "EM$jʍ%Ҳ2m6MSmDSN^~욇pzXcn\=7s. ;pv4[gTYJ:6zV,dM*_ϋ]W.|IEBE0',kG(+Q>X;)iY\8iICz[a;"`hu@췓eϜHz:͵8j."|Q0,u G7րⴣUuh?[ ` !YN+ƷT_L&̀fWE>6/5?9Z3&$ٸ,˅R8ס^v-[邻7x*[oki 'U*# 7݄{,yiD @bᑃӓ 5)@K,|P7׵M 5j Cb3ɚ901ΟvX~%>z{AUTzޱ~>$Eo87Jmxٔ0I?6YPng:*ma*.)Ǵ Ǵ zU}}MwG &=;ye"Jz&ZptZ>`(A rrKuo)`!ŷ'QƬf?DQ0MŇIޣ˸0)z>7fzk"pL%${X)ρ*@!~n%L'}D9*~ AwxYRnb`6&Mv8y*qM@-F.qpu~w80ʄ%Osc._pat"}D6MEAmR%Cd`H㟷(V!0dxn\lWY>'c'@zo9 ̡%n)# 6 orOK f3€BNMOn+sBJ4X^ykgTmo%}鼘YSs).9]Jq 7$h O*9jojWq" 1GUD6p&䎌%5]QOJxnYA2@?]TP2X,EdDLeZpZZj\o\\=3p1" M Pr۟ >aᆪK{όyȞ{e7U]#!!2-7" O~Oxy~T_^=|?{?zq " DVF|t-$&橶^GZ I8n_S;:RdLXM.ˡ/g%8>211WJvJko!Q%6cx2]`TױGnt1A}s ҃Ԉy@B36I}0~ixUuiDMd˯ \NK]d?ǽR `JeL%aˇץ--ϟ|. #Ru0~V%?M|`HZ/,j=}n6Vk'+(?g۹/6u<-'1ljn}/s,om׬`V::+ȴKY‘ {Sԑ{ۮϢa|'bm&m&pRv TYiJ|Fx\dy|y|[ !W(w"*X+XÂv}ŘŘGeNsh 9OmG^阖]8azS?;'fUL,!1p*7B@!w''Q4 pE=N5پ:ݝGC9$|Hu]hZ|&YŲ r^BRK Oxo1JԴ+MuyetrK'c}c]Hdϗfje;8 YçOc{ WcIK :,hA}dLBmbgƥ ib},-aүΨP="WЂVTP(AVӉ5 ; wq^S$ȃ8f;4GZfin'5W_oOI)dHʓ(dkMpGXc1nx*d.b9VB*9EDi N|b2 up-yؽQjSesVo}lU9*N n:=;& )Γِ'{0aͱRN:kovM"Q3@m\[Ƥ3$Aa:+w3u]7QD֌& S(cu>F QpC ʑu!͡LRC}NAײ5i[ lVշPCRISg2fbΓwK-^->nr9] yY#h |o֚Bt頢].$zqm)^~W67^r2SW&a|j bEx(:gMtO~TOkKQPY$V6 dUC6fuFgIwF~kة@{qI]qHf_s.u.M **1 ud>dMuWmw$pdٙ R1a5;gdg.n% dzzE1b`` b IhhhsqRPodϪ[\(6qڎ0b 3M|0杻_RG66)ͺ=r+JAK+3z v'P/^PN񲛋ю1uPRxG"Aͦ KHPpGo۽0=7Aik@x0~īI5bLh"zUFE\> ٍF&$[Q,f&Y,^AEI=r:vP.d"GOǒ4Gh/q/jarҶg)H=#Q8l*:C>r0߼;^ _.[Mkm OFv0 ,v(ƙF -4c1'?ÔÍMvsPDz!K(F 溷]uu<4Bz[B%FGA((gsRAY:sz3- n"{:AAab>5Kb9tiC3tA|:ڵM 0^onиTҼ:dwYD]UdOMϙI>Fdn3ˊ1eom}S͐h`S[) i o\u ]Ԉ{r MjUPI9R(Y-)o E{kmQv@=NQ)42˜qC8p^E>2BG$Nwr'5ѝhhsBcog1kII42"['4odkcuw[ 4||rwaGvj 5ݎJK Lk$0NVR4Dd}!0 x-aSoIҀ~;͒|=:LJBZT9#x 1SNkN+=u~|~|p2jCRa0؆ӵ-i i?4zFFeS$ZKh_Tpt}Z{?{l8sT(@ve6w"7ݑik2ze+^cO˿)͝d|jyG[cL^)ChC `h[]ԙ{nzUGe'$J} ݀3Xm8~#/lsm)OF*_ouPWj<׶7i7M#HLUhNE>2;OrtɎdӞ±#ާ_m)'||>DOOS}`7#i juY~>^;pCYF-/Hhsu.n8>qj#Pc$L#Y|Ȣ=\F;me^|,60sIsɫ_-*;gc\u,{~7{eA:ۉuhΖ݆H꩔;ls,@Z܄F~fh>2-S|1v|N ;%gyrl%?|J!6Ljh 7f\9sd)` "lWlhwa'T1n|ᗾb:vwf 6 =ftUſ?# in;.~VI2p> K1A_l=?^r@Sy0`"b$`JVS'(cg8.NݧX*991)f%IKP,pK;EcpUa3 ?C㺻k]j%T|NvzK;QS1ې㗑0{wնp.K ]␊4 Ht#}D)iDB%FPAwyWF}ϋ~kf|{}$ N>> )J$Pf-ڲxGC/w^4Mͅ}H0j %-$tltlEEaCR!CUU'-(HZϤ-j 2 7$Y^jŌ~o)_ab҂eGUass|UHLꕷ[D뮐3z1SZV/27vL!)a3YQK=XҥvuskcDNs3Aau&gF6E{Y,Uh,I3m Z~f ij5"-{J;M}~)umLHYI2KJ&kERwm7,t IUi2xEVŌϙnS|_hl?-.-uO6Q&?-Hq*ԕ5pˇs ڌɒV<^m(ǽ]_ͽd0F8^GoZo4det?Kv8إztXwiL}ƶ* ?v碞?ZQ7'3Q>QcvTn܈[9TrENR lpZD݃5sQhs!g}Vq vS^eguDdQL9iCn1C=Њu>@Ηͭ/e?U"-{a|Y`Cp@5%pzsc$Tj*? B5li8 Y-> XqHb/7mOnh>TAAtTz(,\$1;-[<(,))"=8;mq.q.:ߧ v,ZnI$RFP|DɟUH?Uf\5ÞɎkQ++h 55zjKK_2nPGOG?7BUW& (OnsT{zv4.g3>Jurv}ݯ SՀX[70@P+'d-Y&*>&5&jQěGfT*= O ]͎cQD.¢%n8t:f>7D+l\sruEpf Cp !D5םd3׆ڛ;1Pϴ}BAcy {KBV6<ROm7\N+n;xxBٟV9Lm(,>9Yd;:>| Q6nv?j.l10 kI)qkt:9CkM"z ,щl@9.Wv{6*:뾗؅jBK]HقHJJԘ2+p{Haeހ|P:}VB?_si u9,aasn^TCH" τ]b0 qX1 NQ$jӛ̝͝l*$5dk mtآRݏPS^\¸[[[m6vK$F#(oXNa>axm3;6k=ZĞo1ؾմ&;0u7/r\&a(\KXg~ӽl?gۏ1鍷WJV h,9!&a=odȦ٥ "oIƾ~rw&L ߣwZ6o9NU1>J/ψFdP>5 x>vl{YYЗV0v H OnsqnZX|o>VNNED`InOI:ΝK-!7\r9obQ5l89l4F۳.>p{gN6+/`T~7T~1o;|~ogUI\ZM:ֆQG6+Ч  jWɐ5h&,64YZf<V!|>uVslesb iW,<[[,FʰA#XS FZmII6 3q&'\BsM~TJ X9GZ#zJ\lq~ i7%.o'GK GR2@U˧b+~Q4[1Eg|El"zJ/-nHV0|b"-aF0M0PA]gRǼ3n ߯i+ aM7K71=-f$||-֧eb[;o-{یjTkTKL.HT>DDļWw6NVNr&{`Z^ Md4Mʌђlv]kA-y0EHiH)s~8HߒsiՐհb6vqSuX ~jۺ)e߽?tiLۺ꽽![>[(;tnnb堫{ɎdP+-^Hs֚ӹ_l9D".Rǎ Zԁ?|E,9~L0/X+K!C%QvI6],d.,BI.KȞ] -W̭>O1 $k2&lx>.30[NJ 0Gm j6zqu5'a#"ξ&q}0-0 5Ju[\.7k5}m̠M/c `DLYR\ߨ%Y*5쟾9&7U^5 LYNrEuѵu,"rՒƙM!Uɘ&rhdxɢ1ܦFJ~=+0SmY3Uҹ*Ha^:ٸf\?S7`>iHZwč |q3y:_x鑒ݩRՔKv-HMe5ԇ2˝Ә=@1`iPI%aUnQh .d|]7RN>u%I2d WO}m`68q di:6V-_6BfmvX2AKvb0!-GwdNǒϠ<2w+iyXg.#됯KߎE qi蝮}/{׍f͋' Uukuk ,*:ڙkk&)>WR!7!CeTҶ"[ iqM5M}TWծ7A;5|ڋ5CޛhKBBjhqh1iSTāgu/C*^|NVnVp? ;( *m{ M M7y9iRMMטM>:xP(?P˂$gd͞<aAnFH>հlsM !N}'Ҏdz`T9-up FXgyzدv%'ᗏUD2 _ߘ#qgg^j2'͛D2oBH!9$l~tBI~.t 6iC+w2^,A ݄y,w- ֦eNתtYKyKU)Kp*YQ=rMvAQ|qFon\%3*/lm{@HzLRqqt\beVD+YٝPyE1Cab% 8Ks2ϔֻG%>7cq xG,櫅I:2AX@`r^a`ߖ,x)'\?ELO oY ?cRݼ,ZI&JUiJAz;طl1mVv>?)~Pc0uG܎hNW7lgM( 7!U)zyz}etr۴t=sc2k( aiU5Ir}Vύ*n`yyQvsָs(02tt/[JpqBiu#BCd\ABA&yT TWPsơ2oep*h0:gnnFY= A&Ð TG"Pw9@ ]^h聖oWxC+>8 *;s[-7N;#0 qאXKJ6N:uҔ!mY Z$ZUba]e̱sGET̎ޞb)FG `-~zw:ܶ-8y?:ml\0Q*d [^<6P%Enk1>AX`?zX;y@M.E_{]~Dz'"FL7@ZqpV/L'|`AatŇ,w [06j]kUK `?8w~sI+hbO_~{7Nk}04Gyrz*Dž&` 0Zq.7";*K]T²qᨚ+ @[!ŴϲBRa#1tt T>lW| | hi_e|;22,B<߿.',; 6qd[m G iQSuqT%))zXi)]1]C|1&%С֡2^++ى2h1̴'_{u iz HFqN?ۥ0&Œ\rvg'?\}r:  gNu^"&E1!مQ~Xw7o.9 2[IN&ۣY+_5N 0͘ >zI!{ڹ0bzS /fk慀{t<{dS؇%pw!`6#香zߊWr錨Ȝi~pM lw`j)t^q&j/=-R,O-bӾ6U lSpм3̟zVgシVWn+ fM{=̏ؗ|E#I.[yv7}okxX!d`a PAG̹PIɁ ~-8u\?bĝP flN(Ѵ. mCպj\ϩ&&y;@985*T5[}Pw^*8%=kAX[/pݪEzⅪP!ZTÎÎdKBIIO}툢Ӝg:뒷DT#^`o*Nr%ŬT3^O,J<c b?őw..'sTpT,r<`2`2DZqtt%YnϠV]]8\hZz&_djO2i.9_kNd'W(xFl)MЉs!VP,[4 Q=\[#\s]$yq f N&1y`{@qyMEX^ M7 4u45TJ?%5!{F&R>`ݟϼYgLmB5폵4J֨||gm}/$G6`nfs-ig4!I1RF~:jr*.[jh!ធ?w8}cu1([Ȟ#+{42HPv#*#e+3MT¯w+z~Wznu_{h7* jQ;>:&+rm7n;^Gk%U:%{ET2#T,43 R1{(5UDJ#T’«T 1U(|+뗘X0 a11spDO6I! vTQ @tg_Ma ZFMw;"01^ˆo0B ?AkhogHzJzv`=cPaP|}`$gl֏%g [2RҶeԩA 8dH^yIdJ++(e刋|B}Im`+==j_\4rVcfk*wRo|NMZ)}SAHt ѸǬoyp^S o\SEQv.`Iۉ*6[秀iiY'sd^}cyޚ u@^ڷlgJ _{ƀrj[.^#мl#ߴL^9-$}b ?vh*.m!dbV6`Vk5W 릲Hr줪u(_sluK,M6} )J-/\ 1jLa6ST{G5Vmj% .<֝cx5qA3ڠcv0"&8ؗ#D 1q[BrU`Z-?ɚH?Ův 5i8AO"(wqRr|\߬eF[mfgÀXئbf_ %~O崦d/ȵ!^V:DSVkZ% 7KOxіҙV:u+4 x.)[߾Mk8-*sR/*z!dOo^6/Ix5Lѐ2kGFQU_{Orrl x02>6]!BshZ @F.s<ȎA} MIPQ88K(@#I#IwC%a6sp1>RR"Y䏅 ~hIPD 44u ?s@m#` :gʤ,iz{GaS"'WRF.D"{aev k;냚5`Z- ]uYӯyEXpDEe>$[XDw}{[I͐ԵW6k_w h᧛q~JTԲ\._!JǮUtlLoK*Sн+%>+r]&Krި-~_8NSÒThql+{\0Å>ixȳn|[ܝ`)Ow顰PUGg.S5G@ZVK{8nJ 6mF^gR:3Z5fW5+KdȢvԌT`31E W>L?IoGC OМ"e5֙)YpB-h`:bWwQpMl΀ݷf(9VDfZ~εL1mFAGD axQgZ"8~ RQЀaúF>1s _zXcײ;;an1łK^_SQq55" {:< `0ۡKn#D<&H gzum0JoU{΍H2I2.HJ,yIb߭s=zJ-;XTnXՐ43m M>ƺ>czI 8PQY ϐ E V'EKRDF)555@~̩]S=6lnlo;Vn^'A긚,MOmbKyGɕHޫYۿ&gLgR5-yY}{E:p9]zN?}Jv7>-wX1DŽ~Fs~fʟȼ&1XN5+b= xpPmɃB'ٯ 7H. Z4NW,hU>o0kzhzORa4x誆p. @!F DjBNg<<|_˞-?&sN PD en퉒 h :N_[#Y!4Bh-2rP6~T4*Z|cvovKؗ^ nd`B0a*ҿ_<\Uz Qi'7}2La4čVY4sLKEt?Ko`{Zx+6-V:":eJp9jS;;f]8i5/8_朹sUc&-tZgo\SQhd|Lڥwzkݔ) YV`5E84j "ߵ$Pϟ|!OQ$uOC{?mqu݇*o}GQg)pd‚lcg U^\gQ[ R ؾl,p$V؃}vFy vx==S1lEަQ=L{5lQAq tC əi,UE(V^Z_KIKI+\\ %ӇPV?Rbľ~cm&mN_:n(m\؃qbQKig8.Ĵ=J' \|hK_SKL)LhnΜ,,TBB̸66rK]7: P{Ȫ> 3uٞXǖt.wvc_k-MvY_OQK5ٝ wG`^3& vq|dڔ!5!O4uuK[)l[y'( ݞ:Ll:LõJzK5-sp1ضV6Zxi]_ne(="DPƞ!_n9VǬP3κI>)xMB9JG4nJjVa=gjG;’Mf_/#^ضZ-ơohD icbҤ:X*'c,WoliDمs ݼ{;n /|w-3EƤIv+:Oz}X(xN1ނ8ߘ3 l3q}S6Ӛv)OkvP!ێ6Kwhb#baS2Qi~U(K}5Sf7M5BXN̢/+IʍmUيզ̡%D3G q/;_ffgۅPu[>RY?)L?QTid1m[6S[:M'¦[0JӜ|hEo\yjE#~$UѾ#)JGtM\F׌a%AB *y O^^Tucn<nEEQ;Y;y1*R~ ܜ% DΛ@Sً /pR$YZȒ>qaZ(o,ZG$SKXxZc!ݡ{/ήX<; REKo$Jx%ն6 "Yr?dd^eBl⌞{Oep!#[=eR2#%%deS압 V!~\z~\}eua;%8gCyy{ThѩS*ӧw-1𐻺jh/ZELNgiS+wSHDKގR`&;{ {"Z* (.SxB^ E<`cӶ~>6j/lyNu|/Rpi{6cZ4}ʼZ-Fٮ4^;tP~lMh_zIc-R^|S7?}Lve9VoXVE݊N(ŀ@뢍~T "t FQ?@EU:`IZ#7k3X81.a uNBAp,5\ﯧa,gR$j5%"~v8U T4 Uv\|G×TE|p6;F!ES |*궧aw?` N[?gI&3+Dٴ/؎Hv血Rpp=8+l4g;P_&pK}?D:ce9l`{>nLŅZ$W#W[.a>dA/;>Pk=5W|ϨABB*Fđ,4o W^%ٚ6chD۾M1;"s! T,aK'.F z+ oxx^ h%heWUB7l]py"z98EWFp~8a6a8їcd-d-&:7~}.u-G^N'Y(Uy[G 3戦ٯ quu GVyͩ`VYNYHl;ߝ.>]A}ۉ";W˞5|7=Η;&hDdƕtp~ww!hw?;x#D!8LAFUH PIY.]ӥqg{TEKԘZ8GE^ނ|At NQ\.;FA\}[ᅦDjr9wɸ|>Ft'm߉D]=h(Kx#"|+Ы0oE22w ky0]1)@z]<\ykzzh6Ob, \าZ%}VV֬Q'OؐdH2e[-.IhN'͗Y& 0Sܳ,b-&  g~EJTJԬEFO1cc]N&kVTTԗt9T'ebAYyYzwy s]$äSNw$'\BWx.ll` CCƧ85'g'o2&)l`;js D8޶)}iбEP7E`[m<>$|)JVet)gf}e'ZUe? ,FArmGNCW3P?BmkpM6.3C<@&H?%?G^1ZUsҨRRWJ^d{55UZQ.ziTܼvsI8#.%n3 BswMlԹ90J!o:%uG\g\˽ K _:>y-{<{ײto ;%ʤr4* 7z? OuD0iɆfi[ó -q~lS򕤹.%Ѓ/b0bLlB9 ^㹀;*_h {r2?l/G9p3j9Od5bvc s& 9f#6εl{-6F >|r4V5:% j4C0&~d2?a%=S\,si`ߟ18< ;IB3QL9ޑzM[oItw)O;V/A˵ݭ?b, ԽQ!Y7z_I-ry?>UP rZT{EL*}[#_8B]ˆ0S:4u=hNr!?G7 #N#3^lw((FK%?6Q8QxH㍟SɭLAkmiS\)S^ٰt `,a,<7oo+KK&>zPSNǝXoov;\/]]GS5rdgM>Y$\=+\pl`kH%G%8RpTm GVPo.>6v>Bؼ_f`ɬ=FOj;7}uak|AŖіUJomeoe+&Yg}ףz#pY{"byBx`QlpAZԌ2CdCd,^^W0!ԦM/@@-z+::.=wKuЏL*W"iYa+J>X3UUWeWU4 ViVv#qdr /oWSXߓҍ Z%k]k@~NG?ñfB8!5O [}* +;o!{sXtLm(}6NXZZ6u^dSR"!^5"IZu E\?4ƴa"~[Υ%$8z%q (Mܩ]7uAmml3䏎CP$v4a&^$4(7[W?4#<6MM0OG? 1Nn&T/M;: l{>P'$x77&|9g mс".[D@+hc wY J]JȳV3V3 n&俸ΰ]Nq?V=.kaĚ]l6B~VDY υ4D,=s^.vbu;Ŵ+rZ|YsgK+16B^^kkŵ+@hntػhfH,"3{$DȨqYd%ٔ2U_\{D\٥sns~׷9|睇Vtzv4VVyMwF mVr e?R2#Sځ;*g l? c\M'XRBKd t擥Hl-}셕@~ yhSnDu3*ߒ'irB~ lN#?p,fA qtZё7\Šv̙+!6QЮiψ^v?Qz N{q?%;4IEړ hܕa͐3KF̩"&U'T l?1ݬםe`=%q(YV ݮ#vTG4s7F8ۧr "<~C7_j 6ϒ攷F*TS0c¸P0{)J IU9f־J6c{uD5ڶ lNwX~<05io9If6^I;K!$)U`(ǓY,hKm;}5ߌ}/Hh]<`bj q!8"pM)L͕X_9Tݟ|Zpqm^<N@yԠٲ$/ $j-q3uEE;"_EJq4FhhN[ nیm+2 LBz=Ւ|zYɱUnI9b_'㏐$zv哢~OY)<#RsMx 5M' U\L;- 39[6m"AwRSƎ%) WOnTjf6yn#퓢St^WȪU,=qa͊Qݩ>滦`^ ?IMpܠ2za-pַn~Lwktn2˵0:Z+{E ۊ+aJo1ĜM$C'3sXQ9sv 3rw &>7y?ԆHo#9S3!_M}}6.{P8kĩIJ@x|Ͳ-߇\GIFT#^BzSdnའw8Ly\4s44EDwQ$'yM42 (:lsE&&]W,0?FF''IJ}[[趰~{  @d)r CRѽG ɰ핚mimix}}zNL:R(({zVmESS? y3Kۜd&glsmJ1 zNjo@׎$HD^A`i H[޳^1kMpKCR+pB1Z..`3Ɲ]ޑd&a6UM1M4h5NÕdWkY.?e# xbo_5  gl V`'_.ëmo7j'F -H0k99H|OCe l`̀0ԃʹfZu/qn``WUW.? l?.wJqI5t._;sΔlp-%Nx7ͽ8z&:+x!*}|qN"]|xQyrW<&_3<·qivvs&5`0}'KBE-3а pq.w͊Hw AYloF[czZ#tt:ƺz3;;e1H7~lylyPoSVs}'* r(65#(/b*l~V(!A\\rOy9L#BSBS''жc?}*z/,TV <@'Og$c{4Yv9Lk&Ϛt^<;5"13T >K=,WNQ=& ;W4l~[cOL<؀O!R9a3nEwǗXisss]^X!fO*c88k1&l`VH0w6ǜF͆5*WzПԐM= b 2.Kk`2$v#H%_+mZY5>{DJR|Q uz<%]-ϋ:q mP'g>|CE%(492]&|=us|P|]J VA"#{>Akroemz SAf )!xeq3bׄoVkVCoE@%M^$@)ŏ_k7`4h!(3uD C ѵZe ?w>pר2%ZVQ:W%W_ y `2$秚HG`G/\Xޠ_#f~6/EJlJxC8`Ti.A6FF9:GG k_*Ah7J9<5Wc.;T˱~DDɬ&"8alEGfKKG'}OӣWY+þTZo<oZl,"'~>.PE{0߅M߫H |QAbj0i|ϣ"Nzͯ0Xx%%fٷۣb~lGTcr8\o B`<`yA>$]QexYtZvcI]A"͞E٨rM2R5S5t IC^;ddb6okr"\lK$=ШՀ"se 6hbk7(ѨBLSuEY@B2k铨lh}I}IDnZ~%yaˏhnA98D e-Rv?)-ϸޯ - << '2 ?""*؂ՂIvZHTHub!b!cUU^OCRv)PxiOasmW".QIIylgh!wik]Z-2:6`Ph7:$ IʡI[#LSfr ?y4dvqKTd PٷȾ2k){Y#銒,ɾdN)g̝ɭF̨^yy>tȕ3&6:1Ya0-BcO{l;m?l}Va~D)fd z.  l;Ցb}E-CR?)NzKM޳rр asɓgPo-wSL aΥsH%խ"O"\W),[yoDa.$P\n} %Ӑ>Gɷ iBQ`R)hq_գ5UvUvyB_N>q`i0}c1vDϲ M5HC6CZVVn!4 e彣t'S>9 0ۿ]o.o)2VfH>LL[_JuEɲB ޝ$4"yVEʆၖԡ+_p#CEJ^BGBG&&i- 3O ne@'F$FOn< su ~哏H3 aϦpu6vKaS ]E_LgD*l`ڴdM3*EM+MUJ3MF3=rPP"o| >N p\S$eCj~=X%x[+ؾƈTZ?ޏ1H '|*:ާ lA'e_G_!@gVT@8h BgN`Us0L~htaqTDW }eKfeA]U4.yyOVDJSEkܬ8uiTPyhKrKd;ӝ?R_ 3{s ˎek1WuLCDȸvϸhvM孬1Ub4v]k]GHp}NdQdSԎȎ IaP ܻQ]Y󁺥}|:WhiMlgHN**y2a<;{vVݯk3i3EvJk'Zi_lǠ{/"G #{2Uپ11"ccF{ =]~MSx\ ُװ"=9s-C_}f ׀Vt ,mB+D}-1o>+T\3'q}wv{ og 6=r3=:̀!e,]lcg" X,zvoKYLn!$bo  ijjiK ݟNʁ8,޷ǥ"ǒ<l` Ma^2S%9C& x$)o``NkO_CfǤ,}nF^ǻ4:pU.;Xchؾv|ڱ*\#gCLy(Il6j'p7U) dz,XȳI*sr<׉J;ͭ,mU8PUIShIPjjn4J(a?(8nn|04d(CFC 1=i9Ѳcc TH'w476h8Ӎ2gP~zi6@A'heg?OoA!PhvNi lR+N;>)f2H5/x-s&66q+j|nݚTQ̑̑ctt&>Փv W ܪn)O,YgVj+ e0E`쩁8PssvWM0ߎ :jۼرU-sm7¼tN jE%V][,RaREqHkGڧ,Ak3RTg5S8 N$"EvPVg];L;+8Kr0)i2#$>bǔ].C:VOU-.*Y_>ȵeN\ 1B6bH iy?8O.Q+q˛ zq/AE`Fof&8W<ݪݻ'ⲋZD]2q8<ͬ8m{h4i*Jم aYߛ"` SF}M4l`Z T19ɴá!Ri IX~Ċl؀ƿ'!j ǡe*GeEzkNݝ,6S)nUҼS|]̤Q >Jc1k7fd,|5&aoo5zlaSlkЦd*ޡ$$4sЬK֚\Ӊ7]:;u@SJ^s;˦u4+z5sᾮA`7&/R~ȭWW8s)gh(n#Gr^TW6lmE'{x`{ 8G'J, h>6Ѹ{:[]/.U>D3Dˀ׀؀TT "[bBH}ZDU`b=i1%pM!O֛~{ՄζߺO[A#6L~/>-?rS|lvZ5iX}wDyr<^*-Ckys-tJq7i/׉5_%Zvۭ%1c4$:ʆp uyp sz#멬/%|ֈDЃ!p$]spKLMDk񧐺:k{,y+}N&W&7зBBQ"pG@W nSmW_ӼFd=q́o)>FU11|JqY,3}"}4QAȳc^>z@sS)7KeMF֡"`: PZHNnb-rrH`Jhe&k1;>pUjRYkӶ>ϝ9aMmI6kfo{'}gkc ~ӭL='{k" 'p=c !9iTdؽ5C6%{O?T!#̿(F)l\6b =ڝ8xќP Y!$E1.}yl;Ul\ |a][ja+U's` dk1Uo]l?N9o〵~xV# Z05y6RcԤyT8JCW{%y%o3_˷s$gHy:/l`i-?L"+])\1yyZ{]L<;3ISUf=v =ż12&~Ta@'8oIp.IRUVL߈Btazz^g i 55lȌ::/_)-v6CCO-/[iTggf8>֚ǟڼl3Wyl`'Iǹ픈Oh(#N gumeFjtL17|J1v P=N~dW-.,tMQo{j**NK6K(7yQvwf(`A4u֔16;w\sx;KB d GvqBXa[`[Kpp}π[0!?OuZſC`I\(4q.G8n0@WWEE<';wRH N Z@@lb3\ۨ58BUǰolY6zzly+ÊäӔ^W!sʅTCﶕvѦwM""zJ&̇id[M6!\6]S a2r}g1O7B @~cz/5qdӸRW6VD~vAJ0`C%zFBĜÍ T_;Z40_d64kQ.UMj#6`ZlϭPJ?>BA<$H0ohqn~lV&Ǥ]ЇِXaטǯ' ݆۟`xDKq:\>uJ$,ZΚIjE*@l`]XQw)[D5U" (M=R8i1j|13w.Ӳ{ɶ?S$ldieuRY؀&pIEqsΧ .LjA#IwwByk7aWY鐲#^/=QAgt5f"MZ&Rm;; #}Y <܅ e]bv'D-l0y}*vsW۟>26A;b%’g4vfqԐqFAl'Lg; ^hNd+`ǣF%o }>O5Ai|\ͥz[Ń^-~Kt3aq~6B̕whzi`佰M 2$@O/W@ޘ֒BGwSǰ++7^3NuVʦ>/4N>vZtEzrnBR͍T+IIo'I%IFt__[vOm P;dXO_8V6a olf/g|{D? gLbb.GBaD#chKD؀6#2{z@ѥOۼ\#E/%'-gZ"b p@Tc?Z`b rKy'žLXza nl^sqJt/j1$G )~>:˼XjMm) лuP.ܷ 6v\Tzt1RDbm7L6*{GERIR5 y%щ)Bk 䙊b9KqoF6S!O^XMؐ*PւZI{@D9$ /;33UUT lAotS%w\xOY6fm ‹0QDDl]x˹6{j!j!詀>\W4\U) s{¾``,ؘCqr10 H{ 6lGM;ZB*Nxaɴ4,˟ ؜ ^ $@!{c[S5MMN_OAǂgg" jܻvN۟Q$2΄Idx ZČ1?-HG%~&䯻YBSs/q@v\8j1:`?<?>sþDM* T¬øv8i4ESL ҄?~{/F!Qۅd6-Oo l?jZ6Ma7{,f"Z.W{~vDJ?Z{qDF?(=Ol͋[`mS6XŲDJ$Nbk/gޜ5sxE||ͅv;[7զ(/xḂi3%z/EJk lhJ1Mlߝt$όhMp1F*Dl/}`gZ% xqXa$6FwqRKMڠX_f8^ 4ђpϣ$k@~NPо>KMa=yEN!jo {sXmJP`77?rXmo l@ nC9~r؈jDd.EIoT3iLA~L^1m.1Ӻ獖:1Cm%tT] 6f5"L(B Jz7H0ך$d`tH{ .+ɹrR,bpDN|&Wɨ"Y6&wZB;5*d&"2S(+#+{e\!RQT"]Fym߯oxz8~#%y-mAzj@1aia`9KwhɦKW5,|1tHm<&!lAP]'e])o00a%y(?G(UcFS*`c8Ks*"f ]zf(7P(]]U~cvLoSPp5`wm(^$ G3+I- hbHXkvt!d~22Tnll)Xe>| 7cڪ{0t"g5g}nD<"`9g^ʔdy{8-S=oKq+:jA t1Yko7B?_i߱U خNjBZ7[EcP)eMa%Ռ))ۿצ՜ֹ]C6p !TX8ܖD gMǸ2%_{RPk!I!pWkwv+֑k0jzu!z͡Qh.]ZHs9FP>κW&^tVRr-Ch{v<;>5pl`7H,AazA͞΢uݣlT3Fp_+cwhTrTUJD8mم :Pߏ)t)J B0\+ ֞cMg~"M?EW$eϛPnb 6wwBzA6 J4*?|{ qE&`jZ5w$:۬J^"\"e+L g*PYؾ6i2:ľtl^9]D(Z5dilS 9|ʾ 4}bnwr6JҔm,ITWkL;6JsʨPsSfK6BOkl޾@G(Y c%(sDDԳbJ$:R++g/Ț|3\R֏׏Ǚ)T`oFqԟC 5A 80{e`=}=pV?le6l6U;[{{RN#!HJ7R@,Nt衳,@!$.zOw7omZ4ʣ!z]z] 1m=&=&.o|+]JNJN7,D4|D?|_L}Z}nIv,`EeԷ&v ;!_ۖw}*Ͽ/hRG8l\pr>-J?׊,k**>x暣Gۍ5ϙ. 0'mOsPiKi虹đ>ɝ>aiI:kfJkSoZp:yNtvknX $(eœ6s5C jX풟y@SbD5rpgn9xlVsf߸'Wog4#P ygBi`.`Zƹ+}V$uZ,U+,Ǖr۟-y}16sͨ ۤ,Yƛ)]*|l_+yPK THX3}o5_ǃ9_ [M7l`ڵ095IRfo`eV! R-tPǛ ]ې[oXa;ҋ7߭ҴG'˞N]Z5 J OYpζcK?W>'# 6y_ |}%xg;z@sd}f^&FlU`!fa-(MؗPj''g~X%DM``=@1v7P|j ~Non^꒧~^ʚzɕYClog3Lo5\l9 Kj;H_=CJSAgNi2/K\Ӵ} aW,:Ld2=I)RvkE jODA.BtBx]~D.:P3RۇQkLVu0 1H{5c.`l@čQg3)+b(JwH:O6ɦh]K$$xꀍS )-H-(QGSGS*P{-И."q__I (h*h9o0'gd$o}@ݛ خZً킽80z" LL?>pcʏNNjzA_9;J{v88Vf[ODRRQSzzTd:@cL+Y`i+ޏg%JEv'%+>dc,~!{y r:$weH}yelWY[53{# ̏O~2,Pk;Hyl_?fg7RA)ҳ l`ZS@Wvu%ӊÀHwT-s0{e{tXd`ICvM\C3ty݌l+5]L*s`[7~&N"\mXYo5Y!W*~6ꖧ3 |iU!5}pI(֕5ި79XGM)yz/ֹ[]nqҧ4Ox$Gh 6Mp=iE)wաngϝk/Jt=_ nxcp`ЍNi} ,&)1ʄJFH2%W΂_;̢ 򺒯ȩYf9J+5t6;1R8Ȇҿ(OpB׏K D&UY`4Ǎ >KDKGJo72('tQK!9ý G/0a^E1˅ЅPTݷ^1 C}Ai DzPxPb5k|s=a?rr1qtU;Xq/^`lެ.dR@_+ *#wL"]dk3]qȎ%Obg3Ozow2ZBi6ܰC̝C9"j lfSu5.n-| Q7_"gi0n,Ơ1kč\Q||gCRYoBHJH^o1w^.`}s l`] WIޖ%='qm~^f5CL<2b]dcӟmNdM*k8Z&^c}{.xS bYI: v?.~zn|nػhǁo${KU2""{^ed,"B$32Bd2ӯ{޽n|R~.p c832WRt*[Veii:9=9E(?`3k,~NZZ͑~Ԯ,8óp5&0»]1 X GJIСE[pƥ22 c~4_z qgȗA_?:I^P4_r cnů:5lOîpe BmZw~gSOI.\B425}i~$fύ:rmoOE)h;,?Ki *(Bq*_?fwa.7Hb l{_USʧILRxޝhtPP{5Yr9"O[pee*PF!$*;L]#> h l{w6b,%;C$Vz|/=,޳*U1SmH>c8"u :ץ_Dm7"k<.aAorL~w'v!0 E뾉}*nkT|2MD w9(l5g5tRnR0t[r NqeMK'O[Asdy*}W:FkWaⓈ*2yKFgɍ‚²Ns5Oo䗅W >򴯟jGƦ1E_QZCdLdYsru0pIv;}ddO_.\)!|g\G\9ۺ>Kɻsr>&ʙIHA&3Jo71+&SJ`fNM$>[+ Ww|Z,1Wz?{-iiD>yC7n\K7-͗93Ut.3/:09ipB-$s moڦpAħXLVbDlqoLQ*AeBohLF,f^sʤ:-''$tM1!ΩXo-/0苭$͛6BN]y^X)rR%j]*,;3s*ϩX< #\J f^.0_mNey ANSg /f3wrrN&_g>@෈SS󋄋t}v9j0 pdDDA]-!pP8aL>I*HJ**Y{N|ЬȬHTTh]ǜ%Bɴн@ux+ \ kg6֯F-9#9#4)E[B^^챭'xExEu 2Yq40gO$|Wrp&ܾns*{{"+3)'FNнi[G kQWW#u{P?FzlZQ80ȗzw_;7U$C;|r,lOjNFk˖t*[Փ8/r8J0j LpYa3κ$#%+U&ȆO"y,GMq5Eu~ep7fؚ#|B72P?e?Ѡ}L|~g^ut^=D%BH1hWB DL`(-?~8c1NZ7`[)[>JQic$"y²RIg. wޢz쐰BmՌ xQŴ_!DS}*(Jan; vz![C{黸{EGme.3 =@װ՟>}saiv}b߭Oe7*µDuqu Q2:Kz7m4p5`yCNk-'5mdz}v <2Ƣ-[7oPSLk؄s3^0C~gcVV*оW%r NL1V*~~6TPT'W6f͙KB鏱gs^k'Q y0͡0SU_Մݓ.']xf+$j$jXC++;',su !;aY3_;lTyҾ^o d =#&j^.5]`jpDuJLjqM5ȩGP5mrjOT萼.8jJ"[u9ۨ&QsBbt"m ZM…ԾIz1<-j,iUw4#YV k!9W:!BzIIukKI[:Q|hvqӐ ?q O1W*<TjQD-q'';sZ >nL4۹0|#zt=ɞD*RZñ ,_۲qD`w l{mU/gխ ]0O(J7aydB>}F\M]:'6DÿpQA,;U+u*^ԚPElw 8uW 4Z{]le_<g!'4=).f5u:{Z(E"k\&h]BzbdH-6TĔdųg^-N`{Zo }ӓWMƎR42ڏbZ󫉠,u&숴YH]Gqf3=/?7>vxz3FdE v.lp1#pIu## EzuN^Kw*jȇ%Du!9[82LpBϡfNࠨ8JYlLv SZ-7HM}0I4SMOgaiRNB2f3LjFkCȯyzDŭc- s6wK)dbΨcVVb^]x+ګz;d  | 7JVsvۧE.Sa+-X K؋2x<~;cBorܧ-rƕO:l&vۧE_X>~Z.F62㥚$ܳRT,Ur jP6 5n`6)s͵k)PͽM6 i%+lm/.djN~j(Kw Q:DԪ)\Ey>G"Sla魰=2x/(ri>q›dfh-`먩lZ#o$o4?5Z.{p[D O 'u76q 2TfnkƟD&& ؉*#Ἂ6l7lSɞldz466]ĖP9JFÚV[puk6OVNÝ}s>{W`yryR\qwx}x}X )+ir 7Q2dc2gЬYJZSWXަJmܜg襡ꐈ Sš`v@B?Rɴsyo`G91Z1L9͙8FBv-A<꣬笠$΅چ=]~4[!!QY sĵ6|A~_8ζ/׬&E#3ĺbSǶ[ =Րz`{j*6m~jטy1m?|g*|8!h]qonבw'f (wf(33|Y誗v}k,i,eeC@e -pu`AYrDTm{Y;K&,y(UUhYb`07ǘ1)(H""눟=t 4s#hK$"%yYy%_A ͟$i~EIDٽ{+٪٪plfcR-nASUiWN)4Yσ͘lBw٧Vs=pPF߹! Zc _{`b_MM)w X ,?]fa6J d47CkE3#M7O$= l&Y ;4i^pbl\&HFfһL ,+}64+WS^1IJ7Lǝ-3LbMIsRZ-9\CafG=2ƩW{ȖImc !_:^dHf+gKЀP>mn8WuMYP2Jq.MxΕ|7r`&Bq]-{7=D> [%$3fdt\9K{J=4սEܝl#+ϝBd{dF-B⶛;N`͚LSƿVu=BͲ`1pRbh6w"ߟ;7Tatnĥ0)kn|c>=ܪbls hQTT1>7rz󭀧O44 m*yb^1TKHR(}vܲP(-PT_Qv00|5( ޭ oeey֔0WgrH]HTD᯴*ҽKKF~ ]~Bm!c!3OZ\\w> ' G5z3.$WWH{ùLv;v;_OE0z7s j`}ڪ]^Nf$i7q[3!r49iztah\M*VG A9[t}|D\  ɘgk,1)س.em=/#`zqHd% Nsf+u1:QVѡZ-Y-e$ru-I[I[EEO!7@@@G-ඳZ@+E<ʇ#ssn UP(RxQ0qⓐ*A5i `LQΌ䔴ד.]F=-Ie0ҩkM6V_ɞߏc"O3O6"ڇNJ{E{6o,.'LՙD[33 [=2gnpl]c1X* {^#pL53/߲:.Ϛ4ll"\pw=x4y}H3v <~ [  l`m[iq.{3DL7 ]§kY 73ΦSB_HxZk4,vvnLAFyJt=J+]{C υ>x)dE9ZWuLe\iGu)r\ՓD1D٪b`۟m}ܞ3#8i]Gdū\O`dw 'Gț l;5۱&LSJS*VuIϻ|+N1u1+f9>Rxz !#﷛ 0(%7sB&ҲT`֛iw]ϓ)xRY}`p"5#`\ulyRW#%C`Kx%.:M7]P S y"^iLkL^^<ǡ|$~c2K8v(_Eu7$L<ּ0e_1=UP fN!e# 9ynV&X҆|BjwK RNn.>\onI/q#ִZ#k<öK*.;s!s!/.T}k߈O^#4kK 嘆 {&s貔`c a3 rHD; l@h}OѽGKlVX\<^EYpD l52PkMa?f'6syI3FR5|E6#)gfx0B)~Z:knGg5 mgƶx)'r +Bӝ nJ2coxvO*VQ;B0 !㌔2K Ƕk#+fXmWΞn r+)kiz+e1+Oqװcv /^!v0ns O/tsWOUOƙƕvR%st{ǐ&U_"g«%Z) OO&_ќ.+45敔Ơ 0xVu,44A/ s":tr=iy L+SrكB#ksBc]lb; )T٠'>WӔԮ q闝(ؓi:46ߧuQ'p{@3zp/k(ND$I=rN՞vZ^y)ٻDYv=#0C$"giw l?j >_"+i<"ELzc [ػ`ڵWlI!$Dl5\;!{DQp*Dܲ5\!ۯr[4}}93g3>{?T߳ XWqg4>Yږ|zPN'Ny#Qbx2?!=D6s/~4P KSbmq ƈH#ΤA`PDv(8)L0mfb̘/4ӹTmBw\WWzYq .״b*J)L0*+Z7U/Vjqqe̙}Ï=@ְ͎ϜhmľZ"fk *z<ZjİuNA_~~re Q7ZPyRީQ95|:Fr^NL=_bMGSf+BXh_hnytٛk57DVEVo3㵜u管!55)"-(Ў~)NS$R~ c%cuɭMz/Zgu< JJNK`ɫ`%bf.O3V>6]Y=>EG骰PP14b qqqh;k#v:{S;w3׷$? :iikWg 6ϽےWIKޫ?p^<6%$ KJ/,ض~p2j"rϒ뭚"Tn)^g2vF"w{I8uF؞jJNo3XjP UvwhǨ^ <( dELqAO%>LՁ:: l[AT3R 7҇\F,(u-;ϴʶ=,yƠZla7cmwD1)1Sdk`\뢭9j6q ]*mjbsLnln}L~ *{YUNkS7Hjyϴygd~{nGo;JLYfsa݈W?_@mÀÀ TYh*׆[[^} "S<طG8w݆Fgmڭ$zi4(>4uU]5Ttttf o6o6=::z y ^eeǹfRU;xauAW9>pBG BKEb(P?3rЀh@$aA8=0PAg_*S؈2Ғ҂^1_OP!7wM+qSl6¼Ҿc+bXa Xr43N %Q|ق]5+i>6B;Vu\#zՉdG 7[Pcywz ?4oۯil, h<.Q+z=juǗl`jmIl2r[4#Gf;W=;"5nia#:wNhtHѶ +6o]C5UHBEτT'@Pt6G|tg=Mߡ ! eӳ_ijz2'f2(Lt=&Ͼ" )~U tu3?HJhbX瘲JX%Y*ԇ-cշ l@# _i?O|sIuBn"uz8*6V*hv7'TH?rtHCwŘk"FvdBpGĤ]^" RMtVYY>8F`sZ=a~ NvҸEᄿceS4 zebU:w2MʕFѓ ԛbKռzЊ~b^3\E5ԷXǂ|co[?σ7/Yxc?,ٺlN>.˗a1Zig8N3r3m] l_ˁ)W.+"D\g:?ޤ*`XOe}.WuW#;~?H3Qi}[+]z'3 5.D`۵6atMS7^SHN-e9unɂ|j*U!Xvvq|B!x˺r\ɇYkKxSl`jmϮ9QǞз&`u&;P4P,9vF;:~1L 3ybһ% 4 I?M4< ٕRVgwUUkcc<ż@,0 ʹn&> @ntzltAZImfj"dQ3#- ^[k 4=;'/)c![wq4rwLdEzl5qBJ"HFLLBGURUqLKqqNa s?FҺ0gw5OewjK,]ݺgI/y;mhIr~-ѣJBtĭo&7&yWѸ$g)* >mblNmmX=N;ޖn`ܩ;8Qq yk:*2s^>F#cU\J*DQ3[^j֢e^Ǐ3CuI$O#RS>l[i90PҠ[p []6^Iݽ(9A_Ni6R%gyh[kO}ۮU j;Uŋڗ1C*$6mv7H$-]6HŐ4 Gʁ}u.,|Ql=#km#4o{8ۃ*ufFN,cΙ-ByNXld̙0~HnLFL_:2LB%˧w6dz&rkB~S?1x `\:.fb͜|j~j-X Sz\L:l)pdqd˃L(9wZ|f`ɒ``<3,c1cgu0uڬ-o|o ``QMf?3y{b:X\Z\jdr_K7o3%;搄' ww3 NvR?n2Ռ'Q`])?%qepu[T#܏`cĻqϞ/ # Ɠ%ޱI(o6g1ۯiSRSS|uY5FU.fҿ1N-l`^[&k95{ W$DGl)Mq|QɐU'>r_GU ^ceu$ 7n1h]IuK&hQs4K&35G`G_ce#[[;(pu[gAчq&bTbTk ih\k ץ0QUU$(`hcdRDVxx(4GrqV3bB^,o*",MiY.sx O^"a!aiz)mmJ F_JKe>$`rEG!"ONlv4Oe) IJtޙA؈[zDM[J*`1N߸:(z\Ut1X&0Jݯ=&l+y˷j[-'9=B¦*6yTl@o|w>aP6~϶WN=1 JE|&/[B9>J0; r.}hqߞ~TR#lkd%Gb+Aqd]9M6!}ZӫeLKDP^{ u#s綁 l&ŪD>c l;d))Mxd+,K;H*lvDާxB RP=Hj[3 rrDFSCߵMƸu('p6L 0ev&<<-}kzQ!f_+mlr8.sw?a5؀&4e@UDlpQYmq朰^gb x4ufs#oBFuI얳&ma}xG" ͮ>p{Zo CJ ERl0T{ҷR_l6|?cX28/EsA I.7|ˮ%JBS&)9;ǎLJI!TR!+2"Rޒ=dBB6YJ6Oo|:)}qϹ\x+1J x:4Y d)| q E| Ո$(FH˶VV/8 H M a.A!kbþYs'O}Q}Qea ǑrĨ BǞ a[Y]t\pDr;f0W4W|HHq_2e||Tz7JNh,D,D={oXw A 6;_E;Dq>8W?]v,Bf`m ;$&)ߨ O?:`R@v lBN4etƉ?_$ѫ~W lP`.l`]M([\F7S2M]%4Q7ٲc^ntvl`[[/3]X)*ģ`Fmg v* kN;vmTun#g'"3W kMk9ݱ'~ w!@tZhWU.|?O9 &r|YĻz:}O;{M)ŵx&DuguFׅ^'lZ6[:f>i3 h 'EOrAI vB8!I4@H5%楰,|f5.$؜r-JPP}c2L>ƼHjQ*##"E,, k.HϕK9B@?ssO}/ M/KKG`)=Q?czAmm.TT0Jl8k8g* ],x9S"<꽳Q <G؋%+"(}>n2MЊ!]d~[bgCl_/6;-h5l~..&Z<ۓ}M~z|E'_~KU^% W;i3:OR'v׼+ܻeԨNf]NO l`M1V)?6[N}%E'-0\4.?j cFXZ$RU_D(N$zzc:#N,ԴcKwBM?|؞UB$i`ۯNWTMaJDc\vFThX*ZHE=ضZ{Jzє:?bfBK0 Vz[RǂrX%tлyRO 8}.49~={m_/+r3-H'HՑq7pA?B?O_ileRt#vhW1Z)EHΈjj;[UbFq\=||&,t 11c --ݽ~)\hDj4挨~ Rfs!9wŲdf[Rn@,S&4 Hn\hƹ gVo뢬\^#Il.~.D|Ktwc}W3?"KM9@s hc{F;Q+ش˻8}4K3Qmj]GJh2H$ t_MxcHeD2z}/qqg'§K^jKw{?lG8d pk;){B -DAqBuzJ|ضV{_p;\99bo *1S3:M}8;rm7<^S~C^QJUZ^Gh dI 暸+wav;uce5çc7y$ zͮ(WUQ\[2m3mᢖRKy2655-ݑțѾ X%Zp5 Fb#B>=0:0t`h 'ۃ7Z_`\$q(Ox ׾o(u(ᏵF-/3"1(\MyR Hn,Jele,="_gffLt{'6@!G3O>$$J53EoB'-6[S%M]W-(׸/._rZ,:.ćJdʏ y.3'L97[S˷OOq:?'#%>VuG^h㘶{\kbM"KcĆ!ߑ+DGx#*dzwYNY&ۿxG I&{(UxR?.zw[:|X1ڭ|ZL&JTEې,X4@;& lkjM4.!H =V_.,ǝ\sh٘Vܘ#_bS)&չu{Hń"Wو)5> Lna|wFDB _3]F#5M PYfRՏ lsΐ4hLaq>p9Fg|`N7VtX _brj2im&V4.H{wqR&j^Y|"kt?j, }6֝9k}ΈkNj}G7 ?'x.ܧnDx)':Æ$&zx'YaY)~7gS.x2WzG݇-tHuQ1gBQZEQĊՋ 7SKJL"=9R,&YX2[=aGz\G{euu0g@mw E##W9uԽ<(.?4?qgmffЯ1fo ]ǎѾ:Ecwto^:&-'~pvq\xm6 %L#XvUQJcLN>ec'o_mƽ1rs$Ss<%KzqaWnBF 6 >rznevH ~:WB:;KpضZ#zB6L.@65Z|1{4&ޅWez) h%*}{ % 5b:qm qvCʽ̌GVl 6 31NϭZM+۩1x&m(7[_A<0_Uu)ڗUD3-sQsT‹T!p2.ĭbu 6rÌгcxؕ WͶS?0d\O\Snw#^""Rä x:~ %ە% fDt*лm'Ѕ *:p ̯ T>.ϘAh̓F݉_dwCT-Xx+p݀fq_Nsв`#=n/m/u/Z]Zo)a#R~  G]Ouɀ@°cͭЯͿis,Vog_'bq4'%V .H|6Q!`6TM1UwК*;Mtn#ģ;6t}`=~"ԑi8{nΨ4FLTU9ef⚝+5Vme5g >tftLwV BCZ / iDogػdC{0$m6<&P\ $&m+l`m0- QȹZ^F큮v*Oc:xh'KxͶ lC}#O[v؃d]:-kP58qӅqs X|bLqW ɘ#c%pd# l`N<;穏߮R(/üS7sc *E lWJ5M{԰ +2ȮIQ5xm\O$mYB7*%5@eoUcL,0/„TRDhOklOj'zly釮79CN8 JjaMQ[!Mp)iuJV/z$RU/6K*/<>-wzx[9lZ$/ep0FKh\7m+u\8z:b I˖v3BDXnB8TcgRhɟƑղQFtxѫ"l怆11S+Skl~l~_MNVESˍ5h%ܯϳlR@4í!o2|\G,,4-7-_]A}>3fβ uapqfw>UQ|~A{p%ەN%7=--d5t_8sdOQ|GXN?0-Bph`ZZ]%"d-(WpLQ O?.Zs3Gw0:M8T8`|]+-ejOb,/+ u)DJl_3ԠydyvԔ$AUqۤ2<jN+7Ȑ"N@)e!UȐмU6W{tjťbŮeV-2NW*֋-DEIy фZuxԾ&P2XT1^l;[~6bI)!X;`G5[D÷gTz] lGI> {LfTJf7g:w YBL:⅊B;aQ~93 ;4M3LL@uD\;IM,Ds;P3 E EjTL{qh$[Phbe@l(6r!C"vt,,iӅMUR= iFVbɢL=:_ݝQ;{POcV|x?,r4?W0W`nU4>>Z֗ϙRr_պoܜ)"$HT򔛶XuN"ɲ[0.A} qeՙ7hLl )zinȥ|:⊬JϴI6HwGV=de99S +uL.3$sȠ4kH:Qt+[4]9sn3WiʘE &aB ws~ڹO@ɡ6W؉ mW`3̈@>]+k `ռq>֢ТȺC\0Hu ` -U(Tb\é 17xuJ'}6 EU/1,D$F `C0\P P MMY||Rd>?z4YzG$vïiPX֯Np^, + swGttHH_a/O00}iʗps_eŇ)M0cKޞh 4"LTФ3kR@KJzwzVQ3fr/d=eHeMok@qcyPh`($Ȉ=t5[&m-P_(yx@,LIwk ,&=g U8i77r.$l?g~32.  S仟\.@Jw?sq{$b?~v~gHWCbQ&Oc"+hz|&؀"iY*C`zPE4`InN؀4axp4`R/B<,1)*("k.̝)ͯjSPdLm_ʀ,缡B5Vl`YۻG^ IIY@S,bOoo0q lƘ[NM0=q;>a5uӞql` *)_f"ϝg"҉RFn4wOr-#!|ӨՎI5p)Q)QSʥȆط^;6'692ֆxMI VICmd= #S?n؇ӧcYi\`zzK_O&'ԨԨ,,ٗw @މ vSa,Qgl7D)a8,A-AMk3#""*c}c}/ǧ XX]$$mVnVvQ[Mya-z{@؝;s \1R/j2c6;W!0/.'e_Ζ9ǤA^{s濒LD{4$4Sc,vP~bֲcmx7;KAv9h'f{o~pJ̉-ݸ*ڑ8QƆ7p#>êQQB\{cZ _BTzG%nPclb|A;V_yExx]zmM,m'#\癍t$vnw+pƿ^yWS.W*E ^M;},hJz\op{5(/ꩈeyy^J98:`_PPSw5ꬥϋ煀+F`b{V&-NyCy[*`p&(,$ D P!9;:8@[[Fn/%ڇ˄>(r6YZNnx3.|o>+i,óVǂdA Q(1 KF9|cvvuZy)/pg= ? k!k!dv GDq"}"=+!n(6Ṱ"I,+E?BP }Z*Ѹcf>aYOYO֗.QTⴰ]-A-A xx0-4Fd[Z3Zp-8KeVt F/kTO$J JnYeƚu{&hsr}e'9?* OSѹy|ZofyW3 euOXX]XLo(Gi}`;d> 7@ `Jl8Ic%|pфhBDR$Wn6]_{G5`G@dE@dEd$fHt*\>(+rl/QXbza ٵmhs| 0[X^obHb${.] ҫUҵtE"k$A_`~/|rϬ6R1N>NFTsmctr]hH1P F{z`56b{-gN|!94^>8\liy̬Q򣟌ao6HR:{EFW,ؑ)"E9?l'Eѿald߯ɚE1PTxXtŤDɿ> պ\4BuJfCvӟUp!kꔝZEq&G0no,]d-E^ ;[ӱ32F%`fk ;Ywc|ϓ(6w3մ>d icdB8b&:da *,>kO9q:G3x`"~9[4BS܎J8QpT''G7mT3`$! u41Fy tD6({~F Iݝn0o7oq`nU$d;f;FE뒭}v}_e;@kk@j"uE_cl\9C9CZ|l˱ˬUrr^ZZ\]kw=%A ;~rMr=t՟ > Pen6g};$ rKeMsxqvwұFZA j@SHyójYܺ$n\;Z@GBj{-s;D&0SƘiKsuፚ*<9.怶G 軏d7F<'JKyrܡzsxό; uPL}W#bس]M+Zh"w$ ~Fg!`}4p"~J};γRiUf%^S)6UZ;i5}I. חjnh][.i/g)|YjB__SJ!V;S67mvMu=>lr* m FdSJU.{k%)O8<"@fk%#)bHѐi=WQ7E>H FV%ki#GrrCxA˱˓˄uuoeeC;@#ñ]\Xv2j`Ô 66VG~qqSS]&>X Ože=F5Q~aLb HJ[;fT{{oAi9 (K^i)QESضq}])=z JP5ĵ@ bwhM4|]7'3S{ӏLJߧhvr +$!LR)O@DB6*3AgkWԊ[ﯛIi!Qrh݊ SzNnԾCOn+12?q#.ICڋ<%Uē+nmP5+rF}?Tª.KwdrKP<~V5FƂ~ \T wKi-Zs7"َP+MfE'ɢHXlA4Ņ;7X̡s5 3b9"%C4hHto S[]@wUd @ʖafy z5*=Xb;WJ<_st֤=2R20og\1IQ z,PU2Э**&Nzati)2k ̅2cc-0J2J2Ehň$$vv+Y+~[{X "x8b=+Cr0~s@#e11tJ#u uDY ƏU,G6s=OLTjX-z.Is;X_kJ^Yx woԀ݊oW]4_PĂLÃl.g jͺ܀8h&91WP%aDSn?.`?3`A6~k)!ԅWqfމlԛVhrB1ɢ~-Kx@ׅmgvo7BϐP DŽVPz?fsu~FQM#L==9,ccKEf{zL$G_=^ O'嘕,yD=Zԕ;,8f&~2nZaWΚu>mͭp _sՉQX/'J2`\-E%찦ݺ_ MuRҿ;Phڕ**>!dyKM/kNM_L8[ `}{'e9&Ћ}|՘mW>^"PqS`{JB Gmmd_xarrNNL1I6rkmQORnbOÚ5嫏02>,ip dY{AjG  D DԸW>ώ1i"Yo=^f'}ۥ?ޣ|.)ߋpS+ksc&fy!=YZh#1H*R\lo}^^Ysj\Zum rs>s"04 cj%6ާ#n#n M*ς bVC;ʹov߹)UMH1 {lrSPrfoiX[:LwWW@kVbN=1O<3 Wv1iA\ہҽ5 u"۝ylO-А`žӣOhd4ylo퓣u dghߪf:Rt4<.&PktM!+t}XOMܚ[ޱX@mDJy!?< w64؝貟vnfrB:Q1$}1\ntN/"lZMcP/}{ gº UY>0fl6yMzYw1"eg YHOߑ <?Iڇ-$$3/m5wP!%Kه,}_cM "k)YZdN*e(#KB-BȖ-7]OLI<繻zt9Ι^u69q6b4V$Q5:Z=J#b`uwebIhuS  eT.$?46jԕoҢBs 9hnanhj}vABaoRu괓E-E-¬U(._Ѱa mP6}ii|ވ ZkY&8ez[lsmN,kkK^q0Zs$yމx8Nk;2 lLn>% 6&yu#*{~vwJqN SʯR$ xE?!ouClZٚGOHB'c`o{yA Sƶ?혶_]8u~=aBXR] l5Tu 99Hǟf$~G Z?AdZpޘ ~v9(J*+[=ְbDIy'mfo6.Md<1zsgn}%7J֢5z msǠJB[vBܣϼ5~L'Y\$`Kڴs$4^N33kx}X:iT~h^A+ DMskO@Oh9ӯ_<=uNYΥrp 3e0s'Xx wTlvܤӸp9M?*4͈(n-n@ecgccVتRRӍ\o`#ttt(1XaGS,l:疑i2ѩ3&H+ ^jMx-/z^t0\x(}Yk-^ z/}h,8.N曯i1M_E.eC+ߎmΈHJ!,P` TC䳴 ~.ߞЩ7mZiz6\{y]m]yJC&-1KfPXcmVI:Əjgu&n=zw:Oi HNm~V9(!/=DqxblV{9`1CwWJ[$l L gj)W{ 9+?о7hlTSEz  EE03)!mcSkm楈|ۦ ԟ/ݾ xA2^Tneiei.cZ(fkF;ʑ?} HiNiHRMiڵ̌ ?I~@ @MW|7yoo2*P]RZi̜+rr2(!7!7)c0%}TF_/U^F({=iIu?s R@K@k1 kuRBlϟ^;XXJ8׬F8+9Hϐp0|68*-ev)[8C٪fء#jgR݇jƹmǶ^-^yO2b~ SH\b:7>SxO1@Ǩ?0yhƙ̙GGaj9%G8C?W"M8ÇƝiDǂe[ltp5Ӓ*7OKy9zւ9>R?X^.d뜡T0r:.2#6BV/yc`b(0Ἇԧ|S,'α8a۟Ӗ$Oq޶vhaHe_$8Fc*[ ]-ڇmW6C&Z^]NE|졥#I≹z2؆m&'`@:KojEȑJOfo]k\ mwmTH,f4GR63ܧP)2BA:mj^ :)M袁ˎm'CF-|Xg[ÎǁOuEϹ0MȺYЎ2=gR^)Љ'_U"RH`C@[#zh܈Q}q[dJْ\_m/rF4>LJއHDlFFƈ;.)|oGyZZ񷀄%D 1P Yu8d49}eYP*N%suQen˨  kuׇ׳~^ӏ\2 )9s |:b3hYhBoœ'KIqga::3m~ś7˶Ֆږk#hι^\$AFuc 9}~W3CC3k5agZS|[/\<BkLKS*"2oo?0fiHl1g~p0< @L=.4whvAb@\1CEDQVsheddL m<hAB1SW\2\"NiAݳi:: cG @Qv&DdnG@V3̵WR-1 9I%2͒ş/į\X۰?mDz<ژgݙ +?o-CP1˘m3 ʖ؆me3*`udM1+rʼpLfu"gc֊RbFY:1\|"`oh~g@Sя'҃5ö߫46X2 3u-`z p1jD˃`u<YZR؆mi9w)V!ËKdtdX+)o=mᕲ% ۰wkgO>6N/OL'b{5%w\/3E0g13FgvwƾR4j_k6u/࠽fwgg Pe5g|/2" .Ph 3xns!Ag0atdttY5*d5=tݼN oSXll eyyOLMlEAMn/qπ.Cme,,(@׀7IT QQA8~E,w,7$_y4U#Ly28H<%_EPtG iW6SBh|K ""e(TX;tIJC< :fl 1}A,,%!;(4=,~6_3gKr j=k=/O^#AB/>*>AQ*J*{30čOU%z~ak}w֗Kd50Ј[* .؄0zȘ[9kGԀ6}x)c oS;l:M9)N)UR>($Q^}x7˜܂*f=6cqdS$ڍ}\yPcC}yv=iܧF/$O;6#2-ַ݈̺[/9(KʛnNjX,2lP65BLRq).a>v>+R%&J9IB+1=U7l+;&%KeZKL\(m¨ѾǻOz`L-|\S!SŜ |ز.˞Ӟ*x%d~dtY<Z 8 j`4]7]H|aRkk jdjdNR /aGR33(rX%AٞXX3 @i) ,{(ȡZFddoVr>o ʡĤTuUum]]6 4b [{}V414wf@d5!!6zł@hXD..266>ӴYDVyF߁)eXMGUuiOB>e tԝq7BL㽝,c;tt)|_HȎW@l"Hwhj,e$Yzg΍eCi͎~^4 "Yrص=RXˋoH8tL?~W E4fd[%h:G۶ւ4<1go5gy:]r鏴>f5 ^%h[~F1?CQ42 %(T?^5F2iM&Y&-x+\ŴS c%nƃ&Zj'[;@.}v"NK86y˴xdڻ3W謠s5kKZ"M;u{8v_Zdj G1Kp/@ip 8atVp;J/CAlk؃qL>ٍK BM09sÃE){I$ lB.ލAYOy G}~unËΏ1~،%Sff~߆x{kGp>o=XxGk27,X]A]IW""kh/q@*K(m1!/yy ^{@ttRR4~ ֵص(7B$ZH=yynw~KHʾ ͈,DS#* pptVJJܚ6c*PMM~v29{ 6U;]s[4õǂfuPGZ`i3AYoVlsgWTBG̃qdN[DX`LD~4U<1zC)@}XLҤ~BB1;asYYޗ:'laA3GFedekq[#`77Wtvj@ j@ n8{PMjЀRXʒ\Vq:&޼=4mY3|5ЦQnA z- v_hmnILBRp,һI8(u`MzZA jB: ?=rקo3X<6w'rJ)CrWwAgkOh4^hIf%b,}`_,"BwJ=$#m~W'q!rrpBڠDOj$sӶUqtJk3ax(ϭ=j{u=18-ɘI V-lPѪq\\ok8pbZ %Q:pJ/0]\kk: qVqVGVp~5'5ܑ{t i7OO7K 4!\O4dB~+i}uj&{w[V Lh( Mez.{*Hm5b`9H{mF/QcF;t Yȡ]$0 ̇yĺue5`Jy`JPs>EكްA]Pt7 DBS?MM}#70=oU.= jO#,{>_*v^[j|z{{bB jf{@؀o"ÏvSlE;!M5 `km 5 jE?bNwf] cf T!:ӲTϣl#K\2tqr%ʞya ++z#&sA=K_MI6SBa9/\H]\/Px< BBN?ǀj ,PŇ5UrUNovl>3uM[aQ(TMu(ذO8v_x##}f-tԈEE,'OiOVR$2&2&z&}A?]yZ}s36Mь@Ce7^y&8JR?w9)rsZ\Tѿ(n1!0蚆y cN\C'Ԡ+1`:6w"S2M ؎4 "N]z2jwfygFJ94' )kMm“3͡kZdG30*n6)`^VD|nГ57scHkA j?[!Owkvj9k:<\-wy:Q.d>HѕKaRxI {N`ɓ|LeQǐk iLCFSu}ϫuu|*afM$_U?KD%;J@ sK|%eG# 0B(Xuu ^;aXp*p*Up748 W}9 2~~* &&UUÚ{V~lY~sXf/NOm h`ENW7o%?fyT WMMh&sg^収(2n''CWM=A|i_\k|搪@$C$mdtpd$00Dˌc~.` ?Fz#on|70N'@ZSӯ0u3u;1 %+0+A-9IIN# ` { azsK-yI_tI'Hy o}Ycuk7_xL6=c]Eꞧƿgepp՝eG0e Rp+B4}+ʔC8'AcA.Z"pTLF[G[[>@w0|1|!{ xRs-۶!Mb, u5#86KJ7_OPiΡT+tx+V`YC?; HcHc*Ozy6i'Hn;; |?0??V1*ͤqoӗRZ'1g=W)o;lWh[cqA֊L>%Q5E=tPԹCYE%EfjtPnR(ov$=.CNkmn]g'0VƖyJOl ݛꪨC6)!^mmҌ6q{.MmjM֌A+3P@٩ |ҏ8<7 [r6VEFtը%|(˚pw4/:2W9r_YH2NHZA!ͧl>IPPx6@x.8(E?n1nq=I (Gsdx.sclҭH0N0WrPPHEIET-u.u.љϱd  3 d(N;EM.?5->>z_ЭݯHHx "ϒw$ ؚCҫ>Tۂ<K#]9]OItB:ݗ,a6U:Fףa )E7`'вi 6UX#Oһq4%;G|bG!_$961TyycL3PM#nē?FaQ9EY Ͼy@ US&%!ZYUQ;~2I7Ѯ\427-N=lh2 ,Qa1^Aٔ>}l)^m$rޭwh"Gt޻o/ld3b`KT'm9OOvِfoyHڿ$ EqH=Jc_XO|{в#'MOi]Aե3η8\)6htބAx?YB6d*IeASO ;>JyS }ʨz-в$:A̱+W ոD= 1j#&.ơ ńzlMXK(۾_aLk{o~P[ۤ y5zR6ì22, ,{ T$Y_+Ҁu8rqG)eJ W֦do0:jz"z" 2~EgIF Fzpʙn 5mMgMg&=2jݵ=Ǩp`ph7aKI^!پK ӫ)R6KxM?~Iw|Q~~{;˚E j'ى{le(vN$g,DDMQ' ъ O\Me ::\&5 MsPxkssP6CV6]s.p<r NRl MjӾM&Ny֠UUG 9!'\#\SX9 PHdģA6h#sap[11Gci@7T@lef kVZ`B \nrC>bbnn`Jb7Dz14I jVӫJB]eys#`֛ϛCzwWS8/=ȊPnLDjllWwNv]iV6V6~ޭvL\ 6naNf{=fkbmX49.[RmZWit2"8#zِml[Mm=˙«94Nt]tca¼YzWڋllgʨ)O=K~=<5aYCTAqU3ˎdvEzLfK+O uskN\sQ VE߫hj9| kB!5vkFH-Tb }6GtyÖo(O0pv)*2$k\lD^G.=NPxGH;}GMW Zy4E.#,~X`+vW`YL5AC]ϋG<W:ۻ4SSe5jn98CI?}3@\[\[e ؀%iYV@!"8ЭaUnP0'AX! Ũ&8Qf]v<@Zu[vl"" { {wM?p!cy13B2&SduI9E%jOK[ /[>f[cu;¬8/ݢޏ%KEriĢb&s\m36J9=a~։|[i)dԗQg FYM-/x.ĔT_;c$HKt+bi{*_ӕ7LFiS!>'~k=q>r:_Y+\==_P>xkn6/x~OU/6VN䱆+S%~"Z Wz?Xw7TB~/Ӗӊc8_UZ+l0:Z5p c + EN;}JJ=j GI{"YYokk{1}%bҥYX"~1X]{:_}gIի#.+_VNtttF۠rnC׳g3/#ӥNN_ODD}|0NC/7ϲS!}lO%ѭJf==9$FZ"nRE/+k6hO*[RG0Tkg_+ek9&=OZ>ߧ z`&!t"3X.cHqCEIDXM|1 QJ%-yӎd C/OmXv"my 8 GY: 6}Nhܵ-3rc2ٞ({&2h;`5vSJ7E{6ݧI91s[xM߇ 6WĘLg߄3>ļ?g=',\-DziinN")H5¬9yѐAWdǴuJ\ԘT`UZ׬5|t䐤[N;ۓtd(eP;/ϴJ|b<#&$mvpǽznm1{AQe1v_HM8.#ɇUpIܘxP&~a'u]<>hI I? }pXpKͣo- r9ĝq,r⑨#Q29x0el32 ypb]]*&\߈^v (j7duFpp\++Weɾx# .^0FmaOžЭG$+f}LGIQ&$4( P(ϛ՝]]t||C^OSR%_h@Z%'qjYzkFLǻv'ڻB|/^OanCF;cR|+=|rӥ[1ÿ<>9*֭܏wNtM~ҵ ꣆]^/[/Ss /QoLwҡCmJJC\#W@b<@j7""zf\m0)>x\!!{rF`m R R K!LL|0oNm&+&+n߇M (y_PŢ-gH#4zj^1 aޤ$TFFQQ@XE]EhkfmfC˖-${S CCz˴4i8~geo렽+ĵO6ۥbťD:Mrýz:p],`H6#٫ah`{:,<-'D "io r7vtt l E'I014xgh>Gr>|&?l`8ya\}dz"5+8ӻD2ta?%W}/IB&ܘ-.WG8gQt lBҒJ=[SjeZ4v\.'*Z:F;Eg՘S^ܥ-Tx$8i_D[ :n i:CXm(|nxddY Ea2uc̊6^p"_%qW:Ua8ӭa{:C#ͭҀD~VjfԃmNWtz `.l)/a`3b@1(O1>dr844[+ pW6R!'LAŻo7|--9XmRR֮K߇wPMcsky BE9Z'wMؐBH{ģ?bxXSTKHxJ7v4v݋imLQLSU|=֓֍RWXp[!bbl܎&G߲72N = Q(eKcytT5)l^[x"bH0v:ЩK |y~T29J{JZ# m<MGn֊7~[up ŐD)E!Oݵ7ӫ`ۯHgϑy u.lT_JCtRL8c͓r\#${l`-qubzyZ38tB|'Tݏ8 lTܘ)6kUҹeR*UWΛ!;MjhBC7]{MبG.ݾͥk-*ҼKj'r~62|ws6ӌ'{:Y±D4P1~F¨`H,Y{uh9׬Z%/+|퍦f?/~iǓN!!%ÐavV%zz?E+E+("!NzYi[R 43~ R([BqcyX.]~ٞD,p?SG.>mhh"X$WO} ӈ7^'_`33ײw--̺؛*_4ʼz')ޞ> [Ss`}034MnbkCLT5c-+mnEki&|/"L.hmjW̱/WR'HhAv$<+Pap^Pj WCHW[:tXy+ա$ޏCXnj G]Pr-W>vۮgg5 dY+2ܪFP#tA*~DM|AƘ;XcK*XJ>j[ K?odRFrݍ %r,W]bqI4T]8@xqDMճbDb:udzD#~^3̱}*2_N>Xb]R8A8A6coСvpz>ca_{.E!mm_k 3t!{ B՛cc\IND [C?7T°=>Sh͓/KоzF;XjkGh DA~ţ.gg"MĻH$oo+SnwW\j]j:޾lt.g;2"xTȽ qnXҕ1ިHn|tLv:)fߙ[~ny^ZgP0kOsԲ^{;<> it5[чb79rIRPܷJJ|LMtՀz"n<{3%m}X}/WfHQAA:o'&++`h(e$:fOqSSY9ڏJ s꒳#lvDմCE"_%2#FFˣ}P+Ţn/lofDZ@-(IFw"#-u _ O=ϝUIs%ަ/kǗĜ۲d=WqPж'fGTkX+2y);@$$y\( ~DLc8D1clr2׌Dh/xbXWwBǛ7)-VA wJjdLC挱DP ~6-]g̩YRv&wzߥkљSs)m e$P&6Fl {O-Z>-h#8M$^ڕC؂nZ>uchM\ֿsja6k3= 4 S ~ T |n & ^PUa> 4?-?pϢ`ͣpTf v%B8*ҰW0+C45|Dw E{$8Y8||e1haa l8돉UU?`mM9݁ f@/w=f4«7u<_ 3}J(-wl=ИW|4:F2FrWLtޕj׻YDO5NfC4i2٭gY:!m iصFSJo;+4} +7Y>C%m?왽/- >SurͱbsF?Ǝ78$VB^x|d/ ^%jh*G/-L>`%T5~6+TC5kPK(fH/W%nM,lni8 60k_Lh$eDzޔ>˹a5၈aT{7}$=Ėqxd۹mSYC dm)TQ mn 5 hMRcT;>nBcAP 6ջ 邃Ε[PID0:mWag:_#G|P hB6k4x:Y4D8>*F2 Z[;Uk֛y nEȏA>:=˜è¨` XHHjH`cήU =T$ feIfI ͑e]&.M!TE&'״㳀֧ʓR~h?4'" +s9&LE?:"t_eP9ց,[2<_pzlKlK3WTTfm"":F5~xSOQ7>aLܭ|"2ORL)g3z3zOE>cQ;BlÙ3OD$%($@O| kL6J@JA؉ hX# mec4XZ,''-@tSS:Z>Z^j++P95?F[d(]Lb+]пJWg0plia%I)=+&NN\jg6M}g*XGzx`z?0xaHtB.w["TCW;V"(m\$XϜAZ>H 47i_̰Vd_ _ r;M@3j`eLÁv3\Vicp+wdI^2h$YT7YDS䏾۪^ ;s;ߝws?߃4î}1wA *Ԣ 4g:j@~yE>)sɀ3Br:}. { ?B Zl+oU P&&4OF0NNms-ZE1`: p. %,tcx@f ~062ca-1ik83{f4&^H A)#**}J9hQ\&E)2ߑO\n,8 _{ ~U8Β’:F71+ͼ1#11(GжV~.:zvx E~E~3u4XD2|{<r.:/D˧ cXqNFٰ 9.p<ϥ7|dM'ҵC^N7#)Nە}]`UWY2TvUZt&CsZU1!-m:9VugEXfp򻋨{6kZazYš("'Ud]g"2';f]A4MӞozC` r*6D/0w,.-h;G:ǿػhEdHd*B̙E2C2V!Ȑ1Id̘!c2s\}4h]VwZgs%f=T !)â)ll DD3A6CCaSaSl_bmN'|@LCN9bLu|@e~~Hn-Q_I5nRR!0â00il3[51Jg6S@m7gĜ/=c):j*5{;R$7A9/M\|W,}>TϺhm?^v{+05jock= Jq.+GD3n䯠 B?ͱ*vnekMvGc^-;;$ׇxB6}x4qus~ S}ڛ$j#FG>=0qORTMOg0?#{͡)pp~BZa0 aWͨ<Xڏ#%ܓҟ.9A7p:aNƇڏ9'}JN˵ 0 9bcFsR Op=1xn gfM#foowOy6)F&i)n"^ޒ@AN;^ s,qa~O{[g"%-QH*5w):6Irf߁ qnoŗ_0ۓeϼssszz9c<~o@m1%ձ C?a"/0IH([S9 ś;bBC%i?ċb c ŢŢaw.~Ǥdm6 ލt`-on V#ƍX) \5Y3sI )77WW3ur戭txGBL^O4'ͧnmwOB:-a4,+~xhe0Hg{S)=1]*ϗr#EP9=_b|W0R\n鉧*iIzބSWބ/L-_!CUYwo Z"׮Yd jP-HG23Dp вu#֪_" Pךp#λ #=lRAU^ ͬ4Cl kI57w칫XF[}YCN'=K?(n#S2K.͆Gj.ߟnd3Jw1BPZ{1𪣇Pʞ%:I0e,#>5qi9~7Z3{nK|vc}mAxdjL+""u{CAۜ̆~V F6#2B( bb[?PZ 1͈Z$Ec_Q%G~F=qFtg4^v܏hrlw4Fـ4ΎR䞞ȞHDύPJS8Ny6)d@8 J" ! 9jf$(u$ΞER]erBjj`X}j(\$E$뤥:G# hmBBG7"61*uZ60@]k׸]GXۮ&@ 1%Z蔪OOTL հbPzOR[{[S}Md]a>"ÔnݹOGV`EProܛ/ޕLoQb.&{+@wP(_WwSS>dD1ΫD|<ꌜ}(hh- ܋RQaM"I9gr|'jjJi5[ބ!ܐ00 jͫm2 Z J4&UXe%O?7nG=Cftc~ڴᴡ44,ۄZ ߇؇]k>w  wv{WkxԠV+󼪪4}g/\+^H~*4 KlXNY#ʹe྿M9EwhAMO4vDfĵS=D_B~Wn%p9\bAlwgmH5~rAL%yq(,͌vOYAgh4iJOy$muO˩~ws yxHwSkB ;e3Hk1%K Ǻ;;VʆmS#WP~ _һD]]vLjAh5"I(ԠO5ͪZ-RWqPwd rifG^rsgA>u$jdIr.=knmϪj1Acn _uu^ q;a-8\ w8̑'Mل:'x97z&̽Q )ph<9{xMӓ.ƴfrP1D&'Ȋ(ϯTZs2J2J""i80(fch!ver'Yi< JGT4a ȓ˓wY|'=If?ץG'k|SKH4!D;U'#󟸆x YYrttGGEhEhoiLHI1$GG7jv@.xR$澿wUbrEˏɷc/g;5"^Kᜅ׵5:EiyC}/T_Sq7w5G[OoQDR͞$n\݌IWO7ssG@Ayfbt'n )S#Gp:Jn,w΀cT eظξ!{C&(.6iZMv*_WcUc%9m//XMDrʈ:h(ֽ$lW>5XXC8I&[¾GJAJAx"Q"Q&8Ӹ22͎jo`S)ϫ_xya\u}̵#7Mlڸ[яՎăg( _cS8*s//ժJpV͔ h2n3pɤң XjPZ0ܟݕ䁄y#\CϞ:3D[TF_c 9Ob裘8daɲNDdN/~bN }(,.L;eAGkӔ\h8nf%̒(1s({d%#{EHu  EJ"z;N~>>}s0ji`Lxs ڋZU`XEM<;;xĩ#Os o^@) ^[wע?J #|DwNJG1}{لγ LB#)+M{&\3]JkTR[J3AYYC ;~ naqbqKHX5>P"U :e杧 :3 fbELT# _"_401,0$% <.3]!]PEP3sW7DuB[ S8sU`@@q?v??n*3?;?I[=/. ;NB9_Ϙ>fMeIZx:Ǎ~FG}\ك|TeI݅KDIE|luLj)󁞏C~g;z! !3\S&uEDݡfg=y~ w" !QC_(<2 1Y{~R4M߸QWJ%jA,kˈ}ǭ ݀ו^/g]JlN'=Llodi2ws dXԉ|PtsV/c%.%ub9Ru֏n t2@b3ݣvqk)L ٣(-(*@7t%\9*JR="-+¨$|ܺ>:=;=έ|c|u*&z=7-J ݀&֠|֒r"H+AMT`7N%A=gMZ* H5gWbo44EqqCXŠDxcb3ww\suGo! Pѳ#bzݾ)@<IF_C3$$$ИӊjjbcUE$1 ~o;T6۳I&d϶"s.`'Gv=k7&r}{PjoO],O%4C_c,I`lD)3ZQAO Z,7 !*ڇ7|gݾ3[Nl,A">ггlzE٫ 44qqo-<vƓ()54 [wll_tTxF6~?P:xxZ̼tm?̣ckii @YRk˪[yl] FyaLbt/5@{H56v&a?i~)U'.QC=ВO~[g_.Cy)!jyZe_齾ݺu *zdH@*MP8;;= G9``!UU' 8]34O&\BJ{Բ~ϋ.^ц#JR P'On1r+#,44ЗbR k awwkѕC?R 9X$E-![çS@ Z`kxtD\?# ' gJVV1[#ba6[1m _Y_n9%z.g0k恺mnhƣs|! K4,:pDO&+Uڢvniۧƞbbk''kh37G4I}!kd؍nW6‡x|qV{~5Q6BPArbI{wMշp"I!,L.2%d//2dLW eJȔ]HdΜD;/Jk~ֶ׺k}**2D1e$Sc?~,m[!i r<Z.j^@a>Lə#?]K c-9l 4c~Ȳs%% AVLp&xQrSXeX%: XlkG/}s > b`tct   ć+yyp.c0E0jkսB6 {F|hȩYfTF3SO ;nӰ*Ƌ:Hf/bgLۦs+ٔ0o4|4\dѫS@JNNl^o|3`=cc%aw7ksR\2M $f{D}`ҕM]AXD!$OUJ0/nh7rV}LKK''@1LJ'=Xqק`u3:Q@hSSҹ移gH H vqqK-ખϫ݂ͫϨHZm[@dc#8ʾr֪vKuRR̍qY߽m RY,?tS>_:4Ԡ#uJC^&LԲJ곒/ +KBm3Zf 9B3s) x3=d4#5W4{j\Cany"cʷ#^lf"Y:(7%2 _6^MyM46JJݻQ5?뉘sEAoDE>[ 677%c2ls =Gi!DTXllz!WQã$f ݧm~j;K&q})ollgZZ(gg8"i~:pXƭeFk*ܹBith+Hnix[8`G˼9Q\󘾞IZEJP=DbJ%ԝ+F Cn%?rOV{jwj@Z)ʟ,#WYB #0Qufaޫ2EcUPگJ4nw+?L'DJԠO(?4ڴl _!-JgJ>hSv^N}v#te M|ljOxG%ْҎ yOP יE 4gASb'%,썈U0Ͻͅ/yFhb+F5_ڬ1Δqip ?# 7#n :ѥ (o@~.ƚ@^9JcX@" @L Ox?sjI)s**xIږڲXtWX"kZٛP<[ǟYp92? PE)XnD'mMM}t$$!ZZV ]Ro:to[~-L!p0{v*#ޜkwS^5ʍήpW)zݜq4sQOJ~~>]#U[h.ԣ*hn?2~-,\%U%dvRRwsNr# | )4ogJQ#WwAWkP*0QLM8Z^a #:qٞfFAwh*kJG5^eDSiY>ݒԘnҾk棟w&{ݷjCz\^n9鉷cEѾ$"Og]'6p2mH\NO]$߆\S7c}{7oUe!T\qѯ ^v ll/FE6Q)^oa>b""Yn@~IR9{Mӈ*VX(VGGFj(32 B`n)P%u}Hň[Cg` F%Єw?m?gՐԐ\mEaHas'a?jۯt?*h5kH3LLs+9+9zҕ 8 ͊II)#v>!h?QQ3 h wnJ-[>#qR嫒DVM7.⩕f{v,,j\x =^K5J#Xp Lq3̿;wӭP{D;+[J8͢Qr]vUg/ 6AWk'kfl  cg{mƸoIM4{] qx,RoX$jcxDI,,=XD 5m -+#azfm^RORH;(O:H϶"OA jC>DQ߾ȭ§'ugNL3K<ʵ,EZ5]-ҥ5T[}Ұ4=dY;O!,b_B j@ږܩڳoVj1*9|F!}AZc~$nWPNfɼl.i\t%(X(lTD<3N)ъj\4D&Ȟy{<~r|X- z\z@fOs9コ]M nu:H0E%ٖwltjft1"OL<*5ឍ6 GMjڗjp赏vlї1kŬz1Z>5IE HͶ7۲kXUO\^ӦۦQ`S@U@puU&B+D 8now!㐾$.6 ttA oʓ>C,!d{'N(N(1۔}\,?lC]Īה.sk=ϑǙN|gS^!jQeQ4~FXUXչ-Q-Ѻ0\/DАE6̴ֿa6/m/ms4 >=-RJPmt]n] (+7_нa뽘Fcy}soUz,$I_uؤq(2j|Cbs|i.aVR٢.Q\+J+ʃe9 V\ٻXr  ?>O/?v`]l>9XV]ztǠ(%Ϻ&&G jvm\M{eA@NN80=4h9FsWmRՄiwk˭ ftVӂ4eNǘx8z+׊-5k,Zom2h5bLiz'ZΑvAwh`.w23iȘS_nb(̨ ڟznp4-PJDOGT{uFW:(;82ϔ)EdC>:fMtp-Ѝ*H3N z߇2zG^`6!~.j3/Lۙ,@& '&̯mk.O&ANAG@r@n7U,<SoB)tu𫦂p=V tyfRww6@x`x R,ln:uu'[)qgOc>pFzc6/{  )~hh=]$E(F22ӞӞb% imXSY, hcVy/M霥Z{f1 O`6kTnG{c"0Hm k:ġ ̓:{<Έzȡ)[;D88;Bjg8?(Tܑd&f6~+?it0H"_k,KXPZsCG[tux*5\?`5i]wDmτ 7geZ+@ca9_ogz%GJ4wǚ{>9Ň+r=kE/.n7irMZf#4 od %%&9|E؏`e,,gzy> U=@C|JVa#~|>dAdAtfn(p2Q~~R`<11۷ΑNכn.qq@g8 gQkN=<&ػf>1__?4zY _H[[=C'ɪ_O*;''(f" }n{FKc[veDhDT;7=[6.n@sK-PZlD Ix[mO^t$MTrrHH"tثf,P|onէ"^tfy`Ξ aQeo ܔ6/Gv-(Q1DSG1y`ͫ̓,/i^-NOLO<~~ ͭ9;_I9dx~%Ps@&JkxCaG߼:0H)joߠ_dqqNE":y0h'f5p=Sytͷl2pkn"K;e7ݧC[Zˈ4Mn[%rR)] $2!3K{,tFI8d-2=aDR#ĻPߴ9fO,',V^cgOG˝ۇ6@tԠU[r5iUwjww:Oiȏ: 4qޒK'IO"_+{h3JQ 5 "8zU^GN`va4Y'}H{²+x(,+:f7fgz߈LXuE99&^XQo,_޻ddr; QB@|OW #Lej2vuWj#Cv1.jP5{}{#Vb3<٦@(#B‡z55ЎP:n(й,Ѝ 41,9e镤N>@ j&ǢI8EM=.s܉!lkUف 8cPAl~RkzĔse^AaI)@Z5]ۛOI7geA]d4`4IĨu/że`dž>|YD>ϻIV3 L3my_~jP2Eu&wwEC{E^xggceΫ>r QitYր 2^iPn=OB@f@`Ieu]@3g@7SMLR c{/&@f.jMgggQ^f?Kh =\เmb0c0dىk=A9Fa(ͭ ^xzk-S6+%MC겢Qmwv6hhe'~UGV;zSМn:f"}1|׋1̑:&aeveA|#[IP>>lpPZK#KM]U4W4oWd` >Nr1r[2th"" BM8@ Π;?&'M ^SppV VS H88<Pb{ t<]Bx;\E(6m+wd׿eoR).awkU烧Z)~&Q[9m@ޛYX+HInW]( Y4s@ je:jZ.~qyd*MLE~l̔=z.Ws";P Әg /!_27k p\W5]9-mwtÏ?0P % [YŤT w?(S/i:Bzj2˔?"^̛lm곭IG~9`gR{ݟE ^<ZG f{6#ՠ#~5UyЉHc˅i}!cKr57v~J`Gqr6 zpN,1bZk`i`eeOP$c`jW4 (;A{wKK6GxD*, 2;>1"RRXS*&^b橈ttۚ m?g<3P̎ -SDle@y7k9ϖ-lhZY~ÀÀrtwrr:׫׫ 7t 70wv>@F$ ,05x϶!dƻh412Fs\[Mr 8]?y/XŞ#>}>v0'ilTtf8z"hYYMF?/]W^RNO#`dfb! d)JJ֣gAp9P&A& 8FT8qTLL:,U'{uFݶV +)d3RDZ*RO1YbAYT8|<|<,:i,O||aΎQJ^""7J ޔޔ'n{/[|c# VMK7&nYp#3F$PvhWtٝ2g$=86KvO~&P_t#h۝"91tG-Yq8}o6oೇֽ0APLvzĢ}nQc,9c mB]Q*/{We0n5c줽9zxrLaǑDԣ4L\ud7o/˛A j[k!އ%}~>RwP!ٍ} ed $%,!مDE^"N4Iv5U-ۤBe~]tbJ:~}\}9sZ~~N)w h٩,*f`rJ`YTgUhԗ5WW\62A tלggy\?UrDΫ9+F``sjYq(-j-ri+W̔/<O:q0j坂01TT @*,Ƽ*h~.6Hr5Poh9Y|yioDrRRjxjxWfw[㇯=}6Z`H}=}ee2wYЗ >1#3Ӣfʉ3bx^-p-;[5|c(o%3Ȉ?gܷ)Yϐ+nz1ϒl8pI^OŧnZ {=WiЃKKGQZ ,xWXXHR[ $|6ʪaO!ǰ$ 99){T> qCxXpˎ`AT*HYӳ&a]a]ѡm.j Uu]Թb=Kϫ=D*ͤd0 d7b7iS̯wwаwwoeF)/ODN,޴#C<9}|)մEnϹߐl{zK,>)%΍[0 fi+N:ԠJvgUMChGԥ&\>ZFX[Bj{|c ?Od'_D4 ɂ?m2v:(?|aBضMFNz{%cSPڧƸ(WdSb 7YvJpd*s= lewBBoVG|Gc SWjyf_;.~pBDwg55󩒱qܽ1ͮMG^X3'\TSʨ)|YJ<_S JZ8tҁ&{D"luk/kn8wKy2A|/iikjuws)iC^Hz7T)5ɬ-g$h.kk^mXhT7m~;m [w\\Bnbnb5 ̃a\a\LL|sӻDnr'1x|Ocy~Pe5(S'nٺ NxA_pYzv99*g ?m;Qw3B'pyYh~fhSjpjF|=؞,2LP=tb>"s"3ǿDHZ=RܦM!><;1wx'i_ʽjޝ:tG$â,Ԟ~P[V[a˒˖ylQzOR1ncŸ"qE@f7]yyv>HAX_q駑B{R(ņnnug1.:.hu-_-b쳫d&MoT6;~.^ɠ S"*h'D?PgW:ԗz8o=~n!zbx 4uܯ:H@G=}6H?pȕ߳q?cF 7ך׺mo(X)V3S" 9kK9@3 ϟv`BԦ6F8_f*0@#ll_O6}oׯp7>T ER9v(LffZ1'wyaL#"ofgWسwWjPZK{#k=^OBd {;xvnigU~6A5FcF]SIT^{usfx44@ jJ$R< O9noZ:0w:΋fTiPڟNRYֻya| RSd}4G(Si߭>cxO%D91/cnq6lGd-r94FAOhe )3 x2KZ[y횴g%Z8F .zG]n-Uvvl }E!'vң m>u\8ۊ6\ xߊhGBIG)WXʾڿ\/u1sk2ZX@ zBBzils3l pM] Ui Yfb<`MSAA"Qf^xY]{mofTyECo9&feʱOO 6~i⟓s#ia䏇n{qaeǡaj 7oQK=u:`uwnˊ_q̰ l--coBn-1Y˪r{Ϸ3O4+ ⬚R6l {DVj9NpѢ4 jj;ۭȻ5aPSid;Q{5a.qHlG[83εBiF#)ޠ{7,S9 MjSA]rO5nmIun\a[LLK;˼4oX` :MP4]Sh>w[LcJt(/&|HE ="s(u<]΁ cg?*җxoehq>0ִϕ퉢g]eUbEPB.u ez\dԟwW_ָf숍v 'ldUxU XT_LM+zzHVӳ⊀[+%WOArr8v$6RVo]: ATh^R F吏= 3%7%CC%:\1*J3V?nbddksaD1Փ/ ++3j_E+@&@*-&luau!3T`zun>̎,bbҰShL%=z[J7;#}e>RKC%(?hC+K0ϫb_L Z6=D{g$\VfeMzc1p ע- t ^Eb*g"c3(6=lsqqʀQc8@E;A[cVcVڂj9;@G0>KU]twN66-[;SSҵ ?FݮSxBe[rP7=k2>xKm3X`ny]0ƄI#HS$<41C]c5` x,o98w+ &:4ʛC&lvF6Ll'.fyt%;)@\K!wN殞vRssr$$: ʚP(nh_xDjpllznIdec׀Pr `|__ ;{3U㏿ ap'\lgK(3P§XZxH̷f7JzzAO0?EGI?5Ҧfvmvv<ȫtaqxGb}(2}˖f{gg֞0]~Exo9)[.@?ά ./.iDSΙ`;=%_=>#W@4wkwO GE`sm%+<sgbh] O<"]dK/J1jM[W(W[7 )ٮw h_"A.[>y-t#n l7:W%P-˫j~y9f]B+Y.ƒ ^ 4Lڵ+Ryiz ' Y|\rZ1ڪgJ\t&0cCݯBp_ov﷑jUa0 z?|5;]Cwc;y\ *wN9ScwELBB1keu*^   fH]GiP-!,%,gWCNAxvDy:?9Yȳ-nJ# # נJ WA۪ɪ"I } C91dPE$T ܌aVm:#::z7$MM AJI[I2ڍ"eAf7R_E~s-c"ҏť-yJ;]DW mS}3T[ ѤFׅdl2+O& MwVc%a;QXo< "T'y2lk5L4H:Z (}h]^ }Ҟr(eL]yl|g"HMHIJI.&&Lx @@ yyMns>mh! UE~+0ly(sg'g##(&%.%.\% ]tj5ĽGT=r3u&u&p?AQ"ytvyV{`@{m֥P|U-aߡmN޼2'm2SJXf15L\J-xfIy/5#yJ*~}& >bo*ʙ`$ÊxwvLߴE l&inB n%u W-.чHg`}m!>3v@'8tAU~?;0L}gglsrqriR}WʻpEP#[~\GC-a/aX}Q3:[@'V] DW@_? u=}vhngkl4| PP1s_9a)]U J0Hh= ! +Lk*ivs$bzȐz+ygSֳY Ր$m´Ři6?'ܾ MX-9⻉o7_ 0A0 N`Eێn\[n? Ȝeb728pGL4/:)!~b^[>[o(kuvǎ~~Y׌?iqfvK,@?cfa.}H0A/ټy9BSISi3 pV4SiuUlI ArДE52(7mOl3&ysF.kkyTQwJ+[]p]y3^#W;MchԂ̄[V"g.%%^ggQۧo&LjNN? !,;E!![}~=JiURcR#sV0%jn1Ma#g*hnTp=tI}n=R[jѷKs ?Xk3/4smoawECZ HX,<|?};%H/E/E$- Sƍh4Ģ /6, tЋ)j f*jZ !` 5ZМ}ggĵkv߶hh߶Gl2'SQ޽ UF r}FY&׾h5 RmH0|GEEv@ R,芙::`wh˺&ډc}s].,|elU)=>R+=@5GImgvĢiN{g*ypՀSv7cѷge[ؙ0D{},Zz u U ӯ?@3P\HwG<x7:<58f*er$fs!'2}еzlKbJJMozL~K ~~kZS}bWT|aH7W|e3P᳑g#P$t((S" Etk% .b1S%ENXX\uuPӰmdOn$?r%K3ruSWֳNDY(TF<w'NޥEGp kV;3;3kQ) 0W>ƚm8t 3An3\ewx4$<Κ=[ab.g.Ӹ5_mo~\#Gkcr:(*xAڨSQJKcqz-_rv+ û<Ef3_kZyQ*PM--bv0H @}X'ۏ]Pv 45É Qe [t;,m`;b Bu\ 8OҟOG$f'΋m恾1; *Oa8vkS;&ݒk\=g $bm6֬,ɣK+ђHBiNXZ a ~w;&<(؛#)CoyErLVuj|&3)۪C>=}{GJmk^qj/`۽NӋ2v*;? EG;IbT,.Gu6a}Gcl[B #!#4qQJ!#%r+wdd콓=us}nwsϹzvXK&`r.٬M|n*qDO--fC j;#r3c+=T;}3 mKJ|(t% ۣb8 Bע0} :neMVhx69 2Lڕӕs.b k۷^87)\&`> q'+>E(=_''m|Ÿ} jyO$kp'@卌vPMMM웞fl?**k$Hiv#E\kҾP?JЇ-X'+tvNΔՉ "_J,j&sookok9)цz9sb3g޽9Ð#},Sd \C3gXΤ {AaX=€{[NYE,`/,(ODA .AƖoY8^jp{mb ܙ%+Dp?qF!j(;($szu))YC<䍄+(065橠̩̱{}Lt6DҐ;^bCR1-XF0. L003^hBWdyeyE#PY]fe˥6ϱ:qy3+FqoU6$s2=QG >B}n5Xr`d5yE4Ń-5UPٵp7(6PNeʮLu+t{:DwC_K$,ӈܘ 1zm;j_5Hڿ msϰuSx\W d㙚E .6[V@%I1@4;9RxR3s.jm͐cdYAC'f>.lݻO &Ubԟ$:0z0cl&bXn11t2hg*ԻIIWcVJ4s+ hr3-U1@0}@TQtF>#>8ݝzY35|QqovwH@%b~Y( ҦkAbf*A rV5Z V#o܎g MaBB" RA"Hh]ggY389~tFkkWYVY§%U,L̙u+?'U: иTf|H Ҹksu,>UWp, Tگ5!/?]~8Yf~wMM6չcL OD5+{qt,бqvT{{CN{JgZ N_dlk\6 ;-C^&SnЩjf xeGVզh 㤳N] VSi-Uv84a DJ[1(gFHd֋@ j;Vy%mv"?L4LӎɲӬA"1A5i 4aho\s 7 xq: ];e7r9vOl8ClXI D^9+{+Qx(޾IDar|>ԠOd$^x0l\ԇ >#ˡQӅPY w1VYQ.rgC3R?W\}Hg Lhsg1tj6$XZq5W71Qc B biP;t$֔D=>KV|0?[J gAM8+2qr9}B0V~+,,yCue>v/k?m5Ȗx 6ߛtW[o!K5*ZZ8 /5@\fN50f$S#nmܚ+¥7iM@xr` :E\_q,_PYpOyETʗ-Y`B2JMvAmt&T.+Ê`Kt:z~ug_A6h?̻RHGI!3sv)I*Ԡ'3H̫ڮK_+"<4d+┹i4Ii&%l&B./ t hyNNGZ 5 Y㥘. RvѿEAh>W%|Z ;mm{)}Gňm|^ unl|o\yŽl$ks|-pt]N{:[=9O u %Ew\|nUJ ֖6vs/klSJ hBe϶)еO堼GCmT^/`@@ -13[e]${KKO_9' @ @8xb}.kNNC&ЌssΧa?ne²ހT8)Y+urqW|Qn8>>M'V/*.[cc0%%L Ommc7l*X`8]Y *~'?^4,s tF>vs+ԽOlgY XGZuיGW~~?>=XJv6^,o1XFJ"!PH%DV=̳cS&ve<ԉȁh16h4g; 7."|VuJg2.Q=3?~F3TC.jF76!څF/6y+$\,-Q;W.u;jP7I|6=5/!hb֝k0ӃF5rPs6u*9=&}ty.YׅrO?(<҇~W,Khif׮:`dݓϨKV2ԯ昷CkG k 'գM-mX)FFuRY?^b Nkoy_u#,܈Zvǁ\ $?Q(QtO U ug֮1KXɢc;+}˨omV 9b  m WIIott:24[b=E3 L[QAd3Y3Y1b.A#}d =H= /'oOBBFEAzQQjja^q?68@sqs<VglW_ua蔝h pq+,vs+M R2R XrDkju[]] #}?o=QK1';QVWQ0Y< HJ<**I@%]  )- Ge6llFoq"M"ξ}BWiUY҈v{{'6\ѸaWe nQ"8%|8n%;|2{B6Ddede!;22S8J!d'#%8ddt ot4t]i={^~oMnz6P];*•ZfgZl OwXFixz9ٸHjPpڽT ^9+wB˽ܻc>BW؏jw rK3i @f?W~1ز! =ۮ6]ڟ  C+RkԓFDz_;p87mOsuմA j?[ޑ1M塡jsG'"iR%}C(6l"7K}*&tX\wo%9c3 ,g{gۦ(grL 5{g.|e6!k۪cr̄r/m4Ol=C<5JpcqLN992r,OeFih^e׽#exc6!ͩ~/rH$IzIv[S ~ɵLRV8u*Oڽ\R,y ݧBj޽[_7o;/>q5ւ6'<6+ߧ0vϺ䵜 Ol /X-ǫf ?E0=GU?/ bAi'G|EqHG/YAgkEK"޴կST_jcdԢV=V&3o^C2}2 mK&ļz;۱ }2I}. Il57s֓ƕTՕ)l{.)_#,K++ȹDT"*y wP0PT`tgg(Ub&Pܨb~fs^"{w X^RSnݞP\b@jHI3|u% M|Fx|AWm×e}FI]])QWߏ'Kh{N0;ӌp"AR?penAտRJ! FV0,8@ j@8V^Cfȶ6ǽ"Yo˖j˓hyTFޙk=s1ʂotGh5Б*|IMM'C\EQ@$%@޲ߐ^ [,ѭ ff6LhP6?_ >XC |vvyLX&laBVoTfTJJ&ۢrd*ġ84ױ` !Xgϵغ8>c%vJ8ViNH, =wuue\/xD3&O\Hw㘄*HCk$VԄcZ˜YкijEmIэ΅/dz3aq] _^~=׎hG\xIіi_9ÜGML-YF@g|I+M#- 0}P)lq muݏ3tZ5+N?S~Ih:a aߵ6wEpq-?SwM;}J7.itʐvO|)Q#ZJ %-2#S6=1%UJy:(7c[AǓ@HXWSKX^ - -eN%N%F AWqWq*z*zk2o  9J}e倆,+6 :WG$|~=EHD=m.3ŽaT"5_yjP-7ȣ1XV1d%=:.nQ} NgQUPNkO(FeP`T;-JfrYahT'ziYL`^sG@f1sXm՝͝q3&u hry)jB}F &llJ3R=pqr3| 3Prmzmz;lԚdN\70\`&0ŪŪgظ@I?BBоC$%<HH爔r4,k9f=ɩ/T-D3n.O,pg{HyH)`sR:X%"VfDquus7tV vYs'fxPuIFء''}c(9DBZf^N4l4V1I#Y " & 6تT)ADxFbbpQ Px] `>8rl1 Gw+t+0g8Tx1ޞc[exCJQ(琤J5੐8Bcw}+0DP*́$3\QU'oBWkv q+id_hq2 f}#Pi-tR뭈 Tb~7{g:TevTXxn\5Qf J<$!T.O.OC?6˂Ziri2Z G@,DHŰHI ܯ_+yDu/Y`+0waNY 6*))Eբjf俯Ob3 ea{^Lk_Rcip=LHgnݦ!"4mdz =fJJ5l%@vWwW)T<:yl{!=4\/'9NseG$ } x=M&5hΓڟ^=~hJ[ߑ7wQghzt@Ӻ[@V'YR}N޵p֡~WU4|^ 5Fƞ_b?5|x1$uꦨwZсTbtD]Ɠ &Z^`)jmYUܨ z|7ևjPĿ[@iޓ&]fve8A9`uo$ _uLJjҕdk /#m~BcOw' 85xR\nJa͔,lf-q҂L+ hMu6 (U|PZjvfLi|J1с坊Xiڡ_ԙs4ᗘUtU;pqn? rDžFDB,Y-\Ȫ܈'*#ܪ> qAI ξOVlWerʳ&6<pڵ)J珉9W|εȷVm&HyXg[}Ʃa+vf7cI_@dd pvs7|((1:9,9̧`M*"L*]# z*&fxϤa}$'?2NqH𐰳ƩIBm[JQoN|3ѝ1ٰ 6Ivzi!!v̡_ieLM͍&z5B?CQ5[< " eW3434ܛ?۪0++S 3f?!<[~ 4+nnwwVkHy=03᪯CDR\b}q8S@򫪡ϵF'n4L⵨xsHULt9SWa;L'SYsf0ܬǩ53ː#sm-K$vZ[5iw)4TtđMںYd̈́去P4{qdi+r͔ҳ`6 jP'h;vGbnϨOO$yKxIr|q]&|+jPQzP "NH ,IV˻Y d5Pb}n_;b8)[8{Ycz+CU[]t7Ԡ4Qtzi>%iMڡ J{.GM'+TZfМ/PPվWE婏28!2O:Bkm&naT k&zcUzV8.:j2K\wž wQLFkO˚fiwOǚʫؽga-g'2`?ѥkMlYV4H#$hd"k0^J-n%%r*&(&?. ^px440D⼖`((І*DƸZ <߼C Id2\"7}i?)ta.aYM"1}yBsh9Gss\Д(B4Q5nlFG3tKԊvmVլ= h}asolw]~.ψuWa{T?.gW `tf"9M ++3-^l?Yefa%̷ l5A:f4J9- L_KKS̯kkM(K0*Eմ0hV@w HlY:@AGPd Yz&&(\TvvYAYnxݰ(cxTo0t"1H;[QO~g)9 ?'%,%l֞4gjjQFfGfd;}:}s- &6]ۊuػp*ǪȊ2Bdg{glʈ)+$eGq"|ݿS8s]On~K[1 cY7]~֟R]^H;}@(ݼY@SBxF:d4A 5 o{L3M m H:":Iflj1ͫ_bگժtW]/ZZrPͻS>n6ҁAw.LsZJE{Us솓jQaBN0yrߗ,UeWJqw=]VIR%g3SÉ9PWU'"6aگ֮]?tH.\bT{ܴbB'C;UrTG!ͅu8s-|o̼[A,$UT; ? ?i ZjvNF8v4^k$J5%xzA8[]:l5n˦q-LF%$a7ĝqX# gxJ4 FAY{*f*fŒ7EEǦ|n*FHQT6V,%(8z1Pe ׯU7@Xg3VGWG+uׅY~FT"*Ϭwe׀ o vH|8 s<,i%f1ӎ+^hWJ+cfUY27G2EL3Rng屶&9M@' ^L73E>#^A *wZKeY[od(Q.Q d t[0gdKBy|,$ǒÓ /t=sM1}SŰ (FX K K$ZALL6cL$>(=:$FR4_M-1f?Ҥ§%L L aaOSo +| Ol== `}bdi&qzk{#ꮦ4XOeTa)a>GM -NR少E`0|koQ릻&aگAUt'?ϧ}Iwr9Ǎ\ǘi333õ盗t$F>|))TB0 afm}ŒMQ}vQƌ !yf3=B/_xHأq@Y<=tH/6ՕRSѴ=*:P )3P![6DRAϰ. ? [$Tַz!_!8tq.9{Lq/4R!tW#d%Vs FyD&=pu,60&=Fi |(=r1[a)3^ںHc]A>ⵌ!4J&ɆZy~Xxi&ɠs.a^r7Tv:D7D'B1nuXFk}y&~Aw7Uț,y(fcmjqIOl]Ҳ*HMoH+ܑ=hb74QeF)[սW4_hs(4LE|UP8I6גMXou+|Y淋s^l9`?ivӠJJ,},.XXS'S'T>`1f~|Ei5:Ԓ-$&$ƕȂ-Th^8 ̞s͹I|I|f``9pP}Lktkɨɨ%vgذƈAˈCtvLRr&Yw(o DgggN0KUIUYբ1{iIȃQ>Nwq#7Rzz-5~Fђ';+a[](??hWL_y Nh?'QS hE<'u߿b0M6Ϡ˵ay휦Xѹam(OYɪ6۫F[pl862r#`dPI` Q0Bku $)N/NOQ0YԓTR88j\TQjj[ +XВi;UQP=wECսp>r bbOZ0`".s.s6.:::e:lliFFEwg)(< ?r.qr3Ʒ^u ]ȋ7yS>~3y:eLsڢ{F]6ǯ"EpkfgҏU1b657"dKDJp#ݻBǤ?yMȾij\9Y©*s ww=ݵ,jf6%7w{[`NŊ)pLôߵbOS'y1OK^{22IJMR*qhuϿ:Lk9Fws'm_mGϖ_J}ǫD^*[OeE׹vBtt{m8;_e|Z zm;v=s;rã7EVpBn;S?O_jnvNFX3*=1бI Й&d2&h{ٲ2<7yn'LO;em&^9PA }}=KWA%n 3(ա5gτWA mm2m.9&9&c<"ĩcPXr@|fpfpɱIƙ]]u>?wb#0@:d톺E*>?+,)fL{m{_@&rEESڧ/ ^Cy.jB+} jPHqIֿa ^#ajTlzjPM& $q3*XA 'qŽZT1FfECƽ:Ǹ27H|DLrmՄdy"=;Qume*eB jw8Wg١jm\Ӷe2^9vW\ʦ%PVkOalΥoT+P2lu:?hTMp ]AdHH 5Я],F4rغ&֫iyo90lײ`ճRRT\g >;Ϫ @-rnS+FllFŭ3V. = |hXti(ff#B=f%"DDf>||.}!!W\=~j6<@(F-zs \L?KE}e.!`Ґۋg8Ko =>fAfIl'zzi/2v6v.\^ @""{8A;tB̬t^†H-|9#ǻop8bW>\ޑj*$M9o>U{>dG|;OlzŌEzmq'a_ppm4MeOeXOJBxmضbl 81S)?PL;(@?z6kA&@tgt !YY{QWaNχ﫨nZso3OM0R(9X 'рJJM̱? op'4#4S6ii =ax|n$8]._'_'t#Qs?efsF~WMSɬ K $:vp7 Ԁ6!5VNե』Ef !3)hJ|ҚDP2__Ή-!b;GQ4͙ʗF-fayPuu:,DŽZ30WEYi#Wi|r7)n{TosQNq=)h@ȣA ߈mLƗg{H`dhuĈ7zҬP#)`Mf,=V BB/4<6g]J \zF7軮>zR]JrYDt۟j-5V0 ^e!UVD= ,ɤ.[jl \ֳm{uۄz\=Iu1򂽧JM @b~ XϢ91{{.ޠߠOIGu (܄HM^qЇo 9PX"G"9s".hWƢ=#8!c0sdњn_ p^1l:#[|ccehj))֛e .Ua0{E ;z=:woiv\KaJY=rIpw?ZGNR.T٤W+]K=(BNʵӍ<{RջOM;@]Ib}]n$P"DaI\|V*kAmқGzD2lkcu]?84,\ğb5}zoz +3?|WxycbhE2 *UVx%ɵEإOl1jUt9> `tW9z1!Ԡw՘-);Bp]"\(ܝ[7DG9-k j[Y'!"D|j$FO2 k Ws<ҖXl5s]ɂ5~ם7k+\e,;ϴ j:*hPjGzӫ@4l2o`, "I%&?̟Rʍ_E`8.Ԙ8lb\lOcr#*mX*@>j+jF6:~eDŽ@O9=u[nY3 :bZE(rSГ:i7/xvwؖ5tZ$w4g ?qԠ4fSa5w{4$E=<,Ӻ3Զ~[T碞ʢ^ iLMzoq nSԠwut\Uir"RӉg(_jtEPg~@}]0JeQ7h%WReΏEz@ j[?a:6JL5=5,:ʚ7+šbb}ZfAwh"Fʩk /y?H7!c'%$ 27'59_Ik}N܀I&VM~B=k-Sә6[`p7^zn͙Mjد_%Ny/f$מniJmJmupG?j.؄Ʌdea\Ep 4&Q I+ߧ-{U*bOppmbbKߧˣƲjcºjY8q 3Å1"C)h'(Pu8ZsSf{" h' }&5!5y}=܍JttVm5^|S.;6[a_ܬTr>Q3u&T7^3w+3"~؅ 3>Z l\iD:cSCe@ >; x+j6K K hJkfGW80\@5[-i^`a\QN68IQ (`oOrɻt(u(:lnT/ qiL@.Ҳ&ɛSq"C`;MF" TOOXWh^4"GG#C?w}lج^^F; ȄYO>*יִ=ol‚}G|ºLДP^/I DkN ,勹uÁXNfI)~]Oq96VG3ߨL%M|Q+O/TK3m2[p[8.l\7cՎ|UW@6AmeڹSK}"p1;*xkEIdɐ%%jF^wvgRt.?.F[ah2bu>Ɖrɚ($~eyN^/@gzk{V#| SL|m͠ߡ19R$aD{.h4ԟ4]H`(ORPGH /v=~iNT쁰P9TI{Rm>FC j@۳=1jZ߮% Uv5=G;_d5SM.SmUҼ/P~h^\4CmzaؼT4I`L~]*?C!̕DIqpSGF7;fx8V/+{ZWeW#o.t9}r.ق},jYCeit߱5-#_>Ʊ{ި؂}6BIp?Q?M+.Q'بkIl(T(Mq@*t*t4"T5a &v\-;;1/_OC; 8n˾e 5KvSdYYf"[gBɒ2H)e lْFJu}=1%xű;gYELyI(VKDy4hlnPgPG `x pA74T_O{cjCjC;OIXK=L"DqYpv'5^J<_p7NCˡlwjW_/_ 5_W !Ɣ44ou2d8j[1yN GxH{癸7Q* g?=s$kaoL9ϫHa?뙠ryM8N}pMYզXc+[Kq\E23rNttN6Ŷ$@q4]3S8Jz }^C^C-_/'9d&X3#˨$N `g@k5ׁi`~wn?}nc**/I~@@5#@/ _V9 :jt[r"G"l30\~rr ՟~q{z<}4Bu^}!hΨω}Lim9B r**U4:% "<ޖ7UŻjфFhZbmCz xBd_<:Z#{ckcnNp 2?c@@ey5=ܽ="re>G.`2|,zg|+o~iH9.IftFJ}p8?ƀN.O.g$T Yw㜃>ęKVt }BB;슥' B's0v06+^c" Z8B*9piaۦmJKM-@mp0TB*3"9#9䥙E@[ZSC\:,NN뚙x5.aPMV f] H$wđʺK*嘔, ᑣiE1̱ރ[b]i,Vn>YמϺZGj%FK͛m/WY(|6kn~?ㅇY8LȽo5`Y+PSE8`?4{l^]]h*T ZD#"^{q'@+l$ǻ%0SxxII&&B2ؽ{cqIyl@QI}VfZ A۫wms)]XoL6n6_ۿҽ]Z{n~SJW=#}FN+{Ӵ+$%2cp %?Y!EDw{˽iW0,NsJ*$$FhV^MV߲M<}jVm4&U~`$W|oЋbBZAǭW `>5<dwac~}97UbG {9-b*`yՇ[ ?5bӣt_^uՊtQYx;Z2}>!$FhZ;'#S@ް6qn$"/IQ]qRHag;b x]On6ۡ%&"bx^ao(>n2@A6]ї-l.`r66KV +9Z%' @!)>2t<0.Rn2N<%m_ 0N0N+** 5hiffEMnMכ. ?1?eςko,49MWDןm ˙r- Z"Zz~i?ۊW®v#&;=^K)g=}6xH<nm|/G6sXdb|漯MDn2xp!S)g3JPɖ;sKsKAjkiA$;6W?6l΄Z8ʠ}7w6k9tT m˯Ĩiױ3ŝ5rj[MmG1ˠJoxi[jrUĹ".}_FU,6◻Rͩu&u,d7U$ fyUDXy2m԰Td~o;ZX\)/$J.Mo!;bGUzBTc=ЊM]]Rt6}z;lC%R[P[44 `!~t5:V"'W& x($,RFTրPT Ne*>*E##bb+R)˒cn30A!XIvS*V,rûûmQ}Jg,.vQuSuK03S,š'-}8hkm4\  ZJ*3L3/{s`={a^v3ʦjV?Vv-OL*O4#:,ATvw@FknKIeEaɔ6G0GxtGwA=d[[e0Umy##J]C4?ేy``m:Suǵg@1 DB11ē GAaa2s2sȋKL;RHB;=y 8enC8ekD*zJJMTg~hk<~| JJN 2:2:5kA:S4h1hS*<fFlOɝ2T>{6/m􃣔m5%S̭BKU'2zTCyqElg̯>3a͵6j^8 qr|#/8aBRU7rD_-推\<ƅD*:jgi\+2{+SAڏA%4B+J=g3<'jV^ .HFhhleuR[.](t}8线_F?T[$WHe?O D,d]zmEJEG`>JT@$/ADqEdTqƈLjN&&HVAW!c4c4Crw:BWRa(}ξI}HGpWVJZ)e̘T b %UghiUQ )SM'ƆZAA{ YOObk \O7PxKBb_Z1}!=[>ȯRuyszHqt&ߧ-y̱sEs_V:ּKv-׏o%4Hh |+EωȔ.SCD&:sK=aLZ|Břo?V1?kDd>#>#+kk%u%+^=n]VRD#Tf\>՛fo|=d}WYI:8!-w51r"Vn{ƅޭnY8SG9Nt7@-/ ?i\;?%l\mv &1-!TC۰T\69:c`TB6{%ֽ {Q M+ܹR.Qyx*S-c:CxݴPj;F(7(Q;uhT~n6.uNjw[DM:\d, Z(EO J-x|#veHʨPOkoܭXJcg()\e+sRIT5]8~D?O\/+Urohq74J5c ~7jWP&5fznzbS]/VYssrtppd!0< Nc.zzl`|$ԟ :@b0+.X 2tHU 00=e)!F4G4—W؎`aqZ'+*0)i6a+Kž;DV!U*,d;:gֈ2эsI%T)υe>K,$BsΥ7N>#VXWju {dzB^4B?>[\HU+=Im_lWxS22@2A k2)2)afwi N|/RҥjԺb|E P$Bα~zU5*y*F )NρB[@\ !-oFFQ??etiBv`v`p>ٺۺwZrg@ K{k!ܵǝ<ܿ1#Ml 3 3eToAC77 72ZZW&GSp=9uJ9Z^͂!k}v֋Sh=S]1M|tw4V>.2޷ǸFD#.mZp\־KK,VhhqCF❓S.%BXc)杣 Kuj1 Z.xqW^u\<<-]o#ŏUX;3ڏjV[mNsMy|l(^3v *zHq#2p( z̊Z6lasTdTx^BKQ}bn?$Mj$?"r} >x:bzdɻEiO=i=zD.v"{{;v^**wNcBىf/pϿE ̑4vvj|\>3m#dȺ`PGG)ttü! {v<8~柃LAo;W+- |qNC;&G?-o+CvvQ~J>2'|dT68Y"Y"`8g8 II \5ER0ڜrr5_Jj H1F 3#뾀0Rv!I1'_'G*GJM'^H.I.䎹sEـ//`ŌԌnL{Eb(?S (ef-NёO*W$EF?1uC[tDtɽ:ʎ9? koWӔ:FWbt]=;L8;M9ѹ^yG-7Qzq;q]~䯞 %=*4]ej~IkԃVB5Z$#_6nuj `ial{:x7%b69ZΧS5ԊjFJ{BW~|p]hf?PSWT廵稆jOZгJ<]zO4vGk);v8z؃J\TZg9}ӧNl.Ț5MFMJkjV)X”5XY T'9"m aF*Xw.Tϴrrbء$nWo^8z+SxvLT oߤX_b4Bˊ<ߗꈾs%"b/mv|> - w5idڳy WWqȴYۅ++=ӾԾ0$ʵB "y>1d)d)(2*<u ruB`O,c^ ؑۑhka:,r?~ͥ<,sx!x!U-~ A|"M 7A5~7_5pXY-O$$iR!BBduM8M8ɛ.;E;Ee9*@R|j/-9둖ܝ!e]!?)XeN8#ųȀsKB /񽫶;$&1DomS~jkxY 톿"ܼ Ě'2 d6=VVs M3)ծ:D!^el.(M*\}r@˶|K_?'R`?烻 5kw,$#t$SPcV ~I=\Z+37`wcxxH: s;9qrBIFZZn{9>?uu׻zXUi\3ZuwݻB^=f3椪*ѰU,,hg/D0Xta$ڟVdI沈7\Cb_ 28xER?5S}>myP6AO0D2!f%M]G)PcIEjOhI.5!2t8Ne,R%*3 aΨjZM9A"ICY3Y!VGGrz w4P449hݍ.+eΞl9(I%h/%\_4 Vɷ&3Av˄ԑ`EcDͭK;q/hN-jvBT\Nz+Aw8c!##ٛ(+Cvdo%3GFd );[ٕ-Jd&!<~ݿܿ8 {}{q_{]~׹Uv|L\ R+I 6D8R{m꡸ 'G "=6ĒE0`lpu7<ۅcJEV$F|Ҽ`׃{-4.(XRߵ{0i 0jWVKP}A8C{f$Rxh*[uivԟŝɞ ,$6ve1^WWr"{|,NA jBQ'Su)<9>/7= ]&(O^%5(Ɣ"8J6m~or#5؎^A hnĭ놎r,_;@F Nj׊i6ܖ.i[u*0*hT4yijȗ̾x6j^5-?K(ԆpH6g5IxKia ߧ9R[}ۼ801`5If:VwVLGkD'+6 fUHYeEvsNO/ 9 Lat뜓Gy\b #+ؼ!}=+,J1$tǬ@@z"Ӽy:U?uFFf//f&b&E}n~"H{=vp#& K]l8ڟQ*OrrxJU=#ڦc}n]''_{1--SfLV׻ף_5;Z\L9-{z ZSmz$#wCFVo쀑l ꜖W9AtRYR[4nݷUSC 7Cɗ*qfJퟡkW%|Զ6f{nOHKl]]͇kAM+ ^(;uu/zzz0cFyFyssذ0+62,bYPdLhsf{y"&yoDLLm?3ȀȠJkwF݂Tb`ͷ* F<峇 Y~PS:?eEUH%*g*_FF'{<@ ̃!~邫N v]}=A32yAtPW>h 5攀3$@ed]]M·MN&}V]})/g(fag&= |QvQYLpq^̢͢.jimiJl2 ݌`+v$kF@}֕st?]iWͺޅH0,)]Mqx>*+iӠN.T MZ#2:Y҇Iqyp@6ۭY roF׳e;12A@+׏st׽$k{3חapAAj58Rv2Y%{NeA#G1kJ>!xj?Wp8hAy]S8nEQ}|ޣ*^ AiY=sx}06sNLת&Qƛ)[}\S%Aj^+\UM V#lx|kїc2@&Ԡ(j<|-+GΒK/%J츂Mk~z.:%hй/kna }LS{ o\tɜv: Vz G~c3a%Pcl(&d|0*+ϻ/%% d5G $ڗ5@gyeyǦ *+ ym'}p>q=66\LH(~#K SP&$UUTҢRZKKt2w+y X X}M+"X3Y35pmIU5ӿ&}njޥhW\ʫoved-7]Dr,zl1u%?. $84{_[r}nZ!gy(龛L6[ Y*:x@2mA5HQdJ/vs1K/+_|<  ,Z.&(" j^&6=5FhL Jơ+RJ]p;ƠB,,X*nUV7TPSRn\}9mlgnZ oDce~DW4}fzf5trEfoVʲ`ӟv\O,xy[jҌ-6] u_k5DoyLYiAWh⃧t&"+nqY)3z+Q IG&J+(~ - odp1E /-f%$p=VhujLkj!iTr˱Z[ș "cA[m  #s*U[xku\ߥNhКPz$]ao)4nAwWQ)&_ni1Gt7K̓ȓȟ6b(LKٸٸ6ΰi-ଵٓw" oA][}Df Ⓡ8昵22BH4vJg6'9'!c{d8&6/6z'UhHKyjl˜mNt(8#fff[M5M53F&%GSgp_o}pկʍSNVc7u`fvXu\W[HP0m l@C@ee N5g ޿(E ~Sp,a˰{Jw+Szq2${F}ޅxf£ kPf&x}BTtהnj=R}nT:("ayM'dX_jef=44d K;X-5>i>K=,+FbgfxE_( cjP4YڮwIIgjw2dc[I<;,Am?s^"x\-ݶ7V3YY"S·~6"Cᥣ"E*:X`^rJo䝁 ,P$y^ttNdAq>Z-=R;#$^*yPЄ欗Tsu>2t=S>w6ME\y0Ό0XV]΋XwoV t1]/]hg%ʫT$X$xo||uDXZ "=X eTA!88 ]0r(8 O2 p0% )l>Jp`pa'a'I-?~Eف{>(vQ@  vMPC%G|t. &V V1& ;KdVP8*.m]FR)#ojxJ]Qdխ[5*V 'I>=Wj!@2I^Rnelrv r:\ -=orI%w --si` Xo:ux`c5ݑAniuTSACMS+WblQ;AAPXGsfJ%y]tRj!cY;w`KAY+D{D{4xxm QQbnNusJLz:87}<$ ӹl/h[Ou d"; ;40sn!C~zLLfأ g !BڥYG!?sl#PL]748|. _2l Nc9ڹ2Jd@n!H "hw-$*r`XIy}r@~, sHU mvk0>W_-33/gR yi?jxM"H[ @=E@P4a! ʚIICBH~ff ]dAzDzD'zgfѪ1vZY#]#y6sN޵9^fh͏6J  0l vYx}myHR6mT췶|Ҏ&#{M@G@ۇ~|p ::WcwnH и|=tB繃,>Wրu3VŴ{$$ۊ%dƳ22k P/^sN^CIgU Gܲ+88V  dEf3f3RzzvfM 0šד.䔎iSSo7ſ+FC}P晶Gl/OiTiq9ڟmJE-:iSup&{%ܤ4=£x桱 - @Tz.t~';{ TtˢrT`i*ɿl+3" ~VyU_ͦz29{c6ӫ_}qa1]^P϶45RdǍZy |5zyPJPwkX(P8J:uQ"۽P1Y8$۟J# ty肘X-bдv;.wY*Nt |zmTlTS,vys#\29c``Z,PxQMk <33'7offwR_y+W:OL܋Ji. ]O)>¡FFV['}ct1llVK<+vJO7FF ®nG+p( &Փy^)ϧQ@gem)zLa4D޻9ܟNqv|/g#gRiri2Q1@'@K`c49j!|­P],# NzD 8$VI+\--\OdDszp2p] kGj;E1d04_L`xmVa>ND55ѫnRyң%l1ˋ&Bxõ-=!7_~~@O5֨47+BdMb]Wh QjFEBXҚKʖ8=B<෵9t 8YojM+!&'r' 1$p"ngU%m1 zjBccoP|^d ?Y&fAO8@nteЗ*7 --kqdpRAM@~~|w/k[gMn'nQ|̃ϔTݕ+O32!"[^A$KTz_ѦQ1O!Pjá4ڶ]x}ᵫ';Z0~792dh;,dBJJ QN[6\ |{rUUPAw1(ҪLRA|Y` s;(JԤxPG'h;Ss#5ʘ\7c sf*-P-P i52>IaEoax&ss0AZq'7 rrbbDNzz͋wrA[v٭?`*UPw}}8ŨA3ZwɷݻB^{ZP ՀҌnu"; wFW) B2;8>2mP"%I$H=Kl)Pv!kYYʖ5kBrw?wL^~]Wugs( ˭0!Mگ*礝Nٱv!4C8Gݽ E?ASԠmfRq4y!vefOx۩"Ӝ6jP~Z+ǠDS*&2(o 2%Ѥ-L.@ jZCԱd~)gn6gXަso_P?``k~&RfPE*)2 B1 -PֳŠ6>\ғfifTa6 ]AoZMAODUUYDnBt >5!N!NTDh`X)F(P;n4{ r}}7ny <%#P-ڬ G>?m44 nV%& 8o}D̏p-s:wPE[*+Sy|"GssQעcTTzT=EA)U߃vZ\|ƠF:@0l֩DRc7moWGۚF47Zxi?Ø|{:fIǕYjmFy`{uq@Z/QzW{E+;3y3yyꭀ;.G-8n_|p_r_rr5Эr ub8]$T?"l+l+ \+ O*ÉxۅޅxFc(p.ݏG[KjvO Aa%@vv$u#F?cVqV9k7dr`q<<[WZ=aiiyjd͓ BQŝ׶WkPoş{2Ag=G<}#ŵQA[W$iM,[̒nRjdh܊ UN8,a!6X;Ag FRycgh¼y9fYCi"\/pG83˛#(AE>_ᓓE'Ԡe76U,o®e#[cWXDF8ْ[4Dخ#lw5,ZKɀ>듇6|kQۮNyfh}<{Gy^x$Ԡ;4CTm.*'Nrz~ږTq~u"Ajد(r$n%=Mk,61H jP(yLJA`?;F;UG* p;:E5z;jP46o*bw3Myid;o1ڒj\6-vlLP1s,NBX xԸ,f|Bi=ݾNbZ˯{'쭆[ͧxef;S6KևU7 ;jmTm"E/]tornT#Y9l _Zw@e5AGPj(5͞j!Wr_fc- [tYA(RkN[> JTjo?M h|jIE_הu$FN`.`. b ^ 2MMնԶ|}q:L3$f5HM_j@EZޟ/!#DYuǧmB蓨Vzwm!E!EɌf.(/3G? }۹7dVTY)Ml.-ǐPk@yv!eՀ1yi_oOnʏN?ac"â 2Y 1"w<"ݾ jo]SNlaxY3"6nqҋmQz*۠ߡ5[#](YM&ϛ8Vs?j H;jv>6LJCjW߫N|覍iڿM!(m8\( `[nTc9NFV :B jZbHrnw|fZ, TH~=A0q[)C 02#o&1j(XlM,m|4[>>DZd|ܗa.. 7^aR*t~ Ɏd.vvдsIkmqc8ɘYbێy!~eyP-`Yg{&޵ejb 1fi4\ϮԮPP&'h?Uc4V-cySa"=FE5]/immwgFF$"1E".p,Z~[S~K!H!WƧ⥂Dn⵿ŕ7.G%nxo|B`ۙmO!7\MbT'{g7vb~&;}`Qr!쀠\F-l uŧܾvԳDeΣ~1̚2_kkC{úi 1Q{ܭ 8?rs?$Wݢ 4 1Nح(R,Rl`|0>jT ۑ#h +}5_6Sc V/6 fǗD- 3lZ6vw,h-7W=[ P >)m)mw6 i_owlΞ:jIcxLhׅ?ۿ{m)6ɧ: ww{ZHĨݹ3՝ypL[ʐj:J=imsuՒ9I:9Kv^^=6mk5'(C%OIb!f1>)u?Ƀگժ VYм_9zKHS !#Q]vCi:ߊz OkGdV$P)'x}a5fY-O ehN;(Z/aP ujoOBAWkBU2M=UPduWŕC}晦ʙ\{ Q~ck EE4#n'ēi۾üv;0A-Y-寕jQv(DWɌ?۽"}1=('%א&6]IWW9S<57ED̅P+,J.Jj8M8M@q=O>yh@J-}kkwЏm\1Nsk"Cys /.-< wY#|+yǃQl]l=I56os3EZ}p0y2xDQ|w{K썄yg] ׹N|joS} ޹~{<8ξS[ {oy2ğ}] cͶ現H^ Q&Ե@ziH2ggZJ g#` 2M_"lI//;P0d6*%uq{^MSғ) E/D/ylL&&aLV;1.0ɇW#/}-"ABbbڂ8l5?&&~2{g'! I ۷R2BDk=Z8L"vJ<[`0{ H9PԾ>;]^#5l67ݴރ4n[|9;8M ̞=-FI^gჟ] b łpA^Kil/o"MrC"iN8 J{}A) q:q:+䥠,bL#IbKT"A Ϡ \%3^8~[:;w;9st)#-]%8\r?ͱccfn!ɵ̏UZL\;=Hj-Yzbg#!* I |H7MiWZʑ.HֽbͺGa9EZ`5q3Q\(~Ұf{.X<σm<@w8nY("#d8HTT23O =8,>u}us]ޯ MMЦcHgsVS7ƕ3qUFb}vi8ۂڂ:]oOգW&' du=uur @ot=#=LL$?fZuI0&_ܯ.FiD$O{o۬[EijESySyek1y32_ ٗgqn4}3h멢|T"|#ӽ[16m"~%߽a5Bܕr1O4# ̽^W~:pLלg:Qh!oI|+ +J_|GɊzϣK #bvHmPk~죨P|^XؾcGJLWe%&}.Q=C5Owvm%q$$[qo 󢦃yi#bM[KJ6oj w_=փXy:dj֏"M뢰$>BF9TrXq1R4tPڟ ǞvwsR9ĐBYU!Qy6RmaE9{jPO4[jsl rw 3=$_Iaf10Nm 4i]jP"uك$m-Vu!ޘO➯[гEm5Jbe]LZYQyZ76ES"| m?[#[CLPgnhڥ ?*Ǐzz5[izA,9z+d;وV0ۭ [V ?` ESEh!Znzp9i(>*&g"gړ$v + hi0l`o`O_^7Q8NVɭȭ0c4\nֳ?J^&-xz{":.5y=jI‡sՇl:X÷K6xFc}{i}Vv|[͝1K^a%Wwaq[$9mvdZMiuw`KclC9EsA jg=fV:V87$Z:8 {Ќ3<:/8)|A EeOҙeK*Ztu.za>LwAOmT|z{[?ԈƉ[ L{0╭F$,Sw.EP)TeT7PK}6i땏Cٗ;_}ϭo/䎉'v#Kn ʮ]%|Ci櫣ߊʭC=@=I<9o V2xE{Nz'jZe ЙLoq0.4zv2J&e5:foN0]]Jej@NNH$s* q q^{瀎yh&Ϩ]aHdNY_h}jYi+ƱƱK4D_يjja3bz78S*)i˾KDXgc'M_!wMV3̺86W{0uH7[5|Z(VXv FJ{n0}.zt /=v7ãEbf, ĜG‘.fy,s݊?i`¾K;a5P2Gzz4gh)Ȃ]T팏&y5G+&&4(PcWcY4$gR~,H;H?sOXT岋FBKm/d6bYmp[8 ee1YYrTviNΎٍ6|%Aps桃5w{{&ojyfrxP؀[`#On3~a{nzҲw\ }qneX, IlAN5BA^'.WAy0R!E"Awk͔iC U*.t Ql^;ZԪek߫֟,*91MQ+m0lVj#޴v@L=yASrr ?\e٪ŚŚ4C“Vkh9pfL1E1t:ّ g^MjxEY>-`fzfZ`ʤ 44p3ZZs7+NKOy5sm)?yoW|&5N$Ӽ'N:+"#+U U %bS-_x\ⴗ6!ss -fEmԒ[1oY($jLj'I9#pBU5 Q7bZZUp3%6&^MAg:q@IINt{bQI/~--1\at%w`v=ae'*\ m SLЮ|6QTEE-@z߭ K(XAw^IԞހKh,]dh}X(gmI'2(DD{f~EDբ<<8&8>Yx/JotHl3$nܿ\wmQ/mJEVsF̵v֋}Wl=ig'|O,QܞnZV\B#RvPЦ$&P-*Jk̵8# eԬ`?PM>w='ߒjͷױu[g[+^<7 9}j_srqi1bBA"s`' r-OOINB?~6,>Z|ZN6 *4)u"|;Y *sBo\{uJ-}NB/i4Yoe`\mF1127؉m&t&o}SnF(Hl//̪/s׎YsZ Σ=w][/J+@]@b{n>xxO9a‰׿OEMʦpy#4uԵRR{tĻo8TDjy[BمĥdGF{CtEnֲM r|G?yB-)9LW[o-{O*9-!Ɩma*D!_Jtd(ad딉^T<ʡg6XZ3SR jt^R4TSoFSCӺ;_%rƟJXޭ(v(^c+ljP Aw4C:S,?Z4 ?/mPڏh]\n%ph/cOI7\,5&x ?$\Mk HyxiWZP5OuVh!{ͪRʠڨ&,蓊yIl8|k2M8Uk̭*^-?֊( -GxjsTʍHÁyAw(c:6+W Է: w8seȖ([FV{DFʸ:R"ْdtaפֿ u_?8%?|҆-*q V, 9./zot&+_M эVd@G$>C \\taq~ؙrTYY=}xΕ\JYXY[sA5e?Wby-@L>yIp$J3뛢:]1nr}Ҕ{#X$Z$CeḶom)ZWZWзe ˛g+yꤻ5-z+/LB_'<ץgD@wm-oT>MALJ {Y57Oq{55FFx1C5!vluv@'3Hq=|=2 4gK z3dBͥ w?77X @-Ltt44Gj[v ku39&x6U,$ m"҆@nTyeC`g?NLއ27n6n'J[]nAY^?+1 gmɝ6HoҗOܯ]qO к®AkjR t|9&s[ 1LֽIۯVO3琰"#*E)O.0P zHNr8I   JL^\rtCCӕ"o2rׯ\IΝL|&J ic OtC_hOђkMp9>ձb’.'tdmnv|l<㒷/4}hyɴ WmK_kt7 !^FQwE$/X+SI+B FtۥORڪ3DBk]zgvsG]h/9b_$J45.̽^-VɃfe2fr)d22YO-L@jDjDW%bq;S) ,oQH!xu*u_8%ymc"XT I8!~B<R#y #=GRbp u FTPxC9]wۃO&WEiVSE#2?x4P!vZf[lUӭ@Ӧy .lExjIE~/|v9c9a @ (g ` ƀ>)(J @tOW| Z))>^k^(A<~kdwy FHճ,3t"=ԋϨi!D~c⮗! ǖvθ(*ڲ}gXecσ͠j߄o21Qfe nd@g S3 r(d'_g^4gp:b08>."t{{fV'Lฑ(JkNPU/Ogt$׎d=-AUjגMc6+2(;Hu@BO>rxnN4If9t?`v$bx?Sy~/W ptC_! ԭ )ECQSs'mpS ChN0ХF?MYd闋ҋe}G(VCZ}5>Xל!ne@Pw̹d\vޭ+bNpS_ 1YؚՔUj~.Oz+_SәxKo$(E7tZԥ' KngK;p$*RO$YI չ34 hxb7 qQj%VUH1=zf&iEfnVө͒yچ,fVr,SyzqܬX2;v5Q27B6 wgm;<ÀSMxkpӓ'!Vi}W5 .؏vRs\+u3|O& wl-Ƽ:RbeҬԎz6:{:,h,٨$9xqTg/l% R.᱕Z1ޭDpVVsjmegHs6w@:R<@ %Ey3*mՃPkkӁL w'|8S:Sz鲓o΀Y \K'@ofA$}h\Ӏ&&@c{HHJ3<\lRrٶbc/@Ωѩ?(Y.S׋v;G?pV׫76xH_V|qUҀy?=l"~|,Ő塳gY^vYn-mx~U|@.S b!֮48;p`e{Y0[ uGƒ/$>}NsR5MMF4"$VGZ8p3{{A7yy++Gnvw+,C?.0 V RF:ߔy4^b6c7co$ދrrL?w:Qq T$U$=//Bo[iq18O*Lu4t }Jss~7۩9~1Ѓݸ-|_Q br\znVdUa~_R{Bp L[%R{Ab.tCCk #f$8͛hT^h.`8Xo4N]u-셤}cENiYU4 E*&/וTukND_%*H >Oj:]3VL_=XM4L NtC_Q^nsjqX~.ѽ0/!'Jhc)mQ27 -dvHC!vēɨ*K’+IuE"?w[[YJ^Z˚-hh>b@|$/u"5"( qHD}dxgwX55eM^־YٰZ:(}mm!y3 k{>& p/F?2K G'!ea/pp>@GG]NM9k bo!h2#M]WѾdF.@%jvv]KaZGʪeJ pL ؂.G /<޹TZzi]`Ͳ?oN uTn Wt?{7H{n=l\_9s/9Lt5a-CZEf957INj\ ) `X Q.95!se~Vmy/-jN6R brpQ#'NxENˆ/ج 8A7=}xMoj=g.Ȕ}!{:3Ӛ(&]RjoࡉƘ uXޣX<0 ~v=hN RIYy#|\^L R"dQ=JNhNBm"]װ8G :e0? 6]H: Vw3|3cJT[-Ձpr^ q0#D;+}ϷʷSg{ g6 Bqpq<>F 9={J-1LAWIK@5NHPodO=))BÀ/2%` -1ņCwwtH>exVlV"L|8TFQw>0m|ʣzׁԉԩE>ѡΡRTxRxV ^eE3<.(‹?6n3E=xYW]K1&c@uR5E.:~,aT qMRU2` n-EӬ:ĆE'd+/++F{Nu5S T mpaj][}$= L||kb//)(~5{ )nfs ;@A[{`ZߵFFwO9l&i&d!Krc$O5B.\dDRc*]5&H; ^Bq[8}z/܇܇(VO)R;7~ޝGCd]e%oE$kQTTl݄KRDv#.QkL'Eyn>s^|~Վvm%@ccjc '^2yzfs݊d)n%Ng9S6WMT燒ck.1*^l<,I_ B-aZJRP18"%~6lq'2UR#P5Br#|Ÿi*ӯD^p%=-Mv˫8b}?KT6kkk;:Qg&ltI#]S-eB@KN cQQ\Vt,Kg#V'aS;`E{ĚTc腧_ BE &р;x瑀ɔ!A\/;kQ2=Z *c(GH6n545WmZ@wIJKܻʺcORARaR*w6]18>}< Ja:f sέƽ13].{(( *lDDW+͛soF UUŞS- eT֓2EԷJ2]}lFNze 3LmRvb-t 7TԣT㽗1SK|{6s('x۝mO>1GG 6Г >HWMMb,WvWoߐBZ"Y*@&;w@LG994YܺrTkpi\ OI e.h; wjjnORʗ,~,G)@&C.%_sYL5 Ŭ7Yύ p!!lP6(rIb)[ GGt9O 6Ή,*iMV{o7Scm"D !uw"{/M"@B: '.]̤ h٫i$ayl{k%1B0"״z"яl5%;lp &0ܽGX.+I,fnA'''s>f>+$?&  hc~ d~TCYKxyԀ ^%{ JEEEH""&f~[F\c_QN lwj {"]*4]]CUPD+ꍭ#[`߸}GL׊Ţ;.-p+].!lmԂ@4=4=S&@% _kӲg-` èFL0y }sMs3H" > ŷ5k{ˮLCCɶo''ugLz'0R` rR8dFoLTTV/D>4bȆ eVӑyl >N;,mwl#\Y/$dwǡkd.>1\Iy !\Y2u|ke30BN彅N(R[^'{䍈} 8#pI(Yfpݣ)YUE4]Ot%ۿ7ߵ4ak]`7UKF9d:WF>Ju?ݺz#K99醉s`Gյtߒalmp{s_xBHLg 3~?$oV<, FLҠ6]Ë#$&,Ϗ}>=j]ZH`z63mvsƧu <7:G12̲QvhYkkb0>{=Zp$`/88%nMKGsQS4P#<|QWWlmAh>EE{{'OOKWK"{ x pOCuU5s9͸h~>Ӑ$BP>$wM6u8l,nC*C*3zuǝmOn64e;L.yA&CoͳOrox`57}Cp@T Y5+'S(_9yſ` 73g&lti <=PnxTWe  ?_75$Ɋ'0.[E*Y[ll,w)d1x{+&[ ^v O Jig\õ?ۈxq:v4i$މߙBWߐ>WZW5\+IؙbfOvE v#nt)}Ga qj)LcY*>:`d̕NJPIJɔZ_6{.]?^.5z) li%Ksdp lsN?"WJ9~`}k(PuNT*5\Z#= Es]"TL5{tp _}Gu{SQ@]^#hkA Jڭr^$A61AnőRgG(ӡA_| ;~}m1 w/(P||9-cM(m/ %$I(6h x%~~xD:ӊӊړd7֢6&4&OǠZ:LJfrPPv @!A9/0̲e\- wgp|`llMszfBQ%}Jc:tw!K43b%fwQ ;E^ZZbIg4R 26⮮n~t[EXc[,#lR%;sZ-_ۈڈr0}cn#'r1V@h|`S}|+V ;TW"%otZf@MWMCꮱAhh($ڤ>q+:&N8[l1 A~a)XKe7MF6I5'֏YY|W0`T7یaf+o|JQAQx??[vޡ^6Hݞl^ɴ N/1\|_Ao7S_D-kwRc=5ܻlmt3xD5߽o\!l8U.4t"B[:;[Sgͽl0yf;Zn**k]st5?N3U(qNElC7zbqjs-*fUq׀ ;9]+n(t$Jp:O81^V #ΓRy<(LzJiVzx?=5SF@zc}c Ķ2t+̪|/ r9$%$%cl %^WW(ãGiheK%R"S7F'cc0ЭG97hW $r@{W{0H\ȹUU>s;ӹsJH &yVN$F0)o}s_H;j8~LeLex$+d !,D95%Cx2S3eS׹ߺzy,|ֲ>Y◈|haCGjjJc:ogw\눗Efn[|+.'{B,Q f cm#vFǼ-Q6ÝO)ZqJ*trلnԪGװ]&OO;λl5[=>2Ƀx ~i ˮ_vz| ^yt<8#{½"p@e)FjXQ1H { rm) 2*$$|z4ȡ{JpJ%v*`i}r|sLsh|uݻ:hl(ܮN'@B[?Qއ n}BSl&ܪb PWu*8OyC>?g1AVn5:lwR\X+كǎpe~LǬ Bq M4.@g҉ĝ3t^ej9Ƀivڡf'K E3"k2{eԕ?I,t6~v]zAY[$ZNVl`Aݚd"8PvMs+y۾gF[8Č/sѭ5_1e< Y&Ri-j,?`s?q7rAmӖ48 ?,Ds|y,vP]JL`c$TAju-%RM)Ȟn=w,*=lLP9c;<#O;PD&&xYqJc 4?*(i(<ސ1YP^r\'XD`a?.{Nj"E]i? 5ˍ_|1K_Ǻo~y`0;[Jltk3 :PVhhqsRYjQ2c⯩ss5$wwuZg'J j#הޣMY$^ww-="n 2u9֫gHNhݴS5++ϱgg?<Q>˩#Gڠ~_p duѹ _[e!ke~?bzڠڷ׬Ke~eq?-aq̳yK8Q = wB{;\1 X;W34>&G]c6v˒6X_:Ng ?^ A h]^mXv h̦};~6ż.mY!r>}zS1,0 R<[}q=Z[!RސaϠ@O#n%wɯϹeAWhR-^y(:Wc'+)f ]ݡ>cМeXt6gg ſ<6TLVR%=OD5 MR_.x-}ÒǍ#EC}RiY/5 tm-o5~sH̽E\6с"-]bS;T`0⭤.2}3^aW:EzlQ><MΦMAaJ-A-~)?"@#QP8E2GvHڔq#胔=K|Z33166A!FP+HsvB#M09@3?Z67)mG/Çscxlp+#+LŢŢ7_}"-}ёӑo|`--˜{nK5zXN> 0O&[liBh`_2Sʳ)=߾:CQ+|7*l}Vk EJc@mn(нwKK]O%|$$qrs<`5 P$3Ǎ>&>-pZVg,619f:Z( c'4ֈݹJN>~q"TTFT!u*-ȏki=uHv) ^(JL+r[G55hڕE`ʶ IQbtLtL$wxQ%~#I S3z7LeEŜNpk1Dvڏr}"k b V7IUsTKo%= zX=99^O5|<SݶE0 _\֣6ϡ4wrW ;iHbjHU}\w%tC j?[[ի}gWðMX}583A`U)5 jO#-B b1/H\e.XѪHޏWw 픽PH 5m>ҧMg:4#"Mp;-uZ~F7%OW쿛]1YOg),#L&eɞRʜk"P?]8"v g4<V.S>RpCsgA hC> jOWH39tz֏-wF)jPV}dOUöUWr-JctƭFHH51 d"mU H [6ذzi=\:O3>/YH}u)G1N6nh zr8TbbP5zlfaҬdߎy6:t̬6Fձ`|߭^um%aPW7 Ļ$%?yKkE'( . nCZ$E0= 㤀쵦h V'55" 1XT@ԅp4w}xHp@@h(,33*jjV>)M :pES2 Åpr/tLLG>&hkll.9ʆlqt++E湿ms1 .qb1't)ƚ=e!k<:)oWw 7?">?s.#䇜o$ 8=wm__M}heLԸif#daYaY)yx^~ŌX2[/>94l 2OѝB8<6 hp3rbz!!Aw >Wcmߢޢ<<ٍHoӍﳘ럼Ex]NB͢H?'K۟_ĸs *4KK'_Gj$N_xxM'\\*7s~p穊矞+ ZuظyJzHs7Mz tcVCԥ¯\8gcs^+@?Y5W=#Bn9!4蚦iXb}]Pگ,Ik9/LC;WqJ4=1Tg0N_h ݮDvL5BQu]QI˪a/:vzLebs)&z׊y>p6Α)QjPu[s!=O##U"z5(a{Q 'X9i,nbƩadb"t%w8 'EA+% UeeSB|Ĕ]]A*nx+:x@6 e~@z l#($8Mͬm͹}5 ?;?{7{{+zLMD""N֗>GfOhIB~ h*L2)Q iF6D]q8xL yL H\$n)E/!/lBx, 䚟"='K#Xܔ)YMg6:M·'#^TFgڧc%]~i"bƝf[㯯^J2&4A+^ D'Ca]^_kiZ0^,t`H#RA+I<+x@Y_俄lE+|R7٬ez0P& Hb{55ؑH p,pO(t;8^ع/f 8V:eN^Dj!S6]]w0676rf[T##o~9H+"M ҷS;0ggzoZ7a <ั-,,d};Ԑ%d %KfD]؊oe&j_oӢs<8s>gu=g/On8|:| 1Z䋋t4ö^3!kuw~`ғ]t[S\u].j<)]fK SYiC5$ZyҺQ#~/ʱjc;W;?qjPycx4c_*pRuߴQl!ݪӐ o^Zaq{{L"8. 5iHL`?UŠ4CU)׎0C_)Z&Sׄ{]6lZdhXaA"Y9ݜ|<γ HČh(9i`ۯ7) Xve<7/)!Gr/ cnI0|lGa0)@6j| @בDDЧ=a/00̒@u d/?<(ȦӺډ孮$ :W%'@2!:;cNV(%~;xْF,zK:*dפŸ__@_~+w_R~a>VjVX;6 Mʁ e̹M/kۼ*v\|68 ˖zsN_CyLsP[gǵ) P;*|gnɿunI~/]I>䶋MٚK2lZ͙0*ўGS:@kNa'YF1Y|J;+͞ҵRȄƞ%SV9m[fbg4M$'jle n+a ;Vc!~g~>ft&rrf5Y/1QZaycPw!wi:ғxzKr|%ublBD/Ɣw?n#(ѾA0wlFz$PPڠcm\*vxMJ٩"' *D -ZzwBT'd (o G[ '/Ŷ^l l4 uhe"Sqw1zZ\kc\؆m@ ݩxv4hhLr̓L+2A`7掷;v1.'b D_9–D^ON3md,0]">9F.i<"88S4hzliiUJ mqsP!iT'I3T/2sc7S;zyFmХVS Yt sGcۨȴm4{JMR+.֘sn ^=*d]ݬ}R7/V/$$^e)5@fIȞ=\9<WHK^1 M_UI~: 탞!@ZNQRp#dyޒ8\\j/w1D1(W|~BZޛmm奍׷r9I|3͙ʵÑ͎Ϭ8˝v\;U#WĔJ95U tLtLT2lP+"g([AAQ/LM+tK WUl+W@~Rte@(@*'/)7\zU."0 ;FPPi*Xbpb>P吐882B%4jCEI}īoL3(W,]x -äNyλ|ZFZFf[=]Üi bRVXʍrl7Cü ߺi?E6'TWD34 Mh Mn{)$Zn^1;+'b:?}t_L9O6,Px@?C)wS =Qlöjp{9Lʍ|&OutoO6+t RB)tƶ߫xi׉0tfZȻю!8vVœՠ븪˅*blöŏ2%ckRqe2ڧ,-Tew.N)WankM+:i '}(}CG߫5/g$YX,\ȶ0 ?ﯮd$n.de[V%sIZ6sM>}4S -NY!#CB%v;3!hl#@{NG>+,=CU3|zԀrG]/u==eAt~~NQYi<14BZ| HdtLyY^F mu' ij .٠ʶʶ};wŏ$C7@-_-_̹М|)Aa;Bhvs!9Rg&WJiM H A^]9vy3:{p Ƀ37c[ۦyV/q0]6\{ |s9B>=B-LGc8cHe)+Alٍy ykȴwT5BF\++"2?QHdKgddg B![ce&eGHddķ~޿ ϧߏsy4B^ukMy˕IWW3m@ޓgZ^^yYbI(b$(|Z ^yy sp ?OOҫߥ߅I[By3">=bN@xovp<9uʲdRGV>'uuF>;)qKx{ؔs1Xcv16W5Ԁ6u)Wj]t iqG&Q:ɻO@~ZH̨{#n=Q |v>rQH% n{ͺJC6CSg2i-MGJ}^i̹4\m]˽Hj|kiÉƦ\T*aXBp76cs݇g;-y|RWr:sb ;'KK:zTRݾ#^+C6XOUIޥ%vQr1׶}`p,XNSFFuh>>MݷII]W}_I';4Dj%h0 algzYII#ɤ{?1cX``U^tKlo.\ߪDtb@ZZB,rrČyNstI1)vWkTyJjYמ azs9:Jg'J.2h4?%۹s\Bߺ9(h.RnTnE)ֲEوmw4hȾXkqa@j2|cT"`]etreE86M$2mZ`Ms19 *Q|\ȱzPU2&:Oo'c?rQ*=OwsuQ;#uun(oݮٌٌ }}$襁,`yy[܎U @ rxڻ+k5m4BT~p9:BiYQ&LSAGVg3DMi iPx,s,گjiYWb{Ev[.=G?$wyj4jOjs8Lo+p8r&[ۛq{"W ݒ/T0~2Lj,Pb`[T&r{9C5})>Tp'. `)1X`C%իvl8Alt8So'.^wL@ FH$p])ʎ2A6x,b˳,^᩼M ױ'ypjH|n\-pZqG%/QO$orpL^5Ռ ibKk<|w֛|ǗLT%QxǺϴrQgpu6-$ WCؾN=0\ {(}| bvZ߿?Si1Pp~'?_ AIIpNsJ ?3^4dR+0nbw#)9GIG4ڎ^,P]V],,/q_ccWBŷ$f}TQa>:3(<⦅^ S /00W`##| ȍYgN+~+~gSTƝ<ቾwџdeir5{sX{n6ްTY`x#a EzY^WΗ}J:k]S=ƶΧߎ>r @žOAA:A:Z"̘F_ROOޫ7fŠcM2 J](frNLkf@d3x@C쨓*47S,.=de1VWQPttY"Dxr"]8 z`;:ܭX@ф "s\fIshL̈'$Q% fRP \;n4P7PK_-#A (Ʉ/>ij jHn(^Pt"}ۂɃ%` ;5U LI1MR5f bD?fiZh>śTUGMM||EV8FeGe55w2dm*K|̘@_4vVM gu&i~ Z_TpXb{mZ+ZyZ-tQʊIDD9Hak?ˬjLp$ggABpEYnʆ+A'Ԡ5ڷ,6 Ȧ*6A>L>:ԬAZ ߧNZ'1~aQfM^.p9d!Ԡo;O-H.)⇺ }zgUrR%AWh{'NڃKx:#QCmCqZxgQY|!%P?ՂUf IR͋8hXckԀv ?&c!Ve {gN'C䙻:P?TDMcޖ,oN=3p|LY;M &hPjC”b_9gdnyЬ VG@:굆\^<$F~sÖGaw}WLG?jeu- m#!9G*ؙ+6Jfy}ʰCs0]'3D8I$8שnwOmVa7>i#V+FW==X66wv@O~ԝE$T(vk+9y7ZNZ!QmbD$% 9|[NβvhϢ >7QYus70EזDy ɳ|gg^,bwjvE=װ+"PP}]HCN8yFG NDSpj :bv/n}_(jWѶ W qzilhQIl9::EsW,m I_Y_9`߮߮H-` n _BQR8 "mC!VTKB@Zڑ LZ%ǔDp@Ћe";M~4& AWm>=2|k "{b(O+G}6'--G5" */dD-a)a)%&B"g¬Nc}SSO+;3fvBq}3}7MjZoOއkw~aaJ#Kx`ٜeZb]暡wݭ<g\n QEۡ_ֲ8\JZб,f%\VŔ'<;͕0 ϵjOzkW|8-;:уiLև^S%OBcp1,t3ceqϡM':4 m2dW}c'0^ S H O_u5e9'_8R2$$QAriT:DABI)ATIP ^>(}kxaϞKТ44ݦ?6 Ӽ:^^\f_":~jCښښf:N?Դ,E$ 2$W<OVg$Xg/Ir>QBmao;_|4< *L..BY>MQ1c$ǯmˇ;'Dh,k a3Śtwקl44 ~rȻw/s!9\dֆܝ^ ZZyוwrb Ԧv]>^Oa5 Zg t8eG`E&}vCOv]?u]"ˈ2WIl[~4nickPd;U6ϊN3J~bܓ>>3̀Zi@'D2] JjtOۣ=EPǒ ZB@[r r$3P;T1U1&&&쀈"_[H2{PBhDm yiiix;f|w8<`PNN292ʢlj22jG ?%08wS^P5>n{p9Xk|-"ML~.OkoLgpx}=m)|Tj>;nMbcjfIdkoqE65 B7/B98Qj9=ľ g b0W@7\]>#3UjVk߾<6dE}S+o*#0 Cg5.ڏj߻wmA=4A pA"g _|4|GCTu5![oa,bzN.TSuj!G -4pvz0ˈG*ٖ: MKt/$%[$a,,m v* T]Roef# hhT}0@ɶHYD!'"'"㧼δ8nճGSS8VVA[@kic5uUÂWWBLWU=7[@$@YpA)6m( HPTO(^6^{zk!$5=WyjS@]v|@IL2S9S 9ڈ˝1!a=zsI1I1 tBlVv{qήʮB[f;ٵF螻po)M[uB/lm`s i }/BI:\x{)'{Kޟ-pT4nnMtW$o^hpvT.欄2ԩ)㧆yvVQ w o{t{(ś0; }͠|CSG DеyBjts7\ÄԢ>(Xv1),aR:bbg|Il=ޠ>P֯wnF- ^C_8[fB]dHF(ֆFD+lIwssw""׀lzND ;Inm!!<'D/"9LyVYFȳ}q?)ǿ2w P6B>H轋PQ"c8e4OD,"mF!]Zуj~[eJz#d+\WA1ڵ<7zRlP^Mt~aң̰hI`ct*ו7 3 s!1E5T۫maߛ|Q툻'WTkt=n'r-eܡM)%EG$߹Nn,Auθ D M65)4c䭐bȥ0Rb^W.Ts#YA]} |ICލC ڤZP*´ϒ|_/QQcm-FC!GGkGG@k$ bVOJ%2|HY&uˎ5{88X7twx{p r Nu ^| '  =;3f0@>S VHLiWΞL*L^ҡGGYVONC-j!BlZSNbOB)tjN+4Ș%|3gNǪ.rnU*/0wnc+Vq1DËj6!5O-Gqbw.nt)55V.|~MMJo4!z!ޅ ԃPhb.VieEi]]Q+y`[#nqz˪aP7~by6whyՈey>3ʴ6ֆv{_B7{avt,@/y&y+; I5ԲA!"YY5>J'bU gK"Y>NHIhٻ3cj:NyBDDO4ԝz xJmmҀz˓I((Ϥ[aaeгx"!%rOD)o$}?ah'}x;zz`>mDB00=/=l'gC"['ʫ5|$Oo2}%屛#@udԘ ~+A-Z2q/w\fZ[k`eo/|nD}-o j{Rca~VwKMjjmG0gKgKT+)A6P-]>]9皢|2]CkNr**gu@UOUOwtڃkM61nk*, sShhGZ=N]&]&SG~=jFuuTTΗϗZf(gZL YAott cPՂ.uҀ #*)LY䀆B`U_U͗7_j{j{IN/NYQq(ܦ(;!֪ R|;_Yp3B^8qiȋV7R7:w!O`U`u.rW44ypi+x]L%zy:cbm=9ɹ +&Ub\B~^ ?h=gF4U'¯IVz 'ۊ*VH.`dv7RXPYU{-Ֆ..66 :ëG$:ީTYsVsh)\  w83(#S!BYɖ2[F-+*duB*#(y_]Oz~y?|8n:nw*k=Ҟإtjy@ Y1# xۓ7M _\JL) 0>u+q=:k .Fs-r-n+gmuz;68Obǭ׾.5W`qGN;4UDPc]UqX?Yb}f/yvA6,%.Kr<Ιw?e'Hduqh䊇aAW6rZjdαSzX%t}G^?6{Џ&qvW۬J ˠd|Fg$v,9#>;djC*Ѵ-ʿ y+oI8tH^r%ӦLhO#[,C!22뿯RR=}`+$U*Ӛf_ag4@{! ?ȸ U US z'Gŧ_Qd_dߞUvc*k)ب{ɳSɗ*`ɥgDݛ/H cq 蘲Y)Z؛ݞ&(o:[eUCh֌3L޸V6J^_|.Gp1!*[E19@!^aWaPЦ'RH_7h0Pc׌.kIsdJع\hXD j-Ջwc0 )#;EF[!l9T%Cw3mxG0g~]u,vݷ@FQr~ԬR2j-4w&]6 -!I$`zFQ-P )g<|9$wc%S`C&T{>],QΞ8v=*JD̄o^gSRst>5#V# x!7`pkV~2#R)oV=(Ǜݛ{*tPU.H_ iZr&֡^ pvwv_Ybbo…}V8V8M }c^T( BGZP>X1v*  b@((ȼ)+Մ.!AycAvAVv5ssQWα,gFZLz6)Q2~9e)M-nVg?H@d#˻_?S}x'O gWvm?gv tLI*' G'_ZSi2Π>''D0!!` pX1)#L11S_`=K>Vk0Etp'FFGKS h߷))!(dHÑȳgKdY ۮ#d?A2 nQw]#lȏ̏d Bx!֚QfN1/qQrQ&nEL]8ӗ̗ ` W9|b1p7-_K^*Pib"AΠHI%XvB٦> =GTvۢ`ts|xV!f.mL몰KYl?מYZ&?~j 05%"QEG~&h)i) og,/5W)-66<1}[q{o x$55ƋE+#];}wz/^=W4`ۡ|vᥦ<3z'Ḋq#qP]$֝fV6`JQ(l_O}m /WT5[UnĥHIVtNa}-!!l OVh3Z"(oE肚++Dmy|d %;3nCC"8l%m%mkl@[h*fIzJz:}t7}'XKLXDpRz 'b5+7@p-e%-.6I- i g9&CI\*mwO=fywux.ӊN` ׳cB=-Pz}w]9LRexUyX>LB/x4 j-摰6Rp 0'e ^HqMcg_A6d?!XF>!9I)]j9hWwׄ4jP ݇ d%croRI%YH5FV-$pHcxMkK/VGAO 5hTDfŊ7Ɩ*tտMJxyMN"]g{]Wd".`F쬣+ 57,ٻvEvh0vd$q: ZLL _``'P֒3גII' A ~ab-$|u<--ޕr?,7Zr^9H8[srr<@Ffo(ooäX̙$3cϧ&͐#H/c3K^^S 0Gh5o5GK+p,pԽ@@+&/ 1ߎi0=sS -γ;AQO)YLkmresIh)JqQZ~ M><^iCU@JA򭔓@F ߩ %A\so|Йљ;Xܘիğ:^BA$xiIwud@=Os@qkHze ]AyAU:1-1-o(ڙڹ }VVXD?- Oyy.g)Xte/)ĘĤ3]dlpqq}}3QSշoľݐ0 iylml 6Z~]5J(ɒP7w>y5:r2vRK=on#{m;~wO>OMX_g488^{2Y8cbS4x;o8YBFfأʊlUfIȩdD:2ػdF w\+NCܿUqx<ߟs82i|Ќ~7UCnY7L3,F/bcaQV#z Cv#:u_6ѾkcP.m[?C@|@&5),@UQI kؓaFYB 7[ E{v0@pF-k\;g38q|)WWfr j+ Pp䒞Fijhਹbx085ahm愞՗F11btv}5S9';&XZHӽ? du \w(u?q%H/yZ>? W@J;Lu$fHkYi˯;7g3i-$il wQ194XUi^+ҪEଝS? %ji7Q|Ѣ3J0 ը5 R}ȃ$o꼩CcCR&'l+Jo$Ĵ߻ܚv>c$3Ojp2ABʍcĪF "eyMJ&?5,d-NHh^df=먩l4L uKmeǡ 跞s- fP!u^?(݄'c&n}}I);&.} og oŭ"@ênDc[6c_fVSS{|V @d'3 LkXJ­p8#(qec/p~P񆿴t;U;j{'IWP3} (75'^"^ &U0Ƴ }:dx9˟agΖZrvB0V.h:t }78ғĭDwh7Ns^IUӵɻOSE;ELa\VWm7vBNܧ%aiXa|VNgKOmgiVoA4^?a4ɋnڳgQӥ }/kR'csQTt"A\MI)N,)б!j<^0$5$m47`ڑ7~_a;3i 8Ӹr Zji# #997F693QQWՎPP{_x44͝nn'M\8_8,q޲!ұ,=|]Ӯ lJژcT ~W7]HMq*39g>f̰xl`ڟ׌!jی~MmnbSϹ4% yG{QK~ƽyH^a&;]qXi#pqFITq5{o] 0Wka)e[jWNˑ,x@FL2[C|Q ,LvmH7RΨu]O軝3𞹊#USn kaBme +v?PPB#2Zm(TkŇ&&֦]j"c(,6 >-uV_@ggx1]#/(O,'c mm7#mmӥpPTc!~!>r9 >hJ(six7E&Nb9p{q?cqqƛћQ@ooFZVVɭV[U((-SNgO\* 82%KLw˲ĕ->_㿫_ubO"w…)Α2ޒ{2$H,t~k;m/nt:Э &Psᕎ++3&_ɫ4P zjpSJ(pA{WBBڜM*fR*PRPIQIYjP_,,*V}JJuy?WZ E~[rEv0$mkX$s =f93&==aޏ2P&ddn势 Id XXy?#AǂJZ5S ecIO',ԒԒ((m_@\2;! e_.覀ъ~;O@j.സYy/yaϠKETV G7;wVV&_|%Ta3ĜAnr,ࡴS;YbO-MDOD̉kV44F..$h<ϼsqd8ϯn]Z'$=FI'zweyJN`5.3Х|頲#I#!>g+:2Q(q~g?20Kǵy/wF/\lBx^년n.3bV XL̷%oH1oްp<2-yf0'WagҿdR2Ӛ?7$R𡇊"=hqN&_$s1 [M _D(WBKʞ=k#>Rvw_$t4L=Y{[PVB- 6VIY']n]HZf~x7`I}0ZB,hk NsefEBǞ6 qZ $cvpm-^mڴzgHN`~prĞۇQ`X~\=Dq)׈r?%ɓA@mmp 1QXf@;دR8hz9&aΣneIYd4 eɾ a,}'[%{YPNNd_ Kt{9=繻q\3$]j]߹9fgs '' 3Dgr5ZZ)\XV"̀.hg'8ۖHMqQ/Q$oj ũV}sПA'A&:ͯkծ6`Q۷ ;_;~L[ɟ}GpAdElZ㭊 YnNcCc*Dĉ>5 V=۲ JMqDŽźEѱ?9@W~$ړ'aaaQ rDvGvGs+K.Kf9Y|;(!WNE6kso) hhM^}o__/s,N#84t؈̈̏`3SíG'LWdVdg jH:\Y;<>(̇'ǁs%1dgOX3-ULCFFVNk_ YAϠvvRZ* ؎== vkI>6J`%Ql"~qMqqپ$ϞSqV5z{FQ* )u>8&[&[q=4΀;X(wLEQگ,I BJ a'H02F1F]9PPBiz[-<:)Ic^ZZ=MMoճ(__ד{ZJ` .AΎK;?\9*<[Tǿ[ pnQ}#fC*t0wn&sC',5DǦǨo M?%ۍiO6TGN\]JDӞA?sS~iVx0,zzI\1v c3>9JO'?kpTZQaO3~BA2zaO#XlمXiyXjJ4~vrJ$)'3fi@R/0OkI1S┮RnoyQI}tȕN3Mv~i#li/4‚@FmZݧ\3A=z>ދދ|0AW]-ZZ,)i/(Vjn򬵢v'%I&IZu>T j8 bCoO/J)Pkv_`WNџH,,Q RQB-{:x؃#qez`o ڰ4T1W()r30h)[33Ӥ rb& | |9CH2; +egvZ` +\YI3XT$wǿ?g$)'jbOhim{թǷr kb3; ^:}F;,z38a81궯Q53A2䠰<+.).͝MpzE:T(RbV b pia88׵Xrcrci)ch?BzQ(w.a=Yڂ \"sBNQNѴ] 2  _ߘ@U _;Oy. -54Yob'Z/llrbuub>AA;ԤI9V9sU[-ss3"g,Bwڣu';,?\HP]QUt@=0H,q{o=P 6 %.qje2iO|{R1ePƘ2.OzEV?0t~2‹lѤj̕q.´g^lCST7ӪKd-ݗg0%p;Eg'ĕ|-T<ˏ0/vXfRcLô?D_ ૞I"JAUĨ@V,!k*ĮK83 1 ~T[p4֯:7Ů=&ީU>,/4JwϬ;5cڿ+&pį`P̭RkD}F;t9O 1 ~#(O.`#3Jp"/B=c^Hs%{0jObU'y8%/RI 3i(leZL˜@paiޢO]BX|=9q^_ĴN+REߦ5ZR]Y'oe/_c%a97|i~~;nqw4>|ge)v I'K!N<y9zz׆ֆJ{je/L oֲJV)x`вv{tgPڒA]fdx".p]Ts@zG"t:Xve,m `Vճ? 9~i?8? 8SΩ)|BB/jhDVw& :87p^pZZj߬ht+i{VFG27;}ր\c[͎F4$[`Wzj_D|<Lٻg8_.*k2wZ~YK[.f#2VMJ^3yJ0سggPTcXc @QK!~ JX# ~<e"(C{'q d@zS@^ՠwDz9-t:aS׍O;c,,{e^m5@K2J\ (C={ND.U27fP>^>p ӋA6z"ݚN@K[[fn%[]i0"77sLJٚmk;}v`52 T}:vޯ(߆S`ܕi~[fΤ2Ŕ r$&z%0wqP!L3&eh.B<$꼻J@0 MMjcx;i1j}yu1pёEuQa~lkA(ӬpZԽqC7 QpMIlL#((zE9C9Sxb hdɥzܫמOfN3*aW^TyOw #Nx` ` /oq_ѵM^agp$ϙS#Z(g) KQ5>j`W Grr xB_?TD*EuWWTh~ ;k䚮X.36d;A'm 0O@bkYuw^4EB[?ۂ";gy?B.{B j/kw+F^<"ͅd6m6]v".Π,/W$fX.[3Vq(H&%5ޙޙe_ >PŪh>9 ES6 ## 8vL=?K$F::R&3#gF\~N%KkIY=mfxRf߼~ Ap&{OepY%deYee%̈落gX[ed%aΈv'Y"Y!9ui~?R=")#)P}} ymW @wQtQD'=rOY̩soŞ 0{Q@Z7ɠ,[z8-᪫E˅jwqq$0YjAצ$24tnQN5"&\8iK"zײjNsw'we3eN؛S8!Sf5wS*p K 5hmUuAAEX={iqsei֣ϸR _=ߏObZJjtg{zB j60HZ/hdנ~fNSIQo:}A j?CWT #iwW-hY>)Mޤ4u(AjH|Gr3/W%a 7el2lJk8]P'ǭ yGTs2OY'vݝPJ,2U-nq٪+^Q|qSJ#X4n6ɵxbMܬq3,37VeTSKmi_n\8F̄m$QBOoR3gl{L!f`&OlZF/F35Q-6fou7&n&PNFFA$g id88ddX; ʅ{><<rc:ׂHJk=8p`c3j1~QQAxe/tbKc,E~}u,O}'q 'Pe|4UU .nn[ B!'7:w3j@^KгT.l8Qr{y!(r݆8β ~ckdÍ?c"(z܊8{Ifͭ6ҭÖ /noфTti 7Σ̣tG&i&FiS>FWa>(''\)e:P?-(U)z|`5yõ CV"eAʴ(D}]m-ok}׹hKcD0VYdvpŧ-=66^ta #mcf%wHFݰD/+/+TIOU[tsqgG+N` E^9{Jxg`c~QWzAMGTsTQeVLu*v3C j?C{6b 9输yP朱MeAs=0GДhd= گݮDK5<:GftE53VHeE!([P8)OTB[($fdp1Wڪ6AGkS{2ܚO-afy+CC"_zov~1BR"W<{@FO׃u~w}@ Fc}=qyvoߺ𳖱qpНzv}==D.\Q4FpwtY.c'<Ť^rvy(0@cTjr zo@7F7ֺt.SY__=5u%-D^A.Ѫf&YcíIe%I4r3s~~:λAFdϛ/oun{S$py;u[&][{iB4JkXNVNB]2i"߯!E\mg؜$6.q&͵.B>6P:B_TGRH gpMy!:}yj?V9v 3ߵԸ~MO%K[˰~ /EQPuZN,|Iu"pC~|ἷ;TՕz]}qGI;gvjLN/WQ15Jd)E;a1Ծ9ӷIrFJ7v {7rW,75jf3V^2/o-~z d 5n o'4)xsZiԖԖuh@ ~nGUAخط9Drdvo1Ja+h?[dOc? KNtJJC!>R_'qMZevo'oH 6 zA p'r5&3.5E'*::#|cs&մ\(h""g(u-:npτDKF͹iZ{v˔XL^y&t=H'+R&8_3};UN.p,M=uCk`C=i?э=Fk AIT>\`fg-{ QCE%bZ ]_c]cr4/X RŨe>$mr*T#/"8$S--*}wH>!iy3 O^n &@__4kTL{// /%|KMbFFQ>ȅȥ#w͸~'v䵀F| t1>\(@.fLk8ꃁƭ--о?HF"R~~٫ n:x:̉osQ<>hiI_Q54ҷmu"fegp+3 tt˅u۰ψψ R"Q==yJzGZT:OXö:Sc6E궊C/>8&Ġd{~6h~$EʵmShճ;}nE@llgZIQgDhΝ]Sy3oz+yX2q{$~&Uމ804Y}#ŦoD.@Fn"GlǻsA h́ݹ  biQ~2a˪6&F E#8۠EcQ>ҨM;]M?Ճo.&#ͽ6CZFJگQ;nw㉵I)9/3:Wn_?E||Agm/nxJٽW:rtoycX]_Qmb'zEK)har[ 7kS􌚺a:.wֿBn3Q:[ا!֛s]Ul`l3WnQlTzq4\ʉCEڿHW(v )QQԳZZc9"$Oc@s>`M єc 43ĹZC DNƓ?UA8gjBn{ pw׵qu:kmHHw`*~iŞ%(ϹD 8l:;=v ɓ&Oӓk!b$V]V]oOtS@M==#՟Qg{YfTfT>q= gO(<<ƾ.j8u/e|j~z0$%,o wr^<y ׾WjNyޒH(mO z֣5ᔳ0]k? ݱch_#2,0aVZz$@1;_Y7`rrlulUI/ ;_0!o*Y! d>xY ɞ^|zV垸ፀ;7M^~iXqR0CS?X8FZIjjHo.m~{"n?op`1y*YOymf3Ǹ6>VZ"oGΧ[>-)`bk|UǪQQ*v_PoMGG 2c>uQ$#te,|Lbo&Kx[ vFKڶo6mVvcqm}3@y},.,\_{Eθzܕw7A?!*G.;hnߤA/{w/0}?s3DvL6|cY̙0f@zuŽo[nx:6\~u.JM,/Щ) I EFɹ)9y eEřy zfzz `Q0 F(`Q0 F(`Q0 F(`Q0 F(`Q0 >3 !par2cmdline-turbo-1.4.0/tests/subdirdata.tar.gz000066400000000000000000000040201514221355600214600ustar00rootroot00000000000000YPfqٜ=tVg{Iĉ6$a&=P$OLy9@#nPs崿>.m{y16]*طY^nS<.;W_6iXxƳwMs=ԱkZxno\oW;]=Mऌ[Qn02Oi! Ϩf]r8몯6 1}u}?:ΑW q~JØٸۥS}+6p~_\үo>wxYQNt?MUl4, +yX< ٓVn02Oi! Ϩf]r8i* 1}u]:mW q~JØٸۥS}+UR ܈L UkxZ=-XZD崿>LSڼ[l4,LSڼWl4,?'\ڇOGpB [Tn02Oi퐆gTi9{me!s_cZxS<.;W_7/eU|i*k5i:m\opsp̛W*`eXN_M@par2cmdline-turbo-1.4.0/tests/test1000077500000000000000000000022371514221355600172050ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata.tar.gz" 2>&1 || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata-par1files.tar.gz" 2>&1 || { echo "ERROR: Could not extract par test files" ; exit 1; } >&2 banner="Verifying using PAR 1.0 data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY v testdata.par || { echo "ERROR: Initial PAR 1.0 verification failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test1.ps1000066400000000000000000000014461514221355600177050ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 1: Verify using PAR 1.0 data $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination "." Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata-par1files.tar.gz") -Destination "." Write-Banner "Verifying using PAR 1.0 data" $exitCode = Invoke-Par2 -Arguments @("v", "testdata.par") if ($exitCode -ne 0) { Exit-TestWithError "Initial PAR 1.0 verification failed" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test10000077500000000000000000000025571514221355600172720ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/smallsubdirdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf "$TESTDATA/smallsubdirdata-par2files.tar.gz" || { echo "ERROR: Could not extract par test files" ; exit 1; } >&2 banner="Repairing deleted subdir using PAR 2.0 data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes cp subdir1/test-0.data ./test-0.data.orig rm -rf subdir1 $PARBINARY r testdata.par2 || { echo "ERROR: Reconstruction of two files using PAR 2.0 failed" ; exit 1; } >&2 cmp -s subdir1/test-0.data test-0.data.orig || { echo "ERROR: Repaired files do not match originals" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test10.ps1000066400000000000000000000021331514221355600177570ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 10: Repair deleted subdir using PAR 2.0 data $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "smallsubdirdata.tar.gz") -Destination "." Expand-TarGz -Archive (Join-Path $TESTDATA "smallsubdirdata-par2files.tar.gz") -Destination "." Write-Banner "Repairing deleted subdir using PAR 2.0 data" Copy-Item "subdir1\test-0.data" "test-0.data.orig" Remove-Item -Recurse -Force "subdir1" $exitCode = Invoke-Par2 -Arguments @("r", "testdata.par2") if ($exitCode -ne 0) { Exit-TestWithError "Reconstruction of deleted subdir using PAR 2.0 failed" } if (-not (Compare-Files "subdir1\test-0.data" "test-0.data.orig")) { Exit-TestWithError "Repaired files do not match originals" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test11000077500000000000000000000031051514221355600172610ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/100blocks.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="create par2files using 100 blocks" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY c -b100 testdata.par2 * || { echo "ERROR: construction of files using PAR 2.0 failed" ; exit 1; } >&2 banner="repair 5% of 100 blocks par2files removing 3 files" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes rm file file1 file3 $PARBINARY r testdata.par2 * || { echo "ERROR: construction of files using PAR 2.0 failed" ; exit 1; } >&2 banner="repair 5% of 100 blocks par2files removing 1 files" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes rm file5 $PARBINARY r testdata.par2 * || { echo "ERROR: construction of files using PAR 2.0 failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test11.ps1000066400000000000000000000015041514221355600177610ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 11: Read beyond EOF handling $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "readbeyondeof.tar.gz") -Destination "." Write-Banner "Testing read beyond EOF handling" # The archive contains test.par2, not testdata.par2 # This test should verify without error even with the edge case $exitCode = Invoke-Par2 -Arguments @("v", "test.par2") if ($exitCode -ne 0) { Exit-TestWithError "Read beyond EOF test failed" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 } par2cmdline-turbo-1.4.0/tests/test12000077500000000000000000000022101514221355600172560ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/readbeyondeof.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="repair files where the filesize got changed" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes mv test.data test.data-correct dd if=test.data-correct bs=113579 count=1 of=test.data $PARBINARY r test.par2 || { echo "ERROR: repair files using PAR 2.0 failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test12.ps1000066400000000000000000000013631514221355600177650ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 12: par2-0.6.8 crash test $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "par2-0.6.8-crash.tar.gz") -Destination "." Write-Banner "Testing par2-0.6.8 crash case" # This test should not crash - exit code may vary $exitCode = Invoke-Par2 -Arguments @("v", "crashtest.par2") Write-Host "Crash test completed without crashing (exit code: $exitCode)" Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test13000077500000000000000000000026001514221355600172620ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="create 5% recovery files" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY c testdata.par2 *.data || { echo "ERROR: creating repair files using PAR 2.0 failed" ; exit 1; } >&2 banner="repair files where 1 byte got removed at the end of a file" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes mv test-1.data test-1.data-correct dd if=test-1.data-correct bs=177173 count=1 of=test-1.data $PARBINARY r testdata.par2 || { echo "ERROR: repair files using PAR 2.0 failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test13.ps1000066400000000000000000000026151514221355600177670ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 13: Repair file where 1 byte got removed at the end of a file $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination "." Write-Banner "create 5% recovery files" $exitCode = Invoke-Par2 -Arguments @("c", "testdata.par2", "test-0.data", "test-1.data", "test-2.data", "test-3.data", "test-4.data", "test-5.data", "test-6.data", "test-7.data", "test-8.data", "test-9.data") if ($exitCode -ne 0) { Exit-TestWithError "creating repair files using PAR 2.0 failed" } Write-Banner "repair files where 1 byte got removed at the end of a file" # Save original and truncate by 1 byte Move-Item "test-1.data" "test-1.data-correct" $bytes = [System.IO.File]::ReadAllBytes("test-1.data-correct") $truncatedBytes = $bytes[0..($bytes.Length - 2)] [System.IO.File]::WriteAllBytes("test-1.data", $truncatedBytes) $exitCode = Invoke-Par2 -Arguments @("r", "testdata.par2") if ($exitCode -ne 0) { Exit-TestWithError "repair files using PAR 2.0 failed" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test14000077500000000000000000000025741514221355600172750ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="create 5% recovery files" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY c testdata.par2 *.data || { echo "ERROR: creating repair files using PAR 2.0 failed" ; exit 1; } >&2 banner="repair files where 1 byte got removed at the beginning of a file" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes mv test-1.data test-1.data-correct tail -c 177173 test-1.data-correct > test-1.data $PARBINARY r testdata.par2 || { echo "ERROR: repair files using PAR 2.0 failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test14.ps1000066400000000000000000000026301514221355600177650ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 14: Repair file where 1 byte got removed at the beginning of a file $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination "." Write-Banner "create 5% recovery files" $exitCode = Invoke-Par2 -Arguments @("c", "testdata.par2", "test-0.data", "test-1.data", "test-2.data", "test-3.data", "test-4.data", "test-5.data", "test-6.data", "test-7.data", "test-8.data", "test-9.data") if ($exitCode -ne 0) { Exit-TestWithError "creating repair files using PAR 2.0 failed" } Write-Banner "repair files where 1 byte got removed at the beginning of a file" # Save original and remove first byte Move-Item "test-1.data" "test-1.data-correct" $bytes = [System.IO.File]::ReadAllBytes("test-1.data-correct") $truncatedBytes = $bytes[1..($bytes.Length - 1)] [System.IO.File]::WriteAllBytes("test-1.data", $truncatedBytes) $exitCode = Invoke-Par2 -Arguments @("r", "testdata.par2") if ($exitCode -ne 0) { Exit-TestWithError "repair files using PAR 2.0 failed" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test15000077500000000000000000000021561514221355600172720ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/par2-0.6.8-crash.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="repair files should succeed, (issue #35)" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes cd par2-0.6.8-crash $PARBINARY r pack-ea5f7f848340980493ed39f5b7173d956c680e43.par2 || { echo "ERROR: repair files using PAR 2.0 failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test15.ps1000066400000000000000000000015111514221355600177630ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 15: Repair files should succeed (issue #35) $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "par2-0.6.8-crash.tar.gz") -Destination "." Write-Banner "repair files should succeed, (issue #35)" Set-Location "par2-0.6.8-crash" $exitCode = Invoke-Par2 -Arguments @("r", "pack-ea5f7f848340980493ed39f5b7173d956c680e43.par2") if ($exitCode -ne 0) { Exit-TestWithError "repair files using PAR 2.0 failed" } Set-Location .. Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test16000077500000000000000000000025631514221355600172750ustar00rootroot00000000000000#!/bin/sh ###### # This test was 'wrong', it should test if there are data files outside the # basepath ###### execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/subdirdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="don't allow files outside par2 basedir, (issue #34, #36)" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes cd subdir1 $PARBINARY c -r2 test.par2 *.data ../subdir2/*.data > $testname.log || { echo "ERROR: creating files using PAR 2.0 failed" ; exit 1; } >&2 # check if there were ignored files grep 'Ignoring' "$testname.log" || { echo "ERROR: there were no files ignored"; exit 1; } >&2 rm "$testname.log" cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test16.ps1000066400000000000000000000023301514221355600177640ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 16: Don't allow files outside par2 basedir (issue #34, #36) $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "subdirdata.tar.gz") -Destination "." Write-Banner "don't allow files outside par2 basedir, (issue #34, #36)" Set-Location "subdir1" $result = Invoke-Par2 -Arguments @("c", "-r2", "test.par2", "test-0.data", "test-1.data", "test-2.data", "test-3.data", "test-4.data", "..\subdir2\test-5.data", "..\subdir2\test-6.data", "..\subdir2\test-7.data", "..\subdir2\test-8.data", "..\subdir2\test-9.data") -ReturnObject if ($result.ExitCode -ne 0) { Exit-TestWithError "creating files using PAR 2.0 failed" } # Check if there were ignored files $output = $result.StdOut + $result.StdErr if ($output -notmatch "Ignoring") { Exit-TestWithError "there were no files ignored" } Set-Location .. Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test17000077500000000000000000000021041514221355600172650ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/bug44.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="remove subdir structure and repair, see #44" dashes=`echo "$banner" | sed s/./-/g` rm -rf subdir1 echo $dashes echo $banner echo $dashes $PARBINARY r recovery.par2 || { echo "ERROR: reparation of files using PAR 2.0 failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test17.ps1000066400000000000000000000014321514221355600177670ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 17: Remove subdir structure and repair (see #44) $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "bug44.tar.gz") -Destination "." Write-Banner "remove subdir structure and repair, see #44" Remove-Item -Recurse -Force "subdir1" $exitCode = Invoke-Par2 -Arguments @("r", "recovery.par2") if ($exitCode -ne 0) { Exit-TestWithError "reparation of files using PAR 2.0 failed" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test18000077500000000000000000000025631514221355600172770ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 cp "$TESTDATA/flatdata.tar.gz" ./ || { echo "ERROR: Could not copy data test files" ; exit 1; } >&2 banner="Creating PAR 2.0 recovery data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY c recovery flatdata.tar.gz || { echo "ERROR: Creating PAR 2.0 data failed" ; exit 1; } >&2 banner="Verifying using PAR 2.0 data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes mv flatdata.tar.gz flatdata.tar.gz-orig dd if=flatdata.tar.gz-orig bs=1983 count=1 of=flatdata.tar.gz rm -f flatdata.tar.gz-orig $PARBINARY r recovery.par2 ./* || { echo "ERROR: PAR 2.0 repair failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test18.ps1000066400000000000000000000024671514221355600200010ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 18: Create PAR 2.0 recovery data and repair truncated file $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Copy-Item (Join-Path $TESTDATA "flatdata.tar.gz") ".\" Write-Banner "Creating PAR 2.0 recovery data" $exitCode = Invoke-Par2 -Arguments @("c", "recovery", "flatdata.tar.gz") if ($exitCode -ne 0) { Exit-TestWithError "Creating PAR 2.0 data failed" } Write-Banner "Verifying using PAR 2.0 data" # Truncate the file Move-Item "flatdata.tar.gz" "flatdata.tar.gz-orig" $bytes = [System.IO.File]::ReadAllBytes("flatdata.tar.gz-orig") $truncatedBytes = $bytes[0..1982] # Keep first 1983 bytes [System.IO.File]::WriteAllBytes("flatdata.tar.gz", $truncatedBytes) Remove-Item "flatdata.tar.gz-orig" $allFiles = Get-ChildItem -File | Select-Object -ExpandProperty Name $exitCode = Invoke-Par2 -Arguments (@("r", "recovery.par2") + $allFiles) if ($exitCode -ne 0) { Exit-TestWithError "PAR 2.0 repair failed" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test19000077500000000000000000000040731514221355600172760ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 banner="Creating 1024 byte test data file" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes dd if=/dev/urandom of=datafile bs=1024 count=1 || { echo "ERROR: Unable to create test data file" ; exit 1; } >&2 ls -l banner="Creating PAR 2.0 recovery data (block size 200)" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY c -s200 -c1 recovery datafile || { echo "ERROR: Creating PAR 2.0 data failed" ; exit 1; } >&2 ls -l banner="Damaging data file (trim first 100 bytes)" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes mv datafile datafile.orig dd if=datafile.orig of=datafile bs=100 skip=1 || { echo "ERROR: Unable to create damaged data file" ; exit 1; } >&2 rm datafile.orig ls -l banner="Repairing using PAR 2.0 data (with skip leaway 99 - should fail)" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY r -N -S99 -vv recovery.par2 && { echo "ERROR: Repair should not be possible with skip leaway set to 99" ; exit 1; } >&2 ls -l banner="Repairing using PAR 2.0 data (with skip leaway 100 - should succeed)" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY r -N -S100 -vv recovery.par2 || { echo "ERROR: Repair should be possible with skip leaway set to 100" ; exit 1; } >&2 ls -l cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test19.ps1000066400000000000000000000031711514221355600177730ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 19: Skip leaway test $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Write-Banner "Creating 1024 byte test data file" New-RandomFile -Path "datafile" -SizeBytes 1024 Write-Banner "Creating PAR 2.0 recovery data (block size 200)" $exitCode = Invoke-Par2 -Arguments @("c", "-s200", "-c1", "recovery", "datafile") if ($exitCode -ne 0) { Exit-TestWithError "Creating PAR 2.0 data failed" } Write-Banner "Damaging data file (trim first 100 bytes)" Move-Item "datafile" "datafile.orig" $bytes = [System.IO.File]::ReadAllBytes("datafile.orig") $damagedBytes = $bytes[100..($bytes.Length - 1)] [System.IO.File]::WriteAllBytes("datafile", $damagedBytes) Remove-Item "datafile.orig" Write-Banner "Repairing using PAR 2.0 data (with skip leaway 99 - should fail)" $exitCode = Invoke-Par2 -Arguments @("r", "-N", "-S99", "-vv", "recovery.par2") if ($exitCode -eq 0) { Exit-TestWithError "Repair should not be possible with skip leaway set to 99" } Write-Banner "Repairing using PAR 2.0 data (with skip leaway 100 - should succeed)" $exitCode = Invoke-Par2 -Arguments @("r", "-N", "-S100", "-vv", "recovery.par2") if ($exitCode -ne 0) { Exit-TestWithError "Repair should be possible with skip leaway set to 100" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test2000077500000000000000000000022261514221355600172040ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata-par2files.tar.gz" || { echo "ERROR: Could not extract par test files" ; exit 1; } >&2 banner="Verifying using PAR 2.0 data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY v testdata.par2 || { echo "ERROR: Initial PAR 2.0 verification failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test2.ps1000066400000000000000000000014471514221355600177070ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 2: Verify using PAR 2.0 data $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination "." Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata-par2files.tar.gz") -Destination "." Write-Banner "Verifying using PAR 2.0 data" $exitCode = Invoke-Par2 -Arguments @("v", "testdata.par2") if ($exitCode -ne 0) { Exit-TestWithError "Initial PAR 2.0 verification failed" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test20000077500000000000000000000030461514221355600172650ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 banner="generate datafile with 2000 random bytes" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes dd bs=1000 count=2 if=/dev/urandom of=myfile.dat banner="Creating PAR 2.0 recovery data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY c -s1000 -c0 recovery myfile.dat || { echo "ERROR: Creating PAR 2.0 data failed" ; exit 1; } >&2 banner="split files" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes dd bs=1000 count=1 if=myfile.dat of=myfile.dat.001 dd bs=1000 count=1 skip=1 if=myfile.dat of=myfile.dat.002 rm myfile.dat banner="Repairing using PAR 2.0 data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY r recovery.par2 ./* || { echo "ERROR: PAR 2.0 repair failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test20.ps1000066400000000000000000000025121514221355600177610ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 20: Generate datafile with 2000 random bytes and repair split file $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Write-Banner "generate datafile with 2000 random bytes" New-RandomFile -Path "myfile.dat" -SizeBytes 2000 Write-Banner "Creating PAR 2.0 recovery data" $exitCode = Invoke-Par2 -Arguments @("c", "-s1000", "-c0", "recovery", "myfile.dat") if ($exitCode -ne 0) { Exit-TestWithError "Creating PAR 2.0 data failed" } Write-Banner "split files" $bytes = [System.IO.File]::ReadAllBytes("myfile.dat") [System.IO.File]::WriteAllBytes("myfile.dat.001", $bytes[0..999]) [System.IO.File]::WriteAllBytes("myfile.dat.002", $bytes[1000..1999]) Remove-Item "myfile.dat" Write-Banner "Repairing using PAR 2.0 data" $allFiles = Get-ChildItem -File | Select-Object -ExpandProperty Name $exitCode = Invoke-Par2 -Arguments (@("r", "recovery.par2") + $allFiles) if ($exitCode -ne 0) { Exit-TestWithError "PAR 2.0 repair failed" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test21000077500000000000000000000027001514221355600172620ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 banner="save parfiles outside of basedirectory" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes mkdir parfiles mkdir datafiles banner="Creating PAR 2.0 recovery data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes cd datafiles tar -xzf "$TESTDATA/flatdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 $PARBINARY c -B ./ ../parfiles/recovery * || { echo "ERROR: Creating PAR 2.0 data failed" ; exit 1; } >&2 banner="Verifying PAR 2.0 recovery data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY v -B ./ ../parfiles/recovery.par2 || { echo "ERROR: Verifying PAR 2.0 data failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test21.ps1000066400000000000000000000024501514221355600177630ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 21: Save parfiles outside of basedirectory $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Write-Banner "save parfiles outside of basedirectory" New-Item -ItemType Directory -Path "parfiles" -Force | Out-Null New-Item -ItemType Directory -Path "datafiles" -Force | Out-Null Write-Banner "Creating PAR 2.0 recovery data" Set-Location "datafiles" Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination "." $dataFiles = Get-ChildItem -Filter "*.data" | Select-Object -ExpandProperty Name $exitCode = Invoke-Par2 -Arguments (@("c", "-B", ".\", "..\parfiles\recovery") + $dataFiles) if ($exitCode -ne 0) { Exit-TestWithError "Creating PAR 2.0 data failed" } Write-Banner "Verifying PAR 2.0 recovery data" $exitCode = Invoke-Par2 -Arguments @("v", "-B", ".\", "..\parfiles\recovery.par2") if ($exitCode -ne 0) { Exit-TestWithError "Verifying PAR 2.0 data failed" } Set-Location .. Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test22000077500000000000000000000024101514221355600172610ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 faraway="$TESTROOT/run$testname/in/a/folder/far/far/away" mkdir -p "$faraway" || { echo "ERROR: Could not create fancy folder" ; exit 1; } >&2 mkdir rundir tar -xzf "$TESTDATA/flatdata.tar.gz" -C "$faraway" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="par2 can be run from any starting dir" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes cd rundir $PARBINARY c -r2 -B"$faraway" "$faraway"/test.par2 "$faraway"/*.data || { echo "ERROR: create of PAR 2.0 files failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test22.ps1000066400000000000000000000021661514221355600177700ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 22: par2 can be run from any starting dir (create) $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname $faraway = Join-Path $PWD "in\a\folder\far\far\away" New-Item -ItemType Directory -Path $faraway -Force | Out-Null New-Item -ItemType Directory -Path "rundir" -Force | Out-Null Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination $faraway Write-Banner "par2 can be run from any starting dir" Set-Location "rundir" $dataFiles = Get-ChildItem -Path $faraway -Filter "*.data" | Select-Object -ExpandProperty FullName $exitCode = Invoke-Par2 -Arguments (@("c", "-r2", "-B", $faraway, (Join-Path $faraway "test.par2")) + $dataFiles) if ($exitCode -ne 0) { Exit-TestWithError "create of PAR 2.0 files failed" } Set-Location .. Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test23000077500000000000000000000025721514221355600172730ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 faraway="$TESTROOT/run$testname/in/a/folder/far/far/away" mkdir -p "$faraway" || { echo "ERROR: Could not create fancy folder" ; exit 1; } >&2 mkdir rundir tar -xzf "$TESTDATA/flatdata.tar.gz" -C "$faraway" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata-par2files.tar.gz" -C "$faraway" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="par2 can be run from any starting dir" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes cd rundir $PARBINARY v -B "$faraway" "$faraway"/testdata.par2 || { echo "ERROR: verify of PAR 2.0 files failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test23.ps1000066400000000000000000000021361514221355600177660ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 23: par2 can be run from any starting dir (verify) $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname $faraway = Join-Path $PWD "in\a\folder\far\far\away" New-Item -ItemType Directory -Path $faraway -Force | Out-Null New-Item -ItemType Directory -Path "rundir" -Force | Out-Null Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination $faraway Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata-par2files.tar.gz") -Destination $faraway Write-Banner "par2 can be run from any starting dir" Set-Location "rundir" $exitCode = Invoke-Par2 -Arguments @("v", "-B", $faraway, (Join-Path $faraway "testdata.par2")) if ($exitCode -ne 0) { Exit-TestWithError "verify of PAR 2.0 files failed" } Set-Location .. Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test24000077500000000000000000000042121514221355600172650ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 faraway="$TESTROOT/run$testname/in/a/folder/far/far/away" mkdir -p "$faraway" || { echo "ERROR: Could not create fancy folder" ; exit 1; } >&2 mkdir rundir tar -xzf "$TESTDATA/flatdata.tar.gz" -C "$faraway" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata-par2files.tar.gz" -C "$faraway" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 cp "$faraway"/test-0.data "$faraway"/test-0.data.orig cp "$faraway"/test-1.data "$faraway"/test-1.data.orig cp "$faraway"/test-2.data "$faraway"/test-2.data.orig cp "$faraway"/test-3.data "$faraway"/test-3.data.orig cp "$faraway"/test-4.data "$faraway"/test-4.data.orig cp "$faraway"/test-5.data "$faraway"/test-5.data.orig cp "$faraway"/test-6.data "$faraway"/test-6.data.orig cp "$faraway"/test-7.data "$faraway"/test-7.data.orig cp "$faraway"/test-8.data "$faraway"/test-8.data.orig cp "$faraway"/test-9.data "$faraway"/test-9.data.orig banner="par2 can be run from any starting dir" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes cd rundir rm -f "$faraway"/test-1.data "$faraway"/test-3.data $PARBINARY r -B"$faraway" "$faraway"/testdata.par2 || { echo "ERROR: repair of PAR 2.0 files failed" ; exit 1; } >&2 cmp -s "$faraway"/test-1.data "$faraway"/test-1.data.orig && cmp -s "$faraway"/test-3.data "$faraway"/test-3.data.orig || { echo "ERROR: Repaired files do not match originals" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test24.ps1000066400000000000000000000034551514221355600177740ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 24: par2 can be run from any starting dir (repair) $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname $faraway = Join-Path $PWD "in\a\folder\far\far\away" New-Item -ItemType Directory -Path $faraway -Force | Out-Null New-Item -ItemType Directory -Path "rundir" -Force | Out-Null Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination $faraway Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata-par2files.tar.gz") -Destination $faraway # Save original files for ($i = 0; $i -le 9; $i++) { Copy-Item (Join-Path $faraway "test-$i.data") (Join-Path $faraway "test-$i.data.orig") } Write-Banner "par2 can be run from any starting dir" Set-Location "rundir" # Delete some files Remove-Item (Join-Path $faraway "test-1.data") Remove-Item (Join-Path $faraway "test-3.data") $exitCode = Invoke-Par2 -Arguments @("r", "-B", $faraway, (Join-Path $faraway "testdata.par2")) if ($exitCode -ne 0) { Exit-TestWithError "repair of PAR 2.0 files failed" } # Verify repaired files match originals if (-not (Compare-Files (Join-Path $faraway "test-1.data") (Join-Path $faraway "test-1.data.orig"))) { Exit-TestWithError "Repaired file test-1.data does not match original" } if (-not (Compare-Files (Join-Path $faraway "test-3.data") (Join-Path $faraway "test-3.data.orig"))) { Exit-TestWithError "Repaired file test-3.data does not match original" } Set-Location .. Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test25000077500000000000000000000021061514221355600172660ustar00rootroot00000000000000#!/bin/sh -x execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 banner="par2 with full path" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes echo "file contents" > some-file $PARBINARY create "$(pwd)"/some-file || { echo "ERROR: Failed to create parchive"; exit1; } >&2 echo "corrupted contents" > some-file $PARBINARY repair some-file || { echo "ERROR: Failed to repair"; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test25.ps1000066400000000000000000000021101514221355600177600ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 25: par2 with full path $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Write-Banner "par2 with full path" Set-Content -Path "some-file" -Value "file contents" Copy-Item "some-file" "some-file.orig" $fullPath = (Resolve-Path "some-file").Path $exitCode = Invoke-Par2 -Arguments @("create", $fullPath) if ($exitCode -ne 0) { Exit-TestWithError "Failed to create parchive" } Set-Content -Path "some-file" -Value "corrupted contents" $exitCode = Invoke-Par2 -Arguments @("repair", "some-file") if ($exitCode -ne 0) { Exit-TestWithError "Failed to repair" } if (-not (Compare-Files "some-file" "some-file.orig")) { Exit-TestWithError "Repaired file does not match original" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test26000077500000000000000000000022411514221355600172670ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 banner="par2 with full path and creation and repair in subfolder" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes mkdir subfolder echo "file contents" > subfolder/some-file $PARBINARY create "$(pwd)"/subfolder/some-file || { echo "ERROR: Failed to create parchive"; exit1; } >&2 echo "corrupted contents" > subfolder/some-file $PARBINARY repair subfolder/some-file || { echo "ERROR: Failed to repair"; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test26.ps1000066400000000000000000000024501514221355600177700ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 26: par2 with full path and creation and repair in subfolder $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Write-Banner "par2 with full path and creation and repair in subfolder" New-Item -ItemType Directory -Path "subfolder" -Force | Out-Null Set-Content -Path "subfolder\some-file" -Value "file contents" Copy-Item "subfolder\some-file" "subfolder\some-file.orig" $fullPath = (Resolve-Path "subfolder\some-file").Path $exitCode = Invoke-Par2 -Arguments @("create", $fullPath) if ($exitCode -ne 0) { Exit-TestWithError "Failed to create parchive" } Set-Content -Path "subfolder\some-file" -Value "corrupted contents" $exitCode = Invoke-Par2 -Arguments @("repair", "subfolder\some-file") if ($exitCode -ne 0) { Exit-TestWithError "Failed to repair" } if (-not (Compare-Files "subfolder\some-file" "subfolder\some-file.orig")) { Exit-TestWithError "Repaired file does not match original" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test27000077500000000000000000000022011514221355600172640ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 banner="par2 with full path" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes mkdir -p folder/subfolder ln -s folder/subfolder tmp cd tmp echo "file contents" > some-file $PARBINARY create "$(pwd)"/some-file || { echo "ERROR: Failed to create parchive"; exit1; } >&2 echo "corrupted contents" > some-file $PARBINARY repair some-file || { echo "ERROR: Failed to repair"; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test27.ps1000066400000000000000000000007551514221355600177770ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 27: par2 with full path through symlink # DUMMY script on windows, just here for completeness sake $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) Initialize-Test -TestName $testname Write-Banner "par2 with full path through symlink" Write-Host "SKIPPED: no symbolic link testing on Windows" Complete-Test exit 0 par2cmdline-turbo-1.4.0/tests/test28000077500000000000000000000035161514221355600172770ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="Ensuring silent noise level" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY c -a newtest -qq test-*.data >stdout || { echo "ERROR: create failed" ; exit 1; } >&2 test ! -s stdout || { echo "ERROR: create with -qq produced output to stdout" ; exit 1; } >&2 $PARBINARY v -qq newtest test-*.data >stdout || { echo "ERROR: verify failed" ; exit 1; } >&2 test ! -s stdout || { echo "ERROR: verify with -qq produced output to stdout" ; exit 1; } >&2 $PARBINARY r -qq newtest test-*.data >stdout || { echo "ERROR: repair failed" ; exit 1; } >&2 test ! -s stdout || { echo "ERROR: repair with -qq produced output to stdout" ; exit 1; } >&2 $PARBINARY c -a newtest -qq test-*.data >stdout 2>stderr && { echo "ERROR: second create succeeded but shouldn't have" ; exit 1; } >&2 test ! -s stdout || { echo "ERROR: second create with -qq produced output to stdout" ; exit 1; } >&2 test -s stderr || { echo "ERROR: second create with -qq did not produce output to stderr" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test28.ps1000066400000000000000000000046111514221355600177730ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 28: Ensuring silent noise level (-qq flag) $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination "." Write-Banner "Ensuring silent noise level" $dataFiles = @("test-0.data", "test-1.data", "test-2.data", "test-3.data", "test-4.data", "test-5.data", "test-6.data", "test-7.data", "test-8.data", "test-9.data") # Create with -qq should produce no stdout $result = Invoke-Par2 -Arguments (@("c", "-a", "newtest", "-qq") + $dataFiles) -ReturnObject if ($result.ExitCode -ne 0) { Exit-TestWithError "create failed" } if ($result.StdOut -and $result.StdOut.Trim().Length -gt 0) { Exit-TestWithError "create with -qq produced output to stdout" } # Verify with -qq should produce no stdout $result = Invoke-Par2 -Arguments (@("v", "-qq", "newtest") + $dataFiles) -ReturnObject if ($result.ExitCode -ne 0) { Exit-TestWithError "verify failed" } if ($result.StdOut -and $result.StdOut.Trim().Length -gt 0) { Exit-TestWithError "verify with -qq produced output to stdout" } # Repair with -qq should produce no stdout $result = Invoke-Par2 -Arguments (@("r", "-qq", "newtest") + $dataFiles) -ReturnObject if ($result.ExitCode -ne 0) { Exit-TestWithError "repair failed" } if ($result.StdOut -and $result.StdOut.Trim().Length -gt 0) { Exit-TestWithError "repair with -qq produced output to stdout" } # Second create should fail (files already exist) but still be silent on stdout $result = Invoke-Par2 -Arguments (@("c", "-a", "newtest", "-qq") + $dataFiles) -ReturnObject if ($result.ExitCode -eq 0) { Exit-TestWithError "second create succeeded but shouldn't have" } if ($result.StdOut -and $result.StdOut.Trim().Length -gt 0) { Exit-TestWithError "second create with -qq produced output to stdout" } if (-not $result.StdErr -or $result.StdErr.Trim().Length -eq 0) { Exit-TestWithError "second create with -qq did not produce output to stderr" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test29000077500000000000000000000035351514221355600173010ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/bug190.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="Issue 190, 1 bitflip can't be repaired" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes ##### TESTDATA generated with: # # fill with zeros # dd if=/dev/zero bs=1MB count=9 | tr '\000' '\377' > ./9MBones_crc_ok_bad # # replace one bit # printf "\xFF" | dd of=9MBones_crc_ok_bad bs=1 seek=20000 count=1 conv=notrunc # # store original # cp 9MBones_crc_ok_bad 9MBones_crc_ok_orig # # replace 1 bit (1->0) # printf "\xFE" | dd of="9MBones_crc_ok_bad" bs=1 seek=20000 count=1 conv=notrunc ##### END TESTDATA # generate par2 from orig good file, copy that first cp 9MBones_crc_ok_orig 9MBones_crc_ok || { echo "ERROR: good copy failed" ; exit 1; } >&2 # Create PAR2 files $PARBINARY c -m500 -r30 -n1 -v '9MBones_crc_ok' || { echo "ERROR: create failed" ; exit 1; } >&2 # replace with bitflipped bad copy cp 9MBones_crc_ok_bad 9MBones_crc_ok || { echo "ERROR: bad copy failed" ; exit 1; } >&2 # Repair bitflip $PARBINARY repair '9MBones_crc_ok.par2' || { echo "ERROR: bitflip repair failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test29.ps1000066400000000000000000000022071514221355600177730ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 29: Issue 190, 1 bitflip can't be repaired $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "bug190.tar.gz") -Destination "." Write-Banner "Issue 190, 1 bitflip can't be repaired" # generate par2 from orig good file, copy that first Copy-Item "9MBones_crc_ok_orig" "9MBones_crc_ok" # Create PAR2 files $exitCode = Invoke-Par2 -Arguments @("c", "-m500", "-r30", "-n1", "-v", "9MBones_crc_ok") if ($exitCode -ne 0) { Exit-TestWithError "create failed" } # replace with bitflipped bad copy Copy-Item "9MBones_crc_ok_bad" "9MBones_crc_ok" -Force # Repair bitflip $exitCode = Invoke-Par2 -Arguments @("repair", "9MBones_crc_ok.par2") if ($exitCode -ne 0) { Exit-TestWithError "bitflip repair failed" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test3000077500000000000000000000032371514221355600172100ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata-par1files.tar.gz" || { echo "ERROR: Could not extract par test files" ; exit 1; } >&2 cp test-0.data test-0.data.orig cp test-1.data test-1.data.orig cp test-2.data test-2.data.orig cp test-3.data test-3.data.orig cp test-4.data test-4.data.orig cp test-5.data test-5.data.orig cp test-6.data test-6.data.orig cp test-7.data test-7.data.orig cp test-8.data test-8.data.orig cp test-9.data test-9.data.orig banner="Repairing two files using PAR 1.0 data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes rm -f test-1.data test-3.data $PARBINARY r testdata.par || { echo "ERROR: Reconstruction of two files using PAR 1.0 failed" ; exit 1; } >&2 cmp -s test-1.data test-1.data.orig && cmp -s test-3.data test-3.data.orig || { echo "ERROR: Repaired files do not match originals" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test3.ps1000066400000000000000000000020561514221355600177050ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 3: Repair one missing file using PAR 2.0 data $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination "." Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata-par2files.tar.gz") -Destination "." Write-Banner "Repairing one missing file using PAR 2.0 data" Copy-Item "test-0.data" "test-0.data.orig" Remove-Item "test-0.data" $exitCode = Invoke-Par2 -Arguments @("r", "testdata.par2") if ($exitCode -ne 0) { Exit-TestWithError "Reconstruction of one file using PAR 2.0 failed" } if (-not (Compare-Files "test-0.data" "test-0.data.orig")) { Exit-TestWithError "Repaired files do not match originals" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test30000077500000000000000000000025541514221355600172710ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf "$TESTDATA/bug128-parfiles.tar.gz" || { echo "ERROR: Could not extract par test files" ; exit 1; } >&2 banner="Issue 128, 0 byte files cause issue" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes # check par2 with one 0 byte file touch test-a.data # Verify with 0 byte file $PARBINARY verify recovery.par2 || { echo "ERROR: 0 byte file verify failed" ; exit 1; } >&2 rm test-a.data # Repair with 0 byte file $PARBINARY repair recovery.par2 || { echo "ERROR: 0 byte file repair failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test30.ps1000066400000000000000000000022171514221355600177640ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 30: Issue 128, 0 byte files cause issue $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination "." Expand-TarGz -Archive (Join-Path $TESTDATA "bug128-parfiles.tar.gz") -Destination "." Write-Banner "Issue 128, 0 byte files cause issue" # Create a 0 byte file New-Item -ItemType File -Path "test-a.data" -Force | Out-Null # Verify with 0 byte file $exitCode = Invoke-Par2 -Arguments @("verify", "recovery.par2") if ($exitCode -ne 0) { Exit-TestWithError "0 byte file verify failed" } Remove-Item "test-a.data" # Repair with 0 byte file missing $exitCode = Invoke-Par2 -Arguments @("repair", "recovery.par2") if ($exitCode -ne 0) { Exit-TestWithError "0 byte file repair failed" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test31000077500000000000000000000052551514221355600172730ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/smallsubdirdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="Bug 150, Files in rootfolder are not used with recursion" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes # before the subdirs echo "file in rootpath" > aaaa-test.data # after the subdirs echo "another file in rootpath" > test-a.data ###### # BEFORE: # # $ tree # . # ├── aaaa-test.data # ├── subdir1 # │   └── test-0.data # ├── subdir2 # │   ├── test-1.data # │   ├── test-2.data # │   ├── test-3.data # │   ├── test-4.data # │   ├── test-5.data # │   ├── test-6.data # │   ├── test-7.data # │   ├── test-8.data # │   └── test-9.data # └── test-a.data # # 3 directories, 12 files ###### $PARBINARY create -R * || { echo "ERROR: Recursive creation of PAR 2.0 files failed" ; exit 1; } >&2 ###### # AFTER: # # tree # . # ├── aaaa-test.data # ├── aaaa-test.data.par2 # ├── aaaa-test.data.vol000+01.par2 # ├── aaaa-test.data.vol001+02.par2 # ├── aaaa-test.data.vol003+04.par2 # ├── aaaa-test.data.vol007+08.par2 # ├── aaaa-test.data.vol015+16.par2 # ├── aaaa-test.data.vol031+32.par2 # ├── aaaa-test.data.vol063+37.par2 # ├── subdir1 # │   └── test-0.data # ├── subdir2 # │   ├── test-1.data # │   ├── test-2.data # │   ├── test-3.data # │   ├── test-4.data # │   ├── test-5.data # │   ├── test-6.data # │   ├── test-7.data # │   ├── test-8.data # │   └── test-9.data # └── test-a.data # # 3 directories, 20 files ###### # The first file in the wildcard expansion is used ad parfilename so not # covered for repair rm aaaa-test.data $PARBINARY verify aaaa-test.data.par2 || { echo "ERROR: verify of rootpath inclusion failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test31.ps1000066400000000000000000000032561514221355600177710ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 31: Bug 150, Files in rootfolder are not used with recursion $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "smallsubdirdata.tar.gz") -Destination "." Write-Banner "Bug 150, Files in rootfolder are not used with recursion" # Create files before and after subdirs alphabetically Set-Content -Path "aaaa-test.data" -Value "file in rootpath" Set-Content -Path "test-a.data" -Value "another file in rootpath" # Get all items SORTED ALPHABETICALLY to match Unix shell glob behavior # This ensures aaaa-test.data comes first and becomes the par2 base name $allItems = Get-ChildItem | Select-Object -ExpandProperty Name | Sort-Object $exitCode = Invoke-Par2 -Arguments (@("create", "-R") + $allItems) if ($exitCode -ne 0) { Exit-TestWithError "Recursive creation of PAR 2.0 files failed" } # The first file in the alphabetically sorted list is used as parfilename # so it's not covered for repair - remove it Remove-Item "aaaa-test.data" # With alphabetical sorting, aaaa-test.data comes first, so the par2 file # will be named aaaa-test.data.par2 $mainPar2 = "aaaa-test.data.par2" $exitCode = Invoke-Par2 -Arguments @("verify", $mainPar2) if ($exitCode -ne 0) { Exit-TestWithError "verify of rootpath inclusion failed" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 } par2cmdline-turbo-1.4.0/tests/test32000077500000000000000000000023171514221355600172700ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/subdirdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="Bug 205, Files already exist so no par2 is created" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes mkdir par2 $PARBINARY create -vv -a par2/disk1.par2 -b32768 -n31 -R -v -B./ * || { echo "ERROR: Recursive creation of PAR 2.0 files failed" ; exit 1; } >&2 $PARBINARY verify -B./ par2/disk1.par2 || { echo "ERROR: verify failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test32.ps1000066400000000000000000000022261514221355600177660ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 32: Bug 205, Files already exist so no par2 is created $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "subdirdata.tar.gz") -Destination "." Write-Banner "Bug 205, Files already exist so no par2 is created" New-Item -ItemType Directory -Path "par2" -Force | Out-Null # Get all items for recursive creation $allItems = Get-ChildItem | Select-Object -ExpandProperty Name $exitCode = Invoke-Par2 -Arguments (@("create", "-vv", "-a", "par2\disk1.par2", "-b32768", "-n31", "-R", "-v", "-B.\") + $allItems) if ($exitCode -ne 0) { Exit-TestWithError "Recursive creation of PAR 2.0 files failed" } $exitCode = Invoke-Par2 -Arguments @("verify", "-B.\", "par2\disk1.par2") if ($exitCode -ne 0) { Exit-TestWithError "verify failed" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test33000077500000000000000000000040451514221355600172710ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata-par2files.tar.gz" || { echo "ERROR: Could not extract par test files" ; exit 1; } >&2 banner="Testing rename-only mode with renamed files" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes # Save original files cp test-0.data test-0.data.orig cp test-1.data test-1.data.orig cp test-2.data test-2.data.orig # Rename some files (simulate accidental renaming) mv test-0.data renamed-file-a.data mv test-1.data renamed-file-b.data mv test-2.data renamed-file-c.data # Verify fails without the renamed files $PARBINARY v testdata.par2 2>&1 | grep -q "missing" if [ $? -ne 0 ]; then echo "ERROR: Verification should report missing files" >&2 exit 1 fi # Repair with rename-only mode, passing the renamed files as extra files $PARBINARY r -O testdata.par2 renamed-file-a.data renamed-file-b.data renamed-file-c.data || { echo "ERROR: Repair with rename-only mode failed" ; exit 1; } >&2 # Check that the original files are restored with correct names cmp -s test-0.data test-0.data.orig && cmp -s test-1.data test-1.data.orig && cmp -s test-2.data test-2.data.orig || { echo "ERROR: Repaired files do not match originals" ; exit 1 ; } >&2 echo "Rename-only mode test passed!" cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test33.ps1000066400000000000000000000042401514221355600177650ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 33: Rename-only mode with renamed files $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination "." Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata-par2files.tar.gz") -Destination "." Write-Banner "Testing rename-only mode with renamed files" # Save original files Copy-Item "test-0.data" "test-0.data.orig" Copy-Item "test-1.data" "test-1.data.orig" Copy-Item "test-2.data" "test-2.data.orig" # Rename some files (simulate accidental renaming) Move-Item "test-0.data" "renamed-file-a.data" Move-Item "test-1.data" "renamed-file-b.data" Move-Item "test-2.data" "renamed-file-c.data" # Verify fails without the renamed files $result = Invoke-Par2 -Arguments @("v", "testdata.par2") -ReturnObject $output = $result.StdOut + $result.StdErr if ($output -notmatch "missing") { Exit-TestWithError "Verification should report missing files" } # Repair with rename-only mode, passing the renamed files as extra files $exitCode = Invoke-Par2 -Arguments @("r", "-O", "testdata.par2", "renamed-file-a.data", "renamed-file-b.data", "renamed-file-c.data") if ($exitCode -ne 0) { Exit-TestWithError "Repair with rename-only mode failed" } # Check that the original files are restored with correct names if (-not (Compare-Files "test-0.data" "test-0.data.orig")) { Exit-TestWithError "Repaired file test-0.data does not match original" } if (-not (Compare-Files "test-1.data" "test-1.data.orig")) { Exit-TestWithError "Repaired file test-1.data does not match original" } if (-not (Compare-Files "test-2.data" "test-2.data.orig")) { Exit-TestWithError "Repaired file test-2.data does not match original" } Write-Host "Rename-only mode test passed!" Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test34000077500000000000000000000045521514221355600172750ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata-par2files.tar.gz" || { echo "ERROR: Could not extract par test files" ; exit 1; } >&2 banner="Testing rename-only mode skips damaged files" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes # Save original file cp test-0.data test-0.data.orig # Delete original and create a damaged version with a different name rm test-0.data cp test-0.data.orig renamed-damaged.data # Corrupt the file at the beginning dd if=/dev/zero of=renamed-damaged.data bs=1 count=100 conv=notrunc 2>/dev/null # With rename-only mode, repair should NOT be possible because: # - The damaged file should be skipped in rename-only mode # - There are not enough recovery blocks to repair # This tests that rename-only mode correctly skips non-perfect matches $PARBINARY r -O testdata.par2 renamed-damaged.data 2>&1 result=$? # Should return non-zero (repair not possible) because rename-only skips damaged files if [ $result -eq 0 ]; then # Let's check if it actually found a match for the damaged file # If it did, that would be a bug in rename-only mode echo "Note: Repair succeeded, checking if the file was properly restored..." if cmp -s test-0.data test-0.data.orig; then echo "File was restored from recovery data (not from the damaged file)" else echo "ERROR: Rename-only mode should have skipped the damaged file" >&2 exit 1 fi else echo "Repair not possible (as expected with rename-only mode and damaged file)" fi echo "Rename-only mode correctly handles damaged files!" cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test34.ps1000066400000000000000000000036001514221355600177650ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 34: Rename-only mode skips damaged files $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination "." Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata-par2files.tar.gz") -Destination "." Write-Banner "Testing rename-only mode skips damaged files" # Save original file Copy-Item "test-0.data" "test-0.data.orig" # Delete original and create a damaged version with a different name Remove-Item "test-0.data" Copy-Item "test-0.data.orig" "renamed-damaged.data" # Corrupt the file at the beginning $bytes = [System.IO.File]::ReadAllBytes("renamed-damaged.data") for ($i = 0; $i -lt 100; $i++) { $bytes[$i] = 0 } [System.IO.File]::WriteAllBytes("renamed-damaged.data", $bytes) # With rename-only mode, repair may or may not succeed depending on recovery blocks # The key is that it shouldn't crash and should handle the damaged file appropriately $exitCode = Invoke-Par2 -Arguments @("r", "-O", "testdata.par2", "renamed-damaged.data") if ($exitCode -eq 0) { # If repair succeeded, check if file was properly restored if (Test-Path "test-0.data") { if (Compare-Files "test-0.data" "test-0.data.orig") { Write-Host "File was restored from recovery data (not from the damaged file)" } } } else { Write-Host "Repair not possible (as expected with rename-only mode and damaged file)" } Write-Host "Rename-only mode correctly handles damaged files!" Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test35000077500000000000000000000047321514221355600172760ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 banner="Verify symbolic link handling" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes # generate test data and links echo -n '01234567890abcdef' > test_file ln -s test_file test_link mkdir test_dir ln -s $(pwd)/test_file test_dir/test_link ln -s test_dir dir_link # links are not followed for non-primary files $PARBINARY c test_primary test_link && { echo "ERROR: followed non-primary link" ; exit 1; } >&2 $PARBINARY c test_primary -R test_dir && { echo "ERROR: followed link in directory" ; exit 1; } >&2 $PARBINARY c test_primary -R dir_link && { echo "ERROR: followed linked directory" ; exit 1; } >&2 # primary file being a link calculates data for the real file cp test_file test_file.bak echo "*** Create ***" $PARBINARY c -r10 test_link || { echo "ERROR: calculating link data failed" ; exit 1; } >&2 echo "*** Verify good file link ***" $PARBINARY v test_link || { echo "ERROR: verifying good link data failed" ; exit 1; } >&2 # "damage" real file echo -n "!" >> test_file echo "*** Verify damaged file link ***" $PARBINARY v test_link && { echo "ERROR: verifying damaged link data succeeded" ; exit 1; } >&2 # repair via link echo "*** Repair ***" $PARBINARY r test_link || { echo "ERROR: repairing linked data failed" ; exit 1; } >&2 # link becomes the repaired file diff test_link test_file.bak || { echo "ERROR: comparison after repairing linked data failed" ; exit 1; } >&2 # dead links fail ln -s NOT_EXISTING dead_link $PARBINARY c dead_link && { echo "ERROR: calculating dead link data succeeded" ; exit 1; } >&2 $PARBINARY v dead_link && { echo "ERROR: verifying dead link data succeeded" ; exit 1; } >&2 $PARBINARY r dead_link && { echo "ERROR: repairing dead link data succeeded" ; exit 1; } >&2 echo "\nLink tests succeeded!" cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test35.ps1000066400000000000000000000007411514221355600177710ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 35: Verify symbolic link handling # DUMMY script on windows, just here for completeness sake $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) Initialize-Test -TestName $testname Write-Banner "Verify symbolic link handling" Write-Host "SKIPPED: no symbolic link testing on Windows" Complete-Test exit 0 par2cmdline-turbo-1.4.0/tests/test4000077500000000000000000000032401514221355600172030ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata-par2files.tar.gz" || { echo "ERROR: Could not extract par test files" ; exit 1; } >&2 cp test-0.data test-0.data.orig cp test-1.data test-1.data.orig cp test-2.data test-2.data.orig cp test-3.data test-3.data.orig cp test-4.data test-4.data.orig cp test-5.data test-5.data.orig cp test-6.data test-6.data.orig cp test-7.data test-7.data.orig cp test-8.data test-8.data.orig cp test-9.data test-9.data.orig banner="Repairing two files using PAR 2.0 data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes rm -f test-1.data test-3.data $PARBINARY r testdata.par2 || { echo "ERROR: Reconstruction of two files using PAR 2.0 failed" ; exit 1; } >&2 cmp -s test-1.data test-1.data.orig && cmp -s test-3.data test-3.data.orig || { echo "ERROR: Repaired files do not match originals" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test4.ps1000066400000000000000000000024401514221355600177030ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 4: Repair two missing files using PAR 2.0 data $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination "." Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata-par2files.tar.gz") -Destination "." Write-Banner "Repairing two missing files using PAR 2.0 data" Copy-Item "test-0.data" "test-0.data.orig" Copy-Item "test-5.data" "test-5.data.orig" Remove-Item "test-0.data" Remove-Item "test-5.data" $exitCode = Invoke-Par2 -Arguments @("r", "testdata.par2") if ($exitCode -ne 0) { Exit-TestWithError "Reconstruction of two files using PAR 2.0 failed" } if (-not (Compare-Files "test-0.data" "test-0.data.orig")) { Exit-TestWithError "Repaired file test-0.data does not match original" } if (-not (Compare-Files "test-5.data" "test-5.data.orig")) { Exit-TestWithError "Repaired file test-5.data does not match original" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test5000077500000000000000000000041131514221355600172040ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="Creating 100% PAR 2.0 recovery data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY c -r100 -b190 -a newtest test-*.data || { echo "ERROR: Creating PAR 2.0 data failed" ; exit 1; } >&2 cp test-0.data test-0.data.orig cp test-1.data test-1.data.orig cp test-2.data test-2.data.orig cp test-3.data test-3.data.orig cp test-4.data test-4.data.orig cp test-5.data test-5.data.orig cp test-6.data test-6.data.orig cp test-7.data test-7.data.orig cp test-8.data test-8.data.orig cp test-9.data test-9.data.orig rm -f test-*.data banner="Repairing 100% loss using PAR 2.0 data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes rm -f test-*.data $PARBINARY r newtest.par2 || { echo "ERROR: Full Repair using PAR 2.0 failed" ; exit 1; } >&2 cmp -s test-0.data test-0.data.orig && cmp -s test-1.data test-1.data.orig && cmp -s test-2.data test-2.data.orig && cmp -s test-3.data test-3.data.orig && cmp -s test-4.data test-4.data.orig && cmp -s test-5.data test-5.data.orig && cmp -s test-6.data test-6.data.orig && cmp -s test-7.data test-7.data.orig && cmp -s test-8.data test-8.data.orig && cmp -s test-9.data test-9.data.orig || { echo "ERROR: Repaired files do not match originals" ; exit 1 ; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test5.ps1000066400000000000000000000027651514221355600177160ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 5: Create and repair 100% loss using PAR 2.0 data $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination "." Write-Banner "Creating 100% PAR 2.0 recovery data" $exitCode = Invoke-Par2 -Arguments @("c", "-r100", "-b190", "-a", "newtest", "test-0.data", "test-1.data", "test-2.data", "test-3.data", "test-4.data", "test-5.data", "test-6.data", "test-7.data", "test-8.data", "test-9.data") if ($exitCode -ne 0) { Exit-TestWithError "Creating PAR 2.0 data failed" } # Save originals for ($i = 0; $i -le 9; $i++) { Copy-Item "test-$i.data" "test-$i.data.orig" } # Delete all data files Remove-Item "test-*.data" Write-Banner "Repairing 100% loss using PAR 2.0 data" $exitCode = Invoke-Par2 -Arguments @("r", "newtest.par2") if ($exitCode -ne 0) { Exit-TestWithError "Full Repair using PAR 2.0 failed" } # Verify all files for ($i = 0; $i -le 9; $i++) { if (-not (Compare-Files "test-$i.data" "test-$i.data.orig")) { Exit-TestWithError "Repaired file test-$i.data does not match original" } } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test5rk000077500000000000000000000062401514221355600175440ustar00rootroot00000000000000#!/bin/sh # This is a copy of test5 but using "-rk" instead of "-r100". # (This increases redundancy, but I'm missing system knowledge # to refactor the test core). # Couldn't we move the stuff common for all tests and maybe some # helper functions to a utility file and source it? For example: # source testfuncs.sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 banner="Creating 100% PAR 2.0 recovery data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes # About the "magic -rk1302": # par2 c -r100 creates PAR2 files of in sum 1302436 bytes, so do # we approximately (-rk1302). # To also test ISSUE-80 (https://github.com/Parchive/par2cmdline/issues/80) # we also use "-n2". $PARBINARY c -rk1302 -b190 -n2 -a newtest test-*.data || { echo "ERROR: Creating PAR 2.0 data failed" ; exit 1; } >&2 # Checking whether "-n2" worked by creating two newtest.vol*.par2 files # (and also tolerate newtest.VOL*.PAR2 as seen on Windows) test "$(ls newtest.[Vv][Oo][Ll]*.[Pp][Aa][Rr]2 | wc -l)" -eq 2 || { echo "ERROR: File count option -n2 did not work." ; exit 1; } >&2 # Why not simply "mv"? # for f in test-*.data; do mv "$f" "$f.orig"; done # Would save the cp, the rm and keep file date (may help debug). cp test-0.data test-0.data.orig cp test-1.data test-1.data.orig cp test-2.data test-2.data.orig cp test-3.data test-3.data.orig cp test-4.data test-4.data.orig cp test-5.data test-5.data.orig cp test-6.data test-6.data.orig cp test-7.data test-7.data.orig cp test-8.data test-8.data.orig cp test-9.data test-9.data.orig rm -f test-*.data test -e test-0.data && { echo "ERROR: File deletion did not work." ; exit 1; } >&2 banner="Repairing 100% loss using PAR 2.0 data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes rm -f test-*.data $PARBINARY r newtest.par2 || { echo "ERROR: Full Repair using PAR 2.0 failed" ; exit 1; } >&2 # for f in test-*.data; do cmp "$f" "$f.orig" || { echo XXX; exit 1; }; done cmp -s test-0.data test-0.data.orig && cmp -s test-1.data test-1.data.orig && cmp -s test-2.data test-2.data.orig && cmp -s test-3.data test-3.data.orig && cmp -s test-4.data test-4.data.orig && cmp -s test-5.data test-5.data.orig && cmp -s test-6.data test-6.data.orig && cmp -s test-7.data test-7.data.orig && cmp -s test-8.data test-8.data.orig && cmp -s test-9.data test-9.data.orig || { echo "ERROR: Repaired files do not match originals" ; exit 1 ; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test5rk.ps1000066400000000000000000000042351514221355600202450ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 5rk: Create 100% PAR 2.0 recovery data using -rk option # This is a copy of test5 but using "-rk" instead of "-r100" $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "flatdata.tar.gz") -Destination "." Write-Banner "Creating 100% PAR 2.0 recovery data" # About the "magic -rk1302": # par2 c -r100 creates PAR2 files of in sum 1302436 bytes, so do # we approximately (-rk1302). # To also test ISSUE-80 we also use "-n2". $exitCode = Invoke-Par2 -Arguments @("c", "-rk1302", "-b190", "-n2", "-a", "newtest", "test-0.data", "test-1.data", "test-2.data", "test-3.data", "test-4.data", "test-5.data", "test-6.data", "test-7.data", "test-8.data", "test-9.data") if ($exitCode -ne 0) { Exit-TestWithError "Creating PAR 2.0 data failed" } # Check that -n2 created exactly 2 vol files (case-insensitive for Windows) $volFiles = Get-ChildItem -Filter "newtest.vol*par2" | Measure-Object if ($volFiles.Count -ne 2) { Exit-TestWithError "File count option -n2 did not work. Expected 2, got $($volFiles.Count)" } # Save originals for ($i = 0; $i -le 9; $i++) { Copy-Item "test-$i.data" "test-$i.data.orig" } # Delete all data files Remove-Item "test-*.data" # Verify deletion worked if (Test-Path "test-0.data") { Exit-TestWithError "File deletion did not work" } Write-Banner "Repairing 100% loss using PAR 2.0 data" $exitCode = Invoke-Par2 -Arguments @("r", "newtest.par2") if ($exitCode -ne 0) { Exit-TestWithError "Full Repair using PAR 2.0 failed" } # Verify all files for ($i = 0; $i -le 9; $i++) { if (-not (Compare-Files "test-$i.data" "test-$i.data.orig")) { Exit-TestWithError "Repaired file test-$i.data does not match original" } } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test6000077500000000000000000000030041514221355600172030ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/subdirdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf "$TESTDATA/subdirdata-par2files-unix.tar.gz" || { echo "ERROR: Could not extract par test files" ; exit 1; } >&2 banner="Repairing two files in subdirs using PAR 2.0 data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes cp subdir1/test-2.data subdir1/test-2.data.orig cp subdir2/test-7.data subdir2/test-7.data.orig rm -f subdir1/test-2.data subdir2/test-7.data $PARBINARY r testdata.par2 || { echo "ERROR: Reconstruction of two files using PAR 2.0 failed" ; exit 1; } >&2 cmp -s subdir1/test-2.data subdir1/test-2.data.orig && cmp -s subdir2/test-7.data subdir2/test-7.data.orig || { echo "ERROR: Repaired files do not match originals" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test6.ps1000066400000000000000000000026521514221355600177120ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 6: Repair two files in subdirs using PAR 2.0 data (Unix paths) $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "subdirdata.tar.gz") -Destination "." Expand-TarGz -Archive (Join-Path $TESTDATA "subdirdata-par2files-unix.tar.gz") -Destination "." Write-Banner "Repairing two files in subdirs using PAR 2.0 data (Unix paths)" Copy-Item "subdir1\test-2.data" "subdir1\test-2.data.orig" Copy-Item "subdir2\test-7.data" "subdir2\test-7.data.orig" Remove-Item "subdir1\test-2.data" Remove-Item "subdir2\test-7.data" $exitCode = Invoke-Par2 -Arguments @("r", "testdata.par2") if ($exitCode -ne 0) { Exit-TestWithError "Reconstruction of two files using PAR 2.0 failed" } if (-not (Compare-Files "subdir1\test-2.data" "subdir1\test-2.data.orig")) { Exit-TestWithError "Repaired file subdir1\test-2.data does not match original" } if (-not (Compare-Files "subdir2\test-7.data" "subdir2\test-7.data.orig")) { Exit-TestWithError "Repaired file subdir2\test-7.data does not match original" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test7000077500000000000000000000030301514221355600172030ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/subdirdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf "$TESTDATA/subdirdata-par2files-win.tar.gz" || { echo "ERROR: Could not extract par test files" ; exit 1; } >&2 banner="Repairing two files in subdirs using PAR 2.0 data generated on windows" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes cp subdir1/test-2.data subdir1/test-2.data.orig cp subdir2/test-7.data subdir2/test-7.data.orig rm -f subdir1/test-2.data subdir2/test-7.data $PARBINARY r testdata.par2 || { echo "ERROR: Reconstruction of two files using PAR 2.0 failed" ; exit 1; } >&2 cmp -s subdir1/test-2.data subdir1/test-2.data.orig && cmp -s subdir2/test-7.data subdir2/test-7.data.orig || { echo "ERROR: Repaired files do not match originals" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test7.ps1000066400000000000000000000026711514221355600177140ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 7: Repair two files in subdirs using PAR 2.0 data generated on Windows $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "subdirdata.tar.gz") -Destination "." Expand-TarGz -Archive (Join-Path $TESTDATA "subdirdata-par2files-win.tar.gz") -Destination "." Write-Banner "Repairing two files in subdirs using PAR 2.0 data generated on Windows" Copy-Item "subdir1\test-2.data" "subdir1\test-2.data.orig" Copy-Item "subdir2\test-7.data" "subdir2\test-7.data.orig" Remove-Item "subdir1\test-2.data" Remove-Item "subdir2\test-7.data" $exitCode = Invoke-Par2 -Arguments @("r", "testdata.par2") if ($exitCode -ne 0) { Exit-TestWithError "Reconstruction of two files using PAR 2.0 failed" } if (-not (Compare-Files "subdir1\test-2.data" "subdir1\test-2.data.orig")) { Exit-TestWithError "Repaired file subdir1\test-2.data does not match original" } if (-not (Compare-Files "subdir2\test-7.data" "subdir2\test-7.data.orig")) { Exit-TestWithError "Repaired file subdir2\test-7.data does not match original" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test8000077500000000000000000000026001514221355600172060ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/subdirdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 mkdir source1 && mv subdir1 subdir2 source1 cd source1 banner="create par2files on subdir" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY c -R testdata.par2 * || { echo "ERROR: construction of files using PAR 2.0 failed" ; exit 1; } >&2 cd .. mv source1 source2 cd source2 banner="verify par2files on subdir, moved source folder" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes $PARBINARY v testdata.par2 || { echo "ERROR: verification of files using PAR 2.0 failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test8.ps1000066400000000000000000000031071514221355600177100ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 8: Create PAR 2.0 data for files in subdirs, verify after moving folder $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "subdirdata.tar.gz") -Destination "." # Create source1 directory and move subdirs into it (matching Unix test) New-Item -ItemType Directory -Path "source1" -Force | Out-Null Move-Item "subdir1" "source1\" Move-Item "subdir2" "source1\" Push-Location "source1" Write-Banner "Create par2files on subdir" # Use wildcard equivalent - get all items in current directory $items = Get-ChildItem | Select-Object -ExpandProperty Name $exitCode = Invoke-Par2 -Arguments (@("c", "-R", "testdata.par2") + $items) if ($exitCode -ne 0) { Pop-Location Exit-TestWithError "Construction of files using PAR 2.0 failed" } Pop-Location # Rename source1 to source2 (simulating moving the source folder) Rename-Item "source1" "source2" Push-Location "source2" Write-Banner "Verify par2files on subdir, moved source folder" $exitCode = Invoke-Par2 -Arguments @("v", "testdata.par2") if ($exitCode -ne 0) { Pop-Location Exit-TestWithError "Verification of files using PAR 2.0 failed" } Pop-Location Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 }par2cmdline-turbo-1.4.0/tests/test9000077500000000000000000000037541514221355600172220ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" # valgrind tests memory usage. # wine allow for windows testing on linux if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $execdir/par2" elif [ "`which wine`" != "" ] && [ -f "$execdir/par2.exe" ] then PARBINARY="wine $execdir/par2.exe" else PARBINARY="$execdir/par2" fi if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT="$PWD" testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata.tar.gz" || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf "$TESTDATA/flatdata-par2files.tar.gz" || { echo "ERROR: Could not extract par test files" ; exit 1; } >&2 banner="rename using PAR 2.0 data" dashes=`echo "$banner" | sed s/./-/g` echo $dashes echo $banner echo $dashes mv test-1.data rename4 mv test-2.data rename5 mv test-3.data rename6 mv test-4.data rename7 mv test-5.data rename9 mv test-6.data rename8 mv test-7.data rename3 mv test-8.data rename1 mv test-9.data rename2 $PARBINARY r testdata.par2 rename* || { echo "ERROR: Initial PAR 2.0 verification and rename failed" ; exit 1; } >&2 test -e test-1.data || { echo "ERROR: rename failed" ; exit 1; } >&2 test -e test-2.data || { echo "ERROR: rename failed" ; exit 1; } >&2 test -e test-3.data || { echo "ERROR: rename failed" ; exit 1; } >&2 test -e test-4.data || { echo "ERROR: rename failed" ; exit 1; } >&2 test -e test-5.data || { echo "ERROR: rename failed" ; exit 1; } >&2 test -e test-6.data || { echo "ERROR: rename failed" ; exit 1; } >&2 test -e test-7.data || { echo "ERROR: rename failed" ; exit 1; } >&2 test -e test-8.data || { echo "ERROR: rename failed" ; exit 1; } >&2 test -e test-9.data || { echo "ERROR: rename failed" ; exit 1; } >&2 cd "$TESTROOT" rm -rf "run$testname" exit 0 par2cmdline-turbo-1.4.0/tests/test9.ps1000066400000000000000000000037331514221355600177160ustar00rootroot00000000000000#!/usr/bin/env pwsh # Test 9: Create and repair with 100 blocks $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $testname = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.MyCommand.Name) try { Initialize-Test -TestName $testname Expand-TarGz -Archive (Join-Path $TESTDATA "100blocks.tar.gz") -Destination "." Write-Banner "Create par2files using 100 blocks" # Get all files for par2 creation $files = Get-ChildItem -File | Select-Object -ExpandProperty Name $exitCode = Invoke-Par2 -Arguments (@("c", "-b100", "testdata.par2") + $files) if ($exitCode -ne 0) { Exit-TestWithError "Construction of files using PAR 2.0 failed" } Write-Banner "Repair 5% of 100 blocks par2files removing 3 files" # Remove 3 files Remove-Item "file" -ErrorAction SilentlyContinue Remove-Item "file1" -ErrorAction SilentlyContinue Remove-Item "file3" -ErrorAction SilentlyContinue # Get remaining files to pass as extra args $remainingFiles = Get-ChildItem -File | Where-Object { $_.Extension -ne ".par2" } | Select-Object -ExpandProperty Name $exitCode = Invoke-Par2 -Arguments (@("r", "testdata.par2") + $remainingFiles) if ($exitCode -ne 0) { Exit-TestWithError "Repair of files using PAR 2.0 failed (3 files removed)" } Write-Banner "Repair 5% of 100 blocks par2files removing 1 file" # Remove 1 more file Remove-Item "file5" -ErrorAction SilentlyContinue # Get remaining files to pass as extra args $remainingFiles = Get-ChildItem -File | Where-Object { $_.Extension -ne ".par2" } | Select-Object -ExpandProperty Name $exitCode = Invoke-Par2 -Arguments (@("r", "testdata.par2") + $remainingFiles) if ($exitCode -ne 0) { Exit-TestWithError "Repair of files using PAR 2.0 failed (1 file removed)" } Complete-Test exit 0 } catch { Write-Host "ERROR: $_" -ForegroundColor Red Complete-Test exit 1 } par2cmdline-turbo-1.4.0/tests/testMem000077500000000000000000000014221514221355600175560ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" if [ -z "$srcdir" ] || [ "." = "$srcdir" ]; then srcdir="$PWD" TESTDATA="$srcdir/tests" else srcdir="$PWD/$srcdir" TESTDATA="$srcdir/tests" fi TESTROOT=$PWD testname=$(basename $0) rm -f "$testname.log" rm -rf "run$testname" mkdir "run$testname" && cd "run$testname" || { echo "ERROR: Could not change to test directory" ; exit 1; } >&2 tar -xzf $TESTDATA/subdirdata.tar.gz || { echo "ERROR: Could not extract data test files" ; exit 1; } >&2 tar -xzf $TESTDATA/subdirdata-par2files-unix.tar.gz || { echo "ERROR: Could not extract par test files" ; exit 1; } >&2 rm -f subdir1/test-2.data subdir2/test-7.data valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all $execdir/par2 r testdata.par2 cd $TESTROOT rm -rf run$testname exit 0 par2cmdline-turbo-1.4.0/tests/testcollision/000077500000000000000000000000001514221355600211065ustar00rootroot00000000000000par2cmdline-turbo-1.4.0/tests/testcollision/2mb_0s.bin000066400000000000000000100000001514221355600226510ustar00rootroot00000000000000""par2cmdline-turbo-1.4.0/tests/testcollision/2mb_0s.bin.par2000066400000000000000000000007141514221355600235270ustar00rootroot00000000000000PAR2PKTCM1wu2,$B8NsLPAR 2.0FileDesc}mm(P\|) 'ѩz0VG3GωQ/N 9 2mb_0s.binPAR2PKTQVL ĔP#SG$B8NsLPAR 2.0IFSC}mm(P'p=E 6sTZzMY !CY#8 fuY !CY#8 fu1h"F`-`h PAR2PKT\c6Y$B8NsLPAR 2.0Main}mm(PPAR2PKTL" NhOm$B8NsLPAR 2.0Creatorpar2j v1.2.5par2cmdline-turbo-1.4.0/tests/testcollision/2mb_col.bin000066400000000000000000100000001514221355600231040ustar00rootroot00000000000000Aq""par2cmdline-turbo-1.4.0/tests/testcollision/2mb_col.bin.par2000066400000000000000000000007141514221355600237620ustar00rootroot00000000000000PAR2PKTL8l<#)%J slS!8YKPAR 2.0FileDesc9XxͲw!k0s, 3GωQ/N 9 2mb_col.binPAR2PKTTv/@fa;RlS!8YKPAR 2.0IFSC9XxͲ'p=E 6sTZzM: ~-h+ fuY !CY#8 fu1h"F`-`h PAR2PKT\)Rd t=\lS!8YKPAR 2.0Main9XxͲPAR2PKTL;zEꩌ zlS!8YKPAR 2.0Creatorpar2j v1.2.5par2cmdline-turbo-1.4.0/tests/testcollision/pattern16.bin000066400000000000000000100000001514221355600234130ustar00rootroot00000000000000                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                As                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               As par2cmdline-turbo-1.4.0/tests/testcollision/pattern16.bin.par2000066400000000000000000000007201514221355600242660ustar00rootroot00000000000000PAR2PKTc(LsyC.Yp޳9o]4PAR 2.0FileDesc߭X^A1'Q*CE9cnbJ))@k_&G pattern16.binPAR2PKT& M$zȥ,/UG.Yp޳9o]4PAR 2.0IFSC߭X^A1'^cհSZiE,.`ɗ6>sv sN;)rKĬ9Br s*ݛ3uMk߼[PAR2PKT\[x~Գ6N.Yp޳9o]4PAR 2.0Main߭X^A1'PAR2PKTL$j"ĥ&.;&.Yp޳9o]4PAR 2.0Creatorpar2j v1.2.5par2cmdline-turbo-1.4.0/tests/testfuncs.ps1000066400000000000000000000152301514221355600206570ustar00rootroot00000000000000# par2cmdline Windows Test Helper Functions # This file is sourced by individual test scripts to provide common functionality $ErrorActionPreference = "Stop" # Determine directories # When dot-sourced, $PSScriptRoot gives the directory of THIS file (testfuncs.ps1) # This is more reliable than $MyInvocation.MyCommand.Path when dot-sourcing if ($PSScriptRoot) { $script:TestScriptDir = $PSScriptRoot } else { # Fallback for older PowerShell or unusual invocation $script:TestScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path if (-not $script:TestScriptDir) { $script:TestScriptDir = $PWD.Path } } $script:RootDir = Split-Path -Parent $script:TestScriptDir $script:TestDataDir = $script:TestScriptDir # Sync .NET current directory with PowerShell - needed for [System.IO.File] methods [System.IO.Directory]::SetCurrentDirectory($PWD.Path) # Find par2.exe binary function Find-Par2Binary { # Check if PARBINARY environment variable is set if ($env:PARBINARY -and (Test-Path $env:PARBINARY)) { return $env:PARBINARY } # Check common build output locations relative to root $candidates = @( (Join-Path $script:RootDir "x64\Release\par2.exe"), (Join-Path $script:RootDir "x64\Debug\par2.exe"), (Join-Path $script:RootDir "Win32\Release\par2.exe"), (Join-Path $script:RootDir "Win32\Debug\par2.exe"), (Join-Path $script:RootDir "ARM64\Release\par2.exe"), (Join-Path $script:RootDir "ARM64\Debug\par2.exe"), (Join-Path $script:RootDir "build\par2.exe"), (Join-Path $script:RootDir "par2.exe") ) foreach ($candidate in $candidates) { if (Test-Path $candidate) { return (Resolve-Path $candidate).Path } } throw "Could not find par2.exe. Please build the project first or set PARBINARY environment variable." } # Get the par2 binary path and store in environment variable for cross-script access if (-not $env:PARBINARY -or -not (Test-Path $env:PARBINARY)) { $env:PARBINARY = Find-Par2Binary } $script:Par2Binary = $env:PARBINARY # Extract a .tar.gz file (requires tar command available on Windows 10+) function Expand-TarGz { param( [Parameter(Mandatory=$true)] [string]$Archive, [Parameter(Mandatory=$true)] [string]$Destination ) if (-not (Test-Path $Archive)) { throw "Archive not found: $Archive" } # Use tar command (available on Windows 10 1803+) $tarResult = & tar -xzf $Archive -C $Destination 2>&1 if ($LASTEXITCODE -ne 0) { throw "Failed to extract $Archive : $tarResult" } } # Create a banner for test output function Write-Banner { param( [Parameter(Mandatory=$true)] [string]$Message ) $dashes = "-" * $Message.Length Write-Host $dashes Write-Host $Message Write-Host $dashes } # Run par2 # Unified function handling both console output and capturing function Invoke-Par2 { param( [Parameter(Mandatory=$true)] [string[]]$Arguments, [Parameter(Mandatory=$false)] [switch]$ReturnObject ) # Get the par2 binary path from environment variable $par2Path = $env:PARBINARY if (-not $par2Path) { throw "PARBINARY environment variable not set" } $tempOut = [System.IO.Path]::GetTempFileName() $tempErr = [System.IO.Path]::GetTempFileName() try { # Use Start-Process with explicit WorkingDirectory for output capture $process = Start-Process -FilePath $par2Path -ArgumentList $Arguments -NoNewWindow -Wait -PassThru -WorkingDirectory $PWD.Path -RedirectStandardOutput $tempOut -RedirectStandardError $tempErr $stdOutContent = Get-Content $tempOut -Raw if ($null -eq $stdOutContent) { $stdOutContent = "" } $stdErrContent = Get-Content $tempErr -Raw if ($null -eq $stdErrContent) { $stdErrContent = "" } if ($ReturnObject) { return [pscustomobject]@{ ExitCode = $process.ExitCode StdOut = $stdOutContent StdErr = $stdErrContent } } else { # Display output if not returning object if ($stdOutContent) { Write-Host $stdOutContent } if ($stdErrContent) { Write-Host $stdErrContent -ForegroundColor Yellow } return $process.ExitCode } } finally { if (Test-Path $tempOut) { Remove-Item $tempOut -Force -ErrorAction SilentlyContinue } if (Test-Path $tempErr) { Remove-Item $tempErr -Force -ErrorAction SilentlyContinue } } } # Compare two files by hash function Compare-Files { param( [Parameter(Mandatory=$true)] [string]$File1, [Parameter(Mandatory=$true)] [string]$File2 ) if (-not (Test-Path $File1)) { return $false } if (-not (Test-Path $File2)) { return $false } $hash1 = Get-FileHash -Path $File1 -Algorithm SHA256 $hash2 = Get-FileHash -Path $File2 -Algorithm SHA256 return $hash1.Hash -eq $hash2.Hash } # Create random data file function New-RandomFile { param( [Parameter(Mandatory=$true)] [string]$Path, [Parameter(Mandatory=$true)] [int]$SizeBytes ) $bytes = New-Object byte[] $SizeBytes $rng = [System.Security.Cryptography.RandomNumberGenerator]::Create() $rng.GetBytes($bytes) [System.IO.File]::WriteAllBytes($Path, $bytes) $rng.Dispose() } # Setup test environment - creates run directory and changes to it function Initialize-Test { param( [Parameter(Mandatory=$true)] [string]$TestName ) # Use the test script directory as root, not the current working directory $script:TestRoot = $script:TestScriptDir $script:RunDir = Join-Path $script:TestRoot "run$TestName" # Cleanup any previous run if (Test-Path $script:RunDir) { Remove-Item -Recurse -Force $script:RunDir } New-Item -ItemType Directory -Path $script:RunDir -Force | Out-Null Set-Location $script:RunDir # Sync .NET current directory with PowerShell [System.IO.Directory]::SetCurrentDirectory($PWD.Path) } # Cleanup test environment function Complete-Test { Set-Location $script:TestRoot if (Test-Path $script:RunDir) { Remove-Item -Recurse -Force $script:RunDir -ErrorAction SilentlyContinue } } # Exit with error function Exit-TestWithError { param( [Parameter(Mandatory=$true)] [string]$Message ) Write-Host "ERROR: $Message" -ForegroundColor Red Complete-Test exit 1 } # Export commonly needed variables $script:TESTDATA = $script:TestDataDir $script:PARBINARY = $env:PARBINARYpar2cmdline-turbo-1.4.0/tests/unit_tests000077500000000000000000000026051514221355600203450ustar00rootroot00000000000000#!/bin/sh execdir="$PWD" for f in $execdir/tests/*_test do # check that the filename exists. If there are no files, # Bourne shell will treat $execdir/tests/*_test like a # literal string! [ -f "$f" ] || continue echo "------------------------------------------------------" echo "Running unit tests from file $f" echo "------------------------------------------------------" if [ -n "${PARVALGRINDOPTS+set}" ] then PARBINARY="valgrind $PARVALGRINDOPTS $f" else PARBINARY="$f" fi $PARBINARY || { echo "ERROR: $f failed." ; exit 1; } >&2 echo "------------------------------------------------------" echo "Unit test complete for file $f" echo "------------------------------------------------------" done for f in $execdir/tests/*_test.exe do # check that the filename exists. If there are no files, # Bourne shell will treat $execdir/tests/*_test like a # literal string! [ -f "$f" ] || continue echo "------------------------------------------------------" echo "Running unit tests from file $f" echo "------------------------------------------------------" wine $f || { echo "ERROR: $f failed." ; exit 1; } >&2 echo "------------------------------------------------------" echo "Unit test complete for file $f" echo "------------------------------------------------------" done exit 0 par2cmdline-turbo-1.4.0/tests/unit_tests.ps1000066400000000000000000000075651514221355600210560ustar00rootroot00000000000000#!/usr/bin/env pwsh # Unit tests runner - runs all compiled unit test executables # Runs all tests inline in the same console for visible output $ErrorActionPreference = "Stop" # Source common test functions . (Join-Path $PSScriptRoot "testfuncs.ps1") $script:RootDir = Split-Path -Parent $PSScriptRoot # Find the directory containing test executables $testDirs = @( (Join-Path $script:RootDir "x64\Release"), (Join-Path $script:RootDir "x64\Debug"), (Join-Path $script:RootDir "Win32\Release"), (Join-Path $script:RootDir "Win32\Debug"), (Join-Path $script:RootDir "ARM64\Release"), (Join-Path $script:RootDir "ARM64\Debug"), (Split-Path -Parent $PARBINARY) ) $testExeDir = $null foreach ($dir in $testDirs) { if (Test-Path $dir) { $testExeDir = $dir break } } if (-not $testExeDir) { Write-Host "ERROR: Could not find test executable directory" -ForegroundColor Red exit 1 } Write-Host "Looking for unit tests in: $testExeDir" -ForegroundColor Cyan Write-Host "" $unitTestExes = @( "letype_test.exe", "crc_test.exe", "md5_test.exe", "diskfile_test.exe", "libpar2_test.exe", "commandline_test.exe", "descriptionpacket_test.exe", "criticalpacket_test.exe", "reedsolomon_test.exe", "galois_test.exe", "utf8_test.exe" ) $passed = 0 $failed = 0 $skipped = 0 $failedTests = @() foreach ($testExe in $unitTestExes) { $testPath = Join-Path $testExeDir $testExe if (Test-Path $testPath) { Write-Host "------------------------------------------------------" Write-Host "Running unit tests from file $testExe" Write-Host "------------------------------------------------------" $startTime = Get-Date try { # Run the executable inline using the call operator # This keeps output visible in the same console & $testPath $exitCode = $LASTEXITCODE $duration = (Get-Date) - $startTime if ($exitCode -eq 0) { Write-Host "------------------------------------------------------" Write-Host "PASSED: $testExe (duration: $($duration.TotalSeconds.ToString('F2'))s)" -ForegroundColor Green Write-Host "------------------------------------------------------" $passed++ } else { Write-Host "------------------------------------------------------" Write-Host "FAILED: $testExe (exit code: $exitCode, duration: $($duration.TotalSeconds.ToString('F2'))s)" -ForegroundColor Red Write-Host "------------------------------------------------------" $failed++ $failedTests += $testExe } } catch { $duration = (Get-Date) - $startTime Write-Host "------------------------------------------------------" Write-Host "FAILED: $testExe - $_ (duration: $($duration.TotalSeconds.ToString('F2'))s)" -ForegroundColor Red Write-Host "------------------------------------------------------" $failed++ $failedTests += $testExe } Write-Host "" } else { Write-Host "SKIPPED: $testExe not found at $testPath" -ForegroundColor Yellow $skipped++ } } Write-Host "" Write-Host "======================================================" Write-Host "Unit Test Summary" Write-Host "======================================================" Write-Host "Passed: $passed" -ForegroundColor Green Write-Host "Failed: $failed" -ForegroundColor $(if ($failed -gt 0) { "Red" } else { "Green" }) Write-Host "Skipped: $skipped" -ForegroundColor Yellow Write-Host "" if ($failed -gt 0) { Write-Host "Failed tests:" -ForegroundColor Red foreach ($failedTest in $failedTests) { Write-Host " - $failedTest" -ForegroundColor Red } Write-Host "" exit 1 } exit 0par2cmdline-turbo-1.4.0/tests/utf8_test.vcxproj000066400000000000000000000044761514221355600215700ustar00rootroot00000000000000 Debug ARM64 Debug Win32 Debug x64 Release ARM64 Release Win32 Release x64 Win32Proj {757a04a3-9f41-42e2-813c-0774d3835afb} 10.0 Application Unicode v143 Console