vampire_prover/lib.rs
1//! A Rust interface to the Vampire theorem prover.
2//!
3//! This crate provides safe Rust bindings to Vampire, a state-of-the-art automated
4//! theorem prover for first-order logic with equality. Vampire can prove theorems,
5//! check satisfiability, and find counterexamples in various mathematical domains.
6//!
7//! # Thread Safety
8//!
9//! **Important**: The underlying Vampire library is not thread-safe. This crate
10//! protects all operations with a global mutex, so while you can safely use the
11//! library from multiple threads, all proof operations will be serialized. Only
12//! one proof can execute at a time.
13//!
14//! # Quick Start
15//!
16//! ```
17//! use vampire_prover::{Function, Predicate, Problem, ProofRes, Options, forall};
18//!
19//! // Create predicates
20//! let is_mortal = Predicate::new("mortal", 1);
21//! let is_man = Predicate::new("man", 1);
22//!
23//! // Create a universal statement: ∀x. man(x) → mortal(x)
24//! let men_are_mortal = forall(|x| is_man.with(x) >> is_mortal.with(x));
25//!
26//! // Create a constant
27//! let socrates = Function::constant("socrates");
28//!
29//! // Build and solve the problem
30//! let result = Problem::new(Options::new())
31//! .with_axiom(is_man.with(socrates)) // Socrates is a man
32//! .with_axiom(men_are_mortal) // All men are mortal
33//! .conjecture(is_mortal.with(socrates)) // Therefore, Socrates is mortal
34//! .solve();
35//!
36//! assert_eq!(result, ProofRes::Proved);
37//! ```
38//!
39//! # Core Concepts
40//!
41//! ## Terms
42//!
43//! Terms represent objects in first-order logic. They can be:
44//! - **Constants**: Nullary functions like `socrates`
45//! - **Variables**: Bound or free variables like `x` in `∀x. P(x)`
46//! - **Function applications**: e.g., `mult(x, y)`
47//!
48//! ## Formulas
49//!
50//! Formulas are logical statements that can be:
51//! - **Predicates**: `mortal(socrates)`
52//! - **Equality**: `x = y`
53//! - **Logical connectives**: `P ∧ Q`, `P ∨ Q`, `P → Q`, `P ↔ Q`, `¬P`
54//! - **Quantifiers**: `∀x. P(x)`, `∃x. P(x)`
55//!
56//! ## Operators
57//!
58//! The crate provides Rust operators for logical connectives:
59//! - `&` for conjunction (AND)
60//! - `|` for disjunction (OR)
61//! - `>>` for implication
62//! - `!` for negation (NOT)
63//! - [`Formula::iff`] for biconditional (if and only if)
64//!
65//! # Examples
66//!
67//! ## Graph Reachability
68//!
69//! Prove transitivity of paths in a graph:
70//!
71//! ```
72//! use vampire_prover::{Function, Predicate, Problem, ProofRes, Options, forall};
73//!
74//! let edge = Predicate::new("edge", 2);
75//! let path = Predicate::new("path", 2);
76//!
77//! // Create nodes
78//! let a = Function::constant("a");
79//! let b = Function::constant("b");
80//! let c = Function::constant("c");
81//!
82//! // Axiom: edges are paths
83//! let edges_are_paths = forall(|x| forall(|y|
84//! edge.with([x, y]) >> path.with([x, y])
85//! ));
86//!
87//! // Axiom: paths are transitive
88//! let transitivity = forall(|x| forall(|y| forall(|z|
89//! (path.with([x, y]) & path.with([y, z])) >> path.with([x, z])
90//! )));
91//!
92//! let result = Problem::new(Options::new())
93//! .with_axiom(edges_are_paths)
94//! .with_axiom(transitivity)
95//! .with_axiom(edge.with([a, b]))
96//! .with_axiom(edge.with([b, c]))
97//! .conjecture(path.with([a, c]))
98//! .solve();
99//!
100//! assert_eq!(result, ProofRes::Proved);
101//! ```
102//!
103//! ## Group Theory
104//!
105//! Prove that left identity follows from the standard group axioms:
106//!
107//! ```
108//! use vampire_prover::{Function, Problem, ProofRes, Options, Term, forall};
109//!
110//! let mult = Function::new("mult", 2);
111//! let inv = Function::new("inv", 1);
112//! let one = Function::constant("1");
113//!
114//! let mul = |x: Term, y: Term| mult.with([x, y]);
115//!
116//! // Group Axiom 1: Right identity - ∀x. x * 1 = x
117//! let right_identity = forall(|x| mul(x, one).eq(x));
118//!
119//! // Group Axiom 2: Right inverse - ∀x. x * inv(x) = 1
120//! let right_inverse = forall(|x| {
121//! let inv_x = inv.with(x);
122//! mul(x, inv_x).eq(one)
123//! });
124//!
125//! // Group Axiom 3: Associativity - ∀x,y,z. (x * y) * z = x * (y * z)
126//! let associativity = forall(|x| forall(|y| forall(|z|
127//! mul(mul(x, y), z).eq(mul(x, mul(y, z)))
128//! )));
129//!
130//! // Prove left identity: ∀x. 1 * x = x
131//! let left_identity = forall(|x| mul(one, x).eq(x));
132//!
133//! let result = Problem::new(Options::new())
134//! .with_axiom(right_identity)
135//! .with_axiom(right_inverse)
136//! .with_axiom(associativity)
137//! .conjecture(left_identity)
138//! .solve();
139//!
140//! assert_eq!(result, ProofRes::Proved);
141//! ```
142//!
143//! # License
144//!
145//! This Rust crate is dual-licensed under MIT OR Apache-2.0 (your choice).
146//!
147//! The underlying Vampire theorem prover is licensed under the BSD 3-Clause License.
148//! When distributing applications using this crate, you must comply with both
149//! licenses. See the [Vampire LICENCE](https://github.com/vprover/vampire/blob/master/LICENCE)
150//! for details on the Vampire license requirements.
151
152use crate::lock::synced;
153use std::{
154 collections::HashMap,
155 ffi::CString,
156 fmt::Display,
157 ops::{BitAnd, BitOr, Index, Not, Shr},
158 time::Duration,
159};
160use vampire_sys::{self as sys, vampire_unit_t};
161
162mod lock;
163
164/// Trait for types that can be converted into term arguments.
165///
166/// This trait allows `.with()` methods on [`Function`] and [`Predicate`] to accept
167/// different argument formats for convenience:
168/// - Single term: `f.with(x)`
169/// - Array: `f.with([x, y])`
170pub trait IntoTermArgs {
171 /// Convert this type into a slice of terms.
172 fn as_slice(&self) -> &[Term];
173}
174
175impl IntoTermArgs for Term {
176 fn as_slice(&self) -> &[Term] {
177 std::slice::from_ref(self)
178 }
179}
180
181impl<T> IntoTermArgs for T
182where
183 T: AsRef<[Term]>,
184{
185 fn as_slice(&self) -> &[Term] {
186 self.as_ref()
187 }
188}
189
190/// A function symbol in first-order logic.
191///
192/// Functions represent operations that take terms as arguments and produce new terms.
193/// They have a fixed arity (number of arguments). A function with arity 0 is called a
194/// constant and represents a specific object in the domain.
195///
196/// # Examples
197///
198/// ```
199/// use vampire_prover::Function;
200///
201/// // Create a constant (0-ary function)
202/// let socrates = Function::constant("socrates");
203///
204/// // Create a unary function
205/// let successor = Function::new("succ", 1);
206///
207/// // Create a binary function
208/// let add = Function::new("add", 2);
209/// ```
210#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
211pub struct Function {
212 id: u32,
213 arity: u32,
214}
215
216impl Function {
217 /// Creates a new function symbol with the given name and arity.
218 ///
219 /// Calling this method multiple times with the same name and arity will return
220 /// the same function symbol. It is safe to call this with the same name but
221 /// different arities - they will be treated as distinct function symbols.
222 ///
223 /// # Arguments
224 ///
225 /// * `name` - The name of the function symbol
226 /// * `arity` - The number of arguments this function takes
227 ///
228 /// # Examples
229 ///
230 /// ```
231 /// use vampire_prover::Function;
232 ///
233 /// let mult = Function::new("mult", 2);
234 /// assert_eq!(mult.arity(), 2);
235 ///
236 /// // Same name and arity returns the same symbol
237 /// let mult2 = Function::new("mult", 2);
238 /// assert_eq!(mult, mult2);
239 ///
240 /// // Same name but different arity is a different symbol
241 /// let mult3 = Function::new("mult", 3);
242 /// assert_ne!(mult.arity(), mult3.arity());
243 /// ```
244 pub fn new(name: &str, arity: u32) -> Self {
245 synced(|_| {
246 let name = CString::new(name).expect("valid c string");
247 let function = unsafe { sys::vampire_add_function(name.as_ptr(), arity) };
248 Self {
249 id: function,
250 arity,
251 }
252 })
253 }
254
255 /// Returns the arity (number of arguments) of this function.
256 ///
257 /// # Examples
258 ///
259 /// ```
260 /// use vampire_prover::Function;
261 ///
262 /// let f = Function::new("f", 3);
263 /// assert_eq!(f.arity(), 3);
264 /// ```
265 pub fn arity(&self) -> u32 {
266 self.arity
267 }
268
269 /// Creates a constant term (0-ary function).
270 ///
271 /// This is a convenience method equivalent to `Function::new(name, 0).with([])`.
272 /// Constants represent specific objects in the domain.
273 ///
274 /// # Arguments
275 ///
276 /// * `name` - The name of the constant
277 ///
278 /// # Examples
279 ///
280 /// ```
281 /// use vampire_prover::Function;
282 ///
283 /// let socrates = Function::constant("socrates");
284 /// let zero = Function::constant("0");
285 /// ```
286 pub fn constant(name: &str) -> Term {
287 Self::new(name, 0).with([])
288 }
289
290 /// Applies this function to the given arguments, creating a term.
291 ///
292 /// This method accepts multiple argument formats for convenience:
293 /// - Single term: `f.with(x)`
294 /// - Array: `f.with([x, y])`
295 ///
296 /// # Panics
297 ///
298 /// Panics if the number of arguments does not match the function's arity.
299 ///
300 /// # Examples
301 ///
302 /// ```
303 /// use vampire_prover::{Function, Term};
304 ///
305 /// let add = Function::new("add", 2);
306 /// let x = Term::new_var(0);
307 /// let y = Term::new_var(1);
308 ///
309 /// // Multiple arguments:
310 /// let sum = add.with([x, y]);
311 ///
312 /// // Single argument:
313 /// let succ = Function::new("succ", 1);
314 /// let sx = succ.with(x);
315 /// ```
316 pub fn with(&self, args: impl IntoTermArgs) -> Term {
317 Term::new_function(*self, args.as_slice())
318 }
319}
320
321/// A predicate symbol in first-order logic.
322///
323/// Predicates represent relations or properties that can be true or false.
324/// They take terms as arguments and produce formulas. Like functions, predicates
325/// have a fixed arity.
326///
327/// # Examples
328///
329/// ```
330/// use vampire_prover::{Function, Predicate};
331///
332/// // Unary predicate (property)
333/// let is_mortal = Predicate::new("mortal", 1);
334/// let socrates = Function::constant("socrates");
335/// let formula = is_mortal.with(socrates); // mortal(socrates)
336///
337/// // Binary predicate (relation)
338/// let loves = Predicate::new("loves", 2);
339/// let alice = Function::constant("alice");
340/// let bob = Function::constant("bob");
341/// let formula = loves.with([alice, bob]); // loves(alice, bob)
342/// ```
343#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
344pub struct Predicate {
345 id: u32,
346 arity: u32,
347}
348
349impl Predicate {
350 /// Creates a new predicate symbol with the given name and arity.
351 ///
352 /// Calling this method multiple times with the same name and arity will return
353 /// the same predicate symbol. It is safe to call this with the same name but
354 /// different arities - they will be treated as distinct predicate symbols.
355 ///
356 /// # Arguments
357 ///
358 /// * `name` - The name of the predicate symbol
359 /// * `arity` - The number of arguments this predicate takes
360 ///
361 /// # Examples
362 ///
363 /// ```
364 /// use vampire_prover::Predicate;
365 ///
366 /// let edge = Predicate::new("edge", 2);
367 /// assert_eq!(edge.arity(), 2);
368 ///
369 /// // Same name and arity returns the same symbol
370 /// let edge2 = Predicate::new("edge", 2);
371 /// assert_eq!(edge, edge2);
372 ///
373 /// // Same name but different arity is a different symbol
374 /// let edge3 = Predicate::new("edge", 3);
375 /// assert_ne!(edge.arity(), edge3.arity());
376 /// ```
377 pub fn new(name: &str, arity: u32) -> Self {
378 // TODO: predicate/term with same name already exists?
379
380 synced(|_| {
381 let name = CString::new(name).expect("valid c string");
382 let predicate = unsafe { sys::vampire_add_predicate(name.as_ptr(), arity) };
383 Self {
384 id: predicate,
385 arity,
386 }
387 })
388 }
389
390 /// Returns the arity (number of arguments) of this predicate.
391 ///
392 /// # Examples
393 ///
394 /// ```
395 /// use vampire_prover::Predicate;
396 ///
397 /// let p = Predicate::new("p", 2);
398 /// assert_eq!(p.arity(), 2);
399 /// ```
400 pub fn arity(&self) -> u32 {
401 self.arity
402 }
403
404 /// Applies this predicate to the given arguments, creating a formula.
405 ///
406 /// This method accepts multiple argument formats for convenience:
407 /// - Single term: `p.with(x)`
408 /// - Array: `p.with([x, y])`
409 ///
410 /// # Panics
411 ///
412 /// Panics if the number of arguments does not match the predicate's arity.
413 ///
414 /// # Examples
415 ///
416 /// ```
417 /// use vampire_prover::{Function, Predicate};
418 ///
419 /// let mortal = Predicate::new("mortal", 1);
420 /// let socrates = Function::constant("socrates");
421 ///
422 /// // Single argument:
423 /// let formula = mortal.with(socrates);
424 ///
425 /// // Multiple arguments:
426 /// let edge = Predicate::new("edge", 2);
427 /// let a = Function::constant("a");
428 /// let b = Function::constant("b");
429 /// let e = edge.with([a, b]);
430 /// ```
431 pub fn with(&self, args: impl IntoTermArgs) -> Formula {
432 Formula::new_predicate(*self, args.as_slice())
433 }
434}
435
436/// A term in first-order logic.
437///
438/// Terms represent objects in the domain of discourse. A term can be:
439/// - A constant: `socrates`
440/// - A variable: `x`
441/// - A function application: `add(x, y)`
442///
443/// # Examples
444///
445/// ```
446/// use vampire_prover::{Function, Term};
447///
448/// // Create a constant
449/// let zero = Function::constant("0");
450///
451/// // Create a variable
452/// let x = Term::new_var(0);
453///
454/// // Create a function application
455/// let succ = Function::new("succ", 1);
456/// let one = succ.with(zero);
457/// ```
458#[derive(Clone, Copy)]
459#[repr(transparent)]
460pub struct Term {
461 id: *mut sys::vampire_term_t,
462}
463
464impl PartialEq for Term {
465 fn eq(&self, other: &Self) -> bool {
466 synced(|_| unsafe { sys::vampire_term_equal(self.id, other.id) })
467 }
468}
469
470impl Eq for Term {}
471
472impl std::hash::Hash for Term {
473 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
474 synced(|_| unsafe { sys::vampire_term_hash(self.id) }).hash(state);
475 }
476}
477
478impl std::fmt::Debug for Term {
479 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
480 write!(f, "Term({})", self.to_string())
481 }
482}
483
484impl std::fmt::Display for Term {
485 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
486 write!(f, "{}", self.to_string())
487 }
488}
489
490impl Term {
491 /// Converts this term to a string representation.
492 ///
493 /// # Panics
494 ///
495 /// Panics if the underlying C API fails (which should never happen in normal use).
496 ///
497 /// # Examples
498 ///
499 /// ```
500 /// use vampire_prover::Function;
501 ///
502 /// let x = Function::constant("x");
503 /// println!("{}", x.to_string()); // Prints the vampire string representation
504 /// ```
505 pub fn to_string(&self) -> String {
506 synced(|_| unsafe {
507 let ptr = sys::vampire_term_to_string(self.id);
508 assert!(!ptr.is_null(), "vampire_term_to_string returned null");
509
510 let c_str = std::ffi::CStr::from_ptr(ptr);
511 let result = c_str
512 .to_str()
513 .expect("vampire returned invalid UTF-8")
514 .to_string();
515 sys::vampire_free_string(ptr);
516 result
517 })
518 }
519
520 /// Creates a term by applying a function to arguments.
521 ///
522 /// This is typically called via [`Function::with`] rather than directly.
523 ///
524 /// # Panics
525 ///
526 /// Panics if the number of arguments does not match the function's arity.
527 ///
528 /// # Examples
529 ///
530 /// ```
531 /// use vampire_prover::{Function, Term};
532 ///
533 /// let add = Function::new("add", 2);
534 /// let x = Term::new_var(0);
535 /// let y = Term::new_var(1);
536 ///
537 /// let sum = Term::new_function(add, &[x, y]);
538 /// ```
539 pub fn new_function(func: Function, args: &[Term]) -> Self {
540 // TODO: try_new_function?
541 assert!(args.len() == func.arity() as usize);
542
543 synced(|_| unsafe {
544 let arg_count = args.len();
545 let args = std::mem::transmute(args.as_ptr());
546 let term = sys::vampire_term(func.id, args, arg_count);
547 Self { id: term }
548 })
549 }
550
551 /// Creates a variable with the given index.
552 ///
553 /// Variables are typically used within quantified formulas. The index should be
554 /// unique within a formula. For automatic variable management, consider using
555 /// the [`forall`] and [`exists`] helper functions instead.
556 ///
557 /// # Arguments
558 ///
559 /// * `idx` - The unique index for this variable
560 ///
561 /// # Examples
562 ///
563 /// ```
564 /// use vampire_prover::Term;
565 ///
566 /// let x = Term::new_var(0);
567 /// let y = Term::new_var(1);
568 /// ```
569 pub fn new_var(idx: u32) -> Self {
570 synced(|info| unsafe {
571 info.free_var = info.free_var.max(idx + 1);
572 let term = sys::vampire_var(idx);
573 Self { id: term }
574 })
575 }
576
577 /// Creates a fresh variable with an automatically assigned index.
578 ///
579 /// Returns both the variable term and its index. This is primarily used internally
580 /// by the [`forall`] and [`exists`] functions.
581 ///
582 /// # Examples
583 ///
584 /// ```
585 /// use vampire_prover::Term;
586 ///
587 /// let (x, idx) = Term::free_var();
588 /// assert_eq!(idx, 0);
589 ///
590 /// let (y, idx2) = Term::free_var();
591 /// assert_eq!(idx2, 1);
592 /// ```
593 pub fn free_var() -> (Self, u32) {
594 synced(|info| unsafe {
595 let idx = info.free_var;
596 info.free_var += 1;
597 let term = sys::vampire_var(idx);
598 (Self { id: term }, idx)
599 })
600 }
601
602 /// Creates an equality formula between this term and another.
603 ///
604 /// # Arguments
605 ///
606 /// * `rhs` - The right-hand side of the equality
607 ///
608 /// # Examples
609 ///
610 /// ```
611 /// use vampire_prover::{Function, forall};
612 ///
613 /// let succ = Function::new("succ", 1);
614 /// let zero = Function::constant("0");
615 ///
616 /// // ∀x. succ(x) = succ(x)
617 /// let reflexive = forall(|x| {
618 /// let sx = succ.with(x);
619 /// sx.eq(sx)
620 /// });
621 /// ```
622 pub fn eq(&self, rhs: Term) -> Formula {
623 Formula::new_eq(*self, rhs)
624 }
625}
626
627/// A formula in first-order logic.
628///
629/// Formulas are logical statements that can be true or false. They include:
630/// - Atomic formulas: predicates and equalities
631/// - Logical connectives: AND (`&`), OR (`|`), NOT (`!`), implication (`>>`), biconditional
632/// - Quantifiers: universal (`∀`) and existential (`∃`)
633///
634/// # Examples
635///
636/// ```
637/// use vampire_prover::{Function, Predicate, forall};
638///
639/// let p = Predicate::new("P", 1);
640/// let q = Predicate::new("Q", 1);
641/// let x = Function::constant("x");
642///
643/// // Atomic formula
644/// let px = p.with(x);
645/// let qx = q.with(x);
646///
647/// // Conjunction: P(x) ∧ Q(x)
648/// let both = px & qx;
649///
650/// // Disjunction: P(x) ∨ Q(x)
651/// let either = px | qx;
652///
653/// // Implication: P(x) → Q(x)
654/// let implies = px >> qx;
655///
656/// // Negation: ¬P(x)
657/// let not_px = !px;
658///
659/// // Universal quantification: ∀x. P(x)
660/// let all = forall(|x| p.with(x));
661/// ```
662#[derive(Clone, Copy)]
663#[repr(transparent)]
664pub struct Formula {
665 id: *mut sys::vampire_formula_t,
666}
667
668impl PartialEq for Formula {
669 fn eq(&self, other: &Self) -> bool {
670 synced(|_| unsafe { sys::vampire_formula_equal(self.id, other.id) })
671 }
672}
673
674impl Eq for Formula {}
675
676impl std::hash::Hash for Formula {
677 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
678 synced(|_| unsafe { sys::vampire_formula_hash(self.id) }).hash(state);
679 }
680}
681
682impl std::fmt::Debug for Formula {
683 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
684 write!(f, "Formula({})", self.to_string())
685 }
686}
687
688impl std::fmt::Display for Formula {
689 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
690 write!(f, "{}", self.to_string())
691 }
692}
693
694impl Formula {
695 /// Converts this formula to a string representation.
696 ///
697 /// # Panics
698 ///
699 /// Panics if the underlying C API fails (which should never happen in normal use).
700 ///
701 /// # Examples
702 ///
703 /// ```
704 /// use vampire_prover::{Function, Predicate};
705 ///
706 /// let p = Predicate::new("P", 1);
707 /// let x = Function::constant("x");
708 /// let formula = p.with(x);
709 /// println!("{}", formula.to_string()); // Prints the vampire string representation
710 /// ```
711 pub fn to_string(&self) -> String {
712 synced(|_| unsafe {
713 let ptr = sys::vampire_formula_to_string(self.id);
714 assert!(!ptr.is_null(), "vampire_formula_to_string returned null");
715
716 let c_str = std::ffi::CStr::from_ptr(ptr);
717 let result = c_str
718 .to_str()
719 .expect("vampire returned invalid UTF-8")
720 .to_string();
721 sys::vampire_free_string(ptr);
722 result
723 })
724 }
725
726 /// Creates an atomic formula by applying a predicate to arguments.
727 ///
728 /// This is typically called via [`Predicate::with`] rather than directly.
729 ///
730 /// # Panics
731 ///
732 /// Panics if the number of arguments does not match the predicate's arity.
733 ///
734 /// # Examples
735 ///
736 /// ```
737 /// use vampire_prover::{Function, Predicate, Formula};
738 ///
739 /// let mortal = Predicate::new("mortal", 1);
740 /// let socrates = Function::constant("socrates");
741 ///
742 /// let formula = Formula::new_predicate(mortal, &[socrates]);
743 /// ```
744 pub fn new_predicate(pred: Predicate, args: &[Term]) -> Self {
745 assert!(args.len() == pred.arity() as usize);
746
747 synced(|_| unsafe {
748 let arg_count = args.len();
749 let args = std::mem::transmute(args.as_ptr());
750 let lit = sys::vampire_lit(pred.id, true, args, arg_count);
751 let atom = sys::vampire_atom(lit);
752 Self { id: atom }
753 })
754 }
755
756 /// Creates an equality formula between two terms.
757 ///
758 /// This is typically called via [`Term::eq`] rather than directly.
759 ///
760 /// # Examples
761 ///
762 /// ```
763 /// use vampire_prover::{Function, Formula};
764 ///
765 /// let x = Function::constant("x");
766 /// let y = Function::constant("y");
767 ///
768 /// let eq = Formula::new_eq(x, y);
769 /// ```
770 pub fn new_eq(lhs: Term, rhs: Term) -> Self {
771 synced(|_| unsafe {
772 let lit = sys::vampire_eq(true, lhs.id, rhs.id);
773 let atom = sys::vampire_atom(lit);
774 Self { id: atom }
775 })
776 }
777
778 /// Creates a conjunction (AND) of multiple formulas.
779 ///
780 /// For two formulas, the `&` operator is more convenient.
781 ///
782 /// # Examples
783 ///
784 /// ```
785 /// use vampire_prover::{Function, Predicate, Formula};
786 ///
787 /// let p = Predicate::new("P", 1);
788 /// let q = Predicate::new("Q", 1);
789 /// let r = Predicate::new("R", 1);
790 /// let x = Function::constant("x");
791 ///
792 /// // P(x) ∧ Q(x) ∧ R(x)
793 /// let all_three = Formula::new_and(&[
794 /// p.with(x),
795 /// q.with(x),
796 /// r.with(x),
797 /// ]);
798 /// ```
799 pub fn new_and(formulas: &[Formula]) -> Self {
800 synced(|_| unsafe {
801 let formula_count = formulas.len();
802 let formulas = std::mem::transmute(formulas.as_ptr());
803 let id = sys::vampire_and(formulas, formula_count);
804 Self { id }
805 })
806 }
807
808 /// Creates a disjunction (OR) of multiple formulas.
809 ///
810 /// For two formulas, the `|` operator is more convenient.
811 ///
812 /// # Examples
813 ///
814 /// ```
815 /// use vampire_prover::{Function, Predicate, Formula};
816 ///
817 /// let p = Predicate::new("P", 1);
818 /// let q = Predicate::new("Q", 1);
819 /// let r = Predicate::new("R", 1);
820 /// let x = Function::constant("x");
821 ///
822 /// // P(x) ∨ Q(x) ∨ R(x)
823 /// let any = Formula::new_or(&[
824 /// p.with(x),
825 /// q.with(x),
826 /// r.with(x),
827 /// ]);
828 /// ```
829 pub fn new_or(formulas: &[Formula]) -> Self {
830 synced(|_| unsafe {
831 let formula_count = formulas.len();
832 let formulas = std::mem::transmute(formulas.as_ptr());
833 let id = sys::vampire_or(formulas, formula_count);
834 Self { id }
835 })
836 }
837
838 /// Creates a negation (NOT) of a formula.
839 ///
840 /// The `!` operator is more convenient than calling this directly.
841 ///
842 /// # Examples
843 ///
844 /// ```
845 /// use vampire_prover::{Function, Predicate, Formula};
846 ///
847 /// let p = Predicate::new("P", 1);
848 /// let x = Function::constant("x");
849 ///
850 /// let not_p = Formula::new_not(p.with(x));
851 /// ```
852 pub fn new_not(formula: Formula) -> Self {
853 synced(|_| {
854 let id = unsafe { sys::vampire_not(formula.id) };
855 Self { id }
856 })
857 }
858
859 /// Creates the true (tautology) formula.
860 ///
861 /// # Examples
862 ///
863 /// ```
864 /// use vampire_prover::Formula;
865 ///
866 /// let t = Formula::new_true();
867 /// ```
868 pub fn new_true() -> Self {
869 synced(|_| {
870 let id = unsafe { sys::vampire_true() };
871 Self { id }
872 })
873 }
874
875 /// Creates the false (contradiction) formula.
876 ///
877 /// # Examples
878 ///
879 /// ```
880 /// use vampire_prover::Formula;
881 ///
882 /// let f = Formula::new_false();
883 /// ```
884 pub fn new_false() -> Self {
885 synced(|_| {
886 let id = unsafe { sys::vampire_false() };
887 Self { id }
888 })
889 }
890
891 /// Creates a universally quantified formula.
892 ///
893 /// The [`forall`] helper function provides a more ergonomic interface.
894 ///
895 /// # Arguments
896 ///
897 /// * `var` - The index of the variable to quantify
898 /// * `f` - The formula body
899 ///
900 /// # Examples
901 ///
902 /// ```
903 /// use vampire_prover::{Function, Predicate, Formula, Term};
904 ///
905 /// let p = Predicate::new("P", 1);
906 /// let x = Term::new_var(0);
907 ///
908 /// // ∀x. P(x)
909 /// let all_p = Formula::new_forall(0, p.with(x));
910 /// ```
911 pub fn new_forall(var: u32, f: Formula) -> Self {
912 synced(|_| {
913 let id = unsafe { sys::vampire_forall(var, f.id) };
914 Self { id }
915 })
916 }
917
918 /// Creates an existentially quantified formula.
919 ///
920 /// The [`exists`] helper function provides a more ergonomic interface.
921 ///
922 /// # Arguments
923 ///
924 /// * `var` - The index of the variable to quantify
925 /// * `f` - The formula body
926 ///
927 /// # Examples
928 ///
929 /// ```
930 /// use vampire_prover::{Function, Predicate, Formula, Term};
931 ///
932 /// let p = Predicate::new("P", 1);
933 /// let x = Term::new_var(0);
934 ///
935 /// // ∃x. P(x)
936 /// let some_p = Formula::new_exists(0, p.with(x));
937 /// ```
938 pub fn new_exists(var: u32, f: Formula) -> Self {
939 synced(|_| {
940 let id = unsafe { sys::vampire_exists(var, f.id) };
941 Self { id }
942 })
943 }
944
945 /// Creates an implication from this formula to another.
946 ///
947 /// The `>>` operator is more convenient than calling this directly.
948 ///
949 /// # Arguments
950 ///
951 /// * `rhs` - The consequent (right-hand side) of the implication
952 ///
953 /// # Examples
954 ///
955 /// ```
956 /// use vampire_prover::{Function, Predicate};
957 ///
958 /// let p = Predicate::new("P", 1);
959 /// let q = Predicate::new("Q", 1);
960 /// let x = Function::constant("x");
961 ///
962 /// // P(x) → Q(x)
963 /// let implication = p.with(x).imp(q.with(x));
964 /// ```
965 pub fn imp(&self, rhs: Formula) -> Self {
966 synced(|_| {
967 let id = unsafe { sys::vampire_imp(self.id, rhs.id) };
968 Self { id }
969 })
970 }
971
972 /// Creates a biconditional (if and only if) between this formula and another.
973 ///
974 /// A biconditional `P ↔ Q` is true when both formulas have the same truth value.
975 ///
976 /// # Arguments
977 ///
978 /// * `rhs` - The right-hand side of the biconditional
979 ///
980 /// # Examples
981 ///
982 /// ```
983 /// use vampire_prover::{Function, Predicate, forall};
984 ///
985 /// let even = Predicate::new("even", 1);
986 /// let div_by_2 = Predicate::new("divisible_by_2", 1);
987 ///
988 /// // ∀x. even(x) ↔ divisible_by_2(x)
989 /// let equiv = forall(|x| {
990 /// even.with(x).iff(div_by_2.with(x))
991 /// });
992 /// ```
993 pub fn iff(&self, rhs: Formula) -> Self {
994 synced(|_| {
995 let id = unsafe { sys::vampire_iff(self.id, rhs.id) };
996 Self { id }
997 })
998 }
999}
1000
1001/// Creates a universally quantified formula using a closure.
1002///
1003/// This is the most ergonomic way to create formulas with universal quantification.
1004/// The closure receives a fresh variable term that can be used in the formula body.
1005///
1006/// # Arguments
1007///
1008/// * `f` - A closure that takes a [`Term`] representing the quantified variable and
1009/// returns a [`Formula`]
1010///
1011/// # Examples
1012///
1013/// ```
1014/// use vampire_prover::{Function, Predicate, forall};
1015///
1016/// let p = Predicate::new("P", 1);
1017///
1018/// // ∀x. P(x)
1019/// let all_p = forall(|x| p.with(x));
1020///
1021/// // Nested quantifiers: ∀x. ∀y. P(x, y)
1022/// let p2 = Predicate::new("P", 2);
1023/// let all_xy = forall(|x| forall(|y| p2.with([x, y])));
1024/// ```
1025///
1026/// # Complex Example
1027///
1028/// ```
1029/// use vampire_prover::{Function, Predicate, forall};
1030///
1031/// let mortal = Predicate::new("mortal", 1);
1032/// let human = Predicate::new("human", 1);
1033///
1034/// // ∀x. human(x) → mortal(x)
1035/// let humans_are_mortal = forall(|x| {
1036/// human.with(x) >> mortal.with(x)
1037/// });
1038/// ```
1039pub fn forall<F: FnOnce(Term) -> Formula>(f: F) -> Formula {
1040 let (var, var_idx) = Term::free_var();
1041 let f = f(var);
1042 Formula::new_forall(var_idx, f)
1043}
1044
1045/// Creates an existentially quantified formula using a closure.
1046///
1047/// This is the most ergonomic way to create formulas with existential quantification.
1048/// The closure receives a fresh variable term that can be used in the formula body.
1049///
1050/// # Arguments
1051///
1052/// * `f` - A closure that takes a [`Term`] representing the quantified variable and
1053/// returns a [`Formula`]
1054///
1055/// # Examples
1056///
1057/// ```
1058/// use vampire_prover::{Function, Predicate, exists};
1059///
1060/// let prime = Predicate::new("prime", 1);
1061///
1062/// // ∃x. prime(x) - "There exists a prime number"
1063/// let some_prime = exists(|x| prime.with(x));
1064///
1065/// // ∃x. ∃y. edge(x, y) - "There exists an edge"
1066/// let edge = Predicate::new("edge", 2);
1067/// let has_edge = exists(|x| exists(|y| edge.with([x, y])));
1068/// ```
1069///
1070/// # Complex Example
1071///
1072/// ```
1073/// use vampire_prover::{Function, Predicate, exists, forall};
1074///
1075/// let greater = Predicate::new("greater", 2);
1076///
1077/// // ∃x. ∀y. greater(x, y) - "There exists a maximum element"
1078/// let has_maximum = exists(|x| forall(|y| greater.with([x, y])));
1079/// ```
1080pub fn exists<F: FnOnce(Term) -> Formula>(f: F) -> Formula {
1081 let (var, var_idx) = Term::free_var();
1082 let f = f(var);
1083 Formula::new_exists(var_idx, f)
1084}
1085
1086/// Implements the `&` operator for conjunction (AND).
1087///
1088/// # Examples
1089///
1090/// ```
1091/// use vampire_prover::{Function, Predicate};
1092///
1093/// let p = Predicate::new("P", 1);
1094/// let q = Predicate::new("Q", 1);
1095/// let x = Function::constant("x");
1096///
1097/// // P(x) ∧ Q(x)
1098/// let both = p.with(x) & q.with(x);
1099/// ```
1100impl BitAnd for Formula {
1101 type Output = Formula;
1102
1103 fn bitand(self, rhs: Self) -> Self::Output {
1104 Formula::new_and(&[self, rhs])
1105 }
1106}
1107
1108/// Implements the `|` operator for disjunction (OR).
1109///
1110/// # Examples
1111///
1112/// ```
1113/// use vampire_prover::{Function, Predicate};
1114///
1115/// let p = Predicate::new("P", 1);
1116/// let q = Predicate::new("Q", 1);
1117/// let x = Function::constant("x");
1118///
1119/// // P(x) ∨ Q(x)
1120/// let either = p.with(x) | q.with(x);
1121/// ```
1122impl BitOr for Formula {
1123 type Output = Formula;
1124
1125 fn bitor(self, rhs: Self) -> Self::Output {
1126 Formula::new_or(&[self, rhs])
1127 }
1128}
1129
1130/// Implements the `!` operator for negation (NOT).
1131///
1132/// # Examples
1133///
1134/// ```
1135/// use vampire_prover::{Function, Predicate};
1136///
1137/// let p = Predicate::new("P", 1);
1138/// let x = Function::constant("x");
1139///
1140/// // ¬P(x)
1141/// let not_p = !p.with(x);
1142/// ```
1143impl Not for Formula {
1144 type Output = Formula;
1145
1146 fn not(self) -> Self::Output {
1147 Formula::new_not(self)
1148 }
1149}
1150
1151/// Implements the `>>` operator for implication.
1152///
1153/// # Examples
1154///
1155/// ```
1156/// use vampire_prover::{Function, Predicate};
1157///
1158/// let p = Predicate::new("P", 1);
1159/// let q = Predicate::new("Q", 1);
1160/// let x = Function::constant("x");
1161///
1162/// // P(x) → Q(x)
1163/// let implies = p.with(x) >> q.with(x);
1164/// ```
1165impl Shr for Formula {
1166 type Output = Formula;
1167
1168 fn shr(self, rhs: Self) -> Self::Output {
1169 self.imp(rhs)
1170 }
1171}
1172
1173/// Configuration options for the Vampire theorem prover.
1174///
1175/// Options allow you to configure the behavior of the prover, such as setting
1176/// time limits. Use the builder pattern to construct options.
1177///
1178/// # Examples
1179///
1180/// ```
1181/// use vampire_prover::Options;
1182/// use std::time::Duration;
1183///
1184/// // Default options (no timeout)
1185/// let opts = Options::new();
1186///
1187/// // Set a timeout
1188/// let opts = Options::new().timeout(Duration::from_secs(5));
1189/// ```
1190#[derive(Debug, Clone)]
1191pub struct Options {
1192 timeout: Option<Duration>,
1193}
1194
1195impl Options {
1196 /// Creates a new Options with default settings.
1197 ///
1198 /// By default, no timeout is set.
1199 ///
1200 /// # Examples
1201 ///
1202 /// ```
1203 /// use vampire_prover::Options;
1204 ///
1205 /// let opts = Options::new();
1206 /// ```
1207 pub fn new() -> Self {
1208 Self { timeout: None }
1209 }
1210
1211 /// Sets the timeout for the prover.
1212 ///
1213 /// If the prover exceeds this time limit, it will return
1214 /// `ProofRes::Unknown(UnknownReason::Timeout)`.
1215 ///
1216 /// # Arguments
1217 ///
1218 /// * `duration` - The maximum time the prover should run
1219 ///
1220 /// # Examples
1221 ///
1222 /// ```
1223 /// use vampire_prover::Options;
1224 /// use std::time::Duration;
1225 ///
1226 /// let opts = Options::new().timeout(Duration::from_secs(10));
1227 /// ```
1228 pub fn timeout(&mut self, duration: Duration) -> &mut Self {
1229 self.timeout = Some(duration);
1230 self
1231 }
1232}
1233
1234impl Default for Options {
1235 fn default() -> Self {
1236 Self::new()
1237 }
1238}
1239
1240/// A theorem proving problem consisting of axioms and an optional conjecture.
1241///
1242/// A [`Problem`] is constructed by adding axioms (assumed to be true) and optionally
1243/// a conjecture (the statement to be proved). The problem is then solved by calling
1244/// [`Problem::solve`], which invokes the Vampire theorem prover.
1245///
1246/// # Examples
1247///
1248/// ## Basic Usage
1249///
1250/// ```
1251/// use vampire_prover::{Function, Predicate, Problem, ProofRes, Options, forall};
1252///
1253/// let mortal = Predicate::new("mortal", 1);
1254/// let human = Predicate::new("human", 1);
1255/// let socrates = Function::constant("socrates");
1256///
1257/// let result = Problem::new(Options::new())
1258/// .with_axiom(human.with(socrates))
1259/// .with_axiom(forall(|x| human.with(x) >> mortal.with(x)))
1260/// .conjecture(mortal.with(socrates))
1261/// .solve();
1262///
1263/// assert_eq!(result, ProofRes::Proved);
1264/// ```
1265///
1266/// ## Without Conjecture
1267///
1268/// You can also create problems without a conjecture to check satisfiability:
1269///
1270/// ```
1271/// use vampire_prover::{Function, Predicate, Problem, Options};
1272///
1273/// let p = Predicate::new("P", 1);
1274/// let x = Function::constant("x");
1275///
1276/// let result = Problem::new(Options::new())
1277/// .with_axiom(p.with(x))
1278/// .with_axiom(!p.with(x)) // Contradiction
1279/// .solve();
1280///
1281/// // This should be unsatisfiable
1282/// ```
1283#[derive(Debug, Clone)]
1284pub struct Problem {
1285 options: Options,
1286 axioms: Vec<Formula>,
1287 conjecture: Option<Formula>,
1288}
1289
1290impl Problem {
1291 /// Creates a new empty problem with the given options.
1292 ///
1293 /// # Arguments
1294 ///
1295 /// * `options` - Configuration options for the prover
1296 ///
1297 /// # Examples
1298 ///
1299 /// ```
1300 /// use vampire_prover::{Problem, Options};
1301 /// use std::time::Duration;
1302 ///
1303 /// // Default options
1304 /// let problem = Problem::new(Options::new());
1305 /// ```
1306 pub fn new(options: Options) -> Self {
1307 Self {
1308 options,
1309 axioms: Vec::new(),
1310 conjecture: None,
1311 }
1312 }
1313
1314 /// Adds an axiom to the problem.
1315 ///
1316 /// Axioms are formulas assumed to be true. The prover will use these axioms
1317 /// to attempt to prove the conjecture (if one is provided).
1318 ///
1319 /// This method consumes `self` and returns a new [`Problem`], allowing for
1320 /// method chaining.
1321 ///
1322 /// # Arguments
1323 ///
1324 /// * `f` - The axiom formula to add
1325 ///
1326 /// # Examples
1327 ///
1328 /// ```
1329 /// use vampire_prover::{Function, Predicate, Problem, Options, forall};
1330 ///
1331 /// let p = Predicate::new("P", 1);
1332 /// let q = Predicate::new("Q", 1);
1333 ///
1334 /// let problem = Problem::new(Options::new())
1335 /// .with_axiom(forall(|x| p.with(x)))
1336 /// .with_axiom(forall(|x| p.with(x) >> q.with(x)));
1337 /// ```
1338 pub fn with_axiom(&mut self, f: Formula) -> &mut Self {
1339 self.axioms.push(f);
1340 self
1341 }
1342
1343 /// Sets the conjecture for the problem.
1344 ///
1345 /// The conjecture is the statement that the prover will attempt to prove from
1346 /// the axioms. A problem can have at most one conjecture.
1347 ///
1348 /// This method consumes `self` and returns a new [`Problem`], allowing for
1349 /// method chaining.
1350 ///
1351 /// # Arguments
1352 ///
1353 /// * `f` - The conjecture formula
1354 ///
1355 /// # Examples
1356 ///
1357 /// ```
1358 /// use vampire_prover::{Function, Predicate, Problem, Options, forall};
1359 ///
1360 /// let p = Predicate::new("P", 1);
1361 /// let q = Predicate::new("Q", 1);
1362 ///
1363 /// let problem = Problem::new(Options::new())
1364 /// .with_axiom(forall(|x| p.with(x) >> q.with(x)))
1365 /// .conjecture(forall(|x| q.with(x))); // Try to prove this
1366 /// ```
1367 pub fn conjecture(&mut self, f: Formula) -> &mut Self {
1368 self.conjecture = Some(f);
1369 self
1370 }
1371
1372 unsafe fn unsynce_solve(&mut self) -> ProofRes {
1373 unsafe {
1374 sys::vampire_prepare_for_next_proof();
1375
1376 // Apply timeout option if set
1377 if let Some(timeout) = self.options.timeout {
1378 let ms = timeout.as_millis().max(1);
1379 sys::vampire_set_time_limit_milliseconds(ms as i32);
1380 }
1381
1382 let mut units = Vec::new();
1383
1384 for axiom in &self.axioms {
1385 let axiom_unit = sys::vampire_axiom_formula(axiom.id);
1386 units.push(axiom_unit);
1387 }
1388 if let Some(conjecture) = self.conjecture {
1389 let conjecture_unit = sys::vampire_conjecture_formula(conjecture.id);
1390 units.push(conjecture_unit);
1391 }
1392
1393 let problem = sys::vampire_problem_from_units(units.as_mut_ptr(), units.len());
1394 let proof_res = sys::vampire_prove(problem);
1395
1396 ProofRes::new_from_raw(proof_res)
1397 }
1398 }
1399
1400 /// Solves the problem using the Vampire theorem prover.
1401 ///
1402 /// This method consumes the problem and invokes Vampire to either prove the
1403 /// conjecture from the axioms, find a counterexample, or determine that the
1404 /// result is unknown.
1405 ///
1406 /// # Returns
1407 ///
1408 /// A [`ProofRes`] indicating whether the conjecture was proved, found to be
1409 /// unprovable, or whether the result is unknown (due to timeout, memory limits,
1410 /// or incompleteness).
1411 ///
1412 /// # Examples
1413 ///
1414 /// ```
1415 /// use vampire_prover::{Function, Predicate, Problem, ProofRes, Options, forall};
1416 ///
1417 /// let p = Predicate::new("P", 1);
1418 /// let x = Function::constant("x");
1419 ///
1420 /// let result = Problem::new(Options::new())
1421 /// .with_axiom(p.with(x))
1422 /// .conjecture(p.with(x))
1423 /// .solve();
1424 ///
1425 /// assert_eq!(result, ProofRes::Proved);
1426 /// ```
1427 pub fn solve(&mut self) -> ProofRes {
1428 synced(|_| unsafe { self.unsynce_solve() })
1429 }
1430
1431 /// Solves the problem and, if proved, returns the proof.
1432 ///
1433 /// This is like [`Problem::solve`] but also extracts a [`Proof`] when the
1434 /// conjecture is successfully proved. If the result is anything other than
1435 /// [`ProofRes::Proved`], the second element of the tuple is `None`.
1436 ///
1437 /// # Returns
1438 ///
1439 /// A tuple of `(ProofRes, Option<Proof>)`. The `Option<Proof>` is `Some` only
1440 /// when the result is [`ProofRes::Proved`].
1441 ///
1442 /// # Examples
1443 ///
1444 /// ```
1445 /// use vampire_prover::{Function, Predicate, Problem, ProofRes, Options, forall};
1446 ///
1447 /// let p = Predicate::new("P", 1);
1448 /// let x = Function::constant("x");
1449 ///
1450 /// let (result, proof) = Problem::new(Options::new())
1451 /// .with_axiom(p.with(x))
1452 /// .conjecture(p.with(x))
1453 /// .solve_and_prove();
1454 ///
1455 /// assert_eq!(result, ProofRes::Proved);
1456 /// assert!(proof.is_some());
1457 /// println!("{}", proof.unwrap());
1458 /// ```
1459 pub fn solve_and_prove(&mut self) -> (ProofRes, Option<Proof>) {
1460 synced(|_| unsafe {
1461 let res = self.unsynce_solve();
1462
1463 let ProofRes::Proved = res else {
1464 return (res, None);
1465 };
1466
1467 let refutation = sys::vampire_get_refutation();
1468 let proof = Proof::from_refutation(refutation);
1469
1470 (res, Some(proof))
1471 })
1472 }
1473}
1474
1475/// The result of attempting to prove a theorem.
1476///
1477/// After calling [`Problem::solve`], Vampire returns one of three possible results:
1478/// - [`ProofRes::Proved`]: The conjecture was successfully proved from the axioms
1479/// - [`ProofRes::Unprovable`]: The axioms are insufficient to prove the conjecture
1480/// - [`ProofRes::Unknown`]: Vampire could not determine if the axioms imply the conjecture
1481///
1482/// # Examples
1483///
1484/// ```
1485/// use vampire_prover::{Function, Predicate, Problem, ProofRes, Options, forall};
1486///
1487/// let p = Predicate::new("P", 1);
1488/// let x = Function::constant("x");
1489///
1490/// let result = Problem::new(Options::new())
1491/// .with_axiom(p.with(x))
1492/// .conjecture(p.with(x))
1493/// .solve();
1494///
1495/// match result {
1496/// ProofRes::Proved => println!("Theorem proved!"),
1497/// ProofRes::Unprovable => println!("Counterexample found"),
1498/// ProofRes::Unknown(reason) => println!("Unknown: {:?}", reason),
1499/// }
1500/// ```
1501#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1502pub enum ProofRes {
1503 /// The conjecture was successfully proved from the axioms.
1504 Proved,
1505
1506 /// The axioms are insufficient to prove the conjecture.
1507 ///
1508 /// Vampire has determined that the given axioms do not imply the conjecture.
1509 /// Note that this does not mean the conjecture is false - it could still be
1510 /// true or false, but the provided axioms alone cannot establish it.
1511 Unprovable,
1512
1513 /// Vampire could not determine whether the axioms imply the conjecture.
1514 ///
1515 /// This can happen for several reasons, detailed in [`UnknownReason`].
1516 Unknown(UnknownReason),
1517}
1518
1519/// The reason why a proof result is unknown.
1520///
1521/// When Vampire cannot determine whether a conjecture is provable, it returns
1522/// [`ProofRes::Unknown`] with one of these reasons.
1523///
1524/// # Examples
1525///
1526/// ```
1527/// use vampire_prover::{ProofRes, UnknownReason};
1528///
1529/// let result = ProofRes::Unknown(UnknownReason::Timeout);
1530///
1531/// if let ProofRes::Unknown(reason) = result {
1532/// match reason {
1533/// UnknownReason::Timeout => println!("Ran out of time"),
1534/// UnknownReason::MemoryLimit => println!("Ran out of memory"),
1535/// UnknownReason::Incomplete => println!("Problem uses incomplete logic"),
1536/// UnknownReason::Unknown => println!("Unknown reason"),
1537/// }
1538/// }
1539/// ```
1540#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1541pub enum UnknownReason {
1542 /// The prover exceeded its time limit before finding a proof or counterexample.
1543 Timeout,
1544
1545 /// The prover exceeded its memory limit before finding a proof or counterexample.
1546 MemoryLimit,
1547
1548 /// The problem involves features that make the logic incomplete.
1549 ///
1550 /// Some logical theories (e.g., higher-order logic, certain arithmetic theories)
1551 /// are undecidable, meaning no algorithm can always find an answer.
1552 Incomplete,
1553
1554 /// The reason is unknown or not specified by Vampire.
1555 Unknown,
1556}
1557
1558impl ProofRes {
1559 fn new_from_raw(idx: u32) -> ProofRes {
1560 if idx == sys::vampire_proof_result_t_VAMPIRE_PROOF {
1561 ProofRes::Proved
1562 } else if idx == sys::vampire_proof_result_t_VAMPIRE_SATISFIABLE {
1563 ProofRes::Unprovable
1564 } else if idx == sys::vampire_proof_result_t_VAMPIRE_TIMEOUT {
1565 ProofRes::Unknown(UnknownReason::Timeout)
1566 } else if idx == sys::vampire_proof_result_t_VAMPIRE_MEMORY_LIMIT {
1567 ProofRes::Unknown(UnknownReason::MemoryLimit)
1568 } else if idx == sys::vampire_proof_result_t_VAMPIRE_INCOMPLETE {
1569 ProofRes::Unknown(UnknownReason::Incomplete)
1570 } else if idx == sys::vampire_proof_result_t_VAMPIRE_UNKNOWN {
1571 ProofRes::Unknown(UnknownReason::Unknown)
1572 } else {
1573 panic!()
1574 }
1575 }
1576}
1577
1578/// A proof produced by the Vampire theorem prover.
1579///
1580/// A `Proof` is a sequence of [`ProofStep`]s that together form a complete
1581/// derivation of a contradiction from the negated conjecture and axioms.
1582/// Each step records the inference rule used and the indices of the premises
1583/// (earlier steps) it was derived from.
1584///
1585/// Proofs are obtained via [`Problem::solve_and_prove`].
1586///
1587/// # Display
1588///
1589/// `Proof` implements [`std::fmt::Display`], which prints each step on its own
1590/// line in the format `<index>: <conclusion> [<rule> <premise-indices>]`.
1591///
1592/// # Examples
1593///
1594/// ```
1595/// use vampire_prover::{Function, Predicate, Problem, ProofRes, Options};
1596///
1597/// let p = Predicate::new("P", 1);
1598/// let x = Function::constant("x");
1599///
1600/// let (_, proof) = Problem::new(Options::new())
1601/// .with_axiom(p.with(x))
1602/// .conjecture(p.with(x))
1603/// .solve_and_prove();
1604///
1605/// if let Some(proof) = proof {
1606/// for step in proof.steps() {
1607/// println!("{}: {:?}", step.discovery_order(), step.rule());
1608/// }
1609/// }
1610/// ```
1611#[derive(Debug, Clone, PartialEq, Eq)]
1612pub struct Proof {
1613 steps: Vec<ProofStep>,
1614}
1615
1616impl Proof {
1617 unsafe fn from_refutation(refutation: *mut vampire_unit_t) -> Self {
1618 unsafe {
1619 let mut steps_ptr = std::ptr::null_mut();
1620 let mut steps_len: usize = 0;
1621 let success = sys::vampire_extract_proof(refutation, &mut steps_ptr, &mut steps_len);
1622 assert!(success == 0);
1623
1624 let vsteps = std::slice::from_raw_parts(steps_ptr, steps_len);
1625 let mut vsteps = vsteps.to_vec();
1626 vsteps.sort_by_key(|s| s.id);
1627
1628 let mut steps = Vec::new();
1629 let mut step_map = HashMap::new();
1630
1631 for vstep in vsteps {
1632 let discovery_order = vstep.id;
1633 let rule = ProofRule::from_raw(vstep.rule, vstep.input_type);
1634 let premises = if vstep.premise_count == 0 {
1635 Vec::new()
1636 } else {
1637 std::slice::from_raw_parts(vstep.premise_ids, vstep.premise_count)
1638 .iter()
1639 .map(|p| step_map[p])
1640 .collect()
1641 };
1642
1643 let conclusion = sys::vampire_unit_as_formula(vstep.unit);
1644 let conclusion = Formula { id: conclusion };
1645
1646 step_map.insert(discovery_order, steps.len());
1647 let step = ProofStep {
1648 discovery_order,
1649 rule,
1650 premises,
1651 conclusion,
1652 };
1653 steps.push(step);
1654 }
1655
1656 sys::vampire_free_proof_steps(steps_ptr, steps_len);
1657
1658 Self { steps }
1659 }
1660 }
1661
1662 /// Returns all proof steps in the order Vampire discovered them.
1663 ///
1664 /// Steps are sorted by their [`ProofStep::discovery_order`]. Because each
1665 /// step's premises always have a lower discovery order than the step itself,
1666 /// this ordering is also a valid topological ordering of the proof DAG.
1667 pub fn steps(&self) -> &[ProofStep] {
1668 &self.steps
1669 }
1670
1671 /// Iterates over proof steps in topological order (discovery order).
1672 ///
1673 /// This is equivalent to iterating over [`Proof::steps`] and is provided
1674 /// for clarity when the topological property matters.
1675 pub fn topo_iter(&self) -> impl Iterator<Item = &ProofStep> {
1676 self.steps.iter()
1677 }
1678}
1679
1680impl Display for Proof {
1681 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1682 for (i, step) in self.steps().iter().enumerate() {
1683 writeln!(
1684 f,
1685 "{}: {} [{:?}{}]",
1686 i,
1687 step.conclusion(),
1688 step.rule(),
1689 step.premises()
1690 .iter()
1691 .fold(String::new(), |s, p| s + " " + &p.to_string())
1692 )?
1693 }
1694
1695 Ok(())
1696 }
1697}
1698
1699impl Index<usize> for Proof {
1700 type Output = ProofStep;
1701
1702 fn index(&self, index: usize) -> &Self::Output {
1703 &self.steps[index]
1704 }
1705}
1706
1707/// The inference rule used to derive a [`ProofStep`].
1708///
1709/// Each step in a [`Proof`] was produced by one of these rules. Input steps
1710/// (axioms and the negated conjecture) use [`ProofRule::Axiom`] or
1711/// [`ProofRule::NegatedConjecture`]. All other variants represent internal
1712/// Vampire inference rules applied during the proof search.
1713#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
1714pub enum ProofRule {
1715 // claude: don't document what these individual rules are.
1716 Axiom,
1717 NegatedConjecture,
1718 Rectify,
1719 Flatten,
1720 EENFTransformation,
1721 CNFTransformation,
1722 NNFTransformation,
1723 SkolemSymbolIntroduction,
1724 Skolemize,
1725 Superposition,
1726 ForwardDemodulation,
1727 BackwardDemodulation,
1728 ForwardSubsumptionResolution,
1729 Resolution,
1730 TrivialInequalityRemoval,
1731 Avatar,
1732 Other,
1733}
1734
1735impl ProofRule {
1736 fn from_raw(rule: u32, input_type: u32) -> Self {
1737 if rule == sys::vampire_inference_rule_t_INPUT {
1738 if input_type == sys::vampire_input_type_t_VAMPIRE_AXIOM {
1739 Self::Axiom
1740 } else if input_type == sys::vampire_input_type_t_VAMPIRE_NEGATED_CONJECTURE {
1741 Self::NegatedConjecture
1742 } else {
1743 unreachable!()
1744 }
1745 } else if rule == sys::vampire_inference_rule_t_RECTIFY {
1746 Self::Rectify
1747 } else if rule == sys::vampire_inference_rule_t_FLATTEN {
1748 Self::Flatten
1749 } else if rule == sys::vampire_inference_rule_t_ENNF {
1750 Self::EENFTransformation
1751 } else if rule == sys::vampire_inference_rule_t_CLAUSIFY {
1752 Self::CNFTransformation
1753 } else if rule == sys::vampire_inference_rule_t_NNF {
1754 Self::NNFTransformation
1755 } else if rule == sys::vampire_inference_rule_t_SKOLEM_SYMBOL_INTRODUCTION {
1756 Self::Skolemize
1757 } else if rule == sys::vampire_inference_rule_t_SKOLEMIZE {
1758 Self::SkolemSymbolIntroduction
1759 } else if rule == sys::vampire_inference_rule_t_SUPERPOSITION {
1760 Self::Superposition
1761 } else if rule == sys::vampire_inference_rule_t_FORWARD_DEMODULATION {
1762 Self::ForwardDemodulation
1763 } else if rule == sys::vampire_inference_rule_t_BACKWARD_DEMODULATION {
1764 Self::BackwardDemodulation
1765 } else if rule == sys::vampire_inference_rule_t_FORWARD_SUBSUMPTION_RESOLUTION {
1766 Self::ForwardSubsumptionResolution
1767 } else if rule == sys::vampire_inference_rule_t_RESOLUTION {
1768 Self::Resolution
1769 } else if rule == sys::vampire_inference_rule_t_TRIVIAL_INEQUALITY_REMOVAL {
1770 Self::TrivialInequalityRemoval
1771 } else if rule == sys::vampire_inference_rule_t_AVATAR_DEFINITION
1772 || rule == sys::vampire_inference_rule_t_AVATAR_COMPONENT
1773 || rule == sys::vampire_inference_rule_t_AVATAR_SPLIT_CLAUSE
1774 || rule == sys::vampire_inference_rule_t_AVATAR_CONTRADICTION_CLAUSE
1775 || rule == sys::vampire_inference_rule_t_AVATAR_REFUTATION
1776 {
1777 Self::Avatar
1778 } else {
1779 Self::Other
1780 }
1781 }
1782}
1783
1784/// A single step in a [`Proof`].
1785///
1786/// Each step records:
1787/// - The [`Formula`] derived at this step ([`ProofStep::conclusion`])
1788/// - The [`ProofRule`] used to derive it ([`ProofStep::rule`])
1789/// - The indices (into [`Proof::steps`]) of the premises this step depends on
1790/// ([`ProofStep::premises`])
1791/// - A discovery-order counter assigned by Vampire ([`ProofStep::discovery_order`])
1792#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1793pub struct ProofStep {
1794 discovery_order: u32,
1795 rule: ProofRule,
1796 premises: Vec<usize>,
1797 conclusion: Formula,
1798}
1799
1800impl ProofStep {
1801 /// Returns the formula derived at this step.
1802 pub fn conclusion(&self) -> Formula {
1803 self.conclusion
1804 }
1805
1806 /// Returns the inference rule used to derive this step.
1807 pub fn rule(&self) -> ProofRule {
1808 self.rule
1809 }
1810
1811 /// Returns the indices of the premises this step was derived from.
1812 ///
1813 /// Each value is an index into the [`Proof::steps`] slice (i.e. the position
1814 /// of the premise among the steps that actually appear in the proof, not its
1815 /// [`ProofStep::discovery_order`]).
1816 pub fn premises(&self) -> &[usize] {
1817 &self.premises
1818 }
1819
1820 /// Returns Vampire's internal discovery-order ID for this step.
1821 ///
1822 /// As Vampire searches for a proof it assigns every derived fact a
1823 /// monotonically increasing numeric ID. When the proof is extracted, only
1824 /// the facts that were actually *used* in the refutation are included, so
1825 /// there may be gaps in the sequence. The steps in [`Proof::steps`] are
1826 /// sorted by this value, which also gives a valid topological ordering of
1827 /// the proof DAG.
1828 pub fn discovery_order(&self) -> u32 {
1829 self.discovery_order
1830 }
1831}
1832
1833#[cfg(test)]
1834mod test {
1835 use crate::{Function, Options, Predicate, Problem, ProofRes, Term, exists, forall};
1836
1837 #[test]
1838 fn test_with_syntax() {
1839 // Test that all three calling styles work
1840 let f = Function::new("f", 2);
1841 let p = Predicate::new("p", 1);
1842 let x = Term::new_var(0);
1843 let y = Term::new_var(1);
1844
1845 // Test arrays
1846 let _t1 = f.with([x, y]);
1847 let _f1 = p.with([x]);
1848
1849 // Test slice references
1850 let _t2 = f.with(&[x, y]);
1851 let _f2 = p.with(&[x]);
1852
1853 let _t3 = f.with(&vec![x, y]);
1854 let _f3 = p.with(vec![x]);
1855
1856 // Test single term
1857 let _f4 = p.with(x);
1858 }
1859
1860 #[test]
1861 fn socrates_proof() {
1862 // Classic Socrates syllogism
1863 let is_mortal = Predicate::new("mortal", 1);
1864 let is_man = Predicate::new("man", 1);
1865
1866 // All men are mortal
1867 let men_are_mortal = forall(|x| is_man.with(x) >> is_mortal.with(x));
1868
1869 // Socrates is a man
1870 let socrates = Function::constant("socrates");
1871 let socrates_is_man = is_man.with(socrates);
1872
1873 // Therefore, Socrates is mortal
1874 let socrates_is_mortal = is_mortal.with(socrates);
1875
1876 let solution = Problem::new(Options::new())
1877 .with_axiom(socrates_is_man)
1878 .with_axiom(men_are_mortal)
1879 .conjecture(socrates_is_mortal)
1880 .solve();
1881
1882 assert_eq!(solution, ProofRes::Proved);
1883 }
1884
1885 #[test]
1886 fn graph_reachability() {
1887 // Prove transitive reachability in a graph
1888 // Given: edge(a,b), edge(b,c), edge(c,d), edge(d,e)
1889 // And: path(x,y) if edge(x,y)
1890 // And: path is transitive: path(x,y) ∧ path(y,z) → path(x,z)
1891 // Prove: path(a,e)
1892
1893 let edge = Predicate::new("edge", 2);
1894 let path = Predicate::new("path", 2);
1895
1896 // Define nodes
1897 let a = Function::constant("a");
1898 let b = Function::constant("b");
1899 let c = Function::constant("c");
1900 let d = Function::constant("d");
1901 let e = Function::constant("e");
1902
1903 // Axiom 1: Direct edges are paths
1904 // ∀x,y. edge(x,y) → path(x,y)
1905 let direct_edge_is_path = forall(|x| forall(|y| edge.with([x, y]) >> path.with([x, y])));
1906
1907 // Axiom 2: Transitivity of paths
1908 // ∀x,y,z. path(x,y) ∧ path(y,z) → path(x,z)
1909 let path_transitivity = forall(|x| {
1910 forall(|y| forall(|z| (path.with([x, y]) & path.with([y, z])) >> path.with([x, z])))
1911 });
1912
1913 // Concrete edges in the graph
1914 let edge_ab = edge.with([a, b]);
1915 let edge_bc = edge.with([b, c]);
1916 let edge_cd = edge.with([c, d]);
1917 let edge_de = edge.with([d, e]);
1918
1919 // Conjecture: there is a path from a to e
1920 let conjecture = path.with([a, e]);
1921
1922 let solution = Problem::new(Options::new())
1923 .with_axiom(direct_edge_is_path)
1924 .with_axiom(path_transitivity)
1925 .with_axiom(edge_ab)
1926 .with_axiom(edge_bc)
1927 .with_axiom(edge_cd)
1928 .with_axiom(edge_de)
1929 .conjecture(conjecture)
1930 .solve();
1931
1932 assert_eq!(solution, ProofRes::Proved);
1933 }
1934
1935 #[test]
1936 fn group_left_identity() {
1937 // Prove that the identity element works on the left using group axioms
1938 // In group theory, if we define a group with:
1939 // - Right identity: x * 1 = x
1940 // - Right inverse: x * inv(x) = 1
1941 // - Associativity: (x * y) * z = x * (y * z)
1942 // Then we can prove the left identity: 1 * x = x
1943
1944 let mult = Function::new("mult", 2);
1945 let inv = Function::new("inv", 1);
1946 let one = Function::constant("1");
1947
1948 // Helper to make multiplication more readable
1949 let mul = |x: Term, y: Term| -> Term { mult.with([x, y]) };
1950
1951 // Axiom 1: Right identity - ∀x. x * 1 = x
1952 let right_identity = forall(|x| mul(x, one).eq(x));
1953
1954 // Axiom 2: Right inverse - ∀x. x * inv(x) = 1
1955 let right_inverse = forall(|x| {
1956 let inv_x = inv.with(x);
1957 mul(x, inv_x).eq(one)
1958 });
1959
1960 // Axiom 3: Associativity - ∀x,y,z. (x * y) * z = x * (y * z)
1961 let associativity =
1962 forall(|x| forall(|y| forall(|z| mul(mul(x, y), z).eq(mul(x, mul(y, z))))));
1963
1964 // Conjecture: Left identity - ∀x. 1 * x = x
1965 let left_identity = forall(|x| mul(one, x).eq(x));
1966
1967 let solution = Problem::new(Options::new())
1968 .with_axiom(right_identity)
1969 .with_axiom(right_inverse)
1970 .with_axiom(associativity)
1971 .conjecture(left_identity)
1972 .solve();
1973
1974 assert_eq!(solution, ProofRes::Proved);
1975 }
1976
1977 #[test]
1978 fn group_index2_subgroup_normal() {
1979 // Prove that every subgroup of index 2 is normal.
1980 let mult = Function::new("mult", 2);
1981 let inv = Function::new("inv", 1);
1982 let one = Function::constant("1");
1983
1984 // Helper to make multiplication more readable
1985 let mul = |x: Term, y: Term| -> Term { mult.with([x, y]) };
1986
1987 // Group Axiom 1: Right identity - ∀x. x * 1 = x
1988 let right_identity = forall(|x| mul(x, one).eq(x));
1989
1990 // Group Axiom 2: Right inverse - ∀x. x * inv(x) = 1
1991 let right_inverse = forall(|x| {
1992 let inv_x = inv.with(x);
1993 mul(x, inv_x).eq(one)
1994 });
1995
1996 // Group Axiom 3: Associativity - ∀x,y,z. (x * y) * z = x * (y * z)
1997 let associativity =
1998 forall(|x| forall(|y| forall(|z| mul(mul(x, y), z).eq(mul(x, mul(y, z))))));
1999
2000 // Describe the subgroup
2001 let h = Predicate::new("h", 1);
2002
2003 // Any subgroup contains the identity
2004 let h_ident = h.with(one);
2005
2006 // And is closed under multiplication
2007 let h_mul_closed = forall(|x| forall(|y| (h.with(x) & h.with(y)) >> h.with(mul(x, y))));
2008
2009 // And is closed under inverse
2010 let h_inv_closed = forall(|x| h.with(x) >> h.with(inv.with(x)));
2011
2012 // H specifically is of order 2
2013 let h_index_2 = exists(|x| {
2014 // an element not in H
2015 let not_in_h = !h.with(x);
2016 // but everything is in H or x H
2017 let class = forall(|y| h.with(y) | h.with(mul(inv.with(x), y)));
2018
2019 not_in_h & class
2020 });
2021
2022 // Conjecture: H is normal
2023 let h_normal = forall(|x| {
2024 let h_x = h.with(x);
2025 let conj_x = forall(|y| {
2026 let y_inv = inv.with(y);
2027 h.with(mul(mul(y, x), y_inv))
2028 });
2029 h_x.iff(conj_x)
2030 });
2031
2032 let solution = Problem::new(Options::new())
2033 .with_axiom(right_identity)
2034 .with_axiom(right_inverse)
2035 .with_axiom(associativity)
2036 .with_axiom(h_ident)
2037 .with_axiom(h_mul_closed)
2038 .with_axiom(h_inv_closed)
2039 .with_axiom(h_index_2)
2040 .conjecture(h_normal)
2041 .solve();
2042
2043 assert_eq!(solution, ProofRes::Proved);
2044 }
2045
2046 #[test]
2047 fn term_structural_equality() {
2048 let f = Function::new("f_teq", 2);
2049 let g = Function::new("g_teq", 1);
2050
2051 // Same constant constructed twice: equal
2052 let a1 = Function::constant("a_teq");
2053 let a2 = Function::constant("a_teq");
2054 assert_eq!(a1, a2);
2055
2056 // Different constants: not equal
2057 let b = Function::constant("b_teq");
2058 assert_ne!(a1, b);
2059
2060 // Same compound term constructed twice: equal
2061 let t1 = f.with([g.with(a1), b]);
2062 let t2 = f.with([g.with(a1), b]);
2063 assert_eq!(t1, t2);
2064
2065 // Different argument: not equal
2066 let t3 = f.with([g.with(b), b]);
2067 assert_ne!(t1, t3);
2068
2069 // Same variable index: equal
2070 let x0a = Term::new_var(0);
2071 let x0b = Term::new_var(0);
2072 assert_eq!(x0a, x0b);
2073
2074 // Different variable indices: not equal
2075 let x1 = Term::new_var(1);
2076 assert_ne!(x0a, x1);
2077
2078 // Variable vs constant: not equal
2079 assert_ne!(x0a, a1);
2080 }
2081
2082 #[test]
2083 fn term_structural_hash() {
2084 use std::collections::HashSet;
2085
2086 let f = Function::new("f_thash", 2);
2087 let a = Function::constant("a_thash");
2088 let b = Function::constant("b_thash");
2089
2090 let t1 = f.with([a, b]);
2091 let t2 = f.with([a, b]);
2092 let t3 = f.with([b, a]);
2093
2094 // Equal terms hash identically (required by Hash contract)
2095 let mut set = HashSet::new();
2096 set.insert(t1);
2097 assert!(set.contains(&t2));
2098
2099 // Different terms can be stored together
2100 set.insert(t3);
2101 assert_eq!(set.len(), 2);
2102 }
2103
2104 #[test]
2105 fn formula_structural_equality() {
2106 let p = Predicate::new("p_feq", 1);
2107 let q = Predicate::new("q_feq", 1);
2108 let a = Function::constant("a_feq");
2109 let b = Function::constant("b_feq");
2110
2111 // Same atomic formula constructed twice: equal
2112 let pa1 = p.with(a);
2113 let pa2 = p.with(a);
2114 assert_eq!(pa1, pa2);
2115
2116 // Different predicates: not equal
2117 assert_ne!(p.with(a), q.with(a));
2118
2119 // Different arguments: not equal
2120 assert_ne!(p.with(a), p.with(b));
2121
2122 // Negation: equal iff inner equal
2123 assert_eq!(!pa1, !pa2);
2124 assert_ne!(!pa1, !p.with(b));
2125
2126 // Conjunction
2127 let f1 = p.with(a) & q.with(b);
2128 let f2 = p.with(a) & q.with(b);
2129 let f3 = p.with(a) & q.with(a);
2130 assert_eq!(f1, f2);
2131 assert_ne!(f1, f3);
2132
2133 // Implication
2134 let i1 = p.with(a) >> q.with(b);
2135 let i2 = p.with(a) >> q.with(b);
2136 assert_eq!(i1, i2);
2137 // Implication is not symmetric
2138 assert_ne!(p.with(a) >> q.with(b), q.with(b) >> p.with(a));
2139
2140 // A formula is equal to itself (Copy type, same variable index)
2141 let fa = forall(|x| p.with(x));
2142 assert_eq!(fa, fa);
2143
2144 // forall vs exists with same body: not equal (different connective)
2145 let fe = exists(|x| p.with(x));
2146 assert_ne!(fa, fe);
2147
2148 // Equality literals: same args → equal
2149 let eq1 = a.eq(b);
2150 let eq2 = a.eq(b);
2151 assert_eq!(eq1, eq2);
2152 // Equality literal vs inequality literal: not equal
2153 assert_ne!(a.eq(b), !p.with(a));
2154 }
2155
2156 #[test]
2157 fn formula_structural_hash() {
2158 use std::collections::HashSet;
2159
2160 let p = Predicate::new("p_fhash", 1);
2161 let q = Predicate::new("q_fhash", 1);
2162 let a = Function::constant("a_fhash");
2163
2164 let f1 = p.with(a) & q.with(a);
2165 let f2 = p.with(a) & q.with(a);
2166 let f3 = p.with(a) | q.with(a);
2167
2168 let mut set = HashSet::new();
2169 set.insert(f1);
2170 // Equal formula is found in the set
2171 assert!(set.contains(&f2));
2172 // Different formula can be added
2173 set.insert(f3);
2174 assert_eq!(set.len(), 2);
2175 }
2176}