Tidy Tuesday week 27 Animal Rescues, data from London.gov by way of Data is Plural and Georgios Karamanis.

# load libraries
library(tidyverse)
library(ggtext)
library(scales)
library(lubridate)
library(hms)
library(ggsankey)
library(gggrid) 
library(packcircles)
library(cowplot)
library(ggpubr)
library(ggbump)
library(gghalves)
library(colorspace)
library(wesanderson)
# import data
animal_rescues <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-06-29/animal_rescues.csv')

── Column specification ───────────────────────────────────────────────────────────────────────
cols(
  .default = col_character(),
  incident_number = col_double(),
  cal_year = col_double(),
  hourly_notional_cost = col_double(),
  easting_rounded = col_double(),
  northing_rounded = col_double()
)
ℹ Use `spec()` for the full column specifications.
summary(animal_rescues$cal_year)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   2009    2012    2015    2015    2018    2021 

Animal type and Service type

# prepare data
data =animal_rescues %>% 
  group_by(cal_year,special_service_type_category, special_service_type) %>% tally() %>% 
  mutate(special_service_type=tolower(special_service_type)) %>%
  mutate(special_service_type=str_replace_all(special_service_type,
                                            c("animal rescue"="",
                                              "-"="",
                                              "from below ground"="",
                                              "from height"="",
                                              "from water"="",
                                              "or mud"="",
                                              "animal assistance"="",
                                              "involving "="",
                                              "assist "="",
                                              "animal"=""))) %>%
  mutate(special_service_type= str_squish(special_service_type)) %>%
  mutate(SubCat= case_when(grepl("trapped", special_service_type) ~ "trapped",
                        grepl("harm", special_service_type) ~"harm",
                        grepl("lift heavy", special_service_type) ~"lift heavy",
                        grepl("other action", special_service_type) ~"other action",
                        grepl("from below ground", special_service_type_category) ~"from below ground",
                        grepl("from height", special_service_type_category) ~"from height",
                        grepl("from water", special_service_type_category) ~"from water"
                        )) %>%
  mutate(Cat= case_when(grepl("rescue", special_service_type_category) ~ "animal rescue",
                        TRUE ~ "other animal assistance")) %>%
  mutate(AnimalType=str_replace_all(special_service_type,c("lift heavy "="",
                                                   " other action"="",
                                                   "harm "="",
                                                   "trapped "=""))) %>%
  mutate(new_categories = ifelse(special_service_type_category=="Other animal assistance",paste0("Other animal assistance",":"," ",SubCat),special_service_type_category))
data
data %>% filter(cal_year<=2020) %>%
  group_by(new_categories, AnimalType) %>% summarise(total=sum(n)) %>%
  ggplot(aes(y=fct_rev(AnimalType), x=total, fill=total)) + 
  geom_col() + 
  facet_wrap(~new_categories, ncol=3, scales="free",labeller = labeller(new_categories = label_wrap_gen(28))) + 
  theme_minimal(base_size = 10) + 
  theme(panel.grid.minor=element_blank(),
        legend.position="none",
        panel.grid.major.y=element_blank(),
        axis.title=element_text(face="bold",size=8),
        plot.title.position = "plot",
        ) + 
  scale_fill_continuous_sequential(palette="Batlow") + 
  labs(y="Animal Type", x="Rescue Count",
       subtitle="LFB Animal Rescues Count by Animal and Service Type, from 2009 to 2020\n")

Special service type categories (2009 - 2020)

theme_set(theme_minimal(base_size=10))
theme_update(panel.grid.minor=element_blank(),
             plot.title.position="plot",
             plot.margin = unit(c(1, 1, 1, 0.25), "cm"),
             axis.title=element_text(size=8.5, face="bold"),
             legend.position="none")
animal_rescues %>% filter(cal_year<=2020) %>%
  group_by(cal_year, special_service_type_category) %>% tally() -> linedata

linedata %>%
  ggplot(aes(x=cal_year, y=n, color=reorder(special_service_type_category,-n))) +
  geom_bump(size=0.8) + 
  geom_point(size=1.5) + 
  geom_text(data= linedata %>% filter(cal_year==2020),
            aes(x=2020.2,label=special_service_type_category),hjust=0,size=2.5, fontface="bold") + 
  scale_x_continuous(limits=c(2009,2024), breaks=seq(2010,2020,5)) + 
  scale_y_continuous(limits=c(0,400)) +
  scale_color_manual(values = c("#0a2463","#dd1c1a","#3c6e71","#f26419","#247ba0")) +
  labs(x="Year", y="Count", subtitle="LFB special service type category from 2009 to 2020\n")

Special Service Type

# sankey bump chart 

# freq table
plotdata = data %>%filter(cal_year<=2020) %>% group_by(cal_year, new_categories) %>% summarise(count=sum(n)) 

# create labels with order
plotdata2 = plotdata %>% filter(cal_year==2020) %>% arrange(-count) %>%
  mutate(new_categories=fct_inorder(new_categories))

labs <- tibble(
    new_categories = factor(levels(plotdata2$new_categories), levels = levels(plotdata2$new_categories)),
    count =  c(275, 0, -225, -310, -380, -400, -420))

# colors
cols <- c("#28666e", "#c8553d", "#780000","#b5b682","#dda15e",
          "#7c9885","#033f63")

# plot
plot1 = plotdata %>%
  ggplot(aes(x=cal_year, value=count, node=new_categories, fill=factor(new_categories,levels=plotdata2$new_categories))) + 
  geom_sankey_bump(space=15, smooth=12) + 
  scale_fill_manual(values=cols) +
  scale_y_continuous(limits=c(-430,430), breaks=seq(-400,500,100)) +
  geom_text(data=labs, aes(x=2020.1, y=count, label=new_categories),
            hjust=0,size=2.2,lineheight = .75, color=after_scale(cols), fontface="bold") +
  scale_x_continuous(limits=c(2009,2022.5),breaks=c(2010,2015,2020)) + 
  theme_void() + 
  theme(panel.grid.major.x = element_line(color = "#E8E1DB"),
        legend.position="none",
        axis.text.x=element_text(color="grey50",size=8),
        plot.margin=ggplot2::margin(0.5,0,0.5,0,"cm"),
        plot.title=element_text(face="bold",size=12, hjust=0.5)) + 
  labs(title="Animal Rescues by London Fire Brigade (2009 to 2020)\n")
# packing circles chart

data2a = animal_rescues %>% filter(cal_year==2009 | cal_year==2015 | cal_year==2020) %>%
  group_by(cal_year,special_service_type_category) %>% tally() %>% 
  mutate(prop=n/sum(n),prop = round(prop*100)) %>% ungroup()

data2b = data2a %>% group_by(cal_year) %>% count(wt=prop) %>% ungroup()

data2c = data2b %>% 
  pmap_df(
  .f = ~circleProgressiveLayout(rep(0.5, ..2))) %>% 
  mutate(cal_year = rep(data2a$cal_year, data2a$prop),
         special_service_type_category = rep(data2a$special_service_type_category, data2a$prop)
  )

# graphic params
my_gpar <- gpar(
  col = "black",
  fontsize = 7
)

# plot
(plot2 = data2c%>% 
    ggplot(aes(x, y, fill = special_service_type_category)) + 
    geom_point(size = 4,pch = 21, color="white") + 
    grid_panel(
      grob = function(data, coords) {
        if (data$PANEL[1] == 1) {
          .x <- min(coords$x) + 0.085
          .y <- min(coords$y) + 0.085
          gList(
            textGrob(
              label = "Each circle represents\n1% of animal incidents",
              x = unit(.x/2.5, "npc"),
              y = unit(.y/2.5, "npc"),
              just = c("left", "top"),
              gp = my_gpar
            )
          )
        } else {
          nullGrob()
        }
      }
    )+
  facet_wrap(~cal_year) + 
  scale_fill_manual(values=c("#780000","#dda15e","#c8553d","#0a9396")) +
  scale_x_continuous(limits=c(-5,5)) + scale_y_continuous(limits=c(-6,6)) +
  theme_void() +
  theme(legend.position="bottom",
        plot.margin=ggplot2::margin(0.5,1,0.5,1,"cm"),
        plot.title=element_text(hjust=0.5, size=10,face="bold"),
        strip.text=element_text(size=10,face="bold", color="#343a40"),
        plot.caption=element_text(size=8),
        legend.text = element_text(size=8)) +
  guides(fill=guide_legend(ncol=2,title.position = "top", title.hjust = .5)) + 
  labs(title="\n\nProportion of Special Service Type Category\n\n", fill="",
       caption="\n\nTidy Tuesday Week 27 | Data from London.gov")
)
ggarrange(plot1, plot2, ncol=1)

Animal group parent

# animal_group_parent freq table
animal_rescues %>% 
  mutate(animal_group_parent = tolower(animal_group_parent)) %>%
  group_by(animal_group_parent) %>% tally(sort=T)

Clean data

ar_cln = animal_rescues %>%
  mutate(animal_group_parent = tolower(animal_group_parent),
         final_description=tolower(final_description)) %>% 
  mutate(datetime= dmy_hm(date_time_of_call), #parse datetime
         date = date(datetime), #date
         time=as_hms(dmy_hm(date_time_of_call)), #time
         week_day=wday(date, abbr=F, label=T, week_start = 1), #day of week label
         week_day_n=wday(date, week_start = 1), #day of week 
         hour= hour(datetime), #hour 
         year_day=yday(date), #day of year
         week_n = lubridate::isoweek(date)) #week number
ar_cln

Animal rescue count by week number

table_s1 = ar_cln %>% filter(cal_year<=2020) %>% 
  group_by(cal_year, week_n) %>% tally() 

  
table_s2 = table_s1 %>%
  group_by(week_n) %>% summarise(n=mean(n))


ar_cln %>% filter(cal_year<=2020) %>% 
  group_by(cal_year, week_n) %>% tally() %>%
  ggplot(aes(x=week_n, y=n)) + 
  geom_point(aes(color=cal_year),alpha=0.7, shape=21) +
  geom_step(data = (table_s1 %>%
  group_by(week_n)) %>% summarise(n=mean(n)), aes(y=n, x=week_n-0.5)) + 
  #scale_color_continuous_sequential(palette="Peach") + 
  scale_x_continuous(breaks=seq(1,53,13)) + 
  scale_color_gradientn(colours = wes_palette("Zissou1", type = "continuous"),
                        breaks=c(2010,2015,2020)) +
  theme(legend.position = "right",
        legend.title = element_text(size=8.5),
        legend.margin=margin(0,0,0,0),
        legend.box.margin=margin(-5,-10,-5,-10),
        axis.title.y=element_text(margin=margin(r=10)),
        axis.title.x=element_text(margin=margin(t=10))) +
  guides(color = guide_colorbar(reverse=T,
                                title.position = "top", 
                                barheight = unit(8, "lines"), 
                                barwidth = unit(.5, "lines"))) +
  labs(color="Year", x="Week of year", y="Count of animal rescues",
       subtitle="Average number of LFB animal rescues by week")

NA
NA
NA
# day of week
ar_cln %>% filter(cal_year<=2020) %>%
  group_by(week_n, week_day, week_day_n) %>% tally() %>% ungroup() %>% 
  mutate(group=ifelse(week_day_n>=6,"weekend","weekday")) %>%
  ggplot(aes(x=week_day, y=n, color=group)) + 
  geom_half_boxplot() + 
  geom_half_point(alpha=0.6) + 
  scale_color_manual(values=c("#005f73","#ca6702")) +
  theme(axis.title.y=element_text(margin=margin(r=10)),
        axis.title.x=element_text(margin=margin(t=10)),
        axis.text.x=element_text(margin=margin(t=-5)),
        plot.margin = unit(c(1, 1.5, 1, 0.25), "cm")) +
  labs(x="Day of week", y="Count of animal rescues",
       subtitle= "LFB animal recues by day of week")  +
  scale_x_discrete(expand=c(0,1)) + 
  scale_y_continuous(expand=c(0.1,0))

Property category

animal_rescues %>% 
  mutate(group = fct_lump(animal_group_parent, 10)) %>%
  filter(group!="Unknown - Domestic Animal Or Pet") %>%
  filter(property_category!="Boat") %>%
  mutate(prop_cat = fct_lump(property_category, 3)) %>%
  group_by(group,prop_cat) %>% tally() %>%
  mutate(prop=round(n/sum(n),4)) %>%
  mutate(prop_cat=factor(prop_cat, 
                         levels=c("Outdoor","Other","Non Residential","Dwelling",order=T))) %>%
  ggplot(aes(y=fct_rev(group), x=prop, fill=prop_cat)) +
  geom_col(width=0.7, alpha=0.8, color="white",size=0.5) + 
  scale_y_discrete(labels = function(x) str_wrap(x, width = 20)) + 
  scale_x_continuous(expand=c(0,0), labels=percent_format()) +
  theme(legend.position="top",
        legend.justification = "left",
        plot.margin = unit(c(1, 2, 1, 0.25), "cm"),
        legend.margin = margin(l=-5, t=10,b=0),
        legend.title=element_blank(),
        axis.title=element_blank(),
        panel.grid.major.y=element_blank()
        ) + 
  scale_fill_npg() + 
  guides(fill=guide_legend(keywidth = 0.5, nrow=1, reverse = T)) + 
  labs(subtitle="Proportion of LFB animal rescues by animal group and property category")

Recreating Guardian graphic part 1

# data preparation
# reference: https://twitter.com/thomas_mock/status/1409893668375478275/photo/1 
ct = animal_rescues %>% 
  filter(cal_year >= 2019, cal_year != 2021) %>%
  mutate(group = fct_lump(animal_group_parent, 7)) %>%
  group_by(cal_year, group) %>% tally() %>%
  arrange(group) %>%
  filter(group!="Unknown - Domestic Animal Or Pet") %>%
  mutate(group=factor(group, 
                      levels=c("Cat","Bird","Dog","Fox","Horse","Deer","Other",ordered=T))) %>%
  ungroup() %>% group_by(group) %>%
  mutate(prop=percent((n-lag(n))/lag(n))) %>%
  mutate(lab = glue::glue("{group} <b>{unique(na.omit(prop))}</b>"))

# plot
ct %>%
  ggplot(aes(y=fct_rev(lab), x=n, fill=factor(cal_year))) + 
  geom_col(position=position_dodge(width=0.8), width=0.7) + 
  geom_vline(color="white",xintercept=c(seq(0,300,50)), size=0.4) +
  scale_x_continuous(breaks=seq(0,300,50),position="top",expand=c(0,0)) +
  scale_y_discrete(expand=c(0,0)) +
  scale_fill_manual(values=c("grey75","#ba181b")) + 
  theme_light() +
  theme(axis.title=element_blank(),
        plot.title=element_text(face="bold", size=13.5),
        axis.text.y=ggtext::element_markdown(size=10, color="black", hjust=0),
        axis.ticks.y=element_blank(),
        axis.ticks.length = unit(0.14,"cm"),
        plot.title.position = "plot",
        legend.position = "top",
        legend.justification = "left",
        legend.margin = margin(l=-8, t=3.5,b=0),
        legend.text = element_text(size=9.5),
        panel.border = element_blank(),
        panel.grid.minor = element_blank(),
        panel.grid.major.y=element_blank(),
        plot.margin = unit(c(1.5, 1, 1.5, 1), "cm"),
        plot.caption.position = "plot",
        plot.caption=element_text(size=9, color="grey50",hjust=0, margin=margin(t=18)),
        axis.ticks.x.top = element_line(color = "grey75", size=0.4),
        axis.text.x.top = element_text(color="grey70",face="bold",size=10,
                                       margin=margin(b=7,t=-8), vjust=-1)
        ) +
  guides(fill=guide_legend(keyheight=0.8,keywidth=0.4, reverse=T)) +
  labs(title= "Cats accounted for 45% of London fire brigade animal rescues, but the\nbiggest proportional increases were among birds and foxes", fill="",
       caption="Source: London fire brigade") 

Recreating Guardian graphic part 2

animal_rescues %>% filter(cal_year!=2021) %>%
  group_by(cal_year) %>% tally() %>%
  ggplot(aes(x=factor(cal_year), y=n, 
             fill=I(if_else(cal_year==2020,"#d90429","grey84")))) + 
  geom_col() + 
  geom_hline(color="black",yintercept=0, size=0.3) +
  scale_x_discrete(expand=c(0,1.4)) +
  scale_y_continuous(limits=c(0,850),breaks=seq(0,800,200)) +
  theme_minimal() +
  theme(panel.grid.major.x=element_blank(),
        panel.grid.minor=element_blank(),
        panel.grid.major.y=element_line(color="grey90",size=0.25),
        plot.title=element_text(face="bold", size=12),
        plot.caption=element_text(hjust=0, size=8, color="grey50"),
        plot.margin = unit(c(1, 2, 1, 2), "cm"),
        axis.title=element_blank(),
        axis.text.x=element_text(color="grey70",size=9, vjust=5),
        axis.text.y=element_text(color="grey70",size=9, vjust=-0.7,
                                 margin=margin(l=20,r=-20))) + 
  labs(title="London firefighters attended 755 animal rescues across the\ncapital in 2020",
       caption="Source: London fire brigade") + 
  coord_cartesian(xlim = c(1,11.5)) 

Dwelling Animal Rescues in 2020 (percent change from 2019)

library(openintro)
data(london_boroughs) 

animal_rescues_c<-animal_rescues%>%mutate(borough = str_to_title(borough)) %>% group_by(borough,property_category) %>% summarize (n=n())


animal_rescues_c$borough<- fct_recode(animal_rescues_c$borough, 
                                      "Barking & Dagenham" = "Barking And Dagenham",
                                       "Hammersmith & Fulham" = "Hammersmith And Fulham",
                                      "Kensington & Chelsea" = "Kensington And Chelsea",
                                      "Kingston" = "Kingston Upon Thames",
                                      "Richmond" = "Richmond Upon Thames")

london_boroughs<-london_boroughs %>% na.omit()
borough_join<- animal_rescues_c%>% left_join(london_boroughs, by= "borough") 

borough_join_c <- borough_join %>% mutate(
  color = case_when(
    n < 70  ~ "< 70",
    n >= 70 & n < 100  ~ "70 to 100",
    n >= 100 & n < 150  ~ "100 to 150",
    n >= 150   ~ "> 150",
    TRUE ~ "Others"
  ))


borough_join_c$color <- fct_relevel(borough_join_c$color, c("> 150",
                                                                  "100 to 150","70 to 100","< 70"))
bdata<-animal_rescues%>%mutate(borough = str_to_title(borough)) %>%
  filter(cal_year==2019 | cal_year==2020) %>% 
  filter(property_category== "Dwelling") %>%
  group_by(borough,cal_year) %>% summarize (n=n()) %>%
  mutate(diff = n-lag(n), pctdiff = round(diff/lag(n),3)) %>% #pct diff from 2019
  filter(cal_year== 2020)
summary(bdata$pctdiff) 

bdata$borough<- fct_recode(bdata$borough, 
                                      "Barking & Dagenham" = "Barking And Dagenham",
                                      "Hammersmith & Fulham" = "Hammersmith And Fulham",
                                      "Kensington & Chelsea" = "Kensington And Chelsea",
                                      "Kingston" = "Kingston Upon Thames",
                                      "Richmond" = "Richmond Upon Thames")

london_boroughs<-london_boroughs %>% na.omit()
borough_join<- bdata%>% left_join(london_boroughs, by= "borough")  

borough_join_c <- borough_join %>% mutate(
  color = case_when(
    pctdiff <= 0  ~ "-50% to 0%",
    pctdiff > 0 & pctdiff <= 0.5  ~ "+1% to +50%",
    pctdiff > 0.5 & pctdiff <= 1  ~ "+50% to +100%",
    pctdiff > 1   ~ "+100% to +280%",
    TRUE ~ "Others"
  ))

borough_join_c$color <- fct_relevel(borough_join_c$color, 
                                    c("-50% to 0%","+1% to +50%","+50% to +100%","+100% to +280%"))
ggplot() +
  geom_polygon(data=borough_join_c, aes(x=x, y=y, group = borough, fill = color),
               colour="white",size=0.15) + 
  scale_fill_manual(values=c("-50% to 0%" = "#ffa62b",
                             "+1% to +50%" = "#82c0cc",
                             "+50% to +100%" = "#489fb5",
                             "+100% to +280%" ="#16697a")) + 
  coord_fixed() + # lock aspect ratio
  theme_void(base_size=8.5) + 
  theme(plot.margin = unit(c(1, 3, 1, 3), "cm"),
        legend.position="bottom", 
        plot.title=element_text(face="bold",size=10, hjust=0.5),
        plot.subtitle = element_text(size=8, hjust=0.5)) +
  guides(fill = guide_legend(
    label.position = "bottom",
    color = "#808080",
    keywidth = 4, keyheight = 0.3)) + 
  labs(fill="",
       title="Dwelling Animal Rescues by LBF in 2020",
       subtitle="Animal rescue count in 2020 expressed as a percentage of 2019 animal rescue count\n")

Information retrieval

library(tidytext)
library(SnowballC)
dim(animal_rescues)
[1] 7544   31
n_distinct(animal_rescues$incident_number)
[1] 4067
animal_rescues %>% drop_na(incident_number) %>% count() %>% flatten()
$n
[1] 4066
# 10 most common nouns (animal_group_parent == "cat")
 ar_cln %>% drop_na(incident_number) %>%
  filter(animal_group_parent=="cat") %>%
  select(incident_number, final_description) %>%
  unnest_tokens(word,final_description) %>% #token
  filter(word!="redacted") %>% 
  anti_join(get_stopwords()) %>% #remove stop words
  inner_join(parts_of_speech) %>% #pos
  mutate(stem=wordStem(word)) %>% #stem
  filter(pos=="Noun") %>% count(stem,sort=T) %>%
  slice(1:10)
# 10 most common nouns (animal_group_parent == "dog")
 ar_cln %>% drop_na(incident_number) %>%
  filter(animal_group_parent=="dog") %>%
  select(incident_number, final_description) %>%
  unnest_tokens(word,final_description) %>% #token
  filter(word!="redacted") %>% 
  anti_join(get_stopwords()) %>% #remove stop words
  inner_join(parts_of_speech) %>% #pos
  mutate(stem=wordStem(word)) %>% #stem
  filter(pos=="Noun") %>% count(stem,sort=T) %>%
  slice(1:10)
Joining, by = "word"
Joining, by = "word"
LS0tCnRpdGxlOiAiVGlkeSBUdWVzZGF5IDI3LzIwMjEiCmRhdGU6ICIyMDIxLzA2LzI5IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpbVGlkeSBUdWVzZGF5XShodHRwczovL2dpdGh1Yi5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5KSB3ZWVrIDI3IFtBbmltYWwgUmVzY3Vlc10oaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9ibG9iL21hc3Rlci9kYXRhLzIwMjEvMjAyMS0wNi0yOS9yZWFkbWUubWQpLCBkYXRhIGZyb20gW0xvbmRvbi5nb3ZdKGh0dHBzOi8vZGF0YS5sb25kb24uZ292LnVrL2RhdGFzZXQvYW5pbWFsLXJlc2N1ZS1pbmNpZGVudHMtYXR0ZW5kZWQtYnktbGZiKSBieSB3YXkgb2YgW0RhdGEgaXMgUGx1cmFsXShodHRwczovL3d3dy5kYXRhLWlzLXBsdXJhbC5jb20vYXJjaGl2ZS8yMDIxLTA2LTE2LWVkaXRpb24vKSBhbmQgW0dlb3JnaW9zIEthcmFtYW5pc10oaHR0cHM6Ly90d2l0dGVyLmNvbS9nZW9rYXJhbWFuaXMpLgoKYGBge3J9CiMgbG9hZCBsaWJyYXJpZXMKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2d0ZXh0KQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoaG1zKQpsaWJyYXJ5KGdnc2Fua2V5KQpsaWJyYXJ5KGdnZ3JpZCkgCmxpYnJhcnkocGFja2NpcmNsZXMpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoZ2didW1wKQpsaWJyYXJ5KGdnaGFsdmVzKQpsaWJyYXJ5KGNvbG9yc3BhY2UpCmxpYnJhcnkod2VzYW5kZXJzb24pCmBgYAoKCmBgYHtyfQojIGltcG9ydCBkYXRhCmFuaW1hbF9yZXNjdWVzIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDYtMjkvYW5pbWFsX3Jlc2N1ZXMuY3N2JykKYGBgCgpgYGB7cn0Kc3VtbWFyeShhbmltYWxfcmVzY3VlcyRjYWxfeWVhcikKYGBgCgojIyMgQW5pbWFsIHR5cGUgYW5kIFNlcnZpY2UgdHlwZQpgYGB7cn0KIyBwcmVwYXJlIGRhdGEKZGF0YSA9YW5pbWFsX3Jlc2N1ZXMgJT4lIAogIGdyb3VwX2J5KGNhbF95ZWFyLHNwZWNpYWxfc2VydmljZV90eXBlX2NhdGVnb3J5LCBzcGVjaWFsX3NlcnZpY2VfdHlwZSkgJT4lIHRhbGx5KCkgJT4lIAogIG11dGF0ZShzcGVjaWFsX3NlcnZpY2VfdHlwZT10b2xvd2VyKHNwZWNpYWxfc2VydmljZV90eXBlKSkgJT4lCiAgbXV0YXRlKHNwZWNpYWxfc2VydmljZV90eXBlPXN0cl9yZXBsYWNlX2FsbChzcGVjaWFsX3NlcnZpY2VfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCJhbmltYWwgcmVzY3VlIj0iIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICItIj0iIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmcm9tIGJlbG93IGdyb3VuZCI9IiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZnJvbSBoZWlnaHQiPSIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImZyb20gd2F0ZXIiPSIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9yIG11ZCI9IiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYW5pbWFsIGFzc2lzdGFuY2UiPSIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImludm9sdmluZyAiPSIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzc2lzdCAiPSIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFuaW1hbCI9IiIpKSkgJT4lCiAgbXV0YXRlKHNwZWNpYWxfc2VydmljZV90eXBlPSBzdHJfc3F1aXNoKHNwZWNpYWxfc2VydmljZV90eXBlKSkgJT4lCiAgbXV0YXRlKFN1YkNhdD0gY2FzZV93aGVuKGdyZXBsKCJ0cmFwcGVkIiwgc3BlY2lhbF9zZXJ2aWNlX3R5cGUpIH4gInRyYXBwZWQiLAogICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiaGFybSIsIHNwZWNpYWxfc2VydmljZV90eXBlKSB+Imhhcm0iLAogICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgibGlmdCBoZWF2eSIsIHNwZWNpYWxfc2VydmljZV90eXBlKSB+ImxpZnQgaGVhdnkiLAogICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgib3RoZXIgYWN0aW9uIiwgc3BlY2lhbF9zZXJ2aWNlX3R5cGUpIH4ib3RoZXIgYWN0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoImZyb20gYmVsb3cgZ3JvdW5kIiwgc3BlY2lhbF9zZXJ2aWNlX3R5cGVfY2F0ZWdvcnkpIH4iZnJvbSBiZWxvdyBncm91bmQiLAogICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgiZnJvbSBoZWlnaHQiLCBzcGVjaWFsX3NlcnZpY2VfdHlwZV9jYXRlZ29yeSkgfiJmcm9tIGhlaWdodCIsCiAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCJmcm9tIHdhdGVyIiwgc3BlY2lhbF9zZXJ2aWNlX3R5cGVfY2F0ZWdvcnkpIH4iZnJvbSB3YXRlciIKICAgICAgICAgICAgICAgICAgICAgICAgKSkgJT4lCiAgbXV0YXRlKENhdD0gY2FzZV93aGVuKGdyZXBsKCJyZXNjdWUiLCBzcGVjaWFsX3NlcnZpY2VfdHlwZV9jYXRlZ29yeSkgfiAiYW5pbWFsIHJlc2N1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAib3RoZXIgYW5pbWFsIGFzc2lzdGFuY2UiKSkgJT4lCiAgbXV0YXRlKEFuaW1hbFR5cGU9c3RyX3JlcGxhY2VfYWxsKHNwZWNpYWxfc2VydmljZV90eXBlLGMoImxpZnQgaGVhdnkgIj0iIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiBvdGhlciBhY3Rpb24iPSIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGFybSAiPSIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHJhcHBlZCAiPSIiKSkpICU+JQogIG11dGF0ZShuZXdfY2F0ZWdvcmllcyA9IGlmZWxzZShzcGVjaWFsX3NlcnZpY2VfdHlwZV9jYXRlZ29yeT09Ik90aGVyIGFuaW1hbCBhc3Npc3RhbmNlIixwYXN0ZTAoIk90aGVyIGFuaW1hbCBhc3Npc3RhbmNlIiwiOiIsIiAiLFN1YkNhdCksc3BlY2lhbF9zZXJ2aWNlX3R5cGVfY2F0ZWdvcnkpKQpkYXRhCmBgYAoKYGBge3IsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQpkYXRhICU+JSBmaWx0ZXIoY2FsX3llYXI8PTIwMjApICU+JQogIGdyb3VwX2J5KG5ld19jYXRlZ29yaWVzLCBBbmltYWxUeXBlKSAlPiUgc3VtbWFyaXNlKHRvdGFsPXN1bShuKSkgJT4lCiAgZ2dwbG90KGFlcyh5PWZjdF9yZXYoQW5pbWFsVHlwZSksIHg9dG90YWwsIGZpbGw9dG90YWwpKSArIAogIGdlb21fY29sKCkgKyAKICBmYWNldF93cmFwKH5uZXdfY2F0ZWdvcmllcywgbmNvbD0zLCBzY2FsZXM9ImZyZWUiLGxhYmVsbGVyID0gbGFiZWxsZXIobmV3X2NhdGVnb3JpZXMgPSBsYWJlbF93cmFwX2dlbigyOCkpKSArIAogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTApICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5taW5vcj1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJub25lIiwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLHNpemU9OCksCiAgICAgICAgcGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IiwKICAgICAgICApICsgCiAgc2NhbGVfZmlsbF9jb250aW51b3VzX3NlcXVlbnRpYWwocGFsZXR0ZT0iQmF0bG93IikgKyAKICBsYWJzKHk9IkFuaW1hbCBUeXBlIiwgeD0iUmVzY3VlIENvdW50IiwKICAgICAgIHN1YnRpdGxlPSJMRkIgQW5pbWFsIFJlc2N1ZXMgQ291bnQgYnkgQW5pbWFsIGFuZCBTZXJ2aWNlIFR5cGUsIGZyb20gMjAwOSB0byAyMDIwXG4iKQpgYGAKCiMjIyBTcGVjaWFsIHNlcnZpY2UgdHlwZSBjYXRlZ29yaWVzICgyMDA5IC0gMjAyMCkKCmBgYHtyfQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbChiYXNlX3NpemU9MTApKQp0aGVtZV91cGRhdGUocGFuZWwuZ3JpZC5taW5vcj1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICBwbG90LnRpdGxlLnBvc2l0aW9uPSJwbG90IiwKICAgICAgICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDEsIDEsIDEsIDAuMjUpLCAiY20iKSwKICAgICAgICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OC41LCBmYWNlPSJib2xkIiksCiAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb249Im5vbmUiKQpgYGAKCmBgYHtyfQphbmltYWxfcmVzY3VlcyAlPiUgZmlsdGVyKGNhbF95ZWFyPD0yMDIwKSAlPiUKICBncm91cF9ieShjYWxfeWVhciwgc3BlY2lhbF9zZXJ2aWNlX3R5cGVfY2F0ZWdvcnkpICU+JSB0YWxseSgpIC0+IGxpbmVkYXRhCgpsaW5lZGF0YSAlPiUKICBnZ3Bsb3QoYWVzKHg9Y2FsX3llYXIsIHk9biwgY29sb3I9cmVvcmRlcihzcGVjaWFsX3NlcnZpY2VfdHlwZV9jYXRlZ29yeSwtbikpKSArCiAgZ2VvbV9idW1wKHNpemU9MC44KSArIAogIGdlb21fcG9pbnQoc2l6ZT0xLjUpICsgCiAgZ2VvbV90ZXh0KGRhdGE9IGxpbmVkYXRhICU+JSBmaWx0ZXIoY2FsX3llYXI9PTIwMjApLAogICAgICAgICAgICBhZXMoeD0yMDIwLjIsbGFiZWw9c3BlY2lhbF9zZXJ2aWNlX3R5cGVfY2F0ZWdvcnkpLGhqdXN0PTAsc2l6ZT0yLjUsIGZvbnRmYWNlPSJib2xkIikgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzPWMoMjAwOSwyMDI0KSwgYnJlYWtzPXNlcSgyMDEwLDIwMjAsNSkpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDAsNDAwKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjMGEyNDYzIiwiI2RkMWMxYSIsIiMzYzZlNzEiLCIjZjI2NDE5IiwiIzI0N2JhMCIpKSArCiAgbGFicyh4PSJZZWFyIiwgeT0iQ291bnQiLCBzdWJ0aXRsZT0iTEZCIHNwZWNpYWwgc2VydmljZSB0eXBlIGNhdGVnb3J5IGZyb20gMjAwOSB0byAyMDIwXG4iKQpgYGAKCgojIyMgU3BlY2lhbCBTZXJ2aWNlIFR5cGUgCiogc2hhcmVkIG9uIFtUd2l0dGVyXShodHRwczovL3R3aXR0ZXIuY29tL2xlZW9sbmV5My9zdGF0dXMvMTQwOTc0MTc5MjI3OTQxNjgzMy9waG90by8xKQoqIHNhbmtleSBidW1wIGNoYXJ0IHJlZmVyZW5jZTogaHR0cHM6Ly90d2l0dGVyLmNvbS9DZWRTY2hlcmVyL3N0YXR1cy8xMzg0NDIwMDEzNTMzMjQxMzUxL3Bob3RvLzEKKiBwYWNraW5nIGNpcmNsZXMgY2hhcnQgcmVmZXJlbmNlOiBodHRwczovL3R3aXR0ZXIuY29tL2lzc2FfbWFkamlkL3N0YXR1cy8xNDAyNjcxMjQ1NzU4NDU1ODA5L3Bob3RvLzEKKiBwYWNraW5nIGNpcmNsZXMgY2hhcnQgcmVmZXJlbmNlOiBodHRwczovL3R3aXR0ZXIuY29tL2t1c3Rhdl9zZW4vc3RhdHVzLzE0MDM2MTI3MzgyNDk2NjI0NjQKCmBgYHtyfQojIHNhbmtleSBidW1wIGNoYXJ0IAoKIyBmcmVxIHRhYmxlCnBsb3RkYXRhID0gZGF0YSAlPiVmaWx0ZXIoY2FsX3llYXI8PTIwMjApICU+JSBncm91cF9ieShjYWxfeWVhciwgbmV3X2NhdGVnb3JpZXMpICU+JSBzdW1tYXJpc2UoY291bnQ9c3VtKG4pKSAKCiMgY3JlYXRlIGxhYmVscyB3aXRoIG9yZGVyCnBsb3RkYXRhMiA9IHBsb3RkYXRhICU+JSBmaWx0ZXIoY2FsX3llYXI9PTIwMjApICU+JSBhcnJhbmdlKC1jb3VudCkgJT4lCiAgbXV0YXRlKG5ld19jYXRlZ29yaWVzPWZjdF9pbm9yZGVyKG5ld19jYXRlZ29yaWVzKSkKCmxhYnMgPC0gdGliYmxlKAogICAgbmV3X2NhdGVnb3JpZXMgPSBmYWN0b3IobGV2ZWxzKHBsb3RkYXRhMiRuZXdfY2F0ZWdvcmllcyksIGxldmVscyA9IGxldmVscyhwbG90ZGF0YTIkbmV3X2NhdGVnb3JpZXMpKSwKICAgIGNvdW50ID0gIGMoMjc1LCAwLCAtMjI1LCAtMzEwLCAtMzgwLCAtNDAwLCAtNDIwKSkKCiMgY29sb3JzCmNvbHMgPC0gYygiIzI4NjY2ZSIsICIjYzg1NTNkIiwgIiM3ODAwMDAiLCIjYjViNjgyIiwiI2RkYTE1ZSIsCiAgICAgICAgICAiIzdjOTg4NSIsIiMwMzNmNjMiKQoKIyBwbG90CnBsb3QxID0gcGxvdGRhdGEgJT4lCiAgZ2dwbG90KGFlcyh4PWNhbF95ZWFyLCB2YWx1ZT1jb3VudCwgbm9kZT1uZXdfY2F0ZWdvcmllcywgZmlsbD1mYWN0b3IobmV3X2NhdGVnb3JpZXMsbGV2ZWxzPXBsb3RkYXRhMiRuZXdfY2F0ZWdvcmllcykpKSArIAogIGdlb21fc2Fua2V5X2J1bXAoc3BhY2U9MTUsIHNtb290aD0xMikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9Y29scykgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygtNDMwLDQzMCksIGJyZWFrcz1zZXEoLTQwMCw1MDAsMTAwKSkgKwogIGdlb21fdGV4dChkYXRhPWxhYnMsIGFlcyh4PTIwMjAuMSwgeT1jb3VudCwgbGFiZWw9bmV3X2NhdGVnb3JpZXMpLAogICAgICAgICAgICBoanVzdD0wLHNpemU9Mi4yLGxpbmVoZWlnaHQgPSAuNzUsIGNvbG9yPWFmdGVyX3NjYWxlKGNvbHMpLCBmb250ZmFjZT0iYm9sZCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzPWMoMjAwOSwyMDIyLjUpLGJyZWFrcz1jKDIwMTAsMjAxNSwyMDIwKSkgKyAKICB0aGVtZV92b2lkKCkgKyAKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiI0U4RTFEQiIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGNvbG9yPSJncmV5NTAiLHNpemU9OCksCiAgICAgICAgcGxvdC5tYXJnaW49Z2dwbG90Mjo6bWFyZ2luKDAuNSwwLDAuNSwwLCJjbSIpLAogICAgICAgIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLHNpemU9MTIsIGhqdXN0PTAuNSkpICsgCiAgbGFicyh0aXRsZT0iQW5pbWFsIFJlc2N1ZXMgYnkgTG9uZG9uIEZpcmUgQnJpZ2FkZSAoMjAwOSB0byAyMDIwKVxuIikKYGBgCgoKCgpgYGB7cn0KIyBwYWNraW5nIGNpcmNsZXMgY2hhcnQKCmRhdGEyYSA9IGFuaW1hbF9yZXNjdWVzICU+JSBmaWx0ZXIoY2FsX3llYXI9PTIwMDkgfCBjYWxfeWVhcj09MjAxNSB8IGNhbF95ZWFyPT0yMDIwKSAlPiUKICBncm91cF9ieShjYWxfeWVhcixzcGVjaWFsX3NlcnZpY2VfdHlwZV9jYXRlZ29yeSkgJT4lIHRhbGx5KCkgJT4lIAogIG11dGF0ZShwcm9wPW4vc3VtKG4pLHByb3AgPSByb3VuZChwcm9wKjEwMCkpICU+JSB1bmdyb3VwKCkKCmRhdGEyYiA9IGRhdGEyYSAlPiUgZ3JvdXBfYnkoY2FsX3llYXIpICU+JSBjb3VudCh3dD1wcm9wKSAlPiUgdW5ncm91cCgpCgpkYXRhMmMgPSBkYXRhMmIgJT4lIAogIHBtYXBfZGYoCiAgLmYgPSB+Y2lyY2xlUHJvZ3Jlc3NpdmVMYXlvdXQocmVwKDAuNSwgLi4yKSkpICU+JSAKICBtdXRhdGUoY2FsX3llYXIgPSByZXAoZGF0YTJhJGNhbF95ZWFyLCBkYXRhMmEkcHJvcCksCiAgICAgICAgIHNwZWNpYWxfc2VydmljZV90eXBlX2NhdGVnb3J5ID0gcmVwKGRhdGEyYSRzcGVjaWFsX3NlcnZpY2VfdHlwZV9jYXRlZ29yeSwgZGF0YTJhJHByb3ApCiAgKQoKIyBncmFwaGljIHBhcmFtcwpteV9ncGFyIDwtIGdwYXIoCiAgY29sID0gImJsYWNrIiwKICBmb250c2l6ZSA9IDcKKQoKIyBwbG90CihwbG90MiA9IGRhdGEyYyU+JSAKICAgIGdncGxvdChhZXMoeCwgeSwgZmlsbCA9IHNwZWNpYWxfc2VydmljZV90eXBlX2NhdGVnb3J5KSkgKyAKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDQscGNoID0gMjEsIGNvbG9yPSJ3aGl0ZSIpICsgCiAgICBncmlkX3BhbmVsKAogICAgICBncm9iID0gZnVuY3Rpb24oZGF0YSwgY29vcmRzKSB7CiAgICAgICAgaWYgKGRhdGEkUEFORUxbMV0gPT0gMSkgewogICAgICAgICAgLnggPC0gbWluKGNvb3JkcyR4KSArIDAuMDg1CiAgICAgICAgICAueSA8LSBtaW4oY29vcmRzJHkpICsgMC4wODUKICAgICAgICAgIGdMaXN0KAogICAgICAgICAgICB0ZXh0R3JvYigKICAgICAgICAgICAgICBsYWJlbCA9ICJFYWNoIGNpcmNsZSByZXByZXNlbnRzXG4xJSBvZiBhbmltYWwgaW5jaWRlbnRzIiwKICAgICAgICAgICAgICB4ID0gdW5pdCgueC8yLjUsICJucGMiKSwKICAgICAgICAgICAgICB5ID0gdW5pdCgueS8yLjUsICJucGMiKSwKICAgICAgICAgICAgICBqdXN0ID0gYygibGVmdCIsICJ0b3AiKSwKICAgICAgICAgICAgICBncCA9IG15X2dwYXIKICAgICAgICAgICAgKQogICAgICAgICAgKQogICAgICAgIH0gZWxzZSB7CiAgICAgICAgICBudWxsR3JvYigpCiAgICAgICAgfQogICAgICB9CiAgICApKwogIGZhY2V0X3dyYXAofmNhbF95ZWFyKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjNzgwMDAwIiwiI2RkYTE1ZSIsIiNjODU1M2QiLCIjMGE5Mzk2IikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzPWMoLTUsNSkpICsgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKC02LDYpKSArCiAgdGhlbWVfdm9pZCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIsCiAgICAgICAgcGxvdC5tYXJnaW49Z2dwbG90Mjo6bWFyZ2luKDAuNSwxLDAuNSwxLCJjbSIpLAogICAgICAgIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuNSwgc2l6ZT0xMCxmYWNlPSJib2xkIiksCiAgICAgICAgc3RyaXAudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMCxmYWNlPSJib2xkIiwgY29sb3I9IiMzNDNhNDAiKSwKICAgICAgICBwbG90LmNhcHRpb249ZWxlbWVudF90ZXh0KHNpemU9OCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT04KSkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTIsdGl0bGUucG9zaXRpb24gPSAidG9wIiwgdGl0bGUuaGp1c3QgPSAuNSkpICsgCiAgbGFicyh0aXRsZT0iXG5cblByb3BvcnRpb24gb2YgU3BlY2lhbCBTZXJ2aWNlIFR5cGUgQ2F0ZWdvcnlcblxuIiwgZmlsbD0iIiwKICAgICAgIGNhcHRpb249IlxuXG5UaWR5IFR1ZXNkYXkgV2VlayAyNyB8IERhdGEgZnJvbSBMb25kb24uZ292IikKKQpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NH0KIyBjb21iaW5lIHBsb3QxIGFuZCBwbG90MgpnZ2FycmFuZ2UocGxvdDEsIHBsb3QyLCBuY29sPTEpCmBgYAoKCiMjIyBBbmltYWwgZ3JvdXAgcGFyZW50CgpgYGB7cn0KIyBhbmltYWxfZ3JvdXBfcGFyZW50IGZyZXEgdGFibGUKYW5pbWFsX3Jlc2N1ZXMgJT4lIAogIG11dGF0ZShhbmltYWxfZ3JvdXBfcGFyZW50ID0gdG9sb3dlcihhbmltYWxfZ3JvdXBfcGFyZW50KSkgJT4lCiAgZ3JvdXBfYnkoYW5pbWFsX2dyb3VwX3BhcmVudCkgJT4lIHRhbGx5KHNvcnQ9VCkKYGBgCgojIyMgQ2xlYW4gZGF0YQpgYGB7cn0KYXJfY2xuID0gYW5pbWFsX3Jlc2N1ZXMgJT4lCiAgbXV0YXRlKGFuaW1hbF9ncm91cF9wYXJlbnQgPSB0b2xvd2VyKGFuaW1hbF9ncm91cF9wYXJlbnQpLAogICAgICAgICBmaW5hbF9kZXNjcmlwdGlvbj10b2xvd2VyKGZpbmFsX2Rlc2NyaXB0aW9uKSkgJT4lIAogIG11dGF0ZShkYXRldGltZT0gZG15X2htKGRhdGVfdGltZV9vZl9jYWxsKSwgI3BhcnNlIGRhdGV0aW1lCiAgICAgICAgIGRhdGUgPSBkYXRlKGRhdGV0aW1lKSwgI2RhdGUKICAgICAgICAgdGltZT1hc19obXMoZG15X2htKGRhdGVfdGltZV9vZl9jYWxsKSksICN0aW1lCiAgICAgICAgIHdlZWtfZGF5PXdkYXkoZGF0ZSwgYWJicj1GLCBsYWJlbD1ULCB3ZWVrX3N0YXJ0ID0gMSksICNkYXkgb2Ygd2VlayBsYWJlbAogICAgICAgICB3ZWVrX2RheV9uPXdkYXkoZGF0ZSwgd2Vla19zdGFydCA9IDEpLCAjZGF5IG9mIHdlZWsgCiAgICAgICAgIGhvdXI9IGhvdXIoZGF0ZXRpbWUpLCAjaG91ciAKICAgICAgICAgeWVhcl9kYXk9eWRheShkYXRlKSwgI2RheSBvZiB5ZWFyCiAgICAgICAgIHdlZWtfbiA9IGx1YnJpZGF0ZTo6aXNvd2VlayhkYXRlKSkgI3dlZWsgbnVtYmVyCmFyX2NsbgpgYGAKCgojIyMgQW5pbWFsIHJlc2N1ZSBjb3VudCBieSB3ZWVrIG51bWJlcgpgYGB7cn0KdGFibGVfczEgPSBhcl9jbG4gJT4lIGZpbHRlcihjYWxfeWVhcjw9MjAyMCkgJT4lIAogIGdyb3VwX2J5KGNhbF95ZWFyLCB3ZWVrX24pICU+JSB0YWxseSgpIAoKICAKdGFibGVfczIgPSB0YWJsZV9zMSAlPiUKICBncm91cF9ieSh3ZWVrX24pICU+JSBzdW1tYXJpc2Uobj1tZWFuKG4pKQoKCmFyX2NsbiAlPiUgZmlsdGVyKGNhbF95ZWFyPD0yMDIwKSAlPiUgCiAgZ3JvdXBfYnkoY2FsX3llYXIsIHdlZWtfbikgJT4lIHRhbGx5KCkgJT4lCiAgZ2dwbG90KGFlcyh4PXdlZWtfbiwgeT1uKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvcj1jYWxfeWVhciksYWxwaGE9MC43LCBzaGFwZT0yMSkgKwogIGdlb21fc3RlcChkYXRhID0gKHRhYmxlX3MxICU+JQogIGdyb3VwX2J5KHdlZWtfbikpICU+JSBzdW1tYXJpc2Uobj1tZWFuKG4pKSwgYWVzKHk9biwgeD13ZWVrX24tMC41KSkgKyAKICAjc2NhbGVfY29sb3JfY29udGludW91c19zZXF1ZW50aWFsKHBhbGV0dGU9IlBlYWNoIikgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgxLDUzLDEzKSkgKyAKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3VycyA9IHdlc19wYWxldHRlKCJaaXNzb3UxIiwgdHlwZSA9ICJjb250aW51b3VzIiksCiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcz1jKDIwMTAsMjAxNSwyMDIwKSkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9OC41KSwKICAgICAgICBsZWdlbmQubWFyZ2luPW1hcmdpbigwLDAsMCwwKSwKICAgICAgICBsZWdlbmQuYm94Lm1hcmdpbj1tYXJnaW4oLTUsLTEwLC01LC0xMCksCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfdGV4dChtYXJnaW49bWFyZ2luKHI9MTApKSwKICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF90ZXh0KG1hcmdpbj1tYXJnaW4odD0xMCkpKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfY29sb3JiYXIocmV2ZXJzZT1ULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlLnBvc2l0aW9uID0gInRvcCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhcmhlaWdodCA9IHVuaXQoOCwgImxpbmVzIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhcndpZHRoID0gdW5pdCguNSwgImxpbmVzIikpKSArCiAgbGFicyhjb2xvcj0iWWVhciIsIHg9IldlZWsgb2YgeWVhciIsIHk9IkNvdW50IG9mIGFuaW1hbCByZXNjdWVzIiwKICAgICAgIHN1YnRpdGxlPSJBdmVyYWdlIG51bWJlciBvZiBMRkIgYW5pbWFsIHJlc2N1ZXMgYnkgd2VlayIpCgoKICAKYGBgCgpgYGB7cn0KIyBkYXkgb2Ygd2Vlawphcl9jbG4gJT4lIGZpbHRlcihjYWxfeWVhcjw9MjAyMCkgJT4lCiAgZ3JvdXBfYnkod2Vla19uLCB3ZWVrX2RheSwgd2Vla19kYXlfbikgJT4lIHRhbGx5KCkgJT4lIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKGdyb3VwPWlmZWxzZSh3ZWVrX2RheV9uPj02LCJ3ZWVrZW5kIiwid2Vla2RheSIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9d2Vla19kYXksIHk9biwgY29sb3I9Z3JvdXApKSArIAogIGdlb21faGFsZl9ib3hwbG90KCkgKyAKICBnZW9tX2hhbGZfcG9pbnQoYWxwaGE9MC42KSArIAogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygiIzAwNWY3MyIsIiNjYTY3MDIiKSkgKwogIHRoZW1lKGF4aXMudGl0bGUueT1lbGVtZW50X3RleHQobWFyZ2luPW1hcmdpbihyPTEwKSksCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfdGV4dChtYXJnaW49bWFyZ2luKHQ9MTApKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQobWFyZ2luPW1hcmdpbih0PS01KSksCiAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMSwgMS41LCAxLCAwLjI1KSwgImNtIikpICsKICBsYWJzKHg9IkRheSBvZiB3ZWVrIiwgeT0iQ291bnQgb2YgYW5pbWFsIHJlc2N1ZXMiLAogICAgICAgc3VidGl0bGU9ICJMRkIgYW5pbWFsIHJlY3VlcyBieSBkYXkgb2Ygd2VlayIpICArCiAgc2NhbGVfeF9kaXNjcmV0ZShleHBhbmQ9YygwLDEpKSArIAogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQ9YygwLjEsMCkpCmBgYAoKIyMjIFByb3BlcnR5IGNhdGVnb3J5CmBgYHtyfQphbmltYWxfcmVzY3VlcyAlPiUgCiAgbXV0YXRlKGdyb3VwID0gZmN0X2x1bXAoYW5pbWFsX2dyb3VwX3BhcmVudCwgMTApKSAlPiUKICBmaWx0ZXIoZ3JvdXAhPSJVbmtub3duIC0gRG9tZXN0aWMgQW5pbWFsIE9yIFBldCIpICU+JQogIGZpbHRlcihwcm9wZXJ0eV9jYXRlZ29yeSE9IkJvYXQiKSAlPiUKICBtdXRhdGUocHJvcF9jYXQgPSBmY3RfbHVtcChwcm9wZXJ0eV9jYXRlZ29yeSwgMykpICU+JQogIGdyb3VwX2J5KGdyb3VwLHByb3BfY2F0KSAlPiUgdGFsbHkoKSAlPiUKICBtdXRhdGUocHJvcD1yb3VuZChuL3N1bShuKSw0KSkgJT4lCiAgbXV0YXRlKHByb3BfY2F0PWZhY3Rvcihwcm9wX2NhdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygiT3V0ZG9vciIsIk90aGVyIiwiTm9uIFJlc2lkZW50aWFsIiwiRHdlbGxpbmciLG9yZGVyPVQpKSkgJT4lCiAgZ2dwbG90KGFlcyh5PWZjdF9yZXYoZ3JvdXApLCB4PXByb3AsIGZpbGw9cHJvcF9jYXQpKSArCiAgZ2VvbV9jb2wod2lkdGg9MC43LCBhbHBoYT0wLjgsIGNvbG9yPSJ3aGl0ZSIsc2l6ZT0wLjUpICsgCiAgc2NhbGVfeV9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDIwKSkgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kPWMoMCwwKSwgbGFiZWxzPXBlcmNlbnRfZm9ybWF0KCkpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249InRvcCIsCiAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSAibGVmdCIsCiAgICAgICAgcGxvdC5tYXJnaW4gPSB1bml0KGMoMSwgMiwgMSwgMC4yNSksICJjbSIpLAogICAgICAgIGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4obD0tNSwgdD0xMCxiPTApLAogICAgICAgIGxlZ2VuZC50aXRsZT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55PWVsZW1lbnRfYmxhbmsoKQogICAgICAgICkgKyAKICBzY2FsZV9maWxsX25wZygpICsgCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKGtleXdpZHRoID0gMC41LCBucm93PTEsIHJldmVyc2UgPSBUKSkgKyAKICBsYWJzKHN1YnRpdGxlPSJQcm9wb3J0aW9uIG9mIExGQiBhbmltYWwgcmVzY3VlcyBieSBhbmltYWwgZ3JvdXAgYW5kIHByb3BlcnR5IGNhdGVnb3J5IikKCmBgYAoKIyMjIFJlY3JlYXRpbmcgR3VhcmRpYW4gZ3JhcGhpYyBwYXJ0IDEKCiogb3JpZ2luYWwgZ3VhcmRpYW4gZ3JhcGhpYzogaHR0cHM6Ly93d3cudGhlZ3VhcmRpYW4uY29tL3dvcmxkLzIwMjEvamFuLzA4L2FuaW1hbC1yZXNjdWVzLWxvbmRvbi1maXJlLWJyaWdhZGUtcmlzZS0yMDIwLXBhbmRlbWljLXllYXIpICAgCiAKYGBge3J9CiMgZGF0YSBwcmVwYXJhdGlvbgojIHJlZmVyZW5jZTogaHR0cHM6Ly90d2l0dGVyLmNvbS90aG9tYXNfbW9jay9zdGF0dXMvMTQwOTg5MzY2ODM3NTQ3ODI3NS9waG90by8xIApjdCA9IGFuaW1hbF9yZXNjdWVzICU+JSAKICBmaWx0ZXIoY2FsX3llYXIgPj0gMjAxOSwgY2FsX3llYXIgIT0gMjAyMSkgJT4lCiAgbXV0YXRlKGdyb3VwID0gZmN0X2x1bXAoYW5pbWFsX2dyb3VwX3BhcmVudCwgNykpICU+JQogIGdyb3VwX2J5KGNhbF95ZWFyLCBncm91cCkgJT4lIHRhbGx5KCkgJT4lCiAgYXJyYW5nZShncm91cCkgJT4lCiAgZmlsdGVyKGdyb3VwIT0iVW5rbm93biAtIERvbWVzdGljIEFuaW1hbCBPciBQZXQiKSAlPiUKICBtdXRhdGUoZ3JvdXA9ZmFjdG9yKGdyb3VwLCAKICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1jKCJDYXQiLCJCaXJkIiwiRG9nIiwiRm94IiwiSG9yc2UiLCJEZWVyIiwiT3RoZXIiLG9yZGVyZWQ9VCkpKSAlPiUKICB1bmdyb3VwKCkgJT4lIGdyb3VwX2J5KGdyb3VwKSAlPiUKICBtdXRhdGUocHJvcD1wZXJjZW50KChuLWxhZyhuKSkvbGFnKG4pKSkgJT4lCiAgbXV0YXRlKGxhYiA9IGdsdWU6OmdsdWUoIntncm91cH0gPGI+e3VuaXF1ZShuYS5vbWl0KHByb3ApKX08L2I+IikpCgojIHBsb3QKY3QgJT4lCiAgZ2dwbG90KGFlcyh5PWZjdF9yZXYobGFiKSwgeD1uLCBmaWxsPWZhY3RvcihjYWxfeWVhcikpKSArIAogIGdlb21fY29sKHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKHdpZHRoPTAuOCksIHdpZHRoPTAuNykgKyAKICBnZW9tX3ZsaW5lKGNvbG9yPSJ3aGl0ZSIseGludGVyY2VwdD1jKHNlcSgwLDMwMCw1MCkpLCBzaXplPTAuNCkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsMzAwLDUwKSxwb3NpdGlvbj0idG9wIixleHBhbmQ9YygwLDApKSArCiAgc2NhbGVfeV9kaXNjcmV0ZShleHBhbmQ9YygwLDApKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoImdyZXk3NSIsIiNiYTE4MWIiKSkgKyAKICB0aGVtZV9saWdodCgpICsKICB0aGVtZShheGlzLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90LnRpdGxlPWVsZW1lbnRfdGV4dChmYWNlPSJib2xkIiwgc2l6ZT0xMy41KSwKICAgICAgICBheGlzLnRleHQueT1nZ3RleHQ6OmVsZW1lbnRfbWFya2Rvd24oc2l6ZT0xMCwgY29sb3I9ImJsYWNrIiwgaGp1c3Q9MCksCiAgICAgICAgYXhpcy50aWNrcy55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpY2tzLmxlbmd0aCA9IHVuaXQoMC4xNCwiY20iKSwKICAgICAgICBwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gImxlZnQiLAogICAgICAgIGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4obD0tOCwgdD0zLjUsYj0wKSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTkuNSksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwbG90Lm1hcmdpbiA9IHVuaXQoYygxLjUsIDEsIDEuNSwgMSksICJjbSIpLAogICAgICAgIHBsb3QuY2FwdGlvbi5wb3NpdGlvbiA9ICJwbG90IiwKICAgICAgICBwbG90LmNhcHRpb249ZWxlbWVudF90ZXh0KHNpemU9OSwgY29sb3I9ImdyZXk1MCIsaGp1c3Q9MCwgbWFyZ2luPW1hcmdpbih0PTE4KSksCiAgICAgICAgYXhpcy50aWNrcy54LnRvcCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmV5NzUiLCBzaXplPTAuNCksCiAgICAgICAgYXhpcy50ZXh0LngudG9wID0gZWxlbWVudF90ZXh0KGNvbG9yPSJncmV5NzAiLGZhY2U9ImJvbGQiLHNpemU9MTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbj1tYXJnaW4oYj03LHQ9LTgpLCB2anVzdD0tMSkKICAgICAgICApICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQoa2V5aGVpZ2h0PTAuOCxrZXl3aWR0aD0wLjQsIHJldmVyc2U9VCkpICsKICBsYWJzKHRpdGxlPSAiQ2F0cyBhY2NvdW50ZWQgZm9yIDQ1JSBvZiBMb25kb24gZmlyZSBicmlnYWRlIGFuaW1hbCByZXNjdWVzLCBidXQgdGhlXG5iaWdnZXN0IHByb3BvcnRpb25hbCBpbmNyZWFzZXMgd2VyZSBhbW9uZyBiaXJkcyBhbmQgZm94ZXMiLCBmaWxsPSIiLAogICAgICAgY2FwdGlvbj0iU291cmNlOiBMb25kb24gZmlyZSBicmlnYWRlIikgCmBgYAoKCiMjIyBSZWNyZWF0aW5nIEd1YXJkaWFuIGdyYXBoaWMgcGFydCAyCiogb3JpZ2luYWwgZ2FyZGlhbiBncmFwaGljOiBodHRwczovL3d3dy50aGVndWFyZGlhbi5jb20vd29ybGQvMjAyMS9qYW4vMDgvYW5pbWFsLXJlc2N1ZXMtbG9uZG9uLWZpcmUtYnJpZ2FkZS1yaXNlLTIwMjAtcGFuZGVtaWMteWVhciAgCgpgYGB7cn0KYW5pbWFsX3Jlc2N1ZXMgJT4lIGZpbHRlcihjYWxfeWVhciE9MjAyMSkgJT4lCiAgZ3JvdXBfYnkoY2FsX3llYXIpICU+JSB0YWxseSgpICU+JQogIGdncGxvdChhZXMoeD1mYWN0b3IoY2FsX3llYXIpLCB5PW4sIAogICAgICAgICAgICAgZmlsbD1JKGlmX2Vsc2UoY2FsX3llYXI9PTIwMjAsIiNkOTA0MjkiLCJncmV5ODQiKSkpKSArIAogIGdlb21fY29sKCkgKyAKICBnZW9tX2hsaW5lKGNvbG9yPSJibGFjayIseWludGVyY2VwdD0wLCBzaXplPTAuMykgKwogIHNjYWxlX3hfZGlzY3JldGUoZXhwYW5kPWMoMCwxLjQpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDAsODUwKSxicmVha3M9c2VxKDAsODAwLDIwMCkpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vcj1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55PWVsZW1lbnRfbGluZShjb2xvcj0iZ3JleTkwIixzaXplPTAuMjUpLAogICAgICAgIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLCBzaXplPTEyKSwKICAgICAgICBwbG90LmNhcHRpb249ZWxlbWVudF90ZXh0KGhqdXN0PTAsIHNpemU9OCwgY29sb3I9ImdyZXk1MCIpLAogICAgICAgIHBsb3QubWFyZ2luID0gdW5pdChjKDEsIDIsIDEsIDIpLCAiY20iKSwKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueD1lbGVtZW50X3RleHQoY29sb3I9ImdyZXk3MCIsc2l6ZT05LCB2anVzdD01KSwKICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoY29sb3I9ImdyZXk3MCIsc2l6ZT05LCB2anVzdD0tMC43LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJnaW49bWFyZ2luKGw9MjAscj0tMjApKSkgKyAKICBsYWJzKHRpdGxlPSJMb25kb24gZmlyZWZpZ2h0ZXJzIGF0dGVuZGVkIDc1NSBhbmltYWwgcmVzY3VlcyBhY3Jvc3MgdGhlXG5jYXBpdGFsIGluIDIwMjAiLAogICAgICAgY2FwdGlvbj0iU291cmNlOiBMb25kb24gZmlyZSBicmlnYWRlIikgKyAKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMSwxMS41KSkgCmBgYAoKCiMjIyBEd2VsbGluZyBBbmltYWwgUmVzY3VlcyBpbiAyMDIwIChwZXJjZW50IGNoYW5nZSBmcm9tIDIwMTkpCiogcmVmZXJlbmNlOiBbSnVhbm1hXShodHRwczovL3R3aXR0ZXIuY29tL0p1YW5tYV9NTi9zdGF0dXMvMTQwOTkzNjc4NTA0NDY1NjEzOS9waG90by8xKQoKYGBge3J9CmxpYnJhcnkob3BlbmludHJvKQpkYXRhKGxvbmRvbl9ib3JvdWdocykgCgphbmltYWxfcmVzY3Vlc19jPC1hbmltYWxfcmVzY3VlcyU+JW11dGF0ZShib3JvdWdoID0gc3RyX3RvX3RpdGxlKGJvcm91Z2gpKSAlPiUgZ3JvdXBfYnkoYm9yb3VnaCxwcm9wZXJ0eV9jYXRlZ29yeSkgJT4lIHN1bW1hcml6ZSAobj1uKCkpCgoKYW5pbWFsX3Jlc2N1ZXNfYyRib3JvdWdoPC0gZmN0X3JlY29kZShhbmltYWxfcmVzY3Vlc19jJGJvcm91Z2gsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCYXJraW5nICYgRGFnZW5oYW0iID0gIkJhcmtpbmcgQW5kIERhZ2VuaGFtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhhbW1lcnNtaXRoICYgRnVsaGFtIiA9ICJIYW1tZXJzbWl0aCBBbmQgRnVsaGFtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiS2Vuc2luZ3RvbiAmIENoZWxzZWEiID0gIktlbnNpbmd0b24gQW5kIENoZWxzZWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJLaW5nc3RvbiIgPSAiS2luZ3N0b24gVXBvbiBUaGFtZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSaWNobW9uZCIgPSAiUmljaG1vbmQgVXBvbiBUaGFtZXMiKQoKbG9uZG9uX2Jvcm91Z2hzPC1sb25kb25fYm9yb3VnaHMgJT4lIG5hLm9taXQoKQpib3JvdWdoX2pvaW48LSBhbmltYWxfcmVzY3Vlc19jJT4lIGxlZnRfam9pbihsb25kb25fYm9yb3VnaHMsIGJ5PSAiYm9yb3VnaCIpIAoKYm9yb3VnaF9qb2luX2MgPC0gYm9yb3VnaF9qb2luICU+JSBtdXRhdGUoCiAgY29sb3IgPSBjYXNlX3doZW4oCiAgICBuIDwgNzAgIH4gIjwgNzAiLAogICAgbiA+PSA3MCAmIG4gPCAxMDAgIH4gIjcwIHRvIDEwMCIsCiAgICBuID49IDEwMCAmIG4gPCAxNTAgIH4gIjEwMCB0byAxNTAiLAogICAgbiA+PSAxNTAgICB+ICI+IDE1MCIsCiAgICBUUlVFIH4gIk90aGVycyIKICApKQoKCmJvcm91Z2hfam9pbl9jJGNvbG9yIDwtIGZjdF9yZWxldmVsKGJvcm91Z2hfam9pbl9jJGNvbG9yLCBjKCI+IDE1MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMDAgdG8gMTUwIiwiNzAgdG8gMTAwIiwiPCA3MCIpKQoKYGBgCgpgYGB7cn0KYmRhdGE8LWFuaW1hbF9yZXNjdWVzJT4lbXV0YXRlKGJvcm91Z2ggPSBzdHJfdG9fdGl0bGUoYm9yb3VnaCkpICU+JQogIGZpbHRlcihjYWxfeWVhcj09MjAxOSB8IGNhbF95ZWFyPT0yMDIwKSAlPiUgCiAgZmlsdGVyKHByb3BlcnR5X2NhdGVnb3J5PT0gIkR3ZWxsaW5nIikgJT4lCiAgZ3JvdXBfYnkoYm9yb3VnaCxjYWxfeWVhcikgJT4lIHN1bW1hcml6ZSAobj1uKCkpICU+JQogIG11dGF0ZShkaWZmID0gbi1sYWcobiksIHBjdGRpZmYgPSByb3VuZChkaWZmL2xhZyhuKSwzKSkgJT4lICNwY3QgZGlmZiBmcm9tIDIwMTkKICBmaWx0ZXIoY2FsX3llYXI9PSAyMDIwKQpzdW1tYXJ5KGJkYXRhJHBjdGRpZmYpIAoKYmRhdGEkYm9yb3VnaDwtIGZjdF9yZWNvZGUoYmRhdGEkYm9yb3VnaCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJhcmtpbmcgJiBEYWdlbmhhbSIgPSAiQmFya2luZyBBbmQgRGFnZW5oYW0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJIYW1tZXJzbWl0aCAmIEZ1bGhhbSIgPSAiSGFtbWVyc21pdGggQW5kIEZ1bGhhbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIktlbnNpbmd0b24gJiBDaGVsc2VhIiA9ICJLZW5zaW5ndG9uIEFuZCBDaGVsc2VhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiS2luZ3N0b24iID0gIktpbmdzdG9uIFVwb24gVGhhbWVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmljaG1vbmQiID0gIlJpY2htb25kIFVwb24gVGhhbWVzIikKCmxvbmRvbl9ib3JvdWdoczwtbG9uZG9uX2Jvcm91Z2hzICU+JSBuYS5vbWl0KCkKYm9yb3VnaF9qb2luPC0gYmRhdGElPiUgbGVmdF9qb2luKGxvbmRvbl9ib3JvdWdocywgYnk9ICJib3JvdWdoIikgIAoKYm9yb3VnaF9qb2luX2MgPC0gYm9yb3VnaF9qb2luICU+JSBtdXRhdGUoCiAgY29sb3IgPSBjYXNlX3doZW4oCiAgICBwY3RkaWZmIDw9IDAgIH4gIi01MCUgdG8gMCUiLAogICAgcGN0ZGlmZiA+IDAgJiBwY3RkaWZmIDw9IDAuNSAgfiAiKzElIHRvICs1MCUiLAogICAgcGN0ZGlmZiA+IDAuNSAmIHBjdGRpZmYgPD0gMSAgfiAiKzUwJSB0byArMTAwJSIsCiAgICBwY3RkaWZmID4gMSAgIH4gIisxMDAlIHRvICsyODAlIiwKICAgIFRSVUUgfiAiT3RoZXJzIgogICkpCgpib3JvdWdoX2pvaW5fYyRjb2xvciA8LSBmY3RfcmVsZXZlbChib3JvdWdoX2pvaW5fYyRjb2xvciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIi01MCUgdG8gMCUiLCIrMSUgdG8gKzUwJSIsIis1MCUgdG8gKzEwMCUiLCIrMTAwJSB0byArMjgwJSIpKQpgYGAKCgpgYGB7cn0KZ2dwbG90KCkgKwogIGdlb21fcG9seWdvbihkYXRhPWJvcm91Z2hfam9pbl9jLCBhZXMoeD14LCB5PXksIGdyb3VwID0gYm9yb3VnaCwgZmlsbCA9IGNvbG9yKSwKICAgICAgICAgICAgICAgY29sb3VyPSJ3aGl0ZSIsc2l6ZT0wLjE1KSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCItNTAlIHRvIDAlIiA9ICIjZmZhNjJiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKzElIHRvICs1MCUiID0gIiM4MmMwY2MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIrNTAlIHRvICsxMDAlIiA9ICIjNDg5ZmI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiKzEwMCUgdG8gKzI4MCUiID0iIzE2Njk3YSIpKSArIAogIGNvb3JkX2ZpeGVkKCkgKyAjIGxvY2sgYXNwZWN0IHJhdGlvCiAgdGhlbWVfdm9pZChiYXNlX3NpemU9OC41KSArIAogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDEsIDMsIDEsIDMpLCAiY20iKSwKICAgICAgICBsZWdlbmQucG9zaXRpb249ImJvdHRvbSIsIAogICAgICAgIHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiLHNpemU9MTAsIGhqdXN0PTAuNSksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgsIGhqdXN0PTAuNSkpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCgKICAgIGxhYmVsLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBjb2xvciA9ICIjODA4MDgwIiwKICAgIGtleXdpZHRoID0gNCwga2V5aGVpZ2h0ID0gMC4zKSkgKyAKICBsYWJzKGZpbGw9IiIsCiAgICAgICB0aXRsZT0iRHdlbGxpbmcgQW5pbWFsIFJlc2N1ZXMgYnkgTEJGIGluIDIwMjAiLAogICAgICAgc3VidGl0bGU9IkFuaW1hbCByZXNjdWUgY291bnQgaW4gMjAyMCBleHByZXNzZWQgYXMgYSBwZXJjZW50YWdlIG9mIDIwMTkgYW5pbWFsIHJlc2N1ZSBjb3VudFxuIikKYGBgCgojIyMgSW5mb3JtYXRpb24gcmV0cmlldmFsCmBgYHtyfQpsaWJyYXJ5KHRpZHl0ZXh0KQpsaWJyYXJ5KFNub3diYWxsQykKYGBgCgpgYGB7cn0KZGltKGFuaW1hbF9yZXNjdWVzKQpuX2Rpc3RpbmN0KGFuaW1hbF9yZXNjdWVzJGluY2lkZW50X251bWJlcikKYW5pbWFsX3Jlc2N1ZXMgJT4lIGRyb3BfbmEoaW5jaWRlbnRfbnVtYmVyKSAlPiUgY291bnQoKSAlPiUgZmxhdHRlbigpCmBgYAoKYGBge3IsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQojIDEwIG1vc3QgY29tbW9uIG5vdW5zIChhbmltYWxfZ3JvdXBfcGFyZW50ID09ICJjYXQiKQogYXJfY2xuICU+JSBkcm9wX25hKGluY2lkZW50X251bWJlcikgJT4lCiAgZmlsdGVyKGFuaW1hbF9ncm91cF9wYXJlbnQ9PSJjYXQiKSAlPiUKICBzZWxlY3QoaW5jaWRlbnRfbnVtYmVyLCBmaW5hbF9kZXNjcmlwdGlvbikgJT4lCiAgdW5uZXN0X3Rva2Vucyh3b3JkLGZpbmFsX2Rlc2NyaXB0aW9uKSAlPiUgI3Rva2VuCiAgZmlsdGVyKHdvcmQhPSJyZWRhY3RlZCIpICU+JSAKICBhbnRpX2pvaW4oZ2V0X3N0b3B3b3JkcygpKSAlPiUgI3JlbW92ZSBzdG9wIHdvcmRzCiAgaW5uZXJfam9pbihwYXJ0c19vZl9zcGVlY2gpICU+JSAjcG9zCiAgbXV0YXRlKHN0ZW09d29yZFN0ZW0od29yZCkpICU+JSAjc3RlbQogIGZpbHRlcihwb3M9PSJOb3VuIikgJT4lIGNvdW50KHN0ZW0sc29ydD1UKSAlPiUKICBzbGljZSgxOjEwKQpgYGAKCmBgYHtyfQojIDEwIG1vc3QgY29tbW9uIG5vdW5zIChhbmltYWxfZ3JvdXBfcGFyZW50ID09ICJkb2ciKQogYXJfY2xuICU+JSBkcm9wX25hKGluY2lkZW50X251bWJlcikgJT4lCiAgZmlsdGVyKGFuaW1hbF9ncm91cF9wYXJlbnQ9PSJkb2ciKSAlPiUKICBzZWxlY3QoaW5jaWRlbnRfbnVtYmVyLCBmaW5hbF9kZXNjcmlwdGlvbikgJT4lCiAgdW5uZXN0X3Rva2Vucyh3b3JkLGZpbmFsX2Rlc2NyaXB0aW9uKSAlPiUgI3Rva2VuCiAgZmlsdGVyKHdvcmQhPSJyZWRhY3RlZCIpICU+JSAKICBhbnRpX2pvaW4oZ2V0X3N0b3B3b3JkcygpKSAlPiUgI3JlbW92ZSBzdG9wIHdvcmRzCiAgaW5uZXJfam9pbihwYXJ0c19vZl9zcGVlY2gpICU+JSAjcG9zCiAgbXV0YXRlKHN0ZW09d29yZFN0ZW0od29yZCkpICU+JSAjc3RlbQogIGZpbHRlcihwb3M9PSJOb3VuIikgJT4lIGNvdW50KHN0ZW0sc29ydD1UKSAlPiUKICBzbGljZSgxOjEwKQpgYGAKCgoKCg==