/*=============================================================================
|
| NAME
|  
|     testCRC.cpp
|
| DESCRIPTION
|  
|     Unit test program for CRC generation and checking.
|
| AUTHOR
|
|      Sean O'Connor
|    
| NOTES/METHOD
|  
|     Create as a console application under Windows NT.
|
| LEGAL
|
|     CRCDemo Version 2.1 - A Program for generating and checking CRC codes.
|     Copyright (C) 1999-2017 by Sean Erik O'Connor.  All Rights Reserved.
|
|     This program is free software: you can redistribute it and/or modify
|     it under the terms of the GNU General Public License as published by
|     the Free Software Foundation, either version 3 of the License, or
|     (at your option) any later version.
|
|     This program is distributed in the hope that it will be useful,
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
|     GNU General Public License for more details.
|
|     You should have received a copy of the GNU General Public License
|     along with this program.  If not, see <http://www.gnu.org/licenses/>.
|     
|     The author's address is artificer!AT!seanerikoconnor!DOT!freeservers!DOT!com
|     with !DOT! replaced by . and the !AT! replaced by @
|
+============================================================================*/

#include <iostream>
#include <string>

using namespace std ;

#include "dataTypes.h"     // 
#include "shiftRegister.h" // Shift register class (needed for CRC code).
#include "crcCode.h"       // CRC code class.

string legalNotice(
    "\n"
    "CRCDemo Version 2.1 - A Program for generating and checking CRC codes.\n"
    "Copyright (C) 1999-2017 by Sean Erik O'Connor.  All Rights Reserved.\n"
    "\n"
    "CRCDemo comes with ABSOLUTELY NO WARRANTY; for details see the\n"
    "GNU General Public License.  This is free software, and you are welcome\n"
    "to redistribute it under certain conditions; see the GNU General Public License\n"
    "for details.\n"
) ;

int main( int argc, char * argv[] )
{
    // Show the legal notice first.
    cout << legalNotice << endl ;

    // Check parity word is 32 bits long.
    if (8 * sizeof(syndrome_t) != 32)
    {
        cerr << "ERROR:  make sure the typedef syndrome_t is a 32-bit unsigned integer on your system." << endl ;
        return 1 ;
    }

    cout << "\n\nCRC-16 test:  x ^ 16 + x ^ 15 + x ^ 2 + 1" << endl << endl ;

    // Construct CRC code object from the CRC's generator polynomial.
    // We use the standard code, CRC-16.
    CRCCode
        crc16( "x ^ 16 + x ^ 15 + x ^ 2 + 1" ) ;

    // Do an error check to see if the object was constructed OK.
    if (crc16.getNumParityBytes() == 0)
    {
        cerr << "ERROR:  Cannot create CRC-16" << endl ;
        return 1 ;
    }

    // Create a short sample message to be encoded.
    // Allow for 16 bits extra space at the end of the buffer for
    // parity bits.
    unsigned char buffer[ 18 ] =
    {
        0x0E, 0x00, 0x00, 0x00, 0x01, 0x05,
        0x02, 0x00, 0x63, 0x65, 0x6e, 0x74,
        0x75, 0x72, 0x61, 0x00,
        0x00, 0x00  // 16 bits extra space for parity bits.
    } ;

    long int bufferLen = 16 ;

    // Add the parity bits onto the end of the message to
    // create a systematically encoded codeword.
    crc16.addParityToBuffer( buffer, bufferLen ) ;

    // Update the buffer length now, since it has added parity bytes.
    long int bufferPlusParityLen = bufferLen + crc16.getNumParityBytes() ;

    // Check the codeword.
    syndrome_t shiftedSyndrome =
                      crc16.shiftedSyndrome( buffer, bufferPlusParityLen ) ;

    cout << "Shifted syndrome of codeword       = " << hex << shiftedSyndrome << " (should be zero)" << endl ;
    if (shiftedSyndrome != 0)
    {
        cerr << "ERROR:  shifted syndrome of codeword is nonzero for CRC-16" << endl ;
        return 1 ;
    }

    // Compute parity bits for the message.
    syndrome_t parityBits = crc16.parityBits( buffer, bufferLen ) ;

    cout << "Parity bits                        = " << (unsigned int)( ((parityBits & 0xFFFF0000) >> 16) ) << " (should be 35cf)" << endl ;
    if ((unsigned int)( ((parityBits & 0xFFFF0000) >> 16) ) != 0x35cf)
    {
        cerr << "ERROR:  parity bits incorrect for CRC-16" << endl ;
        return 1 ;
    }

    // Add noise to the codeword.
    buffer[ 2 ] |= 0x10 ;

    // Check the codeword.
    shiftedSyndrome = crc16.shiftedSyndrome( buffer, bufferLen ) ;

    cout << "Shifted syndrome of noisy codeword = " << shiftedSyndrome << " (should be non-zero)" << endl ;
    if (shiftedSyndrome == 0)
    {
        cerr << "ERROR:  shifted syndrome of noisy codeword is zero for CRC-16" << endl ;
        return 1 ;
    }


    //=============================================================

    // Create a short sample message to be encoded.
    unsigned char buffer32Q[ 6 ] =
    {
        0x01, 0x02,
        0x00, 0x00, 0x00, 0x00 // 32 bits extra space for parity bits.
    } ;

    bufferLen = 2 ;

    cout << "\n\nCRC-32Q test: x^32+x^31+x^24+x^22+x^16+x^14+x^8+x^7+x^5+x^3+x+1" << endl ;

    // Construct CRC code object from the CRC's generator polynomial.
    CRCCode
        crc32Q( "x^32+x^31+x^24+x^22+x^16+x^14+x^8+x^7+x^5+x^3+x+1" ) ;

    // Do an error check to see if the object was constructed OK.
    if (crc32Q.getNumParityBytes() == 0)
    {
        cerr << "ERROR:  Cannot create CRC-32Q" << endl ;
    }

    // Add the parity bits onto the end of the message to
    // create a systematically encoded codeword.
    crc32Q.addParityToBuffer( buffer32Q, bufferLen ) ;

    // Update the buffer length now, since it has added parity bytes.
    bufferPlusParityLen = bufferLen + crc32Q.getNumParityBytes() ;

    // Check the codeword.
    shiftedSyndrome = crc32Q.shiftedSyndrome( buffer32Q, bufferPlusParityLen ) ;

    cout << "Shifted syndrome of codeword       = " << shiftedSyndrome << " (should be zero)" << endl ;
    if (shiftedSyndrome != 0)
    {
        cerr << "ERROR:  shifted syndrome of codeword is nonzero for CRC-32Q" << endl ;
        return 1 ;
    }

    // Compute parity bits for the message.
    parityBits = crc32Q.parityBits( buffer32Q, bufferLen ) ;

    cout << "Parity bits                        = " << (unsigned int)(parityBits) << " (should be 03c371cf)" << endl ;
    if ((unsigned int)( parityBits ) != 0x03c371cf)
    {
        cerr << "ERROR:  parity bits incorrect for CRC-32Q" << endl ;
        return 1 ;
    }

    // Add noise to the codeword.
    buffer32Q[ 1 ] |= 0x10 ;

    // Check the codeword.
    shiftedSyndrome = crc32Q.shiftedSyndrome( buffer32Q, bufferLen ) ;

    cout << "Shifted syndrome of noisy codeword = " << shiftedSyndrome << " (should be non-zero)" << endl ;
    if (shiftedSyndrome == 0)
    {
        cerr << "ERROR:  shifted syndrome of noisy codeword is zero for CRC-32Q" << endl ;
        return 1 ;
    }


    //=============================================================

    cout << "\n\nCRC-CCITT test using shift register preset to FFFF" <<
            "and parity bit inversion:  x ^ 16 + x ^ 12 + x ^ 5 + 1" << endl ;

    // Construct CRC code object from the CRC's generator polynomial.
    // We use the standard code, CRC-CCITT, but we preset the shift
    // register contents to FFFF, and we 1's complement the parity
    // bits.
    CRCCode
        crcCCITT( "x ^ 16 + x ^ 12 + x ^ 5 + 1", 0xFFFF, true ) ;

    // Do an error check to see if the object was constructed OK.
    if (crcCCITT.getNumParityBytes() == 0)
    {
        cerr << "ERROR:  cannot create CRC-CCITT" << endl ;
    }

    // Create a short sample message to be encoded.
    // Allow for 16 bits extra space at the end of the buffer for
    // parity bits.
    unsigned char bufferITT[ 7 ] =
    {
        0x00, 0x05, 0x04, 0x00, 0x10,
        0x00, 0x00  // 16 bits extra space for parity bits.
    } ;

    bufferLen = 5 ;

    // Add the parity bits onto the end of the message to
    // create a systematically encoded codeword.
    crcCCITT.addParityToBuffer( bufferITT, bufferLen ) ;

    // Update the buffer length now, since it has added parity bytes.
    bufferPlusParityLen = bufferLen + crcCCITT.getNumParityBytes() ;

    // Check the codeword.
    shiftedSyndrome =
                  crcCCITT.shiftedSyndrome( bufferITT, bufferPlusParityLen ) ;

    cout << "Shifted syndrome of codeword       = " << shiftedSyndrome << " (should be zero)" << endl ;
    if (shiftedSyndrome != 0)
    {
        cerr << "ERROR:  shifted syndrome of codeword is nonzero for CRC-CCITT" << endl ;
        return 1 ;
    }

    // Compute parity bits for the message.
    parityBits = crcCCITT.parityBits( bufferITT, bufferLen ) ;

    cout << "Parity bits                        = " << (unsigned int)( ((parityBits & 0xFFFF0000) >> 16) ) << " (should be 9c47)" << endl ;
    if ((unsigned int)( ((parityBits & 0xFFFF0000) >> 16) ) != 0x9c47)
    {
        cerr << "ERROR:  parity bits incorrect for CRC-CCITT" << endl ;
        return 1 ;
    }

    // Add noise to the codeword.
    bufferITT[ 2 ] |= 0x10 ;

    // Check the codeword.
    shiftedSyndrome = crcCCITT.shiftedSyndrome( bufferITT, bufferLen ) ;

    cout << "Shifted syndrome of noisy codeword = " << shiftedSyndrome << " (should be non-zero)" << endl ;
    if (shiftedSyndrome == 0)
    {
        cerr << "ERROR:  shifted syndrome of noisy codeword is zero for CRC-CCITT" << endl ;
        return 1 ;
    }

    //=============================================================

    cout << "\n\nCRC-DNP test using shift register preset to 0 " <<
            "and no parity bit inversion" <<
            "x^16 + x^13 + x^12 + x^11 + x^10 + x^8 + x^6 + x^5 + x^2 + 1" << endl ;

    // Construct CRC code object from the CRC's generator polynomial.
    CRCCode
        crcDNP(
        "x^16 + x^13 + x^12 + x^11 + x^10 + x^8 + x^6 + x^5 + x^2 + 1",
                 0x0, false ) ;

    // Do an error check to see if the object was constructed OK.
    if (crcDNP.getNumParityBytes() == 0)
    {
        cerr << "ERROR:  cannot create CRC-DNP" << endl ;
    }

    // Create a short sample message to be encoded.
    // Allow for 16 bits extra space at the end of the buffer for
    // parity bits.
    unsigned char bufferDNP[ 10 ] =
    {
        0x05, 0x64, 0x11, 0xc4, 0x65, 0x00, 0x02, 0x00,
        0x00, 0x00  // 16 bits extra space for parity bits.
    } ;

    bufferLen = 8 ;

    // Add the parity bits onto the end of the message to
    // create a systematically encoded codeword.
    crcDNP.addParityToBuffer( bufferDNP, bufferLen ) ;

    // Update the buffer length now, since it has added parity bytes.
    bufferPlusParityLen = bufferLen + crcDNP.getNumParityBytes() ;

    // Check the codeword.
    shiftedSyndrome = crcDNP.shiftedSyndrome( bufferDNP, bufferPlusParityLen ) ;

    cout << "Shifted syndrome of codeword       = " << shiftedSyndrome << " (should be zero)" << endl ;
    if (shiftedSyndrome != 0)
    {
        cerr << "ERROR:  shifted syndrome of codeword is nonzero for CRC-DNP" << endl ;
        return 1 ;
    }

    // Compute parity bits for the message.
    parityBits = crcDNP.parityBits( bufferDNP, bufferLen ) ;

    cout << "Parity bits                        = " << (unsigned int)( ((parityBits & 0xFFFF0000) >> 16) ) << " (should be b1ae)" << endl ;

    // Add noise to the codeword.
    bufferDNP[ 2 ] |= 0xE0 ;

    // Check the codeword.
    shiftedSyndrome = crcDNP.shiftedSyndrome( bufferDNP, bufferLen ) ;

    cout << "Shifted syndrome of noisy codeword = " << shiftedSyndrome << " (should be non-zero)" << endl ;
    if (shiftedSyndrome == 0)
    {
        cerr << "ERROR:  shifted syndrome of noisy codeword is zero for CRC-DNP" << endl ;
        return 1 ;
    }

    return( 0 ) ;
}