WIP
[evolve-layout.git] / layout_base.py
blob063d058af942ceb245fb3bbdfb869a803073e648
1 #!/usr/bin/env python3
2 # encoding: utf-8
4 """Basic functions and constants for working with keyboard layouts."""
6 def read_file(path):
7 """Get the data from a file.
9 >>> read_file("testfile")[:2]
10 'ui'
11 """
13 f = open(path, encoding="utf-8")
14 data = f.read()
15 f.close()
16 return data
18 ### get the config
20 # check if we got one via the commandline (and remove the argument if yes). Otherwise use the default.
21 from sys import argv
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 *")
31 else:
32 from config import *
34 ### Constants
36 #: Die Layout-Datei für Neo = Tastenbelegung - Großbuchstaben integriert.
37 NEO_LAYOUT = [
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
59 NEO_LAYOUT_lx = [
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
81 NEO_LAYOUT_lxwq = [
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, …)
105 QWERTZ_LAYOUT = [
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
114 NORDTAST_LAYOUT = [
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, …)
123 DVORAK_LAYOUT = [
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, …)
132 COLEMAK_LAYOUT = [
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
150 # First reweighting
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.
164 KEY_TO_FINGER = {}
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 = [
186 0.5,
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.
198 ### Caches
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.
206 ### Imports
208 from copy import deepcopy
211 ### Helper Functions
213 def format_layer_1_string(layout):
214 """Format a string looking like this:
216 öckäy zhmlß,´
217 atieo dsnru.
218 xpfüq bgvwj
220 l = ""
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"
223 if layout[3][1]:
224 l += "".join((i[0] for i in layout[3][1:7])) + " " + "".join((i[0] for i in layout[3][7:-1]))
225 else:
226 l += "".join((i[0] for i in layout[3][2:7])) + " " + "".join((i[0] for i in layout[3][7:-1]))
227 return l
230 def get_key(pos, layout=NEO_LAYOUT):
231 """Get the key at the given position.
233 >>> get_key((2, 3, 0))
236 try:
237 return layout[pos[0]][pos[1]][pos[2]]
238 except: return None
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]
243 except IndexError:
244 layout.append({})
245 LETTER_TO_KEY_CACHE = layout[5]
246 pos = None
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:
257 continue
258 pos = (row, col, idx_rev)
259 LETTER_TO_KEY_CACHE[key] = pos
260 return 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.
267 if keys is None:
268 keys = []
269 for line in layout:
270 for key in line:
271 for letter in key:
272 if letter:
273 keys.append(letter)
274 for key in keys:
275 update_letter_to_key_cache(key, layout=layout)
278 def diff_dict(d1, d2):
279 """find the difference between two dictionaries.
281 >>> a = {1: 2, 3: 4}
282 >>> b = {1:2, 7:8}
283 >>> c = {}
284 >>> diff_dict(a, b)
285 {3: 4, 7: 8}
286 >>> a == diff_dict(a, c)
287 True
289 diff = {}
290 for key in d1:
291 if not key in d2:
292 diff[key] = d1[key]
293 for key in d2:
294 if not key in d1:
295 diff[key] = d2[key]
296 return diff
298 def find_key(key, layout):
299 """Find the position of the key in the layout.
301 >>> find_key("a", NEO_LAYOUT)
302 (2, 3, 0)
303 >>> find_key("A", NEO_LAYOUT)
304 (2, 3, 1)
305 >>> find_key("e", NEO_LAYOUT)
306 (2, 4, 0)
307 >>> find_key(",", NEO_LAYOUT)
308 (3, 9, 0)
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]
321 except IndexError:
322 layout.append({})
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]
327 except KeyError:
328 # maybe we didn’t add the uppercase key, should only happen for incomplete layouts.
329 try: pos = LETTER_TO_KEY_CACHE[key.lower()]
330 except KeyError:
331 pos = None # all keys are in there. None means, we don’t need to check by hand.
332 return pos
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'])
344 ('Daumen_L', [' '])
345 ('Daumen_R', [' '])
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]]
352 return keys
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")
358 'Mittel_L'
359 >>> key_to_finger("«")
361 >>> key_to_finger("⇩")
362 'Klein_L'
363 >>> key_to_finger("⇧")
364 'Klein_L'
366 pos = find_key(key, layout=layout)
367 # first check the cache
368 finger = KEY_TO_FINGER.get(pos, "")
369 return finger
372 def string_to_layout(layout_string, base_layout=NEO_LAYOUT):
373 """Turn a layout_string into a layout.
375 öckäy zhmlß,´
376 atieo dsnru.
377 xpfüq bgvwj
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:])
386 else:
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:])
391 else:
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:])
396 else:
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:]))
401 else:
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:])
406 else:
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:])
411 else:
412 layout[2][-3] = (lines[1][11], lines[1][11].upper()) + tuple(layout[2][-3][2:])
414 if lines[0][12:]:
415 if lines[0][12].upper() == lines[0][12]:
416 layout[1][-2] = (lines[0][12], ) + tuple(layout[1][-2][1:])
417 else:
418 layout[1][-2] = (lines[0][12], lines[0][12].upper()) + tuple(layout[1][-2][2:])
420 # third row
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:])
425 else:
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:])
430 else:
431 layout[3][7+i] = (right[i], right[i].upper()) + tuple(layout[3][7+i][2:])
433 return layout
436 if __name__ == "__main__":
437 from doctest import testmod
438 testmod()