Required packages
The following packages were used during the course of this assignment.
library(readr)
library(lubridate)
library(dplyr)
library(tidyr)
library(magrittr)
library(MVN)
Executive Summary
Data preprocessing is a cruical step in data anaylsis. It prepares the data for further statistical analysis to obtain accurate results by ensuring that the data is clean and tidy. For this report, the following data preprocessing steps were taken:
- The datasets were imported into R studio using readr library function and were merged based on the common attribute.
- Once imported,a subset of the original dataset was created for further operations that includes the frist 5000 observations. The dimensionality and structure of the dataset was checked along with understanding the semantics of the variables. Necessary type conversions were made in order to preserve the meaning of the variables.
- The datasets were examined to see if they were untidy and one of the dataset was messy as one of its columns comprised of multiple variables. The respective dataset was then tidied using separate() function.
- Once tidy, a new variable was added to the dataset using mutate() function that was created from the already existing variables.
- The next step was to scan the dataset for missing values along with special and inconsistent values. No such values were found in the dataset.
- After looking for missing values, presence of outliers in numeric variables were detected using MVN package and were dealt by capping or winsorising.
- Finally, distributions of the numeric variables were checked using histogram plots and appropriate transformations were applied to reduce the skewness.
Data
The two datasets used in the report are the Brazilian E-Commerce Datasets by Olist taken from:
Olist order Items Dataset: https://www.kaggle.com/olistbr/brazilian-ecommerce?select=olist_order_items_dataset.csv
Olist Sellers Dataset: https://www.kaggle.com/olistbr/brazilian-ecommerce?select=olist_sellers_dataset.csv
The accompanying datasets have data of orders made at Olist, the biggest retail chain in Brazilian commercial centers. Olist interfaces little organizations from all over Brazil to channels without trouble and with a solitary agreement. When a client buys from Olist, the dealer gets educated to satisfy that order.It comprises of 100k requests from 2016 to 2018 made at various commercial centers in Brazil.
The first dataset used is “Olist Order Items Dataset”. It provides data about items purchased within each order. This dataset consist of 112650 observations and the following 7 variables:
Order_id : ID of order which is unique for each order
Order_item_id : The number of items in a particular order, represented sequentially
Product_id : ID of product which is unique for each product
Seller_id : ID of seller which is unique for each seller
Shipping_limit_date : Represents the shipping date and time by the seller to handle over the products to the logistic partners
Price : Price of the item
Freight_value : Freight value of item
The second dataset used is “Olist Sellers Dataset”. This dataset incorporates data about the sellers that finished requests made at Olist. There are 3095 observations and 4 variables:
Seller_id : Unique seller identifier
Seller_zip_code_prefix : The first five digits of seller’s zip code
Seller_city : Seller’s city name
Seller_state : State of the Seller
The two datasets were merged into one, by the common attribute ‘seller_id’ to show information about items in each order and its seller. The resultant dataset had 2 numeric variables, namely, ‘price’ and ‘freight_value’.
- The datasets were read in R studio using read_csv() function from ‘readr’ package.
- head() function was used to display initial observations of the datasets along with the header.
- left_join() function was used to join the two datasets by the variable ‘seller_id’ to display all the order items information along with their respective seller’s details. It was then displayed using head() function.
#Reading the Order Items Dataset
order_items<- read_csv("olist_order_items_dataset.csv")
Parsed with column specification:
cols(
order_id = [31mcol_character()[39m,
order_item_id = [32mcol_double()[39m,
product_id = [31mcol_character()[39m,
seller_id = [31mcol_character()[39m,
shipping_limit_date = [31mcol_character()[39m,
price = [32mcol_double()[39m,
freight_value = [32mcol_double()[39m
)
head(order_items)
#Reading the Sellers Dataset
seller<-read_csv("olist_sellers_dataset.csv")
Parsed with column specification:
cols(
seller_id = [31mcol_character()[39m,
seller_zip_code_prefix = [32mcol_double()[39m,
seller_city = [31mcol_character()[39m,
seller_state = [31mcol_character()[39m
)
head(seller)
#Merging the datasets Olist Order Items and Olist Sellers
join<- order_items %>% left_join(seller, by= "seller_id")
head(join)
NA
Understand
- Once the datasets were merged, the dimensions was checked using dim() functions.
- The new dataset ‘join’ had 112650 observations and total of 10 variables. Since the number of observations were large and some packages like MVN, work with 5000 observations or less, a subset of the data was generated as ‘seller_order_list’ which consists of the first 5000 entries from the original merged dataset.
- Structure of the subset was checked using str() function.
- Data types of all the 10 variables were checked with the help of class() function. The dataset consists of several character, numeric variables.
- The ‘seller_state’ variable was converted to a factor using factor() function and its levels were set.
- The ‘seller_zip_code_prefix’ variable was converted from numeric to character because ZIP code is not treated as a number that requires any computations.
#Checking dimension of 'join'
dim(join)
[1] 112650 10
#Subsetting with first 5000 observations
seller_order_list<- join[1:5000,]
dim(seller_order_list)
[1] 5000 10
str(seller_order_list)
tibble [5,000 x 10] (S3: tbl_df/tbl/data.frame)
$ order_id : chr [1:5000] "00010242fe8c5a6d1ba2dd792cb16214" "00018f77f2f0320c557190d7a144bdd3" "000229ec398224ef6ca0657da4fc703e" "00024acbcdf0a6daa1e931b038114c75" ...
$ order_item_id : num [1:5000] 1 1 1 1 1 1 1 1 1 1 ...
$ product_id : chr [1:5000] "4244733e06e7ecb4970a6e2683c13e61" "e5f2d52b802189ee658865ca93d83a8f" "c777355d18b72b67abbeef9df44fd0fd" "7634da152a4610f1595efa32f14722fc" ...
$ seller_id : chr [1:5000] "48436dade18ac8b2bce089ec2a041202" "dd7ddc04e1b6c2c614352b383efe2d36" "5b51032eddd242adc84c38acab88f23d" "9d7a1d34a5052409006425275ba1c2b4" ...
$ shipping_limit_date : chr [1:5000] "19-09-2017 09:45" "03-05-2017 11:05" "18-01-2018 14:48" "15-08-2018 10:10" ...
$ price : num [1:5000] 58.9 239.9 199 13 199.9 ...
$ freight_value : num [1:5000] 13.3 19.9 17.9 12.8 18.1 ...
$ seller_zip_code_prefix: num [1:5000] 27277 3471 37564 14403 87900 ...
$ seller_city : chr [1:5000] "volta redonda" "sao paulo" "borda da mata" "franca" ...
$ seller_state : chr [1:5000] "SP" "SP" "MG" "SP" ...
#Checking the data types of all 10 variables
class(seller_order_list$order_id)
[1] "character"
class(seller_order_list$order_item_id)
[1] "numeric"
class(seller_order_list$product_id)
[1] "character"
class(seller_order_list$seller_id)
[1] "character"
class(seller_order_list$shipping_limit_date)
[1] "character"
class(seller_order_list$price)
[1] "numeric"
class(seller_order_list$freight_value)
[1] "numeric"
class(seller_order_list$seller_zip_code_prefix)
[1] "numeric"
class(seller_order_list$seller_city)
[1] "character"
class(seller_order_list$seller_state)
[1] "character"
#Type conversions
#Converting 'seller_state' to a fctor variable
seller_order_list$seller_state<- factor(seller_order_list$seller_state,
levels = c("AC","AM","BA","CE","DF","ES","GO","MA","MG","MS","MT","PA","PB","PE","PI","PR","RJ","RN","RO","RS","SC","SE","SP"))
levels(seller_order_list$seller_state)
[1] "AC" "AM" "BA" "CE" "DF" "ES" "GO" "MA" "MG" "MS" "MT" "PA" "PB" "PE" "PI"
[16] "PR" "RJ" "RN" "RO" "RS" "SC" "SE" "SP"
class(seller_order_list$seller_state)
[1] "factor"
#Converting 'seller_zip_code_prefix' to a character variable
seller_order_list$seller_zip_code_prefix<- as.character(seller_order_list$seller_zip_code_prefix)
class(seller_order_list$seller_zip_code_prefix)
[1] "character"
Tidy & Manipulate Data I
- The olist_order_items_dataset was messy because the variable ‘shipping_limit_date’ stores the date and time of shipping. In this case, mutliple variables are stored in one column and makes the dataset untidy.
- The ‘shipping_limit_date’ variable is then split into two columns, ‘Shipping Date’ and ‘Shipping Time’ using separate() function from tidyr package, by space as a separator.
- Once separate, the ‘Shipping Date’ variable was then correctly converted as a date using Base R as.Date() function and was formatted as the original DD-MM-YYY format using format() function.
#Separating the 'shipping_limit_date' into two columns
seller_order_list<-separate(seller_order_list,shipping_limit_date, into = c("Shipping Date", "Shipping Time"), sep = " ")
seller_order_list %>% head(10)
#Converting 'Shipping Date' to date format
seller_order_list$`Shipping Date`<- format(as.Date(seller_order_list$`Shipping Date`, format = "%d-%m-%Y"), "%d-%m-%Y")
seller_order_list$`Shipping Date` %>% head(10)
[1] "19-09-2017" "03-05-2017" "18-01-2018" "15-08-2018" "13-02-2017"
[6] "23-05-2017" "14-12-2017" "10-07-2018" "26-03-2018" "06-07-2018"
Tidy & Manipulate Data II
- The olist_order_items_dataset consists details about each order with an order ID. It was observed that some orders had multiple items, where the price and freight value was split between the items.
- ‘order_item_id’ is a sequential number identifying number of items included in the same order.
- A new variable ‘total_order_value’ was created that computes the total price of the order, i.e. Total order value = price + freight value.
- mutate() function was used to add the ‘total_order_value’ column to the data after grouping by the order_id and adding the price and freight value.
- The price and freight value for each order was calculated by multiplying the price and freight value with the number of items in each order using length(order_id).
- For example: The order with order_id = 00143d0f86d6fbd9f9b38ab440ac16f5 has 3 items. To get the total order value for each order just sum needs to be calculated as:
- The total order price value is: 21.33 * 3 = 63.99
- The total freight value is: 15.10 * 3 = 45.30
- Therefore, the total order value (price + freight) is: 45.30 + 63.99 = 109.29
#Mutating total price column
seller_order_list<- seller_order_list %>% group_by(order_id) %>%
mutate(total_order_value= (price * length(order_id)+(freight_value * length(order_id))))
seller_order_list$total_order_value %>% head(10)
[1] 72.19 259.83 216.87 25.78 218.04 34.59 31.75 880.75 157.60 65.39
Scan I
- The data was scanned for missing values using is.na() function and the total number of missing values in each column was checked using colSums() function. It was observed that there are no missing values in the used dataset since the total missing values for each column comes out to be 0.
- Every numerical column in the dataset, was then scanned for special values such as -Inf, Inf and NaN.
- Firstly, a subset of the numeric columns i.e. price, freight_value and total_order_value was created using select() function and then selecting just the numeric columns. This variable was named ‘seller_order_sub’.
- To check for special values, a function called special_values() was created.
- Using sapply(), the special_values() function was applied to ‘seller_order_sub’. The result was quite non-informative since it was long, therefore sum() was added together with special_values function to calculate the total special values for each column.
- There were no special values observed in the numerical columns.
- The dataset represents price of various orders along with the freight value. In real world situation, these values cannot be negative. Therefore, each numeric price variables were checked for obvious inconsistency or error.
- A new function called nonnegative() was created that checks if the value are less than zero. It was then applied to the numeric values subset using sapply().
- To check the total number of inconsistent values in each column, colSums() was used and it resulted in 0 inconsistent values for each column.
#Scanning for missing values
colSums(is.na(seller_order_list))
order_id order_item_id product_id
0 0 0
seller_id Shipping Date Shipping Time
0 0 0
price freight_value seller_zip_code_prefix
0 0 0
seller_city seller_state total_order_value
0 0 0
#Scanning for special values
sub<- seller_order_list %>% select(price, freight_value, total_order_value)
Adding missing grouping variables: `order_id`
seller_order_sub<- sub[,2:4]
special_values <- function(x){
if (is.numeric(x)) (is.infinite(x) | is.nan(x))
}
sp_values<-sapply(seller_order_sub, FUN = special_values)
sapply(seller_order_sub, function(x){if (is.numeric(x)) sum(special_values(x))})
price freight_value total_order_value
0 0 0
##Scanning for inconsistencies or obvious errors
nonnegative<- function(x){x<0}
nonnegative_price<-sapply(seller_order_sub, FUN = nonnegative)
colSums(nonnegative_price)
price freight_value total_order_value
0 0 0
Scan II
- The dataset was now scanned for possible outliers. The data contained three numeric variables and with the help of the Mahalanobis distance method the outliers were detected for multivariate setting.
- Using the mvn() function on ‘seller_order_sub’ from the MVN package, multivariate outliers were detected with QQ plot. The showOutliers = TRUE argument depicts the multivariate outliers and show them on QQ plot.
- The QQ plot suggests existence of 68 outliers for this subset.
- The location of outliers in the dataset were detected using seller_order_outliers$multivariateOutliers.
- Once the presence of outliers in the subset of dataset was detected, the next step was to handle them.
- Capping or winsorising was performed in order to deal with the outliers, instead of removing them. This is because the outliers in this case may not necessarily mean that the values reported are due to some errors. Sometimes outliers can be legitimate observations. A high total order value can either mean the number of items in the order are large or the item price along with freight value are high.
- cap() function was created to replace those data points lying outside the lower limit and above the upper limit with the value of 5th percentile and 95th percentile.
- Before capping, descriptive statistics of the subset was produced using summary().
- Each numeric variable was then capped using cap() function and the results were stored in the variables ‘price_capped’, ‘freight_value_capped’ and ‘total_price_capped’.
- Summary statistics of the capped subset was computed using summary().
# Mahanabolis Outlier detection method
seller_order_outliers <- mvn(data = seller_order_sub, multivariateOutlierMethod = "quan", showOutliers = TRUE)
The covariance matrix has become singular during
the iterations of the MCD algorithm.
There are 3893 observations (in the entire dataset of 5000 obs.) lying
on the plane with equation 0.57735 (x_i1-m_1) + 0.57735 (x_i2-m_2) +
-0.57735 (x_i3-m_3) = 0 with (m_1,m_2) the mean of these observations.

#Checking locations of the outliers
seller_order_outliers$multivariateOutliers
#Capping
cap <- function(x){
quantiles <- quantile( x, c(.05, 0.25, 0.75, .95 ) )
x[ x < quantiles[2] - 1.5*IQR(x) ] <- quantiles[1]
x[ x > quantiles[3] + 1.5*IQR(x) ] <- quantiles[4]
x
}
#Descriptive statistics of the subset
summary(seller_order_sub)
price freight_value total_order_value
Min. : 3.49 Min. : 0.00 Min. : 13.38
1st Qu.: 39.99 1st Qu.: 13.11 1st Qu.: 66.25
Median : 77.90 Median : 16.41 Median : 114.92
Mean : 124.36 Mean : 20.28 Mean : 198.56
3rd Qu.: 138.00 3rd Qu.: 21.64 3rd Qu.: 191.67
Max. :6735.00 Max. :375.28 Max. :13664.08
#Applying cap() to the numeric variables
price_capped<- seller_order_list$price %>% cap()
freight_value_capped<- seller_order_list$freight_value %>% cap()
total_price_capped<- seller_order_list$total_order_value %>% cap()
#Checking the summary statistics again
seller_order_capped<- sapply(seller_order_sub, FUN = cap)
summary(seller_order_capped)
price freight_value total_order_value
Min. : 3.49 Min. : 1.09 Min. : 13.38
1st Qu.: 39.99 1st Qu.:13.11 1st Qu.: 66.25
Median : 77.90 Median :16.41 Median :114.92
Mean :104.66 Mean :19.29 Mean :157.57
3rd Qu.:138.00 3rd Qu.:21.64 3rd Qu.:191.67
Max. :350.44 Max. :46.41 Max. :514.40
LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDEsIDIwMjAiDQphdXRob3I6ICJBbnVzaGthIFNoYXJtYSAzODE3MDA0Ig0Kc3VidGl0bGU6IEFzc2lnbm1lbnQgMg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgZGZfcHJpbnQ6IHBhZ2VkDQotLS0NCg0KIyMgUmVxdWlyZWQgcGFja2FnZXMgDQoNCg0KVGhlIGZvbGxvd2luZyBwYWNrYWdlcyB3ZXJlIHVzZWQgZHVyaW5nIHRoZSBjb3Vyc2Ugb2YgdGhpcyBhc3NpZ25tZW50Lg0KDQpgYGB7cn0NCg0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeShNVk4pDQoNCmBgYA0KDQoNCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5IA0KDQpEYXRhIHByZXByb2Nlc3NpbmcgaXMgYSBjcnVpY2FsIHN0ZXAgaW4gZGF0YSBhbmF5bHNpcy4gSXQgcHJlcGFyZXMgdGhlIGRhdGEgZm9yIGZ1cnRoZXIgc3RhdGlzdGljYWwgYW5hbHlzaXMgdG8gb2J0YWluIGFjY3VyYXRlIHJlc3VsdHMgYnkgZW5zdXJpbmcgdGhhdCB0aGUgZGF0YSBpcyBjbGVhbiBhbmQgdGlkeS4gRm9yIHRoaXMgcmVwb3J0LCB0aGUgZm9sbG93aW5nIGRhdGEgcHJlcHJvY2Vzc2luZyBzdGVwcyB3ZXJlIHRha2VuOg0KDQoqIFRoZSBkYXRhc2V0cyB3ZXJlIGltcG9ydGVkIGludG8gUiBzdHVkaW8gdXNpbmcgcmVhZHIgbGlicmFyeSBmdW5jdGlvbiBhbmQgd2VyZSBtZXJnZWQgYmFzZWQgb24gdGhlIGNvbW1vbiBhdHRyaWJ1dGUuDQoqIE9uY2UgaW1wb3J0ZWQsYSBzdWJzZXQgb2YgdGhlIG9yaWdpbmFsIGRhdGFzZXQgd2FzIGNyZWF0ZWQgZm9yIGZ1cnRoZXIgb3BlcmF0aW9ucyB0aGF0IGluY2x1ZGVzIHRoZSBmcmlzdCA1MDAwIG9ic2VydmF0aW9ucy4gVGhlIGRpbWVuc2lvbmFsaXR5IGFuZCBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGFzZXQgd2FzIGNoZWNrZWQgYWxvbmcgd2l0aCB1bmRlcnN0YW5kaW5nIHRoZSBzZW1hbnRpY3Mgb2YgdGhlIHZhcmlhYmxlcy4gTmVjZXNzYXJ5IHR5cGUgY29udmVyc2lvbnMgd2VyZSBtYWRlIGluIG9yZGVyIHRvIHByZXNlcnZlIHRoZSBtZWFuaW5nIG9mIHRoZSB2YXJpYWJsZXMuDQoqIFRoZSBkYXRhc2V0cyB3ZXJlIGV4YW1pbmVkIHRvIHNlZSBpZiB0aGV5IHdlcmUgdW50aWR5IGFuZCBvbmUgb2YgdGhlIGRhdGFzZXQgd2FzIG1lc3N5IGFzIG9uZSBvZiBpdHMgY29sdW1ucyBjb21wcmlzZWQgb2YgbXVsdGlwbGUgdmFyaWFibGVzLiBUaGUgcmVzcGVjdGl2ZSBkYXRhc2V0IHdhcyB0aGVuIHRpZGllZCB1c2luZyBzZXBhcmF0ZSgpIGZ1bmN0aW9uLg0KKiBPbmNlIHRpZHksIGEgbmV3IHZhcmlhYmxlIHdhcyBhZGRlZCB0byB0aGUgZGF0YXNldCB1c2luZyBtdXRhdGUoKSBmdW5jdGlvbiB0aGF0IHdhcyBjcmVhdGVkIGZyb20gdGhlIGFscmVhZHkgZXhpc3RpbmcgdmFyaWFibGVzLg0KKiBUaGUgbmV4dCBzdGVwIHdhcyB0byBzY2FuIHRoZSBkYXRhc2V0IGZvciBtaXNzaW5nIHZhbHVlcyBhbG9uZyB3aXRoIHNwZWNpYWwgYW5kIGluY29uc2lzdGVudCB2YWx1ZXMuIE5vIHN1Y2ggdmFsdWVzIHdlcmUgZm91bmQgaW4gdGhlIGRhdGFzZXQuDQoqIEFmdGVyIGxvb2tpbmcgZm9yIG1pc3NpbmcgdmFsdWVzLCBwcmVzZW5jZSBvZiBvdXRsaWVycyBpbiBudW1lcmljIHZhcmlhYmxlcyB3ZXJlIGRldGVjdGVkIHVzaW5nIE1WTiBwYWNrYWdlIGFuZCB3ZXJlIGRlYWx0IGJ5IGNhcHBpbmcgb3Igd2luc29yaXNpbmcuDQoqIEZpbmFsbHksIGRpc3RyaWJ1dGlvbnMgb2YgdGhlIG51bWVyaWMgdmFyaWFibGVzIHdlcmUgY2hlY2tlZCB1c2luZyBoaXN0b2dyYW0gcGxvdHMgYW5kIGFwcHJvcHJpYXRlIHRyYW5zZm9ybWF0aW9ucyB3ZXJlIGFwcGxpZWQgdG8gcmVkdWNlIHRoZSBza2V3bmVzcy4NCg0KIyMgRGF0YSANCg0KVGhlIHR3byBkYXRhc2V0cyB1c2VkIGluIHRoZSByZXBvcnQgYXJlIHRoZSBCcmF6aWxpYW4gRS1Db21tZXJjZSBEYXRhc2V0cyBieSBPbGlzdCB0YWtlbiBmcm9tOg0KPGJyPg0KT2xpc3Qgb3JkZXIgSXRlbXMgRGF0YXNldDogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9vbGlzdGJyL2JyYXppbGlhbi1lY29tbWVyY2U/c2VsZWN0PW9saXN0X29yZGVyX2l0ZW1zX2RhdGFzZXQuY3N2DQo8YnI+DQpPbGlzdCBTZWxsZXJzIERhdGFzZXQ6IA0KaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9vbGlzdGJyL2JyYXppbGlhbi1lY29tbWVyY2U/c2VsZWN0PW9saXN0X3NlbGxlcnNfZGF0YXNldC5jc3YNCjxicj4NCg0KVGhlIGFjY29tcGFueWluZyBkYXRhc2V0cyBoYXZlIGRhdGEgb2Ygb3JkZXJzIG1hZGUgYXQgT2xpc3QsIHRoZSBiaWdnZXN0IHJldGFpbCBjaGFpbiBpbiBCcmF6aWxpYW4gY29tbWVyY2lhbCBjZW50ZXJzLiBPbGlzdCBpbnRlcmZhY2VzIGxpdHRsZSBvcmdhbml6YXRpb25zIGZyb20gYWxsIG92ZXIgQnJhemlsIHRvIGNoYW5uZWxzIHdpdGhvdXQgdHJvdWJsZSBhbmQgd2l0aCBhIHNvbGl0YXJ5IGFncmVlbWVudC4gV2hlbiBhIGNsaWVudCBidXlzIGZyb20gT2xpc3QsIHRoZSBkZWFsZXIgZ2V0cyBlZHVjYXRlZCB0byBzYXRpc2Z5IHRoYXQgb3JkZXIuSXQgY29tcHJpc2VzIG9mIDEwMGsgcmVxdWVzdHMgZnJvbSAyMDE2IHRvIDIwMTggbWFkZSBhdCB2YXJpb3VzIGNvbW1lcmNpYWwgY2VudGVycyBpbiBCcmF6aWwuIA0KPGJyPg0KDQpUaGUgZmlyc3QgZGF0YXNldCB1c2VkIGlzICJPbGlzdCBPcmRlciBJdGVtcyBEYXRhc2V0Ii4gSXQgcHJvdmlkZXMgZGF0YSBhYm91dCBpdGVtcyBwdXJjaGFzZWQgd2l0aGluIGVhY2ggb3JkZXIuIFRoaXMgZGF0YXNldCBjb25zaXN0IG9mIDExMjY1MCBvYnNlcnZhdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgNyB2YXJpYWJsZXM6DQoNCiogT3JkZXJfaWQgOiBJRCBvZiBvcmRlciB3aGljaCBpcyB1bmlxdWUgZm9yIGVhY2ggb3JkZXINCg0KKiBPcmRlcl9pdGVtX2lkIDogVGhlIG51bWJlciBvZiBpdGVtcyBpbiBhIHBhcnRpY3VsYXIgb3JkZXIsIHJlcHJlc2VudGVkIHNlcXVlbnRpYWxseQ0KDQoqIFByb2R1Y3RfaWQgOiBJRCBvZiBwcm9kdWN0IHdoaWNoIGlzIHVuaXF1ZSBmb3IgZWFjaCBwcm9kdWN0DQoNCiogU2VsbGVyX2lkIDogSUQgb2Ygc2VsbGVyIHdoaWNoIGlzIHVuaXF1ZSBmb3IgZWFjaCBzZWxsZXINCg0KKiBTaGlwcGluZ19saW1pdF9kYXRlIDogUmVwcmVzZW50cyB0aGUgc2hpcHBpbmcgZGF0ZSBhbmQgdGltZSBieSB0aGUgc2VsbGVyIHRvIGhhbmRsZSBvdmVyIHRoZSBwcm9kdWN0cyB0byB0aGUgbG9naXN0aWMgcGFydG5lcnMNCg0KKiBQcmljZSA6IFByaWNlIG9mIHRoZSBpdGVtDQoNCiogRnJlaWdodF92YWx1ZSA6IEZyZWlnaHQgdmFsdWUgb2YgaXRlbQ0KDQoNClRoZSBzZWNvbmQgZGF0YXNldCB1c2VkIGlzICJPbGlzdCBTZWxsZXJzIERhdGFzZXQiLiBUaGlzIGRhdGFzZXQgaW5jb3Jwb3JhdGVzIGRhdGEgYWJvdXQgdGhlIHNlbGxlcnMgdGhhdCBmaW5pc2hlZCByZXF1ZXN0cyBtYWRlIGF0IE9saXN0LiBUaGVyZSBhcmUgMzA5NSBvYnNlcnZhdGlvbnMgYW5kIDQgdmFyaWFibGVzOg0KDQoqIFNlbGxlcl9pZCA6IFVuaXF1ZSBzZWxsZXIgaWRlbnRpZmllcg0KDQoqIFNlbGxlcl96aXBfY29kZV9wcmVmaXggOiBUaGUgZmlyc3QgZml2ZSBkaWdpdHMgb2Ygc2VsbGVyJ3MgemlwIGNvZGUNCg0KKiBTZWxsZXJfY2l0eSA6IFNlbGxlcidzIGNpdHkgbmFtZQ0KDQoqIFNlbGxlcl9zdGF0ZSA6IFN0YXRlIG9mIHRoZSBTZWxsZXINCg0KVGhlIHR3byBkYXRhc2V0cyB3ZXJlIG1lcmdlZCBpbnRvIG9uZSwgYnkgdGhlIGNvbW1vbiBhdHRyaWJ1dGUgJ3NlbGxlcl9pZCcgdG8gc2hvdyBpbmZvcm1hdGlvbiBhYm91dCBpdGVtcyBpbiBlYWNoIG9yZGVyIGFuZCBpdHMgc2VsbGVyLiBUaGUgcmVzdWx0YW50IGRhdGFzZXQgaGFkIDIgbnVtZXJpYyB2YXJpYWJsZXMsIG5hbWVseSwgJ3ByaWNlJyBhbmQgJ2ZyZWlnaHRfdmFsdWUnLg0KDQoqIFRoZSBkYXRhc2V0cyB3ZXJlIHJlYWQgaW4gUiBzdHVkaW8gdXNpbmcgcmVhZF9jc3YoKSBmdW5jdGlvbiBmcm9tICdyZWFkcicgcGFja2FnZS4NCiogaGVhZCgpIGZ1bmN0aW9uIHdhcyB1c2VkIHRvIGRpc3BsYXkgaW5pdGlhbCBvYnNlcnZhdGlvbnMgb2YgdGhlIGRhdGFzZXRzIGFsb25nIHdpdGggdGhlIGhlYWRlci4NCiogbGVmdF9qb2luKCkgZnVuY3Rpb24gd2FzIHVzZWQgdG8gam9pbiB0aGUgdHdvIGRhdGFzZXRzIGJ5IHRoZSB2YXJpYWJsZSAnc2VsbGVyX2lkJyB0byBkaXNwbGF5IGFsbCB0aGUgb3JkZXIgaXRlbXMgaW5mb3JtYXRpb24gYWxvbmcgd2l0aCB0aGVpciByZXNwZWN0aXZlIHNlbGxlcidzIGRldGFpbHMuIEl0IHdhcyB0aGVuIGRpc3BsYXllZCB1c2luZyBoZWFkKCkgZnVuY3Rpb24uDQoNCmBgYHtyfQ0KDQojUmVhZGluZyB0aGUgT3JkZXIgSXRlbXMgRGF0YXNldA0Kb3JkZXJfaXRlbXM8LSByZWFkX2Nzdigib2xpc3Rfb3JkZXJfaXRlbXNfZGF0YXNldC5jc3YiKQ0KaGVhZChvcmRlcl9pdGVtcykNCg0KI1JlYWRpbmcgdGhlIFNlbGxlcnMgRGF0YXNldA0Kc2VsbGVyPC1yZWFkX2Nzdigib2xpc3Rfc2VsbGVyc19kYXRhc2V0LmNzdiIpDQpoZWFkKHNlbGxlcikNCg0KI01lcmdpbmcgdGhlIGRhdGFzZXRzIE9saXN0IE9yZGVyIEl0ZW1zIGFuZCBPbGlzdCBTZWxsZXJzIA0Kam9pbjwtIG9yZGVyX2l0ZW1zICU+JSBsZWZ0X2pvaW4oc2VsbGVyLCBieT0gInNlbGxlcl9pZCIpDQpoZWFkKGpvaW4pDQoNCmBgYA0KDQojIyBVbmRlcnN0YW5kIA0KDQoqIE9uY2UgdGhlIGRhdGFzZXRzIHdlcmUgbWVyZ2VkLCB0aGUgZGltZW5zaW9ucyB3YXMgY2hlY2tlZCB1c2luZyBkaW0oKSBmdW5jdGlvbnMuDQoqIFRoZSBuZXcgZGF0YXNldCAnam9pbicgaGFkIDExMjY1MCBvYnNlcnZhdGlvbnMgYW5kIHRvdGFsIG9mIDEwIHZhcmlhYmxlcy4gU2luY2UgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgd2VyZSBsYXJnZSBhbmQgc29tZSBwYWNrYWdlcyBsaWtlIE1WTiwgd29yayB3aXRoIDUwMDAgb2JzZXJ2YXRpb25zIG9yIGxlc3MsIGEgc3Vic2V0IG9mIHRoZSBkYXRhIHdhcyBnZW5lcmF0ZWQgYXMgJ3NlbGxlcl9vcmRlcl9saXN0JyB3aGljaCBjb25zaXN0cyBvZiB0aGUgZmlyc3QgNTAwMCBlbnRyaWVzIGZyb20gdGhlIG9yaWdpbmFsIG1lcmdlZCBkYXRhc2V0LiANCiogU3RydWN0dXJlIG9mIHRoZSBzdWJzZXQgd2FzIGNoZWNrZWQgdXNpbmcgc3RyKCkgZnVuY3Rpb24uDQoqIERhdGEgdHlwZXMgb2YgYWxsIHRoZSAxMCB2YXJpYWJsZXMgd2VyZSBjaGVja2VkIHdpdGggdGhlIGhlbHAgb2YgY2xhc3MoKSBmdW5jdGlvbi4gVGhlIGRhdGFzZXQgY29uc2lzdHMgb2Ygc2V2ZXJhbCBjaGFyYWN0ZXIsIG51bWVyaWMgdmFyaWFibGVzLg0KKiBUaGUgJ3NlbGxlcl9zdGF0ZScgdmFyaWFibGUgd2FzIGNvbnZlcnRlZCB0byBhIGZhY3RvciB1c2luZyBmYWN0b3IoKSBmdW5jdGlvbiBhbmQgaXRzIGxldmVscyB3ZXJlIHNldC4NCiogVGhlICdzZWxsZXJfemlwX2NvZGVfcHJlZml4JyB2YXJpYWJsZSB3YXMgY29udmVydGVkIGZyb20gbnVtZXJpYyB0byBjaGFyYWN0ZXIgYmVjYXVzZSBaSVAgY29kZSBpcyBub3QgdHJlYXRlZCBhcyBhIG51bWJlciB0aGF0IHJlcXVpcmVzIGFueSBjb21wdXRhdGlvbnMuDQoNCg0KYGBge3J9DQoNCiNDaGVja2luZyBkaW1lbnNpb24gb2YgJ2pvaW4nDQpkaW0oam9pbikNCg0KI1N1YnNldHRpbmcgd2l0aCBmaXJzdCA1MDAwIG9ic2VydmF0aW9ucw0Kc2VsbGVyX29yZGVyX2xpc3Q8LSBqb2luWzE6NTAwMCxdDQpkaW0oc2VsbGVyX29yZGVyX2xpc3QpDQpzdHIoc2VsbGVyX29yZGVyX2xpc3QpDQoNCiNDaGVja2luZyB0aGUgZGF0YSB0eXBlcyBvZiBhbGwgMTAgdmFyaWFibGVzDQpjbGFzcyhzZWxsZXJfb3JkZXJfbGlzdCRvcmRlcl9pZCkNCmNsYXNzKHNlbGxlcl9vcmRlcl9saXN0JG9yZGVyX2l0ZW1faWQpDQpjbGFzcyhzZWxsZXJfb3JkZXJfbGlzdCRwcm9kdWN0X2lkKQ0KY2xhc3Moc2VsbGVyX29yZGVyX2xpc3Qkc2VsbGVyX2lkKQ0KY2xhc3Moc2VsbGVyX29yZGVyX2xpc3Qkc2hpcHBpbmdfbGltaXRfZGF0ZSkNCmNsYXNzKHNlbGxlcl9vcmRlcl9saXN0JHByaWNlKQ0KY2xhc3Moc2VsbGVyX29yZGVyX2xpc3QkZnJlaWdodF92YWx1ZSkNCmNsYXNzKHNlbGxlcl9vcmRlcl9saXN0JHNlbGxlcl96aXBfY29kZV9wcmVmaXgpDQpjbGFzcyhzZWxsZXJfb3JkZXJfbGlzdCRzZWxsZXJfY2l0eSkNCmNsYXNzKHNlbGxlcl9vcmRlcl9saXN0JHNlbGxlcl9zdGF0ZSkNCg0KI1R5cGUgY29udmVyc2lvbnMNCiNDb252ZXJ0aW5nICdzZWxsZXJfc3RhdGUnIHRvIGEgZmN0b3IgdmFyaWFibGUNCnNlbGxlcl9vcmRlcl9saXN0JHNlbGxlcl9zdGF0ZTwtIGZhY3RvcihzZWxsZXJfb3JkZXJfbGlzdCRzZWxsZXJfc3RhdGUsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkFDIiwiQU0iLCJCQSIsIkNFIiwiREYiLCJFUyIsIkdPIiwiTUEiLCJNRyIsIk1TIiwiTVQiLCJQQSIsIlBCIiwiUEUiLCJQSSIsIlBSIiwiUkoiLCJSTiIsIlJPIiwiUlMiLCJTQyIsIlNFIiwiU1AiKSkNCmxldmVscyhzZWxsZXJfb3JkZXJfbGlzdCRzZWxsZXJfc3RhdGUpDQpjbGFzcyhzZWxsZXJfb3JkZXJfbGlzdCRzZWxsZXJfc3RhdGUpDQoNCiNDb252ZXJ0aW5nICdzZWxsZXJfemlwX2NvZGVfcHJlZml4JyB0byBhIGNoYXJhY3RlciB2YXJpYWJsZQ0Kc2VsbGVyX29yZGVyX2xpc3Qkc2VsbGVyX3ppcF9jb2RlX3ByZWZpeDwtIGFzLmNoYXJhY3RlcihzZWxsZXJfb3JkZXJfbGlzdCRzZWxsZXJfemlwX2NvZGVfcHJlZml4KQ0KY2xhc3Moc2VsbGVyX29yZGVyX2xpc3Qkc2VsbGVyX3ppcF9jb2RlX3ByZWZpeCkNCg0KYGBgDQoNCg0KIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIA0KDQoqIFRoZSBvbGlzdF9vcmRlcl9pdGVtc19kYXRhc2V0IHdhcyBtZXNzeSBiZWNhdXNlIHRoZSB2YXJpYWJsZSAnc2hpcHBpbmdfbGltaXRfZGF0ZScgc3RvcmVzIHRoZSBkYXRlIGFuZCB0aW1lIG9mIHNoaXBwaW5nLiBJbiB0aGlzIGNhc2UsIG11dGxpcGxlIHZhcmlhYmxlcyBhcmUgc3RvcmVkIGluIG9uZSBjb2x1bW4gYW5kIG1ha2VzIHRoZSBkYXRhc2V0IHVudGlkeS4NCiogVGhlICdzaGlwcGluZ19saW1pdF9kYXRlJyB2YXJpYWJsZSBpcyB0aGVuIHNwbGl0IGludG8gdHdvIGNvbHVtbnMsICdTaGlwcGluZyBEYXRlJyBhbmQgJ1NoaXBwaW5nIFRpbWUnIHVzaW5nIHNlcGFyYXRlKCkgZnVuY3Rpb24gZnJvbSB0aWR5ciBwYWNrYWdlLCBieSBzcGFjZSBhcyBhIHNlcGFyYXRvci4gDQoqIE9uY2Ugc2VwYXJhdGUsIHRoZSAnU2hpcHBpbmcgRGF0ZScgdmFyaWFibGUgd2FzIHRoZW4gY29ycmVjdGx5IGNvbnZlcnRlZCBhcyBhIGRhdGUgdXNpbmcgQmFzZSBSIGFzLkRhdGUoKSBmdW5jdGlvbiBhbmQgd2FzIGZvcm1hdHRlZCBhcyB0aGUgb3JpZ2luYWwgREQtTU0tWVlZIGZvcm1hdCB1c2luZyBmb3JtYXQoKSBmdW5jdGlvbi4NCg0KDQpgYGB7cn0NCg0KI1NlcGFyYXRpbmcgdGhlICdzaGlwcGluZ19saW1pdF9kYXRlJyBpbnRvIHR3byBjb2x1bW5zDQpzZWxsZXJfb3JkZXJfbGlzdDwtc2VwYXJhdGUoc2VsbGVyX29yZGVyX2xpc3Qsc2hpcHBpbmdfbGltaXRfZGF0ZSwgaW50byA9IGMoIlNoaXBwaW5nIERhdGUiLCAiU2hpcHBpbmcgVGltZSIpLCBzZXAgPSAiICIpDQpzZWxsZXJfb3JkZXJfbGlzdCAlPiUgaGVhZCgxMCkNCg0KI0NvbnZlcnRpbmcgJ1NoaXBwaW5nIERhdGUnIHRvIGRhdGUgZm9ybWF0DQpzZWxsZXJfb3JkZXJfbGlzdCRgU2hpcHBpbmcgRGF0ZWA8LSBmb3JtYXQoYXMuRGF0ZShzZWxsZXJfb3JkZXJfbGlzdCRgU2hpcHBpbmcgRGF0ZWAsIGZvcm1hdCA9ICIlZC0lbS0lWSIpLCAiJWQtJW0tJVkiKQ0Kc2VsbGVyX29yZGVyX2xpc3QkYFNoaXBwaW5nIERhdGVgICU+JSBoZWFkKDEwKQ0KDQpgYGANCg0KIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJSSANCg0KKiBUaGUgb2xpc3Rfb3JkZXJfaXRlbXNfZGF0YXNldCBjb25zaXN0cyBkZXRhaWxzIGFib3V0IGVhY2ggb3JkZXIgd2l0aCBhbiBvcmRlciBJRC4gSXQgd2FzIG9ic2VydmVkIHRoYXQgc29tZSBvcmRlcnMgaGFkIG11bHRpcGxlIGl0ZW1zLCB3aGVyZSB0aGUgcHJpY2UgYW5kIGZyZWlnaHQgdmFsdWUgd2FzIHNwbGl0IGJldHdlZW4gdGhlIGl0ZW1zLiANCiogJ29yZGVyX2l0ZW1faWQnIGlzIGEgc2VxdWVudGlhbCBudW1iZXIgaWRlbnRpZnlpbmcgbnVtYmVyIG9mIGl0ZW1zIGluY2x1ZGVkIGluIHRoZSBzYW1lIG9yZGVyLg0KKiBBIG5ldyB2YXJpYWJsZSAndG90YWxfb3JkZXJfdmFsdWUnIHdhcyBjcmVhdGVkIHRoYXQgY29tcHV0ZXMgdGhlIHRvdGFsIHByaWNlIG9mIHRoZSBvcmRlciwgaS5lLiBUb3RhbCBvcmRlciB2YWx1ZSA9IHByaWNlICsgZnJlaWdodCB2YWx1ZS4NCiogbXV0YXRlKCkgZnVuY3Rpb24gd2FzIHVzZWQgdG8gYWRkIHRoZSAndG90YWxfb3JkZXJfdmFsdWUnIGNvbHVtbiB0byB0aGUgZGF0YSBhZnRlciBncm91cGluZyBieSB0aGUgb3JkZXJfaWQgYW5kIGFkZGluZyB0aGUgcHJpY2UgYW5kIGZyZWlnaHQgdmFsdWUuDQoqIFRoZSBwcmljZSBhbmQgZnJlaWdodCB2YWx1ZSBmb3IgZWFjaCBvcmRlciB3YXMgY2FsY3VsYXRlZCBieSBtdWx0aXBseWluZyB0aGUgcHJpY2UgYW5kIGZyZWlnaHQgdmFsdWUgd2l0aCB0aGUgbnVtYmVyIG9mIGl0ZW1zIGluIGVhY2ggb3JkZXIgdXNpbmcgbGVuZ3RoKG9yZGVyX2lkKS4NCiogRm9yIGV4YW1wbGU6IFRoZSBvcmRlciB3aXRoIG9yZGVyX2lkID0gMDAxNDNkMGY4NmQ2ZmJkOWY5YjM4YWI0NDBhYzE2ZjUgaGFzIDMgaXRlbXMuIFRvIGdldCB0aGUgdG90YWwgb3JkZXIgdmFsdWUgZm9yIGVhY2ggb3JkZXIganVzdCBzdW0gbmVlZHMgdG8gYmUgY2FsY3VsYXRlZCBhczogDQogICAgLSBUaGUgdG90YWwgb3JkZXIgcHJpY2UgdmFsdWUgaXM6IDIxLjMzICogMyA9IDYzLjk5IA0KICAgIC0gVGhlIHRvdGFsIGZyZWlnaHQgdmFsdWUgaXM6IDE1LjEwICogMyA9IDQ1LjMwDQogICAgLSBUaGVyZWZvcmUsIHRoZSB0b3RhbCBvcmRlciB2YWx1ZSAocHJpY2UgKyBmcmVpZ2h0KSBpczogNDUuMzAgKyA2My45OSA9IDEwOS4yOQ0KDQoNCmBgYHtyfQ0KDQojTXV0YXRpbmcgdG90YWwgcHJpY2UgY29sdW1uDQpzZWxsZXJfb3JkZXJfbGlzdDwtIHNlbGxlcl9vcmRlcl9saXN0ICU+JSBncm91cF9ieShvcmRlcl9pZCkgJT4lDQogIG11dGF0ZSh0b3RhbF9vcmRlcl92YWx1ZT0gKHByaWNlICogbGVuZ3RoKG9yZGVyX2lkKSsoZnJlaWdodF92YWx1ZSAqIGxlbmd0aChvcmRlcl9pZCkpKSkNCg0Kc2VsbGVyX29yZGVyX2xpc3QkdG90YWxfb3JkZXJfdmFsdWUgJT4lIGhlYWQoMTApDQoNCmBgYA0KDQoNCiMjCVNjYW4gSSANCg0KKiBUaGUgZGF0YSB3YXMgc2Nhbm5lZCBmb3IgbWlzc2luZyB2YWx1ZXMgdXNpbmcgaXMubmEoKSBmdW5jdGlvbiBhbmQgdGhlIHRvdGFsIG51bWJlciBvZiBtaXNzaW5nIHZhbHVlcyBpbiBlYWNoIGNvbHVtbiB3YXMgY2hlY2tlZCB1c2luZyBjb2xTdW1zKCkgZnVuY3Rpb24uIEl0IHdhcyBvYnNlcnZlZCB0aGF0IHRoZXJlIGFyZSBubyBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgdXNlZCBkYXRhc2V0IHNpbmNlIHRoZSB0b3RhbCBtaXNzaW5nIHZhbHVlcyBmb3IgZWFjaCBjb2x1bW4gY29tZXMgb3V0IHRvIGJlIDAuDQoqIEV2ZXJ5IG51bWVyaWNhbCBjb2x1bW4gaW4gdGhlIGRhdGFzZXQsIHdhcyB0aGVuIHNjYW5uZWQgZm9yIHNwZWNpYWwgdmFsdWVzIHN1Y2ggYXMgLUluZiwgSW5mIGFuZCBOYU4uIA0KICAgIC0gRmlyc3RseSwgYSBzdWJzZXQgb2YgdGhlIG51bWVyaWMgY29sdW1ucyBpLmUuIHByaWNlLCBmcmVpZ2h0X3ZhbHVlIGFuZCB0b3RhbF9vcmRlcl92YWx1ZSB3YXMgY3JlYXRlZCB1c2luZyBzZWxlY3QoKSBmdW5jdGlvbiBhbmQgdGhlbiBzZWxlY3RpbmcganVzdCB0aGUgbnVtZXJpYyBjb2x1bW5zLiBUaGlzIHZhcmlhYmxlIHdhcyBuYW1lZCAnc2VsbGVyX29yZGVyX3N1YicuDQogICAgLSBUbyBjaGVjayBmb3Igc3BlY2lhbCB2YWx1ZXMsIGEgZnVuY3Rpb24gY2FsbGVkIHNwZWNpYWxfdmFsdWVzKCkgd2FzIGNyZWF0ZWQuDQogICAgLSBVc2luZyBzYXBwbHkoKSwgdGhlIHNwZWNpYWxfdmFsdWVzKCkgZnVuY3Rpb24gd2FzIGFwcGxpZWQgdG8gJ3NlbGxlcl9vcmRlcl9zdWInLiBUaGUgcmVzdWx0IHdhcyBxdWl0ZSBub24taW5mb3JtYXRpdmUgc2luY2UgaXQgd2FzIGxvbmcsIHRoZXJlZm9yZSBzdW0oKSB3YXMgYWRkZWQgdG9nZXRoZXIgd2l0aCBzcGVjaWFsX3ZhbHVlcyBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIHRvdGFsIHNwZWNpYWwgdmFsdWVzIGZvciBlYWNoIGNvbHVtbi4NCiAgICAtIFRoZXJlIHdlcmUgbm8gc3BlY2lhbCB2YWx1ZXMgb2JzZXJ2ZWQgaW4gdGhlIG51bWVyaWNhbCBjb2x1bW5zLg0KKiBUaGUgZGF0YXNldCByZXByZXNlbnRzIHByaWNlIG9mIHZhcmlvdXMgb3JkZXJzIGFsb25nIHdpdGggdGhlIGZyZWlnaHQgdmFsdWUuIEluIHJlYWwgd29ybGQgc2l0dWF0aW9uLCB0aGVzZSB2YWx1ZXMgY2Fubm90IGJlIG5lZ2F0aXZlLiBUaGVyZWZvcmUsIGVhY2ggbnVtZXJpYyBwcmljZSB2YXJpYWJsZXMgd2VyZSBjaGVja2VkIGZvciBvYnZpb3VzIGluY29uc2lzdGVuY3kgb3IgZXJyb3IuIA0KICAgIC0gQSBuZXcgZnVuY3Rpb24gY2FsbGVkIG5vbm5lZ2F0aXZlKCkgd2FzIGNyZWF0ZWQgdGhhdCBjaGVja3MgaWYgdGhlIHZhbHVlIGFyZSBsZXNzIHRoYW4gemVyby4gSXQgd2FzIHRoZW4gYXBwbGllZCB0byB0aGUgbnVtZXJpYyB2YWx1ZXMgc3Vic2V0IHVzaW5nIHNhcHBseSgpLiANCiAgICAtIFRvIGNoZWNrIHRoZSB0b3RhbCBudW1iZXIgb2YgaW5jb25zaXN0ZW50IHZhbHVlcyBpbiBlYWNoIGNvbHVtbiwgY29sU3VtcygpIHdhcyB1c2VkIGFuZCBpdCByZXN1bHRlZCBpbiAwIGluY29uc2lzdGVudCB2YWx1ZXMgZm9yIGVhY2ggY29sdW1uLg0KDQoNCmBgYHtyfQ0KDQojU2Nhbm5pbmcgZm9yIG1pc3NpbmcgdmFsdWVzDQpjb2xTdW1zKGlzLm5hKHNlbGxlcl9vcmRlcl9saXN0KSkNCg0KI1NjYW5uaW5nIGZvciBzcGVjaWFsIHZhbHVlcw0Kc3ViPC0gc2VsbGVyX29yZGVyX2xpc3QgJT4lIHNlbGVjdChwcmljZSwgZnJlaWdodF92YWx1ZSwgdG90YWxfb3JkZXJfdmFsdWUpDQpzZWxsZXJfb3JkZXJfc3ViPC0gc3ViWywyOjRdDQoNCnNwZWNpYWxfdmFsdWVzIDwtIGZ1bmN0aW9uKHgpew0KICBpZiAoaXMubnVtZXJpYyh4KSkgKGlzLmluZmluaXRlKHgpIHwgaXMubmFuKHgpKQ0KfQ0KDQpzcF92YWx1ZXM8LXNhcHBseShzZWxsZXJfb3JkZXJfc3ViLCBGVU4gPSBzcGVjaWFsX3ZhbHVlcykNCnNhcHBseShzZWxsZXJfb3JkZXJfc3ViLCBmdW5jdGlvbih4KXtpZiAoaXMubnVtZXJpYyh4KSkgc3VtKHNwZWNpYWxfdmFsdWVzKHgpKX0pDQoNCiMjU2Nhbm5pbmcgZm9yIGluY29uc2lzdGVuY2llcyBvciBvYnZpb3VzIGVycm9ycyANCm5vbm5lZ2F0aXZlPC0gZnVuY3Rpb24oeCl7eDwwfQ0Kbm9ubmVnYXRpdmVfcHJpY2U8LXNhcHBseShzZWxsZXJfb3JkZXJfc3ViLCBGVU4gPSBub25uZWdhdGl2ZSkNCmNvbFN1bXMobm9ubmVnYXRpdmVfcHJpY2UpDQoNCmBgYA0KDQoNCiMjCVNjYW4gSUkNCg0KKiBUaGUgZGF0YXNldCB3YXMgbm93IHNjYW5uZWQgZm9yIHBvc3NpYmxlIG91dGxpZXJzLiBUaGUgZGF0YSBjb250YWluZWQgdGhyZWUgbnVtZXJpYyB2YXJpYWJsZXMgYW5kIHdpdGggdGhlIGhlbHAgb2YgdGhlIE1haGFsYW5vYmlzIGRpc3RhbmNlIG1ldGhvZCB0aGUgb3V0bGllcnMgd2VyZSBkZXRlY3RlZCBmb3IgbXVsdGl2YXJpYXRlIHNldHRpbmcuIA0KKiBVc2luZyB0aGUgbXZuKCkgZnVuY3Rpb24gb24gJ3NlbGxlcl9vcmRlcl9zdWInIGZyb20gdGhlIE1WTiBwYWNrYWdlLCBtdWx0aXZhcmlhdGUgb3V0bGllcnMgd2VyZSBkZXRlY3RlZCB3aXRoIFFRIHBsb3QuIFRoZSBzaG93T3V0bGllcnMgPSBUUlVFIGFyZ3VtZW50IGRlcGljdHMgdGhlIG11bHRpdmFyaWF0ZSBvdXRsaWVycyBhbmQgc2hvdyB0aGVtIG9uIFFRIHBsb3QuDQoqIFRoZSBRUSBwbG90IHN1Z2dlc3RzIGV4aXN0ZW5jZSBvZiA2OCBvdXRsaWVycyBmb3IgdGhpcyBzdWJzZXQuIA0KKiBUaGUgbG9jYXRpb24gb2Ygb3V0bGllcnMgaW4gdGhlIGRhdGFzZXQgd2VyZSBkZXRlY3RlZCB1c2luZyBzZWxsZXJfb3JkZXJfb3V0bGllcnMkbXVsdGl2YXJpYXRlT3V0bGllcnMuDQoqIE9uY2UgdGhlIHByZXNlbmNlIG9mIG91dGxpZXJzIGluIHRoZSBzdWJzZXQgb2YgZGF0YXNldCB3YXMgZGV0ZWN0ZWQsIHRoZSBuZXh0IHN0ZXAgd2FzIHRvIGhhbmRsZSB0aGVtLg0KKiBDYXBwaW5nIG9yIHdpbnNvcmlzaW5nIHdhcyBwZXJmb3JtZWQgaW4gb3JkZXIgdG8gZGVhbCB3aXRoIHRoZSBvdXRsaWVycywgaW5zdGVhZCBvZiByZW1vdmluZyB0aGVtLiBUaGlzIGlzIGJlY2F1c2UgdGhlIG91dGxpZXJzIGluIHRoaXMgY2FzZSBtYXkgbm90IG5lY2Vzc2FyaWx5IG1lYW4gdGhhdCB0aGUgdmFsdWVzIHJlcG9ydGVkIGFyZSBkdWUgdG8gc29tZSBlcnJvcnMuIFNvbWV0aW1lcyBvdXRsaWVycyBjYW4gYmUgbGVnaXRpbWF0ZSBvYnNlcnZhdGlvbnMuIEEgaGlnaCB0b3RhbCBvcmRlciB2YWx1ZSBjYW4gZWl0aGVyIG1lYW4gdGhlIG51bWJlciBvZiBpdGVtcyBpbiB0aGUgb3JkZXIgYXJlIGxhcmdlIG9yIHRoZSBpdGVtIHByaWNlIGFsb25nIHdpdGggZnJlaWdodCB2YWx1ZSBhcmUgaGlnaC4gDQoqIGNhcCgpIGZ1bmN0aW9uIHdhcyBjcmVhdGVkIHRvIHJlcGxhY2UgdGhvc2UgZGF0YSBwb2ludHMgbHlpbmcgb3V0c2lkZSB0aGUgbG93ZXIgbGltaXQgYW5kIGFib3ZlIHRoZSB1cHBlciBsaW1pdCB3aXRoIHRoZSB2YWx1ZSBvZiA1dGggcGVyY2VudGlsZSBhbmQgOTV0aCBwZXJjZW50aWxlLg0KKiBCZWZvcmUgY2FwcGluZywgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBvZiB0aGUgc3Vic2V0IHdhcyBwcm9kdWNlZCB1c2luZyBzdW1tYXJ5KCkuDQoqIEVhY2ggbnVtZXJpYyB2YXJpYWJsZSB3YXMgdGhlbiBjYXBwZWQgdXNpbmcgY2FwKCkgZnVuY3Rpb24gYW5kIHRoZSByZXN1bHRzIHdlcmUgc3RvcmVkIGluIHRoZSB2YXJpYWJsZXMgJ3ByaWNlX2NhcHBlZCcsICdmcmVpZ2h0X3ZhbHVlX2NhcHBlZCcgYW5kICd0b3RhbF9wcmljZV9jYXBwZWQnLg0KKiBTdW1tYXJ5IHN0YXRpc3RpY3Mgb2YgdGhlIGNhcHBlZCBzdWJzZXQgd2FzIGNvbXB1dGVkIHVzaW5nIHN1bW1hcnkoKS4NCg0KDQpgYGB7cn0NCg0KIyBNYWhhbmFib2xpcyBPdXRsaWVyIGRldGVjdGlvbiBtZXRob2QNCnNlbGxlcl9vcmRlcl9vdXRsaWVycyA8LSBtdm4oZGF0YSA9IHNlbGxlcl9vcmRlcl9zdWIsIG11bHRpdmFyaWF0ZU91dGxpZXJNZXRob2QgPSAicXVhbiIsIHNob3dPdXRsaWVycyA9IFRSVUUpDQoNCiNDaGVja2luZyBsb2NhdGlvbnMgb2YgdGhlIG91dGxpZXJzDQpzZWxsZXJfb3JkZXJfb3V0bGllcnMkbXVsdGl2YXJpYXRlT3V0bGllcnMNCg0KI0NhcHBpbmcNCmNhcCA8LSBmdW5jdGlvbih4KXsNCiAgcXVhbnRpbGVzIDwtIHF1YW50aWxlKCB4LCBjKC4wNSwgMC4yNSwgMC43NSwgLjk1ICkgKQ0KICB4WyB4IDwgcXVhbnRpbGVzWzJdIC0gMS41KklRUih4KSBdIDwtIHF1YW50aWxlc1sxXQ0KICB4WyB4ID4gcXVhbnRpbGVzWzNdICsgMS41KklRUih4KSBdIDwtIHF1YW50aWxlc1s0XQ0KICB4DQp9DQoNCiNEZXNjcmlwdGl2ZSBzdGF0aXN0aWNzIG9mIHRoZSBzdWJzZXQNCnN1bW1hcnkoc2VsbGVyX29yZGVyX3N1YikNCg0KI0FwcGx5aW5nIGNhcCgpIHRvIHRoZSBudW1lcmljIHZhcmlhYmxlcw0KcHJpY2VfY2FwcGVkPC0gc2VsbGVyX29yZGVyX2xpc3QkcHJpY2UgJT4lIGNhcCgpDQpmcmVpZ2h0X3ZhbHVlX2NhcHBlZDwtIHNlbGxlcl9vcmRlcl9saXN0JGZyZWlnaHRfdmFsdWUgJT4lIGNhcCgpDQp0b3RhbF9wcmljZV9jYXBwZWQ8LSBzZWxsZXJfb3JkZXJfbGlzdCR0b3RhbF9vcmRlcl92YWx1ZSAlPiUgY2FwKCkNCg0KI0NoZWNraW5nIHRoZSBzdW1tYXJ5IHN0YXRpc3RpY3MgYWdhaW4NCnNlbGxlcl9vcmRlcl9jYXBwZWQ8LSBzYXBwbHkoc2VsbGVyX29yZGVyX3N1YiwgRlVOID0gY2FwKQ0Kc3VtbWFyeShzZWxsZXJfb3JkZXJfY2FwcGVkKQ0KDQpgYGANCg0KDQojIwlUcmFuc2Zvcm0gDQoNCiogVGhlICd0b3RhbF9wcmljZV9jYXBwZWQnLCAncHJpY2VfY2FwcGVkJyByZXByZXNlbnRzIHRoZSB0b3RhbF9vcmRlcl92YWx1ZSBhbmQgcHJpY2UgYWZ0ZXIgY2FwcGluZyB0aGUgb3V0bGllcnMuIE9uIHByb2R1Y2luZyBhIGhpc3RvZ3JhbSBmb3IgYm90aCB0aGUgdmFyaWFibGVzLCBpdCB3YXMgb2JzZXJ2ZWQgdGhhdCB0aGV5IGhhZCByaWdodC1za2V3ZWQgZGlzdHJpYnV0aW9ucy4NCiogU2V2ZXJhbCBtZXRob2RzLCBmb3IgaW5zdGFuY2UsIGxvZzEwIHRyYW5zZm9ybWF0aW9uLCBzcXVhcmUgcm9vdCB0cmFuc2Zvcm1hdGlvbiwgcmVjaXByb2NhbCB0cmFuc2Zvcm1hdGlvbiBhbmQgQm94Q294IHRyYW5zZm9ybWF0aW9uIHdhcyBhcHBsaWVkIHRvIHJlZHVjZSB0aGUgc2tld25lc3MgYnV0IHRoZSBiZXN0IHJlc3VsdHMgd2VyZSBvYnRhaW5lZCB1c2luZyBuYXR1cmFsIGxvZ2FyaXRobSB0cmFuc2Zvcm1hdGlvbi4NCiogU2luY2UgdGhlIHZhcmlhYmxlcyBoYWQgbm8gemVybyBvciBuZWdhdGl2ZSB2YWx1ZXMsIGxvZygpIGZ1bmN0aW9uIHdhcyB1c2VkIHRvIHRyYW5mb3JtIHRoZSAndG90YWxfcHJpY2VfY2FwcGVkJyBhbmQgJ3ByaWNlX2NhcHBlZCcgdmFyaWFibGUgdG8gcHJvZHVjZSBhIG1vcmUgc3ltbWV0cmljYWwgZGlzdHJpYnV0aW9uLg0KKiBUaGUgJ2ZyZWlnaHRfdmFsdWVfY2FwcGVkJyB2YXJpYWJsZSBpcyBvYnNlcnZlZCB0byBiZSBtaWxkbHkgcmlnaHQgc2tld2VkIGFmdGVyIGNoZWNraW5nIGl0cyBoaXN0b2dyYW0uDQoqIFRvIHJlZHVjZSB0aGUgc2tld2VuZXNzLCBzZXZlcmFsIHRyYW5zZm9ybWF0aW9uIHRlY2huaXF1ZXMgd2VyZSBhcHBsaWVkIGFuZCB0aGUgYmVzdCByZXN1bHRzIHdlcmUgb2J0YWluZWQgYnkgdXNpbmcgc3F1YXJlIHJvb3QgdHJhbnNmb3JtYXRpb24sIHVzaW5nIHNxcnQoKSBmdW5jdGlvbi4NCiogSGlzdG9ncmFtcyBvZiBhbGwgdGhyZWUgdmFyaWFibGVzIHdlcmUgcGxvdCB0byBjaGVjayB0aGUgdHJhbnNmb3JtYXRpb24gcmVzdWx0cyB1c2luZyBoaXN0KCkgZnVuY3Rpb24uDQoNCg0KYGBge3J9DQoNCiMgQ2hlY2tpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0b3RhbF9wcmljZV9jYXBwZWQNCmhpc3QodG90YWxfcHJpY2VfY2FwcGVkLCBtYWluID0gIkhpc3RvZ3JhbSBmb3IgdG90YWxfb3JkZXJfdmFsdWUiLCB4bGFiID0gInRvdGFsX29yZGVyX3ZhbHVlIikNCg0KI0FwcGx5aW5nIG5hdHVyYWwgbG9nIHRyYW5zZm9ybWF0aW9uDQpsbl90b3RhbDwtIGxvZyh0b3RhbF9wcmljZV9jYXBwZWQpDQpoaXN0KGxuX3RvdGFsLG1haW4gPSAiTG9nIFRyYW5zZm9ybWF0aW9uIG9mIFRvdGFsIFByaWNlIiwgeGxhYiA9ICJUb3RhbCBQcmljZSIpDQoNCiMgQ2hlY2tpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwcmljZV9jYXBwZWQNCmhpc3QocHJpY2VfY2FwcGVkLCBtYWluID0gIkhpc3RvZ3JhbSBmb3IgcHJpY2UiLCB4bGFiID0gIlByaWNlIG9mIHRoZSBvcmRlciBpdGVtIikNCg0KI0FwcGx5aW5nIG5hdHVyYWwgbG9nIHRyYW5zZm9ybWF0aW9uDQpsbl9wcmljZTwtIGxvZyhwcmljZV9jYXBwZWQpDQpoaXN0KGxuX3ByaWNlLG1haW4gPSAiTG9nIFRyYW5zZm9ybWF0aW9uIG9mIFByaWNlIiwgeGxhYiA9ICJQcmljZSBvZiB0aGUgb3JkZXIgaXRlbSIpDQoNCiMgQ2hlY2tpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBmcmVpZ2h0X3ZhbHVlX2NhcHBlZA0KaGlzdChmcmVpZ2h0X3ZhbHVlX2NhcHBlZCwgbWFpbiA9ICJIaXN0b2dyYW0gZm9yIGZyZWlnaHRfdmFsdWUiLCB4bGFiID0gIkZyZWlnaHQgdmFsdWUiKQ0KDQojQXBwbHlpbmcgc3F1YXJlIHJvb3QgdHJhbnNmb3JtYXRpb24NCnNxcnRfdG90YWw8LSBzcXJ0KGZyZWlnaHRfdmFsdWVfY2FwcGVkKQ0KaGlzdChzcXJ0X3RvdGFsLCBtYWluID0gIlNxdWFyZSBSb290IFRyYW5zZm9ybWF0aW9uIG9mICBmcmVpZ2h0X3ZhbHVlIiwgeGxhYiA9ICJGcmVpZ2h0IHZhbHVlIikNCg0KYGBgDQoNCjxicj4NCjxicj4NCg==