@@ -318,6 +318,101 @@ using strings_matrix = matrix<r_vector<r_string>, r_string, S>;
318318template <typename S = by_column>
319319using complexes_matrix = matrix<r_vector<r_complex>, r_complex, S>;
320320
321+ // Implicit coercion from integer/logical to double
322+ template <typename S = by_column>
323+ class numeric_matrix {
324+ private:
325+ r_vector<double > vector_;
326+ int nrow_;
327+ int ncol_;
328+
329+ // Convert integer SEXP to double r_vector
330+ static r_vector<double > convert_integer (SEXP x, int nrow, int ncol) {
331+ R_xlen_t size = static_cast <R_xlen_t>(nrow) * ncol;
332+ SEXP result = PROTECT (Rf_allocMatrix (REALSXP, nrow, ncol));
333+ const int * CPP4R_RESTRICT x_ptr = INTEGER (x);
334+ double * CPP4R_RESTRICT ret_ptr = REAL (result);
335+
336+ for (R_xlen_t i = 0 ; i < size; ++i) {
337+ int val = x_ptr[i];
338+ ret_ptr[i] = CPP4R_LIKELY (val != NA_INTEGER) ? static_cast <double >(val) : NA_REAL;
339+ }
340+
341+ // Preserve dimnames
342+ SEXP dimnames = Rf_getAttrib (x, R_DimNamesSymbol);
343+ if (CPP4R_UNLIKELY (dimnames != R_NilValue)) {
344+ Rf_setAttrib (result, R_DimNamesSymbol, dimnames);
345+ }
346+
347+ UNPROTECT (1 );
348+ return r_vector<double >(result);
349+ }
350+
351+ // Convert logical SEXP to double r_vector
352+ static r_vector<double > convert_logical (SEXP x, int nrow, int ncol) {
353+ R_xlen_t size = static_cast <R_xlen_t>(nrow) * ncol;
354+ SEXP result = PROTECT (Rf_allocMatrix (REALSXP, nrow, ncol));
355+ const int * CPP4R_RESTRICT x_ptr = LOGICAL (x);
356+ double * CPP4R_RESTRICT ret_ptr = REAL (result);
357+
358+ for (R_xlen_t i = 0 ; i < size; ++i) {
359+ int val = x_ptr[i];
360+ ret_ptr[i] = CPP4R_LIKELY (val != NA_LOGICAL) ? static_cast <double >(val) : NA_REAL;
361+ }
362+
363+ // Preserve dimnames
364+ SEXP dimnames = Rf_getAttrib (x, R_DimNamesSymbol);
365+ if (CPP4R_UNLIKELY (dimnames != R_NilValue)) {
366+ Rf_setAttrib (result, R_DimNamesSymbol, dimnames);
367+ }
368+
369+ UNPROTECT (1 );
370+ return r_vector<double >(result);
371+ }
372+
373+ // Coerce SEXP to double r_vector (zero-copy if already double)
374+ static r_vector<double > coerce_to_double (SEXP x, int nrow, int ncol) {
375+ SEXPTYPE type = detail::r_typeof (x);
376+ if (type == REALSXP) {
377+ return r_vector<double >(x); // Zero-copy
378+ } else if (type == INTSXP) {
379+ return convert_integer (x, nrow, ncol);
380+ } else if (type == LGLSXP) {
381+ return convert_logical (x, nrow, ncol);
382+ }
383+ throw type_error (REALSXP, type);
384+ }
385+
386+ public:
387+ using underlying_type = double ;
388+
389+ numeric_matrix (SEXP x)
390+ : nrow_(Rf_nrows(x)),
391+ ncol_ (Rf_ncols(x)),
392+ vector_(coerce_to_double(x, nrow_, ncol_)) {}
393+
394+ // Allow construction from doubles_matrix (zero-copy)
395+ numeric_matrix (const doubles_matrix<S>& m)
396+ : vector_(m.vector()), nrow_(m.nrow()), ncol_(m.ncol()) {}
397+
398+ CPP4R_ALWAYS_INLINE int nrow () const noexcept { return nrow_; }
399+ CPP4R_ALWAYS_INLINE int ncol () const noexcept { return ncol_; }
400+ R_xlen_t size () const { return vector_.size (); }
401+ SEXP data () const { return vector_.data (); }
402+ operator SEXP () const { return SEXP (vector_); }
403+
404+ // Convert to doubles_matrix for full matrix functionality
405+ operator doubles_matrix<S>() const { return doubles_matrix<S>(vector_.data ()); }
406+
407+ CPP4R_ALWAYS_INLINE double operator ()(int row, int col) const {
408+ return vector_[row + (col * nrow_)];
409+ }
410+
411+ CPP4R_ALWAYS_INLINE const double * CPP4R_RESTRICT data_ptr () const noexcept {
412+ return vector_.data_ptr ();
413+ }
414+ };
415+
321416namespace writable {
322417template <typename S = by_column>
323418using doubles_matrix = matrix<r_vector<double >, typename r_vector<double >::reference, S>;
0 commit comments