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    ffi::CString,
155    ops::{BitAnd, BitOr, Not, Shr},
156    time::Duration,
157};
158use vampire_sys as sys;
159
160mod lock;
161
162/// Trait for types that can be converted into term arguments.
163///
164/// This trait allows `.with()` methods on [`Function`] and [`Predicate`] to accept
165/// different argument formats for convenience:
166/// - Single term: `f.with(x)`
167/// - Array: `f.with([x, y])`
168pub trait IntoTermArgs {
169    /// Convert this type into a slice of terms.
170    fn as_slice(&self) -> &[Term];
171}
172
173impl IntoTermArgs for Term {
174    fn as_slice(&self) -> &[Term] {
175        std::slice::from_ref(self)
176    }
177}
178
179impl<T> IntoTermArgs for T
180where
181    T: AsRef<[Term]>,
182{
183    fn as_slice(&self) -> &[Term] {
184        self.as_ref()
185    }
186}
187
188/// A function symbol in first-order logic.
189///
190/// Functions represent operations that take terms as arguments and produce new terms.
191/// They have a fixed arity (number of arguments). A function with arity 0 is called a
192/// constant and represents a specific object in the domain.
193///
194/// # Examples
195///
196/// ```
197/// use vampire_prover::Function;
198///
199/// // Create a constant (0-ary function)
200/// let socrates = Function::constant("socrates");
201///
202/// // Create a unary function
203/// let successor = Function::new("succ", 1);
204///
205/// // Create a binary function
206/// let add = Function::new("add", 2);
207/// ```
208#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
209pub struct Function {
210    id: u32,
211    arity: u32,
212}
213
214impl Function {
215    /// Creates a new function symbol with the given name and arity.
216    ///
217    /// Calling this method multiple times with the same name and arity will return
218    /// the same function symbol. It is safe to call this with the same name but
219    /// different arities - they will be treated as distinct function symbols.
220    ///
221    /// # Arguments
222    ///
223    /// * `name` - The name of the function symbol
224    /// * `arity` - The number of arguments this function takes
225    ///
226    /// # Examples
227    ///
228    /// ```
229    /// use vampire_prover::Function;
230    ///
231    /// let mult = Function::new("mult", 2);
232    /// assert_eq!(mult.arity(), 2);
233    ///
234    /// // Same name and arity returns the same symbol
235    /// let mult2 = Function::new("mult", 2);
236    /// assert_eq!(mult, mult2);
237    ///
238    /// // Same name but different arity is a different symbol
239    /// let mult3 = Function::new("mult", 3);
240    /// assert_ne!(mult.arity(), mult3.arity());
241    /// ```
242    pub fn new(name: &str, arity: u32) -> Self {
243        synced(|_| {
244            let name = CString::new(name).expect("valid c string");
245            let function = unsafe { sys::vampire_add_function(name.as_ptr(), arity) };
246            Self {
247                id: function,
248                arity,
249            }
250        })
251    }
252
253    /// Returns the arity (number of arguments) of this function.
254    ///
255    /// # Examples
256    ///
257    /// ```
258    /// use vampire_prover::Function;
259    ///
260    /// let f = Function::new("f", 3);
261    /// assert_eq!(f.arity(), 3);
262    /// ```
263    pub fn arity(&self) -> u32 {
264        self.arity
265    }
266
267    /// Creates a constant term (0-ary function).
268    ///
269    /// This is a convenience method equivalent to `Function::new(name, 0).with([])`.
270    /// Constants represent specific objects in the domain.
271    ///
272    /// # Arguments
273    ///
274    /// * `name` - The name of the constant
275    ///
276    /// # Examples
277    ///
278    /// ```
279    /// use vampire_prover::Function;
280    ///
281    /// let socrates = Function::constant("socrates");
282    /// let zero = Function::constant("0");
283    /// ```
284    pub fn constant(name: &str) -> Term {
285        Self::new(name, 0).with([])
286    }
287
288    /// Applies this function to the given arguments, creating a term.
289    ///
290    /// This method accepts multiple argument formats for convenience:
291    /// - Single term: `f.with(x)`
292    /// - Array: `f.with([x, y])`
293    ///
294    /// # Panics
295    ///
296    /// Panics if the number of arguments does not match the function's arity.
297    ///
298    /// # Examples
299    ///
300    /// ```
301    /// use vampire_prover::{Function, Term};
302    ///
303    /// let add = Function::new("add", 2);
304    /// let x = Term::new_var(0);
305    /// let y = Term::new_var(1);
306    ///
307    /// // Multiple arguments:
308    /// let sum = add.with([x, y]);
309    ///
310    /// // Single argument:
311    /// let succ = Function::new("succ", 1);
312    /// let sx = succ.with(x);
313    /// ```
314    pub fn with(&self, args: impl IntoTermArgs) -> Term {
315        Term::new_function(*self, args.as_slice())
316    }
317}
318
319/// A predicate symbol in first-order logic.
320///
321/// Predicates represent relations or properties that can be true or false.
322/// They take terms as arguments and produce formulas. Like functions, predicates
323/// have a fixed arity.
324///
325/// # Examples
326///
327/// ```
328/// use vampire_prover::{Function, Predicate};
329///
330/// // Unary predicate (property)
331/// let is_mortal = Predicate::new("mortal", 1);
332/// let socrates = Function::constant("socrates");
333/// let formula = is_mortal.with(socrates); // mortal(socrates)
334///
335/// // Binary predicate (relation)
336/// let loves = Predicate::new("loves", 2);
337/// let alice = Function::constant("alice");
338/// let bob = Function::constant("bob");
339/// let formula = loves.with([alice, bob]); // loves(alice, bob)
340/// ```
341#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
342pub struct Predicate {
343    id: u32,
344    arity: u32,
345}
346
347impl Predicate {
348    /// Creates a new predicate symbol with the given name and arity.
349    ///
350    /// Calling this method multiple times with the same name and arity will return
351    /// the same predicate symbol. It is safe to call this with the same name but
352    /// different arities - they will be treated as distinct predicate symbols.
353    ///
354    /// # Arguments
355    ///
356    /// * `name` - The name of the predicate symbol
357    /// * `arity` - The number of arguments this predicate takes
358    ///
359    /// # Examples
360    ///
361    /// ```
362    /// use vampire_prover::Predicate;
363    ///
364    /// let edge = Predicate::new("edge", 2);
365    /// assert_eq!(edge.arity(), 2);
366    ///
367    /// // Same name and arity returns the same symbol
368    /// let edge2 = Predicate::new("edge", 2);
369    /// assert_eq!(edge, edge2);
370    ///
371    /// // Same name but different arity is a different symbol
372    /// let edge3 = Predicate::new("edge", 3);
373    /// assert_ne!(edge.arity(), edge3.arity());
374    /// ```
375    pub fn new(name: &str, arity: u32) -> Self {
376        // TODO: predicate/term with same name already exists?
377
378        synced(|_| {
379            let name = CString::new(name).expect("valid c string");
380            let predicate = unsafe { sys::vampire_add_predicate(name.as_ptr(), arity) };
381            Self {
382                id: predicate,
383                arity,
384            }
385        })
386    }
387
388    /// Returns the arity (number of arguments) of this predicate.
389    ///
390    /// # Examples
391    ///
392    /// ```
393    /// use vampire_prover::Predicate;
394    ///
395    /// let p = Predicate::new("p", 2);
396    /// assert_eq!(p.arity(), 2);
397    /// ```
398    pub fn arity(&self) -> u32 {
399        self.arity
400    }
401
402    /// Applies this predicate to the given arguments, creating a formula.
403    ///
404    /// This method accepts multiple argument formats for convenience:
405    /// - Single term: `p.with(x)`
406    /// - Array: `p.with([x, y])`
407    ///
408    /// # Panics
409    ///
410    /// Panics if the number of arguments does not match the predicate's arity.
411    ///
412    /// # Examples
413    ///
414    /// ```
415    /// use vampire_prover::{Function, Predicate};
416    ///
417    /// let mortal = Predicate::new("mortal", 1);
418    /// let socrates = Function::constant("socrates");
419    ///
420    /// // Single argument:
421    /// let formula = mortal.with(socrates);
422    ///
423    /// // Multiple arguments:
424    /// let edge = Predicate::new("edge", 2);
425    /// let a = Function::constant("a");
426    /// let b = Function::constant("b");
427    /// let e = edge.with([a, b]);
428    /// ```
429    pub fn with(&self, args: impl IntoTermArgs) -> Formula {
430        Formula::new_predicate(*self, args.as_slice())
431    }
432}
433
434/// A term in first-order logic.
435///
436/// Terms represent objects in the domain of discourse. A term can be:
437/// - A constant: `socrates`
438/// - A variable: `x`
439/// - A function application: `add(x, y)`
440///
441/// # Examples
442///
443/// ```
444/// use vampire_prover::{Function, Term};
445///
446/// // Create a constant
447/// let zero = Function::constant("0");
448///
449/// // Create a variable
450/// let x = Term::new_var(0);
451///
452/// // Create a function application
453/// let succ = Function::new("succ", 1);
454/// let one = succ.with(zero);
455/// ```
456#[derive(Clone, Copy, PartialEq, Eq, Hash)]
457#[repr(transparent)]
458pub struct Term {
459    id: *mut sys::vampire_term_t,
460}
461
462impl std::fmt::Debug for Term {
463    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
464        write!(f, "Term({})", self.to_string())
465    }
466}
467
468impl Term {
469    /// Converts this term to a string representation.
470    ///
471    /// # Panics
472    ///
473    /// Panics if the underlying C API fails (which should never happen in normal use).
474    ///
475    /// # Examples
476    ///
477    /// ```
478    /// use vampire_prover::Function;
479    ///
480    /// let x = Function::constant("x");
481    /// println!("{}", x.to_string()); // Prints the vampire string representation
482    /// ```
483    pub fn to_string(&self) -> String {
484        synced(|_| unsafe {
485            let ptr = sys::vampire_term_to_string(self.id);
486            assert!(!ptr.is_null(), "vampire_term_to_string returned null");
487
488            let c_str = std::ffi::CStr::from_ptr(ptr);
489            let result = c_str
490                .to_str()
491                .expect("vampire returned invalid UTF-8")
492                .to_string();
493            sys::vampire_free_string(ptr);
494            result
495        })
496    }
497
498    /// Creates a term by applying a function to arguments.
499    ///
500    /// This is typically called via [`Function::with`] rather than directly.
501    ///
502    /// # Panics
503    ///
504    /// Panics if the number of arguments does not match the function's arity.
505    ///
506    /// # Examples
507    ///
508    /// ```
509    /// use vampire_prover::{Function, Term};
510    ///
511    /// let add = Function::new("add", 2);
512    /// let x = Term::new_var(0);
513    /// let y = Term::new_var(1);
514    ///
515    /// let sum = Term::new_function(add, &[x, y]);
516    /// ```
517    pub fn new_function(func: Function, args: &[Term]) -> Self {
518        // TODO: try_new_function?
519        assert!(args.len() == func.arity() as usize);
520
521        synced(|_| unsafe {
522            let arg_count = args.len();
523            let args = std::mem::transmute(args.as_ptr());
524            let term = sys::vampire_term(func.id, args, arg_count);
525            Self { id: term }
526        })
527    }
528
529    /// Creates a variable with the given index.
530    ///
531    /// Variables are typically used within quantified formulas. The index should be
532    /// unique within a formula. For automatic variable management, consider using
533    /// the [`forall`] and [`exists`] helper functions instead.
534    ///
535    /// # Arguments
536    ///
537    /// * `idx` - The unique index for this variable
538    ///
539    /// # Examples
540    ///
541    /// ```
542    /// use vampire_prover::Term;
543    ///
544    /// let x = Term::new_var(0);
545    /// let y = Term::new_var(1);
546    /// ```
547    pub fn new_var(idx: u32) -> Self {
548        synced(|info| unsafe {
549            info.free_var = info.free_var.max(idx + 1);
550            let term = sys::vampire_var(idx);
551            Self { id: term }
552        })
553    }
554
555    /// Creates a fresh variable with an automatically assigned index.
556    ///
557    /// Returns both the variable term and its index. This is primarily used internally
558    /// by the [`forall`] and [`exists`] functions.
559    ///
560    /// # Examples
561    ///
562    /// ```
563    /// use vampire_prover::Term;
564    ///
565    /// let (x, idx) = Term::free_var();
566    /// assert_eq!(idx, 0);
567    ///
568    /// let (y, idx2) = Term::free_var();
569    /// assert_eq!(idx2, 1);
570    /// ```
571    pub fn free_var() -> (Self, u32) {
572        synced(|info| unsafe {
573            let idx = info.free_var;
574            info.free_var += 1;
575            let term = sys::vampire_var(idx);
576            (Self { id: term }, idx)
577        })
578    }
579
580    /// Creates an equality formula between this term and another.
581    ///
582    /// # Arguments
583    ///
584    /// * `rhs` - The right-hand side of the equality
585    ///
586    /// # Examples
587    ///
588    /// ```
589    /// use vampire_prover::{Function, forall};
590    ///
591    /// let succ = Function::new("succ", 1);
592    /// let zero = Function::constant("0");
593    ///
594    /// // ∀x. succ(x) = succ(x)
595    /// let reflexive = forall(|x| {
596    ///     let sx = succ.with(x);
597    ///     sx.eq(sx)
598    /// });
599    /// ```
600    pub fn eq(&self, rhs: Term) -> Formula {
601        Formula::new_eq(*self, rhs)
602    }
603}
604
605/// A formula in first-order logic.
606///
607/// Formulas are logical statements that can be true or false. They include:
608/// - Atomic formulas: predicates and equalities
609/// - Logical connectives: AND (`&`), OR (`|`), NOT (`!`), implication (`>>`), biconditional
610/// - Quantifiers: universal (`∀`) and existential (`∃`)
611///
612/// # Examples
613///
614/// ```
615/// use vampire_prover::{Function, Predicate, forall};
616///
617/// let p = Predicate::new("P", 1);
618/// let q = Predicate::new("Q", 1);
619/// let x = Function::constant("x");
620///
621/// // Atomic formula
622/// let px = p.with(x);
623/// let qx = q.with(x);
624///
625/// // Conjunction: P(x) ∧ Q(x)
626/// let both = px & qx;
627///
628/// // Disjunction: P(x) ∨ Q(x)
629/// let either = px | qx;
630///
631/// // Implication: P(x) → Q(x)
632/// let implies = px >> qx;
633///
634/// // Negation: ¬P(x)
635/// let not_px = !px;
636///
637/// // Universal quantification: ∀x. P(x)
638/// let all = forall(|x| p.with(x));
639/// ```
640#[derive(Clone, Copy, PartialEq, Eq, Hash)]
641#[repr(transparent)]
642pub struct Formula {
643    id: *mut sys::vampire_formula_t,
644}
645
646impl std::fmt::Debug for Formula {
647    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
648        write!(f, "Formula({})", self.to_string())
649    }
650}
651
652impl Formula {
653    /// Converts this formula to a string representation.
654    ///
655    /// # Panics
656    ///
657    /// Panics if the underlying C API fails (which should never happen in normal use).
658    ///
659    /// # Examples
660    ///
661    /// ```
662    /// use vampire_prover::{Function, Predicate};
663    ///
664    /// let p = Predicate::new("P", 1);
665    /// let x = Function::constant("x");
666    /// let formula = p.with(x);
667    /// println!("{}", formula.to_string()); // Prints the vampire string representation
668    /// ```
669    pub fn to_string(&self) -> String {
670        synced(|_| unsafe {
671            let ptr = sys::vampire_formula_to_string(self.id);
672            assert!(!ptr.is_null(), "vampire_formula_to_string returned null");
673
674            let c_str = std::ffi::CStr::from_ptr(ptr);
675            let result = c_str
676                .to_str()
677                .expect("vampire returned invalid UTF-8")
678                .to_string();
679            sys::vampire_free_string(ptr);
680            result
681        })
682    }
683
684    /// Creates an atomic formula by applying a predicate to arguments.
685    ///
686    /// This is typically called via [`Predicate::with`] rather than directly.
687    ///
688    /// # Panics
689    ///
690    /// Panics if the number of arguments does not match the predicate's arity.
691    ///
692    /// # Examples
693    ///
694    /// ```
695    /// use vampire_prover::{Function, Predicate, Formula};
696    ///
697    /// let mortal = Predicate::new("mortal", 1);
698    /// let socrates = Function::constant("socrates");
699    ///
700    /// let formula = Formula::new_predicate(mortal, &[socrates]);
701    /// ```
702    pub fn new_predicate(pred: Predicate, args: &[Term]) -> Self {
703        assert!(args.len() == pred.arity() as usize);
704
705        synced(|_| unsafe {
706            let arg_count = args.len();
707            let args = std::mem::transmute(args.as_ptr());
708            let lit = sys::vampire_lit(pred.id, true, args, arg_count);
709            let atom = sys::vampire_atom(lit);
710            Self { id: atom }
711        })
712    }
713
714    /// Creates an equality formula between two terms.
715    ///
716    /// This is typically called via [`Term::eq`] rather than directly.
717    ///
718    /// # Examples
719    ///
720    /// ```
721    /// use vampire_prover::{Function, Formula};
722    ///
723    /// let x = Function::constant("x");
724    /// let y = Function::constant("y");
725    ///
726    /// let eq = Formula::new_eq(x, y);
727    /// ```
728    pub fn new_eq(lhs: Term, rhs: Term) -> Self {
729        synced(|_| unsafe {
730            let lit = sys::vampire_eq(true, lhs.id, rhs.id);
731            let atom = sys::vampire_atom(lit);
732            Self { id: atom }
733        })
734    }
735
736    /// Creates a conjunction (AND) of multiple formulas.
737    ///
738    /// For two formulas, the `&` operator is more convenient.
739    ///
740    /// # Examples
741    ///
742    /// ```
743    /// use vampire_prover::{Function, Predicate, Formula};
744    ///
745    /// let p = Predicate::new("P", 1);
746    /// let q = Predicate::new("Q", 1);
747    /// let r = Predicate::new("R", 1);
748    /// let x = Function::constant("x");
749    ///
750    /// // P(x) ∧ Q(x) ∧ R(x)
751    /// let all_three = Formula::new_and(&[
752    ///     p.with(x),
753    ///     q.with(x),
754    ///     r.with(x),
755    /// ]);
756    /// ```
757    pub fn new_and(formulas: &[Formula]) -> Self {
758        synced(|_| unsafe {
759            let formula_count = formulas.len();
760            let formulas = std::mem::transmute(formulas.as_ptr());
761            let id = sys::vampire_and(formulas, formula_count);
762            Self { id }
763        })
764    }
765
766    /// Creates a disjunction (OR) of multiple formulas.
767    ///
768    /// For two formulas, the `|` operator is more convenient.
769    ///
770    /// # Examples
771    ///
772    /// ```
773    /// use vampire_prover::{Function, Predicate, Formula};
774    ///
775    /// let p = Predicate::new("P", 1);
776    /// let q = Predicate::new("Q", 1);
777    /// let r = Predicate::new("R", 1);
778    /// let x = Function::constant("x");
779    ///
780    /// // P(x) ∨ Q(x) ∨ R(x)
781    /// let any = Formula::new_or(&[
782    ///     p.with(x),
783    ///     q.with(x),
784    ///     r.with(x),
785    /// ]);
786    /// ```
787    pub fn new_or(formulas: &[Formula]) -> Self {
788        synced(|_| unsafe {
789            let formula_count = formulas.len();
790            let formulas = std::mem::transmute(formulas.as_ptr());
791            let id = sys::vampire_or(formulas, formula_count);
792            Self { id }
793        })
794    }
795
796    /// Creates a negation (NOT) of a formula.
797    ///
798    /// The `!` operator is more convenient than calling this directly.
799    ///
800    /// # Examples
801    ///
802    /// ```
803    /// use vampire_prover::{Function, Predicate, Formula};
804    ///
805    /// let p = Predicate::new("P", 1);
806    /// let x = Function::constant("x");
807    ///
808    /// let not_p = Formula::new_not(p.with(x));
809    /// ```
810    pub fn new_not(formula: Formula) -> Self {
811        synced(|_| {
812            let id = unsafe { sys::vampire_not(formula.id) };
813            Self { id }
814        })
815    }
816
817    /// Creates the true (tautology) formula.
818    ///
819    /// # Examples
820    ///
821    /// ```
822    /// use vampire_prover::Formula;
823    ///
824    /// let t = Formula::new_true();
825    /// ```
826    pub fn new_true() -> Self {
827        synced(|_| {
828            let id = unsafe { sys::vampire_true() };
829            Self { id }
830        })
831    }
832
833    /// Creates the false (contradiction) formula.
834    ///
835    /// # Examples
836    ///
837    /// ```
838    /// use vampire_prover::Formula;
839    ///
840    /// let f = Formula::new_false();
841    /// ```
842    pub fn new_false() -> Self {
843        synced(|_| {
844            let id = unsafe { sys::vampire_false() };
845            Self { id }
846        })
847    }
848
849    /// Creates a universally quantified formula.
850    ///
851    /// The [`forall`] helper function provides a more ergonomic interface.
852    ///
853    /// # Arguments
854    ///
855    /// * `var` - The index of the variable to quantify
856    /// * `f` - The formula body
857    ///
858    /// # Examples
859    ///
860    /// ```
861    /// use vampire_prover::{Function, Predicate, Formula, Term};
862    ///
863    /// let p = Predicate::new("P", 1);
864    /// let x = Term::new_var(0);
865    ///
866    /// // ∀x. P(x)
867    /// let all_p = Formula::new_forall(0, p.with(x));
868    /// ```
869    pub fn new_forall(var: u32, f: Formula) -> Self {
870        synced(|_| {
871            let id = unsafe { sys::vampire_forall(var, f.id) };
872            Self { id }
873        })
874    }
875
876    /// Creates an existentially quantified formula.
877    ///
878    /// The [`exists`] helper function provides a more ergonomic interface.
879    ///
880    /// # Arguments
881    ///
882    /// * `var` - The index of the variable to quantify
883    /// * `f` - The formula body
884    ///
885    /// # Examples
886    ///
887    /// ```
888    /// use vampire_prover::{Function, Predicate, Formula, Term};
889    ///
890    /// let p = Predicate::new("P", 1);
891    /// let x = Term::new_var(0);
892    ///
893    /// // ∃x. P(x)
894    /// let some_p = Formula::new_exists(0, p.with(x));
895    /// ```
896    pub fn new_exists(var: u32, f: Formula) -> Self {
897        synced(|_| {
898            let id = unsafe { sys::vampire_exists(var, f.id) };
899            Self { id }
900        })
901    }
902
903    /// Creates an implication from this formula to another.
904    ///
905    /// The `>>` operator is more convenient than calling this directly.
906    ///
907    /// # Arguments
908    ///
909    /// * `rhs` - The consequent (right-hand side) of the implication
910    ///
911    /// # Examples
912    ///
913    /// ```
914    /// use vampire_prover::{Function, Predicate};
915    ///
916    /// let p = Predicate::new("P", 1);
917    /// let q = Predicate::new("Q", 1);
918    /// let x = Function::constant("x");
919    ///
920    /// // P(x) → Q(x)
921    /// let implication = p.with(x).imp(q.with(x));
922    /// ```
923    pub fn imp(&self, rhs: Formula) -> Self {
924        synced(|_| {
925            let id = unsafe { sys::vampire_imp(self.id, rhs.id) };
926            Self { id }
927        })
928    }
929
930    /// Creates a biconditional (if and only if) between this formula and another.
931    ///
932    /// A biconditional `P ↔ Q` is true when both formulas have the same truth value.
933    ///
934    /// # Arguments
935    ///
936    /// * `rhs` - The right-hand side of the biconditional
937    ///
938    /// # Examples
939    ///
940    /// ```
941    /// use vampire_prover::{Function, Predicate, forall};
942    ///
943    /// let even = Predicate::new("even", 1);
944    /// let div_by_2 = Predicate::new("divisible_by_2", 1);
945    ///
946    /// // ∀x. even(x) ↔ divisible_by_2(x)
947    /// let equiv = forall(|x| {
948    ///     even.with(x).iff(div_by_2.with(x))
949    /// });
950    /// ```
951    pub fn iff(&self, rhs: Formula) -> Self {
952        synced(|_| {
953            let id = unsafe { sys::vampire_iff(self.id, rhs.id) };
954            Self { id }
955        })
956    }
957}
958
959/// Creates a universally quantified formula using a closure.
960///
961/// This is the most ergonomic way to create formulas with universal quantification.
962/// The closure receives a fresh variable term that can be used in the formula body.
963///
964/// # Arguments
965///
966/// * `f` - A closure that takes a [`Term`] representing the quantified variable and
967///         returns a [`Formula`]
968///
969/// # Examples
970///
971/// ```
972/// use vampire_prover::{Function, Predicate, forall};
973///
974/// let p = Predicate::new("P", 1);
975///
976/// // ∀x. P(x)
977/// let all_p = forall(|x| p.with(x));
978///
979/// // Nested quantifiers: ∀x. ∀y. P(x, y)
980/// let p2 = Predicate::new("P", 2);
981/// let all_xy = forall(|x| forall(|y| p2.with([x, y])));
982/// ```
983///
984/// # Complex Example
985///
986/// ```
987/// use vampire_prover::{Function, Predicate, forall};
988///
989/// let mortal = Predicate::new("mortal", 1);
990/// let human = Predicate::new("human", 1);
991///
992/// // ∀x. human(x) → mortal(x)
993/// let humans_are_mortal = forall(|x| {
994///     human.with(x) >> mortal.with(x)
995/// });
996/// ```
997pub fn forall<F: FnOnce(Term) -> Formula>(f: F) -> Formula {
998    let (var, var_idx) = Term::free_var();
999    let f = f(var);
1000    Formula::new_forall(var_idx, f)
1001}
1002
1003/// Creates an existentially quantified formula using a closure.
1004///
1005/// This is the most ergonomic way to create formulas with existential quantification.
1006/// The closure receives a fresh variable term that can be used in the formula body.
1007///
1008/// # Arguments
1009///
1010/// * `f` - A closure that takes a [`Term`] representing the quantified variable and
1011///         returns a [`Formula`]
1012///
1013/// # Examples
1014///
1015/// ```
1016/// use vampire_prover::{Function, Predicate, exists};
1017///
1018/// let prime = Predicate::new("prime", 1);
1019///
1020/// // ∃x. prime(x) - "There exists a prime number"
1021/// let some_prime = exists(|x| prime.with(x));
1022///
1023/// // ∃x. ∃y. edge(x, y) - "There exists an edge"
1024/// let edge = Predicate::new("edge", 2);
1025/// let has_edge = exists(|x| exists(|y| edge.with([x, y])));
1026/// ```
1027///
1028/// # Complex Example
1029///
1030/// ```
1031/// use vampire_prover::{Function, Predicate, exists, forall};
1032///
1033/// let greater = Predicate::new("greater", 2);
1034///
1035/// // ∃x. ∀y. greater(x, y) - "There exists a maximum element"
1036/// let has_maximum = exists(|x| forall(|y| greater.with([x, y])));
1037/// ```
1038pub fn exists<F: FnOnce(Term) -> Formula>(f: F) -> Formula {
1039    let (var, var_idx) = Term::free_var();
1040    let f = f(var);
1041    Formula::new_exists(var_idx, f)
1042}
1043
1044/// Implements the `&` operator for conjunction (AND).
1045///
1046/// # Examples
1047///
1048/// ```
1049/// use vampire_prover::{Function, Predicate};
1050///
1051/// let p = Predicate::new("P", 1);
1052/// let q = Predicate::new("Q", 1);
1053/// let x = Function::constant("x");
1054///
1055/// // P(x) ∧ Q(x)
1056/// let both = p.with(x) & q.with(x);
1057/// ```
1058impl BitAnd for Formula {
1059    type Output = Formula;
1060
1061    fn bitand(self, rhs: Self) -> Self::Output {
1062        Formula::new_and(&[self, rhs])
1063    }
1064}
1065
1066/// Implements the `|` operator for disjunction (OR).
1067///
1068/// # Examples
1069///
1070/// ```
1071/// use vampire_prover::{Function, Predicate};
1072///
1073/// let p = Predicate::new("P", 1);
1074/// let q = Predicate::new("Q", 1);
1075/// let x = Function::constant("x");
1076///
1077/// // P(x) ∨ Q(x)
1078/// let either = p.with(x) | q.with(x);
1079/// ```
1080impl BitOr for Formula {
1081    type Output = Formula;
1082
1083    fn bitor(self, rhs: Self) -> Self::Output {
1084        Formula::new_or(&[self, rhs])
1085    }
1086}
1087
1088/// Implements the `!` operator for negation (NOT).
1089///
1090/// # Examples
1091///
1092/// ```
1093/// use vampire_prover::{Function, Predicate};
1094///
1095/// let p = Predicate::new("P", 1);
1096/// let x = Function::constant("x");
1097///
1098/// // ¬P(x)
1099/// let not_p = !p.with(x);
1100/// ```
1101impl Not for Formula {
1102    type Output = Formula;
1103
1104    fn not(self) -> Self::Output {
1105        Formula::new_not(self)
1106    }
1107}
1108
1109/// Implements the `>>` operator for implication.
1110///
1111/// # Examples
1112///
1113/// ```
1114/// use vampire_prover::{Function, Predicate};
1115///
1116/// let p = Predicate::new("P", 1);
1117/// let q = Predicate::new("Q", 1);
1118/// let x = Function::constant("x");
1119///
1120/// // P(x) → Q(x)
1121/// let implies = p.with(x) >> q.with(x);
1122/// ```
1123impl Shr for Formula {
1124    type Output = Formula;
1125
1126    fn shr(self, rhs: Self) -> Self::Output {
1127        self.imp(rhs)
1128    }
1129}
1130
1131/// Configuration options for the Vampire theorem prover.
1132///
1133/// Options allow you to configure the behavior of the prover, such as setting
1134/// time limits. Use the builder pattern to construct options.
1135///
1136/// # Examples
1137///
1138/// ```
1139/// use vampire_prover::Options;
1140/// use std::time::Duration;
1141///
1142/// // Default options (no timeout)
1143/// let opts = Options::new();
1144///
1145/// // Set a timeout
1146/// let opts = Options::new().timeout(Duration::from_secs(5));
1147/// ```
1148#[derive(Debug, Clone)]
1149pub struct Options {
1150    timeout: Option<Duration>,
1151}
1152
1153impl Options {
1154    /// Creates a new Options with default settings.
1155    ///
1156    /// By default, no timeout is set.
1157    ///
1158    /// # Examples
1159    ///
1160    /// ```
1161    /// use vampire_prover::Options;
1162    ///
1163    /// let opts = Options::new();
1164    /// ```
1165    pub fn new() -> Self {
1166        Self { timeout: None }
1167    }
1168
1169    /// Sets the timeout for the prover.
1170    ///
1171    /// If the prover exceeds this time limit, it will return
1172    /// [`ProofRes::Unknown(UnknownReason::Timeout)`].
1173    ///
1174    /// # Arguments
1175    ///
1176    /// * `duration` - The maximum time the prover should run
1177    ///
1178    /// # Examples
1179    ///
1180    /// ```
1181    /// use vampire_prover::Options;
1182    /// use std::time::Duration;
1183    ///
1184    /// let opts = Options::new().timeout(Duration::from_secs(10));
1185    /// ```
1186    pub fn timeout(&mut self, duration: Duration) -> &mut Self {
1187        self.timeout = Some(duration);
1188        self
1189    }
1190}
1191
1192impl Default for Options {
1193    fn default() -> Self {
1194        Self::new()
1195    }
1196}
1197
1198/// A theorem proving problem consisting of axioms and an optional conjecture.
1199///
1200/// A [`Problem`] is constructed by adding axioms (assumed to be true) and optionally
1201/// a conjecture (the statement to be proved). The problem is then solved by calling
1202/// [`Problem::solve`], which invokes the Vampire theorem prover.
1203///
1204/// # Examples
1205///
1206/// ## Basic Usage
1207///
1208/// ```
1209/// use vampire_prover::{Function, Predicate, Problem, ProofRes, Options, forall};
1210///
1211/// let mortal = Predicate::new("mortal", 1);
1212/// let human = Predicate::new("human", 1);
1213/// let socrates = Function::constant("socrates");
1214///
1215/// let result = Problem::new(Options::new())
1216///     .with_axiom(human.with(socrates))
1217///     .with_axiom(forall(|x| human.with(x) >> mortal.with(x)))
1218///     .conjecture(mortal.with(socrates))
1219///     .solve();
1220///
1221/// assert_eq!(result, ProofRes::Proved);
1222/// ```
1223///
1224/// ## Without Conjecture
1225///
1226/// You can also create problems without a conjecture to check satisfiability:
1227///
1228/// ```
1229/// use vampire_prover::{Function, Predicate, Problem, Options};
1230///
1231/// let p = Predicate::new("P", 1);
1232/// let x = Function::constant("x");
1233///
1234/// let result = Problem::new(Options::new())
1235///     .with_axiom(p.with(x))
1236///     .with_axiom(!p.with(x))  // Contradiction
1237///     .solve();
1238///
1239/// // This should be unsatisfiable
1240/// ```
1241#[derive(Debug, Clone)]
1242pub struct Problem {
1243    options: Options,
1244    axioms: Vec<Formula>,
1245    conjecture: Option<Formula>,
1246}
1247
1248impl Problem {
1249    /// Creates a new empty problem with the given options.
1250    ///
1251    /// # Arguments
1252    ///
1253    /// * `options` - Configuration options for the prover
1254    ///
1255    /// # Examples
1256    ///
1257    /// ```
1258    /// use vampire_prover::{Problem, Options};
1259    /// use std::time::Duration;
1260    ///
1261    /// // Default options
1262    /// let problem = Problem::new(Options::new());
1263    /// ```
1264    pub fn new(options: Options) -> Self {
1265        Self {
1266            options,
1267            axioms: Vec::new(),
1268            conjecture: None,
1269        }
1270    }
1271
1272    /// Adds an axiom to the problem.
1273    ///
1274    /// Axioms are formulas assumed to be true. The prover will use these axioms
1275    /// to attempt to prove the conjecture (if one is provided).
1276    ///
1277    /// This method consumes `self` and returns a new [`Problem`], allowing for
1278    /// method chaining.
1279    ///
1280    /// # Arguments
1281    ///
1282    /// * `f` - The axiom formula to add
1283    ///
1284    /// # Examples
1285    ///
1286    /// ```
1287    /// use vampire_prover::{Function, Predicate, Problem, Options, forall};
1288    ///
1289    /// let p = Predicate::new("P", 1);
1290    /// let q = Predicate::new("Q", 1);
1291    ///
1292    /// let problem = Problem::new(Options::new())
1293    ///     .with_axiom(forall(|x| p.with(x)))
1294    ///     .with_axiom(forall(|x| p.with(x) >> q.with(x)));
1295    /// ```
1296    pub fn with_axiom(&mut self, f: Formula) -> &mut Self {
1297        self.axioms.push(f);
1298        self
1299    }
1300
1301    /// Sets the conjecture for the problem.
1302    ///
1303    /// The conjecture is the statement that the prover will attempt to prove from
1304    /// the axioms. A problem can have at most one conjecture.
1305    ///
1306    /// This method consumes `self` and returns a new [`Problem`], allowing for
1307    /// method chaining.
1308    ///
1309    /// # Arguments
1310    ///
1311    /// * `f` - The conjecture formula
1312    ///
1313    /// # Examples
1314    ///
1315    /// ```
1316    /// use vampire_prover::{Function, Predicate, Problem, Options, forall};
1317    ///
1318    /// let p = Predicate::new("P", 1);
1319    /// let q = Predicate::new("Q", 1);
1320    ///
1321    /// let problem = Problem::new(Options::new())
1322    ///     .with_axiom(forall(|x| p.with(x) >> q.with(x)))
1323    ///     .conjecture(forall(|x| q.with(x)));  // Try to prove this
1324    /// ```
1325    pub fn conjecture(&mut self, f: Formula) -> &mut Self {
1326        self.conjecture = Some(f);
1327        self
1328    }
1329
1330    /// Solves the problem using the Vampire theorem prover.
1331    ///
1332    /// This method consumes the problem and invokes Vampire to either prove the
1333    /// conjecture from the axioms, find a counterexample, or determine that the
1334    /// result is unknown.
1335    ///
1336    /// # Returns
1337    ///
1338    /// A [`ProofRes`] indicating whether the conjecture was proved, found to be
1339    /// unprovable, or whether the result is unknown (due to timeout, memory limits,
1340    /// or incompleteness).
1341    ///
1342    /// # Examples
1343    ///
1344    /// ```
1345    /// use vampire_prover::{Function, Predicate, Problem, ProofRes, Options, forall};
1346    ///
1347    /// let p = Predicate::new("P", 1);
1348    /// let x = Function::constant("x");
1349    ///
1350    /// let result = Problem::new(Options::new())
1351    ///     .with_axiom(p.with(x))
1352    ///     .conjecture(p.with(x))
1353    ///     .solve();
1354    ///
1355    /// assert_eq!(result, ProofRes::Proved);
1356    /// ```
1357    pub fn solve(&mut self) -> ProofRes {
1358        synced(|_| unsafe {
1359            sys::vampire_prepare_for_next_proof();
1360
1361            // Apply timeout option if set
1362            if let Some(timeout) = self.options.timeout {
1363                let ms = timeout.as_millis().max(1);
1364                sys::vampire_set_time_limit_milliseconds(ms as i32);
1365            }
1366
1367            let mut units = Vec::new();
1368
1369            for axiom in &self.axioms {
1370                let axiom_unit = sys::vampire_axiom_formula(axiom.id);
1371                units.push(axiom_unit);
1372            }
1373            if let Some(conjecture) = self.conjecture {
1374                let conjecture_unit = sys::vampire_conjecture_formula(conjecture.id);
1375                units.push(conjecture_unit);
1376            }
1377
1378            let problem = sys::vampire_problem_from_units(units.as_mut_ptr(), units.len());
1379            let proof_res = sys::vampire_prove(problem);
1380
1381            ProofRes::new_from_raw(proof_res)
1382        })
1383    }
1384}
1385
1386/// The result of attempting to prove a theorem.
1387///
1388/// After calling [`Problem::solve`], Vampire returns one of three possible results:
1389/// - [`ProofRes::Proved`]: The conjecture was successfully proved from the axioms
1390/// - [`ProofRes::Unprovable`]: The axioms are insufficient to prove the conjecture
1391/// - [`ProofRes::Unknown`]: Vampire could not determine if the axioms imply the conjecture
1392///
1393/// # Examples
1394///
1395/// ```
1396/// use vampire_prover::{Function, Predicate, Problem, ProofRes, Options, forall};
1397///
1398/// let p = Predicate::new("P", 1);
1399/// let x = Function::constant("x");
1400///
1401/// let result = Problem::new(Options::new())
1402///     .with_axiom(p.with(x))
1403///     .conjecture(p.with(x))
1404///     .solve();
1405///
1406/// match result {
1407///     ProofRes::Proved => println!("Theorem proved!"),
1408///     ProofRes::Unprovable => println!("Counterexample found"),
1409///     ProofRes::Unknown(reason) => println!("Unknown: {:?}", reason),
1410/// }
1411/// ```
1412#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1413pub enum ProofRes {
1414    /// The conjecture was successfully proved from the axioms.
1415    Proved,
1416
1417    /// The axioms are insufficient to prove the conjecture.
1418    ///
1419    /// Vampire has determined that the given axioms do not imply the conjecture.
1420    /// Note that this does not mean the conjecture is false - it could still be
1421    /// true or false, but the provided axioms alone cannot establish it.
1422    Unprovable,
1423
1424    /// Vampire could not determine whether the axioms imply the conjecture.
1425    ///
1426    /// This can happen for several reasons, detailed in [`UnknownReason`].
1427    Unknown(UnknownReason),
1428}
1429
1430/// The reason why a proof result is unknown.
1431///
1432/// When Vampire cannot determine whether a conjecture is provable, it returns
1433/// [`ProofRes::Unknown`] with one of these reasons.
1434///
1435/// # Examples
1436///
1437/// ```
1438/// use vampire_prover::{ProofRes, UnknownReason};
1439///
1440/// let result = ProofRes::Unknown(UnknownReason::Timeout);
1441///
1442/// if let ProofRes::Unknown(reason) = result {
1443///     match reason {
1444///         UnknownReason::Timeout => println!("Ran out of time"),
1445///         UnknownReason::MemoryLimit => println!("Ran out of memory"),
1446///         UnknownReason::Incomplete => println!("Problem uses incomplete logic"),
1447///         UnknownReason::Unknown => println!("Unknown reason"),
1448///     }
1449/// }
1450/// ```
1451#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1452pub enum UnknownReason {
1453    /// The prover exceeded its time limit before finding a proof or counterexample.
1454    Timeout,
1455
1456    /// The prover exceeded its memory limit before finding a proof or counterexample.
1457    MemoryLimit,
1458
1459    /// The problem involves features that make the logic incomplete.
1460    ///
1461    /// Some logical theories (e.g., higher-order logic, certain arithmetic theories)
1462    /// are undecidable, meaning no algorithm can always find an answer.
1463    Incomplete,
1464
1465    /// The reason is unknown or not specified by Vampire.
1466    Unknown,
1467}
1468
1469impl ProofRes {
1470    fn new_from_raw(idx: u32) -> ProofRes {
1471        if idx == sys::vampire_proof_result_t_VAMPIRE_PROOF {
1472            ProofRes::Proved
1473        } else if idx == sys::vampire_proof_result_t_VAMPIRE_SATISFIABLE {
1474            ProofRes::Unprovable
1475        } else if idx == sys::vampire_proof_result_t_VAMPIRE_TIMEOUT {
1476            ProofRes::Unknown(UnknownReason::Timeout)
1477        } else if idx == sys::vampire_proof_result_t_VAMPIRE_MEMORY_LIMIT {
1478            ProofRes::Unknown(UnknownReason::MemoryLimit)
1479        } else if idx == sys::vampire_proof_result_t_VAMPIRE_INCOMPLETE {
1480            ProofRes::Unknown(UnknownReason::Incomplete)
1481        } else if idx == sys::vampire_proof_result_t_VAMPIRE_UNKNOWN {
1482            ProofRes::Unknown(UnknownReason::Unknown)
1483        } else {
1484            panic!()
1485        }
1486    }
1487}
1488
1489#[cfg(test)]
1490mod test {
1491    use crate::{Function, Options, Predicate, Problem, ProofRes, Term, exists, forall};
1492
1493    #[test]
1494    fn test_with_syntax() {
1495        // Test that all three calling styles work
1496        let f = Function::new("f", 2);
1497        let p = Predicate::new("p", 1);
1498        let x = Term::new_var(0);
1499        let y = Term::new_var(1);
1500
1501        // Test arrays
1502        let _t1 = f.with([x, y]);
1503        let _f1 = p.with([x]);
1504
1505        // Test slice references
1506        let _t2 = f.with(&[x, y]);
1507        let _f2 = p.with(&[x]);
1508
1509        let _t3 = f.with(&vec![x, y]);
1510        let _f3 = p.with(vec![x]);
1511
1512        // Test single term
1513        let _f4 = p.with(x);
1514    }
1515
1516    #[test]
1517    fn socrates_proof() {
1518        // Classic Socrates syllogism
1519        let is_mortal = Predicate::new("mortal", 1);
1520        let is_man = Predicate::new("man", 1);
1521
1522        // All men are mortal
1523        let men_are_mortal = forall(|x| is_man.with(x) >> is_mortal.with(x));
1524
1525        // Socrates is a man
1526        let socrates = Function::constant("socrates");
1527        let socrates_is_man = is_man.with(socrates);
1528
1529        // Therefore, Socrates is mortal
1530        let socrates_is_mortal = is_mortal.with(socrates);
1531
1532        let solution = Problem::new(Options::new())
1533            .with_axiom(socrates_is_man)
1534            .with_axiom(men_are_mortal)
1535            .conjecture(socrates_is_mortal)
1536            .solve();
1537
1538        assert_eq!(solution, ProofRes::Proved);
1539    }
1540
1541    #[test]
1542    fn graph_reachability() {
1543        // Prove transitive reachability in a graph
1544        // Given: edge(a,b), edge(b,c), edge(c,d), edge(d,e)
1545        // And: path(x,y) if edge(x,y)
1546        // And: path is transitive: path(x,y) ∧ path(y,z) → path(x,z)
1547        // Prove: path(a,e)
1548
1549        let edge = Predicate::new("edge", 2);
1550        let path = Predicate::new("path", 2);
1551
1552        // Define nodes
1553        let a = Function::constant("a");
1554        let b = Function::constant("b");
1555        let c = Function::constant("c");
1556        let d = Function::constant("d");
1557        let e = Function::constant("e");
1558
1559        // Axiom 1: Direct edges are paths
1560        // ∀x,y. edge(x,y) → path(x,y)
1561        let direct_edge_is_path = forall(|x| forall(|y| edge.with([x, y]) >> path.with([x, y])));
1562
1563        // Axiom 2: Transitivity of paths
1564        // ∀x,y,z. path(x,y) ∧ path(y,z) → path(x,z)
1565        let path_transitivity = forall(|x| {
1566            forall(|y| forall(|z| (path.with([x, y]) & path.with([y, z])) >> path.with([x, z])))
1567        });
1568
1569        // Concrete edges in the graph
1570        let edge_ab = edge.with([a, b]);
1571        let edge_bc = edge.with([b, c]);
1572        let edge_cd = edge.with([c, d]);
1573        let edge_de = edge.with([d, e]);
1574
1575        // Conjecture: there is a path from a to e
1576        let conjecture = path.with([a, e]);
1577
1578        let solution = Problem::new(Options::new())
1579            .with_axiom(direct_edge_is_path)
1580            .with_axiom(path_transitivity)
1581            .with_axiom(edge_ab)
1582            .with_axiom(edge_bc)
1583            .with_axiom(edge_cd)
1584            .with_axiom(edge_de)
1585            .conjecture(conjecture)
1586            .solve();
1587
1588        assert_eq!(solution, ProofRes::Proved);
1589    }
1590
1591    #[test]
1592    fn group_left_identity() {
1593        // Prove that the identity element works on the left using group axioms
1594        // In group theory, if we define a group with:
1595        //   - Right identity: x * 1 = x
1596        //   - Right inverse: x * inv(x) = 1
1597        //   - Associativity: (x * y) * z = x * (y * z)
1598        // Then we can prove the left identity: 1 * x = x
1599
1600        let mult = Function::new("mult", 2);
1601        let inv = Function::new("inv", 1);
1602        let one = Function::constant("1");
1603
1604        // Helper to make multiplication more readable
1605        let mul = |x: Term, y: Term| -> Term { mult.with([x, y]) };
1606
1607        // Axiom 1: Right identity - ∀x. x * 1 = x
1608        let right_identity = forall(|x| mul(x, one).eq(x));
1609
1610        // Axiom 2: Right inverse - ∀x. x * inv(x) = 1
1611        let right_inverse = forall(|x| {
1612            let inv_x = inv.with(x);
1613            mul(x, inv_x).eq(one)
1614        });
1615
1616        // Axiom 3: Associativity - ∀x,y,z. (x * y) * z = x * (y * z)
1617        let associativity =
1618            forall(|x| forall(|y| forall(|z| mul(mul(x, y), z).eq(mul(x, mul(y, z))))));
1619
1620        // Conjecture: Left identity - ∀x. 1 * x = x
1621        let left_identity = forall(|x| mul(one, x).eq(x));
1622
1623        let solution = Problem::new(Options::new())
1624            .with_axiom(right_identity)
1625            .with_axiom(right_inverse)
1626            .with_axiom(associativity)
1627            .conjecture(left_identity)
1628            .solve();
1629
1630        assert_eq!(solution, ProofRes::Proved);
1631    }
1632
1633    #[test]
1634    fn group_index2_subgroup_normal() {
1635        // Prove that every subgroup of index 2 is normal.
1636        let mult = Function::new("mult", 2);
1637        let inv = Function::new("inv", 1);
1638        let one = Function::constant("1");
1639
1640        // Helper to make multiplication more readable
1641        let mul = |x: Term, y: Term| -> Term { mult.with([x, y]) };
1642
1643        // Group Axiom 1: Right identity - ∀x. x * 1 = x
1644        let right_identity = forall(|x| mul(x, one).eq(x));
1645
1646        // Group Axiom 2: Right inverse - ∀x. x * inv(x) = 1
1647        let right_inverse = forall(|x| {
1648            let inv_x = inv.with(x);
1649            mul(x, inv_x).eq(one)
1650        });
1651
1652        // Group Axiom 3: Associativity - ∀x,y,z. (x * y) * z = x * (y * z)
1653        let associativity =
1654            forall(|x| forall(|y| forall(|z| mul(mul(x, y), z).eq(mul(x, mul(y, z))))));
1655
1656        // Describe the subgroup
1657        let h = Predicate::new("h", 1);
1658
1659        // Any subgroup contains the identity
1660        let h_ident = h.with(one);
1661
1662        // And is closed under multiplication
1663        let h_mul_closed = forall(|x| forall(|y| (h.with(x) & h.with(y)) >> h.with(mul(x, y))));
1664
1665        // And is closed under inverse
1666        let h_inv_closed = forall(|x| h.with(x) >> h.with(inv.with(x)));
1667
1668        // H specifically is of order 2
1669        let h_index_2 = exists(|x| {
1670            // an element not in H
1671            let not_in_h = !h.with(x);
1672            // but everything is in H or x H
1673            let class = forall(|y| h.with(y) | h.with(mul(inv.with(x), y)));
1674
1675            not_in_h & class
1676        });
1677
1678        // Conjecture: H is normal
1679        let h_normal = forall(|x| {
1680            let h_x = h.with(x);
1681            let conj_x = forall(|y| {
1682                let y_inv = inv.with(y);
1683                h.with(mul(mul(y, x), y_inv))
1684            });
1685            h_x.iff(conj_x)
1686        });
1687
1688        let solution = Problem::new(Options::new())
1689            .with_axiom(right_identity)
1690            .with_axiom(right_inverse)
1691            .with_axiom(associativity)
1692            .with_axiom(h_ident)
1693            .with_axiom(h_mul_closed)
1694            .with_axiom(h_inv_closed)
1695            .with_axiom(h_index_2)
1696            .conjecture(h_normal)
1697            .solve();
1698
1699        assert_eq!(solution, ProofRes::Proved);
1700    }
1701}