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;