malachite_q/
lib.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
9//! This crate defines [`Rational`]s. The name of this crate refers to the mathematical symbol for
10//! rational numbers, $$\mathbb{Q}$$.
11//! - There are many functions defined on [`Rational`]s.
12//!   These include
13//!   - All the ones you'd expect, like addition, subtraction, multiplication, and division;
14//!   - Functions related to conversion between [`Rational`]s and other kinds of numbers, including
15//!     primitive floats;
16//!   - Functions for Diophantine approximation;
17//!   - Functions for expressing [`Rational`]s in scientific notation.
18//! - The numerators and denominators of [`Rational`]s are stored as [`Natural`]s, so [`Rational`]s
19//!   with small numerators and denominators can be stored entirely on the stack.
20//! - Most arithmetic involving [`Rational`]s requires (automatically) reducing the numerator and
21//!   denominator. This is done very efficiently by using the high performance GCD and exact
22//!   division algorithms implemented by [`Natural`]s.
23//!
24//! # Demos and benchmarks
25//! This crate comes with a `bin` target that can be used for running demos and benchmarks.
26//! - Almost all of the public functions in this crate have an associated demo. Running a demo
27//!   shows you a function's behavior on a large number of inputs. For example, to demo
28//!   [`Rational`] addition, you can use the following command:
29//!   ```text
30//!   cargo run --features bin_build --release -- -l 10000 -m exhaustive -d demo_rational_add
31//!   ```
32//!   This command uses the `exhaustive` mode, which generates every possible input, generally
33//!   starting with the simplest input and progressing to more complex ones. Another mode is
34//!   `random`. The `-l` flag specifies how many inputs should be generated.
35//! - You can use a similar command to run benchmarks. The following command benchmarks various
36//!   addition algorithms:
37//!   ```text
38//!   cargo run --features bin_build --release -- -l 1000000 -m random -b \
39//!       benchmark_rational_add_algorithms -o add-bench.gp
40//!   ```
41//!   or addition implementations of other libraries:
42//!   ```text
43//!   cargo run --features bin_build --release -- -l 1000000 -m random -b \
44//!       benchmark_rational_add_assign_library_comparison -o add-bench.gp
45//!   ```
46//!   This creates a file called gcd-bench.gp. You can use gnuplot to create an SVG from it like
47//!   so:
48//!   ```text
49//!   gnuplot -e "set terminal svg; l \"gcd-bench.gp\"" > gcd-bench.svg
50//!   ```
51//!
52//! The list of available demos and benchmarks is not documented anywhere; you must find them by
53//! browsing through
54//! [`bin_util/demo_and_bench`](https://github.com/mhogrefe/malachite/tree/master/malachite-q/src/bin_util/demo_and_bench).
55//!
56//! # Features
57//! - `32_bit_limbs`: Sets the type of [`Limb`](malachite_nz#limbs) to [`u32`] instead of the
58//!   default, [`u64`].
59//! - `test_build`: A large proportion of the code in this crate is only used for testing. For a
60//!   typical user, building this code would result in an unnecessarily long compilation time and
61//!   an unnecessarily large binary. My solution is to only build this code when the `test_build`
62//!   feature is enabled. If you want to run unit tests, you must enable `test_build`. However,
63//!   doctests don't require it, since they only test the public interface.
64//! - `bin_build`: This feature is used to build the code for demos and benchmarks, which also
65//!   takes a long time to build. Enabling this feature also enables `test_build`.
66
67#![allow(
68    unstable_name_collisions,
69    clippy::assertions_on_constants,
70    clippy::cognitive_complexity,
71    clippy::many_single_char_names,
72    clippy::range_plus_one,
73    clippy::suspicious_arithmetic_impl,
74    clippy::suspicious_op_assign_impl,
75    clippy::too_many_arguments,
76    clippy::type_complexity,
77    clippy::upper_case_acronyms,
78    clippy::multiple_bound_locations
79)]
80#![warn(
81    clippy::cast_lossless,
82    clippy::explicit_into_iter_loop,
83    clippy::explicit_iter_loop,
84    clippy::filter_map_next,
85    clippy::large_digit_groups,
86    clippy::manual_filter_map,
87    clippy::manual_find_map,
88    clippy::map_flatten,
89    clippy::map_unwrap_or,
90    clippy::match_same_arms,
91    clippy::missing_const_for_fn,
92    clippy::mut_mut,
93    clippy::needless_borrow,
94    clippy::needless_continue,
95    clippy::needless_pass_by_value,
96    clippy::print_stdout,
97    clippy::redundant_closure_for_method_calls,
98    clippy::single_match_else,
99    clippy::trait_duplication_in_bounds,
100    clippy::type_repetition_in_bounds,
101    clippy::uninlined_format_args,
102    clippy::unused_self,
103    clippy::if_not_else,
104    clippy::manual_assert,
105    clippy::range_plus_one,
106    clippy::redundant_else,
107    clippy::semicolon_if_nothing_returned,
108    clippy::cloned_instead_of_copied,
109    clippy::flat_map_option,
110    clippy::unnecessary_wraps,
111    clippy::unnested_or_patterns,
112    clippy::use_self,
113    clippy::trivially_copy_pass_by_ref
114)]
115#![cfg_attr(
116    not(any(feature = "test_build", feature = "random", feature = "std")),
117    no_std
118)]
119
120extern crate alloc;
121
122#[macro_use]
123extern crate malachite_base;
124extern crate malachite_nz;
125#[cfg(feature = "serde")]
126#[macro_use]
127extern crate serde;
128
129#[cfg(feature = "test_build")]
130extern crate itertools;
131#[cfg(feature = "test_build")]
132extern crate num;
133#[cfg(feature = "test_build")]
134extern crate rug;
135
136use malachite_base::named::Named;
137#[cfg(feature = "test_build")]
138use malachite_base::num::arithmetic::traits::CoprimeWith;
139use malachite_base::num::basic::traits::{NegativeOne, One, OneHalf, Two, Zero};
140use malachite_base::num::logic::traits::SignificantBits;
141use malachite_nz::natural::Natural;
142
143/// A rational number.
144///
145/// `Rational`s whose numerator and denominator have 64 significant bits or fewer can be represented
146/// without any memory allocation. (Unless Malachite is compiled with `32_bit_limbs`, in which case
147/// the limit is 32).
148#[derive(Clone, Hash, Eq, PartialEq)]
149#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
150pub struct Rational {
151    // whether the `Rational` is non-negative
152    #[cfg_attr(feature = "serde", serde(rename = "s"))]
153    pub(crate) sign: bool,
154    #[cfg_attr(feature = "serde", serde(rename = "n"))]
155    pub(crate) numerator: Natural,
156    #[cfg_attr(feature = "serde", serde(rename = "d"))]
157    pub(crate) denominator: Natural,
158}
159
160impl Rational {
161    // Returns true iff `self` is valid.
162    //
163    // To be valid, its denominator must be nonzero, its numerator and denominator must be
164    // relatively prime, and if its numerator is zero, then `sign` must be `true`. All `Rational`s
165    // must be valid.
166    #[cfg(feature = "test_build")]
167    pub fn is_valid(&self) -> bool {
168        self.denominator != 0
169            && (self.sign || self.numerator != 0)
170            && (&self.numerator).coprime_with(&self.denominator)
171    }
172}
173
174impl SignificantBits for &Rational {
175    /// Returns the sum of the bits needed to represent the numerator and denominator.
176    ///
177    /// # Worst-case complexity
178    /// Constant time and additional memory.
179    ///
180    /// # Examples
181    /// ```
182    /// use malachite_base::num::basic::traits::Zero;
183    /// use malachite_base::num::logic::traits::SignificantBits;
184    /// use malachite_q::Rational;
185    /// use std::str::FromStr;
186    ///
187    /// assert_eq!(Rational::ZERO.significant_bits(), 1);
188    /// assert_eq!(
189    ///     Rational::from_str("-100/101").unwrap().significant_bits(),
190    ///     14
191    /// );
192    /// ```
193    fn significant_bits(self) -> u64 {
194        self.numerator.significant_bits() + self.denominator.significant_bits()
195    }
196}
197
198/// The constant 0.
199impl Zero for Rational {
200    const ZERO: Self = Self {
201        sign: true,
202        numerator: Natural::ZERO,
203        denominator: Natural::ONE,
204    };
205}
206
207/// The constant 1.
208impl One for Rational {
209    const ONE: Self = Self {
210        sign: true,
211        numerator: Natural::ONE,
212        denominator: Natural::ONE,
213    };
214}
215
216/// The constant 2.
217impl Two for Rational {
218    const TWO: Self = Self {
219        sign: true,
220        numerator: Natural::TWO,
221        denominator: Natural::ONE,
222    };
223}
224
225/// The constant -1.
226impl NegativeOne for Rational {
227    const NEGATIVE_ONE: Self = Self {
228        sign: false,
229        numerator: Natural::ONE,
230        denominator: Natural::ONE,
231    };
232}
233
234/// The constant 1/2.
235impl OneHalf for Rational {
236    const ONE_HALF: Self = Self {
237        sign: true,
238        numerator: Natural::ONE,
239        denominator: Natural::TWO,
240    };
241}
242
243impl Default for Rational {
244    /// The default value of a [`Rational`], 0.
245    fn default() -> Self {
246        Self::ZERO
247    }
248}
249
250// Implements `Named` for `Rational`.
251impl_named!(Rational);
252
253/// Traits for arithmetic.
254pub mod arithmetic;
255/// Traits for comparing [`Rational`]s for equality or order.
256pub mod comparison;
257/// Traits for converting to and from [`Rational`]s, converting to and from strings, and extracting
258/// digits and continued fractions.
259pub mod conversion;
260/// Iterators that generate [`Rational`]s without repetition.
261pub mod exhaustive;
262#[cfg(feature = "random")]
263/// Iterators that generate [`Rational`]s randomly.
264pub mod random;
265
266#[cfg(feature = "test_build")]
267pub mod test_util;