Source code for checkdigit.crc

# /usr/bin/env python
# This file is part of checkdigit.

# checkdigit is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# checkdigit is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with checkdigit.  If not, see <http://www.gnu.org/licenses/>.

"""Cyclic Redundancy Check.

A block of binary as a form of data validation is appended to a bitstring.
This can be easily checked even by very simple circuitry,
and can also be used to correct some errors.

WARNING: THIS IS NOT A FAST IMPLEMENTATION OF CRC.

If you want a fast implementation look elsewhere.

"""
from checkdigit._data import cleanse


[docs]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)
[docs]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, "")
[docs]def missing(data: str, polynomial: str) -> str: """Calculates missing digits represented by a question mark. Args: data: A string containing a question mark representing a missing digit. polynomial: What the polynomial that should be used is Returns: str: The missing binary digit Examples: >>> from checkdigit import crc >>> crc.missing("?1111111101", "1101") '1' >>> crc.missing("10?110010100111?0?1111?10010?011?0", "11010111101") '011000' >>> # If there's more than one possible option >>> crc.missing("?1111111001", "1101") 'Invalid' """ final_solution = "Invalid" number = data.count("?") if number == 0: return "Invalid" # if there are no ? to replace the algorithm will not work permutations = 2**number # number of different permutations that are possible for permutation in range(permutations): tocheck = data replacement = bin(permutation)[2:].zfill( number ) # gives us a nice binary number for bit in replacement: tocheck = tocheck.replace("?", bit, 1) # only replaces one bit at a time if validate(tocheck, polynomial): # Should only be one possible solution. If more than one found, return invalid if final_solution == "Invalid": final_solution = replacement else: return "Invalid" return final_solution