1#[allow(unused_imports)]
4use crate::prelude::*;
5use crate::solver::{Solver, SolverResult};
6use oxiz_core::ast::{TermId, TermKind, TermManager};
7#[cfg(feature = "std")]
8use oxiz_core::error::Result;
9#[cfg(feature = "std")]
10use oxiz_core::smtlib::{Command, parse_script};
11use oxiz_core::sort::SortId;
12#[cfg(feature = "std")]
13use std::path::{Path, PathBuf};
14
15#[derive(Debug, Clone)]
17struct DeclaredConst {
18 term: TermId,
20 sort: SortId,
22 name: String,
24}
25
26#[derive(Debug, Clone)]
28struct DeclaredFun {
29 name: String,
31 arg_sorts: Vec<SortId>,
33 ret_sort: SortId,
35}
36
37#[derive(Debug)]
82pub struct Context {
83 pub terms: TermManager,
85 solver: Solver,
87 logic: Option<String>,
89 assertions: Vec<TermId>,
91 assertion_stack: Vec<usize>,
93 declared_consts: Vec<DeclaredConst>,
95 const_stack: Vec<usize>,
97 const_name_to_index: crate::prelude::HashMap<String, usize>,
99 declared_funs: Vec<DeclaredFun>,
101 fun_stack: Vec<usize>,
103 fun_name_to_index: crate::prelude::HashMap<String, usize>,
105 last_result: Option<SolverResult>,
107 options: crate::prelude::HashMap<String, String>,
109 #[cfg(feature = "std")]
115 proof_log_path: Option<PathBuf>,
116}
117
118impl Default for Context {
119 fn default() -> Self {
120 Self::new()
121 }
122}
123
124impl Context {
125 #[must_use]
127 pub fn new() -> Self {
128 Self {
129 terms: TermManager::new(),
130 solver: Solver::new(),
131 logic: None,
132 assertions: Vec::new(),
133 assertion_stack: Vec::new(),
134 declared_consts: Vec::new(),
135 const_stack: Vec::new(),
136 const_name_to_index: crate::prelude::HashMap::new(),
137 declared_funs: Vec::new(),
138 fun_stack: Vec::new(),
139 fun_name_to_index: crate::prelude::HashMap::new(),
140 last_result: None,
141 options: crate::prelude::HashMap::new(),
142 #[cfg(feature = "std")]
143 proof_log_path: None,
144 }
145 }
146
147 #[cfg(feature = "std")]
154 pub fn set_proof_log_path(&mut self, path: Option<PathBuf>) {
155 self.proof_log_path = path;
156 }
157
158 #[cfg(feature = "std")]
160 #[must_use]
161 pub fn proof_log_path(&self) -> Option<&Path> {
162 self.proof_log_path.as_deref()
163 }
164
165 #[cfg(feature = "std")]
175 pub fn verify_proof_log(
176 path: &Path,
177 ) -> std::result::Result<oxiz_proof::replay::VerificationResult, oxiz_proof::replay::ProofError>
178 {
179 oxiz_proof::replay::ProofReplayer::replay_from_file(path)
180 }
181
182 pub fn declare_const(&mut self, name: &str, sort: SortId) -> TermId {
184 let term = self.terms.mk_var(name, sort);
185 let index = self.declared_consts.len();
186 self.declared_consts.push(DeclaredConst {
187 term,
188 sort,
189 name: name.to_string(),
190 });
191 self.const_name_to_index.insert(name.to_string(), index);
192 term
193 }
194
195 pub fn declare_fun(&mut self, name: &str, arg_sorts: Vec<SortId>, ret_sort: SortId) {
200 let index = self.declared_funs.len();
201 self.declared_funs.push(DeclaredFun {
202 name: name.to_string(),
203 arg_sorts,
204 ret_sort,
205 });
206 self.fun_name_to_index.insert(name.to_string(), index);
207 }
208
209 pub fn get_fun_signature(&self, name: &str) -> Option<(Vec<SortId>, SortId)> {
211 self.fun_name_to_index.get(name).and_then(|&idx| {
212 self.declared_funs
213 .get(idx)
214 .map(|f| (f.arg_sorts.clone(), f.ret_sort))
215 })
216 }
217
218 pub fn set_logic(&mut self, logic: &str) {
220 self.logic = Some(logic.to_string());
221 self.solver.set_logic(logic);
222 }
223
224 #[must_use]
226 pub fn logic(&self) -> Option<&str> {
227 self.logic.as_deref()
228 }
229
230 pub fn assert(&mut self, term: TermId) {
232 self.assertions.push(term);
233 self.solver.assert(term, &mut self.terms);
234 }
235
236 pub fn check_sat(&mut self) -> SolverResult {
238 let result = self.solver.check(&mut self.terms);
239 self.last_result = Some(result);
240
241 #[cfg(feature = "std")]
243 if let Some(ref path) = self.proof_log_path.clone() {
244 if let Err(e) = self.write_proof_log(path, result) {
245 #[cfg(feature = "tracing")]
247 tracing::warn!("proof log write failed for {:?}: {}", path, e);
248 let _ = e;
249 }
250 }
251
252 result
253 }
254
255 #[cfg(feature = "std")]
261 fn write_proof_log(
262 &self,
263 path: &Path,
264 result: SolverResult,
265 ) -> std::result::Result<(), oxiz_proof::logging::LoggingError> {
266 use oxiz_proof::logging::ProofLogger;
267 use oxiz_proof::proof::{ProofNodeId, ProofStep};
268 use smallvec::SmallVec;
269
270 let mut logger = ProofLogger::create(path)?;
271
272 match result {
273 SolverResult::Unsat => {
274 if let Some(proof) = self.solver.get_proof() {
275 let mut counter: u32 = 0;
276 for step in proof.steps() {
277 let entry = match step {
278 crate::solver::ProofStep::Input { index, .. } => ProofStep::Axiom {
279 conclusion: format!("input-clause-{}", index),
280 },
281 crate::solver::ProofStep::Resolution {
282 index,
283 left,
284 right,
285 pivot,
286 ..
287 } => {
288 let mut premises: SmallVec<[ProofNodeId; 4]> = SmallVec::new();
289 premises.push(ProofNodeId(*left));
290 premises.push(ProofNodeId(*right));
291 let mut args: SmallVec<[String; 2]> = SmallVec::new();
292 args.push(format!("{:?}", pivot));
293 ProofStep::Inference {
294 rule: "resolution".to_string(),
295 premises,
296 conclusion: format!("resolution-{}", index),
297 args,
298 }
299 }
300 crate::solver::ProofStep::TheoryLemma { index, theory, .. } => {
301 ProofStep::Axiom {
302 conclusion: format!("theory-lemma-{}-{}", theory, index),
303 }
304 }
305 };
306 logger.log_step(ProofNodeId(counter), &entry)?;
307 counter += 1;
308 }
309 if counter == 0 {
310 logger.log_step(
312 ProofNodeId(0),
313 &ProofStep::Axiom {
314 conclusion: "unsat".to_string(),
315 },
316 )?;
317 }
318 } else {
319 logger.log_step(
320 ProofNodeId(0),
321 &ProofStep::Axiom {
322 conclusion: "unsat".to_string(),
323 },
324 )?;
325 }
326 }
327 SolverResult::Sat => {
328 logger.log_step(
329 ProofNodeId(0),
330 &ProofStep::Axiom {
331 conclusion: "sat".to_string(),
332 },
333 )?;
334 }
335 SolverResult::Unknown => {
336 logger.log_step(
337 ProofNodeId(0),
338 &ProofStep::Axiom {
339 conclusion: "unknown".to_string(),
340 },
341 )?;
342 }
343 }
344
345 logger.flush()?;
346 logger.close()
347 }
348
349 pub fn get_model(&self) -> Option<Vec<(String, String, String)>> {
352 if self.last_result != Some(SolverResult::Sat) {
353 return None;
354 }
355
356 let mut model = Vec::new();
357 let solver_model = self.solver.model()?;
358
359 for decl in &self.declared_consts {
360 let value = if let Some(val) = solver_model.get(decl.term) {
361 self.format_value(val)
362 } else {
363 self.default_value(decl.sort)
365 };
366 let sort_name = self.format_sort_name(decl.sort);
367 model.push((decl.name.clone(), sort_name, value));
368 }
369
370 Some(model)
371 }
372
373 fn format_sort_name(&self, sort: SortId) -> String {
375 if sort == self.terms.sorts.bool_sort {
376 "Bool".to_string()
377 } else if sort == self.terms.sorts.int_sort {
378 "Int".to_string()
379 } else if sort == self.terms.sorts.real_sort {
380 "Real".to_string()
381 } else if let Some(s) = self.terms.sorts.get(sort) {
382 if let Some(w) = s.bitvec_width() {
383 format!("(_ BitVec {})", w)
384 } else {
385 "Unknown".to_string()
386 }
387 } else {
388 "Unknown".to_string()
389 }
390 }
391
392 fn format_value(&self, term: TermId) -> String {
394 match self.terms.get(term).map(|t| &t.kind) {
395 Some(TermKind::True) => "true".to_string(),
396 Some(TermKind::False) => "false".to_string(),
397 Some(TermKind::IntConst(n)) => n.to_string(),
398 Some(TermKind::RealConst(r)) => {
399 if *r.denom() == 1 {
400 format!("{}.0", r.numer())
401 } else {
402 format!("(/ {} {})", r.numer(), r.denom())
403 }
404 }
405 Some(TermKind::BitVecConst { value, width }) => {
406 format!(
407 "#b{:0>width$}",
408 format!("{:b}", value),
409 width = *width as usize
410 )
411 }
412 _ => "?".to_string(),
413 }
414 }
415
416 fn default_value(&self, sort: SortId) -> String {
418 if sort == self.terms.sorts.bool_sort {
419 "false".to_string()
420 } else if sort == self.terms.sorts.int_sort {
421 "0".to_string()
422 } else if sort == self.terms.sorts.real_sort {
423 "0.0".to_string()
424 } else if let Some(s) = self.terms.sorts.get(sort) {
425 if let Some(w) = s.bitvec_width() {
426 format!("#b{:0>width$}", "0", width = w as usize)
427 } else {
428 "?".to_string()
429 }
430 } else {
431 "?".to_string()
432 }
433 }
434
435 pub fn format_model(&self) -> String {
437 match self.get_model() {
438 None => "(error \"No model available\")".to_string(),
439 Some(model) if model.is_empty() => "(model)".to_string(),
440 Some(model) => {
441 let mut lines = vec!["(model".to_string()];
442 for (name, sort, value) in model {
443 lines.push(format!(" (define-fun {} () {} {})", name, sort, value));
444 }
445 lines.push(")".to_string());
446 lines.join("\n")
447 }
448 }
449 }
450
451 pub fn push(&mut self) {
453 self.assertion_stack.push(self.assertions.len());
454 self.const_stack.push(self.declared_consts.len());
455 self.fun_stack.push(self.declared_funs.len());
456 self.solver.push();
457 }
458
459 pub fn pop(&mut self) {
461 if let Some(len) = self.assertion_stack.pop() {
462 self.assertions.truncate(len);
463 if let Some(const_len) = self.const_stack.pop() {
464 while self.declared_consts.len() > const_len {
466 if let Some(decl) = self.declared_consts.pop() {
467 self.const_name_to_index.remove(&decl.name);
468 }
469 }
470 }
471 if let Some(fun_len) = self.fun_stack.pop() {
472 while self.declared_funs.len() > fun_len {
474 if let Some(decl) = self.declared_funs.pop() {
475 self.fun_name_to_index.remove(&decl.name);
476 }
477 }
478 }
479 self.solver.pop();
480 }
481 }
482
483 pub fn reset(&mut self) {
485 self.solver.reset();
486 self.assertions.clear();
487 self.assertion_stack.clear();
488 self.declared_consts.clear();
489 self.const_stack.clear();
490 self.const_name_to_index.clear();
491 self.declared_funs.clear();
492 self.fun_stack.clear();
493 self.fun_name_to_index.clear();
494 self.logic = None;
495 self.last_result = None;
496 self.options.clear();
497 }
498
499 pub fn reset_assertions(&mut self) {
501 self.solver.reset();
502 self.assertions.clear();
503 self.assertion_stack.clear();
504 self.last_result = None;
508 }
509
510 #[must_use]
512 pub fn get_assertions(&self) -> &[TermId] {
513 &self.assertions
514 }
515
516 #[cfg(feature = "std")]
518 pub fn format_assertions(&self) -> String {
519 if self.assertions.is_empty() {
520 return "()".to_string();
521 }
522 let printer = oxiz_core::smtlib::Printer::new(&self.terms);
523 let mut parts = Vec::new();
524 for &term in &self.assertions {
525 parts.push(printer.print_term(term));
526 }
527 format!("({})", parts.join("\n "))
528 }
529
530 pub fn set_option(&mut self, key: &str, value: &str) {
532 self.options.insert(key.to_string(), value.to_string());
533
534 match key {
536 "produce-proofs" => {
537 let mut config = self.solver.config().clone();
538 config.proof = value == "true";
539 self.solver.set_config(config);
540 }
541 "produce-unsat-cores" => {
542 self.solver.set_produce_unsat_cores(value == "true");
543 }
544 _ => {}
545 }
546 }
547
548 #[must_use]
550 pub fn get_option(&self, key: &str) -> Option<&str> {
551 self.options.get(key).map(String::as_str)
552 }
553
554 fn format_option(&self, key: &str) -> String {
556 match self.get_option(key) {
557 Some(val) => val.to_string(),
558 None => {
559 match key {
561 "produce-models" => "false".to_string(),
562 "produce-unsat-cores" => "false".to_string(),
563 "produce-proofs" => "false".to_string(),
564 "produce-assignments" => "false".to_string(),
565 "print-success" => "true".to_string(),
566 _ => "unsupported".to_string(),
567 }
568 }
569 }
570 }
571
572 pub fn get_assignment(&self) -> String {
575 "()".to_string()
576 }
577
578 pub fn get_proof(&self) -> String {
580 if self.last_result != Some(SolverResult::Unsat) {
581 return "(error \"Proof is only available after unsat result\")".to_string();
582 }
583
584 match self.solver.get_proof() {
585 Some(proof) => proof.format(),
586 None => {
587 "(error \"Proof generation not enabled. Set :produce-proofs to true\")".to_string()
588 }
589 }
590 }
591
592 pub fn get_statistics(&self) -> String {
595 let stats = self.solver.get_statistics();
596 format!(
597 "(:decisions {} :conflicts {} :propagations {} :restarts {} :learned-clauses {} :theory-propagations {} :theory-conflicts {})",
598 stats.decisions,
599 stats.conflicts,
600 stats.propagations,
601 stats.restarts,
602 stats.learned_clauses,
603 stats.theory_propagations,
604 stats.theory_conflicts
605 )
606 }
607
608 fn parse_sort_name(&mut self, name: &str) -> SortId {
610 match name {
611 "Bool" => self.terms.sorts.bool_sort,
612 "Int" => self.terms.sorts.int_sort,
613 "Real" => self.terms.sorts.real_sort,
614 _ => {
615 if let Some(width_str) = name.strip_prefix("BitVec")
617 && let Ok(width) = width_str.trim().parse::<u32>()
618 {
619 return self.terms.sorts.bitvec(width);
620 }
621 self.terms.sorts.bool_sort
623 }
624 }
625 }
626
627 #[cfg(feature = "std")]
629 pub fn execute_script(&mut self, script: &str) -> Result<Vec<String>> {
630 let commands = parse_script(script, &mut self.terms)?;
631 let mut output = Vec::new();
632
633 for cmd in commands {
634 match cmd {
635 Command::SetLogic(logic) => {
636 self.set_logic(&logic);
637 }
638 Command::DeclareConst(name, sort_name) => {
639 let sort = self.parse_sort_name(&sort_name);
640 self.declare_const(&name, sort);
641 }
642 Command::DeclareFun(name, arg_sorts, ret_sort) => {
643 if arg_sorts.is_empty() {
645 let sort = self.parse_sort_name(&ret_sort);
646 self.declare_const(&name, sort);
647 } else {
648 let parsed_arg_sorts: Vec<SortId> =
650 arg_sorts.iter().map(|s| self.parse_sort_name(s)).collect();
651 let parsed_ret_sort = self.parse_sort_name(&ret_sort);
652 self.declare_fun(&name, parsed_arg_sorts, parsed_ret_sort);
653 }
654 }
655 Command::Assert(term) => {
656 self.assert(term);
657 }
658 Command::CheckSat => {
659 let result = self.check_sat();
660 output.push(match result {
661 SolverResult::Sat => "sat".to_string(),
662 SolverResult::Unsat => "unsat".to_string(),
663 SolverResult::Unknown => "unknown".to_string(),
664 });
665 }
666 Command::Push(n) => {
667 for _ in 0..n {
668 self.push();
669 }
670 }
671 Command::Pop(n) => {
672 for _ in 0..n {
673 self.pop();
674 }
675 }
676 Command::Reset => {
677 self.reset();
678 }
679 Command::ResetAssertions => {
680 self.reset_assertions();
681 }
682 Command::Exit => {
683 break;
684 }
685 Command::Echo(msg) => {
686 output.push(msg);
687 }
688 Command::GetModel => {
689 output.push(self.format_model());
690 }
691 Command::GetAssertions => {
692 output.push(self.format_assertions());
693 }
694 Command::GetAssignment => {
695 output.push(self.get_assignment());
696 }
697 Command::GetProof => {
698 output.push(self.get_proof());
699 }
700 Command::GetOption(key) => {
701 output.push(self.format_option(&key));
702 }
703 Command::SetOption(key, value) => {
704 self.set_option(&key, &value);
705 }
706 Command::CheckSatAssuming(assumptions) => {
707 self.push();
709 for assumption in assumptions {
710 self.assert(assumption);
711 }
712 let result = self.check_sat();
713 self.pop();
714 output.push(match result {
715 SolverResult::Sat => "sat".to_string(),
716 SolverResult::Unsat => "unsat".to_string(),
717 SolverResult::Unknown => "unknown".to_string(),
718 });
719 }
720 Command::Simplify(term) => {
721 let simplified = self.terms.simplify(term);
723 let printer = oxiz_core::smtlib::Printer::new(&self.terms);
724 output.push(printer.print_term(simplified));
725 }
726 Command::GetUnsatCore => {
727 if let Some(core) = self.solver.get_unsat_core() {
728 if core.names.is_empty() {
729 output.push("()".to_string());
730 } else {
731 output.push(format!("({})", core.names.join(" ")));
732 }
733 } else {
734 output.push("(error \"No unsat core available\")".to_string());
735 }
736 }
737 Command::GetValue(terms) => {
738 if self.last_result != Some(SolverResult::Sat) {
739 output.push("(error \"No model available\")".to_string());
740 } else if let Some(model) = self.solver.model() {
741 let mut values = Vec::new();
742 for term in terms {
743 let value = model.eval(term, &mut self.terms);
745 let printer = oxiz_core::smtlib::Printer::new(&self.terms);
747 let term_str = printer.print_term(term);
748 let value_str = printer.print_term(value);
749 values.push(format!("({} {})", term_str, value_str));
750 }
751 output.push(format!("({})", values.join("\n ")));
752 } else {
753 output.push("(error \"No model available\")".to_string());
754 }
755 }
756 Command::GetInfo(keyword) => {
757 if keyword == ":all-statistics" {
759 output.push(self.get_statistics());
760 } else {
761 output.push(format!("(error \"Unsupported info keyword: {}\")", keyword));
762 }
763 }
764 Command::SetInfo(_, _)
765 | Command::DeclareSort(_, _)
766 | Command::DefineSort(_, _, _)
767 | Command::DefineFun(_, _, _, _)
768 | Command::DeclareDatatype { .. } => {
769 }
771 }
772 }
773
774 Ok(output)
775 }
776
777 #[must_use]
779 pub fn stats(&self) -> &oxiz_sat::SolverStats {
780 self.solver.stats()
781 }
782}
783
784#[cfg(test)]
785mod tests {
786 use super::*;
787
788 #[test]
789 fn test_context_basic() {
790 let mut ctx = Context::new();
791
792 ctx.set_logic("QF_UF");
793 assert_eq!(ctx.logic(), Some("QF_UF"));
794
795 let t = ctx.terms.mk_true();
796 ctx.assert(t);
797
798 let result = ctx.check_sat();
799 assert_eq!(result, SolverResult::Sat);
800 }
801
802 #[test]
803 fn test_context_push_pop() {
804 let mut ctx = Context::new();
805
806 let t = ctx.terms.mk_true();
807 ctx.assert(t);
808 ctx.push();
809
810 let f = ctx.terms.mk_false();
811 ctx.assert(f);
812
813 let result = ctx.check_sat();
815 assert_eq!(result, SolverResult::Unsat);
816
817 ctx.pop();
818
819 let result = ctx.check_sat();
821 assert_eq!(result, SolverResult::Sat);
822 }
823
824 #[test]
825 fn test_execute_script() {
826 let mut ctx = Context::new();
827
828 let script = r#"
829 (set-logic QF_UF)
830 (declare-const p Bool)
831 (assert p)
832 (check-sat)
833 "#;
834
835 let output = ctx
836 .execute_script(script)
837 .expect("test operation should succeed");
838 assert_eq!(output, vec!["sat"]);
839 }
840
841 #[test]
842 fn test_declare_const() {
843 let mut ctx = Context::new();
844
845 let bool_sort = ctx.terms.sorts.bool_sort;
846 let int_sort = ctx.terms.sorts.int_sort;
847
848 ctx.declare_const("x", bool_sort);
849 ctx.declare_const("y", int_sort);
850
851 let t = ctx.terms.mk_true();
852 ctx.assert(t);
853 let result = ctx.check_sat();
854 assert_eq!(result, SolverResult::Sat);
855
856 let model = ctx.get_model();
858 assert!(model.is_some());
859 let model = model.expect("test operation should succeed");
860 assert_eq!(model.len(), 2);
861 }
862
863 #[test]
864 fn test_format_model() {
865 let mut ctx = Context::new();
866
867 let bool_sort = ctx.terms.sorts.bool_sort;
868 ctx.declare_const("p", bool_sort);
869
870 let t = ctx.terms.mk_true();
871 ctx.assert(t);
872 let _ = ctx.check_sat();
873
874 let model_str = ctx.format_model();
875 assert!(model_str.contains("(model"));
876 assert!(model_str.contains("define-fun p () Bool"));
877 }
878
879 #[test]
880 fn test_get_model_script() {
881 let mut ctx = Context::new();
882
883 let script = r#"
884 (set-logic QF_LIA)
885 (declare-const x Int)
886 (declare-const y Bool)
887 (assert true)
888 (check-sat)
889 (get-model)
890 "#;
891
892 let output = ctx
893 .execute_script(script)
894 .expect("test operation should succeed");
895 assert_eq!(output.len(), 2);
896 assert_eq!(output[0], "sat");
897 assert!(
898 output[1].contains("(model"),
899 "Expected '(model' in: {}",
900 output[1]
901 );
902 }
905
906 #[test]
907 fn test_push_pop_consts() {
908 let mut ctx = Context::new();
909
910 let bool_sort = ctx.terms.sorts.bool_sort;
911 ctx.declare_const("a", bool_sort);
912 ctx.push();
913 ctx.declare_const("b", bool_sort);
914
915 let t = ctx.terms.mk_true();
916 ctx.assert(t);
917 let _ = ctx.check_sat();
918
919 let model = ctx.get_model().expect("test operation should succeed");
920 assert_eq!(model.len(), 2);
921
922 ctx.pop();
923 let _ = ctx.check_sat();
924
925 let model = ctx.get_model().expect("test operation should succeed");
926 assert_eq!(model.len(), 1);
927 assert_eq!(model[0].0, "a");
928 }
929
930 #[test]
931 fn test_get_assertions() {
932 let mut ctx = Context::new();
933
934 let script = r#"
935 (set-logic QF_UF)
936 (declare-const p Bool)
937 (assert p)
938 (assert (not p))
939 (get-assertions)
940 "#;
941
942 let output = ctx
943 .execute_script(script)
944 .expect("test operation should succeed");
945 assert_eq!(output.len(), 1);
946 assert!(output[0].starts_with('('));
947 assert!(output[0].contains("p"));
949 }
950
951 #[test]
952 fn test_check_sat_assuming_script() {
953 let mut ctx = Context::new();
954
955 let script = r#"
956 (set-logic QF_UF)
957 (declare-const p Bool)
958 (declare-const q Bool)
959 (assert p)
960 (check-sat-assuming (q))
961 "#;
962
963 let output = ctx
964 .execute_script(script)
965 .expect("test operation should succeed");
966 assert_eq!(output.len(), 1);
967 assert_eq!(output[0], "sat");
968 }
969
970 #[test]
971 fn test_get_option_script() {
972 let mut ctx = Context::new();
973
974 let script = r#"
975 (set-option :produce-models true)
976 (get-option :produce-models)
977 "#;
978
979 let output = ctx
980 .execute_script(script)
981 .expect("test operation should succeed");
982 assert_eq!(output.len(), 1);
983 assert_eq!(output[0], "true");
984 }
985
986 #[test]
987 fn test_reset_assertions() {
988 let mut ctx = Context::new();
989
990 let script = r#"
991 (set-logic QF_UF)
992 (declare-const p Bool)
993 (assert p)
994 (reset-assertions)
995 (get-assertions)
996 (check-sat)
997 "#;
998
999 let output = ctx
1000 .execute_script(script)
1001 .expect("test operation should succeed");
1002 assert_eq!(output.len(), 2);
1003 assert_eq!(output[0], "()"); assert_eq!(output[1], "sat"); }
1006
1007 #[test]
1008 fn test_simplify_command() {
1009 let mut ctx = Context::new();
1010
1011 let script = r#"
1012 (simplify (+ 1 2))
1013 "#;
1014
1015 let output = ctx
1016 .execute_script(script)
1017 .expect("test operation should succeed");
1018 assert_eq!(output.len(), 1);
1019 assert_eq!(output[0], "3");
1021 }
1022
1023 #[test]
1024 fn test_simplify_complex() {
1025 let mut ctx = Context::new();
1026
1027 let script = r#"
1028 (simplify (* 2 3 4))
1029 "#;
1030
1031 let output = ctx
1032 .execute_script(script)
1033 .expect("test operation should succeed");
1034 assert_eq!(output.len(), 1);
1035 assert_eq!(output[0], "24");
1037 }
1038
1039 #[test]
1040 fn test_get_value() {
1041 let mut ctx = Context::new();
1042
1043 let script = r#"
1044 (set-logic QF_UF)
1045 (declare-const p Bool)
1046 (declare-const q Bool)
1047 (assert p)
1048 (assert (not q))
1049 (check-sat)
1050 (get-value (p q (and p q) (or p q)))
1051 "#;
1052
1053 let output = ctx
1054 .execute_script(script)
1055 .expect("test operation should succeed");
1056 assert_eq!(output.len(), 2);
1057 assert_eq!(output[0], "sat");
1058
1059 let value_output = &output[1];
1061 assert!(value_output.contains("p"));
1062 assert!(value_output.contains("q"));
1063 assert!(value_output.contains("true"));
1065 assert!(value_output.contains("false"));
1067 }
1068
1069 #[test]
1070 fn test_get_value_no_model() {
1071 let mut ctx = Context::new();
1072
1073 let script = r#"
1074 (set-logic QF_UF)
1075 (declare-const p Bool)
1076 (get-value (p))
1077 "#;
1078
1079 let output = ctx
1080 .execute_script(script)
1081 .expect("test operation should succeed");
1082 assert_eq!(output.len(), 1);
1083 assert!(output[0].contains("error") || output[0].contains("No model"));
1084 }
1085
1086 #[test]
1087 fn test_get_value_after_unsat() {
1088 let mut ctx = Context::new();
1089
1090 let script = r#"
1091 (set-logic QF_UF)
1092 (declare-const p Bool)
1093 (assert p)
1094 (assert (not p))
1095 (check-sat)
1096 (get-value (p))
1097 "#;
1098
1099 let output = ctx
1100 .execute_script(script)
1101 .expect("test operation should succeed");
1102 assert_eq!(output.len(), 2);
1103 assert_eq!(output[0], "unsat");
1104 assert!(output[1].contains("error") || output[1].contains("No model"));
1105 }
1106}