This notebook is licensed under the MIT License. If you use the code or data visualization designs contained within this notebook, it would be greatly appreciated if proper attribution is given back to this notebook and/or myself. Thanks! :)
Setup
[30m── [1mAttaching packages[22m ─────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──[39m
[30m[32m✔[30m [34mggplot2[30m 2.2.1.[31m9000[30m [32m✔[30m [34mpurrr [30m 0.2.4
[32m✔[30m [34mtibble [30m 1.4.2 [32m✔[30m [34mdplyr [30m 0.7.4
[32m✔[30m [34mtidyr [30m 0.8.0 [32m✔[30m [34mstringr[30m 1.2.0
[32m✔[30m [34mreadr [30m 1.1.1 [32m✔[30m [34mforcats[30m 0.2.0 [39m
package ‘tibble’ was built under R version 3.4.3package ‘tidyr’ was built under R version 3.4.3[30m── [1mConflicts[22m ────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
[31m✖[30m [34mdplyr[30m::[32mfilter()[30m masks [34mstats[30m::filter()
[31m✖[30m [34mdplyr[30m::[32mlag()[30m masks [34mstats[30m::lag()[39m
Attaching package: ‘lubridate’
The following object is masked from ‘package:base’:
date
library(tidytext) # created at Stack Overflow by Julia Silge and David Robinson
library(scales)
Attaching package: ‘scales’
The following object is masked from ‘package:purrr’:
discard
The following object is masked from ‘package:readr’:
col_factor
Loading required package: viridisLite
Attaching package: ‘viridis’
The following object is masked from ‘package:viridisLite’:
viridis.map
The following object is masked from ‘package:scales’:
viridis_pal
library(ggrepel)
library(ggridges)
sessionInfo()
R version 3.4.2 (2017-09-28)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS High Sierra 10.13.3
Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.4/Resources/lib/libRlapack.dylib
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] ggridges_0.4.1 ggrepel_0.7.0 viridis_0.4.1 viridisLite_0.3.0 scales_0.5.0
[6] tidytext_0.1.6 lubridate_1.7.1 forcats_0.2.0 stringr_1.2.0 dplyr_0.7.4
[11] purrr_0.2.4 readr_1.1.1 tidyr_0.8.0 tibble_1.4.2 ggplot2_2.2.1.9000
[16] tidyverse_1.2.1
loaded via a namespace (and not attached):
[1] reshape2_1.4.3 haven_1.1.1 lattice_0.20-35 colorspace_1.3-2 SnowballC_0.5.1
[6] yaml_2.1.16 rlang_0.1.6 pillar_1.1.0 foreign_0.8-69 glue_1.2.0
[11] modelr_0.1.1 readxl_1.0.0 bindrcpp_0.2 bindr_0.1 plyr_1.8.4
[16] munsell_0.4.3 gtable_0.2.0 cellranger_1.1.0 rvest_0.3.2 psych_1.7.8
[21] knitr_1.19 parallel_3.4.2 broom_0.4.3 tokenizers_0.1.4 Rcpp_0.12.15
[26] jsonlite_1.5 gridExtra_2.3 mnormt_1.5-5 hms_0.4.1 stringi_1.1.6
[31] grid_3.4.2 cli_1.0.0 tools_3.4.2 magrittr_1.5 lazyeval_0.2.1
[36] janeaustenr_0.1.5 crayon_1.3.4 pkgconfig_2.0.1 Matrix_1.2-12 xml2_1.2.0
[41] assertthat_0.2.0 httr_1.3.1 rstudioapi_0.7 R6_2.2.2 nlme_3.1-131
[46] compiler_3.4.2
Sys.setenv(TZ="America/Los_Angeles")
# https://brandcolors.net/b/stackoverflow
stack_overflow_color <- "#f48024"
theme_set(theme_minimal(base_size=9, base_family="Source Sans Pro") +
theme(plot.title = element_text(size=8, family="Source Sans Pro Bold", margin=margin(t = -0.1, b = 0.1, unit='cm')),
axis.title.x = element_text(size=8),
axis.title.y = element_text(size=8),
plot.subtitle = element_text(family="Source Sans Pro Semibold", color="#969696", size=6),
plot.caption = element_text(size=6, color="#969696"),
legend.text = element_text(size = 6),
legend.key.width = unit(0.25, unit='cm')))
Behavior for new submissions
Use data precomputed from this BigQuery:
#standardSQL
SELECT
DATE_TRUNC(DATE(creation_date), YEAR) AS year,
SUM(view_count_delta) AS total_delta
FROM (
SELECT
id,
creation_date,
b.view_count - a.view_count AS view_count_delta
FROM
`fh-bigquery.stackoverflow_archive.201703_posts_questions` a
LEFT JOIN (
SELECT
id,
view_count
FROM
`fh-bigquery.stackoverflow_archive.201712_posts_questions` ) b
USING
(id) )
GROUP BY
year
ORDER BY
year ASC
Load in the precomputed data.
file_path <- "stack_overflow_delta.csv"
df_deltas <- read_csv(file_path) %>% mutate(perc = total_delta / sum(as.numeric(total_delta)))
Parsed with column specification:
cols(
year = col_date(format = ""),
total_delta = col_integer()
)
Overview of 2017 view counts on older posts.
plot <- ggplot(df_deltas %>% filter(year >= ymd('2009-01-01'), year <= ymd('2016-01-01')), aes(x=year, y=perc)) +
geom_bar(alpha=0.9, stat="identity", fill=stack_overflow_color) +
scale_x_date(date_breaks='1 year', date_labels='%Y', minor_breaks = NULL) +
scale_y_continuous(labels=percent) +
labs(title='Proportion of 2017 Views on Older Stack Overflow Questions by Year',
subtitle='From March 13th, 2017 to December 3rd, 2017. Visualization Excludes Partial Years',
x='Year Question Was Posted',
y='% of All Views',
caption = "Max Woolf — minimaxir.com"
)
ggsave('so_overview.png', plot, width=4, height=2)
Data processed from this BigQuery: (NB: to download large datasets, save as a BigQuery table and export as a CSV, then download the CSV)
#standardSQL
WITH
answers_ordered AS (
SELECT
id,
creation_date,
parent_id AS question_id,
score,
ROW_NUMBER() OVER (PARTITION BY parent_id ORDER BY creation_date ASC) AS time_rank,
ROW_NUMBER() OVER (PARTITION BY parent_id ORDER BY score DESC) AS score_rank,
COUNT(*) OVER (PARTITION BY parent_id) AS num_answers
FROM
`fh-bigquery.stackoverflow_archive.201712_posts_answers` )
SELECT
id,
title,
tags,
DATETIME(creation_date) AS creation_date,
accepted_answer_id,
view_count,
score,
num_answers,
f_answer_id,
DATETIME(f_creation_date) AS f_creation_date,
f_score,
f_score_rank,
a_answer_id,
DATETIME(a_creation_date) AS a_creation_date,
a_score,
a_time_rank,
a_score_rank,
TIMESTAMP_DIFF(f_creation_date, creation_date, SECOND) AS time_to_f,
TIMESTAMP_DIFF(a_creation_date, creation_date, SECOND) AS time_to_a
FROM
`fh-bigquery.stackoverflow_archive.201712_posts_questions` q
LEFT JOIN (
SELECT
id AS f_answer_id,
creation_date AS f_creation_date,
question_id AS f_question_id,
score AS f_score,
score_rank AS f_score_rank,
num_answers
FROM
answers_ordered
WHERE
time_rank = 1 ) f
ON
q.id = f.f_question_id
LEFT JOIN (
SELECT
id AS a_answer_id,
creation_date AS a_creation_date,
score AS a_score,
time_rank AS a_time_rank,
score_rank AS a_score_rank
FROM
answers_ordered ) a
ON
q.accepted_answer_id = a.a_answer_id
WHERE
creation_date >= '2017-01-01 00:00:00' AND creation_date < '2017-12-01 00:00:00'
file_path <- "~/Downloads/stack_overflow_2017.csv"
df <- read_csv(file_path, progress=FALSE)
Parsed with column specification:
cols(
id = col_integer(),
title = col_character(),
tags = col_character(),
creation_date = col_datetime(format = ""),
accepted_answer_id = col_integer(),
view_count = col_integer(),
score = col_integer(),
num_answers = col_integer(),
f_answer_id = col_integer(),
f_creation_date = col_datetime(format = ""),
f_score = col_integer(),
f_score_rank = col_integer(),
a_answer_id = col_integer(),
a_creation_date = col_datetime(format = ""),
a_score = col_integer(),
a_time_rank = col_integer(),
a_score_rank = col_integer(),
time_to_f = col_integer(),
time_to_a = col_integer()
)
Add columns relevant to the timing when the post was made. The raw data is in UTC
, so it must be converted to Eastern.
df <- df %>% mutate(
creation_date = with_tz(creation_date, "America/New_York"),
hour_posted = hour(creation_date),
weekday_posted = wday(creation_date, label=T, abbr=F),
week_posted = floor_date(creation_date, '1 week'),
month_posted = floor_date(creation_date, '1 month')
)
Add a few bespoke features:
df <- df %>% mutate(
num_answers = ifelse(is.na(num_answers), 0, num_answers),
f_answer_in = ifelse(!is.na(time_to_f) & time_to_f < 60*60*4, 1, 0),
a_answer_in = ifelse(!is.na(time_to_a) & time_to_a < 60*60*4, 1, 0),
num_tags = 1 + str_match_all(tags, '\\|') %>% lapply(length) %>% unlist(),
is_answered = ifelse(!is.na(time_to_a), 1, 0)
)
Adjacency Matrix for Top Tags
df_tag_adjacency <- df %>%
select(id, tags, time_to_a, is_answered) %>%
unnest_tokens(tag, tags, token=stringr::str_split, pattern=fixed("|")) %>%
filter(tag %in% (df_top_tags %>% head(40) %>% pull(tag))) %>%
inner_join(
df %>%
select(id, tags) %>%
unnest_tokens(tag, tags, token=stringr::str_split, pattern=fixed("|")) %>%
filter(tag %in% (df_top_tags %>% head(40) %>% pull(tag))), by=c("id" = "id")) %>%
filter(tag.x != tag.y) %>%
group_by(tag.x, tag.y) %>%
summarize(count=n(),
total_answered = sum(is_answered),
perc_answered = total_answered/count) %>%
arrange(tag.x, desc(count))
plot <- ggplot(df_tag_adjacency %>% filter(count >= 1000), aes(x=tag.x, y = tag.y, fill=count)) +
geom_raster() +
scale_fill_viridis(option='inferno', trans='log10', breaks=10^(1:5), labels=comma) +
labs(title='Question Counts for Stack Overflow Tag Pairs',
subtitle='For Questions Asked From January 2017 to November 2017 (min 1,000 questions per pair)',
x='Stack Overflow Question Tag',
fill='# of Questions',
caption = "Max Woolf — minimaxir.com") +
theme(
axis.title.y=element_blank(),
axis.title.x=element_blank(),
axis.text.x = element_text(angle = 270, hjust = 0, vjust = 0.5),
legend.position = 'top',
plot.title = element_text(size=10, family="Source Sans Pro Bold", margin=margin(t = -0.1, b = 0.0, unit='cm')),
#axis.text.y = element_text(size = 5),
legend.text = element_text(size = 6),
legend.title = element_text(size = 6),
legend.key.width = unit(1, unit='cm'),
legend.key.height = unit(0.25, unit='cm'),
legend.margin = margin(c(0, 0, -0.1, 0), unit='cm'))
ggsave('so_tag_adjacency.png', plot, width=6, height=6)
plot <- ggplot(df_tag_adjacency %>% filter(count >= 1000), aes(x=tag.x, y = tag.y, fill=perc_answered)) +
geom_raster() +
geom_text(aes(label=sprintf("%0.0f", perc_answered*100)), family="Roboto Condensed Bold", size=2, color="white") +
scale_fill_viridis(option='inferno', limits=c(0,1), breaks=pretty_breaks(4), labels=percent) +
labs(title='Question Answer Rate for Stack Overflow Tag Pairs',
subtitle='For Questions Asked From January 2017 to November 2017 (min 1,000 questions per pair)',
x='Stack Overflow Question Tag',
fill='% of Tagged Questions\nWhich Have an Accepted Answer',
caption = "Max Woolf — minimaxir.com") +
theme(
axis.title.y=element_blank(),
axis.title.x=element_blank(),
axis.text.x = element_text(angle = 270, hjust=0, vjust = 0.5),
legend.position = 'top',
plot.title = element_text(size=10, family="Source Sans Pro Bold", margin=margin(t = -0.1, b = 0.0, unit='cm')),
#axis.text.y = element_text(size = 5),
legend.text = element_text(size = 6),
legend.title = element_text(size = 6),
legend.key.width = unit(1, unit='cm'),
legend.key.height = unit(0.25, unit='cm'),
legend.margin = margin(c(0, 0, -0.1, 0), unit='cm'))
ggsave('so_tag_adjacency_percent.png', plot, width=6, height=6)
LICENSE
The MIT License (MIT)
Copyright (c) 2018 Max Woolf
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
LS0tCnRpdGxlOiAiQSBWaXN1YWwgTG9vayBhdCBTdGFjayBPdmVyZmxvdydzIFF1ZXN0aW9uIFRhZ3MiCmF1dGhvcjogIk1heCBXb29sZiAoQG1pbmltYXhpcikiCmRhdGU6ICIyMDE3LTAyLTA5IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG1hdGhqYXg6IG51bGwKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogc3BhY2VsYWIKLS0tCgpUaGlzIFIgTm90ZWJvb2sgaXMgdGhlIGNvbXBsZW1lbnQgdG8gbXkgYmxvZyBwb3N0IFtBIFZpc3VhbCBPdmVydmlldyBvZiBTdGFjayBPdmVyZmxvdydzIFF1ZXN0aW9uIFRhZ3NdKGh0dHA6Ly9taW5pbWF4aXIuY29tLzIwMTgvMDIvc3RhY2stb3ZlcmZsb3ctcXVlc3Rpb25zLykuCgpUaGlzIG5vdGVib29rIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBNSVQgTGljZW5zZS4gSWYgeW91IHVzZSB0aGUgY29kZSBvciBkYXRhIHZpc3VhbGl6YXRpb24gZGVzaWducyBjb250YWluZWQgd2l0aGluIHRoaXMgbm90ZWJvb2ssIGl0IHdvdWxkIGJlIGdyZWF0bHkgYXBwcmVjaWF0ZWQgaWYgcHJvcGVyIGF0dHJpYnV0aW9uIGlzIGdpdmVuIGJhY2sgdG8gdGhpcyBub3RlYm9vayBhbmQvb3IgbXlzZWxmLiBUaGFua3MhIDopCgojIFNldHVwCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KHRpZHl0ZXh0KSAgICMgY3JlYXRlZCBhdCBTdGFjayBPdmVyZmxvdyBieSBKdWxpYSBTaWxnZSBhbmQgRGF2aWQgUm9iaW5zb24KbGlicmFyeShzY2FsZXMpCmxpYnJhcnkodmlyaWRpcykKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KGdncmlkZ2VzKQoKc2Vzc2lvbkluZm8oKQpTeXMuc2V0ZW52KFRaPSJBbWVyaWNhL0xvc19BbmdlbGVzIikKCiMgaHR0cHM6Ly9icmFuZGNvbG9ycy5uZXQvYi9zdGFja292ZXJmbG93CnN0YWNrX292ZXJmbG93X2NvbG9yIDwtICIjZjQ4MDI0IgpgYGAKCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbChiYXNlX3NpemU9OSwgYmFzZV9mYW1pbHk9IlNvdXJjZSBTYW5zIFBybyIpICsKICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgsIGZhbWlseT0iU291cmNlIFNhbnMgUHJvIEJvbGQiLCBtYXJnaW49bWFyZ2luKHQgPSAtMC4xLCBiID0gMC4xLCB1bml0PSdjbScpKSwKICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTgpLAogICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGZhbWlseT0iU291cmNlIFNhbnMgUHJvIFNlbWlib2xkIiwgY29sb3I9IiM5Njk2OTYiLCBzaXplPTYpLAogICAgICAgICAgICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZT02LCBjb2xvcj0iIzk2OTY5NiIpLAogICAgICAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICAgICAgICAgICAgICAgIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDAuMjUsIHVuaXQ9J2NtJykpKQpgYGAKCiMgQmVoYXZpb3IgZm9yIG5ldyBzdWJtaXNzaW9ucwoKVXNlIGRhdGEgcHJlY29tcHV0ZWQgZnJvbSB0aGlzIEJpZ1F1ZXJ5OgoKYGBge3NxbCBldmFsPUZBTFNFLCBpbmNsdWRlPVRSVUV9CiNzdGFuZGFyZFNRTApTRUxFQ1QKICBEQVRFX1RSVU5DKERBVEUoY3JlYXRpb25fZGF0ZSksIFlFQVIpIEFTIHllYXIsCiAgU1VNKHZpZXdfY291bnRfZGVsdGEpIEFTIHRvdGFsX2RlbHRhIApGUk9NICgKICBTRUxFQ1QKICAgIGlkLAogICAgY3JlYXRpb25fZGF0ZSwKICAgIGIudmlld19jb3VudCAtIGEudmlld19jb3VudCBBUyB2aWV3X2NvdW50X2RlbHRhCiAgRlJPTQogICAgYGZoLWJpZ3F1ZXJ5LnN0YWNrb3ZlcmZsb3dfYXJjaGl2ZS4yMDE3MDNfcG9zdHNfcXVlc3Rpb25zYCBhCiAgTEVGVCBKT0lOICgKICAgIFNFTEVDVAogICAgICBpZCwKICAgICAgdmlld19jb3VudAogICAgRlJPTQogICAgICBgZmgtYmlncXVlcnkuc3RhY2tvdmVyZmxvd19hcmNoaXZlLjIwMTcxMl9wb3N0c19xdWVzdGlvbnNgICkgYgogIFVTSU5HCiAgICAoaWQpICkKR1JPVVAgQlkKICB5ZWFyCk9SREVSIEJZCiAgeWVhciBBU0MKCmBgYAoKTG9hZCBpbiB0aGUgcHJlY29tcHV0ZWQgZGF0YS4KCmBgYHtyfQpmaWxlX3BhdGggPC0gInN0YWNrX292ZXJmbG93X2RlbHRhLmNzdiIKZGZfZGVsdGFzIDwtIHJlYWRfY3N2KGZpbGVfcGF0aCkgJT4lIG11dGF0ZShwZXJjID0gdG90YWxfZGVsdGEgLyBzdW0oYXMubnVtZXJpYyh0b3RhbF9kZWx0YSkpKQpkZl9kZWx0YXMKYGBgCgpPdmVydmlldyBvZiAyMDE3IHZpZXcgY291bnRzIG9uIG9sZGVyIHBvc3RzLgoKYGBge3J9CnBsb3QgPC0gZ2dwbG90KGRmX2RlbHRhcyAlPiUgZmlsdGVyKHllYXIgPj0geW1kKCcyMDA5LTAxLTAxJyksIHllYXIgPD0geW1kKCcyMDE2LTAxLTAxJykpLCBhZXMoeD15ZWFyLCB5PXBlcmMpKSArCiAgICAgICAgICBnZW9tX2JhcihhbHBoYT0wLjksIHN0YXQ9ImlkZW50aXR5IiwgZmlsbD1zdGFja19vdmVyZmxvd19jb2xvcikgKwogICAgICAgICAgc2NhbGVfeF9kYXRlKGRhdGVfYnJlYWtzPScxIHllYXInLCBkYXRlX2xhYmVscz0nJVknLCBtaW5vcl9icmVha3MgPSBOVUxMKSArCiAgICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXBlcmNlbnQpICsKICAgICAgICAgIGxhYnModGl0bGU9J1Byb3BvcnRpb24gb2YgMjAxNyBWaWV3cyBvbiBPbGRlciBTdGFjayBPdmVyZmxvdyBRdWVzdGlvbnMgYnkgWWVhcicsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZT0nRnJvbSBNYXJjaCAxM3RoLCAyMDE3IHRvIERlY2VtYmVyIDNyZCwgMjAxNy4gVmlzdWFsaXphdGlvbiBFeGNsdWRlcyBQYXJ0aWFsIFllYXJzJywKICAgICAgICAgICAgICAgeD0nWWVhciBRdWVzdGlvbiBXYXMgUG9zdGVkJywKICAgICAgICAgICAgICAgeT0nJSBvZiBBbGwgVmlld3MnLAogICAgICAgICAgICAgICBjYXB0aW9uID0gIk1heCBXb29sZiDigJQgbWluaW1heGlyLmNvbSIKICAgICAgICAgICAgICApCgpnZ3NhdmUoJ3NvX292ZXJ2aWV3LnBuZycsIHBsb3QsIHdpZHRoPTQsIGhlaWdodD0yKQpgYGAKCiFbXShzb19vdmVydmlldy5wbmcpCgpEYXRhIHByb2Nlc3NlZCBmcm9tIHRoaXMgQmlnUXVlcnk6IChOQjogdG8gZG93bmxvYWQgbGFyZ2UgZGF0YXNldHMsIHNhdmUgYXMgYSBCaWdRdWVyeSB0YWJsZSBhbmQgZXhwb3J0IGFzIGEgQ1NWLCB0aGVuIGRvd25sb2FkIHRoZSBDU1YpCgpgYGB7c3FsIGV2YWw9RkFMU0UsIGluY2x1ZGU9VFJVRX0KI3N0YW5kYXJkU1FMCldJVEgKICBhbnN3ZXJzX29yZGVyZWQgQVMgKAogIFNFTEVDVAogICAgaWQsCiAgICBjcmVhdGlvbl9kYXRlLAogICAgcGFyZW50X2lkIEFTIHF1ZXN0aW9uX2lkLAogICAgc2NvcmUsCiAgICBST1dfTlVNQkVSKCkgT1ZFUiAoUEFSVElUSU9OIEJZIHBhcmVudF9pZCBPUkRFUiBCWSBjcmVhdGlvbl9kYXRlIEFTQykgQVMgdGltZV9yYW5rLAogICAgUk9XX05VTUJFUigpIE9WRVIgKFBBUlRJVElPTiBCWSBwYXJlbnRfaWQgT1JERVIgQlkgc2NvcmUgREVTQykgQVMgc2NvcmVfcmFuaywKICAgIENPVU5UKCopIE9WRVIgKFBBUlRJVElPTiBCWSBwYXJlbnRfaWQpIEFTIG51bV9hbnN3ZXJzCiAgRlJPTQogICAgYGZoLWJpZ3F1ZXJ5LnN0YWNrb3ZlcmZsb3dfYXJjaGl2ZS4yMDE3MTJfcG9zdHNfYW5zd2Vyc2AgKQogICAgClNFTEVDVAogIGlkLAogIHRpdGxlLAogIHRhZ3MsCiAgREFURVRJTUUoY3JlYXRpb25fZGF0ZSkgQVMgY3JlYXRpb25fZGF0ZSwKICBhY2NlcHRlZF9hbnN3ZXJfaWQsCiAgdmlld19jb3VudCwKICBzY29yZSwKICBudW1fYW5zd2VycywKICBmX2Fuc3dlcl9pZCwKICBEQVRFVElNRShmX2NyZWF0aW9uX2RhdGUpIEFTIGZfY3JlYXRpb25fZGF0ZSwKICBmX3Njb3JlLAogIGZfc2NvcmVfcmFuaywKICBhX2Fuc3dlcl9pZCwKICBEQVRFVElNRShhX2NyZWF0aW9uX2RhdGUpIEFTIGFfY3JlYXRpb25fZGF0ZSwKICBhX3Njb3JlLAogIGFfdGltZV9yYW5rLAogIGFfc2NvcmVfcmFuaywKICBUSU1FU1RBTVBfRElGRihmX2NyZWF0aW9uX2RhdGUsIGNyZWF0aW9uX2RhdGUsIFNFQ09ORCkgQVMgdGltZV90b19mLAogIFRJTUVTVEFNUF9ESUZGKGFfY3JlYXRpb25fZGF0ZSwgY3JlYXRpb25fZGF0ZSwgU0VDT05EKSBBUyB0aW1lX3RvX2EKRlJPTQogIGBmaC1iaWdxdWVyeS5zdGFja292ZXJmbG93X2FyY2hpdmUuMjAxNzEyX3Bvc3RzX3F1ZXN0aW9uc2AgcQpMRUZUIEpPSU4gKAogIFNFTEVDVAogICAgaWQgQVMgZl9hbnN3ZXJfaWQsCiAgICBjcmVhdGlvbl9kYXRlIEFTIGZfY3JlYXRpb25fZGF0ZSwKICAgIHF1ZXN0aW9uX2lkIEFTIGZfcXVlc3Rpb25faWQsCiAgICBzY29yZSBBUyBmX3Njb3JlLAogICAgc2NvcmVfcmFuayBBUyBmX3Njb3JlX3JhbmssCiAgICBudW1fYW5zd2VycwogIEZST00KICAgIGFuc3dlcnNfb3JkZXJlZAogIFdIRVJFCiAgICB0aW1lX3JhbmsgPSAxICkgZgpPTgogIHEuaWQgPSBmLmZfcXVlc3Rpb25faWQKTEVGVCBKT0lOICgKICBTRUxFQ1QKICAgIGlkIEFTIGFfYW5zd2VyX2lkLAogICAgY3JlYXRpb25fZGF0ZSBBUyBhX2NyZWF0aW9uX2RhdGUsCiAgICBzY29yZSBBUyBhX3Njb3JlLAogICAgdGltZV9yYW5rIEFTIGFfdGltZV9yYW5rLAogICAgc2NvcmVfcmFuayBBUyBhX3Njb3JlX3JhbmsKICBGUk9NCiAgICBhbnN3ZXJzX29yZGVyZWQgKSBhCk9OCiAgcS5hY2NlcHRlZF9hbnN3ZXJfaWQgPSBhLmFfYW5zd2VyX2lkCldIRVJFCiAgY3JlYXRpb25fZGF0ZSA+PSAnMjAxNy0wMS0wMSAwMDowMDowMCcgQU5EIGNyZWF0aW9uX2RhdGUgPCAnMjAxNy0xMi0wMSAwMDowMDowMCcKYGBgCgoKYGBge3J9CmZpbGVfcGF0aCA8LSAifi9Eb3dubG9hZHMvc3RhY2tfb3ZlcmZsb3dfMjAxNy5jc3YiCmRmIDwtIHJlYWRfY3N2KGZpbGVfcGF0aCwgcHJvZ3Jlc3M9RkFMU0UpCmRmICU+JSBoZWFkKCkKYGBgCgpBZGQgY29sdW1ucyByZWxldmFudCB0byB0aGUgdGltaW5nIHdoZW4gdGhlIHBvc3Qgd2FzIG1hZGUuIFRoZSByYXcgZGF0YSBpcyBpbiBgVVRDYCwgc28gaXQgbXVzdCBiZSBjb252ZXJ0ZWQgdG8gRWFzdGVybi4KCmBgYHtyfQpkZiA8LSBkZiAlPiUgbXV0YXRlKAogIGNyZWF0aW9uX2RhdGUgPSB3aXRoX3R6KGNyZWF0aW9uX2RhdGUsICJBbWVyaWNhL05ld19Zb3JrIiksCiAgaG91cl9wb3N0ZWQgPSBob3VyKGNyZWF0aW9uX2RhdGUpLAogIHdlZWtkYXlfcG9zdGVkID0gd2RheShjcmVhdGlvbl9kYXRlLCBsYWJlbD1ULCBhYmJyPUYpLAogIHdlZWtfcG9zdGVkID0gZmxvb3JfZGF0ZShjcmVhdGlvbl9kYXRlLCAnMSB3ZWVrJyksCiAgbW9udGhfcG9zdGVkID0gZmxvb3JfZGF0ZShjcmVhdGlvbl9kYXRlLCAnMSBtb250aCcpCikKYGBgCgpBZGQgYSBmZXcgYmVzcG9rZSBmZWF0dXJlczoKCmBgYHtyfQpkZiA8LSBkZiAlPiUgbXV0YXRlKAogIG51bV9hbnN3ZXJzID0gaWZlbHNlKGlzLm5hKG51bV9hbnN3ZXJzKSwgMCwgbnVtX2Fuc3dlcnMpLAogIGZfYW5zd2VyX2luID0gaWZlbHNlKCFpcy5uYSh0aW1lX3RvX2YpICYgdGltZV90b19mIDwgNjAqNjAqNCwgMSwgMCksCiAgYV9hbnN3ZXJfaW4gPSBpZmVsc2UoIWlzLm5hKHRpbWVfdG9fYSkgJiB0aW1lX3RvX2EgPCA2MCo2MCo0LCAxLCAwKSwKICBudW1fdGFncyA9IDEgKyBzdHJfbWF0Y2hfYWxsKHRhZ3MsICdcXHwnKSAlPiUgbGFwcGx5KGxlbmd0aCkgJT4lIHVubGlzdCgpLAogIGlzX2Fuc3dlcmVkID0gaWZlbHNlKCFpcy5uYSh0aW1lX3RvX2EpLCAxLCAwKQopCmBgYAoKCiMgT3ZlcnZpZXcKCkZvciBhbGwgcXVlc3Rpb25zIGFza2VkOgoKIyMgV2Vla2x5CgpgYGB7cn0KcGxvdCA8LSBnZ3Bsb3QoZGYsIGFlcyh4PXdlZWtfcG9zdGVkLCB5PS4uY291bnQuLikpICsKICBnZW9tX2JhcihmaWxsID0gc3RhY2tfb3ZlcmZsb3dfY29sb3IsIGFscGhhPTAuOSkgKwogIHNjYWxlX3hfZGF0ZXRpbWUoZGF0ZV9icmVha3M9JzIgbW9udGhzJywgZGF0ZV9sYWJlbHM9JyViJykgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEpICsKICBsYWJzKHRpdGxlPSdOZXcgU3RhY2sgT3ZlcmZsb3cgUXVlc3Rpb25zIGluIDIwMTcnLAogICAgICAgeD0nV2VlayBRdWVzdGlvbiB3YXMgUG9zdGVkJywKICAgICAgIHk9JyMgb2YgUXVlc3Rpb25zIFBvc3RlZCcsCiAgICAgICBjYXB0aW9uID0gIk1heCBXb29sZiDigJQgbWluaW1heGlyLmNvbSIpCgpnZ3NhdmUoJ3dlZWtseV9jb3VudC5wbmcnLCBwbG90LCB3aWR0aD00LCBoZWlnaHQ9MikKYGBgCgohW10od2Vla2x5X2NvdW50LnBuZykKCiMgRmFjZXQgYnkgVGFncwoKIyMgQnkgIyBvZiBUYWdzCgpgYGB7cn0KZGZfdGFnX2NvdW50cyA8LSBkZiAlPiUKICBncm91cF9ieShudW1fdGFncykgJT4lCiAgc3VtbWFyaXplKGNvdW50ID0gbigpLAogICAgICAgICAgICBwZXJjX2Fuc3dlcmVkID0gc3VtKGlzX2Fuc3dlcmVkKS9jb3VudCkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShwZXJjX2FsbCA9IGNvdW50L3N1bShjb3VudCkpICU+JQogIGFycmFuZ2UobnVtX3RhZ3MpCgpkZl90YWdfY291bnRzCmBgYAoKYGBge3J9CnBsb3QgPC0gZ2dwbG90KGRmX3RhZ19jb3VudHMsIGFlcyh4PWZhY3RvcihudW1fdGFncyksIHkgPSBwZXJjX2FsbCwgZmlsbD1wZXJjX2FsbCkpICsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPXBlcmNlbnQocGVyY19hbGwpLCBjb2xvcj1wZXJjX2FsbCksIHZqdXN0PS0wLjI1LCBmYW1pbHk9IlNvdXJjZSBTYW5zIFBybyBCb2xkIiwgc2l6ZT0yLjUpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzKG9wdGlvbj0naW5mZXJubycsIGxpbWl0cz1jKDAsMSksIGd1aWRlPUYpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uPSdpbmZlcm5vJywgbGltaXRzPWMoMCwxKSwgZ3VpZGU9RikgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9cGVyY2VudCkgKwogIGxhYnModGl0bGU9J0JyZWFrZG93biBvZiAjIG9mIFRhZ3MgaW4gU3RhY2sgT3ZlcmZsb3cgUXVlc3Rpb25zJywKICAgICAgIHN1YnRpdGxlPSdGb3IgUXVlc3Rpb25zIEFza2VkIEZyb20gSmFudWFyeSAyMDE3IHRvIE5vdmVtYmVyIDIwMTcnLAogICAgICAgeD0nIyBvZiBUYWdzIGluIFF1ZXN0aW9uJywKICAgICAgIHk9JyUgb2YgU3RhY2sgT3ZlcmZsb3cgUXVlc3Rpb25zJywKICAgICAgIGNhcHRpb24gPSAiTWF4IFdvb2xmIOKAlCBtaW5pbWF4aXIuY29tIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJywKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTAsIGZhbWlseT0iU291cmNlIFNhbnMgUHJvIEJvbGQiLCBtYXJnaW49bWFyZ2luKHQgPSAtMC4xLCBiID0gMC4wLCB1bml0PSdjbScpKSkKCmdnc2F2ZSgnc29fdGFnX2JyZWFrZG93bi5wbmcnLCBwbG90LCB3aWR0aD00LCBoZWlnaHQ9Mi41KQpgYGAKCiFbXShzb190YWdfYnJlYWtkb3duLnBuZykKCiMgVG9wIFRhZ3MKCmBgYHtyfQpkZl90b3BfdGFncyA8LSBkZiAlPiUKICBzZWxlY3QoY3JlYXRpb25fZGF0ZSwgdGFncywgc2NvcmUsIG51bV9hbnN3ZXJzLCBpc19hbnN3ZXJlZCkgJT4lCiAgdW5uZXN0X3Rva2Vucyh0YWcsIHRhZ3MsIHRva2VuPXN0cmluZ3I6OnN0cl9zcGxpdCwgcGF0dGVybj1maXhlZCgifCIpKSAlPiUKICBncm91cF9ieSh0YWcpICU+JQogIHN1bW1hcml6ZShjb3VudCA9IG4oKSwKICAgICAgICAgICAgbWVkX3Njb3JlID0gbWVkaWFuKHNjb3JlKSwKICAgICAgICAgICAgbWVkX2Fuc3dlcnMgPSBtZWRpYW4obnVtX2Fuc3dlcnMpLAogICAgICAgICAgICBwZXJjX2lzX2Fuc3dlcmVkID0gc3VtKGlzX2Fuc3dlcmVkKS9jb3VudCkgJT4lCiAgYXJyYW5nZShkZXNjKGNvdW50KSkgJT4lCiAgaGVhZCgxMDAwKQoKZGZfdG9wX3RhZ3MgJT4lIGhlYWQoKSAgICAgICAgICAgICAgIApgYGAKCmBgYHtyfQpkZl90b3BfdGFnc19tb250aCA8LSBkZiAlPiUKICBzZWxlY3QobW9udGhfcG9zdGVkLCB0YWdzKSAlPiUKICB1bm5lc3RfdG9rZW5zKHRhZywgdGFncywgdG9rZW49c3RyaW5ncjo6c3RyX3NwbGl0LCBwYXR0ZXJuPWZpeGVkKCJ8IikpICU+JQogIGZpbHRlcih0YWcgJWluJSAoZGZfdG9wX3RhZ3MgJT4lIGhlYWQoNDApICU+JSBwdWxsKHRhZykpKSAlPiUKICBncm91cF9ieShtb250aF9wb3N0ZWQsIHRhZykgJT4lCiAgc3VtbWFyaXplKGNvdW50PW4oKSkgJT4lCiAgYXJyYW5nZShtb250aF9wb3N0ZWQpICU+JQogIGZpbHRlcihtb250aF9wb3N0ZWQgPj0gZGF0ZSgnMjAxNy0wMS0wMScpKQoKZGZfdG9wX3RhZ3NfbW9udGggJT4lIGhlYWQoKSAgICAgICAgICAgICAgIApgYGAKCiMjIE1vbnRobHkgYnkgVGFncwoKYGBge3J9CnBsb3QgPC0gZ2dwbG90KGRmX3RvcF90YWdzX21vbnRoLCBhZXMoeD1tb250aF9wb3N0ZWQsIHk9Y291bnQsIGZpbGw9dGFnKSkgKwogIGdlb21fYmFyKGFscGhhPTAuOSwgc3RhdD0iaWRlbnRpdHkiKSArCiAgZ2VvbV9zbW9vdGgoc2U9RiwgbWV0aG9kPSJsbSIsIGNvbG9yPSJibGFjayIsIHNpemU9MC41KSArCiAgc2NhbGVfeF9kYXRldGltZShkYXRlX2JyZWFrcz0nMyBtb250aHMnLCBkYXRlX2xhYmVscz0nJWInKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIGZhY2V0X3dyYXAofiB0YWcsIG5yb3c9MTAsIG5jb2w9NCwgc2NhbGVzPSJmcmVlX3kiKSArCiAgbGFicyh0aXRsZT0nTmV3IFN0YWNrIE92ZXJmbG93IFF1ZXN0aW9ucyBmb3IgdGhlIFRvcCA0MCBUYWdzJywKICAgICAgIHN1YnRpdGxlPSdGcm9tIEphbnVhcnkgMjAxNyB0byBOb3ZlbWJlciAyMDE3JywKICAgICAgIHg9J01vbnRoIFF1ZXN0aW9uIFdhcyBQb3N0ZWQnLAogICAgICAgeT0nIyBvZiBRdWVzdGlvbnMgUG9zdGVkIFdpdGggVGFnJywKICAgICAgIGNhcHRpb24gPSAiTWF4IFdvb2xmIOKAlCBtaW5pbWF4aXIuY29tIgogICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCwgZmFtaWx5PSJTb3VyY2UgU2FucyBQcm8gQm9sZCIsIG1hcmdpbj1tYXJnaW4odCA9IC0wLjEsIGIgPSAwLjAsIHVuaXQ9J2NtJykpKQoKZ2dzYXZlKCdtb250aGx5X2NvdW50X3RhZy5wbmcnLCBwbG90LCB3aWR0aD02LCBoZWlnaHQ9OCkKYGBgCgohW10obW9udGhseV9jb3VudF90YWcucG5nKQojIyBEYXkvSG91ciBvZiBXZWVrIHRhZ3Mgd2VyZSBwb3N0ZWQKClJlbGF0ZWQ6IGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5ibG9nLzIwMTcvMDQvMTkvcHJvZ3JhbW1pbmctbGFuZ3VhZ2VzLXVzZWQtbGF0ZS1uaWdodC8KCmBgYHtyfQpkZl90b3BfdGFnc19ocl9kb3kgPC0gZGYgJT4lCiAgc2VsZWN0KGhvdXJfcG9zdGVkLCB3ZWVrZGF5X3Bvc3RlZCwgdGFncykgJT4lCiAgdW5uZXN0X3Rva2Vucyh0YWcsIHRhZ3MsIHRva2VuPXN0cmluZ3I6OnN0cl9zcGxpdCwgcGF0dGVybj1maXhlZCgifCIpKSAlPiUKICBmaWx0ZXIodGFnICVpbiUgKGRmX3RvcF90YWdzICU+JSBoZWFkKDQwKSAlPiUgcHVsbCh0YWcpKSkgJT4lCiAgZ3JvdXBfYnkoaG91cl9wb3N0ZWQsIHdlZWtkYXlfcG9zdGVkLCB0YWcpICU+JQogIHN1bW1hcml6ZShjb3VudD1uKCkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBncm91cF9ieSh0YWcpICU+JQogIG11dGF0ZShwcm9wb3J0aW9uPWNvdW50L3N1bShjb3VudCkpCgpkZl90b3BfdGFnc19ocl9kb3kgJT4lIGhlYWQoKSAKYGBgCgpgYGB7cn0KcGxvdCA8LSBnZ3Bsb3QoZGZfdG9wX3RhZ3NfaHJfZG95LCBhZXMoeD1ob3VyX3Bvc3RlZCwgeT1mY3RfcmV2KHdlZWtkYXlfcG9zdGVkKSwgZmlsbD1wcm9wb3J0aW9uKSkgKwogIGdlb21fcmFzdGVyKHN0YXQ9ImlkZW50aXR5IiwgaW50ZXJwb2xhdGU9RikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD05LCBjb2xvcj0id2hpdGUiLCBzaXplPTAuNSwgYWxwaGE9MC43NSkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD0xNywgY29sb3I9IndoaXRlIiwgc2l6ZT0wLjUsIGFscGhhPTAuNzUpICsKICBzY2FsZV94X2Rpc2NyZXRlKCkgKwogIHNjYWxlX3lfZGlzY3JldGUoKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbj0iaW5mZXJubyIsIGxhYmVscz1wZXJjZW50KSArCiAgZmFjZXRfd3JhcCh+IHRhZywgbnJvdz0xMCwgbmNvbD00KSArCiAgbGFicyh0aXRsZT0nTmV3IFN0YWNrIE92ZXJmbG93IFF1ZXN0aW9ucyBmb3IgdGhlIFRvcCA0MCBUYWdzLCBieSBUaW1lIFBvc3RlZCcsCiAgICAgICBzdWJ0aXRsZT0nRnJvbSBKYW51YXJ5IDIwMTcgdG8gTm92ZW1iZXIgMjAxNywgVmVydGljYWwgTGluZXMgSW5kaWNhdGUgOSBBTSAtIDUgUE0gRWFzdGVybicsCiAgICAgICB4PSdIb3VyIFF1ZXN0aW9uIFdhcyBQb3N0ZWQgKDEyIEFNIC0gMTEgUE0gRWFzdGVybiBUaW1lKScsCiAgICAgICB5PSdEYXkgb2YgV2VlayBRdWVzdGlvbiBXYXMgUG9zdGVkJywKICAgICAgIGZpbGw9J1Byb3BvcnRpb24gb2YgQWxsIFF1ZXN0aW9uc1xuUG9zdGVkIHcvIFRhZycsCiAgICAgICBjYXB0aW9uID0gIk1heCBXb29sZiDigJQgbWluaW1heGlyLmNvbSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAndG9wJywKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTAsIGZhbWlseT0iU291cmNlIFNhbnMgUHJvIEJvbGQiLCBtYXJnaW49bWFyZ2luKHQgPSAtMC4xLCBiID0gMC4wLCB1bml0PSdjbScpKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNSksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICAgICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMSwgdW5pdD0nY20nKSwKICAgICAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoMC4yNSwgdW5pdD0nY20nKSwKICAgICAgICBsZWdlbmQubWFyZ2luID0gbWFyZ2luKGMoMCwgMCwgLTAuNSwgMCksIHVuaXQ9J2NtJykpCgpnZ3NhdmUoJ21vbnRobHlfY291bnRfaHJfZG95LnBuZycsIHBsb3QsIHdpZHRoPTYsIGhlaWdodD04KQpgYGAKCiFbXShtb250aGx5X2NvdW50X2hyX2RveS5wbmcpCgojIyBUYWcgRGlzdHJpYnV0aW9ucwoKYGBge3J9CmRmX3RvcF90YWdzX2Rpc3RyaWJ1dGlvbiA8LSBkZiAlPiUKICBzZWxlY3QobW9udGhfcG9zdGVkLCB0YWdzLCB2aWV3X2NvdW50LCBudW1fYW5zd2VycywgdGltZV90b19mLCB0aW1lX3RvX2EpICU+JQogIHVubmVzdF90b2tlbnModGFnLCB0YWdzLCB0b2tlbj1zdHJpbmdyOjpzdHJfc3BsaXQsIHBhdHRlcm49Zml4ZWQoInwiKSkgJT4lCiAgZmlsdGVyKHRhZyAlaW4lIChkZl90b3BfdGFncyAlPiUgaGVhZCg0MCkgJT4lIHB1bGwodGFnKSksIG1vbnRoX3Bvc3RlZCA+PSBkYXRlKCcyMDE3LTAxLTAxJykpCgpkZl90YWdzX21lZGlhbnMgPC0gZGZfdG9wX3RhZ3NfZGlzdHJpYnV0aW9uICU+JQogIGdyb3VwX2J5KHRhZykgJT4lCiAgc3VtbWFyaXplKG1lZD1tZWRpYW4odmlld19jb3VudCkpICU+JQogIGFycmFuZ2UoZGVzYyhtZWQpKQoKZGZfdG9wX3RhZ3NfZGlzdHJpYnV0aW9uIDwtIGRmX3RvcF90YWdzX2Rpc3RyaWJ1dGlvbiAlPiUKICBtdXRhdGUodGFnID0gZmN0X3JldihmYWN0b3IodGFnLCBsZXZlbHM9KGRmX3RhZ3NfbWVkaWFucyAlPiUgcHVsbCh0YWcpKSkpKQoKZGZfdG9wX3RhZ3NfZGlzdHJpYnV0aW9uICU+JSBoZWFkKCkKYGBgCgpgYGB7cn0KcGxvdCA8LSBnZ3Bsb3QoZGZfdG9wX3RhZ3NfZGlzdHJpYnV0aW9uLCBhZXMoeD12aWV3X2NvdW50LCB5PXRhZywgZmlsbD10YWcpKSArCiAgICAgICAgICAjZ2VvbV9ib3hwbG90KCkgKwogICAgICAgICAgZ2VvbV9kZW5zaXR5X3JpZGdlcyhzY2FsZSA9IDUsIHNpemUgPSAwLjI1LCByZWxfbWluX2hlaWdodCA9IDAuMDMpICsKICAgICAgICAgIHNjYWxlX3lfZGlzY3JldGUoKSArCiAgICAgICAgICBzY2FsZV94X2xvZzEwKGxhYmVscz1jb21tYSwgbGltaXRzPWMoMTAsMTBeNCksIGJyZWFrcz0xMF4oMTo0KSwgbWlub3JfYnJlYWtzPU5VTEwpICsKICAgICAgICAgIGxhYnModGl0bGU9J05ldyBTdGFjayBPdmVyZmxvdyBRdWVzdGlvbnMgZm9yIHRoZSBUb3AgNDAgVGFncycsCiAgICAgICAgICAgICAgICBzdWJ0aXRsZT0nRnJvbSBKYW51YXJ5IDIwMTcgdG8gTm92ZW1iZXIgMjAxNycsCiAgICAgICAgICAgICAgIHg9JyMgb2YgVmlld3Mgb24gUG9zdCcsCiAgICAgICAgICAgICAgIHk9JyMgb2YgVmlld3Mgb24gUG9zdCcsCiAgICAgICAgICAgICAgIGNhcHRpb24gPSAiTWF4IFdvb2xmIOKAlCBtaW5pbWF4aXIuY29tIikgKwogICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnLAogICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwLCBmYW1pbHk9IlNvdXJjZSBTYW5zIFBybyBCb2xkIiwgbWFyZ2luPW1hcmdpbih0ID0gLTAuMSwgYiA9IDAuMCwgdW5pdD0nY20nKSksCiAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkpCgpnZ3NhdmUoJ3ZpZXdzX3RhZ19kaXN0LnBuZycsIHBsb3QsIHdpZHRoPTYsIGhlaWdodD04KQpgYGAKCiFbXSh2aWV3c190YWdfZGlzdC5wbmcpCgojIyBUYWcgV29yZGNsb3VkCgpgYGB7cn0KZGZfdG9wX3RhZ3Nfd29yZHMgPC0gZGYgJT4lCiAgICAgICAgICAgICAgICBzZWxlY3QodGFncywgdGl0bGUpICU+JQogICAgICAgICAgICAgICAgdW5uZXN0X3Rva2Vucyh0YWcsIHRhZ3MsIHRva2VuPXN0cmluZ3I6OnN0cl9zcGxpdCwgcGF0dGVybj1maXhlZCgifCIpKSAlPiUKICAgICAgICAgICAgICAgIGZpbHRlcih0YWcgJWluJSAoZGZfdG9wX3RhZ3MgJT4lIGhlYWQoMjApICU+JSBwdWxsKHRhZykpKSAlPiUKICAgICAgICAgICAgICAgIHVubmVzdF90b2tlbnMod29yZCwgdGl0bGUsIHRva2VuPSJ3b3JkcyIpICU+JQogICAgICAgICAgICAgICAgZmlsdGVyKCEod29yZCAlaW4lIHN0b3Bfd29yZHMkd29yZCkpICU+JQogICAgICAgICAgICAgICAgZ3JvdXBfYnkodGFnLCB3b3JkKSAlPiUKICAgICAgICAgICAgICAgIHN1bW1hcml6ZShjb3VudD1uKCkpICU+JQogICAgICAgICAgICAgICAgdW5ncm91cCgpICU+JQogICAgICAgICAgICAgICAgZ3JvdXBfYnkodGFnKSAlPiUKICAgICAgICAgICAgICAgIG11dGF0ZShtYXhfbm9ybSA9IGNvdW50L21heChjb3VudCkpICU+JQogICAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKGNvdW50KSkgJT4lCiAgICAgICAgICAgICAgICB0b3BfbigyMCkgJT4lCiAgICAgICAgICAgICAgICBhcnJhbmdlKHRhZywgZGVzYyhjb3VudCkpCgpkZl90b3BfdGFnc193b3JkcyAlPiUgaGVhZCg1MCkgCmBgYAoKV29yZGNsb3VkIHRyaWNrIGluIGdncGxvdDIgYWRhcHRlZCBmcm9tIFtNaGFpcmkgTWNOZWlsbCdzIGJsb2cgcG9zdF0oaHR0cDovL21oYWlyaWhtY25laWxsLmNvbS9ibG9nLzIwMTYvMDQvMDUvd29yZGNsb3Vkcy1pbi1nZ3Bsb3QuaHRtbCkuCgpgYGB7cn0Kc2V0LnNlZWQoMTIzKSAgICMgRm9yIGdlb21fdGV4dF9yZXBlbAoKcGxvdCA8LSBnZ3Bsb3QoZGZfdG9wX3RhZ3Nfd29yZHMsIGFlcyh4ID0gMSwgeSA9IDEsIHNpemUgPSBtYXhfbm9ybSwgbGFiZWwgPSB3b3JkLCBjb2xvcj1tYXhfbm9ybSkpICsKICBnZW9tX3RleHRfcmVwZWwoc2VnbWVudC5zaXplID0gMCwgZm9yY2UgPSAxMDAsIGZhbWlseT0iUm9ib3RvIENvbmRlbnNlZCBCb2xkIikgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDIsIDQpLCBndWlkZSA9IEZBTFNFKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpcyhlbmQ9MC44LCBkaXNjcmV0ZT1GLCBvcHRpb249ImluZmVybm8iLCBndWlkZSA9IEZBTFNFKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IE5VTEwpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gTlVMTCkgKwogIGZhY2V0X3dyYXAofiB0YWcsIG5yb3c9MTAsIG5jb2w9NCkgKwogIGxhYnModGl0bGU9J1dvcmRjbG91ZCBvZiBXb3JkcyBpbiBUaXRsZXMgb2YgU3RhY2sgT3ZlcmZsb3cgUXVlc3Rpb25zIGZvciB0aGUgVG9wIDIwIFRhZ3MnLAogICAgICAgc3VidGl0bGU9J0Zyb20gSmFudWFyeSAyMDE3IHRvIE5vdmVtYmVyIDIwMTcnLAogICAgICAgeD0nTW9udGggUXVlc3Rpb24gV2FzIFBvc3RlZCcsCiAgICAgICB5PScjIG9mIFF1ZXN0aW9ucyBQb3N0ZWQgV2l0aCBUYWcnLAogICAgICAgY2FwdGlvbiA9ICJNYXggV29vbGYg4oCUIG1pbmltYXhpci5jb20iKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCwgZmFtaWx5PSJTb3VyY2UgU2FucyBQcm8gQm9sZCIsIG1hcmdpbj1tYXJnaW4odCA9IC0wLjEsIGIgPSAwLjAsIHVuaXQ9J2NtJykpLAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkKICApCgpnZ3NhdmUoJ3NvX3RhZ193b3JkY2xvdWQucG5nJywgcGxvdCwgd2lkdGg9NiwgaGVpZ2h0PTgpCmBgYAoKIVtdKHNvX3RhZ193b3JkY2xvdWQucG5nKQoKIyMgRGlzdHJpYnV0aW9uIG9mIFRpbWVzIHRvIGFuIGFjY2VwdGFibGUgYW5zd2VyCgpgYGB7cn0KZGZfdGFnX21lZGlhbiA8LSBkZl90b3BfdGFnc19kaXN0cmlidXRpb24gJT4lCiAgZ3JvdXBfYnkodGFnKSAlPiUKICBzdW1tYXJpemUoY291bnQ9bigpLAogICAgbWVkID0gbWVkaWFuKHRpbWVfdG9fYSwgbmEucm09VCkpICU+JQogIGFycmFuZ2UobWVkKSAlPiUKICBtdXRhdGUodGFnID0gZmN0X3JldihmYWN0b3IodGFnLCBsZXZlbHM9dGFnKSkpCgpkZl90YWdfbWVkaWFuICU+JSBoZWFkKDQwKQpgYGAKCmBgYHtyfQpkZl90YWdfbWVkaWFuIDwtIGRmX3RhZ19tZWRpYW4gJT4lCiAgYXJyYW5nZShkZXNjKGNvdW50KSkgJT4lCiAgaGVhZCg0MCkgJT4lCiAgYXJyYW5nZShtZWQpICU+JQogIG11dGF0ZSh0YWcgPSBmY3RfcmV2KGZhY3Rvcih0YWcsIGxldmVscz10YWcpKSkKCnBsb3QgPC0gZ2dwbG90KGRmX3RvcF90YWdzX2Rpc3RyaWJ1dGlvbiAlPiUgZmlsdGVyKCFpcy5uYSh0aW1lX3RvX2EpLCB0YWcgJWluJSAoZGZfdGFnX21lZGlhbiAlPiUgIG5hLm9taXQoKSAlPiUgcHVsbCh0YWcpKSkgJT4lIG11dGF0ZSh0YWcgPSBmYWN0b3IodGFnLCBsZXZlbHM9KGRmX3RhZ19tZWRpYW4gJT4lICBuYS5vbWl0KCkgJT4lIHB1bGwodGFnKSkpKSwgYWVzKHg9dGltZV90b19hLCBmaWxsPXRhZykpICsKICBnZW9tX2hpc3RvZ3JhbSgpICsKICBnZW9tX3ZsaW5lKGRhdGE9ZGZfdGFnX21lZGlhbiAlPiUgbXV0YXRlKHRhZyA9IGZjdF9yZWxldmVsKGZhY3Rvcih0YWcpLCAoZGZfdGFnX21lZGlhbiAlPiUgIG5hLm9taXQoKSAlPiUgcHVsbCh0YWcpKSkpLCBhZXMoeGludGVyY2VwdD1tZWQpLCBsaW5ldHlwZT0iZGFzaGVkIikgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsNCo2MCo2MCksIGxhYmVscz0wOjQsIGJyZWFrcz1zZXEoMCw0KjYwKjYwLCA2MCo2MCkpICsKICAjc2NhbGVfeF9sb2cxMChsaW1pdHMgPSBjKDEsMTBeMyo2MCo2MCksIGxhYmVscz1jKDAsIDEwXigwOjMpKSwgYnJlYWtzPWMoMCwgMTBeKDA6MykqNjAqNjApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSkgKwogIGZhY2V0X3dyYXAofiB0YWcsIG5yb3c9MTAsIG5jb2w9NCwgc2NhbGVzPSJmcmVlX3kiKSArCiAgbGFicyh0aXRsZT0nRGlzdHJpYnV0aW9uIG9mIFRpbWUtVG8tQW5zd2VyIFN0YWNrIE92ZXJmbG93IFF1ZXN0aW9ucyBmb3IgdGhlIFRvcCA0MCBUYWdzJywKICAgICAgIHN1YnRpdGxlPSdGcm9tIEphbnVhcnkgMjAxNyB0byBOb3ZlbWJlciAyMDE3LCBTb3J0ZWQgYnkgTG93ZXN0IE1lZGlhbiBUaW1lIHRvIEFjY2VwdGVkIEFuc3dlcicsCiAgICAgICB4PSdNZWRpYW4gVGltZSAoSG91cnMpIFVudGlsIEFjY2VwdGVkIEFuc3dlciBpcyBQb3N0ZWQgRm9yIFF1ZXN0aW9ucyBpbiBUYWcnLAogICAgICAgeT0nIyBvZiBRdWVzdGlvbnMgUG9zdGVkIFdpdGggVGFnJywKICAgICAgIGNhcHRpb24gPSAiTWF4IFdvb2xmIOKAlCBtaW5pbWF4aXIuY29tIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJywKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTAsIGZhbWlseT0iU291cmNlIFNhbnMgUHJvIEJvbGQiLCBtYXJnaW49bWFyZ2luKHQgPSAtMC4xLCBiID0gMC4wLCB1bml0PSdjbScpKSkKCmdnc2F2ZSgnYWNjZXB0YWJsZV9hbnN3ZXJfZGVuc2l0eS5wbmcnLCBwbG90LCB3aWR0aD02LCBoZWlnaHQ9OCkKYGBgCgohW10oYWNjZXB0YWJsZV9hbnN3ZXJfZGVuc2l0eS5wbmcpCgpgYGB7cn0KZGZfdG9wX3RhZ3NfZGlzdHJpYnV0aW9uIDwtIGRmICU+JQogIHNlbGVjdCh0YWdzLCB2aWV3X2NvdW50LCB0aW1lX3RvX2YsIHRpbWVfdG9fYSwgaXNfYW5zd2VyZWQpICU+JQogIHVubmVzdF90b2tlbnModGFnLCB0YWdzLCB0b2tlbj1zdHJpbmdyOjpzdHJfc3BsaXQsIHBhdHRlcm49Zml4ZWQoInwiKSkgJT4lCiAgZmlsdGVyKHRhZyAlaW4lIChkZl90b3BfdGFncyAlPiUgcHVsbCh0YWcpKSkKCmRmX3RvcF90YWdzX2Rpc3RyaWJ1dGlvbiAlPiUgaGVhZCgpCmBgYAoKCiMjIyBCZXN0L1dvcnN0IExhbmd1YWdlcwoKVGhpcyBkb2Vzbid0IG5lY2Vzc2Fpcmx5IGltcGx5IG9uZSB0b29sIGlzICJiZXR0ZXIiIHRoYW4gYW5vdGhlciwgdGhlIGRpZmZlcmVuY2UgbWF5IGJlIGR1ZSB0byBxdWVzdGlvbiBkaWZmaWN1bHR5IGFuZCB0aGUgbnVtYmVyIG9mIHBlb3BsZSBza2lsbGVkIGluIHRoZSB0ZWNobm9sb2d5LgoKYGBge3J9CmRmX2FjY2VwdGFibGVfcGVyY3MgPC0gZGZfdG9wX3RhZ3NfZGlzdHJpYnV0aW9uICU+JQogIGdyb3VwX2J5KHRhZykgJT4lCiAgc3VtbWFyaXplKGNvdW50PW4oKSwKICAgICAgICAgICAgbWVkX3RpbWVfdG9fZiA9IG1lZGlhbih0aW1lX3RvX2YsIG5hLnJtPVQpLAogICAgICAgICAgICBtZWRfdGltZV90b19hID0gbWVkaWFuKHRpbWVfdG9fYSwgbmEucm09VCksCiAgICAgICAgICAgIHBlcmNfaXNfYW5zd2VyZWQgPSBzdW0oaXNfYW5zd2VyZWQpIC8gY291bnQKICApICU+JQogIGFycmFuZ2UoZGVzYyhwZXJjX2lzX2Fuc3dlcmVkKSkKCmRmX2FjY2VwdGFibGVfcGVyY3MgJT4lIGhlYWQoKQpgYGAKCmBgYHtyfQpkZl9hY2NlcHRhYmxlX3BlcmNzX3N1YnNldCA8LSBkZl9hY2NlcHRhYmxlX3BlcmNzICU+JQogIGhlYWQoMzApICU+JQogIG11dGF0ZSh0YWcgPSBmY3RfcmV2KGFzX2ZhY3Rvcih0YWcpKSkKCnBsb3QgPC0gZ2dwbG90KGRmX2FjY2VwdGFibGVfcGVyY3Nfc3Vic2V0LCBhZXMoeD10YWcsIHkgPSBwZXJjX2lzX2Fuc3dlcmVkLCBmaWxsPXBlcmNfaXNfYW5zd2VyZWQpKSArCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wZXJjZW50KHBlcmNfaXNfYW5zd2VyZWQpLCBjb2xvcj1wZXJjX2lzX2Fuc3dlcmVkKSwgaGp1c3Q9LTAuMjUsIGZhbWlseT0iU291cmNlIFNhbnMgUHJvIEJvbGQiLCBzaXplPTMuNSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpcyhvcHRpb249J2luZmVybm8nLCBsaW1pdHM9YygwLDEpLCBndWlkZT1GKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbj0naW5mZXJubycsIGxpbWl0cz1jKDAsMSksIGd1aWRlPUYpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXBlcmNlbnQsIGxpbWl0cz1jKDAsMSkpICsKICBsYWJzKHRpdGxlPSdUb3AgVGFncyBvbiBTdGFjayBPdmVyZmxvdyBmb3IgUXVlc3Rpb25zIHcvIEFjY2VwdGVkIEFuc3dlcnMnLAogICAgICAgc3VidGl0bGU9J0ZvciBRdWVzdGlvbnMgQXNrZWQgRnJvbSBKYW51YXJ5IDIwMTcgdG8gTm92ZW1iZXIgMjAxNywgb3V0IG9mIFRvcCAxLDAwMCBUYWdzJywKICAgICAgIHg9J1N0YWNrIE92ZXJmbG93IFF1ZXN0aW9uIFRhZycsCiAgICAgICB5PSclIG9mIFRhZ2dlZCBRdWVzdGlvbnMgV2hpY2ggSGF2ZSBhbiBBY2NlcHRlZCBBbnN3ZXInLAogICAgICAgY2FwdGlvbiA9ICJNYXggV29vbGYg4oCUIG1pbmltYXhpci5jb20iKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCwgZmFtaWx5PSJTb3VyY2UgU2FucyBQcm8gQm9sZCIsIG1hcmdpbj1tYXJnaW4odCA9IC0wLjEsIGIgPSAwLjAsIHVuaXQ9J2NtJykpLAogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCkpCgpnZ3NhdmUoJ2FjY2VwdGFibGVfYW5zd2VyX3RvcF8zMC5wbmcnLCBwbG90LCB3aWR0aD02LCBoZWlnaHQ9NikKYGBgCgohW10oYWNjZXB0YWJsZV9hbnN3ZXJfdG9wXzMwLnBuZykKCmBgYHtyfQpkZl9hY2NlcHRhYmxlX3BlcmNzX3N1YnNldCA8LSBkZl9hY2NlcHRhYmxlX3BlcmNzICU+JQogIHRhaWwoMzApICU+JQogIG11dGF0ZSh0YWcgPSBmY3RfcmV2KGFzX2ZhY3Rvcih0YWcpKSkKCnBsb3QgPC0gZ2dwbG90KGRmX2FjY2VwdGFibGVfcGVyY3Nfc3Vic2V0LCBhZXMoeD10YWcsIHkgPSBwZXJjX2lzX2Fuc3dlcmVkLCBmaWxsPXBlcmNfaXNfYW5zd2VyZWQpKSArCiAgZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1wZXJjZW50KHBlcmNfaXNfYW5zd2VyZWQpLCBjb2xvcj1wZXJjX2lzX2Fuc3dlcmVkKSwgaGp1c3Q9LTAuMjUsIGZhbWlseT0iU291cmNlIFNhbnMgUHJvIEJvbGQiLCBzaXplPTMuNSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpcyhvcHRpb249J2luZmVybm8nLCBsaW1pdHM9YygwLDEpLCBndWlkZT1GKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbj0naW5mZXJubycsIGxpbWl0cz1jKDAsMSksIGd1aWRlPUYpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXBlcmNlbnQsIGxpbWl0cz1jKDAsMSkpICsKICBsYWJzKHRpdGxlPSdCb3R0b20gVGFncyBvbiBTdGFjayBPdmVyZmxvdyBmb3IgUXVlc3Rpb25zIHcvIEFjY2VwdGVkIEFuc3dlcnMnLAogICAgICAgc3VidGl0bGU9J0ZvciBRdWVzdGlvbnMgQXNrZWQgRnJvbSBKYW51YXJ5IDIwMTcgdG8gTm92ZW1iZXIgMjAxNywgb3V0IG9mIFRvcCAxLDAwMCBUYWdzJywKeD0nU3RhY2sgT3ZlcmZsb3cgUXVlc3Rpb24gVGFnJywKeT0nJSBvZiBUYWdnZWQgUXVlc3Rpb25zIFdoaWNoIEhhdmUgYW4gQWNjZXB0ZWQgQW5zd2VyJywKY2FwdGlvbiA9ICJNYXggV29vbGYg4oCUIG1pbmltYXhpci5jb20iKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCwgZmFtaWx5PSJTb3VyY2UgU2FucyBQcm8gQm9sZCIsIG1hcmdpbj1tYXJnaW4odCA9IC0wLjEsIGIgPSAwLjAsIHVuaXQ9J2NtJykpLAogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCkpCgpnZ3NhdmUoJ2FjY2VwdGFibGVfYW5zd2VyX2JvdHRvbV8zMC5wbmcnLCBwbG90LCB3aWR0aD02LCBoZWlnaHQ9NikKYGBgCgohW10oYWNjZXB0YWJsZV9hbnN3ZXJfYm90dG9tXzMwLnBuZykKCiMgQWRqYWNlbmN5IE1hdHJpeCBmb3IgVG9wIFRhZ3MKCmBgYHtyfQpkZl90YWdfYWRqYWNlbmN5IDwtIGRmICU+JQogIHNlbGVjdChpZCwgdGFncywgdGltZV90b19hLCBpc19hbnN3ZXJlZCkgJT4lCiAgdW5uZXN0X3Rva2Vucyh0YWcsIHRhZ3MsIHRva2VuPXN0cmluZ3I6OnN0cl9zcGxpdCwgcGF0dGVybj1maXhlZCgifCIpKSAlPiUKICBmaWx0ZXIodGFnICVpbiUgKGRmX3RvcF90YWdzICU+JSBoZWFkKDQwKSAlPiUgcHVsbCh0YWcpKSkgJT4lCiAgaW5uZXJfam9pbigKICAgIGRmICU+JQogICAgICBzZWxlY3QoaWQsIHRhZ3MpICU+JQogICAgICB1bm5lc3RfdG9rZW5zKHRhZywgdGFncywgdG9rZW49c3RyaW5ncjo6c3RyX3NwbGl0LCBwYXR0ZXJuPWZpeGVkKCJ8IikpICU+JQogICAgICBmaWx0ZXIodGFnICVpbiUgKGRmX3RvcF90YWdzICU+JSBoZWFkKDQwKSAlPiUgcHVsbCh0YWcpKSksIGJ5PWMoImlkIiA9ICJpZCIpKSAlPiUKICBmaWx0ZXIodGFnLnggIT0gdGFnLnkpICU+JQogIGdyb3VwX2J5KHRhZy54LCB0YWcueSkgJT4lCiAgc3VtbWFyaXplKGNvdW50PW4oKSwKICAgICAgICAgICAgdG90YWxfYW5zd2VyZWQgPSBzdW0oaXNfYW5zd2VyZWQpLAogICAgICAgICAgICBwZXJjX2Fuc3dlcmVkID0gdG90YWxfYW5zd2VyZWQvY291bnQpICU+JQogIGFycmFuZ2UodGFnLngsIGRlc2MoY291bnQpKQpgYGAKCmBgYHtyfQpwbG90IDwtIGdncGxvdChkZl90YWdfYWRqYWNlbmN5ICU+JSBmaWx0ZXIoY291bnQgPj0gMTAwMCksIGFlcyh4PXRhZy54LCB5ID0gdGFnLnksIGZpbGw9Y291bnQpKSArCiAgZ2VvbV9yYXN0ZXIoKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbj0naW5mZXJubycsIHRyYW5zPSdsb2cxMCcsIGJyZWFrcz0xMF4oMTo1KSwgbGFiZWxzPWNvbW1hKSArCiAgbGFicyh0aXRsZT0nUXVlc3Rpb24gQ291bnRzIGZvciBTdGFjayBPdmVyZmxvdyBUYWcgUGFpcnMnLAogICAgICAgc3VidGl0bGU9J0ZvciBRdWVzdGlvbnMgQXNrZWQgRnJvbSBKYW51YXJ5IDIwMTcgdG8gTm92ZW1iZXIgMjAxNyAobWluIDEsMDAwIHF1ZXN0aW9ucyBwZXIgcGFpciknLAogICAgICAgeD0nU3RhY2sgT3ZlcmZsb3cgUXVlc3Rpb24gVGFnJywKICAgICAgIGZpbGw9JyMgb2YgUXVlc3Rpb25zJywKICAgICAgIGNhcHRpb24gPSAiTWF4IFdvb2xmIOKAlCBtaW5pbWF4aXIuY29tIikgKwogIHRoZW1lKAogICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDI3MCwgaGp1c3QgPSAwLCB2anVzdCA9IDAuNSksCiAgICBsZWdlbmQucG9zaXRpb24gPSAndG9wJywKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCwgZmFtaWx5PSJTb3VyY2UgU2FucyBQcm8gQm9sZCIsIG1hcmdpbj1tYXJnaW4odCA9IC0wLjEsIGIgPSAwLjAsIHVuaXQ9J2NtJykpLAogICAgI2F4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1KSwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgxLCB1bml0PSdjbScpLAogICAgbGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuMjUsIHVuaXQ9J2NtJyksCiAgICBsZWdlbmQubWFyZ2luID0gbWFyZ2luKGMoMCwgMCwgLTAuMSwgMCksIHVuaXQ9J2NtJykpCgpnZ3NhdmUoJ3NvX3RhZ19hZGphY2VuY3kucG5nJywgcGxvdCwgd2lkdGg9NiwgaGVpZ2h0PTYpCmBgYAoKIVtdKHNvX3RhZ19hZGphY2VuY3kucG5nKQoKYGBge3J9CnBsb3QgPC0gZ2dwbG90KGRmX3RhZ19hZGphY2VuY3kgJT4lIGZpbHRlcihjb3VudCA+PSAxMDAwKSwgYWVzKHg9dGFnLngsIHkgPSB0YWcueSwgZmlsbD1wZXJjX2Fuc3dlcmVkKSkgKwogIGdlb21fcmFzdGVyKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWw9c3ByaW50ZigiJTAuMGYiLCBwZXJjX2Fuc3dlcmVkKjEwMCkpLCBmYW1pbHk9IlJvYm90byBDb25kZW5zZWQgQm9sZCIsIHNpemU9MiwgY29sb3I9IndoaXRlIikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpcyhvcHRpb249J2luZmVybm8nLCBsaW1pdHM9YygwLDEpLCBicmVha3M9cHJldHR5X2JyZWFrcyg0KSwgbGFiZWxzPXBlcmNlbnQpICsKICBsYWJzKHRpdGxlPSdRdWVzdGlvbiBBbnN3ZXIgUmF0ZSBmb3IgU3RhY2sgT3ZlcmZsb3cgVGFnIFBhaXJzJywKICAgICAgIHN1YnRpdGxlPSdGb3IgUXVlc3Rpb25zIEFza2VkIEZyb20gSmFudWFyeSAyMDE3IHRvIE5vdmVtYmVyIDIwMTcgKG1pbiAxLDAwMCBxdWVzdGlvbnMgcGVyIHBhaXIpJywKICAgICAgIHg9J1N0YWNrIE92ZXJmbG93IFF1ZXN0aW9uIFRhZycsCiAgICAgICBmaWxsPSclIG9mIFRhZ2dlZCBRdWVzdGlvbnNcbldoaWNoIEhhdmUgYW4gQWNjZXB0ZWQgQW5zd2VyJywKICAgICAgIGNhcHRpb24gPSAiTWF4IFdvb2xmIOKAlCBtaW5pbWF4aXIuY29tIikgKwogIHRoZW1lKAogICAgYXhpcy50aXRsZS55PWVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDI3MCwgaGp1c3Q9MCwgdmp1c3QgPSAwLjUpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gJ3RvcCcsCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTAsIGZhbWlseT0iU291cmNlIFNhbnMgUHJvIEJvbGQiLCBtYXJnaW49bWFyZ2luKHQgPSAtMC4xLCBiID0gMC4wLCB1bml0PSdjbScpKSwKICAgICNheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNSksCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpLAogICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMSwgdW5pdD0nY20nKSwKICAgIGxlZ2VuZC5rZXkuaGVpZ2h0ID0gdW5pdCgwLjI1LCB1bml0PSdjbScpLAogICAgbGVnZW5kLm1hcmdpbiA9IG1hcmdpbihjKDAsIDAsIC0wLjEsIDApLCB1bml0PSdjbScpKQoKZ2dzYXZlKCdzb190YWdfYWRqYWNlbmN5X3BlcmNlbnQucG5nJywgcGxvdCwgd2lkdGg9NiwgaGVpZ2h0PTYpCmBgYAoKIVtdKHNvX3RhZ19hZGphY2VuY3lfcGVyY2VudC5wbmcpCgojIExJQ0VOU0UKClRoZSBNSVQgTGljZW5zZSAoTUlUKQoKQ29weXJpZ2h0IChjKSAyMDE4IE1heCBXb29sZgoKUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCwgZnJlZSBvZiBjaGFyZ2UsIHRvIGFueSBwZXJzb24gb2J0YWluaW5nIGEgY29weSBvZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZSAiU29mdHdhcmUiKSwgdG8gZGVhbCBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCwgZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0byB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnM6CgpUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpbiBhbGwgY29waWVzIG9yIHN1YnN0YW50aWFsIHBvcnRpb25zIG9mIHRoZSBTb2Z0d2FyZS4KClRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCAiQVMgSVMiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFWFBSRVNTIE9SIElNUExJRUQsIElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQgVE8gVEhFIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZLCBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQVVUSE9SUyBPUiBDT1BZUklHSFQgSE9MREVSUyBCRSBMSUFCTEUgRk9SIEFOWSBDTEFJTSwgREFNQUdFUyBPUiBPVEhFUiBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQU4gQUNUSU9OIE9GIENPTlRSQUNULCBUT1JUIE9SIE9USEVSV0lTRSwgQVJJU0lORyBGUk9NLCBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOIFRIRSBTT0ZUV0FSRS4=