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