# pylint: disable=C0302
import unicodedata

from shaperglot.checks.no_orphaned_marks import NoOrphanedMarksCheck
from shaperglot.checks.shaping_differs import ShapingDiffersCheck
from shaperglot.checks.unencoded_variants import UnencodedVariantsCheck

isoconv = {
    "aar": "aa",
    "afr": "af",
    "aka": "ak",
    "amh": "am",
    "bam": "bm",
    "ewe": "ee",
    "ful": "ff",
    "hau": "ha",
    "her": "hz",
    "ibo": "ig",
    "kik": "ki",
    "kin": "rw",
    "kon": "kg",
    "kua": "kj",
    "lin": "ln",
    "lub": "lu",
    "lug": "lg",
    "mlg": "mg",
    "nbl": "nr",
    "nde": "nd",
    "ndo": "ng",
    "nya": "ny",
    "orm": "om",
    "run": "rn",
    "sag": "sg",
    "sna": "sn",
    "som": "so",
    "sot": "st",
    "ssw": "ss",
    "swa": "sw",
    "tir": "ti",
    "tsn": "tn",
    "tso": "ts",
    "twi": "tw",
    "ven": "ve",
    "wol": "wo",
    "xho": "xh",
    "yor": "yo",
}

afr_tags = [
    "aaa",
    "aab",
    "aal",
    "aao",
    "aar",
    "aas",
    "aba",
    "abb",
    "abi",
    "abm",
    "abn",
    "abo",
    "abr",
    "abu",
    "acb",
    "acd",
    "ach",
    "acp",
    "acq",
    "acw",
    "acx",
    "acz",
    "ada",
    "add",
    "ade",
    "adh",
    "adj",
    "adq",
    "adu",
    "aeb",
    "aec",
    "ael",
    "afb",
    "afe",
    "afn",
    "afo",
    "afr",
    "aft",
    "afu",
    "agb",
    "agc",
    "agh",
    "agj",
    "agq",
    "ags",
    "aha",
    "ahg",
    "ahi",
    "ahl",
    "ahm",
    "ahn",
    "ahp",
    "ahs",
    "aik",
    "aiw",
    "aiy",
    "aja",
    "ajg",
    "aju",
    "ajw",
    "aka",
    "akd",
    "akf",
    "akp",
    "aks",
    "aku",
    "akw",
    "ala",
    "ald",
    "alf",
    "alw",
    "alz",
    "amb",
    "amf",
    "amh",
    "amj",
    "amo",
    "anc",
    "anf",
    "ank",
    "ann",
    "anu",
    "anv",
    "anw",
    "any",
    "apd",
    "aqd",
    "aqg",
    "arb",
    "arq",
    "arv",
    "ary",
    "arz",
    "asa",
    "asg",
    "asj",
    "ass",
    "asv",
    "atg",
    "ati",
    "ato",
    "atu",
    "aug",
    "auh",
    "auj",
    "aum",
    "avi",
    "avn",
    "avu",
    "awc",
    "awn",
    "awo",
    "axk",
    "ayb",
    "aye",
    "ayg",
    "ayi",
    "ayk",
    "ayl",
    "ayu",
    "azo",
    "bab",
    "baf",
    "bag",
    "bam",
    "bas",
    "bau",
    "bav",
    "baw",
    "bax",
    "bba",
    "bbe",
    "bbg",
    "bbi",
    "bbj",
    "bbk",
    "bbm",
    "bbo",
    "bbp",
    "bbq",
    "bbs",
    "bbt",
    "bbu",
    "bbw",
    "bbx",
    "bby",
    "bcb",
    "bce",
    "bcg",
    "bci",
    "bcn",
    "bcp",
    "bcq",
    "bcs",
    "bct",
    "bcv",
    "bcw",
    "bcy",
    "bcz",
    "bda",
    "bde",
    "bdh",
    "bdi",
    "bdj",
    "bdm",
    "bdn",
    "bdo",
    "bdp",
    "bds",
    "bdt",
    "bdu",
    "beb",
    "bec",
    "beh",
    "bej",
    "bem",
    "beq",
    "bes",
    "bet",
    "bev",
    "bex",
    "bez",
    "bfa",
    "bfd",
    "bff",
    "bfj",
    "bfl",
    "bfm",
    "bfo",
    "bfp",
    "bga",
    "bgf",
    "bgj",
    "bgo",
    "bgu",
    "bhs",
    "bhy",
    "bib",
    "bid",
    "bif",
    "bij",
    "bil",
    "bim",
    "bin",
    "bip",
    "biv",
    "biw",
    "biz",
    "bja",
    "bjg",
    "bji",
    "bjo",
    "bjt",
    "bju",
    "bjv",
    "bjw",
    "bka",
    "bkc",
    "bkf",
    "bkg",
    "bkh",
    "bkj",
    "bkm",
    "bko",
    "bkp",
    "bkt",
    "bkv",
    "bkw",
    "bky",
    "ble",
    "blh",
    "bli",
    "blm",
    "blo",
    "blv",
    "bly",
    "bma",
    "bmb",
    "bmd",
    "bme",
    "bmf",
    "bmg",
    "bmi",
    "bml",
    "bmo",
    "bmq",
    "bms",
    "bmv",
    "bmw",
    "bng",
    "bni",
    "bnl",
    "bnm",
    "bnx",
    "bnz",
    "bob",
    "boe",
    "bof",
    "boh",
    "bok",
    "bol",
    "bom",
    "boo",
    "bot",
    "bou",
    "bov",
    "box",
    "boy",
    "boz",
    "bpd",
    "bpj",
    "bqa",
    "bqc",
    "bqd",
    "bqf",
    "bqg",
    "bqj",
    "bqk",
    "bqm",
    "bqo",
    "bqp",
    "bqt",
    "bqu",
    "bqv",
    "bqw",
    "bqx",
    "bqz",
    "brf",
    "bri",
    "brk",
    "brl",
    "brm",
    "brt",
    "bsc",
    "bse",
    "bsf",
    "bsi",
    "bsj",
    "bsl",
    "bso",
    "bsp",
    "bsq",
    "bsr",
    "bss",
    "bst",
    "bsv",
    "bsw",
    "bsx",
    "bta",
    "btc",
    "bte",
    "btf",
    "btg",
    "btt",
    "btu",
    "bub",
    "bud",
    "buf",
    "bui",
    "buj",
    "bum",
    "bun",
    "bus",
    "buu",
    "buw",
    "bux",
    "buy",
    "buz",
    "bva",
    "bvb",
    "bvf",
    "bvg",
    "bvh",
    "bvi",
    "bvj",
    "bvm",
    "bvo",
    "bvq",
    "bvw",
    "bvx",
    "bwc",
    "bwg",
    "bwh",
    "bwj",
    "bwl",
    "bwo",
    "bwq",
    "bwr",
    "bws",
    "bwt",
    "bwu",
    "bww",
    "bwy",
    "bwz",
    "bxb",
    "bxc",
    "bxe",
    "bxg",
    "bxk",
    "bxl",
    "bxp",
    "bxq",
    "bxs",
    "bxv",
    "bxw",
    "byb",
    "byc",
    "byf",
    "byg",
    "byi",
    "byj",
    "byn",
    "byp",
    "bys",
    "byt",
    "byv",
    "bza",
    "bze",
    "bzm",
    "bzo",
    "bzv",
    "bzw",
    "bzx",
    "bzy",
    "bzz",
    "cae",
    "cbj",
    "cbo",
    "cbq",
    "cce",
    "ccg",
    "cch",
    "ccj",
    "cdr",
    "cen",
    "cet",
    "cfa",
    "cfd",
    "cfg",
    "cgg",
    "chw",
    "cib",
    "cie",
    "cjk",
    "ckl",
    "cko",
    "ckq",
    "ckx",
    "cky",
    "cla",
    "cli",
    "cll",
    "cme",
    "cmt",
    "cnu",
    "coh",
    "cop",
    "cou",
    "cpn",
    "cpo",
    "cra",
    "cry",
    "csk",
    "cug",
    "cuh",
    "cuv",
    "cwa",
    "cwb",
    "cwe",
    "cwt",
    "daa",
    "dae",
    "dag",
    "dai",
    "daj",
    "dal",
    "dam",
    "das",
    "dau",
    "dav",
    "dba",
    "dbb",
    "dbd",
    "dbg",
    "dbi",
    "dbm",
    "dbo",
    "dbp",
    "dbq",
    "dbr",
    "dbt",
    "dbu",
    "dbv",
    "dbw",
    "ddd",
    "dde",
    "ddn",
    "dds",
    "dec",
    "dee",
    "deg",
    "dek",
    "deq",
    "dez",
    "dga",
    "dgb",
    "dgd",
    "dgh",
    "dgi",
    "dgk",
    "dgl",
    "dgs",
    "dhm",
    "dhs",
    "dib",
    "dic",
    "did",
    "dig",
    "dii",
    "dik",
    "dil",
    "dim",
    "dio",
    "dip",
    "dir",
    "diu",
    "diw",
    "diz",
    "djc",
    "dje",
    "djm",
    "dks",
    "dkx",
    "dlk",
    "dma",
    "dmb",
    "dme",
    "dmm",
    "dmo",
    "dmx",
    "dne",
    "dnj",
    "dnn",
    "dno",
    "doe",
    "doh",
    "doo",
    "dop",
    "dos",
    "dot",
    "dov",
    "dow",
    "dox",
    "doy",
    "doz",
    "drb",
    "dri",
    "drs",
    "dsh",
    "dsi",
    "dsq",
    "dti",
    "dtk",
    "dtm",
    "dtn",
    "dto",
    "dts",
    "dtt",
    "dtu",
    "dua",
    "dug",
    "dur",
    "dux",
    "duz",
    "dwa",
    "dwr",
    "dya",
    "dyi",
    "dym",
    "dyo",
    "dyu",
    "dza",
    "dzg",
    "dzn",
    "ebg",
    "ebo",
    "ebr",
    "ebu",
    "efa",
    "efe",
    "efi",
    "ega",
    "ego",
    "ehu",
    "eja",
    "eka",
    "eke",
    "eki",
    "ekm",
    "eko",
    "ekp",
    "ekr",
    "elh",
    "eli",
    "elm",
    "elo",
    "ema",
    "emk",
    "emn",
    "enb",
    "enn",
    "env",
    "enw",
    "eot",
    "epi",
    "erh",
    "etb",
    "eto",
    "ets",
    "etu",
    "etx",
    "evh",
    "ewe",
    "ewo",
    "eyo",
    "eza",
    "eze",
    "fah",
    "fak",
    "fal",
    "fam",
    "fan",
    "fap",
    "fer",
    "ffm",
    "fgr",
    "fia",
    "fie",
    "fip",
    "fir",
    "fkk",
    "fli",
    "fll",
    "flr",
    "fmp",
    "fni",
    "fod",
    "fom",
    "fon",
    "fub",
    "fuc",
    "fue",
    "fuf",
    "fuh",
    "fui",
    "fuj",
    "fum",
    "fuq",
    "fuu",
    "fuv",
    "fvr",
    "fwe",
    "gaa",
    "gab",
    "gax",
    "gaz",
    "gbg",
    "gbh",
    "gbn",
    "gbo",
    "gbp",
    "gbq",
    "gbr",
    "gbs",
    "gbv",
    "gbx",
    "gby",
    "gde",
    "gdf",
    "gdi",
    "gdk",
    "gdl",
    "gdm",
    "gdu",
    "gea",
    "gec",
    "ged",
    "geg",
    "gej",
    "gek",
    "gel",
    "geq",
    "gev",
    "gew",
    "gex",
    "gey",
    "gez",
    "ggb",
    "ggu",
    "gha",
    "ghl",
    "gho",
    "gic",
    "gid",
    "gie",
    "gis",
    "gix",
    "giz",
    "gji",
    "gjn",
    "gke",
    "gkn",
    "gkp",
    "gku",
    "glc",
    "glj",
    "glo",
    "glr",
    "glu",
    "glw",
    "gly",
    "gmd",
    "gmm",
    "gmn",
    "gmv",
    "gmx",
    "gmz",
    "gna",
    "gnd",
    "gne",
    "gng",
    "gnh",
    "gnj",
    "gnk",
    "gnz",
    "goa",
    "god",
    "gof",
    "gog",
    "gol",
    "gou",
    "gow",
    "gox",
    "goy",
    "gpa",
    "gqa",
    "gqr",
    "grd",
    "grh",
    "grj",
    "grr",
    "gru",
    "grv",
    "gry",
    "gsl",
    "gso",
    "gua",
    "gud",
    "guk",
    "gur",
    "guw",
    "gux",
    "guz",
    "gvl",
    "gvm",
    "gwa",
    "gwb",
    "gwd",
    "gwe",
    "gwg",
    "gwj",
    "gwn",
    "gwr",
    "gwx",
    "gxx",
    "gya",
    "gye",
    "gyg",
    "gyi",
    "gyl",
    "gza",
    "hae",
    "hag",
    "han",
    "haq",
    "har",
    "hau",
    "hav",
    "hay",
    "hba",
    "hbb",
    "hbn",
    "hdy",
    "hed",
    "heh",
    "hem",
    "her",
    "hgm",
    "hhr",
    "hia",
    "hig",
    "hij",
    "hio",
    "hka",
    "hke",
    "hmb",
    "hna",
    "hnh",
    "hod",
    "hoe",
    "hol",
    "hom",
    "hoo",
    "hor",
    "hoz",
    "hts",
    "huc",
    "hum",
    "hwa",
    "hwo",
    "hya",
    "ibb",
    "ibe",
    "ibm",
    "ibn",
    "ibo",
    "ibr",
    "iby",
    "ica",
    "ich",
    "ida",
    "idc",
    "idd",
    "ide",
    "idr",
    "ids",
    "idu",
    "ife",
    "ifm",
    "igb",
    "ige",
    "igl",
    "igw",
    "ihi",
    "ijc",
    "ije",
    "ijj",
    "ijn",
    "ijs",
    "iki",
    "ikk",
    "ikl",
    "iko",
    "ikp",
    "ikv",
    "ikw",
    "ikx",
    "ikz",
    "ilb",
    "ilv",
    "ior",
    "iqw",
    "iri",
    "irk",
    "ish",
    "isi",
    "isn",
    "iso",
    "isu",
    "itm",
    "its",
    "itw",
    "iya",
    "iyo",
    "iyx",
    "izr",
    "izz",
    "jab",
    "jad",
    "jaf",
    "jbe",
    "jbn",
    "jbu",
    "jek",
    "jen",
    "jer",
    "jeu",
    "jgb",
    "jgk",
    "jgo",
    "jia",
    "jib",
    "jid",
    "jie",
    "jii",
    "jim",
    "jit",
    "jjr",
    "jku",
    "jle",
    "jmb",
    "jmc",
    "jmi",
    "jmr",
    "jms",
    "jni",
    "jnj",
    "job",
    "jod",
    "jow",
    "jrr",
    "jrt",
    "jub",
    "jud",
    "juh",
    "juk",
    "jum",
    "juo",
    "juu",
    "juw",
    "jwi",
    "jyy",
    "kab",
    "kad",
    "kah",
    "kai",
    "kaj",
    "kam",
    "kao",
    "kbj",
    "kbl",
    "kbn",
    "kbo",
    "kbp",
    "kbr",
    "kbs",
    "kby",
    "kbz",
    "kcc",
    "kce",
    "kcf",
    "kcg",
    "kch",
    "kci",
    "kcj",
    "kck",
    "kcm",
    "kcp",
    "kcq",
    "kcr",
    "kcs",
    "kcu",
    "kcv",
    "kcw",
    "kcx",
    "kcy",
    "kcz",
    "kdc",
    "kde",
    "kdg",
    "kdh",
    "kdi",
    "kdj",
    "kdl",
    "kdm",
    "kdn",
    "kdp",
    "kdu",
    "kdx",
    "kdz",
    "keb",
    "kec",
    "ked",
    "kef",
    "keg",
    "kel",
    "ken",
    "keo",
    "ker",
    "kes",
    "keu",
    "kez",
    "kfl",
    "kfn",
    "kfo",
    "kfz",
    "kga",
    "kgo",
    "kgt",
    "khj",
    "khq",
    "khu",
    "khx",
    "khy",
    "kia",
    "kib",
    "kid",
    "kie",
    "kik",
    "kil",
    "kin",
    "kiv",
    "kiz",
    "kka",
    "kkd",
    "kke",
    "kki",
    "kkj",
    "kkm",
    "kko",
    "kkq",
    "kkr",
    "kks",
    "kku",
    "kkw",
    "klc",
    "klf",
    "klk",
    "klo",
    "klu",
    "kma",
    "kmb",
    "kme",
    "kmi",
    "kmp",
    "kmq",
    "kmw",
    "kmy",
    "kna",
    "knc",
    "knf",
    "kng",
    "kni",
    "knk",
    "kno",
    "knp",
    "knu",
    "knw",
    "kny",
    "knz",
    "koc",
    "koe",
    "kof",
    "koh",
    "kon",
    "koo",
    "koq",
    "kot",
    "kou",
    "kov",
    "kow",
    "kpa",
    "kph",
    "kpk",
    "kpl",
    "kpo",
    "kpz",
    "kqg",
    "kqh",
    "kqk",
    "kqm",
    "kqn",
    "kqo",
    "kqp",
    "kqs",
    "kqu",
    "kqx",
    "kqy",
    "kqz",
    "krh",
    "krn",
    "krp",
    "krs",
    "krt",
    "krw",
    "krx",
    "ksa",
    "ksb",
    "ksf",
    "ksm",
    "kso",
    "ksp",
    "ksq",
    "kss",
    "kst",
    "ksv",
    "ktb",
    "ktc",
    "ktf",
    "kth",
    "ktj",
    "kty",
    "ktz",
    "kua",
    "kub",
    "kug",
    "kuh",
    "kuj",
    "kul",
    "kun",
    "kus",
    "kuw",
    "kvf",
    "kvi",
    "kvj",
    "kvm",
    "kwb",
    "kwc",
    "kwg",
    "kwl",
    "kwm",
    "kwn",
    "kwp",
    "kws",
    "kwu",
    "kwv",
    "kwy",
    "kwz",
    "kxb",
    "kxc",
    "kxh",
    "kxj",
    "kxx",
    "kya",
    "kye",
    "kyf",
    "kym",
    "kyq",
    "kza",
    "kzc",
    "kzn",
    "kzo",
    "kzr",
    "kzy",
    "laf",
    "lag",
    "lai",
    "laj",
    "lak",
    "lal",
    "lam",
    "lan",
    "lap",
    "lar",
    "las",
    "lbi",
    "lch",
    "lda",
    "ldb",
    "ldd",
    "ldg",
    "ldh",
    "ldi",
    "ldj",
    "ldk",
    "ldl",
    "ldm",
    "ldo",
    "ldp",
    "ldq",
    "lea",
    "leb",
    "led",
    "lee",
    "lef",
    "leh",
    "lej",
    "lel",
    "lem",
    "leo",
    "les",
    "lfa",
    "lgg",
    "lgm",
    "lgn",
    "lgq",
    "lgz",
    "lia",
    "lie",
    "lig",
    "lik",
    "lin",
    "lip",
    "liq",
    "liu",
    "liy",
    "liz",
    "lkb",
    "lke",
    "lko",
    "lkr",
    "lks",
    "lky",
    "lla",
    "llb",
    "llc",
    "lli",
    "lln",
    "lma",
    "lmd",
    "lme",
    "lmi",
    "lmp",
    "lmx",
    "lna",
    "lnb",
    "lnl",
    "lno",
    "lns",
    "lnu",
    "lnz",
    "lob",
    "lof",
    "log",
    "loh",
    "loi",
    "lok",
    "lol",
    "lom",
    "lon",
    "loo",
    "lop",
    "loq",
    "lor",
    "lot",
    "loz",
    "lpx",
    "lri",
    "lrm",
    "lro",
    "lse",
    "lsm",
    "lth",
    "lto",
    "lts",
    "lua",
    "lub",
    "luc",
    "lue",
    "lug",
    "luj",
    "lul",
    "lum",
    "lun",
    "luo",
    "lup",
    "luq",
    "luw",
    "lwa",
    "lwg",
    "lwo",
    "lyn",
    "mae",
    "maf",
    "mas",
    "maw",
    "mbm",
    "mbo",
    "mbu",
    "mbv",
    "mcj",
    "mck",
    "mcn",
    "mcp",
    "mcs",
    "mct",
    "mcu",
    "mcw",
    "mcx",
    "mda",
    "mdd",
    "mde",
    "mdg",
    "mdi",
    "mdj",
    "mdk",
    "mdm",
    "mdn",
    "mdp",
    "mdq",
    "mdt",
    "mdu",
    "mdw",
    "mdx",
    "mdy",
    "mea",
    "mei",
    "men",
    "meq",
    "mer",
    "mes",
    "mev",
    "mew",
    "mey",
    "mfc",
    "mfd",
    "mff",
    "mfg",
    "mfh",
    "mfi",
    "mfj",
    "mfk",
    "mfl",
    "mfm",
    "mfn",
    "mfo",
    "mfq",
    "mfu",
    "mfv",
    "mfx",
    "mfz",
    "mgb",
    "mgc",
    "mgd",
    "mge",
    "mgg",
    "mgh",
    "mgi",
    "mgj",
    "mgn",
    "mgo",
    "mgq",
    "mgr",
    "mgs",
    "mgv",
    "mgw",
    "mgy",
    "mgz",
    "mhb",
    "mhd",
    "mhi",
    "mhk",
    "mhm",
    "mho",
    "mhw",
    "mif",
    "mij",
    "mje",
    "mjh",
    "mjs",
    "mka",
    "mkf",
    "mkk",
    "mkl",
    "mko",
    "mku",
    "mlb",
    "mlg",
    "mlj",
    "mlk",
    "mlo",
    "mlq",
    "mlr",
    "mls",
    "mlt",
    "mlw",
    "mma",
    "mmf",
    "mmu",
    "mmy",
    "mmz",
    "mne",
    "mnf",
    "mnh",
    "mnk",
    "mny",
    "moa",
    "moi",
    "moj",
    "mor",
    "mos",
    "mou",
    "mow",
    "moy",
    "moz",
    "mpa",
    "mpe",
    "mpg",
    "mpi",
    "mpk",
    "mqb",
    "mql",
    "mqu",
    "mrt",
    "mru",
    "msc",
    "mse",
    "msj",
    "msv",
    "msw",
    "mtb",
    "mtk",
    "mtl",
    "mua",
    "mub",
    "muc",
    "mug",
    "muh",
    "muj",
    "muo",
    "mur",
    "muu",
    "muy",
    "muz",
    "mvh",
    "mvu",
    "mvw",
    "mvz",
    "mwe",
    "mwk",
    "mwm",
    "mwn",
    "mws",
    "mwu",
    "mwz",
    "mxc",
    "mxf",
    "mxg",
    "mxh",
    "mxl",
    "mxo",
    "mxu",
    "mxx",
    "myb",
    "myc",
    "mye",
    "myf",
    "myg",
    "myj",
    "myk",
    "mym",
    "myo",
    "mys",
    "myx",
    "mzb",
    "mzd",
    "mzj",
    "mzk",
    "mzm",
    "mzv",
    "mzw",
    "naj",
    "naq",
    "nar",
    "nat",
    "naw",
    "nba",
    "nbb",
    "nbd",
    "nbh",
    "nbl",
    "nbm",
    "nbo",
    "nbp",
    "nbr",
    "nbv",
    "nbw",
    "ncr",
    "ncu",
    "nda",
    "ndb",
    "ndc",
    "ndd",
    "nde",
    "ndg",
    "ndh",
    "ndi",
    "ndj",
    "ndk",
    "ndl",
    "ndm",
    "ndn",
    "ndo",
    "ndp",
    "ndq",
    "ndr",
    "ndt",
    "ndu",
    "ndv",
    "ndw",
    "ndy",
    "ndz",
    "neb",
    "ned",
    "ney",
    "nfd",
    "nfr",
    "nfu",
    "nga",
    "ngb",
    "ngc",
    "ngd",
    "nge",
    "ngg",
    "ngh",
    "ngi",
    "ngj",
    "ngl",
    "ngn",
    "ngo",
    "ngp",
    "ngq",
    "ngs",
    "ngv",
    "ngw",
    "ngx",
    "ngy",
    "ngz",
    "nhb",
    "nhr",
    "nhu",
    "nie",
    "nih",
    "nim",
    "nin",
    "niq",
    "nix",
    "niy",
    "nja",
    "njd",
    "njj",
    "njl",
    "njr",
    "njx",
    "njy",
    "nka",
    "nkc",
    "nkn",
    "nko",
    "nkq",
    "nkt",
    "nku",
    "nkv",
    "nkw",
    "nkx",
    "nkz",
    "nla",
    "nle",
    "nlj",
    "nlo",
    "nlu",
    "nmc",
    "nmd",
    "nmg",
    "nmi",
    "nmj",
    "nml",
    "nmn",
    "nmq",
    "nmr",
    "nmz",
    "nnb",
    "nnc",
    "nne",
    "nnh",
    "nnj",
    "nnn",
    "nnq",
    "nnu",
    "nnw",
    "nnz",
    "noq",
    "now",
    "noy",
    "noz",
    "nqg",
    "nqk",
    "nql",
    "nqo",
    "nra",
    "nrb",
    "nsc",
    "nse",
    "nsg",
    "nsh",
    "nso",
    "nsx",
    "nte",
    "nti",
    "ntk",
    "ntm",
    "nto",
    "ntr",
    "nue",
    "nuh",
    "nui",
    "nuj",
    "nup",
    "nus",
    "nuu",
    "nuv",
    "nvo",
    "nwb",
    "nwe",
    "nwm",
    "nxd",
    "nxi",
    "nxo",
    "nya",
    "nyb",
    "nyc",
    "nyd",
    "nye",
    "nyf",
    "nyg",
    "nyi",
    "nyj",
    "nyk",
    "nym",
    "nyn",
    "nyo",
    "nyp",
    "nyr",
    "nyu",
    "nyy",
    "nza",
    "nzb",
    "nzd",
    "nzi",
    "nzk",
    "nzu",
    "nzy",
    "nzz",
    "obl",
    "obu",
    "oda",
    "odu",
    "ofu",
    "ogb",
    "ogc",
    "ogg",
    "ogo",
    "ogu",
    "okb",
    "okd",
    "oke",
    "oki",
    "okr",
    "oks",
    "oku",
    "okx",
    "old",
    "olm",
    "olu",
    "omi",
    "oml",
    "omt",
    "opa",
    "orc",
    "org",
    "orm",
    "orr",
    "orx",
    "oso",
    "ost",
    "otr",
    "oua",
    "oub",
    "oyd",
    "ozm",
    "pae",
    "pai",
    "pbi",
    "pbl",
    "pbn",
    "pbo",
    "pbp",
    "pbr",
    "pcn",
    "pcw",
    "pem",
    "pfe",
    "pgs",
    "phm",
    "pic",
    "pil",
    "pip",
    "piw",
    "piy",
    "pkb",
    "pko",
    "plj",
    "plr",
    "pmb",
    "pmm",
    "pmn",
    "pnd",
    "png",
    "pnl",
    "pnq",
    "pny",
    "pnz",
    "pof",
    "poy",
    "ppp",
    "pqa",
    "pug",
    "puu",
    "pwb",
    "pye",
    "pym",
    "rag",
    "ras",
    "rax",
    "reg",
    "rel",
    "rer",
    "res",
    "rif",
    "rim",
    "rin",
    "rkm",
    "rnd",
    "rng",
    "rnw",
    "rod",
    "rof",
    "rou",
    "rub",
    "ruc",
    "ruf",
    "rui",
    "ruk",
    "run",
    "ruy",
    "ruz",
    "rwk",
    "rwm",
    "rzh",
    "saa",
    "sad",
    "saf",
    "sag",
    "sak",
    "saq",
    "sav",
    "say",
    "sba",
    "sbd",
    "sbf",
    "sbj",
    "sbk",
    "sbm",
    "sbp",
    "sbs",
    "sbw",
    "sby",
    "sbz",
    "scv",
    "scw",
    "sde",
    "sdj",
    "sds",
    "seb",
    "sef",
    "seg",
    "seh",
    "sen",
    "sep",
    "seq",
    "ses",
    "sev",
    "sfw",
    "sgc",
    "sgi",
    "sgm",
    "sgw",
    "sha",
    "shc",
    "she",
    "shg",
    "shi",
    "shj",
    "shk",
    "sho",
    "shq",
    "shr",
    "shu",
    "shw",
    "shy",
    "shz",
    "sid",
    "sie",
    "sif",
    "sig",
    "sil",
    "sir",
    "siz",
    "sjg",
    "sjs",
    "skq",
    "skt",
    "sld",
    "slx",
    "smd",
    "smx",
    "sna",
    "snf",
    "sng",
    "snk",
    "snm",
    "snq",
    "snw",
    "soc",
    "sod",
    "soe",
    "soh",
    "sok",
    "som",
    "soo",
    "sop",
    "sor",
    "sos",
    "sot",
    "sox",
    "soy",
    "soz",
    "spp",
    "spy",
    "sqa",
    "sqh",
    "sqm",
    "srr",
    "ssc",
    "ssl",
    "ssn",
    "ssw",
    "ssy",
    "stj",
    "stv",
    "sub",
    "suj",
    "suk",
    "suq",
    "sur",
    "sus",
    "suw",
    "swa",
    "swb",
    "swc",
    "swf",
    "swh",
    "swj",
    "swk",
    "swn",
    "swq",
    "swy",
    "sxb",
    "sxe",
    "sxs",
    "sxw",
    "syc",
    "syi",
    "syk",
    "sym",
    "sys",
    "syx",
    "sze",
    "szg",
    "szv",
    "tag",
    "tak",
    "tal",
    "tan",
    "tap",
    "taq",
    "tax",
    "taz",
    "tbi",
    "tbm",
    "tbr",
    "tbt",
    "tbz",
    "tcc",
    "tcd",
    "tck",
    "tda",
    "tde",
    "tdk",
    "tdl",
    "tdo",
    "tdq",
    "tdv",
    "tec",
    "ted",
    "teg",
    "tek",
    "tem",
    "teo",
    "teq",
    "teu",
    "tex",
    "tey",
    "tez",
    "tfi",
    "tga",
    "tgd",
    "tgw",
    "tgy",
    "thk",
    "thu",
    "thv",
    "thy",
    "thz",
    "tia",
    "tic",
    "tig",
    "tii",
    "tik",
    "tiq",
    "tir",
    "tiv",
    "tja",
    "tjn",
    "tjo",
    "tke",
    "tkq",
    "tlj",
    "tll",
    "tlo",
    "tma",
    "tmc",
    "tms",
    "tmv",
    "tng",
    "tnr",
    "tny",
    "tod",
    "tog",
    "toh",
    "toi",
    "toq",
    "tor",
    "toz",
    "tpm",
    "tqq",
    "tqr",
    "trj",
    "tsa",
    "tsb",
    "tsc",
    "tsh",
    "tsn",
    "tso",
    "tsp",
    "tst",
    "tsv",
    "tsw",
    "ttb",
    "ttf",
    "ttj",
    "ttl",
    "ttq",
    "ttr",
    "tug",
    "tui",
    "tul",
    "tum",
    "tuq",
    "tuv",
    "tuy",
    "tuz",
    "tvd",
    "tvs",
    "tvu",
    "twi",
    "twl",
    "twn",
    "two",
    "twq",
    "twx",
    "txj",
    "tye",
    "tyi",
    "tyu",
    "tyx",
    "tzm",
    "uba",
    "ubi",
    "uda",
    "udl",
    "udu",
    "uha",
    "uiv",
    "uji",
    "ukh",
    "ukp",
    "ukq",
    "uku",
    "ukw",
    "ula",
    "ulb",
    "umb",
    "umm",
    "une",
    "urh",
    "usk",
    "uss",
    "uta",
    "uth",
    "utr",
    "uya",
    "vae",
    "vag",
    "vai",
    "vaj",
    "vau",
    "vem",
    "ven",
    "ver",
    "vid",
    "vif",
    "vig",
    "vin",
    "vit",
    "vki",
    "vkj",
    "vmk",
    "vmr",
    "vmw",
    "vor",
    "vum",
    "vun",
    "vut",
    "wal",
    "wan",
    "wav",
    "wbf",
    "wbh",
    "wbi",
    "wbj",
    "wci",
    "wdd",
    "wec",
    "weh",
    "wem",
    "wib",
    "wja",
    "wji",
    "wka",
    "wlc",
    "wle",
    "wll",
    "wlx",
    "wma",
    "wmw",
    "wni",
    "wob",
    "wof",
    "wok",
    "wol",
    "wom",
    "won",
    "woy",
    "wrn",
    "wss",
    "wti",
    "wud",
    "wum",
    "wun",
    "wwa",
    "www",
    "xab",
    "xam",
    "xan",
    "xdo",
    "xed",
    "xeg",
    "xel",
    "xho",
    "xii",
    "xkb",
    "xkg",
    "xkt",
    "xku",
    "xkv",
    "xma",
    "xmb",
    "xmc",
    "xmd",
    "xmg",
    "xmj",
    "xnz",
    "xoc",
    "xog",
    "xom",
    "xon",
    "xpe",
    "xrb",
    "xsh",
    "xsj",
    "xsm",
    "xsn",
    "xsq",
    "xtc",
    "xuo",
    "xuu",
    "xwe",
    "xwg",
    "xwl",
    "yaf",
    "yaj",
    "yal",
    "yam",
    "yao",
    "yas",
    "yat",
    "yav",
    "yax",
    "yay",
    "yaz",
    "yba",
    "ybb",
    "ybj",
    "ybl",
    "yei",
    "yel",
    "yer",
    "yes",
    "yey",
    "yko",
    "yky",
    "ymg",
    "ymk",
    "ymm",
    "yng",
    "ynq",
    "yns",
    "yom",
    "yor",
    "yot",
    "yre",
    "yul",
    "yun",
    "zag",
    "zah",
    "zaj",
    "zak",
    "zay",
    "zaz",
    "zdj",
    "zen",
    "zga",
    "zgh",
    "zhi",
    "zhw",
    "zil",
    "zim",
    "zin",
    "zir",
    "ziw",
    "ziz",
    "zmb",
    "zmf",
    "zmn",
    "zmo",
    "zmp",
    "zmq",
    "zms",
    "zmw",
    "zmx",
    "zmz",
    "zna",
    "zne",
    "zns",
    "zrn",
    "zua",
    "zul",
    "zuy",
    "zwa",
]

unencoded_variants = {
    "tod_Latn": ["ʋ", "Ʋ"],
    "xpe_Latn": ["Ɓ"],
    "lom_Latn": ["Ɓ"],
    "dnj_Latn": ["Ɓ"],
    "gaa_Latn": ["Ʃ", "Ʒ"],
}

available_languages = {isoconv.get(tag, tag) + "_Latn": tag for tag in afr_tags}


class AfricanLatinProvider:
    @classmethod
    def fill(cls, language):
        if language["script"] != "Latn":
            return
        if language["id"] not in available_languages:
            return

        exemplar_chars = language.get("exemplarChars", {})
        bases = exemplar_chars.get("base", "").split() or []
        auxiliary = exemplar_chars.get("auxiliary", "").split() or []

        language["shaperglot_checks"].extend(
            cls.build_no_orphaned_marks(bases, auxiliary)
        )
        language["shaperglot_checks"].extend(
            cls.build_unencoded_variants(language, bases, auxiliary)
        )
        language["shaperglot_checks"].extend(cls.build_sd_smcp(bases, auxiliary))

    @classmethod
    def build_no_orphaned_marks(cls, bases, auxiliary):
        basemarks = []
        for base in bases:
            if len(base) > 1:
                unpack = base.replace("{", "").replace("}", "")
                for char in unpack:
                    if unicodedata.combining(char):
                        basemarks.append(unpack)
        for aux in auxiliary:
            if len(aux) > 1:
                unpack = aux.replace("{", "").replace("}", "")
                for char in unpack:
                    if unicodedata.combining(char):
                        basemarks.append(unpack)
        basemark_string = "".join(basemarks)
        if not basemark_string:
            return
        yield NoOrphanedMarksCheck(
            {"check": "no_orphaned_marks", "input": {"text": basemark_string}}
        )
        yield NoOrphanedMarksCheck(
            {
                "check": "no_orphaned_marks",
                "input": {"text": basemark_string, "features": {"smcp": True}},
                "conditions": {"features": ['smcp']},
            }
        )

    @classmethod
    def build_unencoded_variants(cls, language, bases, auxiliary):
        if "Ŋ" in bases or 'Ŋ' in auxiliary:
            yield UnencodedVariantsCheck(
                {"check": "unencoded_variants", "input": {"text": 'Ŋ'}}
            )
        for char in unencoded_variants.get(language["id"], []):
            yield UnencodedVariantsCheck(
                {"check": "unencoded_variants", "input": {"text": char}}
            )

    @classmethod
    def build_sd_smcp(cls, bases, auxiliary):
        for letter in bases + auxiliary:
            if len(letter) == 1 and unicodedata.category(letter) == 'Ll':
                yield ShapingDiffersCheck(
                    {
                        'check': 'shaping_differs',
                        'inputs': [
                            {'text': letter},
                            {'text': letter, 'features': {'smcp': True}},
                        ],
                        'conditions': {'features': ['smcp']},
                        'rationale': "Requires Small-cap: " + letter,
                    }
                )
