Skip to content

Commit b03681a

Browse files
committed
Programatically generate partial final fuzzy entries / inner segement.
This would introduce quite some new entries: e.g. we -> wei, wen, weng The rule to partial final entries: it's not valid, if final of partial final is longer than 2, its (length-1) prefix also need to be invalid pinyin: e.g. sua -> su is valid, so not added to partial final. e.g. ton -> to is invalid, so ton added to partial final. With that we may add more inner fuzzy, so simply generate inner fuzzy programatically. Fix #136
1 parent c993d1a commit b03681a

3 files changed

Lines changed: 153 additions & 24 deletions

File tree

src/libime/pinyin/pinyindata.cpp

Lines changed: 133 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@
66

77
#include "pinyindata.h"
88
#include <algorithm>
9+
#include <array>
10+
#include <cassert>
11+
#include <cstddef>
912
#include <cstdint>
1013
#include <iterator>
1114
#include <optional>
15+
#include <ranges>
1216
#include <string>
1317
#include <unordered_map>
1418
#include <unordered_set>
@@ -21,6 +25,95 @@
2125

2226
namespace libime {
2327

28+
namespace {
29+
30+
void applyPartialFinalFuzzy(PinyinMap &map) {
31+
// There are some finals won't generate any entries, just skip them from
32+
// map.
33+
const auto partialFinalFuzzyMap =
34+
std::to_array<std::pair<std::string, std::vector<PinyinFinal>>>({
35+
{"a",
36+
{PinyinFinal::AN, PinyinFinal::AI, PinyinFinal::AO,
37+
PinyinFinal::ANG}},
38+
{"e",
39+
{PinyinFinal::EI, PinyinFinal::EN, PinyinFinal::ENG,
40+
PinyinFinal::ER}},
41+
{"i",
42+
{PinyinFinal::IA, PinyinFinal::IAN, PinyinFinal::IAO,
43+
PinyinFinal::IANG, PinyinFinal::IE, PinyinFinal::IN,
44+
PinyinFinal::ING, PinyinFinal::IONG, PinyinFinal::IU}},
45+
{"ia", {PinyinFinal::IAN, PinyinFinal::IAO, PinyinFinal::IANG}},
46+
{"ion", {PinyinFinal::IONG}},
47+
{"o", {PinyinFinal::ONG, PinyinFinal::OU}},
48+
{"on", {PinyinFinal::ONG}},
49+
50+
#if 0
51+
// Item won't generate anything below.
52+
{"an", {PinyinFinal::ANG}},
53+
{"en", {PinyinFinal::ENG}},
54+
{"ian", {PinyinFinal::IANG}},
55+
{"in", {PinyinFinal::ING}},
56+
{"io", {PinyinFinal::IONG}},
57+
58+
{"u",
59+
{PinyinFinal::UA, PinyinFinal::UAI, PinyinFinal::UAN,
60+
PinyinFinal::UANG, PinyinFinal::UE, PinyinFinal::UI,
61+
PinyinFinal::UN, PinyinFinal::UO}},
62+
{"ua", {PinyinFinal::UAI, PinyinFinal::UAN, PinyinFinal::UANG}},
63+
{"uan", {PinyinFinal::UANG}},
64+
{"v", {PinyinFinal::VE}},
65+
#endif
66+
});
67+
std::vector<PinyinEntry> newEntries;
68+
std::map<std::string, int> statistic;
69+
for (const auto initial : std::views::iota(PinyinEncoder::firstInitial,
70+
PinyinEncoder::lastInitial) |
71+
std::views::transform([](auto i) {
72+
return static_cast<PinyinInitial>(i);
73+
})) {
74+
if (initial == PinyinInitial::Zero) {
75+
continue;
76+
}
77+
for (const auto &[str, finals] : partialFinalFuzzyMap) {
78+
assert(std::ranges::all_of(finals, [&str](auto f) {
79+
const auto &finalString = PinyinEncoder::finalToString(f);
80+
return finalString.starts_with(str) &&
81+
finalString.size() > str.size();
82+
}));
83+
const auto &initialString = PinyinEncoder::initialToString(initial);
84+
const auto partialFinalString = initialString + str;
85+
bool ignore = false;
86+
for (size_t i = std::max<size_t>(initialString.size() + 1,
87+
partialFinalString.size() - 1);
88+
i <= partialFinalString.size(); i++) {
89+
if (map.contains(partialFinalString.substr(0, i))) {
90+
ignore = true;
91+
break;
92+
}
93+
}
94+
if (ignore) {
95+
continue;
96+
}
97+
for (auto final : finals) {
98+
auto pinyin =
99+
PinyinEncoder::initialFinalToPinyinString(initial, final);
100+
if (auto iter = map.find(pinyin);
101+
iter != map.end() &&
102+
iter->flags() == PinyinFuzzyFlag::None) {
103+
newEntries.push_back(
104+
PinyinEntry(partialFinalString.data(), initial, final,
105+
PinyinFuzzyFlag::PartialFinal));
106+
}
107+
}
108+
}
109+
}
110+
for (const auto &newEntry : newEntries) {
111+
FCITX_ASSERT(map.insert(newEntry).second);
112+
}
113+
}
114+
115+
} // namespace
116+
24117
const std::vector<bool> &getEncodedInitialFinal() {
25118
static const auto encodedInitialFinal = []() {
26119
std::vector<bool> a;
@@ -164,32 +257,46 @@ getInnerSegment() {
164257

165258
const InnerSegmentMap &getInnerSegmentV2() {
166259
static const InnerSegmentMap innerSegment = []() {
167-
InnerSegmentMap innerSegmentV2;
168-
for (const auto &[key, value] : getInnerSegment()) {
169-
innerSegmentV2[key].push_back(value);
170-
}
260+
InnerSegmentMap innerSegmentV2Generate;
261+
const auto &pinyinMap = getPinyinMapV2();
262+
for (const auto &entry : getPinyinMapV2()) {
263+
if (entry.pinyin().size() < 3) {
264+
continue;
265+
}
266+
if (innerSegmentV2Generate.contains(entry.pinyin())) {
267+
continue;
268+
}
269+
// We want to support 2 + 1, 2 + 2, 3 + 1, 2 + 3, 3 + 2, 3 + 3.
270+
for (size_t i = 2; i <= 3 && i < entry.pinyin().size(); i++) {
271+
if (entry.pinyin().size() <= i) {
272+
continue;
273+
}
274+
auto part1 = entry.pinyinView().substr(0, i);
275+
auto part2 = entry.pinyinView().substr(i);
276+
if (part2 == "ng") {
277+
continue;
278+
}
279+
auto range1 = pinyinMap.equal_range(part1);
280+
if (!std::any_of(
281+
range1.first, range1.second, [&](const auto &entry) {
282+
return entry.flags() == PinyinFuzzyFlag::None;
283+
})) {
284+
continue;
285+
}
286+
auto range2 = pinyinMap.equal_range(part2);
287+
if (!std::any_of(
288+
range2.first, range2.second, [&](const auto &entry) {
289+
return entry.flags() == PinyinFuzzyFlag::None;
290+
})) {
291+
continue;
292+
}
171293

172-
for (const auto &newItem : std::vector<
173-
std::pair<std::string, std::pair<std::string, std::string>>>{
174-
{"qiao", {"qia", "o"}},
175-
{"niao", {"nia", "o"}},
176-
{"liao", {"lia", "o"}},
177-
{"zhuo", {"zhu", "o"}},
178-
{"diao", {"dia", "o"}},
179-
{"shao", {"sha", "o"}},
180-
{"xiao", {"xia", "o"}},
181-
{"zhua", {"zhu", "a"}},
182-
{"shuo", {"shu", "o"}},
183-
{"shua", {"shu", "a"}},
184-
{"zhao", {"zha", "o"}},
185-
{"jiao", {"jia", "o"}},
186-
{"chuo", {"chu", "o"}},
187-
{"chua", {"chu", "a"}},
188-
{"chao", {"cha", "o"}},
189-
}) {
190-
innerSegmentV2[newItem.first].push_back(newItem.second);
294+
innerSegmentV2Generate[entry.pinyin()].push_back(
295+
{std::string(part1), std::string(part2)});
296+
}
191297
}
192-
return innerSegmentV2;
298+
299+
return innerSegmentV2Generate;
193300
}();
194301

195302
return innerSegment;
@@ -1417,6 +1524,8 @@ const PinyinMap &getPinyinMapV2() {
14171524
}
14181525
}
14191526

1527+
applyPartialFinalFuzzy(filtered);
1528+
14201529
for (auto fz : {PinyinFuzzyFlag::U_OU, PinyinFuzzyFlag::IN_ING,
14211530
PinyinFuzzyFlag::EN_ENG, PinyinFuzzyFlag::AN_ANG,
14221531
PinyinFuzzyFlag::UAN_UANG, PinyinFuzzyFlag::IAN_IANG,

src/libime/pinyin/pinyinencoder.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,14 @@ PinyinEncoder::parseUserPinyin(std::string userPinyin,
322322
auto iter = innerSegments.find(nextPinyin);
323323
if (iter != innerSegments.end()) {
324324
for (const auto &innerSeg : iter->second) {
325+
if (innerSeg.second == "n") {
326+
bool accept =
327+
(top + nextSize[i] < pinyin.size()) ||
328+
(i == 0 && nNextSize == 1);
329+
if (!accept) {
330+
continue;
331+
}
332+
}
325333
result.addNext(top,
326334
top + innerSeg.first.size());
327335
result.addNext(top + innerSeg.first.size(),

test/testpinyinencoder.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,18 @@ int main() {
279279
graph = PinyinEncoder::parseUserPinyin("suang", &profile,
280280
PinyinFuzzyFlag::Correction);
281281
dfs(graph, {"suan", "g"});
282+
283+
graph = PinyinEncoder::parseUserPinyin(
284+
"to", &profile,
285+
{PinyinFuzzyFlag::PartialFinal, PinyinFuzzyFlag::InnerShort});
286+
dfs(graph, {"to"});
287+
graph = PinyinEncoder::parseUserPinyin(
288+
"ton", &profile,
289+
{PinyinFuzzyFlag::PartialFinal, PinyinFuzzyFlag::InnerShort});
290+
dfs(graph, {"ton"});
291+
graph = PinyinEncoder::parseUserPinyin("to", &profile,
292+
PinyinFuzzyFlag::InnerShort);
293+
dfs(graph, {"t", "o"});
282294
}
283295

284296
{

0 commit comments

Comments
 (0)