1use ipfrs_core::Cid;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::fmt;
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
13pub enum Term {
14 Var(String),
16 Const(Constant),
18 Fun(String, Vec<Term>),
20 Ref(TermRef),
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
26pub enum Constant {
27 String(String),
29 Int(i64),
31 Bool(bool),
33 Float(String),
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
39pub struct TermRef {
40 #[serde(
42 serialize_with = "crate::serialize_cid",
43 deserialize_with = "crate::deserialize_cid"
44 )]
45 pub cid: Cid,
46 pub hint: Option<String>,
48}
49
50impl TermRef {
51 pub fn new(cid: Cid) -> Self {
53 Self { cid, hint: None }
54 }
55
56 pub fn with_hint(cid: Cid, hint: String) -> Self {
58 Self {
59 cid,
60 hint: Some(hint),
61 }
62 }
63}
64
65impl Term {
66 #[inline]
68 pub fn is_var(&self) -> bool {
69 matches!(self, Term::Var(_))
70 }
71
72 #[inline]
74 pub fn is_const(&self) -> bool {
75 matches!(self, Term::Const(_))
76 }
77
78 #[inline]
80 pub fn is_ground(&self) -> bool {
81 match self {
82 Term::Var(_) => false,
83 Term::Const(_) => true,
84 Term::Fun(_, args) => args.iter().all(|t| t.is_ground()),
85 Term::Ref(_) => true, }
87 }
88
89 pub fn variables(&self) -> Vec<String> {
91 let capacity = self.estimate_var_count();
92 let mut vars = Vec::with_capacity(capacity);
93 self.collect_vars(&mut vars);
94 vars.sort_unstable();
95 vars.dedup();
96 vars
97 }
98
99 #[inline]
101 fn estimate_var_count(&self) -> usize {
102 match self {
103 Term::Var(_) => 1,
104 Term::Const(_) | Term::Ref(_) => 0,
105 Term::Fun(_, args) => args.iter().map(|t| t.estimate_var_count()).sum(),
106 }
107 }
108
109 #[inline]
110 fn collect_vars(&self, vars: &mut Vec<String>) {
111 match self {
112 Term::Var(v) => vars.push(v.clone()),
113 Term::Fun(_, args) => {
114 for arg in args {
115 arg.collect_vars(vars);
116 }
117 }
118 _ => {}
119 }
120 }
121
122 #[inline]
124 pub fn complexity(&self) -> usize {
125 match self {
126 Term::Var(_) | Term::Const(_) | Term::Ref(_) => 1,
127 Term::Fun(_, args) => 1 + args.iter().map(|t| t.complexity()).sum::<usize>(),
128 }
129 }
130}
131
132impl fmt::Display for Term {
133 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
134 match self {
135 Term::Var(v) => write!(f, "?{}", v),
136 Term::Const(c) => write!(f, "{}", c),
137 Term::Fun(name, args) => {
138 write!(f, "{}(", name)?;
139 for (i, arg) in args.iter().enumerate() {
140 if i > 0 {
141 write!(f, ", ")?;
142 }
143 write!(f, "{}", arg)?;
144 }
145 write!(f, ")")
146 }
147 Term::Ref(r) => write!(f, "@{}", r.cid),
148 }
149 }
150}
151
152impl fmt::Display for Constant {
153 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154 match self {
155 Constant::String(s) => write!(f, "\"{}\"", s),
156 Constant::Int(i) => write!(f, "{}", i),
157 Constant::Bool(b) => write!(f, "{}", b),
158 Constant::Float(s) => write!(f, "{}", s),
159 }
160 }
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
165pub struct Predicate {
166 pub name: String,
168 pub args: Vec<Term>,
170}
171
172impl Predicate {
173 pub fn new(name: String, args: Vec<Term>) -> Self {
175 Self { name, args }
176 }
177
178 #[inline]
180 pub fn arity(&self) -> usize {
181 self.args.len()
182 }
183
184 #[inline]
186 pub fn is_ground(&self) -> bool {
187 self.args.iter().all(|t| t.is_ground())
188 }
189
190 pub fn variables(&self) -> Vec<String> {
192 let capacity: usize = self.args.iter().map(|t| t.estimate_var_count()).sum();
193 let mut vars = Vec::with_capacity(capacity);
194 for arg in &self.args {
195 arg.collect_vars(&mut vars);
196 }
197 vars.sort_unstable();
198 vars.dedup();
199 vars
200 }
201}
202
203impl fmt::Display for Predicate {
204 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205 write!(f, "{}(", self.name)?;
206 for (i, arg) in self.args.iter().enumerate() {
207 if i > 0 {
208 write!(f, ", ")?;
209 }
210 write!(f, "{}", arg)?;
211 }
212 write!(f, ")")
213 }
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct Rule {
219 pub head: Predicate,
221 pub body: Vec<Predicate>,
223}
224
225impl Rule {
226 pub fn new(head: Predicate, body: Vec<Predicate>) -> Self {
228 Self { head, body }
229 }
230
231 pub fn fact(head: Predicate) -> Self {
233 Self {
234 head,
235 body: Vec::new(),
236 }
237 }
238
239 #[inline]
241 pub fn is_fact(&self) -> bool {
242 self.body.is_empty()
243 }
244
245 pub fn variables(&self) -> Vec<String> {
247 let mut vars = self.head.variables();
248 for pred in &self.body {
249 for var in pred.variables() {
250 if !vars.contains(&var) {
251 vars.push(var);
252 }
253 }
254 }
255 vars.sort_unstable();
256 vars
257 }
258}
259
260impl fmt::Display for Rule {
261 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
262 write!(f, "{}", self.head)?;
263 if !self.body.is_empty() {
264 write!(f, " :- ")?;
265 for (i, pred) in self.body.iter().enumerate() {
266 if i > 0 {
267 write!(f, ", ")?;
268 }
269 write!(f, "{}", pred)?;
270 }
271 }
272 write!(f, ".")
273 }
274}
275
276#[derive(Debug, Clone, Default, Serialize, Deserialize)]
278pub struct KnowledgeBase {
279 pub facts: Vec<Predicate>,
281 pub rules: Vec<Rule>,
283}
284
285impl KnowledgeBase {
286 pub fn new() -> Self {
288 Self::default()
289 }
290
291 pub fn add_fact(&mut self, fact: Predicate) {
293 self.facts.push(fact);
294 }
295
296 pub fn add_rule(&mut self, rule: Rule) {
298 self.rules.push(rule);
299 }
300
301 #[inline]
303 pub fn get_predicates(&self, name: &str) -> Vec<&Predicate> {
304 self.facts.iter().filter(|p| p.name == name).collect()
305 }
306
307 #[inline]
309 pub fn get_rules(&self, name: &str) -> Vec<&Rule> {
310 self.rules.iter().filter(|r| r.head.name == name).collect()
311 }
312
313 pub fn stats(&self) -> KnowledgeBaseStats {
315 KnowledgeBaseStats {
316 num_facts: self.facts.len(),
317 num_rules: self.rules.len(),
318 }
319 }
320
321 pub fn index_rules_by_predicate(
336 &self,
337 rule_cids: &HashMap<usize, Cid>,
338 ) -> HashMap<String, Vec<Cid>> {
339 let mut index: HashMap<String, Vec<Cid>> = HashMap::new();
340 for (idx, rule) in self.rules.iter().enumerate() {
341 if let Some(cid) = rule_cids.get(&idx) {
342 index.entry(rule.head.name.clone()).or_default().push(*cid);
343 }
344 }
345 index
346 }
347
348 pub fn index_rules_by_predicate_local(&self) -> HashMap<String, Vec<usize>> {
352 let mut index: HashMap<String, Vec<usize>> = HashMap::new();
353 for (idx, rule) in self.rules.iter().enumerate() {
354 index.entry(rule.head.name.clone()).or_default().push(idx);
355 }
356 index
357 }
358}
359
360#[derive(Debug, Clone)]
362pub struct KnowledgeBaseStats {
363 pub num_facts: usize,
365 pub num_rules: usize,
367}
368
369#[cfg(test)]
370mod tests {
371 use super::*;
372
373 #[test]
374 fn test_term_creation() {
375 let var = Term::Var("X".to_string());
376 assert!(var.is_var());
377 assert!(!var.is_ground());
378
379 let const_term = Term::Const(Constant::String("Alice".to_string()));
380 assert!(const_term.is_const());
381 assert!(const_term.is_ground());
382 }
383
384 #[test]
385 fn test_predicate() {
386 let pred = Predicate::new(
387 "parent".to_string(),
388 vec![
389 Term::Const(Constant::String("Alice".to_string())),
390 Term::Var("X".to_string()),
391 ],
392 );
393
394 assert_eq!(pred.arity(), 2);
395 assert!(!pred.is_ground());
396 assert_eq!(pred.variables(), vec!["X".to_string()]);
397 }
398
399 #[test]
400 fn test_rule() {
401 let head = Predicate::new(
402 "grandparent".to_string(),
403 vec![Term::Var("X".to_string()), Term::Var("Z".to_string())],
404 );
405
406 let body = vec![
407 Predicate::new(
408 "parent".to_string(),
409 vec![Term::Var("X".to_string()), Term::Var("Y".to_string())],
410 ),
411 Predicate::new(
412 "parent".to_string(),
413 vec![Term::Var("Y".to_string()), Term::Var("Z".to_string())],
414 ),
415 ];
416
417 let rule = Rule::new(head, body);
418 assert!(!rule.is_fact());
419 assert_eq!(
420 rule.variables(),
421 vec!["X".to_string(), "Y".to_string(), "Z".to_string()]
422 );
423 }
424
425 #[test]
426 fn test_knowledge_base() {
427 let mut kb = KnowledgeBase::new();
428
429 kb.add_fact(Predicate::new(
430 "parent".to_string(),
431 vec![
432 Term::Const(Constant::String("Alice".to_string())),
433 Term::Const(Constant::String("Bob".to_string())),
434 ],
435 ));
436
437 let stats = kb.stats();
438 assert_eq!(stats.num_facts, 1);
439 assert_eq!(stats.num_rules, 0);
440 }
441}