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
93gen_sum_iter!(Rfloat);
94gen_partial_ord!(Rfloat, f64);
95
96// Generate binary ops for +, -, * and /
97gen_binop!(
98    Rfloat,
99    f64,
100    Add,
101    |lhs: f64, rhs: f64| Some(lhs + rhs),
102    "Add two Rfloat values or an option of f64."
103);
104gen_binop!(
105    Rfloat,
106    f64,
107    Sub,
108    |lhs: f64, rhs: f64| Some(lhs - rhs),
109    "Subtract two Rfloat values or an option of f64."
110);
111gen_binop!(
112    Rfloat,
113    f64,
114    Mul,
115    |lhs: f64, rhs: f64| Some(lhs * rhs),
116    "Multiply two Rfloat values or an option of f64."
117);
118gen_binop!(
119    Rfloat,
120    f64,
121    Div,
122    |lhs: f64, rhs: f64| Some(lhs / rhs),
123    "Divide two Rfloat values or an option of f64."
124);
125gen_binopassign!(
126    Rfloat,
127    f64,
128    AddAssign,
129    |lhs: f64, rhs: f64| Some(lhs + rhs),
130    "Add two Rfloat values or an option of f64, modifying the left-hand side in place. Overflows to NA."
131);
132gen_binopassign!(
133    Rfloat,
134    f64,
135    SubAssign,
136    |lhs: f64, rhs: f64| Some(lhs - rhs),
137    "Subtract two Rfloat values or an option of f64, modifying the left-hand side in place. Overflows to NA."
138);
139gen_binopassign!(
140    Rfloat,
141    f64,
142    MulAssign,
143    |lhs: f64, rhs: f64| Some(lhs * rhs),
144    "Multiply two Rfloat values or an option of f64, modifying the left-hand side in place. Overflows to NA."
145);
146gen_binopassign!(
147    Rfloat,
148    f64,
149    DivAssign,
150    |lhs: f64, rhs: f64| Some(lhs / rhs),
151    "Divide two Rfloat values or an option of f64, modifying the left-hand side in place. Overflows to NA."
152);
153
154// Generate unary ops for -, !
155gen_unop!(Rfloat, Neg, |lhs: f64| Some(-lhs), "Negate a Rfloat value.");
156
157impl From<i32> for Rfloat {
158    fn from(value: i32) -> Self {
159        Rfloat::from(value as f64)
160    }
161}
162
163impl From<Rint> for Rfloat {
164    fn from(value: Rint) -> Self {
165        if value.is_na() {
166            Rfloat::na()
167        } else {
168            Rfloat::from(value.0)
169        }
170    }
171}
172
173impl TryFrom<&Robj> for Rfloat {
174    type Error = Error;
175
176    fn try_from(robj: &Robj) -> Result<Self> {
177        let f64_val: Result<f64> = robj.try_into();
178        match f64_val {
179            Ok(val) => Ok(Rfloat::from(val)),
180            // TODO: Currently this results in an extra protection of robj
181            Err(Error::MustNotBeNA(_)) => Ok(Rfloat::na()),
182            Err(e) => Err(e),
183        }
184    }
185}
186
187impl std::fmt::Debug for Rfloat {
188    /// Debug format.
189    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190        if self.is_na() {
191            write!(f, "NA_REAL")
192        } else {
193            self.0.fmt(f)
194        }
195    }
196}