Required packages
- dplyr package for
- select()
- inner_join()
- mutate()
- rename()
- lubridate for
- magrittr for
- stringr for
library(dplyr)
library(stringr)
library(knitr)
library(lubridate)
library(magrittr)
Data
Dataset Description
This assessment includes 2 datasets relate to the fatal crashes on Victorian roads during the latest five year reporting period. Road Safety data is provided by VicRoads for educational and research purposes with Creative Commons license 4.0. This data is in Web Mercator (Auxiliary Sphere) projection.(Vicroads,2021) The two datasets include:
- “Road_Crashes_for_five_Years_Victoria.csv” (https://vicroadsopendata-vicroadsmaps.opendata.arcgis.com/datasets/vicroadsmaps::road-crashes-for-five-years-victoria/about) (Full citation in the reference list)
This dataset is about ‘Fatal and injury crashes on Victorian roads during the latest five year reporting period.’ (Victoria Department of Transport Open Data Hub). The time period is between 25/05/2015 and 5/10/2021.
- "Fatal_Crashes_-_Lives_Lost%2C_Last_5_Years_to_Date.csv" (https://vicroadsopendata-vicroadsmaps.opendata.arcgis.com/datasets/vicroadsmaps::fatal-crashes-lives-lost-last-5-years-to-date-160421/about) (Full citation in the reference list)
This dataset is about the fatal crashes on Victorian roads during the latest five complete years and current year to the 5th of August 2021. (Vicroads,2021)
Variables Description
This descriptions just provided only the variables used in the data preprocessing process.
- ACCIDENT_NO is a charector field indicates the unique ID that identify particular accident that occured. Contains 12 characters, starting with T (for example, T20060123456) Where characters 2 to 5 are the year in which accident was registered; Where characters 6 to 12 are a numeric sequencing numbers
- DATE is a field indicates the date that the accident occurred.
- TIME is a field indicating the time that the accident occurred.
- DAY_OF_WEEK is is a charector field indicates the day of the week that the accident occurred.
- ACCIDENT_TYPE is a character field indicates the type of the accident. It is a basic description of what occurred, based on nine categories as following:
- Collision with vehicle
- Struck pedestrian
- Struck animal
- Collision with a fixed object
- Collision with some other object
- Vehicle overturned (no collision)
- Fall from or in moving vehicle
- No collision and no object struck
- Other accident
- NO_OF_VEHICLES is a numeric field indicates the number of vehicles involved in the crash.
- AGE is a field indicates how old the person was at the time of the accident. It is calculated by subtracting the person’s birth date from the accident date to give the person’s age in years.
- ROAD_USER is a character field indicates what the role of the person was at the time of the accident as following:
- Pedestrian
- Driver
- Passenger
- Motorcyclist
- Pillion Passenger
- Bicyclist (incl.passengers)
- Other driver
- Other passenger
- Not known
- SPEED_ZONE is a character field indicates the speed zone (km/hr) at the location of the accident. The speed zone is generally assigned to the main vehicle involved.
- LGA_NAME is a character field contains the local government area name that the accent occured.
- ALCOHOL_RELATED is a field of value “No” and “Yes” which “Yes” means the particular accident is alcohol related which Blood/Breath Alcohol Concentration>0.001 in the road user type: driver,rider,cyclist and pedestrian.
Preparation
step 1.1 Import the datasets by using the read.csv() to read the .csv files and assign “na.string” argument with “U”,“Unknown”, " “,”Not known" to tell R R to treat these values as NA(not available) value. * Assign the “Road_Crashes_for_five_Years_Victoria.csv” to “Crashes” dataframe and "Fatal_Crashes_-_Lives_Lost%2C_Last_5_Years_to_Date.csv" to “Fatal”dataframe.
step 1.2 Check the dataset for pre-processing data.
- Check the structure of both datasets using the str() function. The result shown that:
- They both are dataframe. The results also show the names and types of the variables together with the example of some values in the variable.
- For the “Crashes” dataset, there are 75,320 observations and 65 variables.
- For the “Fatal” dataset, there are 1,264 observations and 31 variables.
step 1.3 Select the following variables that I think they are useful and drop the rest using the select() function from dplyr:
- For “Crashes” dataframe select:
- ACCIDENT_NO
- ACCIDENT_TYPE
- DAY_OF_WEEK
- SPEED_ZONE
- LGA_NAME
- NO_OF_VEHICLES
- ALCOHOL_RELATED
- For “Fatal” dataframe select:
- ACCIDENT_N
- AGE
- RD_U_DESC
- ACCIDENT_D
- ACCIDENT_T
step 1.5 Rename some of variables to make them more sensible and also rename the “ACCIDENT_N” variable to “ACCIDENT_NO” to match with the variable “ACCIDENT_NO” in “Crashes” dataframe that I am going to use it to join the two dataframe in the next step. Use the rename() function from dplyr package to rename the “ACCIDENT_N” variale into “ACCIDENT_NO”, “RD_U_DESC” into “ROAD_USER”, “ACCIDENT_D” into “DATE” and “ACCIDENT_T” into “TIME”.
step 1.6 Merge 2 datasets by keeping only the observation in both dataframe using the inner_join() function from dplyr package. Joining both datasets together by variable “ACCIDENT_NO” using the argument “by” and assign the result into “Lives_lost” dataframe.
step 1.7 Rearrange the variables order by assign the positions of the variable in order that I want to arrange by using square brackets and the c() function. Leaving the rows selection part blank to subset every rows.
Crashes<- read.csv("Road_Crashes_for_five_Years_Victoria.csv", na.strings = c("U","Unknown", " ", "Not known"))
Fatal <- read.csv("Fatal_Crashes_-_Lives_Lost%2C_Last_5_Years_to_Date.csv",na.strings = c("U","Unknown", " ","Not known"))
str(Fatal)
str(Crashes)
Crashes %<>% select(ACCIDENT_NO, ACCIDENT_TYPE, DAY_OF_WEEK, SPEED_ZONE, LGA_NAME ,NO_OF_VEHICLES, ALCOHOL_RELATED)
Fatal %<>% select(ACCIDENT_N, ACCIDENT_D, ACC_TIME, AGE, RD_U_DESC)
Fatal %<>% rename(c("ACCIDENT_NO" = ACCIDENT_N, "ROAD_USER" = RD_U_DESC, "DATE" = ACCIDENT_D, "TIME" = ACC_TIME))
Lives_lost <- inner_join(Crashes, Fatal, by = "ACCIDENT_NO")
Lives_lost <- Lives_lost[,c(1, 8, 9, 3, 2, 6, 10, 11, 4, 5, 7 )]
head(Lives_lost)
Understand
step 2.1 Check the structure of the dataset by using the str() function and the attributes. The result shown:
- The dataset is a dataframe contains 1,264 Observations and 11 variables.
- The variable “ACCIDENT_NO”, “DAY_OF_WEEK”, “ACCIDENT_TYPE”, “ROAD_USER”, “SPEED_ZONE”, “LGA_NAME”, “ALCOHOL_RELATED”, “DATE” and “TIME” are a character type. I will leave the “DATE” and “TIME” variable as they are as I am going to combine them together and convert them into POSIXct class in Step 4.
- The variable “AGE” and “NO_OF_VEHICLES” are integer type. I will leave the “NO_OF_VEHICLES” as they are because integer is the right type for it.
step 2.2 Variables conversion
- Convert the variables “ACCIDENT_NO”, “ACCIDENT_TYPE” and “ROAD_USER” to be factor using the as.factor() function.
- Convert the variables “DAY_OF_WEEK”, “AGE” and “SPEED_ZONE” into an ordered factor varialbles by using the factor() function and ordering them using the “levels” argument with the c() function and assign argument “ordered” to be “TRUE” so the levels are regarded as ordered.
- Convert the variable “ALCOHOL_RELATED” into logical type by:
- Replace values “No” with “FALSE” and values “Yes” with “TRUE”.
- Use the as.logical() function to convert them into logical type.
- Check the result of the conversion by using the str() function. The result shown that:
- The “ACCIDENT_NO”, “ACCIDENT_TYPE” and “ROAD_USER” have converted into factor type.
- The “DAY_OF_WEEK”, “AGE” and “SPEED_ZONE” have converted into ordered factor type
- The “ALCOHOL_RELATED” have converted into logical type.
Lives_lost %>% str()
Lives_lost %>% attributes()
Lives_lost$ACCIDENT_NO %<>% as.factor()
Lives_lost$ACCIDENT_TYPE %<>% as.factor()
Lives_lost$ROAD_USER %<>% as.factor()
Lives_lost$DAY_OF_WEEK %<>% factor(levels=c("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"), ordered=TRUE)
Lives_lost$AGE %<>% factor(levels = c(1:99), ordered = TRUE)
Lives_lost$SPEED_ZONE %<>% factor(levels = c("Camping grounds or off road", "40 km/hr","50 km/hr", "60 km/hr", "70 km/hr","75 km/hr", "80 km/hr", "90 km/hr", "100 km/hr", "110 km/hr", "Other speed limit"), ordered = TRUE)
Lives_lost$ALCOHOL_RELATED<-gsub("No", "FALSE", Lives_lost$ALCOHOL_RELATED)
Lives_lost$ALCOHOL_RELATED<-gsub("Yes", "TRUE", Lives_lost$ALCOHOL_RELATED)
Lives_lost$ALCOHOL_RELATED %<>% as.logical()
Lives_lost %>% str()
Tidy & Manipulate Data I
Step 3 Determine whether the dataset is tidy or untidy.
To determine if my dataset is tidy or not, I used the three interrelated rules:
- Each variable must have its own column.
- Each observation must have its own row.
- Each value must have its own cell. (Hadley Wickham and Garrett Grolemund 2016)
From my point of view my dataset “Live_lost” is already in a tidy format as each variable have got its own column, each Observation have got its own row and Each value have got its own cell.
Tidy & Manipulate Data II
Step 4.1 Shape the format of the values from “DATE” variable by keeping the value from the first position to 10th and drop the rest by using the substr() function. Assign the “start” argument with “1” and the “stop” argument with “10” to indicate the range of position that I want to keep.
Step 4.2 Shape “TIME” variable into the easy format in preparing to use the ymd_hms() function in step 4.4 by using the str_replace_all() function from stringr package to replace “.:” with “:” and replace “.00” with “0.00”. Use the head() function to review the result.
Step 4.3 Create the new variable “DATE” by combining variable “DATE” and “TIME” together.
- Using the mutate() function from the dplyr package to create a new variable called “DATE”.
- Using the paste() function to:
- Put the value from the old variable “DATE” and follow with the values from the variable “TIME”.
- Assign the argument “sep” with " "(blank) to indicate the separator between the values.
Step 4.4 Convert the variable “DATE” into POSIXct class using ymd_hms() function from “lubridate” package.
Step 4.5 Drop variables “TIME” by using the select() function with the c() function and the “-” operator to assign the column name “TIME” that I want to drop. Use the str() function to check the result. The result shown that:
- The “DATE” variable have convert into class POSIXct which representing calendar dates and times.
- The “DATE” variable format change into “yyyy-mm-dd hh:mm:ss”.
- The “TIME” variable has dropped.
Lives_lost$DATE %<>% substr(start = 1, stop = 10)
Lives_lost$TIME %<>% str_replace_all(".:", ":")
Lives_lost$TIME %<>% str_replace_all(".00", "0:00")
head(Lives_lost)
Lives_lost %<>% mutate (DATE = paste(DATE, TIME, sep = " "))
Lives_lost$DATE %<>% ymd_hms()
Lives_lost %<>% select(-TIME)
str(Lives_lost)
Scan I
Step 5.1 Check the amount of NA in each column by using the is.na() function to check for the NA value and the colSum() function to sum up the total of NA value in each column. The result found:
- 2 of the values in “DAY_OF_WEEK” variable are NA.
- 5 of the values in “AGE” variable are NA.
- 16 of the values in “SPEED_ZONE” are NA.
Step 5.2 Replace the missing values in “DAY_OF_WEEK” variable :
- The 2 of the value in “DAY_OF_WEEK” variable are NA but the “DATE” variable have no NA value means I can replace the missing value by using the weekdays() function to find the day of the week and assign the value back to column “DAY_OF_WEEK”.
- By doing this replacement causing the “DAY_OF_WEEK” variable to turn into a character type so I convert it back in to an ordered factor by using the same method as the step 2.2.
- Check the NA values in the column “DAY_OF_WEEK” to make sure there is none NA value left in “DAY_OF_WEEK” column by using the is.na() function together with the which() function and the length() function. The result shown that there is no NA value in “DAY_OF_WEEK” variable.
Step 5.2 Check the amount of NA in the whole dataset by:
- Use the is.na() function to check for the NA value.
- Use the which() function to identify the position of NA value.
- Use length() function to get the number of the NA value.
- Using pipes operator between the function to pass the result of one function to the other one in sequence.
Step 5.3 Subsetting the observations with NA values into the dataframe called “NA_relation_check” to be able to have a better look for the relation of the missing value with other variable.
- Use the is.na() function to find the NA value in the dataset.
- Use the rowSums() function to sum up the missing value in each observation.
- Subset just the observation with missing values (>0) and put in the dataframe “NA_relation_check”.
Step 5.4 After checking, I could not find any relation to the other variable or between the observation. The missing value look like Missing completely at random (MCAR) for me. “A rule of thumb says that when the data include less than 5% random missingness which does not depend on observed or unobserved values, complete case analysis may be an acceptable approach, too”(Hannah Roos, 2021).
- Calculate the percentage of the observation with missing value by deviding the number of the observations with the missing values by the number of all the observations in the dataset and then multiply by 100. The result shown that the observations with the missing values is just 1.66% of all observations. Because of the information above, I choose to exclude all the observation with the missing value.
Step 5.5 Exclude the observations with missing values:
- Use the complete.cases() function to identify complete row of the dataframe and the square brackets to subset those row with all column. Assign the result to dataframe called “Lives_lost2”.
- Check if there is any NA value left in the dataframe with the same function as step 5.2.
- The result shown that there is no NA value left in the dataframe “Lives_lost2”.
is.na(Lives_lost) %>% colSums()
Lives_lost$DAY_OF_WEEK <- weekdays(Lives_lost$DATE)
Lives_lost$DAY_OF_WEEK %<>% factor(levels=c("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"), ordered=TRUE)
is.na(Lives_lost$DAY_OF_WEEK) %>% which() %>% length()
is.na(Lives_lost) %>% which() %>% length()
NA_relation_check <- Lives_lost[rowSums(is.na(Lives_lost)) > 0, ]
NA_relation_check %>% head
((NA_relation_check %>% nrow)/(Lives_lost%>% nrow())) * 100
Lives_lost2<-Lives_lost[complete.cases(Lives_lost), ]
is.na(Lives_lost2)%>% which() %>% length()
Scan II
I am using the univariate outlier detection method with Non-parametric method (Box plot), to approach the “NO_OF_VEHICLES” variable because I am just focusing on one particular variable and I assume that the data is not normally distribute. For the handling process, I choose to delete all the outliers because imputing with mean does not makesense as the value indicate number of vehicles which should be integer (mean is 1.651 which is not integer) and imputing with median is not going to suit with the other variables in the observation. The processes is as following:
Step 6.1 Create Boxplot of the variable “NO_OF_VEHICLES” to see the big picture of the data and the outliers by using the boxplot() function.
- Assign the argument “main” with the title of the boxplot, in this case “Box Plot of Number of vehicles”.
- Assign the argument “ylab” with the y-axis annotation, in this case “Number of vehicles”.
- Assign the argument “col” with the color preference for the bodies of the box plot, in this case I choose “orange”.
Step 6.2 Apply the univariate outlier detection by:
- Getting the Q1 and Q3 values by using the summary() function. The result shown:
- The first position: minimum value = 1.00.
- The second position: Q1 = 1.00.
- The third position: median = 1.00
- The 4th position: mean = 1.65
- The 5th position: Q3 = 2.00
- The 6th position: maximum value = 11.00
- Extract the Q1 value by using the double square brackets to subset the value from the 2nd position of the summary() function’s result and assign to “q1”.
- Extract the Q3 value by using the double square brackets to subset the value from the 5th position of the summary() function’s result and assign to “q3”.
- Calculate the interquartile range(IQR) by subtract q1 value from q3 value and assign to “iqr”.
- Calculate the upper fence by add the 1.5 times of iqr to q3 and assign the value to “upper_fence”.
- Calculate the lower fence by subtract the 1.5 times of iqr from q1 and assign the value to “lower_fence”.
- Extract the position of the outliers by using the which() function, picking just the values that is more than the “upper_fence” value and less than the “lower_fence” value. Assign the outliers over the “upper_fence” value into “up_outliers” and assign the outliers below the “lower_fence” value into “low_outliers”.
- Sum up the amount of outliers in each of the object (the “up_outliers” and the “low_outliers”) by using the “length” function. the result shown that the amount of the outlier above the “upper_fence” value is 37 outliers but there is none outlier below the “lower_fence”
Step 6.3 Handle the outliers by exclude them using the squre bracket and “-” operator to drop the “up_outlier” positions from the “Lives_lost2$NO_OF_VEHICLES” and assign the result into the “Vehicle_clean” object.
- Check the result by using the boxplot() function and assign the list into “bp” and then use the length() function to sum up the amount of the outlier in the “out” element in the “bp” list. The result showm that there is non outlier left in the object “Vehical_clean”.
Lives_lost2$NO_OF_VEHICLES %>%
boxplot(main = "Box Plot of Number of vehicle", ylab = "Number of vehicle", col = "orange")
summary(Lives_lost2$NO_OF_VEHICLES)
q1 <- summary(Lives_lost2$NO_OF_VEHICLES)[[2]]
q3 <- summary(Lives_lost2$NO_OF_VEHICLES)[[5]]
iqr <- q3 - q1
lower_fence <- q1 - (1.5 * iqr)
upper_fence <- q3 + (1.5 * iqr)
up_outliers <- which(Lives_lost2$NO_OF_VEHICLES > upper_fence)
low_outliers <- which(Lives_lost2$NO_OF_VEHICLES < lower_fence)
low_outliers %>% length()
up_outliers %>% length()
Vehicle_clean<- Lives_lost2$NO_OF_VEHICLES[-up_outliers]
bp<- boxplot(Vehicle_clean)
bp$out %>% length()
References:
Calculate Skewness in R n.d., ProgrammingR, viewed 14 October 2021, https://www.programmingr.com/statistics/skewness/
Hadley Wickham and Garrett Grolemund, R for Data Science, R4DS, viewed 18 September 2021, https://r4ds.had.co.nz/tidy-data.html
Mangiafico, S.S. 2016, Summary and Analysis of Extension Program Evaluation in R, viewed 14 October 2021, https://rcompanion.org/handbook/I_12.html
Victoria Department of Transport 2021, Crashes_Last_Five_Years - Open Data, data file, Victoria Department of Transport Open Data Hub, viewed 10 October 2021, https://vicroadsopendata-vicroadsmaps.opendata.arcgis.com/datasets/vicroadsmaps::road-crashes-for-five-years-victoria/about.
Victoria Department of Transport, Fatal Crashes - Lives Lost, Last 5 Years to Date, the Fatal Crashes - Lives Lost Year to Date dataset, data file, Department of Transport Open Data Hub, viewed 10 October 2021, https://vicroadsopendata-vicroadsmaps.opendata.arcgis.com/datasets/vicroadsmaps::road-crashes-for-five-years-victoria/about.
Hannah Roos 2021, Smart handling of missing data in R, Towards Data Science, viewed 10 October 2021, https://towardsdatascience.com/smart-handling-of-missing-data-in-r-6425f8a559f2
LS0tCnRpdGxlOiAiRGF0YSBXcmFuZ2xpbmciCmF1dGhvcjogTmFhbSBNYXJzaGFsbApzdWJ0aXRsZTogIlZpY3RvcmlhIEZhdGFsIENyYXNoZXMgaW4gdGhlIGxhc3QgNSB5ZWFycyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCgoKCiMjIFJlcXVpcmVkIHBhY2thZ2VzIAogIAoqIGRwbHlyIHBhY2thZ2UgZm9yICAKICArIHNlbGVjdCgpCiAgKyBpbm5lcl9qb2luKCkKICArIG11dGF0ZSgpCiAgKyByZW5hbWUoKQogIAoqIGx1YnJpZGF0ZSBmb3IgCiAgKyB5bWRfaG1zKCkKICAKKiBtYWdyaXR0ciBmb3IKICArICU+JSAKICArICU8PiUgCiAgCiogc3RyaW5nciBmb3IKICArIHN0cl9yZXBsYWNlX2FsbCgpCgpgYGB7ciBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShrbml0cikKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkobWFncml0dHIpCgpgYGAKCgojIyBEYXRhIAoKKipEYXRhc2V0IERlc2NyaXB0aW9uKioKClRoaXMgYXNzZXNzbWVudCBpbmNsdWRlcyAyIGRhdGFzZXRzIHJlbGF0ZSB0byB0aGUgZmF0YWwgY3Jhc2hlcyBvbiBWaWN0b3JpYW4gcm9hZHMgZHVyaW5nIHRoZSBsYXRlc3QgZml2ZSB5ZWFyIHJlcG9ydGluZyBwZXJpb2QuIFJvYWQgU2FmZXR5IGRhdGEgaXMgcHJvdmlkZWQgYnkgVmljUm9hZHMgZm9yIGVkdWNhdGlvbmFsIGFuZCByZXNlYXJjaCBwdXJwb3NlcyB3aXRoIENyZWF0aXZlIENvbW1vbnMgbGljZW5zZSA0LjAuIFRoaXMgZGF0YSBpcyBpbiBXZWIgTWVyY2F0b3IgKEF1eGlsaWFyeSBTcGhlcmUpIHByb2plY3Rpb24uKFZpY3JvYWRzLDIwMjEpIFRoZSB0d28gZGF0YXNldHMgaW5jbHVkZTogIAoKMS4gIlJvYWRfQ3Jhc2hlc19mb3JfZml2ZV9ZZWFyc19WaWN0b3JpYS5jc3YiIChodHRwczovL3ZpY3JvYWRzb3BlbmRhdGEtdmljcm9hZHNtYXBzLm9wZW5kYXRhLmFyY2dpcy5jb20vZGF0YXNldHMvdmljcm9hZHNtYXBzOjpyb2FkLWNyYXNoZXMtZm9yLWZpdmUteWVhcnMtdmljdG9yaWEvYWJvdXQpIChGdWxsIGNpdGF0aW9uIGluIHRoZSByZWZlcmVuY2UgbGlzdCkKClRoaXMgZGF0YXNldCBpcyBhYm91dCAnRmF0YWwgYW5kIGluanVyeSBjcmFzaGVzIG9uIFZpY3RvcmlhbiByb2FkcyBkdXJpbmcgdGhlIGxhdGVzdCBmaXZlIHllYXIgcmVwb3J0aW5nIHBlcmlvZC4nIChWaWN0b3JpYSBEZXBhcnRtZW50IG9mIFRyYW5zcG9ydCBPcGVuIERhdGEgSHViKS4gVGhlIHRpbWUgcGVyaW9kIGlzIGJldHdlZW4gMjUvMDUvMjAxNSBhbmQgNS8xMC8yMDIxLgoKMi4gIkZhdGFsX0NyYXNoZXNfLV9MaXZlc19Mb3N0JTJDX0xhc3RfNV9ZZWFyc190b19EYXRlLmNzdiIKKGh0dHBzOi8vdmljcm9hZHNvcGVuZGF0YS12aWNyb2Fkc21hcHMub3BlbmRhdGEuYXJjZ2lzLmNvbS9kYXRhc2V0cy92aWNyb2Fkc21hcHM6OmZhdGFsLWNyYXNoZXMtbGl2ZXMtbG9zdC1sYXN0LTUteWVhcnMtdG8tZGF0ZS0xNjA0MjEvYWJvdXQpIChGdWxsIGNpdGF0aW9uIGluIHRoZSByZWZlcmVuY2UgbGlzdCkKClRoaXMgZGF0YXNldCBpcyBhYm91dCB0aGUgZmF0YWwgY3Jhc2hlcyBvbiBWaWN0b3JpYW4gcm9hZHMgZHVyaW5nIHRoZSBsYXRlc3QgZml2ZSBjb21wbGV0ZSB5ZWFycyBhbmQgY3VycmVudCB5ZWFyIHRvIHRoZSA1dGggb2YgQXVndXN0IDIwMjEuIChWaWNyb2FkcywyMDIxKQoKKipWYXJpYWJsZXMgRGVzY3JpcHRpb24qKgoKVGhpcyBkZXNjcmlwdGlvbnMganVzdCBwcm92aWRlZCBvbmx5IHRoZSB2YXJpYWJsZXMgdXNlZCBpbiB0aGUgZGF0YSBwcmVwcm9jZXNzaW5nIHByb2Nlc3MuCgoqICoqQUNDSURFTlRfTk8qKiBpcyBhIGNoYXJlY3RvciBmaWVsZCBpbmRpY2F0ZXMgdGhlIHVuaXF1ZSBJRCB0aGF0IGlkZW50aWZ5IHBhcnRpY3VsYXIgYWNjaWRlbnQgdGhhdCBvY2N1cmVkLiBDb250YWlucyAxMiBjaGFyYWN0ZXJzLCBzdGFydGluZyB3aXRoIFQgKGZvciBleGFtcGxlLCBUMjAwNjAxMjM0NTYpIFdoZXJlIGNoYXJhY3RlcnMgMiB0byA1IGFyZSB0aGUgeWVhciBpbiB3aGljaCBhY2NpZGVudCB3YXMgcmVnaXN0ZXJlZDsgV2hlcmUgY2hhcmFjdGVycyA2IHRvIDEyIGFyZSBhIG51bWVyaWMgc2VxdWVuY2luZyBudW1iZXJzCiogKipEQVRFKiogaXMgYSBmaWVsZCBpbmRpY2F0ZXMgdGhlIGRhdGUgdGhhdCB0aGUgYWNjaWRlbnQgb2NjdXJyZWQuIAoqICoqVElNRSoqIGlzIGEgZmllbGQgaW5kaWNhdGluZyB0aGUgdGltZSB0aGF0IHRoZSBhY2NpZGVudCBvY2N1cnJlZC4KKiAqKkRBWV9PRl9XRUVLKiogaXMgaXMgYSBjaGFyZWN0b3IgZmllbGQgaW5kaWNhdGVzIHRoZSBkYXkgb2YgdGhlIHdlZWsgdGhhdCB0aGUgYWNjaWRlbnQgb2NjdXJyZWQuCiogKipBQ0NJREVOVF9UWVBFKiogaXMgYSBjaGFyYWN0ZXIgZmllbGQgaW5kaWNhdGVzIHRoZSB0eXBlIG9mIHRoZSBhY2NpZGVudC4gSXQgaXMgYSBiYXNpYyBkZXNjcmlwdGlvbiBvZiB3aGF0IG9jY3VycmVkLCBiYXNlZCBvbiBuaW5lIGNhdGVnb3JpZXMgYXMgZm9sbG93aW5nOiAgCiAgKyBDb2xsaXNpb24gd2l0aCB2ZWhpY2xlCiAgKyBTdHJ1Y2sgcGVkZXN0cmlhbgogICsgU3RydWNrIGFuaW1hbAogICsgQ29sbGlzaW9uIHdpdGggYSBmaXhlZCBvYmplY3QKICArIENvbGxpc2lvbiB3aXRoIHNvbWUgb3RoZXIgb2JqZWN0CiAgKyBWZWhpY2xlIG92ZXJ0dXJuZWQgKG5vIGNvbGxpc2lvbikKICArIEZhbGwgZnJvbSBvciBpbiBtb3ZpbmcgdmVoaWNsZQogICsgTm8gY29sbGlzaW9uIGFuZCBubyBvYmplY3Qgc3RydWNrCiAgKyBPdGhlciBhY2NpZGVudAoqICoqTk9fT0ZfVkVISUNMRVMqKiBpcyBhIG51bWVyaWMgZmllbGQgaW5kaWNhdGVzIHRoZSBudW1iZXIgb2YgdmVoaWNsZXMgaW52b2x2ZWQgaW4gdGhlIGNyYXNoLgoqICoqQUdFKiogaXMgYSBmaWVsZCBpbmRpY2F0ZXMgaG93IG9sZCB0aGUgcGVyc29uIHdhcyBhdCB0aGUgdGltZSBvZiB0aGUgYWNjaWRlbnQuIEl0IGlzIGNhbGN1bGF0ZWQgYnkgc3VidHJhY3RpbmcgdGhlIHBlcnNvbuKAmXMgYmlydGggZGF0ZSBmcm9tIHRoZSBhY2NpZGVudCBkYXRlIHRvIGdpdmUgdGhlIHBlcnNvbuKAmXMgYWdlIGluIHllYXJzLgoqICoqUk9BRF9VU0VSKiogaXMgYSBjaGFyYWN0ZXIgZmllbGQgaW5kaWNhdGVzIHdoYXQgdGhlIHJvbGUgb2YgdGhlIHBlcnNvbiB3YXMgYXQgdGhlIHRpbWUgb2YgdGhlIGFjY2lkZW50IGFzIGZvbGxvd2luZzogIAogICsgUGVkZXN0cmlhbgogICsgRHJpdmVyCiAgKyBQYXNzZW5nZXIKICArIE1vdG9yY3ljbGlzdAogICsgUGlsbGlvbiBQYXNzZW5nZXIKICArIEJpY3ljbGlzdCAoaW5jbC5wYXNzZW5nZXJzKQogICsgT3RoZXIgZHJpdmVyCiAgKyBPdGhlciBwYXNzZW5nZXIKICArIE5vdCBrbm93bgoqICoqU1BFRURfWk9ORSoqIGlzIGEgY2hhcmFjdGVyIGZpZWxkIGluZGljYXRlcyB0aGUgc3BlZWQgem9uZSAoa20vaHIpIGF0IHRoZSBsb2NhdGlvbiBvZiB0aGUgYWNjaWRlbnQuIFRoZSBzcGVlZCB6b25lIGlzIGdlbmVyYWxseSBhc3NpZ25lZCB0byB0aGUgbWFpbiB2ZWhpY2xlIGludm9sdmVkLgoqICoqTEdBX05BTUUqKiBpcyBhIGNoYXJhY3RlciBmaWVsZCBjb250YWlucyB0aGUgbG9jYWwgZ292ZXJubWVudCBhcmVhIG5hbWUgdGhhdCB0aGUgYWNjZW50IG9jY3VyZWQuIAoqICoqQUxDT0hPTF9SRUxBVEVEKiogaXMgYSBmaWVsZCBvZiB2YWx1ZSAiTm8iIGFuZCAiWWVzIiB3aGljaCAiWWVzIiBtZWFucyB0aGUgcGFydGljdWxhciBhY2NpZGVudCBpcyBhbGNvaG9sIHJlbGF0ZWQgd2hpY2ggQmxvb2QvQnJlYXRoIEFsY29ob2wgQ29uY2VudHJhdGlvbj4wLjAwMSBpbiB0aGUgcm9hZCB1c2VyIHR5cGU6IGRyaXZlcixyaWRlcixjeWNsaXN0IGFuZCBwZWRlc3RyaWFuLgoKIyMgUHJlcGFyYXRpb24KIAogKipzdGVwIDEuMSoqIEltcG9ydCB0aGUgZGF0YXNldHMgYnkgdXNpbmcgdGhlIHJlYWQuY3N2KCkgdG8gcmVhZCB0aGUgLmNzdiBmaWxlcyBhbmQgYXNzaWduICJuYS5zdHJpbmciIGFyZ3VtZW50IHdpdGggIlUiLCJVbmtub3duIiwgIiAiLCAiTm90IGtub3duIiB0byB0ZWxsIFIgUiB0byB0cmVhdCB0aGVzZSB2YWx1ZXMgYXMgTkEobm90IGF2YWlsYWJsZSkgdmFsdWUuIAoqIEFzc2lnbiB0aGUgIlJvYWRfQ3Jhc2hlc19mb3JfZml2ZV9ZZWFyc19WaWN0b3JpYS5jc3YiIHRvICJDcmFzaGVzIiBkYXRhZnJhbWUgYW5kICJGYXRhbF9DcmFzaGVzXy1fTGl2ZXNfTG9zdCUyQ19MYXN0XzVfWWVhcnNfdG9fRGF0ZS5jc3YiIHRvICJGYXRhbCJkYXRhZnJhbWUuCgoqKnN0ZXAgMS4yKiogQ2hlY2sgdGhlIGRhdGFzZXQgZm9yIHByZS1wcm9jZXNzaW5nIGRhdGEuICAKCiogQ2hlY2sgdGhlIHN0cnVjdHVyZSBvZiBib3RoIGRhdGFzZXRzIHVzaW5nIHRoZSBzdHIoKSBmdW5jdGlvbi4gVGhlIHJlc3VsdCBzaG93biB0aGF0OgogICsgVGhleSBib3RoIGFyZSBkYXRhZnJhbWUuIFRoZSByZXN1bHRzIGFsc28gc2hvdyB0aGUgbmFtZXMgYW5kIHR5cGVzIG9mIHRoZSB2YXJpYWJsZXMgdG9nZXRoZXIgd2l0aCB0aGUgZXhhbXBsZSBvZiBzb21lIHZhbHVlcyBpbiB0aGUgdmFyaWFibGUuIAogICsgRm9yIHRoZSAiQ3Jhc2hlcyIgZGF0YXNldCwgdGhlcmUgYXJlIDc1LDMyMCBvYnNlcnZhdGlvbnMgYW5kIDY1IHZhcmlhYmxlcy4KICArIEZvciB0aGUgIkZhdGFsIiBkYXRhc2V0LCB0aGVyZSBhcmUgMSwyNjQgb2JzZXJ2YXRpb25zIGFuZCAzMSB2YXJpYWJsZXMuCgoqKnN0ZXAgMS4zKiogU2VsZWN0IHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzIHRoYXQgSSB0aGluayB0aGV5IGFyZSB1c2VmdWwgYW5kIGRyb3AgdGhlIHJlc3QgdXNpbmcgdGhlIHNlbGVjdCgpIGZ1bmN0aW9uIGZyb20gZHBseXI6IAoKKiBGb3IgIkNyYXNoZXMiIGRhdGFmcmFtZSBzZWxlY3Q6CiAgKyBBQ0NJREVOVF9OTwogICsgQUNDSURFTlRfVFlQRQogICsgREFZX09GX1dFRUsKICArIFNQRUVEX1pPTkUKICArIExHQV9OQU1FCiAgKyBOT19PRl9WRUhJQ0xFUwogICsgQUxDT0hPTF9SRUxBVEVECiAgCiogRm9yICJGYXRhbCIgZGF0YWZyYW1lIHNlbGVjdDogCiAgKyBBQ0NJREVOVF9OCiAgKyBBR0UKICArIFJEX1VfREVTQwogICsgQUNDSURFTlRfRAogICsgQUNDSURFTlRfVAoKKipzdGVwIDEuNSoqIFJlbmFtZSBzb21lIG9mIHZhcmlhYmxlcyB0byBtYWtlIHRoZW0gbW9yZSBzZW5zaWJsZSBhbmQgYWxzbyByZW5hbWUgdGhlICJBQ0NJREVOVF9OIiB2YXJpYWJsZSB0byAiQUNDSURFTlRfTk8iIHRvIG1hdGNoIHdpdGggdGhlIHZhcmlhYmxlICJBQ0NJREVOVF9OTyIgaW4gIkNyYXNoZXMiIGRhdGFmcmFtZSB0aGF0IEkgYW0gZ29pbmcgdG8gdXNlIGl0IHRvIGpvaW4gdGhlIHR3byBkYXRhZnJhbWUgaW4gdGhlIG5leHQgc3RlcC4gVXNlIHRoZSByZW5hbWUoKSBmdW5jdGlvbiBmcm9tIGRwbHlyIHBhY2thZ2UgdG8gcmVuYW1lIHRoZSAiQUNDSURFTlRfTiIgdmFyaWFsZSBpbnRvICJBQ0NJREVOVF9OTyIsICJSRF9VX0RFU0MiIGludG8gIlJPQURfVVNFUiIsICJBQ0NJREVOVF9EIiBpbnRvICJEQVRFIiBhbmQgIkFDQ0lERU5UX1QiIGludG8gIlRJTUUiLgoKKipzdGVwIDEuNioqIE1lcmdlIDIgZGF0YXNldHMgYnkga2VlcGluZyBvbmx5IHRoZSBvYnNlcnZhdGlvbiBpbiBib3RoIGRhdGFmcmFtZSB1c2luZyB0aGUgaW5uZXJfam9pbigpIGZ1bmN0aW9uIGZyb20gZHBseXIgcGFja2FnZS4gSm9pbmluZyBib3RoIGRhdGFzZXRzIHRvZ2V0aGVyIGJ5IHZhcmlhYmxlICJBQ0NJREVOVF9OTyIgdXNpbmcgdGhlIGFyZ3VtZW50ICJieSIgYW5kIGFzc2lnbiB0aGUgcmVzdWx0IGludG8gIkxpdmVzX2xvc3QiIGRhdGFmcmFtZS4gCgoqKnN0ZXAgMS43KiogUmVhcnJhbmdlIHRoZSB2YXJpYWJsZXMgb3JkZXIgYnkgYXNzaWduIHRoZSBwb3NpdGlvbnMgb2YgdGhlIHZhcmlhYmxlIGluIG9yZGVyIHRoYXQgSSB3YW50IHRvIGFycmFuZ2UgYnkgdXNpbmcgc3F1YXJlIGJyYWNrZXRzIGFuZCB0aGUgYygpIGZ1bmN0aW9uLiBMZWF2aW5nIHRoZSByb3dzIHNlbGVjdGlvbiBwYXJ0IGJsYW5rIHRvIHN1YnNldCBldmVyeSByb3dzLgoKYGBge3J9CgpDcmFzaGVzPC0gcmVhZC5jc3YoIlJvYWRfQ3Jhc2hlc19mb3JfZml2ZV9ZZWFyc19WaWN0b3JpYS5jc3YiLCBuYS5zdHJpbmdzID0gYygiVSIsIlVua25vd24iLCAiICIsICJOb3Qga25vd24iKSkKRmF0YWwgPC0gcmVhZC5jc3YoIkZhdGFsX0NyYXNoZXNfLV9MaXZlc19Mb3N0JTJDX0xhc3RfNV9ZZWFyc190b19EYXRlLmNzdiIsbmEuc3RyaW5ncyA9IGMoIlUiLCJVbmtub3duIiwgIiAiLCJOb3Qga25vd24iKSkKCnN0cihGYXRhbCkKc3RyKENyYXNoZXMpCgoKQ3Jhc2hlcyAlPD4lIHNlbGVjdChBQ0NJREVOVF9OTywgQUNDSURFTlRfVFlQRSwgREFZX09GX1dFRUssIFNQRUVEX1pPTkUsIExHQV9OQU1FICxOT19PRl9WRUhJQ0xFUywgQUxDT0hPTF9SRUxBVEVEKQpGYXRhbCAlPD4lIHNlbGVjdChBQ0NJREVOVF9OLCBBQ0NJREVOVF9ELCBBQ0NfVElNRSwgQUdFLCBSRF9VX0RFU0MpCkZhdGFsICU8PiUgcmVuYW1lKGMoIkFDQ0lERU5UX05PIiA9IEFDQ0lERU5UX04sICJST0FEX1VTRVIiID0gUkRfVV9ERVNDLCAiREFURSIgPSBBQ0NJREVOVF9ELCAiVElNRSIgPSBBQ0NfVElNRSkpIAoKTGl2ZXNfbG9zdCA8LSBpbm5lcl9qb2luKENyYXNoZXMsIEZhdGFsLCBieSA9ICJBQ0NJREVOVF9OTyIpCkxpdmVzX2xvc3QgPC0gTGl2ZXNfbG9zdFssYygxLCA4LCA5LCAzLCAyLCA2LCAxMCwgMTEsIDQsIDUsIDcgKV0gCmhlYWQoTGl2ZXNfbG9zdCkKYGBgCiMjIFVuZGVyc3RhbmQgCgoqKnN0ZXAgMi4xKiogQ2hlY2sgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YXNldCBieSB1c2luZyB0aGUgc3RyKCkgZnVuY3Rpb24gYW5kIHRoZSBhdHRyaWJ1dGVzLiBUaGUgcmVzdWx0IHNob3duOiAgCgoqIFRoZSBkYXRhc2V0IGlzIGEgZGF0YWZyYW1lIGNvbnRhaW5zIDEsMjY0IE9ic2VydmF0aW9ucyBhbmQgMTEgdmFyaWFibGVzLgoqIFRoZSB2YXJpYWJsZSAiQUNDSURFTlRfTk8iLCAiREFZX09GX1dFRUsiLCAiQUNDSURFTlRfVFlQRSIsICJST0FEX1VTRVIiLCAiU1BFRURfWk9ORSIsICJMR0FfTkFNRSIsICJBTENPSE9MX1JFTEFURUQiLCAiREFURSIgYW5kICJUSU1FIiBhcmUgYSBjaGFyYWN0ZXIgdHlwZS4gSSB3aWxsIGxlYXZlIHRoZSAiREFURSIgYW5kICJUSU1FIiB2YXJpYWJsZSBhcyB0aGV5IGFyZSBhcyBJIGFtIGdvaW5nIHRvIGNvbWJpbmUgdGhlbSB0b2dldGhlciBhbmQgY29udmVydCB0aGVtIGludG8gUE9TSVhjdCBjbGFzcyBpbiBTdGVwIDQuCiogVGhlIHZhcmlhYmxlICJBR0UiIGFuZCAiTk9fT0ZfVkVISUNMRVMiIGFyZSBpbnRlZ2VyIHR5cGUuIEkgd2lsbCBsZWF2ZSB0aGUgIk5PX09GX1ZFSElDTEVTIiBhcyB0aGV5IGFyZSBiZWNhdXNlIGludGVnZXIgaXMgdGhlIHJpZ2h0IHR5cGUgZm9yIGl0LgoKKipzdGVwIDIuMioqIFZhcmlhYmxlcyBjb252ZXJzaW9uICAKCiogQ29udmVydCB0aGUgdmFyaWFibGVzICJBQ0NJREVOVF9OTyIsICJBQ0NJREVOVF9UWVBFIiBhbmQgIlJPQURfVVNFUiIgdG8gYmUgZmFjdG9yIHVzaW5nIHRoZSBhcy5mYWN0b3IoKSBmdW5jdGlvbi4KKiBDb252ZXJ0IHRoZSB2YXJpYWJsZXMgIkRBWV9PRl9XRUVLIiwgIkFHRSIgYW5kICJTUEVFRF9aT05FIiBpbnRvIGFuIG9yZGVyZWQgZmFjdG9yIHZhcmlhbGJsZXMgYnkgdXNpbmcgdGhlIGZhY3RvcigpIGZ1bmN0aW9uIGFuZCBvcmRlcmluZyB0aGVtIHVzaW5nIHRoZSAibGV2ZWxzIiBhcmd1bWVudCB3aXRoIHRoZSBjKCkgZnVuY3Rpb24gYW5kIGFzc2lnbiBhcmd1bWVudCAib3JkZXJlZCIgdG8gYmUgIlRSVUUiIHNvIHRoZSBsZXZlbHMgYXJlIHJlZ2FyZGVkIGFzIG9yZGVyZWQuCiogQ29udmVydCB0aGUgdmFyaWFibGUgIkFMQ09IT0xfUkVMQVRFRCIgaW50byBsb2dpY2FsIHR5cGUgYnk6CiAgKyBSZXBsYWNlIHZhbHVlcyAiTm8iIHdpdGggIkZBTFNFIiBhbmQgdmFsdWVzICJZZXMiIHdpdGggIlRSVUUiLgogICsgVXNlIHRoZSBhcy5sb2dpY2FsKCkgZnVuY3Rpb24gdG8gY29udmVydCB0aGVtIGludG8gbG9naWNhbCB0eXBlLgoqIENoZWNrIHRoZSByZXN1bHQgb2YgdGhlIGNvbnZlcnNpb24gYnkgdXNpbmcgdGhlIHN0cigpIGZ1bmN0aW9uLiBUaGUgcmVzdWx0IHNob3duIHRoYXQ6ICAKICArIFRoZSAiQUNDSURFTlRfTk8iLCAiQUNDSURFTlRfVFlQRSIgYW5kICJST0FEX1VTRVIiIGhhdmUgY29udmVydGVkIGludG8gZmFjdG9yIHR5cGUuCiAgKyBUaGUgIkRBWV9PRl9XRUVLIiwgIkFHRSIgYW5kICJTUEVFRF9aT05FIiBoYXZlIGNvbnZlcnRlZCBpbnRvIG9yZGVyZWQgZmFjdG9yIHR5cGUKICArIFRoZSAiQUxDT0hPTF9SRUxBVEVEIiBoYXZlIGNvbnZlcnRlZCBpbnRvIGxvZ2ljYWwgdHlwZS4KCgpgYGB7cn0KTGl2ZXNfbG9zdCAlPiUgc3RyKCkKTGl2ZXNfbG9zdCAlPiUgYXR0cmlidXRlcygpCgpMaXZlc19sb3N0JEFDQ0lERU5UX05PICU8PiUgYXMuZmFjdG9yKCkKTGl2ZXNfbG9zdCRBQ0NJREVOVF9UWVBFICU8PiUgYXMuZmFjdG9yKCkKTGl2ZXNfbG9zdCRST0FEX1VTRVIgJTw+JSBhcy5mYWN0b3IoKQoKTGl2ZXNfbG9zdCREQVlfT0ZfV0VFSyAlPD4lIGZhY3RvcihsZXZlbHM9YygiU3VuZGF5IiwiTW9uZGF5IiwiVHVlc2RheSIsIldlZG5lc2RheSIsIlRodXJzZGF5IiwiRnJpZGF5IiwiU2F0dXJkYXkiKSwgb3JkZXJlZD1UUlVFKQpMaXZlc19sb3N0JEFHRSAlPD4lIGZhY3RvcihsZXZlbHMgPSBjKDE6OTkpLCBvcmRlcmVkID0gVFJVRSkKTGl2ZXNfbG9zdCRTUEVFRF9aT05FICU8PiUgZmFjdG9yKGxldmVscyA9IGMoIkNhbXBpbmcgZ3JvdW5kcyBvciBvZmYgcm9hZCIsICI0MCBrbS9ociIsIjUwIGttL2hyIiwgIjYwIGttL2hyIiwgIjcwIGttL2hyIiwiNzUga20vaHIiLCAiODAga20vaHIiLCAiOTAga20vaHIiLCAiMTAwIGttL2hyIiwgIjExMCBrbS9ociIsICJPdGhlciBzcGVlZCBsaW1pdCIpLCBvcmRlcmVkID0gVFJVRSkKCkxpdmVzX2xvc3QkQUxDT0hPTF9SRUxBVEVEPC1nc3ViKCJObyIsICJGQUxTRSIsIExpdmVzX2xvc3QkQUxDT0hPTF9SRUxBVEVEKQpMaXZlc19sb3N0JEFMQ09IT0xfUkVMQVRFRDwtZ3N1YigiWWVzIiwgIlRSVUUiLCBMaXZlc19sb3N0JEFMQ09IT0xfUkVMQVRFRCkKTGl2ZXNfbG9zdCRBTENPSE9MX1JFTEFURUQgJTw+JSBhcy5sb2dpY2FsKCkgCgpMaXZlc19sb3N0ICU+JSBzdHIoKQoKYGBgCgoKIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIAoKKipTdGVwIDMqKiBEZXRlcm1pbmUgd2hldGhlciB0aGUgZGF0YXNldCBpcyB0aWR5IG9yIHVudGlkeS4gCgpUbyBkZXRlcm1pbmUgaWYgbXkgZGF0YXNldCBpcyB0aWR5IG9yIG5vdCwgSSB1c2VkIHRoZSB0aHJlZSBpbnRlcnJlbGF0ZWQgcnVsZXM6CgoxLiBFYWNoIHZhcmlhYmxlIG11c3QgaGF2ZSBpdHMgb3duIGNvbHVtbi4KMi4gRWFjaCBvYnNlcnZhdGlvbiBtdXN0IGhhdmUgaXRzIG93biByb3cuCjMuIEVhY2ggdmFsdWUgbXVzdCBoYXZlIGl0cyBvd24gY2VsbC4KKEhhZGxleSBXaWNraGFtIGFuZCBHYXJyZXR0IEdyb2xlbXVuZCAyMDE2KQoKRnJvbSBteSBwb2ludCBvZiB2aWV3IG15IGRhdGFzZXQgIkxpdmVfbG9zdCIgaXMgYWxyZWFkeSBpbiBhIHRpZHkgZm9ybWF0IGFzIGVhY2ggdmFyaWFibGUgaGF2ZSBnb3QgaXRzIG93biBjb2x1bW4sIGVhY2ggT2JzZXJ2YXRpb24gaGF2ZSBnb3QgaXRzIG93biByb3cgYW5kIEVhY2ggdmFsdWUgaGF2ZSBnb3QgaXRzIG93biBjZWxsLgoKYGBge3J9CgpgYGAKCgojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIElJIAoKKipTdGVwIDQuMSoqIFNoYXBlIHRoZSBmb3JtYXQgb2YgdGhlIHZhbHVlcyBmcm9tICJEQVRFIiB2YXJpYWJsZSBieSBrZWVwaW5nIHRoZSB2YWx1ZSBmcm9tIHRoZSBmaXJzdCBwb3NpdGlvbiB0byAxMHRoIGFuZCBkcm9wIHRoZSByZXN0IGJ5IHVzaW5nIHRoZSBzdWJzdHIoKSBmdW5jdGlvbi4gQXNzaWduIHRoZSAic3RhcnQiIGFyZ3VtZW50IHdpdGggIjEiIGFuZCB0aGUgInN0b3AiIGFyZ3VtZW50IHdpdGggIjEwIiB0byBpbmRpY2F0ZSB0aGUgcmFuZ2Ugb2YgcG9zaXRpb24gdGhhdCBJIHdhbnQgdG8ga2VlcC4gIAoKKipTdGVwIDQuMioqIFNoYXBlICJUSU1FIiB2YXJpYWJsZSBpbnRvIHRoZSBlYXN5IGZvcm1hdCBpbiBwcmVwYXJpbmcgdG8gdXNlIHRoZSB5bWRfaG1zKCkgZnVuY3Rpb24gaW4gc3RlcCA0LjQgYnkgdXNpbmcgdGhlIHN0cl9yZXBsYWNlX2FsbCgpIGZ1bmN0aW9uIGZyb20gc3RyaW5nciBwYWNrYWdlIHRvIHJlcGxhY2UgIi46IiB3aXRoICI6IiBhbmQgcmVwbGFjZSAiLjAwIiB3aXRoICIwLjAwIi4gVXNlIHRoZSBoZWFkKCkgZnVuY3Rpb24gdG8gcmV2aWV3IHRoZSByZXN1bHQuIAoKKipTdGVwIDQuMyoqIENyZWF0ZSB0aGUgbmV3IHZhcmlhYmxlICJEQVRFIiBieSBjb21iaW5pbmcgdmFyaWFibGUgIkRBVEUiIGFuZCAiVElNRSIgdG9nZXRoZXIuICAKCiogVXNpbmcgdGhlIG11dGF0ZSgpIGZ1bmN0aW9uIGZyb20gdGhlIGRwbHlyIHBhY2thZ2UgdG8gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIGNhbGxlZCAiREFURSIuCiogVXNpbmcgdGhlIHBhc3RlKCkgZnVuY3Rpb24gdG86ICAKICArIFB1dCB0aGUgdmFsdWUgZnJvbSB0aGUgb2xkIHZhcmlhYmxlICJEQVRFIiBhbmQgZm9sbG93IHdpdGggdGhlIHZhbHVlcyBmcm9tIHRoZSB2YXJpYWJsZSAiVElNRSIuCiAgKyBBc3NpZ24gdGhlIGFyZ3VtZW50ICJzZXAiIHdpdGggIiAiKGJsYW5rKSB0byBpbmRpY2F0ZSB0aGUgc2VwYXJhdG9yIGJldHdlZW4gdGhlIHZhbHVlcy4gIAogIAoqKlN0ZXAgNC40KiogQ29udmVydCB0aGUgdmFyaWFibGUgIkRBVEUiIGludG8gUE9TSVhjdCBjbGFzcyB1c2luZyB5bWRfaG1zKCkgZnVuY3Rpb24gZnJvbSAibHVicmlkYXRlIiBwYWNrYWdlLgoKKipTdGVwIDQuNSoqIERyb3AgdmFyaWFibGVzICJUSU1FIiBieSB1c2luZyB0aGUgc2VsZWN0KCkgZnVuY3Rpb24gd2l0aCB0aGUgYygpIGZ1bmN0aW9uIGFuZCB0aGUgIi0iIG9wZXJhdG9yIHRvIGFzc2lnbiB0aGUgY29sdW1uIG5hbWUgIlRJTUUiIHRoYXQgSSB3YW50IHRvIGRyb3AuIFVzZSB0aGUgc3RyKCkgZnVuY3Rpb24gdG8gY2hlY2sgdGhlIHJlc3VsdC4gVGhlIHJlc3VsdCBzaG93biB0aGF0OiAgCgoKKiBUaGUgIkRBVEUiIHZhcmlhYmxlIGhhdmUgY29udmVydCBpbnRvIGNsYXNzIFBPU0lYY3Qgd2hpY2ggcmVwcmVzZW50aW5nIGNhbGVuZGFyIGRhdGVzIGFuZCB0aW1lcy4KKiBUaGUgIkRBVEUiIHZhcmlhYmxlIGZvcm1hdCBjaGFuZ2UgaW50byAieXl5eS1tbS1kZCBoaDptbTpzcyIuCiogVGhlICJUSU1FIiB2YXJpYWJsZSBoYXMgZHJvcHBlZC4KCgpgYGB7cn0KCkxpdmVzX2xvc3QkREFURSAlPD4lIHN1YnN0cihzdGFydCA9IDEsIHN0b3AgPSAxMCkgCgpMaXZlc19sb3N0JFRJTUUgJTw+JSBzdHJfcmVwbGFjZV9hbGwoIi46IiwgIjoiKQpMaXZlc19sb3N0JFRJTUUgJTw+JSBzdHJfcmVwbGFjZV9hbGwoIi4wMCIsICIwOjAwIikKaGVhZChMaXZlc19sb3N0KQoKTGl2ZXNfbG9zdCAlPD4lIG11dGF0ZSAoREFURSA9IHBhc3RlKERBVEUsIFRJTUUsIHNlcCA9ICIgIikpCkxpdmVzX2xvc3QkREFURSAlPD4lIHltZF9obXMoKQpMaXZlc19sb3N0ICU8PiUgc2VsZWN0KC1USU1FKSAKc3RyKExpdmVzX2xvc3QpCgpgYGAKCiMjCVNjYW4gSSAKCioqU3RlcCA1LjEqKiBDaGVjayB0aGUgYW1vdW50IG9mIE5BIGluIGVhY2ggY29sdW1uIGJ5IHVzaW5nIHRoZSBpcy5uYSgpIGZ1bmN0aW9uIHRvIGNoZWNrIGZvciB0aGUgTkEgdmFsdWUgYW5kIHRoZSBjb2xTdW0oKSBmdW5jdGlvbiB0byBzdW0gdXAgdGhlIHRvdGFsIG9mIE5BIHZhbHVlIGluIGVhY2ggY29sdW1uLiBUaGUgcmVzdWx0IGZvdW5kOiAgCgogICogMiBvZiB0aGUgdmFsdWVzIGluICJEQVlfT0ZfV0VFSyIgdmFyaWFibGUgYXJlIE5BLiAgCiAgKiA1IG9mIHRoZSB2YWx1ZXMgaW4gIkFHRSIgdmFyaWFibGUgYXJlIE5BLiAgCiAgKiAxNiBvZiB0aGUgdmFsdWVzIGluICJTUEVFRF9aT05FIiBhcmUgTkEuCgoqKlN0ZXAgNS4yKiogUmVwbGFjZSB0aGUgbWlzc2luZyB2YWx1ZXMgaW4gIkRBWV9PRl9XRUVLIiB2YXJpYWJsZSA6ICAKCiogVGhlIDIgb2YgdGhlIHZhbHVlIGluICJEQVlfT0ZfV0VFSyIgdmFyaWFibGUgYXJlIE5BIGJ1dCB0aGUgIkRBVEUiIHZhcmlhYmxlIGhhdmUgbm8gTkEgdmFsdWUgbWVhbnMgSSBjYW4gcmVwbGFjZSB0aGUgbWlzc2luZyB2YWx1ZSBieSB1c2luZyB0aGUgd2Vla2RheXMoKSBmdW5jdGlvbiB0byBmaW5kIHRoZSBkYXkgb2YgdGhlIHdlZWsgYW5kIGFzc2lnbiB0aGUgdmFsdWUgYmFjayB0byBjb2x1bW4gIkRBWV9PRl9XRUVLIi4gCiogQnkgZG9pbmcgdGhpcyByZXBsYWNlbWVudCBjYXVzaW5nIHRoZSAiREFZX09GX1dFRUsiIHZhcmlhYmxlIHRvIHR1cm4gaW50byBhIGNoYXJhY3RlciB0eXBlIHNvIEkgY29udmVydCBpdCBiYWNrIGluIHRvIGFuIG9yZGVyZWQgZmFjdG9yIGJ5IHVzaW5nIHRoZSBzYW1lIG1ldGhvZCBhcyB0aGUgc3RlcCAyLjIuCiogQ2hlY2sgdGhlIE5BIHZhbHVlcyBpbiB0aGUgY29sdW1uICJEQVlfT0ZfV0VFSyIgdG8gbWFrZSBzdXJlIHRoZXJlIGlzIG5vbmUgTkEgdmFsdWUgbGVmdCBpbiAiREFZX09GX1dFRUsiIGNvbHVtbiBieSB1c2luZyB0aGUgaXMubmEoKSBmdW5jdGlvbiB0b2dldGhlciB3aXRoIHRoZSB3aGljaCgpIGZ1bmN0aW9uIGFuZCB0aGUgbGVuZ3RoKCkgZnVuY3Rpb24uIFRoZSByZXN1bHQgc2hvd24gdGhhdCB0aGVyZSBpcyBubyBOQSB2YWx1ZSBpbiAiREFZX09GX1dFRUsiIHZhcmlhYmxlLgoKKipTdGVwIDUuMioqIENoZWNrIHRoZSBhbW91bnQgb2YgTkEgaW4gdGhlIHdob2xlIGRhdGFzZXQgYnk6ICAKCiogVXNlIHRoZSBpcy5uYSgpIGZ1bmN0aW9uIHRvIGNoZWNrIGZvciB0aGUgTkEgdmFsdWUuCiogVXNlIHRoZSB3aGljaCgpIGZ1bmN0aW9uIHRvIGlkZW50aWZ5IHRoZSBwb3NpdGlvbiBvZiBOQSB2YWx1ZS4KKiBVc2UgbGVuZ3RoKCkgZnVuY3Rpb24gdG8gZ2V0IHRoZSBudW1iZXIgb2YgdGhlIE5BIHZhbHVlLgoqIFVzaW5nIHBpcGVzIG9wZXJhdG9yIGJldHdlZW4gdGhlIGZ1bmN0aW9uIHRvIHBhc3MgdGhlIHJlc3VsdCBvZiBvbmUgZnVuY3Rpb24gdG8gdGhlIG90aGVyIG9uZSBpbiBzZXF1ZW5jZS4KCioqU3RlcCA1LjMqKiBTdWJzZXR0aW5nIHRoZSBvYnNlcnZhdGlvbnMgd2l0aCBOQSB2YWx1ZXMgaW50byB0aGUgZGF0YWZyYW1lIGNhbGxlZCAiTkFfcmVsYXRpb25fY2hlY2siIHRvIGJlIGFibGUgdG8gaGF2ZSBhIGJldHRlciBsb29rIGZvciB0aGUgcmVsYXRpb24gb2YgdGhlIG1pc3NpbmcgdmFsdWUgd2l0aCBvdGhlciB2YXJpYWJsZS4gIAoKKiBVc2UgdGhlIGlzLm5hKCkgZnVuY3Rpb24gdG8gZmluZCB0aGUgTkEgdmFsdWUgaW4gdGhlIGRhdGFzZXQuCiogVXNlIHRoZSByb3dTdW1zKCkgZnVuY3Rpb24gdG8gc3VtIHVwIHRoZSBtaXNzaW5nIHZhbHVlIGluIGVhY2ggb2JzZXJ2YXRpb24uCiogU3Vic2V0IGp1c3QgdGhlIG9ic2VydmF0aW9uIHdpdGggbWlzc2luZyB2YWx1ZXMgKD4wKSBhbmQgcHV0IGluIHRoZSBkYXRhZnJhbWUgIk5BX3JlbGF0aW9uX2NoZWNrIi4KCioqU3RlcCA1LjQqKgpBZnRlciBjaGVja2luZywgSSBjb3VsZCBub3QgZmluZCBhbnkgcmVsYXRpb24gdG8gdGhlIG90aGVyIHZhcmlhYmxlIG9yIGJldHdlZW4gdGhlIG9ic2VydmF0aW9uLiBUaGUgbWlzc2luZyB2YWx1ZSBsb29rIGxpa2UgTWlzc2luZyBjb21wbGV0ZWx5IGF0IHJhbmRvbSAoTUNBUikgZm9yIG1lLiAiQSBydWxlIG9mIHRodW1iIHNheXMgdGhhdCB3aGVuIHRoZSBkYXRhIGluY2x1ZGUgbGVzcyB0aGFuIDUlIHJhbmRvbSBtaXNzaW5nbmVzcyB3aGljaCBkb2VzIG5vdCBkZXBlbmQgb24gb2JzZXJ2ZWQgb3IgdW5vYnNlcnZlZCB2YWx1ZXMsIGNvbXBsZXRlIGNhc2UgYW5hbHlzaXMgbWF5IGJlIGFuIGFjY2VwdGFibGUgYXBwcm9hY2gsIHRvbyIoSGFubmFoIFJvb3MsIDIwMjEpLiAKCiogQ2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIHRoZSBvYnNlcnZhdGlvbiB3aXRoIG1pc3NpbmcgdmFsdWUgYnkgZGV2aWRpbmcgdGhlIG51bWJlciBvZiB0aGUgb2JzZXJ2YXRpb25zIHdpdGggdGhlIG1pc3NpbmcgdmFsdWVzIGJ5IHRoZSBudW1iZXIgb2YgYWxsIHRoZSBvYnNlcnZhdGlvbnMgaW4gdGhlIGRhdGFzZXQgYW5kIHRoZW4gbXVsdGlwbHkgYnkgMTAwLiBUaGUgcmVzdWx0IHNob3duIHRoYXQgdGhlIG9ic2VydmF0aW9ucyB3aXRoIHRoZSBtaXNzaW5nIHZhbHVlcyBpcyBqdXN0IDEuNjYlIG9mIGFsbCBvYnNlcnZhdGlvbnMuIEJlY2F1c2Ugb2YgdGhlIGluZm9ybWF0aW9uIGFib3ZlLCBJIGNob29zZSB0byBleGNsdWRlIGFsbCB0aGUgb2JzZXJ2YXRpb24gd2l0aCB0aGUgbWlzc2luZyB2YWx1ZS4KCioqU3RlcCA1LjUqKiBFeGNsdWRlIHRoZSBvYnNlcnZhdGlvbnMgd2l0aCBtaXNzaW5nIHZhbHVlczogIAoKKiBVc2UgdGhlIGNvbXBsZXRlLmNhc2VzKCkgZnVuY3Rpb24gdG8gaWRlbnRpZnkgY29tcGxldGUgcm93IG9mIHRoZSBkYXRhZnJhbWUgYW5kIHRoZSBzcXVhcmUgYnJhY2tldHMgdG8gc3Vic2V0IHRob3NlIHJvdyB3aXRoIGFsbCBjb2x1bW4uIEFzc2lnbiB0aGUgcmVzdWx0IHRvIGRhdGFmcmFtZSBjYWxsZWQgIkxpdmVzX2xvc3QyIi4KKiBDaGVjayBpZiB0aGVyZSBpcyBhbnkgTkEgdmFsdWUgbGVmdCBpbiB0aGUgZGF0YWZyYW1lIHdpdGggdGhlIHNhbWUgZnVuY3Rpb24gYXMgc3RlcCA1LjIuCiogVGhlIHJlc3VsdCBzaG93biB0aGF0IHRoZXJlIGlzIG5vIE5BIHZhbHVlIGxlZnQgaW4gdGhlIGRhdGFmcmFtZSAiTGl2ZXNfbG9zdDIiLgoKYGBge3J9Cgppcy5uYShMaXZlc19sb3N0KSAlPiUgY29sU3VtcygpCgpMaXZlc19sb3N0JERBWV9PRl9XRUVLIDwtIHdlZWtkYXlzKExpdmVzX2xvc3QkREFURSkKTGl2ZXNfbG9zdCREQVlfT0ZfV0VFSyAlPD4lIGZhY3RvcihsZXZlbHM9YygiU3VuZGF5IiwiTW9uZGF5IiwiVHVlc2RheSIsIldlZG5lc2RheSIsIlRodXJzZGF5IiwiRnJpZGF5IiwiU2F0dXJkYXkiKSwgb3JkZXJlZD1UUlVFKQppcy5uYShMaXZlc19sb3N0JERBWV9PRl9XRUVLKSAlPiUgd2hpY2goKSAlPiUgbGVuZ3RoKCkKCmlzLm5hKExpdmVzX2xvc3QpICU+JSB3aGljaCgpICU+JSBsZW5ndGgoKQpOQV9yZWxhdGlvbl9jaGVjayA8LSBMaXZlc19sb3N0W3Jvd1N1bXMoaXMubmEoTGl2ZXNfbG9zdCkpID4gMCwgXQpOQV9yZWxhdGlvbl9jaGVjayAlPiUgaGVhZAoKKChOQV9yZWxhdGlvbl9jaGVjayAlPiUgbnJvdykvKExpdmVzX2xvc3QlPiUgbnJvdygpKSkgKiAxMDAKTGl2ZXNfbG9zdDI8LUxpdmVzX2xvc3RbY29tcGxldGUuY2FzZXMoTGl2ZXNfbG9zdCksIF0KaXMubmEoTGl2ZXNfbG9zdDIpJT4lIHdoaWNoKCkgJT4lIGxlbmd0aCgpCmBgYAoKIyMJU2NhbiBJSQoKSSBhbSB1c2luZyB0aGUgdW5pdmFyaWF0ZSBvdXRsaWVyIGRldGVjdGlvbiBtZXRob2Qgd2l0aCBOb24tcGFyYW1ldHJpYyBtZXRob2QgKEJveCBwbG90KSwgdG8gYXBwcm9hY2ggdGhlICJOT19PRl9WRUhJQ0xFUyIgdmFyaWFibGUgYmVjYXVzZSBJIGFtIGp1c3QgZm9jdXNpbmcgb24gb25lIHBhcnRpY3VsYXIgdmFyaWFibGUgYW5kIEkgYXNzdW1lIHRoYXQgdGhlIGRhdGEgaXMgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGUuIEZvciB0aGUgaGFuZGxpbmcgcHJvY2VzcywgSSBjaG9vc2UgdG8gZGVsZXRlIGFsbCB0aGUgb3V0bGllcnMgYmVjYXVzZSBpbXB1dGluZyB3aXRoIG1lYW4gZG9lcyBub3QgbWFrZXNlbnNlIGFzIHRoZSB2YWx1ZSBpbmRpY2F0ZSBudW1iZXIgb2YgdmVoaWNsZXMgd2hpY2ggc2hvdWxkIGJlIGludGVnZXIgKG1lYW4gaXMgMS42NTEgd2hpY2ggaXMgbm90IGludGVnZXIpIGFuZCBpbXB1dGluZyB3aXRoIG1lZGlhbiBpcyBub3QgZ29pbmcgdG8gc3VpdCB3aXRoIHRoZSBvdGhlciB2YXJpYWJsZXMgaW4gdGhlIG9ic2VydmF0aW9uLiBUaGUgcHJvY2Vzc2VzIGlzIGFzIGZvbGxvd2luZzogIAoKKipTdGVwIDYuMSoqIENyZWF0ZSBCb3hwbG90IG9mIHRoZSB2YXJpYWJsZSAiTk9fT0ZfVkVISUNMRVMiIHRvIHNlZSB0aGUgYmlnIHBpY3R1cmUgb2YgdGhlIGRhdGEgYW5kIHRoZSBvdXRsaWVycyBieSB1c2luZyB0aGUgYm94cGxvdCgpIGZ1bmN0aW9uLiAgCgoqIEFzc2lnbiB0aGUgYXJndW1lbnQgIm1haW4iIHdpdGggdGhlIHRpdGxlIG9mIHRoZSBib3hwbG90LCBpbiB0aGlzIGNhc2UgIkJveCBQbG90IG9mIE51bWJlciBvZiB2ZWhpY2xlcyIuICAKKiBBc3NpZ24gdGhlIGFyZ3VtZW50ICJ5bGFiIiB3aXRoIHRoZSB5LWF4aXMgYW5ub3RhdGlvbiwgaW4gdGhpcyBjYXNlICJOdW1iZXIgb2YgdmVoaWNsZXMiLgoqIEFzc2lnbiB0aGUgYXJndW1lbnQgImNvbCIgd2l0aCB0aGUgY29sb3IgcHJlZmVyZW5jZSBmb3IgdGhlIGJvZGllcyBvZiB0aGUgYm94IHBsb3QsIGluIHRoaXMgY2FzZSBJIGNob29zZSAib3JhbmdlIi4KCioqU3RlcCA2LjIqKiBBcHBseSB0aGUgdW5pdmFyaWF0ZSBvdXRsaWVyIGRldGVjdGlvbiBieTogIAoKKiBHZXR0aW5nIHRoZSBRMSBhbmQgUTMgdmFsdWVzIGJ5IHVzaW5nIHRoZSBzdW1tYXJ5KCkgZnVuY3Rpb24uIFRoZSByZXN1bHQgc2hvd246ICAKICArIFRoZSBmaXJzdCBwb3NpdGlvbjogbWluaW11bSB2YWx1ZSA9IDEuMDAuCiAgKyBUaGUgc2Vjb25kIHBvc2l0aW9uOiBRMSA9IDEuMDAuCiAgKyBUaGUgdGhpcmQgcG9zaXRpb246IG1lZGlhbiA9IDEuMDAgCiAgKyBUaGUgNHRoIHBvc2l0aW9uOiBtZWFuID0gMS42NQogICsgVGhlIDV0aCBwb3NpdGlvbjogUTMgPSAyLjAwCiAgKyBUaGUgNnRoIHBvc2l0aW9uOiBtYXhpbXVtIHZhbHVlID0gMTEuMDAKKiBFeHRyYWN0IHRoZSBRMSB2YWx1ZSBieSB1c2luZyB0aGUgZG91YmxlIHNxdWFyZSBicmFja2V0cyB0byBzdWJzZXQgdGhlIHZhbHVlIGZyb20gdGhlIDJuZCBwb3NpdGlvbiBvZiB0aGUgc3VtbWFyeSgpIGZ1bmN0aW9uJ3MgcmVzdWx0IGFuZCBhc3NpZ24gdG8gInExIi4KKiBFeHRyYWN0IHRoZSBRMyB2YWx1ZSBieSB1c2luZyB0aGUgZG91YmxlIHNxdWFyZSBicmFja2V0cyB0byBzdWJzZXQgdGhlIHZhbHVlIGZyb20gdGhlIDV0aCBwb3NpdGlvbiBvZiB0aGUgc3VtbWFyeSgpIGZ1bmN0aW9uJ3MgcmVzdWx0IGFuZCBhc3NpZ24gdG8gInEzIi4KKiBDYWxjdWxhdGUgdGhlIGludGVycXVhcnRpbGUgcmFuZ2UoSVFSKSBieSBzdWJ0cmFjdCBxMSB2YWx1ZSBmcm9tIHEzIHZhbHVlIGFuZCBhc3NpZ24gdG8gImlxciIuCiogQ2FsY3VsYXRlIHRoZSB1cHBlciBmZW5jZSBieSBhZGQgdGhlIDEuNSB0aW1lcyBvZiBpcXIgdG8gcTMgYW5kIGFzc2lnbiB0aGUgdmFsdWUgdG8gInVwcGVyX2ZlbmNlIi4KKiBDYWxjdWxhdGUgdGhlIGxvd2VyIGZlbmNlIGJ5IHN1YnRyYWN0IHRoZSAxLjUgdGltZXMgb2YgaXFyIGZyb20gcTEgYW5kIGFzc2lnbiB0aGUgdmFsdWUgdG8gImxvd2VyX2ZlbmNlIi4KKiBFeHRyYWN0IHRoZSBwb3NpdGlvbiBvZiB0aGUgb3V0bGllcnMgYnkgdXNpbmcgdGhlIHdoaWNoKCkgZnVuY3Rpb24sIHBpY2tpbmcganVzdCB0aGUgdmFsdWVzIHRoYXQgaXMgbW9yZSB0aGFuIHRoZSAidXBwZXJfZmVuY2UiIHZhbHVlIGFuZCBsZXNzIHRoYW4gdGhlICJsb3dlcl9mZW5jZSIgdmFsdWUuIEFzc2lnbiB0aGUgb3V0bGllcnMgb3ZlciB0aGUgInVwcGVyX2ZlbmNlIiB2YWx1ZSBpbnRvICJ1cF9vdXRsaWVycyIgYW5kIGFzc2lnbiB0aGUgb3V0bGllcnMgYmVsb3cgdGhlICJsb3dlcl9mZW5jZSIgdmFsdWUgaW50byAibG93X291dGxpZXJzIi4KKiBTdW0gdXAgdGhlIGFtb3VudCBvZiBvdXRsaWVycyBpbiBlYWNoIG9mIHRoZSBvYmplY3QgKHRoZSAidXBfb3V0bGllcnMiIGFuZCB0aGUgImxvd19vdXRsaWVycyIpIGJ5IHVzaW5nIHRoZSAibGVuZ3RoIiBmdW5jdGlvbi4gdGhlIHJlc3VsdCBzaG93biB0aGF0IHRoZSBhbW91bnQgb2YgdGhlIG91dGxpZXIgYWJvdmUgdGhlICJ1cHBlcl9mZW5jZSIgdmFsdWUgaXMgMzcgb3V0bGllcnMgYnV0IHRoZXJlIGlzIG5vbmUgb3V0bGllciBiZWxvdyB0aGUgImxvd2VyX2ZlbmNlIgoKKipTdGVwIDYuMyoqIEhhbmRsZSB0aGUgb3V0bGllcnMgYnkgZXhjbHVkZSB0aGVtIHVzaW5nIHRoZSBzcXVyZSBicmFja2V0IGFuZCAiLSIgb3BlcmF0b3IgdG8gZHJvcCB0aGUgInVwX291dGxpZXIiIHBvc2l0aW9ucyBmcm9tIHRoZSAiTGl2ZXNfbG9zdDIkTk9fT0ZfVkVISUNMRVMiIGFuZCBhc3NpZ24gdGhlIHJlc3VsdCBpbnRvIHRoZSAiVmVoaWNsZV9jbGVhbiIgb2JqZWN0LgoKKiBDaGVjayB0aGUgcmVzdWx0IGJ5IHVzaW5nIHRoZSBib3hwbG90KCkgZnVuY3Rpb24gYW5kIGFzc2lnbiB0aGUgbGlzdCBpbnRvICJicCIgYW5kIHRoZW4gdXNlIHRoZSBsZW5ndGgoKSBmdW5jdGlvbiB0byBzdW0gdXAgdGhlIGFtb3VudCBvZiB0aGUgb3V0bGllciBpbiB0aGUgIm91dCIgZWxlbWVudCBpbiB0aGUgImJwIiBsaXN0LiBUaGUgcmVzdWx0IHNob3dtIHRoYXQgdGhlcmUgaXMgbm9uIG91dGxpZXIgbGVmdCBpbiB0aGUgb2JqZWN0ICJWZWhpY2FsX2NsZWFuIi4KCmBgYHtyfQoKTGl2ZXNfbG9zdDIkTk9fT0ZfVkVISUNMRVMgJT4lICAKICBib3hwbG90KG1haW4gPSAiQm94IFBsb3Qgb2YgTnVtYmVyIG9mIHZlaGljbGUiLCB5bGFiID0gIk51bWJlciBvZiB2ZWhpY2xlIiwgY29sID0gIm9yYW5nZSIpIAoKc3VtbWFyeShMaXZlc19sb3N0MiROT19PRl9WRUhJQ0xFUykKCnExIDwtIHN1bW1hcnkoTGl2ZXNfbG9zdDIkTk9fT0ZfVkVISUNMRVMpW1syXV0KcTMgPC0gc3VtbWFyeShMaXZlc19sb3N0MiROT19PRl9WRUhJQ0xFUylbWzVdXQppcXIgPC0gcTMgLSBxMQoKbG93ZXJfZmVuY2UgPC0gcTEgLSAoMS41ICogaXFyKQp1cHBlcl9mZW5jZSA8LSBxMyArICgxLjUgKiBpcXIpCgp1cF9vdXRsaWVycyA8LSB3aGljaChMaXZlc19sb3N0MiROT19PRl9WRUhJQ0xFUyA+IHVwcGVyX2ZlbmNlKQpsb3dfb3V0bGllcnMgPC0gd2hpY2goTGl2ZXNfbG9zdDIkTk9fT0ZfVkVISUNMRVMgPCBsb3dlcl9mZW5jZSkKCmxvd19vdXRsaWVycyAlPiUgbGVuZ3RoKCkKdXBfb3V0bGllcnMgJT4lIGxlbmd0aCgpCgpWZWhpY2xlX2NsZWFuPC0gTGl2ZXNfbG9zdDIkTk9fT0ZfVkVISUNMRVNbLXVwX291dGxpZXJzXQoKYnA8LSBib3hwbG90KFZlaGljbGVfY2xlYW4pCmJwJG91dCAlPiUgbGVuZ3RoKCkKCmBgYAoKIyMJVHJhbnNmb3JtIAoKKipTdGVwIDcqKiBUcmFuc2Zvcm1hdGlvbiA6IEkgY2hvb3NlIHRvIHRyYW5zZm9ybSB0aGUgIkFHRSIgdmFyaWFibGUuIEkgY29udmVydCB0aGUgdmFyaWFibGUgZnJvbSBvcmRlcmVkIGZhY3RvciBpbnRvIG51bWVyaWMgYnkgdXNpbmcgdGhlIGFzLm51bWVyaWMoKSBmdW5jdGlvbiB0byBiZSBhYmxlIHRvIHBsb3QgdGhlIGdyYXBoIGFuZCB0cmFuc2Zvcm0uIFRoZW4sIGV4dHJhY3QganVzdCB0aGUgIkFHRSIgdmFyaWFibGUgaW50byAiQWdlIiBvYmplY3Qgc28gdGhpcyBwcm9jZXNzIGRvZXMgbm90IGNoYW5nZSB0aGUgdHlwZSBvZiB0aGUgdmFyaWFibGUgaW4gdGhlIGJpZyBkYXRhc2V0LgoKKipTdGVwIDcuMSoqIENyZWF0ZSBhIGhpc3RvZ3JhbSBvZiB0aGUgIkFnZSIgYnkgdXNpbmcgdGhlIGhpc3QoKSBmdW5jdGlvbi4gIAoKKiBBc3NpZ24gdGhlIGFyZ3VtZW50ICJtYWluIiB3aXRoIHRoZSB0aXRsZSBvZiB0aGUgaGlzdG9ncmFtICJIaXN0b2dyYW0gb2YgQWdlIi4KKiBBc3NpZ24gdGhlIGFyZ3VtZW50ICJ4bGFiIiB3aXRoIHRoZSB4LWF4aXMgYW5ub3RhdGlvbiAiQWdlKHllYXJzIG9sZCkiLgoqIEFzc2lnbiB0aGUgYXJndW1lbnQgImNvbCIgd2l0aCB0aGUgY29sb3IgcHJlZmVyZW5jZSBmb3IgdGhlIGJhci4gCgpUaGUgcmVzdWx0IHNob3duIHRoYXQgdGhlIGRhdGEgYXBwZWFyIHRvIGJlIHJpZ2h0LXNrZXdlZC4gCgoqKlN0ZXAgNy4yKiogVHJhbnNmb3JtIHRoZSBBZ2UgZGF0YSB0byBkZWNyZWFzZSB0aGUgc2tld25lc3MgYnkgdXNpbmcgc3F1YXJlIHJvb3QgdHJhbnNmb3JtYXRpb24gYXMgdGhlIHNxdWFyZSByb290IHRyYW5zZm9ybWF0aW9uIGlzIHVzZWQgZm9yIHJlZHVjaW5nIHJpZ2h0LXNrZXduZXNzLiAgCgoqIFByb2Nlc3MgdGhlIHNxdWFyZSByb290IHRyYW5zZm9ybWF0aW9uIGJ5IHRha2luZyB0aGUgIkFnZSIgdmFyaWFibGUgdmFsdWVzIHRvIHRoZSBwb3dlciBvZiAxLzIuCiogUmV2aWV3IHRoZSByZXN1bHRzIG9mIHRoZSBkYXRhIGJlZm9yZSBhbmQgYWZ0ZXIgdGhlIHRyYW5zZm9ybWF0aW9uIHNpZGUgYnkgc2lkZSBieSB1c2luZyB0aGUgcGFyKCkgZnVuY3Rpb24gdG8gc2V0IHRoZSBncmFwaGljYWwgcGFyYW1ldGVyIG9mIHRoZSBncmFwaC4gVXNlIHRoZSAibWZyb3ciIGFyZ3VtZW50IHRvIHBsb3QgdGhlIGdyYXBoIHRvIDIgY29sdW5tcyBpbiAxIHJvdy4gCiogVXNpbmcgdGhlIGhpc3QoKSBmdW5jdGlvbiB0byBwbG90IGJvdGggZ3JhcGguCiogVGhlIHJlc3VsdCBzaG93biB0aGF0IHRoZSBzcXVhcmUgcm9vdCB0cmFuc2Zvcm1hdGlvbiByZWR1Y2VkIHRoZSByaWdodC1za2V3bmVzcyBvZiB0aGUgIkFnZSIgZGF0YS4gCiogKipUaGUgbW9tZW50cyBwYWNrYWdlIGlzIG5vdCBpbiB0aGUgYXNzZXNzbWVudCByZXF1aXJtZW50IGJ1dCBJIGp1c3Qgd2FudCB0byBleHBsYWluIGhvdyBJIGNoZWNrZWQgdGhlIHNrZXduZXNzIG9mIHRoZSBncmFwaC4gSSB1c2UgdGhlIHNrZXduZXNzKCkgZnVuY3Rpb24gZnJvbSB0aGUgbW9tZW50cyBwYWNrYWdlIHRvIG1ha2Ugc3VyZSB0aGUgZGF0YSBpcyBsZXNzIHNrZXdlZC4gVGhlIHJlc3VsdCBzaG93biB0aGF0IHRoZSBBZ2Ugc2tld25lc3Mgd2FzIDAuMzQgdG8gdGhlIHJpZ2h0IGJ1dCBhZnRlciB0aGUgdHJhbnNmb3JtYXRpb24gdGhlIHNrZXduZXNzIHdhcyAwLjEyIHRvIHRoZSBsZWZ0IHdoaWNoIGlzIGxlc3Mgc2tld2VkLioqCiAKCmBgYHtyfQoKQWdlPC1hcy5udW1lcmljKExpdmVzX2xvc3QyJEFHRSkKCmhpc3QoQWdlLAptYWluPSJIaXN0b2dyYW0gb2YgQWdlIiwKeGxhYj0iQWdlKHllYXJzIG9sZCkiLApjb2w9ICJkYXJrc2xhdGVncmV5IikKCnBhcihtZnJvdz1jKDEsMikpCmhpc3QoQWdlLCBjb2wgPSAiZGFya3NsYXRlZ3JleSIpCnNxcnQgPC0gQWdlXigxLzIpCmhpc3Qoc3FydCwgY29sID0gImRhcmtzbGF0ZWdyYXkzIikKCmxpYnJhcnkobW9tZW50cykKc2tld25lc3MoQWdlKQpza2V3bmVzcyhzcXJ0KQoKYGBgCgoKIyMgUmVmZXJlbmNlczoKCl9DYWxjdWxhdGUgU2tld25lc3MgaW4gUl8gbi5kLiwgUHJvZ3JhbW1pbmdSLCB2aWV3ZWQgMTQgT2N0b2JlciAyMDIxLCA8aHR0cHM6Ly93d3cucHJvZ3JhbW1pbmdyLmNvbS9zdGF0aXN0aWNzL3NrZXduZXNzLz4KCkhhZGxleSBXaWNraGFtIGFuZCBHYXJyZXR0IEdyb2xlbXVuZCwgX1IgZm9yIERhdGEgU2NpZW5jZV8sIFI0RFMsIHZpZXdlZCAxOCBTZXB0ZW1iZXIgMjAyMSwgPGh0dHBzOi8vcjRkcy5oYWQuY28ubnovdGlkeS1kYXRhLmh0bWw+CgpNYW5naWFmaWNvLCBTLlMuIDIwMTYsIFN1bW1hcnkgYW5kIEFuYWx5c2lzIG9mIEV4dGVuc2lvbiBQcm9ncmFtIEV2YWx1YXRpb24gaW4gUiwgdmlld2VkIDE0IE9jdG9iZXIgMjAyMSwgPGh0dHBzOi8vcmNvbXBhbmlvbi5vcmcvaGFuZGJvb2svSV8xMi5odG1sPgoKVmljdG9yaWEgRGVwYXJ0bWVudCBvZiBUcmFuc3BvcnQgMjAyMSwgX0NyYXNoZXNfTGFzdF9GaXZlX1llYXJzIC0gT3BlbiBEYXRhXywgZGF0YSBmaWxlLCBWaWN0b3JpYSBEZXBhcnRtZW50IG9mIFRyYW5zcG9ydCBPcGVuIERhdGEgSHViLCB2aWV3ZWQgMTAgT2N0b2JlciAyMDIxLCA8aHR0cHM6Ly92aWNyb2Fkc29wZW5kYXRhLXZpY3JvYWRzbWFwcy5vcGVuZGF0YS5hcmNnaXMuY29tL2RhdGFzZXRzL3ZpY3JvYWRzbWFwczo6cm9hZC1jcmFzaGVzLWZvci1maXZlLXllYXJzLXZpY3RvcmlhL2Fib3V0Pi4KClZpY3RvcmlhIERlcGFydG1lbnQgb2YgVHJhbnNwb3J0LCBfRmF0YWwgQ3Jhc2hlcyAtIExpdmVzIExvc3QsIExhc3QgNSBZZWFycyB0byBEYXRlXywgdGhlIEZhdGFsIENyYXNoZXMgLSBMaXZlcyBMb3N0IFllYXIgdG8gRGF0ZSBkYXRhc2V0LCBkYXRhIGZpbGUsIERlcGFydG1lbnQgb2YgVHJhbnNwb3J0IE9wZW4gRGF0YSBIdWIsIHZpZXdlZCAxMCBPY3RvYmVyIDIwMjEsIDxodHRwczovL3ZpY3JvYWRzb3BlbmRhdGEtdmljcm9hZHNtYXBzLm9wZW5kYXRhLmFyY2dpcy5jb20vZGF0YXNldHMvdmljcm9hZHNtYXBzOjpyb2FkLWNyYXNoZXMtZm9yLWZpdmUteWVhcnMtdmljdG9yaWEvYWJvdXQ+LgoKSGFubmFoIFJvb3MgMjAyMSwgX1NtYXJ0IGhhbmRsaW5nIG9mIG1pc3NpbmcgZGF0YSBpbiBSXywgVG93YXJkcyBEYXRhIFNjaWVuY2UsIHZpZXdlZCAxMCBPY3RvYmVyIDIwMjEsCjxodHRwczovL3Rvd2FyZHNkYXRhc2NpZW5jZS5jb20vc21hcnQtaGFuZGxpbmctb2YtbWlzc2luZy1kYXRhLWluLXItNjQyNWY4YTU1OWYyPiAKCjxicj4KPGJyPgo8YnI+Cjxicj4K