extendr_api/scalar/
rcplx_full.rs

1use crate::scalar::macros::*;
2use crate::scalar::{Rfloat, Scalar};
3use crate::*;
4use extendr_ffi::{R_IsNA, R_NaReal, Rcomplex};
5use std::convert::TryFrom;
6use std::ops::{Add, Div, Mul, Neg, Sub};
7use std::ops::{AddAssign, DivAssign, MulAssign, SubAssign};
8
9#[allow(non_camel_case_types)]
10pub type c64 = num_complex::Complex<f64>;
11
12impl CanBeNA for c64 {
13    fn is_na(&self) -> bool {
14        unsafe { R_IsNA(self.re) != 0 }
15    }
16
17    fn na() -> c64 {
18        unsafe { c64::new(R_NaReal, R_NaReal) }
19    }
20}
21
22/// Rcplx is a wrapper for f64 in the context of an R's complex vector.
23///
24/// Rcplx has a special NA value, obtained from R headers via R_NaReal.
25///
26/// Rcplx has the same footprint as R's complex value allowing us to use it in zero copy slices.
27#[repr(transparent)]
28pub struct Rcplx(c64);
29
30impl Scalar<c64> for Rcplx {
31    fn inner(&self) -> c64 {
32        self.0
33    }
34
35    fn new(val: c64) -> Self {
36        Rcplx(val)
37    }
38}
39
40impl Rcplx {
41    pub fn new(re: f64, im: f64) -> Self {
42        Self(c64::new(re, im))
43    }
44
45    pub fn is_nan(&self) -> bool {
46        self.0.is_nan()
47    }
48
49    pub fn is_infinite(&self) -> bool {
50        self.0.is_infinite()
51    }
52
53    pub fn re(&self) -> Rfloat {
54        Rfloat::from(self.0.re)
55    }
56
57    pub fn im(&self) -> Rfloat {
58        Rfloat::from(self.0.im)
59    }
60}
61
62impl From<f64> for Rcplx {
63    fn from(val: f64) -> Self {
64        Rcplx(c64::from(val))
65    }
66}
67
68impl From<(f64, f64)> for Rcplx {
69    fn from(val: (f64, f64)) -> Self {
70        Rcplx(c64::new(val.0, val.1))
71    }
72}
73
74impl From<(Rfloat, Rfloat)> for Rcplx {
75    fn from(val: (Rfloat, Rfloat)) -> Self {
76        Rcplx(c64::new(val.0.inner(), val.1.inner()))
77    }
78}
79
80impl From<Rfloat> for Rcplx {
81    fn from(val: Rfloat) -> Self {
82        Rcplx(c64::from(val.inner()))
83    }
84}
85
86impl From<Rcomplex> for Rcplx {
87    fn from(val: Rcomplex) -> Self {
88        Rcplx(c64::new(val.r, val.i))
89    }
90}
91
92impl From<Rcplx> for Option<c64> {
93    fn from(val: Rcplx) -> Self {
94        if val.is_na() {
95            None
96        } else {
97            Some(c64::new(val.re().inner(), val.im().inner()))
98        }
99    }
100}
101
102// `NA_real_` is a `NaN` with specific bit representation.
103// Check that underlying `f64` is `NA_real_`.
104gen_trait_impl!(Rcplx, c64, |x: &Rcplx| x.inner().re.is_na(), c64::na());
105gen_from_primitive!(Rcplx, c64);
106// gen_from_scalar!(Rcplx, c64);
107gen_sum_iter!(Rcplx);
108
109// Generate binary ops for +, -, * and /
110gen_binop!(
111    Rcplx,
112    c64,
113    Add,
114    |lhs: c64, rhs: c64| Some(lhs + rhs),
115    "Add two Rcplx values or an option of c64."
116);
117gen_binop!(
118    Rcplx,
119    c64,
120    Sub,
121    |lhs: c64, rhs: c64| Some(lhs - rhs),
122    "Subtract two Rcplx values or an option of c64."
123);
124gen_binop!(
125    Rcplx,
126    c64,
127    Mul,
128    |lhs: c64, rhs: c64| Some(lhs * rhs),
129    "Multiply two Rcplx values or an option of c64."
130);
131gen_binop!(
132    Rcplx,
133    c64,
134    Div,
135    |lhs: c64, rhs: c64| Some(lhs / rhs),
136    "Divide two Rcplx values or an option of c64."
137);
138gen_binopassign!(
139    Rcplx,
140    c64,
141    AddAssign,
142    |lhs: c64, rhs: c64| Some(lhs + rhs),
143    "Add two Rcplx values or an option of c64, modifying the left-hand side in place. Overflows to NA."
144);
145gen_binopassign!(
146    Rcplx,
147    c64,
148    SubAssign,
149    |lhs: c64, rhs: c64| Some(lhs - rhs),
150    "Subtract two Rcplx values or an option of c64, modifying the left-hand side in place. Overflows to NA."
151);
152gen_binopassign!(
153    Rcplx,
154    c64,
155    MulAssign,
156    |lhs: c64, rhs: c64| Some(lhs * rhs),
157    "Multiply two Rcplx values or an option of c64, modifying the left-hand side in place. Overflows to NA."
158);
159gen_binopassign!(
160    Rcplx,
161    c64,
162    DivAssign,
163    |lhs: c64, rhs: c64| Some(lhs / rhs),
164    "Divide two Rcplx values or an option of c64, modifying the left-hand side in place. Overflows to NA."
165);
166
167// Generate unary ops for -, !
168gen_unop!(Rcplx, Neg, |lhs: c64| Some(-lhs), "Negate a Rcplx value.");
169
170impl PartialEq<f64> for Rcplx {
171    fn eq(&self, other: &f64) -> bool {
172        self.re().inner() == *other && self.im() == 0.0
173    }
174}
175
176impl std::fmt::Debug for Rcplx {
177    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178        if self.is_na() {
179            write!(f, "NA_COMPLEX")
180        } else {
181            write!(
182                f,
183                "{:?} {} {:?}i",
184                self.re(),
185                if self.im().is_sign_negative() {
186                    '-'
187                } else {
188                    '+'
189                },
190                self.im().abs()
191            )
192        }
193    }
194}