using System.Collections.Generic;
public static class Program
public static void Main()
Console.WriteLine(IsChecksumCorrect("ZZ0000001535"));
public static bool IsChecksumCorrect(string code, bool reverseLuhn = false, bool allowSymbols = false) {
var checksum = ToInt(code.Last());
return checksum == CalculateChecksum(code.Take(code.Length - 1), reverseLuhn, allowSymbols);
public static int CalculateChecksum(IEnumerable<char> codeWithoutCheckDigit, bool reverseLuhn = false, bool allowSymbols = false) {
? TensComplement(codeWithoutCheckDigit
.Select((c, i) => SumDigits(ConditionalMultiplyByTwo(OrdinalPosition(c, allowSymbols), IsOdd(i))))
: TensComplement(ToDigits(codeWithoutCheckDigit.ToArray(),
.Select((d, i) => SumDigits(ConditionalMultiplyByTwo(d, IsEven(i))))
private static int OrdinalPosition(char c, bool allowSymbols = false) {
return char.ToUpper(c) - 'A' + 10;
throw new ArgumentOutOfRangeException("Specified character is not a letter, digit or allowed symbol.");
private static bool IsEven(this int x) {
private static bool IsOdd(this int x) {
private static int ToInt(this char digit) {
throw new ArgumentOutOfRangeException("Specified character is not a digit.");
private static IEnumerable<int> ToDigits(this char[] s, bool allowSymbols = false) {
var digits = new List<int>();
for (var i = s.Length - 1; i >= 0; i--) {
var ordinalPosition = OrdinalPosition(s[i], allowSymbols);
digits.Add(ordinalPosition % 10);
digits.Add(ordinalPosition / 10);
private static int SumDigits(this int value) {
return ((value / 10) + (value % 10));
private static int ConditionalMultiplyByTwo(this int value, bool condition) {
return condition ? value * 2 : value;
private static int TensComplement(this int value) {
return (10 - (value % 10)) % 10;