Skip to main content

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}