1# luhn.py - functions for performing the Luhn and Luhn mod N algorithms 2# 3# Copyright (C) 2010, 2011, 2012, 2013 Arthur de Jong 4# 5# This library is free software; you can redistribute it and/or 6# modify it under the terms of the GNU Lesser General Public 7# License as published by the Free Software Foundation; either 8# version 2.1 of the License, or (at your option) any later version. 9# 10# This library is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13# Lesser General Public License for more details. 14# 15# You should have received a copy of the GNU Lesser General Public 16# License along with this library; if not, write to the Free Software 17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 18# 02110-1301 USA 19 20"""The Luhn and Luhn mod N algorithms. 21 22The Luhn algorithm is used to detect most accidental errors in various 23identification numbers. 24 25>>> validate('7894') 26Traceback (most recent call last): 27 ... 28InvalidChecksum: ... 29>>> checksum('7894') 306 31>>> calc_check_digit('7894') 32'9' 33>>> validate('78949') 34'78949' 35 36An alternative alphabet can be provided to use the Luhn mod N algorithm. 37The default alphabet is '0123456789'. 38 39>>> validate('1234', alphabet='0123456789abcdef') 40Traceback (most recent call last): 41 ... 42InvalidChecksum: ... 43>>> checksum('1234', alphabet='0123456789abcdef') 4414 45""" 46 47from stdnum.exceptions import * 48 49 50def checksum(number, alphabet='0123456789'): 51 """Calculate the Luhn checksum over the provided number. The checksum 52 is returned as an int. Valid numbers should have a checksum of 0.""" 53 n = len(alphabet) 54 number = tuple(alphabet.index(i) 55 for i in reversed(str(number))) 56 return (sum(number[::2]) + 57 sum(sum(divmod(i * 2, n)) 58 for i in number[1::2])) % n 59 60 61def validate(number, alphabet='0123456789'): 62 """Check if the number provided passes the Luhn checksum.""" 63 if not bool(number): 64 raise InvalidFormat() 65 try: 66 valid = checksum(number, alphabet) == 0 67 except Exception: 68 raise InvalidFormat() 69 if not valid: 70 raise InvalidChecksum() 71 return number 72 73 74def is_valid(number, alphabet='0123456789'): 75 """Check if the number passes the Luhn checksum.""" 76 try: 77 return bool(validate(number, alphabet)) 78 except ValidationError: 79 return False 80 81 82def calc_check_digit(number, alphabet='0123456789'): 83 """Calculate the extra digit that should be appended to the number to 84 make it a valid number.""" 85 ck = checksum(str(number) + alphabet[0], alphabet) 86 return alphabet[-ck] 87