[go: up one dir, main page]

rug/complex/
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::complex::{BorrowComplex, MiniComplex};
20use crate::float::{MiniFloat, Special, ToSmall};
21use crate::{Assign, Complex};
22use core::fmt::{Debug, Formatter, Result as FmtResult};
23use core::marker::PhantomData;
24use core::mem;
25use core::ops::Deref;
26use gmp_mpfr_sys::gmp::limb_t;
27
28const ZERO_MINI: MiniComplex = MiniComplex::new();
29const ZERO_BORROW: BorrowComplex = ZERO_MINI.borrow();
30const ZERO: &Complex = BorrowComplex::const_deref(&ZERO_BORROW);
31
32/**
33A small complex number that did not require any memory allocation until version 1.23.0.
34
35Because of a [soundness issue], this has been deprecated and replaced by
36[`MiniComplex`]. To fix the soundness issue, this struct now uses allocations
37like [`Complex`] itself, so it is less efficient than [`MiniComplex`].
38
39The `SmallComplex` type can be coerced to a [`Complex`], as it implements
40<code>[Deref]\<[Target][Deref::Target] = [Complex]></code>.
41
42# Examples
43
44```rust
45#![allow(deprecated)]
46
47use rug::complex::SmallComplex;
48use rug::Complex;
49// `a` requires a heap allocation
50let mut a = Complex::with_val(53, (1, 2));
51// `b` can reside on the stack
52let b = SmallComplex::from((-10f64, -20.5f64));
53a += &*b;
54assert_eq!(*a.real(), -9);
55assert_eq!(*a.imag(), -18.5);
56```
57
58[soundness issue]: https://gitlab.com/tspiteri/rug/-/issues/52
59*/
60#[deprecated(since = "1.23.0", note = "use `MiniComplex` instead")]
61#[derive(Clone)]
62pub struct SmallComplex {
63    inner: Option<Complex>,
64    // for !Sync
65    phantom: PhantomData<*const limb_t>,
66}
67
68unsafe impl Send for SmallComplex {}
69
70impl Default for SmallComplex {
71    #[inline]
72    fn default() -> Self {
73        SmallComplex::new()
74    }
75}
76
77impl Debug for SmallComplex {
78    #[inline]
79    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
80        match &self.inner {
81            Some(c) => Debug::fmt(c, f),
82            None => Debug::fmt(ZERO, f),
83        }
84    }
85}
86
87impl SmallComplex {
88    /// Creates a [`SmallComplex`] with value 0 and the [minimum possible
89    /// precision][crate::float::prec_min].
90    ///
91    /// # Examples
92    ///
93    /// ```rust
94    /// #![allow(deprecated)]
95    ///
96    /// use rug::complex::SmallComplex;
97    /// let c = SmallComplex::new();
98    /// // Borrow c as if it were Complex.
99    /// assert_eq!(*c, 0);
100    /// ```
101    #[inline]
102    pub const fn new() -> Self {
103        SmallComplex {
104            inner: None,
105            phantom: PhantomData,
106        }
107    }
108
109    /// Returns a mutable reference to a [`Complex`] number for simple
110    /// operations that do not need to change the precision of the real or
111    /// imaginary part.
112    ///
113    /// # Safety
114    ///
115    /// It is undefined behavior to modify the precision of the referenced
116    /// [`Complex`] number or to swap it with another number.
117    ///
118    /// # Examples
119    ///
120    /// ```rust
121    /// #![allow(deprecated)]
122    ///
123    /// use rug::complex::SmallComplex;
124    /// let mut c = SmallComplex::from((1.0f32, 3.0f32));
125    /// // rotation does not change the precision
126    /// unsafe {
127    ///     c.as_nonreallocating_complex().mul_i_mut(false);
128    /// }
129    /// assert_eq!(*c, (-3.0, 1.0));
130    /// ```
131    #[inline]
132    pub unsafe fn as_nonreallocating_complex(&mut self) -> &mut Complex {
133        if self.inner.is_none() {
134            *self = SmallComplex {
135                inner: Some(Complex::new(ZERO.prec())),
136                phantom: PhantomData,
137            };
138        }
139        match &mut self.inner {
140            Some(c) => c,
141            None => unreachable!(),
142        }
143    }
144}
145
146impl Deref for SmallComplex {
147    type Target = Complex;
148    #[inline]
149    fn deref(&self) -> &Complex {
150        match &self.inner {
151            Some(c) => c,
152            None => ZERO,
153        }
154    }
155}
156
157impl<Re: ToSmall> Assign<Re> for SmallComplex {
158    fn assign(&mut self, src: Re) {
159        let mut mini = MiniFloat::from(src);
160        let src = mini.borrow_excl();
161        unsafe {
162            let dst = self.as_nonreallocating_complex();
163            dst.mut_real().set_prec(src.prec());
164            dst.mut_real().assign(src);
165            dst.mut_imag().set_prec(src.prec());
166            dst.mut_imag().assign(Special::Zero);
167        }
168    }
169}
170
171impl<Re: ToSmall> From<Re> for SmallComplex {
172    fn from(src: Re) -> Self {
173        let mut mini = MiniFloat::from(src);
174        let src = mini.borrow_excl();
175        SmallComplex {
176            inner: Some(Complex::with_val(src.prec(), src)),
177            phantom: PhantomData,
178        }
179    }
180}
181
182impl<Re: ToSmall, Im: ToSmall> Assign<(Re, Im)> for SmallComplex {
183    fn assign(&mut self, src: (Re, Im)) {
184        let mut re = MiniFloat::from(src.0);
185        let mut im = MiniFloat::from(src.1);
186        let re = re.borrow_excl();
187        let im = im.borrow_excl();
188        unsafe {
189            let dst = self.as_nonreallocating_complex();
190            dst.mut_real().set_prec(re.prec());
191            dst.mut_real().assign(re);
192            dst.mut_imag().set_prec(im.prec());
193            dst.mut_imag().assign(im);
194        }
195    }
196}
197
198impl<Re: ToSmall, Im: ToSmall> From<(Re, Im)> for SmallComplex {
199    fn from(src: (Re, Im)) -> Self {
200        let mut re = MiniFloat::from(src.0);
201        let mut im = MiniFloat::from(src.1);
202        let re = re.borrow_excl();
203        let im = im.borrow_excl();
204        SmallComplex {
205            inner: Some(Complex::with_val((re.prec(), im.prec()), (re, im))),
206            phantom: PhantomData,
207        }
208    }
209}
210
211impl Assign<&Self> for SmallComplex {
212    #[inline]
213    fn assign(&mut self, other: &Self) {
214        self.clone_from(other);
215    }
216}
217
218impl Assign for SmallComplex {
219    #[inline]
220    fn assign(&mut self, other: Self) {
221        drop(mem::replace(self, other));
222    }
223}
224
225#[cfg(test)]
226mod tests {
227    use crate::Assign;
228    use crate::complex::SmallComplex;
229    use crate::float;
230    use crate::float::FreeCache;
231
232    #[test]
233    fn check_assign() {
234        let mut c = SmallComplex::from((1.0, 2.0));
235        assert_eq!(*c, (1.0, 2.0));
236        c.assign(3.0);
237        assert_eq!(*c, (3.0, 0.0));
238        let other = SmallComplex::from((4.0, 5.0));
239        c.assign(&other);
240        assert_eq!(*c, (4.0, 5.0));
241        c.assign((6.0, 7.0));
242        assert_eq!(*c, (6.0, 7.0));
243        c.assign(other);
244        assert_eq!(*c, (4.0, 5.0));
245
246        float::free_cache(FreeCache::All);
247    }
248
249    fn swapped_parts(small: &SmallComplex) -> bool {
250        unsafe {
251            let re = (*small.real().as_raw()).d;
252            let im = (*small.imag().as_raw()).d;
253            re > im
254        }
255    }
256
257    #[test]
258    fn check_swapped_parts() {
259        let mut c = SmallComplex::from((1, 2));
260        assert_eq!(*c, (1, 2));
261        assert_eq!(*c.clone(), *c);
262        let mut orig_swapped_parts = swapped_parts(&c);
263        unsafe {
264            c.as_nonreallocating_complex().mul_i_mut(false);
265        }
266        assert_eq!(*c, (-2, 1));
267        assert_eq!(*c.clone(), *c);
268        assert!(swapped_parts(&c) != orig_swapped_parts);
269
270        c.assign(12);
271        assert_eq!(*c, 12);
272        assert_eq!(*c.clone(), *c);
273        orig_swapped_parts = swapped_parts(&c);
274        unsafe {
275            c.as_nonreallocating_complex().mul_i_mut(false);
276        }
277        assert_eq!(*c, (0, 12));
278        assert_eq!(*c.clone(), *c);
279        assert!(swapped_parts(&c) != orig_swapped_parts);
280
281        c.assign((4, 5));
282        assert_eq!(*c, (4, 5));
283        assert_eq!(*c.clone(), *c);
284        orig_swapped_parts = swapped_parts(&c);
285        unsafe {
286            c.as_nonreallocating_complex().mul_i_mut(false);
287        }
288        assert_eq!(*c, (-5, 4));
289        assert_eq!(*c.clone(), *c);
290        assert!(swapped_parts(&c) != orig_swapped_parts);
291    }
292}