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: $crate::strings::latex::ToLatex),*> $crate::strings::latex::ToLatex
147            for $name<$($t),*>
148        {
149            /// Writes a union as a LaTeX math-mode fragment.
150            ///
151            /// The fragment is the variant's letter, upright, followed by the wrapped value's own
152            /// fragment in parentheses. The letter is what keeps two variants holding equal values
153            /// apart.
154            ///
155            /// # Worst-case complexity
156            /// Same as the time and additional memory complexity of `fmt_latex` for the wrapped
157            /// value.
158            ///
159            /// # Examples
160            /// See [here](self#fmt_latex).
161            #[inline]
162            fn fmt_latex(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
163                match self {
164                    $(
165                        $name::$cons($x) => {
166                            ::core::fmt::Write::write_str(f, "\\text{")?;
167                            ::core::fmt::Write::write_char(f, $c)?;
168                            ::core::fmt::Write::write_str(f, "}\\left(")?;
169                            $crate::strings::latex::ToLatex::fmt_latex($x, f)?;
170                            ::core::fmt::Write::write_str(f, "\\right)")
171                        }
172                    ),*
173                }
174            }
175        }
176
177        impl<$($t: $crate::strings::typst::ToTypst),*> $crate::strings::typst::ToTypst
178            for $name<$($t),*>
179        {
180            /// Writes a union as a Typst math-mode fragment.
181            ///
182            /// The fragment is the variant's letter, upright, followed by the wrapped value's own
183            /// fragment in parentheses. The letter is what keeps two variants holding equal values
184            /// apart.
185            ///
186            /// # Worst-case complexity
187            /// Same as the time and additional memory complexity of `fmt_typst` for the wrapped
188            /// value.
189            ///
190            /// # Examples
191            /// See [here](self#fmt_typst).
192            #[inline]
193            fn fmt_typst(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
194                match self {
195                    $(
196                        $name::$cons($x) => {
197                            ::core::fmt::Write::write_char(f, '"')?;
198                            ::core::fmt::Write::write_char(f, $c)?;
199                            ::core::fmt::Write::write_str(f, "\"(")?;
200                            $crate::strings::typst::ToTypst::fmt_typst($x, f)?;
201                            ::core::fmt::Write::write_char(f, ')')
202                        }
203                    ),*
204                }
205            }
206        }
207
208        impl<$($t: FromStr),*> FromStr for $name<$($t),*> {
209            type Err = UnionFromStrError<$name<$($t::Err),*>>;
210
211            /// Converts a string to a union.
212            ///
213            /// If the string does not represent a valid union, an error value is returned.
214            ///
215            /// # Examples
216            /// See [here](self#from_str).
217            #[inline]
218            fn from_str(src: &str) -> Result<$name<$($t),*>, Self::Err> {
219                if src.is_empty() {
220                    return Err(UnionFromStrError::Generic(String::new()));
221                }
222                let (head, tail) = src.split_at(1);
223                let tail = if let Some(tail) = tail.strip_prefix('(') {
224                    tail
225                } else {
226                    return Err(UnionFromStrError::Generic(src.to_string()));
227                };
228                let tail = if let Some(tail) = tail.strip_suffix(')') {
229                    tail
230                } else {
231                    return Err(UnionFromStrError::Generic(src.to_string()));
232                };
233                match head.chars().next().unwrap() {
234                    $(
235                        $c => $t::from_str(tail)
236                                .map($name::$cons)
237                                .map_err(|e| UnionFromStrError::Specific($name::$cons(e))),
238                    )*
239                    _ => Err(UnionFromStrError::Generic(src.to_string()))
240                }
241            }
242        }
243    }
244}
245
246union_struct!((pub), Union2, Union2<T, T>, [A, A, 'A', a], [B, B, 'B', b]);
247
248/// Iterators that generate unions without repetition.
249///
250/// # lex_union2s
251/// ```
252/// use itertools::Itertools;
253/// use malachite_base::bools::exhaustive::exhaustive_bools;
254/// use malachite_base::unions::Union2;
255/// use malachite_base::unions::exhaustive::lex_union2s;
256///
257/// let u2s = lex_union2s(exhaustive_bools(), 0..4).collect_vec();
258/// assert_eq!(
259///     u2s.as_slice(),
260///     &[
261///         Union2::A(false),
262///         Union2::A(true),
263///         Union2::B(0),
264///         Union2::B(1),
265///         Union2::B(2),
266///         Union2::B(3)
267///     ]
268/// );
269/// ```
270///
271/// # exhaustive_union2s
272/// ```
273/// use itertools::Itertools;
274/// use malachite_base::bools::exhaustive::exhaustive_bools;
275/// use malachite_base::unions::Union2;
276/// use malachite_base::unions::exhaustive::exhaustive_union2s;
277///
278/// let u2s = exhaustive_union2s(exhaustive_bools(), 0..4).collect_vec();
279/// assert_eq!(
280///     u2s.as_slice(),
281///     &[
282///         Union2::A(false),
283///         Union2::B(0),
284///         Union2::A(true),
285///         Union2::B(1),
286///         Union2::B(2),
287///         Union2::B(3)
288///     ]
289/// );
290/// ```
291pub mod exhaustive;
292#[cfg(feature = "random")]
293/// Iterators that generate unions randomly.
294///
295/// # random_union2s
296/// ```
297/// use itertools::Itertools;
298/// use malachite_base::chars::random::random_char_inclusive_range;
299/// use malachite_base::num::random::random_unsigned_inclusive_range;
300/// use malachite_base::random::EXAMPLE_SEED;
301/// use malachite_base::unions::Union2;
302/// use malachite_base::unions::random::random_union2s;
303///
304/// let us = random_union2s(
305///     EXAMPLE_SEED,
306///     &|seed| random_char_inclusive_range(seed, 'a', 'z'),
307///     &|seed| random_unsigned_inclusive_range::<u32>(seed, 1, 10),
308/// );
309/// assert_eq!(
310///     us.take(20).collect_vec().as_slice(),
311///     &[
312///         Union2::A('v'),
313///         Union2::B(3),
314///         Union2::A('c'),
315///         Union2::A('q'),
316///         Union2::A('i'),
317///         Union2::A('e'),
318///         Union2::A('p'),
319///         Union2::A('g'),
320///         Union2::A('s'),
321///         Union2::B(7),
322///         Union2::A('n'),
323///         Union2::A('t'),
324///         Union2::B(9),
325///         Union2::A('m'),
326///         Union2::A('z'),
327///         Union2::B(7),
328///         Union2::B(9),
329///         Union2::A('o'),
330///         Union2::A('m'),
331///         Union2::B(3),
332///     ],
333/// );
334/// ```
335pub mod random;