malachite_base/num/conversion/string/options/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 crate::rounding_modes::RoundingMode::{self, *};
10
11/// A `struct` determining how much "detail" should be used when creating a scientific-notation
12/// string.
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14pub enum SciSizeOptions {
15 /// Indicates that the number should be rendered in its full precision.
16 Complete,
17 /// Indicates how many significant figures should be shown. The precision cannot be zero.
18 Precision(u64),
19 /// Indicates how many digits after the decimal (or other-base) point should be shown. For
20 /// example, if the base is 10 and the scale is 2, then up to two digits after the decimal point
21 /// should be shown.
22 Scale(u64),
23}
24
25impl Default for SciSizeOptions {
26 fn default() -> SciSizeOptions {
27 SciSizeOptions::Precision(16) // Similar to f64 string output
28 }
29}
30
31#[cfg(feature = "test_build")]
32impl SciSizeOptions {
33 pub const fn is_valid(&self) -> bool {
34 if let SciSizeOptions::Precision(p) = *self {
35 p != 0
36 } else {
37 true
38 }
39 }
40}
41
42/// A `struct` determining how a number should be formatted as a "scientific" string.
43///
44/// - The base must be between 2 and 36, inclusive. The characters representing the digits are `'0'`
45/// through `'9'` and either `'a'` through `'z'` or `'A'` through `'Z'`, depending on whether the
46/// `lowercase` field is set. The default base is 10.
47///
48/// - The rounding mode determines how the output should be rounded, in case the `size_options`
49/// field is such that the number can't be fully represented. The default rounding mode is
50/// `Nearest`.
51///
52/// - The size options determine the precision or scale that the number should be displayed with.
53/// The default is `Precision(16)`, which is about as much precision as an `f64` is usually
54/// displayed with.
55///
56/// - The negative exponent threshold determines when small numbers switch to scientific notation.
57/// The default is $-6$, meaning that the numbers $1/10, 1/100, 1/1000, \ldots$. would be
58/// displayed as `0.1, 0.01, 0.001, 0.0001, 0.00001, 1e-6, 1e-7...`. The threshold must be
59/// negative.
60///
61/// - The lowercase setting determines whether digits in bases greater than 10 are lowercase or
62/// uppercase. The default is `true`.
63///
64/// - The exponent lowercase setting determines whether the exponent indicator is lowercase (`'e'`)
65/// or uppercase (`'E'`). The default is `true`.
66///
67/// - The "force exponent plus sign" setting determines whether positive exponents should be
68/// rendered with an explicit plus sign. If the base is 15 or greater, then the explicit plus sign
69/// is used regardless, in order to distinguish the exponent indicator from the digit `'e'`. The
70/// default is `false`.
71///
72/// - The "include trailing zeros" setting determines whether trailing zeros after the decimal (or
73/// other-base) point should be included. The default is `false`.
74#[derive(Clone, Copy, Debug, Eq, PartialEq)]
75pub struct ToSciOptions {
76 pub(crate) base: u8,
77 pub(crate) rounding_mode: RoundingMode,
78 pub(crate) size_options: SciSizeOptions,
79 neg_exp_threshold: i64,
80 pub(crate) lowercase: bool,
81 pub(crate) e_lowercase: bool,
82 pub(crate) force_exponent_plus_sign: bool,
83 pub(crate) include_trailing_zeros: bool,
84}
85
86impl Default for ToSciOptions {
87 fn default() -> ToSciOptions {
88 ToSciOptions {
89 base: 10,
90 rounding_mode: Nearest,
91 size_options: SciSizeOptions::default(),
92 neg_exp_threshold: -6,
93 lowercase: true,
94 e_lowercase: true,
95 force_exponent_plus_sign: false,
96 include_trailing_zeros: false,
97 }
98 }
99}
100
101impl ToSciOptions {
102 /// Returns the base to be used in the conversion. It is always between 2 and 36, inclusive.
103 #[inline]
104 pub const fn get_base(&self) -> u8 {
105 self.base
106 }
107
108 /// Returns the rounding mode to be used in the conversion.
109 #[inline]
110 pub const fn get_rounding_mode(&self) -> RoundingMode {
111 self.rounding_mode
112 }
113
114 /// Returns the size options to be used in the conversion.
115 #[inline]
116 pub const fn get_size_options(&self) -> SciSizeOptions {
117 self.size_options
118 }
119
120 /// Returns the exponent low threshold to be used in the conversion. It is always negative.
121 #[inline]
122 pub const fn get_neg_exp_threshold(&self) -> i64 {
123 self.neg_exp_threshold
124 }
125
126 /// Returns whether the digits should be lowercase.
127 #[inline]
128 pub const fn get_lowercase(&self) -> bool {
129 self.lowercase
130 }
131
132 /// Returns whether the exponent indicator should be lowercase (`'e'` rather than `'E'`).
133 #[inline]
134 pub const fn get_e_lowercase(&self) -> bool {
135 self.e_lowercase
136 }
137
138 /// Returns whether positive exponents should always be preceded by an explicit plus sign.
139 #[inline]
140 pub const fn get_force_exponent_plus_sign(&self) -> bool {
141 self.force_exponent_plus_sign
142 }
143
144 /// Returns whether trailing zeros should be included after the decimal (or other-base) point.
145 #[inline]
146 pub const fn get_include_trailing_zeros(&self) -> bool {
147 self.include_trailing_zeros
148 }
149
150 /// Sets the base to be used in the conversion.
151 ///
152 /// # Panics
153 /// Panics if `base` is less than 2 or greater than 36.
154 #[inline]
155 pub fn set_base(&mut self, base: u8) {
156 assert!(base >= 2);
157 assert!(base <= 36);
158 self.base = base;
159 }
160
161 /// Sets the rounding mode to be used in the conversion.
162 #[inline]
163 pub fn set_rounding_mode(&mut self, rm: RoundingMode) {
164 self.rounding_mode = rm;
165 }
166
167 /// Sets the size options to the "Complete" mode, indicating that the number should be converted
168 /// using its full precision.
169 #[inline]
170 pub fn set_size_complete(&mut self) {
171 self.size_options = SciSizeOptions::Complete;
172 }
173
174 /// Sets the size options to some precision, or number of significant digits.
175 ///
176 /// # Panics
177 /// Panics if `precision` is zero.
178 #[inline]
179 pub fn set_precision(&mut self, precision: u64) {
180 assert_ne!(precision, 0);
181 self.size_options = SciSizeOptions::Precision(precision);
182 }
183
184 /// Sets the size options to some scale, or number of digits after the decimal (or other-base)
185 /// point.
186 #[inline]
187 pub fn set_scale(&mut self, scale: u64) {
188 self.size_options = SciSizeOptions::Scale(scale);
189 }
190
191 /// Sets the threshold at which nonzero numbers with a small absolute value start being
192 /// represented using negative exponents.
193 #[inline]
194 pub fn set_neg_exp_threshold(&mut self, neg_exp_threshold: i64) {
195 assert!(neg_exp_threshold < 0);
196 self.neg_exp_threshold = neg_exp_threshold;
197 }
198
199 /// Specifies that digits in bases greater than ten should be output with lowercase letters.
200 #[inline]
201 pub fn set_lowercase(&mut self) {
202 self.lowercase = true;
203 }
204
205 /// Specifies that digits in bases greater than ten should be output with uppercase letters.
206 #[inline]
207 pub fn set_uppercase(&mut self) {
208 self.lowercase = false;
209 }
210
211 /// Specifies that the exponent-indicating character should be `'e'`.
212 #[inline]
213 pub fn set_e_lowercase(&mut self) {
214 self.e_lowercase = true;
215 }
216
217 /// Specifies that the exponent-indicating character should be `'E'`.
218 #[inline]
219 pub fn set_e_uppercase(&mut self) {
220 self.e_lowercase = false;
221 }
222
223 /// Sets whether a positive exponent should be preceded by an explicit plus sign.
224 ///
225 /// If the base is 15 or greater, an explicit plus sign will be used regardless, in order to
226 /// differentiate the exponent-indicating character from the digit `'e'`.
227 #[inline]
228 pub fn set_force_exponent_plus_sign(&mut self, force_exponent_plus_sign: bool) {
229 self.force_exponent_plus_sign = force_exponent_plus_sign;
230 }
231
232 /// Sets whether trailing zeros after the decimal (or other-base) point should be included.
233 #[inline]
234 pub fn set_include_trailing_zeros(&mut self, include_trailing_zeros: bool) {
235 self.include_trailing_zeros = include_trailing_zeros;
236 }
237
238 #[cfg(feature = "test_build")]
239 pub fn is_valid(&self) -> bool {
240 (2..=36).contains(&self.base) && self.neg_exp_threshold < 0 && self.size_options.is_valid()
241 }
242}
243
244/// A `struct` determining how a number should be parsed from a "scientific" string.
245///
246/// - The base must be between 2 and 36, inclusive. The characters representing the digits may be
247/// `'0'` through `'9'` and either `'a'` through `'z'` or `'A'` through `'Z'`. The default base is
248/// 10.
249///
250/// - The rounding mode determines how the output should be rounded, in case the output type can't
251/// represent all possible input strings. The default rounding mode is `Nearest`.
252#[derive(Clone, Copy, Debug, Eq, PartialEq)]
253pub struct FromSciStringOptions {
254 pub(crate) base: u8,
255 pub(crate) rounding_mode: RoundingMode,
256}
257
258impl Default for FromSciStringOptions {
259 fn default() -> FromSciStringOptions {
260 FromSciStringOptions {
261 base: 10,
262 rounding_mode: Nearest,
263 }
264 }
265}
266
267impl FromSciStringOptions {
268 /// Returns the base to be used in the conversion. It is always between 2 and 36, inclusive.
269 #[inline]
270 pub const fn get_base(&self) -> u8 {
271 self.base
272 }
273
274 /// Returns the rounding mode to be used in the conversion.
275 #[inline]
276 pub const fn get_rounding_mode(&self) -> RoundingMode {
277 self.rounding_mode
278 }
279
280 /// Sets the base to be used in the conversion.
281 ///
282 /// # Panics
283 /// Panics if `base` is less than 2 or greater than 36.
284 #[inline]
285 pub fn set_base(&mut self, base: u8) {
286 assert!(base >= 2);
287 assert!(base <= 36);
288 self.base = base;
289 }
290
291 /// Sets the rounding mode to be used in the conversion.
292 #[inline]
293 pub fn set_rounding_mode(&mut self, rm: RoundingMode) {
294 self.rounding_mode = rm;
295 }
296
297 #[cfg(feature = "test_build")]
298 pub fn is_valid(&self) -> bool {
299 (2..=36).contains(&self.base)
300 }
301}
302
303/// Iterators that generate [`SciSizeOptions`], [`ToSciOptions`], and [`FromSciStringOptions`]
304/// without repetition.
305pub mod exhaustive;
306#[cfg(feature = "random")]
307/// Iterators that generate [`SciSizeOptions`], [`ToSciOptions`], and [`FromSciStringOptions`]
308/// randomly.
309pub mod random;