void SparseMatrix<T>::SubMatrixCols(SparseMatrix<T>& result,
                                    const std::vector<unsigned int>& col_indices) const
{
    // extract entire columns from the source matrix to form the dest matrix

    unsigned int new_width = col_indices.size();
    if (0u == new_width)
        throw std::logic_error("SparseMatrix: empty column set");
    
    // check the column indices for validty and count nonzeros

    unsigned int new_size = 0;
    for (auto it=col_indices.begin(); it != col_indices.end(); ++it)
    {
        // index of next source column
        unsigned int c = *it;
        if (c >= width_)
            throw std::logic_error("SparseMatrix::SubMatrix: column index out of range");
        
        // number of nonzeros in source column c
        new_size += (col_offsets_[c+1] - col_offsets_[c]);
    }

    if (0u == new_size)
        throw std::logic_error("SparseMatrix::SubMatrix: submatrix is the zero matrix");

    // allocate memory in the result; won't allocate if sufficient memory available
    result.Reserve(height_, new_width, new_size);

    unsigned int* cols_b = result.ColBuffer();
    unsigned int* rows_b = result.RowBuffer();
    T* data_b            = result.DataBuffer();

    unsigned int c_dest = 0;
    unsigned int elt_count = 0;
    for (auto it=col_indices.begin(); it != col_indices.end(); ++it)
    {
        // index of the next source column
        unsigned int c = *it;

        // set element offset for the next dest column
        cols_b[c_dest] = elt_count;

        unsigned int start = col_offsets_[c];
        unsigned int end   = col_offsets_[c+1];
        for (unsigned int offset = start; offset != end; ++offset)
        {
            rows_b[elt_count] = row_indices_[offset];
            data_b[elt_count] = data_[offset];
            ++elt_count;
        }

        // have now completed another column
        ++c_dest;
    }

    cols_b[new_width] = elt_count;
    assert(new_width == c_dest);

    // set the size of the new matrix explicitly, since Load() has been bypassed
    result.SetSize(elt_count);
}
void SparseMatrix<T>::SubMatrixColsCompact(SparseMatrix<T>& result,
                                           const std::vector<unsigned int>& col_indices,
                                           std::vector<unsigned int>& old_to_new_rows,
                                           std::vector<unsigned int>& new_to_old_rows) const
{
    const unsigned int UNUSED_ROW = 0xFFFFFFFF;

    // extract entire columns from the source matrix to form the dest matrix

    unsigned int new_width = col_indices.size();
    if (0u == new_width)
        throw std::logic_error("SparseMatrix::SubMatrixColsCompact: empty column set");
    
    // need one entry per original row in the row_map
    if (old_to_new_rows.size() < height_)
        old_to_new_rows.resize(height_);

    std::fill(old_to_new_rows.begin(), old_to_new_rows.begin() + height_, UNUSED_ROW);
    // no need to fill 'new_to_old_rows'

    // check the column indices for validty and count nonzeros

    unsigned int new_size = 0;
    for (auto it=col_indices.begin(); it != col_indices.end(); ++it)
    {
        // index of next source column
        unsigned int c = *it;
        if (c >= width_)
            throw std::logic_error("SparseMatrix::SubMatrixColsCompact: column index out of range");
        
        // number of nonzeros in source column c
        new_size += (col_offsets_[c+1] - col_offsets_[c]);
    }

    if (0u == new_size)
        throw std::logic_error("SparseMatrix::SubMatrixColsCompact: submatrix is the zero matrix");

    // allocate memory in the result; won't allocate if sufficient memory available
    result.Reserve(height_, new_width, new_size);

    unsigned int* cols_b = result.ColBuffer();
    unsigned int* rows_b = result.RowBuffer();
    T* data_b            = result.DataBuffer();

    unsigned int c_dest = 0;
    unsigned int elt_count = 0;
    for (auto it=col_indices.begin(); it != col_indices.end(); ++it)
    {
        // index of the next source column
        unsigned int c = *it;

        // set element offset for the next dest column
        cols_b[c_dest] = elt_count;

        unsigned int start = col_offsets_[c];
        unsigned int end   = col_offsets_[c+1];
        for (unsigned int offset = start; offset != end; ++offset)
        {
            unsigned int row = row_indices_[offset];
            old_to_new_rows[row] = row;
            rows_b[elt_count] = row;
            data_b[elt_count] = data_[offset];
            ++elt_count;
        }

        // have now completed another column
        ++c_dest;
    }

    cols_b[new_width] = elt_count;
    assert(new_width == c_dest);

    // set the size of the new matrix explicitly, since Load() has been bypassed
    result.SetSize(elt_count);

    // determine the new height of the submatrix
    unsigned int new_height = 0;
    for (unsigned int r=0; r != height_; ++r)
    {
        if (UNUSED_ROW != old_to_new_rows[r])
            ++new_height;
    }
    
    new_to_old_rows.resize(new_height);

    // renumber the rows in the submatrix

    unsigned int new_r = 0;
    for (unsigned int r=0; r<height_; ++r)
    {
        if (UNUSED_ROW != old_to_new_rows[r])
        {
            old_to_new_rows[r] = new_r;
            new_to_old_rows[new_r] = r;
            ++new_r;
        }
    }

    // set the height of the new matrix explicitly
    assert(new_r == new_height);
    result.SetHeight(new_height);

    // re-index the rows in the submatrix 
    for (unsigned int s=0; s != elt_count; ++s)
    {
        unsigned int old_row_index = rows_b[s];
        rows_b[s] = old_to_new_rows[old_row_index];
    }

    assert(result.Height() == new_height);
    assert(new_to_old_rows.size() == new_height);
    assert(old_to_new_rows.size() == height_);
}
void SparseMatrix<T>::SubMatrix(SparseMatrix<T>& result,
                                const unsigned int r0,
                                const unsigned int c0,
                                const unsigned int new_height,
                                const unsigned int new_width) const
{
    // one past the end of each range
    unsigned int r1 = r0 + new_height;
    unsigned int c1 = c0 + new_width;

    if ( (r0 >= height_) || (r1 > height_))
        throw std::logic_error("SubMatrix: invalid row limits");
    if ( (c0 >= width_) || (c1 > width_))
        throw std::logic_error("SubMatrix: invalid column limits");

    // count the number of nonzeros in the submatrix

    unsigned int new_size = 0;
    for (unsigned int c=c0; c != c1; ++c)
    {
        // number of nonzeros in source column c
        new_size += (col_offsets_[c+1] - col_offsets_[c]);
    }

    if (0u == new_size)
        throw std::logic_error("SparseMatrix::SubMatrix: submatrix is the zero matrix");

    // allocate memory in the result; won't allocate if sufficient memory available
    result.Reserve(new_height, new_width, new_size);

    // Load the elements within the row and column bounds into the new matrix.
    // No need to call result.Clear(), since that will result in a new allocation.

    unsigned int* cols_b = result.ColBuffer();
    unsigned int* rows_b = result.RowBuffer();
    T* data_b            = result.DataBuffer();

    unsigned int count = 0;
    for (unsigned int c=c0; c != c1; ++c)
    {
        cols_b[c-c0] = count;

        unsigned int start = col_offsets_[c];
        unsigned int end   = col_offsets_[c+1];
        for (unsigned int offset = start; offset != end; ++offset)
        {
            unsigned int row = row_indices_[offset];
            if ( (row >= r0) && (row < r1))
            {
                rows_b[count] = row - r0;
                data_b[count] = data_[offset];
                ++count;
            }
        }
    }

    cols_b[c1] = count;
    //assert(new_size == count);

    // set the size of the new matrix explicitly, since Load() has been bypassed
    result.SetSize(count);
}