#include <cmath>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <ostream>

struct format_hex
{
    uint64_t value;
    const uint32_t width;
    const char fill;

    format_hex(uint64_t val, uint32_t width_=4, char fill_='0')
            : value(val),
              width(width_),
              fill(fill_)
    {
    }

    friend inline std::ostream& operator<<(
            std::ostream& os,
            const format_hex& rhs)
    {
        return os << std::hex << std::setfill(rhs.fill) << std::setw(rhs.width)
                  << std::uppercase << static_cast<long>(rhs.value) << std::dec;
    }
};


/**
 * Stream version of hex bytes inline little endian
 */
struct HexBytesInlineLE
{
    const uint8_t* data;
    double numBytes;
    const char* sep;

    HexBytesInlineLE(const char* data_, double numBytes_, const char* sep_=" ")
            : data(reinterpret_cast<const uint8_t*>(data_)), numBytes(numBytes_), sep(sep_)
    {
    }

    HexBytesInlineLE(const uint8_t* data_, double numBytes_, const char* sep_=" ")
            : data(data_), numBytes(numBytes_), sep(sep_)
    {
    }

    friend inline std::ostream& operator<<(
            std::ostream& os,
            const HexBytesInlineLE& rhs)
    {
        for (int32_t idx = rhs.numBytes - 1; idx > -1; --idx)
        {
            os << format_hex(rhs.data[idx], 2) << ((idx <= (rhs.numBytes - 1)) ? rhs.sep : "");
        }

        return os;
    }
};


/**
 * Stream version of hex bytes inline big endian
 */
struct HexBytesInlineBE
{
    const uint8_t* data;
    double numBytes;
    const char* sep;

    HexBytesInlineBE(const char* data_, double numBytes_, const char* sep_=" ")
            : data(reinterpret_cast<const uint8_t*>(data_)), numBytes(numBytes_), sep(sep_)
    {
    }

    HexBytesInlineBE(const uint8_t* data_, double numBytes_, const char* sep_=" ")
            : data(data_), numBytes(numBytes_), sep(sep_)
    {
    }

    friend inline std::ostream& operator<<(
            std::ostream& os,
            const HexBytesInlineBE& rhs)
    {
        for (uint32_t idx = 0; idx < rhs.numBytes; ++idx)
        {
            os << format_hex(rhs.data[idx], 2) << ((idx < (rhs.numBytes - 1)) ? rhs.sep : "");
        }

        return os;
    }
};
