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;