Here's one from my distant past but which may be useful to me at some point today. I can actually see plenty of quicker ways to achieve this but it's also well tested for generating/checking credit card check digits:-
/// <summary> /// Creates/appends Luhn/Mod10 check digits to a specified numeric string /// Goes belly up with a text string. /// </summary> public class LuhnCheck { /// <summary> /// Initialise the LuhnCheck class with a number /// </summary> /// <param name="numberToAddDigitTo">The number to do work on</param> public LuhnCheck(string numberToAddDigitTo) { _numberToAddDigitTo = numberToAddDigitTo; setupok = true; } /// <summary> /// Initialise the LuhnCheck class without a number to start with /// </summary> public LuhnCheck() { _numberToAddDigitTo = ""; } private string _numberToAddDigitTo; /// <summary> /// The number to work on /// </summary> public string NumberToAddDigitTo { get { return _numberToAddDigitTo; } set { _numberToAddDigitTo = value; setupok = true; } } private bool setupok = false; /// <summary> /// Add the check digit to the string and return the whole string /// </summary> /// <returns>The whole string with added check digit</returns> public string AddCheckDigit() { if (setupok) { NumberToAddDigitTo += Convert.ToString(CreateCheckDigit()); return NumberToAddDigitTo; } else { throw new ArgumentException("Please specify the number to add the digit to."); } } /// <summary> /// Creates a check digit without using it /// </summary> /// <returns>The check digit</returns> public int CreateCheckDigit() { if (setupok) { return CreateCheckDigit(NumberToAddDigitTo); } else { throw new ArgumentException("Please specify the number to calculate the digit for."); } } /// <summary> /// Creates the check digit for a specified numeric string - it does not add it, only calculates it /// </summary> /// <param name="numberToAddDigitTo">The number to work the check digit out for</param> /// <returns>The check digit</returns> public int CreateCheckDigit(string numberToAddDigitTo) { string validChars = "0123456789"; foreach (char c in numberToAddDigitTo) { if (!(validChars.IndexOf(c) >= 0)) throw new FormatException("The value passed in was not a valid number"); } string tocheckdigit = numberToAddDigitTo + "0"; //We add zero to use it to calculate //the number from the correct point - since we'll be alternating between multiplying it by one or two int strlen = tocheckdigit.Length; int ncheck = 0; int nweight = 1; //this will alternate between one and two int intermediary = 0; string tosplit = ""; for (int i = 1; i <= strlen; i++) //loop through the card number string { intermediary = nweight * Convert.ToInt32(Convert.ToString(tocheckdigit[strlen - i])); //make intermediary //be the multiple of the variance and the card number if (intermediary > 9) //if it's bigger than ten... { tosplit = Convert.ToString(intermediary); //Split the two digits int firstdigit = Convert.ToInt32(Convert.ToString(tosplit[0])); int seconddigit = Convert.ToInt32(Convert.ToString(tosplit[1])); //Then add them together intermediary = firstdigit + seconddigit; } //Add the intermediary result to the rolling total ncheck += intermediary; //Change the variance as per the Luhn formula if (nweight == 2) { nweight = 1; } else { nweight = 2; } } //ncheck now has the total int remainder = 0; //Work out the remainder Math.DivRem(ncheck, 10, out remainder); //return ten minus the remainder (if it's zero then it divides perfectly by ten so is valid, //but since we're CREATING a check digit we want to know how far off the result is and how much we //need to correct it using the check digit) if (10 - remainder == 10) { return 0; } else { return 10 - remainder; } } /// <summary> /// Whether the number stored in the object is a valid MOD 10/Luhn check digited number /// </summary> public bool IsValid { get { if (CreateCheckDigit() == 0) { return true; } else { return false; } } } }Permalink