// <cdqcsim> -*- C++ -*-

#ifndef _CDQCSIM_INCLUDED_
//! \cond Doxygen_Suppress
#define _CDQCSIM_INCLUDED_
//! \endcond

/*!
 * \file cdqcsim
 * \brief Provides DQCsim's raw C API symbols in the `dqcsim::raw` namespace.
 *
 * This is file is functionally identical to `dqcsim.h`, but doesn't pollute
 * the global namespace.
 */

#include <cstdio>
#include <cstdarg>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <new>

//! \cond Doxygen_Suppress
#ifndef _DQCSIM_HDR_CPP_
#define _DQCSIM_HDR_CPP_
#endif
//! \endcond

#ifdef __cplusplus
extern "C" {
#endif

// For Doxygen:
namespace dqcsim {
/**
 * Namespace containing all the symbols defined by the C API.
 *
 * When including `<cdqcsim>` instead of `<dqcsim.h>`, the C API symbols are
 * placed in this namespace. This behavior mimics the C++ standard library,
 * like how for instance including `<cstring>` gives you `std::memcpy()`
 * instead of `memcpy()`.
 *
 * Note that the `dqcs_`/`DQCS_` namespacing prefixes of the C API remain,
 * so the full name of the symbols becomes `dqcsim::raw::dqcs_*` etc. The
 * `dqcsim::wrap` namespace populated in addition by including `<dqcsim>`
 * provides more C++-like bindings, similar to `<cstring>` versus `<string>`
 * in the standard library.
 */
namespace raw {
} // namespace raw
} // namespace dqcsim


namespace dqcsim {
namespace raw {

/// Enumeration of Pauli bases.
enum class dqcs_basis_t {
  /// Invalid basis. Used as an error return value.
  DQCS_BASIS_INVALID = 0,
  /// The X basis.
  ///
  /// \f[
  /// \psi_X = \frac{1}{\sqrt{2}} \begin{bmatrix}
  /// 1 & -1 \\
  /// 1 & 1
  /// \end{bmatrix}
  /// \f]
  DQCS_BASIS_X = 1,
  /// The Y basis.
  ///
  /// \f[
  /// \psi_Y = \frac{1}{\sqrt{2}} \begin{bmatrix}
  /// 1 & i \\
  /// i & 1
  /// \end{bmatrix}
  /// \f]
  DQCS_BASIS_Y = 2,
  /// The Z basis.
  ///
  /// \f[
  /// \psi_Z = \begin{bmatrix}
  /// 1 & 0 \\
  /// 0 & 1
  /// \end{bmatrix}
  /// \f]
  DQCS_BASIS_Z = 3,
};

/// Return type for functions that normally return a boolean but can also fail.
enum class dqcs_bool_return_t {
  /// The function has failed. More information may be obtained through
  /// `dqcsim_explain()`.
  DQCS_BOOL_FAILURE = -1,
  /// The function did what it was supposed to and returned false.
  DQCS_FALSE = 0,
  /// The function did what it was supposed to and returned true.
  DQCS_TRUE = 1,
};

/// Types of DQCsim gates.
enum class dqcs_gate_type_t {
  /// Invalid gate type. Used as an error return value.
  DQCS_GATE_TYPE_INVALID = 0,
  /// Unitary gates have one or more target qubits, zero or more control
  /// qubits, and a unitary matrix, sized for the number of target qubits.
  ///
  /// The semantics are that the unitary matrix expanded by the number of
  /// control qubits is applied to the qubits.
  ///
  /// The data field may add pragma-like hints to the gate, for instance to
  /// represent the line number in the source file that generated the gate,
  /// error modelling information, and so on. This data may be silently
  /// ignored.
  DQCS_GATE_TYPE_UNITARY,
  /// Measurement gates have one or more measured qubits and a 2x2 unitary
  /// matrix representing the basis.
  ///
  /// The semantics are:
  ///
  ///  - the hermetian of the matrix is applied to each individual qubit;
  ///  - each individual qubit is measured in the Z basis;
  ///  - the matrix is applied to each individual qubit;
  ///  - the results of the measurement are propagated upstream.
  ///
  /// This allows any measurement basis to be used.
  ///
  /// The data field may add pragma-like hints to the gate, for instance to
  /// represent the line number in the source file that generated the gate,
  /// error modelling information, and so on. This data may be silently
  /// ignored.
  DQCS_GATE_TYPE_MEASUREMENT,
  /// Prep gates have one or more target qubits and a 2x2 unitary matrix
  /// representing the basis.
  ///
  /// The semantics are:
  ///
  ///  - each qubit is initialized to |0>;
  ///  - the matrix is applied to each individual qubit.
  ///
  /// This allows any initial state to be used.
  ///
  /// The data field may add pragma-like hints to the gate, for instance to
  /// represent the line number in the source file that generated the gate,
  /// error modelling information, and so on. This data may be silently
  /// ignored.
  DQCS_GATE_TYPE_PREP,
  /// Custom gates perform a user-defined mixed quantum-classical operation,
  /// identified by a name. They can have zero or more target, control, and
  /// measured qubits, of which only the target and control sets must be
  /// mutually exclusive. They also have an optional matrix of arbitrary
  /// size.
  ///
  /// The semantics are:
  ///
  ///  - if the name is not recognized, an error is reported;
  ///  - a user-defined operation is performed based on the name, qubits,
  ///    matrix, and data arguments;
  ///  - exactly one measurement result is reported upstream for exactly the
  ///    qubits in the measures set.
  DQCS_GATE_TYPE_CUSTOM,
};

/// Enumeration of types that can be associated with a handle.
enum class dqcs_handle_type_t {
  /// Indicates that the given handle is invalid.
  ///
  /// This indicates one of the following:
  ///
  ///  - The handle value is invalid (zero or negative).
  ///  - The handle has not been used yet.
  ///  - The object associated with the handle was deleted.
  DQCS_HTYPE_INVALID = 0,
  /// Indicates that the given handle belongs to an `ArbData` object.
  ///
  /// This means that the handle supports the `handle` and `arb` interfaces.
  DQCS_HTYPE_ARB_DATA = 100,
  /// Indicates that the given handle belongs to an `ArbCmd` object.
  ///
  /// This means that the handle supports the `handle`, `arb`, and `cmd`
  /// interfaces.
  DQCS_HTYPE_ARB_CMD = 101,
  /// Indicates that the given handle belongs to a queue of `ArbCmd` object.
  ///
  /// This means that the handle supports the `handle`, `arb`, `cmd`, and
  /// `cq` interfaces.
  DQCS_HTYPE_ARB_CMD_QUEUE = 102,
  /// Indicates that the given handle belongs to a set of qubit references.
  ///
  /// This means that the handle supports the `handle` and `qbset`
  /// interfaces.
  DQCS_HTYPE_QUBIT_SET = 103,
  /// Indicates that the given handle belongs to a quantum gate description.
  ///
  /// This means that the handle supports the `handle`, `gate`, and `arb`
  /// interfaces.
  DQCS_HTYPE_GATE = 104,
  /// Indicates that the given handle belongs to a qubit measurement result.
  ///
  /// This means that the handle supports the `handle`, `meas`, and `arb`
  /// interfaces. It can also be used in place of a qubit measurement result
  /// set by functions that consume the object.
  DQCS_HTYPE_MEAS = 105,
  /// Indicates that the given handle belongs to a set of qubit measurement
  /// results.
  ///
  /// This means that the handle supports the `handle` and `mset` interfaces.
  DQCS_HTYPE_MEAS_SET = 106,
  /// Indicates that the given handle belongs to a matrix.
  ///>
  ///> This means that the handle supports the `handle` and `mat` interfaces.
  DQCS_HTYPE_MATRIX = 107,
  /// Indicates that the given handle belongs to a gate map.
  ///>
  ///> This means that the handle supports the `handle` and `gm` interfaces.
  DQCS_HTYPE_GATE_MAP = 108,
  /// Indicates that the given handle belongs to a frontend plugin process
  /// configuration object.
  ///
  /// This means that the handle supports the `handle`, `pcfg`, and `xcfg`
  /// interfaces.
  DQCS_HTYPE_FRONT_PROCESS_CONFIG = 200,
  /// Indicates that the given handle belongs to an operator plugin process
  /// configuration object.
  ///
  /// This means that the handle supports the `handle`, `pcfg`, and `xcfg`
  /// interfaces.
  DQCS_HTYPE_OPER_PROCESS_CONFIG = 201,
  /// Indicates that the given handle belongs to a backend plugin process
  /// configuration object.
  ///
  /// This means that the handle supports the `handle`, `pcfg`, and `xcfg`
  /// interfaces.
  DQCS_HTYPE_BACK_PROCESS_CONFIG = 203,
  /// Indicates that the given handle belongs to a frontend plugin thread
  /// configuration object.
  ///
  /// This means that the handle supports the `handle`, `tcfg`, and `xcfg`
  /// interfaces.
  DQCS_HTYPE_FRONT_THREAD_CONFIG = 204,
  /// Indicates that the given handle belongs to an operator plugin thread
  /// configuration object.
  ///
  /// This means that the handle supports the `handle`, `tcfg`, and `xcfg`
  /// interfaces.
  DQCS_HTYPE_OPER_THREAD_CONFIG = 205,
  /// Indicates that the given handle belongs to a backend plugin thread
  /// configuration object.
  ///
  /// This means that the handle supports the `handle`, `tcfg`, and `xcfg`
  /// interfaces.
  DQCS_HTYPE_BACK_THREAD_CONFIG = 206,
  /// Indicates that the given handle belongs to a simulator configuration
  /// object.
  ///
  /// This means that the handle supports the `handle` and `scfg` interfaces.
  DQCS_HTYPE_SIM_CONFIG = 207,
  /// Indicates that the given handle belongs to a simulator instance.
  ///
  /// This means that the handle supports the `handle` and `sim` interfaces.
  DQCS_HTYPE_SIM = 208,
  /// Indicates that the given handle belongs to a frontend plugin
  /// definition object.
  ///
  /// This means that the handle supports the `handle` and `pdef` interfaces.
  DQCS_HTYPE_FRONT_DEF = 300,
  /// Indicates that the given handle belongs to an operator plugin
  /// definition object.
  ///
  /// This means that the handle supports the `handle` and `pdef` interfaces.
  DQCS_HTYPE_OPER_DEF = 301,
  /// Indicates that the given handle belongs to a backend plugin
  /// definition object.
  ///
  /// This means that the handle supports the `handle` and `pdef` interfaces.
  DQCS_HTYPE_BACK_DEF = 302,
  /// Indicates that the given handle belongs to a plugin thread join handle.
  ///
  /// This means that the handle supports the `handle` and `pjoin`
  /// interfaces.
  DQCS_HTYPE_PLUGIN_JOIN = 303,
};

/// Enumeration of loglevels and logging modes.
enum class dqcs_loglevel_t {
  /// Invalid loglevel. Used to indicate failure of an API that returns a
  /// loglevel.
  DQCS_LOG_INVALID = -1,
  /// Turns logging off.
  DQCS_LOG_OFF = 0,
  /// This loglevel is to be used for reporting a fatal error, resulting from
  /// the owner of the logger getting into an illegal state from which it
  /// cannot recover. Such problems are also reported to the API caller via
  /// Result::Err if applicable.
  DQCS_LOG_FATAL = 1,
  /// This loglevel is to be used for reporting or propagating a non-fatal
  /// error caused by the API caller doing something wrong. Such problems are
  /// also reported to the API caller via Result::Err if applicable.
  DQCS_LOG_ERROR = 2,
  /// This loglevel is to be used for reporting that a called API/function is
  /// telling us we did something wrong (that we weren't expecting), but we
  /// can recover. For instance, for a failed connection attempt to something
  /// that really should not be failing, we can still retry (and eventually
  /// report critical or error if a retry counter overflows). Since we're
  /// still trying to rectify things at this point, such problems are NOT
  /// reported to the API/function caller via Result::Err.
  DQCS_LOG_WARN = 3,
  /// This loglevel is to be used for reporting information specifically
  /// requested by the user/API caller, such as the result of an API function
  /// requested through the command line, or an explicitly captured
  /// stdout/stderr stream.
  DQCS_LOG_NOTE = 4,
  /// This loglevel is to be used for reporting information NOT specifically
  /// requested by the user/API caller, such as a plugin starting up or
  /// shutting down.
  DQCS_LOG_INFO = 5,
  /// This loglevel is to be used for reporting debugging information useful
  /// for debugging the user of the API provided by the logged instance.
  DQCS_LOG_DEBUG = 6,
  /// This loglevel is to be used for reporting debugging information useful
  /// for debugging the internals of the logged instance. Such messages would
  /// normally only be generated by debug builds, to prevent them from
  /// impacting performance under normal circumstances.
  DQCS_LOG_TRACE = 7,
  /// This is intended to be used when configuring the stdout/stderr capture
  /// mode for a plugin process. Selecting it will prevent the stream from
  /// being captured; it will just be the same stream as DQCsim's own
  /// stdout/stderr. When used as the loglevel for a message, the message
  /// itself is sent to stderr instead of passing into DQCsim's log system.
  /// Using this for loglevel filters leads to undefined behavior.
  DQCS_LOG_PASS = 8,
};

/// Qubit measurement value.
enum class dqcs_measurement_t {
  /// Error value used to indicate that something went wrong.
  DQCS_MEAS_INVALID = -1,
  /// Indicates that the qubit was measured to be zero.
  DQCS_MEAS_ZERO = 0,
  /// Indicates that the qubit was measured to be one.
  DQCS_MEAS_ONE = 1,
  /// Indicates that the measurement value is unknown for whatever reason.
  DQCS_MEAS_UNDEFINED = 2,
};

/// Reproduction file path style.
enum class dqcs_path_style_t {
  /// Error value used to indicate that something went wrong.
  DQCS_PATH_STYLE_INVALID = -1,
  /// Specifies that paths should be saved the same way they were specified
  /// on the command line.
  DQCS_PATH_STYLE_KEEP = 0,
  /// Specifies that all paths should be saved relative to DQCsim's working
  /// directory.
  DQCS_PATH_STYLE_RELATIVE = 1,
  /// Specifies that all paths should be saved canonically, i.e. relative to
  /// the root directory.
  DQCS_PATH_STYLE_ABSOLUTE = 2,
};

/// Enumeration of the three types of plugins.
enum class dqcs_plugin_type_t {
  /// Invalid plugin type. Used to indicate failure of an API that returns
  /// a plugin type.
  DQCS_PTYPE_INVALID = -1,
  /// Frontend plugin.
  DQCS_PTYPE_FRONT = 0,
  /// Operator plugin.
  DQCS_PTYPE_OPER = 1,
  /// Backend plugin.
  DQCS_PTYPE_BACK = 2,
};

/// Enumeration of gates defined by DQCsim.
enum class dqcs_predefined_gate_t {
  /// Invalid gate. Used as an error return value.
  DQCS_GATE_INVALID = 0,
  /// The identity gate for a single qubit.
  ///
  /// \f[
  /// I = \sigma_0 = \begin{bmatrix}
  /// 1 & 0 \\
  /// 0 & 1
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_PAULI_I = 100,
  /// The Pauli X matrix.
  ///
  /// \f[
  /// X = \sigma_1 = \begin{bmatrix}
  /// 0 & 1 \\
  /// 1 & 0
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_PAULI_X = 101,
  /// The Pauli Y matrix.
  ///
  /// \f[
  /// Y = \sigma_2 = \begin{bmatrix}
  /// 0 & -i \\
  /// i & 0
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_PAULI_Y = 102,
  /// The Pauli Z matrix.
  ///
  /// \f[
  /// Z = \sigma_3 = \begin{bmatrix}
  /// 1 & 0 \\
  /// 0 & -1
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_PAULI_Z = 103,
  /// The hadamard gate matrix. That is, a 180-degree Y rotation, followed by
  /// a 90-degree X rotation.
  ///
  /// \f[
  /// H = \frac{1}{\sqrt{2}} \begin{bmatrix}
  /// 1 & 1 \\
  /// 1 & -1
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_H = 104,
  /// The S matrix, also known as a 90 degree Z rotation.
  ///
  /// \f[
  /// S = \begin{bmatrix}
  /// 1 & 0 \\
  /// 0 & i
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_S = 105,
  /// The S-dagger matrix, also known as a negative 90 degree Z rotation.
  ///
  /// \f[
  /// S^\dagger = \begin{bmatrix}
  /// 1 & 0 \\
  /// 0 & -i
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_S_DAG = 106,
  /// The T matrix, also known as a 45 degree Z rotation.
  ///
  /// \f[
  /// T = \begin{bmatrix}
  /// 1 & 0 \\
  /// 0 & e^{i\frac{\pi}{4}}
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_T = 107,
  /// The T-dagger matrix, also known as a negative 45 degree Z rotation.
  ///
  /// \f[
  /// T^\dagger = \begin{bmatrix}
  /// 1 & 0 \\
  /// 0 & e^{-i\frac{\pi}{4}}
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_T_DAG = 108,
  /// Rx(90°) gate.
  ///
  /// \f[
  /// R_x\left(\frac{\pi}{2}\right) = \frac{1}{\sqrt{2}} \begin{bmatrix}
  /// 1 & -i \\
  /// -i & 1
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_RX_90 = 109,
  /// Rx(-90°) gate.
  ///
  /// \f[
  /// R_x\left(-\frac{\pi}{2}\right) = \frac{1}{\sqrt{2}} \begin{bmatrix}
  /// 1 & i \\
  /// i & 1
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_RX_M90 = 110,
  /// Rx(180°) gate.
  ///
  /// \f[
  /// R_x(\pi) = \begin{bmatrix}
  /// 0 & -i \\
  /// -i & 0
  /// \end{bmatrix}
  /// \f]
  ///
  /// This matrix is equivalent to the Pauli X gate, but differs in global
  /// phase. Note that this difference is significant when it is used as a
  /// submatrix for a controlled gate.
  DQCS_GATE_RX_180 = 111,
  /// Ry(90°) gate.
  ///
  /// \f[
  /// R_y\left(\frac{\pi}{2}\right) = \frac{1}{\sqrt{2}} \begin{bmatrix}
  /// 1 & -1 \\
  /// 1 & 1
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_RY_90 = 112,
  /// Ry(-90°) gate.
  ///
  /// \f[
  /// R_y\left(\frac{\pi}{2}\right) = \frac{1}{\sqrt{2}} \begin{bmatrix}
  /// 1 & 1 \\
  /// -1 & 1
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_RY_M90 = 113,
  /// Ry(180°) gate.
  ///
  /// \f[
  /// R_y(\pi) = \begin{bmatrix}
  /// 0 & -1 \\
  /// 1 & 0
  /// \end{bmatrix}
  /// \f]
  ///
  /// This matrix is equivalent to the Pauli Y gate, but differs in global
  /// phase. Note that this difference is significant when it is used as a
  /// submatrix for a controlled gate.
  DQCS_GATE_RY_180 = 114,
  /// Rz(90°) gate.
  ///
  /// \f[
  /// R_z\left(\frac{\pi}{2}\right) = \frac{1}{\sqrt{2}} \begin{bmatrix}
  /// 1-i & 0 \\
  /// 0 & 1+i
  /// \end{bmatrix}
  /// \f]
  ///
  /// This matrix is equivalent to the S gate, but differs in global phase.
  /// Note that this difference is significant when it is used as a submatrix
  /// for a controlled gate.
  DQCS_GATE_RZ_90 = 115,
  /// Rz(-90°) gate.
  ///
  /// \f[
  /// R_z\left(-\frac{\pi}{2}\right) = \frac{1}{\sqrt{2}} \begin{bmatrix}
  /// 1+i & 0 \\
  /// 0 & 1-i
  /// \end{bmatrix}
  /// \f]
  ///
  /// This matrix is equivalent to the S-dagger gate, but differs in global
  /// phase. Note that this difference is significant when it is used as a
  /// submatrix for a controlled gate.
  DQCS_GATE_RZ_M90 = 116,
  /// Rz(180°) gate.
  ///
  /// \f[
  /// R_z(\pi) = \begin{bmatrix}
  /// -i & 0 \\
  /// 0 & i
  /// \end{bmatrix}
  /// \f]
  ///
  /// This matrix is equivalent to the Pauli Z gate, but differs in global
  /// phase. Note that this difference is significant when it is used as a
  /// submatrix for a controlled gate.
  DQCS_GATE_RZ_180 = 117,
  /// The matrix for an arbitrary X rotation.
  ///
  /// \f[
  /// R_x(\theta) = \begin{bmatrix}
  /// \cos{\frac{\theta}{2}} & -i\sin{\frac{\theta}{2}} \\
  /// -i\sin{\frac{\theta}{2}} & \cos{\frac{\theta}{2}}
  /// \end{bmatrix}
  /// \f]
  ///
  /// θ is specified or returned through the first binary string argument
  /// of the parameterization ArbData object. It is represented as a
  /// little-endian double floating point value, specified in radians.
  DQCS_GATE_RX = 150,
  /// The matrix for an arbitrary Y rotation.
  ///
  /// \f[
  /// R_y(\theta) = \begin{bmatrix}
  /// \cos{\frac{\theta}{2}} & -\sin{\frac{\theta}{2}} \\
  /// \sin{\frac{\theta}{2}} & \cos{\frac{\theta}{2}}
  /// \end{bmatrix}
  /// \f]
  ///
  /// θ is specified or returned through the first binary string argument
  /// of the parameterization ArbData object. It is represented as a
  /// little-endian double floating point value, specified in radians.
  DQCS_GATE_RY = 151,
  /// The matrix for an arbitrary Z rotation.
  ///
  /// \f[
  /// R_z(\theta) = \begin{bmatrix}
  /// e^{-i\frac{\theta}{2}} & 0 \\
  /// 0 & e^{i\frac{\theta}{2}}
  /// \end{bmatrix}
  /// \f]
  ///
  /// θ is specified or returned through the first binary string argument
  /// of the parameterization ArbData object. It is represented as a
  /// little-endian double floating point value, specified in radians.
  DQCS_GATE_RZ = 152,
  /// The matrix for a Z rotation with angle π/2^k.
  ///
  /// \f[
  /// \textit{PhaseK}(k) = \textit{Phase}\left(\frac{\pi}{2^k}\right) = \begin{bmatrix}
  /// 1 & 0 \\
  /// 0 & e^{i\pi / 2^k}
  /// \end{bmatrix}
  /// \f]
  ///
  /// k is specified or returned through the first binary string argument
  /// of the parameterization ArbData object. It is represented as a
  /// little-endian unsigned 64-bit integer.
  DQCS_GATE_PHASE_K = 153,
  /// The matrix for an arbitrary Z rotation.
  ///
  /// \f[
  /// \textit{Phase}(\theta) = \begin{bmatrix}
  /// 1 & 0 \\
  /// 0 & e^{i\theta}
  /// \end{bmatrix}
  /// \f]
  ///
  /// θ is specified or returned through the first binary string argument
  /// of the parameterization ArbData object. It is represented as a
  /// little-endian double floating point value, specified in radians.
  ///
  /// This matrix is equivalent to the Rz gate, but differs in global phase.
  /// Note that this difference is significant when it is used as a submatrix
  /// for a controlled gate. Specifically, controlled phase gates use the
  /// phase as specified by this gate, whereas Rz follows the usual algebraic
  /// notation.
  DQCS_GATE_PHASE = 154,
  /// Any single-qubit unitary gate, parameterized as a full unitary matrix.
  ///
  /// The full matrix is specified or returned through the first binary string
  /// argument of the parameterization ArbData object. It is represented as an
  /// array of little-endian double floating point values, structured as
  /// real/imag pairs, with the pairs in row-major order.
  DQCS_GATE_U1 = 190,
  /// Arbitrary rotation matrix.
  ///
  /// \f[
  /// R(\theta, \phi, \lambda) = \begin{bmatrix}
  /// \cos{\frac{\theta}{2}} & -\sin{\frac{\theta}{2}} e^{i\lambda} \\
  /// \sin{\frac{\theta}{2}} e^{i\phi} & \cos{\frac{\theta}{2}} e^{i\phi + i\lambda}
  /// \end{bmatrix}
  /// \f]
  ///
  /// This is equivalent to the following:
  ///
  /// \f[
  /// R(\theta, \phi, \lambda) = \textit{Phase}(\phi) \cdot R_y(\theta) \cdot \textit{Phase}(\lambda)
  /// \f]
  ///
  /// The rotation order and phase is taken from Qiskit's U3 gate. Ignoring
  /// global phase, any unitary single-qubit gate can be represented with this
  /// notation.
  ///
  /// θ, φ, and λ are specified or returned through the first three binary
  /// string arguments of the parameterization ArbData object. They are
  /// represented as little-endian double floating point values, specified in
  /// radians.
  DQCS_GATE_R = 191,
  /// The swap gate matrix.
  ///
  /// \f[
  /// \textit{SWAP} = \begin{bmatrix}
  /// 1 & 0 & 0 & 0 \\
  /// 0 & 0 & 1 & 0 \\
  /// 0 & 1 & 0 & 0 \\
  /// 0 & 0 & 0 & 1
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_SWAP = 200,
  /// The square-root of a swap gate matrix.
  ///
  /// \f[
  /// \sqrt{\textit{SWAP}} = \begin{bmatrix}
  /// 1 & 0 & 0 & 0 \\
  /// 0 & \frac{i+1}{2} & \frac{i-1}{2} & 0 \\
  /// 0 & \frac{i-1}{2} & \frac{i+1}{2} & 0 \\
  /// 0 & 0 & 0 & 1
  /// \end{bmatrix}
  /// \f]
  DQCS_GATE_SQRT_SWAP = 201,
  /// Any two-qubit unitary gate, parameterized as a full unitary matrix.
  ///
  /// The full matrix is specified or returned through the first binary string
  /// argument of the parameterization ArbData object. It is represented as an
  /// array of little-endian double floating point values, structured as
  /// real/imag pairs, with the pairs in row-major order.
  DQCS_GATE_U2 = 290,
  /// Any three-qubit unitary gate, parameterized as a full unitary matrix.
  ///
  /// The full matrix is specified or returned through the first binary string
  /// argument of the parameterization ArbData object. It is represented as an
  /// array of little-endian double floating point values, structured as
  /// real/imag pairs, with the pairs in row-major order.
  DQCS_GATE_U3 = 390,
};

/// Default return type for functions that don't need to return anything.
enum class dqcs_return_t {
  /// The function has failed. More information may be obtained through
  /// `dqcsim_explain()`.
  DQCS_FAILURE = -1,
  /// The function did what it was supposed to.
  DQCS_SUCCESS = 0,
};

/// Type for a handle.
///
/// Handles are like pointers into DQCsim's internal structures: all API calls
/// use these to refer to objects. Besides the object, they contain type
/// information. This type can be retrieved using `dqcs_handle_type()`.
///
/// Handles are always positive integers, counting upwards from 1 upon
/// allocation, and they are not reused even after being deleted. Thus, every
/// subsequent object allocation returns a handle one greater than the
/// previous. Note however that DQCsim may allocate objects as well without
/// the user specifically requesting this, so external code should generally
/// *not* rely on this behavior unless otherwise noted. The value zero is
/// reserved for invalid references or error propagation.
///
/// Note that the scope for handles is thread-local. That is, data referenced
/// by a handle cannot be shared or moved between threads.
///
/// The value zero is reserved for invalid references or error propagation.
using dqcs_handle_t = unsigned long long;

/// Type for a qubit reference.
///
/// Qubit references are exchanged between the frontend, operator, and backend
/// plugins to indicate which qubits a gate operates on. Note that this makes
/// them fundamentally different from handles, which are thread-local.
///
/// Qubit references are always positive integers, counting upwards from 1 upon
/// allocation, and they are not reused even after the qubit is deallocated.
/// Thus, every subsequent allocation returns a qubit reference one greater
/// than the previous. This is guaranteed behavior that external code can rely
/// upon. The value zero is reserved for invalid references or error
/// propagation.
using dqcs_qubit_t = unsigned long long;

/// Type for a plugin state.
///
/// This is an opaque type that is passed along to plugin implementation
/// callback functions, which those callbacks can then use to interact with the
/// plugin instance. User code shall not create or modify values of this type,
/// and shall only use the values when calling `dqcs_plugin_*` functions.
using dqcs_plugin_state_t = void*;

/// Type for a simulation cycle timestamp.
///
/// Timestamps count upward from zero. The type is signed to allow usage of -1
/// for errors, and to allow numerical differences to be represented.
using dqcs_cycle_t = long long;

extern "C" {

/// Copies the data from one object to another.
dqcs_return_t dqcs_arb_assign(dqcs_handle_t dest, dqcs_handle_t src);

/// Returns the JSON/CBOR object of an `ArbData` object in the form of a CBOR
/// object.
///
/// If the actual size of the object differs from the specified object size,
/// this function will copy the minimum of the actual and specified sizes
/// number of bytes, and return what the actual size was.
///
/// If the specified object size is zero, `obj` is allowed to be `NULL`. You
/// can use this to query the size before allocating an object.
///
/// This function returns -1 on failure.
ssize_t dqcs_arb_cbor_get(dqcs_handle_t arb, void *obj, size_t obj_size);

/// Sets the JSON/CBOR object of an `ArbData` object by means of a CBOR object.
dqcs_return_t dqcs_arb_cbor_set(dqcs_handle_t arb, const void *obj, size_t obj_size);

/// Clears the unstructured argument list.
dqcs_return_t dqcs_arb_clear(dqcs_handle_t arb);

/// Returns the unstructured string argument at the specified index.
///
/// If the actual size of the object differs from the specified object size,
/// this function will copy the minimum of the actual and specified sizes
/// number of bytes, and return what the actual size was.
///
/// If the specified object size is zero, `obj` is allowed to be `NULL`. You
/// can use this to determine the size of the argument prior to actually
/// reading it, so you can allocate the right buffer size first.
///
/// This function returns -1 on failure.
ssize_t dqcs_arb_get_raw(dqcs_handle_t arb, ssize_t index, void *obj, size_t obj_size);

/// Returns the size in bytes of the unstructured string argument at the
/// specified index.
///
/// Returns -1 when the function fails.
ssize_t dqcs_arb_get_size(dqcs_handle_t arb, ssize_t index);

/// Returns the unstructured string argument at the specified index.
///
/// On success, this **returns a newly allocated string containing the JSON
/// string. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure, this returns `NULL`.
char *dqcs_arb_get_str(dqcs_handle_t arb, ssize_t index);

/// Inserts an unstructured raw argument into the list at the specified
/// index.
dqcs_return_t dqcs_arb_insert_raw(dqcs_handle_t arb,
                                  ssize_t index,
                                  const void *obj,
                                  size_t obj_size);

/// Inserts an unstructured string argument into the list at the specified
/// index.
dqcs_return_t dqcs_arb_insert_str(dqcs_handle_t arb, ssize_t index, const char *s);

/// Returns the JSON/CBOR object of an `ArbData` object in the form of a JSON
/// string.
///
/// On success, this **returns a newly allocated string containing the JSON
/// string. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure, this returns `NULL`.
char *dqcs_arb_json_get(dqcs_handle_t arb);

/// Sets the JSON/CBOR object of an `ArbData` object by means of a JSON string.
dqcs_return_t dqcs_arb_json_set(dqcs_handle_t arb, const char *json);

/// Returns the number of unstructured arguments, or -1 to indicate failure.
ssize_t dqcs_arb_len(dqcs_handle_t arb);

/// Creates a new `ArbData` object.
///
/// Returns the handle of the newly created `ArbData`. The `ArbData` is
/// initialized with JSON object `{}` and an empty binary argument list.
///
/// `ArbData` objects support the `handle` and `arb` APIs.
dqcs_handle_t dqcs_arb_new();

/// Pops an unstructured argument from the back of the list without returning
/// it.
dqcs_return_t dqcs_arb_pop(dqcs_handle_t arb);

/// Pops an unstructured raw argument from the back of the list.
///
/// If the actual size of the object differs from the specified object size,
/// this function will copy the minimum of the actual and specified sizes
/// number of bytes, and return what the actual size was.
///
/// If the specified object size is zero, `obj` is allowed to be `NULL`. You
/// can use this if you don't need the contents of the argument and just want
/// to delete it.
///
/// Since this function removes the returned element, data will be lost if the
/// specified size is smaller than the actual size. To avoid this, first use
/// `dqcs_arb_get_size(handle, -1)` to query the size.
///
/// This function returns -1 on failure. If this is due to a `NULL` buffer
/// being passed, the data that was popped is lost.
ssize_t dqcs_arb_pop_raw(dqcs_handle_t arb, void *obj, size_t obj_size);

/// Pops an unstructured string argument from the back of the list.
///
/// On success, this **returns a newly allocated string containing the JSON
/// string. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure, this returns `NULL`. If the failure is due to the
/// conversion from binary object to C string (i.e., embedded nulls), the
/// data is still popped and is thus lost.
char *dqcs_arb_pop_str(dqcs_handle_t arb);

/// Pushes an unstructured raw argument to the back of the list.
dqcs_return_t dqcs_arb_push_raw(dqcs_handle_t arb, const void *obj, size_t obj_size);

/// Pushes an unstructured string argument to the back of the list.
dqcs_return_t dqcs_arb_push_str(dqcs_handle_t arb, const char *s);

/// Removes the specified unstructured string argument from the list.
dqcs_return_t dqcs_arb_remove(dqcs_handle_t arb, ssize_t index);

/// Replaces the unstructured argument at the specified index with the
/// specified raw object.
dqcs_return_t dqcs_arb_set_raw(dqcs_handle_t arb, ssize_t index, const void *obj, size_t obj_size);

/// Replaces the unstructured argument at the specified index with the
/// specified string.
dqcs_return_t dqcs_arb_set_str(dqcs_handle_t arb, ssize_t index, const char *s);

/// Compares the interface ID of an `ArbCmd` with the given string.
///
/// Returns -1 for failure, 0 for no match, or 1 for a match.
dqcs_bool_return_t dqcs_cmd_iface_cmp(dqcs_handle_t cmd, const char *iface);

/// Returns the interface ID of an `ArbCmd`.
///
/// On success, this **returns a newly allocated string containing the JSON
/// string. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure, this returns `NULL`.
char *dqcs_cmd_iface_get(dqcs_handle_t cmd);

/// Creates a new `ArbCmd` object.
///
/// Returns the handle of the newly created `ArbCmd`. The `ArbCmd` is
/// initialized with the given interface and operation IDs, JSON object `{}`,
/// and an empty binary argument list. Upon failure, returns 0.
///
/// `ArbCmd` objects support the `handle`, `arb`, and `cmd` interfaces.
dqcs_handle_t dqcs_cmd_new(const char *iface, const char *oper);

/// Compares the operation ID of an `ArbCmd` with the given string.
///
/// Returns -1 for failure, 0 for no match, or 1 for a match.
dqcs_bool_return_t dqcs_cmd_oper_cmp(dqcs_handle_t cmd, const char *oper);

/// Returns the operation ID of an `ArbCmd`.
///
/// On success, this **returns a newly allocated string containing the JSON
/// string. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure, this returns `NULL`.
char *dqcs_cmd_oper_get(dqcs_handle_t cmd);

/// Returns the number of `ArbCmd` objects in the given `ArbCmd` queue.
///
/// This function returns -1 to indicate failure.
ssize_t dqcs_cq_len(dqcs_handle_t cq);

/// Creates a new `ArbCmd` queue object.
///
/// Returns the handle of the newly created `ArbCmd` queue. The queue is
/// initially empty. Queues implement a "first-in, first-out" model.
///
/// `ArbCmd` queue objects support the `handle`, `arb`, `cmd`, and `cq` APIs.
///
/// The `arb` and `cmd` APIs refer to the `ArbCmd` at the front of the queue.
/// Use `dqcs_cq_next()` to remove the front entry, allowing access to the next
/// command.
dqcs_handle_t dqcs_cq_new();

/// Advances an `ArbCmd` queue to the next command.
///
/// Use the `dqcs_arb_*` and `dqcs_cmd_*` interfaces to read out the command
/// before calling this function.
///
/// To iterate over a queue in C, use the following snippit:
///
/// ```C
/// for (; dqcs_cq_len(queue) > 0; dqcs_cq_next(queue)) {
///     dqcs_cmd_...(queue, ...)
///     dqcs_arb_...(queue, ...)
/// }
/// ```
dqcs_return_t dqcs_cq_next(dqcs_handle_t cq);

/// Pushes an `ArbCmd` object into the given `ArbCmd` queue.
///
/// This function returns -1 to indicate failure. The `ArbCmd` object specified
/// by `cmd` is moved into the queue. That is, the handle is consumed if and
/// only if the function succeeds.
dqcs_return_t dqcs_cq_push(dqcs_handle_t cq, dqcs_handle_t cmd);

/// Returns a pointer to the latest error message.
///
/// Call this to get extra information when another function returns a failure
/// code. The returned pointer is temporary and therefore should **NOT** be
/// `free()`d. It will become invalid when a new error occurs.
const char *dqcs_error_get();

/// Sets the latest error message string.
///
/// This must be called by callback functions when an error occurs within the
/// callback, otherwise the upstream result for `dqcs_error_get()` will be
/// undefined.
///
/// If `msg` is set to `NULL`, the error string is cleared instead.
void dqcs_error_set(const char *msg);

/// Returns a handle to a new qubit reference set containing the qubits
/// that control this gate.
dqcs_handle_t dqcs_gate_controls(dqcs_handle_t gate);

/// Utility function that expands a gate matrix to account for all control
/// qubits.
///>
///> This function borrows a handle to any gate with a matrix, and returns an
///> equivalent copy of said gate with any control qubits in the `controls` set
///> moved to the `targets` set. The associated gate matrix is extended
///> accordingly. The control qubits are added at the front of the `targets`
///> set in the same order they appeared in the `controls` qubit set.
///>
///> This function returns a new gate handle with the modified gate, or a copy
///> of the input gate if the matrix could not be reduced. If the input gate
///> does not have a matrix (measurement gate, or custom gate without matrix) an
///> error is returned instead.
dqcs_handle_t dqcs_gate_expand_control(dqcs_handle_t gate);

/// Returns whether the specified gate has control qubits.
dqcs_bool_return_t dqcs_gate_has_controls(dqcs_handle_t gate);

/// Returns whether a unitary matrix is associated with this gate.
dqcs_bool_return_t dqcs_gate_has_matrix(dqcs_handle_t gate);

/// Returns whether the specified gate measures any qubits.
dqcs_bool_return_t dqcs_gate_has_measures(dqcs_handle_t gate);

/// Returns whether the specified gate has a name.
dqcs_bool_return_t dqcs_gate_has_name(dqcs_handle_t gate);

/// Returns whether the specified gate has target qubits.
dqcs_bool_return_t dqcs_gate_has_targets(dqcs_handle_t gate);

/// Returns a copy of the unitary matrix associated with this gate, if one
/// exists.
///
/// If this function succeeds, a new matrix handle is returned. If it fails,
/// 0 is returned.
dqcs_handle_t dqcs_gate_matrix(dqcs_handle_t gate);

/// Returns a handle to a new qubit reference set containing the qubits
/// measured by this gate.
dqcs_handle_t dqcs_gate_measures(dqcs_handle_t gate);

/// Returns the name of a custom gate.
///
/// This function fails if the gate is not a custom gate. Query
/// `dqcs_gate_has_name()` to disambiguate between a non-custom gate and a
/// different error.
///
/// On success, this **returns a newly allocated string containing the gate
/// name. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure, this returns `NULL`.
char *dqcs_gate_name(dqcs_handle_t gate);

/// Constructs a new custom gate.
///
/// The functionality of custom gates is not specified by DQCsim. Instead, this
/// is left up to the plugins. Of course, for this to work, plugins that are
/// connected to each other must agree on the format used.
///
/// `name` specifies the name of the gate. The name is used to indicate which
/// custom operation is to be applied.
///
/// `targets` optionally specifies the set of target qubits. You may pass 0 or
/// an empty qubit set if you don't need target qubits.
///
/// `controls` optionally specifies the set of control qubits. You may pass 0
/// or an empty qubit set if you don't need control qubits.
///
/// `measures` optionally specifies the set of measured qubits. You may pass 0
/// or an empty qubit set if no qubits are measured. Note that the upstream
/// plugin expects exactly one measurement result for each qubit specified in
/// this set; anything else results in a warning and the measurement result
/// being set to undefined.
///
/// `matrix` optionally specifies a handle to an appropriately sized matrix
/// for the `targets` qubit set.
///
/// In addition to the above data, gate objects implement the `arb` interface
/// to allow user-specified classical information to be attached.
///
/// This function returns the handle to the gate, or 0 to indicate failure.
/// The specified qubit sets are consumed/deleted by this function if and only
/// if it succeeds.
dqcs_handle_t dqcs_gate_new_custom(const char *name,
                                   dqcs_handle_t targets,
                                   dqcs_handle_t controls,
                                   dqcs_handle_t measures,
                                   dqcs_handle_t matrix);

/// Constructs a new measurement gate.
///
/// `measures` must be a handle to a qubit set. `matrix` is an optional matrix
/// handle signifying the measurement basis. If zero, the Z basis is used.
/// Otherwise, it must be a handle to a unitary 2x2 matrix, and the semantics
/// of the measurement are as follows:
///
///  - apply the hermetian of the matrix to each qubit
///  - measure each qubit in the Z basis
///  - apply the matrix to each qubit
///
/// This function returns the handle to the gate, or 0 to indicate failure.
/// The `measures` qubit set and `matrix` handle are consumed/deleted by this
/// function if and only if it succeeds.
dqcs_handle_t dqcs_gate_new_measurement(dqcs_handle_t measures, dqcs_handle_t matrix);

/// Constructs a new predefined unitary gate.
///
/// `gate_type` specifies which kind of gate should be constructed.
///
/// `targets` must be a handle to a non-empty qubit set, containing at least
/// as many qubits as needed for the specified gate type. If more qubits are
/// specified, the rightmost qubits become the targets, and the remaining
/// qubits become control qubits to make a controlled gate.
///
/// `param_data` takes an optional `ArbData` object used to parameterize the
/// gate if necessary. If not specified, an empty object is used. Some of the
/// gate types are parameterized, and use values from this `ArbData` as
/// defined in the docs for `dqcs_predefined_gate_t`. Anything remaining in
/// the `ArbData` afterwards is placed in the gate object.
///
/// This function returns the handle to the gate, or 0 to indicate failure.
/// The qubit set and parameterization data (if specified) are consumed/deleted
/// by this function if and only if it succeeds.
dqcs_handle_t dqcs_gate_new_predef(dqcs_predefined_gate_t gate_type,
                                   dqcs_handle_t qubits,
                                   dqcs_handle_t param_data);

/// Constructs a new predefined unitary one-qubit gate.
///
/// This function is simply a shorthand for `dqcs_gate_new_predef()` with
/// one qubit in the `qubits` set, to make constructing one-qubit gates more
/// ergonomic. Refer to its documentation for more information.
dqcs_handle_t dqcs_gate_new_predef_one(dqcs_predefined_gate_t gate_type,
                                       dqcs_qubit_t qa,
                                       dqcs_handle_t param_data);

/// Constructs a new predefined unitary three-qubit gate.
///
/// This function is simply a shorthand for `dqcs_gate_new_predef()` with
/// three qubit in the `qubits` set, to make constructing three-qubit gates
/// more ergonomic. Refer to its documentation for more information.
dqcs_handle_t dqcs_gate_new_predef_three(dqcs_predefined_gate_t gate_type,
                                         dqcs_qubit_t qa,
                                         dqcs_qubit_t qb,
                                         dqcs_qubit_t qc,
                                         dqcs_handle_t param_data);

/// Constructs a new predefined unitary two-qubit gate.
///
/// This function is simply a shorthand for `dqcs_gate_new_predef()` with
/// two qubit in the `qubits` set, to make constructing two-qubit gates more
/// ergonomic. Refer to its documentation for more information.
dqcs_handle_t dqcs_gate_new_predef_two(dqcs_predefined_gate_t gate_type,
                                       dqcs_qubit_t qa,
                                       dqcs_qubit_t qb,
                                       dqcs_handle_t param_data);

/// Constructs a new prep gate.
///
/// `targets` must be a handle to a qubit set. `matrix` is an optional matrix
/// handle signifying the state that the qubits are initialized to. If zero,
/// the qubits are initialized to |0>. Otherwise, it must be a handle to a
/// unitary 2x2 matrix, and the semantics are as follows:
///
///  - initialize each qubit to |0>
///  - apply the matrix to each qubit
///
/// This function returns the handle to the gate, or 0 to indicate failure.
/// The `targets` qubit set and `matrix` handle are consumed/deleted by this
/// function if and only if it succeeds.
dqcs_handle_t dqcs_gate_new_prep(dqcs_handle_t targets, dqcs_handle_t matrix);

/// Constructs a new unitary gate.
///
/// `targets` must be a handle to a non-empty qubit set. The qubits in this set
/// correspond with the supplied unitary matrix.
///
/// `controls` optionally specifies a set of control qubits. You may pass 0 or
/// an empty qubit set if you don't need control qubits.
///
/// `matrix` must be a handle to an appropriately sized matrix.
///
/// The supplied matrix is only applied to the target qubits if all the control
/// qubits are or will be determined to be set. For instance, to encode a
/// CCNOT/Toffoli gate, you can specify one target qubits, two control qubits,
/// and [0, 1; 1, 0] (X) for the matrix. This is equivalent to extending the
/// matrix to the full Toffoli matrix and specifying all three qubits in the
/// targets set, or the midway solution using a CNOT matrix, but these
/// solutions may be less efficient depending on whether the simulator can
/// optimize its calculations for controlled gates.
///
/// Simulators are not required to apply the (hidden) global phase component of
/// the gate matrix in the same way it is specified; that is, if the simulator
/// can optimize its calculations by altering the global phase it is allowed
/// to.
///
/// DQCsim checks whether the matrix is unitary using the equivalent of
/// `dqcs_mat_approx_unitary()` with an epsilon value of 1e-6.
///
/// This function returns the handle to the gate, or 0 to indicate failure.
/// The `targets` qubit set, (if specified) the `controls` qubit set, and the
/// matrix are consumed/deleted by this function if and only if it succeeds.
dqcs_handle_t dqcs_gate_new_unitary(dqcs_handle_t targets,
                                    dqcs_handle_t controls,
                                    dqcs_handle_t matrix);

/// Utility function that detects control qubits in the `targets` list of the
/// gate by means of the gate matrix, and reduces them into `controls` qubits.
///>
///> This function borrows a handle to any gate with a matrix, and returns an
///> equivalent copy of said gate with any control qubits in the `targets` set
///> moved to the `controls` set. The associated gate matrix is accordingly
///> reduced in size. The control qubits are added at the end of the `controls`
///> set in the same order they appeared in the `targets` qubit set.
///>
///> `epsilon` specifies the maximum element-wise deviation from the identity
///> matrix for the relevant array elements for a qubit to be considered a
///> control qubit. Note that if this is greater than zero, the resulting gate
///> may not be exactly equivalent. If `ignore_gphase` is set, any global phase
///> in the matrix is ignored, but the global phase of the non-control submatrix
///> is not changed.
///>
///> This function returns a new gate handle with the modified gate, or a copy
///> of the input gate if the matrix could not be reduced. If the input gate
///> does not have a matrix (measurement gate, or custom gate without matrix) an
///> error is returned instead.
dqcs_handle_t dqcs_gate_reduce_control(dqcs_handle_t gate, double epsilon, bool ignore_gphase);

/// Returns a handle to a new qubit reference set containing the qubits
/// targeted by this gate.
dqcs_handle_t dqcs_gate_targets(dqcs_handle_t gate);

/// Returns the gate type of the given gate.
///
/// Returns DQCS_GATE_TYPE_INVALID if the gate handle is invalid.
dqcs_gate_type_t dqcs_gate_type(dqcs_handle_t gate);

/// Adds a fully customizable gate mapping to the given gate map.
///>
///> Note that this is the only type of mapping that can handle custom/named
///> gates.
///>
///> `detector` is the detector function pointer. It is optional; if null, this
///> mapping only supports construction.
///> `detector_user_free` is an optional callback function used to free
///> `detector_user_data` when the gate map is destroyed, when this function
///> fails, or when `detector` was null.
///> `detector_user_data` is a user-specified value that is passed to the
///> `detector` callback function. It is not used by DQCsim.
///> `constructor` is the constructor function pointer. It is optional; if
///> null, this mapping only supports detection.
///> `constructor_user_free` is an optional callback function used to free
///> `constructor_user_data` when the gate map is destroyed, when this function
///> fails, or when `constructor` was null.
///> `constructor_user_data` is a user-specified value that is passed to the
///> `constructor` callback function. It is not used by DQCsim.
///>
///> If both `constructor` and `detector` are null for some reason, the
///> function is no-op (besides possibly calling the `*_free()` callbacks.
///>
///> The detector callback receives the complete gate passed to the gate map
///> for it to match as it pleases. If the gate matches, the detector function
///> must return `DQCS_TRUE`. It may  assign `qubits` to a `qbset` object
///> representing the qubit arguments (substituted with an empty set if it
///> doesn't), and may assign `param_data` to an `arb` handle with the
///> parameterization data (if it doesn't, the data from the gate is used; if
///> this was modified by the callback, the modified data is used). If the gate
///> doesn't match, it must return `DQCS_FALSE`. If an error occurs, it must
///> call `dqcs_error_set()` with the error message and return
///> `DQCS_BOOL_FAILURE`.
///>
///> The constructor callback performs the reverse operation. It receives an
///> `ArbData` handle containing the parameterization data and a qubit set, and
///> must construct a gate based on this information. If construction succeeds,
///> the constructor function must return the gate handle. If an error occurs,
///> it must call `dqcs_error_set()` with the error message and return 0.
///>
///> It is up to the user how to do the matching and constructing, but the
///> converter functions must always return the same value for the same input.
///> In other words, they must be pure functions. Otherwise, the caching
///> behavior of the `GateMap` will make the results inconsistent.
dqcs_return_t dqcs_gm_add_custom(dqcs_handle_t gm,
                                 void (*key_free)(void *key_data),
                                 void *key_data,
                                 dqcs_bool_return_t (*detector)(const void *user_data, dqcs_handle_t gate, dqcs_handle_t *qubits, dqcs_handle_t *param_data),
                                 void (*detector_user_free)(void *user_data),
                                 void *detector_user_data,
                                 dqcs_handle_t (*constructor)(const void *user_data, dqcs_handle_t qubits, dqcs_handle_t param_data),
                                 void (*constructor_user_free)(void *user_data),
                                 void *constructor_user_data);

/// Adds a custom unitary gate mapping to the given gate map.
///>
///> `gm` must be a handle to a gate map object (`dqcs_gm_new()`).
///> `key_free` is an optional callback function used to free `key_data` when
///> the gate map is destroyed, or when this function fails.
///> `key_data` is the user-specified value used to identify this mapping.
///> `detector` is the detector function pointer. It is optional; if null, this
///> mapping only supports construction.
///> `detector_user_free` is an optional callback function used to free
///> `detector_user_data` when the gate map is destroyed, when this function
///> fails, or when `detector` was null.
///> `detector_user_data` is a user-specified value that is passed to the
///> `detector` callback function. It is not used by DQCsim.
///> `constructor` is the constructor function pointer. It is optional; if
///> null, this mapping only supports detection.
///> `constructor_user_free` is an optional callback function used to free
///> `constructor_user_data` when the gate map is destroyed, when this function
///> fails, or when `constructor` was null.
///> `constructor_user_data` is a user-specified value that is passed to the
///> `constructor` callback function. It is not used by DQCsim.
///>
///> If both `constructor` and `detector` are null for some reason, the
///> function is no-op (besides possibly calling the `*_free()` callbacks.
///>
///> The detector callback receives a matrix and control qubit information for
///> the user to match. The matrix is passed through the `matrix` handle.
///> `num_controls` is passed the number of explicit control qubits that exist
///> besides the matrix (that is, if nonzero, the matrix is actually only the
///> non-controlled submatrix of the controlled gate). `param_data` is given an
///> `ArbData` handle initialized with the `ArbData` attached to the gate. If
///> the gate matches, the detector function must return `DQCS_TRUE`. In this
///> case, it can mutate the `param_data` to add the detected gate parameters.
///> If it doesn't match, it must return `DQCS_FALSE`. If an error occurs, it
///> must call `dqcs_error_set()` with the error message and return
///> `DQCS_BOOL_FAILURE`.
///>
///> The constructor callback performs the reverse operation. It receives an
///> `ArbData` handle containing the parameterization data, and must construct
///> the matrix, return the bound on the number of control qubits, and must
///> return the `ArbData` associated with the gate by mutating the `param_data`
///> handle. `num_controls` will point to a variable initialized to -1
///> representing a constraint on the number of control qubits. This works as
///> follows: if negative, any number of qubits is allowed; if zero or
///> positive, only that number is allowed. If construction succeeds, the
///> constructor function must return a handle to the constructed matrix. If
///> it fails, it must call `dqcs_error_set()` with an error message and return
///> 0.
///>
///> It is up to the user how to do the matching and constructing, but the
///> converter functions must always return the same value for the same input.
///> In other words, they must be pure functions. Otherwise, the caching
///> behavior of the `GateMap` will make the results inconsistent.
dqcs_return_t dqcs_gm_add_custom_unitary(dqcs_handle_t gm,
                                         void (*key_free)(void *key_data),
                                         void *key_data,
                                         dqcs_bool_return_t (*detector)(const void *user_data, dqcs_handle_t matrix, size_t num_controls, dqcs_handle_t *param_data),
                                         void (*detector_user_free)(void *user_data),
                                         void *detector_user_data,
                                         dqcs_handle_t (*constructor)(const void *user_data, dqcs_handle_t *param_data, intptr_t *num_controls),
                                         void (*constructor_user_free)(void *user_data),
                                         void *constructor_user_data);

/// Adds a unitary gate mapping for the given gate matrix to the given gate
/// map.
///>
///> `gm` must be a handle to a gate map object (`dqcs_gm_new()`).
///> `key_free` is an optional callback function used to free `key_data` when
///> the gate map is destroyed, or when this function fails.
///> `key_data` is the user-specified value used to identify this mapping.
///> `matrix` must be passed a handle to the matrix to detect. It is consumed
///> by this function.
///> `num_controls` specifies the number of control qubits associated with this
///> gate type. If negative, the gate can have any number of control qubits.
///> If zero or positive, the number of control qubits must be as specified.
///> `epsilon` specifies the maximum element-wise root-mean-square error
///> between the incoming matrix and the to be detected matrix that results in a
///> positive match.
///> `ignore_phase` specifies whether the aforementioned check should ignore
///> global phase or not when there are no explicit control qubits.
///>
///> The parameterization `ArbData` object returned by detection and consumed
///> by construction is mapped one-to-one to the user data of the gate in the
///> DQCsim-protocol.
dqcs_return_t dqcs_gm_add_fixed_unitary(dqcs_handle_t gm,
                                        void (*key_free)(void *key_data),
                                        void *key_data,
                                        dqcs_handle_t matrix,
                                        intptr_t num_controls,
                                        double epsilon,
                                        bool ignore_gphase);

/// Adds a measurement gate mapping to the given gate map.
///>
///> `gm` must be a handle to a gate map object (`dqcs_gm_new()`).
///> `key_free` is an optional callback function used to free `key_data` when
///> the gate map is destroyed, or when this function fails.
///> `key_data` is the user-specified value used to identify this mapping.
///> `num_measures` specifies the number of measured qubits for this gate type.
///> If negative, the gate can have any number of measured qubits. If zero or
///> positive, the number of measured qubits must be as specified.
///> `basis` optionally specifies a handle to a 2x2 matrix specifying the
///> measurement basis to be detected. If not specified, the Z basis is used.
///> The matrix is deleted by the call iff the function succeeds.
///> `epsilon` specifies the maximum RMS deviation between the specified basis
///> (if any) and the incoming basis.
///>
///> The parameterization `ArbData` object returned by detection and consumed
///> by construction is mapped one-to-one to the user data of the gate in the
///> DQCsim-protocol.
dqcs_return_t dqcs_gm_add_measure(dqcs_handle_t gm,
                                  void (*key_free)(void *user_data),
                                  void *key_data,
                                  intptr_t num_measures,
                                  dqcs_handle_t basis,
                                  double epsilon);

/// Adds a unitary gate mapping for the given DQCsim-defined gate to the
/// given gate map.
///>
///> `gm` must be a handle to a gate map object (`dqcs_gm_new()`).
///> `key_free` is an optional callback function used to free `key_data` when
///> the gate map is destroyed, or when this function fails.
///> `key_data` is the user-specified value used to identify this mapping.
///> `gate` defines which predefined gate to use. Some of the predefined gates
///> are parameterized.
///> `num_controls` specifies the number of control qubits associated with this
///> gate type. If negative, the gate can have any number of control qubits.
///> If zero or positive, the number of control qubits must be as specified.
///> `epsilon` specifies the maximum element-wise root-mean-square error
///> between the incoming matrix and the to be detected matrix that results in a
///> positive match.
///> `ignore_phase` specifies whether the aforementioned check should ignore
///> global phase or not when there are no explicit control qubits.
///>
///> For most gate types, the parameterization `ArbData` object returned by
///> detection and consumed by construction is mapped one-to-one to the user
///> data of the gate in the DQCsim-protocol. Some of the detectors however
///> detect parameterized gate matrices. These detectors prefix a fixed number
///> of binary string arguments to the `ArbData` upon detection, and pop these
///> when constructing. The specs for this can be found in the docs for
///> `dqcs_predefined_gate_t`.
dqcs_return_t dqcs_gm_add_predef_unitary(dqcs_handle_t gm,
                                         void (*key_free)(void *user_data),
                                         void *key_data,
                                         dqcs_predefined_gate_t gate,
                                         intptr_t num_controls,
                                         double epsilon,
                                         bool ignore_gphase);

/// Adds a prep gate mapping to the given gate map.
///>
///> `gm` must be a handle to a gate map object (`dqcs_gm_new()`).
///> `key_free` is an optional callback function used to free `key_data` when
///> the gate map is destroyed, or when this function fails.
///> `key_data` is the user-specified value used to identify this mapping.
///> `num_targets` specifies the number of target qubits for this gate type.
///> If negative, the gate can have any number of targets. If zero or
///> positive, the number of target qubits must be as specified.
///> `basis` optionally specifies a handle to a 2x2 matrix specifying the
///> prep basis. If not specified, the Z basis is used. The matrix is deleted
///> by the call iff the function succeeds.
///> `epsilon` specifies the maximum RMS deviation between the specified basis
///> (if any) and the incoming basis.
///>
///> The parameterization `ArbData` object returned by detection and consumed
///> by construction is mapped one-to-one to the user data of the gate in the
///> DQCsim-protocol.
dqcs_return_t dqcs_gm_add_prep(dqcs_handle_t gm,
                               void (*key_free)(void *user_data),
                               void *key_data,
                               intptr_t num_targets,
                               dqcs_handle_t basis,
                               double epsilon);

/// Uses a gate map object to construct a multi-qubit DQCsim gate from the
/// plugin's representation.
///>
///> `gm` must be a handle to a gate map object (`dqcs_mm_new()`).
///> `gate` must be a handle to a gate. The handle is borrowed; it is not
///> mutated or deleted.
///> `key_data` specifies the gate mapping key for the constructor to use. Note
///> that the *pointer* must match exactly to what was specified when the
///> mapping(s) was/were added.
///> `qubits` specifies the qubits arguments for the constructed gate. It is
///> up to the constructor function to determine how to interpret these. The
///> parameter is optional; passing 0 is equivalent to passing an empty qubit
///> set. The handle is deleted if the function succeeds.
///> `param_data` specifies the `ArbData` object used to parameterize the gate.
///> It is optional; if 0, an empty `ArbData` is automatically constructed by
///> DQCsim. The handle is deleted if the function succeeds.
///>
/// This function returns the handle to the gate, or 0 to indicate failure.
/// The qubit set and parameterization data (if specified) are consumed/deleted
/// by this function if and only if it succeeds.
dqcs_handle_t dqcs_gm_construct(dqcs_handle_t gm,
                                const void *key_data,
                                dqcs_handle_t qubits,
                                dqcs_handle_t param_data);

/// Uses a gate map object to construct a one-qubit DQCsim gate from the
/// plugin's representation.
///>
///> This function is simply a shorthand for `dqcs_gm_construct()` with
///> one qubit in the `qubits` set, to make constructing one-qubit gates more
///> ergonomic. Refer to its documentation for more information.
dqcs_handle_t dqcs_gm_construct_one(dqcs_handle_t gm,
                                    const void *key_data,
                                    dqcs_qubit_t qa,
                                    dqcs_handle_t param_data);

/// Uses a gate map object to construct a three-qubit DQCsim gate from the
/// plugin's representation.
///>
///> This function is simply a shorthand for `dqcs_gm_construct()` with
///> three qubits in the `qubits` set, to make constructing three-qubit gates
///> more ergonomic. Refer to its documentation for more information.
dqcs_handle_t dqcs_gm_construct_three(dqcs_handle_t gm,
                                      const void *key_data,
                                      dqcs_qubit_t qa,
                                      dqcs_qubit_t qb,
                                      dqcs_qubit_t qc,
                                      dqcs_handle_t param_data);

/// Uses a gate map object to construct a two-qubit DQCsim gate from the
/// plugin's representation.
///>
///> This function is simply a shorthand for `dqcs_gm_construct()` with
///> two qubits in the `qubits` set, to make constructing two-qubit gates more
///> ergonomic. Refer to its documentation for more information.
dqcs_handle_t dqcs_gm_construct_two(dqcs_handle_t gm,
                                    const void *key_data,
                                    dqcs_qubit_t qa,
                                    dqcs_qubit_t qb,
                                    dqcs_handle_t param_data);

/// Uses a gate map object to convert an incoming DQCsim gate to the plugin's
/// representation.
///>
///> `gm` must be a handle to a gate map object (`dqcs_mm_new()`).
///> `gate` must be a handle to a gate. The handle is borrowed; it is not
///> mutated or deleted.
///> `key_data` serves as an optional return value; if non-NULL and a match is
///> found, the `key_data` specified when the respective detector was added is
///> returned here as a `const void *`. If no match is found, `*key_data` is
///> not assigned.
///> `qubits` serves as an optional return value; if non-NULL and a match
///> is found, it is set to a handle to a new `QubitSet` object representing the
///> gate's qubits. Ownership of this handle is passed to the user, so it
///> is up to the user to eventually delete it. If no match is found,
///> `*qubits` is set to 0.
///> `param_data` serves as an optional return value; if non-NULL and a match
///> is found, it is set to a handle to a new `ArbData` object representing the
///> gate's parameters. Ownership of this handle is passed to the user, so it
///> is up to the user to eventually delete it. If no match is found,
///> `*param_data` is set to 0.
///>
///> This function returns `DQCS_TRUE` if a match was found, `DQCS_FALSE` if no
///> match was found, or `DQCS_BOOL_FAILURE` if an error occurs.
dqcs_bool_return_t dqcs_gm_detect(dqcs_handle_t gm,
                                  dqcs_handle_t gate,
                                  const void **key_data,
                                  dqcs_handle_t *qubits,
                                  dqcs_handle_t *param_data);

/// Constructs a new gate map.
///>
///> Returns a handle to a gate map with no mappings attached to it yet. Use
///> `dqcs_gm_add_*()` to do that. The mappings are queried in the order in
///> which they are added, so be sure to add more specific gates first. Once
///> added, use `dqcs_gm_detect()` to detect incoming DQCsim gates, and
///> `dqcs_gm_construct*()` to (re)construct gates for transmission.
///>
///> Gate maps objects retain a cache to speed up detection of similar DQCsim
///> gates: if a gate is received for the second time, the cache will hit,
///> avoiding recomputation of the detector functions. What constitutes
///> "similar gates" is defined by the two booleans passed to this function. If
///> `strip_qubit_refs` is set, all qubit references associated with the gate
///> will be invalidated (i.e., set to 0), such that for instance an X gate
///> applied to qubit 1 will be considered equal to an X gate applied to qubit
///> 2. If `strip_data` is set, the `ArbData` associated with the incoming
///> gate is removed.
///>
///> Gates are identified through user-defined `void*` keys. To do the above,
///> however, DQCsim needs to know the following things:
///>
///>  - how to delete an owned copy of a key if your semantics are that DQCsim
///>    owns it,
///>  - how to compare two keys (equality);
///>  - how to hash a key.
///>
///> The deletion function is passed when the key is passed. If the keys are
///> objects of different classes, this allows different constructors to be
///> passed here. There can only be a single comparison and hash function for
///> each gate map, though. They are passed here.
///>
///> `key_cmp` represents this comparison function. It takes two `void*` to
///> keys and must returns whether they are equal or not. If not specified,
///> the default is to compare the pointers themselves, instead of the values
///> they refer to. `key_cmp` must be a pure function, i.e., depend only on its
///> input values.
///>
///> `key_hash` represents the hashing function. It takes a `void*` key and
///> returns a 64-bit hash representative of the key. **For any pair of keys
///> for which `key_cmp` returns true, the hashes must be equal.** The default
///> behavior depends on whether `key_cmp` is defined: if it is, all keys will
///> have the same hash; if it isn't, the pointer is itself hashed. `key_hash`
///> must be a pure function, i.e., depend only on its input values.
///>
///> It is recommended to first preprocess incoming gates with
///> `dqcs_gate_reduce_control()`. In this case, controlled unitary gate
///> matrices will be reduced to their non-controlled submatrix, such that the
///> unitary gate detectors will operate on said submatrix. The predefined
///> unitary gate detectors are more-or-less based on this assumption (as there
///> are no predefined controlled matrices).
///>
///> Alternatively, you can preprocess with `dqcs_gate_expand_control()`. In
///> this case, you can use `dqcs_gm_add_fixed_unitary()` to detect the full
///> matrix in all cases, by specifying the CNOT matrix instead of an X matrix
///> with one control qubit.
///>
///> If you don't preprocess, the upstream plugin determines the
///> representation. That is, it may send a CNOT as a two-qubit gate with a
///> CNOT matrix or as a controlled X gate with a single target and single
///> control qubit. The gate map will then detect these as two different kinds
///> of gates.
dqcs_handle_t dqcs_gm_new(bool strip_qubit_refs,
                          bool strip_data,
                          bool (*key_cmp)(const void*, const void*),
                          uint64_t (*key_hash)(const void*));

/// Destroys the object associated with a handle.
///
/// Returns 0 when successful, -1 otherwise.
dqcs_return_t dqcs_handle_delete(dqcs_handle_t handle);

/// Deletes all handles for the current thread.
///
/// This can be used to clean stuff up at the end of `main()` or before an
/// `abort()` of some kind. If you don't clean up properly, you might get
/// undefined behavior or errors when DQCsim tries to do it for you.
dqcs_return_t dqcs_handle_delete_all();

/// Returns a debug dump of the object associated with the given handle.
///
/// On success, this **returns a newly allocated string containing the
/// description. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
char *dqcs_handle_dump(dqcs_handle_t handle);

/// Succeeds only if there are no live handles in the current thread.
///
/// This is intended for testing and for finding handle leaks. The error
/// message returned when handles remain contains dumps of the first 10
/// remaining handles.
dqcs_return_t dqcs_handle_leak_check();

/// Returns the type of object associated with the given handle.
dqcs_handle_type_t dqcs_handle_type(dqcs_handle_t handle);

/// Primitive API for sending a log message using the current logger.
///
/// Returns `DQCS_SUCCESS` if logging was successful, or `DQCS_FAILURE` if no
/// logger is available in the current thread or one of the arguments could not
/// be converted. Loggers are available in the simulation host thread and in
/// threads running plugins.
///
/// # Formatting and fallback to stderr
///
/// As an alternative to this function, you can also use `dqcs_log_format()`.
/// This function differs from `dqcs_log_raw()` in two ways:
///
///  - Instead of the `message` string, a printf-style format string and
///    associated varargs are passed to construct the message.
///  - When logging fails, this function falls back to writing to `stderr`
///    instead of returning the errors.
///
/// # Macros
///
/// From C and C++, these functions are normally not called directly. Instead,
/// the following macros are used:
///
/// ```C
/// dqcs_log_trace("trace message!");
/// dqcs_log_debug("debug message!");
/// dqcs_log_info("info message!");
/// dqcs_log_note("notice!");
/// dqcs_log_warn("warning!");
/// dqcs_log_error("error!");
/// dqcs_log_fatal("fatal error!");
/// ```
///
/// These macros automatically set `file` to the C source filename and `line`
/// to the line number. `module` is hardcoded to "C" or "CPP" depending on
/// source file language. They use `dqcs_log_format()`, so they also support
/// printf-style formatting. For instance:
///
/// ```C
/// dqcs_note("answer to %s: %d", "ultimate question", 42);
/// ```
dqcs_return_t dqcs_log_raw(dqcs_loglevel_t level,
                           const char *module,
                           const char *file,
                           uint32_t line_nr,
                           const char *message);

/// Constructs a controlled matrix from the given matrix.
///>
///> `mat` specifies the matrix to use as the non-controlled submatrix. This
///> is a borrowed handle. `number_of_controls` specifies the number of control
///> qubits to add. This function returns a new matrix handle with the
///> constructed matrix, or 0 if it fails.
dqcs_handle_t dqcs_mat_add_controls(dqcs_handle_t mat, size_t number_of_controls);

/// Approximately compares two matrices.
///>
///> `a` and `b` are borrowed matrix handles.
///> `epsilon` specifies the maximum element-wise root-mean-square error
///> between the matrices that results in a positive match. `ignore_gphase`
///> specifies whether the check should ignore global phase.
///>
///> If ignore_gphase is set, this checks that the following holds for some x:
///>
///> \f[
///> A \cdot e^{ix} \approx B
///> \f]
///>
///> This function returns `DQCS_TRUE` if the matrices match according to the
///> aforementioned criteria, or `DQCS_FALSE` if not. `DQCS_BOOL_ERROR` is used
///> when either handle is invalid or not a matrix. If the matrices differ in
///> dimensionality, `DQCS_FALSE` is used.
dqcs_bool_return_t dqcs_mat_approx_eq(dqcs_handle_t a,
                                      dqcs_handle_t b,
                                      double epsilon,
                                      bool ignore_gphase);

/// Returns whether the matrix is approximately unitary.
///>
///> `matrix` is a borrowed handle to the matrix to check.
///> `epsilon` specifies the maximum element-wise root-mean-square error
///> between the product of the matrix and its hermetian compared to the
///> identity matrix.
///>
///> This function returns `DQCS_TRUE` if the matrix is approximately unitary,
///> or `DQCS_FALSE` if not. `DQCS_BOOL_ERROR` is used when either handle is
///> invalid or not a matrix.
dqcs_bool_return_t dqcs_mat_approx_unitary(dqcs_handle_t matrix, double epsilon);

/// Constructs a matrix with the eigenvectors of one of the Pauli matrices
/// as column vectors.
///>
///> This can be used for constructing measurement or prep gates with the
///> given basis. Returns a new handle to the constructed matrix or returns
///> 0 if an error occurs.
dqcs_handle_t dqcs_mat_basis(dqcs_basis_t basis);

/// Approximately compares two basis matrices.
///>
///> `a` and `b` are borrowed matrix handles.
///> `epsilon` specifies the maximum element-wise root-mean-square error
///> between the matrices that results in a positive match.
///>
///> This checks that the following holds for some x and y:
///>
///> \f[
///> A \cdot \begin{bmatrix}
///> e^{ix} & 0 \\
///> 0 & e^{iy}
///> \end{bmatrix} \approx B
///> \f]
///>
///> This function returns `DQCS_TRUE` if the matrices match according to the
///> aforementioned criteria, or `DQCS_FALSE` if not. `DQCS_BOOL_ERROR` is used
///> when either handle is invalid or not a matrix. If either matrix is not
///> 2x2, `DQCS_FALSE` is used.
dqcs_bool_return_t dqcs_mat_basis_approx_eq(dqcs_handle_t a, dqcs_handle_t b, double epsilon);

/// Returns the dimension (number of rows == number of columns) of the given
/// matrix.
///>
///> This function returns -1 when an error occurs.
ssize_t dqcs_mat_dimension(dqcs_handle_t mat);

/// Returns a copy of the contained matrix as a C array.
///>
///> If this function succeeds, the matrix is returned in row-major form, using
///> pairs of doubles for the real vs. imaginary component of each entry. The
///> size will be `4**num_qubits` complex numbers = `2*4**num_qubits` doubles =
///> `16*4**num_qubits` bytes. A newly allocated matrix is returned; **free it
///> with `free()` when you're done with it to avoid memory leaks.** On failure,
///> this function returns `NULL`.
double *dqcs_mat_get(dqcs_handle_t mat);

/// Returns whether this matrix is of the given predefined form and, if it is,
/// any parameters needed to describe it.
///>
///> `mat` is a borrowed handle to the matrix to check.
///> `gate_type` specifies which kind of gate should be detected.
///> `param_data`, if non-null, receives a new `ArbData` handle with
///> parameterization data, or an empty `ArbData` if the gate is not
///> parameterized; the caller must delete this object when it is done with
///> it. This function always writes the 0 handle to this return parameter if
///> it fails. The `ArbData` representation can be found in the documentation
///> for `dqcs_predefined_gate_t`.
///>
///> `epsilon` specifies the maximum element-wise root-mean-square error
///> between the matrices that results in a positive match. `ignore_gphase`
///> specifies whether the check should ignore global phase.
///>
///> This function returns `DQCS_TRUE` if the matrices match according to the
///> aforementioned criteria, or `DQCS_FALSE` if not. `DQCS_BOOL_ERROR` is used
///> when either handle is invalid or not a matrix. If the matrices differ in
///> dimensionality, `DQCS_FALSE` is used.
dqcs_bool_return_t dqcs_mat_is_predef(dqcs_handle_t mat,
                                      dqcs_predefined_gate_t gate_type,
                                      dqcs_handle_t *param_data,
                                      double epsilon,
                                      bool ignore_gphase);

/// Returns the number of complex entries in the given matrix.
///>
///> This function returns -1 when an error occurs.
ssize_t dqcs_mat_len(dqcs_handle_t mat);

/// Constructs a new gate matrix.
///>
///> `num_qubits` must be set to the number of qubits mutated by this matrix.
///> It must be greater than or equal to zero.
///> `matrix` must point to an appropriately sized array of doubles. The matrix
///> is specified in row-major form, using pairs of doubles for the real vs.
///> imaginary component of each entry. The size must be `4**num_qubits` complex
///> numbers = `2*4**num_qubits` doubles = `16*4**num_qubits` bytes,
///> representing a `2**num_qubits` by `2**num_qubits` matrix.
///> This function returns the constructed matrix handle, or 0 if an error
///> occurs.
///>
///> While not enforced at this level, the matrix is normally unitary, or
///> approximately so within some floating-point error margin.
///>
///> This function returns the handle to the matrix, or 0 to indicate failure.
dqcs_handle_t dqcs_mat_new(size_t num_qubits, const double *matrix);

/// Returns the number of qubits targeted by the given matrix.
///>
///> This function returns -1 when an error occurs.
ssize_t dqcs_mat_num_qubits(dqcs_handle_t mat);

/// Constructs a new gate matrix for one of DQCsim's predefined gates.
///>
///> `gate_type` specifies which kind of gate should be constructed.
///>
///> `param_data` takes an optional `ArbData` object used to parameterize the
///> matrix if necessary. If not specified, an empty object is used. The
///> `ArbData` representation for each gate can be found in the docs for
///> `dqcs_predefined_gate_t`. If nothing is specified, no `ArbData` is used.
///>
///> This function returns the handle to the matrix, or 0 to indicate failure.
///> The parameterization data (if specified) is consumed/deleted by this
///> function if and only if it succeeds.
dqcs_handle_t dqcs_mat_predef(dqcs_predefined_gate_t gate_type, dqcs_handle_t param_data);

/// Splits a controlled matrix into its non-controlled submatrix and the
/// indices of the control qubits.
///>
///> `mat` specifies the matrix to modify. This is a borrowed handle.
///> `epsilon` specifies the maximum magitude of the difference between the
///> column vectors of the input matrix and the identity matrix (after
///> dephasing if `ignore_gphase` is set) for the column vector to be
///> considered to not affect the respective entry in the quantum state
///> vector. Note that if this is greater than zero, the resulting gate may
///> not be exactly equivalent. If `ignore_global_phase` is set, any global
///> phase in the matrix is ignored, but note that if control qubits are
///> stripped the "global" phase of the resulting submatrix is always
///> significant.
///> `control_indices` is a return argument through which DQCsim will pass
///> the indices of the qubits that were removed in the process of constructing
///> the submatrix. This is represented as an array of indices terminated by
///> a -1 entry. The returned matrix **must be freed using `free()` when you
///> are done with it to avoid memory leaks.** This function returns a new
///> matrix handle with the submatrix, or 0 if it fails. In this case,
///> `control_indices` is not mutated.
///>
///> This function assumes that the incoming matrix is unitary (within
///> `epsilon`) without verifying that this is the case. The results may
///> thus be invalid if it was not.
dqcs_handle_t dqcs_mat_strip_control(dqcs_handle_t mat,
                                     double epsilon,
                                     bool ignore_global_phase,
                                     ssize_t **control_indices);

/// Constructs a new measurement object.
///
/// `qubit` must be set to the qubit that was measured, `value` must be set to
/// its value. The return value is the handle to the measurement object, or 0
/// if something went wrong.
///
/// Note that measurement objects implement the `arb` interface, so additional
/// data can be attached to the object.
dqcs_handle_t dqcs_meas_new(dqcs_qubit_t qubit, dqcs_measurement_t value);

/// Returns the qubit reference associated with a measurement object.
dqcs_qubit_t dqcs_meas_qubit_get(dqcs_handle_t meas);

/// Sets the qubit reference associated with a measurement object.
dqcs_return_t dqcs_meas_qubit_set(dqcs_handle_t meas, dqcs_qubit_t qubit);

/// Returns the measurement value associated with a measurement object.
dqcs_measurement_t dqcs_meas_value_get(dqcs_handle_t meas);

/// Sets the measurement value associated with a measurement object.
dqcs_return_t dqcs_meas_value_set(dqcs_handle_t meas, dqcs_measurement_t value);

/// Returns whether the given qubit measurement set contains data for the given
/// qubit.
dqcs_bool_return_t dqcs_mset_contains(dqcs_handle_t mset, dqcs_qubit_t qubit);

/// Returns a copy of the measurement result for the given qubit from a
/// measurement result set.
dqcs_handle_t dqcs_mset_get(dqcs_handle_t mset, dqcs_qubit_t qubit);

/// Returns the number of qubits measurements in the given measurement set.
///
/// This function returns -1 to indicate failure.
ssize_t dqcs_mset_len(dqcs_handle_t mset);

/// Creates a new set of qubit measurement results.
///
/// Returns the handle of the newly created set. The set is initially empty.
dqcs_handle_t dqcs_mset_new();

/// Removes the measurement result for the given qubit from a measurement
/// result set.
dqcs_return_t dqcs_mset_remove(dqcs_handle_t mset, dqcs_qubit_t qubit);

/// Adds a measurement result to a measurement result set.
///
/// If there was already a measurement for the specified qubit, the previous
/// measurement result is overwritten. The measurement result object is deleted
/// if and only if the function succeeds.
dqcs_return_t dqcs_mset_set(dqcs_handle_t mset, dqcs_handle_t meas);

/// Returns the measurement result for the given qubit from a measurement
/// result set and removes it from the set.
dqcs_handle_t dqcs_mset_take(dqcs_handle_t mset, dqcs_qubit_t qubit);

/// Returns the measurement result for any of the qubits contained in a
/// measurement result set and removes it from the set.
///
/// This is useful for iteration.
dqcs_handle_t dqcs_mset_take_any(dqcs_handle_t mset);

/// Returns the configured timeout for the plugin process to connect to DQCsim.
///
/// The time unit is in seconds. Returns positive inifinity for an infinite
/// timeout. Returns -1 when the function fails.
double dqcs_pcfg_accept_timeout_get(dqcs_handle_t pcfg);

/// Configures the timeout for the plugin process to connect to DQCsim.
///
/// The default is 5 seconds, so you should normally be able to leave this
/// alone.
///
/// The time unit is seconds. Use IEEE positive infinity to specify an infinite
/// timeout.
dqcs_return_t dqcs_pcfg_accept_timeout_set(dqcs_handle_t pcfg, double timeout);

/// Overrides an environment variable for the plugin process.
///
/// The environment variable `key` is set to `value` regardless of whether it
/// exists in the parent environment variable scope.
///
/// If value is `NULL`, the environment variable `key` is unset instead.
dqcs_return_t dqcs_pcfg_env_set(dqcs_handle_t pcfg, const char *key, const char *value);

/// Removes/unsets an environment variable for the plugin process.
///
/// The environment variable `key` is unset regardless of whether it exists in
/// the parent environment variable scope.
dqcs_return_t dqcs_pcfg_env_unset(dqcs_handle_t pcfg, const char *key);

/// Returns the configured executable path for the given plugin process.
///
/// On success, this **returns a newly allocated string containing the
/// executable path. Free it with `free()` when you're done with it to avoid
/// memory leaks.** On failure (i.e., the handle is invalid) this returns
/// `NULL`.
char *dqcs_pcfg_executable(dqcs_handle_t pcfg);

/// Appends an `ArbCmd` to the list of initialization commands of a plugin
/// process.
///
/// The `ArbCmd` handle is consumed by this function, and is thus invalidated,
/// if and only if it is successful.
dqcs_return_t dqcs_pcfg_init_cmd(dqcs_handle_t pcfg, dqcs_handle_t cmd);

/// Returns the configured name for the given plugin process.
///
/// On success, this **returns a newly allocated string containing the
/// name. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
char *dqcs_pcfg_name(dqcs_handle_t pcfg);

/// Creates a new plugin process configuration object using sugared syntax.
///
/// `typ` specifies the type of plugin. `name` specifies the name used to refer
/// to the plugin later, which much be unique within a simulation; if it is
/// empty or `NULL`, auto-naming will be performed: "front" for the frontend,
/// "oper&lt;i&gt;" for the operators (indices starting at 1 from frontend to
/// backend), and "back" for the backend. `spec` specifies which plugin to use,
/// using the same syntax that the `dqcsim` command line interface uses.
dqcs_handle_t dqcs_pcfg_new(dqcs_plugin_type_t typ, const char *name, const char *spec);

/// Creates a new plugin process configuration object using raw paths.
///
/// This works the same as `dqcs_pcfg_new()`, but instead of the sugared,
/// command-line style specification you have to specify the path to the plugin
/// executable and (if applicable) the script it must execute directly. This is
/// useful when you have a specific executable in mind and you don't want the
/// somewhat heuristic desugaring algorithm from doing something unexpected.
///
/// Pass `NULL` or an empty string to `script` to specify a native plugin
/// executable that does not take a script argument.
dqcs_handle_t dqcs_pcfg_new_raw(dqcs_plugin_type_t typ,
                                const char *name,
                                const char *executable,
                                const char *script);

/// Returns the configured script path for the given plugin process.
///
/// On success, this **returns a newly allocated string containing the
/// script path. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure (i.e., the handle is invalid) this returns `NULL`. An
/// empty string will be returned if no script is configured to distinguish it
/// from failure.
char *dqcs_pcfg_script(dqcs_handle_t pcfg);

/// Returns the configured timeout for the plugin process to shut down
/// gracefully.
///
/// The time unit is in seconds. Returns positive inifinity for an infinite
/// timeout. Returns -1 when the function fails.
double dqcs_pcfg_shutdown_timeout_get(dqcs_handle_t pcfg);

/// Configures the timeout for the plugin process to shut down gracefully.
///
/// The default is 5 seconds, so you should normally be able to leave this
/// alone.
///
/// The time unit is seconds. Use IEEE positive infinity to specify an infinite
/// timeout.
dqcs_return_t dqcs_pcfg_shutdown_timeout_set(dqcs_handle_t pcfg, double timeout);

/// Returns the configured stderr capture mode for the specified plugin
/// process.
dqcs_loglevel_t dqcs_pcfg_stderr_mode_get(dqcs_handle_t pcfg);

/// Configures the capture mode for the stderr stream of the specified plugin
/// process.
dqcs_return_t dqcs_pcfg_stderr_mode_set(dqcs_handle_t pcfg, dqcs_loglevel_t level);

/// Returns the configured stdout capture mode for the specified plugin
/// process.
dqcs_loglevel_t dqcs_pcfg_stdout_mode_get(dqcs_handle_t pcfg);

/// Configures the capture mode for the stdout stream of the specified plugin
/// process.
dqcs_return_t dqcs_pcfg_stdout_mode_set(dqcs_handle_t pcfg, dqcs_loglevel_t level);

/// Configures a plugin process to also output its log messages to a file.
///
/// `verbosity` configures the verbosity level for the file only.
dqcs_return_t dqcs_pcfg_tee(dqcs_handle_t pcfg, dqcs_loglevel_t verbosity, const char *filename);

/// Returns the type of the given plugin process configuration.
dqcs_plugin_type_t dqcs_pcfg_type(dqcs_handle_t pcfg);

/// Returns the configured verbosity for the given plugin process.
dqcs_loglevel_t dqcs_pcfg_verbosity_get(dqcs_handle_t pcfg);

/// Configures the logging verbosity for the given plugin process.
dqcs_return_t dqcs_pcfg_verbosity_set(dqcs_handle_t pcfg, dqcs_loglevel_t level);

/// Returns the configured working directory for the given plugin process.
///
/// On success, this **returns a newly allocated string containing the
/// working directory. Free it with `free()` when you're done with it to avoid
/// memory leaks.** On failure (i.e., the handle is invalid) this returns
/// `NULL`.
char *dqcs_pcfg_work_get(dqcs_handle_t pcfg);

/// Overrides the working directory for the plugin process.
dqcs_return_t dqcs_pcfg_work_set(dqcs_handle_t pcfg, const char *work);

/// Returns the plugin author for the given plugin definition object.
///
/// On success, this **returns a newly allocated string containing the JSON
/// string. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure, this returns `NULL`.
char *dqcs_pdef_author(dqcs_handle_t pdef);

/// Returns the plugin name for the given plugin definition object.
///
/// On success, this **returns a newly allocated string containing the JSON
/// string. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure, this returns `NULL`.
char *dqcs_pdef_name(dqcs_handle_t pdef);

/// Creates a new `PluginDefinition` object.
///
/// Plugin definitions contain the callback functions/closures that define the
/// functionality of a plugin. They also contain some metadata to identify the
/// implementation, in the form of a name, author, and version string, that
/// must be specified when the definition is constructed. The callback
/// functions/closures are initialized to sane defaults for the requested
/// plugin type, but obviously one or more of these should be overridden to
/// make the plugin do something.
///
/// Once a definition object has been built, it can be used to spawn a plugin
/// thread or run a plugin in the main thread, given a DQCsim server URL for it
/// to connect to.
dqcs_handle_t dqcs_pdef_new(dqcs_plugin_type_t typ,
                            const char *name,
                            const char *author,
                            const char *version);

/// Sets the callback for advancing time for operators and backends.
///
/// The default behavior for operators is to pass through to
/// `dqcs_plugin_advance()`. The default for backends is no-op. This
/// callback is never called for frontend plugins.
///
/// Besides the common arguments, the callback receives an unsigned integer
/// specifying the number of cycles to advance by.
///
/// The callback can return an error by setting an error message using
/// `dqcs_error_set()` and returning `DQCS_FAILURE`. Otherwise, it should
/// return `DQCS_SUCCESS`.
dqcs_return_t dqcs_pdef_set_advance_cb(dqcs_handle_t pdef,
                                       dqcs_return_t (*callback)(void *user_data, dqcs_plugin_state_t state, dqcs_cycle_t cycles),
                                       void (*user_free)(void *user_data),
                                       void *user_data);

/// Sets the qubit allocation callback for operators and backends.
///
/// The default for operators is to pass through to
/// `dqcs_plugin_allocate()`. The default for backends is no-op. This
/// callback is never called for frontend plugins.
///
/// Besides the common arguments, the callback receives a handle to a qubit
/// set containing the references that are to be used for the
/// to-be-allocated qubits and an `ArbCmd` queue containing user-defined
/// commands to optionally augment the behavior of the qubits. These are
/// borrowed handles; the caller will delete them.
///
/// The callback can return an error by setting an error message using
/// `dqcs_error_set()` and returning `DQCS_FAILURE`. Otherwise, it should
/// return `DQCS_SUCCESS`.
dqcs_return_t dqcs_pdef_set_allocate_cb(dqcs_handle_t pdef,
                                        dqcs_return_t (*callback)(void *user_data, dqcs_plugin_state_t state, dqcs_handle_t qubits, dqcs_handle_t alloc_cmds),
                                        void (*user_free)(void *user_data),
                                        void *user_data);

/// Sets the user logic drop/cleanup callback.
///
/// This is called when a plugin is gracefully terminated. It is not
/// recommended to execute any downstream instructions at this time, but it
/// is supported in case this is really necessary.
///
/// The default behavior is no-op.
///
/// The callback can return an error by setting an error message using
/// `dqcs_error_set()` and returning `DQCS_FAILURE`. Otherwise, it should
/// return `DQCS_SUCCESS`.
dqcs_return_t dqcs_pdef_set_drop_cb(dqcs_handle_t pdef,
                                    dqcs_return_t (*callback)(void *user_data, dqcs_plugin_state_t state),
                                    void (*user_free)(void *user_data),
                                    void *user_data);

/// Sets the qubit deallocation callback for operators and backends.
///
/// The default for operators is to pass through to `dqcs_plugin_free()`.
/// The default for backends is no-op. This callback is never called for
/// frontend plugins.
///
/// Besides the common arguments, the callback receives a handle to a qubit
/// set containing the qubits that are to be freed. This is a borrowed
/// handle; the caller will delete it.
///
/// The callback can return an error by setting an error message using
/// `dqcs_error_set()` and returning `DQCS_FAILURE`. Otherwise, it should
/// return `DQCS_SUCCESS`.
dqcs_return_t dqcs_pdef_set_free_cb(dqcs_handle_t pdef,
                                    dqcs_return_t (*callback)(void *user_data, dqcs_plugin_state_t state, dqcs_handle_t qubits),
                                    void (*user_free)(void *user_data),
                                    void *user_data);

/// Sets the gate execution callback for operators and backends.
///
/// Besides the common arguments, the callback receives a handle to the
/// to-be-executed gate. This is a borrowed handle; the caller will delete
/// it.
///
/// The callback must return one of the following things:
///
///  - a valid handle to a measurement set, created using
///    `dqcs_mset_new()` (this object is automatically deleted after the
///    callback returns);
///  - a valid handle to a single qubit measurement, created using
///    `dqcs_meas_new()` (this object is automatically deleted after the
///    callback returns);
///  - the handle to the supplied gate, a shortcut for not returning any
///    measurements (this is less clear than returning an empty measurement
///    set, but slightly faster); or
///  - 0 to report an error, after calling the error string using
///    `dqcs_set_error()`.
///
/// Backend plugins must return a measurement result set containing exactly
/// those qubits specified in the measurement set. For operators, however,
/// the story is more complicated. Let's say we want to make a silly
/// operator that inverts all measurements. The trivial way to do
/// this would be to forward the gate, query all the measurement results
/// using `dqcs_plugin_get_measurement()`, invert them, stick them in a
/// measurement result set, and return that result set. However, this
/// approach is not very efficient, because `dqcs_plugin_get_measurement()`
/// has to wait for all downstream plugins to finish executing the gate,
/// forcing the OS to switch threads, etc. Instead, operators are allowed
/// to return only a subset (or none) of the measured qubits, as long as
/// they return the measurements as they arrive through the
/// `modify_measurement()` callback.
///
/// The default implementation for this callback for operators is to pass
/// the gate through to the downstream plugin and return an empty set of
/// measurements. Combined with the default implementation of
/// `modify_measurement()`, this behavior is sane. Backends must override
/// this callback; the default is to return a not-implemented error.
///
/// Note that for our silly example operator, the default behavior for this
/// function is sufficient; you'd only have to override
/// `modify_measurement()` to, well, modify the measurements.
dqcs_return_t dqcs_pdef_set_gate_cb(dqcs_handle_t pdef,
                                    dqcs_handle_t (*callback)(void *user_data, dqcs_plugin_state_t state, dqcs_handle_t gate),
                                    void (*user_free)(void *user_data),
                                    void *user_data);

/// Sets the callback function function for handling an arb from the host.
///
/// The default behavior for this is no-op.
///
/// Besides the common arguments, the callback receives a handle to the
/// `ArbCmd` object representing the request. It must return a valid
/// `ArbData` handle containing the response. Both objects are deleted
/// automatically after invocation.
///
/// The callback can return an error by setting an error message using
/// `dqcs_error_set()` and returning 0. Otherwise, it should return a valid
/// `ArbData` handle.
dqcs_return_t dqcs_pdef_set_host_arb_cb(dqcs_handle_t pdef,
                                        dqcs_handle_t (*callback)(void *user_data, dqcs_plugin_state_t state, dqcs_handle_t cmd),
                                        void (*user_free)(void *user_data),
                                        void *user_data);

/// Sets the user logic initialization callback.
///
/// This is always called before any of the other callbacks are run. The
/// downstream plugin has already been initialized at this stage, so it is
/// legal to send it commands.
///
/// The default behavior is no-op.
///
/// Besides the common arguments, the callback receives a handle to an
/// `ArbCmd` queue (`dqcs_cq_*`, `dqcs_cmd_*`, and `dqcs_arb_*` interfaces)
/// containing user-defined initialization commands. This is a borrowed
/// handle; the caller will delete it.
///
/// The callback can return an error by setting an error message using
/// `dqcs_error_set()` and returning `DQCS_FAILURE`. Otherwise, it should
/// return `DQCS_SUCCESS`.
dqcs_return_t dqcs_pdef_set_initialize_cb(dqcs_handle_t pdef,
                                          dqcs_return_t (*callback)(void *user_data, dqcs_plugin_state_t state, dqcs_handle_t init_cmds),
                                          void (*user_free)(void *user_data),
                                          void *user_data);

/// Sets the measurement modification callback for operators.
///
/// This callback is called for every measurement result received from the
/// downstream plugin, and returns the measurements that should be reported
/// to the upstream plugin. Note that the results from our plugin's
/// `dqcs_plugin_get_measurement()` and friends are consistent with the
/// results received from downstream; they are not affected by this
/// function.
///
/// The callback takes a handle to a single qubit measurement object as an
/// argument, and must return one of the following things:
///
///  - a valid handle to a measurement set, created using
///    `dqcs_mset_new()` (this object is automatically deleted after the
///    callback returns);
///  - a valid handle to a single qubit measurement object, which may or
///    may not be the supplied one (this object is automatically deleted
///    after the callback returns); or
///  - 0 to report an error, after calling the error string using
///    `dqcs_set_error()`.
///
/// This callback is somewhat special in that it is not allowed to call
/// any plugin command other than logging and the pseudorandom number
/// generator functions. This is because this function is called
/// asynchronously with respect to the downstream functions, making the
/// timing of these calls non-deterministic based on operating system
/// scheduling.
///
/// Note that while this function is called for only a single measurement
/// at a time, it is allowed to produce a vector of measurements. This
/// allows you to cancel propagation of the measurement by returning an
/// empty vector, to just modify the measurement data itself, or to
/// generate additional measurements from a single measurement. However,
/// if you need to modify the qubit references for operators that remap
/// qubits, take care to only send measurement data upstream when these
/// were explicitly requested through the associated upstream gate
/// function's `measured` list.
///
/// The default behavior for this callback is to return the measurement
/// without modification.
dqcs_return_t dqcs_pdef_set_modify_measurement_cb(dqcs_handle_t pdef,
                                                  dqcs_handle_t (*callback)(void *user_data, dqcs_plugin_state_t state, dqcs_handle_t meas),
                                                  void (*user_free)(void *user_data),
                                                  void *user_data);

/// Sets the run callback for frontends.
///
/// This is called in response to a `start()` host API call. The return
/// value is returned through the `wait()` host API call.
///
/// The default behavior is to fail with a "not implemented" error;
/// frontends backends should always override this. This callback is never
/// called for operator or backend plugins.
///
/// Besides the common arguments, the callback receives a handle to an
/// `ArbData` object containing the data that the host passed to `start()`.
/// This is a borrowed handle; the caller will delete it.
///
/// When the run callback is successful, it should return a valid `ArbData`
/// handle. This can be the same as the argument, but it can also be a new
/// object. This `ArbData` is returned to the host through `wait()`. This
/// `ArbData` object is deleted after the callback completes.
///
/// The callback can return an error by setting an error message using
/// `dqcs_error_set()` and returning 0. Otherwise, it should return a
/// valid `ArbData` handle.
dqcs_return_t dqcs_pdef_set_run_cb(dqcs_handle_t pdef,
                                   dqcs_handle_t (*callback)(void *user_data, dqcs_plugin_state_t state, dqcs_handle_t args),
                                   void (*user_free)(void *user_data),
                                   void *user_data);

/// Sets the callback function for handling an arb from upstream for
/// operators and backends.
///
/// The default behavior for operators is to pass through to
/// `dqcs_plugin_arb()`; operators that do not support the requested
/// interface should always do this. The default for backends is no-op.
/// This callback is never called for frontend plugins.
///
/// Besides the common arguments, the callback receives a handle to the
/// `ArbCmd` object representing the request. It must return a valid
/// `ArbData` handle containing the response. Both objects are deleted
/// automatically after invocation.
///
/// The callback can return an error by setting an error message using
/// `dqcs_error_set()` and returning 0. Otherwise, it should return a valid
/// `ArbData` handle.
dqcs_return_t dqcs_pdef_set_upstream_arb_cb(dqcs_handle_t pdef,
                                            dqcs_handle_t (*callback)(void *user_data, dqcs_plugin_state_t state, dqcs_handle_t cmd),
                                            void (*user_free)(void *user_data),
                                            void *user_data);

/// Returns the plugin type for the given plugin definition object.
dqcs_plugin_type_t dqcs_pdef_type(dqcs_handle_t pdef);

/// Returns the plugin version for the given plugin definition object.
///
/// On success, this **returns a newly allocated string containing the JSON
/// string. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure, this returns `NULL`.
char *dqcs_pdef_version(dqcs_handle_t pdef);

/// Tells the downstream plugin to run for the specified number of cycles.
///
/// Backend plugins are not allowed to call this. Doing so will result in an
/// error.
///
/// The return value is the new cycle counter. This function uses -1 to signal
/// an error.
dqcs_cycle_t dqcs_plugin_advance(dqcs_plugin_state_t plugin, dqcs_cycle_t cycles);

/// Allocate the given number of downstream qubits.
///
/// Backend plugins are not allowed to call this. Doing so will result in an
/// error.
///
/// `num_qubits` specifies the number of qubits that are to be allocated.
///
/// `commands` must be 0 or a valid handle to an `ArbCmd` queue, containing a
/// list of commands that may be used to modify the behavior of the qubit
/// register; 0 is equivalent to zero commands. The queue is consumed by this
/// function, i.e. the handle becomes invalid, if and only if it succeeds.
///
/// If the function is successful, a new handle to the set of qubit references
/// representing the newly allocated register is returned. When the function
/// fails, 0 is returned.
dqcs_handle_t dqcs_plugin_allocate(dqcs_plugin_state_t plugin,
                                   uintptr_t num_qubits,
                                   dqcs_handle_t cq);

/// Sends an arbitrary command downstream.
///
/// Backend plugins are not allowed to call this. Doing so will result in an
/// error.
///
/// This function returns a new handle to an `ArbData` object representing the
/// return value of the `ArbCmd` when successful. Otherwise, it returns 0.
dqcs_handle_t dqcs_plugin_arb(dqcs_plugin_state_t plugin, dqcs_handle_t cmd);

/// Free the given downstream qubits.
///
/// Backend plugins are not allowed to call this. Doing so will result in an
/// error.
///
/// `qubits` must be a valid set of qubit references. The set is consumed by
/// this function, i.e. the handle becomes invalid, if and only if it succeeds.
dqcs_return_t dqcs_plugin_free(dqcs_plugin_state_t plugin, dqcs_handle_t qbset);

/// Tells the downstream plugin to execute a gate.
///
/// Backend plugins are not allowed to call this. Doing so will result in an
/// error.
///
/// `gate` must be a valid gate object. The object is consumed by this
/// function, i.e. the handle becomes invalid, if and only if it succeeds.
dqcs_return_t dqcs_plugin_gate(dqcs_plugin_state_t plugin, dqcs_handle_t gate);

/// Returns the current value of the downstream cycle counter.
///
/// Backend plugins are not allowed to call this. Doing so will result in an
/// error.
///
/// This function uses -1 to signal an error.
dqcs_cycle_t dqcs_plugin_get_cycle(dqcs_plugin_state_t plugin);

/// Returns the number of downstream cycles between the last two measurements
/// of the given downstream qubit.
///
/// Backend plugins are not allowed to call this. Doing so will result in an
/// error.
///
/// This function uses -1 to signal an error.
dqcs_cycle_t dqcs_plugin_get_cycles_between_measures(dqcs_plugin_state_t plugin,
                                                     dqcs_qubit_t qubit);

/// Returns the number of downstream cycles since the latest measurement of the
/// given downstream qubit.
///
/// Backend plugins are not allowed to call this. Doing so will result in an
/// error.
///
/// This function uses -1 to signal an error.
dqcs_cycle_t dqcs_plugin_get_cycles_since_measure(dqcs_plugin_state_t plugin, dqcs_qubit_t qubit);

/// Returns the latest measurement of the given downstream qubit.
///
/// Backend plugins are not allowed to call this. Doing so will result in an
/// error.
///
/// If the function succeeds, it returns a new handle to a qubit measurement
/// result object. Otherwise it returns 0.
dqcs_handle_t dqcs_plugin_get_measurement(dqcs_plugin_state_t plugin, dqcs_qubit_t qubit);

/// Generates a random floating point number using the simulator random seed.
///
/// The generated numbers are uniformly distributed in the range `[0,1>`.
///
/// This function only fails if the `plugin` handle is invalid, in which case
/// it returns 0. Of course, 0 is also a valid (if rare) random return value.
double dqcs_plugin_random_f64(dqcs_plugin_state_t plugin);

/// Generates a random unsigned 64-bit number using the simulator random seed.
///
/// This function only fails if the `plugin` handle is invalid, in which case
/// it returns 0. Of course, 0 is also a valid (if rare) random return value.
dqcs_handle_t dqcs_plugin_random_u64(dqcs_plugin_state_t plugin);

/// Waits for a message from the host.
///
/// It is only legal to call this function from within the `run()` callback.
/// Any other source will result in an error.
///
/// When successful, this function returns a new handle to the received
/// `ArbData` object. 0 is used to indicate that an error occurred.
dqcs_handle_t dqcs_plugin_recv(dqcs_plugin_state_t plugin);

/// Executes a plugin in the current thread.
///
/// `pdef` must be an appropriately populated plugin definition object.
/// Its callback functions will be called from the current thread, from within
/// the context of this function.
///
/// `simulator` must be set to the address of our endpoint of the simulator
/// that's using the plugin; DQCsim normally passes this as the first command
/// line argument of the plugin process.
///
/// If the plugin starts, the `pdef` handle is consumed by this function,
/// regardless of whether the plugin eventually closes normally. The handle is
/// only left alive if `pdef` is not a plugin definition object.
dqcs_return_t dqcs_plugin_run(dqcs_handle_t pdef, const char *simulator);

/// Sends a message to the host.
///
/// It is only legal to call this function from within the `run()` callback.
/// Any other source will result in an error.
///
/// The `cmd` handle is consumed by this function if and only if it succeeds.
dqcs_return_t dqcs_plugin_send(dqcs_plugin_state_t plugin, dqcs_handle_t arb);

/// Executes a plugin in a worker thread.
///
/// This function behaves the same as dqcs_plugin_log(), but is asynchronous;
/// it always returns immediately. Of course, this means that the callbacks in
/// `pdef` will be called from a different thread.
///
/// To wait for the thread to finish executing, call `dqcs_plugin_wait()` on
/// the returned join handle. Alternatively you can delete the join handle
/// object, which will detach the thread.
///
/// Note that `dqcs_log_*()` will only be available in the thread that the
/// plugin actually runs in.
///
/// This function returns 0 to indicate failure to start the plugin. Otherwise,
/// the join handle is returned.
dqcs_handle_t dqcs_plugin_start(dqcs_handle_t pdef, const char *simulator);

/// Waits for a plugin worker thread to finish executing.
///
/// Unless the join handle is invalid, this function returns success/failure
/// based on the result of the plugin execution. If the plugin thread is
/// joined, the join handle is deleted.
dqcs_return_t dqcs_plugin_wait(dqcs_handle_t pjoin);

/// Returns whether the given qubit set contains the given qubit.
dqcs_bool_return_t dqcs_qbset_contains(dqcs_handle_t qbset, dqcs_qubit_t qubit);

/// Returns a copy of the given qubit set, intended for non-destructive
/// iteration.
dqcs_handle_t dqcs_qbset_copy(dqcs_handle_t qbset);

/// Returns the number of qubits in the given set.
///
/// This function returns -1 to indicate failure.
ssize_t dqcs_qbset_len(dqcs_handle_t qbset);

/// Creates a new set of qubit references.
///
/// Returns the handle of the newly created set. The set is initially empty.
/// Qubit sets are ordered, meaning that the order in which qubits are popped
/// from the set equals the order in which they were pushed. To iterate over a
/// set, simply make a copy and drain the copy using pop.
dqcs_handle_t dqcs_qbset_new();

/// Pops a qubit reference off of a qubit reference set.
///
/// Qubits are popped in the same order in which they were pushed. That is,
/// they are FIFO-ordered.
dqcs_qubit_t dqcs_qbset_pop(dqcs_handle_t qbset);

/// Pushes a qubit reference into a qubit reference set.
///
/// This function will fail if the specified qubit was already part of the set.
dqcs_return_t dqcs_qbset_push(dqcs_handle_t qbset, dqcs_qubit_t qubit);

/// Returns the configured verbosity for DQCsim's own messages.
dqcs_loglevel_t dqcs_scfg_dqcsim_verbosity_get(dqcs_handle_t scfg);

/// Configures the logging verbosity for DQCsim's own messages.
dqcs_return_t dqcs_scfg_dqcsim_verbosity_set(dqcs_handle_t scfg, dqcs_loglevel_t level);

/// Configures DQCsim to also output its log messages to callback function.
///
/// `verbosity` specifies the minimum importance of a message required for the
/// callback to be called.
///
/// `callback` is the callback function to install. It is always called with
/// the `user_data` pointer to make calling stuff like class member functions
/// or closures possible. The `user_free` function, if non-null, will be called
/// when the callback is uninstalled in any way. If `callback` is null, any
/// current callback is uninstalled instead. For consistency, if `user_free` is
/// non-null while `callback` is null, `user_free` is called immediately, under
/// the assumption that the caller has allocated resources unbeknownst that the
/// callback it's trying to install is null.
///
/// **NOTE: both `callback` and `user_free` may be called from a thread spawned
/// by the simulator. Calling any API calls from the callback is therefore
/// undefined behavior!**
///
/// The callback takes the following arguments:
///  - `void*`: user defined data.
///  - `const char*`: log message string, excluding metadata.
///  - `const char*`: name assigned to the logger that was used to produce the
///    message (= "dqcsim" or a plugin name).
///  - `dqcs_loglevel_t`: the verbosity level that the message was logged with.
///  - `const char*`: string representing the source of the log message, or
///    `NULL` when no source is known.
///  - `const char*`: string containing the filename of the source that
///    generated the message, or `NULL` when no source is known.
///  - `uint32_t`: line number within the aforementioned file, or 0 if not
///    known.
///  - `uint64_t`: Time in seconds since the Unix epoch.
///  - `uint32_t`: Additional time in nanoseconds since the aforementioned.
///  - `uint32_t`: PID of the generating process.
///  - `uint64_t`: TID of the generating thread.
///
/// If an internal log record is particularly malformed and cannot be coerced
/// into the above (nul bytes in the strings, invalid timestamp, whatever) the
/// message is silently ignored.
///
/// The primary use of this callback is to pipe DQCsim's messages to an
/// external logging framework. When you do this, you probably also want to
/// call `dqcs_scfg_stderr_verbosity_set(handle, DQCS_LOG_OFF)` to prevent
/// DQCsim from writing the messages to stderr itself.
dqcs_return_t dqcs_scfg_log_callback(dqcs_handle_t scfg,
                                     dqcs_loglevel_t verbosity,
                                     void (*callback)(void *user_data, const char *message, const char *logger, dqcs_loglevel_t level, const char *module, const char *file, uint32_t line, uint64_t time_s, uint32_t time_ns, uint32_t pid, uint64_t tid),
                                     void (*user_free)(void *user_data),
                                     void *user_data);

/// Constructs an empty simulation configuration.
///
/// Before the configuration can be used, at least a frontend and a backend
/// plugin configuration must be pushed into it. This can be done with
/// `dqcs_scfg_push_plugin()`. Failing to do this will result in an error when
/// you try to start the simulation.
///
/// The default settings correspond to the defaults of the `dqcsim` command
/// line interface. Refer to its help for more information.
dqcs_handle_t dqcs_scfg_new();

/// Appends a plugin to a simulation configuration.
///
/// Both plugin process and plugin thread configuration objects may be used.
/// The handle is consumed by this function, and is thus invalidated, if and
/// only if it is successful.
///
/// Frontend and backend plugins will automatically be inserted at the front
/// and back of the pipeline when the simulation is created. Operators are
/// inserted in front to back order. This function does not provide safeguards
/// against multiple frontends/backends; such errors will only be reported when
/// the simulation is started.
///
/// Note that it is not possible to observe or mutate a plugin configuration
/// once it has been added to a simulator configuration handle. If you want to
/// do this for some reason, you should maintain your own data structures, and
/// only build the DQCsim structures from them when you're done.
dqcs_return_t dqcs_scfg_push_plugin(dqcs_handle_t scfg, dqcs_handle_t xcfg);

/// Disables the reproduction logging system.
///
/// Calling this will disable the warnings printed when a simulation that
/// cannot be reproduced is constructed.
dqcs_return_t dqcs_scfg_repro_disable(dqcs_handle_t scfg);

/// Returns the path style used when writing reproduction files.
dqcs_path_style_t dqcs_scfg_repro_path_style_get(dqcs_handle_t scfg);

/// Sets the path style used when writing reproduction files.
dqcs_return_t dqcs_scfg_repro_path_style_set(dqcs_handle_t scfg, dqcs_path_style_t path_style);

/// Returns the configured random seed.
///
/// This function will return 0 when it fails, but this can unfortunately not
/// be reliably distinguished from a seed that was set to 0.
uint64_t dqcs_scfg_seed_get(dqcs_handle_t scfg);

/// Configures the random seed that the simulation should use.
///
/// Note that the seed is randomized by default.
dqcs_return_t dqcs_scfg_seed_set(dqcs_handle_t scfg, uint64_t seed);

/// Returns the configured stderr sink verbosity for a simulation.
///
/// That is, the minimum loglevel that a messages needs to have for it to be
/// printed to stderr.
dqcs_loglevel_t dqcs_scfg_stderr_verbosity_get(dqcs_handle_t scfg);

/// Configures the stderr sink verbosity for a simulation.
///
/// That is, the minimum loglevel that a messages needs to have for it to be
/// printed to stderr.
dqcs_return_t dqcs_scfg_stderr_verbosity_set(dqcs_handle_t scfg, dqcs_loglevel_t level);

/// Configures DQCsim to also output its log messages to a file.
///
/// `verbosity` configures the verbosity level for the file only.
dqcs_return_t dqcs_scfg_tee(dqcs_handle_t scfg, dqcs_loglevel_t verbosity, const char *filename);

/// Sends an `ArbCmd` message to one of the plugins, referenced by name.
///
/// `ArbCmd`s are executed immediately after yielding to the simulator, so
/// all pending asynchronous calls are flushed and executed *before* the
/// `ArbCmd`.
///
/// When this succeeds, the received data is returned in the form of a new
/// handle. When it fails, 0 is returned.
///
/// The `ArbCmd` handle is consumed if and only if the API call succeeds.
dqcs_handle_t dqcs_sim_arb(dqcs_handle_t sim, const char *name, dqcs_handle_t cmd);

/// Sends an `ArbCmd` message to one of the plugins, referenced by index.
///
/// The frontend always has index 0. 1 through N are used for the operators
/// in front to back order (where N is the number of operators). The
/// backend is at index N+1.
///
/// Python-style negative indices are supported. That is, -1 can be used to
/// refer to the backend, -2 to the last operator, and so on.
///
/// `ArbCmd`s are executed immediately after yielding to the simulator, so
/// all pending asynchronous calls are flushed and executed *before* the
/// `ArbCmd`.
///
/// When this succeeds, the received data is returned in the form of a new
/// handle. When it fails, 0 is returned.
///
/// The `ArbCmd` handle is consumed if and only if the API call succeeds.
dqcs_handle_t dqcs_sim_arb_idx(dqcs_handle_t sim, ssize_t index, dqcs_handle_t cmd);

/// Queries the author of a plugin, referenced by instance name.
///
/// On success, this **returns a newly allocated string containing the
/// author. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
char *dqcs_sim_get_author(dqcs_handle_t sim, const char *name);

/// Queries the author of a plugin, referenced by index.
///
/// On success, this **returns a newly allocated string containing the
/// author. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
char *dqcs_sim_get_author_idx(dqcs_handle_t sim, ssize_t index);

/// Queries the implementation name of a plugin, referenced by instance
/// name.
///
/// On success, this **returns a newly allocated string containing the
/// name. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
char *dqcs_sim_get_name(dqcs_handle_t sim, const char *name);

/// Queries the implementation name of a plugin, referenced by index.
///
/// On success, this **returns a newly allocated string containing the
/// name. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
char *dqcs_sim_get_name_idx(dqcs_handle_t sim, ssize_t index);

/// Queries the version of a plugin, referenced by instance name.
///
/// On success, this **returns a newly allocated string containing the
/// version. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
char *dqcs_sim_get_version(dqcs_handle_t sim, const char *name);

/// Queries the version of a plugin, referenced by index.
///
/// On success, this **returns a newly allocated string containing the
/// version. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
char *dqcs_sim_get_version_idx(dqcs_handle_t sim, ssize_t index);

/// Constructs a DQCsim simulation.
///
/// The provided handle is consumed if it is a simulation configuration,
/// regardless of whether simulation construction succeeds.
dqcs_handle_t dqcs_sim_new(dqcs_handle_t scfg);

/// Waits for the simulated accelerator to send a message to us.
///
/// When this succeeds, the received data is returned in the form of a new
/// handle. When it fails, 0 is returned.
///
/// Deadlocks are detected and prevented by returning an error.
dqcs_handle_t dqcs_sim_recv(dqcs_handle_t sim);

/// Sends a message to the simulated accelerator.
///
/// This is an asynchronous call: nothing happens until `yield()`,
/// `recv()`, or `wait()` is called.
///
/// The `ArbData` handle is optional; if 0 is passed, an empty data object is
/// used. If a handle is passed, it is consumed if and only if the API call
/// succeeds.
dqcs_return_t dqcs_sim_send(dqcs_handle_t sim, dqcs_handle_t data);

/// Starts a program on the simulated accelerator.
///
/// This is an asynchronous call: nothing happens until `yield()`,
/// `recv()`, or `wait()` is called.
///
/// The `ArbData` handle is optional; if 0 is passed, an empty data object is
/// used. If a handle is passed, it is consumed if and only if the API call
/// succeeds.
dqcs_return_t dqcs_sim_start(dqcs_handle_t sim, dqcs_handle_t data);

/// Waits for the simulated accelerator to finish its current program.
///
/// When this succeeds, the return value of the accelerator's `run()`
/// function is returned in the form of a new handle. When it fails, 0 is
/// returned.
///
/// Deadlocks are detected and prevented by returning an error.
dqcs_handle_t dqcs_sim_wait(dqcs_handle_t sim);

/// Writes a reproduction file for the simulation so far.
dqcs_return_t dqcs_sim_write_reproduction_file(dqcs_handle_t sim, const char *filename);

/// Yields to the simulator.
///
/// The simulation runs until it blocks again. This is useful if you want an
/// immediate response to an otherwise asynchronous call through the logging
/// system or some communication channel outside of DQCsim's control.
///
/// This function silently returns immediately if no asynchronous data was
/// pending or if the simulator is waiting for something that has not been
/// sent yet.
dqcs_return_t dqcs_sim_yield(dqcs_handle_t sim);

/// Appends an `ArbCmd` to the list of initialization commands of a plugin
/// thread.
///
/// The `ArbCmd` handle is consumed by this function, and is thus invalidated,
/// if and only if it is successful.
dqcs_return_t dqcs_tcfg_init_cmd(dqcs_handle_t tcfg, dqcs_handle_t cmd);

/// Returns the configured name for the given plugin thread.
///
/// On success, this **returns a newly allocated string containing the
/// name. Free it with `free()` when you're done with it to avoid memory
/// leaks.** On failure (i.e., the handle is invalid) this returns `NULL`.
char *dqcs_tcfg_name(dqcs_handle_t tcfg);

/// Creates a new plugin thread configuration object from a plugin definition.
///
/// The plugin definition handle is consumed by this function.
dqcs_handle_t dqcs_tcfg_new(dqcs_handle_t pdef, const char *name);

/// Creates a new plugin thread configuration object from a callback.
///
/// The callback is called by DQCsim from a dedicated thread when DQCsim wants
/// to start the plugin. The callback must then in some way spawn a plugin
/// process that connects to the provided simulator string. The callback should
/// return only when the process terminates.
dqcs_handle_t dqcs_tcfg_new_raw(dqcs_plugin_type_t plugin_type,
                                const char *name,
                                void (*callback)(void *user_data, const char *simulator),
                                void (*user_free)(void *user_data),
                                void *user_data);

/// Configures a plugin thread to also output its log messages to a file.
///
/// `verbosity` configures the verbosity level for the file only.
dqcs_return_t dqcs_tcfg_tee(dqcs_handle_t tcfg, dqcs_loglevel_t verbosity, const char *filename);

/// Returns the type of the given plugin thread configuration.
dqcs_plugin_type_t dqcs_tcfg_type(dqcs_handle_t tcfg);

/// Returns the configured verbosity for the given plugin thread.
dqcs_loglevel_t dqcs_tcfg_verbosity_get(dqcs_handle_t tcfg);

/// Configures the logging verbosity for the given plugin thread.
dqcs_return_t dqcs_tcfg_verbosity_set(dqcs_handle_t tcfg, dqcs_loglevel_t level);

} // extern "C"

} // namespace raw
} // namespace dqcsim

//*****************************************************************************
// Functions that cannot be implemented in Rust
//*****************************************************************************

// Place log function in namespace if this is the C++ header.
#ifdef _DQCSIM_HDR_CPP_
namespace dqcsim {
namespace raw {
#endif

// Use namespaced C stdlib functions if this is the C++ header.
//! \cond Doxygen_Suppress
#ifdef _DQCSIM_HDR_CPP_
#define _DQCSIM_STD_PREFIX_ ::std::
#else
#define _DQCSIM_STD_PREFIX_
#endif
//! \endcond

/**
 * Sends a log message using the current logger using printf-like formatting.
 *
 * This function is identical to `dqcs_log_raw()`, except instead of a single
 * string it takes a printf-like format string and varargs to compose the
 * message.
 */
static void dqcs_log_format(
  dqcs_loglevel_t level,
  const char *module,
  const char *file,
  uint32_t line,
  const char *fmt,
  ...
)
#if defined(__GNUC__)
__attribute__((format(printf,5,6)))
#endif
;
static void dqcs_log_format(
  dqcs_loglevel_t level,
  const char *module,
  const char *file,
  uint32_t line,
  const char *fmt,
  ...
)
{
  // Figure out the buffer size we need.
  _DQCSIM_STD_PREFIX_ va_list ap;
  va_start(ap, fmt);
  int size = _DQCSIM_STD_PREFIX_ vsnprintf(NULL, 0, fmt, ap) + 1;
  va_end(ap);

  // Allocate the buffer.
  char *buffer = (char*)_DQCSIM_STD_PREFIX_ malloc(size);
  if (buffer == NULL) {
    _DQCSIM_STD_PREFIX_ fprintf(stderr, "Error: failed to allocate buffer for log message!\n");
    return;
  }

  // Perform the actual formatting operation.
  _DQCSIM_STD_PREFIX_ va_list ap2;
   va_start(ap2, fmt);
  _DQCSIM_STD_PREFIX_ vsnprintf(buffer, size, fmt, ap2);
  va_end(ap2);

  // Send to DQCsim.
  if (((int)dqcs_log_raw(level, module, file, line, buffer)) < 0) {
    _DQCSIM_STD_PREFIX_ fprintf(stderr, "Error while trying to log: %s\n", dqcs_error_get());
    _DQCSIM_STD_PREFIX_ fprintf(stderr, "The message was: %s\n", buffer);
    _DQCSIM_STD_PREFIX_ fprintf(stderr, "In %s, %s:%u\n", module, file, line);
  }

  // Don't leak!
  _DQCSIM_STD_PREFIX_ free(buffer);
}

#undef _DQCSIM_STD_PREFIX_

#ifdef _DQCSIM_HDR_CPP_
} // namespace raw
} // namespace dqcsim
#endif

//*****************************************************************************
// Macros
//*****************************************************************************

//! \cond Doxygen_Suppress
#ifndef _DQCSIM_LANGUAGE_
#ifdef _DQCSIM_HDR_CPP_
#define _DQCSIM_LANGUAGE_ "CPP"
#else
#define _DQCSIM_LANGUAGE_ "C"
#endif
#endif
//! \endcond

//! \cond Doxygen_Suppress
#ifndef _DQCSIM_LOGLEVEL_PREFIX_
#ifdef _DQCSIM_HDR_CPP_
#define _DQCSIM_LOGLEVEL_PREFIX_ ::dqcsim::raw::dqcs_loglevel_t::
#else
#define _DQCSIM_LOGLEVEL_PREFIX_
#endif
#endif
//! \endcond

#ifndef dqcs_log_trace
/**
 * Convenience macro for calling `dqcs_log_format()` with trace loglevel and
 * automatically determined function name, filename, and line number.
 */
#define dqcs_log_trace(fmt, ...)                \
  dqcs_log_format(                              \
    _DQCSIM_LOGLEVEL_PREFIX_ DQCS_LOG_TRACE,    \
    _DQCSIM_LANGUAGE_,                          \
    __FILE__,                                   \
    __LINE__,                                   \
    fmt,                                        \
    ##__VA_ARGS__                               \
  )
#endif

#ifndef dqcs_log_debug
/**
 * Convenience macro for calling `dqcs_log_format()` with debug loglevel and
 * automatically determined function name, filename, and line number.
 */
#define dqcs_log_debug(fmt, ...)                \
  dqcs_log_format(                              \
    _DQCSIM_LOGLEVEL_PREFIX_ DQCS_LOG_DEBUG,    \
    _DQCSIM_LANGUAGE_,                          \
    __FILE__,                                   \
    __LINE__,                                   \
    fmt,                                        \
    ##__VA_ARGS__                               \
  )
#endif

#ifndef dqcs_log_info
/**
 * Convenience macro for calling `dqcs_log_format()` with info loglevel and
 * automatically determined function name, filename, and line number.
 */
#define dqcs_log_info(fmt, ...)                 \
  dqcs_log_format(                              \
    _DQCSIM_LOGLEVEL_PREFIX_ DQCS_LOG_INFO,     \
    _DQCSIM_LANGUAGE_,                          \
    __FILE__,                                   \
    __LINE__,                                   \
    fmt,                                        \
    ##__VA_ARGS__                               \
  )
#endif

#ifndef dqcs_log_note
/**
 * Convenience macro for calling `dqcs_log_format()` with note loglevel and
 * automatically determined function name, filename, and line number.
 */
#define dqcs_log_note(fmt, ...)                 \
  dqcs_log_format(                              \
    _DQCSIM_LOGLEVEL_PREFIX_ DQCS_LOG_NOTE,     \
    _DQCSIM_LANGUAGE_,                          \
    __FILE__,                                   \
    __LINE__,                                   \
    fmt,                                        \
    ##__VA_ARGS__                               \
  )
#endif

#ifndef dqcs_log_warn
/**
 * Convenience macro for calling `dqcs_log_format()` with warn loglevel and
 * automatically determined function name, filename, and line number.
 */
#define dqcs_log_warn(fmt, ...)                 \
  dqcs_log_format(                              \
    _DQCSIM_LOGLEVEL_PREFIX_ DQCS_LOG_WARN,     \
    _DQCSIM_LANGUAGE_,                          \
    __FILE__,                                   \
    __LINE__,                                   \
    fmt,                                        \
    ##__VA_ARGS__                               \
  )
#endif

#ifndef dqcs_log_error
/**
 * Convenience macro for calling `dqcs_log_format()` with error loglevel and
 * automatically determined function name, filename, and line number.
 */
#define dqcs_log_error(fmt, ...)                \
  dqcs_log_format(                              \
    _DQCSIM_LOGLEVEL_PREFIX_ DQCS_LOG_ERROR,    \
    _DQCSIM_LANGUAGE_,                          \
    __FILE__,                                   \
    __LINE__,                                   \
    fmt,                                        \
    ##__VA_ARGS__                               \
  )
#endif

#ifndef dqcs_log_fatal
/**
 * Convenience macro for calling `dqcs_log_format()` with fatal loglevel and
 * automatically determined function name, filename, and line number.
 */
#define dqcs_log_fatal(fmt, ...)                \
  dqcs_log_format(                              \
    _DQCSIM_LOGLEVEL_PREFIX_ DQCS_LOG_FATAL,    \
    _DQCSIM_LANGUAGE_,                          \
    __FILE__,                                   \
    __LINE__,                                   \
    fmt,                                        \
    ##__VA_ARGS__                               \
  )
#endif

#ifdef __cplusplus
} // extern "C"
#endif

#ifdef _DQCSIM_HDR_CPP_
#undef _DQCSIM_HDR_CPP_
#endif

#endif
