Skip to main content

malachite_base/unions/
mod.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use alloc::string::{String, ToString};
10use core::fmt::{self, Display, Formatter};
11use core::str::FromStr;
12
13/// This is the error type for the unions' [`FromStr`] implementations.
14#[derive(Clone, Debug, Eq, Hash, PartialEq)]
15pub enum UnionFromStrError<E> {
16    /// For when the union's variant can't be determined.
17    Generic(String),
18    /// For when the union's variant can be determined but the wrapped value can't be parsed.
19    Specific(E),
20}
21
22/// Defines unions.
23///
24/// Malachite provides [`Union2`], but you can also define `Union3`, `Union4`, and so on, in your
25/// program using the code below. The documentation for [`Union2`] and describes these other `enum`s
26/// as well.
27///
28/// ```
29/// use malachite_base::union_struct;
30/// use malachite_base::unions::UnionFromStrError;
31/// use std::fmt::{self, Display, Formatter};
32/// use std::str::FromStr;
33///
34/// union_struct!(
35///     (pub(crate)),
36///     Union3,
37///     Union3<T, T, T>,
38///     [A, A, 'A', a],
39///     [B, B, 'B', b],
40///     [C, C, 'C', c]
41/// );
42/// union_struct!(
43///     (pub(crate)),
44///     Union4,
45///     Union4<T, T, T, T>,
46///     [A, A, 'A', a],
47///     [B, B, 'B', b],
48///     [C, C, 'C', c],
49///     [D, D, 'D', d]
50/// );
51/// union_struct!(
52///     (pub(crate)),
53///     Union5,
54///     Union5<T, T, T, T, T>,
55///     [A, A, 'A', a],
56///     [B, B, 'B', b],
57///     [C, C, 'C', c],
58///     [D, D, 'D', d],
59///     [E, E, 'E', e]
60/// );
61/// union_struct!(
62///     (pub(crate)),
63///     Union6,
64///     Union6<T, T, T, T, T, T>,
65///     [A, A, 'A', a],
66///     [B, B, 'B', b],
67///     [C, C, 'C', c],
68///     [D, D, 'D', d],
69///     [E, E, 'E', e],
70///     [F, F, 'F', f]
71/// );
72/// union_struct!(
73///     (pub(crate)),
74///     Union7,
75///     Union7<T, T, T, T, T, T, T>,
76///     [A, A, 'A', a],
77///     [B, B, 'B', b],
78///     [C, C, 'C', c],
79///     [D, D, 'D', d],
80///     [E, E, 'E', e],
81///     [F, F, 'F', f],
82///     [G, G, 'G', g]
83/// );
84/// union_struct!(
85///     (pub(crate)),
86///     Union8,
87///     Union8<T, T, T, T, T, T, T, T>,
88///     [A, A, 'A', a],
89///     [B, B, 'B', b],
90///     [C, C, 'C', c],
91///     [D, D, 'D', d],
92///     [E, E, 'E', e],
93///     [F, F, 'F', f],
94///     [G, G, 'G', g],
95///     [H, H, 'H', h]
96/// );
97/// ```
98#[macro_export]
99macro_rules! union_struct {
100    (
101        ($($vis:tt)*),
102        $name: ident,
103        $single: ty,
104        $([$t: ident, $cons: ident, $c: expr, $x: ident]),*
105    ) => {
106        #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
107        /// This is a union, or sum type, of $n$ values. It is essentially a generic enum.
108        $($vis)* enum $name<$($t),*> {
109            $($cons($t)),*
110        }
111
112        impl<T> $single {
113            /// Given a union whose variants all have the same type, unwraps it into a value of that
114            /// type.
115            ///
116            /// # Worst-case complexity
117            /// Constant time and additional memory.
118            ///
119            /// # Examples
120            /// See [here](self#unwrap).
121            #[allow(clippy::missing_const_for_fn)] // Can't be const because of destructor
122            $($vis)* fn unwrap(self) -> T {
123                match self {
124                    $(
125                        $name::$cons($x) => $x
126                    ),*
127                }
128            }
129        }
130
131        impl<$($t: Display),*> Display for $name<$($t),*> {
132            /// Converts a union to a [`String`].
133            ///
134            /// # Examples
135            /// See [here](self#fmt).
136            #[inline]
137            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
138                match self {
139                    $(
140                        $name::$cons($x) => f.write_fmt(format_args!("{}({})", $c, $x))
141                    ),*
142                }
143            }
144        }
145
146        impl<$($t: FromStr),*> FromStr for $name<$($t),*> {
147            type Err = UnionFromStrError<$name<$($t::Err),*>>;
148
149            /// Converts a string to a union.
150            ///
151            /// If the string does not represent a valid union, an error value is returned.
152            ///
153            /// # Examples
154            /// See [here](self#from_str).
155            #[inline]
156            fn from_str(src: &str) -> Result<$name<$($t),*>, Self::Err> {
157                if src.is_empty() {
158                    return Err(UnionFromStrError::Generic(String::new()));
159                }
160                let (head, tail) = src.split_at(1);
161                let tail = if let Some(tail) = tail.strip_prefix('(') {
162                    tail
163                } else {
164                    return Err(UnionFromStrError::Generic(src.to_string()));
165                };
166                let tail = if let Some(tail) = tail.strip_suffix(')') {
167                    tail
168                } else {
169                    return Err(UnionFromStrError::Generic(src.to_string()));
170                };
171                match head.chars().next().unwrap() {
172                    $(
173                        $c => $t::from_str(tail)
174                                .map($name::$cons)
175                                .map_err(|e| UnionFromStrError::Specific($name::$cons(e))),
176                    )*
177                    _ => Err(UnionFromStrError::Generic(src.to_string()))
178                }
179            }
180        }
181    }
182}
183
184union_struct!((pub), Union2, Union2<T, T>, [A, A, 'A', a], [B, B, 'B', b]);
185
186/// Iterators that generate unions without repetition.
187///
188/// # lex_union2s
189/// ```
190/// use itertools::Itertools;
191/// use malachite_base::bools::exhaustive::exhaustive_bools;
192/// use malachite_base::unions::exhaustive::lex_union2s;
193/// use malachite_base::unions::Union2;
194///
195/// let u2s = lex_union2s(exhaustive_bools(), 0..4).collect_vec();
196/// assert_eq!(
197///     u2s.as_slice(),
198///     &[
199///         Union2::A(false),
200///         Union2::A(true),
201///         Union2::B(0),
202///         Union2::B(1),
203///         Union2::B(2),
204///         Union2::B(3)
205///     ]
206/// );
207/// ```
208///
209/// # exhaustive_union2s
210/// ```
211/// use itertools::Itertools;
212/// use malachite_base::bools::exhaustive::exhaustive_bools;
213/// use malachite_base::unions::exhaustive::exhaustive_union2s;
214/// use malachite_base::unions::Union2;
215///
216/// let u2s = exhaustive_union2s(exhaustive_bools(), 0..4).collect_vec();
217/// assert_eq!(
218///     u2s.as_slice(),
219///     &[
220///         Union2::A(false),
221///         Union2::B(0),
222///         Union2::A(true),
223///         Union2::B(1),
224///         Union2::B(2),
225///         Union2::B(3)
226///     ]
227/// );
228/// ```
229pub mod exhaustive;
230#[cfg(feature = "random")]
231/// Iterators that generate unions randomly.
232///
233/// # random_union2s
234/// ```
235/// use itertools::Itertools;
236/// use malachite_base::chars::random::random_char_inclusive_range;
237/// use malachite_base::num::random::random_unsigned_inclusive_range;
238/// use malachite_base::random::EXAMPLE_SEED;
239/// use malachite_base::unions::random::random_union2s;
240/// use malachite_base::unions::Union2;
241///
242/// let us = random_union2s(
243///     EXAMPLE_SEED,
244///     &|seed| random_char_inclusive_range(seed, 'a', 'z'),
245///     &|seed| random_unsigned_inclusive_range::<u32>(seed, 1, 10),
246/// );
247/// assert_eq!(
248///     us.take(20).collect_vec().as_slice(),
249///     &[
250///         Union2::A('v'),
251///         Union2::B(3),
252///         Union2::A('c'),
253///         Union2::A('q'),
254///         Union2::A('i'),
255///         Union2::A('e'),
256///         Union2::A('p'),
257///         Union2::A('g'),
258///         Union2::A('s'),
259///         Union2::B(7),
260///         Union2::A('n'),
261///         Union2::A('t'),
262///         Union2::B(9),
263///         Union2::A('m'),
264///         Union2::A('z'),
265///         Union2::B(7),
266///         Union2::B(9),
267///         Union2::A('o'),
268///         Union2::A('m'),
269///         Union2::B(3),
270///     ],
271/// );
272/// ```
273pub mod random;