4
4
//
5
5
6
6
#import " ContextRanking.h"
7
+ #import < Foundation/Foundation.h>
7
8
8
9
static NSMutableArray <NSString *> *s_recentHistory;
9
10
static NSMutableDictionary <NSString *, NSMutableDictionary <NSString *, NSNumber *> *> *s_bigrams; // prev -> (next -> count)
10
11
static const NSUInteger kHistoryMax = 8 ; // small cap to keep things light
12
+ static NSString * const kBigramsDefaultsKey = @" ContextRankingBigrams" ;
13
+ static const NSUInteger kPrevMax = 64 ; // cap number of prev tokens to persist
14
+ static const NSUInteger kNextPerPrevMax = 12 ; // cap next variants per prev
15
+
16
+ static void ensureStoresLoaded (void ) {
17
+ if (!s_bigrams) {
18
+ NSDictionary *saved = [[NSUserDefaults standardUserDefaults ] dictionaryForKey: kBigramsDefaultsKey ];
19
+ s_bigrams = [[NSMutableDictionary alloc ] init ];
20
+ if ([saved isKindOfClass: [NSDictionary class ]]) {
21
+ [saved enumerateKeysAndObjectsUsingBlock: ^(NSString *prev, NSDictionary *nexts, BOOL *stop) {
22
+ if (![prev isKindOfClass: [NSString class ]] || ![nexts isKindOfClass: [NSDictionary class ]]) return ;
23
+ NSMutableDictionary <NSString *, NSNumber *> *mutableNexts = [[NSMutableDictionary alloc ] init ];
24
+ [nexts enumerateKeysAndObjectsUsingBlock: ^(NSString *nxt, NSNumber *cnt, BOOL *stop2) {
25
+ if ([nxt isKindOfClass: [NSString class ]] && [cnt isKindOfClass: [NSNumber class ]]) {
26
+ mutableNexts[nxt] = cnt;
27
+ }
28
+ }];
29
+ s_bigrams[prev] = mutableNexts;
30
+ }];
31
+ }
32
+ }
33
+ }
34
+
35
+ static void persistBigrams (void ) {
36
+ if (!s_bigrams) return ;
37
+ // Enforce caps: limit number of prev keys and next variants per prev (by lowest count pruning)
38
+ if (s_bigrams.count > kPrevMax ) {
39
+ NSArray *keys = [s_bigrams allKeys ];
40
+ NSMutableArray *scored = [NSMutableArray arrayWithCapacity: keys.count];
41
+ for (NSString *k in keys) {
42
+ NSDictionary *m = s_bigrams[k];
43
+ NSInteger sum = 0 ; for (NSNumber *v in [m allValues ]) sum += v.integerValue ;
44
+ [scored addObject: @{ @" k" : k, @" s" : @(sum) }];
45
+ }
46
+ [scored sortUsingComparator: ^NSComparisonResult (NSDictionary *a, NSDictionary *b){
47
+ return [a[@" s" ] integerValue ] < [b[@" s" ] integerValue ] ? NSOrderedAscending : NSOrderedDescending;
48
+ }];
49
+ for (NSUInteger i = kPrevMax ; i < scored.count ; i++) {
50
+ NSString *drop = scored[i][@" k" ];
51
+ [s_bigrams removeObjectForKey: drop];
52
+ }
53
+ }
54
+ for (NSString *prev in [s_bigrams allKeys ]) {
55
+ NSMutableDictionary <NSString *, NSNumber *> *nextMap = s_bigrams[prev];
56
+ if (nextMap.count > kNextPerPrevMax ) {
57
+ NSArray *pairs = [nextMap allKeys ];
58
+ NSArray *sorted = [pairs sortedArrayUsingComparator: ^NSComparisonResult (NSString *a, NSString *b){
59
+ NSInteger ca = [nextMap[a] integerValue ];
60
+ NSInteger cb = [nextMap[b] integerValue ];
61
+ if (ca == cb) return NSOrderedSame;
62
+ return (ca > cb) ? NSOrderedAscending : NSOrderedDescending;
63
+ }];
64
+ NSSet *keep = [NSSet setWithArray: [sorted subarrayWithRange: NSMakeRange (0 , kNextPerPrevMax )]];
65
+ for (NSString *key in [nextMap allKeys ]) {
66
+ if (![keep containsObject: key]) [nextMap removeObjectForKey: key];
67
+ }
68
+ }
69
+ }
70
+ NSMutableDictionary *toSave = [NSMutableDictionary dictionaryWithCapacity: s_bigrams.count];
71
+ [s_bigrams enumerateKeysAndObjectsUsingBlock: ^(NSString *prev, NSDictionary *nexts, BOOL *stop){
72
+ toSave[prev] = [nexts copy ];
73
+ }];
74
+ [[NSUserDefaults standardUserDefaults ] setObject: toSave forKey: kBigramsDefaultsKey ];
75
+ [[NSUserDefaults standardUserDefaults ] synchronize ];
76
+ }
11
77
12
78
@implementation ContextRanking
13
79
14
80
+ (NSArray <NSString *> *)rankCandidates : (NSArray <NSString *> *)candidates
15
81
withHistory : (NSArray <NSString *> * _Nullable)history
16
82
{
17
83
if (!candidates || candidates.count <= 1 ) return candidates ?: @[];
84
+ ensureStoresLoaded ();
18
85
// Minimal heuristic: boost candidates observed to follow the last committed token.
19
86
NSString *last = (history.count > 0 ? history[0 ] : nil );
20
87
if (!last || !s_bigrams) {
@@ -43,11 +110,11 @@ @implementation ContextRanking
43
110
+ (void )recordCommittedToken : (NSString *)token {
44
111
if (token.length == 0 ) return ;
45
112
@synchronized (self) {
113
+ ensureStoresLoaded ();
46
114
if (!s_recentHistory) s_recentHistory = [[NSMutableArray alloc ] init ];
47
115
// Update bigram counts using previous token (if any)
48
116
NSString *prev = (s_recentHistory.count > 0 ? s_recentHistory[0 ] : nil );
49
117
if (prev && token) {
50
- if (!s_bigrams) s_bigrams = [[NSMutableDictionary alloc ] init ];
51
118
NSMutableDictionary <NSString *, NSNumber *> *nextMap = s_bigrams[prev];
52
119
if (!nextMap) {
53
120
nextMap = [[NSMutableDictionary alloc ] init ];
@@ -62,6 +129,7 @@ + (void)recordCommittedToken:(NSString *)token {
62
129
[s_recentHistory insertObject: token atIndex: 0 ];
63
130
while (s_recentHistory.count > kHistoryMax ) [s_recentHistory removeLastObject ];
64
131
}
132
+ persistBigrams ();
65
133
}
66
134
67
135
+ (NSArray <NSString *> *)recentHistory : (NSUInteger )limit {
0 commit comments