[go: up one dir, main page]

rug/rational/
small.rs

1// Copyright © 2016–2025 Trevor Spiteri
2
3// This program is free software: you can redistribute it and/or modify it under
4// the terms of the GNU Lesser General Public License as published by the Free
5// Software Foundation, either version 3 of the License, or (at your option) any
6// later version.
7//
8// This program is distributed in the hope that it will be useful, but WITHOUT
9// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11// details.
12//
13// You should have received a copy of the GNU Lesser General Public License and
14// a copy of the GNU General Public License along with this program. If not, see
15// <https://www.gnu.org/licenses/>.
16
17#![allow(deprecated)]
18
19use crate::integer::{MiniInteger, ToSmall};
20use crate::{Assign, Rational};
21use core::fmt::{Debug, Formatter, Result as FmtResult};
22use core::marker::PhantomData;
23use core::ops::Deref;
24use gmp_mpfr_sys::gmp::limb_t;
25
26/**
27A small rational number that did not require any memory allocation until version 1.23.0.
28
29Because of a [soundness issue], this has been deprecated and replaced by
30[`MiniRational`]. To fix the soundness issue, this struct now uses allocations
31like [`Rational`] itself, so it is less efficient than [`MiniRational`].
32
33The `SmallRational` type can be coerced to a [`Rational`], as it implements
34<code>[Deref]\<[Target][Deref::Target] = [Rational]></code>.
35
36# Examples
37
38```rust
39#![allow(deprecated)]
40
41use rug::rational::SmallRational;
42use rug::Rational;
43// `a` requires a heap allocation
44let mut a = Rational::from((100, 13));
45// `b` can reside on the stack
46let b = SmallRational::from((-100, 21));
47a /= &*b;
48assert_eq!(*a.numer(), -21);
49assert_eq!(*a.denom(), 13);
50```
51
52[`MiniRational`]: crate::rational::MiniRational
53[soundness issue]: https://gitlab.com/tspiteri/rug/-/issues/52
54*/
55#[deprecated(since = "1.23.0", note = "use `MiniRational` instead")]
56#[derive(Clone)]
57pub struct SmallRational {
58    inner: Option<Rational>,
59    // for !Sync
60    phantom: PhantomData<*const limb_t>,
61}
62
63unsafe impl Send for SmallRational {}
64
65impl Default for SmallRational {
66    #[inline]
67    fn default() -> Self {
68        SmallRational::new()
69    }
70}
71
72impl Debug for SmallRational {
73    #[inline]
74    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
75        match &self.inner {
76            Some(r) => Debug::fmt(r, f),
77            None => Debug::fmt(Rational::ZERO, f),
78        }
79    }
80}
81
82impl SmallRational {
83    /// Creates a [`SmallRational`] with value 0.
84    ///
85    /// # Examples
86    ///
87    /// ```rust
88    /// #![allow(deprecated)]
89    ///
90    /// use rug::rational::SmallRational;
91    /// let r = SmallRational::new();
92    /// // Use r as if it were Rational.
93    /// assert_eq!(*r.numer(), 0);
94    /// assert_eq!(*r.denom(), 1);
95    /// ```
96    #[inline]
97    pub const fn new() -> Self {
98        SmallRational {
99            inner: None,
100            phantom: PhantomData,
101        }
102    }
103
104    /// Returns a mutable reference to a [`Rational`] number for simple
105    /// operations that do not need to allocate more space for the numerator or
106    /// denominator.
107    ///
108    /// # Safety
109    ///
110    /// It is undefined behavior to perform operations that reallocate the
111    /// internal data of the referenced [`Rational`] number or to swap it with
112    /// another number, although it is allowed to swap the numerator and
113    /// denominator allocations, such as in the reciprocal operation
114    /// [`recip_mut`].
115    ///
116    /// Some GMP functions swap the allocations of their target operands;
117    /// calling such functions with the mutable reference returned by this
118    /// method can lead to undefined behavior.
119    ///
120    /// # Examples
121    ///
122    /// ```rust
123    /// #![allow(deprecated)]
124    ///
125    /// use rug::rational::SmallRational;
126    /// let mut r = SmallRational::from((-15i32, 47i32));
127    /// let num_capacity = r.numer().capacity();
128    /// let den_capacity = r.denom().capacity();
129    /// // reciprocating this will not require reallocations
130    /// unsafe {
131    ///     r.as_nonreallocating_rational().recip_mut();
132    /// }
133    /// assert_eq!(*r, SmallRational::from((-47, 15)));
134    /// assert_eq!(r.numer().capacity(), num_capacity);
135    /// assert_eq!(r.denom().capacity(), den_capacity);
136    /// ```
137    ///
138    /// [`recip_mut`]: `Rational::recip_mut`
139    #[inline]
140    pub unsafe fn as_nonreallocating_rational(&mut self) -> &mut Rational {
141        if self.inner.is_none() {
142            *self = SmallRational {
143                inner: Some(Rational::new()),
144                phantom: PhantomData,
145            };
146        }
147        match &mut self.inner {
148            Some(r) => r,
149            None => unreachable!(),
150        }
151    }
152
153    /// Creates a [`SmallRational`] from a numerator and denominator, assuming
154    /// they are in canonical form.
155    ///
156    /// # Safety
157    ///
158    /// This method leads to undefined behavior if `den` is zero or if `num` and
159    /// `den` have common factors.
160    ///
161    /// # Examples
162    ///
163    /// ```rust
164    /// #![allow(deprecated)]
165    ///
166    /// use rug::rational::SmallRational;
167    /// let from_unsafe = unsafe { SmallRational::from_canonical(-13, 10) };
168    /// // from_safe is canonicalized to the same form as from_unsafe
169    /// let from_safe = SmallRational::from((130, -100));
170    /// assert_eq!(from_unsafe.numer(), from_safe.numer());
171    /// assert_eq!(from_unsafe.denom(), from_safe.denom());
172    /// ```
173    pub unsafe fn from_canonical<Num: ToSmall, Den: ToSmall>(num: Num, den: Den) -> Self {
174        let num = MiniInteger::from(num);
175        let den = MiniInteger::from(den);
176        SmallRational {
177            inner: Some(unsafe { Rational::from_canonical(num, den) }),
178            phantom: PhantomData,
179        }
180    }
181
182    /// Assigns a numerator and denominator to a [`SmallRational`], assuming
183    /// they are in canonical form.
184    ///
185    /// # Safety
186    ///
187    /// This method leads to undefined behavior if `den` is zero or negative, or
188    /// if `num` and `den` have common factors.
189    ///
190    /// # Examples
191    ///
192    /// ```rust
193    /// #![allow(deprecated)]
194    ///
195    /// use rug::rational::SmallRational;
196    /// use rug::Assign;
197    /// let mut a = SmallRational::new();
198    /// unsafe {
199    ///     a.assign_canonical(-13, 10);
200    /// }
201    /// // b is canonicalized to the same form as a
202    /// let mut b = SmallRational::new();
203    /// b.assign((130, -100));
204    /// assert_eq!(a.numer(), b.numer());
205    /// assert_eq!(a.denom(), b.denom());
206    /// ```
207    pub unsafe fn assign_canonical<Num: ToSmall, Den: ToSmall>(&mut self, num: Num, den: Den) {
208        let mut num = MiniInteger::from(num);
209        let mut den = MiniInteger::from(den);
210        unsafe {
211            self.as_nonreallocating_rational()
212                .assign_canonical(num.borrow_excl(), den.borrow_excl());
213        }
214    }
215}
216
217impl Deref for SmallRational {
218    type Target = Rational;
219    #[inline]
220    fn deref(&self) -> &Rational {
221        match &self.inner {
222            Some(r) => r,
223            None => Rational::ZERO,
224        }
225    }
226}
227
228impl<Num: ToSmall> Assign<Num> for SmallRational {
229    #[inline]
230    fn assign(&mut self, src: Num) {
231        let mut mini = MiniInteger::from(src);
232        unsafe {
233            self.as_nonreallocating_rational()
234                .assign(mini.borrow_excl());
235        }
236    }
237}
238
239impl<Num: ToSmall> From<Num> for SmallRational {
240    fn from(src: Num) -> Self {
241        let mut mini = MiniInteger::from(src);
242        SmallRational {
243            inner: Some(Rational::from(mini.borrow_excl())),
244            phantom: PhantomData,
245        }
246    }
247}
248
249impl<Num: ToSmall, Den: ToSmall> Assign<(Num, Den)> for SmallRational {
250    fn assign(&mut self, src: (Num, Den)) {
251        assert!(!src.1.is_zero(), "division by zero");
252        let mut num = MiniInteger::from(src.0);
253        let mut den = MiniInteger::from(src.1);
254        unsafe {
255            self.as_nonreallocating_rational()
256                .assign((num.borrow_excl(), den.borrow_excl()));
257        }
258    }
259}
260
261impl<Num: ToSmall, Den: ToSmall> From<(Num, Den)> for SmallRational {
262    fn from(src: (Num, Den)) -> Self {
263        assert!(!src.1.is_zero(), "division by zero");
264        let mut num = MiniInteger::from(src.0);
265        let mut den = MiniInteger::from(src.1);
266        SmallRational {
267            inner: Some(Rational::from((num.borrow_excl(), den.borrow_excl()))),
268            phantom: PhantomData,
269        }
270    }
271}
272
273impl Assign<&Self> for SmallRational {
274    #[inline]
275    fn assign(&mut self, other: &Self) {
276        self.clone_from(other);
277    }
278}
279
280impl Assign for SmallRational {
281    #[inline]
282    fn assign(&mut self, other: Self) {
283        *self = other;
284    }
285}
286
287#[cfg(test)]
288mod tests {
289    use crate::Assign;
290    use crate::rational::SmallRational;
291
292    #[test]
293    fn check_assign() {
294        let mut r = SmallRational::from((1, 2));
295        assert_eq!(*r, SmallRational::from((1, 2)));
296        r.assign(3);
297        assert_eq!(*r, 3);
298        let other = SmallRational::from((4, 5));
299        r.assign(&other);
300        assert_eq!(*r, SmallRational::from((4, 5)));
301        r.assign((6, 7));
302        assert_eq!(*r, SmallRational::from((6, 7)));
303        r.assign(other);
304        assert_eq!(*r, SmallRational::from((4, 5)));
305    }
306
307    fn swapped_parts(small: &SmallRational) -> bool {
308        unsafe {
309            let num = (*small.numer().as_raw()).d;
310            let den = (*small.denom().as_raw()).d;
311            num > den
312        }
313    }
314
315    #[test]
316    fn check_swapped_parts() {
317        let mut r = SmallRational::from((2, 3));
318        assert_eq!(*r, SmallRational::from((2, 3)));
319        assert_eq!(*r.clone(), *r);
320        let mut orig_swapped_parts = swapped_parts(&r);
321        unsafe {
322            r.as_nonreallocating_rational().recip_mut();
323        }
324        assert_eq!(*r, SmallRational::from((3, 2)));
325        assert_eq!(*r.clone(), *r);
326        assert!(swapped_parts(&r) != orig_swapped_parts);
327
328        unsafe {
329            r.assign_canonical(5, 7);
330        }
331        assert_eq!(*r, SmallRational::from((5, 7)));
332        assert_eq!(*r.clone(), *r);
333        orig_swapped_parts = swapped_parts(&r);
334        unsafe {
335            r.as_nonreallocating_rational().recip_mut();
336        }
337        assert_eq!(*r, SmallRational::from((7, 5)));
338        assert_eq!(*r.clone(), *r);
339        assert!(swapped_parts(&r) != orig_swapped_parts);
340
341        r.assign(2);
342        assert_eq!(*r, 2);
343        assert_eq!(*r.clone(), *r);
344        orig_swapped_parts = swapped_parts(&r);
345        unsafe {
346            r.as_nonreallocating_rational().recip_mut();
347        }
348        assert_eq!(*r, SmallRational::from((1, 2)));
349        assert_eq!(*r.clone(), *r);
350        assert!(swapped_parts(&r) != orig_swapped_parts);
351
352        r.assign((3, -5));
353        assert_eq!(*r, SmallRational::from((-3, 5)));
354        assert_eq!(*r.clone(), *r);
355        orig_swapped_parts = swapped_parts(&r);
356        unsafe {
357            r.as_nonreallocating_rational().recip_mut();
358        }
359        assert_eq!(*r, SmallRational::from((-5, 3)));
360        assert_eq!(*r.clone(), *r);
361        assert!(swapped_parts(&r) != orig_swapped_parts);
362    }
363}