Megrez // Upgrade to v1.2.1 release.

This commit is contained in:
ShikiSuen 2022-06-15 22:28:12 +08:00
parent 4b83af377d
commit f4b164b05c
12 changed files with 109 additions and 129 deletions

View File

@ -65,8 +65,10 @@ class KeyHandler {
// Synchronize the sub-languageModel state settings to the new LM. // Synchronize the sub-languageModel state settings to the new LM.
syncBaseLMPrefs() syncBaseLMPrefs()
// Create new grid builder and clear the composer. // Create new compositor and clear the composer.
createNewBuilder() // When it recreates, it adapts to the latest imeMode settings.
// This allows it to work with correct LMs.
reinitCompositor()
composer.clear() composer.clear()
} }
} }
@ -139,7 +141,7 @@ class KeyHandler {
} }
func fixNode(value: String, respectCursorPushing: Bool = true) { func fixNode(value: String, respectCursorPushing: Bool = true) {
let cursorIndex = min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength) let cursorIndex = min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength)
compositor.grid.fixNodeSelectedCandidate(location: cursorIndex, value: value) compositor.grid.fixNodeSelectedCandidate(location: cursorIndex, value: value)
// // // //
// let selectedNode: Megrez.NodeAnchor = compositor.grid.fixNodeSelectedCandidate( // let selectedNode: Megrez.NodeAnchor = compositor.grid.fixNodeSelectedCandidate(
@ -180,8 +182,8 @@ class KeyHandler {
if nextPosition >= cursorIndex { break } if nextPosition >= cursorIndex { break }
nextPosition += node.spanningLength nextPosition += node.spanningLength
} }
if nextPosition <= builderLength { if nextPosition <= compositorLength {
builderCursorIndex = nextPosition compositorCursorIndex = nextPosition
} }
} }
} }
@ -216,7 +218,7 @@ class KeyHandler {
mgrPrefs.useSCPCTypingMode mgrPrefs.useSCPCTypingMode
? "" ? ""
: currentUOM.suggest( : currentUOM.suggest(
walkedNodes: walkedAnchors, cursorIndex: builderCursorIndex, walkedNodes: walkedAnchors, cursorIndex: compositorCursorIndex,
timestamp: NSDate().timeIntervalSince1970 timestamp: NSDate().timeIntervalSince1970
) )
@ -224,7 +226,7 @@ class KeyHandler {
IME.prtDebugIntel( IME.prtDebugIntel(
"UOM: Suggestion retrieved, overriding the node score of the selected candidate.") "UOM: Suggestion retrieved, overriding the node score of the selected candidate.")
compositor.grid.overrideNodeScoreForSelectedCandidate( compositor.grid.overrideNodeScoreForSelectedCandidate(
location: min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength), location: min(actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength),
value: overrideValue, value: overrideValue,
overridingScore: findHighestScore(nodes: rawNodes, epsilon: kEpsilon) overridingScore: findHighestScore(nodes: rawNodes, epsilon: kEpsilon)
) )
@ -285,7 +287,7 @@ class KeyHandler {
// MARK: - Extracted methods and functions (Megrez). // MARK: - Extracted methods and functions (Megrez).
var isBuilderEmpty: Bool { compositor.grid.width == 0 } var isCompositorEmpty: Bool { compositor.grid.width == 0 }
var rawNodes: [Megrez.NodeAnchor] { var rawNodes: [Megrez.NodeAnchor] {
/// 使 nodesCrossing macOS /// 使 nodesCrossing macOS
@ -301,7 +303,7 @@ class KeyHandler {
currentLM.isSymbolEnabled = mgrPrefs.symbolInputEnabled currentLM.isSymbolEnabled = mgrPrefs.symbolInputEnabled
} }
func createNewBuilder() { func reinitCompositor() {
// Each Mandarin syllable is separated by a hyphen. // Each Mandarin syllable is separated by a hyphen.
compositor = Megrez.Compositor(lm: currentLM, separator: "-") compositor = Megrez.Compositor(lm: currentLM, separator: "-")
} }
@ -312,16 +314,16 @@ class KeyHandler {
currentLM.hasUnigramsFor(key: reading) currentLM.hasUnigramsFor(key: reading)
} }
func insertReadingToBuilderAtCursor(reading: String) { func insertToCompositorAtCursor(reading: String) {
compositor.insertReadingAtCursor(reading: reading) compositor.insertReadingAtCursor(reading: reading)
} }
var builderCursorIndex: Int { var compositorCursorIndex: Int {
get { compositor.cursorIndex } get { compositor.cursorIndex }
set { compositor.cursorIndex = newValue } set { compositor.cursorIndex = newValue }
} }
var builderLength: Int { var compositorLength: Int {
compositor.length compositor.length
} }

View File

@ -52,12 +52,12 @@ extension KeyHandler {
if cancelCandidateKey { if cancelCandidateKey {
if (state is InputState.AssociatedPhrases) if (state is InputState.AssociatedPhrases)
|| mgrPrefs.useSCPCTypingMode || mgrPrefs.useSCPCTypingMode
|| isBuilderEmpty || isCompositorEmpty
{ {
// //
// //
// 使 BackSpace // 使 BackSpace
// isBuilderEmpty // isCompositorEmpty
clear() clear()
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(InputState.EmptyIgnoringPreviousState())
} else { } else {

View File

@ -151,7 +151,7 @@ extension KeyHandler {
keyConsumedByReading = true keyConsumedByReading = true
// If we have a tone marker, we have to insert the reading to the // If we have a tone marker, we have to insert the reading to the
// builder in other words, if we don't have a tone marker, we just // compositor in other words, if we don't have a tone marker, we just
// update the composing buffer. // update the composing buffer.
let composeReading = composer.hasToneMarker() let composeReading = composer.hasToneMarker()
if !composeReading { if !composeReading {
@ -177,12 +177,12 @@ extension KeyHandler {
IME.prtDebugIntel("B49C0979語彙庫內無「\(reading)」的匹配記錄。") IME.prtDebugIntel("B49C0979語彙庫內無「\(reading)」的匹配記錄。")
errorCallback() errorCallback()
composer.clear() composer.clear()
stateCallback((builderLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState) stateCallback((compositorLength == 0) ? InputState.EmptyIgnoringPreviousState() : buildInputtingState)
return true return true
} }
// ... and insert it into the grid... // ... and insert it into the grid...
insertReadingToBuilderAtCursor(reading: reading) insertToCompositorAtCursor(reading: reading)
// ... then walk the grid... // ... then walk the grid...
let poppedText = popOverflowComposingTextAndWalk let poppedText = popOverflowComposingTextAndWalk
@ -248,7 +248,7 @@ extension KeyHandler {
if input.isSpace { if input.isSpace {
// If the Space key is NOT set to be a selection key // If the Space key is NOT set to be a selection key
if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace { if input.isShiftHold || !mgrPrefs.chooseCandidateUsingSpace {
if builderCursorIndex >= builderLength { if compositorCursorIndex >= compositorLength {
let composingBuffer = currentState.composingBuffer let composingBuffer = currentState.composingBuffer
if !composingBuffer.isEmpty { if !composingBuffer.isEmpty {
stateCallback(InputState.Committing(poppedText: composingBuffer)) stateCallback(InputState.Committing(poppedText: composingBuffer))
@ -257,7 +257,7 @@ extension KeyHandler {
stateCallback(InputState.Committing(poppedText: " ")) stateCallback(InputState.Committing(poppedText: " "))
stateCallback(InputState.Empty()) stateCallback(InputState.Empty())
} else if ifLangModelHasUnigrams(forKey: " ") { } else if ifLangModelHasUnigrams(forKey: " ") {
insertReadingToBuilderAtCursor(reading: " ") insertToCompositorAtCursor(reading: " ")
let poppedText = popOverflowComposingTextAndWalk let poppedText = popOverflowComposingTextAndWalk
let inputting = buildInputtingState let inputting = buildInputtingState
inputting.poppedText = poppedText inputting.poppedText = poppedText
@ -363,7 +363,7 @@ extension KeyHandler {
if input.isOptionHold { if input.isOptionHold {
if ifLangModelHasUnigrams(forKey: "_punctuation_list") { if ifLangModelHasUnigrams(forKey: "_punctuation_list") {
if composer.isEmpty { if composer.isEmpty {
insertReadingToBuilderAtCursor(reading: "_punctuation_list") insertToCompositorAtCursor(reading: "_punctuation_list")
let poppedText: String! = popOverflowComposingTextAndWalk let poppedText: String! = popOverflowComposingTextAndWalk
let inputting = buildInputtingState let inputting = buildInputtingState
inputting.poppedText = poppedText inputting.poppedText = poppedText

View File

@ -34,7 +34,7 @@ extension KeyHandler {
} }
var actualCandidateCursorIndex: Int { var actualCandidateCursorIndex: Int {
var cursorIndex = builderCursorIndex var cursorIndex = compositorCursorIndex
switch mgrPrefs.useRearCursorMode { switch mgrPrefs.useRearCursorMode {
case false: case false:
do { do {
@ -52,7 +52,7 @@ extension KeyHandler {
// (i.e. the cursor is always at the rear of the phrase.) // (i.e. the cursor is always at the rear of the phrase.)
// No crossing. // No crossing.
switch cursorIndex { switch cursorIndex {
case builderLength: cursorIndex -= 1 case compositorLength: cursorIndex -= 1
default: break default: break
} }
} }

View File

@ -48,44 +48,44 @@ extension KeyHandler {
let arrSplit: [String] = Array(strNodeValue).map { String($0) } let arrSplit: [String] = Array(strNodeValue).map { String($0) }
let codepointCount = arrSplit.count let codepointCount = arrSplit.count
// This re-aligns the cursor index in the composed string // This re-aligns the cursor index in the composed string
// (the actual cursor on the screen) with the builder's logical // (the actual cursor on the screen) with the compositor's logical
// cursor (reading) cursor; each built node has a "spanning length" // cursor (reading) cursor; each built node has a "spanning length"
// (e.g. two reading blocks has a spanning length of 2), and we // (e.g. two reading blocks has a spanning length of 2), and we
// accumulate those lengths to calculate the displayed cursor // accumulate those lengths to calculate the displayed cursor
// index. // index.
let spanningLength: Int = walkedNode.spanningLength let spanningLength: Int = walkedNode.spanningLength
if readingCursorIndex + spanningLength <= builderCursorIndex { if readingCursorIndex + spanningLength <= compositorCursorIndex {
composedStringCursorIndex += strNodeValue.utf16.count composedStringCursorIndex += strNodeValue.utf16.count
readingCursorIndex += spanningLength readingCursorIndex += spanningLength
} else { } else {
if codepointCount == spanningLength { if codepointCount == spanningLength {
var i = 0 var i = 0
while i < codepointCount, readingCursorIndex < builderCursorIndex { while i < codepointCount, readingCursorIndex < compositorCursorIndex {
composedStringCursorIndex += arrSplit[i].utf16.count composedStringCursorIndex += arrSplit[i].utf16.count
readingCursorIndex += 1 readingCursorIndex += 1
i += 1 i += 1
} }
} else { } else {
if readingCursorIndex < builderCursorIndex { if readingCursorIndex < compositorCursorIndex {
composedStringCursorIndex += strNodeValue.utf16.count composedStringCursorIndex += strNodeValue.utf16.count
readingCursorIndex += spanningLength readingCursorIndex += spanningLength
if readingCursorIndex > builderCursorIndex { if readingCursorIndex > compositorCursorIndex {
readingCursorIndex = builderCursorIndex readingCursorIndex = compositorCursorIndex
} }
// Now we start preparing the contents of the tooltips used // Now we start preparing the contents of the tooltips used
// in cases of moving cursors across certain emojis which emoji // in cases of moving cursors across certain emojis which emoji
// char count is inequal to the reading count. // char count is inequal to the reading count.
// Example in McBopomofo: Typing (3 readings) gets a tree emoji. // Example in McBopomofo: Typing (3 readings) gets a tree emoji.
// Example in vChewing: Typing (2 readings) gets a pasta emoji. // Example in vChewing: Typing (2 readings) gets a pasta emoji.
switch builderCursorIndex { switch compositorCursorIndex {
case compositor.readings.count...: case compositor.readings.count...:
tooltipParameterRef[0] = compositor.readings[compositor.readings.count - 1] tooltipParameterRef[0] = compositor.readings[compositor.readings.count - 1]
case 0: case 0:
tooltipParameterRef[1] = compositor.readings[builderCursorIndex] tooltipParameterRef[1] = compositor.readings[compositorCursorIndex]
default: default:
do { do {
tooltipParameterRef[0] = compositor.readings[builderCursorIndex - 1] tooltipParameterRef[0] = compositor.readings[compositorCursorIndex - 1]
tooltipParameterRef[1] = compositor.readings[builderCursorIndex] tooltipParameterRef[1] = compositor.readings[compositorCursorIndex]
} }
} }
} }
@ -260,7 +260,7 @@ extension KeyHandler {
} }
if composer.isEmpty { if composer.isEmpty {
insertReadingToBuilderAtCursor(reading: customPunctuation) insertToCompositorAtCursor(reading: customPunctuation)
let poppedText = popOverflowComposingTextAndWalk let poppedText = popOverflowComposingTextAndWalk
let inputting = buildInputtingState let inputting = buildInputtingState
inputting.poppedText = poppedText inputting.poppedText = poppedText
@ -385,7 +385,7 @@ extension KeyHandler {
if composer.hasToneMarker(withNothingElse: true) { if composer.hasToneMarker(withNothingElse: true) {
composer.clear() composer.clear()
} else if composer.isEmpty { } else if composer.isEmpty {
if builderCursorIndex >= 0 { if compositorCursorIndex >= 0 {
deleteBuilderReadingInFrontOfCursor() deleteBuilderReadingInFrontOfCursor()
walk() walk()
} else { } else {
@ -398,7 +398,7 @@ extension KeyHandler {
composer.doBackSpace() composer.doBackSpace()
} }
if composer.isEmpty, builderLength == 0 { if composer.isEmpty, compositorLength == 0 {
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(InputState.EmptyIgnoringPreviousState())
} else { } else {
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
@ -416,7 +416,7 @@ extension KeyHandler {
guard state is InputState.Inputting else { return false } guard state is InputState.Inputting else { return false }
if composer.isEmpty { if composer.isEmpty {
if builderCursorIndex != builderLength { if compositorCursorIndex != compositorLength {
deleteBuilderReadingToTheFrontOfCursor() deleteBuilderReadingToTheFrontOfCursor()
walk() walk()
let inputting = buildInputtingState let inputting = buildInputtingState
@ -472,8 +472,8 @@ extension KeyHandler {
return true return true
} }
if builderCursorIndex != 0 { if compositorCursorIndex != 0 {
builderCursorIndex = 0 compositorCursorIndex = 0
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
} else { } else {
IME.prtDebugIntel("66D97F90") IME.prtDebugIntel("66D97F90")
@ -500,8 +500,8 @@ extension KeyHandler {
return true return true
} }
if builderCursorIndex != builderLength { if compositorCursorIndex != compositorLength {
builderCursorIndex = builderLength compositorCursorIndex = compositorLength
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
} else { } else {
IME.prtDebugIntel("9B69908E") IME.prtDebugIntel("9B69908E")
@ -534,7 +534,7 @@ extension KeyHandler {
// If reading is not empty, we cancel the reading. // If reading is not empty, we cancel the reading.
if !composer.isEmpty { if !composer.isEmpty {
composer.clear() composer.clear()
if builderLength == 0 { if compositorLength == 0 {
stateCallback(InputState.EmptyIgnoringPreviousState()) stateCallback(InputState.EmptyIgnoringPreviousState())
} else { } else {
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
@ -580,8 +580,8 @@ extension KeyHandler {
stateCallback(state) stateCallback(state)
} }
} else { } else {
if builderCursorIndex < builderLength { if compositorCursorIndex < compositorLength {
builderCursorIndex += 1 compositorCursorIndex += 1
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
} else { } else {
IME.prtDebugIntel("A96AAD58") IME.prtDebugIntel("A96AAD58")
@ -629,8 +629,8 @@ extension KeyHandler {
stateCallback(state) stateCallback(state)
} }
} else { } else {
if builderCursorIndex > 0 { if compositorCursorIndex > 0 {
builderCursorIndex -= 1 compositorCursorIndex -= 1
stateCallback(buildInputtingState) stateCallback(buildInputtingState)
} else { } else {
IME.prtDebugIntel("7045E6F3") IME.prtDebugIntel("7045E6F3")
@ -677,7 +677,7 @@ extension KeyHandler {
var length = 0 var length = 0
var currentAnchor = Megrez.NodeAnchor() var currentAnchor = Megrez.NodeAnchor()
let cursorIndex = min( let cursorIndex = min(
actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), builderLength actualCandidateCursorIndex + (mgrPrefs.useRearCursorMode ? 1 : 0), compositorLength
) )
for anchor in walkedAnchors { for anchor in walkedAnchors {
length += anchor.spanningLength length += anchor.spanningLength

View File

@ -78,10 +78,10 @@ private let kMinCandidateListTextSize: CGFloat = 12
private let kMaxCandidateListTextSize: CGFloat = 196 private let kMaxCandidateListTextSize: CGFloat = 196
// default, min and max composing buffer size (in codepoints) // default, min and max composing buffer size (in codepoints)
// modern Macs can usually work up to 16 codepoints when the builder still // modern Macs can usually work up to 16 codepoints when the compositor still
// walks the grid with good performance slower Macs (like old PowerBooks) // walks the grid with good performance slower Macs (like old PowerBooks)
// will start to sputter beyond 12 such is the algorithmatic complexity // will start to sputter beyond 12 such is the algorithmatic complexity
// of the Viterbi algorithm used in the builder library (at O(N^2)) // of the Viterbi algorithm used in the Megrez library (at O(N^2))
private let kDefaultComposingBufferSize = 20 private let kDefaultComposingBufferSize = 20
private let kMinComposingBufferSize = 10 private let kMinComposingBufferSize = 10
private let kMaxComposingBufferSize = 40 private let kMaxComposingBufferSize = 40

View File

@ -42,8 +42,8 @@ extension vChewing {
/// LMInstantiator is a facade for managing a set of models including /// LMInstantiator is a facade for managing a set of models including
/// the input method language model, user phrases and excluded phrases. /// the input method language model, user phrases and excluded phrases.
/// ///
/// It is the primary model class that the input controller and grammar builder /// It is the primary model class that the input controller and input compositor
/// of vChewing talks to. When the grammar builder starts to build a sentence /// of vChewing talks to. When the input compositor starts to build a sentence
/// from a series of BPMF readings, it passes the readings to the model to see /// from a series of BPMF readings, it passes the readings to the model to see
/// if there are valid unigrams, and use returned unigrams to produce the final /// if there are valid unigrams, and use returned unigrams to produce the final
/// results. /// results.

View File

@ -24,40 +24,43 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
extension Megrez { extension Megrez {
/// ///
public class BlockReadingBuilder { public class Compositor {
/// ///
private let kDroppedPathScore: Double = -999 private let kDroppedPathScore: Double = -999
/// ///
private var mutCursorIndex: Int = 0 private var mutCursorIndex: Int = 0
/// ///
private var mutReadings: [String] = [] private var mutReadings: [String] = []
/// ///
private var mutGrid: Grid = .init() private var mutGrid: Grid = .init()
/// 使 /// 使
private var mutLM: LanguageModel private var mutLM: LanguageModel
/// ///
public var maxBuildSpanLength: Int { mutGrid.maxBuildSpanLength } public var maxBuildSpanLength: Int { mutGrid.maxBuildSpanLength }
/// ///
public var joinSeparator: String = "" public var joinSeparator: String = ""
/// ///
public var cursorIndex: Int { public var cursorIndex: Int {
get { mutCursorIndex } get { mutCursorIndex }
set { mutCursorIndex = (newValue < 0) ? 0 : min(newValue, mutReadings.count) } set { mutCursorIndex = (newValue < 0) ? 0 : min(newValue, mutReadings.count) }
} }
/// ///
public var isEmpty: Bool { grid.isEmpty }
///
public var grid: Grid { mutGrid } public var grid: Grid { mutGrid }
/// ///
public var length: Int { mutReadings.count } public var length: Int { mutReadings.count }
/// ///
public var readings: [String] { mutReadings } public var readings: [String] { mutReadings }
/// ///
/// - Parameters: /// - Parameters:
/// - lm: Megrez.LanguageModel /// - lm: Megrez.LanguageModel
/// - length: 10 /// - length: 10
/// - separator: /// - separator:
public init(lm: LanguageModel, length: Int = 10, separator: String = "") { public init(lm: LanguageModel, length: Int = 10, separator: String = "") {
mutLM = lm mutLM = lm
@ -65,7 +68,7 @@ extension Megrez {
joinSeparator = separator joinSeparator = separator
} }
/// ///
public func clear() { public func clear() {
mutCursorIndex = 0 mutCursorIndex = 0
mutReadings.removeAll() mutReadings.removeAll()
@ -109,7 +112,7 @@ extension Megrez {
return true return true
} }
/// ///
/// ///
/// ///
/// ///
@ -179,12 +182,14 @@ extension Megrez {
$0.scoreForSort > $1.scoreForSort $0.scoreForSort > $1.scoreForSort
} }
if let nodeOfNodeZero = nodes[0].node, nodeOfNodeZero.score >= nodeOfNodeZero.kSelectedCandidateScore { if let nodeZero = nodes[0].node, nodeZero.score >= nodeZero.kSelectedCandidateScore {
// 使 // 使
var nodeZero = nodes[0] var anchorZero = nodes[0]
nodeZero.accumulatedScore = accumulatedScore + nodeOfNodeZero.score anchorZero.accumulatedScore = accumulatedScore + nodeZero.score
var path: [NodeAnchor] = reverseWalk(at: location - nodeZero.spanningLength, score: nodeZero.accumulatedScore) var path: [NodeAnchor] = reverseWalk(
path.insert(nodeZero, at: 0) at: location - anchorZero.spanningLength, score: anchorZero.accumulatedScore
)
path.insert(anchorZero, at: 0)
paths.append(path) paths.append(path)
} else if !longPhrases.isEmpty { } else if !longPhrases.isEmpty {
var path = [NodeAnchor]() var path = [NodeAnchor]()
@ -202,17 +207,12 @@ extension Megrez {
continue continue
} }
theAnchor.accumulatedScore = accumulatedScore + theNode.score theAnchor.accumulatedScore = accumulatedScore + theNode.score
if joinedValue.count >= longPhrases[0].count { path = reverseWalk(
path = reverseWalk( at: location - theAnchor.spanningLength,
at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: "", score: theAnchor.accumulatedScore,
longPhrases: .init() joinedPhrase: (joinedValue.count >= longPhrases[0].count) ? "" : joinedValue,
) longPhrases: .init()
} else { )
path = reverseWalk(
at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: joinedValue,
longPhrases: longPhrases
)
}
path.insert(theAnchor, at: 0) path.insert(theAnchor, at: 0)
paths.append(path) paths.append(path)
} }
@ -234,17 +234,11 @@ extension Megrez {
guard let theNode = theAnchor.node else { continue } guard let theNode = theAnchor.node else { continue }
theAnchor.accumulatedScore = accumulatedScore + theNode.score theAnchor.accumulatedScore = accumulatedScore + theNode.score
var path = [NodeAnchor]() var path = [NodeAnchor]()
if theAnchor.spanningLength > 1 { path = reverseWalk(
path = reverseWalk( at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore,
at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore, joinedPhrase: "", joinedPhrase: (theAnchor.spanningLength > 1) ? "" : theNode.currentKeyValue.value,
longPhrases: .init() longPhrases: .init()
) )
} else {
path = reverseWalk(
at: location - theAnchor.spanningLength, score: theAnchor.accumulatedScore,
joinedPhrase: theNode.currentKeyValue.value, longPhrases: longPhrases
)
}
path.insert(theAnchor, at: 0) path.insert(theAnchor, at: 0)
paths.append(path) paths.append(path)
} }

View File

@ -38,6 +38,9 @@ extension Megrez {
/// ///
var width: Int { mutSpans.count } var width: Int { mutSpans.count }
///
var isEmpty: Bool { mutSpans.isEmpty }
public init(spanLength: Int = 10) { public init(spanLength: Int = 10) {
mutMaxBuildSpanLength = spanLength mutMaxBuildSpanLength = spanLength
mutSpans = [Megrez.Span]() mutSpans = [Megrez.Span]()
@ -256,9 +259,9 @@ extension Megrez.Grid {
strOutput += "\(np.currentKeyValue.value);\n" strOutput += "\(np.currentKeyValue.value);\n"
if (p + ni) < mutSpans.count { if (p + ni) < mutSpans.count {
let destinatedSpan = mutSpans[p + ni] let destinationSpan = mutSpans[p + ni]
for q in 0...(destinatedSpan.maximumLength) { for q in 0...(destinationSpan.maximumLength) {
if let dn = destinatedSpan.node(length: q) { if let dn = destinationSpan.node(length: q) {
strOutput += np.currentKeyValue.value + " -> " + dn.currentKeyValue.value + ";\n" strOutput += np.currentKeyValue.value + " -> " + dn.currentKeyValue.value + ";\n"
} }
} }

View File

@ -49,9 +49,7 @@ extension Megrez {
mutating func insert(node: Node, length: Int) { mutating func insert(node: Node, length: Int) {
let length = abs(length) // let length = abs(length) //
mutLengthNodeMap[length] = node mutLengthNodeMap[length] = node
if length > mutMaximumLength { mutMaximumLength = max(mutMaximumLength, length)
mutMaximumLength = length
}
} }
/// ///
@ -60,21 +58,19 @@ extension Megrez {
mutating func removeNodeOfLengthGreaterThan(_ length: Int) { mutating func removeNodeOfLengthGreaterThan(_ length: Int) {
let length = abs(length) // let length = abs(length) //
if length > mutMaximumLength { return } if length > mutMaximumLength { return }
var max = 0 var lenMax = 0
var removalList: [Int: Megrez.Node] = [:] var removalList: [Int: Megrez.Node] = [:]
for key in mutLengthNodeMap.keys { for key in mutLengthNodeMap.keys {
if key > length { if key > length {
removalList[key] = mutLengthNodeMap[key] removalList[key] = mutLengthNodeMap[key]
} else { } else {
if key > max { lenMax = max(lenMax, key)
max = key
}
} }
} }
for key in removalList.keys { for key in removalList.keys {
mutLengthNodeMap.removeValue(forKey: key) mutLengthNodeMap.removeValue(forKey: key)
} }
mutMaximumLength = max mutMaximumLength = lenMax
} }
/// ///

View File

@ -25,7 +25,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
extension Megrez { extension Megrez {
/// ///
public class Node: CustomStringConvertible { public class Node {
/// ///
private let mutLM: LanguageModel = .init() private let mutLM: LanguageModel = .init()
/// ///
@ -109,23 +109,16 @@ extension Megrez {
for neta in precedingKeyValues { for neta in precedingKeyValues {
let bigrams = mutPrecedingBigramMap[neta] ?? [] let bigrams = mutPrecedingBigramMap[neta] ?? []
for bigram in bigrams { for bigram in bigrams {
if bigram.score > max { guard bigram.score > max else { continue }
if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] { if let valRetrieved = mutValueUnigramIndexMap[bigram.keyValue.value] {
newIndex = valRetrieved as Int newIndex = valRetrieved as Int
max = bigram.score max = bigram.score
}
} }
} }
} }
} }
mutScore = max
if mutScore != max { mutSelectedUnigramIndex = newIndex
mutScore = max
}
if mutSelectedUnigramIndex != newIndex {
mutSelectedUnigramIndex = newIndex
}
} }
/// ///
@ -170,13 +163,5 @@ extension Megrez {
} }
return 0.0 return 0.0
} }
public static func == (lhs: Node, rhs: Node) -> Bool {
lhs.mutUnigrams == rhs.mutUnigrams && lhs.mutCandidates == rhs.mutCandidates
&& lhs.mutValueUnigramIndexMap == rhs.mutValueUnigramIndexMap
&& lhs.mutPrecedingBigramMap == rhs.mutPrecedingBigramMap
&& lhs.mutCandidateFixed == rhs.mutCandidateFixed
&& lhs.mutSelectedUnigramIndex == rhs.mutSelectedUnigramIndex
}
} }
} }

View File

@ -17,7 +17,7 @@
5B38F59D281E2E49007D5F5D /* 4_Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1A15FC0EB100ABF4B3 /* 4_Node.swift */; }; 5B38F59D281E2E49007D5F5D /* 4_Node.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1A15FC0EB100ABF4B3 /* 4_Node.swift */; };
5B38F59E281E2E49007D5F5D /* 6_Bigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */; }; 5B38F59E281E2E49007D5F5D /* 6_Bigram.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */; };
5B38F59F281E2E49007D5F5D /* 3_NodeAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */; }; 5B38F59F281E2E49007D5F5D /* 3_NodeAnchor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */; };
5B38F5A1281E2E49007D5F5D /* 1_BlockReadingBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */; }; 5B38F5A1281E2E49007D5F5D /* 1_Compositor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */; };
5B38F5A2281E2E49007D5F5D /* 0_Megrez.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */; }; 5B38F5A2281E2E49007D5F5D /* 0_Megrez.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */; };
5B38F5A3281E2E49007D5F5D /* 3_Span.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */; }; 5B38F5A3281E2E49007D5F5D /* 3_Span.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */; };
5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */; }; 5B38F5A4281E2E49007D5F5D /* 5_LanguageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0D4F1915FC0EB100ABF4B3 /* 5_LanguageModel.swift */; };
@ -286,7 +286,7 @@
6A0D4EF515FC0DA600ABF4B3 /* IME-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "IME-Info.plist"; sourceTree = "<group>"; }; 6A0D4EF515FC0DA600ABF4B3 /* IME-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "IME-Info.plist"; sourceTree = "<group>"; };
6A0D4EF615FC0DA600ABF4B3 /* vChewing-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "vChewing-Prefix.pch"; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; }; 6A0D4EF615FC0DA600ABF4B3 /* vChewing-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "vChewing-Prefix.pch"; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 6_Bigram.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 6A0D4F1415FC0EB100ABF4B3 /* 6_Bigram.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 6_Bigram.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 1_BlockReadingBuilder.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 1_Compositor.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = 0_Megrez.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = 0_Megrez.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 2_Grid.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 2_Grid.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 7_KeyValuePair.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; }; 6A0D4F1815FC0EB100ABF4B3 /* 7_KeyValuePair.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = 7_KeyValuePair.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
@ -764,7 +764,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */, 6A0D4F1615FC0EB100ABF4B3 /* 0_Megrez.swift */,
6A0D4F1515FC0EB100ABF4B3 /* 1_BlockReadingBuilder.swift */, 6A0D4F1515FC0EB100ABF4B3 /* 1_Compositor.swift */,
6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */, 6A0D4F1715FC0EB100ABF4B3 /* 2_Grid.swift */,
6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */, 6A0D4F1B15FC0EB100ABF4B3 /* 3_NodeAnchor.swift */,
6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */, 6A0D4F1C15FC0EB100ABF4B3 /* 3_Span.swift */,
@ -1117,7 +1117,7 @@
5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */, 5B62A34727AE7CD900A19448 /* ctlCandidate.swift in Sources */,
5BA9FD3F27FEF3C8002DE248 /* Pane.swift in Sources */, 5BA9FD3F27FEF3C8002DE248 /* Pane.swift in Sources */,
5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */, 5BB802DA27FABA8300CF1C19 /* ctlInputMethod_Menu.swift in Sources */,
5B38F5A1281E2E49007D5F5D /* 1_BlockReadingBuilder.swift in Sources */, 5B38F5A1281E2E49007D5F5D /* 1_Compositor.swift in Sources */,
5BDC1CFA27FDF1310052C2B9 /* apiUpdate.swift in Sources */, 5BDC1CFA27FDF1310052C2B9 /* apiUpdate.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;