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