Skip to main content

extendr_api/scalar/
rfloat.rs

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