1use std::collections::{HashMap, HashSet};
24use std::time::Instant;
25use super::working_memory::FactHandle;
26
27#[derive(Debug, Clone, PartialEq, Eq)]
29pub enum JustificationType {
30 Explicit,
32 Logical,
34}
35
36#[derive(Debug, Clone)]
41pub struct Justification {
42 pub fact_handle: FactHandle,
44
45 pub justification_type: JustificationType,
47
48 pub source_rule: Option<String>,
50
51 pub premise_facts: Vec<FactHandle>,
54
55 pub created_at: Instant,
57
58 pub id: u64,
60}
61
62impl Justification {
63 pub fn explicit(fact_handle: FactHandle, id: u64) -> Self {
65 Self {
66 fact_handle,
67 justification_type: JustificationType::Explicit,
68 source_rule: None,
69 premise_facts: Vec::new(),
70 created_at: Instant::now(),
71 id,
72 }
73 }
74
75 pub fn logical(
77 fact_handle: FactHandle,
78 source_rule: String,
79 premise_facts: Vec<FactHandle>,
80 id: u64,
81 ) -> Self {
82 Self {
83 fact_handle,
84 justification_type: JustificationType::Logical,
85 source_rule: Some(source_rule),
86 premise_facts,
87 created_at: Instant::now(),
88 id,
89 }
90 }
91
92 pub fn is_valid(&self, retracted_facts: &HashSet<FactHandle>) -> bool {
95 if self.justification_type == JustificationType::Explicit {
97 return true;
98 }
99
100 !self.premise_facts.iter().any(|h| retracted_facts.contains(h))
102 }
103}
104
105pub struct TruthMaintenanceSystem {
110 justifications: HashMap<u64, Justification>,
112
113 fact_justifications: HashMap<FactHandle, Vec<u64>>,
115
116 fact_dependents: HashMap<FactHandle, Vec<u64>>,
118
119 logical_facts: HashSet<FactHandle>,
121
122 explicit_facts: HashSet<FactHandle>,
124
125 retracted_facts: HashSet<FactHandle>,
127
128 next_justification_id: u64,
130}
131
132impl TruthMaintenanceSystem {
133 pub fn new() -> Self {
135 Self {
136 justifications: HashMap::new(),
137 fact_justifications: HashMap::new(),
138 fact_dependents: HashMap::new(),
139 logical_facts: HashSet::new(),
140 explicit_facts: HashSet::new(),
141 retracted_facts: HashSet::new(),
142 next_justification_id: 1,
143 }
144 }
145
146 pub fn add_explicit_justification(&mut self, fact_handle: FactHandle) {
148 let id = self.next_justification_id;
149 self.next_justification_id += 1;
150
151 let justification = Justification::explicit(fact_handle, id);
152
153 self.justifications.insert(id, justification);
154 self.fact_justifications
155 .entry(fact_handle)
156 .or_insert_with(Vec::new)
157 .push(id);
158
159 self.explicit_facts.insert(fact_handle);
160 }
161
162 pub fn add_logical_justification(
169 &mut self,
170 fact_handle: FactHandle,
171 source_rule: String,
172 premise_facts: Vec<FactHandle>,
173 ) {
174 let id = self.next_justification_id;
175 self.next_justification_id += 1;
176
177 let justification = Justification::logical(
178 fact_handle,
179 source_rule,
180 premise_facts.clone(),
181 id,
182 );
183
184 self.justifications.insert(id, justification);
186
187 self.fact_justifications
189 .entry(fact_handle)
190 .or_insert_with(Vec::new)
191 .push(id);
192
193 for premise in premise_facts {
195 self.fact_dependents
196 .entry(premise)
197 .or_insert_with(Vec::new)
198 .push(id);
199 }
200
201 self.logical_facts.insert(fact_handle);
202 }
203
204 pub fn is_logical(&self, fact_handle: FactHandle) -> bool {
206 self.logical_facts.contains(&fact_handle)
207 }
208
209 pub fn is_explicit(&self, fact_handle: FactHandle) -> bool {
211 self.explicit_facts.contains(&fact_handle)
212 }
213
214 pub fn get_justifications(&self, fact_handle: FactHandle) -> Vec<&Justification> {
216 if let Some(just_ids) = self.fact_justifications.get(&fact_handle) {
217 just_ids
218 .iter()
219 .filter_map(|id| self.justifications.get(id))
220 .collect()
221 } else {
222 Vec::new()
223 }
224 }
225
226 pub fn has_valid_justification(&self, fact_handle: FactHandle) -> bool {
228 if let Some(just_ids) = self.fact_justifications.get(&fact_handle) {
229 just_ids
230 .iter()
231 .filter_map(|id| self.justifications.get(id))
232 .any(|j| j.is_valid(&self.retracted_facts))
233 } else {
234 false
235 }
236 }
237
238 pub fn retract_with_cascade(&mut self, fact_handle: FactHandle) -> Vec<FactHandle> {
242 let mut to_retract = Vec::new();
243
244 self.retracted_facts.insert(fact_handle);
246 self.logical_facts.remove(&fact_handle);
247 self.explicit_facts.remove(&fact_handle);
248
249 if let Some(dependent_just_ids) = self.fact_dependents.get(&fact_handle) {
251 for just_id in dependent_just_ids.clone() {
252 if let Some(justification) = self.justifications.get(&just_id) {
253 let dependent_fact = justification.fact_handle;
254
255 if !self.has_valid_justification(dependent_fact) {
257 if !self.retracted_facts.contains(&dependent_fact) {
259 to_retract.push(dependent_fact);
260
261 let cascaded = self.retract_with_cascade(dependent_fact);
263 to_retract.extend(cascaded);
264 }
265 }
266 }
267 }
268 }
269
270 to_retract
271 }
272
273 pub fn remove_justifications(&mut self, fact_handle: FactHandle) {
275 if let Some(just_ids) = self.fact_justifications.remove(&fact_handle) {
276 for id in just_ids {
277 self.justifications.remove(&id);
278 }
279 }
280 }
281
282 pub fn stats(&self) -> TmsStats {
284 TmsStats {
285 total_justifications: self.justifications.len(),
286 logical_facts: self.logical_facts.len(),
287 explicit_facts: self.explicit_facts.len(),
288 retracted_facts: self.retracted_facts.len(),
289 }
290 }
291
292 pub fn clear(&mut self) {
294 self.justifications.clear();
295 self.fact_justifications.clear();
296 self.fact_dependents.clear();
297 self.logical_facts.clear();
298 self.explicit_facts.clear();
299 self.retracted_facts.clear();
300 self.next_justification_id = 1;
301 }
302
303 pub fn get_logical_facts(&self) -> &HashSet<FactHandle> {
305 &self.logical_facts
306 }
307
308 pub fn get_explicit_facts(&self) -> &HashSet<FactHandle> {
310 &self.explicit_facts
311 }
312}
313
314impl Default for TruthMaintenanceSystem {
315 fn default() -> Self {
316 Self::new()
317 }
318}
319
320#[derive(Debug, Clone)]
322pub struct TmsStats {
323 pub total_justifications: usize,
324 pub logical_facts: usize,
325 pub explicit_facts: usize,
326 pub retracted_facts: usize,
327}
328
329impl std::fmt::Display for TmsStats {
330 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
331 write!(
332 f,
333 "TMS Stats: {} justifications, {} logical facts, {} explicit facts, {} retracted",
334 self.total_justifications,
335 self.logical_facts,
336 self.explicit_facts,
337 self.retracted_facts
338 )
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use super::*;
345
346 #[test]
347 fn test_explicit_justification() {
348 let mut tms = TruthMaintenanceSystem::new();
349 let fact = FactHandle::new(1);
350
351 tms.add_explicit_justification(fact);
352
353 assert!(tms.is_explicit(fact));
354 assert!(!tms.is_logical(fact));
355 assert!(tms.has_valid_justification(fact));
356 }
357
358 #[test]
359 fn test_logical_justification() {
360 let mut tms = TruthMaintenanceSystem::new();
361 let premise = FactHandle::new(1);
362 let derived = FactHandle::new(2);
363
364 tms.add_explicit_justification(premise);
365 tms.add_logical_justification(derived, "TestRule".to_string(), vec![premise]);
366
367 assert!(tms.is_logical(derived));
368 assert!(!tms.is_explicit(derived));
369 assert!(tms.has_valid_justification(derived));
370 }
371
372 #[test]
373 fn test_cascade_retraction() {
374 let mut tms = TruthMaintenanceSystem::new();
375
376 let fact_a = FactHandle::new(1);
378 let fact_b = FactHandle::new(2);
379 let fact_c = FactHandle::new(3);
380
381 tms.add_explicit_justification(fact_a);
382 tms.add_logical_justification(fact_b, "Rule1".to_string(), vec![fact_a]);
383 tms.add_logical_justification(fact_c, "Rule2".to_string(), vec![fact_b]);
384
385 let cascaded = tms.retract_with_cascade(fact_a);
387
388 assert!(cascaded.contains(&fact_b));
389 assert!(cascaded.contains(&fact_c));
390 assert!(!tms.has_valid_justification(fact_b));
391 assert!(!tms.has_valid_justification(fact_c));
392 }
393
394 #[test]
395 fn test_multiple_justifications() {
396 let mut tms = TruthMaintenanceSystem::new();
397
398 let premise1 = FactHandle::new(1);
399 let premise2 = FactHandle::new(2);
400 let derived = FactHandle::new(3);
401
402 tms.add_explicit_justification(premise1);
403 tms.add_explicit_justification(premise2);
404
405 tms.add_logical_justification(derived, "Rule1".to_string(), vec![premise1]);
407 tms.add_logical_justification(derived, "Rule2".to_string(), vec![premise2]);
408
409 let cascaded = tms.retract_with_cascade(premise1);
411
412 assert!(tms.has_valid_justification(derived));
414 assert!(cascaded.is_empty());
415
416 let cascaded = tms.retract_with_cascade(premise2);
418
419 assert!(!tms.has_valid_justification(derived));
421 assert!(cascaded.contains(&derived));
422 }
423}