Implementing Luhn algorithm using C#
Asked Answered
G

11

13

I am using following code to implement Luhn algorithm for credit card check in C# language, but could not get the output to generate the check sum its showing validity. Kindly help me. Thank you in advance.

public class Program
{
    private static void Main(string[]creditcard)
    {
        int sum = 0, d;
        string num ="7992739871";
        int a = 0;

        for (int i = num.Length - 2; i >= 0; i--)
        {
            d = Convert.ToInt32(num.Substring(i, 1));
            if (a % 2 == 0)
                d = d * 2;
            if (d > 9)
                d -= 9;
            sum += d;
            a++;
        }

        if ((10 - (sum % 10) == Convert.ToInt32(num.Substring(num.Length - 1))))
            Console.WriteLine("valid");

        Console.WriteLine("sum of digits of the number" + sum);
    }
}    
Gauleiter answered 21/1, 2014 at 5:12 Comment(3)
What's not working? Are you getting an error? If so, post it along with your stack traceCabe
I want to generate checksum in this process that i am unable to getGauleiter
I think there's bug in this code. It works incorrectly (the check always fails) for sum ≡ 0 mod 10.Primer
A
32

Here are some extension methods that compute a Luhn checkdigit, validate a number with a checkdigit, and add a checkdigit to a number. Tested in .NET 4.5.

There are extension methods for strings, ints, int64s and IList.

I got some ideas for this from rosettacode.org

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

public static class CheckDigitExtension
{
    static readonly int[] Results = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };

    #region extension methods for IList<int>

    /// <summary>
    /// For a list of digits, compute the ending checkdigit 
    /// </summary>
    /// <param name="digits">The list of digits for which to compute the check digit</param>
    /// <returns>the check digit</returns>
    public static int CheckDigit(this IList<int> digits)
    {
        var i = 0;
        var lengthMod = digits.Count%2;
        return (digits.Sum(d => i++ % 2 == lengthMod ? d : Results[d]) * 9) % 10;
    }

    /// <summary>
    /// Return a list of digits including the checkdigit
    /// </summary>
    /// <param name="digits">The original list of digits</param>
    /// <returns>the new list of digits including checkdigit</returns>
    public static IList<int> AppendCheckDigit(this IList<int> digits)
    {
        var result = digits;
        result.Add(digits.CheckDigit());
        return result;
    }

    /// <summary>
    /// Returns true when a list of digits has a valid checkdigit
    /// </summary>
    /// <param name="digits">The list of digits to check</param>
    /// <returns>true/false depending on valid checkdigit</returns>
    public static bool HasValidCheckDigit(this IList<int> digits)
    {
        return digits.Last() == CheckDigit(digits.Take(digits.Count - 1).ToList());
    }

    #endregion extension methods for IList<int>

    #region extension methods for strings

    /// <summary>
    /// Internal conversion function to convert string into a list of ints
    /// </summary>
    /// <param name="digits">the original string</param>
    /// <returns>the list of ints</returns>
    private static IList<int> ToDigitList(this string digits)
    {
        return digits.Select(d => d - 48).ToList();
    }

    /// <summary>
    /// For a string of digits, compute the ending checkdigit 
    /// </summary>
    /// <param name="digits">The string of digits for which to compute the check digit</param>
    /// <returns>the check digit</returns>
    public static string CheckDigit(this string digits)
    {
        return digits.ToDigitList().CheckDigit().ToString(CultureInfo.InvariantCulture);
    }

    /// <summary>
    /// Return a string of digits including the checkdigit
    /// </summary>
    /// <param name="digits">The original string of digits</param>
    /// <returns>the new string of digits including checkdigit</returns>
    public static string AppendCheckDigit(this string digits)
    {
        return digits + digits.CheckDigit(); 
    }

    /// <summary>
    /// Returns true when a string of digits has a valid checkdigit
    /// </summary>
    /// <param name="digits">The string of digits to check</param>
    /// <returns>true/false depending on valid checkdigit</returns>
    public static bool HasValidCheckDigit(this string digits)
    {
        return digits.ToDigitList().HasValidCheckDigit();
    }

    #endregion extension methods for strings

    #region extension methods for integers

    /// <summary>
    /// Internal conversion function to convert int into a list of ints, one for each digit
    /// </summary>
    /// <param name="digits">the original int</param>
    /// <returns>the list of ints</returns>
    private static IList<int> ToDigitList(this int digits)
    {
        return digits.ToString(CultureInfo.InvariantCulture).Select(d => d - 48).ToList();
    }

    /// <summary>
    /// For an integer, compute the ending checkdigit 
    /// </summary>
    /// <param name="digits">The integer for which to compute the check digit</param>
    /// <returns>the check digit</returns>
    public static int CheckDigit(this int digits)
    {
        return digits.ToDigitList().CheckDigit();
    }

    /// <summary>
    /// Return an integer including the checkdigit
    /// </summary>
    /// <param name="digits">The original integer</param>
    /// <returns>the new integer including checkdigit</returns>
    public static int AppendCheckDigit(this int digits)
    {
        return digits * 10 + digits.CheckDigit();
    }

    /// <summary>
    /// Returns true when an integer has a valid checkdigit
    /// </summary>
    /// <param name="digits">The integer to check</param>
    /// <returns>true/false depending on valid checkdigit</returns>
    public static bool HasValidCheckDigit(this int digits)
    {
        return digits.ToDigitList().HasValidCheckDigit();
    }

    #endregion extension methods for integers

    #region extension methods for int64s

    /// <summary>
    /// Internal conversion function to convert int into a list of ints, one for each digit
    /// </summary>
    /// <param name="digits">the original int</param>
    /// <returns>the list of ints</returns>
    private static IList<int> ToDigitList(this Int64 digits)
    {
        return digits.ToString(CultureInfo.InvariantCulture).Select(d => d - 48).ToList();
    }

    /// <summary>
    /// For an integer, compute the ending checkdigit 
    /// </summary>
    /// <param name="digits">The integer for which to compute the check digit</param>
    /// <returns>the check digit</returns>
    public static int CheckDigit(this Int64 digits)
    {
        return digits.ToDigitList().CheckDigit();
    }

    /// <summary>
    /// Return an integer including the checkdigit
    /// </summary>
    /// <param name="digits">The original integer</param>
    /// <returns>the new integer including checkdigit</returns>
    public static Int64 AppendCheckDigit(this Int64 digits)
    {
        return digits * 10 + digits.CheckDigit();
    }

    /// <summary>
    /// Returns true when an integer has a valid checkdigit
    /// </summary>
    /// <param name="digits">The integer to check</param>
    /// <returns>true/false depending on valid checkdigit</returns>
    public static bool HasValidCheckDigit(this Int64 digits)
    {
        return digits.ToDigitList().HasValidCheckDigit();
    }

    #endregion extension methods for int64s
}

Here are some XUnit test cases that show how the extension methods work.

public class CheckDigitExtensionShould
{
    [Fact]
    public void ComputeCheckDigits()
    {
        Assert.Equal(0, (new List<int> { 0 }).CheckDigit());
        Assert.Equal(8, (new List<int> { 1 }).CheckDigit());
        Assert.Equal(6, (new List<int> { 2 }).CheckDigit());

        Assert.Equal(0, (new List<int> { 3, 6, 1, 5, 5 }).CheckDigit());
        Assert.Equal(0, 36155.CheckDigit());
        Assert.Equal(8, (new List<int> { 3, 6, 1, 5, 6 }).CheckDigit());
        Assert.Equal(8, 36156.CheckDigit());
        Assert.Equal(6, 36157.CheckDigit());
        Assert.Equal("6", "36157".CheckDigit());
        Assert.Equal("3", "7992739871".CheckDigit());
    }

    [Fact]
    public void ValidateCheckDigits()
    {
        Assert.True((new List<int> { 3, 6, 1, 5, 6, 8 }).HasValidCheckDigit());
        Assert.True(361568.HasValidCheckDigit());
        Assert.True("361568".HasValidCheckDigit());
        Assert.True("79927398713".HasValidCheckDigit());
    }

    [Fact]
    public void AppendCheckDigits()
    {
        Console.WriteLine("36156".CheckDigit());
        Console.WriteLine("36156".AppendCheckDigit());
        Assert.Equal("361568", "36156".AppendCheckDigit());
        Assert.Equal("79927398713", "7992739871".AppendCheckDigit());
    }
}
Amortize answered 13/5, 2014 at 19:57 Comment(2)
I would put this in a class of its own rather than extending the built-in types which I would consider pollution of the built-in types.Gettings
Great explanation. Complete Answer, I beleveHeilman
M
28

Compact Luhn check:

public static bool Luhn(string digits)
{
    return digits.All(char.IsDigit) && digits.Reverse()
        .Select(c => c - 48)
        .Select((thisNum, i) => i % 2 == 0
            ? thisNum
            :((thisNum *= 2) > 9 ? thisNum - 9 : thisNum)
        ).Sum() % 10 == 0;
}

Fiddle: https://dotnetfiddle.net/CCwE48

Monanthous answered 8/11, 2016 at 16:8 Comment(2)
why you use "-48"?Halfbreed
To convert the char to its numeric value. The char code for 0 is 48, for 1 is 49 etc. Subtract 48 and you have the numeric value.Monanthous
P
5

Here is a correct and fast implementation:

bool PassesLuhnCheck(string value)
{
    long sum = 0;

    for (int i = 0; i < value.Length; i++)
    {
        var digit = value[value.Length - 1 - i] - '0';
        sum += (i % 2 != 0) ? GetDouble(digit) : digit;
    }

    return sum % 10 == 0;

    int GetDouble(long i)
    {
        switch (i)
        {
            case 0: return 0;
            case 1: return 2;
            case 2: return 4;
            case 3: return 6;
            case 4: return 8;
            case 5: return 1;
            case 6: return 3;
            case 7: return 5;
            case 8: return 7;
            case 9: return 9;
            default: return 0;
        }
    }
}
Planography answered 11/9, 2019 at 13:33 Comment(0)
B
1

I have tried this code which might help for other future folk:

    public string GenerateLuhnNumber(string baseNumber)
    {
        if (!double.TryParse(baseNumber, out double baseNumberInt))
            throw new InvalidWorkflowException($"Field contains non-numeric character(s) : {baseNumber}");

        var step2 = string.Empty;
        for (var index = baseNumber.Length - 1; index >= 0; index -= 2)
        {
            var doubleTheValue = (int.Parse(baseNumber[index].ToString())) * 2;

            if (doubleTheValue > 9)
                doubleTheValue = Math.Abs(doubleTheValue).ToString().Sum(c => Convert.ToInt32(c.ToString()));

            step2 = step2.Insert(0, (index != 0 ? baseNumber[index - 1].ToString() : "") + doubleTheValue);
        }
        var step3 = Math.Abs(Convert.ToDouble(step2)).ToString(CultureInfo.InvariantCulture).Sum(c => Convert.ToDouble(c.ToString())).ToString(CultureInfo.InvariantCulture);

        var lastDigitStep3 = Convert.ToInt32(step3[step3.Length - 1].ToString());
        string checkDigit = "0";

        if (lastDigitStep3 != 0)
            checkDigit = (10 - lastDigitStep3).ToString();

        return baseNumber + checkDigit;
    }
Bathyscaphe answered 28/6, 2019 at 11:55 Comment(1)
I noticed that if the string length is 18, to generate the 19th digit, this method through an exceptionComma
E
1

Philippe had an excellent answer, but here's a simpler version that is still O(n). I've tested it in xUnit with a dataset of 30 and it is factors faster than some of the upvoted answers.

    public static bool CheckLuhnParity(string digits)
    {
        bool isValid = false;

        if (!string.IsNullOrEmpty(digits))
        {
            long sum = 0;
            int parity = digits.Length % 2;
            for (int i = 0; i < digits.Length; i++)
            {
                int digit = digits[^(i + 1)] - '0';
                sum += (i % 2 == parity) ? Luhn(digit) : digit;
            }
            isValid = (sum % 10) == 0;
        }

        return isValid;

        int Luhn(int digit) => (digit *= 2) > 9 ? digit - 9 : digit;
    }

This has the same flaw as the accepted answer, though; it doesn't fully implement the Luhn algorithm. It assumes that the actual check digit does not need to be verified which means invalid numbers may be accepted. Here's a better way:

    public static bool CheckLuhnDigit(string digits)
    {
        bool isValid = false;

        if (!string.IsNullOrEmpty(digits) && digits.Length > 2)
        {
            long sum = 0;
            for (int i = 0; i < digits.Length - 1; i++)
            {
                int digit = digits[^(i + 2)] - '0';
                sum += (i % 2 == 0) ? Luhn(digit) : digit;
            }
            int checkDigit = digits[^1] - '0';
            isValid = (10 - (sum % 10)) % 10 == checkDigit;
        }

        return isValid;

        int Luhn(int digit) => (digit *= 2) > 9 ? digit - 9 : digit;
    }

Does anyone want to attempt O(log n)?

Ethylethylate answered 16/8, 2022 at 5:28 Comment(0)
I
0

You can do it very simply (reference),

    public static bool Mod10Check(string creditCardNumber)
    {              
        // check whether input string is null or empty
        if (string.IsNullOrEmpty(creditCardNumber))
        {
            return false;
        }

        int sumOfDigits = creditCardNumber.Where((e) => e >= '0' && e <= '9')
                        .Reverse()
                        .Select((e, i) => ((int)e - 48) * (i % 2 == 0 ? 1 : 2))
                        .Sum((e) => e / 10 + e % 10);


        return sumOfDigits % 10 == 0;
    }
Ingrain answered 23/2, 2016 at 2:56 Comment(1)
This returns true for invalid credit card numbers. Try using "adfafda" as the card number.Puncture
B
0

This one will do it I believe:

static void Main(string[] args)
    {
        string number = "1762483";
        int digit = 0;
        int sum = 0;

        for (int i = 0; i <= number.Length - 1; i++)
        {

            if (i % 2 == 1)
            {
                digit = int.Parse(number.Substring(i, 1));
                sum += DoubleDigitValue(digit);

                Console.WriteLine(digit);
            }
            else
            {
                digit = int.Parse(number.Substring(i, 1));
                sum += digit;
            }

        }
        Console.WriteLine(sum);
        if (sum % 10 == 0)
        {
            Console.WriteLine("valid");
        }
        else
        {
            Console.WriteLine("Invalid");
        }
    }
    static int DoubleDigitValue(int digit)
    {
        int sum;
        int doubledDigit = digit * 2; 
        if (doubledDigit >= 10)
        {
            sum = 1 + doubledDigit % 10;
        } else
        {
            sum = doubledDigit; 
        }
        return sum; 
    }
Burkett answered 17/7, 2018 at 2:3 Comment(0)
P
0

These are my methods for validating and calculating the last digit. To validate a number simply check that the result of the first method is 0;

private int LuhnChecksum(string input)
    {
        var length = input.Length; 
        var even = length % 2; 
        var sum = 0;

        for (var i = length - 1; i >= 0; i--)  
        {
            var d = int.Parse(input[i].ToString());
            if (i % 2 == even)
                d *= 2;
            if (d > 9)
                d -= 9;
            sum += d;
        }
        return sum % 10;
    }

    private int LuhnCalculateLastDigit(string input) 
    {
        var checksum = LuhnChecksum(input + "0");
        return checksum == 0 ? 0 : 10 - checksum;
    }
Pegues answered 11/11, 2019 at 15:55 Comment(0)
D
0

I just interprete code from C to C#. Code in C you can find there:(https://uk.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D1%83%D0%BD%D0%B0). I cheked it for a few device.

byte[] data = new byte[19];

//fill the data[] 
...

//use it
if (checkByLuhn(data))
{
  //check complete
}
...
private bool checkByLuhn(byte[] pPurposed)
{
    int nSum = 0;
    int nDigits = pPurposed.Length;
    int nParity = (nDigits - 1) % 2;
    char[] cDigit = new char[] { '0','\0' };

    for (int i = nDigits; i > 0; i--)
    {
        cDigit[0] = (char)pPurposed[i - 1];
        int nDigit = (int)Char.GetNumericValue(cDigit[0]);

        if (nParity == i % 2)
        {
            nDigit = nDigit * 2;
        }
        nSum += nDigit / 10;
        nSum += nDigit % 10;
    }
    return 0 == nSum % 10;
}
Dett answered 6/11, 2020 at 0:22 Comment(2)
You could improve this by explaining what you did and why it works.Digamy
I just interprete code from C to C#. Code in C you can find there:(ru.wikipedia.org/wiki/…) I cheked it for a few device.Dett
D
0

Here's shorter version to get checksum

private int getCheckSum(string number)
        {
            var sum = number.Reverse() //Reverse
                        .Select((d, i) => i % 2 == 0 ? Convert.ToInt32(d.ToString()) * 2 : Convert.ToInt32(d.ToString())) //double every 2nd digit including starting
                            .Select(x => x.ToString().Select(c => Convert.ToInt32(c.ToString())).Sum()) //sum double digit number 18 = 1 + 8 = 9
                              .Sum(); //Sum all
            return (10 - (sum % 10)) % 10; 
        }

To validate a check sum, pass number without checksum and compare the result with last digit of original number .

Deroo answered 21/12, 2022 at 12:31 Comment(0)
R
-2

Your algorithm is correct, but you're testing it wrong way.

I can see your sample input is from wiki page: Luhn algorithm. Difference is, they are calculating check digit for "7992739871X", where X is the check digit they're looking for. Your code validates number your already have!

Change your input to "79927398713" and it will mark it as correct number.

Update

OK, I see where is the problem. You're not taking this part of algorithm right:

From the rightmost digit, which is the check digit, moving left, double the value of every second digit;

Your code takes every other digit, but not necessary starting from most left digit. Try this code:

for (int i = 0; i < num.Length; i++)
{
    d = Convert.ToInt32(num.Substring(num.Length - 1 - i, 1));
    if (a % 2 == 0)
        d = d * 2;
    if (d > 9)
        d -= 9;
    sum += d;
    a++;
}

var checkDigit = 10 - (sum % 10);
Rarity answered 21/1, 2014 at 5:24 Comment(2)
this is buggy code. it is only correct when the number of digits is even. for a number with an odd number of digits, 15 or 13, for instance, it's doubles the wrong digits. Marcin, you count every second digit from the right, not from the left. this code is counting from the left. :( That's why the original code counted from the right.Polyhistor
Additionally this returns 10 instead of 0 when the sum is 0.Damnify

© 2022 - 2024 — McMap. All rights reserved.