Custom implementation of the Luhn algorithm to calculate an identification number based on an existing ID.
A cool one line C# implementation
Custom implementation of the Luhn algorithm to calculate an identification number based on an existing ID.
A cool one line C# implementation
/// <summary> | |
/// Generates an 8 digit Luhn identification (with checksum) wrapping the given internal ID. | |
/// </summary> | |
/// <param name="internalId">Internal ID</param> | |
/// <returns>Generated Luhn identification number</returns> | |
private long GenerateLuhnNumber(long internalId) | |
{ | |
int baseLength = 8; | |
// generate a 8 digits sequence starting with the number of digits in the internalId | |
// and complete with the internalId with leading 0s | |
// eg. 30_000_845 => id is 845, and counts 3 digits | |
// or 75_000_109 => id is 5000109 and counts 7 digits | |
var sequence = new List<int>(baseLength); | |
sequence.Add(internalId.ToString().Length); | |
sequence.AddRange(internalId.ToString("d7").Select(x => Convert.ToInt32(x.ToString()))); | |
// the last number is the check digit, use Luhn algorithm to calculate it: | |
// double every 2 numbers (starting from the rightmost), if result > 9, retract 9 | |
// eg. 2*4 = 8 -> ok | |
// 2*8 = 16 -> not ok; 16-9 = 7 -> ok | |
bool alt = true; | |
int sumSequence = 0; | |
for (int i = sequence.Count - 1; i >= 0; i--) | |
{ | |
var tmp = sequence[i]; | |
if (alt) | |
{ | |
tmp *= 2; | |
if (tmp > 9) | |
{ | |
tmp -= 9; | |
} | |
} | |
sumSequence += tmp; | |
alt = !alt; | |
} | |
// now, calculate the checkdigit (multiply the sum by nine, and get the unit value) | |
// and add it at the end of the sequence | |
sequence.Add(sumSequence * 9 % 10); | |
return Convert.ToInt64(String.Join("", sequence)); | |
} |
/// <summary> | |
/// Extracts the internal ID wrapped in a Luhn identification number if it is valid. | |
/// </summary> | |
/// <param name="input">Luhn identification number</param> | |
/// <returns>The internal ID or NULL by default</returns> | |
private long? VerifyLuhnNumber(string input) | |
{ | |
// keep only numeric characters | |
input = new string(input.Where(c => char.IsDigit(c)).ToArray()); | |
// extract the digits | |
int[] sequence = input.Select(x => Convert.ToInt32(x.ToString())).ToArray(); | |
// calculate the checksum starting from the 2nd rightmost characters (rightmost being the checkdigit) | |
int checkSum = 0; | |
bool alt = true; | |
for (int i = sequence.Length - 2; i >= 0; i--) | |
{ | |
var tmp = sequence[i]; | |
if (alt) | |
{ | |
tmp *= 2; | |
if (tmp > 9) | |
{ | |
tmp -= 9; | |
} | |
} | |
checkSum += tmp; | |
alt = !alt; | |
} | |
// add the check digit | |
checkSum += sequence.Last(); | |
if (checkSum % 10 == 0) | |
{ | |
// valid, extract the internal Id | |
int idLength = sequence[0]; | |
var tmpIdStr = String.Join("", sequence.Skip(sequence.Count() - 1 - idLength).Take(idLength)); | |
if (tmpIdStr.Any()) | |
{ | |
long tmpValidLong; | |
if (long.TryParse(tmpIdStr, out tmpValidLong)) | |
{ | |
return tmpValidLong; | |
} | |
} | |
} | |
// return NULL by default | |
return null; | |
} |