rm(list = ls())

library(utf8)
library(cli)
library(crayon)
library(dplyr)
library(tidyr)
library(lubridate)
library(readxl)
library(sas7bdat)

1 Data processing

1.1 Get county-level incidence

#source("get.county.incidence0.R")
#nrow(incidence.by.county) # N = 728,944*

*N = 735,228

1.1.1 Import cumlative case counts

# when updating, change date range in line ~37
cases <- read.csv("inputs/hhs.protect.cases.091220.csv") 
nrow(cases) # N = 3,193 counties (cumulative cases stored in columns)
[1] 3193
ncol(cases) # N = 234* (4 region columns, so 234-4 = 230) days
[1] 238
cases.org <- cases

*N = 238

# calculate last day of dataset
data.day.1 <- ymd("2020/01/22")
data.day.1 + ncol(cases.org) - 4 - 1 # last date = 2020-09-11
[1] "2020-09-11"

1.1.2 Clean up data

colnames(cases.org)[1] <- "county.fips"
cases.org$county.fips <- as.character(cases.org$county.fips)

# add leading zero to county.fips with <6 digits
nrow(cases.org) # N = 3,193
[1] 3193
nrow(cases.org[nchar(cases.org$county.fips)==5, ]) # N = 2,869
[1] 2869
nrow(cases.org[nchar(cases.org$county.fips)==4, ]) # N = 324 FIPS with dropped leading zeros
[1] 324
cases.org[nchar(cases.org$county.fips)==4, ]$county.fips <-
  paste0("0", cases.org[nchar(cases.org$county.fips)==4, ]$county.fips)

nrow(cases.org[nchar(cases.org$county.fips)==5, ]) # N = 3,193
[1] 3193
cases.org

1.1.3 Calculate daily incidence

# convert from wide to long
cases.long <- cases.org %>%
  gather(key=date, n.cases, X1.22.2020:X9.11.2020) %>%
  group_by(county.fips) %>%
  mutate(lag.n.cases = dplyr::lag(n.cases)) %>%
  mutate(daily.n.cases = n.cases - lag.n.cases)
cases.long

1.1.4 Import county population data

# import county population data
county.p <- read.sas7bdat("inputs/uscounties2.sas7bdat", debug = FALSE)

nrow(county.p) # N = 3,142
[1] 3142
colnames(county.p)[3] <- "pop.2019"
colnames(county.p)[5] <- "county.fips"
colnames(county.p)[6] <- "msa"

county.p
#temp <- county.p %>% dplyr::select(County, State, county.fips, pop.2019)
#write.csv(temp, "all.county.fips.codes.csv")
county.pop <- county.p

# data cleaning
county.pop$county.fips <- as.character(county.pop$county.fips)

# add leading zeros to FIPs
nrow(county.pop) # N = 3,142
[1] 3142
nrow(county.pop[nchar(county.pop$county.fips)==5, ]) # N = 2,826
[1] 2826
nrow(county.pop[nchar(county.pop$county.fips)==4, ]) # N = 316, FIPs with dropped zeroes
[1] 316
county.pop[nchar(county.pop$county.fips)==4, ]$county.fips <-
  paste0("0",county.pop[nchar(county.pop$county.fips)==4, ]$county.fips)

nrow(county.pop[nchar(county.pop$county.fips)==5, ]) # N = 3,193
[1] 3142
# N = 3,142?
county.pop  %>%
  dplyr::select(county.fips, pop.2019, msa)

county.pop
# join case and population data
# calculate daily incidence / 100k population
incidence.by.county <- cases.long %>%
  left_join(county.pop, by = "county.fips") %>%
  mutate(n.case.per.100k = ((daily.n.cases/pop.2019) * 100000))
incidence.by.county
# calculate moving average over 7 days, as: average of cases over 7 days / 100k population
# using current day and averages over 6 lagged days
incidence.by.county <- incidence.by.county %>%
  group_by(county.fips) %>%
  mutate(lag1 = lag(daily.n.cases),
         lag2 = lag(daily.n.cases, 2),
         lag3 = lag(daily.n.cases, 3),
         lag4 = lag(daily.n.cases, 4),
         lag5 = lag(daily.n.cases, 5),
         lag6 = lag(daily.n.cases, 6),
         lag.avg.7day = ((daily.n.cases + lag1 + lag2 + lag3 + lag4 + lag5 + lag6)/7/pop.2019) * 100000)

# calculate moving average over 7 days, as: avg of cases over 7 days / 100k population
# calculate using current day and 7 leads
incidence.by.county <- incidence.by.county %>%
  group_by(county.fips) %>%
  mutate(lead1 = lead(daily.n.cases),
         lead2 = lead(daily.n.cases, 2),
         lead3 = lead(daily.n.cases, 3),
         lead4 = lead(daily.n.cases, 4),
         lead5 = lead(daily.n.cases, 5),
         lead6 = lead(daily.n.cases, 6),
         lead.avg.7day = ((daily.n.cases + lead1 + lead2 + lead3 + lead4 + lead5 + lead6)/7/pop.2019) * 100000)

incidence.by.county
# calculate moving average over 7 days, as: avg of cases over 7 days / 100k population
# calculate using current day and 7 leads
incidence.by.county <- incidence.by.county %>%
  group_by(county.fips) %>%
  mutate(avg.7day = ((daily.n.cases + lead1 + lead2 + lead3 + lag1 + lag2 + lag3)/7/pop.2019) * 100000)

incidence.by.county
# clean up data
incidence.by.county$date <- gsub("X", "", incidence.by.county$date)
incidence.by.county$date <- mdy(incidence.by.county$date)

# exclude cases that were not allocated to a county
# n = 50 county.fips codes designated for "unallocated" cases, for each state
incidence.by.county %>%
  ungroup() %>%
  select(county.fips) %>%
  distinct() %>%
  summarize('n.counties' = n()) # N = 3,193

nrow(incidence.by.county) # N = 734,390 ***(N = 747,162?)***
[1] 747162
incidence.by.county <- incidence.by.county[!grepl("Unallocated", incidence.by.county$county), ]

nrow(incidence.by.county) # N = 722,890 ***(N = 735,462?)***
[1] 735462
incidence.by.county %>%
  ungroup() %>%
  select(county.fips) %>%
  distinct() %>%
  summarize ('n.counties' = n()) # N = 3,143
# there is one county.fips code with no county name or data
incidence.by.county <- incidence.by.county %>%
  filter(county.fips != "02270")

nrow(incidence.by.county) # N = 722,660 ***(N = 735,228?)***
[1] 735228
incidence.by.county %>%
  ungroup() %>%
  select(county.fips) %>%
  distinct() %>%
  summarize ('n.counties' = n()) # N = 3,192 ***(N = 3,142?)***

# View(incidence.by.county)
# check <- sample(incidence.by.county$county.fips, size=1)
# View(incidence.by.county %>% filter(county.fips==check))

1.2 Get county-level hotspot status

#source("get.county.hs.status0.R")
#nrow(hs.counties) # N = 13,726
# import data
hs.counties <- read_excel("inputs/Emerging Counties Processed 2020-09-09.xlsx", sheet = "History")
nrow(hs.counties) # N = 13,726 for 9/9/2020 data
[1] 13726
str(hs.counties)
tibble [13,726 x 10] (S3: tbl_df/tbl/data.frame)
 $ Data Date               : POSIXct[1:13726], format: "2020-03-08" "2020-03-09" ...
 $ FIPS                    : num [1:13726] 53033 53033 36119 53033 36119 ...
 $ County                  : chr [1:13726] "King County" "King County" "Westchester County" "King County" ...
 $ State                   : chr [1:13726] "WA" "WA" "NY" "WA" ...
 $ FEMA Region             : num [1:13726] 10 10 2 10 2 10 9 2 10 9 ...
 $ CBSA Code               : num [1:13726] 42660 42660 35620 42660 35620 ...
 $ CBSA                    : chr [1:13726] "Seattle-Tacoma-Bellevue, WA" "Seattle-Tacoma-Bellevue, WA" "New York-Newark-Jersey City, NY-NJ-PA" "Seattle-Tacoma-Bellevue, WA" ...
 $ Last on List - Data Date: POSIXct[1:13726], format: NA "2020-03-08" ...
 $ Days Since Last on List : num [1:13726] NA 1 NA 1 1 1 NA 1 1 1 ...
 $ New by CDC Definition   : logi [1:13726] TRUE FALSE TRUE FALSE FALSE FALSE ...
# convert to date format
colnames(hs.counties)[1] <- "data.date"
hs.counties$data.date <- ymd(hs.counties$data.date)

# convert fips code to character
colnames(hs.counties)[2] <- "county.fips"
hs.counties$county.fips <- as.character(hs.counties$county.fips)

# add leading zeros
nrow(hs.counties) # N = 13,560 ***(N = 13,726?)***
[1] 13726
nrow(hs.counties[nchar(hs.counties$county.fips)==5, ]) # N = 11,482 ***(N = 11,640?)***
[1] 11640
nrow(hs.counties[nchar(hs.counties$county.fips)==4, ]) # N = 2,078, ***(N = 2,086?) counties with dropped leading zeros in fips codes
[1] 2086
hs.counties[nchar(hs.counties$county.fips)==4, ]$county.fips <- paste0("0", hs.counties[nchar(hs.counties$county.fips)==4, ]$county.fips)
nrow(hs.counties[nchar(hs.counties$county.fips)==5, ]) # N = 13,560 ***(N = 13,726?)***
[1] 13726
# convert date format
colnames(hs.counties)[10] <- "new.cdc"

# add indicator for newly identified HS
hs.counties$new.hs <- 0
hs.counties[!is.na(hs.counties$new.cdc) & hs.counties$new.cdc == TRUE, "new.hs"] <- 1

# add indicator variable to differentiate HS counties after merge
hs.counties$hs.status <- 1

# limit dataset to fips code and hs status
hs.counties <- hs.counties %>%
  select(county.fips, data.date, hs.status, new.cdc, new.hs)
hs.counties

1.3 Get IHE county abstracted data

#source("get.abstracted.data0.R")
#nrow(ihe.data) # N = 133
#table(ihe.data$open.type)
# import data
ihe.data <- read_excel("inputs/ihe.hotspots.abstraction.091320.xlsx", sheet = "university.sample.082620")
nrow(ihe.data) # N = 152 for 9/13/2020 data
[1] 152
# convert fips to character, add leading zeros
ihe.data$county.fips <- as.character(ihe.data$county.fips)

# add a leading zero to any county.fips that only have 6 digits
nrow(ihe.data) # N = 152
[1] 152
nrow(ihe.data[nchar(ihe.data$county.fips)==5, ]) # N = 119
[1] 119
nrow(ihe.data[nchar(ihe.data$county.fips)==4, ]) # N = 33, counties with dropped leading zeros
[1] 33
ihe.data[nchar(ihe.data$county.fips)==4, ]$county.fips <-
  paste0("0", ihe.data[nchar(ihe.data$county.fips)==4, ]$county.fips)

nrow(ihe.data[nchar(ihe.data$county.fips)==5, ]) # N = 152
[1] 152
# add variable indicating IHE after merge
ihe.data$ihe.status <- 1

# recast dates
ihe.data$open.date <- ymd(ihe.data$open.date)

# exclude observations
ihe.data <- ihe.data %>%
  filter(include == 1)

nrow(ihe.data) # N = 149
[1] 149
ihe.data
# finalize dataset for use in next step
#ihe.data <- ihe.data %>%
#  select(ihe.status, unitid, county.fips, include, open.date, open.type)

#str(ihe.data)
#nrow(ihe.data) # N = 149

1.3.1 Identify counties with multiple IHEs, choose the earliest opener

#ihe.data <- ihe.data %>%
#  group_by(county.fips) %>%
#  filter(open.date == min(open.date))

#nrow(ihe.data) # N = 134 (16 were dropped because they shared a county with another school)

## there is one county w 2 IHEs with identical open.dates and open.type: 06037
#ihe.data <- ihe.data %>%
#  group_by(county.fips) %>%
#  slice(1) 

#nrow(ihe.data) # N = 133, (1 more dropped for a total of 17)
#sum(ihe.data$ihe.status)
# get some basic info about the data
#ihe.data %>%
#  ungroup %>%
#  select(county.fips) %>%
#  distinct() %>%
#  summarize ('n.counties' = n()) # N = 133

#median(ihe.data$open.date) # 2020-08-24

1.3.2 Target end data date to use half the data and get 3 weeks of observation

#median(ihe.data$open.date) + 21 # 2020-09-14
#min(ihe.data$open.date) # 2020-07-27
#mean(ihe.data$open.date) # 2020-08-25
#max(ihe.data$open.date) # 2020-09-30

1.4 Merge county-level indicators (msa, pop) and IHE status

#source("get.merged.all.county.data0.R")
#nrow(all.county.data) # N = 3,142
# join the two datasets
all.county.data <- left_join(county.pop, ihe.data, by = "county.fips")

# assign ihs=0 to counties with no IHE
all.county.data[is.na(all.county.data$ihe.status), "ihe.status"] <- 0

# get median and set as open.date for ihe==0 counties
median.date <- median(all.county.data$open.date, na.rm = TRUE)
all.county.data$open.date.v1 <- all.county.data$open.date # copy over IHE county open.dates
all.county.data[all.county.data$ihe.status == 0, "open.date.v1"] <- median.date # replace NA with median value

# drop the include variable
all.county.data <- all.county.data %>%
  select(-matches("include"))
# check basic info
sum(all.county.data$ihe.status) # N = 133
[1] 149
all.county.data %>%
  select(county.fips) %>%
  distinct() %>%
  summarize ('n.counties' = n()) # N = 133 ***(N = 3,142?)***

table(all.county.data$open.date)

2020-07-27 2020-08-10 2020-08-12 2020-08-17 2020-08-18 2020-08-19 2020-08-20 
         1          3          1         23          1          7          4 
2020-08-22 2020-08-24 2020-08-25 2020-08-26 2020-08-27 2020-08-29 2020-08-31 
         4         52          3          6          1          2         10 
2020-09-01 2020-09-02 2020-09-07 2020-09-08 2020-09-09 2020-09-14 2020-09-16 
         5          7          1          2          2          1          1 
2020-09-19 2020-09-23 2020-09-27 2020-09-28 2020-09-29 2020-09-30 
         1          2          1          6          1          1 
### write file to send to GRASP (and in general)
#write.csv(all.county.data, "outputs/ihe.hotspots.all.counties.csv")
write.csv(all.county.data, "outputs/ihe.hotspots.all.counties2.csv")
# drop msa type and population counts, because they'll be brought in with the incidence file
# also drop open.date.v1, since we'll assign that in analysis file based on subsample used in analysis
all.county.data_1 <- all.county.data %>%
  select(-matches(c("msa", "pop.2019", "open.date.v1")))
# NOTE: merged county info will be exported to GRASP to return matches
# merge incidence and hotspot data
temp <- left_join(incidence.by.county,hs.counties,
                  by = c("county.fips"="county.fips",
                         "date" = "data.date"))

# merge incidence/hotspot and ihe abstraction data
temp <- left_join(temp, all.county.data,
                  by = "county.fips")
# check a random county
#check <- sample(temp$county.fips, size=1)
#View(temp %>% filter(county.fips==check))
# check brazos county tx
#View(temp %>% filter(county.fips=="48041"))
#View(incidence.by.county)
###### save big file #######
#write.csv(temp, "outputs/df_merge.csv")

2 Analysis

# bring in data from merges
a.data <- temp
# simple diagnostics on data
# counties in dataset
a.data %>%
    ungroup %>%
    select(county.fips) %>%
    distinct() %>%
    summarize ('n.counties' = n()) # N = 3,142
# counties with no IHEs
a.data %>%
    ungroup %>%
    filter(ihe.status == 0) %>%
    select(county.fips) %>%
    distinct() %>%
    summarize ('n.counties' = n()) # N = 3,009
# counties with IHEs
a.data %>%
    ungroup %>%
    filter(ihe.status == 1) %>%
    select(county.fips) %>%
    distinct() %>%
    summarize ('n.counties' = n()) # N = 133
# count by ihe.status
(t0.a <- a.data %>% 
    select(county.fips, ihe.status) %>%
    distinct() %>%
    group_by(ihe.status) %>%
    summarize(count = n()))
# count by open.type
(t0.b <- a.data %>% 
    select(county.fips, ihe.status, open.type) %>%
    distinct() %>%
    group_by(ihe.status, open.type) %>%
    summarize(count = n()) %>%
    tidyr::spread(key = open.type, value = count))
# open dates
mean(a.data$open.date, na.rm = TRUE)
[1] "2020-08-26"
median(a.data$open.date, na.rm = TRUE)
[1] "2020-08-24"
# make exclusions
# exclude data from the first part of the year, drop all before June 1st
a.data <- a.data %>%
    filter((date >= mdy("6/1/2020")))

nrow(a.data) # N = 311,157 ***(N = 323,626)
[1] 325274
# counties in dataset
a.data %>%
    ungroup %>%
    select(county.fips) %>%
    distinct() %>%
    summarize ('n.counties' = n()) # N = 3,142
# exclude any IHE counties that have open date after cutoff (8/21/2020)
# cut off date is 8/24/2020, -14 will allow for calc 7 day average at day +11
cut.off.date <- mdy("9/11/2020") - 14

# drop IHE counties with open date after cut.off.date
a.data <- a.data %>%
    filter(ihe.status == 0 | open.date <= cut.off.date)

# total counties in dataset
a.data %>%
    ungroup %>%
    select(county.fips) %>%
    distinct() %>%
    summarize ('n.counties' = n()) # N = 3100? ***(N = 3,110)***
# count by ihe.status
(t0.a <- a.data %>% 
    select(county.fips, ihe.status) %>%
    distinct() %>%
    group_by(ihe.status) %>%
    summarize(count = n()))
# count by open.type
(t0.b <- a.data %>% 
    select(county.fips, ihe.status, open.type) %>%
    distinct() %>%
    group_by(ihe.status, open.type) %>%
    summarize(count = n()) %>%
    tidyr::spread(key = open.type, value = count))
# open dates
mean(a.data$open.date, na.rm = TRUE)
[1] "2020-08-21"
median(a.data$open.date, na.rm = TRUE)
[1] "2020-08-24"
# 1.0 make any assumptions
# 1.1 establish the open date for any non-IHE counties
median.o.date <- median(a.data$open.date, na.rm = TRUE)

a.data$open.date.v2 <- a.data$open.date # copy over open.date for IHE counties
a.data[a.data$ihe.status == 0, "open.date.v2"] <- median.o.date

# 1.2 assign day 0
a.data$days.from.open.date <- a.data$date - a.data$open.date.v2

# 1.3 remove any data outside observation window
a.data <- a.data %>%
    filter(days.from.open.date >= -28)

a.data %>%
    ungroup %>%
    select(county.fips) %>%
    distinct() %>%
    summarize ('n.counties' = n()) # N = 3100 ***(N = 3,110***)
nrow(a.data) # N = 80895 ***(N = 146,447)***
[1] 146695

2.0.1 Calculate results

# counties with IHEs in MSA = 1
a.data %>%
    ungroup %>%
    filter(ihe.status == 1) %>%
    select(county.fips) %>%
    distinct() %>%
    summarize ('n.counties' = n()) # N = 91 ***(N = 101)***
# counties with IHEs in MSA = 1
a.data %>%
    ungroup %>%
    filter(ihe.status == 0) %>%
    select(county.fips) %>%
    distinct() %>%
    summarize ('n.counties' = n()) # N = 91 ***(N = 3009)***

2.0.2 Prelim results

# set the windows of observation
looks <- c(-21, -14, -7, 0, 7, 14)

# means
(t1.a <- a.data %>%
        filter(days.from.open.date %in% looks) %>%
        group_by(ihe.status, days.from.open.date) %>%
        summarize(avg = mean(avg.7day, na.rm = TRUE)) %>%
        tidyr::spread(key = days.from.open.date, value = avg))
# medians
(t1.a <- a.data %>%
        filter(days.from.open.date %in% looks) %>%
        group_by(ihe.status, days.from.open.date) %>%
        summarize(median = median(avg.7day, na.rm = TRUE)) %>%
        tidyr::spread(key = days.from.open.date, value = median))
# counts
(t1.a <- a.data %>%
        filter(days.from.open.date %in% looks) %>%
        group_by(ihe.status, days.from.open.date) %>%
        summarize(count = n()) %>%
        tidyr::spread(key = days.from.open.date, value = count))
# hotspots, counts
# generate simple before/after variable
a.data$after.day.0 <- a.data$days.from.open.date > 0

# counts of new hotspots
(t2.a <- a.data %>%
        group_by(ihe.status, after.day.0) %>%
        summarize(count = sum(new.hs, na.rm = TRUE)) %>%
        tidyr::spread(key = after.day.0, value = count))
# counts of total counties
(t2.b <- a.data %>%
        group_by(ihe.status, after.day.0) %>%
        distinct(county.fips) %>%
        summarize(count = n()) %>%
        tidyr::spread(key = after.day.0, value = count))
# proportions of counties w hotspots
# before
101/3009
[1] 0.03356597
11/101
[1] 0.1089109
# after
151/3009
[1] 0.05018278
32/101
[1] 0.3168317
# means by ihe status and open type
(t1.a <- a.data %>%
     filter(days.from.open.date %in% looks) %>%
     group_by(ihe.status, open.type, days.from.open.date) %>%
     summarize(avg = mean(avg.7day, na.rm = TRUE)) %>%
     tidyr::spread(key = days.from.open.date, value = avg))
LS0tDQp0aXRsZTogIm5vdGVib29rIg0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazogDQogICAgaGlnaGxpZ2h0OiBweWdtZW50cw0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRoZW1lOiBjb3Ntbw0KICAgIGtlZXBfbWQ6IHllcw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KCWVjaG8gPSBUUlVFLA0KCW1lc3NhZ2UgPSBGQUxTRSwNCgl3YXJuaW5nID0gRkFMU0UsDQoJaW5jbHVkZSA9IFRSVUUNCikNCmBgYA0KDQpgYGB7cn0NCnJtKGxpc3QgPSBscygpKQ0KDQpsaWJyYXJ5KHV0ZjgpDQpsaWJyYXJ5KGNsaSkNCmxpYnJhcnkoY3JheW9uKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkocmVhZHhsKQ0KbGlicmFyeShzYXM3YmRhdCkNCmBgYA0KDQojIERhdGEgcHJvY2Vzc2luZw0KDQojIyBHZXQgY291bnR5LWxldmVsIGluY2lkZW5jZQ0KDQoNCmBgYHtyfQ0KI3NvdXJjZSgiZ2V0LmNvdW50eS5pbmNpZGVuY2UwLlIiKQ0KI25yb3coaW5jaWRlbmNlLmJ5LmNvdW50eSkgIyBOID0gNzI4LDk0NCoNCmBgYA0KKk4gPSA3MzUsMjI4DQoNCiMjIyBJbXBvcnQgY3VtbGF0aXZlIGNhc2UgY291bnRzDQoNCmBgYHtyfQ0KIyB3aGVuIHVwZGF0aW5nLCBjaGFuZ2UgZGF0ZSByYW5nZSBpbiBsaW5lIH4zNw0KY2FzZXMgPC0gcmVhZC5jc3YoImlucHV0cy9oaHMucHJvdGVjdC5jYXNlcy4wOTEyMjAuY3N2IikgDQpucm93KGNhc2VzKSAjIE4gPSAzLDE5MyBjb3VudGllcyAoY3VtdWxhdGl2ZSBjYXNlcyBzdG9yZWQgaW4gY29sdW1ucykNCm5jb2woY2FzZXMpICMgTiA9IDIzNCogKDQgcmVnaW9uIGNvbHVtbnMsIHNvIDIzNC00ID0gMjMwKSBkYXlzDQoNCmNhc2VzLm9yZyA8LSBjYXNlcw0KYGBgDQoqTiA9IDIzOA0KDQpgYGB7cn0NCiMgY2FsY3VsYXRlIGxhc3QgZGF5IG9mIGRhdGFzZXQNCmRhdGEuZGF5LjEgPC0geW1kKCIyMDIwLzAxLzIyIikNCmRhdGEuZGF5LjEgKyBuY29sKGNhc2VzLm9yZykgLSA0IC0gMSAjIGxhc3QgZGF0ZSA9IDIwMjAtMDktMTENCmBgYA0KDQojIyMgQ2xlYW4gdXAgZGF0YQ0KDQpgYGB7cn0NCmNvbG5hbWVzKGNhc2VzLm9yZylbMV0gPC0gImNvdW50eS5maXBzIg0KY2FzZXMub3JnJGNvdW50eS5maXBzIDwtIGFzLmNoYXJhY3RlcihjYXNlcy5vcmckY291bnR5LmZpcHMpDQoNCiMgYWRkIGxlYWRpbmcgemVybyB0byBjb3VudHkuZmlwcyB3aXRoIDw2IGRpZ2l0cw0KbnJvdyhjYXNlcy5vcmcpICMgTiA9IDMsMTkzDQpucm93KGNhc2VzLm9yZ1tuY2hhcihjYXNlcy5vcmckY291bnR5LmZpcHMpPT01LCBdKSAjIE4gPSAyLDg2OQ0KbnJvdyhjYXNlcy5vcmdbbmNoYXIoY2FzZXMub3JnJGNvdW50eS5maXBzKT09NCwgXSkgIyBOID0gMzI0IEZJUFMgd2l0aCBkcm9wcGVkIGxlYWRpbmcgemVyb3MNCg0KY2FzZXMub3JnW25jaGFyKGNhc2VzLm9yZyRjb3VudHkuZmlwcyk9PTQsIF0kY291bnR5LmZpcHMgPC0NCiAgcGFzdGUwKCIwIiwgY2FzZXMub3JnW25jaGFyKGNhc2VzLm9yZyRjb3VudHkuZmlwcyk9PTQsIF0kY291bnR5LmZpcHMpDQoNCm5yb3coY2FzZXMub3JnW25jaGFyKGNhc2VzLm9yZyRjb3VudHkuZmlwcyk9PTUsIF0pICMgTiA9IDMsMTkzDQpgYGANCg0KYGBge3J9DQpjYXNlcy5vcmcNCmBgYA0KDQojIyMgQ2FsY3VsYXRlIGRhaWx5IGluY2lkZW5jZQ0KDQpgYGB7cn0NCiMgY29udmVydCBmcm9tIHdpZGUgdG8gbG9uZw0KY2FzZXMubG9uZyA8LSBjYXNlcy5vcmcgJT4lDQogIGdhdGhlcihrZXk9ZGF0ZSwgbi5jYXNlcywgWDEuMjIuMjAyMDpYOS4xMS4yMDIwKSAlPiUNCiAgZ3JvdXBfYnkoY291bnR5LmZpcHMpICU+JQ0KICBtdXRhdGUobGFnLm4uY2FzZXMgPSBkcGx5cjo6bGFnKG4uY2FzZXMpKSAlPiUNCiAgbXV0YXRlKGRhaWx5Lm4uY2FzZXMgPSBuLmNhc2VzIC0gbGFnLm4uY2FzZXMpDQpjYXNlcy5sb25nDQpgYGANCg0KIyMjIEltcG9ydCBjb3VudHkgcG9wdWxhdGlvbiBkYXRhDQoNCmBgYHtyfQ0KIyBpbXBvcnQgY291bnR5IHBvcHVsYXRpb24gZGF0YQ0KY291bnR5LnAgPC0gcmVhZC5zYXM3YmRhdCgiaW5wdXRzL3VzY291bnRpZXMyLnNhczdiZGF0IiwgZGVidWcgPSBGQUxTRSkNCg0KbnJvdyhjb3VudHkucCkgIyBOID0gMywxNDINCg0KY29sbmFtZXMoY291bnR5LnApWzNdIDwtICJwb3AuMjAxOSINCmNvbG5hbWVzKGNvdW50eS5wKVs1XSA8LSAiY291bnR5LmZpcHMiDQpjb2xuYW1lcyhjb3VudHkucClbNl0gPC0gIm1zYSINCg0KY291bnR5LnANCiN0ZW1wIDwtIGNvdW50eS5wICU+JSBkcGx5cjo6c2VsZWN0KENvdW50eSwgU3RhdGUsIGNvdW50eS5maXBzLCBwb3AuMjAxOSkNCiN3cml0ZS5jc3YodGVtcCwgImFsbC5jb3VudHkuZmlwcy5jb2Rlcy5jc3YiKQ0KYGBgDQoNCmBgYHtyfQ0KY291bnR5LnBvcCA8LSBjb3VudHkucA0KDQojIGRhdGEgY2xlYW5pbmcNCmNvdW50eS5wb3AkY291bnR5LmZpcHMgPC0gYXMuY2hhcmFjdGVyKGNvdW50eS5wb3AkY291bnR5LmZpcHMpDQoNCiMgYWRkIGxlYWRpbmcgemVyb3MgdG8gRklQcw0KbnJvdyhjb3VudHkucG9wKSAjIE4gPSAzLDE0Mg0KbnJvdyhjb3VudHkucG9wW25jaGFyKGNvdW50eS5wb3AkY291bnR5LmZpcHMpPT01LCBdKSAjIE4gPSAyLDgyNg0KbnJvdyhjb3VudHkucG9wW25jaGFyKGNvdW50eS5wb3AkY291bnR5LmZpcHMpPT00LCBdKSAjIE4gPSAzMTYsIEZJUHMgd2l0aCBkcm9wcGVkIHplcm9lcw0KDQpjb3VudHkucG9wW25jaGFyKGNvdW50eS5wb3AkY291bnR5LmZpcHMpPT00LCBdJGNvdW50eS5maXBzIDwtDQogIHBhc3RlMCgiMCIsY291bnR5LnBvcFtuY2hhcihjb3VudHkucG9wJGNvdW50eS5maXBzKT09NCwgXSRjb3VudHkuZmlwcykNCg0KbnJvdyhjb3VudHkucG9wW25jaGFyKGNvdW50eS5wb3AkY291bnR5LmZpcHMpPT01LCBdKSAjIE4gPSAzLDE5Mw0KIyBOID0gMywxNDI/DQpgYGANCg0KYGBge3J9DQpjb3VudHkucG9wICAlPiUNCiAgZHBseXI6OnNlbGVjdChjb3VudHkuZmlwcywgcG9wLjIwMTksIG1zYSkNCg0KY291bnR5LnBvcA0KYGBgDQoNCmBgYHtyfQ0KIyBqb2luIGNhc2UgYW5kIHBvcHVsYXRpb24gZGF0YQ0KIyBjYWxjdWxhdGUgZGFpbHkgaW5jaWRlbmNlIC8gMTAwayBwb3B1bGF0aW9uDQppbmNpZGVuY2UuYnkuY291bnR5IDwtIGNhc2VzLmxvbmcgJT4lDQogIGxlZnRfam9pbihjb3VudHkucG9wLCBieSA9ICJjb3VudHkuZmlwcyIpICU+JQ0KICBtdXRhdGUobi5jYXNlLnBlci4xMDBrID0gKChkYWlseS5uLmNhc2VzL3BvcC4yMDE5KSAqIDEwMDAwMCkpDQppbmNpZGVuY2UuYnkuY291bnR5DQpgYGANCg0KYGBge3J9DQojIGNhbGN1bGF0ZSBtb3ZpbmcgYXZlcmFnZSBvdmVyIDcgZGF5cywgYXM6IGF2ZXJhZ2Ugb2YgY2FzZXMgb3ZlciA3IGRheXMgLyAxMDBrIHBvcHVsYXRpb24NCiMgdXNpbmcgY3VycmVudCBkYXkgYW5kIGF2ZXJhZ2VzIG92ZXIgNiBsYWdnZWQgZGF5cw0KaW5jaWRlbmNlLmJ5LmNvdW50eSA8LSBpbmNpZGVuY2UuYnkuY291bnR5ICU+JQ0KICBncm91cF9ieShjb3VudHkuZmlwcykgJT4lDQogIG11dGF0ZShsYWcxID0gbGFnKGRhaWx5Lm4uY2FzZXMpLA0KICAgICAgICAgbGFnMiA9IGxhZyhkYWlseS5uLmNhc2VzLCAyKSwNCiAgICAgICAgIGxhZzMgPSBsYWcoZGFpbHkubi5jYXNlcywgMyksDQogICAgICAgICBsYWc0ID0gbGFnKGRhaWx5Lm4uY2FzZXMsIDQpLA0KICAgICAgICAgbGFnNSA9IGxhZyhkYWlseS5uLmNhc2VzLCA1KSwNCiAgICAgICAgIGxhZzYgPSBsYWcoZGFpbHkubi5jYXNlcywgNiksDQogICAgICAgICBsYWcuYXZnLjdkYXkgPSAoKGRhaWx5Lm4uY2FzZXMgKyBsYWcxICsgbGFnMiArIGxhZzMgKyBsYWc0ICsgbGFnNSArIGxhZzYpLzcvcG9wLjIwMTkpICogMTAwMDAwKQ0KDQojIGNhbGN1bGF0ZSBtb3ZpbmcgYXZlcmFnZSBvdmVyIDcgZGF5cywgYXM6IGF2ZyBvZiBjYXNlcyBvdmVyIDcgZGF5cyAvIDEwMGsgcG9wdWxhdGlvbg0KIyBjYWxjdWxhdGUgdXNpbmcgY3VycmVudCBkYXkgYW5kIDcgbGVhZHMNCmluY2lkZW5jZS5ieS5jb3VudHkgPC0gaW5jaWRlbmNlLmJ5LmNvdW50eSAlPiUNCiAgZ3JvdXBfYnkoY291bnR5LmZpcHMpICU+JQ0KICBtdXRhdGUobGVhZDEgPSBsZWFkKGRhaWx5Lm4uY2FzZXMpLA0KICAgICAgICAgbGVhZDIgPSBsZWFkKGRhaWx5Lm4uY2FzZXMsIDIpLA0KICAgICAgICAgbGVhZDMgPSBsZWFkKGRhaWx5Lm4uY2FzZXMsIDMpLA0KICAgICAgICAgbGVhZDQgPSBsZWFkKGRhaWx5Lm4uY2FzZXMsIDQpLA0KICAgICAgICAgbGVhZDUgPSBsZWFkKGRhaWx5Lm4uY2FzZXMsIDUpLA0KICAgICAgICAgbGVhZDYgPSBsZWFkKGRhaWx5Lm4uY2FzZXMsIDYpLA0KICAgICAgICAgbGVhZC5hdmcuN2RheSA9ICgoZGFpbHkubi5jYXNlcyArIGxlYWQxICsgbGVhZDIgKyBsZWFkMyArIGxlYWQ0ICsgbGVhZDUgKyBsZWFkNikvNy9wb3AuMjAxOSkgKiAxMDAwMDApDQoNCmluY2lkZW5jZS5ieS5jb3VudHkNCmBgYA0KDQpgYGB7cn0NCiMgY2FsY3VsYXRlIG1vdmluZyBhdmVyYWdlIG92ZXIgNyBkYXlzLCBhczogYXZnIG9mIGNhc2VzIG92ZXIgNyBkYXlzIC8gMTAwayBwb3B1bGF0aW9uDQojIGNhbGN1bGF0ZSB1c2luZyBjdXJyZW50IGRheSBhbmQgNyBsZWFkcw0KaW5jaWRlbmNlLmJ5LmNvdW50eSA8LSBpbmNpZGVuY2UuYnkuY291bnR5ICU+JQ0KICBncm91cF9ieShjb3VudHkuZmlwcykgJT4lDQogIG11dGF0ZShhdmcuN2RheSA9ICgoZGFpbHkubi5jYXNlcyArIGxlYWQxICsgbGVhZDIgKyBsZWFkMyArIGxhZzEgKyBsYWcyICsgbGFnMykvNy9wb3AuMjAxOSkgKiAxMDAwMDApDQoNCmluY2lkZW5jZS5ieS5jb3VudHkNCmBgYA0KDQpgYGB7cn0NCiMgY2xlYW4gdXAgZGF0YQ0KaW5jaWRlbmNlLmJ5LmNvdW50eSRkYXRlIDwtIGdzdWIoIlgiLCAiIiwgaW5jaWRlbmNlLmJ5LmNvdW50eSRkYXRlKQ0KaW5jaWRlbmNlLmJ5LmNvdW50eSRkYXRlIDwtIG1keShpbmNpZGVuY2UuYnkuY291bnR5JGRhdGUpDQoNCiMgZXhjbHVkZSBjYXNlcyB0aGF0IHdlcmUgbm90IGFsbG9jYXRlZCB0byBhIGNvdW50eQ0KIyBuID0gNTAgY291bnR5LmZpcHMgY29kZXMgZGVzaWduYXRlZCBmb3IgInVuYWxsb2NhdGVkIiBjYXNlcywgZm9yIGVhY2ggc3RhdGUNCmluY2lkZW5jZS5ieS5jb3VudHkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgc2VsZWN0KGNvdW50eS5maXBzKSAlPiUNCiAgZGlzdGluY3QoKSAlPiUNCiAgc3VtbWFyaXplKCduLmNvdW50aWVzJyA9IG4oKSkgIyBOID0gMywxOTMNCg0KbnJvdyhpbmNpZGVuY2UuYnkuY291bnR5KSAjIE4gPSA3MzQsMzkwICoqKihOID0gNzQ3LDE2Mj8pKioqDQpgYGANCg0KYGBge3J9DQppbmNpZGVuY2UuYnkuY291bnR5IDwtIGluY2lkZW5jZS5ieS5jb3VudHlbIWdyZXBsKCJVbmFsbG9jYXRlZCIsIGluY2lkZW5jZS5ieS5jb3VudHkkY291bnR5KSwgXQ0KDQpucm93KGluY2lkZW5jZS5ieS5jb3VudHkpICMgTiA9IDcyMiw4OTAgKioqKE4gPSA3MzUsNDYyPykqKioNCg0KaW5jaWRlbmNlLmJ5LmNvdW50eSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBzZWxlY3QoY291bnR5LmZpcHMpICU+JQ0KICBkaXN0aW5jdCgpICU+JQ0KICBzdW1tYXJpemUgKCduLmNvdW50aWVzJyA9IG4oKSkgIyBOID0gMywxNDMNCmBgYA0KDQpgYGB7cn0NCiMgdGhlcmUgaXMgb25lIGNvdW50eS5maXBzIGNvZGUgd2l0aCBubyBjb3VudHkgbmFtZSBvciBkYXRhDQppbmNpZGVuY2UuYnkuY291bnR5IDwtIGluY2lkZW5jZS5ieS5jb3VudHkgJT4lDQogIGZpbHRlcihjb3VudHkuZmlwcyAhPSAiMDIyNzAiKQ0KDQpucm93KGluY2lkZW5jZS5ieS5jb3VudHkpICMgTiA9IDcyMiw2NjAgKioqKE4gPSA3MzUsMjI4PykqKioNCg0KaW5jaWRlbmNlLmJ5LmNvdW50eSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBzZWxlY3QoY291bnR5LmZpcHMpICU+JQ0KICBkaXN0aW5jdCgpICU+JQ0KICBzdW1tYXJpemUgKCduLmNvdW50aWVzJyA9IG4oKSkgIyBOID0gMywxOTIgKioqKE4gPSAzLDE0Mj8pKioqDQoNCiMgVmlldyhpbmNpZGVuY2UuYnkuY291bnR5KQ0KIyBjaGVjayA8LSBzYW1wbGUoaW5jaWRlbmNlLmJ5LmNvdW50eSRjb3VudHkuZmlwcywgc2l6ZT0xKQ0KIyBWaWV3KGluY2lkZW5jZS5ieS5jb3VudHkgJT4lIGZpbHRlcihjb3VudHkuZmlwcz09Y2hlY2spKQ0KYGBgDQoNCg0KIyMgR2V0IGNvdW50eS1sZXZlbCBob3RzcG90IHN0YXR1cw0KDQoNCmBgYHtyfQ0KI3NvdXJjZSgiZ2V0LmNvdW50eS5ocy5zdGF0dXMwLlIiKQ0KI25yb3coaHMuY291bnRpZXMpICMgTiA9IDEzLDcyNg0KYGBgDQoNCmBgYHtyfQ0KIyBpbXBvcnQgZGF0YQ0KaHMuY291bnRpZXMgPC0gcmVhZF9leGNlbCgiaW5wdXRzL0VtZXJnaW5nIENvdW50aWVzIFByb2Nlc3NlZCAyMDIwLTA5LTA5Lnhsc3giLCBzaGVldCA9ICJIaXN0b3J5IikNCm5yb3coaHMuY291bnRpZXMpICMgTiA9IDEzLDcyNiBmb3IgOS85LzIwMjAgZGF0YQ0Kc3RyKGhzLmNvdW50aWVzKQ0KDQojIGNvbnZlcnQgdG8gZGF0ZSBmb3JtYXQNCmNvbG5hbWVzKGhzLmNvdW50aWVzKVsxXSA8LSAiZGF0YS5kYXRlIg0KaHMuY291bnRpZXMkZGF0YS5kYXRlIDwtIHltZChocy5jb3VudGllcyRkYXRhLmRhdGUpDQoNCiMgY29udmVydCBmaXBzIGNvZGUgdG8gY2hhcmFjdGVyDQpjb2xuYW1lcyhocy5jb3VudGllcylbMl0gPC0gImNvdW50eS5maXBzIg0KaHMuY291bnRpZXMkY291bnR5LmZpcHMgPC0gYXMuY2hhcmFjdGVyKGhzLmNvdW50aWVzJGNvdW50eS5maXBzKQ0KDQojIGFkZCBsZWFkaW5nIHplcm9zDQpucm93KGhzLmNvdW50aWVzKSAjIE4gPSAxMyw1NjAgKioqKE4gPSAxMyw3MjY/KSoqKg0KbnJvdyhocy5jb3VudGllc1tuY2hhcihocy5jb3VudGllcyRjb3VudHkuZmlwcyk9PTUsIF0pICMgTiA9IDExLDQ4MiAqKiooTiA9IDExLDY0MD8pKioqDQpucm93KGhzLmNvdW50aWVzW25jaGFyKGhzLmNvdW50aWVzJGNvdW50eS5maXBzKT09NCwgXSkgIyBOID0gMiwwNzgsICoqKihOID0gMiwwODY/KSBjb3VudGllcyB3aXRoIGRyb3BwZWQgbGVhZGluZyB6ZXJvcyBpbiBmaXBzIGNvZGVzDQoNCmhzLmNvdW50aWVzW25jaGFyKGhzLmNvdW50aWVzJGNvdW50eS5maXBzKT09NCwgXSRjb3VudHkuZmlwcyA8LSBwYXN0ZTAoIjAiLCBocy5jb3VudGllc1tuY2hhcihocy5jb3VudGllcyRjb3VudHkuZmlwcyk9PTQsIF0kY291bnR5LmZpcHMpDQpucm93KGhzLmNvdW50aWVzW25jaGFyKGhzLmNvdW50aWVzJGNvdW50eS5maXBzKT09NSwgXSkgIyBOID0gMTMsNTYwICoqKihOID0gMTMsNzI2PykqKioNCg0KIyBjb252ZXJ0IGRhdGUgZm9ybWF0DQpjb2xuYW1lcyhocy5jb3VudGllcylbMTBdIDwtICJuZXcuY2RjIg0KDQojIGFkZCBpbmRpY2F0b3IgZm9yIG5ld2x5IGlkZW50aWZpZWQgSFMNCmhzLmNvdW50aWVzJG5ldy5ocyA8LSAwDQpocy5jb3VudGllc1shaXMubmEoaHMuY291bnRpZXMkbmV3LmNkYykgJiBocy5jb3VudGllcyRuZXcuY2RjID09IFRSVUUsICJuZXcuaHMiXSA8LSAxDQoNCiMgYWRkIGluZGljYXRvciB2YXJpYWJsZSB0byBkaWZmZXJlbnRpYXRlIEhTIGNvdW50aWVzIGFmdGVyIG1lcmdlDQpocy5jb3VudGllcyRocy5zdGF0dXMgPC0gMQ0KDQojIGxpbWl0IGRhdGFzZXQgdG8gZmlwcyBjb2RlIGFuZCBocyBzdGF0dXMNCmhzLmNvdW50aWVzIDwtIGhzLmNvdW50aWVzICU+JQ0KICBzZWxlY3QoY291bnR5LmZpcHMsIGRhdGEuZGF0ZSwgaHMuc3RhdHVzLCBuZXcuY2RjLCBuZXcuaHMpDQpgYGANCg0KYGBge3J9DQpocy5jb3VudGllcw0KYGBgDQoNCg0KIyMgR2V0IElIRSBjb3VudHkgYWJzdHJhY3RlZCBkYXRhDQoNCg0KYGBge3J9DQojc291cmNlKCJnZXQuYWJzdHJhY3RlZC5kYXRhMC5SIikNCiNucm93KGloZS5kYXRhKSAjIE4gPSAxMzMNCiN0YWJsZShpaGUuZGF0YSRvcGVuLnR5cGUpDQpgYGANCg0KYGBge3J9DQojIGltcG9ydCBkYXRhDQppaGUuZGF0YSA8LSByZWFkX2V4Y2VsKCJpbnB1dHMvaWhlLmhvdHNwb3RzLmFic3RyYWN0aW9uLjA5MTMyMC54bHN4Iiwgc2hlZXQgPSAidW5pdmVyc2l0eS5zYW1wbGUuMDgyNjIwIikNCm5yb3coaWhlLmRhdGEpICMgTiA9IDE1MiBmb3IgOS8xMy8yMDIwIGRhdGENCg0KIyBjb252ZXJ0IGZpcHMgdG8gY2hhcmFjdGVyLCBhZGQgbGVhZGluZyB6ZXJvcw0KaWhlLmRhdGEkY291bnR5LmZpcHMgPC0gYXMuY2hhcmFjdGVyKGloZS5kYXRhJGNvdW50eS5maXBzKQ0KDQojIGFkZCBhIGxlYWRpbmcgemVybyB0byBhbnkgY291bnR5LmZpcHMgdGhhdCBvbmx5IGhhdmUgNiBkaWdpdHMNCm5yb3coaWhlLmRhdGEpICMgTiA9IDE1Mg0KbnJvdyhpaGUuZGF0YVtuY2hhcihpaGUuZGF0YSRjb3VudHkuZmlwcyk9PTUsIF0pICMgTiA9IDExOQ0KbnJvdyhpaGUuZGF0YVtuY2hhcihpaGUuZGF0YSRjb3VudHkuZmlwcyk9PTQsIF0pICMgTiA9IDMzLCBjb3VudGllcyB3aXRoIGRyb3BwZWQgbGVhZGluZyB6ZXJvcw0KDQppaGUuZGF0YVtuY2hhcihpaGUuZGF0YSRjb3VudHkuZmlwcyk9PTQsIF0kY291bnR5LmZpcHMgPC0NCiAgcGFzdGUwKCIwIiwgaWhlLmRhdGFbbmNoYXIoaWhlLmRhdGEkY291bnR5LmZpcHMpPT00LCBdJGNvdW50eS5maXBzKQ0KDQpucm93KGloZS5kYXRhW25jaGFyKGloZS5kYXRhJGNvdW50eS5maXBzKT09NSwgXSkgIyBOID0gMTUyDQoNCiMgYWRkIHZhcmlhYmxlIGluZGljYXRpbmcgSUhFIGFmdGVyIG1lcmdlDQppaGUuZGF0YSRpaGUuc3RhdHVzIDwtIDENCg0KIyByZWNhc3QgZGF0ZXMNCmloZS5kYXRhJG9wZW4uZGF0ZSA8LSB5bWQoaWhlLmRhdGEkb3Blbi5kYXRlKQ0KDQojIGV4Y2x1ZGUgb2JzZXJ2YXRpb25zDQppaGUuZGF0YSA8LSBpaGUuZGF0YSAlPiUNCiAgZmlsdGVyKGluY2x1ZGUgPT0gMSkNCg0KbnJvdyhpaGUuZGF0YSkgIyBOID0gMTQ5DQpgYGANCmBgYHtyfQ0KaWhlLmRhdGENCmBgYA0KDQoNCmBgYHtyfQ0KIyBmaW5hbGl6ZSBkYXRhc2V0IGZvciB1c2UgaW4gbmV4dCBzdGVwDQojaWhlLmRhdGEgPC0gaWhlLmRhdGEgJT4lDQojICBzZWxlY3QoaWhlLnN0YXR1cywgdW5pdGlkLCBjb3VudHkuZmlwcywgaW5jbHVkZSwgb3Blbi5kYXRlLCBvcGVuLnR5cGUpDQoNCiNzdHIoaWhlLmRhdGEpDQojbnJvdyhpaGUuZGF0YSkgIyBOID0gMTQ5DQpgYGANCg0KIyMjIElkZW50aWZ5IGNvdW50aWVzIHdpdGggbXVsdGlwbGUgSUhFcywgY2hvb3NlIHRoZSBlYXJsaWVzdCBvcGVuZXINCg0KYGBge3J9DQojaWhlLmRhdGEgPC0gaWhlLmRhdGEgJT4lDQojICBncm91cF9ieShjb3VudHkuZmlwcykgJT4lDQojICBmaWx0ZXIob3Blbi5kYXRlID09IG1pbihvcGVuLmRhdGUpKQ0KDQojbnJvdyhpaGUuZGF0YSkgIyBOID0gMTM0ICgxNiB3ZXJlIGRyb3BwZWQgYmVjYXVzZSB0aGV5IHNoYXJlZCBhIGNvdW50eSB3aXRoIGFub3RoZXIgc2Nob29sKQ0KDQojIyB0aGVyZSBpcyBvbmUgY291bnR5IHcgMiBJSEVzIHdpdGggaWRlbnRpY2FsIG9wZW4uZGF0ZXMgYW5kIG9wZW4udHlwZTogMDYwMzcNCiNpaGUuZGF0YSA8LSBpaGUuZGF0YSAlPiUNCiMgIGdyb3VwX2J5KGNvdW50eS5maXBzKSAlPiUNCiMgIHNsaWNlKDEpIA0KDQojbnJvdyhpaGUuZGF0YSkgIyBOID0gMTMzLCAoMSBtb3JlIGRyb3BwZWQgZm9yIGEgdG90YWwgb2YgMTcpDQojc3VtKGloZS5kYXRhJGloZS5zdGF0dXMpDQpgYGANCg0KYGBge3J9DQojIGdldCBzb21lIGJhc2ljIGluZm8gYWJvdXQgdGhlIGRhdGENCiNpaGUuZGF0YSAlPiUNCiMgIHVuZ3JvdXAgJT4lDQojICBzZWxlY3QoY291bnR5LmZpcHMpICU+JQ0KIyAgZGlzdGluY3QoKSAlPiUNCiMgIHN1bW1hcml6ZSAoJ24uY291bnRpZXMnID0gbigpKSAjIE4gPSAxMzMNCg0KI21lZGlhbihpaGUuZGF0YSRvcGVuLmRhdGUpICMgMjAyMC0wOC0yNA0KYGBgDQoNCiMjIyBUYXJnZXQgZW5kIGRhdGEgZGF0ZSB0byB1c2UgaGFsZiB0aGUgZGF0YSBhbmQgZ2V0IDMgd2Vla3Mgb2Ygb2JzZXJ2YXRpb24NCg0KYGBge3J9DQojbWVkaWFuKGloZS5kYXRhJG9wZW4uZGF0ZSkgKyAyMSAjIDIwMjAtMDktMTQNCiNtaW4oaWhlLmRhdGEkb3Blbi5kYXRlKSAjIDIwMjAtMDctMjcNCiNtZWFuKGloZS5kYXRhJG9wZW4uZGF0ZSkgIyAyMDIwLTA4LTI1DQojbWF4KGloZS5kYXRhJG9wZW4uZGF0ZSkgIyAyMDIwLTA5LTMwDQpgYGANCg0KDQojIyBNZXJnZSBjb3VudHktbGV2ZWwgaW5kaWNhdG9ycyAobXNhLCBwb3ApIGFuZCBJSEUgc3RhdHVzDQoNCg0KYGBge3J9DQojc291cmNlKCJnZXQubWVyZ2VkLmFsbC5jb3VudHkuZGF0YTAuUiIpDQojbnJvdyhhbGwuY291bnR5LmRhdGEpICMgTiA9IDMsMTQyDQpgYGANCg0KYGBge3J9DQojIGpvaW4gdGhlIHR3byBkYXRhc2V0cw0KYWxsLmNvdW50eS5kYXRhIDwtIGxlZnRfam9pbihjb3VudHkucG9wLCBpaGUuZGF0YSwgYnkgPSAiY291bnR5LmZpcHMiKQ0KDQojIGFzc2lnbiBpaHM9MCB0byBjb3VudGllcyB3aXRoIG5vIElIRQ0KYWxsLmNvdW50eS5kYXRhW2lzLm5hKGFsbC5jb3VudHkuZGF0YSRpaGUuc3RhdHVzKSwgImloZS5zdGF0dXMiXSA8LSAwDQoNCiMgZ2V0IG1lZGlhbiBhbmQgc2V0IGFzIG9wZW4uZGF0ZSBmb3IgaWhlPT0wIGNvdW50aWVzDQptZWRpYW4uZGF0ZSA8LSBtZWRpYW4oYWxsLmNvdW50eS5kYXRhJG9wZW4uZGF0ZSwgbmEucm0gPSBUUlVFKQ0KYWxsLmNvdW50eS5kYXRhJG9wZW4uZGF0ZS52MSA8LSBhbGwuY291bnR5LmRhdGEkb3Blbi5kYXRlICMgY29weSBvdmVyIElIRSBjb3VudHkgb3Blbi5kYXRlcw0KYWxsLmNvdW50eS5kYXRhW2FsbC5jb3VudHkuZGF0YSRpaGUuc3RhdHVzID09IDAsICJvcGVuLmRhdGUudjEiXSA8LSBtZWRpYW4uZGF0ZSAjIHJlcGxhY2UgTkEgd2l0aCBtZWRpYW4gdmFsdWUNCg0KIyBkcm9wIHRoZSBpbmNsdWRlIHZhcmlhYmxlDQphbGwuY291bnR5LmRhdGEgPC0gYWxsLmNvdW50eS5kYXRhICU+JQ0KICBzZWxlY3QoLW1hdGNoZXMoImluY2x1ZGUiKSkNCmBgYA0KDQpgYGB7cn0NCiMgY2hlY2sgYmFzaWMgaW5mbw0Kc3VtKGFsbC5jb3VudHkuZGF0YSRpaGUuc3RhdHVzKSAjIE4gPSAxMzMNCmFsbC5jb3VudHkuZGF0YSAlPiUNCiAgc2VsZWN0KGNvdW50eS5maXBzKSAlPiUNCiAgZGlzdGluY3QoKSAlPiUNCiAgc3VtbWFyaXplICgnbi5jb3VudGllcycgPSBuKCkpICMgTiA9IDEzMyAqKiooTiA9IDMsMTQyPykqKioNCg0KdGFibGUoYWxsLmNvdW50eS5kYXRhJG9wZW4uZGF0ZSkNCg0KIyMjIHdyaXRlIGZpbGUgdG8gc2VuZCB0byBHUkFTUCAoYW5kIGluIGdlbmVyYWwpDQojd3JpdGUuY3N2KGFsbC5jb3VudHkuZGF0YSwgIm91dHB1dHMvaWhlLmhvdHNwb3RzLmFsbC5jb3VudGllcy5jc3YiKQ0Kd3JpdGUuY3N2KGFsbC5jb3VudHkuZGF0YSwgIm91dHB1dHMvaWhlLmhvdHNwb3RzLmFsbC5jb3VudGllczIuY3N2IikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBkcm9wIG1zYSB0eXBlIGFuZCBwb3B1bGF0aW9uIGNvdW50cywgYmVjYXVzZSB0aGV5J2xsIGJlIGJyb3VnaHQgaW4gd2l0aCB0aGUgaW5jaWRlbmNlIGZpbGUNCiMgYWxzbyBkcm9wIG9wZW4uZGF0ZS52MSwgc2luY2Ugd2UnbGwgYXNzaWduIHRoYXQgaW4gYW5hbHlzaXMgZmlsZSBiYXNlZCBvbiBzdWJzYW1wbGUgdXNlZCBpbiBhbmFseXNpcw0KYWxsLmNvdW50eS5kYXRhXzEgPC0gYWxsLmNvdW50eS5kYXRhICU+JQ0KICBzZWxlY3QoLW1hdGNoZXMoYygibXNhIiwgInBvcC4yMDE5IiwgIm9wZW4uZGF0ZS52MSIpKSkNCmBgYA0KDQpgYGB7cn0NCiMgTk9URTogbWVyZ2VkIGNvdW50eSBpbmZvIHdpbGwgYmUgZXhwb3J0ZWQgdG8gR1JBU1AgdG8gcmV0dXJuIG1hdGNoZXMNCiMgbWVyZ2UgaW5jaWRlbmNlIGFuZCBob3RzcG90IGRhdGENCnRlbXAgPC0gbGVmdF9qb2luKGluY2lkZW5jZS5ieS5jb3VudHksaHMuY291bnRpZXMsDQogICAgICAgICAgICAgICAgICBieSA9IGMoImNvdW50eS5maXBzIj0iY291bnR5LmZpcHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICJkYXRlIiA9ICJkYXRhLmRhdGUiKSkNCg0KIyBtZXJnZSBpbmNpZGVuY2UvaG90c3BvdCBhbmQgaWhlIGFic3RyYWN0aW9uIGRhdGENCnRlbXAgPC0gbGVmdF9qb2luKHRlbXAsIGFsbC5jb3VudHkuZGF0YSwNCiAgICAgICAgICAgICAgICAgIGJ5ID0gImNvdW50eS5maXBzIikNCmBgYA0KDQpgYGB7cn0NCiMgY2hlY2sgYSByYW5kb20gY291bnR5DQojY2hlY2sgPC0gc2FtcGxlKHRlbXAkY291bnR5LmZpcHMsIHNpemU9MSkNCiNWaWV3KHRlbXAgJT4lIGZpbHRlcihjb3VudHkuZmlwcz09Y2hlY2spKQ0KIyBjaGVjayBicmF6b3MgY291bnR5IHR4DQojVmlldyh0ZW1wICU+JSBmaWx0ZXIoY291bnR5LmZpcHM9PSI0ODA0MSIpKQ0KI1ZpZXcoaW5jaWRlbmNlLmJ5LmNvdW50eSkNCmBgYA0KDQpgYGB7cn0NCiMjIyMjIyBzYXZlIGJpZyBmaWxlICMjIyMjIyMNCiN3cml0ZS5jc3YodGVtcCwgIm91dHB1dHMvZGZfbWVyZ2UuY3N2IikNCmBgYA0KDQoNCiMgQW5hbHlzaXMNCg0KDQpgYGB7cn0NCiMgYnJpbmcgaW4gZGF0YSBmcm9tIG1lcmdlcw0KYS5kYXRhIDwtIHRlbXANCmBgYA0KDQpgYGB7cn0NCiMgc2ltcGxlIGRpYWdub3N0aWNzIG9uIGRhdGENCiMgY291bnRpZXMgaW4gZGF0YXNldA0KYS5kYXRhICU+JQ0KICAgIHVuZ3JvdXAgJT4lDQogICAgc2VsZWN0KGNvdW50eS5maXBzKSAlPiUNCiAgICBkaXN0aW5jdCgpICU+JQ0KICAgIHN1bW1hcml6ZSAoJ24uY291bnRpZXMnID0gbigpKSAjIE4gPSAzLDE0Mg0KYGBgDQoNCmBgYHtyfQ0KIyBjb3VudGllcyB3aXRoIG5vIElIRXMNCmEuZGF0YSAlPiUNCiAgICB1bmdyb3VwICU+JQ0KICAgIGZpbHRlcihpaGUuc3RhdHVzID09IDApICU+JQ0KICAgIHNlbGVjdChjb3VudHkuZmlwcykgJT4lDQogICAgZGlzdGluY3QoKSAlPiUNCiAgICBzdW1tYXJpemUgKCduLmNvdW50aWVzJyA9IG4oKSkgIyBOID0gMywwMDkNCmBgYA0KDQpgYGB7cn0NCiMgY291bnRpZXMgd2l0aCBJSEVzDQphLmRhdGEgJT4lDQogICAgdW5ncm91cCAlPiUNCiAgICBmaWx0ZXIoaWhlLnN0YXR1cyA9PSAxKSAlPiUNCiAgICBzZWxlY3QoY291bnR5LmZpcHMpICU+JQ0KICAgIGRpc3RpbmN0KCkgJT4lDQogICAgc3VtbWFyaXplICgnbi5jb3VudGllcycgPSBuKCkpICMgTiA9IDEzMw0KYGBgDQoNCmBgYHtyfQ0KIyBjb3VudCBieSBpaGUuc3RhdHVzDQoodDAuYSA8LSBhLmRhdGEgJT4lIA0KICAgIHNlbGVjdChjb3VudHkuZmlwcywgaWhlLnN0YXR1cykgJT4lDQogICAgZGlzdGluY3QoKSAlPiUNCiAgICBncm91cF9ieShpaGUuc3RhdHVzKSAlPiUNCiAgICBzdW1tYXJpemUoY291bnQgPSBuKCkpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBjb3VudCBieSBvcGVuLnR5cGUNCih0MC5iIDwtIGEuZGF0YSAlPiUgDQogICAgc2VsZWN0KGNvdW50eS5maXBzLCBpaGUuc3RhdHVzLCBvcGVuLnR5cGUpICU+JQ0KICAgIGRpc3RpbmN0KCkgJT4lDQogICAgZ3JvdXBfYnkoaWhlLnN0YXR1cywgb3Blbi50eXBlKSAlPiUNCiAgICBzdW1tYXJpemUoY291bnQgPSBuKCkpICU+JQ0KICAgIHRpZHlyOjpzcHJlYWQoa2V5ID0gb3Blbi50eXBlLCB2YWx1ZSA9IGNvdW50KSkNCmBgYA0KDQpgYGB7cn0NCiMgb3BlbiBkYXRlcw0KbWVhbihhLmRhdGEkb3Blbi5kYXRlLCBuYS5ybSA9IFRSVUUpDQptZWRpYW4oYS5kYXRhJG9wZW4uZGF0ZSwgbmEucm0gPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KIyBtYWtlIGV4Y2x1c2lvbnMNCiMgZXhjbHVkZSBkYXRhIGZyb20gdGhlIGZpcnN0IHBhcnQgb2YgdGhlIHllYXIsIGRyb3AgYWxsIGJlZm9yZSBKdW5lIDFzdA0KYS5kYXRhIDwtIGEuZGF0YSAlPiUNCiAgICBmaWx0ZXIoKGRhdGUgPj0gbWR5KCI2LzEvMjAyMCIpKSkNCg0KbnJvdyhhLmRhdGEpICMgTiA9IDMxMSwxNTcgKioqKE4gPSAzMjMsNjI2KQ0KYGBgDQoNCmBgYHtyfQ0KIyBjb3VudGllcyBpbiBkYXRhc2V0DQphLmRhdGEgJT4lDQogICAgdW5ncm91cCAlPiUNCiAgICBzZWxlY3QoY291bnR5LmZpcHMpICU+JQ0KICAgIGRpc3RpbmN0KCkgJT4lDQogICAgc3VtbWFyaXplICgnbi5jb3VudGllcycgPSBuKCkpICMgTiA9IDMsMTQyDQpgYGANCg0KYGBge3J9DQojIGV4Y2x1ZGUgYW55IElIRSBjb3VudGllcyB0aGF0IGhhdmUgb3BlbiBkYXRlIGFmdGVyIGN1dG9mZiAoOC8yMS8yMDIwKQ0KIyBjdXQgb2ZmIGRhdGUgaXMgOC8yNC8yMDIwLCAtMTQgd2lsbCBhbGxvdyBmb3IgY2FsYyA3IGRheSBhdmVyYWdlIGF0IGRheSArMTENCmN1dC5vZmYuZGF0ZSA8LSBtZHkoIjkvMTEvMjAyMCIpIC0gMTQNCg0KIyBkcm9wIElIRSBjb3VudGllcyB3aXRoIG9wZW4gZGF0ZSBhZnRlciBjdXQub2ZmLmRhdGUNCmEuZGF0YSA8LSBhLmRhdGEgJT4lDQogICAgZmlsdGVyKGloZS5zdGF0dXMgPT0gMCB8IG9wZW4uZGF0ZSA8PSBjdXQub2ZmLmRhdGUpDQoNCiMgdG90YWwgY291bnRpZXMgaW4gZGF0YXNldA0KYS5kYXRhICU+JQ0KICAgIHVuZ3JvdXAgJT4lDQogICAgc2VsZWN0KGNvdW50eS5maXBzKSAlPiUNCiAgICBkaXN0aW5jdCgpICU+JQ0KICAgIHN1bW1hcml6ZSAoJ24uY291bnRpZXMnID0gbigpKSAjIE4gPSAzMTAwPyAqKiooTiA9IDMsMTEwKSoqKg0KYGBgDQoNCmBgYHtyfQ0KIyBjb3VudCBieSBpaGUuc3RhdHVzDQoodDAuYSA8LSBhLmRhdGEgJT4lIA0KICAgIHNlbGVjdChjb3VudHkuZmlwcywgaWhlLnN0YXR1cykgJT4lDQogICAgZGlzdGluY3QoKSAlPiUNCiAgICBncm91cF9ieShpaGUuc3RhdHVzKSAlPiUNCiAgICBzdW1tYXJpemUoY291bnQgPSBuKCkpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBjb3VudCBieSBvcGVuLnR5cGUNCih0MC5iIDwtIGEuZGF0YSAlPiUgDQogICAgc2VsZWN0KGNvdW50eS5maXBzLCBpaGUuc3RhdHVzLCBvcGVuLnR5cGUpICU+JQ0KICAgIGRpc3RpbmN0KCkgJT4lDQogICAgZ3JvdXBfYnkoaWhlLnN0YXR1cywgb3Blbi50eXBlKSAlPiUNCiAgICBzdW1tYXJpemUoY291bnQgPSBuKCkpICU+JQ0KICAgIHRpZHlyOjpzcHJlYWQoa2V5ID0gb3Blbi50eXBlLCB2YWx1ZSA9IGNvdW50KSkNCmBgYA0KDQpgYGB7cn0NCiMgb3BlbiBkYXRlcw0KbWVhbihhLmRhdGEkb3Blbi5kYXRlLCBuYS5ybSA9IFRSVUUpDQptZWRpYW4oYS5kYXRhJG9wZW4uZGF0ZSwgbmEucm0gPSBUUlVFKQ0KYGBgDQoNCmBgYHtyfQ0KIyAxLjAgbWFrZSBhbnkgYXNzdW1wdGlvbnMNCiMgMS4xIGVzdGFibGlzaCB0aGUgb3BlbiBkYXRlIGZvciBhbnkgbm9uLUlIRSBjb3VudGllcw0KbWVkaWFuLm8uZGF0ZSA8LSBtZWRpYW4oYS5kYXRhJG9wZW4uZGF0ZSwgbmEucm0gPSBUUlVFKQ0KDQphLmRhdGEkb3Blbi5kYXRlLnYyIDwtIGEuZGF0YSRvcGVuLmRhdGUgIyBjb3B5IG92ZXIgb3Blbi5kYXRlIGZvciBJSEUgY291bnRpZXMNCmEuZGF0YVthLmRhdGEkaWhlLnN0YXR1cyA9PSAwLCAib3Blbi5kYXRlLnYyIl0gPC0gbWVkaWFuLm8uZGF0ZQ0KDQojIDEuMiBhc3NpZ24gZGF5IDANCmEuZGF0YSRkYXlzLmZyb20ub3Blbi5kYXRlIDwtIGEuZGF0YSRkYXRlIC0gYS5kYXRhJG9wZW4uZGF0ZS52Mg0KDQojIDEuMyByZW1vdmUgYW55IGRhdGEgb3V0c2lkZSBvYnNlcnZhdGlvbiB3aW5kb3cNCmEuZGF0YSA8LSBhLmRhdGEgJT4lDQogICAgZmlsdGVyKGRheXMuZnJvbS5vcGVuLmRhdGUgPj0gLTI4KQ0KDQphLmRhdGEgJT4lDQogICAgdW5ncm91cCAlPiUNCiAgICBzZWxlY3QoY291bnR5LmZpcHMpICU+JQ0KICAgIGRpc3RpbmN0KCkgJT4lDQogICAgc3VtbWFyaXplICgnbi5jb3VudGllcycgPSBuKCkpICMgTiA9IDMxMDAgKioqKE4gPSAzLDExMCoqKikNCmBgYA0KDQpgYGB7cn0NCm5yb3coYS5kYXRhKSAjIE4gPSA4MDg5NSAqKiooTiA9IDE0Niw0NDcpKioqDQpgYGANCg0KDQojIyMgQ2FsY3VsYXRlIHJlc3VsdHMNCg0KDQpgYGB7cn0NCiMgY291bnRpZXMgd2l0aCBJSEVzIGluIE1TQSA9IDENCmEuZGF0YSAlPiUNCiAgICB1bmdyb3VwICU+JQ0KICAgIGZpbHRlcihpaGUuc3RhdHVzID09IDEpICU+JQ0KICAgIHNlbGVjdChjb3VudHkuZmlwcykgJT4lDQogICAgZGlzdGluY3QoKSAlPiUNCiAgICBzdW1tYXJpemUgKCduLmNvdW50aWVzJyA9IG4oKSkgIyBOID0gOTEgKioqKE4gPSAxMDEpKioqDQpgYGANCg0KYGBge3J9DQojIGNvdW50aWVzIHdpdGggSUhFcyBpbiBNU0EgPSAxDQphLmRhdGEgJT4lDQogICAgdW5ncm91cCAlPiUNCiAgICBmaWx0ZXIoaWhlLnN0YXR1cyA9PSAwKSAlPiUNCiAgICBzZWxlY3QoY291bnR5LmZpcHMpICU+JQ0KICAgIGRpc3RpbmN0KCkgJT4lDQogICAgc3VtbWFyaXplICgnbi5jb3VudGllcycgPSBuKCkpICMgTiA9IDkxICoqKihOID0gMzAwOSkqKioNCmBgYA0KDQojIyMgUHJlbGltIHJlc3VsdHMNCg0KYGBge3J9DQojIHNldCB0aGUgd2luZG93cyBvZiBvYnNlcnZhdGlvbg0KbG9va3MgPC0gYygtMjEsIC0xNCwgLTcsIDAsIDcsIDE0KQ0KDQojIG1lYW5zDQoodDEuYSA8LSBhLmRhdGEgJT4lDQogICAgICAgIGZpbHRlcihkYXlzLmZyb20ub3Blbi5kYXRlICVpbiUgbG9va3MpICU+JQ0KICAgICAgICBncm91cF9ieShpaGUuc3RhdHVzLCBkYXlzLmZyb20ub3Blbi5kYXRlKSAlPiUNCiAgICAgICAgc3VtbWFyaXplKGF2ZyA9IG1lYW4oYXZnLjdkYXksIG5hLnJtID0gVFJVRSkpICU+JQ0KICAgICAgICB0aWR5cjo6c3ByZWFkKGtleSA9IGRheXMuZnJvbS5vcGVuLmRhdGUsIHZhbHVlID0gYXZnKSkNCmBgYA0KDQpgYGB7cn0NCiMgbWVkaWFucw0KKHQxLmEgPC0gYS5kYXRhICU+JQ0KICAgICAgICBmaWx0ZXIoZGF5cy5mcm9tLm9wZW4uZGF0ZSAlaW4lIGxvb2tzKSAlPiUNCiAgICAgICAgZ3JvdXBfYnkoaWhlLnN0YXR1cywgZGF5cy5mcm9tLm9wZW4uZGF0ZSkgJT4lDQogICAgICAgIHN1bW1hcml6ZShtZWRpYW4gPSBtZWRpYW4oYXZnLjdkYXksIG5hLnJtID0gVFJVRSkpICU+JQ0KICAgICAgICB0aWR5cjo6c3ByZWFkKGtleSA9IGRheXMuZnJvbS5vcGVuLmRhdGUsIHZhbHVlID0gbWVkaWFuKSkNCmBgYA0KDQpgYGB7cn0NCiMgY291bnRzDQoodDEuYSA8LSBhLmRhdGEgJT4lDQogICAgICAgIGZpbHRlcihkYXlzLmZyb20ub3Blbi5kYXRlICVpbiUgbG9va3MpICU+JQ0KICAgICAgICBncm91cF9ieShpaGUuc3RhdHVzLCBkYXlzLmZyb20ub3Blbi5kYXRlKSAlPiUNCiAgICAgICAgc3VtbWFyaXplKGNvdW50ID0gbigpKSAlPiUNCiAgICAgICAgdGlkeXI6OnNwcmVhZChrZXkgPSBkYXlzLmZyb20ub3Blbi5kYXRlLCB2YWx1ZSA9IGNvdW50KSkNCmBgYA0KDQpgYGB7cn0NCiMgaG90c3BvdHMsIGNvdW50cw0KIyBnZW5lcmF0ZSBzaW1wbGUgYmVmb3JlL2FmdGVyIHZhcmlhYmxlDQphLmRhdGEkYWZ0ZXIuZGF5LjAgPC0gYS5kYXRhJGRheXMuZnJvbS5vcGVuLmRhdGUgPiAwDQoNCiMgY291bnRzIG9mIG5ldyBob3RzcG90cw0KKHQyLmEgPC0gYS5kYXRhICU+JQ0KICAgICAgICBncm91cF9ieShpaGUuc3RhdHVzLCBhZnRlci5kYXkuMCkgJT4lDQogICAgICAgIHN1bW1hcml6ZShjb3VudCA9IHN1bShuZXcuaHMsIG5hLnJtID0gVFJVRSkpICU+JQ0KICAgICAgICB0aWR5cjo6c3ByZWFkKGtleSA9IGFmdGVyLmRheS4wLCB2YWx1ZSA9IGNvdW50KSkNCmBgYA0KDQpgYGB7cn0NCiMgY291bnRzIG9mIHRvdGFsIGNvdW50aWVzDQoodDIuYiA8LSBhLmRhdGEgJT4lDQogICAgICAgIGdyb3VwX2J5KGloZS5zdGF0dXMsIGFmdGVyLmRheS4wKSAlPiUNCiAgICAgICAgZGlzdGluY3QoY291bnR5LmZpcHMpICU+JQ0KICAgICAgICBzdW1tYXJpemUoY291bnQgPSBuKCkpICU+JQ0KICAgICAgICB0aWR5cjo6c3ByZWFkKGtleSA9IGFmdGVyLmRheS4wLCB2YWx1ZSA9IGNvdW50KSkNCmBgYA0KDQpgYGB7cn0NCiMgcHJvcG9ydGlvbnMgb2YgY291bnRpZXMgdyBob3RzcG90cw0KIyBiZWZvcmUNCjEwMS8zMDA5DQoxMS8xMDENCiMgYWZ0ZXINCjE1MS8zMDA5DQozMi8xMDENCmBgYA0KDQpgYGB7cn0NCiMgbWVhbnMgYnkgaWhlIHN0YXR1cyBhbmQgb3BlbiB0eXBlDQoodDEuYSA8LSBhLmRhdGEgJT4lDQogICAgIGZpbHRlcihkYXlzLmZyb20ub3Blbi5kYXRlICVpbiUgbG9va3MpICU+JQ0KICAgICBncm91cF9ieShpaGUuc3RhdHVzLCBvcGVuLnR5cGUsIGRheXMuZnJvbS5vcGVuLmRhdGUpICU+JQ0KICAgICBzdW1tYXJpemUoYXZnID0gbWVhbihhdmcuN2RheSwgbmEucm0gPSBUUlVFKSkgJT4lDQogICAgIHRpZHlyOjpzcHJlYWQoa2V5ID0gZGF5cy5mcm9tLm9wZW4uZGF0ZSwgdmFsdWUgPSBhdmcpKQ0KYGBgDQoNCg==