mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-14 20:44:32 +02:00
Fixes bug where hints starting with the first alphabet's character are _almost never_ generated.
* Switches to "positional notation" to fix the bug. * Consolidates `encode_hint` and `generate_prefix_free_hint` into `encode_hint`. * Moves prefix_free_hints.go to hints_generator.go; `encode_hint` and `decode_hint` were moved here too. * The "prefix-free" logic was made 0-index based instead of 1-index based for two reasons: 1. To simplify 1-off discrepancies. 2. To keep backwards compatibility with the old usage of `encode_hint`. * Slight perfomance improvement added by reusing the runt-to-index map instead of rebuilding for each call to `decode_hint`. * Unit tests added; for all new logic and old affected logic. Fixes #10231
This commit is contained in:
90
kittens/hints/hints_generator.go
Normal file
90
kittens/hints/hints_generator.go
Normal file
@@ -0,0 +1,90 @@
|
||||
// License: GPLv3 Copyright: 2026, Kovid Goyal, <kovid at kovidgoyal.net>
|
||||
|
||||
package hints
|
||||
|
||||
// hints_to_skip calculates how many hints to skip based on the given n and
|
||||
// alphabet size so that the next n hints generated by expanding the prefix tree
|
||||
// of an alphabet of size alphabetSize will be prefix-free.
|
||||
//
|
||||
// The result is 1-based, meaning that the first hint to be skipped is the first
|
||||
// non-empty hint.
|
||||
//
|
||||
// For example, if alphabet was "abc" and n was 5, the first non-empty hints
|
||||
// from that alphabet would be: "a", "b", "c", "aa", "ab", "ac", ...
|
||||
// the result of hints_to_skip would be 1, and this would mean that if you skip
|
||||
// "a" from the list above, the next 5 hints are prefix-free:
|
||||
// "b", "c", "aa", "ab", "ac".
|
||||
//
|
||||
// See issue #10195 for an informal proof on why this function works.
|
||||
func hints_to_skip(n int, alphabetSize int) int {
|
||||
if n < 2 {
|
||||
n = 2
|
||||
}
|
||||
return (n - 2) / (alphabetSize - 1)
|
||||
}
|
||||
|
||||
// encode_hint generates the hint corresponding to the given index in the
|
||||
// breadth-first traversal of the prefix tree generated by the given alphabet.
|
||||
//
|
||||
// For example, given the alphabet "abc", the hints would be generated in
|
||||
// the following order:
|
||||
// 0. "a"
|
||||
// 1. "b"
|
||||
// 2. "c"
|
||||
// 3. "aa"
|
||||
// 4. "ab"
|
||||
// 5. "ac"
|
||||
// 6. "ba"
|
||||
// 7. "bb"
|
||||
// 8. "bc"
|
||||
// 9. "ca"
|
||||
// 10. "cb"
|
||||
// 11. "cc"
|
||||
// 12. "aaa"
|
||||
// ...
|
||||
//
|
||||
// Indices are 0-based on non-empty hints; index=0 the hint of length 1 with
|
||||
// the first character of the alphabet.
|
||||
func encode_hint(num int, alphabet string) (res string) {
|
||||
l := len(alphabet)
|
||||
|
||||
for num >= 0 {
|
||||
mod := num % l
|
||||
res = string(alphabet[mod]) + res
|
||||
num /= l
|
||||
num -= 1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func rune_to_index_map(alphabet string) *map[rune]int {
|
||||
runeToIndexMap := make(map[rune]int)
|
||||
for index, char := range alphabet {
|
||||
runeToIndexMap[char] = index
|
||||
}
|
||||
return &runeToIndexMap
|
||||
}
|
||||
|
||||
// decode_hint converts a hint to its corresponding index as it would appear in
|
||||
// the breadth-first traversal of the prefix tree generated by the given
|
||||
// alphabet.
|
||||
//
|
||||
// For example, given the alphabet "abc", the hints would be generated in
|
||||
// the following order:
|
||||
// 0. "a"
|
||||
// 1. "b"
|
||||
// 2. "c"
|
||||
// 3. "aa"
|
||||
// 4. "ab"
|
||||
// ...
|
||||
//
|
||||
// hint is expected to be non-empty.
|
||||
func decode_hint(hint string, char_to_index *map[rune]int) (res int) {
|
||||
base := len(*char_to_index)
|
||||
|
||||
for _, c := range hint {
|
||||
res = res*base + ((*char_to_index)[c] + 1)
|
||||
}
|
||||
return res - 1
|
||||
}
|
||||
Reference in New Issue
Block a user