├── .github └── workflows │ ├── ci.yml │ └── hexpm-release.yml ├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.ALL ├── LICENSE.TCL ├── LICENSE.txt ├── Makefile ├── README.md ├── c_src ├── stringprep.cpp ├── uni_data.c └── uni_norm.c ├── configure ├── configure.ac ├── rebar.config ├── rebar.config.script ├── src ├── stringprep.app.src └── stringprep.erl ├── test ├── tests.erl └── unload_test.erl ├── tools ├── uni_parse.tcl └── uni_parse2.tcl └── vars.config.in /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | tests: 8 | name: Tests 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | otp: ['20', '25', '26', '27', '28'] 13 | runs-on: ubuntu-24.04 14 | container: 15 | image: public.ecr.aws/docker/library/erlang:${{ matrix.otp }} 16 | steps: 17 | - uses: actions/checkout@v4 18 | - run: ./configure --enable-gcov 19 | - run: rebar3 compile 20 | - run: rebar3 xref 21 | - run: rebar3 dialyzer 22 | - run: rebar3 eunit -v 23 | - run: rebar3 compile 24 | - name: Run tests to obtain Erlang coverage 25 | run: | 26 | mv test/unload_test.erl . 27 | rebar3 eunit -v 28 | mv _build/test/cover/eunit.coverdata . 29 | - name: Run tests to obtain C coverage 30 | run: | 31 | mv unload_test.erl test/ 32 | rebar3 eunit -v 33 | mv eunit.coverdata _build/test/cover/ 34 | - name: Send to Coveralls 35 | if: matrix.otp == 27 36 | env: 37 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 38 | run: | 39 | apt-get -qq update 40 | apt-get -qq install pipx 41 | pipx install cpp-coveralls 42 | /github/home/.local/bin/cpp-coveralls -b `pwd` --verbose --gcov-options '\-lp' --dump c.json 43 | ADDJSONFILE=c.json COVERALLS=true rebar3 as test coveralls send 44 | curl -v -k https://coveralls.io/webhook \ 45 | --header "Content-Type: application/json" \ 46 | --data '{"repo_name":"$GITHUB_REPOSITORY", 47 | "repo_token":"$GITHUB_TOKEN", 48 | "payload":{"build_num":$GITHUB_RUN_ID, 49 | "status":"done"}}' 50 | -------------------------------------------------------------------------------- /.github/workflows/hexpm-release.yml: -------------------------------------------------------------------------------- 1 | name: Hex 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-24.04 11 | steps: 12 | 13 | - name: Check out 14 | uses: actions/checkout@v4 15 | 16 | - name: Get Erlang/OTP 17 | uses: erlef/setup-beam@v1 18 | with: 19 | otp-version: 27 20 | rebar3-version: '3.24.0' 21 | 22 | - name: Setup rebar3 hex 23 | run: | 24 | mkdir -p ~/.config/rebar3/ 25 | echo "{plugins, [rebar3_hex]}." > ~/.config/rebar3/rebar.config 26 | 27 | - name: Publish to hex.pm 28 | run: DEBUG=1 rebar3 hex publish --repo hexpm --yes 29 | env: 30 | HEX_API_KEY: ${{ secrets.HEX_API_KEY }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swo 2 | *.swp 3 | .eunit 4 | .rebar 5 | _build 6 | autom4te.cache 7 | c_src/*.d 8 | c_src/*.gcda 9 | c_src/*.gcno 10 | c_src/*.o 11 | config.log 12 | config.status 13 | deps 14 | ebin 15 | priv 16 | rebar.lock 17 | vars.config 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Version 1.0.32 2 | 3 | * Updating p1_utils to version 1.0.27. 4 | 5 | # Version 1.0.31 6 | 7 | * use on_load attribute for nif loading 8 | 9 | # Version 1.0.30 10 | 11 | * Updating p1_utils to version 1.0.26. 12 | 13 | # Version 1.0.29 14 | 15 | * Make test more robust 16 | 17 | # Version 1.0.28 18 | 19 | * Updating p1_utils to version 1.0.25. 20 | 21 | # Version 1.0.27 22 | 23 | * Updating p1_utils to version 1.0.23. 24 | * Switch from using Travis to Github Actions as CI 25 | 26 | # Version 1.0.26 27 | 28 | * Add tolower_nofilter function 29 | * Don't throw errors if nif library is already loaded 30 | 31 | # Version 1.0.25 32 | 33 | * Updating p1_utils to version 1.0.22. 34 | 35 | # Version 1.0.24 36 | 37 | * Updating p1_utils to version 1.0.21. 38 | 39 | # Version 1.0.23 40 | 41 | * Exclude old OTP releases from Travis 42 | * Fix hex to support compiling ejabberd with rebar3 43 | 44 | # Version 1.0.22 45 | 46 | * Updating p1_utils to version 1.0.20. 47 | 48 | # Version 1.0.21 49 | 50 | * Fix compilation with Erlang/OTP 23.0, and Travis 51 | 52 | # Version 1.0.20 53 | 54 | * Updating p1_utils to version 1.0.19. 55 | 56 | # Version 1.0.19 57 | 58 | * Updating p1_utils to version 1.0.18. 59 | * Update copyright year 60 | 61 | # Version 1.0.18 62 | 63 | * Updating p1_utils to version 1.0.17. 64 | 65 | # Version 1.0.17 66 | 67 | * Updating p1_utils to version 1.0.16. 68 | 69 | # Version 1.0.16 70 | 71 | * Updating p1_utils to version 1.0.15. 72 | 73 | # Version 1.0.15 74 | 75 | * Updating p1_utils to version 1.0.14. 76 | * Add contribution guide 77 | * Fix license discrepancies 78 | 79 | # Version 1.0.14 80 | 81 | * Updating p1_utils to version 1.0.13. 82 | 83 | # Version 1.0.13 84 | 85 | * Updating p1_utils to version 6ff85e8. 86 | 87 | # Version 1.0.12 88 | 89 | * Updating p1_utils to version 1.0.12. 90 | 91 | # Version 1.0.11 92 | 93 | * Updating p1_utils to version 1.0.11. 94 | * Fix compilation with rebar3 95 | * Update FSF address 96 | 97 | # Version 1.0.10 98 | 99 | * Updating p1_utils to version 1.0.10. 100 | 101 | # Version 1.0.9 102 | 103 | * depends on p1_utils-1.0.9 104 | 105 | # Version 1.0.8 106 | 107 | * Updated rebar.config.script (Paweł Chmielowski) 108 | * Use p1_utils 1.0.7 (Christophe Romain) 109 | 110 | # Version 1.0.7 111 | 112 | * Use p1_utils 1.0.6 (Christophe Romain) 113 | * Fix tests (Radek Szymczyszyn) 114 | * Make sure stringprep isn't compiled to native code (Holger Weiss) 115 | 116 | # Version 1.0.6 117 | 118 | * Use p1_utils v1.0.5 (Mickaël Rémond) 119 | 120 | # Version 1.0.5 121 | 122 | * Fix compilation on rebar3 (Paweł Chmielowski) 123 | 124 | # Version 1.0.4 125 | 126 | * Use p1_utils v1.0.4 (Mickaël Rémond) 127 | 128 | # Version 1.0.3 129 | 130 | * Fix for compilation on Windows (Paweł Chmielowski) 131 | * Fix typo in error message (Paweł Chmielowski) 132 | 133 | # Version 1.0.1 134 | 135 | * Use p1_utils v1.0.3 (Mickaël Rémond) 136 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at contact@process-one.net. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We'd love for you to contribute to our source code and to make our project even better than it is 4 | today! Here are the guidelines we'd like you to follow: 5 | 6 | * [Code of Conduct](#coc) 7 | * [Questions and Problems](#question) 8 | * [Issues and Bugs](#issue) 9 | * [Feature Requests](#feature) 10 | * [Issue Submission Guidelines](#submit) 11 | * [Pull Request Submission Guidelines](#submit-pr) 12 | * [Signing the CLA](#cla) 13 | 14 | ## Code of Conduct 15 | 16 | Help us keep our community open-minded and inclusive. Please read and follow our [Code of Conduct][coc]. 17 | 18 | ## Questions, Bugs, Features 19 | 20 | ### Got a Question or Problem? 21 | 22 | Do not open issues for general support questions as we want to keep GitHub issues for bug reports 23 | and feature requests. You've got much better chances of getting your question answered on dedicated 24 | support platforms, the best being [Stack Overflow][stackoverflow]. 25 | 26 | Stack Overflow is a much better place to ask questions since: 27 | 28 | - there are thousands of people willing to help on Stack Overflow 29 | - questions and answers stay available for public viewing so your question / answer might help 30 | someone else 31 | - Stack Overflow's voting system assures that the best answers are prominently visible. 32 | 33 | To save your and our time, we will systematically close all issues that are requests for general 34 | support and redirect people to the section you are reading right now. 35 | 36 | ### Found an Issue or Bug? 37 | 38 | If you find a bug in the source code, you can help us by submitting an issue to our 39 | [GitHub Repository][github]. Even better, you can submit a Pull Request with a fix. 40 | 41 | ### Missing a Feature? 42 | 43 | You can request a new feature by submitting an issue to our [GitHub Repository][github-issues]. 44 | 45 | If you would like to implement a new feature then consider what kind of change it is: 46 | 47 | * **Major Changes** that you wish to contribute to the project should be discussed first in an 48 | [GitHub issue][github-issues] that clearly outlines the changes and benefits of the feature. 49 | * **Small Changes** can directly be crafted and submitted to the [GitHub Repository][github] 50 | as a Pull Request. See the section about [Pull Request Submission Guidelines](#submit-pr). 51 | 52 | ## Issue Submission Guidelines 53 | 54 | Before you submit your issue search the archive, maybe your question was already answered. 55 | 56 | If your issue appears to be a bug, and hasn't been reported, open a new issue. Help us to maximize 57 | the effort we can spend fixing issues and adding new features, by not reporting duplicate issues. 58 | 59 | The "[new issue][github-new-issue]" form contains a number of prompts that you should fill out to 60 | make it easier to understand and categorize the issue. 61 | 62 | ## Pull Request Submission Guidelines 63 | 64 | By submitting a pull request for a code or doc contribution, you need to have the right 65 | to grant your contribution's copyright license to ProcessOne. Please check [ProcessOne CLA][cla] 66 | for details. 67 | 68 | Before you submit your pull request consider the following guidelines: 69 | 70 | * Search [GitHub][github-pr] for an open or closed Pull Request 71 | that relates to your submission. You don't want to duplicate effort. 72 | * Make your changes in a new git branch: 73 | 74 | ```shell 75 | git checkout -b my-fix-branch master 76 | ``` 77 | * Test your changes and, if relevant, expand the automated test suite. 78 | * Create your patch commit, including appropriate test cases. 79 | * If the changes affect public APIs, change or add relevant documentation. 80 | * Commit your changes using a descriptive commit message. 81 | 82 | ```shell 83 | git commit -a 84 | ``` 85 | Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files. 86 | 87 | * Push your branch to GitHub: 88 | 89 | ```shell 90 | git push origin my-fix-branch 91 | ``` 92 | 93 | * In GitHub, send a pull request to `master` branch. This will trigger the continuous integration and run the test. 94 | We will also notify you if you have not yet signed the [contribution agreement][cla]. 95 | 96 | * If you find that the continunous integration has failed, look into the logs to find out 97 | if your changes caused test failures, the commit message was malformed etc. If you find that the 98 | tests failed or times out for unrelated reasons, you can ping a team member so that the build can be 99 | restarted. 100 | 101 | * If we suggest changes, then: 102 | 103 | * Make the required updates. 104 | * Test your changes and test cases. 105 | * Commit your changes to your branch (e.g. `my-fix-branch`). 106 | * Push the changes to your GitHub repository (this will update your Pull Request). 107 | 108 | You can also amend the initial commits and force push them to the branch. 109 | 110 | ```shell 111 | git rebase master -i 112 | git push origin my-fix-branch -f 113 | ``` 114 | 115 | This is generally easier to follow, but separate commits are useful if the Pull Request contains 116 | iterations that might be interesting to see side-by-side. 117 | 118 | That's it! Thank you for your contribution! 119 | 120 | ## Signing the Contributor License Agreement (CLA) 121 | 122 | Upon submitting a Pull Request, we will ask you to sign our CLA if you haven't done 123 | so before. It's a quick process, we promise, and you will be able to do it all online 124 | 125 | You can read [ProcessOne Contribution License Agreement][cla] in PDF. 126 | 127 | This is part of the legal framework of the open-source ecosystem that adds some red tape, 128 | but protects both the contributor and the company / foundation behind the project. It also 129 | gives us the option to relicense the code with a more permissive license in the future. 130 | 131 | 132 | [coc]: https://github.com/processone/stringprep/blob/master/CODE_OF_CONDUCT.md 133 | [stackoverflow]: https://stackoverflow.com/ 134 | [github]: https://github.com/processone/stringprep 135 | [github-issues]: https://github.com/processone/stringprep/issues 136 | [github-new-issue]: https://github.com/processone/stringprep/issues/new 137 | [github-pr]: https://github.com/processone/stringprep/pulls 138 | [cla]: https://www.process-one.net/resources/ejabberd-cla.pdf 139 | [license]: https://github.com/processone/stringprep/blob/master/LICENSE.txt 140 | -------------------------------------------------------------------------------- /LICENSE.ALL: -------------------------------------------------------------------------------- 1 | tools/uni_parse.tcl and tools/uni_parse2.tcl are based on tools/uniParse.tcl 2 | from Tcl distribution, and they generate c_src/uni_data.c and 3 | c_src/uni_norm.c. Those files are distributed under BSD-style Tcl/Tk license 4 | (see LICENSE.TCL). 5 | 6 | The rest of the code is under Apache v2 License. 7 | -------------------------------------------------------------------------------- /LICENSE.TCL: -------------------------------------------------------------------------------- 1 | This software is copyrighted by the Regents of the University of 2 | California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState 3 | Corporation and other parties. The following terms apply to all files 4 | associated with the software unless explicitly disclaimed in 5 | individual files. 6 | 7 | The authors hereby grant permission to use, copy, modify, distribute, 8 | and license this software and its documentation for any purpose, provided 9 | that existing copyright notices are retained in all copies and that this 10 | notice is included verbatim in any distributions. No written agreement, 11 | license, or royalty fee is required for any of the authorized uses. 12 | Modifications to this software may be copyrighted by their authors 13 | and need not follow the licensing terms described here, provided that 14 | the new terms are clearly indicated on the first page of each file where 15 | they apply. 16 | 17 | IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY 18 | FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 19 | ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY 20 | DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE 21 | POSSIBILITY OF SUCH DAMAGE. 22 | 23 | THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, 24 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE 26 | IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE 27 | NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR 28 | MODIFICATIONS. 29 | 30 | GOVERNMENT USE: If you are acquiring this software on behalf of the 31 | U.S. government, the Government shall have only "Restricted Rights" 32 | in the software and related documentation as defined in the Federal 33 | Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you 34 | are acquiring the software on behalf of the Department of Defense, the 35 | software shall be classified as "Commercial Computer Software" and the 36 | Government shall have only "Restricted Rights" as defined in Clause 37 | 252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the 38 | authors grant the U.S. Government and others acting in its behalf 39 | permission to use and distribute the software in accordance with the 40 | terms specified in this license. 41 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REBAR ?= rebar 2 | 3 | all: src 4 | 5 | src: 6 | $(REBAR) get-deps 7 | $(REBAR) compile 8 | 9 | clean: 10 | $(REBAR) clean 11 | 12 | test: all 13 | $(REBAR) eunit 14 | 15 | .PHONY: clean src test all 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fast Stringprep implementation for Erlang / Elixir 2 | 3 | [![CI](https://github.com/processone/stringprep/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/processone/stringprep/actions/workflows/ci.yml) 4 | [![Coverage Status](https://coveralls.io/repos/processone/stringprep/badge.svg?branch=master&service=github)](https://coveralls.io/github/processone/stringprep?branch=master) 5 | [![Hex version](https://img.shields.io/hexpm/v/stringprep.svg "Hex version")](https://hex.pm/packages/stringprep) 6 | 7 | Stringprep is a framework for preparing Unicode test strings in order 8 | to increase the likelihood that string input and string comparison 9 | work. 10 | 11 | The principle are defined in [RFC-3454: Preparation of 12 | Internationalized Strings ](http://tools.ietf.org/html/rfc3454). 13 | 14 | This library is leverage Erlang native NIF mechanism to provide 15 | extremely fast and efficient processing. 16 | 17 | The library includes support for several Stringprep profiles used in 18 | XMPP protocole like: 19 | 20 | * Nodeprep 21 | * Nameprep 22 | * Resourceprep 23 | 24 | For those profiles, the rules are applied according to 25 | [RFC6122](http://xmpp.org/rfcs/rfc6122.html#security-stringprep). The 26 | various functions perform check on the allowed / forbidden chars for a 27 | given profile or prevent combining left-to-right and right-to-left 28 | chars. 29 | 30 | It the binary string passed to a function of the API is valid, it will 31 | return its normalized version according to Stringprep 32 | profile. Otherwise, if the binary string is invalid, for example 33 | because it contains invalid chars, the function will return error. 34 | 35 | The library is heavily used in XMPP string processing. However, the 36 | library is more generally useful in code that need to manipulate and 37 | compare Unicode strings. 38 | 39 | ## Building 40 | 41 | Fast Stringprep processing tool can be build as follow: 42 | 43 | ./configure && make 44 | 45 | Configure script recognizes one flag - pass `--enable-gcov` to enable gcov 46 | coverage reporting. 47 | 48 | It is a rebar-compatible OTP application. Alternatively, you can build 49 | it with rebar: 50 | 51 | rebar get-deps compile 52 | 53 | ## Usage 54 | 55 | You can start the application with the command: 56 | 57 | ``` 58 | $ erl -pa ebin/ 59 | Erlang/OTP 18 [erts-7.1] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace] 60 | 61 | Eshell V7.1 (abort with ^G) 62 | 1> application:start(p1_stringprep). 63 | ``` 64 | 65 | You can then call any of the stringprep function to apply a profile: 66 | 67 | ``` 68 | stringprep:nodeprep(<<>>). 69 | stringprep:nameprep(<<>>). 70 | stringprep:resourceprep(<<>>). 71 | stringprep:tolower(<<>>)). 72 | ``` 73 | 74 | ## Development 75 | 76 | ### Test 77 | 78 | #### Unit test 79 | 80 | You can run eunit test with the command: 81 | 82 | $ make test 83 | -------------------------------------------------------------------------------- /c_src/stringprep.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "uni_data.c" 23 | #include "uni_norm.c" 24 | 25 | /* Hangul constants */ 26 | #define SBase 0xAC00 27 | #define LBase 0x1100 28 | #define VBase 0x1161 29 | #define TBase 0x11A7 30 | #define LCount 19 31 | #define VCount 21 32 | #define TCount 28 33 | #define NCount (VCount * TCount) 34 | #define SCount (LCount * NCount) 35 | 36 | static int compose(int ch1, int ch2) { 37 | int info1, info2; 38 | 39 | if (LBase <= ch1 && ch1 < LBase + LCount && 40 | VBase <= ch2 && ch2 < VBase + VCount) { 41 | return SBase + ((ch1 - LBase) * VCount + (ch2 - VBase)) * TCount; 42 | } 43 | 44 | if (SBase <= ch1 && ch1 < SBase + SCount && ((ch1 - SBase) % TCount) == 0 && 45 | TBase <= ch2 && ch2 < TBase + TCount) { 46 | return ch1 + ch2 - TBase; 47 | } 48 | 49 | info1 = GetUniCharCompInfo(ch1); 50 | if (info1 != -1 && info1 & CompSingleMask) { 51 | if (!(info1 & CompSecondMask) && 52 | ch2 == compFirstList[info1 & CompMask][0]) { 53 | return compFirstList[info1 & CompMask][1]; 54 | } else 55 | return 0; 56 | } 57 | 58 | info2 = GetUniCharCompInfo(ch2); 59 | if (info2 != -1 && info2 & CompSingleMask) { 60 | if ((info2 & CompSecondMask) && 61 | ch1 == compSecondList[info2 & CompMask][0]) { 62 | return compSecondList[info2 & CompMask][1]; 63 | } else 64 | return 0; 65 | } 66 | 67 | if (info1 != -1 && info2 != -1 && 68 | !(info1 & CompSecondMask) && (info2 & CompSecondMask)) 69 | return compBothList[info1][info2 & CompMask]; 70 | else 71 | return 0; 72 | } 73 | 74 | template 75 | class MaybeStaticBuf { 76 | public: 77 | MaybeStaticBuf() : pos(0), size(N), len(0), buf(static_buf) { } 78 | ~MaybeStaticBuf() { 79 | if (buf != static_buf) 80 | enif_free(buf); 81 | } 82 | T init(T ch) { 83 | len = 1; 84 | pos = 0; 85 | return buf[0] = ch; 86 | } 87 | T add(T ch) { 88 | if (len >= size) { 89 | if (buf == static_buf) { 90 | T *old = buf; 91 | buf = (T *) enif_alloc(sizeof(T) * size * 2); 92 | if (!buf) 93 | return -2; 94 | memcpy(buf, old, size * sizeof(T)); 95 | } else { 96 | buf = (T *) enif_realloc(buf, sizeof(T) * size * 2); 97 | if (!buf) 98 | return -2; 99 | } 100 | size *= 2; 101 | } 102 | buf[len++] = ch; 103 | return ch; 104 | } 105 | 106 | void empty() { 107 | pos = len = 0; 108 | } 109 | 110 | void swap(int p1, int p2) { 111 | T ch = buf[p1]; 112 | buf[p1] = buf[p2]; 113 | buf[p2] = ch; 114 | } 115 | 116 | T operator[](int index) { 117 | return buf[index]; 118 | } 119 | 120 | int pos; 121 | int size; 122 | int len; 123 | private: 124 | T static_buf[N]; 125 | T *buf; 126 | }; 127 | 128 | class UTF8DecoderStream { 129 | public: 130 | UTF8DecoderStream(ErlNifBinary *input) : input(input), pos(0) { }; 131 | 132 | void reset() { 133 | pos = 0; 134 | } 135 | 136 | ErlNifBinary *getBinary() { 137 | return input; 138 | } 139 | 140 | int32_t getNext() { 141 | if (pos >= input->size) 142 | return -1; 143 | unsigned char c = input->data[pos++]; 144 | if (c <= 0x80) { 145 | return c; 146 | } else if (c < 0xC0) { 147 | return -2; 148 | } else if (c < 0xE0) { 149 | if (pos < input->size && (input->data[pos] & 0xC0) == 0x80) { 150 | return ((c & 0x1F) << 6) | (input->data[pos++] & 0x3F); 151 | } 152 | } else if (c < 0xF0) { 153 | if (pos + 1 < input->size && (input->data[pos] & 0xC0) == 0x80 && 154 | (input->data[pos + 1] & 0xC0) == 0x80) { 155 | pos += 2; 156 | return ((c & 0x0F) << 12) 157 | | ((input->data[pos - 2] & 0x3F) << 6) 158 | | (input->data[pos - 1] & 0x3F); 159 | } 160 | } else if (c < 0xF8) { 161 | if (pos + 2 < input->size && 162 | (input->data[pos] & 0xC0) == 0x80 && 163 | (input->data[pos + 1] & 0xC0) == 0x80 && 164 | (input->data[pos + 2] & 0xC0) == 0x80) { 165 | int32_t wc = ((c & 0x07) << 18) 166 | | ((input->data[pos] & 0x3F) << 12) 167 | | ((input->data[pos + 1] & 0x3F) << 6) 168 | | (input->data[pos + 2] & 0x3F); 169 | pos += 3; 170 | if (wc <= 0x10FFFF) 171 | return wc; 172 | } 173 | } 174 | return -2; 175 | } 176 | 177 | private: 178 | ErlNifBinary *input; 179 | size_t pos; 180 | }; 181 | 182 | class PreprocessStream { 183 | public: 184 | PreprocessStream(UTF8DecoderStream *source, bool toLower) : 185 | source(source), buf(NULL), pos(0), len(0), toLower(toLower) { 186 | } 187 | 188 | int32_t getNext() { 189 | if (pos < len) 190 | return buf[pos++]; 191 | 192 | loop: 193 | int32_t ch = source->getNext(); 194 | if (ch < 0) 195 | return ch; 196 | int info = GetUniCharInfo(ch); 197 | 198 | if (!(info & B1Mask)) { 199 | if (toLower) { 200 | if (!(info & MCMask)) { 201 | return ch + GetDelta(info); 202 | } else { 203 | buf = GetMC(info) + 1; 204 | len = buf[-1]; 205 | pos = 1; 206 | return buf[0]; 207 | } 208 | } else { 209 | return ch; 210 | } 211 | } else 212 | goto loop; 213 | } 214 | private: 215 | UTF8DecoderStream *source; 216 | int32_t *buf; 217 | int pos; 218 | int len; 219 | bool toLower; 220 | }; 221 | 222 | class DecompositeStream { 223 | public: 224 | DecompositeStream(PreprocessStream *source) : source(source), pos(0), len(0) { } 225 | 226 | int32_t getNext() { 227 | if (pos < len) 228 | return decompList[pos++]; 229 | 230 | int32_t ch = source->getNext(); 231 | 232 | if (ch < 0) 233 | return ch; 234 | 235 | int info = GetUniCharDecompInfo(ch); 236 | if (info >= 0) { 237 | pos = GetDecompShift(info); 238 | len = pos + GetDecompLen(info); 239 | return decompList[pos++]; 240 | } else 241 | return ch; 242 | } 243 | 244 | private: 245 | PreprocessStream *source; 246 | int pos; 247 | int len; 248 | }; 249 | 250 | class CanonicalizeStream { 251 | public: 252 | CanonicalizeStream(DecompositeStream *source) : source(source), buf() { 253 | } 254 | 255 | int32_t getNext() { 256 | if (buf.pos < buf.len - 1) 257 | return buf[buf.pos++]; 258 | 259 | int32_t ch, ch2; 260 | if (buf.len > 0) { 261 | ch = buf.init(buf[buf.len - 1]); 262 | } else { 263 | ch = buf.init(source->getNext()); 264 | 265 | if (ch < 0) 266 | return ch; 267 | } 268 | 269 | buf.pos++; 270 | 271 | int last = GetUniCharCClass(ch); 272 | while ((ch2 = buf.add(source->getNext())) >= 0) { 273 | int next = GetUniCharCClass(ch2); 274 | if (next != 0 && last > next) { 275 | for (int j = buf.len - 2; j >= 0; j--) { 276 | if (GetUniCharCClass(buf[j]) <= next) 277 | break; 278 | buf.swap(j, j + 1); 279 | } 280 | } else { 281 | return buf[0]; 282 | } 283 | } 284 | return buf[0]; 285 | } 286 | 287 | private: 288 | DecompositeStream *source; 289 | MaybeStaticBuf buf; 290 | }; 291 | 292 | class ComposeStream { 293 | public: 294 | ComposeStream(CanonicalizeStream *source) : source(source), buf(), lastCh(-1) { 295 | } 296 | 297 | int32_t getNext() { 298 | int32_t ch, nch; 299 | 300 | if (buf.pos < buf.len) 301 | return buf[buf.pos++]; 302 | else 303 | buf.empty(); 304 | 305 | if (lastCh < 0) { 306 | ch = source->getNext(); 307 | if (ch < 0) 308 | return ch; 309 | } else { 310 | ch = lastCh; 311 | } 312 | 313 | int cclass1 = GetUniCharCClass(ch); 314 | while ((lastCh = source->getNext()) >= 0) { 315 | int cclass2 = GetUniCharCClass(lastCh); 316 | if ((cclass1 == 0 || cclass2 > cclass1) && 317 | (nch = compose(ch, lastCh))) { 318 | ch = nch; 319 | } else if (cclass2 == 0) { 320 | return ch; 321 | } else { 322 | buf.add(lastCh); 323 | cclass1 = cclass2; 324 | } 325 | } 326 | 327 | if (lastCh >= -1) 328 | return ch; 329 | else 330 | return lastCh; 331 | } 332 | private: 333 | CanonicalizeStream *source; 334 | MaybeStaticBuf buf; 335 | int32_t lastCh; 336 | }; 337 | 338 | class PrepCheckStream { 339 | public: 340 | PrepCheckStream(ComposeStream *source, int32_t prohibit) : 341 | source(source), prohibit(prohibit), first_ral(-1), 342 | last_ral(0), have_ral(0), have_l(0) { 343 | } 344 | 345 | int32_t getNext() { 346 | int32_t ch = source->getNext(); 347 | if (ch < 0) 348 | return ch; 349 | 350 | int32_t info = GetUniCharInfo(ch); 351 | 352 | if (info & prohibit) { 353 | return -2; 354 | } 355 | if (first_ral < 0) 356 | first_ral = (info & D1Mask) != 0; 357 | 358 | last_ral = (info & D1Mask) != 0; 359 | have_ral = have_ral || last_ral; 360 | have_l = have_l || (info & D2Mask) != 0; 361 | 362 | return ch; 363 | } 364 | 365 | bool was_valid() { 366 | return !(have_ral && (!first_ral || !last_ral || have_l)); 367 | } 368 | private: 369 | ComposeStream *source; 370 | int32_t prohibit; 371 | char first_ral; 372 | char last_ral; 373 | char have_ral; 374 | char have_l; 375 | }; 376 | 377 | class UTF8Encoder { 378 | public: 379 | UTF8Encoder(size_t initial_size, UTF8DecoderStream *input) : input(*input), pos(0) { 380 | binary.size = initial_size < 4 ? 4 : initial_size; 381 | binary.data = NULL; 382 | } 383 | 384 | ~UTF8Encoder() { 385 | if (binary.data) 386 | enif_release_binary(&binary); 387 | } 388 | 389 | ErlNifBinary *encode_stream(PrepCheckStream *source) { 390 | int32_t ch, ich; 391 | int idx = 0; 392 | 393 | while ((ch = source->getNext()) == (ich = input.getNext()) && ch >= 0) { 394 | idx++; 395 | } 396 | if (ch < -1) 397 | return NULL; 398 | if (ch != ich) { 399 | input.reset(); 400 | while (idx-- > 0) 401 | if (put_char(input.getNext()) < 0) 402 | return NULL; 403 | if (ch >= 0) { 404 | do { 405 | if (put_char(ch) < 0) 406 | return NULL; 407 | } while ((ch = source->getNext()) >= 0); 408 | if (ch < -1) 409 | return NULL; 410 | } 411 | } else { 412 | return input.getBinary(); 413 | } 414 | 415 | if (binary.data) { 416 | if (pos != binary.size && !enif_realloc_binary(&binary, pos)) 417 | return NULL; 418 | } else if (!enif_alloc_binary(0, &binary)) 419 | return NULL; 420 | 421 | return &binary; 422 | } 423 | 424 | int put_char(int32_t ch) { 425 | if (ch <= 0x7F) { 426 | if (!buf_size_inc(1)) return -2; 427 | binary.data[pos++] = (unsigned char) ch; 428 | } else if (ch <= 0x7FF) { 429 | if (!buf_size_inc(2)) return -2; 430 | binary.data[pos] = (unsigned char) ((ch >> 6) | 0xC0); 431 | binary.data[pos + 1] = (unsigned char) ((ch | 0x80) & 0xBF); 432 | pos += 2; 433 | } else if (ch <= 0xFFFF) { 434 | if (!buf_size_inc(3)) return -2; 435 | binary.data[pos] = (unsigned char) ((ch >> 12) | 0xE0); 436 | binary.data[pos + 1] = (unsigned char) (((ch >> 6) | 0x80) & 0xBF); 437 | binary.data[pos + 2] = (unsigned char) ((ch | 0x80) & 0xBF); 438 | pos += 3; 439 | } else if (ch <= 0x1FFFFF) { 440 | if (!buf_size_inc(4)) return -2; 441 | binary.data[pos] = (unsigned char) ((ch >> 18) | 0xF0); 442 | binary.data[pos + 1] = (unsigned char) (((ch >> 12) | 0x80) & 0xBF); 443 | binary.data[pos + 2] = (unsigned char) (((ch >> 6) | 0x80) & 0xBF); 444 | binary.data[pos + 3] = (unsigned char) ((ch | 0x80) & 0xBF); 445 | pos += 4; 446 | } else 447 | return -2; 448 | return 0; 449 | } 450 | private: 451 | int buf_size_inc(int inc) { 452 | int res = 1; 453 | 454 | if (!binary.data) 455 | res = enif_alloc_binary(binary.size, &binary); 456 | 457 | if (pos + inc > binary.size) 458 | res = enif_realloc_binary(&binary, binary.size * 2); 459 | 460 | return res; 461 | } 462 | 463 | UTF8DecoderStream input; 464 | ErlNifBinary binary; 465 | size_t pos; 466 | }; 467 | 468 | static int load(ErlNifEnv *env, void **priv, ERL_NIF_TERM load_info) { 469 | return 0; 470 | } 471 | 472 | static ERL_NIF_TERM prep(ErlNifEnv *env, int argc, 473 | const ERL_NIF_TERM argv[], 474 | int prohibit, bool toLower) { 475 | ErlNifBinary input; 476 | 477 | if (argc != 1) 478 | return enif_make_badarg(env); 479 | 480 | if (!enif_inspect_iolist_as_binary(env, argv[0], &input)) 481 | return enif_make_badarg(env); 482 | 483 | UTF8DecoderStream decoder(&input); 484 | PreprocessStream normalize(&decoder, toLower); 485 | DecompositeStream decomposite(&normalize); 486 | CanonicalizeStream canonicalize(&decomposite); 487 | ComposeStream compose(&canonicalize); 488 | PrepCheckStream prepCheck(&compose, prohibit); 489 | UTF8Encoder encode(input.size, &decoder); 490 | 491 | ErlNifBinary *res = encode.encode_stream(&prepCheck); 492 | 493 | if (!res || !prepCheck.was_valid()) { 494 | return enif_make_atom(env, "error"); 495 | } else 496 | return enif_make_binary(env, res); 497 | } 498 | 499 | static ERL_NIF_TERM nodeprep(ErlNifEnv *env, int argc, 500 | const ERL_NIF_TERM argv[]) { 501 | return prep(env, argc, argv, ACMask | C11Mask | C21Mask | XNPMask, 1); 502 | } 503 | 504 | static ERL_NIF_TERM nameprep(ErlNifEnv *env, int argc, 505 | const ERL_NIF_TERM argv[]) { 506 | return prep(env, argc, argv, ACMask, 1); 507 | } 508 | 509 | static ERL_NIF_TERM resourceprep(ErlNifEnv *env, int argc, 510 | const ERL_NIF_TERM argv[]) { 511 | return prep(env, argc, argv, ACMask | C21Mask, 0); 512 | } 513 | 514 | static ERL_NIF_TERM to_lower(ErlNifEnv *env, int argc, 515 | const ERL_NIF_TERM argv[]) { 516 | return prep(env, argc, argv, ACMask, 1); 517 | } 518 | 519 | static ERL_NIF_TERM to_lower_no_filter(ErlNifEnv *env, int argc, 520 | const ERL_NIF_TERM argv[]) { 521 | return prep(env, argc, argv, 0, 1); 522 | } 523 | 524 | static ErlNifFunc nif_funcs[] = 525 | { 526 | {"nodeprep", 1, nodeprep}, 527 | {"nameprep", 1, nameprep}, 528 | {"resourceprep", 1, resourceprep}, 529 | {"tolower", 1, to_lower}, 530 | {"tolower_nofilter", 1, to_lower_no_filter} 531 | }; 532 | 533 | ERL_NIF_INIT(stringprep, nif_funcs, load, NULL, NULL, NULL) 534 | -------------------------------------------------------------------------------- /c_src/uni_data.c: -------------------------------------------------------------------------------- 1 | /* 2 | * uni_data.c -- 3 | * 4 | * Declarations of Unicode character information tables. This file is 5 | * automatically generated by the uni_parse.tcl script. Do not 6 | * modify this file by hand. 7 | * 8 | * Copyright (c) 1998 by Scriptics Corporation. 9 | * All rights reserved. 10 | * 11 | * Modified for ejabberd by Alexey Shchepin 12 | * 13 | * RCS: @(#) $Id$ 14 | */ 15 | 16 | /* 17 | * A 16-bit Unicode character is split into two parts in order to index 18 | * into the following tables. The lower OFFSET_BITS comprise an offset 19 | * into a page of characters. The upper bits comprise the page number. 20 | */ 21 | 22 | #define OFFSET_BITS 8 23 | 24 | /* 25 | * The pageMap is indexed by page number and returns an alternate page number 26 | * that identifies a unique page of characters. Many Unicode characters map 27 | * to the same alternate page number. 28 | */ 29 | 30 | static unsigned char pageMap[] = { 31 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 32 | 20, 21, 22, 23, 24, 8, 8, 8, 8, 8, 25, 26, 27, 28, 29, 30, 31, 29, 33 | 32, 33, 29, 29, 29, 8, 8, 8, 34, 35, 36, 37, 38, 39, 21, 21, 21, 21, 34 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 35 | 21, 21, 21, 21, 40, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 36 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 37 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 38 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 39 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 40 | 21, 41, 21, 21, 21, 21, 42, 8, 8, 8, 8, 8, 8, 8, 21, 21, 21, 21, 21, 41 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 42 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 43 | 21, 21, 21, 21, 43, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45 | 44, 44, 44, 44, 21, 45, 46, 47, 48, 49, 50, 8, 8, 8, 51, 52, 8, 8, 46 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 47 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 48 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 49 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 50 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 51 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 52 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 53 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 54 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 53, 54, 8, 8, 55, 55 | 56, 57, 58, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 56 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 21, 57 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 58 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 59 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 60 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 61 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 62 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 63 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 64 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 65 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 66 | 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 59, 8, 8, 8, 8, 8, 67 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 68 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 69 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 70 | 8, 8, 8, 8, 8, 8, 8, 21, 21, 60, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 71 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 72 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 73 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 74 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 75 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 76 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 77 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 78 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 79 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 80 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 81 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 82 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 83 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 84 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 85 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 86 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 87 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 88 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 89 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 90 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 91 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 92 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 93 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 94 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 95 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 96 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 97 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 98 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 99 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 100 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 101 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 102 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 103 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 104 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 105 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 106 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 107 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 108 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 109 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 110 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 111 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 112 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 113 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 114 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 115 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 116 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 117 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 118 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 119 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 120 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 121 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 122 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 123 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 124 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 125 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 126 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 127 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 128 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 129 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 130 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 131 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 132 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 133 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 134 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 135 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 136 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 137 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 138 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 139 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 140 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 141 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 142 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 143 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 144 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 145 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 146 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 147 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 148 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 149 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 150 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 151 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 152 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 153 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 154 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 155 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 156 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 157 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 158 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 159 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 160 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 161 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 162 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 163 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 164 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 165 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 166 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 167 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 168 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 169 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 170 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 171 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 172 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 173 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 174 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 175 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 176 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 177 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 178 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 179 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 180 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 181 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 182 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 183 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 184 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 185 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 186 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 187 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 188 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 189 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 190 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 191 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 192 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 193 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 194 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 195 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 196 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 197 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 198 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 199 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 200 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 201 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 202 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 203 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 204 | 8, 8, 8, 8, 8, 8, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 205 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 206 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 207 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 208 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 209 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 210 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 211 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 212 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 213 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 214 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 215 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 216 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 217 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 218 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 219 | 44, 44, 44, 44, 61, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 220 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 221 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 222 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 223 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 224 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 225 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 226 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 227 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 228 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 229 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 230 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 231 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 232 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 233 | 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 234 | 44, 44, 44, 44, 44, 61 235 | }; 236 | 237 | /* 238 | * The groupMap is indexed by combining the alternate page number with 239 | * the page offset and returns a group number that identifies a unique 240 | * set of character attributes. 241 | */ 242 | 243 | static unsigned short int groupMap[] = { 244 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 2, 2, 3, 3, 2, 2, 2, 2, 2, 2, 246 | 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 4, 4, 4, 4, 247 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 248 | 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 249 | 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 250 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 251 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, 7, 2, 2, 2, 2, 2, 2, 2, 8, 2, 2, 252 | 2, 2, 5, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 253 | 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 9, 5, 5, 5, 5, 5, 5, 254 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 255 | 5, 5, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 256 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 257 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 11, 5, 10, 5, 10, 5, 10, 5, 5, 258 | 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 12, 10, 5, 259 | 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 260 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 261 | 5, 10, 5, 10, 5, 13, 10, 5, 10, 5, 10, 5, 14, 5, 15, 10, 5, 10, 5, 262 | 16, 10, 5, 17, 17, 10, 5, 5, 18, 19, 20, 10, 5, 17, 21, 5, 22, 23, 263 | 10, 5, 5, 5, 22, 24, 5, 25, 10, 5, 10, 5, 10, 5, 26, 10, 5, 26, 5, 264 | 5, 10, 5, 26, 10, 5, 27, 27, 10, 5, 10, 5, 28, 10, 5, 5, 5, 10, 5, 265 | 5, 5, 5, 5, 5, 5, 29, 10, 5, 29, 10, 5, 29, 10, 5, 10, 5, 10, 5, 10, 266 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 5, 10, 5, 10, 5, 10, 5, 10, 5, 267 | 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 30, 29, 10, 5, 10, 5, 31, 32, 10, 268 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 269 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 33, 270 | 6, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 6, 271 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 272 | 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 273 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 274 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 275 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 276 | 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 277 | 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 2, 2, 2, 2, 278 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 279 | 2, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 280 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 281 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 282 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 34, 2, 2, 283 | 2, 2, 2, 2, 2, 2, 2, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 284 | 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 2, 2, 285 | 6, 6, 6, 6, 35, 6, 6, 6, 2, 6, 6, 6, 6, 6, 2, 2, 36, 2, 37, 37, 37, 286 | 6, 38, 6, 39, 39, 40, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 287 | 4, 4, 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 41, 5, 5, 5, 5, 5, 288 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 289 | 5, 5, 5, 6, 42, 43, 44, 45, 46, 47, 48, 5, 10, 5, 10, 5, 10, 5, 10, 290 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 49, 50, 291 | 51, 5, 52, 53, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 54, 54, 54, 54, 54, 54, 292 | 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 4, 4, 4, 4, 4, 4, 4, 4, 4, 293 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 294 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 295 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 296 | 5, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 297 | 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 5, 2, 2, 2, 298 | 2, 6, 2, 2, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 299 | 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 300 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 5, 301 | 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 6, 10, 5, 10, 5, 10, 302 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 303 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 6, 6, 10, 5, 6, 6, 6, 304 | 6, 6, 6, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 6, 305 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 306 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 307 | 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 308 | 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 6, 6, 5, 5, 5, 5, 5, 5, 309 | 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 310 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 56, 6, 5, 2, 6, 311 | 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 312 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 313 | 6, 2, 2, 2, 57, 2, 57, 2, 2, 57, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 314 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 315 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 6, 6, 6, 6, 6, 57, 57, 57, 316 | 57, 57, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 317 | 6, 6, 6, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 57, 6, 6, 6, 318 | 57, 6, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 319 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 6, 6, 6, 6, 6, 57, 57, 320 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 321 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 322 | 57, 57, 57, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 323 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 324 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 325 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 326 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 327 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 328 | 57, 57, 57, 2, 2, 2, 2, 2, 2, 2, 58, 2, 2, 2, 2, 2, 2, 2, 57, 57, 2, 329 | 2, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 57, 57, 57, 57, 330 | 57, 6, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 6, 6, 331 | 57, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 332 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 6, 6, 6, 2, 2, 2, 2, 333 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 334 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 335 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 336 | 6, 6, 6, 6, 6, 6, 6, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 337 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 338 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 339 | 57, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 340 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 341 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 342 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 343 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 344 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 345 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 346 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 347 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 348 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 349 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 350 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 351 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 352 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 353 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 5, 6, 5, 5, 5, 5, 354 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 355 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 356 | 5, 5, 5, 6, 6, 2, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 2, 357 | 6, 6, 5, 2, 2, 2, 2, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 358 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 359 | 6, 6, 6, 6, 6, 2, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 6, 6, 360 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 361 | 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 6, 6, 5, 5, 5, 5, 6, 6, 2, 6, 5, 5, 5, 362 | 2, 2, 2, 2, 6, 6, 5, 5, 6, 6, 5, 5, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 363 | 6, 6, 6, 6, 5, 5, 6, 5, 5, 5, 2, 2, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 364 | 5, 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 2, 6, 6, 5, 365 | 5, 5, 5, 5, 5, 6, 6, 6, 6, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 366 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 367 | 6, 5, 5, 6, 5, 5, 6, 6, 2, 6, 5, 5, 5, 2, 2, 6, 6, 6, 6, 2, 2, 6, 6, 368 | 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 6, 5, 6, 6, 6, 369 | 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 6, 6, 6, 6, 370 | 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 5, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 371 | 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 372 | 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 6, 5, 5, 5, 5, 5, 6, 6, 2, 5, 373 | 5, 5, 5, 2, 2, 2, 2, 2, 6, 2, 2, 5, 6, 5, 5, 2, 6, 6, 5, 6, 6, 6, 6, 374 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 375 | 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 5, 376 | 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 377 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 378 | 6, 5, 5, 6, 6, 5, 5, 5, 5, 6, 6, 2, 5, 5, 2, 5, 2, 2, 2, 6, 6, 6, 5, 379 | 5, 6, 6, 5, 5, 2, 6, 6, 6, 6, 6, 6, 6, 6, 2, 5, 6, 6, 6, 6, 5, 5, 6, 380 | 5, 5, 5, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 381 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 5, 6, 5, 5, 5, 5, 5, 5, 6, 6, 382 | 6, 5, 5, 5, 6, 5, 5, 5, 5, 6, 6, 6, 5, 5, 6, 5, 6, 5, 5, 6, 6, 6, 5, 383 | 5, 6, 6, 6, 5, 5, 5, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 6, 384 | 6, 6, 6, 5, 5, 2, 5, 5, 6, 6, 6, 5, 5, 5, 6, 5, 5, 5, 2, 6, 6, 6, 6, 385 | 6, 6, 6, 6, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 386 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 387 | 6, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 6, 5, 5, 5, 5, 5, 388 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 389 | 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 6, 6, 6, 6, 2, 2, 2, 5, 5, 5, 5, 390 | 6, 2, 2, 2, 6, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 2, 2, 6, 6, 6, 6, 6, 391 | 6, 6, 6, 6, 5, 5, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 392 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 6, 5, 5, 5, 5, 5, 393 | 5, 5, 5, 6, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 394 | 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 395 | 5, 5, 6, 6, 6, 6, 5, 2, 5, 5, 5, 5, 5, 6, 2, 5, 5, 6, 5, 5, 2, 2, 6, 396 | 6, 6, 6, 6, 6, 6, 5, 5, 6, 6, 6, 6, 6, 6, 6, 5, 6, 5, 5, 6, 6, 6, 6, 397 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 398 | 6, 6, 6, 6, 6, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 6, 5, 5, 399 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 400 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 5, 5, 5, 2, 401 | 2, 2, 6, 6, 5, 5, 5, 6, 5, 5, 5, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 402 | 6, 6, 6, 6, 6, 6, 6, 5, 5, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 403 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 6, 5, 5, 404 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 5, 5, 5, 5, 405 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 406 | 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 2, 6, 407 | 6, 6, 6, 5, 5, 5, 2, 2, 2, 6, 2, 6, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 408 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 6, 6, 6, 6, 6, 409 | 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 410 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 411 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 412 | 2, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 413 | 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 414 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 6, 415 | 5, 6, 6, 5, 5, 6, 5, 6, 6, 5, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 6, 5, 5, 416 | 5, 5, 5, 5, 5, 6, 5, 5, 5, 6, 5, 6, 5, 6, 6, 5, 5, 6, 5, 5, 5, 5, 2, 417 | 5, 5, 2, 2, 2, 2, 2, 2, 6, 2, 2, 5, 6, 6, 5, 5, 5, 5, 5, 6, 5, 6, 2, 418 | 2, 2, 2, 2, 2, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 6, 6, 419 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 420 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 421 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 422 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 5, 2, 2, 2, 423 | 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 424 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 425 | 5, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 426 | 2, 2, 2, 2, 5, 2, 2, 5, 5, 5, 5, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 427 | 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 428 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 5, 5, 5, 5, 5, 5, 5, 5, 429 | 2, 5, 5, 5, 5, 5, 5, 6, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 430 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 431 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 432 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 433 | 6, 5, 5, 5, 5, 5, 6, 5, 5, 6, 5, 2, 2, 2, 2, 5, 2, 6, 6, 6, 2, 2, 5, 434 | 2, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 435 | 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 436 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 437 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 438 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 439 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 440 | 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 441 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 442 | 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 5, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 443 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 444 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 445 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 446 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 447 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 448 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 449 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 5, 450 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 451 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 452 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 453 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 454 | 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 455 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 456 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 457 | 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 5, 5, 5, 6, 6, 5, 458 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 459 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 5, 5, 5, 6, 460 | 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 461 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 5, 462 | 5, 5, 6, 5, 6, 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 463 | 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 464 | 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 465 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 5, 5, 5, 6, 6, 5, 466 | 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 467 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 468 | 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 469 | 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 470 | 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 471 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 472 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 473 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 474 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 475 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 476 | 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 477 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 478 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 479 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 480 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 481 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 482 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 483 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 484 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 485 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 486 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 487 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 488 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 489 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 490 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 491 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 492 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 493 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 494 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 495 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 496 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 497 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 498 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 499 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 500 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 501 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 502 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 503 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 504 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 505 | 5, 2, 2, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 506 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 507 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 508 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 509 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 510 | 6, 5, 5, 5, 5, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 511 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 5, 5, 6, 6, 6, 6, 512 | 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 513 | 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 514 | 5, 5, 5, 5, 6, 5, 5, 5, 6, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 515 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 516 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 517 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 518 | 5, 2, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 2, 519 | 5, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 520 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 7, 2, 2, 2, 521 | 2, 7, 7, 7, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 5, 522 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 523 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 524 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 525 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 526 | 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 527 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 6, 528 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 529 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 530 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 531 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 10, 5, 10, 5, 10, 5, 532 | 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 533 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 534 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 535 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 536 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 537 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 538 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 539 | 5, 10, 5, 10, 5, 59, 60, 61, 62, 63, 64, 6, 6, 6, 6, 10, 5, 10, 5, 540 | 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 541 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 542 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 543 | 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 5, 10, 544 | 5, 10, 5, 10, 5, 10, 5, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 65, 545 | 65, 65, 65, 65, 65, 65, 65, 5, 5, 5, 5, 5, 5, 6, 6, 65, 65, 65, 65, 546 | 65, 65, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 65, 65, 65, 65, 65, 65, 65, 65, 547 | 5, 5, 5, 5, 5, 5, 5, 5, 65, 65, 65, 65, 65, 65, 65, 65, 5, 5, 5, 5, 548 | 5, 5, 6, 6, 65, 65, 65, 65, 65, 65, 6, 6, 66, 5, 67, 5, 68, 5, 69, 549 | 5, 6, 65, 6, 65, 6, 65, 6, 65, 5, 5, 5, 5, 5, 5, 5, 5, 65, 65, 65, 550 | 65, 65, 65, 65, 65, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 551 | 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 552 | 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 553 | 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 554 | 117, 5, 5, 118, 119, 120, 6, 121, 122, 65, 65, 123, 123, 124, 2, 125, 555 | 2, 2, 2, 126, 127, 128, 6, 129, 130, 131, 131, 131, 131, 132, 2, 2, 556 | 2, 5, 5, 133, 134, 6, 6, 135, 136, 65, 65, 137, 137, 6, 2, 2, 2, 5, 557 | 5, 138, 139, 140, 5, 141, 142, 65, 65, 143, 143, 144, 2, 2, 2, 6, 6, 558 | 145, 146, 147, 6, 148, 149, 150, 150, 151, 151, 152, 2, 2, 6, 6, 6, 559 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 153, 153, 153, 154, 58, 2, 2, 2, 2, 2, 2, 560 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 561 | 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 562 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 2, 6, 6, 6, 563 | 6, 6, 6, 6, 6, 153, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 564 | 5, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 2, 2, 2, 565 | 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 566 | 6, 2, 2, 2, 2, 2, 2, 2, 2, 155, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 567 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 568 | 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 569 | 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 570 | 6, 6, 6, 6, 6, 6, 2, 2, 156, 157, 2, 2, 2, 158, 2, 159, 5, 160, 161, 571 | 162, 5, 5, 163, 164, 165, 5, 2, 163, 166, 2, 2, 167, 167, 167, 168, 572 | 169, 2, 2, 170, 171, 172, 2, 168, 2, 173, 2, 174, 2, 175, 176, 177, 573 | 177, 2, 5, 178, 178, 2, 179, 5, 5, 5, 5, 5, 5, 2, 6, 6, 5, 180, 181, 574 | 2, 2, 2, 2, 2, 182, 5, 5, 5, 5, 2, 2, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 575 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 183, 183, 183, 183, 183, 183, 183, 183, 576 | 183, 183, 183, 183, 183, 183, 183, 183, 5, 5, 5, 5, 5, 5, 5, 5, 5, 577 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 578 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 579 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 580 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 581 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 582 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 583 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 584 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 585 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 586 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 587 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 588 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 589 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 590 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 591 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 592 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 593 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 594 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 595 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 596 | 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 597 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 598 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 599 | 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 600 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 601 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 602 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 603 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 604 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 605 | 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 606 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 607 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 608 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 609 | 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 610 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 611 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 612 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 613 | 5, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 614 | 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 5, 615 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 616 | 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 617 | 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 618 | 2, 2, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 619 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 620 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 621 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 622 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 623 | 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 624 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 625 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 626 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 627 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 628 | 6, 6, 6, 6, 6, 2, 2, 2, 2, 6, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 2, 2, 629 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2, 630 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 631 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2, 6, 2, 2, 2, 2, 6, 6, 6, 2, 6, 632 | 2, 2, 2, 2, 2, 2, 2, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 633 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 634 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 2, 2, 2, 2, 2, 635 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2, 2, 2, 636 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 637 | 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 638 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 639 | 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 640 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 641 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 642 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 643 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 644 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 645 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 646 | 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 647 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 648 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 649 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 650 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 651 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 652 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 653 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 654 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 655 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 656 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 657 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 658 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 659 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 660 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 661 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 5, 5, 5, 2, 2, 662 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 663 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 2, 2, 664 | 5, 5, 5, 5, 5, 2, 2, 2, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 665 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 666 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 667 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 668 | 5, 5, 5, 6, 6, 2, 2, 2, 2, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 669 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 670 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 671 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 672 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 6, 6, 6, 6, 6, 5, 5, 673 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 674 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 5, 5, 5, 5, 675 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 676 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 677 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 678 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 679 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 680 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 681 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 682 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 683 | 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 684 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 685 | 5, 5, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 686 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 687 | 6, 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 688 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 689 | 5, 5, 5, 5, 5, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 690 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 691 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 692 | 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 5, 5, 5, 693 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 694 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 695 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 696 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 697 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 698 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 699 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 185, 5, 700 | 186, 5, 187, 5, 6, 6, 6, 6, 5, 5, 5, 5, 5, 188, 189, 190, 191, 192, 701 | 193, 194, 195, 5, 5, 196, 197, 198, 5, 5, 5, 199, 200, 201, 202, 203, 702 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 204, 205, 703 | 206, 207, 5, 5, 5, 5, 5, 5, 5, 208, 209, 210, 211, 212, 213, 214, 215, 704 | 216, 217, 218, 219, 220, 221, 5, 222, 5, 5, 5, 223, 224, 225, 5, 226, 705 | 5, 227, 228, 5, 5, 5, 5, 5, 5, 5, 5, 229, 5, 230, 231, 5, 232, 233, 706 | 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 707 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 708 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 709 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 710 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 711 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 712 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 713 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 714 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 715 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 716 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 717 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 718 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 719 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 720 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 721 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 722 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 723 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 724 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 725 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 726 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 727 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 728 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 729 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 730 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 731 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 732 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 733 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 734 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 735 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 736 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 737 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 738 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 739 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 740 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 741 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 742 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 743 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 744 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 745 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 746 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 747 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 748 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 749 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 750 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 751 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 752 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 753 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 754 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 755 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 756 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 757 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 758 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 759 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 760 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 761 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 762 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 763 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 764 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 765 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 766 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 767 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 768 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 769 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 770 | 154, 154, 154, 154, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 771 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 772 | 5, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 773 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 774 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 775 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 776 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 777 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 778 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 779 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 780 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 781 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 234, 235, 236, 237, 238, 239, 240, 6, 782 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 241, 242, 243, 244, 245, 6, 6, 6, 783 | 6, 6, 57, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, 57, 57, 57, 784 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 6, 57, 57, 57, 57, 57, 6, 57, 785 | 6, 57, 57, 6, 57, 57, 6, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 786 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 787 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 788 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 789 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 790 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 791 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 6, 6, 6, 6, 6, 6, 6, 792 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 793 | 6, 6, 6, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 794 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 795 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 796 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 797 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 798 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 799 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 800 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 801 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 802 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 803 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 804 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 805 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 806 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 807 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 808 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 809 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 810 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 811 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 812 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 813 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 814 | 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 815 | 6, 6, 6, 6, 6, 6, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 816 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 817 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 818 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 819 | 6, 6, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 820 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 821 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 822 | 57, 57, 57, 57, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 823 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 824 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 6, 6, 6, 7, 7, 825 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 826 | 6, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 827 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 828 | 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 829 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2, 2, 2, 2, 6, 6, 6, 6, 57, 57, 57, 57, 830 | 57, 6, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 831 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 832 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 833 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 834 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 835 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 836 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 837 | 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 838 | 57, 6, 6, 153, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 839 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 840 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 2, 841 | 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 842 | 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 843 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 844 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 845 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 846 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 5, 5, 5, 5, 5, 5, 6, 6, 847 | 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 5, 6, 6, 6, 2, 848 | 2, 2, 2, 2, 2, 2, 6, 2, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 6, 6, 6, 849 | 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 850 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, 6, 851 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 852 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 853 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 854 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 855 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 856 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 857 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 858 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 859 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 860 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 246, 246, 246, 246, 246, 246, 246, 861 | 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 862 | 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 863 | 246, 246, 246, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 864 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 865 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 866 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 867 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 868 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 869 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 870 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 871 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 872 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 873 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 874 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 875 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 876 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 877 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 878 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 879 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 880 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 881 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 882 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 883 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 884 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 885 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 886 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 887 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 888 | 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 889 | 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 2, 2, 2, 2, 2, 2, 2, 5, 5, 890 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 891 | 5, 5, 5, 5, 5, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 892 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 893 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 894 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 247, 895 | 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 896 | 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 247, 5, 5, 5, 5, 897 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 248, 898 | 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 899 | 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 5, 5, 5, 5, 900 | 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 249, 901 | 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 902 | 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 249, 5, 5, 5, 5, 903 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 250, 904 | 6, 250, 250, 6, 6, 250, 6, 6, 250, 250, 6, 6, 250, 250, 250, 250, 6, 905 | 250, 250, 250, 250, 250, 250, 250, 250, 5, 5, 5, 5, 6, 5, 6, 5, 5, 906 | 5, 5, 6, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 251, 251, 251, 251, 907 | 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 251, 908 | 251, 251, 251, 251, 251, 251, 251, 251, 5, 5, 5, 5, 5, 5, 5, 5, 5, 909 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 252, 252, 6, 252, 910 | 252, 252, 252, 6, 6, 252, 252, 252, 252, 252, 252, 252, 252, 6, 252, 911 | 252, 252, 252, 252, 252, 252, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 912 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 253, 253, 6, 253, 253, 253, 913 | 253, 6, 253, 253, 253, 253, 253, 6, 253, 6, 6, 6, 253, 253, 253, 253, 914 | 253, 253, 253, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 915 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 254, 254, 254, 254, 254, 254, 254, 254, 916 | 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 917 | 254, 254, 254, 254, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 918 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 255, 255, 255, 255, 255, 255, 255, 255, 919 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 920 | 255, 255, 255, 255, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 921 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 256, 256, 256, 256, 256, 256, 256, 256, 922 | 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 923 | 256, 256, 256, 256, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 924 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 257, 257, 257, 257, 257, 257, 257, 257, 925 | 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 257, 926 | 257, 257, 257, 257, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 927 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 258, 258, 258, 258, 258, 258, 258, 258, 928 | 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 258, 929 | 258, 258, 258, 258, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 930 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 259, 259, 259, 259, 259, 259, 259, 259, 931 | 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 259, 932 | 259, 259, 259, 259, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 933 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 260, 260, 260, 260, 260, 934 | 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 260, 261, 260, 935 | 260, 260, 260, 260, 260, 260, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 936 | 5, 5, 5, 5, 5, 262, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 263, 937 | 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 263, 938 | 263, 263, 264, 263, 263, 263, 263, 263, 263, 263, 5, 5, 5, 5, 5, 5, 939 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 265, 5, 5, 5, 5, 5, 5, 5, 5, 5, 940 | 5, 5, 5, 5, 5, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 266, 941 | 266, 266, 266, 266, 266, 266, 267, 266, 266, 266, 266, 266, 266, 266, 942 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 268, 5, 5, 5, 943 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 269, 269, 269, 269, 269, 269, 269, 944 | 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 270, 269, 269, 269, 945 | 269, 269, 269, 269, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 946 | 5, 5, 271, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 272, 272, 272, 947 | 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 948 | 273, 272, 272, 272, 272, 272, 272, 272, 5, 5, 5, 5, 5, 5, 5, 5, 5, 949 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 274, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 950 | 5, 5, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 951 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 952 | 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 953 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 954 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 955 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 956 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 957 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 958 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 959 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 960 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 961 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 962 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 963 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 964 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 965 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 966 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 967 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 968 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 969 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 970 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 971 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 972 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 973 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 974 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 154, 154, 154, 154, 975 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 976 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 977 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 978 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 979 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 980 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 981 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 982 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 983 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 984 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 985 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 986 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 987 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 988 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 989 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 990 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 991 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 992 | 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, 6, 6 993 | }; 994 | 995 | /* 996 | * Each group represents a unique set of character attributes. The attributes 997 | * are encoded into a 32-bit value as follows: 998 | * 999 | * Bit 0 A.1 | C.1.2 | C.2.2 | C.3 -- C.9 1000 | * 1001 | * Bit 1 C.1.1 1002 | * 1003 | * Bit 2 C.2.1 1004 | * 1005 | * Bit 3 B.1 1006 | * 1007 | * Bit 4 D.1 1008 | * 1009 | * Bit 5 D.2 1010 | * 1011 | * Bit 6 XNP 1012 | * 1013 | * Bit 7 Case maps to several characters 1014 | * 1015 | * Bits 8-10 Reserved for future use. 1016 | * 1017 | * Bits 11-31 Case delta: delta for case conversions. This should be the 1018 | * highest field so we can easily sign extend. 1019 | */ 1020 | 1021 | static int groups[] = { 1022 | 4, 2, 0, 64, 65568, 32, 1, 8, 1587232, 160, 2080, 2208, 4256, 1023 | -247776, -548832, 430112, 421920, 419872, 161824, 413728, 415776, 1024 | 423968, 432160, 428064, 436256, 438304, 446496, 444448, 448544, 1025 | 4128, 6304, -198624, -114656, -266208, 237568, 8352, 77856, 75808, 1026 | 131104, 129056, 10400, 12448, -61408, -51168, -26592, -12256, 1027 | -18400, -30688, -45024, -110560, -98272, -96224, -122848, -131040, 1028 | 163872, 98336, 14496, 16, 17, 16544, 18592, 20640, 22688, 24736, 1029 | -118752, -16352, 26784, 28832, 30880, 32928, 34976, 37024, 39072, 1030 | 41120, 43168, 45216, 47264, 49312, 51360, 53408, 55456, 57504, 1031 | 59552, 61600, 63648, 65696, 67744, 69792, 71840, 73888, 75936, 1032 | 77984, 80032, 82080, 84128, 86176, 88224, 90272, 92320, 94368, 1033 | 96416, 98464, 100512, 102560, 104608, 106656, 108704, 110752, 1034 | 112800, 114848, 116896, 118944, 120992, 123040, 125088, 127136, 1035 | 129184, 131232, 133280, 135328, 137376, 139424, 141472, -151520, 1036 | 143520, -14690272, 145568, 147616, 149664, 151712, 153760, -176096, 1037 | 155808, 157856, 159904, 161952, 164000, -204768, 166048, 168096, 1038 | 170144, 172192, 174240, -229344, -14304, 176288, 178336, 180384, 1039 | 182432, 184480, -262112, -258016, 186528, 9, 33, 188544, -17102816, 1040 | 190592, -16080864, 192640, -17111008, -17113056, -17115104, -17119200, 1041 | -17121248, -17117152, 194688, -17123296, -17125344, -17127392, 1042 | 196736, 198784, 200832, -15394784, -17133536, -17168352, -16920544, 1043 | -17190880, -17192928, -17182688, -15488992, -15464416, -17237984, 1044 | 32800, 53280, 202912, 204960, 207008, 209056, 211104, 213152, 1045 | 215200, 217248, 219296, 221344, 223392, 225440, 227488, 229536, 1046 | 231584, 233632, 235680, 237728, 239776, 241824, 243872, 245920, 1047 | 247968, 250016, 252064, 254112, 256160, 258208, 260256, 262304, 1048 | 264352, 266400, 268448, 270496, 272544, 274592, 276640, 278688, 1049 | 280736, 282784, 284832, 286880, 288928, 290976, 293024, 295072, 1050 | 297120, 299168, 301216, 303264, 305312, 307360, 309408, 311456, 1051 | 313504, 315552, 317600, 319648, 321696, 323744, 325792, 81952, 1052 | -245168096, -245274592, -245381088, -245487584, -245594080, -245700576, 1053 | -245807072, -245913568, -246020064, -246126560, -246233056, -246339552, 1054 | -246446048, -244824032, -244844512, -244875232, -244942816, -244963296, 1055 | -244994016, -245061600, -245082080, -245112800, -245180384, -245200864, 1056 | -245231584, -245299168, -245319648, -245350368 1057 | }; 1058 | 1059 | /* 1060 | * Table for characters that lowercased to multiple ones 1061 | */ 1062 | 1063 | static int multiCaseTable[][4] = { 1064 | {2, 115, 115}, 1065 | {2, 105, 775}, 1066 | {2, 700, 110}, 1067 | {2, 106, 780}, 1068 | {2, 32, 953}, 1069 | {3, 953, 776, 769}, 1070 | {3, 965, 776, 769}, 1071 | {2, 1381, 1410}, 1072 | {2, 104, 817}, 1073 | {2, 116, 776}, 1074 | {2, 119, 778}, 1075 | {2, 121, 778}, 1076 | {2, 97, 702}, 1077 | {2, 965, 787}, 1078 | {3, 965, 787, 768}, 1079 | {3, 965, 787, 769}, 1080 | {3, 965, 787, 834}, 1081 | {2, 7936, 953}, 1082 | {2, 7937, 953}, 1083 | {2, 7938, 953}, 1084 | {2, 7939, 953}, 1085 | {2, 7940, 953}, 1086 | {2, 7941, 953}, 1087 | {2, 7942, 953}, 1088 | {2, 7943, 953}, 1089 | {2, 7936, 953}, 1090 | {2, 7937, 953}, 1091 | {2, 7938, 953}, 1092 | {2, 7939, 953}, 1093 | {2, 7940, 953}, 1094 | {2, 7941, 953}, 1095 | {2, 7942, 953}, 1096 | {2, 7943, 953}, 1097 | {2, 7968, 953}, 1098 | {2, 7969, 953}, 1099 | {2, 7970, 953}, 1100 | {2, 7971, 953}, 1101 | {2, 7972, 953}, 1102 | {2, 7973, 953}, 1103 | {2, 7974, 953}, 1104 | {2, 7975, 953}, 1105 | {2, 7968, 953}, 1106 | {2, 7969, 953}, 1107 | {2, 7970, 953}, 1108 | {2, 7971, 953}, 1109 | {2, 7972, 953}, 1110 | {2, 7973, 953}, 1111 | {2, 7974, 953}, 1112 | {2, 7975, 953}, 1113 | {2, 8032, 953}, 1114 | {2, 8033, 953}, 1115 | {2, 8034, 953}, 1116 | {2, 8035, 953}, 1117 | {2, 8036, 953}, 1118 | {2, 8037, 953}, 1119 | {2, 8038, 953}, 1120 | {2, 8039, 953}, 1121 | {2, 8032, 953}, 1122 | {2, 8033, 953}, 1123 | {2, 8034, 953}, 1124 | {2, 8035, 953}, 1125 | {2, 8036, 953}, 1126 | {2, 8037, 953}, 1127 | {2, 8038, 953}, 1128 | {2, 8039, 953}, 1129 | {2, 8048, 953}, 1130 | {2, 945, 953}, 1131 | {2, 940, 953}, 1132 | {2, 945, 834}, 1133 | {3, 945, 834, 953}, 1134 | {2, 945, 953}, 1135 | {2, 8052, 953}, 1136 | {2, 951, 953}, 1137 | {2, 942, 953}, 1138 | {2, 951, 834}, 1139 | {3, 951, 834, 953}, 1140 | {2, 951, 953}, 1141 | {3, 953, 776, 768}, 1142 | {3, 953, 776, 769}, 1143 | {2, 953, 834}, 1144 | {3, 953, 776, 834}, 1145 | {3, 965, 776, 768}, 1146 | {3, 965, 776, 769}, 1147 | {2, 961, 787}, 1148 | {2, 965, 834}, 1149 | {3, 965, 776, 834}, 1150 | {2, 8060, 953}, 1151 | {2, 969, 953}, 1152 | {2, 974, 953}, 1153 | {2, 969, 834}, 1154 | {3, 969, 834, 953}, 1155 | {2, 969, 953}, 1156 | {2, 114, 115}, 1157 | {2, 176, 99}, 1158 | {2, 176, 102}, 1159 | {2, 110, 111}, 1160 | {2, 115, 109}, 1161 | {3, 116, 101, 108}, 1162 | {2, 116, 109}, 1163 | {3, 104, 112, 97}, 1164 | {2, 97, 117}, 1165 | {2, 111, 118}, 1166 | {2, 112, 97}, 1167 | {2, 110, 97}, 1168 | {2, 956, 97}, 1169 | {2, 109, 97}, 1170 | {2, 107, 97}, 1171 | {2, 107, 98}, 1172 | {2, 109, 98}, 1173 | {2, 103, 98}, 1174 | {2, 112, 102}, 1175 | {2, 110, 102}, 1176 | {2, 956, 102}, 1177 | {2, 104, 122}, 1178 | {3, 107, 104, 122}, 1179 | {3, 109, 104, 122}, 1180 | {3, 103, 104, 122}, 1181 | {3, 116, 104, 122}, 1182 | {2, 112, 97}, 1183 | {3, 107, 112, 97}, 1184 | {3, 109, 112, 97}, 1185 | {3, 103, 112, 97}, 1186 | {2, 112, 118}, 1187 | {2, 110, 118}, 1188 | {2, 956, 118}, 1189 | {2, 109, 118}, 1190 | {2, 107, 118}, 1191 | {2, 109, 118}, 1192 | {2, 112, 119}, 1193 | {2, 110, 119}, 1194 | {2, 956, 119}, 1195 | {2, 109, 119}, 1196 | {2, 107, 119}, 1197 | {2, 109, 119}, 1198 | {2, 107, 969}, 1199 | {2, 109, 969}, 1200 | {2, 98, 113}, 1201 | {3, 99, 111, 46}, 1202 | {2, 100, 98}, 1203 | {2, 103, 121}, 1204 | {2, 104, 112}, 1205 | {2, 107, 107}, 1206 | {2, 107, 109}, 1207 | {2, 112, 104}, 1208 | {3, 112, 112, 109}, 1209 | {2, 112, 114}, 1210 | {2, 115, 118}, 1211 | {2, 119, 98}, 1212 | {2, 102, 102}, 1213 | {2, 102, 105}, 1214 | {2, 102, 108}, 1215 | {3, 102, 102, 105}, 1216 | {3, 102, 102, 108}, 1217 | {2, 115, 116}, 1218 | {2, 115, 116}, 1219 | {2, 1396, 1398}, 1220 | {2, 1396, 1381}, 1221 | {2, 1396, 1387}, 1222 | {2, 1406, 1398}, 1223 | {2, 1396, 1389} 1224 | }; 1225 | 1226 | /* 1227 | * The following constants are used to determine the category of a 1228 | * Unicode character. 1229 | */ 1230 | 1231 | #define ACMask (1 << 0) 1232 | #define C11Mask (1 << 1) 1233 | #define C21Mask (1 << 2) 1234 | #define B1Mask (1 << 3) 1235 | #define D1Mask (1 << 4) 1236 | #define D2Mask (1 << 5) 1237 | #define XNPMask (1 << 6) 1238 | #define MCMask (1 << 7) 1239 | 1240 | /* 1241 | * The following macros extract the fields of the character info. The 1242 | * GetDelta() macro is complicated because we can't rely on the C compiler 1243 | * to do sign extension on right shifts. 1244 | */ 1245 | 1246 | #define GetCaseType(info) (((info) & 0xE0) >> 5) 1247 | #define GetCategory(info) ((info) & 0x1F) 1248 | #define GetDelta(info) (((info) > 0) ? ((info) >> 11) : (~(~((info)) >> 11))) 1249 | #define GetMC(info) (multiCaseTable[GetDelta(info)]) 1250 | 1251 | /* 1252 | * This macro extracts the information about a character from the 1253 | * Unicode character tables. 1254 | */ 1255 | 1256 | #define GetUniCharInfo(ch) (groups[groupMap[(pageMap[(((int)(ch)) & 0x1fffff) >> OFFSET_BITS] << OFFSET_BITS) | ((ch) & ((1 << OFFSET_BITS)-1))]]) 1257 | 1258 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # -*- Autoconf -*- 2 | # Process this file with autoconf to produce a configure script. 3 | 4 | AC_PREREQ(2.53) 5 | AC_PACKAGE_VERSION(0.1.0) 6 | AC_INIT(stringprep, 0.1.0, [], []) 7 | 8 | # Checks for programs. 9 | AC_PROG_CC 10 | AC_PROG_MAKE_SET 11 | 12 | if test "x$GCC" = "xyes"; then 13 | CFLAGS="$CFLAGS -Wall" 14 | fi 15 | 16 | # Checks for typedefs, structures, and compiler characteristics. 17 | AC_C_CONST 18 | 19 | # Checks for library functions. 20 | AC_FUNC_MALLOC 21 | AC_HEADER_STDC 22 | 23 | # Checks Erlang runtime and compiler 24 | AC_ERLANG_NEED_ERL 25 | AC_ERLANG_NEED_ERLC 26 | 27 | # Checks and sets ERLANG_ROOT_DIR and ERLANG_LIB_DIR variable 28 | # AC_ERLANG_SUBST_ROOT_DIR 29 | # AC_ERLANG_SUBST_LIB_DIR 30 | 31 | AC_ARG_ENABLE(gcov, 32 | [AC_HELP_STRING([--enable-gcov], [compile with gcov enabled (default: no)])], 33 | [case "${enableval}" in 34 | yes) gcov=true ;; 35 | no) gcov=false ;; 36 | *) AC_MSG_ERROR(bad value ${enableval} for --enable-gcov) ;; 37 | esac],[gcov=false]) 38 | 39 | 40 | AC_SUBST(gcov) 41 | 42 | AC_CONFIG_FILES([vars.config]) 43 | 44 | AC_OUTPUT 45 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %%%---------------------------------------------------------------------- 2 | %%% File : rebar.config 3 | %%% Author : Evgeniy Khramtsov 4 | %%% Purpose : Rebar build script. Compliant with rebar and rebar3. 5 | %%% Created : 8 May 2013 by Evgeniy Khramtsov 6 | %%% 7 | %%% Copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. 8 | %%% 9 | %%% Licensed under the Apache License, Version 2.0 (the "License"); 10 | %%% you may not use this file except in compliance with the License. 11 | %%% You may obtain a copy of the License at 12 | %%% 13 | %%% http://www.apache.org/licenses/LICENSE-2.0 14 | %%% 15 | %%% Unless required by applicable law or agreed to in writing, software 16 | %%% distributed under the License is distributed on an "AS IS" BASIS, 17 | %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | %%% See the License for the specific language governing permissions and 19 | %%% limitations under the License. 20 | %%% 21 | %%%---------------------------------------------------------------------- 22 | 23 | {erl_opts, [debug_info]}. 24 | 25 | {deps, [{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.27"}}}]}. 26 | 27 | {port_env, [ 28 | {"ERL_LDFLAGS", " -L$ERL_EI_LIBDIR -lei"}, 29 | {"CXXFLAGS", "-O3 -Wall"}, 30 | {"(linux|solaris|freebsd|netbsd|openbsd|dragonfly|darwin|gnu)", 31 | "LDFLAGS", "$LDFLAGS -lstdc++"}]}. 32 | 33 | {port_specs, [{"priv/lib/stringprep.so", ["c_src/stringprep.cpp"]}]}. 34 | 35 | {clean_files, ["c_src/stringprep.gcda", "c_src/stringprep.gcno"]}. 36 | 37 | {cover_enabled, true}. 38 | {cover_export_enabled, true}. 39 | {coveralls_coverdata , "_build/test/cover/eunit.coverdata"}. 40 | {coveralls_service_name , "github"}. 41 | 42 | {xref_checks, [undefined_function_calls, undefined_functions, deprecated_function_calls, deprecated_functions]}. 43 | 44 | 45 | %% Local Variables: 46 | %% mode: erlang 47 | %% End: 48 | %% vim: set filetype=erlang tabstop=8: 49 | 50 | -------------------------------------------------------------------------------- /rebar.config.script: -------------------------------------------------------------------------------- 1 | %%%---------------------------------------------------------------------- 2 | %%% File : rebar.config.script 3 | %%% Author : Mickael Remond 4 | %%% Purpose : Rebar build script. Compliant with rebar and rebar3. 5 | %%% Created : 27 Nov 2015 by Mickaël Rémond 6 | %%% 7 | %%% Copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. 8 | %%% 9 | %%% Licensed under the Apache License, Version 2.0 (the "License"); 10 | %%% you may not use this file except in compliance with the License. 11 | %%% You may obtain a copy of the License at 12 | %%% 13 | %%% http://www.apache.org/licenses/LICENSE-2.0 14 | %%% 15 | %%% Unless required by applicable law or agreed to in writing, software 16 | %%% distributed under the License is distributed on an "AS IS" BASIS, 17 | %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | %%% See the License for the specific language governing permissions and 19 | %%% limitations under the License. 20 | %%% 21 | %%%---------------------------------------------------------------------- 22 | 23 | Cfg = case file:consult(filename:join([filename:dirname(SCRIPT),"vars.config"])) of 24 | {ok, Terms} -> 25 | Terms; 26 | _Err -> 27 | [] 28 | end ++ [{cxxflags, "-g -O3 -Wall"}, {ldflags, "-lstdc++"}, {with_gcov, "false"}], 29 | {cxxflags, CfgCXXFlags} = lists:keyfind(cxxflags, 1, Cfg), 30 | {ldflags, CfgLDFlags} = lists:keyfind(ldflags, 1, Cfg), 31 | {with_gcov, CfgWithGCov} = lists:keyfind(with_gcov, 1, Cfg), 32 | 33 | IsRebar3 = case application:get_key(rebar, vsn) of 34 | {ok, VSN} -> 35 | [VSN1 | _] = string:tokens(VSN, "-"), 36 | [Maj|_] = string:tokens(VSN1, "."), 37 | (list_to_integer(Maj) >= 3); 38 | undefined -> 39 | lists:keymember(mix, 1, application:loaded_applications()) 40 | end, 41 | 42 | ModCfg0 = fun(F, Cfg, [Key|Tail], Op, Default) -> 43 | {OldVal,PartCfg} = case lists:keytake(Key, 1, Cfg) of 44 | {value, {_, V1}, V2} -> {V1, V2}; 45 | false -> {if Tail == [] -> Default; true -> [] end, Cfg} 46 | end, 47 | case Tail of 48 | [] -> 49 | [{Key, Op(OldVal)} | PartCfg]; 50 | _ -> 51 | [{Key, F(F, OldVal, Tail, Op, Default)} | PartCfg] 52 | end 53 | end, 54 | ModCfg = fun(Cfg, Keys, Op, Default) -> ModCfg0(ModCfg0, Cfg, Keys, Op, 55 | Default) end, 56 | 57 | ModCfgS = fun(Cfg, Keys, Val) -> ModCfg0(ModCfg0, Cfg, Keys, fun(_V) -> 58 | Val end, "") end, 59 | 60 | 61 | FilterConfig = fun(F, Cfg, [{Path, true, ModFun, Default} | Tail]) -> 62 | F(F, ModCfg0(ModCfg0, Cfg, Path, ModFun, Default), Tail); 63 | (F, Cfg, [_ | Tail]) -> 64 | F(F, Cfg, Tail); 65 | (F, Cfg, []) -> 66 | Cfg 67 | end, 68 | 69 | AppendStr = fun(Append) -> 70 | fun("") -> 71 | Append; 72 | (Val) -> 73 | Val ++ " " ++ Append 74 | end 75 | end, 76 | AppendList = fun(Append) -> 77 | fun(Val) -> 78 | Val ++ Append 79 | end 80 | end, 81 | 82 | Rebar3DepsFilter = fun(DepsList) -> 83 | lists:map(fun({DepName,_, {git,_, {tag,Version}}}) -> 84 | {DepName, Version}; 85 | (Dep) -> 86 | Dep 87 | end, DepsList) 88 | end, 89 | 90 | GlobalDepsFilter = fun(Deps) -> 91 | DepNames = lists:map(fun({DepName, _, _}) -> DepName; 92 | ({DepName, _}) -> DepName 93 | end, Deps), 94 | lists:filtermap(fun(Dep) -> 95 | case code:lib_dir(Dep) of 96 | {error, _} -> 97 | {true,"Unable to locate dep '"++atom_to_list(Dep)++"' in system deps."}; 98 | _ -> 99 | false 100 | end 101 | end, DepNames) 102 | end, 103 | 104 | GithubConfig = case {os:getenv("GITHUB_ACTIONS"), os:getenv("GITHUB_TOKEN")} of 105 | {"true", Token} when is_list(Token) -> 106 | CONFIG1 = [{coveralls_repo_token, Token}, 107 | {coveralls_service_job_id, os:getenv("GITHUB_RUN_ID")}, 108 | {coveralls_commit_sha, os:getenv("GITHUB_SHA")}, 109 | {coveralls_service_number, os:getenv("GITHUB_RUN_NUMBER")}], 110 | case os:getenv("GITHUB_EVENT_NAME") =:= "pull_request" 111 | andalso string:tokens(os:getenv("GITHUB_REF"), "/") of 112 | [_, "pull", PRNO, _] -> 113 | [{coveralls_service_pull_request, PRNO} | CONFIG1]; 114 | _ -> 115 | CONFIG1 116 | end; 117 | _ -> 118 | [] 119 | end, 120 | 121 | Rules = [ 122 | {[port_env, "CXXFLAGS"], true, 123 | AppendStr(CfgCXXFlags), "$CXXFLAGS"}, 124 | {[port_env, "LDFLAGS"], true, 125 | AppendStr(CfgLDFlags), "$LDFLAGS"}, 126 | {[post_hooks], (not IsRebar3) and (CfgWithGCov == "true"), 127 | AppendList([{eunit, "gcov -o c_src stringprep"}, 128 | {eunit, "mv *.gcov .eunit/"}]), []}, 129 | {[post_hooks], IsRebar3 and (CfgWithGCov == "true"), 130 | AppendList([{eunit, "gcov -o c_src stringprep"}, 131 | {eunit, "mv *.gcov _build/test/cover/"}]), []}, 132 | {[port_env, "LDFLAGS"], CfgWithGCov == "true", 133 | AppendStr("--coverage"), ""}, 134 | {[port_env, "CXXFLAGS"], CfgWithGCov == "true", 135 | AppendStr("--coverage"), ""}, 136 | {[deps], IsRebar3, 137 | Rebar3DepsFilter, []}, 138 | {[plugins], IsRebar3, 139 | AppendList([pc]), []}, 140 | {[provider_hooks], IsRebar3, 141 | AppendList([{pre, [ 142 | {compile, {pc, compile}}, 143 | {clean, {pc, clean}} 144 | ]}]), []}, 145 | {[plugins], os:getenv("COVERALLS") == "true", 146 | AppendList([{coveralls, {git, 147 | "https://github.com/processone/coveralls-erl.git", 148 | {branch, "addjsonfile"}}} ]), []}, 149 | {[deps], os:getenv("USE_GLOBAL_DEPS") /= false, 150 | GlobalDepsFilter, []} 151 | ], 152 | 153 | 154 | Config = FilterConfig(FilterConfig, CONFIG, Rules) ++ GithubConfig, 155 | 156 | %io:format("Rules:~n~p~n~nCONFIG:~n~p~n~nConfig:~n~p~n", [Rules, CONFIG, Config]), 157 | 158 | Config. 159 | 160 | %% Local Variables: 161 | %% mode: erlang 162 | %% End: 163 | %% vim: set filetype=erlang tabstop=8: 164 | -------------------------------------------------------------------------------- /src/stringprep.app.src: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %%% Author : Evgeniy Khramtsov 3 | %%% Purpose : Application package description 4 | %%% Created : 4 Apr 2013 by Evgeniy Khramtsov 5 | %%% 6 | %%% 7 | %%% Copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. 8 | %%% 9 | %%% Licensed under the Apache License, Version 2.0 (the "License"); 10 | %%% you may not use this file except in compliance with the License. 11 | %%% You may obtain a copy of the License at 12 | %%% 13 | %%% http://www.apache.org/licenses/LICENSE-2.0 14 | %%% 15 | %%% Unless required by applicable law or agreed to in writing, software 16 | %%% distributed under the License is distributed on an "AS IS" BASIS, 17 | %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | %%% See the License for the specific language governing permissions and 19 | %%% limitations under the License. 20 | %%% 21 | %%%------------------------------------------------------------------- 22 | 23 | {application, stringprep, 24 | [{description, "Fast Stringprep Erlang / Elixir implementation"}, 25 | {vsn, "1.0.32"}, 26 | {modules, []}, 27 | {registered, []}, 28 | {applications, [kernel, stdlib, p1_utils]}, 29 | 30 | %% hex.pm packaging: 31 | {files, ["src/", "c_src/stringprep.cpp", "c_src/uni_data.c", "c_src/uni_norm.c", "configure", "rebar.config", "rebar.config.script", "vars.config.in", "README.md", "LICENSE.txt", "LICENCE.ALL", "LICENSE.TCL"]}, 32 | {licenses, ["Apache 2.0", "Tcl/Tk"]}, 33 | {links, [{"Github", "https://github.com/processone/stringprep"}]}]}. 34 | 35 | %% Local Variables: 36 | %% mode: erlang 37 | %% End: 38 | %% vim: set filetype=erlang tabstop=8: 39 | -------------------------------------------------------------------------------- /src/stringprep.erl: -------------------------------------------------------------------------------- 1 | %%%---------------------------------------------------------------------- 2 | %%% File : stringprep.erl 3 | %%% Author : Alexey Shchepin 4 | %%% Purpose : Interface to stringprep 5 | %%% Created : 16 Feb 2003 by Alexey Shchepin 6 | %%% 7 | %%% 8 | %%% Copyright (C) 2002-2025 ProcessOne, SARL. All Rights Reserved. 9 | %%% 10 | %%% Licensed under the Apache License, Version 2.0 (the "License"); 11 | %%% you may not use this file except in compliance with the License. 12 | %%% You may obtain a copy of the License at 13 | %%% 14 | %%% http://www.apache.org/licenses/LICENSE-2.0 15 | %%% 16 | %%% Unless required by applicable law or agreed to in writing, software 17 | %%% distributed under the License is distributed on an "AS IS" BASIS, 18 | %%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19 | %%% See the License for the specific language governing permissions and 20 | %%% limitations under the License. 21 | %%% 22 | %%%---------------------------------------------------------------------- 23 | 24 | -module(stringprep). 25 | -on_load(load_nif/0). 26 | -nifs([tolower/1, tolower_nofilter/1, nameprep/1, nodeprep/1, resourceprep/1]). 27 | 28 | -author('alexey@process-one.net'). 29 | 30 | -export([start/0, tolower/1, nameprep/1, 31 | nodeprep/1, resourceprep/1, tolower_nofilter/1]). 32 | 33 | %%%=================================================================== 34 | %%% API functions 35 | %%%=================================================================== 36 | start() -> 37 | case application:ensure_all_started(stringprep) of 38 | {ok, _} -> ok; 39 | Er -> Er 40 | end. 41 | 42 | load_nif() -> 43 | SOPath = p1_nif_utils:get_so_path(?MODULE, [stringprep], "stringprep"), 44 | case catch erlang:load_nif(SOPath, 0) of 45 | ok -> ok; 46 | {error, {reload, _}} -> ok; 47 | Err -> error_logger:warning_msg("unable to load stringprep NIF: ~p~n", [Err]), 48 | {error, unable_to_load_nif} 49 | end. 50 | 51 | -spec tolower(iodata()) -> binary() | error. 52 | tolower(_String) -> 53 | erlang:nif_error(nif_not_loaded). 54 | 55 | -spec tolower_nofilter(iodata()) -> binary() | error. 56 | tolower_nofilter(_String) -> 57 | erlang:nif_error(nif_not_loaded). 58 | 59 | -spec nameprep(iodata()) -> binary() | error. 60 | nameprep(_String) -> 61 | erlang:nif_error(nif_not_loaded). 62 | 63 | -spec nodeprep(iodata()) -> binary() | error. 64 | nodeprep(_String) -> 65 | erlang:nif_error(nif_not_loaded). 66 | 67 | -spec resourceprep(iodata()) -> binary() | error. 68 | resourceprep(_String) -> 69 | erlang:nif_error(nif_not_loaded). 70 | -------------------------------------------------------------------------------- /test/tests.erl: -------------------------------------------------------------------------------- 1 | -module(tests). 2 | 3 | -include_lib("eunit/include/eunit.hrl"). 4 | -compile(export_all). 5 | 6 | application_start_test_() -> 7 | [ ?_assertMatch({ok, _}, 8 | application:ensure_all_started(stringprep)), 9 | ?_assertEqual(ok, stringprep:start()) ]. 10 | 11 | application_start_stop_start_test_() -> 12 | [ ?_assertEqual({ok, []}, 13 | application:ensure_all_started(stringprep)), 14 | ?_assertEqual(ok, application:stop(stringprep)), 15 | ?_assertEqual(ok, stringprep:start()) ]. 16 | 17 | badarg_test() -> 18 | ?assertError(badarg, stringprep:nodeprep(foo)), 19 | ?assertError(badarg, stringprep:nameprep(123)), 20 | ?assertError(badarg, stringprep:resourceprep({foo, bar})), 21 | ?assertError(badarg, stringprep:tolower(fun() -> ok end)). 22 | 23 | empty_string_test() -> 24 | ?assertEqual(<<>>, stringprep:nodeprep(<<>>)), 25 | ?assertEqual(<<>>, stringprep:nameprep(<<>>)), 26 | ?assertEqual(<<>>, stringprep:resourceprep(<<>>)), 27 | ?assertEqual(<<>>, stringprep:tolower(<<>>)). 28 | 29 | '@_nodeprep_test'() -> 30 | ?assertEqual(error, stringprep:nodeprep(<<"@">>)). 31 | 32 | tolower_test() -> 33 | ?assertEqual(<<"abcd">>, stringprep:tolower(<<"AbCd">>)). 34 | 35 | resourceprep_test() -> 36 | ?assertEqual( 37 | <<95,194,183,194,176,226,137,136,88,46,209,130,208,189,206, 38 | 181,32,208,188,97,206,183,32,195,143,197,139,32,196,174, 39 | 209,143,207,131,206,174,32,208,188,97,115,208,186,46,88, 40 | 226,137,136,194,176,194,183,95>>, 41 | stringprep:resourceprep( 42 | <<95,194,183,194,176,226,137,136,88,46,209,130,208,189, 43 | 206,181,32,208,188,194,170,206,183,32,195,143,197,139, 44 | 32,196,174,209,143,207,131,206,174,32,208,188,194,170, 45 | 115,208,186,46,88,226,137,136,194,176,194,183,95>>)). 46 | 47 | nameprep_fail_test() -> 48 | ?assertEqual(error, 49 | stringprep:nameprep(<<217,173,65,112,107,97,119,97,217,173>>)). 50 | 51 | vector_test() -> 52 | Cases = [ 53 | {<<"foo", 16#C2, 16#AD, 16#CD, 16#8F, 16#E1, 16#A0, 16#86, 16#E1, 16#A0, 16#8B, 54 | "bar", 16#E2, 16#80, 16#8B, 16#E2, 16#81, 16#A0, "baz", 16#EF, 16#B8, 16#80, 55 | 16#EF, 16#B8, 16#88, 16#EF, 16#B8, 16#8F, 16#EF, 16#BB, 16#BF>>, 56 | <<"foobarbaz">>, 57 | <<"foobarbaz">>, 58 | <<"foobarbaz">>, 59 | <<"foobarbaz">>}, 60 | {<<"CAFE">>, 61 | <<"cafe">>, 62 | <<"cafe">>, 63 | <<"CAFE">>, 64 | <<"cafe">>}, 65 | {<<16#C3, 16#9F>>, 66 | <<"ss">>, 67 | <<"ss">>, 68 | <<16#C3, 16#9F>>, 69 | <<"ss">>}, 70 | {<<16#C4, 16#B0>>, 71 | <<"i", 16#CC, 16#87>>, 72 | <<"i", 16#CC, 16#87>>, 73 | <<16#C4, 16#B0>>, 74 | <<"i", 16#CC, 16#87>>}, 75 | {<<16#C5, 16#83, 16#CD, 16#BA>>, 76 | <<16#C5, 16#84, " ", 16#CE, 16#B9>>, 77 | error, 78 | <<16#C5, 16#83, " ", 16#CD, 16#85>>, 79 | <<16#C5, 16#84, " ", 16#CE, 16#B9>>}, 80 | {<<16#E2, 16#84, 16#A1, 16#E3, 16#8F, 16#86, 16#F0, 16#9D, 16#9E, 16#BB>>, 81 | <<"telC", 16#E2, 16#88, 16#95, "kg", 16#CF, 16#83>>, 82 | <<"telC", 16#E2, 16#88, 16#95, "kg", 16#CF, 16#83>>, 83 | <<"TELC", 16#E2, 16#88, 16#95, "kg", 16#CF, 16#82>>, 84 | <<"telC", 16#E2, 16#88, 16#95, "kg", 16#CF, 16#83>>}, 85 | {<<"j", 16#CC, 16#8C, 16#C2, 16#A0, 16#C2, 16#AA>>, 86 | <<16#C7, 16#B0, " a">>, 87 | error, 88 | <<16#C7, 16#B0, " a">>, 89 | <<16#C7, 16#B0, " a">>}, 90 | {<<16#E1, 16#BE, 16#B7>>, 91 | <<16#E1, 16#BE, 16#B6, 16#CE, 16#B9>>, 92 | <<16#E1, 16#BE, 16#B6, 16#CE, 16#B9>>, 93 | <<16#E1, 16#BE, 16#B7>>, 94 | <<16#E1, 16#BE, 16#B6, 16#CE, 16#B9>>}, 95 | {<<16#C7, 16#F0>>, 96 | error, 97 | error, 98 | error, 99 | error}, 100 | {<<16#CE, 16#90>>, 101 | <<16#CE, 16#90>>, 102 | <<16#CE, 16#90>>, 103 | <<16#CE, 16#90>>, 104 | <<16#CE, 16#90>>}, 105 | {<<16#CE, 16#B0>>, 106 | <<16#CE, 16#B0>>, 107 | <<16#CE, 16#B0>>, 108 | <<16#CE, 16#B0>>, 109 | <<16#CE, 16#B0>>}, 110 | {<<16#E1, 16#BA, 16#96>>, 111 | <<16#E1, 16#BA, 16#96>>, 112 | <<16#E1, 16#BA, 16#96>>, 113 | <<16#E1, 16#BA, 16#96>>, 114 | <<16#E1, 16#BA, 16#96>>}, 115 | {<<16#E1, 16#BD, 16#96>>, 116 | <<16#E1, 16#BD, 16#96>>, 117 | <<16#E1, 16#BD, 16#96>>, 118 | <<16#E1, 16#BD, 16#96>>, 119 | <<16#E1, 16#BD, 16#96>>}, 120 | {<<" ">>, 121 | <<" ">>, 122 | error, 123 | <<" ">>, 124 | <<" ">>}, 125 | {<<16#C2, 16#A0>>, 126 | <<" ">>, 127 | error, 128 | <<" ">>, 129 | <<" ">>}, 130 | {<<16#E1, 16#9A, 16#80>>, 131 | error, 132 | error, 133 | error, 134 | error}, 135 | {<<16#E2, 16#80, 16#80>>, 136 | <<" ">>, 137 | error, 138 | <<" ">>, 139 | <<" ">>}, 140 | {<<16#E2, 16#80, 16#8B>>, 141 | <<>>, 142 | <<>>, 143 | <<>>, 144 | <<>>}, 145 | {<<16#E3, 16#80, 16#80>>, 146 | <<" ">>, 147 | error, 148 | <<" ">>, 149 | <<" ">>}, 150 | {<<16#10, 16#7f>>, 151 | <<16#10, 16#7f>>, 152 | error, 153 | error, 154 | <<16#10, 16#7f>>}, 155 | {<<16#C2, 16#85>>, 156 | error, 157 | error, 158 | error, 159 | error}, 160 | {<<16#E1, 16#A0, 16#8E>>, 161 | error, 162 | error, 163 | error, 164 | error}, 165 | {<<16#EF, 16#BB, 16#BF>>, 166 | <<>>, 167 | <<>>, 168 | <<>>, 169 | <<>>}, 170 | {<<16#F0, 16#9D, 16#85, 16#B5>>, 171 | error, 172 | error, 173 | error, 174 | error}, 175 | {<<16#EF, 16#84, 16#A3>>, 176 | error, 177 | error, 178 | error, 179 | error}, 180 | {<<16#F3, 16#B1, 16#88, 16#B4>>, 181 | error, 182 | error, 183 | error, 184 | error}, 185 | {<<16#F4, 16#8F, 16#88, 16#B4>>, 186 | error, 187 | error, 188 | error, 189 | error}, 190 | {<<16#F2, 16#8F, 16#BF, 16#BE>>, 191 | error, 192 | error, 193 | error, 194 | error}, 195 | {<<16#F4, 16#8F, 16#BF, 16#BF>>, 196 | error, 197 | error, 198 | error, 199 | error}, 200 | {<<16#ED, 16#BD, 16#82>>, 201 | error, 202 | error, 203 | error, 204 | error}, 205 | {<<16#EF, 16#BF, 16#BD>>, 206 | error, 207 | error, 208 | error, 209 | error}, 210 | {<<16#E2, 16#BF, 16#B5>>, 211 | error, 212 | error, 213 | error, 214 | error}, 215 | {<<16#CD, 16#81>>, 216 | <<16#CC, 16#81>>, 217 | <<16#CC, 16#81>>, 218 | <<16#CC, 16#81>>, 219 | <<16#CC, 16#81>>}, 220 | {<<16#E2, 16#80, 16#8E>>, 221 | error, 222 | error, 223 | error, 224 | error}, 225 | {<<16#E2, 16#80, 16#AA>>, 226 | error, 227 | error, 228 | error, 229 | error}, 230 | {<<16#F3, 16#A0, 16#80, 16#81>>, 231 | error, 232 | error, 233 | error, 234 | error}, 235 | {<<16#F3, 16#A0, 16#81, 16#82>>, 236 | error, 237 | error, 238 | error, 239 | error}, 240 | {<<"foo", 16#D6, 16#BE, "bar">>, 241 | error, 242 | error, 243 | error, 244 | error}, 245 | {<<"foo", 16#EF, 16#B5, 16#90, "bar">>, 246 | error, 247 | error, 248 | error, 249 | error}, 250 | {<<"foo", 16#EF, 16#B9, 16#B6, "bar">>, 251 | <<"foo ", 16#D9, 16#8E, "bar">>, 252 | error, 253 | <<"foo ", 16#D9, 16#8E, "bar">>, 254 | <<"foo ", 16#D9, 16#8E, "bar">>}, 255 | {<<16#D8, 16#A7, "1">>, 256 | error, 257 | error, 258 | error, 259 | error}, 260 | {<<16#D8, 16#A7, "1", 16#D8, 16#A8>>, 261 | <<16#D8, 16#A7, "1", 16#D8, 16#A8>>, 262 | <<16#D8, 16#A7, "1", 16#D8, 16#A8>>, 263 | <<16#D8, 16#A7, "1", 16#D8, 16#A8>>, 264 | <<16#D8, 16#A7, "1", 16#D8, 16#A8>>}, 265 | {<<16#F3, 16#A0, 16#80, 16#82>>, 266 | error, 267 | error, 268 | error, 269 | error}, 270 | {<<"X", 16#C2, 16#AD, 16#C3, 16#9F, 16#C4, 16#B0, 16#E2, 16#84, 16#A1, "j", 16#CC, 16#8C, 271 | 16#C2, 16#A0, 16#C2, 16#AA, 16#CE, 16#B0, 16#E2, 16#80, 16#80>>, 272 | <<"xssi", 16#CC, 16#87, "tel", 16#C7, 16#B0, " a", 16#CE, 16#B0, " ">>, 273 | error, 274 | <<"X", 16#C3, 16#9F, 16#C4, 16#B0, "TEL", 16#C7, 16#B0, " a", 16#CE, 16#B0, " ">>, 275 | <<"xssi", 16#CC, 16#87, "tel", 16#C7, 16#B0, " a", 16#CE, 16#B0, " ">>}, 276 | {<<"X", 16#C3, 16#9F, 16#E3, 16#8C, 16#96, 16#C4, 16#B0, 16#E2, 16#84, 16#A1, 16#E2, 16#92, 277 | 16#9F, 16#E3, 16#8C, 16#80>>, 278 | <<"xss", 16#E3, 16#82, 16#AD, 16#E3, 16#83, 16#AD, 16#E3, 16#83, 16#A1, 16#E3, 16#83, 16#BC, 279 | 16#E3, 16#83, 16#88, 16#E3, 16#83, 16#AB, "i", 16#CC, 16#87, "tel(d)", 16#E3, 16#82, 16#A2, 280 | 16#E3, 16#83, 16#91, 16#E3, 16#83, 16#BC, 16#E3, 16#83, 16#88>>, 281 | <<"xss", 16#E3, 16#82, 16#AD, 16#E3, 16#83, 16#AD, 16#E3, 16#83, 16#A1, 16#E3, 16#83, 16#BC, 282 | 16#E3, 16#83, 16#88, 16#E3, 16#83, 16#AB, "i", 16#CC, 16#87, "tel(d)", 16#E3, 16#82, 16#A2, 283 | 16#E3, 16#83, 16#91, 16#E3, 16#83, 16#BC, 16#E3, 16#83, 16#88>>, 284 | <<"X", 16#C3, 16#9F, 16#E3, 16#82, 16#AD, 16#E3, 16#83, 16#AD, 16#E3, 16#83, 16#A1, 16#E3, 285 | 16#83, 16#BC, 16#E3, 16#83, 16#88, 16#E3, 16#83, 16#AB, 16#C4, 16#B0, "TEL(d)", 16#E3, 16#82, 286 | 16#A2, 16#E3, 16#83, 16#91, 16#E3, 16#83, 16#BC, 16#E3, 16#83, 16#88>>, 287 | <<"xss", 16#E3, 16#82, 16#AD, 16#E3, 16#83, 16#AD, 16#E3, 16#83, 16#A1, 16#E3, 16#83, 16#BC, 288 | 16#E3, 16#83, 16#88, 16#E3, 16#83, 16#AB, "i", 16#CC, 16#87, "tel(d)", 16#E3, 16#82, 16#A2, 289 | 16#E3, 16#83, 16#91, 16#E3, 16#83, 16#BC, 16#E3, 16#83, 16#88>>} 290 | ], 291 | lists:foreach(fun({Arg, Name, Node, Resource, Lower}) -> 292 | ?assertEqual(Name, stringprep:nameprep(Arg)), 293 | ?assertEqual(Node, stringprep:nodeprep(Arg)), 294 | ?assertEqual(Resource, stringprep:resourceprep(Arg)), 295 | ?assertEqual(Lower, stringprep:tolower(Arg)) 296 | end, Cases). 297 | 298 | application_stop_test() -> 299 | ?assertEqual(ok, application:stop(stringprep)). 300 | -------------------------------------------------------------------------------- /test/unload_test.erl: -------------------------------------------------------------------------------- 1 | -module(unload_test). 2 | -export([unload_test/0]). 3 | 4 | unload_test() -> 5 | code:delete(fast_yaml), 6 | code:purge(fast_yaml), 7 | code:load_file(fast_yaml). 8 | -------------------------------------------------------------------------------- /tools/uni_parse.tcl: -------------------------------------------------------------------------------- 1 | # uni_parse.tcl -- 2 | # 3 | # This program parses the UnicodeData file and generates the 4 | # corresponding uni_data.c file with compressed character 5 | # data tables. The input to this program should be rfc3454.txt 6 | # 7 | # Copyright (c) 1998-1999 by Scriptics Corporation. 8 | # All rights reserved. 9 | # 10 | # Modified for ejabberd by Alexey Shchepin 11 | # 12 | # RCS: @(#) $Id$ 13 | 14 | 15 | namespace eval uni { 16 | set shift 8; # number of bits of data within a page 17 | # This value can be adjusted to find the 18 | # best split to minimize table size 19 | 20 | variable pMap; # map from page to page index, each entry is 21 | # an index into the pages table, indexed by 22 | # page number 23 | variable pages; # map from page index to page info, each 24 | # entry is a list of indices into the groups 25 | # table, the list is indexed by the offset 26 | variable groups; # list of character info values, indexed by 27 | # group number, initialized with the 28 | # unassigned character group 29 | } 30 | 31 | proc uni::getValue {i} { 32 | variable casemap 33 | variable casemap2 34 | variable tablemap 35 | 36 | if {[info exists tablemap($i)]} { 37 | set tables $tablemap($i) 38 | } else { 39 | set tables {} 40 | } 41 | 42 | if {[info exists casemap2($i)]} { 43 | set multicase 1 44 | set delta $casemap2($i) 45 | } else { 46 | set multicase 0 47 | if {[info exists casemap($i)]} { 48 | set delta $casemap($i) 49 | } else { 50 | set delta 0 51 | } 52 | } 53 | 54 | if {abs($delta) > 0xFFFFF} { 55 | puts "delta must be less than 22 bits wide" 56 | exit 57 | } 58 | 59 | set ac 0 60 | set c11 0 61 | set c21 0 62 | set b1 0 63 | set d1 0 64 | set d2 0 65 | set xnp 0 66 | 67 | foreach tab $tables { 68 | switch -glob -- $tab { 69 | C.1.1 {set c11 1} 70 | C.2.1 {set c21 1} 71 | C.* {set ac 1} 72 | A.1 {set ac 1} 73 | B.1 {set b1 1} 74 | D.1 {set d1 1} 75 | D.2 {set d2 1} 76 | XNP {set xnp 1} 77 | } 78 | } 79 | 80 | set val [expr {($ac << 0) | 81 | ($c11 << 1) | 82 | ($c21 << 2) | 83 | ($b1 << 3) | 84 | ($d1 << 4) | 85 | ($d2 << 5) | 86 | ($xnp << 6) | 87 | ($multicase << 7) | 88 | ($delta << 11)}] 89 | 90 | return $val 91 | } 92 | 93 | proc uni::getGroup {value} { 94 | variable groups 95 | 96 | set gIndex [lsearch -exact $groups $value] 97 | if {$gIndex == -1} { 98 | set gIndex [llength $groups] 99 | lappend groups $value 100 | } 101 | return $gIndex 102 | } 103 | 104 | proc uni::addPage {info} { 105 | variable pMap 106 | variable pages 107 | variable pages_map 108 | 109 | if {[info exists pages_map($info)]} { 110 | lappend pMap $pages_map($info) 111 | } else { 112 | set pIndex [llength $pages] 113 | lappend pages $info 114 | set pages_map($info) $pIndex 115 | lappend pMap $pIndex 116 | } 117 | return 118 | } 119 | 120 | 121 | proc uni::load_tables {data} { 122 | variable casemap 123 | variable casemap2 124 | variable multicasemap 125 | variable tablemap 126 | 127 | set multicasemap {} 128 | set table "" 129 | 130 | foreach line [split $data \n] { 131 | if {$table == ""} { 132 | if {[regexp { ----- Start Table (.*) -----} $line temp table]} { 133 | #puts "Start table '$table'" 134 | } 135 | } else { 136 | if {[regexp { ----- End Table (.*) -----} $line temp table1]} { 137 | set table "" 138 | } else { 139 | if {$table == "B.1"} { 140 | if {[regexp {^ ([[:xdigit:]]+); ;} $line \ 141 | temp val]} { 142 | scan $val %x val 143 | if {$val <= 0x10ffff} { 144 | lappend tablemap($val) $table 145 | } 146 | } 147 | } elseif {$table == "B.2"} { 148 | if {[regexp {^ ([[:xdigit:]]+); ([[:xdigit:]]+);} $line \ 149 | temp from to]} { 150 | scan $from %x from 151 | scan $to %x to 152 | if {$from <= 0x10ffff && $to <= 0x10ffff} { 153 | set casemap($from) [expr {$to - $from}] 154 | } 155 | } elseif {[regexp {^ ([[:xdigit:]]+); ([[:xdigit:]]+) ([[:xdigit:]]+);} $line \ 156 | temp from to1 to2]} { 157 | scan $from %x from 158 | scan $to1 %x to1 159 | scan $to2 %x to2 160 | if {$from <= 0x10ffff && \ 161 | $to1 <= 0x10ffff && $to2 <= 0x10ffff} { 162 | set casemap2($from) [llength $multicasemap] 163 | lappend multicasemap [list $to1 $to2] 164 | } 165 | } elseif {[regexp {^ ([[:xdigit:]]+); ([[:xdigit:]]+) ([[:xdigit:]]+) ([[:xdigit:]]+);} $line \ 166 | temp from to1 to2 to3]} { 167 | scan $from %x from 168 | scan $to1 %x to1 169 | scan $to2 %x to2 170 | scan $to3 %x to3 171 | if {$from <= 0x10ffff && \ 172 | $to1 <= 0x10ffff && $to2 <= 0x10ffff && \ 173 | $to3 <= 0x10ffff} { 174 | set casemap2($from) [llength $multicasemap] 175 | lappend multicasemap [list $to1 $to2 $to3] 176 | } 177 | } else { 178 | #puts "missed: $line" 179 | } 180 | 181 | } elseif {$table != "B.3"} { 182 | if {[regexp {^ ([[:xdigit:]]+)-([[:xdigit:]]+)} $line \ 183 | temp from to]} { 184 | scan $from %x from 185 | scan $to %x to 186 | for {set i $from} {$i <= $to && $i <= 0x10ffff} {incr i} { 187 | lappend tablemap($i) $table 188 | } 189 | } elseif {[regexp {^ ([[:xdigit:]]+)} $line \ 190 | temp val]} { 191 | scan $val %x val 192 | if {$val <= 0x10ffff} { 193 | lappend tablemap($val) $table 194 | } 195 | } 196 | } 197 | } 198 | } 199 | } 200 | 201 | # XMPP nodeprep prohibited 202 | foreach val {22 26 27 2f 3a 3c 3e 40} { 203 | scan $val %x val 204 | lappend tablemap($val) XNP 205 | } 206 | } 207 | 208 | proc uni::buildTables {} { 209 | variable shift 210 | 211 | variable casemap 212 | variable tablemap 213 | 214 | variable pMap {} 215 | variable pages {} 216 | variable groups {} 217 | set info {} ;# temporary page info 218 | 219 | set mask [expr {(1 << $shift) - 1}] 220 | 221 | set next 0 222 | 223 | for {set i 0} {$i <= 0x10ffff} {incr i} { 224 | set gIndex [getGroup [getValue $i]] 225 | 226 | # Split character index into offset and page number 227 | set offset [expr {$i & $mask}] 228 | set page [expr {($i >> $shift)}] 229 | 230 | # Add the group index to the info for the current page 231 | lappend info $gIndex 232 | 233 | # If this is the last entry in the page, add the page 234 | if {$offset == $mask} { 235 | addPage $info 236 | set info {} 237 | } 238 | } 239 | return 240 | } 241 | 242 | proc uni::main {} { 243 | global argc argv0 argv 244 | variable pMap 245 | variable pages 246 | variable groups 247 | variable shift 248 | variable multicasemap 249 | 250 | if {$argc != 2} { 251 | puts stderr "\nusage: $argv0 \n" 252 | exit 1 253 | } 254 | set f [open [lindex $argv 0] r] 255 | set data [read $f] 256 | close $f 257 | 258 | load_tables $data 259 | buildTables 260 | puts "X = [llength $pMap] Y= [llength $pages] A= [llength $groups]" 261 | set size [expr {[llength $pMap] + [llength $pages]*(1<<$shift)}] 262 | puts "shift = $shift, space = $size" 263 | 264 | set f [open [file join [lindex $argv 1] uni_data.c] w] 265 | fconfigure $f -translation lf 266 | puts $f "/* 267 | * uni_data.c -- 268 | * 269 | * Declarations of Unicode character information tables. This file is 270 | * automatically generated by the uni_parse.tcl script. Do not 271 | * modify this file by hand. 272 | * 273 | * Copyright (c) 1998 by Scriptics Corporation. 274 | * All rights reserved. 275 | * 276 | * Modified for ejabberd by Alexey Shchepin 277 | * 278 | * RCS: @(#) \$Id\$ 279 | */ 280 | 281 | /* 282 | * A 16-bit Unicode character is split into two parts in order to index 283 | * into the following tables. The lower OFFSET_BITS comprise an offset 284 | * into a page of characters. The upper bits comprise the page number. 285 | */ 286 | 287 | #define OFFSET_BITS $shift 288 | 289 | /* 290 | * The pageMap is indexed by page number and returns an alternate page number 291 | * that identifies a unique page of characters. Many Unicode characters map 292 | * to the same alternate page number. 293 | */ 294 | 295 | static unsigned char pageMap\[\] = {" 296 | set line " " 297 | set last [expr {[llength $pMap] - 1}] 298 | for {set i 0} {$i <= $last} {incr i} { 299 | append line [lindex $pMap $i] 300 | if {$i != $last} { 301 | append line ", " 302 | } 303 | if {[string length $line] > 70} { 304 | puts $f $line 305 | set line " " 306 | } 307 | } 308 | puts $f $line 309 | puts $f "}; 310 | 311 | /* 312 | * The groupMap is indexed by combining the alternate page number with 313 | * the page offset and returns a group number that identifies a unique 314 | * set of character attributes. 315 | */ 316 | 317 | static unsigned short int groupMap\[\] = {" 318 | set line " " 319 | set lasti [expr {[llength $pages] - 1}] 320 | for {set i 0} {$i <= $lasti} {incr i} { 321 | set page [lindex $pages $i] 322 | set lastj [expr {[llength $page] - 1}] 323 | for {set j 0} {$j <= $lastj} {incr j} { 324 | append line [lindex $page $j] 325 | if {$j != $lastj || $i != $lasti} { 326 | append line ", " 327 | } 328 | if {[string length $line] > 70} { 329 | puts $f $line 330 | set line " " 331 | } 332 | } 333 | } 334 | puts $f $line 335 | puts $f "}; 336 | 337 | /* 338 | * Each group represents a unique set of character attributes. The attributes 339 | * are encoded into a 32-bit value as follows: 340 | * 341 | * Bit 0 A.1 | C.1.2 | C.2.2 | C.3 -- C.9 342 | * 343 | * Bit 1 C.1.1 344 | * 345 | * Bit 2 C.2.1 346 | * 347 | * Bit 3 B.1 348 | * 349 | * Bit 4 D.1 350 | * 351 | * Bit 5 D.2 352 | * 353 | * Bit 6 XNP 354 | * 355 | * Bit 7 Case maps to several characters 356 | * 357 | * Bits 8-10 Reserved for future use. 358 | * 359 | * Bits 11-31 Case delta: delta for case conversions. This should be the 360 | * highest field so we can easily sign extend. 361 | */ 362 | 363 | static int groups\[\] = {" 364 | set line " " 365 | set last [expr {[llength $groups] - 1}] 366 | for {set i 0} {$i <= $last} {incr i} { 367 | set val [lindex $groups $i] 368 | 369 | append line [format "%d" $val] 370 | if {$i != $last} { 371 | append line ", " 372 | } 373 | if {[string length $line] > 65} { 374 | puts $f $line 375 | set line " " 376 | } 377 | } 378 | puts $f $line 379 | puts $f "}; 380 | 381 | /* 382 | * Table for characters that lowercased to multiple ones 383 | */ 384 | 385 | static int multiCaseTable\[\]\[4\] = {" 386 | set last [expr {[llength $multicasemap] - 1}] 387 | for {set i 0} {$i <= $last} {incr i} { 388 | set val [lindex $multicasemap $i] 389 | 390 | set line " " 391 | append line [format "{%d, %s}" [llength $val] [join $val ", "]] 392 | if {$i != $last} { 393 | append line ", " 394 | } 395 | puts $f $line 396 | } 397 | puts $f "}; 398 | 399 | /* 400 | * The following constants are used to determine the category of a 401 | * Unicode character. 402 | */ 403 | 404 | #define ACMask (1 << 0) 405 | #define C11Mask (1 << 1) 406 | #define C21Mask (1 << 2) 407 | #define B1Mask (1 << 3) 408 | #define D1Mask (1 << 4) 409 | #define D2Mask (1 << 5) 410 | #define XNPMask (1 << 6) 411 | #define MCMask (1 << 7) 412 | 413 | /* 414 | * The following macros extract the fields of the character info. The 415 | * GetDelta() macro is complicated because we can't rely on the C compiler 416 | * to do sign extension on right shifts. 417 | */ 418 | 419 | #define GetCaseType(info) (((info) & 0xE0) >> 5) 420 | #define GetCategory(info) ((info) & 0x1F) 421 | #define GetDelta(info) (((info) > 0) ? ((info) >> 11) : (~(~((info)) >> 11))) 422 | #define GetMC(info) (multiCaseTable\[GetDelta(info)\]) 423 | 424 | /* 425 | * This macro extracts the information about a character from the 426 | * Unicode character tables. 427 | */ 428 | 429 | #define GetUniCharInfo(ch) (groups\[groupMap\[(pageMap\[(((int)(ch)) & 0x1fffff) >> OFFSET_BITS\] << OFFSET_BITS) | ((ch) & ((1 << OFFSET_BITS)-1))\]\]) 430 | " 431 | 432 | close $f 433 | } 434 | 435 | uni::main 436 | 437 | return 438 | -------------------------------------------------------------------------------- /tools/uni_parse2.tcl: -------------------------------------------------------------------------------- 1 | # uni_parse2.tcl -- 2 | # 3 | # This program parses the UnicodeData file and generates the 4 | # corresponding uni_norm.c file with compressed character 5 | # data tables. The input to this program should be 6 | # UnicodeData-3.2.0.txt and CompositionExclusions-3.2.0.txt files from: 7 | # ftp://ftp.unicode.org/Public/UNIDATA/ 8 | # 9 | # Copyright (c) 1998-1999 by Scriptics Corporation. 10 | # All rights reserved. 11 | # 12 | # Modified for ejabberd by Alexey Shchepin 13 | # 14 | # RCS: @(#) $Id$ 15 | 16 | 17 | namespace eval uni { 18 | set cclass_shift 8 19 | set decomp_shift 8 20 | set comp_shift 8 21 | set shift 5; # number of bits of data within a page 22 | # This value can be adjusted to find the 23 | # best split to minimize table size 24 | 25 | variable pMap; # map from page to page index, each entry is 26 | # an index into the pages table, indexed by 27 | # page number 28 | variable pages; # map from page index to page info, each 29 | # entry is a list of indices into the groups 30 | # table, the list is indexed by the offset 31 | variable groups; # list of character info values, indexed by 32 | # group number, initialized with the 33 | # unassigned character group 34 | 35 | variable categories { 36 | Cn Lu Ll Lt Lm Lo Mn Me Mc Nd Nl No Zs Zl Zp 37 | Cc Cf Co Cs Pc Pd Ps Pe Pi Pf Po Sm Sc Sk So 38 | }; # Ordered list of character categories, must 39 | # match the enumeration in the header file. 40 | 41 | variable titleCount 0; # Count of the number of title case 42 | # characters. This value is used in the 43 | # regular expression code to allocate enough 44 | # space for the title case variants. 45 | } 46 | 47 | proc uni::getValue {items index} { 48 | variable categories 49 | variable titleCount 50 | 51 | # Extract character info 52 | 53 | set category [lindex $items 2] 54 | if {[scan [lindex $items 12] %4x toupper] == 1} { 55 | set toupper [expr {$index - $toupper}] 56 | } else { 57 | set toupper {} 58 | } 59 | if {[scan [lindex $items 13] %4x tolower] == 1} { 60 | set tolower [expr {$tolower - $index}] 61 | } else { 62 | set tolower {} 63 | } 64 | if {[scan [lindex $items 14] %4x totitle] == 1} { 65 | set totitle [expr {$index - $totitle}] 66 | } else { 67 | set totitle {} 68 | } 69 | 70 | set categoryIndex [lsearch -exact $categories $category] 71 | if {$categoryIndex < 0} { 72 | puts "Unexpected character category: $index($category)" 73 | set categoryIndex 0 74 | } elseif {$category == "Lt"} { 75 | incr titleCount 76 | } 77 | 78 | return "$categoryIndex,$toupper,$tolower,$totitle" 79 | } 80 | 81 | proc uni::getGroup {value} { 82 | variable groups 83 | 84 | set gIndex [lsearch -exact $groups $value] 85 | if {$gIndex == -1} { 86 | set gIndex [llength $groups] 87 | lappend groups $value 88 | } 89 | return $gIndex 90 | } 91 | 92 | proc uni::addPage {info} { 93 | variable pMap 94 | variable pages 95 | 96 | set pIndex [lsearch -exact $pages $info] 97 | if {$pIndex == -1} { 98 | set pIndex [llength $pages] 99 | lappend pages $info 100 | } 101 | lappend pMap $pIndex 102 | return 103 | } 104 | 105 | proc uni::addPage {map_var pages_var info} { 106 | variable $map_var 107 | variable $pages_var 108 | 109 | set pIndex [lsearch -exact [set $pages_var] $info] 110 | if {$pIndex == -1} { 111 | set pIndex [llength [set $pages_var]] 112 | lappend $pages_var $info 113 | } 114 | lappend $map_var $pIndex 115 | return 116 | } 117 | 118 | proc uni::load_exclusions {data} { 119 | variable exclusions 120 | 121 | foreach line [split $data \n] { 122 | if {$line == ""} continue 123 | 124 | set items [split $line " "] 125 | 126 | if {[lindex $items 0] == "#"} continue 127 | 128 | scan [lindex $items 0] %x index 129 | 130 | set exclusions($index) "" 131 | } 132 | } 133 | 134 | proc uni::load_tables {data} { 135 | variable cclass_map 136 | variable decomp_map 137 | variable comp_map 138 | variable comp_first 139 | variable comp_second 140 | variable exclusions 141 | 142 | foreach line [split $data \n] { 143 | if {$line == ""} continue 144 | 145 | set items [split $line \;] 146 | 147 | scan [lindex $items 0] %x index 148 | set cclass [lindex $items 3] 149 | set decomp [lindex $items 5] 150 | 151 | set cclass_map($index) $cclass 152 | #set decomp_map($index) $cclass 153 | 154 | if {$decomp != ""} { 155 | if {[string index [lindex $decomp 0] 0] == "<"} { 156 | set decomp1 [lreplace $decomp 0 0] 157 | set decomp {} 158 | foreach ch $decomp1 { 159 | scan $ch %x ch 160 | lappend decomp $ch 161 | } 162 | set decomp_map($index) $decomp 163 | } else { 164 | switch -- [llength $decomp] { 165 | 1 { 166 | scan $decomp %x ch 167 | set decomp_map($index) $ch 168 | } 169 | 2 { 170 | scan $decomp "%x %x" ch1 ch2 171 | set decomp [list $ch1 $ch2] 172 | set decomp_map($index) $decomp 173 | # hackish 174 | if {(![info exists cclass_map($ch1)] || \ 175 | $cclass_map($ch1) == 0) && \ 176 | ![info exists exclusions($index)]} { 177 | if {[info exists comp_first($ch1)]} { 178 | incr comp_first($ch1) 179 | } else { 180 | set comp_first($ch1) 1 181 | } 182 | if {[info exists comp_second($ch2)]} { 183 | incr comp_second($ch2) 184 | } else { 185 | set comp_second($ch2) 1 186 | } 187 | set comp_map($decomp) $index 188 | } else { 189 | puts "Excluded $index" 190 | } 191 | } 192 | default { 193 | puts "Bad canonical decomposition: $line" 194 | } 195 | } 196 | } 197 | 198 | #puts "[format 0x%0.4x $index]\t$cclass\t$decomp_map($index)" 199 | } 200 | } 201 | #puts [array get comp_first] 202 | #puts [array get comp_second] 203 | } 204 | 205 | proc uni::buildTables {} { 206 | variable cclass_shift 207 | variable decomp_shift 208 | variable comp_shift 209 | 210 | variable cclass_map 211 | variable cclass_pmap {} 212 | variable cclass_pages {} 213 | variable decomp_map 214 | variable decomp_pmap {} 215 | variable decomp_pages {} 216 | variable decomp_list {} 217 | variable comp_map 218 | variable comp_pmap {} 219 | variable comp_pages {} 220 | variable comp_first 221 | variable comp_second 222 | variable comp_first_list {} 223 | variable comp_second_list {} 224 | variable comp_x_list {} 225 | variable comp_y_list {} 226 | variable comp_both_map {} 227 | 228 | set cclass_info {} 229 | set decomp_info {} 230 | set comp_info {} 231 | 232 | set cclass_mask [expr {(1 << $cclass_shift) - 1}] 233 | set decomp_mask [expr {(1 << $decomp_shift) - 1}] 234 | set comp_mask [expr {(1 << $comp_shift) - 1}] 235 | 236 | foreach comp [array names comp_map] { 237 | set ch1 [lindex $comp 0] 238 | if {[info exists comp_first($ch1)] && $comp_first($ch1) > 0 && \ 239 | [info exists comp_second($ch1)] && $comp_second($ch1) > 0} { 240 | if {[lsearch -exact $comp_x_list $ch1] < 0} { 241 | set i [llength $comp_x_list] 242 | lappend comp_x_list $ch1 243 | set comp_info_map($ch1) $i 244 | lappend comp_y_list $ch1 245 | set comp_info_map($ch1) $i 246 | puts "There should be no symbols which appears on" 247 | puts "both first and second place in composition" 248 | exit 249 | } 250 | } 251 | } 252 | 253 | foreach comp [array names comp_map] { 254 | set ch1 [lindex $comp 0] 255 | set ch2 [lindex $comp 1] 256 | 257 | if {$comp_first($ch1) == 1 && ![info exists comp_second($ch1)]} { 258 | set i [llength $comp_first_list] 259 | lappend comp_first_list [list $ch2 $comp_map($comp)] 260 | set comp_info_map($ch1) [expr {$i | (1 << 16)}] 261 | } elseif {$comp_second($ch2) == 1 && ![info exists comp_first($ch2)]} { 262 | set i [llength $comp_second_list] 263 | lappend comp_second_list [list $ch1 $comp_map($comp)] 264 | set comp_info_map($ch2) [expr {$i | (1 << 16) | (1 << 17)}] 265 | } else { 266 | if {[lsearch -exact $comp_x_list $ch1] < 0} { 267 | set i [llength $comp_x_list] 268 | lappend comp_x_list $ch1 269 | set comp_info_map($ch1) $i 270 | } 271 | if {[lsearch -exact $comp_y_list $ch2] < 0} { 272 | set i [llength $comp_y_list] 273 | lappend comp_y_list $ch2 274 | set comp_info_map($ch2) [expr {$i | (1 << 17)}] 275 | } 276 | } 277 | } 278 | 279 | set next 0 280 | 281 | for {set i 0} {$i <= 0x10ffff} {incr i} { 282 | #set gIndex [getGroup [getValue $i]] 283 | 284 | set cclass_offset [expr {$i & $cclass_mask}] 285 | 286 | if {[info exists cclass_map($i)]} { 287 | set cclass $cclass_map($i) 288 | } else { 289 | set cclass 0 290 | } 291 | lappend cclass_info $cclass 292 | 293 | if {$cclass_offset == $cclass_mask} { 294 | addPage cclass_pmap cclass_pages $cclass_info 295 | set cclass_info {} 296 | } 297 | 298 | 299 | set decomp_offset [expr {$i & $decomp_mask}] 300 | 301 | if {[info exists decomp_map($i)]} { 302 | set decomp $decomp_map($i) 303 | set b 1 304 | while {$b} { 305 | set b 0 306 | for {set j 0} {$j < [llength $decomp]} {incr j} { 307 | if {[info exists \ 308 | decomp_map([set ch1 [lindex $decomp $j]])]} { 309 | #puts -$decomp 310 | set decomp [eval [list lreplace $decomp $j $j] \ 311 | $decomp_map($ch1)] 312 | #puts +$decomp 313 | set b 1 314 | } 315 | } 316 | } 317 | 318 | if {[info exists decomp_used($decomp)]} { 319 | lappend decomp_info $decomp_used($decomp) 320 | } else { 321 | set val [expr {([llength $decomp] << 16) + \ 322 | [llength $decomp_list]}] 323 | #set val [expr {[llength $decomp_list]}] 324 | lappend decomp_info $val 325 | set decomp_used($decomp) $val 326 | #puts "$val $decomp" 327 | foreach d $decomp { 328 | lappend decomp_list $d 329 | } 330 | } 331 | } else { 332 | lappend decomp_info -1 333 | } 334 | 335 | if {$decomp_offset == $decomp_mask} { 336 | addPage decomp_pmap decomp_pages $decomp_info 337 | set decomp_info {} 338 | } 339 | 340 | 341 | set comp_offset [expr {$i & $comp_mask}] 342 | 343 | if {[info exists comp_info_map($i)]} { 344 | set comp $comp_info_map($i) 345 | } else { 346 | set comp -1 347 | } 348 | lappend comp_info $comp 349 | 350 | if {$comp_offset == $comp_mask} { 351 | addPage comp_pmap comp_pages $comp_info 352 | set comp_info {} 353 | } 354 | } 355 | 356 | #puts [array get decomp_map] 357 | #puts $decomp_list 358 | 359 | return 360 | } 361 | 362 | proc uni::main {} { 363 | global argc argv0 argv 364 | variable cclass_shift 365 | variable cclass_pmap 366 | variable cclass_pages 367 | variable decomp_shift 368 | variable decomp_pmap 369 | variable decomp_pages 370 | variable decomp_list 371 | variable comp_shift 372 | variable comp_map 373 | variable comp_pmap 374 | variable comp_pages 375 | variable comp_first_list 376 | variable comp_second_list 377 | variable comp_x_list 378 | variable comp_y_list 379 | variable pages 380 | variable groups {} 381 | variable titleCount 382 | 383 | if {$argc != 3} { 384 | puts stderr "\nusage: $argv0 \n" 385 | exit 1 386 | } 387 | set f [open [lindex $argv 1] r] 388 | set data [read $f] 389 | close $f 390 | 391 | load_exclusions $data 392 | 393 | set f [open [lindex $argv 0] r] 394 | set data [read $f] 395 | close $f 396 | 397 | load_tables $data 398 | buildTables 399 | #puts "X = [llength $pMap] Y= [llength $pages] A= [llength $groups]" 400 | #set size [expr {[llength $pMap] + [llength $pages]*(1<<$shift)}] 401 | #puts "shift = 6, space = $size" 402 | #puts "title case count = $titleCount" 403 | 404 | set f [open [file join [lindex $argv 2] uni_norm.c] w] 405 | fconfigure $f -translation lf 406 | puts $f "/* 407 | * uni_norm.c -- 408 | * 409 | * Declarations of Unicode character information tables. This file is 410 | * automatically generated by the uni_parse2.tcl script. Do not 411 | * modify this file by hand. 412 | * 413 | * Copyright (c) 1998 by Scriptics Corporation. 414 | * All rights reserved. 415 | * 416 | * Modified for ejabberd by Alexey Shchepin 417 | * 418 | * RCS: @(#) \$Id\$ 419 | */ 420 | 421 | /* 422 | * A 16-bit Unicode character is split into two parts in order to index 423 | * into the following tables. The lower CCLASS_OFFSET_BITS comprise an offset 424 | * into a page of characters. The upper bits comprise the page number. 425 | */ 426 | 427 | #define CCLASS_OFFSET_BITS $cclass_shift 428 | 429 | /* 430 | * The pageMap is indexed by page number and returns an alternate page number 431 | * that identifies a unique page of characters. Many Unicode characters map 432 | * to the same alternate page number. 433 | */ 434 | 435 | static unsigned char cclassPageMap\[\] = {" 436 | set line " " 437 | set last [expr {[llength $cclass_pmap] - 1}] 438 | for {set i 0} {$i <= $last} {incr i} { 439 | append line [lindex $cclass_pmap $i] 440 | if {$i != $last} { 441 | append line ", " 442 | } 443 | if {[string length $line] > 70} { 444 | puts $f $line 445 | set line " " 446 | } 447 | } 448 | puts $f $line 449 | puts $f "}; 450 | 451 | /* 452 | * The cclassGroupMap is indexed by combining the alternate page number with 453 | * the page offset and returns a combining class number. 454 | */ 455 | 456 | static unsigned char cclassGroupMap\[\] = {" 457 | set line " " 458 | set lasti [expr {[llength $cclass_pages] - 1}] 459 | for {set i 0} {$i <= $lasti} {incr i} { 460 | set page [lindex $cclass_pages $i] 461 | set lastj [expr {[llength $page] - 1}] 462 | for {set j 0} {$j <= $lastj} {incr j} { 463 | append line [lindex $page $j] 464 | if {$j != $lastj || $i != $lasti} { 465 | append line ", " 466 | } 467 | if {[string length $line] > 70} { 468 | puts $f $line 469 | set line " " 470 | } 471 | } 472 | } 473 | puts $f $line 474 | puts $f "}; 475 | 476 | #define GetUniCharCClass(ch) (cclassGroupMap\[(cclassPageMap\[(((int)(ch)) & 0x1fffff) >> CCLASS_OFFSET_BITS\] << CCLASS_OFFSET_BITS) | ((ch) & ((1 << CCLASS_OFFSET_BITS)-1))\]) 477 | 478 | 479 | #define DECOMP_OFFSET_BITS $decomp_shift 480 | 481 | /* 482 | * The pageMap is indexed by page number and returns an alternate page number 483 | * that identifies a unique page of characters. Many Unicode characters map 484 | * to the same alternate page number. 485 | */ 486 | 487 | static unsigned char decompPageMap\[\] = {" 488 | set line " " 489 | set last [expr {[llength $decomp_pmap] - 1}] 490 | for {set i 0} {$i <= $last} {incr i} { 491 | append line [lindex $decomp_pmap $i] 492 | if {$i != $last} { 493 | append line ", " 494 | } 495 | if {[string length $line] > 70} { 496 | puts $f $line 497 | set line " " 498 | } 499 | } 500 | puts $f $line 501 | puts $f "}; 502 | 503 | /* 504 | * The decompGroupMap is indexed by combining the alternate page number with 505 | * the page offset and returns a group number that identifies a length and 506 | * shift of decomposition sequence in decompList 507 | */ 508 | 509 | static int decompGroupMap\[\] = {" 510 | set line " " 511 | set lasti [expr {[llength $decomp_pages] - 1}] 512 | for {set i 0} {$i <= $lasti} {incr i} { 513 | set page [lindex $decomp_pages $i] 514 | set lastj [expr {[llength $page] - 1}] 515 | for {set j 0} {$j <= $lastj} {incr j} { 516 | append line [lindex $page $j] 517 | if {$j != $lastj || $i != $lasti} { 518 | append line ", " 519 | } 520 | if {[string length $line] > 70} { 521 | puts $f $line 522 | set line " " 523 | } 524 | } 525 | } 526 | puts $f $line 527 | puts $f "}; 528 | 529 | /* 530 | * List of decomposition sequences 531 | */ 532 | 533 | static int decompList\[\] = {" 534 | set line " " 535 | set last [expr {[llength $decomp_list] - 1}] 536 | for {set i 0} {$i <= $last} {incr i} { 537 | set val [lindex $decomp_list $i] 538 | 539 | append line [format "%d" $val] 540 | if {$i != $last} { 541 | append line ", " 542 | } 543 | if {[string length $line] > 70} { 544 | puts $f $line 545 | set line " " 546 | } 547 | } 548 | puts $f $line 549 | puts $f "}; 550 | 551 | 552 | /* 553 | * This macro extracts the information about a character from the 554 | * Unicode character tables. 555 | */ 556 | 557 | #define GetUniCharDecompInfo(ch) (decompGroupMap\[(decompPageMap\[(((int)(ch)) & 0x1fffff) >> DECOMP_OFFSET_BITS\] << DECOMP_OFFSET_BITS) | ((ch) & ((1 << DECOMP_OFFSET_BITS)-1))\]) 558 | 559 | #define GetDecompShift(info) ((info) & 0xffff) 560 | #define GetDecompLen(info) ((info) >> 16) 561 | 562 | 563 | #define COMP_OFFSET_BITS $comp_shift 564 | 565 | /* 566 | * The pageMap is indexed by page number and returns an alternate page number 567 | * that identifies a unique page of characters. Many Unicode characters map 568 | * to the same alternate page number. 569 | */ 570 | 571 | static unsigned char compPageMap\[\] = {" 572 | set line " " 573 | set last [expr {[llength $comp_pmap] - 1}] 574 | for {set i 0} {$i <= $last} {incr i} { 575 | append line [lindex $comp_pmap $i] 576 | if {$i != $last} { 577 | append line ", " 578 | } 579 | if {[string length $line] > 70} { 580 | puts $f $line 581 | set line " " 582 | } 583 | } 584 | puts $f $line 585 | puts $f "}; 586 | 587 | /* 588 | * The groupMap is indexed by combining the alternate page number with 589 | * the page offset and returns a group number that identifies a unique 590 | * set of character attributes. 591 | */ 592 | 593 | static int compGroupMap\[\] = {" 594 | set line " " 595 | set lasti [expr {[llength $comp_pages] - 1}] 596 | for {set i 0} {$i <= $lasti} {incr i} { 597 | set page [lindex $comp_pages $i] 598 | set lastj [expr {[llength $page] - 1}] 599 | for {set j 0} {$j <= $lastj} {incr j} { 600 | append line [lindex $page $j] 601 | if {$j != $lastj || $i != $lasti} { 602 | append line ", " 603 | } 604 | if {[string length $line] > 70} { 605 | puts $f $line 606 | set line " " 607 | } 608 | } 609 | } 610 | puts $f $line 611 | puts $f "}; 612 | 613 | /* 614 | * Lists of compositions for characters that appears only in one composition 615 | */ 616 | 617 | static int compFirstList\[\]\[2\] = {" 618 | set line " " 619 | set last [expr {[llength $comp_first_list] - 1}] 620 | for {set i 0} {$i <= $last} {incr i} { 621 | set val [lindex $comp_first_list $i] 622 | 623 | append line [format "{%d, %d}" [lindex $val 0] [lindex $val 1]] 624 | if {$i != $last} { 625 | append line ", " 626 | } 627 | if {[string length $line] > 60} { 628 | puts $f $line 629 | set line " " 630 | } 631 | } 632 | puts $f $line 633 | puts $f "}; 634 | 635 | static int compSecondList\[\]\[2\] = {" 636 | set line " " 637 | set last [expr {[llength $comp_second_list] - 1}] 638 | for {set i 0} {$i <= $last} {incr i} { 639 | set val [lindex $comp_second_list $i] 640 | 641 | append line [format "{%d, %d}" [lindex $val 0] [lindex $val 1]] 642 | if {$i != $last} { 643 | append line ", " 644 | } 645 | if {[string length $line] > 60} { 646 | puts $f $line 647 | set line " " 648 | } 649 | } 650 | puts $f $line 651 | puts $f "}; 652 | 653 | /* 654 | * Compositions matrix 655 | */ 656 | 657 | static int compBothList\[[llength $comp_x_list]\]\[[llength $comp_y_list]\] = {" 658 | set lastx [expr {[llength $comp_x_list] - 1}] 659 | set lasty [expr {[llength $comp_y_list] - 1}] 660 | for {set i 0} {$i <= $lastx} {incr i} { 661 | puts $f " \{" 662 | set line " " 663 | for {set j 0} {$j <= $lasty} {incr j} { 664 | set comp [list [lindex $comp_x_list $i] [lindex $comp_y_list $j]] 665 | if {[info exists comp_map($comp)]} { 666 | set val $comp_map($comp) 667 | } else { 668 | set val 0 669 | } 670 | 671 | append line [format "%d" $val] 672 | if {$j != $lasty} { 673 | append line ", " 674 | } 675 | if {[string length $line] > 70} { 676 | puts $f $line 677 | set line " " 678 | } 679 | } 680 | puts $f $line 681 | if {$j != $lasty} { 682 | puts $f " \}," 683 | } else { 684 | puts $f " \}" 685 | } 686 | } 687 | puts $f "}; 688 | 689 | 690 | #define GetUniCharCompInfo(ch) (compGroupMap\[(compPageMap\[(((int)(ch)) & 0x1fffff) >> COMP_OFFSET_BITS\] << COMP_OFFSET_BITS) | ((ch) & ((1 << COMP_OFFSET_BITS)-1))\]) 691 | 692 | #define CompSingleMask (1 << 16) 693 | #define CompMask ((1 << 16) - 1) 694 | #define CompSecondMask (1 << 17) 695 | " 696 | 697 | close $f 698 | } 699 | 700 | uni::main 701 | 702 | return 703 | -------------------------------------------------------------------------------- /vars.config.in: -------------------------------------------------------------------------------- 1 | {cxxflags, "@CPPFLAGS@"}. 2 | {ldflags, "@LDFLAGS@ @LIBS@"}. 3 | {with_gcov, "@gcov@"}. 4 | 5 | %% Local Variables: 6 | %% mode: erlang 7 | %% End: 8 | %% vim: set filetype=erlang tabstop=8: 9 | --------------------------------------------------------------------------------