library(lubridate)
library(stringr)
library(ggplot2)
library(dplyr)
library(sqldf)
library(aod)
Read in data
#gsub('\\\\','/',readClipboard())
setwd(readClipboard())
df <-read.csv(gzfile('data_challenge_price.csv.gz','rt')
, header=T
, stringsAsFactors = F)
str(df)
'data.frame': 329097 obs. of 9 variables:
$ business_id : chr "cdd69a9e27026cd2b0667d18b765c918" "cdd69a9e27026cd2b0667d18b765c918" "cdd69a9e27026cd2b0667d18b765c918" "cdd69a9e27026cd2b0667d18b765c918" ...
$ cob_id : int 5010 5010 5010 5010 5010 5010 5003 5003 5003 5003 ...
$ state : chr "MO" "MO" "MO" "MO" ...
$ bundle_name : chr "proPlus" "basicTria" "basic" "proPlusTria" ...
$ status_name : chr "Quote" "Quote" "Quote" "Quote" ...
$ yearly_premium: num 434 350 350 435 411 413 350 350 350 350 ...
$ start_date : chr "2018-03-02" "2018-03-02" "2018-03-02" "2018-03-02" ...
$ creation_time : chr "2018-01-01 17:35:14.949" "2018-01-01 17:35:14.994" "2018-01-01 17:35:15.025" "2018-01-01 17:35:15.068" ...
$ orig_premium : chr "" "" "" "" ...
Quick glance
c
function (...) .Primitive("c")
nrow(unique(df[1]))
[1] 30650
summary(df$yearly_premium)
Min. 1st Qu. Median Mean 3rd Qu. Max.
350 396 750 25873 944 56822079
Buz ID: 30650 Cob_ID: 5 State: 46 Bundle: 6
Not sure why orig_premium is double-quoted, change type
df$orig_premium1 <- as.numeric(str_sub(df$orig_premium, 2, -2))
##Checking
y <- df[is.na(df$orig_premium1)==F, c("yearly_premium", "orig_premium", "orig_premium1")]
y <- unique(y[order(y$orig_premium1),])
head(y); tail(y)
If orig_premium represents the premium before rounding up to the minimum, some prices seem to be ridiculously too low for a sound policy. We wouldn’t use it to construct price elasticity curves.
Highly skewed yearly premium
ggplot(data = df[df$yearly_premium > 10000, ], aes(x = yearly_premium)) +
geom_histogram(bins= 500, color = 'black') +
ggtitle("Yearly Premium > $10000")

Premium range by state, cob
premium_range <-
df %>% group_by(state, cob_id) %>%
summarise(minPrem = min(yearly_premium)
,maxPrem = max(yearly_premium))
##Fluctuation of state-cob specific premium range
premium_range %>% group_by(cob_id) %>%
summarise(min_minPrem = min(minPrem)
,max_minPrem = max(minPrem)
,min_maxPrem = min(maxPrem)
,max_maxPrem = max(maxPrem))
Minimum premium set to $350, fluctuates ~$100 - $200 across states, depending on cob.
Select premium
#g <- function(x) c(n = length(x), quantile(x, c(0, 0.25, 0.5, 0.75, 0.9, 0.99, 1)))
#tapply(df$yearly_premium, df$status_name, g)
df %>% group_by(status_name) %>%
summarise(n = n()
,min = min(yearly_premium)
,q25 = quantile(yearly_premium, 0.25)
,q50 = quantile(yearly_premium, 0.5)
,q75 = quantile(yearly_premium, 0.75)
,q90 = quantile(yearly_premium, 0.9)
,q99 = quantile(yearly_premium, 0.99)
,max = max(yearly_premium)
)
### Pick latest quote per status
# If any quote is purchased (status of Active or Canceled), use the associated premium
# Else, if any quote is selected (meaning the user has selected that package and clicked through to the payment screen), use the associated premium from the latest quote
# Else, use the associated premium from the latest quote for the Pro bundle
### Assume one staus per business id regardless of state
last_quote <- sqldf("select *, case when status_name in ('Active', 'Canceled') then 1
when status_name = 'Selected' then 2
when status_name = 'Quote' and bundle_name = 'pro' then 3
else 4
end as s
From df
group by business_id, s
having creation_time = max(creation_time)
order by business_id, s")
### Check if business with only Quote status always have pro bundle
Quote <- sqldf("select * From last_quote where business_id not in
(select business_id from last_quote where s in (1,2))")
DFO <- sqldf("select business_id from Quote where status_name = 'Quote' and bundle_name != 'pro'
except
select business_id from Quote where status_name = 'Quote' and bundle_name = 'pro'")
### -> 0 count, Yes!
status <- sqldf("select *
,case when s =1 then 1 else 0 end as Converted
From last_quote
group by business_id
having s = min(s)") # --> get back 30650 unique business id!
table(status[status$s==3, ]$bundle_name) #--> if status = Quote, bundle = pro!
pro
9763
table(status$status_name)
Active Canceled Quote Selected
7280 1355 9763 12252
Coversion rate
### Overall
round(prop.table(table(status$Converted))*100, 2)
0 1
71.83 28.17
#tapply(status$Converted, status$cob_id, function(x) prop.table(table(x)))
28% converted overall
By business class
### Tabulate
status %>% group_by(cob_id) %>% summarise('Conversion%' = round(mean(Converted)*100, 2)) %>%
arrange(desc(.$'Conversion%'))
### Statistical test
status$cob_id <- factor(status$cob_id)
logit_cob <- glm(Converted ~ cob_id, data = status, family = "binomial")
#summary(logit_cob)
wald.test(b = coef(logit_cob), Sigma = vcov(logit_cob), Terms = 2:5)
Wald test:
----------
Chi-squared test:
X2 = 107.7, df = 4, P(> X2) = 0.0
Conversion% significantly different between business classes
By State
### Tabulate
Conv_State <- status %>%
group_by(state) %>%
summarise(conversion_rate = round(mean(Converted)*100, 2)) %>%
arrange(desc(.$conversion_rate))
Conv_State
### Visualize
ggplot(data = Conv_State, aes(x = reorder(state, -conversion_rate), y = conversion_rate))+
geom_point() +
geom_line(group=1) +
theme(axis.text.x = element_text(angle = 40)) +
labs(x = 'State') +
ggtitle("Conversion Rate per State")

### Statistical test
logit_state <- glm(Converted ~ state, data = status, family = "binomial")
#summary(logit_state)
wald.test(b = coef(logit_state), Sigma = vcov(logit_state), Terms = 2:46)
Wald test:
----------
Chi-squared test:
X2 = 325.4, df = 45, P(> X2) = 0.0
Conversion% significantly different between states, ranging from 13% to 48%
By pricing bin
### Distribution of premium per converted status
g <- function(x) c(n = length(x), quantile(x, c(0, 0.25, 0.5, 0.75, 0.9, 0.99, 1)))
tapply(status$yearly_premium, status$Converted, g)
$`0`
n 0% 25% 50% 75% 90% 99% 100%
22015.0 350.0 447.0 750.0 1166.0 2238.2 10048.2 53632126.0
$`1`
n 0% 25% 50% 75% 90% 99% 100%
8635.00 350.00 361.00 566.00 750.00 1143.00 3174.98 13145.00
### Highly skewed distribution, zoom in to just premium < $1500 and see how conversion varies
# ggplot(status[status$yearly_premium <=1500, ],
# aes(x=bin10, color =as.factor(Converted))) +
# geom_histogram(fill="white", binwidth = 10, stat = 'count') +
# ggtitle('Conversion vs Premium')
### Cut into $10 increments, 116 buckets
cutoff <- seq(350, 1500, 10)
status$bin10 <- cut(status$yearly_premium, breaks = cutoff, right = F, labels = F)
status[is.na(status$bin10), ]$bin10 <- 116
x <- status %>% group_by(bin10) %>%
summarise(min = min(yearly_premium)
,max = max(yearly_premium))
#tail(x)
Conv_bin10 <- status %>%
group_by(bin10) %>%
summarise(conversion_rate = round(mean(Converted)*100, 2))
Conv_bin10 <- sqldf("select x.*, b.conversion_rate
from x left join Conv_bin10 b
on x.bin10 = b.bin10")
Conv_bin10
ggplot(data=Conv_bin10,aes(x=min, y=conversion_rate)) +
geom_point() +
geom_line(group=1) +
scale_x_continuous(breaks=seq(350,1500,50)) +
labs(x = 'Premium in $10 increment, lower bound'
,y = 'Conversion Rate %') +
ggtitle('Conversion vs Premium')

This price elasticity curve makes sense as we would expect more people to buy an insurance policy at a lower price.
Based on the line graph, re-bin premium into smaller number of buckets
mean(status[status$yearly_premium >=350 & status$yearly_premium <360, ]$Converted) #34.0%
[1] 0.3401469
mean(status[status$yearly_premium >=360 & status$yearly_premium <440, ]$Converted) #40.7%
[1] 0.4070143
mean(status[status$yearly_premium >=440 & status$yearly_premium <600, ]$Converted) #35.4%
[1] 0.3538642
mean(status[status$yearly_premium >=600 & status$yearly_premium <720, ]$Converted) #32.7%
[1] 0.3265661
mean(status[status$yearly_premium >=720 & status$yearly_premium <1000, ]$Converted) #28.4
[1] 0.2839477
mean(status[status$yearly_premium >=1000 & status$yearly_premium <1360, ]$Converted) #20.4%
[1] 0.2041667
mean(status[status$yearly_premium >=1360, ]$Converted) #11.9%
[1] 0.1189343
# ggplot(status[status$yearly_premium <=1500, ],
# aes(x=yearly_premium, color =as.factor(Converted))) +
# geom_histogram(fill="white", binwidth = 10) +
# ggtitle('Conversion vs Premium')
status$bin <- ifelse(status$yearly_premium >=350 & status$yearly_premium <360, '1-350-359',
ifelse(status$yearly_premium >=360 & status$yearly_premium <440, '2-360-439',
ifelse(status$yearly_premium >=440 & status$yearly_premium <600, '3-440-599',
ifelse(status$yearly_premium >=600 & status$yearly_premium <720, '4-600-719',
ifelse(status$yearly_premium >=720 & status$yearly_premium <1000, '5-720-999',
ifelse(status$yearly_premium >=1000 & status$yearly_premium <1360, '6-1000-1359',
ifelse(status$yearly_premium >=1360, '7-1360+', 'NA')))))))
status$bin <-factor(status$bin)
###Check binning
tapply(status$yearly_premium, status$bin, range)
$`1-350-359`
[1] 350 359
$`2-360-439`
[1] 360 439
$`3-440-599`
[1] 440 599
$`4-600-719`
[1] 600 719
$`5-720-999`
[1] 720 999
$`6-1000-1359`
[1] 1000 1359
$`7-1360+`
[1] 1360 53632126
### Tabulate
Conv_bin <- status %>%
group_by(bin) %>%
summarise(conversion_rate= round(mean(Converted)*100, 2)
,n = n()) %>%
arrange(desc(.$conversion_rate))
Conv_bin
### Visualize
ggplot(data = Conv_bin, aes(x = bin,
#x = reorder(bin2, conversion_rate),
y = conversion_rate))+
geom_point() +
geom_line(group=1) +
labs(x = 'Pricing Bin $'
,y = 'Conversion Rate %') +
ggtitle("Conversion Rate per Pricing")

### Statistical test
logit_bin <- glm(Converted ~ bin, data = status, family = "binomial")
#summary(logit_bin)
wald.test(b = coef(logit_bin), Sigma = vcov(logit_bin), Terms = 2:7)
Wald test:
----------
Chi-squared test:
X2 = 1071.2, df = 6, P(> X2) = 0.0
Conversion% generally drops with increasing premium, and is significantly different between price buckets.
Also, highest conversion rate happens at $360-439. Does it suggset raising the minimum premium from $350 or is it just a minimum price per state requirement for certain business classes?
Does binning effect change with business class?
Conv_bin_cob <- status %>%
group_by(cob_id, bin) %>%
summarise(conversion_rate= round(mean(Converted)*100, 2)
,n = n()) #%>%
#arrange(desc(.$conversion_rate))
Conv_bin_cob
### Visualize
ggplot(data = Conv_bin_cob, aes(x = bin,
#x = reorder(bin2, conversion_rate),
y = conversion_rate)) +
geom_point() +
geom_line(aes(group=cob_id, color=cob_id)) +
labs(x = 'Pricing Bin $') +
ggtitle("Conversion Rate per Pricing per COB")

Conversion trending generally looks similar across all business classes except for 5003 that shows the highest conversion rate of 26.3% at $350-$359 range. For class 100001, premium starts at $720-$999 range. It’s also the same range where the highest conversion hits.
Does binning effect change with state?
Conv_bin_state <- status %>%
group_by(state, bin) %>%
summarise(conversion_rate= round(mean(Converted)*100, 2)
,n = n()) #%>%
#arrange(desc(.$conversion_rate))
Conv_bin_state
### Visualize
### 7 states at a time
st <- sort(unique(Conv_bin_state$state))
for (k in 0:6){
i=1+k*7
if (k==6){
j=46
}
else{
j=7+k*7
}
p <- ggplot(data = Conv_bin_state[Conv_bin_state$state %in% st[i:j], ]
, aes(x = bin,
#x = reorder(bin, conversion_rate),
y = conversion_rate)) +
geom_point() +
geom_line(aes(group=state, color=state)) +
labs(x = 'Pricing Bin $'
,y = 'Conversion Rate %') +
ggtitle(paste("Conversion Rate per Pricing per States ", st[i], " - ", st[j]))
print(p)
}







Conversion trending does vary by state. Not all the states start and peak at the same price range. For example, CA, ME, ND, OK, VT, WY start from $440 - $599 range. OK peaks at $600 - 719 range. Some states have very small sample size (e.g. only 15 customers in DC) and absolute conversion of 100% is based on only 1 case.
Setting minimum premium should take factors like business class and state into consideration, because different business classes may have different risk levels to protect and different states may have different concentrations of business and regulations. Also, when looking at favorable pricing in terms of conversion, one needs to also look at the size of converted pool – are we talking about 9 cases out of 10 or 900 cases out of 1000 being converted? Basing minimum premium on sparse data can lead to unreliable results.
LS0tDQp0aXRsZTogIkRhdGEgQ2hhbGxlbmdlIC0gUHJpY2UgRWxhc3RpY2l0eSINCnN1YnRpdGxlOiB8DQogICAgfCBDb252ZXJzaW9uIEFuYWx5c2lzDQogICAgfCBPYmplY3RpdmU6ICAgQnVpbGQgcXVvdGUtdG8tcHVyY2hhc2UgY29udmVyc2lvbiBjdXJ2ZSBhcyBhIGZ1bmN0aW9uIG9mIHByaWNlDQojIGRhdGU6ICJgciBmb3JtYXQoU3lzLkRhdGUoKSwgJyVZLiVtLiVkJylgIg0KZGF0ZTogImByIGZvcm1hdChhcy5EYXRlKCcyMDE4LjEwLjE5JywgZm9ybWF0PSclWS4lbS4lZCcpLCAnJVkuJW0uJWQnKWAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpDQpgYGANCg0KYGBge3IgTGlicmFyeX0NCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoc3FsZGYpDQpsaWJyYXJ5KGFvZCkNCmBgYA0KDQojIyNSZWFkIGluIGRhdGENCmBgYHtyIFJlYWQgZGF0YX0NCiNnc3ViKCdcXFxcJywnLycscmVhZENsaXBib2FyZCgpKQ0Kc2V0d2QocmVhZENsaXBib2FyZCgpKQ0KZGYgPC1yZWFkLmNzdihnemZpbGUoJ2RhdGFfY2hhbGxlbmdlX3ByaWNlLmNzdi5neicsJ3J0JykNCiAgICAgICAgICAgICAgLCBoZWFkZXI9VA0KICAgICAgICAgICAgICAsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQ0Kc3RyKGRmKQ0KYGBgDQoNCiMjI1F1aWNrIGdsYW5jZQ0KYGBge3IgR2xhbmNlfQ0KYw0KbnJvdyh1bmlxdWUoZGZbMV0pKQ0Kc3VtbWFyeShkZiR5ZWFybHlfcHJlbWl1bSkNCmBgYA0KQnV6IElEOiAzMDY1MA0KQ29iX0lEOiA1DQpTdGF0ZTogNDYNCkJ1bmRsZTogNg0KDQojIyMgTm90IHN1cmUgd2h5IG9yaWdfcHJlbWl1bSBpcyBkb3VibGUtcXVvdGVkLCBjaGFuZ2UgdHlwZQ0KYGBge3J9DQpkZiRvcmlnX3ByZW1pdW0xIDwtIGFzLm51bWVyaWMoc3RyX3N1YihkZiRvcmlnX3ByZW1pdW0sIDIsIC0yKSkNCg0KIyNDaGVja2luZw0KeSA8LSBkZltpcy5uYShkZiRvcmlnX3ByZW1pdW0xKT09RiwgYygieWVhcmx5X3ByZW1pdW0iLCAib3JpZ19wcmVtaXVtIiwgIm9yaWdfcHJlbWl1bTEiKV0NCg0KeSA8LSB1bmlxdWUoeVtvcmRlcih5JG9yaWdfcHJlbWl1bTEpLF0pDQpoZWFkKHkpOyB0YWlsKHkpDQpgYGANCklmIG9yaWdfcHJlbWl1bSByZXByZXNlbnRzIHRoZSBwcmVtaXVtIGJlZm9yZSByb3VuZGluZyB1cCB0byB0aGUgbWluaW11bSwgc29tZSBwcmljZXMgc2VlbSB0byBiZSByaWRpY3Vsb3VzbHkgdG9vIGxvdyBmb3IgYSBzb3VuZCBwb2xpY3kuIFdlIHdvdWxkbid0IHVzZSBpdCB0byBjb25zdHJ1Y3QgcHJpY2UgZWxhc3RpY2l0eSBjdXJ2ZXMuDQoNCg0KIyMjSGlnaGx5IHNrZXdlZCB5ZWFybHkgcHJlbWl1bQ0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGRmW2RmJHllYXJseV9wcmVtaXVtID4gMTAwMDAsIF0sIGFlcyh4ID0geWVhcmx5X3ByZW1pdW0pKSArIA0KICBnZW9tX2hpc3RvZ3JhbShiaW5zPSA1MDAsIGNvbG9yID0gJ2JsYWNrJykgKw0KICBnZ3RpdGxlKCJZZWFybHkgUHJlbWl1bSA+ICQxMDAwMCIpDQpgYGANCg0KIyMjUHJlbWl1bSByYW5nZSBieSBzdGF0ZSwgY29iDQpgYGB7cn0NCnByZW1pdW1fcmFuZ2UgPC0NCiAgZGYgJT4lIGdyb3VwX2J5KHN0YXRlLCBjb2JfaWQpICU+JQ0KICAgICAgICAgc3VtbWFyaXNlKG1pblByZW0gPSBtaW4oeWVhcmx5X3ByZW1pdW0pDQogICAgICAgICAgICAgICAgICAgLG1heFByZW0gPSBtYXgoeWVhcmx5X3ByZW1pdW0pKQ0KDQojI0ZsdWN0dWF0aW9uIG9mIHN0YXRlLWNvYiBzcGVjaWZpYyBwcmVtaXVtIHJhbmdlDQpwcmVtaXVtX3JhbmdlICU+JSBncm91cF9ieShjb2JfaWQpICU+JQ0KICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKG1pbl9taW5QcmVtID0gbWluKG1pblByZW0pDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLG1heF9taW5QcmVtID0gbWF4KG1pblByZW0pDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLG1pbl9tYXhQcmVtID0gbWluKG1heFByZW0pDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgLG1heF9tYXhQcmVtID0gbWF4KG1heFByZW0pKQ0KDQpgYGANCk1pbmltdW0gcHJlbWl1bSBzZXQgdG8gJDM1MCwgZmx1Y3R1YXRlcyB+JDEwMCAtICQyMDAgYWNyb3NzIHN0YXRlcywgZGVwZW5kaW5nIG9uIGNvYi4NCg0KIyMjIFNlbGVjdCBwcmVtaXVtDQpgYGB7cn0NCiNnIDwtIGZ1bmN0aW9uKHgpIGMobiA9IGxlbmd0aCh4KSwgcXVhbnRpbGUoeCwgYygwLCAwLjI1LCAwLjUsIDAuNzUsIDAuOSwgMC45OSwgMSkpKQ0KI3RhcHBseShkZiR5ZWFybHlfcHJlbWl1bSwgZGYkc3RhdHVzX25hbWUsIGcpDQpkZiAlPiUgZ3JvdXBfYnkoc3RhdHVzX25hbWUpICU+JQ0KICAgICAgIHN1bW1hcmlzZShuID0gbigpDQogICAgICAgICAgICAgICAgICAsbWluID0gbWluKHllYXJseV9wcmVtaXVtKQ0KICAgICAgICAgICAgICAgICAgLHEyNSA9IHF1YW50aWxlKHllYXJseV9wcmVtaXVtLCAwLjI1KQ0KICAgICAgICAgICAgICAgICAscTUwID0gcXVhbnRpbGUoeWVhcmx5X3ByZW1pdW0sIDAuNSkNCiAgICAgICAgICAgICAgICAgLHE3NSA9IHF1YW50aWxlKHllYXJseV9wcmVtaXVtLCAwLjc1KQ0KICAgICAgICAgICAgICAgICAscTkwID0gcXVhbnRpbGUoeWVhcmx5X3ByZW1pdW0sIDAuOSkNCiAgICAgICAgICAgICAgICAgLHE5OSA9IHF1YW50aWxlKHllYXJseV9wcmVtaXVtLCAwLjk5KQ0KICAgICAgICAgICAgICAgICAsbWF4ID0gbWF4KHllYXJseV9wcmVtaXVtKQ0KICAgICkNCg0KDQojIyMgUGljayBsYXRlc3QgcXVvdGUgcGVyIHN0YXR1cw0KDQojICAgSWYgYW55IHF1b3RlIGlzIHB1cmNoYXNlZCAoc3RhdHVzIG9mIEFjdGl2ZSBvciBDYW5jZWxlZCksIHVzZSB0aGUgYXNzb2NpYXRlZCBwcmVtaXVtDQojIEVsc2UsIGlmIGFueSBxdW90ZSBpcyBzZWxlY3RlZCAobWVhbmluZyB0aGUgdXNlciBoYXMgc2VsZWN0ZWQgdGhhdCBwYWNrYWdlIGFuZCBjbGlja2VkIHRocm91Z2ggdG8gdGhlIHBheW1lbnQgc2NyZWVuKSwgdXNlIHRoZSBhc3NvY2lhdGVkIHByZW1pdW0gZnJvbSB0aGUgbGF0ZXN0IHF1b3RlDQojIEVsc2UsIHVzZSB0aGUgYXNzb2NpYXRlZCBwcmVtaXVtIGZyb20gdGhlIGxhdGVzdCBxdW90ZSBmb3IgdGhlIFBybyBidW5kbGUNCg0KIyMjIEFzc3VtZSBvbmUgc3RhdXMgcGVyIGJ1c2luZXNzIGlkIHJlZ2FyZGxlc3Mgb2Ygc3RhdGUNCmxhc3RfcXVvdGUgPC0gc3FsZGYoInNlbGVjdCAqLCBjYXNlIHdoZW4gc3RhdHVzX25hbWUgaW4gKCdBY3RpdmUnLCAnQ2FuY2VsZWQnKSB0aGVuIDENCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hlbiBzdGF0dXNfbmFtZSA9ICdTZWxlY3RlZCcgdGhlbiAyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdoZW4gc3RhdHVzX25hbWUgPSAnUXVvdGUnIGFuZCBidW5kbGVfbmFtZSA9ICdwcm8nIHRoZW4gMw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbHNlIDQgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZCBhcyBzDQogICAgICAgICAgICAgICAgICAgICAgICAgRnJvbSBkZg0KICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwIGJ5IGJ1c2luZXNzX2lkLCBzDQogICAgICAgICAgICAgICAgICAgICAgICAgaGF2aW5nIGNyZWF0aW9uX3RpbWUgPSBtYXgoY3JlYXRpb25fdGltZSkNCiAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlciBieSBidXNpbmVzc19pZCwgcyIpDQoNCiMjIyBDaGVjayBpZiBidXNpbmVzcyB3aXRoIG9ubHkgUXVvdGUgc3RhdHVzIGFsd2F5cyBoYXZlIHBybyBidW5kbGUNClF1b3RlIDwtIHNxbGRmKCJzZWxlY3QgKiBGcm9tIGxhc3RfcXVvdGUgd2hlcmUgYnVzaW5lc3NfaWQgbm90IGluDQogICAgICAgICAgICAgICAgKHNlbGVjdCBidXNpbmVzc19pZCBmcm9tIGxhc3RfcXVvdGUgd2hlcmUgcyBpbiAoMSwyKSkiKQ0KREZPIDwtIHNxbGRmKCJzZWxlY3QgYnVzaW5lc3NfaWQgZnJvbSBRdW90ZSB3aGVyZSBzdGF0dXNfbmFtZSA9ICdRdW90ZScgYW5kIGJ1bmRsZV9uYW1lICE9ICdwcm8nDQogICAgICAgICAgICAgIGV4Y2VwdA0KICAgICAgICAgICAgICBzZWxlY3QgYnVzaW5lc3NfaWQgZnJvbSBRdW90ZSB3aGVyZSBzdGF0dXNfbmFtZSA9ICdRdW90ZScgYW5kIGJ1bmRsZV9uYW1lID0gJ3BybyciKQ0KIyMjIC0+IDAgY291bnQsIFllcyENCg0KDQpzdGF0dXMgPC0gc3FsZGYoInNlbGVjdCAqDQogICAgICAgICAgICAgICAgLGNhc2Ugd2hlbiBzID0xIHRoZW4gMSBlbHNlIDAgZW5kIGFzIENvbnZlcnRlZA0KICAgICAgICAgICAgICAgIEZyb20gbGFzdF9xdW90ZQ0KICAgICAgICAgICAgICAgICAgZ3JvdXAgYnkgYnVzaW5lc3NfaWQNCiAgICAgICAgICAgICAgICAgIGhhdmluZyBzID0gbWluKHMpIikgIyAtLT4gZ2V0IGJhY2sgMzA2NTAgdW5pcXVlIGJ1c2luZXNzIGlkIQ0KdGFibGUoc3RhdHVzW3N0YXR1cyRzPT0zLCBdJGJ1bmRsZV9uYW1lKSAjLS0+IGlmIHN0YXR1cyA9IFF1b3RlLCBidW5kbGUgPSBwcm8hDQoNCnRhYmxlKHN0YXR1cyRzdGF0dXNfbmFtZSkNCmBgYA0KDQoNCiMjI0NvdmVyc2lvbiByYXRlDQpgYGB7cn0NCiMjIyBPdmVyYWxsDQpyb3VuZChwcm9wLnRhYmxlKHRhYmxlKHN0YXR1cyRDb252ZXJ0ZWQpKSoxMDAsIDIpDQojdGFwcGx5KHN0YXR1cyRDb252ZXJ0ZWQsIHN0YXR1cyRjb2JfaWQsIGZ1bmN0aW9uKHgpIHByb3AudGFibGUodGFibGUoeCkpKQ0KYGBgDQoyOCUgY29udmVydGVkIG92ZXJhbGwNCg0KIyMjIEJ5IGJ1c2luZXNzIGNsYXNzDQpgYGB7cn0NCg0KIyMjIFRhYnVsYXRlDQpzdGF0dXMgJT4lIGdyb3VwX2J5KGNvYl9pZCkgJT4lIHN1bW1hcmlzZSgnQ29udmVyc2lvbiUnID0gcm91bmQobWVhbihDb252ZXJ0ZWQpKjEwMCwgMikpICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoLiQnQ29udmVyc2lvbiUnKSkNCg0KIyMjIFN0YXRpc3RpY2FsIHRlc3QNCnN0YXR1cyRjb2JfaWQgPC0gZmFjdG9yKHN0YXR1cyRjb2JfaWQpDQpsb2dpdF9jb2IgPC0gZ2xtKENvbnZlcnRlZCB+IGNvYl9pZCwgZGF0YSA9IHN0YXR1cywgZmFtaWx5ID0gImJpbm9taWFsIikNCiNzdW1tYXJ5KGxvZ2l0X2NvYikNCg0Kd2FsZC50ZXN0KGIgPSBjb2VmKGxvZ2l0X2NvYiksIFNpZ21hID0gdmNvdihsb2dpdF9jb2IpLCBUZXJtcyA9IDI6NSkNCmBgYA0KQ29udmVyc2lvbiUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgYmV0d2VlbiBidXNpbmVzcyBjbGFzc2VzDQoNCiMjIyBCeSBTdGF0ZQ0KYGBge3IsIGZpZy53aWR0aD0xMH0NCiMjIyBUYWJ1bGF0ZQ0KQ29udl9TdGF0ZSA8LSBzdGF0dXMgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShzdGF0ZSkgJT4lIA0KICAgICAgICAgICAgICBzdW1tYXJpc2UoY29udmVyc2lvbl9yYXRlID0gcm91bmQobWVhbihDb252ZXJ0ZWQpKjEwMCwgMikpICU+JQ0KICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoLiRjb252ZXJzaW9uX3JhdGUpKQ0KDQpDb252X1N0YXRlIA0KDQojIyMgVmlzdWFsaXplDQpnZ3Bsb3QoZGF0YSA9IENvbnZfU3RhdGUsIGFlcyh4ID0gcmVvcmRlcihzdGF0ZSwgLWNvbnZlcnNpb25fcmF0ZSksIHkgPSBjb252ZXJzaW9uX3JhdGUpKSsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9saW5lKGdyb3VwPTEpICsgDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDApKSArDQogIGxhYnMoeCA9ICdTdGF0ZScpICsgDQogIGdndGl0bGUoIkNvbnZlcnNpb24gUmF0ZSBwZXIgU3RhdGUiKQ0KDQojIyMgU3RhdGlzdGljYWwgdGVzdA0KbG9naXRfc3RhdGUgPC0gZ2xtKENvbnZlcnRlZCB+IHN0YXRlLCBkYXRhID0gc3RhdHVzLCBmYW1pbHkgPSAiYmlub21pYWwiKQ0KI3N1bW1hcnkobG9naXRfc3RhdGUpDQoNCndhbGQudGVzdChiID0gY29lZihsb2dpdF9zdGF0ZSksIFNpZ21hID0gdmNvdihsb2dpdF9zdGF0ZSksIFRlcm1zID0gMjo0NikNCg0KYGBgIA0KQ29udmVyc2lvbiUgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgYmV0d2VlbiBzdGF0ZXMsIHJhbmdpbmcgZnJvbSAxMyUgdG8gNDglDQogIA0KDQoNCg0KIyMjIEJ5IHByaWNpbmcgYmluDQpgYGB7ciwgZmlnLndpZHRoPTEwfQ0KIyMjIERpc3RyaWJ1dGlvbiBvZiBwcmVtaXVtIHBlciBjb252ZXJ0ZWQgc3RhdHVzDQpnIDwtIGZ1bmN0aW9uKHgpIGMobiA9IGxlbmd0aCh4KSwgcXVhbnRpbGUoeCwgYygwLCAwLjI1LCAwLjUsIDAuNzUsIDAuOSwgMC45OSwgMSkpKQ0KdGFwcGx5KHN0YXR1cyR5ZWFybHlfcHJlbWl1bSwgc3RhdHVzJENvbnZlcnRlZCwgZykNCg0KIyMjIEhpZ2hseSBza2V3ZWQgZGlzdHJpYnV0aW9uLCB6b29tIGluIHRvIGp1c3QgcHJlbWl1bSA8ICQxNTAwIGFuZCBzZWUgaG93IGNvbnZlcnNpb24gdmFyaWVzDQoNCiMgZ2dwbG90KHN0YXR1c1tzdGF0dXMkeWVhcmx5X3ByZW1pdW0gPD0xNTAwLCBdLA0KIyAgICAgICAgYWVzKHg9YmluMTAsIGNvbG9yID1hcy5mYWN0b3IoQ29udmVydGVkKSkpICsNCiMgICBnZW9tX2hpc3RvZ3JhbShmaWxsPSJ3aGl0ZSIsIGJpbndpZHRoID0gMTAsIHN0YXQgPSAnY291bnQnKSArDQojICAgZ2d0aXRsZSgnQ29udmVyc2lvbiB2cyBQcmVtaXVtJykNCg0KIyMjIEN1dCBpbnRvICQxMCBpbmNyZW1lbnRzLCAxMTYgYnVja2V0cw0KY3V0b2ZmIDwtIHNlcSgzNTAsIDE1MDAsIDEwKQ0Kc3RhdHVzJGJpbjEwIDwtIGN1dChzdGF0dXMkeWVhcmx5X3ByZW1pdW0sIGJyZWFrcyA9IGN1dG9mZiwgcmlnaHQgPSBGLCBsYWJlbHMgPSBGKQ0Kc3RhdHVzW2lzLm5hKHN0YXR1cyRiaW4xMCksIF0kYmluMTAgPC0gMTE2DQoNCnggPC0gc3RhdHVzICU+JSBncm91cF9ieShiaW4xMCkgJT4lDQogICAgICAgICAgICAgICAgc3VtbWFyaXNlKG1pbiA9IG1pbih5ZWFybHlfcHJlbWl1bSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgLG1heCA9IG1heCh5ZWFybHlfcHJlbWl1bSkpDQoNCiN0YWlsKHgpDQoNCkNvbnZfYmluMTAgPC0gc3RhdHVzICU+JQ0KICAgICAgICAgICAgICBncm91cF9ieShiaW4xMCkgJT4lIA0KICAgICAgICAgICAgICBzdW1tYXJpc2UoY29udmVyc2lvbl9yYXRlID0gcm91bmQobWVhbihDb252ZXJ0ZWQpKjEwMCwgMikpDQoNCkNvbnZfYmluMTAgPC0gc3FsZGYoInNlbGVjdCB4LiosIGIuY29udmVyc2lvbl9yYXRlIA0KICAgICAgICAgICAgICAgICAgICBmcm9tIHggbGVmdCBqb2luIENvbnZfYmluMTAgYg0KICAgICAgICAgICAgICAgICAgICBvbiB4LmJpbjEwID0gYi5iaW4xMCIpDQpDb252X2JpbjEwDQoNCmdncGxvdChkYXRhPUNvbnZfYmluMTAsYWVzKHg9bWluLCB5PWNvbnZlcnNpb25fcmF0ZSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9saW5lKGdyb3VwPTEpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMzUwLDE1MDAsNTApKSArDQogIGxhYnMoeCA9ICdQcmVtaXVtIGluICQxMCBpbmNyZW1lbnQsIGxvd2VyIGJvdW5kJw0KICAgICAgICx5ID0gJ0NvbnZlcnNpb24gUmF0ZSAlJykgKw0KICBnZ3RpdGxlKCdDb252ZXJzaW9uIHZzIFByZW1pdW0nKQ0KYGBgDQpUaGlzIHByaWNlIGVsYXN0aWNpdHkgY3VydmUgbWFrZXMgc2Vuc2UgYXMgd2Ugd291bGQgZXhwZWN0IG1vcmUgcGVvcGxlIHRvIGJ1eSBhbiBpbnN1cmFuY2UgcG9saWN5IGF0IGEgbG93ZXIgcHJpY2UuDQoNCiMjIyBCYXNlZCBvbiB0aGUgbGluZSBncmFwaCwgcmUtYmluIHByZW1pdW0gaW50byBzbWFsbGVyIG51bWJlciBvZiBidWNrZXRzICANCmBgYHtyLCBmaWcud2lkdGg9MTB9DQptZWFuKHN0YXR1c1tzdGF0dXMkeWVhcmx5X3ByZW1pdW0gPj0zNTAgJiBzdGF0dXMkeWVhcmx5X3ByZW1pdW0gPDM2MCwgXSRDb252ZXJ0ZWQpICMzNC4wJQ0KbWVhbihzdGF0dXNbc3RhdHVzJHllYXJseV9wcmVtaXVtID49MzYwICYgc3RhdHVzJHllYXJseV9wcmVtaXVtIDw0NDAsIF0kQ29udmVydGVkKSAjNDAuNyUNCm1lYW4oc3RhdHVzW3N0YXR1cyR5ZWFybHlfcHJlbWl1bSA+PTQ0MCAmIHN0YXR1cyR5ZWFybHlfcHJlbWl1bSA8NjAwLCBdJENvbnZlcnRlZCkgIzM1LjQlDQptZWFuKHN0YXR1c1tzdGF0dXMkeWVhcmx5X3ByZW1pdW0gPj02MDAgJiBzdGF0dXMkeWVhcmx5X3ByZW1pdW0gPDcyMCwgXSRDb252ZXJ0ZWQpICMzMi43JQ0KbWVhbihzdGF0dXNbc3RhdHVzJHllYXJseV9wcmVtaXVtID49NzIwICYgc3RhdHVzJHllYXJseV9wcmVtaXVtIDwxMDAwLCBdJENvbnZlcnRlZCkgIzI4LjQNCm1lYW4oc3RhdHVzW3N0YXR1cyR5ZWFybHlfcHJlbWl1bSA+PTEwMDAgJiBzdGF0dXMkeWVhcmx5X3ByZW1pdW0gPDEzNjAsIF0kQ29udmVydGVkKSAjMjAuNCUNCm1lYW4oc3RhdHVzW3N0YXR1cyR5ZWFybHlfcHJlbWl1bSA+PTEzNjAsIF0kQ29udmVydGVkKSAjMTEuOSUNCg0KIyBnZ3Bsb3Qoc3RhdHVzW3N0YXR1cyR5ZWFybHlfcHJlbWl1bSA8PTE1MDAsIF0sDQojICAgICAgICBhZXMoeD15ZWFybHlfcHJlbWl1bSwgY29sb3IgPWFzLmZhY3RvcihDb252ZXJ0ZWQpKSkgKw0KIyAgIGdlb21faGlzdG9ncmFtKGZpbGw9IndoaXRlIiwgYmlud2lkdGggPSAxMCkgKw0KIyAgIGdndGl0bGUoJ0NvbnZlcnNpb24gdnMgUHJlbWl1bScpDQogIA0Kc3RhdHVzJGJpbiA8LSBpZmVsc2Uoc3RhdHVzJHllYXJseV9wcmVtaXVtID49MzUwICYgc3RhdHVzJHllYXJseV9wcmVtaXVtIDwzNjAsICcxLTM1MC0zNTknLA0KICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHN0YXR1cyR5ZWFybHlfcHJlbWl1bSA+PTM2MCAmIHN0YXR1cyR5ZWFybHlfcHJlbWl1bSA8NDQwLCAnMi0zNjAtNDM5JywNCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc3RhdHVzJHllYXJseV9wcmVtaXVtID49NDQwICYgc3RhdHVzJHllYXJseV9wcmVtaXVtIDw2MDAsICczLTQ0MC01OTknLA0KICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHN0YXR1cyR5ZWFybHlfcHJlbWl1bSA+PTYwMCAmIHN0YXR1cyR5ZWFybHlfcHJlbWl1bSA8NzIwLCAnNC02MDAtNzE5JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHN0YXR1cyR5ZWFybHlfcHJlbWl1bSA+PTcyMCAmIHN0YXR1cyR5ZWFybHlfcHJlbWl1bSA8MTAwMCwgJzUtNzIwLTk5OScsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzdGF0dXMkeWVhcmx5X3ByZW1pdW0gPj0xMDAwICYgc3RhdHVzJHllYXJseV9wcmVtaXVtIDwxMzYwLCAnNi0xMDAwLTEzNTknLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzdGF0dXMkeWVhcmx5X3ByZW1pdW0gPj0xMzYwLCAnNy0xMzYwKycsICdOQScpKSkpKSkpDQpzdGF0dXMkYmluIDwtZmFjdG9yKHN0YXR1cyRiaW4pDQoNCiMjI0NoZWNrIGJpbm5pbmcNCnRhcHBseShzdGF0dXMkeWVhcmx5X3ByZW1pdW0sIHN0YXR1cyRiaW4sIHJhbmdlKQ0KDQojIyMgVGFidWxhdGUNCkNvbnZfYmluIDwtIHN0YXR1cyAlPiUgDQogICAgICAgICAgICAgIGdyb3VwX2J5KGJpbikgJT4lIA0KICAgICAgICAgICAgICBzdW1tYXJpc2UoY29udmVyc2lvbl9yYXRlPSByb3VuZChtZWFuKENvbnZlcnRlZCkqMTAwLCAyKQ0KICAgICAgICAgICAgICAgICAgICAgICAgLG4gPSBuKCkpICU+JQ0KICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoLiRjb252ZXJzaW9uX3JhdGUpKQ0KDQpDb252X2JpbiANCg0KIyMjIFZpc3VhbGl6ZQ0KZ2dwbG90KGRhdGEgPSBDb252X2JpbiwgYWVzKHggPSBiaW4sDQogICAgICAgICAgICAjeCA9IHJlb3JkZXIoYmluMiwgY29udmVyc2lvbl9yYXRlKSwNCiAgICAgICAgICAgeSA9IGNvbnZlcnNpb25fcmF0ZSkpKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX2xpbmUoZ3JvdXA9MSkgKyANCiAgbGFicyh4ID0gJ1ByaWNpbmcgQmluICQnDQogICAgICAgLHkgPSAnQ29udmVyc2lvbiBSYXRlICUnKSArIA0KICBnZ3RpdGxlKCJDb252ZXJzaW9uIFJhdGUgcGVyIFByaWNpbmciKQ0KDQojIyMgU3RhdGlzdGljYWwgdGVzdA0KbG9naXRfYmluIDwtIGdsbShDb252ZXJ0ZWQgfiBiaW4sIGRhdGEgPSBzdGF0dXMsIGZhbWlseSA9ICJiaW5vbWlhbCIpDQojc3VtbWFyeShsb2dpdF9iaW4pDQp3YWxkLnRlc3QoYiA9IGNvZWYobG9naXRfYmluKSwgU2lnbWEgPSB2Y292KGxvZ2l0X2JpbiksIFRlcm1zID0gMjo3KQ0KYGBgDQpDb252ZXJzaW9uJSBnZW5lcmFsbHkgZHJvcHMgd2l0aCBpbmNyZWFzaW5nIHByZW1pdW0sIGFuZCBpcyBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBiZXR3ZWVuIHByaWNlIGJ1Y2tldHMuDQoNCkFsc28sIGhpZ2hlc3QgY29udmVyc2lvbiByYXRlIGhhcHBlbnMgYXQgJDM2MC00MzkuIERvZXMgaXQgc3VnZ3NldCByYWlzaW5nIHRoZSBtaW5pbXVtIHByZW1pdW0gZnJvbSAkMzUwIG9yIGlzIGl0IGp1c3QgYSBtaW5pbXVtIHByaWNlIHBlciBzdGF0ZSByZXF1aXJlbWVudCBmb3IgY2VydGFpbiBidXNpbmVzcyBjbGFzc2VzPw0KDQojIyMgRG9lcyBiaW5uaW5nIGVmZmVjdCBjaGFuZ2Ugd2l0aCBidXNpbmVzcyBjbGFzcz8NCmBgYHtyLCBmaWcud2lkdGg9MTB9DQpDb252X2Jpbl9jb2IgPC0gc3RhdHVzICU+JSANCiAgICAgICAgICAgICAgZ3JvdXBfYnkoY29iX2lkLCBiaW4pICU+JSANCiAgICAgICAgICAgICAgc3VtbWFyaXNlKGNvbnZlcnNpb25fcmF0ZT0gcm91bmQobWVhbihDb252ZXJ0ZWQpKjEwMCwgMikNCiAgICAgICAgICAgICAgICAgICAgICAgICxuID0gbigpKSAjJT4lDQogICAgICAgICAgICAgICNhcnJhbmdlKGRlc2MoLiRjb252ZXJzaW9uX3JhdGUpKQ0KDQpDb252X2Jpbl9jb2INCg0KIyMjIFZpc3VhbGl6ZQ0KZ2dwbG90KGRhdGEgPSBDb252X2Jpbl9jb2IsIGFlcyh4ID0gYmluLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjeCA9IHJlb3JkZXIoYmluMiwgY29udmVyc2lvbl9yYXRlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGNvbnZlcnNpb25fcmF0ZSkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgZ2VvbV9saW5lKGFlcyhncm91cD1jb2JfaWQsIGNvbG9yPWNvYl9pZCkpICsgDQogIGxhYnMoeCA9ICdQcmljaW5nIEJpbiAkJykgKyANCiAgZ2d0aXRsZSgiQ29udmVyc2lvbiBSYXRlIHBlciBQcmljaW5nIHBlciBDT0IiKQ0KDQpgYGANCkNvbnZlcnNpb24gdHJlbmRpbmcgZ2VuZXJhbGx5IGxvb2tzIHNpbWlsYXIgYWNyb3NzIGFsbCBidXNpbmVzcyBjbGFzc2VzIGV4Y2VwdCBmb3IgNTAwMyB0aGF0IHNob3dzIHRoZSBoaWdoZXN0IGNvbnZlcnNpb24gcmF0ZSBvZiAyNi4zJSBhdCAkMzUwLSQzNTkgcmFuZ2UuIEZvciBjbGFzcyAxMDAwMDEsIHByZW1pdW0gc3RhcnRzIGF0ICQ3MjAtJDk5OSByYW5nZS4gSXQncyBhbHNvIHRoZSBzYW1lIHJhbmdlIHdoZXJlIHRoZSBoaWdoZXN0IGNvbnZlcnNpb24gaGl0cy4NCg0KDQojIyMgRG9lcyBiaW5uaW5nIGVmZmVjdCBjaGFuZ2Ugd2l0aCBzdGF0ZT8NCmBgYHtyLCBmaWcud2lkdGg9MTB9DQpDb252X2Jpbl9zdGF0ZSA8LSBzdGF0dXMgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShzdGF0ZSwgYmluKSAlPiUgDQogICAgICAgICAgICAgIHN1bW1hcmlzZShjb252ZXJzaW9uX3JhdGU9IHJvdW5kKG1lYW4oQ29udmVydGVkKSoxMDAsIDIpDQogICAgICAgICAgICAgICAgICAgICAgICAsbiA9IG4oKSkgIyU+JQ0KICAgICAgICAgICAgICAjYXJyYW5nZShkZXNjKC4kY29udmVyc2lvbl9yYXRlKSkNCg0KQ29udl9iaW5fc3RhdGUgDQoNCiMjIyBWaXN1YWxpemUNCiMjIyA3IHN0YXRlcyBhdCBhIHRpbWUNCnN0IDwtIHNvcnQodW5pcXVlKENvbnZfYmluX3N0YXRlJHN0YXRlKSkNCmZvciAoayBpbiAwOjYpew0KICBpPTErayo3DQogIGlmIChrPT02KXsNCiAgICBqPTQ2DQogIH0NCiAgZWxzZXsNCiAgICAgIGo9NytrKjcNCiAgfQ0KcCA8LSBnZ3Bsb3QoZGF0YSA9IENvbnZfYmluX3N0YXRlW0NvbnZfYmluX3N0YXRlJHN0YXRlICVpbiUgc3RbaTpqXSwgXQ0KICAgICAgICAgICAgLCBhZXMoeCA9IGJpbiwNCiAgICAgICAgICAgICAgICAgICN4ID0gcmVvcmRlcihiaW4sIGNvbnZlcnNpb25fcmF0ZSksDQogICAgICAgICAgICAgICAgICB5ID0gY29udmVyc2lvbl9yYXRlKSkgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgZ2VvbV9saW5lKGFlcyhncm91cD1zdGF0ZSwgY29sb3I9c3RhdGUpKSArIA0KICAgIGxhYnMoeCA9ICdQcmljaW5nIEJpbiAkJw0KICAgICAgICAgLHkgPSAnQ29udmVyc2lvbiBSYXRlICUnKSArIA0KICAgIGdndGl0bGUocGFzdGUoIkNvbnZlcnNpb24gUmF0ZSBwZXIgUHJpY2luZyBwZXIgU3RhdGVzICIsIHN0W2ldLCAiIC0gIiwgc3Rbal0pKQ0KDQpwcmludChwKQ0KfQ0KYGBgDQoNCkNvbnZlcnNpb24gdHJlbmRpbmcgZG9lcyB2YXJ5IGJ5IHN0YXRlLiBOb3QgYWxsIHRoZSBzdGF0ZXMgc3RhcnQgYW5kIHBlYWsgYXQgdGhlIHNhbWUgcHJpY2UgcmFuZ2UuIEZvciBleGFtcGxlLCBDQSwgTUUsIE5ELCBPSywgVlQsIFdZIHN0YXJ0IGZyb20gJDQ0MCAtICQ1OTkgcmFuZ2UuIE9LIHBlYWtzIGF0ICQ2MDAgLSA3MTkgcmFuZ2UuDQpTb21lIHN0YXRlcyBoYXZlIHZlcnkgc21hbGwgc2FtcGxlIHNpemUgKGUuZy4gb25seSAxNSBjdXN0b21lcnMgaW4gREMpIGFuZCBhYnNvbHV0ZSBjb252ZXJzaW9uIG9mIDEwMCUgaXMgYmFzZWQgb24gb25seSAxIGNhc2UuDQoNClNldHRpbmcgbWluaW11bSBwcmVtaXVtIHNob3VsZCB0YWtlIGZhY3RvcnMgbGlrZSBidXNpbmVzcyBjbGFzcyBhbmQgc3RhdGUgaW50byBjb25zaWRlcmF0aW9uLCBiZWNhdXNlIGRpZmZlcmVudCBidXNpbmVzcyBjbGFzc2VzIG1heSBoYXZlIGRpZmZlcmVudCByaXNrIGxldmVscyB0byBwcm90ZWN0IGFuZCBkaWZmZXJlbnQgc3RhdGVzIG1heSBoYXZlIGRpZmZlcmVudCBjb25jZW50cmF0aW9ucyBvZiBidXNpbmVzcyBhbmQgcmVndWxhdGlvbnMuIEFsc28sIHdoZW4gbG9va2luZyBhdCBmYXZvcmFibGUgcHJpY2luZyBpbiB0ZXJtcyBvZiBjb252ZXJzaW9uLCBvbmUgbmVlZHMgdG8gYWxzbyBsb29rIGF0IHRoZSBzaXplIG9mIGNvbnZlcnRlZCBwb29sIC0tIGFyZSB3ZSB0YWxraW5nIGFib3V0IDkgY2FzZXMgb3V0IG9mIDEwIG9yIDkwMCBjYXNlcyBvdXQgb2YgMTAwMCBiZWluZyBjb252ZXJ0ZWQ/IEJhc2luZyBtaW5pbXVtIHByZW1pdW0gb24gc3BhcnNlIGRhdGEgY2FuIGxlYWQgdG8gdW5yZWxpYWJsZSByZXN1bHRzLg0KDQoNCg0K