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)]
79#![warn(
80 clippy::cast_lossless,
81 clippy::explicit_into_iter_loop,
82 clippy::explicit_iter_loop,
83 clippy::filter_map_next,
84 clippy::large_digit_groups,
85 clippy::manual_filter_map,
86 clippy::manual_find_map,
87 clippy::map_flatten,
88 clippy::map_unwrap_or,
89 clippy::match_same_arms,
90 clippy::missing_const_for_fn,
91 clippy::mut_mut,
92 clippy::needless_borrow,
93 clippy::needless_continue,
94 clippy::needless_pass_by_value,
95 clippy::print_stdout,
96 clippy::redundant_closure_for_method_calls,
97 clippy::single_match_else,
98 clippy::trait_duplication_in_bounds,
99 clippy::type_repetition_in_bounds,
100 clippy::uninlined_format_args,
101 clippy::unused_self,
102 clippy::if_not_else,
103 clippy::manual_assert,
104 clippy::range_plus_one,
105 clippy::redundant_else,
106 clippy::semicolon_if_nothing_returned,
107 clippy::cloned_instead_of_copied,
108 clippy::flat_map_option,
109 clippy::unnecessary_wraps,
110 clippy::unnested_or_patterns,
111 clippy::trivially_copy_pass_by_ref
112)]
113#![cfg_attr(
114 not(any(feature = "test_build", feature = "random", feature = "std")),
115 no_std
116)]
117
118extern crate alloc;
119
120#[macro_use]
121extern crate malachite_base;
122extern crate malachite_nz;
123#[cfg(feature = "serde")]
124#[macro_use]
125extern crate serde;
126
127#[cfg(feature = "test_build")]
128extern crate itertools;
129#[cfg(feature = "test_build")]
130extern crate num;
131#[cfg(feature = "test_build")]
132extern crate rug;
133
134use malachite_base::named::Named;
135#[cfg(feature = "test_build")]
136use malachite_base::num::arithmetic::traits::CoprimeWith;
137use malachite_base::num::basic::traits::{NegativeOne, One, OneHalf, Two, Zero};
138use malachite_base::num::logic::traits::SignificantBits;
139use malachite_nz::natural::Natural;
140
141/// A rational number.
142///
143/// `Rational`s whose numerator and denominator have 64 significant bits or fewer can be represented
144/// without any memory allocation. (Unless Malachite is compiled with `32_bit_limbs`, in which case
145/// the limit is 32).
146#[derive(Clone, Hash, Eq, PartialEq)]
147#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
148pub struct Rational {
149 // whether the `Rational` is non-negative
150 #[cfg_attr(feature = "serde", serde(rename = "s"))]
151 pub(crate) sign: bool,
152 #[cfg_attr(feature = "serde", serde(rename = "n"))]
153 pub(crate) numerator: Natural,
154 #[cfg_attr(feature = "serde", serde(rename = "d"))]
155 pub(crate) denominator: Natural,
156}
157
158impl Rational {
159 // Returns true iff `self` is valid.
160 //
161 // To be valid, its denominator must be nonzero, its numerator and denominator must be
162 // relatively prime, and if its numerator is zero, then `sign` must be `true`. All `Rational`s
163 // must be valid.
164 #[cfg(feature = "test_build")]
165 pub fn is_valid(&self) -> bool {
166 self.denominator != 0
167 && (self.sign || self.numerator != 0)
168 && (&self.numerator).coprime_with(&self.denominator)
169 }
170}
171
172impl SignificantBits for &Rational {
173 /// Returns the sum of the bits needed to represent the numerator and denominator.
174 ///
175 /// # Worst-case complexity
176 /// Constant time and additional memory.
177 ///
178 /// # Examples
179 /// ```
180 /// use malachite_base::num::basic::traits::Zero;
181 /// use malachite_base::num::logic::traits::SignificantBits;
182 /// use malachite_q::Rational;
183 /// use std::str::FromStr;
184 ///
185 /// assert_eq!(Rational::ZERO.significant_bits(), 1);
186 /// assert_eq!(
187 /// Rational::from_str("-100/101").unwrap().significant_bits(),
188 /// 14
189 /// );
190 /// ```
191 fn significant_bits(self) -> u64 {
192 self.numerator.significant_bits() + self.denominator.significant_bits()
193 }
194}
195
196/// The constant 0.
197impl Zero for Rational {
198 const ZERO: Rational = Rational {
199 sign: true,
200 numerator: Natural::ZERO,
201 denominator: Natural::ONE,
202 };
203}
204
205/// The constant 1.
206impl One for Rational {
207 const ONE: Rational = Rational {
208 sign: true,
209 numerator: Natural::ONE,
210 denominator: Natural::ONE,
211 };
212}
213
214/// The constant 2.
215impl Two for Rational {
216 const TWO: Rational = Rational {
217 sign: true,
218 numerator: Natural::TWO,
219 denominator: Natural::ONE,
220 };
221}
222
223/// The constant -1.
224impl NegativeOne for Rational {
225 const NEGATIVE_ONE: Rational = Rational {
226 sign: false,
227 numerator: Natural::ONE,
228 denominator: Natural::ONE,
229 };
230}
231
232/// The constant 1/2.
233impl OneHalf for Rational {
234 const ONE_HALF: Rational = Rational {
235 sign: true,
236 numerator: Natural::ONE,
237 denominator: Natural::TWO,
238 };
239}
240
241impl Default for Rational {
242 /// The default value of a [`Rational`], 0.
243 fn default() -> Rational {
244 Rational::ZERO
245 }
246}
247
248// Implements `Named` for `Rational`.
249impl_named!(Rational);
250
251/// Traits for arithmetic.
252pub mod arithmetic;
253/// Traits for comparing [`Rational`]s for equality or order.
254pub mod comparison;
255/// Traits for converting to and from [`Rational`]s, converting to and from strings, and extracting
256/// digits and continued fractions.
257pub mod conversion;
258/// Iterators that generate [`Rational`]s without repetition.
259pub mod exhaustive;
260#[cfg(feature = "random")]
261/// Iterators that generate [`Rational`]s randomly.
262pub mod random;
263
264#[cfg(feature = "test_build")]
265pub mod test_util;