Package: integer-pairing
integer-pairingThis library enables encodings of integer tuples as one integer. It implements two well-known types of encodings - Cantor and Szudzik.
There is agreat articleon those two types. It also implements a slight generalization.UsageThe base example isfrominteger_pairingimportcantor,szudzikcantor.pair(11,13)# 313cantor.unpair(313)# (11, 13)szudzik.pair(11,13)# 180szudzik.unpair(180)# (11, 13)You can pair tuples of any size, but have to specify the size when unpairingcantor.pair(11,13,17,19,23)# 1115111727200556569cantor.unpair(1115111727200556569,dim=5)# (11, 13, 17, 19, 23)It is also possible to include negative numbers, but you need to imply that when decodingcantor.pair(11,13,-1)# 726618cantor.unpair(726618,dim=3,neg=True)# (11, 13, -1)Naive implementations of the above algorithms fail to account for very large
integers, as they use numeric calculation of the square root. Python allows for
integers of any size to be stored, but converts them to float (64 bits) when doing numeric operations,
so this approximation ruins the unpairing. Luckily this can be (efficiently) solved and is implemented here.cantor.pair(655482261805334959278882253227,730728447469919519177553911051)# 960790065254702046274404114853633027146937669672812473623832cantor.unpair(960790065254702046274404114853633027146937669672812473623832)# (655482261805334959278882253227, 730728447469919519177553911051)You can also pair (signed) integers in a way that encodes the tuple's dimension.
This is called bundling and is done by encoding each number in a tuple in binary,
then prepending those encodings by the number 2 or 22, depending on the number's sign.
For space-efficiency, the string is then interpreted in a trinary base system.frominteger_pairingimportbundlebundle.pair(*range(-8,8))# 1061264631713144962268472871675bundle.unpair(1061264631713144962268472871675)# (-8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7)The downside is thatbundle.pairis not surjective, so not every number can be unpaired.
Thus calling unpair on an invalid number will produce an exceptionbundle.unpair(0)Traceback(mostrecentcalllast):File"<pyshell#37>",line1,in<module>bundle.unpair(0)File"...\integer_pairing\_interface.py",line66,inunpairreturnself._unbundle(n)File"...\integer_pairing\_bundle.py",line41,in_unbundledi=1ifs[i+1]!='2'else2IndexError:stringindexoutofrangeComplexityThe pairing of n integers will result in an integer of the size of about their product.Example usage from CryptographyWhen encrypting messages deterministically, an attacker can always reproduce the encryption
of any chosen messages. If the possibilities are few (e.g.trueorfalse), those kinds
of algorithms are pretty useless. This is solved by appending a random number, called salt,
to the message. It can be useful to implement this appending via pairing.fromrandomimportgetrandbitssalt=getrandbits(128)message=0encoded=szudzik.pair(message,salt)Also, public-key cryptography can often only deal with integers, so messages have to be encoded
accordingly. You can easily acomplish this with bundling.txt2int=lambdam:bundle.pair(*map(ord,m))int2txt=lambdan:''.join(map(chr,bundle.unpair(n)))message='hi there!'message_enc=txt2int(message)# 2050221782650890524283503336306989message_dec=int2txt(message_enc)# 'hi there!'But there are better ways of doing this.