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