diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 1b5349b..3f8944f 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -44,7 +44,8 @@ jobs: contents: write steps: - - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4.1.1 + - name: Check out project + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Python uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 @@ -72,26 +73,31 @@ jobs: outputs: include: ${{ steps.set-matrix.outputs.include }} steps: - - uses: actions/checkout@v6.0.1 + - name: Check out project + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install cibuildwheel # Nb. keep cibuildwheel version pin consistent with job below - run: pipx install cibuildwheel==3.3.0 + run: pipx install cibuildwheel==3.4.1 - id: set-matrix run: | MATRIX=$( { - cibuildwheel --print-build-identifiers --platform linux \ + cibuildwheel --print-build-identifiers --platform linux --archs x86_64 \ | jq -nRc '{"only": inputs, "os": "ubuntu-latest"}' \ | sed -e '/aarch64\|armv7l/s|ubuntu-latest|ubuntu-24.04-arm|' \ && cibuildwheel --print-build-identifiers --platform macos \ | jq -nRc '{"only": inputs, "os": "macos-latest"}' \ - && cibuildwheel --print-build-identifiers --platform windows \ + && cibuildwheel --print-build-identifiers --platform windows --archs x86,AMD64 \ | jq -nRc '{"only": inputs, "os": "windows-2022"}' \ && cibuildwheel --print-build-identifiers --platform windows --archs ARM64 \ | jq -nRc '{"only": inputs, "os": "windows-11-arm"}' } | jq -sc ) + echo "include=$MATRIX" echo "include=$MATRIX" >> $GITHUB_OUTPUT + env: + # Skip abi3 targets here: + CIBW_SKIP: "cp3{9,1?}-win_arm64 cp3{9,1?}-win32 cp3{9,1?}-macosx_x86_64 pp* *musllinux*" build_wheels: name: Build ${{ matrix.only }} @@ -104,8 +110,8 @@ jobs: include: ${{ fromJson(needs.generate-wheels-matrix.outputs.include) }} steps: - - name: Check out the repo - uses: actions/checkout@v6.0.1 + - name: Check out project + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up QEMU if: runner.os == 'Linux' @@ -114,14 +120,14 @@ jobs: platforms: all - name: Build wheels - uses: pypa/cibuildwheel@v3.3.0 + uses: pypa/cibuildwheel@8d2b08b68458a16aeb24b64e68a09ab1c8e82084 # v3.4.1 with: only: ${{ matrix.only }} - name: Build faster Linux wheels # also build wheels with the most recent manylinux images and gcc if: runner.os == 'Linux' && !contains(matrix.only, 'i686') - uses: pypa/cibuildwheel@v3.3.0 + uses: pypa/cibuildwheel@8d2b08b68458a16aeb24b64e68a09ab1c8e82084 # v3.4.1 env: CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_34 CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_34 # manylinux_2_39 ? @@ -143,9 +149,88 @@ jobs: path: ./wheelhouse/*.whl name: wheels-${{ matrix.only }} + build_limited_api_wheels: + name: Build ${{ matrix.target }} Stable ABI ${{ matrix.lapiversion }} wheels + if: >- + github.event_name == 'push' || + github.event_name == 'release' || + (github.event_name == 'schedule' && github.repository == 'cython/cython') || + github.event_name == 'workflow_dispatch' + + strategy: + fail-fast: false + matrix: + target: + # Smaller set of platforms that we only provide Stable ABI wheels for: + - 'musllinux_x86_64' + - 'musllinux_aarch64' + - 'manylinux_i686' + - 'musllinux_i686' + - 'manylinux_ppc64le' + - 'musllinux_ppc64le' + - 'manylinux_riscv64' + - 'musllinux_riscv64' + - 'manylinux_armv7l' + - 'musllinux_armv7l' + - 'macosx_x86_64' + - 'win32' + - 'win_arm64' + lapiversion: + - "3.9" + - "3.12" + + runs-on: ${{ + contains(matrix.target, 'aarch64') && 'ubuntu-24.04-arm' || + contains(matrix.target, 'armv7l') && 'ubuntu-24.04-arm' || + contains(matrix.target, 'win_arm64') && 'windows-11-arm' || + contains(matrix.target, 'win32') && 'windows-latest' || + contains(matrix.target, 'macosx') && 'macos-latest' || + 'ubuntu-latest' + }} + + steps: + - name: Check out project + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.x' + + - name: Set up QEMU + if: runner.os == 'Linux' + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 + with: + platforms: all + + - name: Setup Visual Studio on Windows + if: startsWith(matrix.target, 'win') + uses: TheMrMilchmann/setup-msvc-dev@79dac248aac9d0059f86eae9d8b5bfab4e95e97c # v4.0.0 + with: + arch: ${{ matrix.target == 'win32' && 'x86' || matrix.target == 'win_arm64' && 'arm64' || matrix.target == 'win_amd64' && 'amd64' || '' }} + + - name: Build wheels + uses: pypa/cibuildwheel@8d2b08b68458a16aeb24b64e68a09ab1c8e82084 # v3.4.1 + env: + CIBW_BUILD: "*${{ matrix.target }}" + CIBW_SKIP: "cp31*t-* pp3*" + CIBW_PROJECT_REQUIRES_PYTHON: ">=${{ matrix.lapiversion }}" + QUICKTIONS_LIMITED_API: "${{ matrix.lapiversion }}" + + - name: Check wheel + run: | + python -m pip install twine + python -m twine check ./wheelhouse/*.whl + + - name: Upload wheels + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: Stable-ABI-${{ matrix.target }}-${{ matrix.lapiversion }} + path: ./wheelhouse/*.whl + merge_wheels: name: Merge wheel archives - needs: build_wheels + needs: [build_wheels, build_limited_api_wheels] runs-on: ubuntu-latest steps: @@ -167,8 +252,8 @@ jobs: contents: write steps: - - name: Check out the repo - uses: actions/checkout@v6.0.1 + - name: Check out project + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Download files uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 diff --git a/MANIFEST.in b/MANIFEST.in index 2abe20f..7c7cfb8 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ include MANIFEST.in LICENSE *.rst include setup.py *.yml tox.ini *.cmd *.txt .coveragerc -recursive-include src *.py *.pyx *.pxd *.c *.txt *.html +recursive-include src *.py *.pyx *.pxd *.c *.h *.txt *.html recursive-include benchmark *.py telco-bench.b diff --git a/Makefile b/Makefile index cafff16..a6350c9 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,9 @@ clean: realclean: clean rm -fr src/*.c src/*.html +src/todecimal.h: gen_todecimal.py + $(PYTHON) $< > $@ + qemu-user-static: docker run --rm --privileged hypriot/qemu-register diff --git a/gen_todecimal.py b/gen_todecimal.py new file mode 100644 index 0000000..c9dd998 --- /dev/null +++ b/gen_todecimal.py @@ -0,0 +1,57 @@ +""" +Generate and print a C snippet that maps Unicode digit code points to ASCII digits. +""" + +import sys +import unicodedata +from collections import defaultdict + + +def list_digits(): + category_of = unicodedata.category + return [ + ch for ch in range(128, 1114111+1) + if category_of(chr(ch)) == 'Nd' + ] + + +def map_to_ascii_digit(digits): + adigit_to_udigit = defaultdict(list) + for ch in digits: + adigit = int(chr(ch)) + if ch & 15 == adigit: + adigit_to_udigit['{digit} & 15'].append(ch) + elif (ch - 6) & 15 == adigit: + adigit_to_udigit['({digit} - 6) & 15'].append(ch) + elif (ch - 0x116da) & 15 == adigit: + adigit_to_udigit['({digit} - 0x116da) & 15'].append(ch) + elif (ch - 0x1e5f1) & 15 == adigit: + adigit_to_udigit['({digit} - 0x1e5f1) & 15'].append(ch) + elif ch >= 0x1d7ce and (ch - 0x1d7ce) % 10 == adigit: + adigit_to_udigit['({digit} - 0x1d7ce) % 10'].append(ch) + else: + adigit_to_udigit[str(adigit)].append(ch) + return adigit_to_udigit + + +def gen_switch_cases(digits_by_adigit): + pyver = sys.version_info + print(f"/* Switch cases generated from Python {pyver[0]}.{pyver[1]}.{pyver[2]} {pyver[3]} {pyver[4]} */") + print("/* Deliberately excluding ASCII digit characters. */") + print() + print(f"/* Lowest digit: {min(ch for characters in digits_by_adigit.values() for ch in characters)} */") + print("switch (digit) {") + + for adigit_format, udigits in digits_by_adigit.items(): + for ch in udigits: + print(f' case 0x{ch:x}:') + + print(f" return {adigit_format.format(digit='digit')};") + + print(" default:") + print(" return -1;") + print("}") + + +if __name__ == '__main__': + gen_switch_cases(map_to_ascii_digit(list_digits())) diff --git a/pyproject.toml b/pyproject.toml index 3a79e65..4a248b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,15 +4,28 @@ requires = ["Cython>=3.2.4", "setuptools"] [tool.cibuildwheel] build-verbosity = 2 skip = ["pp*", "*-musllinux_i686", "cp35*", "cp36*", "cp37*"] -enable =["cpython-freethreading", "cpython-prerelease"] +enable =[ + "cpython-prerelease", + ] +environment-pass = "QUICKTIONS_LIMITED_API" # test-command = "python -m unittest {package}/test_fractions.py -p -v" +[tool.cibuildwheel.windows] +archs = ["AMD64", "x86", "ARM64"] + +[tool.cibuildwheel.macos] +# https://cibuildwheel.readthedocs.io/en/stable/faq/#what-to-provide suggests to provide +# x86_64 and one of universal2 or arm64 wheels. x86_64 is still required by older pips, +# so additional arm64 wheels suffice. +#archs = ["x86_64", "universal2"] +archs = ["x86_64", "arm64"] + [tool.cibuildwheel.linux] archs = ["x86_64", "aarch64", "i686", "ppc64le", "armv7l", "riscv64"] repair-wheel-command = "auditwheel repair --strip -w {dest_dir} {wheel}" [tool.cibuildwheel.linux.environment] -CFLAGS = "-O3 -g1 -pipe -fPIC" +CFLAGS = "-O3 -g1 -pipe -fPIC -std=c99" AR = "gcc-ar" NM = "gcc-nm" RANLIB = "gcc-ranlib" @@ -32,12 +45,10 @@ select = "*aarch64" inherit.environment = "append" environment.CFLAGS = "-O3 -g1 -pipe -fPIC -march=armv8-a -mtune=cortex-a72" -[tool.cibuildwheel.windows] -archs = ["AMD64", "x86"] - -[tool.cibuildwheel.macos] -# https://cibuildwheel.readthedocs.io/en/stable/faq/#what-to-provide suggests to provide -# x86_64 and one of universal2 or arm64 wheels. x86_64 is still required by older pips, -# so additional arm64 wheels suffice. -#archs = ["x86_64", "universal2"] -archs = ["x86_64", "arm64"] +[[tool.cibuildwheel.overrides]] +#select = "*i686 *musllinux* *win32 *win_arm64 *macosx_x86_64 *armv7l" +select = "cp3{9,1?}-*i686 cp3{9,1?}-*musllinux* cp3{9,1?}-*macosx_x86_64 cp3{9,1?}-*armv7l cp3{9,1?}-*ppc64le cp3{9,1?}-*riscv64" +inherit.test-requires = "append" +test-requires = ["abi3audit>=0.0.25"] +inherit.test-command = "append" +test-command = ["abi3audit --strict --report {wheel}"] diff --git a/setup.py b/setup.py index caf120b..1a0ca79 100644 --- a/setup.py +++ b/setup.py @@ -6,10 +6,6 @@ from setuptools import setup, Extension -ext_modules = [ - Extension("quicktions", ["src/quicktions.pyx"]), -] - try: sys.argv.remove("--with-profile") except ValueError: @@ -20,6 +16,44 @@ enable_coverage = os.environ.get("WITH_COVERAGE") == "1" force_rebuild = os.environ.get("FORCE_REBUILD") == "1" +def check_limited_api_option(value): + if not value: + return None + value = value.lower() + if value == "true": + # The default Limited API version is 3.9, unless we're on a lower Python version + # (which is mainly for the sake of testing 3.8 on the CI) + if sys.version_info >= (3, 9): + return (3, 9) + else: + return sys.version_info[:2] + if value == 'false': + return None + major, minor = value.split('.', 1) + return (int(major), int(minor)) + +extra_setup_args = {} +c_defines = [] + +option_limited_api = check_limited_api_option(os.environ.get("QUICKTIONS_LIMITED_API")) +if option_limited_api: + c_defines.append(('Py_LIMITED_API', f'0x{option_limited_api[0]:02x}{option_limited_api[1]:02x}0000')) + + setup_options = extra_setup_args.setdefault('options', {}) + bdist_wheel_options = setup_options.setdefault('bdist_wheel', {}) + bdist_wheel_options['py_limited_api'] = f'cp{option_limited_api[0]}{option_limited_api[1]}' + + +ext_modules = [ + Extension( + "quicktions", + ["src/quicktions.pyx"], + py_limited_api=True if option_limited_api else False, + define_macros=c_defines, + ), +] + + try: sys.argv.remove("--with-cython") except ValueError: @@ -101,4 +135,5 @@ "Topic :: Scientific/Engineering :: Mathematics", "Topic :: Office/Business :: Financial", ], + **extra_setup_args, ) diff --git a/src/quicktions.pyx b/src/quicktions.pyx index 4c550a8..2ff71e8 100644 --- a/src/quicktions.pyx +++ b/src/quicktions.pyx @@ -26,7 +26,6 @@ __all__ = ['Fraction'] __version__ = '1.23' cimport cython -from cpython.unicode cimport Py_UNICODE_TODECIMAL from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE from cpython.long cimport PyLong_FromString @@ -83,6 +82,10 @@ cdef pow10(long long i): # Half-private GCD implementation. +cdef extern from "": + # Cython <= 3.2.4 bug: llabs requires C99 and stdlib.h, but Cython doesn't include that automatically. + pass + cdef extern from *: """ #if defined(__Pyx_PyLong_DigitCount) @@ -175,6 +178,18 @@ cdef extern from *: #define __Quicktions_HAS_FAST_CTZ_ullong (0) #define __Quicktions_trailing_zeros_ullong(x) (0) #endif + + #if defined(Py_LIMITED_API) + #if Py_LIMITED_API >= 0x030d0000 + #define __Quicktions_HAS_FAST_MATH_GCD (1) + #elif Py_LIMITED_API >= 0x030b0000 + #define __Quicktions_HAS_FAST_MATH_GCD (Py_Version >= 0x030d0000) + #else + #define __Quicktions_HAS_FAST_MATH_GCD (0) + #endif + #else + #define __Quicktions_HAS_FAST_MATH_GCD (PY_VERSION_HEX >= 0x030d0000) + #endif """ bint PyLong_IsCompact "__Quicktions_PyLong_IsCompact" (x) unsigned long long PyLong_CompactValueUnsigned "__Quicktions_PyLong_CompactValueUnsigned" (x) @@ -185,7 +200,7 @@ cdef extern from *: # CPython 3.5-3.12 has a fast PyLong GCD implementation that we can use. # In CPython 3.13, math.gcd() is fast enough to call it directly. - const bint HAS_FAST_MATH_GCD "(PY_VERSION_HEX >= 0x030d0000)" + const bint HAS_FAST_MATH_GCD "__Quicktions_HAS_FAST_MATH_GCD" const bint HAS_OLD_PYLONG_GCD "(CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030d0000)" object _PyLong_GCD(object a, object b) @@ -500,7 +515,6 @@ cdef _gcd_fallback(a: int, b: int): """ # Try doing the computation in C space. If the numbers are too # large at the beginning, do object calculations until they are small enough. - cdef ullong au, bu cdef long long ai, bi if HAS_ISLONGLONG: @@ -509,26 +523,18 @@ cdef _gcd_fallback(a: int, b: int): au = _abs(ai) bu = _abs(bi) return _py_gcd(au, bu) - else: - # Optimistically try to switch to C space. - try: - ai, bi = a, b - except OverflowError: - pass - else: - au = _abs(ai) - bu = _abs(bi) - return _py_gcd(au, bu) # Do object calculation until we reach the C space limit. a = abs(a) b = abs(b) while b > PY_MAX_ULONGLONG: a, b = b, a%b - while b and a > PY_MAX_ULONGLONG: - a, b = b, a%b - if not b: + if b == 0: return a + while a > PY_MAX_ULONGLONG: + a, b = b, a%b + if b == 0: + return a return _py_gcd(a, b) @@ -1830,31 +1836,54 @@ cdef _raise_parse_overflow(s): cdef extern from *: """ + static CYTHON_INLINE int __QUICKTIONS_to_decimal(Py_UCS4 digit) { + #if CYTHON_COMPILING_IN_LIMITED_API + #include "todecimal.h" + #else + return Py_UNICODE_TODECIMAL(digit); + #endif + } + static CYTHON_INLINE int __QUICKTIONS_unpack_ustring( PyObject* string, Py_ssize_t *length, void** data, int *kind) { + #if CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX < 0x030c0000 if (PyUnicode_READY(string) < 0) return -1; - *kind = PyUnicode_KIND(string); - *length = PyUnicode_GET_LENGTH(string); - *data = PyUnicode_DATA(string); + #endif + *kind = __Pyx_PyUnicode_KIND(string); + *length = __Pyx_PyUnicode_GET_LENGTH(string); + + #if CYTHON_COMPILING_IN_LIMITED_API + *data = (void*) string; + #else + *data = __Pyx_PyUnicode_DATA(string); + #endif + return 0; } + + #if CYTHON_COMPILING_IN_LIMITED_API + #define __QUICKTIONS_char_at(data, kind, index) (Py_UCS4) (((void)kind), PyUnicode_ReadChar(data, index)) + #else #define __QUICKTIONS_char_at(data, kind, index) \ (((kind == 1) ? (Py_UCS4) ((char*) data)[index] : (Py_UCS4) PyUnicode_READ(kind, data, index))) + #endif """ int _unpack_ustring "__QUICKTIONS_unpack_ustring" ( object string, Py_ssize_t *length, void **data, int *kind) except -1 Py_UCS4 _char_at "__QUICKTIONS_char_at" (void *data, int kind, Py_ssize_t index) - Py_UCS4 PyUnicode_READ(int kind, void *data, Py_ssize_t index) + int _to_decimal "__QUICKTIONS_to_decimal" (Py_UCS4 c) -cdef inline int _parse_digit(char** c_digits, Py_UCS4 c, int allow_unicode): +cdef inline int _parse_digit(char** c_digits, Py_UCS4 c, int allow_unicode) noexcept: cdef unsigned int unum cdef int num unum = ( c) - '0' # Relies on integer underflow for dots etc. if unum > 9: if not allow_unicode: return -1 - num = Py_UNICODE_TODECIMAL(c) + if c < 1632: + return -1 + num = _to_decimal(c) if num == -1: return -1 unum = num diff --git a/src/todecimal.h b/src/todecimal.h new file mode 100644 index 0000000..132ed6d --- /dev/null +++ b/src/todecimal.h @@ -0,0 +1,773 @@ +/* Switch cases generated from Python 3.15.0 alpha 1 */ +/* Deliberately excluding ASCII digit characters. */ + +/* Lowest digit: 1632 */ +switch (digit) { + case 0x660: + case 0x661: + case 0x662: + case 0x663: + case 0x664: + case 0x665: + case 0x666: + case 0x667: + case 0x668: + case 0x669: + case 0x6f0: + case 0x6f1: + case 0x6f2: + case 0x6f3: + case 0x6f4: + case 0x6f5: + case 0x6f6: + case 0x6f7: + case 0x6f8: + case 0x6f9: + case 0x7c0: + case 0x7c1: + case 0x7c2: + case 0x7c3: + case 0x7c4: + case 0x7c5: + case 0x7c6: + case 0x7c7: + case 0x7c8: + case 0x7c9: + case 0xe50: + case 0xe51: + case 0xe52: + case 0xe53: + case 0xe54: + case 0xe55: + case 0xe56: + case 0xe57: + case 0xe58: + case 0xe59: + case 0xed0: + case 0xed1: + case 0xed2: + case 0xed3: + case 0xed4: + case 0xed5: + case 0xed6: + case 0xed7: + case 0xed8: + case 0xed9: + case 0xf20: + case 0xf21: + case 0xf22: + case 0xf23: + case 0xf24: + case 0xf25: + case 0xf26: + case 0xf27: + case 0xf28: + case 0xf29: + case 0x1040: + case 0x1041: + case 0x1042: + case 0x1043: + case 0x1044: + case 0x1045: + case 0x1046: + case 0x1047: + case 0x1048: + case 0x1049: + case 0x1090: + case 0x1091: + case 0x1092: + case 0x1093: + case 0x1094: + case 0x1095: + case 0x1096: + case 0x1097: + case 0x1098: + case 0x1099: + case 0x17e0: + case 0x17e1: + case 0x17e2: + case 0x17e3: + case 0x17e4: + case 0x17e5: + case 0x17e6: + case 0x17e7: + case 0x17e8: + case 0x17e9: + case 0x1810: + case 0x1811: + case 0x1812: + case 0x1813: + case 0x1814: + case 0x1815: + case 0x1816: + case 0x1817: + case 0x1818: + case 0x1819: + case 0x19d0: + case 0x19d1: + case 0x19d2: + case 0x19d3: + case 0x19d4: + case 0x19d5: + case 0x19d6: + case 0x19d7: + case 0x19d8: + case 0x19d9: + case 0x1a80: + case 0x1a81: + case 0x1a82: + case 0x1a83: + case 0x1a84: + case 0x1a85: + case 0x1a86: + case 0x1a87: + case 0x1a88: + case 0x1a89: + case 0x1a90: + case 0x1a91: + case 0x1a92: + case 0x1a93: + case 0x1a94: + case 0x1a95: + case 0x1a96: + case 0x1a97: + case 0x1a98: + case 0x1a99: + case 0x1b50: + case 0x1b51: + case 0x1b52: + case 0x1b53: + case 0x1b54: + case 0x1b55: + case 0x1b56: + case 0x1b57: + case 0x1b58: + case 0x1b59: + case 0x1bb0: + case 0x1bb1: + case 0x1bb2: + case 0x1bb3: + case 0x1bb4: + case 0x1bb5: + case 0x1bb6: + case 0x1bb7: + case 0x1bb8: + case 0x1bb9: + case 0x1c40: + case 0x1c41: + case 0x1c42: + case 0x1c43: + case 0x1c44: + case 0x1c45: + case 0x1c46: + case 0x1c47: + case 0x1c48: + case 0x1c49: + case 0x1c50: + case 0x1c51: + case 0x1c52: + case 0x1c53: + case 0x1c54: + case 0x1c55: + case 0x1c56: + case 0x1c57: + case 0x1c58: + case 0x1c59: + case 0xa620: + case 0xa621: + case 0xa622: + case 0xa623: + case 0xa624: + case 0xa625: + case 0xa626: + case 0xa627: + case 0xa628: + case 0xa629: + case 0xa8d0: + case 0xa8d1: + case 0xa8d2: + case 0xa8d3: + case 0xa8d4: + case 0xa8d5: + case 0xa8d6: + case 0xa8d7: + case 0xa8d8: + case 0xa8d9: + case 0xa900: + case 0xa901: + case 0xa902: + case 0xa903: + case 0xa904: + case 0xa905: + case 0xa906: + case 0xa907: + case 0xa908: + case 0xa909: + case 0xa9d0: + case 0xa9d1: + case 0xa9d2: + case 0xa9d3: + case 0xa9d4: + case 0xa9d5: + case 0xa9d6: + case 0xa9d7: + case 0xa9d8: + case 0xa9d9: + case 0xa9f0: + case 0xa9f1: + case 0xa9f2: + case 0xa9f3: + case 0xa9f4: + case 0xa9f5: + case 0xa9f6: + case 0xa9f7: + case 0xa9f8: + case 0xa9f9: + case 0xaa50: + case 0xaa51: + case 0xaa52: + case 0xaa53: + case 0xaa54: + case 0xaa55: + case 0xaa56: + case 0xaa57: + case 0xaa58: + case 0xaa59: + case 0xabf0: + case 0xabf1: + case 0xabf2: + case 0xabf3: + case 0xabf4: + case 0xabf5: + case 0xabf6: + case 0xabf7: + case 0xabf8: + case 0xabf9: + case 0xff10: + case 0xff11: + case 0xff12: + case 0xff13: + case 0xff14: + case 0xff15: + case 0xff16: + case 0xff17: + case 0xff18: + case 0xff19: + case 0x104a0: + case 0x104a1: + case 0x104a2: + case 0x104a3: + case 0x104a4: + case 0x104a5: + case 0x104a6: + case 0x104a7: + case 0x104a8: + case 0x104a9: + case 0x10d30: + case 0x10d31: + case 0x10d32: + case 0x10d33: + case 0x10d34: + case 0x10d35: + case 0x10d36: + case 0x10d37: + case 0x10d38: + case 0x10d39: + case 0x10d40: + case 0x10d41: + case 0x10d42: + case 0x10d43: + case 0x10d44: + case 0x10d45: + case 0x10d46: + case 0x10d47: + case 0x10d48: + case 0x10d49: + case 0x110f0: + case 0x110f1: + case 0x110f2: + case 0x110f3: + case 0x110f4: + case 0x110f5: + case 0x110f6: + case 0x110f7: + case 0x110f8: + case 0x110f9: + case 0x111d0: + case 0x111d1: + case 0x111d2: + case 0x111d3: + case 0x111d4: + case 0x111d5: + case 0x111d6: + case 0x111d7: + case 0x111d8: + case 0x111d9: + case 0x112f0: + case 0x112f1: + case 0x112f2: + case 0x112f3: + case 0x112f4: + case 0x112f5: + case 0x112f6: + case 0x112f7: + case 0x112f8: + case 0x112f9: + case 0x11450: + case 0x11451: + case 0x11452: + case 0x11453: + case 0x11454: + case 0x11455: + case 0x11456: + case 0x11457: + case 0x11458: + case 0x11459: + case 0x114d0: + case 0x114d1: + case 0x114d2: + case 0x114d3: + case 0x114d4: + case 0x114d5: + case 0x114d6: + case 0x114d7: + case 0x114d8: + case 0x114d9: + case 0x11650: + case 0x11651: + case 0x11652: + case 0x11653: + case 0x11654: + case 0x11655: + case 0x11656: + case 0x11657: + case 0x11658: + case 0x11659: + case 0x116c0: + case 0x116c1: + case 0x116c2: + case 0x116c3: + case 0x116c4: + case 0x116c5: + case 0x116c6: + case 0x116c7: + case 0x116c8: + case 0x116c9: + case 0x116d0: + case 0x116d1: + case 0x116d2: + case 0x116d3: + case 0x116d4: + case 0x116d5: + case 0x116d6: + case 0x116d7: + case 0x116d8: + case 0x116d9: + case 0x11730: + case 0x11731: + case 0x11732: + case 0x11733: + case 0x11734: + case 0x11735: + case 0x11736: + case 0x11737: + case 0x11738: + case 0x11739: + case 0x118e0: + case 0x118e1: + case 0x118e2: + case 0x118e3: + case 0x118e4: + case 0x118e5: + case 0x118e6: + case 0x118e7: + case 0x118e8: + case 0x118e9: + case 0x11950: + case 0x11951: + case 0x11952: + case 0x11953: + case 0x11954: + case 0x11955: + case 0x11956: + case 0x11957: + case 0x11958: + case 0x11959: + case 0x11bf0: + case 0x11bf1: + case 0x11bf2: + case 0x11bf3: + case 0x11bf4: + case 0x11bf5: + case 0x11bf6: + case 0x11bf7: + case 0x11bf8: + case 0x11bf9: + case 0x11c50: + case 0x11c51: + case 0x11c52: + case 0x11c53: + case 0x11c54: + case 0x11c55: + case 0x11c56: + case 0x11c57: + case 0x11c58: + case 0x11c59: + case 0x11d50: + case 0x11d51: + case 0x11d52: + case 0x11d53: + case 0x11d54: + case 0x11d55: + case 0x11d56: + case 0x11d57: + case 0x11d58: + case 0x11d59: + case 0x11da0: + case 0x11da1: + case 0x11da2: + case 0x11da3: + case 0x11da4: + case 0x11da5: + case 0x11da6: + case 0x11da7: + case 0x11da8: + case 0x11da9: + case 0x11de0: + case 0x11de1: + case 0x11de2: + case 0x11de3: + case 0x11de4: + case 0x11de5: + case 0x11de6: + case 0x11de7: + case 0x11de8: + case 0x11de9: + case 0x11f50: + case 0x11f51: + case 0x11f52: + case 0x11f53: + case 0x11f54: + case 0x11f55: + case 0x11f56: + case 0x11f57: + case 0x11f58: + case 0x11f59: + case 0x16130: + case 0x16131: + case 0x16132: + case 0x16133: + case 0x16134: + case 0x16135: + case 0x16136: + case 0x16137: + case 0x16138: + case 0x16139: + case 0x16a60: + case 0x16a61: + case 0x16a62: + case 0x16a63: + case 0x16a64: + case 0x16a65: + case 0x16a66: + case 0x16a67: + case 0x16a68: + case 0x16a69: + case 0x16ac0: + case 0x16ac1: + case 0x16ac2: + case 0x16ac3: + case 0x16ac4: + case 0x16ac5: + case 0x16ac6: + case 0x16ac7: + case 0x16ac8: + case 0x16ac9: + case 0x16b50: + case 0x16b51: + case 0x16b52: + case 0x16b53: + case 0x16b54: + case 0x16b55: + case 0x16b56: + case 0x16b57: + case 0x16b58: + case 0x16b59: + case 0x16d70: + case 0x16d71: + case 0x16d72: + case 0x16d73: + case 0x16d74: + case 0x16d75: + case 0x16d76: + case 0x16d77: + case 0x16d78: + case 0x16d79: + case 0x1ccf0: + case 0x1ccf1: + case 0x1ccf2: + case 0x1ccf3: + case 0x1ccf4: + case 0x1ccf5: + case 0x1ccf6: + case 0x1ccf7: + case 0x1ccf8: + case 0x1ccf9: + case 0x1e140: + case 0x1e141: + case 0x1e142: + case 0x1e143: + case 0x1e144: + case 0x1e145: + case 0x1e146: + case 0x1e147: + case 0x1e148: + case 0x1e149: + case 0x1e2f0: + case 0x1e2f1: + case 0x1e2f2: + case 0x1e2f3: + case 0x1e2f4: + case 0x1e2f5: + case 0x1e2f6: + case 0x1e2f7: + case 0x1e2f8: + case 0x1e2f9: + case 0x1e4f0: + case 0x1e4f1: + case 0x1e4f2: + case 0x1e4f3: + case 0x1e4f4: + case 0x1e4f5: + case 0x1e4f6: + case 0x1e4f7: + case 0x1e4f8: + case 0x1e4f9: + case 0x1e950: + case 0x1e951: + case 0x1e952: + case 0x1e953: + case 0x1e954: + case 0x1e955: + case 0x1e956: + case 0x1e957: + case 0x1e958: + case 0x1e959: + case 0x1fbf0: + case 0x1fbf1: + case 0x1fbf2: + case 0x1fbf3: + case 0x1fbf4: + case 0x1fbf5: + case 0x1fbf6: + case 0x1fbf7: + case 0x1fbf8: + case 0x1fbf9: + return digit & 15; + case 0x966: + case 0x967: + case 0x968: + case 0x969: + case 0x96a: + case 0x96b: + case 0x96c: + case 0x96d: + case 0x96e: + case 0x96f: + case 0x9e6: + case 0x9e7: + case 0x9e8: + case 0x9e9: + case 0x9ea: + case 0x9eb: + case 0x9ec: + case 0x9ed: + case 0x9ee: + case 0x9ef: + case 0xa66: + case 0xa67: + case 0xa68: + case 0xa69: + case 0xa6a: + case 0xa6b: + case 0xa6c: + case 0xa6d: + case 0xa6e: + case 0xa6f: + case 0xae6: + case 0xae7: + case 0xae8: + case 0xae9: + case 0xaea: + case 0xaeb: + case 0xaec: + case 0xaed: + case 0xaee: + case 0xaef: + case 0xb66: + case 0xb67: + case 0xb68: + case 0xb69: + case 0xb6a: + case 0xb6b: + case 0xb6c: + case 0xb6d: + case 0xb6e: + case 0xb6f: + case 0xbe6: + case 0xbe7: + case 0xbe8: + case 0xbe9: + case 0xbea: + case 0xbeb: + case 0xbec: + case 0xbed: + case 0xbee: + case 0xbef: + case 0xc66: + case 0xc67: + case 0xc68: + case 0xc69: + case 0xc6a: + case 0xc6b: + case 0xc6c: + case 0xc6d: + case 0xc6e: + case 0xc6f: + case 0xce6: + case 0xce7: + case 0xce8: + case 0xce9: + case 0xcea: + case 0xceb: + case 0xcec: + case 0xced: + case 0xcee: + case 0xcef: + case 0xd66: + case 0xd67: + case 0xd68: + case 0xd69: + case 0xd6a: + case 0xd6b: + case 0xd6c: + case 0xd6d: + case 0xd6e: + case 0xd6f: + case 0xde6: + case 0xde7: + case 0xde8: + case 0xde9: + case 0xdea: + case 0xdeb: + case 0xdec: + case 0xded: + case 0xdee: + case 0xdef: + case 0x1946: + case 0x1947: + case 0x1948: + case 0x1949: + case 0x194a: + case 0x194b: + case 0x194c: + case 0x194d: + case 0x194e: + case 0x194f: + case 0x11066: + case 0x11067: + case 0x11068: + case 0x11069: + case 0x1106a: + case 0x1106b: + case 0x1106c: + case 0x1106d: + case 0x1106e: + case 0x1106f: + case 0x11136: + case 0x11137: + case 0x11138: + case 0x11139: + case 0x1113a: + case 0x1113b: + case 0x1113c: + case 0x1113d: + case 0x1113e: + case 0x1113f: + case 0x1d7f6: + case 0x1d7f7: + case 0x1d7f8: + case 0x1d7f9: + case 0x1d7fa: + case 0x1d7fb: + case 0x1d7fc: + case 0x1d7fd: + case 0x1d7fe: + case 0x1d7ff: + return (digit - 6) & 15; + case 0x116da: + case 0x116db: + case 0x116dc: + case 0x116dd: + case 0x116de: + case 0x116df: + case 0x116e0: + case 0x116e1: + case 0x116e2: + case 0x116e3: + return (digit - 0x116da) & 15; + case 0x1d7ce: + case 0x1d7cf: + case 0x1d7d0: + case 0x1d7d1: + case 0x1d7d2: + case 0x1d7d3: + case 0x1d7d4: + case 0x1d7d5: + case 0x1d7d6: + case 0x1d7d7: + case 0x1d7d8: + case 0x1d7d9: + case 0x1d7da: + case 0x1d7db: + case 0x1d7dc: + case 0x1d7dd: + case 0x1d7de: + case 0x1d7df: + case 0x1d7e0: + case 0x1d7e1: + case 0x1d7e2: + case 0x1d7e3: + case 0x1d7e4: + case 0x1d7e5: + case 0x1d7e6: + case 0x1d7e7: + case 0x1d7e8: + case 0x1d7e9: + case 0x1d7ea: + case 0x1d7eb: + case 0x1d7ec: + case 0x1d7ed: + case 0x1d7ee: + case 0x1d7ef: + case 0x1d7f0: + case 0x1d7f1: + case 0x1d7f2: + case 0x1d7f3: + case 0x1d7f4: + case 0x1d7f5: + return (digit - 0x1d7ce) % 10; + case 0x1e5f1: + case 0x1e5f2: + case 0x1e5f3: + case 0x1e5f4: + case 0x1e5f5: + case 0x1e5f6: + case 0x1e5f7: + case 0x1e5f8: + case 0x1e5f9: + case 0x1e5fa: + return (digit - 0x1e5f1) & 15; + default: + return -1; +}