2095 lines
79 KiB
C
2095 lines
79 KiB
C
|
/*
|
||
|
* Copyright (c) 2019 Szabolcs Horvát.
|
||
|
*
|
||
|
* See the file LICENSE.txt for copying permission.
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* \mainpage
|
||
|
*
|
||
|
* This is Doxygen-generated documentation for the C++ interface of the LTemplate _Mathematica_ package.
|
||
|
*
|
||
|
* For the latest version of the package go to https://github.com/szhorvat/LTemplate
|
||
|
*
|
||
|
* See `LTemplateTutorial.nb` for an introduction and additional documentation.
|
||
|
*
|
||
|
* Many commented examples can be found in the `LTemplate/Documentation/Examples` directory.
|
||
|
*/
|
||
|
|
||
|
#ifndef LTEMPLATE_H
|
||
|
#define LTEMPLATE_H
|
||
|
|
||
|
/** \file
|
||
|
* \brief Include this header before classes to be used with the LTemplate _Mathematica_ package.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "LTemplateCompilerSetup.h"
|
||
|
|
||
|
#ifndef LTEMPLATE_USE_CXX11
|
||
|
#error LTemplate requires a compiler with C++11 support.
|
||
|
#endif
|
||
|
|
||
|
#include "mathlink.h"
|
||
|
#include "WolframLibrary.h"
|
||
|
#include "WolframSparseLibrary.h"
|
||
|
#include "WolframImageLibrary.h"
|
||
|
|
||
|
#ifdef LTEMPLATE_RAWARRAY
|
||
|
#include "WolframRawArrayLibrary.h"
|
||
|
#endif
|
||
|
|
||
|
#ifdef LTEMPLATE_NUMERICARRAY
|
||
|
#include "WolframNumericArrayLibrary.h"
|
||
|
#endif
|
||
|
|
||
|
// mathlink.h defines P. It has a high potential for conflict, so we undefine it.
|
||
|
// It is normally only used with .tm files and it is not needed for LTemplate.
|
||
|
#undef P
|
||
|
|
||
|
// Sanity check for the size of mint.
|
||
|
#ifdef MINT_32
|
||
|
static_assert(sizeof(mint) == 4, "MINT_32 defined but sizeof(mint) != 4.");
|
||
|
#else
|
||
|
static_assert(sizeof(mint) == 8, "MINT_32 is not defined but sizeof(mint) != 8. Define MINT_32 when compiling on 32-bit platforms.");
|
||
|
#endif
|
||
|
|
||
|
|
||
|
#include <cstdint>
|
||
|
#include <complex>
|
||
|
#include <string>
|
||
|
#include <ostream>
|
||
|
#include <sstream>
|
||
|
#include <vector>
|
||
|
#include <map>
|
||
|
#include <type_traits>
|
||
|
#include <iterator>
|
||
|
#include <initializer_list>
|
||
|
#include <limits>
|
||
|
|
||
|
/// The namespace used by LTemplate
|
||
|
namespace mma {
|
||
|
|
||
|
/// Global `WolframLibraryData` object for accessing the LibraryLink API.
|
||
|
extern WolframLibraryData libData;
|
||
|
|
||
|
/// Complex double type for RawArrays.
|
||
|
typedef std::complex<double> complex_double_t;
|
||
|
|
||
|
/// Complex float type for RawArrays.
|
||
|
typedef std::complex<float> complex_float_t;
|
||
|
|
||
|
/** \brief Complex number type for Tensors. Alias for \ref complex_double_t.
|
||
|
* Same as \c std::complex<double>, thus it can be used with arithmetic operators.
|
||
|
*/
|
||
|
typedef complex_double_t complex_t;
|
||
|
|
||
|
/// For use in the \ref message() function.
|
||
|
enum MessageType { M_INFO, M_WARNING, M_ERROR, M_ASSERT };
|
||
|
|
||
|
|
||
|
/** \brief Issue a _Mathematica_ message.
|
||
|
* \param msg is the text of the message
|
||
|
* \param type determines the message tag which will be used
|
||
|
*
|
||
|
* If `msg == NULL`, no message will be issued. This is for compatibility with other libraries
|
||
|
* that may return a null pointer instead of message text.
|
||
|
*
|
||
|
* \sa print()
|
||
|
*/
|
||
|
void message(const char *msg, MessageType type = M_INFO);
|
||
|
|
||
|
inline void message(const std::string &msg, MessageType type = M_INFO) { message(msg.c_str(), type); }
|
||
|
|
||
|
|
||
|
/** \brief Call _Mathematica_'s `Print[]`.
|
||
|
*
|
||
|
* \sa mout, message()
|
||
|
*/
|
||
|
inline void print(const char *msg) {
|
||
|
if (libData->AbortQ())
|
||
|
return; // trying to use the MathLink connection during an abort appears to break it
|
||
|
|
||
|
MLINK link = libData->getMathLink(libData);
|
||
|
MLPutFunction(link, "EvaluatePacket", 1);
|
||
|
MLPutFunction(link, "Print", 1);
|
||
|
MLPutString(link, msg);
|
||
|
libData->processMathLink(link);
|
||
|
int pkt = MLNextPacket(link);
|
||
|
if (pkt == RETURNPKT)
|
||
|
MLNewPacket(link);
|
||
|
}
|
||
|
|
||
|
/// Call _Mathematica_'s `Print[]`, `std::string` argument version.
|
||
|
inline void print(const std::string &msg) { print(msg.c_str()); }
|
||
|
|
||
|
|
||
|
/** \brief Can be used to output with _Mathematica_'s `Print[]` in a manner similar to `std::cout`.
|
||
|
*
|
||
|
* The stream _must_ be flushed to trigger printing earlier than the return of the library function.
|
||
|
* This is most easily accomplished by inserting `std::endl` or `std::flush`.
|
||
|
*
|
||
|
* \sa print(), message()
|
||
|
*/
|
||
|
extern std::ostream mout;
|
||
|
|
||
|
|
||
|
/** \brief Throwing this type returns to _Mathematica_ immediately.
|
||
|
* \param msg is reported in _Mathematica_ as `LTemplate::error`.
|
||
|
* \param err is used as the LibraryFunction exit code.
|
||
|
*
|
||
|
* Typical values for the exit code are the following, but any integer may be used, *except* 0 (i.e. `LIBRARY_NO_ERROR`).
|
||
|
* Using 0 in LibraryError may cause a crash, as _Mathematica_ will expect to find a return value.
|
||
|
*
|
||
|
* - `LIBRARY_TYPE_ERROR`
|
||
|
* - `LIBRARY_RANK_ERROR`
|
||
|
* - `LIBRARY_DIMENSION_ERROR`
|
||
|
* - `LIBRARY_NUMERICAL_ERROR`
|
||
|
* - `LIBRARY_MEMORY_ERROR`
|
||
|
* - `LIBRARY_FUNCTION_ERROR`
|
||
|
*/
|
||
|
class LibraryError {
|
||
|
const std::string msg;
|
||
|
const bool has_msg;
|
||
|
const int err_code;
|
||
|
|
||
|
public:
|
||
|
explicit LibraryError(int err = LIBRARY_FUNCTION_ERROR) : has_msg(false), err_code(err) { }
|
||
|
explicit LibraryError(const std::string &msg, int err = LIBRARY_FUNCTION_ERROR) : msg(msg), has_msg(true), err_code(err) { }
|
||
|
|
||
|
const std::string &message() const { return msg; }
|
||
|
bool has_message() const { return has_msg; }
|
||
|
int error_code() const { return err_code; }
|
||
|
|
||
|
void report() const {
|
||
|
if (has_msg)
|
||
|
mma::message(msg, M_ERROR);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
#ifdef NDEBUG
|
||
|
#define massert(condition) ((void)0)
|
||
|
#else
|
||
|
/** \brief Replacement for the standard `assert` macro. Instead of aborting the process, it throws a \ref mma::LibraryError
|
||
|
*
|
||
|
* As with the standard `assert` macro, define `NDEBUG` to disable assertion checks.
|
||
|
* LTemplate uses massert() internally in a few places. It can be disabled this way
|
||
|
* for a minor performance boost.
|
||
|
*/
|
||
|
#define massert(condition) (void)(((condition) || mma::detail::massert_impl(#condition, __FILE__, __LINE__)), 0)
|
||
|
#endif
|
||
|
|
||
|
namespace detail { // private
|
||
|
[[ noreturn ]] inline bool massert_impl(const char *cond, const char *file, int line)
|
||
|
{
|
||
|
std::ostringstream msg;
|
||
|
msg << cond << ", file " << file << ", line " << line;
|
||
|
message(msg.str(), M_ASSERT);
|
||
|
throw LibraryError();
|
||
|
}
|
||
|
} // end namespace detail
|
||
|
|
||
|
|
||
|
/// Check for and honour user aborts.
|
||
|
inline void check_abort() {
|
||
|
if (libData->AbortQ())
|
||
|
throw LibraryError();
|
||
|
}
|
||
|
|
||
|
|
||
|
/// Convenience function for disowning `const char *` strings; same as `UTF8String_disown`.
|
||
|
inline void disownString(const char *str) {
|
||
|
libData->UTF8String_disown(const_cast<char *>(str));
|
||
|
}
|
||
|
|
||
|
|
||
|
namespace detail {
|
||
|
template<typename LT>
|
||
|
class LTAutoFree {
|
||
|
bool active;
|
||
|
LT ref;
|
||
|
public:
|
||
|
LTAutoFree(const LT &ref) : active(true), ref(ref) { }
|
||
|
LTAutoFree(LTAutoFree &&af) noexcept : LTAutoFree(af.ref) { af.active = false; }
|
||
|
~LTAutoFree() { if (active) ref.free(); }
|
||
|
|
||
|
LTAutoFree() = delete;
|
||
|
LTAutoFree(const LTAutoFree &) = delete;
|
||
|
LTAutoFree & operator = (const LTAutoFree &) = delete;
|
||
|
|
||
|
operator LT & () { return ref; }
|
||
|
|
||
|
LT * operator -> () { return &ref; }
|
||
|
};
|
||
|
|
||
|
template<typename T>
|
||
|
LTAutoFree<T> autoFree(const T &ref) { return LTAutoFree<T>(ref); }
|
||
|
}
|
||
|
|
||
|
|
||
|
/** \brief Get all instances of an LTemplate class
|
||
|
*
|
||
|
* Do not use `delete` on the Class pointers in this collection or a crash may result later in the session.
|
||
|
*
|
||
|
*/
|
||
|
template<typename Class>
|
||
|
extern const std::map<mint, Class *> &getCollection();
|
||
|
|
||
|
/** \brief Get class instance corresponding to the given managed library expression ID
|
||
|
* \tparam Class is the LTemplate class to get an instance of.
|
||
|
* \param id is the managed library expression ID.
|
||
|
*
|
||
|
* If no class instance corresponding to `id` exists, a \ref LibraryError will be thrown.
|
||
|
*
|
||
|
* \throws LibraryError
|
||
|
*/
|
||
|
template<typename Class>
|
||
|
inline Class &getInstance(mint id) {
|
||
|
const auto &collection = getCollection<Class>();
|
||
|
auto it = collection.find(id);
|
||
|
if (it == collection.end())
|
||
|
throw LibraryError("Managed library expression instance does not exist.");
|
||
|
return *(it->second);
|
||
|
}
|
||
|
|
||
|
|
||
|
/////////////////////////////////////// DENSE AND SPARSE ARRAY HANDLING ///////////////////////////////////////
|
||
|
|
||
|
namespace detail { // private
|
||
|
template<typename T> T * getData(MTensor t);
|
||
|
|
||
|
template<> inline mint * getData(MTensor t) { return libData->MTensor_getIntegerData(t); }
|
||
|
template<> inline double * getData(MTensor t) { return libData->MTensor_getRealData(t); }
|
||
|
template<> inline complex_t * getData(MTensor t) { return reinterpret_cast< complex_t * >( libData->MTensor_getComplexData(t) ); }
|
||
|
|
||
|
// copy data from column major format to row major format
|
||
|
template<typename T, typename U>
|
||
|
inline void transposedCopy(const T *from, U *to, mint nrow, mint ncol) {
|
||
|
for (mint i=0; i < ncol; ++i)
|
||
|
for (mint j=0; j < nrow; ++j)
|
||
|
to[i + j*ncol] = from[j + i*nrow];
|
||
|
}
|
||
|
} // end namespace detail
|
||
|
|
||
|
|
||
|
namespace detail { // private
|
||
|
template<typename T> inline mint libraryType() {
|
||
|
static_assert(std::is_same<T, T&>::value,
|
||
|
"Only mint, double and mma::complex_t are allowed in mma::TensorRef<...> and mma::SparseArrayRef<...>.");
|
||
|
}
|
||
|
|
||
|
template<> inline mint libraryType<mint>() { return MType_Integer; }
|
||
|
template<> inline mint libraryType<double>() { return MType_Real; }
|
||
|
template<> inline mint libraryType<complex_t>() { return MType_Complex; }
|
||
|
} // end namespace detail
|
||
|
|
||
|
|
||
|
template<typename T> class SparseArrayRef;
|
||
|
|
||
|
|
||
|
/** \brief Wrapper class for `MTensor` pointers
|
||
|
* \tparam T is the type of the Tensor; must be `mint`, `double` or `mma::complex_t`.
|
||
|
*
|
||
|
* Specified as `LType[List, T, rank]` or `{T, rank}` in an `LTemplate` in _Mathematica_,
|
||
|
* where `T` is one of `Integer`, `Real` or `Complex`.
|
||
|
*
|
||
|
* Note that just like `MTensor`, this class only holds a reference to a Tensor.
|
||
|
* Multiple \ref TensorRef objects may refer to the same Tensor.
|
||
|
*
|
||
|
* \sa MatrixRef, CubeRef
|
||
|
* \sa makeTensor(), makeVector(), makeMatrix(), makeCube()
|
||
|
*/
|
||
|
template<typename T>
|
||
|
class TensorRef {
|
||
|
const MTensor t; // reminder: MTensor is a pointer type
|
||
|
T * const tensor_data;
|
||
|
const mint len;
|
||
|
|
||
|
// A "null" TensorRef is used only for SparseArrayRef's ev member to handle pattern arrays
|
||
|
// It cannot be publicly constructed
|
||
|
TensorRef() : t(nullptr), tensor_data(nullptr), len(0) { }
|
||
|
bool nullQ() const { return t == nullptr; }
|
||
|
|
||
|
friend class SparseArrayRef<T>;
|
||
|
|
||
|
public:
|
||
|
TensorRef(const MTensor &mt) :
|
||
|
t(mt),
|
||
|
tensor_data(detail::getData<T>(t)),
|
||
|
len(libData->MTensor_getFlattenedLength(t))
|
||
|
{
|
||
|
detail::libraryType<T>(); // causes compile time error if T is invalid
|
||
|
}
|
||
|
|
||
|
/// The referenced \c MTensor
|
||
|
MTensor tensor() const { return t; }
|
||
|
|
||
|
/// Rank of the Tensor, same as \c MTensor_getRank
|
||
|
mint rank() const { return libData->MTensor_getRank(t); }
|
||
|
|
||
|
/// Number of elements in the Tensor, same as \c MTensor_getFlattenedLength
|
||
|
mint length() const { return len; }
|
||
|
|
||
|
/// Number of elements in the Tensor, synonym of \ref length()
|
||
|
mint size() const { return length(); }
|
||
|
|
||
|
/// Free the referenced Tensor; same as \c MTensor_free
|
||
|
/**
|
||
|
* Tensors created by the library with functions such as \ref makeVector() must be freed
|
||
|
* after use unless they are returned to _Mathematica_.
|
||
|
*
|
||
|
* Warning: multiple \ref TensorRef objects may reference the same \c MTensor.
|
||
|
* Freeing the \c MTensor invalidates all references to it.
|
||
|
*/
|
||
|
void free() const { libData->MTensor_free(t); }
|
||
|
|
||
|
/// Same as `MTensor_disown`
|
||
|
void disown() const { libData->MTensor_disown(t); }
|
||
|
|
||
|
/// Same as `MTensor_disownAll`
|
||
|
void disownAll() const { libData->MTensor_disownAll(t); }
|
||
|
|
||
|
/// Same as `MTensor_shareCount`
|
||
|
mint shareCount() const { return libData->MTensor_shareCount(t); }
|
||
|
|
||
|
/// Create a copy of the referenced Tensor
|
||
|
TensorRef clone() const {
|
||
|
MTensor c = nullptr;
|
||
|
int err = libData->MTensor_clone(t, &c);
|
||
|
if (err) throw LibraryError("MTensor_clone() failed.", err);
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
const mint *dimensions() const { return libData->MTensor_getDimensions(t); }
|
||
|
|
||
|
/// Pointer to the underlying storage of the referenced Tensor; alias of \ref begin()
|
||
|
T *data() const { return tensor_data; }
|
||
|
|
||
|
/// Index into the tensor data linearly; analogous to Flatten[tensor][[...]] in _Mathematica_
|
||
|
T & operator [] (mint i) const { return tensor_data[i]; }
|
||
|
|
||
|
/// Iterator to the beginning of the Tensor data
|
||
|
T *begin() const { return data(); }
|
||
|
|
||
|
/// Iterator past the end of the Tensor data
|
||
|
T *end() const { return begin() + length(); }
|
||
|
|
||
|
/// The type of the Tensor; may be `MType_Integer=2`, `MType_Real=3` or `MType_Complex=4`
|
||
|
mint type() const { return detail::libraryType<T>(); }
|
||
|
|
||
|
/** \brief Create a new Tensor of the given type from this Tensor's data
|
||
|
* \tparam U is the element type of the result.
|
||
|
*/
|
||
|
template<typename U>
|
||
|
TensorRef<U> convertTo() const {
|
||
|
MTensor mt = nullptr;
|
||
|
int err = libData->MTensor_new(detail::libraryType<U>(), rank(), dimensions(), &mt);
|
||
|
if (err) throw LibraryError("MTensor_new() failed.", err);
|
||
|
TensorRef<U> tr(mt);
|
||
|
std::copy(begin(), end(), tr.begin());
|
||
|
return tr;
|
||
|
}
|
||
|
|
||
|
/// Create a new SparseArray from the Tensor data
|
||
|
SparseArrayRef<T> toSparseArray() const {
|
||
|
MSparseArray sa = nullptr;
|
||
|
int err = libData->sparseLibraryFunctions->MSparseArray_fromMTensor(t, NULL, &sa);
|
||
|
if (err) throw LibraryError("MSparseArray_fromMTensor() failed.", err);
|
||
|
return sa;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/// @{
|
||
|
typedef TensorRef<mint> IntTensorRef;
|
||
|
typedef TensorRef<double> RealTensorRef;
|
||
|
typedef TensorRef<complex_t> ComplexTensorRef;
|
||
|
/// @}
|
||
|
|
||
|
|
||
|
/** \brief Wrapper class for `MTensor` pointers to rank-2 tensors
|
||
|
*
|
||
|
* Remember that \c MTensor stores data in row-major order.
|
||
|
*
|
||
|
* \sa TensorRef, CubeRef
|
||
|
* \sa makeMatrix()
|
||
|
*/
|
||
|
template<typename T>
|
||
|
class MatrixRef : public TensorRef<T> {
|
||
|
mint nrows, ncols;
|
||
|
|
||
|
public:
|
||
|
MatrixRef(const TensorRef<T> &tr) : TensorRef<T>(tr)
|
||
|
{
|
||
|
if (TensorRef<T>::rank() != 2)
|
||
|
throw LibraryError("MatrixRef: Matrix expected.");
|
||
|
const mint *dims = TensorRef<T>::dimensions();
|
||
|
nrows = dims[0];
|
||
|
ncols = dims[1];
|
||
|
}
|
||
|
|
||
|
/// Number of rows in the matrix
|
||
|
mint rows() const { return nrows; }
|
||
|
|
||
|
/// Number of columns in the matrix
|
||
|
mint cols() const { return ncols; }
|
||
|
|
||
|
/// Returns 2 for a matrix
|
||
|
mint rank() const { return 2; }
|
||
|
|
||
|
/// Index into a matrix using row and column indices
|
||
|
T & operator () (mint i, mint j) const { return (*this)[ncols*i + j]; }
|
||
|
};
|
||
|
|
||
|
/// @{
|
||
|
typedef MatrixRef<mint> IntMatrixRef;
|
||
|
typedef MatrixRef<double> RealMatrixRef;
|
||
|
typedef MatrixRef<complex_t> ComplexMatrixRef;
|
||
|
/// @}
|
||
|
|
||
|
|
||
|
/** \brief Wrapper class for `MTensor` pointers to rank-3 tensors
|
||
|
*
|
||
|
* \sa TensorRef, MatrixRef
|
||
|
* \sa makeCube()
|
||
|
*/
|
||
|
template<typename T>
|
||
|
class CubeRef : public TensorRef<T> {
|
||
|
mint nslices, nrows, ncols;
|
||
|
|
||
|
public:
|
||
|
CubeRef(const TensorRef<T> &tr) : TensorRef<T>(tr)
|
||
|
{
|
||
|
if (TensorRef<T>::rank() != 3)
|
||
|
throw LibraryError("CubeRef: Rank-3 tensor expected.");
|
||
|
const mint *dims = TensorRef<T>::dimensions();
|
||
|
nslices = dims[0];
|
||
|
nrows = dims[1];
|
||
|
ncols = dims[2];
|
||
|
}
|
||
|
|
||
|
/// Number of rows in the cube
|
||
|
mint rows() const { return nrows; }
|
||
|
|
||
|
/// Number of columns in the cube
|
||
|
mint cols() const { return ncols; }
|
||
|
|
||
|
/// Number of slices in the cube
|
||
|
mint slices() const { return nslices; }
|
||
|
|
||
|
/// Returns 3 for a cube
|
||
|
mint rank() const { return 3; }
|
||
|
|
||
|
/// Index into a cube using slice, row, and column indices
|
||
|
T & operator () (mint i, mint j, mint k) const { return (*this)[i*nrows*ncols + j*ncols + k]; }
|
||
|
};
|
||
|
|
||
|
/// @{
|
||
|
typedef CubeRef<mint> IntCubeRef;
|
||
|
typedef CubeRef<double> RealCubeRef;
|
||
|
typedef CubeRef<complex_t> ComplexCubeRef;
|
||
|
/// @}
|
||
|
|
||
|
|
||
|
/** \brief Create a Tensor of the given dimensions
|
||
|
* \tparam T is the type of the Tensor; can be `mint`, `double` or `mma::m_complex`.
|
||
|
* \param dims are the dimensions
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline TensorRef<T> makeTensor(std::initializer_list<mint> dims) {
|
||
|
MTensor t = nullptr;
|
||
|
int err = libData->MTensor_new(detail::libraryType<T>(), dims.size(), dims.begin(), &t);
|
||
|
if (err) throw LibraryError("MTensor_new() failed.", err);
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
/** \brief Create a Tensor of the given dimensions.
|
||
|
* \tparam T is the type of the Tensor; can be `mint`, `double` or `mma::m_complex`.
|
||
|
* \param rank is the Tensor depth
|
||
|
* \param dims are the dimensions stored in a C array of length \c rank and type \c mint
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline TensorRef<T> makeTensor(mint rank, mint *dims) {
|
||
|
MTensor t = nullptr;
|
||
|
int err = libData->MTensor_new(detail::libraryType<T>(), rank, dims, &t);
|
||
|
if (err) throw LibraryError("MTensor_new() failed.", err);
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
/** \brief Create a Tensor of the given dimensions.
|
||
|
* \tparam T is the type of the Tensor; can be `mint`, `double` or `mma::m_complex`.
|
||
|
* \param rank is the Tensor depth
|
||
|
* \param dims are the dimensions stored in a C array of length \c rank and type \c U
|
||
|
*/
|
||
|
template<typename T, typename U>
|
||
|
inline TensorRef<T> makeTensor(mint rank, const U *dims) {
|
||
|
std::vector<mint> d(dims, dims+rank);
|
||
|
return makeTensor<T>(rank, d.data());
|
||
|
}
|
||
|
|
||
|
|
||
|
/** \brief Create a vector (rank-1 Tensor) of the given length
|
||
|
* \tparam T is the type of the Tensor; can be `mint`, `double` or `mma::m_complex`
|
||
|
* \param length is the vector length
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline TensorRef<T> makeVector(mint length) {
|
||
|
return makeTensor<T>({length});
|
||
|
}
|
||
|
|
||
|
/** \brief Create a vector (rank-1 Tensor) of the given length and copies the contents of a C array into it
|
||
|
* \tparam T is the type of the Tensor; can be `mint`, `double` or `mma::m_complex`
|
||
|
* \param length is the length of the C array
|
||
|
* \param data points to the contents of the C array
|
||
|
*/
|
||
|
template<typename T, typename U>
|
||
|
inline TensorRef<T> makeVector(mint length, const U *data) {
|
||
|
TensorRef<T> t = makeVector<T>(length);
|
||
|
std::copy(data, data+length, t.begin());
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
/** \brief Create a vector (rank-1 Tensor) from an initializer list
|
||
|
* \tparam T is the type of the Tensor; can be `mint`, `double` or `mma::m_complex`
|
||
|
* \param values will be copied into the Tensor
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline TensorRef<T> makeVector(std::initializer_list<T> values) {
|
||
|
TensorRef<T> t = makeVector<T>(values.size());
|
||
|
std::copy(values.begin(), values.end(), t.begin());
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** \brief Create a matrix (rank-2 Tensor) of the given dimensions
|
||
|
* \param nrow is the number of rows
|
||
|
* \param ncol is the number of columns
|
||
|
* \tparam T is the type of the Tensor; can be `mint`, `double` or `mma::m_complex`
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline MatrixRef<T> makeMatrix(mint nrow, mint ncol) {
|
||
|
return makeTensor<T>({nrow, ncol});
|
||
|
}
|
||
|
|
||
|
/// Create a matrix (rank-2 Tensor) of the given dimensions and copy the contents of a row-major storage C array into it
|
||
|
template<typename T, typename U>
|
||
|
inline MatrixRef<T> makeMatrix(mint nrow, mint ncol, const U *data) {
|
||
|
MatrixRef<T> t = makeMatrix<T>(nrow, ncol);
|
||
|
std::copy(data, data + t.size(), t.begin());
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
/// Create a matrix (rank-2 Tensor) from a nested initializer list.
|
||
|
template<typename T>
|
||
|
inline MatrixRef<T> makeMatrix(std::initializer_list<std::initializer_list<T>> values) {
|
||
|
MatrixRef<T> t = makeMatrix<T>(values.size(), values.size() ? values.begin()->size() : 0);
|
||
|
T *ptr = t.data();
|
||
|
for (const auto &row : values) {
|
||
|
massert(row.size() == t.cols());
|
||
|
for (const auto &el : row) {
|
||
|
*ptr = el;
|
||
|
ptr++;
|
||
|
}
|
||
|
}
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
/// Create a matrix (rank-2 Tensor) of the given dimensions and copy the contents of a column-major storage C array into it
|
||
|
template<typename T, typename U>
|
||
|
inline MatrixRef<T> makeMatrixTransposed(mint nrow, mint ncol, const U *data) {
|
||
|
TensorRef<T> t = makeMatrix<T>(nrow, ncol);
|
||
|
detail::transposedCopy(data, t.data(), nrow, ncol);
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
|
||
|
/** \brief Create a rank-3 Tensor of the given dimensions
|
||
|
* \tparam T is the type of the Tensor; can be `mint`, `double` or `mma::m_complex`
|
||
|
* \param nslice is the number of slices
|
||
|
* \param nrow is the number of rows
|
||
|
* \param ncol is the number of columns
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline CubeRef<T> makeCube(mint nslice, mint nrow, mint ncol) {
|
||
|
return makeTensor<T>({nslice, nrow, ncol});
|
||
|
}
|
||
|
|
||
|
/// Create a rank-3 Tensor of the given dimensions and copy the contents of a C array into it
|
||
|
template<typename T, typename U>
|
||
|
inline CubeRef<T> makeCube(mint nslice, mint nrow, mint ncol, const U *data) {
|
||
|
CubeRef<T> t = makeCube<T>(nslice, nrow, ncol);
|
||
|
std::copy(data, data + t.size(), t.begin());
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
/// Create a rank-3 Tensor from a nested initializer list
|
||
|
template<typename T>
|
||
|
inline CubeRef<T> makeCube(std::initializer_list<std::initializer_list<std::initializer_list<T>>> values) {
|
||
|
size_t ns = values.size();
|
||
|
size_t rs = ns ? values.begin()->size() : 0;
|
||
|
size_t cs = rs ? values.begin()->begin()->size() : 0;
|
||
|
CubeRef<T> t = makeCube<T>(ns, rs, cs);
|
||
|
T *ptr = t.data();
|
||
|
for (const auto &slice : values) {
|
||
|
massert(slice.size() == rs);
|
||
|
for (const auto &row : slice) {
|
||
|
massert(row.size() == cs);
|
||
|
for (const auto &el : row){
|
||
|
*ptr = el;
|
||
|
ptr++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
|
||
|
template<typename T> class SparseMatrixRef;
|
||
|
|
||
|
/** \brief Wrapper class for `MSparseArray` pointers
|
||
|
*
|
||
|
* Specified as `LType[SparseArray, T, rank]` in an `LTemplate` in _Mathematica_,
|
||
|
* where `T` is one of `Integer`, `Real` or `Complex`.
|
||
|
*
|
||
|
* \sa SparseMatrixRef
|
||
|
* \sa makeSparseArray(), makeSparseMatrix()
|
||
|
*/
|
||
|
template<typename T>
|
||
|
class SparseArrayRef {
|
||
|
const MSparseArray sa; // reminder: MSparseArray is a pointer type
|
||
|
const IntTensorRef rp; // row pointers
|
||
|
const IntTensorRef ci; // column indices
|
||
|
const TensorRef<T> ev; // explicit values, ev.nullQ() may be true
|
||
|
T &iv; // implicit value
|
||
|
|
||
|
static TensorRef<T> getExplicitValues(const MSparseArray &msa) {
|
||
|
MTensor *ev = libData->sparseLibraryFunctions->MSparseArray_getExplicitValues(msa);
|
||
|
if (*ev == nullptr)
|
||
|
return TensorRef<T>();
|
||
|
else
|
||
|
return TensorRef<T>(*ev);
|
||
|
}
|
||
|
|
||
|
static IntTensorRef getColumnIndices(const MSparseArray &msa) {
|
||
|
MTensor *ci = libData->sparseLibraryFunctions->MSparseArray_getColumnIndices(msa);
|
||
|
|
||
|
// Ensure that sparse arrays always have a (possibly empty) column indices vector
|
||
|
if (*ci == nullptr) {
|
||
|
mint dims[2] = {0, libData->sparseLibraryFunctions->MSparseArray_getRank(msa)};
|
||
|
libData->MTensor_new(MType_Integer, 2, dims, ci);
|
||
|
}
|
||
|
|
||
|
return *ci;
|
||
|
}
|
||
|
|
||
|
static T &getImplicitValue(const MSparseArray &msa) {
|
||
|
MTensor *mt = libData->sparseLibraryFunctions->MSparseArray_getImplicitValue(msa);
|
||
|
return *(detail::getData<T>(*mt));
|
||
|
}
|
||
|
|
||
|
friend class SparseMatrixRef<T>;
|
||
|
|
||
|
public:
|
||
|
SparseArrayRef(const MSparseArray &msa) :
|
||
|
sa(msa),
|
||
|
rp(*(libData->sparseLibraryFunctions->MSparseArray_getRowPointers(msa))),
|
||
|
ci(getColumnIndices((msa))),
|
||
|
ev(getExplicitValues(msa)),
|
||
|
iv(getImplicitValue(msa))
|
||
|
{
|
||
|
detail::libraryType<T>(); // causes compile time error if T is invalid
|
||
|
}
|
||
|
|
||
|
/// The references `MSparseArray`
|
||
|
MSparseArray sparseArray() const { return sa; }
|
||
|
|
||
|
/// Rank of the SparseArray
|
||
|
mint rank() const { return libData->sparseLibraryFunctions->MSparseArray_getRank(sa); }
|
||
|
|
||
|
/// Dimensions of the SparseArray
|
||
|
const mint *dimensions() const { return libData->sparseLibraryFunctions->MSparseArray_getDimensions(sa); }
|
||
|
|
||
|
/// The number of explicitly stored positions
|
||
|
mint length() const { return ci.length(); /* use ci because ev may be null */}
|
||
|
|
||
|
/// The number of explicitly stored positions, alias for length()
|
||
|
mint size() const { return length(); }
|
||
|
|
||
|
void free() const { libData->sparseLibraryFunctions->MSparseArray_free(sa); }
|
||
|
void disown() const { libData->sparseLibraryFunctions->MSparseArray_disown(sa); }
|
||
|
void disownAll() const { libData->sparseLibraryFunctions->MSparseArray_disownAll(sa); }
|
||
|
|
||
|
mint shareCount() const { return libData->sparseLibraryFunctions->MSparseArray_shareCount(sa); }
|
||
|
|
||
|
/// Create a copy of the referenced SparseArray
|
||
|
SparseArrayRef clone() const {
|
||
|
MSparseArray c = nullptr;
|
||
|
int err = libData->sparseLibraryFunctions->MSparseArray_clone(sa, &c);
|
||
|
if (err) throw LibraryError("MSparseArray_clone() failed.", err);
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
/** \brief Create a new integer Tensor containing the indices of non-default (i.e. explicit) values in the sparse array.
|
||
|
*
|
||
|
* The positions use 1-based indexing.
|
||
|
*
|
||
|
* You are responsible for freeing this data structure using the TensorRef::free() function when done using it.
|
||
|
*/
|
||
|
IntTensorRef explicitPositions() const {
|
||
|
MTensor mt = nullptr;
|
||
|
int err = libData->sparseLibraryFunctions->MSparseArray_getExplicitPositions(sa, &mt);
|
||
|
if (err) throw LibraryError("MSParseArray_getExplicitPositions() failed.", err);
|
||
|
|
||
|
// Workaround for MSparseArray_getExplicitPositions() returning a non-empty rank-0 MTensor
|
||
|
// when the SparseArray has no explicit positions: in this case we manually construct
|
||
|
// a rank-2 0-by-n empty integer MTensor and return that instead.
|
||
|
if (libData->MTensor_getRank(mt) == 0) {
|
||
|
libData->MTensor_free(mt);
|
||
|
return makeMatrix<mint>(0, rank());
|
||
|
}
|
||
|
else {
|
||
|
return IntTensorRef(mt);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** \brief The column indices of the SparseArray's internal CSR representation, as an integer Tensor.
|
||
|
*
|
||
|
* This function is useful when converting a SparseArray for use with another library that also
|
||
|
* uses a CSR or CSC representation.
|
||
|
*
|
||
|
* The result is either a rank-2 Tensor or an empty one. The indices are 1-based.
|
||
|
*
|
||
|
* The result `MTensor` is part of the `MSparseArray` data structure and will be destroyed at the same time with it.
|
||
|
* Clone it before returning it to the kernel using \ref clone().
|
||
|
*/
|
||
|
IntTensorRef columnIndices() const {
|
||
|
return ci;
|
||
|
}
|
||
|
|
||
|
/** \brief The row pointers of the SparseArray's internal CSR representation, as a rank-1 integer Tensor.
|
||
|
*
|
||
|
* This function is useful when converting a SparseArray for use with another library that also
|
||
|
* uses a CSR or CSC representation.
|
||
|
*
|
||
|
* The result `MTensor` is part of the `MSparseArray` data structure and will be destroyed at the same time with it.
|
||
|
* Clone it before returning it to the kernel using \ref clone().
|
||
|
*/
|
||
|
IntTensorRef rowPointers() const { return rp; }
|
||
|
|
||
|
/// Does the SparseArray store explicit values? Pattern arrays do not have explicit values.
|
||
|
bool explicitValuesQ() const { return ! ev.nullQ(); }
|
||
|
|
||
|
/** \brief The explicit values in the SparseArray as a Tensor.
|
||
|
*
|
||
|
* The result `MTensor` is part of the `MSparseArray` data structure and will be destroyed at the same time with it.
|
||
|
* Clone it before returning it to the kernel using \ref clone().
|
||
|
*
|
||
|
* For pattern arrays, which do not have explicit values, a \ref LibraryError exception is thrown.
|
||
|
*
|
||
|
* \sa explicitValuesQ()
|
||
|
*/
|
||
|
TensorRef<T> explicitValues() const {
|
||
|
if (ev.nullQ())
|
||
|
throw LibraryError("SparseArrayRef::explicitValues() called on pattern array.");
|
||
|
return ev;
|
||
|
}
|
||
|
|
||
|
/// The implicit value (also call background or default value) of the SparseArray
|
||
|
T &implicitValue() const { return iv; }
|
||
|
|
||
|
/** \brief Creates a new SparseArray in which explicitly stored values that are equal to the current implicit value are eliminated.
|
||
|
*
|
||
|
* Useful when the explicit values or the implicit value has been changed, and a recomputation of the CSR structure is desired.
|
||
|
*
|
||
|
* Should not be used on a pattern array.
|
||
|
*/
|
||
|
SparseArrayRef resetImplicitValue() const {
|
||
|
MSparseArray msa = nullptr;
|
||
|
int err = libData->sparseLibraryFunctions->MSparseArray_resetImplicitValue(sa, NULL, &msa);
|
||
|
if (err) throw LibraryError("MSparseArray_resetImplicitValue() failed.", err);
|
||
|
return msa;
|
||
|
}
|
||
|
|
||
|
/** \brief Creates a new SparseArray based on a new implicit value
|
||
|
* \param iv is the new implicit value
|
||
|
*/
|
||
|
SparseArrayRef resetImplicitValue(const T &iv) const {
|
||
|
MSparseArray msa = nullptr;
|
||
|
|
||
|
MTensor it = nullptr;
|
||
|
int err = libData->MTensor_new(detail::libraryType<T>(), 0, NULL, &it);
|
||
|
if (err) throw LibraryError("MTensor_new() failed.", err);
|
||
|
*detail::getData<T>(it) = iv;
|
||
|
|
||
|
err = libData->sparseLibraryFunctions->MSparseArray_resetImplicitValue(sa, it, &msa);
|
||
|
libData->MTensor_free(it);
|
||
|
if (err) throw LibraryError("MSparseArray_resetImplicitValue() failed.", err);
|
||
|
|
||
|
return msa;
|
||
|
}
|
||
|
|
||
|
/// Creates a new Tensor (dense array) containing the same elements as the SparseArray
|
||
|
TensorRef<T> toTensor() const {
|
||
|
MTensor t = nullptr;
|
||
|
int err = libData->sparseLibraryFunctions->MSparseArray_toMTensor(sa, &t);
|
||
|
if (err) throw LibraryError("MSparseArray_toMTensor() failed.", err);
|
||
|
return t;
|
||
|
}
|
||
|
|
||
|
/// The element type of the SparseArray; may be `MType_Integer=2`, `MType_Real=3` or `MType_Complex=4`
|
||
|
mint type() const { return detail::libraryType<T>(); }
|
||
|
};
|
||
|
|
||
|
|
||
|
/** \brief Wrapper class for rank-2 SparseArrays
|
||
|
*
|
||
|
* \sa SparseArrayRef
|
||
|
* \sa makeSparseMatrix()
|
||
|
*/
|
||
|
template<typename T>
|
||
|
class SparseMatrixRef : public SparseArrayRef<T> {
|
||
|
mint ncols, nrows;
|
||
|
|
||
|
using SparseArrayRef<T>::rp;
|
||
|
using SparseArrayRef<T>::ci;
|
||
|
using SparseArrayRef<T>::ev;
|
||
|
using SparseArrayRef<T>::iv;
|
||
|
|
||
|
public:
|
||
|
using SparseArrayRef<T>::dimensions;
|
||
|
using SparseArrayRef<T>::size;
|
||
|
using SparseArrayRef<T>::explicitValuesQ;
|
||
|
|
||
|
/// Bidirectional iterator for enumerating the explicitly stored values and positions of a sparse matrix.
|
||
|
class iterator : public std::iterator<std::bidirectional_iterator_tag, T> {
|
||
|
const SparseMatrixRef *smp;
|
||
|
mint row_index, index;
|
||
|
|
||
|
friend class SparseMatrixRef;
|
||
|
|
||
|
iterator(const SparseMatrixRef *smp, const mint &row_index, const mint &index) :
|
||
|
smp(smp),
|
||
|
row_index(row_index),
|
||
|
index(index)
|
||
|
{ /* empty */ }
|
||
|
|
||
|
public:
|
||
|
|
||
|
iterator() = default;
|
||
|
iterator(const iterator &) = default;
|
||
|
iterator &operator = (const iterator &) = default;
|
||
|
|
||
|
/** \brief Access explicit value.
|
||
|
*
|
||
|
* Should not be used with pattern arrays. There is no safety check for this.
|
||
|
*/
|
||
|
T &operator *() const { return smp->ev[index]; }
|
||
|
|
||
|
bool operator == (const iterator &it) const { return index == it.index; }
|
||
|
bool operator != (const iterator &it) const { return index != it.index; }
|
||
|
|
||
|
iterator &operator ++ () {
|
||
|
index++;
|
||
|
while (smp->rp[row_index+1] == index && index < smp->size())
|
||
|
row_index++;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
iterator operator ++ (int) {
|
||
|
iterator it = *this;
|
||
|
operator++();
|
||
|
return it;
|
||
|
}
|
||
|
|
||
|
iterator &operator -- () {
|
||
|
while (smp->rp[row_index] == index && row_index > 0)
|
||
|
row_index--;
|
||
|
index--;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
iterator operator -- (int) {
|
||
|
iterator it = *this;
|
||
|
operator--();
|
||
|
return it;
|
||
|
}
|
||
|
|
||
|
mint row() const { return row_index; } ///< Row of the referenced element (0-based indexing)
|
||
|
mint col() const { return smp->ci[index]-1; } ///< Column of the referenced element (0-based indexing)
|
||
|
};
|
||
|
|
||
|
SparseMatrixRef(const SparseArrayRef<T> &sa) : SparseArrayRef<T>(sa)
|
||
|
{
|
||
|
if (SparseArrayRef<T>::rank() != 2)
|
||
|
throw LibraryError("SparseMatrixRef: Matrix expected.");
|
||
|
const mint *dims = dimensions();
|
||
|
nrows = dims[0];
|
||
|
ncols = dims[1];
|
||
|
}
|
||
|
|
||
|
/// Number of rows in the sparse matrix
|
||
|
mint rows() const { return nrows; }
|
||
|
|
||
|
/// Number of columns in the sparse matrix
|
||
|
mint cols() const { return ncols; }
|
||
|
|
||
|
mint rank() const { return 2; }
|
||
|
|
||
|
/** \brief Index into a sparse matrix (read-only, 0-based)
|
||
|
*
|
||
|
* This operator provides read access only.
|
||
|
* For write access to explicit values, use explicitValues().
|
||
|
*/
|
||
|
T operator () (mint i, mint j) const {
|
||
|
if (! explicitValuesQ())
|
||
|
throw LibraryError("SparseMatrixRef: cannot index into a pattern array.");
|
||
|
|
||
|
// if (i,j) is explicitly stored, it must be located between
|
||
|
// the following array indices in ev and ci:
|
||
|
mint lower = rp[i];
|
||
|
mint upper = rp[i+1];
|
||
|
|
||
|
// look for the index j between those locations:
|
||
|
mint *cp = std::lower_bound(&ci[lower], &ci[upper], j+1);
|
||
|
if (cp == &ci[upper]) // no bound found
|
||
|
return iv;
|
||
|
else if (*cp == j+1) // found a bound equal to the sought column index
|
||
|
return ev[lower + (cp - &ci[lower])];
|
||
|
else // column index not found
|
||
|
return iv;
|
||
|
}
|
||
|
|
||
|
/** \brief Iterator to beginning of explicit values and positions
|
||
|
*
|
||
|
* If you only need explicit values, not explicit positions, use `sparseArray.explicitValues().begin()` instead.
|
||
|
*
|
||
|
* \sa SparseMatrixRef::iterator
|
||
|
*/
|
||
|
iterator begin() const {
|
||
|
mint row_index = 0;
|
||
|
while (rp[row_index+1] == 0 && row_index < rp.size())
|
||
|
row_index++;
|
||
|
return iterator{this, row_index, 0};
|
||
|
}
|
||
|
|
||
|
/// Iterator to the end of explicit values and positions
|
||
|
iterator end() const {
|
||
|
return iterator{this, rows(), size()};
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
/** \brief Create a new SparseArray from a set of positions and values.
|
||
|
*
|
||
|
* \param pos is the list of explicitly stored positions using 1-based indexing.
|
||
|
* \param vals is the list of explicitly stored values.
|
||
|
* \param dims is a list of the sparse array dimensions.
|
||
|
* \param imp is the implicit value.
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline SparseArrayRef<T> makeSparseArray(IntMatrixRef pos, TensorRef<T> vals, IntTensorRef dims, T imp = 0) {
|
||
|
int err;
|
||
|
|
||
|
massert(pos.cols() == dims.size());
|
||
|
massert(pos.rows() == vals.size());
|
||
|
|
||
|
MTensor it = nullptr;
|
||
|
err = libData->MTensor_new(detail::libraryType<T>(), 0, nullptr, &it);
|
||
|
if (err)
|
||
|
throw LibraryError("makeSparseArray: MTensor_new() failed.", err);
|
||
|
*detail::getData<T>(it) = imp;
|
||
|
|
||
|
MSparseArray sa = nullptr;
|
||
|
err = libData->sparseLibraryFunctions->MSparseArray_fromExplicitPositions(pos.tensor(), vals.tensor(), dims.tensor(), it, &sa);
|
||
|
libData->MTensor_free(it);
|
||
|
if (err)
|
||
|
throw LibraryError("makeSparseArray: MSparseArray_fromExplicitPositions() failed.", err);
|
||
|
|
||
|
// MSparseArray_fromExplicitPositions() will return a pattern array when the positions array is empty.
|
||
|
// When this happens, we manually insert an explicit values array to ensure that this function
|
||
|
// never returns a pattern array.
|
||
|
MTensor *ev;
|
||
|
ev = libData->sparseLibraryFunctions->MSparseArray_getExplicitValues(sa);
|
||
|
if (*ev == nullptr) {
|
||
|
mint evdims[1] = {0};
|
||
|
libData->MTensor_new(detail::libraryType<T>(), 1, evdims, ev);
|
||
|
}
|
||
|
|
||
|
return sa;
|
||
|
}
|
||
|
|
||
|
/** \brief Create a new sparse matrix from a set of positions and values.
|
||
|
*
|
||
|
* \param pos is the list of explicitly stored positions using 1-based indexing.
|
||
|
* \param vals is the list of explicitly stored values.
|
||
|
* \param nrow is the number of matrix rows.
|
||
|
* \param ncol is the number of matrix columns.
|
||
|
* \param imp is the implicit value.
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline SparseMatrixRef<T> makeSparseMatrix(IntMatrixRef pos, TensorRef<T> vals, mint nrow, mint ncol, T imp = 0) {
|
||
|
massert(pos.cols() == 2);
|
||
|
|
||
|
auto dims = detail::autoFree(makeVector<mint>({nrow, ncol}));
|
||
|
SparseMatrixRef<T> sa = makeSparseArray(pos, vals, dims, imp);
|
||
|
|
||
|
return sa;
|
||
|
}
|
||
|
|
||
|
|
||
|
////////////////////////////////////////// RAW ARRAY HANDLING //////////////////////////////////////////
|
||
|
|
||
|
#ifdef LTEMPLATE_RAWARRAY
|
||
|
|
||
|
namespace detail { // private
|
||
|
template<typename T> inline rawarray_t libraryRawType() {
|
||
|
static_assert(std::is_same<T, T&>::value,
|
||
|
"Only int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, float, double, complex_float_t, complex_double_t are allowed in mma::RawArrayRef<...>.");
|
||
|
}
|
||
|
|
||
|
template<> inline rawarray_t libraryRawType<int8_t>() { return MRawArray_Type_Bit8; }
|
||
|
template<> inline rawarray_t libraryRawType<uint8_t>() { return MRawArray_Type_Ubit8; }
|
||
|
template<> inline rawarray_t libraryRawType<int16_t>() { return MRawArray_Type_Bit16; }
|
||
|
template<> inline rawarray_t libraryRawType<uint16_t>() { return MRawArray_Type_Ubit16; }
|
||
|
template<> inline rawarray_t libraryRawType<int32_t>() { return MRawArray_Type_Bit32; }
|
||
|
template<> inline rawarray_t libraryRawType<uint32_t>() { return MRawArray_Type_Ubit32; }
|
||
|
template<> inline rawarray_t libraryRawType<int64_t>() { return MRawArray_Type_Bit64; }
|
||
|
template<> inline rawarray_t libraryRawType<uint64_t>() { return MRawArray_Type_Ubit64; }
|
||
|
template<> inline rawarray_t libraryRawType<float>() { return MRawArray_Type_Real32; }
|
||
|
template<> inline rawarray_t libraryRawType<double>() { return MRawArray_Type_Real64; }
|
||
|
template<> inline rawarray_t libraryRawType<complex_float_t>() { return MRawArray_Type_Float_Complex; }
|
||
|
template<> inline rawarray_t libraryRawType<complex_double_t>() { return MRawArray_Type_Double_Complex; }
|
||
|
|
||
|
inline const char *rawTypeMathematicaName(rawarray_t rt) {
|
||
|
switch (rt) {
|
||
|
case MRawArray_Type_Ubit8: return "UnsignedInteger8";
|
||
|
case MRawArray_Type_Bit8: return "Integer8";
|
||
|
case MRawArray_Type_Ubit16: return "UnsignedInteger16";
|
||
|
case MRawArray_Type_Bit16: return "Integer16";
|
||
|
case MRawArray_Type_Ubit32: return "UnsignedInteger32";
|
||
|
case MRawArray_Type_Bit32: return "Integer32";
|
||
|
case MRawArray_Type_Ubit64: return "UnsignedInteger64";
|
||
|
case MRawArray_Type_Bit64: return "Integer64";
|
||
|
case MRawArray_Type_Real32: return "Real32";
|
||
|
case MRawArray_Type_Real64: return "Real64";
|
||
|
case MRawArray_Type_Float_Complex: return "Complex32";
|
||
|
case MRawArray_Type_Double_Complex: return "Complex64";
|
||
|
case MRawArray_Type_Undef: return "Undefined";
|
||
|
default: return "Unknown"; // should never reach here
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // end namespace detail
|
||
|
|
||
|
|
||
|
template<typename T> class RawArrayRef;
|
||
|
|
||
|
|
||
|
/// Wrapper class for `MRawArray` pointers; unspecialized base class. Typically used through \ref RawArrayRef.
|
||
|
class GenericRawArrayRef {
|
||
|
const MRawArray ra;
|
||
|
const mint len;
|
||
|
|
||
|
public:
|
||
|
GenericRawArrayRef(const MRawArray &mra) :
|
||
|
ra(mra),
|
||
|
len(libData->rawarrayLibraryFunctions->MRawArray_getFlattenedLength(mra))
|
||
|
{ }
|
||
|
|
||
|
/// Returns the referenced \c MRawArray
|
||
|
MRawArray rawArray() const { return ra; }
|
||
|
|
||
|
/// Returns the rank of the RawArray, same as \c MRawArray_getRank
|
||
|
mint rank() const { return libData->rawarrayLibraryFunctions->MRawArray_getRank(ra); }
|
||
|
|
||
|
/// Returns the number of elements in the RawArray, same as \c MRawArray_getFlattenedLength
|
||
|
mint length() const { return len; }
|
||
|
|
||
|
/// Returns the number of elements in the RawArray, synonym of \ref length()
|
||
|
mint size() const { return length(); }
|
||
|
|
||
|
/// Frees the referenced RawArray, same as \c MRawArray_free
|
||
|
/**
|
||
|
* Warning: multiple \ref RawArrayRef objects may reference the same \c MRawArray.
|
||
|
* Freeing the \c MRawArray invalidates all references to it.
|
||
|
*/
|
||
|
void free() const { libData->rawarrayLibraryFunctions->MRawArray_free(ra); }
|
||
|
|
||
|
void disown() const { libData->rawarrayLibraryFunctions->MRawArray_disown(ra); }
|
||
|
void disownAll() const { libData->rawarrayLibraryFunctions->MRawArray_disownAll(ra); }
|
||
|
|
||
|
mint shareCount() const { return libData->rawarrayLibraryFunctions->MRawArray_shareCount(ra); }
|
||
|
|
||
|
const mint *dimensions() const { return libData->rawarrayLibraryFunctions->MRawArray_getDimensions(ra); }
|
||
|
|
||
|
/// Creates a copy of the referenced RawArray
|
||
|
GenericRawArrayRef clone() const {
|
||
|
MRawArray c = nullptr;
|
||
|
int err = libData->rawarrayLibraryFunctions->MRawArray_clone(rawArray(), &c);
|
||
|
if (err) throw LibraryError("MRawArray_clone() failed.", err);
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
/** \brief Convert to the given type of RawArray; same as `MRawArray_convertType`
|
||
|
* \tparam U is the element type of the result
|
||
|
*/
|
||
|
template<typename U>
|
||
|
RawArrayRef<U> convertTo() const {
|
||
|
MRawArray res = libData->rawarrayLibraryFunctions->MRawArray_convertType(ra, detail::libraryRawType<U>());
|
||
|
if (! res)
|
||
|
throw LibraryError("MRawArray_convertType() failed.");
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
rawarray_t type() const { return libData->rawarrayLibraryFunctions->MRawArray_getType(ra); }
|
||
|
};
|
||
|
|
||
|
|
||
|
/// Wrapper class for `MRawArray` pointers. Available only in _Mathematica_ 10.4 and later. With _Mathematica_ 12.0 or later, use \ref NumericArrayRef instead.
|
||
|
template<typename T>
|
||
|
class RawArrayRef : public GenericRawArrayRef {
|
||
|
T * const array_data;
|
||
|
|
||
|
void checkType() {
|
||
|
rawarray_t received = GenericRawArrayRef::type();
|
||
|
rawarray_t expected = detail::libraryRawType<T>();
|
||
|
if (received != expected) {
|
||
|
std::ostringstream err;
|
||
|
err << "RawArray of type " << detail::rawTypeMathematicaName(received) << " received, "
|
||
|
<< detail::rawTypeMathematicaName(expected) << " expected.";
|
||
|
throw LibraryError(err.str(), LIBRARY_TYPE_ERROR);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
|
||
|
RawArrayRef(const MRawArray &mra) :
|
||
|
GenericRawArrayRef(mra),
|
||
|
array_data(reinterpret_cast<T *>(libData->rawarrayLibraryFunctions->MRawArray_getData(mra)))
|
||
|
{
|
||
|
checkType();
|
||
|
}
|
||
|
|
||
|
// explicit conversion required to prevent accidental auto-conversion between RawArrays of different types
|
||
|
explicit RawArrayRef(const GenericRawArrayRef &gra) :
|
||
|
GenericRawArrayRef(gra),
|
||
|
array_data(reinterpret_cast<T *>(libData->rawarrayLibraryFunctions->MRawArray_getData(gra.rawArray())))
|
||
|
{
|
||
|
checkType();
|
||
|
}
|
||
|
|
||
|
/// Creates a copy of the referenced RawArray
|
||
|
RawArrayRef clone() const {
|
||
|
MRawArray c = nullptr;
|
||
|
int err = libData->rawarrayLibraryFunctions->MRawArray_clone(rawArray(), &c);
|
||
|
if (err) throw LibraryError("MRawArray_clone() failed.", err);
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
/// Returns a pointer to the underlying storage of the corresponding \c MRawArray
|
||
|
T *data() const { return array_data; }
|
||
|
|
||
|
T & operator [] (mint i) const { return array_data[i]; }
|
||
|
|
||
|
T *begin() const { return data(); }
|
||
|
T *end() const { return begin() + length(); }
|
||
|
|
||
|
rawarray_t type() const { return detail::libraryRawType<T>(); }
|
||
|
};
|
||
|
|
||
|
/** \brief Creates a RawArray of the given dimensions
|
||
|
* \tparam T is the array element type
|
||
|
* \param dims are the array dimensions
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline RawArrayRef<T> makeRawArray(std::initializer_list<mint> dims) {
|
||
|
MRawArray ra = nullptr;
|
||
|
int err = libData->rawarrayLibraryFunctions->MRawArray_new(detail::libraryRawType<T>(), dims.size(), dims.begin(), &ra);
|
||
|
if (err) throw LibraryError("MRawArray_new() failed.", err);
|
||
|
return ra;
|
||
|
}
|
||
|
|
||
|
/** \brief Create a RawArray of the given dimensions.
|
||
|
* \tparam T is the array element type
|
||
|
* \param rank is the RawArray depth
|
||
|
* \param dims are the dimensions stored in a C array of length \c rank and type \c mint
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline RawArrayRef<T> makeRawArray(mint rank, const mint *dims) {
|
||
|
MRawArray ra = nullptr;
|
||
|
int err = libData->rawarrayLibraryFunctions->MRawArray_new(detail::libraryRawType<T>(), rank, dims, &ra);
|
||
|
if (err) throw LibraryError("MRawArray_new() failed.", err);
|
||
|
return ra;
|
||
|
}
|
||
|
|
||
|
/** \brief Create a RawArray of the given dimensions.
|
||
|
* \tparam T is the array element type
|
||
|
* \param rank is the RawArray depth
|
||
|
* \param dims are the dimensions stored in a C array of length \c rank and type \c U
|
||
|
*/
|
||
|
template<typename T, typename U>
|
||
|
inline RawArrayRef<T> makeRawArray(mint rank, const U *dims) {
|
||
|
std::vector<mint> d(dims, dims+rank);
|
||
|
return makeRawArray<T>(rank, d.data());
|
||
|
}
|
||
|
|
||
|
/** \brief Creates a rank-1 RawArray of the given length
|
||
|
* \tparam T is the array element type
|
||
|
* \param length is the vector length
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline RawArrayRef<T> makeRawVector(mint length) {
|
||
|
return makeRawArray<T>({length});
|
||
|
}
|
||
|
|
||
|
/** \brief Creates a rank-1 RawArray of the given type from a C array of the corresponding type
|
||
|
* \tparam T is the array element type
|
||
|
* \param length is the vector length
|
||
|
* \param data will be copied into the raw vector
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline RawArrayRef<T> makeRawVector(mint length, const T *data) {
|
||
|
auto ra = makeRawVector<T>(length);
|
||
|
std::copy(data, data+length, ra.begin());
|
||
|
return ra;
|
||
|
}
|
||
|
|
||
|
#endif // LTEMPLATE_RAWARRAY
|
||
|
|
||
|
|
||
|
////////////////////////////////////////// NUMERIC ARRAY HANDLING //////////////////////////////////////////
|
||
|
|
||
|
/*
|
||
|
* NumericArray was added in _Mathematica_ 12.0. It is identical to the earlier RawArray, and converts seamlessly from it,
|
||
|
* but it is now fully documented. The old RawArray LibraryLink interface is still present, so we keep RawArrayRef for
|
||
|
* backwards compatibility.
|
||
|
*
|
||
|
* Differences in the RawArray and NumericArray LibraryLink API:
|
||
|
* - Some enum values in MNumericArray_Data_Type have been renamed.
|
||
|
* - The convertType function has changed significantly:
|
||
|
* - Now returns an error code.
|
||
|
* - Can write into an existing NumericArray of the same dimensions. First argument should point to NULL
|
||
|
* (not be NULL) if a new NumericArray is to be created.
|
||
|
* - Conversion method can be specified. It corresponds to the third argument of the NumericArray _Mathematica_ function.
|
||
|
* - Tolerance can be given, to be used with floating point conversions.
|
||
|
*/
|
||
|
|
||
|
#ifdef LTEMPLATE_NUMERICARRAY
|
||
|
|
||
|
/** \brief Decimal digits per one bit (binary digit).
|
||
|
*
|
||
|
* Equal to \f$ \ln 2 / \ln 10 \f$.
|
||
|
* This constant is useful in setting the `tolerance` option of \ref GenericNumericArrayRef::convertTo.
|
||
|
*/
|
||
|
constexpr double decimalDigitsPerBit = 0.3010299956639812;
|
||
|
|
||
|
namespace detail { // private
|
||
|
template<typename T> inline numericarray_data_t libraryNumericType() {
|
||
|
static_assert(std::is_same<T, T&>::value,
|
||
|
"Only int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, float, double, complex_float_t, complex_double_t are allowed in mma::NumericArrayRef<...>.");
|
||
|
}
|
||
|
|
||
|
template<> inline numericarray_data_t libraryNumericType<int8_t>() { return MNumericArray_Type_Bit8; }
|
||
|
template<> inline numericarray_data_t libraryNumericType<uint8_t>() { return MNumericArray_Type_UBit8; }
|
||
|
template<> inline numericarray_data_t libraryNumericType<int16_t>() { return MNumericArray_Type_Bit16; }
|
||
|
template<> inline numericarray_data_t libraryNumericType<uint16_t>() { return MNumericArray_Type_UBit16; }
|
||
|
template<> inline numericarray_data_t libraryNumericType<int32_t>() { return MNumericArray_Type_Bit32; }
|
||
|
template<> inline numericarray_data_t libraryNumericType<uint32_t>() { return MNumericArray_Type_UBit32; }
|
||
|
template<> inline numericarray_data_t libraryNumericType<int64_t>() { return MNumericArray_Type_Bit64; }
|
||
|
template<> inline numericarray_data_t libraryNumericType<uint64_t>() { return MNumericArray_Type_UBit64; }
|
||
|
template<> inline numericarray_data_t libraryNumericType<float>() { return MNumericArray_Type_Real32; }
|
||
|
template<> inline numericarray_data_t libraryNumericType<double>() { return MNumericArray_Type_Real64; }
|
||
|
template<> inline numericarray_data_t libraryNumericType<complex_float_t>() { return MNumericArray_Type_Complex_Real32; }
|
||
|
template<> inline numericarray_data_t libraryNumericType<complex_double_t>() { return MNumericArray_Type_Complex_Real64; }
|
||
|
|
||
|
inline const char *numericTypeMathematicaName(numericarray_data_t rt) {
|
||
|
switch (rt) {
|
||
|
case MNumericArray_Type_UBit8: return "UnsignedInteger8";
|
||
|
case MNumericArray_Type_Bit8: return "Integer8";
|
||
|
case MNumericArray_Type_UBit16: return "UnsignedInteger16";
|
||
|
case MNumericArray_Type_Bit16: return "Integer16";
|
||
|
case MNumericArray_Type_UBit32: return "UnsignedInteger32";
|
||
|
case MNumericArray_Type_Bit32: return "Integer32";
|
||
|
case MNumericArray_Type_UBit64: return "UnsignedInteger64";
|
||
|
case MNumericArray_Type_Bit64: return "Integer64";
|
||
|
case MNumericArray_Type_Real32: return "Real32";
|
||
|
case MNumericArray_Type_Real64: return "Real64";
|
||
|
case MNumericArray_Type_Complex_Real32: return "Complex32";
|
||
|
case MNumericArray_Type_Complex_Real64: return "Complex64";
|
||
|
case MNumericArray_Type_Undef: return "Undefined";
|
||
|
default: return "Unknown"; // should never reach here
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // end namespace detail
|
||
|
|
||
|
|
||
|
template<typename T> class NumericArrayRef;
|
||
|
|
||
|
|
||
|
/// Wrapper class for `MNumericArray` pointers; unspecialized base class. Typically used through \ref NumericArrayRef.
|
||
|
class GenericNumericArrayRef {
|
||
|
const MNumericArray na;
|
||
|
const mint len;
|
||
|
|
||
|
public:
|
||
|
|
||
|
GenericNumericArrayRef(const MNumericArray &mra) :
|
||
|
na(mra),
|
||
|
len(libData->numericarrayLibraryFunctions->MNumericArray_getFlattenedLength(mra))
|
||
|
{ }
|
||
|
|
||
|
/// Returns the referenced \c MNumericArray
|
||
|
MNumericArray numericArray() const { return na; }
|
||
|
|
||
|
/// Returns the rank of the NumericArray, same as \c MNumericArray_getRank
|
||
|
mint rank() const { return libData->numericarrayLibraryFunctions->MNumericArray_getRank(na); }
|
||
|
|
||
|
/// Returns the number of elements in the NumericArray, same as \c MNumericArray_getFlattenedLength
|
||
|
mint length() const { return len; }
|
||
|
|
||
|
/// Returns the number of elements in the NumericArray, synonym of \ref length()
|
||
|
mint size() const { return length(); }
|
||
|
|
||
|
/// Frees the referenced NumericArray, same as \c MNumericArray_free
|
||
|
/**
|
||
|
* Warning: multiple \ref NumericArrayRef objects may reference the same \c MNumericArray.
|
||
|
* Freeing the \c MNumericArray invalidates all references to it.
|
||
|
*/
|
||
|
void free() const { libData->numericarrayLibraryFunctions->MNumericArray_free(na); }
|
||
|
|
||
|
void disown() const { libData->numericarrayLibraryFunctions->MNumericArray_disown(na); }
|
||
|
void disownAll() const { libData->numericarrayLibraryFunctions->MNumericArray_disownAll(na); }
|
||
|
|
||
|
mint shareCount() const { return libData->numericarrayLibraryFunctions->MNumericArray_shareCount(na); }
|
||
|
|
||
|
const mint *dimensions() const { return libData->numericarrayLibraryFunctions->MNumericArray_getDimensions(na); }
|
||
|
|
||
|
/// Creates a copy of the referenced NumericArray
|
||
|
GenericNumericArrayRef clone() const {
|
||
|
MNumericArray c = nullptr;
|
||
|
int err = libData->numericarrayLibraryFunctions->MNumericArray_clone(numericArray(), &c);
|
||
|
if (err) throw LibraryError("MNumericArray_clone() failed.", err);
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
/// Used in \ref convertTo to specify the element type conversion method. The names are consistent with the coercion methods of `NumericArray` in Mathematica.
|
||
|
enum ConversionMethod {
|
||
|
Check = 1, ///< Throw a \ref LibraryError if any values do not fit in target type without modification.
|
||
|
ClipAndCheck, ///< Clip to the target range and check that the values fit in the target type.
|
||
|
Coerce, ///< Coerce values into the target type.
|
||
|
ClipAndCoerce, ///< Clip to the target range and coerce into the target type.
|
||
|
Round, ///< Round reals to integers.
|
||
|
ClipAndRound, ///< Clip to the range and round reals to integers.
|
||
|
Scale, ///< Scale to the range (undocumented as of _Mathematica_ 12.0).
|
||
|
ClipAndScale ///< Clip and scale to the range (undocumented as of _Mathematica_ 12.0).
|
||
|
};
|
||
|
|
||
|
/** \brief Convert to the given type of NumericArray; same as `MNumericArray_convertType`
|
||
|
* \tparam U is the element type of the result
|
||
|
*
|
||
|
* \param method is the conversion method (see \ref ConversionMethod)
|
||
|
* \param tolerance is the tolerance in decimal digits for checking whether a value can be accurately represented using the target type
|
||
|
*
|
||
|
* The default tolerance corresponds to one binary digit for consistency with \c NumericArray in Mathematica.
|
||
|
* Use \ref decimalDigitsPerBit for conveniently specifying the tolerance in bits instead of decimal digits.
|
||
|
* Use e.g. \c 3*decimalDigitsPerBit to set 3 bits of tolerance.
|
||
|
*
|
||
|
* If any of the element values cannot be converted to the target type with the specified conversion method, a \ref LibraryError
|
||
|
* will be thrown.
|
||
|
*/
|
||
|
template<typename U>
|
||
|
NumericArrayRef<U> convertTo(ConversionMethod method = ClipAndRound, double tolerance = decimalDigitsPerBit) const {
|
||
|
MNumericArray res = nullptr;
|
||
|
auto err = libData->numericarrayLibraryFunctions->MNumericArray_convertType(&res, na, detail::libraryNumericType<U>(), static_cast<numericarray_convert_method_t>(method), tolerance);
|
||
|
if (err)
|
||
|
throw LibraryError("MNumericArray_convertType() failed. Check that all values can be converted to the target type using the specified method and tolerance.");
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
template<typename U>
|
||
|
NumericArrayRef<U> convertTo(numericarray_convert_method_t method, double tolerance = decimalDigitsPerBit) const {
|
||
|
return convertTo<U>(ConversionMethod(method), tolerance);
|
||
|
}
|
||
|
|
||
|
numericarray_data_t type() const { return libData->numericarrayLibraryFunctions->MNumericArray_getType(na); }
|
||
|
};
|
||
|
|
||
|
|
||
|
/// Wrapper class for `MNumericArray` pointers. Available only in _Mathematica_ 12.0 and later.
|
||
|
template<typename T>
|
||
|
class NumericArrayRef : public GenericNumericArrayRef {
|
||
|
T * const array_data;
|
||
|
|
||
|
void checkType() {
|
||
|
numericarray_data_t received = GenericNumericArrayRef::type();
|
||
|
numericarray_data_t expected = detail::libraryNumericType<T>();
|
||
|
if (received != expected) {
|
||
|
std::ostringstream err;
|
||
|
err << "NumericArray of type " << detail::numericTypeMathematicaName(received) << " received, "
|
||
|
<< detail::numericTypeMathematicaName(expected) << " expected.";
|
||
|
throw LibraryError(err.str(), LIBRARY_TYPE_ERROR);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public:
|
||
|
|
||
|
NumericArrayRef(const MNumericArray &mra) :
|
||
|
GenericNumericArrayRef(mra),
|
||
|
array_data(reinterpret_cast<T *>(libData->numericarrayLibraryFunctions->MNumericArray_getData(mra)))
|
||
|
{
|
||
|
checkType();
|
||
|
}
|
||
|
|
||
|
// explicit conversion required to prevent accidental auto-conversion between NumericArrays of different types
|
||
|
explicit NumericArrayRef(const GenericNumericArrayRef &gra) :
|
||
|
GenericNumericArrayRef(gra),
|
||
|
array_data(reinterpret_cast<T *>(libData->numericarrayLibraryFunctions->MNumericArray_getData(gra.numericArray())))
|
||
|
{
|
||
|
checkType();
|
||
|
}
|
||
|
|
||
|
/// Creates a copy of the referenced NumericArray
|
||
|
NumericArrayRef clone() const {
|
||
|
MNumericArray c = nullptr;
|
||
|
int err = libData->numericarrayLibraryFunctions->MNumericArray_clone(numericArray(), &c);
|
||
|
if (err) throw LibraryError("MNumericArray_clone() failed.", err);
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
/// Returns a pointer to the underlying storage of the corresponding \c MNumericArray
|
||
|
T *data() const { return array_data; }
|
||
|
|
||
|
T & operator [] (mint i) const { return array_data[i]; }
|
||
|
|
||
|
T *begin() const { return data(); }
|
||
|
T *end() const { return begin() + length(); }
|
||
|
|
||
|
numericarray_data_t type() const { return detail::libraryNumericType<T>(); }
|
||
|
};
|
||
|
|
||
|
/** \brief Creates a NumericArray of the given dimensions
|
||
|
* \tparam T is the array element type
|
||
|
* \param dims are the array dimensions
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline NumericArrayRef<T> makeNumericArray(std::initializer_list<mint> dims) {
|
||
|
MNumericArray na = nullptr;
|
||
|
int err = libData->numericarrayLibraryFunctions->MNumericArray_new(detail::libraryNumericType<T>(), dims.size(), dims.begin(), &na);
|
||
|
if (err) throw LibraryError("MNumericArray_new() failed.", err);
|
||
|
return na;
|
||
|
}
|
||
|
|
||
|
/** \brief Create a NumericArray of the given dimensions.
|
||
|
* \tparam T is the array element type
|
||
|
* \param rank is the NumericArray depth
|
||
|
* \param dims are the dimensions stored in a C array of length \c rank and type \c mint
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline NumericArrayRef<T> makeNumericArray(mint rank, const mint *dims) {
|
||
|
MNumericArray na = nullptr;
|
||
|
int err = libData->numericarrayLibraryFunctions->MNumericArray_new(detail::libraryNumericType<T>(), rank, dims, &na);
|
||
|
if (err) throw LibraryError("MNumericArray_new() failed.", err);
|
||
|
return na;
|
||
|
}
|
||
|
|
||
|
/** \brief Create a NumericArray of the given dimensions.
|
||
|
* \tparam T is the array element type
|
||
|
* \param rank is the NumericArray depth
|
||
|
* \param dims are the dimensions stored in a C array of length \c rank and type \c U
|
||
|
*/
|
||
|
template<typename T, typename U>
|
||
|
inline NumericArrayRef<T> makeNumericArray(mint rank, const U *dims) {
|
||
|
std::vector<mint> d(dims, dims+rank);
|
||
|
return makeNumericArray<T>(rank, d.data());
|
||
|
}
|
||
|
|
||
|
/** \brief Creates a rank-1 NumericArray of the given length
|
||
|
* \tparam T is the array element type
|
||
|
* \param length is the vector length
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline NumericArrayRef<T> makeNumericVector(mint length) {
|
||
|
return makeNumericArray<T>({length});
|
||
|
}
|
||
|
|
||
|
/** \brief Creates a rank-1 NumericArray of the given type from a C array of the corresponding type
|
||
|
* \tparam T is the array element type
|
||
|
* \param length is the vector length
|
||
|
* \param data will be copied into the numeric vector
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline NumericArrayRef<T> makeNumericVector(mint length, const T *data) {
|
||
|
auto na = makeNumericVector<T>(length);
|
||
|
std::copy(data, data+length, na.begin());
|
||
|
return na;
|
||
|
}
|
||
|
|
||
|
#endif // LTEMPLATE_NUMERICARRAY
|
||
|
|
||
|
|
||
|
|
||
|
////////////////////////////////////////// IMAGE HANDLING //////////////////////////////////////////
|
||
|
|
||
|
|
||
|
/* While the C++ standard does not guarantee that sizeof(bool) == 1, this is currently
|
||
|
* the case for most implementations. This was verified at https://gcc.godbolt.org/
|
||
|
* across multiple platforms and architectures in October 2017.
|
||
|
*
|
||
|
* The ABIs used by major operating systems also specify a bool or C99 _Bool of size 1
|
||
|
* https://github.com/rust-lang/rfcs/pull/954#issuecomment-169820630
|
||
|
*
|
||
|
* Thus it seems safe to require sizeof(bool) == 1. A safety check is below.
|
||
|
*/
|
||
|
static_assert(sizeof(bool) == 1, "The bool type is expected to be of size 1.");
|
||
|
|
||
|
/* We use a new set of types for image elements. These all correspond to raw_t_... types
|
||
|
* from WolframImageLibrary.h with the exception of im_bit_t, which is bool.
|
||
|
* This is so that it will be distinct from im_byte_t.
|
||
|
*/
|
||
|
/// @{
|
||
|
typedef bool im_bit_t; ///< `"Bit"`, `MImage_Type_Bit`
|
||
|
typedef unsigned char im_byte_t; ///< `"Byte"`, `MImage_Type_Bit8`
|
||
|
typedef unsigned short im_bit16_t; ///< `"Bit16"`, `MImage_Type_Bit16`
|
||
|
typedef float im_real32_t; ///< `"Real32"`, `MImage_Type_Real32`
|
||
|
typedef double im_real_t; ///< `"Real"`, `MImage_Type_Real`
|
||
|
typedef im_byte_t im_bit8_t; ///< Alias for \ref im_byte_t
|
||
|
/// @}
|
||
|
|
||
|
/** \brief Returns the value representing "white" for the give pixel type
|
||
|
* \tparam T is the pixel type
|
||
|
*
|
||
|
* For integer types, this is the highest value that can be stored.
|
||
|
*
|
||
|
* For floating point types, it is 1.0. Higher values can be stored, but will be
|
||
|
* clipped during display or conversion.
|
||
|
*
|
||
|
* The value representing "black" is always 0.
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline T imageMax() { return std::numeric_limits<T>::max(); }
|
||
|
|
||
|
template<>
|
||
|
inline im_bit_t imageMax() { return 1; }
|
||
|
|
||
|
template<>
|
||
|
inline im_real32_t imageMax() { return 1.0f; }
|
||
|
|
||
|
template<>
|
||
|
inline im_real_t imageMax() { return 1.0; }
|
||
|
|
||
|
namespace detail { // private
|
||
|
template<typename T> inline imagedata_t libraryImageType() {
|
||
|
static_assert(std::is_same<T, T&>::value,
|
||
|
"Only im_bit_t, im_byte_t, im_bit16_t, im_real32_t, im_real_t are allowed in mma::ImageRef<...>.");
|
||
|
}
|
||
|
|
||
|
template<> inline imagedata_t libraryImageType<im_bit_t>() { return MImage_Type_Bit; }
|
||
|
template<> inline imagedata_t libraryImageType<im_byte_t>() { return MImage_Type_Bit8; }
|
||
|
template<> inline imagedata_t libraryImageType<im_bit16_t>() { return MImage_Type_Bit16; }
|
||
|
template<> inline imagedata_t libraryImageType<im_real32_t>() { return MImage_Type_Real32; }
|
||
|
template<> inline imagedata_t libraryImageType<im_real_t>() { return MImage_Type_Real; }
|
||
|
|
||
|
|
||
|
inline const char *imageTypeMathematicaName(imagedata_t it) {
|
||
|
switch (it) {
|
||
|
case MImage_Type_Bit: return "Bit";
|
||
|
case MImage_Type_Bit8: return "Byte";
|
||
|
case MImage_Type_Bit16: return "Bit16";
|
||
|
case MImage_Type_Real32: return "Real32";
|
||
|
case MImage_Type_Real: return "Real";
|
||
|
case MImage_Type_Undef: return "Undefined";
|
||
|
default: return "Unknown"; // should never reach here
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // end namespace detail
|
||
|
|
||
|
|
||
|
template<typename T> class ImageRef;
|
||
|
template<typename T> class Image3DRef;
|
||
|
|
||
|
/** \brief Wrapper class for `MImage` pointers referring to 2D images; unspecialized base class. Typically used through \ref ImageRef.
|
||
|
*
|
||
|
* \sa GenericImage3DRef
|
||
|
*/
|
||
|
class GenericImageRef {
|
||
|
const MImage im;
|
||
|
const mint len;
|
||
|
const mint nrows, ncols, nchannels;
|
||
|
const bool interleaved, alphaChannel;
|
||
|
|
||
|
public:
|
||
|
GenericImageRef(const MImage &mim) :
|
||
|
im(mim),
|
||
|
len(libData->imageLibraryFunctions->MImage_getFlattenedLength(im)),
|
||
|
nrows(libData->imageLibraryFunctions->MImage_getRowCount(im)),
|
||
|
ncols(libData->imageLibraryFunctions->MImage_getColumnCount(im)),
|
||
|
nchannels(libData->imageLibraryFunctions->MImage_getChannels(im)),
|
||
|
interleaved(libData->imageLibraryFunctions->MImage_interleavedQ(im)),
|
||
|
alphaChannel(libData->imageLibraryFunctions->MImage_alphaChannelQ(im))
|
||
|
{
|
||
|
massert(libData->imageLibraryFunctions->MImage_getRank(im) == 2);
|
||
|
}
|
||
|
|
||
|
/// The referenced \c MImage
|
||
|
MImage image() const { return im; }
|
||
|
|
||
|
/// Total number of pixels in all image channels. Same as \c MImage_getFlattenedLength. \sa channelSize()
|
||
|
mint length() const { return len; }
|
||
|
|
||
|
/// Total number of pixels in all image channels; synonym of \ref length(). \sa channelSize()
|
||
|
mint size() const { return length(); }
|
||
|
|
||
|
/// The number of image rows
|
||
|
mint rows() const { return nrows; }
|
||
|
|
||
|
/// The number of image columns
|
||
|
mint cols() const { return ncols; }
|
||
|
|
||
|
/// The number of pixels in a single image channel
|
||
|
mint channelSize() const { return rows()*cols(); }
|
||
|
|
||
|
/// Returns 2 for 2D images
|
||
|
mint rank() const { return 2; }
|
||
|
|
||
|
/// The number of image channels
|
||
|
mint channels() const { return nchannels; }
|
||
|
|
||
|
/// The number of non-alpha channels. Same as \ref channels() if the image has no alpha channel; one less otherwise.
|
||
|
mint nonAlphaChannels() const { return alphaChannelQ() ? channels()-1 : channels(); }
|
||
|
|
||
|
/// Are channels stored in interleaved mode? Interleaved means e.g. `rgbrgbrgb...` instead of `rrr...ggg...bbb...` for an RGB image.
|
||
|
bool interleavedQ() const { return interleaved; }
|
||
|
|
||
|
/// Does the image have an alpha channel?
|
||
|
bool alphaChannelQ() const { return alphaChannel; }
|
||
|
|
||
|
/// The image colour space
|
||
|
colorspace_t colorSpace() const {
|
||
|
return libData->imageLibraryFunctions->MImage_getColorSpace(im);
|
||
|
}
|
||
|
|
||
|
/// Create a copy of the referenced Image
|
||
|
GenericImageRef clone() const {
|
||
|
MImage c = nullptr;
|
||
|
int err = libData->imageLibraryFunctions->MImage_clone(image(), &c);
|
||
|
if (err) throw LibraryError("MImage_clone() failed.", err);
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
/// Free the referenced \c MImage; same as \c MImage_free
|
||
|
void free() const { libData->imageLibraryFunctions->MImage_free(im); }
|
||
|
|
||
|
void disown() const { libData->imageLibraryFunctions->MImage_disown(im); }
|
||
|
void disownAll() const { libData->imageLibraryFunctions->MImage_disownAll(im); }
|
||
|
|
||
|
mint shareCount() const { return libData->imageLibraryFunctions->MImage_shareCount(im); }
|
||
|
|
||
|
/** \brief Convert to the given type of Image; same as `MImage_convertType`.
|
||
|
* \param interleaving specifies whether to store the data in interleaved mode. See \ref interleavedQ()
|
||
|
* \tparam U is the pixel type of the result.
|
||
|
*
|
||
|
* Returns a new image that must be freed explicitly unless returned to the kernel. See \ref free().
|
||
|
*/
|
||
|
template<typename U>
|
||
|
ImageRef<U> convertTo(bool interleaving) const {
|
||
|
MImage res = libData->imageLibraryFunctions->MImage_convertType(im, detail::libraryImageType<U>(), interleaving);
|
||
|
if (! res)
|
||
|
throw LibraryError("MImage_convertType() failed.");
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
/// Convert to the given type of Image. The interleaving mode is preserved.
|
||
|
template<typename U>
|
||
|
ImageRef<U> convertTo() const { return convertTo<U>(interleavedQ()); }
|
||
|
|
||
|
/// Returns the image/pixel type
|
||
|
imagedata_t type() const {
|
||
|
return libData->imageLibraryFunctions->MImage_getDataType(im);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
/** \brief Wrapper class for `MImage` pointers referring to 3D images; unspecialized base class. Typically used through \ref Image3DRef.
|
||
|
*
|
||
|
* \sa GenericImageRef
|
||
|
*/
|
||
|
class GenericImage3DRef {
|
||
|
const MImage im;
|
||
|
const mint len;
|
||
|
const mint nrows, ncols, nslices, nchannels;
|
||
|
const bool interleaved, alphaChannel;
|
||
|
|
||
|
public:
|
||
|
GenericImage3DRef(const MImage &mim) :
|
||
|
im(mim),
|
||
|
len(libData->imageLibraryFunctions->MImage_getFlattenedLength(im)),
|
||
|
nrows(libData->imageLibraryFunctions->MImage_getRowCount(im)),
|
||
|
ncols(libData->imageLibraryFunctions->MImage_getColumnCount(im)),
|
||
|
nslices(libData->imageLibraryFunctions->MImage_getSliceCount(im)),
|
||
|
nchannels(libData->imageLibraryFunctions->MImage_getChannels(im)),
|
||
|
interleaved(libData->imageLibraryFunctions->MImage_interleavedQ(im)),
|
||
|
alphaChannel(libData->imageLibraryFunctions->MImage_alphaChannelQ(im))
|
||
|
{
|
||
|
massert(libData->imageLibraryFunctions->MImage_getRank(im) == 3);
|
||
|
}
|
||
|
|
||
|
/// The referenced \c MImage
|
||
|
MImage image() const { return im; }
|
||
|
|
||
|
/// The total number of pixels in all image channels. Same as \c MImage_getFlattenedLength. \sa channelSize()
|
||
|
mint length() const { return len; }
|
||
|
|
||
|
/// The total number of pixels in all image channels, synonym of \ref length(). \sa channelSize()
|
||
|
mint size() const { return length(); }
|
||
|
|
||
|
/// The number of image rows
|
||
|
mint rows() const { return nrows; }
|
||
|
|
||
|
/// The number of image columns
|
||
|
mint cols() const { return ncols; }
|
||
|
|
||
|
/// The number of image slices in the Image3D
|
||
|
mint slices() const { return nslices; }
|
||
|
|
||
|
/// The number of pixels in a single image channel.
|
||
|
mint channelSize() const { return slices()*rows()*cols(); }
|
||
|
|
||
|
/// Returns 3 for 3D images.
|
||
|
mint rank() const { return 3; }
|
||
|
|
||
|
/// The number of image channels
|
||
|
mint channels() const { return nchannels; }
|
||
|
|
||
|
/// The number of non-alpha channels. Same as \ref channels() if the image has no alpha channel; one less otherwise.
|
||
|
mint nonAlphaChannels() const { return alphaChannelQ() ? channels()-1 : channels(); }
|
||
|
|
||
|
/// Are channels stored in interleaved mode? Interleaved means e.g. `rgbrgbrgb...` instead of `rrr...ggg...bbb...` for an RGB image.
|
||
|
bool interleavedQ() const { return interleaved; }
|
||
|
|
||
|
/// Does the image have an alpha channel?
|
||
|
bool alphaChannelQ() const { return alphaChannel; }
|
||
|
|
||
|
/// The image colour space
|
||
|
colorspace_t colorSpace() const {
|
||
|
return libData->imageLibraryFunctions->MImage_getColorSpace(im);
|
||
|
}
|
||
|
|
||
|
/// Create a copy of the referenced Image3D
|
||
|
GenericImage3DRef clone() const {
|
||
|
MImage c = nullptr;
|
||
|
int err = libData->imageLibraryFunctions->MImage_clone(image(), &c);
|
||
|
if (err) throw LibraryError("MImage_clone() failed.", err);
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
/// Free the referenced \c MImage; same as \c MImage_free
|
||
|
void free() const { libData->imageLibraryFunctions->MImage_free(im); }
|
||
|
|
||
|
void disown() const { libData->imageLibraryFunctions->MImage_disown(im); }
|
||
|
void disownAll() const { libData->imageLibraryFunctions->MImage_disownAll(im); }
|
||
|
|
||
|
mint shareCount() const { return libData->imageLibraryFunctions->MImage_shareCount(im); }
|
||
|
|
||
|
/** \brief Convert to the given type of Image3D; same as `MImage_convertType`.
|
||
|
* \param interleaving specifies whether to store the data in interleaved mode. See \ref interleavedQ()
|
||
|
* \tparam U is the pixel type of the result.
|
||
|
*
|
||
|
* Returns a new image that must be freed explicitly unless returned to the kernel. See \ref free().
|
||
|
*/
|
||
|
template<typename U>
|
||
|
Image3DRef<U> convertTo(bool interleaving) const {
|
||
|
MImage res = libData->imageLibraryFunctions->MImage_convertType(im, detail::libraryImageType<U>(), interleaving);
|
||
|
if (! res)
|
||
|
throw LibraryError("MImage_convertType() failed.");
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
/// Convert to the given type of Image3D. The interleaving mode is preserved.
|
||
|
template<typename U>
|
||
|
Image3DRef<U> convertTo() const { return convertTo<U>(interleavedQ()); }
|
||
|
|
||
|
/// Returns the image/pixel type
|
||
|
imagedata_t type() const {
|
||
|
return libData->imageLibraryFunctions->MImage_getDataType(im);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
template<typename T>
|
||
|
class pixel_iterator : public std::iterator<std::random_access_iterator_tag, T> {
|
||
|
T *ptr;
|
||
|
ptrdiff_t step;
|
||
|
|
||
|
friend ImageRef<T>;
|
||
|
friend Image3DRef<T>;
|
||
|
|
||
|
pixel_iterator(T *ptr, ptrdiff_t step) :
|
||
|
ptr(ptr), step(step)
|
||
|
{ }
|
||
|
|
||
|
public:
|
||
|
|
||
|
pixel_iterator() = default;
|
||
|
pixel_iterator(const pixel_iterator &) = default;
|
||
|
pixel_iterator &operator = (const pixel_iterator &) = default;
|
||
|
|
||
|
|
||
|
bool operator == (const pixel_iterator &it) const { return ptr == it.ptr; }
|
||
|
bool operator != (const pixel_iterator &it) const { return ptr != it.ptr; }
|
||
|
|
||
|
T &operator *() const { return *ptr; }
|
||
|
|
||
|
pixel_iterator &operator ++ () {
|
||
|
ptr += step;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
pixel_iterator operator ++ (int) {
|
||
|
pixel_iterator it = *this;
|
||
|
operator++();
|
||
|
return it;
|
||
|
}
|
||
|
|
||
|
pixel_iterator &operator -- () {
|
||
|
ptr -= step;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
pixel_iterator operator -- (int) {
|
||
|
pixel_iterator it = *this;
|
||
|
operator--();
|
||
|
return it;
|
||
|
}
|
||
|
|
||
|
pixel_iterator operator + (ptrdiff_t n) const { return pixel_iterator(ptr + n*step, step); }
|
||
|
|
||
|
pixel_iterator operator - (ptrdiff_t n) const { return pixel_iterator(ptr - n*step, step); }
|
||
|
|
||
|
ptrdiff_t operator - (const pixel_iterator &it) const { return (ptr - it.ptr)/step; }
|
||
|
|
||
|
T & operator [] (mint i) { return ptr[i*step]; }
|
||
|
|
||
|
bool operator < (const pixel_iterator &it) const { return ptr < it.ptr; }
|
||
|
bool operator > (const pixel_iterator &it) const { return ptr > it.ptr; }
|
||
|
bool operator <= (const pixel_iterator &it) const { return ptr <= it.ptr; }
|
||
|
bool operator >= (const pixel_iterator &it) const { return ptr >= it.ptr; }
|
||
|
};
|
||
|
|
||
|
|
||
|
/** \brief Wrapper class for `MImage` pointers referring to 2D images.
|
||
|
* \tparam T is the pixel type of the image. It corresponds to _Mathematica_'s `ImageType` as per the table below:
|
||
|
*
|
||
|
* `ImageType` | C++ type | Alias for
|
||
|
* ------------|---------------|-------------------
|
||
|
* `"Bit"` | `im_bit_t` | `bool`
|
||
|
* `"Byte"` | `im_byte_t` | `unsigned char`
|
||
|
* `"Bit16"` | `im_bit16_t` | `unsigned short`
|
||
|
* `"Real32"` | `im_real32_t` | `float`
|
||
|
* `"Real"` | `im_real_t` | `double`
|
||
|
*
|
||
|
* Note that this class only holds a reference to an Image. Multiple \ref ImageRef
|
||
|
* or \ref GenericImageRef objects may point to the same Image.
|
||
|
*
|
||
|
* \sa Image3DRef
|
||
|
*/
|
||
|
template<typename T>
|
||
|
class ImageRef : public GenericImageRef {
|
||
|
T * const image_data;
|
||
|
|
||
|
public:
|
||
|
|
||
|
/// Random access iterator for accessing the pixels of a single image channel in order
|
||
|
typedef class pixel_iterator<T> pixel_iterator;
|
||
|
|
||
|
ImageRef(const MImage &mim) :
|
||
|
GenericImageRef(mim),
|
||
|
image_data(reinterpret_cast<T *>(libData->imageLibraryFunctions->MImage_getRawData(mim)))
|
||
|
{ }
|
||
|
|
||
|
// explicit conversion required to prevent accidental auto-conversion between Images of different types or Image and Image3D
|
||
|
/// Cast a GenericImageRef to a type-specialized 2D image. The pixel type must agree with that of the generic image, otherwise an error is thrown.
|
||
|
explicit ImageRef(const GenericImageRef &gim) :
|
||
|
GenericImageRef(gim),
|
||
|
image_data(reinterpret_cast<T *>(libData->imageLibraryFunctions->MImage_getRawData(gim.image())))
|
||
|
{
|
||
|
imagedata_t received = gim.type();
|
||
|
imagedata_t expected = detail::libraryImageType<T>();
|
||
|
if (received != expected) {
|
||
|
std::ostringstream err;
|
||
|
err << "Image of type " << detail::imageTypeMathematicaName(received) << " received, "
|
||
|
<< detail::imageTypeMathematicaName(expected) << " expected.";
|
||
|
throw LibraryError(err.str(), LIBRARY_TYPE_ERROR);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Create a copy of the referenced Image
|
||
|
ImageRef clone() const {
|
||
|
MImage c = nullptr;
|
||
|
int err = libData->imageLibraryFunctions->MImage_clone(image(), &c);
|
||
|
if (err) throw LibraryError("MImage_clone() failed.", err);
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
/// Pointer to the image data; synonym of \ref begin()
|
||
|
T *data() const { return image_data; }
|
||
|
|
||
|
/// Iterator to the beginning of the image data
|
||
|
T *begin() const { return data(); }
|
||
|
|
||
|
/// Iterator past the end of the image data
|
||
|
T *end() const { return begin() + length(); }
|
||
|
|
||
|
/// Pixel iterator to the beginning of \p channel
|
||
|
pixel_iterator pixelBegin(mint channel) const {
|
||
|
if (interleavedQ())
|
||
|
return pixel_iterator(image_data + channel, channels());
|
||
|
else
|
||
|
return pixel_iterator(image_data + channelSize()*channel, 1);
|
||
|
}
|
||
|
|
||
|
/// Pixel iterator past the end of \p channel
|
||
|
pixel_iterator pixelEnd(mint channel) const {
|
||
|
return pixelBegin(channel) + channelSize();
|
||
|
}
|
||
|
|
||
|
/// Index into the Image by pixel coordinates and channel
|
||
|
T &operator ()(mint row, mint col, mint channel = 0) const {
|
||
|
if (interleavedQ())
|
||
|
return image_data[row*cols()*channels() + col*channels()+ channel];
|
||
|
else
|
||
|
return image_data[channel*rows()*cols() + row*cols() + col];
|
||
|
}
|
||
|
|
||
|
/// The element / pixel type of the Image
|
||
|
imagedata_t type() const { return detail::libraryImageType<T>(); }
|
||
|
};
|
||
|
|
||
|
|
||
|
/** \brief Wrapper class for `MImage` pointers referring to 3D images.
|
||
|
* \tparam T is the element type of the image. It corresponds to _Mathematica_'s `ImageType` as per the table below:
|
||
|
*
|
||
|
* `ImageType` | C++ type | Alias for
|
||
|
* ------------|---------------|-------------------
|
||
|
* `"Bit"` | `im_bit_t` | `bool`
|
||
|
* `"Byte"` | `im_byte_t` | `unsigned char`
|
||
|
* `"Bit16"` | `im_bit16_t` | `unsigned short`
|
||
|
* `"Real32"` | `im_real32_t` | `float`
|
||
|
* `"Real"` | `im_real_t` | `double`
|
||
|
*
|
||
|
* Note that this class only holds a reference to an Image3D. Multiple \ref Image3DRef
|
||
|
* or \ref GenericImage3DRef objects may point to the same Image3D.
|
||
|
*
|
||
|
* \sa ImageRef
|
||
|
*/
|
||
|
template<typename T>
|
||
|
class Image3DRef : public GenericImage3DRef {
|
||
|
T * const image_data;
|
||
|
|
||
|
public:
|
||
|
|
||
|
/// Random access iterator for accessing the pixels of a single image channel in order
|
||
|
typedef class pixel_iterator<T> pixel_iterator;
|
||
|
|
||
|
Image3DRef(const MImage &mim) :
|
||
|
GenericImage3DRef(mim),
|
||
|
image_data(reinterpret_cast<T *>(libData->imageLibraryFunctions->MImage_getRawData(mim)))
|
||
|
{ }
|
||
|
|
||
|
// explicit conversion required to prevent accidental auto-conversion between Image3Ds of different types or Image and Image3D
|
||
|
/// Cast a GenericImage3DRef to a type-specialized 3D image. The pixel type must agree with that of the generic image, otherwise an error is thrown.
|
||
|
explicit Image3DRef(const GenericImage3DRef &gim) :
|
||
|
GenericImage3DRef(gim),
|
||
|
image_data(reinterpret_cast<T *>(libData->imageLibraryFunctions->MImage_getRawData(gim.image())))
|
||
|
{
|
||
|
imagedata_t received = gim.type();
|
||
|
imagedata_t expected = detail::libraryImageType<T>();
|
||
|
if (received != expected) {
|
||
|
std::ostringstream err;
|
||
|
err << "Image3D of type " << detail::imageTypeMathematicaName(received) << " received, "
|
||
|
<< detail::imageTypeMathematicaName(expected) << " expected.";
|
||
|
throw LibraryError(err.str(), LIBRARY_TYPE_ERROR);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Create a copy of the referenced Image3D
|
||
|
Image3DRef clone() const {
|
||
|
MImage c = nullptr;
|
||
|
int err = libData->imageLibraryFunctions->MImage_clone(image(), &c);
|
||
|
if (err) throw LibraryError("MImage_clone() failed.", err);
|
||
|
return c;
|
||
|
}
|
||
|
|
||
|
/// Pointer to the image data
|
||
|
T *data() const { return image_data; }
|
||
|
|
||
|
/// Iterator to the beginning of the image data
|
||
|
T *begin() const { return data(); }
|
||
|
|
||
|
/// Iterator past the end of the image data
|
||
|
T *end() const { return begin() + length(); }
|
||
|
|
||
|
/// Pixel iterator to the beginning of \p channel
|
||
|
pixel_iterator pixelBegin(mint channel) const {
|
||
|
if (interleavedQ())
|
||
|
return pixel_iterator(image_data + channel, channels());
|
||
|
else
|
||
|
return pixel_iterator(image_data + channelSize()*channel, 1);
|
||
|
}
|
||
|
|
||
|
/// Pixel iterator past the end of \p channel
|
||
|
pixel_iterator pixelEnd(mint channel) const {
|
||
|
return pixelBegin(channel) + channelSize();
|
||
|
}
|
||
|
|
||
|
/// Index into the Image3D by pixel coordinates and channel
|
||
|
T &operator ()(mint slice, mint row, mint col, mint channel = 0) const {
|
||
|
if (interleavedQ())
|
||
|
return image_data[slice*rows()*cols()*channels() + row*cols()*channels() + col*channels() + channel];
|
||
|
else
|
||
|
return image_data[channel*slices()*rows()*cols() + slice*rows()*cols() + row*cols() + col];
|
||
|
}
|
||
|
|
||
|
/// The element / pixel type of the Image3D
|
||
|
imagedata_t type() const { return detail::libraryImageType<T>(); }
|
||
|
};
|
||
|
|
||
|
|
||
|
/** \brief Create a new Image
|
||
|
* \tparam T is the pixel type
|
||
|
* \param width of the image (number of columns)
|
||
|
* \param height of the image (number of rows)
|
||
|
* \param channels is the number of image channels
|
||
|
* \param interleaving specifies whether to store the data in interleaved mode
|
||
|
* \param colorspace may be one of `MImage_CS_Automatic`, `MImage_CS_Gray`, `MImage_CS_RGB`, `MImage_CS_HSB`, `MImage_CS_CMYK`, `MImage_CS_XYZ`, `MImage_CS_LUV`, `MImage_CS_LAB`, `MImage_CS_LCH`
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline ImageRef<T> makeImage(mint width, mint height, mint channels = 1, bool interleaving = true, colorspace_t colorspace = MImage_CS_Automatic) {
|
||
|
MImage mim = nullptr;
|
||
|
libData->imageLibraryFunctions->MImage_new2D(width, height, channels, detail::libraryImageType<T>(), colorspace, interleaving, &mim);
|
||
|
return mim;
|
||
|
}
|
||
|
|
||
|
/** \brief Create a new Image3D
|
||
|
* \tparam T is the pixel type
|
||
|
* \param slices is the number of image slices
|
||
|
* \param width of individual slices (number of rows)
|
||
|
* \param height of individual slices (number of columns)
|
||
|
* \param channels is the number of image channels
|
||
|
* \param interleaving specifies whether to store the data in interleaved mode
|
||
|
* \param colorspace may be one of `MImage_CS_Automatic`, `MImage_CS_Gray`, `MImage_CS_RGB`, `MImage_CS_HSB`, `MImage_CS_CMYK`, `MImage_CS_XYZ`, `MImage_CS_LUV`, `MImage_CS_LAB`, `MImage_CS_LCH`
|
||
|
*
|
||
|
*/
|
||
|
template<typename T>
|
||
|
inline Image3DRef<T> makeImage3D(mint slices, mint width, mint height, mint channels = 1, bool interleaving = true, colorspace_t colorspace = MImage_CS_Automatic) {
|
||
|
MImage mim = nullptr;
|
||
|
libData->imageLibraryFunctions->MImage_new3D(slices, width, height, channels, detail::libraryImageType<T>(), colorspace, interleaving, &mim);
|
||
|
return mim;
|
||
|
}
|
||
|
|
||
|
} // end namespace mma
|
||
|
|
||
|
#endif // LTEMPLATE_H
|
||
|
|