forked from csev/py4e
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path13-web.html
More file actions
566 lines (521 loc) · 192 KB
/
13-web.html
File metadata and controls
566 lines (521 loc) · 192 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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
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
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta name="generator" content="pandoc" />
<title></title>
<style type="text/css">code{white-space: pre;}; pre.sourceCode {margin-left: 2em;}</style>
<style type="text/css">
div.sourceCode { overflow-x: auto; }
table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
margin: 0; padding: 0; vertical-align: baseline; border: none; }
table.sourceCode { width: 100%; line-height: 100%; }
td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
td.sourceCode { padding-left: 5px; }
code > span.kw { color: #007020; font-weight: bold; } /* Keyword */
code > span.dt { color: #902000; } /* DataType */
code > span.dv { color: #40a070; } /* DecVal */
code > span.bn { color: #40a070; } /* BaseN */
code > span.fl { color: #40a070; } /* Float */
code > span.ch { color: #4070a0; } /* Char */
code > span.st { color: #4070a0; } /* String */
code > span.co { color: #60a0b0; font-style: italic; } /* Comment */
code > span.ot { color: #007020; } /* Other */
code > span.al { color: #ff0000; font-weight: bold; } /* Alert */
code > span.fu { color: #06287e; } /* Function */
code > span.er { color: #ff0000; font-weight: bold; } /* Error */
code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
code > span.cn { color: #880000; } /* Constant */
code > span.sc { color: #4070a0; } /* SpecialChar */
code > span.vs { color: #4070a0; } /* VerbatimString */
code > span.ss { color: #bb6688; } /* SpecialString */
code > span.im { } /* Import */
code > span.va { color: #19177c; } /* Variable */
code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code > span.op { color: #666666; } /* Operator */
code > span.bu { } /* BuiltIn */
code > span.ex { } /* Extension */
code > span.pp { color: #bc7a00; } /* Preprocessor */
code > span.at { color: #7d9029; } /* Attribute */
code > span.do { color: #ba2121; font-style: italic; } /* Documentation */
code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
</style>
<link href="data:text/css;charset=utf-8,div%2Erow%20%7B%0Amax%2Dwidth%3A%2085%25%3B%0A%7D%0Apre%2EsourceCode%20%7B%0Amargin%2Dleft%3A%202em%3B%0A%7D%0Aiframe%20%7B%0Aopacity%3A%200%3B%0Atransition%3A%20opacity%20%2E333s%20ease%2Din%3B%0A%7D%0Aiframe%2Elazyloaded%20%7B%0Aopacity%3A%201%3B%0A%7D%20" rel="stylesheet" type="text/css" />
<link href="data:text/css;charset=utf-8,%0A%2A%2C%20%2A%3Aafter%2C%20%2A%3Abefore%20%7B%0A%2Dwebkit%2Dbox%2Dsizing%3A%20border%2Dbox%3B%0A%2Dmoz%2Dbox%2Dsizing%3A%20border%2Dbox%3B%0Abox%2Dsizing%3A%20border%2Dbox%3B%0A%7D%0Abody%20%7B%0Amargin%3A%200px%3B%0A%7D%0A%5Bclass%2A%3D%27col%2D%27%5D%20%7B%0Afloat%3A%20left%3B%0Apadding%2Dright%3A%2020px%3B%20%0A%7D%0A%2Egrid%20%7B%0Awidth%3A%20100%25%3B%0Amax%2Dwidth%3A%201140px%3B%0Amin%2Dwidth%3A%20755px%3B%0Amargin%3A%200%20auto%3B%0Aoverflow%3A%20hidden%3B%0A%7D%0A%2Egrid%3Aafter%20%7B%0Acontent%3A%20%22%22%3B%0Adisplay%3A%20table%3B%0Aclear%3A%20both%3B%0A%7D%0A%2Egrid%2Dpad%20%7B%0Apadding%2Dtop%3A%2020px%3B%0Apadding%2Dleft%3A%2020px%3B%20%0Apadding%2Dright%3A%200px%3B%20%0A%7D%0A%2Epush%2Dright%20%7B%0Afloat%3A%20right%3B%0A%7D%0A%0A%2Ecol%2D1%2D1%20%7B%0Awidth%3A%20100%25%3B%0A%7D%0A%2Ecol%2D2%2D3%2C%20%2Ecol%2D8%2D12%20%7B%0Awidth%3A%2066%2E66%25%3B%0A%7D%0A%2Ecol%2D1%2D2%2C%20%2Ecol%2D6%2D12%20%7B%0Awidth%3A%2050%25%3B%0A%7D%0A%2Ecol%2D1%2D3%2C%20%2Ecol%2D4%2D12%20%7B%0Awidth%3A%2033%2E33%25%3B%0A%7D%0A%2Ecol%2D1%2D4%2C%20%2Ecol%2D3%2D12%20%7B%0Awidth%3A%2025%25%3B%0A%7D%0A%2Ecol%2D1%2D5%20%7B%0Awidth%3A%2020%25%3B%0A%7D%0A%2Ecol%2D1%2D6%2C%20%2Ecol%2D2%2D12%20%7B%0Awidth%3A%2016%2E667%25%3B%0A%7D%0A%2Ecol%2D1%2D7%20%7B%0Awidth%3A%2014%2E28%25%3B%0A%7D%0A%2Ecol%2D1%2D8%20%7B%0Awidth%3A%2012%2E5%25%3B%0A%7D%0A%2Ecol%2D1%2D9%20%7B%0Awidth%3A%2011%2E1%25%3B%0A%7D%0A%2Ecol%2D1%2D10%20%7B%0Awidth%3A%2010%25%3B%0A%7D%0A%2Ecol%2D1%2D11%20%7B%0Awidth%3A%209%2E09%25%3B%0A%7D%0A%2Ecol%2D1%2D12%20%7B%0Awidth%3A%208%2E33%25%0A%7D%0A%0A%2Ecol%2D11%2D12%20%7B%0Awidth%3A%2091%2E66%25%0A%7D%0A%2Ecol%2D10%2D12%20%7B%0Awidth%3A%2083%2E333%25%3B%0A%7D%0A%2Ecol%2D9%2D12%20%7B%0Awidth%3A%2075%25%3B%0A%7D%0A%2Ecol%2D5%2D12%20%7B%0Awidth%3A%2041%2E66%25%3B%0A%7D%0A%2Ecol%2D7%2D12%20%7B%0Awidth%3A%2058%2E33%25%0A%7D%0A%0A%2Epush%2D2%2D3%2C%20%2Epush%2D8%2D12%20%7B%0Amargin%2Dleft%3A%2066%2E66%25%3B%0A%7D%0A%2Epush%2D1%2D2%2C%20%2Epush%2D6%2D12%20%7B%0Amargin%2Dleft%3A%2050%25%3B%0A%7D%0A%2Epush%2D1%2D3%2C%20%2Epush%2D4%2D12%20%7B%0Amargin%2Dleft%3A%2033%2E33%25%3B%0A%7D%0A%2Epush%2D1%2D4%2C%20%2Epush%2D3%2D12%20%7B%0Amargin%2Dleft%3A%2025%25%3B%0A%7D%0A%2Epush%2D1%2D5%20%7B%0Amargin%2Dleft%3A%2020%25%3B%0A%7D%0A%2Epush%2D1%2D6%2C%20%2Epush%2D2%2D12%20%7B%0Amargin%2Dleft%3A%2016%2E667%25%3B%0A%7D%0A%2Epush%2D1%2D7%20%7B%0Amargin%2Dleft%3A%2014%2E28%25%3B%0A%7D%0A%2Epush%2D1%2D8%20%7B%0Amargin%2Dleft%3A%2012%2E5%25%3B%0A%7D%0A%2Epush%2D1%2D9%20%7B%0Amargin%2Dleft%3A%2011%2E1%25%3B%0A%7D%0A%2Epush%2D1%2D10%20%7B%0Amargin%2Dleft%3A%2010%25%3B%0A%7D%0A%2Epush%2D1%2D11%20%7B%0Amargin%2Dleft%3A%209%2E09%25%3B%0A%7D%0A%2Epush%2D1%2D12%20%7B%0Amargin%2Dleft%3A%208%2E33%25%0A%7D%0A%40media%20handheld%2C%20only%20screen%20and%20%28max%2Dwidth%3A%20767px%29%20%7B%0A%2Egrid%20%7B%0Awidth%3A%20100%25%3B%0Amin%2Dwidth%3A%200%3B%0Amargin%2Dleft%3A%200px%3B%0Amargin%2Dright%3A%200px%3B%0Apadding%2Dleft%3A%2020px%3B%20%0Apadding%2Dright%3A%2010px%3B%20%0A%7D%0A%5Bclass%2A%3D%27col%2D%27%5D%20%7B%0Awidth%3A%20auto%3B%0Afloat%3A%20none%3B%0Amargin%2Dleft%3A%200px%3B%0Amargin%2Dright%3A%200px%3B%0Amargin%2Dtop%3A%2010px%3B%0Amargin%2Dbottom%3A%2010px%3B%0Apadding%2Dleft%3A%200px%3B%0Apadding%2Dright%3A%2010px%3B%20%0A%7D%0A%0A%5Bclass%2A%3D%27mobile%2Dcol%2D%27%5D%20%7B%0Afloat%3A%20left%3B%0Amargin%2Dleft%3A%200px%3B%0Amargin%2Dright%3A%200px%3B%0Amargin%2Dtop%3A%200px%3B%0Amargin%2Dbottom%3A%2010px%3B%0Apadding%2Dleft%3A%200px%3B%0Apadding%2Dright%3A%2010px%3B%20%0Apadding%2Dbottom%3A%200px%3B%0A%7D%0A%2Emobile%2Dcol%2D1%2D1%20%7B%0Awidth%3A%20100%25%3B%0A%7D%0A%2Emobile%2Dcol%2D2%2D3%2C%20%2Emobile%2Dcol%2D8%2D12%20%7B%0Awidth%3A%2066%2E66%25%3B%0A%7D%0A%2Emobile%2Dcol%2D1%2D2%2C%20%2Emobile%2Dcol%2D6%2D12%20%7B%0Awidth%3A%2050%25%3B%0A%7D%0A%2Emobile%2Dcol%2D1%2D3%2C%20%2Emobile%2Dcol%2D4%2D12%20%7B%0Awidth%3A%2033%2E33%25%3B%0A%7D%0A%2Emobile%2Dcol%2D1%2D4%2C%20%2Emobile%2Dcol%2D3%2D12%20%7B%0Awidth%3A%2025%25%3B%0A%7D%0A%2Emobile%2Dcol%2D1%2D5%20%7B%0Awidth%3A%2020%25%3B%0A%7D%0A%2Emobile%2Dcol%2D1%2D6%2C%20%2Emobile%2Dcol%2D2%2D12%20%7B%0Awidth%3A%2016%2E667%25%3B%0A%7D%0A%2Emobile%2Dcol%2D1%2D7%20%7B%0Awidth%3A%2014%2E28%25%3B%0A%7D%0A%2Emobile%2Dcol%2D1%2D8%20%7B%0Awidth%3A%2012%2E5%25%3B%0A%7D%0A%2Emobile%2Dcol%2D1%2D9%20%7B%0Awidth%3A%2011%2E1%25%3B%0A%7D%0A%2Emobile%2Dcol%2D1%2D10%20%7B%0Awidth%3A%2010%25%3B%0A%7D%0A%2Emobile%2Dcol%2D1%2D11%20%7B%0Awidth%3A%209%2E09%25%3B%0A%7D%0A%2Emobile%2Dcol%2D1%2D12%20%7B%0Awidth%3A%208%2E33%25%0A%7D%0A%0A%2Emobile%2Dcol%2D11%2D12%20%7B%0Awidth%3A%2091%2E66%25%0A%7D%0A%2Emobile%2Dcol%2D10%2D12%20%7B%0Awidth%3A%2083%2E333%25%3B%0A%7D%0A%2Emobile%2Dcol%2D9%2D12%20%7B%0Awidth%3A%2075%25%3B%0A%7D%0A%2Emobile%2Dcol%2D5%2D12%20%7B%0Awidth%3A%2041%2E66%25%3B%0A%7D%0A%2Emobile%2Dcol%2D7%2D12%20%7B%0Awidth%3A%2058%2E33%25%0A%7D%0A%2Ehide%2Don%2Dmobile%20%7B%0Adisplay%3A%20none%20%21important%3B%0Awidth%3A%200%3B%0Aheight%3A%200%3B%0A%7D%0A%7D%0A" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="grid grid-pad">
<div class="col-3-12">
<div class="content">
<ul>
<li><a href="#using-web-services">Using Web Services</a><ul>
<li><a href="#extensible-markup-language---xml">eXtensible Markup Language - XML</a></li>
<li><a href="#parsing-xml">Parsing XML</a></li>
<li><a href="#looping-through-nodes">Looping through nodes</a></li>
<li><a href="#javascript-object-notation---json">JavaScript Object Notation - JSON</a></li>
<li><a href="#parsing-json">Parsing JSON</a></li>
<li><a href="#application-programming-interfaces">Application Programming Interfaces</a></li>
<li><a href="#google-geocoding-web-service">Google geocoding web service</a></li>
<li><a href="#security-and-api-usage">Security and API usage</a></li>
<li><a href="#glossary">Glossary</a></li>
<li><a href="#exercises">Exercises</a></li>
</ul></li>
</ul>
</div>
</div>
<div class="col-9-12">
<div class="content">
<h1 id="using-web-services">Using Web Services</h1>
<p>Once it became easy to retrieve documents and parse documents over HTTP using programs, it did not take long to develop an approach where we</p>
<p>started producing documents that were specifically designed to be consumed by other programs (i.e., not HTML to be displayed in a browser).</p>
<p>There are two common formats that we use when exchanging data across the web. The "eXtensible Markup Language" or XML has been in use for a very long time and is best suited for exchanging document-style data. When programs just want to exchange dictionaries, lists, or other internal information with each other, they use JavaScript Object Notation or JSON (see <a href="http://www.json.org">www.json.org</a>). We will look at both formats.</p>
<h2 id="extensible-markup-language---xml">eXtensible Markup Language - XML</h2>
<p>XML looks very similar to HTML, but XML is more structured than HTML. Here is a sample of an XML document:</p>
<div class="sourceCode"><pre class="sourceCode xml"><code class="sourceCode xml"><span class="kw"><person></span>
<span class="kw"><name></span>Chuck<span class="kw"></name></span>
<span class="kw"><phone</span><span class="ot"> type=</span><span class="st">"intl"</span><span class="kw">></span>
+1 734 303 4456
<span class="kw"></phone></span>
<span class="kw"><email</span><span class="ot"> hide=</span><span class="st">"yes"</span><span class="kw">/></span>
<span class="kw"></person></span></code></pre></div>
<p>Often it is helpful to think of an XML document as a tree structure where there is a top tag <code>person</code> and other tags such as <code>phone</code> are drawn as <em>children</em> of their parent nodes.</p>
<div class="figure">
<img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bD0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIxNiAxNiAzNjQgMTU3IiB3aWR0aD0iMzY0cHQiIGhlaWdodD0iMTU3cHQiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyI+PG1ldGFkYXRhPiBQcm9kdWNlZCBieSBPbW5pR3JhZmZsZSA2LjQuMSA8ZGM6ZGF0ZT4yMDE2LTAxLTE2IDIwOjM0OjU1ICswMDAwPC9kYzpkYXRlPjwvbWV0YWRhdGE+PGRlZnM+PGZpbHRlciBpZD0iU2hhZG93IiBmaWx0ZXJVbml0cz0idXNlclNwYWNlT25Vc2UiPjxmZUdhdXNzaWFuQmx1ciBpbj0iU291cmNlQWxwaGEiIHJlc3VsdD0iYmx1ciIgc3RkRGV2aWF0aW9uPSIxLjMwOCIvPjxmZU9mZnNldCBpbj0iYmx1ciIgcmVzdWx0PSJvZmZzZXQiIGR4PSIwIiBkeT0iMiIvPjxmZUZsb29kIGZsb29kLWNvbG9yPSJibGFjayIgZmxvb2Qtb3BhY2l0eT0iLjUiIHJlc3VsdD0iZmxvb2QiLz48ZmVDb21wb3NpdGUgaW49ImZsb29kIiBpbjI9Im9mZnNldCIgb3BlcmF0b3I9ImluIi8+PC9maWx0ZXI+PGZvbnQtZmFjZSBmb250LWZhbWlseT0iSGVsdmV0aWNhIiBmb250LXNpemU9IjEwIiB1bml0cy1wZXItZW09IjEwMDAiIHVuZGVybGluZS1wb3NpdGlvbj0iLTc1LjY4MzU5NCIgdW5kZXJsaW5lLXRoaWNrbmVzcz0iNDkuMzE2NDA2IiBzbG9wZT0iMCIgeC1oZWlnaHQ9IjUyMi45NDkyMiIgY2FwLWhlaWdodD0iNzE3LjI4NTE2IiBhc2NlbnQ9Ijc3MC4wMTk1MyIgZGVzY2VudD0iLTIyOS45ODA0NyIgZm9udC13ZWlnaHQ9IjUwMCI+PGZvbnQtZmFjZS1zcmM+PGZvbnQtZmFjZS1uYW1lIG5hbWU9IkhlbHZldGljYSIvPjwvZm9udC1mYWNlLXNyYz48L2ZvbnQtZmFjZT48ZmlsdGVyIGlkPSJTaGFkb3dfMiIgZmlsdGVyVW5pdHM9InVzZXJTcGFjZU9uVXNlIj48ZmVHYXVzc2lhbkJsdXIgaW49IlNvdXJjZUFscGhhIiByZXN1bHQ9ImJsdXIiIHN0ZERldmlhdGlvbj0iMS4zMDgiLz48ZmVPZmZzZXQgaW49ImJsdXIiIHJlc3VsdD0ib2Zmc2V0IiBkeD0iMCIgZHk9IjIiLz48ZmVGbG9vZCBmbG9vZC1jb2xvcj0iYmxhY2siIGZsb29kLW9wYWNpdHk9Ii41IiByZXN1bHQ9ImZsb29kIi8+PGZlQ29tcG9zaXRlIGluPSJmbG9vZCIgaW4yPSJvZmZzZXQiIG9wZXJhdG9yPSJpbiIgcmVzdWx0PSJjb2xvciIvPjxmZU1lcmdlPjxmZU1lcmdlTm9kZSBpbj0iY29sb3IiLz48ZmVNZXJnZU5vZGUgaW49IlNvdXJjZUdyYXBoaWMiLz48L2ZlTWVyZ2U+PC9maWx0ZXI+PGZvbnQtZmFjZSBmb250LWZhbWlseT0iSGVsdmV0aWNhIiBmb250LXNpemU9IjEyIiB1bml0cy1wZXItZW09IjEwMDAiIHVuZGVybGluZS1wb3NpdGlvbj0iLTc1LjY4MzU5NCIgdW5kZXJsaW5lLXRoaWNrbmVzcz0iNDkuMzE2NDA2IiBzbG9wZT0iMCIgeC1oZWlnaHQ9IjUyMi45NDkyMiIgY2FwLWhlaWdodD0iNzE3LjI4NTE2IiBhc2NlbnQ9Ijc3MC4wMTk1MyIgZGVzY2VudD0iLTIyOS45ODA0NyIgZm9udC13ZWlnaHQ9IjUwMCI+PGZvbnQtZmFjZS1zcmM+PGZvbnQtZmFjZS1uYW1lIG5hbWU9IkhlbHZldGljYSIvPjwvZm9udC1mYWNlLXNyYz48L2ZvbnQtZmFjZT48bWFya2VyIG9yaWVudD0iYXV0byIgb3ZlcmZsb3c9InZpc2libGUiIG1hcmtlclVuaXRzPSJzdHJva2VXaWR0aCIgaWQ9IkZpbGxlZEFycm93X01hcmtlciIgdmlld0JveD0iLTEgLTMgNyA2IiBtYXJrZXJXaWR0aD0iNyIgbWFya2VySGVpZ2h0PSI2IiBjb2xvcj0iYmxhY2siPjxnPjxwYXRoIGQ9Ik0gNC44IDAgTCAwIC0xLjggTCAwIDEuOCBaIiBmaWxsPSJjdXJyZW50Q29sb3IiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2Utd2lkdGg9IjEiLz48L2c+PC9tYXJrZXI+PC9kZWZzPjxnIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLW9wYWNpdHk9IjEiIHN0cm9rZS1kYXNoYXJyYXk9Im5vbmUiIGZpbGw9Im5vbmUiIGZpbGwtb3BhY2l0eT0iMSI+PHRpdGxlPkNhbnZhcyAxPC90aXRsZT48cmVjdCBmaWxsPSJ3aGl0ZSIgd2lkdGg9IjQ1MCIgaGVpZ2h0PSIyNzAiLz48Zz48dGl0bGU+TGF5ZXIgMTwvdGl0bGU+PGc+PHhsOnVzZSB4bDpocmVmPSIjaWQ0X0dyYXBoaWMiIGZpbHRlcj0idXJsKCNTaGFkb3cpIi8+PHhsOnVzZSB4bDpocmVmPSIjaWQ1X0dyYXBoaWMiIGZpbHRlcj0idXJsKCNTaGFkb3cpIi8+PHhsOnVzZSB4bDpocmVmPSIjaWQ0N19HcmFwaGljIiBmaWx0ZXI9InVybCgjU2hhZG93KSIvPjx4bDp1c2UgeGw6aHJlZj0iI2lkNTJfR3JhcGhpYyIgZmlsdGVyPSJ1cmwoI1NoYWRvdykiLz48L2c+PGcgaWQ9ImlkNF9HcmFwaGljIj48ZWxsaXBzZSBjeD0iNTQiIGN5PSI5NC41IiByeD0iMzYuMDAwMDU4IiByeT0iMTMuNTAwMDIxNiIgZmlsbD0id2hpdGUiLz48ZWxsaXBzZSBjeD0iNTQiIGN5PSI5NC41IiByeD0iMzYuMDAwMDU4IiByeT0iMTMuNTAwMDIxNiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjEiLz48dGV4dCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzMC4yIDg4LjUpIiBmaWxsPSJibGFjayI+PHRzcGFuIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EiIGZvbnQtc2l6ZT0iMTAiIGZvbnQtd2VpZ2h0PSI1MDAiIHg9IjExLjI5MjY3NiIgeT0iMTAiIHRleHRMZW5ndGg9IjI1LjAxNDY0OCI+bmFtZTwvdHNwYW4+PC90ZXh0PjwvZz48ZyBpZD0iaWQ1X0dyYXBoaWMiPjxlbGxpcHNlIGN4PSIxOTgiIGN5PSIzMS41IiByeD0iMzYuMDAwMDU4IiByeT0iMTMuNTAwMDIxNiIgZmlsbD0id2hpdGUiLz48ZWxsaXBzZSBjeD0iMTk4IiBjeT0iMzEuNSIgcng9IjM2LjAwMDA1OCIgcnk9IjEzLjUwMDAyMTYiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIxIi8+PHRleHQgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTc0LjIgMjUuNSkiIGZpbGw9ImJsYWNrIj48dHNwYW4gZm9udC1mYW1pbHk9IkhlbHZldGljYSIgZm9udC1zaXplPSIxMCIgZm9udC13ZWlnaHQ9IjUwMCIgeD0iOC41MTE5MTQiIHk9IjEwIiB0ZXh0TGVuZ3RoPSIzMC41NzYxNzIiPnBlcnNvbjwvdHNwYW4+PC90ZXh0PjwvZz48ZyBmaWx0ZXI9InVybCgjU2hhZG93XzIpIj48cGF0aCBkPSJNIDI4IDEzNSBMIDgwIDEzNSBDIDg1LjUyMjg1IDEzNSA5MCAxMzkuNDc3MTUgOTAgMTQ1IEwgOTAgMTYxIEMgOTAgMTY2LjUyMjg1IDg1LjUyMjg1IDE3MSA4MCAxNzEgTCAyOCAxNzEgQyAyMi40NzcxNTMgMTcxIDE4IDE2Ni41MjI4NSAxOCAxNjEgTCAxOCAxNDUgQyAxOCAxMzkuNDc3MTUgMjIuNDc3MTUzIDEzNSAyOCAxMzUgWiIgZmlsbD0id2hpdGUiLz48cGF0aCBkPSJNIDI4IDEzNSBMIDgwIDEzNSBDIDg1LjUyMjg1IDEzNSA5MCAxMzkuNDc3MTUgOTAgMTQ1IEwgOTAgMTYxIEMgOTAgMTY2LjUyMjg1IDg1LjUyMjg1IDE3MSA4MCAxNzEgTCAyOCAxNzEgQyAyMi40NzcxNTMgMTcxIDE4IDE2Ni41MjI4NSAxOCAxNjEgTCAxOCAxNDUgQyAxOCAxMzkuNDc3MTUgMjIuNDc3MTUzIDEzNSAyOCAxMzUgWiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjEiLz48dGV4dCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyMyAxNDYpIiBmaWxsPSJibGFjayI+PHRzcGFuIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EiIGZvbnQtc2l6ZT0iMTIiIGZvbnQtd2VpZ2h0PSI1MDAiIGZpbGw9ImJsYWNrIiB4PSIxMy45OTMxNjQiIHk9IjExIiB0ZXh0TGVuZ3RoPSIzNC4wMTM2NzIiPkNodWNrPC90c3Bhbj48L3RleHQ+PC9nPjxsaW5lIHgxPSIxODkiIHkxPSI0NSIgeDI9IjY2LjQ2NDQzIiB5Mj0iNzcuNjc2MTQ2IiBtYXJrZXItZW5kPSJ1cmwoI0ZpbGxlZEFycm93X01hcmtlcikiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIi8+PGxpbmUgeDE9IjU0IiB5MT0iMTA4LjAwMDAwNyIgeDI9IjU0IiB5Mj0iMTIyLjEiIG1hcmtlci1lbmQ9InVybCgjRmlsbGVkQXJyb3dfTWFya2VyKSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjIiLz48ZyBpZD0iaWQ0N19HcmFwaGljIj48ZWxsaXBzZSBjeD0iMTQ0IiBjeT0iOTQuNSIgcng9IjM2LjAwMDA1OCIgcnk9IjEzLjUwMDAyMTYiIGZpbGw9IndoaXRlIi8+PGVsbGlwc2UgY3g9IjE0NCIgY3k9Ijk0LjUiIHJ4PSIzNi4wMDAwNTgiIHJ5PSIxMy41MDAwMjE2IiBzdHJva2U9ImJsYWNrIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMSIvPjx0ZXh0IHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyMC4yIDg4LjUpIiBmaWxsPSJibGFjayI+PHRzcGFuIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EiIGZvbnQtc2l6ZT0iMTAiIGZvbnQtd2VpZ2h0PSI1MDAiIHg9IjkuODk2MTkxNCIgeT0iMTAiIHRleHRMZW5ndGg9IjI3LjgwNzYxNyI+cGhvbmU8L3RzcGFuPjwvdGV4dD48L2c+PGcgZmlsdGVyPSJ1cmwoI1NoYWRvd18yKSI+PHBhdGggZD0iTSAxMTggMTM1IEwgMTcwIDEzNSBDIDE3NS41MjI4NSAxMzUgMTgwIDEzOS40NzcxNSAxODAgMTQ1IEwgMTgwIDE2MSBDIDE4MCAxNjYuNTIyODUgMTc1LjUyMjg1IDE3MSAxNzAgMTcxIEwgMTE4IDE3MSBDIDExMi40NzcxNSAxNzEgMTA4IDE2Ni41MjI4NSAxMDggMTYxIEwgMTA4IDE0NSBDIDEwOCAxMzkuNDc3MTUgMTEyLjQ3NzE1IDEzNSAxMTggMTM1IFoiIGZpbGw9IndoaXRlIi8+PHBhdGggZD0iTSAxMTggMTM1IEwgMTcwIDEzNSBDIDE3NS41MjI4NSAxMzUgMTgwIDEzOS40NzcxNSAxODAgMTQ1IEwgMTgwIDE2MSBDIDE4MCAxNjYuNTIyODUgMTc1LjUyMjg1IDE3MSAxNzAgMTcxIEwgMTE4IDE3MSBDIDExMi40NzcxNSAxNzEgMTA4IDE2Ni41MjI4NSAxMDggMTYxIEwgMTA4IDE0NSBDIDEwOCAxMzkuNDc3MTUgMTEyLjQ3NzE1IDEzNSAxMTggMTM1IFoiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIxIi8+PHRleHQgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTEzIDEzOSkiIGZpbGw9ImJsYWNrIj48dHNwYW4gZm9udC1mYW1pbHk9IkhlbHZldGljYSIgZm9udC1zaXplPSIxMiIgZm9udC13ZWlnaHQ9IjUwMCIgeD0iMTIuNDgxNDQ1IiB5PSIxMSIgdGV4dExlbmd0aD0iNDAuMzcxMDk0Ij4rMSA3MzQgPC90c3Bhbj48dHNwYW4gZm9udC1mYW1pbHk9IkhlbHZldGljYSIgZm9udC1zaXplPSIxMiIgZm9udC13ZWlnaHQ9IjUwMCIgeD0iNS45NzQ2MDk0IiB5PSIyNSIgdGV4dExlbmd0aD0iNTAuMDUwNzgiPjMwMyA0NDU2PC90c3Bhbj48L3RleHQ+PC9nPjxsaW5lIHgxPSIxOTgiIHkxPSI0NS4wMDAwMDciIHgyPSIxNTQuNzMzNDUiIHkyPSI3My44NDQzNjMiIG1hcmtlci1lbmQ9InVybCgjRmlsbGVkQXJyb3dfTWFya2VyKSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjIiLz48bGluZSB4MT0iMTQ0IiB5MT0iMTA4LjAwMDAwNyIgeDI9IjE0NCIgeTI9IjEyMi4xIiBtYXJrZXItZW5kPSJ1cmwoI0ZpbGxlZEFycm93X01hcmtlcikiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIi8+PGcgZmlsdGVyPSJ1cmwoI1NoYWRvd18yKSI+PHBhdGggZD0iTSAxNzEgOTkgTCAyMjUgOTkgQyAyMjkuOTcwNTYgOTkgMjM0IDEwMy4wMjk0NCAyMzQgMTA4IEwgMjM0IDEwOCBDIDIzNCAxMTIuOTcwNTYgMjI5Ljk3MDU2IDExNyAyMjUgMTE3IEwgMTcxIDExNyBDIDE2Ni4wMjk0NCAxMTcgMTYyIDExMi45NzA1NiAxNjIgMTA4IEwgMTYyIDEwOCBDIDE2MiAxMDMuMDI5NDQgMTY2LjAyOTQ0IDk5IDE3MSA5OSBaIiBmaWxsPSJ3aGl0ZSIvPjxwYXRoIGQ9Ik0gMTcxIDk5IEwgMjI1IDk5IEMgMjI5Ljk3MDU2IDk5IDIzNCAxMDMuMDI5NDQgMjM0IDEwOCBMIDIzNCAxMDggQyAyMzQgMTEyLjk3MDU2IDIyOS45NzA1NiAxMTcgMjI1IDExNyBMIDE3MSAxMTcgQyAxNjYuMDI5NDQgMTE3IDE2MiAxMTIuOTcwNTYgMTYyIDEwOCBMIDE2MiAxMDggQyAxNjIgMTAzLjAyOTQ0IDE2Ni4wMjk0NCA5OSAxNzEgOTkgWiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjEiLz48dGV4dCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNjcgMTAxKSIgZmlsbD0iYmxhY2siPjx0c3BhbiBmb250LWZhbWlseT0iSGVsdmV0aWNhIiBmb250LXNpemU9IjEyIiBmb250LXdlaWdodD0iNTAwIiB4PSI4LjQ4NTM1MTYiIHk9IjExIiB0ZXh0TGVuZ3RoPSI0NS4wMjkyOTciPnR5cGU9aW50bDwvdHNwYW4+PC90ZXh0PjwvZz48ZyBpZD0iaWQ1Ml9HcmFwaGljIj48ZWxsaXBzZSBjeD0iMjg4IiBjeT0iOTQuNSIgcng9IjM2LjAwMDA1OCIgcnk9IjEzLjUwMDAyMTYiIGZpbGw9IndoaXRlIi8+PGVsbGlwc2UgY3g9IjI4OCIgY3k9Ijk0LjUiIHJ4PSIzNi4wMDAwNTgiIHJ5PSIxMy41MDAwMjE2IiBzdHJva2U9ImJsYWNrIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMSIvPjx0ZXh0IHRyYW5zZm9ybT0idHJhbnNsYXRlKDI2NC4yIDg4LjUpIiBmaWxsPSJibGFjayI+PHRzcGFuIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EiIGZvbnQtc2l6ZT0iMTAiIGZvbnQtd2VpZ2h0PSI1MDAiIHg9IjkuODk2MTkxNCIgeT0iMTAiIHRleHRMZW5ndGg9IjI3LjgwNzYxNyI+cGhvbmU8L3RzcGFuPjwvdGV4dD48L2c+PGxpbmUgeDE9IjE5OCIgeTE9IjQ1IiB4Mj0iMjc2LjAyMjY1IiB5Mj0iNzYuMjA5MDU0IiBtYXJrZXItZW5kPSJ1cmwoI0ZpbGxlZEFycm93X01hcmtlcikiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIyIi8+PGcgZmlsdGVyPSJ1cmwoI1NoYWRvd18yKSI+PHBhdGggZD0iTSAzMTUgOTkgTCAzNjkgOTkgQyAzNzMuOTcwNTYgOTkgMzc4IDEwMy4wMjk0NCAzNzggMTA4IEwgMzc4IDEwOCBDIDM3OCAxMTIuOTcwNTYgMzczLjk3MDU2IDExNyAzNjkgMTE3IEwgMzE1IDExNyBDIDMxMC4wMjk0NCAxMTcgMzA2IDExMi45NzA1NiAzMDYgMTA4IEwgMzA2IDEwOCBDIDMwNiAxMDMuMDI5NDQgMzEwLjAyOTQ0IDk5IDMxNSA5OSBaIiBmaWxsPSJ3aGl0ZSIvPjxwYXRoIGQ9Ik0gMzE1IDk5IEwgMzY5IDk5IEMgMzczLjk3MDU2IDk5IDM3OCAxMDMuMDI5NDQgMzc4IDEwOCBMIDM3OCAxMDggQyAzNzggMTEyLjk3MDU2IDM3My45NzA1NiAxMTcgMzY5IDExNyBMIDMxNSAxMTcgQyAzMTAuMDI5NDQgMTE3IDMwNiAxMTIuOTcwNTYgMzA2IDEwOCBMIDMwNiAxMDggQyAzMDYgMTAzLjAyOTQ0IDMxMC4wMjk0NCA5OSAzMTUgOTkgWiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjEiLz48dGV4dCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzMTEgMTAxKSIgZmlsbD0iYmxhY2siPjx0c3BhbiBmb250LWZhbWlseT0iSGVsdmV0aWNhIiBmb250LXNpemU9IjEyIiBmb250LXdlaWdodD0iNTAwIiB4PSI2LjgxNTQyOTciIHk9IjExIiB0ZXh0TGVuZ3RoPSI0OC4zNjkxNCI+aGlkZT15ZXM8L3RzcGFuPjwvdGV4dD48L2c+PC9nPjwvZz48L3N2Zz4K" alt="A Tree Representation of XML" />
<p class="caption">A Tree Representation of XML</p>
</div>
<h2 id="parsing-xml">Parsing XML</h2>
<p> </p>
<p>Here is a simple application that parses some XML and extracts some data elements from the XML:</p>
<script type="text/javascript">(function(d,l,s,i,c){function n(e){e=e.nextSibling;return (!e||e.nodeType!=3)?e:n(e);};function r(f){/in/.test(d.readyState) ? setTimeout(function(){r(f);},9):f()};l=d.getElementsByTagName('script');s=l[l.length-1];r(function(){i=n(s),c=n(i);i.setAttribute('data-src','https://trinket.io/tools/1.0/jekyll/embed/python3#code='+encodeURIComponent(c.nodeValue.replace(/^\s+|\s+$/g,'')));});})(document)</script>
<iframe width="100%" height="400" frameborder="0" marginwidth="0" marginheight="0" class="lazyload" allowfullscreen>
</iframe>
<!--
import xml.etree.ElementTree as ET
data = '''
<person>
<name>Chuck</name>
<phone type="intl">
+1 734 303 4456
</phone>
<email hide="yes"/>
</person>'''
tree = ET.fromstring(data)
print('Name:',tree.find('name').text)
print('Attr:',tree.find('email').get('hide'))
# Code: http://www.pythonlearn.com/code3/xml1.py
-->
<p>Calling <code>fromstring</code> converts the string representation of the XML into a "tree" of XML nodes. When the XML is in a tree, we have a series of methods we can call to extract portions of data from the XML.</p>
<p>The <code>find</code> function searches through the XML tree and retrieves a <em>node</em> that matches the specified tag. Each node can have some text, some attributes (like hide), and some "child" nodes. Each node can be the top of a tree of nodes.</p>
<pre><code>Name: Chuck
Attr: yes</code></pre>
<p>Using an XML parser such as <code>ElementTree</code> has the advantage that while the XML in this example is quite simple, it turns out there are many rules regarding valid XML and using <code>ElementTree</code> allows us to extract data from XML without worrying about the rules of XML syntax.</p>
<h2 id="looping-through-nodes">Looping through nodes</h2>
<p> </p>
<p>Often the XML has multiple nodes and we need to write a loop to process all of the nodes. In the following program, we loop through all of the <code>user</code> nodes:</p>
<script type="text/javascript">(function(d,l,s,i,c){function n(e){e=e.nextSibling;return (!e||e.nodeType!=3)?e:n(e);};function r(f){/in/.test(d.readyState) ? setTimeout(function(){r(f);},9):f()};l=d.getElementsByTagName('script');s=l[l.length-1];r(function(){i=n(s),c=n(i);i.setAttribute('data-src','https://trinket.io/tools/1.0/jekyll/embed/python3#code='+encodeURIComponent(c.nodeValue.replace(/^\s+|\s+$/g,'')));});})(document)</script>
<iframe width="100%" height="400" frameborder="0" marginwidth="0" marginheight="0" class="lazyload" allowfullscreen>
</iframe>
<!--
import xml.etree.ElementTree as ET
input = '''
<stuff>
<users>
<user x="2">
<id>001</id>
<name>Chuck</name>
</user>
<user x="7">
<id>009</id>
<name>Brent</name>
</user>
</users>
</stuff>'''
stuff = ET.fromstring(input)
lst = stuff.findall('users/user')
print('User count:', len(lst))
for item in lst:
print('Name', item.find('name').text)
print('Id', item.find('id').text)
print('Attribute', item.get("x"))
# Code: http://www.pythonlearn.com/code3/xml2.py
-->
<p>The <code>findall</code> method retrieves a Python list of subtrees that represent the <code>user</code> structures in the XML tree. Then we can write a <code>for</code> loop that looks at each of the user nodes, and prints the <code>name</code> and <code>id</code> text elements as well as the <code>x</code> attribute from the <code>user</code> node.</p>
<pre><code>User count: 2
Name Chuck
Id 001
Attribute 2
Name Brent
Id 009
Attribute 7</code></pre>
<h2 id="javascript-object-notation---json">JavaScript Object Notation - JSON</h2>
<p> </p>
<p>The JSON format was inspired by the object and array format used in the JavaScript language. But since Python was invented before JavaScript, Python's syntax for dictionaries and lists influenced the syntax of JSON. So the format of JSON is nearly identical to a combination of Python lists and dictionaries.</p>
<p>Here is a JSON encoding that is roughly equivalent to the simple XML from above:</p>
<div class="sourceCode"><pre class="sourceCode json"><code class="sourceCode json"><span class="fu">{</span>
<span class="dt">"name"</span> <span class="fu">:</span> <span class="st">"Chuck"</span><span class="fu">,</span>
<span class="dt">"phone"</span> <span class="fu">:</span> <span class="fu">{</span>
<span class="dt">"type"</span> <span class="fu">:</span> <span class="st">"intl"</span><span class="fu">,</span>
<span class="dt">"number"</span> <span class="fu">:</span> <span class="st">"+1 734 303 4456"</span>
<span class="fu">},</span>
<span class="dt">"email"</span> <span class="fu">:</span> <span class="fu">{</span>
<span class="dt">"hide"</span> <span class="fu">:</span> <span class="st">"yes"</span>
<span class="fu">}</span>
<span class="fu">}</span></code></pre></div>
<p>You will notice some differences. First, in XML, we can add attributes like "intl" to the "phone" tag. In JSON, we simply have key-value pairs. Also the XML "person" tag is gone, replaced by a set of outer curly braces.</p>
<p>In general, JSON structures are simpler than XML because JSON has fewer capabilities than XML. But JSON has the advantage that it maps <em>directly</em> to some combination of dictionaries and lists. And since nearly all programming languages have something equivalent to Python's dictionaries and lists, JSON is a very natural format to have two cooperating programs exchange data.</p>
<p>JSON is quickly becoming the format of choice for nearly all data exchange between applications because of its relative simplicity compared to XML.</p>
<h2 id="parsing-json">Parsing JSON</h2>
<p>We construct our JSON by nesting dictionaries (objects) and lists as needed. In this example, we represent a list of users where each user is a set of key-value pairs (i.e., a dictionary). So we have a list of dictionaries.</p>
<p>In the following program, we use the built-in <em>json</em> library to parse the JSON and read through the data. Compare this closely to the equivalent XML data and code above. The JSON has less detail, so we must know in advance that we are getting a list and that the list is of users and each user is a set of key-value pairs. The JSON is more succinct (an advantage) but also is less self-describing (a disadvantage).</p>
<script type="text/javascript">(function(d,l,s,i,c){function n(e){e=e.nextSibling;return (!e||e.nodeType!=3)?e:n(e);};function r(f){/in/.test(d.readyState) ? setTimeout(function(){r(f);},9):f()};l=d.getElementsByTagName('script');s=l[l.length-1];r(function(){i=n(s),c=n(i);i.setAttribute('data-src','https://trinket.io/tools/1.0/jekyll/embed/python3#code='+encodeURIComponent(c.nodeValue.replace(/^\s+|\s+$/g,'')));});})(document)</script>
<iframe width="100%" height="400" frameborder="0" marginwidth="0" marginheight="0" class="lazyload" allowfullscreen>
</iframe>
<!--
import json
input = '''
[
{ "id" : "001",
"x" : "2",
"name" : "Chuck"
} ,
{ "id" : "009",
"x" : "7",
"name" : "Chuck"
}
]'''
info = json.loads(input)
print('User count:', len(info))
for item in info:
print('Name', item['name'])
print('Id', item['id'])
print('Attribute', item['x'])
# Code: http://www.pythonlearn.com/code3/json2.py
-->
<p>If you compare the code to extract data from the parsed JSON and XML you will see that what we get from <em>json.loads()</em> is a Python list which we traverse with a <code>for</code> loop, and each item within that list is a Python dictionary. Once the JSON has been parsed, we can use the Python index operator to extract the various bits of data for each user. We don't have to use the JSON library to dig through the parsed JSON, since the returned data is simply native Python structures.</p>
<p>The output of this program is exactly the same as the XML version above.</p>
<pre><code>User count: 2
Name Chuck
Id 001
Attribute 2
Name Brent
Id 009
Attribute 7</code></pre>
<p>In general, there is an industry trend away from XML and towards JSON for web services. Because the JSON is simpler and more directly maps to native data structures we already have in programming languages, the parsing and data extraction code is usually simpler and more direct when using JSON. But XML is more self-descriptive than JSON and so there are some applications where XML retains an advantage. For example, most word processors store documents internally using XML rather than JSON.</p>
<h2 id="application-programming-interfaces">Application Programming Interfaces</h2>
<p>We now have the ability to exchange data between applications using HyperText Transport Protocol (HTTP) and a way to represent complex data that we are sending back and forth between these applications using eXtensible Markup Language (XML) or JavaScript Object Notation (JSON).</p>
<p>The next step is to begin to define and document "contracts" between applications using these techniques. The general name for these application-to-application contracts is <em>Application Program Interfaces</em> or APIs. When we use an API, generally one program makes a set of <em>services</em> available for use by other applications and publishes the APIs (i.e., the "rules") that must be followed to access the services provided by the program.</p>
<p>When we begin to build our programs where the functionality of our program includes access to services provided by other programs, we call the approach a <em>Service-Oriented Architecture</em> or SOA. A SOA approach is one where our overall application makes use of the services of other applications. A non-SOA approach is where the application is a single standalone application which contains all of the code necessary to implement the application.</p>
<p>We see many examples of SOA when we use the web. We can go to a single web site and book air travel, hotels, and automobiles all from a single site. The data for hotels is not stored on the airline computers. Instead, the airline computers contact the services on the hotel computers and retrieve the hotel data and present it to the user. When the user agrees to make a hotel reservation using the airline site, the airline site uses another web service on the hotel systems to actually make the reservation. And when it comes time to charge your credit card for the whole transaction, still other computers become involved in the process.</p>
<div class="figure">
<img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bD0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIxMyAxMCA0NzUgMzI4IiB3aWR0aD0iNDc1cHQiIGhlaWdodD0iMzI4cHQiIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyI+PG1ldGFkYXRhPiBQcm9kdWNlZCBieSBPbW5pR3JhZmZsZSA2LjQuMSA8ZGM6ZGF0ZT4yMDE2LTAxLTE2IDIwOjQ3OjUwICswMDAwPC9kYzpkYXRlPjwvbWV0YWRhdGE+PGRlZnM+PGZvbnQtZmFjZSBmb250LWZhbWlseT0iSGVsdmV0aWNhIE5ldWUiIGZvbnQtc2l6ZT0iMTYiIHBhbm9zZS0xPSIyIDAgNSAzIDAgMCAwIDIgMCA0IiB1bml0cy1wZXItZW09IjEwMDAiIHVuZGVybGluZS1wb3NpdGlvbj0iLTEwMCIgdW5kZXJsaW5lLXRoaWNrbmVzcz0iNTAiIHNsb3BlPSIwIiB4LWhlaWdodD0iNTE3IiBjYXAtaGVpZ2h0PSI3MTQiIGFzY2VudD0iOTUxLjk5NTg1IiBkZXNjZW50PSItMjEyLjk5NzQ0IiBmb250LXdlaWdodD0iNTAwIj48Zm9udC1mYWNlLXNyYz48Zm9udC1mYWNlLW5hbWUgbmFtZT0iSGVsdmV0aWNhTmV1ZSIvPjwvZm9udC1mYWNlLXNyYz48L2ZvbnQtZmFjZT48L2RlZnM+PGcgc3Ryb2tlPSJub25lIiBzdHJva2Utb3BhY2l0eT0iMSIgc3Ryb2tlLWRhc2hhcnJheT0ibm9uZSIgZmlsbD0ibm9uZSIgZmlsbC1vcGFjaXR5PSIxIj48dGl0bGU+Q2FudmFzIDE8L3RpdGxlPjxyZWN0IGZpbGw9IndoaXRlIiB3aWR0aD0iNjU3IiBoZWlnaHQ9IjQ4MSIvPjxnPjx0aXRsZT5MYXllciAxPC90aXRsZT48cGF0aCBkPSJNIDMwLjY0NDQgNjYuODE3MjkgQyAxMS4yNSA2MS41IDE4Ljk4NCAxNi43MzcxNSA0OS45MjI0IDI0LjM3NSBDIDUyLjc5MjggOS40ODYzOSA4OC43NyAxMS45MDI5OCA4OC41MzQ4IDI0LjM3NSBDIDExMS4wOTM2IDguNDIzMTMgMTM5LjkyMjQgNDAuMjMwODQgMTIwLjU4NTYgNTYuMTgyNzEgQyAxNDMuNzg4OCA2My45MTY1OSAxMjAuMjkyOCAxMDUuNTg1NjkgMTAxLjI1IDk4LjYyNSBDIDk5LjcyNiAxMTAuMjI2ODEgNjUuNjgzMiAxMTQuMjg2OCA2Mi42OTUyIDk4LjYyNSBDIDQzLjQxODQgMTE1LjM1MTA1IDMuMjIzMiA4OS42MzM4MiAzMC42NDQ0IDY2LjgxNzI5IFoiIGZpbGw9IiMzYThlZWQiLz48cGF0aCBkPSJNIDMwLjY0NDQgNjYuODE3MjkgQyAxMS4yNSA2MS41IDE4Ljk4NCAxNi43MzcxNSA0OS45MjI0IDI0LjM3NSBDIDUyLjc5MjggOS40ODYzOSA4OC43NyAxMS45MDI5OCA4OC41MzQ4IDI0LjM3NSBDIDExMS4wOTM2IDguNDIzMTMgMTM5LjkyMjQgNDAuMjMwODQgMTIwLjU4NTYgNTYuMTgyNzEgQyAxNDMuNzg4OCA2My45MTY1OSAxMjAuMjkyOCAxMDUuNTg1NjkgMTAxLjI1IDk4LjYyNSBDIDk5LjcyNiAxMTAuMjI2ODEgNjUuNjgzMiAxMTQuMjg2OCA2Mi42OTUyIDk4LjYyNSBDIDQzLjQxODQgMTE1LjM1MTA1IDMuMjIzMiA4OS42MzM4MiAzMC42NDQ0IDY2LjgxNzI5IFoiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIxIi8+PHRleHQgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMzggMzQuNSkiIGZpbGw9ImJsYWNrIj48dHNwYW4gZm9udC1mYW1pbHk9IkhlbHZldGljYSBOZXVlIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNTAwIiBmaWxsPSJibGFjayIgeD0iMjAuMjU2IiB5PSIxNSIgdGV4dExlbmd0aD0iMzcuOTM2Ij5BdXRvIDwvdHNwYW4+PHRzcGFuIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EgTmV1ZSIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjUwMCIgZmlsbD0iYmxhY2siIHg9IjE0LjE4NCIgeT0iMzMiIHRleHRMZW5ndGg9IjUwLjA4Ij5SZW50YWwgPC90c3Bhbj48dHNwYW4gZm9udC1mYW1pbHk9IkhlbHZldGljYSBOZXVlIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNTAwIiBmaWxsPSJibGFjayIgeD0iMTAuNDg4IiB5PSI1MSIgdGV4dExlbmd0aD0iNTMuMDI0Ij5TZXJ2aWNlPC90c3Bhbj48L3RleHQ+PHBhdGggZD0iTSAxODEuOTQ2NjEgNjYuODE3MjkgQyAxNTcuMjE4NzUgNjEuNSAxNjcuMDc5NiAxNi43MzcxNSAyMDYuNTI2MDYgMjQuMzc1IEMgMjEwLjE4NTgyIDkuNDg2MzkgMjU2LjA1Njc1IDExLjkwMjk4IDI1NS43NTY4NyAyNC4zNzUgQyAyODQuNTE5MzQgOC40MjMxMyAzMjEuMjc2MDYgNDAuMjMwODQgMjk2LjYyMTY0IDU2LjE4MjcxIEMgMzI2LjIwNTcyIDYzLjkxNjU5IDI5Ni4yNDgzMiAxMDUuNTg1NjkgMjcxLjk2ODc1IDk4LjYyNSBDIDI3MC4wMjU2NSAxMTAuMjI2ODEgMjI2LjYyMTA4IDExNC4yODY4IDIyMi44MTEzOCA5OC42MjUgQyAxOTguMjMzNDYgMTE1LjM1MTA1IDE0Ni45ODQ1OCA4OS42MzM4MiAxODEuOTQ2NjEgNjYuODE3MjkgWiIgZmlsbD0iI2RlYWQyNiIvPjxwYXRoIGQ9Ik0gMTgxLjk0NjYxIDY2LjgxNzI5IEMgMTU3LjIxODc1IDYxLjUgMTY3LjA3OTYgMTYuNzM3MTUgMjA2LjUyNjA2IDI0LjM3NSBDIDIxMC4xODU4MiA5LjQ4NjM5IDI1Ni4wNTY3NSAxMS45MDI5OCAyNTUuNzU2ODcgMjQuMzc1IEMgMjg0LjUxOTM0IDguNDIzMTMgMzIxLjI3NjA2IDQwLjIzMDg0IDI5Ni42MjE2NCA1Ni4xODI3MSBDIDMyNi4yMDU3MiA2My45MTY1OSAyOTYuMjQ4MzIgMTA1LjU4NTY5IDI3MS45Njg3NSA5OC42MjUgQyAyNzAuMDI1NjUgMTEwLjIyNjgxIDIyNi42MjEwOCAxMTQuMjg2OCAyMjIuODExMzggOTguNjI1IEMgMTk4LjIzMzQ2IDExNS4zNTEwNSAxNDYuOTg0NTggODkuNjMzODIgMTgxLjk0NjYxIDY2LjgxNzI5IFoiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIxIi8+PHRleHQgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTg5Ljk1IDM0LjUpIiBmaWxsPSJibGFjayI+PHRzcGFuIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EgTmV1ZSIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjUwMCIgeD0iMjkuNTkiIHk9IjE1IiB0ZXh0TGVuZ3RoPSIzNy45MiI+SG90ZWw8L3RzcGFuPjx0c3BhbiBmb250LWZhbWlseT0iSGVsdmV0aWNhIE5ldWUiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI1MDAiIHg9IjYuMTgyIiB5PSIzMyIgdGV4dExlbmd0aD0iODkuMTg0Ij5SZXNlcnZhdGlvbiA8L3RzcGFuPjx0c3BhbiBmb250LWZhbWlseT0iSGVsdmV0aWNhIE5ldWUiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI1MDAiIHg9IjIyLjAzOCIgeT0iNTEiIHRleHRMZW5ndGg9IjUzLjAyNCI+U2VydmljZTwvdHNwYW4+PC90ZXh0PjxwYXRoIGQ9Ik0gMzUyLjk0NjYgNjYuNDk1MDMgQyAzMjguMjE4NzUgNjEuNSAzMzguMDc5NiAxOS40NTAwNSAzNzcuNTI2MDYgMjYuNjI1IEMgMzgxLjE4NTgyIDEyLjYzODczIDQyNy4wNTY3NSAxNC45MDg4NiA0MjYuNzU2ODcgMjYuNjI1IEMgNDU1LjUxOTM0IDExLjYzOTkxIDQ5Mi4yNzYwNiA0MS41MTk4OCA0NjcuNjIxNjQgNTYuNTA0OTcgQyA0OTcuMjA1NzIgNjMuNzcwMTMgNDY3LjI0ODMyIDEwMi45MTM4MyA0NDIuOTY4NzUgOTYuMzc1IEMgNDQxLjAyNTY1IDEwNy4yNzM2NyAzOTcuNjIxMDggMTExLjA4NzYgMzkzLjgxMTM4IDk2LjM3NSBDIDM2OS4yMzM0NiAxMTIuMDg3MzUgMzE3Ljk4NDU4IDg3LjkyODc0IDM1Mi45NDY2IDY2LjQ5NTAzIFoiIGZpbGw9IiNmMjYwNzciLz48cGF0aCBkPSJNIDM1Mi45NDY2IDY2LjQ5NTAzIEMgMzI4LjIxODc1IDYxLjUgMzM4LjA3OTYgMTkuNDUwMDUgMzc3LjUyNjA2IDI2LjYyNSBDIDM4MS4xODU4MiAxMi42Mzg3MyA0MjcuMDU2NzUgMTQuOTA4ODYgNDI2Ljc1Njg3IDI2LjYyNSBDIDQ1NS41MTkzNCAxMS42Mzk5MSA0OTIuMjc2MDYgNDEuNTE5ODggNDY3LjYyMTY0IDU2LjUwNDk3IEMgNDk3LjIwNTcyIDYzLjc3MDEzIDQ2Ny4yNDgzMiAxMDIuOTEzODMgNDQyLjk2ODc1IDk2LjM3NSBDIDQ0MS4wMjU2NSAxMDcuMjczNjcgMzk3LjYyMTA4IDExMS4wODc2IDM5My44MTEzOCA5Ni4zNzUgQyAzNjkuMjMzNDYgMTEyLjA4NzM1IDMxNy45ODQ1OCA4Ny45Mjg3NCAzNTIuOTQ2NiA2Ni40OTUwMyBaIiBzdHJva2U9ImJsYWNrIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMSIvPjx0ZXh0IHRyYW5zZm9ybT0idHJhbnNsYXRlKDM2MC45NSAzNC41KSIgZmlsbD0iYmxhY2siPjx0c3BhbiBmb250LWZhbWlseT0iSGVsdmV0aWNhIE5ldWUiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI1MDAiIHg9IjI2LjYzIiB5PSIxNSIgdGV4dExlbmd0aD0iNDMuODQiPkFpcmxpbmU8L3RzcGFuPjx0c3BhbiBmb250LWZhbWlseT0iSGVsdmV0aWNhIE5ldWUiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI1MDAiIHg9IjYuMTgyIiB5PSIzMyIgdGV4dExlbmd0aD0iODkuMTg0Ij5SZXNlcnZhdGlvbiA8L3RzcGFuPjx0c3BhbiBmb250LWZhbWlseT0iSGVsdmV0aWNhIE5ldWUiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI1MDAiIHg9IjIyLjAzOCIgeT0iNTEiIHRleHRMZW5ndGg9IjUzLjAyNCI+U2VydmljZTwvdHNwYW4+PC90ZXh0PjxyZWN0IHg9IjE4NiIgeT0iMjc5IiB3aWR0aD0iMTIwIiBoZWlnaHQ9IjU3IiBmaWxsPSIjZGFkYWRhIi8+PHJlY3QgeD0iMTg2IiB5PSIyNzkiIHdpZHRoPSIxMjAiIGhlaWdodD0iNTciIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIxIi8+PHRleHQgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTkxIDI4OS41KSIgZmlsbD0iYmxhY2siPjx0c3BhbiBmb250LWZhbWlseT0iSGVsdmV0aWNhIE5ldWUiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI1MDAiIGZpbGw9ImJsYWNrIiB4PSIzNC4xMTIiIHk9IjE1IiB0ZXh0TGVuZ3RoPSI5LjE4NCI+VDwvdHNwYW4+PHRzcGFuIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EgTmV1ZSIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjUwMCIgZmlsbD0iYmxhY2siIHg9IjQxLjgyNCIgeT0iMTUiIHRleHRMZW5ndGg9IjM0LjA2NCI+cmF2ZWw8L3RzcGFuPjx0c3BhbiBmb250LWZhbWlseT0iSGVsdmV0aWNhIE5ldWUiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI1MDAiIGZpbGw9ImJsYWNrIiB4PSIxNC44NDgiIHk9IjMzIiB0ZXh0TGVuZ3RoPSI4MC4zMDQiPkFwcGxpY2F0aW9uPC90c3Bhbj48L3RleHQ+PHBhdGggZD0iTSAyMzkuOTUxNiAxMDkuMTEyNDcgTCAyNDcuOTA1MjIgMTIzLjg3Njk1IEwgMjQ0LjE1Njk2IDEyMy45OTEyMjUgTCAyNDguNDA3MDEgMjYzLjM5MjkyIEwgMjUyLjE1NTI3IDI2My4yNzg2NSBMIDI0NS4xMTU4NiAyNzguNTAwMjMgTCAyMzcuMTYyMjQgMjYzLjczNTc1IEwgMjQwLjkxMDUgMjYzLjYyMTQ3IEwgMjM2LjY2MDQ1IDEyNC4yMTk3OCBMIDIzMi45MTIxOSAxMjQuMzM0MDUgWiIgZmlsbD0iI2RlYWQyNiIvPjxwYXRoIGQ9Ik0gMjM5Ljk1MTYgMTA5LjExMjQ3IEwgMjQ3LjkwNTIyIDEyMy44NzY5NSBMIDI0NC4xNTY5NiAxMjMuOTkxMjI1IEwgMjQ4LjQwNzAxIDI2My4zOTI5MiBMIDI1Mi4xNTUyNyAyNjMuMjc4NjUgTCAyNDUuMTE1ODYgMjc4LjUwMDIzIEwgMjM3LjE2MjI0IDI2My43MzU3NSBMIDI0MC45MTA1IDI2My42MjE0NyBMIDIzNi42NjA0NSAxMjQuMjE5NzggTCAyMzIuOTEyMTkgMTI0LjMzNDA1IFoiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIxIi8+PHJlY3QgeD0iMTgwIiB5PSIxNDEiIHdpZHRoPSIxMjAiIGhlaWdodD0iMzAiIGZpbGw9IiNkZWFkMjYiLz48cmVjdCB4PSIxODAiIHk9IjE0MSIgd2lkdGg9IjEyMCIgaGVpZ2h0PSIzMCIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjEiLz48dGV4dCB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxODUgMTQ3KSIgZmlsbD0iYmxhY2siPjx0c3BhbiBmb250LWZhbWlseT0iSGVsdmV0aWNhIE5ldWUiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI1MDAiIHg9IjQyLjU2IiB5PSIxNSIgdGV4dExlbmd0aD0iMjQuODgiPkFQSTwvdHNwYW4+PC90ZXh0PjxwYXRoIGQ9Ik0gMTAxLjQ0NjUyIDk5LjU0NTg2NyBMIDExNi4xNjY0IDEwNy41ODE3MiBMIDExMy4wODcyNCAxMDkuNzIyMTE0IEwgMjIwLjQyMTIzIDI2NC4xMzI0MiBMIDIyMy41MDAzOSAyNjEuOTkyMDIgTCAyMjUuOTAzNjQgMjc4LjU4OTQ1IEwgMjExLjE4Mzc1IDI3MC41NTM1OSBMIDIxNC4yNjI5MSAyNjguNDEzMiBMIDEwNi45Mjg5MjMgMTE0LjAwMjkgTCAxMDMuODQ5NzY0IDExNi4xNDMyOSBaIiBmaWxsPSIjM2E4ZWVkIi8+PHBhdGggZD0iTSAxMDEuNDQ2NTIgOTkuNTQ1ODY3IEwgMTE2LjE2NjQgMTA3LjU4MTcyIEwgMTEzLjA4NzI0IDEwOS43MjIxMTQgTCAyMjAuNDIxMjMgMjY0LjEzMjQyIEwgMjIzLjUwMDM5IDI2MS45OTIwMiBMIDIyNS45MDM2NCAyNzguNTg5NDUgTCAyMTEuMTgzNzUgMjcwLjU1MzU5IEwgMjE0LjI2MjkxIDI2OC40MTMyIEwgMTA2LjkyODkyMyAxMTQuMDAyOSBMIDEwMy44NDk3NjQgMTE2LjE0MzI5IFoiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIxIi8+PHJlY3QgeD0iOTYiIHk9IjE4NiIgd2lkdGg9IjEyMCIgaGVpZ2h0PSIzMCIgZmlsbD0iIzNhOGVlZCIvPjxyZWN0IHg9Ijk2IiB5PSIxODYiIHdpZHRoPSIxMjAiIGhlaWdodD0iMzAiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIxIi8+PHRleHQgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAxIDE5MikiIGZpbGw9ImJsYWNrIj48dHNwYW4gZm9udC1mYW1pbHk9IkhlbHZldGljYSBOZXVlIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNTAwIiB4PSI0Mi41NiIgeT0iMTUiIHRleHRMZW5ndGg9IjI0Ljg4Ij5BUEk8L3RzcGFuPjwvdGV4dD48cGF0aCBkPSJNIDM5OS42MzY1NSAxMDQuMjQyNDY2IEwgMzk2LjU3NDc1IDEyMC43MzExMSBMIDM5My41ODMyIDExOC40Njk4OCBMIDI3OS44ODAyOCAyNjguODk2MTYgTCAyODIuODcxODMgMjcxLjE1NzM5IEwgMjY3Ljg0MzgzIDI3OC42MDExMyBMIDI3MC45MDU2NCAyNjIuMTEyNDggTCAyNzMuODk3MTkgMjY0LjM3MzcxIEwgMzg3LjYwMDEgMTEzLjk0NzQzIEwgMzg0LjYwODU2IDExMS42ODYyMSBaIiBmaWxsPSIjZjI2MDc3Ii8+PHBhdGggZD0iTSAzOTkuNjM2NTUgMTA0LjI0MjQ2NiBMIDM5Ni41NzQ3NSAxMjAuNzMxMTEgTCAzOTMuNTgzMiAxMTguNDY5ODggTCAyNzkuODgwMjggMjY4Ljg5NjE2IEwgMjgyLjg3MTgzIDI3MS4xNTczOSBMIDI2Ny44NDM4MyAyNzguNjAxMTMgTCAyNzAuOTA1NjQgMjYyLjExMjQ4IEwgMjczLjg5NzE5IDI2NC4zNzM3MSBMIDM4Ny42MDAxIDExMy45NDc0MyBMIDM4NC42MDg1NiAxMTEuNjg2MjEgWiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2Utd2lkdGg9IjEiLz48cmVjdCB4PSIyNzAiIHk9IjE4NiIgd2lkdGg9IjEyMCIgaGVpZ2h0PSIzMCIgZmlsbD0iI2YyNjA3NyIvPjxyZWN0IHg9IjI3MCIgeT0iMTg2IiB3aWR0aD0iMTIwIiBoZWlnaHQ9IjMwIiBzdHJva2U9ImJsYWNrIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMSIvPjx0ZXh0IHRyYW5zZm9ybT0idHJhbnNsYXRlKDI3NSAxOTIpIiBmaWxsPSJibGFjayI+PHRzcGFuIGZvbnQtZmFtaWx5PSJIZWx2ZXRpY2EgTmV1ZSIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjUwMCIgeD0iNDIuNTYiIHk9IjE1IiB0ZXh0TGVuZ3RoPSIyNC44OCI+QVBJPC90c3Bhbj48L3RleHQ+PC9nPjwvZz48L3N2Zz4K" alt="Service Oriented Architecture" />
<p class="caption">Service Oriented Architecture</p>
</div>
<p>A Service-Oriented Architecture has many advantages including: (1) we always maintain only one copy of data (this is particularly important for things like hotel reservations where we do not want to over-commit) and (2) the owners of the data can set the rules about the use of their data. With these advantages, an SOA system must be carefully designed to have good performance and meet the user's needs.</p>
<p>When an application makes a set of services in its API available over the web, we call these <em>web services</em>.</p>
<h2 id="google-geocoding-web-service">Google geocoding web service</h2>
<p> </p>
<p>Google has an excellent web service that allows us to make use of their large database of geographic information. We can submit a geographical search string like "Ann Arbor, MI" to their geocoding API and have Google return its best guess as to where on a map we might find our search string and tell us about the landmarks nearby.</p>
<p>The geocoding service is free but rate limited so you cannot make unlimited use of the API in a commercial application. But if you have some survey data where an end user has entered a location in a free-format input box, you can use this API to clean up your data quite nicely.</p>
<p><em>When you are using a free API like Google's geocoding API, you need to be respectful in your use of these resources. If too many people abuse the service, Google might drop or significantly curtail its free service.</em></p>
<p></p>
<p>You can read the online documentation for this service, but it is quite simple and you can even test it using a browser by typing the following URL into your browser:</p>
<p><a href="http://maps.googleapis.com/maps/api/geocode/json?address=Ann+Arbor%2C+MI">http://maps.googleapis.com/maps/api/geocode/json?address=Ann+Arbor%2C+MI</a></p>
<p>Make sure to unwrap the URL and remove any spaces from the URL before pasting it into your browser.</p>
<p>The following is a simple application to prompt the user for a search string, call the Google geocoding API, and extract information from the returned JSON.</p>
<script type="text/javascript">(function(d,l,s,i,c){function n(e){e=e.nextSibling;return (!e||e.nodeType!=3)?e:n(e);};function r(f){/in/.test(d.readyState) ? setTimeout(function(){r(f);},9):f()};l=d.getElementsByTagName('script');s=l[l.length-1];r(function(){i=n(s),c=n(i);i.setAttribute('data-src','https://trinket.io/tools/1.0/jekyll/embed/python3#code='+encodeURIComponent(c.nodeValue.replace(/^\s+|\s+$/g,'')));});})(document)</script>
<iframe width="100%" height="400" frameborder="0" marginwidth="0" marginheight="0" class="lazyload" allowfullscreen>
</iframe>
<!--
import urllib.request, urllib.parse, urllib.error
import json
serviceurl = 'http://maps.googleapis.com/maps/api/geocode/json?'
while True:
address = input('Enter location: ')
if len(address) < 1 : break
url = serviceurl + urllib.parse.urlencode(
{'sensor':'false', 'address': address})
print('Retrieving', url)
uh = urllib.request.urlopen(url)
data = uh.read().decode()
print('Retrieved',len(data),'characters')
try:
js = json.loads(data)
except:
js = None
if not js or 'status' not in js or js['status'] != 'OK':
print('==== Failure To Retrieve ====')
print(data)
continue
print(json.dumps(js, indent=4))
lat = js["results"][0]["geometry"]["location"]["lat"]
lng = js["results"][0]["geometry"]["location"]["lng"]
print('lat',lat,'lng',lng)
location = js['results'][0]['formatted_address']
print(location)
# Code: http://www.pythonlearn.com/code3/geojson.py
-->
<p>The program takes the search string and constructs a URL with the search string as a properly encoded parameter and then uses <em>urllib</em> to retrieve the text from the Google geocoding API. Unlike a fixed web page, the data we get depends on the parameters we send and the geographical data stored in Google's servers.</p>
<p>Once we retrieve the JSON data, we parse it with the <em>json</em> library and do a few checks to make sure that we received good data, then extract the information that we are looking for.</p>
<p>The output of the program is as follows (some of the returned JSON has been removed):</p>
<pre><code>$ python geojson.py
Enter location: Ann Arbor, MI
Retrieving http://maps.googleapis.com/maps/api/
geocode/json?sensor=false&address=Ann+Arbor%2C+MI
Retrieved 1669 characters</code></pre>
<div class="sourceCode"><pre class="sourceCode json"><code class="sourceCode json"><span class="fu">{</span>
<span class="dt">"status"</span><span class="fu">:</span> <span class="st">"OK"</span><span class="fu">,</span>
<span class="dt">"results"</span><span class="fu">:</span> <span class="ot">[</span>
<span class="fu">{</span>
<span class="dt">"geometry"</span><span class="fu">:</span> <span class="fu">{</span>
<span class="dt">"location_type"</span><span class="fu">:</span> <span class="st">"APPROXIMATE"</span><span class="fu">,</span>
<span class="dt">"location"</span><span class="fu">:</span> <span class="fu">{</span>
<span class="dt">"lat"</span><span class="fu">:</span> <span class="fl">42.2808256</span><span class="fu">,</span>
<span class="dt">"lng"</span><span class="fu">:</span> <span class="fl">-83.7430378</span>
<span class="fu">}</span>
<span class="fu">},</span>
<span class="dt">"address_components"</span><span class="fu">:</span> <span class="ot">[</span>
<span class="fu">{</span>
<span class="dt">"long_name"</span><span class="fu">:</span> <span class="st">"Ann Arbor"</span><span class="fu">,</span>
<span class="dt">"types"</span><span class="fu">:</span> <span class="ot">[</span>
<span class="st">"locality"</span><span class="ot">,</span>
<span class="st">"political"</span>
<span class="ot">]</span><span class="fu">,</span>
<span class="dt">"short_name"</span><span class="fu">:</span> <span class="st">"Ann Arbor"</span>
<span class="fu">}</span>
<span class="ot">]</span><span class="fu">,</span>
<span class="dt">"formatted_address"</span><span class="fu">:</span> <span class="st">"Ann Arbor, MI, USA"</span><span class="fu">,</span>
<span class="dt">"types"</span><span class="fu">:</span> <span class="ot">[</span>
<span class="st">"locality"</span><span class="ot">,</span>
<span class="st">"political"</span>
<span class="ot">]</span>
<span class="fu">}</span>
<span class="ot">]</span>
<span class="fu">}</span>
<span class="er">lat</span> <span class="er">42.2808256</span> <span class="er">lng</span> <span class="er">-83.7430378</span>
<span class="er">Ann</span> <span class="er">Arbor,</span> <span class="er">MI,</span> <span class="er">USA</span></code></pre></div>
<pre><code>Enter location:</code></pre>
<p>You can download <a href="http://www.pythonlearn.com/code3/geoxml.py">www.pythonlearn.com/code3/geoxml.py</a> to explore the XML variant of the Google geocoding API.</p>
<h2 id="security-and-api-usage">Security and API usage</h2>
<p> </p>
<p>It is quite common that you need some kind of "API key" to make use of a vendor's API. The general idea is that they want to know who is using their services and how much each user is using. Perhaps they have free and pay tiers of their services or have a policy that limits the number of requests that a single individual can make during a particular time period.</p>
<p>Sometimes once you get your API key, you simply include the key as part of POST data or perhaps as a parameter on the URL when calling the API.</p>
<p>Other times, the vendor wants increased assurance of the source of the requests and so they add expect you to send cryptographically signed messages using shared keys and secrets. A very common technology that is used to sign requests over the Internet is called <em>OAuth</em>. You can read more about the OAuth protocol at <a href="http://www.oauth.net">www.oauth.net</a>.</p>
<p>As the Twitter API became increasingly valuable, Twitter went from an open and public API to an API that required the use of OAuth signatures on each API request. Thankfully there are still a number of convenient and free OAuth libraries so you can avoid writing an OAuth implementation from scratch by reading the specification. These libraries are of varying complexity and have varying degrees of richness. The OAuth web site has information about various OAuth libraries.</p>
<p>For this next sample program we will download the files <em>twurl.py</em>, <em>hidden.py</em>, <em>oauth.py</em>, and <em>twitter1.py</em> from <a href="http://www.pythonlearn.com/code">www.pythonlearn.com/code</a> and put them all in a folder on your computer.</p>
<p>To make use of these programs you will need to have a Twitter account, and authorize your Python code as an application, set up a key, secret, token and token secret. You will edit the file <em>hidden.py</em> and put these four strings into the appropriate variables in the file:</p>
<script type="text/javascript">(function(d,l,s,i,c){function n(e){e=e.nextSibling;return (!e||e.nodeType!=3)?e:n(e);};function r(f){/in/.test(d.readyState) ? setTimeout(function(){r(f);},9):f()};l=d.getElementsByTagName('script');s=l[l.length-1];r(function(){i=n(s),c=n(i);i.setAttribute('data-src','https://trinket.io/tools/1.0/jekyll/embed/python3#code='+encodeURIComponent(c.nodeValue.replace(/^\s+|\s+$/g,'')));});})(document)</script>
<iframe width="100%" height="400" frameborder="0" marginwidth="0" marginheight="0" class="lazyload" allowfullscreen>
</iframe>
<!--
# Keep this file separate
# https://apps.twitter.com/
# Create new App
def oauth() :
return { "consumer_key" : "h7Lu...Ng",
"consumer_secret" : "dNKenAC3New...mmn7Q",
"token_key" : "10185562-eibxCp9n2...P4GEQQOSGI",
"token_secret" : "H0ycCFemmC4wyf1...qoIpBo" }
# Code: http://www.pythonlearn.com/code3/hidden.py
-->
<p>The Twitter web service are accessed using a URL like this:</p>
<p><a href="https://api.twitter.com/1.1/statuses/user_timeline.json" class="uri">https://api.twitter.com/1.1/statuses/user_timeline.json</a></p>
<p>But once all of the security information has been added, the URL will look more like:</p>
<pre><code>https://api.twitter.com/1.1/statuses/user_timeline.json?count=2
&oauth_version=1.0&oauth_token=101...SGI&screen_name=drchuck
&oauth_nonce=09239679&oauth_timestamp=1380395644
&oauth_signature=rLK...BoD&oauth_consumer_key=h7Lu...GNg
&oauth_signature_method=HMAC-SHA1</code></pre>
<p>You can read the OAuth specification if you want to know more about the meaning of the various parameters that are added to meet the security requirements of OAuth.</p>
<p>For the programs we run with Twitter, we hide all the complexity in the files <em>oauth.py</em> and <em>twurl.py</em>. We simply set the secrets in <em>hidden.py</em> and then send the desired URL to the <em>twurl.augment()</em> function and the library code adds all the necessary parameters to the URL for us.</p>
<p>This program retrieves the timeline for a particular Twitter user and returns it to us in JSON format in a string. We simply print the first 250 characters of the string:</p>
<script type="text/javascript">(function(d,l,s,i,c){function n(e){e=e.nextSibling;return (!e||e.nodeType!=3)?e:n(e);};function r(f){/in/.test(d.readyState) ? setTimeout(function(){r(f);},9):f()};l=d.getElementsByTagName('script');s=l[l.length-1];r(function(){i=n(s),c=n(i);i.setAttribute('data-src','https://trinket.io/tools/1.0/jekyll/embed/python3#code='+encodeURIComponent(c.nodeValue.replace(/^\s+|\s+$/g,'')));});})(document)</script>
<iframe width="100%" height="400" frameborder="0" marginwidth="0" marginheight="0" class="lazyload" allowfullscreen>
</iframe>
<!--
import urllib.request, urllib.parse, urllib.error
import twurl
TWITTER_URL = 'https://api.twitter.com/1.1/statuses/user_timeline.json'
while True:
print('')
acct = input('Enter Twitter Account:')
if ( len(acct) < 1 ) : break
url = twurl.augment(TWITTER_URL,
{'screen_name': acct, 'count': '2'} )
print('Retrieving', url)
connection = urllib.request.urlopen(url)
data = connection.read().decode()
print(data[:250])
headers = dict(connection.getheaders())
# print headers
print('Remaining', headers['x-rate-limit-remaining'])
# Code: http://www.pythonlearn.com/code3/twitter1.py
----{twurl.py}----
import urllib.request, urllib.parse, urllib.error
import oauth
import hidden
def augment(url, parameters) :
secrets = hidden.oauth()
consumer = oauth.OAuthConsumer(secrets['consumer_key'], secrets['consumer_secret'])
token = oauth.OAuthToken(secrets['token_key'],secrets['token_secret'])
oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer,
token=token, http_method='GET', http_url=url, parameters=parameters)
oauth_request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), consumer, token)
return oauth_request.to_url()
def test_me() :
print('* Calling Twitter...')
url = augment('https://api.twitter.com/1.1/statuses/user_timeline.json',
{'screen_name': 'drchuck', 'count': '2'} )
print(url)
connection = urllib.request.urlopen(url)
data = connection.read()
print(data)
headers = dict(connection.getheaders())
print(headers)
-->
<p>When the program runs it produces the following output:</p>
<pre><code>Enter Twitter Account:drchuck
Retrieving https://api.twitter.com/1.1/ ...
[{"created_at":"Sat Sep 28 17:30:25 +0000 2013","
id":384007200990982144,"id_str":"384007200990982144",
"text":"RT @fixpert: See how the Dutch handle traffic
intersections: http:\/\/t.co\/tIiVWtEhj4\n#brilliant",
"source":"web","truncated":false,"in_rep
Remaining 178
Enter Twitter Account:fixpert
Retrieving https://api.twitter.com/1.1/ ...
[{"created_at":"Sat Sep 28 18:03:56 +0000 2013",
"id":384015634108919808,"id_str":"384015634108919808",
"text":"3 months after my freak bocce ball accident,
my wedding ring fits again! :)\n\nhttps:\/\/t.co\/2XmHPx7kgX",
"source":"web","truncated":false,
Remaining 177
Enter Twitter Account:</code></pre>
<p>Along with the returned timeline data, Twitter also returns metadata about the request in the HTTP response headers. One header in particular, <em>x-rate-limit-remaining</em>, informs us how many more requests we can make before we will be shut off for a short time period. You can see that our remaining retrievals drop by one each time we make a request to the API.</p>
<p>In the following example, we retrieve a user's Twitter friends, parse the returned JSON, and extract some of the information about the friends. We also dump the JSON after parsing and "pretty-print" it with an indent of four characters to allow us to pore through the data when we want to extract more fields.</p>
<script type="text/javascript">(function(d,l,s,i,c){function n(e){e=e.nextSibling;return (!e||e.nodeType!=3)?e:n(e);};function r(f){/in/.test(d.readyState) ? setTimeout(function(){r(f);},9):f()};l=d.getElementsByTagName('script');s=l[l.length-1];r(function(){i=n(s),c=n(i);i.setAttribute('data-src','https://trinket.io/tools/1.0/jekyll/embed/python3#code='+encodeURIComponent(c.nodeValue.replace(/^\s+|\s+$/g,'')));});})(document)</script>
<iframe width="100%" height="400" frameborder="0" marginwidth="0" marginheight="0" class="lazyload" allowfullscreen>
</iframe>
<!--
import urllib.request, urllib.parse, urllib.error
import twurl
import json
TWITTER_URL = 'https://api.twitter.com/1.1/friends/list.json'
while True:
print('')
acct = input('Enter Twitter Account:')
if ( len(acct) < 1 ) : break
url = twurl.augment(TWITTER_URL,
{'screen_name': acct, 'count': '5'} )
print('Retrieving', url)
connection = urllib.request.urlopen(url)
data = connection.read().decode()
headers = dict(connection.getheaders())
print('Remaining', headers['x-rate-limit-remaining'])
js = json.loads(data)
print(json.dumps(js, indent=4))
for u in js['users'] :
print(u['screen_name'])
s = u['status']['text']
print(' ',s[:50])
# Code: http://www.pythonlearn.com/code3/twitter2.py
----{twurl.py}----
import urllib.request, urllib.parse, urllib.error
import oauth
import hidden
def augment(url, parameters) :
secrets = hidden.oauth()
consumer = oauth.OAuthConsumer(secrets['consumer_key'], secrets['consumer_secret'])
token = oauth.OAuthToken(secrets['token_key'],secrets['token_secret'])
oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer,
token=token, http_method='GET', http_url=url, parameters=parameters)
oauth_request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), consumer, token)
return oauth_request.to_url()
def test_me() :
print('* Calling Twitter...')
url = augment('https://api.twitter.com/1.1/statuses/user_timeline.json',
{'screen_name': 'drchuck', 'count': '2'} )
print(url)
connection = urllib.request.urlopen(url)
data = connection.read()
print(data)
headers = dict(connection.getheaders())
print(headers)
-->
<p>Since the JSON becomes a set of nested Python lists and dictionaries, we can use a combination of the index operation and <code>for</code> loops to wander through the returned data structures with very little Python code.</p>
<p>The output of the program looks as follows (some of the data items are shortened to fit on the page):</p>
<pre><code>Enter Twitter Account:drchuck
Retrieving https://api.twitter.com/1.1/friends ...
Remaining 14</code></pre>
<div class="sourceCode"><pre class="sourceCode json"><code class="sourceCode json"><span class="fu">{</span>
<span class="dt">"next_cursor"</span><span class="fu">:</span> <span class="dv">1444171224491980205</span><span class="fu">,</span>
<span class="dt">"users"</span><span class="fu">:</span> <span class="ot">[</span>
<span class="fu">{</span>
<span class="dt">"id"</span><span class="fu">:</span> <span class="dv">662433</span><span class="fu">,</span>
<span class="dt">"followers_count"</span><span class="fu">:</span> <span class="dv">28725</span><span class="fu">,</span>
<span class="dt">"status"</span><span class="fu">:</span> <span class="fu">{</span>
<span class="dt">"text"</span><span class="fu">:</span> <span class="st">"@jazzychad I just bought one .__."</span><span class="fu">,</span>
<span class="dt">"created_at"</span><span class="fu">:</span> <span class="st">"Fri Sep 20 08:36:34 +0000 2013"</span><span class="fu">,</span>
<span class="dt">"retweeted"</span><span class="fu">:</span> <span class="kw">false</span><span class="fu">,</span>
<span class="fu">},</span>
<span class="dt">"location"</span><span class="fu">:</span> <span class="st">"San Francisco, California"</span><span class="fu">,</span>
<span class="dt">"screen_name"</span><span class="fu">:</span> <span class="st">"leahculver"</span><span class="fu">,</span>
<span class="dt">"name"</span><span class="fu">:</span> <span class="st">"Leah Culver"</span><span class="fu">,</span>
<span class="fu">}</span><span class="ot">,</span>
<span class="fu">{</span>
<span class="dt">"id"</span><span class="fu">:</span> <span class="dv">40426722</span><span class="fu">,</span>
<span class="dt">"followers_count"</span><span class="fu">:</span> <span class="dv">2635</span><span class="fu">,</span>
<span class="dt">"status"</span><span class="fu">:</span> <span class="fu">{</span>
<span class="dt">"text"</span><span class="fu">:</span> <span class="st">"RT @WSJ: Big employers like Google ..."</span><span class="fu">,</span>
<span class="dt">"created_at"</span><span class="fu">:</span> <span class="st">"Sat Sep 28 19:36:37 +0000 2013"</span><span class="fu">,</span>
<span class="fu">},</span>
<span class="dt">"location"</span><span class="fu">:</span> <span class="st">"Victoria Canada"</span><span class="fu">,</span>
<span class="dt">"screen_name"</span><span class="fu">:</span> <span class="st">"_valeriei"</span><span class="fu">,</span>
<span class="dt">"name"</span><span class="fu">:</span> <span class="st">"Valerie Irvine"</span><span class="fu">,</span>
<span class="er">]</span><span class="fu">,</span>
<span class="dt">"next_cursor_str"</span><span class="fu">:</span> <span class="st">"1444171224491980205"</span>
<span class="fu">}</span></code></pre></div>
<pre><code>leahculver
@jazzychad I just bought one .__.
_valeriei
RT @WSJ: Big employers like Google, AT&amp;T are h
ericbollens
RT @lukew: sneak peek: my LONG take on the good &a
halherzog
Learning Objects is 10. We had a cake with the LO,
scweeker
@DeviceLabDC love it! Now where so I get that "etc
Enter Twitter Account:</code></pre>
<p>The last bit of the output is where we see the for loop reading the five most recent "friends" of the <em>drchuck</em> Twitter account and printing the most recent status for each friend. There is a great deal more data available in the returned JSON. If you look in the output of the program, you can also see that the "find the friends" of a particular account has a different rate limitation than the number of timeline queries we are allowed to run per time period.</p>
<p>These secure API keys allow Twitter to have solid confidence that they know who is using their API and data and at what level. The rate-limiting approach allows us to do simple, personal data retrievals but does not allow us to build a product that pulls data from their API millions of times per day.</p>
<h2 id="glossary">Glossary</h2>
<dl>
<dt>API</dt>
<dd>Application Program Interface - A contract between applications that defines the patterns of interaction between two application components.
</dd>
<dt>ElementTree</dt>
<dd>A built-in Python library used to parse XML data.
</dd>
<dt>JSON</dt>
<dd>JavaScript Object Notation. A format that allows for the markup of structured data based on the syntax of JavaScript Objects.
</dd>
<dt>SOA</dt>
<dd>Service-Oriented Architecture. When an application is made of components connected across a network.
</dd>
<dt>XML</dt>
<dd>eXtensible Markup Language. A format that allows for the markup of structured data.
</dd>
</dl>
<h2 id="exercises">Exercises</h2>
<p><strong>Exercise 1:</strong> Change either the <a href="http://www.pythonlearn.com/code3/geojson.py">www.pythonlearn.com/code3/geojson.py</a> or <a href="http://www.pythonlearn.com/code3/geoxml.py">www.pythonlearn.com/code3/geoxml.py</a> to print out the two-character country code from the retrieved data. Add error checking so your program does not traceback if the country code is not there. Once you have it working, search for "Atlantic Ocean" and make sure it can handle locations that are not in any country.</p>
</div>
</div>
</div>
<script src="data:application/javascript; charset=utf-8;base64,LyohIGpRdWVyeSB2Mi4yLjAgfCAoYykgalF1ZXJ5IEZvdW5kYXRpb24gfCBqcXVlcnkub3JnL2xpY2Vuc2UgKi8KIWZ1bmN0aW9uKGEsYil7Im9iamVjdCI9PXR5cGVvZiBtb2R1bGUmJiJvYmplY3QiPT10eXBlb2YgbW9kdWxlLmV4cG9ydHM/bW9kdWxlLmV4cG9ydHM9YS5kb2N1bWVudD9iKGEsITApOmZ1bmN0aW9uKGEpe2lmKCFhLmRvY3VtZW50KXRocm93IG5ldyBFcnJvcigialF1ZXJ5IHJlcXVpcmVzIGEgd2luZG93IHdpdGggYSBkb2N1bWVudCIpO3JldHVybiBiKGEpfTpiKGEpfSgidW5kZWZpbmVkIiE9dHlwZW9mIHdpbmRvdz93aW5kb3c6dGhpcyxmdW5jdGlvbihhLGIpe3ZhciBjPVtdLGQ9YS5kb2N1bWVudCxlPWMuc2xpY2UsZj1jLmNvbmNhdCxnPWMucHVzaCxoPWMuaW5kZXhPZixpPXt9LGo9aS50b1N0cmluZyxrPWkuaGFzT3duUHJvcGVydHksbD17fSxtPSIyLjIuMCIsbj1mdW5jdGlvbihhLGIpe3JldHVybiBuZXcgbi5mbi5pbml0KGEsYil9LG89L15bXHNcdUZFRkZceEEwXSt8W1xzXHVGRUZGXHhBMF0rJC9nLHA9L14tbXMtLyxxPS8tKFtcZGEtel0pL2dpLHI9ZnVuY3Rpb24oYSxiKXtyZXR1cm4gYi50b1VwcGVyQ2FzZSgpfTtuLmZuPW4ucHJvdG90eXBlPXtqcXVlcnk6bSxjb25zdHJ1Y3RvcjpuLHNlbGVjdG9yOiIiLGxlbmd0aDowLHRvQXJyYXk6ZnVuY3Rpb24oKXtyZXR1cm4gZS5jYWxsKHRoaXMpfSxnZXQ6ZnVuY3Rpb24oYSl7cmV0dXJuIG51bGwhPWE/MD5hP3RoaXNbYSt0aGlzLmxlbmd0aF06dGhpc1thXTplLmNhbGwodGhpcyl9LHB1c2hTdGFjazpmdW5jdGlvbihhKXt2YXIgYj1uLm1lcmdlKHRoaXMuY29uc3RydWN0b3IoKSxhKTtyZXR1cm4gYi5wcmV2T2JqZWN0PXRoaXMsYi5jb250ZXh0PXRoaXMuY29udGV4dCxifSxlYWNoOmZ1bmN0aW9uKGEpe3JldHVybiBuLmVhY2godGhpcyxhKX0sbWFwOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLnB1c2hTdGFjayhuLm1hcCh0aGlzLGZ1bmN0aW9uKGIsYyl7cmV0dXJuIGEuY2FsbChiLGMsYil9KSl9LHNsaWNlOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMucHVzaFN0YWNrKGUuYXBwbHkodGhpcyxhcmd1bWVudHMpKX0sZmlyc3Q6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5lcSgwKX0sbGFzdDpmdW5jdGlvbigpe3JldHVybiB0aGlzLmVxKC0xKX0sZXE6ZnVuY3Rpb24oYSl7dmFyIGI9dGhpcy5sZW5ndGgsYz0rYSsoMD5hP2I6MCk7cmV0dXJuIHRoaXMucHVzaFN0YWNrKGM+PTAmJmI+Yz9bdGhpc1tjXV06W10pfSxlbmQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5wcmV2T2JqZWN0fHx0aGlzLmNvbnN0cnVjdG9yKCl9LHB1c2g6Zyxzb3J0OmMuc29ydCxzcGxpY2U6Yy5zcGxpY2V9LG4uZXh0ZW5kPW4uZm4uZXh0ZW5kPWZ1bmN0aW9uKCl7dmFyIGEsYixjLGQsZSxmLGc9YXJndW1lbnRzWzBdfHx7fSxoPTEsaT1hcmd1bWVudHMubGVuZ3RoLGo9ITE7Zm9yKCJib29sZWFuIj09dHlwZW9mIGcmJihqPWcsZz1hcmd1bWVudHNbaF18fHt9LGgrKyksIm9iamVjdCI9PXR5cGVvZiBnfHxuLmlzRnVuY3Rpb24oZyl8fChnPXt9KSxoPT09aSYmKGc9dGhpcyxoLS0pO2k+aDtoKyspaWYobnVsbCE9KGE9YXJndW1lbnRzW2hdKSlmb3IoYiBpbiBhKWM9Z1tiXSxkPWFbYl0sZyE9PWQmJihqJiZkJiYobi5pc1BsYWluT2JqZWN0KGQpfHwoZT1uLmlzQXJyYXkoZCkpKT8oZT8oZT0hMSxmPWMmJm4uaXNBcnJheShjKT9jOltdKTpmPWMmJm4uaXNQbGFpbk9iamVjdChjKT9jOnt9LGdbYl09bi5leHRlbmQoaixmLGQpKTp2b2lkIDAhPT1kJiYoZ1tiXT1kKSk7cmV0dXJuIGd9LG4uZXh0ZW5kKHtleHBhbmRvOiJqUXVlcnkiKyhtK01hdGgucmFuZG9tKCkpLnJlcGxhY2UoL1xEL2csIiIpLGlzUmVhZHk6ITAsZXJyb3I6ZnVuY3Rpb24oYSl7dGhyb3cgbmV3IEVycm9yKGEpfSxub29wOmZ1bmN0aW9uKCl7fSxpc0Z1bmN0aW9uOmZ1bmN0aW9uKGEpe3JldHVybiJmdW5jdGlvbiI9PT1uLnR5cGUoYSl9LGlzQXJyYXk6QXJyYXkuaXNBcnJheSxpc1dpbmRvdzpmdW5jdGlvbihhKXtyZXR1cm4gbnVsbCE9YSYmYT09PWEud2luZG93fSxpc051bWVyaWM6ZnVuY3Rpb24oYSl7dmFyIGI9YSYmYS50b1N0cmluZygpO3JldHVybiFuLmlzQXJyYXkoYSkmJmItcGFyc2VGbG9hdChiKSsxPj0wfSxpc1BsYWluT2JqZWN0OmZ1bmN0aW9uKGEpe3JldHVybiJvYmplY3QiIT09bi50eXBlKGEpfHxhLm5vZGVUeXBlfHxuLmlzV2luZG93KGEpPyExOmEuY29uc3RydWN0b3ImJiFrLmNhbGwoYS5jb25zdHJ1Y3Rvci5wcm90b3R5cGUsImlzUHJvdG90eXBlT2YiKT8hMTohMH0saXNFbXB0eU9iamVjdDpmdW5jdGlvbihhKXt2YXIgYjtmb3IoYiBpbiBhKXJldHVybiExO3JldHVybiEwfSx0eXBlOmZ1bmN0aW9uKGEpe3JldHVybiBudWxsPT1hP2ErIiI6Im9iamVjdCI9PXR5cGVvZiBhfHwiZnVuY3Rpb24iPT10eXBlb2YgYT9pW2ouY2FsbChhKV18fCJvYmplY3QiOnR5cGVvZiBhfSxnbG9iYWxFdmFsOmZ1bmN0aW9uKGEpe3ZhciBiLGM9ZXZhbDthPW4udHJpbShhKSxhJiYoMT09PWEuaW5kZXhPZigidXNlIHN0cmljdCIpPyhiPWQuY3JlYXRlRWxlbWVudCgic2NyaXB0IiksYi50ZXh0PWEsZC5oZWFkLmFwcGVuZENoaWxkKGIpLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoYikpOmMoYSkpfSxjYW1lbENhc2U6ZnVuY3Rpb24oYSl7cmV0dXJuIGEucmVwbGFjZShwLCJtcy0iKS5yZXBsYWNlKHEscil9LG5vZGVOYW1lOmZ1bmN0aW9uKGEsYil7cmV0dXJuIGEubm9kZU5hbWUmJmEubm9kZU5hbWUudG9Mb3dlckNhc2UoKT09PWIudG9Mb3dlckNhc2UoKX0sZWFjaDpmdW5jdGlvbihhLGIpe3ZhciBjLGQ9MDtpZihzKGEpKXtmb3IoYz1hLmxlbmd0aDtjPmQ7ZCsrKWlmKGIuY2FsbChhW2RdLGQsYVtkXSk9PT0hMSlicmVha31lbHNlIGZvcihkIGluIGEpaWYoYi5jYWxsKGFbZF0sZCxhW2RdKT09PSExKWJyZWFrO3JldHVybiBhfSx0cmltOmZ1bmN0aW9uKGEpe3JldHVybiBudWxsPT1hPyIiOihhKyIiKS5yZXBsYWNlKG8sIiIpfSxtYWtlQXJyYXk6ZnVuY3Rpb24oYSxiKXt2YXIgYz1ifHxbXTtyZXR1cm4gbnVsbCE9YSYmKHMoT2JqZWN0KGEpKT9uLm1lcmdlKGMsInN0cmluZyI9PXR5cGVvZiBhP1thXTphKTpnLmNhbGwoYyxhKSksY30saW5BcnJheTpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIG51bGw9PWI/LTE6aC5jYWxsKGIsYSxjKX0sbWVyZ2U6ZnVuY3Rpb24oYSxiKXtmb3IodmFyIGM9K2IubGVuZ3RoLGQ9MCxlPWEubGVuZ3RoO2M+ZDtkKyspYVtlKytdPWJbZF07cmV0dXJuIGEubGVuZ3RoPWUsYX0sZ3JlcDpmdW5jdGlvbihhLGIsYyl7Zm9yKHZhciBkLGU9W10sZj0wLGc9YS5sZW5ndGgsaD0hYztnPmY7ZisrKWQ9IWIoYVtmXSxmKSxkIT09aCYmZS5wdXNoKGFbZl0pO3JldHVybiBlfSxtYXA6ZnVuY3Rpb24oYSxiLGMpe3ZhciBkLGUsZz0wLGg9W107aWYocyhhKSlmb3IoZD1hLmxlbmd0aDtkPmc7ZysrKWU9YihhW2ddLGcsYyksbnVsbCE9ZSYmaC5wdXNoKGUpO2Vsc2UgZm9yKGcgaW4gYSllPWIoYVtnXSxnLGMpLG51bGwhPWUmJmgucHVzaChlKTtyZXR1cm4gZi5hcHBseShbXSxoKX0sZ3VpZDoxLHByb3h5OmZ1bmN0aW9uKGEsYil7dmFyIGMsZCxmO3JldHVybiJzdHJpbmciPT10eXBlb2YgYiYmKGM9YVtiXSxiPWEsYT1jKSxuLmlzRnVuY3Rpb24oYSk/KGQ9ZS5jYWxsKGFyZ3VtZW50cywyKSxmPWZ1bmN0aW9uKCl7cmV0dXJuIGEuYXBwbHkoYnx8dGhpcyxkLmNvbmNhdChlLmNhbGwoYXJndW1lbnRzKSkpfSxmLmd1aWQ9YS5ndWlkPWEuZ3VpZHx8bi5ndWlkKyssZik6dm9pZCAwfSxub3c6RGF0ZS5ub3csc3VwcG9ydDpsfSksImZ1bmN0aW9uIj09dHlwZW9mIFN5bWJvbCYmKG4uZm5bU3ltYm9sLml0ZXJhdG9yXT1jW1N5bWJvbC5pdGVyYXRvcl0pLG4uZWFjaCgiQm9vbGVhbiBOdW1iZXIgU3RyaW5nIEZ1bmN0aW9uIEFycmF5IERhdGUgUmVnRXhwIE9iamVjdCBFcnJvciBTeW1ib2wiLnNwbGl0KCIgIiksZnVuY3Rpb24oYSxiKXtpWyJbb2JqZWN0ICIrYisiXSJdPWIudG9Mb3dlckNhc2UoKX0pO2Z1bmN0aW9uIHMoYSl7dmFyIGI9ISFhJiYibGVuZ3RoImluIGEmJmEubGVuZ3RoLGM9bi50eXBlKGEpO3JldHVybiJmdW5jdGlvbiI9PT1jfHxuLmlzV2luZG93KGEpPyExOiJhcnJheSI9PT1jfHwwPT09Ynx8Im51bWJlciI9PXR5cGVvZiBiJiZiPjAmJmItMSBpbiBhfXZhciB0PWZ1bmN0aW9uKGEpe3ZhciBiLGMsZCxlLGYsZyxoLGksaixrLGwsbSxuLG8scCxxLHIscyx0LHU9InNpenpsZSIrMSpuZXcgRGF0ZSx2PWEuZG9jdW1lbnQsdz0wLHg9MCx5PWdhKCksej1nYSgpLEE9Z2EoKSxCPWZ1bmN0aW9uKGEsYil7cmV0dXJuIGE9PT1iJiYobD0hMCksMH0sQz0xPDwzMSxEPXt9Lmhhc093blByb3BlcnR5LEU9W10sRj1FLnBvcCxHPUUucHVzaCxIPUUucHVzaCxJPUUuc2xpY2UsSj1mdW5jdGlvbihhLGIpe2Zvcih2YXIgYz0wLGQ9YS5sZW5ndGg7ZD5jO2MrKylpZihhW2NdPT09YilyZXR1cm4gYztyZXR1cm4tMX0sSz0iY2hlY2tlZHxzZWxlY3RlZHxhc3luY3xhdXRvZm9jdXN8YXV0b3BsYXl8Y29udHJvbHN8ZGVmZXJ8ZGlzYWJsZWR8aGlkZGVufGlzbWFwfGxvb3B8bXVsdGlwbGV8b3BlbnxyZWFkb25seXxyZXF1aXJlZHxzY29wZWQiLEw9IltcXHgyMFxcdFxcclxcblxcZl0iLE09Iig/OlxcXFwufFtcXHctXXxbXlxceDAwLVxceGEwXSkrIixOPSJcXFsiK0wrIiooIitNKyIpKD86IitMKyIqKFsqXiR8IX5dPz0pIitMKyIqKD86JygoPzpcXFxcLnxbXlxcXFwnXSkqKSd8XCIoKD86XFxcXC58W15cXFxcXCJdKSopXCJ8KCIrTSsiKSl8KSIrTCsiKlxcXSIsTz0iOigiK00rIikoPzpcXCgoKCcoKD86XFxcXC58W15cXFxcJ10pKiknfFwiKCg/OlxcXFwufFteXFxcXFwiXSkqKVwiKXwoKD86XFxcXC58W15cXFxcKClbXFxdXXwiK04rIikqKXwuKilcXCl8KSIsUD1uZXcgUmVnRXhwKEwrIisiLCJnIiksUT1uZXcgUmVnRXhwKCJeIitMKyIrfCgoPzpefFteXFxcXF0pKD86XFxcXC4pKikiK0wrIiskIiwiZyIpLFI9bmV3IFJlZ0V4cCgiXiIrTCsiKiwiK0wrIioiKSxTPW5ldyBSZWdFeHAoIl4iK0wrIiooWz4rfl18IitMKyIpIitMKyIqIiksVD1uZXcgUmVnRXhwKCI9IitMKyIqKFteXFxdJ1wiXSo/KSIrTCsiKlxcXSIsImciKSxVPW5ldyBSZWdFeHAoTyksVj1uZXcgUmVnRXhwKCJeIitNKyIkIiksVz17SUQ6bmV3IFJlZ0V4cCgiXiMoIitNKyIpIiksQ0xBU1M6bmV3IFJlZ0V4cCgiXlxcLigiK00rIikiKSxUQUc6bmV3IFJlZ0V4cCgiXigiK00rInxbKl0pIiksQVRUUjpuZXcgUmVnRXhwKCJeIitOKSxQU0VVRE86bmV3IFJlZ0V4cCgiXiIrTyksQ0hJTEQ6bmV3IFJlZ0V4cCgiXjoob25seXxmaXJzdHxsYXN0fG50aHxudGgtbGFzdCktKGNoaWxkfG9mLXR5cGUpKD86XFwoIitMKyIqKGV2ZW58b2RkfCgoWystXXwpKFxcZCopbnwpIitMKyIqKD86KFsrLV18KSIrTCsiKihcXGQrKXwpKSIrTCsiKlxcKXwpIiwiaSIpLGJvb2w6bmV3IFJlZ0V4cCgiXig/OiIrSysiKSQiLCJpIiksbmVlZHNDb250ZXh0Om5ldyBSZWdFeHAoIl4iK0wrIipbPit+XXw6KGV2ZW58b2RkfGVxfGd0fGx0fG50aHxmaXJzdHxsYXN0KSg/OlxcKCIrTCsiKigoPzotXFxkKT9cXGQqKSIrTCsiKlxcKXwpKD89W14tXXwkKSIsImkiKX0sWD0vXig/OmlucHV0fHNlbGVjdHx0ZXh0YXJlYXxidXR0b24pJC9pLFk9L15oXGQkL2ksWj0vXltee10rXHtccypcW25hdGl2ZSBcdy8sJD0vXig/OiMoW1x3LV0rKXwoXHcrKXxcLihbXHctXSspKSQvLF89L1srfl0vLGFhPS8nfFxcL2csYmE9bmV3IFJlZ0V4cCgiXFxcXChbXFxkYS1mXXsxLDZ9IitMKyI/fCgiK0wrIil8LikiLCJpZyIpLGNhPWZ1bmN0aW9uKGEsYixjKXt2YXIgZD0iMHgiK2ItNjU1MzY7cmV0dXJuIGQhPT1kfHxjP2I6MD5kP1N0cmluZy5mcm9tQ2hhckNvZGUoZCs2NTUzNik6U3RyaW5nLmZyb21DaGFyQ29kZShkPj4xMHw1NTI5NiwxMDIzJmR8NTYzMjApfSxkYT1mdW5jdGlvbigpe20oKX07dHJ5e0guYXBwbHkoRT1JLmNhbGwodi5jaGlsZE5vZGVzKSx2LmNoaWxkTm9kZXMpLEVbdi5jaGlsZE5vZGVzLmxlbmd0aF0ubm9kZVR5cGV9Y2F0Y2goZWEpe0g9e2FwcGx5OkUubGVuZ3RoP2Z1bmN0aW9uKGEsYil7Ry5hcHBseShhLEkuY2FsbChiKSl9OmZ1bmN0aW9uKGEsYil7dmFyIGM9YS5sZW5ndGgsZD0wO3doaWxlKGFbYysrXT1iW2QrK10pO2EubGVuZ3RoPWMtMX19fWZ1bmN0aW9uIGZhKGEsYixkLGUpe3ZhciBmLGgsaixrLGwsbyxyLHMsdz1iJiZiLm93bmVyRG9jdW1lbnQseD1iP2Iubm9kZVR5cGU6OTtpZihkPWR8fFtdLCJzdHJpbmciIT10eXBlb2YgYXx8IWF8fDEhPT14JiY5IT09eCYmMTEhPT14KXJldHVybiBkO2lmKCFlJiYoKGI/Yi5vd25lckRvY3VtZW50fHxiOnYpIT09biYmbShiKSxiPWJ8fG4scCkpe2lmKDExIT09eCYmKG89JC5leGVjKGEpKSlpZihmPW9bMV0pe2lmKDk9PT14KXtpZighKGo9Yi5nZXRFbGVtZW50QnlJZChmKSkpcmV0dXJuIGQ7aWYoai5pZD09PWYpcmV0dXJuIGQucHVzaChqKSxkfWVsc2UgaWYodyYmKGo9dy5nZXRFbGVtZW50QnlJZChmKSkmJnQoYixqKSYmai5pZD09PWYpcmV0dXJuIGQucHVzaChqKSxkfWVsc2V7aWYob1syXSlyZXR1cm4gSC5hcHBseShkLGIuZ2V0RWxlbWVudHNCeVRhZ05hbWUoYSkpLGQ7aWYoKGY9b1szXSkmJmMuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSYmYi5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKXJldHVybiBILmFwcGx5KGQsYi5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKGYpKSxkfWlmKGMucXNhJiYhQVthKyIgIl0mJighcXx8IXEudGVzdChhKSkpe2lmKDEhPT14KXc9YixzPWE7ZWxzZSBpZigib2JqZWN0IiE9PWIubm9kZU5hbWUudG9Mb3dlckNhc2UoKSl7KGs9Yi5nZXRBdHRyaWJ1dGUoImlkIikpP2s9ay5yZXBsYWNlKGFhLCJcXCQmIik6Yi5zZXRBdHRyaWJ1dGUoImlkIixrPXUpLHI9ZyhhKSxoPXIubGVuZ3RoLGw9Vi50ZXN0KGspPyIjIitrOiJbaWQ9JyIraysiJ10iO3doaWxlKGgtLSlyW2hdPWwrIiAiK3FhKHJbaF0pO3M9ci5qb2luKCIsIiksdz1fLnRlc3QoYSkmJm9hKGIucGFyZW50Tm9kZSl8fGJ9aWYocyl0cnl7cmV0dXJuIEguYXBwbHkoZCx3LnF1ZXJ5U2VsZWN0b3JBbGwocykpLGR9Y2F0Y2goeSl7fWZpbmFsbHl7az09PXUmJmIucmVtb3ZlQXR0cmlidXRlKCJpZCIpfX19cmV0dXJuIGkoYS5yZXBsYWNlKFEsIiQxIiksYixkLGUpfWZ1bmN0aW9uIGdhKCl7dmFyIGE9W107ZnVuY3Rpb24gYihjLGUpe3JldHVybiBhLnB1c2goYysiICIpPmQuY2FjaGVMZW5ndGgmJmRlbGV0ZSBiW2Euc2hpZnQoKV0sYltjKyIgIl09ZX1yZXR1cm4gYn1mdW5jdGlvbiBoYShhKXtyZXR1cm4gYVt1XT0hMCxhfWZ1bmN0aW9uIGlhKGEpe3ZhciBiPW4uY3JlYXRlRWxlbWVudCgiZGl2Iik7dHJ5e3JldHVybiEhYShiKX1jYXRjaChjKXtyZXR1cm4hMX1maW5hbGx5e2IucGFyZW50Tm9kZSYmYi5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGIpLGI9bnVsbH19ZnVuY3Rpb24gamEoYSxiKXt2YXIgYz1hLnNwbGl0KCJ8IiksZT1jLmxlbmd0aDt3aGlsZShlLS0pZC5hdHRySGFuZGxlW2NbZV1dPWJ9ZnVuY3Rpb24ga2EoYSxiKXt2YXIgYz1iJiZhLGQ9YyYmMT09PWEubm9kZVR5cGUmJjE9PT1iLm5vZGVUeXBlJiYofmIuc291cmNlSW5kZXh8fEMpLSh+YS5zb3VyY2VJbmRleHx8Qyk7aWYoZClyZXR1cm4gZDtpZihjKXdoaWxlKGM9Yy5uZXh0U2libGluZylpZihjPT09YilyZXR1cm4tMTtyZXR1cm4gYT8xOi0xfWZ1bmN0aW9uIGxhKGEpe3JldHVybiBmdW5jdGlvbihiKXt2YXIgYz1iLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk7cmV0dXJuImlucHV0Ij09PWMmJmIudHlwZT09PWF9fWZ1bmN0aW9uIG1hKGEpe3JldHVybiBmdW5jdGlvbihiKXt2YXIgYz1iLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk7cmV0dXJuKCJpbnB1dCI9PT1jfHwiYnV0dG9uIj09PWMpJiZiLnR5cGU9PT1hfX1mdW5jdGlvbiBuYShhKXtyZXR1cm4gaGEoZnVuY3Rpb24oYil7cmV0dXJuIGI9K2IsaGEoZnVuY3Rpb24oYyxkKXt2YXIgZSxmPWEoW10sYy5sZW5ndGgsYiksZz1mLmxlbmd0aDt3aGlsZShnLS0pY1tlPWZbZ11dJiYoY1tlXT0hKGRbZV09Y1tlXSkpfSl9KX1mdW5jdGlvbiBvYShhKXtyZXR1cm4gYSYmInVuZGVmaW5lZCIhPXR5cGVvZiBhLmdldEVsZW1lbnRzQnlUYWdOYW1lJiZhfWM9ZmEuc3VwcG9ydD17fSxmPWZhLmlzWE1MPWZ1bmN0aW9uKGEpe3ZhciBiPWEmJihhLm93bmVyRG9jdW1lbnR8fGEpLmRvY3VtZW50RWxlbWVudDtyZXR1cm4gYj8iSFRNTCIhPT1iLm5vZGVOYW1lOiExfSxtPWZhLnNldERvY3VtZW50PWZ1bmN0aW9uKGEpe3ZhciBiLGUsZz1hP2Eub3duZXJEb2N1bWVudHx8YTp2O3JldHVybiBnIT09biYmOT09PWcubm9kZVR5cGUmJmcuZG9jdW1lbnRFbGVtZW50PyhuPWcsbz1uLmRvY3VtZW50RWxlbWVudCxwPSFmKG4pLChlPW4uZGVmYXVsdFZpZXcpJiZlLnRvcCE9PWUmJihlLmFkZEV2ZW50TGlzdGVuZXI/ZS5hZGRFdmVudExpc3RlbmVyKCJ1bmxvYWQiLGRhLCExKTplLmF0dGFjaEV2ZW50JiZlLmF0dGFjaEV2ZW50KCJvbnVubG9hZCIsZGEpKSxjLmF0dHJpYnV0ZXM9aWEoZnVuY3Rpb24oYSl7cmV0dXJuIGEuY2xhc3NOYW1lPSJpIiwhYS5nZXRBdHRyaWJ1dGUoImNsYXNzTmFtZSIpfSksYy5nZXRFbGVtZW50c0J5VGFnTmFtZT1pYShmdW5jdGlvbihhKXtyZXR1cm4gYS5hcHBlbmRDaGlsZChuLmNyZWF0ZUNvbW1lbnQoIiIpKSwhYS5nZXRFbGVtZW50c0J5VGFnTmFtZSgiKiIpLmxlbmd0aH0pLGMuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZT1aLnRlc3Qobi5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKSxjLmdldEJ5SWQ9aWEoZnVuY3Rpb24oYSl7cmV0dXJuIG8uYXBwZW5kQ2hpbGQoYSkuaWQ9dSwhbi5nZXRFbGVtZW50c0J5TmFtZXx8IW4uZ2V0RWxlbWVudHNCeU5hbWUodSkubGVuZ3RofSksYy5nZXRCeUlkPyhkLmZpbmQuSUQ9ZnVuY3Rpb24oYSxiKXtpZigidW5kZWZpbmVkIiE9dHlwZW9mIGIuZ2V0RWxlbWVudEJ5SWQmJnApe3ZhciBjPWIuZ2V0RWxlbWVudEJ5SWQoYSk7cmV0dXJuIGM/W2NdOltdfX0sZC5maWx0ZXIuSUQ9ZnVuY3Rpb24oYSl7dmFyIGI9YS5yZXBsYWNlKGJhLGNhKTtyZXR1cm4gZnVuY3Rpb24oYSl7cmV0dXJuIGEuZ2V0QXR0cmlidXRlKCJpZCIpPT09Yn19KTooZGVsZXRlIGQuZmluZC5JRCxkLmZpbHRlci5JRD1mdW5jdGlvbihhKXt2YXIgYj1hLnJlcGxhY2UoYmEsY2EpO3JldHVybiBmdW5jdGlvbihhKXt2YXIgYz0idW5kZWZpbmVkIiE9dHlwZW9mIGEuZ2V0QXR0cmlidXRlTm9kZSYmYS5nZXRBdHRyaWJ1dGVOb2RlKCJpZCIpO3JldHVybiBjJiZjLnZhbHVlPT09Yn19KSxkLmZpbmQuVEFHPWMuZ2V0RWxlbWVudHNCeVRhZ05hbWU/ZnVuY3Rpb24oYSxiKXtyZXR1cm4idW5kZWZpbmVkIiE9dHlwZW9mIGIuZ2V0RWxlbWVudHNCeVRhZ05hbWU/Yi5nZXRFbGVtZW50c0J5VGFnTmFtZShhKTpjLnFzYT9iLnF1ZXJ5U2VsZWN0b3JBbGwoYSk6dm9pZCAwfTpmdW5jdGlvbihhLGIpe3ZhciBjLGQ9W10sZT0wLGY9Yi5nZXRFbGVtZW50c0J5VGFnTmFtZShhKTtpZigiKiI9PT1hKXt3aGlsZShjPWZbZSsrXSkxPT09Yy5ub2RlVHlwZSYmZC5wdXNoKGMpO3JldHVybiBkfXJldHVybiBmfSxkLmZpbmQuQ0xBU1M9Yy5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lJiZmdW5jdGlvbihhLGIpe3JldHVybiJ1bmRlZmluZWQiIT10eXBlb2YgYi5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lJiZwP2IuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZShhKTp2b2lkIDB9LHI9W10scT1bXSwoYy5xc2E9Wi50ZXN0KG4ucXVlcnlTZWxlY3RvckFsbCkpJiYoaWEoZnVuY3Rpb24oYSl7by5hcHBlbmRDaGlsZChhKS5pbm5lckhUTUw9IjxhIGlkPSciK3UrIic+PC9hPjxzZWxlY3QgaWQ9JyIrdSsiLVxyXFwnIG1zYWxsb3djYXB0dXJlPScnPjxvcHRpb24gc2VsZWN0ZWQ9Jyc+PC9vcHRpb24+PC9zZWxlY3Q+IixhLnF1ZXJ5U2VsZWN0b3JBbGwoIlttc2FsbG93Y2FwdHVyZV49JyddIikubGVuZ3RoJiZxLnB1c2goIlsqXiRdPSIrTCsiKig/OicnfFwiXCIpIiksYS5xdWVyeVNlbGVjdG9yQWxsKCJbc2VsZWN0ZWRdIikubGVuZ3RofHxxLnB1c2goIlxcWyIrTCsiKig/OnZhbHVlfCIrSysiKSIpLGEucXVlcnlTZWxlY3RvckFsbCgiW2lkfj0iK3UrIi1dIikubGVuZ3RofHxxLnB1c2goIn49IiksYS5xdWVyeVNlbGVjdG9yQWxsKCI6Y2hlY2tlZCIpLmxlbmd0aHx8cS5wdXNoKCI6Y2hlY2tlZCIpLGEucXVlcnlTZWxlY3RvckFsbCgiYSMiK3UrIisqIikubGVuZ3RofHxxLnB1c2goIi4jLitbK35dIil9KSxpYShmdW5jdGlvbihhKXt2YXIgYj1uLmNyZWF0ZUVsZW1lbnQoImlucHV0Iik7Yi5zZXRBdHRyaWJ1dGUoInR5cGUiLCJoaWRkZW4iKSxhLmFwcGVuZENoaWxkKGIpLnNldEF0dHJpYnV0ZSgibmFtZSIsIkQiKSxhLnF1ZXJ5U2VsZWN0b3JBbGwoIltuYW1lPWRdIikubGVuZ3RoJiZxLnB1c2goIm5hbWUiK0wrIipbKl4kfCF+XT89IiksYS5xdWVyeVNlbGVjdG9yQWxsKCI6ZW5hYmxlZCIpLmxlbmd0aHx8cS5wdXNoKCI6ZW5hYmxlZCIsIjpkaXNhYmxlZCIpLGEucXVlcnlTZWxlY3RvckFsbCgiKiw6eCIpLHEucHVzaCgiLC4qOiIpfSkpLChjLm1hdGNoZXNTZWxlY3Rvcj1aLnRlc3Qocz1vLm1hdGNoZXN8fG8ud2Via2l0TWF0Y2hlc1NlbGVjdG9yfHxvLm1vek1hdGNoZXNTZWxlY3Rvcnx8by5vTWF0Y2hlc1NlbGVjdG9yfHxvLm1zTWF0Y2hlc1NlbGVjdG9yKSkmJmlhKGZ1bmN0aW9uKGEpe2MuZGlzY29ubmVjdGVkTWF0Y2g9cy5jYWxsKGEsImRpdiIpLHMuY2FsbChhLCJbcyE9JyddOngiKSxyLnB1c2goIiE9IixPKX0pLHE9cS5sZW5ndGgmJm5ldyBSZWdFeHAocS5qb2luKCJ8IikpLHI9ci5sZW5ndGgmJm5ldyBSZWdFeHAoci5qb2luKCJ8IikpLGI9Wi50ZXN0KG8uY29tcGFyZURvY3VtZW50UG9zaXRpb24pLHQ9Ynx8Wi50ZXN0KG8uY29udGFpbnMpP2Z1bmN0aW9uKGEsYil7dmFyIGM9OT09PWEubm9kZVR5cGU/YS5kb2N1bWVudEVsZW1lbnQ6YSxkPWImJmIucGFyZW50Tm9kZTtyZXR1cm4gYT09PWR8fCEoIWR8fDEhPT1kLm5vZGVUeXBlfHwhKGMuY29udGFpbnM/Yy5jb250YWlucyhkKTphLmNvbXBhcmVEb2N1bWVudFBvc2l0aW9uJiYxNiZhLmNvbXBhcmVEb2N1bWVudFBvc2l0aW9uKGQpKSl9OmZ1bmN0aW9uKGEsYil7aWYoYil3aGlsZShiPWIucGFyZW50Tm9kZSlpZihiPT09YSlyZXR1cm4hMDtyZXR1cm4hMX0sQj1iP2Z1bmN0aW9uKGEsYil7aWYoYT09PWIpcmV0dXJuIGw9ITAsMDt2YXIgZD0hYS5jb21wYXJlRG9jdW1lbnRQb3NpdGlvbi0hYi5jb21wYXJlRG9jdW1lbnRQb3NpdGlvbjtyZXR1cm4gZD9kOihkPShhLm93bmVyRG9jdW1lbnR8fGEpPT09KGIub3duZXJEb2N1bWVudHx8Yik/YS5jb21wYXJlRG9jdW1lbnRQb3NpdGlvbihiKToxLDEmZHx8IWMuc29ydERldGFjaGVkJiZiLmNvbXBhcmVEb2N1bWVudFBvc2l0aW9uKGEpPT09ZD9hPT09bnx8YS5vd25lckRvY3VtZW50PT09diYmdCh2LGEpPy0xOmI9PT1ufHxiLm93bmVyRG9jdW1lbnQ9PT12JiZ0KHYsYik/MTprP0ooayxhKS1KKGssYik6MDo0JmQ/LTE6MSl9OmZ1bmN0aW9uKGEsYil7aWYoYT09PWIpcmV0dXJuIGw9ITAsMDt2YXIgYyxkPTAsZT1hLnBhcmVudE5vZGUsZj1iLnBhcmVudE5vZGUsZz1bYV0saD1bYl07aWYoIWV8fCFmKXJldHVybiBhPT09bj8tMTpiPT09bj8xOmU/LTE6Zj8xOms/SihrLGEpLUooayxiKTowO2lmKGU9PT1mKXJldHVybiBrYShhLGIpO2M9YTt3aGlsZShjPWMucGFyZW50Tm9kZSlnLnVuc2hpZnQoYyk7Yz1iO3doaWxlKGM9Yy5wYXJlbnROb2RlKWgudW5zaGlmdChjKTt3aGlsZShnW2RdPT09aFtkXSlkKys7cmV0dXJuIGQ/a2EoZ1tkXSxoW2RdKTpnW2RdPT09dj8tMTpoW2RdPT09dj8xOjB9LG4pOm59LGZhLm1hdGNoZXM9ZnVuY3Rpb24oYSxiKXtyZXR1cm4gZmEoYSxudWxsLG51bGwsYil9LGZhLm1hdGNoZXNTZWxlY3Rvcj1mdW5jdGlvbihhLGIpe2lmKChhLm93bmVyRG9jdW1lbnR8fGEpIT09biYmbShhKSxiPWIucmVwbGFjZShULCI9JyQxJ10iKSxjLm1hdGNoZXNTZWxlY3RvciYmcCYmIUFbYisiICJdJiYoIXJ8fCFyLnRlc3QoYikpJiYoIXF8fCFxLnRlc3QoYikpKXRyeXt2YXIgZD1zLmNhbGwoYSxiKTtpZihkfHxjLmRpc2Nvbm5lY3RlZE1hdGNofHxhLmRvY3VtZW50JiYxMSE9PWEuZG9jdW1lbnQubm9kZVR5cGUpcmV0dXJuIGR9Y2F0Y2goZSl7fXJldHVybiBmYShiLG4sbnVsbCxbYV0pLmxlbmd0aD4wfSxmYS5jb250YWlucz1mdW5jdGlvbihhLGIpe3JldHVybihhLm93bmVyRG9jdW1lbnR8fGEpIT09biYmbShhKSx0KGEsYil9LGZhLmF0dHI9ZnVuY3Rpb24oYSxiKXsoYS5vd25lckRvY3VtZW50fHxhKSE9PW4mJm0oYSk7dmFyIGU9ZC5hdHRySGFuZGxlW2IudG9Mb3dlckNhc2UoKV0sZj1lJiZELmNhbGwoZC5hdHRySGFuZGxlLGIudG9Mb3dlckNhc2UoKSk/ZShhLGIsIXApOnZvaWQgMDtyZXR1cm4gdm9pZCAwIT09Zj9mOmMuYXR0cmlidXRlc3x8IXA/YS5nZXRBdHRyaWJ1dGUoYik6KGY9YS5nZXRBdHRyaWJ1dGVOb2RlKGIpKSYmZi5zcGVjaWZpZWQ/Zi52YWx1ZTpudWxsfSxmYS5lcnJvcj1mdW5jdGlvbihhKXt0aHJvdyBuZXcgRXJyb3IoIlN5bnRheCBlcnJvciwgdW5yZWNvZ25pemVkIGV4cHJlc3Npb246ICIrYSl9LGZhLnVuaXF1ZVNvcnQ9ZnVuY3Rpb24oYSl7dmFyIGIsZD1bXSxlPTAsZj0wO2lmKGw9IWMuZGV0ZWN0RHVwbGljYXRlcyxrPSFjLnNvcnRTdGFibGUmJmEuc2xpY2UoMCksYS5zb3J0KEIpLGwpe3doaWxlKGI9YVtmKytdKWI9PT1hW2ZdJiYoZT1kLnB1c2goZikpO3doaWxlKGUtLSlhLnNwbGljZShkW2VdLDEpfXJldHVybiBrPW51bGwsYX0sZT1mYS5nZXRUZXh0PWZ1bmN0aW9uKGEpe3ZhciBiLGM9IiIsZD0wLGY9YS5ub2RlVHlwZTtpZihmKXtpZigxPT09Znx8OT09PWZ8fDExPT09Zil7aWYoInN0cmluZyI9PXR5cGVvZiBhLnRleHRDb250ZW50KXJldHVybiBhLnRleHRDb250ZW50O2ZvcihhPWEuZmlyc3RDaGlsZDthO2E9YS5uZXh0U2libGluZyljKz1lKGEpfWVsc2UgaWYoMz09PWZ8fDQ9PT1mKXJldHVybiBhLm5vZGVWYWx1ZX1lbHNlIHdoaWxlKGI9YVtkKytdKWMrPWUoYik7cmV0dXJuIGN9LGQ9ZmEuc2VsZWN0b3JzPXtjYWNoZUxlbmd0aDo1MCxjcmVhdGVQc2V1ZG86aGEsbWF0Y2g6VyxhdHRySGFuZGxlOnt9LGZpbmQ6e30scmVsYXRpdmU6eyI+Ijp7ZGlyOiJwYXJlbnROb2RlIixmaXJzdDohMH0sIiAiOntkaXI6InBhcmVudE5vZGUifSwiKyI6e2RpcjoicHJldmlvdXNTaWJsaW5nIixmaXJzdDohMH0sIn4iOntkaXI6InByZXZpb3VzU2libGluZyJ9fSxwcmVGaWx0ZXI6e0FUVFI6ZnVuY3Rpb24oYSl7cmV0dXJuIGFbMV09YVsxXS5yZXBsYWNlKGJhLGNhKSxhWzNdPShhWzNdfHxhWzRdfHxhWzVdfHwiIikucmVwbGFjZShiYSxjYSksIn49Ij09PWFbMl0mJihhWzNdPSIgIithWzNdKyIgIiksYS5zbGljZSgwLDQpfSxDSElMRDpmdW5jdGlvbihhKXtyZXR1cm4gYVsxXT1hWzFdLnRvTG93ZXJDYXNlKCksIm50aCI9PT1hWzFdLnNsaWNlKDAsMyk/KGFbM118fGZhLmVycm9yKGFbMF0pLGFbNF09KyhhWzRdP2FbNV0rKGFbNl18fDEpOjIqKCJldmVuIj09PWFbM118fCJvZGQiPT09YVszXSkpLGFbNV09KyhhWzddK2FbOF18fCJvZGQiPT09YVszXSkpOmFbM10mJmZhLmVycm9yKGFbMF0pLGF9LFBTRVVETzpmdW5jdGlvbihhKXt2YXIgYixjPSFhWzZdJiZhWzJdO3JldHVybiBXLkNISUxELnRlc3QoYVswXSk/bnVsbDooYVszXT9hWzJdPWFbNF18fGFbNV18fCIiOmMmJlUudGVzdChjKSYmKGI9ZyhjLCEwKSkmJihiPWMuaW5kZXhPZigiKSIsYy5sZW5ndGgtYiktYy5sZW5ndGgpJiYoYVswXT1hWzBdLnNsaWNlKDAsYiksYVsyXT1jLnNsaWNlKDAsYikpLGEuc2xpY2UoMCwzKSl9fSxmaWx0ZXI6e1RBRzpmdW5jdGlvbihhKXt2YXIgYj1hLnJlcGxhY2UoYmEsY2EpLnRvTG93ZXJDYXNlKCk7cmV0dXJuIioiPT09YT9mdW5jdGlvbigpe3JldHVybiEwfTpmdW5jdGlvbihhKXtyZXR1cm4gYS5ub2RlTmFtZSYmYS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpPT09Yn19LENMQVNTOmZ1bmN0aW9uKGEpe3ZhciBiPXlbYSsiICJdO3JldHVybiBifHwoYj1uZXcgUmVnRXhwKCIoXnwiK0wrIikiK2ErIigiK0wrInwkKSIpKSYmeShhLGZ1bmN0aW9uKGEpe3JldHVybiBiLnRlc3QoInN0cmluZyI9PXR5cGVvZiBhLmNsYXNzTmFtZSYmYS5jbGFzc05hbWV8fCJ1bmRlZmluZWQiIT10eXBlb2YgYS5nZXRBdHRyaWJ1dGUmJmEuZ2V0QXR0cmlidXRlKCJjbGFzcyIpfHwiIil9KX0sQVRUUjpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIGZ1bmN0aW9uKGQpe3ZhciBlPWZhLmF0dHIoZCxhKTtyZXR1cm4gbnVsbD09ZT8iIT0iPT09YjpiPyhlKz0iIiwiPSI9PT1iP2U9PT1jOiIhPSI9PT1iP2UhPT1jOiJePSI9PT1iP2MmJjA9PT1lLmluZGV4T2YoYyk6Iio9Ij09PWI/YyYmZS5pbmRleE9mKGMpPi0xOiIkPSI9PT1iP2MmJmUuc2xpY2UoLWMubGVuZ3RoKT09PWM6In49Ij09PWI/KCIgIitlLnJlcGxhY2UoUCwiICIpKyIgIikuaW5kZXhPZihjKT4tMToifD0iPT09Yj9lPT09Y3x8ZS5zbGljZSgwLGMubGVuZ3RoKzEpPT09YysiLSI6ITEpOiEwfX0sQ0hJTEQ6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgZj0ibnRoIiE9PWEuc2xpY2UoMCwzKSxnPSJsYXN0IiE9PWEuc2xpY2UoLTQpLGg9Im9mLXR5cGUiPT09YjtyZXR1cm4gMT09PWQmJjA9PT1lP2Z1bmN0aW9uKGEpe3JldHVybiEhYS5wYXJlbnROb2RlfTpmdW5jdGlvbihiLGMsaSl7dmFyIGosayxsLG0sbixvLHA9ZiE9PWc/Im5leHRTaWJsaW5nIjoicHJldmlvdXNTaWJsaW5nIixxPWIucGFyZW50Tm9kZSxyPWgmJmIubm9kZU5hbWUudG9Mb3dlckNhc2UoKSxzPSFpJiYhaCx0PSExO2lmKHEpe2lmKGYpe3doaWxlKHApe209Yjt3aGlsZShtPW1bcF0paWYoaD9tLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk9PT1yOjE9PT1tLm5vZGVUeXBlKXJldHVybiExO289cD0ib25seSI9PT1hJiYhbyYmIm5leHRTaWJsaW5nIn1yZXR1cm4hMH1pZihvPVtnP3EuZmlyc3RDaGlsZDpxLmxhc3RDaGlsZF0sZyYmcyl7bT1xLGw9bVt1XXx8KG1bdV09e30pLGs9bFttLnVuaXF1ZUlEXXx8KGxbbS51bmlxdWVJRF09e30pLGo9a1thXXx8W10sbj1qWzBdPT09dyYmalsxXSx0PW4mJmpbMl0sbT1uJiZxLmNoaWxkTm9kZXNbbl07d2hpbGUobT0rK24mJm0mJm1bcF18fCh0PW49MCl8fG8ucG9wKCkpaWYoMT09PW0ubm9kZVR5cGUmJisrdCYmbT09PWIpe2tbYV09W3csbix0XTticmVha319ZWxzZSBpZihzJiYobT1iLGw9bVt1XXx8KG1bdV09e30pLGs9bFttLnVuaXF1ZUlEXXx8KGxbbS51bmlxdWVJRF09e30pLGo9a1thXXx8W10sbj1qWzBdPT09dyYmalsxXSx0PW4pLHQ9PT0hMSl3aGlsZShtPSsrbiYmbSYmbVtwXXx8KHQ9bj0wKXx8by5wb3AoKSlpZigoaD9tLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk9PT1yOjE9PT1tLm5vZGVUeXBlKSYmKyt0JiYocyYmKGw9bVt1XXx8KG1bdV09e30pLGs9bFttLnVuaXF1ZUlEXXx8KGxbbS51bmlxdWVJRF09e30pLGtbYV09W3csdF0pLG09PT1iKSlicmVhaztyZXR1cm4gdC09ZSx0PT09ZHx8dCVkPT09MCYmdC9kPj0wfX19LFBTRVVETzpmdW5jdGlvbihhLGIpe3ZhciBjLGU9ZC5wc2V1ZG9zW2FdfHxkLnNldEZpbHRlcnNbYS50b0xvd2VyQ2FzZSgpXXx8ZmEuZXJyb3IoInVuc3VwcG9ydGVkIHBzZXVkbzogIithKTtyZXR1cm4gZVt1XT9lKGIpOmUubGVuZ3RoPjE/KGM9W2EsYSwiIixiXSxkLnNldEZpbHRlcnMuaGFzT3duUHJvcGVydHkoYS50b0xvd2VyQ2FzZSgpKT9oYShmdW5jdGlvbihhLGMpe3ZhciBkLGY9ZShhLGIpLGc9Zi5sZW5ndGg7d2hpbGUoZy0tKWQ9SihhLGZbZ10pLGFbZF09IShjW2RdPWZbZ10pfSk6ZnVuY3Rpb24oYSl7cmV0dXJuIGUoYSwwLGMpfSk6ZX19LHBzZXVkb3M6e25vdDpoYShmdW5jdGlvbihhKXt2YXIgYj1bXSxjPVtdLGQ9aChhLnJlcGxhY2UoUSwiJDEiKSk7cmV0dXJuIGRbdV0/aGEoZnVuY3Rpb24oYSxiLGMsZSl7dmFyIGYsZz1kKGEsbnVsbCxlLFtdKSxoPWEubGVuZ3RoO3doaWxlKGgtLSkoZj1nW2hdKSYmKGFbaF09IShiW2hdPWYpKX0pOmZ1bmN0aW9uKGEsZSxmKXtyZXR1cm4gYlswXT1hLGQoYixudWxsLGYsYyksYlswXT1udWxsLCFjLnBvcCgpfX0pLGhhczpoYShmdW5jdGlvbihhKXtyZXR1cm4gZnVuY3Rpb24oYil7cmV0dXJuIGZhKGEsYikubGVuZ3RoPjB9fSksY29udGFpbnM6aGEoZnVuY3Rpb24oYSl7cmV0dXJuIGE9YS5yZXBsYWNlKGJhLGNhKSxmdW5jdGlvbihiKXtyZXR1cm4oYi50ZXh0Q29udGVudHx8Yi5pbm5lclRleHR8fGUoYikpLmluZGV4T2YoYSk+LTF9fSksbGFuZzpoYShmdW5jdGlvbihhKXtyZXR1cm4gVi50ZXN0KGF8fCIiKXx8ZmEuZXJyb3IoInVuc3VwcG9ydGVkIGxhbmc6ICIrYSksYT1hLnJlcGxhY2UoYmEsY2EpLnRvTG93ZXJDYXNlKCksZnVuY3Rpb24oYil7dmFyIGM7ZG8gaWYoYz1wP2IubGFuZzpiLmdldEF0dHJpYnV0ZSgieG1sOmxhbmciKXx8Yi5nZXRBdHRyaWJ1dGUoImxhbmciKSlyZXR1cm4gYz1jLnRvTG93ZXJDYXNlKCksYz09PWF8fDA9PT1jLmluZGV4T2YoYSsiLSIpO3doaWxlKChiPWIucGFyZW50Tm9kZSkmJjE9PT1iLm5vZGVUeXBlKTtyZXR1cm4hMX19KSx0YXJnZXQ6ZnVuY3Rpb24oYil7dmFyIGM9YS5sb2NhdGlvbiYmYS5sb2NhdGlvbi5oYXNoO3JldHVybiBjJiZjLnNsaWNlKDEpPT09Yi5pZH0scm9vdDpmdW5jdGlvbihhKXtyZXR1cm4gYT09PW99LGZvY3VzOmZ1bmN0aW9uKGEpe3JldHVybiBhPT09bi5hY3RpdmVFbGVtZW50JiYoIW4uaGFzRm9jdXN8fG4uaGFzRm9jdXMoKSkmJiEhKGEudHlwZXx8YS5ocmVmfHx+YS50YWJJbmRleCl9LGVuYWJsZWQ6ZnVuY3Rpb24oYSl7cmV0dXJuIGEuZGlzYWJsZWQ9PT0hMX0sZGlzYWJsZWQ6ZnVuY3Rpb24oYSl7cmV0dXJuIGEuZGlzYWJsZWQ9PT0hMH0sY2hlY2tlZDpmdW5jdGlvbihhKXt2YXIgYj1hLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk7cmV0dXJuImlucHV0Ij09PWImJiEhYS5jaGVja2VkfHwib3B0aW9uIj09PWImJiEhYS5zZWxlY3RlZH0sc2VsZWN0ZWQ6ZnVuY3Rpb24oYSl7cmV0dXJuIGEucGFyZW50Tm9kZSYmYS5wYXJlbnROb2RlLnNlbGVjdGVkSW5kZXgsYS5zZWxlY3RlZD09PSEwfSxlbXB0eTpmdW5jdGlvbihhKXtmb3IoYT1hLmZpcnN0Q2hpbGQ7YTthPWEubmV4dFNpYmxpbmcpaWYoYS5ub2RlVHlwZTw2KXJldHVybiExO3JldHVybiEwfSxwYXJlbnQ6ZnVuY3Rpb24oYSl7cmV0dXJuIWQucHNldWRvcy5lbXB0eShhKX0saGVhZGVyOmZ1bmN0aW9uKGEpe3JldHVybiBZLnRlc3QoYS5ub2RlTmFtZSl9LGlucHV0OmZ1bmN0aW9uKGEpe3JldHVybiBYLnRlc3QoYS5ub2RlTmFtZSl9LGJ1dHRvbjpmdW5jdGlvbihhKXt2YXIgYj1hLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk7cmV0dXJuImlucHV0Ij09PWImJiJidXR0b24iPT09YS50eXBlfHwiYnV0dG9uIj09PWJ9LHRleHQ6ZnVuY3Rpb24oYSl7dmFyIGI7cmV0dXJuImlucHV0Ij09PWEubm9kZU5hbWUudG9Mb3dlckNhc2UoKSYmInRleHQiPT09YS50eXBlJiYobnVsbD09KGI9YS5nZXRBdHRyaWJ1dGUoInR5cGUiKSl8fCJ0ZXh0Ij09PWIudG9Mb3dlckNhc2UoKSl9LGZpcnN0Om5hKGZ1bmN0aW9uKCl7cmV0dXJuWzBdfSksbGFzdDpuYShmdW5jdGlvbihhLGIpe3JldHVybltiLTFdfSksZXE6bmEoZnVuY3Rpb24oYSxiLGMpe3JldHVyblswPmM/YytiOmNdfSksZXZlbjpuYShmdW5jdGlvbihhLGIpe2Zvcih2YXIgYz0wO2I+YztjKz0yKWEucHVzaChjKTtyZXR1cm4gYX0pLG9kZDpuYShmdW5jdGlvbihhLGIpe2Zvcih2YXIgYz0xO2I+YztjKz0yKWEucHVzaChjKTtyZXR1cm4gYX0pLGx0Om5hKGZ1bmN0aW9uKGEsYixjKXtmb3IodmFyIGQ9MD5jP2MrYjpjOy0tZD49MDspYS5wdXNoKGQpO3JldHVybiBhfSksZ3Q6bmEoZnVuY3Rpb24oYSxiLGMpe2Zvcih2YXIgZD0wPmM/YytiOmM7KytkPGI7KWEucHVzaChkKTtyZXR1cm4gYX0pfX0sZC5wc2V1ZG9zLm50aD1kLnBzZXVkb3MuZXE7Zm9yKGIgaW57cmFkaW86ITAsY2hlY2tib3g6ITAsZmlsZTohMCxwYXNzd29yZDohMCxpbWFnZTohMH0pZC5wc2V1ZG9zW2JdPWxhKGIpO2ZvcihiIGlue3N1Ym1pdDohMCxyZXNldDohMH0pZC5wc2V1ZG9zW2JdPW1hKGIpO2Z1bmN0aW9uIHBhKCl7fXBhLnByb3RvdHlwZT1kLmZpbHRlcnM9ZC5wc2V1ZG9zLGQuc2V0RmlsdGVycz1uZXcgcGEsZz1mYS50b2tlbml6ZT1mdW5jdGlvbihhLGIpe3ZhciBjLGUsZixnLGgsaSxqLGs9elthKyIgIl07aWYoaylyZXR1cm4gYj8wOmsuc2xpY2UoMCk7aD1hLGk9W10saj1kLnByZUZpbHRlcjt3aGlsZShoKXsoIWN8fChlPVIuZXhlYyhoKSkpJiYoZSYmKGg9aC5zbGljZShlWzBdLmxlbmd0aCl8fGgpLGkucHVzaChmPVtdKSksYz0hMSwoZT1TLmV4ZWMoaCkpJiYoYz1lLnNoaWZ0KCksZi5wdXNoKHt2YWx1ZTpjLHR5cGU6ZVswXS5yZXBsYWNlKFEsIiAiKX0pLGg9aC5zbGljZShjLmxlbmd0aCkpO2ZvcihnIGluIGQuZmlsdGVyKSEoZT1XW2ddLmV4ZWMoaCkpfHxqW2ddJiYhKGU9altnXShlKSl8fChjPWUuc2hpZnQoKSxmLnB1c2goe3ZhbHVlOmMsdHlwZTpnLG1hdGNoZXM6ZX0pLGg9aC5zbGljZShjLmxlbmd0aCkpO2lmKCFjKWJyZWFrfXJldHVybiBiP2gubGVuZ3RoOmg/ZmEuZXJyb3IoYSk6eihhLGkpLnNsaWNlKDApfTtmdW5jdGlvbiBxYShhKXtmb3IodmFyIGI9MCxjPWEubGVuZ3RoLGQ9IiI7Yz5iO2IrKylkKz1hW2JdLnZhbHVlO3JldHVybiBkfWZ1bmN0aW9uIHJhKGEsYixjKXt2YXIgZD1iLmRpcixlPWMmJiJwYXJlbnROb2RlIj09PWQsZj14Kys7cmV0dXJuIGIuZmlyc3Q/ZnVuY3Rpb24oYixjLGYpe3doaWxlKGI9YltkXSlpZigxPT09Yi5ub2RlVHlwZXx8ZSlyZXR1cm4gYShiLGMsZil9OmZ1bmN0aW9uKGIsYyxnKXt2YXIgaCxpLGosaz1bdyxmXTtpZihnKXt3aGlsZShiPWJbZF0paWYoKDE9PT1iLm5vZGVUeXBlfHxlKSYmYShiLGMsZykpcmV0dXJuITB9ZWxzZSB3aGlsZShiPWJbZF0paWYoMT09PWIubm9kZVR5cGV8fGUpe2lmKGo9Ylt1XXx8KGJbdV09e30pLGk9altiLnVuaXF1ZUlEXXx8KGpbYi51bmlxdWVJRF09e30pLChoPWlbZF0pJiZoWzBdPT09dyYmaFsxXT09PWYpcmV0dXJuIGtbMl09aFsyXTtpZihpW2RdPWssa1syXT1hKGIsYyxnKSlyZXR1cm4hMH19fWZ1bmN0aW9uIHNhKGEpe3JldHVybiBhLmxlbmd0aD4xP2Z1bmN0aW9uKGIsYyxkKXt2YXIgZT1hLmxlbmd0aDt3aGlsZShlLS0paWYoIWFbZV0oYixjLGQpKXJldHVybiExO3JldHVybiEwfTphWzBdfWZ1bmN0aW9uIHRhKGEsYixjKXtmb3IodmFyIGQ9MCxlPWIubGVuZ3RoO2U+ZDtkKyspZmEoYSxiW2RdLGMpO3JldHVybiBjfWZ1bmN0aW9uIHVhKGEsYixjLGQsZSl7Zm9yKHZhciBmLGc9W10saD0wLGk9YS5sZW5ndGgsaj1udWxsIT1iO2k+aDtoKyspKGY9YVtoXSkmJighY3x8YyhmLGQsZSkpJiYoZy5wdXNoKGYpLGomJmIucHVzaChoKSk7cmV0dXJuIGd9ZnVuY3Rpb24gdmEoYSxiLGMsZCxlLGYpe3JldHVybiBkJiYhZFt1XSYmKGQ9dmEoZCkpLGUmJiFlW3VdJiYoZT12YShlLGYpKSxoYShmdW5jdGlvbihmLGcsaCxpKXt2YXIgaixrLGwsbT1bXSxuPVtdLG89Zy5sZW5ndGgscD1mfHx0YShifHwiKiIsaC5ub2RlVHlwZT9baF06aCxbXSkscT0hYXx8IWYmJmI/cDp1YShwLG0sYSxoLGkpLHI9Yz9lfHwoZj9hOm98fGQpP1tdOmc6cTtpZihjJiZjKHEscixoLGkpLGQpe2o9dWEocixuKSxkKGosW10saCxpKSxrPWoubGVuZ3RoO3doaWxlKGstLSkobD1qW2tdKSYmKHJbbltrXV09IShxW25ba11dPWwpKX1pZihmKXtpZihlfHxhKXtpZihlKXtqPVtdLGs9ci5sZW5ndGg7d2hpbGUoay0tKShsPXJba10pJiZqLnB1c2gocVtrXT1sKTtlKG51bGwscj1bXSxqLGkpfWs9ci5sZW5ndGg7d2hpbGUoay0tKShsPXJba10pJiYoaj1lP0ooZixsKTptW2tdKT4tMSYmKGZbal09IShnW2pdPWwpKX19ZWxzZSByPXVhKHI9PT1nP3Iuc3BsaWNlKG8sci5sZW5ndGgpOnIpLGU/ZShudWxsLGcscixpKTpILmFwcGx5KGcscil9KX1mdW5jdGlvbiB3YShhKXtmb3IodmFyIGIsYyxlLGY9YS5sZW5ndGgsZz1kLnJlbGF0aXZlW2FbMF0udHlwZV0saD1nfHxkLnJlbGF0aXZlWyIgIl0saT1nPzE6MCxrPXJhKGZ1bmN0aW9uKGEpe3JldHVybiBhPT09Yn0saCwhMCksbD1yYShmdW5jdGlvbihhKXtyZXR1cm4gSihiLGEpPi0xfSxoLCEwKSxtPVtmdW5jdGlvbihhLGMsZCl7dmFyIGU9IWcmJihkfHxjIT09ail8fCgoYj1jKS5ub2RlVHlwZT9rKGEsYyxkKTpsKGEsYyxkKSk7cmV0dXJuIGI9bnVsbCxlfV07Zj5pO2krKylpZihjPWQucmVsYXRpdmVbYVtpXS50eXBlXSltPVtyYShzYShtKSxjKV07ZWxzZXtpZihjPWQuZmlsdGVyW2FbaV0udHlwZV0uYXBwbHkobnVsbCxhW2ldLm1hdGNoZXMpLGNbdV0pe2ZvcihlPSsraTtmPmU7ZSsrKWlmKGQucmVsYXRpdmVbYVtlXS50eXBlXSlicmVhaztyZXR1cm4gdmEoaT4xJiZzYShtKSxpPjEmJnFhKGEuc2xpY2UoMCxpLTEpLmNvbmNhdCh7dmFsdWU6IiAiPT09YVtpLTJdLnR5cGU/IioiOiIifSkpLnJlcGxhY2UoUSwiJDEiKSxjLGU+aSYmd2EoYS5zbGljZShpLGUpKSxmPmUmJndhKGE9YS5zbGljZShlKSksZj5lJiZxYShhKSl9bS5wdXNoKGMpfXJldHVybiBzYShtKX1mdW5jdGlvbiB4YShhLGIpe3ZhciBjPWIubGVuZ3RoPjAsZT1hLmxlbmd0aD4wLGY9ZnVuY3Rpb24oZixnLGgsaSxrKXt2YXIgbCxvLHEscj0wLHM9IjAiLHQ9ZiYmW10sdT1bXSx2PWoseD1mfHxlJiZkLmZpbmQuVEFHKCIqIixrKSx5PXcrPW51bGw9PXY/MTpNYXRoLnJhbmRvbSgpfHwuMSx6PXgubGVuZ3RoO2ZvcihrJiYoaj1nPT09bnx8Z3x8ayk7cyE9PXomJm51bGwhPShsPXhbc10pO3MrKyl7aWYoZSYmbCl7bz0wLGd8fGwub3duZXJEb2N1bWVudD09PW58fChtKGwpLGg9IXApO3doaWxlKHE9YVtvKytdKWlmKHEobCxnfHxuLGgpKXtpLnB1c2gobCk7YnJlYWt9ayYmKHc9eSl9YyYmKChsPSFxJiZsKSYmci0tLGYmJnQucHVzaChsKSl9aWYocis9cyxjJiZzIT09cil7bz0wO3doaWxlKHE9YltvKytdKXEodCx1LGcsaCk7aWYoZil7aWYocj4wKXdoaWxlKHMtLSl0W3NdfHx1W3NdfHwodVtzXT1GLmNhbGwoaSkpO3U9dWEodSl9SC5hcHBseShpLHUpLGsmJiFmJiZ1Lmxlbmd0aD4wJiZyK2IubGVuZ3RoPjEmJmZhLnVuaXF1ZVNvcnQoaSl9cmV0dXJuIGsmJih3PXksaj12KSx0fTtyZXR1cm4gYz9oYShmKTpmfXJldHVybiBoPWZhLmNvbXBpbGU9ZnVuY3Rpb24oYSxiKXt2YXIgYyxkPVtdLGU9W10sZj1BW2ErIiAiXTtpZighZil7Ynx8KGI9ZyhhKSksYz1iLmxlbmd0aDt3aGlsZShjLS0pZj13YShiW2NdKSxmW3VdP2QucHVzaChmKTplLnB1c2goZik7Zj1BKGEseGEoZSxkKSksZi5zZWxlY3Rvcj1hfXJldHVybiBmfSxpPWZhLnNlbGVjdD1mdW5jdGlvbihhLGIsZSxmKXt2YXIgaSxqLGssbCxtLG49ImZ1bmN0aW9uIj09dHlwZW9mIGEmJmEsbz0hZiYmZyhhPW4uc2VsZWN0b3J8fGEpO2lmKGU9ZXx8W10sMT09PW8ubGVuZ3RoKXtpZihqPW9bMF09b1swXS5zbGljZSgwKSxqLmxlbmd0aD4yJiYiSUQiPT09KGs9alswXSkudHlwZSYmYy5nZXRCeUlkJiY5PT09Yi5ub2RlVHlwZSYmcCYmZC5yZWxhdGl2ZVtqWzFdLnR5cGVdKXtpZihiPShkLmZpbmQuSUQoay5tYXRjaGVzWzBdLnJlcGxhY2UoYmEsY2EpLGIpfHxbXSlbMF0sIWIpcmV0dXJuIGU7biYmKGI9Yi5wYXJlbnROb2RlKSxhPWEuc2xpY2Uoai5zaGlmdCgpLnZhbHVlLmxlbmd0aCl9aT1XLm5lZWRzQ29udGV4dC50ZXN0KGEpPzA6ai5sZW5ndGg7d2hpbGUoaS0tKXtpZihrPWpbaV0sZC5yZWxhdGl2ZVtsPWsudHlwZV0pYnJlYWs7aWYoKG09ZC5maW5kW2xdKSYmKGY9bShrLm1hdGNoZXNbMF0ucmVwbGFjZShiYSxjYSksXy50ZXN0KGpbMF0udHlwZSkmJm9hKGIucGFyZW50Tm9kZSl8fGIpKSl7aWYoai5zcGxpY2UoaSwxKSxhPWYubGVuZ3RoJiZxYShqKSwhYSlyZXR1cm4gSC5hcHBseShlLGYpLGU7YnJlYWt9fX1yZXR1cm4obnx8aChhLG8pKShmLGIsIXAsZSwhYnx8Xy50ZXN0KGEpJiZvYShiLnBhcmVudE5vZGUpfHxiKSxlfSxjLnNvcnRTdGFibGU9dS5zcGxpdCgiIikuc29ydChCKS5qb2luKCIiKT09PXUsYy5kZXRlY3REdXBsaWNhdGVzPSEhbCxtKCksYy5zb3J0RGV0YWNoZWQ9aWEoZnVuY3Rpb24oYSl7cmV0dXJuIDEmYS5jb21wYXJlRG9jdW1lbnRQb3NpdGlvbihuLmNyZWF0ZUVsZW1lbnQoImRpdiIpKX0pLGlhKGZ1bmN0aW9uKGEpe3JldHVybiBhLmlubmVySFRNTD0iPGEgaHJlZj0nIyc+PC9hPiIsIiMiPT09YS5maXJzdENoaWxkLmdldEF0dHJpYnV0ZSgiaHJlZiIpfSl8fGphKCJ0eXBlfGhyZWZ8aGVpZ2h0fHdpZHRoIixmdW5jdGlvbihhLGIsYyl7cmV0dXJuIGM/dm9pZCAwOmEuZ2V0QXR0cmlidXRlKGIsInR5cGUiPT09Yi50b0xvd2VyQ2FzZSgpPzE6Mil9KSxjLmF0dHJpYnV0ZXMmJmlhKGZ1bmN0aW9uKGEpe3JldHVybiBhLmlubmVySFRNTD0iPGlucHV0Lz4iLGEuZmlyc3RDaGlsZC5zZXRBdHRyaWJ1dGUoInZhbHVlIiwiIiksIiI9PT1hLmZpcnN0Q2hpbGQuZ2V0QXR0cmlidXRlKCJ2YWx1ZSIpfSl8fGphKCJ2YWx1ZSIsZnVuY3Rpb24oYSxiLGMpe3JldHVybiBjfHwiaW5wdXQiIT09YS5ub2RlTmFtZS50b0xvd2VyQ2FzZSgpP3ZvaWQgMDphLmRlZmF1bHRWYWx1ZX0pLGlhKGZ1bmN0aW9uKGEpe3JldHVybiBudWxsPT1hLmdldEF0dHJpYnV0ZSgiZGlzYWJsZWQiKX0pfHxqYShLLGZ1bmN0aW9uKGEsYixjKXt2YXIgZDtyZXR1cm4gYz92b2lkIDA6YVtiXT09PSEwP2IudG9Mb3dlckNhc2UoKTooZD1hLmdldEF0dHJpYnV0ZU5vZGUoYikpJiZkLnNwZWNpZmllZD9kLnZhbHVlOm51bGx9KSxmYX0oYSk7bi5maW5kPXQsbi5leHByPXQuc2VsZWN0b3JzLG4uZXhwclsiOiJdPW4uZXhwci5wc2V1ZG9zLG4udW5pcXVlU29ydD1uLnVuaXF1ZT10LnVuaXF1ZVNvcnQsbi50ZXh0PXQuZ2V0VGV4dCxuLmlzWE1MRG9jPXQuaXNYTUwsbi5jb250YWlucz10LmNvbnRhaW5zO3ZhciB1PWZ1bmN0aW9uKGEsYixjKXt2YXIgZD1bXSxlPXZvaWQgMCE9PWM7d2hpbGUoKGE9YVtiXSkmJjkhPT1hLm5vZGVUeXBlKWlmKDE9PT1hLm5vZGVUeXBlKXtpZihlJiZuKGEpLmlzKGMpKWJyZWFrO2QucHVzaChhKX1yZXR1cm4gZH0sdj1mdW5jdGlvbihhLGIpe2Zvcih2YXIgYz1bXTthO2E9YS5uZXh0U2libGluZykxPT09YS5ub2RlVHlwZSYmYSE9PWImJmMucHVzaChhKTtyZXR1cm4gY30sdz1uLmV4cHIubWF0Y2gubmVlZHNDb250ZXh0LHg9L148KFtcdy1dKylccypcLz8+KD86PFwvXDE+fCkkLyx5PS9eLlteOiNcW1wuLF0qJC87ZnVuY3Rpb24geihhLGIsYyl7aWYobi5pc0Z1bmN0aW9uKGIpKXJldHVybiBuLmdyZXAoYSxmdW5jdGlvbihhLGQpe3JldHVybiEhYi5jYWxsKGEsZCxhKSE9PWN9KTtpZihiLm5vZGVUeXBlKXJldHVybiBuLmdyZXAoYSxmdW5jdGlvbihhKXtyZXR1cm4gYT09PWIhPT1jfSk7aWYoInN0cmluZyI9PXR5cGVvZiBiKXtpZih5LnRlc3QoYikpcmV0dXJuIG4uZmlsdGVyKGIsYSxjKTtiPW4uZmlsdGVyKGIsYSl9cmV0dXJuIG4uZ3JlcChhLGZ1bmN0aW9uKGEpe3JldHVybiBoLmNhbGwoYixhKT4tMSE9PWN9KX1uLmZpbHRlcj1mdW5jdGlvbihhLGIsYyl7dmFyIGQ9YlswXTtyZXR1cm4gYyYmKGE9Ijpub3QoIithKyIpIiksMT09PWIubGVuZ3RoJiYxPT09ZC5ub2RlVHlwZT9uLmZpbmQubWF0Y2hlc1NlbGVjdG9yKGQsYSk/W2RdOltdOm4uZmluZC5tYXRjaGVzKGEsbi5ncmVwKGIsZnVuY3Rpb24oYSl7cmV0dXJuIDE9PT1hLm5vZGVUeXBlfSkpfSxuLmZuLmV4dGVuZCh7ZmluZDpmdW5jdGlvbihhKXt2YXIgYixjPXRoaXMubGVuZ3RoLGQ9W10sZT10aGlzO2lmKCJzdHJpbmciIT10eXBlb2YgYSlyZXR1cm4gdGhpcy5wdXNoU3RhY2sobihhKS5maWx0ZXIoZnVuY3Rpb24oKXtmb3IoYj0wO2M+YjtiKyspaWYobi5jb250YWlucyhlW2JdLHRoaXMpKXJldHVybiEwfSkpO2ZvcihiPTA7Yz5iO2IrKyluLmZpbmQoYSxlW2JdLGQpO3JldHVybiBkPXRoaXMucHVzaFN0YWNrKGM+MT9uLnVuaXF1ZShkKTpkKSxkLnNlbGVjdG9yPXRoaXMuc2VsZWN0b3I/dGhpcy5zZWxlY3RvcisiICIrYTphLGR9LGZpbHRlcjpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5wdXNoU3RhY2soeih0aGlzLGF8fFtdLCExKSl9LG5vdDpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5wdXNoU3RhY2soeih0aGlzLGF8fFtdLCEwKSl9LGlzOmZ1bmN0aW9uKGEpe3JldHVybiEheih0aGlzLCJzdHJpbmciPT10eXBlb2YgYSYmdy50ZXN0KGEpP24oYSk6YXx8W10sITEpLmxlbmd0aH19KTt2YXIgQSxCPS9eKD86XHMqKDxbXHdcV10rPilbXj5dKnwjKFtcdy1dKikpJC8sQz1uLmZuLmluaXQ9ZnVuY3Rpb24oYSxiLGMpe3ZhciBlLGY7aWYoIWEpcmV0dXJuIHRoaXM7aWYoYz1jfHxBLCJzdHJpbmciPT10eXBlb2YgYSl7aWYoZT0iPCI9PT1hWzBdJiYiPiI9PT1hW2EubGVuZ3RoLTFdJiZhLmxlbmd0aD49Mz9bbnVsbCxhLG51bGxdOkIuZXhlYyhhKSwhZXx8IWVbMV0mJmIpcmV0dXJuIWJ8fGIuanF1ZXJ5PyhifHxjKS5maW5kKGEpOnRoaXMuY29uc3RydWN0b3IoYikuZmluZChhKTtpZihlWzFdKXtpZihiPWIgaW5zdGFuY2VvZiBuP2JbMF06YixuLm1lcmdlKHRoaXMsbi5wYXJzZUhUTUwoZVsxXSxiJiZiLm5vZGVUeXBlP2Iub3duZXJEb2N1bWVudHx8YjpkLCEwKSkseC50ZXN0KGVbMV0pJiZuLmlzUGxhaW5PYmplY3QoYikpZm9yKGUgaW4gYiluLmlzRnVuY3Rpb24odGhpc1tlXSk/dGhpc1tlXShiW2VdKTp0aGlzLmF0dHIoZSxiW2VdKTtyZXR1cm4gdGhpc31yZXR1cm4gZj1kLmdldEVsZW1lbnRCeUlkKGVbMl0pLGYmJmYucGFyZW50Tm9kZSYmKHRoaXMubGVuZ3RoPTEsdGhpc1swXT1mKSx0aGlzLmNvbnRleHQ9ZCx0aGlzLnNlbGVjdG9yPWEsdGhpc31yZXR1cm4gYS5ub2RlVHlwZT8odGhpcy5jb250ZXh0PXRoaXNbMF09YSx0aGlzLmxlbmd0aD0xLHRoaXMpOm4uaXNGdW5jdGlvbihhKT92b2lkIDAhPT1jLnJlYWR5P2MucmVhZHkoYSk6YShuKToodm9pZCAwIT09YS5zZWxlY3RvciYmKHRoaXMuc2VsZWN0b3I9YS5zZWxlY3Rvcix0aGlzLmNvbnRleHQ9YS5jb250ZXh0KSxuLm1ha2VBcnJheShhLHRoaXMpKX07Qy5wcm90b3R5cGU9bi5mbixBPW4oZCk7dmFyIEQ9L14oPzpwYXJlbnRzfHByZXYoPzpVbnRpbHxBbGwpKS8sRT17Y2hpbGRyZW46ITAsY29udGVudHM6ITAsbmV4dDohMCxwcmV2OiEwfTtuLmZuLmV4dGVuZCh7aGFzOmZ1bmN0aW9uKGEpe3ZhciBiPW4oYSx0aGlzKSxjPWIubGVuZ3RoO3JldHVybiB0aGlzLmZpbHRlcihmdW5jdGlvbigpe2Zvcih2YXIgYT0wO2M+YTthKyspaWYobi5jb250YWlucyh0aGlzLGJbYV0pKXJldHVybiEwfSl9LGNsb3Nlc3Q6ZnVuY3Rpb24oYSxiKXtmb3IodmFyIGMsZD0wLGU9dGhpcy5sZW5ndGgsZj1bXSxnPXcudGVzdChhKXx8InN0cmluZyIhPXR5cGVvZiBhP24oYSxifHx0aGlzLmNvbnRleHQpOjA7ZT5kO2QrKylmb3IoYz10aGlzW2RdO2MmJmMhPT1iO2M9Yy5wYXJlbnROb2RlKWlmKGMubm9kZVR5cGU8MTEmJihnP2cuaW5kZXgoYyk+LTE6MT09PWMubm9kZVR5cGUmJm4uZmluZC5tYXRjaGVzU2VsZWN0b3IoYyxhKSkpe2YucHVzaChjKTticmVha31yZXR1cm4gdGhpcy5wdXNoU3RhY2soZi5sZW5ndGg+MT9uLnVuaXF1ZVNvcnQoZik6Zil9LGluZGV4OmZ1bmN0aW9uKGEpe3JldHVybiBhPyJzdHJpbmciPT10eXBlb2YgYT9oLmNhbGwobihhKSx0aGlzWzBdKTpoLmNhbGwodGhpcyxhLmpxdWVyeT9hWzBdOmEpOnRoaXNbMF0mJnRoaXNbMF0ucGFyZW50Tm9kZT90aGlzLmZpcnN0KCkucHJldkFsbCgpLmxlbmd0aDotMX0sYWRkOmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMucHVzaFN0YWNrKG4udW5pcXVlU29ydChuLm1lcmdlKHRoaXMuZ2V0KCksbihhLGIpKSkpfSxhZGRCYWNrOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmFkZChudWxsPT1hP3RoaXMucHJldk9iamVjdDp0aGlzLnByZXZPYmplY3QuZmlsdGVyKGEpKX19KTtmdW5jdGlvbiBGKGEsYil7d2hpbGUoKGE9YVtiXSkmJjEhPT1hLm5vZGVUeXBlKTtyZXR1cm4gYX1uLmVhY2goe3BhcmVudDpmdW5jdGlvbihhKXt2YXIgYj1hLnBhcmVudE5vZGU7cmV0dXJuIGImJjExIT09Yi5ub2RlVHlwZT9iOm51bGx9LHBhcmVudHM6ZnVuY3Rpb24oYSl7cmV0dXJuIHUoYSwicGFyZW50Tm9kZSIpfSxwYXJlbnRzVW50aWw6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiB1KGEsInBhcmVudE5vZGUiLGMpfSxuZXh0OmZ1bmN0aW9uKGEpe3JldHVybiBGKGEsIm5leHRTaWJsaW5nIil9LHByZXY6ZnVuY3Rpb24oYSl7cmV0dXJuIEYoYSwicHJldmlvdXNTaWJsaW5nIil9LG5leHRBbGw6ZnVuY3Rpb24oYSl7cmV0dXJuIHUoYSwibmV4dFNpYmxpbmciKX0scHJldkFsbDpmdW5jdGlvbihhKXtyZXR1cm4gdShhLCJwcmV2aW91c1NpYmxpbmciKX0sbmV4dFVudGlsOmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gdShhLCJuZXh0U2libGluZyIsYyl9LHByZXZVbnRpbDpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIHUoYSwicHJldmlvdXNTaWJsaW5nIixjKX0sc2libGluZ3M6ZnVuY3Rpb24oYSl7cmV0dXJuIHYoKGEucGFyZW50Tm9kZXx8e30pLmZpcnN0Q2hpbGQsYSl9LGNoaWxkcmVuOmZ1bmN0aW9uKGEpe3JldHVybiB2KGEuZmlyc3RDaGlsZCl9LGNvbnRlbnRzOmZ1bmN0aW9uKGEpe3JldHVybiBhLmNvbnRlbnREb2N1bWVudHx8bi5tZXJnZShbXSxhLmNoaWxkTm9kZXMpfX0sZnVuY3Rpb24oYSxiKXtuLmZuW2FdPWZ1bmN0aW9uKGMsZCl7dmFyIGU9bi5tYXAodGhpcyxiLGMpO3JldHVybiJVbnRpbCIhPT1hLnNsaWNlKC01KSYmKGQ9YyksZCYmInN0cmluZyI9PXR5cGVvZiBkJiYoZT1uLmZpbHRlcihkLGUpKSx0aGlzLmxlbmd0aD4xJiYoRVthXXx8bi51bmlxdWVTb3J0KGUpLEQudGVzdChhKSYmZS5yZXZlcnNlKCkpLHRoaXMucHVzaFN0YWNrKGUpfX0pO3ZhciBHPS9cUysvZztmdW5jdGlvbiBIKGEpe3ZhciBiPXt9O3JldHVybiBuLmVhY2goYS5tYXRjaChHKXx8W10sZnVuY3Rpb24oYSxjKXtiW2NdPSEwfSksYn1uLkNhbGxiYWNrcz1mdW5jdGlvbihhKXthPSJzdHJpbmciPT10eXBlb2YgYT9IKGEpOm4uZXh0ZW5kKHt9LGEpO3ZhciBiLGMsZCxlLGY9W10sZz1bXSxoPS0xLGk9ZnVuY3Rpb24oKXtmb3IoZT1hLm9uY2UsZD1iPSEwO2cubGVuZ3RoO2g9LTEpe2M9Zy5zaGlmdCgpO3doaWxlKCsraDxmLmxlbmd0aClmW2hdLmFwcGx5KGNbMF0sY1sxXSk9PT0hMSYmYS5zdG9wT25GYWxzZSYmKGg9Zi5sZW5ndGgsYz0hMSl9YS5tZW1vcnl8fChjPSExKSxiPSExLGUmJihmPWM/W106IiIpfSxqPXthZGQ6ZnVuY3Rpb24oKXtyZXR1cm4gZiYmKGMmJiFiJiYoaD1mLmxlbmd0aC0xLGcucHVzaChjKSksZnVuY3Rpb24gZChiKXtuLmVhY2goYixmdW5jdGlvbihiLGMpe24uaXNGdW5jdGlvbihjKT9hLnVuaXF1ZSYmai5oYXMoYyl8fGYucHVzaChjKTpjJiZjLmxlbmd0aCYmInN0cmluZyIhPT1uLnR5cGUoYykmJmQoYyl9KX0oYXJndW1lbnRzKSxjJiYhYiYmaSgpKSx0aGlzfSxyZW1vdmU6ZnVuY3Rpb24oKXtyZXR1cm4gbi5lYWNoKGFyZ3VtZW50cyxmdW5jdGlvbihhLGIpe3ZhciBjO3doaWxlKChjPW4uaW5BcnJheShiLGYsYykpPi0xKWYuc3BsaWNlKGMsMSksaD49YyYmaC0tfSksdGhpc30saGFzOmZ1bmN0aW9uKGEpe3JldHVybiBhP24uaW5BcnJheShhLGYpPi0xOmYubGVuZ3RoPjB9LGVtcHR5OmZ1bmN0aW9uKCl7cmV0dXJuIGYmJihmPVtdKSx0aGlzfSxkaXNhYmxlOmZ1bmN0aW9uKCl7cmV0dXJuIGU9Zz1bXSxmPWM9IiIsdGhpc30sZGlzYWJsZWQ6ZnVuY3Rpb24oKXtyZXR1cm4hZn0sbG9jazpmdW5jdGlvbigpe3JldHVybiBlPWc9W10sY3x8KGY9Yz0iIiksdGhpc30sbG9ja2VkOmZ1bmN0aW9uKCl7cmV0dXJuISFlfSxmaXJlV2l0aDpmdW5jdGlvbihhLGMpe3JldHVybiBlfHwoYz1jfHxbXSxjPVthLGMuc2xpY2U/Yy5zbGljZSgpOmNdLGcucHVzaChjKSxifHxpKCkpLHRoaXN9LGZpcmU6ZnVuY3Rpb24oKXtyZXR1cm4gai5maXJlV2l0aCh0aGlzLGFyZ3VtZW50cyksdGhpc30sZmlyZWQ6ZnVuY3Rpb24oKXtyZXR1cm4hIWR9fTtyZXR1cm4gan0sbi5leHRlbmQoe0RlZmVycmVkOmZ1bmN0aW9uKGEpe3ZhciBiPVtbInJlc29sdmUiLCJkb25lIixuLkNhbGxiYWNrcygib25jZSBtZW1vcnkiKSwicmVzb2x2ZWQiXSxbInJlamVjdCIsImZhaWwiLG4uQ2FsbGJhY2tzKCJvbmNlIG1lbW9yeSIpLCJyZWplY3RlZCJdLFsibm90aWZ5IiwicHJvZ3Jlc3MiLG4uQ2FsbGJhY2tzKCJtZW1vcnkiKV1dLGM9InBlbmRpbmciLGQ9e3N0YXRlOmZ1bmN0aW9uKCl7cmV0dXJuIGN9LGFsd2F5czpmdW5jdGlvbigpe3JldHVybiBlLmRvbmUoYXJndW1lbnRzKS5mYWlsKGFyZ3VtZW50cyksdGhpc30sdGhlbjpmdW5jdGlvbigpe3ZhciBhPWFyZ3VtZW50cztyZXR1cm4gbi5EZWZlcnJlZChmdW5jdGlvbihjKXtuLmVhY2goYixmdW5jdGlvbihiLGYpe3ZhciBnPW4uaXNGdW5jdGlvbihhW2JdKSYmYVtiXTtlW2ZbMV1dKGZ1bmN0aW9uKCl7dmFyIGE9ZyYmZy5hcHBseSh0aGlzLGFyZ3VtZW50cyk7YSYmbi5pc0Z1bmN0aW9uKGEucHJvbWlzZSk/YS5wcm9taXNlKCkucHJvZ3Jlc3MoYy5ub3RpZnkpLmRvbmUoYy5yZXNvbHZlKS5mYWlsKGMucmVqZWN0KTpjW2ZbMF0rIldpdGgiXSh0aGlzPT09ZD9jLnByb21pc2UoKTp0aGlzLGc/W2FdOmFyZ3VtZW50cyl9KX0pLGE9bnVsbH0pLnByb21pc2UoKX0scHJvbWlzZTpmdW5jdGlvbihhKXtyZXR1cm4gbnVsbCE9YT9uLmV4dGVuZChhLGQpOmR9fSxlPXt9O3JldHVybiBkLnBpcGU9ZC50aGVuLG4uZWFjaChiLGZ1bmN0aW9uKGEsZil7dmFyIGc9ZlsyXSxoPWZbM107ZFtmWzFdXT1nLmFkZCxoJiZnLmFkZChmdW5jdGlvbigpe2M9aH0sYlsxXmFdWzJdLmRpc2FibGUsYlsyXVsyXS5sb2NrKSxlW2ZbMF1dPWZ1bmN0aW9uKCl7cmV0dXJuIGVbZlswXSsiV2l0aCJdKHRoaXM9PT1lP2Q6dGhpcyxhcmd1bWVudHMpLHRoaXN9LGVbZlswXSsiV2l0aCJdPWcuZmlyZVdpdGh9KSxkLnByb21pc2UoZSksYSYmYS5jYWxsKGUsZSksZX0sd2hlbjpmdW5jdGlvbihhKXt2YXIgYj0wLGM9ZS5jYWxsKGFyZ3VtZW50cyksZD1jLmxlbmd0aCxmPTEhPT1kfHxhJiZuLmlzRnVuY3Rpb24oYS5wcm9taXNlKT9kOjAsZz0xPT09Zj9hOm4uRGVmZXJyZWQoKSxoPWZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gZnVuY3Rpb24oZCl7YlthXT10aGlzLGNbYV09YXJndW1lbnRzLmxlbmd0aD4xP2UuY2FsbChhcmd1bWVudHMpOmQsYz09PWk/Zy5ub3RpZnlXaXRoKGIsYyk6LS1mfHxnLnJlc29sdmVXaXRoKGIsYyl9fSxpLGosaztpZihkPjEpZm9yKGk9bmV3IEFycmF5KGQpLGo9bmV3IEFycmF5KGQpLGs9bmV3IEFycmF5KGQpO2Q+YjtiKyspY1tiXSYmbi5pc0Z1bmN0aW9uKGNbYl0ucHJvbWlzZSk/Y1tiXS5wcm9taXNlKCkucHJvZ3Jlc3MoaChiLGosaSkpLmRvbmUoaChiLGssYykpLmZhaWwoZy5yZWplY3QpOi0tZjtyZXR1cm4gZnx8Zy5yZXNvbHZlV2l0aChrLGMpLGcucHJvbWlzZSgpfX0pO3ZhciBJO24uZm4ucmVhZHk9ZnVuY3Rpb24oYSl7cmV0dXJuIG4ucmVhZHkucHJvbWlzZSgpLmRvbmUoYSksdGhpc30sbi5leHRlbmQoe2lzUmVhZHk6ITEscmVhZHlXYWl0OjEsaG9sZFJlYWR5OmZ1bmN0aW9uKGEpe2E/bi5yZWFkeVdhaXQrKzpuLnJlYWR5KCEwKX0scmVhZHk6ZnVuY3Rpb24oYSl7KGE9PT0hMD8tLW4ucmVhZHlXYWl0Om4uaXNSZWFkeSl8fChuLmlzUmVhZHk9ITAsYSE9PSEwJiYtLW4ucmVhZHlXYWl0PjB8fChJLnJlc29sdmVXaXRoKGQsW25dKSxuLmZuLnRyaWdnZXJIYW5kbGVyJiYobihkKS50cmlnZ2VySGFuZGxlcigicmVhZHkiKSxuKGQpLm9mZigicmVhZHkiKSkpKX19KTtmdW5jdGlvbiBKKCl7ZC5yZW1vdmVFdmVudExpc3RlbmVyKCJET01Db250ZW50TG9hZGVkIixKKSxhLnJlbW92ZUV2ZW50TGlzdGVuZXIoImxvYWQiLEopLG4ucmVhZHkoKX1uLnJlYWR5LnByb21pc2U9ZnVuY3Rpb24oYil7cmV0dXJuIEl8fChJPW4uRGVmZXJyZWQoKSwiY29tcGxldGUiPT09ZC5yZWFkeVN0YXRlfHwibG9hZGluZyIhPT1kLnJlYWR5U3RhdGUmJiFkLmRvY3VtZW50RWxlbWVudC5kb1Njcm9sbD9hLnNldFRpbWVvdXQobi5yZWFkeSk6KGQuYWRkRXZlbnRMaXN0ZW5lcigiRE9NQ29udGVudExvYWRlZCIsSiksYS5hZGRFdmVudExpc3RlbmVyKCJsb2FkIixKKSkpLEkucHJvbWlzZShiKX0sbi5yZWFkeS5wcm9taXNlKCk7dmFyIEs9ZnVuY3Rpb24oYSxiLGMsZCxlLGYsZyl7dmFyIGg9MCxpPWEubGVuZ3RoLGo9bnVsbD09YztpZigib2JqZWN0Ij09PW4udHlwZShjKSl7ZT0hMDtmb3IoaCBpbiBjKUsoYSxiLGgsY1toXSwhMCxmLGcpfWVsc2UgaWYodm9pZCAwIT09ZCYmKGU9ITAsbi5pc0Z1bmN0aW9uKGQpfHwoZz0hMCksaiYmKGc/KGIuY2FsbChhLGQpLGI9bnVsbCk6KGo9YixiPWZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gai5jYWxsKG4oYSksYyl9KSksYikpZm9yKDtpPmg7aCsrKWIoYVtoXSxjLGc/ZDpkLmNhbGwoYVtoXSxoLGIoYVtoXSxjKSkpO3JldHVybiBlP2E6aj9iLmNhbGwoYSk6aT9iKGFbMF0sYyk6Zn0sTD1mdW5jdGlvbihhKXtyZXR1cm4gMT09PWEubm9kZVR5cGV8fDk9PT1hLm5vZGVUeXBlfHwhK2Eubm9kZVR5cGV9O2Z1bmN0aW9uIE0oKXt0aGlzLmV4cGFuZG89bi5leHBhbmRvK00udWlkKyt9TS51aWQ9MSxNLnByb3RvdHlwZT17cmVnaXN0ZXI6ZnVuY3Rpb24oYSxiKXt2YXIgYz1ifHx7fTtyZXR1cm4gYS5ub2RlVHlwZT9hW3RoaXMuZXhwYW5kb109YzpPYmplY3QuZGVmaW5lUHJvcGVydHkoYSx0aGlzLmV4cGFuZG8se3ZhbHVlOmMsd3JpdGFibGU6ITAsY29uZmlndXJhYmxlOiEwfSksYVt0aGlzLmV4cGFuZG9dfSxjYWNoZTpmdW5jdGlvbihhKXtpZighTChhKSlyZXR1cm57fTt2YXIgYj1hW3RoaXMuZXhwYW5kb107cmV0dXJuIGJ8fChiPXt9LEwoYSkmJihhLm5vZGVUeXBlP2FbdGhpcy5leHBhbmRvXT1iOk9iamVjdC5kZWZpbmVQcm9wZXJ0eShhLHRoaXMuZXhwYW5kbyx7dmFsdWU6Yixjb25maWd1cmFibGU6ITB9KSkpLGJ9LHNldDpmdW5jdGlvbihhLGIsYyl7dmFyIGQsZT10aGlzLmNhY2hlKGEpO2lmKCJzdHJpbmciPT10eXBlb2YgYillW2JdPWM7ZWxzZSBmb3IoZCBpbiBiKWVbZF09YltkXTtyZXR1cm4gZX0sZ2V0OmZ1bmN0aW9uKGEsYil7cmV0dXJuIHZvaWQgMD09PWI/dGhpcy5jYWNoZShhKTphW3RoaXMuZXhwYW5kb10mJmFbdGhpcy5leHBhbmRvXVtiXX0sYWNjZXNzOmZ1bmN0aW9uKGEsYixjKXt2YXIgZDtyZXR1cm4gdm9pZCAwPT09Ynx8YiYmInN0cmluZyI9PXR5cGVvZiBiJiZ2b2lkIDA9PT1jPyhkPXRoaXMuZ2V0KGEsYiksdm9pZCAwIT09ZD9kOnRoaXMuZ2V0KGEsbi5jYW1lbENhc2UoYikpKToodGhpcy5zZXQoYSxiLGMpLHZvaWQgMCE9PWM/YzpiKX0scmVtb3ZlOmZ1bmN0aW9uKGEsYil7dmFyIGMsZCxlLGY9YVt0aGlzLmV4cGFuZG9dO2lmKHZvaWQgMCE9PWYpe2lmKHZvaWQgMD09PWIpdGhpcy5yZWdpc3RlcihhKTtlbHNle24uaXNBcnJheShiKT9kPWIuY29uY2F0KGIubWFwKG4uY2FtZWxDYXNlKSk6KGU9bi5jYW1lbENhc2UoYiksYiBpbiBmP2Q9W2IsZV06KGQ9ZSxkPWQgaW4gZj9bZF06ZC5tYXRjaChHKXx8W10pKSxjPWQubGVuZ3RoO3doaWxlKGMtLSlkZWxldGUgZltkW2NdXX0odm9pZCAwPT09Ynx8bi5pc0VtcHR5T2JqZWN0KGYpKSYmKGEubm9kZVR5cGU/YVt0aGlzLmV4cGFuZG9dPXZvaWQgMDpkZWxldGUgYVt0aGlzLmV4cGFuZG9dKX19LGhhc0RhdGE6ZnVuY3Rpb24oYSl7dmFyIGI9YVt0aGlzLmV4cGFuZG9dO3JldHVybiB2b2lkIDAhPT1iJiYhbi5pc0VtcHR5T2JqZWN0KGIpfX07dmFyIE49bmV3IE0sTz1uZXcgTSxQPS9eKD86XHtbXHdcV10qXH18XFtbXHdcV10qXF0pJC8sUT0vW0EtWl0vZztmdW5jdGlvbiBSKGEsYixjKXt2YXIgZDtpZih2b2lkIDA9PT1jJiYxPT09YS5ub2RlVHlwZSlpZihkPSJkYXRhLSIrYi5yZXBsYWNlKFEsIi0kJiIpLnRvTG93ZXJDYXNlKCksYz1hLmdldEF0dHJpYnV0ZShkKSwic3RyaW5nIj09dHlwZW9mIGMpe3RyeXtjPSJ0cnVlIj09PWM/ITA6ImZhbHNlIj09PWM/ITE6Im51bGwiPT09Yz9udWxsOitjKyIiPT09Yz8rYzpQLnRlc3QoYyk/bi5wYXJzZUpTT04oYyk6Y31jYXRjaChlKXt9Ty5zZXQoYSxiLGMpOwp9ZWxzZSBjPXZvaWQgMDtyZXR1cm4gY31uLmV4dGVuZCh7aGFzRGF0YTpmdW5jdGlvbihhKXtyZXR1cm4gTy5oYXNEYXRhKGEpfHxOLmhhc0RhdGEoYSl9LGRhdGE6ZnVuY3Rpb24oYSxiLGMpe3JldHVybiBPLmFjY2VzcyhhLGIsYyl9LHJlbW92ZURhdGE6ZnVuY3Rpb24oYSxiKXtPLnJlbW92ZShhLGIpfSxfZGF0YTpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIE4uYWNjZXNzKGEsYixjKX0sX3JlbW92ZURhdGE6ZnVuY3Rpb24oYSxiKXtOLnJlbW92ZShhLGIpfX0pLG4uZm4uZXh0ZW5kKHtkYXRhOmZ1bmN0aW9uKGEsYil7dmFyIGMsZCxlLGY9dGhpc1swXSxnPWYmJmYuYXR0cmlidXRlcztpZih2b2lkIDA9PT1hKXtpZih0aGlzLmxlbmd0aCYmKGU9Ty5nZXQoZiksMT09PWYubm9kZVR5cGUmJiFOLmdldChmLCJoYXNEYXRhQXR0cnMiKSkpe2M9Zy5sZW5ndGg7d2hpbGUoYy0tKWdbY10mJihkPWdbY10ubmFtZSwwPT09ZC5pbmRleE9mKCJkYXRhLSIpJiYoZD1uLmNhbWVsQ2FzZShkLnNsaWNlKDUpKSxSKGYsZCxlW2RdKSkpO04uc2V0KGYsImhhc0RhdGFBdHRycyIsITApfXJldHVybiBlfXJldHVybiJvYmplY3QiPT10eXBlb2YgYT90aGlzLmVhY2goZnVuY3Rpb24oKXtPLnNldCh0aGlzLGEpfSk6Syh0aGlzLGZ1bmN0aW9uKGIpe3ZhciBjLGQ7aWYoZiYmdm9pZCAwPT09Yil7aWYoYz1PLmdldChmLGEpfHxPLmdldChmLGEucmVwbGFjZShRLCItJCYiKS50b0xvd2VyQ2FzZSgpKSx2b2lkIDAhPT1jKXJldHVybiBjO2lmKGQ9bi5jYW1lbENhc2UoYSksYz1PLmdldChmLGQpLHZvaWQgMCE9PWMpcmV0dXJuIGM7aWYoYz1SKGYsZCx2b2lkIDApLHZvaWQgMCE9PWMpcmV0dXJuIGN9ZWxzZSBkPW4uY2FtZWxDYXNlKGEpLHRoaXMuZWFjaChmdW5jdGlvbigpe3ZhciBjPU8uZ2V0KHRoaXMsZCk7Ty5zZXQodGhpcyxkLGIpLGEuaW5kZXhPZigiLSIpPi0xJiZ2b2lkIDAhPT1jJiZPLnNldCh0aGlzLGEsYil9KX0sbnVsbCxiLGFyZ3VtZW50cy5sZW5ndGg+MSxudWxsLCEwKX0scmVtb3ZlRGF0YTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKCl7Ty5yZW1vdmUodGhpcyxhKX0pfX0pLG4uZXh0ZW5kKHtxdWV1ZTpmdW5jdGlvbihhLGIsYyl7dmFyIGQ7cmV0dXJuIGE/KGI9KGJ8fCJmeCIpKyJxdWV1ZSIsZD1OLmdldChhLGIpLGMmJighZHx8bi5pc0FycmF5KGMpP2Q9Ti5hY2Nlc3MoYSxiLG4ubWFrZUFycmF5KGMpKTpkLnB1c2goYykpLGR8fFtdKTp2b2lkIDB9LGRlcXVldWU6ZnVuY3Rpb24oYSxiKXtiPWJ8fCJmeCI7dmFyIGM9bi5xdWV1ZShhLGIpLGQ9Yy5sZW5ndGgsZT1jLnNoaWZ0KCksZj1uLl9xdWV1ZUhvb2tzKGEsYiksZz1mdW5jdGlvbigpe24uZGVxdWV1ZShhLGIpfTsiaW5wcm9ncmVzcyI9PT1lJiYoZT1jLnNoaWZ0KCksZC0tKSxlJiYoImZ4Ij09PWImJmMudW5zaGlmdCgiaW5wcm9ncmVzcyIpLGRlbGV0ZSBmLnN0b3AsZS5jYWxsKGEsZyxmKSksIWQmJmYmJmYuZW1wdHkuZmlyZSgpfSxfcXVldWVIb29rczpmdW5jdGlvbihhLGIpe3ZhciBjPWIrInF1ZXVlSG9va3MiO3JldHVybiBOLmdldChhLGMpfHxOLmFjY2VzcyhhLGMse2VtcHR5Om4uQ2FsbGJhY2tzKCJvbmNlIG1lbW9yeSIpLmFkZChmdW5jdGlvbigpe04ucmVtb3ZlKGEsW2IrInF1ZXVlIixjXSl9KX0pfX0pLG4uZm4uZXh0ZW5kKHtxdWV1ZTpmdW5jdGlvbihhLGIpe3ZhciBjPTI7cmV0dXJuInN0cmluZyIhPXR5cGVvZiBhJiYoYj1hLGE9ImZ4IixjLS0pLGFyZ3VtZW50cy5sZW5ndGg8Yz9uLnF1ZXVlKHRoaXNbMF0sYSk6dm9pZCAwPT09Yj90aGlzOnRoaXMuZWFjaChmdW5jdGlvbigpe3ZhciBjPW4ucXVldWUodGhpcyxhLGIpO24uX3F1ZXVlSG9va3ModGhpcyxhKSwiZngiPT09YSYmImlucHJvZ3Jlc3MiIT09Y1swXSYmbi5kZXF1ZXVlKHRoaXMsYSl9KX0sZGVxdWV1ZTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKCl7bi5kZXF1ZXVlKHRoaXMsYSl9KX0sY2xlYXJRdWV1ZTpmdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5xdWV1ZShhfHwiZngiLFtdKX0scHJvbWlzZTpmdW5jdGlvbihhLGIpe3ZhciBjLGQ9MSxlPW4uRGVmZXJyZWQoKSxmPXRoaXMsZz10aGlzLmxlbmd0aCxoPWZ1bmN0aW9uKCl7LS1kfHxlLnJlc29sdmVXaXRoKGYsW2ZdKX07InN0cmluZyIhPXR5cGVvZiBhJiYoYj1hLGE9dm9pZCAwKSxhPWF8fCJmeCI7d2hpbGUoZy0tKWM9Ti5nZXQoZltnXSxhKyJxdWV1ZUhvb2tzIiksYyYmYy5lbXB0eSYmKGQrKyxjLmVtcHR5LmFkZChoKSk7cmV0dXJuIGgoKSxlLnByb21pc2UoYil9fSk7dmFyIFM9L1srLV0/KD86XGQqXC58KVxkKyg/OltlRV1bKy1dP1xkK3wpLy5zb3VyY2UsVD1uZXcgUmVnRXhwKCJeKD86KFsrLV0pPXwpKCIrUysiKShbYS16JV0qKSQiLCJpIiksVT1bIlRvcCIsIlJpZ2h0IiwiQm90dG9tIiwiTGVmdCJdLFY9ZnVuY3Rpb24oYSxiKXtyZXR1cm4gYT1ifHxhLCJub25lIj09PW4uY3NzKGEsImRpc3BsYXkiKXx8IW4uY29udGFpbnMoYS5vd25lckRvY3VtZW50LGEpfTtmdW5jdGlvbiBXKGEsYixjLGQpe3ZhciBlLGY9MSxnPTIwLGg9ZD9mdW5jdGlvbigpe3JldHVybiBkLmN1cigpfTpmdW5jdGlvbigpe3JldHVybiBuLmNzcyhhLGIsIiIpfSxpPWgoKSxqPWMmJmNbM118fChuLmNzc051bWJlcltiXT8iIjoicHgiKSxrPShuLmNzc051bWJlcltiXXx8InB4IiE9PWomJitpKSYmVC5leGVjKG4uY3NzKGEsYikpO2lmKGsmJmtbM10hPT1qKXtqPWp8fGtbM10sYz1jfHxbXSxrPStpfHwxO2RvIGY9Znx8Ii41IixrLz1mLG4uc3R5bGUoYSxiLGsraik7d2hpbGUoZiE9PShmPWgoKS9pKSYmMSE9PWYmJi0tZyl9cmV0dXJuIGMmJihrPStrfHwraXx8MCxlPWNbMV0/aysoY1sxXSsxKSpjWzJdOitjWzJdLGQmJihkLnVuaXQ9aixkLnN0YXJ0PWssZC5lbmQ9ZSkpLGV9dmFyIFg9L14oPzpjaGVja2JveHxyYWRpbykkL2ksWT0vPChbXHc6LV0rKS8sWj0vXiR8XC8oPzpqYXZhfGVjbWEpc2NyaXB0L2ksJD17b3B0aW9uOlsxLCI8c2VsZWN0IG11bHRpcGxlPSdtdWx0aXBsZSc+IiwiPC9zZWxlY3Q+Il0sdGhlYWQ6WzEsIjx0YWJsZT4iLCI8L3RhYmxlPiJdLGNvbDpbMiwiPHRhYmxlPjxjb2xncm91cD4iLCI8L2NvbGdyb3VwPjwvdGFibGU+Il0sdHI6WzIsIjx0YWJsZT48dGJvZHk+IiwiPC90Ym9keT48L3RhYmxlPiJdLHRkOlszLCI8dGFibGU+PHRib2R5Pjx0cj4iLCI8L3RyPjwvdGJvZHk+PC90YWJsZT4iXSxfZGVmYXVsdDpbMCwiIiwiIl19OyQub3B0Z3JvdXA9JC5vcHRpb24sJC50Ym9keT0kLnRmb290PSQuY29sZ3JvdXA9JC5jYXB0aW9uPSQudGhlYWQsJC50aD0kLnRkO2Z1bmN0aW9uIF8oYSxiKXt2YXIgYz0idW5kZWZpbmVkIiE9dHlwZW9mIGEuZ2V0RWxlbWVudHNCeVRhZ05hbWU/YS5nZXRFbGVtZW50c0J5VGFnTmFtZShifHwiKiIpOiJ1bmRlZmluZWQiIT10eXBlb2YgYS5xdWVyeVNlbGVjdG9yQWxsP2EucXVlcnlTZWxlY3RvckFsbChifHwiKiIpOltdO3JldHVybiB2b2lkIDA9PT1ifHxiJiZuLm5vZGVOYW1lKGEsYik/bi5tZXJnZShbYV0sYyk6Y31mdW5jdGlvbiBhYShhLGIpe2Zvcih2YXIgYz0wLGQ9YS5sZW5ndGg7ZD5jO2MrKylOLnNldChhW2NdLCJnbG9iYWxFdmFsIiwhYnx8Ti5nZXQoYltjXSwiZ2xvYmFsRXZhbCIpKX12YXIgYmE9Lzx8JiM/XHcrOy87ZnVuY3Rpb24gY2EoYSxiLGMsZCxlKXtmb3IodmFyIGYsZyxoLGksaixrLGw9Yi5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCksbT1bXSxvPTAscD1hLmxlbmd0aDtwPm87bysrKWlmKGY9YVtvXSxmfHwwPT09ZilpZigib2JqZWN0Ij09PW4udHlwZShmKSluLm1lcmdlKG0sZi5ub2RlVHlwZT9bZl06Zik7ZWxzZSBpZihiYS50ZXN0KGYpKXtnPWd8fGwuYXBwZW5kQ2hpbGQoYi5jcmVhdGVFbGVtZW50KCJkaXYiKSksaD0oWS5leGVjKGYpfHxbIiIsIiJdKVsxXS50b0xvd2VyQ2FzZSgpLGk9JFtoXXx8JC5fZGVmYXVsdCxnLmlubmVySFRNTD1pWzFdK24uaHRtbFByZWZpbHRlcihmKStpWzJdLGs9aVswXTt3aGlsZShrLS0pZz1nLmxhc3RDaGlsZDtuLm1lcmdlKG0sZy5jaGlsZE5vZGVzKSxnPWwuZmlyc3RDaGlsZCxnLnRleHRDb250ZW50PSIifWVsc2UgbS5wdXNoKGIuY3JlYXRlVGV4dE5vZGUoZikpO2wudGV4dENvbnRlbnQ9IiIsbz0wO3doaWxlKGY9bVtvKytdKWlmKGQmJm4uaW5BcnJheShmLGQpPi0xKWUmJmUucHVzaChmKTtlbHNlIGlmKGo9bi5jb250YWlucyhmLm93bmVyRG9jdW1lbnQsZiksZz1fKGwuYXBwZW5kQ2hpbGQoZiksInNjcmlwdCIpLGomJmFhKGcpLGMpe2s9MDt3aGlsZShmPWdbaysrXSlaLnRlc3QoZi50eXBlfHwiIikmJmMucHVzaChmKX1yZXR1cm4gbH0hZnVuY3Rpb24oKXt2YXIgYT1kLmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKSxiPWEuYXBwZW5kQ2hpbGQoZC5jcmVhdGVFbGVtZW50KCJkaXYiKSksYz1kLmNyZWF0ZUVsZW1lbnQoImlucHV0Iik7Yy5zZXRBdHRyaWJ1dGUoInR5cGUiLCJyYWRpbyIpLGMuc2V0QXR0cmlidXRlKCJjaGVja2VkIiwiY2hlY2tlZCIpLGMuc2V0QXR0cmlidXRlKCJuYW1lIiwidCIpLGIuYXBwZW5kQ2hpbGQoYyksbC5jaGVja0Nsb25lPWIuY2xvbmVOb2RlKCEwKS5jbG9uZU5vZGUoITApLmxhc3RDaGlsZC5jaGVja2VkLGIuaW5uZXJIVE1MPSI8dGV4dGFyZWE+eDwvdGV4dGFyZWE+IixsLm5vQ2xvbmVDaGVja2VkPSEhYi5jbG9uZU5vZGUoITApLmxhc3RDaGlsZC5kZWZhdWx0VmFsdWV9KCk7dmFyIGRhPS9ea2V5LyxlYT0vXig/Om1vdXNlfHBvaW50ZXJ8Y29udGV4dG1lbnV8ZHJhZ3xkcm9wKXxjbGljay8sZmE9L14oW14uXSopKD86XC4oLispfCkvO2Z1bmN0aW9uIGdhKCl7cmV0dXJuITB9ZnVuY3Rpb24gaGEoKXtyZXR1cm4hMX1mdW5jdGlvbiBpYSgpe3RyeXtyZXR1cm4gZC5hY3RpdmVFbGVtZW50fWNhdGNoKGEpe319ZnVuY3Rpb24gamEoYSxiLGMsZCxlLGYpe3ZhciBnLGg7aWYoIm9iamVjdCI9PXR5cGVvZiBiKXsic3RyaW5nIiE9dHlwZW9mIGMmJihkPWR8fGMsYz12b2lkIDApO2ZvcihoIGluIGIpamEoYSxoLGMsZCxiW2hdLGYpO3JldHVybiBhfWlmKG51bGw9PWQmJm51bGw9PWU/KGU9YyxkPWM9dm9pZCAwKTpudWxsPT1lJiYoInN0cmluZyI9PXR5cGVvZiBjPyhlPWQsZD12b2lkIDApOihlPWQsZD1jLGM9dm9pZCAwKSksZT09PSExKWU9aGE7ZWxzZSBpZighZSlyZXR1cm4gdGhpcztyZXR1cm4gMT09PWYmJihnPWUsZT1mdW5jdGlvbihhKXtyZXR1cm4gbigpLm9mZihhKSxnLmFwcGx5KHRoaXMsYXJndW1lbnRzKX0sZS5ndWlkPWcuZ3VpZHx8KGcuZ3VpZD1uLmd1aWQrKykpLGEuZWFjaChmdW5jdGlvbigpe24uZXZlbnQuYWRkKHRoaXMsYixlLGQsYyl9KX1uLmV2ZW50PXtnbG9iYWw6e30sYWRkOmZ1bmN0aW9uKGEsYixjLGQsZSl7dmFyIGYsZyxoLGksaixrLGwsbSxvLHAscSxyPU4uZ2V0KGEpO2lmKHIpe2MuaGFuZGxlciYmKGY9YyxjPWYuaGFuZGxlcixlPWYuc2VsZWN0b3IpLGMuZ3VpZHx8KGMuZ3VpZD1uLmd1aWQrKyksKGk9ci5ldmVudHMpfHwoaT1yLmV2ZW50cz17fSksKGc9ci5oYW5kbGUpfHwoZz1yLmhhbmRsZT1mdW5jdGlvbihiKXtyZXR1cm4idW5kZWZpbmVkIiE9dHlwZW9mIG4mJm4uZXZlbnQudHJpZ2dlcmVkIT09Yi50eXBlP24uZXZlbnQuZGlzcGF0Y2guYXBwbHkoYSxhcmd1bWVudHMpOnZvaWQgMH0pLGI9KGJ8fCIiKS5tYXRjaChHKXx8WyIiXSxqPWIubGVuZ3RoO3doaWxlKGotLSloPWZhLmV4ZWMoYltqXSl8fFtdLG89cT1oWzFdLHA9KGhbMl18fCIiKS5zcGxpdCgiLiIpLnNvcnQoKSxvJiYobD1uLmV2ZW50LnNwZWNpYWxbb118fHt9LG89KGU/bC5kZWxlZ2F0ZVR5cGU6bC5iaW5kVHlwZSl8fG8sbD1uLmV2ZW50LnNwZWNpYWxbb118fHt9LGs9bi5leHRlbmQoe3R5cGU6byxvcmlnVHlwZTpxLGRhdGE6ZCxoYW5kbGVyOmMsZ3VpZDpjLmd1aWQsc2VsZWN0b3I6ZSxuZWVkc0NvbnRleHQ6ZSYmbi5leHByLm1hdGNoLm5lZWRzQ29udGV4dC50ZXN0KGUpLG5hbWVzcGFjZTpwLmpvaW4oIi4iKX0sZiksKG09aVtvXSl8fChtPWlbb109W10sbS5kZWxlZ2F0ZUNvdW50PTAsbC5zZXR1cCYmbC5zZXR1cC5jYWxsKGEsZCxwLGcpIT09ITF8fGEuYWRkRXZlbnRMaXN0ZW5lciYmYS5hZGRFdmVudExpc3RlbmVyKG8sZykpLGwuYWRkJiYobC5hZGQuY2FsbChhLGspLGsuaGFuZGxlci5ndWlkfHwoay5oYW5kbGVyLmd1aWQ9Yy5ndWlkKSksZT9tLnNwbGljZShtLmRlbGVnYXRlQ291bnQrKywwLGspOm0ucHVzaChrKSxuLmV2ZW50Lmdsb2JhbFtvXT0hMCl9fSxyZW1vdmU6ZnVuY3Rpb24oYSxiLGMsZCxlKXt2YXIgZixnLGgsaSxqLGssbCxtLG8scCxxLHI9Ti5oYXNEYXRhKGEpJiZOLmdldChhKTtpZihyJiYoaT1yLmV2ZW50cykpe2I9KGJ8fCIiKS5tYXRjaChHKXx8WyIiXSxqPWIubGVuZ3RoO3doaWxlKGotLSlpZihoPWZhLmV4ZWMoYltqXSl8fFtdLG89cT1oWzFdLHA9KGhbMl18fCIiKS5zcGxpdCgiLiIpLnNvcnQoKSxvKXtsPW4uZXZlbnQuc3BlY2lhbFtvXXx8e30sbz0oZD9sLmRlbGVnYXRlVHlwZTpsLmJpbmRUeXBlKXx8byxtPWlbb118fFtdLGg9aFsyXSYmbmV3IFJlZ0V4cCgiKF58XFwuKSIrcC5qb2luKCJcXC4oPzouKlxcLnwpIikrIihcXC58JCkiKSxnPWY9bS5sZW5ndGg7d2hpbGUoZi0tKWs9bVtmXSwhZSYmcSE9PWsub3JpZ1R5cGV8fGMmJmMuZ3VpZCE9PWsuZ3VpZHx8aCYmIWgudGVzdChrLm5hbWVzcGFjZSl8fGQmJmQhPT1rLnNlbGVjdG9yJiYoIioqIiE9PWR8fCFrLnNlbGVjdG9yKXx8KG0uc3BsaWNlKGYsMSksay5zZWxlY3RvciYmbS5kZWxlZ2F0ZUNvdW50LS0sbC5yZW1vdmUmJmwucmVtb3ZlLmNhbGwoYSxrKSk7ZyYmIW0ubGVuZ3RoJiYobC50ZWFyZG93biYmbC50ZWFyZG93bi5jYWxsKGEscCxyLmhhbmRsZSkhPT0hMXx8bi5yZW1vdmVFdmVudChhLG8sci5oYW5kbGUpLGRlbGV0ZSBpW29dKX1lbHNlIGZvcihvIGluIGkpbi5ldmVudC5yZW1vdmUoYSxvK2Jbal0sYyxkLCEwKTtuLmlzRW1wdHlPYmplY3QoaSkmJk4ucmVtb3ZlKGEsImhhbmRsZSBldmVudHMiKX19LGRpc3BhdGNoOmZ1bmN0aW9uKGEpe2E9bi5ldmVudC5maXgoYSk7dmFyIGIsYyxkLGYsZyxoPVtdLGk9ZS5jYWxsKGFyZ3VtZW50cyksaj0oTi5nZXQodGhpcywiZXZlbnRzIil8fHt9KVthLnR5cGVdfHxbXSxrPW4uZXZlbnQuc3BlY2lhbFthLnR5cGVdfHx7fTtpZihpWzBdPWEsYS5kZWxlZ2F0ZVRhcmdldD10aGlzLCFrLnByZURpc3BhdGNofHxrLnByZURpc3BhdGNoLmNhbGwodGhpcyxhKSE9PSExKXtoPW4uZXZlbnQuaGFuZGxlcnMuY2FsbCh0aGlzLGEsaiksYj0wO3doaWxlKChmPWhbYisrXSkmJiFhLmlzUHJvcGFnYXRpb25TdG9wcGVkKCkpe2EuY3VycmVudFRhcmdldD1mLmVsZW0sYz0wO3doaWxlKChnPWYuaGFuZGxlcnNbYysrXSkmJiFhLmlzSW1tZWRpYXRlUHJvcGFnYXRpb25TdG9wcGVkKCkpKCFhLnJuYW1lc3BhY2V8fGEucm5hbWVzcGFjZS50ZXN0KGcubmFtZXNwYWNlKSkmJihhLmhhbmRsZU9iaj1nLGEuZGF0YT1nLmRhdGEsZD0oKG4uZXZlbnQuc3BlY2lhbFtnLm9yaWdUeXBlXXx8e30pLmhhbmRsZXx8Zy5oYW5kbGVyKS5hcHBseShmLmVsZW0saSksdm9pZCAwIT09ZCYmKGEucmVzdWx0PWQpPT09ITEmJihhLnByZXZlbnREZWZhdWx0KCksYS5zdG9wUHJvcGFnYXRpb24oKSkpfXJldHVybiBrLnBvc3REaXNwYXRjaCYmay5wb3N0RGlzcGF0Y2guY2FsbCh0aGlzLGEpLGEucmVzdWx0fX0saGFuZGxlcnM6ZnVuY3Rpb24oYSxiKXt2YXIgYyxkLGUsZixnPVtdLGg9Yi5kZWxlZ2F0ZUNvdW50LGk9YS50YXJnZXQ7aWYoaCYmaS5ub2RlVHlwZSYmKCJjbGljayIhPT1hLnR5cGV8fGlzTmFOKGEuYnV0dG9uKXx8YS5idXR0b248MSkpZm9yKDtpIT09dGhpcztpPWkucGFyZW50Tm9kZXx8dGhpcylpZigxPT09aS5ub2RlVHlwZSYmKGkuZGlzYWJsZWQhPT0hMHx8ImNsaWNrIiE9PWEudHlwZSkpe2ZvcihkPVtdLGM9MDtoPmM7YysrKWY9YltjXSxlPWYuc2VsZWN0b3IrIiAiLHZvaWQgMD09PWRbZV0mJihkW2VdPWYubmVlZHNDb250ZXh0P24oZSx0aGlzKS5pbmRleChpKT4tMTpuLmZpbmQoZSx0aGlzLG51bGwsW2ldKS5sZW5ndGgpLGRbZV0mJmQucHVzaChmKTtkLmxlbmd0aCYmZy5wdXNoKHtlbGVtOmksaGFuZGxlcnM6ZH0pfXJldHVybiBoPGIubGVuZ3RoJiZnLnB1c2goe2VsZW06dGhpcyxoYW5kbGVyczpiLnNsaWNlKGgpfSksZ30scHJvcHM6ImFsdEtleSBidWJibGVzIGNhbmNlbGFibGUgY3RybEtleSBjdXJyZW50VGFyZ2V0IGRldGFpbCBldmVudFBoYXNlIG1ldGFLZXkgcmVsYXRlZFRhcmdldCBzaGlmdEtleSB0YXJnZXQgdGltZVN0YW1wIHZpZXcgd2hpY2giLnNwbGl0KCIgIiksZml4SG9va3M6e30sa2V5SG9va3M6e3Byb3BzOiJjaGFyIGNoYXJDb2RlIGtleSBrZXlDb2RlIi5zcGxpdCgiICIpLGZpbHRlcjpmdW5jdGlvbihhLGIpe3JldHVybiBudWxsPT1hLndoaWNoJiYoYS53aGljaD1udWxsIT1iLmNoYXJDb2RlP2IuY2hhckNvZGU6Yi5rZXlDb2RlKSxhfX0sbW91c2VIb29rczp7cHJvcHM6ImJ1dHRvbiBidXR0b25zIGNsaWVudFggY2xpZW50WSBvZmZzZXRYIG9mZnNldFkgcGFnZVggcGFnZVkgc2NyZWVuWCBzY3JlZW5ZIHRvRWxlbWVudCIuc3BsaXQoIiAiKSxmaWx0ZXI6ZnVuY3Rpb24oYSxiKXt2YXIgYyxlLGYsZz1iLmJ1dHRvbjtyZXR1cm4gbnVsbD09YS5wYWdlWCYmbnVsbCE9Yi5jbGllbnRYJiYoYz1hLnRhcmdldC5vd25lckRvY3VtZW50fHxkLGU9Yy5kb2N1bWVudEVsZW1lbnQsZj1jLmJvZHksYS5wYWdlWD1iLmNsaWVudFgrKGUmJmUuc2Nyb2xsTGVmdHx8ZiYmZi5zY3JvbGxMZWZ0fHwwKS0oZSYmZS5jbGllbnRMZWZ0fHxmJiZmLmNsaWVudExlZnR8fDApLGEucGFnZVk9Yi5jbGllbnRZKyhlJiZlLnNjcm9sbFRvcHx8ZiYmZi5zY3JvbGxUb3B8fDApLShlJiZlLmNsaWVudFRvcHx8ZiYmZi5jbGllbnRUb3B8fDApKSxhLndoaWNofHx2b2lkIDA9PT1nfHwoYS53aGljaD0xJmc/MToyJmc/Mzo0Jmc/MjowKSxhfX0sZml4OmZ1bmN0aW9uKGEpe2lmKGFbbi5leHBhbmRvXSlyZXR1cm4gYTt2YXIgYixjLGUsZj1hLnR5cGUsZz1hLGg9dGhpcy5maXhIb29rc1tmXTtofHwodGhpcy5maXhIb29rc1tmXT1oPWVhLnRlc3QoZik/dGhpcy5tb3VzZUhvb2tzOmRhLnRlc3QoZik/dGhpcy5rZXlIb29rczp7fSksZT1oLnByb3BzP3RoaXMucHJvcHMuY29uY2F0KGgucHJvcHMpOnRoaXMucHJvcHMsYT1uZXcgbi5FdmVudChnKSxiPWUubGVuZ3RoO3doaWxlKGItLSljPWVbYl0sYVtjXT1nW2NdO3JldHVybiBhLnRhcmdldHx8KGEudGFyZ2V0PWQpLDM9PT1hLnRhcmdldC5ub2RlVHlwZSYmKGEudGFyZ2V0PWEudGFyZ2V0LnBhcmVudE5vZGUpLGguZmlsdGVyP2guZmlsdGVyKGEsZyk6YX0sc3BlY2lhbDp7bG9hZDp7bm9CdWJibGU6ITB9LGZvY3VzOnt0cmlnZ2VyOmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMhPT1pYSgpJiZ0aGlzLmZvY3VzPyh0aGlzLmZvY3VzKCksITEpOnZvaWQgMH0sZGVsZWdhdGVUeXBlOiJmb2N1c2luIn0sYmx1cjp7dHJpZ2dlcjpmdW5jdGlvbigpe3JldHVybiB0aGlzPT09aWEoKSYmdGhpcy5ibHVyPyh0aGlzLmJsdXIoKSwhMSk6dm9pZCAwfSxkZWxlZ2F0ZVR5cGU6ImZvY3Vzb3V0In0sY2xpY2s6e3RyaWdnZXI6ZnVuY3Rpb24oKXtyZXR1cm4iY2hlY2tib3giPT09dGhpcy50eXBlJiZ0aGlzLmNsaWNrJiZuLm5vZGVOYW1lKHRoaXMsImlucHV0Iik/KHRoaXMuY2xpY2soKSwhMSk6dm9pZCAwfSxfZGVmYXVsdDpmdW5jdGlvbihhKXtyZXR1cm4gbi5ub2RlTmFtZShhLnRhcmdldCwiYSIpfX0sYmVmb3JldW5sb2FkOntwb3N0RGlzcGF0Y2g6ZnVuY3Rpb24oYSl7dm9pZCAwIT09YS5yZXN1bHQmJmEub3JpZ2luYWxFdmVudCYmKGEub3JpZ2luYWxFdmVudC5yZXR1cm5WYWx1ZT1hLnJlc3VsdCl9fX19LG4ucmVtb3ZlRXZlbnQ9ZnVuY3Rpb24oYSxiLGMpe2EucmVtb3ZlRXZlbnRMaXN0ZW5lciYmYS5yZW1vdmVFdmVudExpc3RlbmVyKGIsYyl9LG4uRXZlbnQ9ZnVuY3Rpb24oYSxiKXtyZXR1cm4gdGhpcyBpbnN0YW5jZW9mIG4uRXZlbnQ/KGEmJmEudHlwZT8odGhpcy5vcmlnaW5hbEV2ZW50PWEsdGhpcy50eXBlPWEudHlwZSx0aGlzLmlzRGVmYXVsdFByZXZlbnRlZD1hLmRlZmF1bHRQcmV2ZW50ZWR8fHZvaWQgMD09PWEuZGVmYXVsdFByZXZlbnRlZCYmYS5yZXR1cm5WYWx1ZT09PSExP2dhOmhhKTp0aGlzLnR5cGU9YSxiJiZuLmV4dGVuZCh0aGlzLGIpLHRoaXMudGltZVN0YW1wPWEmJmEudGltZVN0YW1wfHxuLm5vdygpLHZvaWQodGhpc1tuLmV4cGFuZG9dPSEwKSk6bmV3IG4uRXZlbnQoYSxiKX0sbi5FdmVudC5wcm90b3R5cGU9e2NvbnN0cnVjdG9yOm4uRXZlbnQsaXNEZWZhdWx0UHJldmVudGVkOmhhLGlzUHJvcGFnYXRpb25TdG9wcGVkOmhhLGlzSW1tZWRpYXRlUHJvcGFnYXRpb25TdG9wcGVkOmhhLHByZXZlbnREZWZhdWx0OmZ1bmN0aW9uKCl7dmFyIGE9dGhpcy5vcmlnaW5hbEV2ZW50O3RoaXMuaXNEZWZhdWx0UHJldmVudGVkPWdhLGEmJmEucHJldmVudERlZmF1bHQoKX0sc3RvcFByb3BhZ2F0aW9uOmZ1bmN0aW9uKCl7dmFyIGE9dGhpcy5vcmlnaW5hbEV2ZW50O3RoaXMuaXNQcm9wYWdhdGlvblN0b3BwZWQ9Z2EsYSYmYS5zdG9wUHJvcGFnYXRpb24oKX0sc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uOmZ1bmN0aW9uKCl7dmFyIGE9dGhpcy5vcmlnaW5hbEV2ZW50O3RoaXMuaXNJbW1lZGlhdGVQcm9wYWdhdGlvblN0b3BwZWQ9Z2EsYSYmYS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKSx0aGlzLnN0b3BQcm9wYWdhdGlvbigpfX0sbi5lYWNoKHttb3VzZWVudGVyOiJtb3VzZW92ZXIiLG1vdXNlbGVhdmU6Im1vdXNlb3V0Iixwb2ludGVyZW50ZXI6InBvaW50ZXJvdmVyIixwb2ludGVybGVhdmU6InBvaW50ZXJvdXQifSxmdW5jdGlvbihhLGIpe24uZXZlbnQuc3BlY2lhbFthXT17ZGVsZWdhdGVUeXBlOmIsYmluZFR5cGU6YixoYW5kbGU6ZnVuY3Rpb24oYSl7dmFyIGMsZD10aGlzLGU9YS5yZWxhdGVkVGFyZ2V0LGY9YS5oYW5kbGVPYmo7cmV0dXJuKCFlfHxlIT09ZCYmIW4uY29udGFpbnMoZCxlKSkmJihhLnR5cGU9Zi5vcmlnVHlwZSxjPWYuaGFuZGxlci5hcHBseSh0aGlzLGFyZ3VtZW50cyksYS50eXBlPWIpLGN9fX0pLG4uZm4uZXh0ZW5kKHtvbjpmdW5jdGlvbihhLGIsYyxkKXtyZXR1cm4gamEodGhpcyxhLGIsYyxkKX0sb25lOmZ1bmN0aW9uKGEsYixjLGQpe3JldHVybiBqYSh0aGlzLGEsYixjLGQsMSl9LG9mZjpmdW5jdGlvbihhLGIsYyl7dmFyIGQsZTtpZihhJiZhLnByZXZlbnREZWZhdWx0JiZhLmhhbmRsZU9iailyZXR1cm4gZD1hLmhhbmRsZU9iaixuKGEuZGVsZWdhdGVUYXJnZXQpLm9mZihkLm5hbWVzcGFjZT9kLm9yaWdUeXBlKyIuIitkLm5hbWVzcGFjZTpkLm9yaWdUeXBlLGQuc2VsZWN0b3IsZC5oYW5kbGVyKSx0aGlzO2lmKCJvYmplY3QiPT10eXBlb2YgYSl7Zm9yKGUgaW4gYSl0aGlzLm9mZihlLGIsYVtlXSk7cmV0dXJuIHRoaXN9cmV0dXJuKGI9PT0hMXx8ImZ1bmN0aW9uIj09dHlwZW9mIGIpJiYoYz1iLGI9dm9pZCAwKSxjPT09ITEmJihjPWhhKSx0aGlzLmVhY2goZnVuY3Rpb24oKXtuLmV2ZW50LnJlbW92ZSh0aGlzLGEsYyxiKX0pfX0pO3ZhciBrYT0vPCg/IWFyZWF8YnJ8Y29sfGVtYmVkfGhyfGltZ3xpbnB1dHxsaW5rfG1ldGF8cGFyYW0pKChbXHc6LV0rKVtePl0qKVwvPi9naSxsYT0vPHNjcmlwdHw8c3R5bGV8PGxpbmsvaSxtYT0vY2hlY2tlZFxzKig/OltePV18PVxzKi5jaGVja2VkLikvaSxuYT0vXnRydWVcLyguKikvLG9hPS9eXHMqPCEoPzpcW0NEQVRBXFt8LS0pfCg/OlxdXF18LS0pPlxzKiQvZztmdW5jdGlvbiBwYShhLGIpe3JldHVybiBuLm5vZGVOYW1lKGEsInRhYmxlIikmJm4ubm9kZU5hbWUoMTEhPT1iLm5vZGVUeXBlP2I6Yi5maXJzdENoaWxkLCJ0ciIpP2EuZ2V0RWxlbWVudHNCeVRhZ05hbWUoInRib2R5IilbMF18fGE6YX1mdW5jdGlvbiBxYShhKXtyZXR1cm4gYS50eXBlPShudWxsIT09YS5nZXRBdHRyaWJ1dGUoInR5cGUiKSkrIi8iK2EudHlwZSxhfWZ1bmN0aW9uIHJhKGEpe3ZhciBiPW5hLmV4ZWMoYS50eXBlKTtyZXR1cm4gYj9hLnR5cGU9YlsxXTphLnJlbW92ZUF0dHJpYnV0ZSgidHlwZSIpLGF9ZnVuY3Rpb24gc2EoYSxiKXt2YXIgYyxkLGUsZixnLGgsaSxqO2lmKDE9PT1iLm5vZGVUeXBlKXtpZihOLmhhc0RhdGEoYSkmJihmPU4uYWNjZXNzKGEpLGc9Ti5zZXQoYixmKSxqPWYuZXZlbnRzKSl7ZGVsZXRlIGcuaGFuZGxlLGcuZXZlbnRzPXt9O2ZvcihlIGluIGopZm9yKGM9MCxkPWpbZV0ubGVuZ3RoO2Q+YztjKyspbi5ldmVudC5hZGQoYixlLGpbZV1bY10pfU8uaGFzRGF0YShhKSYmKGg9Ty5hY2Nlc3MoYSksaT1uLmV4dGVuZCh7fSxoKSxPLnNldChiLGkpKX19ZnVuY3Rpb24gdGEoYSxiKXt2YXIgYz1iLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCk7ImlucHV0Ij09PWMmJlgudGVzdChhLnR5cGUpP2IuY2hlY2tlZD1hLmNoZWNrZWQ6KCJpbnB1dCI9PT1jfHwidGV4dGFyZWEiPT09YykmJihiLmRlZmF1bHRWYWx1ZT1hLmRlZmF1bHRWYWx1ZSl9ZnVuY3Rpb24gdWEoYSxiLGMsZCl7Yj1mLmFwcGx5KFtdLGIpO3ZhciBlLGcsaCxpLGosayxtPTAsbz1hLmxlbmd0aCxwPW8tMSxxPWJbMF0scj1uLmlzRnVuY3Rpb24ocSk7aWYocnx8bz4xJiYic3RyaW5nIj09dHlwZW9mIHEmJiFsLmNoZWNrQ2xvbmUmJm1hLnRlc3QocSkpcmV0dXJuIGEuZWFjaChmdW5jdGlvbihlKXt2YXIgZj1hLmVxKGUpO3ImJihiWzBdPXEuY2FsbCh0aGlzLGUsZi5odG1sKCkpKSx1YShmLGIsYyxkKX0pO2lmKG8mJihlPWNhKGIsYVswXS5vd25lckRvY3VtZW50LCExLGEsZCksZz1lLmZpcnN0Q2hpbGQsMT09PWUuY2hpbGROb2Rlcy5sZW5ndGgmJihlPWcpLGd8fGQpKXtmb3IoaD1uLm1hcChfKGUsInNjcmlwdCIpLHFhKSxpPWgubGVuZ3RoO28+bTttKyspaj1lLG0hPT1wJiYoaj1uLmNsb25lKGosITAsITApLGkmJm4ubWVyZ2UoaCxfKGosInNjcmlwdCIpKSksYy5jYWxsKGFbbV0saixtKTtpZihpKWZvcihrPWhbaC5sZW5ndGgtMV0ub3duZXJEb2N1bWVudCxuLm1hcChoLHJhKSxtPTA7aT5tO20rKylqPWhbbV0sWi50ZXN0KGoudHlwZXx8IiIpJiYhTi5hY2Nlc3MoaiwiZ2xvYmFsRXZhbCIpJiZuLmNvbnRhaW5zKGssaikmJihqLnNyYz9uLl9ldmFsVXJsJiZuLl9ldmFsVXJsKGouc3JjKTpuLmdsb2JhbEV2YWwoai50ZXh0Q29udGVudC5yZXBsYWNlKG9hLCIiKSkpfXJldHVybiBhfWZ1bmN0aW9uIHZhKGEsYixjKXtmb3IodmFyIGQsZT1iP24uZmlsdGVyKGIsYSk6YSxmPTA7bnVsbCE9KGQ9ZVtmXSk7ZisrKWN8fDEhPT1kLm5vZGVUeXBlfHxuLmNsZWFuRGF0YShfKGQpKSxkLnBhcmVudE5vZGUmJihjJiZuLmNvbnRhaW5zKGQub3duZXJEb2N1bWVudCxkKSYmYWEoXyhkLCJzY3JpcHQiKSksZC5wYXJlbnROb2RlLnJlbW92ZUNoaWxkKGQpKTtyZXR1cm4gYX1uLmV4dGVuZCh7aHRtbFByZWZpbHRlcjpmdW5jdGlvbihhKXtyZXR1cm4gYS5yZXBsYWNlKGthLCI8JDE+PC8kMj4iKX0sY2xvbmU6ZnVuY3Rpb24oYSxiLGMpe3ZhciBkLGUsZixnLGg9YS5jbG9uZU5vZGUoITApLGk9bi5jb250YWlucyhhLm93bmVyRG9jdW1lbnQsYSk7aWYoIShsLm5vQ2xvbmVDaGVja2VkfHwxIT09YS5ub2RlVHlwZSYmMTEhPT1hLm5vZGVUeXBlfHxuLmlzWE1MRG9jKGEpKSlmb3IoZz1fKGgpLGY9XyhhKSxkPTAsZT1mLmxlbmd0aDtlPmQ7ZCsrKXRhKGZbZF0sZ1tkXSk7aWYoYilpZihjKWZvcihmPWZ8fF8oYSksZz1nfHxfKGgpLGQ9MCxlPWYubGVuZ3RoO2U+ZDtkKyspc2EoZltkXSxnW2RdKTtlbHNlIHNhKGEsaCk7cmV0dXJuIGc9XyhoLCJzY3JpcHQiKSxnLmxlbmd0aD4wJiZhYShnLCFpJiZfKGEsInNjcmlwdCIpKSxofSxjbGVhbkRhdGE6ZnVuY3Rpb24oYSl7Zm9yKHZhciBiLGMsZCxlPW4uZXZlbnQuc3BlY2lhbCxmPTA7dm9pZCAwIT09KGM9YVtmXSk7ZisrKWlmKEwoYykpe2lmKGI9Y1tOLmV4cGFuZG9dKXtpZihiLmV2ZW50cylmb3IoZCBpbiBiLmV2ZW50cyllW2RdP24uZXZlbnQucmVtb3ZlKGMsZCk6bi5yZW1vdmVFdmVudChjLGQsYi5oYW5kbGUpO2NbTi5leHBhbmRvXT12b2lkIDB9Y1tPLmV4cGFuZG9dJiYoY1tPLmV4cGFuZG9dPXZvaWQgMCl9fX0pLG4uZm4uZXh0ZW5kKHtkb21NYW5pcDp1YSxkZXRhY2g6ZnVuY3Rpb24oYSl7cmV0dXJuIHZhKHRoaXMsYSwhMCl9LHJlbW92ZTpmdW5jdGlvbihhKXtyZXR1cm4gdmEodGhpcyxhKX0sdGV4dDpmdW5jdGlvbihhKXtyZXR1cm4gSyh0aGlzLGZ1bmN0aW9uKGEpe3JldHVybiB2b2lkIDA9PT1hP24udGV4dCh0aGlzKTp0aGlzLmVtcHR5KCkuZWFjaChmdW5jdGlvbigpeygxPT09dGhpcy5ub2RlVHlwZXx8MTE9PT10aGlzLm5vZGVUeXBlfHw5PT09dGhpcy5ub2RlVHlwZSkmJih0aGlzLnRleHRDb250ZW50PWEpfSl9LG51bGwsYSxhcmd1bWVudHMubGVuZ3RoKX0sYXBwZW5kOmZ1bmN0aW9uKCl7cmV0dXJuIHVhKHRoaXMsYXJndW1lbnRzLGZ1bmN0aW9uKGEpe2lmKDE9PT10aGlzLm5vZGVUeXBlfHwxMT09PXRoaXMubm9kZVR5cGV8fDk9PT10aGlzLm5vZGVUeXBlKXt2YXIgYj1wYSh0aGlzLGEpO2IuYXBwZW5kQ2hpbGQoYSl9fSl9LHByZXBlbmQ6ZnVuY3Rpb24oKXtyZXR1cm4gdWEodGhpcyxhcmd1bWVudHMsZnVuY3Rpb24oYSl7aWYoMT09PXRoaXMubm9kZVR5cGV8fDExPT09dGhpcy5ub2RlVHlwZXx8OT09PXRoaXMubm9kZVR5cGUpe3ZhciBiPXBhKHRoaXMsYSk7Yi5pbnNlcnRCZWZvcmUoYSxiLmZpcnN0Q2hpbGQpfX0pfSxiZWZvcmU6ZnVuY3Rpb24oKXtyZXR1cm4gdWEodGhpcyxhcmd1bWVudHMsZnVuY3Rpb24oYSl7dGhpcy5wYXJlbnROb2RlJiZ0aGlzLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGEsdGhpcyl9KX0sYWZ0ZXI6ZnVuY3Rpb24oKXtyZXR1cm4gdWEodGhpcyxhcmd1bWVudHMsZnVuY3Rpb24oYSl7dGhpcy5wYXJlbnROb2RlJiZ0aGlzLnBhcmVudE5vZGUuaW5zZXJ0QmVmb3JlKGEsdGhpcy5uZXh0U2libGluZyl9KX0sZW1wdHk6ZnVuY3Rpb24oKXtmb3IodmFyIGEsYj0wO251bGwhPShhPXRoaXNbYl0pO2IrKykxPT09YS5ub2RlVHlwZSYmKG4uY2xlYW5EYXRhKF8oYSwhMSkpLGEudGV4dENvbnRlbnQ9IiIpO3JldHVybiB0aGlzfSxjbG9uZTpmdW5jdGlvbihhLGIpe3JldHVybiBhPW51bGw9PWE/ITE6YSxiPW51bGw9PWI/YTpiLHRoaXMubWFwKGZ1bmN0aW9uKCl7cmV0dXJuIG4uY2xvbmUodGhpcyxhLGIpfSl9LGh0bWw6ZnVuY3Rpb24oYSl7cmV0dXJuIEsodGhpcyxmdW5jdGlvbihhKXt2YXIgYj10aGlzWzBdfHx7fSxjPTAsZD10aGlzLmxlbmd0aDtpZih2b2lkIDA9PT1hJiYxPT09Yi5ub2RlVHlwZSlyZXR1cm4gYi5pbm5lckhUTUw7aWYoInN0cmluZyI9PXR5cGVvZiBhJiYhbGEudGVzdChhKSYmISRbKFkuZXhlYyhhKXx8WyIiLCIiXSlbMV0udG9Mb3dlckNhc2UoKV0pe2E9bi5odG1sUHJlZmlsdGVyKGEpO3RyeXtmb3IoO2Q+YztjKyspYj10aGlzW2NdfHx7fSwxPT09Yi5ub2RlVHlwZSYmKG4uY2xlYW5EYXRhKF8oYiwhMSkpLGIuaW5uZXJIVE1MPWEpO2I9MH1jYXRjaChlKXt9fWImJnRoaXMuZW1wdHkoKS5hcHBlbmQoYSl9LG51bGwsYSxhcmd1bWVudHMubGVuZ3RoKX0scmVwbGFjZVdpdGg6ZnVuY3Rpb24oKXt2YXIgYT1bXTtyZXR1cm4gdWEodGhpcyxhcmd1bWVudHMsZnVuY3Rpb24oYil7dmFyIGM9dGhpcy5wYXJlbnROb2RlO24uaW5BcnJheSh0aGlzLGEpPDAmJihuLmNsZWFuRGF0YShfKHRoaXMpKSxjJiZjLnJlcGxhY2VDaGlsZChiLHRoaXMpKX0sYSl9fSksbi5lYWNoKHthcHBlbmRUbzoiYXBwZW5kIixwcmVwZW5kVG86InByZXBlbmQiLGluc2VydEJlZm9yZToiYmVmb3JlIixpbnNlcnRBZnRlcjoiYWZ0ZXIiLHJlcGxhY2VBbGw6InJlcGxhY2VXaXRoIn0sZnVuY3Rpb24oYSxiKXtuLmZuW2FdPWZ1bmN0aW9uKGEpe2Zvcih2YXIgYyxkPVtdLGU9bihhKSxmPWUubGVuZ3RoLTEsaD0wO2Y+PWg7aCsrKWM9aD09PWY/dGhpczp0aGlzLmNsb25lKCEwKSxuKGVbaF0pW2JdKGMpLGcuYXBwbHkoZCxjLmdldCgpKTtyZXR1cm4gdGhpcy5wdXNoU3RhY2soZCl9fSk7dmFyIHdhLHhhPXtIVE1MOiJibG9jayIsQk9EWToiYmxvY2sifTtmdW5jdGlvbiB5YShhLGIpe3ZhciBjPW4oYi5jcmVhdGVFbGVtZW50KGEpKS5hcHBlbmRUbyhiLmJvZHkpLGQ9bi5jc3MoY1swXSwiZGlzcGxheSIpO3JldHVybiBjLmRldGFjaCgpLGR9ZnVuY3Rpb24gemEoYSl7dmFyIGI9ZCxjPXhhW2FdO3JldHVybiBjfHwoYz15YShhLGIpLCJub25lIiE9PWMmJmN8fCh3YT0od2F8fG4oIjxpZnJhbWUgZnJhbWVib3JkZXI9JzAnIHdpZHRoPScwJyBoZWlnaHQ9JzAnLz4iKSkuYXBwZW5kVG8oYi5kb2N1bWVudEVsZW1lbnQpLGI9d2FbMF0uY29udGVudERvY3VtZW50LGIud3JpdGUoKSxiLmNsb3NlKCksYz15YShhLGIpLHdhLmRldGFjaCgpKSx4YVthXT1jKSxjfXZhciBBYT0vXm1hcmdpbi8sQmE9bmV3IFJlZ0V4cCgiXigiK1MrIikoPyFweClbYS16JV0rJCIsImkiKSxDYT1mdW5jdGlvbihiKXt2YXIgYz1iLm93bmVyRG9jdW1lbnQuZGVmYXVsdFZpZXc7cmV0dXJuIGMub3BlbmVyfHwoYz1hKSxjLmdldENvbXB1dGVkU3R5bGUoYil9LERhPWZ1bmN0aW9uKGEsYixjLGQpe3ZhciBlLGYsZz17fTtmb3IoZiBpbiBiKWdbZl09YS5zdHlsZVtmXSxhLnN0eWxlW2ZdPWJbZl07ZT1jLmFwcGx5KGEsZHx8W10pO2ZvcihmIGluIGIpYS5zdHlsZVtmXT1nW2ZdO3JldHVybiBlfSxFYT1kLmRvY3VtZW50RWxlbWVudDshZnVuY3Rpb24oKXt2YXIgYixjLGUsZixnPWQuY3JlYXRlRWxlbWVudCgiZGl2IiksaD1kLmNyZWF0ZUVsZW1lbnQoImRpdiIpO2lmKGguc3R5bGUpe2guc3R5bGUuYmFja2dyb3VuZENsaXA9ImNvbnRlbnQtYm94IixoLmNsb25lTm9kZSghMCkuc3R5bGUuYmFja2dyb3VuZENsaXA9IiIsbC5jbGVhckNsb25lU3R5bGU9ImNvbnRlbnQtYm94Ij09PWguc3R5bGUuYmFja2dyb3VuZENsaXAsZy5zdHlsZS5jc3NUZXh0PSJib3JkZXI6MDt3aWR0aDo4cHg7aGVpZ2h0OjA7dG9wOjA7bGVmdDotOTk5OXB4O3BhZGRpbmc6MDttYXJnaW4tdG9wOjFweDtwb3NpdGlvbjphYnNvbHV0ZSIsZy5hcHBlbmRDaGlsZChoKTtmdW5jdGlvbiBpKCl7aC5zdHlsZS5jc3NUZXh0PSItd2Via2l0LWJveC1zaXppbmc6Ym9yZGVyLWJveDstbW96LWJveC1zaXppbmc6Ym9yZGVyLWJveDtib3gtc2l6aW5nOmJvcmRlci1ib3g7cG9zaXRpb246cmVsYXRpdmU7ZGlzcGxheTpibG9jazttYXJnaW46YXV0bztib3JkZXI6MXB4O3BhZGRpbmc6MXB4O3RvcDoxJTt3aWR0aDo1MCUiLGguaW5uZXJIVE1MPSIiLEVhLmFwcGVuZENoaWxkKGcpO3ZhciBkPWEuZ2V0Q29tcHV0ZWRTdHlsZShoKTtiPSIxJSIhPT1kLnRvcCxmPSIycHgiPT09ZC5tYXJnaW5MZWZ0LGM9IjRweCI9PT1kLndpZHRoLGguc3R5bGUubWFyZ2luUmlnaHQ9IjUwJSIsZT0iNHB4Ij09PWQubWFyZ2luUmlnaHQsRWEucmVtb3ZlQ2hpbGQoZyl9bi5leHRlbmQobCx7cGl4ZWxQb3NpdGlvbjpmdW5jdGlvbigpe3JldHVybiBpKCksYn0sYm94U2l6aW5nUmVsaWFibGU6ZnVuY3Rpb24oKXtyZXR1cm4gbnVsbD09YyYmaSgpLGN9LHBpeGVsTWFyZ2luUmlnaHQ6ZnVuY3Rpb24oKXtyZXR1cm4gbnVsbD09YyYmaSgpLGV9LHJlbGlhYmxlTWFyZ2luTGVmdDpmdW5jdGlvbigpe3JldHVybiBudWxsPT1jJiZpKCksZn0scmVsaWFibGVNYXJnaW5SaWdodDpmdW5jdGlvbigpe3ZhciBiLGM9aC5hcHBlbmRDaGlsZChkLmNyZWF0ZUVsZW1lbnQoImRpdiIpKTtyZXR1cm4gYy5zdHlsZS5jc3NUZXh0PWguc3R5bGUuY3NzVGV4dD0iLXdlYmtpdC1ib3gtc2l6aW5nOmNvbnRlbnQtYm94O2JveC1zaXppbmc6Y29udGVudC1ib3g7ZGlzcGxheTpibG9jazttYXJnaW46MDtib3JkZXI6MDtwYWRkaW5nOjAiLGMuc3R5bGUubWFyZ2luUmlnaHQ9Yy5zdHlsZS53aWR0aD0iMCIsaC5zdHlsZS53aWR0aD0iMXB4IixFYS5hcHBlbmRDaGlsZChnKSxiPSFwYXJzZUZsb2F0KGEuZ2V0Q29tcHV0ZWRTdHlsZShjKS5tYXJnaW5SaWdodCksRWEucmVtb3ZlQ2hpbGQoZyksaC5yZW1vdmVDaGlsZChjKSxifX0pfX0oKTtmdW5jdGlvbiBGYShhLGIsYyl7dmFyIGQsZSxmLGcsaD1hLnN0eWxlO3JldHVybiBjPWN8fENhKGEpLGMmJihnPWMuZ2V0UHJvcGVydHlWYWx1ZShiKXx8Y1tiXSwiIiE9PWd8fG4uY29udGFpbnMoYS5vd25lckRvY3VtZW50LGEpfHwoZz1uLnN0eWxlKGEsYikpLCFsLnBpeGVsTWFyZ2luUmlnaHQoKSYmQmEudGVzdChnKSYmQWEudGVzdChiKSYmKGQ9aC53aWR0aCxlPWgubWluV2lkdGgsZj1oLm1heFdpZHRoLGgubWluV2lkdGg9aC5tYXhXaWR0aD1oLndpZHRoPWcsZz1jLndpZHRoLGgud2lkdGg9ZCxoLm1pbldpZHRoPWUsaC5tYXhXaWR0aD1mKSksdm9pZCAwIT09Zz9nKyIiOmd9ZnVuY3Rpb24gR2EoYSxiKXtyZXR1cm57Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIGEoKT92b2lkIGRlbGV0ZSB0aGlzLmdldDoodGhpcy5nZXQ9YikuYXBwbHkodGhpcyxhcmd1bWVudHMpfX19dmFyIEhhPS9eKG5vbmV8dGFibGUoPyEtY1tlYV0pLispLyxJYT17cG9zaXRpb246ImFic29sdXRlIix2aXNpYmlsaXR5OiJoaWRkZW4iLGRpc3BsYXk6ImJsb2NrIn0sSmE9e2xldHRlclNwYWNpbmc6IjAiLGZvbnRXZWlnaHQ6IjQwMCJ9LEthPVsiV2Via2l0IiwiTyIsIk1veiIsIm1zIl0sTGE9ZC5jcmVhdGVFbGVtZW50KCJkaXYiKS5zdHlsZTtmdW5jdGlvbiBNYShhKXtpZihhIGluIExhKXJldHVybiBhO3ZhciBiPWFbMF0udG9VcHBlckNhc2UoKSthLnNsaWNlKDEpLGM9S2EubGVuZ3RoO3doaWxlKGMtLSlpZihhPUthW2NdK2IsYSBpbiBMYSlyZXR1cm4gYX1mdW5jdGlvbiBOYShhLGIsYyl7dmFyIGQ9VC5leGVjKGIpO3JldHVybiBkP01hdGgubWF4KDAsZFsyXS0oY3x8MCkpKyhkWzNdfHwicHgiKTpifWZ1bmN0aW9uIE9hKGEsYixjLGQsZSl7Zm9yKHZhciBmPWM9PT0oZD8iYm9yZGVyIjoiY29udGVudCIpPzQ6IndpZHRoIj09PWI/MTowLGc9MDs0PmY7Zis9MikibWFyZ2luIj09PWMmJihnKz1uLmNzcyhhLGMrVVtmXSwhMCxlKSksZD8oImNvbnRlbnQiPT09YyYmKGctPW4uY3NzKGEsInBhZGRpbmciK1VbZl0sITAsZSkpLCJtYXJnaW4iIT09YyYmKGctPW4uY3NzKGEsImJvcmRlciIrVVtmXSsiV2lkdGgiLCEwLGUpKSk6KGcrPW4uY3NzKGEsInBhZGRpbmciK1VbZl0sITAsZSksInBhZGRpbmciIT09YyYmKGcrPW4uY3NzKGEsImJvcmRlciIrVVtmXSsiV2lkdGgiLCEwLGUpKSk7cmV0dXJuIGd9ZnVuY3Rpb24gUGEoYixjLGUpe3ZhciBmPSEwLGc9IndpZHRoIj09PWM/Yi5vZmZzZXRXaWR0aDpiLm9mZnNldEhlaWdodCxoPUNhKGIpLGk9ImJvcmRlci1ib3giPT09bi5jc3MoYiwiYm94U2l6aW5nIiwhMSxoKTtpZihkLm1zRnVsbHNjcmVlbkVsZW1lbnQmJmEudG9wIT09YSYmYi5nZXRDbGllbnRSZWN0cygpLmxlbmd0aCYmKGc9TWF0aC5yb3VuZCgxMDAqYi5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKVtjXSkpLDA+PWd8fG51bGw9PWcpe2lmKGc9RmEoYixjLGgpLCgwPmd8fG51bGw9PWcpJiYoZz1iLnN0eWxlW2NdKSxCYS50ZXN0KGcpKXJldHVybiBnO2Y9aSYmKGwuYm94U2l6aW5nUmVsaWFibGUoKXx8Zz09PWIuc3R5bGVbY10pLGc9cGFyc2VGbG9hdChnKXx8MH1yZXR1cm4gZytPYShiLGMsZXx8KGk/ImJvcmRlciI6ImNvbnRlbnQiKSxmLGgpKyJweCJ9ZnVuY3Rpb24gUWEoYSxiKXtmb3IodmFyIGMsZCxlLGY9W10sZz0wLGg9YS5sZW5ndGg7aD5nO2crKylkPWFbZ10sZC5zdHlsZSYmKGZbZ109Ti5nZXQoZCwib2xkZGlzcGxheSIpLGM9ZC5zdHlsZS5kaXNwbGF5LGI/KGZbZ118fCJub25lIiE9PWN8fChkLnN0eWxlLmRpc3BsYXk9IiIpLCIiPT09ZC5zdHlsZS5kaXNwbGF5JiZWKGQpJiYoZltnXT1OLmFjY2VzcyhkLCJvbGRkaXNwbGF5Iix6YShkLm5vZGVOYW1lKSkpKTooZT1WKGQpLCJub25lIj09PWMmJmV8fE4uc2V0KGQsIm9sZGRpc3BsYXkiLGU/YzpuLmNzcyhkLCJkaXNwbGF5IikpKSk7Zm9yKGc9MDtoPmc7ZysrKWQ9YVtnXSxkLnN0eWxlJiYoYiYmIm5vbmUiIT09ZC5zdHlsZS5kaXNwbGF5JiYiIiE9PWQuc3R5bGUuZGlzcGxheXx8KGQuc3R5bGUuZGlzcGxheT1iP2ZbZ118fCIiOiJub25lIikpO3JldHVybiBhfW4uZXh0ZW5kKHtjc3NIb29rczp7b3BhY2l0eTp7Z2V0OmZ1bmN0aW9uKGEsYil7aWYoYil7dmFyIGM9RmEoYSwib3BhY2l0eSIpO3JldHVybiIiPT09Yz8iMSI6Y319fX0sY3NzTnVtYmVyOnthbmltYXRpb25JdGVyYXRpb25Db3VudDohMCxjb2x1bW5Db3VudDohMCxmaWxsT3BhY2l0eTohMCxmbGV4R3JvdzohMCxmbGV4U2hyaW5rOiEwLGZvbnRXZWlnaHQ6ITAsbGluZUhlaWdodDohMCxvcGFjaXR5OiEwLG9yZGVyOiEwLG9ycGhhbnM6ITAsd2lkb3dzOiEwLHpJbmRleDohMCx6b29tOiEwfSxjc3NQcm9wczp7ImZsb2F0IjoiY3NzRmxvYXQifSxzdHlsZTpmdW5jdGlvbihhLGIsYyxkKXtpZihhJiYzIT09YS5ub2RlVHlwZSYmOCE9PWEubm9kZVR5cGUmJmEuc3R5bGUpe3ZhciBlLGYsZyxoPW4uY2FtZWxDYXNlKGIpLGk9YS5zdHlsZTtyZXR1cm4gYj1uLmNzc1Byb3BzW2hdfHwobi5jc3NQcm9wc1toXT1NYShoKXx8aCksZz1uLmNzc0hvb2tzW2JdfHxuLmNzc0hvb2tzW2hdLHZvaWQgMD09PWM/ZyYmImdldCJpbiBnJiZ2b2lkIDAhPT0oZT1nLmdldChhLCExLGQpKT9lOmlbYl06KGY9dHlwZW9mIGMsInN0cmluZyI9PT1mJiYoZT1ULmV4ZWMoYykpJiZlWzFdJiYoYz1XKGEsYixlKSxmPSJudW1iZXIiKSxudWxsIT1jJiZjPT09YyYmKCJudW1iZXIiPT09ZiYmKGMrPWUmJmVbM118fChuLmNzc051bWJlcltoXT8iIjoicHgiKSksbC5jbGVhckNsb25lU3R5bGV8fCIiIT09Y3x8MCE9PWIuaW5kZXhPZigiYmFja2dyb3VuZCIpfHwoaVtiXT0iaW5oZXJpdCIpLGcmJiJzZXQiaW4gZyYmdm9pZCAwPT09KGM9Zy5zZXQoYSxjLGQpKXx8KGlbYl09YykpLHZvaWQgMCl9fSxjc3M6ZnVuY3Rpb24oYSxiLGMsZCl7dmFyIGUsZixnLGg9bi5jYW1lbENhc2UoYik7cmV0dXJuIGI9bi5jc3NQcm9wc1toXXx8KG4uY3NzUHJvcHNbaF09TWEoaCl8fGgpLGc9bi5jc3NIb29rc1tiXXx8bi5jc3NIb29rc1toXSxnJiYiZ2V0ImluIGcmJihlPWcuZ2V0KGEsITAsYykpLHZvaWQgMD09PWUmJihlPUZhKGEsYixkKSksIm5vcm1hbCI9PT1lJiZiIGluIEphJiYoZT1KYVtiXSksIiI9PT1jfHxjPyhmPXBhcnNlRmxvYXQoZSksYz09PSEwfHxpc0Zpbml0ZShmKT9mfHwwOmUpOmV9fSksbi5lYWNoKFsiaGVpZ2h0Iiwid2lkdGgiXSxmdW5jdGlvbihhLGIpe24uY3NzSG9va3NbYl09e2dldDpmdW5jdGlvbihhLGMsZCl7cmV0dXJuIGM/SGEudGVzdChuLmNzcyhhLCJkaXNwbGF5IikpJiYwPT09YS5vZmZzZXRXaWR0aD9EYShhLElhLGZ1bmN0aW9uKCl7cmV0dXJuIFBhKGEsYixkKX0pOlBhKGEsYixkKTp2b2lkIDB9LHNldDpmdW5jdGlvbihhLGMsZCl7dmFyIGUsZj1kJiZDYShhKSxnPWQmJk9hKGEsYixkLCJib3JkZXItYm94Ij09PW4uY3NzKGEsImJveFNpemluZyIsITEsZiksZik7cmV0dXJuIGcmJihlPVQuZXhlYyhjKSkmJiJweCIhPT0oZVszXXx8InB4IikmJihhLnN0eWxlW2JdPWMsYz1uLmNzcyhhLGIpKSxOYShhLGMsZyl9fX0pLG4uY3NzSG9va3MubWFyZ2luTGVmdD1HYShsLnJlbGlhYmxlTWFyZ2luTGVmdCxmdW5jdGlvbihhLGIpe3JldHVybiBiPyhwYXJzZUZsb2F0KEZhKGEsIm1hcmdpbkxlZnQiKSl8fGEuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkubGVmdC1EYShhLHttYXJnaW5MZWZ0OjB9LGZ1bmN0aW9uKCl7cmV0dXJuIGEuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkubGVmdH0pKSsicHgiOnZvaWQgMH0pLG4uY3NzSG9va3MubWFyZ2luUmlnaHQ9R2EobC5yZWxpYWJsZU1hcmdpblJpZ2h0LGZ1bmN0aW9uKGEsYil7cmV0dXJuIGI/RGEoYSx7ZGlzcGxheToiaW5saW5lLWJsb2NrIn0sRmEsW2EsIm1hcmdpblJpZ2h0Il0pOnZvaWQgMH0pLG4uZWFjaCh7bWFyZ2luOiIiLHBhZGRpbmc6IiIsYm9yZGVyOiJXaWR0aCJ9LGZ1bmN0aW9uKGEsYil7bi5jc3NIb29rc1thK2JdPXtleHBhbmQ6ZnVuY3Rpb24oYyl7Zm9yKHZhciBkPTAsZT17fSxmPSJzdHJpbmciPT10eXBlb2YgYz9jLnNwbGl0KCIgIik6W2NdOzQ+ZDtkKyspZVthK1VbZF0rYl09ZltkXXx8ZltkLTJdfHxmWzBdO3JldHVybiBlfX0sQWEudGVzdChhKXx8KG4uY3NzSG9va3NbYStiXS5zZXQ9TmEpfSksbi5mbi5leHRlbmQoe2NzczpmdW5jdGlvbihhLGIpe3JldHVybiBLKHRoaXMsZnVuY3Rpb24oYSxiLGMpe3ZhciBkLGUsZj17fSxnPTA7aWYobi5pc0FycmF5KGIpKXtmb3IoZD1DYShhKSxlPWIubGVuZ3RoO2U+ZztnKyspZltiW2ddXT1uLmNzcyhhLGJbZ10sITEsZCk7cmV0dXJuIGZ9cmV0dXJuIHZvaWQgMCE9PWM/bi5zdHlsZShhLGIsYyk6bi5jc3MoYSxiKX0sYSxiLGFyZ3VtZW50cy5sZW5ndGg+MSl9LHNob3c6ZnVuY3Rpb24oKXtyZXR1cm4gUWEodGhpcywhMCl9LGhpZGU6ZnVuY3Rpb24oKXtyZXR1cm4gUWEodGhpcyl9LHRvZ2dsZTpmdW5jdGlvbihhKXtyZXR1cm4iYm9vbGVhbiI9PXR5cGVvZiBhP2E/dGhpcy5zaG93KCk6dGhpcy5oaWRlKCk6dGhpcy5lYWNoKGZ1bmN0aW9uKCl7Vih0aGlzKT9uKHRoaXMpLnNob3coKTpuKHRoaXMpLmhpZGUoKX0pfX0pO2Z1bmN0aW9uIFJhKGEsYixjLGQsZSl7cmV0dXJuIG5ldyBSYS5wcm90b3R5cGUuaW5pdChhLGIsYyxkLGUpfW4uVHdlZW49UmEsUmEucHJvdG90eXBlPXtjb25zdHJ1Y3RvcjpSYSxpbml0OmZ1bmN0aW9uKGEsYixjLGQsZSxmKXt0aGlzLmVsZW09YSx0aGlzLnByb3A9Yyx0aGlzLmVhc2luZz1lfHxuLmVhc2luZy5fZGVmYXVsdCx0aGlzLm9wdGlvbnM9Yix0aGlzLnN0YXJ0PXRoaXMubm93PXRoaXMuY3VyKCksdGhpcy5lbmQ9ZCx0aGlzLnVuaXQ9Znx8KG4uY3NzTnVtYmVyW2NdPyIiOiJweCIpfSxjdXI6ZnVuY3Rpb24oKXt2YXIgYT1SYS5wcm9wSG9va3NbdGhpcy5wcm9wXTtyZXR1cm4gYSYmYS5nZXQ/YS5nZXQodGhpcyk6UmEucHJvcEhvb2tzLl9kZWZhdWx0LmdldCh0aGlzKX0scnVuOmZ1bmN0aW9uKGEpe3ZhciBiLGM9UmEucHJvcEhvb2tzW3RoaXMucHJvcF07cmV0dXJuIHRoaXMub3B0aW9ucy5kdXJhdGlvbj90aGlzLnBvcz1iPW4uZWFzaW5nW3RoaXMuZWFzaW5nXShhLHRoaXMub3B0aW9ucy5kdXJhdGlvbiphLDAsMSx0aGlzLm9wdGlvbnMuZHVyYXRpb24pOnRoaXMucG9zPWI9YSx0aGlzLm5vdz0odGhpcy5lbmQtdGhpcy5zdGFydCkqYit0aGlzLnN0YXJ0LHRoaXMub3B0aW9ucy5zdGVwJiZ0aGlzLm9wdGlvbnMuc3RlcC5jYWxsKHRoaXMuZWxlbSx0aGlzLm5vdyx0aGlzKSxjJiZjLnNldD9jLnNldCh0aGlzKTpSYS5wcm9wSG9va3MuX2RlZmF1bHQuc2V0KHRoaXMpLHRoaXN9fSxSYS5wcm90b3R5cGUuaW5pdC5wcm90b3R5cGU9UmEucHJvdG90eXBlLFJhLnByb3BIb29rcz17X2RlZmF1bHQ6e2dldDpmdW5jdGlvbihhKXt2YXIgYjtyZXR1cm4gMSE9PWEuZWxlbS5ub2RlVHlwZXx8bnVsbCE9YS5lbGVtW2EucHJvcF0mJm51bGw9PWEuZWxlbS5zdHlsZVthLnByb3BdP2EuZWxlbVthLnByb3BdOihiPW4uY3NzKGEuZWxlbSxhLnByb3AsIiIpLGImJiJhdXRvIiE9PWI/YjowKX0sc2V0OmZ1bmN0aW9uKGEpe24uZnguc3RlcFthLnByb3BdP24uZnguc3RlcFthLnByb3BdKGEpOjEhPT1hLmVsZW0ubm9kZVR5cGV8fG51bGw9PWEuZWxlbS5zdHlsZVtuLmNzc1Byb3BzW2EucHJvcF1dJiYhbi5jc3NIb29rc1thLnByb3BdP2EuZWxlbVthLnByb3BdPWEubm93Om4uc3R5bGUoYS5lbGVtLGEucHJvcCxhLm5vdythLnVuaXQpfX19LFJhLnByb3BIb29rcy5zY3JvbGxUb3A9UmEucHJvcEhvb2tzLnNjcm9sbExlZnQ9e3NldDpmdW5jdGlvbihhKXthLmVsZW0ubm9kZVR5cGUmJmEuZWxlbS5wYXJlbnROb2RlJiYoYS5lbGVtW2EucHJvcF09YS5ub3cpfX0sbi5lYXNpbmc9e2xpbmVhcjpmdW5jdGlvbihhKXtyZXR1cm4gYX0sc3dpbmc6ZnVuY3Rpb24oYSl7cmV0dXJuLjUtTWF0aC5jb3MoYSpNYXRoLlBJKS8yfSxfZGVmYXVsdDoic3dpbmcifSxuLmZ4PVJhLnByb3RvdHlwZS5pbml0LG4uZnguc3RlcD17fTt2YXIgU2EsVGEsVWE9L14oPzp0b2dnbGV8c2hvd3xoaWRlKSQvLFZhPS9xdWV1ZUhvb2tzJC87ZnVuY3Rpb24gV2EoKXtyZXR1cm4gYS5zZXRUaW1lb3V0KGZ1bmN0aW9uKCl7U2E9dm9pZCAwfSksU2E9bi5ub3coKX1mdW5jdGlvbiBYYShhLGIpe3ZhciBjLGQ9MCxlPXtoZWlnaHQ6YX07Zm9yKGI9Yj8xOjA7ND5kO2QrPTItYiljPVVbZF0sZVsibWFyZ2luIitjXT1lWyJwYWRkaW5nIitjXT1hO3JldHVybiBiJiYoZS5vcGFjaXR5PWUud2lkdGg9YSksZX1mdW5jdGlvbiBZYShhLGIsYyl7Zm9yKHZhciBkLGU9KF9hLnR3ZWVuZXJzW2JdfHxbXSkuY29uY2F0KF9hLnR3ZWVuZXJzWyIqIl0pLGY9MCxnPWUubGVuZ3RoO2c+ZjtmKyspaWYoZD1lW2ZdLmNhbGwoYyxiLGEpKXJldHVybiBkfWZ1bmN0aW9uIFphKGEsYixjKXt2YXIgZCxlLGYsZyxoLGksaixrLGw9dGhpcyxtPXt9LG89YS5zdHlsZSxwPWEubm9kZVR5cGUmJlYoYSkscT1OLmdldChhLCJmeHNob3ciKTtjLnF1ZXVlfHwoaD1uLl9xdWV1ZUhvb2tzKGEsImZ4IiksbnVsbD09aC51bnF1ZXVlZCYmKGgudW5xdWV1ZWQ9MCxpPWguZW1wdHkuZmlyZSxoLmVtcHR5LmZpcmU9ZnVuY3Rpb24oKXtoLnVucXVldWVkfHxpKCl9KSxoLnVucXVldWVkKyssbC5hbHdheXMoZnVuY3Rpb24oKXtsLmFsd2F5cyhmdW5jdGlvbigpe2gudW5xdWV1ZWQtLSxuLnF1ZXVlKGEsImZ4IikubGVuZ3RofHxoLmVtcHR5LmZpcmUoKX0pfSkpLDE9PT1hLm5vZGVUeXBlJiYoImhlaWdodCJpbiBifHwid2lkdGgiaW4gYikmJihjLm92ZXJmbG93PVtvLm92ZXJmbG93LG8ub3ZlcmZsb3dYLG8ub3ZlcmZsb3dZXSxqPW4uY3NzKGEsImRpc3BsYXkiKSxrPSJub25lIj09PWo/Ti5nZXQoYSwib2xkZGlzcGxheSIpfHx6YShhLm5vZGVOYW1lKTpqLCJpbmxpbmUiPT09ayYmIm5vbmUiPT09bi5jc3MoYSwiZmxvYXQiKSYmKG8uZGlzcGxheT0iaW5saW5lLWJsb2NrIikpLGMub3ZlcmZsb3cmJihvLm92ZXJmbG93PSJoaWRkZW4iLGwuYWx3YXlzKGZ1bmN0aW9uKCl7by5vdmVyZmxvdz1jLm92ZXJmbG93WzBdLG8ub3ZlcmZsb3dYPWMub3ZlcmZsb3dbMV0sby5vdmVyZmxvd1k9Yy5vdmVyZmxvd1syXX0pKTtmb3IoZCBpbiBiKWlmKGU9YltkXSxVYS5leGVjKGUpKXtpZihkZWxldGUgYltkXSxmPWZ8fCJ0b2dnbGUiPT09ZSxlPT09KHA/ImhpZGUiOiJzaG93Iikpe2lmKCJzaG93IiE9PWV8fCFxfHx2b2lkIDA9PT1xW2RdKWNvbnRpbnVlO3A9ITB9bVtkXT1xJiZxW2RdfHxuLnN0eWxlKGEsZCl9ZWxzZSBqPXZvaWQgMDtpZihuLmlzRW1wdHlPYmplY3QobSkpImlubGluZSI9PT0oIm5vbmUiPT09aj96YShhLm5vZGVOYW1lKTpqKSYmKG8uZGlzcGxheT1qKTtlbHNle3E/ImhpZGRlbiJpbiBxJiYocD1xLmhpZGRlbik6cT1OLmFjY2VzcyhhLCJmeHNob3ciLHt9KSxmJiYocS5oaWRkZW49IXApLHA/bihhKS5zaG93KCk6bC5kb25lKGZ1bmN0aW9uKCl7bihhKS5oaWRlKCl9KSxsLmRvbmUoZnVuY3Rpb24oKXt2YXIgYjtOLnJlbW92ZShhLCJmeHNob3ciKTtmb3IoYiBpbiBtKW4uc3R5bGUoYSxiLG1bYl0pfSk7Zm9yKGQgaW4gbSlnPVlhKHA/cVtkXTowLGQsbCksZCBpbiBxfHwocVtkXT1nLnN0YXJ0LHAmJihnLmVuZD1nLnN0YXJ0LGcuc3RhcnQ9IndpZHRoIj09PWR8fCJoZWlnaHQiPT09ZD8xOjApKX19ZnVuY3Rpb24gJGEoYSxiKXt2YXIgYyxkLGUsZixnO2ZvcihjIGluIGEpaWYoZD1uLmNhbWVsQ2FzZShjKSxlPWJbZF0sZj1hW2NdLG4uaXNBcnJheShmKSYmKGU9ZlsxXSxmPWFbY109ZlswXSksYyE9PWQmJihhW2RdPWYsZGVsZXRlIGFbY10pLGc9bi5jc3NIb29rc1tkXSxnJiYiZXhwYW5kImluIGcpe2Y9Zy5leHBhbmQoZiksZGVsZXRlIGFbZF07Zm9yKGMgaW4gZiljIGluIGF8fChhW2NdPWZbY10sYltjXT1lKX1lbHNlIGJbZF09ZX1mdW5jdGlvbiBfYShhLGIsYyl7dmFyIGQsZSxmPTAsZz1fYS5wcmVmaWx0ZXJzLmxlbmd0aCxoPW4uRGVmZXJyZWQoKS5hbHdheXMoZnVuY3Rpb24oKXtkZWxldGUgaS5lbGVtfSksaT1mdW5jdGlvbigpe2lmKGUpcmV0dXJuITE7Zm9yKHZhciBiPVNhfHxXYSgpLGM9TWF0aC5tYXgoMCxqLnN0YXJ0VGltZStqLmR1cmF0aW9uLWIpLGQ9Yy9qLmR1cmF0aW9ufHwwLGY9MS1kLGc9MCxpPWoudHdlZW5zLmxlbmd0aDtpPmc7ZysrKWoudHdlZW5zW2ddLnJ1bihmKTtyZXR1cm4gaC5ub3RpZnlXaXRoKGEsW2osZixjXSksMT5mJiZpP2M6KGgucmVzb2x2ZVdpdGgoYSxbal0pLCExKX0saj1oLnByb21pc2Uoe2VsZW06YSxwcm9wczpuLmV4dGVuZCh7fSxiKSxvcHRzOm4uZXh0ZW5kKCEwLHtzcGVjaWFsRWFzaW5nOnt9LGVhc2luZzpuLmVhc2luZy5fZGVmYXVsdH0sYyksb3JpZ2luYWxQcm9wZXJ0aWVzOmIsb3JpZ2luYWxPcHRpb25zOmMsc3RhcnRUaW1lOlNhfHxXYSgpLGR1cmF0aW9uOmMuZHVyYXRpb24sdHdlZW5zOltdLGNyZWF0ZVR3ZWVuOmZ1bmN0aW9uKGIsYyl7dmFyIGQ9bi5Ud2VlbihhLGoub3B0cyxiLGMsai5vcHRzLnNwZWNpYWxFYXNpbmdbYl18fGoub3B0cy5lYXNpbmcpO3JldHVybiBqLnR3ZWVucy5wdXNoKGQpLGR9LHN0b3A6ZnVuY3Rpb24oYil7dmFyIGM9MCxkPWI/ai50d2VlbnMubGVuZ3RoOjA7aWYoZSlyZXR1cm4gdGhpcztmb3IoZT0hMDtkPmM7YysrKWoudHdlZW5zW2NdLnJ1bigxKTtyZXR1cm4gYj8oaC5ub3RpZnlXaXRoKGEsW2osMSwwXSksaC5yZXNvbHZlV2l0aChhLFtqLGJdKSk6aC5yZWplY3RXaXRoKGEsW2osYl0pLHRoaXN9fSksaz1qLnByb3BzO2ZvcigkYShrLGoub3B0cy5zcGVjaWFsRWFzaW5nKTtnPmY7ZisrKWlmKGQ9X2EucHJlZmlsdGVyc1tmXS5jYWxsKGosYSxrLGoub3B0cykpcmV0dXJuIG4uaXNGdW5jdGlvbihkLnN0b3ApJiYobi5fcXVldWVIb29rcyhqLmVsZW0sai5vcHRzLnF1ZXVlKS5zdG9wPW4ucHJveHkoZC5zdG9wLGQpKSxkO3JldHVybiBuLm1hcChrLFlhLGopLG4uaXNGdW5jdGlvbihqLm9wdHMuc3RhcnQpJiZqLm9wdHMuc3RhcnQuY2FsbChhLGopLG4uZngudGltZXIobi5leHRlbmQoaSx7ZWxlbTphLGFuaW06aixxdWV1ZTpqLm9wdHMucXVldWV9KSksai5wcm9ncmVzcyhqLm9wdHMucHJvZ3Jlc3MpLmRvbmUoai5vcHRzLmRvbmUsai5vcHRzLmNvbXBsZXRlKS5mYWlsKGoub3B0cy5mYWlsKS5hbHdheXMoai5vcHRzLmFsd2F5cyl9bi5BbmltYXRpb249bi5leHRlbmQoX2Ese3R3ZWVuZXJzOnsiKiI6W2Z1bmN0aW9uKGEsYil7dmFyIGM9dGhpcy5jcmVhdGVUd2VlbihhLGIpO3JldHVybiBXKGMuZWxlbSxhLFQuZXhlYyhiKSxjKSxjfV19LHR3ZWVuZXI6ZnVuY3Rpb24oYSxiKXtuLmlzRnVuY3Rpb24oYSk/KGI9YSxhPVsiKiJdKTphPWEubWF0Y2goRyk7Zm9yKHZhciBjLGQ9MCxlPWEubGVuZ3RoO2U+ZDtkKyspYz1hW2RdLF9hLnR3ZWVuZXJzW2NdPV9hLnR3ZWVuZXJzW2NdfHxbXSxfYS50d2VlbmVyc1tjXS51bnNoaWZ0KGIpfSxwcmVmaWx0ZXJzOltaYV0scHJlZmlsdGVyOmZ1bmN0aW9uKGEsYil7Yj9fYS5wcmVmaWx0ZXJzLnVuc2hpZnQoYSk6X2EucHJlZmlsdGVycy5wdXNoKGEpfX0pLG4uc3BlZWQ9ZnVuY3Rpb24oYSxiLGMpe3ZhciBkPWEmJiJvYmplY3QiPT10eXBlb2YgYT9uLmV4dGVuZCh7fSxhKTp7Y29tcGxldGU6Y3x8IWMmJmJ8fG4uaXNGdW5jdGlvbihhKSYmYSxkdXJhdGlvbjphLGVhc2luZzpjJiZifHxiJiYhbi5pc0Z1bmN0aW9uKGIpJiZifTtyZXR1cm4gZC5kdXJhdGlvbj1uLmZ4Lm9mZj8wOiJudW1iZXIiPT10eXBlb2YgZC5kdXJhdGlvbj9kLmR1cmF0aW9uOmQuZHVyYXRpb24gaW4gbi5meC5zcGVlZHM/bi5meC5zcGVlZHNbZC5kdXJhdGlvbl06bi5meC5zcGVlZHMuX2RlZmF1bHQsKG51bGw9PWQucXVldWV8fGQucXVldWU9PT0hMCkmJihkLnF1ZXVlPSJmeCIpLGQub2xkPWQuY29tcGxldGUsZC5jb21wbGV0ZT1mdW5jdGlvbigpe24uaXNGdW5jdGlvbihkLm9sZCkmJmQub2xkLmNhbGwodGhpcyksZC5xdWV1ZSYmbi5kZXF1ZXVlKHRoaXMsZC5xdWV1ZSl9LGR9LG4uZm4uZXh0ZW5kKHtmYWRlVG86ZnVuY3Rpb24oYSxiLGMsZCl7cmV0dXJuIHRoaXMuZmlsdGVyKFYpLmNzcygib3BhY2l0eSIsMCkuc2hvdygpLmVuZCgpLmFuaW1hdGUoe29wYWNpdHk6Yn0sYSxjLGQpfSxhbmltYXRlOmZ1bmN0aW9uKGEsYixjLGQpe3ZhciBlPW4uaXNFbXB0eU9iamVjdChhKSxmPW4uc3BlZWQoYixjLGQpLGc9ZnVuY3Rpb24oKXt2YXIgYj1fYSh0aGlzLG4uZXh0ZW5kKHt9LGEpLGYpOyhlfHxOLmdldCh0aGlzLCJmaW5pc2giKSkmJmIuc3RvcCghMCl9O3JldHVybiBnLmZpbmlzaD1nLGV8fGYucXVldWU9PT0hMT90aGlzLmVhY2goZyk6dGhpcy5xdWV1ZShmLnF1ZXVlLGcpfSxzdG9wOmZ1bmN0aW9uKGEsYixjKXt2YXIgZD1mdW5jdGlvbihhKXt2YXIgYj1hLnN0b3A7ZGVsZXRlIGEuc3RvcCxiKGMpfTtyZXR1cm4ic3RyaW5nIiE9dHlwZW9mIGEmJihjPWIsYj1hLGE9dm9pZCAwKSxiJiZhIT09ITEmJnRoaXMucXVldWUoYXx8ImZ4IixbXSksdGhpcy5lYWNoKGZ1bmN0aW9uKCl7dmFyIGI9ITAsZT1udWxsIT1hJiZhKyJxdWV1ZUhvb2tzIixmPW4udGltZXJzLGc9Ti5nZXQodGhpcyk7aWYoZSlnW2VdJiZnW2VdLnN0b3AmJmQoZ1tlXSk7ZWxzZSBmb3IoZSBpbiBnKWdbZV0mJmdbZV0uc3RvcCYmVmEudGVzdChlKSYmZChnW2VdKTtmb3IoZT1mLmxlbmd0aDtlLS07KWZbZV0uZWxlbSE9PXRoaXN8fG51bGwhPWEmJmZbZV0ucXVldWUhPT1hfHwoZltlXS5hbmltLnN0b3AoYyksYj0hMSxmLnNwbGljZShlLDEpKTsoYnx8IWMpJiZuLmRlcXVldWUodGhpcyxhKX0pfSxmaW5pc2g6ZnVuY3Rpb24oYSl7cmV0dXJuIGEhPT0hMSYmKGE9YXx8ImZ4IiksdGhpcy5lYWNoKGZ1bmN0aW9uKCl7dmFyIGIsYz1OLmdldCh0aGlzKSxkPWNbYSsicXVldWUiXSxlPWNbYSsicXVldWVIb29rcyJdLGY9bi50aW1lcnMsZz1kP2QubGVuZ3RoOjA7Zm9yKGMuZmluaXNoPSEwLG4ucXVldWUodGhpcyxhLFtdKSxlJiZlLnN0b3AmJmUuc3RvcC5jYWxsKHRoaXMsITApLGI9Zi5sZW5ndGg7Yi0tOylmW2JdLmVsZW09PT10aGlzJiZmW2JdLnF1ZXVlPT09YSYmKGZbYl0uYW5pbS5zdG9wKCEwKSxmLnNwbGljZShiLDEpKTtmb3IoYj0wO2c+YjtiKyspZFtiXSYmZFtiXS5maW5pc2gmJmRbYl0uZmluaXNoLmNhbGwodGhpcyk7ZGVsZXRlIGMuZmluaXNofSl9fSksbi5lYWNoKFsidG9nZ2xlIiwic2hvdyIsImhpZGUiXSxmdW5jdGlvbihhLGIpe3ZhciBjPW4uZm5bYl07bi5mbltiXT1mdW5jdGlvbihhLGQsZSl7cmV0dXJuIG51bGw9PWF8fCJib29sZWFuIj09dHlwZW9mIGE/Yy5hcHBseSh0aGlzLGFyZ3VtZW50cyk6dGhpcy5hbmltYXRlKFhhKGIsITApLGEsZCxlKX19KSxuLmVhY2goe3NsaWRlRG93bjpYYSgic2hvdyIpLHNsaWRlVXA6WGEoImhpZGUiKSxzbGlkZVRvZ2dsZTpYYSgidG9nZ2xlIiksZmFkZUluOntvcGFjaXR5OiJzaG93In0sZmFkZU91dDp7b3BhY2l0eToiaGlkZSJ9LGZhZGVUb2dnbGU6e29wYWNpdHk6InRvZ2dsZSJ9fSxmdW5jdGlvbihhLGIpe24uZm5bYV09ZnVuY3Rpb24oYSxjLGQpe3JldHVybiB0aGlzLmFuaW1hdGUoYixhLGMsZCl9fSksbi50aW1lcnM9W10sbi5meC50aWNrPWZ1bmN0aW9uKCl7dmFyIGEsYj0wLGM9bi50aW1lcnM7Zm9yKFNhPW4ubm93KCk7YjxjLmxlbmd0aDtiKyspYT1jW2JdLGEoKXx8Y1tiXSE9PWF8fGMuc3BsaWNlKGItLSwxKTtjLmxlbmd0aHx8bi5meC5zdG9wKCksU2E9dm9pZCAwfSxuLmZ4LnRpbWVyPWZ1bmN0aW9uKGEpe24udGltZXJzLnB1c2goYSksYSgpP24uZnguc3RhcnQoKTpuLnRpbWVycy5wb3AoKX0sbi5meC5pbnRlcnZhbD0xMyxuLmZ4LnN0YXJ0PWZ1bmN0aW9uKCl7VGF8fChUYT1hLnNldEludGVydmFsKG4uZngudGljayxuLmZ4LmludGVydmFsKSl9LG4uZnguc3RvcD1mdW5jdGlvbigpe2EuY2xlYXJJbnRlcnZhbChUYSksVGE9bnVsbH0sbi5meC5zcGVlZHM9e3Nsb3c6NjAwLGZhc3Q6MjAwLF9kZWZhdWx0OjQwMH0sbi5mbi5kZWxheT1mdW5jdGlvbihiLGMpe3JldHVybiBiPW4uZng/bi5meC5zcGVlZHNbYl18fGI6YixjPWN8fCJmeCIsdGhpcy5xdWV1ZShjLGZ1bmN0aW9uKGMsZCl7dmFyIGU9YS5zZXRUaW1lb3V0KGMsYik7ZC5zdG9wPWZ1bmN0aW9uKCl7YS5jbGVhclRpbWVvdXQoZSl9fSl9LGZ1bmN0aW9uKCl7dmFyIGE9ZC5jcmVhdGVFbGVtZW50KCJpbnB1dCIpLGI9ZC5jcmVhdGVFbGVtZW50KCJzZWxlY3QiKSxjPWIuYXBwZW5kQ2hpbGQoZC5jcmVhdGVFbGVtZW50KCJvcHRpb24iKSk7YS50eXBlPSJjaGVja2JveCIsbC5jaGVja09uPSIiIT09YS52YWx1ZSxsLm9wdFNlbGVjdGVkPWMuc2VsZWN0ZWQsYi5kaXNhYmxlZD0hMCxsLm9wdERpc2FibGVkPSFjLmRpc2FibGVkLGE9ZC5jcmVhdGVFbGVtZW50KCJpbnB1dCIpLGEudmFsdWU9InQiLGEudHlwZT0icmFkaW8iLGwucmFkaW9WYWx1ZT0idCI9PT1hLnZhbHVlfSgpO3ZhciBhYixiYj1uLmV4cHIuYXR0ckhhbmRsZTtuLmZuLmV4dGVuZCh7YXR0cjpmdW5jdGlvbihhLGIpe3JldHVybiBLKHRoaXMsbi5hdHRyLGEsYixhcmd1bWVudHMubGVuZ3RoPjEpfSxyZW1vdmVBdHRyOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKXtuLnJlbW92ZUF0dHIodGhpcyxhKX0pfX0pLG4uZXh0ZW5kKHthdHRyOmZ1bmN0aW9uKGEsYixjKXt2YXIgZCxlLGY9YS5ub2RlVHlwZTtpZigzIT09ZiYmOCE9PWYmJjIhPT1mKXJldHVybiJ1bmRlZmluZWQiPT10eXBlb2YgYS5nZXRBdHRyaWJ1dGU/bi5wcm9wKGEsYixjKTooMT09PWYmJm4uaXNYTUxEb2MoYSl8fChiPWIudG9Mb3dlckNhc2UoKSxlPW4uYXR0ckhvb2tzW2JdfHwobi5leHByLm1hdGNoLmJvb2wudGVzdChiKT9hYjp2b2lkIDApKSx2b2lkIDAhPT1jP251bGw9PT1jP3ZvaWQgbi5yZW1vdmVBdHRyKGEsYik6ZSYmInNldCJpbiBlJiZ2b2lkIDAhPT0oZD1lLnNldChhLGMsYikpP2Q6KGEuc2V0QXR0cmlidXRlKGIsYysiIiksYyk6ZSYmImdldCJpbiBlJiZudWxsIT09KGQ9ZS5nZXQoYSxiKSk/ZDooZD1uLmZpbmQuYXR0cihhLGIpLG51bGw9PWQ/dm9pZCAwOmQpKX0sYXR0ckhvb2tzOnt0eXBlOntzZXQ6ZnVuY3Rpb24oYSxiKXtpZighbC5yYWRpb1ZhbHVlJiYicmFkaW8iPT09YiYmbi5ub2RlTmFtZShhLCJpbnB1dCIpKXt2YXIgYz1hLnZhbHVlO3JldHVybiBhLnNldEF0dHJpYnV0ZSgidHlwZSIsYiksYyYmKGEudmFsdWU9YyksYn19fX0scmVtb3ZlQXR0cjpmdW5jdGlvbihhLGIpe3ZhciBjLGQsZT0wLGY9YiYmYi5tYXRjaChHKTtpZihmJiYxPT09YS5ub2RlVHlwZSl3aGlsZShjPWZbZSsrXSlkPW4ucHJvcEZpeFtjXXx8YyxuLmV4cHIubWF0Y2guYm9vbC50ZXN0KGMpJiYoYVtkXT0hMSksYS5yZW1vdmVBdHRyaWJ1dGUoYyl9fSksYWI9e3NldDpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIGI9PT0hMT9uLnJlbW92ZUF0dHIoYSxjKTphLnNldEF0dHJpYnV0ZShjLGMpLGN9fSxuLmVhY2gobi5leHByLm1hdGNoLmJvb2wuc291cmNlLm1hdGNoKC9cdysvZyksZnVuY3Rpb24oYSxiKXt2YXIgYz1iYltiXXx8bi5maW5kLmF0dHI7YmJbYl09ZnVuY3Rpb24oYSxiLGQpe3ZhciBlLGY7cmV0dXJuIGR8fChmPWJiW2JdLGJiW2JdPWUsZT1udWxsIT1jKGEsYixkKT9iLnRvTG93ZXJDYXNlKCk6bnVsbCxiYltiXT1mKSxlfX0pO3ZhciBjYj0vXig/OmlucHV0fHNlbGVjdHx0ZXh0YXJlYXxidXR0b24pJC9pLGRiPS9eKD86YXxhcmVhKSQvaTtuLmZuLmV4dGVuZCh7cHJvcDpmdW5jdGlvbihhLGIpe3JldHVybiBLKHRoaXMsbi5wcm9wLGEsYixhcmd1bWVudHMubGVuZ3RoPjEpfSxyZW1vdmVQcm9wOmZ1bmN0aW9uKGEpe3JldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oKXtkZWxldGUgdGhpc1tuLnByb3BGaXhbYV18fGFdfSl9fSksbi5leHRlbmQoe3Byb3A6ZnVuY3Rpb24oYSxiLGMpe3ZhciBkLGUsZj1hLm5vZGVUeXBlO2lmKDMhPT1mJiY4IT09ZiYmMiE9PWYpcmV0dXJuIDE9PT1mJiZuLmlzWE1MRG9jKGEpfHwoYj1uLnByb3BGaXhbYl18fGIsZT1uLnByb3BIb29rc1tiXSksdm9pZCAwIT09Yz9lJiYic2V0ImluIGUmJnZvaWQgMCE9PShkPWUuc2V0KGEsYyxiKSk/ZDphW2JdPWM6ZSYmImdldCJpbiBlJiZudWxsIT09KGQ9ZS5nZXQoYSxiKSk/ZDphW2JdOwp9LHByb3BIb29rczp7dGFiSW5kZXg6e2dldDpmdW5jdGlvbihhKXt2YXIgYj1uLmZpbmQuYXR0cihhLCJ0YWJpbmRleCIpO3JldHVybiBiP3BhcnNlSW50KGIsMTApOmNiLnRlc3QoYS5ub2RlTmFtZSl8fGRiLnRlc3QoYS5ub2RlTmFtZSkmJmEuaHJlZj8wOi0xfX19LHByb3BGaXg6eyJmb3IiOiJodG1sRm9yIiwiY2xhc3MiOiJjbGFzc05hbWUifX0pLGwub3B0U2VsZWN0ZWR8fChuLnByb3BIb29rcy5zZWxlY3RlZD17Z2V0OmZ1bmN0aW9uKGEpe3ZhciBiPWEucGFyZW50Tm9kZTtyZXR1cm4gYiYmYi5wYXJlbnROb2RlJiZiLnBhcmVudE5vZGUuc2VsZWN0ZWRJbmRleCxudWxsfX0pLG4uZWFjaChbInRhYkluZGV4IiwicmVhZE9ubHkiLCJtYXhMZW5ndGgiLCJjZWxsU3BhY2luZyIsImNlbGxQYWRkaW5nIiwicm93U3BhbiIsImNvbFNwYW4iLCJ1c2VNYXAiLCJmcmFtZUJvcmRlciIsImNvbnRlbnRFZGl0YWJsZSJdLGZ1bmN0aW9uKCl7bi5wcm9wRml4W3RoaXMudG9Mb3dlckNhc2UoKV09dGhpc30pO3ZhciBlYj0vW1x0XHJcblxmXS9nO2Z1bmN0aW9uIGZiKGEpe3JldHVybiBhLmdldEF0dHJpYnV0ZSYmYS5nZXRBdHRyaWJ1dGUoImNsYXNzIil8fCIifW4uZm4uZXh0ZW5kKHthZGRDbGFzczpmdW5jdGlvbihhKXt2YXIgYixjLGQsZSxmLGcsaCxpPTA7aWYobi5pc0Z1bmN0aW9uKGEpKXJldHVybiB0aGlzLmVhY2goZnVuY3Rpb24oYil7bih0aGlzKS5hZGRDbGFzcyhhLmNhbGwodGhpcyxiLGZiKHRoaXMpKSl9KTtpZigic3RyaW5nIj09dHlwZW9mIGEmJmEpe2I9YS5tYXRjaChHKXx8W107d2hpbGUoYz10aGlzW2krK10paWYoZT1mYihjKSxkPTE9PT1jLm5vZGVUeXBlJiYoIiAiK2UrIiAiKS5yZXBsYWNlKGViLCIgIikpe2c9MDt3aGlsZShmPWJbZysrXSlkLmluZGV4T2YoIiAiK2YrIiAiKTwwJiYoZCs9ZisiICIpO2g9bi50cmltKGQpLGUhPT1oJiZjLnNldEF0dHJpYnV0ZSgiY2xhc3MiLGgpfX1yZXR1cm4gdGhpc30scmVtb3ZlQ2xhc3M6ZnVuY3Rpb24oYSl7dmFyIGIsYyxkLGUsZixnLGgsaT0wO2lmKG4uaXNGdW5jdGlvbihhKSlyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKGIpe24odGhpcykucmVtb3ZlQ2xhc3MoYS5jYWxsKHRoaXMsYixmYih0aGlzKSkpfSk7aWYoIWFyZ3VtZW50cy5sZW5ndGgpcmV0dXJuIHRoaXMuYXR0cigiY2xhc3MiLCIiKTtpZigic3RyaW5nIj09dHlwZW9mIGEmJmEpe2I9YS5tYXRjaChHKXx8W107d2hpbGUoYz10aGlzW2krK10paWYoZT1mYihjKSxkPTE9PT1jLm5vZGVUeXBlJiYoIiAiK2UrIiAiKS5yZXBsYWNlKGViLCIgIikpe2c9MDt3aGlsZShmPWJbZysrXSl3aGlsZShkLmluZGV4T2YoIiAiK2YrIiAiKT4tMSlkPWQucmVwbGFjZSgiICIrZisiICIsIiAiKTtoPW4udHJpbShkKSxlIT09aCYmYy5zZXRBdHRyaWJ1dGUoImNsYXNzIixoKX19cmV0dXJuIHRoaXN9LHRvZ2dsZUNsYXNzOmZ1bmN0aW9uKGEsYil7dmFyIGM9dHlwZW9mIGE7cmV0dXJuImJvb2xlYW4iPT10eXBlb2YgYiYmInN0cmluZyI9PT1jP2I/dGhpcy5hZGRDbGFzcyhhKTp0aGlzLnJlbW92ZUNsYXNzKGEpOm4uaXNGdW5jdGlvbihhKT90aGlzLmVhY2goZnVuY3Rpb24oYyl7bih0aGlzKS50b2dnbGVDbGFzcyhhLmNhbGwodGhpcyxjLGZiKHRoaXMpLGIpLGIpfSk6dGhpcy5lYWNoKGZ1bmN0aW9uKCl7dmFyIGIsZCxlLGY7aWYoInN0cmluZyI9PT1jKXtkPTAsZT1uKHRoaXMpLGY9YS5tYXRjaChHKXx8W107d2hpbGUoYj1mW2QrK10pZS5oYXNDbGFzcyhiKT9lLnJlbW92ZUNsYXNzKGIpOmUuYWRkQ2xhc3MoYil9ZWxzZSh2b2lkIDA9PT1hfHwiYm9vbGVhbiI9PT1jKSYmKGI9ZmIodGhpcyksYiYmTi5zZXQodGhpcywiX19jbGFzc05hbWVfXyIsYiksdGhpcy5zZXRBdHRyaWJ1dGUmJnRoaXMuc2V0QXR0cmlidXRlKCJjbGFzcyIsYnx8YT09PSExPyIiOk4uZ2V0KHRoaXMsIl9fY2xhc3NOYW1lX18iKXx8IiIpKX0pfSxoYXNDbGFzczpmdW5jdGlvbihhKXt2YXIgYixjLGQ9MDtiPSIgIithKyIgIjt3aGlsZShjPXRoaXNbZCsrXSlpZigxPT09Yy5ub2RlVHlwZSYmKCIgIitmYihjKSsiICIpLnJlcGxhY2UoZWIsIiAiKS5pbmRleE9mKGIpPi0xKXJldHVybiEwO3JldHVybiExfX0pO3ZhciBnYj0vXHIvZztuLmZuLmV4dGVuZCh7dmFsOmZ1bmN0aW9uKGEpe3ZhciBiLGMsZCxlPXRoaXNbMF07e2lmKGFyZ3VtZW50cy5sZW5ndGgpcmV0dXJuIGQ9bi5pc0Z1bmN0aW9uKGEpLHRoaXMuZWFjaChmdW5jdGlvbihjKXt2YXIgZTsxPT09dGhpcy5ub2RlVHlwZSYmKGU9ZD9hLmNhbGwodGhpcyxjLG4odGhpcykudmFsKCkpOmEsbnVsbD09ZT9lPSIiOiJudW1iZXIiPT10eXBlb2YgZT9lKz0iIjpuLmlzQXJyYXkoZSkmJihlPW4ubWFwKGUsZnVuY3Rpb24oYSl7cmV0dXJuIG51bGw9PWE/IiI6YSsiIn0pKSxiPW4udmFsSG9va3NbdGhpcy50eXBlXXx8bi52YWxIb29rc1t0aGlzLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCldLGImJiJzZXQiaW4gYiYmdm9pZCAwIT09Yi5zZXQodGhpcyxlLCJ2YWx1ZSIpfHwodGhpcy52YWx1ZT1lKSl9KTtpZihlKXJldHVybiBiPW4udmFsSG9va3NbZS50eXBlXXx8bi52YWxIb29rc1tlLm5vZGVOYW1lLnRvTG93ZXJDYXNlKCldLGImJiJnZXQiaW4gYiYmdm9pZCAwIT09KGM9Yi5nZXQoZSwidmFsdWUiKSk/YzooYz1lLnZhbHVlLCJzdHJpbmciPT10eXBlb2YgYz9jLnJlcGxhY2UoZ2IsIiIpOm51bGw9PWM/IiI6Yyl9fX0pLG4uZXh0ZW5kKHt2YWxIb29rczp7b3B0aW9uOntnZXQ6ZnVuY3Rpb24oYSl7cmV0dXJuIG4udHJpbShhLnZhbHVlKX19LHNlbGVjdDp7Z2V0OmZ1bmN0aW9uKGEpe2Zvcih2YXIgYixjLGQ9YS5vcHRpb25zLGU9YS5zZWxlY3RlZEluZGV4LGY9InNlbGVjdC1vbmUiPT09YS50eXBlfHwwPmUsZz1mP251bGw6W10saD1mP2UrMTpkLmxlbmd0aCxpPTA+ZT9oOmY/ZTowO2g+aTtpKyspaWYoYz1kW2ldLChjLnNlbGVjdGVkfHxpPT09ZSkmJihsLm9wdERpc2FibGVkPyFjLmRpc2FibGVkOm51bGw9PT1jLmdldEF0dHJpYnV0ZSgiZGlzYWJsZWQiKSkmJighYy5wYXJlbnROb2RlLmRpc2FibGVkfHwhbi5ub2RlTmFtZShjLnBhcmVudE5vZGUsIm9wdGdyb3VwIikpKXtpZihiPW4oYykudmFsKCksZilyZXR1cm4gYjtnLnB1c2goYil9cmV0dXJuIGd9LHNldDpmdW5jdGlvbihhLGIpe3ZhciBjLGQsZT1hLm9wdGlvbnMsZj1uLm1ha2VBcnJheShiKSxnPWUubGVuZ3RoO3doaWxlKGctLSlkPWVbZ10sKGQuc2VsZWN0ZWQ9bi5pbkFycmF5KG4udmFsSG9va3Mub3B0aW9uLmdldChkKSxmKT4tMSkmJihjPSEwKTtyZXR1cm4gY3x8KGEuc2VsZWN0ZWRJbmRleD0tMSksZn19fX0pLG4uZWFjaChbInJhZGlvIiwiY2hlY2tib3giXSxmdW5jdGlvbigpe24udmFsSG9va3NbdGhpc109e3NldDpmdW5jdGlvbihhLGIpe3JldHVybiBuLmlzQXJyYXkoYik/YS5jaGVja2VkPW4uaW5BcnJheShuKGEpLnZhbCgpLGIpPi0xOnZvaWQgMH19LGwuY2hlY2tPbnx8KG4udmFsSG9va3NbdGhpc10uZ2V0PWZ1bmN0aW9uKGEpe3JldHVybiBudWxsPT09YS5nZXRBdHRyaWJ1dGUoInZhbHVlIik/Im9uIjphLnZhbHVlfSl9KTt2YXIgaGI9L14oPzpmb2N1c2luZm9jdXN8Zm9jdXNvdXRibHVyKSQvO24uZXh0ZW5kKG4uZXZlbnQse3RyaWdnZXI6ZnVuY3Rpb24oYixjLGUsZil7dmFyIGcsaCxpLGosbCxtLG8scD1bZXx8ZF0scT1rLmNhbGwoYiwidHlwZSIpP2IudHlwZTpiLHI9ay5jYWxsKGIsIm5hbWVzcGFjZSIpP2IubmFtZXNwYWNlLnNwbGl0KCIuIik6W107aWYoaD1pPWU9ZXx8ZCwzIT09ZS5ub2RlVHlwZSYmOCE9PWUubm9kZVR5cGUmJiFoYi50ZXN0KHErbi5ldmVudC50cmlnZ2VyZWQpJiYocS5pbmRleE9mKCIuIik+LTEmJihyPXEuc3BsaXQoIi4iKSxxPXIuc2hpZnQoKSxyLnNvcnQoKSksbD1xLmluZGV4T2YoIjoiKTwwJiYib24iK3EsYj1iW24uZXhwYW5kb10/YjpuZXcgbi5FdmVudChxLCJvYmplY3QiPT10eXBlb2YgYiYmYiksYi5pc1RyaWdnZXI9Zj8yOjMsYi5uYW1lc3BhY2U9ci5qb2luKCIuIiksYi5ybmFtZXNwYWNlPWIubmFtZXNwYWNlP25ldyBSZWdFeHAoIihefFxcLikiK3Iuam9pbigiXFwuKD86LipcXC58KSIpKyIoXFwufCQpIik6bnVsbCxiLnJlc3VsdD12b2lkIDAsYi50YXJnZXR8fChiLnRhcmdldD1lKSxjPW51bGw9PWM/W2JdOm4ubWFrZUFycmF5KGMsW2JdKSxvPW4uZXZlbnQuc3BlY2lhbFtxXXx8e30sZnx8IW8udHJpZ2dlcnx8by50cmlnZ2VyLmFwcGx5KGUsYykhPT0hMSkpe2lmKCFmJiYhby5ub0J1YmJsZSYmIW4uaXNXaW5kb3coZSkpe2ZvcihqPW8uZGVsZWdhdGVUeXBlfHxxLGhiLnRlc3QoaitxKXx8KGg9aC5wYXJlbnROb2RlKTtoO2g9aC5wYXJlbnROb2RlKXAucHVzaChoKSxpPWg7aT09PShlLm93bmVyRG9jdW1lbnR8fGQpJiZwLnB1c2goaS5kZWZhdWx0Vmlld3x8aS5wYXJlbnRXaW5kb3d8fGEpfWc9MDt3aGlsZSgoaD1wW2crK10pJiYhYi5pc1Byb3BhZ2F0aW9uU3RvcHBlZCgpKWIudHlwZT1nPjE/ajpvLmJpbmRUeXBlfHxxLG09KE4uZ2V0KGgsImV2ZW50cyIpfHx7fSlbYi50eXBlXSYmTi5nZXQoaCwiaGFuZGxlIiksbSYmbS5hcHBseShoLGMpLG09bCYmaFtsXSxtJiZtLmFwcGx5JiZMKGgpJiYoYi5yZXN1bHQ9bS5hcHBseShoLGMpLGIucmVzdWx0PT09ITEmJmIucHJldmVudERlZmF1bHQoKSk7cmV0dXJuIGIudHlwZT1xLGZ8fGIuaXNEZWZhdWx0UHJldmVudGVkKCl8fG8uX2RlZmF1bHQmJm8uX2RlZmF1bHQuYXBwbHkocC5wb3AoKSxjKSE9PSExfHwhTChlKXx8bCYmbi5pc0Z1bmN0aW9uKGVbcV0pJiYhbi5pc1dpbmRvdyhlKSYmKGk9ZVtsXSxpJiYoZVtsXT1udWxsKSxuLmV2ZW50LnRyaWdnZXJlZD1xLGVbcV0oKSxuLmV2ZW50LnRyaWdnZXJlZD12b2lkIDAsaSYmKGVbbF09aSkpLGIucmVzdWx0fX0sc2ltdWxhdGU6ZnVuY3Rpb24oYSxiLGMpe3ZhciBkPW4uZXh0ZW5kKG5ldyBuLkV2ZW50LGMse3R5cGU6YSxpc1NpbXVsYXRlZDohMH0pO24uZXZlbnQudHJpZ2dlcihkLG51bGwsYiksZC5pc0RlZmF1bHRQcmV2ZW50ZWQoKSYmYy5wcmV2ZW50RGVmYXVsdCgpfX0pLG4uZm4uZXh0ZW5kKHt0cmlnZ2VyOmZ1bmN0aW9uKGEsYil7cmV0dXJuIHRoaXMuZWFjaChmdW5jdGlvbigpe24uZXZlbnQudHJpZ2dlcihhLGIsdGhpcyl9KX0sdHJpZ2dlckhhbmRsZXI6ZnVuY3Rpb24oYSxiKXt2YXIgYz10aGlzWzBdO3JldHVybiBjP24uZXZlbnQudHJpZ2dlcihhLGIsYywhMCk6dm9pZCAwfX0pLG4uZWFjaCgiYmx1ciBmb2N1cyBmb2N1c2luIGZvY3Vzb3V0IGxvYWQgcmVzaXplIHNjcm9sbCB1bmxvYWQgY2xpY2sgZGJsY2xpY2sgbW91c2Vkb3duIG1vdXNldXAgbW91c2Vtb3ZlIG1vdXNlb3ZlciBtb3VzZW91dCBtb3VzZWVudGVyIG1vdXNlbGVhdmUgY2hhbmdlIHNlbGVjdCBzdWJtaXQga2V5ZG93biBrZXlwcmVzcyBrZXl1cCBlcnJvciBjb250ZXh0bWVudSIuc3BsaXQoIiAiKSxmdW5jdGlvbihhLGIpe24uZm5bYl09ZnVuY3Rpb24oYSxjKXtyZXR1cm4gYXJndW1lbnRzLmxlbmd0aD4wP3RoaXMub24oYixudWxsLGEsYyk6dGhpcy50cmlnZ2VyKGIpfX0pLG4uZm4uZXh0ZW5kKHtob3ZlcjpmdW5jdGlvbihhLGIpe3JldHVybiB0aGlzLm1vdXNlZW50ZXIoYSkubW91c2VsZWF2ZShifHxhKX19KSxsLmZvY3VzaW49Im9uZm9jdXNpbiJpbiBhLGwuZm9jdXNpbnx8bi5lYWNoKHtmb2N1czoiZm9jdXNpbiIsYmx1cjoiZm9jdXNvdXQifSxmdW5jdGlvbihhLGIpe3ZhciBjPWZ1bmN0aW9uKGEpe24uZXZlbnQuc2ltdWxhdGUoYixhLnRhcmdldCxuLmV2ZW50LmZpeChhKSl9O24uZXZlbnQuc3BlY2lhbFtiXT17c2V0dXA6ZnVuY3Rpb24oKXt2YXIgZD10aGlzLm93bmVyRG9jdW1lbnR8fHRoaXMsZT1OLmFjY2VzcyhkLGIpO2V8fGQuYWRkRXZlbnRMaXN0ZW5lcihhLGMsITApLE4uYWNjZXNzKGQsYiwoZXx8MCkrMSl9LHRlYXJkb3duOmZ1bmN0aW9uKCl7dmFyIGQ9dGhpcy5vd25lckRvY3VtZW50fHx0aGlzLGU9Ti5hY2Nlc3MoZCxiKS0xO2U/Ti5hY2Nlc3MoZCxiLGUpOihkLnJlbW92ZUV2ZW50TGlzdGVuZXIoYSxjLCEwKSxOLnJlbW92ZShkLGIpKX19fSk7dmFyIGliPWEubG9jYXRpb24samI9bi5ub3coKSxrYj0vXD8vO24ucGFyc2VKU09OPWZ1bmN0aW9uKGEpe3JldHVybiBKU09OLnBhcnNlKGErIiIpfSxuLnBhcnNlWE1MPWZ1bmN0aW9uKGIpe3ZhciBjO2lmKCFifHwic3RyaW5nIiE9dHlwZW9mIGIpcmV0dXJuIG51bGw7dHJ5e2M9KG5ldyBhLkRPTVBhcnNlcikucGFyc2VGcm9tU3RyaW5nKGIsInRleHQveG1sIil9Y2F0Y2goZCl7Yz12b2lkIDB9cmV0dXJuKCFjfHxjLmdldEVsZW1lbnRzQnlUYWdOYW1lKCJwYXJzZXJlcnJvciIpLmxlbmd0aCkmJm4uZXJyb3IoIkludmFsaWQgWE1MOiAiK2IpLGN9O3ZhciBsYj0vIy4qJC8sbWI9LyhbPyZdKV89W14mXSovLG5iPS9eKC4qPyk6WyBcdF0qKFteXHJcbl0qKSQvZ20sb2I9L14oPzphYm91dHxhcHB8YXBwLXN0b3JhZ2V8ListZXh0ZW5zaW9ufGZpbGV8cmVzfHdpZGdldCk6JC8scGI9L14oPzpHRVR8SEVBRCkkLyxxYj0vXlwvXC8vLHJiPXt9LHNiPXt9LHRiPSIqLyIuY29uY2F0KCIqIiksdWI9ZC5jcmVhdGVFbGVtZW50KCJhIik7dWIuaHJlZj1pYi5ocmVmO2Z1bmN0aW9uIHZiKGEpe3JldHVybiBmdW5jdGlvbihiLGMpeyJzdHJpbmciIT10eXBlb2YgYiYmKGM9YixiPSIqIik7dmFyIGQsZT0wLGY9Yi50b0xvd2VyQ2FzZSgpLm1hdGNoKEcpfHxbXTtpZihuLmlzRnVuY3Rpb24oYykpd2hpbGUoZD1mW2UrK10pIisiPT09ZFswXT8oZD1kLnNsaWNlKDEpfHwiKiIsKGFbZF09YVtkXXx8W10pLnVuc2hpZnQoYykpOihhW2RdPWFbZF18fFtdKS5wdXNoKGMpfX1mdW5jdGlvbiB3YihhLGIsYyxkKXt2YXIgZT17fSxmPWE9PT1zYjtmdW5jdGlvbiBnKGgpe3ZhciBpO3JldHVybiBlW2hdPSEwLG4uZWFjaChhW2hdfHxbXSxmdW5jdGlvbihhLGgpe3ZhciBqPWgoYixjLGQpO3JldHVybiJzdHJpbmciIT10eXBlb2Yganx8Znx8ZVtqXT9mPyEoaT1qKTp2b2lkIDA6KGIuZGF0YVR5cGVzLnVuc2hpZnQoaiksZyhqKSwhMSl9KSxpfXJldHVybiBnKGIuZGF0YVR5cGVzWzBdKXx8IWVbIioiXSYmZygiKiIpfWZ1bmN0aW9uIHhiKGEsYil7dmFyIGMsZCxlPW4uYWpheFNldHRpbmdzLmZsYXRPcHRpb25zfHx7fTtmb3IoYyBpbiBiKXZvaWQgMCE9PWJbY10mJigoZVtjXT9hOmR8fChkPXt9KSlbY109YltjXSk7cmV0dXJuIGQmJm4uZXh0ZW5kKCEwLGEsZCksYX1mdW5jdGlvbiB5YihhLGIsYyl7dmFyIGQsZSxmLGcsaD1hLmNvbnRlbnRzLGk9YS5kYXRhVHlwZXM7d2hpbGUoIioiPT09aVswXSlpLnNoaWZ0KCksdm9pZCAwPT09ZCYmKGQ9YS5taW1lVHlwZXx8Yi5nZXRSZXNwb25zZUhlYWRlcigiQ29udGVudC1UeXBlIikpO2lmKGQpZm9yKGUgaW4gaClpZihoW2VdJiZoW2VdLnRlc3QoZCkpe2kudW5zaGlmdChlKTticmVha31pZihpWzBdaW4gYylmPWlbMF07ZWxzZXtmb3IoZSBpbiBjKXtpZighaVswXXx8YS5jb252ZXJ0ZXJzW2UrIiAiK2lbMF1dKXtmPWU7YnJlYWt9Z3x8KGc9ZSl9Zj1mfHxnfXJldHVybiBmPyhmIT09aVswXSYmaS51bnNoaWZ0KGYpLGNbZl0pOnZvaWQgMH1mdW5jdGlvbiB6YihhLGIsYyxkKXt2YXIgZSxmLGcsaCxpLGo9e30saz1hLmRhdGFUeXBlcy5zbGljZSgpO2lmKGtbMV0pZm9yKGcgaW4gYS5jb252ZXJ0ZXJzKWpbZy50b0xvd2VyQ2FzZSgpXT1hLmNvbnZlcnRlcnNbZ107Zj1rLnNoaWZ0KCk7d2hpbGUoZilpZihhLnJlc3BvbnNlRmllbGRzW2ZdJiYoY1thLnJlc3BvbnNlRmllbGRzW2ZdXT1iKSwhaSYmZCYmYS5kYXRhRmlsdGVyJiYoYj1hLmRhdGFGaWx0ZXIoYixhLmRhdGFUeXBlKSksaT1mLGY9ay5zaGlmdCgpKWlmKCIqIj09PWYpZj1pO2Vsc2UgaWYoIioiIT09aSYmaSE9PWYpe2lmKGc9altpKyIgIitmXXx8alsiKiAiK2ZdLCFnKWZvcihlIGluIGopaWYoaD1lLnNwbGl0KCIgIiksaFsxXT09PWYmJihnPWpbaSsiICIraFswXV18fGpbIiogIitoWzBdXSkpe2c9PT0hMD9nPWpbZV06altlXSE9PSEwJiYoZj1oWzBdLGsudW5zaGlmdChoWzFdKSk7YnJlYWt9aWYoZyE9PSEwKWlmKGcmJmFbInRocm93cyJdKWI9ZyhiKTtlbHNlIHRyeXtiPWcoYil9Y2F0Y2gobCl7cmV0dXJue3N0YXRlOiJwYXJzZXJlcnJvciIsZXJyb3I6Zz9sOiJObyBjb252ZXJzaW9uIGZyb20gIitpKyIgdG8gIitmfX19cmV0dXJue3N0YXRlOiJzdWNjZXNzIixkYXRhOmJ9fW4uZXh0ZW5kKHthY3RpdmU6MCxsYXN0TW9kaWZpZWQ6e30sZXRhZzp7fSxhamF4U2V0dGluZ3M6e3VybDppYi5ocmVmLHR5cGU6IkdFVCIsaXNMb2NhbDpvYi50ZXN0KGliLnByb3RvY29sKSxnbG9iYWw6ITAscHJvY2Vzc0RhdGE6ITAsYXN5bmM6ITAsY29udGVudFR5cGU6ImFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZDsgY2hhcnNldD1VVEYtOCIsYWNjZXB0czp7IioiOnRiLHRleHQ6InRleHQvcGxhaW4iLGh0bWw6InRleHQvaHRtbCIseG1sOiJhcHBsaWNhdGlvbi94bWwsIHRleHQveG1sIixqc29uOiJhcHBsaWNhdGlvbi9qc29uLCB0ZXh0L2phdmFzY3JpcHQifSxjb250ZW50czp7eG1sOi9cYnhtbFxiLyxodG1sOi9cYmh0bWwvLGpzb246L1xianNvblxiL30scmVzcG9uc2VGaWVsZHM6e3htbDoicmVzcG9uc2VYTUwiLHRleHQ6InJlc3BvbnNlVGV4dCIsanNvbjoicmVzcG9uc2VKU09OIn0sY29udmVydGVyczp7IiogdGV4dCI6U3RyaW5nLCJ0ZXh0IGh0bWwiOiEwLCJ0ZXh0IGpzb24iOm4ucGFyc2VKU09OLCJ0ZXh0IHhtbCI6bi5wYXJzZVhNTH0sZmxhdE9wdGlvbnM6e3VybDohMCxjb250ZXh0OiEwfX0sYWpheFNldHVwOmZ1bmN0aW9uKGEsYil7cmV0dXJuIGI/eGIoeGIoYSxuLmFqYXhTZXR0aW5ncyksYik6eGIobi5hamF4U2V0dGluZ3MsYSl9LGFqYXhQcmVmaWx0ZXI6dmIocmIpLGFqYXhUcmFuc3BvcnQ6dmIoc2IpLGFqYXg6ZnVuY3Rpb24oYixjKXsib2JqZWN0Ij09dHlwZW9mIGImJihjPWIsYj12b2lkIDApLGM9Y3x8e307dmFyIGUsZixnLGgsaSxqLGssbCxtPW4uYWpheFNldHVwKHt9LGMpLG89bS5jb250ZXh0fHxtLHA9bS5jb250ZXh0JiYoby5ub2RlVHlwZXx8by5qcXVlcnkpP24obyk6bi5ldmVudCxxPW4uRGVmZXJyZWQoKSxyPW4uQ2FsbGJhY2tzKCJvbmNlIG1lbW9yeSIpLHM9bS5zdGF0dXNDb2RlfHx7fSx0PXt9LHU9e30sdj0wLHc9ImNhbmNlbGVkIix4PXtyZWFkeVN0YXRlOjAsZ2V0UmVzcG9uc2VIZWFkZXI6ZnVuY3Rpb24oYSl7dmFyIGI7aWYoMj09PXYpe2lmKCFoKXtoPXt9O3doaWxlKGI9bmIuZXhlYyhnKSloW2JbMV0udG9Mb3dlckNhc2UoKV09YlsyXX1iPWhbYS50b0xvd2VyQ2FzZSgpXX1yZXR1cm4gbnVsbD09Yj9udWxsOmJ9LGdldEFsbFJlc3BvbnNlSGVhZGVyczpmdW5jdGlvbigpe3JldHVybiAyPT09dj9nOm51bGx9LHNldFJlcXVlc3RIZWFkZXI6ZnVuY3Rpb24oYSxiKXt2YXIgYz1hLnRvTG93ZXJDYXNlKCk7cmV0dXJuIHZ8fChhPXVbY109dVtjXXx8YSx0W2FdPWIpLHRoaXN9LG92ZXJyaWRlTWltZVR5cGU6ZnVuY3Rpb24oYSl7cmV0dXJuIHZ8fChtLm1pbWVUeXBlPWEpLHRoaXN9LHN0YXR1c0NvZGU6ZnVuY3Rpb24oYSl7dmFyIGI7aWYoYSlpZigyPnYpZm9yKGIgaW4gYSlzW2JdPVtzW2JdLGFbYl1dO2Vsc2UgeC5hbHdheXMoYVt4LnN0YXR1c10pO3JldHVybiB0aGlzfSxhYm9ydDpmdW5jdGlvbihhKXt2YXIgYj1hfHx3O3JldHVybiBlJiZlLmFib3J0KGIpLHooMCxiKSx0aGlzfX07aWYocS5wcm9taXNlKHgpLmNvbXBsZXRlPXIuYWRkLHguc3VjY2Vzcz14LmRvbmUseC5lcnJvcj14LmZhaWwsbS51cmw9KChifHxtLnVybHx8aWIuaHJlZikrIiIpLnJlcGxhY2UobGIsIiIpLnJlcGxhY2UocWIsaWIucHJvdG9jb2wrIi8vIiksbS50eXBlPWMubWV0aG9kfHxjLnR5cGV8fG0ubWV0aG9kfHxtLnR5cGUsbS5kYXRhVHlwZXM9bi50cmltKG0uZGF0YVR5cGV8fCIqIikudG9Mb3dlckNhc2UoKS5tYXRjaChHKXx8WyIiXSxudWxsPT1tLmNyb3NzRG9tYWluKXtqPWQuY3JlYXRlRWxlbWVudCgiYSIpO3RyeXtqLmhyZWY9bS51cmwsai5ocmVmPWouaHJlZixtLmNyb3NzRG9tYWluPXViLnByb3RvY29sKyIvLyIrdWIuaG9zdCE9ai5wcm90b2NvbCsiLy8iK2ouaG9zdH1jYXRjaCh5KXttLmNyb3NzRG9tYWluPSEwfX1pZihtLmRhdGEmJm0ucHJvY2Vzc0RhdGEmJiJzdHJpbmciIT10eXBlb2YgbS5kYXRhJiYobS5kYXRhPW4ucGFyYW0obS5kYXRhLG0udHJhZGl0aW9uYWwpKSx3YihyYixtLGMseCksMj09PXYpcmV0dXJuIHg7az1uLmV2ZW50JiZtLmdsb2JhbCxrJiYwPT09bi5hY3RpdmUrKyYmbi5ldmVudC50cmlnZ2VyKCJhamF4U3RhcnQiKSxtLnR5cGU9bS50eXBlLnRvVXBwZXJDYXNlKCksbS5oYXNDb250ZW50PSFwYi50ZXN0KG0udHlwZSksZj1tLnVybCxtLmhhc0NvbnRlbnR8fChtLmRhdGEmJihmPW0udXJsKz0oa2IudGVzdChmKT8iJiI6Ij8iKSttLmRhdGEsZGVsZXRlIG0uZGF0YSksbS5jYWNoZT09PSExJiYobS51cmw9bWIudGVzdChmKT9mLnJlcGxhY2UobWIsIiQxXz0iK2piKyspOmYrKGtiLnRlc3QoZik/IiYiOiI/IikrIl89IitqYisrKSksbS5pZk1vZGlmaWVkJiYobi5sYXN0TW9kaWZpZWRbZl0mJnguc2V0UmVxdWVzdEhlYWRlcigiSWYtTW9kaWZpZWQtU2luY2UiLG4ubGFzdE1vZGlmaWVkW2ZdKSxuLmV0YWdbZl0mJnguc2V0UmVxdWVzdEhlYWRlcigiSWYtTm9uZS1NYXRjaCIsbi5ldGFnW2ZdKSksKG0uZGF0YSYmbS5oYXNDb250ZW50JiZtLmNvbnRlbnRUeXBlIT09ITF8fGMuY29udGVudFR5cGUpJiZ4LnNldFJlcXVlc3RIZWFkZXIoIkNvbnRlbnQtVHlwZSIsbS5jb250ZW50VHlwZSkseC5zZXRSZXF1ZXN0SGVhZGVyKCJBY2NlcHQiLG0uZGF0YVR5cGVzWzBdJiZtLmFjY2VwdHNbbS5kYXRhVHlwZXNbMF1dP20uYWNjZXB0c1ttLmRhdGFUeXBlc1swXV0rKCIqIiE9PW0uZGF0YVR5cGVzWzBdPyIsICIrdGIrIjsgcT0wLjAxIjoiIik6bS5hY2NlcHRzWyIqIl0pO2ZvcihsIGluIG0uaGVhZGVycyl4LnNldFJlcXVlc3RIZWFkZXIobCxtLmhlYWRlcnNbbF0pO2lmKG0uYmVmb3JlU2VuZCYmKG0uYmVmb3JlU2VuZC5jYWxsKG8seCxtKT09PSExfHwyPT09dikpcmV0dXJuIHguYWJvcnQoKTt3PSJhYm9ydCI7Zm9yKGwgaW57c3VjY2VzczoxLGVycm9yOjEsY29tcGxldGU6MX0peFtsXShtW2xdKTtpZihlPXdiKHNiLG0sYyx4KSl7aWYoeC5yZWFkeVN0YXRlPTEsayYmcC50cmlnZ2VyKCJhamF4U2VuZCIsW3gsbV0pLDI9PT12KXJldHVybiB4O20uYXN5bmMmJm0udGltZW91dD4wJiYoaT1hLnNldFRpbWVvdXQoZnVuY3Rpb24oKXt4LmFib3J0KCJ0aW1lb3V0Iil9LG0udGltZW91dCkpO3RyeXt2PTEsZS5zZW5kKHQseil9Y2F0Y2goeSl7aWYoISgyPnYpKXRocm93IHk7eigtMSx5KX19ZWxzZSB6KC0xLCJObyBUcmFuc3BvcnQiKTtmdW5jdGlvbiB6KGIsYyxkLGgpe3ZhciBqLGwsdCx1LHcseT1jOzIhPT12JiYodj0yLGkmJmEuY2xlYXJUaW1lb3V0KGkpLGU9dm9pZCAwLGc9aHx8IiIseC5yZWFkeVN0YXRlPWI+MD80OjAsaj1iPj0yMDAmJjMwMD5ifHwzMDQ9PT1iLGQmJih1PXliKG0seCxkKSksdT16YihtLHUseCxqKSxqPyhtLmlmTW9kaWZpZWQmJih3PXguZ2V0UmVzcG9uc2VIZWFkZXIoIkxhc3QtTW9kaWZpZWQiKSx3JiYobi5sYXN0TW9kaWZpZWRbZl09dyksdz14LmdldFJlc3BvbnNlSGVhZGVyKCJldGFnIiksdyYmKG4uZXRhZ1tmXT13KSksMjA0PT09Ynx8IkhFQUQiPT09bS50eXBlP3k9Im5vY29udGVudCI6MzA0PT09Yj95PSJub3Rtb2RpZmllZCI6KHk9dS5zdGF0ZSxsPXUuZGF0YSx0PXUuZXJyb3Isaj0hdCkpOih0PXksKGJ8fCF5KSYmKHk9ImVycm9yIiwwPmImJihiPTApKSkseC5zdGF0dXM9Yix4LnN0YXR1c1RleHQ9KGN8fHkpKyIiLGo/cS5yZXNvbHZlV2l0aChvLFtsLHkseF0pOnEucmVqZWN0V2l0aChvLFt4LHksdF0pLHguc3RhdHVzQ29kZShzKSxzPXZvaWQgMCxrJiZwLnRyaWdnZXIoaj8iYWpheFN1Y2Nlc3MiOiJhamF4RXJyb3IiLFt4LG0saj9sOnRdKSxyLmZpcmVXaXRoKG8sW3gseV0pLGsmJihwLnRyaWdnZXIoImFqYXhDb21wbGV0ZSIsW3gsbV0pLC0tbi5hY3RpdmV8fG4uZXZlbnQudHJpZ2dlcigiYWpheFN0b3AiKSkpfXJldHVybiB4fSxnZXRKU09OOmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gbi5nZXQoYSxiLGMsImpzb24iKX0sZ2V0U2NyaXB0OmZ1bmN0aW9uKGEsYil7cmV0dXJuIG4uZ2V0KGEsdm9pZCAwLGIsInNjcmlwdCIpfX0pLG4uZWFjaChbImdldCIsInBvc3QiXSxmdW5jdGlvbihhLGIpe25bYl09ZnVuY3Rpb24oYSxjLGQsZSl7cmV0dXJuIG4uaXNGdW5jdGlvbihjKSYmKGU9ZXx8ZCxkPWMsYz12b2lkIDApLG4uYWpheChuLmV4dGVuZCh7dXJsOmEsdHlwZTpiLGRhdGFUeXBlOmUsZGF0YTpjLHN1Y2Nlc3M6ZH0sbi5pc1BsYWluT2JqZWN0KGEpJiZhKSl9fSksbi5fZXZhbFVybD1mdW5jdGlvbihhKXtyZXR1cm4gbi5hamF4KHt1cmw6YSx0eXBlOiJHRVQiLGRhdGFUeXBlOiJzY3JpcHQiLGFzeW5jOiExLGdsb2JhbDohMSwidGhyb3dzIjohMH0pfSxuLmZuLmV4dGVuZCh7d3JhcEFsbDpmdW5jdGlvbihhKXt2YXIgYjtyZXR1cm4gbi5pc0Z1bmN0aW9uKGEpP3RoaXMuZWFjaChmdW5jdGlvbihiKXtuKHRoaXMpLndyYXBBbGwoYS5jYWxsKHRoaXMsYikpfSk6KHRoaXNbMF0mJihiPW4oYSx0aGlzWzBdLm93bmVyRG9jdW1lbnQpLmVxKDApLmNsb25lKCEwKSx0aGlzWzBdLnBhcmVudE5vZGUmJmIuaW5zZXJ0QmVmb3JlKHRoaXNbMF0pLGIubWFwKGZ1bmN0aW9uKCl7dmFyIGE9dGhpczt3aGlsZShhLmZpcnN0RWxlbWVudENoaWxkKWE9YS5maXJzdEVsZW1lbnRDaGlsZDtyZXR1cm4gYX0pLmFwcGVuZCh0aGlzKSksdGhpcyl9LHdyYXBJbm5lcjpmdW5jdGlvbihhKXtyZXR1cm4gbi5pc0Z1bmN0aW9uKGEpP3RoaXMuZWFjaChmdW5jdGlvbihiKXtuKHRoaXMpLndyYXBJbm5lcihhLmNhbGwodGhpcyxiKSl9KTp0aGlzLmVhY2goZnVuY3Rpb24oKXt2YXIgYj1uKHRoaXMpLGM9Yi5jb250ZW50cygpO2MubGVuZ3RoP2Mud3JhcEFsbChhKTpiLmFwcGVuZChhKX0pfSx3cmFwOmZ1bmN0aW9uKGEpe3ZhciBiPW4uaXNGdW5jdGlvbihhKTtyZXR1cm4gdGhpcy5lYWNoKGZ1bmN0aW9uKGMpe24odGhpcykud3JhcEFsbChiP2EuY2FsbCh0aGlzLGMpOmEpfSl9LHVud3JhcDpmdW5jdGlvbigpe3JldHVybiB0aGlzLnBhcmVudCgpLmVhY2goZnVuY3Rpb24oKXtuLm5vZGVOYW1lKHRoaXMsImJvZHkiKXx8bih0aGlzKS5yZXBsYWNlV2l0aCh0aGlzLmNoaWxkTm9kZXMpfSkuZW5kKCl9fSksbi5leHByLmZpbHRlcnMuaGlkZGVuPWZ1bmN0aW9uKGEpe3JldHVybiFuLmV4cHIuZmlsdGVycy52aXNpYmxlKGEpfSxuLmV4cHIuZmlsdGVycy52aXNpYmxlPWZ1bmN0aW9uKGEpe3JldHVybiBhLm9mZnNldFdpZHRoPjB8fGEub2Zmc2V0SGVpZ2h0PjB8fGEuZ2V0Q2xpZW50UmVjdHMoKS5sZW5ndGg+MH07dmFyIEFiPS8lMjAvZyxCYj0vXFtcXSQvLENiPS9ccj9cbi9nLERiPS9eKD86c3VibWl0fGJ1dHRvbnxpbWFnZXxyZXNldHxmaWxlKSQvaSxFYj0vXig/OmlucHV0fHNlbGVjdHx0ZXh0YXJlYXxrZXlnZW4pL2k7ZnVuY3Rpb24gRmIoYSxiLGMsZCl7dmFyIGU7aWYobi5pc0FycmF5KGIpKW4uZWFjaChiLGZ1bmN0aW9uKGIsZSl7Y3x8QmIudGVzdChhKT9kKGEsZSk6RmIoYSsiWyIrKCJvYmplY3QiPT10eXBlb2YgZSYmbnVsbCE9ZT9iOiIiKSsiXSIsZSxjLGQpfSk7ZWxzZSBpZihjfHwib2JqZWN0IiE9PW4udHlwZShiKSlkKGEsYik7ZWxzZSBmb3IoZSBpbiBiKUZiKGErIlsiK2UrIl0iLGJbZV0sYyxkKX1uLnBhcmFtPWZ1bmN0aW9uKGEsYil7dmFyIGMsZD1bXSxlPWZ1bmN0aW9uKGEsYil7Yj1uLmlzRnVuY3Rpb24oYik/YigpOm51bGw9PWI/IiI6YixkW2QubGVuZ3RoXT1lbmNvZGVVUklDb21wb25lbnQoYSkrIj0iK2VuY29kZVVSSUNvbXBvbmVudChiKX07aWYodm9pZCAwPT09YiYmKGI9bi5hamF4U2V0dGluZ3MmJm4uYWpheFNldHRpbmdzLnRyYWRpdGlvbmFsKSxuLmlzQXJyYXkoYSl8fGEuanF1ZXJ5JiYhbi5pc1BsYWluT2JqZWN0KGEpKW4uZWFjaChhLGZ1bmN0aW9uKCl7ZSh0aGlzLm5hbWUsdGhpcy52YWx1ZSl9KTtlbHNlIGZvcihjIGluIGEpRmIoYyxhW2NdLGIsZSk7cmV0dXJuIGQuam9pbigiJiIpLnJlcGxhY2UoQWIsIisiKX0sbi5mbi5leHRlbmQoe3NlcmlhbGl6ZTpmdW5jdGlvbigpe3JldHVybiBuLnBhcmFtKHRoaXMuc2VyaWFsaXplQXJyYXkoKSl9LHNlcmlhbGl6ZUFycmF5OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMubWFwKGZ1bmN0aW9uKCl7dmFyIGE9bi5wcm9wKHRoaXMsImVsZW1lbnRzIik7cmV0dXJuIGE/bi5tYWtlQXJyYXkoYSk6dGhpc30pLmZpbHRlcihmdW5jdGlvbigpe3ZhciBhPXRoaXMudHlwZTtyZXR1cm4gdGhpcy5uYW1lJiYhbih0aGlzKS5pcygiOmRpc2FibGVkIikmJkViLnRlc3QodGhpcy5ub2RlTmFtZSkmJiFEYi50ZXN0KGEpJiYodGhpcy5jaGVja2VkfHwhWC50ZXN0KGEpKX0pLm1hcChmdW5jdGlvbihhLGIpe3ZhciBjPW4odGhpcykudmFsKCk7cmV0dXJuIG51bGw9PWM/bnVsbDpuLmlzQXJyYXkoYyk/bi5tYXAoYyxmdW5jdGlvbihhKXtyZXR1cm57bmFtZTpiLm5hbWUsdmFsdWU6YS5yZXBsYWNlKENiLCJcclxuIil9fSk6e25hbWU6Yi5uYW1lLHZhbHVlOmMucmVwbGFjZShDYiwiXHJcbiIpfX0pLmdldCgpfX0pLG4uYWpheFNldHRpbmdzLnhocj1mdW5jdGlvbigpe3RyeXtyZXR1cm4gbmV3IGEuWE1MSHR0cFJlcXVlc3R9Y2F0Y2goYil7fX07dmFyIEdiPXswOjIwMCwxMjIzOjIwNH0sSGI9bi5hamF4U2V0dGluZ3MueGhyKCk7bC5jb3JzPSEhSGImJiJ3aXRoQ3JlZGVudGlhbHMiaW4gSGIsbC5hamF4PUhiPSEhSGIsbi5hamF4VHJhbnNwb3J0KGZ1bmN0aW9uKGIpe3ZhciBjLGQ7cmV0dXJuIGwuY29yc3x8SGImJiFiLmNyb3NzRG9tYWluP3tzZW5kOmZ1bmN0aW9uKGUsZil7dmFyIGcsaD1iLnhocigpO2lmKGgub3BlbihiLnR5cGUsYi51cmwsYi5hc3luYyxiLnVzZXJuYW1lLGIucGFzc3dvcmQpLGIueGhyRmllbGRzKWZvcihnIGluIGIueGhyRmllbGRzKWhbZ109Yi54aHJGaWVsZHNbZ107Yi5taW1lVHlwZSYmaC5vdmVycmlkZU1pbWVUeXBlJiZoLm92ZXJyaWRlTWltZVR5cGUoYi5taW1lVHlwZSksYi5jcm9zc0RvbWFpbnx8ZVsiWC1SZXF1ZXN0ZWQtV2l0aCJdfHwoZVsiWC1SZXF1ZXN0ZWQtV2l0aCJdPSJYTUxIdHRwUmVxdWVzdCIpO2ZvcihnIGluIGUpaC5zZXRSZXF1ZXN0SGVhZGVyKGcsZVtnXSk7Yz1mdW5jdGlvbihhKXtyZXR1cm4gZnVuY3Rpb24oKXtjJiYoYz1kPWgub25sb2FkPWgub25lcnJvcj1oLm9uYWJvcnQ9aC5vbnJlYWR5c3RhdGVjaGFuZ2U9bnVsbCwiYWJvcnQiPT09YT9oLmFib3J0KCk6ImVycm9yIj09PWE/Im51bWJlciIhPXR5cGVvZiBoLnN0YXR1cz9mKDAsImVycm9yIik6ZihoLnN0YXR1cyxoLnN0YXR1c1RleHQpOmYoR2JbaC5zdGF0dXNdfHxoLnN0YXR1cyxoLnN0YXR1c1RleHQsInRleHQiIT09KGgucmVzcG9uc2VUeXBlfHwidGV4dCIpfHwic3RyaW5nIiE9dHlwZW9mIGgucmVzcG9uc2VUZXh0P3tiaW5hcnk6aC5yZXNwb25zZX06e3RleHQ6aC5yZXNwb25zZVRleHR9LGguZ2V0QWxsUmVzcG9uc2VIZWFkZXJzKCkpKX19LGgub25sb2FkPWMoKSxkPWgub25lcnJvcj1jKCJlcnJvciIpLHZvaWQgMCE9PWgub25hYm9ydD9oLm9uYWJvcnQ9ZDpoLm9ucmVhZHlzdGF0ZWNoYW5nZT1mdW5jdGlvbigpezQ9PT1oLnJlYWR5U3RhdGUmJmEuc2V0VGltZW91dChmdW5jdGlvbigpe2MmJmQoKX0pfSxjPWMoImFib3J0Iik7dHJ5e2guc2VuZChiLmhhc0NvbnRlbnQmJmIuZGF0YXx8bnVsbCl9Y2F0Y2goaSl7aWYoYyl0aHJvdyBpfX0sYWJvcnQ6ZnVuY3Rpb24oKXtjJiZjKCl9fTp2b2lkIDB9KSxuLmFqYXhTZXR1cCh7YWNjZXB0czp7c2NyaXB0OiJ0ZXh0L2phdmFzY3JpcHQsIGFwcGxpY2F0aW9uL2phdmFzY3JpcHQsIGFwcGxpY2F0aW9uL2VjbWFzY3JpcHQsIGFwcGxpY2F0aW9uL3gtZWNtYXNjcmlwdCJ9LGNvbnRlbnRzOntzY3JpcHQ6L1xiKD86amF2YXxlY21hKXNjcmlwdFxiL30sY29udmVydGVyczp7InRleHQgc2NyaXB0IjpmdW5jdGlvbihhKXtyZXR1cm4gbi5nbG9iYWxFdmFsKGEpLGF9fX0pLG4uYWpheFByZWZpbHRlcigic2NyaXB0IixmdW5jdGlvbihhKXt2b2lkIDA9PT1hLmNhY2hlJiYoYS5jYWNoZT0hMSksYS5jcm9zc0RvbWFpbiYmKGEudHlwZT0iR0VUIil9KSxuLmFqYXhUcmFuc3BvcnQoInNjcmlwdCIsZnVuY3Rpb24oYSl7aWYoYS5jcm9zc0RvbWFpbil7dmFyIGIsYztyZXR1cm57c2VuZDpmdW5jdGlvbihlLGYpe2I9bigiPHNjcmlwdD4iKS5wcm9wKHtjaGFyc2V0OmEuc2NyaXB0Q2hhcnNldCxzcmM6YS51cmx9KS5vbigibG9hZCBlcnJvciIsYz1mdW5jdGlvbihhKXtiLnJlbW92ZSgpLGM9bnVsbCxhJiZmKCJlcnJvciI9PT1hLnR5cGU/NDA0OjIwMCxhLnR5cGUpfSksZC5oZWFkLmFwcGVuZENoaWxkKGJbMF0pfSxhYm9ydDpmdW5jdGlvbigpe2MmJmMoKX19fX0pO3ZhciBJYj1bXSxKYj0vKD0pXD8oPz0mfCQpfFw/XD8vO24uYWpheFNldHVwKHtqc29ucDoiY2FsbGJhY2siLGpzb25wQ2FsbGJhY2s6ZnVuY3Rpb24oKXt2YXIgYT1JYi5wb3AoKXx8bi5leHBhbmRvKyJfIitqYisrO3JldHVybiB0aGlzW2FdPSEwLGF9fSksbi5hamF4UHJlZmlsdGVyKCJqc29uIGpzb25wIixmdW5jdGlvbihiLGMsZCl7dmFyIGUsZixnLGg9Yi5qc29ucCE9PSExJiYoSmIudGVzdChiLnVybCk/InVybCI6InN0cmluZyI9PXR5cGVvZiBiLmRhdGEmJjA9PT0oYi5jb250ZW50VHlwZXx8IiIpLmluZGV4T2YoImFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCIpJiZKYi50ZXN0KGIuZGF0YSkmJiJkYXRhIik7cmV0dXJuIGh8fCJqc29ucCI9PT1iLmRhdGFUeXBlc1swXT8oZT1iLmpzb25wQ2FsbGJhY2s9bi5pc0Z1bmN0aW9uKGIuanNvbnBDYWxsYmFjayk/Yi5qc29ucENhbGxiYWNrKCk6Yi5qc29ucENhbGxiYWNrLGg/YltoXT1iW2hdLnJlcGxhY2UoSmIsIiQxIitlKTpiLmpzb25wIT09ITEmJihiLnVybCs9KGtiLnRlc3QoYi51cmwpPyImIjoiPyIpK2IuanNvbnArIj0iK2UpLGIuY29udmVydGVyc1sic2NyaXB0IGpzb24iXT1mdW5jdGlvbigpe3JldHVybiBnfHxuLmVycm9yKGUrIiB3YXMgbm90IGNhbGxlZCIpLGdbMF19LGIuZGF0YVR5cGVzWzBdPSJqc29uIixmPWFbZV0sYVtlXT1mdW5jdGlvbigpe2c9YXJndW1lbnRzfSxkLmFsd2F5cyhmdW5jdGlvbigpe3ZvaWQgMD09PWY/bihhKS5yZW1vdmVQcm9wKGUpOmFbZV09ZixiW2VdJiYoYi5qc29ucENhbGxiYWNrPWMuanNvbnBDYWxsYmFjayxJYi5wdXNoKGUpKSxnJiZuLmlzRnVuY3Rpb24oZikmJmYoZ1swXSksZz1mPXZvaWQgMH0pLCJzY3JpcHQiKTp2b2lkIDB9KSxsLmNyZWF0ZUhUTUxEb2N1bWVudD1mdW5jdGlvbigpe3ZhciBhPWQuaW1wbGVtZW50YXRpb24uY3JlYXRlSFRNTERvY3VtZW50KCIiKS5ib2R5O3JldHVybiBhLmlubmVySFRNTD0iPGZvcm0+PC9mb3JtPjxmb3JtPjwvZm9ybT4iLDI9PT1hLmNoaWxkTm9kZXMubGVuZ3RofSgpLG4ucGFyc2VIVE1MPWZ1bmN0aW9uKGEsYixjKXtpZighYXx8InN0cmluZyIhPXR5cGVvZiBhKXJldHVybiBudWxsOyJib29sZWFuIj09dHlwZW9mIGImJihjPWIsYj0hMSksYj1ifHwobC5jcmVhdGVIVE1MRG9jdW1lbnQ/ZC5pbXBsZW1lbnRhdGlvbi5jcmVhdGVIVE1MRG9jdW1lbnQoIiIpOmQpO3ZhciBlPXguZXhlYyhhKSxmPSFjJiZbXTtyZXR1cm4gZT9bYi5jcmVhdGVFbGVtZW50KGVbMV0pXTooZT1jYShbYV0sYixmKSxmJiZmLmxlbmd0aCYmbihmKS5yZW1vdmUoKSxuLm1lcmdlKFtdLGUuY2hpbGROb2RlcykpfTt2YXIgS2I9bi5mbi5sb2FkO24uZm4ubG9hZD1mdW5jdGlvbihhLGIsYyl7aWYoInN0cmluZyIhPXR5cGVvZiBhJiZLYilyZXR1cm4gS2IuYXBwbHkodGhpcyxhcmd1bWVudHMpO3ZhciBkLGUsZixnPXRoaXMsaD1hLmluZGV4T2YoIiAiKTtyZXR1cm4gaD4tMSYmKGQ9bi50cmltKGEuc2xpY2UoaCkpLGE9YS5zbGljZSgwLGgpKSxuLmlzRnVuY3Rpb24oYik/KGM9YixiPXZvaWQgMCk6YiYmIm9iamVjdCI9PXR5cGVvZiBiJiYoZT0iUE9TVCIpLGcubGVuZ3RoPjAmJm4uYWpheCh7dXJsOmEsdHlwZTplfHwiR0VUIixkYXRhVHlwZToiaHRtbCIsZGF0YTpifSkuZG9uZShmdW5jdGlvbihhKXtmPWFyZ3VtZW50cyxnLmh0bWwoZD9uKCI8ZGl2PiIpLmFwcGVuZChuLnBhcnNlSFRNTChhKSkuZmluZChkKTphKX0pLmFsd2F5cyhjJiZmdW5jdGlvbihhLGIpe2cuZWFjaChmdW5jdGlvbigpe2MuYXBwbHkoZyxmfHxbYS5yZXNwb25zZVRleHQsYixhXSl9KX0pLHRoaXN9LG4uZWFjaChbImFqYXhTdGFydCIsImFqYXhTdG9wIiwiYWpheENvbXBsZXRlIiwiYWpheEVycm9yIiwiYWpheFN1Y2Nlc3MiLCJhamF4U2VuZCJdLGZ1bmN0aW9uKGEsYil7bi5mbltiXT1mdW5jdGlvbihhKXtyZXR1cm4gdGhpcy5vbihiLGEpfX0pLG4uZXhwci5maWx0ZXJzLmFuaW1hdGVkPWZ1bmN0aW9uKGEpe3JldHVybiBuLmdyZXAobi50aW1lcnMsZnVuY3Rpb24oYil7cmV0dXJuIGE9PT1iLmVsZW19KS5sZW5ndGh9O2Z1bmN0aW9uIExiKGEpe3JldHVybiBuLmlzV2luZG93KGEpP2E6OT09PWEubm9kZVR5cGUmJmEuZGVmYXVsdFZpZXd9bi5vZmZzZXQ9e3NldE9mZnNldDpmdW5jdGlvbihhLGIsYyl7dmFyIGQsZSxmLGcsaCxpLGosaz1uLmNzcyhhLCJwb3NpdGlvbiIpLGw9bihhKSxtPXt9OyJzdGF0aWMiPT09ayYmKGEuc3R5bGUucG9zaXRpb249InJlbGF0aXZlIiksaD1sLm9mZnNldCgpLGY9bi5jc3MoYSwidG9wIiksaT1uLmNzcyhhLCJsZWZ0Iiksaj0oImFic29sdXRlIj09PWt8fCJmaXhlZCI9PT1rKSYmKGYraSkuaW5kZXhPZigiYXV0byIpPi0xLGo/KGQ9bC5wb3NpdGlvbigpLGc9ZC50b3AsZT1kLmxlZnQpOihnPXBhcnNlRmxvYXQoZil8fDAsZT1wYXJzZUZsb2F0KGkpfHwwKSxuLmlzRnVuY3Rpb24oYikmJihiPWIuY2FsbChhLGMsbi5leHRlbmQoe30saCkpKSxudWxsIT1iLnRvcCYmKG0udG9wPWIudG9wLWgudG9wK2cpLG51bGwhPWIubGVmdCYmKG0ubGVmdD1iLmxlZnQtaC5sZWZ0K2UpLCJ1c2luZyJpbiBiP2IudXNpbmcuY2FsbChhLG0pOmwuY3NzKG0pfX0sbi5mbi5leHRlbmQoe29mZnNldDpmdW5jdGlvbihhKXtpZihhcmd1bWVudHMubGVuZ3RoKXJldHVybiB2b2lkIDA9PT1hP3RoaXM6dGhpcy5lYWNoKGZ1bmN0aW9uKGIpe24ub2Zmc2V0LnNldE9mZnNldCh0aGlzLGEsYil9KTt2YXIgYixjLGQ9dGhpc1swXSxlPXt0b3A6MCxsZWZ0OjB9LGY9ZCYmZC5vd25lckRvY3VtZW50O2lmKGYpcmV0dXJuIGI9Zi5kb2N1bWVudEVsZW1lbnQsbi5jb250YWlucyhiLGQpPyhlPWQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCksYz1MYihmKSx7dG9wOmUudG9wK2MucGFnZVlPZmZzZXQtYi5jbGllbnRUb3AsbGVmdDplLmxlZnQrYy5wYWdlWE9mZnNldC1iLmNsaWVudExlZnR9KTplfSxwb3NpdGlvbjpmdW5jdGlvbigpe2lmKHRoaXNbMF0pe3ZhciBhLGIsYz10aGlzWzBdLGQ9e3RvcDowLGxlZnQ6MH07cmV0dXJuImZpeGVkIj09PW4uY3NzKGMsInBvc2l0aW9uIik/Yj1jLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpOihhPXRoaXMub2Zmc2V0UGFyZW50KCksYj10aGlzLm9mZnNldCgpLG4ubm9kZU5hbWUoYVswXSwiaHRtbCIpfHwoZD1hLm9mZnNldCgpKSxkLnRvcCs9bi5jc3MoYVswXSwiYm9yZGVyVG9wV2lkdGgiLCEwKS1hLnNjcm9sbFRvcCgpLGQubGVmdCs9bi5jc3MoYVswXSwiYm9yZGVyTGVmdFdpZHRoIiwhMCktYS5zY3JvbGxMZWZ0KCkpLHt0b3A6Yi50b3AtZC50b3Atbi5jc3MoYywibWFyZ2luVG9wIiwhMCksbGVmdDpiLmxlZnQtZC5sZWZ0LW4uY3NzKGMsIm1hcmdpbkxlZnQiLCEwKX19fSxvZmZzZXRQYXJlbnQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5tYXAoZnVuY3Rpb24oKXt2YXIgYT10aGlzLm9mZnNldFBhcmVudDt3aGlsZShhJiYic3RhdGljIj09PW4uY3NzKGEsInBvc2l0aW9uIikpYT1hLm9mZnNldFBhcmVudDtyZXR1cm4gYXx8RWF9KX19KSxuLmVhY2goe3Njcm9sbExlZnQ6InBhZ2VYT2Zmc2V0IixzY3JvbGxUb3A6InBhZ2VZT2Zmc2V0In0sZnVuY3Rpb24oYSxiKXt2YXIgYz0icGFnZVlPZmZzZXQiPT09YjtuLmZuW2FdPWZ1bmN0aW9uKGQpe3JldHVybiBLKHRoaXMsZnVuY3Rpb24oYSxkLGUpe3ZhciBmPUxiKGEpO3JldHVybiB2b2lkIDA9PT1lP2Y/ZltiXTphW2RdOnZvaWQoZj9mLnNjcm9sbFRvKGM/Zi5wYWdlWE9mZnNldDplLGM/ZTpmLnBhZ2VZT2Zmc2V0KTphW2RdPWUpfSxhLGQsYXJndW1lbnRzLmxlbmd0aCl9fSksbi5lYWNoKFsidG9wIiwibGVmdCJdLGZ1bmN0aW9uKGEsYil7bi5jc3NIb29rc1tiXT1HYShsLnBpeGVsUG9zaXRpb24sZnVuY3Rpb24oYSxjKXtyZXR1cm4gYz8oYz1GYShhLGIpLEJhLnRlc3QoYyk/bihhKS5wb3NpdGlvbigpW2JdKyJweCI6Yyk6dm9pZCAwfSl9KSxuLmVhY2goe0hlaWdodDoiaGVpZ2h0IixXaWR0aDoid2lkdGgifSxmdW5jdGlvbihhLGIpe24uZWFjaCh7cGFkZGluZzoiaW5uZXIiK2EsY29udGVudDpiLCIiOiJvdXRlciIrYX0sZnVuY3Rpb24oYyxkKXtuLmZuW2RdPWZ1bmN0aW9uKGQsZSl7dmFyIGY9YXJndW1lbnRzLmxlbmd0aCYmKGN8fCJib29sZWFuIiE9dHlwZW9mIGQpLGc9Y3x8KGQ9PT0hMHx8ZT09PSEwPyJtYXJnaW4iOiJib3JkZXIiKTtyZXR1cm4gSyh0aGlzLGZ1bmN0aW9uKGIsYyxkKXt2YXIgZTtyZXR1cm4gbi5pc1dpbmRvdyhiKT9iLmRvY3VtZW50LmRvY3VtZW50RWxlbWVudFsiY2xpZW50IithXTo5PT09Yi5ub2RlVHlwZT8oZT1iLmRvY3VtZW50RWxlbWVudCxNYXRoLm1heChiLmJvZHlbInNjcm9sbCIrYV0sZVsic2Nyb2xsIithXSxiLmJvZHlbIm9mZnNldCIrYV0sZVsib2Zmc2V0IithXSxlWyJjbGllbnQiK2FdKSk6dm9pZCAwPT09ZD9uLmNzcyhiLGMsZyk6bi5zdHlsZShiLGMsZCxnKX0sYixmP2Q6dm9pZCAwLGYsbnVsbCl9fSl9KSxuLmZuLmV4dGVuZCh7YmluZDpmdW5jdGlvbihhLGIsYyl7cmV0dXJuIHRoaXMub24oYSxudWxsLGIsYyl9LHVuYmluZDpmdW5jdGlvbihhLGIpe3JldHVybiB0aGlzLm9mZihhLG51bGwsYil9LGRlbGVnYXRlOmZ1bmN0aW9uKGEsYixjLGQpe3JldHVybiB0aGlzLm9uKGIsYSxjLGQpfSx1bmRlbGVnYXRlOmZ1bmN0aW9uKGEsYixjKXtyZXR1cm4gMT09PWFyZ3VtZW50cy5sZW5ndGg/dGhpcy5vZmYoYSwiKioiKTp0aGlzLm9mZihiLGF8fCIqKiIsYyl9LHNpemU6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5sZW5ndGh9fSksbi5mbi5hbmRTZWxmPW4uZm4uYWRkQmFjaywiZnVuY3Rpb24iPT10eXBlb2YgZGVmaW5lJiZkZWZpbmUuYW1kJiZkZWZpbmUoImpxdWVyeSIsW10sZnVuY3Rpb24oKXtyZXR1cm4gbn0pO3ZhciBNYj1hLmpRdWVyeSxOYj1hLiQ7cmV0dXJuIG4ubm9Db25mbGljdD1mdW5jdGlvbihiKXtyZXR1cm4gYS4kPT09biYmKGEuJD1OYiksYiYmYS5qUXVlcnk9PT1uJiYoYS5qUXVlcnk9TWIpLG59LGJ8fChhLmpRdWVyeT1hLiQ9biksbn0pOwo=" type="text/javascript"></script>
<script src="data:application/javascript; charset=utf-8;base64,LyoqCiAqIEFuY2hvckpTIC0gdjIuMC4wIC0gMjAxNS0xMC0zMQogKiBodHRwczovL2dpdGh1Yi5jb20vYnJ5YW5icmF1bi9hbmNob3JqcwogKiBDb3B5cmlnaHQgKGMpIDIwMTUgQnJ5YW4gQnJhdW47IExpY2Vuc2VkIE1JVAogKi8KZnVuY3Rpb24gQW5jaG9ySlMoQSl7InVzZSBzdHJpY3QiO2Z1bmN0aW9uIHQoQSl7by5vcHRpb25zLmljb249by5vcHRpb25zLmhhc093blByb3BlcnR5KCJpY29uIik/QS5pY29uOiLup4siLG8ub3B0aW9ucy52aXNpYmxlPW8ub3B0aW9ucy5oYXNPd25Qcm9wZXJ0eSgidmlzaWJsZSIpP0EudmlzaWJsZToiaG92ZXIiLG8ub3B0aW9ucy5wbGFjZW1lbnQ9by5vcHRpb25zLmhhc093blByb3BlcnR5KCJwbGFjZW1lbnQiKT9BLnBsYWNlbWVudDoicmlnaHQiLG8ub3B0aW9ucy5jbGFzcz1vLm9wdGlvbnMuaGFzT3duUHJvcGVydHkoImNsYXNzIik/QS5jbGFzczoiIixvLm9wdGlvbnMudHJ1bmNhdGU9by5vcHRpb25zLmhhc093blByb3BlcnR5KCJ0cnVuY2F0ZSIpP01hdGguZmxvb3IoQS50cnVuY2F0ZSk6NjR9ZnVuY3Rpb24gZSgpe2lmKG51bGw9PT1kb2N1bWVudC5oZWFkLnF1ZXJ5U2VsZWN0b3IoInN0eWxlLmFuY2hvcmpzIikpe3ZhciBBLHQ9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3R5bGUiKSxlPSIgLmFuY2hvcmpzLWxpbmsgeyAgIG9wYWNpdHk6IDA7ICAgdGV4dC1kZWNvcmF0aW9uOiBub25lOyAgIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGFudGlhbGlhc2VkOyAgIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7IH0iLG89IiAqOmhvdmVyID4gLmFuY2hvcmpzLWxpbmssIC5hbmNob3Jqcy1saW5rOmZvY3VzICB7ICAgb3BhY2l0eTogMTsgfSIsbj0nIEBmb250LWZhY2UgeyAgIGZvbnQtZmFtaWx5OiAiYW5jaG9yanMtaWNvbnMiOyAgIGZvbnQtc3R5bGU6IG5vcm1hbDsgICBmb250LXdlaWdodDogbm9ybWFsOyAgIHNyYzogdXJsKGRhdGE6YXBwbGljYXRpb24veC1mb250LXR0ZjtjaGFyc2V0PXV0Zi04O2Jhc2U2NCxBQUVBQUFBTEFJQUFBd0F3VDFNdk1nOFNCVFVBQUFDOEFBQUFZR050WVhBV2k5UWRBQUFCSEFBQUFGUm5ZWE53QUFBQUVBQUFBWEFBQUFBSVoyeDVaZ3EyOVRjQUFBRjRBQUFCTkdobFlXUUVaTTNwQUFBQ3JBQUFBRFpvYUdWaEJoVUR4Z0FBQXVRQUFBQWthRzEwZUFTQUFERUFBQU1JQUFBQUZHeHZZMkVBS0FDdUFBQURIQUFBQUF4dFlYaHdBQWdBVndBQUF5Z0FBQUFnYm1GdFpRNXlKM2NBQUFOSUFBQUIybkJ2YzNRQUF3QUFBQUFGSkFBQUFDQUFBd0pBQVpBQUJRQUFBcGtDekFBQUFJOENtUUxNQUFBQjZ3QXpBUWtBQUFBQUFBQUFBQUFBQUFBQUFBQUJFQUFBQUFBQUFBQUFBQUFBQUFBQUFBQkFBQURweXdQQS84QUFRQVBBQUVBQUFBQUJBQUFBQUFBQUFBQUFBQUFnQUFBQUFBQURBQUFBQXdBQUFCd0FBUUFEQUFBQUhBQURBQUVBQUFBY0FBUUFPQUFBQUFvQUNBQUNBQUlBQVFBZzZjdi8vZi8vQUFBQUFBQWc2Y3YvL2YvL0FBSC80eFk1QUFNQUFRQUFBQUFBQUFBQUFBQUFBUUFCLy84QUR3QUJBQUFBQUFBQUFBQUFBZ0FBTnprQkFBQUFBQUVBQUFBQUFBQUFBQUFDQUFBM09RRUFBQUFBQVFBQUFBQUFBQUFBQUFJQUFEYzVBUUFBQUFBQ0FERUFSQUpUQXNBQUt3QlVBQUFCSWlZbkpqUS9BVDRCTXpJV0Z4WVVEd0VHSWljbU5EOEJOalFuTGdFaklnWVBBUVlVRnhZVUJ3NEJJd2NpSmljbU5EOEJOaklYRmhRUEFRWVVGeDRCTXpJMlB3RTJOQ2NtTkRjMk1oY1dGQThCRGdFakFSUUdEQVV0TFhvV09SOGZPUll0TFRnS0d3b0tDamdhR2cwZ0VoSWdEWG9hR2drSkJRd0hkUjg1RmkwdE9Bb2JDZ29LT0JvYURTQVNFaUFOZWhvYUNRa0tHd290TFhvV09SOEJNd1VGTFlFdWVoWVhGeFl1Z0M0NENRa0tHd280R2tvYURRME5EWG9hU2hvS0d3b0ZCZThYRmk2QUxqZ0pDUW9iQ2pnYVNob05EUTBOZWhwS0dnb2JDZ29LTFlFdWVoWVhBQUVBQUFBQkFBQ2lUb2MxWHc4ODlRQUxCQUFBQUFBQTBYbkZGZ0FBQUFEUmVjVVdBQUFBQUFKVEFzQUFBQUFJQUFJQUFBQUFBQUFBQVFBQUE4RC93QUFBQkFBQUFBQUFBbE1BQVFBQUFBQUFBQUFBQUFBQUFBQUFBQVVBQUFBQUFBQUFBQUFBQUFBQ0FBQUFBb0FBTVFBQUFBQUFDZ0FVQUI0QW1nQUJBQUFBQlFCVkFBSUFBQUFBQUFJQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQURnQ3VBQUVBQUFBQUFBRUFEZ0FBQUFFQUFBQUFBQUlBQndDZkFBRUFBQUFBQUFNQURnQkxBQUVBQUFBQUFBUUFEZ0MwQUFFQUFBQUFBQVVBQ3dBcUFBRUFBQUFBQUFZQURnQjFBQUVBQUFBQUFBb0FHZ0RlQUFNQUFRUUpBQUVBSEFBT0FBTUFBUVFKQUFJQURnQ21BQU1BQVFRSkFBTUFIQUJaQUFNQUFRUUpBQVFBSEFEQ0FBTUFBUVFKQUFVQUZnQTFBQU1BQVFRSkFBWUFIQUNEQUFNQUFRUUpBQW9BTkFENFlXNWphRzl5YW5NdGFXTnZibk1BWVFCdUFHTUFhQUJ2QUhJQWFnQnpBQzBBYVFCakFHOEFiZ0J6Vm1WeWMybHZiaUF4TGpBQVZnQmxBSElBY3dCcEFHOEFiZ0FnQURFQUxnQXdZVzVqYUc5eWFuTXRhV052Ym5NQVlRQnVBR01BYUFCdkFISUFhZ0J6QUMwQWFRQmpBRzhBYmdCellXNWphRzl5YW5NdGFXTnZibk1BWVFCdUFHTUFhQUJ2QUhJQWFnQnpBQzBBYVFCakFHOEFiZ0J6VW1WbmRXeGhjZ0JTQUdVQVp3QjFBR3dBWVFCeVlXNWphRzl5YW5NdGFXTnZibk1BWVFCdUFHTUFhQUJ2QUhJQWFnQnpBQzBBYVFCakFHOEFiZ0J6Um05dWRDQm5aVzVsY21GMFpXUWdZbmtnU1dOdlRXOXZiaTRBUmdCdkFHNEFkQUFnQUdjQVpRQnVBR1VBY2dCaEFIUUFaUUJrQUNBQVlnQjVBQ0FBU1FCakFHOEFUUUJ2QUc4QWJnQXVBQUFBQXdBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBPT0pIGZvcm1hdCgidHJ1ZXR5cGUiKTsgfScsaT0iIFtkYXRhLWFuY2hvcmpzLWljb25dOjphZnRlciB7ICAgY29udGVudDogYXR0cihkYXRhLWFuY2hvcmpzLWljb24pOyB9Ijt0LmNsYXNzTmFtZT0iYW5jaG9yanMiLHQuYXBwZW5kQ2hpbGQoZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoIiIpKSxBPWRvY3VtZW50LmhlYWQucXVlcnlTZWxlY3RvcignW3JlbD0ic3R5bGVzaGVldCJdLCBzdHlsZScpLHZvaWQgMD09PUE/ZG9jdW1lbnQuaGVhZC5hcHBlbmRDaGlsZCh0KTpkb2N1bWVudC5oZWFkLmluc2VydEJlZm9yZSh0LEEpLHQuc2hlZXQuaW5zZXJ0UnVsZShlLHQuc2hlZXQuY3NzUnVsZXMubGVuZ3RoKSx0LnNoZWV0Lmluc2VydFJ1bGUobyx0LnNoZWV0LmNzc1J1bGVzLmxlbmd0aCksdC5zaGVldC5pbnNlcnRSdWxlKGksdC5zaGVldC5jc3NSdWxlcy5sZW5ndGgpLHQuc2hlZXQuaW5zZXJ0UnVsZShuLHQuc2hlZXQuY3NzUnVsZXMubGVuZ3RoKX19dmFyIG89dGhpczt0aGlzLm9wdGlvbnM9QXx8e30sdChBKSx0aGlzLmFkZD1mdW5jdGlvbihBKXt2YXIgbyxuLGkscyxhLHIsbCxjLGgsZyxCLHU7aWYodCh0aGlzLm9wdGlvbnMpLEEpe2lmKCJzdHJpbmciIT10eXBlb2YgQSl0aHJvdyBuZXcgRXJyb3IoIlRoZSBzZWxlY3RvciBwcm92aWRlZCB0byBBbmNob3JKUyB3YXMgaW52YWxpZC4iKX1lbHNlIEE9ImgxLCBoMiwgaDMsIGg0LCBoNSwgaDYiO2lmKG89ZG9jdW1lbnQucXVlcnlTZWxlY3RvckFsbChBKSwwPT09by5sZW5ndGgpcmV0dXJuITE7Zm9yKGUoKSxuPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoIltpZF0iKSxpPVtdLm1hcC5jYWxsKG4sZnVuY3Rpb24oQSl7cmV0dXJuIEEuaWR9KSxhPTA7YTxvLmxlbmd0aDthKyspe2lmKG9bYV0uaGFzQXR0cmlidXRlKCJpZCIpKXM9b1thXS5nZXRBdHRyaWJ1dGUoImlkIik7ZWxzZXtyPW9bYV0udGV4dENvbnRlbnQsbD10aGlzLnVybGlmeShyKSxnPWwsaD0wO2RvIHZvaWQgMCE9PWMmJihnPWwrIi0iK2gpLGM9aS5pbmRleE9mKGcpLGgrPTE7d2hpbGUoLTEhPT1jKTtjPXZvaWQgMCxpLnB1c2goZyksb1thXS5zZXRBdHRyaWJ1dGUoImlkIixnKSxzPWd9Qj1zLnJlcGxhY2UoLy0vZywiICIpLHU9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiYSIpLHUuY2xhc3NOYW1lPSJhbmNob3Jqcy1saW5rICIrdGhpcy5vcHRpb25zLmNsYXNzLHUuaHJlZj0iIyIrcyx1LnNldEF0dHJpYnV0ZSgiYXJpYS1sYWJlbCIsIkFuY2hvciBsaW5rIGZvcjogIitCKSx1LnNldEF0dHJpYnV0ZSgiZGF0YS1hbmNob3Jqcy1pY29uIix0aGlzLm9wdGlvbnMuaWNvbiksImFsd2F5cyI9PT10aGlzLm9wdGlvbnMudmlzaWJsZSYmKHUuc3R5bGUub3BhY2l0eT0iMSIpLCLup4siPT09dGhpcy5vcHRpb25zLmljb24mJih1LnN0eWxlLmZvbnRGYW1pbHk9ImFuY2hvcmpzLWljb25zIix1LnN0eWxlLmZvbnRTdHlsZT0ibm9ybWFsIix1LnN0eWxlLmZvbnRWYXJpYW50PSJub3JtYWwiLHUuc3R5bGUuZm9udFdlaWdodD0ibm9ybWFsIix1LnN0eWxlLmxpbmVIZWlnaHQ9MSwibGVmdCI9PT10aGlzLm9wdGlvbnMucGxhY2VtZW50JiYodS5zdHlsZS5saW5lSGVpZ2h0PSJpbmhlcml0IikpLCJsZWZ0Ij09PXRoaXMub3B0aW9ucy5wbGFjZW1lbnQ/KHUuc3R5bGUucG9zaXRpb249ImFic29sdXRlIix1LnN0eWxlLm1hcmdpbkxlZnQ9Ii0xZW0iLHUuc3R5bGUucGFkZGluZ1JpZ2h0PSIwLjVlbSIsb1thXS5pbnNlcnRCZWZvcmUodSxvW2FdLmZpcnN0Q2hpbGQpKToodS5zdHlsZS5wYWRkaW5nTGVmdD0iMC4zNzVlbSIsb1thXS5hcHBlbmRDaGlsZCh1KSl9cmV0dXJuIHRoaXN9LHRoaXMucmVtb3ZlPWZ1bmN0aW9uKEEpe2Zvcih2YXIgdCxlPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoQSksbz0wO288ZS5sZW5ndGg7bysrKXQ9ZVtvXS5xdWVyeVNlbGVjdG9yKCIuYW5jaG9yanMtbGluayIpLHQmJmVbb10ucmVtb3ZlQ2hpbGQodCk7cmV0dXJuIHRoaXN9LHRoaXMudXJsaWZ5PWZ1bmN0aW9uKEEpe3ZhciBlLG89L1smICskLDo7PT9AIiN7fXxefltgJSEnXF1cLlwvXChcKVwqXFxdL2c7cmV0dXJuIHQodGhpcy5vcHRpb25zKSxlPUEucmVwbGFjZSgvXCcvZ2ksIiIpLnJlcGxhY2UobywiLSIpLnJlcGxhY2UoLy17Mix9L2csIi0iKS5zdWJzdHJpbmcoMCx0aGlzLm9wdGlvbnMudHJ1bmNhdGUpLnJlcGxhY2UoL14tK3wtKyQvZ20sIiIpLnRvTG93ZXJDYXNlKCl9fXZhciBhbmNob3JzPW5ldyBBbmNob3JKUzs=" type="text/javascript"></script>
<script src="data:application/x-javascript;base64,LyohIGxhenlzaXplcyAtIHYxLjMuMiAqLwohZnVuY3Rpb24oYSxiKXt2YXIgYz1iKGEsYS5kb2N1bWVudCk7YS5sYXp5U2l6ZXM9Yywib2JqZWN0Ij09dHlwZW9mIG1vZHVsZSYmbW9kdWxlLmV4cG9ydHM/bW9kdWxlLmV4cG9ydHM9YzoiZnVuY3Rpb24iPT10eXBlb2YgZGVmaW5lJiZkZWZpbmUuYW1kJiZkZWZpbmUoYyl9KHdpbmRvdyxmdW5jdGlvbihhLGIpeyJ1c2Ugc3RyaWN0IjtpZihiLmdldEVsZW1lbnRzQnlDbGFzc05hbWUpe3ZhciBjLGQ9Yi5kb2N1bWVudEVsZW1lbnQsZT1hLkhUTUxQaWN0dXJlRWxlbWVudCYmInNpemVzImluIGIuY3JlYXRlRWxlbWVudCgiaW1nIiksZj0iYWRkRXZlbnRMaXN0ZW5lciIsZz1hW2ZdLGg9YS5zZXRUaW1lb3V0LGk9YS5yZXF1ZXN0QW5pbWF0aW9uRnJhbWV8fGgsaj0vXnBpY3R1cmUkL2ksaz1bImxvYWQiLCJlcnJvciIsImxhenlpbmNsdWRlZCIsIl9sYXp5bG9hZGVkIl0sbD17fSxtPUFycmF5LnByb3RvdHlwZS5mb3JFYWNoLG49ZnVuY3Rpb24oYSxiKXtyZXR1cm4gbFtiXXx8KGxbYl09bmV3IFJlZ0V4cCgiKFxcc3xeKSIrYisiKFxcc3wkKSIpKSxsW2JdLnRlc3QoYS5jbGFzc05hbWUpJiZsW2JdfSxvPWZ1bmN0aW9uKGEsYil7bihhLGIpfHwoYS5jbGFzc05hbWU9YS5jbGFzc05hbWUudHJpbSgpKyIgIitiKX0scD1mdW5jdGlvbihhLGIpe3ZhciBjOyhjPW4oYSxiKSkmJihhLmNsYXNzTmFtZT1hLmNsYXNzTmFtZS5yZXBsYWNlKGMsIiAiKSl9LHE9ZnVuY3Rpb24oYSxiLGMpe3ZhciBkPWM/ZjoicmVtb3ZlRXZlbnRMaXN0ZW5lciI7YyYmcShhLGIpLGsuZm9yRWFjaChmdW5jdGlvbihjKXthW2RdKGMsYil9KX0scj1mdW5jdGlvbihhLGMsZCxlLGYpe3ZhciBnPWIuY3JlYXRlRXZlbnQoIkN1c3RvbUV2ZW50Iik7cmV0dXJuIGcuaW5pdEN1c3RvbUV2ZW50KGMsIWUsIWYsZHx8e30pLGEuZGlzcGF0Y2hFdmVudChnKSxnfSxzPWZ1bmN0aW9uKGIsZCl7dmFyIGY7IWUmJihmPWEucGljdHVyZWZpbGx8fGMucGYpP2Yoe3JlZXZhbHVhdGU6ITAsZWxlbWVudHM6W2JdfSk6ZCYmZC5zcmMmJihiLnNyYz1kLnNyYyl9LHQ9ZnVuY3Rpb24oYSxiKXtyZXR1cm4oZ2V0Q29tcHV0ZWRTdHlsZShhLG51bGwpfHx7fSlbYl19LHU9ZnVuY3Rpb24oYSxiLGQpe2ZvcihkPWR8fGEub2Zmc2V0V2lkdGg7ZDxjLm1pblNpemUmJmImJiFhLl9sYXp5c2l6ZXNXaWR0aDspZD1iLm9mZnNldFdpZHRoLGI9Yi5wYXJlbnROb2RlO3JldHVybiBkfSx2PWZ1bmN0aW9uKGIpe3ZhciBjLGQ9MCxlPWEuRGF0ZSxmPWZ1bmN0aW9uKCl7Yz0hMSxkPWUubm93KCksYigpfSxnPWZ1bmN0aW9uKCl7aChmKX0saj1mdW5jdGlvbigpe2koZyl9O3JldHVybiBmdW5jdGlvbigpe2lmKCFjKXt2YXIgYT0xMjUtKGUubm93KCktZCk7Yz0hMCw2PmEmJihhPTYpLGgoaixhKX19fSx3PWZ1bmN0aW9uKCl7dmFyIGUsayxsLHUsdyx5LHosQSxCLEMsRCxFLEYsRyxILEk9L15pbWckL2ksSj0vXmlmcmFtZSQvaSxLPSJvbnNjcm9sbCJpbiBhJiYhL2dsZWJvdC8udGVzdChuYXZpZ2F0b3IudXNlckFnZW50KSxMPTAsTT0wLE49MCxPPTAsUD1mdW5jdGlvbihhKXtOLS0sYSYmYS50YXJnZXQmJnEoYS50YXJnZXQsUCksKCFhfHwwPk58fCFhLnRhcmdldCkmJihOPTApfSxRPWZ1bmN0aW9uKGEsYil7dmFyIGMsZD1hLGU9ImhpZGRlbiIhPXQoYSwidmlzaWJpbGl0eSIpO2ZvcihCLT1iLEUrPWIsQy09YixEKz1iO2UmJihkPWQub2Zmc2V0UGFyZW50KTspZT0odChkLCJvcGFjaXR5Iil8fDEpPjAsZSYmInZpc2libGUiIT10KGQsIm92ZXJmbG93IikmJihjPWQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCksZT1EPmMubGVmdCYmQzxjLnJpZ2h0JiZFPmMudG9wLTEmJkI8Yy5ib3R0b20rMSk7cmV0dXJuIGV9LFI9ZnVuY3Rpb24oKXt2YXIgYSxiLGQsZixnLGgsaSxqLG07aWYoKHc9Yy5sb2FkTW9kZSkmJjg+TiYmKGE9ZS5sZW5ndGgpKXtmb3IoYj0wLE8rKyxHPk0mJjE+TiYmTz4zJiZ3PjI/KE09RyxPPTApOk09dz4xJiZPPjImJjY+Tj9GOkw7YT5iO2IrKylpZihlW2JdJiYhZVtiXS5fbGF6eVJhY2UpaWYoSylpZigoaj1lW2JdLmdldEF0dHJpYnV0ZSgiZGF0YS1leHBhbmQiKSkmJihoPTEqail8fChoPU0pLG0hPT1oJiYoej1pbm5lcldpZHRoK2gqSCxBPWlubmVySGVpZ2h0K2gsaT0tMSpoLG09aCksZD1lW2JdLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLChFPWQuYm90dG9tKT49aSYmKEI9ZC50b3ApPD1BJiYoRD1kLnJpZ2h0KT49aSpIJiYoQz1kLmxlZnQpPD16JiYoRXx8RHx8Q3x8QikmJihsJiYzPk4mJiFqJiYoMz53fHw0Pk8pfHxRKGVbYl0saCkpKXtpZihYKGVbYl0pLGc9ITAsTj45KWJyZWFrfWVsc2UhZyYmbCYmIWYmJjQ+TiYmND5PJiZ3PjImJihrWzBdfHxjLnByZWxvYWRBZnRlckxvYWQpJiYoa1swXXx8IWomJihFfHxEfHxDfHxCfHwiYXV0byIhPWVbYl0uZ2V0QXR0cmlidXRlKGMuc2l6ZXNBdHRyKSkpJiYoZj1rWzBdfHxlW2JdKTtlbHNlIFgoZVtiXSk7ZiYmIWcmJlgoZil9fSxTPXYoUiksVD1mdW5jdGlvbihhKXtvKGEudGFyZ2V0LGMubG9hZGVkQ2xhc3MpLHAoYS50YXJnZXQsYy5sb2FkaW5nQ2xhc3MpLHEoYS50YXJnZXQsVCl9LFU9ZnVuY3Rpb24oYSxiKXt0cnl7YS5jb250ZW50V2luZG93LmxvY2F0aW9uLnJlcGxhY2UoYil9Y2F0Y2goYyl7YS5zcmM9Yn19LFY9ZnVuY3Rpb24oYSl7dmFyIGIsZCxlPWEuZ2V0QXR0cmlidXRlKGMuc3Jjc2V0QXR0cik7KGI9Yy5jdXN0b21NZWRpYVthLmdldEF0dHJpYnV0ZSgiZGF0YS1tZWRpYSIpfHxhLmdldEF0dHJpYnV0ZSgibWVkaWEiKV0pJiZhLnNldEF0dHJpYnV0ZSgibWVkaWEiLGIpLGUmJmEuc2V0QXR0cmlidXRlKCJzcmNzZXQiLGUpLGImJihkPWEucGFyZW50Tm9kZSxkLmluc2VydEJlZm9yZShhLmNsb25lTm9kZSgpLGEpLGQucmVtb3ZlQ2hpbGQoYSkpfSxXPWZ1bmN0aW9uKCl7dmFyIGEsYj1bXSxjPWZ1bmN0aW9uKCl7Zm9yKDtiLmxlbmd0aDspYi5zaGlmdCgpKCk7YT0hMX07cmV0dXJuIGZ1bmN0aW9uKGQpe2IucHVzaChkKSxhfHwoYT0hMCxpKGMpKX19KCksWD1mdW5jdGlvbihhKXt2YXIgYixkLGUsZixnLGksayx0PUkudGVzdChhLm5vZGVOYW1lKSx2PXQmJihhLmdldEF0dHJpYnV0ZShjLnNpemVzQXR0cil8fGEuZ2V0QXR0cmlidXRlKCJzaXplcyIpKSx3PSJhdXRvIj09djsoIXcmJmx8fCF0fHwhYS5zcmMmJiFhLnNyY3NldHx8YS5jb21wbGV0ZXx8bihhLGMuZXJyb3JDbGFzcykpJiYodyYmKGs9YS5vZmZzZXRXaWR0aCksYS5fbGF6eVJhY2U9ITAsTisrLFcoZnVuY3Rpb24oKXthLl9sYXp5UmFjZSYmZGVsZXRlIGEuX2xhenlSYWNlLHAoYSxjLmxhenlDbGFzcyksKGc9cihhLCJsYXp5YmVmb3JldW52ZWlsIikpLmRlZmF1bHRQcmV2ZW50ZWR8fCh2JiYodz8obyhhLGMuYXV0b3NpemVzQ2xhc3MpLHgudXBkYXRlRWxlbShhLCEwLGspKTphLnNldEF0dHJpYnV0ZSgic2l6ZXMiLHYpKSxkPWEuZ2V0QXR0cmlidXRlKGMuc3Jjc2V0QXR0ciksYj1hLmdldEF0dHJpYnV0ZShjLnNyY0F0dHIpLHQmJihlPWEucGFyZW50Tm9kZSxmPWUmJmoudGVzdChlLm5vZGVOYW1lfHwiIikpLGk9Zy5kZXRhaWwuZmlyZXNMb2FkfHwic3JjImluIGEmJihkfHxifHxmKSxnPXt0YXJnZXQ6YX0saSYmKHEoYSxQLCEwKSxjbGVhclRpbWVvdXQodSksdT1oKFAsMjUwMCksbyhhLGMubG9hZGluZ0NsYXNzKSxxKGEsVCwhMCkpLGYmJm0uY2FsbChlLmdldEVsZW1lbnRzQnlUYWdOYW1lKCJzb3VyY2UiKSxWKSxkP2Euc2V0QXR0cmlidXRlKCJzcmNzZXQiLGQpOmImJiFmJiYoSi50ZXN0KGEubm9kZU5hbWUpP1UoYSxiKTphLnNyYz1iKSwoZHx8ZikmJnMoYSx7c3JjOmJ9KSksKCFpfHxhLmNvbXBsZXRlKSYmKGk/UChnKTpOLS0sVChnKSl9KSl9LFk9ZnVuY3Rpb24oKXtpZighbCl7aWYoRGF0ZS5ub3coKS15PDk5OSlyZXR1cm4gdm9pZCBoKFksOTk5KTt2YXIgYSxiPWZ1bmN0aW9uKCl7Yy5sb2FkTW9kZT0zLFMoKX07bD0hMCxjLmxvYWRNb2RlPTMsTnx8KE8/UygpOmgoUikpLGcoInNjcm9sbCIsZnVuY3Rpb24oKXszPT1jLmxvYWRNb2RlJiYoYy5sb2FkTW9kZT0yKSxjbGVhclRpbWVvdXQoYSksYT1oKGIsOTkpfSwhMCl9fTtyZXR1cm57XzpmdW5jdGlvbigpe3k9RGF0ZS5ub3coKSxlPWIuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZShjLmxhenlDbGFzcyksaz1iLmdldEVsZW1lbnRzQnlDbGFzc05hbWUoYy5sYXp5Q2xhc3MrIiAiK2MucHJlbG9hZENsYXNzKSxIPWMuaEZhYyxGPWMuZXhwYW5kLEc9RipjLmV4cEZhY3RvcixnKCJzY3JvbGwiLFMsITApLGcoInJlc2l6ZSIsUywhMCksYS5NdXRhdGlvbk9ic2VydmVyP25ldyBNdXRhdGlvbk9ic2VydmVyKFMpLm9ic2VydmUoZCx7Y2hpbGRMaXN0OiEwLHN1YnRyZWU6ITAsYXR0cmlidXRlczohMH0pOihkW2ZdKCJET01Ob2RlSW5zZXJ0ZWQiLFMsITApLGRbZl0oIkRPTUF0dHJNb2RpZmllZCIsUywhMCksc2V0SW50ZXJ2YWwoUyw5OTkpKSxnKCJoYXNoY2hhbmdlIixTLCEwKSxbImZvY3VzIiwibW91c2VvdmVyIiwiY2xpY2siLCJsb2FkIiwidHJhbnNpdGlvbmVuZCIsImFuaW1hdGlvbmVuZCIsIndlYmtpdEFuaW1hdGlvbkVuZCJdLmZvckVhY2goZnVuY3Rpb24oYSl7YltmXShhLFMsITApfSksL2QkfF5jLy50ZXN0KGIucmVhZHlTdGF0ZSk/WSgpOihnKCJsb2FkIixZKSxiW2ZdKCJET01Db250ZW50TG9hZGVkIixTKSxoKFksMmU0KSksUyhlLmxlbmd0aD4wKX0sY2hlY2tFbGVtczpTLHVudmVpbDpYfX0oKSx4PWZ1bmN0aW9uKCl7dmFyIGEsZD1mdW5jdGlvbihhLGIsYyl7dmFyIGQsZSxmLGcsaD1hLnBhcmVudE5vZGU7aWYoaCYmKGM9dShhLGgsYyksZz1yKGEsImxhenliZWZvcmVzaXplcyIse3dpZHRoOmMsZGF0YUF0dHI6ISFifSksIWcuZGVmYXVsdFByZXZlbnRlZCYmKGM9Zy5kZXRhaWwud2lkdGgsYyYmYyE9PWEuX2xhenlzaXplc1dpZHRoKSkpe2lmKGEuX2xhenlzaXplc1dpZHRoPWMsYys9InB4IixhLnNldEF0dHJpYnV0ZSgic2l6ZXMiLGMpLGoudGVzdChoLm5vZGVOYW1lfHwiIikpZm9yKGQ9aC5nZXRFbGVtZW50c0J5VGFnTmFtZSgic291cmNlIiksZT0wLGY9ZC5sZW5ndGg7Zj5lO2UrKylkW2VdLnNldEF0dHJpYnV0ZSgic2l6ZXMiLGMpO2cuZGV0YWlsLmRhdGFBdHRyfHxzKGEsZy5kZXRhaWwpfX0sZT1mdW5jdGlvbigpe3ZhciBiLGM9YS5sZW5ndGg7aWYoYylmb3IoYj0wO2M+YjtiKyspZChhW2JdKX0sZj12KGUpO3JldHVybntfOmZ1bmN0aW9uKCl7YT1iLmdldEVsZW1lbnRzQnlDbGFzc05hbWUoYy5hdXRvc2l6ZXNDbGFzcyksZygicmVzaXplIixmKX0sY2hlY2tFbGVtczpmLHVwZGF0ZUVsZW06ZH19KCkseT1mdW5jdGlvbigpe3kuaXx8KHkuaT0hMCx4Ll8oKSx3Ll8oKSl9O3JldHVybiBmdW5jdGlvbigpe3ZhciBiLGU9e2xhenlDbGFzczoibGF6eWxvYWQiLGxvYWRlZENsYXNzOiJsYXp5bG9hZGVkIixsb2FkaW5nQ2xhc3M6Imxhenlsb2FkaW5nIixwcmVsb2FkQ2xhc3M6ImxhenlwcmVsb2FkIixlcnJvckNsYXNzOiJsYXp5ZXJyb3IiLGF1dG9zaXplc0NsYXNzOiJsYXp5YXV0b3NpemVzIixzcmNBdHRyOiJkYXRhLXNyYyIsc3Jjc2V0QXR0cjoiZGF0YS1zcmNzZXQiLHNpemVzQXR0cjoiZGF0YS1zaXplcyIsbWluU2l6ZTo0MCxjdXN0b21NZWRpYTp7fSxpbml0OiEwLGV4cEZhY3RvcjoxLjcsaEZhYzouOCxleHBhbmQ6ZC5jbGllbnRIZWlnaHQ+NjAwP2QuY2xpZW50V2lkdGg+ODYwPzUwMDo0MTA6MzU5LGxvYWRNb2RlOjJ9O2M9YS5sYXp5U2l6ZXNDb25maWd8fGEubGF6eXNpemVzQ29uZmlnfHx7fTtmb3IoYiBpbiBlKWIgaW4gY3x8KGNbYl09ZVtiXSk7YS5sYXp5U2l6ZXNDb25maWc9YyxoKGZ1bmN0aW9uKCl7Yy5pbml0JiZ5KCl9KX0oKSx7Y2ZnOmMsYXV0b1NpemVyOngsbG9hZGVyOncsaW5pdDp5LHVQOnMsYUM6byxyQzpwLGhDOm4sZmlyZTpyLGdXOnV9fX0pOw==" type="text/javascript"></script>
<script src="data:application/x-javascript;base64,YW5jaG9ycy5vcHRpb25zID0gewogIHBsYWNlbWVudDogJ2xlZnQnLAogIHZpc2libGU6ICdhbHdheXMnLAp9OwphbmNob3JzLmFkZCgnaDEsIGgyLCBoMycpOw==" type="text/javascript"></script>
</body>
</html>