"""Some support functions""" def code0(): """A trivial code - no change.""" return {} def code1(): """A very simple example (symmetric).""" return {'e': 'x', 'x': 'e'} def code2(): """A simple example i->s, s->g and g->i.""" return {'i': 's', 's': 'g', 'g': 'i'} def code3(): """A more complicated code.""" dic = {} # lower case letters dic['z'] = 'a' for i in range(ord('a'), ord('z')): dic[chr(i)] = chr(i + 1) # upper case letters dic['Z'] = 'A' for i in range(ord('A'), ord('Z')): dic[chr(i)] = chr(i + 1) # space, stop and some other special characters dic[' '] = '$' dic['.'] = '#' dic['#'] = '.' dic['$'] = ' ' dic['?'] = '!' dic['!'] = '?' return dic def check_code_is_reversible(dic): """Given a dictionary used as a code mapping, this function checks whether the set of keys is the same set of values: if the elements in the keys are the same unique set as those in the values, then this mapping is bijective (the set of values is then actually a permutation of the set of input values) and can be inverted. If this is not the case, some debug information is printed, and a ValueError exception raised. """ unique_keys = set() unique_vals = set() for key, val in dic.items(): unique_keys.add(key) unique_vals.add(val) if unique_vals != unique_keys: print("Code is not reversible:") print("keys are %s", sorted(unique_keys)) print("values are %s", sorted(unique_vals)) raise ValueError("Code is not reversible - stopping here") return True def test_codes(): """Check that codes defined above are reversible.""" for c in (code0(), code1(), code2(), code3()): assert check_code_is_reversible(c) secretmessage = \ "Zpv$ibwf$tvddfttgvmmz$efdpefe$uijt$tfdsfu$nfttbhf#$Dpohsbuvmbujpot?" # if this file is executed on it's own, check codes given if __name__ == "__main__": test_codes()