cop/
fof.rs

1//! First-order formulas and various normal forms.
2use crate::term::{Args, Arity, Fresh, Term};
3use crate::Lit;
4use alloc::{boxed::Box, vec::Vec};
5use core::fmt::{self, Display};
6use core::hash::Hash;
7use core::ops::Neg;
8use hashbrown::HashMap;
9
10/// Full first-order formula over atoms `A` and variables `V`.
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub enum Fof<A, V> {
13    /// atom
14    Atom(A),
15    /// negation
16    Neg(Box<Fof<A, V>>),
17    /// binary operation
18    Bin(Box<Fof<A, V>>, Op, Box<Fof<A, V>>),
19    /// associative binary operation
20    BinA(OpA, Vec<Fof<A, V>>),
21    /// quantification
22    Quant(Quantifier, V, Box<Fof<A, V>>),
23}
24
25/// Atoms occurring in a FOF, containing predicates `P`, constants `C`, and variables `V`.
26#[derive(Clone, Debug, PartialEq, Eq)]
27pub enum FofAtom<P, C, V> {
28    /// literal
29    Atom(Lit<P, C, V>),
30    /// equality between two terms
31    EqTm(Term<C, V>, Term<C, V>),
32}
33
34/// Negation-normal form of literals `L`, variables `V`, and quantifiers `V`.
35#[derive(Clone, Debug, PartialEq, Eq)]
36pub enum Nnf<L, V, Q> {
37    /// literal
38    Lit(L),
39    /// associative binary operation
40    BinA(OpA, Vec<Nnf<L, V, Q>>),
41    /// quantification
42    Quant(Q, V, Box<Nnf<L, V, Q>>),
43}
44
45/// Conjunctive normal form of literals `L`, preserving parentheses.
46#[derive(Clone, Debug, PartialEq, Eq)]
47pub enum Cnf<L> {
48    /// conjunction
49    Conj(Vec<Cnf<L>>),
50    /// disjunction
51    Disj(Dnf<L>),
52}
53
54/// Disjunction of literals that preserves parentheses.
55#[derive(Clone, Debug, PartialEq, Eq)]
56pub enum Dnf<L> {
57    /// literal
58    Lit(L),
59    /// disjunction
60    Disj(Vec<Dnf<L>>),
61}
62
63/// Binary operation.
64#[derive(Copy, Clone, Debug, PartialEq, Eq)]
65pub enum Op {
66    /// implication
67    Impl,
68    /// equality between formulas
69    EqFm,
70}
71
72/// Associative binary operation.
73#[derive(Copy, Clone, Debug, PartialEq, Eq)]
74pub enum OpA {
75    /// conjunction
76    Conj,
77    /// disjunction
78    Disj,
79}
80
81/// Quantifier.
82#[derive(Copy, Clone, Debug, PartialEq, Eq)]
83pub enum Quantifier {
84    /// universal
85    Forall,
86    /// existential
87    Exists,
88}
89
90/// Universal quantifier.
91///
92/// This serves e.g. to indicate in [`Nnf`] that it only contains universal quantifiers.
93#[derive(Copy, Clone, Debug, PartialEq, Eq)]
94pub struct Forall;
95
96impl Neg for OpA {
97    type Output = Self;
98
99    fn neg(self) -> Self {
100        match self {
101            Self::Conj => Self::Disj,
102            Self::Disj => Self::Conj,
103        }
104    }
105}
106
107impl Neg for Quantifier {
108    type Output = Self;
109
110    fn neg(self) -> Self {
111        match self {
112            Self::Forall => Self::Exists,
113            Self::Exists => Self::Forall,
114        }
115    }
116}
117
118impl<Atom: Display, V: Display> Display for Fof<Atom, V> {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        use Fof::*;
121        match self {
122            Atom(a) => a.fmt(f),
123            Neg(fm) => write!(f, "¬ {}", fm),
124            Bin(l, o, r) => write!(f, "({} {} {})", l, o, r),
125            BinA(o, fms) => o.fmt_args(fms, f),
126            Quant(q, v, fm) => write!(f, "{} {}. {}", q, v, fm),
127        }
128    }
129}
130
131impl<L: Display, V: Display, Q: Display> Display for Nnf<L, V, Q> {
132    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133        use Nnf::*;
134        match self {
135            Lit(lit) => lit.fmt(f),
136            BinA(o, fms) => o.fmt_args(fms, f),
137            Quant(q, v, fm) => write!(f, "{} {}. {}", q, v, fm),
138        }
139    }
140}
141
142impl<L: Display> Display for Cnf<L> {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        use Cnf::*;
145        match self {
146            Conj(fms) => OpA::Conj.fmt_args(fms, f),
147            Disj(disj) => disj.fmt(f),
148        }
149    }
150}
151
152impl<L: Display> Display for Dnf<L> {
153    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154        use Dnf::*;
155        match self {
156            Lit(lit) => lit.fmt(f),
157            Disj(fms) => OpA::Disj.fmt_args(fms, f),
158        }
159    }
160}
161
162impl<P: Display, C: Display, V: Display> Display for FofAtom<P, C, V> {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        match self {
165            Self::Atom(a) => a.fmt(f),
166            Self::EqTm(l, r) => write!(f, "{} = {}", l, r),
167        }
168    }
169}
170
171impl Display for Op {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        match self {
174            Op::Impl => write!(f, "⇒"),
175            Op::EqFm => write!(f, "⇔"),
176        }
177    }
178}
179
180impl Display for OpA {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        match self {
183            OpA::Conj => write!(f, "∧"),
184            OpA::Disj => write!(f, "∨"),
185        }
186    }
187}
188
189impl Display for Quantifier {
190    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191        match self {
192            Quantifier::Forall => write!(f, "∀"),
193            Quantifier::Exists => write!(f, "∃"),
194        }
195    }
196}
197
198impl Display for Forall {
199    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200        Quantifier::Forall.fmt(f)
201    }
202}
203
204impl<A, V> Neg for Fof<A, V> {
205    type Output = Self;
206    fn neg(self) -> Self {
207        Self::Neg(Box::new(self))
208    }
209}
210
211impl<L: Neg<Output = L>, V, Q: Neg<Output = Q>> Neg for Nnf<L, V, Q> {
212    type Output = Self;
213    fn neg(self) -> Self {
214        match self {
215            Self::Lit(l) => Self::Lit(-l),
216            Self::BinA(op, fms) => Self::BinA(-op, fms.into_iter().map(|fm| -fm).collect()),
217            Self::Quant(q, v, fm) => Self::Quant(-q, v, Box::new(-*fm)),
218        }
219    }
220}
221
222impl<A, V> core::ops::BitAnd for Fof<A, V> {
223    type Output = Self;
224    fn bitand(self, rhs: Self) -> Self {
225        Self::bina(self, OpA::Conj, rhs)
226    }
227}
228
229impl<A, V> core::ops::BitOr for Fof<A, V> {
230    type Output = Self;
231    fn bitor(self, rhs: Self) -> Self {
232        Self::bina(self, OpA::Disj, rhs)
233    }
234}
235
236impl<L, V, Q> core::ops::BitAnd for Nnf<L, V, Q> {
237    type Output = Self;
238    fn bitand(self, rhs: Self) -> Self {
239        Self::bina(self, OpA::Conj, rhs)
240    }
241}
242
243impl<L, V, Q> core::ops::BitOr for Nnf<L, V, Q> {
244    type Output = Self;
245    fn bitor(self, rhs: Self) -> Self {
246        Self::bina(self, OpA::Disj, rhs)
247    }
248}
249
250impl<L> core::ops::BitAnd for Cnf<L> {
251    type Output = Self;
252    fn bitand(self, rhs: Self) -> Self {
253        match rhs {
254            Self::Conj(fms) => join(self, fms, Self::Conj),
255            _ => Self::Conj(Vec::from([self, rhs])),
256        }
257    }
258}
259
260impl<L: Clone> core::ops::BitOr for Cnf<L> {
261    type Output = Self;
262    fn bitor(self, rhs: Self) -> Self {
263        match (self, rhs) {
264            (Self::Conj(lc), r) => Self::conjs(lc.into_iter().map(|ln| ln | r.clone())),
265            (l, Self::Conj(rc)) => Self::conjs(rc.into_iter().map(|rn| l.clone() | rn)),
266            (Self::Disj(ld), Self::Disj(rd)) => Self::Disj(ld | rd),
267        }
268    }
269}
270
271impl<L> core::ops::BitOr for Dnf<L> {
272    type Output = Self;
273    fn bitor(self, rhs: Self) -> Self {
274        match rhs {
275            Self::Disj(fms) => join(self, fms, Self::Disj),
276            _ => Self::Disj(Vec::from([self, rhs])),
277        }
278    }
279}
280
281impl<A, V> Fof<A, V> {
282    /// Apply a binary operation to two formulas.
283    pub fn bin(l: Self, o: Op, r: Self) -> Self {
284        Self::Bin(Box::new(l), o, Box::new(r))
285    }
286
287    /// Apply an associative binary operation to two formulas.
288    ///
289    /// If `r` is itself an application of `o`, then `l` is simply added to `r`.
290    /// For example, if `o` is conjunction and `r` is `a & b`,
291    /// then the result is `l & a & b`, not `l & (a & b)`.
292    pub fn bina(l: Self, o: OpA, r: Self) -> Self {
293        match r {
294            Self::BinA(op, fms) if o == op => join(l, fms, |fms| Self::BinA(o, fms)),
295            _ => Self::BinA(o, Vec::from([l, r])),
296        }
297    }
298
299    /// For formulas f1, .., fn, return f1 o (... o fn).
300    pub fn binas(o: OpA, fms: impl DoubleEndedIterator<Item = Self>) -> Self {
301        fms.rev()
302            .reduce(|acc, x| Self::bina(x, o, acc))
303            .unwrap_or_else(|| Self::BinA(o, Vec::new()))
304    }
305
306    /// Create the implication from `l` to `r`.
307    pub fn imp(l: Self, r: Self) -> Self {
308        Self::bin(l, Op::Impl, r)
309    }
310
311    /// Create a quantification.
312    pub fn quant(q: Quantifier, v: V, fm: Self) -> Self {
313        Self::Quant(q, v, Box::new(fm))
314    }
315
316    /// Create a universal quantification.
317    pub fn forall(v: V, fm: Self) -> Self {
318        Self::quant(Quantifier::Forall, v, fm)
319    }
320
321    /// Universally quantify over a sequence of variables.
322    pub fn foralls(vs: impl Iterator<Item = V>, fm: Self) -> Self {
323        vs.fold(fm, |fm, v| Self::forall(v, fm))
324    }
325
326    /// If `self` is of shape `a => b`, then return `premise & a => b`, else `premise => self`.
327    pub fn add_premise(self, premise: Self) -> Self {
328        match self {
329            Self::Bin(a, Op::Impl, b) => Self::imp(premise & *a, *b),
330            _ => Self::imp(premise, self),
331        }
332    }
333
334    /// If `self` is of shape `a => c`, then return `a & fm() => fm() & c`, else `self`.
335    ///
336    /// Also return whether `self` is an implication, i.e.
337    /// whether the output formula does not equal `self`.
338    pub fn mark_impl(self, fm: impl Fn() -> Self) -> (bool, Self) {
339        match self {
340            Self::Bin(a, Op::Impl, c) => (true, Self::imp(*a & fm(), fm() & *c)),
341            _ => (false, self),
342        }
343    }
344
345    /// Apply a function to all atoms.
346    pub fn map_atoms<B>(self, f: &mut impl FnMut(A) -> B) -> Fof<B, V> {
347        use Fof::*;
348        match self {
349            Atom(a) => Atom(f(a)),
350            Neg(fm) => -fm.map_atoms(f),
351            Bin(l, op, r) => Fof::bin(l.map_atoms(f), op, r.map_atoms(f)),
352            BinA(o, fms) => BinA(o, fms.into_iter().map(|fm| fm.map_atoms(f)).collect()),
353            Quant(q, v, fm) => Fof::Quant(q, v, Box::new(fm.map_atoms(f))),
354        }
355    }
356
357    /// Apply a function to all variables.
358    pub fn map_vars<W>(self, f: &mut impl FnMut(V) -> W) -> Fof<A, W> {
359        use Fof::*;
360        match self {
361            Atom(a) => Atom(a),
362            Neg(fm) => -fm.map_vars(f),
363            Bin(l, o, r) => Fof::bin(l.map_vars(f), o, r.map_vars(f)),
364            BinA(o, fms) => BinA(o, fms.into_iter().map(|fm| fm.map_vars(f)).collect()),
365            Quant(q, v, fm) => Quant(q, f(v), Box::new(fm.map_vars(f))),
366        }
367    }
368
369    /// Return all atoms occurring in the formula.
370    pub fn atoms(&self) -> Box<dyn Iterator<Item = &A> + '_> {
371        use Fof::*;
372        match self {
373            Atom(a) => Box::new(core::iter::once(a)),
374            Neg(fm) | Quant(_, _, fm) => fm.atoms(),
375            Bin(l, _, r) => Box::new(l.atoms().chain(r.atoms())),
376            BinA(_, fms) => Box::new(fms.iter().flat_map(|fm| fm.atoms())),
377        }
378    }
379}
380
381impl<P, C, V> Fof<FofAtom<P, C, V>, V> {
382    /// Construct an atom.
383    pub fn atom(p: P, args: Args<C, V>) -> Self {
384        Self::Atom(FofAtom::Atom(Lit::new(p, args)))
385    }
386
387    /// Construct an equality between two terms.
388    pub fn eqtm(l: Term<C, V>, r: Term<C, V>) -> Self {
389        Self::Atom(FofAtom::EqTm(l, r))
390    }
391}
392
393impl<A: Clone + Neg<Output = A>, V: Clone> Fof<A, V> {
394    /// Convert to NNF, replacing non-associative binary operations via function.
395    pub fn qnnf(self, f: &impl Fn(bool, Self, Op, Self) -> Self) -> Nnf<A, V, Quantifier> {
396        let qnnf = |fm: Self| fm.qnnf(f);
397        use Fof::*;
398        match self {
399            Atom(a) => Nnf::Lit(a),
400            BinA(op, fms) => Nnf::BinA(op, fms.into_iter().map(qnnf).collect()),
401            Quant(q, v, t) => Nnf::Quant(q, v, Box::new(qnnf(*t))),
402            Bin(l, op, r) => qnnf(f(true, *l, op, *r)),
403            Neg(fm) => match *fm {
404                Neg(t) => qnnf(*t),
405                Atom(a) => Nnf::Lit(-a),
406                BinA(op, fms) => Nnf::BinA(-op, fms.into_iter().map(|fm| qnnf(-fm)).collect()),
407                Quant(q, v, t) => Nnf::Quant(-q, v, Box::new(qnnf(-*t))),
408                Bin(l, op, r) => qnnf(f(false, *l, op, *r)),
409            },
410        }
411    }
412
413    /// Unfold logical equivalence with a disjunction of conjunctions.
414    ///
415    /// Used in (nondefinitional) leanCoP.
416    pub fn unfold_eqfm_disj_conj(pol: bool, l: Self, op: Op, r: Self) -> Self {
417        match (pol, op) {
418            (true, Op::Impl) => -l | r,
419            (false, Op::Impl) => l & -r,
420            // leanCoP-specific
421            (true, Op::EqFm) => (l.clone() & r.clone()) | (-l & -r),
422            (false, Op::EqFm) => (l.clone() & -r.clone()) | (-l & r),
423        }
424    }
425
426    /// Unfold logical equivalence with a conjunction of implications.
427    ///
428    /// Used in nanoCoP.
429    pub fn unfold_eqfm_conj_impl(pol: bool, l: Self, op: Op, r: Self) -> Self {
430        match (pol, op) {
431            (true, Op::Impl) => -l | r,
432            (false, Op::Impl) => l & -r,
433            // nanoCoP-specific
434            (true, Op::EqFm) => Self::imp(l.clone(), r.clone()) & Self::imp(r, l),
435            (false, Op::EqFm) => -(Self::imp(l.clone(), r.clone()) & Self::imp(r, l)),
436        }
437    }
438}
439
440impl<P, C, V> FofAtom<P, C, V> {
441    /// Apply a function to all variables.
442    pub fn map_vars<W>(self, f: &mut impl Fn(V) -> W) -> FofAtom<P, C, W> {
443        use FofAtom::*;
444        let mut mv = |v| Term::V(f(v));
445        match self {
446            Atom(lit) => Atom(lit.map_args(|tms| tms.map_vars(&mut mv))),
447            EqTm(l, r) => EqTm(l.map_vars(&mut mv), r.map_vars(&mut mv)),
448        }
449    }
450
451    /// Return a sequence of all variables contained in the atom.
452    pub fn vars(&self) -> Box<dyn Iterator<Item = &V> + '_> {
453        use FofAtom::*;
454        match self {
455            Atom(lit) => Box::new(lit.args().iter().flat_map(|tms| tms.vars())),
456            EqTm(l, r) => Box::new(l.vars().chain(r.vars())),
457        }
458    }
459
460    /// Convert to a literal, mapping a term equality `l = r` to
461    /// an application `e(l, r)`, where `e` is the output of `eq()`.
462    pub fn to_lit(self, eq: impl Fn() -> P) -> Lit<P, C, V> {
463        match self {
464            Self::Atom(lit) => lit,
465            Self::EqTm(l, r) => Lit::new(eq(), Args::from([l, r])),
466        }
467    }
468}
469
470impl<L, V, Q> Nnf<L, V, Q> {
471    /// Sort the formula by ascending number of paths.
472    #[cfg(feature = "order")]
473    pub fn order(self) -> (Self, num_bigint::BigUint) {
474        use num_traits::{One, Zero};
475        use Nnf::*;
476        match self {
477            BinA(op, fms) => {
478                let neutral = match op {
479                    OpA::Conj => One::one(),
480                    OpA::Disj => Zero::zero(),
481                };
482                let comb = match op {
483                    OpA::Conj => |l, r| l * r,
484                    OpA::Disj => |l, r| l + r,
485                };
486                let fms = fms.into_iter().rev().map(|fm| fm.order());
487                fms.reduce(|acc, x| {
488                    let (l, r) = if x.1 > acc.1 {
489                        (acc.0, x.0)
490                    } else {
491                        (x.0, acc.0)
492                    };
493                    (Self::bina(l, op, r), comb(acc.1, x.1))
494                })
495                .unwrap_or_else(|| (BinA(op, Vec::new()), neutral))
496            }
497            Quant(q, v, fm) => {
498                let (fm, size) = fm.order();
499                (Quant(q, v, Box::new(fm)), size)
500            }
501            Lit(_) => (self, One::one()),
502        }
503    }
504}
505
506impl<L, V, Q> Nnf<L, V, Q> {
507    // TODO: used in `order`, really necessary?
508    /// Apply an associative binary operation to two formulas.
509    ///
510    /// Behaves similarly as [`Fof::bina`].
511    pub fn bina(l: Self, o: OpA, r: Self) -> Self {
512        match r {
513            Self::BinA(op, fms) if o == op => join(l, fms, |fms| Self::BinA(o, fms)),
514            _ => Self::BinA(o, Vec::from([l, r])),
515        }
516    }
517
518    // TODO: used in `cnf`, really necessary?
519    /// For formulas f1, .., fn, return f1 o (... o fn).
520    pub fn binas(o: OpA, fms: impl DoubleEndedIterator<Item = Self>) -> Self {
521        fms.rev()
522            .reduce(|acc, x| Self::bina(x, o, acc))
523            .unwrap_or_else(|| Self::BinA(o, Vec::new()))
524    }
525
526    /// Apply a function to the literals.
527    pub fn map_literals<M>(self, f: &mut impl FnMut(L) -> M) -> Nnf<M, V, Q> {
528        match self {
529            Self::Lit(l) => Nnf::Lit(f(l)),
530            Self::BinA(op, fms) => {
531                Nnf::BinA(op, fms.into_iter().map(|fm| fm.map_literals(f)).collect())
532            }
533            Self::Quant(q, v, fm) => Nnf::Quant(q, v, Box::new(fm.map_literals(f))),
534        }
535    }
536}
537
538impl<L: Clone, V: Clone> Nnf<L, V, Forall> {
539    /// CNF of the disjunction of two formulas.
540    pub fn cnf_disj(self, other: Self) -> Cnf<L> {
541        use Nnf::{BinA, Quant};
542        match (self, other) {
543            (Quant(Forall, _, l), r) => l.cnf_disj(r),
544            (l, Quant(Forall, _, r)) => l.cnf_disj(*r),
545            (BinA(OpA::Conj, lc), r) => Cnf::conjs(lc.into_iter().map(|ln| ln.cnf_disj(r.clone()))),
546            (l, BinA(OpA::Conj, rc)) => Cnf::conjs(rc.into_iter().map(|rn| l.clone().cnf_disj(rn))),
547            (l, r) => l.cnf() | r.cnf(),
548        }
549    }
550
551    /// CNF of an NNF with only universal quantifiers.
552    pub fn cnf(self) -> Cnf<L> {
553        use Nnf::*;
554        match self {
555            Quant(Forall, _, fm) => fm.cnf(),
556            BinA(OpA::Conj, fms) => Cnf::conjs(fms.into_iter().map(|fm| fm.cnf())),
557            BinA(OpA::Disj, fms) => {
558                let mut fms = fms.into_iter();
559                match fms.next() {
560                    None => Cnf::Disj(Dnf::Disj(Vec::new())),
561                    Some(fm1) => fm1.cnf_disj(Self::binas(OpA::Disj, fms)),
562                }
563            }
564            Lit(l) => Cnf::Disj(Dnf::Lit(l)),
565        }
566    }
567}
568
569type Arities<T> = Vec<(T, Arity)>;
570
571impl<P: Eq, C: Eq, V> Fof<FofAtom<P, C, V>, V> {
572    /// Corresponds to leanCoP's `collect_predfunc`.
573    pub fn predconst_unique(&self) -> (Arities<&P>, Arities<&C>) {
574        use Fof::*;
575        match self {
576            Atom(FofAtom::Atom(a)) => (
577                Vec::from([(a.head(), a.args().len())]),
578                a.args().const_unique(),
579            ),
580            Atom(FofAtom::EqTm(l, r)) => {
581                let mut cl = l.const_unique();
582                let cr = r.const_unique();
583                crate::union1(&mut cl, cr);
584                (Vec::new(), cl)
585            }
586            Bin(l, _, r) => {
587                let (mut pl, mut cl) = l.predconst_unique();
588                let (pr, cr) = r.predconst_unique();
589                crate::union1(&mut pl, pr);
590                crate::union1(&mut cl, cr);
591                (pl, cl)
592            }
593            BinA(_, fms) => fms
594                .iter()
595                .rev()
596                .fold((Vec::new(), Vec::new()), |(pr, cr), x| {
597                    let (mut pl, mut cl) = x.predconst_unique();
598                    crate::union1(&mut pl, pr);
599                    crate::union1(&mut cl, cr);
600                    (pl, cl)
601                }),
602            Neg(fm) | Quant(_, _, fm) => fm.predconst_unique(),
603        }
604    }
605}
606
607impl<P, C, V: Clone + Eq + Hash, Q> Nnf<Lit<P, C, V>, V, Q> {
608    /// Replace all variables (bound and unbound) by fresh ones.
609    pub fn fresh_vars<W: Clone + Fresh>(
610        self,
611        map: &mut HashMap<V, W>,
612        st: &mut W::State,
613    ) -> Nnf<Lit<P, C, W>, W, Q> {
614        use Nnf::*;
615        match self {
616            Lit(lit) => Lit(lit.fresh_vars(map, st)),
617            BinA(o, fms) => BinA(
618                o,
619                fms.into_iter().map(|fm| fm.fresh_vars(map, st)).collect(),
620            ),
621            Quant(q, v, fm) => {
622                let i = W::fresh(st);
623                let old = map.insert(v.clone(), i.clone());
624                let fm = fm.fresh_vars(map, st);
625                match old {
626                    Some(old) => map.insert(v, old),
627                    None => map.remove(&v),
628                };
629                Quant(q, i, Box::new(fm))
630            }
631        }
632    }
633}
634
635/// The state of Skolemisation at a given position in a formula.
636pub struct SkolemState<C: Fresh, V> {
637    /// universally bound variables at the current position
638    universal: Vec<V>,
639    /// mapping of existentially quantified variables to their corresponding Skolem terms
640    existential: HashMap<V, Term<C, V>>,
641    /// a generator for fresh Skolem symbols
642    fresh: C::State,
643}
644
645impl<C: Fresh, V> SkolemState<C, V> {
646    /// Initialise a Skolemisation state.
647    pub fn new(fresh: C::State) -> Self {
648        Self {
649            universal: Vec::new(),
650            existential: HashMap::new(),
651            fresh,
652        }
653    }
654}
655
656impl<P, C: Clone + Fresh, V: Clone + Eq + Hash> Nnf<Lit<P, C, V>, V, Quantifier> {
657    /// Outer Skolemisation, eliminating existential quantifiers.
658    ///
659    /// Unlike inner Skolemisation, this does not determine the arguments of
660    /// Skolem terms by looking at which universally bound variables are
661    /// actually used inside the subterm of existential quantification,
662    /// but by taking all currently bound universal variables outside.
663    pub fn skolem_outer(self, st: &mut SkolemState<C, V>) -> Nnf<Lit<P, C, V>, V, Forall> {
664        use Nnf::*;
665        match self {
666            Lit(lit) => Lit(lit.map_args(|tms| tms.subst(&st.existential))),
667            BinA(o, fms) => BinA(o, fms.into_iter().map(|fm| fm.skolem_outer(st)).collect()),
668            Quant(Quantifier::Forall, v, fm) => {
669                st.universal.push(v);
670                let fm = fm.skolem_outer(st);
671                let v = st.universal.pop().unwrap();
672                Quant(Forall, v, Box::new(fm))
673            }
674            Quant(Quantifier::Exists, v, fm) => {
675                let skolem = Term::skolem(&mut st.fresh, st.universal.clone());
676                assert!(!st.existential.contains_key(&v));
677                st.existential.insert(v.clone(), skolem);
678                let fm = fm.skolem_outer(st);
679                st.existential.remove(&v);
680                fm
681            }
682        }
683    }
684}
685
686impl<L> Cnf<L> {
687    /// Create a conjunction of CNFs.
688    pub fn conjs(fms: impl DoubleEndedIterator<Item = Self>) -> Self {
689        fms.rev()
690            .reduce(|acc, x| x & acc)
691            .unwrap_or_else(|| Self::Conj(Vec::new()))
692    }
693}
694
695impl OpA {
696    fn fmt_args<T: Display>(self, fms: &[T], f: &mut fmt::Formatter<'_>) -> fmt::Result {
697        let mut fms = fms.iter();
698        match (self, fms.next()) {
699            (OpA::Conj, None) => write!(f, "⊤"),
700            (OpA::Disj, None) => write!(f, "⊥"),
701            (o, Some(fm1)) => {
702                write!(f, "({}", fm1)?;
703                fms.try_for_each(|fm| write!(f, " {} {}", o, fm))?;
704                write!(f, ")")
705            }
706        }
707    }
708}
709
710/// Given x and y1 o ... o yn, return x if n = 0, else x o y1 o ... o yn.
711pub fn join<T>(x: T, mut ys: Vec<T>, f: impl Fn(Vec<T>) -> T) -> T {
712    if ys.is_empty() {
713        x
714    } else {
715        ys.insert(0, x);
716        f(ys)
717    }
718}