forked from UWPCE-PythonCert/ProgrammingInPython
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrot13.py
More file actions
180 lines (141 loc) · 5.4 KB
/
Copy pathrot13.py
File metadata and controls
180 lines (141 loc) · 5.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/usr/bin/env python
"""
A simple function to compute rot13 encoding
ROT13 encryption
Applying ROT13 to a piece of text merely requires examining its alphabetic
characters and replacing each one by the letter 13 places further along in
the alphabet, wrapping back to the beginning if necessary
"""
# note: the string translate() method would be the high-performance solution
# the string module has some handy constants.
import string
# a few handy constants:
a = ord('a')
z = ord('z')
A = ord('A')
Z = ord('Z')
def rot13a(text):
"""
A brute force solution
"""
# loop through the letters in the input string
new_text = []
for c in text:
# do upper and lower case separately
if c in string.ascii_lowercase:
o = ord(c) + 13
if o > z:
o = a - 1 + o - z
elif c in string.ascii_uppercase:
o = ord(c) + 13
if o > Z:
o = A - 1 + o - Z
else:
o = ord(c)
new_text.append(chr(o))
return "".join(new_text)
def rot13b(text):
"""
A little smarter to use % to take care of the wrap-around
And do a check on the ord value, rather than looking in
string.ascii_lowercase
"""
# loop through the letters in teh input string
new_text = []
for c in text:
o = ord(c)
# do upper and lower case separately
if a <= o <= z:
o = a + ((o - a + 13) % 26)
elif A <= o <= Z:
o = A + ((o - A + 13) % 26)
new_text.append(chr(o))
return "".join(new_text)
# Building a translation table is much faster (once the table is built)
# build a translation table with one-byte ascii only characters
str_table = []
# loop through all possible ascii values
for c in range(z + 1): # only need up to z
# do upper and lower case separately
if a <= c <= z:
c = a + (c - a + 13) % 26
elif A <= c <= Z:
c = A + (c - A + 13) % 26
str_table.append(chr(c))
str_table = "".join(str_table)
# Unicode has a LOT of code points, so better to use a dict
# and only specify the ones that need changing -- in a dict
# NOTE: we haven't covered dicts yet, but for completeness' sake,
# it's here.
dict_table = {}
# the lower-case letters
for c in range(a, z + 1):
dict_table[c] = a + (c - a + 13) % 26
# the lower-case letters
for c in range(A, Z + 1):
dict_table[c] = A + (c - A + 13) % 26
# OR use the maketrans() method, and hard code the translation
orig = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
rotated = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM"
table = str.maketrans(orig, rotated)
def rot13c(text):
"""
This one uses the translation table above
"""
return text.translate(str_table)
def rot13d(text):
"""
this one uses a dict translation table -- so better suite to unicode
"""
return text.translate(dict_table)
def rot13e(text):
"""
This one uses a dict translation table -- so better suite to unicode
"""
return text.translate(dict_table)
# Maybe you noticed that there is a rot13 encoding build in to Python!
import codecs
def rot13f(text):
"""
This one "cheats" by using the built-in 'rot13' encoding
"""
return codecs.encode(text, encoding='rot13')
if __name__ == "__main__":
print (rot13a("Zntargvp sebz bhgfvqr arne pbeare"))
print (rot13b("Zntargvp sebz bhgfvqr arne pbeare"))
print (rot13c("Zntargvp sebz bhgfvqr arne pbeare"))
print (rot13d("Zntargvp sebz bhgfvqr arne pbeare"))
print (rot13e("Zntargvp sebz bhgfvqr arne pbeare"))
print (rot13f("Zntargvp sebz bhgfvqr arne pbeare"))
assert (rot13a("Zntargvp sebz bhgfvqr arne pbeare") ==
"Magnetic from outside near corner")
# rot13 should be reversible:
text = "Some random text to try!"
assert rot13a(rot13a(text)) == text
# they all should do the same thing
assert (rot13a(text) ==
rot13b(text) ==
rot13c(text) ==
rot13d(text) ==
rot13e(text) ==
rot13f(text)
)
print ("All assertions pass")
# # Some timings:
# # Note that the translate table versions are MUCH faster
# In [2]: timeit rot13a('This is a pretty short string, but maybe long enough to test')
# 10000 loops, best of 3: 31.5 µs per loop
# In [3]: timeit rot13b('This is a pretty short string, but maybe long enough to test')
# 10000 loops, best of 3: 32.5 µs per loop
# In [4]: timeit rot13c('This is a pretty short string, but maybe long enough to test')
# The slowest run took 9.36 times longer than the fastest. This could mean that an intermediate result is being cached
# 1000000 loops, best of 3: 1.03 µs per loop
# In [5]: timeit rot13c('This is a pretty short string, but maybe long enough to test')
# The slowest run took 10.23 times longer than the fastest. This could mean that an intermediate result is being cached
# 1000000 loops, best of 3: 1.02 µs per loop
# In [6]: timeit rot13d('This is a pretty short string, but maybe long enough to test')
# The slowest run took 6.75 times longer than the fastest. This could mean that an intermediate result is being cached
# 1000000 loops, best of 3: 1.1 µs per loop
# In [7]: timeit rot13e('This is a pretty short string, but maybe long enough to test')
# The slowest run took 8.59 times longer than the fastest. This could mean that an intermediate result is being cached
# 100000 loops, best of 3: 2.31 µs per loop