├── .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 | [](https://github.com/processone/stringprep/actions/workflows/ci.yml)
4 | [](https://coveralls.io/github/processone/stringprep?branch=master)
5 | [](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 |
--------------------------------------------------------------------------------