inst/include/beachmat3/lin_matrix.h
d83b9a7f
 #ifndef BEACHMAT_LIN_MATRIX_H
 #define BEACHMAT_LIN_MATRIX_H
 
 /**
  * @file lin_matrix.h
  *
  * Class definitions for the logical, integer or numeric (LIN) matrix.
  */
 
 #include "Rcpp.h"
 #include "ordinary_reader.h"
 #include "Csparse_reader.h"
 #include "utils.h"
 
e8086957
 #include <memory>
 #include <algorithm>
 
d83b9a7f
 namespace beachmat {
 
 /**
  * @brief Virtual base class for a logical, integer or numeric (i.e., double-precision) matrix,
e8086957
  *
  * This provides methods to extract rows or columns in dense form.
  * We suggest using the `read_lin_block()` function to construct instances of this class.
d83b9a7f
  */
 class lin_matrix {
 public:
     /**
      * Trivial constructor.
      */
     lin_matrix() {}
 
     virtual ~lin_matrix() = default;
     lin_matrix(const lin_matrix&) = default;
     lin_matrix& operator=(const lin_matrix&) = default;
     lin_matrix(lin_matrix&&) = default;
     lin_matrix& operator=(lin_matrix&&) = default;
 
     /**
e8086957
      * Extract values from a column as an array of integers, restricted to a contiguous subset of rows.
d83b9a7f
      *
      * @param c Index of the column of interest.
      * @param work The workspace, potentially used to store extracted values.
      * This should have at least `last - first` addressable elements.
      * @param first The index of the first row of interest.
e8086957
      * @param last The index of one-past-the-last row of interest.
d83b9a7f
      *
      * @return A pointer is returned to the values of `c` as integers, starting at the `first` element.
      * This may or may not involve populating `work` with values copied from the underlying matrix;
e8086957
      * a copy will have been performed iff the return value compares equal to `work`.
d83b9a7f
      */
     virtual const int* get_col(size_t c, int* work, size_t first, size_t last) = 0;
 
     /**
e8086957
      * Extract values from a row as an array of integers, restricted to a contiguous subset of columns.
d83b9a7f
      *
      * @param r Index of the row of interest.
      * @param work The workspace, potentially used to store extracted values.
      * This should have at least `last - first` addressable elements.
      * @param first The index of the first column of interest.
e8086957
      * @param last The index of one-past-the-last column of interest.
d83b9a7f
      *
      * @return A pointer is returned to the values of `r` as integers, starting at the `first` element.
      * This involves creating a copy in the workspace so the return value is always equal to `work`.
      */
     virtual const int* get_row(size_t r, int* work, size_t first, size_t last) = 0;
 
     /**
e8086957
      * Extract values from a column as an array of doubles, restricted to a contiguous subset of rows.
d83b9a7f
      *
      * @param c Index of the column of interest.
      * @param work The workspace, potentially used to store extracted values.
      * This should have at least `last - first` addressable elements.
      * @param first The index of the first row of interest.
e8086957
      * @param last The index of one-past-the-last row of interest.
d83b9a7f
      *
      * @return A pointer is returned to the values of `c` as doubles, starting at the `first` element.
      * This may or may not involve populating `work` with values copied from the underlying matrix;
e8086957
      * a copy will have been performed iff the return value compares equal to `work`.
d83b9a7f
      */
     virtual const double* get_col(size_t c, double* work, size_t first, size_t last) = 0;
 
     /**
e8086957
      * Extract values from a row as an array of doubles, restricted to a contiguous subset of columns.
d83b9a7f
      *
      * @param r Index of the row of interest.
      * @param work The workspace, potentially used to store extracted values.
      * This should have at least `last - first` addressable elements.
      * @param first The index of the first column of interest.
e8086957
      * @param last The index of one-past-the-last column of interest.
d83b9a7f
      *
      * @return A pointer is returned to the values of `r` as doubles, starting at the `first` element.
      * This involves creating a copy in the workspace so the return value is always equal to `work`.
      */
     virtual const double* get_row(size_t r, double* work, size_t first, size_t last) = 0;
 
     /**
e8086957
      * Extract values from a column as an array of integers.
d83b9a7f
      *
      * @param c Index of the column of interest.
      * @param work The workspace, potentially used to store extracted values.
      * This should have at least `last - first` addressable elements.
      *
      * @return A pointer is returned to the values of `c` as integers, starting at the first element.
      * This may or may not involve populating `work` with values copied from the underlying matrix;
e8086957
      * a copy will have been performed iff the return value compares equal to `work`.
d83b9a7f
      */
     const int* get_col(size_t c, int* work) {
         return get_col(c, work, 0, nrow);
     }
 
     /**
e8086957
      * Extract values from a row as an array of integers.
d83b9a7f
      *
      * @param r Index of the column of interest.
      * @param work The workspace, potentially used to store extracted values.
      * This should have at least `last - first` addressable elements.
      *
      * @return A pointer is returned to the values of `r` as integers, starting at the first element.
      * This involves creating a copy in the workspace so the return value is always equal to `work`.
      */
     const int* get_row(size_t r, int* work) {
         return get_row(r, work, 0, ncol);        
     }
 
     /**
e8086957
      * Extract values from a column as an array of doubles.
d83b9a7f
      *
      * @param c Index of the column of interest.
      * @param work The workspace, potentially used to store extracted values.
      * This should have at least `last - first` addressable elements.
      *
      * @return A pointer is returned to the values of `c` as doubles, starting at the first element.
      * This may or may not involve populating `work` with values copied from the underlying matrix;
e8086957
      * a copy will have been performed iff the return value compares equal to `work`.
d83b9a7f
      */
     const double* get_col(size_t c, double* work) {
         return get_col(c, work, 0, nrow);
     }
 
     /**
e8086957
      * Extract values from a row as an array of doubles.
d83b9a7f
      *
      * @param r Index of the column of interest.
      * @param work The workspace, potentially used to store extracted values.
      * This should have at least `last - first` addressable elements.
      *
      * @return A pointer is returned to the values of `r` as doubles, starting at the first element.
      * This involves creating a copy in the workspace so the return value is always equal to `work`.
      */
     const double* get_row(size_t r, double* work) {
         return get_row(r, work, 0, ncol);        
     }
 
     /**
      * Get the number of rows in the matrix.
      */
     size_t get_nrow() const { return nrow; }
 
     /**
      * Get the number of columns in the matrix.
      */
     size_t get_ncol() const { return ncol; }
 
     /**
      * Is the matrix sparse?
      */
     virtual bool is_sparse() const { return false; }
 
     /**
      * Clone the current object, returning a pointer to a copy.
      */
     std::unique_ptr<lin_matrix> clone() const {
         return std::unique_ptr<lin_matrix>(this->clone_internal());
     }
 
 protected:
     size_t nrow=0, ncol=0;
 
     virtual lin_matrix* clone_internal() const = 0;
 };
 
 /**
e8086957
  * @brief Virtual base class for a sparse logical, integer or numeric (i.e., double-precision) matrix.
  *
  * This provides methods to extract rows or columns in dense or sparse form.
  * We suggest using the `read_lin_sparse_block()` function to construct instances of this class.
d83b9a7f
  */
 class lin_sparse_matrix : public lin_matrix {
 public:
     /**
      * Trivial constructor.
      */
     lin_sparse_matrix() {}
 
     ~lin_sparse_matrix() = default;
     lin_sparse_matrix(const lin_sparse_matrix&) = default;
     lin_sparse_matrix& operator=(const lin_sparse_matrix&) = default;
     lin_sparse_matrix(lin_sparse_matrix&&) = default;
     lin_sparse_matrix& operator=(lin_sparse_matrix&&) = default;
 
     /**
e8086957
      * Extract all non-zero elements in a row, restricted to a contiguous subset of columns.
      * Values are returned as integers.
d83b9a7f
      *
      * @param r Index of the row of interest.
      * @param work_x The workspace for extracted non-zero values.
      * This should have at least `last - first` addressable elements.
      * @param work_i The workspace for column indices.
      * This should have at least `last - first` addressable elements.
      * @param first The index of the first column of interest.
e8086957
      * @param last The index of one-past-the-last column of interest.
d83b9a7f
      *
e8086957
      * @return A `sparse_index` is returned containing pointers to the workspaces,
d83b9a7f
      * containing non-zero elements in `r` with column indices in `[first, last)`.
e8086957
      * A copy of non-zero values and their indices is always performed into the workspaces.
d83b9a7f
      */
     virtual sparse_index<const int*, int> get_row(size_t r, int* work_x, int* work_i, size_t first, size_t last) = 0;
 
     /**
e8086957
      * Extract all non-zero elements in a column, restricted to a contiguous subset of rows.
      * Values are returned as integers.
d83b9a7f
      *
      * @param r Index of the column of interest.
      * @param work_x The workspace for extracted non-zero values.
      * This should have at least `last - first` addressable elements.
      * @param work_i The workspace for column indices.
      * This should have at least `last - first` addressable elements.
      * @param first The index of the first row of interest.
e8086957
      * @param last The index of one-past-the-last row of interest.
d83b9a7f
      *
      * @return A `sparse_index` is returned containing pointers to non-zero elements in `c` with row indices in `[first, last)`.
      * This may or may not involve populating `work` with values copied from the underlying matrix;
e8086957
      * a copy will have been performed iff the return value's pointers compare equal to `work_x` and `work_i`.
d83b9a7f
      */
     virtual sparse_index<const int*, int> get_col(size_t c, int* work_x, int* work_i, size_t first, size_t last) = 0;
 
     /**
e8086957
      * Extract all non-zero elements in a row, restricted to a contiguous subset of columns.
      * Values are returned as doubles.
d83b9a7f
      *
      * @param r Index of the row of interest.
      * @param work_x The workspace for extracted non-zero values.
      * This should have at least `last - first` addressable elements.
      * @param work_i The workspace for column indices.
      * This should have at least `last - first` addressable elements.
      * @param first The index of the first column of interest.
e8086957
      * @param last The index of one-past-the-last column of interest.
d83b9a7f
      *
e8086957
      * @return A `sparse_index` is returned containing pointers to the workspaces.
d83b9a7f
      * containing non-zero elements in `r` with column indices in `[first, last)`.
e8086957
      * A copy of non-zero values and their indices is always performed into the workspaces.
d83b9a7f
      */
     virtual sparse_index<const double*, int> get_row(size_t r, double* work_x, int* work_i, size_t first, size_t last) = 0;
 
     /**
e8086957
      * Extract all non-zero elements in a column, restricted to a contiguous subset of rows.
      * Values are returned as doubles.
d83b9a7f
      *
      * @param r Index of the column of interest.
      * @param work_x The workspace for extracted non-zero values.
      * This should have at least `last - first` addressable elements.
      * @param work_i The workspace for column indices.
      * This should have at least `last - first` addressable elements.
      * @param first The index of the first row of interest.
e8086957
      * @param last The index of one-past-the-last row of interest.
d83b9a7f
      *
      * @return A `sparse_index` is returned containing pointers to non-zero elements in `c` with row indices in `[first, last)`.
      * This may or may not involve populating `work` with values copied from the underlying matrix;
e8086957
      * a copy will have been performed iff the return value's pointers compare equal to `work_x` and `work_i`.
d83b9a7f
      */
     virtual sparse_index<const double*, int> get_col(size_t c, double* work_x, int* work_i, size_t first, size_t last) = 0;
 
     /**
      * Extract all non-zero elements in a column, storing values as integers.
      *
      * @param r Index of the column of interest.
      * @param work_x The workspace for extracted non-zero values.
      * This should have at least `last - first` addressable elements.
      * @param work_i The workspace for column indices.
      * This should have at least `last - first` addressable elements.
      *
      * @return A `sparse_index` is returned containing pointers to all non-zero elements in `c`.
      * This may or may not involve populating `work` with values copied from the underlying matrix;
e8086957
      * a copy will have been performed iff the return value's pointers compare equal to `work_x` and `work_i`.
d83b9a7f
      */
     sparse_index<const int*, int> get_col(size_t c, int* work_x, int* work_i) {
         return get_col(c, work_x, work_i, 0, this->nrow);
     }
 
     /**
      * Extract all non-zero elements in a row, storing values as integers.
      *
      * @param r Index of the row of interest.
      * @param work_x The workspace for extracted non-zero values.
      * This should have at least `last - first` addressable elements.
      * @param work_i The workspace for column indices.
      * This should have at least `last - first` addressable elements.
      *
e8086957
      * @return A `sparse_index` is returned containing pointers to the workspaces,
d83b9a7f
      * containing all non-zero elements in `r`.
e8086957
      * A copy of non-zero values and their indices is always performed into the workspaces.
d83b9a7f
      */
     sparse_index<const int*, int> get_row(size_t r, int* work_x, int* work_i) {
         return get_row(r, work_x, work_i, 0, this->ncol);
     }
 
     /**
      * Extract all non-zero elements in a column, storing values as doubles.
      *
      * @param r Index of the column of interest.
      * @param work_x The workspace for extracted non-zero values.
      * This should have at least `last - first` addressable elements.
      * @param work_i The workspace for column indices.
      * This should have at least `last - first` addressable elements.
      *
      * @return A `sparse_index` is returned containing pointers to all non-zero elements in `c`.
      * This may or may not involve populating `work` with values copied from the underlying matrix;
e8086957
      * a copy will have been performed iff the return value's pointers compare equal to `work_x` and `work_i`,
d83b9a7f
      */
     sparse_index<const double*, int> get_col(size_t c, double* work_x, int* work_i) {
         return get_col(c, work_x, work_i, 0, this->nrow);
     }
 
     /**
      * Extract all non-zero elements in a row, storing values as doubles.
      *
      * @param r Index of the row of interest.
      * @param work_x The workspace for extracted non-zero values.
      * This should have at least `last - first` addressable elements.
      * @param work_i The workspace for column indices.
      * This should have at least `last - first` addressable elements.
      *
      * @return A `sparse_index` is returned containing pointers to the workspace,
      * containing all non-zero elements in `r`.
e8086957
      * A copy of non-zero values and their indices is always performed into the workspaces.
d83b9a7f
      */
     sparse_index<const double*, int> get_row(size_t r, double* work_x, int* work_i) {
         return get_row(r, work_x, work_i, 0, this->ncol);
     }
 
     bool is_sparse() const { return true; }
 
     /**
      * Get the number of non-zero elements in the matrix.
      */
     virtual size_t get_nnzero() const = 0;
 
     /**
      * Clone the current object, returning a pointer to a copy.
      */
     std::unique_ptr<lin_sparse_matrix> clone() const {
         return std::unique_ptr<lin_sparse_matrix>(this->clone_internal());
     }
 protected:
     lin_sparse_matrix* clone_internal() const = 0;
 };
 
 /**
  * @brief Logical, integer or numeric matrices in the "ordinary" R format, i.e., column-major dense arrays.
  *
e8086957
  * It is unlikely that this class will be constructed directly by users;
  * most applications will use `read_lin_block()` instead.
  *
d83b9a7f
  * @tparam V The class of the `Rcpp::Vector` holding the R-level data.
  */
 template <class V>
 class lin_ordinary_matrix : public lin_matrix {
 public:
     /**
      * Constructor from an ordinary R-level matrix.
      *
      * @param mat An ordinary R matrix.
      */
     lin_ordinary_matrix(Rcpp::RObject mat) : reader(mat) {
         this->nrow = reader.get_nrow();
         this->ncol = reader.get_ncol();
         return;
     }
 
     ~lin_ordinary_matrix() = default;
     lin_ordinary_matrix(const lin_ordinary_matrix&) = default;
     lin_ordinary_matrix& operator=(const lin_ordinary_matrix&) = default;
     lin_ordinary_matrix(lin_ordinary_matrix&&) = default;
     lin_ordinary_matrix& operator=(lin_ordinary_matrix&&) = default;
 
     const int* get_col(size_t c, int* work, size_t first, size_t last);
 
     const int* get_row(size_t r, int* work, size_t first, size_t last) {
         reader.get_row(r, work, first, last);       
         return work;
     }
 
     const double* get_col(size_t c, double* work, size_t first, size_t last);
 
     const double* get_row(size_t r, double* work, size_t first, size_t last) {
         reader.get_row(r, work, first, last); 
         return work;
     }
 private:
     ordinary_reader<V> reader;
 
     lin_ordinary_matrix<V>* clone_internal() const {
         return new lin_ordinary_matrix<V>(*this);
     }
 };
 
 using integer_ordinary_matrix = lin_ordinary_matrix<Rcpp::IntegerVector>;
 
 template <>
 inline const int* integer_ordinary_matrix::get_col(size_t c, int* work, size_t first, size_t last) {
     return reader.get_col(c, first, last);
 }
 
 template <>
 inline const double* integer_ordinary_matrix::get_col(size_t c, double* work, size_t first, size_t last) {
     auto out = reader.get_col(c, first, last);
     std::copy(out, out + last - first, work);
     return work;
 }
 
 using logical_ordinary_matrix = lin_ordinary_matrix<Rcpp::LogicalVector>;
 
 template <>
 inline const int* logical_ordinary_matrix::get_col(size_t c, int* work, size_t first, size_t last) {
     return reader.get_col(c, first, last);
 }
 
 template <>
 inline const double* logical_ordinary_matrix::get_col(size_t c, double* work, size_t first, size_t last) {
     auto out = reader.get_col(c, first, last);
     std::copy(out, out + last - first, work);
     return work;
 }
 
 using double_ordinary_matrix = lin_ordinary_matrix<Rcpp::NumericVector>;
 
 template <>
 inline const int* double_ordinary_matrix::get_col(size_t c, int* work, size_t first, size_t last) {
     auto out = reader.get_col(c, first, last);
     std::copy(out, out + last - first, work);
     return work;
 }
 
 template <>
 inline const double* double_ordinary_matrix::get_col(size_t c, double* work, size_t first, size_t last) {
     return reader.get_col(c, first, last);
 }
 
 /**
  * @brief Sparse logical or numeric matrices in the `lgCMatrix` or `dgCMatrix` format, respectively, from the **Matrix** package.
  *
e8086957
  * It is unlikely that this class will be constructed directly by users;
  * most applications will use `read_lin_block()` or `read_lin_sparse_block()` instead.
  *
d83b9a7f
  * @tparam V The class of the `Rcpp::Vector` holding the R-level data for non-zero values.
  */
 template <class V, typename TIT>
 class gCMatrix : public lin_sparse_matrix {
 public:
     /**
      * Constructor from a `*gCMatrix`.
      *
      * @param mat A S4 object of the `dgCMatrix` or `lgCMatrix` class.
      */
     gCMatrix(Rcpp::RObject mat) : reader(mat) {
         this->nrow = reader.get_nrow();
         this->ncol = reader.get_ncol();
         return;
     }
    
     ~gCMatrix() = default;
     gCMatrix(const gCMatrix&) = default;
     gCMatrix& operator=(const gCMatrix&) = default;
     gCMatrix(gCMatrix&&) = default;
     gCMatrix& operator=(gCMatrix&&) = default;
 
     const int* get_col(size_t c, int* work, size_t first, size_t last) {
         reader.get_col(c, work, first, last);
         return work;        
     }
 
     const int* get_row(size_t r, int* work, size_t first, size_t last) {
         reader.get_row(r, work, first, last);       
         return work;
     }
 
     const double* get_col(size_t c, double* work, size_t first, size_t last) {
         reader.get_col(c, work, first, last);
         return work;
     }
 
     const double* get_row(size_t r, double* work, size_t first, size_t last) {
         reader.get_row(r, work, first, last); 
         return work;
     }
     
     sparse_index<const int*, int> get_col(size_t c, int* work_x, int* work_i, size_t first, size_t last);
 
     sparse_index<const int*, int> get_row(size_t r, int* work_x, int* work_i, size_t first, size_t last) {
         return reader.template get_row<const int*>(r, work_x, work_i, first, last);
     }
 
     sparse_index<const double*, int> get_col(size_t c, double* work_x, int* work_i, size_t first, size_t last);
 
     sparse_index<const double*, int> get_row(size_t r, double* work_x, int* work_i, size_t first, size_t last) {
         return reader.template get_row<const double*>(r, work_x, work_i, first, last);
     }
 
     size_t get_nnzero () const {
         return reader.get_nnzero();
     }
 private:
     gCMatrix_reader<V, TIT> reader;
 
     gCMatrix<V, TIT>* clone_internal() const {
         return new gCMatrix<V, TIT>(*this);
     }
 };
 
 using lgCMatrix = gCMatrix<Rcpp::LogicalVector, const int*>;
 
 template <>
 inline sparse_index<const int*, int> lgCMatrix::get_col(size_t c, int* work_x, int* work_i, size_t first, size_t last) {
     return reader.get_col(c, first, last);
 }
 
 template <>
 inline sparse_index<const double*, int> lgCMatrix::get_col(size_t c, double* work_x, int* work_i, size_t first, size_t last) {
     return transplant<const double*>(reader.get_col(c, first, last), work_x, work_i);
 }
 
 using dgCMatrix = gCMatrix<Rcpp::NumericVector, const double*>;
 
 template <>
 inline sparse_index<const int*, int> dgCMatrix::get_col(size_t c, int* work_x, int* work_i, size_t first, size_t last) {
     return transplant<const int*>(reader.get_col(c, first, last), work_x, work_i);
 }
 
 template <>
 inline sparse_index<const double*, int> dgCMatrix::get_col(size_t c, double* work_x, int* work_i, size_t first, size_t last) {
     return reader.get_col(c, first, last);
 }
 
 /**
  * @brief Sparse integer, logical or numeric matrices in the `SparseArraySeed` format from the **DelayedArray** package.
  *
e8086957
  * It is unlikely that this class will be constructed directly by users;
  * most applications will use `read_lin_block()` or `read_lin_sparse_block()` instead.
  *
d83b9a7f
  * @tparam V The class of the `Rcpp::Vector` holding the R-level data for non-zero values.
  */
 template <class V, typename TIT>
 class lin_SparseArraySeed : public lin_sparse_matrix {
 public:
     /**
      * Constructor from a `SparseArraySeed`.
      *
      * @param mat A S4 object of the `SparseArraySeed` class.
      */
     lin_SparseArraySeed(Rcpp::RObject mat) : reader(mat) {
         this->nrow = reader.get_nrow();
         this->ncol = reader.get_ncol();
         return;
     }
 
     ~lin_SparseArraySeed() = default;
     lin_SparseArraySeed(const lin_SparseArraySeed&) = default;
     lin_SparseArraySeed& operator=(const lin_SparseArraySeed&) = default;
     lin_SparseArraySeed(lin_SparseArraySeed&&) = default;
     lin_SparseArraySeed& operator=(lin_SparseArraySeed&&) = default;
 
     const int* get_col(size_t c, int* work, size_t first, size_t last) {
         reader.get_col(c, work, first, last);
         return work;        
     }
 
     const int* get_row(size_t r, int* work, size_t first, size_t last) {
         reader.get_row(r, work, first, last);       
         return work;
     }
 
     const double* get_col(size_t c, double* work, size_t first, size_t last) {
         reader.get_col(c, work, first, last);
         return work;
     }
 
     const double* get_row(size_t r, double* work, size_t first, size_t last) {
         reader.get_row(r, work, first, last); 
         return work;
     }
 
     sparse_index<const int*, int> get_col(size_t c, int* work_x, int* work_i, size_t first, size_t last);
 
     sparse_index<const int*, int> get_row(size_t r, int* work_x, int* work_i, size_t first, size_t last) {
         return reader.template get_row<const int*>(r, work_x, work_i, first, last);
     }
 
     sparse_index<const double*, int> get_col(size_t c, double* work_x, int* work_i, size_t first, size_t last);
 
     sparse_index<const double*, int> get_row(size_t r, double* work_x, int* work_i, size_t first, size_t last) {
         return reader.template get_row<const double*>(r, work_x, work_i, first, last);
     }
 
     size_t get_nnzero () const {
         return reader.get_nnzero();
     }
 private:
     SparseArraySeed_reader<V, TIT> reader;
 
     lin_SparseArraySeed<V, TIT>* clone_internal() const {
         return new lin_SparseArraySeed<V, TIT>(*this);
     }
 };
 
 using integer_SparseArraySeed = lin_SparseArraySeed<Rcpp::IntegerVector, const int*>;
 
 template <>
 inline sparse_index<const int*, int> integer_SparseArraySeed::get_col(size_t c, int* work_x, int* work_i, size_t first, size_t last) {
     return reader.get_col(c, first, last);
 }
 
 template <>
 inline sparse_index<const double*, int> integer_SparseArraySeed::get_col(size_t c, double* work_x, int* work_i, size_t first, size_t last)
 {
     return transplant<const double*>(reader.get_col(c, first, last), work_x, work_i);
 }
 
 using logical_SparseArraySeed = lin_SparseArraySeed<Rcpp::LogicalVector, const int*>;
 
 template <>
 inline sparse_index<const int*, int> logical_SparseArraySeed::get_col(size_t c, int* work_x, int* work_i, size_t first, size_t last) {
     return reader.get_col(c, first, last);
 }
 
 template <>
 inline sparse_index<const double*, int> logical_SparseArraySeed::get_col(size_t c, double* work_x, int* work_i, size_t first, size_t last)
 {
     return transplant<const double*>(reader.get_col(c, first, last), work_x, work_i);
 }
 
 using double_SparseArraySeed = lin_SparseArraySeed<Rcpp::NumericVector, const double*>;
 
 template <>
 inline sparse_index<const int*, int> double_SparseArraySeed::get_col(size_t c, int* work_x, int* work_i, size_t first, size_t last) {
     return transplant<const int*>(reader.get_col(c, first, last), work_x, work_i);
 }
 
 template <>
 inline sparse_index<const double*, int> double_SparseArraySeed::get_col(size_t c, double* work_x, int* work_i, size_t first, size_t last)
 {
     return reader.get_col(c, first, last);
 }
 
 }
 
 #endif