diff --git a/cpp11test/src/test-complexes.cpp b/cpp11test/src/test-complexes.cpp new file mode 100644 index 00000000..21bf174b --- /dev/null +++ b/cpp11test/src/test-complexes.cpp @@ -0,0 +1,500 @@ +#include "cpp11/complexes.hpp" + +#include "cpp11/strings.hpp" + +#include + +context("complexes-C++") { + test_that("complexes::r_vector(SEXP)") { + cpp11::complexes x(Rf_allocVector(CPLXSXP, 2)); + expect_true(x.size() == 2); + + expect_error(cpp11::complexes(Rf_allocVector(INTSXP, 2))); + } + + test_that("complexes::r_vector::const_iterator()") { + cpp11::complexes x(Rf_allocVector(CPLXSXP, 100)); + COMPLEX(x)[0] = Rcomplex{1, 1}; + COMPLEX(x)[1] = Rcomplex{2, 2}; + COMPLEX(x)[2] = Rcomplex{3, 3}; + COMPLEX(x)[3] = Rcomplex{4, 4}; + COMPLEX(x)[4] = Rcomplex{5, 5}; + COMPLEX(x)[97] = Rcomplex{98, 98}; + COMPLEX(x)[98] = Rcomplex{99, 99}; + COMPLEX(x)[99] = Rcomplex{100, 100}; + expect_true(x.size() == 100); + + auto it = x.begin(); + auto it2 = x.begin(); + expect_true(it == it2); + + ++it; + expect_true(!(it == it2)); + expect_true(it != it2); + + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + cpp11::r_complex ninety_nine{99, 99}; + cpp11::r_complex ninety_eight{98, 98}; + cpp11::r_complex one_hundred{100, 100}; + + ++it; + expect_true(*it == three); + --it; + expect_true(*it == two); + --it; + + it += 99; + expect_true(*it == one_hundred); + --it; + expect_true(*it == ninety_nine); + --it; + expect_true(*it == ninety_eight); + it -= 95; + expect_true(*it == three); + } + + test_that("complexes.push_back()") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + cpp11::writable::complexes x; + x.push_back(one); + x.push_back(two); + x.push_back(three); + + expect_true(x.size() == 3); + expect_true(x[0] == one); + expect_true(x[1] == two); + expect_true(x[2] == three); + } + test_that("complexes.resize()") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + cpp11::writable::complexes x; + x.resize(3); + x[0] = one; + x[1] = two; + x[2] = three; + + expect_true(x.size() == 3); + expect_true(x[0] == one); + expect_true(x[1] == two); + expect_true(x[2] == three); + } + test_that("complexes.at()") { + cpp11::writable::complexes x; + + expect_error(x.at(-1)); + + expect_error(x.at(0)); + + cpp11::r_complex one{1, 1}; + + x.push_back(one); + expect_true(x.at(0) == one); + expect_error(x.at(1)); + } + test_that("complexes.pop_back()") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + cpp11::writable::complexes x; + + x.push_back(one); + x.push_back(two); + x.pop_back(); + + expect_true(x.size() == 1); + expect_true(x[0] == one); + + expect_error(x.at(1)); + } + test_that("complexes.insert()") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + cpp11::writable::complexes x; + + x.insert(0, one); + x.insert(0, two); + x.insert(1, three); + expect_true(x.size() == 3); + + expect_true(x[0] == two); + expect_true(x[1] == three); + expect_true(x[2] == one); + } + test_that("complexes.erase()") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + cpp11::r_complex four{4, 4}; + cpp11::r_complex five{5, 5}; + + cpp11::writable::complexes x; + + x.push_back(one); + x.push_back(two); + x.push_back(three); + x.push_back(four); + x.push_back(five); + + expect_true(x.size() == 5); + + x.erase(0); + + expect_true(x.size() == 4); + expect_true(x[0] == two); + expect_true(x[1] == three); + expect_true(x[2] == four); + expect_true(x[3] == five); + + x.erase(2); + + expect_true(x.size() == 3); + expect_true(x[0] == two); + expect_true(x[1] == three); + expect_true(x[2] == five); + } + test_that("complexes.iterator* = ") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + cpp11::r_complex four{4, 4}; + + cpp11::writable::complexes x; + x.push_back(one); + x.push_back(two); + x.push_back(three); + auto it = x.begin() + 1; + *it = three; + ++it; + *it = four; + + expect_true(x.size() == 3); + expect_true(x[0] == one); + expect_true(x[1] == three); + expect_true(x[2] == four); + } + + test_that("writable::complexes(SEXP)") { + Rcomplex one{1, 1}; + Rcomplex two{2, 2}; + Rcomplex three{3, 3}; + Rcomplex four{4, 4}; + Rcomplex five{5, 5}; + Rcomplex six{6, 6}; + Rcomplex seven{7, 7}; + + SEXP x = PROTECT(Rf_allocVector(CPLXSXP, 5)); + + COMPLEX(x)[0] = one; + COMPLEX(x)[1] = two; + COMPLEX(x)[2] = three; + COMPLEX(x)[3] = four; + COMPLEX(x)[4] = five; + + cpp11::writable::complexes y(x); + y[0] = cpp11::r_complex(six); + + expect_true(x != y.data()); + + expect_true(COMPLEX(x)[0].r == one.r); + expect_true(COMPLEX(x)[0].i == one.i); + expect_true(y[0] == cpp11::r_complex(six)); + + cpp11::writable::complexes z(y); + z[0] = cpp11::r_complex(seven); + + expect_true(z.data() != y.data()); + + expect_true(COMPLEX(x)[0].r == one.r); + expect_true(COMPLEX(x)[0].i == one.i); + expect_true(y[0] == cpp11::r_complex(six)); + expect_true(z[0] == cpp11::r_complex(seven)); + + UNPROTECT(1); + } + test_that("writable::complexes(SEXP, bool)") { + Rcomplex five{5, 5}; + SEXP x = PROTECT(Rf_ScalarComplex(five)); + cpp11::writable::complexes y(x, false); + + expect_true(COMPLEX(y)[0].r == five.r); + expect_true(COMPLEX(y)[0].i == five.i); + UNPROTECT(1); + } + + test_that("writable::complexes(SEXP) assignment") { + cpp11::r_complex zero{0, 0}; + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + cpp11::writable::complexes x({one, two, three}); + cpp11::writable::complexes y({zero}); + y = x; + expect_true(y.size() == 3); + expect_true(y.data() != x.data()); + expect_true(y.is_altrep() == x.is_altrep()); + } + + test_that("writable::complexes(SEXP) move assignment") { + cpp11::r_complex zero{0, 0}; + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + cpp11::writable::complexes x({one, two, three}); + cpp11::writable::complexes y({zero}); + auto x_data = x.data(); + + y = std::move(x); + expect_true(y.size() == 3); + expect_true(y.data() == x_data); + expect_true(y.is_altrep() == false); + } + + test_that("complexes::names(empty)") { + cpp11::complexes x; + auto nms = x.names(); + expect_true(nms.size() == 0); + } + + test_that("complexes::names") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + using namespace cpp11::literals; + cpp11::writable::complexes x({"a"_nm = one, "b"_nm = two, "c"_nm = three}); + expect_true(x[0] == one); + expect_true(x[1] == two); + expect_true(x[2] == three); + + expect_true(x.contains("a")); + expect_true(!x.contains("d")); + + expect_true(x["a"] == one); + expect_true(x["b"] == two); + expect_true(x["c"] == three); + + cpp11::sexp nms(x.names()); + expect_true(Rf_xlength(nms) == 3); + auto nms0 = CHAR(STRING_ELT(nms, 0)); + auto nms1 = CHAR(STRING_ELT(nms, 1)); + auto nms2 = CHAR(STRING_ELT(nms, 2)); + expect_true(strcmp(nms0, "a") == 0); + expect_true(strcmp(nms1, "b") == 0); + expect_true(strcmp(nms2, "c") == 0); + } + + test_that("complexes::attr") { + cpp11::complexes x(PROTECT(Rf_allocVector(CPLXSXP, 2))); + COMPLEX(x)[0] = Rcomplex{1, 1}; + COMPLEX(x)[1] = Rcomplex{2, 2}; + + SEXP foo = Rf_install("foo"); + Rf_setAttrib(x, foo, Rf_mkString("bar")); + + // This doesn't compile by design + // x.attr("foo") = "bar"; + + // But this will + cpp11::writable::complexes y(x); + y.attr("foo") = "baz"; + + expect_true(strcmp(CHAR(STRING_ELT(x.attr("foo"), 0)), "bar") == 0); + expect_true(strcmp(CHAR(STRING_ELT(y.attr("foo"), 0)), "baz") == 0); + + UNPROTECT(1); + } + + test_that("writable::complexes(std::vector::iterator)") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + std::vector x({one, two}); + cpp11::writable::complexes y(x.begin(), x.end()); + + expect_true(y.size() == 2); + expect_true(y[0] == one); + expect_true(y[1] == two); + } + + test_that("writable::complexes(std::vector)") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + std::vector x({one, two, three}); + cpp11::writable::complexes y(x); + + expect_true(y.size() == 3); + expect_true(y[0] == one); + expect_true(y[2] == three); + } + + test_that("writable::complexes attributes are kept when converted to complexes") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + cpp11::writable::complexes x({one, two}); + x.names() = {"a", "b"}; + cpp11::strings x_nms(x.names()); + expect_true(x_nms[0] == "a"); + expect_true(x_nms[1] == "b"); + + cpp11::complexes y(x); + cpp11::strings y_nms(y.names()); + expect_true(y_nms[0] == "a"); + expect_true(y_nms[1] == "b"); + } + + test_that("comparison operator works") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + using namespace cpp11; + cpp11::complexes base({one, two}); + cpp11::complexes same_values({one, two}); + cpp11::complexes diff_length({one}); + cpp11::complexes diff_values({one, three}); + + expect_true(base == base); + expect_true(base == base); + expect_true(base == same_values); + expect_true(!(base == diff_length)); + expect_true(!(base == diff_values)); + + expect_true(!(base != base)); + expect_true(!(base != same_values)); + expect_true(base != diff_length); + expect_true(base != diff_values); + } + + test_that("proxy comparison works symmetrically") { + cpp11::r_complex x{1, 2}; + cpp11::writable::complexes y({x}); + + expect_true(x == y[0]); + expect_true(y[0] == x); + } + + test_that("complexes operator[] and at") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + cpp11::complexes x(Rf_allocVector(CPLXSXP, 2)); + COMPLEX(x)[0] = Rcomplex(one); + COMPLEX(x)[1] = Rcomplex(two); + + int i0 = 0; + R_xlen_t x0 = 0; + size_t s0 = 0; + + expect_true(x[i0] == one); + expect_true(x[x0] == one); + expect_true(x[s0] == one); + + expect_true(x.at(i0) == one); + expect_true(x.at(x0) == one); + expect_true(x.at(s0) == one); + } + + test_that("writable::complexes operator[] and at") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + cpp11::writable::complexes x(Rf_allocVector(CPLXSXP, 2)); + COMPLEX(x)[0] = Rcomplex(one); + COMPLEX(x)[1] = Rcomplex(two); + + int i0 = 0; + R_xlen_t x0 = 0; + size_t s0 = 0; + + expect_true(x[i0] == one); + expect_true(x[x0] == one); + expect_true(x[s0] == one); + + expect_true(x.at(i0) == one); + expect_true(x.at(x0) == one); + expect_true(x.at(s0) == one); + } + + test_that("operator[] and at with names") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + using namespace cpp11::literals; + cpp11::writable::complexes x({"a"_nm = one, "b"_nm = two}); + cpp11::complexes y(x); + + expect_true(x["a"] == one); + expect_true(x["b"] == two); + expect_error(x["c"] == two); + + expect_true(y["a"] == one); + expect_true(y["b"] == two); + expect_error(y["c"] == two); + } + + test_that("complexes::find") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + using namespace cpp11::literals; + cpp11::writable::complexes x({"a"_nm = one, "b"_nm = two}); + cpp11::complexes y(x); + + expect_true(x.find("a") == x.begin()); + expect_true(x.find("b") == x.begin() + 1); + expect_true(x.find("c") == x.end()); + + expect_true(y.find("a") == y.begin()); + expect_true(y.find("b") == y.begin() + 1); + expect_true(y.find("c") == y.end()); + } + + test_that("writable::complexes compound assignments") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + + cpp11::writable::complexes x({one}); + + auto x0 = x[0]; + expect_true(x0 == one); + + // Arithmetic is not defined on Rcomplex or r_complex, + // so using it on a proxy also fails and is not defined + // expect_error(x0 += two); + // expect_error(x0 -= two); + // expect_error(x0 *= two); + // expect_error(x0 /= two); + // expect_error(x0--); + // expect_error(x0++); + // expect_error(++x0); + // expect_error(--x0); + } + + test_that("writable::complexes convert to complexes with correct size (#128)") { + cpp11::r_complex one{1, 1}; + cpp11::r_complex two{2, 2}; + cpp11::r_complex three{3, 3}; + + cpp11::writable::complexes foo; + foo.push_back(one); + foo.push_back(two); + foo.push_back(three); + + cpp11::complexes bar(foo); + expect_true(Rf_xlength(bar) == 3); + } +} diff --git a/cpp11test/src/test-r_complex.cpp b/cpp11test/src/test-r_complex.cpp new file mode 100644 index 00000000..3533eed6 --- /dev/null +++ b/cpp11test/src/test-r_complex.cpp @@ -0,0 +1,94 @@ +#include "cpp11/r_complex.hpp" + +#include "cpp11/sexp.hpp" + +#include + +context("r_complex-C++") { + test_that("r_complex() zero initialization") { + // `cpp11::r_complex x;` is "not initialized", this is "zero initialized" + cpp11::r_complex x{}; + expect_true(x.real() == 0.); + expect_true(x.imag() == 0.); + } + + test_that("r_complex(double, double) and accessors") { + cpp11::r_complex x(1., 2.); + expect_true(x.real() == 1.); + expect_true(x.imag() == 2.); + } + + test_that("r_complex(SEXP)") { + cpp11::r_complex x(1, 2); + + cpp11::sexp value = Rf_allocVector(CPLXSXP, 1); + COMPLEX(value)[0] = static_cast(x); + + cpp11::r_complex x2(value); + + expect_true(x2 == x); + } + + test_that("explicit construction from Rcomplex") { + Rcomplex x{1, 2}; + cpp11::r_complex y(x); + expect_true(y.real() == x.r); + expect_true(y.imag() == x.i); + } + + test_that("explicit construction from std::complex") { + std::complex x{1, 2}; + cpp11::r_complex y(x); + expect_true(y.real() == x.real()); + expect_true(y.imag() == x.imag()); + } + + test_that("explicit conversion to Rcomplex") { + cpp11::r_complex x(1, 2); + Rcomplex y(x); + expect_true(y.r == x.real()); + expect_true(y.i == x.imag()); + } + + test_that("explicit conversion to std::complex") { + cpp11::r_complex x(1, 2); + std::complex y(x); + expect_true(y.real() == x.real()); + expect_true(y.imag() == x.imag()); + } + + test_that("equality comparison of two r_complex") { + expect_true(cpp11::r_complex(1, 3) == cpp11::r_complex(1, 3)); + expect_false(cpp11::r_complex(1, 3) == cpp11::r_complex(2, 3)); + expect_false(cpp11::r_complex(1, 3) == cpp11::r_complex(1, 4)); + } + + test_that("na()") { + cpp11::r_complex x = cpp11::na(); + // Not `ISNA()`, checking specifically for `NA_REAL` + expect_true(R_IsNA(x.real())); + expect_true(R_IsNA(x.imag())); + } + + test_that("is_na(r_complex)") { + cpp11::r_complex x{1, 2}; + expect_false(cpp11::is_na(x)); + + cpp11::r_complex na_na{NA_REAL, NA_REAL}; + cpp11::r_complex na_real{NA_REAL, 1}; + cpp11::r_complex real_na{1, NA_REAL}; + + expect_true(cpp11::is_na(na_na)); + expect_true(cpp11::is_na(na_real)); + expect_true(cpp11::is_na(real_na)); + } + + test_that("as_sexp(r_complex)") { + cpp11::r_complex x{1, 2}; + cpp11::sexp value = cpp11::as_sexp(x); + + expect_true(Rf_xlength(value) == 1); + expect_true(COMPLEX(value)[0].r == x.real()); + expect_true(COMPLEX(value)[0].i == x.imag()); + } +} diff --git a/inst/include/cpp11.hpp b/inst/include/cpp11.hpp index 71e1cf1d..95b7db3b 100644 --- a/inst/include/cpp11.hpp +++ b/inst/include/cpp11.hpp @@ -4,6 +4,7 @@ #include "cpp11/altrep.hpp" #include "cpp11/as.hpp" #include "cpp11/attribute_proxy.hpp" +#include "cpp11/complexes.hpp" #include "cpp11/data_frame.hpp" #include "cpp11/doubles.hpp" #include "cpp11/environment.hpp" @@ -17,6 +18,7 @@ #include "cpp11/named_arg.hpp" #include "cpp11/protect.hpp" #include "cpp11/r_bool.hpp" +#include "cpp11/r_complex.hpp" #include "cpp11/r_string.hpp" #include "cpp11/r_vector.hpp" #include "cpp11/raws.hpp" diff --git a/inst/include/cpp11/complexes.hpp b/inst/include/cpp11/complexes.hpp new file mode 100644 index 00000000..1a1edc80 --- /dev/null +++ b/inst/include/cpp11/complexes.hpp @@ -0,0 +1,154 @@ +#pragma once + +#include // for min +#include // for array +#include // for initializer_list + +#include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_allocVector +#include "cpp11/as.hpp" // for as_sexp +#include "cpp11/attribute_proxy.hpp" // for attribute_proxy +#include "cpp11/named_arg.hpp" // for named_arg +#include "cpp11/protect.hpp" // for preserved +#include "cpp11/r_complex.hpp" // for r_complex +#include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy +#include "cpp11/sexp.hpp" // for sexp + +// Specializations for complex + +namespace cpp11 { + +template <> +inline SEXP r_vector::valid_type(SEXP data) { + if (data == nullptr) { + throw type_error(CPLXSXP, NILSXP); + } + if (TYPEOF(data) != CPLXSXP) { + throw type_error(CPLXSXP, TYPEOF(data)); + } + return data; +} + +template <> +inline r_complex r_vector::operator[](const R_xlen_t pos) const { + // NOPROTECT: likely too costly to unwind protect every elt + return static_cast(is_altrep_ ? COMPLEX_ELT(data_, pos) : data_p_[pos]); +} + +template <> +inline typename r_vector::underlying_type* r_vector::get_p( + bool is_altrep, SEXP data) { + if (is_altrep) { + return nullptr; + } else { + return COMPLEX(data); + } +} + +template <> +inline void r_vector::const_iterator::fill_buf(R_xlen_t pos) { + length_ = std::min(64_xl, data_->size() - pos); + COMPLEX_GET_REGION(data_->data_, pos, length_, buf_.data()); + block_start_ = pos; +} + +typedef r_vector complexes; + +namespace writable { + +template <> +inline typename r_vector::proxy& r_vector::proxy::operator=( + const r_complex& rhs) { + if (is_altrep_) { + // NOPROTECT: likely too costly to unwind protect every set elt + SET_COMPLEX_ELT(data_, index_, static_cast(rhs)); + } else { + *p_ = static_cast(rhs); + } + return *this; +} + +template <> +inline r_vector::proxy::operator r_complex() const { + if (p_ == nullptr) { + // NOPROTECT: likely too costly to unwind protect every elt + return static_cast(COMPLEX_ELT(data_, index_)); + } else { + return static_cast(*p_); + } +} + +inline bool operator==(const r_vector::proxy& lhs, r_complex rhs) { + return static_cast(lhs) == rhs; +} + +template <> +inline r_vector::r_vector(std::initializer_list il) + : cpp11::r_vector(Rf_allocVector(CPLXSXP, il.size())), capacity_(il.size()) { + protect_ = preserved.insert(data_); + auto it = il.begin(); + for (R_xlen_t i = 0; i < capacity_; ++i, ++it) { + SET_COMPLEX_ELT(data_, i, static_cast(*it)); + } +} + +template <> +inline void r_vector::reserve(R_xlen_t new_capacity) { + data_ = data_ == R_NilValue ? safe[Rf_allocVector](CPLXSXP, new_capacity) + : safe[Rf_xlengthgets](data_, new_capacity); + SEXP old_protect = protect_; + + // Protect the new data + protect_ = preserved.insert(data_); + + // Release the old protection; + preserved.release(old_protect); + + data_p_ = COMPLEX(data_); + capacity_ = new_capacity; +} + +template <> +inline r_vector::r_vector(std::initializer_list il) + : cpp11::r_vector(safe[Rf_allocVector](CPLXSXP, il.size())), + capacity_(il.size()) { + protect_ = preserved.insert(data_); + int n_protected = 0; + + try { + unwind_protect([&] { + Rf_setAttrib(data_, R_NamesSymbol, Rf_allocVector(STRSXP, capacity_)); + SEXP names = PROTECT(Rf_getAttrib(data_, R_NamesSymbol)); + ++n_protected; + auto it = il.begin(); + for (R_xlen_t i = 0; i < capacity_; ++i, ++it) { + data_p_[i] = COMPLEX_ELT(it->value(), 0); + SET_STRING_ELT(names, i, Rf_mkCharCE(it->name(), CE_UTF8)); + } + UNPROTECT(n_protected); + }); + } catch (const unwind_exception& e) { + preserved.release(protect_); + UNPROTECT(n_protected); + throw e; + } +} + +template <> +inline void r_vector::push_back(r_complex value) { + while (length_ >= capacity_) { + reserve(capacity_ == 0 ? 1 : capacity_ *= 2); + } + if (is_altrep_) { + // NOPROTECT: likely too costly to unwind protect every elt + SET_COMPLEX_ELT(data_, length_, static_cast(value)); + } else { + data_p_[length_] = static_cast(value); + } + ++length_; +} + +typedef r_vector complexes; + +} // namespace writable + +} // namespace cpp11 diff --git a/inst/include/cpp11/r_complex.hpp b/inst/include/cpp11/r_complex.hpp new file mode 100644 index 00000000..fd093944 --- /dev/null +++ b/inst/include/cpp11/r_complex.hpp @@ -0,0 +1,92 @@ +#pragma once + +#include +#include // for complex +#include // for is_convertible, enable_if + +#include "R_ext/Arith.h" // for NA_REAL +#include "R_ext/Complex.h" // for Rcomplex +#include "cpp11/R.hpp" // for SEXP, SEXPREC, ... +#include "cpp11/as.hpp" // for as_sexp +#include "cpp11/protect.hpp" // for unwind_protect, preserved +#include "cpp11/sexp.hpp" // for sexp + +namespace cpp11 { + +class r_complex { + public: + r_complex() = default; + + r_complex(SEXP data) { + if (Rf_isComplex(data) && Rf_xlength(data) == 1) { + Rcomplex elt = COMPLEX_ELT(data, 0); + real_ = elt.r; + imag_ = elt.i; + } else { + throw std::invalid_argument("Invalid r_complex value"); + } + } + + r_complex(double real, double imag) : + real_(real), imag_(imag) {} + + explicit r_complex(Rcomplex value) : + real_(value.r), imag_(value.i) {} + explicit r_complex(std::complex value) : + real_(value.real()), imag_(value.imag()) {} + + explicit operator Rcomplex() const { + return Rcomplex{real_, imag_}; + } + explicit operator std::complex() const { + return std::complex(real_, imag_); + } + + double real() const { + return real_; + } + double imag() const { + return imag_; + } + private: + double real_; + double imag_; +}; + +inline bool operator==(const r_complex& x, const r_complex& y) { + return (x.real() == y.real()) && (x.imag() == y.imag()); +} + +inline std::ostream& operator<<(std::ostream& os, const r_complex& value) { + os << value.real() << "+" << value.imag() << "i" ; + return os; +} + +template <> +inline r_complex na() { + return r_complex(NA_REAL, NA_REAL); +} + +template <> +inline bool is_na(const r_complex& x) { + return ISNA(x.real()) || ISNA(x.imag()); +} + +template +using enable_if_r_complex = enable_if_t::value, R>; + +template +enable_if_r_complex as_sexp(T from) { + sexp res = Rf_allocVector(CPLXSXP, 1); + unwind_protect([&] { SET_COMPLEX_ELT(res.data(), 0, static_cast(from)); }); + return res; +} + +namespace traits { +template <> +struct get_underlying_type { + using type = Rcomplex; +}; +} // namespace traits + +} // namespace cpp11