1use oxiz_core::ast::TermManager;
14use oxiz_core::ast::{TermId, TermKind};
15use oxiz_core::sort::SortId;
16use std::collections::HashMap;
17use std::fmt;
18
19#[derive(Debug, Clone)]
21pub enum SkolemizationError {
22 UnknownTerm(TermId),
24 UnknownSort(SortId),
26 CounterOverflow,
28 TermConstructionFailed(String),
30}
31
32impl fmt::Display for SkolemizationError {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 match self {
35 SkolemizationError::UnknownTerm(id) => {
36 write!(f, "unknown term with id {}", id.raw())
37 }
38 SkolemizationError::UnknownSort(id) => {
39 write!(f, "unknown sort with id {}", id.0)
40 }
41 SkolemizationError::CounterOverflow => {
42 write!(f, "Skolem counter overflow")
43 }
44 SkolemizationError::TermConstructionFailed(msg) => {
45 write!(f, "term construction failed: {}", msg)
46 }
47 }
48 }
49}
50
51impl std::error::Error for SkolemizationError {}
52
53#[derive(Debug, Clone)]
55pub struct SkolemSymbol {
56 pub name: String,
58 pub sort: SortId,
60 pub term: TermId,
62 pub arg_sorts: Vec<SortId>,
65}
66
67#[derive(Debug)]
73pub struct SkolemizationContext {
74 outer_universals: Vec<(SortId, TermId)>,
78 skolem_map: HashMap<TermId, TermId>,
80 skolem_counter: u64,
82 skolem_symbols: Vec<SkolemSymbol>,
84 nnf_cache: HashMap<(TermId, bool), TermId>,
86 skolem_cache: HashMap<TermId, TermId>,
88}
89
90impl Default for SkolemizationContext {
91 fn default() -> Self {
92 Self::new()
93 }
94}
95
96impl SkolemizationContext {
97 pub fn new() -> Self {
99 SkolemizationContext {
100 outer_universals: Vec::new(),
101 skolem_map: HashMap::new(),
102 skolem_counter: 0,
103 skolem_symbols: Vec::new(),
104 nnf_cache: HashMap::new(),
105 skolem_cache: HashMap::new(),
106 }
107 }
108
109 pub fn skolem_symbols(&self) -> &[SkolemSymbol] {
111 &self.skolem_symbols
112 }
113
114 pub fn skolem_count(&self) -> u64 {
116 self.skolem_counter
117 }
118
119 pub fn skolemize(
129 &mut self,
130 tm: &mut TermManager,
131 term: TermId,
132 ) -> Result<TermId, SkolemizationError> {
133 let nnf = self.convert_nnf(tm, term, false)?;
135 self.skolemize_inner(tm, nnf)
137 }
138
139 fn convert_nnf(
144 &mut self,
145 tm: &mut TermManager,
146 term: TermId,
147 negated: bool,
148 ) -> Result<TermId, SkolemizationError> {
149 if let Some(&cached) = self.nnf_cache.get(&(term, negated)) {
151 return Ok(cached);
152 }
153
154 let t = tm.get(term).ok_or(SkolemizationError::UnknownTerm(term))?;
155 let kind = t.kind.clone();
156
157 let result = match kind {
158 TermKind::True => {
159 if negated {
160 Ok(tm.mk_false())
161 } else {
162 Ok(tm.mk_true())
163 }
164 }
165 TermKind::False => {
166 if negated {
167 Ok(tm.mk_true())
168 } else {
169 Ok(tm.mk_false())
170 }
171 }
172 TermKind::Var(_)
173 | TermKind::IntConst(_)
174 | TermKind::RealConst(_)
175 | TermKind::BitVecConst { .. }
176 | TermKind::StringLit(_) => {
177 if negated {
178 Ok(tm.mk_not(term))
179 } else {
180 Ok(term)
181 }
182 }
183 TermKind::Not(arg) => {
184 self.convert_nnf(tm, arg, !negated)
186 }
187 TermKind::And(args) => {
188 let mut nnf_args = Vec::with_capacity(args.len());
189 for &a in &args {
190 nnf_args.push(self.convert_nnf(tm, a, negated)?);
191 }
192 if negated {
193 Ok(tm.mk_or(nnf_args))
195 } else {
196 Ok(tm.mk_and(nnf_args))
197 }
198 }
199 TermKind::Or(args) => {
200 let mut nnf_args = Vec::with_capacity(args.len());
201 for &a in &args {
202 nnf_args.push(self.convert_nnf(tm, a, negated)?);
203 }
204 if negated {
205 Ok(tm.mk_and(nnf_args))
207 } else {
208 Ok(tm.mk_or(nnf_args))
209 }
210 }
211 TermKind::Implies(lhs, rhs) => {
212 let lhs_nnf = self.convert_nnf(tm, lhs, !negated)?;
215 let rhs_nnf = self.convert_nnf(tm, rhs, negated)?;
216 if negated {
217 Ok(tm.mk_and([lhs_nnf, rhs_nnf]))
218 } else {
219 Ok(tm.mk_or([lhs_nnf, rhs_nnf]))
220 }
221 }
222 TermKind::Xor(lhs, rhs) => {
223 let a = self.convert_nnf(tm, lhs, false)?;
225 let b = self.convert_nnf(tm, rhs, false)?;
226 let not_a = self.convert_nnf(tm, lhs, true)?;
227 let not_b = self.convert_nnf(tm, rhs, true)?;
228 let clause1 = tm.mk_or([a, b]);
229 let clause2 = tm.mk_or([not_a, not_b]);
230 let xor_nnf = tm.mk_and([clause1, clause2]);
231 if negated {
232 self.convert_nnf(tm, xor_nnf, true)
236 } else {
237 Ok(xor_nnf)
238 }
239 }
240 TermKind::Forall {
241 vars,
242 body,
243 patterns,
244 } => {
245 let body_nnf = self.convert_nnf(tm, body, negated)?;
246
247 let var_names: Vec<(String, SortId)> = vars
249 .iter()
250 .map(|(s, sort)| (tm.resolve_str(*s).to_string(), *sort))
251 .collect();
252
253 if negated {
254 Ok(tm.mk_exists_with_patterns(
256 var_names
257 .iter()
258 .map(|(s, sort): &(String, SortId)| (s.as_str(), *sort)),
259 body_nnf,
260 patterns,
261 ))
262 } else {
263 Ok(tm.mk_forall_with_patterns(
264 var_names
265 .iter()
266 .map(|(s, sort): &(String, SortId)| (s.as_str(), *sort)),
267 body_nnf,
268 patterns,
269 ))
270 }
271 }
272 TermKind::Exists {
273 vars,
274 body,
275 patterns,
276 } => {
277 let body_nnf = self.convert_nnf(tm, body, negated)?;
278
279 let var_names: Vec<(String, SortId)> = vars
281 .iter()
282 .map(|(s, sort)| (tm.resolve_str(*s).to_string(), *sort))
283 .collect();
284
285 if negated {
286 Ok(tm.mk_forall_with_patterns(
288 var_names
289 .iter()
290 .map(|(s, sort): &(String, SortId)| (s.as_str(), *sort)),
291 body_nnf,
292 patterns,
293 ))
294 } else {
295 Ok(tm.mk_exists_with_patterns(
296 var_names
297 .iter()
298 .map(|(s, sort): &(String, SortId)| (s.as_str(), *sort)),
299 body_nnf,
300 patterns,
301 ))
302 }
303 }
304 TermKind::Eq(_, _)
305 | TermKind::Distinct(_)
306 | TermKind::Lt(_, _)
307 | TermKind::Le(_, _)
308 | TermKind::Gt(_, _)
309 | TermKind::Ge(_, _)
310 | TermKind::Apply { .. }
311 | TermKind::Ite(_, _, _) => {
312 if negated {
314 Ok(tm.mk_not(term))
315 } else {
316 Ok(term)
317 }
318 }
319 _ => {
322 if negated {
323 Ok(tm.mk_not(term))
324 } else {
325 Ok(term)
326 }
327 }
328 }?;
329
330 self.nnf_cache.insert((term, negated), result);
331 Ok(result)
332 }
333
334 fn skolemize_inner(
343 &mut self,
344 tm: &mut TermManager,
345 term: TermId,
346 ) -> Result<TermId, SkolemizationError> {
347 if let Some(&cached) = self.skolem_cache.get(&term) {
349 return Ok(cached);
350 }
351
352 let t = tm.get(term).ok_or(SkolemizationError::UnknownTerm(term))?;
353 let kind = t.kind.clone();
354
355 let result = match kind {
356 TermKind::True
358 | TermKind::False
359 | TermKind::IntConst(_)
360 | TermKind::RealConst(_)
361 | TermKind::BitVecConst { .. }
362 | TermKind::StringLit(_) => Ok(term),
363
364 TermKind::Var(_) => {
366 if let Some(&replacement) = self.skolem_map.get(&term) {
367 Ok(replacement)
368 } else {
369 Ok(term)
370 }
371 }
372
373 TermKind::Forall {
375 vars,
376 body,
377 patterns: _,
378 } => {
379 let push_count = vars.len();
381
382 for (name_spur, sort) in &vars {
386 let var_name = tm.resolve_str(*name_spur).to_string();
387 let var_id = tm.mk_var(&var_name, *sort);
388 self.outer_universals.push((*sort, var_id));
389 }
390
391 let sk_body = self.skolemize_inner(tm, body)?;
393
394 for _ in 0..push_count {
396 self.outer_universals.pop();
397 }
398
399 let var_names: Vec<(String, SortId)> = vars
402 .iter()
403 .map(|(s, sort)| (tm.resolve_str(*s).to_string(), *sort))
404 .collect();
405
406 Ok(tm.mk_forall(
407 var_names
408 .iter()
409 .map(|(s, sort): &(String, SortId)| (s.as_str(), *sort)),
410 sk_body,
411 ))
412 }
413
414 TermKind::Exists { vars, body, .. } => {
416 for (name_spur, sort) in &vars {
418 let var_name = tm.resolve_str(*name_spur).to_string();
419 let var_id = tm.mk_var(&var_name, *sort);
420
421 let skolem_term = if self.outer_universals.is_empty() {
422 self.mk_skolem_constant(tm, *sort)?
424 } else {
425 self.mk_skolem_function(tm, *sort)?
428 };
429
430 self.skolem_map.insert(var_id, skolem_term);
431 }
432
433 self.skolemize_inner(tm, body)
435 }
436
437 TermKind::Not(arg) => {
439 let sk_arg = self.skolemize_inner(tm, arg)?;
440 Ok(tm.mk_not(sk_arg))
441 }
442 TermKind::And(args) => {
443 let mut sk_args = Vec::with_capacity(args.len());
444 for &a in &args {
445 sk_args.push(self.skolemize_inner(tm, a)?);
446 }
447 Ok(tm.mk_and(sk_args))
448 }
449 TermKind::Or(args) => {
450 let mut sk_args = Vec::with_capacity(args.len());
451 for &a in &args {
452 sk_args.push(self.skolemize_inner(tm, a)?);
453 }
454 Ok(tm.mk_or(sk_args))
455 }
456 TermKind::Implies(lhs, rhs) => {
457 let sk_lhs = self.skolemize_inner(tm, lhs)?;
458 let sk_rhs = self.skolemize_inner(tm, rhs)?;
459 Ok(tm.mk_implies(sk_lhs, sk_rhs))
460 }
461 TermKind::Xor(lhs, rhs) => {
462 let sk_lhs = self.skolemize_inner(tm, lhs)?;
463 let sk_rhs = self.skolemize_inner(tm, rhs)?;
464 Ok(tm.mk_xor(sk_lhs, sk_rhs))
465 }
466 TermKind::Ite(cond, then_br, else_br) => {
467 let sk_cond = self.skolemize_inner(tm, cond)?;
468 let sk_then = self.skolemize_inner(tm, then_br)?;
469 let sk_else = self.skolemize_inner(tm, else_br)?;
470 Ok(tm.mk_ite(sk_cond, sk_then, sk_else))
471 }
472
473 TermKind::Eq(lhs, rhs) => {
475 let sk_lhs = self.skolemize_inner(tm, lhs)?;
476 let sk_rhs = self.skolemize_inner(tm, rhs)?;
477 Ok(tm.mk_eq(sk_lhs, sk_rhs))
478 }
479 TermKind::Distinct(args) => {
480 let mut sk_args = Vec::with_capacity(args.len());
481 for &a in &args {
482 sk_args.push(self.skolemize_inner(tm, a)?);
483 }
484 Ok(tm.mk_distinct(sk_args))
485 }
486 TermKind::Lt(lhs, rhs) => {
487 let sk_lhs = self.skolemize_inner(tm, lhs)?;
488 let sk_rhs = self.skolemize_inner(tm, rhs)?;
489 Ok(tm.mk_lt(sk_lhs, sk_rhs))
490 }
491 TermKind::Le(lhs, rhs) => {
492 let sk_lhs = self.skolemize_inner(tm, lhs)?;
493 let sk_rhs = self.skolemize_inner(tm, rhs)?;
494 Ok(tm.mk_le(sk_lhs, sk_rhs))
495 }
496 TermKind::Gt(lhs, rhs) => {
497 let sk_lhs = self.skolemize_inner(tm, lhs)?;
498 let sk_rhs = self.skolemize_inner(tm, rhs)?;
499 Ok(tm.mk_gt(sk_lhs, sk_rhs))
500 }
501 TermKind::Ge(lhs, rhs) => {
502 let sk_lhs = self.skolemize_inner(tm, lhs)?;
503 let sk_rhs = self.skolemize_inner(tm, rhs)?;
504 Ok(tm.mk_ge(sk_lhs, sk_rhs))
505 }
506
507 TermKind::Neg(arg) => {
509 let sk_arg = self.skolemize_inner(tm, arg)?;
510 Ok(tm.mk_neg(sk_arg))
511 }
512 TermKind::Add(args) => {
513 let mut sk_args = Vec::with_capacity(args.len());
514 for &a in &args {
515 sk_args.push(self.skolemize_inner(tm, a)?);
516 }
517 Ok(tm.mk_add(sk_args))
518 }
519 TermKind::Sub(lhs, rhs) => {
520 let sk_lhs = self.skolemize_inner(tm, lhs)?;
521 let sk_rhs = self.skolemize_inner(tm, rhs)?;
522 Ok(tm.mk_sub(sk_lhs, sk_rhs))
523 }
524 TermKind::Mul(args) => {
525 let mut sk_args = Vec::with_capacity(args.len());
526 for &a in &args {
527 sk_args.push(self.skolemize_inner(tm, a)?);
528 }
529 Ok(tm.mk_mul(sk_args))
530 }
531 TermKind::Div(lhs, rhs) => {
532 let sk_lhs = self.skolemize_inner(tm, lhs)?;
533 let sk_rhs = self.skolemize_inner(tm, rhs)?;
534 Ok(tm.mk_div(sk_lhs, sk_rhs))
535 }
536 TermKind::Mod(lhs, rhs) => {
537 let sk_lhs = self.skolemize_inner(tm, lhs)?;
538 let sk_rhs = self.skolemize_inner(tm, rhs)?;
539 Ok(tm.mk_mod(sk_lhs, sk_rhs))
540 }
541
542 TermKind::Apply { func, args } => {
544 let mut sk_args = Vec::with_capacity(args.len());
545 for &a in &args {
546 sk_args.push(self.skolemize_inner(tm, a)?);
547 }
548 let func_name = tm.resolve_str(func).to_string();
549 let result_sort = tm
550 .get(term)
551 .ok_or(SkolemizationError::UnknownTerm(term))?
552 .sort;
553 Ok(tm.mk_apply(&func_name, sk_args, result_sort))
554 }
555
556 TermKind::Select(arr, idx) => {
558 let sk_arr = self.skolemize_inner(tm, arr)?;
559 let sk_idx = self.skolemize_inner(tm, idx)?;
560 Ok(tm.mk_select(sk_arr, sk_idx))
561 }
562 TermKind::Store(arr, idx, val) => {
563 let sk_arr = self.skolemize_inner(tm, arr)?;
564 let sk_idx = self.skolemize_inner(tm, idx)?;
565 let sk_val = self.skolemize_inner(tm, val)?;
566 Ok(tm.mk_store(sk_arr, sk_idx, sk_val))
567 }
568
569 TermKind::Let { bindings, body } => {
571 let mut sk_bindings = Vec::with_capacity(bindings.len());
572 for (name, val) in &bindings {
573 let sk_val = self.skolemize_inner(tm, *val)?;
574 let name_str = tm.resolve_str(*name).to_string();
575 sk_bindings.push((name_str, sk_val));
576 }
577 let sk_body = self.skolemize_inner(tm, body)?;
578 Ok(tm.mk_let(
579 sk_bindings
580 .iter()
581 .map(|(n, v): &(String, TermId)| (n.as_str(), *v)),
582 sk_body,
583 ))
584 }
585
586 _ => Ok(term),
592 }?;
593
594 self.skolem_cache.insert(term, result);
595 Ok(result)
596 }
597
598 fn mk_skolem_constant(
603 &mut self,
604 tm: &mut TermManager,
605 sort: SortId,
606 ) -> Result<TermId, SkolemizationError> {
607 let counter = self.skolem_counter;
608 self.skolem_counter = self
609 .skolem_counter
610 .checked_add(1)
611 .ok_or(SkolemizationError::CounterOverflow)?;
612
613 let name = format!("sk!{}", counter);
614 let term = tm.mk_var(&name, sort);
615
616 self.skolem_symbols.push(SkolemSymbol {
617 name,
618 sort,
619 term,
620 arg_sorts: Vec::new(),
621 });
622
623 Ok(term)
624 }
625
626 fn mk_skolem_function(
632 &mut self,
633 tm: &mut TermManager,
634 result_sort: SortId,
635 ) -> Result<TermId, SkolemizationError> {
636 let counter = self.skolem_counter;
637 self.skolem_counter = self
638 .skolem_counter
639 .checked_add(1)
640 .ok_or(SkolemizationError::CounterOverflow)?;
641
642 let name = format!("skf!{}", counter);
643
644 let arg_sorts: Vec<SortId> = self.outer_universals.iter().map(|(s, _)| *s).collect();
646 let arg_terms: Vec<TermId> = self.outer_universals.iter().map(|(_, t)| *t).collect();
647
648 let term = tm.mk_apply(&name, arg_terms, result_sort);
650
651 self.skolem_symbols.push(SkolemSymbol {
652 name,
653 sort: result_sort,
654 term,
655 arg_sorts,
656 });
657
658 Ok(term)
659 }
660}
661
662#[cfg(test)]
663mod tests {
664 use super::*;
665 use num_bigint::BigInt;
666
667 fn is_existential_free(tm: &TermManager, term: TermId) -> bool {
669 let Some(t) = tm.get(term) else {
670 return true;
671 };
672 match &t.kind {
673 TermKind::Exists { .. } => false,
674 TermKind::Not(arg) => is_existential_free(tm, *arg),
675 TermKind::And(args) | TermKind::Or(args) => {
676 args.iter().all(|&a| is_existential_free(tm, a))
677 }
678 TermKind::Implies(lhs, rhs) => {
679 is_existential_free(tm, *lhs) && is_existential_free(tm, *rhs)
680 }
681 TermKind::Forall { body, .. } => is_existential_free(tm, *body),
682 _ => true,
683 }
684 }
685
686 #[test]
687 fn test_skolemize_simple_exists() {
688 let mut tm = TermManager::new();
691 let bool_sort = tm.sorts.bool_sort;
692 let x = tm.mk_var("x", bool_sort);
693 let exists = tm.mk_exists([("x", bool_sort)], x);
694
695 let mut ctx = SkolemizationContext::new();
696 let result = ctx.skolemize(&mut tm, exists);
697 assert!(result.is_ok());
698 let result_id = result.expect("skolemize should succeed");
699
700 assert!(is_existential_free(&tm, result_id));
702
703 assert_eq!(ctx.skolem_count(), 1);
705 let sym = &ctx.skolem_symbols()[0];
706 assert_eq!(sym.name, "sk!0");
707 assert!(sym.arg_sorts.is_empty());
708 }
709
710 #[test]
711 fn test_skolemize_exists_with_body() {
712 let mut tm = TermManager::new();
715 let int_sort = tm.sorts.int_sort;
716 let x = tm.mk_var("x", int_sort);
717 let zero = tm.mk_int(BigInt::from(0));
718 let gt = tm.mk_gt(x, zero);
719 let exists = tm.mk_exists([("x", int_sort)], gt);
720
721 let mut ctx = SkolemizationContext::new();
722 let result = ctx.skolemize(&mut tm, exists);
723 assert!(result.is_ok());
724 let result_id = result.expect("skolemize should succeed");
725
726 assert!(is_existential_free(&tm, result_id));
727 assert_eq!(ctx.skolem_count(), 1);
728 }
729
730 #[test]
731 fn test_skolemize_forall_exists() {
732 let mut tm = TermManager::new();
735 let int_sort = tm.sorts.int_sort;
736 let x = tm.mk_var("x", int_sort);
737 let y = tm.mk_var("y", int_sort);
738 let gt = tm.mk_gt(x, y);
739 let exists = tm.mk_exists([("x", int_sort)], gt);
740 let forall = tm.mk_forall([("y", int_sort)], exists);
741
742 let mut ctx = SkolemizationContext::new();
743 let result = ctx.skolemize(&mut tm, forall);
744 assert!(result.is_ok());
745 let result_id = result.expect("skolemize should succeed");
746
747 assert!(is_existential_free(&tm, result_id));
748
749 assert_eq!(ctx.skolem_count(), 1);
751 let sym = &ctx.skolem_symbols()[0];
752 assert_eq!(sym.name, "skf!0");
753 assert_eq!(sym.arg_sorts.len(), 1);
754 assert_eq!(sym.arg_sorts[0], int_sort);
755 }
756
757 #[test]
758 fn test_skolemize_nested_exists() {
759 let mut tm = TermManager::new();
762 let int_sort = tm.sorts.int_sort;
763 let x = tm.mk_var("x", int_sort);
764 let y = tm.mk_var("y", int_sort);
765 let gt = tm.mk_gt(x, y);
766 let exists_y = tm.mk_exists([("y", int_sort)], gt);
767 let exists_x = tm.mk_exists([("x", int_sort)], exists_y);
768
769 let mut ctx = SkolemizationContext::new();
770 let result = ctx.skolemize(&mut tm, exists_x);
771 assert!(result.is_ok());
772 let result_id = result.expect("skolemize should succeed");
773
774 assert!(is_existential_free(&tm, result_id));
775 assert_eq!(ctx.skolem_count(), 2);
776 assert!(ctx.skolem_symbols()[0].arg_sorts.is_empty());
778 assert!(ctx.skolem_symbols()[1].arg_sorts.is_empty());
779 }
780
781 #[test]
782 fn test_skolemize_negated_forall() {
783 let mut tm = TermManager::new();
787 let bool_sort = tm.sorts.bool_sort;
788 let x = tm.mk_var("x", bool_sort);
789 let forall = tm.mk_forall([("x", bool_sort)], x);
790 let neg_forall = tm.mk_not(forall);
791
792 let mut ctx = SkolemizationContext::new();
793 let result = ctx.skolemize(&mut tm, neg_forall);
794 assert!(result.is_ok());
795 let result_id = result.expect("skolemize should succeed");
796
797 assert!(is_existential_free(&tm, result_id));
798 assert_eq!(ctx.skolem_count(), 1);
799 }
800
801 #[test]
802 fn test_skolemize_multiple_universal_vars() {
803 let mut tm = TermManager::new();
806 let int_sort = tm.sorts.int_sort;
807 let x = tm.mk_var("x", int_sort);
808 let y = tm.mk_var("y", int_sort);
809 let z = tm.mk_var("z", int_sort);
810 let sum = tm.mk_add([y, z]);
811 let gt = tm.mk_gt(x, sum);
812 let exists = tm.mk_exists([("x", int_sort)], gt);
813 let forall = tm.mk_forall([("y", int_sort), ("z", int_sort)], exists);
814
815 let mut ctx = SkolemizationContext::new();
816 let result = ctx.skolemize(&mut tm, forall);
817 assert!(result.is_ok());
818 let result_id = result.expect("skolemize should succeed");
819
820 assert!(is_existential_free(&tm, result_id));
821 assert_eq!(ctx.skolem_count(), 1);
822 let sym = &ctx.skolem_symbols()[0];
823 assert_eq!(sym.name, "skf!0");
824 assert_eq!(sym.arg_sorts.len(), 2);
825 assert_eq!(sym.arg_sorts[0], int_sort);
826 assert_eq!(sym.arg_sorts[1], int_sort);
827 }
828
829 #[test]
830 fn test_skolemize_preserves_ground_terms() {
831 let mut tm = TermManager::new();
833 let bool_sort = tm.sorts.bool_sort;
834 let p = tm.mk_var("p", bool_sort);
835 let q = tm.mk_var("q", bool_sort);
836 let and = tm.mk_and([p, q]);
837
838 let mut ctx = SkolemizationContext::new();
839 let result = ctx.skolemize(&mut tm, and);
840 assert!(result.is_ok());
841 let result_id = result.expect("skolemize should succeed");
842
843 assert_eq!(ctx.skolem_count(), 0);
845 assert_eq!(result_id, and);
847 }
848
849 #[test]
850 fn test_skolemize_mixed_sorts() {
851 let mut tm = TermManager::new();
854 let int_sort = tm.sorts.int_sort;
855 let bool_sort = tm.sorts.bool_sort;
856 let x = tm.mk_var("x", bool_sort);
857 let y = tm.mk_var("y", int_sort);
858 let zero = tm.mk_int(BigInt::from(0));
859 let gt = tm.mk_gt(y, zero);
860 let and = tm.mk_and([x, gt]);
861 let exists = tm.mk_exists([("x", bool_sort)], and);
862 let forall = tm.mk_forall([("y", int_sort)], exists);
863
864 let mut ctx = SkolemizationContext::new();
865 let result = ctx.skolemize(&mut tm, forall);
866 assert!(result.is_ok());
867 let result_id = result.expect("skolemize should succeed");
868
869 assert!(is_existential_free(&tm, result_id));
870 assert_eq!(ctx.skolem_count(), 1);
871 let sym = &ctx.skolem_symbols()[0];
872 assert_eq!(sym.sort, bool_sort);
873 assert_eq!(sym.arg_sorts.len(), 1);
874 assert_eq!(sym.arg_sorts[0], int_sort);
875 }
876
877 #[test]
878 fn test_nnf_conversion_via_skolemize() {
879 let mut tm = TermManager::new();
882 let bool_sort = tm.sorts.bool_sort;
883 let p = tm.mk_var("p", bool_sort);
884 let q = tm.mk_var("q", bool_sort);
885 let and = tm.mk_and([p, q]);
886 let neg = tm.mk_not(and);
887
888 let mut ctx = SkolemizationContext::new();
889 let result = ctx.skolemize(&mut tm, neg);
890 assert!(result.is_ok());
891 let result_id = result.expect("skolemize should succeed");
892
893 let t = tm.get(result_id);
895 assert!(t.is_some());
896 assert!(matches!(t.map(|t| &t.kind), Some(TermKind::Or(_))));
897 }
898
899 #[test]
900 fn test_skolemize_error_on_unknown_term() {
901 let mut tm = TermManager::new();
902 let bogus = TermId::new(999_999);
903
904 let mut ctx = SkolemizationContext::new();
905 let result = ctx.skolemize(&mut tm, bogus);
906 assert!(result.is_err());
907 }
908}