text:
#ifndef DART_COMMON_H
#define DART_COMMON_H

// Automatically detect libraries if possible.
#if defined(__has_include)
# if !defined(DART_HAS_RAPIDJSON)
#   define DART_HAS_RAPIDJSON __has_include(<rapidjson/reader.h>) && __has_include(<rapidjson/writer.h>)
# endif
# ifndef DART_HAS_YAML
#   define DART_HAS_YAML __has_include(<yaml.h>)
# endif
#endif

/*----- System Includes -----*/

#include <map>
#include <vector>
#include <math.h>
#include <gsl/gsl>
#include <errno.h>
#include <cstdlib>
#include <stdarg.h>
#include <limits.h>
#include <algorithm>

#if DART_HAS_RAPIDJSON
#include <rapidjson/reader.h>
#include <rapidjson/writer.h>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#endif

#if DART_HAS_YAML
#include <yaml.h>
#endif

/*----- Local Includes -----*/

#include "shim.h"
#include "meta.h"
#include "support/ptrs.h"
#include "support/ordered.h"

/*----- System Includes with Compiler Flags -----*/

// Current version of sajson emits unused parameter
// warnings on Clang.
#ifdef DART_USE_SAJSON
#if DART_USING_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wunused-parameter"
#endif
#include <sajson.h>
#if DART_USING_CLANG
#pragma clang diagnostic pop
#endif
#endif

/*----- Macro Definitions -----*/

#define DART_FROM_THIS                                                                                \
  reinterpret_cast<gsl::byte const*>(this)

#define DART_FROM_THIS_MUT                                                                                \
  reinterpret_cast<gsl::byte*>(this)

/*----- Global Declarations -----*/

namespace dart {

  template <class Object>
  class basic_object;
  template <class Array>
  class basic_array;
  template <class String>
  class basic_string;
  template <class Number>
  class basic_number;
  template <class Boolean>
  class basic_flag;
  template <class Null>
  class basic_null;

  template <template <class> class RefCount>
  class basic_heap;
  template <template <class> class RefCount>
  class basic_buffer;
  template <template <class> class RefCount>
  class basic_packet;

  struct type_error : std::logic_error {
    type_error(char const* msg) : logic_error(msg) {}
  };

  struct state_error : std::runtime_error {
    state_error(char const* msg) : runtime_error(msg) {}
  };

  struct parse_error : std::runtime_error {
    parse_error(char const* msg) : runtime_error(msg) {}
  };

  struct validation_error : std::runtime_error {
    validation_error(char const* msg) : runtime_error(msg) {}
  };

  namespace detail {

    template <class T>
    struct is_dart_type : std::false_type {};
    template <template <class> class RC>
    struct is_dart_type<dart::basic_heap<RC>> : std::true_type {};
    template <template <class> class RC>
    struct is_dart_type<dart::basic_buffer<RC>> : std::true_type {};
    template <template <class> class RC>
    struct is_dart_type<dart::basic_packet<RC>> : std::true_type {};

    template <class T>
    struct is_wrapper_type : std::false_type {};
    template <class T>
    struct is_wrapper_type<dart::basic_object<T>> : std::true_type {};
    template <class T>
    struct is_wrapper_type<dart::basic_array<T>> : std::true_type {};
    template <class T>
    struct is_wrapper_type<dart::basic_string<T>> : std::true_type {};
    template <class T>
    struct is_wrapper_type<dart::basic_number<T>> : std::true_type {};
    template <class T>
    struct is_wrapper_type<dart::basic_flag<T>> : std::true_type {};
    template <class T>
    struct is_wrapper_type<dart::basic_null<T>> : std::true_type {};

    template <class T>
    struct is_dart_api_type : std::false_type {};
    template <template <class> class RC>
    struct is_dart_api_type<dart::basic_heap<RC>> : std::true_type {};
    template <template <class> class RC>
    struct is_dart_api_type<dart::basic_buffer<RC>> : std::true_type {};
    template <template <class> class RC>
    struct is_dart_api_type<dart::basic_packet<RC>> : std::true_type {};
    template <class T>
    struct is_dart_api_type<dart::basic_object<T>> : std::true_type {};
    template <class T>
    struct is_dart_api_type<dart::basic_array<T>> : std::true_type {};
    template <class T>
    struct is_dart_api_type<dart::basic_string<T>> : std::true_type {};
    template <class T>
    struct is_dart_api_type<dart::basic_number<T>> : std::true_type {};
    template <class T>
    struct is_dart_api_type<dart::basic_flag<T>> : std::true_type {};
    template <class T>
    struct is_dart_api_type<dart::basic_null<T>> : std::true_type {};

    template <class T>
    struct maybe_cast_return {
      template <class U, class =
        std::enable_if_t<
          std::is_pointer<U>::value
          ||
          is_dart_type<U>::value
        >
      >
      static U detect(int);

      template <class U>
      static shim::optional<U> detect(...);

      using type = decltype(detect<T>(0));
    };
    template <class T>
    using maybe_cast_return_t = typename maybe_cast_return<T>::type;

    /**
     *  @brief
     *  Enum encodes the type of an individual packet instance.
     *
     *  @details
     *  Internally, dart::packet manages a much larger set of types that encode ancillary information
     *  such as precision, signedness, alignment, size, however, all internal types map onto one of those
     *  contained in dart::packet::type, and all public API functions conceptually interact with objects
     *  of these types.
     */
    enum class type : uint8_t {
      object,
      array,
      string,
      integer,
      decimal,
      boolean,
      null
    };

    /**
     *  @brief
     *  Low-level type information that encodes information about the underlying machine-type,
     *  like precision, signedness, etc.
     */
    enum class raw_type : uint8_t {
      object,
      array,
      string,
      small_string,
      big_string,
      short_integer,
      integer,
      long_integer,
      decimal,
      long_decimal,
      boolean,
      null
    };

    /**
     *  @brief
     *  Used internally in scenarios where two dart types aren't contained within
     *  some existing data structure, but they need to be paired together.
     */
    template <class Packet>
    struct basic_pair {

      /*----- Lifecycle Functions -----*/

      basic_pair() = default;
      template <class P, class =
        std::enable_if_t<
          std::is_convertible<
            P,
            Packet
          >::value
        >
      >
      basic_pair(P&& key, P&& value) : key(std::forward<P>(key)), value(std::forward<P>(value)) {}
      basic_pair(basic_pair const&) = default;
      basic_pair(basic_pair&&) = default;
      ~basic_pair() = default;

      /*----- Operators -----*/

      basic_pair& operator =(basic_pair const&) = default;
      basic_pair& operator =(basic_pair&&) = default;

      /*----- Members -----*/

      Packet key, value;

    };
    template <template <class> class RefCount>
    using heap_pair = basic_pair<basic_heap<RefCount>>;
    template <template <class> class RefCount>
    using buffer_pair = basic_pair<basic_buffer<RefCount>>;
    template <template <class> class RefCount>
    using packet_pair = basic_pair<basic_packet<RefCount>>;

    /**
     *  @brief
     *  Helper-struct for sorting `dart::packet`s within `std::map`s.
     */
    template <template <class> class RefCount>
    struct dart_comparator {
      using is_transparent = shim::string_view;

      bool operator ()(shim::string_view lhs, shim::string_view rhs) const;

      // Comparison between some packet type and a string
      template <template <template <class> class> class Packet>
      bool operator ()(Packet<RefCount> const& lhs, shim::string_view rhs) const;
      template <template <template <class> class> class Packet>
      bool operator ()(shim::string_view lhs, Packet<RefCount> const& rhs) const;

      // Comparison between a key-value pair of some packet type and a string
      template <template <template <class> class> class Packet>
      bool operator ()(basic_pair<Packet<RefCount>> const& lhs, shim::string_view rhs) const;
      template <template <template <class> class> class Packet>
      bool operator ()(shim::string_view lhs, basic_pair<Packet<RefCount>> const& rhs) const;

      // Comparison between two, potentially disparate, packet types
      template <
        template <template <class> class> class LhsPacket,
        template <template <class> class> class RhsPacket
      >
      bool operator ()(LhsPacket<RefCount> const& lhs, RhsPacket<RefCount> const& rhs) const;

      // Comparison between a key-value pair of some packet type and some,
      // potentially disparate, packet type
      template <
        template <template <class> class> class LhsPacket,
        template <template <class> class> class RhsPacket
      >
      bool operator ()(basic_pair<LhsPacket<RefCount>> const& lhs, RhsPacket<RefCount> const& rhs) const;
      template <
        template <template <class> class> class LhsPacket,
        template <template <class> class> class RhsPacket
      >
      bool operator ()(LhsPacket<RefCount> const& lhs, basic_pair<RhsPacket<RefCount>> const& rhs) const;

      // Comparison between a key-value pair of some packet type and
      // a key-value pair of some, potentially disparate, packet type.
      template <
        template <template <class> class> class LhsPacket,
        template <template <class> class> class RhsPacket
      >
      bool operator ()(basic_pair<LhsPacket<RefCount>> const& lhs, basic_pair<RhsPacket<RefCount>> const& rhs) const;
    };

    struct dynamic_string_layout {
      bool operator ==(dynamic_string_layout const& other) const noexcept {
        if (len != other.len) return false;
        else return !strcmp(ptr.get(), other.ptr.get());
      }
      bool operator !=(dynamic_string_layout const& other) const noexcept {
        return !(*this == other);
      }

      std::shared_ptr<char> ptr;
      size_t len;
    };
    struct inline_string_layout {
      bool operator ==(inline_string_layout const& other) const noexcept {
        if (left != other.left) return false;
        else return !strcmp(buffer.data(), other.buffer.data());
      }
      bool operator !=(inline_string_layout const& other) const noexcept {
        return !(*this == other);
      }

      std::array<char, sizeof(dynamic_string_layout) - sizeof(uint8_t)> buffer;
      uint8_t left;
    };

    /**
     *  @brief
     *  Helper-struct for safely comparing objects of any type.
     */
    struct typeless_comparator {
      template <class Lhs, class Rhs>
      bool operator ()(Lhs&& lhs, Rhs&& rhs) const noexcept;
    };

    template <template <class> class RefCount>
    using buffer_refcount_type = shareable_ptr<RefCount<gsl::byte const>>;

    class prefix_entry;
    template <class>
    struct table_layout {
      using offset_type = uint32_t;
      static constexpr auto max_offset = std::numeric_limits<offset_type>::max();

      alignas(4) little_order<offset_type> offset;
      alignas(4) little_order<uint8_t> type;
    };
    template <>
    struct table_layout<prefix_entry> {
      using offset_type = uint32_t;
      using prefix_type = uint16_t;
      static constexpr auto max_offset = std::numeric_limits<offset_type>::max();

      alignas(4) little_order<uint32_t> offset;
      alignas(1) little_order<uint8_t> type;
      alignas(1) little_order<uint8_t> len;
      alignas(2) prefix_type prefix;
    };

    /**
     *  @brief
     *  Macro customizes functionality usually provided by assert().
     *
     *  @details
     *  Not strictly necessary, but tries to provide a bit more context and
     *  information as to why I just murdered the user's program (in production, no doubt).
     *
     *  @remarks
     *  Don't actually know if Doxygen lets you document macros, guess we'll see.
     */
    template <class T>
    class vtable_entry {

      public:

        /*----- Lifecycle Functions -----*/

        vtable_entry(detail::raw_type type, uint32_t offset);
        vtable_entry(vtable_entry const&) = default;

        /*----- Operators -----*/

        vtable_entry& operator =(vtable_entry const&) = default;

        /*----- Public API -----*/

        raw_type get_type() const noexcept;
        uint32_t get_offset() const noexcept;

        // This sucks, but we need it for dart::buffer::inject
        void adjust_offset(std::ptrdiff_t diff) noexcept;

      protected:

        /*----- Protected Members -----*/

        table_layout<T> layout;

    };

    /**
     *  @brief
     *  Class represents an entry in the vtable of an object.
     *
     *  @details
     *  Class wraps memory that is stored in little endian byte order,
     *  irrespective of the native ordering of the host machine.
     *  In other words, attempt to subvert its API at your peril.
     *
     *  @remarks
     *  Although class inherts from vtable_entry, and can be safely
     *  be used as such, it remains a standard layout type due to some
     *  hackery with its internals.
     */
    class prefix_entry : public vtable_entry<prefix_entry> {

      public:

        /*----- Public Types -----*/

        using prefix_type = table_layout<prefix_entry>::prefix_type;

        /*----- Lifecycle Functions -----*/

        inline prefix_entry(detail::raw_type type, uint32_t offset, shim::string_view prefix) noexcept;
        prefix_entry(prefix_entry const&) = default;

        /*----- Operators -----*/

        prefix_entry& operator =(prefix_entry const&) = default;

        /*----- Public API -----*/

        inline int prefix_compare(shim::string_view str) const noexcept;

      private:

        /*----- Private Helpers -----*/

        inline int compare_impl(char const* const str, size_t const len) const noexcept;

        /*----- Private Types -----*/

        using storage_t = std::aligned_storage_t<sizeof(prefix_type), 4>;

    };

    using object_entry = prefix_entry;
    using array_entry = vtable_entry<void>;
    using object_layout = table_layout<prefix_entry>;
    using array_layout = table_layout<void>;
    static_assert(std::is_standard_layout<array_entry>::value, "dart library is misconfigured");
    static_assert(std::is_standard_layout<object_entry>::value, "dart library is misconfigured");

    // Aliases for STL structures.
    template <template <class> class RefCount>
    using packet_elements = std::vector<refcount::owner_indirection_t<basic_heap, RefCount>>;
    template <template <class> class RefCount>
    using packet_fields = std::map<
      refcount::owner_indirection_t<basic_heap, RefCount>,
      refcount::owner_indirection_t<basic_heap, RefCount>,
      refcount::owner_indirection_t<dart_comparator, RefCount>
    >;

    /**
     *  @brief
     *  Struct represents the minimum context required to perform
     *  an operation on a dart::buffer object.
     *
     *  @details
     *  At its core, this is what dart::buffer "is".
     *  It just provides a nice API, and some memory safety around
     *  this structure, which is why it's so fast.
     */
    struct raw_element {
      raw_type type;
      gsl::byte const* buffer;
    };

    /**
     *  @brief
     *  Struct is the lowest level abstraction for safe iteration over a dart::buffer
     *  object/array.
     *
     *  @details
     *  Struct holds onto the context for what vtable entry it's currently on,
     *  where the base of its aggregate is, and how to dereference itself.
     *
     *  @remarks
     *  ll_iterator holds onto more state than I'd like, but it's kind of unavoidable
     *  given the fact that the vtable provides offsets from the base of the aggregate
     *  itself and the fact that we may be iterating over values OR pairs of values.
     */
    template <template <class> class RefCount>
    struct ll_iterator {

      /*----- Types -----*/

      using value_type = raw_element;
      using loading_function = value_type (gsl::byte const*, size_t idx);

      /*----- Lifecycle Functions -----*/

      ll_iterator() = delete;
      ll_iterator(size_t idx, gsl::byte const* base, loading_function* load_func) noexcept :
        idx(idx),
        base(base),
        load_func(load_func)
      {}
      ll_iterator(ll_iterator const&) = default;
      ll_iterator(ll_iterator&&) noexcept = default;
      ~ll_iterator() = default;

      /*----- Operators -----*/

      ll_iterator& operator =(ll_iterator const&) = default;
      ll_iterator& operator =(ll_iterator&&) noexcept = default;

      bool operator ==(ll_iterator const& other) const noexcept;
      bool operator !=(ll_iterator const& other) const noexcept;

      auto operator ++() noexcept -> ll_iterator&;
      auto operator --() noexcept -> ll_iterator&;
      auto operator ++(int) noexcept -> ll_iterator;
      auto operator --(int) noexcept -> ll_iterator;

      auto operator *() const noexcept -> value_type;

      /*----- Members -----*/

      size_t idx;
      gsl::byte const* base;
      loading_function* load_func;

    };

    /**
     *  @brief
     *  Struct is the lowest level abstraction for safe iteration over a dart::heap
     *  object/array.
     *
     *  @details
     *  Struct simply wraps an STL iterator of some type, and a way to dereference it.
     */
    template <template <class> class RefCount>
    struct dn_iterator {

      /*----- Types -----*/

      using value_type = basic_heap<RefCount>;
      using reference = value_type const&;
      using fields_deref_func = reference (typename packet_fields<RefCount>::const_iterator const&);
      using elements_deref_func = reference (typename packet_elements<RefCount>::const_iterator const&);

      struct fields_layout {
        fields_deref_func* deref;
        typename packet_fields<RefCount>::const_iterator it;
      };

      struct elements_layout {
        elements_deref_func* deref;
        typename packet_elements<RefCount>::const_iterator it;
      };

      using implerator = shim::variant<fields_layout, elements_layout>;

      /*----- Lifecycle Functions -----*/

      dn_iterator() = delete;
      dn_iterator(typename packet_fields<RefCount>::const_iterator it, fields_deref_func* func) noexcept :
        impl(fields_layout {func, it})
      {}
      dn_iterator(typename packet_elements<RefCount>::const_iterator it, elements_deref_func* func) noexcept :
        impl(elements_layout {func, it})
      {}
      dn_iterator(dn_iterator const&) = default;
      dn_iterator(dn_iterator&&) noexcept = default;
      ~dn_iterator() = default;

      /*----- Operators -----*/

      dn_iterator& operator =(dn_iterator const&) = default;
      dn_iterator& operator =(dn_iterator&&) noexcept = default;

      bool operator ==(dn_iterator const& other) const noexcept;
      bool operator !=(dn_iterator const& other) const noexcept;

      auto operator ++() noexcept -> dn_iterator&;
      auto operator --() noexcept -> dn_iterator&;
      auto operator ++(int) noexcept -> dn_iterator;
      auto operator --(int) noexcept -> dn_iterator;

      auto operator *() const noexcept -> reference;

      /*----- Members -----*/

      implerator impl;

    };
    template <template <class> class RefCount>
    using dynamic_iterator = refcount::owner_indirection_t<dn_iterator, RefCount>;

    /**
     *  @brief
     *  Class is the lowest level abstraction for safe interaction with
     *  a dart::buffer object.
     *
     *  @details
     *  Class wraps memory that is stored in little endian byte order,
     *  irrespective of the native ordering of the host machine.
     *  In other words, attempt to subvert its API at your peril.
     */
    template <template <class> class RefCount>
    class object {

      public:

        /*----- Lifecycle Functions -----*/

        object() = delete;

        // JSON Constructors
#ifdef DART_USE_SAJSON
        explicit object(sajson::value fields) noexcept;
#endif
#if DART_HAS_RAPIDJSON
        explicit object(rapidjson::Value const& fields) noexcept;
#endif

        // Direct constructors
        explicit object(gsl::span<packet_pair<RefCount>> pairs) noexcept;
        explicit object(packet_fields<RefCount> const* fields) noexcept;

        // Special constructors
        object(object const* base, object const* incoming) noexcept;
        template <class Key>
        object(object const* base, gsl::span<Key const*> key_ptrs) noexcept;

        object(object const&) = delete;
        ~object() = delete;

        /*----- Operators -----*/

        object& operator =(object const&) = delete;

        /*----- Public API -----*/

        template <bool silent>
        bool is_valid(size_t bytes) const noexcept(silent);

        size_t size() const noexcept;
        size_t get_sizeof() const noexcept;

        auto begin() const noexcept -> ll_iterator<RefCount>;
        auto key_begin() const noexcept -> ll_iterator<RefCount>;
        auto end() const noexcept -> ll_iterator<RefCount>;
        auto key_end() const noexcept -> ll_iterator<RefCount>;

        template <class Callback>
        auto get_key(shim::string_view const key, Callback&& cb) const noexcept -> raw_element;
        auto get_it(shim::string_view const key) const noexcept -> ll_iterator<RefCount>;
        auto get_key_it(shim::string_view const key) const noexcept -> ll_iterator<RefCount>;
        auto get_value(shim::string_view const key) const noexcept -> raw_element;
        auto at_value(shim::string_view const key) const -> raw_element;

        static auto load_key(gsl::byte const* base, size_t idx) noexcept -> typename ll_iterator<RefCount>::value_type;
        static auto load_value(gsl::byte const* base, size_t idx) noexcept -> typename ll_iterator<RefCount>::value_type;

        /*----- Public Members -----*/

        static constexpr auto alignment = sizeof(int64_t);

      private:

        /*----- Private Helpers -----*/

        size_t realign(size_t guess, size_t offset) noexcept;

        template <class Callback>
        auto get_value_impl(shim::string_view const key, Callback&& cb) const -> raw_element;

        object_entry* vtable() noexcept;
        object_entry const* vtable() const noexcept;

        gsl::byte* raw_vtable() noexcept;
        gsl::byte const* raw_vtable() const noexcept;

        /*----- Private Members -----*/

        alignas(4) little_order<uint32_t> bytes;
        alignas(4) little_order<uint32_t> elems;

        static constexpr auto header_len = sizeof(bytes) + sizeof(elems);

    };
    static_assert(std::is_standard_layout<object<std::shared_ptr>>::value, "dart library is misconfigured");

    /**
     *  @brief
     *  Class is the lowest level abstraction for safe interaction with
     *  a dart::buffer array.
     *
     *  @details
     *  Class wraps memory that is stored in little endian byte order,
     *  irrespective of the native ordering of the host machine.
     *  In other words, attempt to subvert its API at your peril.
     */
    template <template <class> class RefCount>
    class array {

      public:

        /*----- Lifecycle Functions -----*/

        array() = delete;
#ifdef DART_USE_SAJSON
        explicit array(sajson::value elems) noexcept;
#endif
#if DART_HAS_RAPIDJSON
        explicit array(rapidjson::Value const& elems) noexcept;
#endif
        array(packet_elements<RefCount> const* elems) noexcept;
        array(array const&) = delete;
        ~array() = delete;

        /*----- Operators -----*/

        array& operator =(array const&) = delete;

        /*----- Public API -----*/

        template <bool silent>
        bool is_valid(size_t bytes) const noexcept(silent);

        size_t size() const noexcept;
        size_t get_sizeof() const noexcept;

        auto begin() const noexcept -> ll_iterator<RefCount>;
        auto end() const noexcept -> ll_iterator<RefCount>;

        auto get_elem(size_t index) const noexcept -> raw_element;
        auto at_elem(size_t index) const -> raw_element;

        static auto load_elem(gsl::byte const* base, size_t idx) noexcept -> typename ll_iterator<RefCount>::value_type;

        /*----- Public Members -----*/

        static constexpr auto alignment = sizeof(int64_t);

      private:

        /*----- Private Helpers -----*/

        auto get_elem_impl(size_t index, bool throw_if_absent) const -> raw_element;

        array_entry* vtable() noexcept;
        array_entry const* vtable() const noexcept;

        gsl::byte* raw_vtable() noexcept;
        gsl::byte const* raw_vtable() const noexcept;

        /*----- Private Members -----*/

        alignas(4) little_order<uint32_t> bytes;
        alignas(4) little_order<uint32_t> elems;

        static constexpr auto header_len = sizeof(bytes) + sizeof(elems);

    };
    static_assert(std::is_standard_layout<array<std::shared_ptr>>::value, "dart library is misconfigured");

    /**
     *  @brief
     *  Class is the lowest level abstraction for safe interaction with
     *  a dart::buffer string.
     *
     *  @details
     *  Class wraps memory that is stored in little endian byte order,
     *  irrespective of the native ordering of the host machine.
     *  In other words, attempt to subvert its API at your peril.
     */
    template <class SizeType>
    class basic_string {

      public:

        /*----- Public Types -----*/

        using size_type = SizeType;

        /*----- Lifecycle Functions -----*/

        basic_string() = delete;
        explicit basic_string(shim::string_view strv) noexcept;
        explicit basic_string(char const* str, size_t len) noexcept;
        basic_string(basic_string const&) = delete;
        ~basic_string() = delete;

        /*----- Operators -----*/

        basic_string& operator =(basic_string const&) = delete;

        /*----- Public API -----*/

        template <bool silent>
        bool is_valid(size_t bytes) const noexcept(silent);

        size_t size() const noexcept;
        size_t get_sizeof() const noexcept;

        shim::string_view get_strv() const noexcept;

        static size_t static_sizeof(size_type len) noexcept;

        /*----- Public Members -----*/

        static constexpr auto alignment = sizeof(size_type);

      private:

        /*----- Private Helpers -----*/

        char* data() noexcept;
        char const* data() const noexcept;

        /*----- Private Members -----*/

        alignas(alignment) little_order<size_type> len;

        static constexpr auto header_len = sizeof(len);

    };
    using string = basic_string<uint16_t>;
    using big_string = basic_string<uint32_t>;
    static_assert(std::is_standard_layout<string>::value, "dart library is misconfigured");
    static_assert(std::is_standard_layout<big_string>::value, "dart library is misconfigured");

    /**
     *  @brief
     *  Class is the lowest level abstraction for safe interaction with
     *  a dart::buffer primitive value.
     *
     *  @details
     *  Class wraps memory that is stored in little endian byte order,
     *  irrespective of the native ordering of the host machine.
     *  In other words, attempt to subvert its API at your peril.
     */
    template <class T>
    class primitive {

      public:

        /*----- Lifecycle Functions -----*/

        primitive() = delete;
        explicit primitive(T data) noexcept : data(data) {}
        primitive(primitive const&) = delete;
        ~primitive() = delete;

        /*---- Operators -----*/

        primitive& operator =(primitive const&) = delete;

        /*----- Public API -----*/

        template <bool silent>
        bool is_valid(size_t bytes) const noexcept(silent);

        size_t get_sizeof() const noexcept;

        T get_data() const noexcept;

        static size_t static_sizeof() noexcept;

        /*----- Public Members -----*/

        static constexpr auto alignment = sizeof(T);

      private:

        /*----- Private Members -----*/

        alignas(alignment) little_order<T> data;

        static constexpr auto header_len = sizeof(data);

    };
    static_assert(std::is_standard_layout<primitive<int>>::value, "dart library is misconfigured");

    template <template <class> class RefCount>
    struct buffer_builder {
      using buffer = basic_buffer<RefCount>;

      template <class Span>
      static auto build_buffer(Span pairs) -> buffer;
      static auto merge_buffers(buffer const& base, buffer const& incoming) -> buffer;

      template <class Spannable>
      static auto project_keys(buffer const& base, Spannable const& keys) -> buffer;

      template <class Callback>
      static void each_unique_pair(object<RefCount> const* base, object<RefCount> const* incoming, Callback&& cb);
      template <class Key, class Callback>
      static void project_each_pair(object<RefCount> const* base, gsl::span<Key const*> key_ptrs, Callback&& cb);

      template <class Span>
      static size_t max_bytes(Span pairs);
    };

    // Used for tag dispatch from factory functions.
    struct view_tag {};
    struct object_tag {
      static constexpr auto alignment = object<std::shared_ptr>::alignment;
    };
    struct array_tag {
      static constexpr auto alignment = array<std::shared_ptr>::alignment;
    };
    struct string_tag {
      static constexpr auto alignment = big_string::alignment;
    };
    struct small_string_tag : string_tag {
      static constexpr auto alignment = string::alignment;
    };
    struct big_string_tag : string_tag {
      static constexpr auto alignment = string_tag::alignment;
    };
    struct integer_tag {
      static constexpr auto alignment = primitive<int64_t>::alignment;
    };
    struct short_integer_tag : integer_tag {
      static constexpr auto alignment = primitive<int16_t>::alignment;
    };
    struct medium_integer_tag : integer_tag {
      static constexpr auto alignment = primitive<int32_t>::alignment;
    };
    struct long_integer_tag : integer_tag {
      static constexpr auto alignment = integer_tag::alignment;
    };
    struct decimal_tag {
      static constexpr auto alignment = primitive<double>::alignment;
    };
    struct short_decimal_tag : decimal_tag {
      static constexpr auto alignment = primitive<float>::alignment;
    };
    struct long_decimal_tag : decimal_tag {
      static constexpr auto alignment = decimal_tag::alignment;
    };
    struct boolean_tag {
      static constexpr auto alignment = primitive<bool>::alignment;
    };
    struct null_tag {
      static constexpr auto alignment = 1;
    };

    /**
     *  @brief
     *  Function converts between internal type information and
     *  user-facing type information.
     *
     *  @details
     *  Dart internally has to encode significantly more complicated
     *  type information than it publicly exposes, so this function
     *  works as a an efficient go-between for the two.
     */
    inline type simplify_type(raw_type type) noexcept {
      switch (type) {
        case raw_type::object:
          return detail::type::object;
        case raw_type::array:
          return detail::type::array;
        case raw_type::small_string:
        case raw_type::string:
        case raw_type::big_string:
          return detail::type::string;
        case raw_type::short_integer:
        case raw_type::integer:
        case raw_type::long_integer:
          return detail::type::integer;
        case raw_type::decimal:
        case raw_type::long_decimal:
          return detail::type::decimal;
        case raw_type::boolean:
          return detail::type::boolean;
        default:
          DART_ASSERT(type == raw_type::null);
          return detail::type::null;
      }
    }

    inline bool valid_type(raw_type type) noexcept {
      switch (type) {
        case raw_type::object:
        case raw_type::array:
        case raw_type::string:
        case raw_type::small_string:
        case raw_type::big_string:
        case raw_type::short_integer:
        case raw_type::integer:
        case raw_type::long_integer:
        case raw_type::decimal:
        case raw_type::long_decimal:
        case raw_type::boolean:
        case raw_type::null:
          return true;
        default:
          return false;
      }
    }

    /**
     *  @brief
     *  Function provides a "safe" bridge between the high level
     *  and low level object apis.
     *
     *  @details
     *  Don't pass it null.
     *  Tries its damndest not to invoke UB, but god knows.
     */
    template <template <class> class RefCount>
    object<RefCount> const* get_object(raw_element raw) {
      if (simplify_type(raw.type) == type::object) {
        DART_ASSERT(raw.buffer != nullptr);
        return shim::launder(reinterpret_cast<object<RefCount> const*>(raw.buffer));
      } else {
        throw type_error("dart::buffer is not a finalized object and cannot be accessed as such");
      }
    }

    /**
     *  @brief
     *  Function provides a "safe" bridge between the high level
     *  and low level array apis.
     *
     *  @details
     *  Don't pass it null.
     *  Tries its damndest not to invoke UB, but god knows.
     */
    template <template <class> class RefCount>
    array<RefCount> const* get_array(raw_element raw) {
      if (simplify_type(raw.type) == type::array) {
        DART_ASSERT(raw.buffer != nullptr);
        return shim::launder(reinterpret_cast<array<RefCount> const*>(raw.buffer));
      } else {
        throw type_error("dart::buffer is not a finalized array and cannot be accessed as such");
      }
    }

    /**
     *  @brief
     *  Function provides a "safe" bridge between the high level
     *  and low level string apis.
     *
     *  @details
     *  Don't pass it null.
     *  Tries its damndest not to invoke UB, but god knows.
     */
    inline string const* get_string(raw_element raw) {
      if (raw.type == raw_type::small_string || raw.type == raw_type::string) {
        DART_ASSERT(raw.buffer != nullptr);
        return shim::launder(reinterpret_cast<string const*>(raw.buffer));
      } else {
        throw type_error("dart::buffer is not a finalized string and cannot be accessed as such");
      }
    }

    /**
     *  @brief
     *  Function provides a "safe" bridge between the high level
     *  and low level string apis.
     *
     *  @details
     *  Don't pass it null.
     *  Tries its damndest not to invoke UB, but god knows.
     */
    inline big_string const* get_big_string(raw_element raw) {
      if (raw.type == raw_type::big_string) {
        DART_ASSERT(raw.buffer != nullptr);
        return shim::launder(reinterpret_cast<big_string const*>(raw.buffer));
      } else {
        throw type_error("dart::buffer is not a finalized string and cannot be accessed as such");
      }
    }

    /**
     *  @brief
     *  Function provides a "safe" bridge between the high level
     *  and low level primitive apis.
     *
     *  @details
     *  Don't pass it null.
     *  Tries its damndest not to invoke UB, but god knows.
     */
    template <class T>
    primitive<T> const* get_primitive(raw_element raw) {
      auto simple = simplify_type(raw.type);
      if (simple == type::integer || simple == type::decimal || simple == type::boolean) {
        DART_ASSERT(raw.buffer != nullptr);
        return shim::launder(reinterpret_cast<primitive<T> const*>(raw.buffer));
      } else {
        throw type_error("dart::buffer is not a finalized primitive and cannot be accessed as such");
      }
    }

    template <template <class> class RefCount, class Callback>
    auto aggregate_deref(Callback&& cb, raw_element raw)
      -> std::common_type_t<
        decltype(std::forward<Callback>(cb)(std::declval<object<RefCount>&>())),
        decltype(std::forward<Callback>(cb)(std::declval<array<RefCount>&>()))
      >
    {
      switch (raw.type) {
        case raw_type::object:
          return std::forward<Callback>(cb)(*get_object<RefCount>(raw));
        case raw_type::array:
          return std::forward<Callback>(cb)(*get_array<RefCount>(raw));
        default:
          throw type_error("dart::buffer is not a finalized aggregate and cannot be accessed as such");
      }
    }

    template <class Callback>
    auto string_deref(Callback&& cb, raw_element raw)
      -> std::common_type_t<
        decltype(std::forward<Callback>(cb)(std::declval<string&>())),
        decltype(std::forward<Callback>(cb)(std::declval<big_string&>()))
      >
    {
      switch (raw.type) {
        case raw_type::small_string:
        case raw_type::string:
          return std::forward<Callback>(cb)(*get_string(raw));
        case raw_type::big_string:
          return std::forward<Callback>(cb)(*get_big_string(raw));
        default:
          throw type_error("dart::buffer is not a finalized string and cannot be accessed as such");
      }
    }

    template <class Callback>
    auto integer_deref(Callback&& cb, raw_element raw)
      -> std::common_type_t<
        decltype(std::forward<Callback>(cb)(std::declval<primitive<int16_t>&>())),
        decltype(std::forward<Callback>(cb)(std::declval<primitive<int32_t>&>())),
        decltype(std::forward<Callback>(cb)(std::declval<primitive<int64_t>&>()))
      >
    {
      switch (raw.type) {
        case raw_type::short_integer:
          return std::forward<Callback>(cb)(*get_primitive<int16_t>(raw));
        case raw_type::integer:
          return std::forward<Callback>(cb)(*get_primitive<int32_t>(raw));
        case raw_type::long_integer:
          return std::forward<Callback>(cb)(*get_primitive<int64_t>(raw));
        default:
          throw type_error("dart::buffer is not a finalized integer and cannot be accessed as such");
      }
    }

    template <class Callback>
    auto decimal_deref(Callback&& cb, raw_element raw)
      -> std::common_type_t<
        decltype(std::forward<Callback>(cb)(std::declval<primitive<float>&>())),
        decltype(std::forward<Callback>(cb)(std::declval<primitive<double>&>()))
      >
    {
      switch (raw.type) {
        case raw_type::decimal:
          return std::forward<Callback>(cb)(*get_primitive<float>(raw));
        case raw_type::long_decimal:
          return std::forward<Callback>(cb)(*get_primitive<double>(raw));
        default:
          throw type_error("dart::buffer is not a finalized decimal and cannot be accessed as such");
      }
    }

    template <class Callback>
    auto match_generic(Callback&& cb, raw_type raw)
      -> std::common_type_t<
        decltype(std::forward<Callback>(cb)(object_tag {})),
        decltype(std::forward<Callback>(cb)(array_tag {})),
        decltype(std::forward<Callback>(cb)(small_string_tag {})),
        decltype(std::forward<Callback>(cb)(big_string_tag {})),
        decltype(std::forward<Callback>(cb)(short_integer_tag {})),
        decltype(std::forward<Callback>(cb)(medium_integer_tag {})),
        decltype(std::forward<Callback>(cb)(long_integer_tag {})),
        decltype(std::forward<Callback>(cb)(short_decimal_tag {})),
        decltype(std::forward<Callback>(cb)(long_decimal_tag {})),
        decltype(std::forward<Callback>(cb)(boolean_tag {})),
        decltype(std::forward<Callback>(cb)(null_tag {}))
      >
    {
      switch (raw) {
        case raw_type::object:
          return std::forward<Callback>(cb)(object_tag {});
        case raw_type::array:
          return std::forward<Callback>(cb)(array_tag {});
        case raw_type::small_string:
        case raw_type::string:
          return std::forward<Callback>(cb)(small_string_tag {});
        case raw_type::big_string:
          return std::forward<Callback>(cb)(big_string_tag {});
        case raw_type::short_integer:
          return std::forward<Callback>(cb)(short_integer_tag {});
        case raw_type::integer:
          return std::forward<Callback>(cb)(medium_integer_tag {});
        case raw_type::long_integer:
          return std::forward<Callback>(cb)(long_integer_tag {});
        case raw_type::decimal:
          return std::forward<Callback>(cb)(short_decimal_tag {});
        case raw_type::long_decimal:
          return std::forward<Callback>(cb)(long_decimal_tag {});
        case raw_type::boolean:
          return std::forward<Callback>(cb)(boolean_tag {});
        default:
          DART_ASSERT(raw == raw_type::null);
          return std::forward<Callback>(cb)(null_tag {});
      }
    }

    template <template <class> class RefCount, class Callback>
    auto generic_deref(Callback&& cb, raw_element raw)
      -> std::common_type_t<
        decltype(aggregate_deref<RefCount>(std::forward<Callback>(cb), raw)),
        decltype(string_deref(std::forward<Callback>(cb), raw)),
        decltype(integer_deref(std::forward<Callback>(cb), raw)),
        decltype(decimal_deref(std::forward<Callback>(cb), raw)),
        decltype(std::forward<Callback>(cb)(std::declval<primitive<bool>&>()))
      >
    {
      switch (raw.type) {
        case raw_type::object:
        case raw_type::array:
          return aggregate_deref<RefCount>(std::forward<Callback>(cb), raw);
        case raw_type::small_string:
        case raw_type::string:
        case raw_type::big_string:
          return string_deref(std::forward<Callback>(cb), raw);
        case raw_type::short_integer:
        case raw_type::integer:
        case raw_type::long_integer:
          return integer_deref(std::forward<Callback>(cb), raw);
        case raw_type::decimal:
        case raw_type::long_decimal:
          return decimal_deref(std::forward<Callback>(cb), raw);
        case raw_type::boolean:
          return std::forward<Callback>(cb)(*get_primitive<bool>(raw));
        default:
          DART_ASSERT(raw.type == raw_type::null);
          throw type_error("dart::buffer is null, and has no value to access");
      }
    }

    // Returns the native alignment requirements of the given type.
    template <template <class> class RefCount>
    constexpr size_t alignment_of(raw_type type) noexcept {
      return match_generic([] (auto v) { return decltype(v)::alignment; }, type);
    }

    // Function takes a pointer and returns a pointer to the "next" (greater than OR equal to) boundary
    // conforming to the native alignment of the given type.
    // Assumes that all alignment requests are for powers of two.
    template <template <class> class RefCount, class T>
    constexpr T* align_pointer(T* ptr, raw_type type) noexcept {
      // Get the required alignment for a pointer of this type.
      auto alignment = alignment_of<RefCount>(type);

      // Bump forward to the next boundary of the given alignment requirement.
      uintptr_t offset = reinterpret_cast<uintptr_t>(ptr);
      return reinterpret_cast<T*>((offset + (alignment - 1)) & ~(alignment - 1));
    }

    template <template <class> class RefCount, class T>
    constexpr T pad_bytes(T bytes, raw_type type) noexcept {
      // Get the required alignment for a pointer of this type.
      auto alignment = alignment_of<RefCount>(type);

      // Pad the given bytes to ensure its end lands on an alignment boundary.
      return (bytes + (alignment - 1)) & ~(alignment - 1);
    }

    template <template <class> class RefCount>
    size_t find_sizeof(raw_element elem) noexcept {
      if (elem.type != raw_type::null) {
        return generic_deref<RefCount>([] (auto& v) { return v.get_sizeof(); }, elem);
      } else {
        return 0;
      }
    }

    template <bool silent, template <class> class RefCount>
    bool valid_buffer(raw_element elem, size_t bytes) noexcept(silent) {
      // Null is a special case because it occupies zero space in the network buffer
      // Check if the given element has a valid type.
      // If we're validating against corrupted garbage it likely won't
      if (elem.type == raw_type::null) return true;
      else if (!valid_type(elem.type)) return false;

      // Call through to our implementation
      return generic_deref<RefCount>([=] (auto& v) { return v.template is_valid<silent>(bytes); }, elem);
    }

    template <template <class> class RefCount>
    constexpr size_t sso_bytes() {
      return basic_heap<RefCount>::sso_bytes;
    }

    template <template <class> class RefCount>
    raw_type identify_string(shim::string_view base, shim::string_view app = "") noexcept {
      auto total = base.size() + app.size();
      if (total > std::numeric_limits<uint16_t>::max()) {
        return detail::raw_type::big_string;
      } else if (total > sso_bytes<RefCount>()) {
        return detail::raw_type::string;
      } else {
        return detail::raw_type::small_string;
      }
    }

    constexpr raw_type identify_integer(int64_t val) noexcept {
      if (val > INT32_MAX || val < INT32_MIN) return raw_type::long_integer;
      else if (val > INT16_MAX || val < INT16_MIN) return raw_type::integer;
      else return raw_type::short_integer;
    }

    // Returns the smallest floating point type capable of precisely representing the given
    // value.
    constexpr raw_type identify_decimal(double val) noexcept {
      // XXX: I'm not impressed by this check either, but I can't find any legit way
      // to check if a double will be precisely representable as a float.
      if (val != static_cast<float>(val)) return raw_type::long_decimal;
      else return raw_type::decimal;
    }

// Dart is header-only, so I think the scenarios where the ABI stability of
// symbol mangling will be relevant are relatively few.
#if DART_USING_GCC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wnoexcept-type"
#elif DART_USING_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wc++17-compat-mangling"
#pragma clang diagnostic ignored "-Wc++1z-compat"
#endif

    template <class Packet, class T>
    T safe_optional_access(Packet const& that, T opt,
        bool (Packet::* guard) () const noexcept, T (Packet::* accessor) () const) {
      if (!(that.*guard)()) {
        return opt;
      } else try {
        return (that.*accessor)();
      } catch (...) {
        return opt;
      }
    }

#if DART_USING_GCC
#pragma GCC diagnostic pop
#elif DART_USING_CLANG
#pragma clang diagnostic pop
#endif

    template <template <class> class RefCount, class Owner = buffer_refcount_type<RefCount>, class Callback>
    Owner aligned_alloc(size_t bytes, raw_type type, Callback&& cb) {
      // Make an aligned allocation.
      gsl::byte* tmp;
      int retval = shim::aligned_alloc(reinterpret_cast<void**>(&tmp), alignment_of<RefCount>(type), bytes);
      if (retval) throw std::bad_alloc();

      // Associate it with an owner in case anything goes wrong.
      Owner ref {tmp, +[] (gsl::byte const* ptr) { shim::aligned_free(const_cast<gsl::byte*>(ptr)); }};

      // Hand out mutable access and return.
      cb(tmp);
      return ref;
    }

    template <template <class> class RefCount, size_t static_elems = 8, class Spannable, class Callback>
    decltype(auto) sort_spannable(Spannable const& elems, Callback&& cb) {
      // XXX: This is necessary because I'm supporting an ANCIENT version of gsl-lite that
      // doesn't contain the nested type alias value_type, only element_type
      // Modern versions of gsl-lite, and Microsoft GSL, and std::span, include a value_type
      // As a fix, meta::spannable_value_type_t returns value_type if it exists,
      // next element_type if it exists,
      // and finally meta::nonesuch if neither alias exists
      using value_type = meta::spannable_value_type_t<Spannable>;
      static_assert(
        !std::is_same<
          value_type,
          meta::nonesuch
        >::value,
        "Sort spannable only supports types that conform to the std::span interface"
      );

      // Check if we can perform the sorting statically.
      dart_comparator<RefCount> comp;
      auto const size = static_cast<size_t>(elems.size());
      if (size <= static_elems) {
        // We've got enough static space, setup a local array.
        std::array<value_type const*, static_elems> ptrs;
        std::transform(std::begin(elems), std::end(elems), std::begin(ptrs), [] (auto& e) { return &e; });

        // Sort the pointers and pass back to the user.
        std::sort(ptrs.data(), ptrs.data() + size, [&] (auto* lhs, auto* rhs) { return comp(*lhs, *rhs); });
        return cb(gsl::make_span(ptrs.data(), size));
      } else {
        // Dynamic fallback.
        std::vector<value_type const*> ptrs(size, nullptr);
        std::transform(std::begin(elems), std::end(elems), std::begin(ptrs), [] (auto& e) { return &e; });

        // Sort and pass back.
        std::sort(std::begin(ptrs), std::end(ptrs), [&] (auto* lhs, auto* rhs) { return comp(*lhs, *rhs); });
        return cb(gsl::make_span(ptrs));
      }
    }

#ifdef DART_USE_SAJSON
    // FIXME: Find somewhere better to put these functions.
    template <template <class> class RefCount>
    raw_type json_identify(sajson::value curr_val) {
      switch (curr_val.get_type()) {
        case sajson::TYPE_OBJECT:
          return raw_type::object;
        case sajson::TYPE_ARRAY:
          return raw_type::array;
        case sajson::TYPE_STRING:
          // Figure out what type of string we've been given.
          return identify_string<RefCount>({curr_val.as_cstring(), curr_val.get_string_length()});
        case sajson::TYPE_INTEGER:
          return identify_integer(curr_val.get_integer_value());
        case sajson::TYPE_DOUBLE:
          return identify_decimal(curr_val.get_double_value());
        case sajson::TYPE_FALSE:
        case sajson::TYPE_TRUE:
          return raw_type::boolean;
        default:
          DART_ASSERT(curr_val.get_type() == sajson::TYPE_NULL);
          return raw_type::null;
      }
    }

    template <template <class> class RefCount>
    size_t json_lower(gsl::byte* buffer, sajson::value curr_val) {
      auto raw = json_identify<RefCount>(curr_val);
      switch (raw) {
        case raw_type::object:
          new(buffer) object<RefCount>(curr_val);
          break;
        case raw_type::array:
          new(buffer) array<RefCount>(curr_val);
          break;
        case raw_type::small_string:
        case raw_type::string:
          new(buffer) string(curr_val.as_cstring(), curr_val.get_string_length());
          break;
        case raw_type::big_string:
          new(buffer) big_string(curr_val.as_cstring(), curr_val.get_string_length());
          break;
        case raw_type::short_integer:
          new(buffer) primitive<int16_t>(curr_val.get_integer_value());
          break;
        case raw_type::integer:
          new(buffer) primitive<int32_t>(curr_val.get_integer_value());
          break;
        case raw_type::long_integer:
          new(buffer) primitive<int64_t>(curr_val.get_integer_value());
          break;
        case raw_type::decimal:
          new(buffer) primitive<float>(curr_val.get_double_value());
          break;
        case raw_type::long_decimal:
          new(buffer) primitive<double>(curr_val.get_double_value());
          break;
        case raw_type::boolean:
          new(buffer) detail::primitive<bool>((curr_val.get_type() == sajson::TYPE_TRUE) ? true : false);
          break;
        default:
          DART_ASSERT(curr_val.get_type() == sajson::TYPE_NULL);
          break;
      }
      return detail::find_sizeof<RefCount>({raw, buffer});
    }
#endif

#if DART_HAS_RAPIDJSON
    // FIXME: Find somewhere better to put these functions.
    template <template <class> class RefCount>
    raw_type json_identify(rapidjson::Value const& curr_val) {
      switch (curr_val.GetType()) {
        case rapidjson::kObjectType:
          return raw_type::object;
        case rapidjson::kArrayType:
          return raw_type::array;
        case rapidjson::kStringType:
          // Figure out what type of string we've been given.
          return identify_string<RefCount>({curr_val.GetString(), curr_val.GetStringLength()});
        case rapidjson::kNumberType:
          // Figure out what type of number we've been given.
          if (curr_val.IsInt64()) return identify_integer(curr_val.GetInt64());
          else return identify_decimal(curr_val.GetDouble());
        case rapidjson::kTrueType:
        case rapidjson::kFalseType:
          return raw_type::boolean;
        default:
          DART_ASSERT(curr_val.IsNull());
          return raw_type::null;
      }
    }

    template <template <class> class RefCount>
    size_t json_lower(gsl::byte* buffer, rapidjson::Value const& curr_val) {
      auto raw = json_identify<RefCount>(curr_val);
      switch (raw) {
        case raw_type::object:
          new(buffer) object<RefCount>(curr_val);
          break;
        case raw_type::array:
          new(buffer) array<RefCount>(curr_val);
          break;
        case raw_type::small_string:
        case raw_type::string:
          new(buffer) string(curr_val.GetString(), curr_val.GetStringLength());
          break;
        case raw_type::big_string:
          new(buffer) big_string(curr_val.GetString(), curr_val.GetStringLength());
          break;
        case raw_type::short_integer:
          new(buffer) primitive<int16_t>(curr_val.GetInt());
          break;
        case raw_type::integer:
          new(buffer) primitive<int32_t>(curr_val.GetInt());
          break;
        case raw_type::long_integer:
          new(buffer) primitive<int64_t>(curr_val.GetInt64());
          break;
        case raw_type::decimal:
          new(buffer) primitive<float>(curr_val.GetFloat());
          break;
        case raw_type::long_decimal:
          new(buffer) primitive<double>(curr_val.GetDouble());
          break;
        case raw_type::boolean:
          new(buffer) detail::primitive<bool>(curr_val.GetBool());
          break;
        default:
          DART_ASSERT(curr_val.IsNull());
          break;
      }
      return detail::find_sizeof<RefCount>({raw, buffer});
    }
#endif

    // Helper function handles the edge case where we're working with
    // a view type, and we need to cast it back to an owner, ONLY IF
    // we are an owner ourselves.
    template <class MaybeView, class View>
    decltype(auto) view_return_indirection(View&& view) {
      return shim::compose_together(
        [] (auto&& v, std::true_type) -> decltype(auto) {
          return std::forward<decltype(v)>(v);
        },
        [] (auto&& v, std::false_type) -> decltype(auto) {
          return std::forward<decltype(v)>(v).as_owner();
        }
      )(std::forward<View>(view), std::is_same<std::decay_t<MaybeView>, std::decay_t<View>> {});
    }

    template <class Packet>
    Packet get_nested_impl(Packet haystack, shim::string_view needle, char separator) {
      // Spin through the provided needle until we reach the end, or hit a leaf.
      auto start = needle.begin();
      typename Packet::view curr = haystack;
      while (start < needle.end() && curr.is_object()) {
        // Tokenize up the needle and drag the current packet through each one.
        auto stop = std::find(start, needle.end(), separator);
        curr = curr[needle.substr(start - needle.begin(), stop - start)];

        // Prepare for next iteration.
        stop == needle.end() ? start = stop : start = stop + 1;
      }

      // If we finished, find our final value, otherwise return null.
      if (start < needle.end()) return Packet::make_null();
      else return view_return_indirection<Packet>(curr);
    }

    template <class Packet>
    std::vector<Packet> keys_impl(Packet const& that) {
      std::vector<Packet> packets;
      packets.reserve(that.size());
      for (auto it = that.key_begin(); it != that.key_end(); ++it) {
        packets.push_back(*it);
      }
      return packets;
    }

    template <class Packet>
    std::vector<Packet> values_impl(Packet const& that) {
      std::vector<Packet> packets;
      packets.reserve(that.size());
      for (auto entry : that) packets.push_back(std::move(entry));
      return packets;
    }

    template <class T>
    decltype(auto) maybe_dereference(T&& maybeptr, std::true_type) {
      return *std::forward<T>(maybeptr);
    }
    template <class T>
    decltype(auto) maybe_dereference(T&& maybeptr, std::false_type) {
      return std::forward<T>(maybeptr);
    }

  }

}

// Include main header file for all implementation files.
#include "../dart.h"
#include "common.tcc"

#endif


text:
/// FicTrac http://rjdmoore.net/fictrac/
/// \file       NLoptFunc.h
/// \brief      Wrapper class for NLopt.
/// \author     Saul Thurrowgood, Richard Moore
/// \copyright  CC BY-NC-SA 3.0

#pragma once

#include <nlopt.h>

#include <vector>

///
/// C++ wrapper of the NLopt library for nonlinear optimisation, v2.2+.
/// See:  http://ab-initio.mit.edu/nlopt
///
class NLoptFunc
{
public:
	NLoptFunc();
	virtual ~NLoptFunc();

	///
	/// Run the optimiser.  Default behaviour is to start the search at the
	/// position of the previous result.
	///
	virtual nlopt_result optimize(const double *xInit=0);

	///
	/// Sets the initial value used for the next call to optimize(),
	/// which assumes that call passes NULL as the xInit argument.
	///
	void setXInit(const double *xInit);

	///
	/// Get the optimised value.
	///
	void getOptX(float *x);
	void getOptX(double *x);
	double getOptF();

	///
	/// Returns the number of objective() evaluations by the last optimisation.
	///
	unsigned getNumEval();

	///
	/// Initialise the optimiser.  If 'minimise' is false then the objective
	/// function will be maximised.
	///
	void init(nlopt_algorithm algo, unsigned n, bool minimise=true);

    void setPopulation(unsigned int pop);

	///
	/// Set initial step size.
	///
	void setInitialStep(const double *dx);

	///
	/// Set stopping criteria.
	///
	void setLowerBounds(const double *lb);
	void setUpperBounds(const double *ub);
	void setLowerBounds(double lb); /// convenience: sets all to same value
	void setUpperBounds(double ub);
	void setFtol(double tol);   // relative
	void setXtol(double tol);   // absolute
    void setXtolRel(double tol);
	void setMaxEval(unsigned n);

	void getLowerBounds(double *lb);
	void getUpperBounds(double *ub);

	unsigned getDimension();

protected:
	unsigned _nEval;

	///
	/// Objective function executed during optimisation.
	///
	virtual double objective(unsigned n, const double *x, double *grad) = 0;

private:
	static double _cb(unsigned n, const double *x, double *grad, void *data);

	nlopt_opt _opt;
	std::vector<double> _x;
	double _optF;
};

ext: h

max_stars_repo_path: Unused Code/FicTrac v2.0 (Not Working Well !!)/fictrac2_source/include/NLoptFunc.h


text:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>

#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_cblas.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_randist.h>

#define TSSIZE 10
#define WL 10000

typedef struct tagInterpolant{
  gsl_matrix *Bs;
  int *nodes;
}Interpolant;

/* function to create an orthonormal basis set from a training set of models */
double *create_basis(double weight, double tolerance, gsl_matrix *TS, size_t *nbases);

/* function to create the empirical interpolant */
//void create_interpolant(gsl_matrix *RB, gsl_matrix **Bs, int **idxarray);
Interpolant *create_interpolant(gsl_matrix *RB);

/* define function to project model vector onto the training set of models */
void project_onto_basis(double dt, gsl_matrix *RB, gsl_matrix *TS, gsl_matrix *projections, gsl_matrix *projection_coefficients, int idx);

/* dot product of two vectors, but with one multiplied by the weighting factor */
double weighted_dot_product(double weight, gsl_vector *a, gsl_vector *b);

/* normalise a vector */
void normalise(double weight, gsl_vector *a);

/* get the B_matrix */
gsl_matrix *B_matrix(gsl_matrix *V, gsl_matrix *RB);

void print_shape(gsl_matrix *a);

/* create ROQ weights for interpolant to calculate the data dot model terms */
gsl_vector *create_data_model_weights(gsl_matrix *B, double *data);

/* calculate ROQ version of the data model dot product (where the model
   vector is the model just computed at the interpolant points) */
double roq_data_dot_model(gsl_vector *weights, double *model);

/* create ROQ weights for interpolant to calculate the model dot model terms */
gsl_matrix *create_model_model_weights(gsl_matrix *B);

/* calculate ROQ version of the model model dot product (where the model
   vector is the model just computed at the interpolant points) */
double roq_model_dot_model(gsl_matrix *weights, double *model);

double *create_basis(double weight, double tolerance, gsl_matrix *TS, size_t *nbases){
  double *RB = NULL;

  gsl_matrix *projections; /* projections of the basis onto the training set */
  gsl_matrix *residual;
  gsl_matrix *projection_coeffs;
  gsl_vector *projection_errors;
  gsl_vector_view resrow;

  double sigma = 1.;
  size_t dlength = TS->size2, nts = TS->size1;
  size_t mindex = 0, k=0;
  int idx = 0;

  /* allocate reduced basis (initially just one model vector in length).
     Here we have RB just as a double array, rather than a gsl_matrix,
     as there is no realloc functions for GSL matrices. When wanting to
     use RB as a GSL matrix we will have to use matrix views. */
  RB = calloc(dlength, sizeof(double));

  gsl_matrix_view RBview = gsl_matrix_view_array(RB, 1, dlength);
  gsl_vector *firstrow = gsl_vector_calloc(dlength);
  gsl_matrix_get_row(firstrow, TS, 0);
  gsl_matrix_set_row(&RBview.matrix, 0, firstrow);
  gsl_vector_free(firstrow);

  projection_errors = gsl_vector_calloc(dlength);
  residual = gsl_matrix_calloc(nts, dlength);
  projections = gsl_matrix_calloc(nts, dlength);
  projection_coeffs = gsl_matrix_calloc(nts, nts);

  /* create reduced basis set using greedy binning Algorithm 1 of http://arxiv.org/abs/1308.3565 */
  while ( sigma >= tolerance ){
    if ( idx > nts-1 ){
      fprintf(stderr, "Not enough training models (%zu) to produce orthonormal basis given the tolerance of %le\n", nts, tolerance);
      return NULL;
    }

    project_onto_basis(weight, &RBview.matrix, TS, projections, projection_coeffs, idx);

    gsl_matrix_memcpy(residual, TS); /* copy training set into residual */

    /* get residuals by subtracting projections from training set */
    gsl_matrix_sub(residual, projections);

    /* get projection errors */
    for( k=0; k < nts; k++ ){
      double err;

      resrow = gsl_matrix_row(residual, k);
      err = weighted_dot_product(weight, &resrow.vector, &resrow.vector);

      gsl_vector_set(projection_errors, k, err);
    }

    sigma = fabs(gsl_vector_max(projection_errors));

    if ( sigma > 1e-5 ){ fprintf(stderr, "%.12lf\t%d\n", sigma, idx); }
    else { fprintf(stderr, "%.12le\t%d\n", sigma, idx); }

    if ( sigma < tolerance ) { break; }

    /* get index of training set with the largest projection errors */
    mindex = gsl_vector_max_index( projection_errors );

    gsl_vector *next_basis = gsl_vector_calloc(dlength);
    gsl_vector_view proj_basis = gsl_matrix_row(projections, mindex);
    gsl_matrix_get_row(next_basis, TS, mindex);
    gsl_vector_sub(next_basis, &proj_basis.vector);

    /* normalise vector */
    normalise(weight, next_basis);

    idx++;

    /* expand reduced basis */
    RB = realloc(RB, sizeof(double)*dlength*(idx+1));

    /* add on next basis */
    RBview = gsl_matrix_view_array(RB, idx+1, dlength);
    gsl_matrix_set_row(&RBview.matrix, idx, next_basis);

    gsl_vector_free(next_basis);
  }

  *nbases = (size_t)idx;

  gsl_matrix_free(projection_coeffs);
  gsl_matrix_free(residual);
  gsl_matrix_free(TS);

  return RB;
}

void project_onto_basis(double dt, gsl_matrix *RB, gsl_matrix *TS, gsl_matrix *projections, gsl_matrix *projection_coefficients, int idx){
  size_t row = 0;

  gsl_vector_view basis = gsl_matrix_row(RB, idx);

  for ( row=0; row < TS->size1; row++ ){
    double prod;
    gsl_vector_view proj = gsl_matrix_row(projections, row);
    gsl_vector *basisscale = gsl_vector_calloc(TS->size2);

    gsl_vector_view model = gsl_matrix_row(TS, row); /* get model from training set */

    prod = weighted_dot_product(dt, &basis.vector, &model.vector);

    gsl_matrix_set(projection_coefficients, idx, row, prod);
    gsl_vector_memcpy(basisscale, &basis.vector);
    gsl_vector_scale(basisscale, prod);
    gsl_vector_add(&proj.vector, basisscale);
    gsl_vector_free(basisscale);
  }
}

double weighted_dot_product(double weight, gsl_vector *a, gsl_vector *b){
  double dp;
  gsl_vector *weighted = gsl_vector_calloc(a->size);
  gsl_vector_memcpy(weighted, a);

  /* multiply vector by weight */
  gsl_vector_scale(weighted, weight);

  /* get dot product */
  gsl_blas_ddot(weighted, b, &dp);

  gsl_vector_free(weighted);

  return dp;
}

void normalise(double weight, gsl_vector *a){
  double norm = gsl_blas_dnrm2(a); /* use GSL normalisation calculation function */

  gsl_vector_scale(a, 1./(norm*sqrt(weight)));
}

gsl_matrix *B_matrix(gsl_matrix *V, gsl_matrix *RB){
  /* get inverse of V */
  size_t n = V->size1;
  gsl_matrix *invV = gsl_matrix_alloc(n, n);
  int signum;

  /* use LU decomposition to get inverse */
  gsl_matrix *LU = gsl_matrix_alloc(n, n);
  gsl_matrix_memcpy(LU, V);

  gsl_permutation *p = gsl_permutation_alloc(n);
  gsl_linalg_LU_decomp(LU, p, &signum);
  gsl_linalg_LU_invert(LU, p, invV);
  gsl_permutation_free(p);
  gsl_matrix_free(LU);

  /* get B matrix */
  gsl_matrix_view subRB = gsl_matrix_submatrix(RB, 0, 0, n, RB->size2);
  gsl_matrix *B = gsl_matrix_alloc(n, RB->size2);
  gsl_blas_dgemm(CblasTrans, CblasNoTrans, 1.0, invV, &subRB.matrix, 0., B);

  gsl_matrix_free(invV);
  return B;
}

void print_shape(gsl_matrix *a){
  fprintf(stderr, "Matrix is %zu x %zu\n", a->size1, a->size2);
}

gsl_vector *create_data_model_weights(gsl_matrix *B, double *data){
  gsl_vector_view dataview = gsl_vector_view_array(data, B->size2);

  /* create weights */
  gsl_vector *weights = gsl_vector_alloc(B->size1);

  gsl_blas_dgemv(CblasNoTrans, 1.0, B, &dataview.vector, 0., weights);

  return weights;
}

double roq_data_dot_model(gsl_vector *weights, double *model){
  double d_dot_m = 0.;
  gsl_vector_view modelview = gsl_vector_view_array(model, weights->size);

  gsl_blas_ddot(weights, &modelview.vector, &d_dot_m);

  return d_dot_m;
}

gsl_matrix *create_model_model_weights(gsl_matrix *B){
  gsl_matrix *weights = gsl_matrix_alloc(B->size1, B->size1);
  size_t i=0, j=0;
  double ressum = 0.;

  for ( i=0; i<B->size1; i++ ){
    for ( j=0; j<B->size1; j++ ){
      gsl_vector_view Bi = gsl_matrix_row(B, i);
      gsl_vector_view Bj = gsl_matrix_row(B, j);
      gsl_blas_ddot(&Bi.vector, &Bj.vector, &ressum);
      gsl_matrix_set(weights, i, j, ressum);
    }
  }

  return weights;
}

double roq_model_dot_model(gsl_matrix *weights, double *model){
  gsl_vector *ws = gsl_vector_alloc(weights->size1);
  gsl_vector_view modelview = gsl_vector_view_array(model, weights->size1);
  double m_dot_m = 0.;

  gsl_blas_dgemv(CblasTrans, 1.0, weights, &modelview.vector, 0., ws);
  gsl_blas_ddot(ws, &modelview.vector, &m_dot_m);

  return m_dot_m;
}

Interpolant *create_interpolant(gsl_matrix *RB){
  /* now find the empirical interopolant and interpolation nodes using Algorithm 2
     of http://arxiv.org/abs/1308.3565 */
  size_t RBsize = RB->size1; /* reduced basis size (no. of reduced bases) */
  size_t dlength = RB->size2; /* length of each base */
  size_t i=1, j=0, k=0;
  double *V = malloc(sizeof(double));
  gsl_matrix_view Vview;

  Interpolant *interp = malloc(sizeof(Interpolant));

  int idmax = 0, newidx = 0;

  /* get index of maximum absolute value of first basis */
  gsl_vector_view firstbasis = gsl_matrix_row(RB, 0);
  idmax = (int)gsl_blas_idamax(&firstbasis.vector); /* function gets index of maximum absolute value */

  interp->nodes = malloc(RBsize*sizeof(int));

  interp->nodes[0] = idmax;

  fprintf(stderr, "first index = %d\n", interp->nodes[0]);

  for ( i=1; i<RBsize; i++ ){
   Vview = gsl_matrix_view_array(V, i, i);

   for ( j=0; j<i; j++ ){
      for ( k=0; k<i; k++ ){
        gsl_matrix_set(&Vview.matrix, k, j, gsl_matrix_get(RB, j, interp->nodes[k]));
      }
    }

    /* get B matrix */
    gsl_matrix *B = B_matrix(&Vview.matrix, RB);

    /* make empirical interpolant of basis */
    gsl_vector *interpolant = gsl_vector_calloc(dlength);
    gsl_vector *subbasis = gsl_vector_calloc(i);
    gsl_vector_view subview = gsl_matrix_row(RB, i);

    for ( k=0; k<i; k++ ){
      gsl_vector_set(subbasis, k, gsl_vector_get(&subview.vector, interp->nodes[k]));
    }

    gsl_blas_dgemv(CblasTrans, 1.0, B, subbasis, 0., interpolant);

    /* get residuals of interpolant */
    gsl_vector_sub(interpolant, &subview.vector);

    newidx = (int)gsl_blas_idamax(interpolant);

    interp->nodes[i] = newidx;

    fprintf(stderr, "%zu: idx[%d]\n", i, interp->nodes[i]);

    gsl_vector_free(subbasis);
    gsl_matrix_free(B);
    gsl_vector_free(interpolant);

    /* reallocate memory for V */
    V = realloc(V, (i+1)*(i+1)*sizeof(double));
  }

  /* NOTE: the above works and produces identical results to the ipython notebook */

  /* get final B vector with all the indices */
  Vview = gsl_matrix_view_array(V, RBsize, RBsize);
  for( j=0; j<RBsize; j++ ){
    for( k=0; k<RBsize; k++ ){
      gsl_matrix_set(&Vview.matrix, k, j, gsl_matrix_get(RB, j, interp->nodes[k]));
    }
  }

  interp->Bs = B_matrix(&Vview.matrix, RB);

  free(V);

  return interp;
}

int main(){
  gsl_matrix *TS; /* the training set of waveforms */

  size_t TSsize;  /* the size of the training set (number of waveforms) */
  size_t wl;      /* the length of each waveform */
  size_t k = 0, j = 0, i = 0, nbases = 0;

  double *RB = NULL; /* the reduced basis set */
  Interpolant *interp = NULL;

  gsl_vector *times;

  double tolerance = 1e-12, sigma = 1.; /* tolerance for reduced basis generation loop */

  double dt = 60.; /* model time steps */

  TSsize = TSSIZE;
  wl = WL;

  /* allocate memory for training set */
  TS = gsl_matrix_calloc(TSsize, wl);

  double fmin = -0.0001, fmax = 0.0001, f0 = 0., m0 = 0.;
  times = gsl_vector_alloc(wl);

  /* set up training set */
  for ( k=0; k < TSsize; k++ ){
    f0 = fmin + (double)k*(fmax-fmin)/((double)TSsize-1.);

    for ( j=0; j < wl; j++ ){
      double tv = dt*(double)j;
      m0 = sin(2.*M_PI*f0*tv);
      gsl_vector_set(times, j, tv);
      gsl_matrix_set(TS, k, j, m0);
    }

    gsl_vector_view rowview = gsl_matrix_row(TS, k);
    normalise(dt, &rowview.vector);
  }

  if ( (RB = create_basis(dt, tolerance, TS, &nbases)) == NULL){
    fprintf(stderr, "Error... problem producing basis\n");
    return 1;
  }

  gsl_matrix_view RBview = gsl_matrix_view_array(RB, nbases, wl);

  fprintf(stderr, "%zu, %zu x %zu\n", nbases, RBview.matrix.size1, RBview.matrix.size2);

  //FILE *fp = fopen("test_vector.txt", "w");
  //gsl_vector_view testview = gsl_matrix_row(&RBview.matrix, RBview.matrix.size1-1);
  //gsl_vector_fprintf(fp, &testview.vector, "%le");
  //fclose(fp);

  /* NOTE: the above appears to be correct and gives identical values to those in the ipython notebook */

  interp = create_interpolant(&RBview.matrix);

  /* do some timing tests */

  /* create the model dot model weights */
  gsl_matrix *mmw = create_model_model_weights(interp->Bs);

  double randf0 = -0.00004; /* a random frequency to create a model */

  double *modelfull = calloc(wl, sizeof(double));
  double *modelreduced = calloc(nbases, sizeof(double));
  double *timesred = calloc(nbases, sizeof(double));

  /* create model */
  for ( i=0; i<wl; i++ ){ modelfull[i] = sin(2.*M_PI*randf0*gsl_vector_get(times, i)); }
  for ( i=0; i<nbases; i++ ){ modelreduced[i] = sin(2.*M_PI*randf0*gsl_vector_get(times, interp->nodes[i])); }

  gsl_vector_view mfview = gsl_vector_view_array(modelfull, wl);

  struct timespec t1, t2, t3, t4;
  double dt1, dt2;

  /* get the model model term with the full model */
  double mmfull, mmred;
  clock_gettime(CLOCK_MONOTONIC, &t1);
  gsl_blas_ddot(&mfview.vector, &mfview.vector, &mmfull);
  clock_gettime(CLOCK_MONOTONIC, &t2);

  clock_gettime(CLOCK_MONOTONIC, &t3);
  mmred = roq_model_dot_model(mmw, modelreduced);
  clock_gettime(CLOCK_MONOTONIC, &t4);

  dt1 = (double)((t2.tv_sec + t2.tv_nsec*1.e-9) - (t1.tv_sec + t1.tv_nsec*1.e-9));
  dt2 = (double)((t4.tv_sec + t4.tv_nsec*1.e-9) - (t3.tv_sec + t3.tv_nsec*1.e-9));
  fprintf(stderr, "M dot M (full) = %le [%.9lf s], M dot M (reduced) = %le [%.9lf s], time ratio = %lf\n", mmfull, dt1, mmred, dt2, dt1/dt2);

  /* get the data dot model terms by generating some random data */
  const gsl_rng_type *T;
  gsl_rng * r;

  gsl_rng_env_setup();

  T = gsl_rng_default;
  r = gsl_rng_alloc(T);

  double *data = calloc(wl, sizeof(double));
  for ( i=0; i<wl; i++ ){ data[i] = gsl_ran_gaussian(r, 1.0); }

  /* create the data dot model weights */
  gsl_vector *dmw = create_data_model_weights(interp->Bs, data);
  gsl_vector_view dataview = gsl_vector_view_array(data, wl);

  double dmfull, dmred;
  clock_gettime(CLOCK_MONOTONIC, &t1);
  gsl_blas_ddot(&dataview.vector, &mfview.vector, &dmfull);
  clock_gettime(CLOCK_MONOTONIC, &t2);

  clock_gettime(CLOCK_MONOTONIC, &t3);
  dmred = roq_data_dot_model(dmw, modelreduced);
  clock_gettime(CLOCK_MONOTONIC, &t4);

  dt1 = (double)((t2.tv_sec + t2.tv_nsec*1.e-9) - (t1.tv_sec + t1.tv_nsec*1.e-9));
  dt2 = (double)((t4.tv_sec + t4.tv_nsec*1.e-9) - (t3.tv_sec + t3.tv_nsec*1.e-9));
  fprintf(stderr, "D dot M (full) = %le [%.9lf s], D dot M (reduced) = %le [%.9lf s], time ratio = %lf\n", dmfull, dt1, dmred, dt2, dt1/dt2);

  /* check difference in log likelihoods */
  double Lfull, Lred;

  Lfull = mmfull - 2.*dmfull;
  Lred = mmred - 2.*dmred;

  fprintf(stderr, "Fractional difference in log likelihoods = %lf%%\n", 100.*fabs(Lfull-Lred)/fabs(Lfull));

  return 0;
}

ext: c


text:
/***************************************************************************/
/*                                                                         */
/* matrix.h - Matrix class for mruby                                       */
/* Copyright (C) 2015 Paolo Bosetti                                        */
/* paolo[dot]bosetti[at]unitn.it                                           */
/* Department of Industrial Engineering, University of Trento              */
/*                                                                         */
/* This library is free software.  You can redistribute it and/or          */
/* modify it under the terms of the GNU GENERAL PUBLIC LICENSE 2.0.        */
/*                                                                         */
/* This library is distributed in the hope that it will be useful,         */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of          */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           */
/* Artistic License 2.0 for more details.                                  */
/*                                                                         */
/* See the file LICENSE                                                    */
/*                                                                         */
/***************************************************************************/

#ifndef MATRIX_H
#define MATRIX_H

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/param.h>
#include <time.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>

#include "mruby.h"
#include "mruby/variable.h"
#include "mruby/string.h"
#include "mruby/data.h"
#include "mruby/class.h"
#include "mruby/value.h"
#include "mruby/array.h"
#include "mruby/numeric.h"
#include "mruby/compile.h"

#define E_MATRIX_ERROR (mrb_class_get(mrb, "MatrixError"))

/***********************************************\
 MATRICES
\***********************************************/

// Garbage collector handler, for play_data struct
// if play_data contains other dynamic data, free it too!
// Check it with GC.start
void matrix_destructor(mrb_state *mrb, void *p_);

// Utility function for getting the struct out of the wrapping IV @data
void mrb_matrix_get_data(mrb_state *mrb, mrb_value self, gsl_matrix **data);

void mrb_gsl_matrix_init(mrb_state *mrb);

#endif // MATRIX_H

max_stars_repo_name: UniTN-Mechatronics/mruby-gsl

max_issues_repo_name: UniTN-Mechatronics/mruby-gsl

max_forks_repo_name: UniTN-Mechatronics/mruby-gsl


text:
/* Copyright (C) 2015 Atsushi Togo */
/* All rights reserved. */

/* This file is part of phonopy. */

/* Redistribution and use in source and binary forms, with or without */
/* modification, are permitted provided that the following conditions */
/* are met: */

/* * Redistributions of source code must retain the above copyright */
/*   notice, this list of conditions and the following disclaimer. */

/* * Redistributions in binary form must reproduce the above copyright */
/*   notice, this list of conditions and the following disclaimer in */
/*   the documentation and/or other materials provided with the */
/*   distribution. */

/* * Neither the name of the phonopy project nor the names of its */
/*   contributors may be used to endorse or promote products derived */
/*   from this software without specific prior written permission. */

/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
/* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE */
/* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, */
/* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, */
/* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; */
/* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER */
/* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT */
/* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN */
/* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE */
/* POSSIBILITY OF SUCH DAMAGE. */

#include <lapacke.h>
#include <phonoc_array.h>
#include <phonoc_utils.h>
#include <phonon4_h/real_to_reciprocal.h>

static void real_to_reciprocal_elements(lapack_complex_double *fc4_rec_elem,
					const double q[12],
					const double *fc4,
					const Darray *shortest_vectors,
					const Iarray *multiplicity,
					const int *p2s,
					const int *s2p,
					const int pi0,
					const int pi1,
					const int pi2,
					const int pi3);

/* fc4_reciprocal[num_patom, num_patom, num_patom, num_patom, 3, 3, 3, 3] */
void real_to_reciprocal4(lapack_complex_double *fc4_reciprocal,
			 const double q[12],
			 const double *fc4,
			 const Darray *shortest_vectors,
			 const Iarray *multiplicity,
			 const int *p2s_map,
			 const int *s2p_map)
{
  int i, j, k, l, num_patom;

  num_patom = multiplicity->dims[1];

  for (i = 0; i < num_patom; i++) {
    for (j = 0; j < num_patom; j++) {
      for (k = 0; k < num_patom; k++) {
	for (l = 0; l < num_patom; l++) {
	  real_to_reciprocal_elements
	    (fc4_reciprocal +
	     i * 81 * num_patom * num_patom * num_patom +
	     j * 81 * num_patom * num_patom +
	     k * 81 * num_patom +
	     l * 81,
	     q,
	     fc4,
	     shortest_vectors,
	     multiplicity,
	     p2s_map,
	     s2p_map,
	     i, j, k, l);
	}
      }
    }
  }
}

static void real_to_reciprocal_elements(lapack_complex_double *fc4_rec_elem,
					const double q[12],
					const double *fc4,
					const Darray *shortest_vectors,
					const Iarray *multiplicity,
					const int *p2s,
					const int *s2p,
					const int pi0,
					const int pi1,
					const int pi2,
					const int pi3)
{
  int i, j, k, l, m, num_satom;
  lapack_complex_double phase_factor, phase_factors[3];
  double fc4_rec_real[81], fc4_rec_imag[81];
  int fc4_elem_address;

  for (i = 0; i < 81; i++) {
    fc4_rec_real[i] = 0;
    fc4_rec_imag[i] = 0;
  }

  num_satom = multiplicity->dims[0];

  i = p2s[pi0];

  for (j = 0; j < num_satom; j++) {
    if (s2p[j] != p2s[pi1]) {
      continue;
    }
    phase_factors[0] =
      get_phase_factor(q, shortest_vectors, multiplicity, pi0, j, 1);

    for (k = 0; k < num_satom; k++) {
      if (s2p[k] != p2s[pi2]) {
	continue;
      }
      phase_factors[1] =
	get_phase_factor(q, shortest_vectors, multiplicity, pi0, k, 2);

      for (l = 0; l < num_satom; l++) {
	if (s2p[l] != p2s[pi3]) {
	  continue;
	}
	phase_factors[2] =
	  get_phase_factor(q, shortest_vectors, multiplicity, pi0, l, 3);

	fc4_elem_address = (i * 81 * num_satom * num_satom * num_satom +
			    j * 81 * num_satom * num_satom +
			    k * 81 * num_satom +
			    l * 81);

	phase_factor = phonoc_complex_prod(phase_factors[0], phase_factors[1]);
	phase_factor = phonoc_complex_prod(phase_factor, phase_factors[2]);
	for (m = 0; m < 81; m++) {
	  fc4_rec_real[m] +=
	    lapack_complex_double_real(phase_factor) * fc4[fc4_elem_address + m];
	  fc4_rec_imag[m] +=
	    lapack_complex_double_imag(phase_factor) * fc4[fc4_elem_address + m];
	}
      }
    }
  }

  for (i = 0; i < 81; i++) {
    fc4_rec_elem[i] =
      lapack_make_complex_double(fc4_rec_real[i], fc4_rec_imag[i]);
  }
}

max_stars_repo_name: atztogo/forcefit

max_issues_repo_name: atztogo/forcefit

max_forks_repo_name: atztogo/forcefit


text:
#ifndef SEARCHPATTERNBASE_HHHHH
#define SEARCHPATTERNBASE_HHHHH

#include "../../SigScannerMemoryData.h"

#include <vector>
#include <string>
#include <memory>

#include <gsl/span>

namespace MPSig {
    namespace detail {
        class SearchPatternBase {
        public:
            virtual ~SearchPatternBase() = default;

            using gsl_span_cit_t = typename gsl::span<const char>::const_iterator;
            using gsl_span_crit_t = typename gsl::span<const char>::const_reverse_iterator;

            template<typename It>
            struct ExecFirstResult {
                bool success; // If it was a success
                It rangeBegin; // The first valid iterator
                It rangeEnd; // The next depended iterator point (from which all other patterns depend on)
            };

            // ExecFirst can look up begin to end fully, without restrictions
            virtual ExecFirstResult<gsl_span_cit_t> ExecFirst(const MPSig::SigScannerMemoryData& data, gsl_span_cit_t begin, gsl_span_cit_t end) const = 0;
            virtual ExecFirstResult<gsl_span_crit_t> ExecFirst(const MPSig::SigScannerMemoryData& data, gsl_span_crit_t begin, gsl_span_crit_t end) const = 0;

            // ExecDepend must only check the current location as it depends on a search success
            virtual ExecFirstResult<gsl_span_cit_t> ExecDepend(const MPSig::SigScannerMemoryData& data, gsl_span_cit_t begin, gsl_span_cit_t end) const = 0;
            virtual ExecFirstResult<gsl_span_crit_t> ExecDepend(const MPSig::SigScannerMemoryData& data, gsl_span_crit_t begin, gsl_span_crit_t end) const = 0;

            virtual std::unique_ptr<SearchPatternBase> Clone() const = 0;
        };

    }
}

#endif