1use std::collections::{HashMap, HashSet};
2
3use crate::field::NodeId;
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
7pub struct TrailId(u64);
8
9#[derive(Clone, Debug)]
10struct Entry {
11 node: NodeId,
12 prev: Option<TrailId>,
13 next: Option<TrailId>,
14}
15
16pub struct Trail {
21 next_id: u64,
22
23 head: Option<TrailId>,
24 tail: Option<TrailId>,
25 cursor: Option<TrailId>,
26
27 entries: HashMap<TrailId, Entry>,
28
29 by_node: HashMap<NodeId, HashSet<TrailId>>,
31}
32
33impl Trail {
34 pub fn new() -> Self {
35 Self {
36 next_id: 1,
37 head: None,
38 tail: None,
39 cursor: None,
40 entries: HashMap::new(),
41 by_node: HashMap::new(),
42 }
43 }
44
45 pub fn cursor(&self) -> Option<NodeId> {
46 self.cursor
47 .and_then(|id| self.entries.get(&id))
48 .map(|e| e.node)
49 }
50
51 pub fn is_empty(&self) -> bool {
52 self.head.is_none()
53 }
54
55 pub fn len(&self) -> usize {
56 self.entries.len()
57 }
58
59 pub fn entries(&self) -> Vec<NodeId> {
60 let mut out = Vec::with_capacity(self.entries.len());
61 let mut it = self.head;
62 while let Some(id) = it {
63 let Some(entry) = self.entries.get(&id) else {
64 break;
65 };
66 out.push(entry.node);
67 it = entry.next;
68 }
69 out
70 }
71
72 pub fn cursor_index(&self) -> Option<usize> {
73 let cursor = self.cursor?;
74 let mut index = 0usize;
75 let mut it = self.head;
76 while let Some(id) = it {
77 if id == cursor {
78 return Some(index);
79 }
80 let Some(entry) = self.entries.get(&id) else {
81 break;
82 };
83 index += 1;
84 it = entry.next;
85 }
86 None
87 }
88
89 pub fn node_at_index(&self, index: usize) -> Option<NodeId> {
90 let mut current = 0usize;
91 let mut it = self.head;
92 while let Some(id) = it {
93 let entry = self.entries.get(&id)?;
94 if current == index {
95 return Some(entry.node);
96 }
97 current += 1;
98 it = entry.next;
99 }
100 None
101 }
102
103 pub fn seek_to_index(&mut self, index: usize) -> Option<NodeId> {
104 let mut current = 0usize;
105 let mut it = self.head;
106 while let Some(id) = it {
107 let entry = self.entries.get(&id)?;
108 if current == index {
109 self.cursor = Some(id);
110 return Some(entry.node);
111 }
112 current += 1;
113 it = entry.next;
114 }
115 None
116 }
117
118 pub fn seek_to_node(&mut self, node: NodeId) -> bool {
119 let mut last_match = None;
120 let mut it = self.head;
121 while let Some(id) = it {
122 let Some(entry) = self.entries.get(&id) else {
123 break;
124 };
125 if entry.node == node {
126 last_match = Some(id);
127 }
128 it = entry.next;
129 }
130 if let Some(id) = last_match {
131 self.cursor = Some(id);
132 true
133 } else {
134 false
135 }
136 }
137
138 pub fn record(&mut self, node: NodeId) {
140 self.drop_forward();
142
143 let id = TrailId(self.next_id);
144 self.next_id += 1;
145
146 let entry = Entry {
147 node,
148 prev: self.tail,
149 next: None,
150 };
151
152 if let Some(tail_id) = self.tail {
153 if let Some(tail) = self.entries.get_mut(&tail_id) {
154 tail.next = Some(id);
155 }
156 } else {
157 self.head = Some(id);
159 }
160
161 self.tail = Some(id);
162 self.cursor = Some(id);
163
164 self.entries.insert(id, entry);
165 self.by_node.entry(node).or_default().insert(id);
166 }
167
168 pub fn back(&mut self) -> Option<NodeId> {
170 let cur = self.cursor?;
171 let prev = self.entries.get(&cur)?.prev?;
172 self.cursor = Some(prev);
173 self.cursor()
174 }
175
176 pub fn forward(&mut self) -> Option<NodeId> {
178 let cur = self.cursor?;
179 let next = self.entries.get(&cur)?.next?;
180 self.cursor = Some(next);
181 self.cursor()
182 }
183
184 pub fn back_wrapping(&mut self) -> Option<NodeId> {
185 if let Some(node) = self.back() {
186 return Some(node);
187 }
188 let tail = self.tail?;
189 self.cursor = Some(tail);
190 self.cursor()
191 }
192
193 pub fn forward_wrapping(&mut self) -> Option<NodeId> {
194 if let Some(node) = self.forward() {
195 return Some(node);
196 }
197 let head = self.head?;
198 self.cursor = Some(head);
199 self.cursor()
200 }
201
202 pub fn truncate_to(&mut self, max_len: usize) {
203 if max_len == 0 {
204 self.head = None;
205 self.tail = None;
206 self.cursor = None;
207 self.entries.clear();
208 self.by_node.clear();
209 return;
210 }
211
212 while self.entries.len() > max_len {
213 let Some(head) = self.head else {
214 break;
215 };
216 self.remove_entry(head);
217 }
218 }
219
220 pub fn forget_node(&mut self, node: NodeId) {
222 if !self.by_node.contains_key(&node) {
223 return;
224 }
225
226 let mut ids = Vec::new();
228 let mut it = self.head;
229 while let Some(id) = it {
230 let next = self.entries.get(&id).and_then(|e| e.next);
231 if self
232 .entries
233 .get(&id)
234 .is_some_and(|entry| entry.node == node)
235 {
236 ids.push(id);
237 }
238 it = next;
239 }
240
241 for id in ids {
242 self.remove_entry(id);
243 }
244 }
245
246 fn drop_forward(&mut self) {
247 let Some(cur) = self.cursor else { return };
249
250 let next = match self.entries.get(&cur) {
252 Some(e) => e.next,
253 None => return,
254 };
255
256 if let Some(e) = self.entries.get_mut(&cur) {
258 e.next = None;
259 }
260 self.tail = Some(cur);
261
262 let mut it = next;
264 while let Some(id) = it {
265 let next_id = self.entries.get(&id).and_then(|e| e.next);
266 self.remove_entry(id);
267 it = next_id;
268 }
269 }
270
271 fn remove_entry(&mut self, id: TrailId) {
272 let Some(entry) = self.entries.remove(&id) else {
273 return;
274 };
275
276 if let Some(set) = self.by_node.get_mut(&entry.node) {
278 set.remove(&id);
279 if set.is_empty() {
280 self.by_node.remove(&entry.node);
281 }
282 }
283
284 match entry.prev {
286 Some(p) => {
287 if let Some(pe) = self.entries.get_mut(&p) {
288 pe.next = entry.next;
289 }
290 }
291 None => {
292 self.head = entry.next;
294 }
295 }
296
297 match entry.next {
298 Some(n) => {
299 if let Some(ne) = self.entries.get_mut(&n) {
300 ne.prev = entry.prev;
301 }
302 }
303 None => {
304 self.tail = entry.prev;
306 }
307 }
308
309 if self.cursor == Some(id) {
311 self.cursor = entry.prev.or(entry.next);
312 }
313
314 if self.head.is_none() {
316 self.cursor = None;
317 self.tail = None;
318 }
319 }
320}
321
322impl Default for Trail {
323 fn default() -> Self {
324 Self::new()
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use super::*;
331 use crate::field::NodeId;
332
333 #[test]
334 fn record_sets_cursor_and_allows_back_forward() {
335 let mut t = Trail::new();
336 let a = NodeId::new(1);
337 let b = NodeId::new(2);
338 let c = NodeId::new(3);
339
340 t.record(a);
341 t.record(b);
342 t.record(c);
343
344 assert_eq!(t.cursor(), Some(c));
345 assert_eq!(t.back(), Some(b));
346 assert_eq!(t.back(), Some(a));
347 assert_eq!(t.back(), None); assert_eq!(t.forward(), Some(b));
349 assert_eq!(t.forward(), Some(c));
350 assert_eq!(t.forward(), None); }
352
353 #[test]
354 fn record_drops_forward_history() {
355 let mut t = Trail::new();
356 let a = NodeId::new(1);
357 let b = NodeId::new(2);
358 let c = NodeId::new(3);
359 let d = NodeId::new(4);
360
361 t.record(a);
362 t.record(b);
363 t.record(c);
364
365 assert_eq!(t.back(), Some(b));
367
368 t.record(d);
370
371 assert_eq!(t.cursor(), Some(d));
372 assert_eq!(t.forward(), None);
373 assert_eq!(t.back(), Some(b));
374 }
375
376 #[test]
377 fn forget_node_removes_all_occurrences_and_preserves_chain() {
378 let mut t = Trail::new();
379 let a = NodeId::new(1);
380 let b = NodeId::new(2);
381 let c = NodeId::new(3);
382
383 t.record(a);
385 t.record(b);
386 t.record(a);
387 t.record(c);
388
389 t.forget_node(a);
391
392 assert_eq!(t.cursor(), Some(c));
394 assert_eq!(t.back(), Some(b));
395 assert_eq!(t.back(), None);
396 assert_eq!(t.forward(), Some(c));
397 }
398
399 #[test]
400 fn forget_node_moves_cursor_safely() {
401 let mut t = Trail::new();
402 let a = NodeId::new(1);
403 let b = NodeId::new(2);
404
405 t.record(a);
406 t.record(b);
407
408 t.forget_node(b);
410 assert_eq!(t.cursor(), Some(a));
411
412 t.forget_node(a);
414 assert_eq!(t.cursor(), None);
415 assert!(t.is_empty());
416 }
417
418 #[test]
419 fn forget_node_with_multiple_occurrences_keeps_cursor_deterministic() {
420 let mut t = Trail::new();
421 let a = NodeId::new(1);
422 let b = NodeId::new(2);
423 let c = NodeId::new(3);
424
425 t.record(a);
426 t.record(b);
427 t.record(a);
428 t.record(c);
429 t.record(a);
430
431 t.forget_node(a);
432
433 assert_eq!(t.cursor(), Some(c));
434 assert_eq!(t.back(), Some(b));
435 assert_eq!(t.forward(), Some(c));
436 }
437}