Required packages



library(dplyr) 

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(tidyr)
library(magrittr) 

Attaching package: ‘magrittr’

The following object is masked from ‘package:tidyr’:

    extract
library(outliers) 
library(stats)
library(ggplot2)
Warning: package ‘ggplot2’ was built under R version 4.1.1
library(knitr)
Warning: package ‘knitr’ was built under R version 4.1.1
library(MVN)
Warning: package ‘MVN’ was built under R version 4.1.1
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
library(stats)
library(ggplot2)

Executive Summary

The purpose of this assignment is to apply Data Wrangling methods learnt during this course and use it in real-world data. The process includes following data pre-processing steps:

  1. Two datasets from an open source data platform as been read in R Studio using read_csv() and appropriate library packages and R codes havw been used for the analysis.
  2. The datasets have been merged into one and useful variables were selected.
  3. Required variable for the analysis were pre-determined and a brief description about the variables have been provided.
  4. By understanding the data structure, data type it can be identified if there is factor variables and necessary data conversions will be implemented.
  5. Tidy and Manipulate technique are used to ensure the data meets the Tidy Data principals such as mutate a new variable with calculation, reshape the data from untidy to tidy format for better analysis.
  6. By scanning the data, we are able to find missing values, errors, or special values and was able conduct appropriate remediation.
  7. Using summary statistics we conduct the second scan for any outliers,appropriate plots are then applied on the data to transform it for better insight.

Data

The first dataset contains raw counts of all cancer incidence and mortality data by year, age,sex and type.The data was published by Australian Institute of Health and Welfare (AIHW), and accessed from https://data.gov.au/ .

Dataset Name - Cancer counts Its comprises counts by cancer types for incidents and mortalities, the data range is from 1968 to 2010.

The second dataset is cancer rates among all Australian population for all cancer incidence and mortality data by sex, year, age, and type.The data was published by Australian Institute of Health and Welfare (AIHW), and accessed from https://data.gov.au/

Dataset Name - Cancer Rates

Both datasets have 9 main variables that can be used for further analysis and are described as follow:

Both these 2 datasets were in CSV format.


cancer_counts <- read_csv("~/Desktop/practice/data/cancer_counts.csv")
Rows: 9240 Columns: 23
── Column specification ────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (3): Sex, Type, Cancer_Type
dbl (20): Year, Age_0_to_4, Age_5_to_9, Age_10_to_14, Age_15_to_19, Age_20_to_24, Age_25_to_29, Age_30_to_34...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
str(cancer_counts)
spec_tbl_df [9,240 × 23] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ Year        : num [1:9240] 1968 1969 1970 1971 1972 ...
 $ Sex         : chr [1:9240] "Male" "Male" "Male" "Male" ...
 $ Type        : chr [1:9240] "Incidence" "Incidence" "Incidence" "Incidence" ...
 $ Cancer_Type : chr [1:9240] "Acute lymphoblastic leukaemia" "Acute lymphoblastic leukaemia" "Acute lymphoblastic leukaemia" "Acute lymphoblastic leukaemia" ...
 $ Age_0_to_4  : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_5_to_9  : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_10_to_14: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_15_to_19: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_20_to_24: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_25_to_29: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_30_to_34: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_35_to_39: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_40_to_44: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_45_to_49: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_50_to_54: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_55_to_59: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_60_to_64: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_65_to_69: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_70_to_74: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_75_to_79: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_80_to_84: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_85+     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_Unknown : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 - attr(*, "spec")=
  .. cols(
  ..   Year = col_double(),
  ..   Sex = col_character(),
  ..   Type = col_character(),
  ..   Cancer_Type = col_character(),
  ..   Age_0_to_4 = col_double(),
  ..   Age_5_to_9 = col_double(),
  ..   Age_10_to_14 = col_double(),
  ..   Age_15_to_19 = col_double(),
  ..   Age_20_to_24 = col_double(),
  ..   Age_25_to_29 = col_double(),
  ..   Age_30_to_34 = col_double(),
  ..   Age_35_to_39 = col_double(),
  ..   Age_40_to_44 = col_double(),
  ..   Age_45_to_49 = col_double(),
  ..   Age_50_to_54 = col_double(),
  ..   Age_55_to_59 = col_double(),
  ..   Age_60_to_64 = col_double(),
  ..   Age_65_to_69 = col_double(),
  ..   Age_70_to_74 = col_double(),
  ..   Age_75_to_79 = col_double(),
  ..   Age_80_to_84 = col_double(),
  ..   `Age_85+` = col_double(),
  ..   Age_Unknown = col_double()
  .. )
 - attr(*, "problems")=<externalptr> 
cancer_rates <- read_csv("~/Desktop/practice/data/cancer_rates.csv")
Rows: 9240 Columns: 26
── Column specification ────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (3): Sex, Type, Cancer_Type
dbl (23): Year, Age_0_to_4, Age_5_to_9, Age_10_to_14, Age_15_to_19, Age_20_to_24, Age_25_to_29, Age_30_to_34...

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
str(cancer_rates)
spec_tbl_df [9,240 × 26] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ Year             : num [1:9240] 1968 1969 1970 1971 1972 ...
 $ Sex              : chr [1:9240] "Male" "Male" "Male" "Male" ...
 $ Type             : chr [1:9240] "Incidence" "Incidence" "Incidence" "Incidence" ...
 $ Cancer_Type      : chr [1:9240] "Acute lymphoblastic leukaemia" "Acute lymphoblastic leukaemia" "Acute lymphoblastic leukaemia" "Acute lymphoblastic leukaemia" ...
 $ Age_0_to_4       : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_5_to_9       : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_10_to_14     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_15_to_19     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_20_to_24     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_25_to_29     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_30_to_34     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_35_to_39     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_40_to_44     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_45_to_49     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_50_to_54     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_55_to_59     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_60_to_64     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_65_to_69     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_70_to_74     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_75_to_79     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_80_to_84     : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_85+          : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_Unknown      : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_Std_Rate_Aust: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_Std_Rate_Segi: num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 $ Age_Std_Rate_WHO : num [1:9240] NA NA NA NA NA NA NA NA NA NA ...
 - attr(*, "spec")=
  .. cols(
  ..   Year = col_double(),
  ..   Sex = col_character(),
  ..   Type = col_character(),
  ..   Cancer_Type = col_character(),
  ..   Age_0_to_4 = col_double(),
  ..   Age_5_to_9 = col_double(),
  ..   Age_10_to_14 = col_double(),
  ..   Age_15_to_19 = col_double(),
  ..   Age_20_to_24 = col_double(),
  ..   Age_25_to_29 = col_double(),
  ..   Age_30_to_34 = col_double(),
  ..   Age_35_to_39 = col_double(),
  ..   Age_40_to_44 = col_double(),
  ..   Age_45_to_49 = col_double(),
  ..   Age_50_to_54 = col_double(),
  ..   Age_55_to_59 = col_double(),
  ..   Age_60_to_64 = col_double(),
  ..   Age_65_to_69 = col_double(),
  ..   Age_70_to_74 = col_double(),
  ..   Age_75_to_79 = col_double(),
  ..   Age_80_to_84 = col_double(),
  ..   `Age_85+` = col_double(),
  ..   Age_Unknown = col_double(),
  ..   Age_Std_Rate_Aust = col_double(),
  ..   Age_Std_Rate_Segi = col_double(),
  ..   Age_Std_Rate_WHO = col_double()
  .. )
 - attr(*, "problems")=<externalptr> 

The Dataset contains 9240 observations and 26 Variable.

Merged dataset

By merging the two datasets,it combines the common variables in cancer counts and cancer rates by year, type,sex and cancer_type.


cancer_merged <- left_join(cancer_counts, cancer_rates, by = c("Year" = "Year", "Cancer_Type" = "Cancer_Type", "Type" = "Type", "Sex" = "Sex" ))

str(cancer_merged)
dim(cancer_merged)

Understand

The two datasets are merged into one using the left_join() function by using the common variables in the both dataset.

After the datasets were merged, we ended up with up with 45 variables and 9240 observations.

Variable descriptions / data dictionary 1. Year- each year the statistics were gathered for cancer mortality (numeric value to be converted to interger) 2. Sex - gender male or female(character value - categorical to be converted to factor) 3. Type- If its a incident or a mortality by the cancer type reported (character value - categorical) 4. Cancer Type - fatalities resulting by the type of cancer (character value - categorical) 5. Age.x and Age.y - cancer incident or mortality count by the age band (character value - categorical) 6. Age_Std_Rate* - further clarification on the cancer incident/mortality rates.

By using str(cancer_merged) we can deteremind the structure of the dataset and to check the dimension of the datset dim(cancer_merged).

Sex variable has been changed from character to factor,ordered using levels(). Year varible has been changed from numeric to factor.


##Sex variable from character to factor 
 
cancer_merged$Sex <- factor(cancer_merged$Sex ,levels = c("Male", "Female", "Persons"))

str(cancer_merged$Sex)


## Year varible has been changed from numeric to factor.

cancer_merged$Year <- as.integer(cancer_merged$Year)
is.integer(cancer_merged$Year)

newcancer_merged <- cancer_merged
newcancer_merged %<>% select(-c(Age_Unknown.x, Age_Unknown.y,Age_Std_Rate_WHO,Age_Std_Rate_Aust, Age_Std_Rate_Segi))

#glimpe(newcancer_merged) or str(newcancer_merged) will confirm the new subset dataframe, it contains 40 varibles and 9,240 observations.

Tidy & Manipulate Data I

For the data to meet tidy data principles, tidy data needs to adhere to the following rules (Wickham and Grolemund (2016)):

Each variable must have its own column. Each observation must have its own row. Each value must have its own cell.

Further scrutiny of the merged dataset confirms the data has several columns for the age band therefore it does not meet the tidy principals, hence all these columns should be in one column.

To amend the dataset we will use pivot_longer function, however as there are many age bands, we will need to consilidate the age bands to the below categories using the transmute function:

Age 0 to 9 years old

Age 10 to 19 years old

Ages 20 to 64 years old

Ages 65 years to 84 years old

Tidy and Manipulate Data I & II has been combined to one, transmute and pivot_longer has been action-ed below.

Age.band x and y represent the same age band for cancer counts and cancer rates however the contain different values such as cancer counts and cancer rates.


# Age categories have been grouped together in the below bands, Senior band is 
newcancer_merged1 <- newcancer_merged
newcancer_merged1 %<>% 
transmute(Year, Sex, Type, Cancer_Type, 
            Age0_9x = Age_0_to_4.x + Age_5_to_9.x, 
            Age10_19x =  Age_10_to_14.x + Age_15_to_19.x, 
            Age20_64x = Age_20_to_24.x + Age_25_to_29.x + 
                      Age_30_to_34.x + Age_35_to_39.x + Age_40_to_44.x + 
                      Age_45_to_49.x + Age_50_to_54.x + Age_55_to_59.x + 
                      Age_60_to_64.x, 
            Age65_84x = Age_65_to_69.x + Age_70_to_74.x + Age_75_to_79.x + Age_80_to_84.x,
            Age0_9y = Age_0_to_4.y + Age_5_to_9.y,
            Age10_19y =  Age_10_to_14.y + Age_15_to_19.y, 
            Age20_64y = Age_20_to_24.y + Age_25_to_29.y + 
                      Age_30_to_34.y + Age_35_to_39.y + Age_40_to_44.y + 
                      Age_45_to_49.y + Age_50_to_54.y + Age_55_to_59.y + 
                      Age_60_to_64.y, 
            Age64_84y = Age_65_to_69.y + Age_70_to_74.y + Age_75_to_79.y + Age_80_to_84.y)

str(newcancer_merged1)

# convering to data.frame()
newcancer_merged1 <-as.data.frame(newcancer_merged1)

The age band columns have now been merged into one category by group them to a age range, however to meet the tidy date age range will need to be placed into a single “Age” column.



# a) Creating counts dataset to combine all of the age groups into one age column.

Age_counts <- newcancer_merged1 
Age_counts %<>% 
  select(Year, Sex, Type, Cancer_Type, Age0_9x, Age10_19x,Age20_64x,Age65_84x) %>% 
  pivot_longer(- c(Year, Sex, Type, Cancer_Type),
               names_to = "Age_Category", values_to = "Count")

# b) Create rates dataset to combine all of the age groups into one age column.
Age_rates <- newcancer_merged1 
Age_rates %<>% 
  select(Year, Sex, Type, Cancer_Type, Age0_9x, Age10_19x,Age20_64x,Age65_84x) %>% 
  pivot_longer(- c(Year, Sex, Type, Cancer_Type),
               names_to = "Age_Category", values_to = "Rate")

# c) Rejoining datasets counts and rates

newcancer_merged2 <- left_join(Age_counts,Age_rates , by = c("Year" = "Year", "Sex" = "Sex", "Type" = "Type", 
                                             "Cancer_Type" = "Cancer_Type", 
                                             "Age_Category" = "Age_Category" ))


# Converting Age columns to factor
newcancer_merged2$Age_Category <- factor(newcancer_merged2$Age_Category, levels = c("Age0_9x", "Age10_19x","Age20-64x","Age65_84x")) 
                                                                    "


str(newcancer_merged2)

Scan I

The below actions are taken to determine missing values,inconsistencies and obvious errors.

The following steps can be taken to confirm the missing values in the dataset.

summarise(count = n()). function sum(is.na()) - to get missing values for the entire dataset names(which(colSums(is.na())>0)) -This can determined which variables have missing values completed.cases() function excludes missing values from dataset


# To get missing values (NA's) are for the entire dataset

sum(is.na(newcancer_merged2))


# Obtaining missing values per each varible
colSums(is.na(newcancer_merged2))

# Determing the years with missing values.
 
 newcancer_merged2%>% 
  filter(is.na(Count) | is.na(Rate)) %>% 
  select(Year, Sex, Type, Cancer_Type, Age_Category, Count, Rate) %>% 
  group_by(Year) %>% 
  summarise(count = n())
  

Between 1968 -1981 and 2011 there was 472 missing values. Between 1982 -2000 there was 116 missing value and from 2002 to 2011 there was 104 missing values , gradually decreasing.


# Treatment of missing values using completed.cases function

newcancer_merged2 <- newcancer_merged2[complete.cases(newcancer_merged2),]

nrow(newcancer_merged2) # Get number of non-missing rows
# check that all NA have been excluded

sum(is.na(str(newcancer_merged2)))

# OR

Summary(newcancer_merged2)

Scan II

With Scan II we check the numeric data for outliers.

As both counts and rates are the individual numeric variables, univariate outlier detection will be implemented.


# Generate boxplot - Count variable

newcancer_merged2$Count %>% 
  boxplot(main ="Cancer Counts", ylab = "Count", col = "red")

# Mean and Median values for Count

summary(newcancer_merged2$Count)

# Generate boxplot - Rate variable

newcancer_merged2$Rate %>%
  boxplot(main = "Cancer Rates", ylab = "Count", col = "blue") 

# Mean and Median values for Rate variable
summary(newcancer_merged2$Rate)

We have detected following outliers for each of the variables Count - has 197.6 outliers above the mean Rates - has 197.6 outliers above the mean


# Verifying IQR for upper and lower fences for Count and Rate variables

 # Count variable IQR
count_iqr <- IQR(newcancer_merged2$Count)
count_iqr

rate_iqr <- IQR(newcancer_merged2$Rate) # Rate variable IQR
rate_iqr

IQR for Counts and Rates are 71.


#To determine upper and lower fences for Count and Rate variable.

# Lower Count quantile
q1 <- quantile(newcancer_merged2$Count, probs = 0.25) 

# Upper Count quantile
q3 <- quantile(newcancer_merged2$Count, probs = 0.75) 

# Lower fence value for Count variable
count_low_fence <- q1 - (1.5 * count_iqr) 

# Upper fence value for Count variable
count_up_fence  <- q3 + (1.5 * count_iqr)  

count_low_fence

count_up_fence

As can be seen above, the fence values for both variables are as follows:

Fence Count Rate Low -106.5 -106.5 Up 177.5 177.5


# Count outliers below low fence
count_up_low_outliers <- which(newcancer_merged2$Count < count_low_fence)

# Count outliers above up fence
count_up_outliers <- which(newcancer_merged2$Count > count_up_fence)      


# Number of Count outliers below low fence and above low fence

length(count_up_low_outliers)
length(count_up_outliers)


# Rate outliers below low fence
rate_up_low_outliers <- which(newcancer_merged2$Rate < count_low_fence)

# Rate outliers above up fence
rate_up_outliers <- which(newcancer_merged2$Rate > count_up_fence)     

# Number of Count outliers below low fence and above low fence
length(rate_up_low_outliers)
length(rate_up_outliers)

Outliers for both variables Fence Count Rate Low 0 0 Up 3841 3841

Imputing these outliers with the mean of the Count and Rate values.


#Impute Count outliers with mean of Count

newcancer_merged2$Count[count_up_outliers] <- mean(newcancer_merged2$Count)
length(newcancer_merged2$Count[count_up_outliers])


#Impute Rate outliers with mean of Rate

newcancer_merged2$Rate[rate_up_outliers] <- mean(newcancer_merged2$Rate)
length(newcancer_merged2$Rate[rate_up_outliers])

Both variables aligns with the initial outlier amount.

newcancer_merged2$Count %>%
  boxplot(main = "Cancer Counts", ylab = "Count", col = "blue")

newcancer_merged2$Rate %>%
  boxplot(main = "Cancer Rate", ylab = "Rate", col = "yellow")  

Transform

Applying log transformation is the best way to reduce outliers and decrease the skewness of the empirical distribution.

When applying log transformation to the boxplot, the outliers are reduced and only 1 outlier is detected which is normal as it is not fall far away from the Maximum value.


hist(newcancer_merged2$Rate, main = "Histogram of Cancer Rates",
     xlab="Cancer Rate")


log_rate<- log10(newcancer_merged2$Rate)
hist(log_rate, 
     main = "Histogram of log 10 log Cancer Rates", 
     xlab = "Cancer Rate", col = "blue", border = "grey")

Conclusion

We conclude with mortality rates varying between age groups and the lowest spectrum cancer related cause of death for Age 0-9 and Age 10-19 is Acute lymphoblastic leukaemia.

Adults are at high risk from colon cancer. And in Seniors Rectal cancer is the biggest killer with Non-Hodgkin lymphoma having the highest rates of incidents.

``` # Reference

Boehmke, BC 2016, Data Wrangling with R (Links to an external site.), Springer International Publishing, Cham, Switzerland.

Wickham, H 2019, Advanced R (Links to an external site.), CRC press.



LS0tCnRpdGxlOiAnRGF0YSBXcmFuZ2xpbmcgQXNzZXNzbWVudCBUYXNrIDM6IERhdGFzZXQgY2hhbGxlbmdlJwphdXRob3I6ICJKb3NlcGhpbmUgRWxheWFwZXJ1bWEgYW5kIFMzOTEwNjA3IgpMb29tIFVSTDogaHR0cHM6Ly93d3cubG9vbS5jb20vc2hhcmUvNzlmOTkxOTk2NjQ2NGI0YjgzZmQ5NGE2MmY4ZjU2YzEKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBkZl9wcmludDogcGFnZWQKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKCiMjIFJlcXVpcmVkIHBhY2thZ2VzIApgYGB7cn0KCgpsaWJyYXJ5KGRwbHlyKSAKbGlicmFyeSh0aWR5cikKbGlicmFyeShtYWdyaXR0cikgCmxpYnJhcnkob3V0bGllcnMpIApsaWJyYXJ5KHN0YXRzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoTVZOKQpsaWJyYXJ5KHN0YXRzKQpsaWJyYXJ5KGdncGxvdDIpCgpgYGAKCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5CgpUaGUgcHVycG9zZSBvZiB0aGlzIGFzc2lnbm1lbnQgaXMgdG8gYXBwbHkgRGF0YSBXcmFuZ2xpbmcgbWV0aG9kcyBsZWFybnQgZHVyaW5nIHRoaXMgY291cnNlIGFuZCB1c2UgaXQgaW4gcmVhbC13b3JsZCBkYXRhLiBUaGUgcHJvY2VzcyBpbmNsdWRlcyBmb2xsb3dpbmcgZGF0YSBwcmUtcHJvY2Vzc2luZyBzdGVwczoKCjEuIFR3byBkYXRhc2V0cyBmcm9tIGFuIG9wZW4gc291cmNlIGRhdGEgcGxhdGZvcm0gYXMgYmVlbiByZWFkIGluIFIgU3R1ZGlvIHVzaW5nICAgICAgICAgICAgICAgICAgIHJlYWRfY3N2KCkgYW5kIGFwcHJvcHJpYXRlIGxpYnJhcnkgcGFja2FnZXMgYW5kIFIgY29kZXMgaGF2dyBiZWVuIHVzZWQgZm9yIHRoZSBhbmFseXNpcy4KMi4gVGhlIGRhdGFzZXRzIGhhdmUgYmVlbiBtZXJnZWQgaW50byBvbmUgYW5kIHVzZWZ1bCB2YXJpYWJsZXMgd2VyZSBzZWxlY3RlZC4KMy4gUmVxdWlyZWQgdmFyaWFibGUgZm9yIHRoZSBhbmFseXNpcyB3ZXJlIHByZS1kZXRlcm1pbmVkIGFuZCBhIGJyaWVmIGRlc2NyaXB0aW9uIGFib3V0IHRoZSAgICAgICAgdmFyaWFibGVzIGhhdmUgYmVlbiBwcm92aWRlZC4KNC4gQnkgdW5kZXJzdGFuZGluZyB0aGUgZGF0YSBzdHJ1Y3R1cmUsIGRhdGEgdHlwZSBpdCBjYW4gYmUgaWRlbnRpZmllZCBpZiB0aGVyZSBpcyBmYWN0b3IgICAgICAgICAgdmFyaWFibGVzIGFuZCBuZWNlc3NhcnkgZGF0YSBjb252ZXJzaW9ucyB3aWxsIGJlIGltcGxlbWVudGVkLgo1LiBUaWR5IGFuZCBNYW5pcHVsYXRlIHRlY2huaXF1ZSBhcmUgdXNlZCB0byBlbnN1cmUgdGhlIGRhdGEgbWVldHMgdGhlIFRpZHkgRGF0YSBwcmluY2lwYWxzICAgICAgICBzdWNoIGFzIG11dGF0ZSBhIG5ldyB2YXJpYWJsZSB3aXRoIGNhbGN1bGF0aW9uLCByZXNoYXBlIHRoZSBkYXRhIGZyb20gdW50aWR5IHRvIHRpZHkgZm9ybWF0ICAgICBmb3IgYmV0dGVyIGFuYWx5c2lzLgo2LiBCeSBzY2FubmluZyB0aGUgZGF0YSwgd2UgYXJlIGFibGUgdG8gZmluZCBtaXNzaW5nIHZhbHVlcywgZXJyb3JzLCBvciBzcGVjaWFsIHZhbHVlcyBhbmQgd2FzICAgICBhYmxlIGNvbmR1Y3QgYXBwcm9wcmlhdGUgcmVtZWRpYXRpb24uIAo3LiBVc2luZyBzdW1tYXJ5IHN0YXRpc3RpY3Mgd2UgY29uZHVjdCB0aGUgc2Vjb25kIHNjYW4gZm9yIGFueSBvdXRsaWVycyxhcHByb3ByaWF0ZSBwbG90cyBhcmUgICAgICB0aGVuIGFwcGxpZWQgb24gdGhlIGRhdGEgdG8gdHJhbnNmb3JtIGl0IGZvciBiZXR0ZXIgaW5zaWdodC4gCiAKCiMjIERhdGEKVGhlIGZpcnN0IGRhdGFzZXQgY29udGFpbnMgcmF3IGNvdW50cyBvZiBhbGwgY2FuY2VyIGluY2lkZW5jZSBhbmQgbW9ydGFsaXR5IGRhdGEgYnkgeWVhciwgYWdlLHNleCBhbmQgdHlwZS5UaGUgZGF0YSB3YXMgcHVibGlzaGVkIGJ5IEF1c3RyYWxpYW4gSW5zdGl0dXRlIG9mIEhlYWx0aCBhbmQgV2VsZmFyZSAoQUlIVyksICBhbmQgYWNjZXNzZWQgZnJvbSBodHRwczovL2RhdGEuZ292LmF1LyAuCgpEYXRhc2V0IE5hbWUgLSBDYW5jZXIgY291bnRzCkl0cyBjb21wcmlzZXMgY291bnRzIGJ5IGNhbmNlciB0eXBlcyBmb3IgaW5jaWRlbnRzIGFuZCBtb3J0YWxpdGllcywgdGhlIGRhdGEgcmFuZ2UgaXMgZnJvbSAxOTY4IHRvIDIwMTAuCgpUaGUgc2Vjb25kIGRhdGFzZXQgaXMgY2FuY2VyIHJhdGVzIGFtb25nIGFsbCBBdXN0cmFsaWFuIHBvcHVsYXRpb24gZm9yIGFsbCBjYW5jZXIgaW5jaWRlbmNlIGFuZCBtb3J0YWxpdHkgZGF0YSBieSBzZXgsIHllYXIsIGFnZSwgYW5kIHR5cGUuVGhlIGRhdGEgd2FzIHB1Ymxpc2hlZCBieSBBdXN0cmFsaWFuIEluc3RpdHV0ZSBvZiBIZWFsdGggYW5kIFdlbGZhcmUgKEFJSFcpLCAgYW5kIGFjY2Vzc2VkIGZyb20gaHR0cHM6Ly9kYXRhLmdvdi5hdS8gCgpEYXRhc2V0IE5hbWUgLSBDYW5jZXIgUmF0ZXMKCkJvdGggZGF0YXNldHMgaGF2ZSA5IG1haW4gdmFyaWFibGVzIHRoYXQgY2FuIGJlIHVzZWQgZm9yIGZ1cnRoZXIgYW5hbHlzaXMgYW5kIGFyZSBkZXNjcmliZWQgYXMgZm9sbG93OgoKQm90aCB0aGVzZSAyIGRhdGFzZXRzIHdlcmUgaW4gQ1NWIGZvcm1hdC4KCgpgYGB7cn0KCmNhbmNlcl9jb3VudHMgPC0gcmVhZF9jc3YoIn4vRGVza3RvcC9wcmFjdGljZS9kYXRhL2NhbmNlcl9jb3VudHMuY3N2IikKc3RyKGNhbmNlcl9jb3VudHMpCgoKY2FuY2VyX3JhdGVzIDwtIHJlYWRfY3N2KCJ+L0Rlc2t0b3AvcHJhY3RpY2UvZGF0YS9jYW5jZXJfcmF0ZXMuY3N2IikKc3RyKGNhbmNlcl9yYXRlcykKCmBgYApUaGUgRGF0YXNldCBjb250YWlucyA5MjQwIG9ic2VydmF0aW9ucyBhbmQgMjYgVmFyaWFibGUuCgoKIyMgTWVyZ2VkIGRhdGFzZXQKQnkgbWVyZ2luZyB0aGUgdHdvIGRhdGFzZXRzLGl0IGNvbWJpbmVzIHRoZSBjb21tb24gdmFyaWFibGVzIGluIGNhbmNlciBjb3VudHMgYW5kIGNhbmNlciByYXRlcyBieSB5ZWFyLCB0eXBlLHNleCBhbmQgY2FuY2VyX3R5cGUuCgoKYGBge3J9CgpjYW5jZXJfbWVyZ2VkIDwtIGxlZnRfam9pbihjYW5jZXJfY291bnRzLCBjYW5jZXJfcmF0ZXMsIGJ5ID0gYygiWWVhciIgPSAiWWVhciIsICJDYW5jZXJfVHlwZSIgPSAiQ2FuY2VyX1R5cGUiLCAiVHlwZSIgPSAiVHlwZSIsICJTZXgiID0gIlNleCIgKSkKCnN0cihjYW5jZXJfbWVyZ2VkKQpkaW0oY2FuY2VyX21lcmdlZCkKCmBgYAoKIyMgVW5kZXJzdGFuZCAKClRoZSB0d28gZGF0YXNldHMgYXJlIG1lcmdlZCBpbnRvIG9uZSB1c2luZyB0aGUgbGVmdF9qb2luKCkgZnVuY3Rpb24gYnkgdXNpbmcgdGhlIGNvbW1vbiB2YXJpYWJsZXMgaW4gdGhlIGJvdGggZGF0YXNldC4KCkFmdGVyIHRoZSBkYXRhc2V0cyB3ZXJlIG1lcmdlZCwgd2UgZW5kZWQgdXAgd2l0aCB1cCB3aXRoIDQ1IHZhcmlhYmxlcyBhbmQgOTI0MCBvYnNlcnZhdGlvbnMuCgoKVmFyaWFibGUgZGVzY3JpcHRpb25zIC8gZGF0YSBkaWN0aW9uYXJ5IAoxLiBZZWFyLSBlYWNoIHllYXIgdGhlIHN0YXRpc3RpY3Mgd2VyZSBnYXRoZXJlZCBmb3IgY2FuY2VyIG1vcnRhbGl0eSAobnVtZXJpYyB2YWx1ZSB0byBiZSBjb252ZXJ0ZWQgdG8gaW50ZXJnZXIpIAoyLiBTZXggLSBnZW5kZXIgbWFsZSBvciBmZW1hbGUoY2hhcmFjdGVyIHZhbHVlIC0gY2F0ZWdvcmljYWwgdG8gYmUgY29udmVydGVkIHRvIGZhY3RvcikKMy4gVHlwZS0gSWYgaXRzIGEgaW5jaWRlbnQgb3IgYSBtb3J0YWxpdHkgYnkgdGhlIGNhbmNlciB0eXBlIHJlcG9ydGVkIChjaGFyYWN0ZXIgdmFsdWUgLSBjYXRlZ29yaWNhbCkgCjQuIENhbmNlciBUeXBlIC0gZmF0YWxpdGllcyByZXN1bHRpbmcgYnkgdGhlIHR5cGUgb2YgY2FuY2VyIChjaGFyYWN0ZXIgdmFsdWUgLSBjYXRlZ29yaWNhbCkgCjUuIEFnZS54IGFuZCBBZ2UueSAtIGNhbmNlciBpbmNpZGVudCBvciBtb3J0YWxpdHkgY291bnQgYnkgdGhlIGFnZSBiYW5kIChjaGFyYWN0ZXIgdmFsdWUgLSBjYXRlZ29yaWNhbCkKNi4gQWdlX1N0ZF9SYXRlKiAtIGZ1cnRoZXIgY2xhcmlmaWNhdGlvbiBvbiB0aGUgY2FuY2VyIGluY2lkZW50L21vcnRhbGl0eSByYXRlcy4KCgpCeSB1c2luZyBzdHIoY2FuY2VyX21lcmdlZCkgd2UgY2FuIGRldGVyZW1pbmQgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YXNldCBhbmQgdG8gY2hlY2sgdGhlIGRpbWVuc2lvbiBvZiB0aGUgZGF0c2V0IGRpbShjYW5jZXJfbWVyZ2VkKS4KCioqU2V4IHZhcmlhYmxlICBoYXMgYmVlbiBjaGFuZ2VkIGZyb20gY2hhcmFjdGVyIHRvIGZhY3RvcixvcmRlcmVkIHVzaW5nIGxldmVscygpLgoqKlllYXIgdmFyaWJsZSBoYXMgYmVlbiBjaGFuZ2VkIGZyb20gbnVtZXJpYyB0byBmYWN0b3IuCgpgYGB7cn0KCiMjU2V4IHZhcmlhYmxlIGZyb20gY2hhcmFjdGVyIHRvIGZhY3RvciAKIApjYW5jZXJfbWVyZ2VkJFNleCA8LSBmYWN0b3IoY2FuY2VyX21lcmdlZCRTZXggLGxldmVscyA9IGMoIk1hbGUiLCAiRmVtYWxlIiwgIlBlcnNvbnMiKSkKCnN0cihjYW5jZXJfbWVyZ2VkJFNleCkKCgojIyBZZWFyIHZhcmlibGUgaGFzIGJlZW4gY2hhbmdlZCBmcm9tIG51bWVyaWMgdG8gZmFjdG9yLgoKY2FuY2VyX21lcmdlZCRZZWFyIDwtIGFzLmludGVnZXIoY2FuY2VyX21lcmdlZCRZZWFyKQppcy5pbnRlZ2VyKGNhbmNlcl9tZXJnZWQkWWVhcikKCmBgYApgYGB7cn0KCm5ld2NhbmNlcl9tZXJnZWQgPC0gY2FuY2VyX21lcmdlZApuZXdjYW5jZXJfbWVyZ2VkICU8PiUgc2VsZWN0KC1jKEFnZV9Vbmtub3duLngsIEFnZV9Vbmtub3duLnksQWdlX1N0ZF9SYXRlX1dITyxBZ2VfU3RkX1JhdGVfQXVzdCwgQWdlX1N0ZF9SYXRlX1NlZ2kpKQoKI2dsaW1wZShuZXdjYW5jZXJfbWVyZ2VkKSBvciBzdHIobmV3Y2FuY2VyX21lcmdlZCkgd2lsbCBjb25maXJtIHRoZSBuZXcgc3Vic2V0IGRhdGFmcmFtZSwgaXQgY29udGFpbnMgNDAgdmFyaWJsZXMgYW5kIDksMjQwIG9ic2VydmF0aW9ucy4KCmBgYAoKCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSSAKCkZvciB0aGUgZGF0YSB0byBtZWV0IHRpZHkgZGF0YSBwcmluY2lwbGVzLCB0aWR5IGRhdGEgbmVlZHMgdG8gYWRoZXJlIHRvIHRoZSBmb2xsb3dpbmcgcnVsZXMgKFdpY2toYW0gYW5kIEdyb2xlbXVuZCAoMjAxNikpOgoKRWFjaCB2YXJpYWJsZSBtdXN0IGhhdmUgaXRzIG93biBjb2x1bW4uIEVhY2ggb2JzZXJ2YXRpb24gbXVzdCBoYXZlIGl0cyBvd24gcm93LiBFYWNoIHZhbHVlIG11c3QgaGF2ZSBpdHMgb3duIGNlbGwuCgoKRnVydGhlciBzY3J1dGlueSBvZiB0aGUgbWVyZ2VkIGRhdGFzZXQgY29uZmlybXMgdGhlIGRhdGEgaGFzIHNldmVyYWwgY29sdW1ucyBmb3IgdGhlIGFnZSBiYW5kIHRoZXJlZm9yZSBpdCBkb2VzIG5vdCBtZWV0IHRoZSB0aWR5IHByaW5jaXBhbHMsIGhlbmNlIGFsbCB0aGVzZSBjb2x1bW5zIHNob3VsZCBiZSBpbiBvbmUgY29sdW1uLiAKClRvIGFtZW5kIHRoZSBkYXRhc2V0IHdlIHdpbGwgdXNlICBwaXZvdF9sb25nZXIgZnVuY3Rpb24sIGhvd2V2ZXIgYXMgdGhlcmUgYXJlIG1hbnkgYWdlIGJhbmRzLCB3ZSB3aWxsIG5lZWQgdG8gY29uc2lsaWRhdGUgdGhlIGFnZSBiYW5kcyB0byB0aGUgYmVsb3cgY2F0ZWdvcmllcyB1c2luZyB0aGUgdHJhbnNtdXRlIGZ1bmN0aW9uOgoKQWdlIDAgdG8gOSB5ZWFycyBvbGQKCkFnZSAxMCB0byAxOSB5ZWFycyBvbGQKCkFnZXMgMjAgdG8gNjQgeWVhcnMgb2xkCgpBZ2VzIDY1IHllYXJzIHRvIDg0IHllYXJzIG9sZCAKClRpZHkgYW5kIE1hbmlwdWxhdGUgRGF0YSBJICYgSUkgaGFzIGJlZW4gY29tYmluZWQgdG8gb25lLCB0cmFuc211dGUgYW5kIHBpdm90X2xvbmdlciBoYXMgYmVlbiBhY3Rpb24tZWQgYmVsb3cuCgpBZ2UuYmFuZCB4IGFuZCB5IHJlcHJlc2VudCB0aGUgc2FtZSBhZ2UgYmFuZCBmb3IgY2FuY2VyIGNvdW50cyBhbmQgY2FuY2VyIHJhdGVzIGhvd2V2ZXIgdGhlIGNvbnRhaW4gZGlmZmVyZW50IHZhbHVlcyBzdWNoIGFzIGNhbmNlciBjb3VudHMgYW5kIGNhbmNlciByYXRlcy4KCgoKCmBgYHtyfQoKIyBBZ2UgY2F0ZWdvcmllcyBoYXZlIGJlZW4gZ3JvdXBlZCB0b2dldGhlciBpbiB0aGUgYmVsb3cgYmFuZHMsIFNlbmlvciBiYW5kIGlzIApuZXdjYW5jZXJfbWVyZ2VkMSA8LSBuZXdjYW5jZXJfbWVyZ2VkCm5ld2NhbmNlcl9tZXJnZWQxICU8PiUgCnRyYW5zbXV0ZShZZWFyLCBTZXgsIFR5cGUsIENhbmNlcl9UeXBlLCAKICAgICAgICAgICAgQWdlMF85eCA9IEFnZV8wX3RvXzQueCArIEFnZV81X3RvXzkueCwgCiAgICAgICAgICAgIEFnZTEwXzE5eCA9ICBBZ2VfMTBfdG9fMTQueCArIEFnZV8xNV90b18xOS54LCAKICAgICAgICAgICAgQWdlMjBfNjR4ID0gQWdlXzIwX3RvXzI0LnggKyBBZ2VfMjVfdG9fMjkueCArIAogICAgICAgICAgICAgICAgICAgICAgQWdlXzMwX3RvXzM0LnggKyBBZ2VfMzVfdG9fMzkueCArIEFnZV80MF90b180NC54ICsgCiAgICAgICAgICAgICAgICAgICAgICBBZ2VfNDVfdG9fNDkueCArIEFnZV81MF90b181NC54ICsgQWdlXzU1X3RvXzU5LnggKyAKICAgICAgICAgICAgICAgICAgICAgIEFnZV82MF90b182NC54LCAKICAgICAgICAgICAgQWdlNjVfODR4ID0gQWdlXzY1X3RvXzY5LnggKyBBZ2VfNzBfdG9fNzQueCArIEFnZV83NV90b183OS54ICsgQWdlXzgwX3RvXzg0LngsCiAgICAgICAgICAgIEFnZTBfOXkgPSBBZ2VfMF90b180LnkgKyBBZ2VfNV90b185LnksCiAgICAgICAgICAgIEFnZTEwXzE5eSA9ICBBZ2VfMTBfdG9fMTQueSArIEFnZV8xNV90b18xOS55LCAKICAgICAgICAgICAgQWdlMjBfNjR5ID0gQWdlXzIwX3RvXzI0LnkgKyBBZ2VfMjVfdG9fMjkueSArIAogICAgICAgICAgICAgICAgICAgICAgQWdlXzMwX3RvXzM0LnkgKyBBZ2VfMzVfdG9fMzkueSArIEFnZV80MF90b180NC55ICsgCiAgICAgICAgICAgICAgICAgICAgICBBZ2VfNDVfdG9fNDkueSArIEFnZV81MF90b181NC55ICsgQWdlXzU1X3RvXzU5LnkgKyAKICAgICAgICAgICAgICAgICAgICAgIEFnZV82MF90b182NC55LCAKICAgICAgICAgICAgQWdlNjRfODR5ID0gQWdlXzY1X3RvXzY5LnkgKyBBZ2VfNzBfdG9fNzQueSArIEFnZV83NV90b183OS55ICsgQWdlXzgwX3RvXzg0LnkpCgpzdHIobmV3Y2FuY2VyX21lcmdlZDEpCgojIGNvbnZlcmluZyB0byBkYXRhLmZyYW1lKCkKbmV3Y2FuY2VyX21lcmdlZDEgPC1hcy5kYXRhLmZyYW1lKG5ld2NhbmNlcl9tZXJnZWQxKQoKYGBgCgpUaGUgYWdlIGJhbmQgY29sdW1ucyBoYXZlIG5vdyBiZWVuIG1lcmdlZCBpbnRvIG9uZSBjYXRlZ29yeSBieSBncm91cCB0aGVtIHRvIGEgYWdlIHJhbmdlLCBob3dldmVyIHRvIG1lZXQgdGhlIHRpZHkgZGF0ZSBhZ2UgcmFuZ2Ugd2lsbCBuZWVkIHRvIGJlIHBsYWNlZCBpbnRvIGEgc2luZ2xlICJBZ2UiIGNvbHVtbi4KCgpgYGB7cn0KCgojIGEpIENyZWF0aW5nIGNvdW50cyBkYXRhc2V0IHRvIGNvbWJpbmUgYWxsIG9mIHRoZSBhZ2UgZ3JvdXBzIGludG8gb25lIGFnZSBjb2x1bW4uCgpBZ2VfY291bnRzIDwtIG5ld2NhbmNlcl9tZXJnZWQxIApBZ2VfY291bnRzICU8PiUgCiAgc2VsZWN0KFllYXIsIFNleCwgVHlwZSwgQ2FuY2VyX1R5cGUsIEFnZTBfOXgsIEFnZTEwXzE5eCxBZ2UyMF82NHgsQWdlNjVfODR4KSAlPiUgCiAgcGl2b3RfbG9uZ2VyKC0gYyhZZWFyLCBTZXgsIFR5cGUsIENhbmNlcl9UeXBlKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiQWdlX0NhdGVnb3J5IiwgdmFsdWVzX3RvID0gIkNvdW50IikKCiMgYikgQ3JlYXRlIHJhdGVzIGRhdGFzZXQgdG8gY29tYmluZSBhbGwgb2YgdGhlIGFnZSBncm91cHMgaW50byBvbmUgYWdlIGNvbHVtbi4KQWdlX3JhdGVzIDwtIG5ld2NhbmNlcl9tZXJnZWQxIApBZ2VfcmF0ZXMgJTw+JSAKICBzZWxlY3QoWWVhciwgU2V4LCBUeXBlLCBDYW5jZXJfVHlwZSwgQWdlMF85eCwgQWdlMTBfMTl4LEFnZTIwXzY0eCxBZ2U2NV84NHgpICU+JSAKICBwaXZvdF9sb25nZXIoLSBjKFllYXIsIFNleCwgVHlwZSwgQ2FuY2VyX1R5cGUpLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJBZ2VfQ2F0ZWdvcnkiLCB2YWx1ZXNfdG8gPSAiUmF0ZSIpCgojIGMpIFJlam9pbmluZyBkYXRhc2V0cyBjb3VudHMgYW5kIHJhdGVzCgpuZXdjYW5jZXJfbWVyZ2VkMiA8LSBsZWZ0X2pvaW4oQWdlX2NvdW50cyxBZ2VfcmF0ZXMgLCBieSA9IGMoIlllYXIiID0gIlllYXIiLCAiU2V4IiA9ICJTZXgiLCAiVHlwZSIgPSAiVHlwZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2FuY2VyX1R5cGUiID0gIkNhbmNlcl9UeXBlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBZ2VfQ2F0ZWdvcnkiID0gIkFnZV9DYXRlZ29yeSIgKSkKCgojIENvbnZlcnRpbmcgQWdlIGNvbHVtbnMgdG8gZmFjdG9yCm5ld2NhbmNlcl9tZXJnZWQyJEFnZV9DYXRlZ29yeSA8LSBmYWN0b3IobmV3Y2FuY2VyX21lcmdlZDIkQWdlX0NhdGVnb3J5LCBsZXZlbHMgPSBjKCJBZ2UwXzl4IiwgIkFnZTEwXzE5eCIsIkFnZTIwLTY0eCIsIkFnZTY1Xzg0eCIpKSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiCgoKc3RyKG5ld2NhbmNlcl9tZXJnZWQyKQoKYGBgCgoKIyMJU2NhbiBJIAoKVGhlIGJlbG93IGFjdGlvbnMgYXJlIHRha2VuIHRvIGRldGVybWluZSBtaXNzaW5nIHZhbHVlcyxpbmNvbnNpc3RlbmNpZXMgYW5kIG9idmlvdXMgZXJyb3JzLgoKVGhlIGZvbGxvd2luZyBzdGVwcyBjYW4gYmUgdGFrZW4gdG8gY29uZmlybSB0aGUgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQuCgpzdW1tYXJpc2UoY291bnQgPSBuKCkpLgpmdW5jdGlvbiBzdW0oaXMubmEoKSkgLSB0byBnZXQgbWlzc2luZyB2YWx1ZXMgZm9yIHRoZSBlbnRpcmUgZGF0YXNldApuYW1lcyh3aGljaChjb2xTdW1zKGlzLm5hKCkpPjApKSAtVGhpcyBjYW4gZGV0ZXJtaW5lZCB3aGljaCB2YXJpYWJsZXMgaGF2ZSBtaXNzaW5nIHZhbHVlcwpjb21wbGV0ZWQuY2FzZXMoKSBmdW5jdGlvbiBleGNsdWRlcyBtaXNzaW5nIHZhbHVlcyBmcm9tIGRhdGFzZXQgCgoKYGBge3J9CgojIFRvIGdldCBtaXNzaW5nIHZhbHVlcyAoTkEncykgYXJlIGZvciB0aGUgZW50aXJlIGRhdGFzZXQKCnN1bShpcy5uYShuZXdjYW5jZXJfbWVyZ2VkMikpCgoKIyBPYnRhaW5pbmcgbWlzc2luZyB2YWx1ZXMgcGVyIGVhY2ggdmFyaWJsZQpjb2xTdW1zKGlzLm5hKG5ld2NhbmNlcl9tZXJnZWQyKSkKCmBgYApgYGB7cn0KCiMgRGV0ZXJtaW5nIHRoZSB5ZWFycyB3aXRoIG1pc3NpbmcgdmFsdWVzLgogCiBuZXdjYW5jZXJfbWVyZ2VkMiU+JSAKICBmaWx0ZXIoaXMubmEoQ291bnQpIHwgaXMubmEoUmF0ZSkpICU+JSAKICBzZWxlY3QoWWVhciwgU2V4LCBUeXBlLCBDYW5jZXJfVHlwZSwgQWdlX0NhdGVnb3J5LCBDb3VudCwgUmF0ZSkgJT4lIAogIGdyb3VwX2J5KFllYXIpICU+JSAKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpCiAgCmBgYApCZXR3ZWVuIDE5NjggLTE5ODEgYW5kIDIwMTEgdGhlcmUgd2FzIDQ3MiBtaXNzaW5nIHZhbHVlcy4KQmV0d2VlbiAxOTgyIC0yMDAwIHRoZXJlIHdhcyAxMTYgbWlzc2luZyB2YWx1ZSBhbmQgZnJvbSAyMDAyIHRvIDIwMTEgdGhlcmUgd2FzIDEwNCBtaXNzaW5nIHZhbHVlcyAsIGdyYWR1YWxseSBkZWNyZWFzaW5nLgoKYGBge3J9CgojIFRyZWF0bWVudCBvZiBtaXNzaW5nIHZhbHVlcyB1c2luZyBjb21wbGV0ZWQuY2FzZXMgZnVuY3Rpb24KCm5ld2NhbmNlcl9tZXJnZWQyIDwtIG5ld2NhbmNlcl9tZXJnZWQyW2NvbXBsZXRlLmNhc2VzKG5ld2NhbmNlcl9tZXJnZWQyKSxdCgpucm93KG5ld2NhbmNlcl9tZXJnZWQyKSAjIEdldCBudW1iZXIgb2Ygbm9uLW1pc3Npbmcgcm93cwoKCmBgYAoKYGBge3J9CiMgY2hlY2sgdGhhdCBhbGwgTkEgaGF2ZSBiZWVuIGV4Y2x1ZGVkCgpzdW0oaXMubmEoc3RyKG5ld2NhbmNlcl9tZXJnZWQyKSkpCgojIE9SCgpTdW1tYXJ5KG5ld2NhbmNlcl9tZXJnZWQyKQoKCgpgYGAKCgojIwlTY2FuIElJCgpXaXRoIFNjYW4gSUkgd2UgY2hlY2sgdGhlIG51bWVyaWMgZGF0YSBmb3Igb3V0bGllcnMuCgpBcyBib3RoIGNvdW50cyBhbmQgcmF0ZXMgYXJlIHRoZSBpbmRpdmlkdWFsIG51bWVyaWMgdmFyaWFibGVzLCB1bml2YXJpYXRlIG91dGxpZXIgZGV0ZWN0aW9uIHdpbGwgYmUgaW1wbGVtZW50ZWQuCgoKYGBge3J9CgojIEdlbmVyYXRlIGJveHBsb3QgLSBDb3VudCB2YXJpYWJsZQoKbmV3Y2FuY2VyX21lcmdlZDIkQ291bnQgJT4lIAogIGJveHBsb3QobWFpbiA9IkNhbmNlciBDb3VudHMiLCB5bGFiID0gIkNvdW50IiwgY29sID0gInJlZCIpCgoKYGBgCmBgYHtyfQoKIyBNZWFuIGFuZCBNZWRpYW4gdmFsdWVzIGZvciBDb3VudAoKc3VtbWFyeShuZXdjYW5jZXJfbWVyZ2VkMiRDb3VudCkKCmBgYAoKYGBge3J9CgojIEdlbmVyYXRlIGJveHBsb3QgLSBSYXRlIHZhcmlhYmxlCgpuZXdjYW5jZXJfbWVyZ2VkMiRSYXRlICU+JQogIGJveHBsb3QobWFpbiA9ICJDYW5jZXIgUmF0ZXMiLCB5bGFiID0gIkNvdW50IiwgY29sID0gImJsdWUiKSAKCmBgYApgYGB7cn0KCiMgTWVhbiBhbmQgTWVkaWFuIHZhbHVlcyBmb3IgUmF0ZSB2YXJpYWJsZQpzdW1tYXJ5KG5ld2NhbmNlcl9tZXJnZWQyJFJhdGUpCgpgYGAKV2UgaGF2ZSBkZXRlY3RlZCBmb2xsb3dpbmcgb3V0bGllcnMgZm9yIGVhY2ggb2YgdGhlIHZhcmlhYmxlcwpDb3VudCAtIGhhcyAxOTcuNiBvdXRsaWVycyBhYm92ZSB0aGUgbWVhbgpSYXRlcyAtIGhhcyAxOTcuNiBvdXRsaWVycyBhYm92ZSB0aGUgbWVhbgoKYGBge3J9CgojIFZlcmlmeWluZyBJUVIgZm9yIHVwcGVyIGFuZCBsb3dlciBmZW5jZXMgZm9yIENvdW50IGFuZCBSYXRlIHZhcmlhYmxlcwoKICMgQ291bnQgdmFyaWFibGUgSVFSCmNvdW50X2lxciA8LSBJUVIobmV3Y2FuY2VyX21lcmdlZDIkQ291bnQpCmNvdW50X2lxcgoKCmBgYApgYGB7cn0KCnJhdGVfaXFyIDwtIElRUihuZXdjYW5jZXJfbWVyZ2VkMiRSYXRlKSAjIFJhdGUgdmFyaWFibGUgSVFSCnJhdGVfaXFyCmBgYApJUVIgZm9yIENvdW50cyBhbmQgUmF0ZXMgYXJlIDcxLgoKCmBgYHtyfQoKI1RvIGRldGVybWluZSB1cHBlciBhbmQgbG93ZXIgZmVuY2VzIGZvciBDb3VudCBhbmQgUmF0ZSB2YXJpYWJsZS4KCiMgTG93ZXIgQ291bnQgcXVhbnRpbGUKcTEgPC0gcXVhbnRpbGUobmV3Y2FuY2VyX21lcmdlZDIkQ291bnQsIHByb2JzID0gMC4yNSkgCgojIFVwcGVyIENvdW50IHF1YW50aWxlCnEzIDwtIHF1YW50aWxlKG5ld2NhbmNlcl9tZXJnZWQyJENvdW50LCBwcm9icyA9IDAuNzUpIAoKIyBMb3dlciBmZW5jZSB2YWx1ZSBmb3IgQ291bnQgdmFyaWFibGUKY291bnRfbG93X2ZlbmNlIDwtIHExIC0gKDEuNSAqIGNvdW50X2lxcikgCgojIFVwcGVyIGZlbmNlIHZhbHVlIGZvciBDb3VudCB2YXJpYWJsZQpjb3VudF91cF9mZW5jZSAgPC0gcTMgKyAoMS41ICogY291bnRfaXFyKSAgCgpjb3VudF9sb3dfZmVuY2UKCmBgYApgYGB7cn0KCmNvdW50X3VwX2ZlbmNlCgpgYGAKQXMgY2FuIGJlIHNlZW4gYWJvdmUsIHRoZSBmZW5jZSB2YWx1ZXMgZm9yIGJvdGggdmFyaWFibGVzIGFyZSBhcyBmb2xsb3dzOgoKRmVuY2UJICBDb3VudAkgICAgUmF0ZQpMb3cJICAgIC0xMDYuNQkgICAgLTEwNi41ClVwCSAgICAgMTc3LjUgICAgICAgMTc3LjUKCmBgYHtyfQoKIyBDb3VudCBvdXRsaWVycyBiZWxvdyBsb3cgZmVuY2UKY291bnRfdXBfbG93X291dGxpZXJzIDwtIHdoaWNoKG5ld2NhbmNlcl9tZXJnZWQyJENvdW50IDwgY291bnRfbG93X2ZlbmNlKQoKIyBDb3VudCBvdXRsaWVycyBhYm92ZSB1cCBmZW5jZQpjb3VudF91cF9vdXRsaWVycyA8LSB3aGljaChuZXdjYW5jZXJfbWVyZ2VkMiRDb3VudCA+IGNvdW50X3VwX2ZlbmNlKSAgICAgIAoKCiMgTnVtYmVyIG9mIENvdW50IG91dGxpZXJzIGJlbG93IGxvdyBmZW5jZSBhbmQgYWJvdmUgbG93IGZlbmNlCgpsZW5ndGgoY291bnRfdXBfbG93X291dGxpZXJzKQpsZW5ndGgoY291bnRfdXBfb3V0bGllcnMpCgoKCmBgYAoKYGBge3J9CgoKIyBSYXRlIG91dGxpZXJzIGJlbG93IGxvdyBmZW5jZQpyYXRlX3VwX2xvd19vdXRsaWVycyA8LSB3aGljaChuZXdjYW5jZXJfbWVyZ2VkMiRSYXRlIDwgY291bnRfbG93X2ZlbmNlKQoKIyBSYXRlIG91dGxpZXJzIGFib3ZlIHVwIGZlbmNlCnJhdGVfdXBfb3V0bGllcnMgPC0gd2hpY2gobmV3Y2FuY2VyX21lcmdlZDIkUmF0ZSA+IGNvdW50X3VwX2ZlbmNlKSAgICAgCgojIE51bWJlciBvZiBDb3VudCBvdXRsaWVycyBiZWxvdyBsb3cgZmVuY2UgYW5kIGFib3ZlIGxvdyBmZW5jZQpsZW5ndGgocmF0ZV91cF9sb3dfb3V0bGllcnMpCmxlbmd0aChyYXRlX3VwX291dGxpZXJzKQpgYGAKCk91dGxpZXJzIGZvciBib3RoIHZhcmlhYmxlcwpGZW5jZQkgQ291bnQJIFJhdGUKTG93CSAgICAgMAkgICAgMApVcCAgIAkzODQxCSAgIDM4NDEKCgpJbXB1dGluZyB0aGVzZSBvdXRsaWVycyB3aXRoIHRoZSBtZWFuIG9mIHRoZSBDb3VudCBhbmQgUmF0ZSB2YWx1ZXMuCgpgYGB7cn0KCiNJbXB1dGUgQ291bnQgb3V0bGllcnMgd2l0aCBtZWFuIG9mIENvdW50CgpuZXdjYW5jZXJfbWVyZ2VkMiRDb3VudFtjb3VudF91cF9vdXRsaWVyc10gPC0gbWVhbihuZXdjYW5jZXJfbWVyZ2VkMiRDb3VudCkKbGVuZ3RoKG5ld2NhbmNlcl9tZXJnZWQyJENvdW50W2NvdW50X3VwX291dGxpZXJzXSkKCgojSW1wdXRlIFJhdGUgb3V0bGllcnMgd2l0aCBtZWFuIG9mIFJhdGUKCm5ld2NhbmNlcl9tZXJnZWQyJFJhdGVbcmF0ZV91cF9vdXRsaWVyc10gPC0gbWVhbihuZXdjYW5jZXJfbWVyZ2VkMiRSYXRlKQpsZW5ndGgobmV3Y2FuY2VyX21lcmdlZDIkUmF0ZVtyYXRlX3VwX291dGxpZXJzXSkKCmBgYApCb3RoIHZhcmlhYmxlcyBhbGlnbnMgd2l0aCB0aGUgaW5pdGlhbCBvdXRsaWVyIGFtb3VudC4KCmBgYHtyfQpgYGAKCgpgYGB7cn0KbmV3Y2FuY2VyX21lcmdlZDIkQ291bnQgJT4lCiAgYm94cGxvdChtYWluID0gIkNhbmNlciBDb3VudHMiLCB5bGFiID0gIkNvdW50IiwgY29sID0gImJsdWUiKQoKYGBgCmBgYHtyfQoKbmV3Y2FuY2VyX21lcmdlZDIkUmF0ZSAlPiUKICBib3hwbG90KG1haW4gPSAiQ2FuY2VyIFJhdGUiLCB5bGFiID0gIlJhdGUiLCBjb2wgPSAieWVsbG93IikgIApgYGAKCiMjCVRyYW5zZm9ybSAKCgpBcHBseWluZyBsb2cgdHJhbnNmb3JtYXRpb24gaXMgdGhlIGJlc3Qgd2F5IHRvIHJlZHVjZSBvdXRsaWVycyBhbmQgZGVjcmVhc2UgdGhlIHNrZXduZXNzIG9mIHRoZSBlbXBpcmljYWwgZGlzdHJpYnV0aW9uLgoKV2hlbiBhcHBseWluZyBsb2cgdHJhbnNmb3JtYXRpb24gdG8gdGhlIGJveHBsb3QsIHRoZSBvdXRsaWVycyBhcmUgcmVkdWNlZCBhbmQgb25seSAxIG91dGxpZXIgaXMgZGV0ZWN0ZWQgd2hpY2ggaXMgbm9ybWFsIGFzIGl0IGlzIG5vdCBmYWxsIGZhciBhd2F5IGZyb20gdGhlIE1heGltdW0gdmFsdWUuCgoKYGBge3J9CgpoaXN0KG5ld2NhbmNlcl9tZXJnZWQyJFJhdGUsIG1haW4gPSAiSGlzdG9ncmFtIG9mIENhbmNlciBSYXRlcyIsCiAgICAgeGxhYj0iQ2FuY2VyIFJhdGUiKQoKYGBgCmBgYHtyfQoKCmxvZ19yYXRlPC0gbG9nMTAobmV3Y2FuY2VyX21lcmdlZDIkUmF0ZSkKaGlzdChsb2dfcmF0ZSwgCiAgICAgbWFpbiA9ICJIaXN0b2dyYW0gb2YgbG9nIDEwIGxvZyBDYW5jZXIgUmF0ZXMiLCAKICAgICB4bGFiID0gIkNhbmNlciBSYXRlIiwgY29sID0gImJsdWUiLCBib3JkZXIgPSAiZ3JleSIpCmBgYAojIENvbmNsdXNpb24KCldlIGNvbmNsdWRlIHdpdGggbW9ydGFsaXR5IHJhdGVzIHZhcnlpbmcgYmV0d2VlbiBhZ2UgZ3JvdXBzIGFuZCB0aGUgbG93ZXN0IHNwZWN0cnVtIGNhbmNlciByZWxhdGVkIGNhdXNlIG9mIGRlYXRoIGZvciBBZ2UgMC05ICBhbmQgQWdlIDEwLTE5IGlzIEFjdXRlIGx5bXBob2JsYXN0aWMgbGV1a2FlbWlhLiAKCkFkdWx0cyBhcmUgYXQgaGlnaCByaXNrIGZyb20gY29sb24gY2FuY2VyLiAKQW5kIGluIFNlbmlvcnMgUmVjdGFsIGNhbmNlciBpcyB0aGUgYmlnZ2VzdCBraWxsZXIgd2l0aCBOb24tSG9kZ2tpbiBseW1waG9tYSBoYXZpbmcgdGhlIGhpZ2hlc3QgcmF0ZXMgb2YgaW5jaWRlbnRzLgoKCmBgYAojIFJlZmVyZW5jZQoKQm9laG1rZSwgQkMgMjAxNiwgRGF0YSBXcmFuZ2xpbmcgd2l0aCBSIChMaW5rcyB0byBhbiBleHRlcm5hbCBzaXRlLiksIFNwcmluZ2VyIEludGVybmF0aW9uYWwgUHVibGlzaGluZywgQ2hhbSwgU3dpdHplcmxhbmQuCgpXaWNraGFtLCBIIDIwMTksIEFkdmFuY2VkIFIgKExpbmtzIHRvIGFuIGV4dGVybmFsIHNpdGUuKSwgQ1JDIHByZXNzLgoKCjxicj4KPGJyPgoK