#pragma once

#include <halley/utils/utils.h>
#include <vector>
#include <gsl/gsl>
#include "halley/data_structures/flat_map.h"
#include "halley/maths/vector2.h"
#include "halley/maths/rect.h"
#include "halley/file/path.h"
#include <map>
#include <unordered_map>
#include <cstdint>
#include <utility>
#include <set>
#include "halley/data_structures/maybe.h"
#include "halley/maths/colour.h"
#include "halley/maths/vector4.h"

namespace Halley
{
    class String;

    class SerializerOptions
    {
    public:
        constexpr static int maxVersion = 1;

        int version = 0;
        bool exhaustiveDictionary = false;
        std::function< std::optional< size_t >( const String& string ) > stringToIndex;
        std::function< const String&( size_t index ) > indexToString;

        SerializerOptions() = default;
        SerializerOptions( int version ) :
            version( version )
        { }
    };

    class SerializerState
    {
    };

    class ByteSerializationBase
    {
    public:
        ByteSerializationBase( SerializerOptions options ) :
            options( std::move( options ) )
        { }

        SerializerState* setState( SerializerState* state );

        template < typename T >
        T* getState() const
        {
            return static_cast< T* >( state );
        }

        int getVersion() const { return version; }
        void setVersion( int v ) { version = v; }

    protected:
        SerializerOptions options;

    private:
        SerializerState* state = nullptr;
        int version = 0;
    };

    class Serializer : public ByteSerializationBase
    {
    public:
        Serializer( SerializerOptions options );
        explicit Serializer( gsl::span< gsl::byte > dst, SerializerOptions options );

        template < typename T, typename std::enable_if< std::is_convertible< T, std::function< void( Serializer& ) > >::value, int >::type = 0 >
        static Bytes toBytes( const T& f, SerializerOptions options = {} )
        {
            auto dry = Serializer( options );
            f( dry );
            Bytes result( dry.getSize() );
            auto s = Serializer( gsl::as_writable_bytes( gsl::span< Halley::Byte >( result ) ), options );
            f( s );
            return result;
        }

        template < typename T, typename std::enable_if< !std::is_convertible< T, std::function< void( Serializer& ) > >::value, int >::type = 0 >
        static Bytes toBytes( const T& value, SerializerOptions options = {} )
        {
            return toBytes( [ &value ]( Serializer& s ) { s << value; }, options );
        }

        size_t getSize() const { return size; }

        Serializer& operator<<( bool val ) { return serializePod( val ); }
        Serializer& operator<<( int8_t val ) { return serializeInteger( val ); }
        Serializer& operator<<( uint8_t val ) { return serializeInteger( val ); }
        Serializer& operator<<( int16_t val ) { return serializeInteger( val ); }
        Serializer& operator<<( uint16_t val ) { return serializeInteger( val ); }
        Serializer& operator<<( int32_t val ) { return serializeInteger( val ); }
        Serializer& operator<<( uint32_t val ) { return serializeInteger( val ); }
        Serializer& operator<<( int64_t val ) { return serializeInteger( val ); }
        Serializer& operator<<( uint64_t val ) { return serializeInteger( val ); }
        Serializer& operator<<( float val ) { return serializePod( val ); }
        Serializer& operator<<( double val ) { return serializePod( val ); }

        Serializer& operator<<( const std::string& str );
        Serializer& operator<<( const String& str );
        Serializer& operator<<( const Path& path );
        Serializer& operator<<( gsl::span< const gsl::byte > span );
        Serializer& operator<<( const Bytes& bytes );

        template < typename T >
        Serializer& operator<<( const std::vector< T >& val )
        {
            unsigned int sz = static_cast< unsigned int >( val.size() );
            *this << sz;
            for ( unsigned int i = 0; i < sz; i++ )
            {
                *this << val[ i ];
            }
            return *this;
        }

        template < typename T, typename U >
        Serializer& operator<<( const FlatMap< T, U >& val )
        {
            *this << static_cast< unsigned int >( val.size() );
            for ( auto& kv : val )
            {
                *this << kv.first << kv.second;
            }
            return *this;
        }

        template < typename T, typename U >
        Serializer& operator<<( const std::map< T, U >& val )
        {
            *this << static_cast< unsigned int >( val.size() );
            for ( auto& kv : val )
            {
                *this << kv.first << kv.second;
            }
            return *this;
        }

        template < typename T, typename U >
        Serializer& operator<<( const std::unordered_map< T, U >& val )
        {
            std::map< T, U > m;
            for ( auto& kv : val )
            {
                m[ kv.first ] = kv.second;
            }
            return ( *this << m );
        }

        template < typename T >
        Serializer& operator<<( const std::set< T >& val )
        {
            unsigned int sz = static_cast< unsigned int >( val.size() );
            *this << sz;
            for ( auto& v : val )
            {
                *this << v;
            }
            return *this;
        }

        template < typename T >
        Serializer& operator<<( const Vector2D< T >& val )
        {
            return *this << val.x << val.y;
        }

        template < typename T >
        Serializer& operator<<( const Vector3D< T >& val )
        {
            return *this << val.x << val.y << val.z;
        }

        template < typename T >
        Serializer& operator<<( const Vector4D< T >& val )
        {
            return *this << val.x << val.y << val.z << val.w;
        }

        template < typename T >
        Serializer& operator<<( const Colour4< T >& val )
        {
            return *this << val.r << val.g << val.b << val.a;
        }

        template < typename T >
        Serializer& operator<<( const Rect2D< T >& val )
        {
            return *this << val.getTopLeft() << val.getBottomRight();
        }

        template < typename T, typename U >
        Serializer& operator<<( const std::pair< T, U >& p )
        {
            return *this << p.first << p.second;
        }

        template < typename T >
        Serializer& operator<<( const std::optional< T >& p )
        {
            if ( p )
            {
                return *this << true << p.value();
            }
            else
            {
                return *this << false;
            }
        }

        template < typename T >
        Serializer& operator<<( const Range< T >& p )
        {
            return *this << p.start << p.end;
        }

        template < typename T, std::enable_if_t< std::is_enum< T >::value == true, int > = 0 >
        Serializer& operator<<( const T& val )
        {
            using B = typename std::underlying_type< T >::type;
            return *this << B( val );
        }

        template < typename T, std::enable_if_t< std::is_enum< T >::value == false, int > = 0 >
        Serializer& operator<<( const T& val )
        {
            val.serialize( *this );
            return *this;
        }

        size_t getPosition() const { return size; }

    private:
        size_t size = 0;
        gsl::span< gsl::byte > dst;
        bool dryRun;

        template < typename T >
        Serializer& serializePod( T val )
        {
            if ( !dryRun )
            {
                memcpy( dst.data() + size, &val, sizeof( T ) );
            }
            size += sizeof( T );
            return *this;
        }

        template < typename T >
        Serializer& serializeInteger( T val )
        {
            if ( options.version >= 1 )
            {
                // Variable-length
                if constexpr ( std::is_signed_v< T > )
                {
                    serializeVariableInteger( static_cast< uint64_t >( val >= 0 ? val : -( val + 1 ) ), val < 0 );
                }
                else
                {
                    serializeVariableInteger( val, {} );
                }
                return *this;
            }
            else
            {
                // Fixed length
                return serializePod( val );
            }
        }

        void serializeVariableInteger( uint64_t val, std::optional< bool > sign );
    };

    class Deserializer : public ByteSerializationBase
    {
    public:
        Deserializer( gsl::span< const gsl::byte > src, SerializerOptions options = {} );
        Deserializer( const Bytes& src, SerializerOptions options = {} );

        template < typename T >
        static T fromBytes( const Bytes& src, SerializerOptions options = {} )
        {
            T result;
            Deserializer s( src, std::move( options ) );
            s >> result;
            return result;
        }

        template < typename T >
        static T fromBytes( gsl::span< const gsl::byte > src, SerializerOptions options = {} )
        {
            T result;
            Deserializer s( src, std::move( options ) );
            s >> result;
            return result;
        }

        template < typename T >
        static void fromBytes( T& target, const Bytes& src, SerializerOptions options = {} )
        {
            Deserializer s( src, std::move( options ) );
            s >> target;
        }

        template < typename T >
        static void fromBytes( T& target, gsl::span< const gsl::byte > src, SerializerOptions options = {} )
        {
            Deserializer s( src, std::move( options ) );
            s >> target;
        }

        Deserializer& operator>>( bool& val ) { return deserializePod( val ); }
        Deserializer& operator>>( int8_t& val ) { return deserializeInteger( val ); }
        Deserializer& operator>>( uint8_t& val ) { return deserializeInteger( val ); }
        Deserializer& operator>>( int16_t& val ) { return deserializeInteger( val ); }
        Deserializer& operator>>( uint16_t& val ) { return deserializeInteger( val ); }
        Deserializer& operator>>( int32_t& val ) { return deserializeInteger( val ); }
        Deserializer& operator>>( uint32_t& val ) { return deserializeInteger( val ); }
        Deserializer& operator>>( int64_t& val ) { return deserializeInteger( val ); }
        Deserializer& operator>>( uint64_t& val ) { return deserializeInteger( val ); }
        Deserializer& operator>>( float& val ) { return deserializePod( val ); }
        Deserializer& operator>>( double& val ) { return deserializePod( val ); }

        Deserializer& operator>>( std::string& str );
        Deserializer& operator>>( String& str );
        Deserializer& operator>>( Path& p );
        Deserializer& operator>>( gsl::span< gsl::byte > span );
        Deserializer& operator>>( Bytes& bytes );

        template < typename T >
        Deserializer& operator>>( std::vector< T >& val )
        {
            unsigned int sz;
            *this >> sz;
            ensureSufficientBytesRemaining( sz ); // Expect at least one byte per vector entry

            val.clear();
            val.reserve( sz );
            for ( unsigned int i = 0; i < sz; i++ )
            {
                val.push_back( T() );
                *this >> val[ i ];
            }
            return *this;
        }

        template < typename T, typename U >
        Deserializer& operator>>( FlatMap< T, U >& val )
        {
            unsigned int sz;
            *this >> sz;
            ensureSufficientBytesRemaining( sz * 2 ); // Expect at least two bytes per map entry

            std::vector< std::pair< T, U > > tmpData( sz );
            for ( unsigned int i = 0; i < sz; i++ )
            {
                *this >> tmpData[ i ].first >> tmpData[ i ].second;
            }
            val = FlatMap< T, U >( boost::container::ordered_unique_range_t(), tmpData.begin(), tmpData.end() );
            return *this;
        }

        template < typename T, typename U >
        Deserializer& operator>>( std::map< T, U >& val )
        {
            unsigned int sz;
            *this >> sz;
            ensureSufficientBytesRemaining( size_t( sz ) * 2 ); // Expect at least two bytes per map entry

            for ( unsigned int i = 0; i < sz; i++ )
            {
                T key;
                U value;
                *this >> key >> value;
                val[ key ] = std::move( value );
            }
            return *this;
        }

        template < typename T, typename U >
        Deserializer& operator>>( std::unordered_map< T, U >& val )
        {
            unsigned int sz;
            *this >> sz;
            ensureSufficientBytesRemaining( sz * 2 ); // Expect at least two bytes per map entry

            for ( unsigned int i = 0; i < sz; i++ )
            {
                T key;
                U value;
                *this >> key >> value;
                val[ key ] = std::move( value );
            }
            return *this;
        }

        template < typename T >
        Deserializer& operator>>( std::set< T >& val )
        {
            unsigned int sz;
            *this >> sz;
            ensureSufficientBytesRemaining( sz ); // Expect at least one byte per set entry

            val.clear();
            for ( unsigned int i = 0; i < sz; i++ )
            {
                T v;
                *this >> v;
                val.insert( std::move( v ) );
            }
            return *this;
        }

        template < typename T >
        Deserializer& operator>>( Vector2D< T >& val )
        {
            *this >> val.x;
            *this >> val.y;
            return *this;
        }

        template < typename T >
        Deserializer& operator>>( Vector3D< T >& val )
        {
            *this >> val.x;
            *this >> val.y;
            *this >> val.z;
            return *this;
        }

        template < typename T >
        Deserializer& operator>>( Vector4D< T >& val )
        {
            *this >> val.x;
            *this >> val.y;
            *this >> val.z;
            *this >> val.w;
            return *this;
        }

        template < typename T >
        Deserializer& operator>>( Colour4< T >& val )
        {
            *this >> val.r;
            *this >> val.g;
            *this >> val.b;
            *this >> val.a;
            return *this;
        }

        template < typename T >
        Deserializer& operator>>( Rect2D< T >& val )
        {
            Vector2D< T > p1, p2;
            *this >> p1;
            *this >> p2;
            val = Rect2D< T >( p1, p2 );
            return *this;
        }

        template < typename T, typename U >
        Deserializer& operator>>( std::pair< T, U >& p )
        {
            return *this >> p.first >> p.second;
        }

        template < typename T >
        Deserializer& operator>>( Range< T >& p )
        {
            return *this >> p.start >> p.end;
        }

        template < typename T >
        Deserializer& operator>>( std::optional< T >& p )
        {
            bool present;
            *this >> present;
            if ( present )
            {
                T tmp;
                *this >> tmp;
                p = tmp;
            }
            else
            {
                p = std::optional< T >();
            }
            return *this;
        }

        template < typename T, std::enable_if_t< std::is_enum< T >::value == true, int > = 0 >
        Deserializer& operator>>( T& val )
        {
            typename std::underlying_type< T >::type tmp;
            *this >> tmp;
            val = T( tmp );
            return *this;
        }

        template < typename T, std::enable_if_t< std::is_enum< T >::value == false, int > = 0 >
        Deserializer& operator>>( T& val )
        {
            val.deserialize( *this );
            return *this;
        }

        template < typename T >
        void peek( T& val )
        {
            const auto oldPos = pos;
            *this >> val;
            pos = oldPos;
        }

        size_t getPosition() const { return pos; }

    private:
        size_t pos = 0;
        gsl::span< const gsl::byte > src;

        template < typename T >
        Deserializer& deserializePod( T& val )
        {
            ensureSufficientBytesRemaining( sizeof( T ) );
            memcpy( &val, src.data() + pos, sizeof( T ) );
            pos += sizeof( T );
            return *this;
        }

        template < typename T >
        Deserializer& deserializeInteger( T& val )
        {
            if ( options.version >= 1 )
            {
                // Variable-length
                bool sign;
                uint64_t temp;
                deserializeVariableInteger( temp, sign, std::is_signed_v< T > );
                if ( sign )
                {
                    int64_t signedTemp = -int64_t( temp ) - 1;
                    val = static_cast< T >( signedTemp );
                }
                else
                {
                    val = static_cast< T >( temp );
                }
                return *this;
            }
            else
            {
                // Fixed length
                return deserializePod( val );
            }
        }

        void deserializeVariableInteger( uint64_t& val, bool& sign, bool isSigned );

        void ensureSufficientBytesRemaining( size_t bytes );
        size_t getBytesRemaining() const;
    };
} // namespace Halley


#pragma once

#include <imageview/IsPixelFormat.h>
#include <imageview/internal/ImageViewStorage.h>

#include <gsl/span>

namespace imageview {
namespace detail {

template <class PixelFormat>
class PixelRef {
 public:
  static_assert(IsPixelFormat<PixelFormat>::value, "Not a PixelFormat.");

  using color_type = typename PixelFormat::color_type;

  constexpr PixelRef(gsl::span<std::byte, PixelFormat::kBytesPerPixel> pixel_data,
                     std::reference_wrapper<const PixelFormat> pixel_format);

  constexpr PixelRef(const PixelRef& other) = default;
  constexpr PixelRef(PixelRef&&) = default;
  ~PixelRef() = default;

  // Implicit conversion to color_type.
  // \return the color of the referenced pixel.
  constexpr operator color_type() const;

  // Assigns the specified color to the referenced pixel.
  // \param color - color to assign.
  // \return *this.
  constexpr PixelRef& operator=(const color_type& color);

  // Assigns the specified color to the referenced pixel.
  // Note: PixelRef has reference semantics: copy and move assignment operators change the value of the
  // referenced pixel rather than PixelRef object itself.
  constexpr PixelRef& operator=(const PixelRef& other);
  constexpr PixelRef& operator=(PixelRef&& other);

 private:
  // TODO: store PixelFormat by reference if it's not eligible for empty base optimization,
  // otherwise by value (as an empty base).
  detail::ImageViewStorage<PixelFormat, true> storage_;
};

template <class PixelFormat>
constexpr PixelRef<PixelFormat>::PixelRef(gsl::span<std::byte, PixelFormat::kBytesPerPixel> pixel_data,
                                          std::reference_wrapper<const PixelFormat> pixel_format)
    : storage_(pixel_data.data(), pixel_format.get()) {}

template <class PixelFormat>
constexpr PixelRef<PixelFormat>::operator color_type() const {
  constexpr std::size_t kBytesPerPixel = PixelFormat::kBytesPerPixel;
  const gsl::span<const std::byte, kBytesPerPixel> pixel_data(storage_.data_, kBytesPerPixel);
  return storage_.pixelFormat().read(pixel_data);
}

template <class PixelFormat>
constexpr PixelRef<PixelFormat>& PixelRef<PixelFormat>::operator=(const color_type& color) {
  constexpr std::size_t kBytesPerPixel = PixelFormat::kBytesPerPixel;
  const gsl::span<std::byte, kBytesPerPixel> pixel_data(storage_.data_, kBytesPerPixel);
  storage_.pixelFormat().write(color, pixel_data);
  return *this;
}

template <class PixelFormat>
constexpr PixelRef<PixelFormat>& PixelRef<PixelFormat>::operator=(const PixelRef& other) {
  // TODO: In theory, we could just memcpy() the binary data. However, if PixelFormat is stateful,
  // and the state of our PixelFormat differs from the state of @other, then simply copying the binary
  // data might lead to the wrong result.
  // Possible workaround: add a constexpr if for the case when PixelFormat is an empty class.
  return *this = static_cast<color_type>(other);
}

template <class PixelFormat>
constexpr PixelRef<PixelFormat>& PixelRef<PixelFormat>::operator=(PixelRef&& other) {
  return *this = static_cast<color_type>(other);
}

}  // namespace detail
}  // namespace imageview


#pragma once

#include <imageview/IsPixelFormat.h>
#include <imageview/internal/ImageViewIterator.h>
#include <imageview/internal/ImageViewStorage.h>
#include <imageview/internal/PixelRef.h>

#include <gsl/assert>
#include <gsl/span>

#include <cstddef>
#include <stdexcept>

namespace imageview {

template <class PixelFormat, bool Mutable = false>
class ImageRowView {
 public:
  static_assert(IsPixelFormat<PixelFormat>::value, "Not a PixelFormat.");

  using byte_type = std::conditional_t<Mutable, std::byte, const std::byte>;
  using value_type = typename PixelFormat::color_type;
  // TODO: consider using 'const value_type' instead of 'value_type' for immutable views.
  using reference = std::conditional_t<Mutable, detail::PixelRef<PixelFormat>, value_type>;

  // Constant LegacyInputIterator whose value_type is @value_type. The type satisfies all
  // requirements of LegacyRandomAccessIterator except the multipass guarantee: for dereferenceable iterators a and b
  // with a == b, there is no requirement that *a and *b are bound to the same object.
  using const_iterator = detail::ImageViewIterator<PixelFormat, false>;
  using iterator = detail::ImageViewIterator<PixelFormat, Mutable>;

  // Constructs an empty view.
  // This constructor is only available if PixelFormat is default-constructible.
  template <class Enable = std::enable_if_t<std::is_default_constructible_v<PixelFormat>>>
  constexpr ImageRowView() noexcept(noexcept(std::is_nothrow_default_constructible_v<PixelFormat>)){};

  // Constructs an empty view.
  // \param pixel_format - instance of PixelFormat to use.
  constexpr explicit ImageRowView(const PixelFormat& pixel_format) noexcept(
      noexcept(std::is_nothrow_copy_constructible_v<PixelFormat>));

  // Constructs an empty view.
  // \param pixel_format - instance of PixelFormat to use.
  constexpr explicit ImageRowView(PixelFormat&& pixel_format) noexcept(
      noexcept(std::is_nothrow_move_constructible_v<PixelFormat>));

  // Constructs a flat view into the given bitmap.
  // This constructor is only available if PixelFormat is default-constructible.
  // \param data - bitmap data.
  // \param width - the number of pixels in the bitmap.
  //
  // Expects(data.size() == width * PixelFormat::kBytesPerPixel)
  template <class Enable = std::enable_if_t<std::is_default_constructible_v<PixelFormat>>>
  constexpr ImageRowView(gsl::span<byte_type> data,
                         std::size_t width) noexcept(noexcept(std::is_nothrow_default_constructible_v<PixelFormat>));

  // Constructs a flat view into the given bitmap.
  // \param data - bitmap data.
  // \param width - the number of pixels in the bitmap.
  // \param pixel_format - instance of PixelFormat to use.
  //
  // Expects(data.size() == width * PixelFormat::kBytesPerPixel)
  constexpr ImageRowView(gsl::span<byte_type> data, std::size_t width, const PixelFormat& pixel_format) noexcept(
      noexcept(std::is_nothrow_copy_constructible_v<PixelFormat>));

  // Constructs a flat view into the given bitmap.
  // \param data - bitmap data.
  // \param width - the number of pixels in the bitmap.
  // \param pixel_format - instance of PixelFormat to use.
  //
  // Expects(data.size() == width * PixelFormat::kBytesPerPixel)
  constexpr ImageRowView(gsl::span<byte_type> data, std::size_t width, PixelFormat&& pixel_format) noexcept(
      noexcept(std::is_nothrow_move_constructible_v<PixelFormat>));

  // Construct a read-only view from a mutable view.
  template <class Enable = std::enable_if_t<!Mutable>>
  constexpr ImageRowView(ImageRowView<PixelFormat, !Mutable> row_view);

  // Returns the pixel format used by this image.
  constexpr const PixelFormat& pixelFormat() const noexcept;

  // Returns the bitmap data.
  constexpr gsl::span<byte_type> data() const noexcept;

  // Returns the total number of pixels in this view.
  constexpr std::size_t size() const noexcept;

  // Returns true if the view is empty (i.e. has 0 pixels), false otherwise.
  constexpr bool empty() const noexcept;

  // Returns the size of the referenced bitmap in bytes, i.e.
  //   size() * PixelFormat::kBytesPerPixel
  constexpr std::size_t size_bytes() const noexcept;

  // Returns an iterator to the first pixel.
  constexpr iterator begin() const;

  // Returns a const iterator to the first pixel.
  constexpr const_iterator cbegin() const;

  // Returns an iterator past the last pixel.
  constexpr iterator end() const;

  // Returns a const iterator past the last pixel.
  constexpr const_iterator cend() const;

  // Returns the color of the specified pixel.
  // \param index - 0-based index of the pixel. No bounds checking is performed - the behavior is undefined if @index is
  // outside [0; size()).
  constexpr reference operator[](std::size_t index) const;

  // Returns the color of the specified pixel.
  // \param index - 0-based index of the pixel.
  // \throw std::out_of_range if @index is outside [0; size()).
  constexpr reference at(std::size_t index) const;

 private:
  detail::ImageViewStorage<PixelFormat, Mutable> storage_;
  std::size_t width_ = 0;
};

template <class PixelFormat, bool Mutable>
constexpr ImageRowView<PixelFormat, Mutable>::ImageRowView(const PixelFormat& pixel_format) noexcept(
    noexcept(std::is_nothrow_copy_constructible_v<PixelFormat>))
    : storage_(nullptr, pixel_format) {}

template <class PixelFormat, bool Mutable>
constexpr ImageRowView<PixelFormat, Mutable>::ImageRowView(PixelFormat&& pixel_format) noexcept(
    noexcept(std::is_nothrow_move_constructible_v<PixelFormat>))
    : storage_(nullptr, std::move(pixel_format)) {}

template <class PixelFormat, bool Mutable>
template <class Enable>
constexpr ImageRowView<PixelFormat, Mutable>::ImageRowView(gsl::span<byte_type> data, std::size_t width) noexcept(
    noexcept(std::is_nothrow_default_constructible_v<PixelFormat>))
    : storage_(data.data()), width_(width) {
  Expects(data.size() == width * PixelFormat::kBytesPerPixel);
}

template <class PixelFormat, bool Mutable>
constexpr ImageRowView<PixelFormat, Mutable>::ImageRowView(
    gsl::span<byte_type> data, std::size_t width,
    const PixelFormat& pixel_format) noexcept(noexcept(std::is_nothrow_copy_constructible_v<PixelFormat>))
    : storage_(data.data(), pixel_format), width_(width) {
  Expects(data.size() == width * PixelFormat::kBytesPerPixel);
}

template <class PixelFormat, bool Mutable>
constexpr ImageRowView<PixelFormat, Mutable>::ImageRowView(
    gsl::span<byte_type> data, std::size_t width,
    PixelFormat&& pixel_format) noexcept(noexcept(std::is_nothrow_move_constructible_v<PixelFormat>))
    : storage_(data.data(), std::move(pixel_format)), width_(width) {
  Expects(data.size() == width * PixelFormat::kBytesPerPixel);
}

template <class PixelFormat, bool Mutable>
template <class Enable>
constexpr ImageRowView<PixelFormat, Mutable>::ImageRowView(ImageRowView<PixelFormat, !Mutable> row_view)
    : ImageRowView(row_view.data(), row_view.size(), row_view.pixelFormat()) {}

template <class PixelFormat, bool Mutable>
constexpr const PixelFormat& ImageRowView<PixelFormat, Mutable>::pixelFormat() const noexcept {
  return storage_.pixelFormat();
}

template <class PixelFormat, bool Mutable>
constexpr auto ImageRowView<PixelFormat, Mutable>::data() const noexcept -> gsl::span<byte_type> {
  return gsl::span<byte_type>(storage_.data(), size_bytes());
}

template <class PixelFormat, bool Mutable>
constexpr std::size_t ImageRowView<PixelFormat, Mutable>::size() const noexcept {
  return width_;
}

template <class PixelFormat, bool Mutable>
constexpr bool ImageRowView<PixelFormat, Mutable>::empty() const noexcept {
  return width_ == 0;
}

template <class PixelFormat, bool Mutable>
constexpr std::size_t ImageRowView<PixelFormat, Mutable>::size_bytes() const noexcept {
  return width_ * PixelFormat::kBytesPerPixel;
}

template <class PixelFormat, bool Mutable>
constexpr auto ImageRowView<PixelFormat, Mutable>::begin() const -> iterator {
  return iterator(storage_.data_, storage_.pixelFormat());
}

template <class PixelFormat, bool Mutable>
constexpr auto ImageRowView<PixelFormat, Mutable>::cbegin() const -> const_iterator {
  return const_iterator(storage_.data_, storage_.pixelFormat());
}

template <class PixelFormat, bool Mutable>
constexpr auto ImageRowView<PixelFormat, Mutable>::end() const -> iterator {
  return iterator(storage_.data_ + width_ * PixelFormat::kBytesPerPixel, storage_.pixelFormat());
}

template <class PixelFormat, bool Mutable>
constexpr auto ImageRowView<PixelFormat, Mutable>::cend() const -> const_iterator {
  return const_iterator(storage_.data_ + width_ * PixelFormat::kBytesPerPixel, storage_.pixelFormat());
}

template <class PixelFormat, bool Mutable>
constexpr auto ImageRowView<PixelFormat, Mutable>::operator[](std::size_t index) const -> reference {
  const gsl::span<byte_type, PixelFormat::kBytesPerPixel> pixel_data(
      storage_.data() + index * PixelFormat::kBytesPerPixel, PixelFormat::kBytesPerPixel);
  if constexpr (Mutable) {
    return detail::PixelRef<PixelFormat>(pixel_data, pixelFormat());
  } else {
    return pixelFormat().read(pixel_data);
  }
}

template <class PixelFormat, bool Mutable>
constexpr auto ImageRowView<PixelFormat, Mutable>::at(std::size_t index) const -> reference {
  if (index >= width_) {
    throw std::out_of_range("ImageRowView::at(): attempting to access an element out of range.");
  }
  return (*this)[index];
}

}  // namespace imageview


#pragma once

#include <d3d11.h>
#include <DirectXMath.h>
#include <map>
#include <gsl\gsl>
#include "DrawableGameComponent.h"
#include "GaussianBlur.h"
#include "FullScreenRenderTarget.h"
#include "FullScreenQuad.h"
#include <winrt\Windows.Foundation.h>

namespace Library
{
	class FullScreenQuadMaterial;

	struct BloomSettings
	{
		float BloomThreshold{ 0.45f };
		float BlurAmount{ 2.0f };
		float BloomIntensity{ 1.25f };
		float BloomSaturation{ 1.0f };
		float SceneIntensity{ 1.0f };
		float SceneSaturation{ 1.0f };
	};

	enum class BloomDrawModes
	{
		Normal = 0,
		GlowMap,
		BlurredGlowMap,
		End
	};

	class Bloom final : public DrawableGameComponent
	{
		RTTI_DECLARATIONS(Bloom, DrawableGameComponent)

	public:
		Bloom(Game& game, const BloomSettings& bloomSettings = DefaultBloomSettings);
		Bloom(const Bloom&) = delete;
		Bloom(Bloom&&) = default;
		Bloom& operator=(const Bloom&) = delete;
		Bloom& operator=(Bloom&&) = default;
		~Bloom() = default;

		ID3D11ShaderResourceView* SceneTexture() const;
		void SetSceneTexture(winrt::com_ptr<ID3D11ShaderResourceView> sceneTexture);

		const BloomSettings& GetBloomSettings() const;
		void SetBloomSettings(const BloomSettings& bloomSettings);

		BloomDrawModes DrawMode() const;
		const std::string& DrawModeString() const;
		void SetDrawMode(BloomDrawModes drawMode);

		virtual void Initialize() override;
		virtual void Draw(const GameTime& gameTime) override;

		inline static const BloomSettings DefaultBloomSettings{ 0.45f, 2.0f, 1.25f, 1.0f, 1.0f, 1.0f };

	private:
		static const std::map<BloomDrawModes, std::string> DrawModeDisplayNames;

		struct PixelCBufferPerObject
		{
			float BloomThreshold;
			float BloomIntensity;
			float BloomSaturation;
			float SceneIntensity;
			float SceneSaturation;
			DirectX::XMFLOAT3 Padding;

			PixelCBufferPerObject(const BloomSettings& bloomSettings) :
				BloomThreshold(bloomSettings.BloomThreshold), BloomIntensity(bloomSettings.BloomIntensity),
				BloomSaturation(bloomSettings.BloomSaturation), SceneIntensity(bloomSettings.SceneIntensity),
				SceneSaturation(bloomSettings.SceneSaturation), Padding()
			{
			}

			PixelCBufferPerObject& operator=(const BloomSettings& bloomSettings)
			{
				BloomThreshold = bloomSettings.BloomThreshold;
				BloomIntensity = bloomSettings.BloomIntensity;
				BloomSaturation = bloomSettings.BloomSaturation;
				SceneIntensity = bloomSettings.SceneIntensity;
				SceneSaturation = bloomSettings.SceneSaturation;

				return *this;
			}
		};

		enum class BloomShaderClass
		{
			Extract = 0,
			Composite,
			NoBloom
		};

		void DrawNormal(const GameTime& gameTime);
		void DrawGlowMap(const GameTime& gameTime);
		void DrawBlurredGlowMap(const GameTime& gameTime);

		FullScreenQuad mFullScreenQuad;
		FullScreenRenderTarget mRenderTarget;
		GaussianBlur mGaussianBlur;
		winrt::com_ptr<ID3D11ShaderResourceView> mSceneTexture;
		winrt::com_ptr<ID3D11Buffer> mPixelCBufferPerObject;
		PixelCBufferPerObject mPixelCBufferPerObjectData;
		BloomDrawModes mDrawMode{ BloomDrawModes::Normal };
		std::map<BloomDrawModes, std::function<void(const GameTime& gameTime)>> mDrawFunctions;
		std::map<BloomShaderClass, winrt::com_ptr<ID3D11ClassInstance>> mShaderClassInstances;
		BloomSettings mBloomSettings;
	};
}


#ifndef PROTO_H_
#define PROTO_H_

#define _DEFAULT_SOURCE 1

/** PROTO SETTINGS **/
#define PRECISION 2
#define MAX_LINE_LENGTH 9046
#define USE_CBLAS
#define USE_LAPACK
/* #define USE_CERES */
#define USE_STB_IMAGE

#define WARN_UNUSED __attribute__((warn_unused_result))

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <unistd.h>
#include <dirent.h>
#include <assert.h>
#include <sys/time.h>

#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/poll.h>

#ifdef USE_CBLAS
#include <cblas.h>
#endif

#ifdef USE_LAPACK
#include <lapacke.h>
#endif

#ifdef USE_CERES
#include <ceres/c_api.h>
#endif

/******************************************************************************
 * MACROS
 ******************************************************************************/

/**
 * Mark variable unused.
 * @param[in] expr Variable to mark as unused
 */
#define UNUSED(expr)                                                           \
  do {                                                                         \
    (void) (expr);                                                             \
  } while (0)

/**
 * Check if condition is satisfied.
 *
 * If the condition is not satisfied a message M will be logged and a goto
 * error is called.
 *
 * @param[in] A Condition to be checked
 * @param[in] M Error message
 * @param[in] ... Varadic arguments for error message
 */
#define CHECK(A, M, ...)                                                       \
  if (!(A)) {                                                                  \
    LOG_ERROR(M, ##__VA_ARGS__);                                               \
    goto error;                                                                \
  }

/******************************************************************************
 * LOGGING
 ******************************************************************************/

/** Terminal ANSI colors */
#define KRED "\x1B[1;31m"
#define KGRN "\x1B[1;32m"
#define KYEL "\x1B[1;33m"
#define KBLU "\x1B[1;34m"
#define KMAG "\x1B[1;35m"
#define KCYN "\x1B[1;36m"
#define KWHT "\x1B[1;37m"
#define KNRM "\x1B[1;0m"

/** Macro function that returns the caller's filename */
#define __FILENAME__                                                           \
  (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)

/**
 * Debug
 * @param[in] M Message
 * @param[in] ... Varadic arguments
 */
#ifdef NDEBUG
#define DEBUG(M, ...)
#else
#define DEBUG(M, ...) fprintf(stdout, "[DEBUG] " M "\n", ##__VA_ARGS__)
#endif

/**
 * Log info
 * @param[in] M Message
 * @param[in] ... Varadic arguments
 */
#define LOG_INFO(M, ...)                                                       \
  fprintf(stderr,                                                              \
          "[INFO] [%s:%d] " M "\n",                                            \
          __FILENAME__,                                                        \
          __LINE__,                                                            \
          ##__VA_ARGS__)

/**
 * Log error
 * @param[in] M Message
 * @param[in] ... Varadic arguments
 */
#define LOG_ERROR(M, ...)                                                      \
  fprintf(stderr,                                                              \
          KRED "[ERROR] [%s:%d] " M KNRM "\n",                                 \
          __FILENAME__,                                                        \
          __LINE__,                                                            \
          ##__VA_ARGS__)

/**
 * Log warn
 * @param[in] M Message
 * @param[in] ... Varadic arguments
 */
#define LOG_WARN(M, ...)                                                       \
  fprintf(stderr,                                                              \
          KYEL "[WARN] [%s:%d] " M KNRM "\n",                                  \
          __FILENAME__,                                                        \
          __LINE__,                                                            \
          ##__VA_ARGS__)

/**
 * Fatal
 *
 * @param[in] M Message
 * @param[in] ... Varadic arguments
 */
#define FATAL(M, ...)                                                          \
  fprintf(stdout,                                                              \
          KRED "[FATAL] [%s:%d] " M KNRM "\n",                                 \
          __FILENAME__,                                                        \
          __LINE__,                                                            \
          ##__VA_ARGS__);                                                      \
  exit(-1)

/******************************************************************************
 * FILESYSTEM
 ******************************************************************************/

void path_file_name(const char *path, char *fname);
void path_file_ext(const char *path, char *fext);
void path_dir_name(const char *path, char *dir_name);
char *path_join(const char *x, const char *y);
char **list_files(const char *path, int *nb_files);
void list_files_free(char **data, const int n);
char *file_read(const char *fp);
void skip_line(FILE *fp);
int file_exists(const char *fp);
int file_rows(const char *fp);
int file_copy(const char *src, const char *dest);

/******************************************************************************
 * DATA
 ******************************************************************************/

#if PRECISION == 1
typedef float real_t;
#elif PRECISION == 2
typedef double real_t;
#else
#error "Precision not defined!"
#endif

size_t string_copy(char *dst, const char *src);
void string_cat(char *dst, const char *src);
char *string_malloc(const char *s);
int **load_iarrays(const char *csv_path, int *nb_arrays);
real_t **load_darrays(const char *csv_path, int *nb_arrays);

int dsv_rows(const char *fp);
int dsv_cols(const char *fp, const char delim);
char **dsv_fields(const char *fp, const char delim, int *nb_fields);
real_t **dsv_data(const char *fp, const char delim, int *nb_rows, int *nb_cols);
void dsv_free(real_t **data, const int nb_rows);

real_t **csv_data(const char *fp, int *nb_rows, int *nb_cols);
void csv_free(real_t **data, const int nb_rows);

/* real_t *load_matrix(const char *file_path); */
/* real_t *load_vector(const char *file_path); */

/******************************************************************************
 * TIME
 ******************************************************************************/

/** Timestamp Type */
typedef uint64_t timestamp_t;

struct timespec tic();
float toc(struct timespec *tic);
float mtoc(struct timespec *tic);
timestamp_t time_now();

real_t ts2sec(const timestamp_t ts);
timestamp_t sec2ts(const real_t time_s);

/******************************************************************************
 * NETWORK
 ******************************************************************************/

/**
 * TCP server
 */
typedef struct tcp_server_t {
  int port;
  int sockfd;
  int conn;
  void *(*conn_handler)(void *);
} tcp_server_t;

/**
 * TCP client
 */
typedef struct tcp_client_t {
  char server_ip[1024];
  int server_port;
  int sockfd;
  int (*loop_cb)(struct tcp_client_t *);
} tcp_client_t;

int ip_port_info(const int sockfd, char *ip, int *port);

int tcp_server_setup(tcp_server_t *server, const int port);
int tcp_server_loop(tcp_server_t *server);

int tcp_client_setup(tcp_client_t *client,
                     const char *server_ip,
                     const int server_port);
int tcp_client_loop(tcp_client_t *client);

/******************************************************************************
 * MATHS
 ******************************************************************************/

/** Mathematical Pi constant (i.e. 3.1415..) */
#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif

/** Real number comparison tolerance */
#ifndef CMP_TOL
#define CMP_TOL 1e-6
#endif

/** Min of two numbers, X or Y. */
#define MIN(x, y) ((x) < (y) ? (x) : (y))

/** Max of two numbers, X or Y. */
#define MAX(x, y) ((x) > (y) ? (x) : (y))

/** Based on sign of b, return +ve or -ve a. */
#define SIGN2(a, b) ((b) >= 0.0 ? fabs(a) : -fabs(a))

float randf(float a, float b);
real_t deg2rad(const real_t d);
real_t rad2deg(const real_t r);
int fltcmp(const real_t x, const real_t y);
int fltcmp2(const void *x, const void *y);
real_t pythag(const real_t a, const real_t b);
real_t lerp(const real_t a, const real_t b, const real_t t);
void lerp3(const real_t a[3], const real_t b[3], const real_t t, real_t x[3]);
real_t sinc(const real_t x);
real_t mean(const real_t *x, const size_t length);
real_t median(const real_t *x, const size_t length);
real_t var(const real_t *x, const size_t length);
real_t stddev(const real_t *x, const size_t length);

/******************************************************************************
 * LINEAR ALGEBRA
 ******************************************************************************/

void print_matrix(const char *prefix,
                  const real_t *A,
                  const size_t m,
                  const size_t n);
void print_vector(const char *prefix, const real_t *v, const size_t n);

void eye(real_t *A, const size_t m, const size_t n);
void ones(real_t *A, const size_t m, const size_t n);
void zeros(real_t *A, const size_t m, const size_t n);

real_t *mat_malloc(const size_t m, const size_t n);
int mat_cmp(const real_t *A, const real_t *B, const size_t m, const size_t n);
int mat_equals(const real_t *A,
               const real_t *B,
               const size_t m,
               const size_t n,
               const real_t tol);
int mat_save(const char *save_path, const real_t *A, const int m, const int n);
real_t *mat_load(const char *save_path, int *nb_rows, int *nb_cols);
void mat_set(real_t *A,
             const size_t stride,
             const size_t i,
             const size_t j,
             const real_t val);
real_t
mat_val(const real_t *A, const size_t stride, const size_t i, const size_t j);
void mat_copy(const real_t *src, const int m, const int n, real_t *dest);
void mat_row_set(real_t *A,
                 const size_t stride,
                 const int row_idx,
                 const real_t *x);
void mat_col_set(real_t *A,
                 const size_t stride,
                 const int nb_rows,
                 const int col_idx,
                 const real_t *x);
void mat_block_get(const real_t *A,
                   const size_t stride,
                   const size_t rs,
                   const size_t cs,
                   const size_t re,
                   const size_t ce,
                   real_t *block);
void mat_block_set(real_t *A,
                   const size_t stride,
                   const size_t rs,
                   const size_t cs,
                   const size_t re,
                   const size_t ce,
                   const real_t *block);
void mat_diag_get(const real_t *A, const int m, const int n, real_t *d);
void mat_diag_set(real_t *A, const int m, const int n, const real_t *d);
void mat_triu(const real_t *A, const size_t n, real_t *U);
void mat_tril(const real_t *A, const size_t n, real_t *L);
real_t mat_trace(const real_t *A, const size_t m, const size_t n);
void mat_transpose(const real_t *A, size_t m, size_t n, real_t *A_t);
void mat_add(const real_t *A, const real_t *B, real_t *C, size_t m, size_t n);
void mat_sub(const real_t *A, const real_t *B, real_t *C, size_t m, size_t n);
void mat_scale(real_t *A, const size_t m, const size_t n, const real_t scale);

real_t *vec_malloc(const size_t n);
void vec_copy(const real_t *src, const size_t n, real_t *dest);
int vec_equals(const real_t *x, const real_t *y, const size_t n);
void vec_add(const real_t *x, const real_t *y, real_t *z, size_t n);
void vec_sub(const real_t *x, const real_t *y, real_t *z, size_t n);
void vec_scale(real_t *x, const size_t n, const real_t scale);
real_t vec_norm(const real_t *x, const size_t n);
void vec_normalize(real_t *x, const size_t n);

void dot(const real_t *A,
         const size_t A_m,
         const size_t A_n,
         const real_t *B,
         const size_t B_m,
         const size_t B_n,
         real_t *C);
void skew(const real_t x[3], real_t A[3 * 3]);
void skew_inv(const real_t A[3 * 3], real_t x[3]);
void fwdsubs(const real_t *L, const real_t *b, real_t *y, const size_t n);
void bwdsubs(const real_t *U, const real_t *y, real_t *x, const size_t n);
int check_jacobian(const char *jac_name,
                   const real_t *fdiff,
                   const real_t *jac,
                   const size_t m,
                   const size_t n,
                   const real_t tol,
                   const int verbose);

#ifdef USE_CBLAS
void cblas_dot(const real_t *A,
               const size_t A_m,
               const size_t A_n,
               const real_t *B,
               const size_t B_m,
               const size_t B_n,
               real_t *C);
#endif

/******************************************************************************
 * SVD
 ******************************************************************************/

int svd(real_t *A, const int m, const int n, real_t *w, real_t *V);

#ifdef USE_LAPACK
void lapack_svd(real_t *A, int m, int n, real_t **S, real_t **U, real_t **V_t);
#endif

/******************************************************************************
 * CHOL
 ******************************************************************************/

void chol(const real_t *A, const size_t n, real_t *L);
void chol_solve(const real_t *A, const real_t *b, real_t *x, const size_t n);

#ifdef USE_LAPACK
void lapack_chol_solve(const real_t *A,
                       const real_t *b,
                       real_t *x,
                       const size_t n);
#endif

/******************************************************************************
 * TRANSFORMS
 ******************************************************************************/

void tf(const real_t params[7], real_t T[4 * 4]);
void tf_vector(const real_t T[4 * 4], real_t params[7]);
void tf_decompose(const real_t T[4 * 4], real_t C[3 * 3], real_t r[3]);
void tf_rot_set(real_t T[4 * 4], const real_t C[3 * 3]);
void tf_rot_get(const real_t T[4 * 4], real_t C[3 * 3]);
void tf_quat_set(real_t T[4 * 4], const real_t q[4]);
void tf_quat_get(const real_t T[4 * 4], real_t q[4]);
void tf_euler_set(real_t T[4 * 4], const real_t ypr[3]);
void tf_euler_get(const real_t T[4 * 4], real_t ypr[3]);
void tf_trans_set(real_t T[4 * 4], const real_t r[3]);
void tf_trans_get(const real_t T[4 * 4], real_t r[3]);
void tf_inv(const real_t T[4 * 4], real_t T_inv[4 * 4]);
void tf_point(const real_t T[4 * 4], const real_t p[3], real_t retval[3]);
void tf_hpoint(const real_t T[4 * 4], const real_t p[4], real_t retval[4]);
void tf_perturb_rot(real_t T[4 * 4], const real_t step_size, const int i);
void tf_perturb_trans(real_t T[4 * 4], const real_t step_size, const int i);
void print_pose_vector(const char *prefix, const real_t pose[7]);
void rvec2rot(const real_t *rvec, const real_t eps, real_t *R);
void euler321(const real_t ypr[3], real_t C[3 * 3]);
void euler2quat(const real_t ypr[3], real_t q[4]);
void rot2quat(const real_t C[3 * 3], real_t q[4]);
void rot2euler(const real_t C[3 * 3], real_t ypr[3]);
void quat2euler(const real_t q[4], real_t ypr[3]);
void quat2rot(const real_t q[4], real_t C[3 * 3]);
real_t quat_norm(const real_t q[4]);
void quat_normalize(real_t q[4]);
void quat_inv(const real_t q[4], real_t q_inv[4]);
void quat_left(const real_t q[4], real_t left[4 * 4]);
void quat_right(const real_t q[4], real_t right[4 * 4]);
void quat_lmul(const real_t p[4], const real_t q[4], real_t r[4]);
void quat_rmul(const real_t p[4], const real_t q[4], real_t r[4]);
void quat_mul(const real_t p[4], const real_t q[4], real_t r[4]);
void quat_delta(const real_t dalpha[3], real_t dq[4]);
void quat_perturb(real_t q[4], const int i, const real_t h);

/******************************************************************************
 * Lie
 ******************************************************************************/

void lie_Exp(const real_t phi[3], real_t C[3 * 3]);
void lie_Log(const real_t C[3 * 3], real_t rvec[3]);

/******************************************************************************
 * CV
 ******************************************************************************/

// IMAGE ///////////////////////////////////////////////////////////////////////

typedef struct image_t {
  int width;
  int height;
  int channels;
  uint8_t *data;
} image_t;

void image_setup(image_t *img,
                 const int width,
                 const int height,
                 uint8_t *data);
image_t *image_load(const char *file_path);
void image_print_properties(const image_t *img);
void image_free(image_t *img);

// GEOMETRY ////////////////////////////////////////////////////////////////////

void linear_triangulation(const real_t P_i[3 * 4],
                          const real_t P_j[3 * 4],
                          const real_t z_i[2],
                          const real_t z_j[2],
                          real_t p[3]);

// RADTAN //////////////////////////////////////////////////////////////////////

void radtan4_distort(const real_t params[4], const real_t p[2], real_t p_d[2]);
void radtan4_point_jacobian(const real_t params[4],
                            const real_t p[2],
                            real_t J_point[2 * 2]);
void radtan4_params_jacobian(const real_t params[4],
                             const real_t p[2],
                             real_t J_param[2 * 4]);

// EQUI ////////////////////////////////////////////////////////////////////////

void equi4_distort(const real_t params[4], const real_t p[2], real_t p_d[2]);
void equi4_point_jacobian(const real_t params[4],
                          const real_t p[2],
                          real_t J_point[2 * 2]);
void equi4_params_jacobian(const real_t params[4],
                           const real_t p[2],
                           real_t J_param[2 * 4]);

// PINHOLE /////////////////////////////////////////////////////////////////////

real_t pinhole_focal(const int image_width, const real_t fov);
void pinhole_K(const real_t params[4], real_t K[3 * 3]);
void pinhole_projection_matrix(const real_t params[4],
                               const real_t T[4 * 4],
                               real_t P[3 * 4]);
void pinhole_project(const real_t params[4], const real_t p_C[3], real_t z[2]);
void pinhole_point_jacobian(const real_t params[4], real_t J_point[2 * 2]);
void pinhole_params_jacobian(const real_t params[4],
                             const real_t x[2],
                             real_t J[2 * 4]);

// PINHOLE-RADTAN4 /////////////////////////////////////////////////////////////

void pinhole_radtan4_project(const real_t params[8],
                             const real_t p_C[3],
                             real_t x[2]);
void pinhole_radtan4_project_jacobian(const real_t params[8],
                                      const real_t p_C[3],
                                      real_t J[2 * 3]);
void pinhole_radtan4_params_jacobian(const real_t params[8],
                                     const real_t p_C[3],
                                     real_t J[2 * 8]);

// PINHOLE-EQUI4 ///////////////////////////////////////////////////////////////

void pinhole_equi4_project(const real_t params[8],
                           const real_t p_C[3],
                           real_t x[2]);
void pinhole_equi4_project_jacobian(const real_t params[8],
                                    const real_t p_C[3],
                                    real_t J[2 * 3]);
void pinhole_equi4_params_jacobian(const real_t params[8],
                                   const real_t p_C[3],
                                   real_t J[2 * 8]);

/******************************************************************************
 * SENSOR FUSION
 ******************************************************************************/

#define POSE_PARAM 1
#define SB_PARAM 2
#define FEATURE_PARAM 3
#define EXTRINSICS_PARAM 4
#define CAM_PARAM 5

// POSE ////////////////////////////////////////////////////////////////////////

typedef struct pose_t {
  timestamp_t ts;
  real_t pos[3];
  real_t quat[4];
} pose_t;

void pose_setup(pose_t *pose, const timestamp_t ts, const real_t *param);
void pose_print(const char *prefix, const pose_t *pose);

// SPEED AND BIASES ////////////////////////////////////////////////////////////

typedef struct speed_biases_t {
  timestamp_t ts;
  real_t data[9];
} speed_biases_t;

void speed_biases_setup(speed_biases_t *sb,
                        const timestamp_t ts,
                        const real_t *param);
void speed_biases_print(const speed_biases_t *sb);

// FEATURE /////////////////////////////////////////////////////////////////////

#define MAX_FEATURES 10000

typedef struct feature_t {
  real_t data[3];
} feature_t;

void feature_setup(feature_t *p, const real_t *param);
void feature_print(const feature_t *feature);

typedef struct features_t {
  feature_t data[MAX_FEATURES];
  int nb_features;
  int status[MAX_FEATURES];
} features_t;

void features_setup(features_t *features);
int features_exists(const features_t *features, const int feature_id);
feature_t *features_get(features_t *features, const int feature_id);
feature_t *features_add(features_t *features,
                        const int feature_id,
                        const real_t *param);
void features_remove(features_t *features, const int feature_id);

// EXTRINSICS //////////////////////////////////////////////////////////////////

typedef struct extrinsics_t {
  real_t pos[3];
  real_t quat[4];
} extrinsics_t;

void extrinsics_setup(extrinsics_t *extrinsics, const real_t *param);
void extrinsics_print(const char *prefix, const extrinsics_t *exts);

// CAMERA PARAMS ///////////////////////////////////////////////////////////////

typedef struct camera_params_t {
  int cam_idx;
  int resolution[2];
  char proj_model[20];
  char dist_model[20];
  real_t data[8];
} camera_params_t;

void camera_params_setup(camera_params_t *camera,
                         const int cam_idx,
                         const int cam_res[2],
                         const char *proj_model,
                         const char *dist_model,
                         const real_t *data);
void camera_params_print(const camera_params_t *camera);

// POSE FACTOR /////////////////////////////////////////////////////////////////

typedef struct pose_factor_t {
  real_t pos_meas[3];
  real_t quat_meas[4];
  pose_t *pose_est;
  int nb_params;

  real_t covar[6 * 6];
  real_t sqrt_info[6 * 6];
} pose_factor_t;

void pose_factor_setup(pose_factor_t *factor,
                       pose_t *pose,
                       const real_t var[6]);
int pose_factor_eval(pose_factor_t *factor,
                     real_t **params,
                     real_t *residuals,
                     real_t **jacobians);
int pose_factor_ceres_eval(void *factor,
                           double **params,
                           double *residuals,
                           double **jacobians);

// BA FACTOR ///////////////////////////////////////////////////////////////////

typedef struct ba_factor_t {
  const pose_t *pose;
  const camera_params_t *camera;
  const feature_t *feature;
  int nb_params;

  real_t covar[2 * 2];
  real_t sqrt_info[2 * 2];
  real_t z[2];
} ba_factor_t;

void ba_factor_setup(ba_factor_t *factor,
                     const pose_t *pose,
                     const feature_t *feature,
                     const camera_params_t *camera,
                     const real_t z[2],
                     const real_t var[2]);
int ba_factor_eval(ba_factor_t *factor,
                   real_t **params,
                   real_t *residuals,
                   real_t **jacobians);
int ba_factor_ceres_eval(void *factor,
                         double **params,
                         double *residuals,
                         double **jacobians);

// CAMERA FACTOR ///////////////////////////////////////////////////////////////

typedef struct cam_factor_t {
  const pose_t *pose;
  const extrinsics_t *extrinsics;
  const camera_params_t *camera;
  const feature_t *feature;
  int nb_params;

  real_t covar[2 * 2];
  real_t sqrt_info[2 * 2];
  real_t z[2];
} cam_factor_t;

void cam_factor_setup(cam_factor_t *factor,
                      const pose_t *pose,
                      const extrinsics_t *extrinsics,
                      const feature_t *feature,
                      const camera_params_t *camera,
                      const real_t z[2],
                      const real_t var[2]);
int cam_factor_eval(cam_factor_t *factor,
                    real_t **params,
                    real_t *residuals,
                    real_t **jacobians);
int cam_factor_ceres_eval(void *factor,
                          double **params,
                          double *residuals,
                          double **jacobians);

// IMU FACTOR //////////////////////////////////////////////////////////////////

#define MAX_IMU_BUF_SIZE 10000

typedef struct imu_params_t {
  uint64_t param_id;
  int imu_idx;
  real_t rate;

  real_t n_aw;
  real_t n_gw;
  real_t n_a;
  real_t n_g;
  real_t g;
} imu_params_t;

typedef struct imu_buf_t {
  timestamp_t ts[MAX_IMU_BUF_SIZE];
  real_t acc[MAX_IMU_BUF_SIZE][3];
  real_t gyr[MAX_IMU_BUF_SIZE][3];
  int size;
} imu_buf_t;

typedef struct imu_factor_t {
  imu_params_t *imu_params;
  imu_buf_t imu_buf;
  pose_t *pose_i;
  pose_t *pose_j;
  speed_biases_t *sb_i;
  speed_biases_t *sb_j;

  real_t covar[15 * 15];
  real_t r[15];
  int r_size;

  real_t J0[2 * 6]; /* Jacobian w.r.t pose i */
  real_t J1[2 * 9]; /* Jacobian w.r.t speed and biases i */
  real_t J2[2 * 6]; /* Jacobian w.r.t pose j */
  real_t J3[2 * 9]; /* Jacobian w.r.t speed and biases j */
  real_t *jacs[4];
  int nb_params;

  /* Preintegration variables */
  real_t Dt;
  real_t F[15 * 15]; /* State jacobian */
  real_t P[15 * 15]; /* State covariance */
  real_t Q[15 * 15]; /* Noise matrix */

  real_t dr[3];     /* Relative position */
  real_t dv[3];     /* Relative velocity */
  real_t dC[3 * 3]; /* Relative rotation */
  real_t ba[3];     /* Accel biase */
  real_t bg[3];     /* Gyro biase */

} imu_factor_t;

void imu_buf_setup(imu_buf_t *imu_buf);
void imu_buf_add(imu_buf_t *imu_buf,
                 const timestamp_t ts,
                 const real_t acc[3],
                 const real_t gyr[3]);
void imu_buf_clear(imu_buf_t *imu_buf);
void imu_buf_copy(const imu_buf_t *from, imu_buf_t *to);
void imu_buf_print(const imu_buf_t *imu_buf);

/* void imu_factor_setup(imu_factor_t *factor, */
/*                       imu_params_t *imu_params, */
/*                       imu_buf_t *imu_buf, */
/*                       pose_t *pose_i, */
/*                       speed_biases_t *sb_i, */
/*                       pose_t *pose_j, */
/*                       speed_biases_t *sb_j); */
void imu_factor_reset(imu_factor_t *factor);

// GRAPH ///////////////////////////////////////////////////////////////////////

#define MAX_NB_FACTORS 1000

#define POSE_FACTOR 1
#define BA_FACTOR 2
#define CAM_FACTOR 3
#define IMU_FACTOR 4

typedef struct keyframe_t {
  cam_factor_t *cam_factors;
  int nb_cam_factors;

  imu_factor_t *imu_factors;
  int nb_imu_factors;

  pose_t *pose;
} keyframe_t;

typedef struct graph_t {
  void *factors[MAX_NB_FACTORS];
  int nb_factors;
  int *factor_types;

  pose_t **poses;
  int nb_poses;

  const extrinsics_t *extrinsics;
  int nb_exts;

  camera_params_t **cam_params;
  int nb_cams;

  feature_t **features;
  int nb_features;

  real_t *H;
  real_t *g;
  real_t *x;
  int x_size;
  int r_size;
} graph_t;

void graph_setup(graph_t *graph);
void graph_print(graph_t *graph);
int graph_add_factor(graph_t *graph, void *factor, int factor_type);
int graph_eval(graph_t *graph);
void graph_optimize(graph_t *graph);

/******************************************************************************
 * DATASET
 ******************************************************************************/

pose_t *load_poses(const char *fp, int *nb_poses);
int **assoc_pose_data(pose_t *gnd_poses,
                      size_t nb_gnd_poses,
                      pose_t *est_poses,
                      size_t nb_est_poses,
                      double threshold,
                      size_t *nb_matches);

/******************************************************************************
 * SIM
 ******************************************************************************/

// SIM FEATURES ////////////////////////////////////////////////////////////////

typedef struct sim_features_t {
  real_t **features;
  int nb_features;
} sim_features_t;

sim_features_t *load_sim_features(const char *csv_path);
void free_sim_features(sim_features_t *features_data);

// SIM IMU DATA ////////////////////////////////////////////////////////////////

typedef struct sim_imu_data_t {
  real_t **data;
  int nb_measurements;
} sim_imu_data_t;

sim_imu_data_t *load_sim_imu_data(const char *csv_path);
void free_sim_imu_data(sim_imu_data_t *imu_data);

// SIM CAM DATA ////////////////////////////////////////////////////////////////

typedef struct sim_cam_frame_t {
  timestamp_t ts;
  int *feature_ids;
  real_t **keypoints;
  int nb_measurements;
} sim_cam_frame_t;

typedef struct sim_cam_data_t {
  sim_cam_frame_t **frames;
  int nb_frames;

  timestamp_t *ts;
  real_t **poses;
} sim_cam_data_t;

sim_cam_frame_t *load_sim_cam_frame(const char *csv_path);
void print_sim_cam_frame(sim_cam_frame_t *frame_data);
void free_sim_cam_frame(sim_cam_frame_t *frame_data);

sim_cam_data_t *load_sim_cam_data(const char *dir_path);
void free_sim_cam_data(sim_cam_data_t *cam_data);

#endif // _PROTO_H_


#pragma once

#define NOMINMAX

// Windows
#include <windows.h>
#include <winrt\Windows.Foundation.h>

// Standard
#include <string>
#include <sstream>

#if defined(DEBUG) || defined(_DEBUG)
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif

// Guidelines Support Library
#include <gsl\gsl>

// DirectX
#include <d3d11_4.h>
#include <dxgi1_6.h>
#include <DirectXMath.h>


/*
 * Copyright (c) 2020 Microsoft. All rights reserved.
 */

#pragma once

// a place for frequently used types, especially ones relied on for interfaces
// eg gsl::span<uint8_t> etc

#include "machine.h"

#include <vector>

#include <gsl/span>
#include <gsl/span_ext>

#include <gsl/util>

#include <type_traits> // for make_unsigned

template <typename T>
T RemoveUnsigned(typename std::make_unsigned<T>::type val) {
  return gsl::narrow_cast<T>(val);
}

template <typename T> typename std::make_unsigned<T>::type MakeUnsigned(T val) {
  return gsl::narrow_cast<typename std::make_unsigned<T>::type>(val);
}

// encapsulate some memory which may be written, use instead of a ptr, len
using MemRef = gsl::span<uint8_t>;
using MemRefSizeType = MemRef::size_type;

using SignedMemRef = gsl::span<char>;
using SignedMemRefSizeType = SignedMemRef::size_type;

using MemVector = std::vector<uint8_t>;

// encapsulate some read only memory
using CMemRef = gsl::span<const uint8_t>;
using CSignedMemRef = gsl::span<const char>;

// from any old class T make a span covering uint8_ts
// intended for the Params classes.
template <class T> MemRef MakeMemRef(T *p) noexcept {
  void *ptr = p;
  static_assert(sizeof(*p) != 1);
  return MemRef(static_cast<uint8_t *>(ptr), sizeof(*p));
}

template <class T> MemRef MakeMemRef(T *p, size_t len) noexcept {
  void *ptr = p;
  return MemRef(static_cast<uint8_t *>(ptr), len);
}

template <class T> CMemRef MakeCMemRef(const T *p, size_t len) noexcept {
  const void *ptr = p;
  return CMemRef(static_cast<const uint8_t *>(ptr), len);
}

template <class T> CMemRef MakeCMemRef(const T *p) noexcept {
  const void *ptr = p;
  return CMemRef(static_cast<const uint8_t *>(ptr), sizeof(*p));
}

template <class T, size_t TLength>
CMemRef MakeCMemRef2(const T (&p)[TLength]) noexcept {
  const void *ptr = p;
  return CMemRef(static_cast<const uint8_t *>(ptr), TLength);
}

template <class T> SignedMemRef MakeSignedMemRef(T *p) noexcept {
  void *ptr = p;
  static_assert(sizeof(*p) != 1);
  return SignedMemRef(static_cast<char *>(ptr), sizeof(*p));
}

template <class T> SignedMemRef MakeSignedMemRef(T *p, size_t len) noexcept {
  void *ptr = p;
  return SignedMemRef(static_cast<char *>(ptr), len);
}

template <class T> CSignedMemRef MakeCSignedMemRef(T *p) noexcept {
  const void *ptr = p;
  static_assert(sizeof(*p) != 1);
  return CSignedMemRef(static_cast<const char *>(ptr), sizeof(*p));
}

template <class T> CSignedMemRef MakeCSignedMemRef(T *p, size_t len) noexcept {
  const void *ptr = p;
  return CSignedMemRef(static_cast<const char *>(ptr), len);
}

// T will needs top be copyable
template <class T> T ReHydrate(CMemRef source) noexcept {
  const void *ptr = source.data();
  const T *tInPlace = static_cast<const T *>(ptr);
  T ret = *tInPlace;
  return ret;
}

// Work around C4686
using _FixedLengthString = std::array<char, 1024>;
class FixedLengthString : public _FixedLengthString {
  using _FixedLengthString::_FixedLengthString;
};

// helper for HexDump
void HexByte(uint8_t byte, char &c1, char &c2);
// hex dump a block of memory into another block of memory.
size_t HexDump(CMemRef src, SignedMemRef dst);

template <class T, int LEN> struct IsPODEtc {
  static constexpr bool value =
      sizeof(T) == LEN && std::is_pod<T>::value && std::is_trivial<T>::value &&
      std::is_trivially_copyable<T>::value && std::is_standard_layout<T>::value;
};

/*
    Compile time protection against objects being different between the
    host and HW.

    usage - put something like the following outside the class:

    static_assert(OkAsParam<MyClass, 666>::value);

*/

template <class T, int LEN> struct SizeIsOK {
  static constexpr bool value = sizeof(T) == LEN;
};

template <class T> struct LayoutIsOK {
  // These two checks fail for most of our types as they have user provided
  // default ctors or members with default initialisers.
  // static_assert(std::is_pod<T>::value);
  // static_assert(std::is_trivial<T>::value);

  static constexpr bool value =

      // but anything we can serialise by copying must be trivially copyable
      std::is_trivially_copyable<T>::value &&

      // and must be standard layout, ie if you can't pass it to a C program
      // what hope is there that you can pass it over the wire to the HW.
      std::is_standard_layout<T>::value;
};

template <class T, int LEN> struct OkAsParam {
  static constexpr bool value = SizeIsOK<T, LEN>::value && LayoutIsOK<T>::value;
};

// Thinking about labeling classes explicitly. TBC, ignore for now.

class CannotBeSerialsed {
public:
  constexpr bool canBeSerialised() const { return false; }
};

class CanBeSerialsed {
public:
  constexpr bool canBeSerialised() const { return true; }
};


/* block/gsl_block_uint.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_BLOCK_UINT_H__
#define __GSL_BLOCK_UINT_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_errno.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_uint_struct
{
  size_t size;
  unsigned int *data;
};

typedef struct gsl_block_uint_struct gsl_block_uint;

GSL_FUN gsl_block_uint *gsl_block_uint_alloc (const size_t n);
GSL_FUN gsl_block_uint *gsl_block_uint_calloc (const size_t n);
GSL_FUN void gsl_block_uint_free (gsl_block_uint * b);

GSL_FUN int gsl_block_uint_fread (FILE * stream, gsl_block_uint * b);
GSL_FUN int gsl_block_uint_fwrite (FILE * stream, const gsl_block_uint * b);
GSL_FUN int gsl_block_uint_fscanf (FILE * stream, gsl_block_uint * b);
GSL_FUN int gsl_block_uint_fprintf (FILE * stream, const gsl_block_uint * b, const char *format);

GSL_FUN int gsl_block_uint_raw_fread (FILE * stream, unsigned int * b, const size_t n, const size_t stride);
GSL_FUN int gsl_block_uint_raw_fwrite (FILE * stream, const unsigned int * b, const size_t n, const size_t stride);
GSL_FUN int gsl_block_uint_raw_fscanf (FILE * stream, unsigned int * b, const size_t n, const size_t stride);
GSL_FUN int gsl_block_uint_raw_fprintf (FILE * stream, const unsigned int * b, const size_t n, const size_t stride, const char *format);

GSL_FUN size_t gsl_block_uint_size (const gsl_block_uint * b);
GSL_FUN unsigned int * gsl_block_uint_data (const gsl_block_uint * b);

__END_DECLS

#endif /* __GSL_BLOCK_UINT_H__ */


/* vector/gsl_vector_complex_double.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_VECTOR_COMPLEX_DOUBLE_H__
#define __GSL_VECTOR_COMPLEX_DOUBLE_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_complex.h>
#include <gsl/gsl_check_range.h>
#include <gsl/gsl_vector_double.h>
#include <gsl/gsl_vector_complex.h>
#include <gsl/gsl_block_complex_double.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 size;
  size_t stride;
  double *data;
  gsl_block_complex *block;
  int owner;
} gsl_vector_complex;

typedef struct
{
  gsl_vector_complex vector;
} _gsl_vector_complex_view;

typedef _gsl_vector_complex_view gsl_vector_complex_view;

typedef struct
{
  gsl_vector_complex vector;
} _gsl_vector_complex_const_view;

typedef const _gsl_vector_complex_const_view gsl_vector_complex_const_view;

/* Allocation */

GSL_FUN gsl_vector_complex *gsl_vector_complex_alloc (const size_t n);
GSL_FUN gsl_vector_complex *gsl_vector_complex_calloc (const size_t n);

GSL_FUN gsl_vector_complex *
gsl_vector_complex_alloc_from_block (gsl_block_complex * b,
                                           const size_t offset,
                                           const size_t n,
                                           const size_t stride);

GSL_FUN gsl_vector_complex *
gsl_vector_complex_alloc_from_vector (gsl_vector_complex * v,
                                             const size_t offset,
                                             const size_t n,
                                             const size_t stride);

GSL_FUN void gsl_vector_complex_free (gsl_vector_complex * v);

/* Views */

GSL_FUN _gsl_vector_complex_view
gsl_vector_complex_view_array (double *base,
                                     size_t n);

GSL_FUN _gsl_vector_complex_view
gsl_vector_complex_view_array_with_stride (double *base,
                                                 size_t stride,
                                                 size_t n);

GSL_FUN _gsl_vector_complex_const_view
gsl_vector_complex_const_view_array (const double *base,
                                           size_t n);

GSL_FUN _gsl_vector_complex_const_view
gsl_vector_complex_const_view_array_with_stride (const double *base,
                                                       size_t stride,
                                                       size_t n);

GSL_FUN _gsl_vector_complex_view
gsl_vector_complex_subvector (gsl_vector_complex *base,
                                         size_t i,
                                         size_t n);

GSL_FUN _gsl_vector_complex_view
gsl_vector_complex_subvector_with_stride (gsl_vector_complex *v,
                                                size_t i,
                                                size_t stride,
                                                size_t n);

GSL_FUN _gsl_vector_complex_const_view
gsl_vector_complex_const_subvector (const gsl_vector_complex *base,
                                               size_t i,
                                               size_t n);

GSL_FUN _gsl_vector_complex_const_view
gsl_vector_complex_const_subvector_with_stride (const gsl_vector_complex *v,
                                                      size_t i,
                                                      size_t stride,
                                                      size_t n);

GSL_FUN _gsl_vector_view
gsl_vector_complex_real (gsl_vector_complex *v);

GSL_FUN _gsl_vector_view
gsl_vector_complex_imag (gsl_vector_complex *v);

GSL_FUN _gsl_vector_const_view
gsl_vector_complex_const_real (const gsl_vector_complex *v);

GSL_FUN _gsl_vector_const_view
gsl_vector_complex_const_imag (const gsl_vector_complex *v);

/* Operations */

GSL_FUN void gsl_vector_complex_set_zero (gsl_vector_complex * v);
GSL_FUN void gsl_vector_complex_set_all (gsl_vector_complex * v,
                                       gsl_complex z);
GSL_FUN int gsl_vector_complex_set_basis (gsl_vector_complex * v, size_t i);

GSL_FUN int gsl_vector_complex_fread (FILE * stream,
                                    gsl_vector_complex * v);
GSL_FUN int gsl_vector_complex_fwrite (FILE * stream,
                                     const gsl_vector_complex * v);
GSL_FUN int gsl_vector_complex_fscanf (FILE * stream,
                                     gsl_vector_complex * v);
GSL_FUN int gsl_vector_complex_fprintf (FILE * stream,
                                      const gsl_vector_complex * v,
                                      const char *format);

GSL_FUN int gsl_vector_complex_memcpy (gsl_vector_complex * dest, const gsl_vector_complex * src);

GSL_FUN int gsl_vector_complex_reverse (gsl_vector_complex * v);

GSL_FUN int gsl_vector_complex_swap (gsl_vector_complex * v, gsl_vector_complex * w);
GSL_FUN int gsl_vector_complex_swap_elements (gsl_vector_complex * v, const size_t i, const size_t j);

GSL_FUN int gsl_vector_complex_isnull (const gsl_vector_complex * v);
GSL_FUN int gsl_vector_complex_ispos (const gsl_vector_complex * v);
GSL_FUN int gsl_vector_complex_isneg (const gsl_vector_complex * v);
GSL_FUN int gsl_vector_complex_isnonneg (const gsl_vector_complex * v);

GSL_FUN int gsl_vector_complex_add (gsl_vector_complex * a, const gsl_vector_complex * b);
GSL_FUN int gsl_vector_complex_sub (gsl_vector_complex * a, const gsl_vector_complex * b);
GSL_FUN int gsl_vector_complex_mul (gsl_vector_complex * a, const gsl_vector_complex * b);
GSL_FUN int gsl_vector_complex_div (gsl_vector_complex * a, const gsl_vector_complex * b);
GSL_FUN int gsl_vector_complex_scale (gsl_vector_complex * a, const gsl_complex x);
GSL_FUN int gsl_vector_complex_add_constant (gsl_vector_complex * a, const gsl_complex x);

GSL_FUN INLINE_DECL gsl_complex gsl_vector_complex_get (const gsl_vector_complex * v, const size_t i);
GSL_FUN INLINE_DECL void gsl_vector_complex_set (gsl_vector_complex * v, const size_t i, gsl_complex z);
GSL_FUN INLINE_DECL gsl_complex *gsl_vector_complex_ptr (gsl_vector_complex * v, const size_t i);
GSL_FUN INLINE_DECL const gsl_complex *gsl_vector_complex_const_ptr (const gsl_vector_complex * v, const size_t i);

#ifdef HAVE_INLINE

INLINE_FUN
gsl_complex
gsl_vector_complex_get (const gsl_vector_complex * v,
                              const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      gsl_complex zero = {{0, 0}};
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, zero);
    }
#endif
  return *GSL_COMPLEX_AT (v, i);
}

INLINE_FUN
void
gsl_vector_complex_set (gsl_vector_complex * v,
                              const size_t i, gsl_complex z)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  *GSL_COMPLEX_AT (v, i) = z;
}

INLINE_FUN
gsl_complex *
gsl_vector_complex_ptr (gsl_vector_complex * v,
                              const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return GSL_COMPLEX_AT (v, i);
}

INLINE_FUN
const gsl_complex *
gsl_vector_complex_const_ptr (const gsl_vector_complex * v,
                                    const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return GSL_COMPLEX_AT (v, i);
}

#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_COMPLEX_DOUBLE_H__ */


/* vector/gsl_vector_long.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_VECTOR_LONG_H__
#define __GSL_VECTOR_LONG_H__

#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_block_long.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 size;
  size_t stride;
  long *data;
  gsl_block_long *block;
  int owner;
}
gsl_vector_long;

typedef struct
{
  gsl_vector_long vector;
} _gsl_vector_long_view;

typedef _gsl_vector_long_view gsl_vector_long_view;

typedef struct
{
  gsl_vector_long vector;
} _gsl_vector_long_const_view;

typedef const _gsl_vector_long_const_view gsl_vector_long_const_view;

/* Allocation */

gsl_vector_long *gsl_vector_long_alloc (const size_t n);
gsl_vector_long *gsl_vector_long_calloc (const size_t n);

gsl_vector_long *gsl_vector_long_alloc_from_block (gsl_block_long * b,
                                                     const size_t offset,
                                                     const size_t n,
                                                     const size_t stride);

gsl_vector_long *gsl_vector_long_alloc_from_vector (gsl_vector_long * v,
                                                      const size_t offset,
                                                      const size_t n,
                                                      const size_t stride);

void gsl_vector_long_free (gsl_vector_long * v);

/* Views */

_gsl_vector_long_view
gsl_vector_long_view_array (long *v, size_t n);

_gsl_vector_long_view
gsl_vector_long_view_array_with_stride (long *base,
                                         size_t stride,
                                         size_t n);

_gsl_vector_long_const_view
gsl_vector_long_const_view_array (const long *v, size_t n);

_gsl_vector_long_const_view
gsl_vector_long_const_view_array_with_stride (const long *base,
                                               size_t stride,
                                               size_t n);

_gsl_vector_long_view
gsl_vector_long_subvector (gsl_vector_long *v,
                            size_t i,
                            size_t n);

_gsl_vector_long_view
gsl_vector_long_subvector_with_stride (gsl_vector_long *v,
                                        size_t i,
                                        size_t stride,
                                        size_t n);

_gsl_vector_long_const_view
gsl_vector_long_const_subvector (const gsl_vector_long *v,
                                  size_t i,
                                  size_t n);

_gsl_vector_long_const_view
gsl_vector_long_const_subvector_with_stride (const gsl_vector_long *v,
                                              size_t i,
                                              size_t stride,
                                              size_t n);

/* Operations */

void gsl_vector_long_set_zero (gsl_vector_long * v);
void gsl_vector_long_set_all (gsl_vector_long * v, long x);
int gsl_vector_long_set_basis (gsl_vector_long * v, size_t i);

int gsl_vector_long_fread (FILE * stream, gsl_vector_long * v);
int gsl_vector_long_fwrite (FILE * stream, const gsl_vector_long * v);
int gsl_vector_long_fscanf (FILE * stream, gsl_vector_long * v);
int gsl_vector_long_fprintf (FILE * stream, const gsl_vector_long * v,
                              const char *format);

int gsl_vector_long_memcpy (gsl_vector_long * dest, const gsl_vector_long * src);

int gsl_vector_long_reverse (gsl_vector_long * v);

int gsl_vector_long_swap (gsl_vector_long * v, gsl_vector_long * w);
int gsl_vector_long_swap_elements (gsl_vector_long * v, const size_t i, const size_t j);

long gsl_vector_long_max (const gsl_vector_long * v);
long gsl_vector_long_min (const gsl_vector_long * v);
void gsl_vector_long_minmax (const gsl_vector_long * v, long * min_out, long * max_out);

size_t gsl_vector_long_max_index (const gsl_vector_long * v);
size_t gsl_vector_long_min_index (const gsl_vector_long * v);
void gsl_vector_long_minmax_index (const gsl_vector_long * v, size_t * imin, size_t * imax);

int gsl_vector_long_add (gsl_vector_long * a, const gsl_vector_long * b);
int gsl_vector_long_sub (gsl_vector_long * a, const gsl_vector_long * b);
int gsl_vector_long_mul (gsl_vector_long * a, const gsl_vector_long * b);
int gsl_vector_long_div (gsl_vector_long * a, const gsl_vector_long * b);
int gsl_vector_long_scale (gsl_vector_long * a, const double x);
int gsl_vector_long_add_constant (gsl_vector_long * a, const double x);

int gsl_vector_long_equal (const gsl_vector_long * u,
                            const gsl_vector_long * v);

int gsl_vector_long_isnull (const gsl_vector_long * v);
int gsl_vector_long_ispos (const gsl_vector_long * v);
int gsl_vector_long_isneg (const gsl_vector_long * v);
int gsl_vector_long_isnonneg (const gsl_vector_long * v);

INLINE_DECL long gsl_vector_long_get (const gsl_vector_long * v, const size_t i);
INLINE_DECL void gsl_vector_long_set (gsl_vector_long * v, const size_t i, long x);
INLINE_DECL long * gsl_vector_long_ptr (gsl_vector_long * v, const size_t i);
INLINE_DECL const long * gsl_vector_long_const_ptr (const gsl_vector_long * v, const size_t i);

#ifdef HAVE_INLINE

INLINE_FUN
long
gsl_vector_long_get (const gsl_vector_long * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VAL ("index out of range", GSL_EINVAL, 0);
    }
#endif
  return v->data[i * v->stride];
}

INLINE_FUN
void
gsl_vector_long_set (gsl_vector_long * v, const size_t i, long x)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_VOID ("index out of range", GSL_EINVAL);
    }
#endif
  v->data[i * v->stride] = x;
}

INLINE_FUN
long *
gsl_vector_long_ptr (gsl_vector_long * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (long *) (v->data + i * v->stride);
}

INLINE_FUN
const long *
gsl_vector_long_const_ptr (const gsl_vector_long * v, const size_t i)
{
#if GSL_RANGE_CHECK
  if (GSL_RANGE_COND(i >= v->size))
    {
      GSL_ERROR_NULL ("index out of range", GSL_EINVAL);
    }
#endif
  return (const long *) (v->data + i * v->stride);
}
#endif /* HAVE_INLINE */

__END_DECLS

#endif /* __GSL_VECTOR_LONG_H__ */