373 lines
17 KiB
C++
373 lines
17 KiB
C++
/* Copyright 2016-2018 The MathWorks, Inc. */
|
|
|
|
#ifndef SPARSE_ARRAY_HPP_
|
|
#define SPARSE_ARRAY_HPP_
|
|
|
|
#include "matlab_data_array_defs.hpp"
|
|
#include "matlab_extdata_defs.hpp"
|
|
#include "MDArray.hpp"
|
|
#include "Exception.hpp"
|
|
|
|
#include "detail/publish_util.hpp"
|
|
#include "detail/FunctionType.hpp"
|
|
|
|
#include "detail/HelperFunctions.hpp"
|
|
|
|
#include <stdint.h>
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#if defined (_MSC_VER) && (_MSC_VER < 1900)
|
|
#include "SparseArray_VS2013.hpp"
|
|
#else
|
|
namespace matlab {
|
|
namespace data {
|
|
|
|
namespace impl {
|
|
class ArrayImpl;
|
|
}
|
|
|
|
namespace detail {
|
|
class IteratorImpl;
|
|
class Access;
|
|
}
|
|
|
|
typedef std::pair<size_t, size_t> SparseIndex;
|
|
|
|
/**
|
|
* SparseArray class provides an API for accessing sparse TypedArray data.
|
|
* It contains functionality to access only the non-zero elements
|
|
* of the array.
|
|
*/
|
|
|
|
template<typename T>
|
|
class SparseArray : public Array {
|
|
public:
|
|
|
|
using iterator = TypedIterator<T>;
|
|
using const_iterator = TypedIterator<typename std::add_const<T>::type>;
|
|
|
|
static const ArrayType type = GetSparseArrayType<T>::type;
|
|
/**
|
|
* Copy constructor
|
|
* @param rhs SparseArray to be copied to the current one
|
|
* @throw none
|
|
*/
|
|
SparseArray(const SparseArray<T> &rhs) MW_NOEXCEPT
|
|
: Array(rhs)
|
|
{}
|
|
|
|
/**
|
|
* Operator=
|
|
* @param rhs SparseArray to be assigned to the current one
|
|
* @throw none
|
|
*/
|
|
SparseArray& operator=(const SparseArray<T> &rhs) MW_NOEXCEPT {
|
|
Array::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* Move constructor
|
|
* @param rhs SparseArray to be "moved" into the current one
|
|
* @throw none
|
|
*/
|
|
SparseArray(SparseArray&& rhs) MW_NOEXCEPT
|
|
: Array(std::move(rhs)) {}
|
|
|
|
/**
|
|
* Move operator==
|
|
* @param rhs SparseArray to be "move-assigned" into the current one
|
|
* @throw none
|
|
*/
|
|
SparseArray& operator=(SparseArray<T>&& rhs) MW_NOEXCEPT {
|
|
Array::operator=(std::move(rhs));
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* Copy construct a SparseArray from an Array
|
|
* @param rhs Array to copy to the new Sparse array
|
|
* @throw InvalidArrayTypeException if type of rhs type is not sparse
|
|
*
|
|
*/
|
|
SparseArray(const Array& rhs) : Array(rhs) {
|
|
typedef int(*TypedArrayIsValidConversionFcnPtr)(int, int, bool*);
|
|
static const TypedArrayIsValidConversionFcnPtr fcn = detail::resolveFunction<TypedArrayIsValidConversionFcnPtr>
|
|
(detail::FunctionType::TYPED_ARRAY_IS_VALID_CONVERSION);
|
|
bool result;
|
|
detail::throwIfError(fcn(static_cast<int>(type), static_cast<int>(rhs.getType()), &result));
|
|
if (!result) {
|
|
throw InvalidArrayTypeException("Can't convert the Array to this SparseArray");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy Operator=
|
|
* @param rhs Array to be assigned to the current one
|
|
* @throw InvalidArrayTypeException if type of rhs doesn't match
|
|
*/
|
|
SparseArray& operator=(const Array& rhs) {
|
|
typedef int(*TypedArrayIsValidConversionFcnPtr)(int, int, bool*);
|
|
static const TypedArrayIsValidConversionFcnPtr fcn = detail::resolveFunction<TypedArrayIsValidConversionFcnPtr>
|
|
(detail::FunctionType::TYPED_ARRAY_IS_VALID_CONVERSION);
|
|
bool result;
|
|
detail::throwIfError(fcn(static_cast<int>(type), static_cast<int>(rhs.getType()), &result));
|
|
if (!result) {
|
|
throw InvalidArrayTypeException("Can't convert the Array to this SparseArray");
|
|
}
|
|
Array::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* Move construct a SparseArray from an Array
|
|
* @param rhs Array to be moved to the new Sparse array
|
|
* @throw InvalidArrayTypeException if type of rhs is not sparse
|
|
*/
|
|
SparseArray(Array&& rhs)
|
|
: Array(std::move(rhs)) {
|
|
typedef int(*TypedArrayIsValidConversionFcnPtr)(int, int, bool*);
|
|
static const TypedArrayIsValidConversionFcnPtr fcn = detail::resolveFunction<TypedArrayIsValidConversionFcnPtr>
|
|
(detail::FunctionType::TYPED_ARRAY_IS_VALID_CONVERSION);
|
|
bool result;
|
|
detail::throwIfError(fcn(static_cast<int>(type), static_cast<int>(getType()), &result));
|
|
if (!result) {
|
|
rhs = std::move(*this);
|
|
throw InvalidArrayTypeException("Can't convert the Array to this SparseArray");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Move Operator=
|
|
* @param rhs Array to be move-assigned to the current one
|
|
* @throw InvalidArrayTypeException if type of rhs doesn't match or is not recognized
|
|
*/
|
|
SparseArray& operator=(Array&& rhs) {
|
|
typedef int(*TypedArrayIsValidConversionFcnPtr)(int, int, bool*);
|
|
static const TypedArrayIsValidConversionFcnPtr fcn = detail::resolveFunction<TypedArrayIsValidConversionFcnPtr>
|
|
(detail::FunctionType::TYPED_ARRAY_IS_VALID_CONVERSION);
|
|
bool result;
|
|
detail::throwIfError(fcn(static_cast<int>(type), static_cast<int>(rhs.getType()), &result));
|
|
if (!result) {
|
|
throw InvalidArrayTypeException("Can't convert the Array to this SparseArray");
|
|
}
|
|
Array::operator=(std::move(rhs));
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
* Return an iterator to the beginning of the array
|
|
*
|
|
* @return iterator
|
|
* @throw none
|
|
*/
|
|
iterator begin() MW_NOEXCEPT {
|
|
impl::ArrayImpl* newImpl = nullptr;
|
|
typedef bool(*ArrayUnshareFcnPtr)(impl::ArrayImpl*, bool, impl::ArrayImpl**);
|
|
static const ArrayUnshareFcnPtr u_fcn = detail::resolveFunction<ArrayUnshareFcnPtr>
|
|
(detail::FunctionType::ARRAY_UNSHARE);
|
|
if (u_fcn(pImpl.get(), (pImpl.use_count() == 1), &newImpl)) {
|
|
pImpl.reset(newImpl, [](impl::ArrayImpl* ptr) {
|
|
typedef void(*ArrayDestroyFcnPtr)(impl::ArrayImpl*);
|
|
static const ArrayDestroyFcnPtr d_fcn = detail::resolveFunction<ArrayDestroyFcnPtr>
|
|
(detail::FunctionType::ARRAY_DESTROY);
|
|
d_fcn(ptr);
|
|
});
|
|
}
|
|
typedef detail::IteratorImpl*(*TypedArrayBeginFcnPtr)(impl::ArrayImpl*, bool);
|
|
static const TypedArrayBeginFcnPtr fcn3 = detail::resolveFunctionNoExcept<TypedArrayBeginFcnPtr>
|
|
(detail::FunctionType::TYPED_ARRAY_BEGIN_V2);
|
|
if (fcn3 == nullptr) {
|
|
static const TypedArrayBeginFcnPtr fcn4 = detail::resolveFunction<TypedArrayBeginFcnPtr>
|
|
(detail::FunctionType::TYPED_ARRAY_BEGIN);
|
|
return detail::Access::createObj<iterator>(fcn4(pImpl.get(), !std::is_const<T>::value));
|
|
}
|
|
return detail::Access::createObj<iterator>(fcn3(pImpl.get(), !std::is_const<T>::value));
|
|
}
|
|
|
|
/**
|
|
* Return an const_iterator to the beginning of the array
|
|
*
|
|
* @return const_iterator
|
|
* @throw none
|
|
*/
|
|
const_iterator begin() const MW_NOEXCEPT {
|
|
return cbegin();
|
|
}
|
|
|
|
|
|
/**
|
|
* Return an const_iterator to the beginning of the array
|
|
*
|
|
* @return const_iterator
|
|
* @throw none
|
|
*/
|
|
const_iterator cbegin() const MW_NOEXCEPT {
|
|
typedef detail::IteratorImpl*(*TypedArrayBeginFcnPtr)(impl::ArrayImpl*, bool);
|
|
static const TypedArrayBeginFcnPtr fcn3 = detail::resolveFunctionNoExcept<TypedArrayBeginFcnPtr>
|
|
(detail::FunctionType::TYPED_ARRAY_BEGIN_V2);
|
|
if (fcn3 == nullptr) {
|
|
static const TypedArrayBeginFcnPtr fcn4 = detail::resolveFunction<TypedArrayBeginFcnPtr>
|
|
(detail::FunctionType::TYPED_ARRAY_BEGIN);
|
|
return detail::Access::createObj<const_iterator>(fcn4(pImpl.get(), false));
|
|
}
|
|
return detail::Access::createObj<const_iterator>(fcn3(pImpl.get(), false));
|
|
}
|
|
|
|
/**
|
|
* Return an iterator to the end of the array
|
|
*
|
|
* @return iterator
|
|
* @throw none
|
|
*/
|
|
iterator end() MW_NOEXCEPT {
|
|
impl::ArrayImpl* newImpl = nullptr;
|
|
typedef bool(*ArrayUnshareFcnPtr)(impl::ArrayImpl*, bool, impl::ArrayImpl**);
|
|
static const ArrayUnshareFcnPtr u_fcn = detail::resolveFunction<ArrayUnshareFcnPtr>
|
|
(detail::FunctionType::ARRAY_UNSHARE);
|
|
if (u_fcn(pImpl.get(), (pImpl.use_count() == 1), &newImpl)) {
|
|
pImpl.reset(newImpl, [](impl::ArrayImpl* ptr) {
|
|
typedef void(*ArrayDestroyFcnPtr)(impl::ArrayImpl*);
|
|
static const ArrayDestroyFcnPtr d_fcn = detail::resolveFunction<ArrayDestroyFcnPtr>
|
|
(detail::FunctionType::ARRAY_DESTROY);
|
|
d_fcn(ptr);
|
|
});
|
|
}
|
|
typedef detail::IteratorImpl*(*TypedArrayEndFcnPtr)(impl::ArrayImpl*, bool);
|
|
static const TypedArrayEndFcnPtr fcn = detail::resolveFunctionNoExcept<TypedArrayEndFcnPtr>
|
|
(detail::FunctionType::TYPED_ARRAY_END_V2);
|
|
if (fcn == nullptr) {
|
|
static const TypedArrayEndFcnPtr fcn2 = detail::resolveFunction<TypedArrayEndFcnPtr>
|
|
(detail::FunctionType::TYPED_ARRAY_END);
|
|
return detail::Access::createObj<iterator>(fcn2(pImpl.get(), !std::is_const<T>::value));
|
|
}
|
|
return detail::Access::createObj<iterator>(fcn(pImpl.get(), !std::is_const<T>::value));
|
|
}
|
|
|
|
/**
|
|
* Return an const_iterator to the end of the array
|
|
*
|
|
* @return const_iterator
|
|
* @throw none
|
|
*/
|
|
const_iterator end() const MW_NOEXCEPT {
|
|
return cend();
|
|
}
|
|
|
|
/**
|
|
* Return an const_iterator to the end of the array
|
|
*
|
|
* @return const_iterator
|
|
* @throw none
|
|
*/
|
|
const_iterator cend() const MW_NOEXCEPT {
|
|
typedef detail::IteratorImpl*(*TypedArrayEndFcnPtr)(impl::ArrayImpl*, bool);
|
|
static const TypedArrayEndFcnPtr fcn = detail::resolveFunctionNoExcept<TypedArrayEndFcnPtr>
|
|
(detail::FunctionType::TYPED_ARRAY_END_V2);
|
|
if (fcn == nullptr) {
|
|
static const TypedArrayEndFcnPtr fcn2 = detail::resolveFunction<TypedArrayEndFcnPtr>
|
|
(detail::FunctionType::TYPED_ARRAY_END);
|
|
return detail::Access::createObj<const_iterator>(fcn2(pImpl.get(), false));
|
|
}
|
|
return detail::Access::createObj<const_iterator>(fcn(pImpl.get(), false));
|
|
}
|
|
|
|
/**
|
|
* This API returns the number of non-zero elements in the array.
|
|
* Since sparse arrays only store non-zero elements, this API effectively
|
|
* returns the actual array size. It is different from array dimensions
|
|
* that specify the full array size.
|
|
* @return Number of non-zero elements in the array
|
|
* @throw none
|
|
*/
|
|
size_t getNumberOfNonZeroElements() const MW_NOEXCEPT {
|
|
size_t val;
|
|
typedef void(*SparseArrayGetNumNonZeroElementsFcnPtr)(impl::ArrayImpl*, size_t*);
|
|
static const SparseArrayGetNumNonZeroElementsFcnPtr fcn = detail::resolveFunction<SparseArrayGetNumNonZeroElementsFcnPtr>
|
|
(detail::FunctionType::SPARSE_ARRAY_GET_NUM_NONZERO_ELEMENTS);
|
|
fcn(detail::Access::getImpl<impl::ArrayImpl>(*this), &val);
|
|
return val;
|
|
}
|
|
|
|
/**
|
|
* This API returns the row-column coordinates of the non-zero entry that the iterator is pointing to.
|
|
*
|
|
* @param it - Iterator pointing to the current entry in the Sparse matrix
|
|
* @return - {m,n} of the entry
|
|
* @throw FeatureNotSupportedException if running against a MATLAB version prior to R2018b
|
|
*/
|
|
template <typename U = T>
|
|
typename std::enable_if<std::is_arithmetic<U>::value || is_complex<U>::value, SparseIndex>::type getIndex(const TypedIterator<T>& it) {
|
|
size_t row, col;
|
|
auto b = begin();
|
|
typedef void(*SparseArrayGetIndexArithmeticFcnPtr)(impl::ArrayImpl*, size_t, size_t*, size_t*);
|
|
static const SparseArrayGetIndexArithmeticFcnPtr fcn = detail::resolveFunction<SparseArrayGetIndexArithmeticFcnPtr>
|
|
(detail::FunctionType::SPARSE_ARRAY_GET_INDEX_ARITHMETIC);
|
|
fcn(detail::Access::getImpl<impl::ArrayImpl>(*this),
|
|
(it - b),
|
|
&row,
|
|
&col);
|
|
return SparseIndex(row, col);
|
|
}
|
|
template <typename U = T>
|
|
typename std::enable_if<!std::is_arithmetic<U>::value && !is_complex<U>::value, SparseIndex>::type getIndex(const TypedIterator<T>& it) MW_NOEXCEPT {
|
|
size_t row, col;
|
|
typedef void(*SparseArrayGetIndexFcnPtr)(impl::ArrayImpl*, detail::IteratorImpl*, size_t*, size_t*);
|
|
static const SparseArrayGetIndexFcnPtr fcn = detail::resolveFunction<SparseArrayGetIndexFcnPtr>
|
|
(detail::FunctionType::SPARSE_ARRAY_GET_INDEX);
|
|
fcn(detail::Access::getImpl<impl::ArrayImpl>(*this),
|
|
detail::Access::getImpl<detail::IteratorImpl>(it),
|
|
&row,
|
|
&col);
|
|
return SparseIndex(row, col);
|
|
}
|
|
|
|
/**
|
|
* This API returns the row-column coordinates of the non-zero entry that the iterator is pointing to.
|
|
*
|
|
* @param it - Iterator pointing to the current entry in the Sparse matrix
|
|
* @return - {m,n} of the entry
|
|
* @throw FeatureNotSupportedException if running against a MATLAB version prior to R2018b
|
|
*/
|
|
template <typename U = T>
|
|
typename std::enable_if<std::is_arithmetic<U>::value || is_complex<U>::value, SparseIndex>::type getIndex(const TypedIterator<T const>& it) const {
|
|
size_t row, col;
|
|
auto b = cbegin();
|
|
typedef void(*SparseArrayGetIndexArithmeticFcnPtr)(impl::ArrayImpl*, size_t, size_t*, size_t*);
|
|
static const SparseArrayGetIndexArithmeticFcnPtr fcn = detail::resolveFunction<SparseArrayGetIndexArithmeticFcnPtr>
|
|
(detail::FunctionType::SPARSE_ARRAY_GET_INDEX_ARITHMETIC);
|
|
fcn(detail::Access::getImpl<impl::ArrayImpl>(*this),
|
|
(it - b),
|
|
&row,
|
|
&col);
|
|
return SparseIndex(row, col);
|
|
}
|
|
template <typename U = T>
|
|
typename std::enable_if<!std::is_arithmetic<U>::value && !is_complex<U>::value, SparseIndex>::type getIndex(const TypedIterator<T const>& it) const MW_NOEXCEPT {
|
|
size_t row, col;
|
|
typedef void(*SparseArrayGetIndexFcnPtr)(impl::ArrayImpl*, detail::IteratorImpl*, size_t*, size_t*);
|
|
static const SparseArrayGetIndexFcnPtr fcn = detail::resolveFunction<SparseArrayGetIndexFcnPtr>
|
|
(detail::FunctionType::SPARSE_ARRAY_GET_INDEX);
|
|
fcn(detail::Access::getImpl<impl::ArrayImpl>(*this),
|
|
detail::Access::getImpl<detail::IteratorImpl>(it),
|
|
&row,
|
|
&col);
|
|
return SparseIndex(row, col);
|
|
}
|
|
|
|
private:
|
|
friend class detail::Access;
|
|
|
|
SparseArray(impl::ArrayImpl *impl) MW_NOEXCEPT :
|
|
Array(impl) {}
|
|
|
|
SparseArray() = delete;
|
|
};
|
|
|
|
}
|
|
}
|
|
|
|
#endif
|
|
#endif
|