Required packages

# This is the R chunk for the required packages
library(readr)
suppressMessages(library(dplyr))
library(tidyr)
library(stringr)
library(readxl)

Executive Summary

In this RStudio report we are creating clean and tidy data that will be used to analyse society wellbeing in different Local Govemrent Areas in Victoria. These indicators include life satisfaction, life worthwhile and overall community satisfaction, as well as housing prices to give an idea about the standard of living in specific area. We have loaded 4 different dataframes from 3 different datasets. All 4 dataframes has at least one variable that stores the Local Goverment Area and it was used to join all 4 dataframes. Firstly, we have imported the dataset using appropriate function according to file type. We made sure that unnecessary columns and rows are omitted from the beginning. Then, we went through all variables in the dataframes and checked that they have the correct data type and overall correct structure. Next, we mutated our dataframes and create extra variables that gives better insights about our data. After that, and before going into further data manipulation, we dealt with missing values and outliers, because it was easier to do that while data was in wide format. Moreover, data transformation was applied to help gain better understanding about our data and helped us better identify outliers. Next, we made sure that all four dataframes has a consistent common variable that can be used for merging and then we merged that dataset all together. Finally, we made our data more tidy and ready for analysis by changing it from wide to long format.

Note: The order of tasks given in the assignment sheet is different than the order used here, and that is due to necessity to preprocess the data.

Data

Dataset 1: Life satisfaction and worthwhile

Victorian Population Health Survey 2017 dataset is provided by The Department of Health and Human Services. The dataset includes different health indicators about popultation according to local goverment areas in Vicrtoria. The dataset in an Excel file that has different sheets, where each sheet has one indicator. In this report we will use two sheets only, life satisfaction and life worthwhile.

Page link: https://www2.health.vic.gov.au/public-health/population-health-systems/health-status-of-victorians/survey-data-and-reports/victorian-population-health-survey/victorian-population-health-survey-2017

Download link: https://www2.health.vic.gov.au/Api/downloadmedia/%7B1AF5D560-4144-4AFE-BDBA-7CFA431865E5%7D

Dataset 1a variables description:

1- LocalGovArea: the name of the Victorian local goverment council, there are overall 79 local goverments

2- LowAndMedium Female: percentage of female who have low or medium life satisfaction

3- LowAndMedium Male: percentage of male who have low or medium life satisfaction

4- High Female: percentage of female who have high life satisfaction

5- High Male: percentage of male who have high life satisfaction

6- VeryHigh Female: percentage of female who have very high life satisfaction

7- VeryHigh Male: percentage of male who have very high life satisfaction

Let’s import the dataset which is an xlsx file that contains more than one sheet, we will use two sheets only (life satisfaction and life worthwhile). We will import everyting first, and we will exclude the first 7 rows that contains no observations.

# Importing data 1a
suppressMessages(life_satisfaction <- read_excel("VPHS 2017 by LGA and gender - v2.xlsx",
                                sheet = "Life satisfacation", col_names = FALSE, skip = 7))
# Remove unnecessary columns
life_satisfaction <- life_satisfaction %>%  select(2,3,10,17,24,31,38)
# Remove unnecessary rows at the bottom of the sheet
life_satisfaction <- life_satisfaction[-seq(80,94), ]
# Define columns names
colnames(life_satisfaction) <- c("LocalGovArea","LowAndMedium Female","LowAndMedium Male",
                            "High Female", "High Male", "VeryHigh Female", "VeryHigh Male")
head(life_satisfaction)

Dataset 1b variables description:

1- LocalGovArea: the name of the Victorian local goverment council, there are overall 79 local goverments

2- LowAndMedium Female: percentage of female who have low or medium life worthwhile

3- LowAndMedium Male: percentage of male who have low or medium life worthwhile

4- High Female: percentage of female who have high life worthwhile

5- High Male: percentage of male who have high life worthwhile

6- VeryHigh Female: percentage of female who have very high life worthwhile

7- VeryHigh Male: percentage of male who have very high life worthwhile

Importing the second sheet:

# Importing data 1b
suppressMessages(life_worthwhile <- read_excel("VPHS 2017 by LGA and gender - v2.xlsx",
                              sheet = "Life worthwhile", col_names = FALSE, skip = 7))
# Remove unnecessary columns
life_worthwhile <- life_worthwhile %>% select(2,3,10,17,24,31,38)
# Remove unnecessary rows at the bottom of the sheet
life_worthwhile <- life_worthwhile[-seq(80,94), ]
# Define columns names
colnames(life_worthwhile) <- c("LocalGovArea","LowAndMedium Female","LowAndMedium Male",
                               "High Female", "High Male", "VeryHigh Female", "VeryHigh Male")
head(life_worthwhile)

Dataset 2: Melbourne Housing Market

Melbourne Housing Market is a dataset that is published in Kaggle which has over 60,000 obervations. Each observation is a house/unit/townhouse that is for sale and includes information like price, number of bedrooms, address and local goverment area. That dataset is in a CSV file.

Page link: https://www.kaggle.com/anthonypino/melbourne-housing-market

Download link: https://www.kaggle.com/anthonypino/melbourne-housing-market/download

Dataset variables description:

1- Suburb 2- Address 3- Rooms 4- Type

5- Price 6-Method 7- Seller 8- Date

9- Postcode 10- Regoinname

11- Propertycount: number of properties for sale in same council area

12- Distance: distance from Melbourne CBD

13- CouncilArea: Local goverment area

Let’s import that dataset.

suppressMessages(melbourne_house_prices <- read_csv("MELBOURNE_HOUSE_PRICES_LESS.csv"))
head(melbourne_house_prices)

Dataset 3: Know You Council

This dataset, which is provided by Know Your Council website, gives different indicators to evaluate the performace of each local goverment area in Victoria. There are over 60 indicators, but we will only use one indicator that has to do with society satisfaction about the council’s services.

Page link: https://discover.data.vic.gov.au/dataset/know-your-council-local-government-performance-reporting-framework

Download link: https://knowyourcouncil.vic.gov.au/__data/assets/excel_doc/0010/32887/LGPRF-2014-2019-Full-Council-Data-Set-EXTERNAL.xlsx

Dataset variables description:

1- Council: local government area name

2- Group: goverment group

3- ID

4- Descriptio: indicator’s description

5- Service Provided: T/F if service is provided or not

6- Result: the numerical value used as result for each indicator

Now let

vic_councils_indicators <- read_excel("LGPRF-2014-2019-Full-Council-Data-Set-EXTERNAL.xlsx", 
     sheet = "Indicators 2017-18", col_types = c("text", 
         "text", "text", "text", "text", "text", 
         "numeric", "skip", "skip", "skip", 
         "skip", "skip", "skip", "skip", "skip", 
         "skip"))
head(vic_councils_indicators)

Understand

Dataset 1: Life satisfaction and worthwhile

Checking the dimension of dataset 1a:

#Dimension of dataset 1a
dim(life_satisfaction)
[1] 79  7
#How many values for Local goverments areas
paste("There are ", dim(life_satisfaction)[1], " local goverment areas in Victoria")
[1] "There are  79  local goverment areas in Victoria"
#Structure of the dataset
str(life_satisfaction)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   79 obs. of  7 variables:
 $ LocalGovArea       : chr  "Alpine (S)" "Ararat (RC)" "Ballarat (C)" "Banyule (C)" ...
 $ LowAndMedium Female: num  28.2 29.6 20 18.5 19.9 ...
 $ LowAndMedium Male  : num  5.26 14.52 20.97 17.52 26.28 ...
 $ High Female        : num  47.7 51.5 48.2 56.4 51.9 ...
 $ High Male          : num  74 44.1 55.1 61.4 43.2 ...
 $ VeryHigh Female    : num  23.1 18.1 31.3 24.2 27.6 ...
 $ VeryHigh Male      : num  19.4 40.8 23.2 21.1 30.3 ...
# Using a combination of (sapply) and (typeof), let's check the data type of each column
sapply(life_satisfaction,typeof)
       LocalGovArea LowAndMedium Female   LowAndMedium Male         High Female           High Male     VeryHigh Female 
        "character"            "double"            "double"            "double"            "double"            "double" 
      VeryHigh Male 
           "double" 

All data types of all columns are OK in this dataset.

Checking the dimension of dataset 1b:

#Dimension of dataset 1b
dim(life_worthwhile)
[1] 79  7
#How many values for Local goverments areas
paste("There are ", dim(life_worthwhile)[1], " local goverment areas in Victoria")
[1] "There are  79  local goverment areas in Victoria"

Checking the structure:

#Structure of the dataset
str(life_worthwhile)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   79 obs. of  7 variables:
 $ LocalGovArea       : chr  "Alpine (S)" "Ararat (RC)" "Ballarat (C)" "Banyule (C)" ...
 $ LowAndMedium Female: num  17.7 18.5 10.7 16.5 18.5 ...
 $ LowAndMedium Male  : num  3.16 14.77 26.66 13.35 13.08 ...
 $ High Female        : num  48.2 46.2 41.9 43.6 46.5 ...
 $ High Male          : num  52.1 40.3 47.1 59.4 48.4 ...
 $ VeryHigh Female    : num  33.1 34.8 46.5 37.6 33.7 ...
 $ VeryHigh Male      : num  41.9 43.3 25.2 27.2 37.5 ...
# Using a combination of (sapply) and (typeof), let's check the data type of each column
sapply(life_worthwhile,typeof)
       LocalGovArea LowAndMedium Female   LowAndMedium Male         High Female           High Male     VeryHigh Female 
        "character"            "double"            "double"            "double"            "double"            "double" 
      VeryHigh Male 
           "double" 

All data types of all columns are OK in this dataset.

Both dataframes 1a and 1b has Local Goverment Areas column with Group type between brackets. We need to rename it and remove brackets so that they are consistent with other dataframes.

#Before
head(life_satisfaction$LocalGovArea)
[1] "Alpine (S)"     "Ararat (RC)"    "Ballarat (C)"   "Banyule (C)"    "Bass Coast (S)" "Baw Baw (S)"   
#dataset 1a
life_satisfaction$LocalGovArea <- gsub("\\(.*?)","",life_satisfaction$LocalGovArea)
#dataset 1a
life_worthwhile$LocalGovArea <- gsub("\\(.*?)","",life_worthwhile$LocalGovArea)
# Now remove resulting whitespaces
life_satisfaction$LocalGovArea <- trimws(life_satisfaction$LocalGovArea,"r")
life_worthwhile$LocalGovArea <- trimws(life_worthwhile$LocalGovArea,"r")
#After
head(life_satisfaction$LocalGovArea)
[1] "Alpine"     "Ararat"     "Ballarat"   "Banyule"    "Bass Coast" "Baw Baw"   

Dataset 2: Melbourne Housing Market

Check the dimensions of the dataset:

dim(melbourne_house_prices)
[1] 63023    13
paste("There are ", dim(melbourne_house_prices)[1], " observations in this dataset")
[1] "There are  63023  observations in this dataset"

Check the structure of the dataframe:

#Structure of the dataset
str(melbourne_house_prices)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    63023 obs. of  13 variables:
 $ Suburb       : chr  "Abbotsford" "Abbotsford" "Abbotsford" "Aberfeldie" ...
 $ Address      : chr  "49 Lithgow St" "59A Turner St" "119B Yarra St" "68 Vida St" ...
 $ Rooms        : num  3 3 3 3 2 2 2 3 6 3 ...
 $ Type         : chr  "h" "h" "h" "h" ...
 $ Price        : num  1490000 1220000 1420000 1515000 670000 ...
 $ Method       : chr  "S" "S" "S" "S" ...
 $ SellerG      : chr  "Jellis" "Marshall" "Nelson" "Barry" ...
 $ Date         : chr  "1/04/2017" "1/04/2017" "1/04/2017" "1/04/2017" ...
 $ Postcode     : num  3067 3067 3067 3040 3042 ...
 $ Regionname   : chr  "Northern Metropolitan" "Northern Metropolitan" "Northern Metropolitan" "Western Metropolitan" ...
 $ Propertycount: num  4019 4019 4019 1543 3464 ...
 $ Distance     : num  3 3 3 7.5 10.4 10.4 10.4 10.4 14 3 ...
 $ CouncilArea  : chr  "Yarra City Council" "Yarra City Council" "Yarra City Council" "Moonee Valley City Council" ...
 - attr(*, "spec")=
  .. cols(
  ..   Suburb = col_character(),
  ..   Address = col_character(),
  ..   Rooms = col_double(),
  ..   Type = col_character(),
  ..   Price = col_double(),
  ..   Method = col_character(),
  ..   SellerG = col_character(),
  ..   Date = col_character(),
  ..   Postcode = col_double(),
  ..   Regionname = col_character(),
  ..   Propertycount = col_double(),
  ..   Distance = col_double(),
  ..   CouncilArea = col_character()
  .. )

Since we are going to match datasets with local goverment area, we need to remove the pattern “City Council” from Council Area.

# Before
head(melbourne_house_prices$CouncilArea)
[1] "Yarra City Council"         "Yarra City Council"         "Yarra City Council"         "Moonee Valley City Council"
[5] "Moonee Valley City Council" "Moonee Valley City Council"
# Removing pattern
melbourne_house_prices$CouncilArea <-  str_remove(melbourne_house_prices$CouncilArea,
                                                  " City Council")
# After
head(melbourne_house_prices$CouncilArea)
[1] "Yarra"         "Yarra"         "Yarra"         "Moonee Valley" "Moonee Valley" "Moonee Valley"

Let’s rename CouncilArea column name to LocalGovArea so it matches other dataframes to be merged later

colnames(melbourne_house_prices)[13] <- "LocalGovArea"

Therse are columns that can be changed to factors:

paste("There are ",length(unique(melbourne_house_prices$Rooms))," sizes according to rooms number")
[1] "There are  14  sizes according to rooms number"
paste("There are ",length(unique(melbourne_house_prices$Suburb)), " unique Suburbs")
[1] "There are  380  unique Suburbs"
paste("There are ",length(unique(melbourne_house_prices$Type)), " types of Houses")
[1] "There are  3  types of Houses"
paste("There are ",length(unique(melbourne_house_prices$Regionname)), " unique Regionname")
[1] "There are  8  unique Regionname"
paste("There are ",length(unique(melbourne_house_prices$LocalGovArea)), " unique Local goverment areas")
[1] "There are  34  unique Local goverment areas"

As we can see this dataset only has 34 Local Government areas, while the previous dataset had 79.

These unique values can be converted to factors instead, and some can given better meaningful labels.

melbourne_house_prices$Rooms = factor(melbourne_house_prices$Rooms, levels = unique(melbourne_house_prices$Rooms), ordered = FALSE)
melbourne_house_prices$Suburb = factor(melbourne_house_prices$Suburb, levels = unique(melbourne_house_prices$Suburb), ordered = FALSE)
melbourne_house_prices$Regionname = factor(melbourne_house_prices$Regionname, levels = unique(melbourne_house_prices$Regionname), ordered = FALSE)
melbourne_house_prices$LocalGovArea = factor(melbourne_house_prices$LocalGovArea, levels = unique(melbourne_house_prices$LocalGovArea), ordered = FALSE)

# Let's also convert Type to factor and give levels meaningful labels
melbourne_house_prices$Type <- factor(melbourne_house_prices$Type,levels=c('h','u','t'), labels=c('house','unit','townhouse'), ordered = FALSE)
head(melbourne_house_prices$Type)
[1] house     house     house     house     house     townhouse
Levels: house unit townhouse

Now convert the date column to date type

melbourne_house_prices$Date <- as.Date(melbourne_house_prices$Date, "%d/%m/%Y")

Let’s check everything is now ok in this dataframe:

#Structure of the dataset
str(melbourne_house_prices)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame':    63023 obs. of  13 variables:
 $ Suburb       : Factor w/ 380 levels "Abbotsford","Aberfeldie",..: 1 1 1 2 3 3 3 3 4 5 ...
 $ Address      : chr  "49 Lithgow St" "59A Turner St" "119B Yarra St" "68 Vida St" ...
 $ Rooms        : Factor w/ 14 levels "3","2","6","4",..: 1 1 1 1 2 2 2 1 3 1 ...
 $ Type         : Factor w/ 3 levels "house","unit",..: 1 1 1 1 1 3 2 1 1 1 ...
 $ Price        : num  1490000 1220000 1420000 1515000 670000 ...
 $ Method       : chr  "S" "S" "S" "S" ...
 $ SellerG      : chr  "Jellis" "Marshall" "Nelson" "Barry" ...
 $ Date         : Date, format: "2017-04-01" "2017-04-01" "2017-04-01" "2017-04-01" ...
 $ Postcode     : num  3067 3067 3067 3040 3042 ...
 $ Regionname   : Factor w/ 8 levels "Northern Metropolitan",..: 1 1 1 2 2 2 2 2 2 3 ...
 $ Propertycount: num  4019 4019 4019 1543 3464 ...
 $ Distance     : num  3 3 3 7.5 10.4 10.4 10.4 10.4 14 3 ...
 $ LocalGovArea : Factor w/ 34 levels "Yarra","Moonee Valley",..: 1 1 1 2 2 2 2 2 3 4 ...
 - attr(*, "spec")=
  .. cols(
  ..   Suburb = col_character(),
  ..   Address = col_character(),
  ..   Rooms = col_double(),
  ..   Type = col_character(),
  ..   Price = col_double(),
  ..   Method = col_character(),
  ..   SellerG = col_character(),
  ..   Date = col_character(),
  ..   Postcode = col_double(),
  ..   Regionname = col_character(),
  ..   Propertycount = col_double(),
  ..   Distance = col_double(),
  ..   CouncilArea = col_character()
  .. )

All good.

Dataset 3: Know You Council

Checking dimensions

dim(vic_councils_indicators)
[1] 5214    7

This dataset is too big for our need, we will only take obervations that we need for our analysis. We need ‘Community Satisfaction’ only so we will filter dataframe to only give this indicator:

# Use filter to use Community Satisfaction only
vic_councils_indicators <- vic_councils_indicators %>%
  filter(Description == "Community satisfaction rating out 100 with how council has performed in making decisions in the interests of the community")
# Check that we have 79 local goverments
paste("There are ", dim(vic_councils_indicators)[1], " local goverment areas in this dataset")
[1] "There are  79  local goverment areas in this dataset"

Remove unnecessary variables and give a better name for the dataset:

community_satisfaction <- vic_councils_indicators %>% select(1,2,7)

Also, change column names to match other dataset:

colnames(community_satisfaction)[1] <- "LocalGovArea"
colnames(community_satisfaction)[3] <- "CommunitySatisfaction"

Let’s check the structure of the dataset now:

#Structure of the dataset
str(community_satisfaction)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   79 obs. of  3 variables:
 $ LocalGovArea         : chr  "Southern Grampians Shire" "South Gippsland Shire" "Stonnington City" "Ararat Rural City" ...
 $ Group                : chr  "LARGE_SHIRE" "LARGE_SHIRE" "METRO" "SMALL_SHIRE" ...
 $ CommunitySatisfaction: num  50 42 58 33 48 52 54 59 58 61 ...

Check Group variable if can be converted to factor:

unique(community_satisfaction$Group)
[1] "LARGE_SHIRE" "METRO"       "SMALL_SHIRE" "INTERFACE"   "REGIONAL"   

Thus, we need to change group to factor:

community_satisfaction$Group = factor(community_satisfaction$Group, levels = unique(community_satisfaction$Group), ordered = FALSE)

One last thing, we need to rename some Local Goverment Areas so the name is aligned with other datasets:

community_satisfaction$LocalGovArea <-  str_remove(community_satisfaction$LocalGovArea, " Shire")
community_satisfaction$LocalGovArea <-  str_remove(community_satisfaction$LocalGovArea, " City")
community_satisfaction$LocalGovArea <-  str_remove(community_satisfaction$LocalGovArea, "Borough of ")
community_satisfaction$LocalGovArea <-  str_remove(community_satisfaction$LocalGovArea, " Rural")

Scan I : Missing values

In our case, it is easier to work with missing and special values and outliers while data is in wide format, so before going into further transformation, let’s deal with NAs first.

#create function for NAs and special values
is.specialorNA <- function(x){
if (is.numeric(x)) (is.infinite(x) | is.nan(x) | is.na(x))
}

Dataset 1: Life satisfaction and worthwhile

Check if dataset 1a and 1b has NAs

sum(is.na(life_satisfaction))
[1] 0
sum(is.na(life_worthwhile))
[1] 0
sum(is.infinite(life_satisfaction$LifeSatisfaction))
Unknown or uninitialised column: 'LifeSatisfaction'.
[1] 0
sum(is.infinite(life_worthwhile$LifeWorthwhile))
Unknown or uninitialised column: 'LifeWorthwhile'.
[1] 0
sum(is.nan(life_satisfaction$LifeSatisfaction))
Unknown or uninitialised column: 'LifeSatisfaction'.
[1] 0
sum(is.nan(life_worthwhile$LifeWorthwhile))
Unknown or uninitialised column: 'LifeWorthwhile'.
[1] 0

None found.

Dataset 2: Melbourne Housing Market

Check if dataset 2 has NAs

colSums(is.na(melbourne_house_prices))
       Suburb       Address         Rooms          Type         Price        Method       SellerG          Date      Postcode 
            0             0             0             0         14590             0             0             0             0 
   Regionname Propertycount      Distance  LocalGovArea 
            0             0             0             0 

We see that prices in melbourne_house_prices dataset has many missing values, 14590. It is reasonable to find the average price of houses that has the same rooms numbre, same type and same local government area, and then use it to fill those NAs. Therefore, we fill them with the average prices of houses/units/townhouses in same local goverment area taking into account type and number of rooms.

# Group the dataset accorind to localGovArea, type and rooms then mutate price of NAs to
# have mean value of their corresponding group
melbourne_house_prices <-
  melbourne_house_prices %>%
  group_by(LocalGovArea,Type,Rooms) %>% 
  mutate(Price = ifelse(is.na(Price), mean(Price, na.rm = T), Price))

There will probably be some NAs left, because their group combination doesn’t exist thus can’t have a mean. Let’s check how many NAs left:

sum(is.na(melbourne_house_prices$Price))
[1] 42

42 is far less than 14590.

We can now safely omit these 42 observations.

melbourne_house_prices <- na.omit(melbourne_house_prices)
sum(is.na(melbourne_house_prices))
[1] 0

Dataset 3: Know You Council

Check for NAs:

colSums(is.na(melbourne_house_prices))
       Suburb       Address         Rooms          Type         Price        Method       SellerG          Date      Postcode 
            0             0             0             0             0             0             0             0             0 
   Regionname Propertycount      Distance  LocalGovArea 
            0             0             0             0 

None found.

Tidy & Manipulate Data I

Dataset 1: Life satisfaction and worthwhile

Working in life statisfaction and worthwhile dataset, we will mutate dataset male/female columns to give average of both

life_satisfaction <- life_satisfaction %>%
  mutate(LowAndMedium =(`LowAndMedium Male`+`LowAndMedium Female`)/2,
                             High = (`High Male`+`High Female`) / 2,
                             VeryHigh = (`VeryHigh Male`+`VeryHigh Female`) /2)

life_worthwhile <- life_worthwhile %>%
  mutate(LowAndMedium = (`LowAndMedium Male`+`LowAndMedium Female`)/2,
                             High = (`High Male`+`High Female`) / 2,
                             VeryHigh = (`VeryHigh Male`+`VeryHigh Female`) /2)

Let us also also consider high satistisfaction and very hight satistisfaction to be in same category

life_satisfaction <- life_satisfaction %>% mutate(HighAndVeryHigh = High + VeryHigh)
life_worthwhile <- life_worthwhile %>% mutate(HighAndVeryHigh = High + VeryHigh)

The sum of LowAndMedium and HighAndVeryHigh is always 100, thus we get rid of one, and since we are more interested in the high satisfaction rate will will only use HighAndVeryHigh from both datasets.

life_satisfaction <- life_satisfaction %>% select(LocalGovArea, HighAndVeryHigh)
life_worthwhile <- life_worthwhile %>% select(LocalGovArea, HighAndVeryHigh)
# Rename columns
colnames(life_satisfaction)[2] <- "LifeSatisfaction"
colnames(life_worthwhile)[2] <- "LifeWorthwhile"

Now we can merge the two dataset into one dataset that gives life satisfaction and life worthwhile:

life_sat_worth <- life_satisfaction %>% left_join(life_worthwhile, "LocalGovArea")
head(life_sat_worth)

Dataset 2: Melbourne Housing Market

We remove unnecessory columns:

melbourne_house_prices <- melbourne_house_prices %>% select(-c(1,2,6,7,8,9,10,11))
head(melbourne_house_prices)

Scan II and Transform: Outliers

Some houses might be very old, thus very cheap when compared to the average. On the other hand, some houses might be like mansion or made out of very good materials, thus very expensive. We can consider these as outliers because of sampling error and remove them. But we need proceed with cautious here. Furthermore, since data still in wide format it easier to proceed with outlier scanning.

Let’s first take a subgroup of this dataset which are houses with 3-5 rooms so that there is no huge different in their prices.

house_prices_3to5_rooms <- melbourne_house_prices %>%
  filter((Rooms == 3) | (Rooms == 4) | (Rooms == 5))
dim(house_prices_3to5_rooms)
[1] 47123     5

We are left with 47123 observation which is about 75% of original data.

Let’s take a subset and plot a box plot for a 5 local goverment areas:

tmpHouses <- house_prices_3to5_rooms %>%
  filter((LocalGovArea == "Yarra") | (LocalGovArea == "Melbourne") |
           (LocalGovArea == "Hume") | (LocalGovArea == "Monash") |
           (LocalGovArea == "Glen Eira"))
outliers = boxplot(tmpHouses$Price ~ droplevels(tmpHouses)$LocalGovArea,
                   main="Houses prices by local goverment", ylab = "Price",
                   xlab = "Local Goverment", plot = TRUE)

paste('There are ', length(outliers$out), ' outliers in this subset')
[1] "There are  320  outliers in this subset"

There are many outliers at the top side which indicate skewness, removing these outliers will most probably remove the skewness but there is a valid reason why we have this behavior since it applies to all local goverment areas. So, in this case we better transform our data first to remove skewness and then check for outliers.

If we take Yarra area as an example and plot a histogram:

tmpHouses <- house_prices_3to5_rooms %>% filter((LocalGovArea == "Yarra"))
hist(tmpHouses$Price, breaks = 20, main="Yarra's houses' prices", ylab = "Price", xlab = "Price")

Let’s use Log transformation to try and elimnate skewness

house_prices_3to5_rooms <- house_prices_3to5_rooms %>% mutate(logPrice = log(Price))

Check the skewness in Yarra now:

tmpHouses <- house_prices_3to5_rooms %>% filter((LocalGovArea == "Yarra"))
hist(tmpHouses$logPrice, breaks = 20, main="Yarra's houses' prices", ylab = "Price",
     xlab = "Log(Price)")

Now let’s go back to our subset of local goverments and check the outliers:

tmpHouses <- house_prices_3to5_rooms %>%
  filter((LocalGovArea == "Yarra") | (LocalGovArea == "Melbourne") |
           (LocalGovArea == "Hume") | (LocalGovArea == "Monash") | (LocalGovArea == "Glen Eira"))
outliers <- boxplot(tmpHouses$logPrice ~ droplevels(tmpHouses)$LocalGovArea,
                    main="Houses prices by local goverment", ylab = "logPrice",
                    xlab = "Local Goverment", plot = TRUE)

paste('There are ', length(outliers$out), ' in this subset')
[1] "There are  238  in this subset"

There are now fewer outliers because of the transformation, which means that we are making use of more data.

Now lets omit outliers from the whole dataset using boxplot to find all outliers per group area.

# Use boxplot to store all outliers
all_outliers <- boxplot(house_prices_3to5_rooms$logPrice ~ house_prices_3to5_rooms$LocalGovArea,
                        plot = FALSE)
paste('Overall there are ', length(all_outliers$out), ' outliers.')
[1] "Overall there are  1229  outliers."

Now use the outliers object to go through all the data and omit outliers that were defined by the boxplot.

for(i in seq(1, length(all_outliers$out)))
  house_prices_3to5_rooms <-
  house_prices_3to5_rooms[!((house_prices_3to5_rooms$LocalGovArea==all_outliers$names[all_outliers$group[i]]) & (house_prices_3to5_rooms$logPrice==all_outliers$out[i])),]

The new box plot for the subset

tmpHouses <- house_prices_3to5_rooms %>%
  filter((LocalGovArea == "Yarra") | (LocalGovArea == "Melbourne") | (LocalGovArea == "Hume") |
           (LocalGovArea == "Monash") | (LocalGovArea == "Glen Eira"))
outliers <- boxplot(tmpHouses$logPrice ~ droplevels(tmpHouses)$LocalGovArea,
                    main="Houses prices by local goverment", ylab = "logPrice",
                    xlab = "Local Goverment", plot = TRUE)

As you can see it is better than previous dataset. In this way we can have a good average price for each local goverment.

Tidy & Manipulate Data II

Merging indicators datasets and checking tidiness

Life satisfaction and worthwhile dataframes along with community satisfaction dataframe are both giving indicators about wellbeing of people in specific local goverment area. We will join these two datasets into one.

wellbening_indicators <- life_sat_worth %>% left_join(community_satisfaction, "LocalGovArea")
head(wellbening_indicators)

Check if anything went wrong by checking for NAs

sum(is.na(wellbening_indicators))
[1] 0

All good.

Life statisfaction and worthwhile dataset is not tidy because it has two observations per row, we will use gather function to transform it long format. Although it might take more space, long format is more flexiable for analysis and it is easy to slice data by adding a condition to a column.

wellbening_indicators <- wellbening_indicators %>%
  gather(LifeSatisfaction, LifeWorthwhile, CommunitySatisfaction, key = "Indicator",
         value = "IndicatorPercentage")
head(wellbening_indicators)

Merging Melbourne houses prices dataset with wellbeing indicators

First let’s group melboune_houses_prices by LocalGovArea and calculate mean price and mean distance

house_prices_3to5_rooms <- house_prices_3to5_rooms %>% group_by(LocalGovArea) %>%
  summarise(meanPrice = mean(Price), meanDistance = mean(Distance))
head(house_prices_3to5_rooms)

We can see that the above dataset is in wide format. We consider it tidy because this is how we want it to be when merging the dataset with wellbing indicators dataset.

After grouping, local Goverment Areas are no longer factors, so we change their type to char:

house_prices_3to5_rooms$LocalGovArea <- as.character(house_prices_3to5_rooms$LocalGovArea)

Rename some of the LocalGovArea so they satisfy the naming in the other dataset

house_prices_3to5_rooms$LocalGovArea[house_prices_3to5_rooms$LocalGovArea ==
                                       "Cardinia Shire Council"] <- "Cardinia"
house_prices_3to5_rooms$LocalGovArea[house_prices_3to5_rooms$LocalGovArea ==
                                       "Macedon Ranges Shire Council"] <- "Macedon Ranges"
house_prices_3to5_rooms$LocalGovArea[house_prices_3to5_rooms$LocalGovArea ==
                                       "Mitchell Shire Council"] <- "Mitchell"
house_prices_3to5_rooms$LocalGovArea[house_prices_3to5_rooms$LocalGovArea ==
                                       "Moorabool Shire Council"] <- "Moorabool"
house_prices_3to5_rooms$LocalGovArea[house_prices_3to5_rooms$LocalGovArea ==
                                       "Murrindindi Shire Council"] <- "Murrindindi"
house_prices_3to5_rooms$LocalGovArea[house_prices_3to5_rooms$LocalGovArea ==
                                       "Nillumbik Shire Council"] <- "Nillumbik"
house_prices_3to5_rooms$LocalGovArea[house_prices_3to5_rooms$LocalGovArea ==
                                       "Yarra Ranges Shire Council"] <- "Yarra Ranges"

Now merge Local Goverments Areas houses information with wellbeing indicators, and since we only have 34 local goverment in house_prices_3to5_rooms dataset and 79 in the other dataset, we will left join house_prices_3to5_rooms with wellbening_indicators so that local goverment area that has no house information will be automatically discarded.

final_dataset <- house_prices_3to5_rooms %>% left_join(wellbening_indicators,"LocalGovArea")
head(final_dataset)

Check for NAs

sum(is.na(final_dataset))
[1] 0

None found.

Now our dataset is clean, tidy and ready for analysis.



LS0tCnRpdGxlOiAiTUFUSDIzNDkgRGF0YSBXcmFuZ2xpbmciCmF1dGhvcjogIkhhbWVkIEFsLUphYnJpIHMzNjIzNDM5IgpzdWJ0aXRsZTogQXNzaWdubWVudCAyCm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKIyMgUmVxdWlyZWQgcGFja2FnZXMgCgpgYGB7cn0KIyBUaGlzIGlzIHRoZSBSIGNodW5rIGZvciB0aGUgcmVxdWlyZWQgcGFja2FnZXMKbGlicmFyeShyZWFkcikKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGRwbHlyKSkKbGlicmFyeSh0aWR5cikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHJlYWR4bCkKYGBgCgoKIyMgRXhlY3V0aXZlIFN1bW1hcnkgCgpJbiB0aGlzIFJTdHVkaW8gcmVwb3J0IHdlIGFyZSBjcmVhdGluZyBjbGVhbiBhbmQgdGlkeSBkYXRhIHRoYXQgd2lsbCBiZSB1c2VkIHRvIGFuYWx5c2Ugc29jaWV0eSB3ZWxsYmVpbmcgaW4gZGlmZmVyZW50IExvY2FsIEdvdmVtcmVudCBBcmVhcyBpbiBWaWN0b3JpYS4gVGhlc2UgaW5kaWNhdG9ycyBpbmNsdWRlIGxpZmUgc2F0aXNmYWN0aW9uLCBsaWZlIHdvcnRod2hpbGUgYW5kIG92ZXJhbGwgY29tbXVuaXR5IHNhdGlzZmFjdGlvbiwgYXMgd2VsbCBhcyBob3VzaW5nIHByaWNlcyB0byBnaXZlIGFuIGlkZWEgYWJvdXQgdGhlIHN0YW5kYXJkIG9mIGxpdmluZyBpbiBzcGVjaWZpYyBhcmVhLiBXZSBoYXZlIGxvYWRlZCA0IGRpZmZlcmVudCBkYXRhZnJhbWVzIGZyb20gMyBkaWZmZXJlbnQgZGF0YXNldHMuIEFsbCA0IGRhdGFmcmFtZXMgaGFzIGF0IGxlYXN0IG9uZSB2YXJpYWJsZSB0aGF0IHN0b3JlcyB0aGUgTG9jYWwgR292ZXJtZW50IEFyZWEgYW5kIGl0IHdhcyB1c2VkIHRvIGpvaW4gYWxsIDQgZGF0YWZyYW1lcy4gRmlyc3RseSwgd2UgaGF2ZSBpbXBvcnRlZCB0aGUgZGF0YXNldCB1c2luZyBhcHByb3ByaWF0ZSBmdW5jdGlvbiBhY2NvcmRpbmcgdG8gZmlsZSB0eXBlLiBXZSBtYWRlIHN1cmUgdGhhdCB1bm5lY2Vzc2FyeSBjb2x1bW5zIGFuZCByb3dzIGFyZSBvbWl0dGVkIGZyb20gdGhlIGJlZ2lubmluZy4gVGhlbiwgd2Ugd2VudCB0aHJvdWdoIGFsbCB2YXJpYWJsZXMgaW4gdGhlIGRhdGFmcmFtZXMgYW5kIGNoZWNrZWQgdGhhdCB0aGV5IGhhdmUgdGhlIGNvcnJlY3QgZGF0YSB0eXBlIGFuZCBvdmVyYWxsIGNvcnJlY3Qgc3RydWN0dXJlLiBOZXh0LCB3ZSBtdXRhdGVkIG91ciBkYXRhZnJhbWVzIGFuZCBjcmVhdGUgZXh0cmEgdmFyaWFibGVzIHRoYXQgZ2l2ZXMgYmV0dGVyIGluc2lnaHRzIGFib3V0IG91ciBkYXRhLiBBZnRlciB0aGF0LCBhbmQgYmVmb3JlIGdvaW5nIGludG8gZnVydGhlciBkYXRhIG1hbmlwdWxhdGlvbiwgd2UgZGVhbHQgd2l0aCBtaXNzaW5nIHZhbHVlcyBhbmQgb3V0bGllcnMsIGJlY2F1c2UgaXQgd2FzIGVhc2llciB0byBkbyB0aGF0IHdoaWxlIGRhdGEgd2FzIGluIHdpZGUgZm9ybWF0LiBNb3Jlb3ZlciwgZGF0YSB0cmFuc2Zvcm1hdGlvbiB3YXMgYXBwbGllZCB0byBoZWxwIGdhaW4gYmV0dGVyIHVuZGVyc3RhbmRpbmcgYWJvdXQgb3VyIGRhdGEgYW5kIGhlbHBlZCB1cyBiZXR0ZXIgaWRlbnRpZnkgb3V0bGllcnMuIE5leHQsIHdlIG1hZGUgc3VyZSB0aGF0IGFsbCBmb3VyIGRhdGFmcmFtZXMgaGFzIGEgY29uc2lzdGVudCBjb21tb24gdmFyaWFibGUgdGhhdCBjYW4gYmUgdXNlZCBmb3IgbWVyZ2luZyBhbmQgdGhlbiB3ZSBtZXJnZWQgdGhhdCBkYXRhc2V0IGFsbCB0b2dldGhlci4gRmluYWxseSwgd2UgbWFkZSBvdXIgZGF0YSBtb3JlIHRpZHkgYW5kIHJlYWR5IGZvciBhbmFseXNpcyBieSBjaGFuZ2luZyBpdCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQuIAoKTm90ZTogVGhlIG9yZGVyIG9mIHRhc2tzIGdpdmVuIGluIHRoZSBhc3NpZ25tZW50IHNoZWV0IGlzIGRpZmZlcmVudCB0aGFuIHRoZSBvcmRlciB1c2VkIGhlcmUsIGFuZCB0aGF0IGlzIGR1ZSB0byBuZWNlc3NpdHkgdG8gcHJlcHJvY2VzcyB0aGUgZGF0YS4KCiMjIERhdGEgCiMjIyBEYXRhc2V0IDE6IExpZmUgc2F0aXNmYWN0aW9uIGFuZCB3b3J0aHdoaWxlClZpY3RvcmlhbiBQb3B1bGF0aW9uIEhlYWx0aCBTdXJ2ZXkgMjAxNyBkYXRhc2V0IGlzIHByb3ZpZGVkIGJ5IFRoZSBEZXBhcnRtZW50IG9mIEhlYWx0aCBhbmQgSHVtYW4gU2VydmljZXMuIFRoZSBkYXRhc2V0IGluY2x1ZGVzIGRpZmZlcmVudCBoZWFsdGggaW5kaWNhdG9ycyBhYm91dCBwb3B1bHRhdGlvbiBhY2NvcmRpbmcgdG8gbG9jYWwgZ292ZXJtZW50IGFyZWFzIGluIFZpY3J0b3JpYS4gVGhlIGRhdGFzZXQgaW4gYW4gRXhjZWwgZmlsZSB0aGF0IGhhcyBkaWZmZXJlbnQgc2hlZXRzLCB3aGVyZSBlYWNoIHNoZWV0IGhhcyBvbmUgaW5kaWNhdG9yLiBJbiB0aGlzIHJlcG9ydCB3ZSB3aWxsIHVzZSB0d28gc2hlZXRzIG9ubHksIGxpZmUgc2F0aXNmYWN0aW9uIGFuZCBsaWZlIHdvcnRod2hpbGUuCgpQYWdlIGxpbms6IGh0dHBzOi8vd3d3Mi5oZWFsdGgudmljLmdvdi5hdS9wdWJsaWMtaGVhbHRoL3BvcHVsYXRpb24taGVhbHRoLXN5c3RlbXMvaGVhbHRoLXN0YXR1cy1vZi12aWN0b3JpYW5zL3N1cnZleS1kYXRhLWFuZC1yZXBvcnRzL3ZpY3Rvcmlhbi1wb3B1bGF0aW9uLWhlYWx0aC1zdXJ2ZXkvdmljdG9yaWFuLXBvcHVsYXRpb24taGVhbHRoLXN1cnZleS0yMDE3CgpEb3dubG9hZCBsaW5rOiBodHRwczovL3d3dzIuaGVhbHRoLnZpYy5nb3YuYXUvQXBpL2Rvd25sb2FkbWVkaWEvJTdCMUFGNUQ1NjAtNDE0NC00QUZFLUJEQkEtN0NGQTQzMTg2NUU1JTdECgoKRGF0YXNldCAxYSB2YXJpYWJsZXMgZGVzY3JpcHRpb246CgoxLSBMb2NhbEdvdkFyZWE6IHRoZSBuYW1lIG9mIHRoZSBWaWN0b3JpYW4gbG9jYWwgZ292ZXJtZW50IGNvdW5jaWwsIHRoZXJlIGFyZSBvdmVyYWxsIDc5IGxvY2FsIGdvdmVybWVudHMKCjItIExvd0FuZE1lZGl1bSBGZW1hbGU6IHBlcmNlbnRhZ2Ugb2YgZmVtYWxlIHdobyBoYXZlIGxvdyBvciBtZWRpdW0gbGlmZSBzYXRpc2ZhY3Rpb24KCjMtIExvd0FuZE1lZGl1bSBNYWxlOiBwZXJjZW50YWdlIG9mIG1hbGUgd2hvIGhhdmUgbG93IG9yIG1lZGl1bSBsaWZlIHNhdGlzZmFjdGlvbgoKNC0gSGlnaCBGZW1hbGU6IHBlcmNlbnRhZ2Ugb2YgZmVtYWxlIHdobyBoYXZlIGhpZ2ggbGlmZSBzYXRpc2ZhY3Rpb24KCjUtIEhpZ2ggTWFsZTogcGVyY2VudGFnZSBvZiBtYWxlIHdobyBoYXZlIGhpZ2ggbGlmZSBzYXRpc2ZhY3Rpb24KCjYtIFZlcnlIaWdoIEZlbWFsZTogcGVyY2VudGFnZSBvZiBmZW1hbGUgd2hvIGhhdmUgdmVyeSBoaWdoIGxpZmUgc2F0aXNmYWN0aW9uCgo3LSBWZXJ5SGlnaCBNYWxlOiBwZXJjZW50YWdlIG9mIG1hbGUgd2hvIGhhdmUgdmVyeSBoaWdoIGxpZmUgc2F0aXNmYWN0aW9uCgoKTGV0J3MgaW1wb3J0IHRoZSBkYXRhc2V0IHdoaWNoIGlzIGFuIHhsc3ggZmlsZSB0aGF0IGNvbnRhaW5zIG1vcmUgdGhhbiBvbmUgc2hlZXQsIHdlIHdpbGwgdXNlIHR3byBzaGVldHMgb25seSAobGlmZSBzYXRpc2ZhY3Rpb24gYW5kIGxpZmUgd29ydGh3aGlsZSkuIFdlIHdpbGwgaW1wb3J0IGV2ZXJ5dGluZyBmaXJzdCwgYW5kIHdlIHdpbGwgZXhjbHVkZSB0aGUgZmlyc3QgNyByb3dzIHRoYXQgY29udGFpbnMgbm8gb2JzZXJ2YXRpb25zLgpgYGB7cn0KIyBJbXBvcnRpbmcgZGF0YSAxYQpzdXBwcmVzc01lc3NhZ2VzKGxpZmVfc2F0aXNmYWN0aW9uIDwtIHJlYWRfZXhjZWwoIlZQSFMgMjAxNyBieSBMR0EgYW5kIGdlbmRlciAtIHYyLnhsc3giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ID0gIkxpZmUgc2F0aXNmYWNhdGlvbiIsIGNvbF9uYW1lcyA9IEZBTFNFLCBza2lwID0gNykpCiMgUmVtb3ZlIHVubmVjZXNzYXJ5IGNvbHVtbnMKbGlmZV9zYXRpc2ZhY3Rpb24gPC0gbGlmZV9zYXRpc2ZhY3Rpb24gJT4lICBzZWxlY3QoMiwzLDEwLDE3LDI0LDMxLDM4KQojIFJlbW92ZSB1bm5lY2Vzc2FyeSByb3dzIGF0IHRoZSBib3R0b20gb2YgdGhlIHNoZWV0CmxpZmVfc2F0aXNmYWN0aW9uIDwtIGxpZmVfc2F0aXNmYWN0aW9uWy1zZXEoODAsOTQpLCBdCiMgRGVmaW5lIGNvbHVtbnMgbmFtZXMKY29sbmFtZXMobGlmZV9zYXRpc2ZhY3Rpb24pIDwtIGMoIkxvY2FsR292QXJlYSIsIkxvd0FuZE1lZGl1bSBGZW1hbGUiLCJMb3dBbmRNZWRpdW0gTWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSGlnaCBGZW1hbGUiLCAiSGlnaCBNYWxlIiwgIlZlcnlIaWdoIEZlbWFsZSIsICJWZXJ5SGlnaCBNYWxlIikKaGVhZChsaWZlX3NhdGlzZmFjdGlvbikKYGBgCgpEYXRhc2V0IDFiIHZhcmlhYmxlcyBkZXNjcmlwdGlvbjoKCjEtIExvY2FsR292QXJlYTogdGhlIG5hbWUgb2YgdGhlIFZpY3RvcmlhbiBsb2NhbCBnb3Zlcm1lbnQgY291bmNpbCwgdGhlcmUgYXJlIG92ZXJhbGwgNzkgbG9jYWwgZ292ZXJtZW50cwoKMi0gTG93QW5kTWVkaXVtIEZlbWFsZTogcGVyY2VudGFnZSBvZiBmZW1hbGUgd2hvIGhhdmUgbG93IG9yIG1lZGl1bSBsaWZlIHdvcnRod2hpbGUKCjMtIExvd0FuZE1lZGl1bSBNYWxlOiBwZXJjZW50YWdlIG9mIG1hbGUgd2hvIGhhdmUgbG93IG9yIG1lZGl1bSBsaWZlIHdvcnRod2hpbGUKCjQtIEhpZ2ggRmVtYWxlOiBwZXJjZW50YWdlIG9mIGZlbWFsZSB3aG8gaGF2ZSBoaWdoIGxpZmUgd29ydGh3aGlsZQoKNS0gSGlnaCBNYWxlOiBwZXJjZW50YWdlIG9mIG1hbGUgd2hvIGhhdmUgaGlnaCBsaWZlIHdvcnRod2hpbGUKCjYtIFZlcnlIaWdoIEZlbWFsZTogcGVyY2VudGFnZSBvZiBmZW1hbGUgd2hvIGhhdmUgdmVyeSBoaWdoIGxpZmUgd29ydGh3aGlsZQoKNy0gVmVyeUhpZ2ggTWFsZTogcGVyY2VudGFnZSBvZiBtYWxlIHdobyBoYXZlIHZlcnkgaGlnaCBsaWZlIHdvcnRod2hpbGUKCgpJbXBvcnRpbmcgdGhlIHNlY29uZCBzaGVldDoKYGBge3J9CiMgSW1wb3J0aW5nIGRhdGEgMWIKc3VwcHJlc3NNZXNzYWdlcyhsaWZlX3dvcnRod2hpbGUgPC0gcmVhZF9leGNlbCgiVlBIUyAyMDE3IGJ5IExHQSBhbmQgZ2VuZGVyIC0gdjIueGxzeCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ID0gIkxpZmUgd29ydGh3aGlsZSIsIGNvbF9uYW1lcyA9IEZBTFNFLCBza2lwID0gNykpCiMgUmVtb3ZlIHVubmVjZXNzYXJ5IGNvbHVtbnMKbGlmZV93b3J0aHdoaWxlIDwtIGxpZmVfd29ydGh3aGlsZSAlPiUgc2VsZWN0KDIsMywxMCwxNywyNCwzMSwzOCkKIyBSZW1vdmUgdW5uZWNlc3Nhcnkgcm93cyBhdCB0aGUgYm90dG9tIG9mIHRoZSBzaGVldApsaWZlX3dvcnRod2hpbGUgPC0gbGlmZV93b3J0aHdoaWxlWy1zZXEoODAsOTQpLCBdCiMgRGVmaW5lIGNvbHVtbnMgbmFtZXMKY29sbmFtZXMobGlmZV93b3J0aHdoaWxlKSA8LSBjKCJMb2NhbEdvdkFyZWEiLCJMb3dBbmRNZWRpdW0gRmVtYWxlIiwiTG93QW5kTWVkaXVtIE1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkhpZ2ggRmVtYWxlIiwgIkhpZ2ggTWFsZSIsICJWZXJ5SGlnaCBGZW1hbGUiLCAiVmVyeUhpZ2ggTWFsZSIpCmhlYWQobGlmZV93b3J0aHdoaWxlKQpgYGAKCiMjIyBEYXRhc2V0IDI6IE1lbGJvdXJuZSBIb3VzaW5nIE1hcmtldApNZWxib3VybmUgSG91c2luZyBNYXJrZXQgaXMgYSBkYXRhc2V0IHRoYXQgaXMgcHVibGlzaGVkIGluIEthZ2dsZSB3aGljaCBoYXMgb3ZlciA2MCwwMDAgb2JlcnZhdGlvbnMuIEVhY2ggb2JzZXJ2YXRpb24gaXMgYSBob3VzZS91bml0L3Rvd25ob3VzZSB0aGF0IGlzIGZvciBzYWxlIGFuZCBpbmNsdWRlcyBpbmZvcm1hdGlvbiBsaWtlIHByaWNlLCBudW1iZXIgb2YgYmVkcm9vbXMsIGFkZHJlc3MgYW5kIGxvY2FsIGdvdmVybWVudCBhcmVhLiBUaGF0IGRhdGFzZXQgaXMgaW4gYSBDU1YgZmlsZS4KClBhZ2UgbGluazogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9hbnRob255cGluby9tZWxib3VybmUtaG91c2luZy1tYXJrZXQKCkRvd25sb2FkIGxpbms6IGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vYW50aG9ueXBpbm8vbWVsYm91cm5lLWhvdXNpbmctbWFya2V0L2Rvd25sb2FkCgoKRGF0YXNldCB2YXJpYWJsZXMgZGVzY3JpcHRpb246CgoxLSBTdWJ1cmIgICAgMi0gQWRkcmVzcyAgICAzLSBSb29tcyAgICAgNC0gVHlwZQoKNS0gUHJpY2UgICAgIDYtTWV0aG9kICAgICAgNy0gU2VsbGVyICAgIDgtIERhdGUKCjktIFBvc3Rjb2RlICAxMC0gUmVnb2lubmFtZQoKMTEtIFByb3BlcnR5Y291bnQ6IG51bWJlciBvZiBwcm9wZXJ0aWVzIGZvciBzYWxlIGluIHNhbWUgY291bmNpbCBhcmVhCgoxMi0gRGlzdGFuY2U6IGRpc3RhbmNlIGZyb20gTWVsYm91cm5lIENCRAoKMTMtIENvdW5jaWxBcmVhOiBMb2NhbCBnb3Zlcm1lbnQgYXJlYQoKCkxldCdzIGltcG9ydCB0aGF0IGRhdGFzZXQuCmBgYHtyfQpzdXBwcmVzc01lc3NhZ2VzKG1lbGJvdXJuZV9ob3VzZV9wcmljZXMgPC0gcmVhZF9jc3YoIk1FTEJPVVJORV9IT1VTRV9QUklDRVNfTEVTUy5jc3YiKSkKaGVhZChtZWxib3VybmVfaG91c2VfcHJpY2VzKQpgYGAKCiMjIyBEYXRhc2V0IDM6IEtub3cgWW91IENvdW5jaWwKVGhpcyBkYXRhc2V0LCB3aGljaCBpcyBwcm92aWRlZCBieSBLbm93IFlvdXIgQ291bmNpbCB3ZWJzaXRlLCBnaXZlcyBkaWZmZXJlbnQgaW5kaWNhdG9ycyB0byBldmFsdWF0ZSB0aGUgcGVyZm9ybWFjZSBvZiBlYWNoIGxvY2FsIGdvdmVybWVudCBhcmVhIGluIFZpY3RvcmlhLiBUaGVyZSBhcmUgb3ZlciA2MCBpbmRpY2F0b3JzLCBidXQgd2Ugd2lsbCBvbmx5IHVzZSBvbmUgaW5kaWNhdG9yIHRoYXQgaGFzIHRvIGRvIHdpdGggc29jaWV0eSBzYXRpc2ZhY3Rpb24gYWJvdXQgdGhlIGNvdW5jaWwncyBzZXJ2aWNlcy4KClBhZ2UgbGluazogaHR0cHM6Ly9kaXNjb3Zlci5kYXRhLnZpYy5nb3YuYXUvZGF0YXNldC9rbm93LXlvdXItY291bmNpbC1sb2NhbC1nb3Zlcm5tZW50LXBlcmZvcm1hbmNlLXJlcG9ydGluZy1mcmFtZXdvcmsKCkRvd25sb2FkIGxpbms6IGh0dHBzOi8va25vd3lvdXJjb3VuY2lsLnZpYy5nb3YuYXUvX19kYXRhL2Fzc2V0cy9leGNlbF9kb2MvMDAxMC8zMjg4Ny9MR1BSRi0yMDE0LTIwMTktRnVsbC1Db3VuY2lsLURhdGEtU2V0LUVYVEVSTkFMLnhsc3gKCgpEYXRhc2V0IHZhcmlhYmxlcyBkZXNjcmlwdGlvbjoKCjEtIENvdW5jaWw6IGxvY2FsIGdvdmVybm1lbnQgYXJlYSBuYW1lCgoyLSBHcm91cDogZ292ZXJtZW50IGdyb3VwCgozLSBJRAoKNC0gRGVzY3JpcHRpbzogaW5kaWNhdG9yJ3MgZGVzY3JpcHRpb24KCjUtIFNlcnZpY2UgUHJvdmlkZWQ6IFQvRiBpZiBzZXJ2aWNlIGlzIHByb3ZpZGVkIG9yIG5vdAoKNi0gUmVzdWx0OiB0aGUgbnVtZXJpY2FsIHZhbHVlIHVzZWQgYXMgcmVzdWx0IGZvciBlYWNoIGluZGljYXRvcgoKCk5vdyBsZXQKYGBge3J9CnZpY19jb3VuY2lsc19pbmRpY2F0b3JzIDwtIHJlYWRfZXhjZWwoIkxHUFJGLTIwMTQtMjAxOS1GdWxsLUNvdW5jaWwtRGF0YS1TZXQtRVhURVJOQUwueGxzeCIsIAogICAgIHNoZWV0ID0gIkluZGljYXRvcnMgMjAxNy0xOCIsIGNvbF90eXBlcyA9IGMoInRleHQiLCAKICAgICAgICAgInRleHQiLCAidGV4dCIsICJ0ZXh0IiwgInRleHQiLCAidGV4dCIsIAogICAgICAgICAibnVtZXJpYyIsICJza2lwIiwgInNraXAiLCAic2tpcCIsIAogICAgICAgICAic2tpcCIsICJza2lwIiwgInNraXAiLCAic2tpcCIsICJza2lwIiwgCiAgICAgICAgICJza2lwIikpCmhlYWQodmljX2NvdW5jaWxzX2luZGljYXRvcnMpCmBgYAoKCiMjIFVuZGVyc3RhbmQgCiMjIyBEYXRhc2V0IDE6IExpZmUgc2F0aXNmYWN0aW9uIGFuZCB3b3J0aHdoaWxlCgpDaGVja2luZyB0aGUgZGltZW5zaW9uIG9mIGRhdGFzZXQgMWE6CmBgYHtyfQojRGltZW5zaW9uIG9mIGRhdGFzZXQgMWEKZGltKGxpZmVfc2F0aXNmYWN0aW9uKQojSG93IG1hbnkgdmFsdWVzIGZvciBMb2NhbCBnb3Zlcm1lbnRzIGFyZWFzCnBhc3RlKCJUaGVyZSBhcmUgIiwgZGltKGxpZmVfc2F0aXNmYWN0aW9uKVsxXSwgIiBsb2NhbCBnb3Zlcm1lbnQgYXJlYXMgaW4gVmljdG9yaWEiKQpgYGAKYGBge3J9CiNTdHJ1Y3R1cmUgb2YgdGhlIGRhdGFzZXQKc3RyKGxpZmVfc2F0aXNmYWN0aW9uKQpgYGAKYGBge3J9CiMgVXNpbmcgYSBjb21iaW5hdGlvbiBvZiAoc2FwcGx5KSBhbmQgKHR5cGVvZiksIGxldCdzIGNoZWNrIHRoZSBkYXRhIHR5cGUgb2YgZWFjaCBjb2x1bW4Kc2FwcGx5KGxpZmVfc2F0aXNmYWN0aW9uLHR5cGVvZikKYGBgCkFsbCBkYXRhIHR5cGVzIG9mIGFsbCBjb2x1bW5zIGFyZSBPSyBpbiB0aGlzIGRhdGFzZXQuCgoKQ2hlY2tpbmcgdGhlIGRpbWVuc2lvbiBvZiBkYXRhc2V0IDFiOgpgYGB7cn0KI0RpbWVuc2lvbiBvZiBkYXRhc2V0IDFiCmRpbShsaWZlX3dvcnRod2hpbGUpCiNIb3cgbWFueSB2YWx1ZXMgZm9yIExvY2FsIGdvdmVybWVudHMgYXJlYXMKcGFzdGUoIlRoZXJlIGFyZSAiLCBkaW0obGlmZV93b3J0aHdoaWxlKVsxXSwgIiBsb2NhbCBnb3Zlcm1lbnQgYXJlYXMgaW4gVmljdG9yaWEiKQpgYGAKQ2hlY2tpbmcgdGhlIHN0cnVjdHVyZToKYGBge3J9CiNTdHJ1Y3R1cmUgb2YgdGhlIGRhdGFzZXQKc3RyKGxpZmVfd29ydGh3aGlsZSkKYGBgCgpgYGB7cn0KIyBVc2luZyBhIGNvbWJpbmF0aW9uIG9mIChzYXBwbHkpIGFuZCAodHlwZW9mKSwgbGV0J3MgY2hlY2sgdGhlIGRhdGEgdHlwZSBvZiBlYWNoIGNvbHVtbgpzYXBwbHkobGlmZV93b3J0aHdoaWxlLHR5cGVvZikKYGBgCkFsbCBkYXRhIHR5cGVzIG9mIGFsbCBjb2x1bW5zIGFyZSBPSyBpbiB0aGlzIGRhdGFzZXQuCgoKQm90aCBkYXRhZnJhbWVzIDFhIGFuZCAxYiBoYXMgTG9jYWwgR292ZXJtZW50IEFyZWFzIGNvbHVtbiB3aXRoIEdyb3VwIHR5cGUgYmV0d2VlbiBicmFja2V0cy4gV2UgbmVlZCB0byByZW5hbWUgaXQgYW5kIHJlbW92ZSBicmFja2V0cyBzbyB0aGF0IHRoZXkgYXJlIGNvbnNpc3RlbnQgd2l0aCBvdGhlciBkYXRhZnJhbWVzLgpgYGB7cn0KI0JlZm9yZQpoZWFkKGxpZmVfc2F0aXNmYWN0aW9uJExvY2FsR292QXJlYSkKYGBgCgpgYGB7cn0KI2RhdGFzZXQgMWEKbGlmZV9zYXRpc2ZhY3Rpb24kTG9jYWxHb3ZBcmVhIDwtIGdzdWIoIlxcKC4qPykiLCIiLGxpZmVfc2F0aXNmYWN0aW9uJExvY2FsR292QXJlYSkKI2RhdGFzZXQgMWEKbGlmZV93b3J0aHdoaWxlJExvY2FsR292QXJlYSA8LSBnc3ViKCJcXCguKj8pIiwiIixsaWZlX3dvcnRod2hpbGUkTG9jYWxHb3ZBcmVhKQojIE5vdyByZW1vdmUgcmVzdWx0aW5nIHdoaXRlc3BhY2VzCmxpZmVfc2F0aXNmYWN0aW9uJExvY2FsR292QXJlYSA8LSB0cmltd3MobGlmZV9zYXRpc2ZhY3Rpb24kTG9jYWxHb3ZBcmVhLCJyIikKbGlmZV93b3J0aHdoaWxlJExvY2FsR292QXJlYSA8LSB0cmltd3MobGlmZV93b3J0aHdoaWxlJExvY2FsR292QXJlYSwiciIpCiNBZnRlcgpoZWFkKGxpZmVfc2F0aXNmYWN0aW9uJExvY2FsR292QXJlYSkKYGBgCgojIyMgRGF0YXNldCAyOiBNZWxib3VybmUgSG91c2luZyBNYXJrZXQKQ2hlY2sgdGhlIGRpbWVuc2lvbnMgb2YgdGhlIGRhdGFzZXQ6CmBgYHtyfQpkaW0obWVsYm91cm5lX2hvdXNlX3ByaWNlcykKcGFzdGUoIlRoZXJlIGFyZSAiLCBkaW0obWVsYm91cm5lX2hvdXNlX3ByaWNlcylbMV0sICIgb2JzZXJ2YXRpb25zIGluIHRoaXMgZGF0YXNldCIpCmBgYApDaGVjayB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhZnJhbWU6CmBgYHtyfQojU3RydWN0dXJlIG9mIHRoZSBkYXRhc2V0CnN0cihtZWxib3VybmVfaG91c2VfcHJpY2VzKQpgYGAKU2luY2Ugd2UgYXJlIGdvaW5nIHRvIG1hdGNoIGRhdGFzZXRzIHdpdGggbG9jYWwgZ292ZXJtZW50IGFyZWEsIHdlIG5lZWQgdG8gcmVtb3ZlIHRoZSBwYXR0ZXJuICJDaXR5IENvdW5jaWwiIGZyb20gQ291bmNpbCBBcmVhLgpgYGB7cn0KIyBCZWZvcmUKaGVhZChtZWxib3VybmVfaG91c2VfcHJpY2VzJENvdW5jaWxBcmVhKQpgYGAKCmBgYHtyfQojIFJlbW92aW5nIHBhdHRlcm4KbWVsYm91cm5lX2hvdXNlX3ByaWNlcyRDb3VuY2lsQXJlYSA8LSAgc3RyX3JlbW92ZShtZWxib3VybmVfaG91c2VfcHJpY2VzJENvdW5jaWxBcmVhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIgQ2l0eSBDb3VuY2lsIikKIyBBZnRlcgpoZWFkKG1lbGJvdXJuZV9ob3VzZV9wcmljZXMkQ291bmNpbEFyZWEpCmBgYApMZXQncyByZW5hbWUgQ291bmNpbEFyZWEgY29sdW1uIG5hbWUgdG8gTG9jYWxHb3ZBcmVhIHNvIGl0IG1hdGNoZXMgb3RoZXIgZGF0YWZyYW1lcyB0byBiZSBtZXJnZWQgbGF0ZXIKYGBge3J9CmNvbG5hbWVzKG1lbGJvdXJuZV9ob3VzZV9wcmljZXMpWzEzXSA8LSAiTG9jYWxHb3ZBcmVhIgpgYGAKClRoZXJzZSBhcmUgY29sdW1ucyB0aGF0IGNhbiBiZSBjaGFuZ2VkIHRvIGZhY3RvcnM6CmBgYHtyfQpwYXN0ZSgiVGhlcmUgYXJlICIsbGVuZ3RoKHVuaXF1ZShtZWxib3VybmVfaG91c2VfcHJpY2VzJFJvb21zKSksIiBzaXplcyBhY2NvcmRpbmcgdG8gcm9vbXMgbnVtYmVyIikKcGFzdGUoIlRoZXJlIGFyZSAiLGxlbmd0aCh1bmlxdWUobWVsYm91cm5lX2hvdXNlX3ByaWNlcyRTdWJ1cmIpKSwgIiB1bmlxdWUgU3VidXJicyIpCnBhc3RlKCJUaGVyZSBhcmUgIixsZW5ndGgodW5pcXVlKG1lbGJvdXJuZV9ob3VzZV9wcmljZXMkVHlwZSkpLCAiIHR5cGVzIG9mIEhvdXNlcyIpCnBhc3RlKCJUaGVyZSBhcmUgIixsZW5ndGgodW5pcXVlKG1lbGJvdXJuZV9ob3VzZV9wcmljZXMkUmVnaW9ubmFtZSkpLCAiIHVuaXF1ZSBSZWdpb25uYW1lIikKcGFzdGUoIlRoZXJlIGFyZSAiLGxlbmd0aCh1bmlxdWUobWVsYm91cm5lX2hvdXNlX3ByaWNlcyRMb2NhbEdvdkFyZWEpKSwgIiB1bmlxdWUgTG9jYWwgZ292ZXJtZW50IGFyZWFzIikKYGBgCkFzIHdlIGNhbiBzZWUgdGhpcyBkYXRhc2V0IG9ubHkgaGFzIDM0IExvY2FsIEdvdmVybm1lbnQgYXJlYXMsIHdoaWxlIHRoZSBwcmV2aW91cyBkYXRhc2V0IGhhZCA3OS4KClRoZXNlIHVuaXF1ZSB2YWx1ZXMgY2FuIGJlIGNvbnZlcnRlZCB0byBmYWN0b3JzIGluc3RlYWQsIGFuZCBzb21lIGNhbiBnaXZlbiBiZXR0ZXIgbWVhbmluZ2Z1bCBsYWJlbHMuCmBgYHtyfQptZWxib3VybmVfaG91c2VfcHJpY2VzJFJvb21zID0gZmFjdG9yKG1lbGJvdXJuZV9ob3VzZV9wcmljZXMkUm9vbXMsIGxldmVscyA9IHVuaXF1ZShtZWxib3VybmVfaG91c2VfcHJpY2VzJFJvb21zKSwgb3JkZXJlZCA9IEZBTFNFKQptZWxib3VybmVfaG91c2VfcHJpY2VzJFN1YnVyYiA9IGZhY3RvcihtZWxib3VybmVfaG91c2VfcHJpY2VzJFN1YnVyYiwgbGV2ZWxzID0gdW5pcXVlKG1lbGJvdXJuZV9ob3VzZV9wcmljZXMkU3VidXJiKSwgb3JkZXJlZCA9IEZBTFNFKQptZWxib3VybmVfaG91c2VfcHJpY2VzJFJlZ2lvbm5hbWUgPSBmYWN0b3IobWVsYm91cm5lX2hvdXNlX3ByaWNlcyRSZWdpb25uYW1lLCBsZXZlbHMgPSB1bmlxdWUobWVsYm91cm5lX2hvdXNlX3ByaWNlcyRSZWdpb25uYW1lKSwgb3JkZXJlZCA9IEZBTFNFKQptZWxib3VybmVfaG91c2VfcHJpY2VzJExvY2FsR292QXJlYSA9IGZhY3RvcihtZWxib3VybmVfaG91c2VfcHJpY2VzJExvY2FsR292QXJlYSwgbGV2ZWxzID0gdW5pcXVlKG1lbGJvdXJuZV9ob3VzZV9wcmljZXMkTG9jYWxHb3ZBcmVhKSwgb3JkZXJlZCA9IEZBTFNFKQoKIyBMZXQncyBhbHNvIGNvbnZlcnQgVHlwZSB0byBmYWN0b3IgYW5kIGdpdmUgbGV2ZWxzIG1lYW5pbmdmdWwgbGFiZWxzCm1lbGJvdXJuZV9ob3VzZV9wcmljZXMkVHlwZSA8LSBmYWN0b3IobWVsYm91cm5lX2hvdXNlX3ByaWNlcyRUeXBlLGxldmVscz1jKCdoJywndScsJ3QnKSwgbGFiZWxzPWMoJ2hvdXNlJywndW5pdCcsJ3Rvd25ob3VzZScpLCBvcmRlcmVkID0gRkFMU0UpCmhlYWQobWVsYm91cm5lX2hvdXNlX3ByaWNlcyRUeXBlKQpgYGAKCk5vdyBjb252ZXJ0IHRoZSBkYXRlIGNvbHVtbiB0byBkYXRlIHR5cGUKYGBge3J9Cm1lbGJvdXJuZV9ob3VzZV9wcmljZXMkRGF0ZSA8LSBhcy5EYXRlKG1lbGJvdXJuZV9ob3VzZV9wcmljZXMkRGF0ZSwgIiVkLyVtLyVZIikKYGBgCgpMZXQncyBjaGVjayBldmVyeXRoaW5nIGlzIG5vdyBvayBpbiB0aGlzIGRhdGFmcmFtZToKYGBge3J9CiNTdHJ1Y3R1cmUgb2YgdGhlIGRhdGFzZXQKc3RyKG1lbGJvdXJuZV9ob3VzZV9wcmljZXMpCmBgYApBbGwgZ29vZC4KCiMjIyBEYXRhc2V0IDM6IEtub3cgWW91IENvdW5jaWwKQ2hlY2tpbmcgZGltZW5zaW9ucwpgYGB7cn0KZGltKHZpY19jb3VuY2lsc19pbmRpY2F0b3JzKQpgYGAKVGhpcyBkYXRhc2V0IGlzIHRvbyBiaWcgZm9yIG91ciBuZWVkLCB3ZSB3aWxsIG9ubHkgdGFrZSBvYmVydmF0aW9ucyB0aGF0IHdlIG5lZWQgZm9yIG91ciBhbmFseXNpcy4gV2UgbmVlZCAnQ29tbXVuaXR5IFNhdGlzZmFjdGlvbicgb25seSBzbyB3ZSB3aWxsIGZpbHRlciBkYXRhZnJhbWUgdG8gb25seSBnaXZlIHRoaXMgaW5kaWNhdG9yOgpgYGB7cn0KIyBVc2UgZmlsdGVyIHRvIHVzZSBDb21tdW5pdHkgU2F0aXNmYWN0aW9uIG9ubHkKdmljX2NvdW5jaWxzX2luZGljYXRvcnMgPC0gdmljX2NvdW5jaWxzX2luZGljYXRvcnMgJT4lCiAgZmlsdGVyKERlc2NyaXB0aW9uID09ICJDb21tdW5pdHkgc2F0aXNmYWN0aW9uIHJhdGluZyBvdXQgMTAwIHdpdGggaG93IGNvdW5jaWwgaGFzIHBlcmZvcm1lZCBpbiBtYWtpbmcgZGVjaXNpb25zIGluIHRoZSBpbnRlcmVzdHMgb2YgdGhlIGNvbW11bml0eSIpCiMgQ2hlY2sgdGhhdCB3ZSBoYXZlIDc5IGxvY2FsIGdvdmVybWVudHMKcGFzdGUoIlRoZXJlIGFyZSAiLCBkaW0odmljX2NvdW5jaWxzX2luZGljYXRvcnMpWzFdLCAiIGxvY2FsIGdvdmVybWVudCBhcmVhcyBpbiB0aGlzIGRhdGFzZXQiKQpgYGAKUmVtb3ZlIHVubmVjZXNzYXJ5IHZhcmlhYmxlcyBhbmQgZ2l2ZSBhIGJldHRlciBuYW1lIGZvciB0aGUgZGF0YXNldDoKYGBge3J9CmNvbW11bml0eV9zYXRpc2ZhY3Rpb24gPC0gdmljX2NvdW5jaWxzX2luZGljYXRvcnMgJT4lIHNlbGVjdCgxLDIsNykKYGBgCkFsc28sIGNoYW5nZSBjb2x1bW4gbmFtZXMgdG8gbWF0Y2ggb3RoZXIgZGF0YXNldDoKYGBge3J9CmNvbG5hbWVzKGNvbW11bml0eV9zYXRpc2ZhY3Rpb24pWzFdIDwtICJMb2NhbEdvdkFyZWEiCmNvbG5hbWVzKGNvbW11bml0eV9zYXRpc2ZhY3Rpb24pWzNdIDwtICJDb21tdW5pdHlTYXRpc2ZhY3Rpb24iCmBgYApMZXQncyBjaGVjayB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhc2V0IG5vdzoKYGBge3J9CiNTdHJ1Y3R1cmUgb2YgdGhlIGRhdGFzZXQKc3RyKGNvbW11bml0eV9zYXRpc2ZhY3Rpb24pCmBgYApDaGVjayBHcm91cCB2YXJpYWJsZSBpZiBjYW4gYmUgY29udmVydGVkIHRvIGZhY3RvcjoKYGBge3J9CnVuaXF1ZShjb21tdW5pdHlfc2F0aXNmYWN0aW9uJEdyb3VwKQpgYGAKClRodXMsIHdlIG5lZWQgdG8gY2hhbmdlIGdyb3VwIHRvIGZhY3RvcjoKYGBge3J9CmNvbW11bml0eV9zYXRpc2ZhY3Rpb24kR3JvdXAgPSBmYWN0b3IoY29tbXVuaXR5X3NhdGlzZmFjdGlvbiRHcm91cCwgbGV2ZWxzID0gdW5pcXVlKGNvbW11bml0eV9zYXRpc2ZhY3Rpb24kR3JvdXApLCBvcmRlcmVkID0gRkFMU0UpCmBgYAoKT25lIGxhc3QgdGhpbmcsIHdlIG5lZWQgdG8gcmVuYW1lIHNvbWUgTG9jYWwgR292ZXJtZW50IEFyZWFzIHNvIHRoZSBuYW1lIGlzIGFsaWduZWQgd2l0aCBvdGhlciBkYXRhc2V0czoKYGBge3J9CmNvbW11bml0eV9zYXRpc2ZhY3Rpb24kTG9jYWxHb3ZBcmVhIDwtICBzdHJfcmVtb3ZlKGNvbW11bml0eV9zYXRpc2ZhY3Rpb24kTG9jYWxHb3ZBcmVhLCAiIFNoaXJlIikKY29tbXVuaXR5X3NhdGlzZmFjdGlvbiRMb2NhbEdvdkFyZWEgPC0gIHN0cl9yZW1vdmUoY29tbXVuaXR5X3NhdGlzZmFjdGlvbiRMb2NhbEdvdkFyZWEsICIgQ2l0eSIpCmNvbW11bml0eV9zYXRpc2ZhY3Rpb24kTG9jYWxHb3ZBcmVhIDwtICBzdHJfcmVtb3ZlKGNvbW11bml0eV9zYXRpc2ZhY3Rpb24kTG9jYWxHb3ZBcmVhLCAiQm9yb3VnaCBvZiAiKQpjb21tdW5pdHlfc2F0aXNmYWN0aW9uJExvY2FsR292QXJlYSA8LSAgc3RyX3JlbW92ZShjb21tdW5pdHlfc2F0aXNmYWN0aW9uJExvY2FsR292QXJlYSwgIiBSdXJhbCIpCmBgYAoKIyMJU2NhbiBJIDogTWlzc2luZyB2YWx1ZXMKSW4gb3VyIGNhc2UsIGl0IGlzIGVhc2llciB0byB3b3JrIHdpdGggbWlzc2luZyBhbmQgc3BlY2lhbCB2YWx1ZXMgYW5kIG91dGxpZXJzIHdoaWxlIGRhdGEgaXMgaW4gd2lkZSBmb3JtYXQsIHNvIGJlZm9yZSBnb2luZyBpbnRvIGZ1cnRoZXIgdHJhbnNmb3JtYXRpb24sIGxldCdzIGRlYWwgd2l0aCBOQXMgZmlyc3QuCgpgYGB7cn0KI2NyZWF0ZSBmdW5jdGlvbiBmb3IgTkFzIGFuZCBzcGVjaWFsIHZhbHVlcwppcy5zcGVjaWFsb3JOQSA8LSBmdW5jdGlvbih4KXsKaWYgKGlzLm51bWVyaWMoeCkpIChpcy5pbmZpbml0ZSh4KSB8IGlzLm5hbih4KSB8IGlzLm5hKHgpKQp9CmBgYAoKCiMjIyBEYXRhc2V0IDE6IExpZmUgc2F0aXNmYWN0aW9uIGFuZCB3b3J0aHdoaWxlCkNoZWNrIGlmIGRhdGFzZXQgMWEgYW5kIDFiIGhhcyBOQXMKYGBge3J9CnN1bShpcy5uYShsaWZlX3NhdGlzZmFjdGlvbikpCnN1bShpcy5uYShsaWZlX3dvcnRod2hpbGUpKQpzdW0oaXMuaW5maW5pdGUobGlmZV9zYXRpc2ZhY3Rpb24kTGlmZVNhdGlzZmFjdGlvbikpCnN1bShpcy5pbmZpbml0ZShsaWZlX3dvcnRod2hpbGUkTGlmZVdvcnRod2hpbGUpKQpzdW0oaXMubmFuKGxpZmVfc2F0aXNmYWN0aW9uJExpZmVTYXRpc2ZhY3Rpb24pKQpzdW0oaXMubmFuKGxpZmVfd29ydGh3aGlsZSRMaWZlV29ydGh3aGlsZSkpCmBgYApOb25lIGZvdW5kLgoKIyMjIERhdGFzZXQgMjogTWVsYm91cm5lIEhvdXNpbmcgTWFya2V0CkNoZWNrIGlmIGRhdGFzZXQgMiBoYXMgTkFzCmBgYHtyfQpjb2xTdW1zKGlzLm5hKG1lbGJvdXJuZV9ob3VzZV9wcmljZXMpKQpgYGAKV2Ugc2VlIHRoYXQgcHJpY2VzIGluIG1lbGJvdXJuZV9ob3VzZV9wcmljZXMgZGF0YXNldCBoYXMgbWFueSBtaXNzaW5nIHZhbHVlcywgMTQ1OTAuIEl0IGlzIHJlYXNvbmFibGUgdG8gZmluZCB0aGUgYXZlcmFnZSBwcmljZSBvZiBob3VzZXMgdGhhdCBoYXMgdGhlIHNhbWUgcm9vbXMgbnVtYnJlLCBzYW1lIHR5cGUgYW5kIHNhbWUgbG9jYWwgZ292ZXJubWVudCBhcmVhLCBhbmQgdGhlbiB1c2UgaXQgdG8gZmlsbCB0aG9zZSBOQXMuIFRoZXJlZm9yZSwgd2UgZmlsbCB0aGVtIHdpdGggdGhlIGF2ZXJhZ2UgcHJpY2VzIG9mIGhvdXNlcy91bml0cy90b3duaG91c2VzIGluIHNhbWUgbG9jYWwgZ292ZXJtZW50IGFyZWEgdGFraW5nIGludG8gYWNjb3VudCB0eXBlIGFuZCBudW1iZXIgb2Ygcm9vbXMuCmBgYHtyfQojIEdyb3VwIHRoZSBkYXRhc2V0IGFjY29yaW5kIHRvIGxvY2FsR292QXJlYSwgdHlwZSBhbmQgcm9vbXMgdGhlbiBtdXRhdGUgcHJpY2Ugb2YgTkFzIHRvCiMgaGF2ZSBtZWFuIHZhbHVlIG9mIHRoZWlyIGNvcnJlc3BvbmRpbmcgZ3JvdXAKbWVsYm91cm5lX2hvdXNlX3ByaWNlcyA8LQogIG1lbGJvdXJuZV9ob3VzZV9wcmljZXMgJT4lCiAgZ3JvdXBfYnkoTG9jYWxHb3ZBcmVhLFR5cGUsUm9vbXMpICU+JSAKICBtdXRhdGUoUHJpY2UgPSBpZmVsc2UoaXMubmEoUHJpY2UpLCBtZWFuKFByaWNlLCBuYS5ybSA9IFQpLCBQcmljZSkpCmBgYAoKVGhlcmUgd2lsbCBwcm9iYWJseSBiZSBzb21lIE5BcyBsZWZ0LCBiZWNhdXNlIHRoZWlyIGdyb3VwIGNvbWJpbmF0aW9uIGRvZXNuJ3QgZXhpc3QgdGh1cyBjYW4ndCBoYXZlIGEgbWVhbi4KTGV0J3MgY2hlY2sgaG93IG1hbnkgTkFzIGxlZnQ6CmBgYHtyfQpzdW0oaXMubmEobWVsYm91cm5lX2hvdXNlX3ByaWNlcyRQcmljZSkpCmBgYAo0MiBpcyBmYXIgbGVzcyB0aGFuIDE0NTkwLgoKV2UgY2FuIG5vdyBzYWZlbHkgb21pdCB0aGVzZSA0MiBvYnNlcnZhdGlvbnMuCmBgYHtyfQptZWxib3VybmVfaG91c2VfcHJpY2VzIDwtIG5hLm9taXQobWVsYm91cm5lX2hvdXNlX3ByaWNlcykKc3VtKGlzLm5hKG1lbGJvdXJuZV9ob3VzZV9wcmljZXMpKQpgYGAKCiMjIyBEYXRhc2V0IDM6IEtub3cgWW91IENvdW5jaWwKQ2hlY2sgZm9yIE5BczoKYGBge3J9CmNvbFN1bXMoaXMubmEobWVsYm91cm5lX2hvdXNlX3ByaWNlcykpCmBgYApOb25lIGZvdW5kLgoKIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIAoKIyMjIERhdGFzZXQgMTogTGlmZSBzYXRpc2ZhY3Rpb24gYW5kIHdvcnRod2hpbGUKV29ya2luZyBpbiBsaWZlIHN0YXRpc2ZhY3Rpb24gYW5kIHdvcnRod2hpbGUgZGF0YXNldCwgd2Ugd2lsbCBtdXRhdGUgZGF0YXNldCBtYWxlL2ZlbWFsZSBjb2x1bW5zIHRvIGdpdmUgYXZlcmFnZSBvZiBib3RoCmBgYHtyfQpsaWZlX3NhdGlzZmFjdGlvbiA8LSBsaWZlX3NhdGlzZmFjdGlvbiAlPiUKICBtdXRhdGUoTG93QW5kTWVkaXVtID0oYExvd0FuZE1lZGl1bSBNYWxlYCtgTG93QW5kTWVkaXVtIEZlbWFsZWApLzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSGlnaCA9IChgSGlnaCBNYWxlYCtgSGlnaCBGZW1hbGVgKSAvIDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVmVyeUhpZ2ggPSAoYFZlcnlIaWdoIE1hbGVgK2BWZXJ5SGlnaCBGZW1hbGVgKSAvMikKCmxpZmVfd29ydGh3aGlsZSA8LSBsaWZlX3dvcnRod2hpbGUgJT4lCiAgbXV0YXRlKExvd0FuZE1lZGl1bSA9IChgTG93QW5kTWVkaXVtIE1hbGVgK2BMb3dBbmRNZWRpdW0gRmVtYWxlYCkvMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBIaWdoID0gKGBIaWdoIE1hbGVgK2BIaWdoIEZlbWFsZWApIC8gMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWZXJ5SGlnaCA9IChgVmVyeUhpZ2ggTWFsZWArYFZlcnlIaWdoIEZlbWFsZWApIC8yKQpgYGAKCkxldCB1cyBhbHNvIGFsc28gY29uc2lkZXIgaGlnaCBzYXRpc3Rpc2ZhY3Rpb24gYW5kIHZlcnkgaGlnaHQgc2F0aXN0aXNmYWN0aW9uIHRvIGJlIGluIHNhbWUgY2F0ZWdvcnkKYGBge3J9CmxpZmVfc2F0aXNmYWN0aW9uIDwtIGxpZmVfc2F0aXNmYWN0aW9uICU+JSBtdXRhdGUoSGlnaEFuZFZlcnlIaWdoID0gSGlnaCArIFZlcnlIaWdoKQpsaWZlX3dvcnRod2hpbGUgPC0gbGlmZV93b3J0aHdoaWxlICU+JSBtdXRhdGUoSGlnaEFuZFZlcnlIaWdoID0gSGlnaCArIFZlcnlIaWdoKQpgYGAKClRoZSBzdW0gb2YgTG93QW5kTWVkaXVtIGFuZCBIaWdoQW5kVmVyeUhpZ2ggaXMgYWx3YXlzIDEwMCwgdGh1cyB3ZSBnZXQgcmlkIG9mIG9uZSwgYW5kIHNpbmNlIHdlIGFyZSBtb3JlIGludGVyZXN0ZWQgaW4gdGhlIGhpZ2ggc2F0aXNmYWN0aW9uIHJhdGUgd2lsbCB3aWxsIG9ubHkgdXNlIEhpZ2hBbmRWZXJ5SGlnaCBmcm9tIGJvdGggZGF0YXNldHMuCmBgYHtyfQpsaWZlX3NhdGlzZmFjdGlvbiA8LSBsaWZlX3NhdGlzZmFjdGlvbiAlPiUgc2VsZWN0KExvY2FsR292QXJlYSwgSGlnaEFuZFZlcnlIaWdoKQpsaWZlX3dvcnRod2hpbGUgPC0gbGlmZV93b3J0aHdoaWxlICU+JSBzZWxlY3QoTG9jYWxHb3ZBcmVhLCBIaWdoQW5kVmVyeUhpZ2gpCiMgUmVuYW1lIGNvbHVtbnMKY29sbmFtZXMobGlmZV9zYXRpc2ZhY3Rpb24pWzJdIDwtICJMaWZlU2F0aXNmYWN0aW9uIgpjb2xuYW1lcyhsaWZlX3dvcnRod2hpbGUpWzJdIDwtICJMaWZlV29ydGh3aGlsZSIKYGBgCgpOb3cgd2UgY2FuIG1lcmdlIHRoZSB0d28gZGF0YXNldCBpbnRvIG9uZSBkYXRhc2V0IHRoYXQgZ2l2ZXMgbGlmZSBzYXRpc2ZhY3Rpb24gYW5kIGxpZmUgd29ydGh3aGlsZToKYGBge3J9CmxpZmVfc2F0X3dvcnRoIDwtIGxpZmVfc2F0aXNmYWN0aW9uICU+JSBsZWZ0X2pvaW4obGlmZV93b3J0aHdoaWxlLCAiTG9jYWxHb3ZBcmVhIikKaGVhZChsaWZlX3NhdF93b3J0aCkKYGBgCgojIyMgRGF0YXNldCAyOiBNZWxib3VybmUgSG91c2luZyBNYXJrZXQKCldlIHJlbW92ZSB1bm5lY2Vzc29yeSBjb2x1bW5zOgpgYGB7cn0KbWVsYm91cm5lX2hvdXNlX3ByaWNlcyA8LSBtZWxib3VybmVfaG91c2VfcHJpY2VzICU+JSBzZWxlY3QoLWMoMSwyLDYsNyw4LDksMTAsMTEpKQpoZWFkKG1lbGJvdXJuZV9ob3VzZV9wcmljZXMpCmBgYAoKIyMgU2NhbiBJSSBhbmQgVHJhbnNmb3JtOiBPdXRsaWVycwpTb21lIGhvdXNlcyBtaWdodCBiZSB2ZXJ5IG9sZCwgdGh1cyB2ZXJ5IGNoZWFwIHdoZW4gY29tcGFyZWQgdG8gdGhlIGF2ZXJhZ2UuIE9uIHRoZSBvdGhlciBoYW5kLCBzb21lIGhvdXNlcyBtaWdodCBiZSBsaWtlIG1hbnNpb24gb3IgbWFkZSBvdXQgb2YgdmVyeSBnb29kIG1hdGVyaWFscywgdGh1cyB2ZXJ5IGV4cGVuc2l2ZS4gV2UgY2FuIGNvbnNpZGVyIHRoZXNlIGFzIG91dGxpZXJzIGJlY2F1c2Ugb2Ygc2FtcGxpbmcgZXJyb3IgYW5kIHJlbW92ZSB0aGVtLiBCdXQgd2UgbmVlZCBwcm9jZWVkIHdpdGggY2F1dGlvdXMgaGVyZS4gRnVydGhlcm1vcmUsIHNpbmNlIGRhdGEgc3RpbGwgaW4gd2lkZSBmb3JtYXQgaXQgZWFzaWVyIHRvIHByb2NlZWQgd2l0aCBvdXRsaWVyIHNjYW5uaW5nLgoKTGV0J3MgZmlyc3QgdGFrZSBhIHN1Ymdyb3VwIG9mIHRoaXMgZGF0YXNldCB3aGljaCBhcmUgaG91c2VzIHdpdGggMy01IHJvb21zIHNvIHRoYXQgdGhlcmUgaXMgbm8gaHVnZSBkaWZmZXJlbnQgaW4gdGhlaXIgcHJpY2VzLgpgYGB7cn0KaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMgPC0gbWVsYm91cm5lX2hvdXNlX3ByaWNlcyAlPiUKICBmaWx0ZXIoKFJvb21zID09IDMpIHwgKFJvb21zID09IDQpIHwgKFJvb21zID09IDUpKQpkaW0oaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMpCmBgYApXZSBhcmUgbGVmdCB3aXRoIDQ3MTIzIG9ic2VydmF0aW9uIHdoaWNoIGlzIGFib3V0IDc1JSBvZiBvcmlnaW5hbCBkYXRhLgoKTGV0J3MgdGFrZSBhIHN1YnNldCBhbmQgcGxvdCBhIGJveCBwbG90IGZvciBhIDUgbG9jYWwgZ292ZXJtZW50IGFyZWFzOgpgYGB7cn0KdG1wSG91c2VzIDwtIGhvdXNlX3ByaWNlc18zdG81X3Jvb21zICU+JQogIGZpbHRlcigoTG9jYWxHb3ZBcmVhID09ICJZYXJyYSIpIHwgKExvY2FsR292QXJlYSA9PSAiTWVsYm91cm5lIikgfAogICAgICAgICAgIChMb2NhbEdvdkFyZWEgPT0gIkh1bWUiKSB8IChMb2NhbEdvdkFyZWEgPT0gIk1vbmFzaCIpIHwKICAgICAgICAgICAoTG9jYWxHb3ZBcmVhID09ICJHbGVuIEVpcmEiKSkKb3V0bGllcnMgPSBib3hwbG90KHRtcEhvdXNlcyRQcmljZSB+IGRyb3BsZXZlbHModG1wSG91c2VzKSRMb2NhbEdvdkFyZWEsCiAgICAgICAgICAgICAgICAgICBtYWluPSJIb3VzZXMgcHJpY2VzIGJ5IGxvY2FsIGdvdmVybWVudCIsIHlsYWIgPSAiUHJpY2UiLAogICAgICAgICAgICAgICAgICAgeGxhYiA9ICJMb2NhbCBHb3Zlcm1lbnQiLCBwbG90ID0gVFJVRSkKYGBgCmBgYHtyfQpwYXN0ZSgnVGhlcmUgYXJlICcsIGxlbmd0aChvdXRsaWVycyRvdXQpLCAnIG91dGxpZXJzIGluIHRoaXMgc3Vic2V0JykKYGBgCgpUaGVyZSBhcmUgbWFueSBvdXRsaWVycyBhdCB0aGUgdG9wIHNpZGUgd2hpY2ggaW5kaWNhdGUgc2tld25lc3MsIHJlbW92aW5nIHRoZXNlIG91dGxpZXJzIHdpbGwgbW9zdCBwcm9iYWJseSByZW1vdmUgdGhlIHNrZXduZXNzIGJ1dCB0aGVyZSBpcyBhIHZhbGlkIHJlYXNvbiB3aHkgd2UgaGF2ZSB0aGlzIGJlaGF2aW9yIHNpbmNlIGl0IGFwcGxpZXMgdG8gYWxsIGxvY2FsIGdvdmVybWVudCBhcmVhcy4gU28sIGluIHRoaXMgY2FzZSB3ZSBiZXR0ZXIgdHJhbnNmb3JtIG91ciBkYXRhIGZpcnN0IHRvIHJlbW92ZSBza2V3bmVzcyBhbmQgdGhlbiBjaGVjayBmb3Igb3V0bGllcnMuCgpJZiB3ZSB0YWtlIFlhcnJhIGFyZWEgYXMgYW4gZXhhbXBsZSBhbmQgcGxvdCBhIGhpc3RvZ3JhbToKYGBge3J9CnRtcEhvdXNlcyA8LSBob3VzZV9wcmljZXNfM3RvNV9yb29tcyAlPiUgZmlsdGVyKChMb2NhbEdvdkFyZWEgPT0gIllhcnJhIikpCmhpc3QodG1wSG91c2VzJFByaWNlLCBicmVha3MgPSAyMCwgbWFpbj0iWWFycmEncyBob3VzZXMnIHByaWNlcyIsIHlsYWIgPSAiUHJpY2UiLCB4bGFiID0gIlByaWNlIikKYGBgCkxldCdzIHVzZSBMb2cgdHJhbnNmb3JtYXRpb24gdG8gdHJ5IGFuZCBlbGltbmF0ZSBza2V3bmVzcwpgYGB7cn0KaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMgPC0gaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMgJT4lIG11dGF0ZShsb2dQcmljZSA9IGxvZyhQcmljZSkpCmBgYApDaGVjayB0aGUgc2tld25lc3MgaW4gWWFycmEgbm93OgpgYGB7cn0KdG1wSG91c2VzIDwtIGhvdXNlX3ByaWNlc18zdG81X3Jvb21zICU+JSBmaWx0ZXIoKExvY2FsR292QXJlYSA9PSAiWWFycmEiKSkKaGlzdCh0bXBIb3VzZXMkbG9nUHJpY2UsIGJyZWFrcyA9IDIwLCBtYWluPSJZYXJyYSdzIGhvdXNlcycgcHJpY2VzIiwgeWxhYiA9ICJQcmljZSIsCiAgICAgeGxhYiA9ICJMb2coUHJpY2UpIikKYGBgCk5vdyBsZXQncyBnbyBiYWNrIHRvIG91ciBzdWJzZXQgb2YgbG9jYWwgZ292ZXJtZW50cyBhbmQgY2hlY2sgdGhlIG91dGxpZXJzOgpgYGB7cn0KdG1wSG91c2VzIDwtIGhvdXNlX3ByaWNlc18zdG81X3Jvb21zICU+JQogIGZpbHRlcigoTG9jYWxHb3ZBcmVhID09ICJZYXJyYSIpIHwgKExvY2FsR292QXJlYSA9PSAiTWVsYm91cm5lIikgfAogICAgICAgICAgIChMb2NhbEdvdkFyZWEgPT0gIkh1bWUiKSB8IChMb2NhbEdvdkFyZWEgPT0gIk1vbmFzaCIpIHwgKExvY2FsR292QXJlYSA9PSAiR2xlbiBFaXJhIikpCm91dGxpZXJzIDwtIGJveHBsb3QodG1wSG91c2VzJGxvZ1ByaWNlIH4gZHJvcGxldmVscyh0bXBIb3VzZXMpJExvY2FsR292QXJlYSwKICAgICAgICAgICAgICAgICAgICBtYWluPSJIb3VzZXMgcHJpY2VzIGJ5IGxvY2FsIGdvdmVybWVudCIsIHlsYWIgPSAibG9nUHJpY2UiLAogICAgICAgICAgICAgICAgICAgIHhsYWIgPSAiTG9jYWwgR292ZXJtZW50IiwgcGxvdCA9IFRSVUUpCmBgYApgYGB7cn0KcGFzdGUoJ1RoZXJlIGFyZSAnLCBsZW5ndGgob3V0bGllcnMkb3V0KSwgJyBpbiB0aGlzIHN1YnNldCcpCmBgYApUaGVyZSBhcmUgbm93IGZld2VyIG91dGxpZXJzIGJlY2F1c2Ugb2YgdGhlIHRyYW5zZm9ybWF0aW9uLCB3aGljaCBtZWFucyB0aGF0IHdlIGFyZSBtYWtpbmcgdXNlIG9mIG1vcmUgZGF0YS4KCk5vdyBsZXRzIG9taXQgb3V0bGllcnMgZnJvbSB0aGUgd2hvbGUgZGF0YXNldCB1c2luZyBib3hwbG90IHRvIGZpbmQgYWxsIG91dGxpZXJzIHBlciBncm91cCBhcmVhLgpgYGB7cn0KIyBVc2UgYm94cGxvdCB0byBzdG9yZSBhbGwgb3V0bGllcnMKYWxsX291dGxpZXJzIDwtIGJveHBsb3QoaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMkbG9nUHJpY2UgfiBob3VzZV9wcmljZXNfM3RvNV9yb29tcyRMb2NhbEdvdkFyZWEsCiAgICAgICAgICAgICAgICAgICAgICAgIHBsb3QgPSBGQUxTRSkKcGFzdGUoJ092ZXJhbGwgdGhlcmUgYXJlICcsIGxlbmd0aChhbGxfb3V0bGllcnMkb3V0KSwgJyBvdXRsaWVycy4nKQpgYGAKTm93IHVzZSB0aGUgb3V0bGllcnMgb2JqZWN0IHRvIGdvIHRocm91Z2ggYWxsIHRoZSBkYXRhIGFuZCBvbWl0IG91dGxpZXJzIHRoYXQgd2VyZSBkZWZpbmVkIGJ5IHRoZSBib3hwbG90LgpgYGB7cn0KZm9yKGkgaW4gc2VxKDEsIGxlbmd0aChhbGxfb3V0bGllcnMkb3V0KSkpCiAgaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMgPC0KICBob3VzZV9wcmljZXNfM3RvNV9yb29tc1shKChob3VzZV9wcmljZXNfM3RvNV9yb29tcyRMb2NhbEdvdkFyZWE9PWFsbF9vdXRsaWVycyRuYW1lc1thbGxfb3V0bGllcnMkZ3JvdXBbaV1dKSAmIChob3VzZV9wcmljZXNfM3RvNV9yb29tcyRsb2dQcmljZT09YWxsX291dGxpZXJzJG91dFtpXSkpLF0KYGBgCgpUaGUgbmV3IGJveCBwbG90IGZvciB0aGUgc3Vic2V0CmBgYHtyfQp0bXBIb3VzZXMgPC0gaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMgJT4lCiAgZmlsdGVyKChMb2NhbEdvdkFyZWEgPT0gIllhcnJhIikgfCAoTG9jYWxHb3ZBcmVhID09ICJNZWxib3VybmUiKSB8IChMb2NhbEdvdkFyZWEgPT0gIkh1bWUiKSB8CiAgICAgICAgICAgKExvY2FsR292QXJlYSA9PSAiTW9uYXNoIikgfCAoTG9jYWxHb3ZBcmVhID09ICJHbGVuIEVpcmEiKSkKb3V0bGllcnMgPC0gYm94cGxvdCh0bXBIb3VzZXMkbG9nUHJpY2UgfiBkcm9wbGV2ZWxzKHRtcEhvdXNlcykkTG9jYWxHb3ZBcmVhLAogICAgICAgICAgICAgICAgICAgIG1haW49IkhvdXNlcyBwcmljZXMgYnkgbG9jYWwgZ292ZXJtZW50IiwgeWxhYiA9ICJsb2dQcmljZSIsCiAgICAgICAgICAgICAgICAgICAgeGxhYiA9ICJMb2NhbCBHb3Zlcm1lbnQiLCBwbG90ID0gVFJVRSkKYGBgCkFzIHlvdSBjYW4gc2VlIGl0IGlzIGJldHRlciB0aGFuIHByZXZpb3VzIGRhdGFzZXQuIEluIHRoaXMgd2F5IHdlIGNhbiBoYXZlIGEgZ29vZCBhdmVyYWdlIHByaWNlIGZvciBlYWNoIGxvY2FsIGdvdmVybWVudC4KCgojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIElJIAoKIyMjIE1lcmdpbmcgaW5kaWNhdG9ycyBkYXRhc2V0cyBhbmQgY2hlY2tpbmcgdGlkaW5lc3MKTGlmZSBzYXRpc2ZhY3Rpb24gYW5kIHdvcnRod2hpbGUgZGF0YWZyYW1lcyBhbG9uZyB3aXRoIGNvbW11bml0eSBzYXRpc2ZhY3Rpb24gZGF0YWZyYW1lIGFyZSBib3RoIGdpdmluZyBpbmRpY2F0b3JzIGFib3V0IHdlbGxiZWluZyBvZiBwZW9wbGUgaW4gc3BlY2lmaWMgbG9jYWwgZ292ZXJtZW50IGFyZWEuIFdlIHdpbGwgam9pbiB0aGVzZSB0d28gZGF0YXNldHMgaW50byBvbmUuCmBgYHtyfQp3ZWxsYmVuaW5nX2luZGljYXRvcnMgPC0gbGlmZV9zYXRfd29ydGggJT4lIGxlZnRfam9pbihjb21tdW5pdHlfc2F0aXNmYWN0aW9uLCAiTG9jYWxHb3ZBcmVhIikKaGVhZCh3ZWxsYmVuaW5nX2luZGljYXRvcnMpCmBgYApDaGVjayBpZiBhbnl0aGluZyB3ZW50IHdyb25nIGJ5IGNoZWNraW5nIGZvciBOQXMKYGBge3J9CnN1bShpcy5uYSh3ZWxsYmVuaW5nX2luZGljYXRvcnMpKQpgYGAKQWxsIGdvb2QuCgpMaWZlIHN0YXRpc2ZhY3Rpb24gYW5kIHdvcnRod2hpbGUgZGF0YXNldCBpcyBub3QgdGlkeSBiZWNhdXNlIGl0IGhhcyB0d28gb2JzZXJ2YXRpb25zIHBlciByb3csIHdlIHdpbGwgdXNlIGdhdGhlciBmdW5jdGlvbiB0byB0cmFuc2Zvcm0gaXQgbG9uZyBmb3JtYXQuIEFsdGhvdWdoIGl0IG1pZ2h0IHRha2UgbW9yZSBzcGFjZSwgbG9uZyBmb3JtYXQgaXMgbW9yZSBmbGV4aWFibGUgZm9yIGFuYWx5c2lzIGFuZCBpdCBpcyBlYXN5IHRvIHNsaWNlIGRhdGEgYnkgYWRkaW5nIGEgY29uZGl0aW9uIHRvIGEgY29sdW1uLgpgYGB7cn0Kd2VsbGJlbmluZ19pbmRpY2F0b3JzIDwtIHdlbGxiZW5pbmdfaW5kaWNhdG9ycyAlPiUKICBnYXRoZXIoTGlmZVNhdGlzZmFjdGlvbiwgTGlmZVdvcnRod2hpbGUsIENvbW11bml0eVNhdGlzZmFjdGlvbiwga2V5ID0gIkluZGljYXRvciIsCiAgICAgICAgIHZhbHVlID0gIkluZGljYXRvclBlcmNlbnRhZ2UiKQpoZWFkKHdlbGxiZW5pbmdfaW5kaWNhdG9ycykKYGBgCgojIyMgIE1lcmdpbmcgTWVsYm91cm5lIGhvdXNlcyBwcmljZXMgZGF0YXNldCB3aXRoIHdlbGxiZWluZyBpbmRpY2F0b3JzCkZpcnN0IGxldCdzIGdyb3VwIG1lbGJvdW5lX2hvdXNlc19wcmljZXMgYnkgTG9jYWxHb3ZBcmVhIGFuZCBjYWxjdWxhdGUgbWVhbiBwcmljZSBhbmQgbWVhbiBkaXN0YW5jZQpgYGB7cn0KaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMgPC0gaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMgJT4lIGdyb3VwX2J5KExvY2FsR292QXJlYSkgJT4lCiAgc3VtbWFyaXNlKG1lYW5QcmljZSA9IG1lYW4oUHJpY2UpLCBtZWFuRGlzdGFuY2UgPSBtZWFuKERpc3RhbmNlKSkKaGVhZChob3VzZV9wcmljZXNfM3RvNV9yb29tcykKYGBgCldlIGNhbiBzZWUgdGhhdCB0aGUgYWJvdmUgZGF0YXNldCBpcyBpbiB3aWRlIGZvcm1hdC4gV2UgY29uc2lkZXIgaXQgdGlkeSBiZWNhdXNlIHRoaXMgaXMgaG93IHdlIHdhbnQgaXQgdG8gYmUgd2hlbiBtZXJnaW5nIHRoZSBkYXRhc2V0IHdpdGggd2VsbGJpbmcgaW5kaWNhdG9ycyBkYXRhc2V0LiAKCkFmdGVyIGdyb3VwaW5nLCBsb2NhbCBHb3Zlcm1lbnQgQXJlYXMgYXJlIG5vIGxvbmdlciBmYWN0b3JzLCBzbyB3ZSBjaGFuZ2UgdGhlaXIgdHlwZSB0byBjaGFyOgpgYGB7cn0KaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMkTG9jYWxHb3ZBcmVhIDwtIGFzLmNoYXJhY3Rlcihob3VzZV9wcmljZXNfM3RvNV9yb29tcyRMb2NhbEdvdkFyZWEpCmBgYAoKUmVuYW1lIHNvbWUgb2YgdGhlIExvY2FsR292QXJlYSBzbyB0aGV5IHNhdGlzZnkgdGhlIG5hbWluZyBpbiB0aGUgb3RoZXIgZGF0YXNldApgYGB7cn0KaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMkTG9jYWxHb3ZBcmVhW2hvdXNlX3ByaWNlc18zdG81X3Jvb21zJExvY2FsR292QXJlYSA9PQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2FyZGluaWEgU2hpcmUgQ291bmNpbCJdIDwtICJDYXJkaW5pYSIKaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMkTG9jYWxHb3ZBcmVhW2hvdXNlX3ByaWNlc18zdG81X3Jvb21zJExvY2FsR292QXJlYSA9PQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWFjZWRvbiBSYW5nZXMgU2hpcmUgQ291bmNpbCJdIDwtICJNYWNlZG9uIFJhbmdlcyIKaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMkTG9jYWxHb3ZBcmVhW2hvdXNlX3ByaWNlc18zdG81X3Jvb21zJExvY2FsR292QXJlYSA9PQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWl0Y2hlbGwgU2hpcmUgQ291bmNpbCJdIDwtICJNaXRjaGVsbCIKaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMkTG9jYWxHb3ZBcmVhW2hvdXNlX3ByaWNlc18zdG81X3Jvb21zJExvY2FsR292QXJlYSA9PQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTW9vcmFib29sIFNoaXJlIENvdW5jaWwiXSA8LSAiTW9vcmFib29sIgpob3VzZV9wcmljZXNfM3RvNV9yb29tcyRMb2NhbEdvdkFyZWFbaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMkTG9jYWxHb3ZBcmVhID09CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNdXJyaW5kaW5kaSBTaGlyZSBDb3VuY2lsIl0gPC0gIk11cnJpbmRpbmRpIgpob3VzZV9wcmljZXNfM3RvNV9yb29tcyRMb2NhbEdvdkFyZWFbaG91c2VfcHJpY2VzXzN0bzVfcm9vbXMkTG9jYWxHb3ZBcmVhID09CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOaWxsdW1iaWsgU2hpcmUgQ291bmNpbCJdIDwtICJOaWxsdW1iaWsiCmhvdXNlX3ByaWNlc18zdG81X3Jvb21zJExvY2FsR292QXJlYVtob3VzZV9wcmljZXNfM3RvNV9yb29tcyRMb2NhbEdvdkFyZWEgPT0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIllhcnJhIFJhbmdlcyBTaGlyZSBDb3VuY2lsIl0gPC0gIllhcnJhIFJhbmdlcyIKYGBgCk5vdyBtZXJnZSBMb2NhbCBHb3Zlcm1lbnRzIEFyZWFzIGhvdXNlcyBpbmZvcm1hdGlvbiB3aXRoIHdlbGxiZWluZyBpbmRpY2F0b3JzLCBhbmQgc2luY2Ugd2Ugb25seSBoYXZlIDM0IGxvY2FsIGdvdmVybWVudCBpbiBob3VzZV9wcmljZXNfM3RvNV9yb29tcyBkYXRhc2V0IGFuZCA3OSBpbiB0aGUgb3RoZXIgZGF0YXNldCwgd2Ugd2lsbCAqbGVmdCogam9pbiBob3VzZV9wcmljZXNfM3RvNV9yb29tcyB3aXRoIHdlbGxiZW5pbmdfaW5kaWNhdG9ycyBzbyB0aGF0IGxvY2FsIGdvdmVybWVudCBhcmVhIHRoYXQgaGFzIG5vIGhvdXNlIGluZm9ybWF0aW9uIHdpbGwgYmUgYXV0b21hdGljYWxseSBkaXNjYXJkZWQuIApgYGB7cn0KZmluYWxfZGF0YXNldCA8LSBob3VzZV9wcmljZXNfM3RvNV9yb29tcyAlPiUgbGVmdF9qb2luKHdlbGxiZW5pbmdfaW5kaWNhdG9ycywiTG9jYWxHb3ZBcmVhIikKaGVhZChmaW5hbF9kYXRhc2V0KQpgYGAKQ2hlY2sgZm9yIE5BcwpgYGB7cn0Kc3VtKGlzLm5hKGZpbmFsX2RhdGFzZXQpKQpgYGAKTm9uZSBmb3VuZC4KCk5vdyBvdXIgZGF0YXNldCBpcyBjbGVhbiwgdGlkeSBhbmQgcmVhZHkgZm9yIGFuYWx5c2lzLgoKPGJyPgo8YnI+Cg==