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;