/*  $Id: crc16_arc.cs $ 
 *   Last updated:
 *   $Date: 2019-11-23 07:07Z $
 *   $Version: 1.1 $
 */

/******************************* LICENSE ***********************************
 * Copyright (C) 2019 David Ireland, DI Management Services Pty Limited.
 * All rights reserved. <www.di-mgt.com.au> <www.cryptosys.net>
 * The code in this module is licensed under the terms of the MIT license.  
 * For a copy, see <http://opensource.org/licenses/MIT>
****************************************************************************
*/

using System;
using System.Text;
using System.Diagnostics;

namespace crc16
{
    /* Compute CRC-16 checksum over input string.
    Ref: Catalogue of parametrised CRC algorithms with 16 bits
	http://reveng.sourceforge.net/crc-catalogue/16.htm
	CRC-16/ARC
	Alias: ARC, CRC-16, CRC-16/LHA, CRC-IBM
	width=16 poly=0x8005 init=0x0000 refin=true refout=true xorout=0x0000 check=0xbb3d residue=0x0000 name="CRC-16/ARC"
	CRC('123456789')=bb3d
    */

    class crc16_arc
    {
        static void Main(string[] args)
        {
            // Standard check
            CheckTest("123456789", 0xbb3d);
            // Other tests
            CheckTest("hello world", 0x39c1);
            CheckTest("a", 0xe8c1);
            CheckTest(" ", 0xd801);
            CheckTest("", 0x0000);
        }

        static bool CheckTest(string msg, UInt16 check)
        {
            UInt16 crc;
            crc = crc16(msg);
            Console.WriteLine("CRC-16('{0}')={1:X4}, CHECK={2:X4}", msg, crc, check);
            Debug.Assert(crc == check, "CRC check failed");

            return (crc == check);
        }

        /// <summary>
        /// Compute CRC-16/ARC checksum over input string.
        /// </summary>
        /// <param name="Str">Input string</param>
        /// <returns>CRC value as 16-bit unsigned integer</returns>
        static UInt16 crc16(string Str)
        {
            const UInt16 INIT = 0x0000;
            const UInt16 POLY = 0xA001;     // 0x8005 with bits reversed
            const UInt16 XOUT = 0x0000;
            
            // Convert string to byte array
            byte[] b = System.Text.Encoding.Default.GetBytes(Str);
            int len = b.Length;
            UInt16 temp;
            int i, j;

            UInt16 crc = INIT;
            for (i = 0; i < len; i++) {
                temp = (UInt16)((crc & 0xFF) ^ b[i]);
                for (j = 0; j < 8; j++) {
                    if ((temp & 0x1) > 0)
                        temp = (UInt16)((temp >> 1) ^ POLY);
                    else
                        temp >>= 1;
                }
                crc = (UInt16)((crc >> 8) ^ temp);
            }
            return (UInt16)(crc ^ XOUT);
        }
    }
}