comparison aggregate.py @ 7:8ee385f883e3 draft

Uploaded
author kaymccoy
date Fri, 12 Aug 2016 16:45:00 -0400
parents
children
comparison
equal deleted inserted replaced
6:7357fa7c34aa 7:8ee385f883e3
1 # A translation of aggregate.pl into python! For analysis of Tn-Seq.
2 # This script requires BioPython just like calc_fitness.py, so you need it installed along with its dependencies if you want to run these scripts on your own.
3 # How to install BioPython and a list of its dependencies can be found here: http://biopython.org/DIST/docs/install/Installation.html
4 # K. McCoy
5
6
7
8
9
10
11
12
13
14 ##### ARGUMENTS #####
15
16 def print_usage():
17 print "Aggregate.py's usage is as follows:" + "\n\n"
18 print "\033[1m" + "Required" + "\033[0m" + "\n"
19 print "-o" + "\t\t" + "Output file for aggregated data." + "\n"
20 print "\n"
21 print "\033[1m" + "Optional" + "\033[0m" + "\n"
22 print "-c" + "\t\t" + "Check for missing genes in the data set - provide a reference genome in genbank format. Missing genes will be sent to stdout." + "\n"
23 print "-m" + "\t\t" + "Place a mark in an extra column for this set of genes. Provide a file with a list of genes seperated by newlines." + "\n"
24 print "-x" + "\t\t" + "Cutoff: Don't include fitness scores with average counts (c1+c2)/2 < x (default: 0)" + "\n"
25 print "-b" + "\t\t" + "Blanks: Exclude -b % of blank fitness scores (scores where c2 = 0) (default: 0 = 0%)" + "\n"
26 print "-f" + "\t\t" + "An in-between file carrying information on the blank count found from calc_fitness or consol_fitness; one of two ways to pass a blank count to this script" + "\n"
27 print "-w" + "\t\t" + "Use weighted algorithm to calculate averages, variance, sd, se" + "\n"
28 print "-l" + "\t\t" + "Weight ceiling: maximum value to use as a weight (default: 999,999)" + "\n"
29 print "\n"
30 print "All remainder arguements will be treated as fitness files (those files created by calc_fitness.py)" + "\n"
31 print "\n"
32
33 import argparse
34 parser = argparse.ArgumentParser()
35 parser.add_argument("-o", action="store", dest="summary")
36 parser.add_argument("-c", action="store", dest="find_missing")
37 parser.add_argument("-m", action="store", dest="marked")
38 parser.add_argument("-x", action="store", dest="cutoff")
39 parser.add_argument("-b", action="store", dest="blank_pc")
40 parser.add_argument("-f", action="store", dest="blank_file")
41 parser.add_argument("-w", action="store", dest="weighted")
42 parser.add_argument("-l", action="store", dest="weight_ceiling")
43 parser.add_argument("fitnessfiles", nargs=argparse.REMAINDER)
44
45 arguments = parser.parse_args()
46
47 if not arguments.summary:
48 print "\n" + "You are missing a value for the -o flag. "
49 print_usage()
50 quit()
51
52 if not arguments.fitnessfiles:
53 print "\n" + "You are missing fitness file(s); these should be entered immediately after all the flags. "
54 print_usage()
55 quit()
56
57 # 999,999 is a trivial placeholder number
58
59 if (not arguments.weight_ceiling):
60 arguments.max_weight = 999999
61
62 # Cutoff exists to discard positions with a low number of counted transcripts, because their fitness may not be as accurate - for the same reasoning that studies with low sample sizes can be innacurate.
63
64 if (not arguments.cutoff):
65 arguments.cutoff = 0
66
67 # Gets information from the txt output file of calc_fit / consol, if inputted
68
69 if arguments.blank_file:
70 with open(arguments.blank_file) as file:
71 blank_pc = file.read().splitlines()
72 arguments.blank_pc = float(blank_pc[0].split()[1])
73
74 if (not arguments.blank_pc):
75 arguments.blank_pc = 0
76
77
78
79
80
81 ##### SUBROUTINES #####
82
83 # A subroutine that calculates the average, variance, standard deviation (sd), and standard error (se) of a group of scores; for use when aggregating scores by gene later on
84
85 import math
86 def average(scores):
87 sum = 0
88 num = 0
89 for i in scores:
90 sum += i
91 num += 1
92 average = sum/num
93 xminusxbars = 0
94 for i in scores:
95 xminusxbars += (i - average)**2
96 variance = xminusxbars/(num-1)
97 sd = math.sqrt(variance)
98 se = sd / math.sqrt(num)
99 return (average, variance, sd, se)
100
101 # A subroutine that calculates the weighted average, variance, standard deviation (sd), and standard error (se) of a group of scores; the weights come from the number of reads each insertion location has
102 # For use when aggregating scores by gene later on, if the weighted argument is called
103
104 def weighted_average(scores,weights):
105 sum = 0
106 weighted_average = 0
107 weighted_variance = 0
108 top = 0
109 bottom = 0
110 i = 0
111 while i < len(weights):
112 if not scores[i]:
113 scores[i] = 0.0
114 top += float(weights[i])*float(scores[i])
115 bottom += float(weights[i])
116 i += 1
117 if bottom == 0:
118 return 0
119 weighted_average = top/bottom
120 top = 0
121 bottom = 0
122 i = 0
123 while i < len(weights):
124 top += float(weights[i]) * (float(scores[i]) - weighted_average)**2
125 bottom += float(weights[i])
126 i += 1
127 weighted_variance = top/bottom
128 weighted_stdev = math.sqrt(weighted_variance)
129 weighted_stder = weighted_stdev/math.sqrt(len(scores))
130 return (weighted_average, weighted_variance, weighted_stdev, weighted_stder)
131
132
133
134
135
136
137
138
139
140
141 ##### AGGREGATION / CALCULATIONS #####
142
143 #Reads the genes which should be marked in the final aggregate file into an array
144
145 import os.path
146 if arguments.marked:
147 with open(arguments.marked) as file:
148 marked_set = file.read().splitlines()
149
150 #Creates a dictionary of dictionaries to contain a summary of all genes and their fitness values
151 #The fitness values and weights match up, so that the weight of gene_summary[locus]["w"][2] would be gene_summary[locus]["s"][2]
152
153 import csv
154 gene_summary = {}
155 for eachfile in arguments.fitnessfiles:
156 with open(eachfile) as csvfile:
157 lines = csv.reader(csvfile)
158 for line in lines:
159 locus = line[9]
160 w = line[12]
161 if w == 'nW':
162 continue
163 if not w:
164 w == 0
165 c1 = float(line[2])
166 c2 = float(line[3])
167 avg = (c1+c2)/2
168 if avg < float(arguments.cutoff):
169 continue
170 if avg > float(arguments.weight_ceiling):
171 avg = arguments.weight_ceiling
172 if locus not in gene_summary:
173 gene_summary[locus] = {"w" : [], "s": []}
174 gene_summary[locus]["w"].append(w)
175 gene_summary[locus]["s"].append(avg)
176
177 #If finding any missing gene loci is requested in the arguments, starts out by loading all the known features from a genbank file
178
179 from Bio import SeqIO
180 if (arguments.find_missing):
181 output = [["locus","mean","var","sd","se","gene","Total","Blank","Not Blank","Blank Removed","M\n"]]
182 handle = open(arguments.find_missing, "rU")
183 for record in SeqIO.parse(handle, "genbank"):
184 refname = record.id
185 features = record.features
186 handle.close()
187
188 #Goes through the features to find which are genes
189
190 for feature in features:
191 gene = ""
192 if feature.type == "gene":
193 locus = "".join(feature.qualifiers["locus_tag"])
194 if "gene" in feature.qualifiers:
195 gene = "".join(feature.qualifiers["gene"])
196 else:
197 continue
198
199 #Goes through the fitness scores of insertions within each gene, and removes whatever % of blank fitness scores were requested along with their corresponding weights
200
201 sum = 0
202 num = 0
203 avgsum = 0
204 blank_ws = 0
205 i = 0
206 if locus in gene_summary.keys():
207 for w in gene_summary[locus]["w"]:
208 if float(w) == 0:
209 blank_ws += 1
210 else:
211 sum += float(w)
212 num += 1
213 count = num + blank_ws
214 removed = 0
215 to_remove = int(float(arguments.blank_pc)*count)
216 if blank_ws > 0:
217 i = 0
218 while i < len(gene_summary[locus]["w"]):
219 w = gene_summary[locus]["w"][i]
220 if removed == to_remove:
221 break
222 if float(w) == 0:
223 del gene_summary[locus]["w"][i]
224 del gene_summary[locus]["s"][i]
225 removed += 1
226 i -= 1
227 i += 1
228
229 #If all the fitness values within a gene are empty, sets mean/var to 0.10 and Xs out sd/se; marks the gene if that's requested
230
231 if num == 0:
232 if (arguments.marked and locus in marked_set):
233 output.append([locus, "0.10", "0.10", "X", "X", gene, count, blank_ws, num, removed, "M", "\n"])
234 else:
235 output.append([locus, "0.10", "0.10", "X", "X", gene, count, blank_ws, num, removed, "\n"])
236
237 #Otherwise calls average() or weighted_average() to find the aggregate w / count / standard deviation / standard error of the insertions within each gene; marks the gene if that's requested
238
239 else:
240 if not arguments.weighted:
241 (average, variance, stdev, stderr) = average(gene_summary[locus]["w"])
242 else:
243 (average, variance, stdev, stderr) = weighted_average(gene_summary[locus]["w"],gene_summary[locus]["s"])
244 if (arguments.marked and locus in marked_set):
245 output.append([locus, average, variance, stdev, stderr, gene, count, blank_ws, num, removed, "M", "\n"])
246 else:
247 output.append([locus, average, variance, stdev, stderr, gene, count, blank_ws, num, removed, "\n"])
248
249 #If a gene doesn't have any insertions, sets mean/var to 0.10 and Xs out sd/se, plus leaves count through removed blank because there were no reads.
250
251 else:
252 if (arguments.marked and locus in marked_set):
253 output.append([locus, "0.10", "0.10", "X", "X", gene, "", "", "", "", "M", "\n"])
254 else:
255 output.append([locus, "0.10", "0.10", "X", "X", gene, "", "", "", "", "\n"])
256
257 #Writes the aggregated fitness file
258
259 with open(arguments.summary, "wb") as csvfile:
260 writer = csv.writer(csvfile)
261 writer.writerows(output)
262
263 #If finding missing genes is not requested, just finds the aggregate w / count / standard deviation / standard error of the insertions within each gene, and writes them to a file, plus marks the genes requested
264 #This is never called through Galaxy since finding missing genes is just better than not finding them.
265
266 else:
267 output = [["Locus","W","Count","SD","SE","M\n"]]
268 for gene in gene_summary.keys():
269 sum = 0
270 num = 0
271 average = 0
272 if "w" not in gene_summary[gene]:
273 continue
274 for i in gene_summary[gene]["w"]:
275 sum += i
276 num += 1
277 average = sum/num
278 xminusxbars = 0
279 for i in w:
280 xminusxbars += (i-average)**2
281 if num > 1:
282 sd = math.sqrt(xminusxbars/(num-1))
283 se = sd / math.sqrt(num)
284 if (arguments.marked and locus in marked_set):
285 output.append([gene, average, num, sd, se, "M", "\n"])
286 else:
287 output.append([gene, average, num, sd, se, "\n"])
288 with open(arguments.summary, "wb") as csvfile:
289 writer = csv.writer(csvfile)
290 writer.writerows(output)
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386 #
387 # ~MMM=:DMMM?, +NMMO=,:~I8MMMMM8+, , ~I8MMMMMN87~?8NNMMN8: +NMND~ +MN= ,$MMMI ?M8, ,OM8, :MN+ =MM? ,MMDNMMD ,+DM8I, ,,:::~~~::::::::::
388 # IMMMNMM8I ,I8MM87~::+$8NNMMMMOI+=~~:, ,,:~=?$DNMMMMMMDOZI7ZDMMMD8I , , $M8+?8MM8I , 7MI +MN= ZMN, 8MD MMN8MMM, :$ONM8I+=:, ,,,::::~~~~~=====~:
389 # , ,DMNN7: , ,OMMN7==~::~=?8NNMMMMMMNNMMMMMMMMMMMN8OO8ODNMMMMMD~ , IMMNMN~ ,OM+ ,NM$ ,NMO, :MM$ , ,:::,,::::,, $MNMMNM, ,,, :?ONMMNN8?~,, , ,,,,,,,::~~=+++??=~
390 # ,,:=+????+, I$ :ZMMN8$~:,,, ,:=?7$O8DD8O$7+==+$O8DNMMMMMMMMM$ ?$, == ,~, ~NM= 8MD, ,OM8ZMMO , ,::::::~~~:,, ?MNMMZ ,,,,,, ,+7ONMNMD8O$+~, ,,,,,,::~====::
391 # ,:=IONMMMMMMMMMMM8: ZN$: ,~DMMMND7=, ,,:~====:=$DNMMMMMMMN88MMMZ +N$ , ,7DN8= =MN, IMM =DMN7 ,,,,,,,,,, ,~?, ,,,,, , ~?$8MMMMNN8Z?~:,, ,:::,,,
392 #+ONMMMMMMNO7=:,, ,,+MMO, 7D$: ~OMNMNNMNNNNNNNNNNNMMMMMMMMMNMMMM?,~MMM8 ND, 7MM=, ?NN:, +MO, ?MM, ,,,, , :,:=$DMMMMMMN87=~, ,,,,,
393 #MMND8$: , 7NM7 , =?~,,, :?88DDDNNMNNNDNDD88Z?:, ZMM$ ,MMMO ,MM+ZNMM? ::ZNZ, +M$ ,MM?, , ,, ,,:=?Z8NMMMMMN8Z+=,
394 #: ~ZMM8~ ,,,, 8MM, +MMM, 7ZZI~=$OOZ$: ,:+???+, +MZ =MN= ,,, , ,,:~=?IIII$ZO88DDNNNNNNMMMMMMMMMMN~,
395 # ,:OMMM? ,,:,, 8MM ~NNM7 :OMMMMMMMMO: =M8, :NM~ = ,~?I, ,,,,,,,,,,,,,, ,~$DNMMMMMMMMMND8O888Z$II7777I??+++===:,
396 # ?DMM8?, OMN??NMM$ ~8MMMO?===7MMM8: ~NM= =NN: ,OM~ ,, +NMMN~ ,,,,, ,,,,,,,, ,?$O8NNMDDZ7?+=~:, , , ,
397 # , ~$MMMD+ , , ~MMNMMN~ : +NMMZ, NMNM~ 7MI , ZNN,, IMN, :DMNM+ ~+NMMMD~, ,,,,,,,, ,, ,,,,,:, +OMNMMOI~,
398 # , $MMMM$, , ,=?ODNMNNMNMMMMNNND~ ,$D$, , ,8MM8 ,MMM7 ,ZMNNMM= DMMNMMMMMMMMMMMMNI: , ,,,,,,,,, ,,, ?NMO=, ,
399 # ,~ZNMND7, ,,:~=+$DNNMMMMNDDDD888OZZZZ8NMMN IMMN: ,MMN~ +Z$+, ?NNNDO+:?O888OI, ,,,,,,, ,,, +MN+
400 # =ONMMM8~ , ,:=IDMMMMMMMND8$+:, , ,INMNZ :MMM~, , +MMD , ,, ,,::,,,, ,,:::, ?M8: ,
401 #8MMMNZ~ , =I$ONMMNDZ7?+, ,,=I8NMD7: DMMN DMN= ,:::, ,:~~=~~:,, :ZMMZI+: ,, ,,,,,,,
402 #MN7:,:=7ONMMMD$?=, , ~7ODNMM$+: ~MMM++7ZOZOO8O8D8$~ ,MM8 ,,,,::~~==~~:,, :+7DMMMMNNDD88OOZZZO88DNNNN8=, ,,,,,,,
403 #I,~+DMMNND7: , ,$MMMMMN7, $MMN :??+=~::,,,, NMD, ,:,,:::~=~~,,,, ,,=I$8DNMMMMMMMMMNMMMNZ: ,,,,,,
404 #DNMNN7:, ,+ONMMMMNI: NMM$ DMN= ,,,,:::::~~::,, ?DMMMMDZ=, ,,,
405 #N8$: ,,, ,:=?ONMMMD8Z+, ,,,, MMM= ZMMI ,=?$8NMMMMMMMMMMMN87=~~,,, :=ZMMMMD$~
406 # , ,=ZNMMMMMNI:, ,~?Z88888$=, ,:~+??~, MMM, IMM$ ,=ZNMMMMMN8$+~=~=~~===7ODNMMMN8DNMMMN+, ,,
407 # , ~?$ODMMMMNZ?: :II+~, ,=7= :?77?=:====?O+ ,,:,,, MMM, ?MM$ ,,,, :?ONMM8II=, , =DMMMM87=, ,,,,
408 # , ,, ,~I8MMMMMMN87?~:=+?7$ZOO88DD888O$I+~:, ~ZZ: ,$7,~??, ,?+ ,+Z8$?==??= MMM =MM$ :?ODNNNNNNNMMO: ,:?NNMNO= ,,,IMMMNZ, ,,,:,,,,,,,,,,,,,
409 #, ,~7DNMMMMMMMMMMMNNNNMMMNND8Z7II7$$$$ZODNNNMND$, :O$: , ,IN$, I+ +ZZ=,, 7+ MMM =ODDDDNNNNNN8= :MM$ ?DDNN8?::,, ,,7NMM8, 7NMNZ~, :OMMM$, , ,,:::~~:,,,,,,,,,,,,,,,,,
410 #?8NMMMMMMMMMMMN8I:,,, ~$NMMM$ :87 +DM$ ID+~78I, O7 :=+~ MMM , , ?MM7 $NMN$, :I8MMMMMMNMDNNNNNNNNNDD88ZI=: ,ZMM7, +MMN~ ,,,:::,, ,,:,::::,,,,,,
411 #MMMD8DNMDZ7=: ,:=+7ZNNNDOZ~ =DI , :7MM7, IDDZI, =DN88ZI77$N? NMM: $MM= ~: =OMNZ+ ,=7DMMMMMMNDDOOOOZ$7IIIII77$ZOO8NNMMD$+~ :OND~, ,, MMN= ,,,,,,,,,,,,,
412 #: ,,, , ,:+ZNMNNMMNO?: +8? +NMN7 , , ON~ 8MM$ NMD, ,7MMMN, 7MMO, ,:ONMMMMN8I: ,~ZNMNN$, ~MM? , ZMNND?~: ,,, ,, ,,,,,,
413 # , , ,:~?7$ZZ8NNMMMNO7I=, ~D+ ZNO=, , :NM$: =MMM NMO ~NMNMMM :ZDN$, ,,=7DMD$?=, :?ZMD$: :DMNOZZZ$: ,,,,, ~IDMMMMMMMMMMMMMMMMMMMMM8~
414 # , =DMNNNNNNNN8$= : , ,,~?ODNZ: :DM? =: ,MMM7 +MN~ ,MM8:NMN INMZ, ,=ONOI , +NMZ ,,+ZDMMMMMMMN+, ,,,,,,, ,~?$ODNNNNNNNMMMMMMMMD= ,
415 # ,:::::~7$I?=: ,~78NMNMMMMM? :+Z+ M8 $MM, =, DMMD NMN DMO OMM77NMI, ?8$~ , ~ZDDDNNNNNMMMMMMMMNMNNNNNDD8Z+:, IMN: I8DNMMMMN7~: ,,,,,, +$O$, ~$DMMMMMMMD~ ,, ,
416 # :I+ :ID? ~ZDNNNNMMNO?~,, ,:::::=7ONMNNNDMMMN$ ,=IONNMMMMZ:, ~MO ,7MMMI $MN, , :MMN ?8NDDNND$~ +MM~ ,MM~=MMMNMD, =ONNDDNMMMMMMMMMMMMNNND88DNNNNNMMMMMMMNDO7+~::,:,7MM+ =DMMMNNO? ~ZMMMMM7::~INMMMMMMMMMMMMMN8:
417 #:MMM+,INMMM: ,:~+78NMMMM8?==++++++???++??I$Z77$$$$$$$7II??I$ZZO8MMMMM8Z7~ ,IZI ~77+ ?MMMMD88MN8+~, +8MO$OMMMMMNMMMZ, ~=: OMM? ,MM? ZMM~MMMM8~ ,:+7$$$$ZZ7?==: :8MDOZ$ZZZODMMMMMM8+, ,:=?$ZZOOOOOOZ$: , , ,=8MNMMMMMMMMMMNDZ$7$MMMMMMMMMMMMMMMMMMNI+?I7ZDNMMMMMMMMMM$,
418 #=MMMZ$MMMMMM~ ,::::~==~~+I$8NMMMMMN$::::::::,,, ,,=ONDDO$7II?+~, , ,,$DD87: =NND= , ,+$$=:~, ,:, ,MMD NMI ,~?Z8DND88$?: 8MM$MMMZ:=~:,~~:::,,,=$DNMND$MM8, ~DMNMMMN?, :7$7?+==~=:,,,,,,,, ,,,,,,, ,,,,,,::,,,,,:::::::,,,::::::,, ,OM$,7MMMMMMNZ= , ~8MMMMMMMNDO7I7MMMMMMMMMMMMN8Z7+?NMMMD,,
419 #NMMMMMM? ,++, ~=I??= :7$ 7MM+ 7M7 ,:?ZZ$MMM8NMZ~+, :?INMMM? +$NMMMNZ: :+?I7$$Z$O88D88DDDDNNNNNNNNNNNNNNNNNNDDNDDDNNNNNMMNMMMMMMMNNNN$:??~ ,+II?=, , ,, ,?I??+=~, :MMMMMNM
420 #MMMMMD~ ,:=++++++++++=~,,, +MMN: ,MMD, :M$ ,INMNMMMMMMMMMM~~~?D, :OMM8: ,+$8Z$+,,$MMMMMD, :IODDDD
421 #N$~,,, ,~I$8NMMMMMMMMMNMMMMMMMMMMMMMMMMNNNN7: ZMMMM? NMMMNZ~:, +$: ,NMNI:::~?8MMM7I? IO ~I$ODNNNDND8OO$I?DMMMD$I8NMMMMMMMNMNMMMMMMM8=, ,
422 # ,:~?ONMMMMNNNDDD8NMD+, ,~?Z8NMNM8=, , $MNNNMM? :DMMMNDD8DZ7$+, ,8NMMNMNMMMD$: =8, ,INMMMNZI?====+I$$8DDNMMMMMMNNNMND7, :~$DMMMNMO
423 # ,?DMMMMMNNNMMM+::~++ZMMMM8ZZZZZZ$II77II7???=~:, ,+DMN7 =NMM8, ,NMMNMNMN$?I7I77OZ~ ~8D$~, ,MMMMMMNM8: :DMMM, , DMO=ZMZ,
424 # ,?OMMN87=, IZNNNMMMMMMMMMMND7IIII??III$8DDNN8Z+, , ~MM ?OMMMM~ ,MMMMMZ+ ~I= IMMMO$$= ?NNM? MN~ +NM:
425 # IMM8~ =MD, ?N= :,, ,+77?=+, $8MM7::OMMMMMMZ+ I+, ?NI , :MMM8$ NNI, 7M$
426 # ,MMN $MD 7M+ ~ODNZ~, :7MMN? , $MMMN= 7MNMN: +8+ ,D8~ =MMDD8Z= =NMD OM7,
427 # OMMD~ IMN: =8O:,~+$OO? :IZ+: :ZMMNM8= =DMMI ~8MM8= , ,, :8M?,$MM, ::, DM, =Z~ ,I8DDDN8$DMN~ MN ,=Z8DNDZZ= MD: ,
428 # ,,,,,,,, ?8NNMM8$I????++=+8MMMD$77$ZDMMNMMMDNOZ~ :IZ8DOI~, =MMMNMNM8I:, ,7M8I +MND7 ~MNDDM8~ 7MI MM7 :8MDDM7 8N= ~ID$, ,:Z$?:,,,=ONNMNMD= ?M$ :: +MI ,,,,,,::~~:::,,,,,,, ,+OMO:, ,
429 # ,,,::~===~~:, ,~+I$88DNNNNNNNNNDNDD8O$?~:, ,7DNMNDMMMN~,ZMND$8NNDZ=, $M? ?NNN7 =MO OMD$MM= MMMINMO $M7 DN: 8MMD: ?D~ :O8ZDMMMMD=, ,ZN$ ,INNMD= :NO ,:+8MMMMMMMMDI:,::,,
430 #=~~~::~~::, ~?78MMMDNMM? :+I$DD87?=, OMNDO$7$OI: 7N+ ?MNMN~ MMMNM$ 7M$ 7M$ +NMMO, ?N~ ~8+ ~7NMMNNOI= ~?ONZ+ ~ZNND,$M8 ~MO $MMMMMMMMMM7:::~~~~~~=+++===~~~:::,,
431 # ,~ONMMNO~, :?8NNMN8?~ZNMMMMMMMMMNMMNDOI=~NM? =MMM+ NMMMO 8M$ ,MM, $MMMN =D, ,?N~ , ?NMMMNNNDDO8DDD887, ,$ND= :O$NMZ IMO ,IMMMMN$, 7NO, ,,,,,:,,,
432 # ,~IDMNN8OI, :~+$DMMMMMMMN87+=~~~~?ZDNNMM, ?NM? :NMN :MM7 ~MN ~DMMMN O~ :DD, ,MMMMNNOI:,, , ,=ZODD8D? :MMM: NMZ +8NM8, ,, :~ ,,~:,
433 # ,INNMMNNO+ :=78NNZIIONMNNDMMN ,7D~ :MN~ MNMM?=MM $? OD, ,IMD ,:8MNMNNNNNNNNMMNM7: :8~ ZND OMN7, :NNMMMMN :8MMMMMNZ=:
434 # :O88NMMN$=~:, ?MMN88NMMMD =MN? =, :NM +7$NO, =N8 , OMI, ,, 7M: ZDDND? MMO ,, +MM8?OMN: ,::
435 # :$NMMMNMM8I: ,8MD, +MMN= ,=?77=:ZMMM7 ?MN ~$+ 7M7 ,DZ :M~ IMM8: ,8MN, , ,,,, :MMD,=DMM+,?O8= , ,
436 # ,=I77$ONNNZ?+ONMZ =$D ,+=::+$8NNMNDNMD7?, ?MN :~~: ~D7 ,NI ,M7 ~?ZNZ?, 8MM~ =ZMMN~$MMMMMMMNMMD$ONMMMMD:
437 # ~DMM= ~I7=, =DMMMD7MMM, :+?=?O$: ~N: ~MN88D$: NMM=, ~MMMMMMMMMMMMOZMMMMMM7::8MMI
438 # , :DNNI :~IDMMND :::=?II?==~::ZN7=+I$ZZZ8DZ+~~: IMMM~ ~MMMNMMMMMI~:7NMMMMD7,: +NM8,
439 # :NMM7, ~MM+ ,,,:~==~~~: , OMMN: ?NNMMMMM8, ~NMNO, =MMMN?,,
440 # ,,,,,,,, ,$8MMO= ~M8 +MNO: ,MM~ , :: ,~, ,,:::,,,,
441 # ,::,,,, ~ONND+ OD+ ?NMD, ?? ,::::::,,
442 # :=+??=:,,,,, ~?$D87I: ~Z? =$DM$: ,::,,,,,,,,
443 # ,:~==~, :+=, $NNNNZ~ ,,:~~: , :DMMMI
444 # ,~~~~~:,,, ,,, ,~IMMMN8O+, ,:~?7$Z7~, :ZDMNNDNM8ZI
445 # ,,~~:,, , :$DNNMMN8?, , ,~7ZOO?: ,:$NMMMM? :7NMMD?,
446 # ,:~~:,,, $NMMMMMNMNO?~ ~?ODDD$=, :?8MMMMMD?~7NMMMD$~ :ONMMN?
447 # ,,,,,,,,, ?$DMMMMMMMMMMD$?=~, ,~7ZZODN87????I$ODNMDOZ$7I: ~$ZDMD7==~ =$ZDND$++~,
448 # ~?++=~~, ~+?II7ZNMMMMMM8$$$?~, ,~?II7Z8DDOZ$77II+: ~?IZDM8O$I~, :??$8MMNZZ7+,
449 # , , ,+D7 :=IZ8NMMMNNMNNO$= ,:?ZDNNMMMNMND8Z7=, =ZNMMMMMD?,, :ONMNMMMN?,
450 # ,MMMM7 ,,::~IONNMMNNDDD8OZI=, ,::::=+I$ONMNNNNDDDNNMMMMMMMMMD87: ,:~=ZNNNDOI, ~7$ZO8DDNNNNDD8O+:,
451 #~, +MMMMDI?~ ,~+Z8DNDNNMMMMDD$+:,,, , ,~?7O8DNNNNNNMMMMMNOOZI??++?IZ88$: ,,~ZDMMMMMMMMMMMMMMMMMMNNNNNMMMNND8$=,,
452 # ,~: NMMMMOZMM8: ,7DZ~ ,,~IONMMMMMMMMMNDZ+~: , ,=I8DNMMMMMNMNMMMMMDNMMMMNMMMNNNDDDDD8O8Z$7II+++IZDDMMNMN$,,
453 # :~: MMMMMNMMM+ :, +?DOI~ ,:~?7$$ZODDNMNN8Z7??+=~:, ,~=+?7I?=:,,,,:=?I7$$$$$$$ZZZOO8DDNNMMNNNNNMMMMNMZ+
454 # ~=, MMMMMMMM8 ,NNNO , ,~?ZDNMNNNNNNND8O7?:,,, ,~=7DMMMMMMMMMZ~
455 # ,:+~ ,MMMMMMMMO ~N8: ,,:~, ,:~:,,, ,:~, ,:~~=+++?7$O8DNNNNNMMN8$=, ~+$ZO8DNNMMNNNND8OOOZ$+:, :=+I8MMMMDOI
456 # ,=+ MMMMMMMMMDMZ, ~?: ,,,,, ,:~~~, ,NN?$NMMMMMMMMMMMN87~:,,,=+=:,,,,,,:?NMMNMMMMMMNNDNDI8MMM+ ,
457 # +=, $MMMN?$NMN, ,+?+:, ,,:~~:, :=~, 8M= , :7ONNNO?~, , :$NNMMMMMMMMMMMMMN,
458 # ,+? ,NNO,, , :ODNNNZ: :?777I= IMO ,,=$Z7+~ ,?NNMMNZ: :7NMMMMMNMMMM8 , ,IDND~
459 # ,=~, 7I :+I+~::,, ~?I~ , ,~=~==: ZMZ ?8I: :I$DND$?~ :?$8MMN$?: ?ZDMMMMMMMM7 +$NMMMMMI
460 # ~~ , ,=+=: ,== ::, , ZMM: ?NN8: ,7NMMNI, ,$NMMMO: =NMMMNMM+ I8NMMMM,
461 # ~= ,~:,,~~, ,:, ,,,, , ,, +MN8 ,~OMM8Z, :+ZMNDOI =IMMN8= ,=NNMO ?NMMMD
462 # ::, ,,,:: ,:, ,, =MMO~ , ?NMMMI, ,INMMMDI, ~DMMN+, NMO ?DNO~
463 # ,~, ,,,::, ,, , ,, ,INMND+: :MMM+ ~ZDDMMMD?~:$MMMMMMMMMMM?
464 # ,, :~~:, ,:, ,, :ZMMMMNNMMMN, ,=ONMMMMNM$,,,,,, ,,
465 #Z, :, ,, ,~=~, ,, ,=?7$I= , :~~~, ,,,,
466 #MD= :: ,,,, , ,, ,,,,
467 #MNMI :: ,,, ,,,,
468 #7MMM? , ,~: :~:, ,:,
469 # OMMMO: ~NMD, :=: :+=: ,,,
470 # ~MMMN8: NMMM~ +?: ,:=++~ ,:,,
471 # $MMMMMNI,?MMMMZ ,DM7 ,=I= ~+~~:, ,:,,,
472 # +MMO~OMMMMMMMMD MMM7 , , =$~ ?OZ+ ,:,
473 # ~NMN: :+DNN7MMMNMMMDODMMN+ :+?, +77+~: :::,
474 # IMM+ ,NMMMMMMMMN?NM7 ~7+, , :+$I, , +8? :+~
475 # =NMD, NMMMMMM8~ ?NN, =ND? ,=??: ,8NMMMM= :++
476 # +DO, ~ZZ$ODI :8M~ :=I8NMNMMD+, ,:~~=: , +8MO$DMD+ ~===~: :~~:
477 # :$~ 7NMD$, ZMM8I? ,~=: , ZNN7,:MM8~,OMNMD8DMM= ++:
478 # =ZNOI: ?DND= ,?I~ ,,,: ,$ND+ ?NMNNNNN7+, 7MN, ,=+~
479 # :ZI: :, ,=7: ::,, $NN= 7NMMMM7: :DMMMN8NMMDDMN7 ,::,
480 # ~?= ,,, ,ZM= DMMD8 ?MMMMNN7, ,I+ :~~,
481 # =+, ,,, ,, =Z8: +$~
482 # :~ ,,~~,
483 # :, ::,
484 # ::, ,:::,
485 # ,, ,~
486 # ,, ,,,
487 #
488 #