Tidy Tuesday week 35 Lemurs, data from Duke Lemur Center and was cleaned by Jesse Mostipak.
# Load libraries
library(tidytuesdayR)
library(tidyverse)
library(scales)
library(ggtext)
library(gghalves)
library(DT)
library(rcartocolor)
library(lubridate)
options(dplyr.summarise.inform = FALSE)
theme_set(theme_minimal(base_size = 10))
# import data
lemurs <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-08-24/lemur_data.csv')
── Column specification ─────────────────────────────────────────────────────────────────────────────
cols(
.default = col_double(),
taxon = col_character(),
dlc_id = col_character(),
hybrid = col_character(),
sex = col_character(),
name = col_character(),
current_resident = col_character(),
stud_book = col_character(),
dob = col_date(format = ""),
estimated_dob = col_character(),
birth_type = col_character(),
birth_institution = col_character(),
estimated_concep = col_date(format = ""),
dam_id = col_character(),
dam_name = col_character(),
dam_taxon = col_character(),
dam_dob = col_date(format = ""),
sire_id = col_character(),
sire_name = col_character(),
sire_taxon = col_character(),
sire_dob = col_date(format = "")
# ... with 8 more columns
)
ℹ Use `spec()` for the full column specifications.
29130 parsing failures.
row col expected actual file
1324 age_of_living_y 1/0/T/F/TRUE/FALSE 23.77 'https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-08-24/lemur_data.csv'
1325 age_of_living_y 1/0/T/F/TRUE/FALSE 23.77 'https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-08-24/lemur_data.csv'
1326 age_of_living_y 1/0/T/F/TRUE/FALSE 23.77 'https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-08-24/lemur_data.csv'
1327 age_of_living_y 1/0/T/F/TRUE/FALSE 23.77 'https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-08-24/lemur_data.csv'
1328 age_of_living_y 1/0/T/F/TRUE/FALSE 23.77 'https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-08-24/lemur_data.csv'
.... ............... .................. ...... ..........................................................................................................
See problems(...) for more details.
tax <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-08-24/taxonomy.csv')
── Column specification ─────────────────────────────────────────────────────────────────────────────
cols(
taxon = col_character(),
latin_name = col_character(),
common_name = col_character()
)
head(lemurs)
# proportion missing data by variable
lemurs %>% summarise(across(everything(), ~mean(is.na(.)))) %>%
gather() %>%
mutate(value=round(value,3)) %>%
arrange(desc(value))
# unique values by variable
lemurs %>% summarise_all(n_distinct)
# summary of record count per animal
lemurs %>%
count(dlc_id) %>%
summary()
dlc_id n
Length:2270 Min. : 1.00
Class :character 1st Qu.: 2.00
Mode :character Median : 10.00
Mean : 36.39
3rd Qu.: 47.00
Max. :465.00
Weight by age category and gender group
# Latest recorded weight by specimen ID
weight = lemurs %>%
mutate(age_cat = case_when(age_category=="young_adult" ~ "Young-Adult",
age_category=="IJ" ~ "Infant/Juvenile",
age_category=="adult" ~ "Adult"),
gender= case_when(sex=="F"~ "Female",
sex=="M" ~ "Male")
) %>%
mutate(group=paste(age_cat,gender)) %>%
mutate(group=factor(group,levels=c("Adult Female","Adult Male",
"Young-Adult Female","Young-Adult Male",
"Infant/Juvenile Female","Infant/Juvenile Male","Infant/Juvenile NA"),
ordered=T)) %>%
group_by(dlc_id) %>%
slice(which.max(weight_date))
# median
weight %>%
group_by(group) %>%
summarise(med=median(weight_g)) %>%
arrange(desc(med))
# boxplot with barcode strips
weight %>%
ggplot(aes(y=weight_g, x=group, color=group)) +
geom_boxplot(
width = .2,
outlier.shape = NA,
fill="#f4f4f2",
position=position_nudge(x=0.2,y=0)
) +
geom_point(
shape = 95,
size = 6,
alpha = .2,
position=position_nudge(x=-0.1,y=0)
) +
scale_x_discrete(labels = function(x) str_wrap(x, width = 15)) +
scale_color_manual(values=c("#003049","#003049",
"#f77f00","#003049",
"#003049","#003049","#003049")) +
theme(legend.position="none",
panel.grid=element_line(size=.2, color="#ced4da"),
panel.grid.minor=element_blank(),
panel.grid.major.x=element_blank(),
axis.title.x=element_markdown(size=8.2, margin=margin(t=8)),
axis.title.y=element_markdown(size=8.2, margin=margin(r=8)),
axis.text.x=element_text(size=7.5, margin=margin(t=-5)),
plot.caption=element_text(size=7, color="#495057"),
plot.title.position = "plot",
plot.title=element_text(face="bold", size=13),
plot.subtitle = element_text(size=8),
plot.margin=unit(c(.5,.5,.5,.5),"cm"),
plot.background = element_rect(fill="#f4f4f2", color=NA)
) +
labs(x="**Age and gender group**", y="**Latest weight recorded** (in grams)",
caption="#TidyTuesday Week 35 | Data from Duke Lemur Center",
title="Weight of lemurs",
subtitle="by age category and gender (latest date by specimen ID), where Young Adult Females have the highest median weight at 2155 grams.")

Adult male weight by month of weigh-in
# adult male: summary of record count
lemurs %>%
filter(sex=="M") %>%
filter(age_category=="adult") %>%
count(dlc_id) %>% summary(n)
dlc_id n
Length:745 Min. : 1.00
Class :character 1st Qu.: 4.00
Mode :character Median : 15.00
Mean : 39.67
3rd Qu.: 55.00
Max. :384.00
# adult male weight by month of weigh-in
pal = colorRampPalette(rcartocolor::carto_pal(n=12,name="Prism")[1:10])
lemurs %>%
filter(sex=="M") %>%
filter(age_category=="adult") %>%
group_by(dlc_id) %>%
mutate(n=n()) %>%
filter(n>40) %>%
ggplot(aes(x=factor(birth_month), y=weight_g, color=factor(birth_month))) +
geom_half_boxplot(outlier.size = -1) +
geom_half_point(shape=21, alpha=0.4,size=1) +
scale_color_manual(values=pal(n_distinct(lemurs$month_of_weight))) +
scale_y_continuous(breaks=seq(0,5000,1000)) +
theme(legend.position="none",
panel.grid=element_line(size=.3),
panel.grid.minor=element_blank(),
panel.grid.major.x=element_blank(),
plot.title.position = "plot",
axis.title.x=element_markdown(size=8.5),
axis.title.y=element_markdown(size=8.5),
plot.title=element_text(face="bold", size=13),
plot.subtitle = element_text(size=8),
plot.margin=unit(c(.5,.5,.5,.5),"cm"),
axis.text.x=element_text(margin=margin(t=-7, b=5))) +
labs(y="**Weight** (in grams)", x="**Month** (of the year the animal was weighed)",
title="Weight of adult male lemurs",
subtitle="by month of the year the animal was weighed, for adult male lemurs that have more than 40 total records")

Lemur count by species
tax = tax %>%
mutate(common_name= ifelse(common_name=="hybrid",
paste0(common_name," ","(",latin_name,")"),common_name),
taxon= ifelse(taxon=="CMEAD","CMED", taxon))
# join
common_name = lemurs %>% left_join(tax, by="taxon") %>%
group_by(dlc_id) %>%
slice(which.max(weight_date)) %>%
ungroup() %>%
mutate(common_name_group = fct_lump(common_name,20)) %>%
count(common_name_group,sort=T) %>%
mutate(common_name_group = forcats::fct_rev(forcats::fct_inorder(common_name_group)),
common_name_group = forcats::fct_relevel(common_name_group, "Other", after = 0),
perc = paste0(sprintf("%4.1f", n / sum(n) * 100), "%"),
perc = if_else(row_number() == 1, paste(perc, "of all lemurs species"), perc),
place = if_else(row_number() == 1, 1, 0.2),
perc = paste(" ", perc, " ")
)
pal <- c(
"#cdd2d7",
rep("#adb5bd", length(common_name$common_name_group) - 4),
"#b7094c", "#0091ad", "#f9c74f"
)
common_name %>%
ggplot(aes(x=n, y=common_name_group, fill=common_name_group)) +
geom_col() +
geom_text(aes(label=perc, hjust = place), size=2.8, fontface = "bold") +
scale_fill_manual(values = pal, guide = "none") +
scale_x_continuous(expand = c(.01, .01)) +
theme_void(base_size = 10) +
theme(axis.text.y=element_text(hjust=1, size=8),
plot.margin = margin(15, 30, 15, 15),
plot.title.position = "plot",
plot.title=element_text(face="bold", size=13,margin=margin(b=10))) +
labs(title ="Lemur count by species")

Weight of female lemurs compared to mal
female = lemurs %>% left_join(tax, by="taxon") %>%
filter(preg_status!="P") %>%
filter(age_category!="IJ") %>%
drop_na(common_name, sex, dlc_id, weight_g) %>%
group_by(common_name, sex, dlc_id) %>%
summarise(weight=mean(weight_g)) %>%
ungroup() %>%
group_by(common_name, sex) %>%
summarise(weight2=mean(weight)) %>%
mutate(diff=weight2-lag(weight2),
col=ifelse(diff<0,"Female>Male","Male>Female")) %>%
filter(sex=="M") %>%
mutate(diff=-(diff),
pct_diff=round(diff/weight2,4)) %>%
ungroup()%>%
mutate(al= ifelse(pct_diff==max(pct_diff)|pct_diff==min(pct_diff), 1,0.7))
ggplot(female,aes(y=reorder(common_name, pct_diff), x=pct_diff, fill=col)) +
geom_col(aes(alpha=al),show.legend=F) +
geom_text(data=female %>% filter(col=="Female>Male"), aes(x=-0.003, y=reorder(common_name, pct_diff), label=common_name,alpha=al), hjust=1, size=2.6, color="#023e8a") +
geom_text(data=female %>% filter(col=="Male>Female"), aes(x=0.003, y=reorder(common_name, pct_diff), label=common_name, alpha=al), hjust=0, size=2.6, color="#e85d04") +
geom_vline(xintercept = 0, size=0.7) +
scale_fill_manual(values=c("#023e8a","#e85d04")) +
scale_x_continuous(limits=c(-.15,.15), breaks=seq(-.15,.15,.05),
labels=scales::percent_format(accuracy = 1)) +
scale_alpha_identity() +
theme(panel.grid.minor=element_blank(),
panel.grid.major.y=element_blank(),
panel.grid=element_line(size=.3),
axis.text.y=element_blank(),
axis.text.x=element_text(size=8),
axis.title=element_blank(),
plot.margin = margin(15, 25, 15, 25),
plot.title=element_text(size=10, face="bold"),
plot.subtitle = element_text(size=8.7, margin=margin(b=10))
) +
labs(title="Weight of female lemurs compared to male",
subtitle="Average non-pregnant female (adult and young adult) lemurs' weight expressed as a percentage difference to male")

Age category, hybrid, birth type
lemurs2 = lemurs %>%
mutate(age_category = case_when(age_category=="young_adult" ~ "Young-Adult",
age_category=="IJ" ~ "Infant/Juvenile",
age_category=="adult" ~ "Adult"),
hybrid= case_when(hybrid=="N"~"Not hybrid",
hybrid=="Sp"~"Hybrid"),
birth_type= case_when(birth_type=="CB"~'Captive-born',
birth_type=="WB"~'Wild-born',
birth_type=="Unk"~'Unknown'),
gender= case_when(sex=="F"~ "Female",
sex=="M" ~ "Male")
)
# age category
lemurs2 %>%
group_by(age_category) %>%
summarise(n=n_distinct(dlc_id)) %>%
arrange(desc(n))
# hybrid
lemurs2 %>%
group_by(hybrid) %>%
summarise(n=n_distinct(dlc_id)) %>%
arrange(desc(n))
# birth type
lemurs2 %>%
group_by(birth_type) %>%
summarise(n=n_distinct(dlc_id)) %>%
arrange(desc(n))
# birth type, age category of animals that are dead
lemurs2 %>%
filter(!is.na(dod)) %>%
group_by(birth_type, age_category) %>%
summarise(n=n_distinct(dlc_id)) %>%
mutate(percentage=scales::percent(n / sum(n), accuracy = .1, trim = FALSE))
Average weight of male captive-born lemurs
# average weight of male captive born lemurs, by species and age category
lemurs %>% left_join(taxon_df, by="taxon") %>%
filter(birth_type=="CB") %>%
filter(sex=="M") %>%
group_by(dlc_id, common_name, age_category) %>%
summarise(weight=mean(weight_g)) %>%
ungroup() %>%
mutate(age_category = case_when(age_category=="young_adult" ~ "Young-Adult",
age_category=="IJ" ~ "Infant/Juvenile",
age_category=="adult" ~ "Adult")) %>%
group_by(common_name, age_category) %>%
summarise("average weight"= round(mean(weight),2)) %>%
pivot_wider(names_from=age_category, values_from="average weight") %>%
rename("Species"=common_name) %>%
ungroup() %>%
select("Species","Adult", "Young-Adult", "Infant/Juvenile") %>%
DT::datatable(rownames=FALSE,options = list(order = list(list(1, 'desc'))),
caption = htmltools::tags$caption( style = 'caption-side: top; text-align: center; color:black; font-size:130% ;','Average weight of male captive-born lemurs (in grams)'))
Age category count by year
age_year = lemurs2 %>%
mutate(age_category=factor(age_category,
levels=c("Infant/Juvenile","Young-Adult","Adult"), ordered=T)) %>%
group_by(dlc_id) %>%
mutate(weight_date = min(ymd(weight_date))) %>%
mutate(year_joined=year(weight_date)) %>%
summarise(name, taxon, year_joined, age_category) %>%
unique() %>%
group_by(year_joined, age_category) %>%
mutate(n=length(year_joined)) %>%
unique()
ggplot(age_year, aes(x=year_joined, fill=age_category)) +
geom_bar(stat="count", alpha=0.9) +
scale_fill_manual(values=c("#6699cc","#ff8c42","#a23e48"), guide="none") +
scale_x_continuous(expand=c(0,0)) +
theme(plot.title.position = "plot",
panel.grid=element_line(size=.3),
plot.subtitle=element_markdown(size=9.5),
plot.title=element_text(face="bold", size=13),
axis.title = element_text(size=8.5, face="bold"),
plot.margin = margin(15, 25, 15, 25),
) +
labs(subtitle="Count of age categories by first record year <span style = 'color:#6699cc'><b>Infant/Juvenile</b></span>, <span style = 'color:#ff8c42'><b>Young-Adult</b></span> and <span style = 'color:#a23e48'><b>Adult</b></span>",
title="Lemur age categories by year",
x="Count", y="Year joined")

Life span by species
life = lemurs %>% left_join(tax, by="taxon") %>%
mutate(common_name = str_to_title(common_name) %>% str_remove(" Lemur"),
birth_type= case_when(birth_type=="CB"~'Captive-born',
birth_type=="WB"~'Wild-born',
birth_type=="Unk"~'Unknown')) %>%
group_by(dlc_id) %>%
slice(which.max(weight_date)) %>%
ungroup() %>%
filter(!is.na(age_at_death_y)) %>%
filter(birth_type!="Unknown")
life %>%
group_by(common_name) %>%
summarise(mean=mean(age_at_death_y)) %>%
left_join(., life, by="common_name") %>%
mutate(mean = round(mean, 1),
label = sprintf('%.1f', mean)) %>%
ggplot(aes(y=fct_rev(fct_reorder(common_name, desc(mean))), x=mean)) +
geom_point(aes(x= age_at_death_y, color=birth_type), shape=21) +
geom_point(size=6, color="#5c4d7d") +
geom_text(aes(label=mean),size=2, color="white") +
scale_color_manual(values=c("#dda15e","#0077b6"), guide="none") +
#coord_cartesian(expand=F, clip="off") +
theme(panel.grid=element_line(size=.3),
panel.grid.major.y=element_blank(),
panel.grid.minor=element_blank(),
plot.title.position = "plot",
plot.margin = margin(15, 25, 15, 25),
axis.title=element_text(size=8.5),
axis.text.y=element_text(size=7.5,margin=margin(r=-10)),
plot.title=element_text(size=10, face="bold"),
plot.subtitle=element_markdown(size=8.5)
) +
labs(x="Lifespan (in years)", y="Lemur species",
title="Lemur lifespan by taxonomy",
subtitle="<span style = 'color:#5c4d7d'><b>Average</b></span> lifespan of <span style = 'color:#dda15e'><b>Captive-born</b></span> and <span style = 'color:#0077b6'><b>Wild-born</b></span> lemurs by taxonomy")

Number of offspring by year
offspring = lemurs %>%
drop_na(dob,n_known_offspring) %>%
mutate(year=year(dob)) %>%
filter(between(year, 1958,2016)) %>%
group_by(dlc_id) %>%
slice(which.max(weight_date)) %>%
ungroup() %>%
group_by(year) %>% arrange(year) %>%
mutate(sh = ifelse(n_known_offspring==max(n_known_offspring)|n_known_offspring==min(n_known_offspring)
,19,1),
al = ifelse(n_known_offspring==max(n_known_offspring)|n_known_offspring==min(n_known_offspring)
,1,0.5)) %>%
mutate(col= case_when(n_known_offspring==min(n_known_offspring)~"#4DBBD5FF",
n_known_offspring==max(n_known_offspring)~"#00A087FF",
TRUE~"grey50"))
summary = offspring %>% group_by(year) %>% summarise(med=median(n_known_offspring))
spline_df <- as.data.frame(spline(summary$year, summary$med))
offspring %>%
ggplot(aes(x=year, y= n_known_offspring)) +
geom_rect(aes(xmin=1960, xmax=1970, ymin=0, ymax=Inf), fill="#f8f9fa", alpha=0.1, inherit.aes = F) +
geom_rect(aes(xmin=1980, xmax=1990, ymin=0, ymax=Inf), fill="#f8f9fa", alpha=0.1, inherit.aes = F) +
geom_rect(aes(xmin=2000, xmax=2010, ymin=0, ymax=Inf), fill="#f8f9fa", alpha=0.1, inherit.aes = F) +
geom_point(aes(shape=sh, alpha=al, color=col)) +
#geom_segment(data=summary, aes(x=year-0.5,xend=year+0.5, y=med, yend=med),size=1.5, color="red")
#geom_step(data=summary, aes(x=year-0.5, y=med), color="red")
geom_point(data=summary, aes(x=year, y=med), color="#E64B35FF") +
geom_line(data=spline_df, aes(x=x,y=y), color="#E64B35FF") +
scale_shape_identity() +
scale_alpha_identity() +
scale_color_identity() +
scale_x_continuous(breaks=seq(1960,2020,10), expand = c(.01, .01)) +
theme(panel.grid.minor.x=element_blank(),
plot.title.position = "plot",
axis.title = element_text(size=8.5, face="bold"),
plot.title=element_text(size=12, face="bold"),
plot.subtitle=element_markdown(size=8.5, margin=margin(b=10)),
plot.margin = margin(15, 25, 15, 25),
axis.text.x=element_text(margin=margin(t=-7))
) +
labs(x="Year",y="Number of offspring",
title="Lemur offspring by year",
subtitle="<span style = 'color:#E64B35FF'><b>Median</b></span>, <span style = 'color:#4DBBD5FF'><b>minimum</b></span> and <span style = 'color:#00A087FF'><b>maximum</b></span> count of offspring of individual is known to have produced (latest record date by specimen ID)")

LS0tCnRpdGxlOiAiVGlkeSBUdWVzZGF5IFdlZWsgMzUvMjAyMSIKZGF0ZTogIjIwMjEvMDgvMjQiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCltUaWR5IFR1ZXNkYXldKGh0dHBzOi8vZ2l0aHViLmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkpIHdlZWsgMzUKW0xlbXVyc10oaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9ibG9iL21hc3Rlci9kYXRhLzIwMjEvMjAyMS0wOC0yNC9yZWFkbWUubWQpLCBkYXRhIGZyb20gW0R1a2UgTGVtdXIgQ2VudGVyXShodHRwczovL2xlbXVyLmR1a2UuZWR1LykgYW5kIHdhcyBjbGVhbmVkIGJ5IFtKZXNzZSBNb3N0aXBha10oaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9pc3N1ZXMvMjA0KS4KCmBgYHtyfQojIExvYWQgbGlicmFyaWVzCmxpYnJhcnkodGlkeXR1ZXNkYXlSKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkoZ2d0ZXh0KQpsaWJyYXJ5KGdnaGFsdmVzKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KHJjYXJ0b2NvbG9yKQpsaWJyYXJ5KGx1YnJpZGF0ZSkKCm9wdGlvbnMoZHBseXIuc3VtbWFyaXNlLmluZm9ybSA9IEZBTFNFKQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMCkpCmBgYAoKYGBge3J9CiMgaW1wb3J0IGRhdGEKbGVtdXJzIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDgtMjQvbGVtdXJfZGF0YS5jc3YnKQp0YXggPC0gcmVhZHI6OnJlYWRfY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L21hc3Rlci9kYXRhLzIwMjEvMjAyMS0wOC0yNC90YXhvbm9teS5jc3YnKQpgYGAKYGBge3J9CmhlYWQobGVtdXJzKQpgYGAKYGBge3J9CiMgcHJvcG9ydGlvbiBtaXNzaW5nIGRhdGEgYnkgdmFyaWFibGUKbGVtdXJzICU+JSBzdW1tYXJpc2UoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgfm1lYW4oaXMubmEoLikpKSkgJT4lIAogIGdhdGhlcigpICU+JQogIG11dGF0ZSh2YWx1ZT1yb3VuZCh2YWx1ZSwzKSkgJT4lCiAgYXJyYW5nZShkZXNjKHZhbHVlKSkKCiMgdW5pcXVlIHZhbHVlcyBieSB2YXJpYWJsZQpsZW11cnMgJT4lIHN1bW1hcmlzZV9hbGwobl9kaXN0aW5jdCkKYGBgCgpgYGB7cn0KIyBzdW1tYXJ5IG9mIHJlY29yZCBjb3VudCBwZXIgYW5pbWFsCmxlbXVycyAlPiUgCiAgY291bnQoZGxjX2lkKSAlPiUgCiAgc3VtbWFyeSgpCmBgYAoKIyMjIFdlaWdodCBieSBhZ2UgY2F0ZWdvcnkgYW5kIGdlbmRlciBncm91cCAgICAKKiBzaGFyZWQgb24gW1R3aXR0ZXJdKGh0dHBzOi8vdHdpdHRlci5jb20vbGVlb2xuZXkzL3N0YXR1cy8xNDMwMDgzODI3NTQwNTc4MzA4KQoKYGBge3J9CiMgTGF0ZXN0IHJlY29yZGVkIHdlaWdodCBieSBzcGVjaW1lbiBJRAp3ZWlnaHQgPSBsZW11cnMgJT4lIAogIG11dGF0ZShhZ2VfY2F0ID0gY2FzZV93aGVuKGFnZV9jYXRlZ29yeT09InlvdW5nX2FkdWx0IiB+ICJZb3VuZy1BZHVsdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlX2NhdGVnb3J5PT0iSUoiIH4gIkluZmFudC9KdXZlbmlsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlX2NhdGVnb3J5PT0iYWR1bHQiIH4gIkFkdWx0IiksCiAgICAgICAgIGdlbmRlcj0gY2FzZV93aGVuKHNleD09IkYifiAiRmVtYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iTSIgfiAiTWFsZSIpCiAgICAgICAgICkgJT4lCiAgbXV0YXRlKGdyb3VwPXBhc3RlKGFnZV9jYXQsZ2VuZGVyKSkgJT4lCiAgbXV0YXRlKGdyb3VwPWZhY3Rvcihncm91cCxsZXZlbHM9YygiQWR1bHQgRmVtYWxlIiwiQWR1bHQgTWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAiWW91bmctQWR1bHQgRmVtYWxlIiwiWW91bmctQWR1bHQgTWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAiSW5mYW50L0p1dmVuaWxlIEZlbWFsZSIsIkluZmFudC9KdXZlbmlsZSBNYWxlIiwiSW5mYW50L0p1dmVuaWxlIE5BIiksCiAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkPVQpKSAgJT4lIAogIGdyb3VwX2J5KGRsY19pZCkgJT4lCiAgc2xpY2Uod2hpY2gubWF4KHdlaWdodF9kYXRlKSkgCgojIG1lZGlhbgp3ZWlnaHQgJT4lCiAgZ3JvdXBfYnkoZ3JvdXApICU+JQogIHN1bW1hcmlzZShtZWQ9bWVkaWFuKHdlaWdodF9nKSkgJT4lCiAgYXJyYW5nZShkZXNjKG1lZCkpCgojIGJveHBsb3Qgd2l0aCBiYXJjb2RlIHN0cmlwcyAKd2VpZ2h0ICU+JQoJZ2dwbG90KGFlcyh5PXdlaWdodF9nLCB4PWdyb3VwLCBjb2xvcj1ncm91cCkpICsgCiAgIGdlb21fYm94cGxvdCgKICAgIHdpZHRoID0gLjIsIAogICAgb3V0bGllci5zaGFwZSA9IE5BLAogICAgZmlsbD0iI2Y0ZjRmMiIsCiAgICBwb3NpdGlvbj1wb3NpdGlvbl9udWRnZSh4PTAuMix5PTApCiAgKSArIAogIGdlb21fcG9pbnQoCiAgICBzaGFwZSA9IDk1LAogICAgc2l6ZSA9IDYsCiAgICBhbHBoYSA9IC4yLAogICAgcG9zaXRpb249cG9zaXRpb25fbnVkZ2UoeD0tMC4xLHk9MCkKICApICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDE1KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiIzAwMzA0OSIsIiMwMDMwNDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiI2Y3N2YwMCIsIiMwMDMwNDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIzAwMzA0OSIsIiMwMDMwNDkiLCIjMDAzMDQ5IikpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwKICAgICAgICBwYW5lbC5ncmlkPWVsZW1lbnRfbGluZShzaXplPS4yLCBjb2xvcj0iI2NlZDRkYSIpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3I9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfbWFya2Rvd24oc2l6ZT04LjIsIG1hcmdpbj1tYXJnaW4odD04KSksCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfbWFya2Rvd24oc2l6ZT04LjIsIG1hcmdpbj1tYXJnaW4ocj04KSksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KHNpemU9Ny41LCBtYXJnaW49bWFyZ2luKHQ9LTUpKSwKICAgICAgICBwbG90LmNhcHRpb249ZWxlbWVudF90ZXh0KHNpemU9NywgY29sb3I9IiM0OTUwNTciKSwKICAgICAgICBwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLAogICAgICAgIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBzaXplPTEzKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgcGxvdC5tYXJnaW49dW5pdChjKC41LC41LC41LC41KSwiY20iKSwKICAgICAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0iI2Y0ZjRmMiIsIGNvbG9yPU5BKQogICAgICAgICkgKyAKICBsYWJzKHg9IioqQWdlIGFuZCBnZW5kZXIgZ3JvdXAqKiIsIHk9IioqTGF0ZXN0IHdlaWdodCByZWNvcmRlZCoqIChpbiBncmFtcykiLAogICAgICAgY2FwdGlvbj0iI1RpZHlUdWVzZGF5IFdlZWsgMzUgfCBEYXRhIGZyb20gRHVrZSBMZW11ciBDZW50ZXIiLAogICAgICAgdGl0bGU9IldlaWdodCBvZiBsZW11cnMiLAogICAgICAgc3VidGl0bGU9ImJ5IGFnZSBjYXRlZ29yeSBhbmQgZ2VuZGVyIChsYXRlc3QgZGF0ZSBieSBzcGVjaW1lbiBJRCksIHdoZXJlIFlvdW5nIEFkdWx0IEZlbWFsZXMgaGF2ZSB0aGUgaGlnaGVzdCBtZWRpYW4gd2VpZ2h0IGF0IDIxNTUgZ3JhbXMuIikKCmBgYAoKIyMjIEFkdWx0IG1hbGUgd2VpZ2h0IGJ5IG1vbnRoIG9mIHdlaWdoLWluCmBgYHtyfQojIGFkdWx0IG1hbGU6IHN1bW1hcnkgb2YgcmVjb3JkIGNvdW50CmxlbXVycyAlPiUgCiAgZmlsdGVyKHNleD09Ik0iKSAlPiUKICBmaWx0ZXIoYWdlX2NhdGVnb3J5PT0iYWR1bHQiKSAlPiUKICBjb3VudChkbGNfaWQpICU+JSBzdW1tYXJ5KG4pCgojIGFkdWx0IG1hbGUgd2VpZ2h0IGJ5IG1vbnRoIG9mIHdlaWdoLWluCnBhbCA9IGNvbG9yUmFtcFBhbGV0dGUocmNhcnRvY29sb3I6OmNhcnRvX3BhbChuPTEyLG5hbWU9IlByaXNtIilbMToxMF0pCiAKbGVtdXJzICU+JSAKICBmaWx0ZXIoc2V4PT0iTSIpICU+JQogIGZpbHRlcihhZ2VfY2F0ZWdvcnk9PSJhZHVsdCIpICU+JQogIGdyb3VwX2J5KGRsY19pZCkgJT4lCiAgbXV0YXRlKG49bigpKSAlPiUKICBmaWx0ZXIobj40MCkgJT4lIAogIGdncGxvdChhZXMoeD1mYWN0b3IoYmlydGhfbW9udGgpLCB5PXdlaWdodF9nLCBjb2xvcj1mYWN0b3IoYmlydGhfbW9udGgpKSkgKyAKICBnZW9tX2hhbGZfYm94cGxvdChvdXRsaWVyLnNpemUgPSAtMSkgKyAKICBnZW9tX2hhbGZfcG9pbnQoc2hhcGU9MjEsIGFscGhhPTAuNCxzaXplPTEpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXBhbChuX2Rpc3RpbmN0KGxlbXVycyRtb250aF9vZl93ZWlnaHQpKSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDUwMDAsMTAwMCkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLAogICAgICAgIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKHNpemU9LjMpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3I9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwKICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF9tYXJrZG93bihzaXplPTguNSksCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfbWFya2Rvd24oc2l6ZT04LjUpLAogICAgICAgIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBzaXplPTEzKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgcGxvdC5tYXJnaW49dW5pdChjKC41LC41LC41LC41KSwiY20iKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQobWFyZ2luPW1hcmdpbih0PS03LCBiPTUpKSkgKyAKICBsYWJzKHk9IioqV2VpZ2h0KiogKGluIGdyYW1zKSIsIHg9IioqTW9udGgqKiAob2YgdGhlIHllYXIgdGhlIGFuaW1hbCB3YXMgd2VpZ2hlZCkiLAogICAgICAgdGl0bGU9IldlaWdodCBvZiBhZHVsdCBtYWxlIGxlbXVycyIsCiAgICAgICBzdWJ0aXRsZT0iYnkgbW9udGggb2YgdGhlIHllYXIgdGhlIGFuaW1hbCB3YXMgd2VpZ2hlZCwgZm9yIGFkdWx0IG1hbGUgbGVtdXJzIHRoYXQgaGF2ZSBtb3JlIHRoYW4gNDAgdG90YWwgcmVjb3JkcyIpCmBgYAoKIyMjIExlbXVyIGNvdW50IGJ5IHNwZWNpZXMKYGBge3J9CnRheCA9IHRheCAlPiUgCiAgbXV0YXRlKGNvbW1vbl9uYW1lPSBpZmVsc2UoY29tbW9uX25hbWU9PSJoeWJyaWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKGNvbW1vbl9uYW1lLCIgIiwiKCIsbGF0aW5fbmFtZSwiKSIpLGNvbW1vbl9uYW1lKSwKICAgICAgICAgdGF4b249IGlmZWxzZSh0YXhvbj09IkNNRUFEIiwiQ01FRCIsIHRheG9uKSkKYGBgCgpgYGB7cn0KIyBqb2luCmNvbW1vbl9uYW1lID0gbGVtdXJzICU+JSBsZWZ0X2pvaW4odGF4LCBieT0idGF4b24iKSAlPiUKICBncm91cF9ieShkbGNfaWQpICU+JQogIHNsaWNlKHdoaWNoLm1heCh3ZWlnaHRfZGF0ZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoY29tbW9uX25hbWVfZ3JvdXAgPSBmY3RfbHVtcChjb21tb25fbmFtZSwyMCkpICU+JQogIGNvdW50KGNvbW1vbl9uYW1lX2dyb3VwLHNvcnQ9VCkgJT4lCiAgbXV0YXRlKGNvbW1vbl9uYW1lX2dyb3VwID0gZm9yY2F0czo6ZmN0X3Jldihmb3JjYXRzOjpmY3RfaW5vcmRlcihjb21tb25fbmFtZV9ncm91cCkpLAogICAgY29tbW9uX25hbWVfZ3JvdXAgPSBmb3JjYXRzOjpmY3RfcmVsZXZlbChjb21tb25fbmFtZV9ncm91cCwgIk90aGVyIiwgYWZ0ZXIgPSAwKSwKICAgIHBlcmMgPSBwYXN0ZTAoc3ByaW50ZigiJTQuMWYiLCBuIC8gc3VtKG4pICogMTAwKSwgIiUiKSwKICAgIHBlcmMgPSBpZl9lbHNlKHJvd19udW1iZXIoKSA9PSAxLCBwYXN0ZShwZXJjLCAib2YgYWxsIGxlbXVycyBzcGVjaWVzIiksIHBlcmMpLAogICAgcGxhY2UgPSBpZl9lbHNlKHJvd19udW1iZXIoKSA9PSAxLCAxLCAwLjIpLAogICAgcGVyYyA9IHBhc3RlKCIgIiwgcGVyYywgIiAiKQogICAgICAgICApCgpwYWwgPC0gYygKICAiI2NkZDJkNyIsCiAgcmVwKCIjYWRiNWJkIiwgbGVuZ3RoKGNvbW1vbl9uYW1lJGNvbW1vbl9uYW1lX2dyb3VwKSAtIDQpLCAKICAiI2I3MDk0YyIsICIjMDA5MWFkIiwgIiNmOWM3NGYiCikKCmNvbW1vbl9uYW1lICU+JQogIGdncGxvdChhZXMoeD1uLCB5PWNvbW1vbl9uYW1lX2dyb3VwLCBmaWxsPWNvbW1vbl9uYW1lX2dyb3VwKSkgKwogIGdlb21fY29sKCkgKyAKICBnZW9tX3RleHQoYWVzKGxhYmVsPXBlcmMsIGhqdXN0ID0gcGxhY2UpLCBzaXplPTIuOCwgZm9udGZhY2UgPSAiYm9sZCIpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gcGFsLCBndWlkZSA9ICJub25lIikgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gYyguMDEsIC4wMSkpICsKICB0aGVtZV92b2lkKGJhc2Vfc2l6ZSA9IDEwKSArIAogIHRoZW1lKGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChoanVzdD0xLCBzaXplPTgpLAogICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDE1LCAzMCwgMTUsIDE1KSwKICAgICAgICBwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLAogICAgICAgIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBzaXplPTEzLG1hcmdpbj1tYXJnaW4oYj0xMCkpKSArCiAgbGFicyh0aXRsZSA9IkxlbXVyIGNvdW50IGJ5IHNwZWNpZXMiKQpgYGAKCgojIyMgV2VpZ2h0IG9mIGZlbWFsZSBsZW11cnMgY29tcGFyZWQgdG8gbWFsCmBgYHtyfQpmZW1hbGUgPSBsZW11cnMgJT4lIGxlZnRfam9pbih0YXgsIGJ5PSJ0YXhvbiIpICU+JQogIGZpbHRlcihwcmVnX3N0YXR1cyE9IlAiKSAlPiUKICBmaWx0ZXIoYWdlX2NhdGVnb3J5IT0iSUoiKSAlPiUKICBkcm9wX25hKGNvbW1vbl9uYW1lLCBzZXgsIGRsY19pZCwgd2VpZ2h0X2cpICU+JQogIGdyb3VwX2J5KGNvbW1vbl9uYW1lLCBzZXgsIGRsY19pZCkgJT4lCiAgc3VtbWFyaXNlKHdlaWdodD1tZWFuKHdlaWdodF9nKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGdyb3VwX2J5KGNvbW1vbl9uYW1lLCBzZXgpICU+JQogIHN1bW1hcmlzZSh3ZWlnaHQyPW1lYW4od2VpZ2h0KSkgJT4lCiAgbXV0YXRlKGRpZmY9d2VpZ2h0Mi1sYWcod2VpZ2h0MiksCiAgICAgICAgIGNvbD1pZmVsc2UoZGlmZjwwLCJGZW1hbGU+TWFsZSIsIk1hbGU+RmVtYWxlIikpICU+JQogIGZpbHRlcihzZXg9PSJNIikgJT4lCiAgbXV0YXRlKGRpZmY9LShkaWZmKSwKICAgICAgICAgcGN0X2RpZmY9cm91bmQoZGlmZi93ZWlnaHQyLDQpKSAlPiUKICB1bmdyb3VwKCklPiUKICBtdXRhdGUoYWw9IGlmZWxzZShwY3RfZGlmZj09bWF4KHBjdF9kaWZmKXxwY3RfZGlmZj09bWluKHBjdF9kaWZmKSwgMSwwLjcpKQoKCmdncGxvdChmZW1hbGUsYWVzKHk9cmVvcmRlcihjb21tb25fbmFtZSwgcGN0X2RpZmYpLCB4PXBjdF9kaWZmLCBmaWxsPWNvbCkpICsgCiAgZ2VvbV9jb2woYWVzKGFscGhhPWFsKSxzaG93LmxlZ2VuZD1GKSArCiAgZ2VvbV90ZXh0KGRhdGE9ZmVtYWxlICU+JSBmaWx0ZXIoY29sPT0iRmVtYWxlPk1hbGUiKSwgYWVzKHg9LTAuMDAzLCB5PXJlb3JkZXIoY29tbW9uX25hbWUsIHBjdF9kaWZmKSwgbGFiZWw9Y29tbW9uX25hbWUsYWxwaGE9YWwpLCBoanVzdD0xLCBzaXplPTIuNiwgY29sb3I9IiMwMjNlOGEiKSArCiAgZ2VvbV90ZXh0KGRhdGE9ZmVtYWxlICU+JSBmaWx0ZXIoY29sPT0iTWFsZT5GZW1hbGUiKSwgYWVzKHg9MC4wMDMsIHk9cmVvcmRlcihjb21tb25fbmFtZSwgcGN0X2RpZmYpLCBsYWJlbD1jb21tb25fbmFtZSwgYWxwaGE9YWwpLCBoanVzdD0wLCBzaXplPTIuNiwgY29sb3I9IiNlODVkMDQiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgc2l6ZT0wLjcpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiIzAyM2U4YSIsIiNlODVkMDQiKSkgKwogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHM9YygtLjE1LC4xNSksIGJyZWFrcz1zZXEoLS4xNSwuMTUsLjA1KSwgCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1zY2FsZXM6OnBlcmNlbnRfZm9ybWF0KGFjY3VyYWN5ID0gMSkpICsKICBzY2FsZV9hbHBoYV9pZGVudGl0eSgpICsKICB0aGVtZShwYW5lbC5ncmlkLm1pbm9yPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKHNpemU9LjMpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigxNSwgMjUsIDE1LCAyNSksCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCwgZmFjZT0iYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04LjcsIG1hcmdpbj1tYXJnaW4oYj0xMCkpCiAgICAgICAgKSArIAogIGxhYnModGl0bGU9IldlaWdodCBvZiBmZW1hbGUgbGVtdXJzIGNvbXBhcmVkIHRvIG1hbGUiLAogICAgICAgc3VidGl0bGU9IkF2ZXJhZ2Ugbm9uLXByZWduYW50IGZlbWFsZSAoYWR1bHQgYW5kIHlvdW5nIGFkdWx0KSBsZW11cnMnIHdlaWdodCBleHByZXNzZWQgYXMgYSBwZXJjZW50YWdlIGRpZmZlcmVuY2UgdG8gbWFsZSIpCmBgYAoKIyMjIEFnZSBjYXRlZ29yeSwgaHlicmlkLCBiaXJ0aCB0eXBlCgpgYGB7cn0KbGVtdXJzMiA9IGxlbXVycyAlPiUgCiAgbXV0YXRlKGFnZV9jYXRlZ29yeSA9IGNhc2Vfd2hlbihhZ2VfY2F0ZWdvcnk9PSJ5b3VuZ19hZHVsdCIgfiAiWW91bmctQWR1bHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFnZV9jYXRlZ29yeT09IklKIiB+ICJJbmZhbnQvSnV2ZW5pbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFnZV9jYXRlZ29yeT09ImFkdWx0IiB+ICJBZHVsdCIpLAogICAgICAgICBoeWJyaWQ9IGNhc2Vfd2hlbihoeWJyaWQ9PSJOIn4iTm90IGh5YnJpZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGh5YnJpZD09IlNwIn4iSHlicmlkIiksCiAgICAgICAgIGJpcnRoX3R5cGU9IGNhc2Vfd2hlbihiaXJ0aF90eXBlPT0iQ0IifidDYXB0aXZlLWJvcm4nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmlydGhfdHlwZT09IldCIn4nV2lsZC1ib3JuJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJpcnRoX3R5cGU9PSJVbmsifidVbmtub3duJyksCiAgICAgICAgIGdlbmRlcj0gY2FzZV93aGVuKHNleD09IkYifiAiRmVtYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2V4PT0iTSIgfiAiTWFsZSIpCiAgICAgICAgICkKCiMgYWdlIGNhdGVnb3J5CmxlbXVyczIgJT4lIAogIGdyb3VwX2J5KGFnZV9jYXRlZ29yeSkgJT4lCiAgc3VtbWFyaXNlKG49bl9kaXN0aW5jdChkbGNfaWQpKSAlPiUKICBhcnJhbmdlKGRlc2MobikpIAoKIyBoeWJyaWQgCmxlbXVyczIgJT4lCiAgZ3JvdXBfYnkoaHlicmlkKSAlPiUKICBzdW1tYXJpc2Uobj1uX2Rpc3RpbmN0KGRsY19pZCkpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkKCiMgYmlydGggdHlwZQpsZW11cnMyICU+JSAKICBncm91cF9ieShiaXJ0aF90eXBlKSAlPiUKICBzdW1tYXJpc2Uobj1uX2Rpc3RpbmN0KGRsY19pZCkpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkKCiMgYmlydGggdHlwZSwgYWdlIGNhdGVnb3J5IG9mIGFuaW1hbHMgdGhhdCBhcmUgZGVhZCAKbGVtdXJzMiAlPiUKICBmaWx0ZXIoIWlzLm5hKGRvZCkpICU+JQogIGdyb3VwX2J5KGJpcnRoX3R5cGUsIGFnZV9jYXRlZ29yeSkgJT4lCiAgc3VtbWFyaXNlKG49bl9kaXN0aW5jdChkbGNfaWQpKSAlPiUKICBtdXRhdGUocGVyY2VudGFnZT1zY2FsZXM6OnBlcmNlbnQobiAvIHN1bShuKSwgYWNjdXJhY3kgPSAuMSwgdHJpbSA9IEZBTFNFKSkKYGBgCgojIyMgQXZlcmFnZSB3ZWlnaHQgb2YgbWFsZSBjYXB0aXZlLWJvcm4gbGVtdXJzCmBgYHtyfQojIGF2ZXJhZ2Ugd2VpZ2h0IG9mIG1hbGUgY2FwdGl2ZSBib3JuIGxlbXVycywgYnkgc3BlY2llcyBhbmQgYWdlIGNhdGVnb3J5CmxlbXVycyAlPiUgbGVmdF9qb2luKHRheG9uX2RmLCBieT0idGF4b24iKSAlPiUKICBmaWx0ZXIoYmlydGhfdHlwZT09IkNCIikgJT4lCiAgZmlsdGVyKHNleD09Ik0iKSAlPiUKICBncm91cF9ieShkbGNfaWQsIGNvbW1vbl9uYW1lLCBhZ2VfY2F0ZWdvcnkpICU+JQogIHN1bW1hcmlzZSh3ZWlnaHQ9bWVhbih3ZWlnaHRfZykpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoYWdlX2NhdGVnb3J5ID0gY2FzZV93aGVuKGFnZV9jYXRlZ29yeT09InlvdW5nX2FkdWx0IiB+ICJZb3VuZy1BZHVsdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlX2NhdGVnb3J5PT0iSUoiIH4gIkluZmFudC9KdXZlbmlsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlX2NhdGVnb3J5PT0iYWR1bHQiIH4gIkFkdWx0IikpICU+JQogIGdyb3VwX2J5KGNvbW1vbl9uYW1lLCBhZ2VfY2F0ZWdvcnkpICU+JQogIHN1bW1hcmlzZSgiYXZlcmFnZSB3ZWlnaHQiPSByb3VuZChtZWFuKHdlaWdodCksMikpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb209YWdlX2NhdGVnb3J5LCB2YWx1ZXNfZnJvbT0iYXZlcmFnZSB3ZWlnaHQiKSAlPiUKICByZW5hbWUoIlNwZWNpZXMiPWNvbW1vbl9uYW1lKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgc2VsZWN0KCJTcGVjaWVzIiwiQWR1bHQiLCAiWW91bmctQWR1bHQiLCAiSW5mYW50L0p1dmVuaWxlIikgJT4lCiAgRFQ6OmRhdGF0YWJsZShyb3duYW1lcz1GQUxTRSxvcHRpb25zID0gbGlzdChvcmRlciA9IGxpc3QobGlzdCgxLCAnZGVzYycpKSksCiAgICAgICAgICAgICAgICBjYXB0aW9uID0gaHRtbHRvb2xzOjp0YWdzJGNhcHRpb24oIHN0eWxlID0gJ2NhcHRpb24tc2lkZTogdG9wOyB0ZXh0LWFsaWduOiBjZW50ZXI7IGNvbG9yOmJsYWNrOyBmb250LXNpemU6MTMwJSA7JywnQXZlcmFnZSB3ZWlnaHQgb2YgbWFsZSBjYXB0aXZlLWJvcm4gbGVtdXJzIChpbiBncmFtcyknKSkKYGBgCgojIyMgQWdlIGNhdGVnb3J5IGNvdW50IGJ5IHllYXIKKiByZWZlcmVuY2U6IGh0dHBzOi8vdHdpdHRlci5jb20vYWx5c3Nhc3R3ZWV0aW5nL3N0YXR1cy8xNDMwMzYzMjczNDAxMDQ0OTk5CmBgYHtyfQphZ2VfeWVhciA9IGxlbXVyczIgJT4lIAogIG11dGF0ZShhZ2VfY2F0ZWdvcnk9ZmFjdG9yKGFnZV9jYXRlZ29yeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygiSW5mYW50L0p1dmVuaWxlIiwiWW91bmctQWR1bHQiLCJBZHVsdCIpLCBvcmRlcmVkPVQpKSAlPiUKICBncm91cF9ieShkbGNfaWQpICU+JQogIG11dGF0ZSh3ZWlnaHRfZGF0ZSA9IG1pbih5bWQod2VpZ2h0X2RhdGUpKSkgJT4lCiAgbXV0YXRlKHllYXJfam9pbmVkPXllYXIod2VpZ2h0X2RhdGUpKSAlPiUKICBzdW1tYXJpc2UobmFtZSwgdGF4b24sIHllYXJfam9pbmVkLCBhZ2VfY2F0ZWdvcnkpICU+JQogIHVuaXF1ZSgpICU+JQogIGdyb3VwX2J5KHllYXJfam9pbmVkLCBhZ2VfY2F0ZWdvcnkpICU+JQogIG11dGF0ZShuPWxlbmd0aCh5ZWFyX2pvaW5lZCkpICU+JQogIHVuaXF1ZSgpIApgYGAKCmBgYHtyfQpnZ3Bsb3QoYWdlX3llYXIsIGFlcyh4PXllYXJfam9pbmVkLCBmaWxsPWFnZV9jYXRlZ29yeSkpICsgCiAgZ2VvbV9iYXIoc3RhdD0iY291bnQiLCBhbHBoYT0wLjkpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiM2Njk5Y2MiLCIjZmY4YzQyIiwiI2EyM2U0OCIpLCBndWlkZT0ibm9uZSIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kPWMoMCwwKSkgKwogIHRoZW1lKHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsCiAgICAgICAgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoc2l6ZT0uMyksCiAgICAgICAgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X21hcmtkb3duKHNpemU9OS41KSwKICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChmYWNlPSJib2xkIiwgc2l6ZT0xMyksCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTguNSwgZmFjZT0iYm9sZCIpLAogICAgICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDE1LCAyNSwgMTUsIDI1KSwKICAgICAgICApICsgCiAgbGFicyhzdWJ0aXRsZT0iQ291bnQgb2YgYWdlIGNhdGVnb3JpZXMgYnkgZmlyc3QgcmVjb3JkIHllYXIgPHNwYW4gc3R5bGUgPSAnY29sb3I6IzY2OTljYyc+PGI+SW5mYW50L0p1dmVuaWxlPC9iPjwvc3Bhbj4sIDxzcGFuIHN0eWxlID0gJ2NvbG9yOiNmZjhjNDInPjxiPllvdW5nLUFkdWx0PC9iPjwvc3Bhbj4gYW5kIDxzcGFuIHN0eWxlID0gJ2NvbG9yOiNhMjNlNDgnPjxiPkFkdWx0PC9iPjwvc3Bhbj4iLAogICAgICAgdGl0bGU9IkxlbXVyIGFnZSBjYXRlZ29yaWVzIGJ5IHllYXIiLAogICAgICAgeD0iQ291bnQiLCB5PSJZZWFyIGpvaW5lZCIpCmBgYAoKIyMjIExpZmUgc3BhbiBieSBzcGVjaWVzIAoqIHJlZmVyZW5jZTogaHR0cHM6Ly9naXRodWIuY29tL0JsYWtlUk1pbGxzL1RpZHlUdWVzZGF5L2Jsb2IvbWFpbi9MZW11cnMlMjAoMjQlMjBBdWclMjAyMDIxKS9UaWR5VHVlc2RheSUyMDI0JTIwQXVnJTIwMjAyMSUyMExlbXVycy5SCgpgYGB7ciwgZmlnLndpZHRoPTMuNSwgZmlnLmhlaWdodD0zLjN9CmxpZmUgPSBsZW11cnMgJT4lIGxlZnRfam9pbih0YXgsIGJ5PSJ0YXhvbiIpICU+JQogIG11dGF0ZShjb21tb25fbmFtZSA9IHN0cl90b190aXRsZShjb21tb25fbmFtZSkgJT4lIHN0cl9yZW1vdmUoIiBMZW11ciIpLAogICAgICAgICBiaXJ0aF90eXBlPSBjYXNlX3doZW4oYmlydGhfdHlwZT09IkNCIn4nQ2FwdGl2ZS1ib3JuJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJpcnRoX3R5cGU9PSJXQiJ+J1dpbGQtYm9ybicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiaXJ0aF90eXBlPT0iVW5rIn4nVW5rbm93bicpKSAlPiUKICBncm91cF9ieShkbGNfaWQpICU+JQogIHNsaWNlKHdoaWNoLm1heCh3ZWlnaHRfZGF0ZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBmaWx0ZXIoIWlzLm5hKGFnZV9hdF9kZWF0aF95KSkgJT4lCiAgZmlsdGVyKGJpcnRoX3R5cGUhPSJVbmtub3duIikKCmxpZmUgJT4lCiAgZ3JvdXBfYnkoY29tbW9uX25hbWUpICU+JQogIHN1bW1hcmlzZShtZWFuPW1lYW4oYWdlX2F0X2RlYXRoX3kpKSAlPiUKICBsZWZ0X2pvaW4oLiwgbGlmZSwgYnk9ImNvbW1vbl9uYW1lIikgJT4lCiAgbXV0YXRlKG1lYW4gPSByb3VuZChtZWFuLCAxKSwKICAgICAgICAgbGFiZWwgPSBzcHJpbnRmKCclLjFmJywgbWVhbikpICU+JQogIGdncGxvdChhZXMoeT1mY3RfcmV2KGZjdF9yZW9yZGVyKGNvbW1vbl9uYW1lLCBkZXNjKG1lYW4pKSksIHg9bWVhbikpICsgCiAgZ2VvbV9wb2ludChhZXMoeD0gYWdlX2F0X2RlYXRoX3ksIGNvbG9yPWJpcnRoX3R5cGUpLCBzaGFwZT0yMSkgKwogIGdlb21fcG9pbnQoc2l6ZT02LCBjb2xvcj0iIzVjNGQ3ZCIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPW1lYW4pLHNpemU9MiwgY29sb3I9IndoaXRlIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiI2RkYTE1ZSIsIiMwMDc3YjYiKSwgZ3VpZGU9Im5vbmUiKSArCiAgI2Nvb3JkX2NhcnRlc2lhbihleHBhbmQ9RiwgY2xpcD0ib2ZmIikgKwogIHRoZW1lKHBhbmVsLmdyaWQ9ZWxlbWVudF9saW5lKHNpemU9LjMpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vcj1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwKICAgICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigxNSwgMjUsIDE1LCAyNSksCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04LjUpLAogICAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfdGV4dChzaXplPTcuNSxtYXJnaW49bWFyZ2luKHI9LTEwKSksCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCwgZmFjZT0iYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGU9ZWxlbWVudF9tYXJrZG93bihzaXplPTguNSkKICAgICAgICApICsKICBsYWJzKHg9IkxpZmVzcGFuIChpbiB5ZWFycykiLCB5PSJMZW11ciBzcGVjaWVzIiwKICAgICAgIHRpdGxlPSJMZW11ciBsaWZlc3BhbiBieSB0YXhvbm9teSIsCiAgICAgICBzdWJ0aXRsZT0iPHNwYW4gc3R5bGUgPSAnY29sb3I6IzVjNGQ3ZCc+PGI+QXZlcmFnZTwvYj48L3NwYW4+IGxpZmVzcGFuIG9mIDxzcGFuIHN0eWxlID0gJ2NvbG9yOiNkZGExNWUnPjxiPkNhcHRpdmUtYm9ybjwvYj48L3NwYW4+IGFuZCA8c3BhbiBzdHlsZSA9ICdjb2xvcjojMDA3N2I2Jz48Yj5XaWxkLWJvcm48L2I+PC9zcGFuPiBsZW11cnMgYnkgdGF4b25vbXkiKQpgYGAKCiMjIyBOdW1iZXIgb2Ygb2Zmc3ByaW5nIGJ5IHllYXIgCiogcmVmZXJlbmNlOiBodHRwczovL3R3aXR0ZXIuY29tL3RhY2hlYm91dGl0L3N0YXR1cy8xNDMwMjU3NDEzMzA3OTE2MjkzL3Bob3RvLzEKCmBgYHtyLCB3YXJuaW5nPUZ9Cm9mZnNwcmluZyA9IGxlbXVycyAlPiUgCiAgZHJvcF9uYShkb2Isbl9rbm93bl9vZmZzcHJpbmcpICU+JQogIG11dGF0ZSh5ZWFyPXllYXIoZG9iKSkgJT4lCiAgZmlsdGVyKGJldHdlZW4oeWVhciwgMTk1OCwyMDE1KSkgJT4lCiAgZ3JvdXBfYnkoZGxjX2lkKSAlPiUKICBzbGljZSh3aGljaC5tYXgod2VpZ2h0X2RhdGUpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lIGFycmFuZ2UoeWVhcikgJT4lCiAgbXV0YXRlKHNoID0gaWZlbHNlKG5fa25vd25fb2Zmc3ByaW5nPT1tYXgobl9rbm93bl9vZmZzcHJpbmcpfG5fa25vd25fb2Zmc3ByaW5nPT1taW4obl9rbm93bl9vZmZzcHJpbmcpCiAgICAgICAgICAgICAgICAgICAgICAgLDE5LDEpLAogICAgICAgICBhbCA9IGlmZWxzZShuX2tub3duX29mZnNwcmluZz09bWF4KG5fa25vd25fb2Zmc3ByaW5nKXxuX2tub3duX29mZnNwcmluZz09bWluKG5fa25vd25fb2Zmc3ByaW5nKQogICAgICAgICAgICAgICAgICAgICAgICwxLDAuNSkpICU+JQogIG11dGF0ZShjb2w9IGNhc2Vfd2hlbihuX2tub3duX29mZnNwcmluZz09bWluKG5fa25vd25fb2Zmc3ByaW5nKX4iIzREQkJENUZGIiwKICAgICAgICAgICAgICAgICAgICAgICAgbl9rbm93bl9vZmZzcHJpbmc9PW1heChuX2tub3duX29mZnNwcmluZyl+IiMwMEEwODdGRiIsCiAgICAgICAgICAgICAgICAgICAgICAgIFRSVUV+ImdyZXk1MCIpKQoKc3VtbWFyeSA9IG9mZnNwcmluZyAlPiUgZ3JvdXBfYnkoeWVhcikgJT4lIHN1bW1hcmlzZShtZWQ9bWVkaWFuKG5fa25vd25fb2Zmc3ByaW5nKSkKc3BsaW5lX2RmIDwtIGFzLmRhdGEuZnJhbWUoc3BsaW5lKHN1bW1hcnkkeWVhciwgc3VtbWFyeSRtZWQpKQpgYGAKCmBgYHtyfQpvZmZzcHJpbmcgJT4lCiAgZ2dwbG90KGFlcyh4PXllYXIsIHk9IG5fa25vd25fb2Zmc3ByaW5nKSkgKwogIGdlb21fcmVjdChhZXMoeG1pbj0xOTYwLCB4bWF4PTE5NzAsIHltaW49MCwgeW1heD1JbmYpLCBmaWxsPSIjZjhmOWZhIiwgYWxwaGE9MC4xLCBpbmhlcml0LmFlcyA9IEYpICsKICBnZW9tX3JlY3QoYWVzKHhtaW49MTk4MCwgeG1heD0xOTkwLCB5bWluPTAsIHltYXg9SW5mKSwgZmlsbD0iI2Y4ZjlmYSIsIGFscGhhPTAuMSwgaW5oZXJpdC5hZXMgPSBGKSArCiAgZ2VvbV9yZWN0KGFlcyh4bWluPTIwMDAsIHhtYXg9MjAxMCwgeW1pbj0wLCB5bWF4PUluZiksIGZpbGw9IiNmOGY5ZmEiLCBhbHBoYT0wLjEsIGluaGVyaXQuYWVzID0gRikgKwogIGdlb21fcG9pbnQoYWVzKHNoYXBlPXNoLCBhbHBoYT1hbCwgY29sb3I9Y29sKSkgKwogICNnZW9tX3NlZ21lbnQoZGF0YT1zdW1tYXJ5LCBhZXMoeD15ZWFyLTAuNSx4ZW5kPXllYXIrMC41LCB5PW1lZCwgeWVuZD1tZWQpLHNpemU9MS41LCBjb2xvcj0icmVkIikKICAjZ2VvbV9zdGVwKGRhdGE9c3VtbWFyeSwgYWVzKHg9eWVhci0wLjUsIHk9bWVkKSwgY29sb3I9InJlZCIpCiAgZ2VvbV9wb2ludChkYXRhPXN1bW1hcnksIGFlcyh4PXllYXIsIHk9bWVkKSwgY29sb3I9IiNFNjRCMzVGRiIpICsKICBnZW9tX2xpbmUoZGF0YT1zcGxpbmVfZGYsIGFlcyh4PXgseT15KSwgY29sb3I9IiNFNjRCMzVGRiIpICsKICBzY2FsZV9zaGFwZV9pZGVudGl0eSgpICsKICBzY2FsZV9hbHBoYV9pZGVudGl0eSgpICsKICBzY2FsZV9jb2xvcl9pZGVudGl0eSgpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMTk2MCwyMDIwLDEwKSwgZXhwYW5kID0gYyguMDEsIC4wMSkpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vci54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT04LjUsIGZhY2U9ImJvbGQiKSwKICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyLCBmYWNlPSJib2xkIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X21hcmtkb3duKHNpemU9OC41LCBtYXJnaW49bWFyZ2luKGI9MTApKSwKICAgICAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigxNSwgMjUsIDE1LCAyNSksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KG1hcmdpbj1tYXJnaW4odD0tNykpCiAgICAgICAgKSArIAogIGxhYnMoeD0iWWVhciIseT0iTnVtYmVyIG9mIG9mZnNwcmluZyIsCiAgICAgICB0aXRsZT0iTGVtdXIgb2Zmc3ByaW5nIGJ5IHllYXIiLAogICAgICAgc3VidGl0bGU9IjxzcGFuIHN0eWxlID0gJ2NvbG9yOiNFNjRCMzVGRic+PGI+TWVkaWFuPC9iPjwvc3Bhbj4sIDxzcGFuIHN0eWxlID0gJ2NvbG9yOiM0REJCRDVGRic+PGI+bWluaW11bTwvYj48L3NwYW4+IGFuZCA8c3BhbiBzdHlsZSA9ICdjb2xvcjojMDBBMDg3RkYnPjxiPm1heGltdW08L2I+PC9zcGFuPiBjb3VudCBvZiBvZmZzcHJpbmcgb2YgaW5kaXZpZHVhbCBpcyBrbm93biB0byBoYXZlIHByb2R1Y2VkIChsYXRlc3QgcmVjb3JkIGRhdGUgYnkgc3BlY2ltZW4gSUQpIikKYGBgCgoKCgoKCgo=