/**
 Copyright 2015-2022 The MathWorks, Inc.
*/
#ifndef MATHWORKS_MPS_CLIENT
#define MATHWORKS_MPS_CLIENT

#define MPS_CLIENT_1_0 0x00010000
#define MPS_CLIENT_1_1 0x00010001

#ifndef MPS_MAX_VER
#define MPS_MAX_VER MPS_CLIENT_1_1
#endif

#ifdef _MSC_VER
#define MPS_DLL_EXPORT_SYM __declspec(dllexport)
#define MPS_DLL_IMPORT_SYM __declspec(dllimport)
#elif __GNUC__ >= 4
#define MPS_DLL_EXPORT_SYM __attribute__((visibility("default")))
#define MPS_DLL_IMPORT_SYM __attribute__((visibility("default")))
#else
#define MPS_DLL_EXPORT_SYM
#define MPS_DLL_IMPORT_SYM
#endif

#if defined(BUILDING_MPSCLIENT)
#define MPSCLIENT_PUBLISHED_API MPS_DLL_EXPORT_SYM
#else
#define MPSCLIENT_PUBLISHED_API MPS_DLL_IMPORT_SYM
#endif

#ifdef __cplusplus
#define MPSCLIENT_PUBLISHED_API_EXTERN_C extern "C" MPSCLIENT_PUBLISHED_API
#else
#define MPSCLIENT_PUBLISHED_API_EXTERN_C extern MPSCLIENT_PUBLISHED_API
#endif

#include <limits.h>

#ifdef _MSC_VER
typedef unsigned __int64 uint64;
#else
#include <stddef.h>
#include <stdint.h>
typedef uint64_t uint64;
#endif

/** Type that represents size values, such as array dimensions. */
typedef uint64 mpsSize;

/** Type that represents index values, such as indices into arrays. */
typedef uint64 mpsIndex;

/** Fundamental type underlying MATLAB data. Use any of the <c>mpsCreate</c> functions to create
    data, and the corresponding <c>mpsDestroyArray</c> function to free memory. */
typedef struct mpsArray mpsArray;

/** Structure containing information configuring the connection between the client and a
    server instance.
    \sa <c>mpsClientRuntime</c>
*/
typedef struct mpsClientConfig mpsClientConfig;

/** Establishes a connection between a client and a server.
    An instance of <c>mpsClientContext</c> should not be shared across multiple threads at the
    same time. In a multi-threaded environment, each thread should get its own instance of
    <c>mpsClientContext</c>.
    \sa <c>mpsClientRuntime</c>
*/
typedef struct mpsClientContext mpsClientContext;

#ifndef TMW_BITS_PER_INT
#if INT_MAX == 0x7FL
#define TMW_BITS_PER_INT 8
#elif INT_MAX == 0x7FFFL
#define TMW_BITS_PER_INT 16
#elif INT_MAX == 0x7FFFFFFFL
#define TMW_BITS_PER_INT 32
#else
#define TMW_BITS_PER_INT -1
#endif
#endif

#ifndef TMW_BITS_PER_LONG
#if LONG_MAX == 0x7FL
#define TMW_BITS_PER_LONG 8
#elif LONG_MAX == 0x7FFFL
#define TMW_BITS_PER_LONG 16
#elif LONG_MAX == 0x7FFFFFFFL
#define TMW_BITS_PER_LONG 32
#else
#define TMW_BITS_PER_LONG -1
#endif
#endif

#ifndef TMW_BITS_PER_SCHAR
#if SCHAR_MAX == 0x7FL
#define TMW_BITS_PER_SCHAR 8
#elif SCHAR_MAX == 0x7FFFL
#define TMW_BITS_PER_SCHAR 16
#elif SCHAR_MAX == 0x7FFFFFFFL
#define TMW_BITS_PER_SCHAR 32
#else
#define TMW_BITS_PER_SCHAR -1
#endif
#endif

#ifndef TMW_BITS_PER_SHRT
#if SHRT_MAX == 0x7FL
#define TMW_BITS_PER_SHRT 8
#elif SHRT_MAX == 0x7FFFL
#define TMW_BITS_PER_SHRT 16
#elif SHRT_MAX == 0x7FFFFFFFL
#define TMW_BITS_PER_SHRT 32
#else
#define TMW_BITS_PER_SHRT -1
#endif
#endif

#ifndef UINT16_T
#if TMW_BITS_PER_INT == 16
#define UINT16_T unsigned int
#elif TMW_BITS_PER_LONG == 16
#define UINT16_T unsigned long
#elif TMW_BITS_PER_SCHAR == 16
#define UINT16_T unsigned char
#elif TMW_BITS_PER_SHRT == 16
#define UINT16_T unsigned short
#endif
#endif

/* +0 because of a doxygen bug */
#if (defined(__cplusplus) && (__cplusplus + 0 >= 201103L)) || \
    (defined(_HAS_CHAR16_T_LANGUAGE_SUPPORT) && _HAS_CHAR16_T_LANGUAGE_SUPPORT)
typedef char16_t CHAR16_T;
#elif defined(_MSC_VER)
typedef wchar_t CHAR16_T;
#else
typedef UINT16_T CHAR16_T;
#endif

typedef CHAR16_T mpsChar;

#if !defined(__cplusplus)

typedef unsigned char bool;

#ifndef false
#define false (0)
#endif
#ifndef true
#define true (1)
#endif

#endif

/** Type for logical array. All logical <c>mpsArray</c>s store their data elements as
    <c>mpsLogical</c> rather than as <c>bool</c>. */
typedef bool mpsLogical;

/** Types of errors that can be thrown when MATLAB function invocation fails */
typedef enum mpsErrorInfoType {

    /** A non-200 HTTP response when MATLAB function in invoked from the client */
    MPS_HTTP_ERROR_INFO,

    /** A MATLAB execution error */
    MPS_MATLAB_ERROR_INFO,

    /** Any error other than MATLAB execution error or a non-200 HTTP response */
    MPS_GENERIC_ERROR_INFO

} mpsErrorInfoType;


/** Details about a non-200 HTTP response */
typedef struct mpsErrorInfoHTTP
{

    /** HTTP response code */
    unsigned int responseCode;

    /** HTTP response message */
    const char* responseMessage;

} mpsErrorInfoHTTP;


/** Entry in the MATLAB runtime stack */
typedef struct matlabStackFrame
{

    /** Name of the MATLAB file that threw the MATLAB error */
    const char* file;

    /** Name of the MATLAB function which in most cases is same as the MATLAB file name */
    const char* function;

    /** Line number in the MATLAB file where the error occurred */
    int line;

} matlabStackFrame;


/** Details about a MATLAB execution error */
typedef struct mpsErrorInfoMATLAB
{

    /** Error message corresponding to the error thrown in MATLAB */
    const char* message;

    /** Unique error identifier corresponding to the MATLAB error */
    const char* identifier;

    /** MATLAB stack associated with the MATLAB error */
    const matlabStackFrame* matlabStack;

    /** Number of entries in the MATLAB stack */
    size_t matlabStackDepth;

} mpsErrorInfoMATLAB;

/** Details about an error not caused by non-200 HTTP errors or MATLAB execution errors */
typedef struct mpsErrorInfoGeneric
{

    /** Error message corresponding to the generic error */
    const char* genericErrorMsg;

} mpsErrorInfoGeneric;


/** Error thrown when a MATLAB function is invoked by the MATLAB Production Server client context */
typedef struct mpsErrorInfo
{

    /** Type of underlying error */
    mpsErrorInfoType type;

    /** Message regarding the error */
    char const* message;

    /** All possible errors that can be thrown when a MATLAB function is invoked from the client */
    union {
        /** Error caused by a non-200 HTTP response */
        mpsErrorInfoHTTP http;

        /** Error thrown during execution of MATLAB code */
        mpsErrorInfoMATLAB matlab;

        /** Error other than MATLAB execution error and non-200 HTTP response */
        mpsErrorInfoGeneric general;
    } details;

} mpsErrorInfo;

/** Error status codes for all methods which are part of the MATLAB Production Server client API */
typedef enum mpsStatus {

    /** Successful invocation of a method */
    MPS_OK,

    /** Failure */
    MPS_FAILURE

} mpsStatus;

/** Flag specifying whether array has imaginary components */
typedef enum mpsComplexity {
    /** Identifies an <c>mpsArray</c> with no imaginary components. */
    mpsREAL,
    /** Identifies an <c>mpsArray</c> with imaginary components. */
    mpsCOMPLEX
} mpsComplexity;

/** Flag identifying class of array */
typedef enum mpsClassID {
    /** Identifies a numeric <c>mpsArray</c> whose data is stored as either <c>unsigned char</c> or
       <c>byte</c>. */
    mpsUINT8_CLASS,
    /** Identifies a numeric <c>mpsArray</c> whose data is stored as either <c>char</c> or
       <c>byte</c>. */
    mpsINT8_CLASS,
    /** Identifies a numeric <c>mpsArray</c> whose data is stored as <c>unsigned short</c>. */
    mpsUINT16_CLASS,
    /** Identifies a numeric <c>mpsArray</c> whose data is stored as <c>short</c>. */
    mpsINT16_CLASS,
    /** Identifies a numeric <c>mpsArray</c> whose data is stored as <c>unsigned int</c>. */
    mpsUINT32_CLASS,
    /** Identifies a numeric <c>mpsArray</c> whose data is stored as <c>int</c>. */
    mpsINT32_CLASS,
    /** Identifies a numeric <c>mpsArray</c> whose data is stored as <c>unsigned long long</c>. */
    mpsUINT64_CLASS,
    /** Identifies a numeric <c>mpsArray</c> whose data is stored as <c>long long</c>. */
    mpsINT64_CLASS,
    /** Identifies a numeric <c>mpsArray</c> whose data is stored as <c>float</c>. */
    mpsSINGLE_CLASS,
    /** Identifies a numeric <c>mpsArray</c> whose data is stored as <c>double</c>. */
    mpsDOUBLE_CLASS,
    /** Identifies a logical <c>mpsArray</c>. */
    mpsLOGICAL_CLASS,
    /** Identifies a string <c>mpsArray</c>. */
    mpsCHAR_CLASS,
    /** Identifies a structure <c>mpsArray</c>. */
    mpsSTRUCT_CLASS,
    /** Identifies a cell <c>mpsArray</c>. */
    mpsCELL_CLASS
} mpsClassID;

/** MATLAB Production Server client API container.
    <c>mpsClientRuntime</c> provides functions needed to create the MATLAB Production Server client
   execution context, configure it, and use the execution context to execute MATLAB functions hosted
   by an MATLAB Production Server instance.
*/
typedef struct mpsClientRuntime
{

    /** Initialize pointer to MATLAB Production Server client configuration instance with default
       values. The configuration parameters are used to configure the connection between client and
       MATLAB Production Server instance. Currently, the only parameter that can be initialized is
       the response timeout in seconds. This is the time the client will wait for the MATLAB
       Production Server instance to send the response. The default value is 120 seconds.
        @param config An uninitialized pointer to MATLAB Production Server client configuration
       instance.
        @return Status representing whether client configuration initialization was
                successful.
    */
    mpsStatus (*createConfig)(mpsClientConfig** config);

    /** Clean up the memory allocated to the client configuration instance
        @param config Pointer to the client configuration that needs to be cleaned up
    */
    void (*destroyConfig)(mpsClientConfig* config);

    /** Set the timeout value, in seconds, for the client to receive response from the server.*/
    void (*setResponseTimeOutSec)(mpsClientConfig* config, unsigned long value);

    /** Get the timeout value, in seconds, for the client to receive response from the server */
    unsigned long (*getResponseTimeOutSec)(const mpsClientConfig* config);

    /** Set the response size limit value, in bytes, for the client.*/
    void (*setResponseSizeLimit)(mpsClientConfig* config, unsigned int value);

    /** Get the responze size limit value, in bytes, for the client. */
    unsigned int (*getResponseSizeLimit)(const mpsClientConfig* config);

    /** Initialize the MATLAB Production Server client execution context.
        Execution context is initialized with the configuration parameters passed using
        MATLAB Production Server client configuration instance. The execution context encapsulates
       the HTTP framework required to execute MATLAB functions from the client. It also manages the
       connections between the client and the server instance. The execution context makes copy of
       the configuration being passed. As a result, the configuration pointer does not need to be
       kept around in the client application once the execution context has been created. You should
       use one execution context for every thread and it should not be shared across multiple
       threads.
        @param context Uninitialized memory location of pointer to the client context
        @param config Pointer to the client-server connection configuration
    */
    mpsStatus (*createContext)(mpsClientContext** context, const mpsClientConfig* config);

    /** Clean up the memory allocated for the client execution context.
        This function cleans up all the resources used while invoking MATLAB functions
        from the client e.g the container for holding the last error. It also closes
        all the open connections between the client and the server instance created
        by the client execution context. Once destroyed, the execution context cannot be used to
       invoke MATLAB functions.
        @param context Pointer to the client context
    */
    void (*destroyContext)(mpsClientContext* context);

    /** Access the error thrown while invoking a MATLAB function from the client
        A MATLAB function invocation failure can happen due to one of the following reasons:
        1. MATLAB function threw an error
        2. Client received a response code other than 200
        3. Failure due to connection issues between the client and MATLAB Production Server instance
        The purpose of this function is to provide detailed information about the root cause of the
       failure. This function should be invoked only when the return status of MATLAB function
       invocation is a non-success status. Invoking this method when the MATLAB function invocation
       is successful will result in a undefined behaviour.
        @param context MATLAB Production Server client context used to invoke the MATLAB function
        @param errorInfo Struct containing detailed information regarding failed MATLAB function
       invocation
    */
    void (*getLastErrorInfo)(mpsClientContext const* context, mpsErrorInfo* errorInfo);

    /** Cleans up dynamic memory allocated while initializing mpsErrorInfo instance
     */
    void (*destroyLastErrorInfo)(mpsErrorInfo* errorInfo);

    /** Invoke MATLAB function hosted by a server instance and available at a Url.
        MATLAB Production Server clients invoke MATLAB functions hosted by a server instance using
        a Url with the following format:
        `http://<hostname>:<port_number>/<archive_name>/<MATLAB_function_name>`.

        The following Url exposes the MATLAB function `mymagic` deployed in `magic.ctf` at port
        9910 localhost.

            http://localhost:9910/magic/mymagic

        The inputs and outputs are of type `mpsArray`.

        If the return status of this function is not success, the root cause of the failure is
       accessed by invoking `getLastErrorInfo()`.
        @param context Client context that holds a handle to the underlying HTTP framework instance
        @param url Url to the MATLAB function
        @param nlhs Number of outputs (number of left hand side parameters)
        @param plhs Pointer to the output
        @param nrhs Number of input arguments (number of right hand side parameters)
        @param prhs Pointer to the inputs
        @return Status of execution.
    */
    mpsStatus (*feval)(mpsClientContext* context,
                       const char* url,
                       int nlhs,
                       mpsArray* plhs[],
                       int nrhs,
                       const mpsArray* prhs[]);

    /**
        MPS_CLIENT_1_1 version provides support for request execution using HTTPS protocol for
       secure client-server communication
    */
#if MPS_MAX_VER >= MPS_CLIENT_1_1

    /** Set client certificate file in PEM format. */
    void (*setClientCertFile)(mpsClientConfig* config, const char* certFile);

    /** Get client certificate file */
    const char* (*getClientCertFile)(const mpsClientConfig* config);

    /** Set private key file in PEM format */
    void (*setPrivateKeyFile)(mpsClientConfig* config, const char* pkFile);

    /** Get the private key file */
    const char* (*getPrivateKeyFile)(const mpsClientConfig* config);

    /** Set password for private key file */
    void (*setPrivateKeyPasswd)(mpsClientConfig* config, const char* passwd);

    /** Get password for private key file */
    const char* (*getPrivateKeyPasswd)(const mpsClientConfig* config);

    /** Set file Certificate Authority file in PEM format */
    void (*setCAFile)(mpsClientConfig* config, const char* caFile);

    /** Get Certificate Authority file */
    const char* (*getCAFile)(const mpsClientConfig* config);

    /** Set the revocation list file */
    void (*setRevocationListFile)(mpsClientConfig* config, const char* crlFile);

    /** Get the revocation list file */
    const char* (*getRevocationListFile)(const mpsClientConfig* config);

    /** Setting this flag to true will verify the hostname in thr url against the common name in the
     * certificate */
    void (*setVerifyHost)(mpsClientConfig* config, mpsLogical verifyHost);

    /** Get the host verification flag value */
    mpsLogical (*getVerifyHost)(const mpsClientConfig* config);

    /** Setting this flag to false will not peform the authentication of server certificate. Doing
       so will make the connection insecure and can be prone to man-in-the-middle attack
    */
    void (*setVerifyPeer)(mpsClientConfig* config, mpsLogical verifyPeer);

    /** Get the peer verification flag value */
    mpsLogical (*getVerifyPeer)(const mpsClientConfig* config);

#endif

} mpsClientRuntime;

/** Set up the programming environment for MATLAB Production Server client based on
    version 1.0.
    This global initialization should be done just once in the program. Invoking
    it multiple times will return the pointer to the same underlying API struct.
    @return A pointer to the MATLAB Production Server client API struct.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsClientRuntime* mpsInitialize(void);

/** Set up the programming environment for MATLAB Production Server client based
    on the version number provided by the user. This function can only be used to
    access the versions above 1.0. To use version 1.0, please use <c>mpsInitialize()</c>
    function.
    This global initialization should be done just once in the program. Invoking
    it multiple times will return the pointer to the same underlying API struct.
    @return A pointer to the MATLAB Production Server client API struct corresponding
            to the version provided.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsClientRuntime* mpsInitializeEx(int version);

/** Perform global clean up of resources consumed by MATLAB Production Server client environment.
    This should be done just once in the application.
    Invoking it multiple times will have no effect.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C void mpsTerminate(void);

/** Returns the error message representing the failure in global initialization
    that sets up the programming environment for MATLAB Production Server client.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C const char* mpsGetInitializationErrorMessage(void);

/** Determine whether <c>mpsArray</c> represents data as double-precision, floating-point numbers.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array stores its data as double-precision, floating-point numbers, and false
   otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsDouble(const mpsArray* mlArr);

/** Determine whether <c>mpsArray</c> represents data as single-precision, floating-point numbers.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array stores its data as single-precision, floating-point numbers, and false
   otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsSingle(const mpsArray* mlArr);

/** Determine whether <c>mpsArray</c> contains an imaginary part.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array contains an imaginary part, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsComplex(const mpsArray* mlArr);

/** Determine whether <c>mpsArray</c> contains numeric data.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array contains numeric data, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsNumeric(const mpsArray* mlArr);

/** Determine whether <c>mpsArray</c> represents data as signed 64-bit integers.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array represents data as signed 64-bit integers, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsInt64(const mpsArray* mlArr);

/** Determine whether <c>mpsArray</c> represents data as unsigned 64-bit integers.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array represents data as unsigned 64-bit integers, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsUint64(const mpsArray* mlArr);

/** Determine whether <c>mpsArray</c> represents data as signed 32-bit integers.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array represents data as signed 32-bit integers, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsInt32(const mpsArray* mlArr);

/** Determine whether <c>mpsArray</c> represents data as unsigned 32-bit integers.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array represents data as unsigned 32-bit integers, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsUint32(const mpsArray* mlArr);

/** Determine whether <c>mpsArray</c> represents data as signed 16-bit integers.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array represents data as signed 16-bit integers, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsInt16(const mpsArray* mlArr);

/** Determine whether <c>mpsArray</c> represents data as unsigned 16-bit integers.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array represents data as unsigned 16-bit integers, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsUint16(const mpsArray* mlArr);

/** Determine whether <c>mpsArray</c> represents data as signed 8-bit integers.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array represents data as signed 8-bit integers, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsInt8(const mpsArray* mlArr);

/** Determine whether <c>mpsArray</c> represents data as unsigned 8-bit integers.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array represents data as unsigned 8-bit integers, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsUint8(const mpsArray* mlArr);

/** Determine whether <c>mpsArray</c> represents a string array.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array has the class <c>mpsCHAR_CLASS</c>, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsChar(const mpsArray* mlArr);

/** Determine whether <c>mpsArray</c> represents data as <c>mpsLogical</c>.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array represents data as <c>mpsLogical</c>, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsLogical(const mpsArray* mlArr);

/** Determine whether MATLAB treats the scalar data in the <c>mpsArray</c> as logical or numerical.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array represents data as <c>mpsLogical</c> and has 1-by-1 dimensions, and
   false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsLogicalScalar(const mpsArray* mlArr);

/** Determine whether scalar array is true.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the value of the array's logical, scalar element is true, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsLogicalScalarTrue(const mpsArray* mlArr);

/** Determine whether input is structure array.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array represents data as <c>mpsStruct</c>, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsStruct(const mpsArray* mlArr);

/** Determine whether input is cell array.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array has the class <c>mxCELL_CLASS</c>, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsCell(const mpsArray* mlArr);

/** Determine whether array is member of specified class.
    @param mlArr Pointer to an <c>mpsArray</c>
    @param classname Array category you are testing. You can specify any one of the following
   predefined constants: Value of <c>classname</c> | Corresponding Class
    --------------------------| ---------------------
    `cell`                    | `mpsCELL_CLASS`
    `char`                    | `mpsCHAR_CLASS`
    `double`                  | `mpsDOUBLE_CLASS`
    `int8`                    | `mpsINT8_CLASS`
    `int16`                   | `mpsINT16_CLASS`
    `int32`                   | `mpsINT32_CLASS`
    `int64`                   | `mpsINT64_CLASS`
    `logical`                 | `mpsLOGICAL_CLASS`
    `single`                  | `mpsSINGLE_CLASS`
    `struct`                  | `mpsSTRUCT_CLASS`
    `uint8`                   | `mpsUINT8_CLASS`
    `uint16`                  | `mpsUINT16_CLASS`
    `uint32`                  | `mpsUINT32_CLASS`
    `uint64`                  | `mpsUINT64_CLASS`
    `<class_name>`            | `<class_id>`

    In the table, `<class_name>` represents the name of a specific MATLAB custom object. You can
   also specify one of your own class names.
    @return True if the array has the class <c>classname</c>, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsClass(const mpsArray* mlArr, const char* classname);

/** Determine whether array is empty.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array has no data, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsEmpty(const mpsArray* mlArr);

/** Determine whether array is sparse.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return True if the array is sparse, and false otherwise.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C bool mpsIsSparse(const mpsArray* mlArr);

/** Create a two-dimensional double-precision floating-point array.
    @param m Number of rows.
    @param n Number of columns.
    @param complexFlag If the array is to contain imaginary data, set to <c>mpsCOMPLEX</c>.
    Otherwise, set to <c>mpsREAL</c>.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateDoubleMatrix(mpsSize m,
                                                                 mpsSize n,
                                                                 mpsComplexity complexFlag);

/** Create a scalar double-precision floating-point array.
    @param value Value to which you want to initialize the array.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateDoubleScalar(double value);

/** Create a two-dimensional numeric array.
    @param m Number of rows.
    @param n Number of columns.
    @param classid Identifier for the class of the array, which determines the way the numerical
   data is represented in memory.
    @param complexFlag If the array is to contain imaginary data, set to <c>mpsCOMPLEX</c>.
    Otherwise, set to <c>mpsREAL</c>.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateNumericMatrix(mpsSize m,
                                                                  mpsSize n,
                                                                  mpsClassID classid,
                                                                  mpsComplexity complexFlag);

/** Create a two-dimensional numeric array.
    @param ndim Number of dimensions. If you specify a value that is less than 2, the number of
    dimensions is automatically set to 2.
    @param dims Dimensions array. Each element in the dimensions array contains the size of the
   array in that dimension.
    @param classid Identifier for the class of the array, which determines the way the numerical
   data is represented in memory.
    @param complexFlag If the array is to contain imaginary data, set to <c>mpsCOMPLEX</c>.
    Otherwise, set to <c>mpsREAL</c>.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateNumericArray(mpsSize ndim,
                                                                 const mpsSize* dims,
                                                                 mpsClassID classid,
                                                                 mpsComplexity complexFlag);

/** Create a string array.
    @param str String that is to serve as the initial data.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateString(const char* str);

/** Create a two-dimensional string array.
    @param rows Number of rows in the created array. The value you specify is the number of strings
   in <c>str</c>.
    @param str Array of strings containing at least <c>rows</c> strings.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateCharMatrixFromStrings(mpsSize rows,
                                                                          const char** str);

/** Create a N-dimensional string array.
    @param ndim Number of dimensions. If you specify a value that is less than 2, the number of
    dimensions is automatically set to 2.
    @param dims Dimensions array. Each element in the dimensions array contains the size of the
   array in that dimension.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateCharArray(mpsSize ndim, const mpsSize* dims);

/** Create a scalar logical array.
    @param value Logical value to which you want to initialize the array.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateLogicalScalar(mpsLogical value);

/** Create a two-dimensional logical array.
    @param m Number of rows.
    @param n Number of columns.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateLogicalMatrix(mpsSize m, mpsSize n);

/** Create a N-dimensional logical array.
    @param ndim Number of dimensions. If you specify a value that is less than 2, the number of
    dimensions is automatically set to 2.
    @param dims Dimensions array. Each element in the dimensions array contains the size of the
   array in that dimension.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateLogicalArray(mpsSize ndim, const mpsSize* dims);

/** Create a two-dimensional struct array.
    @param m Number of rows.
    @param n Number of columns.
    @param nfields Number of fields in each element.
    @param fieldnames List of field names.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateStructMatrix(mpsSize m,
                                                                 mpsSize n,
                                                                 int nfields,
                                                                 const char** fieldnames);

/** Create a N-dimensional struct array.
    @param ndim Number of dimensions. If you specify a value that is less than 2, the number of
    dimensions is automatically set to 2.
    @param dims Dimensions array. Each element in the dimensions array contains the size of the
   array in that dimension.
    @param nfields Number of fields in each element.
    @param fieldnames List of field names.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateStructArray(mpsSize ndim,
                                                                const mpsSize* dims,
                                                                int nfields,
                                                                const char** fieldnames);

/** Create a two-dimensional cell array.
    @param m Number of rows.
    @param n Number of columns.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateCellMatrix(mpsSize m, mpsSize n);

/** Create a N-dimensional cell array.
    @param ndim Number of dimensions. If you specify a value that is less than 2, the number of
    dimensions is automatically set to 2.
    @param dims Dimensions array. Each element in the dimensions array contains the size of the
   array in that dimension.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateCellArray(mpsSize ndim, const mpsSize* dims);

/** Create a two-dimensional sparse array.
    @param m Number of rows.
    @param n Number of columns.
    @param nzmax Number of elements to allocate to hold the <c>pr</c>, <c>ir</c>, and, if
    <c>complexFlag</c> is <c>mpsCOMPLEX</c>), <c>pi</c> arrays. Set the value to be greater than
    or equal to the number of non-zero elements you plan to put into the array, but make sure that
    <c>nzmax</c> is less than or equal to <c>m*n</c>.
    @param complexFlag If the array is to contain imaginary data, set to <c>mpsCOMPLEX</c>.
    Otherwise, set to <c>mpsREAL</c>.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateSparse(mpsSize m,
                                                           mpsSize n,
                                                           mpsSize nzmax,
                                                           mpsComplexity complexFlag);

/** Create a two-dimensional sparse array.
    @param m Number of rows.
    @param n Number of columns.
    @param nzmax Number of elements to allocate to hold the data. Set the value to be greater than
    or equal to the number of non-zero elements you plan to put into the array, but make sure that
    <c>nzmax</c> is less than or equal to <c>m*n</c>.
    @return Pointer to the created array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsCreateSparseLogicalMatrix(mpsSize m,
                                                                        mpsSize n,
                                                                        mpsSize nzmax);

/** Deallocate the memory occupied by an array.
    <c>mpsDestroyArray</c> not only deallocates the memory occupied by the array's characteristics
    fields, but also deallocates all the array's associated data arrays, such as <c>pr</c> and
    <c>pi</c> for complex arrays, <c>ir</c> and <c>jc</c> for sparse arrays, fields of structure
    arrays, and cells of cell arrays. Do __not__ call <c>mpsDestroyArray</c> on an array you are
    returning it in a left side argument.
    @param mlArr Array to destroy
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C void mpsDestroyArray(mpsArray* mlArr);

/** Make a deep copy of input <c>mpsArray</c>.
    @param mlArr Pointer to an <c>mpsArray</c> to be duplicated
    @return Duplicate copy of input <c>mpsArray</c>
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsDuplicateArray(const mpsArray* mlArr);

/** Deallocate heap space using the MATLAB memory management facility.
    @param data Pointer to the beginning of any memory parcel allocated by MATLAB.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C void mpsFree(void* data);

/** Determine the number of dimensions in an array.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return Number of dimensions in the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsSize mpsGetNumberOfDimensions(const mpsArray* mlArr);

/** Determine the number of bytes in each data element of an array.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return Number of bytes required to store one element of the specified array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C size_t mpsGetElementSize(const mpsArray* mlArr);

/** Determine how many elements are in each dimension of an array.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return Pointer to the first element in the dimensions array. Each integer in the dimensions
    array represents the number of elements in a particular dimension. The array is not <c>NULL</c>
    terminated.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C const mpsSize* mpsGetDimensions(const mpsArray* mlArr);

/** Modify number of dimensions and size of each dimension of an array.
    @param mlArr Pointer to an <c>mpsArray</c>
    @param dims Dimensions array. Each element in the dimensions array contains the size of the
   array in that dimension.
    @param ndim Number of dimensions.
    @return <c>0</c> on success, and <c>1</c> on failure.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C int mpsSetDimensions(mpsArray* mlArr,
                                                      const mpsSize* dims,
                                                      mpsSize ndim);

/** Determine how many elements are in the array.
    @param mlArr Pointer to an <c>mpsArray</c>
    @return Number of elements in the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C size_t mpsGetNumberOfElements(const mpsArray* mlArr);

/** Determine how many elements there are between the beginning of an array and a given element.
    Arrays in MATLAB are stored column-major, so accessing the elements of an `mpsArray` using
    standard C indexing will produce a transposed array. This function accepts row-major
    dimension subscripts and calculate the proper column-major index to the the desired value.
    @param mlArr Pointer to an <c>mpsArray</c>.
    @param nsubs Number of elements in the subs array.
    @param subs Array of integers. Each value in the array specifies that dimension's subscript.
    @return Number of elements, or _index_, between the start of the array and the specified
    subscript. This is the linear index equivalent of the subscripts calculated as if the array
    were stored column major as they are in MATLAB.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsIndex mpsCalcSingleSubscript(const mpsArray* mlArr,
                                                                 mpsSize nsubs,
                                                                 mpsIndex* subs);

/** Determine the first dimension of an array.
    @param mlArr Pointer to an <c>mpsArray</c>.
    @return First dimension of the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C size_t mpsGetM(const mpsArray* mlArr);

/** Set the first dimension of an array.
    @param mlArr Pointer to an <c>mpsArray</c>.
    @param m First dimension of the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C void mpsSetM(mpsArray* mlArr, mpsSize m);

/** Determine the number of columns in an array.
    @param mlArr Pointer to an <c>mpsArray</c>.
    @return Number of columns in the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C size_t mpsGetN(const mpsArray* mlArr);

/** Set the number of columns in an array.
    @param mlArr Pointer to an <c>mpsArray</c>.
    @param n Number of columns in the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C void mpsSetN(mpsArray* mlArr, mpsSize n);

/** Determine the value of the first real element of an array.
    @param mlArr Pointer to an <c>mpsArray</c>.
    @return Pointer to the value of the first real element of the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C double mpsGetScalar(const mpsArray* mlArr);

/** Access the real data in an array of doubles.
    @param mlArr Pointer to an <c>mpsArray</c> of doubles.
    @return Pointer to the first element of the real data.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C double* mpsGetPr(const mpsArray* mlArr);

/** Access the imaginary data in an array of doubles.
    @param mlArr Pointer to an <c>mpsArray</c> of doubles.
    @return Pointer to the imaginary data elements of the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C double* mpsGetPi(const mpsArray* mlArr);

/** Access the real numeric data in an array.
    @param mlArr Pointer to an <c>mpsArray</c>.
    @return Pointer to the real data elements of the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C void* mpsGetData(const mpsArray* mlArr);

/** Access the imaginary data in an array.
    @param mlArr Pointer to an <c>mpsArray</c>.
    @return Pointer to the imaginary data elements of the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C void* mpsGetImagData(const mpsArray* mlArr);

/** Access the data in a character array.
    @param mlArr Pointer to an <c>mpsArray</c>.
    @return Pointer to the imaginary data elements of the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsChar* mpsGetChars(const mpsArray* mlArr);

/** Copy the character data of a string array into a C-style string.
    @param mlArr Pointer to an <c>mpsArray</c> having the <c>mpsCHAR_CLASS</c> class.
    @param str Starting location for the string. <c>mpsGetString</c> writes the character data
    into <c>str</c> and then terminates the string with a <c>NULL</c> character.
    @param len Size, in bytes, of destination buffer pointed to by <c>str</c>.
    @return <c>0</c> on success or if <c>strlen == 0</c>, and <c>1</c> on failure.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C int mpsGetString(const mpsArray* mlArr, char* str, mpsSize len);

/** Copy the character data of a string array into a C-style string.
    @param mlArr Pointer to an <c>mpsArray</c> having the <c>mpsCHAR_CLASS</c> class.
    @return <c>NULL</c> terminated string.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C char* mpsArrayToString(const mpsArray* mlArr);

/** Determine the class of an array.
    @param mlArr Pointer to an <c>mpsArray</c>.
    @return Numeric identifier of the class of the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsClassID mpsGetClassID(const mpsArray* mlArr);

/** Determine the class of an array.
    @param mlArr Pointer to an <c>mpsArray</c>.
    @return String identifier of the class of the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C const char* mpsGetClassName(const mpsArray* mlArr);

/** Access the first logical element in an array.
    @param mlArr Pointer to an <c>mpsArray</c>.
    @return Pointer to the first logical element in the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsLogical* mpsGetLogicals(const mpsArray* mlArr);

/** Access the value held in the specified element of the specified field of an array of struct.
    @param mlArr Pointer to an <c>mpsArray</c> of struct.
    @param index Index of the desired element.
    @param fieldname Name of the field whose value you want to extract.
    @return Pointer to the <c>mpsArray</c> in the specified field at the specified <c>fieldname</c>.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsGetField(const mpsArray* mlArr,
                                                       mpsIndex index,
                                                       const char* fieldname);

/** Set the value held in the specified element of the specified field of an array of struct.
    @param mlArr Pointer to an <c>mpsArray</c> of struct.
    @param index Index of the desired element.
    @param fieldname Name of a field in the struct. The field **must** exist.
    @param pvalue Pointer to an <c>mpsArray</c> containing the data you want to assign to
   <c>fieldname</c>.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C void mpsSetField(mpsArray* mlArr,
                                                  mpsIndex index,
                                                  const char* fieldname,
                                                  mpsArray* pvalue);

/** Determine the number of fields in an array of struct.
    @param mlArr Pointer to an <c>mpsArray</c> of struct.
    @return Number of fields.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C int mpsGetNumberOfFields(const mpsArray* mlArr);

/** Determine the number of a field in an array of struct given the field name.
    @param mlArr Pointer to an <c>mpsArray</c> of struct.
    @param fieldname Name of a field in the struct.
    @return Field number of the specified field.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C int mpsGetFieldNumber(const mpsArray* mlArr,
                                                       const char* fieldname);

/** Determine the name of a field in an array of struct given the field number.
    @param mlArr Pointer to an <c>mpsArray</c> of struct.
    @param fieldnumber Number of a field in the struct.
    @return Pointer to the name of the specified field.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C const char* mpsGetFieldNameByNumber(const mpsArray* mlArr,
                                                                     int fieldnumber);

/** Access the value held in the specified field number at the indexed element of an array.
    @param mlArr Pointer to an <c>mpsArray</c> of struct.
    @param index Index of the desired element.
    @param fieldnumber Number of a field in the struct.
    @return Pointer to the <c>mpsArray</c> in the specified field for the desired element.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsGetFieldByNumber(const mpsArray* mlArr,
                                                               mpsIndex index,
                                                               int fieldnumber);

/** Set the value held in the specified field number at the indexed element of an array.
    @param mlArr Pointer to an <c>mpsArray</c> of struct.
    @param index Index of the desired element.
    @param fieldnumber Number of a field in the struct.
    @param pvalue Pointer to the <c>mpsArray</c> containing the data to set.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C void mpsSetFieldByNumber(mpsArray* mlArr,
                                                          mpsIndex index,
                                                          int fieldnumber,
                                                          mpsArray* pvalue);

/** Add a field to an array of struct.
    @param mlArr Pointer to an <c>mpsArray</c> of struct.
    @param fieldname Name of the field to add.
    @return Field number on success, or <c>-1</c> if inputs are invalid or an out-of-memory
   condition occurs.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C int mpsAddField(mpsArray* mlArr, const char* fieldname);

/** Delete a field from an array of struct.
    @param mlArr Pointer to an <c>mpsArray</c> of struct.
    @param fieldnumber Number of the field to delete.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C void mpsRemoveField(mpsArray* mlArr, int fieldnumber);

/** Access the contents of a cell array at a specified index.
    @param mlArr Pointer to an <c>mpsArray</c> cell array.
    @param index Number of elements in the cell array between the first element and the desired one.
    @return Pointer to the data in specified cell of the array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsArray* mpsGetCell(const mpsArray* mlArr, mpsIndex index);

/** Set the contents of a cell array at a specified index.
    @param mlArr Pointer to an <c>mpsArray</c> cell array.
    @param index Number of elements in the cell array between the first element and the desired one.
    @param value Pointer to new value for the cell. You can put an <c>mpsArray</c> of any type into
   a cell. You can even put another cell <c>mpsArray</c> into a cell.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C void mpsSetCell(mpsArray* mlArr, mpsIndex index, mpsArray* value);

/** Determine the number of non-zero elements in a sparse array.
    @param mlArr Pointer to a sparse <c>mpsArray</c>.
    @return Number of elements allocated to hold non-zero entries in the specified sparse array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsSize mpsGetNzmax(const mpsArray* mlArr);

/** Set the maximum number of non-zero elements in a sparse array.
    @param mlArr Pointer to a sparse <c>mpsArray</c>.
    @param nzmax Number of elements allocated to hold the arrays pointed to by <c>ir</c>, <c>pr</c>,
    and <c>pi</c>. Set <c>nzmax</c> greater than or equal to the number of non-zero elements in the
   array, but set it to be less than or equal to the number of rows times the number of columns. If
   you specify a value of <c>0</c>, the value of nzmax is automatically set to <c>1</c>.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C void mpsSetNzmax(mpsArray* mlArr, mpsSize nzmax);

/** Determine the starting address of the <c>ir</c> array in a sparse array.
    @param mlArr Pointer to a sparse <c>mpsArray</c>.
    @return Pointer to the first element in the <c>ir</c> array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsIndex* mpsGetIr(const mpsArray* mlArr);

/** Determine the starting address of the <c>jc</c> array in a sparse array.
    @param mlArr Pointer to a sparse <c>mpsArray</c>.
    @return Pointer to the first element in the <c>jc</c> array.
*/
MPSCLIENT_PUBLISHED_API_EXTERN_C mpsIndex* mpsGetJc(const mpsArray* mlArr);

#endif
