EDA exercise

Introduction

This notebook uses Germany Cars sample dataset from Zenrow, by way of Kaggle. The dataset contains information of 46405 old and new vehicles that are registered between 2011 and 2021 in Germany, collected on 2021/06/09, scraped from AutoScout24 by Zenrow.

Data dictionary

No. Feature Description
1 mileage kilometres traveled by the vehicle
2 make make of the car
3 model model of the car
4 fuel fuel type
5 gear manual or automatic
6 offerType type of offer (new, used, …)
7 price sale price of the vehicle
8 hp horse power
9 year the vehicle registration year
# load libraries
library(tidyverse, warn.conflicts = F)
library(scales)
library(skimr)
library(janitor)
library(psych)
library(gghalves)
library(ggdist)
library(ggbump)
library(ggstatsplot)
library(gt)
library(ggsci)
library(colorspace)
library(factoextra)

# set theme
theme_set(theme_minimal(base_size = 10))
theme_update(panel.grid.minor=element_blank(),
             plot.title.position="plot",
             axis.title=element_text(size=9),
             legend.title=element_text(size=9),
             plot.margin=ggplot2::margin(1,1,1,1,"cm"))

# suppress summarise info
options(dplyr.summarise.inform = FALSE)
# import data 
de_cars = read_csv("autoscout24-germany-dataset.csv") %>% 
  clean_names() 

── Column specification ─────────────────────────────────────────────────────────────────────────
cols(
  mileage = col_double(),
  make = col_character(),
  model = col_character(),
  fuel = col_character(),
  gear = col_character(),
  offerType = col_character(),
  price = col_double(),
  hp = col_double(),
  year = col_double()
)
glimpse(de_cars)
Rows: 46,405
Columns: 9
$ mileage    <dbl> 235000, 92800, 149300, 96200, 156000, 147000, 91894, 127500, 115000, 104, 59…
$ make       <chr> "BMW", "Volkswagen", "SEAT", "Renault", "Peugeot", "Toyota", "Renault", "Ope…
$ model      <chr> "316", "Golf", "Exeo", "Megane", "308", "Auris", "Scenic", "Zafira", "3", "T…
$ fuel       <chr> "Diesel", "Gasoline", "Gasoline", "Gasoline", "Gasoline", "Electric/Gasoline…
$ gear       <chr> "Manual", "Manual", "Manual", "Manual", "Manual", "Automatic", "Manual", "Ma…
$ offer_type <chr> "Used", "Used", "Used", "Used", "Used", "Used", "Used", "Used", "Used", "Use…
$ price      <dbl> 6800, 6877, 6900, 6950, 6950, 6950, 6970, 6972, 6980, 6990, 6990, 6990, 6990…
$ hp         <dbl> 116, 122, 160, 110, 156, 99, 131, 116, 150, 86, 101, 105, 204, 141, 120, 60,…
$ year       <dbl> 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2011…
# data summary
skim(de_cars)
── Data Summary ────────────────────────
                           Values 
Name                       de_cars
Number of rows             46405  
Number of columns          9      
_______________________           
Column type frequency:            
  character                5      
  numeric                  4      
________________________          
Group variables            None   

── Variable type: character ─────────────────────────────────────────────────────────────────────
  skim_variable n_missing complete_rate   min   max empty n_unique whitespace
1 make                  0         1         2    16     0       77          0
2 model               143         0.997     1    28     0      841          0
3 fuel                  0         1         3    17     0       11          0
4 gear                182         0.996     6    14     0        3          0
5 offer_type            0         1         3    14     0        5          0

── Variable type: numeric ───────────────────────────────────────────────────────────────────────
  skim_variable n_missing complete_rate   mean       sd    p0   p25   p50    p75    p100 hist 
1 mileage               0         1     71178. 62625.       0 19800 60000 105000 1111111 ▇▁▁▁▁
2 price                 0         1     16572. 19305.    1100  7490 10999  19490 1199900 ▇▁▁▁▁
3 hp                   29         0.999   133.    75.4      1    86   116    150     850 ▇▂▁▁▁
4 year                  0         1      2016.     3.16  2011  2013  2016   2019    2021 ▇▅▆▆▆
# missing data 
sapply(de_cars, function(x) sum(is.na(x)))
   mileage       make      model       fuel       gear offer_type      price         hp 
         0          0        143          0        182          0          0         29 
      year 
         0 
# check for duplicates (rows)
# de_cars %>% get_dupes() 

# drop duplicates and NA in hp
cars_cln = de_cars %>% distinct() %>% drop_na(hp)
dim(cars_cln)
[1] 44241     9
# count of observations by registration year
cars_cln %>% count(year)

Horsepower, Mileage and Price

# boxplot (hp, mileage, price)
cars_cln %>% mutate(id=row_number()) %>%
  select(where(is.numeric)) %>%
  select(-year) %>%
  pivot_longer(!id) %>%
  ggplot(aes(y=value)) + 
  geom_half_violin(aes(fill=name, color=name),side="r", nudge = 0.05) + 
  geom_half_boxplot(aes(color=name),outlier.shape = 21, outlier.alpha = 0.6, outlier.size = 1, outlier.fill = NA) +
  #geom_boxplot(outlier.alpha = 0.8, outlier.shape = 21, show.legend = F) + 
  facet_wrap(~name, scales = "free", ncol=1, strip.position = "left") + 
  theme(legend.position="none",
        axis.text.y=element_blank(),
        panel.grid.major.y=element_blank(),
        strip.text.y.left=element_text(angle=0)) + 
  scale_y_continuous(labels=comma_format()) + 
  scale_x_continuous(limits=c(-0.4,0.45)) +
  scale_color_npg() + 
  scale_fill_npg() +
  coord_flip() 

Offer type

# price summary by offer_type
psych::describeBy(cars_cln$price, cars_cln$offer_type, mat=T) %>% arrange(desc(n))
cars_cln %>% group_by(year, offer_type) %>% tally() %>%
  mutate(prop=round(n/sum(n)*100,1)) %>%
  ggplot(aes(x=factor(year), y=offer_type, fill=prop)) + 
  geom_tile(size=2, color="white") + 
  geom_text(aes(label=paste(prop,"%"), color=I(ifelse(prop>90,"white","black"))),size=2.8) +
  scale_x_discrete(position="top") + 
  scale_fill_continuous_sequential(palette="heat") + 
  theme(legend.position="none",
        panel.grid=element_line(size=.2),
        axis.title=element_text(face="bold")) + 
  labs(x="Registration year\n", y="Offer type", subtitle="Proportion of offer type by registration year\n")

# used vehicle subset
used = cars_cln %>% filter(offer_type=="Used")

# count of used vehicle by registration year
used %>% count(year)

Make

# make count 
cars_cln %>% group_by(make) %>% tally(sort=T)
# make count by registration year

# get top 7 makes by count 
top_make = cars_cln %>% filter(year<2021) %>% 
  group_by(make) %>% tally(sort=T) %>% slice(1:7)

make = cars_cln %>% filter(year<2021) %>%
  filter(make %in% top_make$make) %>%
  group_by(year,make) %>% tally() 

# plot
make %>%
  ggplot(aes(year, n, color=make)) + 
  geom_bump() + 
  geom_point(size=2) +
  geom_text(data = make %>% filter(year==2020), aes(label=make, x=2020.2),size=3, hjust=0) +
  scale_color_d3() +
  scale_x_continuous(limits=c(2011,2021)) + 
  theme(legend.position = "none",
        plot.title=element_text(size=10),
        plot.subtitle=element_text(size=8),
        axis.title = element_text(color="grey30")) + 
  labs(x="Registration year", y="Vehicle count",
       title="Count of vehicle make by registration year",
       subtitle="7 makes with highest vehicle count from 2011 to 2020")

Proportion of make in 2020

# sum of cars in 2020
cars_cln %>% filter(year==2020) %>% count()

# proportion of car makes in 2020
make_sum = cars_cln %>% filter(year==2020) %>%
  mutate(make_grp = fct_lump(factor(make), 10)) %>%
  count(make_grp, sort=T) %>%
  mutate(make_grp = fct_rev(fct_inorder(make_grp)),
         make_grp=fct_relevel(make_grp,"Other",after=0),
         perc= paste0(sprintf("%2.1f", n/sum(n)*100),"%"),
         color=case_when(row_number()==1 ~"grey",
                         row_number()==2 ~"#ee9b00",
                         row_number()==3 ~"#4DBBD5FF",
                         row_number()==4 ~"#00A087FF",
                         TRUE~"grey55")
         ) 

 ggplot(make_sum, aes(n, make_grp, fill=color)) + 
  geom_col(width=0.8) + 
  geom_text(aes(label=perc), hjust=1.2, nudge_x = -.5, size=3.2, fontface="bold") +
  geom_text(aes(x=-10,label=make_grp, color=color), 
            size=3.5, hjust=1, nudge_x=-0.5, fontface="bold") +
  scale_x_continuous(expand = expansion(mult = c(.2,.1))) +
  scale_fill_identity(guide="none") +
  scale_color_identity(guide="none") +
  theme_void(base_size=10) + 
  theme(plot.title=element_text(hjust=0.03),
        plot.subtitle=element_text(hjust=0.03, color="grey55",size=10),
        plot.margin=ggplot2::margin(1,1,1,1,"cm")
        ) +
  labs(title="Proportion of car makes in 2020", subtitle = "(4118 new and old cars)\n")

Model

# most popular car by registration year 
cars_cln %>% 
  filter(year<2021) %>%
  group_by(year) %>%
  count(make, model, sort=T) %>%
  slice(1)
# Volkswagen golf price by registration year
cars_cln %>% 
  filter(year<2021) %>%
  filter(model=="Golf") %>%
  ggplot(aes(x=factor(year), y=price)) + 
  ggdist::stat_halfeye(width = .6, .width = 0, justification = -.2, point_colour = NA, alpha=0.7) +
  geom_boxplot(width = .1, outlier.shape = NA, color="#f77f00", fill=NA) + 
  gghalves::geom_half_point(side = "l", range_scale = .5, alpha = .3,size=.9, shape=21) +
  theme(panel.grid.major.y=element_blank()) + 
  coord_flip() + 
  theme(legend.position="none",
        axis.title.y=element_text(margin=margin(r=5)),
        axis.title.x=element_text(margin=margin(t=5))) + 
  labs(x="Price", y="Registration year", subtitle="Volkswagen golf price by registration year")

All cars: median price by make and registration year

medprice = cars_cln %>% 
  mutate(make_grp = fct_lump(factor(make), 10)) %>%
  group_by(make_grp,year) %>% 
  summarise(median_price = median(price)) %>% 
  ungroup() %>% 
  group_by(make_grp) %>% 
  mutate(max_price = max(median_price)) %>%
  arrange(max_price) %>%
  ungroup() %>%
  mutate(make_grp=fct_inorder(make_grp),
         make_grp=fct_relevel(make_grp,"Other",after=0))

medprice %>%
  ggplot(aes(y=make_grp, x=median_price, color=year)) + 
  geom_line(aes(group=make_grp),color="grey") +
  geom_point() + 
  scale_color_continuous_sequential(palette="batlow") + 
  scale_x_continuous(breaks=seq(5000,60000,10000), expand=c(.01,.01),
                     labels=scales::comma_format()) + 
  theme(legend.position="top",
        panel.grid.major.y=element_blank(),
        panel.grid.major.x=element_line(size=.3),
        plot.margin=ggplot2::margin(.5,.5,.5,.5,"cm"))  + 
  guides(color = guide_colorbar(title.position = "top", 
                                title.hjust = .5, 
                                barwidth = unit(20, "lines"), 
                                barheight = unit(.3, "lines"))) + 
  labs(color="Registration year", subtitle="Median price by make and registration year",
       x="Median Price",y="Make")

Gear

# price summary by gear
psych::describeBy(cars_cln$price, cars_cln$gear, mat=T, ) %>% arrange(desc(n))
# proportion of manual and automatic gear 2011-2020
cars_cln %>% 
  filter(gear=="Manual"| gear=="Automatic") %>%
  filter(year<2021) %>%
  group_by(year, gear) %>% tally() %>%
  mutate(prop=n/sum(n)) %>%
  ggplot(aes(y=fct_rev(factor(year)), x=prop, fill=fct_rev(gear))) + 
  geom_col(alpha=0.9,width=0.6) + 
  theme(panel.grid.major.y=element_blank()) + 
  theme(axis.title=element_blank(),
        plot.margin=ggplot2::margin(1,2,1,2,"cm"),
        legend.position = "top",
        legend.justification = "left",
        legend.margin = margin(l=3, t=3.5,b=0),
        legend.text = element_text(size=9),
        legend.title=element_blank()) + 
  scale_fill_npg() + 
  scale_x_continuous(expand=c(.01,.01), labels=percent_format(), position="top") + 
  guides(fill=guide_legend(keyheight=0.8,keywidth=0.4, reverse=T)) + 
  ggtitle("Porportion of manual and automatic cars, 2011 to 2020")

# median price by gear (manual, automatic) and year
gear_agg = cars_cln %>% filter(gear=="Manual"| gear=="Automatic") %>%
  group_by(gear, year) %>% 
  summarise(med = median(price)) %>%
  ungroup() %>%
  group_by(gear) %>%
  mutate(current = med[which(year == 2021)])
  

gear_agg %>%  
  ggplot(aes(x=gear, y=med)) + 
   geom_col(
    aes(color = factor(year), fill = I(ifelse(year==2021,"#0077b6","grey"))),
    position = position_dodge(), size = .001
  )  + 
  theme(legend.position="none",
        panel.grid=element_line(size=.3),
        axis.text.y=element_blank(),
        axis.title.y=element_blank(),
        axis.text.x.bottom = element_blank(),
        axis.title.x.bottom = element_blank(),
        panel.grid.major.y=element_blank()) + 
  geom_text(
    data = gear_agg %>% filter(gear == "Manual") %>% 
      mutate(year_lab = if_else(year %in% c(2011, 2016, 2021), as.character(year), "•")),
    aes(y = 0, label = year_lab, color = factor(year), size = year_lab == "•"),
    position = position_dodge(width = .9), 
    hjust = 2
  ) + 
  geom_text(
    data = gear_agg %>% filter(gear == "Automatic") %>% 
      mutate(year_lab = if_else(year %in% c(2011, 2016, 2021), as.character(year), "•")),
    aes(y = 0, label = year_lab, color = factor(year), size = year_lab == "•"),
    position = position_dodge(width = .9), 
    hjust = 2
  ) + 
  scale_color_manual(values = c("grey50","grey50","grey50","grey50","grey50",
                              "grey50","grey50","grey50","grey50","grey50","#0077b6")) + 
  coord_flip() + 
  scale_y_continuous(label=scales::comma_format(),limits=c(-4000,38000),
                     sec.axis = dup_axis(name = "Median price"),
                     breaks=seq(0,30000,10000))  + 
  annotate("text", y=-3500, x=1.15, label="Automatic", hjust=1,size=3, angle=90) + 
  annotate("text", y=-3500, x=2.15, label="Manual", hjust=1,size=3, angle=90) + 
  labs(subtitle="Median price by registration year and gear")

Fuel

# price summary by fuel
psych::describeBy(cars_cln$price, cars_cln$fuel, mat=T) %>% arrange(desc(n))

# median price by fuel and registration year
cars_cln %>% mutate(fuel_grp = fct_lump(factor(fuel), 2)) %>%
  group_by(fuel_grp, year) %>% 
  summarise(med = median(price)) %>% 
  ggplot(aes(y=factor(year),x=med, 
             fill=factor(fuel_grp, levels=c("Gasoline","Other","Diesel", ordered=T)))) + 
  geom_line(aes(group=year), color="grey") +
  geom_point(size=2.5, alpha=0.75, shape=21, color="black") + 
  scale_fill_d3() +
  scale_x_continuous(labels=scales::comma_format(),
                     limits=c(5000,42000),
                     breaks=seq(5000,42000,10000)) +
  theme(legend.position = "top",
        panel.grid=element_line(size=.3),
        axis.title.y=element_text(margin=margin(r=10)),
        axis.title.x=element_text(margin=margin(t=8))) + 
  labs(fill="Fuel type",x="Median price", y="Registration Year",
       subtitle="Median price by fuel and registration year")

Price

# price and registration year (2011-2020)
cars_cln %>% 
  filter(year<2021) %>%
  ggplot(aes(x=factor(year), y=price, color=fct_rev(factor(year)))) +
  geom_half_violin(size=0.5, alpha=0.5, show.legend=F, side="r") +
  geom_half_point(size=1, alpha=0.5, show.legend=F, side="l") +
  theme(panel.grid.major.y=element_blank(),
        plot.margin=ggplot2::margin(.1,.5,.1,.1,"cm")) + 
  scale_y_continuous(labels = unit_format(unit = "K", scale = 1e-3))+
  labs(subtitle="Price and registration year\n",y="Price",x="Year") + 
  scale_color_futurama() + 
  coord_flip()

# boxplot (without outliers)

# filtering function - turns outliers into NAs to be removed
filter_lims <- function(x){
  l <- boxplot.stats(x)$stats[1]
  u <- boxplot.stats(x)$stats[5]

  for (i in 1:length(x)){
    x[i] <- ifelse(x[i]>l & x[i]<u, x[i], NA)
  }
  return(x)
}

# plot
cars_cln %>% filter(year<2021) %>% 
  group_by(year) %>% 
  mutate(price2= filter_lims(price)) %>%
  ggplot(aes(x=factor(year), y=price2, color=fct_rev(factor(year)))) + 
  geom_boxplot(na.rm = TRUE, coef = 5) +
  scale_y_continuous(labels = scales::comma_format())+
  theme(panel.grid.major.y=element_blank(),
        legend.position="none") +
  coord_flip() + 
  scale_color_futurama() + 
  labs(x="Year",y="Price", subtitle="Boxplot without outliers: price and registration year\n") 

# proportion of price groups by registration year
summary(cars_cln$price)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   1100    7490   10990   16554   19490 1199900 
cars2 = cars_cln %>% filter(year<2021) 
cars2$price_group = cut(cars2$price, breaks=c(1100,7490,10990,19490,1199900),
                labels=c("1,100 to 7,490","7,490 to 10,990","10,990 to 19,490","19,490 to 119,900"),
                include.lowest = T)

cars2 %>% count(price_group) 

cars2 %>% group_by(year, price_group) %>% tally() %>%
  mutate(prop=n/sum(n)) %>%
  ggplot(aes(x=factor(year), y=prop, fill=price_group)) + 
  geom_col(width=0.8, alpha=0.9) +
  coord_flip() + 
  theme(legend.position="top") + 
  scale_fill_npg(guide=guide_legend(reverse=T)) + 
  labs(fill="Price group",y="Proportion", x="Registration year",
       subtitle="Proportion of price group by registration year")

Used vehicle price

# used vehicle prices by registration year
used = cars_cln %>% filter(offer_type=="Used") %>% filter(year<2021)
used_table = psych::describeBy(used_2021$price, used_2021$year, mat=T)

used_table %>% select(group1, mean, sd, median, min, max, range, skew) %>%
  rename(year = group1) %>%
  gt() %>%
  fmt_number(
    columns = c("mean","sd","median","min","max","range","skew"),
    decimals=0) %>%
  data_color(
    columns =c("mean","sd","median","min","max","range","skew"),
    colors = scales::col_numeric(
      palette = c("#ffffff", "#f2fbd2", "#c9ecb4", "#93d3ab", "#35b0ab"),
      domain = NULL)) %>%
  tab_style(
    style = list(
      cell_borders(
        sides = "bottom",
        color = "black",
        weight = px(3)
      )
    ),
    locations = list(
      cells_column_labels(
        columns = gt::everything()
      )
    )
  ) %>%
  tab_options(table.font.size=13) %>%
  tab_header(title=md("**Used Vehicle Prices by Registration Year**"))
Used Vehicle Prices by Registration Year
year mean sd median min max range skew
2011 7,066 6,738 5,975 1,100 220,000 218,900 14
2012 8,273 7,218 6,900 1,250 149,000 147,750 10
2013 9,288 9,011 7,900 1,900 295,000 293,100 18
2014 9,968 9,271 8,497 1,500 349,000 347,500 20
2015 11,170 13,103 9,000 1,832 465,000 463,168 21
2016 12,741 12,664 9,900 3,000 399,999 396,999 13
2017 18,370 24,588 12,948 2,300 1,199,900 1,197,600 29
2018 22,019 19,385 17,410 3,979 499,800 495,821 8
2019 24,632 25,773 18,770 2,450 717,078 714,628 13
2020 30,129 22,400 24,723 2,499 349,000 346,501 5

Mileage

# mileage and registration year
cars_cln %>% filter(year<2021) %>%
  ggplot(aes(x=factor(year), y=mileage)) + 
  geom_half_boxplot(width=.5, outlier.color = NA) + 
  geom_half_point(side="r", range_scale=.5, alpha=0.3, size=1, color="#3C5488FF") + 
  scale_y_continuous(labels = scales::comma_format()) + 
  theme(panel.grid.major.x=element_blank(),
        axis.title.y.left = element_text(margin=margin(r=5)),
        axis.title=element_text(size=9, face="bold", color="grey50")) +
  labs(x="Year",y="Mileage (in kilometers)",subtitle="Mileage and registration year") 

# median mileage and registration year
cars_cln %>% filter(year<2021) %>%
  filter(offer_type=="Used") %>%
  group_by(year) %>% 
  summarise(median_mileage = median(mileage)) %>%
  ggplot(aes(y=fct_rev(factor(year)), x=median_mileage)) + 
  geom_col(aes(fill=I(if_else(year==2011,"#3C5488FF","grey70"))),width=0.7, show.legend=F) + 
  geom_vline(color="white",xintercept=c(seq(0,100000,25000)), size=0.4) +
  scale_x_continuous(limits=c(0,125000), expand=c(0,0), position = "top",
                     breaks=seq(0,100000,25000)) +
  #scale_fill_gradientn(colours = wes_palette("Zissou1", 10, type = "continuous"), trans="reverse") +
  theme_light(base_size=10) +
  theme(panel.grid.major.x=element_blank(),
        axis.title=element_text(size=9, face="bold", color="grey50"),
        axis.title.y.left = element_text(margin=margin(r=5)),
        axis.text.x.top = element_text(margin=margin(b=5,t=-5), vjust=-1),
        axis.ticks.y=element_blank(),
        panel.border = element_blank(),
        panel.grid.minor = element_blank(),
        axis.ticks.length=unit(.2, "cm"),
        plot.margin=ggplot2::margin(1,1,1,1,"cm"),
        plot.title.position = "plot"
        ) +
  labs(y="Year",x="Median Mileage\n", subtitle = "Used vehicles: Median mileage by registration year\n")

hp

# hp and price
cars_cln %>% filter(year<2021) %>%
  ggplot(aes(x=factor(year), y=hp)) + 
  geom_half_boxplot(width=.5, outlier.color = NA) + 
  geom_half_point(side="r", range_scale=.5, alpha=0.2, size=1, color="#E64B35FF") + 
  theme(panel.grid.major.x=element_blank(),
        axis.title.y.left = element_text(margin=margin(r=5)),
        axis.title=element_text(size=9, face="bold", color="grey50")) +
  labs(x="Year",y="Horsepower",subtitle="Horsepower and registration year") 

Correlation

cars_cln2 =  cars_cln %>% mutate(id=row_number())
#used cars with registration year from 2011 to 2020
cars_n = cars_cln %>% 
  filter(offer_type=="Used") %>%
  filter(year<2021) %>%
  select(year, price, mileage, hp) %>%
  drop_na()

# correlation
set.seed(123)
ggcorrmat(
  data=cars_n,
  cor.vars=c(year:hp),
  title="Correlation",
)

cars_n2 = cars_num %>% 
  # get zscore
  mutate(zscore_p =(price- mean(price))/ sd(price),
         zscore_m =(mileage- mean(mileage))/ sd(mileage),
         zscore_hp =(hp- mean(hp))/ sd(hp)) %>%
  # drop outliers
  filter(between(zscore_p,-3,3)) %>%
  filter(between(zscore_m,-3,3)) %>%
  filter(between(zscore_hp,-3,3)) %>%
  # select variables
  select(year, price, mileage,hp)

dim(cars_n)
[1] 38285     4
dim(cars_n2)
[1] 37028     4
# correlation after dropping outliers
set.seed(123)
ggcorrmat(
  data=cars_n2,
  cor.vars=c(year:hp),
  title="Correlation",
)

Clustering

# used cars registered between 2017 to 2019
used2 = cars_cln %>% 
  filter(offer_type=="Used") %>% 
  filter(between(year, 2017,2019)) %>%
  drop_na(year, price, mileage,hp)
# drop outliers
used2b = used2 %>%
  mutate(zscore_p =(price- mean(price))/ sd(price),
         zscore_m =(mileage- mean(mileage))/ sd(mileage),
         zscore_hp =(hp- mean(hp))/ sd(hp)) %>%
  filter(between(zscore_p,-3,3)) %>%
  filter(between(zscore_m,-3,3)) %>%
  filter(between(zscore_hp,-3,3)) %>%
  select(-zscore_p,-zscore_m,-zscore_hp,-offer_type)

dim(used2)
[1] 11813     9
dim(used2b)
[1] 11334     8
used3 = used2b %>%  select(price, mileage,hp)

# scale 
used3_scaled = scale(used3)
#hierarchical clustering
set.seed(1234)
hc= hclust(dist(used3_scaled))
plot(hc)

# check optimal clusters: elbow method 
set.seed(123)
fviz_nbclust(used3_scaled,kmeans,method="wss")

#k means: 4 clusters
set.seed(1234)
k4= kmeans(used3_scaled,centers=4,nstart=50)
k4
K-means clustering with 4 clusters of sizes 1855, 1288, 3005, 5186

Cluster means:
       price     mileage          hp
1 -0.3570169  1.73860167 -0.03468078
2  1.9694059  0.02292972  2.05642370
3  0.5368099 -0.30873698  0.43343275
4 -0.6724724 -0.44868588 -0.74948058

Clustering vector:
   [1] 3 3 3 2 2 4 4 4 1 4 4 1 4 4 4 4 1 4 4 2 3 3 3 3 2 2 2 2 3 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3
  [46] 3 3 3 3 3 3 3 3 4 4 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 3 3 3 2 4 4 1 4 4 1
  [91] 4 2 2 2 2 2 2 2 2 2 2 3 3 2 3 2 3 3 2 3 2 2 2 2 3 2 2 3 2 3 3 2 1 3 2 3 3 2 3 2 3 3 2 2 2
 [136] 2 3 2 3 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 4 4 1 4 1 4 1 4 4 1 4 4 3 4 4 3 3 3 4 2 2
 [181] 2 2 2 2 2 2 2 2 2 2 2 1 4 4 4 4 4 4 4 4 1 4 4 4 1 1 4 1 4 4 4 1 4 1 4 4 1 1 4 4 4 4 4 4 4
 [226] 4 4 4 4 1 4 4 4 4 1 4 4 3 3 3 3 3 1 3 1 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 1 4 3 4 1 3 3 3 4
 [271] 3 4 4 4 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
 [316] 4 4 4 4 1 4 4 4 4 4 4 4 4 4 1 4 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 1 4 1 1 4 4 1 1
 [361] 4 4 1 1 1 4 4 3 4 1 3 2 2 3 1 3 3 2 2 2 3 2 2 2 2 2 2 3 3 3 1 3 3 3 3 3 3 3 3 2 3 3 2 2 2
 [406] 4 1 1 4 4 4 4 4 1 4 4 4 4 1 1 4 4 4 1 2 2 2 2 2 2 4 4 4 1 4 4 4 4 1 4 4 3 3 3 2 3 3 3 3 2
 [451] 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 1 4 4 4 4 4 4 4 4 4
 [496] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 3 4 2 3 2 2 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 2 2 3 2 2 2 3
 [541] 2 2 2 2 2 2 2 2 2 3 3 3 3 3 1 3 3 3 3 3 3 3 3 2 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1
 [586] 3 1 3 3 1 3 1 3 3 1 1 1 1 3 3 3 3 1 4 4 4 4 4 4 1 4 4 4 4 4 4 4 3 3 1 1 4 4 1 1 1 1 4 4 1
 [631] 1 4 4 1 4 1 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 3 3 1 2 3 2 3 2 3 2 1 3 3 2 3 3 3 3 2 1 1
 [676] 3 1 1 3 1 3 3 3 3 3 3 3 3 3 3 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 3 3 1 4 4 4 4 4 4 4
 [721] 4 4 1 4 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 1 4 4 1 1 1 4 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
 [766] 4 4 4 4 4 3 3 3 2 3 3 2 4 1 4 4 4 4 1 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 3 2 3 2 3
 [811] 3 3 2 2 3 2 3 3 2 2 2 2 4 4 1 4 4 1 1 1 4 1 4 4 4 4 4 4 1 4 4 4 3 2 2 3 3 2 4 4 1 1 1 4 4
 [856] 4 4 4 4 3 2 3 2 3 3 2 2 2 3 2 3 2 2 2 2 2 2 4 4 4 4 4 1 4 4 4 4 4 4 1 1 4 1 4 4 1 1 4 4 1
 [901] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 3 4 3 4 3 3 3 3 3 4 3 3 3 3 3 3 3 4 1 4 4 4 4 1 4 1 4 4 4
 [946] 2 2 2 2 2 2 2 2 2 2 2 4 4 4 3 3 3 3 3 3 3 3 2 3 2 3 2 3 2 2 3 3 3 4 1 4 4 4 4 4 4 4 4 1 4
 [991] 4 4 4 1 1 4 4 4 3 3
 [ reached getOption("max.print") -- omitted 10334 entries ]

Within cluster sum of squares by cluster:
[1] 2208.972 2777.538 2287.290 2417.063
 (between_SS / total_SS =  71.5 %)

Available components:

[1] "cluster"      "centers"      "totss"        "withinss"     "tot.withinss" "betweenss"   
[7] "size"         "iter"         "ifault"      
fviz_cluster(k4, data=used3_scaled, labelsize=0) #cluster plot

with(used3,pairs(used3_scaled,col=(1:4)[k4$cluster])) #pair plot

colnames(used2b)
[1] "mileage"    "make"       "model"      "fuel"       "gear"       "offer_type" "price"     
[8] "hp"         "year"      
# summary by cluster id 
used2c = used2b %>% mutate_at(vars(make, model, fuel, gear, year), list(factor))

used2c$clustid=as.factor(k4$cluster)
by(used2c,used2c$clustid,summary)
used2c$clustid: 1
    mileage               make         model                     fuel                  gear     
 Min.   : 65197   Volkswagen:339   Astra  : 187   Diesel           :1443   Automatic     : 833  
 1st Qu.: 86826   Opel      :306   Focus  : 101   Gasoline         : 384   Manual        :1014  
 Median :102434   Ford      :244   Golf   :  94   Electric/Gasoline:  17   Semi-automatic:   2  
 Mean   :108217   Skoda     :205   Octavia:  82   LPG              :   7   NA's          :   6  
 3rd Qu.:126521   Audi      :128   Tiguan :  44   Electric         :   2                        
 Max.   :171423   BMW       :122   (Other):1343   -/- (Fuel)       :   1                        
                  (Other)   :511   NA's   :   4   (Other)          :   1                        
     price             hp          year      clustid 
 Min.   : 2300   Min.   : 58.0   2017:1071   1:1855  
 1st Qu.: 9850   1st Qu.:110.0   2018: 675   2:   0  
 Median :13950   Median :136.0   2019: 109   3:   0  
 Mean   :15392   Mean   :138.6               4:   0  
 3rd Qu.:19950   3rd Qu.:160.0                       
 Max.   :44990   Max.   :313.0                       
                                                     
------------------------------------------------------------------------ 
used2c$clustid: 2
    mileage                  make         model                    fuel    
 Min.   :    10   Audi         :302   XC90   : 88   Diesel           :691  
 1st Qu.: 25583   BMW          :271   A6     : 79   Gasoline         :551  
 Median : 43766   Volvo        :205   530    : 65   Electric/Gasoline: 30  
 Mean   : 47934   Mercedes-Benz:158   XC60   : 40   Electric         :  7  
 3rd Qu.: 66115   Volkswagen   :128   Touareg: 36   Electric/Diesel  :  7  
 Max.   :160000   Land         : 41   (Other):976   LPG              :  2  
                  (Other)      :183   NA's   :  4   (Other)          :  0  
             gear          price             hp          year     clustid 
 Automatic     :1268   Min.   :23550   Min.   :140.0   2017:363   1:   0  
 Manual        :  19   1st Qu.:34999   1st Qu.:250.0   2018:519   2:1288  
 Semi-automatic:   0   Median :39980   Median :272.0   2019:406   3:   0  
 NA's          :   1   Mean   :43204   Mean   :284.7              4:   0  
                       3rd Qu.:47990   3rd Qu.:320.0                      
                       Max.   :89900   Max.   :417.0                      
                                                                          
------------------------------------------------------------------------ 
used2c$clustid: 3
    mileage                 make         model                     fuel     
 Min.   :   10   Volkswagen   :547   Tiguan : 138   Diesel           :1450  
 1st Qu.:19490   Audi         :427   A4     : 117   Gasoline         :1412  
 Median :33719   BMW          :352   Golf   :  94   Electric/Gasoline:  96  
 Mean   :36281   Ford         :282   Kuga   :  94   Electric         :  28  
 3rd Qu.:52300   Mercedes-Benz:267   A3     :  64   CNG              :   8  
 Max.   :90450   Volvo        :165   (Other):2490   Electric/Diesel  :   5  
                 (Other)      :965   NA's   :   8   (Other)          :   6  
             gear          price             hp          year      clustid 
 Automatic     :2272   Min.   :14330   Min.   : 68.0   2017: 724   1:   0  
 Manual        : 727   1st Qu.:21980   1st Qu.:150.0   2018:1043   2:   0  
 Semi-automatic:   0   Median :25350   Median :170.0   2019:1238   3:3005  
 NA's          :   6   Mean   :26078   Mean   :171.3               4:   0  
                       3rd Qu.:29490   3rd Qu.:190.0                       
                       Max.   :55900   Max.   :288.0                       
                                                                           
------------------------------------------------------------------------ 
used2c$clustid: 4
    mileage              make          model                     fuel                  gear     
 Min.   :    0   Opel      : 827   Fiesta : 398   Gasoline         :4438   Automatic     : 775  
 1st Qu.:16436   Volkswagen: 742   Corsa  : 362   Diesel           : 490   Manual        :4381  
 Median :28300   Ford      : 641   up!    : 349   Electric         : 158   Semi-automatic:   2  
 Mean   :31363   Renault   : 431   Polo   : 197   Electric/Gasoline:  61   NA's          :  28  
 3rd Qu.:44164   smart     : 305   forTwo : 191   CNG              :  18                        
 Max.   :80400   Fiat      : 274   (Other):3661   Others           :  13                        
                 (Other)   :1966   NA's   :  28   (Other)          :   8                        
     price             hp           year      clustid 
 Min.   : 2450   Min.   :  1.00   2017:1701   1:   0  
 1st Qu.: 8990   1st Qu.: 71.00   2018:1637   2:   0  
 Median :10980   Median : 86.00   2019:1848   3:   0  
 Mean   :11621   Mean   : 88.65               4:5186  
 3rd Qu.:13795   3rd Qu.:102.00                       
 Max.   :23820   Max.   :150.00                       
                                                      
LS0tCnRpdGxlOiAiR2VybWFueSBDYXJzIgpkYXRlOiAiMjAyMS8wNy8wNyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIEVEQSBleGVyY2lzZQoKKipJbnRyb2R1Y3Rpb24qKiAgCgpUaGlzIG5vdGVib29rIHVzZXMgW0dlcm1hbnkgQ2Fyc10oaHR0cHM6Ly93d3cuemVucm93cy5jb20vZGF0YXNldHMvZ2VybWFueS1jYXJzKSBzYW1wbGUgZGF0YXNldCBmcm9tIFtaZW5yb3ddKGh0dHBzOi8vd3d3LnplbnJvd3MuY29tL2RhdGFzZXRzLyksIGJ5IHdheSBvZiBbS2FnZ2xlXShodHRwczovL3d3dy5rYWdnbGUuY29tL2FuZGVyMjg5Mzg2L2NhcnMtZ2VybWFueSkuIFRoZSBkYXRhc2V0IGNvbnRhaW5zIGluZm9ybWF0aW9uIG9mIDQ2NDA1IG9sZCBhbmQgbmV3IHZlaGljbGVzIHRoYXQgYXJlIHJlZ2lzdGVyZWQgYmV0d2VlbiAyMDExIGFuZCAyMDIxIGluIEdlcm1hbnksIGNvbGxlY3RlZCBvbiAyMDIxLzA2LzA5LCBzY3JhcGVkIGZyb20gQXV0b1Njb3V0MjQgYnkgWmVucm93LiAKCioqRGF0YSBkaWN0aW9uYXJ5KioKCnwgTm8uIHwgRmVhdHVyZSAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICB8CnwtLS0tLXwtLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18CnwgMSAgIHwgbWlsZWFnZSAgIHwga2lsb21ldHJlcyB0cmF2ZWxlZCBieSB0aGUgdmVoaWNsZSB8CnwgMiAgIHwgbWFrZSAgICAgIHwgbWFrZSBvZiB0aGUgY2FyICAgICAgICAgICAgICAgICAgICB8CnwgMyAgIHwgbW9kZWwgICAgIHwgbW9kZWwgb2YgdGhlIGNhciAgICAgICAgICAgICAgICAgICB8CnwgNCAgIHwgZnVlbCAgICAgIHwgZnVlbCB0eXBlICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgNSAgIHwgZ2VhciAgICAgIHwgbWFudWFsIG9yIGF1dG9tYXRpYyAgICAgICAgICAgICAgICB8CnwgNiAgIHwgb2ZmZXJUeXBlIHwgdHlwZSBvZiBvZmZlciAobmV3LCB1c2VkLCAuLi4pICAgICB8CnwgNyAgIHwgcHJpY2UgICAgIHwgc2FsZSBwcmljZSBvZiB0aGUgdmVoaWNsZSAgICAgICAgICB8CnwgOCAgIHwgaHAgICAgICAgIHwgaG9yc2UgcG93ZXIgICAgICAgICAgICAgICAgICAgICAgICB8CnwgOSAgIHwgeWVhciAgICAgIHwgdGhlIHZlaGljbGUgcmVnaXN0cmF0aW9uIHllYXIgICAgICB8CgoKYGBge3IsIG1lc3NhZ2U9Rn0KIyBsb2FkIGxpYnJhcmllcwpsaWJyYXJ5KHRpZHl2ZXJzZSwgd2Fybi5jb25mbGljdHMgPSBGKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShza2ltcikKbGlicmFyeShqYW5pdG9yKQpsaWJyYXJ5KHBzeWNoKQpsaWJyYXJ5KGdnaGFsdmVzKQpsaWJyYXJ5KGdnZGlzdCkKbGlicmFyeShnZ2J1bXApCmxpYnJhcnkoZ2dzdGF0c3Bsb3QpCmxpYnJhcnkoZ3QpCmxpYnJhcnkoZ2dzY2kpCmxpYnJhcnkoY29sb3JzcGFjZSkKbGlicmFyeShmYWN0b2V4dHJhKQoKIyBzZXQgdGhlbWUKdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTApKQp0aGVtZV91cGRhdGUocGFuZWwuZ3JpZC5taW5vcj1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICBwbG90LnRpdGxlLnBvc2l0aW9uPSJwbG90IiwKICAgICAgICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OSksCiAgICAgICAgICAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OSksCiAgICAgICAgICAgICBwbG90Lm1hcmdpbj1nZ3Bsb3QyOjptYXJnaW4oMSwxLDEsMSwiY20iKSkKCiMgc3VwcHJlc3Mgc3VtbWFyaXNlIGluZm8Kb3B0aW9ucyhkcGx5ci5zdW1tYXJpc2UuaW5mb3JtID0gRkFMU0UpCmBgYAoKCmBgYHtyfQojIGltcG9ydCBkYXRhIApkZV9jYXJzID0gcmVhZF9jc3YoImF1dG9zY291dDI0LWdlcm1hbnktZGF0YXNldC5jc3YiKSAlPiUgCiAgY2xlYW5fbmFtZXMoKSAKZ2xpbXBzZShkZV9jYXJzKQpgYGAKCgpgYGB7cn0KIyBkYXRhIHN1bW1hcnkKc2tpbShkZV9jYXJzKQpgYGAKCmBgYHtyfQojIG1pc3NpbmcgZGF0YSAKc2FwcGx5KGRlX2NhcnMsIGZ1bmN0aW9uKHgpIHN1bShpcy5uYSh4KSkpCmBgYAoKYGBge3J9CiMgY2hlY2sgZm9yIGR1cGxpY2F0ZXMgKHJvd3MpCiMgZGVfY2FycyAlPiUgZ2V0X2R1cGVzKCkgCgojIGRyb3AgZHVwbGljYXRlcyBhbmQgTkEgaW4gaHAKY2Fyc19jbG4gPSBkZV9jYXJzICU+JSBkaXN0aW5jdCgpICU+JSBkcm9wX25hKGhwKQpkaW0oY2Fyc19jbG4pCmBgYAoKYGBge3J9CiMgY291bnQgb2Ygb2JzZXJ2YXRpb25zIGJ5IHJlZ2lzdHJhdGlvbiB5ZWFyCmNhcnNfY2xuICU+JSBjb3VudCh5ZWFyKQpgYGAKCgoKIyMjIEhvcnNlcG93ZXIsIE1pbGVhZ2UgYW5kIFByaWNlCmBgYHtyfQojIGJveHBsb3QgKGhwLCBtaWxlYWdlLCBwcmljZSkKY2Fyc19jbG4gJT4lIG11dGF0ZShpZD1yb3dfbnVtYmVyKCkpICU+JQogIHNlbGVjdCh3aGVyZShpcy5udW1lcmljKSkgJT4lCiAgc2VsZWN0KC15ZWFyKSAlPiUKICBwaXZvdF9sb25nZXIoIWlkKSAlPiUKICBnZ3Bsb3QoYWVzKHk9dmFsdWUpKSArIAogIGdlb21faGFsZl92aW9saW4oYWVzKGZpbGw9bmFtZSwgY29sb3I9bmFtZSksc2lkZT0iciIsIG51ZGdlID0gMC4wNSkgKyAKICBnZW9tX2hhbGZfYm94cGxvdChhZXMoY29sb3I9bmFtZSksb3V0bGllci5zaGFwZSA9IDIxLCBvdXRsaWVyLmFscGhhID0gMC42LCBvdXRsaWVyLnNpemUgPSAxLCBvdXRsaWVyLmZpbGwgPSBOQSkgKwogICNnZW9tX2JveHBsb3Qob3V0bGllci5hbHBoYSA9IDAuOCwgb3V0bGllci5zaGFwZSA9IDIxLCBzaG93LmxlZ2VuZCA9IEYpICsgCiAgZmFjZXRfd3JhcCh+bmFtZSwgc2NhbGVzID0gImZyZWUiLCBuY29sPTEsIHN0cmlwLnBvc2l0aW9uID0gImxlZnQiKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgc3RyaXAudGV4dC55LmxlZnQ9ZWxlbWVudF90ZXh0KGFuZ2xlPTApKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWFfZm9ybWF0KCkpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cz1jKC0wLjQsMC40NSkpICsKICBzY2FsZV9jb2xvcl9ucGcoKSArIAogIHNjYWxlX2ZpbGxfbnBnKCkgKwogIGNvb3JkX2ZsaXAoKSAKYGBgCgojIyMgT2ZmZXIgdHlwZQpgYGB7cn0KIyBwcmljZSBzdW1tYXJ5IGJ5IG9mZmVyX3R5cGUKcHN5Y2g6OmRlc2NyaWJlQnkoY2Fyc19jbG4kcHJpY2UsIGNhcnNfY2xuJG9mZmVyX3R5cGUsIG1hdD1UKSAlPiUgYXJyYW5nZShkZXNjKG4pKQpgYGAKCmBgYHtyfQpjYXJzX2NsbiAlPiUgZ3JvdXBfYnkoeWVhciwgb2ZmZXJfdHlwZSkgJT4lIHRhbGx5KCkgJT4lCiAgbXV0YXRlKHByb3A9cm91bmQobi9zdW0obikqMTAwLDEpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZmFjdG9yKHllYXIpLCB5PW9mZmVyX3R5cGUsIGZpbGw9cHJvcCkpICsgCiAgZ2VvbV90aWxlKHNpemU9MiwgY29sb3I9IndoaXRlIikgKyAKICBnZW9tX3RleHQoYWVzKGxhYmVsPXBhc3RlKHByb3AsIiUiKSwgY29sb3I9SShpZmVsc2UocHJvcD45MCwid2hpdGUiLCJibGFjayIpKSksc2l6ZT0yLjgpICsKICBzY2FsZV94X2Rpc2NyZXRlKHBvc2l0aW9uPSJ0b3AiKSArIAogIHNjYWxlX2ZpbGxfY29udGludW91c19zZXF1ZW50aWFsKHBhbGV0dGU9ImhlYXQiKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsCiAgICAgICAgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoc2l6ZT0uMiksCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoZmFjZT0iYm9sZCIpKSArIAogIGxhYnMoeD0iUmVnaXN0cmF0aW9uIHllYXJcbiIsIHk9Ik9mZmVyIHR5cGUiLCBzdWJ0aXRsZT0iUHJvcG9ydGlvbiBvZiBvZmZlciB0eXBlIGJ5IHJlZ2lzdHJhdGlvbiB5ZWFyXG4iKQpgYGAKCmBgYHtyfQojIHVzZWQgdmVoaWNsZSBzdWJzZXQKdXNlZCA9IGNhcnNfY2xuICU+JSBmaWx0ZXIob2ZmZXJfdHlwZT09IlVzZWQiKQoKIyBjb3VudCBvZiB1c2VkIHZlaGljbGUgYnkgcmVnaXN0cmF0aW9uIHllYXIKdXNlZCAlPiUgY291bnQoeWVhcikKYGBgCgoKIyMjIE1ha2UKYGBge3J9CiMgbWFrZSBjb3VudCAKY2Fyc19jbG4gJT4lIGdyb3VwX2J5KG1ha2UpICU+JSB0YWxseShzb3J0PVQpCmBgYAoKYGBge3J9CiMgbWFrZSBjb3VudCBieSByZWdpc3RyYXRpb24geWVhcgoKIyBnZXQgdG9wIDcgbWFrZXMgYnkgY291bnQgCnRvcF9tYWtlID0gY2Fyc19jbG4gJT4lIGZpbHRlcih5ZWFyPDIwMjEpICU+JSAKICBncm91cF9ieShtYWtlKSAlPiUgdGFsbHkoc29ydD1UKSAlPiUgc2xpY2UoMTo3KQoKbWFrZSA9IGNhcnNfY2xuICU+JSBmaWx0ZXIoeWVhcjwyMDIxKSAlPiUKICBmaWx0ZXIobWFrZSAlaW4lIHRvcF9tYWtlJG1ha2UpICU+JQogIGdyb3VwX2J5KHllYXIsbWFrZSkgJT4lIHRhbGx5KCkgCgojIHBsb3QKbWFrZSAlPiUKICBnZ3Bsb3QoYWVzKHllYXIsIG4sIGNvbG9yPW1ha2UpKSArIAogIGdlb21fYnVtcCgpICsgCiAgZ2VvbV9wb2ludChzaXplPTIpICsKICBnZW9tX3RleHQoZGF0YSA9IG1ha2UgJT4lIGZpbHRlcih5ZWFyPT0yMDIwKSwgYWVzKGxhYmVsPW1ha2UsIHg9MjAyMC4yKSxzaXplPTMsIGhqdXN0PTApICsKICBzY2FsZV9jb2xvcl9kMygpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzPWMoMjAxMSwyMDIxKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgcGxvdC50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X3RleHQoc2l6ZT04KSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yPSJncmV5MzAiKSkgKyAKICBsYWJzKHg9IlJlZ2lzdHJhdGlvbiB5ZWFyIiwgeT0iVmVoaWNsZSBjb3VudCIsCiAgICAgICB0aXRsZT0iQ291bnQgb2YgdmVoaWNsZSBtYWtlIGJ5IHJlZ2lzdHJhdGlvbiB5ZWFyIiwKICAgICAgIHN1YnRpdGxlPSI3IG1ha2VzIHdpdGggaGlnaGVzdCB2ZWhpY2xlIGNvdW50IGZyb20gMjAxMSB0byAyMDIwIikKYGBgCgojIyMgUHJvcG9ydGlvbiBvZiBtYWtlIGluIDIwMjAKKiByZWZlcmVuY2U6IGh0dHBzOi8vd3d3LmNlZHJpY3NjaGVyZXIuY29tLzIwMjEvMDcvMDUvYS1xdWljay1ob3ctdG8tb24tbGFiZWxsaW5nLWJhci1ncmFwaHMtaW4tZ2dwbG90Mi8KYGBge3J9CiMgc3VtIG9mIGNhcnMgaW4gMjAyMApjYXJzX2NsbiAlPiUgZmlsdGVyKHllYXI9PTIwMjApICU+JSBjb3VudCgpCgojIHByb3BvcnRpb24gb2YgY2FyIG1ha2VzIGluIDIwMjAKbWFrZV9zdW0gPSBjYXJzX2NsbiAlPiUgZmlsdGVyKHllYXI9PTIwMjApICU+JQogIG11dGF0ZShtYWtlX2dycCA9IGZjdF9sdW1wKGZhY3RvcihtYWtlKSwgMTApKSAlPiUKICBjb3VudChtYWtlX2dycCwgc29ydD1UKSAlPiUKICBtdXRhdGUobWFrZV9ncnAgPSBmY3RfcmV2KGZjdF9pbm9yZGVyKG1ha2VfZ3JwKSksCiAgICAgICAgIG1ha2VfZ3JwPWZjdF9yZWxldmVsKG1ha2VfZ3JwLCJPdGhlciIsYWZ0ZXI9MCksCiAgICAgICAgIHBlcmM9IHBhc3RlMChzcHJpbnRmKCIlMi4xZiIsIG4vc3VtKG4pKjEwMCksIiUiKSwKICAgICAgICAgY29sb3I9Y2FzZV93aGVuKHJvd19udW1iZXIoKT09MSB+ImdyZXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgcm93X251bWJlcigpPT0yIH4iI2VlOWIwMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICByb3dfbnVtYmVyKCk9PTMgfiIjNERCQkQ1RkYiLAogICAgICAgICAgICAgICAgICAgICAgICAgcm93X251bWJlcigpPT00IH4iIzAwQTA4N0ZGIiwKICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUV+ImdyZXk1NSIpCiAgICAgICAgICkgCgogZ2dwbG90KG1ha2Vfc3VtLCBhZXMobiwgbWFrZV9ncnAsIGZpbGw9Y29sb3IpKSArIAogIGdlb21fY29sKHdpZHRoPTAuOCkgKyAKICBnZW9tX3RleHQoYWVzKGxhYmVsPXBlcmMpLCBoanVzdD0xLjIsIG51ZGdlX3ggPSAtLjUsIHNpemU9My4yLCBmb250ZmFjZT0iYm9sZCIpICsKICBnZW9tX3RleHQoYWVzKHg9LTEwLGxhYmVsPW1ha2VfZ3JwLCBjb2xvcj1jb2xvciksIAogICAgICAgICAgICBzaXplPTMuNSwgaGp1c3Q9MSwgbnVkZ2VfeD0tMC41LCBmb250ZmFjZT0iYm9sZCIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKC4yLC4xKSkpICsKICBzY2FsZV9maWxsX2lkZW50aXR5KGd1aWRlPSJub25lIikgKwogIHNjYWxlX2NvbG9yX2lkZW50aXR5KGd1aWRlPSJub25lIikgKwogIHRoZW1lX3ZvaWQoYmFzZV9zaXplPTEwKSArIAogIHRoZW1lKHBsb3QudGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuMDMpLAogICAgICAgIHBsb3Quc3VidGl0bGU9ZWxlbWVudF90ZXh0KGhqdXN0PTAuMDMsIGNvbG9yPSJncmV5NTUiLHNpemU9MTApLAogICAgICAgIHBsb3QubWFyZ2luPWdncGxvdDI6Om1hcmdpbigxLDEsMSwxLCJjbSIpCiAgICAgICAgKSArCiAgbGFicyh0aXRsZT0iUHJvcG9ydGlvbiBvZiBjYXIgbWFrZXMgaW4gMjAyMCIsIHN1YnRpdGxlID0gIig0MTE4IG5ldyBhbmQgb2xkIGNhcnMpXG4iKQpgYGAKCgoKCiMjIyBNb2RlbApgYGB7cn0KIyBtb3N0IHBvcHVsYXIgY2FyIGJ5IHJlZ2lzdHJhdGlvbiB5ZWFyIApjYXJzX2NsbiAlPiUgCiAgZmlsdGVyKHllYXI8MjAyMSkgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgY291bnQobWFrZSwgbW9kZWwsIHNvcnQ9VCkgJT4lCiAgc2xpY2UoMSkKYGBgCmBgYHtyLGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTMuNX0KIyBWb2xrc3dhZ2VuIGdvbGYgcHJpY2UgYnkgcmVnaXN0cmF0aW9uIHllYXIKY2Fyc19jbG4gJT4lIAogIGZpbHRlcih5ZWFyPDIwMjEpICU+JQogIGZpbHRlcihtb2RlbD09IkdvbGYiKSAlPiUKICBnZ3Bsb3QoYWVzKHg9ZmFjdG9yKHllYXIpLCB5PXByaWNlKSkgKyAKICBnZ2Rpc3Q6OnN0YXRfaGFsZmV5ZSh3aWR0aCA9IC42LCAud2lkdGggPSAwLCBqdXN0aWZpY2F0aW9uID0gLS4yLCBwb2ludF9jb2xvdXIgPSBOQSwgYWxwaGE9MC43KSArCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gLjEsIG91dGxpZXIuc2hhcGUgPSBOQSwgY29sb3I9IiNmNzdmMDAiLCBmaWxsPU5BKSArIAogIGdnaGFsdmVzOjpnZW9tX2hhbGZfcG9pbnQoc2lkZSA9ICJsIiwgcmFuZ2Vfc2NhbGUgPSAuNSwgYWxwaGEgPSAuMyxzaXplPS45LCBzaGFwZT0yMSkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueT1lbGVtZW50X2JsYW5rKCkpICsgCiAgY29vcmRfZmxpcCgpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwKICAgICAgICBheGlzLnRpdGxlLnk9ZWxlbWVudF90ZXh0KG1hcmdpbj1tYXJnaW4ocj01KSksCiAgICAgICAgYXhpcy50aXRsZS54PWVsZW1lbnRfdGV4dChtYXJnaW49bWFyZ2luKHQ9NSkpKSArIAogIGxhYnMoeD0iUHJpY2UiLCB5PSJSZWdpc3RyYXRpb24geWVhciIsIHN1YnRpdGxlPSJWb2xrc3dhZ2VuIGdvbGYgcHJpY2UgYnkgcmVnaXN0cmF0aW9uIHllYXIiKQpgYGAKCgoKIyMjIEFsbCBjYXJzOiBtZWRpYW4gcHJpY2UgYnkgbWFrZSBhbmQgcmVnaXN0cmF0aW9uIHllYXIKYGBge3IsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQptZWRwcmljZSA9IGNhcnNfY2xuICU+JSAKICBtdXRhdGUobWFrZV9ncnAgPSBmY3RfbHVtcChmYWN0b3IobWFrZSksIDEwKSkgJT4lCiAgZ3JvdXBfYnkobWFrZV9ncnAseWVhcikgJT4lIAogIHN1bW1hcmlzZShtZWRpYW5fcHJpY2UgPSBtZWRpYW4ocHJpY2UpKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBncm91cF9ieShtYWtlX2dycCkgJT4lIAogIG11dGF0ZShtYXhfcHJpY2UgPSBtYXgobWVkaWFuX3ByaWNlKSkgJT4lCiAgYXJyYW5nZShtYXhfcHJpY2UpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUobWFrZV9ncnA9ZmN0X2lub3JkZXIobWFrZV9ncnApLAogICAgICAgICBtYWtlX2dycD1mY3RfcmVsZXZlbChtYWtlX2dycCwiT3RoZXIiLGFmdGVyPTApKQoKbWVkcHJpY2UgJT4lCiAgZ2dwbG90KGFlcyh5PW1ha2VfZ3JwLCB4PW1lZGlhbl9wcmljZSwgY29sb3I9eWVhcikpICsgCiAgZ2VvbV9saW5lKGFlcyhncm91cD1tYWtlX2dycCksY29sb3I9ImdyZXkiKSArCiAgZ2VvbV9wb2ludCgpICsgCiAgc2NhbGVfY29sb3JfY29udGludW91c19zZXF1ZW50aWFsKHBhbGV0dGU9ImJhdGxvdyIpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoNTAwMCw2MDAwMCwxMDAwMCksIGV4cGFuZD1jKC4wMSwuMDEpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHM9c2NhbGVzOjpjb21tYV9mb3JtYXQoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb249InRvcCIsCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1ham9yLng9ZWxlbWVudF9saW5lKHNpemU9LjMpLAogICAgICAgIHBsb3QubWFyZ2luPWdncGxvdDI6Om1hcmdpbiguNSwuNSwuNSwuNSwiY20iKSkgICsgCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfY29sb3JiYXIodGl0bGUucG9zaXRpb24gPSAidG9wIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUuaGp1c3QgPSAuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFyd2lkdGggPSB1bml0KDIwLCAibGluZXMiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFyaGVpZ2h0ID0gdW5pdCguMywgImxpbmVzIikpKSArIAogIGxhYnMoY29sb3I9IlJlZ2lzdHJhdGlvbiB5ZWFyIiwgc3VidGl0bGU9Ik1lZGlhbiBwcmljZSBieSBtYWtlIGFuZCByZWdpc3RyYXRpb24geWVhciIsCiAgICAgICB4PSJNZWRpYW4gUHJpY2UiLHk9Ik1ha2UiKQpgYGAKCgoKIyMjIEdlYXIKCmBgYHtyfQojIHByaWNlIHN1bW1hcnkgYnkgZ2Vhcgpwc3ljaDo6ZGVzY3JpYmVCeShjYXJzX2NsbiRwcmljZSwgY2Fyc19jbG4kZ2VhciwgbWF0PVQsICkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKYGBgCgoKYGBge3J9CiMgcHJvcG9ydGlvbiBvZiBtYW51YWwgYW5kIGF1dG9tYXRpYyBnZWFyIDIwMTEtMjAyMApjYXJzX2NsbiAlPiUgCiAgZmlsdGVyKGdlYXI9PSJNYW51YWwifCBnZWFyPT0iQXV0b21hdGljIikgJT4lCiAgZmlsdGVyKHllYXI8MjAyMSkgJT4lCiAgZ3JvdXBfYnkoeWVhciwgZ2VhcikgJT4lIHRhbGx5KCkgJT4lCiAgbXV0YXRlKHByb3A9bi9zdW0obikpICU+JQogIGdncGxvdChhZXMoeT1mY3RfcmV2KGZhY3Rvcih5ZWFyKSksIHg9cHJvcCwgZmlsbD1mY3RfcmV2KGdlYXIpKSkgKyAKICBnZW9tX2NvbChhbHBoYT0wLjksd2lkdGg9MC42KSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueT1lbGVtZW50X2JsYW5rKCkpICsgCiAgdGhlbWUoYXhpcy50aXRsZT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5tYXJnaW49Z2dwbG90Mjo6bWFyZ2luKDEsMiwxLDIsImNtIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSAibGVmdCIsCiAgICAgICAgbGVnZW5kLm1hcmdpbiA9IG1hcmdpbihsPTMsIHQ9My41LGI9MCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT05KSwKICAgICAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpKSArIAogIHNjYWxlX2ZpbGxfbnBnKCkgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kPWMoLjAxLC4wMSksIGxhYmVscz1wZXJjZW50X2Zvcm1hdCgpLCBwb3NpdGlvbj0idG9wIikgKyAKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQoa2V5aGVpZ2h0PTAuOCxrZXl3aWR0aD0wLjQsIHJldmVyc2U9VCkpICsgCiAgZ2d0aXRsZSgiUG9ycG9ydGlvbiBvZiBtYW51YWwgYW5kIGF1dG9tYXRpYyBjYXJzLCAyMDExIHRvIDIwMjAiKQpgYGAKCmBgYHtyLCBtZXNzYWdlPUYsIHdhcm5pbmc9Rn0KIyBtZWRpYW4gcHJpY2UgYnkgZ2VhciAobWFudWFsLCBhdXRvbWF0aWMpIGFuZCB5ZWFyCmdlYXJfYWdnID0gY2Fyc19jbG4gJT4lIGZpbHRlcihnZWFyPT0iTWFudWFsInwgZ2Vhcj09IkF1dG9tYXRpYyIpICU+JQogIGdyb3VwX2J5KGdlYXIsIHllYXIpICU+JSAKICBzdW1tYXJpc2UobWVkID0gbWVkaWFuKHByaWNlKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGdyb3VwX2J5KGdlYXIpICU+JQogIG11dGF0ZShjdXJyZW50ID0gbWVkW3doaWNoKHllYXIgPT0gMjAyMSldKQogIAoKZ2Vhcl9hZ2cgJT4lICAKICBnZ3Bsb3QoYWVzKHg9Z2VhciwgeT1tZWQpKSArIAogICBnZW9tX2NvbCgKICAgIGFlcyhjb2xvciA9IGZhY3Rvcih5ZWFyKSwgZmlsbCA9IEkoaWZlbHNlKHllYXI9PTIwMjEsIiMwMDc3YjYiLCJncmV5IikpKSwKICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoKSwgc2l6ZSA9IC4wMDEKICApICArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsCiAgICAgICAgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoc2l6ZT0uMyksCiAgICAgICAgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0LnguYm90dG9tID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGUueC5ib3R0b20gPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvci55PWVsZW1lbnRfYmxhbmsoKSkgKyAKICBnZW9tX3RleHQoCiAgICBkYXRhID0gZ2Vhcl9hZ2cgJT4lIGZpbHRlcihnZWFyID09ICJNYW51YWwiKSAlPiUgCiAgICAgIG11dGF0ZSh5ZWFyX2xhYiA9IGlmX2Vsc2UoeWVhciAlaW4lIGMoMjAxMSwgMjAxNiwgMjAyMSksIGFzLmNoYXJhY3Rlcih5ZWFyKSwgIuKAoiIpKSwKICAgIGFlcyh5ID0gMCwgbGFiZWwgPSB5ZWFyX2xhYiwgY29sb3IgPSBmYWN0b3IoeWVhciksIHNpemUgPSB5ZWFyX2xhYiA9PSAi4oCiIiksCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gLjkpLCAKICAgIGhqdXN0ID0gMgogICkgKyAKICBnZW9tX3RleHQoCiAgICBkYXRhID0gZ2Vhcl9hZ2cgJT4lIGZpbHRlcihnZWFyID09ICJBdXRvbWF0aWMiKSAlPiUgCiAgICAgIG11dGF0ZSh5ZWFyX2xhYiA9IGlmX2Vsc2UoeWVhciAlaW4lIGMoMjAxMSwgMjAxNiwgMjAyMSksIGFzLmNoYXJhY3Rlcih5ZWFyKSwgIuKAoiIpKSwKICAgIGFlcyh5ID0gMCwgbGFiZWwgPSB5ZWFyX2xhYiwgY29sb3IgPSBmYWN0b3IoeWVhciksIHNpemUgPSB5ZWFyX2xhYiA9PSAi4oCiIiksCiAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gLjkpLCAKICAgIGhqdXN0ID0gMgogICkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZ3JleTUwIiwiZ3JleTUwIiwiZ3JleTUwIiwiZ3JleTUwIiwiZ3JleTUwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImdyZXk1MCIsImdyZXk1MCIsImdyZXk1MCIsImdyZXk1MCIsImdyZXk1MCIsIiMwMDc3YjYiKSkgKyAKICBjb29yZF9mbGlwKCkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWw9c2NhbGVzOjpjb21tYV9mb3JtYXQoKSxsaW1pdHM9YygtNDAwMCwzODAwMCksCiAgICAgICAgICAgICAgICAgICAgIHNlYy5heGlzID0gZHVwX2F4aXMobmFtZSA9ICJNZWRpYW4gcHJpY2UiKSwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzPXNlcSgwLDMwMDAwLDEwMDAwKSkgICsgCiAgYW5ub3RhdGUoInRleHQiLCB5PS0zNTAwLCB4PTEuMTUsIGxhYmVsPSJBdXRvbWF0aWMiLCBoanVzdD0xLHNpemU9MywgYW5nbGU9OTApICsgCiAgYW5ub3RhdGUoInRleHQiLCB5PS0zNTAwLCB4PTIuMTUsIGxhYmVsPSJNYW51YWwiLCBoanVzdD0xLHNpemU9MywgYW5nbGU9OTApICsgCiAgbGFicyhzdWJ0aXRsZT0iTWVkaWFuIHByaWNlIGJ5IHJlZ2lzdHJhdGlvbiB5ZWFyIGFuZCBnZWFyIikKYGBgCgojIyMgRnVlbAoKYGBge3J9CiMgcHJpY2Ugc3VtbWFyeSBieSBmdWVsCnBzeWNoOjpkZXNjcmliZUJ5KGNhcnNfY2xuJHByaWNlLCBjYXJzX2NsbiRmdWVsLCBtYXQ9VCkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKCiMgbWVkaWFuIHByaWNlIGJ5IGZ1ZWwgYW5kIHJlZ2lzdHJhdGlvbiB5ZWFyCmNhcnNfY2xuICU+JSBtdXRhdGUoZnVlbF9ncnAgPSBmY3RfbHVtcChmYWN0b3IoZnVlbCksIDIpKSAlPiUKICBncm91cF9ieShmdWVsX2dycCwgeWVhcikgJT4lIAogIHN1bW1hcmlzZShtZWQgPSBtZWRpYW4ocHJpY2UpKSAlPiUgCiAgZ2dwbG90KGFlcyh5PWZhY3Rvcih5ZWFyKSx4PW1lZCwgCiAgICAgICAgICAgICBmaWxsPWZhY3RvcihmdWVsX2dycCwgbGV2ZWxzPWMoIkdhc29saW5lIiwiT3RoZXIiLCJEaWVzZWwiLCBvcmRlcmVkPVQpKSkpICsgCiAgZ2VvbV9saW5lKGFlcyhncm91cD15ZWFyKSwgY29sb3I9ImdyZXkiKSArCiAgZ2VvbV9wb2ludChzaXplPTIuNSwgYWxwaGE9MC43NSwgc2hhcGU9MjEsIGNvbG9yPSJibGFjayIpICsgCiAgc2NhbGVfZmlsbF9kMygpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6Y29tbWFfZm9ybWF0KCksCiAgICAgICAgICAgICAgICAgICAgIGxpbWl0cz1jKDUwMDAsNDIwMDApLAogICAgICAgICAgICAgICAgICAgICBicmVha3M9c2VxKDUwMDAsNDIwMDAsMTAwMDApKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgcGFuZWwuZ3JpZD1lbGVtZW50X2xpbmUoc2l6ZT0uMyksCiAgICAgICAgYXhpcy50aXRsZS55PWVsZW1lbnRfdGV4dChtYXJnaW49bWFyZ2luKHI9MTApKSwKICAgICAgICBheGlzLnRpdGxlLng9ZWxlbWVudF90ZXh0KG1hcmdpbj1tYXJnaW4odD04KSkpICsgCiAgbGFicyhmaWxsPSJGdWVsIHR5cGUiLHg9Ik1lZGlhbiBwcmljZSIsIHk9IlJlZ2lzdHJhdGlvbiBZZWFyIiwKICAgICAgIHN1YnRpdGxlPSJNZWRpYW4gcHJpY2UgYnkgZnVlbCBhbmQgcmVnaXN0cmF0aW9uIHllYXIiKQpgYGAKIyMjIFByaWNlCgoKYGBge3IsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTMuNX0KIyBwcmljZSBhbmQgcmVnaXN0cmF0aW9uIHllYXIgKDIwMTEtMjAyMCkKY2Fyc19jbG4gJT4lIAogIGZpbHRlcih5ZWFyPDIwMjEpICU+JQogIGdncGxvdChhZXMoeD1mYWN0b3IoeWVhciksIHk9cHJpY2UsIGNvbG9yPWZjdF9yZXYoZmFjdG9yKHllYXIpKSkpICsKICBnZW9tX2hhbGZfdmlvbGluKHNpemU9MC41LCBhbHBoYT0wLjUsIHNob3cubGVnZW5kPUYsIHNpZGU9InIiKSArCiAgZ2VvbV9oYWxmX3BvaW50KHNpemU9MSwgYWxwaGE9MC41LCBzaG93LmxlZ2VuZD1GLCBzaWRlPSJsIikgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGxvdC5tYXJnaW49Z2dwbG90Mjo6bWFyZ2luKC4xLC41LC4xLC4xLCJjbSIpKSArIAogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSB1bml0X2Zvcm1hdCh1bml0ID0gIksiLCBzY2FsZSA9IDFlLTMpKSsKICBsYWJzKHN1YnRpdGxlPSJQcmljZSBhbmQgcmVnaXN0cmF0aW9uIHllYXJcbiIseT0iUHJpY2UiLHg9IlllYXIiKSArIAogIHNjYWxlX2NvbG9yX2Z1dHVyYW1hKCkgKyAKICBjb29yZF9mbGlwKCkKYGBgCgpgYGB7cn0KIyBib3hwbG90ICh3aXRob3V0IG91dGxpZXJzKQoKIyBmaWx0ZXJpbmcgZnVuY3Rpb24gLSB0dXJucyBvdXRsaWVycyBpbnRvIE5BcyB0byBiZSByZW1vdmVkCmZpbHRlcl9saW1zIDwtIGZ1bmN0aW9uKHgpewogIGwgPC0gYm94cGxvdC5zdGF0cyh4KSRzdGF0c1sxXQogIHUgPC0gYm94cGxvdC5zdGF0cyh4KSRzdGF0c1s1XQoKICBmb3IgKGkgaW4gMTpsZW5ndGgoeCkpewogICAgeFtpXSA8LSBpZmVsc2UoeFtpXT5sICYgeFtpXTx1LCB4W2ldLCBOQSkKICB9CiAgcmV0dXJuKHgpCn0KCiMgcGxvdApjYXJzX2NsbiAlPiUgZmlsdGVyKHllYXI8MjAyMSkgJT4lIAogIGdyb3VwX2J5KHllYXIpICU+JSAKICBtdXRhdGUocHJpY2UyPSBmaWx0ZXJfbGltcyhwcmljZSkpICU+JQogIGdncGxvdChhZXMoeD1mYWN0b3IoeWVhciksIHk9cHJpY2UyLCBjb2xvcj1mY3RfcmV2KGZhY3Rvcih5ZWFyKSkpKSArIAogIGdlb21fYm94cGxvdChuYS5ybSA9IFRSVUUsIGNvZWYgPSA1KSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWFfZm9ybWF0KCkpKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJub25lIikgKwogIGNvb3JkX2ZsaXAoKSArIAogIHNjYWxlX2NvbG9yX2Z1dHVyYW1hKCkgKyAKICBsYWJzKHg9IlllYXIiLHk9IlByaWNlIiwgc3VidGl0bGU9IkJveHBsb3Qgd2l0aG91dCBvdXRsaWVyczogcHJpY2UgYW5kIHJlZ2lzdHJhdGlvbiB5ZWFyXG4iKSAKCmBgYAoKYGBge3J9CiMgcHJvcG9ydGlvbiBvZiBwcmljZSBncm91cHMgYnkgcmVnaXN0cmF0aW9uIHllYXIKc3VtbWFyeShjYXJzX2NsbiRwcmljZSkKCmNhcnMyID0gY2Fyc19jbG4gJT4lIGZpbHRlcih5ZWFyPDIwMjEpIApjYXJzMiRwcmljZV9ncm91cCA9IGN1dChjYXJzMiRwcmljZSwgYnJlYWtzPWMoMTEwMCw3NDkwLDEwOTkwLDE5NDkwLDExOTk5MDApLAogICAgICAgICAgICAgICAgbGFiZWxzPWMoIjEsMTAwIHRvIDcsNDkwIiwiNyw0OTAgdG8gMTAsOTkwIiwiMTAsOTkwIHRvIDE5LDQ5MCIsIjE5LDQ5MCB0byAxMTksOTAwIiksCiAgICAgICAgICAgICAgICBpbmNsdWRlLmxvd2VzdCA9IFQpCgpjYXJzMiAlPiUgY291bnQocHJpY2VfZ3JvdXApIAoKY2FyczIgJT4lIGdyb3VwX2J5KHllYXIsIHByaWNlX2dyb3VwKSAlPiUgdGFsbHkoKSAlPiUKICBtdXRhdGUocHJvcD1uL3N1bShuKSkgJT4lCiAgZ2dwbG90KGFlcyh4PWZhY3Rvcih5ZWFyKSwgeT1wcm9wLCBmaWxsPXByaWNlX2dyb3VwKSkgKyAKICBnZW9tX2NvbCh3aWR0aD0wLjgsIGFscGhhPTAuOSkgKwogIGNvb3JkX2ZsaXAoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIikgKyAKICBzY2FsZV9maWxsX25wZyhndWlkZT1ndWlkZV9sZWdlbmQocmV2ZXJzZT1UKSkgKyAKICBsYWJzKGZpbGw9IlByaWNlIGdyb3VwIix5PSJQcm9wb3J0aW9uIiwgeD0iUmVnaXN0cmF0aW9uIHllYXIiLAogICAgICAgc3VidGl0bGU9IlByb3BvcnRpb24gb2YgcHJpY2UgZ3JvdXAgYnkgcmVnaXN0cmF0aW9uIHllYXIiKQpgYGAKCiMjIyBVc2VkIHZlaGljbGUgcHJpY2UgCgpgYGB7cn0KIyB1c2VkIHZlaGljbGUgcHJpY2VzIGJ5IHJlZ2lzdHJhdGlvbiB5ZWFyCnVzZWQgPSBjYXJzX2NsbiAlPiUgZmlsdGVyKG9mZmVyX3R5cGU9PSJVc2VkIikgJT4lIGZpbHRlcih5ZWFyPDIwMjEpCnVzZWRfdGFibGUgPSBwc3ljaDo6ZGVzY3JpYmVCeSh1c2VkXzIwMjEkcHJpY2UsIHVzZWRfMjAyMSR5ZWFyLCBtYXQ9VCkKCnVzZWRfdGFibGUgJT4lIHNlbGVjdChncm91cDEsIG1lYW4sIHNkLCBtZWRpYW4sIG1pbiwgbWF4LCByYW5nZSwgc2tldykgJT4lCiAgcmVuYW1lKHllYXIgPSBncm91cDEpICU+JQogIGd0KCkgJT4lCiAgZm10X251bWJlcigKICAgIGNvbHVtbnMgPSBjKCJtZWFuIiwic2QiLCJtZWRpYW4iLCJtaW4iLCJtYXgiLCJyYW5nZSIsInNrZXciKSwKICAgIGRlY2ltYWxzPTApICU+JQogIGRhdGFfY29sb3IoCiAgICBjb2x1bW5zID1jKCJtZWFuIiwic2QiLCJtZWRpYW4iLCJtaW4iLCJtYXgiLCJyYW5nZSIsInNrZXciKSwKICAgIGNvbG9ycyA9IHNjYWxlczo6Y29sX251bWVyaWMoCiAgICAgIHBhbGV0dGUgPSBjKCIjZmZmZmZmIiwgIiNmMmZiZDIiLCAiI2M5ZWNiNCIsICIjOTNkM2FiIiwgIiMzNWIwYWIiKSwKICAgICAgZG9tYWluID0gTlVMTCkpICU+JQogIHRhYl9zdHlsZSgKICAgIHN0eWxlID0gbGlzdCgKICAgICAgY2VsbF9ib3JkZXJzKAogICAgICAgIHNpZGVzID0gImJvdHRvbSIsCiAgICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICAgIHdlaWdodCA9IHB4KDMpCiAgICAgICkKICAgICksCiAgICBsb2NhdGlvbnMgPSBsaXN0KAogICAgICBjZWxsc19jb2x1bW5fbGFiZWxzKAogICAgICAgIGNvbHVtbnMgPSBndDo6ZXZlcnl0aGluZygpCiAgICAgICkKICAgICkKICApICU+JQogIHRhYl9vcHRpb25zKHRhYmxlLmZvbnQuc2l6ZT0xMykgJT4lCiAgdGFiX2hlYWRlcih0aXRsZT1tZCgiKipVc2VkIFZlaGljbGUgUHJpY2VzIGJ5IFJlZ2lzdHJhdGlvbiBZZWFyKioiKSkKYGBgCgojIyMgTWlsZWFnZQoKYGBge3J9CiMgbWlsZWFnZSBhbmQgcmVnaXN0cmF0aW9uIHllYXIKY2Fyc19jbG4gJT4lIGZpbHRlcih5ZWFyPDIwMjEpICU+JQogIGdncGxvdChhZXMoeD1mYWN0b3IoeWVhciksIHk9bWlsZWFnZSkpICsgCiAgZ2VvbV9oYWxmX2JveHBsb3Qod2lkdGg9LjUsIG91dGxpZXIuY29sb3IgPSBOQSkgKyAKICBnZW9tX2hhbGZfcG9pbnQoc2lkZT0iciIsIHJhbmdlX3NjYWxlPS41LCBhbHBoYT0wLjMsIHNpemU9MSwgY29sb3I9IiMzQzU0ODhGRiIpICsgCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWFfZm9ybWF0KCkpICsgCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvci54PWVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRpdGxlLnkubGVmdCA9IGVsZW1lbnRfdGV4dChtYXJnaW49bWFyZ2luKHI9NSkpLAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OSwgZmFjZT0iYm9sZCIsIGNvbG9yPSJncmV5NTAiKSkgKwogIGxhYnMoeD0iWWVhciIseT0iTWlsZWFnZSAoaW4ga2lsb21ldGVycykiLHN1YnRpdGxlPSJNaWxlYWdlIGFuZCByZWdpc3RyYXRpb24geWVhciIpIApgYGAKCmBgYHtyfQojIG1lZGlhbiBtaWxlYWdlIGFuZCByZWdpc3RyYXRpb24geWVhcgpjYXJzX2NsbiAlPiUgZmlsdGVyKHllYXI8MjAyMSkgJT4lCiAgZmlsdGVyKG9mZmVyX3R5cGU9PSJVc2VkIikgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lIAogIHN1bW1hcmlzZShtZWRpYW5fbWlsZWFnZSA9IG1lZGlhbihtaWxlYWdlKSkgJT4lCiAgZ2dwbG90KGFlcyh5PWZjdF9yZXYoZmFjdG9yKHllYXIpKSwgeD1tZWRpYW5fbWlsZWFnZSkpICsgCiAgZ2VvbV9jb2woYWVzKGZpbGw9SShpZl9lbHNlKHllYXI9PTIwMTEsIiMzQzU0ODhGRiIsImdyZXk3MCIpKSksd2lkdGg9MC43LCBzaG93LmxlZ2VuZD1GKSArIAogIGdlb21fdmxpbmUoY29sb3I9IndoaXRlIix4aW50ZXJjZXB0PWMoc2VxKDAsMTAwMDAwLDI1MDAwKSksIHNpemU9MC40KSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cz1jKDAsMTI1MDAwKSwgZXhwYW5kPWMoMCwwKSwgcG9zaXRpb24gPSAidG9wIiwKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzPXNlcSgwLDEwMDAwMCwyNTAwMCkpICsKICAjc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IHdlc19wYWxldHRlKCJaaXNzb3UxIiwgMTAsIHR5cGUgPSAiY29udGludW91cyIpLCB0cmFucz0icmV2ZXJzZSIpICsKICB0aGVtZV9saWdodChiYXNlX3NpemU9MTApICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9OSwgZmFjZT0iYm9sZCIsIGNvbG9yPSJncmV5NTAiKSwKICAgICAgICBheGlzLnRpdGxlLnkubGVmdCA9IGVsZW1lbnRfdGV4dChtYXJnaW49bWFyZ2luKHI9NSkpLAogICAgICAgIGF4aXMudGV4dC54LnRvcCA9IGVsZW1lbnRfdGV4dChtYXJnaW49bWFyZ2luKGI9NSx0PS01KSwgdmp1c3Q9LTEpLAogICAgICAgIGF4aXMudGlja3MueT1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aWNrcy5sZW5ndGg9dW5pdCguMiwgImNtIiksCiAgICAgICAgcGxvdC5tYXJnaW49Z2dwbG90Mjo6bWFyZ2luKDEsMSwxLDEsImNtIiksCiAgICAgICAgcGxvdC50aXRsZS5wb3NpdGlvbiA9ICJwbG90IgogICAgICAgICkgKwogIGxhYnMoeT0iWWVhciIseD0iTWVkaWFuIE1pbGVhZ2VcbiIsIHN1YnRpdGxlID0gIlVzZWQgdmVoaWNsZXM6IE1lZGlhbiBtaWxlYWdlIGJ5IHJlZ2lzdHJhdGlvbiB5ZWFyXG4iKQpgYGAKCiMjIyBocApgYGB7cn0KIyBocCBhbmQgcHJpY2UKY2Fyc19jbG4gJT4lIGZpbHRlcih5ZWFyPDIwMjEpICU+JQogIGdncGxvdChhZXMoeD1mYWN0b3IoeWVhciksIHk9aHApKSArIAogIGdlb21faGFsZl9ib3hwbG90KHdpZHRoPS41LCBvdXRsaWVyLmNvbG9yID0gTkEpICsgCiAgZ2VvbV9oYWxmX3BvaW50KHNpZGU9InIiLCByYW5nZV9zY2FsZT0uNSwgYWxwaGE9MC4yLCBzaXplPTEsIGNvbG9yPSIjRTY0QjM1RkYiKSArIAogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50aXRsZS55LmxlZnQgPSBlbGVtZW50X3RleHQobWFyZ2luPW1hcmdpbihyPTUpKSwKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTksIGZhY2U9ImJvbGQiLCBjb2xvcj0iZ3JleTUwIikpICsKICBsYWJzKHg9IlllYXIiLHk9IkhvcnNlcG93ZXIiLHN1YnRpdGxlPSJIb3JzZXBvd2VyIGFuZCByZWdpc3RyYXRpb24geWVhciIpIApgYGAKCgojIyMgQ29ycmVsYXRpb24KCmBgYHtyfQpjYXJzX2NsbjIgPSAgY2Fyc19jbG4gJT4lIG11dGF0ZShpZD1yb3dfbnVtYmVyKCkpCmBgYAoKYGBge3J9CiN1c2VkIGNhcnMgd2l0aCByZWdpc3RyYXRpb24geWVhciBmcm9tIDIwMTEgdG8gMjAyMApjYXJzX24gPSBjYXJzX2NsbiAlPiUgCiAgZmlsdGVyKG9mZmVyX3R5cGU9PSJVc2VkIikgJT4lCiAgZmlsdGVyKHllYXI8MjAyMSkgJT4lCiAgc2VsZWN0KHllYXIsIHByaWNlLCBtaWxlYWdlLCBocCkgJT4lCiAgZHJvcF9uYSgpCgojIGNvcnJlbGF0aW9uCnNldC5zZWVkKDEyMykKZ2djb3JybWF0KAogIGRhdGE9Y2Fyc19uLAogIGNvci52YXJzPWMoeWVhcjpocCksCiAgdGl0bGU9IkNvcnJlbGF0aW9uIiwKKQpgYGAKCgoKYGBge3J9CmNhcnNfbjIgPSBjYXJzX251bSAlPiUgCiAgIyBnZXQgenNjb3JlCiAgbXV0YXRlKHpzY29yZV9wID0ocHJpY2UtIG1lYW4ocHJpY2UpKS8gc2QocHJpY2UpLAogICAgICAgICB6c2NvcmVfbSA9KG1pbGVhZ2UtIG1lYW4obWlsZWFnZSkpLyBzZChtaWxlYWdlKSwKICAgICAgICAgenNjb3JlX2hwID0oaHAtIG1lYW4oaHApKS8gc2QoaHApKSAlPiUKICAjIGRyb3Agb3V0bGllcnMKICBmaWx0ZXIoYmV0d2Vlbih6c2NvcmVfcCwtMywzKSkgJT4lCiAgZmlsdGVyKGJldHdlZW4oenNjb3JlX20sLTMsMykpICU+JQogIGZpbHRlcihiZXR3ZWVuKHpzY29yZV9ocCwtMywzKSkgJT4lCiAgIyBzZWxlY3QgdmFyaWFibGVzCiAgc2VsZWN0KHllYXIsIHByaWNlLCBtaWxlYWdlLGhwKQoKZGltKGNhcnNfbikKZGltKGNhcnNfbjIpCgojIGNvcnJlbGF0aW9uIGFmdGVyIGRyb3BwaW5nIG91dGxpZXJzCnNldC5zZWVkKDEyMykKZ2djb3JybWF0KAogIGRhdGE9Y2Fyc19uMiwKICBjb3IudmFycz1jKHllYXI6aHApLAogIHRpdGxlPSJDb3JyZWxhdGlvbiIsCikKYGBgCgojIyMgQ2x1c3RlcmluZwoqIHVzZWQgY2FycyByZWdpc3RlcmVkIGJldHdlZW4gMjAxNyB0byAyMDE5CiogdmFyaWFibGVzOiBwcmljZSwgbWlsZWFnZSwgaHAKCmBgYHtyfQojIHVzZWQgY2FycyByZWdpc3RlcmVkIGJldHdlZW4gMjAxNyB0byAyMDE5CnVzZWQyID0gY2Fyc19jbG4gJT4lIAogIGZpbHRlcihvZmZlcl90eXBlPT0iVXNlZCIpICU+JSAKICBmaWx0ZXIoYmV0d2Vlbih5ZWFyLCAyMDE3LDIwMTkpKSAlPiUKICBkcm9wX25hKHllYXIsIHByaWNlLCBtaWxlYWdlLGhwKQpgYGAKCmBgYHtyfQojIGRyb3Agb3V0bGllcnMKdXNlZDJiID0gdXNlZDIgJT4lCiAgbXV0YXRlKHpzY29yZV9wID0ocHJpY2UtIG1lYW4ocHJpY2UpKS8gc2QocHJpY2UpLAogICAgICAgICB6c2NvcmVfbSA9KG1pbGVhZ2UtIG1lYW4obWlsZWFnZSkpLyBzZChtaWxlYWdlKSwKICAgICAgICAgenNjb3JlX2hwID0oaHAtIG1lYW4oaHApKS8gc2QoaHApKSAlPiUKICBmaWx0ZXIoYmV0d2Vlbih6c2NvcmVfcCwtMywzKSkgJT4lCiAgZmlsdGVyKGJldHdlZW4oenNjb3JlX20sLTMsMykpICU+JQogIGZpbHRlcihiZXR3ZWVuKHpzY29yZV9ocCwtMywzKSkgJT4lCiAgc2VsZWN0KC16c2NvcmVfcCwtenNjb3JlX20sLXpzY29yZV9ocCwtb2ZmZXJfdHlwZSkKCmRpbSh1c2VkMikKZGltKHVzZWQyYikKCnVzZWQzID0gdXNlZDJiICU+JSAgc2VsZWN0KHByaWNlLCBtaWxlYWdlLGhwKQoKIyBzY2FsZSAKdXNlZDNfc2NhbGVkID0gc2NhbGUodXNlZDMpCmBgYAoKYGBge3J9CiNoaWVyYXJjaGljYWwgY2x1c3RlcmluZwpzZXQuc2VlZCgxMjM0KQpoYz0gaGNsdXN0KGRpc3QodXNlZDNfc2NhbGVkKSkKcGxvdChoYykKYGBgCgoKYGBge3J9CiMgY2hlY2sgb3B0aW1hbCBjbHVzdGVyczogZWxib3cgbWV0aG9kIApzZXQuc2VlZCgxMjMpCmZ2aXpfbmJjbHVzdCh1c2VkM19zY2FsZWQsa21lYW5zLG1ldGhvZD0id3NzIikKYGBgCgpgYGB7cn0KI2sgbWVhbnM6IDQgY2x1c3RlcnMKc2V0LnNlZWQoMTIzNCkKazQ9IGttZWFucyh1c2VkM19zY2FsZWQsY2VudGVycz00LG5zdGFydD01MCkKazQKYGBgCgogCgpgYGB7cn0KZnZpel9jbHVzdGVyKGs0LCBkYXRhPXVzZWQzX3NjYWxlZCwgbGFiZWxzaXplPTApICNjbHVzdGVyIHBsb3QKd2l0aCh1c2VkMyxwYWlycyh1c2VkM19zY2FsZWQsY29sPSgxOjQpW2s0JGNsdXN0ZXJdKSkgI3BhaXIgcGxvdApgYGAKCmBgYHtyfQpjb2xuYW1lcyh1c2VkMmIpCmBgYAoKYGBge3J9CiMgc3VtbWFyeSBieSBjbHVzdGVyIGlkIAp1c2VkMmMgPSB1c2VkMmIgJT4lIG11dGF0ZV9hdCh2YXJzKG1ha2UsIG1vZGVsLCBmdWVsLCBnZWFyLCB5ZWFyKSwgbGlzdChmYWN0b3IpKQoKdXNlZDJjJGNsdXN0aWQ9YXMuZmFjdG9yKGs0JGNsdXN0ZXIpCmJ5KHVzZWQyYyx1c2VkMmMkY2x1c3RpZCxzdW1tYXJ5KQpgYGAKCgoqIGNsdXN0ZXIgc2l6ZTogYzQgKG49NTE4NikgPiBjMyAobj0zMDA1KSA+IGMxIChuPTE4NTUpID4gYzIgKG49MTI4OCkgICAKKiBtZWFuIHByaWNlOiBjMiA+IGMzID4gYzEgPiBjNCAgICAKKiBtZWFuIG1pbGVhZ2U6IGMxID4gYzIgPiBjMyA+IGM0ICAgCiogbWVhbiBocDogYzIgPiBjMyA+IGMxID4gYzQgICAKCiogc3VtbWFyeSBvZiBjbHVzdGVyIG1lYW4KICArIGMyOiBoaWdoZXN0IHByaWNlL2hwLCBzZWNvbmQgaGlnaGVzdCBtaWxlYWdlICAgIAogICsgYzM6IHNlY29uZCBoaWdoZXN0IHByaWNlL2hwLCBzZWNvbmQgbG93ZXN0IG1pbGVhZ2UgICAKICArIGMxOiBzZWNvbmQgbG93ZXN0IHByaWNlL2hwLCBoaWdoZXN0IG1pbGVhZ2UgICAKICArIGM0OiBsb3dlc3QgcHJpY2UvbWlsZWFnZS9ocCAgIAogICAgCiogc3VtbWFyeSBvZiBvdGhlciBmZWF0dXJlcyBieSBjbHVzdGVyIElECiAgKiBoaWdoZXN0IGNvdW50IG9mIG1ha2UgKGhpZ2hlc3QgY291bnQpICAgICAgICAKICAgICsgYzE6IFZvbGtzd2FnZW4sIGMyOiBBdWRpLCBjMzogVm9sa3N3YWdlbiwgYzQ6IE9wZWwgCiAgKiBtb2RlbCAoaGlnaGVzdCBjb3VudCkgICAKICAgICsgYzE6IEFzdHJhLCBjMjogWEM5MCwgYzM6IFRpZ3VhbiwgYzQ6IEZpZXN0YSAKICAqIGhpZ2hlc3QgY291bnQgb2YgZnVlbCAoaGlnaGVzdCBjb3VudCkgICAKICAgICsgYzE6IERpZXNlbCwgYzI6IERpZXNlbCwgYzM6IERpZXNlbCwgYzQ6IEdhc29saW5lIAogICogZ2VhciAoaGlnaGVzdCBjb3VudCkgIAogICAgKyBjMTogTWFudWFsLCBjMjogQXV0b21hdGljLCBjMzogQXV0b21hdGljLCBjNDogTWFudWFsIAogICogcmVnaXN0cmF0aW9uIHllYXIgKGhpZ2hlc3QgY291bnQpICAgCiAgICArIGMxOiAyMDE3LCBjMjogMjAxOCwgYzM6IDIwMTksIGM0OiAyMDE5IAogIAoKICAKCgoK