1 // =================================
   2 // Copyright (c) 2024 Seppo Laakko
   3 // Distributed under the MIT license
   4 // =================================
   5 
   6 using System.IO;
   7 using System.Collections;
   8 using System.Unicode;
   9 
  10 namespace System
  11 {
  12     [nodiscard]
  13     public Result<ustring> ToUtf32(const string& utf8Str)
  14     {
  15         ustring result;
  16         const char* p = utf8Str.Chars();
  17         long bytesRemaining = utf8Str.Length();
  18         while (bytesRemaining > 0)
  19         {
  20             char c = *p;
  21             byte x = cast<byte>(c);
  22             if ((x & 128u) ==  0u)
  23             {
  24                 result.Append(cast<uchar>(cast<uint>(x)));
  25                 --bytesRemaining;
  26                 ++p;
  27             }
  28             else if ((x & 224u) ==  192u )
  29             {
  30                 if (bytesRemaining < 2)
  31                 {
  32                     int errorId = AllocateError("invalid UTF-8 sequence: index=" + ToString(p - utf8Str.Chars()));
  33                     return Result<ustring>(ErrorId(errorId));
  34                 }
  35                 uchar u = cast<uchar>(cast<uint>(0u));
  36                 byte b1 = cast<byte>(p[1]);
  37                 if ((b1 & 192u) !=  128u )
  38                 {
  39                     int errorId = AllocateError("invalid UTF-8 sequence: index=" + ToString(p - utf8Str.Chars()));
  40                     return Result<ustring>(ErrorId(errorId));
  41                 }
  42                 byte shift = 0u;
  43                 for (byte i = 0u; i < 6u; ++i;)
  44                 {
  45                     byte bit = b1 & 1u;
  46                     b1 = b1 >> 1u;
  47                     u = cast<uchar>(cast<uint>(u) | (cast<uint>(bit) << shift));
  48                     ++shift;
  49                 }
  50                 byte b0 = x;
  51                 for (byte i = 0u; i < 5u; ++i;)
  52                 {
  53                     byte bit = b0 & 1u;
  54                     b0 = b0 >> 1u;
  55                     u = cast<uchar>(cast<uint>(u) | (cast<uint>(bit) << shift));
  56                     ++shift;
  57                 }
  58                 result.Append(u);
  59                 bytesRemaining = bytesRemaining - 2;
  60                 p = p + 2;
  61             }
  62             else if ((x & 240u) ==  224u )
  63             {
  64                 if (bytesRemaining < 3)
  65                 {
  66                     int errorId = AllocateError("invalid UTF-8 sequence: index=" + ToString(p - utf8Str.Chars()));
  67                     return Result<ustring>(ErrorId(errorId));
  68                 }
  69                 uchar u = cast<uchar>(cast<uint>(0u));
  70                 byte b2 = cast<byte>(p[2]);
  71                 if ((b2 & 192u) !=  128u )
  72                 {
  73                     int errorId = AllocateError("invalid UTF-8 sequence: index=" + ToString(p - utf8Str.Chars()));
  74                     return Result<ustring>(ErrorId(errorId));
  75                 }
  76                 byte shift = 0u;
  77                 for (byte i = 0u; i < 6u; ++i;)
  78                 {
  79                     byte bit = b2 & 1u;
  80                     b2 = b2 >> 1u;
  81                     u = cast<uchar>(cast<uint>(u) | (cast<uint>(bit) << shift));
  82                     ++shift;
  83                 }
  84                 byte b1 = cast<byte>(p[1]);
  85                 if ((b1 & 192u) !=  128u )
  86                 {
  87                     int errorId = AllocateError("invalid UTF-8 sequence: index=" + ToString(p - utf8Str.Chars()));
  88                     return Result<ustring>(ErrorId(errorId));
  89                 }
  90                 for (byte i = 0u; i < 6u; ++i;)
  91                 {
  92                     byte bit = b1 & 1u;
  93                     b1 = b1 >> 1u;
  94                     u = cast<uchar>(cast<uint>(u) | (cast<uint>(bit) << shift));
  95                     ++shift;
  96                 }
  97                 byte b0 = x;
  98                 for (byte i = 0u; i < 4u; ++i;)
  99                 {
 100                     byte bit = b0 & 1u;
 101                     b0 = b0 >> 1u;
 102                     u = cast<uchar>(cast<uint>(u) | (cast<uint>(bit) << shift));
 103                     ++shift;
 104                 }
 105                 result.Append(u);
 106                 bytesRemaining = bytesRemaining - 3;
 107                 p = p + 3;
 108             }
 109             else if ((x & 248u) ==  240u )
 110             {
 111                 if (bytesRemaining < 4)
 112                 {
 113                     int errorId = AllocateError("invalid UTF-8 sequence: index=" + ToString(p - utf8Str.Chars()));
 114                     return Result<ustring>(ErrorId(errorId));
 115                 }
 116                 uchar u = cast<uchar>(cast<uint>(0u));
 117                 byte b3 = cast<byte>(p[3]);
 118                 if ((b3 & 192u) !=  128u )
 119                 {
 120                     int errorId = AllocateError("invalid UTF-8 sequence: index=" + ToString(p - utf8Str.Chars()));
 121                     return Result<ustring>(ErrorId(errorId));
 122                 }
 123                 byte shift = 0u;
 124                 for (byte i = 0u; i < 6u; ++i;)
 125                 {
 126                     byte bit = b3 & 1u;
 127                     b3 = b3 >> 1u;
 128                     u = cast<uchar>(cast<uint>(u) | (cast<uint>(bit) << shift));
 129                     ++shift;
 130                 }
 131                 byte b2 = cast<byte>(p[2]);
 132                 if ((b2 & 192u) !=  128u )
 133                 {
 134                     int errorId = AllocateError("invalid UTF-8 sequence: index=" + ToString(p - utf8Str.Chars()));
 135                     return Result<ustring>(ErrorId(errorId));
 136                 }
 137                 for (byte i = 0u; i < 6u; ++i;)
 138                 {
 139                     byte bit = b2 & 1u;
 140                     b2 = b2 >> 1u;
 141                     u = cast<uchar>(cast<uint>(u) | (cast<uint>(bit) << shift));
 142                     ++shift;
 143                 }
 144                 byte b1 = cast<byte>(p[1]);
 145                 if ((b1 & 192u) !=  128u )
 146                 {
 147                     int errorId = AllocateError("invalid UTF-8 sequence: index=" + ToString(p - utf8Str.Chars()));
 148                     return Result<ustring>(ErrorId(errorId));
 149                 }
 150                 for (byte i = 0u; i < 6u; ++i;)
 151                 {
 152                     byte bit = b1 & 1u;
 153                     b1 = b1 >> 1u;
 154                     u = cast<uchar>(cast<uint>(u) | (cast<uint>(bit) << shift));
 155                     ++shift;
 156                 }
 157                 byte b0 = x;
 158                 for (byte i = 0u; i < 3u; ++i;)
 159                 {
 160                     byte bit = b0 & 1u;
 161                     b0 = b0 >> 1u;
 162                     u = cast<uchar>(cast<uint>(u) | (cast<uint>(bit) << shift));
 163                     ++shift;
 164                 }
 165                 result.Append(u);
 166                 bytesRemaining = bytesRemaining - 4;
 167                 p = p + 4;
 168             }
 169             else
 170             {
 171                 int errorId = AllocateError("invalid UTF-8 sequence: index=" + ToString(p - utf8Str.Chars()));
 172                 return Result<ustring>(ErrorId(errorId));
 173             }
 174         }
 175         return Result<ustring>(result);
 176     }
 177 
 178     [nodiscard]
 179     public Result<ustring> ToUtf32(const wstring& utf16Str)
 180     {
 181         ustring result;
 182         const wchar* w = utf16Str.Chars();
 183         long remaining = utf16Str.Length();
 184         while (remaining > 0)
 185         {
 186             wchar w1 = *w++;
 187             --remaining;
 188             if (cast<ushort>(w1) < 55296u ||  cast<ushort>(w1) > 57343u )
 189             {
 190                 result.Append(w1);
 191             }
 192             else
 193             {
 194                 if (cast<ushort>(w1) < 55296u ||  cast<ushort>(w1) > 56319u )
 195                 {
 196                     int errorId = AllocateError("invalid UTF-16 sequence: index=" + ToString(w - utf16Str.Chars()));
 197                     return Result<ustring>(ErrorId(errorId));
 198                 }
 199                 if (remaining > 0)
 200                 {
 201                     wchar w2 = *w++;
 202                     --remaining;
 203                     if (cast<ushort>(w2) < 56320u ||  cast<ushort>(w2) > 57343u )
 204                     {
 205                         int errorId = AllocateError("invalid UTF-16 sequence: index=" + ToString(w - utf16Str.Chars()));
 206                         return Result<ustring>(ErrorId(errorId));
 207                     }
 208                     else
 209                     {
 210                         uchar uprime = cast<uchar>(((1023u &   cast<uint>(w1)) << 10u) | (1023u &   cast<uint>(w2)));
 211                         uchar u = cast<uchar>(cast<uint>(uprime) + 65536u);
 212                         result.Append(u);
 213                     }
 214                 }
 215                 else
 216                 {
 217                     int errorId = RtmAllocateError("invalid UTF-16 sequence");
 218                     return Result<ustring>(ErrorId(errorId));
 219                 }
 220             }
 221         }
 222         return Result<ustring>(result);
 223     }
 224 
 225     [nodiscard]
 226     public Result<wstring> ToUtf16(const ustring& utf32Str)
 227     {
 228         wstring result;
 229         int index = 0;
 230         for (uchar u : utf32Str)
 231         {
 232             if (cast<uint>(u) > 1114111u )
 233             {
 234                 int errorId = AllocateError("invalid UTF-32 code point: index=" + ToString(index));
 235                 return Result<wstring>(ErrorId(errorId));
 236             }
 237             if (cast<uint>(u) < 65536u  )
 238             {
 239                 if (cast<uint>(u) >= 55296u && cast<uint>(u) <= 57343u)
 240                 {
 241                     int errorId = AllocateError("invalid UTF-32 code point (reserved for UTF-16): index=" + ToString(index));
 242                     return Result<wstring>(ErrorId(errorId));
 243                 }
 244                 wchar x = cast<wchar>(u);
 245                 result.Append(x);
 246             }
 247             else
 248             {
 249                 uchar uprime = cast<uchar>(cast<uint>(u) - 65536u);
 250                 wchar w1 = cast<wchar>(55296u);
 251                 wchar w2 = cast<wchar>(56320u);
 252                 for (ushort i = 0u; i < 10u; ++i;)
 253                 {
 254                     ushort bit = cast<ushort>(cast<uint>(uprime) & (cast<uint>(1u) <<   i));
 255                     w2 = cast<wchar>(cast<ushort>(w2) | bit);
 256                 }
 257                 for (ushort i = 10u; i < 20u; ++i;)
 258                 {
 259                     ushort bit = cast<ushort>((cast<uint>(uprime) & (cast<uint>(1u) <<   i)) >> 10u);
 260                     w1 = cast<wchar>(cast<ushort>(w1) | bit);
 261                 }
 262                 result.Append(w1);
 263                 result.Append(w2);
 264             }
 265             ++index;
 266         }
 267         return Result<wstring>(result);
 268     }
 269 
 270     [nodiscard]
 271     public Result<wstring> ToUtf16(const string& utf8Str)
 272     {
 273         auto result = ToUtf32(utf8Str);
 274         if (result.Error())
 275         {
 276             return Result<wstring>(ErrorId(result.GetErrorId()));
 277         }
 278         return ToUtf16(result.Value());
 279     }
 280 
 281     [nodiscard]
 282     public Result<string> ToUtf8(const ustring& utf32Str)
 283     {
 284         string result;
 285         int index = 0;
 286         for (uchar c : utf32Str)
 287         {
 288             uint x = cast<uint>(c);
 289             if (x < 128u )
 290             {
 291                 result.Append(cast<char>(x & 127u));
 292             }
 293             else if (x < 2048u )
 294             {
 295                 byte b1 = 128u;
 296                 for (byte i = 0u; i < 6u; ++i;)
 297                 {
 298                     b1 = b1 | (cast<byte>(x & 1u) << i);
 299                     x = x >> 1u;
 300                 }
 301                 byte b0 = 192u;
 302                 for (byte i = 0u; i < 5u; ++i;)
 303                 {
 304                     b0 = b0 | (cast<byte>(x & 1u) << i);
 305                     x = x >> 1u;
 306                 }
 307                 result.Append(cast<char>(b0));
 308                 result.Append(cast<char>(b1));
 309             }
 310             else if (x < 65536u  )
 311             {
 312                 byte b2 = 128u;
 313                 for (byte i = 0u; i < 6u; ++i;)
 314                 {
 315                     b2 = b2 | (cast<byte>(x & 1u) << i);
 316                     x = x >> 1u;
 317                 }
 318                 byte b1 = 128u;
 319                 for (byte i = 0u; i < 6u; ++i;)
 320                 {
 321                     b1 = b1 | (cast<byte>(x & 1u) << i);
 322                     x = x >> 1u;
 323                 }
 324                 byte b0 = 224u;
 325                 for (byte i = 0u; i < 4u; ++i;)
 326                 {
 327                     b0 = b0 | (cast<byte>(x & 1u) << i);
 328                     x = x >> 1u;
 329                 }
 330                 result.Append(cast<char>(b0));
 331                 result.Append(cast<char>(b1));
 332                 result.Append(cast<char>(b2));
 333             }
 334             else if (x < 1114112u )
 335             {
 336                 byte b3 = 128u;
 337                 for (byte i = 0u; i < 6u; ++i;)
 338                 {
 339                     b3 = b3 | (cast<byte>(x & 1u) << i);
 340                     x = x >> 1u;
 341                 }
 342                 byte b2 = 128u;
 343                 for (byte i = 0u; i < 6u; ++i;)
 344                 {
 345                     b2 = b2 | (cast<byte>(x & 1u) << i);
 346                     x = x >> 1u;
 347                 }
 348                 byte b1 = 128u;
 349                 for (byte i = 0u; i < 6u; ++i;)
 350                 {
 351                     b1 = b1 | (cast<byte>(x & 1u) << i);
 352                     x = x >> 1u;
 353                 }
 354                 byte b0 = 240u;
 355                 for (byte i = 0u; i < 3u; ++i;)
 356                 {
 357                     b0 = b0 | (cast<byte>(x & 1u) << i);
 358                     x = x >> 1u;
 359                 }
 360                 result.Append(cast<char>(b0));
 361                 result.Append(cast<char>(b1));
 362                 result.Append(cast<char>(b2));
 363                 result.Append(cast<char>(b3));
 364             }
 365             else
 366             {
 367                 int errorId = AllocateError("invalid UTF-32 code point (" + ToString(x) + "): index=" + ToString(index));
 368                 return Result<string>(ErrorId(errorId));
 369             }
 370             ++index;
 371         }
 372         return Result<string>(result);
 373     }
 374 
 375     [nodiscard]
 376     public Result<string> ToUtf8(const wstring& utf16Str)
 377     {
 378         auto result = ToUtf32(utf16Str);
 379         if (result.Error())
 380         {
 381             return Result<string>(ErrorId(result.GetErrorId()));
 382         }
 383         return ToUtf8(result.Value());
 384     }
 385 
 386     [nodiscard]
 387     public Result<bool> IsUpperLetter(uchar c)
 388     {
 389         auto result = GetGeneralCategory(c);
 390         if (result.Error())
 391         {
 392             return Result<bool>(ErrorId(result.GetErrorId()));
 393         }
 394         return Result<bool>(result.Value() == GeneralCategoryId.Lu);
 395     }
 396 
 397     [nodiscard]
 398     public Result<bool> IsLowerLetter(uchar c)
 399     {
 400         auto result = GetGeneralCategory(c);
 401         if (result.Error())
 402         {
 403             return Result<bool>(ErrorId(result.GetErrorId()));
 404         }
 405         return Result<bool>(result.Value() == GeneralCategoryId.Ll);
 406     }
 407 
 408     [nodiscard]
 409     public Result<bool> IsTitleLetter(uchar c)
 410     {
 411         auto result = GetGeneralCategory(c);
 412         if (result.Error())
 413         {
 414             return Result<bool>(ErrorId(result.GetErrorId()));
 415         }
 416         return Result<bool>(result.Value() == GeneralCategoryId.Lt);
 417     }
 418 
 419     [nodiscard]
 420     public Result<bool> IsModifierLetter(uchar c)
 421     {
 422         auto result = GetGeneralCategory(c);
 423         if (result.Error())
 424         {
 425             return Result<bool>(ErrorId(result.GetErrorId()));
 426         }
 427         return Result<bool>(result.Value() == GeneralCategoryId.Lm);
 428     }
 429 
 430     [nodiscard]
 431     public Result<bool> IsOtherLetter(uchar c)
 432     {
 433         auto result = GetGeneralCategory(c);
 434         if (result.Error())
 435         {
 436             return Result<bool>(ErrorId(result.GetErrorId()));
 437         }
 438         return Result<bool>(result.Value() == GeneralCategoryId.Lo);
 439     }
 440 
 441     [nodiscard]
 442     public Result<bool> IsCasedLetter(uchar c)
 443     {
 444         return HasGeneralCategory(cGeneralCategoryId.LC);
 445     }
 446 
 447     [nodiscard]
 448     public Result<bool> IsLetter(uchar c)
 449     {
 450         return HasGeneralCategory(cGeneralCategoryId.L);
 451     }
 452 
 453     [nodiscard]
 454     public Result<bool> IsNonspacingMark(uchar c)
 455     {
 456         auto result = GetGeneralCategory(c);
 457         if (result.Error())
 458         {
 459             return Result<bool>(ErrorId(result.GetErrorId()));
 460         }
 461         return Result<bool>(result.Value() == GeneralCategoryId.Mn);
 462     }
 463 
 464     [nodiscard]
 465     public Result<bool> IsSpacingMark(uchar c)
 466     {
 467         auto result = GetGeneralCategory(c);
 468         if (result.Error())
 469         {
 470             return Result<bool>(ErrorId(result.GetErrorId()));
 471         }
 472         return Result<bool>(result.Value() == GeneralCategoryId.Mc);
 473     }
 474 
 475     [nodiscard]
 476     public Result<bool> IsEnclosingMark(uchar c)
 477     {
 478         auto result = GetGeneralCategory(c);
 479         if (result.Error())
 480         {
 481             return Result<bool>(ErrorId(result.GetErrorId()));
 482         }
 483         return Result<bool>(result.Value() == GeneralCategoryId.Me);
 484     }
 485 
 486     [nodiscard]
 487     public Result<bool> IsMark(uchar c)
 488     {
 489         return HasGeneralCategory(cGeneralCategoryId.M);
 490     }
 491 
 492     [nodiscard]
 493     public Result<bool> IsDecimalNumber(uchar c)
 494     {
 495         auto result = GetGeneralCategory(c);
 496         if (result.Error())
 497         {
 498             return Result<bool>(ErrorId(result.GetErrorId()));
 499         }
 500         return Result<bool>(result.Value() == GeneralCategoryId.Nd);
 501     }
 502 
 503     [nodiscard]
 504     public Result<bool> IsLetterNumber(uchar c)
 505     {
 506         auto result = GetGeneralCategory(c);
 507         if (result.Error())
 508         {
 509             return Result<bool>(ErrorId(result.GetErrorId()));
 510         }
 511         return Result<bool>(result.Value() == GeneralCategoryId.Nl);
 512     }
 513 
 514     [nodiscard]
 515     public Result<bool> IsOtherNumber(uchar c)
 516     {
 517         auto result = GetGeneralCategory(c);
 518         if (result.Error())
 519         {
 520             return Result<bool>(ErrorId(result.GetErrorId()));
 521         }
 522         return Result<bool>(result.Value() == GeneralCategoryId.No);
 523     }
 524 
 525     [nodiscard]
 526     public Result<bool> IsNumber(uchar c)
 527     {
 528         return HasGeneralCategory(cGeneralCategoryId.N);
 529     }
 530 
 531     [nodiscard]
 532     public Result<bool> IsConnectorPunctuation(uchar c)
 533     {
 534         auto result = GetGeneralCategory(c);
 535         if (result.Error())
 536         {
 537             return Result<bool>(ErrorId(result.GetErrorId()));
 538         }
 539         return Result<bool>(result.Value() == GeneralCategoryId.Pc);
 540     }
 541 
 542     [nodiscard]
 543     public Result<bool> IsDashPunctuation(uchar c)
 544     {
 545         auto result = GetGeneralCategory(c);
 546         if (result.Error())
 547         {
 548             return Result<bool>(ErrorId(result.GetErrorId()));
 549         }
 550         return Result<bool>(result.Value() == GeneralCategoryId.Pd);
 551     }
 552 
 553     [nodiscard]
 554     public Result<bool> IsOpenPunctuation(uchar c)
 555     {
 556         auto result = GetGeneralCategory(c);
 557         if (result.Error())
 558         {
 559             return Result<bool>(ErrorId(result.GetErrorId()));
 560         }
 561         return Result<bool>(result.Value() == GeneralCategoryId.Ps);
 562     }
 563 
 564     [nodiscard]
 565     public Result<bool> IsClosePunctuation(uchar c)
 566     {
 567         auto result = GetGeneralCategory(c);
 568         if (result.Error())
 569         {
 570             return Result<bool>(ErrorId(result.GetErrorId()));
 571         }
 572         return Result<bool>(result.Value() == GeneralCategoryId.Pe);
 573     }
 574 
 575     [nodiscard]
 576     public Result<bool> IsInitialPunctuation(uchar c)
 577     {
 578         auto result = GetGeneralCategory(c);
 579         if (result.Error())
 580         {
 581             return Result<bool>(ErrorId(result.GetErrorId()));
 582         }
 583         return Result<bool>(result.Value() == GeneralCategoryId.Pi);
 584     }
 585 
 586     [nodiscard]
 587     public Result<bool> IsFinalPunctuation(uchar c)
 588     {
 589         auto result = GetGeneralCategory(c);
 590         if (result.Error())
 591         {
 592             return Result<bool>(ErrorId(result.GetErrorId()));
 593         }
 594         return Result<bool>(result.Value() == GeneralCategoryId.Pf);
 595     }
 596 
 597     [nodiscard]
 598     public Result<bool> IsOtherPunctuation(uchar c)
 599     {
 600         auto result = GetGeneralCategory(c);
 601         if (result.Error())
 602         {
 603             return Result<bool>(ErrorId(result.GetErrorId()));
 604         }
 605         return Result<bool>(result.Value() == GeneralCategoryId.Po);
 606     }
 607 
 608     [nodiscard]
 609     public Result<bool> IsPunctuation(uchar c)
 610     {
 611         return HasGeneralCategory(cGeneralCategoryId.P);
 612     }
 613 
 614     [nodiscard]
 615     public Result<bool> IsMathSymbol(uchar c)
 616     {
 617         auto result = GetGeneralCategory(c);
 618         if (result.Error())
 619         {
 620             return Result<bool>(ErrorId(result.GetErrorId()));
 621         }
 622         return Result<bool>(result.Value() == GeneralCategoryId.Sm);
 623     }
 624 
 625     [nodiscard]
 626     public Result<bool> IsCurrencySymbol(uchar c)
 627     {
 628         auto result = GetGeneralCategory(c);
 629         if (result.Error())
 630         {
 631             return Result<bool>(ErrorId(result.GetErrorId()));
 632         }
 633         return Result<bool>(result.Value() == GeneralCategoryId.Sc);
 634     }
 635 
 636     [nodiscard]
 637     public Result<bool> IsModifierSymbol(uchar c)
 638     {
 639         auto result = GetGeneralCategory(c);
 640         if (result.Error())
 641         {
 642             return Result<bool>(ErrorId(result.GetErrorId()));
 643         }
 644         return Result<bool>(result.Value() == GeneralCategoryId.Sk);
 645     }
 646 
 647     [nodiscard]
 648     public Result<bool> IsOtherSymbol(uchar c)
 649     {
 650         auto result = GetGeneralCategory(c);
 651         if (result.Error())
 652         {
 653             return Result<bool>(ErrorId(result.GetErrorId()));
 654         }
 655         return Result<bool>(result.Value() == GeneralCategoryId.So);
 656     }
 657 
 658     [nodiscard]
 659     public Result<bool> IsSymbol(uchar c)
 660     {
 661         return HasGeneralCategory(cGeneralCategoryId.S);
 662     }
 663 
 664     [nodiscard]
 665     public Result<bool> IsSpaceSeparator(uchar c)
 666     {
 667         auto result = GetGeneralCategory(c);
 668         if (result.Error())
 669         {
 670             return Result<bool>(ErrorId(result.GetErrorId()));
 671         }
 672         return Result<bool>(result.Value() == GeneralCategoryId.Zs);
 673     }
 674 
 675     [nodiscard]
 676     public Result<bool> IsLineSeparator(uchar c)
 677     {
 678         auto result = GetGeneralCategory(c);
 679         if (result.Error())
 680         {
 681             return Result<bool>(ErrorId(result.GetErrorId()));
 682         }
 683         return Result<bool>(result.Value() == GeneralCategoryId.Zl);
 684     }
 685 
 686     [nodiscard]
 687     public Result<bool> IsParagraphSeparator(uchar c)
 688     {
 689         auto result = GetGeneralCategory(c);
 690         if (result.Error())
 691         {
 692             return Result<bool>(ErrorId(result.GetErrorId()));
 693         }
 694         return Result<bool>(result.Value() == GeneralCategoryId.Zp);
 695     }
 696 
 697     [nodiscard]
 698     public Result<bool> IsSeparator(uchar c)
 699     {
 700         return HasGeneralCategory(cGeneralCategoryId.Z);
 701     }
 702 
 703     [nodiscard]
 704     public Result<bool> IsControl(uchar c)
 705     {
 706         auto result = GetGeneralCategory(c);
 707         if (result.Error())
 708         {
 709             return Result<bool>(ErrorId(result.GetErrorId()));
 710         }
 711         return Result<bool>(result.Value() == GeneralCategoryId.Cc);
 712     }
 713 
 714     [nodiscard]
 715     public Result<bool> IsFormat(uchar c)
 716     {
 717         auto result = GetGeneralCategory(c);
 718         if (result.Error())
 719         {
 720             return Result<bool>(ErrorId(result.GetErrorId()));
 721         }
 722         return Result<bool>(result.Value() == GeneralCategoryId.Cf);
 723     }
 724 
 725     [nodiscard]
 726     public Result<bool> IsSurrogate(uchar c)
 727     {
 728         auto result = GetGeneralCategory(c);
 729         if (result.Error())
 730         {
 731             return Result<bool>(ErrorId(result.GetErrorId()));
 732         }
 733         return Result<bool>(result.Value() == GeneralCategoryId.Cs);
 734     }
 735 
 736     [nodiscard]
 737     public Result<bool> IsPrivateUse(uchar c)
 738     {
 739         auto result = GetGeneralCategory(c);
 740         if (result.Error())
 741         {
 742             return Result<bool>(ErrorId(result.GetErrorId()));
 743         }
 744         return Result<bool>(result.Value() == GeneralCategoryId.Co);
 745     }
 746 
 747     [nodiscard]
 748     public Result<bool> IsUnassigned(uchar c)
 749     {
 750         auto result = GetGeneralCategory(c);
 751         if (result.Error())
 752         {
 753             return Result<bool>(ErrorId(result.GetErrorId()));
 754         }
 755         return Result<bool>(result.Value() == GeneralCategoryId.Cn);
 756     }
 757 
 758     [nodiscard]
 759     public Result<bool> IsOther(uchar c)
 760     {
 761         return HasGeneralCategory(cGeneralCategoryId.C);
 762     }
 763 
 764     [nodiscard]
 765     public Result<bool> IsGraphic(uchar c)
 766     {
 767         return HasGeneralCategory(cGeneralCategoryId.G);
 768     }
 769 
 770     [nodiscard]
 771     public Result<bool> IsBaseChar(uchar c)
 772     {
 773         return HasGeneralCategory(cGeneralCategoryId.B);
 774     }
 775 
 776     [nodiscard]
 777     public Result<bool> IsCombining(uchar c)
 778     {
 779         return IsMark(c);
 780     }
 781 
 782     [nodiscard]
 783     public Result<uchar> ToUpper(uchar c)
 784     {
 785         auto result = GetCharacterInfo(c);
 786         if (result.Error())
 787         {
 788             return Result<uchar>(ErrorId(result.GetErrorId()));
 789         }
 790         const CharacterInfo* characterInfo = result.Value();
 791         return Result<uchar>(characterInfo->Upper());
 792     }
 793 
 794     [nodiscard]
 795     public Result<uchar> ToLower(uchar c)
 796     {
 797         auto result = GetCharacterInfo(c);
 798         if (result.Error())
 799         {
 800             return Result<uchar>(ErrorId(result.GetErrorId()));
 801         }
 802         const CharacterInfo* characterInfo = result.Value();
 803         return Result<uchar>(characterInfo->Lower());
 804     }
 805 
 806     [nodiscard]
 807     public Result<uchar> ToTitle(uchar c)
 808     {
 809         auto result = GetCharacterInfo(c);
 810         if (result.Error())
 811         {
 812             return Result<uchar>(ErrorId(result.GetErrorId()));
 813         }
 814         const CharacterInfo* characterInfo = result.Value();
 815         return Result<uchar>(characterInfo->Title());
 816     }
 817 
 818     [nodiscard]
 819     public Result<uchar> ToFolding(uchar c)
 820     {
 821         auto result = GetCharacterInfo(c);
 822         if (result.Error())
 823         {
 824             return Result<uchar>(ErrorId(result.GetErrorId()));
 825         }
 826         const CharacterInfo* characterInfo = result.Value();
 827         return Result<uchar>(characterInfo->Folding());
 828     }
 829 
 830     [nodiscard]
 831     public Result<ustring> FullUpper(uchar c)
 832     {
 833         auto result = GetExtendedCharacterInfo(c);
 834         if (result.Error())
 835         {
 836             return Result<ustring>(ErrorId(result.GetErrorId()));
 837         }
 838         const ExtendedCharacterInfo* extendedCharacterInfo = result.Value();
 839         return Result<ustring>(extendedCharacterInfo->FullUpper());
 840     }
 841 
 842     [nodiscard]
 843     public Result<ustring> FullLower(uchar c)
 844     {
 845         auto result = GetExtendedCharacterInfo(c);
 846         if (result.Error())
 847         {
 848             return Result<ustring>(ErrorId(result.GetErrorId()));
 849         }
 850         const ExtendedCharacterInfo* extendedCharacterInfo = result.Value();
 851         return Result<ustring>(extendedCharacterInfo->FullLower());
 852     }
 853 
 854     [nodiscard]
 855     public Result<ustring> FullTitle(uchar c)
 856     {
 857         auto result = GetExtendedCharacterInfo(c);
 858         if (result.Error())
 859         {
 860             return Result<ustring>(ErrorId(result.GetErrorId()));
 861         }
 862         const ExtendedCharacterInfo* extendedCharacterInfo = result.Value();
 863         return Result<ustring>(extendedCharacterInfo->FullTitle());
 864     }
 865 
 866     [nodiscard]
 867     public Result<ustring> FullFolding(uchar c)
 868     {
 869         auto result = GetExtendedCharacterInfo(c);
 870         if (result.Error())
 871         {
 872             return Result<ustring>(ErrorId(result.GetErrorId()));
 873         }
 874         const ExtendedCharacterInfo* extendedCharacterInfo = result.Value();
 875         return Result<ustring>(extendedCharacterInfo->FullFolding());
 876     }
 877 
 878     [nodiscard]
 879     public Result<bool> IsWhiteSpace(uchar c)
 880     {
 881         auto result = GetCharacterInfo(c);
 882         if (result.Error())
 883         {
 884             return Result<bool>(ErrorId(result.GetErrorId()));
 885         }
 886         const CharacterInfo* characterInfo = result.Value();
 887         return Result<bool>(characterInfo->GetBinaryProperty(BinaryPropertyId.whiteSpace));
 888     }
 889 
 890     [nodiscard]
 891     public Result<bool> IsAlphabetic(uchar c)
 892     {
 893         auto result = GetCharacterInfo(c);
 894         if (result.Error())
 895         {
 896             return Result<bool>(ErrorId(result.GetErrorId()));
 897         }
 898         const CharacterInfo* characterInfo = result.Value();
 899         return Result<bool>(characterInfo->GetBinaryProperty(BinaryPropertyId.alphabetic));
 900     }
 901 
 902     [nodiscard]
 903     public Result<bool> IsAsciiHexDigit(uchar c)
 904     {
 905         auto result = GetCharacterInfo(c);
 906         if (result.Error())
 907         {
 908             return Result<bool>(ErrorId(result.GetErrorId()));
 909         }
 910         const CharacterInfo* characterInfo = result.Value();
 911         return Result<bool>(characterInfo->GetBinaryProperty(BinaryPropertyId.asciiHexDigit));
 912     }
 913 
 914     [nodiscard]
 915     public bool IsAsciiDigit(uchar c)
 916     {
 917         return cast<uint>(c) < 256u && IsDigit(cast<char>(c));
 918     }
 919 
 920     [nodiscard]
 921     public Result<bool> IsUppercase(uchar c)
 922     {
 923         auto result = GetCharacterInfo(c);
 924         if (result.Error())
 925         {
 926             return Result<bool>(ErrorId(result.GetErrorId()));
 927         }
 928         const CharacterInfo* characterInfo = result.Value();
 929         return Result<bool>(characterInfo->GetBinaryProperty(BinaryPropertyId.uppercase));
 930     }
 931 
 932     [nodiscard]
 933     public Result<bool> IsLowercase(uchar c)
 934     {
 935         auto result = GetCharacterInfo(c);
 936         if (result.Error())
 937         {
 938             return Result<bool>(ErrorId(result.GetErrorId()));
 939         }
 940         const CharacterInfo* characterInfo = result.Value();
 941         return Result<bool>(characterInfo->GetBinaryProperty(BinaryPropertyId.lowercase));
 942     }
 943 
 944     [nodiscard]
 945     public Result<bool> IsIdStart(uchar c)
 946     {
 947         auto result = GetCharacterInfo(c);
 948         if (result.Error())
 949         {
 950             return Result<bool>(ErrorId(result.GetErrorId()));
 951         }
 952         const CharacterInfo* characterInfo = result.Value();
 953         return Result<bool>(characterInfo->GetBinaryProperty(BinaryPropertyId.idStart));
 954     }
 955 
 956     [nodiscard]
 957     public Result<bool> IsIdCont(uchar c)
 958     {
 959         auto result = GetCharacterInfo(c);
 960         if (result.Error())
 961         {
 962             return Result<bool>(ErrorId(result.GetErrorId()));
 963         }
 964         const CharacterInfo* characterInfo = result.Value();
 965         return Result<bool>(characterInfo->GetBinaryProperty(BinaryPropertyId.idContinue));
 966     }
 967 
 968     [nodiscard]
 969     public Result<bool> IsGraphemeBase(uchar c)
 970     {
 971         auto result = GetCharacterInfo(c);
 972         if (result.Error())
 973         {
 974             return Result<bool>(ErrorId(result.GetErrorId()));
 975         }
 976         const CharacterInfo* characterInfo = result.Value();
 977         return Result<bool>(characterInfo->GetBinaryProperty(BinaryPropertyId.graphemeBase));
 978     }
 979 
 980     [nodiscard]
 981     public Result<bool> IsGraphemeExtender(uchar c)
 982     {
 983         auto result = GetCharacterInfo(c);
 984         if (result.Error())
 985         {
 986             return Result<bool>(ErrorId(result.GetErrorId()));
 987         }
 988         const CharacterInfo* characterInfo = result.Value();
 989         return Result<bool>(characterInfo->GetBinaryProperty(BinaryPropertyId.graphemeExtend));
 990     }
 991 
 992     [nodiscard]
 993     public Result<bool> IsOtherLower(uchar c)
 994     {
 995         auto result = GetCharacterInfo(c);
 996         if (result.Error())
 997         {
 998             return Result<bool>(ErrorId(result.GetErrorId()));
 999         }
1000         const CharacterInfo* characterInfo = result.Value();
1001         return Result<bool>(characterInfo->GetBinaryProperty(BinaryPropertyId.otherLowercase));
1002     }
1003 
1004     [nodiscard]
1005     public Result<bool> IsOtherUpper(uchar c)
1006     {
1007         auto result = GetCharacterInfo(c);
1008         if (result.Error())
1009         {
1010             return Result<bool>(ErrorId(result.GetErrorId()));
1011         }
1012         const CharacterInfo* characterInfo = result.Value();
1013         return Result<bool>(characterInfo->GetBinaryProperty(BinaryPropertyId.otherUppercase));
1014     }
1015 
1016     [nodiscard]
1017     public Result<string> GetCharacterName(uchar c)
1018     {
1019         auto result = GetExtendedCharacterInfo(c);
1020         if (result.Error())
1021         {
1022             return Result<string>(ErrorId(result.GetErrorId()));
1023         }
1024         const ExtendedCharacterInfo* extendedCharacterInfo = result.Value();
1025         return Result<string>(extendedCharacterInfo->CharacterName());
1026     }
1027 
1028     [nodiscard]
1029     public Result<string> GetUnicode1Name(uchar c)
1030     {
1031         auto result = GetExtendedCharacterInfo(c);
1032         if (result.Error())
1033         {
1034             return Result<string>(ErrorId(result.GetErrorId()));
1035         }
1036         const ExtendedCharacterInfo* extendedCharacterInfo = result.Value();
1037         return Result<string>(extendedCharacterInfo->Unicode1Name());
1038     }
1039 
1040     [nodiscard]
1041     public Result<NumericTypeId> GetNumericType(uchar c)
1042     {
1043         auto result = GetExtendedCharacterInfo(c);
1044         if (result.Error())
1045         {
1046             return Result<NumericTypeId>(ErrorId(result.GetErrorId()));
1047         }
1048         const ExtendedCharacterInfo* extendedCharacterInfo = result.Value();
1049         return Result<NumericTypeId>(extendedCharacterInfo->NumericTypeValue());
1050     }
1051 
1052     [nodiscard]
1053     public Result<ustring> GetNumericValue(uchar c)
1054     {
1055         auto result = GetExtendedCharacterInfo(c);
1056         if (result.Error())
1057         {
1058             return Result<ustring>(ErrorId(result.GetErrorId()));
1059         }
1060         const ExtendedCharacterInfo* extendedCharacterInfo = result.Value();
1061         return Result<ustring>(extendedCharacterInfo->NumericValue());
1062     }
1063 
1064     [nodiscard]
1065     public Result<bool> IsBidiMirrored(uchar c)
1066     {
1067         auto result = GetCharacterInfo(c);
1068         if (result.Error())
1069         {
1070             return Result<bool>(ErrorId(result.GetErrorId()));
1071         }
1072         const CharacterInfo* characterInfo = result.Value();
1073         return Result<bool>(characterInfo->GetBinaryProperty(BinaryPropertyId.bidiMirrored));
1074     }
1075 
1076     [nodiscard]
1077     public Result<bool> IsBidiControl(uchar c)
1078     {
1079         auto result = GetCharacterInfo(c);
1080         if (result.Error())
1081         {
1082             return Result<bool>(ErrorId(result.GetErrorId()));
1083         }
1084         const CharacterInfo* characterInfo = result.Value();
1085         return Result<bool>(characterInfo->GetBinaryProperty(BinaryPropertyId.bidiControl));
1086     }
1087 
1088     [nodiscard]
1089     public Result<uchar> GetBidiMirroringGlyph(uchar c)
1090     {
1091         auto result = GetExtendedCharacterInfo(c);
1092         if (result.Error())
1093         {
1094             return Result<uchar>(ErrorId(result.GetErrorId()));
1095         }
1096         const ExtendedCharacterInfo* extendedCharacterInfo = result.Value();
1097         return Result<uchar>(extendedCharacterInfo->BidiMirroringGlyph());
1098     }
1099 
1100     [nodiscard]
1101     public Result<BidiPairedBracketTypeId> GetBidiPairedBracketType(uchar c)
1102     {
1103         auto result = GetExtendedCharacterInfo(c);
1104         if (result.Error())
1105         {
1106             return Result<BidiPairedBracketTypeId>(ErrorId(result.GetErrorId()));
1107         }
1108         const ExtendedCharacterInfo* extendedCharacterInfo = result.Value();
1109         return Result<BidiPairedBracketTypeId>(extendedCharacterInfo->BidiPairedBracketTypeValue());
1110     }
1111 
1112     [nodiscard]
1113     public Result<uchar> GetBidiPairedBracket(uchar c)
1114     {
1115         auto result = GetExtendedCharacterInfo(c);
1116         if (result.Error())
1117         {
1118             return Result<uchar>(ErrorId(result.GetErrorId()));
1119         }
1120         const ExtendedCharacterInfo* extendedCharacterInfo = result.Value();
1121         return Result<uchar>(extendedCharacterInfo->BidiPairedBracket());
1122     }
1123 
1124     [nodiscard]
1125     public Result<List<Alias>> Aliases(uchar c)
1126     {
1127         auto result = GetExtendedCharacterInfo(c);
1128         if (result.Error())
1129         {
1130             return Result<List<Alias>>(ErrorId(result.GetErrorId()));
1131         }
1132         const ExtendedCharacterInfo* extendedCharacterInfo = result.Value();
1133         return Result<List<Alias>>(extendedCharacterInfo->Aliases());
1134     }