Skip to content

Commit 25643af

Browse files
committed
Switch to associated type and remove free functions
We swich `HasAngle` to use an associated type to get rid of generics. This unfortunately stops us from using blanket implementations, so we implement just for f32 and f64 and their respective complex types.
1 parent 213712e commit 25643af

3 files changed

Lines changed: 47 additions & 140 deletions

File tree

src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1849,8 +1849,7 @@ mod impl_2d;
18491849
mod impl_dyn;
18501850

18511851
mod numeric;
1852-
#[cfg(feature = "std")]
1853-
pub use crate::numeric::{angle, angle_scalar, HasAngle};
1852+
pub use crate::numeric::HasAngle;
18541853

18551854
pub mod linalg;
18561855

src/numeric/impl_float_maths.rs

Lines changed: 45 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,61 @@
11
// Element-wise methods for ndarray
22

3-
#[cfg(feature = "std")]
4-
use num_complex::Complex;
3+
use num_complex::{Complex32, Complex64};
54
#[cfg(feature = "std")]
65
use num_traits::Float;
76

87
use crate::imp_prelude::*;
98

109
/// Trait for types that can generalize the phase angle (argument)
1110
/// calculation for both real floats and complex numbers.
12-
#[cfg(feature = "std")]
13-
pub trait HasAngle<F: Float>
11+
pub trait HasAngle
1412
{
13+
/// The type of the associated angle.
14+
type Angle;
15+
1516
/// Return the phase angle (argument) of the value
16-
fn to_angle(&self) -> F;
17+
fn to_angle(&self) -> Self::Angle;
1718
}
1819

19-
#[cfg(feature = "std")]
20-
impl<F> HasAngle<F> for F
21-
where F: Float
20+
impl HasAngle for f32
2221
{
22+
type Angle = Self;
23+
2324
#[inline]
24-
fn to_angle(&self) -> F
25+
fn to_angle(&self) -> Self
2526
{
26-
F::zero().atan2(*self)
27+
0f32.atan2(*self)
2728
}
2829
}
2930

30-
#[cfg(feature = "std")]
31-
impl<F> HasAngle<F> for Complex<F>
32-
where F: Float
31+
impl HasAngle for f64
32+
{
33+
type Angle = Self;
34+
35+
#[inline]
36+
fn to_angle(&self) -> Self
37+
{
38+
0f64.atan2(*self)
39+
}
40+
}
41+
42+
impl HasAngle for Complex32
43+
{
44+
type Angle = f32;
45+
46+
#[inline]
47+
fn to_angle(&self) -> Self::Angle
48+
{
49+
self.im.atan2(self.re)
50+
}
51+
}
52+
53+
impl HasAngle for Complex64
3354
{
55+
type Angle = f64;
56+
3457
#[inline]
35-
fn to_angle(&self) -> F
58+
fn to_angle(&self) -> Self::Angle
3659
{
3760
self.im.atan2(self.re)
3861
}
@@ -203,10 +226,10 @@ where
203226
/// # Angle calculation methods for arrays
204227
///
205228
/// Methods for calculating phase angles of complex values in arrays.
206-
#[cfg(feature = "std")]
207-
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
208229
impl<A, D> ArrayRef<A, D>
209-
where D: Dimension
230+
where
231+
D: Dimension,
232+
A: HasAngle,
210233
{
211234
/// Return the [phase angle (argument)](https://en.wikipedia.org/wiki/Argument_(complex_analysis)) of complex values in the array.
212235
///
@@ -241,10 +264,9 @@ where D: Dimension
241264
/// assert!((angles[2] - PI/4.0).abs() < 1e-10);
242265
/// ```
243266
#[must_use = "method returns a new array and does not mutate the original value"]
244-
pub fn angle<F: Float>(&self) -> Array<F, D>
245-
where A: HasAngle<F> + Clone
267+
pub fn angle(&self) -> Array<A::Angle, D>
246268
{
247-
self.mapv(|f| A::to_angle(&f))
269+
self.map(A::to_angle)
248270
}
249271
}
250272

@@ -273,77 +295,9 @@ where
273295
}
274296
}
275297

276-
/// Scalar convenience function for angle calculation.
277-
///
278-
/// Calculate the [phase angle (argument)](https://en.wikipedia.org/wiki/Argument_(complex_analysis)) of a single complex value.
279-
///
280-
/// # Arguments
281-
///
282-
/// * `z` - A real or complex value (f32/f64, `Complex<f32>`/`Complex<f64>`).
283-
///
284-
/// # Returns
285-
///
286-
/// The phase angle as `f64` in radians or degrees.
287-
///
288-
/// # Examples
289-
///
290-
/// ```
291-
/// use num_complex::Complex;
292-
/// use std::f64::consts::PI;
293-
///
294-
/// assert!((ndarray::angle_scalar(Complex::new(1.0f64, 1.0)) - PI/4.0).abs() < 1e-10);
295-
/// assert!((ndarray::angle_scalar(1.0f32) - 0.0).abs() < 1e-10);
296-
/// assert!((ndarray::angle_scalar(-1.0f32) - 180.0).abs() < 1e-10);
297-
/// ```
298-
#[cfg(feature = "std")]
299-
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
300-
pub fn angle_scalar<F: Float, T: HasAngle<F>>(z: T) -> F
301-
{
302-
z.to_angle()
303-
}
304-
305-
/// Calculate the phase angle of complex values.
306-
///
307-
/// # Arguments
308-
///
309-
/// * `z` - Array of real or complex values.
310-
///
311-
/// # Returns
312-
///
313-
/// An `Array<F, D>` with the same shape as `z`.
314-
///
315-
/// # Examples
316-
///
317-
/// ```
318-
/// use ndarray::array;
319-
/// use num_complex::Complex;
320-
///
321-
/// // f32 precision for complex numbers
322-
/// let z32 = array![Complex::new(0.0f32, 1.0)];
323-
/// let out32 = ndarray::angle(&z32);
324-
/// // out32 has type Array<f32, _>
325-
///
326-
/// // f64 precision for complex numbers
327-
/// let z64 = array![Complex::new(0.0f64, -1.0)];
328-
/// let out64 = ndarray::angle(&z64);
329-
/// // out64 has type Array<f64, _>
330-
/// ```
331-
#[cfg(feature = "std")]
332-
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
333-
pub fn angle<A, F, S, D>(z: &ArrayBase<S, D>) -> Array<F, D>
334-
where
335-
A: HasAngle<F>,
336-
F: Float,
337-
S: Data<Elem = A>,
338-
D: Dimension,
339-
{
340-
z.map(HasAngle::to_angle)
341-
}
342-
343298
#[cfg(all(test, feature = "std"))]
344299
mod angle_tests
345300
{
346-
use super::*;
347301
use crate::Array;
348302
use num_complex::Complex;
349303
use std::f64::consts::PI;
@@ -392,7 +346,7 @@ mod angle_tests
392346
fn test_complex_numbers_radians()
393347
{
394348
let arr = Array::from_vec(vec![
395-
Complex::new(1.0, 0.0), // 0
349+
Complex::new(1.0f64, 0.0), // 0
396350
Complex::new(0.0, 1.0), // π/2
397351
Complex::new(-1.0, 0.0), // π
398352
Complex::new(0.0, -1.0), // -π/2
@@ -413,7 +367,7 @@ mod angle_tests
413367
fn test_complex_numbers_degrees()
414368
{
415369
let arr = Array::from_vec(vec![
416-
Complex::new(1.0, 0.0),
370+
Complex::new(1.0f64, 0.0),
417371
Complex::new(0.0, 1.0),
418372
Complex::new(-1.0, 0.0),
419373
Complex::new(1.0, 1.0),
@@ -430,7 +384,7 @@ mod angle_tests
430384
fn test_signed_zeros()
431385
{
432386
let arr = Array::from_vec(vec![
433-
Complex::new(0.0, 0.0),
387+
Complex::new(0.0f64, 0.0),
434388
Complex::new(-0.0, 0.0),
435389
Complex::new(0.0, -0.0),
436390
Complex::new(-0.0, -0.0),
@@ -443,51 +397,6 @@ mod angle_tests
443397
assert_approx_eq!(a[3], -PI, 1e-10);
444398
}
445399

446-
#[test]
447-
fn test_angle_scalar_f64()
448-
{
449-
assert_approx_eq!(angle_scalar(Complex::new(1.0, 1.0)), PI / 4.0, 1e-10);
450-
assert_approx_eq!(angle_scalar(1.0f64), 0.0, 1e-10);
451-
assert_approx_eq!(angle_scalar(-1.0f64), PI, 1e-10);
452-
}
453-
454-
#[test]
455-
fn test_angle_scalar_f32()
456-
{
457-
use std::f32::consts::FRAC_PI_4;
458-
assert_approx_eq!(angle_scalar(Complex::new(1.0f32, 1.0)), FRAC_PI_4, 1e-6);
459-
assert_approx_eq!(angle_scalar(1.0f32), 0.0, 1e-6);
460-
assert_approx_eq!(angle_scalar(-1.0f32), std::f32::consts::PI, 1e-6);
461-
}
462-
463-
#[test]
464-
fn test_angle_function()
465-
{
466-
let arr = Array::from_vec(vec![
467-
Complex::new(1.0, 0.0),
468-
Complex::new(0.0, 1.0),
469-
Complex::new(-1.0, 1.0),
470-
]);
471-
let a = angle(&arr);
472-
473-
assert_approx_eq!(a[0], 0.0, 1e-10);
474-
assert_approx_eq!(a[1], PI / 2.0, 1e-10);
475-
assert_approx_eq!(a[2], 3.0 * PI / 4.0, 1e-10);
476-
}
477-
478-
#[test]
479-
fn test_angle_function_degrees()
480-
{
481-
let arr = Array::from_vec(vec![
482-
Complex::new(1.0f32, 1.0),
483-
Complex::new(-1.0f32, 0.0),
484-
]);
485-
let a = angle(&arr);
486-
487-
assert_approx_eq!(a[0], 45.0f32.to_radians(), 1e-6);
488-
assert_approx_eq!(a[1], 180.0f32.to_radians(), 1e-6);
489-
}
490-
491400
#[test]
492401
fn test_edge_cases()
493402
{

src/numeric/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@ mod impl_numeric;
22

33
mod impl_float_maths;
44

5-
#[cfg(feature = "std")]
6-
pub use self::impl_float_maths::{angle, angle_scalar, HasAngle};
5+
pub use self::impl_float_maths::HasAngle;

0 commit comments

Comments
 (0)