"""
This module contains support functions for more advanced unicode operations.
This is not a public API and is for Numba internal use only. Most of the
functions are relatively straightforward translations of the functions with the
same name in CPython.
"""
from collections import namedtuple
from enum import IntEnum
import numpy as np
import llvmlite.llvmpy.core as lc

from numba import types, cgutils
from numba.targets.imputils import (impl_ret_untracked)

from numba.extending import overload, intrinsic, register_jitable
from .errors import TypingError

# This is equivalent to the struct `_PyUnicode_TypeRecord defined in CPython's
# Objects/unicodectype.c
typerecord = namedtuple('typerecord',
                        'upper lower title decimal digit flags')

# The Py_UCS4 type from CPython:
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Include/unicodeobject.h#L112    # noqa: E501
_Py_UCS4 = types.uint32

# ------------------------------------------------------------------------------
# Start code related to/from CPython's unicodectype impl
#
# NOTE: the original source at:
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Objects/unicodectype.c   # noqa: E501
# contains this statement:
#
# /*
#   Unicode character type helpers.
#
#   Written by Marc-Andre Lemburg (mal@lemburg.com).
#   Modified for Python 2.0 by Fredrik Lundh (fredrik@pythonware.com)
#
#   Copyright (c) Corporation for National Research Initiatives.
#
# */


# This enum contains the values defined in CPython's Objects/unicodectype.c that
# provide masks for use against the various members of the typerecord
#
# See: https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Objects/unicodectype.c#L13-L27    # noqa: E501
#


class _PyUnicode_TyperecordMasks(IntEnum):
    ALPHA_MASK = 0x01
    DECIMAL_MASK = 0x02
    DIGIT_MASK = 0x04
    LOWER_MASK = 0x08
    LINEBREAK_MASK = 0x10
    SPACE_MASK = 0x20
    TITLE_MASK = 0x40
    UPPER_MASK = 0x80
    XID_START_MASK = 0x100
    XID_CONTINUE_MASK = 0x200
    PRINTABLE_MASK = 0x400
    NUMERIC_MASK = 0x800
    CASE_IGNORABLE_MASK = 0x1000
    CASED_MASK = 0x2000
    EXTENDED_CASE_MASK = 0x4000


def _PyUnicode_gettyperecord(a):
    raise RuntimeError("Calling the Python definition is invalid")


@intrinsic
def _gettyperecord_impl(typingctx, codepoint):
    """
    Provides the binding to numba_gettyperecord, returns a `typerecord`
    namedtuple of properties from the codepoint.
    """
    if not isinstance(codepoint, types.Integer):
        raise TypingError("codepoint must be an integer")

    def details(context, builder, signature, args):
        ll_void = context.get_value_type(types.void)
        ll_Py_UCS4 = context.get_value_type(_Py_UCS4)
        ll_intc = context.get_value_type(types.intc)
        ll_intc_ptr = ll_intc.as_pointer()
        ll_uchar = context.get_value_type(types.uchar)
        ll_uchar_ptr = ll_uchar.as_pointer()
        ll_ushort = context.get_value_type(types.ushort)
        ll_ushort_ptr = ll_ushort.as_pointer()
        fnty = lc.Type.function(ll_void, [
            ll_Py_UCS4,    # code
            ll_intc_ptr,   # upper
            ll_intc_ptr,   # lower
            ll_intc_ptr,   # title
            ll_uchar_ptr,  # decimal
            ll_uchar_ptr,  # digit
            ll_ushort_ptr, # flags
        ])
        fn = builder.module.get_or_insert_function(
            fnty, name="numba_gettyperecord")
        upper = cgutils.alloca_once(builder, ll_intc, name='upper')
        lower = cgutils.alloca_once(builder, ll_intc, name='lower')
        title = cgutils.alloca_once(builder, ll_intc, name='title')
        decimal = cgutils.alloca_once(builder, ll_uchar, name='decimal')
        digit = cgutils.alloca_once(builder, ll_uchar, name='digit')
        flags = cgutils.alloca_once(builder, ll_ushort, name='flags')

        byref = [ upper, lower, title, decimal, digit, flags]
        builder.call(fn, [args[0]] + byref)
        buf = []
        for x in byref:
            buf.append(builder.load(x))

        res = context.make_tuple(builder, signature.return_type, tuple(buf))
        return impl_ret_untracked(context, builder, signature.return_type, res)

    tupty = types.NamedTuple([types.intc, types.intc, types.intc, types.uchar,
                              types.uchar, types.ushort], typerecord)
    sig = tupty(_Py_UCS4)
    return sig, details


@overload(_PyUnicode_gettyperecord)
def gettyperecord_impl(a):
    """
    Provides a _PyUnicode_gettyperecord binding, for convenience it will accept
    single character strings and code points.
    """
    if isinstance(a, types.UnicodeType):
        from numba.unicode import _get_code_point

        def impl(a):
            if len(a) > 1:
                msg = "gettyperecord takes a single unicode character"
                raise ValueError(msg)
            code_point = _get_code_point(a, 0)
            data = _gettyperecord_impl(_Py_UCS4(code_point))
            return data
        return impl
    if isinstance(a, types.Integer):
        return lambda a: _gettyperecord_impl(_Py_UCS4(a))


# whilst it's possible to grab the _PyUnicode_ExtendedCase symbol as it's global
# it is safer to use a defined api:
@intrinsic
def _PyUnicode_ExtendedCase(typingctx, index):
    """
    Accessor function for the _PyUnicode_ExtendedCase array, binds to
    numba_get_PyUnicode_ExtendedCase which wraps the array and does the lookup
    """
    if not isinstance(index, types.Integer):
        raise TypingError("Expected an index")

    def details(context, builder, signature, args):
        ll_Py_UCS4 = context.get_value_type(_Py_UCS4)
        ll_intc = context.get_value_type(types.intc)
        fnty = lc.Type.function(ll_Py_UCS4, [ll_intc])
        fn = builder.module.get_or_insert_function(
            fnty, name="numba_get_PyUnicode_ExtendedCase")
        return builder.call(fn, [args[0]])

    sig = _Py_UCS4(types.intc)
    return sig, details

# The following functions are replications of the functions with the same name
# in CPython's Objects/unicodectype.c


# From: https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Objects/unicodectype.c#L64-L71    # noqa: E501
@register_jitable
def _PyUnicode_ToTitlecase(ch):
    ctype = _PyUnicode_gettyperecord(ch)
    if (ctype.flags & _PyUnicode_TyperecordMasks.EXTENDED_CASE_MASK):
        return _PyUnicode_ExtendedCase(ctype.title & 0xFFFF)
    return ch + ctype.title


# From: https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Objects/unicodectype.c#L76-L81    # noqa: E501
@register_jitable
def _PyUnicode_IsTitlecase(ch):
    ctype = _PyUnicode_gettyperecord(ch)
    return ctype.flags & _PyUnicode_TyperecordMasks.TITLE_MASK != 0


@register_jitable
def _PyUnicode_IsXidStart(ch):
    raise NotImplementedError


@register_jitable
def _PyUnicode_IsXidContinue(ch):
    raise NotImplementedError


@register_jitable
def _PyUnicode_ToDecimalDigit(ch):
    raise NotImplementedError


@register_jitable
def _PyUnicode_IsDecimalDigit(ch):
    raise NotImplementedError


@register_jitable
def _PyUnicode_ToDigit(ch):
    raise NotImplementedError


@register_jitable
def _PyUnicode_IsDigit(ch):
    raise NotImplementedError


@register_jitable
def _PyUnicode_IsNumeric(ch):
    raise NotImplementedError


@register_jitable
def _PyUnicode_IsPrintable(ch):
    raise NotImplementedError


# From: https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Objects/unicodectype.c#L170-L175    # noqa: E501
@register_jitable
def _PyUnicode_IsLowercase(ch):
    ctype = _PyUnicode_gettyperecord(ch)
    return ctype.flags & _PyUnicode_TyperecordMasks.LOWER_MASK != 0


# From: https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Objects/unicodectype.c#L180-L185    # noqa: E501
@register_jitable
def _PyUnicode_IsUppercase(ch):
    ctype = _PyUnicode_gettyperecord(ch)
    return ctype.flags & _PyUnicode_TyperecordMasks.UPPER_MASK != 0


@register_jitable
def _PyUnicode_ToUppercase(ch):
    raise NotImplementedError


@register_jitable
def _PyUnicode_ToLowercase(ch):
    raise NotImplementedError


@register_jitable
def _PyUnicode_ToLowerFull(ch, res):
    raise NotImplementedError


@register_jitable
def _PyUnicode_ToTitleFull(ch, res):
    raise NotImplementedError


# From: https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Objects/unicodectype.c#L243-L257    # noqa: E501
@register_jitable
def _PyUnicode_ToUpperFull(ch, res):
    ctype = _PyUnicode_gettyperecord(ch)
    if (ctype.flags & _PyUnicode_TyperecordMasks.EXTENDED_CASE_MASK):
        index = ctype.upper & 0xFFFF
        n = ctype.upper >> 24
        for i in range(n):
            res[i] = _PyUnicode_ExtendedCase(index + i)
        return n
    res[0] = ch + ctype.upper
    return 1


@register_jitable
def _PyUnicode_ToFoldedFull(ch, res):
    raise NotImplementedError


@register_jitable
def _PyUnicode_IsCased(ch):
    raise NotImplementedError


@register_jitable
def _PyUnicode_IsCaseIgnorable(ch):
    raise NotImplementedError


@register_jitable
def _PyUnicode_IsAlpha(ch):
    raise NotImplementedError

# End code related to/from CPython's unicodectype impl
# ------------------------------------------------------------------------------


# ------------------------------------------------------------------------------
# Start code related to/from CPython's pyctype

# From the definition in CPython's Include/pyctype.h
# From: https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Include/pyctype.h#L5-L11    # noqa: E501
class _PY_CTF(IntEnum):
    LOWER = 0x01
    UPPER = 0x02
    ALPHA = 0x01 | 0x02
    DIGIT = 0x04
    ALNUM = 0x01 | 0x02 | 0x04
    SPACE = 0x08
    XDIGIT = 0x10


# From the definition in CPython's Python/pyctype.c
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Python/pyctype.c#L5    # noqa: E501
_Py_ctype_table = np.array([
    0,  # 0x0 '\x00'
    0,  # 0x1 '\x01'
    0,  # 0x2 '\x02'
    0,  # 0x3 '\x03'
    0,  # 0x4 '\x04'
    0,  # 0x5 '\x05'
    0,  # 0x6 '\x06'
    0,  # 0x7 '\x07'
    0,  # 0x8 '\x08'
    _PY_CTF.SPACE,  # 0x9 '\t'
    _PY_CTF.SPACE,  # 0xa '\n'
    _PY_CTF.SPACE,  # 0xb '\v'
    _PY_CTF.SPACE,  # 0xc '\f'
    _PY_CTF.SPACE,  # 0xd '\r'
    0,  # 0xe '\x0e'
    0,  # 0xf '\x0f'
    0,  # 0x10 '\x10'
    0,  # 0x11 '\x11'
    0,  # 0x12 '\x12'
    0,  # 0x13 '\x13'
    0,  # 0x14 '\x14'
    0,  # 0x15 '\x15'
    0,  # 0x16 '\x16'
    0,  # 0x17 '\x17'
    0,  # 0x18 '\x18'
    0,  # 0x19 '\x19'
    0,  # 0x1a '\x1a'
    0,  # 0x1b '\x1b'
    0,  # 0x1c '\x1c'
    0,  # 0x1d '\x1d'
    0,  # 0x1e '\x1e'
    0,  # 0x1f '\x1f'
    _PY_CTF.SPACE,  # 0x20 ' '
    0,  # 0x21 '!'
    0,  # 0x22 '"'
    0,  # 0x23 '#'
    0,  # 0x24 '$'
    0,  # 0x25 '%'
    0,  # 0x26 '&'
    0,  # 0x27 "'"
    0,  # 0x28 '('
    0,  # 0x29 ')'
    0,  # 0x2a '*'
    0,  # 0x2b '+'
    0,  # 0x2c ','
    0,  # 0x2d '-'
    0,  # 0x2e '.'
    0,  # 0x2f '/'
    _PY_CTF.DIGIT | _PY_CTF.XDIGIT,  # 0x30 '0'
    _PY_CTF.DIGIT | _PY_CTF.XDIGIT,  # 0x31 '1'
    _PY_CTF.DIGIT | _PY_CTF.XDIGIT,  # 0x32 '2'
    _PY_CTF.DIGIT | _PY_CTF.XDIGIT,  # 0x33 '3'
    _PY_CTF.DIGIT | _PY_CTF.XDIGIT,  # 0x34 '4'
    _PY_CTF.DIGIT | _PY_CTF.XDIGIT,  # 0x35 '5'
    _PY_CTF.DIGIT | _PY_CTF.XDIGIT,  # 0x36 '6'
    _PY_CTF.DIGIT | _PY_CTF.XDIGIT,  # 0x37 '7'
    _PY_CTF.DIGIT | _PY_CTF.XDIGIT,  # 0x38 '8'
    _PY_CTF.DIGIT | _PY_CTF.XDIGIT,  # 0x39 '9'
    0,  # 0x3a ':'
    0,  # 0x3b ';'
    0,  # 0x3c '<'
    0,  # 0x3d '='
    0,  # 0x3e '>'
    0,  # 0x3f '?'
    0,  # 0x40 '@'
    _PY_CTF.UPPER | _PY_CTF.XDIGIT,  # 0x41 'A'
    _PY_CTF.UPPER | _PY_CTF.XDIGIT,  # 0x42 'B'
    _PY_CTF.UPPER | _PY_CTF.XDIGIT,  # 0x43 'C'
    _PY_CTF.UPPER | _PY_CTF.XDIGIT,  # 0x44 'D'
    _PY_CTF.UPPER | _PY_CTF.XDIGIT,  # 0x45 'E'
    _PY_CTF.UPPER | _PY_CTF.XDIGIT,  # 0x46 'F'
    _PY_CTF.UPPER,  # 0x47 'G'
    _PY_CTF.UPPER,  # 0x48 'H'
    _PY_CTF.UPPER,  # 0x49 'I'
    _PY_CTF.UPPER,  # 0x4a 'J'
    _PY_CTF.UPPER,  # 0x4b 'K'
    _PY_CTF.UPPER,  # 0x4c 'L'
    _PY_CTF.UPPER,  # 0x4d 'M'
    _PY_CTF.UPPER,  # 0x4e 'N'
    _PY_CTF.UPPER,  # 0x4f 'O'
    _PY_CTF.UPPER,  # 0x50 'P'
    _PY_CTF.UPPER,  # 0x51 'Q'
    _PY_CTF.UPPER,  # 0x52 'R'
    _PY_CTF.UPPER,  # 0x53 'S'
    _PY_CTF.UPPER,  # 0x54 'T'
    _PY_CTF.UPPER,  # 0x55 'U'
    _PY_CTF.UPPER,  # 0x56 'V'
    _PY_CTF.UPPER,  # 0x57 'W'
    _PY_CTF.UPPER,  # 0x58 'X'
    _PY_CTF.UPPER,  # 0x59 'Y'
    _PY_CTF.UPPER,  # 0x5a 'Z'
    0,  # 0x5b '['
    0,  # 0x5c '\\'
    0,  # 0x5d ']'
    0,  # 0x5e '^'
    0,  # 0x5f '_'
    0,  # 0x60 '`'
    _PY_CTF.LOWER | _PY_CTF.XDIGIT,  # 0x61 'a'
    _PY_CTF.LOWER | _PY_CTF.XDIGIT,  # 0x62 'b'
    _PY_CTF.LOWER | _PY_CTF.XDIGIT,  # 0x63 'c'
    _PY_CTF.LOWER | _PY_CTF.XDIGIT,  # 0x64 'd'
    _PY_CTF.LOWER | _PY_CTF.XDIGIT,  # 0x65 'e'
    _PY_CTF.LOWER | _PY_CTF.XDIGIT,  # 0x66 'f'
    _PY_CTF.LOWER,  # 0x67 'g'
    _PY_CTF.LOWER,  # 0x68 'h'
    _PY_CTF.LOWER,  # 0x69 'i'
    _PY_CTF.LOWER,  # 0x6a 'j'
    _PY_CTF.LOWER,  # 0x6b 'k'
    _PY_CTF.LOWER,  # 0x6c 'l'
    _PY_CTF.LOWER,  # 0x6d 'm'
    _PY_CTF.LOWER,  # 0x6e 'n'
    _PY_CTF.LOWER,  # 0x6f 'o'
    _PY_CTF.LOWER,  # 0x70 'p'
    _PY_CTF.LOWER,  # 0x71 'q'
    _PY_CTF.LOWER,  # 0x72 'r'
    _PY_CTF.LOWER,  # 0x73 's'
    _PY_CTF.LOWER,  # 0x74 't'
    _PY_CTF.LOWER,  # 0x75 'u'
    _PY_CTF.LOWER,  # 0x76 'v'
    _PY_CTF.LOWER,  # 0x77 'w'
    _PY_CTF.LOWER,  # 0x78 'x'
    _PY_CTF.LOWER,  # 0x79 'y'
    _PY_CTF.LOWER,  # 0x7a 'z'
    0,  # 0x7b '{'
    0,  # 0x7c '|'
    0,  # 0x7d '}'
    0,  # 0x7e '~'
    0,  # 0x7f '\x7f'
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
], dtype=np.intc)


# From the definition in CPython's Python/pyctype.c
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Python/pyctype.c#L145    # noqa: E501
_Py_ctype_tolower = np.array([
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
    0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
    0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
], dtype=np.uint8)


# From the definition in CPython's Python/pyctype.c
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Python/pyctype.c#L180
_Py_ctype_toupper = np.array([
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
    0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
    0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
], dtype=np.uint8)


# Translation of:
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Include/pymacro.h#L25    # noqa: E501
@register_jitable
def _Py_CHARMASK(ch):
    """
    Equivalent to the CPython macro `Py_CHARMASK()`, masks off all but the
    lowest 256 bits of ch.
    """
    return types.uint8(ch) & types.uint8(0xff)


# Translation of:
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Include/pyctype.h#L30    # noqa: E501
@register_jitable
def _Py_TOUPPER(ch):
    """
    Equivalent to the CPython macro `Py_TOUPPER()` converts an ASCII range
    code point to the upper equivalent
    """
    return _Py_ctype_toupper[_Py_CHARMASK(ch)]


# Translation of:
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Include/pyctype.h#L29    # noqa: E501
@register_jitable
def _Py_TOLOWER(ch):
    """
    Equivalent to the CPython macro `Py_TOLOWER()` converts an ASCII range
    code point to the lower equivalent
    """
    return _Py_ctype_tolower[_Py_CHARMASK(ch)]


# Translation of:
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Include/pyctype.h#L18    # noqa: E501
@register_jitable
def _Py_ISLOWER(ch):
    """
    Equivalent to the CPython macro `Py_ISLOWER()`
    """
    return _Py_ctype_table[_Py_CHARMASK(ch)] & _PY_CTF.LOWER


# Translation of:
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Include/pyctype.h#L19    # noqa: E501
@register_jitable
def _Py_ISUPPER(ch):
    """
    Equivalent to the CPython macro `Py_ISUPPER()`
    """
    return _Py_ctype_table[_Py_CHARMASK(ch)] & _PY_CTF.UPPER


# Translation of:
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Include/pyctype.h#L20    # noqa: E501
@register_jitable
def _Py_ISALPHA(ch):
    """
    Equivalent to the CPython macro `Py_ISALPHA()`
    """
    return _Py_ctype_table[_Py_CHARMASK(ch)] & _PY_CTF.ALPHA


# Translation of:
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Include/pyctype.h#L21    # noqa: E501
@register_jitable
def _Py_ISDIGIT(ch):
    """
    Equivalent to the CPython macro `Py_ISDIGIT()`
    """
    return _Py_ctype_table[_Py_CHARMASK(ch)] & _PY_CTF.DIGIT


# Translation of:
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Include/pyctype.h#L22    # noqa: E501
@register_jitable
def _Py_ISXDIGIT(ch):
    """
    Equivalent to the CPython macro `Py_ISXDIGIT()`
    """
    return _Py_ctype_table[_Py_CHARMASK(ch)] & _PY_CTF.XDIGIT


# Translation of:
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Include/pyctype.h#L23    # noqa: E501
@register_jitable
def _Py_ISALNUM(ch):
    """
    Equivalent to the CPython macro `Py_ISALNUM()`
    """
    return _Py_ctype_table[_Py_CHARMASK(ch)] & _PY_CTF.ALNUM


# Translation of:
# https://github.com/python/cpython/blob/1d4b6ba19466aba0eb91c4ba01ba509acf18c723/Include/pyctype.h#L24    # noqa: E501
@register_jitable
def _Py_ISSPACE(ch):
    """
    Equivalent to the CPython macro `Py_ISSPACE()`
    """
    return _Py_ctype_table[_Py_CHARMASK(ch)] & _PY_CTF.SPACE

# End code related to/from CPython's pyctype
# ------------------------------------------------------------------------------
