const int validCodePointCount = 10;
const int validCodePointOffset = 48;
Constants should be written in PascalCase, whether it is private
or local.
while (0 < index--) {
var digit = (value[index] - validCodePointOffset);
What is this? A reverse foreach which could be written as foreach(var c in value.Reverse())
. Also, the community standard for bracket placement is generally vertically aligned. (Although, this is more a question of style that you or your team follow.)
var digit = (value[index] - validCodePointOffset); // validCodePointOffset -> 48
A int.Parse
could be used instead. While we are at it, there is no argument guard that ensures value
will always be a string of digits.
edit: If you look to support an arbitrary set of characters, you can use a lookup table to map the character to its "numerical" value.
if ((validCodePointCount - 1) < (digit <<= 1)) {
digit -= (validCodePointCount - 1);
}
It it really hard to understand what this code is trying to do. The wiki says: "If the result of this doubling operation is greater than 9, then add the digits of the product." This could be added as a comment to explain this bit of code. I wouldn't recommend using bit-shifting for simple multiplication. It adds an unnecessary layer in this case, when *= 2
could be used.
Or, use code that is easier to read:
digit *= 2;
if (digit >= @base)
digit = digit % @base + 1;
return ((validCodePointCount - (sum %= validCodePointCount)) % validCodePointCount);
10 - (x mod 10)
can never exceed 10, so the last modulo operation is completely unnecessary. I would suggest replacing the validCodePointCount
constant with its value. Unless there is a better variable name, which I have a lack of word for.
Again quoting the wiki: "The check digit (x) is obtained by computing the sum of the other digits (third row) then subtracting the units digit from 10."
private static readonly Dictionary<char, int> CharacterMap =
"0123456789"
.Select((x,i) => (x,i))
.ToDictionary(x => x.x, x => x.i);
public static int GetLuhnCheckSum(string value)
{
if (!value.All(CharacterMap.ContainsKey))
throw new ArgumentException("Value contains invalid characters", nameof(value));
int @base = CharacterMap.Count;
var parity = true;
var sum = 0;
// process the number from right to left
foreach (var c in value.Reverse())
{
var digit = CharacterMap[c];
// From the rightmost digit, which is the check digit, and moving left, double the value of every second digit.
if (parity)
{
digit *= 2;
// If the result of this doubling operation is greater than 9, then add the digits of the product.
if (digit >= @base)
digit = digit % @base + 1;
}
parity = !parity;
sum += digit;
}
// The check digit (x) is obtained by computing the sum of the other digits then subtracting the units digit from 10
return (@base - (sum % @base)) % @base;
}
edit: fixed check sum can be base
instead of 0
edit: updated the code to support different base system
Luhn.GetCheckValue
) : rosettacode.org/wiki/Luhn_test_of_credit_card_numbers#C.23 \$\endgroup\$ – Xiaoy312 Oct 5 '18 at 21:21