extendr_api/scalar/
rfloat.rs

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