feat: initial commit
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
using MaigoLabs.NeedLe.Common.Extensions;
|
||||
using OpenccNetLib;
|
||||
|
||||
namespace MaigoLabs.NeedLe.Indexer.Han;
|
||||
|
||||
public class HanVariantProvider
|
||||
{
|
||||
private readonly Dictionary<int, int[]> EXCHANGE_MAP;
|
||||
public HanVariantProvider(DictWithMaxLength[]? dicts = null)
|
||||
{
|
||||
dicts ??=
|
||||
[
|
||||
DictionaryLib.Provider.hk_variants,
|
||||
DictionaryLib.Provider.hk_variants_rev,
|
||||
DictionaryLib.Provider.jp_variants,
|
||||
DictionaryLib.Provider.jp_variants_rev,
|
||||
DictionaryLib.Provider.st_characters,
|
||||
DictionaryLib.Provider.ts_characters,
|
||||
DictionaryLib.Provider.tw_variants,
|
||||
DictionaryLib.Provider.tw_variants_rev,
|
||||
];
|
||||
EXCHANGE_MAP = BuildHanExchangeMap(dicts);
|
||||
}
|
||||
|
||||
private Dictionary<int, int[]> BuildHanExchangeMap(DictWithMaxLength[] dicts)
|
||||
{
|
||||
var unionFindSet = new UnionFindSet();
|
||||
foreach (var dict in dicts) foreach (var item in dict.Dict)
|
||||
{
|
||||
var from = item.Key.ToCodePoints().ToArray();
|
||||
var to = item.Value.ToCodePoints().ToArray();
|
||||
if (from.Length != 1 || to.Length != 1) continue;
|
||||
unionFindSet.Union(from[0], to[0]);
|
||||
}
|
||||
var variants = new Dictionary<int, List<int>>();
|
||||
foreach (var x in unionFindSet.Keys)
|
||||
{
|
||||
var parent = unionFindSet.Find(x);
|
||||
if (!variants.TryGetValue(parent, out var list)) variants[parent] = list = [];
|
||||
if (x != parent) variants[x] = list;
|
||||
list.Add(x);
|
||||
}
|
||||
return variants.ToDictionary(item => item.Key, item => item.Value.OrderBy(x => x).ToArray());
|
||||
}
|
||||
|
||||
// https://github.com/google/re2/blob/e7aec5985072c1dbe735add802653ef4b36c231a/re2/unicode_groups.cc#L5590-L5615
|
||||
private static readonly (int Min, int Max)[] RE2_SCRIPT_HAN_RENAGES =
|
||||
[
|
||||
// Han_range16
|
||||
(11904, 11929),
|
||||
(11931, 12019),
|
||||
(12032, 12245),
|
||||
(12293, 12293),
|
||||
(12295, 12295),
|
||||
(12321, 12329),
|
||||
(12344, 12347),
|
||||
(13312, 19903),
|
||||
(19968, 40959),
|
||||
(63744, 64109),
|
||||
(64112, 64217),
|
||||
// Han_range32
|
||||
(94178, 94179),
|
||||
(94192, 94193),
|
||||
(131072, 173791),
|
||||
(173824, 177977),
|
||||
(177984, 178205),
|
||||
(178208, 183969),
|
||||
(183984, 191456),
|
||||
(191472, 192093),
|
||||
(194560, 195101),
|
||||
(196608, 201546),
|
||||
(201552, 205743),
|
||||
];
|
||||
|
||||
public static bool IsHanCharacter(int codePoint) => RE2_SCRIPT_HAN_RENAGES.Any(range => codePoint >= range.Min && codePoint <= range.Max);
|
||||
|
||||
public int[] GetHanVariants(int codePoint) => EXCHANGE_MAP.TryGetValue(codePoint, out var variants)
|
||||
? variants
|
||||
: IsHanCharacter(codePoint) ? [codePoint] : [];
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using hyjiacan.py4n;
|
||||
|
||||
namespace MaigoLabs.NeedLe.Indexer.Han;
|
||||
|
||||
public static class PinyinHelper
|
||||
{
|
||||
private static readonly string[] PINYIN_INITIALS = ["b", "p", "m", "f", "d", "t", "n", "l", "g", "k", "h", "j", "q", "x", "zh", "ch", "sh", "r", "z", "c", "s", "y", "w"];
|
||||
private static readonly Dictionary<string, string> PINYIN_FINALS_FUZZY_MAP = new() { ["ang"] = "an", ["eng"] = "en", ["ing"] = "in" };
|
||||
|
||||
public static IEnumerable<string> GetPinyinCandidates(int codePoint) => codePoint < char.MinValue || codePoint > char.MaxValue || !PinyinUtil.IsHanzi((char)codePoint) ? [] :
|
||||
Pinyin4Net.GetPinyin((char)codePoint, PinyinFormat.LOWERCASE | PinyinFormat.WITHOUT_TONE).Where(pinyin => pinyin.Length > 0).SelectMany(pinyin =>
|
||||
{
|
||||
var initial = PINYIN_INITIALS.FirstOrDefault(initial => pinyin.StartsWith(initial));
|
||||
var initialAlphabet = initial != null ? initial[..1] : pinyin[..1];
|
||||
var fuzzySuffix = pinyin.Length < 3 ? null : pinyin[^3..];
|
||||
var fuzzyPinyin = fuzzySuffix != null && PINYIN_FINALS_FUZZY_MAP.TryGetValue(fuzzySuffix, out var fuzzySuffixTarget) ? pinyin[..^3] + fuzzySuffixTarget : null;
|
||||
return new string?[] { pinyin, initial, initialAlphabet, fuzzyPinyin }.OfType<string>();
|
||||
}).Distinct();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
namespace MaigoLabs.NeedLe.Indexer.Han;
|
||||
|
||||
public class UnionFindSet
|
||||
{
|
||||
private Dictionary<int, int> Parent { get; set; } = [];
|
||||
private Dictionary<int, int> Rank { get; set; } = [];
|
||||
|
||||
public IEnumerable<int> Keys => Parent.Keys;
|
||||
|
||||
public int Find(int x)
|
||||
{
|
||||
if (!Parent.TryGetValue(x, out var parent)) return Parent[x] = x;
|
||||
else if (x == parent) return x;
|
||||
else return Parent[x] = Find(parent);
|
||||
}
|
||||
|
||||
public void Union(int x, int y)
|
||||
{
|
||||
x = Find(x);
|
||||
y = Find(y);
|
||||
if (x == y) return;
|
||||
int rankX = GetRank(x), rankY = GetRank(y);
|
||||
if (rankX < rankY) Parent[x] = y;
|
||||
else if (rankX > rankY) Parent[y] = x;
|
||||
else
|
||||
{
|
||||
Parent[y] = x;
|
||||
Rank[x] = rankX + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private int GetRank(int x) => !Rank.TryGetValue(x, out var rank) ? 0 : rank;
|
||||
}
|
||||
Reference in New Issue
Block a user