Required packages
library(readr)
library(dplyr)
library(tidyr)
library(forecast)
library(stringr)
library(lubridate)
library(car)
library(ggplot2)
library(infotheo)
library(knitr)
library(magrittr)
Executive Summary
The data sets chosen contained numerous observations taken across many years and variables. Instead of working with all of it, a portion was selected from both files over the same period. It was decided that it would be easier to tidy each of these separately, before merging the two.
The date and time columns in each were both messy for opposite reasons. Tiantan had these spread across multiple columns, and hence they were united. Meanwhile, CRRI had the date and time together in one column, and this was separated. After this was completed, various string manipulations were performed to the time (hour) variable to ensure that the format was the same across both data sets.
A variable was created for both data sets, using a function that assigns an Air Quality Rating (AQI) category value (based on India’s AQI category values) for PM2.5. This new variable is then able to be factored and ordered based on severity levels.
The structure was then checked for Tiantan and CRRI. Appropriate date conversions were made, as well as factoring various variables. The two data sets were then subsetted to only include mutual variables. Once completed, both were finally merged using the bind_rows() function.
Missing and special values were scanned for and recorded. Seeing as there was less than 5% of the data recorded as ‘NA’, it was decided to leave these values out. There were no special values found in any of the numeric variable columns.
The data were plotted using a boxplot to visually scan for outliers in the PM2.5 variable. Noting high quantities, these were dealt with using two methods. The first was capping the variable before transforming it to reduce the positive skewness. The second was transforming 2.5PM, and then removing any outstanding outliers by capping. Both outcomes were attached to the data set as new variables.
Data
The Beijing Multi-Site Air Quality data set contains hourly meteorological and air pollutant measurements from several weather monitoring sites, between the years of 2013-2017. It contains 35,064 observations across 18 variables, however this report will focus on the following variables taken during January 2015:
- date: the date the measurement is recorded
- hour: the time the measurement is recorded
- PM2.5: the amount of particulate matter size less than 2.5 micrometres
- PM10: the amount of particulate matter size less than 10 micrometres
- NO2: the amount of nitrogen dioxide
- CO: the amount of carbon oxide
- O3: the amount of ozone
- station: the station where the measurement is recorded at
The second data set - Air Quality Data in India - similarly contains hourly air pollutant measurements from several weather monitoring sites, between 2015 and 2020. It contains 2,589,083 observations across 16 variables. To compare the data for both sets, the same variables (as listed above) will be used.
The first data set was sourced from Beijing Municipal Enironmental Monitioring Center, and downloaded from the following URL: http://archive.ics.uci.edu/ml/datasets/Beijing+Multi-Site+Air-Quality+Data# . The second data set was sourced from the Central Pollution Control Board, and downloaded from the following URL: https://www.kaggle.com/rohanrao/air-quality-data-in-india?select=station_hour.csv
The data files are imported into R using read_csv() and viewed using the head() function. The files are then subsetted to only include the data from January 2015 from the Tiantan Station and DL007 Station. ‘StationID’ is renamed to ‘station’ for uniformity across both data sets. Note that these two data sets will be merged after they have been tidied.
# Set working directory
setwd("C:/Users/Agent 007/Desktop/2020/MATH2349 Data Wrangling/A2")
# Import and inspect data
tiantan <- read_csv("Tiantan.csv")
Parsed with column specification:
cols(
No = col_double(),
year = col_double(),
month = col_double(),
day = col_double(),
hour = col_double(),
PM2.5 = col_double(),
PM10 = col_double(),
SO2 = col_double(),
NO2 = col_double(),
CO = col_double(),
O3 = col_double(),
TEMP = col_double(),
PRES = col_double(),
DEWP = col_double(),
RAIN = col_double(),
wd = col_character(),
WSPM = col_double(),
station = col_character()
)
india <- read_csv("station_hour.csv")
Parsed with column specification:
cols(
StationId = col_character(),
Datetime = col_datetime(format = ""),
PM2.5 = col_double(),
PM10 = col_double(),
NO = col_double(),
NO2 = col_double(),
NOx = col_double(),
NH3 = col_double(),
CO = col_double(),
SO2 = col_double(),
O3 = col_double(),
Benzene = col_double(),
Toluene = col_double(),
Xylene = col_double(),
AQI = col_double(),
AQI_Bucket = col_character()
)
head(tiantan)
head(india)
# Subset data to contain only January 2015
tiantan_2015 <- tiantan[tiantan$year == "2015",]
tiantan_jan2015 <- tiantan_2015[tiantan_2015$month == 1,]
india_dl007 <- india[india$StationId == "DL007",]
dl007_jan2015 <- india_dl007[1:743,]
# Rename column
dl007_jan2015 %<>% rename(station = StationId)
Tidy & Manipulate Data I
Firstly, the Tiantan data set has the day, month, and year columns separated for the date. This contributes to the ‘untidiness’ of the data since a single subject is over multiple columns, and therefore makes it ‘wide format’. The CRRI (DL007) data set is untidy as on the other hand, the date and time are merged into one column. Thus, every variable does not have its own column.
To tidy these up, the year, month, and day for Tiantan is united into one column. The date and time is separated into their own columns for CRRI, and the original joint column is dropped.
A discrepancy in the time (hour) formats was also noted. Tiantan hours were recorded in 24-hour format, but excluded the zeros. To fix this, the ‘hour’ column was first subsetted into single and double digits. The ‘morning’ subset was then padded with zeros on both the right and left sides, using the str_pad() function. Similarly, the ‘afternoon’ subset was padded with zeros, but only on the right side. Finally, these two subsets were joined back together.
CRRI (DL007) hours were recorded in 24hr format with a colon in the middle. To match the format with the Tiantan data set, the colons were removed using str_replace_all().
# Tidy Tiantan data
# Merge separated date columns into one
tiantan_jan2015 %<>% unite(date, year, month, day, sep = "-")
# Manipulate 'hour' column to 24hr format
tiantan_morning <- tiantan_jan2015 %>% filter(hour < 10)
tiantan_morning$hour %<>%
str_pad(width = 3, side = "right", pad = 0) %>%
str_pad(width = 4, side = "left", pad = 0)
tiantan_afternoon <- tiantan_jan2015 %>% filter(hour > 9)
tiantan_afternoon$hour %<>%
str_pad(width = 4, side = "right", pad = 0)
tiantan_jan2015 <- bind_rows(tiantan_morning, tiantan_afternoon)
# Tidy DL007 data
# Separate 'datetime' column into 'hour' and 'date'
hour <- format(as.POSIXct(dl007_jan2015$Datetime, "%Y-%m-%d %H:%M:%S", tz=""), format = "%H:%M")
date <- format(as.POSIXct(dl007_jan2015$Datetime, "%Y-%m-%d %H:%M:%S", tz=""), format = "%Y-%m-%d")
dl007_jan2015$hour <- hour
dl007_jan2015$date <- date
dl007_jan2015 <- subset(dl007_jan2015, select = c(1, 3:18))
# Manipulate 'hour' column to 24hr format
dl007_jan2015$hour %<>% str_replace_all(pattern = ":", replacement = "")
Tidy & Manipulate Data II
A function is created to assign an Air Quality Index (AQI) rating for the variable ‘PM2.5’. The rating values are based on India’s AQI category ranges for specifically the PM2.5 measurement. The same category range is applied to both data sets, allowing them to be compared and analysed. In addition, the function can be altered to assign an AQI category based off a different measurement.
The function itself loops through each value in the column and assigns a category based on the measurement. It then captures the output and saves it to a value. The output is then tidied by removing any unwanted values. These new variables are added to their respective data sets in a new column.
# Creating a variable
tiantan_aqi <- capture.output(for (i in tiantan_jan2015$PM2.5){
if (is.na(i) || i == "") {
print(NA)
} else if (i <= 30){
print("Good")
} else if (i <= 60){
print("Satisfactory")
} else if (i <= 90){
print("Moderately polluted")
} else if (i <= 120){
print("Poor")
} else if (i <= 250){
print("Very poor")
} else {
print("Severe")
}
})
tiantan_aqi %<>%
str_replace_all(pattern = "[:punct:]", replacement = "") %>%
str_replace_all(pattern = "1 ", replacement = "")
dl007_aqi <- capture.output(for (i in dl007_jan2015$PM2.5){
if (is.na(i) || i == "") {
print(NA)
} else if (i <= 30){
print("Good")
} else if (i <= 60){
print("Satisfactory")
} else if (i <= 90){
print("Moderately polluted")
} else if (i <= 120){
print("Poor")
} else if (i <= 250){
print("Very poor")
} else {
print("Severe")
}
})
dl007_aqi %<>%
str_replace_all(pattern = "[:punct:]", replacement = "") %>%
str_replace_all(pattern = "1 ", replacement = "")
tiantan_jan2015$AQI_PM2.5 <- tiantan_aqi
dl007_jan2015$AQI_PM2.5 <- dl007_aqi
Understand
The data sets’ structure is examined and the date variables are converted the correct date format. The class is checked for both to ensure the conversion was successful. Next, the station variable is factored and its label changed to the station name, instead of the station ID.
Mutual variable columns are subsetted for both Tiantan and CRRI. The data sets are merged into one using the bind_rows() function. Once they have been joined, the AQI_PM2.5 variable is converted to a factor and ordered based on the severity of pollution (from lowest to highest danger).
# Checking data structure
str(tiantan_jan2015)
tibble [744 x 17] (S3: tbl_df/tbl/data.frame)
$ No : num [1:744] 16105 16106 16107 16108 16109 ...
$ date : chr [1:744] "2015-1-1" "2015-1-1" "2015-1-1" "2015-1-1" ...
$ hour : chr [1:744] "0000" "0100" "0200" "0300" ...
$ PM2.5 : num [1:744] 7 3 11 3 6 10 9 11 6 6 ...
$ PM10 : num [1:744] 14 15 18 18 23 17 17 17 18 18 ...
$ SO2 : num [1:744] 14 13 11 9 9 9 7 5 4 5 ...
$ NO2 : num [1:744] 29 22 14 15 16 29 34 40 50 51 ...
$ CO : num [1:744] 300 400 300 300 400 500 500 500 500 NA ...
$ O3 : num [1:744] 45 52 63 63 48 35 31 19 7 7 ...
$ TEMP : num [1:744] -7 -2 -4 -4 -4 -6 -8 -9 -6 -4 ...
$ PRES : num [1:744] 1026 1030 1031 1028 1029 ...
$ DEWP : num [1:744] -23.8 -22 -22.1 -22.6 -22.6 -23.8 -23 -22.7 -24.3 -22.6 ...
$ RAIN : num [1:744] 0 0 0 0 0 0 0 0 0 0 ...
$ wd : chr [1:744] "ENE" "NW" "ENE" "N" ...
$ WSPM : num [1:744] 0.9 3 2.1 1.4 1.6 1.7 1.3 0.8 0.9 2 ...
$ station : chr [1:744] "Tiantan" "Tiantan" "Tiantan" "Tiantan" ...
$ AQI_PM2.5: chr [1:744] "Good" "Good" "Good" "Good" ...
str(dl007_jan2015)
tibble [743 x 18] (S3: tbl_df/tbl/data.frame)
$ station : chr [1:743] "DL007" "DL007" "DL007" "DL007" ...
$ PM2.5 : num [1:743] 599 656 658 690 625 ...
$ PM10 : num [1:743] 935 NA NA NA 977 ...
$ NO : num [1:743] 69 80.4 94.7 88.1 70.9 ...
$ NO2 : num [1:743] 35.7 35.6 35.5 35.5 35.8 ...
$ NOx : num [1:743] 105 116 130 124 107 ...
$ NH3 : num [1:743] NA NA NA NA NA NA NA NA NA NA ...
$ CO : num [1:743] 0.58 0.55 0.51 0.53 0.58 0.61 0.67 0.67 0.74 0.75 ...
$ SO2 : num [1:743] NA NA NA NA NA NA NA NA NA NA ...
$ O3 : num [1:743] 108 121 NA NA NA ...
$ Benzene : num [1:743] 12.9 14.6 16.1 15.7 13.3 ...
$ Toluene : num [1:743] 19.9 22.5 24.7 24.2 20.6 ...
$ Xylene : num [1:743] 13.8 15.6 17.2 16.8 14.3 ...
$ AQI : num [1:743] NA NA NA NA NA NA NA NA NA NA ...
$ AQI_Bucket: chr [1:743] NA NA NA NA ...
$ hour : chr [1:743] "0100" "0200" "0300" "0400" ...
$ date : chr [1:743] "2015-01-01" "2015-01-01" "2015-01-01" "2015-01-01" ...
$ AQI_PM2.5 : chr [1:743] "Severe" "Severe" "Severe" "Severe" ...
# Convert to correct data type
tiantan_jan2015$date %<>% ymd()
class(tiantan_jan2015$date)
[1] "Date"
dl007_jan2015$date %<>% ymd()
class(dl007_jan2015$date)
[1] "Date"
# Factoring
tiantan_2015$station %<>% factor(levels = "Tiantan", labels = "Tiantan")
dl007_jan2015$station %<>% factor(levels = "DL007", labels = "CRRI")
crri_jan2015 <- dl007_jan2015
# Subset mutual variables
crri_jan2015_sub <- subset(crri_jan2015, select = c(1:3, 5, 8, 10, 16:18))
tiantan_jan2015_sub <- subset(tiantan_jan2015, select = c(2:5, 7:9, 16:17))
# Join both data sets
crri_tiantan <- bind_rows(tiantan_jan2015_sub, crri_jan2015_sub)
str(crri_tiantan)
tibble [1,487 x 9] (S3: tbl_df/tbl/data.frame)
$ date : Date[1:1487], format: "2015-01-01" "2015-01-01" "2015-01-01" ...
$ hour : chr [1:1487] "0000" "0100" "0200" "0300" ...
$ PM2.5 : num [1:1487] 7 3 11 3 6 10 9 11 6 6 ...
$ PM10 : num [1:1487] 14 15 18 18 23 17 17 17 18 18 ...
$ NO2 : num [1:1487] 29 22 14 15 16 29 34 40 50 51 ...
$ CO : num [1:1487] 300 400 300 300 400 500 500 500 500 NA ...
$ O3 : num [1:1487] 45 52 63 63 48 35 31 19 7 7 ...
$ station : chr [1:1487] "Tiantan" "Tiantan" "Tiantan" "Tiantan" ...
$ AQI_PM2.5: chr [1:1487] "Good" "Good" "Good" "Good" ...
# Refactor and order variable
crri_tiantan$AQI_PM2.5 %<>%
factor(levels = c("Good", "Satisfactory", "Moderately polluted", "Poor", "Very poor", "Severe", "NA"),
ordered = TRUE)
head(crri_tiantan)
NA
Scan I
Missing values are scanned for the PM2.5 column first using is.na(), as that is one of the primary variables being used in this report. Finding no missing values but knowing there are some from viewing the data, an appropriate conversion is applied to ensure that “NA”s show up as missing values. Running is.na() again, it is found that there are ten missing values.
Scanning the entire data set using the same function, 52 missing values were found. Seeing as this value is less than 5% of the data set, the decision is to omit the NA values in any further analysis of the data using na.omit().
Special values were scanned for in the numeric columns (3-7). A for loop function is set up to check for each of the following special values in each column selected:
- -Inf: negative infinity
- Inf: positive infinity
- NaN: not a number
- NA: missing value
The output results demonstrate that there are no infinite values and no not-a-number values in these columns, and the finite and missing values add up accordingly. Thus, no further actions are taken.
# Missing values
is.na(crri_tiantan$AQI_PM2.5) %>% sum()
[1] 0
crri_tiantan$AQI_PM2.5[which(crri_tiantan$AQI_PM2.5 == "NA")] <- NA
is.na(crri_tiantan$AQI_PM2.5) %>% sum()
[1] 10
is.na(crri_tiantan) %>% sum()
[1] 52
# Special values
crri_tiantan_sub <- subset(crri_tiantan, select = 3:7)
for (i in crri_tiantan_sub) {
print(sum(is.finite(i)))
print(sum(is.infinite(i)))
print(sum(is.nan(i)))
print(sum(is.na(i)))
}
[1] 1477
[1] 0
[1] 0
[1] 10
[1] 1474
[1] 0
[1] 0
[1] 13
[1] 1484
[1] 0
[1] 0
[1] 3
[1] 1483
[1] 0
[1] 0
[1] 4
[1] 1475
[1] 0
[1] 0
[1] 12
Scan II
The variable PM2.5 is summarised and grouped by its station. It is then plotted using a boxplot to visually determine if there are any outliers. Both stations have numerous outliers, with CRRI having the most. Upon locating these in the data set, it is found that there is quite a range in differences for some of the other variable measurements. Therefore, some of these outliers could be attributed to “measurement errors”.
Method 1:
It is decided that these outliers will be capped using the cap() function defined. The new data is plotted using a boxplot to confirm the removal of outliers.
Method 2:
There are a lot of factors that influence the value of these measurements, and they could still be considered valid and valuable data points. As there is such a high quantity of outliers, the data for the variable PM2.5 will be transformed first, and remaining outliers will be dealt with accordingly afterwards.
crri_tiantan %>% group_by(station) %>% summarise(Min = min(PM2.5, na.rm = TRUE),
Q1 = quantile(PM2.5,probs = .25,na.rm = TRUE),
Median = median(PM2.5, na.rm = TRUE),
Q3 = quantile(PM2.5,probs = .75,na.rm = TRUE),
Max = max(PM2.5,na.rm = TRUE),
Mean = mean(PM2.5, na.rm = TRUE),
SD = sd(PM2.5, na.rm = TRUE),
n = n(),
Missing = sum(is.na(PM2.5)))
`summarise()` ungrouping output (override with `.groups` argument)
boxplot(PM2.5 ~ station, data = crri_tiantan,
main = "Levels Particulate Matter in the Air in Beijing and Delhi",
ylab = "Weather Station",
xlab = "Particulate Matter (PM2.5)",
col = c("darkorange", "firebrick1"),
horizontal = TRUE)

# Method 1:
hist(crri_tiantan$PM2.5,
main = "Levels Particulate Matter in the Air in Beijing and Delhi",
xlab = "Particulate Matter (PM2.5)",
col = "pink")

cap <- function(x){
quantiles <- quantile(x, c(0.05, 0.25, 0.75, 0.95))
x[x < quantiles[2] - 1.5*IQR(x)] <- quantiles[1]
x[x > quantiles[3] + 1.5*IQR(x)] <- quantiles[4]
x
}
capped_ct <- cap(na.omit(crri_tiantan$PM2.5))
boxplot(capped_ct,
main = "Levels Particulate Matter in the Air in Beijing and Delhi",
xlab = "Particulate Matter (PM2.5)",
col = "pink",
horizontal = TRUE)

Transform
It is observed from the boxplot above that the variable PM2.5 is heavily positively skewed. In attempts to decrease the skewness, several data transformations were performed. The majority of them proved unsuccessful in decreasing the skewness of the variable, except the two functions sqrt() and BoxCox().
Method 1:
The sqrt() function proved to do a better job in balancing the distribution, and so that method is chosen.
Method 2:
An additional boxplot was plotted for the two, on top of the histogram. After further comparison between them, it was decided that the BoxCox() method was more evenly distributed and thus the better method to continue with. There are now only three outliers in the data for PM2.5, which is considerably less than previously. These outliers are capped using the cap() function that has been defined above.
Both distributions are checked for normality using a QQ-Plot. Even though it is significantly less skewed, the data still does not fall completely between the blue lines that indicate normality. Finally, both are added to the data set as new variables. Note that final distribution plots are highlighted in colour.
log_ct <- log10(capped_ct)
hist(log_ct, breaks = 20)

ln_ct <- log(capped_ct)
hist(ln_ct, breaks = 20)

rec_ct <- 1 / capped_ct
hist(rec_ct)

centre_ct <- scale(capped_ct, center = TRUE, scale = FALSE)
hist(centre_ct)

scale_ct2 <- scale(capped_ct, center = FALSE, scale = sd(capped_ct))
hist(scale_ct2, breaks = 20)

z_ct <- scale(capped_ct, center = TRUE, scale = TRUE)
hist(z_ct)

minmaxnormalise <- function(x){(x- min(x)) /(max(x)-min(x))}
ct_minmax <- minmaxnormalise(capped_ct)
hist(ct_minmax, breaks = 20)

ct_ew <- discretize(capped_ct, disc = "equalwidth")
hist(ct_ew$X)

ct_ed <- discretize(capped_ct, disc = "equalfreq")
hist(ct_ed$X)

# Method 1:
bc <- BoxCox(capped_ct, lambda = "auto")
hist(bc)

sqr <- sqrt(capped_ct)
hist(sqr, col = "thistle")

qqPlot(sqr)
[1] 2 4

# Method 2:
sqrt_ct <- sqrt(crri_tiantan$PM2.5)
hist(sqrt_ct, breaks = 20)

boxplot(sqrt_ct, horizontal = TRUE, main = "Boxplot of sqrt_ct")

bc_ct <- BoxCox(crri_tiantan$PM2.5, lambda = "auto")
hist(bc_ct, breaks = 20)

boxplot(bc_ct, horizontal = TRUE, main = "Boxplot of bc_ct")

bc_ct2 <- bc_ct %>% na.omit()
bc_ct3 <- cap(bc_ct2)
boxplot(bc_ct3, horizontal = TRUE, main = "Boxplot of bc_ct3")

hist(bc_ct3, breaks = 20, col = "powderblue")

qqPlot(bc_ct3, dist = "norm")
[1] 744 740

# Adding to the data set
crri_tiantan %<>% drop_na(PM2.5)
crri_tiantan$PM2.5_alt1 <- sqr
crri_tiantan$PM2.5_alt2 <- bc_ct3
LS0tDQp0aXRsZTogIk1BVEgyMzQ5IERhdGEgV3JhbmdsaW5nIHwgQWlyIFF1YWxpdHkgaW4gQmVpamluZyBhbmQgSW5kaWEsIEphbnVhcnkgMjAxNSINCmF1dGhvcjogIkthaXRseW4gTGVlIHwgczM4NzE2MzAiDQpzdWJ0aXRsZTogQXNzaWdubWVudCAyDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQogIGZpZ193aWR0aDogNg0KICBmaWdfaGVpZ2h0OiA0DQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQojIyBSZXF1aXJlZCBwYWNrYWdlcyANCg0KYGBge3IsIHJlc3VsdHM9J2hpZGUnfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShmb3JlY2FzdCkNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShjYXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGluZm90aGVvKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkobWFncml0dHIpDQpgYGANCg0KDQojIyBFeGVjdXRpdmUgU3VtbWFyeSANCg0KVGhlIGRhdGEgc2V0cyBjaG9zZW4gY29udGFpbmVkIG51bWVyb3VzIG9ic2VydmF0aW9ucyB0YWtlbiBhY3Jvc3MgbWFueSB5ZWFycyBhbmQgdmFyaWFibGVzLiBJbnN0ZWFkIG9mIHdvcmtpbmcgd2l0aCBhbGwgb2YgaXQsIGEgcG9ydGlvbiB3YXMgc2VsZWN0ZWQgZnJvbSBib3RoIGZpbGVzIG92ZXIgdGhlIHNhbWUgcGVyaW9kLiBJdCB3YXMgZGVjaWRlZCB0aGF0IGl0IHdvdWxkIGJlIGVhc2llciB0byB0aWR5IGVhY2ggb2YgdGhlc2Ugc2VwYXJhdGVseSwgYmVmb3JlIG1lcmdpbmcgdGhlIHR3by4gDQoNClRoZSBkYXRlIGFuZCB0aW1lIGNvbHVtbnMgaW4gZWFjaCB3ZXJlIGJvdGggbWVzc3kgZm9yIG9wcG9zaXRlIHJlYXNvbnMuIFRpYW50YW4gaGFkIHRoZXNlIHNwcmVhZCBhY3Jvc3MgbXVsdGlwbGUgY29sdW1ucywgYW5kIGhlbmNlIHRoZXkgd2VyZSB1bml0ZWQuIE1lYW53aGlsZSwgQ1JSSSBoYWQgdGhlIGRhdGUgYW5kIHRpbWUgdG9nZXRoZXIgaW4gb25lIGNvbHVtbiwgYW5kIHRoaXMgd2FzIHNlcGFyYXRlZC4gQWZ0ZXIgdGhpcyB3YXMgY29tcGxldGVkLCB2YXJpb3VzIHN0cmluZyBtYW5pcHVsYXRpb25zIHdlcmUgcGVyZm9ybWVkIHRvIHRoZSB0aW1lIChob3VyKSB2YXJpYWJsZSB0byBlbnN1cmUgdGhhdCB0aGUgZm9ybWF0IHdhcyB0aGUgc2FtZSBhY3Jvc3MgYm90aCBkYXRhIHNldHMuDQoNCkEgdmFyaWFibGUgd2FzIGNyZWF0ZWQgZm9yIGJvdGggZGF0YSBzZXRzLCB1c2luZyBhIGZ1bmN0aW9uIHRoYXQgYXNzaWducyBhbiBBaXIgUXVhbGl0eSBSYXRpbmcgKEFRSSkgY2F0ZWdvcnkgdmFsdWUgKGJhc2VkIG9uIEluZGlhJ3MgQVFJIGNhdGVnb3J5IHZhbHVlcykgZm9yIFBNMi41LiBUaGlzIG5ldyB2YXJpYWJsZSBpcyB0aGVuIGFibGUgdG8gYmUgZmFjdG9yZWQgYW5kIG9yZGVyZWQgYmFzZWQgb24gc2V2ZXJpdHkgbGV2ZWxzLg0KDQpUaGUgc3RydWN0dXJlIHdhcyB0aGVuIGNoZWNrZWQgZm9yIFRpYW50YW4gYW5kIENSUkkuIEFwcHJvcHJpYXRlIGRhdGUgY29udmVyc2lvbnMgd2VyZSBtYWRlLCBhcyB3ZWxsIGFzIGZhY3RvcmluZyB2YXJpb3VzIHZhcmlhYmxlcy4gVGhlIHR3byBkYXRhIHNldHMgd2VyZSB0aGVuIHN1YnNldHRlZCB0byBvbmx5IGluY2x1ZGUgbXV0dWFsIHZhcmlhYmxlcy4gT25jZSBjb21wbGV0ZWQsIGJvdGggd2VyZSBmaW5hbGx5IG1lcmdlZCB1c2luZyB0aGUgYmluZF9yb3dzKCkgZnVuY3Rpb24uDQoNCk1pc3NpbmcgYW5kIHNwZWNpYWwgdmFsdWVzIHdlcmUgc2Nhbm5lZCBmb3IgYW5kIHJlY29yZGVkLiBTZWVpbmcgYXMgdGhlcmUgd2FzIGxlc3MgdGhhbiA1JSBvZiB0aGUgZGF0YSByZWNvcmRlZCBhcyAnTkEnLCBpdCB3YXMgZGVjaWRlZCB0byBsZWF2ZSB0aGVzZSB2YWx1ZXMgb3V0LiBUaGVyZSB3ZXJlIG5vIHNwZWNpYWwgdmFsdWVzIGZvdW5kIGluIGFueSBvZiB0aGUgbnVtZXJpYyB2YXJpYWJsZSBjb2x1bW5zLiAgIA0KDQpUaGUgZGF0YSB3ZXJlIHBsb3R0ZWQgdXNpbmcgYSBib3hwbG90IHRvIHZpc3VhbGx5IHNjYW4gZm9yIG91dGxpZXJzIGluIHRoZSBQTTIuNSB2YXJpYWJsZS4gTm90aW5nIGhpZ2ggcXVhbnRpdGllcywgdGhlc2Ugd2VyZSBkZWFsdCB3aXRoIHVzaW5nIHR3byBtZXRob2RzLiBUaGUgZmlyc3Qgd2FzIGNhcHBpbmcgdGhlIHZhcmlhYmxlIGJlZm9yZSB0cmFuc2Zvcm1pbmcgaXQgdG8gcmVkdWNlIHRoZSBwb3NpdGl2ZSBza2V3bmVzcy4gVGhlIHNlY29uZCB3YXMgdHJhbnNmb3JtaW5nIDIuNVBNLCBhbmQgdGhlbiByZW1vdmluZyBhbnkgb3V0c3RhbmRpbmcgb3V0bGllcnMgYnkgY2FwcGluZy4gQm90aCBvdXRjb21lcyB3ZXJlIGF0dGFjaGVkIHRvIHRoZSBkYXRhIHNldCBhcyBuZXcgdmFyaWFibGVzLg0KDQojIyBEYXRhIA0KDQpUaGUgQmVpamluZyBNdWx0aS1TaXRlIEFpciBRdWFsaXR5IGRhdGEgc2V0IGNvbnRhaW5zIGhvdXJseSBtZXRlb3JvbG9naWNhbCBhbmQgYWlyIHBvbGx1dGFudCBtZWFzdXJlbWVudHMgZnJvbSBzZXZlcmFsIHdlYXRoZXIgbW9uaXRvcmluZyBzaXRlcywgYmV0d2VlbiB0aGUgeWVhcnMgb2YgMjAxMy0yMDE3LiBJdCBjb250YWlucyAzNSwwNjQgb2JzZXJ2YXRpb25zIGFjcm9zcyAxOCB2YXJpYWJsZXMsIGhvd2V2ZXIgdGhpcyByZXBvcnQgd2lsbCBmb2N1cyBvbiB0aGUgZm9sbG93aW5nIHZhcmlhYmxlcyB0YWtlbiBkdXJpbmcgSmFudWFyeSAyMDE1Og0KDQoqIGRhdGU6IHRoZSBkYXRlIHRoZSBtZWFzdXJlbWVudCBpcyByZWNvcmRlZA0KKiBob3VyOiB0aGUgdGltZSB0aGUgbWVhc3VyZW1lbnQgaXMgcmVjb3JkZWQNCiogUE0yLjU6IHRoZSBhbW91bnQgb2YgcGFydGljdWxhdGUgbWF0dGVyIHNpemUgbGVzcyB0aGFuIDIuNSBtaWNyb21ldHJlcyANCiogUE0xMDogdGhlIGFtb3VudCBvZiBwYXJ0aWN1bGF0ZSBtYXR0ZXIgc2l6ZSBsZXNzIHRoYW4gMTAgbWljcm9tZXRyZXMgDQoqIE5PMjogdGhlIGFtb3VudCBvZiBuaXRyb2dlbiBkaW94aWRlDQoqIENPOiB0aGUgYW1vdW50IG9mIGNhcmJvbiBveGlkZQ0KKiBPMzogdGhlIGFtb3VudCBvZiBvem9uZQ0KKiBzdGF0aW9uOiB0aGUgc3RhdGlvbiB3aGVyZSB0aGUgbWVhc3VyZW1lbnQgaXMgcmVjb3JkZWQgYXQNCg0KVGhlIHNlY29uZCBkYXRhIHNldCAtIEFpciBRdWFsaXR5IERhdGEgaW4gSW5kaWEgLSBzaW1pbGFybHkgY29udGFpbnMgaG91cmx5IGFpciBwb2xsdXRhbnQgbWVhc3VyZW1lbnRzIGZyb20gc2V2ZXJhbCB3ZWF0aGVyIG1vbml0b3Jpbmcgc2l0ZXMsIGJldHdlZW4gMjAxNSBhbmQgMjAyMC4gSXQgY29udGFpbnMgMiw1ODksMDgzIG9ic2VydmF0aW9ucyBhY3Jvc3MgMTYgdmFyaWFibGVzLiBUbyBjb21wYXJlIHRoZSBkYXRhIGZvciBib3RoIHNldHMsIHRoZSBzYW1lIHZhcmlhYmxlcyAoYXMgbGlzdGVkIGFib3ZlKSB3aWxsIGJlIHVzZWQuDQoNClRoZSBmaXJzdCBkYXRhIHNldCB3YXMgc291cmNlZCBmcm9tIEJlaWppbmcgTXVuaWNpcGFsIEVuaXJvbm1lbnRhbCBNb25pdGlvcmluZyBDZW50ZXIsIGFuZCBkb3dubG9hZGVkIGZyb20gdGhlIGZvbGxvd2luZyBVUkw6IGh0dHA6Ly9hcmNoaXZlLmljcy51Y2kuZWR1L21sL2RhdGFzZXRzL0JlaWppbmcrTXVsdGktU2l0ZStBaXItUXVhbGl0eStEYXRhIyAuIFRoZSBzZWNvbmQgZGF0YSBzZXQgd2FzIHNvdXJjZWQgZnJvbSB0aGUgQ2VudHJhbCBQb2xsdXRpb24gQ29udHJvbCBCb2FyZCwgYW5kIGRvd25sb2FkZWQgZnJvbSB0aGUgZm9sbG93aW5nIFVSTDogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9yb2hhbnJhby9haXItcXVhbGl0eS1kYXRhLWluLWluZGlhP3NlbGVjdD1zdGF0aW9uX2hvdXIuY3N2IA0KDQpUaGUgZGF0YSBmaWxlcyBhcmUgaW1wb3J0ZWQgaW50byBSIHVzaW5nIHJlYWRfY3N2KCkgYW5kIHZpZXdlZCB1c2luZyB0aGUgaGVhZCgpIGZ1bmN0aW9uLiBUaGUgZmlsZXMgYXJlIHRoZW4gc3Vic2V0dGVkIHRvIG9ubHkgaW5jbHVkZSB0aGUgZGF0YSBmcm9tIEphbnVhcnkgMjAxNSBmcm9tIHRoZSBUaWFudGFuIFN0YXRpb24gYW5kIERMMDA3IFN0YXRpb24uICdTdGF0aW9uSUQnIGlzIHJlbmFtZWQgdG8gJ3N0YXRpb24nIGZvciB1bmlmb3JtaXR5IGFjcm9zcyBib3RoIGRhdGEgc2V0cy4gTm90ZSB0aGF0IHRoZXNlIHR3byBkYXRhIHNldHMgd2lsbCBiZSBtZXJnZWQgYWZ0ZXIgdGhleSBoYXZlIGJlZW4gdGlkaWVkLiAgDQoNCmBgYHtyfQ0KIyBTZXQgd29ya2luZyBkaXJlY3RvcnkNCnNldHdkKCJDOi9Vc2Vycy9BZ2VudCAwMDcvRGVza3RvcC8yMDIwL01BVEgyMzQ5IERhdGEgV3JhbmdsaW5nL0EyIikNCg0KIyBJbXBvcnQgYW5kIGluc3BlY3QgZGF0YQ0KdGlhbnRhbiA8LSByZWFkX2NzdigiVGlhbnRhbi5jc3YiKQ0KaW5kaWEgPC0gcmVhZF9jc3YoInN0YXRpb25faG91ci5jc3YiKQ0KDQpoZWFkKHRpYW50YW4pDQpoZWFkKGluZGlhKQ0KIyBTdWJzZXQgZGF0YSB0byBjb250YWluIG9ubHkgSmFudWFyeSAyMDE1DQp0aWFudGFuXzIwMTUgPC0gdGlhbnRhblt0aWFudGFuJHllYXIgPT0gIjIwMTUiLF0NCnRpYW50YW5famFuMjAxNSA8LSB0aWFudGFuXzIwMTVbdGlhbnRhbl8yMDE1JG1vbnRoID09IDEsXQ0KDQppbmRpYV9kbDAwNyA8LSBpbmRpYVtpbmRpYSRTdGF0aW9uSWQgPT0gIkRMMDA3IixdDQpkbDAwN19qYW4yMDE1IDwtIGluZGlhX2RsMDA3WzE6NzQzLF0NCg0KIyBSZW5hbWUgY29sdW1uDQpkbDAwN19qYW4yMDE1ICU8PiUgcmVuYW1lKHN0YXRpb24gPSBTdGF0aW9uSWQpDQoNCmBgYA0KDQoNCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSSANCg0KRmlyc3RseSwgdGhlIFRpYW50YW4gZGF0YSBzZXQgaGFzIHRoZSBkYXksIG1vbnRoLCBhbmQgeWVhciBjb2x1bW5zIHNlcGFyYXRlZCBmb3IgdGhlIGRhdGUuIFRoaXMgY29udHJpYnV0ZXMgdG8gdGhlICd1bnRpZGluZXNzJyBvZiB0aGUgZGF0YSBzaW5jZSBhIHNpbmdsZSBzdWJqZWN0IGlzIG92ZXIgbXVsdGlwbGUgY29sdW1ucywgYW5kIHRoZXJlZm9yZSBtYWtlcyBpdCAnd2lkZSBmb3JtYXQnLiBUaGUgQ1JSSSAoREwwMDcpIGRhdGEgc2V0IGlzIHVudGlkeSBhcyBvbiB0aGUgb3RoZXIgaGFuZCwgdGhlIGRhdGUgYW5kIHRpbWUgYXJlIG1lcmdlZCBpbnRvIG9uZSBjb2x1bW4uIFRodXMsIGV2ZXJ5IHZhcmlhYmxlIGRvZXMgbm90IGhhdmUgaXRzIG93biBjb2x1bW4uIA0KDQpUbyB0aWR5IHRoZXNlIHVwLCB0aGUgeWVhciwgbW9udGgsIGFuZCBkYXkgZm9yIFRpYW50YW4gaXMgdW5pdGVkIGludG8gb25lIGNvbHVtbi4gVGhlIGRhdGUgYW5kIHRpbWUgaXMgc2VwYXJhdGVkIGludG8gdGhlaXIgb3duIGNvbHVtbnMgZm9yIENSUkksIGFuZCB0aGUgb3JpZ2luYWwgam9pbnQgY29sdW1uIGlzIGRyb3BwZWQuIA0KDQpBIGRpc2NyZXBhbmN5IGluIHRoZSB0aW1lIChob3VyKSBmb3JtYXRzIHdhcyBhbHNvIG5vdGVkLiBUaWFudGFuIGhvdXJzIHdlcmUgcmVjb3JkZWQgaW4gMjQtaG91ciBmb3JtYXQsIGJ1dCBleGNsdWRlZCB0aGUgemVyb3MuIFRvIGZpeCB0aGlzLCB0aGUgJ2hvdXInIGNvbHVtbiB3YXMgZmlyc3Qgc3Vic2V0dGVkIGludG8gc2luZ2xlIGFuZCBkb3VibGUgZGlnaXRzLiBUaGUgJ21vcm5pbmcnIHN1YnNldCB3YXMgdGhlbiBwYWRkZWQgd2l0aCB6ZXJvcyBvbiBib3RoIHRoZSByaWdodCBhbmQgbGVmdCBzaWRlcywgdXNpbmcgdGhlIHN0cl9wYWQoKSBmdW5jdGlvbi4gU2ltaWxhcmx5LCB0aGUgJ2FmdGVybm9vbicgc3Vic2V0IHdhcyBwYWRkZWQgd2l0aCB6ZXJvcywgYnV0IG9ubHkgb24gdGhlIHJpZ2h0IHNpZGUuIEZpbmFsbHksIHRoZXNlIHR3byBzdWJzZXRzIHdlcmUgam9pbmVkIGJhY2sgdG9nZXRoZXIuIA0KDQpDUlJJIChETDAwNykgaG91cnMgd2VyZSByZWNvcmRlZCBpbiAyNGhyIGZvcm1hdCB3aXRoIGEgY29sb24gaW4gdGhlIG1pZGRsZS4gVG8gbWF0Y2ggdGhlIGZvcm1hdCB3aXRoIHRoZSBUaWFudGFuIGRhdGEgc2V0LCB0aGUgY29sb25zIHdlcmUgcmVtb3ZlZCB1c2luZyBzdHJfcmVwbGFjZV9hbGwoKS4NCg0KYGBge3J9DQojIFRpZHkgVGlhbnRhbiBkYXRhDQojIE1lcmdlIHNlcGFyYXRlZCBkYXRlIGNvbHVtbnMgaW50byBvbmUNCnRpYW50YW5famFuMjAxNSAlPD4lIHVuaXRlKGRhdGUsIHllYXIsIG1vbnRoLCBkYXksIHNlcCA9ICItIikNCg0KIyBNYW5pcHVsYXRlICdob3VyJyBjb2x1bW4gdG8gMjRociBmb3JtYXQNCnRpYW50YW5fbW9ybmluZyA8LSB0aWFudGFuX2phbjIwMTUgJT4lIGZpbHRlcihob3VyIDwgMTApIA0KdGlhbnRhbl9tb3JuaW5nJGhvdXIgJTw+JSANCiAgc3RyX3BhZCh3aWR0aCA9IDMsIHNpZGUgPSAicmlnaHQiLCBwYWQgPSAwKSAlPiUNCiAgc3RyX3BhZCh3aWR0aCA9IDQsIHNpZGUgPSAibGVmdCIsIHBhZCA9IDApDQoNCnRpYW50YW5fYWZ0ZXJub29uIDwtIHRpYW50YW5famFuMjAxNSAlPiUgZmlsdGVyKGhvdXIgPiA5KQ0KdGlhbnRhbl9hZnRlcm5vb24kaG91ciAlPD4lDQogIHN0cl9wYWQod2lkdGggPSA0LCBzaWRlID0gInJpZ2h0IiwgcGFkID0gMCkNCg0KdGlhbnRhbl9qYW4yMDE1IDwtIGJpbmRfcm93cyh0aWFudGFuX21vcm5pbmcsIHRpYW50YW5fYWZ0ZXJub29uKQ0KDQojIFRpZHkgREwwMDcgZGF0YQ0KIyBTZXBhcmF0ZSAnZGF0ZXRpbWUnIGNvbHVtbiBpbnRvICdob3VyJyBhbmQgJ2RhdGUnIA0KaG91ciA8LSBmb3JtYXQoYXMuUE9TSVhjdChkbDAwN19qYW4yMDE1JERhdGV0aW1lLCAiJVktJW0tJWQgJUg6JU06JVMiLCB0ej0iIiksIGZvcm1hdCA9ICIlSDolTSIpDQpkYXRlIDwtIGZvcm1hdChhcy5QT1NJWGN0KGRsMDA3X2phbjIwMTUkRGF0ZXRpbWUsICIlWS0lbS0lZCAlSDolTTolUyIsIHR6PSIiKSwgZm9ybWF0ID0gIiVZLSVtLSVkIikNCmRsMDA3X2phbjIwMTUkaG91ciA8LSBob3VyDQpkbDAwN19qYW4yMDE1JGRhdGUgPC0gZGF0ZQ0KZGwwMDdfamFuMjAxNSA8LSBzdWJzZXQoZGwwMDdfamFuMjAxNSwgc2VsZWN0ID0gYygxLCAzOjE4KSkNCg0KIyBNYW5pcHVsYXRlICdob3VyJyBjb2x1bW4gdG8gMjRociBmb3JtYXQNCmRsMDA3X2phbjIwMTUkaG91ciAlPD4lIHN0cl9yZXBsYWNlX2FsbChwYXR0ZXJuID0gIjoiLCByZXBsYWNlbWVudCA9ICIiKQ0KDQpgYGANCg0KIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJSSANCg0KQSBmdW5jdGlvbiBpcyBjcmVhdGVkIHRvIGFzc2lnbiBhbiBBaXIgUXVhbGl0eSBJbmRleCAoQVFJKSByYXRpbmcgZm9yIHRoZSB2YXJpYWJsZSAnUE0yLjUnLiBUaGUgcmF0aW5nIHZhbHVlcyBhcmUgYmFzZWQgb24gSW5kaWEncyBBUUkgY2F0ZWdvcnkgcmFuZ2VzIGZvciBzcGVjaWZpY2FsbHkgdGhlIFBNMi41IG1lYXN1cmVtZW50LiBUaGUgc2FtZSBjYXRlZ29yeSByYW5nZSBpcyBhcHBsaWVkIHRvIGJvdGggZGF0YSBzZXRzLCBhbGxvd2luZyB0aGVtIHRvIGJlIGNvbXBhcmVkIGFuZCBhbmFseXNlZC4gSW4gYWRkaXRpb24sIHRoZSBmdW5jdGlvbiBjYW4gYmUgYWx0ZXJlZCB0byBhc3NpZ24gYW4gQVFJIGNhdGVnb3J5IGJhc2VkIG9mZiBhIGRpZmZlcmVudCBtZWFzdXJlbWVudC4NCg0KVGhlIGZ1bmN0aW9uIGl0c2VsZiBsb29wcyB0aHJvdWdoIGVhY2ggdmFsdWUgaW4gdGhlIGNvbHVtbiBhbmQgYXNzaWducyBhIGNhdGVnb3J5IGJhc2VkIG9uIHRoZSBtZWFzdXJlbWVudC4gSXQgdGhlbiBjYXB0dXJlcyB0aGUgb3V0cHV0IGFuZCBzYXZlcyBpdCB0byBhIHZhbHVlLiBUaGUgb3V0cHV0IGlzIHRoZW4gdGlkaWVkIGJ5IHJlbW92aW5nIGFueSB1bndhbnRlZCB2YWx1ZXMuIFRoZXNlIG5ldyB2YXJpYWJsZXMgYXJlIGFkZGVkIHRvIHRoZWlyIHJlc3BlY3RpdmUgZGF0YSBzZXRzIGluIGEgbmV3IGNvbHVtbi4gDQoNCmBgYHtyfQ0KIyBDcmVhdGluZyBhIHZhcmlhYmxlDQp0aWFudGFuX2FxaSA8LSBjYXB0dXJlLm91dHB1dChmb3IgKGkgaW4gdGlhbnRhbl9qYW4yMDE1JFBNMi41KXsNCiAgaWYgKGlzLm5hKGkpIHx8IGkgPT0gIiIpIHsNCiAgICBwcmludChOQSkNCiAgfSBlbHNlIGlmIChpIDw9IDMwKXsNCiAgICBwcmludCgiR29vZCIpDQogIH0gZWxzZSBpZiAoaSA8PSA2MCl7DQogICAgcHJpbnQoIlNhdGlzZmFjdG9yeSIpDQogIH0gZWxzZSBpZiAoaSA8PSA5MCl7DQogICAgcHJpbnQoIk1vZGVyYXRlbHkgcG9sbHV0ZWQiKQ0KICB9IGVsc2UgaWYgKGkgPD0gMTIwKXsNCiAgICBwcmludCgiUG9vciIpDQogIH0gZWxzZSBpZiAoaSA8PSAyNTApew0KICAgIHByaW50KCJWZXJ5IHBvb3IiKQ0KICB9IGVsc2Ugew0KICAgIHByaW50KCJTZXZlcmUiKQ0KICB9DQp9KQ0KDQp0aWFudGFuX2FxaSAlPD4lIA0KICBzdHJfcmVwbGFjZV9hbGwocGF0dGVybiA9ICJbOnB1bmN0Ol0iLCByZXBsYWNlbWVudCA9ICIiKSAlPiUNCiAgc3RyX3JlcGxhY2VfYWxsKHBhdHRlcm4gPSAiMSAiLCByZXBsYWNlbWVudCA9ICIiKQ0KDQpkbDAwN19hcWkgPC0gY2FwdHVyZS5vdXRwdXQoZm9yIChpIGluIGRsMDA3X2phbjIwMTUkUE0yLjUpew0KICBpZiAoaXMubmEoaSkgfHwgaSA9PSAiIikgew0KICAgIHByaW50KE5BKQ0KICB9IGVsc2UgaWYgKGkgPD0gMzApew0KICAgIHByaW50KCJHb29kIikNCiAgfSBlbHNlIGlmIChpIDw9IDYwKXsNCiAgICBwcmludCgiU2F0aXNmYWN0b3J5IikNCiAgfSBlbHNlIGlmIChpIDw9IDkwKXsNCiAgICBwcmludCgiTW9kZXJhdGVseSBwb2xsdXRlZCIpDQogIH0gZWxzZSBpZiAoaSA8PSAxMjApew0KICAgIHByaW50KCJQb29yIikNCiAgfSBlbHNlIGlmIChpIDw9IDI1MCl7DQogICAgcHJpbnQoIlZlcnkgcG9vciIpDQogIH0gZWxzZSB7DQogICAgcHJpbnQoIlNldmVyZSIpDQogIH0NCn0pDQoNCmRsMDA3X2FxaSAlPD4lIA0KICBzdHJfcmVwbGFjZV9hbGwocGF0dGVybiA9ICJbOnB1bmN0Ol0iLCByZXBsYWNlbWVudCA9ICIiKSAlPiUNCiAgc3RyX3JlcGxhY2VfYWxsKHBhdHRlcm4gPSAiMSAiLCByZXBsYWNlbWVudCA9ICIiKQ0KDQp0aWFudGFuX2phbjIwMTUkQVFJX1BNMi41IDwtIHRpYW50YW5fYXFpDQpkbDAwN19qYW4yMDE1JEFRSV9QTTIuNSA8LSBkbDAwN19hcWkNCg0KYGBgDQoNCiMjIFVuZGVyc3RhbmQgDQoNClRoZSBkYXRhIHNldHMnIHN0cnVjdHVyZSBpcyBleGFtaW5lZCBhbmQgdGhlIGRhdGUgdmFyaWFibGVzIGFyZSBjb252ZXJ0ZWQgdGhlIGNvcnJlY3QgZGF0ZSBmb3JtYXQuIFRoZSBjbGFzcyBpcyBjaGVja2VkIGZvciBib3RoIHRvIGVuc3VyZSB0aGUgY29udmVyc2lvbiB3YXMgc3VjY2Vzc2Z1bC4gTmV4dCwgdGhlIHN0YXRpb24gdmFyaWFibGUgaXMgZmFjdG9yZWQgYW5kIGl0cyBsYWJlbCBjaGFuZ2VkIHRvIHRoZSBzdGF0aW9uIG5hbWUsIGluc3RlYWQgb2YgdGhlIHN0YXRpb24gSUQuIA0KDQpNdXR1YWwgdmFyaWFibGUgY29sdW1ucyBhcmUgc3Vic2V0dGVkIGZvciBib3RoIFRpYW50YW4gYW5kIENSUkkuIFRoZSBkYXRhIHNldHMgYXJlIG1lcmdlZCBpbnRvIG9uZSB1c2luZyB0aGUgYmluZF9yb3dzKCkgZnVuY3Rpb24uIE9uY2UgdGhleSBoYXZlIGJlZW4gam9pbmVkLCB0aGUgQVFJX1BNMi41IHZhcmlhYmxlIGlzIGNvbnZlcnRlZCB0byBhIGZhY3RvciBhbmQgb3JkZXJlZCBiYXNlZCBvbiB0aGUgc2V2ZXJpdHkgb2YgcG9sbHV0aW9uIChmcm9tIGxvd2VzdCB0byBoaWdoZXN0IGRhbmdlcikuDQoNCmBgYHtyfQ0KIyBDaGVja2luZyBkYXRhIHN0cnVjdHVyZQ0Kc3RyKHRpYW50YW5famFuMjAxNSkNCnN0cihkbDAwN19qYW4yMDE1KQ0KDQojIENvbnZlcnQgdG8gY29ycmVjdCBkYXRhIHR5cGUNCnRpYW50YW5famFuMjAxNSRkYXRlICU8PiUgeW1kKCkNCmNsYXNzKHRpYW50YW5famFuMjAxNSRkYXRlKQ0KZGwwMDdfamFuMjAxNSRkYXRlICU8PiUgeW1kKCkNCmNsYXNzKGRsMDA3X2phbjIwMTUkZGF0ZSkNCg0KIyBGYWN0b3JpbmcNCnRpYW50YW5fMjAxNSRzdGF0aW9uICU8PiUgZmFjdG9yKGxldmVscyA9ICJUaWFudGFuIiwgbGFiZWxzID0gIlRpYW50YW4iKQ0KZGwwMDdfamFuMjAxNSRzdGF0aW9uICU8PiUgZmFjdG9yKGxldmVscyA9ICJETDAwNyIsIGxhYmVscyA9ICJDUlJJIikNCmNycmlfamFuMjAxNSA8LSBkbDAwN19qYW4yMDE1DQoNCiMgU3Vic2V0IG11dHVhbCB2YXJpYWJsZXMNCmNycmlfamFuMjAxNV9zdWIgPC0gc3Vic2V0KGNycmlfamFuMjAxNSwgc2VsZWN0ID0gYygxOjMsIDUsIDgsIDEwLCAxNjoxOCkpDQp0aWFudGFuX2phbjIwMTVfc3ViIDwtIHN1YnNldCh0aWFudGFuX2phbjIwMTUsIHNlbGVjdCA9IGMoMjo1LCA3OjksIDE2OjE3KSkNCg0KIyBKb2luIGJvdGggZGF0YSBzZXRzDQpjcnJpX3RpYW50YW4gPC0gYmluZF9yb3dzKHRpYW50YW5famFuMjAxNV9zdWIsIGNycmlfamFuMjAxNV9zdWIpDQpzdHIoY3JyaV90aWFudGFuKQ0KDQojIFJlZmFjdG9yIGFuZCBvcmRlciB2YXJpYWJsZQ0KY3JyaV90aWFudGFuJEFRSV9QTTIuNSAlPD4lIA0KICBmYWN0b3IobGV2ZWxzID0gYygiR29vZCIsICJTYXRpc2ZhY3RvcnkiLCAiTW9kZXJhdGVseSBwb2xsdXRlZCIsICJQb29yIiwgIlZlcnkgcG9vciIsICJTZXZlcmUiLCAiTkEiKSwNCiAgICAgICAgIG9yZGVyZWQgPSBUUlVFKQ0KDQpoZWFkKGNycmlfdGlhbnRhbikNCg0KYGBgDQoNCiMjCVNjYW4gSSANCg0KTWlzc2luZyB2YWx1ZXMgYXJlIHNjYW5uZWQgZm9yIHRoZSBQTTIuNSBjb2x1bW4gZmlyc3QgdXNpbmcgaXMubmEoKSwgYXMgdGhhdCBpcyBvbmUgb2YgdGhlIHByaW1hcnkgdmFyaWFibGVzIGJlaW5nIHVzZWQgaW4gdGhpcyByZXBvcnQuIEZpbmRpbmcgbm8gbWlzc2luZyB2YWx1ZXMgYnV0IGtub3dpbmcgdGhlcmUgYXJlIHNvbWUgZnJvbSB2aWV3aW5nIHRoZSBkYXRhLCBhbiBhcHByb3ByaWF0ZSBjb252ZXJzaW9uIGlzIGFwcGxpZWQgdG8gZW5zdXJlIHRoYXQgIk5BInMgc2hvdyB1cCBhcyBtaXNzaW5nIHZhbHVlcy4gUnVubmluZyBpcy5uYSgpIGFnYWluLCBpdCBpcyBmb3VuZCB0aGF0IHRoZXJlIGFyZSB0ZW4gbWlzc2luZyB2YWx1ZXMuIA0KDQpTY2FubmluZyB0aGUgZW50aXJlIGRhdGEgc2V0IHVzaW5nIHRoZSBzYW1lIGZ1bmN0aW9uLCA1MiBtaXNzaW5nIHZhbHVlcyB3ZXJlIGZvdW5kLiBTZWVpbmcgYXMgdGhpcyB2YWx1ZSBpcyBsZXNzIHRoYW4gNSUgb2YgdGhlIGRhdGEgc2V0LCB0aGUgZGVjaXNpb24gaXMgdG8gb21pdCB0aGUgTkEgdmFsdWVzIGluIGFueSBmdXJ0aGVyIGFuYWx5c2lzIG9mIHRoZSBkYXRhIHVzaW5nIG5hLm9taXQoKS4NCg0KU3BlY2lhbCB2YWx1ZXMgd2VyZSBzY2FubmVkIGZvciBpbiB0aGUgbnVtZXJpYyBjb2x1bW5zICgzLTcpLiBBIGZvciBsb29wIGZ1bmN0aW9uIGlzIHNldCB1cCB0byBjaGVjayBmb3IgZWFjaCBvZiB0aGUgZm9sbG93aW5nIHNwZWNpYWwgdmFsdWVzIGluIGVhY2ggY29sdW1uIHNlbGVjdGVkOg0KDQoqIC1JbmY6IG5lZ2F0aXZlIGluZmluaXR5DQoqIEluZjogcG9zaXRpdmUgaW5maW5pdHkNCiogTmFOOiBub3QgYSBudW1iZXINCiogTkE6IG1pc3NpbmcgdmFsdWUNCg0KVGhlIG91dHB1dCByZXN1bHRzIGRlbW9uc3RyYXRlIHRoYXQgdGhlcmUgYXJlIG5vIGluZmluaXRlIHZhbHVlcyBhbmQgbm8gbm90LWEtbnVtYmVyIHZhbHVlcyBpbiB0aGVzZSBjb2x1bW5zLCBhbmQgdGhlIGZpbml0ZSBhbmQgbWlzc2luZyB2YWx1ZXMgYWRkIHVwIGFjY29yZGluZ2x5LiBUaHVzLCBubyBmdXJ0aGVyIGFjdGlvbnMgYXJlIHRha2VuLg0KDQpgYGB7cn0NCiMgTWlzc2luZyB2YWx1ZXMNCmlzLm5hKGNycmlfdGlhbnRhbiRBUUlfUE0yLjUpICU+JSBzdW0oKQ0KDQpjcnJpX3RpYW50YW4kQVFJX1BNMi41W3doaWNoKGNycmlfdGlhbnRhbiRBUUlfUE0yLjUgPT0gIk5BIildIDwtIE5BDQppcy5uYShjcnJpX3RpYW50YW4kQVFJX1BNMi41KSAlPiUgc3VtKCkNCg0KaXMubmEoY3JyaV90aWFudGFuKSAlPiUgc3VtKCkgIA0KDQojIFNwZWNpYWwgdmFsdWVzDQpjcnJpX3RpYW50YW5fc3ViIDwtIHN1YnNldChjcnJpX3RpYW50YW4sIHNlbGVjdCA9IDM6NykgDQoNCmZvciAoaSBpbiBjcnJpX3RpYW50YW5fc3ViKSB7DQogIHByaW50KHN1bShpcy5maW5pdGUoaSkpKQ0KICBwcmludChzdW0oaXMuaW5maW5pdGUoaSkpKQ0KICBwcmludChzdW0oaXMubmFuKGkpKSkNCiAgcHJpbnQoc3VtKGlzLm5hKGkpKSkNCn0NCg0KYGBgDQoNCiMjCVNjYW4gSUkNClRoZSB2YXJpYWJsZSBQTTIuNSBpcyBzdW1tYXJpc2VkIGFuZCBncm91cGVkIGJ5IGl0cyBzdGF0aW9uLiBJdCBpcyB0aGVuIHBsb3R0ZWQgdXNpbmcgYSBib3hwbG90IHRvIHZpc3VhbGx5IGRldGVybWluZSBpZiB0aGVyZSBhcmUgYW55IG91dGxpZXJzLiBCb3RoIHN0YXRpb25zIGhhdmUgbnVtZXJvdXMgb3V0bGllcnMsIHdpdGggQ1JSSSBoYXZpbmcgdGhlIG1vc3QuIFVwb24gbG9jYXRpbmcgdGhlc2UgaW4gdGhlIGRhdGEgc2V0LCBpdCBpcyBmb3VuZCB0aGF0IHRoZXJlIGlzIHF1aXRlIGEgcmFuZ2UgaW4gZGlmZmVyZW5jZXMgZm9yIHNvbWUgb2YgdGhlIG90aGVyIHZhcmlhYmxlIG1lYXN1cmVtZW50cy4gVGhlcmVmb3JlLCBzb21lIG9mIHRoZXNlIG91dGxpZXJzIGNvdWxkIGJlIGF0dHJpYnV0ZWQgdG8gIm1lYXN1cmVtZW50IGVycm9ycyIuDQoNCiMjIyMjIE1ldGhvZCAxOg0KSXQgaXMgZGVjaWRlZCB0aGF0IHRoZXNlIG91dGxpZXJzIHdpbGwgYmUgY2FwcGVkIHVzaW5nIHRoZSBjYXAoKSBmdW5jdGlvbiBkZWZpbmVkLiBUaGUgbmV3IGRhdGEgaXMgcGxvdHRlZCB1c2luZyBhIGJveHBsb3QgdG8gY29uZmlybSB0aGUgcmVtb3ZhbCBvZiBvdXRsaWVycy4gIA0KDQojIyMjIyBNZXRob2QgMjoNClRoZXJlIGFyZSBhIGxvdCBvZiBmYWN0b3JzIHRoYXQgaW5mbHVlbmNlIHRoZSB2YWx1ZSBvZiB0aGVzZSBtZWFzdXJlbWVudHMsIGFuZCB0aGV5IGNvdWxkIHN0aWxsIGJlIGNvbnNpZGVyZWQgdmFsaWQgYW5kIHZhbHVhYmxlIGRhdGEgcG9pbnRzLiBBcyB0aGVyZSBpcyBzdWNoIGEgaGlnaCBxdWFudGl0eSBvZiBvdXRsaWVycywgdGhlIGRhdGEgZm9yIHRoZSB2YXJpYWJsZSBQTTIuNSB3aWxsIGJlIHRyYW5zZm9ybWVkIGZpcnN0LCBhbmQgcmVtYWluaW5nIG91dGxpZXJzIHdpbGwgYmUgZGVhbHQgd2l0aCBhY2NvcmRpbmdseSBhZnRlcndhcmRzLiANCg0KYGBge3J9DQpjcnJpX3RpYW50YW4gJT4lIGdyb3VwX2J5KHN0YXRpb24pICU+JSBzdW1tYXJpc2UoTWluID0gbWluKFBNMi41LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFExID0gcXVhbnRpbGUoUE0yLjUscHJvYnMgPSAuMjUsbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNZWRpYW4gPSBtZWRpYW4oUE0yLjUsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUTMgPSBxdWFudGlsZShQTTIuNSxwcm9icyA9IC43NSxuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1heCA9IG1heChQTTIuNSxuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1lYW4gPSBtZWFuKFBNMi41LCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNEID0gc2QoUE0yLjUsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbiA9IG4oKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNaXNzaW5nID0gc3VtKGlzLm5hKFBNMi41KSkpDQoNCmJveHBsb3QoUE0yLjUgfiBzdGF0aW9uLCBkYXRhID0gY3JyaV90aWFudGFuLA0KICAgICAgICBtYWluID0gIkxldmVscyBQYXJ0aWN1bGF0ZSBNYXR0ZXIgaW4gdGhlIEFpciBpbiBCZWlqaW5nIGFuZCBEZWxoaSIsDQogICAgICAgIHlsYWIgPSAiV2VhdGhlciBTdGF0aW9uIiwNCiAgICAgICAgeGxhYiA9ICJQYXJ0aWN1bGF0ZSBNYXR0ZXIgKFBNMi41KSIsDQogICAgICAgIGNvbCA9IGMoImRhcmtvcmFuZ2UiLCAiZmlyZWJyaWNrMSIpLA0KICAgICAgICBob3Jpem9udGFsID0gVFJVRSkNCiMgTWV0aG9kIDE6DQpoaXN0KGNycmlfdGlhbnRhbiRQTTIuNSwNCiAgICAgbWFpbiA9ICJMZXZlbHMgUGFydGljdWxhdGUgTWF0dGVyIGluIHRoZSBBaXIgaW4gQmVpamluZyBhbmQgRGVsaGkiLA0KICAgICB4bGFiID0gIlBhcnRpY3VsYXRlIE1hdHRlciAoUE0yLjUpIiwNCiAgICAgY29sID0gInBpbmsiKQ0KY2FwIDwtIGZ1bmN0aW9uKHgpew0KICBxdWFudGlsZXMgPC0gcXVhbnRpbGUoeCwgYygwLjA1LCAwLjI1LCAwLjc1LCAwLjk1KSkNCiAgeFt4IDwgcXVhbnRpbGVzWzJdIC0gMS41KklRUih4KV0gPC0gcXVhbnRpbGVzWzFdDQogIHhbeCA+IHF1YW50aWxlc1szXSArIDEuNSpJUVIoeCldIDwtIHF1YW50aWxlc1s0XQ0KICB4DQp9DQpjYXBwZWRfY3QgPC0gY2FwKG5hLm9taXQoY3JyaV90aWFudGFuJFBNMi41KSkNCmJveHBsb3QoY2FwcGVkX2N0LA0KICAgICAgICBtYWluID0gIkxldmVscyBQYXJ0aWN1bGF0ZSBNYXR0ZXIgaW4gdGhlIEFpciBpbiBCZWlqaW5nIGFuZCBEZWxoaSIsDQogICAgICAgIHhsYWIgPSAiUGFydGljdWxhdGUgTWF0dGVyIChQTTIuNSkiLA0KICAgICAgICBjb2wgPSAicGluayIsDQogICAgICAgIGhvcml6b250YWwgPSBUUlVFKQ0KDQpgYGANCg0KIyMJVHJhbnNmb3JtIA0KDQpJdCBpcyBvYnNlcnZlZCBmcm9tIHRoZSBib3hwbG90IGFib3ZlIHRoYXQgdGhlIHZhcmlhYmxlIFBNMi41IGlzIGhlYXZpbHkgcG9zaXRpdmVseSBza2V3ZWQuIEluIGF0dGVtcHRzIHRvIGRlY3JlYXNlIHRoZSBza2V3bmVzcywgc2V2ZXJhbCBkYXRhIHRyYW5zZm9ybWF0aW9ucyB3ZXJlIHBlcmZvcm1lZC4gVGhlIG1ham9yaXR5IG9mIHRoZW0gcHJvdmVkIHVuc3VjY2Vzc2Z1bCBpbiBkZWNyZWFzaW5nIHRoZSBza2V3bmVzcyBvZiB0aGUgdmFyaWFibGUsIGV4Y2VwdCB0aGUgdHdvIGZ1bmN0aW9ucyBzcXJ0KCkgYW5kIEJveENveCgpLiANCg0KIyMjIyMgTWV0aG9kIDE6DQpUaGUgc3FydCgpIGZ1bmN0aW9uIHByb3ZlZCB0byBkbyBhIGJldHRlciBqb2IgaW4gYmFsYW5jaW5nIHRoZSBkaXN0cmlidXRpb24sIGFuZCBzbyB0aGF0IG1ldGhvZCBpcyBjaG9zZW4uDQoNCiMjIyMjIE1ldGhvZCAyOg0KQW4gYWRkaXRpb25hbCBib3hwbG90IHdhcyBwbG90dGVkIGZvciB0aGUgdHdvLCBvbiB0b3Agb2YgdGhlIGhpc3RvZ3JhbS4gQWZ0ZXIgZnVydGhlciBjb21wYXJpc29uIGJldHdlZW4gdGhlbSwgaXQgd2FzIGRlY2lkZWQgdGhhdCB0aGUgQm94Q294KCkgbWV0aG9kIHdhcyBtb3JlIGV2ZW5seSBkaXN0cmlidXRlZCBhbmQgdGh1cyB0aGUgYmV0dGVyIG1ldGhvZCB0byBjb250aW51ZSB3aXRoLiBUaGVyZSBhcmUgbm93IG9ubHkgdGhyZWUgb3V0bGllcnMgaW4gdGhlIGRhdGEgZm9yIFBNMi41LCB3aGljaCBpcyBjb25zaWRlcmFibHkgbGVzcyB0aGFuIHByZXZpb3VzbHkuIFRoZXNlIG91dGxpZXJzIGFyZSBjYXBwZWQgdXNpbmcgdGhlIGNhcCgpIGZ1bmN0aW9uIHRoYXQgaGFzIGJlZW4gZGVmaW5lZCBhYm92ZS4gDQoNCkJvdGggZGlzdHJpYnV0aW9ucyBhcmUgY2hlY2tlZCBmb3Igbm9ybWFsaXR5IHVzaW5nIGEgUVEtUGxvdC4gRXZlbiB0aG91Z2ggaXQgaXMgc2lnbmlmaWNhbnRseSBsZXNzIHNrZXdlZCwgdGhlIGRhdGEgc3RpbGwgZG9lcyBub3QgZmFsbCBjb21wbGV0ZWx5IGJldHdlZW4gdGhlIGJsdWUgbGluZXMgdGhhdCBpbmRpY2F0ZSBub3JtYWxpdHkuIEZpbmFsbHksIGJvdGggYXJlIGFkZGVkIHRvIHRoZSBkYXRhIHNldCBhcyBuZXcgdmFyaWFibGVzLiBOb3RlIHRoYXQgZmluYWwgZGlzdHJpYnV0aW9uIHBsb3RzIGFyZSBoaWdobGlnaHRlZCBpbiBjb2xvdXIuDQoNCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD0zLCBmaWcuYWxpZ249J2NlbnRlcid9DQpsb2dfY3QgPC0gbG9nMTAoY2FwcGVkX2N0KQ0KaGlzdChsb2dfY3QsIGJyZWFrcyA9IDIwKQ0KbG5fY3QgPC0gbG9nKGNhcHBlZF9jdCkNCmhpc3QobG5fY3QsIGJyZWFrcyA9IDIwKQ0KcmVjX2N0IDwtIDEgLyBjYXBwZWRfY3QgIA0KaGlzdChyZWNfY3QpDQpjZW50cmVfY3QgPC0gc2NhbGUoY2FwcGVkX2N0LCBjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IEZBTFNFKQ0KaGlzdChjZW50cmVfY3QpDQpzY2FsZV9jdDIgPC0gc2NhbGUoY2FwcGVkX2N0LCBjZW50ZXIgPSBGQUxTRSwgc2NhbGUgPSBzZChjYXBwZWRfY3QpKQ0KaGlzdChzY2FsZV9jdDIsIGJyZWFrcyA9IDIwKQ0Kel9jdCA8LSBzY2FsZShjYXBwZWRfY3QsIGNlbnRlciA9IFRSVUUsIHNjYWxlID0gVFJVRSkNCmhpc3Qoel9jdCkNCm1pbm1heG5vcm1hbGlzZSA8LSBmdW5jdGlvbih4KXsoeC0gbWluKHgpKSAvKG1heCh4KS1taW4oeCkpfQ0KY3RfbWlubWF4IDwtIG1pbm1heG5vcm1hbGlzZShjYXBwZWRfY3QpDQpoaXN0KGN0X21pbm1heCwgYnJlYWtzID0gMjApIA0KY3RfZXcgPC0gZGlzY3JldGl6ZShjYXBwZWRfY3QsIGRpc2MgPSAiZXF1YWx3aWR0aCIpICANCmhpc3QoY3RfZXckWCkNCmN0X2VkIDwtIGRpc2NyZXRpemUoY2FwcGVkX2N0LCBkaXNjID0gImVxdWFsZnJlcSIpIA0KaGlzdChjdF9lZCRYKQ0KIyBNZXRob2QgMToNCmJjIDwtIEJveENveChjYXBwZWRfY3QsIGxhbWJkYSA9ICJhdXRvIikNCmhpc3QoYmMpDQpzcXIgPC0gc3FydChjYXBwZWRfY3QpDQpoaXN0KHNxciwgY29sID0gInRoaXN0bGUiKQ0KcXFQbG90KHNxcikNCiMgTWV0aG9kIDI6DQpzcXJ0X2N0IDwtIHNxcnQoY3JyaV90aWFudGFuJFBNMi41KSANCmhpc3Qoc3FydF9jdCwgYnJlYWtzID0gMjApDQpib3hwbG90KHNxcnRfY3QsIGhvcml6b250YWwgPSBUUlVFLCBtYWluID0gIkJveHBsb3Qgb2Ygc3FydF9jdCIpDQpiY19jdCA8LSBCb3hDb3goY3JyaV90aWFudGFuJFBNMi41LCBsYW1iZGEgPSAiYXV0byIpDQpoaXN0KGJjX2N0LCBicmVha3MgPSAyMCkNCmJveHBsb3QoYmNfY3QsIGhvcml6b250YWwgPSBUUlVFLCBtYWluID0gIkJveHBsb3Qgb2YgYmNfY3QiKQ0KYmNfY3QyIDwtIGJjX2N0ICU+JSBuYS5vbWl0KCkNCmJjX2N0MyA8LSBjYXAoYmNfY3QyKQ0KYm94cGxvdChiY19jdDMsIGhvcml6b250YWwgPSBUUlVFLCBtYWluID0gIkJveHBsb3Qgb2YgYmNfY3QzIikNCmhpc3QoYmNfY3QzLCBicmVha3MgPSAyMCwgY29sID0gInBvd2RlcmJsdWUiKQ0KcXFQbG90KGJjX2N0MywgZGlzdCA9ICJub3JtIikNCiMgQWRkaW5nIHRvIHRoZSBkYXRhIHNldA0KY3JyaV90aWFudGFuICU8PiUgZHJvcF9uYShQTTIuNSkNCmNycmlfdGlhbnRhbiRQTTIuNV9hbHQxIDwtIHNxcg0KY3JyaV90aWFudGFuJFBNMi41X2FsdDIgPC0gYmNfY3QzDQoNCmBgYA0KDQojIyBSZWZlcmVuY2VzDQoNCiogRHVhLCBELiBhbmQgR3JhZmYsIEMuICgyMDE5KS4gVUNJIE1hY2hpbmUgTGVhcm5pbmcgUmVwb3NpdG9yeSBbaHR0cDovL2FyY2hpdmUuaWNzLnVjaS5lZHUvbWxdLiBJcnZpbmUsIENBOiBVbml2ZXJzaXR5IG9mIENhbGlmb3JuaWEsIFNjaG9vbCBvZiBJbmZvcm1hdGlvbiBhbmQgQ29tcHV0ZXIgU2NpZW5jZS4NCipDcGNiLm5pYy5pbi4gKDIwMTkpLiBDUENCIHwgQ2VudHJhbCBQb2xsdXRpb24gQ29udHJvbCBCb2FyZC4gW29ubGluZV0gQXZhaWxhYmxlIGF0OiBodHRwczovL2NwY2IubmljLmluLyBbQWNjZXNzZWQgNCBPY3QuIDIwMjBdLg0KKiBWb3BhbmkgKDIwMTUpLiBBaXIgUXVhbGl0eSBEYXRhIGluIEluZGlhICgyMDE1IC0gMjAyMCkuIFtvbmxpbmVdIEthZ2dsZS5jb20uIEF2YWlsYWJsZSBhdDogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9yb2hhbnJhby9haXItcXVhbGl0eS1kYXRhLWluLWluZGlhP3NlbGVjdD1zdGF0aW9uX2hvdXIuY3N2IFtBY2Nlc3NlZCA0IE9jdC4gMjAyMF0uDQoqIFVjaS5lZHUuICgyMDE5KS4gVUNJIE1hY2hpbmUgTGVhcm5pbmcgUmVwb3NpdG9yeTogQmVpamluZyBNdWx0aS1TaXRlIEFpci1RdWFsaXR5IERhdGEgRGF0YSBTZXQuIFtvbmxpbmVdIEF2YWlsYWJsZSBhdDogaHR0cDovL2FyY2hpdmUuaWNzLnVjaS5lZHUvbWwvZGF0YXNldHMvQmVpamluZytNdWx0aS1TaXRlK0Fpci1RdWFsaXR5K0RhdGEjIFtBY2Nlc3NlZCA0IE9jdC4gMjAyMF0uDQoqIGh0dHBzOi8vd3d3Lm9uZWluZGlhLmNvbS8uICgyMDIwKS4gQWlyIFF1YWxpdHkgSW5kZXgsIEFpciBQb2xsdXRpb24gaW4gSW5kaWEsIEFpciBRdWFsaXR5IGluIEluZGlhIC0gT25laW5kaWEuIFtvbmxpbmVdIEF2YWlsYWJsZSBhdDogaHR0cHM6Ly93d3cub25laW5kaWEuY29tL2Fpci1xdWFsaXR5LWluZGV4LyBbQWNjZXNzZWQgNSBPY3QuIDIwMjBdLg0KKiBXaWtpcGVkaWEgQ29udHJpYnV0b3JzICgyMDIwKS4gQWlyIHF1YWxpdHkgaW5kZXguIFtvbmxpbmVdIFdpa2lwZWRpYS4gQXZhaWxhYmxlIGF0OiBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9BaXJfcXVhbGl0eV9pbmRleCNJbmRpYSBbQWNjZXNzZWQgNSBPY3QuIDIwMjBdLg0KKiBEci4gQW5pbCBEb2xndW4gKDIwMjApLiBNb2R1bGUgMS4gW29ubGluZV0gQXBwc3BvdC5jb20uIEF2YWlsYWJsZSBhdDogaHR0cDovL3JhcmUtcGhvZW5peC0xNjE2MTAuYXBwc3BvdC5jb20vc2VjdXJlZC9Nb2R1bGVfMDEuaHRtbCBbQWNjZXNzZWQgNiBPY3QuIDIwMjBdLg0KKiBEci4gQW5pbCBEb2xndW4gKDIwMjApLiBNb2R1bGUgMi4gW29ubGluZV0gQXBwc3BvdC5jb20uIEF2YWlsYWJsZSBhdDogaHR0cDovL3JhcmUtcGhvZW5peC0xNjE2MTAuYXBwc3BvdC5jb20vc2VjdXJlZC9Nb2R1bGVfMDIuaHRtbCBbQWNjZXNzZWQgNiBPY3QuIDIwMjBdLg0KKiBEci4gQW5pbCBEb2xndW4gKDIwMjApLiBNb2R1bGUgMy4gW29ubGluZV0gQXBwc3BvdC5jb20uIEF2YWlsYWJsZSBhdDogaHR0cDovL3JhcmUtcGhvZW5peC0xNjE2MTAuYXBwc3BvdC5jb20vc2VjdXJlZC9Nb2R1bGVfMDMuaHRtbCBbQWNjZXNzZWQgNiBPY3QuIDIwMjBdLg0KKiBEci4gQW5pbCBEb2xndW4gKDIwMjApLiBNb2R1bGUgNC4gW29ubGluZV0gQXBwc3BvdC5jb20uIEF2YWlsYWJsZSBhdDogaHR0cDovL3JhcmUtcGhvZW5peC0xNjE2MTAuYXBwc3BvdC5jb20vc2VjdXJlZC9Nb2R1bGVfMDQuaHRtbCBbQWNjZXNzZWQgNiBPY3QuIDIwMjBdLg0KKiBEci4gQW5pbCBEb2xndW4gKDIwMjApLiBNb2R1bGUgNS4gW29ubGluZV0gQXBwc3BvdC5jb20uIEF2YWlsYWJsZSBhdDogaHR0cDovL3JhcmUtcGhvZW5peC0xNjE2MTAuYXBwc3BvdC5jb20vc2VjdXJlZC9Nb2R1bGVfMDUuaHRtbCBbQWNjZXNzZWQgNiBPY3QuIDIwMjBdLg0KKiBEci4gQW5pbCBEb2xndW4gKDIwMjApLiBNb2R1bGUgNi4gW29ubGluZV0gQXBwc3BvdC5jb20uIEF2YWlsYWJsZSBhdDogaHR0cDovL3JhcmUtcGhvZW5peC0xNjE2MTAuYXBwc3BvdC5jb20vc2VjdXJlZC9Nb2R1bGVfMDYuaHRtbCBbQWNjZXNzZWQgNiBPY3QuIDIwMjBdLg0KKiBEci4gQW5pbCBEb2xndW4gKDIwMjApLiBNb2R1bGUgNy4gW29ubGluZV0gQXBwc3BvdC5jb20uIEF2YWlsYWJsZSBhdDogaHR0cDovL3JhcmUtcGhvZW5peC0xNjE2MTAuYXBwc3BvdC5jb20vc2VjdXJlZC9Nb2R1bGVfMDcuaHRtbCBbQWNjZXNzZWQgNiBPY3QuIDIwMjBdLg0KKiBEci4gQW5pbCBEb2xndW4gKDIwMjApLiBNb2R1bGUgOC4gW29ubGluZV0gQXBwc3BvdC5jb20uIEF2YWlsYWJsZSBhdDogaHR0cDovL3JhcmUtcGhvZW5peC0xNjE2MTAuYXBwc3BvdC5jb20vc2VjdXJlZC9Nb2R1bGVfMDguaHRtbCBbQWNjZXNzZWQgNiBPY3QuIDIwMjBdLg0KDQo8YnI+DQo8YnI+DQo=