@@ -12,7 +12,6 @@ class VietnameseTelexTest {
1212 fun setup () {
1313 telexRules = hashMapOf(
1414 " w" to " ư" ,
15- " ww" to " ư" ,
1615 " a" to " ă" ,
1716 " aw" to " ă" ,
1817 " aa" to " â" ,
@@ -66,8 +65,9 @@ class VietnameseTelexTest {
6665 val shortestFirstResult = applyRulesShortestFirst(input)
6766 assertEquals(" wư" , shortestFirstResult)
6867
68+ // With escape logic, "ww" should produce "w" (escape sequence)
6969 val longestFirstResult = applyRulesLongestFirst(input)
70- assertEquals(" ư " , longestFirstResult)
70+ assertEquals(" w " , longestFirstResult)
7171 }
7272
7373 @Test
@@ -113,14 +113,16 @@ class VietnameseTelexTest {
113113 fun testTripleW () {
114114 val input = " www"
115115 val result = applyRulesLongestFirst(input)
116- assertEquals(" wư" , result)
116+ // "www" → lastTwo "ww" escape to "w", result is "ww"
117+ assertEquals(" ww" , result)
117118 }
118119
119120 @Test
120121 fun testSpaceDoubleW () {
121122 val input = " O ww"
122123 val result = applyRulesLongestFirst(input)
123- assertEquals(" O ư" , result)
124+ // "ww" after space escapes to "w"
125+ assertEquals(" O w" , result)
124126 }
125127
126128 @Test
@@ -151,6 +153,21 @@ class VietnameseTelexTest {
151153 assertEquals(" ơ" , result)
152154 }
153155
156+ @Test
157+ fun testEscapeSequence_ww () {
158+ // Typing "ww" should produce "w" (escape transformation)
159+ val result = applyRulesLongestFirst(" ww" )
160+ assertEquals(" Double 'w' should escape to literal 'w'" , " w" , result)
161+ }
162+
163+ @Test
164+ fun testEscapeAfterTransformation () {
165+ // "ưw" is not a doubled character (last two are 'ư' and 'w'), so "w" transforms normally to "ư"
166+ // This gives us "ư" (prefix) + "ư" (transformation of "w") = "ưư"
167+ val result = applyRulesLongestFirst(" ưw" )
168+ assertEquals(" ưw should transform the 'w' to 'ư', giving 'ưư'" , " ưư" , result)
169+ }
170+
154171 /* *
155172 * Helper function that applies transformation rules checking shortest patterns first.
156173 * This demonstrates incorrect behavior when shorter patterns match before longer ones.
@@ -180,6 +197,22 @@ class VietnameseTelexTest {
180197 * This demonstrates correct behavior where longer patterns take precedence.
181198 */
182199 private fun applyRulesLongestFirst (word : String ): String {
200+ // Check for escape sequence FIRST (before single-char transformations)
201+ // Only applies when: 1) last two chars are same, 2) no rule for doubled sequence, 3) rule exists for single char
202+ if (word.length >= 2 ) {
203+ val lastTwo = word.takeLast(2 )
204+ if (lastTwo[0 ] == lastTwo[1 ]) {
205+ val doubledSeq = lastTwo.lowercase()
206+ val singleChar = lastTwo[0 ].toString().lowercase()
207+ // If there's NO rule for the doubled sequence, but there IS a rule for single char, it's an escape
208+ if (! telexRules.containsKey(doubledSeq) && telexRules.containsKey(singleChar)) {
209+ // This is an escape sequence - return word with just one of the doubled char
210+ return word.substring(0 , word.length - 1 )
211+ }
212+ }
213+ }
214+
215+ // Then check for transformation rules (longest patterns first)
183216 for (length in word.length downTo 1 ) {
184217 val suffix = word.substring(word.length - length)
185218 val suffixLower = suffix.lowercase()
@@ -197,6 +230,21 @@ class VietnameseTelexTest {
197230 * Matches case-insensitively but preserves the original case in output.
198231 */
199232 private fun applyRulesWithCasePreservation (word : String ): String {
233+ // Check for escape sequence FIRST (before single-char transformations)
234+ if (word.length >= 2 ) {
235+ val lastTwo = word.takeLast(2 )
236+ if (lastTwo[0 ] == lastTwo[1 ]) {
237+ val doubledSeq = lastTwo.lowercase()
238+ val singleChar = lastTwo[0 ].toString().lowercase()
239+ // If there's NO rule for the doubled sequence, but there IS a rule for single char, it's an escape
240+ if (! telexRules.containsKey(doubledSeq) && telexRules.containsKey(singleChar)) {
241+ // This is an escape sequence - return word with just one of the doubled char
242+ return word.substring(0 , word.length - 1 )
243+ }
244+ }
245+ }
246+
247+ // Then check for transformation rules (longest patterns first)
200248 for (length in word.length downTo 1 ) {
201249 val suffix = word.substring(word.length - length)
202250 val suffixLower = suffix.lowercase()
@@ -212,6 +260,7 @@ class VietnameseTelexTest {
212260 return prefix + finalReplacement
213261 }
214262 }
263+
215264 return word
216265 }
217266
0 commit comments