metrale_speculative/
ngram.rs1pub struct NgramProposer {
13 min_match: usize,
15 max_match: usize,
17 pub accepts: u64,
19 pub rejects: u64,
20}
21
22#[allow(clippy::len_without_is_empty)]
23impl NgramProposer {
24 pub fn new(_order: usize) -> Self {
25 Self {
26 min_match: 2,
27 max_match: 16,
28 accepts: 0,
29 rejects: 0,
30 }
31 }
32
33 pub fn record_accept(&mut self) {
35 self.accepts += 1;
36 }
37
38 pub fn record_reject(&mut self) {
40 self.rejects += 1;
41 }
42
43 pub fn propose(&self, all_tokens: &[u32]) -> Option<u32> {
47 let len = all_tokens.len();
48 if len < self.min_match + 1 {
49 return None;
50 }
51
52 let max_n = self.max_match.min(len - 1);
53 let mut best_next: Option<u32> = None;
54 let mut best_match_len: usize = 0;
55
56 for n in self.min_match..=max_n {
59 let suffix = &all_tokens[len - n..len];
60 for start in 0..=(len - n - 1) {
61 if all_tokens[start..start + n] == *suffix && n > best_match_len {
62 best_match_len = n;
63 best_next = Some(all_tokens[start + n]);
64 break;
65 }
66 }
67 }
68
69 best_next
70 }
71
72 pub fn observe(&mut self, _history: &[u32], _next: u32) {}
74
75 pub fn len(&self) -> usize {
76 0
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn test_prompt_lookup_basic() {
86 let p = NgramProposer::new(4);
87
88 let tokens = vec![1, 2, 3, 4, 5, 1, 2, 3];
90 assert_eq!(p.propose(&tokens), Some(4));
91 }
92
93 #[test]
94 fn test_prompt_lookup_no_match() {
95 let p = NgramProposer::new(4);
96
97 let tokens = vec![1, 2, 3, 4, 5, 6, 7, 8];
98 assert_eq!(p.propose(&tokens), None);
99 }
100
101 #[test]
102 fn test_prompt_lookup_short() {
103 let p = NgramProposer::new(4);
104
105 let tokens = vec![1, 2];
106 assert_eq!(p.propose(&tokens), None);
107 }
108
109 #[test]
110 fn test_prompt_lookup_repetitive() {
111 let p = NgramProposer::new(4);
112
113 let tokens = vec![10, 20, 30, 10, 20, 30, 10, 20];
116 assert_eq!(p.propose(&tokens), Some(30));
117 }
118}