Skip to content

Commit

Permalink
Add set_na() (#119)
Browse files Browse the repository at this point in the history
  • Loading branch information
yutannihilation authored Mar 27, 2024
1 parent f94e3ae commit 7064b4c
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 26 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
<!-- next-header -->
## [Unreleased] (ReleaseDate)

### New Features

* `OwnedIntegerSexp` and etc now have `set_na(i)` method for shorthand of
`set_elt(i, T::na())`. This is particularly useful for `OwnedLogicalSexp`
because its setter interface `set_elt()` only accepts `bool` and no missing
values.

## [v0.3.0] (2024-03-24)

### New Features
Expand Down
5 changes: 5 additions & 0 deletions R-package/R/wrappers.R
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,11 @@ flip_logical <- function(x) {
.Call(flip_logical__impl, x)
}


flip_logical_expert_only <- function(x) {
.Call(flip_logical_expert_only__impl, x)
}

#' Or operation
#'
#' @param x A logical vector.
Expand Down
6 changes: 6 additions & 0 deletions R-package/src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ SEXP flip_logical__impl(SEXP x) {
return handle_result(res);
}

SEXP flip_logical_expert_only__impl(SEXP x) {
SEXP res = flip_logical_expert_only(x);
return handle_result(res);
}

SEXP or_logical__impl(SEXP x, SEXP y) {
SEXP res = or_logical(x, y);
return handle_result(res);
Expand Down Expand Up @@ -368,6 +373,7 @@ static const R_CallMethodDef CallEntries[] = {
{"times_two_numeric__impl", (DL_FUNC) &times_two_numeric__impl, 1},
{"times_any_numeric__impl", (DL_FUNC) &times_any_numeric__impl, 2},
{"flip_logical__impl", (DL_FUNC) &flip_logical__impl, 1},
{"flip_logical_expert_only__impl", (DL_FUNC) &flip_logical_expert_only__impl, 1},
{"or_logical__impl", (DL_FUNC) &or_logical__impl, 2},
{"print_list__impl", (DL_FUNC) &print_list__impl, 1},
{"list_with_no_values__impl", (DL_FUNC) &list_with_no_values__impl, 0},
Expand Down
1 change: 1 addition & 0 deletions R-package/src/rust/api.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ SEXP times_any_int(SEXP x, SEXP y);
SEXP times_two_numeric(SEXP x);
SEXP times_any_numeric(SEXP x, SEXP y);
SEXP flip_logical(SEXP x);
SEXP flip_logical_expert_only(SEXP x);
SEXP or_logical(SEXP x, SEXP y);
SEXP print_list(SEXP x);
SEXP list_with_no_values(void);
Expand Down
28 changes: 22 additions & 6 deletions R-package/src/rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fn to_upper(x: StringSexp) -> savvy::Result<savvy::Sexp> {

for (i, e) in x.iter().enumerate() {
if e.is_na() {
out.set_elt(i, <&str>::na())?;
out.set_na(i)?;
continue;
}

Expand All @@ -61,7 +61,7 @@ fn add_suffix(x: StringSexp, y: &str) -> savvy::Result<savvy::Sexp> {

for (i, e) in x.iter().enumerate() {
if e.is_na() {
out.set_elt(i, <&str>::na())?;
out.set_na(i)?;
continue;
}

Expand All @@ -82,7 +82,7 @@ fn times_two_int(x: IntegerSexp) -> savvy::Result<savvy::Sexp> {

for (i, e) in x.iter().enumerate() {
if e.is_na() {
out[i] = i32::na();
out.set_na(i)?;
} else {
out[i] = e * 2;
}
Expand All @@ -103,7 +103,7 @@ fn times_any_int(x: IntegerSexp, y: i32) -> savvy::Result<savvy::Sexp> {

for (i, e) in x.iter().enumerate() {
if e.is_na() {
out[i] = i32::na();
out.set_na(i)?;
} else {
out[i] = e * y;
}
Expand All @@ -123,7 +123,7 @@ fn times_two_numeric(x: RealSexp) -> savvy::Result<savvy::Sexp> {

for (i, e) in x.iter().enumerate() {
if e.is_na() {
out[i] = f64::na();
out.set_na(i)?;
} else {
out[i] = e * 2.0;
}
Expand All @@ -144,7 +144,7 @@ fn times_any_numeric(x: RealSexp, y: f64) -> savvy::Result<savvy::Sexp> {

for (i, e) in x.iter().enumerate() {
if e.is_na() {
out[i] = f64::na();
out.set_na(i)?;
} else {
out[i] = e * y;
}
Expand All @@ -169,6 +169,22 @@ fn flip_logical(x: LogicalSexp) -> savvy::Result<savvy::Sexp> {
out.into()
}

// To handle NA values in a logical vector, use the raw values of i32, instead of bool.
#[savvy]
fn flip_logical_expert_only(x: LogicalSexp) -> savvy::Result<savvy::Sexp> {
let mut out = OwnedLogicalSexp::new(x.len())?;

for (i, e) in x.as_slice_raw().iter().enumerate() {
if e.is_na() {
out.set_na(i)?;
} else {
out.set_elt(i, *e != 1)?; // 1 means TRUE
}
}

out.into()
}

/// Or operation
///
/// @param x A logical vector.
Expand Down
6 changes: 6 additions & 0 deletions R-package/tests/testthat/test-unittest.R
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ test_that("functions work", {
c(FALSE, TRUE, TRUE)
)

# Use as_slice_raw() to handle NA
expect_equal(
flip_logical_expert_only(c(TRUE, FALSE, NA)),
c(FALSE, TRUE, NA)
)

# bool vector and scalar
expect_equal(
or_logical(c(TRUE, FALSE), TRUE),
Expand Down
20 changes: 14 additions & 6 deletions src/sexp/integer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use savvy_ffi::{INTEGER, INTSXP, SEXP};

use super::{impl_common_sexp_ops, impl_common_sexp_ops_owned, Sexp};
use crate::protect;
use crate::NotAvailableValue; // for na()

/// An external SEXP of an integer vector.
pub struct IntegerSexp(pub SEXP);
Expand Down Expand Up @@ -63,13 +64,9 @@ impl OwnedIntegerSexp {
self.as_slice().to_vec()
}

/// Set the value of the `i`-th element.
pub fn set_elt(&mut self, i: usize, v: i32) -> crate::error::Result<()> {
if i >= self.len {
return Err(crate::error::Error::new(&format!(
"index out of bounds: the length is {} but the index is {}",
self.len, i
)));
}
super::utils::verify_len(self.len, i)?;

unsafe {
*(self.raw.add(i)) = v;
Expand All @@ -78,6 +75,17 @@ impl OwnedIntegerSexp {
Ok(())
}

/// Set the `i`-th element to NA.
pub fn set_na(&mut self, i: usize) -> crate::error::Result<()> {
super::utils::verify_len(self.len, i)?;

unsafe {
*(self.raw.add(i)) = i32::na();
}

Ok(())
}

fn new_inner(len: usize, init: bool) -> crate::error::Result<Self> {
let inner = crate::alloc_vector(INTSXP, len as _)?;

Expand Down
21 changes: 14 additions & 7 deletions src/sexp/logical.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use savvy_ffi::{LGLSXP, LOGICAL, SET_LOGICAL_ELT, SEXP};
use savvy_ffi::{R_NaInt, LGLSXP, LOGICAL, SET_LOGICAL_ELT, SEXP};

use super::{impl_common_sexp_ops, impl_common_sexp_ops_owned, Sexp};
use crate::protect;
Expand Down Expand Up @@ -59,13 +59,9 @@ impl OwnedLogicalSexp {
self.iter().collect()
}

/// Set the value of the `i`-th element.
pub fn set_elt(&mut self, i: usize, v: bool) -> crate::error::Result<()> {
if i >= self.len {
return Err(crate::error::Error::new(&format!(
"index out of bounds: the length is {} but the index is {}",
self.len, i
)));
}
super::utils::verify_len(self.len, i)?;

unsafe {
SET_LOGICAL_ELT(self.inner, i as _, v as _);
Expand All @@ -74,6 +70,17 @@ impl OwnedLogicalSexp {
Ok(())
}

/// Set the `i`-th element to NA.
pub fn set_na(&mut self, i: usize) -> crate::error::Result<()> {
super::utils::verify_len(self.len, i)?;

unsafe {
SET_LOGICAL_ELT(self.inner, i as _, R_NaInt);
}

Ok(())
}

fn new_inner(len: usize, init: bool) -> crate::error::Result<Self> {
let inner = crate::alloc_vector(LGLSXP, len as _)?;

Expand Down
1 change: 1 addition & 0 deletions src/sexp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub mod null;
pub mod real;
pub mod scalar;
pub mod string;
pub mod utils;

/// An `SEXP`.
pub struct Sexp(pub SEXP);
Expand Down
20 changes: 14 additions & 6 deletions src/sexp/real.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use savvy_ffi::{REAL, REALSXP, SEXP};

use super::{impl_common_sexp_ops, impl_common_sexp_ops_owned, Sexp};
use crate::protect;
use crate::NotAvailableValue; // for na()

/// An external SEXP of a real vector.
pub struct RealSexp(pub SEXP);
Expand Down Expand Up @@ -59,13 +60,9 @@ impl OwnedRealSexp {
self.as_slice().to_vec()
}

/// Set the value of the `i`-th element.
pub fn set_elt(&mut self, i: usize, v: f64) -> crate::error::Result<()> {
if i >= self.len {
return Err(crate::error::Error::new(&format!(
"index out of bounds: the length is {} but the index is {}",
self.len, i
)));
}
super::utils::verify_len(self.len, i)?;

unsafe {
*(self.raw.add(i)) = v;
Expand All @@ -74,6 +71,17 @@ impl OwnedRealSexp {
Ok(())
}

/// Set the `i`-th element to NA.
pub fn set_na(&mut self, i: usize) -> crate::error::Result<()> {
super::utils::verify_len(self.len, i)?;

unsafe {
*(self.raw.add(i)) = f64::na();
}

Ok(())
}

fn new_inner(len: usize, init: bool) -> crate::error::Result<Self> {
let inner = crate::alloc_vector(REALSXP, len as _)?;

Expand Down
20 changes: 19 additions & 1 deletion src/sexp/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use std::ffi::CStr;
use std::os::raw::c_char;

use savvy_ffi::{
cetype_t_CE_UTF8, Rf_mkCharLenCE, Rf_xlength, R_CHAR, SET_STRING_ELT, SEXP, STRING_ELT, STRSXP,
cetype_t_CE_UTF8, R_NaString, Rf_mkCharLenCE, Rf_xlength, R_CHAR, SET_STRING_ELT, SEXP,
STRING_ELT, STRSXP,
};

use super::na::NotAvailableValue;
Expand Down Expand Up @@ -54,6 +55,7 @@ impl OwnedStringSexp {
self.iter().collect()
}

/// Set the value of the `i`-th element.
pub fn set_elt(&mut self, i: usize, v: &str) -> crate::error::Result<()> {
if i >= self.len {
return Err(crate::error::Error::new(&format!(
Expand All @@ -68,6 +70,22 @@ impl OwnedStringSexp {
Ok(())
}

/// Set the `i`-th element to NA.
pub fn set_na(&mut self, i: usize) -> crate::error::Result<()> {
if i >= self.len {
return Err(crate::error::Error::new(&format!(
"index out of bounds: the length is {} but the index is {}",
self.len, i
)));
}

unsafe {
SET_STRING_ELT(self.inner, i as _, R_NaString);
}

Ok(())
}

/// Constructs a new string vector.
pub fn new(len: usize) -> crate::error::Result<Self> {
let inner = crate::alloc_vector(STRSXP, len as _)?;
Expand Down
10 changes: 10 additions & 0 deletions src/sexp/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pub fn verify_len(len: usize, i: usize) -> crate::error::Result<()> {
if i >= len {
Err(crate::error::Error::new(&format!(
"index out of bounds: the length is {} but the index is {}",
len, i
)))
} else {
Ok(())
}
}

0 comments on commit 7064b4c

Please sign in to comment.