"""Map MTPro2 source slots to Unicode identities and glyph names.

Project-authored interoperability mappings use TeX and Unicode semantics.
TFM supplies variant chains and assemblies; None means intentionally unencoded.
"""


# Mathematical alphabet ranges.
def _range(base, letters):
    return {chr(ord('A') + i) if letters == 'U' else chr(ord('a') + i): base + i
            for i in range(26)}


MATH_IT_UC = _range(0x1D434, 'U')
MATH_IT_LC = _range(0x1D44E, 'l')
MATH_IT_LC['h'] = 0x210E
SCRIPT_UC = _range(0x1D49C, 'U')
SCRIPT_UC.update({'B': 0x212C, 'E': 0x2130, 'F': 0x2131, 'H': 0x210B,
                  'I': 0x2110, 'L': 0x2112, 'M': 0x2133, 'R': 0x211B})
SCRIPT_LC = _range(0x1D4B6, 'l')
SCRIPT_LC.update({'e': 0x212F, 'g': 0x210A, 'o': 0x2134})
FRAK_UC = _range(0x1D504, 'U')
FRAK_UC.update({'C': 0x212D, 'H': 0x210C, 'I': None, 'R': None, 'Z': 0x2128})
# Reserve U+2111/U+211C for mt2syt Im/Re; keep Fraktur I/R unencoded.
FRAK_LC = _range(0x1D51E, 'l')
BB_UC = _range(0x1D538, 'U')
BB_UC.update({'C': 0x2102, 'H': 0x210D, 'N': 0x2115, 'P': 0x2119,
              'Q': 0x211A, 'R': 0x211D, 'Z': 0x2124})
BB_LC = _range(0x1D552, 'l')
BOLD_UC = _range(0x1D400, 'U')
BOLD_LC = _range(0x1D41A, 'l')

GREEK_IT_LC = [  # OML slot order.
    0x1D6FC, 0x1D6FD, 0x1D6FE, 0x1D6FF, 0x1D716, 0x1D701, 0x1D702, 0x1D703,
    0x1D704, 0x1D705, 0x1D706, 0x1D707, 0x1D708, 0x1D709, 0x1D70B, 0x1D70C,
    0x1D70E, 0x1D70F, 0x1D710, 0x1D719, 0x1D712, 0x1D713, 0x1D714,          # ..omega
    0x1D700, 0x1D717, 0x1D71B, 0x1D71A, 0x1D70D, 0x1D711]  # Variant forms.
# mt2mit slot contract: 0-10 are italic; 0x7F/0x80-0x89 are upright.
GREEK_UP_UC = [0x1D6E4, 0x1D6E5, 0x1D6E9, 0x1D6EC, 0x1D6EF, 0x1D6F1, 0x1D6F4,
               0x1D6F6, 0x1D6F7, 0x1D6F9, 0x1D6FA]
GREEK_IT_UC = [0x0393, 0x0394, 0x0398, 0x039B, 0x039E, 0x03A0, 0x03A3,
               0x03A5, 0x03A6, 0x03A8]
GREEK_UP_LC = [  # Upright Greek source-slot order.
    0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03F5, 0x03B6, 0x03B7, 0x03B8,
    0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03C0, 0x03C1,
    0x03C3, 0x03C4, 0x03C5, 0x03D5, 0x03C7, 0x03C8, 0x03C9,
    0x03B5, 0x03D1, 0x03D6, 0x03F1, 0x03C2, 0x03C6]

# Latin-shaped Greek aliases share glyph IDs with the matching Latin style.
# Capital theta symbols share the corresponding Theta design.
GREEK_LATIN_SHAPED_BASIC_ALIASES = {
    0x0391: 0x0041,
    0x0392: 0x0042,
    0x0395: 0x0045,
    0x0396: 0x005A,
    0x0397: 0x0048,
    0x0399: 0x0049,
    0x039A: 0x004B,
    0x039C: 0x004D,
    0x039D: 0x004E,
    0x039F: 0x004F,
    0x03A1: 0x0050,
    0x03A4: 0x0054,
    0x03A7: 0x0058,
    0x03F4: 0x0398,
    0x03BF: 0x006F,
}

GREEK_LATIN_SHAPED_MATH_ITALIC_ALIASES = {
    0x1D6E2: MATH_IT_UC['A'],
    0x1D6E3: MATH_IT_UC['B'],
    0x1D6E6: MATH_IT_UC['E'],
    0x1D6E7: MATH_IT_UC['Z'],
    0x1D6E8: MATH_IT_UC['H'],
    0x1D6EA: MATH_IT_UC['I'],
    0x1D6EB: MATH_IT_UC['K'],
    0x1D6ED: MATH_IT_UC['M'],
    0x1D6EE: MATH_IT_UC['N'],
    0x1D6F0: MATH_IT_UC['O'],
    0x1D6F2: MATH_IT_UC['P'],
    0x1D6F3: 0x1D6E9,
    0x1D6F5: MATH_IT_UC['T'],
    0x1D6F8: MATH_IT_UC['X'],
    0x1D70A: MATH_IT_LC['o'],
}

# mt2mit
MIT = {}
for i, u in enumerate(GREEK_UP_UC):
    MIT[i] = (u, None)
for i, u in enumerate(GREEK_IT_LC):
    MIT[0x0B + i] = (u, None)
MIT.update({
    0x28: (0x21BC, None), 0x29: (0x21BD, None),
    0x2A: (0x21C0, None), 0x2B: (0x21C1, None),
    0x2C: (None, 'lhook'), 0x2D: (None, 'rhook'),
    0x2E: (0x0028, 'parenleft'), 0x2F: (0x0029, 'parenright'),  # 46,47
    0x3A: (0x002E, 'period'), 0x3B: (0x002C, 'comma'),
    0x3C: (0x003C, 'less'), 0x3D: (0x002F, 'slash'), 0x3E: (0x003E, 'greater'),
    0x3F: (0x22C6, 'star'), 0x40: (0x1D715, 'partialdiff'),
    0x5B: (0x266D, 'flat'), 0x5C: (0x266E, 'natural'), 0x5D: (0x266F, 'sharp'),
    0x5E: (0x2323, 'smile'), 0x5F: (0x2322, 'frown'), 0x60: (0x2113, 'ell'),
    0x7B: (0x1D6A4, 'imath.it'), 0x7C: (0x1D6A5, 'jmath.it'),
    0x7D: (0x2118, 'weierstrass'), 0x7E: (0x1D718, 'varkappa.it'),
    0x7F: (0x03A9, 'Omega.up'),
    0x8A: (0x0021, 'exclam'), 0x8B: (0x003F, 'question'),
    0x8C: (0x005B, 'bracketleft'), 0x8D: (0x005D, 'bracketright'),
    0x8E: (0x2020, 'dagger'), 0x8F: (0x2021, 'daggerdbl'),
    0x90: (0x00A7, 'section'), 0x91: (0x00B6, 'paragraph'),
    0xB0: (None, 'varbeta.it'), 0xB1: (0x03D0, 'upvarbeta'),
    0xB2: (None, 'vardelta.it'), 0xB3: (None, 'upvardelta'),
    0xB4: (None, 'z.alt'),
    0xB5: (None, 'dbar.it'), 0xB6: (None, 'updbar'),
})
for i in range(10):
    MIT[0x30 + i] = (0x30 + i, None)  # Default math digits.
for c in range(ord('A'), ord('Z') + 1):
    MIT[c] = (MATH_IT_UC[chr(c)], None)
for c in range(ord('a'), ord('z') + 1):
    MIT[c] = (MATH_IT_LC[chr(c)], None)
for i, u in enumerate(GREEK_IT_UC):
    MIT[0x80 + i] = (u, None)
for i, u in enumerate(GREEK_UP_LC):
    slot = 0x92 + i if i < 14 else 160 + (i - 14)
    MIT[slot] = (u, None)
MIT[0xAF] = (0x03F0, 'upvarkappa')            # 175

# mt2syt
SYT = {
    0x00: (0x2212, 'minus'), 0x01: (0x22C5, 'dotmath'), 0x02: (0x00D7, 'multiply'),
    0x03: (0x2217, 'asteriskmath'), 0x04: (0x00F7, 'divide'), 0x05: (0x22C4, 'diamondmath'),
    0x06: (0x00B1, 'plusminus'), 0x07: (0x2213, 'minusplus'),
    0x08: (0x2295, None), 0x09: (0x2296, None), 0x0A: (0x2297, None),
    0x0B: (0x2298, None), 0x0C: (0x2299, None), 0x0D: (0x25CB, 'circlebig'),
    0x0E: (0x2218, 'openbullet'), 0x0F: (0x2219, 'bullet'),
    0x10: (0x224D, 'equivasymptotic'), 0x11: (0x2261, 'equivalence'),
    0x12: (0x2286, None), 0x13: (0x2287, None), 0x14: (0x2264, None), 0x15: (0x2265, None),
    0x16: (0x2AAF, 'precedesequal'), 0x17: (0x2AB0, 'followsequal'),
    0x18: (0x223C, 'similar'), 0x19: (0x2248, 'approxequal'),
    0x1A: (0x2282, None), 0x1B: (0x2283, None), 0x1C: (0x226A, 'muchless'),
    0x1D: (0x226B, 'muchgreater'), 0x1E: (0x227A, 'precedes'), 0x1F: (0x227B, 'follows'),
    0x20: (0x2190, None), 0x21: (0x2192, None), 0x22: (0x2191, None), 0x23: (0x2193, None),
    0x24: (0x2194, None), 0x25: (0x2197, None), 0x26: (0x2198, None), 0x27: (0x2243, None),
    0x28: (0x21D0, None), 0x29: (0x21D2, None), 0x2A: (0x21D1, None), 0x2B: (0x21D3, None),
    0x2C: (0x21D4, None), 0x2D: (0x2196, None), 0x2E: (0x2199, None), 0x2F: (0x221D, None),
    0x30: (0x2032, 'prime'), 0x31: (0x221E, 'infinity'),
    0x32: (0x2208, 'element'), 0x33: (0x220B, 'owner'),
    0x34: (0x25B3, 'triangle'), 0x35: (0x25BD, 'triangleinv'),
    0x36: (None, 'negationslash'), 0x37: (None, 'mapstochar'),
    0x38: (0x2200, 'universal'), 0x39: (0x2203, 'existential'),
    0x3A: (0x00AC, 'logicalnot'), 0x3B: (0x2205, 'emptyset'),
    0x3C: (0x211C, 'Rfraktur'), 0x3D: (0x2111, 'Ifraktur'),
    0x3E: (0x22A4, 'top'), 0x3F: (0x22A5, 'perpendicular'),
    0x40: (0x2135, 'aleph'),
    # MTPro2-specific symbol slots.
    0x41: (None, 'tie.sy'),
    0x42: (None, 'compose'),  # Small composition circle.
    0x43: (0x002B, 'plus'), 0x44: (0x003D, 'equal'),
    0x45: (0x20D7, 'vec.accent'),
    # mtpro2.sty: \triangleright=symbols@70, \triangleleft=symbols@71
    0x46: (0x25B7, 'triangleright'), 0x47: (0x25C1, 'triangleleft'),
    0x48: (None, 'Relbar'), 0x49: (0x003B, 'semicolon'),
    0x4A: (0x0300, 'grave.accent'), 0x4B: (0x0301, 'acute.accent'),
    0x4C: (0x030C, 'check.accent'), 0x4D: (0x0306, 'breve.accent'),
    0x4E: (0x0304, 'bar.accent'), 0x4F: (0x0302, 'hat.accent'),
    0x50: (0x0307, 'dot.accent'), 0x51: (0x0303, 'tilde.accent'),
    0x52: (0x0308, 'ddot.accent'), 0x53: (None, 'wwbar'),
    0x54: (None, 'dotup.accent'), 0x55: (None, 'ddotup.accent'),
    0x56: (0x030A, 'ring.accent'), 0x57: (0x003A, 'colon'),
    0x58: (0x2216, 'setdif'), 0x59: (0x228D, 'cupprod'), 0x5A: (0x2A40, 'capprod'),
    0x5B: (0x222A, 'union'), 0x5C: (0x2229, 'intersection'),
    0x5D: (0x228E, 'unionmulti'), 0x5E: (0x2227, 'logicaland'), 0x5F: (0x2228, 'logicalor'),
    0x60: (0x22A2, 'turnstileleft'), 0x61: (0x22A3, 'turnstileright'),
    0x62: (0x230A, 'floorleft'), 0x63: (0x230B, 'floorright'),
    0x64: (0x2308, 'ceilingleft'), 0x65: (0x2309, 'ceilingright'),
    0x66: (0x007B, 'braceleft'), 0x67: (0x007D, 'braceright'),
    0x68: (0x27E8, 'angbracketleft'), 0x69: (0x27E9, 'angbracketright'),
    0x6A: (0x007C, 'bar'), 0x6B: (0x2016, 'bardbl'),
    0x6C: (0x2195, 'arrowupdn'), 0x6D: (0x21D5, 'arrowdblupdn'),
    0x6E: (0x005C, 'backslash'), 0x6F: (0x2240, 'wreathproduct'),
    0x70: (0x221A, 'radical'), 0x71: (0x2A3F, 'coproduct.small'),
    0x72: (0x2207, 'nabla'), 0x73: (None, 'smallint'),
    0x74: (0x2294, 'unionsq'), 0x75: (0x2293, 'intersectionsq'),
    0x76: (0x2291, 'subsetsqequal'), 0x77: (0x2292, 'supersetsqequal'),
    0x78: (None, 'wbar'), 0x79: (None, 'what'), 0x7A: (None, 'wtilde'), 0x7B: (None, 'wcheck'),
    0x7C: (0x2663, 'club'), 0x7D: (0x2662, 'diamond'),
    0x7E: (0x2661, 'heart'), 0x7F: (0x2660, 'spade'),
    0x80: (0x2667, 'clubopen'), 0x81: (None, 'clubshaded'),
    0x82: (0x2664, 'spadeopen'), 0x83: (None, 'spadeshaded'),
    0x84: (0x210F, 'hbar'), 0x85: (0x2209, 'notelement'),
    0x86: (0x2220, 'angle'), 0x87: (0x2250, 'doteq'),
    0x88: (0x22A7, 'models'),  # models uses U+22A7; vDash uses U+22A8.
    0x89: (0x22C8, 'bowtie'), 0x8A: (0x2245, 'congruent'),
    0x8B: (0x21A9, None), 0x8C: (0x21AA, None), 0x8D: (0x27F5, None), 0x8E: (0x27F6, None),
    0x8F: (0x27F8, None), 0x90: (0x27F9, None), 0x91: (0x21A6, 'mapsto'),
    0x92: (0x27FC, None), 0x93: (0x27F7, None), 0x94: (0x27FA, None),
    0x95: (0x21CC, None),
    0x96: (0x226E, None), 0x97: (0x2270, None), 0x98: (0x2280, None), 0x99: (0x22E0, None),
    0x9A: (0x2284, None), 0x9B: (0x2288, None), 0x9C: (0x22E2, None), 0x9D: (0x226F, None),
    0x9E: (0x2271, None), 0x9F: (0x2281, None),
    160: (0x22E1, None), 161: (0x2285, None), 162: (0x2289, None), 163: (0x22E3, None),
    164: (0x2260, 'notequal'), 165: (0x2262, None), 166: (0x2241, None), 167: (0x2244, None),
    168: (0x2249, None), 169: (0x2247, None), 170: (0x226D, None),
    171: (0x20DB, 'dddot.accent'), 172: (0x20DC, 'ddddot.accent'),
    173: (None, 'dddotup.accent'), 174: (None, 'ddddotup.accent'),
    175: (None, 'hslash'), 176: (None, 'simarrow'), 177: (0x03DD, 'digamma'),
    178: (None, 'varland'), 179: (None, 'contraction'),
    180: (0x2254, 'coloneq'), 181: (0x2255, 'eqcolon'), 182: (0x2259, 'hateq'),
    183: (None, 'circdashbullet'), 184: (None, 'bulletdashcirc'),
    185: (None, 'braceleft.straight'), 186: (None, 'braceright.straight'),
}

# Encode text-size operators; name delimiter variants and parts from TFM recipes.
EXA_OPS = {   # slot(text): (unicode, name, display slot)
    0x50: (0x2211, 'summation', 0x58),
    0x51: (0x220F, 'product', 0x59),
    0x60: (0x2210, 'coproduct', 0x61),
    0x52: (0x222B, 'integral', 0x5A),
    0x48: (0x222E, 'contintegral', 0x49),
    0x4A: (0x2A00, 'circledot', 0x4B),
    0x4C: (0x2A01, 'circleplus', 0x4D),
    0x4E: (0x2A02, 'circlemultiply', 0x4F),
    0x53: (0x22C3, 'union.big', 0x5B),
    0x54: (0x22C2, 'intersection.big', 0x5C),
    0x55: (0x2A04, 'unionmulti.big', 0x5D),
    0x56: (0x22C0, 'logicaland.big', 0x5E),
    0x57: (0x22C1, 'logicalor.big', 0x5F),
    0x46: (0x2A06, 'unionsq.big', 0x47),
    146: (0x222C, 'iint', 147),
    148: (0x222D, 'iiint', 149),
    150: (0x222F, 'oiint', 151),
    152: (0x2230, 'oiiint', 153),
    154: (0x2232, 'cwoint', 155),
    156: (0x2233, 'awoint', 157),
    158: (0x2231, 'cwint', 159),
    142: (0x2A03, 'cupprod.big', 143),
    144: (None, 'capprod.big', 145),
    160: (None, 'slsum', 161),
    162: (None, 'slprod', 163),
    164: (None, 'slcoprod', 165),
    166: (None, 'varland.big', 167),
    168: (None, 'ast.big', 169),
    170: (0x2A0D, 'barint', 171),
    172: (0x2A0F, 'slashint', 173),
}
# Additional operator sizes: operator name -> source slots.
XL_TABLE = {  # name: (xl_slot(mt2xl 96..), XL(mt2xl 0..), XXL(mt2xl 48..), XXXL(mt2xxxl))
    'circledot': (96, 0, 48, 0),
    'circleplus': (97, 1, 49, 1),
    'circlemultiply': (98, 2, 50, 2),
    'unionsq.big': (99, 3, 51, 3),
    'union.big': (100, 4, 52, 4),
    'intersection.big': (101, 5, 53, 5),
    'unionmulti.big': (102, 6, 54, 6),
    'logicaland.big': (103, 7, 55, 7),
    'logicalor.big': (104, 8, 56, 8),
    'summation': (105, 9, 57, 9),
    'product': (106, 10, 58, 10),
    'coproduct': (107, 11, 59, 11),
    'integral': (108, 12, 60, 12),
    'contintegral': (109, 13, 61, 13),
    'cupprod.big': (110, 14, (62, 64), (14, 16)),
    'capprod.big': (111, 15, (63, 65), (15, 17)),
    'cwoint': (112, 16, 66, 18),
    'awoint': (113, 17, 67, 19),
    'cwint': (114, 18, 68, 20),
    'iint': (115, 19, 69, 21),
    'iiint': (116, 20, 70, 22),
    'oiint': (117, 21, 71, 23),
    'oiiint': (118, 22, 72, 24),
    'slsum': (119, 23, 73, 25),
    'slprod': (120, 24, 74, 26),
    'slcoprod': (121, 25, 75, 27),
    'varland.big': (122, 26, 76, (28, 29)),
    'ast.big': (123, 27, 77, 30),
    'barint': (124, 28, 78, 31),
    'slashint': (125, 29, 79, 32),
}

# Vertical delimiters: codepoint -> (base source, extension chain head).
V_DELIMS = {
    0x0028: (('mit', 46), 0x00), 0x0029: (('mit', 47), 0x01),
    0x005B: (('mit', 140), 0x02), 0x005D: (('mit', 141), 0x03),
    0x230A: (('syt', 0x62), 0x04), 0x230B: (('syt', 0x63), 0x05),
    0x2308: (('syt', 0x64), 0x06), 0x2309: (('syt', 0x65), 0x07),
    0x007B: (('syt', 0x66), 0x08), 0x007D: (('syt', 0x67), 0x09),
    0x27E8: (('syt', 0x68), 0x0A), 0x27E9: (('syt', 0x69), 0x0B),
    0x007C: (('syt', 0x6A), 0x0C), 0x2016: (('syt', 0x6B), 0x0D),
    0x002F: (('mit', 0x3D), 0x0E), 0x005C: (('syt', 0x6E), 0x0F),
    0x2191: (('syt', 0x22), 0x78), 0x2193: (('syt', 0x23), 0x79),
    0x2195: (('syt', 0x6C), 0x3F),
    0x21D1: (('syt', 0x2A), 0x7E), 0x21D3: (('syt', 0x2B), 0x7F),
    0x21D5: (('syt', 0x6D), 0x77),
    0x221A: (('syt', 0x70), 0x70),
}
# Straight-brace alternatives use their dedicated source chain heads.
V_DELIMS_ALT = {
    'braceleft.straight': (('syt', 185), 0xAE),
    'braceright.straight': (('syt', 186), 0xAF),
}

# Horizontal variants in ascending size order.
H_CHAINS = {
    0x0302: [('syt', 0x4F), ('syt', 0x79), ('exa', 98), ('exa', 99), ('exa', 100), ('exa', 128)],
    0x0303: [('syt', 0x51), ('syt', 0x7A), ('exa', 101), ('exa', 102), ('exa', 103), ('exa', 129)],
    0x030C: [('syt', 0x4C), ('syt', 0x7B), ('exa', 122), ('exa', 123), ('exa', 124), ('exa', 125)],
    0x0304: [('syt', 0x4E), ('syt', 0x78), ('syt', 0x53)],
    # Include long arrows as horizontal variants.
    0x2190: [('syt', 0x20), ('syt', 0x8D)],
    0x2192: [('syt', 0x21), ('syt', 0x8E)],
    0x2194: [('syt', 0x24), ('syt', 0x93)],
    0x21D0: [('syt', 0x28), ('syt', 0x8F)],
    0x21D2: [('syt', 0x29), ('syt', 0x90)],
    0x21D4: [('syt', 0x2C), ('syt', 0x94)],
    0x21A6: [('syt', 0x91), ('syt', 0x92)],
    0x23DC: [('exa', 190), ('exa', 191), ('exa', 192), ('exa', 193), ('exa', 194)],
}
# Named extension parts for TFM recipe references.
EXA_PIECES = {
    130: 'braceld', 131: 'bracerd', 132: 'bracelu', 133: 'braceru',
    117: 'radical.ex', 118: 'radical.tp',
    63: 'vertex.single', 119: 'vertex.double',
    120: 'arrowup.big', 121: 'arrowdown.big',
    126: 'arrowdblup.big', 127: 'arrowdbldown.big',
    136: 'braceleft.ex',
    48: 'parenleft.tp', 49: 'parenright.tp',
    64: 'parenleft.bt', 65: 'parenright.bt',
    66: 'parenleft.ex', 67: 'parenright.ex',
    50: 'bracketleft.tp', 51: 'bracketright.tp',
    52: 'bracketleft.bt', 53: 'bracketright.bt',
    54: 'bracketleft.ex', 55: 'bracketright.ex',
    56: 'braceleft.tp', 57: 'braceright.tp',
    58: 'braceleft.bt', 59: 'braceright.bt',
    60: 'braceleft.mid', 61: 'braceright.mid',
    62: 'braceright.ex', 116: 'radical.bt',
    134: 'cupprod.lhalf', 135: 'cupprod.rhalf',
}

# mt2syat (AMSa)
AMSA = {  # slot: (unicode, name)
    0x00: (0x22A1, 'boxdot'), 0x01: (0x229E, 'boxplus'), 0x02: (0x22A0, 'boxtimes'),
    0x03: (0x25A1, 'square'), 0x04: (0x25A0, 'blacksquare'), 0x05: (0x2B1D, 'centerdot'),
    0x06: (0x25CA, 'lozenge'), 0x07: (0x29EB, 'blacklozenge'),
    0x08: (0x21BB, 'circlearrowright'), 0x09: (0x21BA, 'circlearrowleft'),
    0x0B: (0x21CB, 'leftrightharpoons'), 0x0C: (0x229F, 'boxminus'),
    0x0D: (0x22A9, 'Vdash'), 0x0E: (0x22AA, 'Vvdash'), 0x0F: (0x22A8, 'vDash'),   # \vDash=AMSa@15
    0x10: (0x21A0, 'twoheadrightarrow'), 0x11: (0x219E, 'twoheadleftarrow'),
    0x12: (0x21C7, 'leftleftarrows'), 0x13: (0x21C9, 'rightrightarrows'),
    0x14: (0x21C8, 'upuparrows'), 0x15: (0x21CA, 'downdownarrows'),
    0x16: (0x21BE, 'upharpoonright'), 0x17: (0x21C2, 'downharpoonright'),
    0x18: (0x21BF, 'upharpoonleft'), 0x19: (0x21C3, 'downharpoonleft'),
    0x1A: (0x21A3, 'rightarrowtail'), 0x1B: (0x21A2, 'leftarrowtail'),
    0x1C: (0x21C6, 'leftrightarrows'), 0x1D: (0x21C4, 'rightleftarrows'),
    0x1E: (0x21B0, 'Lsh'), 0x1F: (0x21B1, 'Rsh'),
    0x20: (0x21DD, 'rightsquigarrow'), 0x21: (0x21AD, 'leftrightsquigarrow'),
    0x22: (0x21AB, 'looparrowleft'), 0x23: (0x21AC, 'looparrowright'),
    0x24: (0x2257, 'circeq'), 0x25: (0x227F, 'succsim'),
    0x26: (0x2273, 'gtrsim'), 0x27: (0x2A86, 'gtrapprox'),
    0x28: (0x22B8, 'multimap'), 0x29: (0x2234, 'therefore'), 0x2A: (0x2235, 'because'),
    0x2B: (0x2251, 'doteqdot'), 0x2C: (0x225C, 'triangleq'),
    0x2D: (0x227E, 'precsim'), 0x2E: (0x2272, 'lesssim'), 0x2F: (0x2A85, 'lessapprox'),
    0x30: (0x2A95, 'eqslantless'), 0x31: (0x2A96, 'eqslantgtr'),
    0x32: (0x22DE, 'curlyeqprec'), 0x33: (0x22DF, 'curlyeqsucc'),
    0x34: (0x227C, 'preccurlyeq'), 0x35: (0x2266, 'leqq'), 0x36: (0x2A7D, 'leqslant'),
    0x37: (0x2276, 'lessgtr'), 0x38: (0x2035, 'backprime'), 0x39: (None, 'midshaft'),
    0x3A: (0x2253, 'risingdotseq'), 0x3B: (0x2252, 'fallingdotseq'),
    0x3C: (0x227D, 'succcurlyeq'), 0x3D: (0x2267, 'geqq'), 0x3E: (0x2A7E, 'geqslant'),
    0x3F: (0x2277, 'gtrless'),
    0x40: (0x228F, 'sqsubset'), 0x41: (0x2290, 'sqsupset'),
    0x42: (0x22B3, 'vartriangleright'), 0x43: (0x22B2, 'vartriangleleft'),
    0x44: (0x22B5, 'trianglerighteq'), 0x45: (0x22B4, 'trianglelefteq'),
    0x46: (0x2605, 'bigstar'), 0x47: (0x226C, 'between'),
    0x48: (0x25BE, 'blacktriangledown'),  # Use the AMS family for all triangle directions.
    0x49: (0x25B6, 'blacktriangleright'),
    0x4A: (0x25C0, 'blacktriangleleft'), 0x4B: (None, 'rarrowhead'), 0x4C: (None, 'larrowhead'),
    0x4D: (0x25B5, 'vartriangle'), 0x4E: (0x25B4, 'blacktriangle'),
    0x4F: (0x25BF, 'triangledown.small'),
    0x50: (0x2256, 'eqcirc'), 0x51: (0x22DA, 'lesseqgtr'), 0x52: (0x22DB, 'gtreqless'),
    0x53: (0x2A8B, 'lesseqqgtr'), 0x54: (0x2A8C, 'gtreqqless'),
    0x56: (0x21DB, 'Rrightarrow'), 0x57: (0x21DA, 'Lleftarrow'),
    0x59: (0x22BB, 'veebar'), 0x5A: (0x22BC, 'barwedge'), 0x5B: (0x2A5E, 'doublebarwedge'),
    0x5D: (0x2221, 'measuredangle'), 0x5E: (0x2222, 'sphericalangle'),
    0x5F: (None, 'varpropto'),
    0x60: (None, 'smallsmile'), 0x61: (None, 'smallfrown'),
    0x62: (0x22D0, 'Subset'), 0x63: (0x22D1, 'Supset'),
    0x64: (0x22D3, 'Cup'), 0x65: (0x22D2, 'Cap'),
    0x66: (0x22CF, 'curlywedge'), 0x67: (0x22CE, 'curlyvee'),
    0x68: (0x22CB, 'leftthreetimes'), 0x69: (0x22CC, 'rightthreetimes'),
    0x6A: (0x2AC5, 'subseteqq'), 0x6B: (0x2AC6, 'supseteqq'),
    0x6C: (0x224F, 'bumpeq'), 0x6D: (0x224E, 'Bumpeq'),
    0x6E: (0x22D8, 'lll'), 0x6F: (0x22D9, 'ggg'),
    0x70: (0x231C, 'ulcorner'), 0x71: (0x231D, 'urcorner'),
    0x73: (0x24C8, 'circledS'), 0x74: (0x22D4, 'pitchfork'), 0x75: (0x2214, 'dotplus'),
    0x76: (0x223D, 'backsim'), 0x77: (0x22CD, 'backsimeq'),
    0x78: (0x231E, 'llcorner'), 0x79: (0x231F, 'lrcorner'),
    0x7B: (0x2201, 'complement'), 0x7C: (0x22BA, 'intercal'),
    0x7D: (0x229A, 'circledcirc'), 0x7E: (0x229B, 'circledast'), 0x7F: (0x229D, 'circleddash'),
    0x80: (None, 'lvertneqq'), 0x81: (None, 'gvertneqq'),
    0x88: (0x2268, 'lneqq'), 0x89: (0x2269, 'gneqq'),
    0x8A: (None, 'nleqslant'), 0x8B: (None, 'ngeqslant'),
    0x8C: (0x2A87, 'lneq'), 0x8D: (0x2A88, 'gneq'),
    0x8E: (None, 'npreceq'), 0x8F: (None, 'nsucceq'),
    0x90: (0x22E8, 'precnsim'), 0x91: (0x22E9, 'succnsim'),
    0x92: (0x22E6, 'lnsim'), 0x93: (0x22E7, 'gnsim'),
    0x94: (None, 'nleqq'), 0x95: (None, 'ngeqq'),
    0x96: (0x2AB5, 'precneqq'), 0x97: (0x2AB6, 'succneqq'),
    0x98: (0x2AB9, 'precnapprox'), 0x99: (0x2ABA, 'succnapprox'),
    0x9A: (0x2A89, 'lnapprox'), 0x9B: (0x2A8A, 'gnapprox'),
    0x9C: (None, 'nsim.ams'),
    0x9E: (0x27CB, 'diagup'), 0x9F: (0x27CD, 'diagdown'),
    # Encode subsetneq/supsetneq; expose alternate forms through cv features.
    160: (None, 'varsubsetneq'), 161: (None, 'varsupsetneq'),
    162: (None, 'nsubseteqq'), 163: (None, 'nsupseteqq'),
    164: (0x2ACB, 'subsetneqq'), 165: (0x2ACC, 'supsetneqq'),
    166: (None, 'varsubsetneqq'), 167: (None, 'varsupsetneqq'),
    168: (0x228A, 'subsetneq'), 169: (0x228B, 'supsetneq'),
    170: (None, 'nsubseteq.ams'), 171: (None, 'nsupseteq.ams'),
    172: (0x2226, 'nparallel'), 173: (0x2224, 'nmid'),
    174: (None, 'nshortmid'), 175: (None, 'nshortparallel'),
    176: (0x22AC, 'nvdash'), 177: (0x22AE, 'nVdash'),
    178: (0x22AD, 'nvDash'), 179: (0x22AF, 'nVDash'),
    180: (0x22ED, 'ntrianglerighteq'), 181: (0x22EC, 'ntrianglelefteq'),
    182: (0x22EA, 'ntriangleleft'), 183: (0x22EB, 'ntriangleright'),
    184: (0x219A, 'nleftarrow'), 185: (0x219B, 'nrightarrow'),
    186: (0x21CD, 'nLeftarrow'), 187: (0x21CF, 'nRightarrow'),
    188: (0x21CE, 'nLeftrightarrow'), 189: (0x21AE, 'nleftrightarrow'),
    190: (0x22C7, 'divideontimes'), 191: (None, 'varnothing'),
    192: (0x2204, 'nexists'),
    193: (0x2132, 'Finv'), 194: (0x2141, 'Game'), 195: (0x2127, 'mho'),
    196: (0x00F0, 'eth'), 197: (0x2242, 'eqsim'),
    198: (0x2136, 'beth'), 199: (0x2137, 'gimel'), 200: (0x2138, 'daleth'),
    201: (0x22D6, 'lessdot'), 202: (0x22D7, 'gtrdot'),
    203: (0x22C9, 'ltimes'), 204: (0x22CA, 'rtimes'),
    205: (None, 'shortmid'), 206: (None, 'shortparallel'),
    207: (None, 'thicksim'), 208: (None, 'thickapprox'),
    209: (0x224A, 'approxeq'), 210: (0x2AB8, 'succapprox'), 211: (0x2AB7, 'precapprox'),
    212: (0x21B6, 'curvearrowleft'), 213: (0x21B7, 'curvearrowright'),
    214: (0x03F6, 'backepsilon'),
    215: (None, 'nsqsubset'), 216: (None, 'nsqsupset'),
    219: (0x21E0, 'dashleftarrow'), 220: (0x21E2, 'dashrightarrow'),
    221: (None, 'leadsto'), 222: (0x25C7, 'Diamond'),
    223: (0x21C5, 'updownarrows'),
    224: (0x21F5, 'downuparrows'),
    225: (0x296E, 'updownharpoons'), 226: (0x296F, 'downupharpoons'),
    227: (0x2963, 'upupharpoons'), 228: (0x2965, 'downdownharpoons'),
    229: (None, 'undercurvearrowleft'), 230: (None, 'undercurvearrowright'),
}


# Alphabet mappings.
def alpha_map(kind):
    """kind: bb / script / frak / bold / curly"""
    out = {}
    tables = {
        'bb': (BB_UC, BB_LC, 0x1D7D8, '.bb'),
        'script': (SCRIPT_UC, SCRIPT_LC, None, '.scr'),
        'frak': (FRAK_UC, FRAK_LC, None, '.frak'),
        'bold': (BOLD_UC, BOLD_LC, 0x1D7CE, '.bf'),
        'curly': ({c: None for c in SCRIPT_UC}, {c: None for c in SCRIPT_LC}, None, '.curly'),
    }
    uc, lc, dig, sfx = tables[kind]
    for c in range(ord('A'), ord('Z') + 1):
        u = uc[chr(c)]
        out[c] = (u, chr(c) + sfx)
    for c in range(ord('a'), ord('z') + 1):
        u = lc[chr(c)]
        out[c] = (u, chr(c) + sfx)
    if dig is not None:
        for i in range(10):
            out[0x30 + i] = (dig + i, chr(0x30 + i) + sfx)
    return out
