#pragma once

#include <src/common/Span.h>
#include <gsl/gsl>

#include <type_traits>

namespace miner {

    //simple replacement for std::vector<char> in situations where huge
    //allocations happen, since the standard doesn't define how
    //much more memory std::vector allocates on reserve or resize.

    //basically an owning gsl::span
    template<class T = uint8_t>
    class DynamicBuffer {

        gsl::owner<T*> owner = nullptr;
        span<T> buffer;

    public:

        DynamicBuffer() = default;
        // only use alignment if a stronger power of two alignment than the natural alignment of T is needed
        DynamicBuffer(size_t size, size_t alignment = alignof(T))
            : owner(reinterpret_cast<T*>(::operator new(size * sizeof(T) + alignment - 1)))
            , buffer(reinterpret_cast<T*>((uintptr_t(owner) + alignment - 1) & ~uintptr_t(alignment - 1))
                , ptrdiff_t(size)) {
            if (!std::is_trivially_constructible<T>::value) {
                new(buffer.data()) T[size];
            }
        }

        ~DynamicBuffer() {
            if (owner) { //if not moved-from
                if (!std::is_trivially_destructible<T>::value) {
                    for (auto &element : buffer) {
                        element.~T();
                    }
                }
                ::operator delete(owner);
            }
        }

        DynamicBuffer(DynamicBuffer &&o) noexcept
            : owner(o.owner)
            , buffer(o.buffer) {
            o.owner = nullptr; //remove ownership
        }

        DynamicBuffer &operator=(DynamicBuffer &&o) noexcept {
            owner = o.owner;
            buffer = o.buffer;
            o.owner = nullptr; //remove ownership
            return *this;
        }

        //copy
        DynamicBuffer(const DynamicBuffer &) = delete;
        DynamicBuffer &operator=(const DynamicBuffer &) = delete;

        T *data() {
            return buffer.data();
        }

        uint8_t *bytes() {
            return reinterpret_cast<uint8_t*>(buffer.data());
        }

        const T *data() const {
            return buffer.data();
        }

        const uint8_t *bytes() const {
            return reinterpret_cast<const uint8_t*>(buffer.data());
        }

        size_t size_bytes() const {
            return static_cast<size_t>(buffer.size_bytes());
        }

        size_t size() const {
            return static_cast<size_t>(buffer.size());
        }

        span<const T> getSpan() const {
            return buffer;
        }

        span<const uint8_t> getByteSpan() const {
            return {bytes(), static_cast<ptrdiff_t>(size_bytes())};
        }

        operator bool() const {
            return owner != nullptr;
        }

    };

}


//----------------------------------------------------------------------------
//  History:    2018-04-30 Dwayne Robinson - Created
//----------------------------------------------------------------------------
#pragma once

#include <iterator>
#include <memory> // For uninitialized_move/copy and std::unique_ptr.
#include <assert.h>
#include <algorithm>

#if USE_CPP_MODULES
//import std.core;
import Common.ArrayRef;
#else
#ifdef USE_GSL_SPAN_INSTEAD_OF_ARRAY_REF
#include <gsl/span>
#define array_ref gsl::span
#else
#include "Common.ArrayRef.h" // gsl::span may be mostly substituted instead of array_ref, just missing intersects().
#endif
#endif

MODULE(Common.FastVector);
EXPORT_BEGIN

#pragma warning(push)
#pragma warning(disable:4127) // Conditional expression is constant. VS can't tell that certain compound conditionals of template parameters aren't always constant when the tempalate parameter is true.

#if defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL > 0
// For std::uninitialized_copy and std::uninitialized_move.
#define FASTVECTOR_MAKE_UNCHECKED stdext::make_unchecked_array_iterator
#else
#define FASTVECTOR_MAKE_UNCHECKED
#endif

// fast_vector is a dynamic array that can substitute for std::vector, where it:
// (1) avoids heap allocations when the element count fits within the fixed-size capacity.
// (2) avoids unnecessarily initializing elements if ShouldInitializeElements == false.
//     This is useful for large buffers which will just be overwritten soon anyway.
// (3) supports most vector methods except insert/erase/emplace.
//
// Template parameters:
// - DefaultArraySize - passing 0 means it is solely heap allocated. Passing > 0
//   reserves that much element capacity before allocating heap memory.
//
// - ShouldInitializeElements - ensures elements are constructed when resizing, and
//   it must be true for objects with non-trivial constructors, but it can be set
//   false for large buffers to avoid unnecessarily initializing memory which will
//   just be overwritten soon later anyway.
//
// Examples:
//  fast_vector<int, 20> axes        - up to 20 integers before heap allocation.
//  fast_vector<int, 0, false> axes  - always heap allocated but never initialized.
//  fast_vector<int> axes            - basically std::vector.

template<typename T, size_t DefaultArraySize = 0, bool ShouldInitializeElements = true>
class fast_vector;

enum fast_vector_use_memory_buffer_enum
{
    fast_vector_use_memory_buffer // To avoid ambiguous constructor overload resolution.
};

// The base class is separated out for the customization of passing specific
// memory, and to avoid bloating additional template permutations solely due to
// differing array sizes.
template<typename T, bool ShouldInitializeElements>
class fast_vector<T, 0, ShouldInitializeElements>
{
    static_assert(ShouldInitializeElements || std::is_trivial<T>::value);
    using self = fast_vector<T, 0, ShouldInitializeElements>;

public:
    // Standard container type definitions.
    using value_type = T;
    using pointer = T*;
    using reference = T&;
    using iterator = pointer;
    using const_reference = T const&;
    using const_iterator = T const*;
    using reverse_iterator = std::reverse_iterator<iterator>;
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
    using size_type = size_t;
    using difference_type = ptrdiff_t;
    using mutable_value_type = typename std::remove_const<T>::type;
    using mutable_iterator = mutable_value_type*;

    // If true, the data types needs additional alignment like SSE and AVX types.
    constexpr static bool NeedsTypeAlignmentBeyondMaxAlignT = alignof(T) > alignof(std::max_align_t);
    constexpr static size_t MinimumMemoryBlockSize = NeedsTypeAlignmentBeyondMaxAlignT ? sizeof(void*) : 0;

public:
    constexpr fast_vector() noexcept
    {
    }

    fast_vector(size_t initialSize)
    {
        resize(initialSize);
    }

    fast_vector(array_ref<const T> initialValues)
    {
        assign(initialValues);
    }

    fast_vector(const fast_vector& otherVector)
    {
        assign(otherVector);
    }

    fast_vector(fast_vector&& otherVector) // Throws std::bad_alloc if low on memory.
    {
        transfer_from(otherVector);
    }

    // Construct using explicit fixed size memory buffer.
    // - Used by the derived template specialization which includes a fixed size buffer.
    // - May also be used by the caller to pass an explicit buffer such as a local array,
    //   where the buffer is guaranteed to live for the lifetime of the fast_vector.
    constexpr fast_vector(fast_vector_use_memory_buffer_enum, array_ref<T> initialBackingArray)
    :   data_(initialBackingArray.data()),
        capacity_(initialBackingArray.size())
    {
    }

    fast_vector(fast_vector_use_memory_buffer_enum, array_ref<T> initialBackingArray, array_ref<const T> initialValues)
    :   data_(initialBackingArray.data()),
        capacity_(initialBackingArray.size())
    {
        assign(initialValues);
    }

    fast_vector(fast_vector_use_memory_buffer_enum, array_ref<T> initialBackingArray, size_t initialSize)
    :   data_(initialBackingArray.data()),
        capacity_(initialBackingArray.size())
    {
        resize(initialSize);
    }

    fast_vector(fast_vector_use_memory_buffer_enum, array_ref<T> initialBackingArray, pointer p, size_t elementCount)
    :   data_(initialBackingArray.data()),
        capacity_(initialBackingArray.size())
    {
        assign({p, elementCount});
    }

    template <typename IteratorType>
    fast_vector(fast_vector_use_memory_buffer_enum, array_ref<T> initialBackingArray, IteratorType begin, IteratorType end)
    :   data_(initialBackingArray.data()),
        capacity_(initialBackingArray.size())
    {
        assign(begin, end);
    }

    ~fast_vector() noexcept(std::is_nothrow_destructible<T>::value)
    {
        free();
    }

    // Clear the vector and free all memory. Calling clear() then shrink_to_fit()
    // is a less efficient way to accomplish it too.
    void free()
    {
        clear(); // Destruct objects and zero the size.

        if (dataIsAllocatedMemory_)
        {
            FreeMemoryBlock(data_);
            data_ = nullptr;
            capacity_ = 0;
        }
    }

    // Iterators
    iterator begin() const noexcept                 { return data_; }
    iterator end() const noexcept                   { return data_ + size_; }
    const_iterator cbegin() const noexcept          { return begin(); }
    const_iterator cend() const noexcept            { return end(); };
    reverse_iterator rbegin() const noexcept        { return reverse_iterator(begin()); }
    reverse_iterator rend() const noexcept          { return reverse_iterator(end()); }
    const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(begin()); }
    const_reverse_iterator crend() const noexcept   { return const_reverse_iterator(end()); }

    // Capacity
    size_type size() const noexcept                 { return size_; }
    size_type size_in_bytes() const noexcept        { return size_ * sizeof(T); }
    size_type capacity() const noexcept             { return capacity_; }
    static constexpr size_type max_size() noexcept  { return SIZE_MAX / sizeof(T); }
    bool empty() const noexcept                     { return size_ == 0; }

    // Element access
    T& operator[](size_t i) const noexcept          { return data_[i]; }
    T& front() const noexcept                       { return data_[0]; }
    T& back() const noexcept                        { return data_[size_ - 1]; }
    T* data() const noexcept                        { return data_; }
    T* data_end() const noexcept                    { return data_ + size_; }

    array_ref<T> data_span() noexcept               { return {data_, size_}; }
    array_ref<T const> data_span() const noexcept   { return {data_, size_}; }

    T& at(size_t i)                                 { return checked_read(i); }
    T& at(size_t i) const                           { return checked_read(i); }

    T& checked_read(size_t i)
    {
        if (i >= size_)
        {
            throw std::out_of_range("fast_vector array index out of range.");
        }

        return data_[i];
    }

    const T& checked_read(size_t i) const
    {
        return const_cast<self&>(*this).checked_read(i);
    }

    fast_vector& operator=(const fast_vector& otherVector)
    {
        assign(otherVector);
        return *this;
    }

    fast_vector& operator=(fast_vector&& otherVector) // Throws std::bad_alloc if low on memory.
    {
        transfer_from(otherVector);
        return *this;
    }

    // Clear any existing elements and copy the new elements from span.
    void assign(array_ref<const T> span)
    {
        #ifndef USE_GSL_SPAN_INSTEAD_OF_ARRAY_REF
        assert(!span.intersects(data_span())); // No self intersection.
        #endif

        clear();

        size_t newSize = span.size();
        reserve(newSize);
        std::uninitialized_copy(FASTVECTOR_MAKE_UNCHECKED(span.data()), FASTVECTOR_MAKE_UNCHECKED(span.data()) + newSize, /*out*/ FASTVECTOR_MAKE_UNCHECKED(data_));
        size_ = newSize;
    }

    // Clear any existing elements and copy the new elements from the iterable range.
    template <typename IteratorType>
    void assign(IteratorType begin, IteratorType end)
    {
        clear();

        size_t newSize = std::distance(begin, end);
        reserve(newSize);
        std::uninitialized_copy(FASTVECTOR_MAKE_UNCHECKED(begin), FASTVECTOR_MAKE_UNCHECKED(end), /*out*/ FASTVECTOR_MAKE_UNCHECKED(data_));
        size_ = newSize;
    }

    // Tranfer all elements from the other vector to this one.
    // This only offers the weak exception guarantee, that no leaks will happen
    // if type T throws in the middle of a copy.
    void transfer_from(array_ref<const T> span)
    {
        #ifndef USE_GSL_SPAN_INSTEAD_OF_ARRAY_REF
        assert(!span.intersects(data_span())); // No self intersection.
        #endif

        clear();

        size_t newSize = span.size();
        reserve(newSize);
        std::uninitialized_move(FASTVECTOR_MAKE_UNCHECKED(span.data()), FASTVECTOR_MAKE_UNCHECKED(span.data()) + newSize, /*out*/ FASTVECTOR_MAKE_UNCHECKED(data_));
        size_ = newSize;
    }

    // Tranfer all elements from the other vector to this one.
    // This only offers the weak exception guarantee, that no leaks will happen
    // if type T throws in the middle of a copy.
    void transfer_from(fast_vector<T, 0, ShouldInitializeElements>& other)
    {
        if (&other == this)
        {
            return; // Nop for self assignment.
        }

        if (other.dataIsAllocatedMemory_)
        {
            free();

            // Take ownership of new data directly.
            data_ = other.data_;
            other.data_ = nullptr;
            size_ = other.size_;
            other.size_ = 0;
            capacity_ = other.capacity_;
            other.capacity_ = 0;
            dataIsAllocatedMemory_ = other.dataIsAllocatedMemory_;
            other.dataIsAllocatedMemory_ = false;
        }
        else
        {
            // Copying from a fixed size buffer; so it's unsafe to simply
            // steal the pointers as that may leave a dangling pointer
            // when the other fast vector disappears.
            transfer_from(make_array_ref(other));
        }
    }

    void clear() noexcept(std::is_nothrow_destructible<T>::value)
    {
        if (ShouldInitializeElements)
        {
            std::destroy(data_, data_ + size_);
        }
        size_ = 0;
        // But do not free heap memory.
    }

    void resize(size_t newSize)
    {
        if (newSize > size_)
        {
            if (newSize > capacity_)
            {
                reserve_at_least(newSize);
            }

            // Grow data to the new size, calling the default constructor on each new item.
            if (ShouldInitializeElements)
            {
                std::uninitialized_value_construct<iterator>(data_ + size_, data_ + newSize);
            }

            size_ = newSize;
        }
        else if (newSize < size_)
        {
            // Shrink the data to the new size, calling the destructor on each item. Capacity remains intact.
            if (ShouldInitializeElements)
            {
                std::destroy(data_ + newSize, data_ + size_);
            }

            size_ = newSize;
        }
    }

    void reserve(size_t newCapacity)
    {
        if (newCapacity <= capacity_)
        {
            return; // Nothing to do.
        }

        if (newCapacity > max_size())
        {
            throw std::bad_alloc(); // Too many elements.
        }

        size_t newByteSize = newCapacity * sizeof(T);
        ReallocateMemory(newByteSize);

        capacity_ = newCapacity;
    }

    void reserve_at_least(size_t newCapacity)
    {
        // Grow with 1.5x factor to avoid frequent reallocations.
        newCapacity = std::max((size_ * 3 / 2), newCapacity);
        reserve(newCapacity);
    }

    void shrink_to_fit()
    {
        if (!dataIsAllocatedMemory_ || capacity_ == size_)
        {
            return; // Nothing to do.
        }

        size_t newByteSize = size_ * sizeof(T);
        ReallocateMemory(newByteSize);

        capacity_ = size_;
    }

    void push_back(const T& newValue)
    {
        reserve(size_ + 1);//
        new(static_cast<void*>(data_ + size_)) T(newValue);
        ++size_;
    }

    void push_back(T&& newValue)
    {
        reserve(size_ + 1);
        new(static_cast<void*>(data_ + size_)) T(std::move(newValue));
        ++size_;
    }

    void append(array_ref<const T> span)
    {
        insert(size_, span);
    }

    void insert(size_t insertionOffset, array_ref<const T> span)
    {
        insert(insertionOffset, span.begin(), span.end());
    }

    void insert(size_t insertionOffset, const_iterator begin, const_iterator end)
    {
        assert(insertionOffset <= size_);

        // Grow array size.
        size_t newSize = std::distance(begin, end) + size_;
        if (newSize < size_)
        {
            throw std::bad_alloc();
        }
        reserve_at_least(newSize);

        // Add empty elements to the end.
        size_t oldSize = size_;
        if (ShouldInitializeElements)
        {
            std::uninitialized_value_construct<iterator>(data_ + size_, data_ + newSize);
        }
        size_ = newSize;

        // Shift elements to the end to make more room.
        std::move_backward(data_ + insertionOffset, data_ + oldSize, data_ + newSize);

        std::copy(begin, end, /*out*/ data_ + insertionOffset);
    }

    void insert(const_iterator position, array_ref<const T> span)
    {
        insert(position - begin(), span.begin(), span.end());
    }

    void erase(const_iterator begin, const_iterator end)
    {
        assert(end >= begin);
        assert(begin >= data_);
        assert(end <= data_ + size_);

        // Shift elements to front.
        size_t oldSize = size_;
        std::copy(iterator(end), data_ + size_, iterator(begin));
        size_ -= std::distance(begin, end);

        // Destroy elements at the end.
        if (ShouldInitializeElements)
        {
            std::destroy(data_ + size_, data_ + oldSize);
        }
    }

    void erase(const_iterator position)
    {
        erase(position, position + 1);
    }

    // Returns underlying malloc()'d memory block.
    // - May be transferred to another fast_vector via attach_memory.
    // - Caller must free() the memory if not transferred.
    // - This is more safely used when T is a POD type, as the caller would also
    //   need to appropriately call any destructors if complex objects.
    // - If the vector is using fixed size memory (no allocations have happened),
    //   the returned span is empty and points to null.
    // - If the vector is using allocated memory, the memory block will be not
    //   empty and non-null, and the vector is then empty.
    // - No object destructors are called.
    // - If NeedsTypeAlignmentBeyondMaxAlignT is true, the data array might start
    //   after the memory block for alignment.
    //
    // fast_vector<int> vector(20);
    // ...
    // auto memory = vector.detach_memory();
    // std::unique_ptr<char, decltype(&std::free)> p(memory.data(), &std::free);
    // ...
    // otherVector.attach_memory(memory);
    // p.release(); // unique_ptr does not own it anymore.
    //
    array_ref<uint8_t> detach_memory() noexcept
    {
        array_ref<uint8_t> data;

        // Only heap allocated memory can be returned, not the fixed size buffer.
        if (dataIsAllocatedMemory_)
        {
            // Return the raw memory block, from the actual beginning of memory to the true end.
            // The data might start beyond the memory block for alignment purposes.
            uint8_t* bytes = reinterpret_cast<uint8_t*>(data_);
            size_t byteCount = size_ * sizeof(T);
            data = {reinterpret_cast<uint8_t*>(GetMemoryBlock(data_)), bytes + byteCount};

            data_ = nullptr;
            size_ = 0;
            capacity_ = 0;
        }

        return data;
    }

    // Take ownership of the memory, which came from malloc or another fast_vector
    // via detach_memory.
    // - If ShouldInitializeElements == true, then the memory is presumed to contain
    //   valid objects, which will be destructed when the fast_vector dies.
    // - Because the memory is raw, it's possible to reuse a different data type.
    // - The memory must be aligned to alignof(std::max_align_t).
    // - If NeedsTypeAlignmentBeyondMaxAlignT is true, the memory must be at least
    //   sizeof(void*) bytes.
    //
    void attach_memory(array_ref<uint8_t> memory) noexcept(std::is_nothrow_destructible<T>::value)
    {
        #ifndef USE_GSL_SPAN_INSTEAD_OF_ARRAY_REF
        assert(!memory.intersects(data_span())); // No self intersection.
        #endif

        assert((reinterpret_cast<size_t>(memory.data()) & (alignof(std::max_align_t) - 1)) == 0); // malloc/realloc should return aligned pointers up to max_align_t.
        assert(memory.data() == nullptr || memory.size() >= MinimumMemoryBlockSize);

        free();

        T* data = AlignMemoryBlock(memory.data());

        // Take ownership of new data.
        data_ = data;
        uint8_t* dataEnd = memory.data() + memory.size();
        size_ = (dataEnd - reinterpret_cast<uint8_t*>(data_)) / sizeof(T);
        capacity_ = size_;
        dataIsAllocatedMemory_ = true;
    }

protected:
    void ReallocateMemory(size_t newByteSize) // Throws std::bad_alloc if low on memory, or if T's move constructor fails.
    {
        assert(newByteSize >= size_ * sizeof(T)); // Shouldn't have been called otherwise, because it's wrong for size_ to be less than actual memory.

        // Handle alignment needs when the type is beyond what malloc/realloc guarantee.
        // This needed for SSE and AVX types (which need 16 and 32 bytes). Any other standard
        // type should have sufficient alignment by default.
        newByteSize = InflateMemorySizeForAlignment(newByteSize);

        // Try to reallocate the memory block directly rather allocate a new block and manually
        // copying. The memory manager can sometimes just extend the existing data block in-place
        // if there is space behind the block. Only trivially moveable types comply though, whereas
        // std::string would not comply due to the small string optimization.

        if (std::is_trivially_move_constructible<T>::value && dataIsAllocatedMemory_)
        {
            // Try to just reallocate the existing memory block.

            void* memory = realloc(GetMemoryBlock(data_), newByteSize);
            if (memory == nullptr && newByteSize > 0)
            {
                throw std::bad_alloc();
            }
            data_ = AlignMemoryBlock(/*modified*/ memory);
        }
        else
        {
            // Allocate a new memory buffer if one isn't allocated yet,
            // or if the data type is complex enough that it's not trivially
            // moveable (e.g. std::string, which contains pointers that point
            // into the class address itself for the small string optimization).

            void* memory = std::malloc(newByteSize);
            std::unique_ptr<void, decltype(std::free)*> newDataHolder(memory, &std::free);

            // Copy an any existing elements from the fixed size buffer.
            T* newData = AlignMemoryBlock(memory);
            std::uninitialized_move(FASTVECTOR_MAKE_UNCHECKED(data_), FASTVECTOR_MAKE_UNCHECKED(data_) + size_, /*out*/ FASTVECTOR_MAKE_UNCHECKED(newData));

            // Release the existing block, and assign the new one.
            if (dataIsAllocatedMemory_)
            {
                FreeMemoryBlock(data_);
            }
            newDataHolder.release();
            data_ = newData;
            dataIsAllocatedMemory_ = true;

            // Caller updates capacity_ and size_. This simply reallocates the memory block.
        }
    }

    // No object destruction, just a direct free.
    static void FreeMemoryBlock(T* data)
    {
        std::free(GetMemoryBlock(data));
    }

    // Get the actual memory block associated with the data.
    static void* GetMemoryBlock(T* data)
    {
        mutable_value_type* mutableData = const_cast<mutable_value_type*>(data);

        if (NeedsTypeAlignmentBeyondMaxAlignT && data != nullptr)
        {
            // Read the pointer to the actual block which is set immediately before the data.
            // [ptr to memory block][data ...]
            return reinterpret_cast<void**>(mutableData)[-1];
        }
        else
        {
            return mutableData;
        }
    }

    // Return aligned data for the given type, storing the raw memory block from malloc.
    // Write the memory block associated with the data, and return the data.
    // The caller has already ensure enough space exists in the block for alignment.
    static T* AlignMemoryBlock(void* memory)
    {
        if (NeedsTypeAlignmentBeyondMaxAlignT && memory != nullptr)
        {
            // Return a pointer to the aligned data, which starts after the pointer to the original
            // memory.
            //
            // [ptr to memory block][data ...]

            // Return  the pointer to the actual block which is set immediately before the data.
            // [ptr to memory block][data ...]
            constexpr size_t alignmentMask = alignof(T) - 1;
            static_assert((alignof(T) & alignmentMask) == 0, "Alignment must be a power of 2.");
            static_assert(NeedsTypeAlignmentBeyondMaxAlignT == false || alignof(T) >= alignof(void*), "Alignment must be at least the size of a pointer for NeedsTypeAlignmentBeyondMaxAlignT to be true.");

            // Align memory. Note calling std::align is awkward given it expects sizes, which are
            // already correct earlier and do not need to be passed to this function.
            size_t memoryAddress = reinterpret_cast<size_t>(memory);
            memoryAddress = (memoryAddress + sizeof(void*) + alignmentMask) & ~alignmentMask;
            mutable_value_type* data = reinterpret_cast<mutable_value_type*>(memoryAddress);

            // Store the pointer to the original memory block.
            reinterpret_cast<void**>(data)[-1] = memory;
            return data;
        }
        else
        {
            assert((reinterpret_cast<size_t>(memory) & (alignof(std::max_align_t) - 1)) == 0); // malloc/realloc should return aligned pointers up to max_align_t.
            return reinterpret_cast<T*>(memory);
        }
    }

    static size_t InflateMemorySizeForAlignment(size_t newByteSize)
    {
        if (NeedsTypeAlignmentBeyondMaxAlignT && newByteSize != 0)
        {
            // Need to increase the allocation size to include enough for full type alignment
            // and the size of a single pointer preceding the data.
            size_t alignedByteSize = newByteSize + alignof(T) - alignof(std::max_align_t) + MinimumMemoryBlockSize;
            if (alignedByteSize < newByteSize)
            {
                throw std::bad_alloc(); // Too much memory requested and overflowed.
            }
            newByteSize = alignedByteSize;
        }
        // else malloc/realloc default alignment will suffice.

        return newByteSize;
    }

protected:
    T* data_ = nullptr;     // May point to fixed size array or allocated memory, depending on dataIsAllocatedMemory_.
    size_t size_ = 0;       // Count in elements.
    size_t capacity_ = 0;   // Count in elements.
    bool dataIsAllocatedMemory_ = false;
};

// Derived specialization which includes a fixed size buffer.
template <typename T, size_t DefaultArraySize, bool ShouldInitializeElements>
class fast_vector : public fast_vector<T, 0, ShouldInitializeElements>
{
public:
    using BaseClass = fast_vector<T, 0, ShouldInitializeElements>;

    constexpr fast_vector() noexcept
    :   BaseClass(fast_vector_use_memory_buffer, GetFixedSizeArrayData())
    {
    }

    fast_vector(array_ref<const T> initialValues)
    :   BaseClass(fast_vector_use_memory_buffer, GetFixedSizeArrayData(), initialValues)
    {
    }

    fast_vector(std::initializer_list<const T> initialValues)
    :   BaseClass(fast_vector_use_memory_buffer, GetFixedSizeArrayData(), make_array_ref(initialValues))
    {
    }

    fast_vector(const fast_vector& otherVector)
    :   BaseClass(otherVector)
    {
    }

    fast_vector(typename BaseClass::pointer p, size_t elementCount)
    :   BaseClass(fast_vector_use_memory_buffer, GetFixedSizeArrayData(), p, elementCount)
    {
    }

    template <typename IteratorType>
    fast_vector(IteratorType begin, IteratorType end)
    :   BaseClass(fast_vector_use_memory_buffer, GetFixedSizeArrayData(), begin, end)
    {
    }

    fast_vector(size_t initialSize)
    :   BaseClass(fast_vector_use_memory_buffer, GetFixedSizeArrayData(), initialSize)
    {
    }

    // Move constructor. Will not throw unless type T throws, since the
    // target is guaranteed to have enough space.
    fast_vector(fast_vector&& otherVector) noexcept(std::is_nothrow_move_assignable<T>::value)
    :   BaseClass(std::move(otherVector))
    {
    }

    // Explicit overload for base class, to avoid the compiler from undesireably
    // choosing the constructor overload which takes a span of initialValues or
    // the copy constructor.
    fast_vector(BaseClass&& otherVector) // Throws std::bad_alloc if low on memory.
    :   BaseClass(std::move(otherVector))
    {
    }

    // Move assignment. Will not throw unless type T throws, since the
    // target vector is guaranteed to have a large enough fixed-size array.
    fast_vector& operator=(fast_vector&& otherVector) noexcept(std::is_nothrow_move_assignable<T>::value && std::is_nothrow_destructible<T>::value)
    {
        BaseClass::transfer_from(otherVector);
        return *this;
    }

    fast_vector& operator=(BaseClass&& otherVector) // Throws std::bad_alloc if low on memory.
    {
        BaseClass::transfer_from(otherVector);
        return *this;
    }

    fast_vector& operator=(const fast_vector& otherVector)
    {
        BaseClass::assign(otherVector);
        return *this;
    }

    fast_vector& operator=(const BaseClass& otherVector)
    {
        BaseClass::assign(otherVector);
        return *this;
    }

private:
    constexpr array_ref<T> GetFixedSizeArrayData() noexcept
    {
        return array_ref<T>(reinterpret_cast<T*>(std::data(fixedSizedArrayData_)), std::size(fixedSizedArrayData_));
    }

private:
    // Uninitialized data to be used by the base class.
    // It's declared as raw bytes rather than an std::array<T> to avoid any initialization cost
    // up front, only initializing the fields which actually exist when resized later.
    //
    // Note Visual Studio 2017 15.8 requires you to declare _ENABLE_EXTENDED_ALIGNED_STORAGE
    // if your type T is > max_align_t.
    //
    std::aligned_storage_t<sizeof(T), alignof(T)> fixedSizedArrayData_[DefaultArraySize];
};

#ifdef USE_GSL_SPAN_INSTEAD_OF_ARRAY_REF
#undef array_ref
#endif

#undef FASTVECTOR_MAKE_UNCHECKED

#pragma warning(pop)

EXPORT_END


/* matrix/gsl_matrix_uchar.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program 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 GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_MATRIX_UCHAR_H__
#define __GSL_MATRIX_UCHAR_H__

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_uchar.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size1;
  size_t size2;
  size_t tda;
  unsigned char * data;
  gsl_block_uchar * block;
  int owner;
} gsl_matrix_uchar;

typedef struct
{
  gsl_matrix_uchar matrix;
} _gsl_matrix_uchar_view;

typedef _gsl_matrix_uchar_view gsl_matrix_uchar_view;

typedef struct
{
  gsl_matrix_uchar matrix;
} _gsl_matrix_uchar_const_view;

typedef const _gsl_matrix_uchar_const_view gsl_matrix_uchar_const_view;

/* Allocation */

GSL_EXPORT
gsl_matrix_uchar *
gsl_matrix_uchar_alloc (const size_t n1, const size_t n2);

GSL_EXPORT
gsl_matrix_uchar *
gsl_matrix_uchar_calloc (const size_t n1, const size_t n2);

GSL_EXPORT
gsl_matrix_uchar *
gsl_matrix_uchar_alloc_from_block (gsl_block_uchar * b,
                                   const size_t offset,
                                   const size_t n1,
                                   const size_t n2,
                                   const size_t d2);

GSL_EXPORT
gsl_matrix_uchar *
gsl_matrix_uchar_alloc_from_matrix (gsl_matrix_uchar * m,
                                    const size_t k1,
                                    const size_t k2,
                                    const size_t n1,
                                    const size_t n2);

GSL_EXPORT
gsl_vector_uchar *
gsl_vector_uchar_alloc_row_from_matrix (gsl_matrix_uchar * m,
                                        const size_t i);

GSL_EXPORT
gsl_vector_uchar *
gsl_vector_uchar_alloc_col_from_matrix (gsl_matrix_uchar * m,
                                        const size_t j);

GSL_EXPORT void gsl_matrix_uchar_free (gsl_matrix_uchar * m);

/* Views */

GSL_EXPORT
_gsl_matrix_uchar_view
gsl_matrix_uchar_submatrix (gsl_matrix_uchar * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

GSL_EXPORT
_gsl_vector_uchar_view
gsl_matrix_uchar_row (gsl_matrix_uchar * m, const size_t i);

GSL_EXPORT
_gsl_vector_uchar_view
gsl_matrix_uchar_column (gsl_matrix_uchar * m, const size_t j);

GSL_EXPORT
_gsl_vector_uchar_view
gsl_matrix_uchar_diagonal (gsl_matrix_uchar * m);

GSL_EXPORT
_gsl_vector_uchar_view
gsl_matrix_uchar_subdiagonal (gsl_matrix_uchar * m, const size_t k);

GSL_EXPORT
_gsl_vector_uchar_view
gsl_matrix_uchar_superdiagonal (gsl_matrix_uchar * m, const size_t k);

GSL_EXPORT
_gsl_matrix_uchar_view
gsl_matrix_uchar_view_array (unsigned char * base,
                             const size_t n1,
                             const size_t n2);

GSL_EXPORT
_gsl_matrix_uchar_view
gsl_matrix_uchar_view_array_with_tda (unsigned char * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

GSL_EXPORT
_gsl_matrix_uchar_view
gsl_matrix_uchar_view_vector (gsl_vector_uchar * v,
                              const size_t n1,
                              const size_t n2);

GSL_EXPORT
_gsl_matrix_uchar_view
gsl_matrix_uchar_view_vector_with_tda (gsl_vector_uchar * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

GSL_EXPORT
_gsl_matrix_uchar_const_view
gsl_matrix_uchar_const_submatrix (const gsl_matrix_uchar * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

GSL_EXPORT
_gsl_vector_uchar_const_view
gsl_matrix_uchar_const_row (const gsl_matrix_uchar * m,
                            const size_t i);

GSL_EXPORT
_gsl_vector_uchar_const_view
gsl_matrix_uchar_const_column (const gsl_matrix_uchar * m,
                               const size_t j);

GSL_EXPORT
_gsl_vector_uchar_const_view
gsl_matrix_uchar_const_diagonal (const gsl_matrix_uchar * m);

GSL_EXPORT
_gsl_vector_uchar_const_view
gsl_matrix_uchar_const_subdiagonal (const gsl_matrix_uchar * m,
                                    const size_t k);

GSL_EXPORT
_gsl_vector_uchar_const_view
gsl_matrix_uchar_const_superdiagonal (const gsl_matrix_uchar * m,
                                      const size_t k);

GSL_EXPORT
_gsl_matrix_uchar_const_view
gsl_matrix_uchar_const_view_array (const unsigned char * base,
                                   const size_t n1,
                                   const size_t n2);

GSL_EXPORT
_gsl_matrix_uchar_const_view
gsl_matrix_uchar_const_view_array_with_tda (const unsigned char * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

GSL_EXPORT
_gsl_matrix_uchar_const_view
gsl_matrix_uchar_const_view_vector (const gsl_vector_uchar * v,
                                    const size_t n1,
                                    const size_t n2);

GSL_EXPORT
_gsl_matrix_uchar_const_view
gsl_matrix_uchar_const_view_vector_with_tda (const gsl_vector_uchar * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

GSL_EXPORT unsigned char   gsl_matrix_uchar_get(const gsl_matrix_uchar * m, const size_t i, const size_t j);
GSL_EXPORT void    gsl_matrix_uchar_set(gsl_matrix_uchar * m, const size_t i, const size_t j, const unsigned char x);

GSL_EXPORT unsigned char * gsl_matrix_uchar_ptr(gsl_matrix_uchar * m, const size_t i, const size_t j);
GSL_EXPORT const unsigned char * gsl_matrix_uchar_const_ptr(const gsl_matrix_uchar * m, const size_t i, const size_t j);

GSL_EXPORT void gsl_matrix_uchar_set_zero (gsl_matrix_uchar * m);
GSL_EXPORT void gsl_matrix_uchar_set_identity (gsl_matrix_uchar * m);
GSL_EXPORT void gsl_matrix_uchar_set_all (gsl_matrix_uchar * m, unsigned char x);

GSL_EXPORT int gsl_matrix_uchar_fread (FILE * stream, gsl_matrix_uchar * m) ;
GSL_EXPORT int gsl_matrix_uchar_fwrite (FILE * stream, const gsl_matrix_uchar * m) ;
GSL_EXPORT int gsl_matrix_uchar_fscanf (FILE * stream, gsl_matrix_uchar * m);
GSL_EXPORT int gsl_matrix_uchar_fprintf (FILE * stream, const gsl_matrix_uchar * m, const char * format);

GSL_EXPORT int gsl_matrix_uchar_memcpy(gsl_matrix_uchar * dest, const gsl_matrix_uchar * src);
GSL_EXPORT int gsl_matrix_uchar_swap(gsl_matrix_uchar * m1, gsl_matrix_uchar * m2);

GSL_EXPORT int gsl_matrix_uchar_swap_rows(gsl_matrix_uchar * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_uchar_swap_columns(gsl_matrix_uchar * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_uchar_swap_rowcol(gsl_matrix_uchar * m, const size_t i, const size_t j);
GSL_EXPORT int gsl_matrix_uchar_transpose (gsl_matrix_uchar * m);
GSL_EXPORT int gsl_matrix_uchar_transpose_memcpy (gsl_matrix_uchar * dest, const gsl_matrix_uchar * src);

GSL_EXPORT unsigned char gsl_matrix_uchar_max (const gsl_matrix_uchar * m);
GSL_EXPORT unsigned char gsl_matrix_uchar_min (const gsl_matrix_uchar * m);
GSL_EXPORT void gsl_matrix_uchar_minmax (const gsl_matrix_uchar * m, unsigned char * min_out, unsigned char * max_out);

GSL_EXPORT void gsl_matrix_uchar_max_index (const gsl_matrix_uchar * m, size_t * imax, size_t *jmax);
GSL_EXPORT void gsl_matrix_uchar_min_index (const gsl_matrix_uchar * m, size_t * imin, size_t *jmin);
GSL_EXPORT void gsl_matrix_uchar_minmax_index (const gsl_matrix_uchar * m, size_t * imin, size_t * jmin, size_t * imax, size_t * jmax);

GSL_EXPORT int gsl_matrix_uchar_isnull (const gsl_matrix_uchar * m);

GSL_EXPORT int gsl_matrix_uchar_add (gsl_matrix_uchar * a, const gsl_matrix_uchar * b);
GSL_EXPORT int gsl_matrix_uchar_sub (gsl_matrix_uchar * a, const gsl_matrix_uchar * b);
GSL_EXPORT int gsl_matrix_uchar_mul_elements (gsl_matrix_uchar * a, const gsl_matrix_uchar * b);
GSL_EXPORT int gsl_matrix_uchar_div_elements (gsl_matrix_uchar * a, const gsl_matrix_uchar * b);
GSL_EXPORT int gsl_matrix_uchar_scale (gsl_matrix_uchar * a, const double x);
GSL_EXPORT int gsl_matrix_uchar_add_constant (gsl_matrix_uchar * a, const double x);
GSL_EXPORT int gsl_matrix_uchar_add_diagonal (gsl_matrix_uchar * a, const double x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
GSL_EXPORT int gsl_matrix_uchar_get_row(gsl_vector_uchar * v, const gsl_matrix_uchar * m, const size_t i);
GSL_EXPORT int gsl_matrix_uchar_get_col(gsl_vector_uchar * v, const gsl_matrix_uchar * m, const size_t j);
GSL_EXPORT int gsl_matrix_uchar_set_row(gsl_matrix_uchar * m, const size_t i, const gsl_vector_uchar * v);
GSL_EXPORT int gsl_matrix_uchar_set_col(gsl_matrix_uchar * m, const size_t j, const gsl_vector_uchar * v);

/* inline functions if you are using GCC */

#ifdef HAVE_INLINE
extern inline
unsigned char
gsl_matrix_uchar_get(const gsl_matrix_uchar * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_VAL("first index out of range", GSL_EINVAL, 0) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_VAL("second index out of range", GSL_EINVAL, 0) ;
    }
#endif
  return m->data[i * m->tda + j] ;
}

extern inline
void
gsl_matrix_uchar_set(gsl_matrix_uchar * m, const size_t i, const size_t j, const unsigned char x)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_VOID("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_VOID("second index out of range", GSL_EINVAL) ;
    }
#endif
  m->data[i * m->tda + j] = x ;
}

extern inline
unsigned char *
gsl_matrix_uchar_ptr(gsl_matrix_uchar * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
    }
#endif
  return (unsigned char *) (m->data + (i * m->tda + j)) ;
}

extern inline
const unsigned char *
gsl_matrix_uchar_const_ptr(const gsl_matrix_uchar * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (i >= m->size1)
    {
      GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
    }
  else if (j >= m->size2)
    {
      GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
    }
#endif
  return (const unsigned char *) (m->data + (i * m->tda + j)) ;
}

#endif

__END_DECLS

#endif /* __GSL_MATRIX_UCHAR_H__ */


#pragma once

#define UPCA_ARCH_H

#include <memory>
#include <vector>

#include <stdint.h>

#include <gsl/gsl>

namespace upca {
namespace arch {

namespace detail {
template <typename Reason> struct null_resolver {
  struct null_type {
    friend std::ostream &operator<<(std::ostream &os, const null_type &) {
      return os;
    }
  };
  static null_type resolve(const std::string &) {
    throw std::runtime_error(Reason::reason);
  }
};

template <typename TIMESTAMP, typename REASON> struct basic_pmu {
  using resolver_type = upca::arch::detail::null_resolver<REASON>;
  template <typename T> basic_pmu(const T &) {}

  uint64_t timestamp_begin() { return TIMESTAMP::timestamp(); }
  uint64_t timestamp_end() { return TIMESTAMP::timestamp(); }

  gsl::span<uint64_t>::index_type start(gsl::span<uint64_t>) { return 0; }
  gsl::span<uint64_t>::index_type stop(gsl::span<uint64_t>) { return 0; }
};

} // namespace detail

template <typename ARCH> class arch_common_base final {
private:
  ptrdiff_t offset_ = 0;
  const ptrdiff_t slice_;
  ARCH arch_;

  template <typename T> friend class resolver;

public:
  using resolver_type = typename ARCH::resolver_type;

  template <typename C>
  arch_common_base(const C &pmcs, const unsigned external_pmcs = 0)
      : slice_(gsl::narrow<ptrdiff_t>(pmcs.size() + external_pmcs)),
        arch_(pmcs) {}

  gsl::span<uint64_t>::index_type start(gsl::span<uint64_t> output) {
    const auto count = arch_.start(output.subspan(1, slice_ - 1));
    output[0] = arch_.timestamp_begin();
    return count + 1;
  }

  gsl::span<uint64_t>::index_type stop(gsl::span<uint64_t> output) {
    output[0] = arch_.timestamp_end() - output[0];
    const auto count = arch_.stop(output.subspan(1, slice_ - 1));
    return count + 1;
  }
};

} // namespace arch
} // namespace upca

#ifdef __aarch64__

#  include "aarch64.h"

using pmu = upca::arch::arch_common_base<upca::arch::aarch64::pmu>;

#elif defined(__ARM_ARCH_7A__)

#  include "aarch32.h"

using pmu = upca::arch::arch_common_base<upca::arch::aarch32::pmu>;

#elif defined(__sparc)

#  include "sparc.h"

using pmu = upca::arch::arch_common_base<upca::arch::sparc::pmu>;

#elif defined(__x86_64__)
#  include "x86_64.h"

/* MCK can be either:
 * - mck_rawmsr for raw MSR access via rdmsr/wrmsr mcK syscalls
 * - mck_mckmsr for PMU programmed via mcK pmc_* syscalls
 *
 * LINUX can be any of:
 * - linux_perf for using perf
 * - linux_jevents for using jevents
 * - linux_rawmsr for raw MSR access using /dev/cpu/N/msr
 */

#ifdef JEVENTS_FOUND
using pmu = upca::arch::arch_common_base<upca::arch::x86_64::x86_linux_mckernel<
    upca::arch::x86_64::mck_mckmsr, upca::arch::x86_64::linux_rawmsr>>;
#else
using pmu = upca::arch::arch_common_base<upca::arch::x86_64::x86_64_pmu>;
#endif

#elif defined(__bgq__)

#  include "bgq.h"

using pmu = upca::arch::arch_common_base<upca::arch::bgq::pmu>;

#elif defined(__ppc__) || defined(_ARCH_PPC) || defined(__PPC__)

#  include "ppc.h"

using pmu = upca::arch::arch_common_base<upca::arch::ppc::pmu>;

#else

#error "Unkown/Unsupported architecture"

#endif


/* block/gsl_block_long_double.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program 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 GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_BLOCK_LONG_DOUBLE_H__
#define __GSL_BLOCK_LONG_DOUBLE_H__

#include <stdlib.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_types.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

struct gsl_block_long_double_struct
{
  size_t size;
  long double *data;
};

typedef struct gsl_block_long_double_struct gsl_block_long_double;

GSL_EXPORT gsl_block_long_double *gsl_block_long_double_alloc (const size_t n);
GSL_EXPORT gsl_block_long_double *gsl_block_long_double_calloc (const size_t n);
GSL_EXPORT void gsl_block_long_double_free (gsl_block_long_double * b);

GSL_EXPORT int gsl_block_long_double_fread (FILE * stream, gsl_block_long_double * b);
GSL_EXPORT int gsl_block_long_double_fwrite (FILE * stream, const gsl_block_long_double * b);
GSL_EXPORT int gsl_block_long_double_fscanf (FILE * stream, gsl_block_long_double * b);
GSL_EXPORT int gsl_block_long_double_fprintf (FILE * stream, const gsl_block_long_double * b, const char *format);

GSL_EXPORT int gsl_block_long_double_raw_fread (FILE * stream, long double * b, const size_t n, const size_t stride);
GSL_EXPORT int gsl_block_long_double_raw_fwrite (FILE * stream, const long double * b, const size_t n, const size_t stride);
GSL_EXPORT int gsl_block_long_double_raw_fscanf (FILE * stream, long double * b, const size_t n, const size_t stride);
GSL_EXPORT int gsl_block_long_double_raw_fprintf (FILE * stream, const long double * b, const size_t n, const size_t stride, const char *format);

GSL_EXPORT size_t gsl_block_long_double_size (const gsl_block_long_double * b);
GSL_EXPORT long double * gsl_block_long_double_data (const gsl_block_long_double * b);

__END_DECLS

#endif /* __GSL_BLOCK_LONG_DOUBLE_H__ */


//
// Created by bessermt on 6/3/18.
//

#ifndef SRSGRADE_TIMESTAMP_H
#define SRSGRADE_TIMESTAMP_H

#include <iostream>
#include <iomanip>
#include <chrono>
#include <gsl.h>

class Timestamp
{
private:
    std::chrono::system_clock::time_point time_point_{};

    void set(const std::string date_string, const std::string time_string);

    friend std::istream& operator>>(std::istream& is, Timestamp& timestamp)
    {
        std::string date_string;

		is >> date_string;
		Ensures(!date_string.empty());

		char time_cstr[256]{};
		constexpr auto max_time_len{ std::extent<decltype(time_cstr)>::value };
		is >> std::ws;
		is.getline(time_cstr, max_time_len, ',');
		const auto time_len{ strlen(time_cstr) };
		Ensures(time_len > 0 && time_len < max_time_len);

		const auto time_string{ std::string(time_cstr) };

        timestamp.set(date_string, time_string);

        return is;
    }

	friend bool operator<(const Timestamp lhs, const Timestamp rhs)
    {
		return lhs.time_point_ < rhs.time_point_;
    }

public:
    auto is_good() const
    {
		const auto is_past{ time_point_ < std::chrono::system_clock::now() };

        return is_past; // TODO: Any better tests?
    }
};

#endif // SRSGRADE_TIMESTAMP_H


/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2017 Couchbase, Inc
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#pragma once

#include "monotonic.h"

#include <memcached/dockey.h>
#include <memcached/types.h>
#include <nlohmann/json_fwd.hpp>
#include <platform/sized_buffer.h>
#include <gsl/gsl>

#include <unordered_map>
#include <vector>

namespace Collections {

// The reserved name of the system owned, default collection.
const char* const _DefaultCollectionIdentifier = "_default";
static cb::const_char_buffer DefaultCollectionIdentifier(
        _DefaultCollectionIdentifier);

const char* const _DefaultScopeIdentifier = "_default";
static cb::const_char_buffer DefaultScopeIdentifier(_DefaultScopeIdentifier);

// SystemEvent keys or parts which will be made into keys
const char* const SystemSeparator = ":"; // Note this never changes
const char* const CollectionEventPrefixWithSeparator = "_collection:";
const char* const ScopeEventPrefixWithSeparator = "_scope:";

// Couchstore private file name for manifest data
const char CouchstoreManifest[] = "_local/collections_manifest";

// Length of the string excluding the zero terminator (i.e. strlen)
const size_t CouchstoreManifestLen = sizeof(CouchstoreManifest) - 1;

using ManifestUid = WeaklyMonotonic<uint64_t>;

// Map used in summary stats
using Summary = std::unordered_map<CollectionID, uint64_t>;

struct ManifestUidNetworkOrder {
    ManifestUidNetworkOrder(ManifestUid uid) : uid(htonll(uid)) {
    }
    ManifestUid to_host() const {
        return ntohll(uid);
    }
    ManifestUid::value_type uid;
};
static_assert(sizeof(ManifestUidNetworkOrder) == 8,
              "ManifestUidNetworkOrder must have fixed size of 8 bytes as "
              "written to disk.");

/**
 * Return a ManifestUid from a C-string.
 * A valid ManifestUid is a C-string where each character satisfies
 * std::isxdigit and can be converted to a ManifestUid by std::strtoull.
 *
 * @param uid C-string uid
 * @param len a length for validation purposes
 * @throws std::invalid_argument if uid is invalid
 */
ManifestUid makeUid(const char* uid, size_t len = 16);

/**
 * Return a ManifestUid from a std::string
 * A valid ManifestUid is a std::string where each character satisfies
 * std::isxdigit and can be converted to a ManifestUid by std::strtoull.
 *
 * @param uid std::string
 * @throws std::invalid_argument if uid is invalid
 */
static inline ManifestUid makeUid(const std::string& uid) {
    return makeUid(uid.c_str());
}

/**
 * Return a CollectionID from a C-string.
 * A valid CollectionID is a C-string where each character satisfies
 * std::isxdigit and can be converted to a CollectionID by std::strtoul.
 *
 * @param uid C-string uid
 * @throws std::invalid_argument if uid is invalid
 */
static inline CollectionID makeCollectionID(const char* uid) {
    // CollectionID is 8 characters max and smaller than a ManifestUid
    return gsl::narrow_cast<CollectionID>(makeUid(uid, 8));
}

/**
 * Return a CollectionID from a std::string
 * A valid CollectionID is a std::string where each character satisfies
 * std::isxdigit and can be converted to a CollectionID by std::strtoul
 *
 * @param uid std::string
 * @throws std::invalid_argument if uid is invalid
 */
static inline CollectionID makeCollectionID(const std::string& uid) {
    return makeCollectionID(uid.c_str());
}

/**
 * Return a ScopeID from a C-string.
 * A valid CollectionID is a std::string where each character satisfies
 * std::isxdigit and can be converted to a CollectionID by std::strtoul
 * @param uid C-string uid
 * @return std::invalid_argument if uid is invalid
 */
static inline ScopeID makeScopeID(const char* uid) {
    // ScopeId is 8 characters max and smaller than a ManifestUid
    return gsl::narrow_cast<ScopeID>(makeUid(uid, 8));
}

/**
 * Return a ScopeID from a std::string
 * A valid ScopeID is a std::string where each character satisfies
 * std::isxdigit and can be converted to a CollectionID by std::strtoul
 * @param uid std::string
 * @return std::invalid_argument if uid is invalid
 */
static inline ScopeID makeScopeID(const std::string& uid) {
    return makeScopeID(uid.c_str());
}

/**
 * The metadata of a single collection
 *
 * Default construction yields the default collection
 */
struct CollectionMetaData {
    ScopeID sid{ScopeID::Default}; // The scope that the collection belongs to
    CollectionID cid{CollectionID::Default}; // The collection's ID
    std::string name{_DefaultCollectionIdentifier}; // The collection's name
    cb::ExpiryLimit maxTtl; // The collection's maxTTL

    bool operator==(const CollectionMetaData& other) const {
        return sid == other.sid && cid == other.cid && name == other.name &&
               maxTtl == other.maxTtl;
    }
};

/**
 * All of the data a system event needs
 */
struct CreateEventData {
    ManifestUid manifestUid; // The Manifest which generated the event
    CollectionMetaData metaData; // The data of the new collection
};

struct DropEventData {
    ManifestUid manifestUid; // The Manifest which generated the event
    ScopeID sid; // The scope that the collection belonged to
    CollectionID cid; // The collection the event belongs to
};

struct CreateScopeEventData {
    ManifestUid manifestUid; // The Manifest which generated the event
    ScopeID sid; // The scope id
    std::string name; // The scope name
};

struct DropScopeEventData {
    ManifestUid manifestUid; // The Manifest which generated the event
    ScopeID sid; // The scope the event belongs to
};

/**
 * All of the data a DCP create event message will transmit in the value of the
 * message. This is the layout to be used on the wire and is in the correct
 * byte order
 */
struct CreateEventDcpData {
    CreateEventDcpData(const CreateEventData& ev)
        : manifestUid(ev.manifestUid),
          sid(ev.metaData.sid),
          cid(ev.metaData.cid) {
    }
    /// The manifest uid stored in network byte order ready for sending
    ManifestUidNetworkOrder manifestUid;
    /// The scope id stored in network byte order ready for sending
    ScopeIDNetworkOrder sid;
    /// The collection id stored in network byte order ready for sending
    CollectionIDNetworkOrder cid;
    // The size is sizeof(manifestUid) + sizeof(cid) + sizeof(sid)
    // (msvc won't allow that expression)
    constexpr static size_t size{16};
};

/**
 * All of the data a DCP create event message will transmit in the value of a
 * DCP system event message (when the collection is created with a TTL). This is
 * the layout to be used on the wire and is in the correct byte order
 */
struct CreateWithMaxTtlEventDcpData {
    CreateWithMaxTtlEventDcpData(const CreateEventData& ev)
        : manifestUid(ev.manifestUid),
          sid(ev.metaData.sid),
          cid(ev.metaData.cid),
          maxTtl(htonl(gsl::narrow_cast<uint32_t>(
                  ev.metaData.maxTtl.get().count()))) {
    }
    /// The manifest uid stored in network byte order ready for sending
    ManifestUidNetworkOrder manifestUid;
    /// The scope id stored in network byte order ready for sending
    ScopeIDNetworkOrder sid;
    /// The collection id stored in network byte order ready for sending
    CollectionIDNetworkOrder cid;
    /// The collection's maxTTL value (in network byte order)
    uint32_t maxTtl;
    // The size is sizeof(manifestUid) + sizeof(cid) + sizeof(sid) +
    //             sizeof(maxTTL) (msvc won't allow that expression)
    constexpr static size_t size{20};
};

/**
 * All of the data a DCP drop event message will transmit in the value of the
 * message. This is the layout to be used on the wire and is in the correct
 * byte order
 */
struct DropEventDcpData {
    DropEventDcpData(const DropEventData& data)
        : manifestUid(data.manifestUid), sid(data.sid), cid(data.cid) {
    }

    /// The manifest uid stored in network byte order ready for sending
    ManifestUidNetworkOrder manifestUid;
    /// The scope id stored in network byte order ready for sending
    ScopeIDNetworkOrder sid;
    /// The collection id stored in network byte order ready for sending
    CollectionIDNetworkOrder cid;
    // The size is sizeof(manifestUid) + sizeof(cid) (msvc won't allow that
    // expression)
    constexpr static size_t size{16};
};

/**
 * All of the data a DCP create scope event message will transmit in the value
 * of the message. This is the layout to be used on the wire and is in the
 * correct byte order
 */
struct CreateScopeEventDcpData {
    CreateScopeEventDcpData(const CreateScopeEventData& data)
        : manifestUid(data.manifestUid), sid(data.sid) {
    }
    /// The manifest uid stored in network byte order ready for sending
    ManifestUidNetworkOrder manifestUid;
    /// The scope id stored in network byte order ready for sending
    ScopeIDNetworkOrder sid;
    constexpr static size_t size{12};
};

/**
 * All of the data a DCP drop scope event message will transmit in the value of
 * the message. This is the layout to be used on the wire and is in the correct
 * byte order
 */
struct DropScopeEventDcpData {
    DropScopeEventDcpData(const DropScopeEventData& data)
        : manifestUid(data.manifestUid), sid(data.sid) {
    }

    /// The manifest uid stored in network byte order ready for sending
    ManifestUidNetworkOrder manifestUid;
    /// The collection id stored in network byte order ready for sending
    ScopeIDNetworkOrder sid;
    constexpr static size_t size{12};
};

/**
 * All unknown_collection errors should be accompanied by a error context
 * value which includes the manifest ID which was what the collection lookup
 * failed against.
 * @return json for use in setErrorJsonExtras
 */
nlohmann::json getUnknownCollectionErrorContext(uint64_t manifestUid);

/**
 * For creation of collection SystemEvents - The SystemEventFactory
 * glues the CollectionID into the event key (so create of x doesn't
 * collide with create of y). This method yields the 'keyExtra' parameter
 *
 * @param collection The value to turn into a string
 * @return the keyExtra parameter to be passed to SystemEventFactory
 */
std::string makeCollectionIdIntoString(CollectionID collection);

/**
 * For creation of scope SystemEvents - The SystemEventFactory
 * glues the ScopeID into the event key (so create of x doesn't
 * collide with create of y). This method yields the 'keyExtra' parameter
 *
 * @param sid The ScopeId to turn into a string
 * @return the keyExtra parameter to be passed to SystemEventFactory
 */
std::string makeScopeIdIntoString(ScopeID sid);

/**
 * For creation of collection SystemEvents - The SystemEventFactory
 * glues the CollectionID into the event key (so create of x doesn't
 * collide with create of y). This method basically reverses
 * makeCollectionIdIntoString so we can get a CollectionID from a
 * SystemEvent key
 *
 * @param key DocKey from a SystemEvent
 * @param separator the separator between the SystemEvent prefix and the
 *        CollectionID
 * @return the ID which was in the event
 */
CollectionID getCollectionIDFromKey(
        const DocKey& key,
        const char* separator = Collections::SystemSeparator);

/// Same as getCollectionIDFromKey but for events changing scopes
ScopeID getScopeIDFromKey(const DocKey& key,
                          const char* separator = Collections::SystemSeparator);

/**
 * Callback function for processing against dropped collections in an ephemeral
 * vb, returns true if the key at seqno should be dropped
 */
using IsDroppedEphemeralCb = std::function<bool(const DocKey&, int64_t)>;

} // end namespace Collections


///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2015 Microsoft Corporation. All rights reserved.
//
// This code is licensed under the MIT License (MIT).
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef GSL_GSL_H
#define GSL_GSL_H

#include <gsl/gsl_algorithm> // copy
#include <gsl/gsl_assert>    // Ensures/Expects
#include <gsl/gsl_byte>      // byte
#include <gsl/gsl_util>      // finally()/narrow()/narrow_cast()...
#include <gsl/multi_span>    // multi_span, strided_span...
#include <gsl/pointers>      // owner, not_null
#include <gsl/span>          // span
#include <gsl/string_span>   // zstring, string_span, zstring_builder...

#endif // GSL_GSL_H


/*  Copyright 2017 International Business Machines Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.  */

#pragma once

#include "kernelpp/types.h"
#include <gsl.h>

namespace mwe
{
    using kernelpp::maybe;
    using kernelpp::status;

    /* Operations  --------------------------------------------------------- */

    maybe<int> add(int a, int b);

    status add(const gsl::span<int> a,
               const gsl::span<int> b,
                     gsl::span<int> result
              );
}


/* matrix/gsl_matrix_uchar.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program 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 GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GSL_MATRIX_UCHAR_H__
#define __GSL_MATRIX_UCHAR_H__

#if !defined( GSL_FUN )
#  if !defined( GSL_DLL )
#    define GSL_FUN extern
#  elif defined( BUILD_GSL_DLL )
#    define GSL_FUN extern __declspec(dllexport)
#  else
#    define GSL_FUN extern __declspec(dllimport)
#  endif
#endif

#include <stdlib.h>
#include <gsl/gsl_types.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_inline.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_uchar.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

typedef struct
{
  size_t size1;
  size_t size2;
  size_t tda;
  unsigned char * data;
  gsl_block_uchar * block;
  int owner;
} gsl_matrix_uchar;

typedef struct
{
  gsl_matrix_uchar matrix;
} _gsl_matrix_uchar_view;

typedef _gsl_matrix_uchar_view gsl_matrix_uchar_view;

typedef struct
{
  gsl_matrix_uchar matrix;
} _gsl_matrix_uchar_const_view;

typedef const _gsl_matrix_uchar_const_view gsl_matrix_uchar_const_view;

/* Allocation */

GSL_FUN gsl_matrix_uchar *
gsl_matrix_uchar_alloc (const size_t n1, const size_t n2);

GSL_FUN gsl_matrix_uchar *
gsl_matrix_uchar_calloc (const size_t n1, const size_t n2);

GSL_FUN gsl_matrix_uchar *
gsl_matrix_uchar_alloc_from_block (gsl_block_uchar * b,
                                   const size_t offset,
                                   const size_t n1,
                                   const size_t n2,
                                   const size_t d2);

GSL_FUN gsl_matrix_uchar *
gsl_matrix_uchar_alloc_from_matrix (gsl_matrix_uchar * m,
                                    const size_t k1,
                                    const size_t k2,
                                    const size_t n1,
                                    const size_t n2);

GSL_FUN gsl_vector_uchar *
gsl_vector_uchar_alloc_row_from_matrix (gsl_matrix_uchar * m,
                                        const size_t i);

GSL_FUN gsl_vector_uchar *
gsl_vector_uchar_alloc_col_from_matrix (gsl_matrix_uchar * m,
                                        const size_t j);

GSL_FUN void gsl_matrix_uchar_free (gsl_matrix_uchar * m);

/* Views */

GSL_FUN _gsl_matrix_uchar_view
gsl_matrix_uchar_submatrix (gsl_matrix_uchar * m,
                            const size_t i, const size_t j,
                            const size_t n1, const size_t n2);

GSL_FUN _gsl_vector_uchar_view
gsl_matrix_uchar_row (gsl_matrix_uchar * m, const size_t i);

GSL_FUN _gsl_vector_uchar_view
gsl_matrix_uchar_column (gsl_matrix_uchar * m, const size_t j);

GSL_FUN _gsl_vector_uchar_view
gsl_matrix_uchar_diagonal (gsl_matrix_uchar * m);

GSL_FUN _gsl_vector_uchar_view
gsl_matrix_uchar_subdiagonal (gsl_matrix_uchar * m, const size_t k);

GSL_FUN _gsl_vector_uchar_view
gsl_matrix_uchar_superdiagonal (gsl_matrix_uchar * m, const size_t k);

GSL_FUN _gsl_vector_uchar_view
gsl_matrix_uchar_subrow (gsl_matrix_uchar * m, const size_t i,
                         const size_t offset, const size_t n);

GSL_FUN _gsl_vector_uchar_view
gsl_matrix_uchar_subcolumn (gsl_matrix_uchar * m, const size_t j,
                            const size_t offset, const size_t n);

GSL_FUN _gsl_matrix_uchar_view
gsl_matrix_uchar_view_array (unsigned char * base,
                             const size_t n1,
                             const size_t n2);

GSL_FUN _gsl_matrix_uchar_view
gsl_matrix_uchar_view_array_with_tda (unsigned char * base,
                                      const size_t n1,
                                      const size_t n2,
                                      const size_t tda);

GSL_FUN _gsl_matrix_uchar_view
gsl_matrix_uchar_view_vector (gsl_vector_uchar * v,
                              const size_t n1,
                              const size_t n2);

GSL_FUN _gsl_matrix_uchar_view
gsl_matrix_uchar_view_vector_with_tda (gsl_vector_uchar * v,
                                       const size_t n1,
                                       const size_t n2,
                                       const size_t tda);

GSL_FUN _gsl_matrix_uchar_const_view
gsl_matrix_uchar_const_submatrix (const gsl_matrix_uchar * m,
                                  const size_t i, const size_t j,
                                  const size_t n1, const size_t n2);

GSL_FUN _gsl_vector_uchar_const_view
gsl_matrix_uchar_const_row (const gsl_matrix_uchar * m,
                            const size_t i);

GSL_FUN _gsl_vector_uchar_const_view
gsl_matrix_uchar_const_column (const gsl_matrix_uchar * m,
                               const size_t j);

GSL_FUN _gsl_vector_uchar_const_view
gsl_matrix_uchar_const_diagonal (const gsl_matrix_uchar * m);

GSL_FUN _gsl_vector_uchar_const_view
gsl_matrix_uchar_const_subdiagonal (const gsl_matrix_uchar * m,
                                    const size_t k);

GSL_FUN _gsl_vector_uchar_const_view
gsl_matrix_uchar_const_superdiagonal (const gsl_matrix_uchar * m,
                                      const size_t k);

GSL_FUN _gsl_vector_uchar_const_view
gsl_matrix_uchar_const_subrow (const gsl_matrix_uchar * m, const size_t i,
                               const size_t offset, const size_t n);

GSL_FUN _gsl_vector_uchar_const_view
gsl_matrix_uchar_const_subcolumn (const gsl_matrix_uchar * m, const size_t j,
                                  const size_t offset, const size_t n);

GSL_FUN _gsl_matrix_uchar_const_view
gsl_matrix_uchar_const_view_array (const unsigned char * base,
                                   const size_t n1,
                                   const size_t n2);

GSL_FUN _gsl_matrix_uchar_const_view
gsl_matrix_uchar_const_view_array_with_tda (const unsigned char * base,
                                            const size_t n1,
                                            const size_t n2,
                                            const size_t tda);

GSL_FUN _gsl_matrix_uchar_const_view
gsl_matrix_uchar_const_view_vector (const gsl_vector_uchar * v,
                                    const size_t n1,
                                    const size_t n2);

GSL_FUN _gsl_matrix_uchar_const_view
gsl_matrix_uchar_const_view_vector_with_tda (const gsl_vector_uchar * v,
                                             const size_t n1,
                                             const size_t n2,
                                             const size_t tda);

/* Operations */

GSL_FUN void gsl_matrix_uchar_set_zero (gsl_matrix_uchar * m);
GSL_FUN void gsl_matrix_uchar_set_identity (gsl_matrix_uchar * m);
GSL_FUN void gsl_matrix_uchar_set_all (gsl_matrix_uchar * m, unsigned char x);

GSL_FUN int gsl_matrix_uchar_fread (FILE * stream, gsl_matrix_uchar * m) ;
GSL_FUN int gsl_matrix_uchar_fwrite (FILE * stream, const gsl_matrix_uchar * m) ;
GSL_FUN int gsl_matrix_uchar_fscanf (FILE * stream, gsl_matrix_uchar * m);
GSL_FUN int gsl_matrix_uchar_fprintf (FILE * stream, const gsl_matrix_uchar * m, const char * format);

GSL_FUN int gsl_matrix_uchar_memcpy(gsl_matrix_uchar * dest, const gsl_matrix_uchar * src);
GSL_FUN int gsl_matrix_uchar_swap(gsl_matrix_uchar * m1, gsl_matrix_uchar * m2);

GSL_FUN int gsl_matrix_uchar_swap_rows(gsl_matrix_uchar * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_uchar_swap_columns(gsl_matrix_uchar * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_uchar_swap_rowcol(gsl_matrix_uchar * m, const size_t i, const size_t j);
GSL_FUN int gsl_matrix_uchar_transpose (gsl_matrix_uchar * m);
GSL_FUN int gsl_matrix_uchar_transpose_memcpy (gsl_matrix_uchar * dest, const gsl_matrix_uchar * src);

GSL_FUN unsigned char gsl_matrix_uchar_max (const gsl_matrix_uchar * m);
GSL_FUN unsigned char gsl_matrix_uchar_min (const gsl_matrix_uchar * m);
GSL_FUN void gsl_matrix_uchar_minmax (const gsl_matrix_uchar * m, unsigned char * min_out, unsigned char * max_out);

GSL_FUN void gsl_matrix_uchar_max_index (const gsl_matrix_uchar * m, size_t * imax, size_t *jmax);
GSL_FUN void gsl_matrix_uchar_min_index (const gsl_matrix_uchar * m, size_t * imin, size_t *jmin);
GSL_FUN void gsl_matrix_uchar_minmax_index (const gsl_matrix_uchar * m, size_t * imin, size_t * jmin, size_t * imax, size_t * jmax);

GSL_FUN int gsl_matrix_uchar_isnull (const gsl_matrix_uchar * m);
GSL_FUN int gsl_matrix_uchar_ispos (const gsl_matrix_uchar * m);
GSL_FUN int gsl_matrix_uchar_isneg (const gsl_matrix_uchar * m);
GSL_FUN int gsl_matrix_uchar_isnonneg (const gsl_matrix_uchar * m);

GSL_FUN int gsl_matrix_uchar_add (gsl_matrix_uchar * a, const gsl_matrix_uchar * b);
GSL_FUN int gsl_matrix_uchar_sub (gsl_matrix_uchar * a, const gsl_matrix_uchar * b);
GSL_FUN int gsl_matrix_uchar_mul_elements (gsl_matrix_uchar * a, const gsl_matrix_uchar * b);
GSL_FUN int gsl_matrix_uchar_div_elements (gsl_matrix_uchar * a, const gsl_matrix_uchar * b);
GSL_FUN int gsl_matrix_uchar_scale (gsl_matrix_uchar * a, const double x);
GSL_FUN int gsl_matrix_uchar_add_constant (gsl_matrix_uchar * a, const double x);
GSL_FUN int gsl_matrix_uchar_add_diagonal (gsl_matrix_uchar * a, const double x);

/***********************************************************************/
/* The functions below are obsolete                                    */
/***********************************************************************/
GSL_FUN int gsl_matrix_uchar_get_row(gsl_vector_uchar * v, const gsl_matrix_uchar * m, const size_t i);
GSL_FUN int gsl_matrix_uchar_get_col(gsl_vector_uchar * v, const gsl_matrix_uchar * m, const size_t j);
GSL_FUN int gsl_matrix_uchar_set_row(gsl_matrix_uchar * m, const size_t i, const gsl_vector_uchar * v);
GSL_FUN int gsl_matrix_uchar_set_col(gsl_matrix_uchar * m, const size_t j, const gsl_vector_uchar * v);
/***********************************************************************/

/* inline functions if you are using GCC */

GSL_FUN INLINE_DECL unsigned char   gsl_matrix_uchar_get(const gsl_matrix_uchar * m, const size_t i, const size_t j);
GSL_FUN INLINE_DECL void    gsl_matrix_uchar_set(gsl_matrix_uchar * m, const size_t i, const size_t j, const unsigned char x);
GSL_FUN INLINE_DECL unsigned char * gsl_matrix_uchar_ptr(gsl_matrix_uchar * m, const size_t i, const size_t j);
GSL_FUN INLINE_DECL const unsigned char * gsl_matrix_uchar_const_ptr(const gsl_matrix_uchar * m, const size_t i, const size_t j);

#ifdef HAVE_INLINE
INLINE_FUN
unsigned char
gsl_matrix_uchar_get(const gsl_matrix_uchar * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_VAL("first index out of range", GSL_EINVAL, 0) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_VAL("second index out of range", GSL_EINVAL, 0) ;
        }
    }
#endif
  return m->data[i * m->tda + j] ;
}

INLINE_FUN
void
gsl_matrix_uchar_set(gsl_matrix_uchar * m, const size_t i, const size_t j, const unsigned char x)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_VOID("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_VOID("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  m->data[i * m->tda + j] = x ;
}

INLINE_FUN
unsigned char *
gsl_matrix_uchar_ptr(gsl_matrix_uchar * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  return (unsigned char *) (m->data + (i * m->tda + j)) ;
}

INLINE_FUN
const unsigned char *
gsl_matrix_uchar_const_ptr(const gsl_matrix_uchar * m, const size_t i, const size_t j)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(1))
    {
      if (i >= m->size1)
        {
          GSL_ERROR_NULL("first index out of range", GSL_EINVAL) ;
        }
      else if (j >= m->size2)
        {
          GSL_ERROR_NULL("second index out of range", GSL_EINVAL) ;
        }
    }
#endif
  return (const unsigned char *) (m->data + (i * m->tda + j)) ;
}

#endif

__END_DECLS

#endif /* __GSL_MATRIX_UCHAR_H__ */


#pragma once

#include <gsl.h>

#include <boost/graph/graph_traits.hpp>
#include <boost/range/adaptor/filtered.hpp>

#include "VesselForestData.h"

namespace crimson
{
struct ActiveVesselsFilter {
    ActiveVesselsFilter() = default;
    ActiveVesselsFilter(const VesselForestData* owner)
        : _owner(owner)
    {
    }

    bool operator()(const VesselForestData::VesselPathUIDType& uid) const { return _owner->getVesselUsedInBlending(uid); }

private:
    const VesselForestData* _owner = nullptr;
};

struct ActiveBooleanOperationsFilter {
    ActiveBooleanOperationsFilter() = default;
    ActiveBooleanOperationsFilter(const VesselForestData* owner)
        : _owner(owner)
    {
    }

    bool operator()(const VesselForestData::BooleanOperationInfo& bop) const
    {
        return _owner->getVesselUsedInBlending(bop.vessels.first) && _owner->getVesselUsedInBlending(bop.vessels.second);
    }

private:
    const VesselForestData* _owner;
};

struct OutEdgeIteratorFilter {
    OutEdgeIteratorFilter() = default;
    OutEdgeIteratorFilter(const VesselForestData::VesselPathUIDType* uid)
        : _uid(uid)
    {
    }

    bool operator()(const VesselForestData::BooleanOperationInfo& bop) const
    {
        return bop.vessels.first == *_uid || bop.vessels.second == *_uid;
    }

private:
    const VesselForestData::VesselPathUIDType* _uid = nullptr;
};

struct ExtractOutEdgeFromBooleanOperationInfo {
    ExtractOutEdgeFromBooleanOperationInfo() = default;
    ExtractOutEdgeFromBooleanOperationInfo(const VesselForestData::VesselPathUIDType* sourceVertexUID)
        : _sourceVertexUID(sourceVertexUID)
    {
    }

    VesselForestData::VesselPathUIDPair operator()(const VesselForestData::BooleanOperationInfo& bop) const
    {
        if (*_sourceVertexUID == bop.vessels.first) {
            return bop.vessels;
        }
        Expects(*_sourceVertexUID == bop.vessels.second);
        return std::make_pair(bop.vessels.second, bop.vessels.first);
    }

private:
    const VesselForestData::VesselPathUIDType* _sourceVertexUID;
};

} // namespace crimson

namespace boost
{
template <>
struct graph_traits<crimson::VesselForestData> {
    /////////////////////////////////
    // Graph concept
    /////////////////////////////////
    using vertex_descriptor = crimson::VesselForestData::VesselPathUIDType;
    using edge_descriptor = crimson::VesselForestData::VesselPathUIDPair;

    using directed_category = boost::undirected_tag;
    using edge_parallel_category = boost::disallow_parallel_edge_tag;

    struct traversal_category : boost::vertex_list_graph_tag, boost::incidence_graph_tag {
    };

    /////////////////////////////////
    // Incidence graph concept
    /////////////////////////////////
    using out_edge_iterator = boost::transform_iterator<
        crimson::ExtractOutEdgeFromBooleanOperationInfo,
        boost::filter_iterator<
            crimson::OutEdgeIteratorFilter,
            boost::filter_iterator<crimson::ActiveBooleanOperationsFilter,
                                   crimson::VesselForestData::BooleanOperationContainerType::const_iterator>>>;
    using degree_size_type = std::size_t;

    // out_edges(v, g)
    // source(e, g)
    // target(e, g)
    // out_degree(v, g)

    /////////////////////////////////
    // Vertex list graph concept
    /////////////////////////////////
    using vertex_iterator = boost::filter_iterator<crimson::ActiveVesselsFilter,
                                                   crimson::VesselForestData::VesselPathUIDContainerType::const_iterator>;
    using vertices_size_type = crimson::VesselForestData::VesselPathUIDContainerType::size_type;

    // vertices(g)
    // num_vertices(g)

    static vertex_descriptor null_vertex() { return {}; }
};
} // namespace boost

namespace crimson
{

inline boost::graph_traits<crimson::VesselForestData>::vertex_descriptor
source(const boost::graph_traits<crimson::VesselForestData>::edge_descriptor& e, const crimson::VesselForestData& g)
{
    return e.first;
}

inline boost::graph_traits<crimson::VesselForestData>::vertex_descriptor
target(const boost::graph_traits<crimson::VesselForestData>::edge_descriptor& e, const crimson::VesselForestData& g)
{
    return e.second;
}

inline std::pair<boost::graph_traits<crimson::VesselForestData>::out_edge_iterator,
                 boost::graph_traits<crimson::VesselForestData>::out_edge_iterator>
out_edges(const boost::graph_traits<crimson::VesselForestData>::vertex_descriptor& v, const crimson::VesselForestData& g)
{
    auto filtIter1 = boost::make_filter_iterator(crimson::ActiveBooleanOperationsFilter{&g}, g.getBooleanOperations().begin(),
                                                 g.getBooleanOperations().end());
    auto endFiltIter1 = boost::make_filter_iterator(crimson::ActiveBooleanOperationsFilter{&g}, g.getBooleanOperations().end(),
                                                    g.getBooleanOperations().end());

    auto filtIter2 = boost::make_filter_iterator(crimson::OutEdgeIteratorFilter{&v}, filtIter1, endFiltIter1);
    auto endFiltIter2 = boost::make_filter_iterator(crimson::OutEdgeIteratorFilter{&v}, endFiltIter1, endFiltIter1);

    return {boost::make_transform_iterator(filtIter2, crimson::ExtractOutEdgeFromBooleanOperationInfo{&v}),
            boost::make_transform_iterator(endFiltIter2, crimson::ExtractOutEdgeFromBooleanOperationInfo{&v})};
}

inline boost::graph_traits<crimson::VesselForestData>::degree_size_type
out_degree(const boost::graph_traits<crimson::VesselForestData>::vertex_descriptor& v, const crimson::VesselForestData& g)
{
    auto iterPair = out_edges(v, g);
    return static_cast<boost::graph_traits<crimson::VesselForestData>::degree_size_type>(
        std::distance(iterPair.first, iterPair.second));
}

inline std::pair<boost::graph_traits<crimson::VesselForestData>::vertex_iterator,
                 boost::graph_traits<crimson::VesselForestData>::vertex_iterator>
vertices(const crimson::VesselForestData& g)
{
    auto iter = boost::make_filter_iterator(crimson::ActiveVesselsFilter{&g}, g.getVessels().begin(), g.getVessels().end());
    auto endIter = boost::make_filter_iterator(crimson::ActiveVesselsFilter{&g}, g.getVessels().end(), g.getVessels().end());

    return {iter, endIter};
}

inline boost::graph_traits<crimson::VesselForestData>::vertices_size_type num_vertices(const crimson::VesselForestData& g)
{
    auto iterPair = vertices(g);
    return static_cast<boost::graph_traits<crimson::VesselForestData>::vertices_size_type>(
        std::distance(iterPair.first, iterPair.second));
}

// static void _test_vertex_list_graph_concept()
//{
//    BOOST_CONCEPT_ASSERT((boost::IncidenceGraphConcept<crimson::VesselForestData>));
//    BOOST_CONCEPT_ASSERT((boost::VertexListGraphConcept<crimson::VesselForestData>));
//}

} // namespace crimson


#pragma once

#include <vector>
#include <string>
#include <memory>
#include <functional>
#include <gsl>

struct Vec3 { float x,y,z; };
struct Vec2 { float x,y; };

#include "types.h"
#include "automaticId.h"