/*
 * [TestCRC16.java]
 *
 * Summary: 16-bit CRC, cyclic redundancy checks, computed from scratch.
 *
 * Copyright: (c) 2009-2017 Roedy Green, Canadian Mind Products, http://mindprod.com
 *
 * Licence: This software may be copied and used freely for any purpose but military.
 *          http://mindprod.com/contact/nonmil.html
 *
 * Requires: JDK 1.8+
 *
 * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/
 *
 * Version History:
 *  1.0 2009-01-01 initial version
 */
package com.mindprod.example;

import com.mindprod.common18.EIO;

import java.io.UnsupportedEncodingException;

import static java.lang.System.*;

/**
 * 16-bit CRC, cyclic redundancy checks, computed from scratch.
 *
 * @author Roedy Green, Canadian Mind Products
 * @version 1.0 2009-01-01 initial version
 * @since 2009-01-01
 */
public final class TestCRC16
    {
    /**
     * generator polynomial
     */
    private static final int poly = 0x1021;/* x16 + x12 + x5 + 1 generator polynomial */
    /* 0x8408 used in European X.25 */

    /**
     * scrambler lookup table for fast computation.
     */
    private static final int[] crcTable = new int[ 256 ];

    static
        {
        // initialise scrambler table
        for ( int i = 0; i < 256; i++ )
            {
            int fcs = 0;
            int d = i << 8;
            for ( int k = 0; k < 8; k++ )
                {
                if ( ( ( fcs ^ d ) & 0x8000 ) != 0 )
                    {
                    fcs = ( fcs << 1 ) ^ poly;
                    }
                else
                    {
                    fcs = ( fcs << 1 );
                    }
                d <<= 1;
                fcs &= 0xffff;
                }
            crcTable[ i ] = fcs;
            }
        }

    /**
     * Calc 16-bit CRC with CCITT method.
     *
     * @param ba byte array to compute CRC on
     *
     * @return 16-bit CRC, unsigned
     */
    private static int crc16( byte[] ba )
        {
        // loop, calculating CRC for each byte of the string
        int work = 0xffff;
        for ( byte b : ba )
            {
            // xor the next data byte with the high byte of what we have so far to
            // look up the scrambler.
            // xor that with the low byte of what we have so far.
            // Mask back to 16 bits.
            work = ( crcTable[ ( b ^ ( work >>> 8 ) ) & 0xff ] ^ ( work << 8 ) ) &
                   0xffff;
            }
        return work;
        }

    /**
     * Test harness
     *
     * @param args not used
     *
     * @throws java.io.UnsupportedEncodingException in case UTF-8 support missing.
     */
    public static void main( String[] args ) throws UnsupportedEncodingException
        {
        final String s = "The quick brown fox jumped over the lazy dog's back; sample url: http://mindprod.com/jgloss/digest.html";
        final byte[] theTextToDigestAsBytes = s.getBytes( EIO.UTF8 );
        out.println( crc16( theTextToDigestAsBytes ) );
        long start = System.nanoTime();
        for ( int i = 0; i < 100_000100_000; i++ )
            {
            crc16( theTextToDigestAsBytes );
            }
        long stop = System.nanoTime();
        out.println( ( stop - start ) / 1_000_0001_000_000L + " seconds to compute 100,000 crc16 hashes" );
        // On my machine it takes: 40 seconds to compute 100,000 crc16 hashes
        }
    }