def validate(data: str) -> bool: """Validates a luhn check digit. Args: data: A string of characters representing a full luhn code Returns: bool: A boolean representing whether the check digit validates the data or not Examples: >>> from checkdigit import luhn >>> luhn.validate("541756116585277") True >>> luhn.validate("79927398713") True >>> luhn.validate("49927398717") False >>> luhn.validate("1234567812345678") False """ data = cleanse(data) return ( calculate(data[:-1]) == data[-1] ) # Determines if calculated Check Digit of the data is the last digit given
def calculate(data: str, even: bool = True) -> str: """Adds a parity bit onto the end of a block of data. Args: data: A string containing binary digits even: Whether to use even or odd parity (defaults to even) Returns: str: The parity bit of the data Examples: >>> from checkdigit import parity >>> # Even parity >>> parity.calculate("0110") '0' >>> parity.calculate("01101") '1' >>> # Odd parity >>> parity.calculate("01101", False) '0' >>> parity.calculate("0", False) '1' """ data = cleanse(data) if (even and not data.count("1") % 2) or (not even and data.count("1") % 2): return "0" return "1"
def validate(data: str, even: bool = True) -> bool: """Validates whether the check digit matches a block of data. Args: data: A string containing binary digits even: Whether to use even or odd parity (defaults to even) Returns: bool: A boolean representing whether the data is valid or not Examples: >>> from checkdigit import parity >>> # Even parity >>> parity.validate("01100") True >>> parity.validate("01101") False >>> # Odd parity >>> parity.validate("01101", False) True >>> parity.validate("01100", False) False """ data = cleanse(data) return calculate(data[:-1], even) == data[-1]
def calculate(data: str, polynomial: str, pad: str = "0") -> str: """Adds a parity part onto the end of a block of data. Args: data: A string containing binary digits of any length polynomial: A string of binary digits representing the polynomial being used pad: "1" or "0" or nothing to be used to pad the data (defaults to 0) Returns: str: Check Value that should be appended to the stream Examples: >>> from checkdigit import crc >>> crc.calculate("1010", "1011") '011' >>> crc.calculate("100110010100111101111101", "11010111101") '0010001100' """ data = cleanse(data) data += pad * (len(polynomial) - 1) bitarray = list(data) while len(bitarray) != len(polynomial) - 1: for position, bit in enumerate(polynomial): # XOR calculation bitarray[position] = "0" if bit == bitarray[position] else "1" while bitarray[0] == "0" and len(bitarray) >= len(polynomial): bitarray.pop(0) return "".join(bitarray)
def missing(data: str) -> str: """Calculates a missing digit in an ISBN Code represented by a question mark. Args: data: A string of characters representing a full ISBN code with a question mark representing a missing character Returns: str: The missing value that should've been where the question mark was """ data = cleanse(data) data_length = len(data) # ISBN-10 or 13 # We already have an efficient method for the checkdigit if data[-1] == "?": # Remove question mark check digit return calculate10(data[:-1]) if data_length == 10 else calculate13(data[:-1]) # We've dealt with the check digit, so X can't be an option # Brute force all the possible numbers (0-9 inclusive) for option in (data.replace("?", str(i)) for i in range(10)): # Validate the new option if ( data_length == 10 and validate10(option) or data_length == 13 and validate13(option) ): # Replace question mark with new value return option[data.index("?")] return "Invalid"
def calculate(data: str) -> str: """Calculates the luhn check digit. Args: data: A block of data without the check digit Returns: str: A string representing the missing check digit Examples: >>> from checkdigit import luhn >>> luhn.calculate("53251309870224") '3' >>> luhn.calculate("950123440000") '8' """ data = cleanse(data) # Double every other digit, starting from the final digit backwards # i.e. double 0th digit, 2nd, 4th, ... double_digits = (int(element) if index % 2 else int(element) * 2 for index, element in enumerate(data[::-1])) # For digits with more than one digit, sum the digits together # The maximum is 9*2 = 18. This divmod method will work <100 sum_digits = sum(sum(divmod(i, 10)) for i in double_digits) # Mod 10 returns 0-9 (not 10 or 11) # Hence convert method not required (faster to use str) return str((sum_digits * 9) % 10)
def validate(data: str, even: bool = True) -> bool: """Validates whether the check digit matches a block of data. Args: data: A string containing binary digits even: Whether to use even or odd parity (defaults to even) Returns: bool: A boolean representing whether the data is valid or not """ data = cleanse(data) return calculate(data[:-1], even) == data[-1]
def validate13(data: str) -> bool: """Validates ISBN-13. Args: data: A string of characters representing a full ISBN-13 code Returns: bool: A boolean representing whether the check digit validates the data """ data = cleanse(data) return calculate13(data[:-1]) == data[-1]
def calculate(data: str, even: bool = True) -> str: """Adds a parity bit onto the end of a block of data. Args: data: A string containing binary digits even: Whether to use even or odd parity (defaults to even) Returns: str: The parity bit of the data """ data = cleanse(data) if (even and not data.count("1") % 2) or (not even and data.count("1") % 2): return "0" return "1"
def calculate(data: str, even: bool = True) -> str: """Adds a parity bit onto the end of a block of data. Args: data: A string containing binary digits even: Whether to use even parity (otherwise uses odd parity) Returns: str: The original data with the parity bit added to the end """ data = cleanse(data) if (even and not data.count("1") % 2) or (not even and data.count("1") % 2): return data + "0" return data + "1"
def calculate10(data: str) -> str: """Calculates ISBN-10 Check Digit. Args: data: A string of 9 characters Returns: str: The check digit that was missing """ data = cleanse(data) # Multiply first digit by 10, second by 9, ... and take the sum total_sum = sum( int(digit) * weight for digit, weight in zip(data, range(10, 0, -1)) ) return convert(11 - (total_sum % 11))
def validate(data: str) -> bool: """Validates GS1. This method works for all fixed length numeric GS1 data structures (including GDTI, GLN, GRAI, etc.) that require a check digit. Args: data: A string of characters representing a full GS1 code Returns: bool: A boolean representing whether the check digit validates the data or not """ data = cleanse(data) return calculate(data[:-1]) == data[-1]
def calculate10(data: str) -> str: """Calculates ISBN-10 Check Digit. Args: data: A string of 9 characters Returns: str: The check digit that was missing """ data = cleanse(data) total_sum = 0 multiply_counter = 10 for item in data: total_sum += int(item) * multiply_counter multiply_counter -= 1 # Multiplies first digit by 10, second by 9... check_digit = 11 - (total_sum % 11) return convert(check_digit)
def calculate13(data: str, barcode: str = "isbn") -> str: """Calculates ISBN-13 Check Digit. Args: data: A string of 12 characters barcode: The type of code (either isbn or upc) Returns: str: The check digit that was missing """ data = cleanse(data) # ISBN weights is 1 for odd positions and 3 for even # The opposite is true for upc weights = (1, 3) * 6 if barcode == "isbn" else (3, 1) * 6 # Multiply each digit by its weight total_sum = sum(int(digit) * weight for digit, weight in zip(data, weights)) # Return final check digit and type of barcode return convert(10 - (total_sum % 10), barcode)
def validate(data: str, polynomial: str) -> bool: """Validates whether the check digit matches a block of data. Args: data: A string containing binary digits including the check digit polynomial: Polynomial to use Returns: bool: A boolean representing whether the data is valid or not Examples: >>> from checkdigit import crc >>> crc.validate("1010101", "101") True >>> crc.validate("1000101", "101") False """ data = cleanse(data) # the principle of CRCs is that when done again but with the check digit # appended if the data is fine it should all be 0s return "0" * (len(polynomial) - 1) == calculate(data, polynomial, "")
def missing(data: str) -> str: """Calculates a missing digit in a GS1 Code. This method works for all fixed length numeric GS1 data structures (including GDTI, GLN, GRAI, etc.) that require a check digit. Args: data: A string of characters representing a full ISBN code with a question mark representing a missing character Returns: str: The missing value that should've been where the question mark was """ data = cleanse(data) for poss_digit in range(10): # Brute Force the 10 options option = convert(poss_digit) # tests it with the generated number # If this fails, the next number is tried if validate(data.replace("?", option)): return option return "Invalid"
def missing(data: str) -> str: """Calculates a missing digit in an ISBN Code. Args: data: A string of characters representing a full ISBN code with a question mark representing a missing character Returns: str: The missing value that should've been where the question mark was """ data = cleanse(data) for poss_digit in range(11): # Brute Force the 11 options option = convert(poss_digit) # Depending on the size of the data, the relevant validating function # tests it with the generated number # If this fails, the next number is tried if (len(data) == 10 and validate10(data.replace("?", option))) or ( len(data) == 13 and validate13(data.replace("?", option)) ): return option return "Invalid"
def calculate13(data: str, barcode: str = "isbn") -> str: """Calculates ISBN-13 Check Digit. Args: data: A string of 12 characters barcode: The type of code (either isbn or upc) Returns: str: The check digit that was missing """ data = cleanse(data) mod_number = 0 if barcode == "isbn" else 1 total_sum = 0 position_counter = 1 # 1 based indexing for data for item in data: digit = int(item) if position_counter % 2 == mod_number: total_sum += digit * 3 # Multiplies by 3 if position is even else: total_sum += digit position_counter += 1 final_value = 10 - (total_sum % 10) return convert(final_value, barcode)
def calculate(data: str) -> str: """Calculates GS1 Check Digit. This method works for all fixed length numeric GS1 data structures (including GDTI, GLN, GRAI, etc.) that require a check digit. Args: data: A string of characters Returns: str: The check digit that was missing """ data = cleanse(data) data = data[::-1] # Reverse the barcode, as last digit is always multiplied by 3 total_sum = 0 for index, value in enumerate(data): if index % 2 == 0: total_sum += int(value) * 3 else: total_sum += int(value) next_multiple_of_ten = int(math.ceil(total_sum / 10.0)) * 10 check_digit = next_multiple_of_ten - total_sum return convert(check_digit, "gs1")
def calculate(data: str) -> str: """Calculates ISBN Check Digits. Args: data: A string of characters representing an ISBN code without the check digit Returns: str: The check digit that was missing Examples: >>> from checkdigit import isbn >>> # ISBN-10 >>> isbn.calculate("043942089") 'X' >>> # ISBN-13 >>> isbn.calculate("978-1-86197-876") '9' """ data = cleanse(data) if len(data) == 9: # ISBN 10 (without the check digit) # Multiply first digit by 10, second by 9, ... and take the sum total_sum = sum( int(digit) * weight for digit, weight in zip(data, range(10, 0, -1)) ) return convert(11 - (total_sum % 11)) # elif not required since return above (and makes pylint happy) if len(data) == 12: # ISBN weights is 1 for odd positions and 3 for even # Since there are 12 digits, multiply weights by 6 weights = (1, 3) * 6 # Multiply each digit by its weight total_sum = sum(int(digit) * weight for digit, weight in zip(data, weights)) # Return final check digit and type of barcode return convert(10 - (total_sum % 10)) return "Invalid"