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;