4 """Basic functions and constants for working with keyboard layouts."""
7 """Get the data from a file.
9 >>> read_file("testfile")[:2]
13 f
= open(path
, encoding
="utf-8")
20 # check if we got one via the commandline (and remove the argument if yes). Otherwise use the default.
22 if "--config" in argv
:
23 idx
= argv
.index("--config")
24 # the config module is the file without the extension.
25 cfg
= argv
[idx
+1][:-3]
26 # replace all / and \ with .
27 cfg
= cfg
.replace("/", ".")
28 cfg
= cfg
.replace("\\", ".")
29 argv
= argv
[:idx
] + argv
[idx
+2:]
30 exec("from " + cfg
+ " import *")
36 #: Die Layout-Datei für Neo = Tastenbelegung - Großbuchstaben integriert.
38 [("^", "ˇ", "↻", "˙", "˞", "̣"),("1", "°", "¹", "ª", "₁", "¬"),("2", "§", "²", "º", "₂", "∨"),("3", "ℓ", "³", "№", "₃", "∧"),
39 ("4", "»", "›", "", "♀", "⊥"),("5", "«", "‹", "·", "♂", "∡"),("6", "$", "¢", "£", "⚥", "∥"),("7", "€", "¥", "¤", "ϰ", "→"),
40 ("8", "„", "‚", "⇥", "⟨", "∞"),("9", "“", "‘", " /", "⟩", "∝"),("0", "”", "’", "*", "₀", "∅"),("-", "—", "-", "‑", ""),
41 ("`", "¸", "°", "¨", "", "¯"),("←")], # Zahlenreihe (0)
43 [("⇥"),("x", "X", "…", "⇞", "ξ", "Ξ"),("v", "V", "_", "⌫", "", "√"),("l", "L", "[", "⇡", "", "λ", "Λ"),
44 ("c", "C", "]", "Entf", "χ", "ℂ"),("w", "W", "^", "⇟", "ω", "Ω"),("k", "K", "!", "¡", "κ", "×"),("h", "H", "<", "7", "ψ", "Ψ"),
45 ("g", "G", ">", "8", "γ", "Γ"),("f", "F", "=", "9", "φ", "Φ"),("q", "Q", "&", "+", "ϕ", "ℚ"),("ß", "ẞ", "ſ", "−", "ς", "∘"),
46 ("´", "~", "/", "˝", "", "˘"),()], # Reihe 1
48 [("⇩"),("u", "U", "\\", "⇱", "", "⊂"),("i", "I", "/", "⇠", "ι", "∫"),("a", "A", "{", "⇣", "α", "∀"),
49 ("e", "E", "}", "⇢", "ε", "∃"),("o", "O", "*", "⇲", "ο", "∈"),("s", "S", "?", "¿", "σ", "Σ"),("n", "N", "(", "4", "ν", "ℕ"),
50 ("r", "R", ")", "5", "ρ", "ℝ"),("t", "T", "-", "6", "τ", "∂"),("d", "D", ":", ",", "δ", "Δ"),("y", "Y", "@", ".", "υ", "∇"),
51 ("⇘"),("\n")], # Reihe 2
53 [("⇧"),(),("ü", "Ü", "\#", "\e", "", "∪"),("ö", "Ö", "$", "", "ϵ", "∩"),("ä", "Ä", "|", "⎀", "η", "ℵ"),
54 ("p", "P", "~", "\n", "π", "Π"),("z", "Z", "`", "↶", "ζ", "ℤ"),("b", "B", "+", ":", "β", "⇐"),("m", "M", "%", "1", "μ", "⇔"),
55 (",", "–", '"', "2", "ϱ", "⇒"),(".", "•", "'", "3", "ϑ", "↦"),("j", "J", ";", ";", "θ", "Θ"),("⇗")], # Reihe 3
57 [(), (), (), (" ", " ", " ", "0", " ", " "), (), (), (), ()] # Reihe 4 mit Leertaste
60 [("^", "ˇ", "↻", "˙", "˞", "̣"),("1", "°", "¹", "ª", "₁", "¬"),("2", "§", "²", "º", "₂", "∨"),("3", "ℓ", "³", "№", "₃", "∧"),
61 ("4", "»", "›", "", "♀", "⊥"),("5", "«", "‹", "·", "♂", "∡"),("6", "$", "¢", "£", "⚥", "∥"),("7", "€", "¥", "¤", "ϰ", "→"),
62 ("8", "„", "‚", "⇥", "⟨", "∞"),("9", "“", "‘", " /", "⟩", "∝"),("0", "”", "’", "*", "₀", "∅"),("-", "—", "-", "‑", ""),
63 ("`", "¸", "°", "¨", "", "¯"),("←")], # Zahlenreihe (0)
65 [("⇥"),("l", "L", "…", "⇞", "ξ", "Ξ"),("v", "V", "_", "⌫", "", "√"),("x", "X", "[", "⇡", "", "λ", "Λ"),
66 ("c", "C", "]", "Entf", "χ", "ℂ"),("w", "W", "^", "⇟", "ω", "Ω"),("k", "K", "!", "¡", "κ", "×"),("h", "H", "<", "7", "ψ", "Ψ"),
67 ("g", "G", ">", "8", "γ", "Γ"),("f", "F", "=", "9", "φ", "Φ"),("q", "Q", "&", "+", "ϕ", "ℚ"),("ß", "ẞ", "ſ", "−", "ς", "∘"),
68 ("´", "~", "/", "˝", "", "˘"),()], # Reihe 1
70 [("⇩"),("u", "U", "\\", "⇱", "", "⊂"),("i", "I", "/", "⇠", "ι", "∫"),("a", "A", "{", "⇣", "α", "∀"),
71 ("e", "E", "}", "⇢", "ε", "∃"),("o", "O", "*", "⇲", "ο", "∈"),("s", "S", "?", "¿", "σ", "Σ"),("n", "N", "(", "4", "ν", "ℕ"),
72 ("r", "R", ")", "5", "ρ", "ℝ"),("t", "T", "-", "6", "τ", "∂"),("d", "D", ":", ",", "δ", "Δ"),("y", "Y", "@", ".", "υ", "∇"),
73 ("⇘"),("\n")], # Reihe 2
75 [("⇧"),(),("ü", "Ü", "\#", "\e", "", "∪"),("ö", "Ö", "$", "", "ϵ", "∩"),("ä", "Ä", "|", "⎀", "η", "ℵ"),
76 ("p", "P", "~", "\n", "π", "Π"),("z", "Z", "`", "↶", "ζ", "ℤ"),("b", "B", "+", ":", "β", "⇐"),("m", "M", "%", "1", "μ", "⇔"),
77 (",", "–", '"', "2", "ϱ", "⇒"),(".", "•", "'", "3", "ϑ", "↦"),("j", "J", ";", ";", "θ", "Θ"),("⇗")], # Reihe 3
79 [(), (), (), (" ", " ", " ", "0", " ", " "), (), (), (), ()] # Reihe 4 mit Leertaste
82 [("^", "ˇ", "↻", "˙", "˞", "̣"),("1", "°", "¹", "ª", "₁", "¬"),("2", "§", "²", "º", "₂", "∨"),("3", "ℓ", "³", "№", "₃", "∧"),
83 ("4", "»", "›", "", "♀", "⊥"),("5", "«", "‹", "·", "♂", "∡"),("6", "$", "¢", "£", "⚥", "∥"),("7", "€", "¥", "¤", "ϰ", "→"),
84 ("8", "„", "‚", "⇥", "⟨", "∞"),("9", "“", "‘", " /", "⟩", "∝"),("0", "”", "’", "*", "₀", "∅"),("-", "—", "-", "‑", ""),
85 ("`", "¸", "°", "¨", "", "¯"),("←")], # Zahlenreihe (0)
87 [("⇥"),("l", "L", "…", "⇞", "ξ", "Ξ"),("v", "V", "_", "⌫", "", "√"),("x", "X", "[", "⇡", "", "λ", "Λ"),
88 ("c", "C", "]", "Entf", "χ", "ℂ"),("q", "Q", "^", "⇟", "ω", "Ω"),("k", "K", "!", "¡", "κ", "×"),("h", "H", "<", "7", "ψ", "Ψ"),
89 ("g", "G", ">", "8", "γ", "Γ"),("f", "F", "=", "9", "φ", "Φ"),("w", "W", "&", "+", "ϕ", "ℚ"),("ß", "ẞ", "ſ", "−", "ς", "∘"),
90 ("´", "~", "/", "˝", "", "˘"),()], # Reihe 1
92 [("⇩"),("u", "U", "\\", "⇱", "", "⊂"),("i", "I", "/", "⇠", "ι", "∫"),("a", "A", "{", "⇣", "α", "∀"),
93 ("e", "E", "}", "⇢", "ε", "∃"),("o", "O", "*", "⇲", "ο", "∈"),("s", "S", "?", "¿", "σ", "Σ"),("n", "N", "(", "4", "ν", "ℕ"),
94 ("r", "R", ")", "5", "ρ", "ℝ"),("t", "T", "-", "6", "τ", "∂"),("d", "D", ":", ",", "δ", "Δ"),("y", "Y", "@", ".", "υ", "∇"),
95 ("⇘"),("\n")], # Reihe 2
97 [("⇧"),(),("ü", "Ü", "\#", "\e", "", "∪"),("ö", "Ö", "$", "", "ϵ", "∩"),("ä", "Ä", "|", "⎀", "η", "ℵ"),
98 ("p", "P", "~", "\n", "π", "Π"),("z", "Z", "`", "↶", "ζ", "ℤ"),("b", "B", "+", ":", "β", "⇐"),("m", "M", "%", "1", "μ", "⇔"),
99 (",", "–", '"', "2", "ϱ", "⇒"),(".", "•", "'", "3", "ϑ", "↦"),("j", "J", ";", ";", "θ", "Θ"),("⇗")], # Reihe 3
101 [(), (), (), (" ", " ", " ", "0", " ", " "), (), (), (), ()] # Reihe 4 mit Leertaste
104 # TODO: Add higher layers (shift for the numbers, …)
106 [("^"),("1"),("2"),("3"),("4"),("5"),("6"),("7"),("8"),("9"),("0"),("ß"),("´"),("←")], # Zahlenreihe (0)
107 [("⇥"),("q"),("w"),("e"),("r"),("t"),("z"),("u"),("i"),("o"),("p"),("ü"),("+"),()], # Reihe 1
108 [("⇩"),("a"),("s"),("d"),("f"),("g"),("h"),("j"),("k"),("l"),("ö"),("ä"),("#"),("\n")], # Reihe 2
109 [("⇧"),("<"),("y"),("x"),("c"),("v"),("b"),("n"),("m"),(",", ";"),(".", ":"),("-"),("⇗")], # Reihe 3
110 [(), (), (), (" "), (), (), (), ()] # Reihe 4 mit Leertaste
113 # from Ulf Bro, http://nordtast.org
115 [("^"),("1"),("2"),("3"),("4"),("5"),("6"),("7"),("8"),("9"),("0"),("ß"),("´"),("←")], # Zahlenreihe (0)
116 [("⇥"),("ä"),("u"),("o"),("b"),("p"),("k"),("g"),("l"),("m"),("f"),("x"),("+"),()], # Reihe 1
117 [("⇩"),("a"),("i"),("e"),("t"),("c"),("h"),("d"),("n"),("r"),("s"),("ß"),(),("\n")], # Reihe 2
118 [("⇧"),(),("."),(","),("ü"),("ö"),("q"),("y"),("z"),("w"),("v"),("j"),("⇗")], # Reihe 3
119 [(), (), (), (" "), (), (), (), ()] # Reihe 4 mit Leertaste
122 # TODO: Add higher layers (shift for the numbers, …)
124 [("^"),("1"),("2"),("3"),("4"),("5"),("6"),("7"),("8"),("9"),("0"),("ß"),("´"),("←")], # Zahlenreihe (0)
125 [("⇥"),("’"),(","),("."),("p"),("y"),("f"),("g"),("c"),("r"),("l"),("/"),("="),()], # Reihe 1
126 [("⇩"),("a"),("o"),("e"),("u"),("i"),("d"),("h"),("t"),("n"),("s"),("-"),(),("\n")], # Reihe 2
127 [("⇧"),(),(";"),("q"),("j"),("k"),("x"),("b"),("m"),("w"),("v"),("z"),("⇗")], # Reihe 3
128 [(), (), (), (" "), (), (), (), ()] # Reihe 4 mit Leertaste
131 # TODO: Add higher layers (shift for the numbers, …)
133 [("^"),("1"),("2"),("3"),("4"),("5"),("6"),("7"),("8"),("9"),("0"),("ß"),("´"),("←")], # Zahlenreihe (0)
134 [("⇥"),("q"),("w"),("f"),("p"),("g"),("j"),("l"),("u"),("y"),(";"),("["),("]"),("\\")], # Reihe 1
135 [("⇩"),("a"),("r"),("s"),("t"),("d"),("h"),("n"),("e"),("i"),("o"),("`"),(),("\n")], # Reihe 2
136 [("⇧"),(),("z"),("x"),("c"),("v"),("b"),("k"),("m"),(","),("."),("/"),("⇗")], # Reihe 3
137 [(), (), (), (" "), (), (), (), ()] # Reihe 4 mit Leertaste
141 # Ulfs All fingers equal but the small one
142 COST_PER_KEY_OLD
= [ # 0 heißt nicht beachtet
143 [0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Zahlenreihe (0)
144 [0,6,3,3,3,4,4,3,3,3,6,7,8,0], # Reihe 1
145 [0,2,1,1,1,3,3,1,1,1,2,6,0,9], # Reihe 2
146 [0,4,5,5,5,5,7,7,5,5,5,5,0], # Reihe 3
147 [0,0,0, 9 ,0,0,0,0] # Reihe 4 mit Leertaste
151 COST_PER_KEY_OLD2
= [ # 0 heißt nicht beachtet
152 [0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Zahlenreihe (0)
153 [0,6,3,3,3,4,4,3,3,3,6,7,8,0], # Reihe 1
154 [0,3,2,2,1,3,3,1,2,2,3,6,0,9], # Reihe 2
155 [0,5,5,5,5,5,7,7,5,5,5,5,0], # Reihe 3
156 [0,0,0, 9 ,0,0,0,0] # Reihe 4 mit Leertaste
159 #: The names of the fingers from left to right
160 FINGER_NAMES
= ["Klein_L", "Ring_L", "Mittel_L", "Zeige_L", "Daumen_L",
161 "Daumen_R", "Zeige_R", "Mittel_R", "Ring_R", "Klein_R"]
163 # Optimized structure for accessing by position. key_to_finger gets 3 times faster than with a cache and doublechecking.
165 for finger
in FINGER_POSITIONS
:
166 for pos
in FINGER_POSITIONS
[finger
]:
167 KEY_TO_FINGER
[pos
] = finger
169 ### Constants for testing
170 # Weighting for the tests — DON’T CHANGE THIS, it’s necessary for correct testing
171 TEST_COST_PER_KEY
= [ # 0 heißt nicht beachtet
172 [0,0,0,0,0,0,0,0,0,0,0,0,0,0], # Zahlenreihe (0)
173 [0, 12,9,6,4,10,10,4,6,9,12,15,18,0], # Reihe 1
174 [0, 5,3,3,2,5,5,2,3,3,5,12,0,15], # Reihe 2
175 [15,0,10,11,11,7,12,10,7,11,11,10,15], # Reihe 3
176 [0,0,0, 5 ,0,0,0,0] # Reihe 4 mit Leertaste
179 # Gewichtung der unterschiedlichen Kosten
180 TEST_WEIGHT_FINGER_REPEATS
= 8 #: higher than a switch from center to side, but lower than a switch from center to upper left.
181 TEST_WEIGHT_FINGER_REPEATS_TOP_BOTTOM
= 16 #: 2 times a normal repeat, since it's really slow. Better two outside low or up than an up-down repeat.
182 TEST_WEIGHT_POSITION
= 1 #: reference
183 TEST_WEIGHT_FINGER_DISBALANCE
= 5 #: multiplied with the standard deviation of the finger usage - value guessed and only valid for the 1gramme.txt corpus.
184 TEST_WEIGHT_TOO_LITTLE_HANDSWITCHING
= 1 #: how high should it be counted, if the hands aren’t switched in a triple?
185 TEST_WEIGHT_INTENDED_FINGER_LOAD_LEFT_PINKY_TO_RIGHT_PINKY
= [
195 0.5] #: The intended load per finger. Inversed and then used as multiplier for the finger load before calculating the finger disbalance penalty. Any load distribution which strays from this optimum gives a penalty.
200 # together with the more efficient datastructure for key_to_finger, these caches provide a performance boost by about factor 6.6
202 #_LETTER_TO_KEY_CACHE = {}
204 # TODO: Refresh directly when mutating. Then we don’t have to check anymore for the letter if it really is at the given position.
208 from copy
import deepcopy
213 def format_layer_1_string(layout
):
214 """Format a string looking like this:
221 l
+= "".join((i
[0] for i
in layout
[1][1:6])) + " " + "".join((i
[0] for i
in layout
[1][6:-1])) + "\n"
222 l
+= "".join((i
[0] for i
in layout
[2][1:6])) + " " + "".join((i
[0] for i
in layout
[2][6:-2])) + "\n"
224 l
+= "".join((i
[0] for i
in layout
[3][1:7])) + " " + "".join((i
[0] for i
in layout
[3][7:-1]))
226 l
+= "".join((i
[0] for i
in layout
[3][2:7])) + " " + "".join((i
[0] for i
in layout
[3][7:-1]))
230 def get_key(pos
, layout
=NEO_LAYOUT
):
231 """Get the key at the given position.
233 >>> get_key((2, 3, 0))
237 return layout
[pos
[0]][pos
[1]][pos
[2]]
240 def update_letter_to_key_cache(key
, layout
):
241 """Update the cache entry for the given key."""
242 try: LETTER_TO_KEY_CACHE
= layout
[5]
245 LETTER_TO_KEY_CACHE
= layout
[5]
247 for row
in range(len(layout
[:5])):
248 for col
in range(len(layout
[row
])):
249 if key
in layout
[row
][col
]:
250 key_num
= len(layout
[row
][col
])
251 for idx
in range(key_num
):
252 # make sure that the keys on lower levels always win agains those on higher levels.
253 # TODO (maybe someday): update the scheme to allow for multiple positions ⇒ only take the lowest cost.
254 idx_rev
= key_num
- idx
-1
255 if layout
[row
][col
][idx_rev
] == key
:
256 if pos
and pos
[2] < idx_rev
:
258 pos
= (row
, col
, idx_rev
)
259 LETTER_TO_KEY_CACHE
[key
] = pos
262 def update_letter_to_key_cache_multiple(keys
, layout
):
263 """Update the cache entries for many keys.
265 @param keys: the keys to update. If it’s None, update ALL.
275 update_letter_to_key_cache(key
, layout
=layout
)
278 def diff_dict(d1
, d2
):
279 """find the difference between two dictionaries.
286 >>> a == diff_dict(a, c)
298 def find_key(key
, layout
):
299 """Find the position of the key in the layout.
301 >>> find_key("a", NEO_LAYOUT)
303 >>> find_key("A", NEO_LAYOUT)
305 >>> find_key("e", NEO_LAYOUT)
307 >>> find_key(",", NEO_LAYOUT)
310 # check, if the layout already has a cache. If not, create it.
311 # this approach reduces the time to find a key by about 50%.
312 # TODO: find out why this change affects the costs of layouts!
313 # the cost is raised by a value between 1.2480213606 (NordTast)
314 # and 1.2964878374 (Colemak).
315 # a part of the change might be, that now uppercase keys
316 # are properly taken into account.
317 #if key != key.lower():
318 # raise ValueError("You shall not ask me for upperkey letters (yet)!")
320 try: LETTER_TO_KEY_CACHE
= layout
[5]
323 LETTER_TO_KEY_CACHE
= layout
[5]
324 update_letter_to_key_cache_multiple(None, layout
=layout
)
325 # first check the caches
326 try: pos
= LETTER_TO_KEY_CACHE
[key
]
328 # maybe we didn’t add the uppercase key, should only happen for incomplete layouts.
329 try: pos
= LETTER_TO_KEY_CACHE
[key
.lower()]
331 pos
= None # all keys are in there. None means, we don’t need to check by hand.
335 def finger_keys(finger_name
, layout
=NEO_LAYOUT
):
336 """Get the keys corresponding to the given finger name.
338 >>> for name in FINGER_NAMES:
339 ... name, finger_keys(name)
340 ('Klein_L', ['x', '⇩', 'u', '⇧', 'None', 'ü'])
341 ('Ring_L', ['v', 'i', 'ö'])
342 ('Mittel_L', ['l', 'a', 'ä'])
343 ('Zeige_L', ['c', 'e', 'p', 'w', 'o', 'z'])
346 ('Zeige_R', ['k', 's', 'b', 'h', 'n', 'm'])
347 ('Mittel_R', ['g', 'r', ','])
348 ('Ring_R', ['f', 't', '.'])
349 ('Klein_R', ['q', 'd', 'j', 'ß', 'y', '´', '⇘', '\\n', '⇗'])
351 keys
= [str(get_key(pos
, layout
=layout
)) for pos
in FINGER_POSITIONS
[finger_name
]]
354 def key_to_finger(key
, layout
=NEO_LAYOUT
):
355 """Get the finger name used to hit the given key.
357 >>> key_to_finger("a")
359 >>> key_to_finger("«")
361 >>> key_to_finger("⇩")
363 >>> key_to_finger("⇧")
366 pos
= find_key(key
, layout
=layout
)
367 # first check the cache
368 finger
= KEY_TO_FINGER
.get(pos
, "")
372 def string_to_layout(layout_string
, base_layout
=NEO_LAYOUT
):
373 """Turn a layout_string into a layout.
380 layout
= deepcopy(base_layout
)
381 lines
= layout_string
.splitlines()
382 # first and second letter row, the ifs are for replacing the second layer with uppercase, where aplicable.
383 for i
in range(1, 6):
384 if lines
[0][i
-1].upper() == lines
[0][i
-1]: # nonstandard keys.
385 layout
[1][i
] = (lines
[0][i
-1], ) + tuple(layout
[1][i
][1:])
387 layout
[1][i
] = (lines
[0][i
-1], lines
[0][i
-1].upper()) + tuple(layout
[1][i
][2:])
389 if lines
[0][i
+5].upper() == lines
[0][i
+5]: # nonstandard keys.
390 layout
[1][i
+5] = (lines
[0][i
+5], ) + tuple(layout
[1][i
+5][1:])
392 layout
[1][i
+5] = (lines
[0][i
+5], lines
[0][i
+5].upper()) + tuple(layout
[1][i
+5][2:])
394 if lines
[0][i
-1].upper() == lines
[0][i
-1]: # nonstandard keys.
395 layout
[2][i
] = (lines
[1][i
-1], ) + tuple(layout
[2][i
][1:])
397 layout
[2][i
] = (lines
[1][i
-1], lines
[1][i
-1].upper()) + tuple(layout
[2][i
][2:])
399 if lines
[0][i
-1].upper() == lines
[0][i
-1]: # nonstandard keys.
400 layout
[2][i
+5] = (lines
[1][i
+5], ) + tuple(tuple(layout
[2][i
+5][1:]))
402 layout
[2][i
+5] = (lines
[1][i
+5], lines
[1][i
+5].upper()) + tuple(tuple(layout
[2][i
+5][2:]))
404 if lines
[0][11].upper() == lines
[0][11]:
405 layout
[1][-3] = (lines
[0][11], ) + tuple(layout
[1][-3][1:])
407 layout
[1][-3] = (lines
[0][11], lines
[0][11].upper()) + tuple(layout
[1][-3][2:])
409 if lines
[1][11].upper() == lines
[1][11]:
410 layout
[2][-3] = (lines
[1][11], ) + tuple(layout
[2][-3][1:])
412 layout
[2][-3] = (lines
[1][11], lines
[1][11].upper()) + tuple(layout
[2][-3][2:])
415 if lines
[0][12].upper() == lines
[0][12]:
416 layout
[1][-2] = (lines
[0][12], ) + tuple(layout
[1][-2][1:])
418 layout
[1][-2] = (lines
[0][12], lines
[0][12].upper()) + tuple(layout
[1][-2][2:])
421 left
, right
= lines
[2].split()[:2]
422 for i
in range(len(left
)):
423 if left
[-i
-1].upper() == left
[-i
-1]:
424 layout
[3][6-i
] = (left
[-i
-1], ) + tuple(layout
[3][6-i
][1:])
426 layout
[3][6-i
] = (left
[-i
-1], left
[-i
-1].upper()) + tuple(layout
[3][6-i
][2:])
427 for i
in range(len(right
)):
428 if right
[i
].upper() == right
[i
]:
429 layout
[3][7+i
] = (right
[i
], ) + tuple(layout
[3][7+i
][1:])
431 layout
[3][7+i
] = (right
[i
], right
[i
].upper()) + tuple(layout
[3][7+i
][2:])
436 if __name__
== "__main__":
437 from doctest
import testmod