text:
"""
    The 2- and 3-letter ISO 639 language codes.
"""
const IsoLanguageTable =
         "aa" * "aar" * # Afar
         "ab" * "abk" * # Abkhazian
         "ae" * "ave" * # Avestan
         "af" * "afr" * # Afrikaans
         "ak" * "aka" * # Akan
         "am" * "amh" * # Amharic
         "an" * "arg" * # Aragonese
         "ar" * "ara" * # Arabic
         "as" * "asm" * # Assamese
         "av" * "ava" * # Avaric
         "ay" * "aym" * # Aymara
         "az" * "aze" * # Azerbaijani
         "ba" * "bak" * # Bashkir
         "be" * "bel" * # Belarusian
         "bg" * "bul" * # Bulgarian
         "bh" * "bih" * # Bihari
         "bi" * "bis" * # Bislama
         "bm" * "bam" * # Bambara
         "bn" * "ben" * # Bengali
         "bo" * "bod" * # Tibetan
         "br" * "bre" * # Breton
         "bs" * "bos" * # Bosnian
         "ca" * "cat" * # Catalan
         "ce" * "che" * # Chechen
         "ch" * "cha" * # Chamorro
         "co" * "cos" * # Corsican
         "cr" * "cre" * # Cree
         "cs" * "ces" * # Czech
         "cu" * "chu" * # Church Slavic
         "cv" * "chv" * # Chuvash
         "cy" * "cym" * # Welsh
         "da" * "dan" * # Danish
         "de" * "deu" * # German
         "dv" * "div" * # Divehi
         "dz" * "dzo" * # Dzongkha
         "ee" * "ewe" * # Ewe
         "el" * "ell" * # Greek
         "en" * "eng" * # English
         "eo" * "epo" * # Esperanto
         "es" * "spa" * # Spanish
         "et" * "est" * # Estonian
         "eu" * "eus" * # Basque
         "fa" * "fas" * # Persian
         "ff" * "ful" * # Fulah
         "fi" * "fin" * # Finnish
         "fj" * "fij" * # Fijian
         "fo" * "fao" * # Faroese
         "fr" * "fra" * # French
         "fy" * "fry" * # Frisian
         "ga" * "gle" * # Irish
         "gd" * "gla" * # Scottish Gaelic
         "gl" * "glg" * # Gallegan
         "gn" * "grn" * # Guarani
         "gu" * "guj" * # Gujarati
         "gv" * "glv" * # Manx
         "ha" * "hau" * # Hausa
         "he" * "heb" * # Hebrew
         "hi" * "hin" * # Hindi
         "ho" * "hmo" * # Hiri Motu
         "hr" * "hrv" * # Croatian
         "ht" * "hat" * # Haitian
         "hu" * "hun" * # Hungarian
         "hy" * "hye" * # Armenian
         "hz" * "her" * # Herero
         "ia" * "ina" * # Interlingua
         "id" * "ind" * # Indonesian
         "ie" * "ile" * # Interlingue
         "ig" * "ibo" * # Igbo
         "ii" * "iii" * # Sichuan Yi
         "ik" * "ipk" * # Inupiaq
         "id" * "ind" * # Indonesian (new)
         "io" * "ido" * # Ido
         "is" * "isl" * # Icelandic
         "it" * "ita" * # Italian
         "iu" * "iku" * # Inuktitut
         "ne" * "heb" * # Hebrew (new)
         "ja" * "jpn" * # Japanese
         "yi" * "yid" * # Yiddish (new)
         "jv" * "jav" * # Javanese
         "ka" * "kat" * # Georgian
         "kg" * "kon" * # Kongo
         "ki" * "kik" * # Kikuyu
         "kj" * "kua" * # Kwanyama
         "kk" * "kaz" * # Kazakh
         "kl" * "kal" * # Greenlandic
         "km" * "khm" * # Khmer
         "kn" * "kan" * # Kannada
         "ko" * "kor" * # Korean
         "kr" * "kau" * # Kanuri
         "ks" * "kas" * # Kashmiri
         "ku" * "kur" * # Kurdish
         "kv" * "kom" * # Komi
         "kw" * "cor" * # Cornish
         "ky" * "kir" * # Kirghiz
         "la" * "lat" * # Latin
         "lb" * "ltz" * # Luxembourgish
         "lg" * "lug" * # Ganda
         "li" * "lim" * # Limburgish
         "ln" * "lin" * # Lingala
         "lo" * "lao" * # Lao
         "lt" * "lit" * # Lithuanian
         "lu" * "lub" * # Luba-Katanga
         "lv" * "lav" * # Latvian
         "mg" * "mlg" * # Malagasy
         "mh" * "mah" * # Marshallese
         "mi" * "mri" * # Maori
         "mk" * "mkd" * # Macedonian
         "ml" * "mal" * # Malayalam
         "mn" * "mon" * # Mongolian
         "mo" * "mol" * # Moldavian
         "mr" * "mar" * # Marathi
         "ms" * "msa" * # Malay
         "mt" * "mlt" * # Maltese
         "my" * "mya" * # Burmese
         "na" * "nau" * # Nauru
         "nb" * "nob" * # Norwegian Bokmål
         "nd" * "nde" * # North Ndebele
         "ne" * "nep" * # Nepali
         "ng" * "ndo" * # Ndonga
         "nl" * "nld" * # Dutch
         "nn" * "nno" * # Norwegian Nynorsk
         "no" * "nor" * # Norwegian
         "nr" * "nbl" * # South Ndebele
         "nv" * "nav" * # Navajo
         "ny" * "nya" * # Nyanja
         "oc" * "oci" * # Occitan
         "oj" * "oji" * # Ojibwa
         "om" * "orm" * # Oromo
         "or" * "ori" * # Oriya
         "os" * "oss" * # Ossetian
         "pa" * "pan" * # Panjabi
         "pi" * "pli" * # Pali
         "pl" * "pol" * # Polish
         "ps" * "pus" * # Pushto
         "pt" * "por" * # Portuguese
         "qu" * "que" * # Quechua
         "rm" * "roh" * # Raeto-Romance
         "rn" * "run" * # Rundi
         "ro" * "ron" * # Romanian
         "ru" * "rus" * # Russian
         "rw" * "kin" * # Kinyarwanda
         "sa" * "san" * # Sanskrit
         "sc" * "srd" * # Sardinian
         "sd" * "snd" * # Sindhi
         "se" * "sme" * # Northern Sami
         "sg" * "sag" * # Sango
         "si" * "sin" * # Sinhalese
         "sk" * "slk" * # Slovak
         "sl" * "slv" * # Slovenian
         "sm" * "smo" * # Samoan
         "sn" * "sna" * # Shona
         "so" * "som" * # Somali
         "sq" * "sqi" * # Albanian
         "sr" * "srp" * # Serbian
         "ss" * "ssw" * # Swati
         "st" * "sot" * # Southern Sotho
         "su" * "sun" * # Sundanese
         "sv" * "swe" * # Swedish
         "sw" * "swa" * # Swahili
         "ta" * "tam" * # Tamil
         "te" * "tel" * # Telugu
         "tg" * "tgk" * # Tajik
         "th" * "tha" * # Thai
         "ti" * "tir" * # Tigrinya
         "tk" * "tuk" * # Turkmen
         "tl" * "tgl" * # Tagalog
         "tn" * "tsn" * # Tswana
         "to" * "ton" * # Tonga
         "tr" * "tur" * # Turkish
         "ts" * "tso" * # Tsonga
         "tt" * "tat" * # Tatar
         "tw" * "twi" * # Twi
         "ty" * "tah" * # Tahitian
         "ug" * "uig" * # Uighur
         "uk" * "ukr" * # Ukrainian
         "ur" * "urd" * # Urdu
         "uz" * "uzb" * # Uzbek
         "ve" * "ven" * # Venda
         "vi" * "vie" * # Vietnamese
         "vo" * "vol" * # Volapük
         "wa" * "wln" * # Walloon
         "wo" * "wol" * # Wolof
         "xh" * "xho" * # Xhosa
         "yi" * "yid" * # Yiddish
         "yo" * "yor" * # Yoruba
         "za" * "zha" * # Zhuang
         "zh" * "zho" * # Chinese
         "zu" * "zul"   # Zulu

"""
    The 2- and 3-letter ISO 3166 country codes.
"""
const IsoCountryTable =
         "AD" * "AND" * # Andorra, Principality of
         "AE" * "ARE" * # United Arab Emirates
         "AF" * "AFG" * # Afghanistan
         "AG" * "ATG" * # Antigua and Barbuda
         "AI" * "AIA" * # Anguilla
         "AL" * "ALB" * # Albania, People's Socialist Republic of
         "AM" * "ARM" * # Armenia
         "AN" * "ANT" * # Netherlands Antilles
         "AO" * "AGO" * # Angola, Republic of
         "AQ" * "ATA" * # Antarctica (the territory South of 60 deg S)
         "AR" * "ARG" * # Argentina, Argentine Republic
         "AS" * "ASM" * # American Samoa
         "AT" * "AUT" * # Austria, Republic of
         "AU" * "AUS" * # Australia, Commonwealth of
         "AW" * "ABW" * # Aruba
         "AX" * "ALA" * # Åland Islands
         "AZ" * "AZE" * # Azerbaijan, Republic of
         "BA" * "BIH" * # Bosnia and Herzegovina
         "BB" * "BRB" * # Barbados
         "BD" * "BGD" * # Bangladesh, People's Republic of
         "BE" * "BEL" * # Belgium, Kingdom of
         "BF" * "BFA" * # Burkina Faso
         "BG" * "BGR" * # Bulgaria, People's Republic of
         "BH" * "BHR" * # Bahrain, Kingdom of
         "BI" * "BDI" * # Burundi, Republic of
         "BJ" * "BEN" * # Benin, People's Republic of
         "BL" * "BLM" * # Saint Barthélemy
         "BM" * "BMU" * # Bermuda
         "BN" * "BRN" * # Brunei Darussalam
         "BO" * "BOL" * # Bolivia, Republic of
         "BQ" * "BES" * # Bonaire, Sint Eustatius and Saba
         "BR" * "BRA" * # Brazil, Federative Republic of
         "BS" * "BHS" * # Bahamas, Commonwealth of the
         "BT" * "BTN" * # Bhutan, Kingdom of
         "BV" * "BVT" * # Bouvet Island (Bouvetoya)
         "BW" * "BWA" * # Botswana, Republic of
         "BY" * "BLR" * # Belarus
         "BZ" * "BLZ" * # Belize
         "CA" * "CAN" * # Canada
         "CC" * "CCK" * # Cocos (Keeling) Islands
         "CD" * "COD" * # Congo, Democratic Republic of
         "CF" * "CAF" * # Central African Republic
         "CG" * "COG" * # Congo, People's Republic of
         "CH" * "CHE" * # Switzerland, Swiss Confederation
         "CI" * "CIV" * # Cote D'Ivoire, Ivory Coast, Republic of the
         "CK" * "COK" * # Cook Islands
         "CL" * "CHL" * # Chile, Republic of
         "CM" * "CMR" * # Cameroon, United Republic of
         "CN" * "CHN" * # China, People's Republic of
         "CO" * "COL" * # Colombia, Republic of
         "CR" * "CRI" * # Costa Rica, Republic of
         "CS" * "SCG" * # Serbia and Montenegro
         "CU" * "CUB" * # Cuba, Republic of
         "CV" * "CPV" * # Cape Verde, Republic of
         "CW" * "CUW" * # Cura\u00e7ao
         "CX" * "CXR" * # Christmas Island
         "CY" * "CYP" * # Cyprus, Republic of
         "CZ" * "CZE" * # Czech Republic
         "DE" * "DEU" * # Germany
         "DJ" * "DJI" * # Djibouti, Republic of
         "DK" * "DNK" * # Denmark, Kingdom of
         "DM" * "DMA" * # Dominica, Commonwealth of
         "DO" * "DOM" * # Dominican Republic
         "DZ" * "DZA" * # Algeria, People's Democratic Republic of
         "EC" * "ECU" * # Ecuador, Republic of
         "EE" * "EST" * # Estonia
         "EG" * "EGY" * # Egypt, Arab Republic of
         "EH" * "ESH" * # Western Sahara
         "ER" * "ERI" * # Eritrea
         "ES" * "ESP" * # Spain, Spanish State
         "ET" * "ETH" * # Ethiopia
         "FI" * "FIN" * # Finland, Republic of
         "FJ" * "FJI" * # Fiji, Republic of the Fiji Islands
         "FK" * "FLK" * # Falkland Islands (Malvinas)
         "FM" * "FSM" * # Micronesia, Federated States of
         "FO" * "FRO" * # Faeroe Islands
         "FR" * "FRA" * # France, French Republic
         "GA" * "GAB" * # Gabon, Gabonese Republic
         "GB" * "GBR" * # United Kingdom of Great Britain & N. Ireland
         "GD" * "GRD" * # Grenada
         "GE" * "GEO" * # Georgia
         "GF" * "GUF" * # French Guiana
         "GG" * "GGY" * # Guernsey
         "GH" * "GHA" * # Ghana, Republic of
         "GI" * "GIB" * # Gibraltar
         "GL" * "GRL" * # Greenland
         "GM" * "GMB" * # Gambia, Republic of the
         "GN" * "GIN" * # Guinea, Revolutionary People's Rep'c of
         "GP" * "GLP" * # Guadaloupe
         "GQ" * "GNQ" * # Equatorial Guinea, Republic of
         "GR" * "GRC" * # Greece, Hellenic Republic
         "GS" * "SGS" * # South Georgia and the South Sandwich Islands
         "GT" * "GTM" * # Guatemala, Republic of
         "GU" * "GUM" * # Guam
         "GW" * "GNB" * # Guinea-Bissau, Republic of
         "GY" * "GUY" * # Guyana, Republic of
         "HK" * "HKG" * # Hong Kong, Special Administrative Region of China
         "HM" * "HMD" * # Heard and McDonald Islands
         "HN" * "HND" * # Honduras, Republic of
         "HR" * "HRV" * # Hrvatska (Croatia)
         "HT" * "HTI" * # Haiti, Republic of
         "HU" * "HUN" * # Hungary, Hungarian People's Republic
         "ID" * "IDN" * # Indonesia, Republic of
         "IE" * "IRL" * # Ireland
         "IL" * "ISR" * # Israel, State of
         "IM" * "IMN" * # Isle of Man
         "IN" * "IND" * # India, Republic of
         "IO" * "IOT" * # British Indian Ocean Territory (Chagos Archipelago)
         "IQ" * "IRQ" * # Iraq, Republic of
         "IR" * "IRN" * # Iran, Islamic Republic of
         "IS" * "ISL" * # Iceland, Republic of
         "IT" * "ITA" * # Italy, Italian Republic
         "JE" * "JEY" * # Jersey
         "JM" * "JAM" * # Jamaica
         "JO" * "JOR" * # Jordan, Hashemite Kingdom of
         "JP" * "JPN" * # Japan
         "KE" * "KEN" * # Kenya, Republic of
         "KG" * "KGZ" * # Kyrgyz Republic
         "KH" * "KHM" * # Cambodia, Kingdom of
         "KI" * "KIR" * # Kiribati, Republic of
         "KM" * "COM" * # Comoros, Union of the
         "KN" * "KNA" * # St. Kitts and Nevis
         "KP" * "PRK" * # Korea, Democratic People's Republic of
         "KR" * "KOR" * # Korea, Republic of
         "KW" * "KWT" * # Kuwait, State of
         "KY" * "CYM" * # Cayman Islands
         "KZ" * "KAZ" * # Kazakhstan, Republic of
         "LA" * "LAO" * # Lao People's Democratic Republic
         "LB" * "LBN" * # Lebanon, Lebanese Republic
         "LC" * "LCA" * # St. Lucia
         "LI" * "LIE" * # Liechtenstein, Principality of
         "LK" * "LKA" * # Sri Lanka, Democratic Socialist Republic of
         "LR" * "LBR" * # Liberia, Republic of
         "LS" * "LSO" * # Lesotho, Kingdom of
         "LT" * "LTU" * # Lithuania
         "LU" * "LUX" * # Luxembourg, Grand Duchy of
         "LV" * "LVA" * # Latvia
         "LY" * "LBY" * # Libyan Arab Jamahiriya
         "MA" * "MAR" * # Morocco, Kingdom of
         "MC" * "MCO" * # Monaco, Principality of
         "MD" * "MDA" * # Moldova, Republic of
         "ME" * "MNE" * # Montenegro, Republic of
         "MF" * "MAF" * # Saint Martin
         "MG" * "MDG" * # Madagascar, Republic of
         "MH" * "MHL" * # Marshall Islands
         "MK" * "MKD" * # Macedonia, the former Yugoslav Republic of
         "ML" * "MLI" * # Mali, Republic of
         "MM" * "MMR" * # Myanmar
         "MN" * "MNG" * # Mongolia, Mongolian People's Republic
         "MO" * "MAC" * # Macao, Special Administrative Region of China
         "MP" * "MNP" * # Northern Mariana Islands
         "MQ" * "MTQ" * # Martinique
         "MR" * "MRT" * # Mauritania, Islamic Republic of
         "MS" * "MSR" * # Montserrat
         "MT" * "MLT" * # Malta, Republic of
         "MU" * "MUS" * # Mauritius
         "MV" * "MDV" * # Maldives, Republic of
         "MW" * "MWI" * # Malawi, Republic of
         "MX" * "MEX" * # Mexico, United Mexican States
         "MY" * "MYS" * # Malaysia
         "MZ" * "MOZ" * # Mozambique, People's Republic of
         "NA" * "NAM" * # Namibia
         "NC" * "NCL" * # New Caledonia
         "NE" * "NER" * # Niger, Republic of the
         "NF" * "NFK" * # Norfolk Island
         "NG" * "NGA" * # Nigeria, Federal Republic of
         "NI" * "NIC" * # Nicaragua, Republic of
         "NL" * "NLD" * # Netherlands, Kingdom of the
         "NO" * "NOR" * # Norway, Kingdom of
         "NP" * "NPL" * # Nepal, Kingdom of
         "NR" * "NRU" * # Nauru, Republic of
         "NU" * "NIU" * # Niue, Republic of
         "NZ" * "NZL" * # New Zealand
         "OM" * "OMN" * # Oman, Sultanate of
         "PA" * "PAN" * # Panama, Republic of
         "PE" * "PER" * # Peru, Republic of
         "PF" * "PYF" * # French Polynesia
         "PG" * "PNG" * # Papua New Guinea
         "PH" * "PHL" * # Philippines, Republic of the
         "PK" * "PAK" * # Pakistan, Islamic Republic of
         "PL" * "POL" * # Poland, Republic of Poland
         "PM" * "SPM" * # St. Pierre and Miquelon
         "PN" * "PCN" * # Pitcairn Island
         "PR" * "PRI" * # Puerto Rico
         "PS" * "PSE" * # Palestinian Territory, Occupied
         "PT" * "PRT" * # Portugal, Portuguese Republic
         "PW" * "PLW" * # Palau
         "PY" * "PRY" * # Paraguay, Republic of
         "QA" * "QAT" * # Qatar, State of
         "RE" * "REU" * # Reunion
         "RO" * "ROU" * # Romania, Socialist Republic of
         "RS" * "SRB" * # Serbia, Republic of
         "RU" * "RUS" * # Russian Federation
         "RW" * "RWA" * # Rwanda, Rwandese Republic
         "SA" * "SAU" * # Saudi Arabia, Kingdom of
         "SB" * "SLB" * # Solomon Islands
         "SC" * "SYC" * # Seychelles, Republic of
         "SD" * "SDN" * # Sudan, Democratic Republic of the
         "SE" * "SWE" * # Sweden, Kingdom of
         "SG" * "SGP" * # Singapore, Republic of
         "SH" * "SHN" * # St. Helena
         "SI" * "SVN" * # Slovenia
         "SJ" * "SJM" * # Svalbard & Jan Mayen Islands
         "SK" * "SVK" * # Slovakia (Slovak Republic)
         "SL" * "SLE" * # Sierra Leone, Republic of
         "SM" * "SMR" * # San Marino, Republic of
         "SN" * "SEN" * # Senegal, Republic of
         "SO" * "SOM" * # Somalia, Somali Republic
         "SR" * "SUR" * # Suriname, Republic of
         "SS" * "SSD" * # South Sudan
         "ST" * "STP" * # Sao Tome and Principe, Democratic Republic of
         "SV" * "SLV" * # El Salvador, Republic of
         "SX" * "SXM" * # Sint Maarten (Dutch part)
         "SY" * "SYR" * # Syrian Arab Republic
         "SZ" * "SWZ" * # Swaziland, Kingdom of
         "TC" * "TCA" * # Turks and Caicos Islands
         "TD" * "TCD" * # Chad, Republic of
         "TF" * "ATF" * # French Southern Territories
         "TG" * "TGO" * # Togo, Togolese Republic
         "TH" * "THA" * # Thailand, Kingdom of
         "TJ" * "TJK" * # Tajikistan
         "TK" * "TKL" * # Tokelau (Tokelau Islands)
         "TL" * "TLS" * # Timor-Leste, Democratic Republic of
         "TM" * "TKM" * # Turkmenistan
         "TN" * "TUN" * # Tunisia, Republic of
         "TO" * "TON" * # Tonga, Kingdom of
         "TR" * "TUR" * # Turkey, Republic of
         "TT" * "TTO" * # Trinidad and Tobago, Republic of
         "TV" * "TUV" * # Tuvalu
         "TW" * "TWN" * # Taiwan, Province of China
         "TZ" * "TZA" * # Tanzania, United Republic of
         "UA" * "UKR" * # Ukraine
         "UG" * "UGA" * # Uganda, Republic of
         "UM" * "UMI" * # United States Minor Outlying Islands
         "US" * "USA" * # United States of America
         "UY" * "URY" * # Uruguay, Eastern Republic of
         "UZ" * "UZB" * # Uzbekistan
         "VA" * "VAT" * # Holy See (Vatican City State)
         "VC" * "VCT" * # St. Vincent and the Grenadines
         "VE" * "VEN" * # Venezuela, Bolivarian Republic of
         "VG" * "VGB" * # British Virgin Islands
         "VI" * "VIR" * # US Virgin Islands
         "VN" * "VNM" * # Viet Nam, Socialist Republic of
         "VU" * "VUT" * # Vanuatu
         "WF" * "WLF" * # Wallis and Futuna Islands
         "WS" * "WSM" * # Samoa, Independent State of
         "YE" * "YEM" * # Yemen
         "YT" * "MYT" * # Mayotte
         "ZA" * "ZAF" * # South Africa, Republic of
         "ZM" * "ZMB" * # Zambia, Republic of
         "ZW" * "ZWE"   # Zimbabwe

"""
 ISO 15924 - Codes for the representation of names of scripts
"""
const IsoScriptTable =
	"Adlm" * # Adlam
	"Afak" * # Afaka
	"Aghb" * # Caucasian Albanian
	"Ahom" * # Ahom, Tai Ahom
	"Arab" * # Arabic
	"Aran" * # Arabic (Nastaliq variant)
	"Armi" * # Imperial Aramaic
	"Armn" * # Armenian
	"Avst" * # Avestan
	"Bali" * # Balinese
	"Bamu" * # Bamum
	"Bass" * # Bassa Vah
	"Batk" * # Batak
	"Beng" * # Bengali (Bangla)
	"Bhks" * # Bhaiksuki
	"Blis" * # Blissymbols
	"Bopo" * # Bopomofo
	"Brah" * # Brahmi
	"Brai" * # Braille
	"Bugi" * # Buginese
	"Buhd" * # Buhid
	"Cakm" * # Chakma
	"Cans" * # Unified Canadian Aboriginal Syllabics
	"Cari" * # Carian
	"Cham" * # Cham
	"Cher" * # Cherokee
	"Cirt" * # Cirth
	"Copt" * # Coptic
	"Cpmn" * # Cypro-Minoan
	"Cprt" * # Cypriot syllabary
	"Cyrl" * # Cyrillic
	"Cyrs" * # Cyrillic (Old Church Slavonic variant)
	"Deva" * # Devanagari (Nagari)
	"Dogr" * # Dogra
	"Dsrt" * # Deseret (Mormon)
	"Dupl" * # Duployan shorthand, Duployan stenography
	"Egyd" * # Egyptian demotic
	"Egyh" * # Egyptian hieratic
	"Egyp" * # Egyptian hieroglyphs
	"Elba" * # Elbasan
	"Ethi" * # Ethiopic (Geʻez)
	"Geok" * # Khutsuri (Asomtavruli and Nuskhuri)
	"Geor" * # Georgian (Mkhedruli and Mtavruli)
	"Glag" * # Glagolitic
	"Gong" * # Gunjala Gondi
	"Gonm" * # Masaram Gondi
	"Goth" * # Gothic
	"Gran" * # Grantha
	"Grek" * # Greek
	"Gujr" * # Gujarati
	"Guru" * # Gurmukhi
	"Hanb" * # Han with Bopomofo (alias for Han + Bopomofo)
	"Hang" * # Hangul (Hangŭl, Hangeul)
	"Hani" * # Han (Hanzi, Kanji, Hanja)
	"Hano" * # Hanunoo (Hanunóo)
	"Hans" * # Han (Simplified variant)
	"Hant" * # Han (Traditional variant)
	"Hatr" * # Hatran
	"Hebr" * # Hebrew
	"Hira" * # Hiragana
	"Hluw" * # Anatolian Hieroglyphs (Luwian Hieroglyphs, Hittite Hieroglyphs)
	"Hmng" * # Pahawh Hmong
	"Hmnp" * # Nyiakeng Puachue Hmong
	"Hrkt" * # Japanese syllabaries (alias for Hiragana + Katakana)
	"Hung" * # Old Hungarian (Hungarian Runic)
	"Inds" * # Indus (Harappan)
	"Ital" * # Old Italic (Etruscan, Oscan, etc.)
	"Jamo" * # Jamo (alias for Jamo subset of Hangul)
	"Java" * # Javanese
	"Jpan" * # Japanese (alias for Han + Hiragana + Katakana)
	"Jurc" * # Jurchen
	"Kali" * # Kayah Li
	"Kana" * # Katakana
	"Khar" * # Kharoshthi
	"Khmr" * # Khmer
	"Khoj" * # Khojki
	"Kitl" * # Khitan large script
	"Kits" * # Khitan small script
	"Knda" * # Kannada
	"Kore" * # Korean (alias for Hangul + Han)
	"Kpel" * # Kpelle
	"Kthi" * # Kaithi
	"Lana" * # Tai Tham (Lanna)
	"Laoo" * # Lao
	"Latf" * # Latin (Fraktur variant)
	"Latg" * # Latin (Gaelic variant)
	"Latn" * # Latin
	"Leke" * # Leke
	"Lepc" * # Lepcha (Róng)
	"Limb" * # Limbu
	"Lina" * # Linear A
	"Linb" * # Linear B
	"Lisu" * # Lisu (Fraser)
	"Loma" * # Loma
	"Lyci" * # Lycian
	"Lydi" * # Lydian
	"Mahj" * # Mahajani
	"Maka" * # Makasar
	"Mand" * # Mandaic, Mandaean
	"Mani" * # Manichaean
	"Marc" * # Marchen
	"Maya" * # Mayan hieroglyphs
	"Medf" * # Medefaidrin (Oberi Okaime, Oberi Ɔkaimɛ)
	"Mend" * # Mende Kikakui
	"Merc" * # Meroitic Cursive
	"Mero" * # Meroitic Hieroglyphs
	"Mlym" * # Malayalam
	"Modi" * # Modi, Moḍī
	"Mong" * # Mongolian
	"Moon" * # Moon (Moon code, Moon script, Moon type)
	"Mroo" * # Mro, Mru
	"Mtei" * # Meitei Mayek (Meithei, Meetei)
	"Mult" * # Multani
	"Mymr" * # Myanmar (Burmese)
	"Narb" * # Old North Arabian (Ancient North Arabian)
	"Nbat" * # Nabataean
	"Newa" * # Newa, Newar, Newari, Nepāla lipi
	"Nkdb" * # Naxi Dongba (na²¹ɕi³³ to³³ba²¹, Nakhi Tomba)
	"Nkgb" * # Naxi Geba (na²¹ɕi³³ gʌ²¹ba²¹, 'Na-'Khi ²Ggŏ-¹baw, Nakhi Geba)
	"Nkoo" * # N’Ko
	"Nshu" * # Nüshu
	"Ogam" * # Ogham
	"Olck" * # Ol Chiki (Ol Cemet’, Ol, Santali)
	"Orkh" * # Old Turkic, Orkhon Runic
	"Orya" * # Oriya (Odia)
	"Osge" * # Osage
	"Osma" * # Osmanya
	"Palm" * # Palmyrene
	"Pauc" * # Pau Cin Hau
	"Perm" * # Old Permic
	"Phag" * # Phags-pa
	"Phli" * # Inscriptional Pahlavi
	"Phlp" * # Psalter Pahlavi
	"Phlv" * # Book Pahlavi
	"Phnx" * # Phoenician
	"Plrd" * # Miao (Pollard)
	"Piqd" * # Klingon (KLI pIqaD)
	"Prti" * # Inscriptional Parthian
	"Qaaa" * # Reserved for private use (start)
	"Qabx" * # Reserved for private use (end)
	"Rjng" * # Rejang (Redjang, Kaganga)
	"Roro" * # Rongorongo
	"Runr" * # Runic
	"Samr" * # Samaritan
	"Sara" * # Sarati
	"Sarb" * # Old South Arabian
	"Saur" * # Saurashtra
	"Sgnw" * # SignWriting
	"Shaw" * # Shavian (Shaw)
	"Shrd" * # Sharada, Śāradā
	"Shui" * # Shuishu
	"Sidd" * # Siddham, Siddhaṃ, Siddhamātṛkā
	"Sind" * # Khudawadi, Sindhi
	"Sinh" * # Sinhala
	"Sora" * # Sora Sompeng
	"Soyo" * # Soyombo
	"Sund" * # Sundanese
	"Sylo" * # Syloti Nagri
	"Syrc" * # Syriac
	"Syre" * # Syriac (Estrangelo variant)
	"Syrj" * # Syriac (Western variant)
	"Syrn" * # Syriac (Eastern variant)
	"Tagb" * # Tagbanwa
	"Takr" * # Takri, Ṭākrī, Ṭāṅkrī
	"Tale" * # Tai Le
	"Talu" * # New Tai Lue
	"Taml" * # Tamil
	"Tang" * # Tangut
	"Tavt" * # Tai Viet
	"Telu" * # Telugu
	"Teng" * # Tengwar
	"Tfng" * # Tifinagh (Berber)
	"Tglg" * # Tagalog (Baybayin, Alibata)
	"Thaa" * # Thaana
	"Thai" * # Thai
	"Tibt" * # Tibetan
	"Tirh" * # Tirhuta
	"Ugar" * # Ugaritic
	"Vaii" * # Vai
	"Visp" * # Visible Speech
	"Wara" * # Warang Citi (Varang Kshiti)
	"Wcho" * # Wancho
	"Wole" * # Woleai
	"Xpeo" * # Old Persian
	"Xsux" * # Cuneiform, Sumero-Akkadian
	"Yiii" * # Yi
	"Zanb" * # Zanabazar Square
	"Zinh" * # Code for inherited script
	"Zmth" * # Mathematical notation
	"Zsye" * # Symbols (Emoji variant)
	"Zsym" * # Symbols
	"Zxxx" * # Code for unwritten documents
	"Zyyy" * # Code for undetermined script
	"Zzzz"   # Code for uncoded script

# convert old registered language tags to replacements
GRANDFATHERED = Dict{String,String}(
    # "tag"      => "preferred",
   "art-lojban"  => "jbo",
   "cel-gaulish" => "xtg-x-cel-gaulish",   # fallback
   "en-gb-oed"   => "en-gb-x-oed",         # fallback
   "i-ami"       => "ami",
   "i-bnn"       => "bnn",
   "i-default"   => "en-x-i-default",      # fallback
   "i-enochian"  => "und-x-i-enochian",    # fallback
   "i-hak"       => "hak",
   "i-klingon"   => "tlh",
   "i-lux"       => "lb",
   "i-mingo"     => "see-x-i-mingo",       # fallback
   "i-navajo"    => "nv",
   "i-pwn"       => "pwn",
   "i-tao"       => "tao",
   "i-tay"       => "tay",
   "i-tsu"       => "tsu",
   "no-bok"      => "nb",
   "no-nyn"      => "nn",
   "sgn-be-fr"   => "sfb",
   "sgn-be-nl"   => "vgt",
   "sgn-ch-de"   => "sgg",
   "zh-guoyu"    => "cmn",
   "zh-hakka"    => "hak",
   "zh-min"      => "nan-x-zh-min", # fallback
   "zh-min-nan"  => "nan",
   "zh-xiang"    => "hsn",
  )

# convert old language codes to new codes
OLD_TO_NEW_LANG = Dict{String,String}(
  # "old"=> "new",
    "iw" => "he",
    "ji" => "yi",
    "in" => "id")

abstract type StringDict{K,L,M} end

struct StringDict32 <: StringDict{2,3,5}
    data::String
end
struct StringDict50 <: StringDict{0,4,4}
    data::String
end

const LANGUAGE3_DICT = StringDict32(IsoLanguageTable)
const COUNTRY3_DICT = StringDict32(IsoCountryTable)
const SCRIPT_SET = StringDict50(IsoScriptTable)

function Base.getindex(d::StringDict{K,L,M}, x3::Union{T,Symbol}) where {K,L,M,T<:AbstractString}
    x = string(x3)
    length(x) != L && return isa(x3, Symbol) ? x3 : Symbol(x3)
    ix = K
    while ix >= 0 && ix % M != K+1
        fx = findnext(x, d.data, ix+1)
        ix = fx == nothing ? -1 : first(fx)
    end
    ix > 0  ? Symbol(d.data[ix-K:ix-1]) : isa(x3, Symbol) ? x3 : Symbol(x)
end

Base.keys(d::StringDict{K,L,M}) where {K,L,M} = StringKeyIterator{K,L,M}(d.data)
struct StringKeyIterator{K,L,M}
    data::String
end
Base.iterate(d::StringDict{K,L,M}) where {K,L,M} = iterate(d, 0)
function Base.iterate(d::StringDict{K,L,M}, s) where {K,L,M}
    s >= length(d.data) && return nothing
    K == 0 ? Symbol(d.data[s+1:s+L]) : Symbol(d.data[s+K+1:s+K+L]) => Symbol(d.data[s+1:s+K]), s + M
end
Base.length(d::StringDict{K,L,M}) where {K,L,M} = length(d.data) ÷ M

Base.iterate(it::StringKeyIterator{K,L,M}) where {K,L,M} = iterate(it, 1 + K)
function Base.iterate(it::StringKeyIterator{K,L,M}, s) where {K,L,M}
    s > length(it.data) && return nothing
    Symbol(it.data[s:s+L-1]), s + M
end
Base.length(it::StringKeyIterator{K,L,M}) where {K,L,M} = length(it.data) ÷ M
Base.in(x, d::StringDict{0,L,M}) where {L,M} = d[x] == Symbol("")

"""
The following translation dictionary was created essentially using the following
code snippet:
``` julia
julia> dict = Dict{String,String}()
Dict{String,String} with 0 entries

julia> function normal(s::AbstractString)
           a = split(s, "@")
           b = split(a[1], ".")
           length(a) <= 1 || a[2] == "euro" ? b[1] : b[1] * "@" * a[2]
       end
normal (generic function with 1 method)

julia> for line in eachline("/usr/share/X11/locale/locale.alias")
           a = split(lowercase(line), r"[[:space]]+", keep=false)
           if length(a) == 2 && !startswith(a[1], "#")
               endswith(a[1], ":") && (a[1] = a[1][1:end-1])
               a1 = normal(a[1])
               a2 = normal(a[2])
               if a1 != a2
                   if !haskey(dict, a1)
                       dict[a1] = a2
                       println('"', a1, '"', " => ", '"', a2, '"', ",")
                   end
               end
           end
       end
```
The codeset specifiers have been removed from the input data.
It is assumed, that the codesset has not been used to encode relevant data.
"""
const POSIX_OLD_TO_NEW = Dict(
    "posix" => "c",
    "posix-utf2" => "c",
    "c_c" => "c",
    "c" => "en_us",
    "cextend" => "en_us",
    "english_united-states" => "c",
    "a3" => "az_az",
    "a3_az" => "az_az",
    "af" => "af_za",
    "am" => "am_et",
    "ar" => "ar_aa",
    "as" => "as_in",
    "az" => "az_az",
    "be" => "be_by",
    "be@latin" => "be_by@latin",
    "bg" => "bg_bg",
    "be_bg" => "bg_bg",
    "br" => "br_fr",
    "bs" => "bs_ba",
    "ca" => "ca_es",
    "cs" => "cs_cz",
    "cs_cs" => "cs_cz",
    "cz" => "cs_cz",
    "cz_cz" => "cs_cz",
    "cy" => "cy_gb",
    "da" => "da_dk",
    "de" => "de_de",
    "ger_de" => "de_de",
    "ee" => "ee_ee",
    "el" => "el_gr",
    "en" => "en_us",
    "en_uk" => "en_gb",
    "eng_gb" => "en_gb",
    "en_zw" => "en_zs",
    "eo" => "eo_xx",
    "es" => "es_es",
    "et" => "et_ee",
    "eu" => "eu_es",
    "fa" => "fa_ir",
    "fi" => "fi_fi",
    "fo" => "fo_fo",
    "fr" => "fr_fr",
    "fre_fr" => "fr_fr",
    "ga" => "ga_ie",
    "gd" => "gd_gb",
    "gl" => "gl_es",
    "gv" => "gv_gb",
    "he" => "he_il",
    "hi" => "hi_in",
    "hne" => "hne_in",
    "hr" => "hr_hr",
    "hu" => "hu_hu",
    "in" => "id_id",
    "in_id" => "id_id",
    "is" => "is_is",
    "it" => "it_it",
    "iu" => "iu_ca",
    "iw" => "he_il",
    "iw_il" => "he_il",
    "ja" => "ja_jp",
    "jp_jp" => "ja_jp",
    "ka" => "ka_ge",
    "kl" => "kl_gl",
    "kn" => "kn_in",
    "ko" => "ko_kr",
    "ks" => "ks_in",
    "kw" => "kw_gb",
    "ky" => "ky_kg",
    "lo" => "lo_la",
    "lt" => "lt_lt",
    "lv" => "lv_lv",
    "mai" => "mai_in",
    "mi" => "mi_nz",
    "mk" => "mk_mk",
    "ml" => "ml_in",
    "mr" => "mr_in",
    "ms" => "ms_my",
    "mt" => "mt_mt",
    "nb" => "nb_no",
    "nl" => "nl_nl",
    "nn" => "nn_no",
    "no" => "no_no",
    "no_no@bokmal" => "no_no",
    "no_no@nynorsk" => "no_no",
    "nr" => "nr_za",
    "nso" => "nso_za",
    "ny" => "ny_no",
    "no@nynorsk" => "ny_no",
    "nynorsk" => "nn_no",
    "oc" => "oc_fr",
    "or" => "or_in",
    "pa" => "pa_in",
    "pd" => "pd_us",
    "ph" => "ph_ph",
    "pl" => "pl_pl",
    "pp" => "pp_an",
    "pt" => "pt_pt",
    "ro" => "ro_ro",
    "ru" => "ru_ru",
    "rw" => "rw_rw",
    "sd" => "sd_in",
    "sd@devanagari" => "sd_in@devanagari",
    "sh" => "sr_rs@latin",
    "sh_ba@bosnia" => "sr_cs",
    "sh_hr" => "hr_hr",
    "sh_yu" => "sr_rs@latin",
    "si" => "si_lk",
    "sk" => "sk_sk",
    "sl" => "sl_si",
    "sq" => "sq_al",
    "sr" => "sr_rs",
    "sr_yu" => "sr_rs@latin",
    "sr@cyrillic" => "sr_rs",
    "sr_yu@cyrillic" => "sr_rs",
    "sr@latn" => "sr_cs@latin",
    "sr_cs@latn" => "sr_cs@latin",
    "sr@latin" => "sr_rs@latin",
    "sr_rs@latn" => "sr_rs@latin",
    "ss" => "ss_za",
    "st" => "st_za",
    "sv" => "sv_se",
    "ta" => "ta_in",
    "te" => "te_in",
    "tg" => "tg_tj",
    "th" => "th_th",
    "tl" => "tl_ph",
    "tn" => "tn_za",
    "tr" => "tr_tr",
    "ts" => "ts_za",
    "tt" => "tt_ru",
    "uk" => "uk_ua",
    "ur" => "ur_in",
    "uz" => "uz_uz",
    "uz_uz@cyrillic" => "uz_uz",
    "ve" => "ve_za",
    "vi" => "vi_vn",
    "wa" => "wa_be",
    "xh" => "xh_za",
    "yi" => "yi_us",
    "zh_cn" => "zh_tw",
    "zu" => "zu_za",
    "english_uk" => "en_gb",
    "english_us" => "en_us",
    "french_france" => "fr_fr",
    "german_germany" => "de_de",
    "portuguese_brazil" => "pt_br",
    "spanish_spain" => "es_es",
    "american" => "en_us",
    "arabic" => "ar_aa",
    "bokmal" => "nb_no",
    "bokmål" => "nb_no",
    "bulgarian" => "bg_bg",
    "c-french" => "fr_ca",
    "catalan" => "ca_es",
    "chinese-s" => "zh_cn",
    "chinese-t" => "zh_tw",
    "croatian" => "hr_hr",
    "czech" => "cs_cz",
    "danish" => "da_dk",
    "dansk" => "da_dk",
    "deutsch" => "de_de",
    "dutch" => "nl_nl",
    "eesti" => "et_ee",
    "english" => "en_en",
    "estonian" => "et_ee",
    "finnish" => "fi_fi",
    "français" => "fr_fr",
    "french" => "fr_fr",
    "galego" => "gl_es",
    "galician" => "gl_es",
    "german" => "de_de",
    "greek" => "el_gr",
    "hebrew" => "he_il",
    "hrvatski" => "hr_hr",
    "hungarian" => "hu_hu",
    "icelandic" => "is_is",
    "italian" => "it_it",
    "japanese" => "ja_jp",
    "korean" => "ko_kr",
    "lithuanian" => "lt_lt",
    "norwegian" => "no_no",
    "polish" => "pl_pl",
    "portuguese" => "pt_pt",
    "romanian" => "ro_ro",
    "rumanian" => "ro_ro",
    "russian" => "ru_ru",
    "serbocroatian" => "sr_rs@latin",
    "sinhala" => "si_lk",
    "slovak" => "sk_sk",
    "slovene" => "sl_si",
    "slovenian" => "sl_si",
    "spanish" => "es_es",
    "swedish" => "sv_se",
    "turkish" => "tr_tr",
    "thai" => "th_th",
    "univ" => "en_us",
    "universal@ucs4" => "en_us",
    "iso_8859_1" => "en_us",
    "iso_8859_15" => "en_us",
    "iso8859-1" => "en_us",
    "iso-8859-1" => "en_us",
    "japan" => "ja_jp",
    "japanese-euc" => "ja_jp",
 )

"""
    Extracted from cldr_32.0.1/tools/java/org/unicode/cldr/util/data/Script_Metadata.csv
"""

const SCRIPT_OLD_NEW = Dict(
    "Common" => "Zyyy",
    "Latin" => "Latn",
    "Han" => "Hani",
    "Cyrillic" => "Cyrl",
    "Hiragana" => "Hira",
    "Katakana" => "Kana",
    "Thai" => "Thai",
    "Arabic" => "Arab",
    "Hangul" => "Hang",
    "Devanagari" => "Deva",
    "Greek" => "Grek",
    "Hebrew" => "Hebr",
    "Tamil" => "Taml",
    "Kannada" => "Knda",
    "Georgian" => "Geor",
    "Malayalam" => "Mlym",
    "Telugu" => "Telu",
    "Armenian" => "Armn",
    "Myanmar" => "Mymr",
    "Gujarati" => "Gujr",
    "Bengali" => "Beng",
    "Gurmukhi" => "Guru",
    "Lao" => "Laoo",
    "Inherited" => "Zinh",
    "Khmer" => "Khmr",
    "Tibetan" => "Tibt",
    "Sinhala" => "Sinh",
    "Ethiopic" => "Ethi",
    "Thaana" => "Thaa",
    "Oriya" => "Orya",
    "Unknown" => "Zzzz",
    "Canadian_Aboriginal" => "Cans",
    "Syriac" => "Syrc",
    "Bopomofo" => "Bopo",
    "Nko" => "Nkoo",
    "Cherokee" => "Cher",
    "Yi" => "Yiii",
    "Samaritan" => "Samr",
    "Coptic" => "Copt",
    "Mongolian" => "Mong",
    "Glagolitic" => "Glag",
    "Vai" => "Vaii",
    "Balinese" => "Bali",
    "Tifinagh" => "Tfng",
    "Bamum" => "Bamu",
    "Batak" => "Batk",
    "Cham" => "Cham",
    "Javanese" => "Java",
    "Kayah_Li" => "Kali",
    "Lepcha" => "Lepc",
    "Limbu" => "Limb",
    "Lisu" => "Lisu",
    "Mandaic" => "Mand",
    "Meetei_Mayek" => "Mtei",
    "New_Tai_Lue" => "Talu",
    "Ol_Chiki" => "Olck",
    "Saurashtra" => "Saur",
    "Sundanese" => "Sund",
    "Syloti_Nagri" => "Sylo",
    "Tai_Le" => "Tale",
    "Tai_Tham" => "Lana",
    "Tai_Viet" => "Tavt",
    "Avestan" => "Avst",
    "Brahmi" => "Brah",
    "Buginese" => "Bugi",
    "Buhid" => "Buhd",
    "Carian" => "Cari",
    "Cuneiform" => "Xsux",
    "Cypriot" => "Cprt",
    "Deseret" => "Dsrt",
    "Egyptian_Hieroglyphs" => "Egyp",
    "Gothic" => "Goth",
    "Hanunoo" => "Hano",
    "Imperial_Aramaic" => "Armi",
    "Inscriptional_Pahlavi" => "Phli",
    "Inscriptional_Parthian" => "Prti",
    "Kaithi" => "Kthi",
    "Kharoshthi" => "Khar",
    "Linear_B" => "Linb",
    "Lycian" => "Lyci",
    "Lydian" => "Lydi",
    "Ogham" => "Ogam",
    "Old_Italic" => "Ital",
    "Old_Persian" => "Xpeo",
    "Old_South_Arabian" => "Sarb",
    "Old_Turkic" => "Orkh",
    "Osmanya" => "Osma",
    "Phags_Pa" => "Phag",
    "Phoenician" => "Phnx",
    "Rejang" => "Rjng",
    "Runic" => "Runr",
    "Shavian" => "Shaw",
    "Tagalog" => "Tglg",
    "Tagbanwa" => "Tagb",
    "Ugaritic" => "Ugar",
    "Chakma" => "Cakm",
    "Meroitic_Cursive" => "Merc",
    "Meroitic_Hieroglyphs" => "Mero",
    "Miao" => "Plrd",
    "Sharada" => "Shrd",
    "Sora_Sompeng" => "Sora",
    "Takri" => "Takr",
    "Braille" => "Brai",
    "Caucasian_Albanian" => "Aghb",
    "Bassa_Vah" => "Bass",
    "Duployan" => "Dupl",
    "Elbasan" => "Elba",
    "Grantha" => "Gran",
    "Pahawh_Hmong" => "Hmng",
    "Khojki" => "Khoj",
    "Linear_A" => "Lina",
    "Mahajani" => "Mahj",
    "Manichaean" => "Mani",
    "Mende_Kikakui" => "Mend",
    "Modi" => "Modi",
    "Mro" => "Mroo",
    "Old_North_Arabian" => "Narb",
    "Nabataean" => "Nbat",
    "Palmyrene" => "Palm",
    "Pau_Cin_Hau" => "Pauc",
    "Old_Permic" => "Perm",
    "Psalter_Pahlavi" => "Phlp",
    "Siddham" => "Sidd",
    "Khudawadi" => "Sind",
    "Tirhuta" => "Tirh",
    "Warang_Citi" => "Wara",
    "Ahom" => "Ahom",
    "Anatolian_Hieroglyphs" => "Hluw",
    "Hatran" => "Hatr",
    "Multani" => "Mult",
    "Old_Hungarian" => "Hung",
    "SignWriting" => "Sgnw",
    "Adlam" => "Adlm",
    "Bhaiksuki" => "Bhks",
    "Marchen" => "Marc",
    "Osage" => "Osge",
    "Tangut" => "Tang",
    "Newa" => "Newa",
    "Masaram Gondi" => "Gonm",
    "Nushu" => "Nshu",
    "Soyombo" => "Soyo",
    "Zanabazar Square" => "Zanb",
)

"""
    Registered variants as found in cldr32.0.1/common/validity/variant.xml
"""
const VARIANTS = Set([
 "1606nict", "1694acad", "1901", "1959acad", "1994", "1996",
 "abl1943", "akuapem", "alalc97", "aluku", "ao1990", "arevela", "arevmda", "asant",
 "baku1926", "balanka", "barla", "basiceng", "bauddha", "biscayan", "biske", "bohoric", "boont",
 "colb1945", "cornu",
 "dajnko",
 "ekavsk", "emodeng",
 "fonipa", "fonnapa", "fonupa", "fonxsamp",
 "hepburn", "heploc", "hognorsk", "hsistemo",
 "ijekavsk", "itihasa",
 "jauer", "jyutping",
 "kkcor", "kociewie", "kscor",
 "laukika", "lipaw", "luna1918",
 "metelko", "monoton",
 "ndyuka", "nedis", "newfound", "njiva", "nulik",
 "osojs", "oxendict",
 "pahawh2", "pahawh3", "pahawh4", "pamaka", "petr1708", "pinyin", "polyton", "puter",
 "rigik", "rozaj", "rumgr",
 "scotland", "scouse", "simple", "solba", "sotav", "spanglis", "surmiran", "sursilv", "sutsilv",
 "tarask",
 "uccor", "ucrcor", "ulster", "unifon",
 "vaidika", "valencia", "vallader",
 "wadegile",
 "xsistemo",
])


text:
@testset "Verify updateqrfactUnblocked! for a vector" begin

    ψ = randn(100, 20)
    ϕ = randn(100)

    F = qrfactUnblocked(ψ)
    Fold = deepcopy(F)
    G = qrfactUnblocked(hcat(ψ, ϕ))
    F = updateqrfactUnblocked!(F, ϕ)
    @test norm(F.τ-G.τ)<1e-14
    @test norm(F.factors - G.factors)<1e-14
end

@testset "Verify updateqrfactUnblocked! for a matrix" begin

    ψ = randn(100, 20)
    ϕ = randn(100, 5)

    F = qrfactUnblocked(ψ)
    Fold = deepcopy(F)
    G = qrfactUnblocked(hcat(ψ, ϕ))
    F = updateqrfactUnblocked!(F, ϕ)
    @test norm(F.τ-G.τ)<1e-14
    @test norm(F.factors - G.factors)<1e-14
end

ext: jl

max_forks_repo_name: mleprovost/QROMP


text:
"""
    function download_filing(file_name::String,
        new_file::String, dest::String
    )

Download filing from https://www.sec.gov/Archives/

Parameters
* file_name: SEC file name
* new_file: new local file
"""
function download_filing(file_name::String, new_file::String, dest::String)
    # get filing from SEC
    full_url = "https://www.sec.gov/Archives/" * file_name
    text = HTTP.get(full_url).body

    # create company folder
    company_folder = joinpath(dest, split(new_file, "/")[end-1])
    if !isdir(company_folder)
        mkdir(company_folder)
    end

    # save filing
    f = open(new_file, "w")
    write(f, text)
    close(f)

    return
end

"""
    function get_metadata_files(
        time_periods::Vector{Tuple{Int64, Int64}},
        metadata_dest::String
    )::Vector{String}

Creates an array of file paths of the metadata files

Parameters
* time_periods: Vector of time periods (year, quarter) to get metadata files Vector{Tuple{year, quarter}}
* metadata_dest: Directory where metadata is stored

Returns: Vector of metadata file paths
"""
function get_metadata_files(time_periods::Vector{Tuple{Int64, Int64}}, metadata_dest::String)::Vector{String}

    # get file paths for all time get_time_periods
    file_paths = [joinpath(metadata_dest, string(i[1])*"/QTR"*string(i[2])*"/master.zip") for i in time_periods]

    return file_paths
end

  """
    function get_quarterly_filings(
        metadata_file::String;
        dest="../data/"::String,
        filing_types=["10-K", ]::Vector{String},
        download_rate=10::Int,
        skip_file=true::Bool
    )

Get quarterly filings from https://www.sec.gov/Archives/ using a metadata file

Parameters
* metadata_file: CSV file with filing metadata
* dest: Destination folder for downloaded filings
* filing_types: Types of filings to download (eg. ["10-K", "10-Q"])
* download_rate: Number of filings to download every second (limit=10)
* skip_file: If true, existing files will be skipped
"""
function get_quarterly_filings(metadata_file::String; dest="../data/"::String, filing_types=["10-K", ]::Vector{String},
                            download_rate=10::Int, skip_file=true::Bool)

    # verify download_rate is valid (less than 10 requests per second, more than 0)
    if download_rate > 10
        download_rate = 10
        println("download_rate of more than 10 per second(", download_rate, ") is not valid. download_rate has been set to 10/second.")
    end

    println("Metadata: " * metadata_file)

    df = CSV.File(metadata_file, delim="|") |> DataFrame
    df = df[∈(filing_types).(df[!, "Form Type"]), :]

    # create download folder if needed
    if !isdir(dest)
        mkdir(dest)
    end

    # download filings at 10 requests per second
    sleep_time = 1 / download_rate
    @showprogress 1 "Downloading Filings..." for row in eachrow(df)

        # check if filing already has been downloaded
        full_file = joinpath(dest, replace(row["Filename"], "edgar/data/" => ""))
        if isfile(full_file) && skip_file
            continue
        end

        # download new filing
        @async download_filing(row["Filename"], full_file, dest)

        # rest to throttle api hits to around 10/second
        sleep(sleep_time)
    end

    return
end

"""
    function get_quarterly_filings(
        start_year::Int,
        end_year::Int;
        quarters=[1,2,3,4]::Vector{Int},
        dest="../data/"::String,
        filing_types=["10-K", ]::Vector{String},
        download_rate=10::Int,
        metadata_dest="../metadata/"::String,
        skip_file=true::Bool,
        skip_metadata_file=true::Bool
    )

Get quarterly filings from https://www.sec.gov/Archives/

Parameters
* start_year: First year to download filings
* end_year: Last year to download filings
* quarters: Quarters to download filings from
* dest: Destination folder for downloaded filings
* filing_types: Types of filings to download (eg. ["10-K", "10-Q"])
* download_rate: Number of filings to download every second (limit=10)
* metadata_dest: Directory to store metadata files
* skip_file: If true, existing files will be skipped
* skip_metadata_file: If true, existing metadata files will be skipped
"""
function get_quarterly_filings(start_year::Int, end_year::Int; quarters=[1,2,3,4]::Vector{Int},
                                dest="../data/"::String, filing_types=["10-K", ]::Vector{String},
                                download_rate=10::Int, metadata_dest="../metadata/"::String,
                                skip_file=true::Bool, skip_metadata_file=true::Bool)

                                # get current year, quarter to prevent errors trying to get future data
    current_date = Dates.now()
    current_year = Dates.year(current_date)
    current_quarter = Dates.quarterofyear(current_date)

    # set end year to current year if no year is specified
    if end_year == nothing
        end_year = current_year
    end

    # get an array of dates to download metadata
    years = collect(start_year:end_year)
    time_periods = [(y, q) for y in years for q in quarters if (q <= current_quarter || y < current_year) && (q > 2 || y > 1993)]

    # make sure all the metadata is downloaded
    download_metadata_files(start_year, end_year;
                    quarters=quarters,
                    download_rate=download_rate,
                    dest=metadata_dest,
                    skip_file=skip_metadata_file)

    # download all quarterly filings
    file_paths = [joinpath(metadata_dest, string(t[1]) * "-QTR" * string(t[2]) * ".tsv") for t in time_periods]
    for file in file_paths
        get_quarterly_filings(file; dest=dest, filing_types=filing_types,
                            download_rate=download_rate, skip_file=skip_file)
    end

    return
end


text:
if begin
     try
       import FactCheck
       true
     catch
      false
     end
   end

  export using_fixtures

  immutable ApplyFixturesFlag end
  const using_fixtures = ApplyFixturesFlag()

  import FactCheck.facts
  facts(fn::Function, applyfixtures::ApplyFixturesFlag; fixture_values=false) = facts(fn, nothing, applyfixtures, fixture_values=fixture_values)
  facts(fn::Function, desc, applyfixtures::ApplyFixturesFlag; fixture_values=false) = apply_fixtures((args::Any...)->facts(( () -> fn(args...) ), desc), :facts, fixture_values=fixture_values)

  import FactCheck.context
  context(fn::Function, applyfixtures::ApplyFixturesFlag; fixture_values=false) = apply_fixtures((args::Any...)->context(( () -> fn(args...) )), :context, fixture_values=fixture_values)
  context(fn::Function, desc, applyfixtures::ApplyFixturesFlag; fixture_values=false) = apply_fixtures((args::Any...)->context(( () -> fn(args...) ), desc), :context, fixture_values=fixture_values)
end


text:
# This file is a part of Julia. License is MIT: https://julialang.org/license

"""
Experimental multithreading support.
"""
module Threads

include("threadingconstructs.jl")
include("atomics.jl")
include("locks.jl")

"""
    resize_nthreads!(A, copyvalue=A[1])

Resize the array `A` to length [`nthreads()`](@ref).   Any new
elements that are allocated are initialized to `deepcopy(copyvalue)`,
where `copyvalue` defaults to `A[1]`.

This is typically used to allocate per-thread variables, and
should be called in `__init__` if `A` is a global constant.
"""
function resize_nthreads!(A::AbstractVector, copyvalue=A[1])
    nthr = nthreads()
    nold = length(A)
    resize!(A, nthr)
    for i = nold+1:nthr
        A[i] = deepcopy(copyvalue)
    end
    return A
end

end

max_stars_repo_name: greimel/julia

max_issues_repo_name: greimel/julia

max_forks_repo_name: greimel/julia


text:
module SeisHDF
using Dates, HDF5, SeisIO, SeisIO.Quake

# imports
include("SeisHDF/imports.jl")

# constants
include("SeisHDF/constants.jl")

# auxiliary functions
include("SeisHDF/load_data.jl")
include("SeisHDF/save_data.jl")
include("SeisHDF/id_match.jl")
include("SeisHDF/get_trace_bounds.jl")
include("SeisHDF/asdf_aux.jl")

# readers
include("SeisHDF/read_asdf.jl")
include("SeisHDF/read_asdf_evt.jl")

# writers
include("SeisHDF/write_asdf.jl")

# wrappers
include("SeisHDF/read_hdf5.jl")
include("SeisHDF/write_hdf5.jl")

# scanners
include("SeisHDF/scan_hdf5.jl")
include("SeisHDF/asdf_qml.jl")

# exports
include("SeisHDF/exports.jl")
end


text:
export Vertex, DVertex, IVertex, vertex, dvertex

import Base: copy, hash, ==, <, <<

abstract type Vertex{T} end

Base.eltype(::Vertex{T}) where T= T

Base.show(io::IO, V::Type{<:Vertex{T}}) where T=
  print(io, V.name.name, (T == Any ? [] : ["{", T, "}"])...)

include("set.jl")
include("dlgraph.jl")
include("ilgraph.jl")
include("conversions.jl")

thread!(to::Vertex, from) = thread!(to, convert(typeof(to), from))

thread!(v::Vertex, xs...) = foldl(thread!, xs; init=v)

(::Type{T})(x, args...) where T <: Vertex = thread!(T(x), args...)

Base.getindex(v::Vertex, i) = inputs(v)[i]
Base.getindex(v::Vertex, i, is...) = v[i][is...]
Base.setindex!(v::Vertex, x::Vertex, i) = v.inputs[i] = x
Base.setindex!(v::Vertex, x::Vertex, i, is...) = v.inputs[i][is...] = x
Base.iterate(v::Vertex) = iterate(inputs(v))
Base.iterate(v::Vertex, state) = iterate(inputs(v), state)
Base.lastindex(v::Vertex) = lastindex(v.inputs)

function collectv(v::Vertex, vs = OASet{typeof(v)}())
  v ∈ vs && return collect(vs)
  push!(vs, v)
  foreach(v′ -> collectv(v′, vs), inputs(v))
  foreach(v′ -> collectv(v′, vs), outputs(v))
  return collect(vs)
end

function topo_up(v::Vertex, vs, seen)
  v ∈ seen && return vs
  push!(seen, v)
  foreach(v′ -> topo_up(v′, vs, seen), inputs(v))
  push!(vs, v)
end

function topo(v::Vertex)
  seen, vs = OSet{typeof(v)}(), typeof(v)[]
  for v in sort!(collectv(v), by = x -> x ≡ v)
    topo_up(v, vs, seen)
  end
  return vs
end

function isreaching(from::Vertex, to::Vertex, seen = OSet())
  to ∈ seen && return false
  push!(seen, to)
  any(v -> v ≡ from || isreaching(from, v, seen), inputs(to))
end

Base.isless(a::Vertex, b::Vertex) = isreaching(a, b)

<<(a::Vertex, b::Vertex) = a < b && !(a > b)


text:
# functions for phenotype

"""
    assign_phenotype!(group::PTGroup, gen=nothing; male=false, female=true, year=nothing, repeated=true, limit=nothing, idlist=1:group.pop.maxAnimal)

Put a phenotype to animals in generation `gen` in `group`, born in `year` (`nothing` to ignore the birth year).
If `gen` is `nothing` or a negative integer, this function gives all animals in this group.
By default, only females have phenotypes (`male=false` and `female=true`).
Also, an animal can have multiple (repeated records) due to `repeated=true`.
To limit the number of repeated reacord, use `limit` (default = `nothing`, no limitation).
Given `idlist`, only the animals in this list will have the phenotype; by default, all animals will have the phenotype.
The list has to have been sorted.
"""
function assign_phenotype!(group::PTGroup; gen::Union{Nothing,Int}=nothing, male::Bool=false, female::Bool=true, year::Union{Nothing,Int}=nothing,
   repeated::Bool=true, verbose::Bool=false, limit::Union{Nothing,Int}=nothing, idlist::Union{UnitRange{Int},Vector{Int}}=1:group.pop.maxAnimal)
   pop = group.pop
   par = group.pop.par
   if isnothing(gen)
      gencode = -1
   else
      gencode = gen
   end
   if gencode<0
      seqlist = collect(1:group.n)
      mingen = minimum(group.generation)
      maxgen = maximum(group.generation)
      cglist = Dict{Int,Int}()
      for i in mingen:maxgen
         pop.maxCG = pop.maxCG + 1
         cglist[i] = pop.maxCG
      end
   else
      seqlist = (1:group.n)[group.generation .== gencode]
      if length(seqlist)<1
         error("no such generation in this group")
      end
      pop.maxCG = pop.maxCG + 1
      cglist = Dict{Int,Int}(gen => pop.maxCG)
   end

   if !isnothing(year)
      yearlist = pop.df[group.id[seqlist],:year]
      seqlist = seqlist[yearlist .== year]
   end
   if verbose
      if length(seqlist)<1
         @warn "no animals will be phenotyped this time."
      end
   end

   _assign_phenotype!(group, seqlist, cglist, male, female, repeated, limit, idlist=idlist)

   return nothing
end

function assign_phenotype!(groups::Vector{PTGroup}; gen::Union{Nothing,Int}=nothing, male::Bool=false, female::Bool=true, year::Union{Nothing,Int}=nothing,
   repeated::Bool=true, verbose::Bool=false, limit::Union{Nothing,Int}=nothing, idlist::Union{UnitRange{Int},Vector{Int}}=1:groups[1].pop.maxAnimal)
   for group in groups
      assign_phenotype!(group, gen=gen, male=male, female=female, year=year, repeated=repeated, verbose=verbose, limit=limit, idlist=idlist)
   end
   return nothing
end

function _assign_phenotype!(group::PTGroup, seqlist::Vector{Int}, cglist::Dict{Int,Int}, male::Bool, female::Bool,
   repeated::Bool, limit::Union{Nothing,Int}; idlist::Union{UnitRange{Int},Vector{Int}}=1:group.pop.maxAnimal)
   pop = group.pop
   par = group.pop.par

   # some CG would not be assigned if repeated=false
   var_a = get_var_poly(par)
   var_g = get_var_qtl(par)
   var_pe = get_var_pe(par)
   var_e = get_var_error(par)
   sd_pe = sqrt(var_pe)
   sd_e = sqrt(var_e)
   @inbounds for seq in seqlist
      id = group.id[seq]
      if !(male && pop.df[id,:male]) && !(female && !pop.df[id,:male]) && group.pop.df[id,:alive]
         # out of condition
         continue
      end
      if length(searchsorted(idlist,id))<1
         # not on the list
         continue
      end
      gen = group.generation[seq]
      cg = cglist[gen]
      animal = pop.animal[id]
      nrec = length(animal.y)
      if isnothing(limit)
         nlim = nrec+1
      else
         nlim = limit
      end
      if (nrec==0 || (nrec>=1 && repeated)) && nrec<=nlim
         tbv = pop.df[id,:tbv]
         if nrec==0
            this_pe = sd_pe*randn()
         else
            this_pe = animal.pe[1]
         end
         this_e = sd_e*randn()
         this_y = par.mu + tbv + this_pe + this_e
         push!(animal.cg, cg)
         push!(animal.y, this_y)
         push!(animal.pe, this_pe)
         push!(animal.e, this_e)
         s = pop.df[id,:sire]
         d = pop.df[id,:dam]
         if s>0
            pop.df[s,:nrecprog] = pop.df[s,:nrecprog] + 1
         end
         if d>0
            pop.df[d,:nrecprog] = pop.df[d,:nrecprog] + 1
         end
         pop.df[id,:nrec] = length(animal.y)
         pop.df[id,:firsty] = animal.y[1]
         pop.df[id,:lasty] = animal.y[end]
         pop.df[id,:avgy] = mean(animal.y)
      end
   end

   return nothing
end


text:
"helper function to calculate a run-length encoding of a tuple type"
Base.@pure function runlength(::Type{T}) where {T <: Tuple}
    rle = Tuple{Type, Int}[]
    fieldcount(T) == 0 && return rle
    curT = fieldtype(T, 1)
    prevT = curT
    len = 1
    for i = 2:fieldcount(T)
        @inbounds curT = fieldtype(T, i)
        if curT === prevT
            len += 1
        else
            push!(rle, (prevT, len))
            prevT = curT
            len = 1
        end
    end
    push!(rle, (curT, len))
    return rle
end

"""
    Tables.eachcolumn(f, sch::Tables.Schema{names, types}, x::Union{Tables.AbstractRow, Tables.AbstractColumns})
    Tables.eachcolumn(f, sch::Tables.Schema{names, nothing}, x::Union{Tables.AbstractRow, Tables.AbstractColumns})

Takes a function `f`, table schema `sch`, `x`, which is an object that satisfies the `AbstractRow` or `AbstractColumns` interfaces;
it generates calls to get the value for each column (`Tables.getcolumn(x, nm)`) and then calls `f(val, index, name)`, where `f` is the
user-provided function, `val` is the column value (`AbstractRow`) or entire column (`AbstractColumns`), `index` is the column index as an `Int`,
and `name` is the column name as a `Symbol`.

An example using `Tables.eachcolumn` is:
```julia
rows = Tables.rows(tbl)
sch = Tables.schema(rows)
if sch === nothing
    state = iterate(rows)
    state === nothing && return
    row, st = state
    sch = Tables.schema(Tables.columnnames(row), nothing)
    while state !== nothing
        Tables.eachcolumn(sch, row) do val, i, nm
            bind!(stmt, i, val)
        end
        state = iterate(rows, st)
        state === nothing && return
        row, st = state
    end
else
    for row in rows
        Tables.eachcolumn(sch, row) do val, i, nm
            bind!(stmt, i, val)
        end
    end
end
```
Note in this example we account for the input table potentially returning `nothing` from `Tables.schema(rows)`; in that case, we start
iterating the rows, and build a partial schema using the column names from the first row `sch = Tables.schema(Tables.columnnames(row), nothing)`,
which is valid to pass to `Tables.eachcolumn`.
"""
function eachcolumn end

quot(s::Symbol) = Meta.QuoteNode(s)
quot(x::Int) = x

@inline function eachcolumn(f::Base.Callable, sch::Schema{names, types}, row) where {names, types}
    if @generated
        if length(names) < 101
            block = Expr(:block, Expr(:meta, :inline))
            for i = 1:length(names)
                push!(block.args, quote
                    f(getcolumn(row, $(fieldtype(types, i)), $i, $(quot(names[i]))), $i, $(quot(names[i])))
                end)
            end
            return block
        end
        rle = runlength(types)
        if length(rle) < 100
            block = Expr(:block, Expr(:meta, :inline))
            i = 1
            for (T, len) in rle
                push!(block.args, quote
                    for j = 0:$(len-1)
                        @inbounds f(getcolumn(row, $T, $i + j, names[$i + j]), $i + j, names[$i + j])
                    end
                end)
                i += len
            end
            b = block
        else
            b = quote
                $(Expr(:meta, :inline))
                for (i, nm) in enumerate(names)
                    f(getcolumn(row, fieldtype(types, i), i, nm), i, nm)
                end
                return
            end
        end
        # println(b)
        return b
    else
        for (i, nm) in enumerate(names)
            f(getcolumn(row, fieldtype(types, i), i, nm), i, nm)
        end
        return
    end
end

@inline function eachcolumn(f::Base.Callable, sch::Schema{names, nothing}, row) where {names}
    if @generated
        if length(names) < 100
            block = Expr(:block, Expr(:meta, :inline))
            for i = 1:length(names)
                push!(block.args, quote
                    f(getcolumn(row, $(quot(names[i]))), $i, $(quot(names[i])))
                end)
            end
            return block
        else
            b = quote
                $(Expr(:meta, :inline))
                for (i, nm) in enumerate(names)
                    f(getcolumn(row, nm), i, nm)
                end
                return
            end
            return b
        end
    else
        for (i, nm) in enumerate(names)
            f(getcolumn(row, nm), i, nm)
        end
        return
    end
end

# these are specialized `eachcolumn`s where we also want
# the indexing of `columns` to be constant propagated, so it needs to be returned from the generated function
@inline function eachcolumns(f::Base.Callable, sch::Schema{names, types}, row, columns, args...) where {names, types}
    if @generated
        if length(names) < 101
            block = Expr(:block, Expr(:meta, :inline))
            for i = 1:length(names)
                push!(block.args, quote
                    f(getcolumn(row, $(fieldtype(types, i)), $i, $(quot(names[i]))), $i, $(quot(names[i])), columns[$i], args...)
                end)
            end
            return block
        end
        rle = runlength(types)
        if length(rle) < 100
            block = Expr(:block, Expr(:meta, :inline))
            i = 1
            for (T, len) in rle
                push!(block.args, quote
                    for j = 0:$(len-1)
                        @inbounds f(getcolumn(row, $T, $i + j, names[$i + j]), $i + j, names[$i + j], columns[$i + j], args...)
                    end
                end)
                i += len
            end
            b = block
        else
            b = quote
                $(Expr(:meta, :inline))
                for (i, nm) in enumerate(names)
                    f(getcolumn(row, fieldtype(types, i), i, nm), i, nm, columns[i], args...)
                end
                return
            end
        end
        # println(b)
        return b
    else
        for (i, nm) in enumerate(names)
            f(getcolumn(row, fieldtype(types, i), i, nm), i, nm, columns[i], args...)
        end
        return
    end
end

@inline function eachcolumns(f::Base.Callable, sch::Schema{names, nothing}, row, columns, args...) where {names}
    if @generated
        if length(names) < 100
            block = Expr(:block, Expr(:meta, :inline))
            for i = 1:length(names)
                push!(block.args, quote
                    f(getcolumn(row, $(quot(names[i]))), $i, $(quot(names[i])), columns[$i], args...)
                end)
            end
            return block
        else
            b = quote
                $(Expr(:meta, :inline))
                for (i, nm) in enumerate(names)
                    f(getcolumn(row, nm), i, nm, columns[i], args...)
                end
                return
            end
            return b
        end
    else
        for (i, nm) in enumerate(names)
            f(getcolumn(row, nm), i, nm, columns[i], args...)
        end
        return
    end
end


text:
module ideal

include("general.jl")

using Unitful
using NLsolve

∑(x)=sum(x)
P(T,x,CA,i) = x[i]*general.P_Antoine(CA,T,i)
Pₜₒₜ(T,x,CA,n) = ∑(P(T,x,CA,i) for i=1:n)
y_calc(T,x,CA,i,n) = P(T,x,CA,i)/Pₜₒₜ(T,x,CA,n)

"""
    pxy(n,T,CA,xx;uni=u"Torr")

**Los campos de entrada son:**
- `n :: Int` Es el numero de componentes
- `T :: Float` Es la temperatura del sistema
- `CA :: n×3 Array{Float,2}` Constantes de Antoine para los `n` componentes
- `xx :: StepRange ó Vector` Son los puntos en el liquido donde se buscara el equilibrio con el vapor
- `uni` Son las unidades en las que se desea el resultado, por defecto esta en Torr

**Salida:**

Regresa las fracciones de liquido `x`,
las fracciones del vapor `y` y la presión `P` en Torr (`m` es la longitud de `xx`).

- `x :: n×m Array{Float64,2}` liquido
- `y :: n×m Array{Float64,2}` vapor
- `P :: m-element Array{Float64,1}` presión
"""
function pxy(n::Int,T,CA::Array,xx;uni=u"Torr")
    T = T |> u"K"
    xx = general.nc(xx...)
    m = size(xx,2)
    y = Array{Float64}(undef,n,m)
    P = Vector{Float64}(undef,m)*uni
    for i=1:m
        x = xx[:,i]
        for j=1:n
            y[j,i] = y_calc(T,x,CA,j,n)
        end
        P[i] = Pₜₒₜ(T,x,CA,n) |> uni
    end

    return xx,y,P
end

"""
    txy(n,Pobj,CA,xx;uni=u"K")

**Los campos de entrada son:**
- `n :: Int` Es el numero de componentes
- `Pobj :: Float` Es la presión del sistema
- `CA :: n×3 Array{Float,2}` Constantes de Antoine para los `n` componentes
- `xx :: StepRange ó Vector` Son los puntos en el liquido donde se buscara el equilibrio con el vapor
- `uni` Son las unidades en las que se desea el resultado, por defecto esta en grados kelvin

**Salida:**

Regresa las fracciones de liquido `x`,
las fracciones del vapor `y` y la temperatura `T` en grados kelvin (`m` es la longitud de `xx`).

- `x :: n×m Array{Float64,2}` liquido
- `y :: n×m Array{Float64,2}` vapor
- `T :: m-element Array{Float64,1}` temperatura
"""
function txy(n::Int,Pobj,CA::Array,xx;uni=u"K")
    Pobj = Pobj |> u"Torr"
    xx = general.nc(xx...)
    m = size(xx,2)
    y = Array{Float64}(undef,n,m)
    T = Vector{Float64}(undef,m)*u"K"
    Tᵢ = 300.0
    #temperatura inicial
    for i=1:n
        if xx[i,1]==1
            T[1] = general.T_Antoine(CA,Pobj,i)
            Tᵢ  = ustrip(u"K",T[1])
        end
    end
    for i = 1:m
        x = xx[:,i]
        P2ₜₒₜ(T) = Pₜₒₜ(T,x,CA,n) - ustrip(u"Torr",Pobj)
        #println(i)
        Pₛₒₗ = nlsolve(n_ary(P2ₜₒₜ),[Tᵢ])
        Tᵢ = Pₛₒₗ.zero[1]
        T[i] = Tᵢ*u"K"
        for j = 1:n
            y[j,i] = y_calc(T[i],x,CA,j,n)
        end
    end

    return xx,y,T .|> uni
end

end  # module

max_stars_repo_name: EmilioAlvizo/TermoQuimica

max_issues_repo_name: EmilioAlvizo/TermoQuimica

max_forks_repo_name: EmilioAlvizo/TermoQuimica


text:
export Problem, loss, lossgrad, lossngrad, jacobian, valueandjacobian
export project, normcs, sizen, sizep, predictor, prediction

type Problem{L<:AbLoss,
             B<:AbBasis,
             CS<:AbConstraintSet}
  X :: Matrix
  y :: Vector
  loss :: L
  ϕ :: Type{B}
  Ω :: CS
end

Problem(X, y, l, B, ::Type{BasisL2Constraint}) = Problem(X, y, l, B, BasisL2Constraint(B, X))
Problem(X, y, l, B, ::Type{CS}) where CS<:AbConstraintSet = Problem(X, y, l, B, CS())

################### Pass through sub-sub-routines #############################
loss(prob::Problem, z::Vector) = prob.loss(prob.y, z)
lossgrad(prob::Problem, z::Vector) = derv(prob.loss, prob.y, z)
lossngrad(prob::Problem, z::Vector) = valnderv(prob.loss, prob.y, z)

jacobian(prob::Problem{L,B}, u) where {L,B} = jacobian(B, prob.X, u)
valueandjacobian(prob::Problem{L, B}, u) where {L,B} = valueandjacobian(B, prob.X, u)

project(prob::Problem, u) = project(prob.Ω, u)
normcs(prob::Problem, u) = normcs(prob.Ω, u)

############################### Sub-routines ##################################
### Basics
sizen(prob::Problem) = length(prob.y)
sizep(prob::Problem) = size(prob.X)[2]

### Advanced
predictor(X::Matrix, ω::AbBasis) = calc(ω, X)
predictor(prob::Problem, ω::AbBasis) = predictor(prob.X, ω)
predictor(prob::Problem{L, B}, u::Vector) where {L,B<:AbUniDirBasis}= calc(B, prob.X, u)
prediction(X::Matrix{<:Real}, ωs::Vector{<:AbBasis}, βs::Vector{<:Real}) = sum(βs .* calc.(ωs, (X,)))
prediction(prob::Problem, args...) = prediction(prob.X, args...)

############################### Splitter ##################################
function getsplitter(prob::Problem)
    n, p = size(prob.X)
    medians = [median(prob.X[:, j]) for j in 2:p]
    actives = [prob.X[:, j] .≥ medians[j-1] for j in 2:p]
    u0s = zeros(p, p-1)
    u0s[1, :] = medians
    for j in 2:p
        u0s[j, j-1] = -1.
        u0s[:, j-1] /= normcs(prob, u0s[:, j-1])
    end
    j -> u0s[:, j-1]
end

function getsplits(prob::Problem{L, B}) where {L, B<:AbBasis}
    spliter = getsplitter(prob)
    [B(su*spliter(j), s) for j in 2:sizep(prob) for su in (-1., 1.) for s in (-1., 1.)]
end

function cardinalsplitdirs(prob::Problem{L, B})  where {L,B}
    stdlib = B[]
    p = sizep(prob)
    for j in 2:p
        u = zeros(p)
        u[j] = 1
        u[1] = -median(prob.X[:, j])
        u /= normcs(prob, u)
        for su in (-1., 1.)
            for s in (-1., 1.)
                push!(stdlib, B(su*u, s))
    end end end
    stdlib
end

function randomsplitdir(prob::Problem{L, B})  where {L,B}
    p = sizep(prob)
    u = randn(p)
    u[1] = 0.
    Xu = prob.X*u
    med = median(Xu)
    u[1] = -med
    u /= normcs(prob, u)
    su, s = rand([-1., 1.], 2)
    B(su*u, s)
end

randsplitdirs(prob, num) = [randomsplitdir(prob) for j in 1:num]


text:
using Caesar, Caesar.ZmqCaesar
using JSON, ZMQ
using Unmarshal
using Test

# @testset "ZMQ End-To-End Test" begin

emptyCmd = Dict{String, Any}()
unknownCmd = Dict{String, Any}("request" => "NOPENOPENOPE")
mockServerCmd = Dict{String, Any}("request" => "toggleMockServer", "payload" => Dict{String, Any}("isMockServer" => "true"))
addVariableCmd = Dict{String, Any}("request" => "addVariable", "payload" => JSON.parse(JSON.json(VariableRequest("x0", "Pose2", 100, ["TEST"]))))
lsCmd = Dict{String, Any}("request" => "ls", "payload" => Dict{String, Any}("variables" => "true", "factors" => "true"))
shutdownCmd = Dict{String, Any}("request" => "shutdown")
addOdo2DCmd = Dict{String, Any}(
    "request" => "addOdometry2D",
    "payload" => JSON.parse("{\n  \"covariance\": [\n    [\n      0.1,\n      0.0,\n      0.1\n    ],\n    [\n      0.1,\n      0.0,\n      0.1\n    ],\n    [\n      0.1,\n      0.0,\n      0.1\n    ]\n  ],\n  \"measurement\": [\n    10.0,\n    0.0,\n    1.0471975511965976\n  ],\n  \"robot_id\": \"Hexagonal\",\n  \"session_id\": \"cjz002\"}"));

@info "Starting the ZMQ server in an @async..."
@async begin
    fg = initfg()
    config = Dict{String, String}()
    zmqConfig = ZmqServer(fg, config, true, "tcp://*:5555")
    start(zmqConfig)
end
sleep(20)
@info "Started the ZMQ server!"

function sendCmd(cmd::Dict{String, Any})::String
    ctx=Context()
    sock=Socket(ctx, REQ)
    ZMQ.connect(sock, "tcp://localhost:5555")
    ZMQ.send(sock, JSON.json(cmd))
    msg = ZMQ.recv(sock)
    out=convert(IOStream, msg)
    str = String(take!(out))
    ZMQ.close(sock)
    return str
end

# Send various failure commands
@test sendCmd(unknownCmd) == "{\"status\":\"ERROR\",\"error\":\"Command 'NOPENOPENOPE' not a valid command.\"}"

@test sendCmd(emptyCmd) == "{\"status\":\"ERROR\",\"error\":\"Command 'ERROR_NOCOMMANDPROVIDED' not a valid command.\"}"

# Sinmple operational tests
@test sendCmd(addVariableCmd) == "{\"status\":\"OK\",\"id\":\"x0\"}"
@test sendCmd(lsCmd) == "{\"variables\":[{\"factors\":[],\"id\":\"x0\"}],\"status\":\"OK\"}"

# Lastly mock server test
@test sendCmd(mockServerCmd) == "{\"status\":\"OK\",\"isMockServer\":\"true\"}"
@test sendCmd(addOdo2DCmd) == "{\"status\":\"OK\"}"
# Disable it
mockServerCmd["payload"]["isMockServer"]="false"
@test sendCmd(mockServerCmd) == "{\"status\":\"OK\",\"isMockServer\":\"false\"}"

# Send the shutdown command - cannot be mocked.
@test sendCmd(shutdownCmd) == "{\"status\":\"OK\"}"
# end


text:
# Random Test Problem of size 100
X = randn(2, 100)
Y = randn(1, 100)

prob = DirectDataDrivenProblem(X, Y)

@testset "Train Test Split" begin
    for r in 0.2:0.2:0.8
        split = Split(ratio = r)
        train, test = split(prob)
        @test length(train) == Int(r*100)
        @test length(test) == Int(100 - r*100)
        @test length(train) + length(test) == 100
    end
end

@testset "Mini Batching" begin
    for n in 2:5, repeat_ in [true, false], shuffle_ in [true, false]
        batch = Batcher(n = n, repeated = repeat_, shuffle = shuffle_)
        train, test = batch(prob)
        @test length(train) == n
        @test all(length.(train) .>= round(Int, 100/n))
        @test sum(length.(train)) == 100
    end
end

@testset "DataSampler" begin
    for repeat_ in [true, false], shuffle_ in [true, false]
        ds = DataSampler(Batcher(n = 2, repeated = repeat_, shuffle = shuffle_), Split(ratio = 0.8))
        train, test = ds(prob)
        @test length(test) == 20
        @test sum(length, train) == 80
    end
end


text:
"""
Package: Smoothers

    filter(b,a,x,si)

Apply a digital filter to x using the following linear, time-invariant difference equation

```math
y(n) = \\sum_{k=0}^M d_{k+1} \\cdot x_{n-k}-\\sum_{k=1}^N c_{k+1} \\cdot y_{n-k}
\\\\ \\forall n | 1\\le n \\le \\left\\| x \\right\\| | c=a/a_1, d=b/a_1
```

The implementation follows the description of [Octave filter function](https://octave.sourceforge.io/octave/function/filter.html)

# Arguments
- `a`: Vector of numerator coefficients for the filter rational transfer function.
- `b`: Vector of denominator coefficients for the filter rational transfer function.
- `x`: Vector of data.
- `si`: Vector of initial states.

# Returns

Vector of filtered data

# Examples
```julia-repl
using Plots

t = Array(LinRange(-pi,pi,100));
x = sin.(t) .+ 0.25*rand(length(t));

# Moving Average Filter
w = 5;
b = ones(w)/w;
a = [1];

plot(t,x,label="sin(x)",legend=:bottomright)
y1 = filter(b,a,x)
si = x[1:4] .+ .1;
y2 = filter(b,a,x,si)
plot!(t,y1,label="MA")
plot!(t,y2,label="MA with si")
```
"""
@inline function filter(b::AbstractVector{A},
                        a::AbstractVector{B},
                        x::AbstractVector{C},
                        si::AbstractVector{D}=zeros(C,max(length(a),length(b))-1)
                        ) where {A<:Real,B<:Real,C<:Real,D<:Real}

    @assert a[1] != 0 "a[1] must not be zero"

    T = Base.promote_op(/,C,Base.promote_op(/,B,A))
    a,b,x,si,_ = Base.promote(a,b,x,si,[T(1.0)])

    Na,Nb,Nx = length(a),length(b),length(x)
    Nsi = max(Na,Nb)-1
    @assert Nsi == length(si) "length(si) must be max(length(a),length(b))-1)"

    N,M = Na-1,Nb-1
    c,d = a/a[1],b/a[1]

    y = zeros(T,Nx)
    y[1:Nsi] = si

    for n in 1:Nx
        for k in 0:min(n-1,M)
            @inbounds y[n] += d[k+1]*x[n-k]
        end
        for k in 1:min(n-1,N)
            @inbounds y[n] -= c[k+1]*y[n-k]
        end
    end

    return y
end


text:
# FIXME: make PR to OrderedCollections to allow for reverse iterators
function Base.iterate(rt::Iterators.Reverse{<:OrderedDict})
    t = rt.itr
    t.ndel > 0 && DataStructures.OrderedCollections.rehash!(t)
    n = length(t.keys)
    n < 1 && return nothing
    return (Pair(t.keys[n], t.vals[n]), n-1)
end
function Base.iterate(rt::Iterators.Reverse{<:OrderedDict}, i)
    t = rt.itr
    i < 1 && return nothing
    return (Pair(t.keys[i], t.vals[i]), i-1)
end


text:
module Sugars
using MLStyle
using MLStyle.AbstractPatterns

export Q, And, Or
@nospecialize

struct Q end
struct And end
struct Or end

function MLStyle.pattern_unref(::Type{Or}, self::Function, args::AbstractArray)
    isempty(args) && error("An Or pattern should take more than 1 clause!")
    or([self(arg) for arg in args])
end

function MLStyle.pattern_unref(::Type{And}, self::Function, args::AbstractArray)
    isempty(args) && error("An And pattern should take more than 1 clause!")
    and([self(arg) for arg in args])
end

function MLStyle.pattern_unref(::Type{Q}, self::Function, args::AbstractArray)
    length(args) === 1 || error("A Q pattern should take only 1 argument!")
    self(Expr(:quote, args[1]))
end
@specialize
end


text:
# the packages we need
include("CostumeFunctions.jl")

using Gtk, Graphics, Logging, Printf

# the main window
win = GtkWindow("SO(3)")

render_scale = 50

function init_canvas(h,w)
    # create the drawing canvas
    c = @GtkCanvas(h,w)
    # create the initial drawing inside a try/catch loop
    @guarded draw(c) do widget
        #  draw the background with the canvas drawing context
        # the code for this comes later
        draw_the_canvas(c)
    end

    show(c)
    return c
end

# Conversion from meter to pixel and pixel to meter
PIXEL_TO_METER = 0.01
METER_TO_PIXEL = 100

# make the canvas
the_canvas = init_canvas(800,800)

# Our origin
origin2D = [400 600]

# --------- part 2 -------------
# define all the widgets

# a widget for status messages that we define at the beginning so we can use it from the callback
msg_label = GtkLabel("No message at this time")

# bool that determines if we are at camera viewpoint or global viewpoint
viewpoint_global = false

# Array of circle points view from the camera
camera_points = 0

# Matrix of the points given by the exercice
vector_points = [0.9115 3.7207 1.9659 2.6663;
                1.9397 2.8794 1.0000 3.8191;
                3.3304 4.4372 3.2588 4.5087]

V1 = [1 0 0]
V2 = [0 1 0]
V3 = [0 0 1]

function find_by_name(list, name)
    for item in list
        if get_gtk_property(item, :name, String) == name
            return item
        end
    end
    @warn name, "not found in list"
    @warn "available names are"
    for item in list
        @warn get_gtk_property(item, :name, String)
    end
end

# Make a vector of dimensions x an homogenic vector of dimensions x+1
function homog(v, f=1.0)
    if (typeof(f) != Float64) f = parse(Float64, f) end
    push!(v, f)
    return v
end

# Make a vector of dimenions x a dehmog vector of dimensions x-1
function dehomog(v)
    vector = zeros(Float64, length(v)-1)
    for i = 1:length(v)-1
        vector[i] = v[i]/v[length(v)]
    end
    return vector
end

function button_callback(widget)
    #if (get_gtk_property(widget, :name, String)[1])

    global viewpoint_global = !viewpoint_global

    # actually draw the changes
    draw_the_canvas(the_canvas)
    reveal(the_canvas)

end

function button_box(label_string)

    button = GtkButton(label_string)
    set_gtk_property!(button, "halign", 3)
    set_gtk_property!(button, "valign", 3)

    signal_connect(button_callback, button, "clicked")

    return button

end

function bold_label(label_string)
    label = GtkLabel("")
    GAccessor.markup(label, """<b>""" * label_string * """</b>""")
    return label
end

function init_window(win, canvas)

    # make a vertically stacked box for the data entry widgets
    control_box = GtkBox(:v)
    push!(control_box, button_box("Press me"))
    push!(control_box, msg_label)

    # make another box for the drawing canvas
    canvas_box = GtkBox(:v)
    push!(canvas_box, canvas)

    # make a containing box that will stack the widgets and the canvas side by side
    global_box = GtkBox(:v)
    push!(global_box, canvas_box)
    push!(global_box, GtkLabel("   ")) # a very basic separator
    push!(global_box, control_box)
    push!(global_box, GtkLabel("   ")) # a very basic separator

    # put it all inside the window
    push!(win, global_box)

end

# Draw an axis on a point on screen
function draw_axis(origin, R1, R2, R3, ctx)
    # Set line
    set_line_width(ctx, 5)
    set_source_rgb(ctx, 0, 0, 1)
    # Move to Origin
    move_to(ctx, origin[1], origin[2])
    line_to(ctx, origin[1] + R1[1] * render_scale, origin[2] - R1[2] * render_scale)
    stroke(ctx)
    # Circle to determine direction
    circle(ctx, origin[1] + R1[1] * render_scale, origin[2] - R1[2] * render_scale, 5)
    fill(ctx)

    # Repeat on the other two vectors
    set_line_width(ctx, 5)
    set_source_rgb(ctx, 1, 0, 0)
    move_to(ctx , origin[1], origin[2])
    line_to(ctx, origin[1] + R2[1] * render_scale, origin[2] - R2[2] * render_scale)
    stroke(ctx)

    circle(ctx, origin[1] + R2[1] * render_scale, origin[2] - R2[2] * render_scale, 5)
    fill(ctx)

    set_line_width(ctx, 5)
    set_source_rgb(ctx, 0, 1, 0)
    move_to(ctx, origin[1], origin[2])
    line_to(ctx, origin[1] + R3[1] * render_scale, origin[2] - R3[2] * render_scale)
    stroke(ctx)

    circle(ctx, origin[1] + R3[1] * render_scale, origin[2] - R3[2] * render_scale, 5)
    fill(ctx)

end

function resize_matrix(A, rows, cols)
    resized = similar(A, rows, cols)
    resized[1:size(A, 1), 1:size(A, 2)] = A
    return resized
end

function calculate_point_coordinates()

    # Calculate camera viewpoint
    camera_pos = [4.665; 3.735; -0.5395]
    axis = [0.01;-0.2;1.0]
    angle = -150
    focal_distance = 1.0

    # Get rotation
    rotation_matrix = axis_angle_to_mat(EulerAngleAxis(angle, axis))

    #Create affine camera base
    camera_affine_base = [rotation_matrix camera_pos]
    camera_affine_base = [camera_affine_base;[0 0 0 1]]
    camera_affine_base_inverted = inv(camera_affine_base)

    # Create projection matrix
    projection_matrix = [focal_distance 0.0 0.0 0.0; 0.0 focal_distance 0.0 0.0; 0.0 0.0 1.0 0.0]

    # Create matrix of the same dimensions as circle_points to store the points on the plane
    global camera_points = zeros(Float64, 4,3)

    # Calculate camera points
    for i = 1:4
        point = [vector_points[1,i]; vector_points[2,i]; vector_points[3,i]]
        point = dehomog(projection_matrix * camera_affine_base_inverted * homog(point))
        for k = 1:2
            camera_points[i,k] = point[k]
        end
    end
end

# Draw the scene in 3D
function draw_canvas_global(ctx)

    # Draw a global axis---------------
    R1 = [10 0 0]
    R2 = [0 10 0]
    R3 = [0 0 10]

    R1 = to_2d(R1)
    R2 = to_2d(R2)
    R3 = to_2d(R3)

    draw_axis(origin2D, R1, R2, R3, ctx)

    # Draw vectors seen from the global viewpoints
    for i = 1:2:4
        origin = [vector_points[1,i]; vector_points[2,i]; vector_points[3,i]]
        destination = [vector_points[1,i+1]; vector_points[2,i+1]; vector_points[3,i+1]]

        origin = to_2d(origin)
        destination = to_2d(destination)

        set_line_width(ctx, 5)
        set_source_rgb(ctx, 0, 0, 1)
        # Move to Origin
        move_to(ctx, origin2D[1] + origin[1] * METER_TO_PIXEL, origin2D[2] + origin[2] * METER_TO_PIXEL)
        line_to(ctx, origin2D[1] + destination[1] * METER_TO_PIXEL, origin2D[2] + destination[2] * METER_TO_PIXEL)
        stroke(ctx)
    end

    # Draw camera axis------------------------------------------------------
    camera_pos = to_2d([4.665; 3.735; -0.5395])

    # Get camera pos in 2D
    camera_pos_pixels = [origin2D[1] + (camera_pos[1] * METER_TO_PIXEL); origin2D[2] + (camera_pos[2] * METER_TO_PIXEL)]

    # Get camera axis vectors
    V1 = [1;0;0]
    V2 = [0;1;0]
    V3 = [0;0;1]

    # Rotate vectors on the axis give by the exercice
    V1 = rotate_phi(V1, -150, [0.01;-0.2;1.0])
    V2 = rotate_phi(V2, -150, [0.01;-0.2;1.0])
    V3 = rotate_phi(V3, -150, [0.01;-0.2;1.0])

    V1 = to_2d(V1)
    V2 = to_2d(V2)
    V3 = to_2d(V3)

    # Draw the axis
    draw_axis(camera_pos_pixels, V1, V2, V3, ctx)

end

# Draw the scene in 2D (camera point of view)
function draw_canvas_camera(ctx)
    # Draw the vectors seen from the camera
    for i = 1:2:3
        origin = [camera_points[i,1] camera_points[i,2]]
        destination = [camera_points[i+1,1] camera_points[i+1,2]]

        set_line_width(ctx, 5)
        set_source_rgb(ctx, 0, 0, 1)
        # Move to Origin
        move_to(ctx, origin2D[1] + origin[1] * METER_TO_PIXEL, origin2D[2] + origin[2] * METER_TO_PIXEL)
        line_to(ctx, origin2D[1] + destination[1] * METER_TO_PIXEL, origin2D[2] + destination[2] * METER_TO_PIXEL)
        stroke(ctx)
    end
end

function draw_the_canvas(canvas)

    h   = height(canvas)
    w   =  width(canvas)
    ctx =  getgc(canvas)
    # clear the canvas
    rectangle(ctx, 0, 0, w, h)
    set_source_rgb(ctx, 1, 1, 1)
    fill(ctx)

    # Check if we are seeing the 3D or 2D point of view
    if (viewpoint_global == true)
        draw_canvas_global(ctx)
    else
        draw_canvas_camera(ctx)
    end
end

println("----------------------------------START--------------------------------")

# prepare and  the initial widgets
init_window(win, the_canvas)
showall(win)
calculate_point_coordinates()


text:
# we cache different compile target results
const GLOBAL_CI_CACHE = Dict{Any,GPUCompiler.CodeCache}()

@option struct HardwareFreeOptions <: AbstractCompilerParams
    group_quantum_stmts::Bool = true
    phase_teleportation::Bool = false
    clifford_simplification::Bool = false
end

abstract type YaoCompileTarget <: AbstractCompilerTarget end

"""
    NativeJuliaTarget <: YaoCompileTarget

This target assumes one compiles to native Julia, thus the measurement
type will be directly inferred from native intrinsics.
"""
abstract type NativeJuliaTarget <: YaoCompileTarget end

function target_measure_result_type(::NativeJuliaTarget, interp, f, fargs, argtypes, sv, max_methods)
    callinfo = Core.Compiler.abstract_call_known(parent(interp), f, fargs, argtypes, sv, max_methods)
    return callinfo.rt
end

"""
    target_measure_result_type(target::YaoCompileTarget[, interp::YaoInterpreter, f, fargs, argtypes, sv, max_methods])

Hook for handling different measurement result type. Default type is `MeasureResult`.
"""
function target_measure_result_type(
    target::YaoCompileTarget,
    interp,
    f,
    fargs,
    argtypes,
    sv,
    max_methods,
)
    target_measure_result_type(target)
end

# this is not always true, we need to replace this with a compiler internal
# type
target_measure_result_type(::YaoCompileTarget) = MeasureResult{Int}

@option struct JLGenericTarget <: YaoCompileTarget end

get_cache(target::YaoCompileTarget) = GLOBAL_CI_CACHE[target]

@option struct YaoInterpreter{Target<:YaoCompileTarget} <: JuliaLikeInterpreter
    native_interpreter::NativeInterpreter = NativeInterpreter()
    target::Target = JLGenericTarget()
    cache::CodeCache = get!(GLOBAL_CI_CACHE, target, GPUCompiler.CodeCache())
    options::HardwareFreeOptions = HardwareFreeOptions()
    max_const_invoke_elim::Int = 10
end

YaoInterpreter(target; kw...) = YaoInterpreter(NativeInterpreter(), target; kw...)

function Core.Compiler.code_cache(interp::YaoInterpreter)
    Core.Compiler.WorldView(get_cache(interp.target), Core.Compiler.get_world_counter(interp))
end

function Core.Compiler.abstract_call(
    interp::YaoInterpreter,
    fargs::Union{Nothing,Vector{Any}},
    argtypes::Vector{Any},
    sv::InferenceState,
    max_methods::Int = InferenceParams(interp).MAX_METHODS,
)

    ft = argtypes[1]
    if isa(ft, Const)
        f = ft.val
    elseif isconstType(ft)
        f = ft.parameters[1]
    elseif isa(ft, DataType) && isdefined(ft, :instance)
        f = ft.instance
    else
        # non-constant function, but the number of arguments is known
        # and the ft is not a Builtin or IntrinsicFunction
        if Core.Compiler.typeintersect(widenconst(ft), Core.Builtin) != Union{}
            Core.Compiler.add_remark!(interp, sv, "Could not identify method table for call")
            return Core.Compiler.CallMeta(Any, false)
        end
        return Core.Compiler.abstract_call_gf_by_type(
            interp,
            nothing,
            argtypes,
            argtypes_to_type(argtypes),
            sv,
            max_methods,
        )
    end

    allconst = true
    for x in argtypes
        if !(x isa Const)
            allconst = false
        end
    end

    if Intrinsics.isintrinsic(f)
        return abstract_call_quantum(interp, f, fargs, argtypes, sv, max_methods)
    elseif (f isa UnionAll || f isa DataType) && f <: IntrinsicRoutine && allconst
        # force intrinsic gates not pure to prevent inline
        callinfo = Core.Compiler.abstract_call_known(interp, f, fargs, argtypes, sv, max_methods)
        return CallMeta(callinfo.rt, nothing)
    elseif f === Locations && allconst # force Locations pure to inline
        return CallMeta(Const(f(argtypes[2].val)), MethodResultPure())
    elseif f === CtrlLocations && allconst
        la = length(fargs) - 1
        if la == 1
            loc = f(argtypes[2].val)
        elseif la == 2
            loc = f(argtypes[2].val, argtypes[3].val)
        else
            error("Invalid CtrlLocation statement")
        end
        return CallMeta(Const(loc), MethodResultPure())
    elseif f === getindex && allconst
        # getindex(locs, locs)
        la = length(argtypes)
        if la == 3 && argtypes[2].val isa AbstractLocations && argtypes[3].val isa AbstractLocations
            return CallMeta(Const(f(argtypes[2].val, argtypes[3].val)), MethodResultPure())
        end
    elseif f === measure_cmp
        return CallMeta(Bool, nothing)
    end

    return Core.Compiler.abstract_call_known(interp, f, fargs, argtypes, sv, max_methods)
end

function abstract_call_quantum(
    interp::YaoInterpreter,
    @nospecialize(f),
    fargs::Union{Nothing,Vector{Any}},
    argtypes::Vector{Any},
    sv::InferenceState,
    max_methods::Int = InferenceParams(interp).MAX_METHODS,
)

    if f === Intrinsics.measure
        result_type =
            target_measure_result_type(interp.target, interp, f, fargs, argtypes, sv, max_methods)
        return Core.Compiler.CallMeta(result_type, nothing)
    elseif f === Intrinsics.apply # || f === Intrinsics.ctrl
        gt = widenconst(argtypes[3])

        if gt <: IntrinsicRoutine
            return CallMeta(Const(nothing), nothing)
        else
            ret = Core.Compiler.abstract_call_known(interp, f, fargs, argtypes, sv, max_methods)
            return ret
        end
    else # mark other intrinsic not pure to prevent inline
        return Core.Compiler.CallMeta(Const(nothing), nothing)
    end
end

function elim_location_mapping!(ir::IRCode)
    ir = const_invoke!(map_check_nothrow, ir, GlobalRef(YaoLocations, :map_check_nothrow))
    ir = const_invoke!(unsafe_mapping, ir, GlobalRef(YaoLocations, :unsafe_mapping))
    ir = const_invoke!(merge_locations, ir, GlobalRef(YaoLocations, :merge_locations))
    ir = compact!(ir, true) # Simplify CFG
    # group quantum statements so we can work on
    # larger quantum circuits before we start optimizations
    ir = Core.Compiler.cfg_simplify!(ir)
    return compact!(ir)
end

function CompilerPluginTools.optimize(interp::YaoInterpreter, ir::IRCode)
    ir = inline_const!(ir)
    ir = elim_location_mapping!(ir)

    # try to eliminate location mapping as much as possible
    count = 0
    while count < interp.max_const_invoke_elim &&
        contains_const_invoke(ir, GlobalRef(YaoLocations, :map_check_nothrow))
        ir = elim_location_mapping!(ir)
        count += 1
    end

    if interp.options.group_quantum_stmts
        ir = group_quantum_stmts!(ir)
    end

    # ir = quote_globalref_gate(ir)
    # target agnoistic
    if interp.options.phase_teleportation
        ir = run_pure_quantum_passes(ir) do block_ir::BlockIR
            phase_teleportation(block_ir)
        end
    end

    if interp.options.clifford_simplification
        ir = run_pure_quantum_passes(ir) do block_ir::BlockIR
            clifford_simplification(block_ir)
        end
    end

    ir = target_specific_pipeline(interp.target, ir)
    ir = compact!(ir)
    return ir
end

target_specific_pipeline(::YaoCompileTarget, ir::IRCode) = ir

function group_quantum_stmts!(ir::IRCode)
    perm = group_quantum_stmts_perm(ir)
    return permute_stmts!(ir, perm)
end

function group_quantum_stmts_perm(ir::IRCode)
    perms = Int[]
    cstmts_tape = Int[]
    qstmts_tape = Int[]

    for b in ir.cfg.blocks
        for v in b.stmts
            e = ir.stmts[v][:inst]
            @switch e begin
                # terminator
                @case Expr(:invoke, _, GlobalRef(Intrinsics, :measure), _...) ||
                      Expr(:invoke, _, GlobalRef(Intrinsics, :expect), _...) ||
                      Expr(:invoke, _, GlobalRef(Intrinsics, :barrier), _...)

                exit_block!(perms, cstmts_tape, qstmts_tape)
                push!(perms, v)
                # intrinsic
                @case Expr(:invoke, _, GlobalRef(Intrinsics, :apply), _...)
                push!(qstmts_tape, v)
                @case ::ReturnNode || ::GotoIfNot || ::GotoNode
                exit_block!(perms, cstmts_tape, qstmts_tape)
                push!(cstmts_tape, v)
                @case Expr(:enter, _...)
                exit_block!(perms, cstmts_tape, qstmts_tape)
                push!(cstmts_tape, v)
                @case _
                push!(cstmts_tape, v)
            end
        end
        exit_block!(perms, cstmts_tape, qstmts_tape)
    end

    append!(perms, cstmts_tape)
    append!(perms, qstmts_tape)

    return perms # permute_stmts(ci, perms)
end

function exit_block!(perms::Vector, cstmts_tape::Vector, qstmts_tape::Vector)
    append!(perms, cstmts_tape)
    append!(perms, qstmts_tape)
    empty!(cstmts_tape)
    empty!(qstmts_tape)
    return perms
end

function compute_quantum_blocks(ir::IRCode)
    quantum_blocks = UnitRange{Int}[]

    for b in ir.cfg.blocks
        start, stop = 0, 0
        for v in b.stmts
            st = ir.stmts[v][:inst]

            @switch st begin
                @case Expr(:invoke, _, GlobalRef(Intrinsics, :apply), _...)
                    if start > 0
                        stop += 1
                    else
                        start = stop = v
                    end
                # quantum terminator
                @case Expr(:invoke, _, GlobalRef(Intrinsic, :measure), _...) ||
                    Expr(:invoke, _, GlobalRef(Intrinsic, :barrier), _...) ||
                    Expr(:invoke, _, GlobalRef(Intrinsic, :expect), _...)

                    if start > 0
                        push!(quantum_blocks, start:stop+1)
                    else
                        push!(quantum_blocks, v:v)
                    end
                    start = stop = 0
                # classical terminator
                @case _
                    if start > 0
                        push!(quantum_blocks, start:stop)
                        start = stop = 0
                    end
            end

        end

        if start > 0
            push!(quantum_blocks, start:stop)
        end
    end
    return quantum_blocks
end

function quote_globalref_gate(ir::IRCode, quantum_blocks=compute_quantum_blocks(ir))
    for b in quantum_blocks, v in b
        @switch ir.stmts[v][:inst] begin
            @case Expr(:invoke, mi, &(GlobalRef(Intrinsics, :apply)), reg, op, args...)
                ir.stmts[v][:inst] = Expr(:invoke, mi, GlobalRef(Intrinsics, :apply), reg, CompilerPluginTools.eval_global(op), args...)
            @case _
                nothing
        end
    end
    return ir
end

function run_pure_quantum_passes(f, ir::IRCode, quantum_blocks=compute_quantum_blocks(ir))
    n = count_qubits(ir, quantum_blocks)
    # NOTE: we can't optimize
    # non-constant location program
    isnothing(n) && return ir
    iszero(n) && return ir

    # NOTE: pure quantum block requires locations to be constant
    compact = IncrementalCompact(ir, true)
    for b in quantum_blocks
        circuit = Chain()
        for v in b
            e = ir.stmts[v][:inst]
            @switch e begin
                # constant case
                @case Expr(:invoke, _, GlobalRef(Intrinsics, :apply), reg, QuoteNode(op), QuoteNode(locs))
                    push!(circuit.args, Gate(op, locs))
                @case Expr(:invoke, _, GlobalRef(Intrinsics, :apply), reg, QuoteNode(op), QuoteNode(locs), QuoteNode(ctrl))
                    push!(circuit.args, Ctrl(Gate(op, locs), ctrl))

                # gate operator is not constant
                @case Expr(:invoke, _, GlobalRef(Intrinsics, :apply), reg, op::SSAValue, QuoteNode(locs))
                    op_type = ir.stmts[op.id][:type]
                    push!(circuit.args, Gate(Typed(op, op_type), locs))
                @case Expr(:invoke, _, GlobalRef(Intrinsics, :apply), reg, op::SSAValue, QuoteNode(locs), QuoteNode(ctrl))
                    op_type = ir.stmts[op.id][:type]
                    push!(circuit.args, Ctrl(Gate(Typed(op, op_type), locs), ctrl))

                # terminators
                @case Expr(:invoke, _, GlobalRef(Intrinsics, :measure), _, ::QuoteNode) ||
                    Expr(:invoke, _, GlobalRef(Intrinsics, :barrier), _, ::QuoteNode) ||
                    Expr(:invoke, _, GlobalRef(Intrinsics, :expect), _, ::QuoteNode)
                    break

                # not pure quantum anymore
                @case _
                    return ir
            end
        end

        block_ir = f(BlockIR(ir, n, circuit))::BlockIR

        # delete old circuit
        reg = Argument(2)
        reg_type = widenconst(ir.argtypes[2])
        first_terminator = nothing
        for v in b
            e = ir.stmts[v][:inst]
            @switch e begin
                @case Expr(:invoke, _, GlobalRef(Intrinsics, :apply), reg, op, locs) ||
                Expr(:invoke, _, GlobalRef(Intrinsics, :apply), reg, op, locs, ctrl)
                    compact[v] = nothing
                @case _ # don't process measure etc.
                    first_terminator = v
                    break
            end
        end

        # pure quantum block without measure/barrier
        if isnothing(first_terminator)
            first_terminator = last(b) + 1
        end

        # assert type
        first_terminator = first_terminator::Int

        # NOTE: assume we will only get constant gate
        #       and constant locations
        #       reject non-constant gate/locations for now
        for each in YaoHIR.leaves(block_ir.circuit)
            @switch each begin
                @case Gate(op::IntrinsicRoutine, locs::Locations)
                    mi = specialize_apply(reg_type, typeof(op), typeof(locs))
                    e = Expr(:invoke, mi, Intrinsics.apply, reg, QuoteNode(op), QuoteNode(locs))
                @case Ctrl(Gate(op::IntrinsicRoutine, locs::Locations), ctrl::CtrlLocations)
                    mi = specialize_apply(reg_type, typeof(op), typeof(locs), typeof(ctrl))
                    e = Expr(:invoke, mi, Intrinsics.apply, reg, QuoteNode(op), QuoteNode(locs), QuoteNode(ctrl))
                @case Gate(Typed(op::SSAValue, op_type), locs::Locations)
                    mi = specialize_apply(reg_type, op_type, typeof(locs))
                    e = Expr(:invoke, mi, Intrinsics.apply, reg, op, QuoteNode(locs))
                @case Ctrl(Gate(Typed(op::SSAValue, op_type), locs::Locations), ctrl::CtrlLocations)
                    mi = specialize_apply(reg_type, op_type, typeof(locs), typeof(ctrl))
                    e = Expr(:invoke, mi, Intrinsics.apply, reg, op, QuoteNode(locs), QuoteNode(ctrl))
                @case _
                    error("invalid statement: $each")
            end

            Core.Compiler.insert_node!(compact, SSAValue(first_terminator), Nothing, e)
        end
    end

    for _ in compact; end
    new = Core.Compiler.finish(compact)
    return new
end

function count_qubits(ir::IRCode, quantum_blocks=compute_quantum_blocks(ir))
    locations = Set{Locations{Int}}()

    for b in quantum_blocks, v in b
        e = ir.stmts[v][:inst]
        @switch e begin
            @case Expr(:invoke, _, GlobalRef(Intrinsics, :apply), _, _, locs)
                locs isa QuoteNode || return
                union!(locations, collect(locs.value))
            @case Expr(:invoke, _, GlobalRef(Intrinsics, :apply), _, _, locs, ctrl)
                locs isa QuoteNode || return
                ctrl isa QuoteNode || return
                union!(locations, collect(locs.value))
                union!(locations, collect(ctrl.value.storage))
            @case Expr(:invoke, _, GlobalRef(Intrinsics, :measure), _, locs)
                locs isa QuoteNode || return
                union!(locations, collect(locs.value))
            @case Expr(:invoke, _, GlobalRef(Intrinsics, :barrier), _, locs)
                locs isa QuoteNode || return
                union!(locations, collect(locs.value))
            @case Expr(:invoke, _, GlobalRef(Intrinsics, :expect), _, locs, _)
                locs isa QuoteNode || return
                union!(locations, collect(locs.value))
            @case _
                nothing
        end
    end
    return length(locations)
end

function specialize_apply(reg_type, op_type, locs_type)
    method = first(methods(Intrinsics.apply, Tuple{reg_type, op_type, locs_type}))
    atypes = Tuple{typeof(Intrinsics.apply), reg_type, op_type, locs_type}
    return Core.Compiler.specialize_method(method, atypes, Core.svec())
end

function specialize_apply(reg_type, op_type, locs_type, ctrl_type)
    method = first(methods(Intrinsics.apply, Tuple{reg_type, op_type, locs_type, ctrl_type}))
    atypes = Tuple{typeof(Intrinsics.apply), reg_type, op_type, locs_type, ctrl_type}
    return Core.Compiler.specialize_method(method, atypes, Core.svec())
end


text:
using Mimi

include("two-region-model.jl")

run(model)

# show results
getdataframe(model, :emissions, :E_Global)


text:
using ArrayFire
function warmup()
    a = rand(10,10)
    ad = AFArray(a)
    ad * ad
    a * a
    fft(a)
    fft(ad)
    chol(a*a')
    chol(ad*ad')
    rand(AFArray{Float64}, 10, 10)
    sort(vec(a))
    sort(vec(ad))
end

function matmul(a::AFArray, b::AFArray)
    for i = 1:10
        r = a * b
        ArrayFire.eval(r)
    end
    sync()
end

function fast_fourier(a::AFArray)
    for i = 1:10
        r = fft(a)
        ArrayFire.eval(r)
    end
    sync()
end

function cholesky(a::AFArray)
    for i = 1:10
        r = chol(a)
        ArrayFire.eval(r)
    end
    sync()
end

function random()
    for i = 1:10
        r = rand(AFArray{Float32}, 5000, 5000)
        ArrayFire.eval(r)
    end
    sync()
end

function sorting(a::AFArray)
    for i = 1:10
        r = sort(a)
        ArrayFire.eval(r)
    end
    sync()
end

function benchmark()

    warmup()
    info("Warmup done!")
    gc()

    a = rand(Float32, 2000, 2000)
    ad = AFArray(a)

    #Matmul
    info("Matmul")
    t1 = @elapsed a * a
    t2 = @elapsed matmul(ad, ad)
    println("Time (CPU): $t1")
    println("Time (GPU): $(t2/10)")
    gc()

    #FFT
    info("FFT")
    t1 = @elapsed fft(a)
    t2 = @elapsed fast_fourier(ad)
    println("Time (CPU): $t1")
    println("Time (GPU): $(t2/10)")

    #Cholesky
    b = a * a'
    bd = AFArray(b)
    info("Cholesky")
    t1 = @elapsed chol(b)
    t2 = @elapsed cholesky(bd)
    println("Time (CPU): $t1")
    println("Time (GPU): $(t2/10)")

    #Rand
    info("Rand")
    t1 = @elapsed rand(Float32, 5000, 5000)
    t2 = @elapsed random()
    println("Time (CPU): $t1")
    println("Time (GPU): $(t2/10)")

    #Vecsort
    c = rand(Float32, 10^6)
    cd = AFArray(c)
    info("Vec sort")
    t1 = @elapsed sort(c)
    t2 = @elapsed sorting(cd)
    println("Time (CPU): $t1")
    println("Time (GPU): $(t2/10)")

end

benchmark()