code:
/**
    @file CRC.h
    @author Daniel Bahr
    @version 0.2.0.6
    @copyright
    @parblock
        CRC++
        Copyright (c) 2016, Daniel Bahr
        All rights reserved.

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

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

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

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

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

/*
    CRC++ can be configured by setting various #defines before #including this header file:

        #define crcpp_uint8                             - Specifies the type used to store CRCs that have a width of 8 bits or less.
                                                          This type is not used in CRC calculations. Defaults to ::std::uint8_t.
        #define crcpp_uint16                            - Specifies the type used to store CRCs that have a width between 9 and 16 bits (inclusive).
                                                          This type is not used in CRC calculations. Defaults to ::std::uint16_t.
        #define crcpp_uint32                            - Specifies the type used to store CRCs that have a width between 17 and 32 bits (inclusive).
                                                          This type is not used in CRC calculations. Defaults to ::std::uint32_t.
        #define crcpp_uint64                            - Specifies the type used to store CRCs that have a width between 33 and 64 bits (inclusive).
                                                          This type is not used in CRC calculations. Defaults to ::std::uint64_t.
        #define crcpp_size                              - This type is used for loop iteration and function signatures only. Defaults to ::std::size_t.
        #define CRCPP_USE_NAMESPACE                     - Define to place all CRC++ code within the ::CRCPP namespace.
        #define CRCPP_BRANCHLESS                        - Define to enable a branchless CRC implementation. The branchless implementation uses a single integer
                                                          multiplication in the bit-by-bit calculation instead of a small conditional. The branchless implementation
                                                          may be faster on processor architectures which support single-instruction integer multiplication.
        #define CRCPP_USE_CPP11                         - Define to enables C++11 features (move semantics, constexpr, static_assert, etc.).
        #define CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS  - Define to include definitions for little-used CRCs.
*/

#ifndef CRCPP_CRC_H_
#define CRCPP_CRC_H_

#include <climits>  // Includes CHAR_BIT
#ifdef CRCPP_USE_CPP11
#include <cstddef>  // Includes ::std::size_t
#include <cstdint>  // Includes ::std::uint8_t, ::std::uint16_t, ::std::uint32_t, ::std::uint64_t
#else
#include <stddef.h> // Includes size_t
#include <stdint.h> // Includes uint8_t, uint16_t, uint32_t, uint64_t
#endif
#include <limits>   // Includes ::std::numeric_limits
#include <utility>  // Includes ::std::move

#ifndef crcpp_uint8
#   ifdef CRCPP_USE_CPP11
        /// @brief Unsigned 8-bit integer definition, used primarily for parameter definitions.
#       define crcpp_uint8 ::std::uint8_t
#   else
        /// @brief Unsigned 8-bit integer definition, used primarily for parameter definitions.
#       define crcpp_uint8 uint8_t
#   endif
#endif

#ifndef crcpp_uint16
#   ifdef CRCPP_USE_CPP11
        /// @brief Unsigned 16-bit integer definition, used primarily for parameter definitions.
#       define crcpp_uint16 ::std::uint16_t
#   else
        /// @brief Unsigned 16-bit integer definition, used primarily for parameter definitions.
#       define crcpp_uint16 uint16_t
#   endif
#endif

#ifndef crcpp_uint32
#   ifdef CRCPP_USE_CPP11
        /// @brief Unsigned 32-bit integer definition, used primarily for parameter definitions.
#       define crcpp_uint32 ::std::uint32_t
#   else
        /// @brief Unsigned 32-bit integer definition, used primarily for parameter definitions.
#       define crcpp_uint32 uint32_t
#   endif
#endif

#ifndef crcpp_uint64
#   ifdef CRCPP_USE_CPP11
        /// @brief Unsigned 64-bit integer definition, used primarily for parameter definitions.
#       define crcpp_uint64 ::std::uint64_t
#   else
        /// @brief Unsigned 64-bit integer definition, used primarily for parameter definitions.
#       define crcpp_uint64 uint64_t
#   endif
#endif

#ifndef crcpp_size
#   ifdef CRCPP_USE_CPP11
        /// @brief Unsigned size definition, used for specifying data sizes.
#       define crcpp_size ::std::size_t
#   else
        /// @brief Unsigned size definition, used for specifying data sizes.
#       define crcpp_size size_t
#   endif
#endif

#ifdef CRCPP_USE_CPP11
    /// @brief Compile-time expression definition.
#   define crcpp_constexpr constexpr
#else
    /// @brief Compile-time expression definition.
#   define crcpp_constexpr const
#endif

#ifdef CRCPP_USE_NAMESPACE
namespace CRCPP
{
#endif

/**
    @brief Static class for computing CRCs.
    @note This class supports computation of full and multi-part CRCs, using a bit-by-bit algorithm or a
        byte-by-byte lookup table. The CRCs are calculated using as many optimizations as is reasonable.
        If compiling with C++11, the constexpr keyword is used liberally so that many calculations are
        performed at compile-time instead of at runtime.
*/
class CRC
{
public:
    // Forward declaration
    template <typename CRCType, crcpp_uint16 CRCWidth>
    struct Table;

    /**
        @brief CRC parameters.
    */
    template <typename CRCType, crcpp_uint16 CRCWidth>
    struct Parameters
    {
        CRCType polynomial;   ///< CRC polynomial
        CRCType initialValue; ///< Initial CRC value
        CRCType finalXOR;     ///< Value to XOR with the final CRC
        bool reflectInput;    ///< true to reflect all input bytes
        bool reflectOutput;   ///< true to reflect the output CRC (reflection occurs before the final XOR)

        Table<CRCType, CRCWidth> MakeTable() const;
    };

    /**
        @brief CRC lookup table. After construction, the CRC parameters are fixed.
        @note A CRC table can be used for multiple CRC calculations.
    */
    template <typename CRCType, crcpp_uint16 CRCWidth>
    struct Table
    {
        // Constructors are intentionally NOT marked explicit.
        Table(const Parameters<CRCType, CRCWidth> & parameters);

#ifdef CRCPP_USE_CPP11
        Table(Parameters<CRCType, CRCWidth> && parameters);
#endif

        const Parameters<CRCType, CRCWidth> & GetParameters() const;

        const CRCType * GetTable() const;

        CRCType operator[](unsigned char index) const;

    private:
        void InitTable();

        Parameters<CRCType, CRCWidth> parameters; ///< CRC parameters used to construct the table
        CRCType table[1 << CHAR_BIT];             ///< CRC lookup table
    };

    // The number of bits in CRCType must be at least as large as CRCWidth.
    // CRCType must be an unsigned integer type or a custom type with operator overloads.
    template <typename CRCType, crcpp_uint16 CRCWidth>
    static CRCType Calculate(const void * data, crcpp_size size, const Parameters<CRCType, CRCWidth> & parameters);

    template <typename CRCType, crcpp_uint16 CRCWidth>
    static CRCType Calculate(const void * data, crcpp_size size, const Parameters<CRCType, CRCWidth> & parameters, CRCType crc);

    template <typename CRCType, crcpp_uint16 CRCWidth>
    static CRCType Calculate(const void * data, crcpp_size size, const Table<CRCType, CRCWidth> & lookupTable);

    template <typename CRCType, crcpp_uint16 CRCWidth>
    static CRCType Calculate(const void * data, crcpp_size size, const Table<CRCType, CRCWidth> & lookupTable, CRCType crc);

    // Common CRCs up to 64 bits.
    // Note: Check values are the computed CRCs when given an ASCII input of "123456789" (without null terminator)
#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
    static const Parameters< crcpp_uint8,  4> & CRC_4_ITU();
    static const Parameters< crcpp_uint8,  5> & CRC_5_EPC();
    static const Parameters< crcpp_uint8,  5> & CRC_5_ITU();
    static const Parameters< crcpp_uint8,  5> & CRC_5_USB();
    static const Parameters< crcpp_uint8,  6> & CRC_6_CDMA2000A();
    static const Parameters< crcpp_uint8,  6> & CRC_6_CDMA2000B();
    static const Parameters< crcpp_uint8,  6> & CRC_6_ITU();
    static const Parameters< crcpp_uint8,  7> & CRC_7();
#endif
    static const Parameters< crcpp_uint8,  8> & CRC_8();
#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
    static const Parameters< crcpp_uint8,  8> & CRC_8_EBU();
    static const Parameters< crcpp_uint8,  8> & CRC_8_MAXIM();
    static const Parameters< crcpp_uint8,  8> & CRC_8_WCDMA();
    static const Parameters<crcpp_uint16, 10> & CRC_10();
    static const Parameters<crcpp_uint16, 10> & CRC_10_CDMA2000();
    static const Parameters<crcpp_uint16, 11> & CRC_11();
    static const Parameters<crcpp_uint16, 12> & CRC_12_CDMA2000();
    static const Parameters<crcpp_uint16, 12> & CRC_12_DECT();
    static const Parameters<crcpp_uint16, 12> & CRC_12_UMTS();
    static const Parameters<crcpp_uint16, 13> & CRC_13_BBC();
    static const Parameters<crcpp_uint16, 15> & CRC_15();
    static const Parameters<crcpp_uint16, 15> & CRC_15_MPT1327();
#endif
    static const Parameters<crcpp_uint16, 16> & CRC_16_ARC();
    static const Parameters<crcpp_uint16, 16> & CRC_16_BUYPASS();
    static const Parameters<crcpp_uint16, 16> & CRC_16_CCITTFALSE();
#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
    static const Parameters<crcpp_uint16, 16> & CRC_16_CDMA2000();
    static const Parameters<crcpp_uint16, 16> & CRC_16_DECTR();
    static const Parameters<crcpp_uint16, 16> & CRC_16_DECTX();
    static const Parameters<crcpp_uint16, 16> & CRC_16_DNP();
#endif
    static const Parameters<crcpp_uint16, 16> & CRC_16_GENIBUS();
    static const Parameters<crcpp_uint16, 16> & CRC_16_KERMIT();
#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
    static const Parameters<crcpp_uint16, 16> & CRC_16_MAXIM();
    static const Parameters<crcpp_uint16, 16> & CRC_16_MODBUS();
    static const Parameters<crcpp_uint16, 16> & CRC_16_T10DIF();
    static const Parameters<crcpp_uint16, 16> & CRC_16_USB();
#endif
    static const Parameters<crcpp_uint16, 16> & CRC_16_X25();
    static const Parameters<crcpp_uint16, 16> & CRC_16_XMODEM();
#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
    static const Parameters<crcpp_uint32, 17> & CRC_17_CAN();
    static const Parameters<crcpp_uint32, 21> & CRC_21_CAN();
    static const Parameters<crcpp_uint32, 24> & CRC_24();
    static const Parameters<crcpp_uint32, 24> & CRC_24_FLEXRAYA();
    static const Parameters<crcpp_uint32, 24> & CRC_24_FLEXRAYB();
    static const Parameters<crcpp_uint32, 30> & CRC_30();
#endif
    static const Parameters<crcpp_uint32, 32> & CRC_32();
    static const Parameters<crcpp_uint32, 32> & CRC_32_BZIP2();
#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
    static const Parameters<crcpp_uint32, 32> & CRC_32_C();
#endif
    static const Parameters<crcpp_uint32, 32> & CRC_32_MPEG2();
    static const Parameters<crcpp_uint32, 32> & CRC_32_POSIX();
#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
    static const Parameters<crcpp_uint32, 32> & CRC_32_Q();
    static const Parameters<crcpp_uint64, 40> & CRC_40_GSM();
    static const Parameters<crcpp_uint64, 64> & CRC_64();
#endif

#ifdef CRCPP_USE_CPP11
    CRC() = delete;
    CRC(const CRC & other) = delete;
    CRC & operator=(const CRC & other) = delete;
    CRC(CRC && other) = delete;
    CRC & operator=(CRC && other) = delete;
#endif

private:
#ifndef CRCPP_USE_CPP11
    CRC();
    CRC(const CRC & other);
    CRC & operator=(const CRC & other);
#endif

    template <typename IntegerType>
    static IntegerType Reflect(IntegerType value, crcpp_uint16 numBits);

    template <typename CRCType, crcpp_uint16 CRCWidth>
    static CRCType Finalize(CRCType remainder, CRCType finalXOR, bool reflectOutput);

    template <typename CRCType, crcpp_uint16 CRCWidth>
    static CRCType UndoFinalize(CRCType remainder, CRCType finalXOR, bool reflectOutput);

    template <typename CRCType, crcpp_uint16 CRCWidth>
    static CRCType CalculateRemainder(const void * data, crcpp_size size, const Parameters<CRCType, CRCWidth> & parameters, CRCType remainder);

    template <typename CRCType, crcpp_uint16 CRCWidth>
    static CRCType CalculateRemainder(const void * data, crcpp_size size, const Table<CRCType, CRCWidth> & lookupTable, CRCType remainder);

    template <typename IntegerType>
    static crcpp_constexpr IntegerType BoundedConstexprValue(IntegerType x);
};

/**
    @brief Returns a CRC lookup table construct using these CRC parameters.
    @note This function primarily exists to allow use of the auto keyword instead of instantiating
        a table directly, since template parameters are not inferred in constructors.
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
    @return CRC lookup table
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline CRC::Table<CRCType, CRCWidth> CRC::Parameters<CRCType, CRCWidth>::MakeTable() const
{
    // This should take advantage of RVO and optimize out the copy.
    return CRC::Table<CRCType, CRCWidth>(*this);
}

/**
    @brief Constructs a CRC table from a set of CRC parameters
    @param[in] parameters CRC parameters
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline CRC::Table<CRCType, CRCWidth>::Table(const Parameters<CRCType, CRCWidth> & parameters) :
    parameters(parameters)
{
    InitTable();
}

#ifdef CRCPP_USE_CPP11
/**
    @brief Constructs a CRC table from a set of CRC parameters
    @param[in] parameters CRC parameters
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline CRC::Table<CRCType, CRCWidth>::Table(Parameters<CRCType, CRCWidth> && parameters) :
    parameters(::std::move(parameters))
{
    InitTable();
}
#endif

/**
    @brief Gets the CRC parameters used to construct the CRC table
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
    @return CRC parameters
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline const CRC::Parameters<CRCType, CRCWidth> & CRC::Table<CRCType, CRCWidth>::GetParameters() const
{
    return parameters;
}

/**
    @brief Gets the CRC table
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
    @return CRC table
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline const CRCType * CRC::Table<CRCType, CRCWidth>::GetTable() const
{
    return table;
}

/**
    @brief Gets an entry in the CRC table
    @param[in] index Index into the CRC table
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
    @return CRC table entry
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline CRCType CRC::Table<CRCType, CRCWidth>::operator[](unsigned char index) const
{
    return table[index];
}

/**
    @brief Initializes a CRC table.
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline void CRC::Table<CRCType, CRCWidth>::InitTable()
{
    // For masking off the bits for the CRC (in the event that the number of bits in CRCType is larger than CRCWidth)
    static crcpp_constexpr CRCType BIT_MASK((CRCType(1) << (CRCWidth - CRCType(1))) |
                                           ((CRCType(1) << (CRCWidth - CRCType(1))) - CRCType(1)));

    static crcpp_constexpr CRCType SHIFT(CRC::BoundedConstexprValue(CHAR_BIT - CRCWidth));

    CRCType crc;
    unsigned char byte = 0;

    // Loop over each dividend (each possible number storable in an unsigned char)
    do
    {
        crc = CRC::CalculateRemainder<CRCType, CRCWidth>(&byte, sizeof(byte), parameters, CRCType(0));

        // This mask might not be necessary; all unit tests pass with this line commented out,
        // but that might just be a coincidence based on the CRC parameters used for testing.
        // In any case, this is harmless to leave in and only adds a single machine instruction per loop iteration.
        crc &= BIT_MASK;

        if (!parameters.reflectInput && CRCWidth < CHAR_BIT)
        {
            // Undo the special operation at the end of the CalculateRemainder()
            // function for non-reflected CRCs < CHAR_BIT.
            crc <<= SHIFT;
        }

        table[byte] = crc;
    }
    while (++byte);
}

/**
    @brief Computes a CRC.
    @param[in] data Data over which CRC will be computed
    @param[in] size Size of the data
    @param[in] parameters CRC parameters
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
    @return CRC
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Parameters<CRCType, CRCWidth> & parameters)
{
    CRCType remainder = CalculateRemainder(data, size, parameters, parameters.initialValue);

    // No need to mask the remainder here; the mask will be applied in the Finalize() function.

    return Finalize<CRCType, CRCWidth>(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput);
}
/**
    @brief Appends additional data to a previous CRC calculation.
    @note This function can be used to compute multi-part CRCs.
    @param[in] data Data over which CRC will be computed
    @param[in] size Size of the data
    @param[in] parameters CRC parameters
    @param[in] crc CRC from a previous calculation
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
    @return CRC
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Parameters<CRCType, CRCWidth> & parameters, CRCType crc)
{
    CRCType remainder = UndoFinalize<CRCType, CRCWidth>(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput);

    remainder = CalculateRemainder(data, size, parameters, remainder);

    // No need to mask the remainder here; the mask will be applied in the Finalize() function.

    return Finalize<CRCType, CRCWidth>(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput);
}

/**
    @brief Computes a CRC via a lookup table.
    @param[in] data Data over which CRC will be computed
    @param[in] size Size of the data
    @param[in] lookupTable CRC lookup table
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
    @return CRC
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Table<CRCType, CRCWidth> & lookupTable)
{
    const Parameters<CRCType, CRCWidth> & parameters = lookupTable.GetParameters();

    CRCType remainder = CalculateRemainder(data, size, lookupTable, parameters.initialValue);

    // No need to mask the remainder here; the mask will be applied in the Finalize() function.

    return Finalize<CRCType, CRCWidth>(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput);
}

/**
    @brief Appends additional data to a previous CRC calculation using a lookup table.
    @note This function can be used to compute multi-part CRCs.
    @param[in] data Data over which CRC will be computed
    @param[in] size Size of the data
    @param[in] lookupTable CRC lookup table
    @param[in] crc CRC from a previous calculation
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
    @return CRC
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline CRCType CRC::Calculate(const void * data, crcpp_size size, const Table<CRCType, CRCWidth> & lookupTable, CRCType crc)
{
    const Parameters<CRCType, CRCWidth> & parameters = lookupTable.GetParameters();

    CRCType remainder = UndoFinalize<CRCType, CRCWidth>(crc, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput);

    remainder = CalculateRemainder(data, size, lookupTable, remainder);

    // No need to mask the remainder here; the mask will be applied in the Finalize() function.

    return Finalize<CRCType, CRCWidth>(remainder, parameters.finalXOR, parameters.reflectInput != parameters.reflectOutput);
}

/**
    @brief Reflects (i.e. reverses the bits within) an integer value.
    @param[in] value Value to reflect
    @param[in] numBits Number of bits in the integer which will be reflected
    @tparam IntegerType Integer type of the value being reflected
    @return Reflected value
*/
template <typename IntegerType>
inline IntegerType CRC::Reflect(IntegerType value, crcpp_uint16 numBits)
{
    IntegerType reversedValue(0);

    for (crcpp_uint16 i = 0; i < numBits; ++i)
    {
        reversedValue = (reversedValue << 1) | (value & 1);
        value >>= 1;
    }

    return reversedValue;
}

/**
    @brief Computes the final reflection and XOR of a CRC remainder.
    @param[in] remainder CRC remainder to reflect and XOR
    @param[in] finalXOR Final value to XOR with the remainder
    @param[in] reflectOutput true to reflect each byte of the remainder before the XOR
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
    @return Final CRC
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline CRCType CRC::Finalize(CRCType remainder, CRCType finalXOR, bool reflectOutput)
{
    // For masking off the bits for the CRC (in the event that the number of bits in CRCType is larger than CRCWidth)
    static crcpp_constexpr CRCType BIT_MASK = (CRCType(1) << (CRCWidth - CRCType(1))) |
                                             ((CRCType(1) << (CRCWidth - CRCType(1))) - CRCType(1));

    if (reflectOutput)
    {
        remainder = Reflect(remainder, CRCWidth);
    }

    return (remainder ^ finalXOR) & BIT_MASK;
}

/**
    @brief Undoes the process of computing the final reflection and XOR of a CRC remainder.
    @note This function allows for computation of multi-part CRCs
    @note Calling UndoFinalize() followed by Finalize() (or vice versa) will always return the original remainder value:

        CRCType x = ...;
        CRCType y = Finalize(x, finalXOR, reflectOutput);
        CRCType z = UndoFinalize(y, finalXOR, reflectOutput);
        assert(x == z);

    @param[in] crc Reflected and XORed CRC
    @param[in] finalXOR Final value XORed with the remainder
    @param[in] reflectOutput true if the remainder is to be reflected
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
    @return Un-finalized CRC remainder
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline CRCType CRC::UndoFinalize(CRCType crc, CRCType finalXOR, bool reflectOutput)
{
    // For masking off the bits for the CRC (in the event that the number of bits in CRCType is larger than CRCWidth)
    static crcpp_constexpr CRCType BIT_MASK = (CRCType(1) << (CRCWidth - CRCType(1))) |
                                             ((CRCType(1) << (CRCWidth - CRCType(1))) - CRCType(1));

    crc = (crc & BIT_MASK) ^ finalXOR;

    if (reflectOutput)
    {
        crc = Reflect(crc, CRCWidth);
    }

    return crc;
}

/**
    @brief Computes a CRC remainder.
    @param[in] data Data over which the remainder will be computed
    @param[in] size Size of the data
    @param[in] parameters CRC parameters
    @param[in] remainder Running CRC remainder. Can be an initial value or the result of a previous CRC remainder calculation.
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
    @return CRC remainder
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline CRCType CRC::CalculateRemainder(const void * data, crcpp_size size, const Parameters<CRCType, CRCWidth> & parameters, CRCType remainder)
{
#ifdef CRCPP_USE_CPP11
    // This static_assert is put here because this function will always be compiled in no matter what
    // the template parameters are and whether or not a table lookup or bit-by-bit algorithm is used.
    static_assert(::std::numeric_limits<CRCType>::digits >= CRCWidth, "CRCType is too small to contain a CRC of width CRCWidth.");
#else
    // Catching this compile-time error is very important. Sadly, the compiler error will be very cryptic, but it's
    // better than nothing.
    enum { static_assert_failed_CRCType_is_too_small_to_contain_a_CRC_of_width_CRCWidth = 1 / (::std::numeric_limits<CRCType>::digits >= CRCWidth ? 1 : 0) };
#endif

    const unsigned char * current = reinterpret_cast<const unsigned char *>(data);

    // Slightly different implementations based on the parameters. The current implementations try to eliminate as much
    // computation from the inner loop (looping over each bit) as possible.
    if (parameters.reflectInput)
    {
        CRCType polynomial = CRC::Reflect(parameters.polynomial, CRCWidth);
        while (size--)
        {
            remainder ^= *current++;

            // An optimizing compiler might choose to unroll this loop.
            for (crcpp_size i = 0; i < CHAR_BIT; ++i)
            {
#ifdef CRCPP_BRANCHLESS
                // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following:
                // if (remainder & 1)
                //     remainder = (remainder >> 1) ^ polynomial;
                // else
                //     remainder >>= 1;
                remainder = (remainder >> 1) ^ ((remainder & 1) * polynomial);
#else
                remainder = (remainder & 1) ? ((remainder >> 1) ^ polynomial) : (remainder >> 1);
#endif
            }
        }
    }
    else if (CRCWidth >= CHAR_BIT)
    {
        static crcpp_constexpr CRCType CRC_WIDTH_MINUS_ONE(CRCWidth - CRCType(1));
#ifndef CRCPP_BRANCHLESS
        static crcpp_constexpr CRCType CRC_HIGHEST_BIT_MASK(CRCType(1) << CRC_WIDTH_MINUS_ONE);
#endif
        static crcpp_constexpr CRCType SHIFT(BoundedConstexprValue(CRCWidth - CHAR_BIT));

        while (size--)
        {
            remainder ^= (static_cast<CRCType>(*current++) << SHIFT);

            // An optimizing compiler might choose to unroll this loop.
            for (crcpp_size i = 0; i < CHAR_BIT; ++i)
            {
#ifdef CRCPP_BRANCHLESS
                // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following:
                // if (remainder & CRC_HIGHEST_BIT_MASK)
                //     remainder = (remainder << 1) ^ parameters.polynomial;
                // else
                //     remainder <<= 1;
                remainder = (remainder << 1) ^ (((remainder >> CRC_WIDTH_MINUS_ONE) & 1) * parameters.polynomial);
#else
                remainder = (remainder & CRC_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ parameters.polynomial) : (remainder << 1);
#endif
            }
        }
    }
    else
    {
        static crcpp_constexpr CRCType CHAR_BIT_MINUS_ONE(CHAR_BIT - 1);
#ifndef CRCPP_BRANCHLESS
        static crcpp_constexpr CRCType CHAR_BIT_HIGHEST_BIT_MASK(CRCType(1) << CHAR_BIT_MINUS_ONE);
#endif
        static crcpp_constexpr CRCType SHIFT(BoundedConstexprValue(CHAR_BIT - CRCWidth));

        CRCType polynomial = parameters.polynomial << SHIFT;
        remainder <<= SHIFT;

        while (size--)
        {
            remainder ^= *current++;

            // An optimizing compiler might choose to unroll this loop.
            for (crcpp_size i = 0; i < CHAR_BIT; ++i)
            {
#ifdef CRCPP_BRANCHLESS
                // Clever way to avoid a branch at the expense of a multiplication. This code is equivalent to the following:
                // if (remainder & CHAR_BIT_HIGHEST_BIT_MASK)
                //     remainder = (remainder << 1) ^ polynomial;
                // else
                //     remainder <<= 1;
                remainder = (remainder << 1) ^ (((remainder >> CHAR_BIT_MINUS_ONE) & 1) * polynomial);
#else
                remainder = (remainder & CHAR_BIT_HIGHEST_BIT_MASK) ? ((remainder << 1) ^ polynomial) : (remainder << 1);
#endif
            }
        }

        remainder >>= SHIFT;
    }

    return remainder;
}

/**
    @brief Computes a CRC remainder using lookup table.
    @param[in] data Data over which the remainder will be computed
    @param[in] size Size of the data
    @param[in] lookupTable CRC lookup table
    @param[in] remainder Running CRC remainder. Can be an initial value or the result of a previous CRC remainder calculation.
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
    @return CRC remainder
*/
template <typename CRCType, crcpp_uint16 CRCWidth>
inline CRCType CRC::CalculateRemainder(const void * data, crcpp_size size, const Table<CRCType, CRCWidth> & lookupTable, CRCType remainder)
{
    const unsigned char * current = reinterpret_cast<const unsigned char *>(data);

    if (lookupTable.GetParameters().reflectInput)
    {
        while (size--)
        {
#if defined(WIN32) || defined(_WIN32) || defined(WINCE)
    // Disable warning about data loss when doing (remainder >> CHAR_BIT) when
    // remainder is one byte long. The algorithm is still correct in this case,
    // though it's possible that one additional machine instruction will be executed.
#   pragma warning (push)
#   pragma warning (disable : 4333)
#endif
            remainder = (remainder >> CHAR_BIT) ^ lookupTable[static_cast<unsigned char>(remainder ^ *current++)];
#if defined(WIN32) || defined(_WIN32) || defined(WINCE)
#   pragma warning (pop)
#endif
        }
    }
    else if (CRCWidth >= CHAR_BIT)
    {
        static crcpp_constexpr CRCType SHIFT(BoundedConstexprValue(CRCWidth - CHAR_BIT));

        while (size--)
        {
            remainder = (remainder << CHAR_BIT) ^ lookupTable[static_cast<unsigned char>((remainder >> SHIFT) ^ *current++)];
        }
    }
    else
    {
        static crcpp_constexpr CRCType SHIFT(BoundedConstexprValue(CHAR_BIT - CRCWidth));

        remainder <<= SHIFT;

        while (size--)
        {
            // Note: no need to mask here since remainder is guaranteed to fit in a single byte.
            remainder = lookupTable[static_cast<unsigned char>(remainder ^ *current++)];
        }

        remainder >>= SHIFT;
    }

    return remainder;
}

/**
    @brief Function to force a compile-time expression to be >= 0.
    @note This function is used to avoid compiler warnings because all constexpr values are evaluated
        in a function even in a branch will never be executed. This also means we don't need pragmas
        to get rid of warnings, but it still can be computed at compile-time. Win-win!
    @param[in] x Compile-time expression to bound
    @tparam CRCType Integer type for storing the CRC result
    @tparam CRCWidth Number of bits in the CRC
    @return Non-negative compile-time expression
*/
template <typename IntegerType>
inline crcpp_constexpr IntegerType CRC::BoundedConstexprValue(IntegerType x)
{
    return (x < IntegerType(0)) ? IntegerType(0) : x;
}

#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
/**
    @brief Returns a set of parameters for CRC-4 ITU.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-4 ITU has the following parameters and check value:
        - polynomial     = 0x3
        - initial value  = 0x0
        - final XOR      = 0x0
        - reflect input  = true
        - reflect output = true
        - check value    = 0x7
    @return CRC-4 ITU parameters
*/
inline const CRC::Parameters<crcpp_uint8, 4> & CRC::CRC_4_ITU()
{
    static const Parameters<crcpp_uint8, 4> parameters = { 0x3, 0x0, 0x0, true, true };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-5 EPC.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-5 EPC has the following parameters and check value:
        - polynomial     = 0x09
        - initial value  = 0x09
        - final XOR      = 0x00
        - reflect input  = false
        - reflect output = false
        - check value    = 0x00
    @return CRC-5 EPC parameters
*/
inline const CRC::Parameters<crcpp_uint8, 5> & CRC::CRC_5_EPC()
{
    static const Parameters<crcpp_uint8, 5> parameters = { 0x09, 0x09, 0x00, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-5 ITU.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-5 ITU has the following parameters and check value:
        - polynomial     = 0x15
        - initial value  = 0x00
        - final XOR      = 0x00
        - reflect input  = true
        - reflect output = true
        - check value    = 0x07
    @return CRC-5 ITU parameters
*/
inline const CRC::Parameters<crcpp_uint8, 5> & CRC::CRC_5_ITU()
{
    static const Parameters<crcpp_uint8, 5> parameters = { 0x15, 0x00, 0x00, true, true };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-5 USB.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-5 USB has the following parameters and check value:
        - polynomial     = 0x05
        - initial value  = 0x1F
        - final XOR      = 0x1F
        - reflect input  = true
        - reflect output = true
        - check value    = 0x19
    @return CRC-5 USB parameters
*/
inline const CRC::Parameters<crcpp_uint8, 5> & CRC::CRC_5_USB()
{
    static const Parameters<crcpp_uint8, 5> parameters = { 0x05, 0x1F, 0x1F, true, true };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-6 CDMA2000-A.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-6 CDMA2000-A has the following parameters and check value:
        - polynomial     = 0x27
        - initial value  = 0x3F
        - final XOR      = 0x00
        - reflect input  = false
        - reflect output = false
        - check value    = 0x0D
    @return CRC-6 CDMA2000-A parameters
*/
inline const CRC::Parameters<crcpp_uint8, 6> & CRC::CRC_6_CDMA2000A()
{
    static const Parameters<crcpp_uint8, 6> parameters = { 0x27, 0x3F, 0x00, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-6 CDMA2000-B.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-6 CDMA2000-A has the following parameters and check value:
        - polynomial     = 0x07
        - initial value  = 0x3F
        - final XOR      = 0x00
        - reflect input  = false
        - reflect output = false
        - check value    = 0x3B
    @return CRC-6 CDMA2000-B parameters
*/
inline const CRC::Parameters<crcpp_uint8, 6> & CRC::CRC_6_CDMA2000B()
{
    static const Parameters<crcpp_uint8, 6> parameters = { 0x07, 0x3F, 0x00, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-6 ITU.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-6 ITU has the following parameters and check value:
        - polynomial     = 0x03
        - initial value  = 0x00
        - final XOR      = 0x00
        - reflect input  = true
        - reflect output = true
        - check value    = 0x06
    @return CRC-6 ITU parameters
*/
inline const CRC::Parameters<crcpp_uint8, 6> & CRC::CRC_6_ITU()
{
    static const Parameters<crcpp_uint8, 6> parameters = { 0x03, 0x00, 0x00, true, true };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-7 JEDEC.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-7 JEDEC has the following parameters and check value:
        - polynomial     = 0x09
        - initial value  = 0x00
        - final XOR      = 0x00
        - reflect input  = false
        - reflect output = false
        - check value    = 0x75
    @return CRC-7 JEDEC parameters
*/
inline const CRC::Parameters<crcpp_uint8, 7> & CRC::CRC_7()
{
    static const Parameters<crcpp_uint8, 7> parameters = { 0x09, 0x00, 0x00, false, false };
    return parameters;
}
#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS

/**
    @brief Returns a set of parameters for CRC-8 SMBus.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-8 SMBus has the following parameters and check value:
        - polynomial     = 0x07
        - initial value  = 0x00
        - final XOR      = 0x00
        - reflect input  = false
        - reflect output = false
        - check value    = 0xF4
    @return CRC-8 SMBus parameters
*/
inline const CRC::Parameters<crcpp_uint8, 8> & CRC::CRC_8()
{
    static const Parameters<crcpp_uint8, 8> parameters = { 0x07, 0x00, 0x00, false, false };
    return parameters;
}

#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
/**
    @brief Returns a set of parameters for CRC-8 EBU (aka CRC-8 AES).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-8 EBU has the following parameters and check value:
        - polynomial     = 0x1D
        - initial value  = 0xFF
        - final XOR      = 0x00
        - reflect input  = true
        - reflect output = true
        - check value    = 0x97
    @return CRC-8 EBU parameters
*/
inline const CRC::Parameters<crcpp_uint8, 8> & CRC::CRC_8_EBU()
{
    static const Parameters<crcpp_uint8, 8> parameters = { 0x1D, 0xFF, 0x00, true, true };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-8 MAXIM (aka CRC-8 DOW-CRC).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-8 MAXIM has the following parameters and check value:
        - polynomial     = 0x31
        - initial value  = 0x00
        - final XOR      = 0x00
        - reflect input  = true
        - reflect output = true
        - check value    = 0xA1
    @return CRC-8 MAXIM parameters
*/
inline const CRC::Parameters<crcpp_uint8, 8> & CRC::CRC_8_MAXIM()
{
    static const Parameters<crcpp_uint8, 8> parameters = { 0x31, 0x00, 0x00, true, true };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-8 WCDMA.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-8 WCDMA has the following parameters and check value:
        - polynomial     = 0x9B
        - initial value  = 0x00
        - final XOR      = 0x00
        - reflect input  = true
        - reflect output = true
        - check value    = 0x25
    @return CRC-8 WCDMA parameters
*/
inline const CRC::Parameters<crcpp_uint8, 8> & CRC::CRC_8_WCDMA()
{
    static const Parameters<crcpp_uint8, 8> parameters = { 0x9B, 0x00, 0x00, true, true };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-10 ITU.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-10 ITU has the following parameters and check value:
        - polynomial     = 0x233
        - initial value  = 0x000
        - final XOR      = 0x000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x199
    @return CRC-10 ITU parameters
*/
inline const CRC::Parameters<crcpp_uint16, 10> & CRC::CRC_10()
{
    static const Parameters<crcpp_uint16, 10> parameters = { 0x233, 0x000, 0x000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-10 CDMA2000.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-10 CDMA2000 has the following parameters and check value:
        - polynomial     = 0x3D9
        - initial value  = 0x3FF
        - final XOR      = 0x000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x233
    @return CRC-10 CDMA2000 parameters
*/
inline const CRC::Parameters<crcpp_uint16, 10> & CRC::CRC_10_CDMA2000()
{
    static const Parameters<crcpp_uint16, 10> parameters = { 0x3D9, 0x3FF, 0x000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-11 FlexRay.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-11 FlexRay has the following parameters and check value:
        - polynomial     = 0x385
        - initial value  = 0x01A
        - final XOR      = 0x000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x5A3
    @return CRC-11 FlexRay parameters
*/
inline const CRC::Parameters<crcpp_uint16, 11> & CRC::CRC_11()
{
    static const Parameters<crcpp_uint16, 11> parameters = { 0x385, 0x01A, 0x000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-12 CDMA2000.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-12 CDMA2000 has the following parameters and check value:
        - polynomial     = 0xF13
        - initial value  = 0xFFF
        - final XOR      = 0x000
        - reflect input  = false
        - reflect output = false
        - check value    = 0xD4D
    @return CRC-12 CDMA2000 parameters
*/
inline const CRC::Parameters<crcpp_uint16, 12> & CRC::CRC_12_CDMA2000()
{
    static const Parameters<crcpp_uint16, 12> parameters = { 0xF13, 0xFFF, 0x000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-12 DECT (aka CRC-12 X-CRC).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-12 DECT has the following parameters and check value:
        - polynomial     = 0x80F
        - initial value  = 0x000
        - final XOR      = 0x000
        - reflect input  = false
        - reflect output = false
        - check value    = 0xF5B
    @return CRC-12 DECT parameters
*/
inline const CRC::Parameters<crcpp_uint16, 12> & CRC::CRC_12_DECT()
{
    static const Parameters<crcpp_uint16, 12> parameters = { 0x80F, 0x000, 0x000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-12 UMTS (aka CRC-12 3GPP).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-12 UMTS has the following parameters and check value:
        - polynomial     = 0x80F
        - initial value  = 0x000
        - final XOR      = 0x000
        - reflect input  = false
        - reflect output = true
        - check value    = 0xDAF
    @return CRC-12 UMTS parameters
*/
inline const CRC::Parameters<crcpp_uint16, 12> & CRC::CRC_12_UMTS()
{
    static const Parameters<crcpp_uint16, 12> parameters = { 0x80F, 0x000, 0x000, false, true };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-13 BBC.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-13 BBC has the following parameters and check value:
        - polynomial     = 0x1CF5
        - initial value  = 0x0000
        - final XOR      = 0x0000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x04FA
    @return CRC-13 BBC parameters
*/
inline const CRC::Parameters<crcpp_uint16, 13> & CRC::CRC_13_BBC()
{
    static const Parameters<crcpp_uint16, 13> parameters = { 0x1CF5, 0x0000, 0x0000, false, false };
   return parameters;
}

/**
    @brief Returns a set of parameters for CRC-15 CAN.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-15 CAN has the following parameters and check value:
        - polynomial     = 0x4599
        - initial value  = 0x0000
        - final XOR      = 0x0000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x059E
    @return CRC-15 CAN parameters
*/
inline const CRC::Parameters<crcpp_uint16, 15> & CRC::CRC_15()
{
    static const Parameters<crcpp_uint16, 15> parameters = { 0x4599, 0x0000, 0x0000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-15 MPT1327.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-15 MPT1327 has the following parameters and check value:
        - polynomial     = 0x6815
        - initial value  = 0x0000
        - final XOR      = 0x0001
        - reflect input  = false
        - reflect output = false
        - check value    = 0x2566
    @return CRC-15 MPT1327 parameters
*/
inline const CRC::Parameters<crcpp_uint16, 15> & CRC::CRC_15_MPT1327()
{
    static const Parameters<crcpp_uint16, 15> parameters = { 0x6815, 0x0000, 0x0001, false, false };
    return parameters;
}
#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS

/**
    @brief Returns a set of parameters for CRC-16 ARC (aka CRC-16 IBM, CRC-16 LHA).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 ARC has the following parameters and check value:
        - polynomial     = 0x8005
        - initial value  = 0x0000
        - final XOR      = 0x0000
        - reflect input  = true
        - reflect output = true
        - check value    = 0xBB3D
    @return CRC-16 ARC parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_ARC()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x8005, 0x0000, 0x0000, true, true };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-16 BUYPASS (aka CRC-16 VERIFONE, CRC-16 UMTS).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 BUYPASS has the following parameters and check value:
        - polynomial     = 0x8005
        - initial value  = 0x0000
        - final XOR      = 0x0000
        - reflect input  = false
        - reflect output = false
        - check value    = 0xFEE8
    @return CRC-16 BUYPASS parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_BUYPASS()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x8005, 0x0000, 0x0000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-16 CCITT FALSE.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 CCITT FALSE has the following parameters and check value:
        - polynomial     = 0x1021
        - initial value  = 0xFFFF
        - final XOR      = 0x0000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x29B1
    @return CRC-16 CCITT FALSE parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_CCITTFALSE()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x1021, 0xFFFF, 0x0000, false, false };
    return parameters;
}

#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
/**
    @brief Returns a set of parameters for CRC-16 CDMA2000.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 CDMA2000 has the following parameters and check value:
        - polynomial     = 0xC867
        - initial value  = 0xFFFF
        - final XOR      = 0x0000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x4C06
    @return CRC-16 CDMA2000 parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_CDMA2000()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0xC867, 0xFFFF, 0x0000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-16 DECT-R (aka CRC-16 R-CRC).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 DECT-R has the following parameters and check value:
        - polynomial     = 0x0589
        - initial value  = 0x0000
        - final XOR      = 0x0001
        - reflect input  = false
        - reflect output = false
        - check value    = 0x007E
    @return CRC-16 DECT-R parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_DECTR()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x0589, 0x0000, 0x0001, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-16 DECT-X (aka CRC-16 X-CRC).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 DECT-X has the following parameters and check value:
        - polynomial     = 0x0589
        - initial value  = 0x0000
        - final XOR      = 0x0000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x007F
    @return CRC-16 DECT-X parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_DECTX()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x0589, 0x0000, 0x0000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-16 DNP.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 DNP has the following parameters and check value:
        - polynomial     = 0x3D65
        - initial value  = 0x0000
        - final XOR      = 0xFFFF
        - reflect input  = true
        - reflect output = true
        - check value    = 0xEA82
    @return CRC-16 DNP parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_DNP()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x3D65, 0x0000, 0xFFFF, true, true };
    return parameters;
}
#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS

/**
    @brief Returns a set of parameters for CRC-16 GENIBUS (aka CRC-16 EPC, CRC-16 I-CODE, CRC-16 DARC).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 GENIBUS has the following parameters and check value:
        - polynomial     = 0x1021
        - initial value  = 0xFFFF
        - final XOR      = 0xFFFF
        - reflect input  = false
        - reflect output = false
        - check value    = 0xD64E
    @return CRC-16 GENIBUS parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_GENIBUS()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x1021, 0xFFFF, 0xFFFF, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-16 KERMIT (aka CRC-16 CCITT, CRC-16 CCITT-TRUE).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 KERMIT has the following parameters and check value:
        - polynomial     = 0x1021
        - initial value  = 0x0000
        - final XOR      = 0x0000
        - reflect input  = true
        - reflect output = true
        - check value    = 0x2189
    @return CRC-16 KERMIT parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_KERMIT()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x1021, 0x0000, 0x0000, true, true };
    return parameters;
}

#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
/**
    @brief Returns a set of parameters for CRC-16 MAXIM.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 MAXIM has the following parameters and check value:
        - polynomial     = 0x8005
        - initial value  = 0x0000
        - final XOR      = 0xFFFF
        - reflect input  = true
        - reflect output = true
        - check value    = 0x44C2
    @return CRC-16 MAXIM parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_MAXIM()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x8005, 0x0000, 0xFFFF, true, true };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-16 MODBUS.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 MODBUS has the following parameters and check value:
        - polynomial     = 0x8005
        - initial value  = 0xFFFF
        - final XOR      = 0x0000
        - reflect input  = true
        - reflect output = true
        - check value    = 0x4B37
    @return CRC-16 MODBUS parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_MODBUS()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x8005, 0xFFFF, 0x0000, true, true };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-16 T10-DIF.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 T10-DIF has the following parameters and check value:
        - polynomial     = 0x8BB7
        - initial value  = 0x0000
        - final XOR      = 0x0000
        - reflect input  = false
        - reflect output = false
        - check value    = 0xD0DB
    @return CRC-16 T10-DIF parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_T10DIF()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x8BB7, 0x0000, 0x0000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-16 USB.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 USB has the following parameters and check value:
        - polynomial     = 0x8005
        - initial value  = 0xFFFF
        - final XOR      = 0xFFFF
        - reflect input  = true
        - reflect output = true
        - check value    = 0xB4C8
    @return CRC-16 USB parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_USB()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x8005, 0xFFFF, 0xFFFF, true, true };
    return parameters;
}
#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS

/**
    @brief Returns a set of parameters for CRC-16 X-25 (aka CRC-16 IBM-SDLC, CRC-16 ISO-HDLC, CRC-16 B).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 X-25 has the following parameters and check value:
        - polynomial     = 0x1021
        - initial value  = 0xFFFF
        - final XOR      = 0xFFFF
        - reflect input  = true
        - reflect output = true
        - check value    = 0x906E
    @return CRC-16 X-25 parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_X25()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x1021, 0xFFFF, 0xFFFF, true, true };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-16 XMODEM (aka CRC-16 ZMODEM, CRC-16 ACORN, CRC-16 LTE).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-16 XMODEM has the following parameters and check value:
        - polynomial     = 0x1021
        - initial value  = 0x0000
        - final XOR      = 0x0000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x31C3
    @return CRC-16 XMODEM parameters
*/
inline const CRC::Parameters<crcpp_uint16, 16> & CRC::CRC_16_XMODEM()
{
    static const Parameters<crcpp_uint16, 16> parameters = { 0x1021, 0x0000, 0x0000, false, false };
    return parameters;
}

#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
/**
    @brief Returns a set of parameters for CRC-17 CAN.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-17 CAN has the following parameters and check value:
        - polynomial     = 0x1685B
        - initial value  = 0x00000
        - final XOR      = 0x00000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x04F03
    @return CRC-17 CAN parameters
*/
inline const CRC::Parameters<crcpp_uint32, 17> & CRC::CRC_17_CAN()
{
    static const Parameters<crcpp_uint32, 17> parameters = { 0x1685B, 0x00000, 0x00000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-21 CAN.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-21 CAN has the following parameters and check value:
        - polynomial     = 0x102899
        - initial value  = 0x000000
        - final XOR      = 0x000000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x0ED841
    @return CRC-21 CAN parameters
*/
inline const CRC::Parameters<crcpp_uint32, 21> & CRC::CRC_21_CAN()
{
    static const Parameters<crcpp_uint32, 21> parameters = { 0x102899, 0x000000, 0x000000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-24 OPENPGP.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-24 OPENPGP has the following parameters and check value:
        - polynomial     = 0x864CFB
        - initial value  = 0xB704CE
        - final XOR      = 0x000000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x21CF02
    @return CRC-24 OPENPGP parameters
*/
inline const CRC::Parameters<crcpp_uint32, 24> & CRC::CRC_24()
{
    static const Parameters<crcpp_uint32, 24> parameters = { 0x864CFB, 0xB704CE, 0x000000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-24 FlexRay-A.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-24 FlexRay-A has the following parameters and check value:
        - polynomial     = 0x5D6DCB
        - initial value  = 0xFEDCBA
        - final XOR      = 0x000000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x7979BD
    @return CRC-24 FlexRay-A parameters
*/
inline const CRC::Parameters<crcpp_uint32, 24> & CRC::CRC_24_FLEXRAYA()
{
    static const Parameters<crcpp_uint32, 24> parameters = { 0x5D6DCB, 0xFEDCBA, 0x000000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-24 FlexRay-B.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-24 FlexRay-B has the following parameters and check value:
        - polynomial     = 0x5D6DCB
        - initial value  = 0xABCDEF
        - final XOR      = 0x000000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x1F23B8
    @return CRC-24 FlexRay-B parameters
*/
inline const CRC::Parameters<crcpp_uint32, 24> & CRC::CRC_24_FLEXRAYB()
{
    static const Parameters<crcpp_uint32, 24> parameters = { 0x5D6DCB, 0xABCDEF, 0x000000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-30 CDMA.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-30 CDMA has the following parameters and check value:
        - polynomial     = 0x2030B9C7
        - initial value  = 0x3FFFFFFF
        - final XOR      = 0x00000000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x3B3CB540
    @return CRC-30 CDMA parameters
*/
inline const CRC::Parameters<crcpp_uint32, 30> & CRC::CRC_30()
{
    static const Parameters<crcpp_uint32, 30> parameters = { 0x2030B9C7, 0x3FFFFFFF, 0x00000000, false, false };
    return parameters;
}
#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS

/**
    @brief Returns a set of parameters for CRC-32 (aka CRC-32 ADCCP, CRC-32 PKZip).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-32 has the following parameters and check value:
        - polynomial     = 0x04C11DB7
        - initial value  = 0xFFFFFFFF
        - final XOR      = 0xFFFFFFFF
        - reflect input  = true
        - reflect output = true
        - check value    = 0xCBF43926
    @return CRC-32 parameters
*/
inline const CRC::Parameters<crcpp_uint32, 32> & CRC::CRC_32()
{
    static const Parameters<crcpp_uint32, 32> parameters = { 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-32 BZIP2 (aka CRC-32 AAL5, CRC-32 DECT-B, CRC-32 B-CRC).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-32 BZIP2 has the following parameters and check value:
        - polynomial     = 0x04C11DB7
        - initial value  = 0xFFFFFFFF
        - final XOR      = 0xFFFFFFFF
        - reflect input  = false
        - reflect output = false
        - check value    = 0xFC891918
    @return CRC-32 BZIP2 parameters
*/
inline const CRC::Parameters<crcpp_uint32, 32> & CRC::CRC_32_BZIP2()
{
    static const Parameters<crcpp_uint32, 32> parameters = { 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, false, false };
    return parameters;
}

#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
/**
    @brief Returns a set of parameters for CRC-32 C (aka CRC-32 ISCSI, CRC-32 Castagnoli, CRC-32 Interlaken).
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-32 C has the following parameters and check value:
        - polynomial     = 0x1EDC6F41
        - initial value  = 0xFFFFFFFF
        - final XOR      = 0xFFFFFFFF
        - reflect input  = true
        - reflect output = true
        - check value    = 0xE3069283
    @return CRC-32 C parameters
*/
inline const CRC::Parameters<crcpp_uint32, 32> & CRC::CRC_32_C()
{
    static const Parameters<crcpp_uint32, 32> parameters = { 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true, true };
    return parameters;
}
#endif

/**
    @brief Returns a set of parameters for CRC-32 MPEG-2.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-32 MPEG-2 has the following parameters and check value:
        - polynomial     = 0x04C11DB7
        - initial value  = 0xFFFFFFFF
        - final XOR      = 0x00000000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x0376E6E7
    @return CRC-32 MPEG-2 parameters
*/
inline const CRC::Parameters<crcpp_uint32, 32> & CRC::CRC_32_MPEG2()
{
    static const Parameters<crcpp_uint32, 32> parameters = { 0x04C11DB7, 0xFFFFFFFF, 0x00000000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-32 POSIX.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-32 POSIX has the following parameters and check value:
        - polynomial     = 0x04C11DB7
        - initial value  = 0x00000000
        - final XOR      = 0xFFFFFFFF
        - reflect input  = false
        - reflect output = false
        - check value    = 0x765E7680
    @return CRC-32 POSIX parameters
*/
inline const CRC::Parameters<crcpp_uint32, 32> & CRC::CRC_32_POSIX()
{
    static const Parameters<crcpp_uint32, 32> parameters = { 0x04C11DB7, 0x00000000, 0xFFFFFFFF, false, false };
    return parameters;
}

#ifdef CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS
/**
    @brief Returns a set of parameters for CRC-32 Q.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-32 Q has the following parameters and check value:
        - polynomial     = 0x814141AB
        - initial value  = 0x00000000
        - final XOR      = 0x00000000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x3010BF7F
    @return CRC-32 Q parameters
*/
inline const CRC::Parameters<crcpp_uint32, 32> & CRC::CRC_32_Q()
{
    static const Parameters<crcpp_uint32, 32> parameters = { 0x814141AB, 0x00000000, 0x00000000, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-40 GSM.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-40 GSM has the following parameters and check value:
        - polynomial     = 0x0004820009
        - initial value  = 0x0000000000
        - final XOR      = 0xFFFFFFFFFF
        - reflect input  = false
        - reflect output = false
        - check value    = 0xD4164FC646
    @return CRC-40 GSM parameters
*/
inline const CRC::Parameters<crcpp_uint64, 40> & CRC::CRC_40_GSM()
{
    static const Parameters<crcpp_uint64, 40> parameters = { 0x0004820009, 0x0000000000, 0xFFFFFFFFFF, false, false };
    return parameters;
}

/**
    @brief Returns a set of parameters for CRC-64 ECMA.
    @note The parameters are static and are delayed-constructed to reduce memory footprint.
    @note CRC-64 ECMA has the following parameters and check value:
        - polynomial     = 0x42F0E1EBA9EA3693
        - initial value  = 0x0000000000000000
        - final XOR      = 0x0000000000000000
        - reflect input  = false
        - reflect output = false
        - check value    = 0x6C40DF5F0B497347
    @return CRC-64 ECMA parameters
*/
inline const CRC::Parameters<crcpp_uint64, 64> & CRC::CRC_64()
{
    static const Parameters<crcpp_uint64, 64> parameters = { 0x42F0E1EBA9EA3693, 0x0000000000000000, 0x0000000000000000, false, false };
    return parameters;
}
#endif // CRCPP_INCLUDE_ESOTERIC_CRC_DEFINITIONS

#ifdef CRCPP_USE_NAMESPACE
}
#endif

#endif // CRCPP_CRC_H_


code:
find_program (GZIP_EXECUTABLE NAMES gzip)

string (TIMESTAMP ts "%w, %d [%m] %Y %H:%M:%S +0000" UTC)

set (__days Sun Mon Tue Wed Thu Fri Sat)
string (SUBSTRING ${ts} 0 1 __index)
list (GET __days ${__index} __day)
string (REPLACE "${__index}," "${__day}," ts ${ts})

set (__months Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
string (SUBSTRING ${ts} 9 2 __month_index)
math (EXPR __index "${__month_index} - 1")
list (GET __months "${__index}" __month)
string (REPLACE "[${__month_index}]" "${__month}" ts ${ts})

set (deb_changelog changelog.Debian)
configure_file (
  "${CMAKE_CURRENT_SOURCE_DIR}/${deb_changelog}.in"
  "${CMAKE_CURRENT_BINARY_DIR}/${deb_changelog}"
  @ONLY)

add_custom_target (debian
  COMMAND ${GZIP_EXECUTABLE} -cf9 ${deb_changelog} > ${deb_changelog}.gz
  DEPENDS ${deb_changelog}.in
  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
  COMMENT "Compressing Debian changelog"
  )

configure_file (
  "${CMAKE_CURRENT_SOURCE_DIR}/copyright.in"
  "${CMAKE_CURRENT_BINARY_DIR}/copyright"
  @ONLY)

install (FILES
  ${CMAKE_CURRENT_BINARY_DIR}/${deb_changelog}.gz
  ${CMAKE_CURRENT_BINARY_DIR}/copyright
  DESTINATION ${CMAKE_INSTALL_DOCDIR}
  #COMPONENT Debian
  )

license: mit


code:
@CMAKE_PROJECT_NAME@ (@wsjtx_VERSION@) experimental; urgency=low

  * wsjtx Debian maintainer and upstream author are identical.
    Therefore see also normal changelog file for Debian changes.

 -- @PROJECT_CONTACT@  @ts@


code:
Format-Specification: http://wiki.debian.org/Proposals/CopyrightFormat
Upstream-Name: @CMAKE_PROJECT_NAME@
Upstream-Maintainer: @PROJECT_CONTACT@
Upstream-Source: @PROJECT_HOMEPAGE@

Files: *
Copyright: @PROJECT_COPYRIGHT@.
License: GPL v3
 The full text of the GPL is distributed in
 /usr/share/common-licenses/GPL-3 on Debian systems.


code:
#include "decodedtext.h"

#include <QStringList>
#include <QRegularExpression>
#include <QDebug>

#include <varicode.h>

extern "C" {
  bool stdmsg_(char const * msg, bool contest_mode, char const * mygrid, fortran_charlen_t, fortran_charlen_t);
}

namespace
{
  QRegularExpression words_re {R"(^(?:(?<word1>(?:CQ|DE|QRZ)(?:\s?DX|\s(?:[A-Z]{2}|\d{3}))|[A-Z0-9/]+)\s)(?:(?<word2>[A-Z0-9/]+)(?:\s(?<word3>[-+A-Z0-9]+)(?:\s(?<word4>(?:OOO|(?!RR73)[A-R]{2}[0-9]{2})))?)?)?)"};
}

DecodedText::DecodedText (QString const& the_string, bool contest_mode, QString const& my_grid)
  : string_ {the_string.left (the_string.indexOf (QChar::Nbsp))} // discard appended info
  , padding_ {string_.indexOf (" ") > 4 ? 2 : 0} // allow for seconds
  , contest_mode_ {contest_mode}
  , message_ {string_.mid (column_qsoText + padding_).trimmed ()}
  , is_standard_ {false}
  , frameType_(Varicode::FrameUnknown)
  , isBeacon_(false)
  , isAlt_(false)
{
    if(message_.length() >= 1) {
        message_ = message_.left (21).remove (QRegularExpression {"[<>]"});
        int i1 = message_.indexOf ('\r');
        if (i1 > 0) {
            message_ = message_.left (i1 - 1);
        }

        if (message_.contains (QRegularExpression {"^(CQ|QRZ)\\s"})) {
            // TODO this magic position 16 is guaranteed to be after the
            // last space in a decoded CQ or QRZ message but before any
            // appended DXCC entity name or worked before information
            auto eom_pos = message_.indexOf (' ', 16);
            // we always want at least the characters to position 16
            if (eom_pos < 16) eom_pos = message_.size () - 1;
            // remove DXCC entity and worked B4 status. TODO need a better way to do this
            message_ = message_.left (eom_pos + 1);
        }

        // stdmsg is a fortran routine that packs the text, unpacks it
        // and compares the result
        auto message_c_string = message_.toLocal8Bit ();
        message_c_string += QByteArray {22 - message_c_string.size (), ' '};
        auto grid_c_string = my_grid.toLocal8Bit ();
        grid_c_string += QByteArray {6 - grid_c_string.size (), ' '};
        is_standard_ = stdmsg_ (message_c_string.constData (), contest_mode_, grid_c_string.constData (), 22, 6);

        // We're only going to unpack standard messages for CQs && beacons...
        // TODO: jsherer - this is a hack for now...
        if(is_standard_){
            is_standard_ = QRegularExpression("^(CQ|DE|QRZ)\\s").match(message_).hasMatch();
        }
    }

    tryUnpack();
}

DecodedText::DecodedText (QString const& js8callmessage):
    frameType_(Varicode::FrameUnknown),
    message_(js8callmessage),
    isBeacon_(false),
    isAlt_(false)
{
    is_standard_ = QRegularExpression("^(CQ|DE|QRZ)\\s").match(message_).hasMatch();

    tryUnpack();
}

bool DecodedText::tryUnpack(){
    if(is_standard_){
        message_ = message_.append(" ");
        return false;
    }

    bool unpacked = false;
    if(!unpacked){
        unpacked = tryUnpackBeacon();
    }

    if(!unpacked){
        unpacked = tryUnpackCompound();
    }

    if(!unpacked){
        unpacked = tryUnpackDirected();
    }

    if(!unpacked){
        unpacked = tryUnpackData();
    }

    return unpacked;
}

bool DecodedText::tryUnpackBeacon(){
    QString m = message().trimmed();

    // directed calls will always be 12+ chars and contain no spaces.
    if(m.length() < 12 || m.contains(' ')){
      return false;
    }

    bool isAlt = false;
    quint8 type = Varicode::FrameUnknown;
    quint8 bits3 = 0;
    QStringList parts = Varicode::unpackBeaconMessage(m, &type, &isAlt, &bits3);

    if(parts.isEmpty() || parts.length() < 2){
        return false;
    }

    // Beacon Alt Type
    // ---------------
    // 1      0   BCN
    // 1      1   CQ
    isBeacon_ = true;
    isAlt_ = isAlt;
    extra_ = parts.length() < 3 ? "" : parts.at(2);

    QStringList cmp;
    if(!parts.at(0).isEmpty()){
        cmp.append(parts.at(0));
    }
    if(!parts.at(1).isEmpty()){
        cmp.append(parts.at(1));
    }
    compound_ = cmp.join("/");
    message_ = QString("%1: %2 %3 ").arg(compound_).arg(isAlt ? Varicode::cqString(bits3) : "BEACON").arg(extra_);
    frameType_ = type;
    return true;
}

bool DecodedText::tryUnpackCompound(){
    auto m = message().trimmed();
    // directed calls will always be 12+ chars and contain no spaces.
    if(m.length() < 12 || m.contains(' ')){
        return false;
    }

    quint8 type = Varicode::FrameUnknown;
    quint8 bits3 = 0;
    auto parts = Varicode::unpackCompoundMessage(m, &type, &bits3);
    if(parts.isEmpty() || parts.length() < 2){
        return false;
    }

    QStringList cmp;
    if(!parts.at(0).isEmpty()){
        cmp.append(parts.at(0));
    }
    if(!parts.at(1).isEmpty()){
        cmp.append(parts.at(1));
    }
    compound_ = cmp.join("/");
    extra_ = parts.length() < 3 ? "" : parts.mid(2).join(" ");

    if(type == Varicode::FrameCompound){
        message_ = QString("%1: ").arg(compound_);
    } else if(type == Varicode::FrameCompoundDirected){
        message_ = QString("%1%2 ").arg(compound_).arg(extra_);
        directed_ = QStringList{ "<....>", compound_ } + parts.mid(2);
    }

    frameType_ = type;
    return true;
}

bool DecodedText::tryUnpackDirected(){
    QString m = message().trimmed();

    // directed calls will always be 12+ chars and contain no spaces.
    if(m.length() < 12 || m.contains(' ')){
      return false;
    }

    quint8 type = Varicode::FrameUnknown;
    QStringList parts = Varicode::unpackDirectedMessage(m, &type);

    if(parts.isEmpty()){
      return false;
    }

    if(parts.length() == 3){
      // replace it with the correct unpacked (directed)
      message_ = QString("%1: %2%3 ").arg(parts.at(0), parts.at(1), parts.at(2));
    } else if(parts.length() == 4){
      // replace it with the correct unpacked (directed numeric)
      message_ = QString("%1: %2%3 %4 ").arg(parts.at(0), parts.at(1), parts.at(2), parts.at(3));
    } else {
      // replace it with the correct unpacked (freetext)
      message_ = QString(parts.join(""));
    }

    directed_ = parts;
    frameType_ = type;
    return true;
}

bool DecodedText::tryUnpackData(){
    QString m = message().trimmed();

    // data frames calls will always be 12+ chars and contain no spaces.
    if(m.length() < 12 || m.contains(' ')){
      return false;
    }

    quint8 type = Varicode::FrameUnknown;
    QString data = Varicode::unpackDataMessage(m, &type);

    if(data.isEmpty()){
      return false;
    }

    message_ = data;
    frameType_ = type;
    return true;
}

QStringList DecodedText::messageWords () const
{
  if (is_standard_)
    {
      // extract up to the first four message words
      return words_re.match (message_).capturedTexts ();
    }
  // simple word split for free text messages
  auto words = message_.split (' ', QString::SkipEmptyParts);
  // add whole message as item 0 to mimic RE capture list
  words.prepend (message_);
  return words;
}

QString DecodedText::CQersCall() const
{
  QRegularExpression callsign_re {R"(^(CQ|DE|QRZ)(\s?DX|\s([A-Z]{2}|\d{3}))?\s(?<callsign>[A-Z0-9/]{2,})(\s[A-R]{2}[0-9]{2})?)"};
  return callsign_re.match (message_).captured ("callsign");
}

bool DecodedText::isJT65() const
{
    return string_.indexOf("#") == column_mode + padding_;
}

bool DecodedText::isJT9() const
{
    return string_.indexOf("@") == column_mode + padding_;
}

bool DecodedText::isTX() const
{
    int i = string_.indexOf("Tx");
    return (i >= 0 && i < 15); // TODO guessing those numbers. Does Tx ever move?
}

bool DecodedText::isLowConfidence () const
{
  return QChar {'?'} == string_.mid (padding_ + column_qsoText + 21, 1);
}

int DecodedText::frequencyOffset() const
{
    return string_.mid(column_freq + padding_,4).toInt();
}

int DecodedText::snr() const
{
  int i1=string_.indexOf(" ")+1;
  return string_.mid(i1,3).toInt();
}

float DecodedText::dt() const
{
  return string_.mid(column_dt + padding_,5).toFloat();
}

/*
2343 -11  0.8 1259 # YV6BFE F6GUU R-08
2343 -19  0.3  718 # VE6WQ SQ2NIJ -14
2343  -7  0.3  815 # KK4DSD W7VP -16
2343 -13  0.1 3627 @ CT1FBK IK5YZT R+02

0605  Tx      1259 # CQ VK3ACF QF22
*/

// find and extract any report. Returns true if this is a standard message
bool DecodedText::report(QString const& myBaseCall, QString const& dxBaseCall, /*mod*/QString& report) const
{
  if (message_.size () < 1) return false;

  QStringList const& w = message_.split(" ",QString::SkipEmptyParts);
  if (w.size ()
      && is_standard_ && (w[0] == myBaseCall
                          || w[0].endsWith ("/" + myBaseCall)
                          || w[0].startsWith (myBaseCall + "/")
                          || (w.size () > 1 && !dxBaseCall.isEmpty ()
                              && (w[1] == dxBaseCall
                                  || w[1].endsWith ("/" + dxBaseCall)
                                  || w[1].startsWith (dxBaseCall + "/")))))
    {
      QString tt="";
      if(w.size() > 2) tt=w[2];
      bool ok;
      auto i1=tt.toInt(&ok);
      if (ok and i1>=-50 and i1<50)
        {
          report = tt;
        }
      else
        {
          if (tt.mid(0,1)=="R")
            {
              i1=tt.mid(1).toInt(&ok);
              if(ok and i1>=-50 and i1<50)
                {
                  report = tt.mid(1);
                }
            }
        }
    }
  return is_standard_;
}

// get the first text word, usually the call
QString DecodedText::call() const
{
  return words_re.match (message_).captured ("word1");
}

// get the second word, most likely the de call and the third word, most likely grid
void DecodedText::deCallAndGrid(/*out*/QString& call, QString& grid) const
{
  auto const& match = words_re.match (message_);
  call = match.captured ("word2");
  grid = match.captured ("word3");
  if (contest_mode_ && "R" == grid)
    {
      grid = match.captured ("word4");
    }
}

unsigned DecodedText::timeInSeconds() const
{
  return 3600 * string_.mid (column_time, 2).toUInt ()
    + 60 * string_.mid (column_time + 2, 2).toUInt()
    + (padding_ ? string_.mid (column_time + 2 + padding_, 2).toUInt () : 0U);
}

/*
2343 -11  0.8 1259 # YV6BFE F6GUU R-08
2343 -19  0.3  718 # VE6WQ SQ2NIJ -14
2343  -7  0.3  815 # KK4DSD W7VP -16
2343 -13  0.1 3627 @ CT1FBK IK5YZT R+02

0605  Tx      1259 # CQ VK3ACF QF22
*/

QString DecodedText::report() const // returns a string of the SNR field with a leading + or - followed by two digits
{
    int sr = snr();
    if (sr<-50)
        sr = -50;
    else
        if (sr > 49)
            sr = 49;

    QString rpt;
    rpt.sprintf("%d",abs(sr));
    if (sr > 9)
        rpt = "+" + rpt;
    else
        if (sr >= 0)
            rpt = "+0" + rpt;
        else
            if (sr >= -9)
                rpt = "-0" + rpt;
            else
                rpt = "-" + rpt;
    return rpt;
}


code:
// -*- Mode: C++ -*-
/*
 * Class to handle the formatted string as returned from the fortran decoder
 *
 * VK3ACF August 2013
 */

#ifndef DECODEDTEXT_H
#define DECODEDTEXT_H

#include <QString>
#include <QStringList>

/*
012345678901234567890123456789012345678901
^    ^    ^   ^    ^  ^
2343 -11  0.8 1259 #  CQ VP2X/GM4WJS GL33
2343 -11  0.8 1259 #  CQ 999 VP2V/GM4WJS
2343 -11  0.8 1259 #  YV6BFE F6GUU R-08
2343 -19  0.3  718 #  VE6WQ SQ2NIJ -14
2343  -7  0.3  815 #  KK4DSD W7VP -16
2343 -13  0.1 3627 @  CT1FBK IK5YZT R+02

0605  Tx      1259 #  CQ VK3ACF QF22
*/

class DecodedText
{
public:
  explicit DecodedText (QString const& message, bool, QString const& my_grid);
  explicit DecodedText (QString const& js8callmessage);

  bool tryUnpack();
  bool tryUnpackBeacon();
  bool tryUnpackCompound();
  bool tryUnpackDirected();
  bool tryUnpackData();

  quint8 frameType() const { return frameType_; }

  QString extra() const { return extra_; }
  QString compoundCall() const { return compound_; }
  bool isCompound() const { return !compound_.isEmpty(); }

  bool isBeacon() const { return isBeacon_; }
  bool isAlt() const { return isAlt_; }

  QStringList directedMessage() const { return directed_; }
  bool isDirectedMessage() const { return !directed_.isEmpty() && directed_.length() > 2; }

  QString string() const { return string_; }
  QString message() const { return message_; }
  QStringList messageWords () const;
  int indexOf(QString s) const { return string_.indexOf(s); }
  int indexOf(QString s, int i) const { return string_.indexOf(s,i); }
  QString mid(int f, int t) const { return string_.mid(f,t); }
  QString left(int i) const { return string_.left(i); }

  void clear() { string_.clear(); }

  QString CQersCall() const;

  bool isJT65() const;
  bool isJT9() const;
  bool isTX() const;
  bool isStandardMessage () const {return is_standard_;}
  bool isLowConfidence () const;
  int frequencyOffset() const;  // hertz offset from the tuned dial or rx frequency, aka audio frequency
  int snr() const;
  bool hasBits() const { return !string_.right(5).trimmed().isEmpty(); }
  int bits() const { return string_.right(5).trimmed().toShort(); }
  float dt() const;

  // find and extract any report. Returns true if this is a standard message
  bool report(QString const& myBaseCall, QString const& dxBaseCall, /*mod*/QString& report) const;

  // get the first message text word, usually the call
  QString call() const;

  // get the second word, most likely the de call and the third word, most likely grid
  void deCallAndGrid(/*out*/QString& call, QString& grid) const;

  unsigned timeInSeconds() const;

  // returns a string of the SNR field with a leading + or - followed by two digits
  QString report() const;

private:
  // These define the columns in the decoded text where fields are to be found.
  // We rely on these columns being the same in the fortran code (lib/decoder.f90) that formats the decoded text
  enum Columns {column_time    = 0,
      column_snr     = 5,
      column_dt      = 9,
      column_freq    = 14,
      column_mode    = 19,
      column_qsoText = 22 };

  quint8 frameType_;
  bool isBeacon_;
  bool isAlt_;
  QString compound_;
  QString extra_;
  QStringList directed_;
  QString string_;
  int padding_;
  bool contest_mode_;
  QString message_;
  bool is_standard_;
};

#endif // DECODEDTEXT_H


code:
#include "displaytext.h"
#include "mainwindow.h"
#include <QMouseEvent>
#include <QDateTime>
#include <QTextCharFormat>
#include <QTextCursor>
#include <QTextBlock>
#include <QMenu>
#include <QAction>

#include "DriftingDateTime.h"

#include "qt_helpers.hpp"

#include "moc_displaytext.cpp"

DisplayText::DisplayText(QWidget *parent)
  : QTextEdit(parent)
  , erase_action_ {new QAction {tr ("&Erase"), this}}
{
  setReadOnly (true);
  setUndoRedoEnabled (false);
  viewport ()->setCursor (Qt::ArrowCursor);
  setWordWrapMode (QTextOption::NoWrap);

  // max lines to limit heap usage
  document ()->setMaximumBlockCount (5000);

  // context menu erase action
  setContextMenuPolicy (Qt::CustomContextMenu);
  connect (this, &DisplayText::customContextMenuRequested, [this] (QPoint const& position) {
      auto * menu = createStandardContextMenu (position);
      menu->addAction (erase_action_);
      menu->exec (mapToGlobal (position));
      delete menu;
    });
  connect (erase_action_, &QAction::triggered, this, &DisplayText::erase);
}

void DisplayText::erase ()
{
  clear ();
  Q_EMIT erased ();
}

void DisplayText::setContentFont(QFont const& font)
{
  char_font_ = font;
  selectAll ();
  auto cursor = textCursor ();
  cursor.beginEditBlock ();
  auto char_format = cursor.charFormat ();
  char_format.setFont (char_font_);
  cursor.mergeCharFormat (char_format);
  cursor.clearSelection ();
  cursor.movePosition (QTextCursor::End);

  // position so viewport scrolled to left
  cursor.movePosition (QTextCursor::Up);
  cursor.movePosition (QTextCursor::StartOfLine);
  cursor.endEditBlock ();

  setTextCursor (cursor);
  ensureCursorVisible ();
}

void DisplayText::mouseDoubleClickEvent(QMouseEvent *e)
{
  Q_EMIT selectCallsign(e->modifiers ());
  QTextEdit::mouseDoubleClickEvent(e);
}

void DisplayText::insertLineSpacer(QString const& line)
{
  appendText (line, "#d3d3d3");
}

void DisplayText::appendText(QString const& text, QColor bg, QString const& call1, QString const& call2)
{
  auto cursor = textCursor ();
  cursor.movePosition (QTextCursor::End);
  auto block_format = cursor.blockFormat ();
  block_format.setBackground (bg);
  if (0 == cursor.position ())
    {
      cursor.setBlockFormat (block_format);
      auto char_format = cursor.charFormat ();
      char_format.setFont (char_font_);
      cursor.setCharFormat (char_format);
    }
  else
    {
      cursor.insertBlock (block_format);
    }

  QTextCharFormat format = cursor.charFormat();
  format.clearBackground();
  int text_index {0};
  if (call1.size ())
    {
      auto call_index = text.indexOf (call1);
      if (call_index != -1) // sanity check
        {
          auto pos = highlighted_calls_.find (call1);
          if (pos != highlighted_calls_.end ())
            {
              cursor.insertText(text.left (call_index), format);
              if (pos.value ().first.isValid ())
                {
                  format.setBackground (pos.value ().first);
                }
              if (pos.value ().second.isValid ())
                {
                  format.setForeground (pos.value ().second);
                }
              cursor.insertText(text.mid (call_index, call1.size ()), format);
              text_index = call_index + call1.size ();
            }
        }
    }
  if (call2.size ())
    {
      auto call_index = text.indexOf (call2, text_index);
      if (call_index != -1) // sanity check
        {
          auto pos = highlighted_calls_.find (call2);
          if (pos != highlighted_calls_.end ())
            {
              format.setBackground (bg);
              format.clearForeground ();
              cursor.insertText(text.mid (text_index, call_index - text_index), format);
              if (pos.value ().second.isValid ())
                {
                  format.setBackground (pos.value ().first);
                }
              if (pos.value ().second.isValid ())
                {
                  format.setForeground (pos.value ().second);
                }
              cursor.insertText(text.mid (call_index, call2.size ()), format);
              text_index = call_index + call2.size ();
            }
        }
    }
  format.setBackground (bg);
  format.clearForeground ();
  cursor.insertText(text.mid (text_index), format);

  // position so viewport scrolled to left
  cursor.movePosition (QTextCursor::StartOfLine);
  setTextCursor (cursor);
  ensureCursorVisible ();
  document ()->setMaximumBlockCount (document ()->maximumBlockCount ());
}

QString DisplayText::appendDXCCWorkedB4(QString message, QString const& callsign, QColor * bg,
          LogBook const& logBook, QColor color_CQ,
          QColor color_DXCC,
          QColor color_NewCall)
{
  // allow for seconds
  int padding {message.indexOf (" ") > 4 ? 2 : 0};
  QString call = callsign;
  QString countryName;
  bool callWorkedBefore;
  bool countryWorkedBefore;

  if(call.length()==2) {
    int i0=message.indexOf("CQ "+call);
    call=message.mid(i0+6,-1);
    i0=call.indexOf(" ");
    call=call.mid(0,i0);
  }
  if(call.length()<3) return message;
  if(!call.contains(QRegExp("[0-9]|[A-Z]"))) return message;

  logBook.match(/*in*/call,/*out*/countryName,callWorkedBefore,countryWorkedBefore);
  message = message.trimmed ();
  QString appendage;
  if (!countryWorkedBefore) // therefore not worked call either
    {
      appendage += "!";
      *bg = color_DXCC;
    }
  else
    {
      if (!callWorkedBefore) // but have worked the country
        {
          appendage += "~";
          *bg = color_NewCall;
        }
      else
        {
          appendage += " ";  // have worked this call before
          *bg = color_CQ;
        }
    }

  int i1=countryName.indexOf(";");
  if(m_bPrincipalPrefix) {
    int i2=countryName.lastIndexOf(";");
    if(i1>0) countryName=countryName.mid(i1+2,i2-i1-2);
  } else {
    if(i1>0) countryName=countryName.mid(0,i1);
  // do some obvious abbreviations
    countryName.replace ("Islands", "Is.");
    countryName.replace ("Island", "Is.");
    countryName.replace ("North ", "N. ");
    countryName.replace ("Northern ", "N. ");
    countryName.replace ("South ", "S. ");
    countryName.replace ("East ", "E. ");
    countryName.replace ("Eastern ", "E. ");
    countryName.replace ("West ", "W. ");
    countryName.replace ("Western ", "W. ");
    countryName.replace ("Central ", "C. ");
    countryName.replace (" and ", " & ");
    countryName.replace ("Republic", "Rep.");
    countryName.replace ("United States", "U.S.A.");
    countryName.replace ("Fed. Rep. of ", "");
    countryName.replace ("French ", "Fr.");
    countryName.replace ("Asiatic", "AS");
    countryName.replace ("European", "EU");
    countryName.replace ("African", "AF");
  }

  appendage += countryName;

  // use a nbsp to save the start of appended text so we can find
  // it again later, align appended data at a fixed column if
  // there is space otherwise let it float to the right
  int space_count {40 + padding - message.size ()};
  if (space_count > 0)
    {
      message += QString {space_count, QChar {' '}};
    }
  message += QChar::Nbsp + appendage;

  return message;
}

void DisplayText::displayDecodedText(DecodedText const& decodedText, QString const& myCall,
                                     bool displayDXCCEntity, LogBook const& logBook,
                                     QColor color_CQ, QColor color_MyCall,
                                     QColor color_DXCC, QColor color_NewCall, bool ppfx,
                                     bool bCQonly)
{
  m_bPrincipalPrefix=ppfx;
  QColor bg {Qt::transparent};
  bool CQcall = false;
  if (decodedText.string ().contains (" CQ ")
      || decodedText.string ().contains (" CQDX ")
      || decodedText.string ().contains (" QRZ "))
    {
      CQcall = true;
      bg = color_CQ;
    }
  if(bCQonly and !CQcall) return;
  if (myCall != "" and (
                        decodedText.indexOf (" " + myCall + " ") >= 0
                        or decodedText.indexOf (" " + myCall + "/") >= 0
                        or decodedText.indexOf ("/" + myCall + " ") >= 0
                        or decodedText.indexOf ("<" + myCall + " ") >= 0
                        or decodedText.indexOf (" " + myCall + ">") >= 0)) {
    bg = color_MyCall;
  }
  auto message = decodedText.string ();
  QString dxCall;
  QString dxGrid;
  decodedText.deCallAndGrid (dxCall, dxGrid);
  message = message.left (message.indexOf (QChar::Nbsp)); // strip appended info
  if (displayDXCCEntity && CQcall)
    // if enabled add the DXCC entity and B4 status to the end of the
    // preformated text line t1
    message = appendDXCCWorkedB4 (message, decodedText.CQersCall (), &bg, logBook, color_CQ,
                                  color_DXCC, color_NewCall);
  appendText (message.trimmed (), bg, decodedText.call (), dxCall);
}

void DisplayText::displayTransmittedText(QString text, QString modeTx, qint32 txFreq,
                                         QColor color_ReceivedMsg, bool bFastMode)
{
    QString t1=" @  ";
    if(modeTx=="FT8") t1=" ~  ";
    if(modeTx=="JT4") t1=" $  ";
    if(modeTx=="JT65") t1=" #  ";
    if(modeTx=="MSK144") t1=" &  ";
    QString t2;
    t2.sprintf("%4d",txFreq);
    QString t;
    if(bFastMode or modeTx=="FT8") {
      t = DriftingDateTime::currentDateTimeUtc().toString("hhmmss") + \
        "  Tx      " + t2 + t1 + text;
    } else if(modeTx.mid(0,6)=="FT8fox") {
      t = DriftingDateTime::currentDateTimeUtc().toString("hhmmss") + \
        " Tx" + modeTx.mid(7) + " " + text;
    } else {
      t = DriftingDateTime::currentDateTimeUtc().toString("hhmm") + \
        "  Tx      " + t2 + t1 + text;
    }
    appendText (t, color_ReceivedMsg);
}

void DisplayText::displayQSY(QString text)
{
  QString t = DriftingDateTime::currentDateTimeUtc().toString("hhmmss") + "            " + text;
  appendText (t, "hotpink");
}

void DisplayText::displayFoxToBeCalled(QString t, QColor bg)
{
  appendText(t,bg);
}

namespace
{
  void update_selection (QTextCursor& cursor, QColor const& bg, QColor const& fg)
  {
    if (!cursor.isNull ())
      {
        QTextCharFormat format {cursor.charFormat ()};
        if (bg.isValid ())
          {
            format.setBackground (bg);
          }
        else
          {
            format.clearBackground ();
          }
        if (fg.isValid ())
          {
            format.setForeground (fg);
          }
        else
          {
            format.clearForeground ();
          }
        cursor.mergeCharFormat (format);
      }
  }

  void reset_selection (QTextCursor& cursor)
  {
    if (!cursor.isNull ())
      {
        // restore previous text format, we rely on the text
        // char format at he start of the selection being the
        // old one which should be the case
        auto c2 = cursor;
        c2.setPosition (c2.selectionStart ());
        cursor.setCharFormat (c2.charFormat ());
      }
  }
}

void DisplayText::highlight_callsign (QString const& callsign, QColor const& bg, QColor const& fg, bool last_only)
{
  QTextCharFormat old_format {currentCharFormat ()};
  QTextCursor cursor {document ()};
  if (last_only)
    {
      cursor.movePosition (QTextCursor::End);
      cursor = document ()->find (callsign, cursor
                                  , QTextDocument::FindBackward | QTextDocument::FindWholeWords);
      if (bg.isValid () || fg.isValid ())
        {
          update_selection (cursor, bg, fg);
        }
      else
        {
          reset_selection (cursor);
        }
    }
  else
    {
      auto pos = highlighted_calls_.find (callsign);
      if (bg.isValid () || fg.isValid ())
        {
          auto colours = qMakePair (bg, fg);
          if (pos == highlighted_calls_.end ())
            {
              pos = highlighted_calls_.insert (callsign.toUpper (), colours);
            }
          else
            {
              pos.value () = colours; // update colours
            }
          while (!cursor.isNull ())
            {
              cursor = document ()->find (callsign, cursor, QTextDocument::FindWholeWords);
              update_selection (cursor, bg, fg);
            }
        }
      else if (pos != highlighted_calls_.end ())
        {
          highlighted_calls_.erase (pos);
          QTextCursor cursor {document ()};
          while (!cursor.isNull ())
            {
              cursor = document ()->find (callsign, cursor, QTextDocument::FindWholeWords);
              reset_selection (cursor);
            }
        }
    }
  setCurrentCharFormat (old_format);
}


code:
// -*- Mode: C++ -*-
#ifndef DISPLAYTEXT_H
#define DISPLAYTEXT_H

#include <QTextEdit>
#include <QFont>
#include <QHash>
#include <QPair>
#include <QString>

#include "logbook/logbook.h"
#include "decodedtext.h"

class QAction;

class DisplayText
  : public QTextEdit
{
  Q_OBJECT
public:
  explicit DisplayText(QWidget *parent = 0);

  void setContentFont (QFont const&);
  void insertLineSpacer(QString const&);
  void displayDecodedText(DecodedText const& decodedText, QString const& myCall, bool displayDXCCEntity,
        LogBook const& logBook, QColor color_CQ, QColor color_MyCall,
        QColor color_DXCC, QColor color_NewCall, bool ppfx, bool bCQonly=false);
  void displayTransmittedText(QString text, QString modeTx, qint32 txFreq,
			      QColor color_ReceivedMsg, bool bFastMode);
  void displayQSY(QString text);
  void displayFoxToBeCalled(QString t, QColor bg);

  Q_SIGNAL void selectCallsign (Qt::KeyboardModifiers);
  Q_SIGNAL void erased ();

  Q_SLOT void appendText (QString const& text, QColor bg = Qt::white
                          , QString const& call1 = QString {}, QString const& call2 = QString {});
  Q_SLOT void erase ();
  Q_SLOT void highlight_callsign (QString const& callsign, QColor const& bg, QColor const& fg, bool last_only);

protected:
  void mouseDoubleClickEvent(QMouseEvent *e);

private:
  bool m_bPrincipalPrefix;
  QString appendDXCCWorkedB4(QString message, QString const& callsign, QColor * bg, LogBook const& logBook,
			     QColor color_CQ, QColor color_DXCC, QColor color_NewCall);

  QFont char_font_;
  QAction * erase_action_;
  QHash<QString, QPair<QColor, QColor>> highlighted_calls_;
};

#endif // DISPLAYTEXT_H