Rpubs link for this study: http://rpubs.com/The_Analyst/431304

Required packages

library(readr)
library(magrittr) 
library(dplyr)
library(tidyr)
library(readxl)
library(lubridate) 
library(stringr) 
library(ggplot2)
library(mlr)

Executive Summary

Data collection

Melbourne housing data is taken from the kaggle data set hyperlink,Melbourne Housing Market and Melbourne Crime Statistics has been sourced from hyperlink,Melbourne Crime Statistics

Data_Dictionary :

The followig variables are required:

Melbourne Housing Data:
  • Suburb: Suburbs
  • Rooms: Number of rooms
  • Price: Price in Australian dollars
  • Type: br - bedroom(s); h - house,cottage,villa, semi,terrace; u - unit, duplex; t - townhouse; dev site - development site; o res - other residential.
  • Date: Date sold
  • Distance: Distance from CBD in Kilometres
  • Regionname: General Region (West, North West, North, North east …etc)
Crime Statistics data:
  • Year ending June: Year of crime
  • Postcode: Self explanatory
  • Suburb/Town Name: Suburb
  • Offence Division: offence category
  • Incidents Recorded:Number of incidents
Crime_Inc_PostCode <- read_excel("Data_tables_Criminal_Incidents_Visualisation_year_ending_June_2018.xlsx", sheet = "Table 07" )
head(Crime_Inc_PostCode)
Melb <- read_csv("MELBOURNE_HOUSE_PRICES_LESS.csv") #Read the Housing data set
Parsed with column specification:
cols(
  Suburb = col_character(),
  Address = col_character(),
  Rooms = col_integer(),
  Type = col_character(),
  Price = col_integer(),
  Method = col_character(),
  SellerG = col_character(),
  Date = col_character(),
  Postcode = col_integer(),
  Regionname = col_character(),
  Propertycount = col_integer(),
  Distance = col_double(),
  CouncilArea = col_character()
)
head(Melb)

Structure and manipulations of Melbourne Housing data set

sum(is.na(Melb$Price)) #check for NA
[1] 13836
Melb_drop_na <- drop_na(Melb,Price) #handling NA's
Melb_filtered_data_2017 <- Melb_drop_na %>% filter( Type == 'h' & Rooms == 3 & Distance <100 & Date >= '2017-01-01') #apply the filtering criteria on the dataset
sapply(Melb_filtered_data_2017, class)
       Suburb       Address         Rooms          Type         Price 
  "character"   "character"     "integer"   "character"     "integer" 
       Method       SellerG          Date      Postcode    Regionname 
  "character"   "character"   "character"     "integer"   "character" 
Propertycount      Distance   CouncilArea 
    "integer"     "numeric"   "character" 
#factorise the Region Names 
Melb_filtered_data_2017$Regionname <- as.factor(Melb_filtered_data_2017$Regionname)
levels(Melb_filtered_data_2017$Regionname)
[1] "Eastern Metropolitan"       "Eastern Victoria"          
[3] "Northern Metropolitan"      "Northern Victoria"         
[5] "South-Eastern Metropolitan" "Southern Metropolitan"     
[7] "Western Metropolitan"       "Western Victoria"          
Melb_filtered_data_2017$Date<- dmy(Melb_filtered_data_2017$Date) #date transformation 
#to check the month in the date 
Melb_filtered_data_2017$Date %>% month(label = TRUE, abbr = FALSE) %>% head(10)
 [1] February February February February February February February February
 [9] February February
12 Levels: January < February < March < April < May < June < ... < December
#transform the Suburb variable to upper case
Melb_filtered_data_2017$Suburb <- toupper(Melb_filtered_data_2017$Suburb ) 

Structure and manipulations of Melbourne Crime data set

names(Crime_Inc_PostCode) #check the names of  dataset
[1] "Year ending June"    "Postcode"            "Suburb/Town Name"   
[4] "Offence Division"    "Offence Subdivision" "Offence Subgroup"   
[7] "Incidents Recorded" 
#rename the columns to make increase readablity
Crime_Inc_PostCode <- rename(Crime_Inc_PostCode,
                       Year = `Year ending June`,
                       Suburb = `Suburb/Town Name`,
                       Offence = `Offence Division`,
                       No_of_Incident = `Incidents Recorded`)
sapply(Crime_Inc_PostCode,class) #check datatypes 
               Year            Postcode              Suburb 
          "numeric"           "numeric"         "character" 
            Offence Offence Subdivision    Offence Subgroup 
        "character"         "character"         "character" 
     No_of_Incident 
          "numeric" 
#factorise the offence variable 
Crime_Inc_PostCode$Offence <- factor(Crime_Inc_PostCode$Offence, levels = c("A Crimes against the person","B Property and deception offences","C Drug offences","D Public order and security offences","E Justice procedures offences","F Other offences"),
       labels = c("Crime against person","Property offences","Drug offences","Public order and security","Justice Procedures","Others"))
Crime_Inc_PostCode$Suburb <- as.factor(Crime_Inc_PostCode$Suburb)
#filter the data for 2017 crime statistics 
Crime_Inc_PostCode_2017 <- Crime_Inc_PostCode %>% filter(Year %in% "2017")
sum(is.na(Crime_Inc_PostCode_2017)) #check for NA's
[1] 0

Summarise the data and create new variables.

# Summarise the data and create new columns fro median price and distance. 
Melb_working_set_2017 <- Melb_filtered_data_2017 %>%  group_by(Postcode,Suburb) %>% 
  mutate(median_price = median(Price),
         median_distance = median(Distance)
         ) %>% distinct(Postcode,median_price,median_distance,count,Regionname)
Trying to compute distinct() for variables not found in the data:
- `count`
This is an error, but only a warning is raised for compatibility reasons.
The following variables will be used:
- Postcode
- median_price
- median_distance
- Regionname
Melb_working_set_2017 %>% head(5)
#Summarise the Crime Data to find No of Incidents per Suburb
Suburbs_Crime_2017 <- Crime_Inc_PostCode_2017 %>% group_by(Suburb) %>% summarise(
  Total_Incidents = sum(No_of_Incident)
)
Suburbs_Crime_2017 %>% head(5)

Join the data sets

#Join the Data sets
# On the Melbourne Housing Data , Join the Number of Criminal Incidents , per Suburb
Ready_to_work_data <- left_join(Melb_working_set_2017, Suburbs_Crime_2017, by="Suburb") 
Column `Suburb` joining character vector and factor, coercing into character vector
Ready_to_work_data %>% head(5)

Scanning final data set for inconsistency

#Investigatge if there is any NA's in this data set as result of Join
sum(is.na(Ready_to_work_data))
[1] 1
Ready_to_work_data[!complete.cases(Ready_to_work_data),] #inspect NA rows
#Eliminate this suburb as we dont have info about the total number of Incidents
Ready_to_work_data <- Ready_to_work_data[complete.cases(Ready_to_work_data),]
#create a rule to check if the values are non negative
Rule <- editset(c("median_price >= 0", "median_distance >= 0","Total_Incidents>=0"))
Rule

Edit set:
num1 : 0 <= median_price
num2 : 0 <= median_distance
num3 : 0 <= Total_Incidents 
#apply rule on the Final data set 
rules_violated <- violatedEdits(Rule,Ready_to_work_data)
summary(rules_violated) #visualise if any rules is violated
No violations detected, 0 checks evaluated to NA
NULL
Ready_to_work_data %>% head(5)

Identify the suburb with higher price median (outliers)

outliers_price = boxplot(Ready_to_work_data$median_price ~ Ready_to_work_data$Regionname,plot=FALSE)$out
#Make a new column for labelling the outliers : Suburb/Region with highest number of crime incidents
Ready_to_work_data$label_price <- ifelse((Ready_to_work_data$median_price %in% outliers_price & Ready_to_work_data$median_price>3000000),
                                    paste(Ready_to_work_data$Suburb,"\n",
Ready_to_work_data$median_price),"")
ggplot(aes(y=median_price,x=Regionname,fill=Regionname),data = Ready_to_work_data)+geom_boxplot()+ geom_text(aes(label=label_price))+
  theme(legend.position = "bottom")+scale_x_discrete(labels=c("E.Metro","E.Vic","N.Metro",
                                                              "N.Vic","S-E.Metro","S.Metro","W.Metro",
                                                              "W.Vic")) +  
  ggtitle("Melbourne Property Price  by Region") + 
  xlab("Region Names") + ylab("Median_Price") +
  theme(
    
    plot.title = element_text(colour = "Black",size = 14, face = "bold", hjust = 0.5 )
  )

Identify the suburb with higher crime incidents (outliers)

outliers_crime = boxplot(Ready_to_work_data$Total_Incidents ~ Ready_to_work_data$Regionname,plot=FALSE)$out
#Make a new column for labelling the outliers : Suburb/Region with highest number of crime incidents. Use Paste to concatenate strings. 
Ready_to_work_data$label_crime <- ifelse((Ready_to_work_data$Total_Incidents %in% outliers_crime & Ready_to_work_data$Total_Incidents>15000),
                                    paste(Ready_to_work_data$Suburb,"\n",
Ready_to_work_data$Total_Incidents),"")
#Make a boxplot 
ggplot(aes(y=Total_Incidents,x=Regionname,fill=Regionname),data = Ready_to_work_data)+geom_boxplot()+
  #theme_grey(base_size = 10) +
  theme(legend.position = "bottom")+
  scale_x_discrete(labels=c("E.Metro","E.Vic","N.Metro",
                                                              "N.Vic","S-E.Metro","S.Metro","W.Metro",                                                              "W.Vic")) + geom_text(aes(label=label_crime))+
  ggtitle("Melbourne Crime Incidents by Region") + 
  xlab("Region Names") + ylab("No of Crime Incidents") +
  theme(
    
    plot.title = element_text(colour = "Black",size = 14, face = "bold", hjust = 0.5 )
  )

Excluding outlier

Data_without_Melb_Suburb <- Ready_to_work_data %>% filter(!Suburb %in% "MELBOURNE")
ggplot(aes(y=Total_Incidents,x=Regionname,fill=Regionname),data = Data_without_Melb_Suburb)+geom_boxplot()+
  # theme_grey(base_size = 10)+
  theme(legend.position = "bottom")+
  scale_x_discrete(labels=c("E.Metro","E.Vic","N.Metro",
                                                              "N.Vic","S-E.Metro","S.Metro","W.Metro",                                                              "W.Vic")) +
  ggtitle("Melbourne Crime Incidents by Region") + 
  xlab("Region Names") + ylab("No of Crime Incidents") +
  theme(
    
    plot.title = element_text(colour = "Black",size = 14, face = "bold", hjust = 0.5 )
  )

Multivariate outlier detection:

   qplot(data=Ready_to_work_data, x=median_price, y=Total_Incidents) +geom_text(aes(label=ifelse((Total_Incidents>5*IQR(Total_Incidents)|median_price>4*IQR(median_price)),paste(Suburb,"\n"),"")), hjust=1.1) + 
  ggtitle("Melbourne:  Number of crime incidents against property prices") + 
  ylab("No of Crime Incidents") + xlab("median property price") +
  theme(
    plot.title = element_text(colour = "Black",size = 14, face = "bold", hjust = 0.5 )
  ) # 5*IQR / 4*IQR - random values just for labelling most extreme points. 

Data Transformation for further processing

Further I wanted to explore if there is any correlation between the price and distance (from the central).

  1. Create a new copy of the data set with only median_distance and median_price.
  2. Check for the distrbution of the data.
  3. Apply suitable mathematical operations to transform the data as close to normal as possible. In this case sqrt and logarithmic fuctions gave the distribuions closer to normality.
  4. Transform the data frame copy accordingly.
#create a new copy of data for this task 
ML_dataset <- Melb_filtered_data_2017 %>% group_by(Suburb) %>% summarise( median_distance =median(Distance),
median_price= median(Price)) %>% select(median_distance,median_price)
ML_dataset <- as.data.frame(ML_dataset) #Covert the tibble into a df
hist(ML_dataset$median_distance) #right skewed

hist(ML_dataset$median_price) #right skewed

#apply transformation to achieve normality
hist(sqrt(ML_dataset$median_distance))

hist(log10(ML_dataset$median_price))

# transform the data frame accordingly 
for (i in length(ML_dataset)) {
  
  ML_dataset$median_distance <- sqrt(ML_dataset$median_distance)
  ML_dataset$median_price <- log10(ML_dataset$median_price)
  
}

Machine Learning model to predict the price using distance

Steps:
  • Step 1: Define the task as regression analysis with Price as target variable.
  • Step 2: We will use the classic regression as our algorithm
  • Step 3: Fitting the model
    • Divide the data into train and test set.
    • Train the learner on the training set.
  • Step 4 : Make pricing predictions using the trained model to predict the price in the test set.
  • Step 5: Evaluating the learner: We calculate the mean square error and mean absolute error to see the performance metric of our learner.
# Task
ML_dataset_task <- makeRegrTask(data = ML_dataset,target = "median_price")
#Learner algorithm - classic regression 
lrnr = makeLearner("regr.glm")
#definine the train and test sets
n=nrow(ML_dataset)
training_set = sample(n, size = 2/3*n)
testing_set = setdiff(1:n, training_set)
# make a model of the data and train the learner on the task using the training set
pricing_model = mlr::train(learner = lrnr, task = ML_dataset_task, subset = training_set)
pricing_model
Model for learner.id=regr.glm; learner.class=regr.glm
Trained on: task.id = ML_dataset; obs = 218; features = 1
Hyperparameters: family=gaussian,model=FALSE
#predict the price 
pred_price = predict(pricing_model, task = ML_dataset_task, subset = testing_set)
#evaluate the learner 
performance(pred_price,measures = list(mse,mae))
       mse        mae 
0.01667724 0.10352674 

Findings:

As the absolute error is small, the model performed well against the given set of data. The model can be applied on additional data sets to evaluate its performance. Further an F test can be done for overall model significance.

Further steps:

The data can be used to find a correlation between the housing prices and distance (from Melbourne Central). Also if the relationship changes travelling the same distance but in different metropolitan regions. Further, Crime statistics for the suburb can be looked to explore a correlation, if any between the housing price and crime in that suburb.



LS0tCnRpdGxlOiAiTUFUSDIzNDkgU2VtZXN0ZXIgMiwgMjAxOCIKYXV0aG9yOiAiQWhtYWQgSGFzbmFpbiBzMzcxMjUzOCIKc3VidGl0bGU6IEFzc2lnbm1lbnQgMwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KClJwdWJzIGxpbmsgZm9yIHRoaXMgc3R1ZHk6IGh0dHA6Ly9ycHVicy5jb20vVGhlX0FuYWx5c3QvNDMxMzA0IAoKCiMjIFJlcXVpcmVkIHBhY2thZ2VzIAoKYGBge3J9CgpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KG1hZ3JpdHRyKSAKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkobHVicmlkYXRlKSAKbGlicmFyeShzdHJpbmdyKSAKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KG1scikKYGBgCgojIyBFeGVjdXRpdmUgU3VtbWFyeSAKCiogSW4gdGhpcyByZXBvcnQsIHdlIGFyZSB0cnlpbmcgdG8gYW5hbHlzZSB0aGUgaG91c2luZyBwcmljZXMgYW5kIGNyaW1lIHN0YXRpc3RpY3MgZm9yIHRoZSBrZXkgcmVnaW9uYWwgYXJlYXMgb2YgTWVsYm91cm5lIGFuc3dlcmluZyBxdWVzdGlvbnMgbGlrZSB3aGljaCByZWdpb24gaGFzIHRoZSBoaWdoZXN0IHByb3BlcnR5IHByaWNlIG9yIHdoaWNoIHJlZ2lvbiBoYXMgdGhlIGxvd2VzdCBudW1iZXIgb2YgcmVwb3J0ZWQgY3JpbWUgaW5jaWRlbnRzLgoqIFR3byBkYXRhIHNldHMgaGF2ZSBiZWVuIHVzZWQgZm9yIHRoaXMgcHVycG9zZS4KKiBUaGUgRmlyc3Qgb25lIGlzIHRoZSBNZWxib3VybmUgSG91c2luZyBEYXRhIHdoaWNoIGNvbnRhaW5zIHRoZSBwcmljZXMgb2YgcHJvcGVydGllcyBzb2xkLCBhbG9uZyB3aXRoIHRoZSB0eXBlIG9mIHByb3BlcnR5LCBpdHMgc3VidXJiLCByZWdpb24gZXRjLiBJIGhhdmUgcmVzdHJpY3RlZCBteSBhbmFseXNpcyB0byB0aHJlZSBiZWRyb29tIGxhbmRlZCBob3VzZXMgc29sZCBpbiAyMDE3IGFuZCB3aXRoaW4gMTAwa20gZnJvbSBDQkQuIEFsc28gdGhlIHJvd3Mgd2hlcmUgcHJpY2VzIGFyZSBub3QgYXZhaWxhYmxlIGhhdmUgYmVlbiByZW1vdmVkIGFzIHRoZSBob3VzaW5nIHByaWNlcyBhcmUgc2tld2VkIGFuZCBpbXB1dGluZyB0aGVtIHdpdGggbWVhbiBvciBtZWRpYW4gbWlnaHQgaW5mbHVlbmNlIHRoZSB3aG9sZSByZXN1bHQuIFJlZ2lvbiBuYW1lcyBoYXZlIGJlZW4gZmFjdG9yaXNlZCBhbmQgb3RoZXIgbmVjZXNzYXJ5IGRhdGEgdHJhbnNmb3JtYXRpb24gbGlrZSBjaGFuZ2luZyB0aGUgY2xhc3Mgb2YgRGF0ZSB2YXJpYWJsZSBoYXMgYmVlbiBkb25lLiBQb3N0IHRoaXMgdGhlIG1lZGlhbiBwcmljZSBhbmQgZGlzdGFuY2UgaGF2ZSBiZWVuIGNhbGN1bGF0ZWQuIAoqIFRoZSBjcmltZSBzdGF0aXN0aWNzIGRhdGEgaGFzIGJlZW4gaW1wb3J0ZWQgIGFuZCBpdHMgdmFyaWFibGUgbmFtZXMgaGF2ZSBiZWVuIHVwZGF0ZWQuVGhpcyB3YXMgZm9sbG93ZWQgYnkgY29udmVydGluZyB2YXJpYWJsZXMgaW50byBzdWl0YWJsZSBkYXRhIHR5cGVzIGFuZCBsYWJlbGxpbmcgdGhlIGZhY3RvcnMuIFRoZSBkYXRhIGZvciB5ZWFyIDIwMTcgaGFzIGJlZW4gZmlsdGVyZWQgYW5kIGNoZWNrZWQgZm9yIHBvc3NpYmxlIG1pc3NpbmcgdmFsdWVzLiAKKiBQb3N0IHNhbml0YXRpb24sIHRoZSBjcmltZSBzdGF0aXN0aWNzIGRhdGEgaGFzIGJlZW4gam9pbmVkIHRvIHRoZSBob3VzaW5nIGRhdGEsIHVzaW5nIHN1YnVyYiBhcyB0aGUgZ3JvdXBpbmcgdmFyaWFibGUuIE1pc3NpbmcgdmFsdWVzIHdlcmUgaWRlbnRpZmllZCBhbmQgaGFuZGxlZC5PYnZpb3VzIGVycm9yIGFuZCBpbmNvbnNpc3RlbmN5IHdhcyBjaGVja2VkLiAKKiBBcyB0aGUgbmV4dCBsb2dpY2FsIHN0ZXAsIHdlIGhhdmUgdXNlZCBhIGJveHBsb3QgdG8gbG9vayBpbnRvIHRoZSBvdXRsaWVycyBmb3IgUHJpY2UgYW5kIENyaW1lLiBJbiB0aGlzIGNhc2UgdGhlIHN1YnVyYnMgd2hvc2UgcHJvcGVydHkgcHJpY2Ugb3IgY3JpbWUgcmF0ZSBhcmUgc2lnbmlmaWNhbnRseSBoaWdoZXIgdGhhbiB0aGF0IG9mIG90aGVyIHN1YnVyYnMgaW4gdGhlIHNhbWUgcmVnaW9uLiAgCkkgaGF2ZSBub3QgZWxpbWluYXRlZCB0aGUgb3V0bGllcnMgYXMgdGhleSBhcmUgbm90IGZhbHNlIGFsYXJtcyBidXQgYSByZWFsIGRlcGljdGlvbiBvZiB0aGUgaGlnaCBwcm9wZXJ0eSBwcmljZSBvciBjcmltZSBzdGF0cyBvZiBhIHBhcnRpY3VsYXIgc3VidXJiLiAKKiBHb2luZyBmdXJ0aGVyLCBJIGhhdmUgYXBwbGllZCBtYXRoZW1hdGljYWwgZnVuY3Rpb25zIHRvIHRyYW5zZm9ybSB0aGUgbWVkaWFuIHByaWNlIGFuZCBkaXN0YW5jZSwgdG8gYWNoaWV2ZSBub3JtYWxpdHkgLCBwb3N0IHdoaWNoIHdlIGhhdmUgdXNlZCB0aGUgbWxyIHBhY2thZ2UgdG8gY29tZSB3aXRoIGEgbW9kZWwgdG8gcHJlZGljdCB0aGUgbWVkaWFuIHByaWNlIG9mIGEgcHJvcGVydHkgdXNpbmcgb3RoZXIgdmFyaWFibGVzLiBXZSBoYXZlIHVzZWQgdGhlIGdlbmVyYWxpc2VkIGxpbmVhciBtb2RlbCBhbmQgYWNoaWV2ZWQgYSBwZXJmb3JtYW5jZSBzdGF0aXN0aWNzIG9yIG1lYW4gYWJzb2x1dGUgZXJyb3IgYmVsb3cgMC4xLiAKKiBUaGlzIGRhdGFzZXQgY2FuIGJlIHVzZWQgaW4gZnV0dXJlIGZvciBmdXJ0aGVyIHN0YXRpc3RpY2FsIGFuYWx5c2lzLiAKCiMjIERhdGEgY29sbGVjdGlvbiAKCk1lbGJvdXJuZSBob3VzaW5nIGRhdGEgaXMgdGFrZW4gZnJvbSB0aGUga2FnZ2xlIGRhdGEgc2V0IGh5cGVybGluayxbTWVsYm91cm5lIEhvdXNpbmcgTWFya2V0XShodHRwczovL3d3dy5rYWdnbGUuY29tL2FudGhvbnlwaW5vL21lbGJvdXJuZS1ob3VzaW5nLW1hcmtldC9ob21lKSBhbmQgTWVsYm91cm5lIENyaW1lIFN0YXRpc3RpY3MgaGFzIGJlZW4gc291cmNlZCBmcm9tIGh5cGVybGluayxbTWVsYm91cm5lIENyaW1lIFN0YXRpc3RpY3NdKGh0dHBzOi8vd3d3LmNyaW1lc3RhdGlzdGljcy52aWMuZ292LmF1KSAgIAoKIyMjIyMgRGF0YV9EaWN0aW9uYXJ5IDoKVGhlIGZvbGxvd2lnIHZhcmlhYmxlcyBhcmUgcmVxdWlyZWQ6CgojIyMjIyMgTWVsYm91cm5lIEhvdXNpbmcgRGF0YToKKiBTdWJ1cmI6IFN1YnVyYnMgCiogUm9vbXM6IE51bWJlciBvZiByb29tcwoqIFByaWNlOiBQcmljZSBpbiBBdXN0cmFsaWFuIGRvbGxhcnMKKiBUeXBlOiBiciAtIGJlZHJvb20ocyk7IGggLSBob3VzZSxjb3R0YWdlLHZpbGxhLCBzZW1pLHRlcnJhY2U7IHUgLSB1bml0LCBkdXBsZXg7IHQgLSB0b3duaG91c2U7IGRldiBzaXRlIC0gZGV2ZWxvcG1lbnQgc2l0ZTsgbyByZXMgLSBvdGhlciByZXNpZGVudGlhbC4KKiBEYXRlOiBEYXRlIHNvbGQKKiBEaXN0YW5jZTogRGlzdGFuY2UgZnJvbSBDQkQgaW4gS2lsb21ldHJlcwoqIFJlZ2lvbm5hbWU6IEdlbmVyYWwgUmVnaW9uIChXZXN0LCBOb3J0aCBXZXN0LCBOb3J0aCwgTm9ydGggZWFzdCAuLi5ldGMpCgojIyMjIyMgQ3JpbWUgU3RhdGlzdGljcyBkYXRhOiAKKiBZZWFyIGVuZGluZyBKdW5lOiBZZWFyIG9mIGNyaW1lIAoqIFBvc3Rjb2RlOiBTZWxmIGV4cGxhbmF0b3J5CiogU3VidXJiL1Rvd24gTmFtZTogU3VidXJiCiogT2ZmZW5jZSBEaXZpc2lvbjogb2ZmZW5jZSBjYXRlZ29yeQoqIEluY2lkZW50cyBSZWNvcmRlZDpOdW1iZXIgb2YgaW5jaWRlbnRzIAoKCgpgYGB7cn0KCkNyaW1lX0luY19Qb3N0Q29kZSA8LSByZWFkX2V4Y2VsKCJEYXRhX3RhYmxlc19DcmltaW5hbF9JbmNpZGVudHNfVmlzdWFsaXNhdGlvbl95ZWFyX2VuZGluZ19KdW5lXzIwMTgueGxzeCIsIHNoZWV0ID0gIlRhYmxlIDA3IiApCmhlYWQoQ3JpbWVfSW5jX1Bvc3RDb2RlKQoKTWVsYiA8LSByZWFkX2NzdigiTUVMQk9VUk5FX0hPVVNFX1BSSUNFU19MRVNTLmNzdiIpICNSZWFkIHRoZSBIb3VzaW5nIGRhdGEgc2V0CmhlYWQoTWVsYikKCgpgYGAKCgojIyBTdHJ1Y3R1cmUgYW5kIG1hbmlwdWxhdGlvbnMgb2YgTWVsYm91cm5lIEhvdXNpbmcgZGF0YSBzZXQKCiogQ2hlY2sgZm9yIE5Bcy4gCk91dCBvZiA2MDY3MiByb3dzIHRoZXJlIGFyZSAxMyw4MzYgaXRlbXMgd2hlcmUgdGhlIFByaWNlIGlzIG1pc3NpbmcuCldlIHdpbGwgIGlnbm9yZSB0aG9zZSBjYXNlcyBhbmQgb25seSBsb29rIGF0IHRoZSByb3dzIHdoZXJlIHdlIGhhdmUgdGhlIHByaWNlIGxpc3RlZC4KKiBGb3IgdGhpcyBhc3NpZ25tZW50IHdlIGFyZSBvbmx5IGxvb2tpbmcgYXQgdGhlIHByaWNlIGluIDIwMTcgZm9yIHRoZSBsYW5kZWQgaG91c2VzICgnaCcpIHdpdGggMyBiZWRyb29tcyBhbmQgd2l0aGluIDEwMGttcyAoZnJvbSBNZWxib3VybmUgQ2VudHJhbCkuCiogQ2hlY2sgdGhlIGNsYXNzIG9mIHZhcmlhYmxlcy4gCiogRmFjdG9yaXNlIHRoZSByZWdpb24gbmFtZXMuICAKKiBDaGFuZ2UgdGhlIHR5cGUgb2YgZGF0ZSB2YXJpYWJsZSBmcm9tIG51bWVyaWMgdG8gZGF0ZS4gIAoqIFRyYW5zZm9ybSB0aGUgc3VidXJiIGludG8gdXBwZXIgY2FzZSB0byBtYXRjaCB3aXRoIHRoZSBvdGhlciBkYXRhIHNldCBhdCB0aW1lIG9mIGpvaW4uIAoKCmBgYHtyfQoKc3VtKGlzLm5hKE1lbGIkUHJpY2UpKSAjY2hlY2sgZm9yIE5BCgpNZWxiX2Ryb3BfbmEgPC0gZHJvcF9uYShNZWxiLFByaWNlKSAjaGFuZGxpbmcgTkEncwpNZWxiX2ZpbHRlcmVkX2RhdGFfMjAxNyA8LSBNZWxiX2Ryb3BfbmEgJT4lIGZpbHRlciggVHlwZSA9PSAnaCcgJiBSb29tcyA9PSAzICYgRGlzdGFuY2UgPDEwMCAmIERhdGUgPj0gJzIwMTctMDEtMDEnKSAjYXBwbHkgdGhlIGZpbHRlcmluZyBjcml0ZXJpYSBvbiB0aGUgZGF0YXNldAoKCnNhcHBseShNZWxiX2ZpbHRlcmVkX2RhdGFfMjAxNywgY2xhc3MpCgojZmFjdG9yaXNlIHRoZSBSZWdpb24gTmFtZXMgCk1lbGJfZmlsdGVyZWRfZGF0YV8yMDE3JFJlZ2lvbm5hbWUgPC0gYXMuZmFjdG9yKE1lbGJfZmlsdGVyZWRfZGF0YV8yMDE3JFJlZ2lvbm5hbWUpCmxldmVscyhNZWxiX2ZpbHRlcmVkX2RhdGFfMjAxNyRSZWdpb25uYW1lKQoKTWVsYl9maWx0ZXJlZF9kYXRhXzIwMTckRGF0ZTwtIGRteShNZWxiX2ZpbHRlcmVkX2RhdGFfMjAxNyREYXRlKSAjZGF0ZSB0cmFuc2Zvcm1hdGlvbiAKCiN0byBjaGVjayB0aGUgbW9udGggaW4gdGhlIGRhdGUgCk1lbGJfZmlsdGVyZWRfZGF0YV8yMDE3JERhdGUgJT4lIG1vbnRoKGxhYmVsID0gVFJVRSwgYWJiciA9IEZBTFNFKSAlPiUgaGVhZCgxMCkKCiN0cmFuc2Zvcm0gdGhlIFN1YnVyYiB2YXJpYWJsZSB0byB1cHBlciBjYXNlCk1lbGJfZmlsdGVyZWRfZGF0YV8yMDE3JFN1YnVyYiA8LSB0b3VwcGVyKE1lbGJfZmlsdGVyZWRfZGF0YV8yMDE3JFN1YnVyYiApIAoKYGBgCgojIyBTdHJ1Y3R1cmUgYW5kIG1hbmlwdWxhdGlvbnMgb2YgTWVsYm91cm5lIENyaW1lIGRhdGEgc2V0CgoKKiBSZW5hbWUgdGhlIGNvbHVtbiBuYW1lIHRvIGVhc2UgcmVhZGFiaWxpdHkuIAoqIENoZWNrIHRoZSBkYXRhdHlwZSBvZiB2YXJpYWJsZXMuCiogRmFjdG9yaXNlIHRoZSBPZmZlbmNlIHR5cGUgIGFuZCBTdWJ1cmJzLiAKKiBGaWx0ZXIgdGhlIDIwMTcgY3JpbWUgc3RhdGlzdGljcyBkYXRhIGZvciB0aGUgcHVycG9zZSBvZiB0aGlzIHN0dWR5LiAKKiBDaGVjayBmb3IgTkEncyBpbiB0aGUgZmluYWwgZGF0YXNldC4gCgpgYGB7cn0KCm5hbWVzKENyaW1lX0luY19Qb3N0Q29kZSkgI2NoZWNrIHRoZSBuYW1lcyBvZiAgZGF0YXNldAoKI3JlbmFtZSB0aGUgY29sdW1ucyB0byBtYWtlIGluY3JlYXNlIHJlYWRhYmxpdHkKQ3JpbWVfSW5jX1Bvc3RDb2RlIDwtIHJlbmFtZShDcmltZV9JbmNfUG9zdENvZGUsCiAgICAgICAgICAgICAgICAgICAgICAgWWVhciA9IGBZZWFyIGVuZGluZyBKdW5lYCwKICAgICAgICAgICAgICAgICAgICAgICBTdWJ1cmIgPSBgU3VidXJiL1Rvd24gTmFtZWAsCiAgICAgICAgICAgICAgICAgICAgICAgT2ZmZW5jZSA9IGBPZmZlbmNlIERpdmlzaW9uYCwKICAgICAgICAgICAgICAgICAgICAgICBOb19vZl9JbmNpZGVudCA9IGBJbmNpZGVudHMgUmVjb3JkZWRgKQpzYXBwbHkoQ3JpbWVfSW5jX1Bvc3RDb2RlLGNsYXNzKSAjY2hlY2sgZGF0YXR5cGVzIAoKI2ZhY3RvcmlzZSB0aGUgb2ZmZW5jZSB2YXJpYWJsZSAKQ3JpbWVfSW5jX1Bvc3RDb2RlJE9mZmVuY2UgPC0gZmFjdG9yKENyaW1lX0luY19Qb3N0Q29kZSRPZmZlbmNlLCBsZXZlbHMgPSBjKCJBIENyaW1lcyBhZ2FpbnN0IHRoZSBwZXJzb24iLCJCIFByb3BlcnR5IGFuZCBkZWNlcHRpb24gb2ZmZW5jZXMiLCJDIERydWcgb2ZmZW5jZXMiLCJEIFB1YmxpYyBvcmRlciBhbmQgc2VjdXJpdHkgb2ZmZW5jZXMiLCJFIEp1c3RpY2UgcHJvY2VkdXJlcyBvZmZlbmNlcyIsIkYgT3RoZXIgb2ZmZW5jZXMiKSwKICAgICAgIGxhYmVscyA9IGMoIkNyaW1lIGFnYWluc3QgcGVyc29uIiwiUHJvcGVydHkgb2ZmZW5jZXMiLCJEcnVnIG9mZmVuY2VzIiwiUHVibGljIG9yZGVyIGFuZCBzZWN1cml0eSIsIkp1c3RpY2UgUHJvY2VkdXJlcyIsIk90aGVycyIpKQoKCkNyaW1lX0luY19Qb3N0Q29kZSRTdWJ1cmIgPC0gYXMuZmFjdG9yKENyaW1lX0luY19Qb3N0Q29kZSRTdWJ1cmIpCgoKI2ZpbHRlciB0aGUgZGF0YSBmb3IgMjAxNyBjcmltZSBzdGF0aXN0aWNzIApDcmltZV9JbmNfUG9zdENvZGVfMjAxNyA8LSBDcmltZV9JbmNfUG9zdENvZGUgJT4lIGZpbHRlcihZZWFyICVpbiUgIjIwMTciKQoKc3VtKGlzLm5hKENyaW1lX0luY19Qb3N0Q29kZV8yMDE3KSkgI2NoZWNrIGZvciBOQSdzCgpgYGAKCgojIyBTdW1tYXJpc2UgdGhlIGRhdGEgYW5kIGNyZWF0ZSBuZXcgdmFyaWFibGVzLiAKCiogVXNlIG11dGF0ZSB0byBjcmVhdGUgdHdvIG5ldyB2YXJpYWJsZXMgbWVkaWFuX3ByaWNlIGFuZCBtZWRpYW5fZGlzdGFuY2UgZm9yIHRoZSBNZWxib3VybmUgaG91c2luZyBkYXRhIHNldC4KKiBHcm91cCBieSBTdWJ1cmJzIGFuZCB1c2Ugc3VtbWFyaXNlIHRvIGNyZWF0ZSBUb3RhbF9JbmNpZGVudHMgYnkgU3VidXJicy4gCgpgYGB7cn0KCiMgU3VtbWFyaXNlIHRoZSBkYXRhIGFuZCBjcmVhdGUgbmV3IGNvbHVtbnMgZnJvIG1lZGlhbiBwcmljZSBhbmQgZGlzdGFuY2UuIAoKTWVsYl93b3JraW5nX3NldF8yMDE3IDwtIE1lbGJfZmlsdGVyZWRfZGF0YV8yMDE3ICU+JSAgZ3JvdXBfYnkoUG9zdGNvZGUsU3VidXJiKSAlPiUgCiAgbXV0YXRlKG1lZGlhbl9wcmljZSA9IG1lZGlhbihQcmljZSksCiAgICAgICAgIG1lZGlhbl9kaXN0YW5jZSA9IG1lZGlhbihEaXN0YW5jZSkKICAgICAgICAgKSAlPiUgZGlzdGluY3QoUG9zdGNvZGUsbWVkaWFuX3ByaWNlLG1lZGlhbl9kaXN0YW5jZSxjb3VudCxSZWdpb25uYW1lKQoKTWVsYl93b3JraW5nX3NldF8yMDE3ICU+JSBoZWFkKDUpCgojU3VtbWFyaXNlIHRoZSBDcmltZSBEYXRhIHRvIGZpbmQgTm8gb2YgSW5jaWRlbnRzIHBlciBTdWJ1cmIKU3VidXJic19DcmltZV8yMDE3IDwtIENyaW1lX0luY19Qb3N0Q29kZV8yMDE3ICU+JSBncm91cF9ieShTdWJ1cmIpICU+JSBzdW1tYXJpc2UoCiAgVG90YWxfSW5jaWRlbnRzID0gc3VtKE5vX29mX0luY2lkZW50KQopCgpTdWJ1cmJzX0NyaW1lXzIwMTcgJT4lIGhlYWQoNSkKCmBgYAoKIyMgSm9pbiB0aGUgZGF0YSBzZXRzCgoqIEpvaW4gdGhlIE1lbGJvdXJuZSBob3VzaW5nIGRhdGEgd2l0aCB0aGUgY3JpbWUgc3RhdGlzdGljcyBkYXRhIHRvIG1ha2UgYSB1bmlmaWVkIGRhdGEgc2V0IHdpdGggU3VidXJiIG5hbWUsIHBvc3Rjb2RlLCByZWdpb25hIG5hbWUsIG1lZGlhbiBwcmljZSwgbWVkaWFuIGRpc3RhbmNlIGFuZCAgdG90YWwgbnVtYmVyIG9mIGNyaW1lIGluY2lkZW50cy4gIAoKYGBge3J9CgojSm9pbiB0aGUgRGF0YSBzZXRzCgojIE9uIHRoZSBNZWxib3VybmUgSG91c2luZyBEYXRhICwgSm9pbiB0aGUgTnVtYmVyIG9mIENyaW1pbmFsIEluY2lkZW50cyAsIHBlciBTdWJ1cmIKClJlYWR5X3RvX3dvcmtfZGF0YSA8LSBsZWZ0X2pvaW4oTWVsYl93b3JraW5nX3NldF8yMDE3LCBTdWJ1cmJzX0NyaW1lXzIwMTcsIGJ5PSJTdWJ1cmIiKSAKClJlYWR5X3RvX3dvcmtfZGF0YSAlPiUgaGVhZCg1KQpgYGAKCgojIwlTY2FubmluZyBmaW5hbCBkYXRhIHNldCBmb3IgaW5jb25zaXN0ZW5jeSAgCgoqIENoZWNrIGZvciBOQSdzIGluIHRoZSBmaW5hbCBkYXRhIHNldC4gCiogRWxpbWluYXRlIHRoZSBTdWJ1cmIgd2hlcmUgd2UgZG8gbm90IGhhdmUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIG51bWJlciBvZiBpbmNpZGVudHMgY29tbWl0dGVkLiAKKiBDcmVhdGUgcnVsZSB0byBjaGVjayBmb3Igb2J2aW91cyBlcnJvciBvciBpbmNvbnNpc3RlbmN5LgoKYGBge3J9CgojSW52ZXN0aWdhdGdlIGlmIHRoZXJlIGlzIGFueSBOQSdzIGluIHRoaXMgZGF0YSBzZXQgYXMgcmVzdWx0IG9mIEpvaW4Kc3VtKGlzLm5hKFJlYWR5X3RvX3dvcmtfZGF0YSkpCgpSZWFkeV90b193b3JrX2RhdGFbIWNvbXBsZXRlLmNhc2VzKFJlYWR5X3RvX3dvcmtfZGF0YSksXSAjaW5zcGVjdCBOQSByb3dzCgojRWxpbWluYXRlIHRoaXMgc3VidXJiIGFzIHdlIGRvbnQgaGF2ZSBpbmZvIGFib3V0IHRoZSB0b3RhbCBudW1iZXIgb2YgSW5jaWRlbnRzClJlYWR5X3RvX3dvcmtfZGF0YSA8LSBSZWFkeV90b193b3JrX2RhdGFbY29tcGxldGUuY2FzZXMoUmVhZHlfdG9fd29ya19kYXRhKSxdCgoKI2NyZWF0ZSBhIHJ1bGUgdG8gY2hlY2sgaWYgdGhlIHZhbHVlcyBhcmUgbm9uIG5lZ2F0aXZlClJ1bGUgPC0gZWRpdHNldChjKCJtZWRpYW5fcHJpY2UgPj0gMCIsICJtZWRpYW5fZGlzdGFuY2UgPj0gMCIsIlRvdGFsX0luY2lkZW50cz49MCIpKQpSdWxlCiNhcHBseSBydWxlIG9uIHRoZSBGaW5hbCBkYXRhIHNldCAKcnVsZXNfdmlvbGF0ZWQgPC0gdmlvbGF0ZWRFZGl0cyhSdWxlLFJlYWR5X3RvX3dvcmtfZGF0YSkKCnN1bW1hcnkocnVsZXNfdmlvbGF0ZWQpICN2aXN1YWxpc2UgaWYgYW55IHJ1bGVzIGlzIHZpb2xhdGVkCgpSZWFkeV90b193b3JrX2RhdGEgJT4lIGhlYWQoNSkKCmBgYAoKIyMgSWRlbnRpZnkgdGhlIHN1YnVyYiB3aXRoIGhpZ2hlciBwcmljZSBtZWRpYW4gKG91dGxpZXJzKQoKKiBHcm91cCB0aGUgZGF0YSBieSBzdWJ1cmJzIGFuZCBwbG90IHRoZSBtZWRpYW5fcHJpY2UgYWdhaW5zdCB0aGUgcmVnaW9uLgoqIEZpbmQgdGhlIFN1YnVyYiB3aXRoIHRoZSBoaWdoZXN0IHByb3BlcnR5IHByaWNlLiAKKiBXZSBjYW4gc2VlIHRoYXQgU291dGggTWV0cm9wb2xpdGFuIFJlZ2lvbiBhcHBlYXJzIHRvIGJlIHRoZSBjb3N0bGllc3QgcmVnaW9uIHdpdGggbWVkaWFuIHByb3BlcnR5IHByaWNlIGFyb3VuZCAxLjUgbWlsbGlvbnMgYW5kIFRvb3JhayBiZWluZyB0aGUgU3VidXJiIHdpdGggaGlnaGVzdCBwcmljZSB3aXRoIG92ZXIgMyBtaWxsaW9uIEF1ICQuIAoKCmBgYHtyIGZpZy5oZWlnaHQgPSAwOCwgZmlnLndpZHRoID0gNX0Kb3V0bGllcnNfcHJpY2UgPSBib3hwbG90KFJlYWR5X3RvX3dvcmtfZGF0YSRtZWRpYW5fcHJpY2UgfiBSZWFkeV90b193b3JrX2RhdGEkUmVnaW9ubmFtZSxwbG90PUZBTFNFKSRvdXQKCiNNYWtlIGEgbmV3IGNvbHVtbiBmb3IgbGFiZWxsaW5nIHRoZSBvdXRsaWVycyA6IFN1YnVyYi9SZWdpb24gd2l0aCBoaWdoZXN0IG51bWJlciBvZiBjcmltZSBpbmNpZGVudHMKUmVhZHlfdG9fd29ya19kYXRhJGxhYmVsX3ByaWNlIDwtIGlmZWxzZSgoUmVhZHlfdG9fd29ya19kYXRhJG1lZGlhbl9wcmljZSAlaW4lIG91dGxpZXJzX3ByaWNlICYgUmVhZHlfdG9fd29ya19kYXRhJG1lZGlhbl9wcmljZT4zMDAwMDAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUoUmVhZHlfdG9fd29ya19kYXRhJFN1YnVyYiwiXG4iLApSZWFkeV90b193b3JrX2RhdGEkbWVkaWFuX3ByaWNlKSwiIikKCgpnZ3Bsb3QoYWVzKHk9bWVkaWFuX3ByaWNlLHg9UmVnaW9ubmFtZSxmaWxsPVJlZ2lvbm5hbWUpLGRhdGEgPSBSZWFkeV90b193b3JrX2RhdGEpK2dlb21fYm94cGxvdCgpKyBnZW9tX3RleHQoYWVzKGxhYmVsPWxhYmVsX3ByaWNlKSkrCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpK3NjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIkUuTWV0cm8iLCJFLlZpYyIsIk4uTWV0cm8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOLlZpYyIsIlMtRS5NZXRybyIsIlMuTWV0cm8iLCJXLk1ldHJvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVy5WaWMiKSkgKyAgCiAgZ2d0aXRsZSgiTWVsYm91cm5lIFByb3BlcnR5IFByaWNlICBieSBSZWdpb24iKSArIAogIHhsYWIoIlJlZ2lvbiBOYW1lcyIpICsgeWxhYigiTWVkaWFuX1ByaWNlIikgKwogIHRoZW1lKAogICAgCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJCbGFjayIsc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSApCiAgKQoKCmBgYAoKCiMjCUlkZW50aWZ5IHRoZSBzdWJ1cmIgd2l0aCBoaWdoZXIgY3JpbWUgaW5jaWRlbnRzIChvdXRsaWVycykKCiogR3JvdXAgdGhlIHN1YnVyYnMgYnkgcmVnaW9ucyBhbmQgcGxvdCB0aGUgY3JpbWUgaW5jaWRlbnQgZm9yIGVhY2ggcmVnaW9uLiAKKiBUaGUgb3V0bGllciByZXByZXNlbnRzIHRob3NlICJTdWJ1cmJzIiB3aGVyZSB0aGUgb2JzZXJ2ZWQgbnVtYmVyIG9mIGNyaW1lIGluY2lkZW50cyBkZXBhcnRzIHNpZ25pZmljYW50bHkgZnJvbSB0aGUgbnVtYmVyIG9mIGluY2lkZW50cyBpbiBvdGhlciBzdWJ1cmJzIG9uIHRoZSBzYW1lIHJlZ2lvbi4gSW4gb3RoZXIgd29yZHMgaXQgZGVwaWN0cyB0aGUgU3VidXJicyB3aXRoIGhpZ2hlciBjcmltZSByYXRlIGluIHRoYXQgcmVnaW9uLiAKKiBNZWxib3VybmUgc3VidXJiIHdpdGggMTY4MjYgbnVtYmVyIG9mIGluY2lkZW50cyBpbiBtYXJrZWQgdG8gc2hvdyB0aGUgU3VidXJiIHdoaWNoIGhhcyB0aGUgaGlnaGVzdCBudW1iZXIgb2YgY3JpbWUgaW5jaWRlbnRzIHJlY29yZGVkIGFjcm9zcyBhbGwgcmVnaW9ucy4gCgoKYGBge3IgZmlnLmhlaWdodCA9IDA4LCBmaWcud2lkdGggPSA1fQoKb3V0bGllcnNfY3JpbWUgPSBib3hwbG90KFJlYWR5X3RvX3dvcmtfZGF0YSRUb3RhbF9JbmNpZGVudHMgfiBSZWFkeV90b193b3JrX2RhdGEkUmVnaW9ubmFtZSxwbG90PUZBTFNFKSRvdXQKCiNNYWtlIGEgbmV3IGNvbHVtbiBmb3IgbGFiZWxsaW5nIHRoZSBvdXRsaWVycyA6IFN1YnVyYi9SZWdpb24gd2l0aCBoaWdoZXN0IG51bWJlciBvZiBjcmltZSBpbmNpZGVudHMuIFVzZSBQYXN0ZSB0byBjb25jYXRlbmF0ZSBzdHJpbmdzLiAKUmVhZHlfdG9fd29ya19kYXRhJGxhYmVsX2NyaW1lIDwtIGlmZWxzZSgoUmVhZHlfdG9fd29ya19kYXRhJFRvdGFsX0luY2lkZW50cyAlaW4lIG91dGxpZXJzX2NyaW1lICYgUmVhZHlfdG9fd29ya19kYXRhJFRvdGFsX0luY2lkZW50cz4xNTAwMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlKFJlYWR5X3RvX3dvcmtfZGF0YSRTdWJ1cmIsIlxuIiwKUmVhZHlfdG9fd29ya19kYXRhJFRvdGFsX0luY2lkZW50cyksIiIpCgoKI01ha2UgYSBib3hwbG90IApnZ3Bsb3QoYWVzKHk9VG90YWxfSW5jaWRlbnRzLHg9UmVnaW9ubmFtZSxmaWxsPVJlZ2lvbm5hbWUpLGRhdGEgPSBSZWFkeV90b193b3JrX2RhdGEpK2dlb21fYm94cGxvdCgpKwogICN0aGVtZV9ncmV5KGJhc2Vfc2l6ZSA9IDEwKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoIkUuTWV0cm8iLCJFLlZpYyIsIk4uTWV0cm8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOLlZpYyIsIlMtRS5NZXRybyIsIlMuTWV0cm8iLCJXLk1ldHJvIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJXLlZpYyIpKSArIGdlb21fdGV4dChhZXMobGFiZWw9bGFiZWxfY3JpbWUpKSsKICBnZ3RpdGxlKCJNZWxib3VybmUgQ3JpbWUgSW5jaWRlbnRzIGJ5IFJlZ2lvbiIpICsgCiAgeGxhYigiUmVnaW9uIE5hbWVzIikgKyB5bGFiKCJObyBvZiBDcmltZSBJbmNpZGVudHMiKSArCiAgdGhlbWUoCiAgICAKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gIkJsYWNrIixzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41ICkKICApCgoKCmBgYAoKCiMjCUV4Y2x1ZGluZyBvdXRsaWVyCgoqIE9uY2Ugd2Ugc2VlIHRoYXQgdGhlIHN1YnVyYiBNZWxib3VybmUgaGFzIHRoZSBtYXhpbXVtIG51bWJlciBvZiBDcmltZSBJbmNpZGVudHMgaW4gMjAxNywgbGV0cyBleGNsdWRlIGl0IGFuZCB2aWV3IHRoZSBib3hwbG90IGFnYWluLk5vdyB3ZSBjYW4gY2xlYXJseSBzZWUgdGhlIG1lZGlhbiBpLmUgbnVtYmVyIG9mIGNyaW1lcyBpbiBhIHJlZ2lvbiBpcyBoaWdoZXN0IGZvciBXZXN0ZXJuIFZpY3RvcmlhLiggdGhpcyBpcyBkb25lIG9ubHkgZm9yIHZpc3VhbGlzYXRpb24pCiogU3RpbGwgdGhlcmUgYXJlIG1hbnkgb3V0bGllcnMgaW4gdGhlIGRhdGEsIGJ1dCBhcyBwZXIgdGhpcyBzdHVkeSwgdGhleSByZXByZXNlbnQgdGhlIHN1YnVyYnMgd2l0aCBoaWdoZXIgY3JpbWVzIGNvbXBhcmVkIHRvIHRoYXQgcmVnaW9uIGFuZCBzaG91bGQgbm90IGJlIGV4Y2x1ZGVkIGF0IGFueSBwb2ludC4gCgoKYGBge3IgZmlnLmhlaWdodCA9IDA2LCBmaWcud2lkdGggPSA1fQoKRGF0YV93aXRob3V0X01lbGJfU3VidXJiIDwtIFJlYWR5X3RvX3dvcmtfZGF0YSAlPiUgZmlsdGVyKCFTdWJ1cmIgJWluJSAiTUVMQk9VUk5FIikKCmdncGxvdChhZXMoeT1Ub3RhbF9JbmNpZGVudHMseD1SZWdpb25uYW1lLGZpbGw9UmVnaW9ubmFtZSksZGF0YSA9IERhdGFfd2l0aG91dF9NZWxiX1N1YnVyYikrZ2VvbV9ib3hwbG90KCkrCiAgIyB0aGVtZV9ncmV5KGJhc2Vfc2l6ZSA9IDEwKSsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikrCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygiRS5NZXRybyIsIkUuVmljIiwiTi5NZXRybyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk4uVmljIiwiUy1FLk1ldHJvIiwiUy5NZXRybyIsIlcuTWV0cm8iLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlcuVmljIikpICsKICBnZ3RpdGxlKCJNZWxib3VybmUgQ3JpbWUgSW5jaWRlbnRzIGJ5IFJlZ2lvbiIpICsgCiAgeGxhYigiUmVnaW9uIE5hbWVzIikgKyB5bGFiKCJObyBvZiBDcmltZSBJbmNpZGVudHMiKSArCiAgdGhlbWUoCiAgICAKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoY29sb3VyID0gIkJsYWNrIixzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41ICkKICApCmBgYAoKIyMJTXVsdGl2YXJpYXRlIG91dGxpZXIgZGV0ZWN0aW9uOgoKKiBFeHBsb3JlIHRoZSByZWxhdGlvbiBiZXR3ZWVuIHByb3BlcnR5IHByaWNlIGFuZCBjcmltZSBpbmNpZGVudHMuIAoqIFVzZSBhIHNjYXR0ZXIgcGxvdCBmb3IgbnVtYmVyIG9mIENyaW1lIEluY2lkZW50cyBhZ2FpbnN0IHByb3BlcnR5IHByaWNlLgoqIFRoZXJlIGFyZSBmZXcgb3V0bGllcnMgaW4gdGhpcyBwbG90IHdoaWNoIHN1Z2dlc3QgdGhlIHN1YnVyYnMgd2l0aCBhbiB1bnVzdWFsbHkgaGlnaCBwcm9wZXJ0eSBwcmljZSBhbmQgY3JpbWUgcmF0ZSBjb21wYXJlZCB0byBvdGhlciBzdWJ1cmJzLiAKKiBUaGVzZSBvdXRsaWVycyBzaG91bGRudCBiZSByZW1vdmVkIG9yIGltcHV0ZWQgYXMgdGhleSByZXByZXNlbnQgaW1wb3J0YW50IGZhY3RzLiAgCgoKYGBge3IgZmlnLmhlaWdodCA9IDA2LCBmaWcud2lkdGggPSAxMX0KCiAgIHFwbG90KGRhdGE9UmVhZHlfdG9fd29ya19kYXRhLCB4PW1lZGlhbl9wcmljZSwgeT1Ub3RhbF9JbmNpZGVudHMpICtnZW9tX3RleHQoYWVzKGxhYmVsPWlmZWxzZSgoVG90YWxfSW5jaWRlbnRzPjUqSVFSKFRvdGFsX0luY2lkZW50cyl8bWVkaWFuX3ByaWNlPjQqSVFSKG1lZGlhbl9wcmljZSkpLHBhc3RlKFN1YnVyYiwiXG4iKSwiIikpLCBoanVzdD0xLjEpICsgCiAgZ2d0aXRsZSgiTWVsYm91cm5lOiAgTnVtYmVyIG9mIGNyaW1lIGluY2lkZW50cyBhZ2FpbnN0IHByb3BlcnR5IHByaWNlcyIpICsgCiAgeWxhYigiTm8gb2YgQ3JpbWUgSW5jaWRlbnRzIikgKyB4bGFiKCJtZWRpYW4gcHJvcGVydHkgcHJpY2UiKSArCiAgdGhlbWUoCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJCbGFjayIsc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSApCiAgKSAjIDUqSVFSIC8gNCpJUVIgLSByYW5kb20gdmFsdWVzIGp1c3QgZm9yIGxhYmVsbGluZyBtb3N0IGV4dHJlbWUgcG9pbnRzLiAKYGBgCgoqIExvb2tpbmcgYXQgdGhlIHBsb3QgYWJvdmUgVG9vcmFrIGRvZXNuJ3QgYXBwZWFyIHRvIGJlIHRoZSBzdWJ1cmIgd2l0aCBiZXN0IHZhbHVlIGZvciBtb25leSBhcyB0aGVyZSBhcmUgb3RoZXIgc3VidXJicyB3aGljaCBhcmUgY2hlYXBlciBhbmQgd2l0aCBsb3dlciBjcmltZSByYXRlLCBob3dldmVyIHRoZXJlIG1pZ2h0IGJlIG90aGVyIGZhY3RvcnMgaW52b2x2ZWQgd2hpY2ggaXMgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIHJlcG9ydC4gCgojIyBEYXRhIFRyYW5zZm9ybWF0aW9uIGZvciBmdXJ0aGVyIHByb2Nlc3NpbmcKCkZ1cnRoZXIgSSB3YW50ZWQgIHRvIGV4cGxvcmUgaWYgdGhlcmUgaXMgYW55IGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHByaWNlIGFuZCBkaXN0YW5jZSAoZnJvbSB0aGUgY2VudHJhbCkuCgoxLiBDcmVhdGUgYSBuZXcgY29weSBvZiB0aGUgZGF0YSBzZXQgd2l0aCBvbmx5IG1lZGlhbl9kaXN0YW5jZSBhbmQgbWVkaWFuX3ByaWNlLgoyLiBDaGVjayBmb3IgdGhlIGRpc3RyYnV0aW9uIG9mIHRoZSBkYXRhLiAKMy4gQXBwbHkgc3VpdGFibGUgbWF0aGVtYXRpY2FsIG9wZXJhdGlvbnMgdG8gdHJhbnNmb3JtIHRoZSBkYXRhIGFzIGNsb3NlIHRvIG5vcm1hbCBhcyBwb3NzaWJsZS4gSW4gdGhpcyBjYXNlIHNxcnQgYW5kIGxvZ2FyaXRobWljIGZ1Y3Rpb25zIGdhdmUgdGhlIGRpc3RyaWJ1aW9ucyBjbG9zZXIgdG8gbm9ybWFsaXR5LiAKNC4gVHJhbnNmb3JtIHRoZSBkYXRhIGZyYW1lIGNvcHkgYWNjb3JkaW5nbHkuICAKCmBgYHtyfQoKI2NyZWF0ZSBhIG5ldyBjb3B5IG9mIGRhdGEgZm9yIHRoaXMgdGFzayAKCk1MX2RhdGFzZXQgPC0gTWVsYl9maWx0ZXJlZF9kYXRhXzIwMTcgJT4lIGdyb3VwX2J5KFN1YnVyYikgJT4lIHN1bW1hcmlzZSggbWVkaWFuX2Rpc3RhbmNlID1tZWRpYW4oRGlzdGFuY2UpLAptZWRpYW5fcHJpY2U9IG1lZGlhbihQcmljZSkpICU+JSBzZWxlY3QobWVkaWFuX2Rpc3RhbmNlLG1lZGlhbl9wcmljZSkKCk1MX2RhdGFzZXQgPC0gYXMuZGF0YS5mcmFtZShNTF9kYXRhc2V0KSAjQ292ZXJ0IHRoZSB0aWJibGUgaW50byBhIGRmCgpoaXN0KE1MX2RhdGFzZXQkbWVkaWFuX2Rpc3RhbmNlKSAjcmlnaHQgc2tld2VkCmhpc3QoTUxfZGF0YXNldCRtZWRpYW5fcHJpY2UpICNyaWdodCBza2V3ZWQKCiNhcHBseSB0cmFuc2Zvcm1hdGlvbiB0byBhY2hpZXZlIG5vcm1hbGl0eQpoaXN0KHNxcnQoTUxfZGF0YXNldCRtZWRpYW5fZGlzdGFuY2UpKQpoaXN0KGxvZzEwKE1MX2RhdGFzZXQkbWVkaWFuX3ByaWNlKSkKCiMgdHJhbnNmb3JtIHRoZSBkYXRhIGZyYW1lIGFjY29yZGluZ2x5IAoKZm9yIChpIGluIGxlbmd0aChNTF9kYXRhc2V0KSkgewogIAogIE1MX2RhdGFzZXQkbWVkaWFuX2Rpc3RhbmNlIDwtIHNxcnQoTUxfZGF0YXNldCRtZWRpYW5fZGlzdGFuY2UpCiAgTUxfZGF0YXNldCRtZWRpYW5fcHJpY2UgPC0gbG9nMTAoTUxfZGF0YXNldCRtZWRpYW5fcHJpY2UpCiAgCn0KCmBgYAoKIyMgTWFjaGluZSBMZWFybmluZyBtb2RlbCB0byBwcmVkaWN0IHRoZSBwcmljZSB1c2luZyBkaXN0YW5jZQoKIyMjIyMgU3RlcHM6IAoKCiogU3RlcCAxOiBEZWZpbmUgdGhlIHRhc2sgYXMgcmVncmVzc2lvbiBhbmFseXNpcyB3aXRoIFByaWNlIGFzIHRhcmdldCB2YXJpYWJsZS4KKiBTdGVwIDI6IFdlIHdpbGwgdXNlIHRoZSBjbGFzc2ljIHJlZ3Jlc3Npb24gYXMgb3VyIGFsZ29yaXRobSAKKiBTdGVwIDM6IEZpdHRpbmcgdGhlIG1vZGVsCiAgICAgKyBEaXZpZGUgdGhlIGRhdGEgaW50byB0cmFpbiBhbmQgdGVzdCBzZXQuCiAgICAgKyBUcmFpbiB0aGUgbGVhcm5lciBvbiB0aGUgdHJhaW5pbmcgc2V0LiAKKiBTdGVwIDQgOiBNYWtlIHByaWNpbmcgcHJlZGljdGlvbnMgdXNpbmcgdGhlIHRyYWluZWQgbW9kZWwgdG8gcHJlZGljdCB0aGUgcHJpY2UgaW4gdGhlIHRlc3Qgc2V0LiAKKiBTdGVwIDU6IEV2YWx1YXRpbmcgdGhlIGxlYXJuZXI6IApXZSBjYWxjdWxhdGUgdGhlIG1lYW4gc3F1YXJlIGVycm9yIGFuZCBtZWFuIGFic29sdXRlIGVycm9yIHRvIHNlZSB0aGUgcGVyZm9ybWFuY2UgbWV0cmljIG9mIG91ciBsZWFybmVyLgoKYGBge3J9CgojIFRhc2sKTUxfZGF0YXNldF90YXNrIDwtIG1ha2VSZWdyVGFzayhkYXRhID0gTUxfZGF0YXNldCx0YXJnZXQgPSAibWVkaWFuX3ByaWNlIikKCiNMZWFybmVyIGFsZ29yaXRobSAtIGNsYXNzaWMgcmVncmVzc2lvbiAKbHJuciA9IG1ha2VMZWFybmVyKCJyZWdyLmdsbSIpCgojZGVmaW5pbmUgdGhlIHRyYWluIGFuZCB0ZXN0IHNldHMKbj1ucm93KE1MX2RhdGFzZXQpCnRyYWluaW5nX3NldCA9IHNhbXBsZShuLCBzaXplID0gMi8zKm4pCnRlc3Rpbmdfc2V0ID0gc2V0ZGlmZigxOm4sIHRyYWluaW5nX3NldCkKCiMgbWFrZSBhIG1vZGVsIG9mIHRoZSBkYXRhIGFuZCB0cmFpbiB0aGUgbGVhcm5lciBvbiB0aGUgdGFzayB1c2luZyB0aGUgdHJhaW5pbmcgc2V0CnByaWNpbmdfbW9kZWwgPSBtbHI6OnRyYWluKGxlYXJuZXIgPSBscm5yLCB0YXNrID0gTUxfZGF0YXNldF90YXNrLCBzdWJzZXQgPSB0cmFpbmluZ19zZXQpCnByaWNpbmdfbW9kZWwKCiNwcmVkaWN0IHRoZSBwcmljZSAKCnByZWRfcHJpY2UgPSBwcmVkaWN0KHByaWNpbmdfbW9kZWwsIHRhc2sgPSBNTF9kYXRhc2V0X3Rhc2ssIHN1YnNldCA9IHRlc3Rpbmdfc2V0KQoKI2V2YWx1YXRlIHRoZSBsZWFybmVyIAoKcGVyZm9ybWFuY2UocHJlZF9wcmljZSxtZWFzdXJlcyA9IGxpc3QobXNlLG1hZSkpCgpgYGAKCioqRmluZGluZ3M6KioKCkFzIHRoZSBhYnNvbHV0ZSBlcnJvciBpcyBzbWFsbCwgdGhlIG1vZGVsIHBlcmZvcm1lZCB3ZWxsIGFnYWluc3QgdGhlIGdpdmVuIHNldCBvZiBkYXRhLiBUaGUgbW9kZWwgY2FuICBiZSBhcHBsaWVkIG9uIGFkZGl0aW9uYWwgZGF0YSBzZXRzIHRvIGV2YWx1YXRlIGl0cyBwZXJmb3JtYW5jZS4gRnVydGhlciBhbiBGIHRlc3QgY2FuIGJlIGRvbmUgZm9yIG92ZXJhbGwgbW9kZWwgc2lnbmlmaWNhbmNlLiAgCgoqKkZ1cnRoZXIgc3RlcHM6KioKClRoZSBkYXRhIGNhbiBiZSB1c2VkIHRvIGZpbmQgYSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBob3VzaW5nIHByaWNlcyBhbmQgZGlzdGFuY2UgKGZyb20gTWVsYm91cm5lIENlbnRyYWwpLiBBbHNvIGlmIHRoZSByZWxhdGlvbnNoaXAgY2hhbmdlcyB0cmF2ZWxsaW5nIHRoZSBzYW1lIGRpc3RhbmNlIGJ1dCBpbiBkaWZmZXJlbnQgbWV0cm9wb2xpdGFuIHJlZ2lvbnMuCkZ1cnRoZXIsIENyaW1lIHN0YXRpc3RpY3MgZm9yIHRoZSBzdWJ1cmIgY2FuIGJlIGxvb2tlZCB0byBleHBsb3JlIGEgY29ycmVsYXRpb24sIGlmIGFueSBiZXR3ZWVuIHRoZSBob3VzaW5nIHByaWNlIGFuZCBjcmltZSBpbiB0aGF0IHN1YnVyYi4gCgo8YnI+Cjxicj4=