LCOV - code coverage report
Current view: top level - src/rendering/fonts - make_similarities.cc (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 211 216 97.7 %
Date: 2022-06-15 20:16:21 Functions: 12 12 100.0 %
Branches: 275 443 62.1 %

           Branch data     Line data    Source code
       1                 :            : #include <map>
       2                 :            : #include <regex>
       3                 :            : #include <string>
       4                 :            : #include <ranges>
       5                 :            : #include <fstream>
       6                 :            : #include <ostream>
       7                 :            : #include <functional>
       8                 :            : #include <unordered_set>
       9                 :            : /** @file rendering/fonts/make_similarities.cc
      10                 :            :  * @brief Builds a similarity map between glyphs. Used internally by FontPlan.
      11                 :            :  */
      12                 :            : 
      13                 :            : #include "share.hh"
      14                 :            : #include "ctype.hh"
      15                 :            : #include "make_similarities.hh"
      16                 :            : 
      17                 :            : using namespace std::literals;
      18                 :            : 
      19                 :            : static constexpr auto Ropt = std::regex_constants::ECMAScript | std::regex_constants::optimize;
      20                 :            : 
      21                 :          2 : static auto UnicodeDataFileName()
      22                 :            : {
      23         [ +  - ]:          4 :     return FindShareFile("unicode/UnicodeData.txt", {"/usr/local/share", "/usr/share"});
      24                 :            :     /* Try UnicodeData.txt in the following:
      25                 :            : 
      26                 :            :         <path>/share/unicode/
      27                 :            :         <home>/.local/share/<prog>/unicode/
      28                 :            :         /usr/local/share/<prog>/unicode/
      29                 :            :         /usr/share/<prog>/unicode/
      30                 :            :         /usr/share/unicode
      31                 :            : 
      32                 :            :      * Where
      33                 :            :         <prog> = that_terminal
      34                 :            :         <path> = pathname(argv[0])
      35                 :            :      */
      36                 :            : }
      37                 :            : 
      38                 :      11136 : static std::string u8(char32_t code)
      39                 :            : {
      40                 :      11136 :     return ToUTF8(std::u32string_view(&code,1));
      41                 :            : }
      42                 :            : 
      43                 :       2524 : static std::vector<std::string_view> Split(std::string_view str, std::string_view delimiter)
      44                 :            : {
      45         [ +  - ]:       2524 :     std::vector<std::string_view> result;
      46         [ +  - ]:       2524 :     if(!str.empty())
      47                 :        361 :         for(std::size_t pos = 0; ; )
      48                 :            :         {
      49                 :       2885 :             std::size_t d = str.find(delimiter, pos);
      50   [ +  +  +  - ]:       2885 :             if(d == str.npos) { result.emplace_back(str.data()+pos, str.size()-pos); break; }
      51         [ +  - ]:        361 :             result.emplace_back(str.data()+pos, d-pos);
      52                 :        361 :             pos = d + delimiter.size();
      53                 :        361 :         }
      54                 :       2524 :     return result;
      55                 :          0 : }
      56                 :            : template<typename T>
      57                 :          1 : static std::string Join(const T& elems, std::string_view delim)
      58                 :            : {
      59                 :          1 :     std::string result;
      60         [ +  + ]:         26 :     for(auto i = elems.begin(); i != elems.end(); ++i)
      61                 :            :     {
      62   [ +  +  +  - ]:         49 :         if(i != elems.begin()) result += delim;
      63                 :         25 :         result += *i;
      64                 :            :     }
      65                 :          1 :     return result;
      66                 :          0 : }
      67                 :            : template<typename T, typename U>
      68                 :        800 : static std::string Join(const std::map<T,U>& elems, std::string_view delim)
      69                 :            : {
      70                 :        800 :     std::string result;
      71         [ +  + ]:       1717 :     for(auto i = elems.begin(); i != elems.end(); ++i)
      72                 :            :     {
      73   [ +  +  +  - ]:       1034 :         if(i != elems.begin()) result += delim;
      74         [ +  - ]:        917 :         result += i->second;
      75                 :            :     }
      76                 :        800 :     return result;
      77                 :          0 : }
      78                 :            : 
      79                 :            : /** Contents of UniData.txt in a parsed format */
      80                 :          1 : struct UniData
      81                 :            : {
      82                 :            :     std::map<std::string, char32_t> codes; ///< Translation of names into codepoints
      83                 :            :     std::map<char32_t, std::string> names; ///< Translation of codepoints into names
      84                 :            : 
      85                 :            :     /** Load from @param unipath file */
      86                 :          1 :     void Load(std::filesystem::path unipath)
      87                 :            :     {
      88                 :          1 :         std::regex pattern("([0-9A-F]+);([^;]+);.*", Ropt);
      89         [ +  - ]:          1 :         std::regex mathpat("(MATHEMATICAL.*) (CAPITAL|SMALL) ([A-Z])", Ropt);
      90                 :            : 
      91         [ +  - ]:          1 :         std::ifstream f(unipath);
      92   [ +  -  +  + ]:      33829 :         for(std::string line; std::getline(f, line), f; )
      93   [ +  -  +  - ]:      33828 :             if(std::smatch mat; std::regex_match(line, mat, pattern))
      94                 :            :             {
      95         [ +  - ]:      33828 :                 std::string name = mat[2];
      96   [ +  -  +  - ]:      33828 :                 char32_t    code = std::stoi(mat[1], nullptr, 16);
      97                 :            : 
      98                 :            :                 // Rename math symbols with "CAPITAL L" into with "LATIN CAPITAL LETTER L"
      99                 :            :                 // to get better matches.
     100   [ +  -  +  + ]:      33828 :                 if(std::regex_match(name, mat, mathpat))
     101                 :            :                 {
     102   [ +  -  +  -  :        652 :                     name = std::string(mat[1]) + " LATIN " + std::string(mat[2]) + " LETTER " + std::string(mat[3]);
          +  -  +  -  +  
             -  +  -  +  
                      - ]
     103                 :            :                 }
     104                 :            : 
     105         [ +  - ]:      33828 :                 codes[name] = code;
     106   [ +  -  +  - ]:      67656 :                 names[code] = name;
     107                 :      67656 :             }
     108                 :          1 :     }
     109                 :            :     /** Query whether the given name is defined.
     110                 :            :      * @param s name to search for
     111                 :            :      * @returns codepoint if known, 0 otherwise
     112                 :            :      */
     113                 :       7031 :     char32_t Has(const std::string& s) const
     114                 :            :     {
     115         [ +  + ]:       7031 :         if(auto i = codes.find(s); i != codes.end()) return i->second;
     116                 :       1953 :         return 0;
     117                 :            :     }
     118                 :            :     /** Query whether the given codepoint has a name.
     119                 :            :      * @param c codepoint to search for
     120                 :            :      * @returns True if the codepoint has a known name.
     121                 :            :      */
     122                 :            :     bool Has(char32_t c) const
     123                 :            :     {
     124                 :            :         return names.find(c) != names.end();
     125                 :            :     }
     126                 :            : };
     127                 :            : 
     128                 :          1 : void MakeSimilarities(std::ostream& out, std::filesystem::path unipath) // Same as old "similarities.dat"
     129                 :            : {
     130         [ +  - ]:          1 :     UniData d;
     131   [ +  -  +  - ]:          1 :     d.Load(unipath);
     132                 :            : 
     133                 :            :     // Create special matching recipe for ASCII from FULLWIDTH
     134                 :            :     // Do this BEFORE the MATHEMATICAL section
     135                 :            :     // to avoid your ASCII letters looking completely silly.
     136         [ +  - ]:          1 :     out << "// If this is fullwidth font, create ASCII characters from FULLWIDTH characters\n";
     137                 :            : 
     138         [ +  - ]:          1 :     std::regex fullhalf("(FULLWIDTH|HALFWIDTH) (.*)", Ropt);
     139                 :          1 :     char32_t tmp;
     140   [ +  -  +  + ]:      33765 :     for(auto&[name,code]: d.codes)
     141   [ +  -  +  +  :      33992 :         if(std::smatch mat; std::regex_match(name, mat, fullhalf) && (tmp = d.Has(mat[2])))
          +  -  +  +  +  
                +  +  + ]
     142   [ +  -  +  -  :      34430 :             out << "→ " << u8(tmp) << u8(code) << '\n';
             +  -  +  - ]
     143                 :            : 
     144         [ +  - ]:          1 :     out << R"(
     145                 :            : // Insert some manually crafted rules between pre-composed encircled or stylished letters
     146                 :            : // Do this before the MATH section may alias doublestruck R (𝕉) with regular R
     147                 :            : // when the font may in fact have doublestruck R (ℝ) in the letterlike section.
     148                 :            : = ℂ𝔺
     149                 :            : = ℊ𝒼
     150                 :            : = ℋ𝔋
     151                 :            : = ℌ𝔥
     152                 :            : = ℍ𝔿
     153                 :            : = ℎ𝑕
     154                 :            : = ℐ𝒤
     155                 :            : = ℑ𝔌
     156                 :            : = ℒ𝒧
     157                 :            : = ℓ𝓁
     158                 :            : = ℕ𝕅
     159                 :            : = ℙ𝕇
     160                 :            : = ℚ𝕈
     161                 :            : = ℛ𝒭
     162                 :            : = ℜ𝔕
     163                 :            : = ℝ𝕉
     164                 :            : = ℤ𝕑
     165                 :            : = K𝖪
     166                 :            : = ℬ𝒝
     167                 :            : = ℭ𝔆
     168                 :            : = ℮𝕖
     169                 :            : = ℯ𝑒
     170                 :            : = ℳ𝒨
     171                 :            : = ℴ𝓄
     172                 :            : = ℹ𝐢
     173                 :            : = ⅅ𝔻
     174                 :            : = ⅆ𝕕
     175                 :            : = ⅇ𝕖
     176                 :            : = ⅈ𝕚
     177                 :            : = ⅉ𝕛
     178                 :            : = ⅀𝚺
     179                 :            : = ℿ𝚷
     180                 :            : = ℾ𝚪
     181                 :            : = ℽ𝛄
     182                 :            : = ℼ𝛑
     183                 :            : = ℗Ⓟ
     184                 :            : = ©Ⓒ
     185                 :            : = ®Ⓡ
     186                 :            : 
     187                 :            : // Insert equality rules between symbols that are visually completely indiscernible
     188                 :            : // First, ASCII-like characters
     189                 :            : )";
     190                 :          1 :     std::vector<std::u32string_view> equal_symbols{
     191                 :            :         UR"(!ǃ)",
     192                 :            :         UR"(#ⵌꖛ⌗⋕)",
     193                 :            :         UR"(÷➗)",
     194                 :            :         UR"(+➕ᚐ)",
     195                 :            :         UR"(-−–➖)",
     196                 :            :         UR"(.ꓸ)",
     197                 :            :         UR"(,ꓹ)",
     198                 :            :         UR"(ꓽ::׃꞉⁚ː∶։܃𝄈)",
     199                 :            :         UR"(;;;ꓼ)",
     200                 :            :         UR"(=ꘌ⚌゠᐀꓿)",
     201                 :            :         UR"(/⟋╱⁄𝈺)",
     202                 :            :         UR"(\⟍╲𝈻⧹⧵)",
     203                 :            :         UR"(2շ)",
     204                 :            :         UR"(3З𝟹ვ౩𝈆)",
     205                 :            :         UR"(ɜзᴈ)",
     206                 :            :         UR"(ƷӠ)",
     207                 :            :         UR"(ʒӡ)",
     208                 :            :         UR"(4Ꮞ)",
     209                 :            :         UR"(8𐌚)",
     210                 :            :         UR"(➊❶⓵➀①)",
     211                 :            :         UR"(➋❷⓶➁②)",
     212                 :            :         UR"(➌❸⓷➂③)",
     213                 :            :         UR"(➍❹⓸➃④)",
     214                 :            :         UR"(➎❺⓹➄⑤)",
     215                 :            :         UR"(➏❻⓺➅⑥)",
     216                 :            :         UR"(➐❼⓻➆⑦)",
     217                 :            :         UR"(➑❽⓼➇⑧)",
     218                 :            :         UR"(➒❾⓽➈⑨)",
     219                 :            :         UR"(➓❿⓾➉⑩)",
     220                 :            :         UR"(AΑАᎪᗅᗋ𐌀ꓐꓮ)",
     221                 :            :         UR"(ĂӐᾸ)",
     222                 :            :         UR"(ĀᾹ)",
     223                 :            :         UR"(ÄӒ)",
     224                 :            :         UR"(ÅÅ)",
     225                 :            :         UR"(ÆӔ)",
     226                 :            :         UR"(ʙвⲃ)",
     227                 :            :         UR"(BΒВᏴⲂꕗ𐌁)",
     228                 :            :         UR"(ƂБ)",
     229                 :            :         UR"(ϭб)",
     230                 :            :         UR"(CϹСᏟⲤⅭꓚ)",
     231                 :            :         UR"(ϽƆↃꓛ)",
     232                 :            :         UR"(DᎠ𐌃ᗞⅮⅮꓓ)",
     233                 :            :         UR"(ꓷᗡ⫏)",
     234                 :            :         UR"(EΕЕᎬⴹꗋ⋿ꓰ)",
     235                 :            :         UR"(ꓱ∃Ǝⴺ)",
     236                 :            :         UR"(ÈЀ)",
     237                 :            :         UR"(ËЁ)",
     238                 :            :         UR"(ĔӖ)",
     239                 :            :         UR"(ƐԐ)",
     240                 :            :         UR"(FϜ𝈓ߓᖴ𐌅ꓝ)",
     241                 :            :         UR"(GᏀႺꓖ)",
     242                 :            :         UR"(HΗНᎻⲎᕼꖾꓧ)",
     243                 :            :         UR"(ʜнⲏ)",
     244                 :            :         UR"(IΙІӀᏆⲒⅠꓲ)",
     245                 :            :         UR"(ΪÏЇ)",
     246                 :            :         UR"(JЈᒍلﻝᎫꓙ)",
     247                 :            :         UR"(KΚКⲔᏦK𐌊ꓗ)",
     248                 :            :         UR"(ḰЌ)",
     249                 :            :         UR"(κᴋкⲕ)",
     250                 :            :         UR"(LᏞᒪ𝈪Ⅼ˪ԼⅬԼⅬԼլꓡ)",
     251                 :            :         UR"(MΜМᎷⲘϺ𐌑Ⅿꓟ)",
     252                 :            :         UR"(ᴍмⲙ)",
     253                 :            :         UR"(NΝⲚꓠ)",
     254                 :            :         UR"(ͶИ)",
     255                 :            :         UR"(ɴⲛ)",
     256                 :            :         UR"(ͷи)",
     257                 :            :         UR"(OΟОⲞ◯○⃝❍🌕߀ⵔՕ⚪⭕౦೦ꓳ)",
     258                 :            :         UR"(ÖӦ)",
     259                 :            :         UR"(ϴθѲӨƟᎾⲐ)",
     260                 :            :         UR"(ΦФⲪ)",
     261                 :            :         UR"(PΡРᏢⲢ𐌓ᑭ𐌛ꓑ)",
     262                 :            :         UR"(ΠПⲠ)",
     263                 :            :         UR"(QԚꝖႳႭⵕℚ)",
     264                 :            :         UR"(RᎡ𝈖ᖇᏒꓣ)",
     265                 :            :         UR"(Яᖆ)",
     266                 :            :         UR"(SЅᏚՏႽꚂऽ𐒖ꕶꓢ)",
     267                 :            :         UR"(ΣƩ∑⅀Ʃⵉ)",
     268                 :            :         UR"(TΤТᎢⲦ⊤⟙ꔋ𝍮🝨⏉ߠꓔ)",
     269                 :            :         UR"(U⋃ᑌ∪ՍՍꓴ)",
     270                 :            :         UR"(VᏙᐯⴸ⋁𝈍ⅤⅤꓦ)",
     271                 :            :         UR"(WԜᎳꓪ)",
     272                 :            :         UR"(XΧХⲬ╳ⵝ𐌢Ⅹ𐌗Ⅹꓫ)",
     273                 :            :         UR"(YΥҮⲨꓬ)",
     274                 :            :         UR"(ΫŸ)",
     275                 :            :         UR"(ZΖᏃⲌꓜ)",
     276                 :            :         UR"(aа)",
     277                 :            :         UR"(äӓ)",
     278                 :            :         UR"(ăӑ)",
     279                 :            :         UR"(æӕ)",
     280                 :            :         UR"(əә)",
     281                 :            :         UR"(ЬᏏ)",
     282                 :            :         UR"(cϲсⲥⅽ)",
     283                 :            :         UR"(ͻɔᴐↄ)",
     284                 :            :         UR"(dⅾ)",
     285                 :            :         UR"(eе)",
     286                 :            :         UR"(ĕӗ)",
     287                 :            :         UR"(ϵє)",
     288                 :            :         UR"(ɛԑ)",
     289                 :            :         UR"(gց)",
     290                 :            :         UR"(гᴦⲅ)",
     291                 :            :         UR"(iіⅰ)",
     292                 :            :         UR"(ïї)",
     293                 :            :         UR"(jϳј)",
     294                 :            :         UR"(lⅼ)",
     295                 :            :         UR"(ιɩ)",
     296                 :            :         UR"(mⅿ)",
     297                 :            :         UR"(ʌᴧ)",
     298                 :            :         UR"(oοоסᴏⲟօ૦௦ഠ๐໐໐)",
     299                 :            :         UR"(òὸ)",
     300                 :            :         UR"(óό)",
     301                 :            :         UR"(öӧ)",
     302                 :            :         UR"(ɵөⲑ)",
     303                 :            :         UR"(фⲫ)",
     304                 :            :         UR"(pρрⲣ)",
     305                 :            :         UR"(πпᴨⲡ)",
     306                 :            :         UR"(яᴙ)",
     307                 :            :         UR"(qԛ)",
     308                 :            :         UR"(sѕ)",
     309                 :            :         UR"(uս)",
     310                 :            :         UR"(vᴠݍⅴ)",
     311                 :            :         UR"(xⅹ)",
     312                 :            :         UR"(yу)",
     313                 :            :         UR"(~῀)",
     314                 :            :         UR"(··•∙⋅・)",
     315                 :            :         UR"(ᴛтⲧ)",
     316                 :            :         UR"(૰。࿁)",
     317                 :            :         UR"(ᐃ△🜂∆ΔᐃⵠΔꕔ)",
     318                 :            :         UR"(ᐊᐊ◁⊲)",
     319                 :            :         UR"(ᐁ▽🜄⛛∇ᐁ𝈔)",
     320                 :            :         UR"(ᐅ▷⊳▻)",
     321                 :            :         UR"(ᐱΛ𐤂⋀ⴷ𐌡Ʌ)",
     322                 :            :         UR"(ᑎႶ⋂Ո∩𝉅ꓵ)",
     323                 :            :         UR"(⨆∐ⵡ𝈈)",
     324                 :            :         UR"(∏⨅ПΠ⊓)",
     325                 :            :         UR"(⊏ⵎ𝈸)",
     326                 :            :         UR"(コ⊐ߏ𝈹)",
     327                 :            :         UR"(⎕□☐⬜◻▢⃞❑❒❐❏⧠⃢⌷ロ)",
     328                 :            :         UR"(⛝⌧🝱)",
     329                 :            :         UR"(           )"
     330         [ +  - ]:          1 :     };
     331         [ +  + ]:        140 :     for(auto w: equal_symbols)
     332   [ +  -  +  -  :        278 :         out << "= " << ToUTF8(w) << '\n';
                   +  - ]
     333                 :            : 
     334         [ +  - ]:          1 :     out << "\n// Create similarity rules between modified stylished symbols\n";
     335                 :          1 :     if(1)
     336                 :            :     {
     337         [ +  - ]:          1 :         std::initializer_list<std::string_view> words
     338                 :            :         {
     339                 :            :             "BLACK",
     340                 :            :             "HEAVY",
     341                 :            :             "FULLWIDTH",
     342                 :            :             "MATHEMATICAL BOLD",                  // 1D400,1D41A
     343                 :            :             "MATHEMATICAL SANS-SERIF BOLD",       // 1D5D4,1D5EE
     344                 :            :             "MATHEMATICAL SANS-SERIF BOLD ITALIC",// 1D63C,1D656
     345                 :            :             "MATHEMATICAL BOLD ITALIC",           // 1D468,1D482
     346                 :            :             "MATHEMATICAL ITALIC",                // 1D434,1D44E
     347                 :            :             "MATHEMATICAL SANS-SERIF ITALIC",     // 1D608,1D622
     348                 :            :             "",                                   // no modifier.
     349                 :            :             "MATHEMATICAL SANS-SERIF",            // 1D5A0,1D5BA
     350                 :            :             "MATHEMATICAL SCRIPT",                // 1D49C,1D4B6
     351                 :            :             "MATHEMATICAL BOLD SCRIPT",           // 1D4D0,1D4EA
     352                 :            :             "MATHEMATICAL FRAKTUR",               // 1D504,1D51E
     353                 :            :             "MATHEMATICAL BOLD FRAKTUR",          // 1D56C,1D586
     354                 :            :             "MATHEMATICAL DOUBLE-STRUCK",         // 1D538,1D552
     355                 :            :             "MATHEMATICAL MONOSPACE",             // 1D670,1D68A
     356                 :            :             "MATHEMATICAL",
     357                 :            :             "WHITE",
     358                 :            :             "LIGHT",
     359                 :            :             "HALFWIDTH",
     360                 :            :             "SMALL",
     361                 :            :             "PARENTHESIZED",
     362                 :            :             "CIRCLED",
     363                 :            :             "TAG"
     364                 :          1 :         };
     365   [ +  -  +  -  :          2 :         std::regex word_style_pat("(" + Join(words, "|") + ") (.*)", Ropt);
                   +  - ]
     366                 :          1 :         std::map<std::string, std::map<std::string, char32_t>> symbols;
     367   [ +  -  +  + ]:      33765 :         for(const auto& [name,code]: d.codes)
     368                 :            :         {
     369   [ +  -  +  -  :      33764 :             symbols[name][""] = code;
                   +  - ]
     370   [ +  -  +  + ]:      33764 :             if(std::smatch mat; std::regex_match(name, mat, word_style_pat))
     371   [ +  -  +  -  :      36006 :                 symbols[mat[2]][mat[1]] = code;
             +  -  +  - ]
     372                 :            :         }
     373   [ +  +  +  + ]:      34510 :         for(auto&& [basename,group]: symbols)
     374         [ +  + ]:      34509 :             if(group.size() > 1)
     375                 :            :             {
     376         [ +  - ]:        679 :                 out << "◆ ";
     377         [ +  + ]:      17654 :                 for(auto w: words)
     378   [ +  -  +  + ]:      33950 :                     if(auto i = group.find(std::string(w)); i != group.end())
     379         [ +  - ]:       4352 :                         out << u8(i->second);
     380         [ +  - ]:        679 :                 out << '\n';
     381                 :            :             }
     382                 :          1 :     }
     383                 :            : 
     384                 :            :     // Convert the equal-symbols list into a searchable one
     385                 :          1 :     std::map<char32_t, std::vector<char32_t>> equal_with;
     386         [ +  + ]:        140 :     for(auto w: equal_symbols)
     387         [ +  + ]:        749 :         for(char32_t code:  w)
     388         [ +  + ]:       4544 :         for(char32_t code2: w)
     389         [ +  + ]:       3934 :             if(code != code2)
     390   [ +  -  +  - ]:       3288 :                 equal_with[code].push_back(code2);
     391         [ +  + ]:        593 :     for(auto& w: equal_with)
     392                 :            :     {
     393                 :        592 :         std::sort(w.second.begin(), w.second.end());
     394                 :        592 :         w.second.erase(std::unique(w.second.begin(), w.second.end()), w.second.end());
     395                 :            :     }
     396                 :            : 
     397                 :          1 :     out << "// Then go through all symbols that are “WITH” something.\n"
     398                 :            :            "// As a general rule, try to compose things that have more “WITHs”\n"
     399         [ +  - ]:          1 :            "// from things that have less “WITHs”.\n";
     400                 :            : 
     401         [ +  - ]:          1 :     std::map<std::string, std::map<std::string, std::size_t>> with_lists;
     402         [ +  - ]:          1 :     std::regex with_match("(.*) WITH (.*)", Ropt);
     403   [ +  -  +  + ]:      33765 :     for(const auto& [name,code]: d.codes)
     404   [ +  -  +  + ]:      33764 :         if(std::smatch mat; std::regex_match(name, mat, with_match))
     405                 :            :         {
     406         [ +  - ]:       2524 :             std::string base = mat[1];
     407         [ +  - ]:       2524 :             std::string full = mat[2];
     408         [ +  - ]:       2524 :             std::vector<std::string_view> attrs = Split(full, " AND ");
     409         [ +  - ]:       2524 :             std::size_t len = attrs.size();
     410   [ +  -  +  - ]:       2524 :             auto& wl = with_lists[" WITH " + full];
     411   [ +  -  +  - ]:       2524 :             wl[""] = 0;
     412         [ +  + ]:       2885 :             for(std::size_t n = len; --n > 0; )
     413                 :            :             {
     414         [ +  - ]:        361 :                 std::map<std::size_t, std::string_view> pick;
     415                 :        839 :                 std::function<void(std::size_t,std::size_t)> Do = [&](std::size_t index, std::size_t start)
     416                 :            :                 {
     417         [ +  + ]:       1395 :                     for(std::size_t a=start; a<len; ++a)
     418                 :            :                     {
     419                 :        917 :                         pick[index] = attrs[a];
     420         [ +  + ]:        917 :                         if(index+1 == n)
     421                 :            :                         {
     422   [ +  -  +  - ]:        800 :                             wl[" WITH " + /*partial*/Join(pick, " AND ")] = pick.size();
     423                 :            :                             //print "try make $partial from $full for $name for $base\n";
     424                 :            :                         }
     425                 :            :                         else
     426                 :        117 :                             Do(index+1, a+1);
     427                 :            :                     }
     428         [ +  - ]:        839 :                 };
     429         [ +  - ]:        361 :                 Do(0, 0);
     430                 :        361 :             }
     431                 :      36288 :         }
     432         [ +  + ]:          3 :     for(auto operation: std::initializer_list<std::string_view>{"→ ", "← "})
     433   [ +  -  +  + ]:       1268 :         for(auto& [full_with, p]: with_lists)
     434                 :            :         {
     435         [ +  - ]:       1266 :             std::vector<std::pair<std::string,std::size_t>> partial_list(p.begin(), p.end());
     436                 :            :             // Sort in descending order according to the score (second item)
     437                 :       1266 :             std::sort(partial_list.begin(), partial_list.end(),
     438   [ -  -  -  -  :       1328 :                 [](const auto& a, const auto& b) { return b.second > a.second; });
          +  +  -  -  -  
          +  -  -  -  -  
          -  -  -  -  -  
             -  -  -  -  
                      - ]
     439                 :            :             // Find all symbols that have this "full with" list.
     440         [ +  - ]:       2532 :             std::regex pat("(.*)" + full_with, Ropt);
     441   [ +  +  +  + ]:   42746490 :             for(auto& [name,code]: d.codes)
     442   [ +  +  +  -  :   42745224 :                 if(name.size() >= full_with.size() && name.compare(name.size()-full_with.size(), full_with.size(), full_with) == 0)
                   +  + ]
     443                 :            :                 {
     444         [ +  - ]:       5048 :                     std::string_view mat1(name.data(), name.size()-full_with.size());
     445         [ +  - ]:       5048 :                     std::vector<std::tuple<char32_t, std::string_view, std::string_view>> rep_list, sub_list;
     446         [ +  - ]:       5048 :                     rep_list.emplace_back(code, name, mat1);
     447         [ +  + ]:       5048 :                     if(auto i = equal_with.find(code); i != equal_with.end())
     448         [ +  + ]:        234 :                         for(auto code2: i->second)
     449                 :            :                         {
     450         [ +  - ]:        158 :                             const auto& name2 = d.names[code2];
     451         [ +  - ]:        158 :                             std::smatch mat2;
     452         [ +  - ]:        158 :                             std::regex_match(name2, mat2, with_match); // (.*) WITH.*
     453         [ +  - ]:        158 :                             rep_list.emplace_back(code2, name2, std::string_view(&*mat2[1].first, mat2[1].second-mat2[1].first));
     454                 :        158 :                         }
     455         [ +  + ]:      11696 :                     for(auto& [partial_with,dummy]: partial_list)
     456         [ +  + ]:      13454 :                         for(auto& rep: rep_list)
     457                 :            :                         {
     458         [ +  - ]:       6806 :                             std::string sub_name{std::get<2>(rep)};
     459         [ +  - ]:       6806 :                             sub_name += partial_with;
     460                 :            :                             //print "can we find $sub_name?\n";
     461         [ +  + ]:       6806 :                             if(auto code2 = d.Has(sub_name); code2)
     462         [ +  - ]:       4856 :                                 sub_list.emplace_back(code2, sub_name, sub_name);
     463                 :       6806 :                         }
     464   [ +  -  +  + ]:       9904 :                     for(auto&& sub: sub_list) rep_list.push_back(std::move(sub));
     465         [ +  + ]:       5048 :                     if(rep_list.size() > 1)
     466                 :            :                     {
     467         [ +  - ]:       3502 :                         out << operation;
     468   [ +  -  +  + ]:      12018 :                         for(auto& rep: rep_list) out << u8(std::get<0>(rep));
     469         [ +  - ]:       3502 :                         out << '\n';
     470                 :            :                     }
     471                 :       5048 :                 }
     472                 :       1266 :         }
     473                 :            : 
     474         [ +  - ]:          1 :     out << R"(
     475                 :            : // Some symbols that act as last resort…
     476                 :            : = Ⅱ║∥‖ǁ𝄁
     477                 :            : = Ⅲ⫴⦀⫼𝍫ꔖ
     478                 :            : = -‐‑–—−-‒―➖─━一╴╶╸╺╼╾╼╾
     479                 :            : = ┄┅⋯┈┉╌╍
     480                 :            : = ╎╏¦
     481                 :            : = │┃|╿╽
     482                 :            : = ═=꓿
     483                 :            : = ~⁓~
     484                 :            : = <く𐌂ᐸᑉ
     485                 :            : = ┌┍┎┏╭╒╓╔гᴦⲅ
     486                 :            : = ┐┑┒┓╮╕╖╗
     487                 :            : = └┕┖┗╰╘╙╚˪լ
     488                 :            : = ┘┙┚┛╯╛╜╝
     489                 :            : = ┬┭┮┯┰┱┲┳╤╥╦⊤
     490                 :            : = ┴┵┶┷┸┹┺┻╧╨╩
     491                 :            : = ├┝┞┟┠┡┢┣߅╞╟╠
     492                 :            : = ┤┥┦┧┨┩┪┫╡╢╣
     493                 :            : = ┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋╪╫╬
     494                 :            : = ▉⬛██▉▇
     495                 :            : → ガカ
     496                 :            : → グク
     497                 :            : → ギキ
     498                 :            : → ゲケ
     499                 :            : → ゴコ
     500                 :            : → パバハ
     501                 :            : → ピビヒ
     502                 :            : → ペベヘ
     503                 :            : → ポボホ
     504                 :            : → プブフ
     505                 :            : → ピビ
     506                 :            : → ペベ
     507                 :            : → ポボ
     508                 :            : → プブ
     509                 :            : → ザサ
     510                 :            : → ジシ
     511                 :            : → ズス
     512                 :            : → ゼセ
     513                 :            : → ゾソ
     514                 :            : → ダタ
     515                 :            : → ヂチ
     516                 :            : → ヅツ
     517                 :            : → デテ
     518                 :            : → ドト
     519                 :            : → がか
     520                 :            : → ぐく
     521                 :            : → ぎき
     522                 :            : → げけ
     523                 :            : → ごこ
     524                 :            : → ぱばは
     525                 :            : → ぴびひ
     526                 :            : → ぺべへ
     527                 :            : → ぽぼほ
     528                 :            : → ぷぶふ
     529                 :            : → ぱば
     530                 :            : → ぴび
     531                 :            : → ぺべ
     532                 :            : → ぽぼ
     533                 :            : → ぷぶ
     534                 :            : → ざさ
     535                 :            : → じし
     536                 :            : → ずす
     537                 :            : → ぜせ
     538                 :            : → ぞそ
     539                 :            : → だた
     540                 :            : → ぢち
     541                 :            : → づつ
     542                 :            : → でて
     543                 :            : → どと
     544                 :            : )";
     545                 :          1 : }
     546                 :            : 
     547                 :          2 : std::vector<std::pair<char32_t/*goal*/, char32_t/*recipe*/>> ParseSimilarities(std::istream& f) // Same as old "similarities.inc"
     548                 :            : {
     549                 :          2 :     std::vector<std::pair<char32_t, char32_t>> results;
     550                 :            : 
     551   [ +  -  +  + ]:       9330 :     for(std::string line; std::getline(f, line), f; )
     552         [ +  + ]:       9326 :         if(!line.empty())
     553                 :            :         {
     554   [ -  +  -  - ]:       9318 :             if(line.back() == '\r') { line.erase(line.size()-1); }
     555   [ +  -  +  - ]:       9318 :             if(auto codes = FromUTF8(line); !codes.empty())
     556                 :            :             {
     557                 :       9318 :                 std::size_t a=2, b=codes.size();
     558   [ +  +  +  +  :       9318 :                 switch(codes[0])
                      + ]
     559                 :            :                 {
     560                 :            :                     case U'→':
     561                 :            :                         // Generate symbol on left from symbol on right
     562         [ +  + ]:       9620 :                         for(std::size_t n=a, m=n+1; m<b; ++m)
     563         [ +  - ]:       5576 :                             results.emplace_back(codes[n], codes[m]);
     564                 :            :                         break;
     565                 :            :                     case U'←':
     566                 :            :                         // Generate symbol on right from symbol on left
     567         [ +  + ]:      15520 :                         for(std::size_t n=b; n-- >= a; )
     568         [ +  + ]:      20344 :                             for(std::size_t m=n; --m >= a; )
     569         [ +  - ]:       8326 :                                 results.emplace_back(codes[n], codes[m]);
     570                 :            :                         break;
     571                 :       1358 :                     case U'◆':
     572                 :       1358 :                     {
     573                 :            :                         // Generate any symbol from any other,
     574                 :            :                         // with preference for close-by one
     575                 :       1358 :                         std::map<std::size_t, std::vector<std::pair<std::size_t,std::size_t>>> score;
     576         [ +  + ]:       5710 :                         for(std::size_t n=a; n<b; ++n)
     577         [ +  + ]:      31904 :                             for(std::size_t m=a; m<b; ++m)
     578         [ +  + ]:      27552 :                                 if(m != n)
     579   [ +  -  +  - ]:      23200 :                                     score[std::abs(int(n-m))].emplace_back(n, m);
     580         [ +  + ]:       4352 :                         for(auto& [dummy,pairs]: score)
     581         [ +  + ]:      26194 :                             for(auto pair: pairs)
     582         [ +  - ]:      23200 :                                 results.emplace_back(codes[pair.first], codes[pair.second]);
     583                 :       1358 :                         break;
     584                 :       1358 :                     }
     585                 :            :                     case U'=':
     586                 :            :                         // Generate any symbol from any other symbol
     587         [ +  + ]:       2098 :                         for(std::size_t n=a; n<b; ++n)
     588         [ +  + ]:      13708 :                             for(std::size_t m=a; m<b; ++m)
     589         [ +  + ]:      12002 :                                 if(m != n)
     590         [ +  - ]:      10296 :                                     results.emplace_back(codes[n], codes[m]);
     591                 :            :                         break;
     592                 :            :                 }
     593                 :       9318 :             }
     594                 :          0 :         }
     595                 :            : 
     596                 :            :     // Improve the list using iconv's //TRANSLIT option (pregenerated using make-alias.php)
     597   [ +  -  +  -  :          8 :     auto [location, status] = FindShareFile(std::filesystem::path("fonts") / "alias.txt");
          +  -  +  -  -  
                      + ]
     598         [ -  + ]:          2 :     if(std::filesystem::exists(status))
     599                 :            :     {
     600         [ +  - ]:          2 :         std::ifstream f2(location);
     601                 :          2 :         auto delim = " \t"sv, hex = "0123456789ABCDEFabcdef"sv;
     602   [ +  -  +  + ]:       9230 :         for(std::string line; std::getline(f2, line), f2; )
     603   [ +  -  +  - ]:       9228 :             if(!line.empty() && line[0] != '#')
     604                 :            :             {
     605                 :       9228 :                 std::size_t space    = line.find_first_of(delim);
     606         [ -  + ]:       9228 :                 if(space == line.npos) continue;
     607                 :       9228 :                 std::size_t notspace = line.find_first_not_of(delim, space);
     608         [ -  + ]:       9228 :                 if(notspace == line.npos) continue;
     609                 :       9228 :                 std::size_t notdigit = line.find_first_not_of(hex, notspace);
     610         [ -  + ]:       9228 :                 if(notdigit == line.npos) notdigit = line.size();
     611                 :            : 
     612         [ +  - ]:       9228 :                 results.emplace_back(std::stoi(std::string(line, 0, space), nullptr, 16),
     613   [ +  -  +  - ]:      27684 :                                      std::stoi(std::string(line, notspace, notdigit-notspace), nullptr, 16));
     614                 :          0 :             }
     615                 :          2 :     }
     616                 :            : 
     617                 :            :     // Delete duplicates
     618         [ +  - ]:          4 :     std::unordered_set<std::uint_fast64_t> seen;
     619         [ +  - ]:          2 :     results.erase(std::remove_if(results.begin(), results.end(), [&](auto& pair)
     620                 :            :     {
     621                 :      56626 :         uint_fast64_t key = pair.first; key = (key << 32) | pair.second;
     622         [ +  + ]:      56626 :         if(seen.find(key) == seen.end())
     623                 :            :         {
     624                 :      49138 :             seen.insert(key);
     625                 :      49138 :             return false;
     626                 :            :         }
     627                 :            :         return true;
     628         [ +  - ]:          2 :     }), results.end());
     629                 :            : 
     630                 :          2 :     std::sort(results.begin(), results.end());
     631                 :          2 :     return results;
     632                 :          2 : }
     633                 :            : 
     634                 :          2 : std::vector<std::pair<char32_t/*goal*/, char32_t/*recipe*/>> ParseSimilarities() // Same as old "similarities.inc"
     635                 :            : {
     636   [ +  -  +  - ]:          4 :     auto [path, status] = FindCacheFile("similarities.dat", true);
     637         [ +  - ]:          2 :     auto [unipath, unistatus] = UnicodeDataFileName();
     638                 :            : 
     639         [ -  + ]:          2 :     if(!std::filesystem::exists(status)
     640   [ +  -  +  + ]:          2 :     || !std::filesystem::file_size(path)
     641   [ +  -  +  - ]:          1 :     || (std::filesystem::exists(unipath)
     642   [ +  -  +  -  :          1 :      && std::filesystem::last_write_time(unipath) > std::filesystem::last_write_time(path)))
                   -  + ]
     643                 :            :     {
     644         [ +  - ]:          1 :         std::ofstream file(path);
     645   [ +  -  +  - ]:          1 :         MakeSimilarities(file, unipath);
     646                 :          1 :     }
     647                 :            : 
     648         [ +  - ]:          2 :     std::ifstream f(path);
     649         [ +  - ]:          2 :     return ParseSimilarities(f);
     650                 :          6 : }

Generated by: LCOV version 1.16