slyvain
10/22/2018 - 1:57 PM

Luhn algorithm implementation

Custom implementation of the Luhn algorithm to calculate an identification number based on an existing ID.

A cool one line C# implementation

/// <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;
}
/// <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));
}