//  Boost CRC unit test program file  ----------------------------------------//

//  Copyright 2011 Daryle Walker.
//  Distributed under the Boost Software License, Version 1.0.  (See the
//  accompanying file LICENSE_1_0.txt or a copy at
//  <http://www.boost.org/LICENSE_1_0.txt>.)

//  See <http://www.boost.org/libs/crc/> for the library's home page.

#include <boost/crc.hpp>                      // for boost::crc_basic,crc_optimal,augmented_crc,crc
#include <boost/core/bit.hpp>                 // for boost::core::byteswap, boost::core::endian
#include <boost/core/detail/minstd_rand.hpp>  // for boost::detail::minstd_rand
#include <boost/core/lightweight_test.hpp>    // for BOOST_TEST_EQ, et al
#include <boost/cstdint.hpp>                  // for boost::uint16_t, uint32_t, uintmax_t
#include <boost/integer.hpp>                  // for boost::uint_t

#include <algorithm>    // for std::generate_n, for_each
#include <climits>      // for CHAR_BIT
#include <cstddef>      // for std::size_t
#include <type_traits>  // for std::integral_constant

// Sanity check
#if CHAR_BIT != 8
#error The expected results assume octet-sized bytes.
#endif

// Control tests at compile-time
#ifndef CONTROL_SUB_BYTE_MISMATCHED_REFLECTION_TEST
#define CONTROL_SUB_BYTE_MISMATCHED_REFLECTION_TEST  1
#endif

#define BOOST_AUTO(var, exp) auto var = exp

// Common definitions  -------------------------------------------------------//

namespace {

// Many CRC configurations use the string "123456789" in ASCII as test data.
unsigned char const  std_data[] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
                      0x38, 0x39 };
std::size_t const    std_data_len = sizeof( std_data ) / sizeof( std_data[0] );

// Checksums of the standard test data for common configurations
boost::uint16_t const  std_crc_ccitt_false_result = 0x29B1u;
boost::uint16_t const  std_crc_ccitt_true_result = 0x2189u;
boost::uint16_t const  std_crc_xmodem_result = 0x31C3u;
boost::uint16_t const  std_crc_16_result = 0xBB3Du;
boost::uint32_t const  std_crc_32_result = 0xCBF43926ul;

// Conversion functions between native- and big-endian representations

inline boost::uint32_t native_to_big_impl( boost::uint32_t x, std::true_type )
{
    return x;
}

inline boost::uint32_t native_to_big_impl( boost::uint32_t x, std::false_type )
{
    return boost::core::byteswap( x );
}

inline boost::uint32_t native_to_big( boost::uint32_t x )
{
    return native_to_big_impl( x, std::integral_constant<bool, boost::core::endian::native == boost::core::endian::big>() );
}

// Define CRC parameters inside traits classes.  Probably will use this in a
// future version of the CRC libray!
template < std::size_t Bits >
class my_crc_rt_traits
{
public:
    typedef std::integral_constant<std::size_t, Bits>            register_length_c;
    typedef typename boost::uint_t<Bits>::fast  register_type;
    typedef boost::crc_basic<Bits>              computer_type;

    register_type  divisor_polynominal;
    register_type  initial_remainder;
    bool           reflect_input_byte;
    bool           reflect_output_remainder;
    register_type  final_xor_mask;

    computer_type  make_crc_basic() const
    { return computer_type(divisor_polynominal, initial_remainder,
     final_xor_mask, reflect_input_byte, reflect_output_remainder); }
};

template < std::size_t Bits, boost::uintmax_t DivisorPolynominal,
           boost::uintmax_t InitialRemainder, bool ReflectInputBytes,
           bool ReflectOutputRemainder, boost::uintmax_t FinalXorMask >
class my_crc_ct_traits
{
public:
    typedef my_crc_rt_traits<Bits>                           rt_adaptor_type;
    typedef typename rt_adaptor_type::register_type            register_type;
    typedef boost::crc_optimal<Bits, DivisorPolynominal, InitialRemainder,
     FinalXorMask, ReflectInputBytes, ReflectOutputRemainder>  computer_type;

    typedef std::integral_constant<std::size_t, Bits>  register_length_c;
    typedef std::integral_constant<register_type, DivisorPolynominal>
      divisor_polynominal_c;
    typedef std::integral_constant<register_type, InitialRemainder>
      initial_remainder_c;
    typedef std::integral_constant<bool, ReflectInputBytes>  reflect_input_byte_c;
    typedef std::integral_constant<bool, ReflectOutputRemainder>
      reflect_output_remainder_c;
    typedef std::integral_constant<register_type, FinalXorMask>
      final_xor_mask_c;

    operator rt_adaptor_type() const
    {
        rt_adaptor_type const  result = { divisor_polynominal_c::value,
         initial_remainder_c::value, reflect_input_byte_c::value,
         reflect_output_remainder_c::value, final_xor_mask_c::value };

        return result;
    }

    static  computer_type  make_crc_optimal()
    { return boost::crc_optimal<register_length_c::value,
     divisor_polynominal_c::value, initial_remainder_c::value,
     final_xor_mask_c::value, reflect_input_byte_c::value,
     reflect_output_remainder_c::value>(); }
};

template < std::size_t Bits, boost::uintmax_t DivisorPolynominal,
           boost::uintmax_t InitialRemainder, bool ReflectInputBytes,
           bool ReflectOutputRemainder, boost::uintmax_t FinalXorMask,
           boost::uintmax_t StandardTestDataResult >
class my_crc_test_traits
{
public:
    typedef my_crc_ct_traits<Bits, DivisorPolynominal, InitialRemainder,
     ReflectInputBytes, ReflectOutputRemainder, FinalXorMask>  ct_traits_type;
    typedef my_crc_rt_traits<Bits>  rt_traits_type;

    typedef typename rt_traits_type::register_type  register_type;

    typedef std::integral_constant<std::size_t, Bits>  register_length_c;
    typedef std::integral_constant<register_type, DivisorPolynominal>
      divisor_polynominal_c;
    typedef std::integral_constant<register_type, InitialRemainder>
      initial_remainder_c;
    typedef std::integral_constant<bool, ReflectInputBytes>  reflect_input_byte_c;
    typedef std::integral_constant<bool, ReflectOutputRemainder>
      reflect_output_remainder_c;
    typedef std::integral_constant<register_type, FinalXorMask>
      final_xor_mask_c;
    typedef std::integral_constant<register_type, StandardTestDataResult>
      standard_test_data_CRC_c;

    typedef typename ct_traits_type::computer_type  computer_ct_type;
    typedef typename rt_traits_type::computer_type  computer_rt_type;

    static  computer_ct_type  make_crc_optimal()
    { return ct_traits_type::make_crc_optimal(); }
    static  computer_rt_type  make_crc_basic()
    { return ct_traits_type().operator rt_traits_type().make_crc_basic(); }
};

// Now make some example CRC profiles
typedef my_crc_test_traits<16u, 0x8005u, 0u, true, true, 0u, std_crc_16_result>
  my_crc_16_traits;
typedef my_crc_test_traits<16u, 0x1021u, 0xFFFFu, false, false, 0u,
 std_crc_ccitt_false_result>  my_crc_ccitt_false_traits;
typedef my_crc_test_traits<16u, 0x1021u, 0u, true, true, 0u,
 std_crc_ccitt_true_result>  my_crc_ccitt_true_traits;
typedef my_crc_test_traits<16u, 0x1021u, 0u, false, false, 0u,
 std_crc_xmodem_result>  my_crc_xmodem_traits;
typedef my_crc_test_traits<32u, 0x04C11DB7ul, 0xFFFFFFFFul, true, true,
 0xFFFFFFFFul, std_crc_32_result>  my_crc_32_traits;

template<class Test>
void run_crc_test_policies()
{
    Test()(my_crc_16_traits());
    Test()(my_crc_ccitt_false_traits());
    Test()(my_crc_ccitt_true_traits());
    Test()(my_crc_xmodem_traits());
    Test()(my_crc_32_traits());
}

// Need to test when ReflectInputBytes and ReflectOutputRemainder differ
// (Grabbed from table at <http://regregex.bbcmicro.net/crc-catalogue.htm>.)
typedef my_crc_test_traits<6u, 0x19u, 0u, true, false, 0u, 0x19u>
  my_crc_6_darc_traits;
typedef my_crc_test_traits<12u, 0x80Fu, 0u, false, true, 0u, 0xDAFu>
  my_crc_12_3gpp_traits;

template<class Test>
void run_crc_extended_test_policies()
{
    Test()(my_crc_16_traits());
    Test()(my_crc_ccitt_false_traits());
    Test()(my_crc_ccitt_true_traits());
    Test()(my_crc_xmodem_traits());
    Test()(my_crc_32_traits());
#if CONTROL_SUB_BYTE_MISMATCHED_REFLECTION_TEST
    Test()(my_crc_6_darc_traits());
#endif
    Test()(my_crc_12_3gpp_traits());
}

// Bit mask constants
template < std::size_t BitIndex >
struct high_bit_mask_c
    : boost::detail::high_bit_mask_c<BitIndex>
{};
template < std::size_t BitCount >
struct low_bits_mask_c
    : boost::detail::low_bits_mask_c<BitCount>
{};

}  // anonymous namespace


// Unit tests  ---------------------------------------------------------------//

struct computation_comparison_test {
template<class CRCPolicy>
void operator()(CRCPolicy)
{
    BOOST_AUTO( crc_f, CRCPolicy::make_crc_optimal() );
    BOOST_AUTO( crc_s, CRCPolicy::make_crc_basic() );
    typename CRCPolicy::register_type const  func_result
     = boost::crc<CRCPolicy::register_length_c::value,
        CRCPolicy::divisor_polynominal_c::value,
        CRCPolicy::initial_remainder_c::value,
        CRCPolicy::final_xor_mask_c::value,
        CRCPolicy::reflect_input_byte_c::value,
        CRCPolicy::reflect_output_remainder_c::value>( std_data, std_data_len );

    crc_f.process_bytes( std_data, std_data_len );
    crc_s.process_bytes( std_data, std_data_len );

    BOOST_TEST_EQ( crc_f.checksum(),
     CRCPolicy::standard_test_data_CRC_c::value );
    BOOST_TEST_EQ( crc_s.checksum(),
     CRCPolicy::standard_test_data_CRC_c::value );
    BOOST_TEST_EQ( CRCPolicy::standard_test_data_CRC_c::value,
     func_result );
}
};

struct accessor_and_split_run_test {
template<class CRCPolicy>
void operator()(CRCPolicy)
{
    typedef typename CRCPolicy::computer_ct_type  optimal_crc_type;
    typedef typename CRCPolicy::computer_rt_type    basic_crc_type;

    // Test accessors
    optimal_crc_type  faster_crc1;
    basic_crc_type    slower_crc1( faster_crc1.get_truncated_polynominal(),
     faster_crc1.get_initial_remainder(), faster_crc1.get_final_xor_value(),
     faster_crc1.get_reflect_input(), faster_crc1.get_reflect_remainder() );

    BOOST_TEST_EQ( faster_crc1.get_interim_remainder(),
     slower_crc1.get_initial_remainder() );

    // Process the first half of the standard data
    std::size_t const  mid_way = std_data_len / 2u;

    faster_crc1.process_bytes( std_data, mid_way );
    slower_crc1.process_bytes( std_data, mid_way );

    BOOST_TEST_EQ( faster_crc1.checksum(), slower_crc1.checksum() );

    // Process the second half of the standard data, testing more accessors
    unsigned char const * const  std_data_end = std_data + std_data_len;
    boost::crc_optimal<optimal_crc_type::bit_count,
     optimal_crc_type::truncated_polynominal,
     optimal_crc_type::initial_remainder, optimal_crc_type::final_xor_value,
     optimal_crc_type::reflect_input, optimal_crc_type::reflect_remainder>
      faster_crc2( faster_crc1.get_interim_remainder() );
    boost::crc_basic<basic_crc_type::bit_count>  slower_crc2(
     slower_crc1.get_truncated_polynominal(),
     slower_crc1.get_interim_remainder(), slower_crc1.get_final_xor_value(),
     slower_crc1.get_reflect_input(), slower_crc1.get_reflect_remainder() );

    faster_crc2.process_block( std_data + mid_way, std_data_end );
    slower_crc2.process_block( std_data + mid_way, std_data_end );

    BOOST_TEST_EQ( slower_crc2.checksum(), faster_crc2.checksum() );
    BOOST_TEST_EQ( faster_crc2.checksum(),
     CRCPolicy::standard_test_data_CRC_c::value );
    BOOST_TEST_EQ( CRCPolicy::standard_test_data_CRC_c::value,
     slower_crc2.checksum() );
}
};

struct reset_and_single_bit_error_test {
template<class CRCPolicy>
void operator()(CRCPolicy)
{
    // A single-bit error in a CRC can be guaranteed to be detected if the
    // modulo-2 polynomial divisor has at least two non-zero coefficients.  The
    // implicit highest coefficient is always one, so that leaves an explicit
    // coefficient, i.e. at least one of the polynomial's bits is set.
    BOOST_TEST( CRCPolicy::divisor_polynominal_c::value &
     low_bits_mask_c<CRCPolicy::register_length_c::value>::value );

    // Create a random block of data
    boost::uint32_t    ran_data[ 256 ];
    std::size_t const  ran_length = sizeof(ran_data) / sizeof(ran_data[0]);

    std::generate_n( ran_data, ran_length, boost::detail::minstd_rand() );

    // Create computers and compute the checksum of the data
    BOOST_AUTO( optimal_tester, CRCPolicy::make_crc_optimal() );
    BOOST_AUTO( basic_tester, CRCPolicy::make_crc_basic() );

    optimal_tester.process_bytes( ran_data, sizeof(ran_data) );
    basic_tester.process_bytes( ran_data, sizeof(ran_data) );

    BOOST_AUTO( const optimal_checksum, optimal_tester.checksum() );
    BOOST_AUTO( const basic_checksum, basic_tester.checksum() );

    BOOST_TEST_EQ( optimal_checksum, basic_checksum );

    // Do the checksum again, while testing the capability to reset the current
    // remainder (to either a default or a given value)
    optimal_tester.reset();
    basic_tester.reset( CRCPolicy::initial_remainder_c::value );

    optimal_tester.process_bytes( ran_data, sizeof(ran_data) );
    basic_tester.process_bytes( ran_data, sizeof(ran_data) );

    BOOST_TEST_EQ( optimal_tester.checksum(), basic_tester.checksum() );
    BOOST_TEST_EQ( optimal_tester.checksum(), optimal_checksum );
    BOOST_TEST_EQ( basic_tester.checksum(), basic_checksum );

    // Introduce a single-bit error
    ran_data[ ran_data[0] % ran_length ] ^= ( 1u << (ran_data[ 1 ] % 32u) );

    // Compute the checksum of the errorenous data, while continuing to test
    // the remainder-resetting methods
    optimal_tester.reset( CRCPolicy::initial_remainder_c::value );
    basic_tester.reset();

    optimal_tester.process_bytes( ran_data, sizeof(ran_data) );
    basic_tester.process_bytes( ran_data, sizeof(ran_data) );

    BOOST_TEST_EQ( basic_tester.checksum(), optimal_tester.checksum() );
    BOOST_TEST_NE( optimal_checksum, optimal_tester.checksum() );
    BOOST_TEST_NE( basic_checksum, basic_tester.checksum() );
}
};

void augmented_crc_test()
{
    using std::size_t;
    using boost::uint32_t;
    using boost::augmented_crc;

    // Common CRC parameters, all others are zero/false
    static  size_t const    bits = 32u;
    static  uint32_t const  poly = 0x04C11DB7ul;

    // Create a random block of data, with space at the end for a CRC
    static  size_t const  data_length = 256u;
    static  size_t const  run_length = data_length + 1u;

    uint32_t      run_data[ run_length ];
    uint32_t &    run_crc = run_data[ data_length ];
    size_t const  data_size = sizeof( run_data ) - sizeof( run_crc );

    std::generate_n( run_data, data_length, boost::detail::minstd_rand() );
    run_crc = 0u;

    // The augmented-CRC routine needs to push an appropriate number of zero
    // bits (the register size) through before the checksum can be extracted.
    // The other CRC methods, which are un-augmented, don't need to do this.
    uint32_t const  checksum = boost::crc<bits, poly, 0u, 0u, false, false>(
     run_data, data_size );

    BOOST_TEST_EQ( (augmented_crc<bits, poly>)(run_data, sizeof( run_data
     )), checksum );

    // Now appending a message's CRC to the message should lead to a zero-value
    // checksum.  Note that the CRC must be read from the largest byte on down,
    // i.e. big-endian!
    run_crc = native_to_big( checksum );
    BOOST_TEST_EQ( (augmented_crc<bits, poly>)(run_data, sizeof( run_data
     )), 0u );

    // Check again with the non-augmented methods
    boost::crc_basic<bits>  crc_b( poly );

    crc_b.process_bytes( run_data, data_size );
    BOOST_TEST_EQ( crc_b.checksum(), checksum );

    // Introduce a single-bit error, now the checksum shouldn't match!
    uint32_t const  affected_word_index = run_data[ 0 ] % data_length;
    uint32_t const  affected_bit_index = run_data[ 1 ] % 32u;
    uint32_t const  affecting_mask = 1ul << affected_bit_index;

    run_data[ affected_word_index ] ^= affecting_mask;

    crc_b.reset();
    crc_b.process_bytes( run_data, data_size );
    BOOST_TEST_NE( crc_b.checksum(), checksum );

    BOOST_TEST_NE( (augmented_crc<bits, poly>)(run_data, sizeof( run_data )),
     0u );

    run_crc = 0u;
    BOOST_TEST_NE( (augmented_crc<bits, poly>)(run_data, sizeof( run_data )),
     checksum );

    // Now introduce the single error in the checksum instead
    run_data[ affected_word_index ] ^= affecting_mask;
    run_crc = native_to_big( checksum ) ^ affecting_mask;
    BOOST_TEST_NE( (augmented_crc<bits, poly>)(run_data, sizeof( run_data )),
     0u );

    // Repeat these tests with a non-zero initial remainder.  Before we can
    // check the results against a non-augmented CRC computer, realize that they
    // interpret the inital remainder differently.  However, the two standards
    // can convert between each other.
    // (checksum2 initial value is as a scratch pad.  So are the address and new
    //  value of run_crc, but it's also useful for the next sub-step.)
    // (TODO: getting the equivalent unaugmented-CRC initial-remainder given an
    //  augmented-CRC initial-remainder is done by putting said augmented-CRC
    //  initial-remainder through the augmented-CRC computation with a
    //  zero-value message.  I don't know how to go the other way, yet.)
    run_crc = 0u;
    uint32_t        checksum2 = run_data[ run_data[2] % data_length ];
    uint32_t const  initial_residue = checksum2 + !checksum2;  // ensure nonzero
    uint32_t const  initial_residue_unaugmented = augmented_crc<bits, poly>(
                     &run_crc, sizeof(run_crc), initial_residue );

    BOOST_TEST_NE( initial_residue, 0u );
    crc_b.reset( initial_residue_unaugmented );
    crc_b.process_bytes( run_data, data_size );
    checksum2 = crc_b.checksum();

    BOOST_TEST_EQ( run_crc, 0u );
    BOOST_TEST_EQ( (augmented_crc<bits, poly>)(run_data, sizeof( run_data ),
     initial_residue), checksum2 );
    run_crc = native_to_big( checksum2 );
    BOOST_TEST_EQ( (augmented_crc<bits, poly>)(run_data, sizeof( run_data ),
     initial_residue), 0u );

    // Use the inital remainder argument to split a CRC-computing run
    size_t const    split_index = data_length / 2u;
    uint32_t const  intermediate = augmented_crc<bits, poly>( run_data,
                     sizeof(run_crc) * split_index, initial_residue );

    BOOST_TEST_EQ( (augmented_crc<bits, poly>)(&run_data[ split_index ],
     sizeof( run_data ) - sizeof( run_crc ) * split_index, intermediate), 0u );
    run_crc = 0u;
    BOOST_TEST_EQ( (augmented_crc<bits, poly>)(&run_data[ split_index ],
     sizeof( run_data ) - sizeof( run_crc ) * split_index, intermediate),
     checksum2 );

    // Repeat the single-bit error test, with a non-zero initial-remainder
    run_data[ run_data[3] % data_length ] ^= ( 1ul << (run_data[4] % 32u) );
    run_crc = native_to_big( checksum2 );
    BOOST_TEST_NE( (augmented_crc<bits, poly>)(run_data, sizeof( run_data ),
     initial_residue), 0u );
}
    
// Optimal computer, via the single-run function
unsigned crc_f1( const void * buffer, std::size_t byte_count )
{
    return boost::crc<3u, 0x03u, 0u, 0u, false, false>( buffer, byte_count );
}

void sub_nybble_polynominal_test()
{
    // The CRC standard is a SDH/SONET Low Order LCAS control word with CRC-3
    // taken from ITU-T G.707 (12/03) XIII.2.

    // Four samples, each four bytes; should all have a CRC of zero
    unsigned char const  samples[4][4]
      = {
            { 0x3Au, 0xC4u, 0x08u, 0x06u },
            { 0x42u, 0xC5u, 0x0Au, 0x41u },
            { 0x4Au, 0xC5u, 0x08u, 0x22u },
            { 0x52u, 0xC4u, 0x08u, 0x05u }
        };

    // Basic computer
    boost::crc_basic<3u>  crc_1( 0x03u );

    crc_1.process_bytes( samples[0], 4u );
    BOOST_TEST_EQ( crc_1.checksum(), 0u );

    crc_1.reset();
    crc_1.process_bytes( samples[1], 4u );
    BOOST_TEST_EQ( crc_1.checksum(), 0u );

    crc_1.reset();
    crc_1.process_bytes( samples[2], 4u );
    BOOST_TEST_EQ( crc_1.checksum(), 0u );

    crc_1.reset();
    crc_1.process_bytes( samples[3], 4u );
    BOOST_TEST_EQ( crc_1.checksum(), 0u );

    BOOST_TEST_EQ( crc_f1(samples[ 0 ], 4u), 0u );
    BOOST_TEST_EQ( crc_f1(samples[ 1 ], 4u), 0u );
    BOOST_TEST_EQ( crc_f1(samples[ 2 ], 4u), 0u );
    BOOST_TEST_EQ( crc_f1(samples[ 3 ], 4u), 0u );

    // TODO: do similar tests with boost::augmented_crc<3, 0x03>
    // (Now I think that this can't be done right now, since that function reads
    // byte-wise, so the register size needs to be a multiple of CHAR_BIT.)
}

// Optimal computer, via the single-run function
unsigned crc_f2( const void * buffer, std::size_t byte_count )
{
    return boost::crc<7u, 0x09u, 0u, 0u, false, false>( buffer, byte_count );
}

void sub_octet_polynominal_test()
{
    // The CRC standard is a SDH/SONET J0/J1/J2/N1/N2/TR TTI (trace message)
    // with CRC-7, o.a. ITU-T G.707 Annex B, G.832 Annex A.

    // Two samples, each sixteen bytes
    // Sample 1 is '\x80' + ASCII("123456789ABCDEF")
    // Sample 2 is '\x80' + ASCII("TTI UNAVAILABLE")
    unsigned char const  samples[2][16]
      = {
            { 0x80u, 0x31u, 0x32u, 0x33u, 0x34u, 0x35u, 0x36u, 0x37u, 0x38u,
              0x39u, 0x41u, 0x42u, 0x43u, 0x44u, 0x45u, 0x46u },
            { 0x80u, 0x54u, 0x54u, 0x49u, 0x20u, 0x55u, 0x4Eu, 0x41u, 0x56u,
              0x41u, 0x49u, 0x4Cu, 0x41u, 0x42u, 0x4Cu, 0x45u }
        };
    unsigned const       results[2] = { 0x62u, 0x23u };

    // Basic computer
    boost::crc_basic<7u>  crc_1( 0x09u );

    crc_1.process_bytes( samples[0], 16u );
    BOOST_TEST_EQ( crc_1.checksum(), results[0] );

    crc_1.reset();
    crc_1.process_bytes( samples[1], 16u );
    BOOST_TEST_EQ( crc_1.checksum(), results[1] );

    BOOST_TEST_EQ( crc_f2(samples[ 0 ], 16u), results[0] );
    BOOST_TEST_EQ( crc_f2(samples[ 1 ], 16u), results[1] );

    // TODO: do similar tests with boost::augmented_crc<7, 0x09>
    // (Now I think that this can't be done right now, since that function reads
    // byte-wise, so the register size needs to be a multiple of CHAR_BIT.)
}

void one_bit_polynominal_test()
{
    // Try a CRC based on the (x + 1) polynominal, which is a factor in
    // many real-life polynominals and doesn't fit evenly in a byte.
    boost::crc_basic<1u>  crc_1( 1u );

    crc_1.process_bytes( std_data, std_data_len );
    BOOST_TEST_EQ( crc_1.checksum(), 1u );

    // Do it again, but using crc_optimal.  The real purpose of this is to test
    // crc_optimal::process_byte, which doesn't get exercised anywhere else in
    // this file (directly or indirectly).
    boost::crc_optimal<1u, 1u, 0u, 0u, false, false>  crc_2;

    for ( std::size_t  i = 0u ; i < std_data_len ; ++i )
        crc_2.process_byte( std_data[i] );
    BOOST_TEST_EQ( crc_2.checksum(), 1u );
}

struct function_object_test {
template<class CRCPolicy>
void operator()(CRCPolicy)
{
    typename CRCPolicy::computer_ct_type  crc_c;

    crc_c = std::for_each( std_data, std_data + std_data_len, crc_c );
    BOOST_TEST_EQ( crc_c(), CRCPolicy::standard_test_data_CRC_c::value );
}
};

// Ticket #2492: crc_optimal with reversed CRC16
// <https://svn.boost.org/trac/boost/ticket/2492>
void issue_2492_test()
{
    // I'm trusting that the original bug reporter got his/her calculations
    // correct.
    boost::uint16_t const  expected_result = 0xF990u;

    boost::crc_optimal<16, 0x100Bu, 0xFFFFu, 0x0000, true, false>  boost_crc_1;
    boost::crc_basic<16>  boost_crc_2( 0x100Bu, 0xFFFFu, 0x0000, true, false );

    // This should be right...
    boost_crc_1.process_byte( 0u );
    BOOST_TEST_EQ( boost_crc_1.checksum(), expected_result );

    // ...but the reporter said this didn't reflect, giving 0x099F as the
    // (wrong) result.  However, I get the right answer!
    boost_crc_2.process_byte( 0u );
    BOOST_TEST_EQ( boost_crc_2.checksum(), expected_result );
}

int main()
{
    run_crc_extended_test_policies<computation_comparison_test>();
    run_crc_test_policies<accessor_and_split_run_test>();
    run_crc_test_policies<reset_and_single_bit_error_test>();
    augmented_crc_test();
    sub_nybble_polynominal_test();
    sub_octet_polynominal_test();
    one_bit_polynominal_test();
    run_crc_test_policies<function_object_test>();
    issue_2492_test();
    return boost::report_errors();
}
