text:
/*
 * Portions Copyright (c) 2010-Present Couchbase
 * Portions Copyright (c) 2008 Sun Microsystems
 *
 * Use of this software is governed by the Apache License, Version 2.0 and
 * BSD 3 Clause included in the files licenses/APL2.txt and
 * licenses/BSD-3-Clause-Sun-Microsystems.txt
 */
#pragma once

#include "dockey.h"

#include <gsl/gsl-lite.hpp>
#include <memcached/vbucket.h>
#include <platform/socket.h>

#ifndef WIN32
#include <arpa/inet.h>
#endif
#include <cstdint>
#include <stdexcept>
#include <string>

namespace cb::durability {
enum class Level : uint8_t;
} // namespace cb::durability

/**
 * \addtogroup Protocol
 * @{
 */

/**
 * This file contains definitions of the constants and packet formats
 * defined in the binary specification. Please note that you _MUST_ remember
 * to convert each multibyte field to / from network byte order to / from
 * host order.
 */

#include <mcbp/protocol/datatype.h>
#include <mcbp/protocol/dcp_stream_end_status.h>
#include <mcbp/protocol/feature.h>
#include <mcbp/protocol/magic.h>
#include <mcbp/protocol/opcode.h>
#include <mcbp/protocol/request.h>
#include <mcbp/protocol/response.h>
#include <mcbp/protocol/status.h>

// For backward compatibility with old sources

/**
 * Definition of the header structure for a request packet.
 * See section 2
 */
union protocol_binary_request_header {
    cb::mcbp::Request request;
    uint8_t bytes[24];
};

/**
 * Definition of the header structure for a response packet.
 * See section 2
 */
union protocol_binary_response_header {
    cb::mcbp::Response response;
    uint8_t bytes[24];
};

/**
 * Definition of a request-packet containing no extras
 */
typedef union {
    struct {
        protocol_binary_request_header header;
    } message;
    uint8_t bytes[sizeof(protocol_binary_request_header)];
} protocol_binary_request_no_extras;

/**
 * Definition of a response-packet containing no extras
 */
typedef union {
    struct {
        protocol_binary_response_header header;
    } message;
    uint8_t bytes[sizeof(protocol_binary_response_header)];
} protocol_binary_response_no_extras;

/**
 * Definition of the packet used by set, add and replace
 * See section 4
 */
namespace cb::mcbp::request {
#pragma pack(1)
class MutationPayload {
public:
    /// The memcached core keep the flags stored in network byte order
    /// internally as it does not use them for anything else than sending
    /// them back to the client
    uint32_t getFlagsInNetworkByteOrder() const {
        return flags;
    }

    uint32_t getFlags() const {
        return ntohl(flags);
    }

    void setFlags(uint32_t flags) {
        MutationPayload::flags = htonl(flags);
    }
    uint32_t getExpiration() const {
        return ntohl(expiration);
    }
    void setExpiration(uint32_t expiration) {
        MutationPayload::expiration = htonl(expiration);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint32_t flags = 0;
    uint32_t expiration = 0;
};
static_assert(sizeof(MutationPayload) == 8, "Unexpected struct size");

class ArithmeticPayload {
public:
    uint64_t getDelta() const {
        return ntohll(delta);
    }
    void setDelta(uint64_t delta) {
        ArithmeticPayload::delta = htonll(delta);
    }
    uint64_t getInitial() const {
        return ntohll(initial);
    }
    void setInitial(uint64_t initial) {
        ArithmeticPayload::initial = htonll(initial);
    }
    uint32_t getExpiration() const {
        return ntohl(expiration);
    }
    void setExpiration(uint32_t expiration) {
        ArithmeticPayload::expiration = htonl(expiration);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

private:
    uint64_t delta = 0;
    uint64_t initial = 0;
    uint32_t expiration = 0;
};
static_assert(sizeof(ArithmeticPayload) == 20, "Unexpected struct size");

class DeprecatedSetClusterConfigPayload {
public:
    int32_t getRevision() const {
        return ntohl(revision);
    }

    void setRevision(int32_t rev) {
        revision = htonl(rev);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    int32_t revision{};
};
static_assert(sizeof(DeprecatedSetClusterConfigPayload) == 4,
              "Unexpected struct size");

class SetClusterConfigPayload {
public:
    int64_t getEpoch() const {
        return ntohll(epoch);
    }

    void setEpoch(int64_t ep) {
        epoch = htonll(ep);
    }

    int64_t getRevision() const {
        return ntohll(revision);
    }

    void setRevision(int64_t rev) {
        revision = htonll(rev);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    int64_t epoch{0};
    int64_t revision{0};
};
static_assert(sizeof(SetClusterConfigPayload) == 16, "Unexpected struct size");

class VerbosityPayload {
public:
    uint32_t getLevel() const {
        return ntohl(level);
    }
    void setLevel(uint32_t level) {
        VerbosityPayload::level = htonl(level);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint32_t level = 0;
};
static_assert(sizeof(VerbosityPayload) == 4, "Unexpected size");

class TouchPayload {
public:
    uint32_t getExpiration() const {
        return ntohl(expiration);
    }
    void setExpiration(uint32_t expiration) {
        TouchPayload::expiration = htonl(expiration);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint32_t expiration = 0;
};
static_assert(sizeof(TouchPayload) == 4, "Unexpected size");
using GatPayload = TouchPayload;
using GetLockedPayload = TouchPayload;

class SetCtrlTokenPayload {
public:
    uint64_t getCas() const {
        return ntohll(cas);
    }
    void setCas(uint64_t cas) {
        SetCtrlTokenPayload::cas = htonll(cas);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint64_t cas = 0;
};
static_assert(sizeof(SetCtrlTokenPayload) == 8, "Unexpected size");

#pragma pack()
} // namespace cb::mcbp::request

/**
 * Definitions for extended (flexible) metadata
 *
 * @1: Flex Code to identify the number of extended metadata fields
 * @2: Size of the Flex Code, set to 1 byte
 * @3: Current size of extended metadata
 */
typedef enum {
    FLEX_META_CODE = 0x01,
    FLEX_DATA_OFFSET = 1,
    EXT_META_LEN = 1
} protocol_binary_flexmeta;

/**
 * Definitions of sub-document path flags (this is a bitmap)
 */
typedef enum : uint8_t {
    /** No flags set */
    SUBDOC_FLAG_NONE = 0x0,

    /** (Mutation) Should non-existent intermediate paths be created? */
    SUBDOC_FLAG_MKDIR_P = 0x01,

    /**
     * 0x02 is unused
     */

    /**
     * If set, the path refers to an Extended Attribute (XATTR).
     * If clear, the path refers to a path inside the document body.
     */
    SUBDOC_FLAG_XATTR_PATH = 0x04,

    /**
     * 0x08 is unused
     */

    /**
     * Expand macro values inside extended attributes. The request is
     * invalid if this flag is set without SUBDOC_FLAG_XATTR_PATH being
     * set.
     */
    SUBDOC_FLAG_EXPAND_MACROS = 0x10,

} protocol_binary_subdoc_flag;

namespace mcbp::subdoc {

/**
 * Definitions of sub-document doc flags (this is a bitmap).
 */
enum class doc_flag : uint8_t {
    None = 0x0,

    /**
     * (Mutation) Create the document if it does not exist. Implies
     * SUBDOC_FLAG_MKDIR_P and Set (upsert) mutation semantics. Not valid
     * with Add.
     */
    Mkdoc = 0x1,

    /**
     * (Mutation) Add the document only if it does not exist. Implies
     * SUBDOC_FLAG_MKDIR_P. Not valid with Mkdoc.
     */
    Add = 0x02,

    /**
     * Allow access to XATTRs for deleted documents (instead of
     * returning KEY_ENOENT). The result of mutations on a deleted
     * document is still a deleted document unless ReviveDocument is
     * being used.
     */
    AccessDeleted = 0x04,

    /**
     * (Mutation) Used with Mkdoc / Add; if the document does not exist then
     * create it in the Deleted state, instead of the normal Alive state.
     * Not valid unless Mkdoc or Add specified.
     */
    CreateAsDeleted = 0x08,

    /**
     * (Mutation) If the document exists and isn't deleted the operation
     * will fail with SubdocCanOnlyReviveDeletedDocuments. If the input
     * document _is_ deleted the result of the operation will store the
     * document as a "live" document instead of a deleted document.
     */
    ReviveDocument = 0x10,
};

/**
 * Used for validation at parsing the doc-flags.
 * The value depends on how many bits the doc_flag enum is actually using and
 * must change accordingly.
 */
static constexpr uint8_t extrasDocFlagMask = 0xe0;

} // namespace mcbp::subdoc

/**
 * Definition of the packet used by SUBDOCUMENT single-path commands.
 *
 * The path, which is always required, is in the Body, after the Key.
 *
 *   Header:                        24 @0: <protocol_binary_request_header>
 *   Extras:
 *     Sub-document pathlen          2 @24: <variable>
 *     Sub-document flags            1 @26: <protocol_binary_subdoc_flag>
 *     Expiry                        4 @27: (Optional) Mutations only. The
 *                                          ttl
 *     Sub-document doc flags        1 @27: (Optional) @31 if expiry is
 *                                          set. Note these are the
 *                                          subdocument doc flags not the
 *                                          flag section in the document.
 *   Body:
 *     Key                      keylen @27: <variable>
 *     Path                    pathlen @27+keylen: <variable>
 *     Value to insert/replace
 *               vallen-keylen-pathlen @27+keylen+pathlen: [variable]
 */
typedef union {
    struct {
        protocol_binary_request_header header;
        struct {
            uint16_t pathlen; // Length in bytes of the sub-doc path.
            uint8_t subdoc_flags; // See protocol_binary_subdoc_flag
            /* uint32_t expiry     (optional for mutations only - present
                                    if extlen == 7 or extlen == 8) */
            /* uint8_t doc_flags   (optional - present if extlen == 4 or
                                    extlen == 8)  Note these are the
                                    subdocument doc flags not the flag
                                    \section in the document. */
        } extras;
    } message;
    uint8_t bytes[sizeof(protocol_binary_request_header) + 3];
} protocol_binary_request_subdocument;

/** Definition of the packet used by SUBDOCUMENT responses.
 */
typedef union {
    struct {
        protocol_binary_response_header header;
    } message;
    uint8_t bytes[sizeof(protocol_binary_response_header)];
} protocol_binary_response_subdocument;

/**
 * Definition of the request packets used by SUBDOCUMENT multi-path commands.
 *
 * Multi-path sub-document commands differ from single-path in that they
 * encode a series of multiple paths to operate on (from a single key).
 * There are two multi-path commands - MULTI_LOOKUP and MULTI_MUTATION.
 * - MULTI_LOOKUP consists of variable number of subdoc lookup commands
 *                (SUBDOC_GET or SUBDOC_EXISTS).
 * - MULTI_MUTATION consists of a variable number of subdoc mutation
 *                  commands (i.e. all subdoc commands apart from
 *                  SUBDOC_{GET,EXISTS}).
 *
 * Each path to be operated on is specified by an Operation Spec, which are
 * contained in the body. This defines the opcode, path, and value
 * (for mutations).
 *
 * A maximum of MULTI_MAX_PATHS paths (operations) can be encoded in a
 * single multi-path command.
 *
 *  SUBDOC_MULTI_LOOKUP:
 *    Header:                24 @0:  <protocol_binary_request_header>
 *    Extras:            0 or 1 @24: (optional) doc_flags. Note these are
 *                                   the subdocument doc flags not the flag
 *                                   section in the document.
 *    Body:         <variable>  @24:
 *        Key            keylen @24: <variable>
 *        1..MULTI_MAX_PATHS [Lookup Operation Spec]
 *
 *        Lookup Operation Spec:
 *                            1 @0 : Opcode
 *                            1 @1 : Flags
 *                            2 @2 : Path Length
 *                      pathlen @4 : Path
 */
static const int PROTOCOL_BINARY_SUBDOC_MULTI_MAX_PATHS = 16;

typedef struct {
    cb::mcbp::ClientOpcode opcode;
    uint8_t flags;
    uint16_t pathlen;
    /* uint8_t path[pathlen] */
} protocol_binary_subdoc_multi_lookup_spec;

typedef protocol_binary_request_no_extras
        protocol_binary_request_subdocument_multi_lookup;

/*
 *
 * SUBDOC_MULTI_MUTATION
 *    Header:                24 @0:  <protocol_binary_request_header>
 *    Extras:            0 OR 4 @24: (optional) expiration
 *                       0 OR 1 @24: (optional) doc_flags. Note these are
 *                                   the subdocument doc flags not the
 *                                   flag section in the document.
 *    Body:           variable  @24 + extlen:
 *        Key            keylen @24: <variable>
 *        1..MULTI_MAX_PATHS [Mutation Operation Spec]
 *
 *        Mutation Operation Spec:
 *                            1 @0         : Opcode
 *                            1 @1         : Flags
 *                            2 @2         : Path Length
 *                            4 @4         : Value Length
 *                      pathlen @8         : Path
 *                       vallen @8+pathlen : Value
 */
typedef struct {
    cb::mcbp::ClientOpcode opcode;
    uint8_t flags;
    uint16_t pathlen;
    uint32_t valuelen;
    /* uint8_t path[pathlen] */
    /* uint8_t value[valuelen]  */
} protocol_binary_subdoc_multi_mutation_spec;

typedef protocol_binary_request_no_extras
        protocol_binary_request_subdocument_multi_mutation;

/**
 * Definition of the response packets used by SUBDOCUMENT multi-path
 * commands.
 *
 * SUBDOC_MULTI_LOOKUP - Body consists of a series of lookup_result structs,
 *                       one per lookup_spec in the request.
 *
 * Lookup Result:
 *                            2 @0 : status
 *                            4 @2 : resultlen
 *                    resultlen @6 : result
 */
typedef struct {
    protocol_binary_request_header header;
    /* Variable-length 1..PROTOCOL_BINARY_SUBDOC_MULTI_MAX_PATHS */
    protocol_binary_subdoc_multi_lookup_spec body[1];
} protocol_binary_response_subdoc_multi_lookup;

/**
 * SUBDOC_MULTI_MUTATION response
 *
 * Extras is either 0 or 16 if MUTATION_SEQNO is enabled.
 *
 * Body consists of a variable number of subdoc_multi_mutation_result_spec
 * structs:
 *
 * On success (header.status == SUCCESS), zero or more result specs, one for
 * each multi_mutation_spec which wishes to return a value.
 *
 * Mutation Result (success):
 *   [0..N] of:
 *                   1 @0 : index - Index of multi_mutation spec this result
 *                          corresponds to.
 *                   2 @1 : status - Status of the mutation (should always
 *                          be SUCCESS for successful multi-mutation
 *                          requests).
 *                   4 @3 : resultlen - Result value length
 *           resultlen @7 : Value payload
 *

 * On one of more of the mutation specs failing, there is exactly one
 * result spec, specifying the index and status code of the first failing
 * mutation spec.
 *
 * Mutation Result (failure):
 *   1 of:
 *                   1 @0 : index - Index of multi_mutation spec this result
 *                          corresponds to.
 *                   2 @1 : status - Status of the mutation (should always be
 *                          !SUCCESS for failures).
 *
 * (Note: On failure the multi_mutation_result_spec only includes the
 *        first two fields).
 */
typedef union {
    struct {
        protocol_binary_response_header header;
    } message;
    uint8_t bytes[sizeof(protocol_binary_response_header)];
} protocol_binary_response_subdoc_multi_mutation;

/* DCP related stuff */

namespace cb::mcbp {
namespace request {
#pragma pack(1)
class DcpOpenPayload {
public:
    uint32_t getSeqno() const {
        return ntohl(seqno);
    }
    void setSeqno(uint32_t seqno) {
        DcpOpenPayload::seqno = htonl(seqno);
    }
    uint32_t getFlags() const {
        return ntohl(flags);
    }
    void setFlags(uint32_t flags) {
        DcpOpenPayload::flags = htonl(flags);
    }

    // Flags is a bitmask where the following values are used:

    /**
     * If set a Producer connection should be opened, if clear a Consumer
     * connection should be opened.
     */
    static const uint32_t Producer = 1;
    /// Invalid - should not be set (Previously the Notifier flag)
    static const uint32_t Invalid = 2;
    /**
     * Indicate that the server include the documents' XATTRs
     * within mutation and deletion bodies.
     */
    static const uint32_t IncludeXattrs = 4;
    /**
     * Indicate that the server should strip off the values (note,
     * if you add INCLUDE_XATTR those will be present)
     */
    static const uint32_t NoValue = 8;
    static const uint32_t Unused = 16;
    /**
     * Request that DCP delete message include the time the a delete was
     * persisted. This only applies to deletes being backfilled from storage,
     * in-memory deletes will have a delete time of 0
     */
    static const uint32_t IncludeDeleteTimes = 32;

    /**
     * Indicates that the server should strip off the values, but return the
     * datatype of the underlying document (note, if you add
     * INCLUDE_XATTR those will be present).
     * Note this differs from DCP_OPEN_NO_VALUE in that the datatype field will
     * contain the underlying datatype of the document; not the datatype of the
     * transmitted payload.
     * This flag can be used to obtain the full, original datatype for a
     * document without the user's value. Not valid to specify with
     * DCP_OPEN_NO_VALUE.
     */
    static const uint32_t NoValueWithUnderlyingDatatype = 64;

    /// Requst PiTR for the connection (only legal for Producers)
    static const uint32_t PiTR = 128;

    /**
     * Indicates that the server includes the document UserXattrs within
     * deletion values.
     */
    static const uint32_t IncludeDeletedUserXattrs = 256;

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint32_t seqno = 0;
    uint32_t flags = 0;
};
static_assert(sizeof(DcpOpenPayload) == 8, "Unexpected struct size");
} // namespace request

namespace response {
class DcpAddStreamPayload {
public:
    uint32_t getOpaque() const {
        return ntohl(opaque);
    }
    void setOpaque(uint32_t opaque) {
        DcpAddStreamPayload::opaque = htonl(opaque);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint32_t opaque = 0;
};
static_assert(sizeof(DcpAddStreamPayload) == 4, "Unexpected struct size");
} // namespace response

namespace request {
class DcpAddStreamPayload {
public:
    uint32_t getFlags() const {
        return ntohl(flags);
    }
    void setFlags(uint32_t flags) {
        DcpAddStreamPayload::flags = htonl(flags);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
/*
 * The following flags are defined
 */
#define DCP_ADD_STREAM_FLAG_TAKEOVER 1
#define DCP_ADD_STREAM_FLAG_DISKONLY 2
#define DCP_ADD_STREAM_FLAG_LATEST 4
/**
 * This flag is not used anymore, and should NOT be
 * set. It is replaced by DCP_OPEN_NO_VALUE.
 */
#define DCP_ADD_STREAM_FLAG_NO_VALUE 8
/**
 * Indicate the server to add stream only if the vbucket
 * is active.
 * If the vbucket is not active, the stream request fails with
 * error cb::engine_errc::not_my_vbucket
 */
#define DCP_ADD_STREAM_ACTIVE_VB_ONLY 16
/**
 * Indicate the server to check for vb_uuid match even at start_seqno 0 before
 * adding the stream successfully.
 * If the flag is set and there is a vb_uuid mismatch at start_seqno 0, then
 * the server returns cb::engine_errc::rollback error.
 */
#define DCP_ADD_STREAM_STRICT_VBUUID 32
    uint32_t flags = 0;
};
static_assert(sizeof(DcpAddStreamPayload) == 4, "Unexpected struct size");

class DcpStreamReqPayload {
public:
    uint32_t getFlags() const {
        return ntohl(flags);
    }
    void setFlags(uint32_t flags) {
        DcpStreamReqPayload::flags = htonl(flags);
    }
    uint32_t getReserved() const {
        return ntohl(reserved);
    }
    void setReserved(uint32_t reserved) {
        DcpStreamReqPayload::reserved = htonl(reserved);
    }
    uint64_t getStartSeqno() const {
        return ntohll(start_seqno);
    }
    void setStartSeqno(uint64_t start_seqno) {
        DcpStreamReqPayload::start_seqno = htonll(start_seqno);
    }
    uint64_t getEndSeqno() const {
        return ntohll(end_seqno);
    }
    void setEndSeqno(uint64_t end_seqno) {
        DcpStreamReqPayload::end_seqno = htonll(end_seqno);
    }
    uint64_t getVbucketUuid() const {
        return ntohll(vbucket_uuid);
    }
    void setVbucketUuid(uint64_t vbucket_uuid) {
        DcpStreamReqPayload::vbucket_uuid = htonll(vbucket_uuid);
    }
    uint64_t getSnapStartSeqno() const {
        return ntohll(snap_start_seqno);
    }
    void setSnapStartSeqno(uint64_t snap_start_seqno) {
        DcpStreamReqPayload::snap_start_seqno = htonll(snap_start_seqno);
    }
    uint64_t getSnapEndSeqno() const {
        return ntohll(snap_end_seqno);
    }
    void setSnapEndSeqno(uint64_t snap_end_seqno) {
        DcpStreamReqPayload::snap_end_seqno = htonll(snap_end_seqno);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint32_t flags = 0;
    uint32_t reserved = 0;
    uint64_t start_seqno = 0;
    uint64_t end_seqno = 0;
    uint64_t vbucket_uuid = 0;
    uint64_t snap_start_seqno = 0;
    uint64_t snap_end_seqno = 0;
};
static_assert(sizeof(DcpStreamReqPayload) == 48, "Unexpected struct size");

class DcpStreamEndPayload {
public:
    DcpStreamEndStatus getStatus() const {
        return DcpStreamEndStatus(ntohl(status));
    }
    void setStatus(DcpStreamEndStatus status) {
        DcpStreamEndPayload::status = htonl(uint32_t(status));
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    /**
     * Note the following is maintained in network/big endian
     * see protocol/dcp_stream_end_status.h for values
     */
    uint32_t status = 0;
};
static_assert(sizeof(DcpStreamEndPayload) == 4, "Unexpected struct size");

class DcpSnapshotMarkerV1Payload {
public:
    uint64_t getStartSeqno() const {
        return ntohll(start_seqno);
    }
    void setStartSeqno(uint64_t start_seqno) {
        DcpSnapshotMarkerV1Payload::start_seqno = htonll(start_seqno);
    }
    uint64_t getEndSeqno() const {
        return ntohll(end_seqno);
    }
    void setEndSeqno(uint64_t end_seqno) {
        DcpSnapshotMarkerV1Payload::end_seqno = htonll(end_seqno);
    }
    uint32_t getFlags() const {
        return ntohl(flags);
    }
    void setFlags(uint32_t flags) {
        DcpSnapshotMarkerV1Payload::flags = htonl(flags);
    }
    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint64_t start_seqno = 0;
    uint64_t end_seqno = 0;
    uint32_t flags = 0;
};
static_assert(sizeof(DcpSnapshotMarkerV1Payload) == 20,
              "Unexpected struct size");

enum class DcpSnapshotMarkerFlag : uint32_t {
    Memory = 0x01,
    Disk = 0x02,
    Checkpoint = 0x04,
    Acknowledge = 0x08
};

enum class DcpSnapshotMarkerV2xVersion : uint8_t { Zero = 0, One = 1 };

// Version 2.x
class DcpSnapshotMarkerV2xPayload {
public:
    explicit DcpSnapshotMarkerV2xPayload(DcpSnapshotMarkerV2xVersion v)
        : version(v) {
    }
    DcpSnapshotMarkerV2xVersion getVersion() const {
        return version;
    }
    void setVersion(DcpSnapshotMarkerV2xVersion v) {
        version = v;
    }
    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    DcpSnapshotMarkerV2xVersion version{DcpSnapshotMarkerV2xVersion::Zero};
};
static_assert(sizeof(DcpSnapshotMarkerV2xPayload) == 1,
              "Unexpected struct size");

class DcpSnapshotMarkerV2_0Value : public DcpSnapshotMarkerV1Payload {
public:
    uint64_t getMaxVisibleSeqno() const {
        return ntohll(maxVisibleSeqno);
    }
    void setMaxVisibleSeqno(uint64_t maxVisibleSeqno) {
        DcpSnapshotMarkerV2_0Value::maxVisibleSeqno = htonll(maxVisibleSeqno);
    }
    uint64_t getHighCompletedSeqno() const {
        return ntohll(highCompletedSeqno);
    }
    void setHighCompletedSeqno(uint64_t highCompletedSeqno) {
        DcpSnapshotMarkerV2_0Value::highCompletedSeqno =
                htonll(highCompletedSeqno);
    }
    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint64_t maxVisibleSeqno{0};
    uint64_t highCompletedSeqno{0};
};
static_assert(sizeof(DcpSnapshotMarkerV2_0Value) == 36,
              "Unexpected struct size");

class DcpSnapshotMarkerV2_1Value : public DcpSnapshotMarkerV2_0Value {
public:
    uint64_t getTimestamp() const {
        return ntohll(timestamp);
    }
    void setTimestamp(uint64_t value) {
        timestamp = htonll(value);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint64_t timestamp{0};
};
static_assert(sizeof(DcpSnapshotMarkerV2_1Value) == 44,
              "Unexpected struct size");

class DcpMutationPayload {
public:
    DcpMutationPayload() = default;
    DcpMutationPayload(uint64_t by_seqno,
                       uint64_t rev_seqno,
                       uint32_t flags,
                       uint32_t expiration,
                       uint32_t lock_time,
                       uint8_t nru)
        : by_seqno(htonll(by_seqno)),
          rev_seqno(htonll(rev_seqno)),
          flags(flags),
          expiration(htonl(expiration)),
          lock_time(htonl(lock_time)),
          nru(nru) {
    }
    uint64_t getBySeqno() const {
        return ntohll(by_seqno);
    }
    void setBySeqno(uint64_t by_seqno) {
        DcpMutationPayload::by_seqno = htonll(by_seqno);
    }
    uint64_t getRevSeqno() const {
        return ntohll(rev_seqno);
    }
    void setRevSeqno(uint64_t rev_seqno) {
        DcpMutationPayload::rev_seqno = htonll(rev_seqno);
    }
    uint32_t getFlags() const {
        return flags;
    }
    void setFlags(uint32_t flags) {
        DcpMutationPayload::flags = flags;
    }
    uint32_t getExpiration() const {
        return ntohl(expiration);
    }
    void setExpiration(uint32_t expiration) {
        DcpMutationPayload::expiration = htonl(expiration);
    }
    uint32_t getLockTime() const {
        return ntohl(lock_time);
    }
    void setLockTime(uint32_t lock_time) {
        DcpMutationPayload::lock_time = htonl(lock_time);
    }
    uint16_t getNmeta() const {
        return ntohs(nmeta);
    }
    uint8_t getNru() const {
        return nru;
    }
    void setNru(uint8_t nru) {
        DcpMutationPayload::nru = nru;
    }

    std::string_view getBuffer() const {
        return {reinterpret_cast<const char*>(this), sizeof(*this)};
    }

protected:
    uint64_t by_seqno = 0;
    uint64_t rev_seqno = 0;
    uint32_t flags = 0;
    uint32_t expiration = 0;
    uint32_t lock_time = 0;
    /// We don't set this anymore, but old servers may send it to us
    /// but we'll ignore it
    const uint16_t nmeta = 0;
    uint8_t nru = 0;
};
static_assert(sizeof(DcpMutationPayload) == 31, "Unexpected struct size");

class DcpDeletionV1Payload {
public:
    DcpDeletionV1Payload(uint64_t _by_seqno, uint64_t _rev_seqno)
        : by_seqno(htonll(_by_seqno)), rev_seqno(htonll(_rev_seqno)) {
    }
    uint64_t getBySeqno() const {
        return ntohll(by_seqno);
    }
    void setBySeqno(uint64_t by_seqno) {
        DcpDeletionV1Payload::by_seqno = htonll(by_seqno);
    }
    uint64_t getRevSeqno() const {
        return ntohll(rev_seqno);
    }
    void setRevSeqno(uint64_t rev_seqno) {
        DcpDeletionV1Payload::rev_seqno = htonll(rev_seqno);
    }
    uint16_t getNmeta() const {
        return ntohs(nmeta);
    }
    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint64_t by_seqno = 0;
    uint64_t rev_seqno = 0;
    const uint16_t nmeta = 0;
};
static_assert(sizeof(DcpDeletionV1Payload) == 18, "Unexpected struct size");

class DcpDeleteRequestV1 {
public:
    DcpDeleteRequestV1(uint32_t opaque,
                       Vbid vbucket,
                       uint64_t cas,
                       uint16_t keyLen,
                       uint32_t valueLen,
                       protocol_binary_datatype_t datatype,
                       uint64_t bySeqno,
                       uint64_t revSeqno)
        : req{}, body(bySeqno, revSeqno) {
        req.setMagic(cb::mcbp::Magic::ClientRequest);
        req.setOpcode(cb::mcbp::ClientOpcode::DcpDeletion);
        req.setExtlen(gsl::narrow<uint8_t>(sizeof(body)));
        req.setKeylen(keyLen);
        req.setBodylen(gsl::narrow<uint32_t>(sizeof(body) + keyLen + valueLen));
        req.setOpaque(opaque);
        req.setVBucket(vbucket);
        req.setCas(cas);
        req.setDatatype(cb::mcbp::Datatype(datatype));
    }

protected:
    cb::mcbp::Request req;
    DcpDeletionV1Payload body;
};
static_assert(sizeof(DcpDeleteRequestV1) == 42, "Unexpected struct size");

class DcpDeletionV2Payload {
public:
    DcpDeletionV2Payload(uint64_t by_seqno,
                         uint64_t rev_seqno,
                         uint32_t delete_time)
        : by_seqno(htonll(by_seqno)),
          rev_seqno(htonll(rev_seqno)),
          delete_time(htonl(delete_time)) {
    }
    uint64_t getBySeqno() const {
        return ntohll(by_seqno);
    }
    void setBySeqno(uint64_t by_seqno) {
        DcpDeletionV2Payload::by_seqno = htonll(by_seqno);
    }
    uint64_t getRevSeqno() const {
        return ntohll(rev_seqno);
    }
    void setRevSeqno(uint64_t rev_seqno) {
        DcpDeletionV2Payload::rev_seqno = htonll(rev_seqno);
    }
    uint32_t getDeleteTime() const {
        return ntohl(delete_time);
    }
    void setDeleteTime(uint32_t delete_time) {
        DcpDeletionV2Payload::delete_time = htonl(delete_time);
    }
    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint64_t by_seqno = 0;
    uint64_t rev_seqno = 0;
    uint32_t delete_time = 0;
    const uint8_t unused = 0;
};
static_assert(sizeof(DcpDeletionV2Payload) == 21, "Unexpected struct size");

class DcpDeleteRequestV2 {
public:
    DcpDeleteRequestV2(uint32_t opaque,
                       Vbid vbucket,
                       uint64_t cas,
                       uint16_t keyLen,
                       uint32_t valueLen,
                       protocol_binary_datatype_t datatype,
                       uint64_t bySeqno,
                       uint64_t revSeqno,
                       uint32_t deleteTime)
        : req{}, body(bySeqno, revSeqno, deleteTime) {
        req.setMagic(cb::mcbp::Magic::ClientRequest);
        req.setOpcode(cb::mcbp::ClientOpcode::DcpDeletion);
        req.setExtlen(gsl::narrow<uint8_t>(sizeof(body)));
        req.setKeylen(keyLen);
        req.setBodylen(gsl::narrow<uint32_t>(sizeof(body) + keyLen + valueLen));
        req.setOpaque(opaque);
        req.setVBucket(vbucket);
        req.setCas(cas);
        req.setDatatype(cb::mcbp::Datatype(datatype));
    }

protected:
    cb::mcbp::Request req;
    DcpDeletionV2Payload body;
};
static_assert(sizeof(DcpDeleteRequestV2) == 45, "Unexpected struct size");

class DcpExpirationPayload {
public:
    DcpExpirationPayload() = default;
    DcpExpirationPayload(uint64_t by_seqno,
                         uint64_t rev_seqno,
                         uint32_t delete_time)
        : by_seqno(htonll(by_seqno)),
          rev_seqno(htonll(rev_seqno)),
          delete_time(htonl(delete_time)) {
    }

    uint64_t getBySeqno() const {
        return ntohll(by_seqno);
    }
    void setBySeqno(uint64_t by_seqno) {
        DcpExpirationPayload::by_seqno = htonll(by_seqno);
    }
    uint64_t getRevSeqno() const {
        return ntohll(rev_seqno);
    }
    void setRevSeqno(uint64_t rev_seqno) {
        DcpExpirationPayload::rev_seqno = htonll(rev_seqno);
    }
    uint32_t getDeleteTime() const {
        return ntohl(delete_time);
    }
    void setDeleteTime(uint32_t delete_time) {
        DcpExpirationPayload::delete_time = htonl(delete_time);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint64_t by_seqno = 0;
    uint64_t rev_seqno = 0;
    uint32_t delete_time = 0;
};
static_assert(sizeof(DcpExpirationPayload) == 20, "Unexpected struct size");

class DcpSetVBucketState {
public:
    uint8_t getState() const {
        return state;
    }
    void setState(uint8_t state) {
        DcpSetVBucketState::state = state;
    }

    bool isValid() const {
        return is_valid_vbucket_state_t(state);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint8_t state;
};
static_assert(sizeof(DcpSetVBucketState) == 1, "Unexpected struct size");

class DcpBufferAckPayload {
public:
    uint32_t getBufferBytes() const {
        return ntohl(buffer_bytes);
    }
    void setBufferBytes(uint32_t buffer_bytes) {
        DcpBufferAckPayload::buffer_bytes = htonl(buffer_bytes);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint32_t buffer_bytes = 0;
};

static_assert(sizeof(DcpBufferAckPayload) == 4, "Unexpected struct size");

enum class DcpOsoSnapshotFlags : uint32_t {
    Start = 0x01,
    End = 0x02,
};

class DcpOsoSnapshotPayload {
public:
    explicit DcpOsoSnapshotPayload(uint32_t flags) : flags(htonl(flags)) {
    }
    uint32_t getFlags() const {
        return ntohl(flags);
    }
    void setFlags(uint32_t flags) {
        DcpOsoSnapshotPayload::flags = htonl(flags);
    }
    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint32_t flags = 0;
};
static_assert(sizeof(DcpOsoSnapshotPayload) == 4, "Unexpected struct size");

class DcpSeqnoAdvancedPayload {
public:
    explicit DcpSeqnoAdvancedPayload(uint64_t seqno) : by_seqno(htonll(seqno)) {
    }
    [[nodiscard]] uint64_t getSeqno() const {
        return ntohll(by_seqno);
    }
    void setSeqno(uint64_t seqno) {
        DcpSeqnoAdvancedPayload::by_seqno = htonll(seqno);
    }
    [[nodiscard]] cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint64_t by_seqno = 0;
};
static_assert(sizeof(DcpSeqnoAdvancedPayload) == 8, "Unexpected struct size");

#pragma pack()
} // namespace request
} // namespace cb::mcbp

/**
 * Events that the system may send
 */
namespace mcbp::systemevent {

enum class id : uint32_t {
    CreateCollection = 0,
    DeleteCollection = 1,
    FlushCollection = 2,
    CreateScope = 3,
    DropScope = 4
};

enum class version : uint8_t { version0 = 0, version1 = 1 };
} // namespace mcbp::systemevent

namespace cb::mcbp::request {
#pragma pack(1)

class DcpSystemEventPayload {
public:
    DcpSystemEventPayload() = default;
    DcpSystemEventPayload(uint64_t by_seqno,
                          ::mcbp::systemevent::id event,
                          ::mcbp::systemevent::version version)
        : by_seqno(htonll(by_seqno)),
          event(htonl(static_cast<uint32_t>(event))),
          version(static_cast<uint8_t>(version)) {
    }

    uint64_t getBySeqno() const {
        return ntohll(by_seqno);
    }
    void setBySeqno(uint64_t by_seqno) {
        DcpSystemEventPayload::by_seqno = htonll(by_seqno);
    }
    uint32_t getEvent() const {
        return ntohl(event);
    }
    void setEvent(uint32_t event) {
        DcpSystemEventPayload::event = htonl(event);
    }
    uint8_t getVersion() const {
        return version;
    }
    void setVersion(uint8_t version) {
        DcpSystemEventPayload::version = version;
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

    /**
     * Validate that the uint32_t event field represents a valid systemevent::id
     */
    bool isValidEvent() const {
        using ::mcbp::systemevent::id;
        switch (id(getEvent())) {
        case id::CreateCollection:
        case id::DeleteCollection:
        case id::FlushCollection:
        case id::CreateScope:
        case id::DropScope:
            return true;
        }
        return false;
    }

    /**
     * Validate that the uint8_t version represents a valid systemevent::version
     */
    bool isValidVersion() const {
        using ::mcbp::systemevent::version;
        switch (version(getVersion())) {
        case version::version0:
        case version::version1:
            return true;
        }
        return false;
    }

protected:
    uint64_t by_seqno = 0;
    uint32_t event = 0;
    uint8_t version = 0;
};
static_assert(sizeof(DcpSystemEventPayload) == 13, "Unexpected struct size");

class DcpPreparePayload {
public:
    DcpPreparePayload() = default;
    DcpPreparePayload(uint64_t by_seqno,
                      uint64_t rev_seqno,
                      uint32_t flags,
                      uint32_t expiration,
                      uint32_t lock_time,
                      uint8_t nru)
        : by_seqno(htonll(by_seqno)),
          rev_seqno(htonll(rev_seqno)),
          flags(flags),
          expiration(htonl(expiration)),
          lock_time(htonl(lock_time)),
          nru(nru) {
    }
    uint64_t getBySeqno() const {
        return ntohll(by_seqno);
    }
    void setBySeqno(uint64_t by_seqno) {
        DcpPreparePayload::by_seqno = htonll(by_seqno);
    }
    uint64_t getRevSeqno() const {
        return ntohll(rev_seqno);
    }
    void setRevSeqno(uint64_t rev_seqno) {
        DcpPreparePayload::rev_seqno = htonll(rev_seqno);
    }
    uint32_t getFlags() const {
        return flags;
    }
    void setFlags(uint32_t flags) {
        DcpPreparePayload::flags = flags;
    }
    uint32_t getExpiration() const {
        return ntohl(expiration);
    }
    void setExpiration(uint32_t expiration) {
        DcpPreparePayload::expiration = htonl(expiration);
    }
    uint32_t getLockTime() const {
        return ntohl(lock_time);
    }
    void setLockTime(uint32_t lock_time) {
        DcpPreparePayload::lock_time = htonl(lock_time);
    }
    uint8_t getNru() const {
        return nru;
    }
    void setNru(uint8_t nru) {
        DcpPreparePayload::nru = nru;
    }

    uint8_t getDeleted() const {
        return deleted;
    }
    void setDeleted(uint8_t deleted) {
        DcpPreparePayload::deleted = deleted;
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

    cb::durability::Level getDurabilityLevel() const;

    void setDurabilityLevel(cb::durability::Level level);

protected:
    uint64_t by_seqno = 0;
    uint64_t rev_seqno = 0;
    uint32_t flags = 0;
    uint32_t expiration = 0;
    uint32_t lock_time = 0;
    uint8_t nru = 0;
    // set to true if this is a document deletion
    uint8_t deleted = 0;
    uint8_t durability_level = 0;
};

static_assert(sizeof(DcpPreparePayload) == 31, "Unexpected struct size");

class DcpSeqnoAcknowledgedPayload {
public:
    explicit DcpSeqnoAcknowledgedPayload(uint64_t prepared)
        : prepared_seqno(htonll(prepared)) {
    }

    uint64_t getPreparedSeqno() const {
        return ntohll(prepared_seqno);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    // Stored in network order.
    uint64_t prepared_seqno = 0;
};
static_assert(sizeof(DcpSeqnoAcknowledgedPayload) == 8,
              "Unexpected struct size");

class DcpCommitPayload {
public:
    DcpCommitPayload(uint64_t prepared, uint64_t committed)
        : prepared_seqno(htonll(prepared)), commit_seqno(htonll(committed)) {
    }

    uint64_t getPreparedSeqno() const {
        return ntohll(prepared_seqno);
    }
    void setPreparedSeqno(uint64_t prepared_seqno) {
        DcpCommitPayload::prepared_seqno = htonll(prepared_seqno);
    }
    uint64_t getCommitSeqno() const {
        return ntohll(commit_seqno);
    }
    void setCommitSeqno(uint64_t commit_seqno) {
        DcpCommitPayload::commit_seqno = htonll(commit_seqno);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint64_t prepared_seqno = 0;
    uint64_t commit_seqno = 0;
};
static_assert(sizeof(DcpCommitPayload) == 16, "Unexpected struct size");

class DcpAbortPayload {
public:
    DcpAbortPayload(uint64_t prepared, uint64_t aborted)
        : prepared_seqno(htonll(prepared)), abort_seqno(htonll(aborted)) {
    }

    uint64_t getPreparedSeqno() const {
        return ntohll(prepared_seqno);
    }

    void setPreparedSeqno(uint64_t seqno) {
        prepared_seqno = htonll(seqno);
    }

    uint64_t getAbortSeqno() const {
        return ntohll(abort_seqno);
    }

    void setAbortSeqno(uint64_t seqno) {
        abort_seqno = htonll(seqno);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint64_t prepared_seqno = 0;
    uint64_t abort_seqno = 0;
};
static_assert(sizeof(DcpAbortPayload) == 16, "Unexpected struct size");

class SetParamPayload {
public:
    enum class Type : uint32_t {
        Flush = 1,
        Replication,
        Checkpoint,
        Dcp,
        Vbucket
    };

    Type getParamType() const {
        return static_cast<Type>(ntohl(param_type));
    }

    void setParamType(Type param_type) {
        SetParamPayload::param_type = htonl(static_cast<uint32_t>(param_type));
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

    bool validate() const {
        switch (getParamType()) {
        case Type::Flush:
        case Type::Replication:
        case Type::Checkpoint:
        case Type::Dcp:
        case Type::Vbucket:
            return true;
        }
        return false;
    }

protected:
    uint32_t param_type = 0;
};
static_assert(sizeof(SetParamPayload) == 4, "Unexpected size");
#pragma pack()
} // namespace cb::mcbp::request

/**
 * This flag is used by the setWithMeta/addWithMeta/deleteWithMeta packets
 * to specify that the operation should be forced. The update will not
 * be subject to conflict resolution and the target vb can be active/pending or
 * replica.
 */
#define FORCE_WITH_META_OP 0x01

/**
 * This flag is used to indicate that the *_with_meta should be accepted
 * regardless of the bucket config. LWW buckets require this flag.
 */
#define FORCE_ACCEPT_WITH_META_OPS 0x02

/**
 * This flag asks that the server regenerates the CAS. The server requires
 * that SKIP_CONFLICT_RESOLUTION_FLAG is set along with this option.
 */
#define REGENERATE_CAS 0x04

/**
 * This flag is used by the setWithMeta/addWithMeta/deleteWithMeta packets
 * to specify that the conflict resolution mechanism should be skipped for
 * this operation.
 */
#define SKIP_CONFLICT_RESOLUTION_FLAG 0x08

/**
 * This flag is used by deleteWithMeta packets to specify if the delete sent
 * instead represents an expiration.
 */
#define IS_EXPIRATION 0x10

/**
 * This flag is used with the get meta response packet. If set it
 * specifies that the item recieved has been deleted, but that the
 * items meta data is still contained in ep-engine. Eg. the item
 * has been soft deleted.
 */
#define GET_META_ITEM_DELETED_FLAG 0x01

namespace cb::mcbp::request {
#pragma pack(1)
class SetWithMetaPayload {
public:
    uint32_t getFlags() const {
        return ntohl(flags);
    }
    uint32_t getFlagsInNetworkByteOrder() const {
        return flags;
    }
    void setFlags(uint32_t flags) {
        SetWithMetaPayload::flags = htonl(flags);
    }
    void setFlagsInNetworkByteOrder(uint32_t flags) {
        SetWithMetaPayload::flags = flags;
    }
    uint32_t getExpiration() const {
        return ntohl(expiration);
    }
    void setExpiration(uint32_t expiration) {
        SetWithMetaPayload::expiration = htonl(expiration);
    }
    uint64_t getSeqno() const {
        return ntohll(seqno);
    }
    void setSeqno(uint64_t seqno) {
        SetWithMetaPayload::seqno = htonll(seqno);
    }
    uint64_t getCas() const {
        return ntohll(cas);
    }
    void setCas(uint64_t cas) {
        SetWithMetaPayload::cas = htonll(cas);
    }
    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint32_t flags = 0;
    uint32_t expiration = 0;
    uint64_t seqno = 0;
    uint64_t cas = 0;
};
static_assert(sizeof(SetWithMetaPayload) == 24, "Unexpected struct size");

class DelWithMetaPayload {
public:
    DelWithMetaPayload(uint32_t flags,
                       uint32_t delete_time,
                       uint64_t seqno,
                       uint64_t cas)
        : flags(htonl(flags)),
          delete_time(htonl(delete_time)),
          seqno(htonll(seqno)),
          cas(htonll(cas)) {
    }

    uint32_t getFlags() const {
        return ntohl(flags);
    }
    uint32_t getFlagsInNetworkByteOrder() const {
        return flags;
    }
    void setFlags(uint32_t flags) {
        DelWithMetaPayload::flags = htonl(flags);
    }
    uint32_t getDeleteTime() const {
        return ntohl(delete_time);
    }
    void setDeleteTime(uint32_t delete_time) {
        DelWithMetaPayload::delete_time = htonl(delete_time);
    }
    uint64_t getSeqno() const {
        return ntohll(seqno);
    }
    void setSeqno(uint64_t seqno) {
        DelWithMetaPayload::seqno = htonll(seqno);
    }
    uint64_t getCas() const {
        return ntohll(cas);
    }
    void setCas(uint64_t cas) {
        DelWithMetaPayload::cas = htonll(cas);
    }
    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint32_t flags = 0;
    uint32_t delete_time = 0;
    uint64_t seqno = 0;
    uint64_t cas = 0;
};
static_assert(sizeof(DelWithMetaPayload) == 24, "Unexpected struct size");
#pragma pack()
} // namespace cb::mcbp::request

/**
 * The physical layout for a CMD_GET_META command returns the meta-data
 * section for an item:
 */
typedef protocol_binary_request_no_extras protocol_binary_request_get_meta;

/**
 * Structure holding getMeta command response fields
 */
#pragma pack(1)

struct GetMetaResponse {
    uint32_t deleted;
    uint32_t flags;
    uint32_t expiry;
    uint64_t seqno;
    uint8_t datatype;

    GetMetaResponse() : deleted(0), flags(0), expiry(0), seqno(0), datatype(0) {
    }

    GetMetaResponse(uint32_t deleted,
                    uint32_t flags,
                    uint32_t expiry,
                    uint64_t seqno,
                    uint8_t datatype)
        : deleted(deleted),
          flags(flags),
          expiry(expiry),
          seqno(seqno),
          datatype(datatype) {
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }
};

#pragma pack()

static_assert(sizeof(GetMetaResponse) == 21, "Incorrect compiler padding");

/* Meta data versions for GET_META */
enum class GetMetaVersion : uint8_t {
    V1 = 1, // returns deleted, flags, expiry and seqno
    V2 = 2, // The 'spock' version returns V1 + the datatype
};

/**
 * The physical layout for the CMD_RETURN_META
 */
namespace cb::mcbp::request {

#pragma pack(1)

enum class ReturnMetaType : uint32_t { Set = 1, Add = 2, Del = 3 };

class ReturnMetaPayload {
public:
    ReturnMetaType getMutationType() const {
        return static_cast<ReturnMetaType>(ntohl(mutation_type));
    }
    void setMutationType(ReturnMetaType mutation_type) {
        ReturnMetaPayload::mutation_type =
                htonl(static_cast<uint32_t>(mutation_type));
    }
    uint32_t getFlags() const {
        return ntohl(flags);
    }
    void setFlags(uint32_t flags) {
        ReturnMetaPayload::flags = htonl(flags);
    }
    uint32_t getExpiration() const {
        return ntohl(expiration);
    }
    void setExpiration(uint32_t expiration) {
        ReturnMetaPayload::expiration = htonl(expiration);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint32_t mutation_type = 0;
    uint32_t flags = 0;
    uint32_t expiration = 0;
};
static_assert(sizeof(ReturnMetaPayload) == 12, "Unexpected struct size");

/**
 * Message format for CMD_COMPACT_DB
 *
 * The PROTOCOL_BINARY_CMD_COMPACT_DB is used by ns_server to
 * issue a compaction request to ep-engine to compact the
 * underlying store's database files
 *
 * Request:
 *
 * Header: Contains the vbucket id. The vbucket id will be used
 *         to identify the database file if the backend is
 *         couchstore. If the vbucket id is set to 0xFFFF, then
 *         the vbid field will be used for compaction.
 * Body:
 * - purge_before_ts:  Deleted items whose expiry timestamp is less
 *                     than purge_before_ts will be purged.
 * - purge_before_seq: Deleted items whose sequence number is less
 *                     than purge_before_seq will be purged.
 * - drop_deletes:     whether to purge deleted items or not.
 * - vbid  :     Database file id for the underlying store.
 *
 * Response:
 *
 * The response will return a SUCCESS after compaction is done
 * successfully and a NOT_MY_VBUCKET (along with cluster config)
 * if the vbucket isn't found.
 */
class CompactDbPayload {
public:
    uint64_t getPurgeBeforeTs() const {
        return ntohll(purge_before_ts);
    }
    void setPurgeBeforeTs(uint64_t purge_before_ts) {
        CompactDbPayload::purge_before_ts = htonll(purge_before_ts);
    }
    uint64_t getPurgeBeforeSeq() const {
        return ntohll(purge_before_seq);
    }
    void setPurgeBeforeSeq(uint64_t purge_before_seq) {
        CompactDbPayload::purge_before_seq = htonll(purge_before_seq);
    }
    uint8_t getDropDeletes() const {
        return drop_deletes;
    }
    void setDropDeletes(uint8_t drop_deletes) {
        CompactDbPayload::drop_deletes = drop_deletes;
    }
    const Vbid getDbFileId() const {
        return db_file_id.ntoh();
    }
    void setDbFileId(const Vbid& db_file_id) {
        CompactDbPayload::db_file_id = db_file_id.hton();
    }

    // Generate a method which use align_pad1 and 3 to avoid the compiler
    // to generate a warning about unused member (because we
    bool validate() const {
        return align_pad1 == 0 && align_pad3 == 0;
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint64_t purge_before_ts = 0;
    uint64_t purge_before_seq = 0;
    uint8_t drop_deletes = 0;
    uint8_t align_pad1 = 0;
    Vbid db_file_id = Vbid{0};
    uint32_t align_pad3 = 0;
};
#pragma pack()
static_assert(sizeof(CompactDbPayload) == 24, "Unexpected struct size");
} // namespace cb::mcbp::request

#define OBS_STATE_NOT_PERSISTED 0x00
#define OBS_STATE_PERSISTED 0x01
#define OBS_STATE_NOT_FOUND 0x80
#define OBS_STATE_LOGICAL_DEL 0x81

/**
 * The physical layout for the PROTOCOL_BINARY_CMD_AUDIT_PUT
 */
typedef union {
    struct {
        protocol_binary_request_header header;
        struct {
            uint32_t id;
        } body;
    } message;
    uint8_t bytes[sizeof(protocol_binary_request_header) + 4];
} protocol_binary_request_audit_put;

typedef protocol_binary_response_no_extras protocol_binary_response_audit_put;

/**
 * The PROTOCOL_BINARY_CMD_OBSERVE_SEQNO command is used by the
 * client to retrieve information about the vbucket in order to
 * find out if a particular mutation has been persisted or
 * replicated at the server side. In order to do so, the client
 * would pass the vbucket uuid of the vbucket that it wishes to
 * observe to the serve.  The response would contain the last
 * persisted sequence number and the latest sequence number in the
 * vbucket. For example, if a client sends a request to observe
 * the vbucket 0 with uuid 12345 and if the response contains the
 * values <58, 65> and then the client can infer that sequence
 * number 56 has been persisted, 60 has only been replicated and
 * not been persisted yet and 68 has not been replicated yet.
 */

/**
 * Definition of the request packet for the observe_seqno command.
 *
 * Header: Contains the vbucket id of the vbucket that the client
 *         wants to observe.
 *
 * Body: Contains the vbucket uuid of the vbucket that the client
 *       wants to observe. The vbucket uuid is of type uint64_t.
 *
 */
typedef union {
    struct {
        protocol_binary_request_header header;
        struct {
            uint64_t uuid;
        } body;
    } message;
    uint8_t bytes[sizeof(protocol_binary_request_header) + 8];
} protocol_binary_request_observe_seqno;

/**
 * Definition of the response packet for the observe_seqno command.
 * Body: Contains a tuple of the form
 *       <format_type, vbucket id, vbucket uuid, last_persisted_seqno,
 * current_seqno>
 *
 *       - format_type is of type uint8_t and it describes whether
 *         the vbucket has failed over or not. 1 indicates a hard
 *         failover, 0 indicates otherwise.
 *       - vbucket id is of type Vbid and it is the identifier for
 *         the vbucket.
 *       - vbucket uuid is of type uint64_t and it represents a UUID for
 *          the vbucket.
 *       - last_persisted_seqno is of type uint64_t and it is the
 *         last sequence number that was persisted for this
 *         vbucket.
 *       - current_seqno is of the type uint64_t and it is the
 *         sequence number of the latest mutation in the vbucket.
 *
 *       In the case of a hard failover, the tuple is of the form
 *       <format_type, vbucket id, vbucket uuid, last_persisted_seqno,
 * current_seqno, old vbucket uuid, last_received_seqno>
 *
 *       - old vbucket uuid is of type uint64_t and it is the
 *         vbucket UUID of the vbucket prior to the hard failover.
 *
 *       - last_received_seqno is of type uint64_t and it is the
 *         last received sequence number in the old vbucket uuid.
 *
 *       The other fields are the same as that mentioned in the normal case.
 */
typedef protocol_binary_response_no_extras
        protocol_binary_response_observe_seqno;

/**
 * Definition of the request packet for the command
 * PROTOCOL_BINARY_CMD_GET_ALL_VB_SEQNOS
 *
 * Header: Only opcode field is used.
 *
 * Body: Contains the vBucket state and/or collection id for which the vb
 *       sequence numbers are requested.
 *       Please note that these fields are optional, header.request.extlen is
 *       checked to see if they are present. If a vBucket state is not
 *       present or 0 it implies request is for all vbucket states. If
 *       collection id is not present it it implies the request is for the
 *       vBucket high seqno number.
 *
 */
typedef union {
    struct {
        protocol_binary_request_header header;
        struct {
            RequestedVBState state;
            CollectionIDType cid;
        } body;
    } message;
    uint8_t bytes[sizeof(protocol_binary_request_header) +
                  sizeof(RequestedVBState) + sizeof(CollectionIDType)];
} protocol_binary_request_get_all_vb_seqnos;

/**
 * Definition of the payload in the PROTOCOL_BINARY_CMD_GET_ALL_VB_SEQNOS
 * response.
 *
 * The body contains a "list" of "vbucket id - seqno pairs" for all
 * active and replica buckets on the node in network byte order.
 *
 *
 *    Byte/     0       |       1       |       2       |       3       |
 *       /              |               |               |               |
 *      |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
 *      +---------------+---------------+---------------+---------------+
 *     0| VBID          | VBID          | SEQNO         | SEQNO         |
 *      +---------------+---------------+---------------+---------------+
 *     4| SEQNO         | SEQNO         | VBID          | VBID          |
 *      +---------------+---------------+---------------+---------------+
 *     4| SEQNO         | SEQNO         |
 *      +---------------+---------------+
 */
typedef protocol_binary_response_no_extras
        protocol_binary_response_get_all_vb_seqnos;

/**
 * Message format for PROTOCOL_BINARY_CMD_GET_KEYS
 *
 * The extras field may contain a 32 bit integer specifying the number
 * of keys to fetch. If no value specified 1000 keys is transmitted.
 *
 * Key is mandatory and specifies the starting key
 *
 * Get keys is used to fetch a sequence of keys from the server starting
 * at the specified key.
 */
typedef protocol_binary_request_no_extras protocol_binary_request_get_keys;

namespace cb::mcbp::request {
#pragma pack(1)

class AdjustTimePayload {
public:
    enum class TimeType : uint8_t { TimeOfDay = 0, Uptime = 1 };

    uint64_t getOffset() const {
        return ntohll(offset);
    }
    void setOffset(uint64_t offset) {
        AdjustTimePayload::offset = htonll(offset);
    }
    TimeType getTimeType() const {
        return time_type;
    }
    void setTimeType(TimeType time_type) {
        AdjustTimePayload::time_type = time_type;
    }

    bool isValid() const {
        switch (getTimeType()) {
        case TimeType::TimeOfDay:
        case TimeType::Uptime:
            return true;
        }
        return false;
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint64_t offset = 0;
    TimeType time_type = TimeType::TimeOfDay;
};
static_assert(sizeof(AdjustTimePayload) == 9, "Unexpected struct size");

/**
 * Message format for PROTOCOL_BINARY_CMD_EWOULDBLOCK_CTL
 *
 * See engines/ewouldblock_engine for more information.
 */
class EWB_Payload {
public:
    uint32_t getMode() const {
        return ntohl(mode);
    }
    void setMode(uint32_t m) {
        mode = htonl(m);
    }
    uint32_t getValue() const {
        return ntohl(value);
    }
    void setValue(uint32_t v) {
        value = htonl(v);
    }
    uint32_t getInjectError() const {
        return ntohl(inject_error);
    }
    void setInjectError(uint32_t ie) {
        inject_error = htonl(ie);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint32_t mode = 0; // See EWB_Engine_Mode
    uint32_t value = 0;
    uint32_t inject_error = 0; // cb::engine_errc to inject.
};
static_assert(sizeof(EWB_Payload) == 12, "Unepected struct size");

/**
 * Message format for PROTOCOL_BINARY_CMD_GET_ERRORMAP
 *
 * The payload (*not* specified as extras) contains a 2 byte payload
 * containing a 16 bit encoded version number. This version number should
 * indicate the highest version number of the error map the client is able
 * to understand. The server will return a JSON-formatted error map
 * which is formatted to either the version requested by the client, or
 * a lower version (thus, clients must be ready to parse lower version
 * formats).
 */
class GetErrmapPayload {
public:
    uint16_t getVersion() const {
        return ntohs(version);
    }
    void setVersion(uint16_t version) {
        GetErrmapPayload::version = htons(version);
    }

    cb::const_byte_buffer getBuffer() const {
        return {reinterpret_cast<const uint8_t*>(this), sizeof(*this)};
    }

protected:
    uint16_t version = 0;
};
static_assert(sizeof(GetErrmapPayload) == 2, "Unexpected struct size");
#pragma pack()
} // namespace cb::mcbp::request

/**
 * Message format for PROTOCOL_BINARY_CMD_COLLECTIONS_SET_MANIFEST
 *
 * The body contains a JSON collections manifest.
 * No key and no extras
 */
typedef union {
    struct {
        protocol_binary_request_header header;
    } message;
    uint8_t bytes[sizeof(protocol_binary_request_header)];
} protocol_binary_collections_set_manifest;

typedef protocol_binary_response_no_extras
        protocol_binary_response_collections_set_manifest;

/**
 * @}
 */
inline protocol_binary_subdoc_flag operator|(protocol_binary_subdoc_flag a,
                                             protocol_binary_subdoc_flag b) {
    return protocol_binary_subdoc_flag(static_cast<uint8_t>(a) |
                                       static_cast<uint8_t>(b));
}

namespace mcbp::subdoc {
inline constexpr mcbp::subdoc::doc_flag operator|(mcbp::subdoc::doc_flag a,
                                                  mcbp::subdoc::doc_flag b) {
    return mcbp::subdoc::doc_flag(static_cast<uint8_t>(a) |
                                  static_cast<uint8_t>(b));
}

inline constexpr mcbp::subdoc::doc_flag operator&(mcbp::subdoc::doc_flag a,
                                                  mcbp::subdoc::doc_flag b) {
    return mcbp::subdoc::doc_flag(static_cast<uint8_t>(a) &
                                  static_cast<uint8_t>(b));
}

inline constexpr mcbp::subdoc::doc_flag operator~(mcbp::subdoc::doc_flag a) {
    return mcbp::subdoc::doc_flag(~static_cast<uint8_t>(a));
}

inline std::string to_string(mcbp::subdoc::doc_flag a) {
    using mcbp::subdoc::doc_flag;
    switch (a) {
    case doc_flag::None:
        return "None";
    case doc_flag::Mkdoc:
        return "Mkdoc";
    case doc_flag::AccessDeleted:
        return "AccessDeleted";
    case doc_flag::Add:
        return "Add";
    case doc_flag::CreateAsDeleted:
        return "CreateAsDeleted";
    case doc_flag::ReviveDocument:
        return "ReviveDocument";
    }
    return std::to_string(static_cast<uint8_t>(a));
}

inline bool hasAccessDeleted(mcbp::subdoc::doc_flag a) {
    return (a & mcbp::subdoc::doc_flag::AccessDeleted) !=
           mcbp::subdoc::doc_flag::None;
}

inline bool hasMkdoc(mcbp::subdoc::doc_flag a) {
    return (a & mcbp::subdoc::doc_flag::Mkdoc) != mcbp::subdoc::doc_flag::None;
}

inline bool hasAdd(mcbp::subdoc::doc_flag a) {
    return (a & mcbp::subdoc::doc_flag::Add) != mcbp::subdoc::doc_flag::None;
}

inline bool hasReviveDocument(mcbp::subdoc::doc_flag a) {
    return (a & mcbp::subdoc::doc_flag::ReviveDocument) ==
           mcbp::subdoc::doc_flag::ReviveDocument;
}

inline bool hasCreateAsDeleted(mcbp::subdoc::doc_flag a) {
    return (a & mcbp::subdoc::doc_flag::CreateAsDeleted) !=
           mcbp::subdoc::doc_flag::None;
}

inline bool isNone(mcbp::subdoc::doc_flag a) {
    return a == mcbp::subdoc::doc_flag::None;
}
inline bool impliesMkdir_p(mcbp::subdoc::doc_flag a) {
    return hasAdd(a) || hasMkdoc(a);
}
} // namespace mcbp::subdoc

namespace mcbp::cas {
/**
 * The special value used as a wildcard and match all CAS values
 */
const uint64_t Wildcard = 0x0;
} // namespace mcbp::cas

namespace cb::mcbp::request {
#pragma pack(1)

// Payload for get_collection_id opcode 0xbb, data stored in network byte order
class GetCollectionIDPayload {
public:
    GetCollectionIDPayload() = default;
    GetCollectionIDPayload(uint64_t manifestId, CollectionID collectionId)
        : manifestId(htonll(manifestId)),
          collectionId(htonl(uint32_t(collectionId))) {
    }

    CollectionID getCollectionId() const {
        return ntohl(collectionId);
    }

    uint64_t getManifestId() const {
        return ntohll(manifestId);
    }

    std::string_view getBuffer() const {
        return {reinterpret_cast<const char*>(this), sizeof(*this)};
    }

protected:
    uint64_t manifestId{0};
    uint32_t collectionId{0};
};

// Payload for get_scope_id opcode 0xbc, data stored in network byte order
class GetScopeIDPayload {
public:
    GetScopeIDPayload() = default;
    GetScopeIDPayload(uint64_t manifestId, ScopeID scopeId)
        : manifestId(htonll(manifestId)), scopeId(htonl(uint32_t(scopeId))) {
    }
    ScopeID getScopeId() const {
        return ntohl(scopeId);
    }

    uint64_t getManifestId() const {
        return ntohll(manifestId);
    }

    std::string_view getBuffer() const {
        return {reinterpret_cast<const char*>(this), sizeof(*this)};
    }

protected:
    uint64_t manifestId{0};
    uint32_t scopeId{0};
};

// Payload for get_rando_key opcode 0xb6, data stored in network byte order
class GetRandomKeyPayload {
public:
    GetRandomKeyPayload() = default;
    explicit GetRandomKeyPayload(uint32_t collectionId)
        : collectionId(htonl(collectionId)) {
    }

    CollectionID getCollectionId() const {
        return ntohl(collectionId);
    }

    std::string_view getBuffer() const {
        return {reinterpret_cast<const char*>(this), sizeof(*this)};
    }

protected:
    CollectionIDType collectionId{0};
};
#pragma pack()
} // namespace cb::mcbp::request

ext: h


text:
#pragma once

#include <memory>

#include <gsl/gsl>
#include "../platform/d3d.h"
#include "../infrastructure/macros.h"

namespace gfx {

class IndexBuffer {
	friend class RenderingDevice;
public:
	IndexBuffer(CComPtr<ID3D11Buffer> buffer, size_t count);
	~IndexBuffer();

	void Update(gsl::span<uint16_t> data);

	NO_COPY_OR_MOVE(IndexBuffer)
private:
	void Unlock();

	size_t mCount;
	CComPtr<ID3D11Buffer> mBuffer;
};

using IndexBufferPtr = std::shared_ptr<IndexBuffer>;

class VertexBuffer {
	friend class RenderingDevice;
	friend class BufferBinding;
public:
	VertexBuffer(CComPtr<ID3D11Buffer> vertexBufferNew, size_t size);
	~VertexBuffer();

	void Update(gsl::span<const uint8_t> data);

	template <typename T>
	void Update(gsl::span<T> data) {
		Update(gsl::span(reinterpret_cast<const uint8_t*>(&data[0]), data.size_bytes()));
	}

	NO_COPY_OR_MOVE(VertexBuffer);
private:
	size_t mSize;
	CComPtr<ID3D11Buffer> mBuffer;
};

using VertexBufferPtr = std::shared_ptr<VertexBuffer>;

}

max_stars_repo_name: edoipi/TemplePlus

max_issues_repo_name: edoipi/TemplePlus

max_forks_repo_name: edoipi/TemplePlus


text:
/* applying a t-model to performance data to obtain a robust
   estimate of the mean via ML of the loglikelihood function */

/* PREPROCESSOR FLAGS:
   NR - Algorithms from Numerical Recipes
     NELDER - Nelder-Mead algorithm, not working
     POWELL - Powell's method
     QUASI_NEWTON - quasi-Newton (Newton-Raphson) method
   GSL - algrithms from GSL library
     (select Conjugate gradient, BFGS or steepest decent in function
     gsl_minimize_loglikelihood)
   MAIN - compile with main() and test data
   SUMMARY - short output of results
   DEBUG - turns on various output
*/

// Nealder and Mead: does not work
//gcc -DNR -DMAIN -DNELDER robust.c dfpmin.c lnsrch.c nrutil.c amoeba.c amotry.c -I/usr/include/gsl -lm -L/usr/local/lib -lgsl -lgslcblas -g  -o test.x

// quasi-Newton:
//gcc -DNR -DMAIN -DQUASI_NEWTON robust.c dfpmin.c lnsrch.c nrutil.c -I/usr/include/gsl -lm -L/usr/local/lib -lgsl -lgslcblas -g  -o test.x

// Powell:
// gcc -DNR -DMAIN -DPOWELL robust.c  nrutil.c  powell.c linmin.c f1dim.c mnbrak.c brent.c -I/usr/include/gsl -lm -L/usr/local/lib -lgsl -lgslcblas -g  -o test.x

// GSL:
// gcc -DGSL -DMAIN robust.c dfpmin.c lnsrch.c nrutil.c -I/usr/include/gsl -lm -L/usr/local/lib -lgsl -lgslcblas -g  -o test.x
#if defined(GSL) || defined(NR)

#ifdef NR
#include <stdlib.h>
#include <stdio.h>
#define NRANSI
#include "nr.h"
#include "nrutil.h"
#endif

#ifdef GSL
#include <gsl_math.h>
#include <gsl_sort.h>
#include <gsl_sf_gamma.h>
#include <gsl_sf_psi.h>
#include <gsl_sf_log.h>
#include <gsl/gsl_sf_pow_int.h>
#include <gsl_statistics_double.h>
#include <gsl/gsl_sf_pow_int.h>
#include <gsl_multimin.h>
#endif

#ifdef NR
void get_starting_value(float* x0);
void output_results(float* x0, int iter, float* val);
#endif
#ifdef GSL
void get_starting_value(gsl_vector* x0, void* params);
void output_results(gsl_vector* x0, int iter, double* val, void* params);

void loglikelihood_fdf (const gsl_vector *x, void *params, double *f,
   gsl_vector *df);
int  gsl_minimize_loglikelihood(const double step_size, const double tol,
   const double eps_abs, const int maxiter, gsl_vector* x0, double* val, int* iter,
   void* params);
int  gsl_minimize_loglikelihood_old(const double step_size, const double tol,
   const double eps_abs, const int maxiter, gsl_vector* x0, double* val, int* iter,
   void* params);
#endif

#ifdef NR
void nelder_mead(float* x0, float (*func)(float []), float* ftol,
   float* val);
#endif

static int nfunc;  // number of function calls
static int ndfunc; // number of calls to compute derivative
double nu=3;
int with_nu;
double nustart=4.0;
int ndim;

#ifdef MAIN
/* speed of light data set */
double c_data[66] = {
      28.0, 26.0, 33.0, 24.0, 34.0, -44.0, 27.0, 16.0, 40.0, -2.0,
      29.0, 22.0, 24.0, 21.0, 25.0, 30.0, 23.0, 29.0, 31.0, 19.0,
      24.0, 20.0, 36.0, 32.0, 36.0, 28.0, 25.0, 21.0, 28.0, 29.0,
      37.0, 25.0, 28.0, 26.0, 30.0, 32.0, 36.0, 26.0, 30.0, 22.0,
      36.0, 23.0, 27.0, 27.0, 28.0, 27.0, 31.0, 27.0, 26.0, 33.0,
      26.0, 32.0, 32.0, 24.0, 39.0, 28.0, 24.0, 25.0, 32.0, 25.0,
      29.0, 27.0, 28.0, 29.0, 16.0, 23.0};
#endif

int     ndata;    // #data points
double *data;

/*****************************************************************************/
#ifdef NR
float loglikelihood_f(float x[])
#endif
#ifdef GSL
double loglikelihood_f(const gsl_vector *x, void *params)
#endif
/*****************************************************************************/
{
   double mu, // estimate of location
     sigma2;  // estimate of scale
   double delta, g, psi;

#ifdef GSL
   int ndata;
   double nu, *data;
   double * tmp = (double *) params;
#endif
   double y;  // temporary variable
   int i;

   nfunc++;

#ifdef NR
   mu      = x[1];
   sigma2  = x[2];
   if (with_nu) nu = x[3];
#else
   /* extract arguments and parameters */
   mu     = gsl_vector_get(x, 0);
   sigma2 = gsl_vector_get(x, 1);
   nu     = tmp[0];
   if (nu == 0){
      with_nu = 1;
      nu = gsl_vector_get(x,2);
   }
   ndata   = (int) tmp[1];
   data    = &(tmp[2]);
   //printf("%d %f %f\n", n, nu, data[0]);
#endif

   y = - 0.5 * gsl_sf_log(sigma2) * ndata;
   for (i=0; i<ndata; i++){
     delta = (data[i] - mu)*( data[i] - mu) / (nu * sigma2);
     y = y - 0.5 * (nu + 1.) * gsl_sf_log( 1+delta );
   }

   if (with_nu){ /* compute whole log likelyhood function */
      g =  gsl_sf_lngamma(0.5*(nu+1.)) - gsl_sf_lngamma(0.5*nu)
           - 0.5 * gsl_sf_log(nu);
      y = y + ndata * g;
   }

#ifdef DEBUG
   if (with_nu)
      printf("mu=%.12f, sigma=%.12f, nu=%.12f, y=%.12f\n", mu, sqrt(sigma2), nu, y);
   else
      printf("mu=%.12f, sigma=%.12f, y=%.12f\n", mu, sqrt(sigma2), y);
#endif

   return -y; /* since we're minimizing, not maximizing */

}

/*****************************************************************************/
#ifdef NR
void loglikelihood_df(float x[],float df[])
#endif
#ifdef GSL
void loglikelihood_df(const gsl_vector *x, void *params, gsl_vector *df)
#endif
/*****************************************************************************/
/* this function should store the n-dimensional gradient
g_i = d f(x,params) / d x_i in the vector g for argument x and parameters
params, returning an appropriate error code if the function cannot be computed. */
{
   double mu, sigma2;

   int i, k;
   double dldtheta, dldphi, dldnu, y, r, t, d, g, s1, s2;
#ifdef GSL
   int ndata, with_nu = 0;
   double nu, *data;
   double * tmp = (double *) params;
#endif

   ndfunc++;

#ifdef NR
   /* extract arguments and parameters */
   mu     = x[1];
   sigma2 = x[2];
   if (with_nu)    nu = x[3];
#endif
#ifdef GSL
   /* extract arguments and parameters */
   mu       = gsl_vector_get(x, 0);
   sigma2   = gsl_vector_get(x, 1);
   nu       = tmp[0];
   if (nu == 0){
      with_nu = 1;
      nu = gsl_vector_get(x,2);
   }
   ndata    = (int) tmp[1];
   data     = &(tmp[2]);
   //printf("%d %f %f\n", ndata, nu, data[0]);
   //printf("df mu=%lf sigma=%lf nu=%lf \n", mu, sqrt(sigma2), nu);
#endif

   dldtheta = 0.;
   dldphi   = 0.;
   if (with_nu) {
      dldnu = 0.;
      s1 = 0.;
      s2 = 0.;
   }

   /* calculate terms depending on data[i] */
   for (i=0; i<ndata; i++){
      y = data[i];
      d = y - mu;
      r = d*d / (sigma2*nu);

      dldtheta = dldtheta + d / ( 1 + r );
      dldphi   = dldphi + r / (1+r); // + 0.5/sigma2;
      if (with_nu){
         s1 = s1 +  r / (1+r);  //t * d;
         s2 = s2 +  gsl_sf_log(1+r) ;
      }
   }

   /* add constant terms */
   dldtheta = (nu+1)/(sigma2*nu) * dldtheta;
   dldphi   = - 0.5*ndata/sigma2 + 0.5*(nu+1)/sigma2 * dldphi;
   if (with_nu) {
      /* digamma(z) = psi(z) = d/dz( ln gamma(z) ) */
      g =  0.5 * ndata * ( gsl_sf_psi((nu+1)/2) - gsl_sf_psi(0.5*nu) - 1./nu );
      dldnu    = g + 0.5*(nu+1)/nu * s1 - 0.5 * s2;
   }

   /* change signs since we are minimizing, not maximizing */
#ifdef NR
   df[1] = - dldtheta;
   df[2] = - dldphi;
   if (with_nu)  df[3] = - dldnu;
#endif
#ifdef GSL
   gsl_vector_set(df, 0, - dldtheta);
   gsl_vector_set(df, 1, - dldphi);
   if (with_nu) {
      gsl_vector_set(df, 2, - dldnu);
   }
#endif

#ifdef DEBUG
#ifdef NR
   if (with_nu) printf("  df %lf %lf %lf\n", df[1], df[2], df[3]);
   else printf("  df %lf %lf \n", df[1], df[2]);
#endif
#ifdef GSL
   if (with_nu) printf("  df %lf %lf %lf\n", gsl_vector_get(df, 0), gsl_vector_get(df, 1),
       gsl_vector_get(df, 2));
   else printf("  df %lf %lf \n", gsl_vector_get(df, 0), gsl_vector_get(df, 1));
#endif
#endif
}

/*****************************************************************************/
#ifdef NR
int ml(const int ndata_loc,  double* const data_loc, double* nu, double* mu,
       double* sigma, float* val)
#endif
#ifdef GSL
int ml(const int ndata,      double* const data,     double* nu, double* mu,
       double* sigma, double* val)
#endif
/*****************************************************************************/
/* performs a ML method for a t model
   ndata  (in)      size of data
   data   (in)      data / measurements
   nu     (in/out)  if nu is 0, optimize also for nu and return
                    optimized value
                    if nu is != 0, accept nu as parameter
   mu     (out)     optimized value for mu
   sigma  (out)     optimized value for sigma
   val    (out)     if nu is variable, value of log likelihood function
                    if nu is fixed, value of simplified log likelihood
                    function*/
{
#ifdef NR
   float *x0;            // starting point / maximum
#endif
#ifdef GSL
   gsl_vector *x0;       // starting point / maximum
   double *params;

   double step_size, tol, eps_abs;
#endif
   int maxiter = 1000;
   int iter = 0, i, j;

   /* init starting point */
   if (*nu == 0)
      ndim = 3;
   else
      ndim = 2;

#ifdef NR
   ndata = ndata_loc;
   data  = data_loc;

   x0  = vector(1,ndim);
#endif

#ifdef GSL
   /* init parameters */
   params = malloc((ndata+2)*sizeof(double));
   params[0] = *nu;
   params[1] = (double) ndata;
   for (i=0; i<ndata; i++){
      params[2+i] = data[i];
   }

   x0  = gsl_vector_alloc(ndim);
#endif

#ifdef NR
   get_starting_value(x0);
#endif
#ifdef GSL
   get_starting_value(x0, params);
#endif

   nfunc = ndfunc = 0;

#ifdef NR

#ifdef NELDER
   float ftol = 1.0e-6;
   nelder_mead(x0, loglikelihood_f, &(ftol), val);
#endif

#ifdef POWELL
   float ftol = 1.0e-6;
   float **p;

   p=matrix(1,ndim,1,ndim);
   for (i=1;i<=ndim;i++)
      for (j=1;j<=ndim;j++)
         p[i][j]=(i == j ? 1. : 0.0);
   if (with_nu)
      p[3][3] = 0.01;

   iter = maxiter;
   powell(x0,p,ndim,ftol,&iter,val,loglikelihood_f);

   free_matrix(p,1,ndim,1,ndim);
#endif

#ifdef QUASI_NEWTON
   float gtol = 1.0e-8;
   dfpmin(x0,ndim,gtol,&iter,val,loglikelihood_f,loglikelihood_df);
#endif

// endif NR
#endif

#ifdef GSL
   step_size = 0.1;   // size of first trial step
   tol       = 0.1;   // accuracy of line minimization, 0.1 will do
   eps_abs   = 1e-8;

   gsl_minimize_loglikelihood(step_size, tol, eps_abs, maxiter, x0, val,
      &(iter), params);
#endif

#ifdef SUMMARY
#ifdef NR
   output_results(x0, iter, val);
#endif
#ifdef GSL
   output_results(x0, iter, val, params);
#endif
#endif

#ifdef NR
   *mu     = (double) x0[1];
   *sigma = sqrt((double) x0[2]);
   if (with_nu)
      *nu = (double) x0[3];

   free_vector(x0,1,ndim);
#endif
#ifdef GSL
   *mu    = gsl_vector_get(x0, 0);
   *sigma = sqrt(gsl_vector_get(x0, 1));
   if (with_nu)
      *nu = gsl_vector_get(x0, 2);

   gsl_vector_free(x0);
   free (params);
#endif

   return 0;

}

#ifdef MAIN
/*****************************************************************************/
int main()
/*****************************************************************************/
{
   int i, varnu=0;
#ifdef NR
   float val;
#endif
#ifdef GSL
   double val;
#endif
   double mu, sigma;

   const int ndata_loc = 66;
   double *data_loc = &(c_data[0]);

   //nu = 0.0; with_nu = 1;
   nu =  4.0; with_nu = 0;

   ml(ndata_loc, data_loc, &(nu), &(mu), &(sigma), &(val));

#ifdef SUMMARY
   printf("\n\nReturn values:\n");
   printf("nu=%.5lf mu=%.5lf sigma=%.5lf val=%.5lf\n", nu, mu, sigma, val);
#endif

   return 0;

}
#endif

/*****************************************************************************/
#ifdef NR
void get_starting_value(float* x0)
#endif
#ifdef GSL
void get_starting_value(gsl_vector* x0, void* params)
#endif
/*****************************************************************************/
/* computes an estimate of location and scale                                */
/*****************************************************************************/
{
   double mean, variance;
   double median, *madv, mad;
   int i;

#ifdef GSL
   double* tmp = (double*) params;
   int ndata   = (int) tmp[1];
   double* data    = &(tmp[2]);
#endif

   /* Possiblity I: Use mean and variance as starting value
                    this is probably going to crash when outliers are present */
   mean = gsl_stats_mean(&(data[0]), (size_t) 1,(size_t) ndata);
   variance = gsl_stats_variance_with_fixed_mean(&(data[0]),
      (size_t) 1, (size_t) ndata, mean);
#ifdef NR
   x0[1] = mean;
   x0[2] = variance;
#endif
#ifdef GSL
   gsl_vector_set(x0, 0, mean);
   gsl_vector_set(x0, 1, variance);
#endif

   /* Possiblity II: Use median and 1.483*MAD as starting value
                     Absolute Deviatons MAD(x) = med_i {|x_i - med_j (x_j)|} */
   gsl_sort(data, (size_t) 1, (size_t) ndata);
   median = gsl_stats_median_from_sorted_data(data, (size_t) 1, (size_t) ndata);
   madv = (double*) malloc(ndata * sizeof(double));
   for (i=0; i<ndata; i++){
       madv[i] = abs(data[i] - median);
   }
   gsl_sort(madv, (size_t) 1, (size_t) ndata);
   mad = 1.483 * gsl_stats_median_from_sorted_data(madv, (size_t) 1, (size_t) ndata);
   //printf("median: %20.5f, mad     : %20.5f\n", median, mad);
   //mad = 2 * median; //5.25 * gsl_stats_median_from_sorted_data(madv, (size_t) 1, (size_t) ndata);
   free(madv);

#ifdef NR
   x0[1] = median;
   x0[2] = 0.7*mad*mad;
   if (with_nu)
      x0[3] = nustart;
#ifdef SUMMARY
   printf("\nStarting vector        : ");
   for (i=1;i<=ndim;i++) printf("%12.6f",x0[i]);
#endif
#endif

#ifdef GSL
   gsl_vector_set(x0, 0, median);
   gsl_vector_set(x0, 1, 0.7*mad*mad);
   if (with_nu)
      gsl_vector_set(x0, 2, nustart);
#ifdef SUMMARY
   printf("\nStarting vector        : ");
   for (i=0;i<ndim;i++) printf("%12.6f", gsl_vector_get(x0, i));
#endif
#endif

}

/*****************************************************************************/
#ifdef NR
void output_results(float* x0, int iter, float* val)
#endif
#ifdef GSL
void output_results(gsl_vector* x0, int iter, double* val, void* params)
#endif
/*****************************************************************************/
/* output of minimum  and statistics                                         */
/*****************************************************************************/
{
   int i;
#ifdef NR
   float      *xdf;        // gradient at maximum
   xdf = vector(1,ndim);
#endif

#ifdef GSL
   int with_nu = 0;
   gsl_vector *xdf;      // gradient at maximum
   double* tmp = (double*) params;
   double nu;

   xdf = gsl_vector_alloc(ndim);

   nu = tmp[0];
   if (nu == 0.0) with_nu = 1;
#endif

   printf("\n\nIterations             : %3d\n",iter);
   printf("Func. evals            : %3d\n",nfunc);
#ifdef QUASI_NEWTON
   printf("Deriv. evals           : %3d\n",ndfunc);
#endif

   printf("\nMaximum found at       : ");
#ifdef NR
   x0[2] = sqrt(x0[2]);
   for (i=1;i<=ndim;i++) printf("%12.6f",x0[i]);
   x0[2] = x0[2] * x0[2];
#endif
#ifdef GSL
   gsl_vector_set(x0, 1, sqrt(gsl_vector_get(x0, 1)));
   for (i=0;i<ndim;i++) printf("%12.6f", gsl_vector_get(x0, i));
   gsl_vector_set(x0, 1, gsl_vector_get(x0, 1) * gsl_vector_get(x0, 1));
#endif

   printf("\nMaximum function value : %12.6f\n",*val);

   printf("Derivative at maximum  : ");
#ifdef NR
   loglikelihood_df(x0, xdf);
   for (i=1;i<=ndim;i++) printf("%12.6f",xdf[i]);
#endif
#ifdef GSL
   loglikelihood_df(x0, params, xdf);
   for (i=0;i<ndim;i++) printf("%12.6f",gsl_vector_get(xdf, i));
#endif

#ifdef MAIN
   if (with_nu){ // for variable nu
       printf("\n\nTrue maximum is at (mu_hat, sigma_hat, vu) = (27.40, 3.81, 2.13)\n");
#ifdef NR
       x0[1] =  27.40;
       x0[2] =  3.81*3.81;
       x0[3] =  2.13;
       printf("Test: function value at maximum = %12.6f\n",  loglikelihood_f(x0));
       printf("Derivative at maximum           : ");
       loglikelihood_df(x0, xdf);
       for (i=1;i<=ndim;i++) printf("%12.6f",xdf[i]);
#endif
#ifdef GSL
       gsl_vector_set(x0, 0, 27.40);
       gsl_vector_set(x0, 1, 3.81*3.81);
       gsl_vector_set(x0, 2, 2.13);

       printf("Test: function value at maximum = %12.6f\n",  loglikelihood_f(x0, params));
       loglikelihood_df(x0, params, xdf);
       printf("Derivative at maximum           : ");
       for (i=0;i<ndim;i++) printf("%12.6f",gsl_vector_get(xdf, i));
#endif
   } else {         // for fixed nu=4:
       printf("\n\nTrue maximum is at (mu_hat, sigma_hat) = (27.49, 4.51)\n");
#ifdef NR
       x0[1] =  27.49;
       x0[2] =  4.51 * 4.51;
       printf("Test: function value at maximum : %12.6f\n",  loglikelihood_f(x0));
       printf("Derivative at true maximum      : ");
       loglikelihood_df(x0, xdf);
       for (i=1;i<=ndim;i++) printf("%12.6f",xdf[i]);
#endif
#ifdef GSL
       gsl_vector_set(x0, 0, 27.40);
       gsl_vector_set(x0, 1, 4.51 * 4.51);
       printf("Test: function value at maximum : %12.6f\n",  loglikelihood_f(x0, params));
       loglikelihood_df(x0, params, xdf);
       printf("Derivative at true maximum      : ");
       for (i=0;i<ndim;i++) printf("%12.6f",gsl_vector_get(xdf, i));
#endif
   }
#endif

#ifdef NR
   free_vector(xdf,1,ndim);
#endif
#ifdef GSL
   gsl_vector_free(xdf);
#endif

}

#ifdef NR
#ifdef NELDER
/*****************************************************************************/
void nelder_mead(float* x0, float(*func)(float []), float* ftol,
   float* val)
/*****************************************************************************/
/* computes minimum with method of Nelder and Mead from numerical recipes    */
/*
/* parameters:
/* x0    on input: starting value; on output: minimum
/* func  function
/* ftol  tolerance
/* val   function value at minimum */
/*****************************************************************************/
{
   int i, j;
   float *x, *y, **p;
   double delta_mu, delta_sigma2;

   if (ndim != 2) {
      printf("ndim != 2 not implemented\n");
      exit(0);
   }

   x=vector(1,ndim);
   y=vector(1,ndim+1);
   p=matrix(1,ndim+1,1,ndim);

   delta_mu = 0.5e0;
   delta_sigma2 = 1e0;

   for (i=1;i<=ndim+1;i++) {
      x[1] = p[i][1]=(i == (j+1) ? x0[1] + delta_mu     : x0[1] - delta_mu    );
      x[2] = p[i][2]=(i == (j+1) ? x0[2] + delta_sigma2 : x0[2] - delta_sigma2);
      y[i]=loglikelihood_f(x);
   }

   amoeba(p,y,ndim,*ftol,func,&nfunc);
   y[2] = sqrt(y[2]);

   printf("Vertices of final 3-d simplex and\n");
   printf("function values at the vertices:\n\n");
   printf("%3s %10s %12s %14s\n\n",
      "i","x[i]","y[i]", "function");
   for (i=1;i<=ndim+1;i++) {
      printf("%3d ",i);
      for (j=1;j<=ndim;j++) printf("%12.6f ",p[i][j]);
         printf("%12.6f\n",y[i]);
   }

   for (j=1;j<=ndim;j++) {
      // sum up vertices of final simplex
      x0[j] = p[1][j];
      for (i=2;i<=ndim+1;i++)
         x0[j] = x0[j] + p[i][j];
      // ... and average ...
      x0[j] = x0[j]/(ndim+1);
   }

   // calculate average function value at maximum
   *val = y[1];
   for (i=2;i<=ndim+1;i++)
      *val = *val + y[i];
   *val = *val / (ndim+1);

   free_vector(x,1,ndim);
   free_matrix(p,1,ndim+1,1,ndim);
   free_vector(y,1,ndim+1);
}
//endif NELDER
#endif

//endif NR
#endif

#ifdef GSL
/* Compute both f and df together. */
void loglikelihood_fdf (const gsl_vector *x, void *params, double *f,
   gsl_vector *df)
{
     *f = loglikelihood_f(x, params);
     loglikelihood_df(x, params, df);

     //printf("fdf =%lf %lf %lf %lf \n", gsl_vector_get(x,0), gsl_vector_get(x,1),
     //   gsl_vector_get(df,0), gsl_vector_get(df,1));
}

int  gsl_minimize_loglikelihood(const double step_size, const double tol,
   const double eps_abs, const int maxiter, gsl_vector* x0, double* val, int* iter,
   void* params){
   /*
   s       (in)      GSL multimin fminimizer object
   eps_abs (in)
   maxiter (in)      maximum number of iterations
   iter    (in/out)  number of iterations             */

   const gsl_multimin_fdfminimizer_type *T;
   gsl_multimin_fdfminimizer *s;
   gsl_multimin_function_fdf my_func;
   double* tmp = (double*) params;
   double nu = tmp[0];
   double sigma2;
   int my_iter = 0;
   int status;

   /* init function object */
   if (nu != 0.0) my_func.n = 2;
   else           my_func.n = 3;
   my_func.f   = &loglikelihood_f;
   my_func.df  = &loglikelihood_df;
   my_func.fdf = &loglikelihood_fdf;
   my_func.params = params;

   /* set up solver */
   T = gsl_multimin_fdfminimizer_conjugate_fr;
   // T = gsl_multimin_fdfminimizer_vector_bfgs;
   // T = gsl_multimin_fdfminimizer_steepest_descent;
   if (nu != 0.0)  s = gsl_multimin_fdfminimizer_alloc(T,2);
   else            s = gsl_multimin_fdfminimizer_alloc(T,3);

   /* call minimizer */
   //s->f = my_func->f;
   //gsl_multimin_fminimizer_set(s, &my_func, x0, step_size)
   gsl_multimin_fdfminimizer_set(s, &my_func, x0, step_size, tol);

   /* let gsl minimizer iterate */
   do
   {
      status = gsl_multimin_fdfminimizer_iterate(s);
      if (status) break;

      status = gsl_multimin_test_gradient(s->gradient, eps_abs);
      /* if (status == GSL_SUCCESS){
         printf("Maximum found after %d iterations at:\n", *iter);
      }
      printf ("%5d %.5f %.5f %10.5f\n", *iter,
        gsl_vector_get (s->x, 0), sqrt(gsl_vector_get (s->x, 1)), s->f); */
      (*iter)++;
   } while (status == GSL_CONTINUE && *iter < maxiter);

   /* just precaution */
   sigma2 =  gsl_vector_get(x0, 1);
   if (isnan(sigma2)){
     printf("!!! sigma2 is nan\n");
     printf("%5d %15.5f %15.5f", *iter,
     gsl_vector_get(s->x, 0),
     sqrt(gsl_vector_get(s->x, 1)));
     if (nu == 0.0) printf("%15.5f", gsl_vector_get(s->x, 2));
     printf("%15.5f\n", -gsl_multimin_fdfminimizer_minimum(s));

     exit;
   }

   /* set return values */
   gsl_vector_set(x0, 0, gsl_vector_get(s->x, 0));
   gsl_vector_set(x0, 1, gsl_vector_get(s->x, 1));
   if (nu == 0.0)  /* return optimized value */
       gsl_vector_set(x0, 2, gsl_vector_get(s->x, 2));
   *val   = -gsl_multimin_fdfminimizer_minimum(s);

   /* clean up */
   //gsl_multimin_fminimizer_free(s);
   gsl_multimin_fdfminimizer_free(s);

   return 0;
}

// obsolete
int  gsl_minimize_loglikelihood_old(const double step_size, const double tol,
   const double eps_abs, const int maxiter, gsl_vector* x0, double* val, int* iter,
   void* params){
   /*
   s       (in)      GSL multimin fminimizer object
   eps_abs (in)
   maxiter (in)      maximum number of iterations
   iter    (in/out)  number of iterations             */

   const gsl_multimin_fdfminimizer_type *T;
   gsl_multimin_fdfminimizer *s;
   gsl_multimin_function_fdf my_func;
   double* tmp = (double*) params;
   double nu = tmp[0];
   double sigma2;
   int my_iter = 0;
   int status;

   int converged = 0;
   double start=0.4;
   double median = gsl_vector_get(x0, 0);
   double mad    = gsl_vector_get(x0, 1);
   mad = sqrt(mad/0.7);

   /* init function object */
   if (nu != 0.0) my_func.n = 2;
   else           my_func.n = 3;
   my_func.f   = &loglikelihood_f;
   my_func.df  = &loglikelihood_df;
   my_func.fdf = &loglikelihood_fdf;
   my_func.params = params;

   /* set up solver */
   T = gsl_multimin_fdfminimizer_conjugate_fr;
   // T = gsl_multimin_fdfminimizer_vector_bfgs;
   // T = gsl_multimin_fdfminimizer_steepest_descent;
   if (nu != 0.0)  s = gsl_multimin_fdfminimizer_alloc(T,2);
   else            s = gsl_multimin_fdfminimizer_alloc(T,3);

   /* call minimizer */
   //s->f = my_func->f;
   //gsl_multimin_fminimizer_set(s, &my_func, x0, step_size)

   gsl_multimin_fdfminimizer_set(s, &my_func, x0, step_size, tol);

   do {//while (!converged && my_iter < 10){
      converged = 1;

      /* let gsl minimizer iterate */
      do
      {
         status = gsl_multimin_fdfminimizer_iterate(s);
         if (status) break;

         status = gsl_multimin_test_gradient(s->gradient, eps_abs);
         /* if (status == GSL_SUCCESS){
            printf("Maximum found after %d iterations at:\n", *iter);
         }
         printf ("%5d %.5f %.5f %10.5f\n", *iter,
            gsl_vector_get (s->x, 0), sqrt(gsl_vector_get (s->x, 1)), s->f); */
         (*iter)++;
      } while (status == GSL_CONTINUE && *iter < maxiter);

      sigma2 =  gsl_vector_get(x0, 1);
      if (!isnan(sigma2)) break;

      gsl_vector_set(x0, 0, median);
      gsl_vector_set(x0, 1, start*mad*mad);
      gsl_multimin_fdfminimizer_set (s, &my_func, x0, step_size, tol);
      start = start + 0.5;
      my_iter = my_iter + 1;
      if (my_iter > 1) printf("-- it=%d, sigma=%lf\n", my_iter, sqrt(sigma2));
   } while (my_iter < 10);
  if (*iter > maxiter){
     printf("!!! sigma2 is nan\n");
     printf("%5d %15.5f %15.5f", *iter,
     gsl_vector_get(s->x, 0),
     sqrt(gsl_vector_get(s->x, 1)));
     if (nu == 0.0) printf("%15.5f", gsl_vector_get(s->x, 2));
     printf("%15.5f\n", -gsl_multimin_fdfminimizer_minimum(s));
   }

   gsl_vector_set(x0, 0, gsl_vector_get(s->x, 0));
   gsl_vector_set(x0, 1, gsl_vector_get(s->x, 1));
   if (nu == 0.0)  /* return optimized value */
       gsl_vector_set(x0, 2, gsl_vector_get(s->x, 2));
   *val   = -gsl_multimin_fdfminimizer_minimum(s);

   /* clean up */
   //gsl_multimin_fminimizer_free(s);
   gsl_multimin_fdfminimizer_free(s);

   return 0;
}
//endif GSL
#endif

#endif
// GSL || NR

ext: c

max_stars_repo_name: edgargabriel/ADCL

max_issues_repo_name: PSTL-UH/ADCL

max_forks_repo_name: PSTL-UH/ADCL


text:
#ifndef SPC_FITSCARDS_H
#define SPC_FITSCARDS_H

#include <stdlib.h>

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>

#include "aXe_grism.h"
#include "fitsio.h"
#include "disp_conf.h"
#include "aper_conf.h"

typedef struct
{
  int n;          /* Number of cards in the **cards array */
  char **cards;   /* A pointer to an array of strings     */
} FITScards;

extern FITScards *
allocate_FITScards(int n);

extern void
free_FITScards(FITScards *cards);

extern FITScards *
get_WCS_FITScards(const double rval1, const double delta1, const double rval2);

extern FITScards *
beam_to_FITScards(object *ob, int beamnum);

extern FITScards *
stpmin_to_FITScards(d_point stp_min);

extern FITScards *
drzinfo_to_FITScards(object *ob, int beamnum, d_point outref,
		     aperture_conf *conf, gsl_matrix *drizzcoeffs,
		     int trlength, double relx, double rely,
		     double objwidth, d_point refwave_pos,
		     float sky_cps, double drizzle_width,
		     double cdcorr, double spcorr);

extern FITScards *
nicbck_info_to_FITScards(const double skypix_frac, const double scale_factor,
			 const double offset);

extern FITScards *
dispstruct_to_FITScards(dispstruct *disp);

extern void
update_contam_model(fitsfile *fptr, char model_name[]);

extern int
check_quantitative_contamination(fitsfile *fptr);

extern void
transport_cont_modname(char grism_file_path[], char PET_file_path[]);

#endif

max_stars_repo_name: sosey/pyaxe

max_issues_repo_name: sosey/pyaxe

max_forks_repo_name: sosey/pyaxe