text:
# generated
# length(dict) = 1817
const emoji_name_table = Dict{String, String}(
    "#⃣" => "\\:hash:",
    "*⃣" => "\\:keycap_star:",
    "0⃣" => "\\:zero:",
    "1⃣" => "\\:one:",
    "2⃣" => "\\:two:",
    "3⃣" => "\\:three:",
    "4⃣" => "\\:four:",
    "5⃣" => "\\:five:",
    "6⃣" => "\\:six:",
    "7⃣" => "\\:seven:",
    "8⃣" => "\\:eight:",
    "9⃣" => "\\:nine:",
    "©" => "\\:copyright:",
    "®" => "\\:registered:",
    "‼" => "\\:bangbang:",
    "⁉" => "\\:interrobang:",
    "™" => "\\:tm:",
    "ℹ" => "\\:information_source:",
    "↔" => "\\:left_right_arrow:",
    "↕" => "\\:arrow_up_down:",
    "↖" => "\\:arrow_upper_left:",
    "↗" => "\\:arrow_upper_right:",
    "↘" => "\\:arrow_lower_right:",
    "↙" => "\\:arrow_lower_left:",
    "↩" => "\\:leftwards_arrow_with_hook:",
    "↪" => "\\:arrow_right_hook:",
    "⌚" => "\\:watch:",
    "⌛" => "\\:hourglass:",
    "⌨" => "\\:keyboard:",
    "⏏" => "\\:eject:",
    "⏩" => "\\:fast_forward:",
    "⏪" => "\\:rewind:",
    "⏫" => "\\:arrow_double_up:",
    "⏬" => "\\:arrow_double_down:",
    "⏭" => "\\:black_right_pointing_double_triangle_with_vertical_bar:",
    "⏮" => "\\:black_left_pointing_double_triangle_with_vertical_bar:",
    "⏯" => "\\:black_right_pointing_triangle_with_double_vertical_bar:",
    "⏰" => "\\:alarm_clock:",
    "⏱" => "\\:stopwatch:",
    "⏲" => "\\:timer_clock:",
    "⏳" => "\\:hourglass_flowing_sand:",
    "⏸" => "\\:double_vertical_bar:",
    "⏹" => "\\:black_square_for_stop:",
    "⏺" => "\\:black_circle_for_record:",
    "Ⓜ" => "\\:m:",
    "▪" => "\\:black_small_square:",
    "▫" => "\\:white_small_square:",
    "▶" => "\\:arrow_forward:",
    "◀" => "\\:arrow_backward:",
    "◻" => "\\:white_medium_square:",
    "◼" => "\\:black_medium_square:",
    "◽" => "\\:white_medium_small_square:",
    "◾" => "\\:black_medium_small_square:",
    "☀" => "\\:sunny:",
    "☁" => "\\:cloud:",
    "☂" => "\\:umbrella:",
    "☃" => "\\:snowman:",
    "☄" => "\\:comet:",
    "☎" => "\\:phone:",
    "☑" => "\\:ballot_box_with_check:",
    "☔" => "\\:umbrella_with_rain_drops:",
    "☕" => "\\:coffee:",
    "☘" => "\\:shamrock:",
    "☝" => "\\:point_up:",
    "☠" => "\\:skull_and_crossbones:",
    "☢" => "\\:radioactive_sign:",
    "☣" => "\\:biohazard_sign:",
    "☦" => "\\:orthodox_cross:",
    "☪" => "\\:star_and_crescent:",
    "☮" => "\\:peace_symbol:",
    "☯" => "\\:yin_yang:",
    "☸" => "\\:wheel_of_dharma:",
    "☹" => "\\:white_frowning_face:",
    "☺" => "\\:relaxed:",
    "♀" => "\\:female_sign:",
    "♂" => "\\:male_sign:",
    "♈" => "\\:aries:",
    "♉" => "\\:taurus:",
    "♊" => "\\:gemini:",
    "♋" => "\\:cancer:",
    "♌" => "\\:leo:",
    "♍" => "\\:virgo:",
    "♎" => "\\:libra:",
    "♏" => "\\:scorpius:",
    "♐" => "\\:sagittarius:",
    "♑" => "\\:capricorn:",
    "♒" => "\\:aquarius:",
    "♓" => "\\:pisces:",
    "♟" => "\\:chess_pawn:",
    "♠" => "\\:spades:",
    "♣" => "\\:clubs:",
    "♥" => "\\:hearts:",
    "♦" => "\\:diamonds:",
    "♨" => "\\:hotsprings:",
    "♻" => "\\:recycle:",
    "♾" => "\\:infinity:",
    "♿" => "\\:wheelchair:",
    "⚒" => "\\:hammer_and_pick:",
    "⚓" => "\\:anchor:",
    "⚔" => "\\:crossed_swords:",
    "⚕" => "\\:medical_symbol:",
    "⚖" => "\\:scales:",
    "⚗" => "\\:alembic:",
    "⚙" => "\\:gear:",
    "⚛" => "\\:atom_symbol:",
    "⚜" => "\\:fleur_de_lis:",
    "⚠" => "\\:warning:",
    "⚡" => "\\:zap:",
    "⚧" => "\\:transgender_symbol:",
    "⚪" => "\\:white_circle:",
    "⚫" => "\\:black_circle:",
    "⚰" => "\\:coffin:",
    "⚱" => "\\:funeral_urn:",
    "⚽" => "\\:soccer:",
    "⚾" => "\\:baseball:",
    "⛄" => "\\:snowman_without_snow:",
    "⛅" => "\\:partly_sunny:",
    "⛈" => "\\:thunder_cloud_and_rain:",
    "⛎" => "\\:ophiuchus:",
    "⛏" => "\\:pick:",
    "⛑" => "\\:helmet_with_white_cross:",
    "⛓" => "\\:chains:",
    "⛔" => "\\:no_entry:",
    "⛩" => "\\:shinto_shrine:",
    "⛪" => "\\:church:",
    "⛰" => "\\:mountain:",
    "⛱" => "\\:umbrella_on_ground:",
    "⛲" => "\\:fountain:",
    "⛳" => "\\:golf:",
    "⛴" => "\\:ferry:",
    "⛵" => "\\:boat:",
    "⛷" => "\\:skier:",
    "⛸" => "\\:ice_skate:",
    "⛹" => "\\:person_with_ball:",
    "⛹\u200d♀" => "\\:woman-bouncing-ball:",
    "⛹\u200d♂" => "\\:man-bouncing-ball:",
    "⛺" => "\\:tent:",
    "⛽" => "\\:fuelpump:",
    "✂" => "\\:scissors:",
    "✅" => "\\:white_check_mark:",
    "✈" => "\\:airplane:",
    "✉" => "\\:email:",
    "✊" => "\\:fist:",
    "✋" => "\\:hand:",
    "✌" => "\\:v:",
    "✍" => "\\:writing_hand:",
    "✏" => "\\:pencil2:",
    "✒" => "\\:black_nib:",
    "✔" => "\\:heavy_check_mark:",
    "✖" => "\\:heavy_multiplication_x:",
    "✝" => "\\:latin_cross:",
    "✡" => "\\:star_of_david:",
    "✨" => "\\:sparkles:",
    "✳" => "\\:eight_spoked_asterisk:",
    "✴" => "\\:eight_pointed_black_star:",
    "❄" => "\\:snowflake:",
    "❇" => "\\:sparkle:",
    "❌" => "\\:x:",
    "❎" => "\\:negative_squared_cross_mark:",
    "❓" => "\\:question:",
    "❔" => "\\:grey_question:",
    "❕" => "\\:grey_exclamation:",
    "❗" => "\\:exclamation:",
    "❣" => "\\:heavy_heart_exclamation_mark_ornament:",
    "❤" => "\\:heart:",
    "❤\u200d🔥" => "\\:heart_on_fire:",
    "❤\u200d🩹" => "\\:mending_heart:",
    "➕" => "\\:heavy_plus_sign:",
    "➖" => "\\:heavy_minus_sign:",
    "➗" => "\\:heavy_division_sign:",
    "➡" => "\\:arrow_right:",
    "➰" => "\\:curly_loop:",
    "➿" => "\\:loop:",
    "⤴" => "\\:arrow_heading_up:",
    "⤵" => "\\:arrow_heading_down:",
    "⬅" => "\\:arrow_left:",
    "⬆" => "\\:arrow_up:",
    "⬇" => "\\:arrow_down:",
    "⬛" => "\\:black_large_square:",
    "⬜" => "\\:white_large_square:",
    "⭐" => "\\:star:",
    "⭕" => "\\:o:",
    "〰" => "\\:wavy_dash:",
    "〽" => "\\:part_alternation_mark:",
    "㊗" => "\\:congratulations:",
    "㊙" => "\\:secret:",
    "🀄" => "\\:mahjong:",
    "🃏" => "\\:black_joker:",
    "🅰" => "\\:a:",
    "🅱" => "\\:b:",
    "🅾" => "\\:o2:",
    "🅿" => "\\:parking:",
    "🆎" => "\\:ab:",
    "🆑" => "\\:cl:",
    "🆒" => "\\:cool:",
    "🆓" => "\\:free:",
    "🆔" => "\\:id:",
    "🆕" => "\\:new:",
    "🆖" => "\\:ng:",
    "🆗" => "\\:ok:",
    "🆘" => "\\:sos:",
    "🆙" => "\\:up:",
    "🆚" => "\\:vs:",
    "🇦🇨" => "\\:flag-ac:",
    "🇦🇩" => "\\:flag-ad:",
    "🇦🇪" => "\\:flag-ae:",
    "🇦🇫" => "\\:flag-af:",
    "🇦🇬" => "\\:flag-ag:",
    "🇦🇮" => "\\:flag-ai:",
    "🇦🇱" => "\\:flag-al:",
    "🇦🇲" => "\\:flag-am:",
    "🇦🇴" => "\\:flag-ao:",
    "🇦🇶" => "\\:flag-aq:",
    "🇦🇷" => "\\:flag-ar:",
    "🇦🇸" => "\\:flag-as:",
    "🇦🇹" => "\\:flag-at:",
    "🇦🇺" => "\\:flag-au:",
    "🇦🇼" => "\\:flag-aw:",
    "🇦🇽" => "\\:flag-ax:",
    "🇦🇿" => "\\:flag-az:",
    "🇧🇦" => "\\:flag-ba:",
    "🇧🇧" => "\\:flag-bb:",
    "🇧🇩" => "\\:flag-bd:",
    "🇧🇪" => "\\:flag-be:",
    "🇧🇫" => "\\:flag-bf:",
    "🇧🇬" => "\\:flag-bg:",
    "🇧🇭" => "\\:flag-bh:",
    "🇧🇮" => "\\:flag-bi:",
    "🇧🇯" => "\\:flag-bj:",
    "🇧🇱" => "\\:flag-bl:",
    "🇧🇲" => "\\:flag-bm:",
    "🇧🇳" => "\\:flag-bn:",
    "🇧🇴" => "\\:flag-bo:",
    "🇧🇶" => "\\:flag-bq:",
    "🇧🇷" => "\\:flag-br:",
    "🇧🇸" => "\\:flag-bs:",
    "🇧🇹" => "\\:flag-bt:",
    "🇧🇻" => "\\:flag-bv:",
    "🇧🇼" => "\\:flag-bw:",
    "🇧🇾" => "\\:flag-by:",
    "🇧🇿" => "\\:flag-bz:",
    "🇨🇦" => "\\:flag-ca:",
    "🇨🇨" => "\\:flag-cc:",
    "🇨🇩" => "\\:flag-cd:",
    "🇨🇫" => "\\:flag-cf:",
    "🇨🇬" => "\\:flag-cg:",
    "🇨🇭" => "\\:flag-ch:",
    "🇨🇮" => "\\:flag-ci:",
    "🇨🇰" => "\\:flag-ck:",
    "🇨🇱" => "\\:flag-cl:",
    "🇨🇲" => "\\:flag-cm:",
    "🇨🇳" => "\\:cn:",
    "🇨🇴" => "\\:flag-co:",
    "🇨🇵" => "\\:flag-cp:",
    "🇨🇷" => "\\:flag-cr:",
    "🇨🇺" => "\\:flag-cu:",
    "🇨🇻" => "\\:flag-cv:",
    "🇨🇼" => "\\:flag-cw:",
    "🇨🇽" => "\\:flag-cx:",
    "🇨🇾" => "\\:flag-cy:",
    "🇨🇿" => "\\:flag-cz:",
    "🇩🇪" => "\\:de:",
    "🇩🇬" => "\\:flag-dg:",
    "🇩🇯" => "\\:flag-dj:",
    "🇩🇰" => "\\:flag-dk:",
    "🇩🇲" => "\\:flag-dm:",
    "🇩🇴" => "\\:flag-do:",
    "🇩🇿" => "\\:flag-dz:",
    "🇪🇦" => "\\:flag-ea:",
    "🇪🇨" => "\\:flag-ec:",
    "🇪🇪" => "\\:flag-ee:",
    "🇪🇬" => "\\:flag-eg:",
    "🇪🇭" => "\\:flag-eh:",
    "🇪🇷" => "\\:flag-er:",
    "🇪🇸" => "\\:es:",
    "🇪🇹" => "\\:flag-et:",
    "🇪🇺" => "\\:flag-eu:",
    "🇫🇮" => "\\:flag-fi:",
    "🇫🇯" => "\\:flag-fj:",
    "🇫🇰" => "\\:flag-fk:",
    "🇫🇲" => "\\:flag-fm:",
    "🇫🇴" => "\\:flag-fo:",
    "🇫🇷" => "\\:fr:",
    "🇬🇦" => "\\:flag-ga:",
    "🇬🇧" => "\\:gb:",
    "🇬🇩" => "\\:flag-gd:",
    "🇬🇪" => "\\:flag-ge:",
    "🇬🇫" => "\\:flag-gf:",
    "🇬🇬" => "\\:flag-gg:",
    "🇬🇭" => "\\:flag-gh:",
    "🇬🇮" => "\\:flag-gi:",
    "🇬🇱" => "\\:flag-gl:",
    "🇬🇲" => "\\:flag-gm:",
    "🇬🇳" => "\\:flag-gn:",
    "🇬🇵" => "\\:flag-gp:",
    "🇬🇶" => "\\:flag-gq:",
    "🇬🇷" => "\\:flag-gr:",
    "🇬🇸" => "\\:flag-gs:",
    "🇬🇹" => "\\:flag-gt:",
    "🇬🇺" => "\\:flag-gu:",
    "🇬🇼" => "\\:flag-gw:",
    "🇬🇾" => "\\:flag-gy:",
    "🇭🇰" => "\\:flag-hk:",
    "🇭🇲" => "\\:flag-hm:",
    "🇭🇳" => "\\:flag-hn:",
    "🇭🇷" => "\\:flag-hr:",
    "🇭🇹" => "\\:flag-ht:",
    "🇭🇺" => "\\:flag-hu:",
    "🇮🇨" => "\\:flag-ic:",
    "🇮🇩" => "\\:flag-id:",
    "🇮🇪" => "\\:flag-ie:",
    "🇮🇱" => "\\:flag-il:",
    "🇮🇲" => "\\:flag-im:",
    "🇮🇳" => "\\:flag-in:",
    "🇮🇴" => "\\:flag-io:",
    "🇮🇶" => "\\:flag-iq:",
    "🇮🇷" => "\\:flag-ir:",
    "🇮🇸" => "\\:flag-is:",
    "🇮🇹" => "\\:it:",
    "🇯🇪" => "\\:flag-je:",
    "🇯🇲" => "\\:flag-jm:",
    "🇯🇴" => "\\:flag-jo:",
    "🇯🇵" => "\\:jp:",
    "🇰🇪" => "\\:flag-ke:",
    "🇰🇬" => "\\:flag-kg:",
    "🇰🇭" => "\\:flag-kh:",
    "🇰🇮" => "\\:flag-ki:",
    "🇰🇲" => "\\:flag-km:",
    "🇰🇳" => "\\:flag-kn:",
    "🇰🇵" => "\\:flag-kp:",
    "🇰🇷" => "\\:kr:",
    "🇰🇼" => "\\:flag-kw:",
    "🇰🇾" => "\\:flag-ky:",
    "🇰🇿" => "\\:flag-kz:",
    "🇱🇦" => "\\:flag-la:",
    "🇱🇧" => "\\:flag-lb:",
    "🇱🇨" => "\\:flag-lc:",
    "🇱🇮" => "\\:flag-li:",
    "🇱🇰" => "\\:flag-lk:",
    "🇱🇷" => "\\:flag-lr:",
    "🇱🇸" => "\\:flag-ls:",
    "🇱🇹" => "\\:flag-lt:",
    "🇱🇺" => "\\:flag-lu:",
    "🇱🇻" => "\\:flag-lv:",
    "🇱🇾" => "\\:flag-ly:",
    "🇲🇦" => "\\:flag-ma:",
    "🇲🇨" => "\\:flag-mc:",
    "🇲🇩" => "\\:flag-md:",
    "🇲🇪" => "\\:flag-me:",
    "🇲🇫" => "\\:flag-mf:",
    "🇲🇬" => "\\:flag-mg:",
    "🇲🇭" => "\\:flag-mh:",
    "🇲🇰" => "\\:flag-mk:",
    "🇲🇱" => "\\:flag-ml:",
    "🇲🇲" => "\\:flag-mm:",
    "🇲🇳" => "\\:flag-mn:",
    "🇲🇴" => "\\:flag-mo:",
    "🇲🇵" => "\\:flag-mp:",
    "🇲🇶" => "\\:flag-mq:",
    "🇲🇷" => "\\:flag-mr:",
    "🇲🇸" => "\\:flag-ms:",
    "🇲🇹" => "\\:flag-mt:",
    "🇲🇺" => "\\:flag-mu:",
    "🇲🇻" => "\\:flag-mv:",
    "🇲🇼" => "\\:flag-mw:",
    "🇲🇽" => "\\:flag-mx:",
    "🇲🇾" => "\\:flag-my:",
    "🇲🇿" => "\\:flag-mz:",
    "🇳🇦" => "\\:flag-na:",
    "🇳🇨" => "\\:flag-nc:",
    "🇳🇪" => "\\:flag-ne:",
    "🇳🇫" => "\\:flag-nf:",
    "🇳🇬" => "\\:flag-ng:",
    "🇳🇮" => "\\:flag-ni:",
    "🇳🇱" => "\\:flag-nl:",
    "🇳🇴" => "\\:flag-no:",
    "🇳🇵" => "\\:flag-np:",
    "🇳🇷" => "\\:flag-nr:",
    "🇳🇺" => "\\:flag-nu:",
    "🇳🇿" => "\\:flag-nz:",
    "🇴🇲" => "\\:flag-om:",
    "🇵🇦" => "\\:flag-pa:",
    "🇵🇪" => "\\:flag-pe:",
    "🇵🇫" => "\\:flag-pf:",
    "🇵🇬" => "\\:flag-pg:",
    "🇵🇭" => "\\:flag-ph:",
    "🇵🇰" => "\\:flag-pk:",
    "🇵🇱" => "\\:flag-pl:",
    "🇵🇲" => "\\:flag-pm:",
    "🇵🇳" => "\\:flag-pn:",
    "🇵🇷" => "\\:flag-pr:",
    "🇵🇸" => "\\:flag-ps:",
    "🇵🇹" => "\\:flag-pt:",
    "🇵🇼" => "\\:flag-pw:",
    "🇵🇾" => "\\:flag-py:",
    "🇶🇦" => "\\:flag-qa:",
    "🇷🇪" => "\\:flag-re:",
    "🇷🇴" => "\\:flag-ro:",
    "🇷🇸" => "\\:flag-rs:",
    "🇷🇺" => "\\:ru:",
    "🇷🇼" => "\\:flag-rw:",
    "🇸🇦" => "\\:flag-sa:",
    "🇸🇧" => "\\:flag-sb:",
    "🇸🇨" => "\\:flag-sc:",
    "🇸🇩" => "\\:flag-sd:",
    "🇸🇪" => "\\:flag-se:",
    "🇸🇬" => "\\:flag-sg:",
    "🇸🇭" => "\\:flag-sh:",
    "🇸🇮" => "\\:flag-si:",
    "🇸🇯" => "\\:flag-sj:",
    "🇸🇰" => "\\:flag-sk:",
    "🇸🇱" => "\\:flag-sl:",
    "🇸🇲" => "\\:flag-sm:",
    "🇸🇳" => "\\:flag-sn:",
    "🇸🇴" => "\\:flag-so:",
    "🇸🇷" => "\\:flag-sr:",
    "🇸🇸" => "\\:flag-ss:",
    "🇸🇹" => "\\:flag-st:",
    "🇸🇻" => "\\:flag-sv:",
    "🇸🇽" => "\\:flag-sx:",
    "🇸🇾" => "\\:flag-sy:",
    "🇸🇿" => "\\:flag-sz:",
    "🇹🇦" => "\\:flag-ta:",
    "🇹🇨" => "\\:flag-tc:",
    "🇹🇩" => "\\:flag-td:",
    "🇹🇫" => "\\:flag-tf:",
    "🇹🇬" => "\\:flag-tg:",
    "🇹🇭" => "\\:flag-th:",
    "🇹🇯" => "\\:flag-tj:",
    "🇹🇰" => "\\:flag-tk:",
    "🇹🇱" => "\\:flag-tl:",
    "🇹🇲" => "\\:flag-tm:",
    "🇹🇳" => "\\:flag-tn:",
    "🇹🇴" => "\\:flag-to:",
    "🇹🇷" => "\\:flag-tr:",
    "🇹🇹" => "\\:flag-tt:",
    "🇹🇻" => "\\:flag-tv:",
    "🇹🇼" => "\\:flag-tw:",
    "🇹🇿" => "\\:flag-tz:",
    "🇺🇦" => "\\:flag-ua:",
    "🇺🇬" => "\\:flag-ug:",
    "🇺🇲" => "\\:flag-um:",
    "🇺🇳" => "\\:flag-un:",
    "🇺🇸" => "\\:us:",
    "🇺🇾" => "\\:flag-uy:",
    "🇺🇿" => "\\:flag-uz:",
    "🇻🇦" => "\\:flag-va:",
    "🇻🇨" => "\\:flag-vc:",
    "🇻🇪" => "\\:flag-ve:",
    "🇻🇬" => "\\:flag-vg:",
    "🇻🇮" => "\\:flag-vi:",
    "🇻🇳" => "\\:flag-vn:",
    "🇻🇺" => "\\:flag-vu:",
    "🇼🇫" => "\\:flag-wf:",
    "🇼🇸" => "\\:flag-ws:",
    "🇽🇰" => "\\:flag-xk:",
    "🇾🇪" => "\\:flag-ye:",
    "🇾🇹" => "\\:flag-yt:",
    "🇿🇦" => "\\:flag-za:",
    "🇿🇲" => "\\:flag-zm:",
    "🇿🇼" => "\\:flag-zw:",
    "🈁" => "\\:koko:",
    "🈂" => "\\:sa:",
    "🈚" => "\\:u7121:",
    "🈯" => "\\:u6307:",
    "🈲" => "\\:u7981:",
    "🈳" => "\\:u7a7a:",
    "🈴" => "\\:u5408:",
    "🈵" => "\\:u6e80:",
    "🈶" => "\\:u6709:",
    "🈷" => "\\:u6708:",
    "🈸" => "\\:u7533:",
    "🈹" => "\\:u5272:",
    "🈺" => "\\:u55b6:",
    "🉐" => "\\:ideograph_advantage:",
    "🉑" => "\\:accept:",
    "🌀" => "\\:cyclone:",
    "🌁" => "\\:foggy:",
    "🌂" => "\\:closed_umbrella:",
    "🌃" => "\\:night_with_stars:",
    "🌄" => "\\:sunrise_over_mountains:",
    "🌅" => "\\:sunrise:",
    "🌆" => "\\:city_sunset:",
    "🌇" => "\\:city_sunrise:",
    "🌈" => "\\:rainbow:",
    "🌉" => "\\:bridge_at_night:",
    "🌊" => "\\:ocean:",
    "🌋" => "\\:volcano:",
    "🌌" => "\\:milky_way:",
    "🌍" => "\\:earth_africa:",
    "🌎" => "\\:earth_americas:",
    "🌏" => "\\:earth_asia:",
    "🌐" => "\\:globe_with_meridians:",
    "🌑" => "\\:new_moon:",
    "🌒" => "\\:waxing_crescent_moon:",
    "🌓" => "\\:first_quarter_moon:",
    "🌔" => "\\:moon:",
    "🌕" => "\\:full_moon:",
    "🌖" => "\\:waning_gibbous_moon:",
    "🌗" => "\\:last_quarter_moon:",
    "🌘" => "\\:waning_crescent_moon:",
    "🌙" => "\\:crescent_moon:",
    "🌚" => "\\:new_moon_with_face:",
    "🌛" => "\\:first_quarter_moon_with_face:",
    "🌜" => "\\:last_quarter_moon_with_face:",
    "🌝" => "\\:full_moon_with_face:",
    "🌞" => "\\:sun_with_face:",
    "🌟" => "\\:star2:",
    "🌠" => "\\:stars:",
    "🌡" => "\\:thermometer:",
    "🌤" => "\\:mostly_sunny:",
    "🌥" => "\\:barely_sunny:",
    "🌦" => "\\:partly_sunny_rain:",
    "🌧" => "\\:rain_cloud:",
    "🌨" => "\\:snow_cloud:",
    "🌩" => "\\:lightning:",
    "🌪" => "\\:tornado:",
    "🌫" => "\\:fog:",
    "🌬" => "\\:wind_blowing_face:",
    "🌭" => "\\:hotdog:",
    "🌮" => "\\:taco:",
    "🌯" => "\\:burrito:",
    "🌰" => "\\:chestnut:",
    "🌱" => "\\:seedling:",
    "🌲" => "\\:evergreen_tree:",
    "🌳" => "\\:deciduous_tree:",
    "🌴" => "\\:palm_tree:",
    "🌵" => "\\:cactus:",
    "🌶" => "\\:hot_pepper:",
    "🌷" => "\\:tulip:",
    "🌸" => "\\:cherry_blossom:",
    "🌹" => "\\:rose:",
    "🌺" => "\\:hibiscus:",
    "🌻" => "\\:sunflower:",
    "🌼" => "\\:blossom:",
    "🌽" => "\\:corn:",
    "🌾" => "\\:ear_of_rice:",
    "🌿" => "\\:herb:",
    "🍀" => "\\:four_leaf_clover:",
    "🍁" => "\\:maple_leaf:",
    "🍂" => "\\:fallen_leaf:",
    "🍃" => "\\:leaves:",
    "🍄" => "\\:mushroom:",
    "🍅" => "\\:tomato:",
    "🍆" => "\\:eggplant:",
    "🍇" => "\\:grapes:",
    "🍈" => "\\:melon:",
    "🍉" => "\\:watermelon:",
    "🍊" => "\\:tangerine:",
    "🍋" => "\\:lemon:",
    "🍌" => "\\:banana:",
    "🍍" => "\\:pineapple:",
    "🍎" => "\\:apple:",
    "🍏" => "\\:green_apple:",
    "🍐" => "\\:pear:",
    "🍑" => "\\:peach:",
    "🍒" => "\\:cherries:",
    "🍓" => "\\:strawberry:",
    "🍔" => "\\:hamburger:",
    "🍕" => "\\:pizza:",
    "🍖" => "\\:meat_on_bone:",
    "🍗" => "\\:poultry_leg:",
    "🍘" => "\\:rice_cracker:",
    "🍙" => "\\:rice_ball:",
    "🍚" => "\\:rice:",
    "🍛" => "\\:curry:",
    "🍜" => "\\:ramen:",
    "🍝" => "\\:spaghetti:",
    "🍞" => "\\:bread:",
    "🍟" => "\\:fries:",
    "🍠" => "\\:sweet_potato:",
    "🍡" => "\\:dango:",
    "🍢" => "\\:oden:",
    "🍣" => "\\:sushi:",
    "🍤" => "\\:fried_shrimp:",
    "🍥" => "\\:fish_cake:",
    "🍦" => "\\:icecream:",
    "🍧" => "\\:shaved_ice:",
    "🍨" => "\\:ice_cream:",
    "🍩" => "\\:doughnut:",
    "🍪" => "\\:cookie:",
    "🍫" => "\\:chocolate_bar:",
    "🍬" => "\\:candy:",
    "🍭" => "\\:lollipop:",
    "🍮" => "\\:custard:",
    "🍯" => "\\:honey_pot:",
    "🍰" => "\\:cake:",
    "🍱" => "\\:bento:",
    "🍲" => "\\:stew:",
    "🍳" => "\\:fried_egg:",
    "🍴" => "\\:fork_and_knife:",
    "🍵" => "\\:tea:",
    "🍶" => "\\:sake:",
    "🍷" => "\\:wine_glass:",
    "🍸" => "\\:cocktail:",
    "🍹" => "\\:tropical_drink:",
    "🍺" => "\\:beer:",
    "🍻" => "\\:beers:",
    "🍼" => "\\:baby_bottle:",
    "🍽" => "\\:knife_fork_plate:",
    "🍾" => "\\:champagne:",
    "🍿" => "\\:popcorn:",
    "🎀" => "\\:ribbon:",
    "🎁" => "\\:gift:",
    "🎂" => "\\:birthday:",
    "🎃" => "\\:jack_o_lantern:",
    "🎄" => "\\:christmas_tree:",
    "🎅" => "\\:santa:",
    "🎆" => "\\:fireworks:",
    "🎇" => "\\:sparkler:",
    "🎈" => "\\:balloon:",
    "🎉" => "\\:tada:",
    "🎊" => "\\:confetti_ball:",
    "🎋" => "\\:tanabata_tree:",
    "🎌" => "\\:crossed_flags:",
    "🎍" => "\\:bamboo:",
    "🎎" => "\\:dolls:",
    "🎏" => "\\:flags:",
    "🎐" => "\\:wind_chime:",
    "🎑" => "\\:rice_scene:",
    "🎒" => "\\:school_satchel:",
    "🎓" => "\\:mortar_board:",
    "🎖" => "\\:medal:",
    "🎗" => "\\:reminder_ribbon:",
    "🎙" => "\\:studio_microphone:",
    "🎚" => "\\:level_slider:",
    "🎛" => "\\:control_knobs:",
    "🎞" => "\\:film_frames:",
    "🎟" => "\\:admission_tickets:",
    "🎠" => "\\:carousel_horse:",
    "🎡" => "\\:ferris_wheel:",
    "🎢" => "\\:roller_coaster:",
    "🎣" => "\\:fishing_pole_and_fish:",
    "🎤" => "\\:microphone:",
    "🎥" => "\\:movie_camera:",
    "🎦" => "\\:cinema:",
    "🎧" => "\\:headphones:",
    "🎨" => "\\:art:",
    "🎩" => "\\:tophat:",
    "🎪" => "\\:circus_tent:",
    "🎫" => "\\:ticket:",
    "🎬" => "\\:clapper:",
    "🎭" => "\\:performing_arts:",
    "🎮" => "\\:video_game:",
    "🎯" => "\\:dart:",
    "🎰" => "\\:slot_machine:",
    "🎱" => "\\:8ball:",
    "🎲" => "\\:game_die:",
    "🎳" => "\\:bowling:",
    "🎴" => "\\:flower_playing_cards:",
    "🎵" => "\\:musical_note:",
    "🎶" => "\\:notes:",
    "🎷" => "\\:saxophone:",
    "🎸" => "\\:guitar:",
    "🎹" => "\\:musical_keyboard:",
    "🎺" => "\\:trumpet:",
    "🎻" => "\\:violin:",
    "🎼" => "\\:musical_score:",
    "🎽" => "\\:running_shirt_with_sash:",
    "🎾" => "\\:tennis:",
    "🎿" => "\\:ski:",
    "🏀" => "\\:basketball:",
    "🏁" => "\\:checkered_flag:",
    "🏂" => "\\:snowboarder:",
    "🏃" => "\\:runner:",
    "🏃\u200d♀" => "\\:woman-running:",
    "🏃\u200d♂" => "\\:man-running:",
    "🏄" => "\\:surfer:",
    "🏄\u200d♀" => "\\:woman-surfing:",
    "🏄\u200d♂" => "\\:man-surfing:",
    "🏅" => "\\:sports_medal:",
    "🏆" => "\\:trophy:",
    "🏇" => "\\:horse_racing:",
    "🏈" => "\\:football:",
    "🏉" => "\\:rugby_football:",
    "🏊" => "\\:swimmer:",
    "🏊\u200d♀" => "\\:woman-swimming:",
    "🏊\u200d♂" => "\\:man-swimming:",
    "🏋" => "\\:weight_lifter:",
    "🏋\u200d♀" => "\\:woman-lifting-weights:",
    "🏋\u200d♂" => "\\:man-lifting-weights:",
    "🏌" => "\\:golfer:",
    "🏌\u200d♀" => "\\:woman-golfing:",
    "🏌\u200d♂" => "\\:man-golfing:",
    "🏍" => "\\:racing_motorcycle:",
    "🏎" => "\\:racing_car:",
    "🏏" => "\\:cricket_bat_and_ball:",
    "🏐" => "\\:volleyball:",
    "🏑" => "\\:field_hockey_stick_and_ball:",
    "🏒" => "\\:ice_hockey_stick_and_puck:",
    "🏓" => "\\:table_tennis_paddle_and_ball:",
    "🏔" => "\\:snow_capped_mountain:",
    "🏕" => "\\:camping:",
    "🏖" => "\\:beach_with_umbrella:",
    "🏗" => "\\:building_construction:",
    "🏘" => "\\:house_buildings:",
    "🏙" => "\\:cityscape:",
    "🏚" => "\\:derelict_house_building:",
    "🏛" => "\\:classical_building:",
    "🏜" => "\\:desert:",
    "🏝" => "\\:desert_island:",
    "🏞" => "\\:national_park:",
    "🏟" => "\\:stadium:",
    "🏠" => "\\:house:",
    "🏡" => "\\:house_with_garden:",
    "🏢" => "\\:office:",
    "🏣" => "\\:post_office:",
    "🏤" => "\\:european_post_office:",
    "🏥" => "\\:hospital:",
    "🏦" => "\\:bank:",
    "🏧" => "\\:atm:",
    "🏨" => "\\:hotel:",
    "🏩" => "\\:love_hotel:",
    "🏪" => "\\:convenience_store:",
    "🏫" => "\\:school:",
    "🏬" => "\\:department_store:",
    "🏭" => "\\:factory:",
    "🏮" => "\\:izakaya_lantern:",
    "🏯" => "\\:japanese_castle:",
    "🏰" => "\\:european_castle:",
    "🏳" => "\\:waving_white_flag:",
    "🏳\u200d⚧" => "\\:transgender_flag:",
    "🏳\u200d🌈" => "\\:rainbow-flag:",
    "🏴" => "\\:waving_black_flag:",
    "🏴\u200d☠" => "\\:pirate_flag:",
    "🏴\Ue0067\Ue0062\Ue0065\Ue006e\Ue0067\Ue007f" => "\\:flag-england:",
    "🏴\Ue0067\Ue0062\Ue0073\Ue0063\Ue0074\Ue007f" => "\\:flag-scotland:",
    "🏴\Ue0067\Ue0062\Ue0077\Ue006c\Ue0073\Ue007f" => "\\:flag-wales:",
    "🏵" => "\\:rosette:",
    "🏷" => "\\:label:",
    "🏸" => "\\:badminton_racquet_and_shuttlecock:",
    "🏹" => "\\:bow_and_arrow:",
    "🏺" => "\\:amphora:",
    "🏻" => "\\:skin-tone-2:",
    "🏼" => "\\:skin-tone-3:",
    "🏽" => "\\:skin-tone-4:",
    "🏾" => "\\:skin-tone-5:",
    "🏿" => "\\:skin-tone-6:",
    "🐀" => "\\:rat:",
    "🐁" => "\\:mouse2:",
    "🐂" => "\\:ox:",
    "🐃" => "\\:water_buffalo:",
    "🐄" => "\\:cow2:",
    "🐅" => "\\:tiger2:",
    "🐆" => "\\:leopard:",
    "🐇" => "\\:rabbit2:",
    "🐈" => "\\:cat2:",
    "🐈\u200d⬛" => "\\:black_cat:",
    "🐉" => "\\:dragon:",
    "🐊" => "\\:crocodile:",
    "🐋" => "\\:whale2:",
    "🐌" => "\\:snail:",
    "🐍" => "\\:snake:",
    "🐎" => "\\:racehorse:",
    "🐏" => "\\:ram:",
    "🐐" => "\\:goat:",
    "🐑" => "\\:sheep:",
    "🐒" => "\\:monkey:",
    "🐓" => "\\:rooster:",
    "🐔" => "\\:chicken:",
    "🐕" => "\\:dog2:",
    "🐕\u200d🦺" => "\\:service_dog:",
    "🐖" => "\\:pig2:",
    "🐗" => "\\:boar:",
    "🐘" => "\\:elephant:",
    "🐙" => "\\:octopus:",
    "🐚" => "\\:shell:",
    "🐛" => "\\:bug:",
    "🐜" => "\\:ant:",
    "🐝" => "\\:bee:",
    "🐞" => "\\:ladybug:",
    "🐟" => "\\:fish:",
    "🐠" => "\\:tropical_fish:",
    "🐡" => "\\:blowfish:",
    "🐢" => "\\:turtle:",
    "🐣" => "\\:hatching_chick:",
    "🐤" => "\\:baby_chick:",
    "🐥" => "\\:hatched_chick:",
    "🐦" => "\\:bird:",
    "🐧" => "\\:penguin:",
    "🐨" => "\\:koala:",
    "🐩" => "\\:poodle:",
    "🐪" => "\\:dromedary_camel:",
    "🐫" => "\\:camel:",
    "🐬" => "\\:dolphin:",
    "🐭" => "\\:mouse:",
    "🐮" => "\\:cow:",
    "🐯" => "\\:tiger:",
    "🐰" => "\\:rabbit:",
    "🐱" => "\\:cat:",
    "🐲" => "\\:dragon_face:",
    "🐳" => "\\:whale:",
    "🐴" => "\\:horse:",
    "🐵" => "\\:monkey_face:",
    "🐶" => "\\:dog:",
    "🐷" => "\\:pig:",
    "🐸" => "\\:frog:",
    "🐹" => "\\:hamster:",
    "🐺" => "\\:wolf:",
    "🐻" => "\\:bear:",
    "🐻\u200d❄" => "\\:polar_bear:",
    "🐼" => "\\:panda_face:",
    "🐽" => "\\:pig_nose:",
    "🐾" => "\\:feet:",
    "🐿" => "\\:chipmunk:",
    "👀" => "\\:eyes:",
    "👁" => "\\:eye:",
    "👁\u200d🗨" => "\\:eye-in-speech-bubble:",
    "👂" => "\\:ear:",
    "👃" => "\\:nose:",
    "👄" => "\\:lips:",
    "👅" => "\\:tongue:",
    "👆" => "\\:point_up_2:",
    "👇" => "\\:point_down:",
    "👈" => "\\:point_left:",
    "👉" => "\\:point_right:",
    "👊" => "\\:facepunch:",
    "👋" => "\\:wave:",
    "👌" => "\\:ok_hand:",
    "👍" => "\\:+1:",
    "👎" => "\\:-1:",
    "👏" => "\\:clap:",
    "👐" => "\\:open_hands:",
    "👑" => "\\:crown:",
    "👒" => "\\:womans_hat:",
    "👓" => "\\:eyeglasses:",
    "👔" => "\\:necktie:",
    "👕" => "\\:shirt:",
    "👖" => "\\:jeans:",
    "👗" => "\\:dress:",
    "👘" => "\\:kimono:",
    "👙" => "\\:bikini:",
    "👚" => "\\:womans_clothes:",
    "👛" => "\\:purse:",
    "👜" => "\\:handbag:",
    "👝" => "\\:pouch:",
    "👞" => "\\:mans_shoe:",
    "👟" => "\\:athletic_shoe:",
    "👠" => "\\:high_heel:",
    "👡" => "\\:sandal:",
    "👢" => "\\:boot:",
    "👣" => "\\:footprints:",
    "👤" => "\\:bust_in_silhouette:",
    "👥" => "\\:busts_in_silhouette:",
    "👦" => "\\:boy:",
    "👧" => "\\:girl:",
    "👨" => "\\:man:",
    "👨\u200d⚕" => "\\:male-doctor:",
    "👨\u200d⚖" => "\\:male-judge:",
    "👨\u200d✈" => "\\:male-pilot:",
    "👨\u200d❤\u200d👨" => "\\:man-heart-man:",
    "👨\u200d❤\u200d💋\u200d👨" => "\\:man-kiss-man:",
    "👨\u200d🌾" => "\\:male-farmer:",
    "👨\u200d🍳" => "\\:male-cook:",
    "👨\u200d🍼" => "\\:man_feeding_baby:",
    "👨\u200d🎓" => "\\:male-student:",
    "👨\u200d🎤" => "\\:male-singer:",
    "👨\u200d🎨" => "\\:male-artist:",
    "👨\u200d🏫" => "\\:male-teacher:",
    "👨\u200d🏭" => "\\:male-factory-worker:",
    "👨\u200d👦" => "\\:man-boy:",
    "👨\u200d👦\u200d👦" => "\\:man-boy-boy:",
    "👨\u200d👧" => "\\:man-girl:",
    "👨\u200d👧\u200d👦" => "\\:man-girl-boy:",
    "👨\u200d👧\u200d👧" => "\\:man-girl-girl:",
    "👨\u200d👨\u200d👦" => "\\:man-man-boy:",
    "👨\u200d👨\u200d👦\u200d👦" => "\\:man-man-boy-boy:",
    "👨\u200d👨\u200d👧" => "\\:man-man-girl:",
    "👨\u200d👨\u200d👧\u200d👦" => "\\:man-man-girl-boy:",
    "👨\u200d👨\u200d👧\u200d👧" => "\\:man-man-girl-girl:",
    "👨\u200d👩\u200d👦" => "\\:man-woman-boy:",
    "👨\u200d👩\u200d👦\u200d👦" => "\\:man-woman-boy-boy:",
    "👨\u200d👩\u200d👧" => "\\:man-woman-girl:",
    "👨\u200d👩\u200d👧\u200d👦" => "\\:man-woman-girl-boy:",
    "👨\u200d👩\u200d👧\u200d👧" => "\\:man-woman-girl-girl:",
    "👨\u200d💻" => "\\:male-technologist:",
    "👨\u200d💼" => "\\:male-office-worker:",
    "👨\u200d🔧" => "\\:male-mechanic:",
    "👨\u200d🔬" => "\\:male-scientist:",
    "👨\u200d🚀" => "\\:male-astronaut:",
    "👨\u200d🚒" => "\\:male-firefighter:",
    "👨\u200d🦯" => "\\:man_with_probing_cane:",
    "👨\u200d🦰" => "\\:red_haired_man:",
    "👨\u200d🦱" => "\\:curly_haired_man:",
    "👨\u200d🦲" => "\\:bald_man:",
    "👨\u200d🦳" => "\\:white_haired_man:",
    "👨\u200d🦼" => "\\:man_in_motorized_wheelchair:",
    "👨\u200d🦽" => "\\:man_in_manual_wheelchair:",
    "👩" => "\\:woman:",
    "👩\u200d⚕" => "\\:female-doctor:",
    "👩\u200d⚖" => "\\:female-judge:",
    "👩\u200d✈" => "\\:female-pilot:",
    "👩\u200d❤\u200d👨" => "\\:woman-heart-man:",
    "👩\u200d❤\u200d👩" => "\\:woman-heart-woman:",
    "👩\u200d❤\u200d💋\u200d👨" => "\\:woman-kiss-man:",
    "👩\u200d❤\u200d💋\u200d👩" => "\\:woman-kiss-woman:",
    "👩\u200d🌾" => "\\:female-farmer:",
    "👩\u200d🍳" => "\\:female-cook:",
    "👩\u200d🍼" => "\\:woman_feeding_baby:",
    "👩\u200d🎓" => "\\:female-student:",
    "👩\u200d🎤" => "\\:female-singer:",
    "👩\u200d🎨" => "\\:female-artist:",
    "👩\u200d🏫" => "\\:female-teacher:",
    "👩\u200d🏭" => "\\:female-factory-worker:",
    "👩\u200d👦" => "\\:woman-boy:",
    "👩\u200d👦\u200d👦" => "\\:woman-boy-boy:",
    "👩\u200d👧" => "\\:woman-girl:",
    "👩\u200d👧\u200d👦" => "\\:woman-girl-boy:",
    "👩\u200d👧\u200d👧" => "\\:woman-girl-girl:",
    "👩\u200d👩\u200d👦" => "\\:woman-woman-boy:",
    "👩\u200d👩\u200d👦\u200d👦" => "\\:woman-woman-boy-boy:",
    "👩\u200d👩\u200d👧" => "\\:woman-woman-girl:",
    "👩\u200d👩\u200d👧\u200d👦" => "\\:woman-woman-girl-boy:",
    "👩\u200d👩\u200d👧\u200d👧" => "\\:woman-woman-girl-girl:",
    "👩\u200d💻" => "\\:female-technologist:",
    "👩\u200d💼" => "\\:female-office-worker:",
    "👩\u200d🔧" => "\\:female-mechanic:",
    "👩\u200d🔬" => "\\:female-scientist:",
    "👩\u200d🚀" => "\\:female-astronaut:",
    "👩\u200d🚒" => "\\:female-firefighter:",
    "👩\u200d🦯" => "\\:woman_with_probing_cane:",
    "👩\u200d🦰" => "\\:red_haired_woman:",
    "👩\u200d🦱" => "\\:curly_haired_woman:",
    "👩\u200d🦲" => "\\:bald_woman:",
    "👩\u200d🦳" => "\\:white_haired_woman:",
    "👩\u200d🦼" => "\\:woman_in_motorized_wheelchair:",
    "👩\u200d🦽" => "\\:woman_in_manual_wheelchair:",
    "👪" => "\\:family:",
    "👫" => "\\:man_and_woman_holding_hands:",
    "👬" => "\\:two_men_holding_hands:",
    "👭" => "\\:two_women_holding_hands:",
    "👮" => "\\:cop:",
    "👮\u200d♀" => "\\:female-police-officer:",
    "👮\u200d♂" => "\\:male-police-officer:",
    "👯" => "\\:dancers:",
    "👯\u200d♀" => "\\:women-with-bunny-ears-partying:",
    "👯\u200d♂" => "\\:men-with-bunny-ears-partying:",
    "👰" => "\\:bride_with_veil:",
    "👰\u200d♀" => "\\:woman_with_veil:",
    "👰\u200d♂" => "\\:man_with_veil:",
    "👱" => "\\:person_with_blond_hair:",
    "👱\u200d♀" => "\\:blond-haired-woman:",
    "👱\u200d♂" => "\\:blond-haired-man:",
    "👲" => "\\:man_with_gua_pi_mao:",
    "👳" => "\\:man_with_turban:",
    "👳\u200d♀" => "\\:woman-wearing-turban:",
    "👳\u200d♂" => "\\:man-wearing-turban:",
    "👴" => "\\:older_man:",
    "👵" => "\\:older_woman:",
    "👶" => "\\:baby:",
    "👷" => "\\:construction_worker:",
    "👷\u200d♀" => "\\:female-construction-worker:",
    "👷\u200d♂" => "\\:male-construction-worker:",
    "👸" => "\\:princess:",
    "👹" => "\\:japanese_ogre:",
    "👺" => "\\:japanese_goblin:",
    "👻" => "\\:ghost:",
    "👼" => "\\:angel:",
    "👽" => "\\:alien:",
    "👾" => "\\:space_invader:",
    "👿" => "\\:imp:",
    "💀" => "\\:skull:",
    "💁" => "\\:information_desk_person:",
    "💁\u200d♀" => "\\:woman-tipping-hand:",
    "💁\u200d♂" => "\\:man-tipping-hand:",
    "💂" => "\\:guardsman:",
    "💂\u200d♀" => "\\:female-guard:",
    "💂\u200d♂" => "\\:male-guard:",
    "💃" => "\\:dancer:",
    "💄" => "\\:lipstick:",
    "💅" => "\\:nail_care:",
    "💆" => "\\:massage:",
    "💆\u200d♀" => "\\:woman-getting-massage:",
    "💆\u200d♂" => "\\:man-getting-massage:",
    "💇" => "\\:haircut:",
    "💇\u200d♀" => "\\:woman-getting-haircut:",
    "💇\u200d♂" => "\\:man-getting-haircut:",
    "💈" => "\\:barber:",
    "💉" => "\\:syringe:",
    "💊" => "\\:pill:",
    "💋" => "\\:kiss:",
    "💌" => "\\:love_letter:",
    "💍" => "\\:ring:",
    "💎" => "\\:gem:",
    "💏" => "\\:couplekiss:",
    "💐" => "\\:bouquet:",
    "💑" => "\\:couple_with_heart:",
    "💒" => "\\:wedding:",
    "💓" => "\\:heartbeat:",
    "💔" => "\\:broken_heart:",
    "💕" => "\\:two_hearts:",
    "💖" => "\\:sparkling_heart:",
    "💗" => "\\:heartpulse:",
    "💘" => "\\:cupid:",
    "💙" => "\\:blue_heart:",
    "💚" => "\\:green_heart:",
    "💛" => "\\:yellow_heart:",
    "💜" => "\\:purple_heart:",
    "💝" => "\\:gift_heart:",
    "💞" => "\\:revolving_hearts:",
    "💟" => "\\:heart_decoration:",
    "💠" => "\\:diamond_shape_with_a_dot_inside:",
    "💡" => "\\:bulb:",
    "💢" => "\\:anger:",
    "💣" => "\\:bomb:",
    "💤" => "\\:zzz:",
    "💥" => "\\:boom:",
    "💦" => "\\:sweat_drops:",
    "💧" => "\\:droplet:",
    "💨" => "\\:dash:",
    "💩" => "\\:hankey:",
    "💪" => "\\:muscle:",
    "💫" => "\\:dizzy:",
    "💬" => "\\:speech_balloon:",
    "💭" => "\\:thought_balloon:",
    "💮" => "\\:white_flower:",
    "💯" => "\\:100:",
    "💰" => "\\:moneybag:",
    "💱" => "\\:currency_exchange:",
    "💲" => "\\:heavy_dollar_sign:",
    "💳" => "\\:credit_card:",
    "💴" => "\\:yen:",
    "💵" => "\\:dollar:",
    "💶" => "\\:euro:",
    "💷" => "\\:pound:",
    "💸" => "\\:money_with_wings:",
    "💹" => "\\:chart:",
    "💺" => "\\:seat:",
    "💻" => "\\:computer:",
    "💼" => "\\:briefcase:",
    "💽" => "\\:minidisc:",
    "💾" => "\\:floppy_disk:",
    "💿" => "\\:cd:",
    "📀" => "\\:dvd:",
    "📁" => "\\:file_folder:",
    "📂" => "\\:open_file_folder:",
    "📃" => "\\:page_with_curl:",
    "📄" => "\\:page_facing_up:",
    "📅" => "\\:date:",
    "📆" => "\\:calendar:",
    "📇" => "\\:card_index:",
    "📈" => "\\:chart_with_upwards_trend:",
    "📉" => "\\:chart_with_downwards_trend:",
    "📊" => "\\:bar_chart:",
    "📋" => "\\:clipboard:",
    "📌" => "\\:pushpin:",
    "📍" => "\\:round_pushpin:",
    "📎" => "\\:paperclip:",
    "📏" => "\\:straight_ruler:",
    "📐" => "\\:triangular_ruler:",
    "📑" => "\\:bookmark_tabs:",
    "📒" => "\\:ledger:",
    "📓" => "\\:notebook:",
    "📔" => "\\:notebook_with_decorative_cover:",
    "📕" => "\\:closed_book:",
    "📖" => "\\:book:",
    "📗" => "\\:green_book:",
    "📘" => "\\:blue_book:",
    "📙" => "\\:orange_book:",
    "📚" => "\\:books:",
    "📛" => "\\:name_badge:",
    "📜" => "\\:scroll:",
    "📝" => "\\:memo:",
    "📞" => "\\:telephone_receiver:",
    "📟" => "\\:pager:",
    "📠" => "\\:fax:",
    "📡" => "\\:satellite_antenna:",
    "📢" => "\\:loudspeaker:",
    "📣" => "\\:mega:",
    "📤" => "\\:outbox_tray:",
    "📥" => "\\:inbox_tray:",
    "📦" => "\\:package:",
    "📧" => "\\:e-mail:",
    "📨" => "\\:incoming_envelope:",
    "📩" => "\\:envelope_with_arrow:",
    "📪" => "\\:mailbox_closed:",
    "📫" => "\\:mailbox:",
    "📬" => "\\:mailbox_with_mail:",
    "📭" => "\\:mailbox_with_no_mail:",
    "📮" => "\\:postbox:",
    "📯" => "\\:postal_horn:",
    "📰" => "\\:newspaper:",
    "📱" => "\\:iphone:",
    "📲" => "\\:calling:",
    "📳" => "\\:vibration_mode:",
    "📴" => "\\:mobile_phone_off:",
    "📵" => "\\:no_mobile_phones:",
    "📶" => "\\:signal_strength:",
    "📷" => "\\:camera:",
    "📸" => "\\:camera_with_flash:",
    "📹" => "\\:video_camera:",
    "📺" => "\\:tv:",
    "📻" => "\\:radio:",
    "📼" => "\\:vhs:",
    "📽" => "\\:film_projector:",
    "📿" => "\\:prayer_beads:",
    "🔀" => "\\:twisted_rightwards_arrows:",
    "🔁" => "\\:repeat:",
    "🔂" => "\\:repeat_one:",
    "🔃" => "\\:arrows_clockwise:",
    "🔄" => "\\:arrows_counterclockwise:",
    "🔅" => "\\:low_brightness:",
    "🔆" => "\\:high_brightness:",
    "🔇" => "\\:mute:",
    "🔈" => "\\:speaker:",
    "🔉" => "\\:sound:",
    "🔊" => "\\:loud_sound:",
    "🔋" => "\\:battery:",
    "🔌" => "\\:electric_plug:",
    "🔍" => "\\:mag:",
    "🔎" => "\\:mag_right:",
    "🔏" => "\\:lock_with_ink_pen:",
    "🔐" => "\\:closed_lock_with_key:",
    "🔑" => "\\:key:",
    "🔒" => "\\:lock:",
    "🔓" => "\\:unlock:",
    "🔔" => "\\:bell:",
    "🔕" => "\\:no_bell:",
    "🔖" => "\\:bookmark:",
    "🔗" => "\\:link:",
    "🔘" => "\\:radio_button:",
    "🔙" => "\\:back:",
    "🔚" => "\\:end:",
    "🔛" => "\\:on:",
    "🔜" => "\\:soon:",
    "🔝" => "\\:top:",
    "🔞" => "\\:underage:",
    "🔟" => "\\:keycap_ten:",
    "🔠" => "\\:capital_abcd:",
    "🔡" => "\\:abcd:",
    "🔢" => "\\:1234:",
    "🔣" => "\\:symbols:",
    "🔤" => "\\:abc:",
    "🔥" => "\\:fire:",
    "🔦" => "\\:flashlight:",
    "🔧" => "\\:wrench:",
    "🔨" => "\\:hammer:",
    "🔩" => "\\:nut_and_bolt:",
    "🔪" => "\\:hocho:",
    "🔫" => "\\:gun:",
    "🔬" => "\\:microscope:",
    "🔭" => "\\:telescope:",
    "🔮" => "\\:crystal_ball:",
    "🔯" => "\\:six_pointed_star:",
    "🔰" => "\\:beginner:",
    "🔱" => "\\:trident:",
    "🔲" => "\\:black_square_button:",
    "🔳" => "\\:white_square_button:",
    "🔴" => "\\:red_circle:",
    "🔵" => "\\:large_blue_circle:",
    "🔶" => "\\:large_orange_diamond:",
    "🔷" => "\\:large_blue_diamond:",
    "🔸" => "\\:small_orange_diamond:",
    "🔹" => "\\:small_blue_diamond:",
    "🔺" => "\\:small_red_triangle:",
    "🔻" => "\\:small_red_triangle_down:",
    "🔼" => "\\:arrow_up_small:",
    "🔽" => "\\:arrow_down_small:",
    "🕉" => "\\:om_symbol:",
    "🕊" => "\\:dove_of_peace:",
    "🕋" => "\\:kaaba:",
    "🕌" => "\\:mosque:",
    "🕍" => "\\:synagogue:",
    "🕎" => "\\:menorah_with_nine_branches:",
    "🕐" => "\\:clock1:",
    "🕑" => "\\:clock2:",
    "🕒" => "\\:clock3:",
    "🕓" => "\\:clock4:",
    "🕔" => "\\:clock5:",
    "🕕" => "\\:clock6:",
    "🕖" => "\\:clock7:",
    "🕗" => "\\:clock8:",
    "🕘" => "\\:clock9:",
    "🕙" => "\\:clock10:",
    "🕚" => "\\:clock11:",
    "🕛" => "\\:clock12:",
    "🕜" => "\\:clock130:",
    "🕝" => "\\:clock230:",
    "🕞" => "\\:clock330:",
    "🕟" => "\\:clock430:",
    "🕠" => "\\:clock530:",
    "🕡" => "\\:clock630:",
    "🕢" => "\\:clock730:",
    "🕣" => "\\:clock830:",
    "🕤" => "\\:clock930:",
    "🕥" => "\\:clock1030:",
    "🕦" => "\\:clock1130:",
    "🕧" => "\\:clock1230:",
    "🕯" => "\\:candle:",
    "🕰" => "\\:mantelpiece_clock:",
    "🕳" => "\\:hole:",
    "🕴" => "\\:man_in_business_suit_levitating:",
    "🕵" => "\\:sleuth_or_spy:",
    "🕵\u200d♀" => "\\:female-detective:",
    "🕵\u200d♂" => "\\:male-detective:",
    "🕶" => "\\:dark_sunglasses:",
    "🕷" => "\\:spider:",
    "🕸" => "\\:spider_web:",
    "🕹" => "\\:joystick:",
    "🕺" => "\\:man_dancing:",
    "🖇" => "\\:linked_paperclips:",
    "🖊" => "\\:lower_left_ballpoint_pen:",
    "🖋" => "\\:lower_left_fountain_pen:",
    "🖌" => "\\:lower_left_paintbrush:",
    "🖍" => "\\:lower_left_crayon:",
    "🖐" => "\\:raised_hand_with_fingers_splayed:",
    "🖕" => "\\:middle_finger:",
    "🖖" => "\\:spock-hand:",
    "🖤" => "\\:black_heart:",
    "🖥" => "\\:desktop_computer:",
    "🖨" => "\\:printer:",
    "🖱" => "\\:three_button_mouse:",
    "🖲" => "\\:trackball:",
    "🖼" => "\\:frame_with_picture:",
    "🗂" => "\\:card_index_dividers:",
    "🗃" => "\\:card_file_box:",
    "🗄" => "\\:file_cabinet:",
    "🗑" => "\\:wastebasket:",
    "🗒" => "\\:spiral_note_pad:",
    "🗓" => "\\:spiral_calendar_pad:",
    "🗜" => "\\:compression:",
    "🗝" => "\\:old_key:",
    "🗞" => "\\:rolled_up_newspaper:",
    "🗡" => "\\:dagger_knife:",
    "🗣" => "\\:speaking_head_in_silhouette:",
    "🗨" => "\\:left_speech_bubble:",
    "🗯" => "\\:right_anger_bubble:",
    "🗳" => "\\:ballot_box_with_ballot:",
    "🗺" => "\\:world_map:",
    "🗻" => "\\:mount_fuji:",
    "🗼" => "\\:tokyo_tower:",
    "🗽" => "\\:statue_of_liberty:",
    "🗾" => "\\:japan:",
    "🗿" => "\\:moyai:",
    "😀" => "\\:grinning:",
    "😁" => "\\:grin:",
    "😂" => "\\:joy:",
    "😃" => "\\:smiley:",
    "😄" => "\\:smile:",
    "😅" => "\\:sweat_smile:",
    "😆" => "\\:laughing:",
    "😇" => "\\:innocent:",
    "😈" => "\\:smiling_imp:",
    "😉" => "\\:wink:",
    "😊" => "\\:blush:",
    "😋" => "\\:yum:",
    "😌" => "\\:relieved:",
    "😍" => "\\:heart_eyes:",
    "😎" => "\\:sunglasses:",
    "😏" => "\\:smirk:",
    "😐" => "\\:neutral_face:",
    "😑" => "\\:expressionless:",
    "😒" => "\\:unamused:",
    "😓" => "\\:sweat:",
    "😔" => "\\:pensive:",
    "😕" => "\\:confused:",
    "😖" => "\\:confounded:",
    "😗" => "\\:kissing:",
    "😘" => "\\:kissing_heart:",
    "😙" => "\\:kissing_smiling_eyes:",
    "😚" => "\\:kissing_closed_eyes:",
    "😛" => "\\:stuck_out_tongue:",
    "😜" => "\\:stuck_out_tongue_winking_eye:",
    "😝" => "\\:stuck_out_tongue_closed_eyes:",
    "😞" => "\\:disappointed:",
    "😟" => "\\:worried:",
    "😠" => "\\:angry:",
    "😡" => "\\:rage:",
    "😢" => "\\:cry:",
    "😣" => "\\:persevere:",
    "😤" => "\\:triumph:",
    "😥" => "\\:disappointed_relieved:",
    "😦" => "\\:frowning:",
    "😧" => "\\:anguished:",
    "😨" => "\\:fearful:",
    "😩" => "\\:weary:",
    "😪" => "\\:sleepy:",
    "😫" => "\\:tired_face:",
    "😬" => "\\:grimacing:",
    "😭" => "\\:sob:",
    "😮" => "\\:open_mouth:",
    "😮\u200d💨" => "\\:face_exhaling:",
    "😯" => "\\:hushed:",
    "😰" => "\\:cold_sweat:",
    "😱" => "\\:scream:",
    "😲" => "\\:astonished:",
    "😳" => "\\:flushed:",
    "😴" => "\\:sleeping:",
    "😵" => "\\:dizzy_face:",
    "😵\u200d💫" => "\\:face_with_spiral_eyes:",
    "😶" => "\\:no_mouth:",
    "😶\u200d🌫" => "\\:face_in_clouds:",
    "😷" => "\\:mask:",
    "😸" => "\\:smile_cat:",
    "😹" => "\\:joy_cat:",
    "😺" => "\\:smiley_cat:",
    "😻" => "\\:heart_eyes_cat:",
    "😼" => "\\:smirk_cat:",
    "😽" => "\\:kissing_cat:",
    "😾" => "\\:pouting_cat:",
    "😿" => "\\:crying_cat_face:",
    "🙀" => "\\:scream_cat:",
    "🙁" => "\\:slightly_frowning_face:",
    "🙂" => "\\:slightly_smiling_face:",
    "🙃" => "\\:upside_down_face:",
    "🙄" => "\\:face_with_rolling_eyes:",
    "🙅" => "\\:no_good:",
    "🙅\u200d♀" => "\\:woman-gesturing-no:",
    "🙅\u200d♂" => "\\:man-gesturing-no:",
    "🙆" => "\\:ok_woman:",
    "🙆\u200d♀" => "\\:woman-gesturing-ok:",
    "🙆\u200d♂" => "\\:man-gesturing-ok:",
    "🙇" => "\\:bow:",
    "🙇\u200d♀" => "\\:woman-bowing:",
    "🙇\u200d♂" => "\\:man-bowing:",
    "🙈" => "\\:see_no_evil:",
    "🙉" => "\\:hear_no_evil:",
    "🙊" => "\\:speak_no_evil:",
    "🙋" => "\\:raising_hand:",
    "🙋\u200d♀" => "\\:woman-raising-hand:",
    "🙋\u200d♂" => "\\:man-raising-hand:",
    "🙌" => "\\:raised_hands:",
    "🙍" => "\\:person_frowning:",
    "🙍\u200d♀" => "\\:woman-frowning:",
    "🙍\u200d♂" => "\\:man-frowning:",
    "🙎" => "\\:person_with_pouting_face:",
    "🙎\u200d♀" => "\\:woman-pouting:",
    "🙎\u200d♂" => "\\:man-pouting:",
    "🙏" => "\\:pray:",
    "🚀" => "\\:rocket:",
    "🚁" => "\\:helicopter:",
    "🚂" => "\\:steam_locomotive:",
    "🚃" => "\\:railway_car:",
    "🚄" => "\\:bullettrain_side:",
    "🚅" => "\\:bullettrain_front:",
    "🚆" => "\\:train2:",
    "🚇" => "\\:metro:",
    "🚈" => "\\:light_rail:",
    "🚉" => "\\:station:",
    "🚊" => "\\:tram:",
    "🚋" => "\\:train:",
    "🚌" => "\\:bus:",
    "🚍" => "\\:oncoming_bus:",
    "🚎" => "\\:trolleybus:",
    "🚏" => "\\:busstop:",
    "🚐" => "\\:minibus:",
    "🚑" => "\\:ambulance:",
    "🚒" => "\\:fire_engine:",
    "🚓" => "\\:police_car:",
    "🚔" => "\\:oncoming_police_car:",
    "🚕" => "\\:taxi:",
    "🚖" => "\\:oncoming_taxi:",
    "🚗" => "\\:car:",
    "🚘" => "\\:oncoming_automobile:",
    "🚙" => "\\:blue_car:",
    "🚚" => "\\:truck:",
    "🚛" => "\\:articulated_lorry:",
    "🚜" => "\\:tractor:",
    "🚝" => "\\:monorail:",
    "🚞" => "\\:mountain_railway:",
    "🚟" => "\\:suspension_railway:",
    "🚠" => "\\:mountain_cableway:",
    "🚡" => "\\:aerial_tramway:",
    "🚢" => "\\:ship:",
    "🚣" => "\\:rowboat:",
    "🚣\u200d♀" => "\\:woman-rowing-boat:",
    "🚣\u200d♂" => "\\:man-rowing-boat:",
    "🚤" => "\\:speedboat:",
    "🚥" => "\\:traffic_light:",
    "🚦" => "\\:vertical_traffic_light:",
    "🚧" => "\\:construction:",
    "🚨" => "\\:rotating_light:",
    "🚩" => "\\:triangular_flag_on_post:",
    "🚪" => "\\:door:",
    "🚫" => "\\:no_entry_sign:",
    "🚬" => "\\:smoking:",
    "🚭" => "\\:no_smoking:",
    "🚮" => "\\:put_litter_in_its_place:",
    "🚯" => "\\:do_not_litter:",
    "🚰" => "\\:potable_water:",
    "🚱" => "\\:non-potable_water:",
    "🚲" => "\\:bike:",
    "🚳" => "\\:no_bicycles:",
    "🚴" => "\\:bicyclist:",
    "🚴\u200d♀" => "\\:woman-biking:",
    "🚴\u200d♂" => "\\:man-biking:",
    "🚵" => "\\:mountain_bicyclist:",
    "🚵\u200d♀" => "\\:woman-mountain-biking:",
    "🚵\u200d♂" => "\\:man-mountain-biking:",
    "🚶" => "\\:walking:",
    "🚶\u200d♀" => "\\:woman-walking:",
    "🚶\u200d♂" => "\\:man-walking:",
    "🚷" => "\\:no_pedestrians:",
    "🚸" => "\\:children_crossing:",
    "🚹" => "\\:mens:",
    "🚺" => "\\:womens:",
    "🚻" => "\\:restroom:",
    "🚼" => "\\:baby_symbol:",
    "🚽" => "\\:toilet:",
    "🚾" => "\\:wc:",
    "🚿" => "\\:shower:",
    "🛀" => "\\:bath:",
    "🛁" => "\\:bathtub:",
    "🛂" => "\\:passport_control:",
    "🛃" => "\\:customs:",
    "🛄" => "\\:baggage_claim:",
    "🛅" => "\\:left_luggage:",
    "🛋" => "\\:couch_and_lamp:",
    "🛌" => "\\:sleeping_accommodation:",
    "🛍" => "\\:shopping_bags:",
    "🛎" => "\\:bellhop_bell:",
    "🛏" => "\\:bed:",
    "🛐" => "\\:place_of_worship:",
    "🛑" => "\\:octagonal_sign:",
    "🛒" => "\\:shopping_trolley:",
    "🛕" => "\\:hindu_temple:",
    "🛖" => "\\:hut:",
    "🛗" => "\\:elevator:",
    "🛠" => "\\:hammer_and_wrench:",
    "🛡" => "\\:shield:",
    "🛢" => "\\:oil_drum:",
    "🛣" => "\\:motorway:",
    "🛤" => "\\:railway_track:",
    "🛥" => "\\:motor_boat:",
    "🛩" => "\\:small_airplane:",
    "🛫" => "\\:airplane_departure:",
    "🛬" => "\\:airplane_arriving:",
    "🛰" => "\\:satellite:",
    "🛳" => "\\:passenger_ship:",
    "🛴" => "\\:scooter:",
    "🛵" => "\\:motor_scooter:",
    "🛶" => "\\:canoe:",
    "🛷" => "\\:sled:",
    "🛸" => "\\:flying_saucer:",
    "🛹" => "\\:skateboard:",
    "🛺" => "\\:auto_rickshaw:",
    "🛻" => "\\:pickup_truck:",
    "🛼" => "\\:roller_skate:",
    "🟠" => "\\:large_orange_circle:",
    "🟡" => "\\:large_yellow_circle:",
    "🟢" => "\\:large_green_circle:",
    "🟣" => "\\:large_purple_circle:",
    "🟤" => "\\:large_brown_circle:",
    "🟥" => "\\:large_red_square:",
    "🟦" => "\\:large_blue_square:",
    "🟧" => "\\:large_orange_square:",
    "🟨" => "\\:large_yellow_square:",
    "🟩" => "\\:large_green_square:",
    "🟪" => "\\:large_purple_square:",
    "🟫" => "\\:large_brown_square:",
    "🤌" => "\\:pinched_fingers:",
    "🤍" => "\\:white_heart:",
    "🤎" => "\\:brown_heart:",
    "🤏" => "\\:pinching_hand:",
    "🤐" => "\\:zipper_mouth_face:",
    "🤑" => "\\:money_mouth_face:",
    "🤒" => "\\:face_with_thermometer:",
    "🤓" => "\\:nerd_face:",
    "🤔" => "\\:thinking_face:",
    "🤕" => "\\:face_with_head_bandage:",
    "🤖" => "\\:robot_face:",
    "🤗" => "\\:hugging_face:",
    "🤘" => "\\:the_horns:",
    "🤙" => "\\:call_me_hand:",
    "🤚" => "\\:raised_back_of_hand:",
    "🤛" => "\\:left-facing_fist:",
    "🤜" => "\\:right-facing_fist:",
    "🤝" => "\\:handshake:",
    "🤞" => "\\:crossed_fingers:",
    "🤟" => "\\:i_love_you_hand_sign:",
    "🤠" => "\\:face_with_cowboy_hat:",
    "🤡" => "\\:clown_face:",
    "🤢" => "\\:nauseated_face:",
    "🤣" => "\\:rolling_on_the_floor_laughing:",
    "🤤" => "\\:drooling_face:",
    "🤥" => "\\:lying_face:",
    "🤦" => "\\:face_palm:",
    "🤦\u200d♀" => "\\:woman-facepalming:",
    "🤦\u200d♂" => "\\:man-facepalming:",
    "🤧" => "\\:sneezing_face:",
    "🤨" => "\\:face_with_raised_eyebrow:",
    "🤩" => "\\:star-struck:",
    "🤪" => "\\:zany_face:",
    "🤫" => "\\:shushing_face:",
    "🤬" => "\\:face_with_symbols_on_mouth:",
    "🤭" => "\\:face_with_hand_over_mouth:",
    "🤮" => "\\:face_vomiting:",
    "🤯" => "\\:exploding_head:",
    "🤰" => "\\:pregnant_woman:",
    "🤱" => "\\:breast-feeding:",
    "🤲" => "\\:palms_up_together:",
    "🤳" => "\\:selfie:",
    "🤴" => "\\:prince:",
    "🤵" => "\\:person_in_tuxedo:",
    "🤵\u200d♀" => "\\:woman_in_tuxedo:",
    "🤵\u200d♂" => "\\:man_in_tuxedo:",
    "🤶" => "\\:mrs_claus:",
    "🤷" => "\\:shrug:",
    "🤷\u200d♀" => "\\:woman-shrugging:",
    "🤷\u200d♂" => "\\:man-shrugging:",
    "🤸" => "\\:person_doing_cartwheel:",
    "🤸\u200d♀" => "\\:woman-cartwheeling:",
    "🤸\u200d♂" => "\\:man-cartwheeling:",
    "🤹" => "\\:juggling:",
    "🤹\u200d♀" => "\\:woman-juggling:",
    "🤹\u200d♂" => "\\:man-juggling:",
    "🤺" => "\\:fencer:",
    "🤼" => "\\:wrestlers:",
    "🤼\u200d♀" => "\\:woman-wrestling:",
    "🤼\u200d♂" => "\\:man-wrestling:",
    "🤽" => "\\:water_polo:",
    "🤽\u200d♀" => "\\:woman-playing-water-polo:",
    "🤽\u200d♂" => "\\:man-playing-water-polo:",
    "🤾" => "\\:handball:",
    "🤾\u200d♀" => "\\:woman-playing-handball:",
    "🤾\u200d♂" => "\\:man-playing-handball:",
    "🤿" => "\\:diving_mask:",
    "🥀" => "\\:wilted_flower:",
    "🥁" => "\\:drum_with_drumsticks:",
    "🥂" => "\\:clinking_glasses:",
    "🥃" => "\\:tumbler_glass:",
    "🥄" => "\\:spoon:",
    "🥅" => "\\:goal_net:",
    "🥇" => "\\:first_place_medal:",
    "🥈" => "\\:second_place_medal:",
    "🥉" => "\\:third_place_medal:",
    "🥊" => "\\:boxing_glove:",
    "🥋" => "\\:martial_arts_uniform:",
    "🥌" => "\\:curling_stone:",
    "🥍" => "\\:lacrosse:",
    "🥎" => "\\:softball:",
    "🥏" => "\\:flying_disc:",
    "🥐" => "\\:croissant:",
    "🥑" => "\\:avocado:",
    "🥒" => "\\:cucumber:",
    "🥓" => "\\:bacon:",
    "🥔" => "\\:potato:",
    "🥕" => "\\:carrot:",
    "🥖" => "\\:baguette_bread:",
    "🥗" => "\\:green_salad:",
    "🥘" => "\\:shallow_pan_of_food:",
    "🥙" => "\\:stuffed_flatbread:",
    "🥚" => "\\:egg:",
    "🥛" => "\\:glass_of_milk:",
    "🥜" => "\\:peanuts:",
    "🥝" => "\\:kiwifruit:",
    "🥞" => "\\:pancakes:",
    "🥟" => "\\:dumpling:",
    "🥠" => "\\:fortune_cookie:",
    "🥡" => "\\:takeout_box:",
    "🥢" => "\\:chopsticks:",
    "🥣" => "\\:bowl_with_spoon:",
    "🥤" => "\\:cup_with_straw:",
    "🥥" => "\\:coconut:",
    "🥦" => "\\:broccoli:",
    "🥧" => "\\:pie:",
    "🥨" => "\\:pretzel:",
    "🥩" => "\\:cut_of_meat:",
    "🥪" => "\\:sandwich:",
    "🥫" => "\\:canned_food:",
    "🥬" => "\\:leafy_green:",
    "🥭" => "\\:mango:",
    "🥮" => "\\:moon_cake:",
    "🥯" => "\\:bagel:",
    "🥰" => "\\:smiling_face_with_3_hearts:",
    "🥱" => "\\:yawning_face:",
    "🥲" => "\\:smiling_face_with_tear:",
    "🥳" => "\\:partying_face:",
    "🥴" => "\\:woozy_face:",
    "🥵" => "\\:hot_face:",
    "🥶" => "\\:cold_face:",
    "🥷" => "\\:ninja:",
    "🥸" => "\\:disguised_face:",
    "🥺" => "\\:pleading_face:",
    "🥻" => "\\:sari:",
    "🥼" => "\\:lab_coat:",
    "🥽" => "\\:goggles:",
    "🥾" => "\\:hiking_boot:",
    "🥿" => "\\:womans_flat_shoe:",
    "🦀" => "\\:crab:",
    "🦁" => "\\:lion_face:",
    "🦂" => "\\:scorpion:",
    "🦃" => "\\:turkey:",
    "🦄" => "\\:unicorn_face:",
    "🦅" => "\\:eagle:",
    "🦆" => "\\:duck:",
    "🦇" => "\\:bat:",
    "🦈" => "\\:shark:",
    "🦉" => "\\:owl:",
    "🦊" => "\\:fox_face:",
    "🦋" => "\\:butterfly:",
    "🦌" => "\\:deer:",
    "🦍" => "\\:gorilla:",
    "🦎" => "\\:lizard:",
    "🦏" => "\\:rhinoceros:",
    "🦐" => "\\:shrimp:",
    "🦑" => "\\:squid:",
    "🦒" => "\\:giraffe_face:",
    "🦓" => "\\:zebra_face:",
    "🦔" => "\\:hedgehog:",
    "🦕" => "\\:sauropod:",
    "🦖" => "\\:t-rex:",
    "🦗" => "\\:cricket:",
    "🦘" => "\\:kangaroo:",
    "🦙" => "\\:llama:",
    "🦚" => "\\:peacock:",
    "🦛" => "\\:hippopotamus:",
    "🦜" => "\\:parrot:",
    "🦝" => "\\:raccoon:",
    "🦞" => "\\:lobster:",
    "🦟" => "\\:mosquito:",
    "🦠" => "\\:microbe:",
    "🦡" => "\\:badger:",
    "🦢" => "\\:swan:",
    "🦣" => "\\:mammoth:",
    "🦤" => "\\:dodo:",
    "🦥" => "\\:sloth:",
    "🦦" => "\\:otter:",
    "🦧" => "\\:orangutan:",
    "🦨" => "\\:skunk:",
    "🦩" => "\\:flamingo:",
    "🦪" => "\\:oyster:",
    "🦫" => "\\:beaver:",
    "🦬" => "\\:bison:",
    "🦭" => "\\:seal:",
    "🦮" => "\\:guide_dog:",
    "🦯" => "\\:probing_cane:",
    "🦴" => "\\:bone:",
    "🦵" => "\\:leg:",
    "🦶" => "\\:foot:",
    "🦷" => "\\:tooth:",
    "🦸" => "\\:superhero:",
    "🦸\u200d♀" => "\\:female_superhero:",
    "🦸\u200d♂" => "\\:male_superhero:",
    "🦹" => "\\:supervillain:",
    "🦹\u200d♀" => "\\:female_supervillain:",
    "🦹\u200d♂" => "\\:male_supervillain:",
    "🦺" => "\\:safety_vest:",
    "🦻" => "\\:ear_with_hearing_aid:",
    "🦼" => "\\:motorized_wheelchair:",
    "🦽" => "\\:manual_wheelchair:",
    "🦾" => "\\:mechanical_arm:",
    "🦿" => "\\:mechanical_leg:",
    "🧀" => "\\:cheese_wedge:",
    "🧁" => "\\:cupcake:",
    "🧂" => "\\:salt:",
    "🧃" => "\\:beverage_box:",
    "🧄" => "\\:garlic:",
    "🧅" => "\\:onion:",
    "🧆" => "\\:falafel:",
    "🧇" => "\\:waffle:",
    "🧈" => "\\:butter:",
    "🧉" => "\\:mate_drink:",
    "🧊" => "\\:ice_cube:",
    "🧋" => "\\:bubble_tea:",
    "🧍" => "\\:standing_person:",
    "🧍\u200d♀" => "\\:woman_standing:",
    "🧍\u200d♂" => "\\:man_standing:",
    "🧎" => "\\:kneeling_person:",
    "🧎\u200d♀" => "\\:woman_kneeling:",
    "🧎\u200d♂" => "\\:man_kneeling:",
    "🧏" => "\\:deaf_person:",
    "🧏\u200d♀" => "\\:deaf_woman:",
    "🧏\u200d♂" => "\\:deaf_man:",
    "🧐" => "\\:face_with_monocle:",
    "🧑" => "\\:adult:",
    "🧑\u200d⚕" => "\\:health_worker:",
    "🧑\u200d⚖" => "\\:judge:",
    "🧑\u200d✈" => "\\:pilot:",
    "🧑\u200d🌾" => "\\:farmer:",
    "🧑\u200d🍳" => "\\:cook:",
    "🧑\u200d🍼" => "\\:person_feeding_baby:",
    "🧑\u200d🎄" => "\\:mx_claus:",
    "🧑\u200d🎓" => "\\:student:",
    "🧑\u200d🎤" => "\\:singer:",
    "🧑\u200d🎨" => "\\:artist:",
    "🧑\u200d🏫" => "\\:teacher:",
    "🧑\u200d🏭" => "\\:factory_worker:",
    "🧑\u200d💻" => "\\:technologist:",
    "🧑\u200d💼" => "\\:office_worker:",
    "🧑\u200d🔧" => "\\:mechanic:",
    "🧑\u200d🔬" => "\\:scientist:",
    "🧑\u200d🚀" => "\\:astronaut:",
    "🧑\u200d🚒" => "\\:firefighter:",
    "🧑\u200d🤝\u200d🧑" => "\\:people_holding_hands:",
    "🧑\u200d🦯" => "\\:person_with_probing_cane:",
    "🧑\u200d🦰" => "\\:red_haired_person:",
    "🧑\u200d🦱" => "\\:curly_haired_person:",
    "🧑\u200d🦲" => "\\:bald_person:",
    "🧑\u200d🦳" => "\\:white_haired_person:",
    "🧑\u200d🦼" => "\\:person_in_motorized_wheelchair:",
    "🧑\u200d🦽" => "\\:person_in_manual_wheelchair:",
    "🧒" => "\\:child:",
    "🧓" => "\\:older_adult:",
    "🧔" => "\\:bearded_person:",
    "🧔\u200d♀" => "\\:woman_with_beard:",
    "🧔\u200d♂" => "\\:man_with_beard:",
    "🧕" => "\\:person_with_headscarf:",
    "🧖" => "\\:person_in_steamy_room:",
    "🧖\u200d♀" => "\\:woman_in_steamy_room:",
    "🧖\u200d♂" => "\\:man_in_steamy_room:",
    "🧗" => "\\:person_climbing:",
    "🧗\u200d♀" => "\\:woman_climbing:",
    "🧗\u200d♂" => "\\:man_climbing:",
    "🧘" => "\\:person_in_lotus_position:",
    "🧘\u200d♀" => "\\:woman_in_lotus_position:",
    "🧘\u200d♂" => "\\:man_in_lotus_position:",
    "🧙" => "\\:mage:",
    "🧙\u200d♀" => "\\:female_mage:",
    "🧙\u200d♂" => "\\:male_mage:",
    "🧚" => "\\:fairy:",
    "🧚\u200d♀" => "\\:female_fairy:",
    "🧚\u200d♂" => "\\:male_fairy:",
    "🧛" => "\\:vampire:",
    "🧛\u200d♀" => "\\:female_vampire:",
    "🧛\u200d♂" => "\\:male_vampire:",
    "🧜" => "\\:merperson:",
    "🧜\u200d♀" => "\\:mermaid:",
    "🧜\u200d♂" => "\\:merman:",
    "🧝" => "\\:elf:",
    "🧝\u200d♀" => "\\:female_elf:",
    "🧝\u200d♂" => "\\:male_elf:",
    "🧞" => "\\:genie:",
    "🧞\u200d♀" => "\\:female_genie:",
    "🧞\u200d♂" => "\\:male_genie:",
    "🧟" => "\\:zombie:",
    "🧟\u200d♀" => "\\:female_zombie:",
    "🧟\u200d♂" => "\\:male_zombie:",
    "🧠" => "\\:brain:",
    "🧡" => "\\:orange_heart:",
    "🧢" => "\\:billed_cap:",
    "🧣" => "\\:scarf:",
    "🧤" => "\\:gloves:",
    "🧥" => "\\:coat:",
    "🧦" => "\\:socks:",
    "🧧" => "\\:red_envelope:",
    "🧨" => "\\:firecracker:",
    "🧩" => "\\:jigsaw:",
    "🧪" => "\\:test_tube:",
    "🧫" => "\\:petri_dish:",
    "🧬" => "\\:dna:",
    "🧭" => "\\:compass:",
    "🧮" => "\\:abacus:",
    "🧯" => "\\:fire_extinguisher:",
    "🧰" => "\\:toolbox:",
    "🧱" => "\\:bricks:",
    "🧲" => "\\:magnet:",
    "🧳" => "\\:luggage:",
    "🧴" => "\\:lotion_bottle:",
    "🧵" => "\\:thread:",
    "🧶" => "\\:yarn:",
    "🧷" => "\\:safety_pin:",
    "🧸" => "\\:teddy_bear:",
    "🧹" => "\\:broom:",
    "🧺" => "\\:basket:",
    "🧻" => "\\:roll_of_paper:",
    "🧼" => "\\:soap:",
    "🧽" => "\\:sponge:",
    "🧾" => "\\:receipt:",
    "🧿" => "\\:nazar_amulet:",
    "🩰" => "\\:ballet_shoes:",
    "🩱" => "\\:one-piece_swimsuit:",
    "🩲" => "\\:briefs:",
    "🩳" => "\\:shorts:",
    "🩴" => "\\:thong_sandal:",
    "🩸" => "\\:drop_of_blood:",
    "🩹" => "\\:adhesive_bandage:",
    "🩺" => "\\:stethoscope:",
    "🪀" => "\\:yo-yo:",
    "🪁" => "\\:kite:",
    "🪂" => "\\:parachute:",
    "🪃" => "\\:boomerang:",
    "🪄" => "\\:magic_wand:",
    "🪅" => "\\:pinata:",
    "🪆" => "\\:nesting_dolls:",
    "🪐" => "\\:ringed_planet:",
    "🪑" => "\\:chair:",
    "🪒" => "\\:razor:",
    "🪓" => "\\:axe:",
    "🪔" => "\\:diya_lamp:",
    "🪕" => "\\:banjo:",
    "🪖" => "\\:military_helmet:",
    "🪗" => "\\:accordion:",
    "🪘" => "\\:long_drum:",
    "🪙" => "\\:coin:",
    "🪚" => "\\:carpentry_saw:",
    "🪛" => "\\:screwdriver:",
    "🪜" => "\\:ladder:",
    "🪝" => "\\:hook:",
    "🪞" => "\\:mirror:",
    "🪟" => "\\:window:",
    "🪠" => "\\:plunger:",
    "🪡" => "\\:sewing_needle:",
    "🪢" => "\\:knot:",
    "🪣" => "\\:bucket:",
    "🪤" => "\\:mouse_trap:",
    "🪥" => "\\:toothbrush:",
    "🪦" => "\\:headstone:",
    "🪧" => "\\:placard:",
    "🪨" => "\\:rock:",
    "🪰" => "\\:fly:",
    "🪱" => "\\:worm:",
    "🪲" => "\\:beetle:",
    "🪳" => "\\:cockroach:",
    "🪴" => "\\:potted_plant:",
    "🪵" => "\\:wood:",
    "🪶" => "\\:feather:",
    "🫀" => "\\:anatomical_heart:",
    "🫁" => "\\:lungs:",
    "🫂" => "\\:people_hugging:",
    "🫐" => "\\:blueberries:",
    "🫑" => "\\:bell_pepper:",
    "🫒" => "\\:olive:",
    "🫓" => "\\:flatbread:",
    "🫔" => "\\:tamale:",
    "🫕" => "\\:fondue:",
    "🫖" => "\\:teapot:",
)


text:
using
    Test,
    GNSSSimulator,
    Rotations,
    CoordinateTransformations,
    StaticArrays,
    GNSSSignals,
    Random,
    Unitful,
    LinearAlgebra,
    PhasedArray,
    StructArrays

import Unitful: dB, Hz, kHz, MHz, GHz, dBHz, m, s, rad, ms, μs

const EARTH_RADIUS = 6_360_000m

include("carrier.jl")
include("attitude.jl")
include("doa.jl")
include("existence.jl")
include("satellite.jl")
include("jammer.jl")
include("structural_interference.jl")
include("synthetic_satellite.jl")
include("gain_phase_mism_crosstalk.jl")
include("receiver.jl")
include("measurement.jl")

ext: jl


text:
@testset "gym envs" begin
    gym_env_names = ReinforcementLearningEnvironments.list_gym_env_names(
        modules = [
            "gym.envs.algorithmic",
            "gym.envs.classic_control",
            "gym.envs.unittest",
        ],
    )  # mujoco, box2d, robotics are not tested here

    for x in gym_env_names
        env = GymEnv(x)
        RLBase.test_runnable!(env)
    end
end


text:
using AssignTaxonomy
using Test

@testset "AssignTaxonomy.jl" begin
    # Write your tests here.
end


text:
using SparseArrays
using Base: RefValue
using BetterExp

export SABipartite, optimize_sa

"""
    SABipartite{RT,BT}
    SABipartite(; sc_target=25, ntrials=50, βs=0.1:0.2:15.0, niters=1000
        max_group_size=40, greedy_config=GreedyMethod(), initializer=:random)

Optimize the einsum code contraction order using the Simulated Annealing bipartition + Greedy approach.
This program first recursively cuts the tensors into several groups using simulated annealing,
with maximum group size specifed by `max_group_size` and maximum space complexity specified by `sc_target`,
Then finds the contraction order inside each group with the greedy search algorithm. Other arguments are

* `size_dict`, a dictionary that specifies leg dimensions,
* `sc_target` is the target space complexity, defined as `log2(number of elements in the largest tensor)`,
* `max_group_size` is the maximum size that allowed to used greedy search,
* `βs` is a list of inverse temperature `1/T`,
* `niters` is the number of iteration in each temperature,
* `ntrials` is the number of repetition (with different random seeds),
* `greedy_config` configures the greedy method,
* `initializer`, the partition configuration initializer, one can choose `:random` or `:greedy` (slow but better).

### References
* [Hyper-optimized tensor network contraction](https://arxiv.org/abs/2002.01935)
"""
Base.@kwdef struct SABipartite{RT,BT,GM} <: CodeOptimizer
    sc_target::RT = 25
    ntrials::Int = 50  # number of trials
    βs::BT = 0.1:0.2:15.0  # temperatures
    niters::Int = 1000  # number of iterations in each temperature
    max_group_size::Int = 40
    # configure greedy algorithm
    greedy_config::GM = GreedyMethod()
    initializer::Symbol = :random
end

struct PartitionState
    config::Vector{Int}
    loss::RefValue{Float64}

    group_sizes::Vector{Int}     # group sizes
    group_scs::Vector{Float64}   # space complexities
    group_degrees::Matrix{Int}   # degree of edges
end

function partition_state(adj, group, config, log2_sizes)
    group1 = [group[i] for (i,c) in enumerate(config) if c==1]
    group2 = [group[i] for (i,c) in enumerate(config) if c==2]
    group_sizes = [length(group1), length(group2)]   # group size in terms of number of vertices.
    group_scs = [group_sc(adj, group1, log2_sizes), group_sc(adj, group2, log2_sizes)]  # space complexity of each group.
    group_degrees = hcat(sum(adj[group1,:]; dims=1)', sum(adj[group2,:]; dims=1)')
    loss = compute_loss(group_scs..., group_sizes...)
    return PartitionState(config, Ref(loss), group_sizes, group_scs, group_degrees)
end

function bipartite_sc(bipartiter::SABipartite, adj::SparseMatrixCSC, vertices, log2_sizes)
    degrees_all = sum(adj, dims=1)
    adjt = SparseMatrixCSC(adj')
    config = _initialize(bipartiter.initializer,adj, vertices, log2_sizes)
    best = partition_state(adj, vertices, config, log2_sizes)  # this is the `state` of current partition.

    for _ = 1:bipartiter.ntrials
        config = _initialize(bipartiter.initializer,adj, vertices, log2_sizes)
        state = partition_state(adj, vertices, config, log2_sizes)  # this is the `state` of current partition.
        if state.group_sizes[1]==0 || state.group_sizes[2] == 0
            continue
        end

        @inbounds for β in bipartiter.βs, iter = 1:bipartiter.niters
            idxi = rand(1:length(vertices))
            ti = state.config[idxi]
            state.group_sizes[ti] <= 1 && continue

            sc_ti, sc_tinew = space_complexity_singlestep_update(state, adjt, degrees_all, log2_sizes, vertices, idxi)
            newloss = compute_loss(sc_ti, sc_tinew, state.group_sizes[ti]-1, state.group_sizes[3-ti]+1)
            sc_ti0, sc_tinew0 = state.group_scs[ti], state.group_scs[3-ti]
            accept = if max(sc_ti0, sc_tinew0) <= bipartiter.sc_target
                max(sc_ti, sc_tinew) <= bipartiter.sc_target && (rand() < BetterExp.exp2(β*(state.loss[] - newloss)))
            else
                rand() < BetterExp.exp2(-β*(max(sc_ti, sc_tinew) - max(sc_ti0, sc_tinew0)))
            end
            accept && update_state!(state, adjt, vertices, idxi, sc_ti, sc_tinew, newloss)
        end
        tc, sc1, sc2 = timespace_complexity_singlestep(state.config, adj, vertices, log2_sizes)
        @assert state.group_scs ≈ [sc1, sc2]  # sanity check
        if maximum(state.group_scs) <= max(bipartiter.sc_target, maximum(best.group_scs)) && (maximum(best.group_scs) >= bipartiter.sc_target || state.loss[] < best.loss[])
            best = state
        end
    end
    best_tc, = timespace_complexity_singlestep(best.config, adj, vertices, log2_sizes)
    @debug "best loss = $(round(best.loss[]; digits=3)) space complexities = $(best.group_scs) time complexity = $(best_tc) groups_sizes = $(best.group_sizes)"
    if maximum(best.group_scs) > bipartiter.sc_target
        @warn "target space complexity not found, got: $(maximum(best.group_scs)), with time complexity $best_tc."
    end
    return vertices[findall(==(1), best.config)], vertices[findall(==(2), best.config)]
end

function timespace_complexity_singlestep(config, adj, group, log2_sizes)
    g1 = group[findall(==(1), config)]
    g2 = group[findall(==(2), config)]
    d1 = sum(adj[g1,:], dims=1)
    d2 = sum(adj[g2,:], dims=1)
    dall = sum(adj, dims=1)
    sc1 = sum(i->(d1[i]!=0 && d1[i]!=dall[i] ? Float64(log2_sizes[i]) : 0.0), 1:size(adj,2))
    sc2 = sum(i->(d2[i]!=0 && d2[i]!=dall[i] ? Float64(log2_sizes[i]) : 0.0), 1:size(adj,2))
    tc = sum(i->((d2[i]!=0 || d1[i]!=0) && (d2[i]!=dall[i] && d1[i]!=dall[i]) ? Float64(log2_sizes[i]) : 0.0), 1:size(adj,2))
    return tc, sc1, sc2
end

function space_complexity_singlestep_update(state, adjt, degrees_all, log2_sizes, group, idxi)
    @inbounds begin
        vertex = group[idxi]
        ti = state.config[idxi]
        tinew = 3-ti
        δsc_ti = δsc(-, view(state.group_degrees, :, ti), adjt, vertex, degrees_all, log2_sizes)
        δsc_tinew = δsc(+, view(state.group_degrees, :, tinew), adjt, vertex, degrees_all, log2_sizes)
        sc_ti = state.group_scs[ti] + δsc_ti
        sc_tinew = state.group_scs[tinew] + δsc_tinew
    end
    return sc_ti, sc_tinew
end

@inline function δsc(f, group_degrees, adjt, vertex, degrees_all, log2_sizes)
    res = 0.0
    @inbounds for k in nzrange(adjt, vertex)
        i = adjt.rowval[k]
        d0 = group_degrees[i]
        D = degrees_all[i]
        d = f(d0, adjt.nzval[k])
        if d0 == D || d0 == 0     # absent
            if d != D && d != 0   # absent
                res += Float64(log2_sizes[i])
            end
        else                      # not absent
            if d == D || d == 0   # absent
                res -= Float64(log2_sizes[i])
            end
        end
    end
    return res
end

@inline function compute_loss(sc1, sc2, gs1, gs2)
    small = min(gs1, gs2)
    max(sc1, sc2) / small * (gs1 + gs2)
end

function update_state!(state, adjt, group, idxi, sc_ti, sc_tinew, newloss)
    @inbounds begin
        ti = state.config[idxi]
        tinew = 3-ti
        state.group_scs[tinew] = sc_tinew
        state.group_scs[ti] = sc_ti
        state.config[idxi] = tinew
        state.group_sizes[ti] -= 1
        state.group_sizes[tinew] += 1
        for i = nzrange(adjt,group[idxi])
            state.group_degrees[adjt.rowval[i], ti] -= adjt.nzval[i]
            state.group_degrees[adjt.rowval[i], tinew] += adjt.nzval[i]
        end
        state.loss[] = newloss
    end
    return state
end

_initialize(method, adj, vertices, log2_sizes) = if method == :random
    initialize_random(length(vertices))
elseif method == :greedy
    initialize_greedy(adj, vertices, log2_sizes)
else
    error("initializer not implemented: `$method`")
end

function initialize_greedy(adj, vertices, log2_sizes)
    adjt = SparseMatrixCSC(adj')
    indegrees = sum(adj[vertices,:], dims=1)
    all = sum(adj, dims=1)
    openedges = findall(i->all[i] > indegrees[i] > 0, 1:size(adj, 2))
    v2e = Dict{Int,Vector{Int}}()
    for v=1:size(adj, 1)
        v2e[v] = adjt.rowval[nzrange(adjt, v)]
    end
    incidence_list = OMEinsum.IncidenceList(v2e; openedges=openedges)
    log2_edge_sizes = Dict([i=>log2_sizes[i] for i=1:length(log2_sizes)])
    # nrepeat=3 because there are overheads
    tree, _, _ = OMEinsum.tree_greedy(incidence_list, log2_edge_sizes; method=OMEinsum.MinSpaceOut(), nrepeat=3)

    # build configuration from the tree
    res = ones(Int, size(adj, 1))
    res[get_vertices!(Int[],tree.right)] .= 2
    return res[vertices]
end
initialize_random(n::Int) = [rand() < 0.5 ? 1 : 2 for _ = 1:n]

function get_vertices!(out, tree)
    if tree isa Integer
        push!(out, tree)
    else
        get_vertices!(out, tree.left)
        get_vertices!(out, tree.right)
    end
    return out
end

# legacy interface
"""
    optimize_sa(code, size_dict; sc_target, max_group_size=40, βs=0.1:0.2:15.0, niters=1000, ntrials=50,
            greedy_method=OMEinsum.MinSpaceOut(), greedy_nrepeat=10, initializer=:random)

Optimize the einsum `code` contraction order using the Simulated Annealing bipartition + Greedy approach.
`size_dict` is a dictionary that specifies leg dimensions.
Check the docstring of `SABipartite` for detailed explaination of other input arguments.

### References
* [Hyper-optimized tensor network contraction](https://arxiv.org/abs/2002.01935)
"""
function optimize_sa(code::EinCode, size_dict; sc_target, max_group_size=40,
             βs=0.01:0.02:15.0, niters=1000, ntrials=50, greedy_method=OMEinsum.MinSpaceOut(), greedy_nrepeat=10,
             initializer=:random)
    bipartiter = SABipartite(; sc_target=sc_target, βs=βs, niters=niters, ntrials=ntrials,
        greedy_config=GreedyMethod(method=greedy_method, nrepeat=greedy_nrepeat),
        max_group_size=max_group_size, initializer=initializer)
    recursive_bipartite_optimize(bipartiter, code, size_dict)
end


text:
#!/usr/bin/env julia
# > julia -O3 geoDist2P.jl

#---------------------------------------------------------------------
# [北緯1]	[東経1]	[北緯2]	[東経2]	[その他]
#---------------------------------------------------------------------
# 十進法 ddd.d...
#---------------------------------------------------------------------
const Data1 = """
35.685187	139.752274	45.416799	141.677155	皇居～稚内駅
35.685187	139.752274	43.385277	145.816641	皇居～納沙布岬
35.685187	139.752274	43.068637	141.350784	皇居～札幌駅
35.685187	139.752274	40.830242	140.734370	皇居～青森駅
35.685187	139.752274	39.701607	141.136465	皇居～盛岡駅
35.685187	139.752274	38.260289	140.882235	皇居～仙台駅
35.685187	139.752274	36.701526	137.213316	皇居～富山駅
35.685187	139.752274	36.642994	138.188653	皇居～長野駅
35.685187	139.752274	36.178553	133.324242	皇居～隠岐空港
35.685187	139.752274	36.082115	140.082335	皇居～研究学園駅
35.685187	139.752274	35.990160	139.083030	皇居～西武秩父駅
35.685187	139.752274	35.784434	139.900782	皇居～松戸駅
35.685187	139.752274	35.493836	134.226236	皇居～鳥取駅
35.685187	139.752274	35.464026	133.064003	皇居～松江駅
35.685187	139.752274	35.170581	136.882010	皇居～名古屋駅
35.685187	139.752274	34.985548	135.758851	皇居～京都駅
35.685187	139.752274	34.971500	138.389196	皇居～静岡駅
35.685187	139.752274	34.393702	131.401591	皇居～萩駅
35.685187	139.752274	34.350416	134.045520	皇居～高松駅
35.685187	139.752274	34.202712	129.287500	皇居～対馬市役所
35.685187	139.752274	33.948006	133.294802	皇居～新居浜駅
35.685187	139.752274	33.232621	131.606233	皇居～大分駅
35.685187	139.752274	31.915742	131.431997	皇居～宮崎駅
35.685187	139.752274	31.583617	130.541825	皇居～鹿児島駅
35.685187	139.752274	27.095627	142.191018	皇居～小笠原支庁
35.685187	139.752274	26.216843	127.718403	皇居～首里城跡
35.685187	139.752274	25.846517	131.263576	皇居～南大東空港
35.685187	139.752274	24.449582	122.934340	皇居～日本最西端之地
35.685187	139.752274	24.340547	124.155636	皇居～竹富町役場
35.685187	139.752274	24.192485	123.556720	皇居～中御神島
35.685187	139.752274	24.059749	123.805554	皇居～波照間空港
0	0	0	0		Debug
0	0	0.5	179.5		Debug
0	0	0.5	179.7		Debug
//	35.685187	139.752274	20.423690	136.075829	皇居～沖ノ鳥島
"""
#---------------------------------------------------------------------
# 度分秒 dddmmss.s...
#---------------------------------------------------------------------
const Data2 = """
354106.6732	1394508.1864	240335.0964	1234819.9944	皇居～波照間空港
//	354106.6732	1394508.1864	202525.284	1360432.9844	皇居～沖ノ鳥島
"""
#---------------------------------------------------------------------

#-------------------
# 度分秒 => 十進法
#-------------------
function
rtnGeoIBLto10A(
	deg::Int64,  # 度
	min::Int64,  # 分
	sec::Float64 # 秒
)
	return (deg + (min / 60.0) + (sec / 3600.0))::Float64
end

function
rtnGeoIBLto10B(
	ddmmss::Float64 # ddmmss.s...
)
	sign = 1

	if ddmmss < 0
		sign = -1
		ddmmss = -ddmmss
	end

	sec = ddmmss % 100
	min = floor(ddmmss / 100) % 100
	deg = floor(ddmmss / 10000)

	return sign * (deg + (min / 60.0) + (sec / 3600.0))::Float64
end

#-------------------
# 十進法 => 度分秒
#-------------------
function
rtnGeo10toIBL(
	angle::Float64 # 十進法
)
	sign = 1

	if angle < 0
		sign = -1
		angle = -angle
	end

	deg = Int64(floor(angle))
		angle = (angle - deg) * 60.0
	min = Int64(floor(angle))
		angle -= min
	sec = angle * 60.0

	# 0.999... * 60 => 60.0 対策
	if sec == 60.0
		min += 1
		sec = 0
	end

	return ((sign * deg)::Int64, min::Int64, sec::Float64)
end

#-------------------------------
# Vincenty法による２点間の距離
#-------------------------------
function
rtnGeoVincentry(
	lat1::Float64, # 開始～緯度
	lng1::Float64, # 開始～経度
	lat2::Float64, # 終了～緯度
	lng2::Float64  # 終了～経度
)
	if lat1 == lat2 && lng1 == lng2
		return (0.0, 0.0)
	end

	## _A = 6378137.0
	_B   = 6356752.314140356    # GRS80
	_F   = 0.003352810681182319 # 1 / 298.257222101
	_RAD = 0.017453292519943295 # π / 180

	latR1 = lat1 * _RAD
	lngR1 = lng1 * _RAD
	latR2 = lat2 * _RAD
	lngR2 = lng2 * _RAD

	f1 = 1 - _F

	omega  = lngR2 - lngR1
	tanU1  = f1 * tan(latR1)
	cosU1  = 1 / sqrt(1 + tanU1 * tanU1)
	sinU1  = tanU1 * cosU1
	tanU2  = f1 * tan(latR2)
	cosU2  = 1 / sqrt(1 + tanU2 * tanU2)
	sinU2  = tanU2 * cosU2

	lamda     = omega
	dLamda    = 0.0
	sinLamda  = 0.0
	cosLamda  = 0.0
	sin2sigma = 0.0
	sinSigma  = 0.0
	cosSigma  = 0.0
	sigma     = 0.0
	sinAlpha  = 0.0
	cos2alpha = 0.0
	cos2sm    = 0.0
	c = 0.0

	iLoop = 0

	while true
		sinLamda = sin(lamda)
		cosLamda = cos(lamda)
		sin2sigma = (cosU2 * sinLamda) * (cosU2 * sinLamda) + (cosU1 * sinU2 - sinU1 * cosU2 * cosLamda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLamda)
		if sin2sigma < 0.0
			return (0.0, 0.0)
		end
		sinSigma = sqrt(sin2sigma)
		cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLamda
		sigma = atan(sinSigma, cosSigma)
		sinAlpha = cosU1 * cosU2 * sinLamda / sinSigma
		cos2alpha = 1 - sinAlpha * sinAlpha
		cos2sm = cosSigma - 2 * sinU1 * sinU2 / cos2alpha
		c = _F / 16 * cos2alpha * (4 + _F * (4 - 3 * cos2alpha))
		dLamda = lamda
		lamda = omega + (1 - c) * _F * sinAlpha * (sigma + c * sinSigma * (cos2sm + c * cosSigma * (-1 + 2 * cos2sm * cos2sm)))

		if abs(lamda - dLamda) <= 1e-12
			break
		end

		iLoop += 1

		# 日本国内であれば５回程度で収束
		if iLoop > 10
			return (-1, -1) # Err
		end
	end

	d2 = cos2alpha * (1 - f1 * f1) / (f1 * f1)
	a = 1 + d2 / 16384 * (4096 + d2 * (-768 + d2 * (320 - 175 * d2)))
	b = d2 / 1024 * (256 + d2 * (-128 + d2 * (74 - 47 * d2)))
	dSigma = b * sinSigma * (cos2sm + b / 4 * (cosSigma * (-1 + 2 * cos2sm * cos2sm) - b / 6 * cos2sm * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2sm * cos2sm)))
	angle = atan(cosU2 * sinLamda, cosU1 * sinU2 - sinU1 * cosU2 * cosLamda) * 57.29577951308232
	dist = _B * a * (sigma - dSigma)

	# 変換
	if angle < 0
		angle += 360.0 # 360度表記
	end
	dist /= 1000.0 # m => km

	return (dist::Float64, angle::Float64)
end

#-------
# Main
#-------
using Printf

const Separater = "\t"

#---------------
# 計算／十進法
#---------------
function
main_Data1()
	for _s1 in split(Data1, "\n")
		_s1 = strip(_s1)

		if length(_s1) > 0 && SubString(_s1, 1, 2) != "//"
			println(_s1)

			as1 = []

			ad1 = split(_s1, Separater)[1:4]
			for _d1 in ad1
				deg, min, sec = rtnGeo10toIBL(parse(Float64, _d1))
				push!(as1, @sprintf("%d度%d分%.6f秒", deg, min, sec))
			end
			println(join(as1, Separater))

			dist, angle = rtnGeoVincentry(parse(Float64, ad1[1]), parse(Float64, ad1[2]), parse(Float64, ad1[3]), parse(Float64, ad1[4]))
			if dist < 0
				@printf("計測不能\n\n")
			else
				@printf("%.6fkm%s%.6f度\n\n", dist, Separater, angle)
			end
		end
	end
end

#---------------
# 計算／度分秒
#---------------
function
main_Data2()
	for _s1 in split(Data2, "\n")
		_s1 = strip(_s1)

		if length(_s1) > 0 && SubString(_s1, 1, 2) != "//"
			println(_s1)

			aLatLng = []
			as1 = []

			for _d1 in split(_s1, Separater)[1:4]
				angle = rtnGeoIBLto10B(parse(Float64, _d1))
				push!(aLatLng, angle)
				push!(as1, @sprintf("%.6f度", angle))
			end
			println(join(as1, Separater))

			dist, angle = rtnGeoVincentry(aLatLng[1], aLatLng[2], aLatLng[3], aLatLng[4])
			if dist < 0
				@printf("計測不能\n\n")
			else
				@printf("%.6fkm%s%.6f度\n\n", dist, Separater, angle)
			end
		end
	end
end

#-------
# Exec
#-------
main_Data1()
main_Data2()

max_stars_repo_name: iwm-iwama/leaflet-

max_issues_repo_name: iwm-iwama/leaflet-

max_forks_repo_name: iwm-iwama/leaflet-


text:
using DiffEqEnvironments
using LinearAlgebra
using ReinforcementLearning
using Test

# LTI system matrices: n_state=2, n_observations=2, n_actions=1
A = [1 0; 0 1] * 0.1
B = [0.1; 0.1]
C = [1 0; 0 1]
D = [0; 0]

# Weights for quadratic cost function
Q = [1 0; 0 1] * 10
R = [1]

# Call constructor for LTI systems
s0 = [0f0, 0f0]
tspan = (0f0, 5f0)
dt = 0.1
env = LTIQuadraticEnv(A, B, C, D, Q, R, s0, tspan, dt; a_lb=-1, a_ub=1)

@test get_state(env) == C * s0 # test initial observation

# Run random policy using ReinforcementLearning.jl
hook = TotalRewardPerEpisode()
run(
    Agent(
        ;policy=RandomPolicy(env),
        trajectory=VectCompactSARTSATrajectory(
            state_type=Any,
            action_type=Any,
            reward_type=Real,
            terminal_type=Bool,
        ),
    ),
    env,
    StopAfterEpisode(10),
    hook
)


text:
const TimeData = NamedTuple{(:time, :bytes, :gctime),
    Tuple{Float64, Int64, Float64}}

# string representation of timedata
function Base.show(io::IO, x::TimeData)
    print(io, "(")
    ks = keys(x)
    for i ∈ 1:length(ks)
        k = ks[i]
        print(io, "$k = $(getfield(x, k))")
        i < length(ks) ? print(io, ", ") : print(io, ")")
    end
end

"""
Construct a [`TimeData`](@ref) from a `NamedTuple` returned by `@timed`
"""
discard_value(stats::NamedTuple) = (
    time=stats.time,
    bytes=stats.bytes,
    gctime=stats.gctime,
)

const EmptyTimeData = (time=0.0, bytes=0, gctime=0.0)

# =============================================================================

mutable struct Report
    reason::Union{ReportReasons.ReportReason, Nothing}
    egraph::EGraph
    iterations::Int
    search_stats::TimeData
    apply_stats::TimeData
    rebuild_stats::TimeData
    total_time::TimeData
end

Report() = Report(nothing, EGraph(), 0,
    EmptyTimeData, EmptyTimeData, EmptyTimeData, EmptyTimeData)

Report(g::EGraph) = Report(nothing, g, 0,
    EmptyTimeData, EmptyTimeData, EmptyTimeData, EmptyTimeData)

function total_time!(r::Report)
    r.total_time = r.search_stats + r.apply_stats + r.rebuild_stats
end

# string representation of timedata
function Base.show(io::IO, x::Report)
    g = x.egraph
    println(io, "Equality Saturation Report")
    println(io, "=================")
    println(io, "\tStop Reason: $(x.reason)")
    println(io, "\tIterations: $(x.iterations)")
    println(io, "\tEGraph Size: $(g.numclasses) eclasses, $(length(g.memo)) nodes")
    println(io, "\tTotal Time: $(x.total_time)")
    println(io, "\tSearch Time: $(x.search_stats)")
    println(io, "\tApply Time: $(x.apply_stats)")
    println(io, "\tRebuild Time: $(x.rebuild_stats)")
end

import Base.(+)
function (+)(a::TimeData, b::TimeData)
    (time=a.time+b.time, bytes=a.bytes+b.bytes, gctime=a.gctime+b.gctime)
end

function (+)(a::Report, b::Report)
    Report(b.reason, b.egraph, a.iterations+b.iterations,
        a.search_stats + b.search_stats,
        a.apply_stats + b.apply_stats,
        a.rebuild_stats + b.rebuild_stats,
        a.total_time + b.total_time
    )
end


text:
# This file is a part of BAT.jl, licensed under the MIT License (MIT).

doc"""
    abstract type AbstractMCMCState end

The following methods must be defined for subtypes (e.g.
for `SomeMCMCState<:AbstractMCMCState` and ):

```julia
nparams(state::SomeMCMCState)

nsteps(state::SomeMCMCState)

nsamples(state::SomeMCMCState)

next_cycle!(state::SomeMCMCState)

density_sample_type(state::SomeMCMCState)
```
"""
abstract type AbstractMCMCState end

function density_sample_type end

doc"""
    abstract type MCMCAlgorithm{S<:AbstractMCMCState} <: BATAlgorithm end

The following methods must be defined for subtypes (e.g.
for `SomeAlgorithm<:MCMCAlgorithm`):

```julia

sample_weight_type(::Type{SomeAlgorithm}) where {Q,W,WS} = W

MCMCIterator(
    algorithm::SomeAlgorithm,
    likelihood::AbstractDensity,
    prior::AbstractDensity,
    id::Int64,
    rng::AbstractRNG,
    initial_params::AbstractVector{P} = default_value,
    exec_context::ExecContext = ExecContext(),
)

exec_capabilities(mcmc_step!, callback::AbstractMCMCCallback, chain::MCMCIterator{<:SomeAlgorithm})

mcmc_step!(
    callback::AbstractMCMCCallback,
    chain::MCMCIterator{<:SomeAlgorithm},
    exec_context::ExecContext,
    ll::LogLevel
)

nsamples_available(chain::MCMCIterator{<:SomeAlgorithm}, nonzero_weights::Bool = false)::Integer

get_samples!(appendable, chain::MCMCIterator{<:SomeAlgorithm}, nonzero_weights::Bool)::typeof(appendable)

get_sample_ids!(appendable, chain::MCMCIterator{<:SomeAlgorithm}, nonzero_weights::Bool)::typeof(appendable)
```

In some cases, these custom methods may necessary (default methods are defined
for `MCMCAlgorithm`):

```julia
rand_initial_params!(rng::AbstractRNG, algorithm::SomeAlgorithm, prior::AbstractDensity, x::StridedVecOrMat{<:Real})
```
"""
abstract type MCMCAlgorithm{S<:AbstractMCMCState} <: BATAlgorithm end
export MCMCAlgorithm

function sample_weight_type end

rand_initial_params!(rng::AbstractRNG, algorithm::MCMCAlgorithm, prior::AbstractDensity, x::StridedVecOrMat{<:Real}) =
    rand!(rng, prior, x)

# ToDo:
# function rand_initial_params!(rng::AbstractRNG, algorithm::MCMCAlgorithm, prior::TransformedDensity, x::StridedVecOrMat{<:Real}) =
#     rand_initial_params!(rng, algorithm, parent(prior), x)
#     ... apply transformation to x ...
#     x
# end

mutable struct MCMCIterator{
    A<:MCMCAlgorithm,
    T<:AbstractDensity,
    S<:AbstractMCMCState,
    R<:AbstractRNG
}
    algorithm::A
    target::T
    state::S
    rng::R
    id::Int64
    cycle::Int
    tuned::Bool
    converged::Bool
end

export MCMCIterator

nparams(chain::MCMCIterator) = nparams(chain.target)

DensitySampleVector(chain::MCMCIterator) = DensitySampleVector(density_sample_type(chain.state), nparams(chain.state))

reset_rng_counters!(chain::MCMCIterator) =
    reset_rng_counters!(chain.rng, chain.id, chain.cycle, nsteps(chain.state))

function next_cycle!(chain::MCMCIterator)
    next_cycle!(chain.state)
    chain.cycle += 1
    reset_rng_counters!(chain)
    chain
end

function mcmc_step! end
export mcmc_step!

function mcmc_iterate! end
export mcmc_iterate!

exec_capabilities(mcmc_iterate!, callback, chain::MCMCIterator) =
    exec_capabilities(mcmc_step!, Base.convert(AbstractMCMCCallback, callback), chain)

function mcmc_iterate!(
    callback,
    chain::MCMCIterator,
    exec_context::ExecContext = ExecContext();
    max_nsamples::Int64 = Int64(1),
    max_nsteps::Int64 = Int64(1000),
    max_time::Float64 = Inf,
    ll::LogLevel = LOG_NONE
)
    @log_msg ll "Starting iteration over MCMC chain $(chain.id)"

    cbfunc = Base.convert(AbstractMCMCCallback, callback)

    start_time = time()
    start_nsteps = nsteps(chain.state)
    start_nsamples = nsamples(chain.state)

    while (
        (nsamples(chain.state) - start_nsamples) < max_nsamples &&
        (nsteps(chain.state) - start_nsteps) < max_nsteps &&
        (time() - start_time) < max_time
    )
        mcmc_step!(cbfunc, chain, exec_context, ll + 1)
    end
    chain
end

function mcmc_iterate!(
    callbacks,
    chains::AbstractVector{<:MCMCIterator},
    exec_context::ExecContext = ExecContext();
    ll::LogLevel = LOG_NONE,
    kwargs...
)
    if isempty(chains)
        @log_msg ll "No MCMC chain(s) to iterate over."
        return chains
    else
        @log_msg ll "Starting iteration over $(length(chains)) MCMC chain(s)."
    end

    cbv = mcmc_callback_vector(callbacks, chains)

    target_caps = exec_capabilities.(mcmc_iterate!, cbv, chains)
    (self_context, target_contexts) = negotiate_exec_context(exec_context, target_caps)

    threadsel = self_context.use_threads ? threads_all() : threads_this()
    idxs = eachindex(cbv, chains, target_contexts)
    onthreads(threadsel) do
        for i in workpartition(idxs, length(threadsel), threadid())
            mcmc_iterate!(cbv[i], chains[i], target_contexts[i]; ll=ll+1, kwargs...)
        end
    end
    chains
end

# TODO/Decision: Make MCMCSpec a subtype of Sampleable{Multivariate,Continuous}?

struct MCMCSpec{
    A<:MCMCAlgorithm,
    L<:AbstractDensity,
    P<:AbstractDensity,
    R<:AbstractRNGSeed
}
    algorithm::A
    likelihood::L
    prior::P
    rngseed::R
end

export MCMCSpec

MCMCSpec(
    algorithm::MCMCAlgorithm,
    likelihood::AbstractDensity,
    prior::Union{AbstractDensity,ParamVolumeBounds},
    rngseed::AbstractRNGSeed = AbstractRNGSeed()
) = MCMCSpec(algorithm, likelihood, convert(AbstractDensity, prior), AbstractRNGSeed())

MCMCSpec(
    algorithm::MCMCAlgorithm,
    distribution::Distribution{Multivariate,Continuous},
    prior::Union{AbstractDensity,ParamVolumeBounds},
    rngseed::AbstractRNGSeed = AbstractRNGSeed()
) = MCMCSpec(algorithm, MvDistDensity(distribution), convert(AbstractDensity, prior), rngseed)

function (spec::MCMCSpec)(
    id::Int64,
    exec_context::ExecContext = ExecContext()
)
    P = float(eltype(param_bounds(spec.prior)))
    rng = spec.rngseed()

    MCMCIterator(
        spec.algorithm,
        spec.likelihood,
        spec.prior,
        id,
        rng,
        Vector{P}(),
        exec_context,
    )
end

function (spec::MCMCSpec)(
    ids::AbstractVector,
    exec_context::ExecContext = ExecContext()
)
    spec.(ids, exec_context)
end

doc"""
    AbstractMCMCCallback <: Function

Subtypes (e.g. `X`) must support

    (::X)(level::Integer, chain::MCMCIterator) => nothing
    (::X)(level::Integer, tuner::AbstractMCMCTuner) => nothing

to be compabtible with `mcmc_iterate!`, `mcmc_tune_burnin!`, etc.
"""
abstract type AbstractMCMCCallback <: Function end
export AbstractMCMCCallback

@inline Base.convert(::Type{AbstractMCMCCallback}, x::AbstractMCMCCallback) = x

@inline Base.convert(::Type{Vector{AbstractMCMCCallback}}, V::Vector{<:AbstractMCMCCallback}) = V

Base.convert(::Type{Vector{AbstractMCMCCallback}}, V::Vector) =
    [convert(AbstractMCMCCallback, x) for x in V]

function mcmc_callback_vector(x, chains::AbstractVector{<:MCMCIterator})
    V = convert(Vector{AbstractMCMCCallback}, x)
    if eachindex(V) != eachindex(chains)
        throw(DimensionMismatch("Indices of callback vector incompatible with number of MCMC chains"))
    end
    V
end

mcmc_callback_vector(x::Tuple{}, chains::AbstractVector{<:MCMCIterator}) =
    [MCMCNopCallback() for _ in chains]

doc"""
    MCMCCallbackWrapper{F} <: AbstractMCMCCallback

Wraps a callable object to turn it into an `AbstractMCMCCallback`.

Constructor:

    MCMCCallbackWrapper(f::Any)

`f` needs to support the call syntax of an `AbstractMCMCCallback`.
"""
struct MCMCCallbackWrapper{F} <: AbstractMCMCCallback
    f::F
end

@inline (wrapper::MCMCCallbackWrapper)(args...) = wrapper.f(args...)

Base.convert(::Type{AbstractMCMCCallback}, f::Function) = MCMCCallbackWrapper(f)

struct MCMCNopCallback <: AbstractMCMCCallback end

(cb::MCMCNopCallback)(level::Integer, obj::Any) = nothing

Base.convert(::Type{AbstractMCMCCallback}, ::Tuple{}) = MCMCNopCallback()

struct MCMCMultiCallback{N,TP<:NTuple{N,AbstractMCMCCallback}} <: AbstractMCMCCallback
    funcs::TP

    MCMCMultiCallback(fs::Vararg{AbstractMCMCCallback,N}) where {N} =
        new{N, typeof(fs)}(fs)

    function fMCMCMultiCallback(fs::Vararg{Any,N}) where {N}
        fs_conv = map(x -> convert(AbstractMCMCCallback, x), fs)
        new{N, typeof(fs_conv)}(fs_conv)
    end
end

function (cb::MCMCMultiCallback)(level::Integer, obj::Any)
    for f in cb.funcs
        f(level, obj)
    end
    nothing
end

Base.convert(::Type{AbstractMCMCCallback}, fs::Tuple) = MCMCMultiCallback(fs...)

struct MCMCAppendCallback{T,F<:Function} <: AbstractMCMCCallback
    appendable::T
    max_level::Int
    get_data_func!::F
    nonzero_weights::Bool
end

export MCMCAppendCallback

function (cb::MCMCAppendCallback)(level::Integer, chain::MCMCIterator)
    if (level <= cb.max_level)
        cb.get_data_func!(cb.appendable, chain, cb.nonzero_weights)
    end
    nothing
end

Base.convert(::Type{AbstractMCMCCallback}, x::DensitySampleVector) = MCMCAppendCallback(x)

MCMCAppendCallback(x::DensitySampleVector, nonzero_weights::Bool = true) =
    MCMCAppendCallback(x, 1, get_samples!, nonzero_weights)


text:
""" Observables that can be measured on a gutzwiller state """

function Szi(chain::GutzwillerChain,i)
    state = get_State(chain)
    model = get_Model(chain)
    Sz = state.spin_config.sc[i]
    data = Dict("Szi" => Float64(Szi))
    return data
end

using Statistics: mean
function NeighborSzSz(chain::GutzwillerChain)
    state = get_State(chain)
    model = get_Model(chain)
    lg = model["lattice"].graph # latticegraph
    spinc = state.spin_config.sc # spin config array

    SzSz = 0

    for i in vertices(lg)
        SzSz += mean([spinc[i]*spinc[j]/4 for j in neighbors(lg,i)])
    end

    data = Dict("SzSz" => Float64(SzSz)/nv(lg))
    return data
end

function MeasureSwap(chain::DoubleGutzwillerChain)
    model = get_Model(chain)
    s = get_State(chain)
    ns =model["dims"][1]

    len = Int64(ns/2)

    type = typeof(s[1].det_A_up)
    sw = zeros(type,len)

    for i = 1:len
        sw[i] = Swap(chain,i-1) # regionsize 0 means just 1 site
    end

    data = Dict("swap" => sw)
    return data
end


text:
eye(T, s) = [i == j ? one(T) : zero(T) for i in 1:s, j in 1:s]

function rational_form(T::MatrixElem, σ, σ_inv, δ)
    n = size(T, 1)
    i0 = 1
    i = 1
    # B = eye(S, n)
    B = identity_matrix(T)
    while i < n
        j = i + 1
        while j <= n && T[i,j] == 0
            j += 1
        end
        if j <= n
            @debug "Before lemma 2" B T
            transform_lemma2(T, i0, i, j, σ, σ_inv, δ, B)
            @debug "After lemma 2" B T
            i += 1
        else
            @debug "Before lemma 3" B T
            transform_lemma3(T, i0, i, σ, δ, B)
            @debug "After lemma 3" B T
            i1 = i + 1
            while i1 <= n && T[i1,i0] == 0
                i1 += 1
            end
            if i1 <= n
                @debug "Before lemma 5" B T
                transform_lemma5(T, i0, i, i1, σ, σ_inv, δ, B)
                @debug "After lemma 5" B T
                i += 1
            else
                i += 1
                i0 = i
            end
        end
    end
    return T, B
end

function transformP(T::MatrixElem, i0::Int, i::Int, k::Int, B::MatrixElem)
    n = size(T, 1)
    for j in i0:n
        T[j,i], T[j,k] = T[j,k], T[j,i]
        # B[j,i], B[j,k] = B[j,k], B[j,i]
    end
    for j in i0:n
        T[i,j], T[k,j] = T[k,j], T[i,j]
    end
    for j in 1:n
        # T[j,i], T[j,k] = T[j,k], T[j,i]
        B[j,i], B[j,k] = B[j,k], B[j,i]
    end
    return T, B
end

function transformR(T::MatrixElem, i0::Int, B::MatrixElem)
    n = size(T, 1)
    c = zeros(base_ring(T), n)
    for i in i0:n
        c[i] = T[i,n]
    end
    for i in i0:n
        for j in n:-1:i0+1
            T[i,j] = T[i,j-1]
        end
    end
    for i in i0:n
        T[i,i0] = c[i]
    end

    for i in i0:n
        c[i] = T[n,i]
    end
    for j in i0:n
        for i in n:-1:i0+1
            T[i,j] = T[i-1,j]
        end
    end
    for i in i0:n
        T[i0,i] = c[i]
    end

    for i in 1:n
    # for i in i0:n
        c[i] = B[i,n]
    end
    for j in n:-1:i0+1
        for i in 1:n
        # for i in i0:n
            B[i,j] = B[i,j-1]
        end
    end
    for i in 1:n
    # for i in i0:n
        B[i,i0] = c[i]
    end
    return T, B
end

function transform_lemma2(T::MatrixElem, i0::Int, i::Int, l::Int, σ, σ_inv, δ, B::MatrixElem)
    n = size(T, 1)
    T, B = transformP(T, i0, i+1, l, B)
    a = σ_inv(inv(T[i,i+1]))

    for j in i:n
        T[j,i+1] *= σ(a) # D1
    end
    for j in i0:n
        T[i+1,j] //= a # D2
    end
    T[i+1,i+1] += δ(a) * inv(a) # D3
    for j in 1:n
        B[j,i+1] *= a # basis change
    end

    for k in i0:i
        a = σ_inv(-T[i,k])
        for j in i:n
            T[j,k] += σ(a) * T[j,i+1] # C1
        end
        if k < i
            T[i+1,k+1] -= a # C2
        else
            for j in i:n
                T[i+1,j] -= a * T[i,j]
            end
        end
        T[i+1,k] += δ(a) # C3
        for j in 1:n
            B[j,k] += a * B[j,i+1] # basis change
        end
    end

    for k = i+2:n
        a = σ_inv(-T[i,k])
        for j in i:n
            T[j,k] += σ(a) * T[j,i+1] # C1
        end
        for j in i0:n
            T[i+1,j] -= a * T[k,j] # C2
        end
        T[i+1,k] += δ(a) # C3
        for j in 1:n
        # for j in i0:n
            B[j,k] += a * B[j,i+1] # basis change
        end
    end
    return T, B
end

function transform_lemma3(T::MatrixElem, i0::Int, i::Int, σ, δ, B::MatrixElem)
    n = size(T, 1)
    for l in i:-1:i0+1
        for k in i+1:n
            a = T[k,l]
            for j in i+1:n
                T[j,l-1] += σ(a) * T[j,k] # C1
            end
            T[k,l] = 0 # C2
            T[k,l-1] += δ(a) # C3
            for j in 1:n
            # for j in i0:n
                B[j,l-1] += a * B[j,k]
            end
        end
    end
    return T, B
end

function transform_lemma4(T::MatrixElem, i0::Int, i::Int, k::Int, σ, σ_inv, δ, B::MatrixElem)
    n = size(T, 1)
    for l in i0:k
        a = σ_inv(-T[k,l])
        T[k,l] = 0 # C1
        T[k+1,l] += σ(a) * T[k+1,k+1]
        if k < i
            T[i+1,l] += σ(a) * T[i+1,k+1]
        end
        if l < k
            T[k+1,l+1] -= a # C2
        else
            for j in i0:k+1
                T[k+1,j] -= a * T[k,j]
            end
            for j in i+2:n
                T[k+1,j] -= a * T[k,j]
            end
        end
        T[k+1,l] += δ(a) # C3
        for j in 1:n
        # for j in i0:n
            B[j,l] += a * B[j,k+1] # basis change
        end
    end

    for l in i+2:n
        a = σ_inv(-T[k,l])
        T[k,l] = 0 # C1
        T[k+1,l] += σ(a) * T[k+1,k+1]
        if k < i
            T[i+1,l] += σ(a) * T[i+1,k+1]
        end
        T[k+1,i0] -= a * T[l,i0] # C2
        for j in i+2:n
            T[k+1,j] -= a * T[l,j]
        end
        T[k+1,l] += δ(a) # C3
        for j in 1:n
        # for j in i0:n
            B[j,l] += a * B[j,k+1] # basis change
        end
    end
    return T, B
end

function transform_lemma5(T::MatrixElem, i0::Int, i::Int, k::Int, σ, σ_inv, δ, B::MatrixElem)
    n = size(T, 1)
    T, B = transformP(T, i0, k, n, B)

    a = T[n,i0]
    for j in i+1:n
        T[j,n] *= σ(a) # D1
    end
    T[n,i0] = 1 # D2
    for j in i+1:n
        T[n,j] //= a
    end
    T[n,n] += δ(a) // a # D3
    for j in 1:n
    # for j in i0:n
        B[j,n] *= a # basis change
    end

    for l in i+1:n-1
        if T[l,i0] != 0
            a = T[l,i0]
            for j in i+1:n
                T[j,n] += σ(a) * T[j,l] # C1
            end
            T[l,i0] -= a # C2
            for j in i+1:n
                T[l,j] -= a * T[n,j]
            end
            T[l,n] += δ(a) # C3
            for j in 1:n
            # for j in i0:n
                B[j,n] += a * B[j,l] # basis change
            end
        end
    end

    T, B = transformR(T, i0, B)
    for j in i0:i
        T, B = transform_lemma4(T, i0, i, j, σ, σ_inv, δ, B)
    end

    return T, B
end


text:
Δt = .1
t₀ = 0.
t₁ = 1.
q₀ = [1.]
p₀ = [1.]
x₀ = [1., 1.]
λ₀ = [0.]

qₛ = rand(1,3,3)
pₛ = rand(1,3,3)
xₛ = rand(2,3,3)

noise_intensity = 0.1

x₁ₛ = [[ 0.5, 0.0],
        [ 0.0, 0.5],
        [-0.5, 0.0]]

q₁ₛ = [[0.5], [0.0], [-0.5]]
p₁ₛ = [[0.0], [0.5], [ 0.0]]


text:
"""
Package: DataWrangler

impute\\[!\\]([x], y; method = "loess", q)

Impute missing values in vector `y` either in-place or returning a copy of `y` with the imputed values.

# Parameters
- `x`: Optional vector containing the support for `y` (no missing value allowed)
- `y`: Vector of type Real with missing values to be imputed
- `method`: This parameter can take three valid values and defaults to "loess":
    - "loess": Runs loess with a window size of `q` on the dataset and interpolate/extrapolate results on the `missing` values.
    - "normal": Random imputation using a Normal empirical distribution based on the size of `q`.
    - "uniform": Random imputation using an Uniform empirical distribution based on the size of `q`.
- `q`: Number of closest vector values to the imputation to be considered, it defaults to 3*length(y)÷4).

# Description

The imputation replaces missing values either with loess, local random uniform or local random normal methods. When the vector's type is of Integers then a rounding is performed on the results.

# Returns

Nothing if the imputation is done in-place or the original vector `y` with all the missing values imputed.

# Examples
```julia
x = sort(rand(100));
y = Array{Union{Missing,Float64}}(undef,100);
y[:] = rand(100);
y[rand(1:100,10)] .= missing;

impute!(x,y)
```
"""
function impute!(y::AbstractVector{<:Union{Missing,T}};
                 method::String = "loess", q = 3*length(y)÷4) where  {T<:Real}

    impute!(T.(1:length(y)), y; method, q)

end

function impute!(x::AbstractVector{R},
                 y::AbstractVector{<:Union{Missing,T}};
                 method::String = "loess", q = 3*length(y)÷4) where  {R<:Real,T<:Real}

    @assert !any(ismissing, x) "No missing values allowed in `x`"
    @assert issorted(x) "x values must be sorted"
    @assert length(x) == length(y) "x and y must have the same length"

    @assert method ∈ ["loess","uniform","normal"]
    # @assert !(T <: Integer) "Vector with Integers cannot be updated in place, use `impute(x)` instead"

    impxi = findall(ismissing,y)
    impx = x[impxi]

    length(impx) == 0 && return nothing

    loq = loess(x,y; q)
    loqy = loq(x)

    if method == "loess"
        loess_impx = loq(impx)
        y[impxi] = T <: Integer ? round.(T,loess_impx) : loess_impx
    end

    if method == "normal"
        n = length(x)
        rnd = randn(n)
        for (i,(ix,ixi)) in enumerate(zip(impx,impxi))
            xv = @. abs(x-ix)
            qidx = sortperm(xv)[1:min(q,n)]

            smy = skipmissing(y[qidx].-loqy[qidx])
            μ = mean(smy)
            σ = std(smy)
            y[ixi] = T <: Integer ? round(T, μ + rnd[i] * σ + loqy[ixi]) : μ + rnd[i] * σ + loqy[ixi]
        end
    end

    if method == "uniform"
        n = length(x)
        for (i,(ixi,ix)) in enumerate(zip(impxi,impx))
            xv = @. abs(x-ix)
            qidx = sortperm(xv)[1:min(q,n)]

            m,M = extrema(skipmissing(y[qidx].-loqy[qidx]))
            y[ixi] = T <: Integer ? round(T,m+rand(1)[1]*(M-m)+loqy[ixi]) : m+rand(1)[1]*(M-m)+loqy[ixi]
        end
    end

end

"""
Package: DataWrangler

Check 'impute! for further information.
"""
function impute(y::AbstractVector{<:Union{Missing,T}};
                 method::String = "loess", q = 3*length(y)÷4) where  {T<:Real}

    impute(T.(1:length(y)),y; method, q)

end

function impute(x::AbstractVector{R},
                y::AbstractVector{<:Union{Missing,T}};
                method::String = "loess", q = 3*length(y)÷4) where  {R<:Real,T<:Real}

    P = any(ismissing,y) ? Union{Missing, Base.promote_op(/,T,T)} : Base.promote_op(/,T,T)
    cy = Vector{P}(undef,length(y))
    cy[:] = y
    impute!(x,cy; method, q)
    return cy

end