diff options
author | Nao Pross <np@0hm.ch> | 2024-02-12 14:52:43 +0100 |
---|---|---|
committer | Nao Pross <np@0hm.ch> | 2024-02-12 14:52:43 +0100 |
commit | eda5bc26f44ee9a6f83dcf8c91f17296d7fc509d (patch) | |
tree | bc2efa38ff4e350f9a111ac87065cd7ae9a911c7 /src/armadillo/include/armadillo_bits/SpMat_meat.hpp | |
download | fsisotool-eda5bc26f44ee9a6f83dcf8c91f17296d7fc509d.tar.gz fsisotool-eda5bc26f44ee9a6f83dcf8c91f17296d7fc509d.zip |
Move into version control
Diffstat (limited to 'src/armadillo/include/armadillo_bits/SpMat_meat.hpp')
-rw-r--r-- | src/armadillo/include/armadillo_bits/SpMat_meat.hpp | 6855 |
1 files changed, 6855 insertions, 0 deletions
diff --git a/src/armadillo/include/armadillo_bits/SpMat_meat.hpp b/src/armadillo/include/armadillo_bits/SpMat_meat.hpp new file mode 100644 index 0000000..b8d51cf --- /dev/null +++ b/src/armadillo/include/armadillo_bits/SpMat_meat.hpp @@ -0,0 +1,6855 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright 2008-2016 Conrad Sanderson (http://conradsanderson.id.au) +// Copyright 2008-2016 National ICT Australia (NICTA) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ------------------------------------------------------------------------ + + +//! \addtogroup SpMat +//! @{ + + +/** + * Initialize a sparse matrix with size 0x0 (empty). + */ +template<typename eT> +inline +SpMat<eT>::SpMat() + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + init_cold(0,0); + } + + + +/** + * Clean up the memory of a sparse matrix and destruct it. + */ +template<typename eT> +inline +SpMat<eT>::~SpMat() + { + arma_extra_debug_sigprint_this(this); + + if(values ) { memory::release(access::rw(values)); } + if(row_indices) { memory::release(access::rw(row_indices)); } + if(col_ptrs ) { memory::release(access::rw(col_ptrs)); } + } + + + +/** + * Constructor with size given. + */ +template<typename eT> +inline +SpMat<eT>::SpMat(const uword in_rows, const uword in_cols) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + init_cold(in_rows, in_cols); + } + + + +template<typename eT> +inline +SpMat<eT>::SpMat(const SizeMat& s) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + init_cold(s.n_rows, s.n_cols); + } + + + +template<typename eT> +inline +SpMat<eT>::SpMat(const arma_reserve_indicator&, const uword in_rows, const uword in_cols, const uword new_n_nonzero) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + init_cold(in_rows, in_cols, new_n_nonzero); + } + + + +template<typename eT> +template<typename eT2> +inline +SpMat<eT>::SpMat(const arma_layout_indicator&, const SpMat<eT2>& x) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + init_cold(x.n_rows, x.n_cols, x.n_nonzero); + + if(x.n_nonzero == 0) { return; } + + if(x.row_indices) { arrayops::copy(access::rwp(row_indices), x.row_indices, x.n_nonzero + 1); } + if(x.col_ptrs ) { arrayops::copy(access::rwp(col_ptrs), x.col_ptrs, x.n_cols + 1); } + + // NOTE: 'values' array is not initialised + } + + + +/** + * Assemble from text. + */ +template<typename eT> +inline +SpMat<eT>::SpMat(const char* text) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + init(std::string(text)); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator=(const char* text) + { + arma_extra_debug_sigprint(); + + init(std::string(text)); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>::SpMat(const std::string& text) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint(); + + init(text); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator=(const std::string& text) + { + arma_extra_debug_sigprint(); + + init(text); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>::SpMat(const SpMat<eT>& x) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + init(x); + } + + + +template<typename eT> +inline +SpMat<eT>::SpMat(SpMat<eT>&& in_mat) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + arma_extra_debug_sigprint(arma_str::format("this = %x in_mat = %x") % this % &in_mat); + + (*this).steal_mem(in_mat); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator=(SpMat<eT>&& in_mat) + { + arma_extra_debug_sigprint(arma_str::format("this = %x in_mat = %x") % this % &in_mat); + + (*this).steal_mem(in_mat); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>::SpMat(const MapMat<eT>& x) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + init(x); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator=(const MapMat<eT>& x) + { + arma_extra_debug_sigprint(); + + init(x); + + return *this; + } + + + +//! Insert a large number of values at once. +//! locations.row[0] should be row indices, locations.row[1] should be column indices, +//! and values should be the corresponding values. +//! If sort_locations is false, then it is assumed that the locations and values +//! are already sorted in column-major ordering. +template<typename eT> +template<typename T1, typename T2> +inline +SpMat<eT>::SpMat(const Base<uword,T1>& locations_expr, const Base<eT,T2>& vals_expr, const bool sort_locations) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + const quasi_unwrap<T1> locs_tmp( locations_expr.get_ref() ); + const quasi_unwrap<T2> vals_tmp( vals_expr.get_ref() ); + + const Mat<uword>& locs = locs_tmp.M; + const Mat<eT>& vals = vals_tmp.M; + + arma_debug_check( (vals.is_vec() == false), "SpMat::SpMat(): given 'values' object must be a vector" ); + arma_debug_check( (locs.n_rows != 2), "SpMat::SpMat(): locations matrix must have two rows" ); + arma_debug_check( (locs.n_cols != vals.n_elem), "SpMat::SpMat(): number of locations is different than number of values" ); + + // If there are no elements in the list, max() will fail. + if(locs.n_cols == 0) { init_cold(0, 0); return; } + + // Automatically determine size before pruning zeros. + uvec bounds = arma::max(locs, 1); + init_cold(bounds[0] + 1, bounds[1] + 1); + + // Ensure that there are no zeros + const uword N_old = vals.n_elem; + uword N_new = 0; + + for(uword i=0; i < N_old; ++i) { N_new += (vals[i] != eT(0)) ? uword(1) : uword(0); } + + if(N_new != N_old) + { + Col<eT> filtered_vals( N_new, arma_nozeros_indicator()); + Mat<uword> filtered_locs(2, N_new, arma_nozeros_indicator()); + + uword index = 0; + for(uword i = 0; i < N_old; ++i) + { + if(vals[i] != eT(0)) + { + filtered_vals[index] = vals[i]; + + filtered_locs.at(0, index) = locs.at(0, i); + filtered_locs.at(1, index) = locs.at(1, i); + + ++index; + } + } + + init_batch_std(filtered_locs, filtered_vals, sort_locations); + } + else + { + init_batch_std(locs, vals, sort_locations); + } + } + + + +//! Insert a large number of values at once. +//! locations.row[0] should be row indices, locations.row[1] should be column indices, +//! and values should be the corresponding values. +//! If sort_locations is false, then it is assumed that the locations and values +//! are already sorted in column-major ordering. +//! In this constructor the size is explicitly given. +template<typename eT> +template<typename T1, typename T2> +inline +SpMat<eT>::SpMat(const Base<uword,T1>& locations_expr, const Base<eT,T2>& vals_expr, const uword in_n_rows, const uword in_n_cols, const bool sort_locations, const bool check_for_zeros) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + const quasi_unwrap<T1> locs_tmp( locations_expr.get_ref() ); + const quasi_unwrap<T2> vals_tmp( vals_expr.get_ref() ); + + const Mat<uword>& locs = locs_tmp.M; + const Mat<eT>& vals = vals_tmp.M; + + arma_debug_check( (vals.is_vec() == false), "SpMat::SpMat(): given 'values' object must be a vector" ); + arma_debug_check( (locs.n_rows != 2), "SpMat::SpMat(): locations matrix must have two rows" ); + arma_debug_check( (locs.n_cols != vals.n_elem), "SpMat::SpMat(): number of locations is different than number of values" ); + + init_cold(in_n_rows, in_n_cols); + + // Ensure that there are no zeros, unless the user asked not to. + if(check_for_zeros) + { + const uword N_old = vals.n_elem; + uword N_new = 0; + + for(uword i=0; i < N_old; ++i) { N_new += (vals[i] != eT(0)) ? uword(1) : uword(0); } + + if(N_new != N_old) + { + Col<eT> filtered_vals( N_new, arma_nozeros_indicator()); + Mat<uword> filtered_locs(2, N_new, arma_nozeros_indicator()); + + uword index = 0; + for(uword i = 0; i < N_old; ++i) + { + if(vals[i] != eT(0)) + { + filtered_vals[index] = vals[i]; + + filtered_locs.at(0, index) = locs.at(0, i); + filtered_locs.at(1, index) = locs.at(1, i); + + ++index; + } + } + + init_batch_std(filtered_locs, filtered_vals, sort_locations); + } + else + { + init_batch_std(locs, vals, sort_locations); + } + } + else + { + init_batch_std(locs, vals, sort_locations); + } + } + + + +template<typename eT> +template<typename T1, typename T2> +inline +SpMat<eT>::SpMat(const bool add_values, const Base<uword,T1>& locations_expr, const Base<eT,T2>& vals_expr, const uword in_n_rows, const uword in_n_cols, const bool sort_locations, const bool check_for_zeros) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + const quasi_unwrap<T1> locs_tmp( locations_expr.get_ref() ); + const quasi_unwrap<T2> vals_tmp( vals_expr.get_ref() ); + + const Mat<uword>& locs = locs_tmp.M; + const Mat<eT>& vals = vals_tmp.M; + + arma_debug_check( (vals.is_vec() == false), "SpMat::SpMat(): given 'values' object must be a vector" ); + arma_debug_check( (locs.n_rows != 2), "SpMat::SpMat(): locations matrix must have two rows" ); + arma_debug_check( (locs.n_cols != vals.n_elem), "SpMat::SpMat(): number of locations is different than number of values" ); + + init_cold(in_n_rows, in_n_cols); + + // Ensure that there are no zeros, unless the user asked not to. + if(check_for_zeros) + { + const uword N_old = vals.n_elem; + uword N_new = 0; + + for(uword i=0; i < N_old; ++i) { N_new += (vals[i] != eT(0)) ? uword(1) : uword(0); } + + if(N_new != N_old) + { + Col<eT> filtered_vals( N_new, arma_nozeros_indicator()); + Mat<uword> filtered_locs(2, N_new, arma_nozeros_indicator()); + + uword index = 0; + for(uword i = 0; i < N_old; ++i) + { + if(vals[i] != eT(0)) + { + filtered_vals[index] = vals[i]; + + filtered_locs.at(0, index) = locs.at(0, i); + filtered_locs.at(1, index) = locs.at(1, i); + + ++index; + } + } + + add_values ? init_batch_add(filtered_locs, filtered_vals, sort_locations) : init_batch_std(filtered_locs, filtered_vals, sort_locations); + } + else + { + add_values ? init_batch_add(locs, vals, sort_locations) : init_batch_std(locs, vals, sort_locations); + } + } + else + { + add_values ? init_batch_add(locs, vals, sort_locations) : init_batch_std(locs, vals, sort_locations); + } + } + + + +//! Insert a large number of values at once. +//! Per CSC format, rowind_expr should be row indices, +//! colptr_expr should column ptr indices locations, +//! and values should be the corresponding values. +//! In this constructor the size is explicitly given. +//! Values are assumed to be sorted, and the size +//! information is trusted +template<typename eT> +template<typename T1, typename T2, typename T3> +inline +SpMat<eT>::SpMat + ( + const Base<uword,T1>& rowind_expr, + const Base<uword,T2>& colptr_expr, + const Base<eT, T3>& values_expr, + const uword in_n_rows, + const uword in_n_cols, + const bool check_for_zeros + ) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + const quasi_unwrap<T1> rowind_tmp( rowind_expr.get_ref() ); + const quasi_unwrap<T2> colptr_tmp( colptr_expr.get_ref() ); + const quasi_unwrap<T3> vals_tmp( values_expr.get_ref() ); + + const Mat<uword>& rowind = rowind_tmp.M; + const Mat<uword>& colptr = colptr_tmp.M; + const Mat<eT>& vals = vals_tmp.M; + + arma_debug_check( (rowind.is_vec() == false), "SpMat::SpMat(): given 'rowind' object must be a vector" ); + arma_debug_check( (colptr.is_vec() == false), "SpMat::SpMat(): given 'colptr' object must be a vector" ); + arma_debug_check( (vals.is_vec() == false), "SpMat::SpMat(): given 'values' object must be a vector" ); + + // Resize to correct number of elements (this also sets n_nonzero) + init_cold(in_n_rows, in_n_cols, vals.n_elem); + + arma_debug_check( (rowind.n_elem != vals.n_elem), "SpMat::SpMat(): number of row indices is not equal to number of values" ); + arma_debug_check( (colptr.n_elem != (n_cols+1) ), "SpMat::SpMat(): number of column pointers is not equal to n_cols+1" ); + + // copy supplied values into sparse matrix -- not checked for consistency + arrayops::copy(access::rwp(row_indices), rowind.memptr(), rowind.n_elem ); + arrayops::copy(access::rwp(col_ptrs), colptr.memptr(), colptr.n_elem ); + arrayops::copy(access::rwp(values), vals.memptr(), vals.n_elem ); + + // important: set the sentinel as well + access::rw(col_ptrs[n_cols + 1]) = std::numeric_limits<uword>::max(); + + // make sure no zeros are stored + if(check_for_zeros) { remove_zeros(); } + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator=(const eT val) + { + arma_extra_debug_sigprint(); + + if(val != eT(0)) + { + // Resize to 1x1 then set that to the right value. + init(1, 1, 1); // Sets col_ptrs to 0. + + // Manually set element. + access::rw(values[0]) = val; + access::rw(row_indices[0]) = 0; + access::rw(col_ptrs[1]) = 1; + } + else + { + init(0, 0); + } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator*=(const eT val) + { + arma_extra_debug_sigprint(); + + if(val != eT(0)) + { + sync_csc(); + invalidate_cache(); + + const uword n_nz = n_nonzero; + + eT* vals = access::rwp(values); + + bool has_zero = false; + + for(uword i=0; i<n_nz; ++i) + { + eT& vals_i = vals[i]; + + vals_i *= val; + + if(vals_i == eT(0)) { has_zero = true; } + } + + if(has_zero) { remove_zeros(); } + } + else + { + (*this).zeros(); + } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator/=(const eT val) + { + arma_extra_debug_sigprint(); + + arma_debug_check( (val == eT(0)), "element-wise division: division by zero" ); + + sync_csc(); + invalidate_cache(); + + const uword n_nz = n_nonzero; + + eT* vals = access::rwp(values); + + bool has_zero = false; + + for(uword i=0; i<n_nz; ++i) + { + eT& vals_i = vals[i]; + + vals_i /= val; + + if(vals_i == eT(0)) { has_zero = true; } + } + + if(has_zero) { remove_zeros(); } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator=(const SpMat<eT>& x) + { + arma_extra_debug_sigprint(); + + init(x); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator+=(const SpMat<eT>& x) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + SpMat<eT> out = (*this) + x; + + steal_mem(out); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator-=(const SpMat<eT>& x) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + SpMat<eT> out = (*this) - x; + + steal_mem(out); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator*=(const SpMat<eT>& y) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + SpMat<eT> z = (*this) * y; + + steal_mem(z); + + return *this; + } + + + +// This is in-place element-wise matrix multiplication. +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator%=(const SpMat<eT>& y) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + SpMat<eT> z = (*this) % y; + + steal_mem(z); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator/=(const SpMat<eT>& x) + { + arma_extra_debug_sigprint(); + + // NOTE: use of this function is not advised; it is implemented only for completeness + + arma_debug_assert_same_size(n_rows, n_cols, x.n_rows, x.n_cols, "element-wise division"); + + for(uword c = 0; c < n_cols; ++c) + for(uword r = 0; r < n_rows; ++r) + { + at(r, c) /= x.at(r, c); + } + + return *this; + } + + + +template<typename eT> +template<typename T1, typename op_type> +inline +SpMat<eT>::SpMat(const SpToDOp<T1, op_type>& expr) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + typedef typename T1::elem_type T; + + // Make sure the type is compatible. + arma_type_check(( is_same_type< eT, T >::no )); + + op_type::apply(*this, expr); + } + + + +// Construct a complex matrix out of two non-complex matrices +template<typename eT> +template<typename T1, typename T2> +inline +SpMat<eT>::SpMat + ( + const SpBase<typename SpMat<eT>::pod_type, T1>& A, + const SpBase<typename SpMat<eT>::pod_type, T2>& B + ) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint(); + + typedef typename T1::elem_type T; + + // Make sure eT is complex and T is not (compile-time check). + arma_type_check(( is_cx<eT>::no )); + arma_type_check(( is_cx< T>::yes )); + + // Compile-time abort if types are not compatible. + arma_type_check(( is_same_type< std::complex<T>, eT >::no )); + + const unwrap_spmat<T1> tmp1(A.get_ref()); + const unwrap_spmat<T2> tmp2(B.get_ref()); + + const SpMat<T>& X = tmp1.M; + const SpMat<T>& Y = tmp2.M; + + arma_debug_assert_same_size(X.n_rows, X.n_cols, Y.n_rows, Y.n_cols, "SpMat()"); + + const uword l_n_rows = X.n_rows; + const uword l_n_cols = X.n_cols; + + // Set size of matrix correctly. + init_cold(l_n_rows, l_n_cols, n_unique(X, Y, op_n_unique_count())); + + // Now on a second iteration, fill it. + typename SpMat<T>::const_iterator x_it = X.begin(); + typename SpMat<T>::const_iterator x_end = X.end(); + + typename SpMat<T>::const_iterator y_it = Y.begin(); + typename SpMat<T>::const_iterator y_end = Y.end(); + + uword cur_pos = 0; + + while((x_it != x_end) || (y_it != y_end)) + { + if(x_it == y_it) // if we are at the same place + { + access::rw(values[cur_pos]) = std::complex<T>((T) *x_it, (T) *y_it); + access::rw(row_indices[cur_pos]) = x_it.row(); + ++access::rw(col_ptrs[x_it.col() + 1]); + + ++x_it; + ++y_it; + } + else + { + if((x_it.col() < y_it.col()) || ((x_it.col() == y_it.col()) && (x_it.row() < y_it.row()))) // if y is closer to the end + { + access::rw(values[cur_pos]) = std::complex<T>((T) *x_it, T(0)); + access::rw(row_indices[cur_pos]) = x_it.row(); + ++access::rw(col_ptrs[x_it.col() + 1]); + + ++x_it; + } + else // x is closer to the end + { + access::rw(values[cur_pos]) = std::complex<T>(T(0), (T) *y_it); + access::rw(row_indices[cur_pos]) = y_it.row(); + ++access::rw(col_ptrs[y_it.col() + 1]); + + ++y_it; + } + } + + ++cur_pos; + } + + // Now fix the column pointers; they are supposed to be a sum. + for(uword c = 1; c <= n_cols; ++c) + { + access::rw(col_ptrs[c]) += col_ptrs[c - 1]; + } + + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>::SpMat(const Base<eT, T1>& x) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + (*this).operator=(x); + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator=(const Base<eT, T1>& expr) + { + arma_extra_debug_sigprint(); + + if(is_same_type< T1, Gen<Mat<eT>, gen_zeros> >::yes) + { + const Proxy<T1> P(expr.get_ref()); + + (*this).zeros( P.get_n_rows(), P.get_n_cols() ); + + return *this; + } + + if(is_same_type< T1, Gen<Mat<eT>, gen_eye> >::yes) + { + const Proxy<T1> P(expr.get_ref()); + + (*this).eye( P.get_n_rows(), P.get_n_cols() ); + + return *this; + } + + const quasi_unwrap<T1> tmp(expr.get_ref()); + const Mat<eT>& x = tmp.M; + + const uword x_n_rows = x.n_rows; + const uword x_n_cols = x.n_cols; + const uword x_n_elem = x.n_elem; + + // Count number of nonzero elements in base object. + uword n = 0; + + const eT* x_mem = x.memptr(); + + for(uword i=0; i < x_n_elem; ++i) { n += (x_mem[i] != eT(0)) ? uword(1) : uword(0); } + + init(x_n_rows, x_n_cols, n); + + if(n == 0) { return *this; } + + // Now the memory is resized correctly; set nonzero elements. + n = 0; + for(uword j = 0; j < x_n_cols; ++j) + for(uword i = 0; i < x_n_rows; ++i) + { + const eT val = (*x_mem); x_mem++; + + if(val != eT(0)) + { + access::rw(values[n]) = val; + access::rw(row_indices[n]) = i; + access::rw(col_ptrs[j + 1])++; + ++n; + } + } + + // Sum column counts to be column pointers. + for(uword c = 1; c <= n_cols; ++c) + { + access::rw(col_ptrs[c]) += col_ptrs[c - 1]; + } + + return *this; + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator+=(const Base<eT, T1>& x) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + return (*this).operator=( (*this) + x.get_ref() ); + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator-=(const Base<eT, T1>& x) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + return (*this).operator=( (*this) - x.get_ref() ); + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator*=(const Base<eT, T1>& x) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + return (*this).operator=( (*this) * x.get_ref() ); + } + + + +// NOTE: use of this function is not advised; it is implemented only for completeness +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator/=(const Base<eT, T1>& x) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + SpMat<eT> tmp = (*this) / x.get_ref(); + + steal_mem(tmp); + + return *this; + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator%=(const Base<eT, T1>& x) + { + arma_extra_debug_sigprint(); + + const quasi_unwrap<T1> U(x.get_ref()); + const Mat<eT>& B = U.M; + + arma_debug_assert_same_size(n_rows, n_cols, B.n_rows, B.n_cols, "element-wise multiplication"); + + sync_csc(); + invalidate_cache(); + + constexpr eT zero = eT(0); + + bool has_zero = false; + + for(uword c=0; c < n_cols; ++c) + { + const uword index_start = col_ptrs[c ]; + const uword index_end = col_ptrs[c + 1]; + + for(uword i=index_start; i < index_end; ++i) + { + const uword r = row_indices[i]; + + eT& val = access::rw(values[i]); + + const eT result = val * B.at(r,c); + + val = result; + + if(result == zero) { has_zero = true; } + } + } + + if(has_zero) { remove_zeros(); } + + return *this; + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>::SpMat(const Op<T1, op_diagmat>& expr) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + (*this).operator=(expr); + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator=(const Op<T1, op_diagmat>& expr) + { + arma_extra_debug_sigprint(); + + const diagmat_proxy<T1> P(expr.m); + + const uword max_n_nonzero = (std::min)(P.n_rows, P.n_cols); + + // resize memory to upper bound + init(P.n_rows, P.n_cols, max_n_nonzero); + + uword count = 0; + + for(uword i=0; i < max_n_nonzero; ++i) + { + const eT val = P[i]; + + if(val != eT(0)) + { + access::rw(values[count]) = val; + access::rw(row_indices[count]) = i; + access::rw(col_ptrs[i + 1])++; + ++count; + } + } + + // fix column pointers to be cumulative + for(uword i = 1; i < n_cols + 1; ++i) + { + access::rw(col_ptrs[i]) += col_ptrs[i - 1]; + } + + // quick resize without reallocating memory and copying data + access::rw( n_nonzero) = count; + access::rw( values[count]) = eT(0); + access::rw(row_indices[count]) = uword(0); + + return *this; + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator+=(const Op<T1, op_diagmat>& expr) + { + arma_extra_debug_sigprint(); + + const SpMat<eT> tmp(expr); + + return (*this).operator+=(tmp); + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator-=(const Op<T1, op_diagmat>& expr) + { + arma_extra_debug_sigprint(); + + const SpMat<eT> tmp(expr); + + return (*this).operator-=(tmp); + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator*=(const Op<T1, op_diagmat>& expr) + { + arma_extra_debug_sigprint(); + + const SpMat<eT> tmp(expr); + + return (*this).operator*=(tmp); + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator/=(const Op<T1, op_diagmat>& expr) + { + arma_extra_debug_sigprint(); + + const SpMat<eT> tmp(expr); + + return (*this).operator/=(tmp); + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator%=(const Op<T1, op_diagmat>& expr) + { + arma_extra_debug_sigprint(); + + const SpMat<eT> tmp(expr); + + return (*this).operator%=(tmp); + } + + + +/** + * Functions on subviews. + */ +template<typename eT> +inline +SpMat<eT>::SpMat(const SpSubview<eT>& X) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + (*this).operator=(X); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator=(const SpSubview<eT>& X) + { + arma_extra_debug_sigprint(); + + if(X.n_nonzero == 0) { zeros(X.n_rows, X.n_cols); return *this; } + + X.m.sync_csc(); + + const bool alias = (this == &(X.m)); + + if(alias) + { + SpMat<eT> tmp(X); + + steal_mem(tmp); + } + else + { + init(X.n_rows, X.n_cols, X.n_nonzero); + + if(X.n_rows == X.m.n_rows) + { + const uword sv_col_start = X.aux_col1; + const uword sv_col_end = X.aux_col1 + X.n_cols - 1; + + typename SpMat<eT>::const_col_iterator m_it = X.m.begin_col_no_sync(sv_col_start); + typename SpMat<eT>::const_col_iterator m_it_end = X.m.end_col_no_sync(sv_col_end); + + uword count = 0; + + while(m_it != m_it_end) + { + const uword m_it_col_adjusted = m_it.col() - sv_col_start; + + access::rw(row_indices[count]) = m_it.row(); + access::rw(values[count]) = (*m_it); + ++access::rw(col_ptrs[m_it_col_adjusted + 1]); + + count++; + + ++m_it; + } + } + else + { + typename SpSubview<eT>::const_iterator it = X.begin(); + typename SpSubview<eT>::const_iterator it_end = X.end(); + + while(it != it_end) + { + const uword it_pos = it.pos(); + + access::rw(row_indices[it_pos]) = it.row(); + access::rw(values[it_pos]) = (*it); + ++access::rw(col_ptrs[it.col() + 1]); + ++it; + } + } + + // Now sum column pointers. + for(uword c = 1; c <= n_cols; ++c) + { + access::rw(col_ptrs[c]) += col_ptrs[c - 1]; + } + } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator+=(const SpSubview<eT>& X) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + SpMat<eT> tmp = (*this) + X; + + steal_mem(tmp); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator-=(const SpSubview<eT>& X) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + SpMat<eT> tmp = (*this) - X; + + steal_mem(tmp); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator*=(const SpSubview<eT>& y) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + SpMat<eT> z = (*this) * y; + + steal_mem(z); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator%=(const SpSubview<eT>& x) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + SpMat<eT> tmp = (*this) % x; + + steal_mem(tmp); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator/=(const SpSubview<eT>& x) + { + arma_extra_debug_sigprint(); + + arma_debug_assert_same_size(n_rows, n_cols, x.n_rows, x.n_cols, "element-wise division"); + + // There is no pretty way to do this. + for(uword elem = 0; elem < n_elem; elem++) + { + at(elem) /= x(elem); + } + + return *this; + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>::SpMat(const SpSubview_col_list<eT,T1>& X) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + SpSubview_col_list<eT,T1>::extract(*this, X); + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator=(const SpSubview_col_list<eT,T1>& X) + { + arma_extra_debug_sigprint(); + + const bool alias = (this == &(X.m)); + + if(alias == false) + { + SpSubview_col_list<eT,T1>::extract(*this, X); + } + else + { + SpMat<eT> tmp(X); + + steal_mem(tmp); + } + + return *this; + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator+=(const SpSubview_col_list<eT,T1>& X) + { + arma_extra_debug_sigprint(); + + SpSubview_col_list<eT,T1>::plus_inplace(*this, X); + + return *this; + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator-=(const SpSubview_col_list<eT,T1>& X) + { + arma_extra_debug_sigprint(); + + SpSubview_col_list<eT,T1>::minus_inplace(*this, X); + + return *this; + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator*=(const SpSubview_col_list<eT,T1>& X) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + SpMat<eT> z = (*this) * X; + + steal_mem(z); + + return *this; + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator%=(const SpSubview_col_list<eT,T1>& X) + { + arma_extra_debug_sigprint(); + + SpSubview_col_list<eT,T1>::schur_inplace(*this, X); + + return *this; + } + + + +template<typename eT> +template<typename T1> +inline +SpMat<eT>& +SpMat<eT>::operator/=(const SpSubview_col_list<eT,T1>& X) + { + arma_extra_debug_sigprint(); + + SpSubview_col_list<eT,T1>::div_inplace(*this, X); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>::SpMat(const spdiagview<eT>& X) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + spdiagview<eT>::extract(*this, X); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator=(const spdiagview<eT>& X) + { + arma_extra_debug_sigprint(); + + spdiagview<eT>::extract(*this, X); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator+=(const spdiagview<eT>& X) + { + arma_extra_debug_sigprint(); + + const SpMat<eT> tmp(X); + + return (*this).operator+=(tmp); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator-=(const spdiagview<eT>& X) + { + arma_extra_debug_sigprint(); + + const SpMat<eT> tmp(X); + + return (*this).operator-=(tmp); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator*=(const spdiagview<eT>& X) + { + arma_extra_debug_sigprint(); + + const SpMat<eT> tmp(X); + + return (*this).operator*=(tmp); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator%=(const spdiagview<eT>& X) + { + arma_extra_debug_sigprint(); + + const SpMat<eT> tmp(X); + + return (*this).operator%=(tmp); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::operator/=(const spdiagview<eT>& X) + { + arma_extra_debug_sigprint(); + + const SpMat<eT> tmp(X); + + return (*this).operator/=(tmp); + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>::SpMat(const SpOp<T1, spop_type>& X) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) // set in application of sparse operation + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + spop_type::apply(*this, X); + + sync_csc(); // in case apply() used element accessors + invalidate_cache(); // in case apply() modified the CSC representation + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>& +SpMat<eT>::operator=(const SpOp<T1, spop_type>& X) + { + arma_extra_debug_sigprint(); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + spop_type::apply(*this, X); + + sync_csc(); // in case apply() used element accessors + invalidate_cache(); // in case apply() modified the CSC representation + + return *this; + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>& +SpMat<eT>::operator+=(const SpOp<T1, spop_type>& X) + { + arma_extra_debug_sigprint(); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator+=(m); + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>& +SpMat<eT>::operator-=(const SpOp<T1, spop_type>& X) + { + arma_extra_debug_sigprint(); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator-=(m); + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>& +SpMat<eT>::operator*=(const SpOp<T1, spop_type>& X) + { + arma_extra_debug_sigprint(); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator*=(m); + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>& +SpMat<eT>::operator%=(const SpOp<T1, spop_type>& X) + { + arma_extra_debug_sigprint(); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator%=(m); + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>& +SpMat<eT>::operator/=(const SpOp<T1, spop_type>& X) + { + arma_extra_debug_sigprint(); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator/=(m); + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>::SpMat(const SpGlue<T1, T2, spglue_type>& X) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + spglue_type::apply(*this, X); + + sync_csc(); // in case apply() used element accessors + invalidate_cache(); // in case apply() modified the CSC representation + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>& +SpMat<eT>::operator=(const SpGlue<T1, T2, spglue_type>& X) + { + arma_extra_debug_sigprint(); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + spglue_type::apply(*this, X); + + sync_csc(); // in case apply() used element accessors + invalidate_cache(); // in case apply() modified the CSC representation + + return *this; + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>& +SpMat<eT>::operator+=(const SpGlue<T1, T2, spglue_type>& X) + { + arma_extra_debug_sigprint(); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator+=(m); + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>& +SpMat<eT>::operator-=(const SpGlue<T1, T2, spglue_type>& X) + { + arma_extra_debug_sigprint(); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator-=(m); + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>& +SpMat<eT>::operator*=(const SpGlue<T1, T2, spglue_type>& X) + { + arma_extra_debug_sigprint(); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator*=(m); + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>& +SpMat<eT>::operator%=(const SpGlue<T1, T2, spglue_type>& X) + { + arma_extra_debug_sigprint(); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator%=(m); + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>& +SpMat<eT>::operator/=(const SpGlue<T1, T2, spglue_type>& X) + { + arma_extra_debug_sigprint(); + + arma_type_check(( is_same_type< eT, typename T1::elem_type >::no )); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator/=(m); + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>::SpMat(const mtSpOp<eT, T1, spop_type>& X) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + spop_type::apply(*this, X); + + sync_csc(); // in case apply() used element accessors + invalidate_cache(); // in case apply() modified the CSC representation + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>& +SpMat<eT>::operator=(const mtSpOp<eT, T1, spop_type>& X) + { + arma_extra_debug_sigprint(); + + spop_type::apply(*this, X); + + sync_csc(); // in case apply() used element accessors + invalidate_cache(); // in case apply() modified the CSC representation + + return *this; + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>& +SpMat<eT>::operator+=(const mtSpOp<eT, T1, spop_type>& X) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator+=(m); + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>& +SpMat<eT>::operator-=(const mtSpOp<eT, T1, spop_type>& X) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator-=(m); + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>& +SpMat<eT>::operator*=(const mtSpOp<eT, T1, spop_type>& X) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator*=(m); + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>& +SpMat<eT>::operator%=(const mtSpOp<eT, T1, spop_type>& X) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator%=(m); + } + + + +template<typename eT> +template<typename T1, typename spop_type> +inline +SpMat<eT>& +SpMat<eT>::operator/=(const mtSpOp<eT, T1, spop_type>& X) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator/=(m); + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>::SpMat(const mtSpGlue<eT, T1, T2, spglue_type>& X) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(0) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + spglue_type::apply(*this, X); + + sync_csc(); // in case apply() used element accessors + invalidate_cache(); // in case apply() modified the CSC representation + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>& +SpMat<eT>::operator=(const mtSpGlue<eT, T1, T2, spglue_type>& X) + { + arma_extra_debug_sigprint(); + + spglue_type::apply(*this, X); + + sync_csc(); // in case apply() used element accessors + invalidate_cache(); // in case apply() modified the CSC representation + + return *this; + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>& +SpMat<eT>::operator+=(const mtSpGlue<eT, T1, T2, spglue_type>& X) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator+=(m); + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>& +SpMat<eT>::operator-=(const mtSpGlue<eT, T1, T2, spglue_type>& X) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator-=(m); + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>& +SpMat<eT>::operator*=(const mtSpGlue<eT, T1, T2, spglue_type>& X) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator*=(m); + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>& +SpMat<eT>::operator%=(const mtSpGlue<eT, T1, T2, spglue_type>& X) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator%=(m); + } + + + +template<typename eT> +template<typename T1, typename T2, typename spglue_type> +inline +SpMat<eT>& +SpMat<eT>::operator/=(const mtSpGlue<eT, T1, T2, spglue_type>& X) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + const SpMat<eT> m(X); + + return (*this).operator/=(m); + } + + + +template<typename eT> +arma_inline +SpSubview_row<eT> +SpMat<eT>::row(const uword row_num) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds(row_num >= n_rows, "SpMat::row(): out of bounds"); + + return SpSubview_row<eT>(*this, row_num); + } + + + +template<typename eT> +arma_inline +const SpSubview_row<eT> +SpMat<eT>::row(const uword row_num) const + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds(row_num >= n_rows, "SpMat::row(): out of bounds"); + + return SpSubview_row<eT>(*this, row_num); + } + + + +template<typename eT> +inline +SpSubview_row<eT> +SpMat<eT>::operator()(const uword row_num, const span& col_span) + { + arma_extra_debug_sigprint(); + + const bool col_all = col_span.whole; + + const uword local_n_cols = n_cols; + + const uword in_col1 = col_all ? 0 : col_span.a; + const uword in_col2 = col_span.b; + const uword submat_n_cols = col_all ? local_n_cols : in_col2 - in_col1 + 1; + + arma_debug_check_bounds + ( + (row_num >= n_rows) + || + ( col_all ? false : ((in_col1 > in_col2) || (in_col2 >= local_n_cols)) ) + , + "SpMat::operator(): indices out of bounds or incorrectly used" + ); + + return SpSubview_row<eT>(*this, row_num, in_col1, submat_n_cols); + } + + + +template<typename eT> +inline +const SpSubview_row<eT> +SpMat<eT>::operator()(const uword row_num, const span& col_span) const + { + arma_extra_debug_sigprint(); + + const bool col_all = col_span.whole; + + const uword local_n_cols = n_cols; + + const uword in_col1 = col_all ? 0 : col_span.a; + const uword in_col2 = col_span.b; + const uword submat_n_cols = col_all ? local_n_cols : in_col2 - in_col1 + 1; + + arma_debug_check_bounds + ( + (row_num >= n_rows) + || + ( col_all ? false : ((in_col1 > in_col2) || (in_col2 >= local_n_cols)) ) + , + "SpMat::operator(): indices out of bounds or incorrectly used" + ); + + return SpSubview_row<eT>(*this, row_num, in_col1, submat_n_cols); + } + + + +template<typename eT> +arma_inline +SpSubview_col<eT> +SpMat<eT>::col(const uword col_num) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds(col_num >= n_cols, "SpMat::col(): out of bounds"); + + return SpSubview_col<eT>(*this, col_num); + } + + + +template<typename eT> +arma_inline +const SpSubview_col<eT> +SpMat<eT>::col(const uword col_num) const + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds(col_num >= n_cols, "SpMat::col(): out of bounds"); + + return SpSubview_col<eT>(*this, col_num); + } + + + +template<typename eT> +inline +SpSubview_col<eT> +SpMat<eT>::operator()(const span& row_span, const uword col_num) + { + arma_extra_debug_sigprint(); + + const bool row_all = row_span.whole; + + const uword local_n_rows = n_rows; + + const uword in_row1 = row_all ? 0 : row_span.a; + const uword in_row2 = row_span.b; + const uword submat_n_rows = row_all ? local_n_rows : in_row2 - in_row1 + 1; + + arma_debug_check_bounds + ( + (col_num >= n_cols) + || + ( row_all ? false : ((in_row1 > in_row2) || (in_row2 >= local_n_rows)) ) + , + "SpMat::operator(): indices out of bounds or incorrectly used" + ); + + return SpSubview_col<eT>(*this, col_num, in_row1, submat_n_rows); + } + + + +template<typename eT> +inline +const SpSubview_col<eT> +SpMat<eT>::operator()(const span& row_span, const uword col_num) const + { + arma_extra_debug_sigprint(); + + const bool row_all = row_span.whole; + + const uword local_n_rows = n_rows; + + const uword in_row1 = row_all ? 0 : row_span.a; + const uword in_row2 = row_span.b; + const uword submat_n_rows = row_all ? local_n_rows : in_row2 - in_row1 + 1; + + arma_debug_check_bounds + ( + (col_num >= n_cols) + || + ( row_all ? false : ((in_row1 > in_row2) || (in_row2 >= local_n_rows)) ) + , + "SpMat::operator(): indices out of bounds or incorrectly used" + ); + + return SpSubview_col<eT>(*this, col_num, in_row1, submat_n_rows); + } + + + +template<typename eT> +arma_inline +SpSubview<eT> +SpMat<eT>::rows(const uword in_row1, const uword in_row2) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds + ( + (in_row1 > in_row2) || (in_row2 >= n_rows), + "SpMat::rows(): indices out of bounds or incorrectly used" + ); + + const uword subview_n_rows = in_row2 - in_row1 + 1; + + return SpSubview<eT>(*this, in_row1, 0, subview_n_rows, n_cols); + } + + + +template<typename eT> +arma_inline +const SpSubview<eT> +SpMat<eT>::rows(const uword in_row1, const uword in_row2) const + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds + ( + (in_row1 > in_row2) || (in_row2 >= n_rows), + "SpMat::rows(): indices out of bounds or incorrectly used" + ); + + const uword subview_n_rows = in_row2 - in_row1 + 1; + + return SpSubview<eT>(*this, in_row1, 0, subview_n_rows, n_cols); + } + + + +template<typename eT> +arma_inline +SpSubview<eT> +SpMat<eT>::cols(const uword in_col1, const uword in_col2) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds + ( + (in_col1 > in_col2) || (in_col2 >= n_cols), + "SpMat::cols(): indices out of bounds or incorrectly used" + ); + + const uword subview_n_cols = in_col2 - in_col1 + 1; + + return SpSubview<eT>(*this, 0, in_col1, n_rows, subview_n_cols); + } + + + +template<typename eT> +arma_inline +const SpSubview<eT> +SpMat<eT>::cols(const uword in_col1, const uword in_col2) const + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds + ( + (in_col1 > in_col2) || (in_col2 >= n_cols), + "SpMat::cols(): indices out of bounds or incorrectly used" + ); + + const uword subview_n_cols = in_col2 - in_col1 + 1; + + return SpSubview<eT>(*this, 0, in_col1, n_rows, subview_n_cols); + } + + + +template<typename eT> +arma_inline +SpSubview<eT> +SpMat<eT>::submat(const uword in_row1, const uword in_col1, const uword in_row2, const uword in_col2) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds + ( + (in_row1 > in_row2) || (in_col1 > in_col2) || (in_row2 >= n_rows) || (in_col2 >= n_cols), + "SpMat::submat(): indices out of bounds or incorrectly used" + ); + + const uword subview_n_rows = in_row2 - in_row1 + 1; + const uword subview_n_cols = in_col2 - in_col1 + 1; + + return SpSubview<eT>(*this, in_row1, in_col1, subview_n_rows, subview_n_cols); + } + + + +template<typename eT> +arma_inline +const SpSubview<eT> +SpMat<eT>::submat(const uword in_row1, const uword in_col1, const uword in_row2, const uword in_col2) const + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds + ( + (in_row1 > in_row2) || (in_col1 > in_col2) || (in_row2 >= n_rows) || (in_col2 >= n_cols), + "SpMat::submat(): indices out of bounds or incorrectly used" + ); + + const uword subview_n_rows = in_row2 - in_row1 + 1; + const uword subview_n_cols = in_col2 - in_col1 + 1; + + return SpSubview<eT>(*this, in_row1, in_col1, subview_n_rows, subview_n_cols); + } + + + +template<typename eT> +arma_inline +SpSubview<eT> +SpMat<eT>::submat(const uword in_row1, const uword in_col1, const SizeMat& s) + { + arma_extra_debug_sigprint(); + + const uword l_n_rows = n_rows; + const uword l_n_cols = n_cols; + + const uword s_n_rows = s.n_rows; + const uword s_n_cols = s.n_cols; + + arma_debug_check_bounds + ( + ((in_row1 >= l_n_rows) || (in_col1 >= l_n_cols) || ((in_row1 + s_n_rows) > l_n_rows) || ((in_col1 + s_n_cols) > l_n_cols)), + "SpMat::submat(): indices or size out of bounds" + ); + + return SpSubview<eT>(*this, in_row1, in_col1, s_n_rows, s_n_cols); + } + + + +template<typename eT> +arma_inline +const SpSubview<eT> +SpMat<eT>::submat(const uword in_row1, const uword in_col1, const SizeMat& s) const + { + arma_extra_debug_sigprint(); + + const uword l_n_rows = n_rows; + const uword l_n_cols = n_cols; + + const uword s_n_rows = s.n_rows; + const uword s_n_cols = s.n_cols; + + arma_debug_check_bounds + ( + ((in_row1 >= l_n_rows) || (in_col1 >= l_n_cols) || ((in_row1 + s_n_rows) > l_n_rows) || ((in_col1 + s_n_cols) > l_n_cols)), + "SpMat::submat(): indices or size out of bounds" + ); + + return SpSubview<eT>(*this, in_row1, in_col1, s_n_rows, s_n_cols); + } + + + +template<typename eT> +inline +SpSubview<eT> +SpMat<eT>::submat(const span& row_span, const span& col_span) + { + arma_extra_debug_sigprint(); + + const bool row_all = row_span.whole; + const bool col_all = col_span.whole; + + const uword local_n_rows = n_rows; + const uword local_n_cols = n_cols; + + const uword in_row1 = row_all ? 0 : row_span.a; + const uword in_row2 = row_span.b; + const uword submat_n_rows = row_all ? local_n_rows : in_row2 - in_row1 + 1; + + const uword in_col1 = col_all ? 0 : col_span.a; + const uword in_col2 = col_span.b; + const uword submat_n_cols = col_all ? local_n_cols : in_col2 - in_col1 + 1; + + arma_debug_check_bounds + ( + ( row_all ? false : ((in_row1 > in_row2) || (in_row2 >= local_n_rows)) ) + || + ( col_all ? false : ((in_col1 > in_col2) || (in_col2 >= local_n_cols)) ) + , + "SpMat::submat(): indices out of bounds or incorrectly used" + ); + + return SpSubview<eT>(*this, in_row1, in_col1, submat_n_rows, submat_n_cols); + } + + + +template<typename eT> +inline +const SpSubview<eT> +SpMat<eT>::submat(const span& row_span, const span& col_span) const + { + arma_extra_debug_sigprint(); + + const bool row_all = row_span.whole; + const bool col_all = col_span.whole; + + const uword local_n_rows = n_rows; + const uword local_n_cols = n_cols; + + const uword in_row1 = row_all ? 0 : row_span.a; + const uword in_row2 = row_span.b; + const uword submat_n_rows = row_all ? local_n_rows : in_row2 - in_row1 + 1; + + const uword in_col1 = col_all ? 0 : col_span.a; + const uword in_col2 = col_span.b; + const uword submat_n_cols = col_all ? local_n_cols : in_col2 - in_col1 + 1; + + arma_debug_check_bounds + ( + ( row_all ? false : ((in_row1 > in_row2) || (in_row2 >= local_n_rows)) ) + || + ( col_all ? false : ((in_col1 > in_col2) || (in_col2 >= local_n_cols)) ) + , + "SpMat::submat(): indices out of bounds or incorrectly used" + ); + + return SpSubview<eT>(*this, in_row1, in_col1, submat_n_rows, submat_n_cols); + } + + + +template<typename eT> +inline +SpSubview<eT> +SpMat<eT>::operator()(const span& row_span, const span& col_span) + { + arma_extra_debug_sigprint(); + + return submat(row_span, col_span); + } + + + +template<typename eT> +inline +const SpSubview<eT> +SpMat<eT>::operator()(const span& row_span, const span& col_span) const + { + arma_extra_debug_sigprint(); + + return submat(row_span, col_span); + } + + + +template<typename eT> +arma_inline +SpSubview<eT> +SpMat<eT>::operator()(const uword in_row1, const uword in_col1, const SizeMat& s) + { + arma_extra_debug_sigprint(); + + return (*this).submat(in_row1, in_col1, s); + } + + + +template<typename eT> +arma_inline +const SpSubview<eT> +SpMat<eT>::operator()(const uword in_row1, const uword in_col1, const SizeMat& s) const + { + arma_extra_debug_sigprint(); + + return (*this).submat(in_row1, in_col1, s); + } + + + +template<typename eT> +inline +SpSubview<eT> +SpMat<eT>::head_rows(const uword N) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds( (N > n_rows), "SpMat::head_rows(): size out of bounds" ); + + return SpSubview<eT>(*this, 0, 0, N, n_cols); + } + + + +template<typename eT> +inline +const SpSubview<eT> +SpMat<eT>::head_rows(const uword N) const + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds( (N > n_rows), "SpMat::head_rows(): size out of bounds" ); + + return SpSubview<eT>(*this, 0, 0, N, n_cols); + } + + + +template<typename eT> +inline +SpSubview<eT> +SpMat<eT>::tail_rows(const uword N) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds( (N > n_rows), "SpMat::tail_rows(): size out of bounds" ); + + const uword start_row = n_rows - N; + + return SpSubview<eT>(*this, start_row, 0, N, n_cols); + } + + + +template<typename eT> +inline +const SpSubview<eT> +SpMat<eT>::tail_rows(const uword N) const + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds( (N > n_rows), "SpMat::tail_rows(): size out of bounds" ); + + const uword start_row = n_rows - N; + + return SpSubview<eT>(*this, start_row, 0, N, n_cols); + } + + + +template<typename eT> +inline +SpSubview<eT> +SpMat<eT>::head_cols(const uword N) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds( (N > n_cols), "SpMat::head_cols(): size out of bounds" ); + + return SpSubview<eT>(*this, 0, 0, n_rows, N); + } + + + +template<typename eT> +inline +const SpSubview<eT> +SpMat<eT>::head_cols(const uword N) const + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds( (N > n_cols), "SpMat::head_cols(): size out of bounds" ); + + return SpSubview<eT>(*this, 0, 0, n_rows, N); + } + + + +template<typename eT> +inline +SpSubview<eT> +SpMat<eT>::tail_cols(const uword N) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds( (N > n_cols), "SpMat::tail_cols(): size out of bounds" ); + + const uword start_col = n_cols - N; + + return SpSubview<eT>(*this, 0, start_col, n_rows, N); + } + + + +template<typename eT> +inline +const SpSubview<eT> +SpMat<eT>::tail_cols(const uword N) const + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds( (N > n_cols), "SpMat::tail_cols(): size out of bounds" ); + + const uword start_col = n_cols - N; + + return SpSubview<eT>(*this, 0, start_col, n_rows, N); + } + + + +template<typename eT> +template<typename T1> +arma_inline +SpSubview_col_list<eT, T1> +SpMat<eT>::cols(const Base<uword, T1>& indices) + { + arma_extra_debug_sigprint(); + + return SpSubview_col_list<eT, T1>(*this, indices); + } + + + +template<typename eT> +template<typename T1> +arma_inline +const SpSubview_col_list<eT, T1> +SpMat<eT>::cols(const Base<uword, T1>& indices) const + { + arma_extra_debug_sigprint(); + + return SpSubview_col_list<eT, T1>(*this, indices); + } + + + +//! creation of spdiagview (diagonal) +template<typename eT> +inline +spdiagview<eT> +SpMat<eT>::diag(const sword in_id) + { + arma_extra_debug_sigprint(); + + const uword row_offset = (in_id < 0) ? uword(-in_id) : 0; + const uword col_offset = (in_id > 0) ? uword( in_id) : 0; + + arma_debug_check_bounds + ( + ((row_offset > 0) && (row_offset >= n_rows)) || ((col_offset > 0) && (col_offset >= n_cols)), + "SpMat::diag(): requested diagonal out of bounds" + ); + + const uword len = (std::min)(n_rows - row_offset, n_cols - col_offset); + + return spdiagview<eT>(*this, row_offset, col_offset, len); + } + + + +//! creation of spdiagview (diagonal) +template<typename eT> +inline +const spdiagview<eT> +SpMat<eT>::diag(const sword in_id) const + { + arma_extra_debug_sigprint(); + + const uword row_offset = uword( (in_id < 0) ? -in_id : 0 ); + const uword col_offset = uword( (in_id > 0) ? in_id : 0 ); + + arma_debug_check_bounds + ( + ((row_offset > 0) && (row_offset >= n_rows)) || ((col_offset > 0) && (col_offset >= n_cols)), + "SpMat::diag(): requested diagonal out of bounds" + ); + + const uword len = (std::min)(n_rows - row_offset, n_cols - col_offset); + + return spdiagview<eT>(*this, row_offset, col_offset, len); + } + + + +template<typename eT> +inline +void +SpMat<eT>::swap_rows(const uword in_row1, const uword in_row2) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds( ((in_row1 >= n_rows) || (in_row2 >= n_rows)), "SpMat::swap_rows(): out of bounds" ); + + if(in_row1 == in_row2) { return; } + + sync_csc(); + invalidate_cache(); + + // The easier way to do this, instead of collecting all the elements in one row and then swapping with the other, will be + // to iterate over each column of the matrix (since we store in column-major format) and then swap the two elements in the two rows at that time. + // We will try to avoid using the at() call since it is expensive, instead preferring to use an iterator to track our position. + uword col1 = (in_row1 < in_row2) ? in_row1 : in_row2; + uword col2 = (in_row1 < in_row2) ? in_row2 : in_row1; + + for(uword lcol = 0; lcol < n_cols; lcol++) + { + // If there is nothing in this column we can ignore it. + if(col_ptrs[lcol] == col_ptrs[lcol + 1]) + { + continue; + } + + // These will represent the positions of the items themselves. + uword loc1 = n_nonzero + 1; + uword loc2 = n_nonzero + 1; + + for(uword search_pos = col_ptrs[lcol]; search_pos < col_ptrs[lcol + 1]; search_pos++) + { + if(row_indices[search_pos] == col1) + { + loc1 = search_pos; + } + + if(row_indices[search_pos] == col2) + { + loc2 = search_pos; + break; // No need to look any further. + } + } + + // There are four cases: we found both elements; we found one element (loc1); we found one element (loc2); we found zero elements. + // If we found zero elements no work needs to be done and we can continue to the next column. + if((loc1 != (n_nonzero + 1)) && (loc2 != (n_nonzero + 1))) + { + // This is an easy case: just swap the values. No index modifying necessary. + eT tmp = values[loc1]; + access::rw(values[loc1]) = values[loc2]; + access::rw(values[loc2]) = tmp; + } + else if(loc1 != (n_nonzero + 1)) // We only found loc1 and not loc2. + { + // We need to find the correct place to move our value to. It will be forward (not backwards) because in_row2 > in_row1. + // Each iteration of the loop swaps the current value (loc1) with (loc1 + 1); in this manner we move our value down to where it should be. + while(((loc1 + 1) < col_ptrs[lcol + 1]) && (row_indices[loc1 + 1] < in_row2)) + { + // Swap both the values and the indices. The column should not change. + eT tmp = values[loc1]; + access::rw(values[loc1]) = values[loc1 + 1]; + access::rw(values[loc1 + 1]) = tmp; + + uword tmp_index = row_indices[loc1]; + access::rw(row_indices[loc1]) = row_indices[loc1 + 1]; + access::rw(row_indices[loc1 + 1]) = tmp_index; + + loc1++; // And increment the counter. + } + + // Now set the row index correctly. + access::rw(row_indices[loc1]) = in_row2; + + } + else if(loc2 != (n_nonzero + 1)) + { + // We need to find the correct place to move our value to. It will be backwards (not forwards) because in_row1 < in_row2. + // Each iteration of the loop swaps the current value (loc2) with (loc2 - 1); in this manner we move our value up to where it should be. + while(((loc2 - 1) >= col_ptrs[lcol]) && (row_indices[loc2 - 1] > in_row1)) + { + // Swap both the values and the indices. The column should not change. + eT tmp = values[loc2]; + access::rw(values[loc2]) = values[loc2 - 1]; + access::rw(values[loc2 - 1]) = tmp; + + uword tmp_index = row_indices[loc2]; + access::rw(row_indices[loc2]) = row_indices[loc2 - 1]; + access::rw(row_indices[loc2 - 1]) = tmp_index; + + loc2--; // And decrement the counter. + } + + // Now set the row index correctly. + access::rw(row_indices[loc2]) = in_row1; + + } + /* else: no need to swap anything; both values are zero */ + } + } + + + +template<typename eT> +inline +void +SpMat<eT>::swap_cols(const uword in_col1, const uword in_col2) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds( ((in_col1 >= n_cols) || (in_col2 >= n_cols)), "SpMat::swap_cols(): out of bounds" ); + + if(in_col1 == in_col2) { return; } + + // TODO: this is a rudimentary implementation + + const SpMat<eT> tmp1 = (*this).col(in_col1); + const SpMat<eT> tmp2 = (*this).col(in_col2); + + (*this).col(in_col2) = tmp1; + (*this).col(in_col1) = tmp2; + + // for(uword lrow = 0; lrow < n_rows; ++lrow) + // { + // const eT tmp = at(lrow, in_col1); + // at(lrow, in_col1) = eT( at(lrow, in_col2) ); + // at(lrow, in_col2) = tmp; + // } + } + + + +template<typename eT> +inline +void +SpMat<eT>::shed_row(const uword row_num) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds(row_num >= n_rows, "SpMat::shed_row(): out of bounds"); + + shed_rows (row_num, row_num); + } + + + +template<typename eT> +inline +void +SpMat<eT>::shed_col(const uword col_num) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds(col_num >= n_cols, "SpMat::shed_col(): out of bounds"); + + shed_cols(col_num, col_num); + } + + + +template<typename eT> +inline +void +SpMat<eT>::shed_rows(const uword in_row1, const uword in_row2) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds + ( + (in_row1 > in_row2) || (in_row2 >= n_rows), + "SpMat::shed_rows(): indices out of bounds or incorectly used" + ); + + sync_csc(); + + SpMat<eT> newmat(n_rows - (in_row2 - in_row1 + 1), n_cols); + + // First, count the number of elements we will be removing. + uword removing = 0; + for(uword i = 0; i < n_nonzero; ++i) + { + const uword lrow = row_indices[i]; + if(lrow >= in_row1 && lrow <= in_row2) + { + ++removing; + } + } + + // Obtain counts of the number of points in each column and store them as the + // (invalid) column pointers of the new matrix. + for(uword i = 1; i < n_cols + 1; ++i) + { + access::rw(newmat.col_ptrs[i]) = col_ptrs[i] - col_ptrs[i - 1]; + } + + // Now initialize memory for the new matrix. + newmat.mem_resize(n_nonzero - removing); + + // Now, copy over the elements. + // i is the index in the old matrix; j is the index in the new matrix. + const_iterator it = cbegin(); + const_iterator it_end = cend(); + + uword j = 0; // The index in the new matrix. + while(it != it_end) + { + const uword lrow = it.row(); + const uword lcol = it.col(); + + if(lrow >= in_row1 && lrow <= in_row2) + { + // This element is being removed. Subtract it from the column counts. + --access::rw(newmat.col_ptrs[lcol + 1]); + } + else + { + // This element is being kept. We may need to map the row index, + // if it is past the section of rows we are removing. + if(lrow > in_row2) + { + access::rw(newmat.row_indices[j]) = lrow - (in_row2 - in_row1 + 1); + } + else + { + access::rw(newmat.row_indices[j]) = lrow; + } + + access::rw(newmat.values[j]) = (*it); + ++j; // Increment index in new matrix. + } + + ++it; + } + + // Finally, sum the column counts so they are correct column pointers. + for(uword i = 1; i < n_cols + 1; ++i) + { + access::rw(newmat.col_ptrs[i]) += newmat.col_ptrs[i - 1]; + } + + // Now steal the memory of the new matrix. + steal_mem(newmat); + } + + + +template<typename eT> +inline +void +SpMat<eT>::shed_cols(const uword in_col1, const uword in_col2) + { + arma_extra_debug_sigprint(); + + arma_debug_check_bounds + ( + (in_col1 > in_col2) || (in_col2 >= n_cols), + "SpMat::shed_cols(): indices out of bounds or incorrectly used" + ); + + sync_csc(); + invalidate_cache(); + + // First we find the locations in values and row_indices for the column entries. + uword col_beg = col_ptrs[in_col1]; + uword col_end = col_ptrs[in_col2 + 1]; + + // Then we find the number of entries in the column. + uword diff = col_end - col_beg; + + if(diff > 0) + { + eT* new_values = memory::acquire<eT> (n_nonzero + 1 - diff); + uword* new_row_indices = memory::acquire<uword>(n_nonzero + 1 - diff); + + // Copy first part. + if(col_beg != 0) + { + arrayops::copy(new_values, values, col_beg); + arrayops::copy(new_row_indices, row_indices, col_beg); + } + + // Copy second part. + if(col_end != n_nonzero) + { + arrayops::copy(new_values + col_beg, values + col_end, n_nonzero - col_end); + arrayops::copy(new_row_indices + col_beg, row_indices + col_end, n_nonzero - col_end); + } + + // Copy sentry element. + new_values[n_nonzero - diff] = values[n_nonzero]; + new_row_indices[n_nonzero - diff] = row_indices[n_nonzero]; + + if(values) { memory::release(access::rw(values)); } + if(row_indices) { memory::release(access::rw(row_indices)); } + + access::rw(values) = new_values; + access::rw(row_indices) = new_row_indices; + + // Update counts and such. + access::rw(n_nonzero) -= diff; + } + + // Update column pointers. + const uword new_n_cols = n_cols - ((in_col2 - in_col1) + 1); + + uword* new_col_ptrs = memory::acquire<uword>(new_n_cols + 2); + new_col_ptrs[new_n_cols + 1] = std::numeric_limits<uword>::max(); + + // Copy first set of columns (no manipulation required). + if(in_col1 != 0) + { + arrayops::copy(new_col_ptrs, col_ptrs, in_col1); + } + + // Copy second set of columns (manipulation required). + uword cur_col = in_col1; + for(uword i = in_col2 + 1; i <= n_cols; ++i, ++cur_col) + { + new_col_ptrs[cur_col] = col_ptrs[i] - diff; + } + + if(col_ptrs) { memory::release(access::rw(col_ptrs)); } + access::rw(col_ptrs) = new_col_ptrs; + + // We update the element and column counts, and we're done. + access::rw(n_cols) = new_n_cols; + access::rw(n_elem) = n_cols * n_rows; + } + + + +/** + * Element access; acces the i'th element (works identically to the Mat accessors). + * If there is nothing at element i, 0 is returned. + */ + +template<typename eT> +arma_inline +SpMat_MapMat_val<eT> +SpMat<eT>::operator[](const uword i) + { + const uword in_col = i / n_rows; + const uword in_row = i % n_rows; + + return SpMat_MapMat_val<eT>((*this), cache, in_row, in_col); + } + + + +template<typename eT> +arma_inline +eT +SpMat<eT>::operator[](const uword i) const + { + return get_value(i); + } + + + +template<typename eT> +arma_inline +SpMat_MapMat_val<eT> +SpMat<eT>::at(const uword i) + { + const uword in_col = i / n_rows; + const uword in_row = i % n_rows; + + return SpMat_MapMat_val<eT>((*this), cache, in_row, in_col); + } + + + +template<typename eT> +arma_inline +eT +SpMat<eT>::at(const uword i) const + { + return get_value(i); + } + + + +template<typename eT> +arma_inline +SpMat_MapMat_val<eT> +SpMat<eT>::operator()(const uword i) + { + arma_debug_check_bounds( (i >= n_elem), "SpMat::operator(): out of bounds" ); + + const uword in_col = i / n_rows; + const uword in_row = i % n_rows; + + return SpMat_MapMat_val<eT>((*this), cache, in_row, in_col); + } + + + +template<typename eT> +arma_inline +eT +SpMat<eT>::operator()(const uword i) const + { + arma_debug_check_bounds( (i >= n_elem), "SpMat::operator(): out of bounds" ); + + return get_value(i); + } + + + +/** + * Element access; access the element at row in_rows and column in_col. + * If there is nothing at that position, 0 is returned. + */ + +#if defined(__cpp_multidimensional_subscript) + + template<typename eT> + arma_inline + SpMat_MapMat_val<eT> + SpMat<eT>::operator[] (const uword in_row, const uword in_col) + { + return SpMat_MapMat_val<eT>((*this), cache, in_row, in_col); + } + + + + template<typename eT> + arma_inline + eT + SpMat<eT>::operator[] (const uword in_row, const uword in_col) const + { + return get_value(in_row, in_col); + } + +#endif + + + +template<typename eT> +arma_inline +SpMat_MapMat_val<eT> +SpMat<eT>::at(const uword in_row, const uword in_col) + { + return SpMat_MapMat_val<eT>((*this), cache, in_row, in_col); + } + + + +template<typename eT> +arma_inline +eT +SpMat<eT>::at(const uword in_row, const uword in_col) const + { + return get_value(in_row, in_col); + } + + + +template<typename eT> +arma_inline +SpMat_MapMat_val<eT> +SpMat<eT>::operator()(const uword in_row, const uword in_col) + { + arma_debug_check_bounds( ((in_row >= n_rows) || (in_col >= n_cols)), "SpMat::operator(): out of bounds" ); + + return SpMat_MapMat_val<eT>((*this), cache, in_row, in_col); + } + + + +template<typename eT> +arma_inline +eT +SpMat<eT>::operator()(const uword in_row, const uword in_col) const + { + arma_debug_check_bounds( ((in_row >= n_rows) || (in_col >= n_cols)), "SpMat::operator(): out of bounds" ); + + return get_value(in_row, in_col); + } + + + +/** + * Check if matrix is empty (no size, no values). + */ +template<typename eT> +arma_inline +bool +SpMat<eT>::is_empty() const + { + return (n_elem == 0); + } + + + +//! returns true if the object can be interpreted as a column or row vector +template<typename eT> +arma_inline +bool +SpMat<eT>::is_vec() const + { + return ( (n_rows == 1) || (n_cols == 1) ); + } + + + +//! returns true if the object can be interpreted as a row vector +template<typename eT> +arma_inline +bool +SpMat<eT>::is_rowvec() const + { + return (n_rows == 1); + } + + + +//! returns true if the object can be interpreted as a column vector +template<typename eT> +arma_inline +bool +SpMat<eT>::is_colvec() const + { + return (n_cols == 1); + } + + + +//! returns true if the object has the same number of non-zero rows and columnns +template<typename eT> +arma_inline +bool +SpMat<eT>::is_square() const + { + return (n_rows == n_cols); + } + + + +template<typename eT> +inline +bool +SpMat<eT>::is_symmetric() const + { + arma_extra_debug_sigprint(); + + const SpMat<eT>& A = (*this); + + if(A.n_rows != A.n_cols) { return false; } + + const SpMat<eT> tmp = A - A.st(); + + return (tmp.n_nonzero == uword(0)); + } + + + +template<typename eT> +inline +bool +SpMat<eT>::is_symmetric(const typename get_pod_type<elem_type>::result tol) const + { + arma_extra_debug_sigprint(); + + typedef typename get_pod_type<eT>::result T; + + if(tol == T(0)) { return (*this).is_symmetric(); } + + arma_debug_check( (tol < T(0)), "is_symmetric(): parameter 'tol' must be >= 0" ); + + const SpMat<eT>& A = (*this); + + if(A.n_rows != A.n_cols) { return false; } + + const T norm_A = as_scalar( arma::max(sum(abs(A), 1), 0) ); + + if(norm_A == T(0)) { return true; } + + const T norm_A_Ast = as_scalar( arma::max(sum(abs(A - A.st()), 1), 0) ); + + return ( (norm_A_Ast / norm_A) <= tol ); + } + + + +template<typename eT> +inline +bool +SpMat<eT>::is_hermitian() const + { + arma_extra_debug_sigprint(); + + const SpMat<eT>& A = (*this); + + if(A.n_rows != A.n_cols) { return false; } + + const SpMat<eT> tmp = A - A.t(); + + return (tmp.n_nonzero == uword(0)); + } + + + +template<typename eT> +inline +bool +SpMat<eT>::is_hermitian(const typename get_pod_type<elem_type>::result tol) const + { + arma_extra_debug_sigprint(); + + typedef typename get_pod_type<eT>::result T; + + if(tol == T(0)) { return (*this).is_hermitian(); } + + arma_debug_check( (tol < T(0)), "is_hermitian(): parameter 'tol' must be >= 0" ); + + const SpMat<eT>& A = (*this); + + if(A.n_rows != A.n_cols) { return false; } + + const T norm_A = as_scalar( arma::max(sum(abs(A), 1), 0) ); + + if(norm_A == T(0)) { return true; } + + const T norm_A_At = as_scalar( arma::max(sum(abs(A - A.t()), 1), 0) ); + + return ( (norm_A_At / norm_A) <= tol ); + } + + + +template<typename eT> +inline +bool +SpMat<eT>::internal_is_finite() const + { + arma_extra_debug_sigprint(); + + sync_csc(); + + return arrayops::is_finite(values, n_nonzero); + } + + + +template<typename eT> +inline +bool +SpMat<eT>::internal_has_inf() const + { + arma_extra_debug_sigprint(); + + sync_csc(); + + return arrayops::has_inf(values, n_nonzero); + } + + + +template<typename eT> +inline +bool +SpMat<eT>::internal_has_nan() const + { + arma_extra_debug_sigprint(); + + sync_csc(); + + return arrayops::has_nan(values, n_nonzero); + } + + + +template<typename eT> +inline +bool +SpMat<eT>::internal_has_nonfinite() const + { + arma_extra_debug_sigprint(); + + sync_csc(); + + return (arrayops::is_finite(values, n_nonzero) == false); + } + + + +//! returns true if the given index is currently in range +template<typename eT> +arma_inline +bool +SpMat<eT>::in_range(const uword i) const + { + return (i < n_elem); + } + + +//! returns true if the given start and end indices are currently in range +template<typename eT> +arma_inline +bool +SpMat<eT>::in_range(const span& x) const + { + arma_extra_debug_sigprint(); + + if(x.whole) + { + return true; + } + else + { + const uword a = x.a; + const uword b = x.b; + + return ( (a <= b) && (b < n_elem) ); + } + } + + + +//! returns true if the given location is currently in range +template<typename eT> +arma_inline +bool +SpMat<eT>::in_range(const uword in_row, const uword in_col) const + { + return ( (in_row < n_rows) && (in_col < n_cols) ); + } + + + +template<typename eT> +arma_inline +bool +SpMat<eT>::in_range(const span& row_span, const uword in_col) const + { + arma_extra_debug_sigprint(); + + if(row_span.whole) + { + return (in_col < n_cols); + } + else + { + const uword in_row1 = row_span.a; + const uword in_row2 = row_span.b; + + return ( (in_row1 <= in_row2) && (in_row2 < n_rows) && (in_col < n_cols) ); + } + } + + + +template<typename eT> +arma_inline +bool +SpMat<eT>::in_range(const uword in_row, const span& col_span) const + { + arma_extra_debug_sigprint(); + + if(col_span.whole) + { + return (in_row < n_rows); + } + else + { + const uword in_col1 = col_span.a; + const uword in_col2 = col_span.b; + + return ( (in_row < n_rows) && (in_col1 <= in_col2) && (in_col2 < n_cols) ); + } + } + + + +template<typename eT> +arma_inline +bool +SpMat<eT>::in_range(const span& row_span, const span& col_span) const + { + arma_extra_debug_sigprint(); + + const uword in_row1 = row_span.a; + const uword in_row2 = row_span.b; + + const uword in_col1 = col_span.a; + const uword in_col2 = col_span.b; + + const bool rows_ok = row_span.whole ? true : ( (in_row1 <= in_row2) && (in_row2 < n_rows) ); + const bool cols_ok = col_span.whole ? true : ( (in_col1 <= in_col2) && (in_col2 < n_cols) ); + + return ( rows_ok && cols_ok ); + } + + + +template<typename eT> +arma_inline +bool +SpMat<eT>::in_range(const uword in_row, const uword in_col, const SizeMat& s) const + { + const uword l_n_rows = n_rows; + const uword l_n_cols = n_cols; + + if( (in_row >= l_n_rows) || (in_col >= l_n_cols) || ((in_row + s.n_rows) > l_n_rows) || ((in_col + s.n_cols) > l_n_cols) ) + { + return false; + } + else + { + return true; + } + } + + + +//! Set the size to the size of another matrix. +template<typename eT> +template<typename eT2> +inline +SpMat<eT>& +SpMat<eT>::copy_size(const SpMat<eT2>& m) + { + arma_extra_debug_sigprint(); + + return set_size(m.n_rows, m.n_cols); + } + + + +template<typename eT> +template<typename eT2> +inline +SpMat<eT>& +SpMat<eT>::copy_size(const Mat<eT2>& m) + { + arma_extra_debug_sigprint(); + + return set_size(m.n_rows, m.n_cols); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::set_size(const uword in_elem) + { + arma_extra_debug_sigprint(); + + // If this is a row vector, we resize to a row vector. + if(vec_state == 2) + { + set_size(1, in_elem); + } + else + { + set_size(in_elem, 1); + } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::set_size(const uword in_rows, const uword in_cols) + { + arma_extra_debug_sigprint(); + + invalidate_cache(); // placed here, as set_size() is used during matrix modification + + if( (n_rows == in_rows) && (n_cols == in_cols) ) { return *this; } + + init(in_rows, in_cols); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::set_size(const SizeMat& s) + { + arma_extra_debug_sigprint(); + + return (*this).set_size(s.n_rows, s.n_cols); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::resize(const uword in_rows, const uword in_cols) + { + arma_extra_debug_sigprint(); + + if( (n_rows == in_rows) && (n_cols == in_cols) ) { return *this; } + + if( (n_elem == 0) || (n_nonzero == 0) ) { return set_size(in_rows, in_cols); } + + SpMat<eT> tmp(in_rows, in_cols); + + if(tmp.n_elem > 0) + { + sync_csc(); + + const uword last_row = (std::min)(in_rows, n_rows) - 1; + const uword last_col = (std::min)(in_cols, n_cols) - 1; + + tmp.submat(0, 0, last_row, last_col) = (*this).submat(0, 0, last_row, last_col); + } + + steal_mem(tmp); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::resize(const SizeMat& s) + { + arma_extra_debug_sigprint(); + + return (*this).resize(s.n_rows, s.n_cols); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::reshape(const uword in_rows, const uword in_cols) + { + arma_extra_debug_sigprint(); + + arma_check( ((in_rows*in_cols) != n_elem), "SpMat::reshape(): changing the number of elements in a sparse matrix is currently not supported" ); + + if( (n_rows == in_rows) && (n_cols == in_cols) ) { return *this; } + + if(vec_state == 1) { arma_debug_check( (in_cols != 1), "SpMat::reshape(): object is a column vector; requested size is not compatible" ); } + if(vec_state == 2) { arma_debug_check( (in_rows != 1), "SpMat::reshape(): object is a row vector; requested size is not compatible" ); } + + if(n_nonzero == 0) { return (*this).zeros(in_rows, in_cols); } + + if(in_cols == 1) + { + (*this).reshape_helper_intovec(); + } + else + { + (*this).reshape_helper_generic(in_rows, in_cols); + } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::reshape(const SizeMat& s) + { + arma_extra_debug_sigprint(); + + return (*this).reshape(s.n_rows, s.n_cols); + } + + + +template<typename eT> +inline +void +SpMat<eT>::reshape_helper_generic(const uword in_rows, const uword in_cols) + { + arma_extra_debug_sigprint(); + + sync_csc(); + invalidate_cache(); + + // We have to modify all of the relevant row indices and the relevant column pointers. + // Iterate over all the points to do this. We won't be deleting any points, but we will be modifying + // columns and rows. We'll have to store a new set of column vectors. + uword* new_col_ptrs = memory::acquire<uword>(in_cols + 2); + new_col_ptrs[in_cols + 1] = std::numeric_limits<uword>::max(); + + uword* new_row_indices = memory::acquire<uword>(n_nonzero + 1); + access::rw(new_row_indices[n_nonzero]) = 0; + + arrayops::fill_zeros(new_col_ptrs, in_cols + 1); + + const_iterator it = cbegin(); + const_iterator it_end = cend(); + + for(; it != it_end; ++it) + { + uword vector_position = (it.col() * n_rows) + it.row(); + new_row_indices[it.pos()] = vector_position % in_rows; + ++new_col_ptrs[vector_position / in_rows + 1]; + } + + // Now sum the column counts to get the new column pointers. + for(uword i = 1; i <= in_cols; i++) + { + access::rw(new_col_ptrs[i]) += new_col_ptrs[i - 1]; + } + + // Copy the new row indices. + if(row_indices) { memory::release(access::rw(row_indices)); } + if(col_ptrs) { memory::release(access::rw(col_ptrs)); } + + access::rw(row_indices) = new_row_indices; + access::rw(col_ptrs) = new_col_ptrs; + + // Now set the size. + access::rw(n_rows) = in_rows; + access::rw(n_cols) = in_cols; + } + + + +template<typename eT> +inline +void +SpMat<eT>::reshape_helper_intovec() + { + arma_extra_debug_sigprint(); + + sync_csc(); + invalidate_cache(); + + const_iterator it = cbegin(); + + const uword t_n_rows = n_rows; + const uword t_n_nonzero = n_nonzero; + + for(uword i=0; i < t_n_nonzero; ++i) + { + const uword t_index = (it.col() * t_n_rows) + it.row(); + + // ensure the iterator is pointing to the next element + // before we overwrite the row index of the current element + ++it; + + access::rw(row_indices[i]) = t_index; + } + + access::rw(row_indices[n_nonzero]) = 0; + + access::rw(col_ptrs[0]) = 0; + access::rw(col_ptrs[1]) = n_nonzero; + access::rw(col_ptrs[2]) = std::numeric_limits<uword>::max(); + + access::rw(n_rows) = (n_rows * n_cols); + access::rw(n_cols) = 1; + } + + + +//! apply a functor to each non-zero element +template<typename eT> +template<typename functor> +inline +SpMat<eT>& +SpMat<eT>::for_each(functor F) + { + arma_extra_debug_sigprint(); + + sync_csc(); + + const uword N = (*this).n_nonzero; + + eT* rw_values = access::rwp(values); + + bool modified = false; + bool has_zero = false; + + for(uword i=0; i < N; ++i) + { + eT& new_value = rw_values[i]; + const eT old_value = new_value; + + F(new_value); + + if(new_value != old_value) { modified = true; } + if(new_value == eT(0) ) { has_zero = true; } + } + + if(modified) { invalidate_cache(); } + if(has_zero) { remove_zeros(); } + + return *this; + } + + + +template<typename eT> +template<typename functor> +inline +const SpMat<eT>& +SpMat<eT>::for_each(functor F) const + { + arma_extra_debug_sigprint(); + + sync_csc(); + + const uword N = (*this).n_nonzero; + + for(uword i=0; i < N; ++i) { F(values[i]); } + + return *this; + } + + + +//! transform each non-zero element using a functor +template<typename eT> +template<typename functor> +inline +SpMat<eT>& +SpMat<eT>::transform(functor F) + { + arma_extra_debug_sigprint(); + + sync_csc(); + invalidate_cache(); + + const uword N = (*this).n_nonzero; + + eT* rw_values = access::rwp(values); + + bool has_zero = false; + + for(uword i=0; i < N; ++i) + { + eT& rw_values_i = rw_values[i]; + + rw_values_i = eT( F(rw_values_i) ); + + if(rw_values_i == eT(0)) { has_zero = true; } + } + + if(has_zero) { remove_zeros(); } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::replace(const eT old_val, const eT new_val) + { + arma_extra_debug_sigprint(); + + if(old_val == eT(0)) + { + arma_debug_warn_level(1, "SpMat::replace(): replacement not done, as old_val = 0"); + } + else + { + sync_csc(); + invalidate_cache(); + + arrayops::replace(access::rwp(values), n_nonzero, old_val, new_val); + + if(new_val == eT(0)) { remove_zeros(); } + } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::clean(const typename get_pod_type<eT>::result threshold) + { + arma_extra_debug_sigprint(); + + if(n_nonzero == 0) { return *this; } + + sync_csc(); + invalidate_cache(); + + arrayops::clean(access::rwp(values), n_nonzero, threshold); + + remove_zeros(); + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::clamp(const eT min_val, const eT max_val) + { + arma_extra_debug_sigprint(); + + if(is_cx<eT>::no) + { + arma_debug_check( (access::tmp_real(min_val) > access::tmp_real(max_val)), "SpMat::clamp(): min_val must be less than max_val" ); + } + else + { + arma_debug_check( (access::tmp_real(min_val) > access::tmp_real(max_val)), "SpMat::clamp(): real(min_val) must be less than real(max_val)" ); + arma_debug_check( (access::tmp_imag(min_val) > access::tmp_imag(max_val)), "SpMat::clamp(): imag(min_val) must be less than imag(max_val)" ); + } + + if(n_nonzero == 0) { return *this; } + + sync_csc(); + invalidate_cache(); + + arrayops::clamp(access::rwp(values), n_nonzero, min_val, max_val); + + if( (min_val == eT(0)) || (max_val == eT(0)) ) { remove_zeros(); } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::zeros() + { + arma_extra_debug_sigprint(); + + if((n_nonzero == 0) && (values != nullptr)) + { + invalidate_cache(); + } + else + { + init(n_rows, n_cols); + } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::zeros(const uword in_elem) + { + arma_extra_debug_sigprint(); + + if(vec_state == 2) + { + zeros(1, in_elem); // Row vector + } + else + { + zeros(in_elem, 1); + } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::zeros(const uword in_rows, const uword in_cols) + { + arma_extra_debug_sigprint(); + + if((n_nonzero == 0) && (n_rows == in_rows) && (n_cols == in_cols) && (values != nullptr)) + { + invalidate_cache(); + } + else + { + init(in_rows, in_cols); + } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::zeros(const SizeMat& s) + { + arma_extra_debug_sigprint(); + + return (*this).zeros(s.n_rows, s.n_cols); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::eye() + { + arma_extra_debug_sigprint(); + + return (*this).eye(n_rows, n_cols); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::eye(const uword in_rows, const uword in_cols) + { + arma_extra_debug_sigprint(); + + const uword N = (std::min)(in_rows, in_cols); + + init(in_rows, in_cols, N); + + arrayops::inplace_set(access::rwp(values), eT(1), N); + + for(uword i = 0; i < N; ++i) { access::rw(row_indices[i]) = i; } + + for(uword i = 0; i <= N; ++i) { access::rw(col_ptrs[i]) = i; } + + // take into account non-square matrices + for(uword i = (N+1); i <= in_cols; ++i) { access::rw(col_ptrs[i]) = N; } + + access::rw(n_nonzero) = N; + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::eye(const SizeMat& s) + { + arma_extra_debug_sigprint(); + + return (*this).eye(s.n_rows, s.n_cols); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::speye() + { + arma_extra_debug_sigprint(); + + return (*this).eye(n_rows, n_cols); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::speye(const uword in_n_rows, const uword in_n_cols) + { + arma_extra_debug_sigprint(); + + return (*this).eye(in_n_rows, in_n_cols); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::speye(const SizeMat& s) + { + arma_extra_debug_sigprint(); + + return (*this).eye(s.n_rows, s.n_cols); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::sprandu(const uword in_rows, const uword in_cols, const double density) + { + arma_extra_debug_sigprint(); + + arma_debug_check( ( (density < double(0)) || (density > double(1)) ), "sprandu(): density must be in the [0,1] interval" ); + + const uword new_n_nonzero = uword(density * double(in_rows) * double(in_cols) + 0.5); + + init(in_rows, in_cols, new_n_nonzero); + + if(new_n_nonzero == 0) { return *this; } + + arma_rng::randu<eT>::fill( access::rwp(values), new_n_nonzero ); + + uvec indices = linspace<uvec>( 0u, in_rows*in_cols-1, new_n_nonzero ); + + // perturb the indices + for(uword i=1; i < new_n_nonzero-1; ++i) + { + const uword index_left = indices[i-1]; + const uword index_right = indices[i+1]; + + const uword center = (index_left + index_right) / 2; + + const uword delta1 = center - index_left - 1; + const uword delta2 = index_right - center - 1; + + const uword min_delta = (std::min)(delta1, delta2); + + uword index_new = uword( double(center) + double(min_delta) * (2.0*randu()-1.0) ); + + // paranoia, but better be safe than sorry + if( (index_left < index_new) && (index_new < index_right) ) + { + indices[i] = index_new; + } + } + + uword cur_index = 0; + uword count = 0; + + for(uword lcol = 0; lcol < in_cols; ++lcol) + for(uword lrow = 0; lrow < in_rows; ++lrow) + { + if(count == indices[cur_index]) + { + access::rw(row_indices[cur_index]) = lrow; + access::rw(col_ptrs[lcol + 1])++; + ++cur_index; + } + + ++count; + } + + if(cur_index != new_n_nonzero) + { + // Fix size to correct size. + mem_resize(cur_index); + } + + // Sum column pointers. + for(uword lcol = 1; lcol <= in_cols; ++lcol) + { + access::rw(col_ptrs[lcol]) += col_ptrs[lcol - 1]; + } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::sprandu(const SizeMat& s, const double density) + { + arma_extra_debug_sigprint(); + + return (*this).sprandu(s.n_rows, s.n_cols, density); + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::sprandn(const uword in_rows, const uword in_cols, const double density) + { + arma_extra_debug_sigprint(); + + arma_debug_check( ( (density < double(0)) || (density > double(1)) ), "sprandn(): density must be in the [0,1] interval" ); + + const uword new_n_nonzero = uword(density * double(in_rows) * double(in_cols) + 0.5); + + init(in_rows, in_cols, new_n_nonzero); + + if(new_n_nonzero == 0) { return *this; } + + arma_rng::randn<eT>::fill( access::rwp(values), new_n_nonzero ); + + uvec indices = linspace<uvec>( 0u, in_rows*in_cols-1, new_n_nonzero ); + + // perturb the indices + for(uword i=1; i < new_n_nonzero-1; ++i) + { + const uword index_left = indices[i-1]; + const uword index_right = indices[i+1]; + + const uword center = (index_left + index_right) / 2; + + const uword delta1 = center - index_left - 1; + const uword delta2 = index_right - center - 1; + + const uword min_delta = (std::min)(delta1, delta2); + + uword index_new = uword( double(center) + double(min_delta) * (2.0*randu()-1.0) ); + + // paranoia, but better be safe than sorry + if( (index_left < index_new) && (index_new < index_right) ) + { + indices[i] = index_new; + } + } + + uword cur_index = 0; + uword count = 0; + + for(uword lcol = 0; lcol < in_cols; ++lcol) + for(uword lrow = 0; lrow < in_rows; ++lrow) + { + if(count == indices[cur_index]) + { + access::rw(row_indices[cur_index]) = lrow; + access::rw(col_ptrs[lcol + 1])++; + ++cur_index; + } + + ++count; + } + + if(cur_index != new_n_nonzero) + { + // Fix size to correct size. + mem_resize(cur_index); + } + + // Sum column pointers. + for(uword lcol = 1; lcol <= in_cols; ++lcol) + { + access::rw(col_ptrs[lcol]) += col_ptrs[lcol - 1]; + } + + return *this; + } + + + +template<typename eT> +inline +SpMat<eT>& +SpMat<eT>::sprandn(const SizeMat& s, const double density) + { + arma_extra_debug_sigprint(); + + return (*this).sprandn(s.n_rows, s.n_cols, density); + } + + + +template<typename eT> +inline +void +SpMat<eT>::reset() + { + arma_extra_debug_sigprint(); + + switch(vec_state) + { + default: init(0, 0); break; + case 1: init(0, 1); break; + case 2: init(1, 0); break; + } + } + + + +template<typename eT> +inline +void +SpMat<eT>::reset_cache() + { + arma_extra_debug_sigprint(); + + sync_csc(); + + #if defined(ARMA_USE_OPENMP) + { + #pragma omp critical (arma_SpMat_cache) + { + cache.reset(); + + sync_state = 0; + } + } + #elif (!defined(ARMA_DONT_USE_STD_MUTEX)) + { + const std::lock_guard<std::mutex> lock(cache_mutex); + + cache.reset(); + + sync_state = 0; + } + #else + { + cache.reset(); + + sync_state = 0; + } + #endif + } + + + +template<typename eT> +inline +void +SpMat<eT>::reserve(const uword in_rows, const uword in_cols, const uword new_n_nonzero) + { + arma_extra_debug_sigprint(); + + init(in_rows, in_cols, new_n_nonzero); + } + + + +template<typename eT> +template<typename T1> +inline +void +SpMat<eT>::set_real(const SpBase<typename SpMat<eT>::pod_type,T1>& X) + { + arma_extra_debug_sigprint(); + + SpMat_aux::set_real(*this, X); + } + + + +template<typename eT> +template<typename T1> +inline +void +SpMat<eT>::set_imag(const SpBase<typename SpMat<eT>::pod_type,T1>& X) + { + arma_extra_debug_sigprint(); + + SpMat_aux::set_imag(*this, X); + } + + + +//! save the matrix to a file +template<typename eT> +inline +bool +SpMat<eT>::save(const std::string name, const file_type type) const + { + arma_extra_debug_sigprint(); + + sync_csc(); + + bool save_okay; + + switch(type) + { + case csv_ascii: + return (*this).save(csv_name(name), type); + break; + + case ssv_ascii: + return (*this).save(csv_name(name), type); + break; + + case arma_binary: + save_okay = diskio::save_arma_binary(*this, name); + break; + + case coord_ascii: + save_okay = diskio::save_coord_ascii(*this, name); + break; + + default: + arma_debug_warn_level(1, "SpMat::save(): unsupported file type"); + save_okay = false; + } + + if(save_okay == false) { arma_debug_warn_level(3, "SpMat::save(): write failed; file: ", name); } + + return save_okay; + } + + + +template<typename eT> +inline +bool +SpMat<eT>::save(const csv_name& spec, const file_type type) const + { + arma_extra_debug_sigprint(); + + if( (type != csv_ascii) && (type != ssv_ascii) ) + { + arma_stop_runtime_error("SpMat::save(): unsupported file type for csv_name()"); + return false; + } + + const bool do_trans = bool(spec.opts.flags & csv_opts::flag_trans ); + const bool no_header = bool(spec.opts.flags & csv_opts::flag_no_header ); + const bool with_header = bool(spec.opts.flags & csv_opts::flag_with_header) && (no_header == false); + const bool use_semicolon = bool(spec.opts.flags & csv_opts::flag_semicolon ) || (type == ssv_ascii); + + arma_extra_debug_print("SpMat::save(csv_name): enabled flags:"); + + if(do_trans ) { arma_extra_debug_print("trans"); } + if(no_header ) { arma_extra_debug_print("no_header"); } + if(with_header ) { arma_extra_debug_print("with_header"); } + if(use_semicolon) { arma_extra_debug_print("semicolon"); } + + const char separator = (use_semicolon) ? char(';') : char(','); + + if(with_header) + { + if( (spec.header_ro.n_cols != 1) && (spec.header_ro.n_rows != 1) ) + { + arma_debug_warn_level(1, "SpMat::save(): given header must have a vector layout"); + return false; + } + + for(uword i=0; i < spec.header_ro.n_elem; ++i) + { + const std::string& token = spec.header_ro.at(i); + + if(token.find(separator) != std::string::npos) + { + arma_debug_warn_level(1, "SpMat::save(): token within the header contains the separator character: '", token, "'"); + return false; + } + } + + const uword save_n_cols = (do_trans) ? (*this).n_rows : (*this).n_cols; + + if(spec.header_ro.n_elem != save_n_cols) + { + arma_debug_warn_level(1, "SpMat::save(): size mismatch between header and matrix"); + return false; + } + } + + bool save_okay = false; + + if(do_trans) + { + const SpMat<eT> tmp = (*this).st(); + + save_okay = diskio::save_csv_ascii(tmp, spec.filename, spec.header_ro, with_header, separator); + } + else + { + save_okay = diskio::save_csv_ascii(*this, spec.filename, spec.header_ro, with_header, separator); + } + + if(save_okay == false) { arma_debug_warn_level(3, "SpMat::save(): write failed; file: ", spec.filename); } + + return save_okay; + } + + + +//! save the matrix to a stream +template<typename eT> +inline +bool +SpMat<eT>::save(std::ostream& os, const file_type type) const + { + arma_extra_debug_sigprint(); + + sync_csc(); + + bool save_okay; + + switch(type) + { + case csv_ascii: + save_okay = diskio::save_csv_ascii(*this, os, char(',')); + break; + + case ssv_ascii: + save_okay = diskio::save_csv_ascii(*this, os, char(';')); + break; + + case arma_binary: + save_okay = diskio::save_arma_binary(*this, os); + break; + + case coord_ascii: + save_okay = diskio::save_coord_ascii(*this, os); + break; + + default: + arma_debug_warn_level(1, "SpMat::save(): unsupported file type"); + save_okay = false; + } + + if(save_okay == false) { arma_debug_warn_level(3, "SpMat::save(): stream write failed"); } + + return save_okay; + } + + + +//! load a matrix from a file +template<typename eT> +inline +bool +SpMat<eT>::load(const std::string name, const file_type type) + { + arma_extra_debug_sigprint(); + + invalidate_cache(); + + bool load_okay; + std::string err_msg; + + switch(type) + { + // case auto_detect: + // load_okay = diskio::load_auto_detect(*this, name, err_msg); + // break; + + case csv_ascii: + return (*this).load(csv_name(name), type); + break; + + case ssv_ascii: + return (*this).load(csv_name(name), type); + break; + + case arma_binary: + load_okay = diskio::load_arma_binary(*this, name, err_msg); + break; + + case coord_ascii: + load_okay = diskio::load_coord_ascii(*this, name, err_msg); + break; + + default: + arma_debug_warn_level(1, "SpMat::load(): unsupported file type"); + load_okay = false; + } + + if(load_okay == false) + { + if(err_msg.length() > 0) + { + arma_debug_warn_level(3, "SpMat::load(): ", err_msg, "; file: ", name); + } + else + { + arma_debug_warn_level(3, "SpMat::load(): read failed; file: ", name); + } + } + + if(load_okay == false) { (*this).reset(); } + + return load_okay; + } + + + +template<typename eT> +inline +bool +SpMat<eT>::load(const csv_name& spec, const file_type type) + { + arma_extra_debug_sigprint(); + + if( (type != csv_ascii) && (type != ssv_ascii) ) + { + arma_stop_runtime_error("SpMat::load(): unsupported file type for csv_name()"); + return false; + } + + const bool do_trans = bool(spec.opts.flags & csv_opts::flag_trans ); + const bool no_header = bool(spec.opts.flags & csv_opts::flag_no_header ); + const bool with_header = bool(spec.opts.flags & csv_opts::flag_with_header) && (no_header == false); + const bool use_semicolon = bool(spec.opts.flags & csv_opts::flag_semicolon ) || (type == ssv_ascii); + const bool strict = bool(spec.opts.flags & csv_opts::flag_strict ); + + arma_extra_debug_print("SpMat::load(csv_name): enabled flags:"); + + if(do_trans ) { arma_extra_debug_print("trans"); } + if(no_header ) { arma_extra_debug_print("no_header"); } + if(with_header ) { arma_extra_debug_print("with_header"); } + if(use_semicolon) { arma_extra_debug_print("semicolon"); } + if(strict ) { arma_extra_debug_print("strict"); } + + if(strict) { arma_debug_warn_level(1, "SpMat::load(): option 'strict' not implemented for sparse matrices"); } + + const char separator = (use_semicolon) ? char(';') : char(','); + + bool load_okay = false; + std::string err_msg; + + if(do_trans) + { + SpMat<eT> tmp_mat; + + load_okay = diskio::load_csv_ascii(tmp_mat, spec.filename, err_msg, spec.header_rw, with_header, separator); + + if(load_okay) + { + (*this) = tmp_mat.st(); + + if(with_header) + { + // field::set_size() preserves data if the number of elements hasn't changed + spec.header_rw.set_size(spec.header_rw.n_elem, 1); + } + } + } + else + { + load_okay = diskio::load_csv_ascii(*this, spec.filename, err_msg, spec.header_rw, with_header, separator); + } + + if(load_okay == false) + { + if(err_msg.length() > 0) + { + arma_debug_warn_level(3, "SpMat::load(): ", err_msg, "; file: ", spec.filename); + } + else + { + arma_debug_warn_level(3, "SpMat::load(): read failed; file: ", spec.filename); + } + } + else + { + const uword load_n_cols = (do_trans) ? (*this).n_rows : (*this).n_cols; + + if(with_header && (spec.header_rw.n_elem != load_n_cols)) + { + arma_debug_warn_level(3, "SpMat::load(): size mismatch between header and matrix"); + } + } + + if(load_okay == false) + { + (*this).reset(); + + if(with_header) { spec.header_rw.reset(); } + } + + return load_okay; + } + + + +//! load a matrix from a stream +template<typename eT> +inline +bool +SpMat<eT>::load(std::istream& is, const file_type type) + { + arma_extra_debug_sigprint(); + + invalidate_cache(); + + bool load_okay; + std::string err_msg; + + switch(type) + { + // case auto_detect: + // load_okay = diskio::load_auto_detect(*this, is, err_msg); + // break; + + case csv_ascii: + load_okay = diskio::load_csv_ascii(*this, is, err_msg, char(',')); + break; + + case ssv_ascii: + load_okay = diskio::load_csv_ascii(*this, is, err_msg, char(';')); + break; + + case arma_binary: + load_okay = diskio::load_arma_binary(*this, is, err_msg); + break; + + case coord_ascii: + load_okay = diskio::load_coord_ascii(*this, is, err_msg); + break; + + default: + arma_debug_warn_level(1, "SpMat::load(): unsupported file type"); + load_okay = false; + } + + if(load_okay == false) + { + if(err_msg.length() > 0) + { + arma_debug_warn_level(3, "SpMat::load(): ", err_msg); + } + else + { + arma_debug_warn_level(3, "SpMat::load(): stream read failed"); + } + } + + if(load_okay == false) { (*this).reset(); } + + return load_okay; + } + + + +template<typename eT> +inline +bool +SpMat<eT>::quiet_save(const std::string name, const file_type type) const + { + arma_extra_debug_sigprint(); + + return (*this).save(name, type); + } + + + +template<typename eT> +inline +bool +SpMat<eT>::quiet_save(std::ostream& os, const file_type type) const + { + arma_extra_debug_sigprint(); + + return (*this).save(os, type); + } + + + +template<typename eT> +inline +bool +SpMat<eT>::quiet_load(const std::string name, const file_type type) + { + arma_extra_debug_sigprint(); + + return (*this).load(name, type); + } + + + +template<typename eT> +inline +bool +SpMat<eT>::quiet_load(std::istream& is, const file_type type) + { + arma_extra_debug_sigprint(); + + return (*this).load(is, type); + } + + + +/** + * Initialize the matrix to the specified size. Data is not preserved, so the matrix is assumed to be entirely sparse (empty). + */ +template<typename eT> +inline +void +SpMat<eT>::init(uword in_rows, uword in_cols, const uword new_n_nonzero) + { + arma_extra_debug_sigprint(); + + invalidate_cache(); // placed here, as init() is used during matrix modification + + // Clean out the existing memory. + if(values ) { memory::release(access::rw(values)); } + if(row_indices) { memory::release(access::rw(row_indices)); } + if(col_ptrs ) { memory::release(access::rw(col_ptrs)); } + + // in case init_cold() throws an exception + access::rw(n_rows) = 0; + access::rw(n_cols) = 0; + access::rw(n_elem) = 0; + access::rw(n_nonzero) = 0; + access::rw(values) = nullptr; + access::rw(row_indices) = nullptr; + access::rw(col_ptrs) = nullptr; + + init_cold(in_rows, in_cols, new_n_nonzero); + } + + + +template<typename eT> +inline +void +SpMat<eT>::init_cold(uword in_rows, uword in_cols, const uword new_n_nonzero) + { + arma_extra_debug_sigprint(); + + // Verify that we are allowed to do this. + if(vec_state > 0) + { + if((in_rows == 0) && (in_cols == 0)) + { + if(vec_state == 1) { in_cols = 1; } + if(vec_state == 2) { in_rows = 1; } + } + else + { + if(vec_state == 1) { arma_debug_check( (in_cols != 1), "SpMat::init(): object is a column vector; requested size is not compatible" ); } + if(vec_state == 2) { arma_debug_check( (in_rows != 1), "SpMat::init(): object is a row vector; requested size is not compatible" ); } + } + } + + #if defined(ARMA_64BIT_WORD) + const char* error_message = "SpMat::init(): requested size is too large"; + #else + const char* error_message = "SpMat::init(): requested size is too large; suggest to enable ARMA_64BIT_WORD"; + #endif + + // Ensure that n_elem can hold the result of (n_rows * n_cols) + arma_debug_check + ( + ( + ( (in_rows > ARMA_MAX_UHWORD) || (in_cols > ARMA_MAX_UHWORD) ) + ? ( (double(in_rows) * double(in_cols)) > double(ARMA_MAX_UWORD) ) + : false + ), + error_message + ); + + access::rw(col_ptrs) = memory::acquire<uword>(in_cols + 2); + access::rw(values) = memory::acquire<eT> (new_n_nonzero + 1); + access::rw(row_indices) = memory::acquire<uword>(new_n_nonzero + 1); + + // fill column pointers with 0, + // except for the last element which contains the maximum possible element + // (so iterators terminate correctly). + arrayops::fill_zeros(access::rwp(col_ptrs), in_cols + 1); + + access::rw(col_ptrs[in_cols + 1]) = std::numeric_limits<uword>::max(); + + access::rw( values[new_n_nonzero]) = 0; + access::rw(row_indices[new_n_nonzero]) = 0; + + // Set the new size accordingly. + access::rw(n_rows) = in_rows; + access::rw(n_cols) = in_cols; + access::rw(n_elem) = (in_rows * in_cols); + access::rw(n_nonzero) = new_n_nonzero; + } + + + +template<typename eT> +inline +void +SpMat<eT>::init(const std::string& text) + { + arma_extra_debug_sigprint(); + + Mat<eT> tmp(text); + + if(vec_state == 1) + { + if((tmp.n_elem > 0) && tmp.is_vec()) + { + access::rw(tmp.n_rows) = tmp.n_elem; + access::rw(tmp.n_cols) = 1; + } + } + + if(vec_state == 2) + { + if((tmp.n_elem > 0) && tmp.is_vec()) + { + access::rw(tmp.n_rows) = 1; + access::rw(tmp.n_cols) = tmp.n_elem; + } + } + + (*this).operator=(tmp); + } + + + +template<typename eT> +inline +void +SpMat<eT>::init(const SpMat<eT>& x) + { + arma_extra_debug_sigprint(); + + if(this == &x) { return; } + + bool init_done = false; + + #if defined(ARMA_USE_OPENMP) + if(x.sync_state == 1) + { + #pragma omp critical (arma_SpMat_init) + if(x.sync_state == 1) + { + (*this).init(x.cache); + init_done = true; + } + } + #elif (!defined(ARMA_DONT_USE_STD_MUTEX)) + if(x.sync_state == 1) + { + const std::lock_guard<std::mutex> lock(x.cache_mutex); + + if(x.sync_state == 1) + { + (*this).init(x.cache); + init_done = true; + } + } + #else + if(x.sync_state == 1) + { + (*this).init(x.cache); + init_done = true; + } + #endif + + if(init_done == false) + { + (*this).init_simple(x); + } + } + + + +template<typename eT> +inline +void +SpMat<eT>::init(const MapMat<eT>& x) + { + arma_extra_debug_sigprint(); + + const uword x_n_rows = x.n_rows; + const uword x_n_cols = x.n_cols; + const uword x_n_nz = x.get_n_nonzero(); + + init(x_n_rows, x_n_cols, x_n_nz); + + if(x_n_nz == 0) { return; } + + typename MapMat<eT>::map_type& x_map_ref = *(x.map_ptr); + + typename MapMat<eT>::map_type::const_iterator x_it = x_map_ref.begin(); + + uword x_col = 0; + uword x_col_index_start = 0; + uword x_col_index_endp1 = x_n_rows; + + for(uword i=0; i < x_n_nz; ++i) + { + const std::pair<uword, eT>& x_entry = (*x_it); + + const uword x_index = x_entry.first; + const eT x_val = x_entry.second; + + // have we gone past the curent column? + if(x_index >= x_col_index_endp1) + { + x_col = x_index / x_n_rows; + + x_col_index_start = x_col * x_n_rows; + x_col_index_endp1 = x_col_index_start + x_n_rows; + } + + const uword x_row = x_index - x_col_index_start; + + // // sanity check + // + // const uword tmp_x_row = x_index % x_n_rows; + // const uword tmp_x_col = x_index / x_n_rows; + // + // if(x_row != tmp_x_row) { cout << "x_row != tmp_x_row" << endl; exit(-1); } + // if(x_col != tmp_x_col) { cout << "x_col != tmp_x_col" << endl; exit(-1); } + + access::rw(values[i]) = x_val; + access::rw(row_indices[i]) = x_row; + + access::rw(col_ptrs[ x_col + 1 ])++; + + ++x_it; + } + + + for(uword i = 0; i < x_n_cols; ++i) + { + access::rw(col_ptrs[i + 1]) += col_ptrs[i]; + } + + + // // OLD METHOD + // + // for(uword i=0; i < x_n_nz; ++i) + // { + // const std::pair<uword, eT>& x_entry = (*x_it); + // + // const uword x_index = x_entry.first; + // const eT x_val = x_entry.second; + // + // const uword x_row = x_index % x_n_rows; + // const uword x_col = x_index / x_n_rows; + // + // access::rw(values[i]) = x_val; + // access::rw(row_indices[i]) = x_row; + // + // access::rw(col_ptrs[ x_col + 1 ])++; + // + // ++x_it; + // } + // + // + // for(uword i = 0; i < x_n_cols; ++i) + // { + // access::rw(col_ptrs[i + 1]) += col_ptrs[i]; + // } + } + + + +template<typename eT> +inline +void +SpMat<eT>::init_simple(const SpMat<eT>& x) + { + arma_extra_debug_sigprint(); + + if(this == &x) { return; } + + if((x.n_nonzero == 0) && (n_nonzero == 0) && (n_rows == x.n_rows) && (n_cols == x.n_cols) && (values != nullptr)) + { + invalidate_cache(); + } + else + { + init(x.n_rows, x.n_cols, x.n_nonzero); + } + + if(x.n_nonzero != 0) + { + if(x.values ) { arrayops::copy(access::rwp(values), x.values, x.n_nonzero + 1); } + if(x.row_indices) { arrayops::copy(access::rwp(row_indices), x.row_indices, x.n_nonzero + 1); } + if(x.col_ptrs ) { arrayops::copy(access::rwp(col_ptrs), x.col_ptrs, x.n_cols + 1); } + } + } + + + +template<typename eT> +inline +void +SpMat<eT>::init_batch_std(const Mat<uword>& locs, const Mat<eT>& vals, const bool sort_locations) + { + arma_extra_debug_sigprint(); + + // Resize to correct number of elements. + mem_resize(vals.n_elem); + + // Reset column pointers to zero. + arrayops::fill_zeros(access::rwp(col_ptrs), n_cols + 1); + + bool actually_sorted = true; + + if(sort_locations) + { + // check if we really need a time consuming sort + + const uword locs_n_cols = locs.n_cols; + + for(uword i = 1; i < locs_n_cols; ++i) + { + const uword* locs_i = locs.colptr(i ); + const uword* locs_im1 = locs.colptr(i-1); + + const uword row_i = locs_i[0]; + const uword col_i = locs_i[1]; + + const uword row_im1 = locs_im1[0]; + const uword col_im1 = locs_im1[1]; + + if( (col_i < col_im1) || ((col_i == col_im1) && (row_i <= row_im1)) ) + { + actually_sorted = false; + break; + } + } + + if(actually_sorted == false) + { + // see op_sort_index_bones.hpp for the definition of arma_sort_index_packet and arma_sort_index_helper_ascend + + std::vector< arma_sort_index_packet<uword> > packet_vec(locs_n_cols); + + const uword* locs_mem = locs.memptr(); + + for(uword i = 0; i < locs_n_cols; ++i) + { + const uword row = (*locs_mem); locs_mem++; + const uword col = (*locs_mem); locs_mem++; + + packet_vec[i].val = (col * n_rows) + row; + packet_vec[i].index = i; + } + + arma_sort_index_helper_ascend<uword> comparator; + + std::sort( packet_vec.begin(), packet_vec.end(), comparator ); + + // insert the elements in the sorted order + for(uword i = 0; i < locs_n_cols; ++i) + { + const uword index = packet_vec[i].index; + + const uword* locs_i = locs.colptr(index); + + const uword row_i = locs_i[0]; + const uword col_i = locs_i[1]; + + arma_debug_check( ( (row_i >= n_rows) || (col_i >= n_cols) ), "SpMat::SpMat(): invalid row or column index" ); + + if(i > 0) + { + const uword prev_index = packet_vec[i-1].index; + + const uword* locs_im1 = locs.colptr(prev_index); + + const uword row_im1 = locs_im1[0]; + const uword col_im1 = locs_im1[1]; + + arma_debug_check( ( (row_i == row_im1) && (col_i == col_im1) ), "SpMat::SpMat(): detected identical locations" ); + } + + access::rw(values[i]) = vals[index]; + access::rw(row_indices[i]) = row_i; + + access::rw(col_ptrs[ col_i + 1 ])++; + } + } + } + + if( (sort_locations == false) || (actually_sorted == true) ) + { + // Now set the values and row indices correctly. + // Increment the column pointers in each column (so they are column "counts"). + + const uword locs_n_cols = locs.n_cols; + + for(uword i=0; i < locs_n_cols; ++i) + { + const uword* locs_i = locs.colptr(i); + + const uword row_i = locs_i[0]; + const uword col_i = locs_i[1]; + + arma_debug_check( ( (row_i >= n_rows) || (col_i >= n_cols) ), "SpMat::SpMat(): invalid row or column index" ); + + if(i > 0) + { + const uword* locs_im1 = locs.colptr(i-1); + + const uword row_im1 = locs_im1[0]; + const uword col_im1 = locs_im1[1]; + + arma_debug_check + ( + ( (col_i < col_im1) || ((col_i == col_im1) && (row_i < row_im1)) ), + "SpMat::SpMat(): out of order points; either pass sort_locations = true, or sort points in column-major ordering" + ); + + arma_debug_check( ( (col_i == col_im1) && (row_i == row_im1) ), "SpMat::SpMat(): detected identical locations" ); + } + + access::rw(values[i]) = vals[i]; + access::rw(row_indices[i]) = row_i; + + access::rw(col_ptrs[ col_i + 1 ])++; + } + } + + // Now fix the column pointers. + for(uword i = 0; i < n_cols; ++i) + { + access::rw(col_ptrs[i + 1]) += col_ptrs[i]; + } + } + + + +template<typename eT> +inline +void +SpMat<eT>::init_batch_add(const Mat<uword>& locs, const Mat<eT>& vals, const bool sort_locations) + { + arma_extra_debug_sigprint(); + + if(locs.n_cols < 2) + { + init_batch_std(locs, vals, false); + return; + } + + // Reset column pointers to zero. + arrayops::fill_zeros(access::rwp(col_ptrs), n_cols + 1); + + bool actually_sorted = true; + + if(sort_locations) + { + // sort_index() uses std::sort() which may use quicksort... so we better + // make sure it's not already sorted before taking an O(N^2) sort penalty. + for(uword i = 1; i < locs.n_cols; ++i) + { + const uword* locs_i = locs.colptr(i ); + const uword* locs_im1 = locs.colptr(i-1); + + if( (locs_i[1] < locs_im1[1]) || (locs_i[1] == locs_im1[1] && locs_i[0] <= locs_im1[0]) ) + { + actually_sorted = false; + break; + } + } + + if(actually_sorted == false) + { + // This may not be the fastest possible implementation but it maximizes code reuse. + Col<uword> abslocs(locs.n_cols, arma_nozeros_indicator()); + + for(uword i = 0; i < locs.n_cols; ++i) + { + const uword* locs_i = locs.colptr(i); + + abslocs[i] = locs_i[1] * n_rows + locs_i[0]; + } + + uvec sorted_indices = sort_index(abslocs); // Ascending sort. + + // work out the number of unique elments + uword n_unique = 1; // first element is unique + + for(uword i=1; i < sorted_indices.n_elem; ++i) + { + const uword* locs_i = locs.colptr( sorted_indices[i ] ); + const uword* locs_im1 = locs.colptr( sorted_indices[i-1] ); + + if( (locs_i[1] != locs_im1[1]) || (locs_i[0] != locs_im1[0]) ) { ++n_unique; } + } + + // resize to correct number of elements + mem_resize(n_unique); + + // Now we add the elements in this sorted order. + uword count = 0; + + // first element + { + const uword i = 0; + const uword* locs_i = locs.colptr( sorted_indices[i] ); + + arma_debug_check( ( (locs_i[0] >= n_rows) || (locs_i[1] >= n_cols) ), "SpMat::SpMat(): invalid row or column index" ); + + access::rw(values[count]) = vals[ sorted_indices[i] ]; + access::rw(row_indices[count]) = locs_i[0]; + + access::rw(col_ptrs[ locs_i[1] + 1 ])++; + } + + for(uword i=1; i < sorted_indices.n_elem; ++i) + { + const uword* locs_i = locs.colptr( sorted_indices[i ] ); + const uword* locs_im1 = locs.colptr( sorted_indices[i-1] ); + + arma_debug_check( ( (locs_i[0] >= n_rows) || (locs_i[1] >= n_cols) ), "SpMat::SpMat(): invalid row or column index" ); + + if( (locs_i[1] == locs_im1[1]) && (locs_i[0] == locs_im1[0]) ) + { + access::rw(values[count]) += vals[ sorted_indices[i] ]; + } + else + { + count++; + access::rw(values[count]) = vals[ sorted_indices[i] ]; + access::rw(row_indices[count]) = locs_i[0]; + + access::rw(col_ptrs[ locs_i[1] + 1 ])++; + } + } + } + } + + if( (sort_locations == false) || (actually_sorted == true) ) + { + // work out the number of unique elments + uword n_unique = 1; // first element is unique + + for(uword i=1; i < locs.n_cols; ++i) + { + const uword* locs_i = locs.colptr(i ); + const uword* locs_im1 = locs.colptr(i-1); + + if( (locs_i[1] != locs_im1[1]) || (locs_i[0] != locs_im1[0]) ) { ++n_unique; } + } + + // resize to correct number of elements + mem_resize(n_unique); + + // Now set the values and row indices correctly. + // Increment the column pointers in each column (so they are column "counts"). + + uword count = 0; + + // first element + { + const uword i = 0; + const uword* locs_i = locs.colptr(i); + + arma_debug_check( ( (locs_i[0] >= n_rows) || (locs_i[1] >= n_cols) ), "SpMat::SpMat(): invalid row or column index" ); + + access::rw(values[count]) = vals[i]; + access::rw(row_indices[count]) = locs_i[0]; + + access::rw(col_ptrs[ locs_i[1] + 1 ])++; + } + + for(uword i=1; i < locs.n_cols; ++i) + { + const uword* locs_i = locs.colptr(i ); + const uword* locs_im1 = locs.colptr(i-1); + + arma_debug_check( ( (locs_i[0] >= n_rows) || (locs_i[1] >= n_cols) ), "SpMat::SpMat(): invalid row or column index" ); + + arma_debug_check + ( + ( (locs_i[1] < locs_im1[1]) || (locs_i[1] == locs_im1[1] && locs_i[0] < locs_im1[0]) ), + "SpMat::SpMat(): out of order points; either pass sort_locations = true, or sort points in column-major ordering" + ); + + if( (locs_i[1] == locs_im1[1]) && (locs_i[0] == locs_im1[0]) ) + { + access::rw(values[count]) += vals[i]; + } + else + { + count++; + + access::rw(values[count]) = vals[i]; + access::rw(row_indices[count]) = locs_i[0]; + + access::rw(col_ptrs[ locs_i[1] + 1 ])++; + } + } + } + + // Now fix the column pointers. + for(uword i = 0; i < n_cols; ++i) + { + access::rw(col_ptrs[i + 1]) += col_ptrs[i]; + } + } + + + +//! constructor used by SpRow and SpCol classes +template<typename eT> +inline +SpMat<eT>::SpMat(const arma_vec_indicator&, const uword in_vec_state) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(in_vec_state) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + const uword in_n_rows = (in_vec_state == 2) ? 1 : 0; + const uword in_n_cols = (in_vec_state == 1) ? 1 : 0; + + init_cold(in_n_rows, in_n_cols); + } + + + +//! constructor used by SpRow and SpCol classes +template<typename eT> +inline +SpMat<eT>::SpMat(const arma_vec_indicator&, const uword in_n_rows, const uword in_n_cols, const uword in_vec_state) + : n_rows(0) + , n_cols(0) + , n_elem(0) + , n_nonzero(0) + , vec_state(in_vec_state) + , values(nullptr) + , row_indices(nullptr) + , col_ptrs(nullptr) + { + arma_extra_debug_sigprint_this(this); + + init_cold(in_n_rows, in_n_cols); + } + + + +template<typename eT> +inline +void +SpMat<eT>::mem_resize(const uword new_n_nonzero) + { + arma_extra_debug_sigprint(); + + invalidate_cache(); // placed here, as mem_resize() is used during matrix modification + + if(n_nonzero == new_n_nonzero) { return; } + + eT* new_values = memory::acquire<eT> (new_n_nonzero + 1); + uword* new_row_indices = memory::acquire<uword>(new_n_nonzero + 1); + + if( (n_nonzero > 0 ) && (new_n_nonzero > 0) ) + { + // Copy old elements. + uword copy_len = (std::min)(n_nonzero, new_n_nonzero); + + arrayops::copy(new_values, values, copy_len); + arrayops::copy(new_row_indices, row_indices, copy_len); + } + + if(values) { memory::release(access::rw(values)); } + if(row_indices) { memory::release(access::rw(row_indices)); } + + access::rw(values) = new_values; + access::rw(row_indices) = new_row_indices; + + // Set the "fake end" of the matrix by setting the last value and row index to 0. + // This helps the iterators work correctly. + access::rw( values[new_n_nonzero]) = 0; + access::rw(row_indices[new_n_nonzero]) = 0; + + access::rw(n_nonzero) = new_n_nonzero; + } + + + +template<typename eT> +inline +void +SpMat<eT>::sync() const + { + arma_extra_debug_sigprint(); + + sync_csc(); + } + + + +template<typename eT> +inline +void +SpMat<eT>::remove_zeros() + { + arma_extra_debug_sigprint(); + + sync_csc(); + + invalidate_cache(); // placed here, as remove_zeros() is used during matrix modification + + const uword old_n_nonzero = n_nonzero; + uword new_n_nonzero = 0; + + const eT* old_values = values; + + constexpr eT zero = eT(0); + + for(uword i=0; i < old_n_nonzero; ++i) + { + new_n_nonzero += (old_values[i] != zero) ? uword(1) : uword(0); + } + + if(new_n_nonzero != old_n_nonzero) + { + if(new_n_nonzero == 0) { init(n_rows, n_cols); return; } + + SpMat<eT> tmp(arma_reserve_indicator(), n_rows, n_cols, new_n_nonzero); + + uword new_index = 0; + + const_iterator it = cbegin(); + const_iterator it_end = cend(); + + for(; it != it_end; ++it) + { + const eT val = eT(*it); + + if(val != zero) + { + const uword it_row = it.row(); + const uword it_col = it.col(); + + access::rw(tmp.values[new_index]) = val; + access::rw(tmp.row_indices[new_index]) = it_row; + access::rw(tmp.col_ptrs[it_col + 1])++; + ++new_index; + } + } + + for(uword i=0; i < n_cols; ++i) + { + access::rw(tmp.col_ptrs[i + 1]) += tmp.col_ptrs[i]; + } + + steal_mem(tmp); + } + } + + + +// Steal memory from another matrix. +template<typename eT> +inline +void +SpMat<eT>::steal_mem(SpMat<eT>& x) + { + arma_extra_debug_sigprint(); + + if(this == &x) { return; } + + bool layout_ok = false; + + if((*this).vec_state == x.vec_state) + { + layout_ok = true; + } + else + { + if( ((*this).vec_state == 1) && (x.n_cols == 1) ) { layout_ok = true; } + if( ((*this).vec_state == 2) && (x.n_rows == 1) ) { layout_ok = true; } + } + + if(layout_ok) + { + arma_extra_debug_print("SpMat::steal_mem(): stealing memory"); + + x.sync_csc(); + + steal_mem_simple(x); + + x.invalidate_cache(); + + invalidate_cache(); + } + else + { + arma_extra_debug_print("SpMat::steal_mem(): copying memory"); + + (*this).operator=(x); + } + } + + + +template<typename eT> +inline +void +SpMat<eT>::steal_mem_simple(SpMat<eT>& x) + { + arma_extra_debug_sigprint(); + + if(this == &x) { return; } + + if(values ) { memory::release(access::rw(values)); } + if(row_indices) { memory::release(access::rw(row_indices)); } + if(col_ptrs ) { memory::release(access::rw(col_ptrs)); } + + access::rw(n_rows) = x.n_rows; + access::rw(n_cols) = x.n_cols; + access::rw(n_elem) = x.n_elem; + access::rw(n_nonzero) = x.n_nonzero; + + access::rw(values) = x.values; + access::rw(row_indices) = x.row_indices; + access::rw(col_ptrs) = x.col_ptrs; + + // Set other matrix to empty. + access::rw(x.n_rows) = 0; + access::rw(x.n_cols) = 0; + access::rw(x.n_elem) = 0; + access::rw(x.n_nonzero) = 0; + + access::rw(x.values) = nullptr; + access::rw(x.row_indices) = nullptr; + access::rw(x.col_ptrs) = nullptr; + } + + + +template<typename eT> +template<typename T1, typename Functor> +inline +void +SpMat<eT>::init_xform(const SpBase<eT,T1>& A, const Functor& func) + { + arma_extra_debug_sigprint(); + + // if possible, avoid doing a copy and instead apply func to the generated elements + if(SpProxy<T1>::Q_is_generated) + { + (*this) = A.get_ref(); + + const uword nnz = n_nonzero; + + eT* t_values = access::rwp(values); + + bool has_zero = false; + + for(uword i=0; i < nnz; ++i) + { + eT& t_values_i = t_values[i]; + + t_values_i = func(t_values_i); + + if(t_values_i == eT(0)) { has_zero = true; } + } + + if(has_zero) { remove_zeros(); } + } + else + { + init_xform_mt(A.get_ref(), func); + } + } + + + +template<typename eT> +template<typename eT2, typename T1, typename Functor> +inline +void +SpMat<eT>::init_xform_mt(const SpBase<eT2,T1>& A, const Functor& func) + { + arma_extra_debug_sigprint(); + + const SpProxy<T1> P(A.get_ref()); + + if( P.is_alias(*this) || (is_SpMat<typename SpProxy<T1>::stored_type>::value) ) + { + // NOTE: unwrap_spmat will convert a submatrix to a matrix, which in effect takes care of aliasing with submatrices; + // NOTE: however, when more delayed ops are implemented, more elaborate handling of aliasing will be necessary + const unwrap_spmat<typename SpProxy<T1>::stored_type> tmp(P.Q); + + const SpMat<eT2>& x = tmp.M; + + if(void_ptr(this) != void_ptr(&x)) + { + init(x.n_rows, x.n_cols, x.n_nonzero); + + arrayops::copy(access::rwp(row_indices), x.row_indices, x.n_nonzero + 1); + arrayops::copy(access::rwp(col_ptrs), x.col_ptrs, x.n_cols + 1); + } + + + // initialise the elements array with a transformed version of the elements from x + + const uword nnz = n_nonzero; + + const eT2* x_values = x.values; + eT* t_values = access::rwp(values); + + bool has_zero = false; + + for(uword i=0; i < nnz; ++i) + { + eT& t_values_i = t_values[i]; + + t_values_i = func(x_values[i]); // NOTE: func() must produce a value of type eT (ie. act as a convertor between eT2 and eT) + + if(t_values_i == eT(0)) { has_zero = true; } + } + + if(has_zero) { remove_zeros(); } + } + else + { + init(P.get_n_rows(), P.get_n_cols(), P.get_n_nonzero()); + + typename SpProxy<T1>::const_iterator_type it = P.begin(); + typename SpProxy<T1>::const_iterator_type it_end = P.end(); + + bool has_zero = false; + + while(it != it_end) + { + const eT val = func(*it); // NOTE: func() must produce a value of type eT (ie. act as a convertor between eT2 and eT) + + if(val == eT(0)) { has_zero = true; } + + const uword it_pos = it.pos(); + + access::rw(row_indices[it_pos]) = it.row(); + access::rw(values[it_pos]) = val; + ++access::rw(col_ptrs[it.col() + 1]); + ++it; + } + + // Now sum column pointers. + for(uword c = 1; c <= n_cols; ++c) + { + access::rw(col_ptrs[c]) += col_ptrs[c - 1]; + } + + if(has_zero) { remove_zeros(); } + } + } + + + +template<typename eT> +arma_inline +bool +SpMat<eT>::is_alias(const SpMat<eT>& X) const + { + return (&X == this); + } + + + +template<typename eT> +inline +typename SpMat<eT>::iterator +SpMat<eT>::begin() + { + arma_extra_debug_sigprint(); + + sync_csc(); + + return iterator(*this); + } + + + +template<typename eT> +inline +typename SpMat<eT>::const_iterator +SpMat<eT>::begin() const + { + arma_extra_debug_sigprint(); + + sync_csc(); + + return const_iterator(*this); + } + + + +template<typename eT> +inline +typename SpMat<eT>::const_iterator +SpMat<eT>::cbegin() const + { + arma_extra_debug_sigprint(); + + sync_csc(); + + return const_iterator(*this); + } + + + +template<typename eT> +inline +typename SpMat<eT>::iterator +SpMat<eT>::end() + { + sync_csc(); + + return iterator(*this, 0, n_cols, n_nonzero); + } + + + +template<typename eT> +inline +typename SpMat<eT>::const_iterator +SpMat<eT>::end() const + { + sync_csc(); + + return const_iterator(*this, 0, n_cols, n_nonzero); + } + + + +template<typename eT> +inline +typename SpMat<eT>::const_iterator +SpMat<eT>::cend() const + { + sync_csc(); + + return const_iterator(*this, 0, n_cols, n_nonzero); + } + + + +template<typename eT> +inline +typename SpMat<eT>::col_iterator +SpMat<eT>::begin_col(const uword col_num) + { + sync_csc(); + + return col_iterator(*this, 0, col_num); + } + + + +template<typename eT> +inline +typename SpMat<eT>::const_col_iterator +SpMat<eT>::begin_col(const uword col_num) const + { + sync_csc(); + + return const_col_iterator(*this, 0, col_num); + } + + + +template<typename eT> +inline +typename SpMat<eT>::col_iterator +SpMat<eT>::begin_col_no_sync(const uword col_num) + { + return col_iterator(*this, 0, col_num); + } + + + +template<typename eT> +inline +typename SpMat<eT>::const_col_iterator +SpMat<eT>::begin_col_no_sync(const uword col_num) const + { + return const_col_iterator(*this, 0, col_num); + } + + + +template<typename eT> +inline +typename SpMat<eT>::col_iterator +SpMat<eT>::end_col(const uword col_num) + { + sync_csc(); + + return col_iterator(*this, 0, col_num + 1); + } + + + +template<typename eT> +inline +typename SpMat<eT>::const_col_iterator +SpMat<eT>::end_col(const uword col_num) const + { + sync_csc(); + + return const_col_iterator(*this, 0, col_num + 1); + } + + + +template<typename eT> +inline +typename SpMat<eT>::col_iterator +SpMat<eT>::end_col_no_sync(const uword col_num) + { + return col_iterator(*this, 0, col_num + 1); + } + + + +template<typename eT> +inline +typename SpMat<eT>::const_col_iterator +SpMat<eT>::end_col_no_sync(const uword col_num) const + { + return const_col_iterator(*this, 0, col_num + 1); + } + + + +template<typename eT> +inline +typename SpMat<eT>::row_iterator +SpMat<eT>::begin_row(const uword row_num) + { + sync_csc(); + + return row_iterator(*this, row_num, 0); + } + + + +template<typename eT> +inline +typename SpMat<eT>::const_row_iterator +SpMat<eT>::begin_row(const uword row_num) const + { + sync_csc(); + + return const_row_iterator(*this, row_num, 0); + } + + + +template<typename eT> +inline +typename SpMat<eT>::row_iterator +SpMat<eT>::end_row() + { + sync_csc(); + + return row_iterator(*this, n_nonzero); + } + + + +template<typename eT> +inline +typename SpMat<eT>::const_row_iterator +SpMat<eT>::end_row() const + { + sync_csc(); + + return const_row_iterator(*this, n_nonzero); + } + + + +template<typename eT> +inline +typename SpMat<eT>::row_iterator +SpMat<eT>::end_row(const uword row_num) + { + sync_csc(); + + return row_iterator(*this, row_num + 1, 0); + } + + + +template<typename eT> +inline +typename SpMat<eT>::const_row_iterator +SpMat<eT>::end_row(const uword row_num) const + { + sync_csc(); + + return const_row_iterator(*this, row_num + 1, 0); + } + + + +template<typename eT> +inline +typename SpMat<eT>::row_col_iterator +SpMat<eT>::begin_row_col() + { + sync_csc(); + + return begin(); + } + + + +template<typename eT> +inline +typename SpMat<eT>::const_row_col_iterator +SpMat<eT>::begin_row_col() const + { + sync_csc(); + + return begin(); + } + + + +template<typename eT> +inline typename SpMat<eT>::row_col_iterator +SpMat<eT>::end_row_col() + { + sync_csc(); + + return end(); + } + + + +template<typename eT> +inline +typename SpMat<eT>::const_row_col_iterator +SpMat<eT>::end_row_col() const + { + sync_csc(); + + return end(); + } + + + +template<typename eT> +inline +void +SpMat<eT>::clear() + { + (*this).reset(); + } + + + +template<typename eT> +inline +bool +SpMat<eT>::empty() const + { + return (n_elem == 0); + } + + + +template<typename eT> +inline +uword +SpMat<eT>::size() const + { + return n_elem; + } + + + +template<typename eT> +arma_inline +SpMat_MapMat_val<eT> +SpMat<eT>::front() + { + arma_debug_check( (n_elem == 0), "SpMat::front(): matrix is empty" ); + + return SpMat_MapMat_val<eT>((*this), cache, 0, 0); + } + + + +template<typename eT> +arma_inline +eT +SpMat<eT>::front() const + { + arma_debug_check( (n_elem == 0), "SpMat::front(): matrix is empty" ); + + return get_value(0,0); + } + + + +template<typename eT> +arma_inline +SpMat_MapMat_val<eT> +SpMat<eT>::back() + { + arma_debug_check( (n_elem == 0), "SpMat::back(): matrix is empty" ); + + return SpMat_MapMat_val<eT>((*this), cache, n_rows-1, n_cols-1); + } + + + +template<typename eT> +arma_inline +eT +SpMat<eT>::back() const + { + arma_debug_check( (n_elem == 0), "SpMat::back(): matrix is empty" ); + + return get_value(n_rows-1, n_cols-1); + } + + + +template<typename eT> +inline +eT +SpMat<eT>::get_value(const uword i) const + { + const MapMat<eT>& const_cache = cache; // declare as const for clarity of intent + + // get the element from the cache if it has more recent data than CSC + + return (sync_state == 1) ? const_cache.operator[](i) : get_value_csc(i); + } + + + +template<typename eT> +inline +eT +SpMat<eT>::get_value(const uword in_row, const uword in_col) const + { + const MapMat<eT>& const_cache = cache; // declare as const for clarity of intent + + // get the element from the cache if it has more recent data than CSC + + return (sync_state == 1) ? const_cache.at(in_row, in_col) : get_value_csc(in_row, in_col); + } + + + +template<typename eT> +inline +eT +SpMat<eT>::get_value_csc(const uword i) const + { + // First convert to the actual location. + uword lcol = i / n_rows; // Integer division. + uword lrow = i % n_rows; + + return get_value_csc(lrow, lcol); + } + + + +template<typename eT> +inline +const eT* +SpMat<eT>::find_value_csc(const uword in_row, const uword in_col) const + { + const uword col_offset = col_ptrs[in_col ]; + const uword next_col_offset = col_ptrs[in_col + 1]; + + const uword* start_ptr = &row_indices[ col_offset]; + const uword* end_ptr = &row_indices[next_col_offset]; + + const uword* pos_ptr = std::lower_bound(start_ptr, end_ptr, in_row); // binary search + + if( (pos_ptr != end_ptr) && ((*pos_ptr) == in_row) ) + { + const uword offset = uword(pos_ptr - start_ptr); + const uword index = offset + col_offset; + + return &(values[index]); + } + + return nullptr; + } + + + +template<typename eT> +inline +eT +SpMat<eT>::get_value_csc(const uword in_row, const uword in_col) const + { + const eT* val_ptr = find_value_csc(in_row, in_col); + + return (val_ptr != nullptr) ? eT(*val_ptr) : eT(0); + } + + + +template<typename eT> +inline +bool +SpMat<eT>::try_set_value_csc(const uword in_row, const uword in_col, const eT in_val) + { + const eT* val_ptr = find_value_csc(in_row, in_col); + + // element not found, ie. it's zero; fail if trying to set it to non-zero value + if(val_ptr == nullptr) { return (in_val == eT(0)); } + + // fail if trying to erase an existing element + if(in_val == eT(0)) { return false; } + + access::rw(*val_ptr) = in_val; + + invalidate_cache(); + + return true; + } + + + +template<typename eT> +inline +bool +SpMat<eT>::try_add_value_csc(const uword in_row, const uword in_col, const eT in_val) + { + const eT* val_ptr = find_value_csc(in_row, in_col); + + // element not found, ie. it's zero; fail if trying to add a non-zero value + if(val_ptr == nullptr) { return (in_val == eT(0)); } + + const eT new_val = eT(*val_ptr) + in_val; + + // fail if trying to erase an existing element + if(new_val == eT(0)) { return false; } + + access::rw(*val_ptr) = new_val; + + invalidate_cache(); + + return true; + } + + + +template<typename eT> +inline +bool +SpMat<eT>::try_sub_value_csc(const uword in_row, const uword in_col, const eT in_val) + { + const eT* val_ptr = find_value_csc(in_row, in_col); + + // element not found, ie. it's zero; fail if trying to subtract a non-zero value + if(val_ptr == nullptr) { return (in_val == eT(0)); } + + const eT new_val = eT(*val_ptr) - in_val; + + // fail if trying to erase an existing element + if(new_val == eT(0)) { return false; } + + access::rw(*val_ptr) = new_val; + + invalidate_cache(); + + return true; + } + + + +template<typename eT> +inline +bool +SpMat<eT>::try_mul_value_csc(const uword in_row, const uword in_col, const eT in_val) + { + const eT* val_ptr = find_value_csc(in_row, in_col); + + // element not found, ie. it's zero; succeed if given value is finite; zero multiplied by anything is zero, except for nan and inf + if(val_ptr == nullptr) { return arma_isfinite(in_val); } + + const eT new_val = eT(*val_ptr) * in_val; + + // fail if trying to erase an existing element + if(new_val == eT(0)) { return false; } + + access::rw(*val_ptr) = new_val; + + invalidate_cache(); + + return true; + } + + + +template<typename eT> +inline +bool +SpMat<eT>::try_div_value_csc(const uword in_row, const uword in_col, const eT in_val) + { + const eT* val_ptr = find_value_csc(in_row, in_col); + + // element not found, ie. it's zero; succeed if given value is not zero and not nan; zero divided by anything is zero, except for zero and nan + if(val_ptr == nullptr) { return ((in_val != eT(0)) && (arma_isnan(in_val) == false)); } + + const eT new_val = eT(*val_ptr) / in_val; + + // fail if trying to erase an existing element + if(new_val == eT(0)) { return false; } + + access::rw(*val_ptr) = new_val; + + invalidate_cache(); + + return true; + } + + + +/** + * Insert an element at the given position, and return a reference to it. + * The element will be set to 0, unless otherwise specified. + * If the element already exists, its value will be overwritten. + */ +template<typename eT> +inline +eT& +SpMat<eT>::insert_element(const uword in_row, const uword in_col, const eT val) + { + arma_extra_debug_sigprint(); + + sync_csc(); + invalidate_cache(); + + // We will assume the new element does not exist and begin the search for + // where to insert it. If we find that it already exists, we will then + // overwrite it. + uword colptr = col_ptrs[in_col ]; + uword next_colptr = col_ptrs[in_col + 1]; + + uword pos = colptr; // The position in the matrix of this value. + + if(colptr != next_colptr) + { + // There are other elements in this column, so we must find where this + // element will fit as compared to those. + while(pos < next_colptr && in_row > row_indices[pos]) + { + pos++; + } + + // We aren't inserting into the last position, so it is still possible + // that the element may exist. + if(pos != next_colptr && row_indices[pos] == in_row) + { + // It already exists. Then, just overwrite it. + access::rw(values[pos]) = val; + + return access::rw(values[pos]); + } + } + + + // + // Element doesn't exist, so we have to insert it + // + + // We have to update the rest of the column pointers. + for(uword i = in_col + 1; i < n_cols + 1; i++) + { + access::rw(col_ptrs[i])++; // We are only inserting one new element. + } + + const uword old_n_nonzero = n_nonzero; + + access::rw(n_nonzero)++; // Add to count of nonzero elements. + + // Allocate larger memory. + eT* new_values = memory::acquire<eT> (n_nonzero + 1); + uword* new_row_indices = memory::acquire<uword>(n_nonzero + 1); + + // Copy things over, before the new element. + if(pos > 0) + { + arrayops::copy(new_values, values, pos); + arrayops::copy(new_row_indices, row_indices, pos); + } + + // Insert the new element. + new_values[pos] = val; + new_row_indices[pos] = in_row; + + // Copy the rest of things over (including the extra element at the end). + arrayops::copy(new_values + pos + 1, values + pos, (old_n_nonzero - pos) + 1); + arrayops::copy(new_row_indices + pos + 1, row_indices + pos, (old_n_nonzero - pos) + 1); + + // Assign new pointers. + if(values) { memory::release(access::rw(values)); } + if(row_indices) { memory::release(access::rw(row_indices)); } + + access::rw(values) = new_values; + access::rw(row_indices) = new_row_indices; + + return access::rw(values[pos]); + } + + + +/** + * Delete an element at the given position. + */ +template<typename eT> +inline +void +SpMat<eT>::delete_element(const uword in_row, const uword in_col) + { + arma_extra_debug_sigprint(); + + sync_csc(); + invalidate_cache(); + + // We assume the element exists (although... it may not) and look for its + // exact position. If it doesn't exist... well, we don't need to do anything. + uword colptr = col_ptrs[in_col]; + uword next_colptr = col_ptrs[in_col + 1]; + + if(colptr != next_colptr) + { + // There's at least one element in this column. + // Let's see if we are one of them. + for(uword pos = colptr; pos < next_colptr; pos++) + { + if(in_row == row_indices[pos]) + { + --access::rw(n_nonzero); // Remove one from the count of nonzero elements. + + // Found it. Now remove it. + + // Make new arrays. + eT* new_values = memory::acquire<eT> (n_nonzero + 1); + uword* new_row_indices = memory::acquire<uword>(n_nonzero + 1); + + if(pos > 0) + { + arrayops::copy(new_values, values, pos); + arrayops::copy(new_row_indices, row_indices, pos); + } + + arrayops::copy(new_values + pos, values + pos + 1, (n_nonzero - pos) + 1); + arrayops::copy(new_row_indices + pos, row_indices + pos + 1, (n_nonzero - pos) + 1); + + if(values) { memory::release(access::rw(values)); } + if(row_indices) { memory::release(access::rw(row_indices)); } + + access::rw(values) = new_values; + access::rw(row_indices) = new_row_indices; + + // And lastly, update all the column pointers (decrement by one). + for(uword i = in_col + 1; i < n_cols + 1; i++) + { + --access::rw(col_ptrs[i]); // We only removed one element. + } + + return; // There is nothing left to do. + } + } + } + + return; // The element does not exist, so there's nothing for us to do. + } + + + +template<typename eT> +arma_inline +void +SpMat<eT>::invalidate_cache() const + { + arma_extra_debug_sigprint(); + + if(sync_state == 0) { return; } + + cache.reset(); + + sync_state = 0; + } + + + +template<typename eT> +arma_inline +void +SpMat<eT>::invalidate_csc() const + { + arma_extra_debug_sigprint(); + + sync_state = 1; + } + + + +template<typename eT> +inline +void +SpMat<eT>::sync_cache() const + { + arma_extra_debug_sigprint(); + + // using approach adapted from http://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/ + // + // OpenMP mode: + // sync_state uses atomic read/write, which has an implied flush; + // flush is also implicitly executed at the entrance and the exit of critical section; + // data races are prevented by the 'critical' directive + // + // C++11 mode: + // underlying type for sync_state is std::atomic<int>; + // reading and writing to sync_state uses std::memory_order_seq_cst which has an implied fence; + // data races are prevented via the mutex + + #if defined(ARMA_USE_OPENMP) + { + if(sync_state == 0) + { + #pragma omp critical (arma_SpMat_cache) + { + sync_cache_simple(); + } + } + } + #elif (!defined(ARMA_DONT_USE_STD_MUTEX)) + { + if(sync_state == 0) + { + const std::lock_guard<std::mutex> lock(cache_mutex); + + sync_cache_simple(); + } + } + #else + { + sync_cache_simple(); + } + #endif + } + + + + +template<typename eT> +inline +void +SpMat<eT>::sync_cache_simple() const + { + arma_extra_debug_sigprint(); + + if(sync_state == 0) + { + cache = (*this); + sync_state = 2; + } + } + + + + +template<typename eT> +inline +void +SpMat<eT>::sync_csc() const + { + arma_extra_debug_sigprint(); + + #if defined(ARMA_USE_OPENMP) + if(sync_state == 1) + { + #pragma omp critical (arma_SpMat_cache) + { + sync_csc_simple(); + } + } + #elif (!defined(ARMA_DONT_USE_STD_MUTEX)) + if(sync_state == 1) + { + const std::lock_guard<std::mutex> lock(cache_mutex); + + sync_csc_simple(); + } + #else + { + sync_csc_simple(); + } + #endif + } + + + +template<typename eT> +inline +void +SpMat<eT>::sync_csc_simple() const + { + arma_extra_debug_sigprint(); + + // method: + // 1. construct temporary matrix to prevent the cache from getting zapped + // 2. steal memory from the temporary matrix + + // sync_state is only set to 1 by non-const element access operators, + // so the shenanigans with const_cast are to satisfy the compiler + + // see also the note in sync_cache() above + + if(sync_state == 1) + { + SpMat<eT>& x = const_cast< SpMat<eT>& >(*this); + + SpMat<eT> tmp(cache); + + x.steal_mem_simple(tmp); + + sync_state = 2; + } + } + + + + +// +// SpMat_aux + + + +template<typename eT, typename T1> +inline +void +SpMat_aux::set_real(SpMat<eT>& out, const SpBase<eT,T1>& X) + { + arma_extra_debug_sigprint(); + + const unwrap_spmat<T1> tmp(X.get_ref()); + const SpMat<eT>& A = tmp.M; + + arma_debug_assert_same_size( out, A, "SpMat::set_real()" ); + + out = A; + } + + + +template<typename eT, typename T1> +inline +void +SpMat_aux::set_imag(SpMat<eT>&, const SpBase<eT,T1>&) + { + arma_extra_debug_sigprint(); + } + + + +template<typename T, typename T1> +inline +void +SpMat_aux::set_real(SpMat< std::complex<T> >& out, const SpBase<T,T1>& X) + { + arma_extra_debug_sigprint(); + + typedef typename std::complex<T> eT; + + const unwrap_spmat<T1> U(X.get_ref()); + const SpMat<T>& Y = U.M; + + arma_debug_assert_same_size(out, Y, "SpMat::set_real()"); + + SpMat<eT> tmp(Y,arma::imag(out)); // arma:: prefix required due to bugs in GCC 4.4 - 4.6 + + out.steal_mem(tmp); + } + + + +template<typename T, typename T1> +inline +void +SpMat_aux::set_imag(SpMat< std::complex<T> >& out, const SpBase<T,T1>& X) + { + arma_extra_debug_sigprint(); + + typedef typename std::complex<T> eT; + + const unwrap_spmat<T1> U(X.get_ref()); + const SpMat<T>& Y = U.M; + + arma_debug_assert_same_size(out, Y, "SpMat::set_imag()"); + + SpMat<eT> tmp(arma::real(out),Y); // arma:: prefix required due to bugs in GCC 4.4 - 4.6 + + out.steal_mem(tmp); + } + + + +#if defined(ARMA_EXTRA_SPMAT_MEAT) + #include ARMA_INCFILE_WRAP(ARMA_EXTRA_SPMAT_MEAT) +#endif + + + +//! @} |