smt_str/re/
mod.rs

1//! Core module for SMT-LIB regular expressions.
2//!
3//! This module defines the core data structures and algorithms for working with regular expressions
4//! in the SMT-LIB theory of strings. Regular expressions are represented as abstract syntax trees (ASTs),
5//! where each node represents a regular operation or base case.
6//!
7//!  The module provides the following types:
8//!
9//! - [`Regex`] — a reference-counted pointer to a regular expression node ([`ReNode`]).
10//! - [`ReNode`] — a node in a regular expression AST.
11//! - [`ReOp`] — the enum describing the different operations in a regular expression (e.g., `re.*`, `re.union`, `re.diff`).
12//!
13//! Each [`ReNode`] contains a [`ReOp`], which may be either a base case (e.g., a literal or character range),
14//! or a regular operation applied to one or more subexpressions.
15//! In the latter case, subexpressions are stored as reference-counted pointer ([`Regex`]), allowing for structural sharing of common subtrees.
16//! This means that if a regular expression contains the same subexpression multiple times, the corresponding [ReOp]s will all point to the same [ReNode] instance.
17//!
18//! For example, if the regex is
19//!
20//! ```text
21//! re.union(re.*(str.to_re("a")), re.comp(str.to_re("a")))`
22//! ```
23//!
24//! then the `re.*` and `re.comp` operations will point to the same exact same [ReNode] instance for the subexpression `str.to_re("a")`.
25//!
26//! Structural sharing enables:
27//!
28//! - Reduced memory usage
29//! - Efficient equality and hashing (by node ID)
30//! - Cheap cloning of regular expressions
31//! - Caching of derived properties (e.g., nullable, alphabet, etc.)
32//!
33//! Because of this, regular expressions must be created using the [`ReBuilder`] and cannot be constructed directly.
34//!
35//! # Example
36//!
37//! ```
38//! use smt_str::re::*;
39//! use std::rc::Rc;
40//!
41//! // Create a regular expression builder
42//! let mut builder = ReBuilder::default();
43//!
44//! // Construct the regular expression `a``
45//! let re_a = builder.to_re("a".into());
46//!
47//! // Construct the regular expression `a*` and `comp(a)`
48//! let re_star = builder.star(re_a.clone());
49//! let re_comp = builder.comp(re_a.clone());
50//!
51//! // The operand of `re_star` and `re_comp` are not only structurally equal
52//! assert_eq!(re_star[0], re_comp[0]);
53//! // but also share the same node in memory
54//! assert!(Rc::ptr_eq(&re_star[0], &re_comp[0]));
55//! ```
56
57mod build;
58pub mod deriv;
59
60use quickcheck::{Arbitrary, Gen};
61use smallvec::{smallvec, SmallVec};
62
63use itertools::Itertools;
64
65use std::cell::RefCell;
66
67use std::hash::Hash;
68use std::ops::Index;
69use std::{fmt::Display, rc::Rc};
70
71pub use build::ReBuilder;
72
73use crate::alphabet::{partition::AlphabetPartition, Alphabet, CharRange};
74use crate::SmtString;
75
76pub type ReId = usize;
77
78type LazyProp<T> = RefCell<Option<T>>;
79
80/// A shared pointer to a regular expression node (`ReNode`).
81///
82/// This alias is used throughout the crate to represent a node in a regular expression AST.
83/// For details on construction and structure sharing see the module documentation.
84pub type Regex = Rc<ReNode>;
85
86/// A node in the abstract syntax tree (AST) of a regular expression.
87///
88/// Each `ReNode` stores a single operation ([`ReOp`]) and a set of lazily computed
89/// properties, such as:
90///
91/// - Whether the expression is nullable (i.e., accepts the empty word)
92/// - Whether it is universal (i.e., accepts all words)
93/// - Whether it denotes the empty set or the empty word
94/// - Whether it has a constant prefix, suffix, or word
95/// - The first-character partition and alphabet
96/// - Whether it contains a complement or difference operation
97///
98/// All these properties are cached and evaluated on demand to improve performance.
99///
100/// Every `ReNode` has a unique identifier assigned by [`ReBuilder`].
101/// This is unique across all regular expressions created by the same builder, which enables
102/// fast (i.e., **O(1)**) equality checking and hashing.
103/// Instances of this type are also superficially order by their ID.
104///
105///**Note**: `ReNode` is not meant to be used directly.
106/// Use the [`Regex`](crate::re::Regex) alias, which wraps it in a reference-counted pointer.
107///
108/// # Cheap Cloning
109///
110/// The subexpressions of a regular expression are stored as reference-counted pointers ([`Regex`]) in the [`ReOp`] variant that defines the operation.
111/// All derived properties are lazily computed and cached using interior mutability.
112/// As a result, cloning a regular expression is inexpensive: it only increments the reference count to shared subexpressions and cached data.
113/// This makes it efficient to clone and pass around regular expressions, even when they are large.
114///
115/// # Caching and Interior Mutability
116///
117/// This structure uses interior mutability (`RefCell`) to cache the derived properties.
118/// These caches do **not** affect the `Hash`, `Ord`, or `Eq` implementations, which
119/// rely solely on the unique `id`.
120///
121/// Therefore, it is safe to use `ReNode` (via [`Regex`]) as a key in hash maps or sets,
122/// even though Clippy may issue warnings about interior mutability.
123/// The warning `clippy::mutable_key_type` can be safely suppressed for this type using `#[allow(clippy::mutable_key_type)]`
124#[derive(Debug, Clone)]
125pub struct ReNode {
126    /// Unique identifier for the regular expression.
127    id: ReId,
128    /// The operation defining the regex structure.
129    op: ReOp,
130
131    /// Whether the regex is simple.
132    simple: LazyProp<bool>,
133
134    /// Whether the regular expression can accept the empty word (ε).
135    nullable: LazyProp<bool>,
136    /// Whether the regular expression accepts all possible strings.
137    universal: LazyProp<Option<bool>>,
138    /// Whether the regular expression is the empty set (∅), meaning it accepts no words.
139    none: LazyProp<Option<bool>>,
140    /// Whether the regular expression exclusively denotes the empty word (ε).
141    epsi: LazyProp<Option<bool>>,
142    /// The set of characters that can be first in a word accepted by the regular expression.
143    /// Partitioned according to left quotients.
144    first: LazyProp<Rc<AlphabetPartition>>,
145
146    /// The alphabet of the regular expression.
147    alphabet: LazyProp<Rc<Alphabet>>,
148    /// The only word accepted by the regular expression, if it is a constant word.
149    is_constant: LazyProp<Option<SmtString>>,
150
151    /// The prefix of all words accepted by the regular expression.
152    /// Not necessarily the longest prefix.
153    prefix: LazyProp<Option<SmtString>>,
154
155    /// The suffix of all words accepted by the regular expression.
156    /// Not necessarily the longest suffix.
157    suffix: LazyProp<Option<SmtString>>,
158
159    /// Whether the regular expression contains a complement operation.
160    /// Also true if the expression contains difference, which is equivalent to complement.
161    contains_complement: LazyProp<bool>,
162}
163
164impl PartialEq for ReNode {
165    fn eq(&self, other: &Self) -> bool {
166        self.id == other.id
167    }
168}
169impl Eq for ReNode {}
170impl Hash for ReNode {
171    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
172        self.id.hash(state)
173    }
174}
175impl PartialOrd for ReNode {
176    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
177        Some(self.cmp(other))
178    }
179}
180impl Ord for ReNode {
181    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
182        self.id.cmp(&other.id)
183    }
184}
185
186impl Index<usize> for ReNode {
187    type Output = Regex;
188
189    /// Returns the subexpression at the given index.
190    /// If the index is out of bounds, this function panics.
191    /// This function always panics on regular expressions that have no subexpressions, i.e., literals, ranges, none, any, and all.
192    fn index(&self, index: usize) -> &Self::Output {
193        match self.op() {
194            ReOp::Concat(v) | ReOp::Union(v) | ReOp::Inter(v) => &v[index],
195            ReOp::Star(r)
196            | ReOp::Plus(r)
197            | ReOp::Opt(r)
198            | ReOp::Comp(r)
199            | ReOp::Pow(r, _)
200            | ReOp::Loop(r, _, _)
201                if index == 0 =>
202            {
203                r
204            }
205            ReOp::Diff(r, _) if index == 0 => r,
206            ReOp::Diff(_, r) if index == 1 => r,
207            _ => panic!("Index out of bounds"),
208        }
209    }
210}
211
212impl ReNode {
213    /// Creates a new regular expression node with the given ID and operation.
214    /// This function should only be used by the `ReBuilder`.
215    fn new(id: ReId, op: ReOp) -> Self {
216        Self {
217            id,
218            op,
219            nullable: RefCell::new(None),
220            universal: RefCell::new(None),
221            simple: RefCell::new(None),
222            none: RefCell::new(None),
223            epsi: RefCell::new(None),
224            first: RefCell::new(None),
225            alphabet: RefCell::new(None),
226            is_constant: RefCell::new(None),
227            prefix: RefCell::new(None),
228            suffix: RefCell::new(None),
229            contains_complement: RefCell::new(None),
230        }
231    }
232
233    /// Returns the unique identifier of the regular expression.
234    /// The ID assigned by the `ReBuilder` and unique across all regular expressions created by the same builder.
235    pub fn id(&self) -> ReId {
236        self.id
237    }
238
239    /// Returns the operation defining the regular expression.
240    ///
241    /// # Example
242    /// ```
243    /// use smt_str::re::*;
244    ///
245    /// let mut builder = ReBuilder::default();
246    /// let re = builder.to_re("a".into());
247    /// assert_eq!(re.op(), &ReOp::Literal("a".into()));
248    ///
249    /// let re_star = builder.star(re.clone());
250    /// assert_eq!(re_star.op(), &ReOp::Star(re));
251    /// ```
252    pub fn op(&self) -> &ReOp {
253        &self.op
254    }
255
256    /// Returns whether the regular expression is nullable.
257    /// A regular expression is nullable if it accepts the empty word.
258    ///
259    /// # Example
260    /// ```
261    /// use smt_str::re::*;
262    /// let mut builder = ReBuilder::default();
263    /// let re = builder.to_re("a".into());
264    /// let re_star = builder.star(re.clone());
265    /// assert!(!re.nullable());
266    /// assert!(re_star.nullable());
267    /// ```
268    pub fn nullable(&self) -> bool {
269        *self
270            .nullable
271            .borrow_mut()
272            .get_or_insert_with(|| self.op.nullable())
273    }
274
275    /// Returns whether the regular expression is simple.
276    /// A regular expression is simple if it does not contain complement, difference, or intersection.
277    ///
278    /// # Example
279    /// ```
280    /// use smt_str::re::*;
281    /// use smallvec::smallvec;
282    /// let mut builder = ReBuilder::default();
283    /// let re_a = builder.to_re("a".into());
284    /// let re_star = builder.star(re_a.clone());
285    /// assert!(re_a.simple());
286    /// assert!(re_star.simple());
287    ///
288    /// // Complement, difference, and intersection are not simple.
289    ///
290    /// let re_comp = builder.comp(re_a.clone());
291    /// let re_diff = builder.diff(re_star.clone(), re_a.clone());
292    /// let re_inter = builder.inter(smallvec![re_star.clone(), re_a.clone()]);
293    /// assert!(!re_comp.simple());
294    /// assert!(!re_diff.simple());
295    /// assert!(!re_inter.simple());
296    /// ```
297    pub fn simple(&self) -> bool {
298        *self
299            .simple
300            .borrow_mut()
301            .get_or_insert_with(|| self.op.simple())
302    }
303
304    /// Returns whether the regular expression is universal.
305    /// A regular expression is universal if it accepts every word.
306    /// This is determined heuristically based on the structure of the regular expression.
307    /// As such, this operation may return `None` if the universality cannot be determined.
308    ///
309    /// # Example
310    /// ```
311    /// use smt_str::re::*;
312    /// use smallvec::smallvec;
313    /// let mut builder = ReBuilder::default();
314    ///
315    /// assert_eq!(builder.all().universal(), Some(true));
316    /// assert_eq!(builder.none().universal(), Some(false));
317    ///
318    /// // Universally can not always be determined.
319    /// let re_a = builder.to_re("a".into());
320    /// let diff = builder.diff(builder.all(), re_a.clone());
321    /// let union = builder.union(smallvec![diff, re_a.clone()]);
322    ///
323    /// assert_eq!(union.universal(), None);
324    /// ```
325    pub fn universal(&self) -> Option<bool> {
326        *self
327            .universal
328            .borrow_mut()
329            .get_or_insert_with(|| self.op.universal())
330    }
331
332    /// Returns whether the regular expression is the empty set.
333    /// A regular expression is the empty set if it does not accept any word.
334    /// This operation may return `None` if the emptiness cannot be determined.
335    ///
336    /// # Example
337    /// ```
338    /// use smt_str::re::*;
339    /// use smallvec::smallvec;
340    /// let mut builder = ReBuilder::default();
341    /// assert_eq!(builder.none().none(), Some(true));
342    /// assert_eq!(builder.all().none(), Some(false));
343    ///
344    /// let re_a = builder.to_re("a".into());
345    /// let re_comp = builder.comp(re_a.clone());
346    /// let inter = builder.inter(smallvec![re_a.clone(), re_comp.clone()]);
347    ///
348    /// assert_eq!(re_a.none(), Some(false));
349    /// assert_eq!(re_comp.none(), Some(false));
350    /// assert_eq!(inter.none(), None);
351    /// ```
352    pub fn none(&self) -> Option<bool> {
353        *self.none.borrow_mut().get_or_insert_with(|| self.op.none())
354    }
355
356    /// Returns whether the regular expression denotes the empty word.
357    /// A regular expression denotes the empty word if it only accepts the empty word.
358    /// This operation may return `None` if the property cannot be determined.
359    ///
360    /// # Example
361    /// ```
362    /// use smt_str::re::*;
363    /// use smallvec::smallvec;
364    /// let mut builder = ReBuilder::default();
365    ///
366    /// assert_eq!(builder.epsilon().epsilon(), Some(true));
367    ///
368    /// let re_a = builder.to_re("a".into());
369    /// let re_b = builder.to_re("b".into());
370    /// let re_a_star = builder.star(re_a.clone());
371    /// let re_b_star = builder.opt(re_a.clone());
372    /// // The intersection contains only the empty word but we can't determine it.
373    /// let re_inter = builder.inter(smallvec![re_a_star.clone(), re_b_star.clone()]);
374    /// assert_eq!(re_inter.epsilon(), None);
375    /// ```
376    pub fn epsilon(&self) -> Option<bool> {
377        *self
378            .epsi
379            .borrow_mut()
380            .get_or_insert_with(|| self.op.epsilon())
381    }
382
383    /// Returns the set of characters that can be first in a word accepted by the regular expression.
384    /// The set of first characters is partitioned into disjoint subsets of the alphabet, such that for each two characters `a` and `b` in the same subset, the left quotient of the regular expression w.r.t. `a` is equal to the left quotient of the regular expression w.r.t. `b`.
385    /// In other words, if  `a` and `b` are in the same subset, then for all words `u`, the regular expression accepts `a \cdot u` iff it accepts `b \cdot u`.
386    pub fn first(&self) -> Rc<AlphabetPartition> {
387        self.first
388            .borrow_mut()
389            .get_or_insert_with(|| self.op.first())
390            .clone()
391    }
392
393    /// The alphabet of the regular expression.
394    /// The alphabet is the set of characters that occur in the regular expression.
395    /// This is not the alphabet of words that the regular expression can accept.
396    /// For example, the alphabet of `[^a]` is `{'a'}`, but the alphabet of `[^a]` is every character except `'a'`.
397    ///
398    ///
399    /// # Example
400    /// ```
401    /// use smt_str::re::*;
402    /// use smt_str::alphabet::{Alphabet, CharRange};
403    ///
404    /// use smallvec::smallvec;
405    /// let mut builder = ReBuilder::default();
406    /// let re_a = builder.to_re("a".into()); // 'a'
407    /// let re_b = builder.to_re("b".into()); // 'b'
408    /// let re_a_comp = builder.star(re_a.clone()); // comp('a')
409    /// let re_union = builder.union(smallvec![re_a_comp.clone(), re_b.clone()]); // comp('a') | 'b'
410    ///
411    /// assert_eq!(re_a.alphabet().as_ref(), &Alphabet::from(CharRange::singleton('a')));
412    /// // even though `comp('a')` accepts words with any character from the SMT-LIB alphabet, the alphabet of the regex is still `{'a'}`.
413    /// assert_eq!(re_a_comp.alphabet().as_ref(), &Alphabet::from(CharRange::singleton('a')));
414    /// assert_eq!(
415    ///     re_union.alphabet().as_ref(),
416    ///     &Alphabet::from_iter(vec![CharRange::singleton('a'), CharRange::singleton('b')])
417    /// );
418    /// ```
419    pub fn alphabet(&self) -> Rc<Alphabet> {
420        self.alphabet
421            .borrow_mut()
422            .get_or_insert_with(|| self.op.alphabet())
423            .clone()
424    }
425
426    /// Returns Some(word) if the regular expression accepts only the given constant word, None otherwise or if it cannot be determined.
427    ///
428    /// # Example
429    /// ```
430    /// use smt_str::re::*;
431    /// use smallvec::smallvec;
432    /// let mut builder = ReBuilder::default();
433    ///
434    /// let re_foo = builder.to_re("foo".into());
435    /// assert_eq!(re_foo.is_constant(), Some("foo".into()));
436    ///
437    /// // Not a constant word
438    /// let re_opt = builder.opt(re_foo.clone());
439    /// assert_eq!(re_opt.is_constant(), None);
440    ///
441    /// // Cannot be determined
442    /// let re_bar = builder.to_re("bar".into());
443    /// let re_foo_or_bar = builder.union(smallvec![re_foo.clone(), re_bar.clone()]);
444    /// let inter = builder.inter(smallvec![re_foo.clone(), re_foo_or_bar.clone()]); // foo & (foo | bar) <--> foo
445    /// assert_eq!(inter.is_constant(), None);
446    ///```
447    pub fn is_constant(&self) -> Option<SmtString> {
448        self.is_constant
449            .borrow_mut()
450            .get_or_insert_with(|| self.op().is_constant())
451            .clone()
452    }
453
454    /// Returns the prefix of all words accepted by the regular expression.
455    /// Makes a best effort to obtain the longest prefix, but does not guarantee it.
456    /// Is `None` if the prefix cannot be determined.
457    ///
458    /// # Example
459    /// ```
460    /// use smt_str::re::*;
461    /// use smallvec::smallvec;
462    /// let mut builder = ReBuilder::default();
463    /// let re_foo = builder.to_re("foo".into());
464    ///
465    /// assert_eq!(re_foo.prefix(), Some("foo".into()));
466    ///
467    /// let re_bar = builder.to_re("bar".into());
468    /// let re_foobar = builder.concat(smallvec![re_foo.clone(), re_bar.clone()]);
469    ///
470    /// let union = builder.union(smallvec![re_foo.clone(), re_foobar.clone()]);
471    /// assert_eq!(union.prefix(), Some("foo".into()));
472    /// ```
473    pub fn prefix(&self) -> Option<SmtString> {
474        self.prefix
475            .borrow_mut()
476            .get_or_insert_with(|| self.op().prefix())
477            .clone()
478    }
479
480    /// Returns the suffix of all words accepted by the regular expression.
481    /// Makes a best effort to obtain the longest suffix, but does not guarantee it.
482    /// Is `None` if the suffix cannot be determined, which is the case for some extended regexes.
483    ///
484    /// # Example
485    /// ```
486    /// use smt_str::re::*;
487    /// use smallvec::smallvec;
488    /// let mut builder = ReBuilder::default();
489    /// let re_bar = builder.to_re("bar".into());
490    ///
491    /// assert_eq!(re_bar.suffix(), Some("bar".into()));
492    ///
493    /// let re_foo = builder.to_re("foo".into());
494    /// let re_foobar = builder.concat(smallvec![re_foo.clone(), re_bar.clone()]);
495    ///
496    /// let union = builder.union(smallvec![re_bar.clone(), re_foobar.clone()]);
497    /// assert_eq!(union.suffix(), Some("bar".into()));
498    /// ```
499    pub fn suffix(&self) -> Option<SmtString> {
500        self.suffix
501            .borrow_mut()
502            .get_or_insert_with(|| self.op().suffix())
503            .clone()
504    }
505
506    /// Returns whether the regular expression contains a complement operation.
507    ///
508    /// # Example
509    /// ```
510    /// use smt_str::re::*;
511    /// use smallvec::smallvec;
512    /// let mut builder = ReBuilder::default();
513    /// let re_a = builder.to_re("a".into());
514    /// let re_b = builder.to_re("b".into());
515    /// let re_a_comp = builder.comp(re_a.clone());
516    ///
517    /// assert!(!re_a.contains_complement());
518    /// assert!(re_a_comp.contains_complement());
519    ///
520    /// // Difference is equivalent to complement
521    /// let diff = builder.diff(re_a.clone(), re_b.clone());
522    /// assert!(diff.contains_complement());
523    /// ```
524    pub fn contains_complement(&self) -> bool {
525        *self
526            .contains_complement
527            .borrow_mut()
528            .get_or_insert_with(|| self.op().contains_complement())
529    }
530
531    /// Return whether the regular expression accepts a given word.
532    ///
533    /// # Example
534    /// ```
535    /// use smt_str::re::*;
536    /// let mut builder = ReBuilder::default();
537    /// let re_a = builder.to_re("a".into());
538    /// let re_star = builder.star(re_a.clone());
539    /// assert!(re_star.accepts(&"a".into()));
540    /// assert!(re_star.accepts(&"aaaa".into()));
541    /// assert!(re_star.accepts(&"".into()));
542    /// ```
543    pub fn accepts(&self, w: &SmtString) -> bool {
544        let mut builder_tmp = ReBuilder::default();
545        let mnged = builder_tmp.regex(&Rc::new(self.clone()));
546        deriv::deriv_word(&mnged, w.clone(), &mut builder_tmp).nullable()
547    }
548
549    /// Returns an iterator over the subexpressions of the regular expression.
550    ///
551    /// The subexpressions of a regular expression are the direct children of the operation defining the regex.
552    /// If the regex is a base case (literal, range, none, any, all), it has no subexpressions.
553    ///
554    /// # Example
555    /// ```
556    /// use smt_str::re::*;
557    /// use smallvec::smallvec;
558    ///
559    /// let mut builder = ReBuilder::default();
560    /// let re_a = builder.to_re("a".into());
561    /// let re_star = builder.star(re_a.clone());
562    /// let re_b = builder.to_re("b".into());
563    /// let re_union = builder.union(smallvec![re_star.clone(), re_b.clone()]);
564    ///
565    /// let mut subexprs = re_union.subexpr();
566    /// assert_eq!(subexprs.next(), Some(&re_star));
567    /// assert_eq!(subexprs.next(), Some(&re_b));
568    /// assert_eq!(subexprs.next(), None);
569    /// ```
570    pub fn subexpr(&self) -> SubExpressions {
571        SubExpressions {
572            node: self,
573            index: 0,
574        }
575    }
576}
577
578impl Display for ReNode {
579    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
580        write!(f, "{}", self.op)
581    }
582}
583
584/// An iterator over the subexpressions of a regular expression.
585/// Only iterates over the immediate subexpressions, not recursively.
586///
587/// The subexpressions of a regular expression are the direct children of the operation defining the regex.
588/// If the regex is a base case (literal, range, none, any, all), the iterator will return `None`.
589pub struct SubExpressions<'a> {
590    node: &'a ReNode,
591    index: usize,
592}
593
594impl<'a> Iterator for SubExpressions<'a> {
595    type Item = &'a Regex;
596
597    fn next(&mut self) -> Option<Self::Item> {
598        match self.node.op() {
599            ReOp::Literal(_) | ReOp::Range(_) | ReOp::None | ReOp::Any | ReOp::All => None,
600            ReOp::Concat(rs) | ReOp::Union(rs) | ReOp::Inter(rs) if self.index < rs.len() => {
601                let r = &rs[self.index];
602                self.index += 1;
603                Some(r)
604            }
605            ReOp::Star(r)
606            | ReOp::Plus(r)
607            | ReOp::Opt(r)
608            | ReOp::Comp(r)
609            | ReOp::Pow(r, _)
610            | ReOp::Loop(r, _, _)
611                if self.index == 0 =>
612            {
613                Some(r)
614            }
615            ReOp::Diff(r1, r2) => {
616                if self.index == 0 {
617                    self.index += 1;
618                    Some(r1)
619                } else if self.index == 1 {
620                    self.index += 1;
621                    Some(r2)
622                } else {
623                    None
624                }
625            }
626            _ => None,
627        }
628    }
629}
630
631/// The core operations that define a regular expression.
632///
633/// This enum represents the different building blocks of a regular expression,
634/// including **base cases** (such as literals and predefined sets) and **derived**
635/// operations (such as concatenation, union, and intersection).
636///
637/// ## Structure Sharing
638/// Many variants use **reference-counted pointers (`Rc<Regex>`)** to enable
639/// **structural sharing** and **deduplication** of sub-expressions. This ensures
640/// that identical sub-expressions are only computed once, improving efficiency.
641///
642/// ## Base Cases
643/// These represent fundamental building blocks of regular expressions:
644/// - `Literal`: A constant word.
645/// - `Range`: A set of characters in a specific range.
646/// - `None`: The empty set, which matches no words.
647/// - `Any`: The set of all possible one-character strings.
648/// - `All`: The set of all words, including the empty word.
649///
650/// ## Derived Operations
651/// These are higher-level operations built from the base cases:
652/// - **Concatenation (`Concat`)**: Sequences two or more regular expressions.
653/// - **Union (`Union`)**: Represents the union (alternation) of two or more regexes.
654/// - **Intersection (`Inter`)**: Matches only strings present in both regexes.
655/// - **Star (`Star`)**: The Kleene star (`r*`), allowing zero or more repetitions.
656/// - **Plus (`Plus`)**: The positive closure (`r+`), requiring at least one repetition.
657/// - **Optional (`Opt`)**: Matches either a regex or the empty word (`r?`).
658/// - **Difference (`Diff`)**: Matches words in one regex but not in another.
659/// - **Complement (`Comp`)**: Matches everything **except** what a regex matches.
660/// - **Power (`Pow`)**: Repeats a regex exactly `n` times (`r^n`).
661/// - **Loop (`Loop`)**: Matches repetitions of a regex within a given range `[l, u]`.
662#[derive(Debug, Clone, PartialEq, Eq, Hash)]
663pub enum ReOp {
664    // Base cases
665    /// A constant (possibly empty) word.
666    Literal(SmtString),
667    /// A range of characters.
668    Range(CharRange),
669    /// The empty set (∅), which matches no strings.
670    None,
671    /// The set of all one-character strings. Independent of the alphabet.
672    Any,
673    /// The set of all possible words, including the empty word.
674    All,
675    // Derived cases
676    /// The concatenation of two or more regular expressions.
677    /// Matches words formed by appending matches of each sub-expression in sequence.
678    Concat(SmallVec<[Regex; 2]>),
679    /// The union (alternation) of two or more regular expressions.
680    /// Matches words accepted by at least one of the sub-expressions.
681    Union(SmallVec<[Regex; 2]>),
682    /// The intersection of two or more regular expressions.
683    /// Matches only words present in all sub-expressions.
684    Inter(SmallVec<[Regex; 2]>),
685    /// The Kleene star (`r*`). Matches zero or more repetitions of the regex.
686    Star(Regex),
687    /// The positive closure (`r+`). Matches one or more repetitions of the regex.
688    Plus(Regex),
689    /// The optional operator (`r?`). Matches either the regex or the empty word (ε).
690    Opt(Regex),
691    /// The difference (`r1 - r2`). Matches words in `r1` that are **not** in `r2`.
692    Diff(Regex, Regex),
693    /// The complement (`¬r`). Matches all words **except** those in the regex.
694    Comp(Regex),
695    /// The power operation (`r^n`). Matches exactly `n` repetitions of the regex.
696    Pow(Regex, u32),
697    /// The bounded repetition (`r[l, u]`). Matches between `l` and `u` repetitions.
698    Loop(Regex, u32, u32),
699}
700
701impl ReOp {
702    /// Computes whether the regular expression is nullable.
703    fn nullable(&self) -> bool {
704        match self {
705            ReOp::Literal(word) => word.is_empty(),
706            ReOp::Range(_) | ReOp::None | ReOp::Any => false,
707            ReOp::All | ReOp::Star(_) | ReOp::Opt(_) => true,
708            ReOp::Inter(rs) | ReOp::Concat(rs) => rs.iter().all(|r| r.nullable()),
709            ReOp::Union(rs) => rs.iter().any(|r| r.nullable()),
710            ReOp::Plus(r) => r.nullable(),
711            ReOp::Diff(r1, r2) => r1.nullable() && !r2.nullable(),
712            ReOp::Comp(r) => !r.nullable(),
713            ReOp::Pow(r, e) => *e == 0 || r.nullable(),
714            ReOp::Loop(r, l, u) => {
715                if l <= u {
716                    *l == 0 || r.nullable()
717                } else {
718                    // Invalid loop, corresponding to the empty set.
719                    false
720                }
721            }
722        }
723    }
724
725    /// Compute whether the regular expression is universal, i.e., accepts all words.
726    fn universal(&self) -> Option<bool> {
727        match self {
728            ReOp::Literal(_) | ReOp::None | ReOp::Range(_) | ReOp::Any => Some(false),
729            ReOp::All => Some(true),
730            ReOp::Concat(rs) | ReOp::Inter(rs) => {
731                if rs.is_empty() {
732                    return match self {
733                        ReOp::Concat(_) => Some(false), // Concatenation of nothing is epsilon.
734                        ReOp::Inter(_) => Some(true),   // Intersection of nothing  is universal.
735                        _ => unreachable!(),
736                    };
737                }
738                // All subexpressions must be universal.
739                for r in rs {
740                    if !r.universal()? {
741                        return Some(false);
742                    }
743                }
744                Some(true)
745            }
746            ReOp::Union(rs) => {
747                if rs.is_empty() {
748                    return Some(false);
749                }
750                // Any subexpression that is universal makes the union universal.
751                if rs.iter().any(|r| r.universal().unwrap_or(false)) {
752                    Some(true)
753                } else {
754                    // The union of the subexpressions may form the universal set, we can't determine.
755                    None
756                }
757            }
758            ReOp::Star(r) | ReOp::Plus(r) | ReOp::Opt(r) => r.universal(),
759            ReOp::Diff(r1, r2) => match (r1.universal(), r2.none()) {
760                (Some(false), _) => Some(false),
761                (Some(true), Some(true)) => Some(true),
762                (_, Some(false)) => Some(false),
763                _ => None,
764            },
765            ReOp::Comp(r) => r.none(),
766            ReOp::Pow(_, 0) => Some(false),
767            ReOp::Pow(r, _) => r.universal(),
768            ReOp::Loop(_, l, u) if l > u || *u == 0 => Some(false),
769            ReOp::Loop(r, _, _) => r.universal(),
770        }
771    }
772
773    /// Compute whether the regular expression is empty, i.e., accepts no words.
774    fn none(&self) -> Option<bool> {
775        match self {
776            ReOp::None => Some(true), // The empty set is trivially empty.
777            ReOp::Literal(_) | ReOp::Any | ReOp::All => Some(false),
778            ReOp::Range(r) => Some(r.is_empty()),
779            ReOp::Concat(rs) => rs.iter().try_fold(false, |a, r| r.none().map(|b| a || b)),
780            ReOp::Inter(rs) => {
781                if rs.iter().any(|r| r.none().unwrap_or(false)) {
782                    Some(true)
783                } else {
784                    // The intersection of the subexpressions may be empty, we can't determine.
785                    None
786                }
787            }
788            ReOp::Union(rs) => rs.iter().all(|r| r.none().unwrap_or(false)).into(),
789            ReOp::Star(_) | ReOp::Opt(_) => Some(false), // These always match at least ε.
790            ReOp::Plus(r) => r.none(),
791            ReOp::Diff(r1, r2) => match (r1.none(), r2.universal()) {
792                (Some(true), _) => Some(true), // If `r1` is empty, the difference is empty.
793                (_, Some(true)) => Some(true), // Subtracting a universal set leaves an empty set.
794                _ => None,
795            },
796            ReOp::Comp(r) => r.universal(), // Complement of a universal regex is an empty set.
797            ReOp::Pow(_, 0) => Some(false), // `r^0 = ε`, which isn't empty.
798            ReOp::Pow(r, _) => r.none(),    // `r^n` is empty if `r` itself is empty.
799            ReOp::Loop(_, l, u) if l > u || *u == 0 => Some(true), // Invalid loop is empty by definition.
800            ReOp::Loop(r, _, _) => r.none(), // If `l > 0`, behavior follows `r`.
801        }
802    }
803
804    /// Compute whether the regular expression accepts the empty word and only the empty word.
805    fn epsilon(&self) -> Option<bool> {
806        match self {
807            ReOp::None | ReOp::Any | ReOp::All | ReOp::Range(_) => Some(false),
808            ReOp::Literal(w) => Some(w.is_empty()),
809            ReOp::Union(rs) | ReOp::Concat(rs) => {
810                if rs.is_empty() {
811                    return match self {
812                        ReOp::Union(_) => Some(false),
813                        ReOp::Concat(_) => Some(true),
814                        _ => unreachable!(),
815                    };
816                }
817                // All subexpressions must accept ε and only ε.
818                for r in rs {
819                    if !r.epsilon()? {
820                        return Some(false);
821                    }
822                }
823                Some(true)
824            }
825            ReOp::Inter(rs) => {
826                if rs.is_empty() {
827                    Some(false)
828                } else if rs.iter().all(|r| r.epsilon().unwrap_or(false)) {
829                    // If all subexpressions accept ε and only ε, the intersection does too.
830                    Some(true)
831                } else {
832                    // We can't determine if the intersection accepts only ε.
833                    None
834                }
835            }
836            ReOp::Plus(r) | ReOp::Star(r) | ReOp::Opt(r) => r.epsilon(),
837            ReOp::Diff(_, _) | ReOp::Comp(_) => None, // can't determine
838            ReOp::Pow(_, 0) => Some(true),            // `r^0 = ε`
839            ReOp::Pow(r, _) => r.epsilon(),           // `r^n` is ε iff `r` itself is ε.
840            ReOp::Loop(_, l, u) if l > u => Some(false), // Invalid loop is empty by definition.
841            ReOp::Loop(_, 0, 0) => Some(true),        // `r^0` is ε
842            ReOp::Loop(r, _, _) => r.epsilon(),       // `r^l..u` is ε iff `r` itself is ε.
843        }
844    }
845
846    /// Compute the set of characters that can be first in a word accepted by the regular expression.
847    /// The character are partitioned by their left quotient.
848    fn first(&self) -> Rc<AlphabetPartition> {
849        match self {
850            ReOp::Literal(w) => {
851                if let Some(c) = w.first() {
852                    Rc::new(AlphabetPartition::singleton(CharRange::singleton(c)))
853                } else {
854                    Rc::new(AlphabetPartition::empty())
855                }
856            }
857            ReOp::None => Rc::new(AlphabetPartition::empty()),
858            ReOp::All | ReOp::Any => Rc::new(AlphabetPartition::singleton(CharRange::all())),
859            ReOp::Concat(rs) => {
860                let mut first = rs
861                    .first()
862                    .map(|r| r.first().as_ref().clone()) // Get first() from the first regex
863                    .unwrap_or_else(AlphabetPartition::empty); // Default to empty
864                for (i, r) in rs.iter().enumerate().skip(1) {
865                    if !rs[i - 1].nullable() {
866                        break;
867                    } else {
868                        let r_first = r.first();
869                        first = first.refine(&r_first);
870                    }
871                }
872                Rc::new(first)
873            }
874            ReOp::Union(rs) | ReOp::Inter(rs) => {
875                let mut first = AlphabetPartition::empty();
876                for r in rs {
877                    first = first.refine(&r.first());
878                }
879                Rc::new(first)
880            }
881            ReOp::Star(r) | ReOp::Plus(r) | ReOp::Opt(r) | ReOp::Comp(r) => r.first().clone(),
882            ReOp::Range(rs) => Rc::new(AlphabetPartition::singleton(*rs)),
883            ReOp::Diff(r, s) => {
884                let first = r.first().as_ref().clone();
885                Rc::new(first.refine(&s.first()))
886            }
887            ReOp::Pow(r, _) | ReOp::Loop(r, _, _) => r.first().clone(),
888        }
889    }
890
891    fn alphabet(&self) -> Rc<Alphabet> {
892        let mut alph = Alphabet::empty();
893        match self {
894            ReOp::Literal(word) => {
895                for c in word.iter() {
896                    alph.insert_char(*c);
897                }
898            }
899            ReOp::Range(r) => {
900                alph.insert(*r);
901            }
902            ReOp::None | ReOp::Any | ReOp::All => {}
903            ReOp::Concat(rs) | ReOp::Union(rs) | ReOp::Inter(rs) => {
904                for r in rs {
905                    alph = alph.union(r.alphabet().as_ref());
906                }
907            }
908            ReOp::Comp(r)
909            | ReOp::Star(r)
910            | ReOp::Plus(r)
911            | ReOp::Opt(r)
912            | ReOp::Pow(r, _)
913            | ReOp::Loop(r, _, _) => return r.alphabet().clone(),
914            ReOp::Diff(r1, r2) => {
915                alph = alph.union(r1.alphabet().as_ref());
916                alph = alph.union(r2.alphabet().as_ref());
917            }
918        }
919
920        Rc::new(alph)
921    }
922
923    fn is_constant(&self) -> Option<SmtString> {
924        match self {
925            ReOp::Literal(word) => Some(word.clone()),
926            ReOp::Range(r) => {
927                if r.start() == r.end() {
928                    Some(SmtString::new(vec![r.start()]))
929                } else {
930                    None
931                }
932            }
933            ReOp::None | ReOp::Any | ReOp::All => None,
934            ReOp::Concat(r) => {
935                let mut word = SmtString::empty();
936                for re in r {
937                    if let Some(w) = re.is_constant() {
938                        word = word.concat(&w);
939                    } else {
940                        return None;
941                    }
942                }
943                Some(word)
944            }
945            ReOp::Union(rs) | ReOp::Inter(rs) => {
946                let mut words = rs.iter().map(|r| r.is_constant()).collect_vec();
947                if words.iter().all(|w| w.is_some()) {
948                    let word = words.pop().unwrap().unwrap();
949                    // check if all words are the same
950                    for w in words {
951                        if word != w.unwrap() {
952                            return None;
953                        }
954                    }
955                    Some(word)
956                } else {
957                    None
958                }
959            }
960            ReOp::Star(r) | ReOp::Opt(r) | ReOp::Plus(r) => {
961                if r.epsilon().unwrap_or(false) {
962                    Some(SmtString::empty())
963                } else {
964                    None
965                }
966            }
967
968            ReOp::Diff(_, _) | ReOp::Comp(_) => None, // can't determine
969            ReOp::Pow(_, 0) | ReOp::Loop(_, _, 0) => Some(SmtString::empty()),
970            ReOp::Pow(r, _) => {
971                if r.epsilon().unwrap_or(false) {
972                    Some(SmtString::empty())
973                } else {
974                    None
975                }
976            }
977            ReOp::Loop(r, l, u) if l <= u => {
978                if r.epsilon().unwrap_or(false) {
979                    Some(SmtString::empty())
980                } else {
981                    None
982                }
983            }
984            ReOp::Loop(_, _, _) => None,
985        }
986    }
987
988    fn prefix(&self) -> Option<SmtString> {
989        match self {
990            ReOp::Literal(word) => Some(word.clone()),
991            ReOp::None | ReOp::Any | ReOp::All => Some(SmtString::empty()),
992            ReOp::Range(r) => r.is_singleton().map(|c| c.into()),
993            ReOp::Concat(rs) => {
994                let mut prefix = SmtString::empty();
995                if rs.is_empty() {
996                    return Some(SmtString::empty());
997                }
998                // while the regexes accept only one word, we can concatenate them
999                let mut i = 0;
1000                while let Some(w) = rs[i].is_constant() {
1001                    prefix = prefix.concat(&w);
1002                    i += 1;
1003                }
1004                if i == rs.len() {
1005                    Some(prefix)
1006                } else {
1007                    prefix = prefix.concat(&rs[i].prefix()?);
1008                    Some(prefix)
1009                }
1010            }
1011            ReOp::Union(rs) => {
1012                let mut prefixes = Vec::with_capacity(rs.len());
1013                for r in rs {
1014                    prefixes.push(r.prefix()?);
1015                }
1016                if prefixes.is_empty() {
1017                    return Some(SmtString::empty());
1018                }
1019                // Find the longest common prefix
1020                let mut i = 0;
1021                let mut done = false;
1022                while !done {
1023                    if prefixes.iter().all(|p| i < p.len()) {
1024                        if !prefixes.iter().map(|p| p[i]).all_equal() {
1025                            // not all characters are equal
1026                            done = true;
1027                        } else {
1028                            // all characters are equal, check next character
1029                            i += 1;
1030                        }
1031                    } else {
1032                        done = true;
1033                    }
1034                }
1035                Some(prefixes.first().unwrap().take(i))
1036            }
1037            ReOp::Star(_) | ReOp::Opt(_) => Some(SmtString::empty()),
1038            ReOp::Plus(r) => r.prefix(),
1039            ReOp::Diff(_, _) | ReOp::Comp(_) | ReOp::Inter(_) => None, // can't determine
1040            ReOp::Pow(r, _) => r.prefix(),
1041            ReOp::Loop(r, l, u) if l <= u => r.prefix(),
1042            ReOp::Loop(_, _, _) => None,
1043        }
1044    }
1045
1046    fn suffix(&self) -> Option<SmtString> {
1047        match self {
1048            ReOp::Literal(word) => Some(word.clone()),
1049            ReOp::None | ReOp::Any | ReOp::All => Some(SmtString::empty()),
1050            ReOp::Range(r) => r.is_singleton().map(|c| c.into()),
1051            ReOp::Concat(rs) => {
1052                let mut suffix = SmtString::empty();
1053                if rs.is_empty() {
1054                    return Some(SmtString::empty());
1055                }
1056                // while the regexes accept only one word, we can concatenate them
1057                let mut i = rs.len() - 1;
1058                while let Some(w) = rs[i].is_constant() {
1059                    suffix = w.concat(&suffix);
1060                    i -= 1;
1061                }
1062                if i == 0 {
1063                    Some(suffix)
1064                } else {
1065                    suffix = rs[i].suffix()?.concat(&suffix);
1066                    Some(suffix)
1067                }
1068            }
1069            ReOp::Union(rs) => {
1070                let mut suffixes = Vec::with_capacity(rs.len());
1071                for r in rs {
1072                    suffixes.push(r.suffix()?);
1073                }
1074                if suffixes.is_empty() {
1075                    return Some(SmtString::empty());
1076                }
1077                // Find the longest common suffix by reversing the words and searching for the longest common prefix
1078                let suffixed_revd = suffixes.iter_mut().map(|w| w.reversed()).collect_vec();
1079                let mut i = 0;
1080                let mut done = false;
1081                while !done {
1082                    if suffixed_revd.iter().all(|p| i < p.len()) {
1083                        if !suffixed_revd.iter().map(|p| p[i]).all_equal() {
1084                            // not all characters are equal
1085                            done = true;
1086                        } else {
1087                            // all characters are equal, check next character
1088                            i += 1;
1089                        }
1090                    } else {
1091                        done = true;
1092                    }
1093                }
1094                let lcs_rev = suffixed_revd.first().unwrap().take(i);
1095                // reverse the longest common prefix
1096                Some(lcs_rev.reversed())
1097            }
1098            ReOp::Star(_) | ReOp::Opt(_) => Some(SmtString::empty()),
1099            ReOp::Plus(r) => r.suffix(),
1100            ReOp::Diff(_, _) | ReOp::Comp(_) | ReOp::Inter(_) => None, // can't determine
1101            ReOp::Pow(r, _) => r.suffix(),
1102            ReOp::Loop(r, l, u) if l <= u => r.suffix(),
1103            ReOp::Loop(_, _, _) => None,
1104        }
1105    }
1106
1107    fn contains_complement(&self) -> bool {
1108        match self {
1109            ReOp::Literal(_) | ReOp::Range(_) | ReOp::None | ReOp::Any | ReOp::All => false,
1110            ReOp::Concat(rs) | ReOp::Union(rs) | ReOp::Inter(rs) => {
1111                rs.iter().any(|r| r.contains_complement())
1112            }
1113            ReOp::Star(r) | ReOp::Plus(r) | ReOp::Opt(r) => r.contains_complement(),
1114            ReOp::Diff(_, _) | ReOp::Comp(_) => true,
1115            ReOp::Pow(_, 0) => false,
1116            ReOp::Pow(r, _) => r.contains_complement(),
1117            ReOp::Loop(_, l, u) if l > u || *u == 0 => false,
1118            ReOp::Loop(r, _, _) => r.contains_complement(),
1119        }
1120    }
1121
1122    fn simple(&self) -> bool {
1123        match self {
1124            ReOp::Literal(_) | ReOp::Range(_) | ReOp::None | ReOp::Any | ReOp::All => true,
1125            ReOp::Diff(_, _) | ReOp::Comp(_) | ReOp::Inter(_) => false,
1126            ReOp::Concat(rs) | ReOp::Union(rs) => rs.iter().all(|r| r.simple()),
1127            ReOp::Star(r)
1128            | ReOp::Plus(r)
1129            | ReOp::Opt(r)
1130            | ReOp::Pow(r, _)
1131            | ReOp::Loop(r, _, _) => r.simple(),
1132        }
1133    }
1134}
1135
1136impl Display for ReOp {
1137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1138        match self {
1139            ReOp::Literal(w) => write!(f, "(str.to_re \"{}\")", w),
1140            ReOp::None => write!(f, "re.none"),
1141            ReOp::All => write!(f, "re.all"),
1142            ReOp::Any => write!(f, "re.allchar"),
1143            ReOp::Concat(rs) => {
1144                write!(f, "(re.++")?;
1145                for r in rs {
1146                    write!(f, " {}", r)?;
1147                }
1148                write!(f, ")")
1149            }
1150            ReOp::Union(rs) => {
1151                write!(f, "(re.union")?;
1152                for r in rs.iter() {
1153                    write!(f, " {}", r)?;
1154                }
1155                write!(f, ")")
1156            }
1157            ReOp::Inter(rs) => {
1158                write!(f, "(re.inter")?;
1159                for r in rs.iter() {
1160                    write!(f, " {}", r)?;
1161                }
1162                write!(f, ")")
1163            }
1164            ReOp::Star(r) => write!(f, "(re.* {})", r),
1165            ReOp::Plus(p) => write!(f, "(re.+ {})", p),
1166            ReOp::Opt(r) => write!(f, "(re.opt {})", r),
1167            ReOp::Range(r) => write!(f, "(re.range \"{}\" \"{}\")", r.start(), r.end()),
1168            ReOp::Comp(c) => write!(f, "(re.comp {})", c),
1169            ReOp::Diff(r1, r2) => write!(f, "(re.diff {} {})", r1, r2),
1170            ReOp::Pow(r, n) => write!(f, "((_ re.^ {}) {})", n, r),
1171            ReOp::Loop(r, l, u) => write!(f, "((_ re.loop {} {}) {})", l, u, r),
1172        }
1173    }
1174}
1175
1176/// Checks whether the regular operator denotes a union of characters, expressed as a ranges.
1177/// If it does, the ranges are returned as a vector. Otherwise, `None` is returned.
1178pub fn union_of_chars(re: &Regex) -> Option<Vec<CharRange>> {
1179    match re.op() {
1180        ReOp::Literal(w) if w.len() == 1 => {
1181            let c = w.first().unwrap();
1182            Some(vec![CharRange::singleton(c)])
1183        }
1184        ReOp::None => Some(vec![]),
1185        ReOp::Any => Some(vec![CharRange::all()]),
1186        ReOp::Union(rs) => {
1187            let mut ranges = vec![];
1188            for r in rs {
1189                ranges.append(&mut union_of_chars(r)?);
1190            }
1191            Some(ranges)
1192        }
1193        ReOp::Range(r) => Some(vec![*r]),
1194        _ => None,
1195    }
1196}
1197
1198impl Arbitrary for ReNode {
1199    fn arbitrary(g: &mut Gen) -> Self {
1200        use smallvec::smallvec;
1201
1202        fn gen_regex(g: &mut Gen, builder: &mut ReBuilder, depth: u8) -> Regex {
1203            fn base_case(g: &mut Gen, n: usize, builder: &mut ReBuilder) -> Regex {
1204                match n {
1205                    0 => builder.epsilon(),
1206                    1 => builder.none(),
1207                    2 => builder.allchar(),
1208                    3 => builder.to_re(SmtString::arbitrary(g)),
1209                    4 => builder.range(CharRange::arbitrary(g)),
1210                    _ => unreachable!(),
1211                }
1212            }
1213            let choice = usize::arbitrary(g) % if depth == 0 { 5 } else { 15 };
1214
1215            match choice {
1216                n if n < 5 => base_case(g, n, builder),
1217                5 => {
1218                    let inner = gen_regex(g, builder, depth - 1);
1219                    builder.star(inner)
1220                }
1221                6 => {
1222                    let inner = gen_regex(g, builder, depth - 1);
1223                    builder.plus(inner)
1224                }
1225                7 => {
1226                    let inner = gen_regex(g, builder, depth - 1);
1227                    builder.opt(inner)
1228                }
1229                8 => {
1230                    let inner = gen_regex(g, builder, depth - 1);
1231                    builder.comp(inner)
1232                }
1233                9 => {
1234                    let left = gen_regex(g, builder, depth - 1);
1235                    let right = gen_regex(g, builder, depth - 1);
1236                    builder.concat(smallvec![left, right])
1237                }
1238                10 => {
1239                    let left = gen_regex(g, builder, depth - 1);
1240                    let right = gen_regex(g, builder, depth - 1);
1241                    builder.union(smallvec![left, right])
1242                }
1243                11 => {
1244                    let left = gen_regex(g, builder, depth - 1);
1245                    let right = gen_regex(g, builder, depth - 1);
1246                    builder.inter(smallvec![left, right])
1247                }
1248                12 => {
1249                    let left = gen_regex(g, builder, depth - 1);
1250                    let right = gen_regex(g, builder, depth - 1);
1251                    builder.diff(left, right)
1252                }
1253                13 => {
1254                    let inner = gen_regex(g, builder, depth - 1);
1255                    let count = u32::arbitrary(g) % 5;
1256                    builder.pow(inner, count)
1257                }
1258                14 => {
1259                    let inner = gen_regex(g, builder, depth - 1);
1260                    let lo = u32::arbitrary(g) % 4;
1261                    let hi = lo + (u32::arbitrary(g) % 4);
1262                    builder.loop_(inner, lo, hi)
1263                }
1264                _ => unreachable!(),
1265            }
1266        }
1267
1268        let mut builder = ReBuilder::default();
1269        let depth = g.size().min(5) as u8;
1270        gen_regex(g, &mut builder, depth).as_ref().clone()
1271    }
1272}
1273
1274#[cfg(test)]
1275mod tests {
1276    use crate::re::ReBuilder;
1277
1278    use super::*;
1279
1280    use quickcheck_macros::quickcheck;
1281    use smallvec::smallvec;
1282
1283    #[test]
1284    fn test_union_of_ranges_const() {
1285        let mut builder = ReBuilder::non_optimizing();
1286        let re = builder.to_re("a".into());
1287        let result = union_of_chars(&re);
1288        assert_eq!(result, Some(vec![CharRange::singleton('a')]));
1289    }
1290
1291    #[test]
1292    fn test_union_of_ranges_const_word() {
1293        let mut builder = ReBuilder::non_optimizing();
1294        let re = builder.to_re("abc".into());
1295        let result = union_of_chars(&re);
1296        assert_eq!(result, None);
1297    }
1298
1299    #[test]
1300    fn test_union_of_ranges_none() {
1301        let builder = ReBuilder::non_optimizing();
1302        let re = builder.none();
1303        let result = union_of_chars(&re);
1304        assert_eq!(result, Some(vec![]));
1305    }
1306
1307    #[test]
1308    fn test_union_of_ranges_all_char() {
1309        let builder = ReBuilder::non_optimizing();
1310        let re = builder.allchar();
1311        let result = union_of_chars(&re);
1312        assert_eq!(result, Some(vec![CharRange::all()]));
1313    }
1314
1315    #[test]
1316    fn test_union_of_ranges_union() {
1317        let mut builder = ReBuilder::non_optimizing();
1318        let alternatives = smallvec![
1319            builder.to_re("a".into()),
1320            builder.to_re("b".into()),
1321            builder.to_re("c".into()),
1322        ];
1323        let reg = builder.union(alternatives);
1324        let result = union_of_chars(&reg);
1325        assert_eq!(
1326            result,
1327            Some(vec![
1328                CharRange::singleton('a'),
1329                CharRange::singleton('b'),
1330                CharRange::singleton('c')
1331            ])
1332        );
1333    }
1334
1335    #[test]
1336    fn test_union_of_ranges_range() {
1337        let mut builder = ReBuilder::non_optimizing();
1338        let re = builder.range_from_to('a', 'z');
1339        let result = union_of_chars(&re);
1340        assert_eq!(result, Some(vec![CharRange::new('a', 'z')]));
1341    }
1342
1343    /* Nullable */
1344
1345    #[quickcheck]
1346    fn nullable_literal(s: SmtString) {
1347        let mut builder = ReBuilder::non_optimizing();
1348        let epsi = builder.to_re(s.clone());
1349        if s.is_empty() {
1350            assert!(epsi.nullable());
1351        } else {
1352            assert!(!epsi.nullable());
1353        }
1354    }
1355
1356    #[quickcheck]
1357    fn nullable_range(r: CharRange) {
1358        let mut builder = ReBuilder::non_optimizing();
1359        let r = builder.range(r);
1360        assert!(!r.nullable());
1361    }
1362
1363    #[test]
1364    fn nullable_base_cases() {
1365        let builder = ReBuilder::non_optimizing();
1366        assert!(!builder.none().nullable());
1367        assert!(!builder.allchar().nullable());
1368        assert!(builder.epsilon().nullable());
1369        assert!(builder.all().nullable());
1370    }
1371
1372    #[quickcheck]
1373    fn nullable_star(r: ReNode) {
1374        let mut builder = ReBuilder::non_optimizing();
1375        let r = builder.regex(&Rc::new(r));
1376        let star = builder.star(r);
1377        assert!(star.nullable());
1378    }
1379
1380    #[quickcheck]
1381    fn nullable_opt(r: ReNode) {
1382        let mut builder = ReBuilder::non_optimizing();
1383        let r = builder.regex(&Rc::new(r));
1384        let opt = builder.opt(r);
1385        assert!(opt.nullable());
1386    }
1387
1388    #[quickcheck]
1389    fn nullable_concat(rs: Vec<ReNode>) {
1390        let mut builder = ReBuilder::non_optimizing();
1391        let rs: SmallVec<_> = rs.into_iter().map(|r| builder.regex(&Rc::new(r))).collect();
1392        let concat = builder.concat(rs.clone());
1393        if rs.iter().all(|r| r.nullable()) {
1394            assert!(concat.nullable());
1395        } else {
1396            assert!(!concat.nullable());
1397        }
1398    }
1399
1400    #[test]
1401    fn nullable_concat_all_parts_nullable() {
1402        let mut builder = ReBuilder::non_optimizing();
1403        let epsi = builder.epsilon();
1404        let a = builder.to_re("a".into());
1405
1406        let r1 = builder.concat(smallvec![epsi.clone(), epsi.clone()]);
1407        assert!(r1.nullable());
1408
1409        let r2 = builder.concat(smallvec![epsi, a]);
1410        assert!(!r2.nullable());
1411    }
1412
1413    #[test]
1414    fn nullable_inter_all_parts_nullable() {
1415        let mut builder = ReBuilder::non_optimizing();
1416        let epsi = builder.epsilon();
1417        let also_epsi = builder.to_re("".into());
1418
1419        let r1 = builder.inter(smallvec![epsi.clone(), also_epsi.clone()]);
1420        assert!(r1.nullable());
1421
1422        let a = builder.to_re("a".into());
1423        let r2 = builder.inter(smallvec![epsi, a]);
1424        assert!(!r2.nullable());
1425    }
1426
1427    #[quickcheck]
1428    fn nullable_inter(rs: Vec<ReNode>) {
1429        let mut builder = ReBuilder::non_optimizing();
1430        let rs: SmallVec<_> = rs.into_iter().map(|r| builder.regex(&Rc::new(r))).collect();
1431        let inter = builder.inter(rs.clone());
1432        if rs.iter().all(|r| r.nullable()) {
1433            assert!(inter.nullable());
1434        } else {
1435            assert!(!inter.nullable());
1436        }
1437    }
1438
1439    #[quickcheck]
1440    fn nullable_union(rs: Vec<ReNode>) {
1441        let mut builder = ReBuilder::non_optimizing();
1442        let rs: SmallVec<_> = rs.into_iter().map(|r| builder.regex(&Rc::new(r))).collect();
1443        let union = builder.union(rs.clone());
1444        if rs.iter().any(|r| r.nullable()) {
1445            assert!(union.nullable());
1446        } else {
1447            assert!(!union.nullable());
1448        }
1449    }
1450
1451    #[quickcheck]
1452    fn nullable_plus(r: ReNode) {
1453        let mut builder = ReBuilder::non_optimizing();
1454        let r = builder.regex(&Rc::new(r));
1455        let plus = builder.plus(r.clone());
1456        if r.nullable() {
1457            assert!(plus.nullable());
1458        } else {
1459            assert!(!plus.nullable());
1460        }
1461    }
1462
1463    #[quickcheck]
1464    fn nullable_diff(l: ReNode, r: ReNode) {
1465        let mut builder = ReBuilder::non_optimizing();
1466        let l = builder.regex(&Rc::new(l));
1467        let r = builder.regex(&Rc::new(r));
1468        let diff = builder.diff(l.clone(), r.clone());
1469
1470        if l.nullable() && !r.nullable() {
1471            assert!(diff.nullable());
1472        } else {
1473            assert!(!diff.nullable());
1474        }
1475    }
1476
1477    #[quickcheck]
1478    fn nullable_comp(r: ReNode) {
1479        let mut builder = ReBuilder::non_optimizing();
1480        let r = builder.regex(&Rc::new(r));
1481        let comp = builder.comp(r.clone());
1482        if !r.nullable() {
1483            assert!(comp.nullable());
1484        } else {
1485            assert!(!comp.nullable());
1486        }
1487    }
1488
1489    #[quickcheck]
1490    fn nullable_pow(r: ReNode, e: u32) {
1491        let mut builder = ReBuilder::non_optimizing();
1492        let r = builder.regex(&Rc::new(r));
1493        let pow = builder.pow(r.clone(), e);
1494
1495        if e == 0 || r.nullable() {
1496            assert!(pow.nullable());
1497        } else {
1498            assert!(!pow.nullable());
1499        }
1500    }
1501
1502    #[quickcheck]
1503    fn nullable_loop(r: ReNode, l: u32, u: u32) {
1504        let mut builder = ReBuilder::non_optimizing();
1505        let r = builder.regex(&Rc::new(r));
1506        let loop_ = builder.loop_(r.clone(), l, u);
1507
1508        if l <= u {
1509            if l == 0 || r.nullable() {
1510                assert!(loop_.nullable());
1511            } else {
1512                assert!(!loop_.nullable());
1513            }
1514        } else {
1515            assert!(!loop_.nullable());
1516        }
1517    }
1518
1519    /* Epsilon */
1520
1521    #[quickcheck]
1522    fn epsilon_literal(s: SmtString) {
1523        let mut builder = ReBuilder::non_optimizing();
1524        let lit = builder.to_re(s.clone());
1525        assert_eq!(lit.epsilon(), Some(s.is_empty()));
1526    }
1527
1528    #[quickcheck]
1529    fn epsilon_range(r: CharRange) {
1530        let mut builder = ReBuilder::non_optimizing();
1531        let r = builder.range(r);
1532        assert_eq!(r.epsilon(), Some(false));
1533    }
1534
1535    #[test]
1536    fn epsilon_base_cases() {
1537        let builder = ReBuilder::non_optimizing();
1538        assert_eq!(builder.none().epsilon(), Some(false));
1539        assert_eq!(builder.allchar().epsilon(), Some(false));
1540        assert_eq!(builder.all().epsilon(), Some(false));
1541        assert_eq!(builder.epsilon().epsilon(), Some(true));
1542    }
1543
1544    #[quickcheck]
1545    fn epsilon_star(r: ReNode) {
1546        let mut builder = ReBuilder::non_optimizing();
1547        let r = builder.regex(&Rc::new(r));
1548        let star = builder.star(r.clone());
1549        assert_eq!(star.epsilon(), r.epsilon());
1550    }
1551
1552    #[quickcheck]
1553    fn epsilon_opt(r: ReNode) {
1554        let mut builder = ReBuilder::non_optimizing();
1555        let r = builder.regex(&Rc::new(r));
1556        let opt = builder.opt(r.clone());
1557        assert_eq!(opt.epsilon(), r.epsilon());
1558    }
1559
1560    #[test]
1561    fn epsi_concat_bug() {
1562        let mut builder = ReBuilder::non_optimizing();
1563        let inner = smallvec![builder.none()];
1564        let concat = builder.concat(inner);
1565        assert_eq!(concat.epsilon(), Some(false));
1566    }
1567
1568    #[quickcheck]
1569    fn epsilon_concat(rs: Vec<ReNode>) {
1570        let mut builder = ReBuilder::non_optimizing();
1571        let rs: SmallVec<_> = rs.into_iter().map(|r| builder.regex(&Rc::new(r))).collect();
1572        let concat = builder.concat(rs.clone());
1573
1574        let mut expected = Some(true);
1575        for r in rs {
1576            match r.epsilon() {
1577                Some(true) => {}
1578                Some(false) => {
1579                    // if one the subexpressions in not epsilon, the whole expression is not epsilon
1580                    expected = Some(false);
1581                    break;
1582                }
1583                None => {
1584                    // if we can't determine if a subexpression is epsilon, thus we can't determine the epsilon of the whole expression
1585                    expected = None;
1586                    break;
1587                }
1588            }
1589        }
1590        assert_eq!(concat.epsilon(), expected);
1591    }
1592
1593    #[quickcheck]
1594    fn epsilon_inter(rs: Vec<ReNode>) {
1595        let mut builder = ReBuilder::non_optimizing();
1596        let rs: SmallVec<_> = rs.into_iter().map(|r| builder.regex(&Rc::new(r))).collect();
1597        let inter = builder.inter(rs.clone());
1598
1599        let mut expected = Some(!rs.is_empty());
1600        for r in rs {
1601            match r.epsilon() {
1602                Some(false) | None => {
1603                    // if one the subexpressions in not epsilon, the whole expression is not epsilon
1604                    expected = None;
1605                    break;
1606                }
1607                _ => {}
1608            }
1609        }
1610        assert_eq!(inter.epsilon(), expected);
1611    }
1612
1613    #[quickcheck]
1614    fn epsilon_union(rs: Vec<ReNode>) {
1615        let mut builder = ReBuilder::non_optimizing();
1616        let rs: SmallVec<_> = rs.into_iter().map(|r| builder.regex(&Rc::new(r))).collect();
1617        let union = builder.union(rs.clone());
1618
1619        let mut expected = Some(!rs.is_empty());
1620        for r in rs {
1621            match r.epsilon() {
1622                Some(true) => {}
1623                Some(false) => {
1624                    // if one the subexpressions in not epsilon, the whole expression is not epsilon
1625                    expected = Some(false);
1626                    break;
1627                }
1628                None => {
1629                    // if we can't determine the epsilon of a subexpression, we can't determine the epsilon of the whole expression
1630                    expected = None;
1631                    break;
1632                }
1633            }
1634        }
1635
1636        assert_eq!(union.epsilon(), expected);
1637    }
1638
1639    #[quickcheck]
1640    fn epsilon_plus(r: ReNode) {
1641        let mut builder = ReBuilder::non_optimizing();
1642        let r = builder.regex(&Rc::new(r));
1643        let plus = builder.plus(r.clone());
1644        assert_eq!(plus.epsilon(), r.epsilon());
1645    }
1646
1647    #[quickcheck]
1648    fn epsilon_diff(r1: ReNode, r2: ReNode) {
1649        let mut builder = ReBuilder::non_optimizing();
1650        let r1 = builder.regex(&Rc::new(r1));
1651        let r2 = builder.regex(&Rc::new(r2));
1652        let diff = builder.diff(r1, r2);
1653
1654        // Cannot determine in general
1655        assert_eq!(diff.epsilon(), None);
1656    }
1657
1658    #[quickcheck]
1659    fn epsilon_comp(r: ReNode) {
1660        let mut builder = ReBuilder::non_optimizing();
1661        let r = builder.regex(&Rc::new(r));
1662        let comp = builder.comp(r);
1663        assert_eq!(comp.epsilon(), None);
1664    }
1665
1666    #[quickcheck]
1667    fn epsilon_pow(r: ReNode, e: u32) {
1668        let mut builder = ReBuilder::non_optimizing();
1669        let r = builder.regex(&Rc::new(r));
1670        let pow = builder.pow(r.clone(), e);
1671
1672        let expected = if e == 0 { Some(true) } else { r.epsilon() };
1673        assert_eq!(pow.epsilon(), expected);
1674    }
1675
1676    #[quickcheck]
1677    fn epsilon_loop(r: ReNode, l: u32, u: u32) {
1678        let mut builder = ReBuilder::non_optimizing();
1679        let r = builder.regex(&Rc::new(r));
1680        let loop_ = builder.loop_(r.clone(), l, u);
1681
1682        let expected = if l > u {
1683            Some(false)
1684        } else if u == 0 {
1685            Some(true)
1686        } else {
1687            r.epsilon()
1688        };
1689        assert_eq!(loop_.epsilon(), expected);
1690    }
1691
1692    /* Universal */
1693
1694    #[quickcheck]
1695    fn universal_literal(s: SmtString) {
1696        let mut builder = ReBuilder::non_optimizing();
1697        let lit = builder.to_re(s.clone());
1698        assert_eq!(lit.universal(), Some(false));
1699    }
1700
1701    #[quickcheck]
1702    fn universal_range(r: CharRange) {
1703        let mut builder = ReBuilder::non_optimizing();
1704        let r = builder.range(r);
1705        assert_eq!(r.universal(), Some(false));
1706    }
1707
1708    #[test]
1709    fn universal_base_cases() {
1710        let builder = ReBuilder::non_optimizing();
1711        assert_eq!(builder.none().universal(), Some(false));
1712        assert_eq!(builder.allchar().universal(), Some(false));
1713        assert_eq!(builder.all().universal(), Some(true));
1714        assert_eq!(builder.epsilon().universal(), Some(false));
1715    }
1716
1717    #[quickcheck]
1718    fn universal_star(r: ReNode) {
1719        let mut builder = ReBuilder::non_optimizing();
1720        let r = builder.regex(&Rc::new(r));
1721        let star = builder.star(r.clone());
1722        assert_eq!(star.universal(), r.universal());
1723    }
1724
1725    #[quickcheck]
1726    fn universal_opt(r: ReNode) {
1727        let mut builder = ReBuilder::non_optimizing();
1728        let r = builder.regex(&Rc::new(r));
1729        let opt = builder.opt(r.clone());
1730        assert_eq!(opt.universal(), r.universal());
1731    }
1732
1733    #[quickcheck]
1734    fn universal_concat(rs: Vec<ReNode>) {
1735        let mut builder = ReBuilder::non_optimizing();
1736        let rs: SmallVec<_> = rs.into_iter().map(|r| builder.regex(&Rc::new(r))).collect();
1737        let concat = builder.concat(rs.clone());
1738
1739        let mut expected = Some(false);
1740        for r in rs {
1741            match r.universal() {
1742                Some(true) => {
1743                    expected = Some(true);
1744                }
1745                Some(false) => {
1746                    // if one the subexpressions in not epsilon, the whole expression is not epsilon
1747                    expected = Some(false);
1748                    break;
1749                }
1750                None => {
1751                    // if we can't determine the epsilon of a subexpression, we can't determine the epsilon of the whole expression
1752                    expected = None;
1753                    break;
1754                }
1755            }
1756        }
1757        assert_eq!(concat.universal(), expected);
1758    }
1759
1760    #[quickcheck]
1761    fn universal_inter(rs: Vec<ReNode>) {
1762        let mut builder = ReBuilder::non_optimizing();
1763        let rs: SmallVec<_> = rs.into_iter().map(|r| builder.regex(&Rc::new(r))).collect();
1764        let inter = builder.inter(rs.clone());
1765
1766        let mut expected = Some(rs.is_empty());
1767        for r in rs {
1768            match r.universal() {
1769                Some(true) => {}
1770                Some(false) => {
1771                    // if one the subexpressions in not epsilon, the whole expression is not epsilon
1772                    expected = Some(false);
1773                    break;
1774                }
1775                None => {
1776                    // if we can't determine the epsilon of a subexpression, we can't determine the epsilon of the whole expression
1777                    expected = None;
1778                    break;
1779                }
1780            }
1781        }
1782        assert_eq!(inter.universal(), expected);
1783    }
1784
1785    #[quickcheck]
1786    fn universal_union(rs: Vec<ReNode>) {
1787        let mut builder = ReBuilder::non_optimizing();
1788        let rs: SmallVec<_> = rs.into_iter().map(|r| builder.regex(&Rc::new(r))).collect();
1789        let union = builder.union(rs.clone());
1790
1791        let mut expected = if rs.is_empty() { Some(false) } else { None };
1792        for r in rs {
1793            if let Some(true) = r.universal() {
1794                expected = Some(true);
1795                break;
1796            }
1797        }
1798        assert_eq!(union.universal(), expected);
1799    }
1800
1801    #[quickcheck]
1802    fn universal_plus(r: ReNode) {
1803        let mut builder = ReBuilder::non_optimizing();
1804        let r = builder.regex(&Rc::new(r));
1805        let plus = builder.plus(r.clone());
1806        assert_eq!(plus.universal(), r.universal());
1807    }
1808
1809    #[quickcheck]
1810    fn universal_diff(r1: ReNode, r2: ReNode) {
1811        let mut builder = ReBuilder::non_optimizing();
1812        let r1 = builder.regex(&Rc::new(r1));
1813        let r2 = builder.regex(&Rc::new(r2));
1814        let diff = builder.diff(r1.clone(), r2.clone());
1815
1816        // Cannot determine in general
1817        let expected = match (r1.universal(), r2.none()) {
1818            (Some(false), _) => Some(false), // Removing something from non-universal is non-universal
1819            (Some(true), Some(true)) => Some(true), // Removing nothing from universal is universal
1820            (_, Some(false)) => Some(false), // Removing something from anything is non-universal
1821            _ => None,
1822        };
1823        assert_eq!(diff.universal(), expected);
1824    }
1825
1826    #[quickcheck]
1827    fn universal_comp(r: ReNode) {
1828        let mut builder = ReBuilder::non_optimizing();
1829        let r = builder.regex(&Rc::new(r));
1830        let comp = builder.comp(r.clone());
1831        if let Some(true) = r.none() {
1832            assert_eq!(comp.universal(), Some(true));
1833        }
1834    }
1835
1836    #[quickcheck]
1837    fn universal_pow(r: ReNode, e: u32) {
1838        let e = e % 20;
1839        let mut builder = ReBuilder::non_optimizing();
1840        let r = builder.regex(&Rc::new(r));
1841        let pow = builder.pow(r.clone(), e);
1842
1843        if r.universal().unwrap_or(false) {
1844            if e == 0 {
1845                assert_eq!(pow.universal(), Some(false));
1846            } else {
1847                assert_eq!(pow.universal(), Some(true));
1848            }
1849        }
1850    }
1851
1852    #[quickcheck]
1853    fn universal_loop(r: ReNode, l: u32, u: u32) {
1854        let mut builder = ReBuilder::non_optimizing();
1855        let r = builder.regex(&Rc::new(r));
1856        let loop_ = builder.loop_(r.clone(), l, u);
1857        if l > u {
1858            assert_eq!(loop_.universal(), Some(false));
1859        } else if r.universal().unwrap_or(false) {
1860            if u == 0 {
1861                assert_eq!(loop_.universal(), Some(false));
1862            } else {
1863                assert_eq!(loop_.universal(), Some(true));
1864            }
1865        }
1866    }
1867
1868    #[test]
1869    fn test_accept() {
1870        // (assert (str.in_re X (re.++ (str.to_re "x") ((_ re.loop 4 4) (re.union (str.to_re "e") (str.to_re "d"))) (str.to_re "x"))))
1871        let mut builder = ReBuilder::non_optimizing();
1872        let x = builder.to_re("x".into());
1873        let e = builder.to_re("e".into());
1874        let d = builder.to_re("d".into());
1875        let union = builder.union(smallvec![e.clone(), d.clone()]);
1876        let looped = builder.loop_(union.clone(), 4, 4);
1877        let re = builder.concat(smallvec![x.clone(), looped.clone(), x.clone()]);
1878
1879        assert!(
1880            re.accepts(&SmtString::from("xededx")),
1881            "Expected xededx to be accepted by {}",
1882            re
1883        );
1884    }
1885
1886    #[test]
1887    fn test_is_const_none() {
1888        let builder = ReBuilder::non_optimizing();
1889        let none = builder.none();
1890        let result = none.is_constant();
1891        assert_eq!(result, None);
1892    }
1893
1894    #[test]
1895    fn test_is_const_all() {
1896        let builder = ReBuilder::non_optimizing();
1897        let all = builder.all();
1898        let result = all.is_constant();
1899        assert_eq!(result, None);
1900    }
1901
1902    #[test]
1903    fn test_is_const_allchar() {
1904        let builder = ReBuilder::non_optimizing();
1905        let any = builder.allchar();
1906        let result = any.is_constant();
1907        assert_eq!(result, None);
1908    }
1909
1910    #[test]
1911    fn test_is_const_concat_consts() {
1912        let mut builder = ReBuilder::non_optimizing();
1913        let a = builder.to_re("a".into());
1914        let b = builder.to_re("b".into());
1915        let c = builder.to_re("c".into());
1916        let abc = builder.concat(smallvec![a.clone(), b.clone(), c.clone()]);
1917        let result = abc.is_constant();
1918        assert_eq!(result, Some(SmtString::from("abc")));
1919    }
1920
1921    #[test]
1922    fn test_is_const_const() {
1923        let mut builder = ReBuilder::non_optimizing();
1924        let abc = builder.to_re("abc".into());
1925
1926        let result = abc.is_constant();
1927        assert_eq!(result, Some(SmtString::from("abc")));
1928    }
1929
1930    #[test]
1931    fn test_is_const_union() {
1932        let mut builder = ReBuilder::non_optimizing();
1933        let abc = builder.to_re("abc".into());
1934        let def = builder.to_re("def".into());
1935
1936        let equal_union = builder.union(smallvec![abc.clone(), abc.clone()]);
1937        let different_union = builder.union(smallvec![abc.clone(), def.clone()]);
1938        assert_eq!(equal_union.is_constant(), Some(SmtString::from("abc")));
1939        assert_eq!(different_union.is_constant(), None);
1940    }
1941
1942    #[test]
1943    fn test_is_const_inter() {
1944        let mut builder = ReBuilder::non_optimizing();
1945        let abc = builder.to_re("abc".into());
1946        let def = builder.to_re("def".into());
1947
1948        let equal_union = builder.inter(smallvec![abc.clone(), abc.clone()]);
1949        let different_union = builder.inter(smallvec![abc.clone(), def.clone()]);
1950        assert_eq!(equal_union.is_constant(), Some(SmtString::from("abc")));
1951        assert_eq!(different_union.is_constant(), None);
1952    }
1953
1954    #[test]
1955    fn test_is_const_star() {
1956        let mut builder = ReBuilder::non_optimizing();
1957        let epsi = builder.to_re("".into());
1958        let a = builder.to_re("a".into());
1959        let star_a = builder.star(a.clone());
1960        let star_epsi = builder.star(epsi.clone());
1961        assert_eq!(star_a.is_constant(), None);
1962        assert_eq!(star_epsi.is_constant(), Some(SmtString::empty()));
1963    }
1964
1965    #[test]
1966    fn test_is_const_plus() {
1967        let mut builder = ReBuilder::non_optimizing();
1968        let epsi = builder.to_re("".into());
1969        let a = builder.to_re("a".into());
1970        let plus_a = builder.plus(a.clone());
1971        let plus_epsi = builder.plus(epsi.clone());
1972        assert_eq!(plus_a.is_constant(), None);
1973        assert_eq!(plus_epsi.is_constant(), Some(SmtString::empty()));
1974    }
1975
1976    #[test]
1977    fn test_is_const_opt() {
1978        let mut builder = ReBuilder::non_optimizing();
1979        let epsi = builder.to_re("".into());
1980        let a = builder.to_re("a".into());
1981        let opt_a = builder.opt(a.clone());
1982        let opt_epsi = builder.opt(epsi.clone());
1983        assert_eq!(opt_a.is_constant(), None);
1984        assert_eq!(opt_epsi.is_constant(), Some(SmtString::empty()));
1985    }
1986
1987    #[test]
1988    fn test_is_const_range() {
1989        let mut builder = ReBuilder::non_optimizing();
1990        let a = builder.range_from_to('a', 'a');
1991        let ab = builder.range_from_to('a', 'b');
1992        assert_eq!(a.is_constant(), Some(SmtString::from("a")));
1993        assert_eq!(ab.is_constant(), None);
1994    }
1995
1996    #[test]
1997    fn prefix_bug() {
1998        // (re.++ (str.to_re "x") ((_ re.^ 4) (re.union (str.to_re "e") (str.to_re "d"))) (str.to_re "x"))
1999        let mut builder = ReBuilder::non_optimizing();
2000        let x = builder.to_re("x".into());
2001        let e = builder.to_re("e".into());
2002        let d = builder.to_re("d".into());
2003        let union = builder.union(smallvec![e.clone(), d.clone()]);
2004        let pow = builder.pow(union.clone(), 4);
2005        let re = builder.concat(smallvec![x.clone(), pow.clone(), x.clone()]);
2006        let prefix = re.prefix();
2007        assert_eq!(prefix, Some(SmtString::from("x")));
2008    }
2009
2010    #[test]
2011    fn prefix_suf() {
2012        // (re.++ (str.to_re "x") ((_ re.^ 4) (re.union (str.to_re "e") (str.to_re "d"))) (str.to_re "x"))
2013        let mut builder = ReBuilder::non_optimizing();
2014        let x = builder.to_re("x".into());
2015        let e = builder.to_re("e".into());
2016        let d = builder.to_re("d".into());
2017        let union = builder.union(smallvec![e.clone(), d.clone()]);
2018        let pow = builder.pow(union.clone(), 4);
2019        let re = builder.concat(smallvec![x.clone(), pow.clone(), x.clone()]);
2020        let prefix = re.suffix();
2021        assert_eq!(prefix, Some(SmtString::from("x")));
2022    }
2023}