Required packages

library(readxl)
library(tidyr)
library(dplyr)
library(lubridate)
library(outliers)
library(stringr)
library(MVN)
library(forecast)

Executive Summary

The purpose of the pre-processing performed in this assignment was to produce a dataset that could be used to explore whether there is a relationship between average maximum temperature, rainfall, windspeed and/or humidity, and population size and/or growth, for locations throughout Australia. The raw data was sourced from the Australian Bureau of Statistics (ABS) and Kaggle (via the Australian Bureau of Meteorology) and pre-processed using the following steps:

Data

The data in this assignment was sourced from the ABS and Kaggle. The ABS data was downloaded from the ABS website on 16/05/2020 using the following link: “https://www.abs.gov.au/AUSSTATS/subscriber.nsf/log?openagent&32180ds0003_2009-19.xls&3218.0&Data%20Cubes&381DE9FC52CAD374CA258535000B98C0&0&2018-19&25.03.2020&Latest”. The data was stored as an Excel 97-2003 workbook (.xls) file.

According to the ABS, the data contains estimates of resident populations according to Significant Urban Areas of Australia as of June 30 each year from 2009 to 2019 (Australian Bureau of Statistics, 2020). Significant Urban Areas are areas with a population of at least 10,000 based on Statistical Areas Level 2 (SA2s) and can represent a collection of Urban Centres whose collective population is at least 10,000 (Australian Bureau of Statistics, 2020). The ABS data contained the following variables:

The Kaggle data was downloaded from “https://www.kaggle.com/jsphyg/weather-dataset-rattle-package#weatherAUS.csv” and is stored as a Comma Separated Value (CSV) file. The Kaggle data was originally sourced from the Australian Bureau of Meteorology (BoM) website (“http://www.bom.gov.au/climate/data”) and was published to the Kaggle website as a single CSV file by the Kaggle user “Joe Young” (“https://www.kaggle.com/jsphyg”).

According to the Kaggle and BoM websites, the data contains daily weather observations from weather stations across Australia from 01/11/2017 to 25/06/2017 (Kaggle.com, 2019). Not all weather stations in the network observe all variables and, in some locations and where appropriate, observations from a number of stations have been combined to produce a summary for a single location (Bureau of Meteorology, 2020). The data contained the following variables:

setwd("C:/Users/James/Desktop/Post Grad studies/Data Wrangling - MATH2349/Assignment 2")
# Import the ABS population data as "abspop"
abspop <- read_excel("32180ds0003_2009-19.xls", sheet = "Table 1", skip = 6)
New names:
* `` -> ...1
* `` -> ...2
* `` -> ...14
* `` -> ...16
names(abspop)[names(abspop) == "...2"] <- "Sig. Urban Area" #Rename second column to "Sig. Urban Area"
names(abspop) #the columns with relevant data are 2-13
 [1] "...1"            "Sig. Urban Area" "2009"            "2010"            "2011"           
 [6] "2012"            "2013"            "2014"            "2015"            "2016"           
[11] "2017"            "2018"            "2019"            "...14"           "2018-2019"      
[16] "...16"          
tail(abspop,10) #There is a totals row and 6 other rows at the bottom of the data that are not needed
abspop <- abspop[3:(nrow(abspop)-7), 2:13] #select the rows and columns with relevant data in them
head(abspop)
# Import the Kaggle weather data as "weather"
weather <- read.csv("weatherAUS.csv", stringsAsFactors = FALSE)
#Select the columns relevant to fulfilling the goal of the pre-processing
desired_measures <- c("Date", "Location", "MaxTemp", "Rainfall", "WindSpeed9am", "WindSpeed3pm", "Humidity9am", "Humidity3pm") #To compare population growth against heat, rain, wind and humidity.
weather <- select(weather, all_of(desired_measures))
head(weather)
## Data will be merged after it has been tidied, manipulated and formatted

Understand

For the ABS population data:

For the Kaggle weather data:

The Year/Date data was converted to an ordered factor after the weather and population data were merged (refer ## Merging the data)

## Review and revise the variables in the population data
str(abspop)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   110 obs. of  12 variables:
 $ Sig. Urban Area: chr  "Not in any Significant Urban Area (NSW)" "Albury - Wodonga" "Armidale" "Ballina" ...
 $ 2009           : chr  "971734" "82307" "23166" "24126" ...
 $ 2010           : chr  "979752" "83245" "23349" "24309" ...
 $ 2011           : chr  "984419" "84195" "23471" "24469" ...
 $ 2012           : chr  "989905" "85229" "23631" "24622" ...
 $ 2013           : chr  "995524" "86564" "23756" "24852" ...
 $ 2014           : chr  "1001028" "87982" "23856" "25066" ...
 $ 2015           : chr  "1006487" "89306" "23979" "25325" ...
 $ 2016           : chr  "1013060" "90836" "24114" "25643" ...
 $ 2017           : chr  "1018827" "92251" "24369" "25930" ...
 $ 2018           : chr  "1025509" "93541" "24482" "26358" ...
 $ 2019           : chr  "1031562" "94837" "24584" "26625" ...
abspop[ ,2:12]<- sapply(abspop[ ,2:12], as.integer) #Convert Year columns to integers
sapply(abspop, class)
Sig. Urban Area            2009            2010            2011            2012            2013 
    "character"       "integer"       "integer"       "integer"       "integer"       "integer" 
           2014            2015            2016            2017            2018            2019 
      "integer"       "integer"       "integer"       "integer"       "integer"       "integer" 
## Review and revise the variables in the weather data
str(weather)
'data.frame':   142193 obs. of  8 variables:
 $ Date        : chr  "2008-12-01" "2008-12-02" "2008-12-03" "2008-12-04" ...
 $ Location    : chr  "Albury" "Albury" "Albury" "Albury" ...
 $ MaxTemp     : num  22.9 25.1 25.7 28 32.3 29.7 25 26.7 31.9 30.1 ...
 $ Rainfall    : num  0.6 0 0 0 1 0.2 0 0 0 1.4 ...
 $ WindSpeed9am: int  20 4 19 11 7 19 20 6 7 15 ...
 $ WindSpeed3pm: int  24 22 26 9 20 24 24 17 28 11 ...
 $ Humidity9am : int  71 44 38 45 82 55 49 48 42 58 ...
 $ Humidity3pm : int  22 25 30 16 33 23 19 19 9 27 ...
weather$Date <- ymd(weather$Date) #lubridate package - convert "Date" to a date type using "ymd()"
sapply(weather, class)
        Date     Location      MaxTemp     Rainfall WindSpeed9am WindSpeed3pm  Humidity9am 
      "Date"  "character"    "numeric"    "numeric"    "integer"    "integer"    "integer" 
 Humidity3pm 
   "integer" 

Tidy & Manipulate Data I

The ABS population data did not conform to the tidy data principles because each observation (estimated population in a given Sig. Urban Area (SUA) in a given year) did not have its own row. Instead, observations were spread across rows and columns, meaning the data was in a wide format. In order to make the ABS population data tidy, the year columns had to be rearranged so they were in rows, and the estimated populations (the values) were in their own column. The ABS population data was tidied using the “gather()” function, which took the year columns and rearranged them to be rows while creating a new column (called “Population”) to house the value of each observation. The Population column was divided by 1,000 to adjust the scale and improve readability. The column was also renamed to “Population(000s)” to ensure its values would be interpreted correctly. Finally, a new column was created to measure the year-on-year growth in population for each SUA, using a technique adapted from the rstudio community website (Community.rstudio.com, 2019). For the first observation in each SUA (2009) the growth was set to a missing value, as setting it to zero could result in inaccurate conclusions being drawn. The missing values were imputed later (refer ## Scan I).

#Tidy the population data
abspop <- abspop %>% gather(Year, Population, -1) %>% select(c(2,1,3))
abspop$Year <- as.integer(abspop$Year)
head(abspop, 3)
#Transform the Population variable to make it easier to read.
abspop <- abspop %>% mutate(Population = Population/1000) %>% rename(`Population(000s)` = Population)
#Add a year-on-year growth column to the population data
abspop <- abspop %>% arrange(`Sig. Urban Area`, Year) %>% 
  mutate(PopGrowthPct = 
  case_when(Year == 2009 ~ as.double(NA),
            Year != 2009 ~ (`Population(000s)`-lag(`Population(000s)`))/lag(`Population(000s)`)*100))

Scan I

The datasets were scanned for missing values and obvious errors before the datasets were manipulated or merged as this made it easier to identify explanations for missing/erroneous values. For example, it would be easier to identify that a missing value in the weather data was due to a weather station being unable to track that measurement before the data is manipulated or merged, because location or time-specific context could be lost when the data is manipulated or merged.

Because variables like temperature, humidity and rainfall can vary from location-to-location, using the mean/median of all locations to impute missing values for a single location could result in unrealistic values being imputed. Therefore, when a missing value was identified, the mean/median for the relevant measure was calculated for that particular location and used to impute the missing value.

The population data was scanned and 110 missing values were identified, being the NAs that were added when the population growth variable was created. Because the distribution of the variable was skewed, the median was used to impute the missing values on a location-by-location basis. There were no special values (“NaN” or infinities) in the ABS population data.

The weather data was scanned and 11,090 missing values were identified, with all missing values being in numeric variables. Before imputing missing values, the variables with normal distributions were identified and their missing values were imputed using means and a technique adapted from stackoverflow (Stackoverflow.com, 2019). The remaining missing values were imputed using medians.

Once the missing values in the weather data had been imputed, no more missing values were present in the data. There were also no special values (“NaN” or infinites).

#Scan the population data for missing values 
sum(is.na(abspop)) #110 NAs in the population data
[1] 110
sum(apply(abspop, 1, is.nan)) #no NAN values
[1] 0
sum(apply(abspop, 1, is.infinite)) #no infinite values
[1] 0
#Impute the NAs in population growth
boxplot(abspop$PopGrowthPct, main = "Distribution of Population Growth (%)") #not normally distributed
#use median to impute missing values in PopGrowthPct as it is not normally distributed
abspop <- abspop %>% group_by(`Sig. Urban Area`) %>%
  mutate(PopGrowthPct = ifelse(is.na(PopGrowthPct), median(PopGrowthPct, na.rm = TRUE), PopGrowthPct))
#Scan the weather data for missing values
initial_weather_NAs <- sum(is.na(weather))
initial_weather_NAs #11,090 NAs in the weather data
[1] 11090
colSums(is.na(weather)) #all columns with missing values are numeric data types
        Date     Location      MaxTemp     Rainfall WindSpeed9am WindSpeed3pm  Humidity9am 
           0            0          322         1406         1348         2630         1774 
 Humidity3pm 
        3610 
##Impute missing values - use mean for normally-distributed variables and median for everything else
par(mfrow = c(2,3))

i <- 1
while (i <= ncol(weather)) { 
  if (is.numeric(weather[,i]) == TRUE)  boxplot(weather[,i], main = names(weather[i]))
  i <- i+1
} #MaxTemp and Humidity3pm appear normally distributed

which(abs(scores(weather$MaxTemp, type = "z")) >3) #0 outliers
integer(0)
which(abs(scores(weather$Humidity3pm, type = "z")) >3) #0 outliers
integer(0)
#MaxTemp and Humidity3pm appear normally distributed with no outliers. Impute them using mean
nrml_dist_vars <- c("MaxTemp", "Humidity3pm")
for (i in nrml_dist_vars) {
  weather[,i] <-  ave(weather[,i], weather$Location, FUN = function(x) 
    ifelse(is.na(x), mean(x,na.rm = TRUE), x))
}
initial_weather_NAs-sum(is.na(weather)) #3,932 fewer NAs
[1] 3932
#For the variables with skewed distributions, impute missing values using median.
weather <- weather %>% group_by(Location) %>%
  mutate(Rainfall = ifelse(is.na(Rainfall), median(Rainfall, na.rm = TRUE), Rainfall))
weather <- weather %>% group_by(Location) %>%
  mutate(WindSpeed9am = ifelse(is.na(WindSpeed9am), median(WindSpeed9am, na.rm = TRUE), WindSpeed9am))
weather <- weather %>% group_by(Location) %>%
  mutate(WindSpeed3pm = ifelse(is.na(WindSpeed3pm), median(WindSpeed3pm, na.rm = TRUE), WindSpeed3pm))
weather <- weather %>% group_by(Location) %>%
  mutate(Humidity9am = ifelse(is.na(Humidity9am), median(Humidity9am, na.rm = TRUE), Humidity9am))
sum(is.na(weather)) #no more missing values in the weather data
[1] 0
sum(apply(weather, 1, is.nan)) #no NaN values in the weather data
[1] 0
sum(apply(weather, 1, is.infinite)) #no infinite values in the weather data
[1] 0

Tidy & Manipulate Data II

While the weather data had daily observations, the ABS population data had yearly observations (population estimates). Therefore, the weather data had to be aggregated to align with the frequency of the ABS population data. The “mutate()” function and “year” argument were used to create a Year column in the weather data, based on the year in the Date column. Once the Year field had been created, mutate was again used to create columns for maximum humidity and wind speed, so that relationships between humidity or windiness and population size/growth could be explored. Finally, in order to align with the population data, the weather data was grouped by year and location and daily measurements were averaged to produce average daily figures for each year and location.

The ABS population data and weather data were then compared to identify any locations that required manipulation in order to match. Although 143 unique names were identified, only 16 names matched. An exploration of the names that did not match revealed that some Sig. Urban Areas had hyphens, which prevented them from matching to very similar Locations that did not (e.g. “Albury” and “Albury - Wodonga”). To resolve this, “gsub()” was used to remove hyphens and everything after them from the names of Sig. Urban Areas using a technique adapted from the “ecology & stats” blog by Steve Walker (Walker, S. C., 2013). This increased the number of matches to 20, an increase of 4 matches. Further examination revealed that the names of some Locations were actually two words without a space separating them (e.g. “CoffsHarbour”). To improve the number of matching locations, spaces within Sig. Urban Area names were removed using “gsub()”, leading to a total of 25 matches.

By manipulating the names of Sig. Urban Areas, the number of matching location names achieved between the two datasets was 25, representing 23% of Sig. Urban Areas and 51% of weather station Locations.

#Group the weather data by year to create a "Year" column using the "Date"" column
weather <- weather %>% mutate(Year = year(Date))
weather$Year <- as.integer(weather$Year)
#Create daily maximums for humidity and windspeed
weather <- weather %>% mutate(MaxHumidity = pmax(Humidity9am, Humidity3pm, na.rm = TRUE),
                              MaxWindspeed = pmax(WindSpeed9am, WindSpeed3pm, na.rm = TRUE))
#Group by year and create average daily values for rain, temperature, humidity and windspeed
avgweather <- weather %>% group_by(Year, Location) %>% 
  summarise("Avg Daily Rainfall" = mean(Rainfall, na.rm = TRUE),
            "Avg MaxTemp" = mean(MaxTemp, na.rm = TRUE),
            "Avg Max Humidity" = mean(MaxHumidity, na.rm = TRUE),
            "Avg Max Windspeed" = mean(MaxWindspeed, na.rm = TRUE))
##Preparation for merging
unique_places <- union(avgweather$Location, abspop$`Sig. Urban Area`)
length(unique_places) #143
[1] 143
matches <- intersect(avgweather$Location, abspop$`Sig. Urban Area`)
length(matches) #16
[1] 16
#Matching on Location and Sig. Urban Area produces 16 matches, could any be altered in order to match?
mismatches <- setdiff(unique_places, matches)
head(sort(mismatches),30) #Hyphenated names and names with spaces appear to be a mismatch issue.
 [1] "Albury"                "Albury - Wodonga"      "Alice Springs"        
 [4] "AliceSprings"          "Armidale"              "Bacchus Marsh"        
 [7] "BadgerysCreek"         "Bairnsdale"            "Ballina"              
[10] "Batemans Bay"          "Bathurst"              "Bowral - Mittagong"   
[13] "Broken Hill"           "Broome"                "Bunbury"              
[16] "Bundaberg"             "Burnie - Wynyard"      "Busselton"            
[19] "Camden Haven"          "Canberra"              "Canberra - Queanbeyan"
[22] "Central Coast"         "Cobar"                 "Coffs Harbour"        
[25] "CoffsHarbour"          "Colac"                 "Dartmoor"             
[28] "Devonport"             "Dubbo"                 "Echuca - Moama"       
#Search for hypens in Location and Sig. Urban Area, are there hyphens in just one?
sum(str_detect(mismatches, pattern = "-"), na.rm = TRUE) #20 hyphens in mismatches
[1] 20
sum(str_detect(avgweather$Location, pattern = "-"), na.rm = TRUE) #no hyphens in "Location"
[1] 0
sum(str_detect(unique(abspop$`Sig. Urban Area`), pattern = "-")) #all hyphens are in `Sig. Urban Area`
[1] 20
#Does removing everything after a hyphen improve the number of matches?
abspop$`Sig. Urban Area` <- gsub(pattern = ' -.*', replacement = '', abspop$`Sig. Urban Area`)
sum(str_detect(abspop$`Sig. Urban Area`, pattern = "-"), na.rm = TRUE) #no more hyphens
[1] 0
matches2 <- intersect(avgweather$Location, abspop$`Sig. Urban Area`)
length(intersect(matches, matches2)) == length(matches) #None of the original matches have been lost
[1] TRUE
length(matches2) #now 20 matches, total matches have improved by 4
[1] 20
#Some Locations are all one word, does removing the spaces in `Sig. Urban Area` improve matching?
abspop$`Sig. Urban Area` <- gsub(pattern = ' ', replacement = '', abspop$`Sig. Urban Area`)
matches3 <- intersect(avgweather$Location, abspop$`Sig. Urban Area`)
length(intersect(matches, matches3)) == length(matches) #None of the original matches have been lost
[1] TRUE
length(matches3) #25 matches, total matches have improved by 5
[1] 25

Merging the Data

“inner_join()” was used to merge the ABS population data and the weather data, with Year and Location (Sig. Urban Area) used as the joining keys. Because the merged data did not require any further string manipulation, “Location” and “Year” were converted to factors so the effect of individual locations or years could be observed. Values in the Year column ranged from 2009 to 2017 and the levels were ordered from smallest to largest.

weatherpop <- inner_join(avgweather, abspop, by = c("Location" = "Sig. Urban Area", "Year"))
sapply(weatherpop, class) #Location is currently a character variable but it should be a factor
              Year           Location Avg Daily Rainfall        Avg MaxTemp   Avg Max Humidity 
         "integer"        "character"          "numeric"          "numeric"          "numeric" 
 Avg Max Windspeed   Population(000s)       PopGrowthPct 
         "numeric"          "numeric"          "numeric" 
weatherpop$Location <- as.factor(weatherpop$Location)
levels(weatherpop$Location)
 [1] "Adelaide"     "Albany"       "Albury"       "AliceSprings" "Ballarat"     "Bendigo"     
 [7] "Brisbane"     "Cairns"       "Canberra"     "CoffsHarbour" "Darwin"       "GoldCoast"   
[13] "Hobart"       "Launceston"   "Melbourne"    "Mildura"      "MountGambier" "Newcastle"   
[19] "Perth"        "Portland"     "Sale"         "Sydney"       "Townsville"   "WaggaWagga"  
[25] "Wollongong"  
#Turn Year into a factor so the effect of individual years can be calculated (if we wish to do so)
weatherpop$Year <- factor(weatherpop$Year, levels = c(2009:2017), ordered = TRUE)
levels(weatherpop$Year)
[1] "2009" "2010" "2011" "2012" "2013" "2014" "2015" "2016" "2017"
is.ordered(weatherpop$Year) #Year is now ordered, the smallest year is 2009
[1] TRUE

Scan II

Visual inspection of boxplots was used to scan the merged data for outliers. Univariate outliers were identified in the Rainfall, Humidity, Population and Population Growth variables. The outliers in these variables were examined to determine whether they were likely to be the result of an error, or valid observations that just happened to be outliers. The same variables were also scanned for bivariate outliers by grouping observations according to year and visually inspecting the resulting boxplots.

The “mvn()” function was used to identify multivariate outliers. While 100 multivariate outliers were detected, none were for observations later than 2012. The multivariate outliers between 2009 and 2012 may have been a result of the after effects of the “Millennium drought”, which ended with heavy rainfall in 2009 and 2010, (Heberger, 2012). That said, there is no mention of the drought’s impact on the weather data by the BoM. Given the data was published by the ABS and BoM (agencies who have a responsibility to, and reputation for, providing accurate and reliable data), it is unlikely that the outliers observed were the result of error (e.g. data entry errors, measurement errors etc.), and therefore, it was decided that the outliers should not be removed or altered.

In summary, once location had been taken into account and the reliability of the data sources had been considered, there were no outliers in the merged data that seemed to be likely the result of error. While multivariate outliers were detected, they were not removed from the data because it was unlikely that the observations were erroneous and removing the multivariate outliers could have resulted in the loss of valuable information (i.e. the effect of a drought ending on population size/growth). Similarly, capping and imputing were not performed so as to avoid reducing the strength of any relationships between certain weather conditions and population size or growth.

par(mfrow=c(2,4))
plot(weatherpop$Year, main = "Frequency of Years")
plot(weatherpop$Location, main = "Frequency of Locations")
i <- 3 #produce boxplots of each numeric variable (#3 onwards), using a while loop for efficiency
while (i <= ncol(weatherpop)) { 
  boxplot(weatherpop[,i], main = names(weatherpop[i]))
  i <- i+1
}
#outliers present in Rainfall, Humidity, Population and Population Growth.
par(mfrow=c(1,3))

rainbox <- boxplot(weatherpop$`Avg Daily Rainfall`, main = "Avg Daily Rainfall")
humidbox <- boxplot(weatherpop$`Avg Max Humidity`, main = "Avg Max Humidity")
popbox <- boxplot(weatherpop$`Population(000s)`, main = "Population(000s)")
#Rainfall - univariate
par(mfrow=c(1,1))

rain_outliers <- weatherpop[which(weatherpop$`Avg Daily Rainfall` == rainbox$out[[1]]),c("Location", "Avg Daily Rainfall")]
i <- 2
while (i <= length(rainbox$out)) {
  rain_outliers <- rbind(rain_outliers, weatherpop[which(weatherpop$`Avg Daily Rainfall` == rainbox$out[[i]]) ,c("Location", "Avg Daily Rainfall")])
  i <- i+1
}
rain_outliers #all tropical climates/locations
#Rainfall - bivariate (rain and year)
outliers_rain <- boxplot(weatherpop$`Avg Daily Rainfall` ~ weatherpop$Year, xlab = "Year", ylab = "Avg Daily Rainfall", main = "Distribution of Avg Daily Rainfall by Year")

rain_outlier_locs <- weatherpop[which(weatherpop$`Avg Daily Rainfall` == outliers_rain$out[[1]]),c("Year", "Location", "Avg Daily Rainfall")]
i <- 2
while (i <= length(outliers_rain$out)) {
  rain_outlier_locs <- rbind(rain_outlier_locs, weatherpop[which(weatherpop$`Avg Daily Rainfall` == outliers_rain$out[[i]]) ,c("Year", "Location", "Avg Daily Rainfall")])
  i <- i+1
}
rain_outlier_locs
as.vector(unique(rain_outlier_locs$Location)) #All are tropical climates/locations
[1] "Cairns"       "CoffsHarbour" "Townsville"   "Darwin"       "GoldCoast"   
#Humidity - univariate
humid_outliers <- weatherpop[which(weatherpop$`Avg Max Humidity` == humidbox$out[[1]]),c("Location", "Avg Max Humidity")]
i <- 2
while (i <= length(humidbox$out)) {
  humid_outliers <- rbind(humid_outliers, weatherpop[which(weatherpop$`Avg Max Humidity` == humidbox$out[[i]]) ,c("Location", "Avg Max Humidity")])
  i <- i+1
}
as.vector(unique(humid_outliers$Location)) # All Alice Springs, which is known to a have low humidity
[1] "AliceSprings"
#Humidity - bivariate (humidity and year)
outliers_humid <- boxplot(weatherpop$`Avg Max Humidity` ~ weatherpop$Year, xlab = "Year", ylab = "Avg Max Humidity", main = "Distribution of Avg Max Humidity by Year")

out_humid_locs <- weatherpop[which(weatherpop$`Avg Max Humidity` == outliers_humid$out[[1]]),c("Year", "Location", "Avg Max Humidity")]
i <- 2
while (i <= length(outliers_humid$out)) {
  out_humid_locs <- rbind(out_humid_locs, weatherpop[which(weatherpop$`Avg Max Humidity` == outliers_humid$out[[i]]) ,c("Year", "Location", "Avg Max Humidity")])
  i <- i+1
}
as.vector(unique(out_humid_locs$Location)) #AliceSprings was the only outlier location, year-to-year
[1] "AliceSprings"
#Population - univariate
population_outliers <- weatherpop[which(weatherpop$`Population(000s)` == popbox$out[[1]]),c("Location", "Population(000s)")]
i <- 2
while (i <= length(popbox$out)) {
  population_outliers <- rbind(population_outliers, weatherpop[which(weatherpop$`Population(000s)` == popbox$out[[i]]),c("Location", "Population(000s)")])
  i <- i+1
}
as.vector(unique(population_outliers$Location))
[1] "Adelaide"  "Brisbane"  "Melbourne" "Perth"     "Sydney"   
#Population - bivariate (population and year)
outliers_pop <- boxplot(weatherpop$`Population(000s)` ~ weatherpop$Year, xlab = "Year", ylab = "Population(000s)", main = "Distribution of Population(000s) by Year")

pop_outlier_locs <- weatherpop[which(weatherpop$`Population(000s)` == outliers_pop$out[[1]]),c("Year", "Location", "Population(000s)")]
i <- 2
while (i <= length(outliers_pop$out)) {
  pop_outlier_locs <- rbind(pop_outlier_locs, weatherpop[which(weatherpop$`Population(000s)` == outliers_pop$out[[i]]),c("Year", "Location", "Population(000s)")])
  i <- i+1
}
as.vector(unique(pop_outlier_locs$Location)) #All population outliers are capital cities.
[1] "Adelaide"  "Brisbane"  "Melbourne" "Perth"     "Sydney"   
# As population growth is a result of year and population, use a bivariate scan.
outliers_growth <- boxplot(weatherpop$PopGrowthPct ~ weatherpop$Year, xlab = "Year", ylab = "YoY Population Growth (%)", main = "Distribution of Year-on-Year Population Growth (%) by year")

growth_outlier_locs <- weatherpop[which(weatherpop$PopGrowthPct == outliers_growth$out[[1]]), c("Year", "Location", "PopGrowthPct")]
i <- 2
while (i <= length(outliers_growth$out)) {
  growth_outlier_locs <- rbind(growth_outlier_locs, weatherpop[which(weatherpop$PopGrowthPct == outliers_growth$out[[i]]),c("Year", "Location", "PopGrowthPct")])
  i <- i+1
}
growth_outlier_locs
## Multivariate outliers
multi <- mvn(data = weatherpop[3:8], multivariateOutlierMethod = "quan", showOutliers = TRUE)

multi$multivariateOutliers #observations 1-100 are multivariate outliers (MVOs)
weather_MVOs <- weatherpop[ c(1:100),] 
summary(weather_MVOs) #Every observation from 2009-2012 is a multivariate outlier
      Year            Location  Avg Daily Rainfall  Avg MaxTemp    Avg Max Humidity
 2009   :25   Adelaide    : 4   Min.   :0.2104     Min.   :17.33   Min.   :32.19   
 2010   :25   Albany      : 4   1st Qu.:1.7150     1st Qu.:20.09   1st Qu.:67.51   
 2011   :25   Albury      : 4   Median :2.3792     Median :22.35   Median :70.36   
 2012   :25   AliceSprings: 4   Mean   :2.7836     Mean   :22.89   Mean   :70.46   
 2013   : 0   Ballarat    : 4   3rd Qu.:3.3721     3rd Qu.:25.17   3rd Qu.:75.95   
 2014   : 0   Bendigo     : 4   Max.   :7.6848     Max.   :32.67   Max.   :85.20   
 (Other): 0   (Other)     :76                                                      
 Avg Max Windspeed Population(000s)   PopGrowthPct    
 Min.   : 9.698    Min.   :  10.81   Min.   :-0.4704  
 1st Qu.:17.503    1st Qu.:  53.78   1st Qu.: 0.8263  
 Median :21.082    Median : 116.52   Median : 1.2165  
 Mean   :20.170    Mean   : 650.32   Mean   : 1.2933  
 3rd Qu.:23.113    3rd Qu.: 454.39   3rd Qu.: 1.8104  
 Max.   :27.645    Max.   :4305.28   Max.   : 3.1997  
                                                      

Transform

When it was identified that a variable may benefit from being transformed, a new variable with the same values as the original was created and this was then transformed. This approach was chosen because it would provide more analysis options to anyone wishing to undertake analysis on the dataset. For example, if a non-parametric test could be used to conduct analysis then the original variables could be used. Alternatively, if parametric tests were going to be conducted and ease of interpretation was not a concern, then the transformed variables could be used.

The distributions of the variables in the weatherpop data were explored and 4 variables were identified as having skewed distributions that could benefit from being transformed.

#Check the distribution of each variable and transform if necessary
i <- 3  #don't need to view the distributions of "Year" or "Location" because they're categorical
par(mfrow = c(2,3))
while (i <= ncol(weatherpop)) {
  hist(weatherpop[[i]], main = colnames(weatherpop[i]), xlab = colnames(weatherpop[i]))
  i <- i+1
} #Rainfall and population are positively-skewed; Humidity and windspeed are negatively-skewed.
#Use BoxCox transformation on Population as it needs to be transformed, not just normalised.
par(mfrow = c(1,2))

weatherpop <- weatherpop %>% mutate(bxcx_pop = `Population(000s)`)
weatherpop$bxcx_pop <- BoxCox(weatherpop$`Population(000s)`, lambda = "auto")
hist(weatherpop$`Population(000s)`, main="Distribution of Population(000s)", xlab="Population(000s)")
hist(weatherpop$bxcx_pop, main="BoxCox Population(000s)", xlab="BoxCox transformed Population(000s)")
#Experiment with Transforming the Rainfall, Humidity and Windspeed data
par(mfrow=c(1,2))

hist(sqrt(weatherpop$`Avg Daily Rainfall`),main="Square-root of Avg Daily Rainfall",xlab="Square-root of Avg Daily Rainfall")
hist(log(weatherpop$`Avg Daily Rainfall`),main="Log of Avg Daily Rainfall", xlab="Log of Avg Daily Rainfall")

weatherpop <- weatherpop %>% mutate(sqrt_rain = sqrt(`Avg Daily Rainfall`))
hist((weatherpop$`Avg Max Humidity`)**2,main="Avg Max Humidity squared",xlab="Avg Max Humidity squared")
hist((weatherpop$`Avg Max Humidity`)**3, main="Avg Max Humidity cubed",xlab="Avg Max Humidity cubed")

weatherpop <- weatherpop %>% mutate(humidity_cubed = `Avg Max Humidity`**3) #appeared closer to normal
hist((weatherpop$`Avg Max Windspeed`)**2, main="Avg Max Windspeed squared",xlab="Avg Max Windspeed squared")
hist((weatherpop$`Avg Max Windspeed`)**3,main="Avg Max Windspeed cubed",xlab="Avg Max Windspeed cubed") #Squaring appears closer to normal

weatherpop <- weatherpop %>% mutate(windspeed_sqrd = `Avg Max Windspeed`**2)

References

Australian Bureau of Statistics, 2020. 3218.0 - Regional Population Growth, Australia, 2018-2019 - Population Estimates by Significant Urban Area, 2009 to 2019, viewed 16 May 2020, https://www.abs.gov.au/AUSSTATS/abs@.nsf/DetailsPage/3218.02018-19?OpenDocument

Australian Bureau of Statistics, 2010. 3218.0 - Regional Population Growth, Australia, 2008-09, viewed 06 June 2020, https://www.abs.gov.au/AUSSTATS/abs@.nsf/Previousproducts/3218.0Main%20Features102008-09?opendocument&tabname=Summary&prodno=3218.0&issue=2008-09#=&view=

Bureau of Meteorology, 2020. Daily Weather Observations, viewed 16 May 2020, http://www.bom.gov.au/climate/dwo/

Community.rstudio.com, 2019. Growth rate calculation in R, viewed 31 May 2020, https://community.rstudio.com/t/growth-rate-calculation-in-r/38675

Downes, P. M., Hanslow, K., & Tulip, P. (2014). The effect of the mining boom on the Australian economy. Reserve Bank of Australia research discussion paper, (2014-08).

Heberger, M. (2012). Australia’s millennium drought: Impacts and responses. In The world’s water (pp. 97-125). Island Press, Washington, DC.

Kaggle.com, 2019. Rain in Australia - Predict rain tomorrow in Australia, viewed 16 May 2020, https://www.kaggle.com/jsphyg/weather-dataset-rattle-package#weatherAUS.csv

Stackoverflow.com, 2019. Impute missing data with mean by group, viewed 23 May 2020, https://stackoverflow.com/questions/55345593/impute-missing-data-with-mean-by-group

Walker, S. C., 2013. Ecology & stats, ‘Remove (or replace) everything before or after a specified character in R strings’, viewed 25 May 2020, https://stevencarlislewalker.wordpress.com/2013/02/13/remove-or-replace-everything-before-or-aftera-specified-character-in-r-strings/



LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDEsIDIwMjAiDQphdXRob3I6ICJKYW1lcyBBbmd1cyAtIHMzODUxOTAxIg0Kc3VidGl0bGU6IEFzc2lnbm1lbnQgMg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQotLS0NCiMjIFJlcXVpcmVkIHBhY2thZ2VzIA0KYGBge3J9DQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KG91dGxpZXJzKQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeShNVk4pDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KYGBgDQoNCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5IA0KDQpUaGUgcHVycG9zZSBvZiB0aGUgcHJlLXByb2Nlc3NpbmcgcGVyZm9ybWVkIGluIHRoaXMgYXNzaWdubWVudCB3YXMgdG8gcHJvZHVjZSBhIGRhdGFzZXQgdGhhdCBjb3VsZCBiZSB1c2VkIHRvIGV4cGxvcmUgd2hldGhlciB0aGVyZSBpcyBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGF2ZXJhZ2UgbWF4aW11bSB0ZW1wZXJhdHVyZSwgcmFpbmZhbGwsIHdpbmRzcGVlZCBhbmQvb3IgaHVtaWRpdHksIGFuZCBwb3B1bGF0aW9uIHNpemUgYW5kL29yIGdyb3d0aCwgZm9yIGxvY2F0aW9ucyB0aHJvdWdob3V0IEF1c3RyYWxpYS4NClRoZSByYXcgZGF0YSB3YXMgc291cmNlZCBmcm9tIHRoZSBBdXN0cmFsaWFuIEJ1cmVhdSBvZiBTdGF0aXN0aWNzIChBQlMpIGFuZCBLYWdnbGUgKHZpYSB0aGUgQXVzdHJhbGlhbiBCdXJlYXUgb2YgTWV0ZW9yb2xvZ3kpIGFuZCBwcmUtcHJvY2Vzc2VkIHVzaW5nIHRoZSBmb2xsb3dpbmcgc3RlcHM6DQoNCiogSW1wb3J0aW5nIHRoZSByYXcgZGF0YSB1c2luZyB0aGUgZnVuY3Rpb25zIHJlYWRfZXhjZWwgKHJlYWRfeGwgcGFja2FnZSkgYW5kIHJlYWQuY3N2IChiYXNlIFIpLg0KKiBVbmRlcnN0YW5kaW5nIHRoZSBkYXRhIGFuZCBhcHBseSBkYXRhIHR5cGUgY29udmVyc2lvbnMgYXMgbmVjZXNzYXJ5Lg0KKiBUaWR5aW5nIGFuZCBtYW5pcHVsYXRpbmcgdGhlIEFCUyBwb3B1bGF0aW9uIGRhdGEsIGluY2x1ZGluZyB0aGUgdHJhbnNmb3JtYXRpb24gb2YgYW4gZXhpc3RpbmcgY29sdW1uIHRvIGltcHJvdmUgcmVhZGFiaWxpdHkgYW5kIHRoZSBjcmVhdGlvbiBvZiBhIGNvbHVtbiBtZWFzdXJpbmcgcG9wdWxhdGlvbiBncm93dGgsIHllYXItdG8teWVhci4NCiogU2Nhbm5pbmcgZWFjaCBkYXRhc2V0IGZvciBtaXNzaW5nIGFuZCBzcGVjaWFsIHZhbHVlcyAoZS5nLiAiTkEiLCAiTmFOIikuDQoqIEltcHV0aW5nIG1pc3NpbmcgdmFsdWVzIHVzaW5nIG1lYW4gb3IgbWVkaWFuIChkZXBlbmRpbmcgb24gdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdmFyaWFibGUpLg0KKiBBZ2dyZWdhdGluZyBkYWlseSBvYnNlcnZhdGlvbnMgYW5kIHByb2R1Y2luZyBhdmVyYWdlcyB0byBhbGlnbiBvYnNlcnZhdGlvbnMgZnJlcXVlbmNpZXMuDQoqIE1hbmlwdWxhdGlvbiBvZiBsb2NhdGlvbiBuYW1lcyAoc3RyaW5ncykgdG8gaW1wcm92ZSBkYXRhIHNpbWlsYXJpdHkuDQoqIE1lcmdpbmcgdGhlIHR3byBkYXRhc2V0cyBiYXNlZCBvbiBsb2NhdGlvbnMgYW5kIFllYXJzLg0KKiBTY2FubmluZyB0aGUgbWVyZ2VkIGRhdGEgZm9yIG91dGxpZXJzLCBpZGVudGlmeWluZyB3aGV0aGVyIHRoZSBvdXRsaWVycyB3ZXJlIGVycm9uZW91cyBvciB2YWxpZCBvYnNlcnZhdGlvbnMgYW5kIGp1c3RpZnlpbmcgdGhlaXIgcmV0ZW50aW9uLg0KKiBNYWtpbmcgZHVwbGljYXRlcyBvZiB2YXJpYWJsZXMgYW5kIHRyYW5zZm9ybWluZyB0aGVtIHRvIHByb3ZpZGUgYWx0ZXJuYXRpdmUgdmVyc2lvbnMgb2YgdGhlIHZhcmlhYmxlcyB0aGF0IGNvdWxkIGJlIHVzZWQgZm9yIHBhcmFtZXRyaWMgdGVzdHMgKGkuZS4gdGhleSBzYXRpc2Z5IHRoZSBhc3N1bXB0aW9uIG9mIG5vcm1hbGl0eSkuDQoNCiMjIERhdGEgDQoNClRoZSBkYXRhIGluIHRoaXMgYXNzaWdubWVudCB3YXMgc291cmNlZCBmcm9tIHRoZSBBQlMgYW5kIEthZ2dsZS4NClRoZSBBQlMgZGF0YSB3YXMgZG93bmxvYWRlZCBmcm9tIHRoZSBBQlMgd2Vic2l0ZSBvbiAxNi8wNS8yMDIwIHVzaW5nIHRoZSBmb2xsb3dpbmcgbGluazogImh0dHBzOi8vd3d3LmFicy5nb3YuYXUvQVVTU1RBVFMvc3Vic2NyaWJlci5uc2YvbG9nP29wZW5hZ2VudCYzMjE4MGRzMDAwM18yMDA5LTE5LnhscyYzMjE4LjAmRGF0YSUyMEN1YmVzJjM4MURFOUZDNTJDQUQzNzRDQTI1ODUzNTAwMEI5OEMwJjAmMjAxOC0xOSYyNS4wMy4yMDIwJkxhdGVzdCIuIFRoZSBkYXRhIHdhcyBzdG9yZWQgYXMgYW4gRXhjZWwgOTctMjAwMyB3b3JrYm9vayAoLnhscykgZmlsZS4NCg0KQWNjb3JkaW5nIHRvIHRoZSBBQlMsIHRoZSBkYXRhIGNvbnRhaW5zIGVzdGltYXRlcyBvZiByZXNpZGVudCBwb3B1bGF0aW9ucyBhY2NvcmRpbmcgdG8gU2lnbmlmaWNhbnQgVXJiYW4gQXJlYXMgb2YgQXVzdHJhbGlhIGFzIG9mIEp1bmUgMzAgZWFjaCB5ZWFyIGZyb20gMjAwOSB0byAyMDE5IChBdXN0cmFsaWFuIEJ1cmVhdSBvZiBTdGF0aXN0aWNzLCAyMDIwKS4gU2lnbmlmaWNhbnQgVXJiYW4gQXJlYXMgYXJlIGFyZWFzIHdpdGggYSBwb3B1bGF0aW9uIG9mIGF0IGxlYXN0IDEwLDAwMCBiYXNlZCBvbiBTdGF0aXN0aWNhbCBBcmVhcyBMZXZlbCAyIChTQTJzKSBhbmQgY2FuIHJlcHJlc2VudCBhIGNvbGxlY3Rpb24gb2YgVXJiYW4gQ2VudHJlcyB3aG9zZSBjb2xsZWN0aXZlIHBvcHVsYXRpb24gaXMgYXQgbGVhc3QgMTAsMDAwIChBdXN0cmFsaWFuIEJ1cmVhdSBvZiBTdGF0aXN0aWNzLCAyMDIwKS4gVGhlIEFCUyBkYXRhIGNvbnRhaW5lZCB0aGUgZm9sbG93aW5nIHZhcmlhYmxlczoNCg0KKiBBU0dTIGNvZGUgLSBBdXN0cmFsaWFuIFN0YXRpc3RpY2FsIEdlb2dyYXBoeSBTdGFuZGFyZCAoQVNHUyksIFNpZ25pZmljYW50IFVyYmFuIEFyZWEgaWRlbnRpZmllcg0KKiBTaWduaWZpY2FudCBVcmJhbiBBcmVhcyAtIFRoZSBuYW1lIG9mIHRoZSBTaWduaWZpY2FudCBVcmJhbiBBcmVhDQoqIEVhY2ggb2YgdGhlIHZhcmlhYmxlcyBmcm9tICIyMDA5IiB0byAiMjAxOSIgYXJlIHRoZSBlc3RpbWF0ZWQgcmVzaWRlbnQgcG9wdWxhdGlvbiBvZiBhIGdpdmVuIFNpZ25pZmljYW50IFVyYmFuIEFyZWEgYXMgb2YgSnVuZSAzMCBvZiB0aGUgcmVsZXZhbnQgeWVhcg0KKiBDaGFuZ2UgMjAxOC0yMDE5IE5vLiAtIHRoZSBjaGFuZ2UgaW4gZXN0aW1hdGVkIHJlc2lkZW50IHBvcHVsYXRpb24gZnJvbSAyMDE4IHRvIDIwMTkNCiogQ2hhbmdlIDIwMTgtMjAxOSAlIC0gdGhlIHBlcmNlbnRhZ2UgY2hhbmdlIGluIGVzdGltYXRlZCByZXNpZGVudCBwb3B1bGF0aW9uIGZyb20gMjAxOCB0byAyMDE5DQoNClRoZSBLYWdnbGUgZGF0YSB3YXMgZG93bmxvYWRlZCBmcm9tICJodHRwczovL3d3dy5rYWdnbGUuY29tL2pzcGh5Zy93ZWF0aGVyLWRhdGFzZXQtcmF0dGxlLXBhY2thZ2Ujd2VhdGhlckFVUy5jc3YiIGFuZCBpcyBzdG9yZWQgYXMgYSBDb21tYSBTZXBhcmF0ZWQgVmFsdWUgKENTVikgZmlsZS4gVGhlIEthZ2dsZSBkYXRhIHdhcyBvcmlnaW5hbGx5IHNvdXJjZWQgZnJvbSB0aGUgQXVzdHJhbGlhbiBCdXJlYXUgb2YgTWV0ZW9yb2xvZ3kgKEJvTSkgd2Vic2l0ZSAoImh0dHA6Ly93d3cuYm9tLmdvdi5hdS9jbGltYXRlL2RhdGEiKSBhbmQgd2FzIHB1Ymxpc2hlZCB0byB0aGUgS2FnZ2xlIHdlYnNpdGUgYXMgYSBzaW5nbGUgQ1NWIGZpbGUgYnkgdGhlIEthZ2dsZSB1c2VyICJKb2UgWW91bmciICgiaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9qc3BoeWciKS4NCg0KQWNjb3JkaW5nIHRvIHRoZSBLYWdnbGUgYW5kIEJvTSB3ZWJzaXRlcywgdGhlIGRhdGEgY29udGFpbnMgZGFpbHkgd2VhdGhlciBvYnNlcnZhdGlvbnMgZnJvbSB3ZWF0aGVyIHN0YXRpb25zIGFjcm9zcyBBdXN0cmFsaWEgZnJvbSAwMS8xMS8yMDE3IHRvIDI1LzA2LzIwMTcgKEthZ2dsZS5jb20sIDIwMTkpLiBOb3QgYWxsIHdlYXRoZXIgc3RhdGlvbnMgaW4gdGhlIG5ldHdvcmsgb2JzZXJ2ZSBhbGwgdmFyaWFibGVzIGFuZCwgaW4gc29tZSBsb2NhdGlvbnMgYW5kIHdoZXJlIGFwcHJvcHJpYXRlLCBvYnNlcnZhdGlvbnMgZnJvbSBhIG51bWJlciBvZiBzdGF0aW9ucyBoYXZlIGJlZW4gY29tYmluZWQgdG8gcHJvZHVjZSBhIHN1bW1hcnkgZm9yIGEgc2luZ2xlIGxvY2F0aW9uIChCdXJlYXUgb2YgTWV0ZW9yb2xvZ3ksIDIwMjApLiBUaGUgZGF0YSBjb250YWluZWQgdGhlIGZvbGxvd2luZyB2YXJpYWJsZXM6DQoNCiogRGF0ZSAtIFRoZSBkYXRlIG9mIG9ic2VydmF0aW9uDQoqIExvY2F0aW9uIC0gVGhlIG5hbWUgb2YgdGhlIGxvY2F0aW9uIG9mIHRoZSB3ZWF0aGVyIHN0YXRpb24NCiogTWluVGVtcCAtIFRoZSBtaW5pbXVtIHRlbXBlcmF0dXJlIG9ic2VydmVkIGluIHRoZSAyNCBob3VycyB0byA5YW0sIG1lYXN1cmVkIGluIGRlZ3JlZXMgQ2Vsc2l1cw0KKiBNYXhUZW1wIC0gVGhlIG1heGltdW0gdGVtcGVyYXR1cmUgb2JzZXJ2ZWQgaW4gdGhlIDI0IGhvdXJzIHRvIDlhbSwgbWVhc3VyZWQgaW4gZGVncmVlcyBDZWxzaXVzDQoqIFJhaW5mYWxsIC0gVGhlIGFtb3VudCBvZiByYWluZmFsbCByZWNvcmRlZCBvbiBhIGdpdmVuIGRheSwgbWVhc3VyZWQgaW4gbWlsbGltZXRyZXMgKG1tKQ0KKiBFdmFwb3JhdGlvbiAtIFRoZSBDbGFzcyBBIHBhbiBldmFwb3JhdGlvbiBpbiB0aGUgMjQgaG91cnMgdG8gOWFtLCBtZWFzdXJlZCBpbiBtaWxsaW1ldHJlcyAobW0pDQoqIFN1bnNoaW5lIC0gVGhlIG51bWJlciBvZiBob3VycyBvZiBzdW5zaGluZSBvYnNlcnZlZCBvbiBhIGdpdmVuIGRheQ0KKiBXaW5kR3VzdERpciAtIFRoZSBkaXJlY3Rpb24gb2YgdGhlIHN0cm9uZ2VzdCB3aW5kIGd1c3Qgb2JzZXJ2ZWQgaW4gdGhlIDI0IGhvdXJzIHRvIG1pZG5pZ2h0DQoqIFdpbmRHdXN0U3BlZWQgLSBTdHJvbmdlc3Qgd2luZCBndXN0IChpbiBraWxvbWV0cmVzIHBlciBob3VyKSBvYnNlcnZlZCBpbiB0aGUgMjQgaG91cnMgdG8gbWlkbmlnaHQNCiogV2luZERpcjlhbSAtIFdpbmQgZGlyZWN0aW9uIG1lYXN1cmVkIGF0IDlhbQ0KKiBXaW5kRGlyM3BtIC0gV2luZCBkaXJlY3Rpb24gbWVhc3VyZWQgYXQgM3BtDQoqIFdpbmRTcGVlZDlhbSAtIFdpbmQgc3BlZWQgbWVhc3VyZWQgaW4ga2lsb21ldHJlcyBwZXIgaG91ciAoa20vaCkgYXQgOWFtDQoqIFdpbmRTcGVlZDNwbSAtIFdpbmQgc3BlZWQgbWVhc3VyZWQgaW4ga2lsb21ldHJlcyBwZXIgaG91ciAoa20vaCkgYXQgM3BtDQoqIEh1bWlkaXR5OWFtIC0gUmVsYXRpdmUgaHVtaWRpdHkgKHBlcmNlbnQpIG1lYXN1cmVkIGF0IDlhbQ0KKiBIdW1pZGl0eTNwbSAtIFJlbGF0aXZlIGh1bWlkaXR5IChwZXJjZW50KSBtZWFzdXJlZCBhdCAzcG0NCiogUHJlc3N1cmU5YW0gLSBBdG1vc3BoZXJpYyBwcmVzc3VyZSBtZWFzdXJlZCBpbiBoZWN0b3Bhc2NhbHMgKGhQYSkgYXQgOWFtDQoqIFByZXNzdXJlM3BtIC0gQXRtb3NwaGVyaWMgcHJlc3N1cmUgbWVhc3VyZWQgaW4gaGVjdG9wYXNjYWxzIChoUGEpIGF0IDNwbQ0KKiBDbG91ZDlhbSAtIEZyYWN0aW9uIG9mIHNreSBvYnNjdXJlZCBieSBjbG91ZCBhdCA5YW0sIG1lYXN1cmVkIGluIGVpZ2h0aHMgKG9jdGFzKQ0KKiBDbG91ZDNwbSAtIEZyYWN0aW9uIG9mIHNreSBvYnNjdXJlZCBieSBjbG91ZCBhdCAzcG0sIG1lYXN1cmVkIGluIGVpZ2h0aHMgKG9jdGFzKQ0KKiBUZW1wOWFtIC0gVGhlIHRlbXBlcmF0dXJlIG9ic2VydmVkIGF0IDlhbSwgbWVhc3VyZWQgaW4gZGVncmVlcyBDZWxzaXVzDQoqIFRlbXAzcG0gLSBUaGUgdGVtcGVyYXR1cmUgb2JzZXJ2ZWQgYXQgM3BtLCBtZWFzdXJlZCBpbiBkZWdyZWVzIENlbHNpdXMNCiogUmFpblRvZGF5IC0gSW5kaWNhdGVzIHdoZXRoZXIgcmFpbiB3YXMgb2JzZXJ2ZWQgb24gdGhlIGRheQ0KKiBSSVNLX01NIC0gVGhlIGFtb3VudCBvZiByYWluZmFsbCBmb3IgdGhlIG5leHQgZGF5LCBtZWFzdXJlZCBpbiBtaWxsaW1ldHJlcyAobW0pDQoqIFJhaW5Ub21vcnJvdyAtIEluZGljYXRlcyB3aGV0aGVyIHJhaW4gd2FzIG9ic2VydmVkIG9uIGZvbGxvd2luZyBkYXkNCg0KYGBge3J9DQpzZXR3ZCgiQzovVXNlcnMvSmFtZXMvRGVza3RvcC9Qb3N0IEdyYWQgc3R1ZGllcy9EYXRhIFdyYW5nbGluZyAtIE1BVEgyMzQ5L0Fzc2lnbm1lbnQgMiIpDQojIEltcG9ydCB0aGUgQUJTIHBvcHVsYXRpb24gZGF0YSBhcyAiYWJzcG9wIg0KYWJzcG9wIDwtIHJlYWRfZXhjZWwoIjMyMTgwZHMwMDAzXzIwMDktMTkueGxzIiwgc2hlZXQgPSAiVGFibGUgMSIsIHNraXAgPSA2KQ0KbmFtZXMoYWJzcG9wKVtuYW1lcyhhYnNwb3ApID09ICIuLi4yIl0gPC0gIlNpZy4gVXJiYW4gQXJlYSIgI1JlbmFtZSBzZWNvbmQgY29sdW1uIHRvICJTaWcuIFVyYmFuIEFyZWEiDQpuYW1lcyhhYnNwb3ApICN0aGUgY29sdW1ucyB3aXRoIHJlbGV2YW50IGRhdGEgYXJlIDItMTMNCnRhaWwoYWJzcG9wLDEwKSAjVGhlcmUgaXMgYSB0b3RhbHMgcm93IGFuZCA2IG90aGVyIHJvd3MgYXQgdGhlIGJvdHRvbSBvZiB0aGUgZGF0YSB0aGF0IGFyZSBub3QgbmVlZGVkDQphYnNwb3AgPC0gYWJzcG9wWzM6KG5yb3coYWJzcG9wKS03KSwgMjoxM10gI3NlbGVjdCB0aGUgcm93cyBhbmQgY29sdW1ucyB3aXRoIHJlbGV2YW50IGRhdGEgaW4gdGhlbQ0KaGVhZChhYnNwb3ApDQojIEltcG9ydCB0aGUgS2FnZ2xlIHdlYXRoZXIgZGF0YSBhcyAid2VhdGhlciINCndlYXRoZXIgPC0gcmVhZC5jc3YoIndlYXRoZXJBVVMuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KI1NlbGVjdCB0aGUgY29sdW1ucyByZWxldmFudCB0byBmdWxmaWxsaW5nIHRoZSBnb2FsIG9mIHRoZSBwcmUtcHJvY2Vzc2luZw0KZGVzaXJlZF9tZWFzdXJlcyA8LSBjKCJEYXRlIiwgIkxvY2F0aW9uIiwgIk1heFRlbXAiLCAiUmFpbmZhbGwiLCAiV2luZFNwZWVkOWFtIiwgIldpbmRTcGVlZDNwbSIsICJIdW1pZGl0eTlhbSIsICJIdW1pZGl0eTNwbSIpICNUbyBjb21wYXJlIHBvcHVsYXRpb24gZ3Jvd3RoIGFnYWluc3QgaGVhdCwgcmFpbiwgd2luZCBhbmQgaHVtaWRpdHkuDQp3ZWF0aGVyIDwtIHNlbGVjdCh3ZWF0aGVyLCBhbGxfb2YoZGVzaXJlZF9tZWFzdXJlcykpDQpoZWFkKHdlYXRoZXIpDQojIyBEYXRhIHdpbGwgYmUgbWVyZ2VkIGFmdGVyIGl0IGhhcyBiZWVuIHRpZGllZCwgbWFuaXB1bGF0ZWQgYW5kIGZvcm1hdHRlZA0KYGBgDQoNCiMjIFVuZGVyc3RhbmQgDQoNCkZvciB0aGUgQUJTIHBvcHVsYXRpb24gZGF0YToNCg0KKiBUaGUgZGF0YSBjb25zaXN0ZWQgb2YgMTEwIG9ic2VydmF0aW9ucyBvZiAxMiB2YXJpYWJsZXMsIGFsbCBvZiB3aGljaCB3ZXJlIGNoYXJhY3RlciBkYXRhIHR5cGVzLg0KKiBUaGUgcG9wdWxhdGlvbiBlc3RpbWF0aW9ucyB3ZXJlIGltcG9ydGVkIGFzIGNoYXJhY3RlcnMgYnV0IHdlcmUgYWN0dWFsbHkgd2hvbGUgbnVtYmVycy4gVGh1cywgdGhlIHBvcHVsYXRpb24gZXN0aW1hdGlvbnMgd2VyZSBjb252ZXJ0ZWQgZnJvbSBjaGFyYWN0ZXJzIHRvIGludGVnZXJzLg0KKiBXaGlsZSB0aGUgU2lnbmlmaWNhbnQgVXJiYW4gQXJlYXMgKFNVQSkgd2VyZSBjYXRlZ29yaWNhbCBhbmQgY291bGQgaGF2ZSBiZWVuIGNvbnZlcnRlZCB0byBmYWN0b3JzLCB0aGUgU1VBcyBoYWQgdG8gYmUgbWFuaXB1bGF0ZWQgbGF0ZXItb24gdG8gaW1wcm92ZSB0aGUgbnVtYmVyIG9mIG1hdGNoZXMgYmV0d2VlbiBTVUFzIGFuZCBMb2NhdGlvbnMgaW4gdGhlIHdlYXRoZXIgZGF0YS4gVGhpcyBtYW5pcHVsYXRpb24gd291bGQgY29udmVydCB0aGUgU1VBcyBiYWNrIHRvIGNoYXJhY3RlcnMgYW5kIHNvLCB0byBhdm9pZCBkdXBsaWNhdGluZyBlZmZvcnQsIFNVQXMgd2VyZSBjb252ZXJ0ZWQgdG8gZmFjdG9ycyBhZnRlciB0aGUgZGF0YXNldHMgd2VyZSBtZXJnZWQuDQoNCkZvciB0aGUgS2FnZ2xlIHdlYXRoZXIgZGF0YToNCg0KKiBUaGUgZGF0YSBjb25zaXN0ZWQgb2YgMTQyLDE5MyBvYnNlcnZhdGlvbnMgb2YgOCB2YXJpYWJsZXMuIFR3byB2YXJpYWJsZXMgd2VyZSBjaGFyYWN0ZXIgdHlwZSAoRGF0ZSBhbmQgTG9jYXRpb24pLCB0d28gd2VyZSBudW1lcmljIHR5cGUgKE1heFRlbXAgYW5kIFJhaW5mYWxsKSBhbmQgdGhlIHJlc3Qgd2VyZSBpbnRlZ2VyIHR5cGUuDQoqIFRoZSBEYXRlcyB3ZXJlIGltcG9ydGVkIGFzIGNoYXJhY3RlcnMgYnV0IHdlcmUgY29udmVydGVkIHRvIGEgZGF0ZSB0eXBlIHVzaW5nIHRoZSAieW1kKCkiIGZ1bmN0aW9uLg0KKiBBbHRob3VnaCB0aGUgTG9jYXRpb25zIHdlcmUgY2F0ZWdvcmljYWwgYW5kIGNvdWxkIGhhdmUgYmVlbiBjb252ZXJ0ZWQgdG8gZmFjdG9ycywgZG9pbmcgc28gd291bGQgaGF2ZSBiZWVuIHN1Ym9wdGltYWwgYmVjYXVzZSB0aGUgU2lnbmlmaWNhbnQgVXJiYW4gQXJlYXMgKFNVQXMpIGluIHRoZSBwb3B1bGF0aW9uIGRhdGEgd2VyZSBsZWZ0IGFzIGNoYXJhY3RlcnMuIFdpdGggdGhlIFNVQXMgYmVpbmcgY2hhcmFjdGVycywgdGhlIExvY2F0aW9ucyB3b3VsZCBiZSBjb2VyY2VkIHRvIGEgY2hhcmFjdGVyIGRhdGEgdHlwZSB3aGVuIHRoZSB0d28gd2VyZSB1c2VkIGFzIGtleXMgZm9yIG1lcmdpbmcgdGhlIHR3byBkYXRhc2V0cy4gVG8gYXZvaWQgdGhpcywgdGhlIExvY2F0aW9ucyB3ZXJlIGNvbnZlcnRlZCB0byBmYWN0b3JzIGFmdGVyIHRoZSBkYXRhc2V0cyB3ZXJlIG1lcmdlZC4NCg0KVGhlIFllYXIvRGF0ZSBkYXRhIHdhcyBjb252ZXJ0ZWQgdG8gYW4gb3JkZXJlZCBmYWN0b3IgYWZ0ZXIgdGhlIHdlYXRoZXIgYW5kIHBvcHVsYXRpb24gZGF0YSB3ZXJlIG1lcmdlZCAocmVmZXIgIyMgTWVyZ2luZyB0aGUgZGF0YSkNCg0KYGBge3J9DQojIyBSZXZpZXcgYW5kIHJldmlzZSB0aGUgdmFyaWFibGVzIGluIHRoZSBwb3B1bGF0aW9uIGRhdGENCnN0cihhYnNwb3ApDQphYnNwb3BbICwyOjEyXTwtIHNhcHBseShhYnNwb3BbICwyOjEyXSwgYXMuaW50ZWdlcikgI0NvbnZlcnQgWWVhciBjb2x1bW5zIHRvIGludGVnZXJzDQpzYXBwbHkoYWJzcG9wLCBjbGFzcykNCiMjIFJldmlldyBhbmQgcmV2aXNlIHRoZSB2YXJpYWJsZXMgaW4gdGhlIHdlYXRoZXIgZGF0YQ0Kc3RyKHdlYXRoZXIpDQp3ZWF0aGVyJERhdGUgPC0geW1kKHdlYXRoZXIkRGF0ZSkgI2x1YnJpZGF0ZSBwYWNrYWdlIC0gY29udmVydCAiRGF0ZSIgdG8gYSBkYXRlIHR5cGUgdXNpbmcgInltZCgpIg0Kc2FwcGx5KHdlYXRoZXIsIGNsYXNzKQ0KYGBgDQoNCiMjCVRpZHkgJiBNYW5pcHVsYXRlIERhdGEgSSANCg0KVGhlIEFCUyBwb3B1bGF0aW9uIGRhdGEgZGlkIG5vdCBjb25mb3JtIHRvIHRoZSB0aWR5IGRhdGEgcHJpbmNpcGxlcyBiZWNhdXNlIGVhY2ggb2JzZXJ2YXRpb24gKGVzdGltYXRlZCBwb3B1bGF0aW9uIGluIGEgZ2l2ZW4gU2lnLiBVcmJhbiBBcmVhIChTVUEpIGluIGEgZ2l2ZW4geWVhcikgZGlkIG5vdCBoYXZlIGl0cyBvd24gcm93LiBJbnN0ZWFkLCBvYnNlcnZhdGlvbnMgd2VyZSBzcHJlYWQgYWNyb3NzIHJvd3MgYW5kIGNvbHVtbnMsIG1lYW5pbmcgdGhlIGRhdGEgd2FzIGluIGEgd2lkZSBmb3JtYXQuIEluIG9yZGVyIHRvIG1ha2UgdGhlIEFCUyBwb3B1bGF0aW9uIGRhdGEgdGlkeSwgdGhlIHllYXIgY29sdW1ucyBoYWQgdG8gYmUgcmVhcnJhbmdlZCBzbyB0aGV5IHdlcmUgaW4gcm93cywgYW5kIHRoZSBlc3RpbWF0ZWQgcG9wdWxhdGlvbnMgKHRoZSB2YWx1ZXMpIHdlcmUgaW4gdGhlaXIgb3duIGNvbHVtbi4NClRoZSBBQlMgcG9wdWxhdGlvbiBkYXRhIHdhcyB0aWRpZWQgdXNpbmcgdGhlICJnYXRoZXIoKSIgZnVuY3Rpb24sIHdoaWNoIHRvb2sgdGhlIHllYXIgY29sdW1ucyBhbmQgcmVhcnJhbmdlZCB0aGVtIHRvIGJlIHJvd3Mgd2hpbGUgY3JlYXRpbmcgYSBuZXcgY29sdW1uIChjYWxsZWQgIlBvcHVsYXRpb24iKSB0byBob3VzZSB0aGUgdmFsdWUgb2YgZWFjaCBvYnNlcnZhdGlvbi4NClRoZSBQb3B1bGF0aW9uIGNvbHVtbiB3YXMgZGl2aWRlZCBieSAxLDAwMCB0byBhZGp1c3QgdGhlIHNjYWxlIGFuZCBpbXByb3ZlIHJlYWRhYmlsaXR5LiBUaGUgY29sdW1uIHdhcyBhbHNvIHJlbmFtZWQgdG8gIlBvcHVsYXRpb24oMDAwcykiIHRvIGVuc3VyZSBpdHMgdmFsdWVzIHdvdWxkIGJlIGludGVycHJldGVkIGNvcnJlY3RseS4NCkZpbmFsbHksIGEgbmV3IGNvbHVtbiB3YXMgY3JlYXRlZCB0byBtZWFzdXJlIHRoZSB5ZWFyLW9uLXllYXIgZ3Jvd3RoIGluIHBvcHVsYXRpb24gZm9yIGVhY2ggU1VBLCB1c2luZyBhIHRlY2huaXF1ZSBhZGFwdGVkIGZyb20gdGhlIHJzdHVkaW8gY29tbXVuaXR5IHdlYnNpdGUgKENvbW11bml0eS5yc3R1ZGlvLmNvbSwgMjAxOSkuIEZvciB0aGUgZmlyc3Qgb2JzZXJ2YXRpb24gaW4gZWFjaCBTVUEgKDIwMDkpIHRoZSBncm93dGggd2FzIHNldCB0byBhIG1pc3NpbmcgdmFsdWUsIGFzIHNldHRpbmcgaXQgdG8gemVybyBjb3VsZCByZXN1bHQgaW4gaW5hY2N1cmF0ZSBjb25jbHVzaW9ucyBiZWluZyBkcmF3bi4gVGhlIG1pc3NpbmcgdmFsdWVzIHdlcmUgaW1wdXRlZCBsYXRlciAocmVmZXIgIyMgU2NhbiBJKS4NCg0KYGBge3J9DQojVGlkeSB0aGUgcG9wdWxhdGlvbiBkYXRhDQphYnNwb3AgPC0gYWJzcG9wICU+JSBnYXRoZXIoWWVhciwgUG9wdWxhdGlvbiwgLTEpICU+JSBzZWxlY3QoYygyLDEsMykpDQphYnNwb3AkWWVhciA8LSBhcy5pbnRlZ2VyKGFic3BvcCRZZWFyKQ0KaGVhZChhYnNwb3AsIDMpDQojVHJhbnNmb3JtIHRoZSBQb3B1bGF0aW9uIHZhcmlhYmxlIHRvIG1ha2UgaXQgZWFzaWVyIHRvIHJlYWQuDQphYnNwb3AgPC0gYWJzcG9wICU+JSBtdXRhdGUoUG9wdWxhdGlvbiA9IFBvcHVsYXRpb24vMTAwMCkgJT4lIHJlbmFtZShgUG9wdWxhdGlvbigwMDBzKWAgPSBQb3B1bGF0aW9uKQ0KI0FkZCBhIHllYXItb24teWVhciBncm93dGggY29sdW1uIHRvIHRoZSBwb3B1bGF0aW9uIGRhdGENCmFic3BvcCA8LSBhYnNwb3AgJT4lIGFycmFuZ2UoYFNpZy4gVXJiYW4gQXJlYWAsIFllYXIpICU+JSANCiAgbXV0YXRlKFBvcEdyb3d0aFBjdCA9IA0KICBjYXNlX3doZW4oWWVhciA9PSAyMDA5IH4gYXMuZG91YmxlKE5BKSwNCiAgICAgICAgICAgIFllYXIgIT0gMjAwOSB+IChgUG9wdWxhdGlvbigwMDBzKWAtbGFnKGBQb3B1bGF0aW9uKDAwMHMpYCkpL2xhZyhgUG9wdWxhdGlvbigwMDBzKWApKjEwMCkpDQpgYGANCg0KIyMJU2NhbiBJDQoNClRoZSBkYXRhc2V0cyB3ZXJlIHNjYW5uZWQgZm9yIG1pc3NpbmcgdmFsdWVzIGFuZCBvYnZpb3VzIGVycm9ycyBiZWZvcmUgdGhlIGRhdGFzZXRzIHdlcmUgbWFuaXB1bGF0ZWQgb3IgbWVyZ2VkIGFzIHRoaXMgbWFkZSBpdCBlYXNpZXIgdG8gaWRlbnRpZnkgZXhwbGFuYXRpb25zIGZvciBtaXNzaW5nL2Vycm9uZW91cyB2YWx1ZXMuIEZvciBleGFtcGxlLCBpdCB3b3VsZCBiZSBlYXNpZXIgdG8gaWRlbnRpZnkgdGhhdCBhIG1pc3NpbmcgdmFsdWUgaW4gdGhlIHdlYXRoZXIgZGF0YSB3YXMgZHVlIHRvIGEgd2VhdGhlciBzdGF0aW9uIGJlaW5nIHVuYWJsZSB0byB0cmFjayB0aGF0IG1lYXN1cmVtZW50IGJlZm9yZSB0aGUgZGF0YSBpcyBtYW5pcHVsYXRlZCBvciBtZXJnZWQsIGJlY2F1c2UgbG9jYXRpb24gb3IgdGltZS1zcGVjaWZpYyBjb250ZXh0IGNvdWxkIGJlIGxvc3Qgd2hlbiB0aGUgZGF0YSBpcyBtYW5pcHVsYXRlZCBvciBtZXJnZWQuDQoNCkJlY2F1c2UgdmFyaWFibGVzIGxpa2UgdGVtcGVyYXR1cmUsIGh1bWlkaXR5IGFuZCByYWluZmFsbCBjYW4gdmFyeSBmcm9tIGxvY2F0aW9uLXRvLWxvY2F0aW9uLCB1c2luZyB0aGUgbWVhbi9tZWRpYW4gb2YgYWxsIGxvY2F0aW9ucyB0byBpbXB1dGUgbWlzc2luZyB2YWx1ZXMgZm9yIGEgc2luZ2xlIGxvY2F0aW9uIGNvdWxkIHJlc3VsdCBpbiB1bnJlYWxpc3RpYyB2YWx1ZXMgYmVpbmcgaW1wdXRlZC4gVGhlcmVmb3JlLCB3aGVuIGEgbWlzc2luZyB2YWx1ZSB3YXMgaWRlbnRpZmllZCwgdGhlIG1lYW4vbWVkaWFuIGZvciB0aGUgcmVsZXZhbnQgbWVhc3VyZSB3YXMgY2FsY3VsYXRlZCBmb3IgdGhhdCBwYXJ0aWN1bGFyIGxvY2F0aW9uIGFuZCB1c2VkIHRvIGltcHV0ZSB0aGUgbWlzc2luZyB2YWx1ZS4NCg0KVGhlIHBvcHVsYXRpb24gZGF0YSB3YXMgc2Nhbm5lZCBhbmQgMTEwIG1pc3NpbmcgdmFsdWVzIHdlcmUgaWRlbnRpZmllZCwgYmVpbmcgdGhlIE5BcyB0aGF0IHdlcmUgYWRkZWQgd2hlbiB0aGUgcG9wdWxhdGlvbiBncm93dGggdmFyaWFibGUgd2FzIGNyZWF0ZWQuIEJlY2F1c2UgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdmFyaWFibGUgd2FzIHNrZXdlZCwgdGhlIG1lZGlhbiB3YXMgdXNlZCB0byBpbXB1dGUgdGhlIG1pc3NpbmcgdmFsdWVzIG9uIGEgbG9jYXRpb24tYnktbG9jYXRpb24gYmFzaXMuIFRoZXJlIHdlcmUgbm8gc3BlY2lhbCB2YWx1ZXMgKCJOYU4iIG9yIGluZmluaXRpZXMpIGluIHRoZSBBQlMgcG9wdWxhdGlvbiBkYXRhLg0KDQpUaGUgd2VhdGhlciBkYXRhIHdhcyBzY2FubmVkIGFuZCAxMSwwOTAgbWlzc2luZyB2YWx1ZXMgd2VyZSBpZGVudGlmaWVkLCB3aXRoIGFsbCBtaXNzaW5nIHZhbHVlcyBiZWluZyBpbiBudW1lcmljIHZhcmlhYmxlcy4gQmVmb3JlIGltcHV0aW5nIG1pc3NpbmcgdmFsdWVzLCB0aGUgdmFyaWFibGVzIHdpdGggbm9ybWFsIGRpc3RyaWJ1dGlvbnMgd2VyZSBpZGVudGlmaWVkIGFuZCB0aGVpciBtaXNzaW5nIHZhbHVlcyB3ZXJlIGltcHV0ZWQgdXNpbmcgbWVhbnMgYW5kIGEgdGVjaG5pcXVlIGFkYXB0ZWQgZnJvbSBzdGFja292ZXJmbG93IChTdGFja292ZXJmbG93LmNvbSwgMjAxOSkuIFRoZSByZW1haW5pbmcgbWlzc2luZyB2YWx1ZXMgd2VyZSBpbXB1dGVkIHVzaW5nIG1lZGlhbnMuDQoNCk9uY2UgdGhlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSB3ZWF0aGVyIGRhdGEgaGFkIGJlZW4gaW1wdXRlZCwgbm8gbW9yZSBtaXNzaW5nIHZhbHVlcyB3ZXJlIHByZXNlbnQgaW4gdGhlIGRhdGEuIFRoZXJlIHdlcmUgYWxzbyBubyBzcGVjaWFsIHZhbHVlcyAoIk5hTiIgb3IgaW5maW5pdGVzKS4NCg0KYGBge3J9DQojU2NhbiB0aGUgcG9wdWxhdGlvbiBkYXRhIGZvciBtaXNzaW5nIHZhbHVlcyANCnN1bShpcy5uYShhYnNwb3ApKSAjMTEwIE5BcyBpbiB0aGUgcG9wdWxhdGlvbiBkYXRhDQpzdW0oYXBwbHkoYWJzcG9wLCAxLCBpcy5uYW4pKSAjbm8gTkFOIHZhbHVlcw0Kc3VtKGFwcGx5KGFic3BvcCwgMSwgaXMuaW5maW5pdGUpKSAjbm8gaW5maW5pdGUgdmFsdWVzDQojSW1wdXRlIHRoZSBOQXMgaW4gcG9wdWxhdGlvbiBncm93dGgNCmJveHBsb3QoYWJzcG9wJFBvcEdyb3d0aFBjdCwgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgUG9wdWxhdGlvbiBHcm93dGggKCUpIikgI25vdCBub3JtYWxseSBkaXN0cmlidXRlZA0KI3VzZSBtZWRpYW4gdG8gaW1wdXRlIG1pc3NpbmcgdmFsdWVzIGluIFBvcEdyb3d0aFBjdCBhcyBpdCBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQNCmFic3BvcCA8LSBhYnNwb3AgJT4lIGdyb3VwX2J5KGBTaWcuIFVyYmFuIEFyZWFgKSAlPiUNCiAgbXV0YXRlKFBvcEdyb3d0aFBjdCA9IGlmZWxzZShpcy5uYShQb3BHcm93dGhQY3QpLCBtZWRpYW4oUG9wR3Jvd3RoUGN0LCBuYS5ybSA9IFRSVUUpLCBQb3BHcm93dGhQY3QpKQ0KI1NjYW4gdGhlIHdlYXRoZXIgZGF0YSBmb3IgbWlzc2luZyB2YWx1ZXMNCmluaXRpYWxfd2VhdGhlcl9OQXMgPC0gc3VtKGlzLm5hKHdlYXRoZXIpKQ0KaW5pdGlhbF93ZWF0aGVyX05BcyAjMTEsMDkwIE5BcyBpbiB0aGUgd2VhdGhlciBkYXRhDQpjb2xTdW1zKGlzLm5hKHdlYXRoZXIpKSAjYWxsIGNvbHVtbnMgd2l0aCBtaXNzaW5nIHZhbHVlcyBhcmUgbnVtZXJpYyBkYXRhIHR5cGVzDQojI0ltcHV0ZSBtaXNzaW5nIHZhbHVlcyAtIHVzZSBtZWFuIGZvciBub3JtYWxseS1kaXN0cmlidXRlZCB2YXJpYWJsZXMgYW5kIG1lZGlhbiBmb3IgZXZlcnl0aGluZyBlbHNlDQpwYXIobWZyb3cgPSBjKDIsMykpDQppIDwtIDENCndoaWxlIChpIDw9IG5jb2wod2VhdGhlcikpIHsgDQogIGlmIChpcy5udW1lcmljKHdlYXRoZXJbLGldKSA9PSBUUlVFKSAgYm94cGxvdCh3ZWF0aGVyWyxpXSwgbWFpbiA9IG5hbWVzKHdlYXRoZXJbaV0pKQ0KICBpIDwtIGkrMQ0KfSAjTWF4VGVtcCBhbmQgSHVtaWRpdHkzcG0gYXBwZWFyIG5vcm1hbGx5IGRpc3RyaWJ1dGVkDQp3aGljaChhYnMoc2NvcmVzKHdlYXRoZXIkTWF4VGVtcCwgdHlwZSA9ICJ6IikpID4zKSAjMCBvdXRsaWVycw0Kd2hpY2goYWJzKHNjb3Jlcyh3ZWF0aGVyJEh1bWlkaXR5M3BtLCB0eXBlID0gInoiKSkgPjMpICMwIG91dGxpZXJzDQojTWF4VGVtcCBhbmQgSHVtaWRpdHkzcG0gYXBwZWFyIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHdpdGggbm8gb3V0bGllcnMuIEltcHV0ZSB0aGVtIHVzaW5nIG1lYW4NCm5ybWxfZGlzdF92YXJzIDwtIGMoIk1heFRlbXAiLCAiSHVtaWRpdHkzcG0iKQ0KZm9yIChpIGluIG5ybWxfZGlzdF92YXJzKSB7DQogIHdlYXRoZXJbLGldIDwtICBhdmUod2VhdGhlclssaV0sIHdlYXRoZXIkTG9jYXRpb24sIEZVTiA9IGZ1bmN0aW9uKHgpIA0KICAgIGlmZWxzZShpcy5uYSh4KSwgbWVhbih4LG5hLnJtID0gVFJVRSksIHgpKQ0KfQ0KaW5pdGlhbF93ZWF0aGVyX05Bcy1zdW0oaXMubmEod2VhdGhlcikpICMzLDkzMiBmZXdlciBOQXMNCiNGb3IgdGhlIHZhcmlhYmxlcyB3aXRoIHNrZXdlZCBkaXN0cmlidXRpb25zLCBpbXB1dGUgbWlzc2luZyB2YWx1ZXMgdXNpbmcgbWVkaWFuLg0Kd2VhdGhlciA8LSB3ZWF0aGVyICU+JSBncm91cF9ieShMb2NhdGlvbikgJT4lDQogIG11dGF0ZShSYWluZmFsbCA9IGlmZWxzZShpcy5uYShSYWluZmFsbCksIG1lZGlhbihSYWluZmFsbCwgbmEucm0gPSBUUlVFKSwgUmFpbmZhbGwpKQ0Kd2VhdGhlciA8LSB3ZWF0aGVyICU+JSBncm91cF9ieShMb2NhdGlvbikgJT4lDQogIG11dGF0ZShXaW5kU3BlZWQ5YW0gPSBpZmVsc2UoaXMubmEoV2luZFNwZWVkOWFtKSwgbWVkaWFuKFdpbmRTcGVlZDlhbSwgbmEucm0gPSBUUlVFKSwgV2luZFNwZWVkOWFtKSkNCndlYXRoZXIgPC0gd2VhdGhlciAlPiUgZ3JvdXBfYnkoTG9jYXRpb24pICU+JQ0KICBtdXRhdGUoV2luZFNwZWVkM3BtID0gaWZlbHNlKGlzLm5hKFdpbmRTcGVlZDNwbSksIG1lZGlhbihXaW5kU3BlZWQzcG0sIG5hLnJtID0gVFJVRSksIFdpbmRTcGVlZDNwbSkpDQp3ZWF0aGVyIDwtIHdlYXRoZXIgJT4lIGdyb3VwX2J5KExvY2F0aW9uKSAlPiUNCiAgbXV0YXRlKEh1bWlkaXR5OWFtID0gaWZlbHNlKGlzLm5hKEh1bWlkaXR5OWFtKSwgbWVkaWFuKEh1bWlkaXR5OWFtLCBuYS5ybSA9IFRSVUUpLCBIdW1pZGl0eTlhbSkpDQpzdW0oaXMubmEod2VhdGhlcikpICNubyBtb3JlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSB3ZWF0aGVyIGRhdGENCnN1bShhcHBseSh3ZWF0aGVyLCAxLCBpcy5uYW4pKSAjbm8gTmFOIHZhbHVlcyBpbiB0aGUgd2VhdGhlciBkYXRhDQpzdW0oYXBwbHkod2VhdGhlciwgMSwgaXMuaW5maW5pdGUpKSAjbm8gaW5maW5pdGUgdmFsdWVzIGluIHRoZSB3ZWF0aGVyIGRhdGENCmBgYA0KDQojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIElJDQoNCldoaWxlIHRoZSB3ZWF0aGVyIGRhdGEgaGFkIGRhaWx5IG9ic2VydmF0aW9ucywgdGhlIEFCUyBwb3B1bGF0aW9uIGRhdGEgaGFkIHllYXJseSBvYnNlcnZhdGlvbnMgKHBvcHVsYXRpb24gZXN0aW1hdGVzKS4gVGhlcmVmb3JlLCB0aGUgd2VhdGhlciBkYXRhIGhhZCB0byBiZSBhZ2dyZWdhdGVkIHRvIGFsaWduIHdpdGggdGhlIGZyZXF1ZW5jeSBvZiB0aGUgQUJTIHBvcHVsYXRpb24gZGF0YS4gVGhlICJtdXRhdGUoKSIgZnVuY3Rpb24gYW5kICJ5ZWFyIiBhcmd1bWVudCB3ZXJlIHVzZWQgdG8gY3JlYXRlIGEgWWVhciBjb2x1bW4gaW4gdGhlIHdlYXRoZXIgZGF0YSwgYmFzZWQgb24gdGhlIHllYXIgaW4gdGhlIERhdGUgY29sdW1uLiBPbmNlIHRoZSBZZWFyIGZpZWxkIGhhZCBiZWVuIGNyZWF0ZWQsIG11dGF0ZSB3YXMgYWdhaW4gdXNlZCB0byBjcmVhdGUgY29sdW1ucyBmb3IgbWF4aW11bSBodW1pZGl0eSBhbmQgd2luZCBzcGVlZCwgc28gdGhhdCByZWxhdGlvbnNoaXBzIGJldHdlZW4gaHVtaWRpdHkgb3Igd2luZGluZXNzIGFuZCBwb3B1bGF0aW9uIHNpemUvZ3Jvd3RoIGNvdWxkIGJlIGV4cGxvcmVkLg0KRmluYWxseSwgaW4gb3JkZXIgdG8gYWxpZ24gd2l0aCB0aGUgcG9wdWxhdGlvbiBkYXRhLCB0aGUgd2VhdGhlciBkYXRhIHdhcyBncm91cGVkIGJ5IHllYXIgYW5kIGxvY2F0aW9uIGFuZCBkYWlseSBtZWFzdXJlbWVudHMgd2VyZSBhdmVyYWdlZCB0byBwcm9kdWNlIGF2ZXJhZ2UgZGFpbHkgZmlndXJlcyBmb3IgZWFjaCB5ZWFyIGFuZCBsb2NhdGlvbi4NCg0KVGhlIEFCUyBwb3B1bGF0aW9uIGRhdGEgYW5kIHdlYXRoZXIgZGF0YSB3ZXJlIHRoZW4gY29tcGFyZWQgdG8gaWRlbnRpZnkgYW55IGxvY2F0aW9ucyB0aGF0IHJlcXVpcmVkIG1hbmlwdWxhdGlvbiBpbiBvcmRlciB0byBtYXRjaC4gQWx0aG91Z2ggMTQzIHVuaXF1ZSBuYW1lcyB3ZXJlIGlkZW50aWZpZWQsIG9ubHkgMTYgbmFtZXMgbWF0Y2hlZC4gQW4gZXhwbG9yYXRpb24gb2YgdGhlIG5hbWVzIHRoYXQgZGlkIG5vdCBtYXRjaCByZXZlYWxlZCB0aGF0IHNvbWUgU2lnLiBVcmJhbiBBcmVhcyBoYWQgaHlwaGVucywgd2hpY2ggcHJldmVudGVkIHRoZW0gZnJvbSBtYXRjaGluZyB0byB2ZXJ5IHNpbWlsYXIgTG9jYXRpb25zIHRoYXQgZGlkIG5vdCAoZS5nLiAiQWxidXJ5IiBhbmQgIkFsYnVyeSAtIFdvZG9uZ2EiKS4gIFRvIHJlc29sdmUgdGhpcywgImdzdWIoKSIgd2FzIHVzZWQgdG8gcmVtb3ZlIGh5cGhlbnMgYW5kIGV2ZXJ5dGhpbmcgYWZ0ZXIgdGhlbSBmcm9tIHRoZSBuYW1lcyBvZiBTaWcuIFVyYmFuIEFyZWFzIHVzaW5nIGEgdGVjaG5pcXVlIGFkYXB0ZWQgZnJvbSB0aGUgImVjb2xvZ3kgJiBzdGF0cyIgYmxvZyBieSBTdGV2ZSBXYWxrZXIgKFdhbGtlciwgUy4gQy4sIDIwMTMpLiAgVGhpcyBpbmNyZWFzZWQgdGhlIG51bWJlciBvZiBtYXRjaGVzIHRvIDIwLCBhbiBpbmNyZWFzZSBvZiA0IG1hdGNoZXMuIEZ1cnRoZXIgZXhhbWluYXRpb24gcmV2ZWFsZWQgdGhhdCB0aGUgbmFtZXMgb2Ygc29tZSBMb2NhdGlvbnMgd2VyZSBhY3R1YWxseSB0d28gd29yZHMgd2l0aG91dCBhIHNwYWNlIHNlcGFyYXRpbmcgdGhlbSAoZS5nLiAiQ29mZnNIYXJib3VyIikuIFRvIGltcHJvdmUgdGhlIG51bWJlciBvZiBtYXRjaGluZyBsb2NhdGlvbnMsIHNwYWNlcyB3aXRoaW4gU2lnLiBVcmJhbiBBcmVhIG5hbWVzIHdlcmUgcmVtb3ZlZCB1c2luZyAiZ3N1YigpIiwgbGVhZGluZyB0byBhIHRvdGFsIG9mIDI1IG1hdGNoZXMuDQoNCkJ5IG1hbmlwdWxhdGluZyB0aGUgbmFtZXMgb2YgU2lnLiBVcmJhbiBBcmVhcywgdGhlIG51bWJlciBvZiBtYXRjaGluZyBsb2NhdGlvbiBuYW1lcyBhY2hpZXZlZCBiZXR3ZWVuIHRoZSB0d28gZGF0YXNldHMgd2FzIDI1LCByZXByZXNlbnRpbmcgMjMlIG9mIFNpZy4gVXJiYW4gQXJlYXMgYW5kIDUxJSBvZiB3ZWF0aGVyIHN0YXRpb24gTG9jYXRpb25zLg0KDQpgYGB7cn0NCiNHcm91cCB0aGUgd2VhdGhlciBkYXRhIGJ5IHllYXIgdG8gY3JlYXRlIGEgIlllYXIiIGNvbHVtbiB1c2luZyB0aGUgIkRhdGUiIiBjb2x1bW4NCndlYXRoZXIgPC0gd2VhdGhlciAlPiUgbXV0YXRlKFllYXIgPSB5ZWFyKERhdGUpKQ0Kd2VhdGhlciRZZWFyIDwtIGFzLmludGVnZXIod2VhdGhlciRZZWFyKQ0KI0NyZWF0ZSBkYWlseSBtYXhpbXVtcyBmb3IgaHVtaWRpdHkgYW5kIHdpbmRzcGVlZA0Kd2VhdGhlciA8LSB3ZWF0aGVyICU+JSBtdXRhdGUoTWF4SHVtaWRpdHkgPSBwbWF4KEh1bWlkaXR5OWFtLCBIdW1pZGl0eTNwbSwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1heFdpbmRzcGVlZCA9IHBtYXgoV2luZFNwZWVkOWFtLCBXaW5kU3BlZWQzcG0sIG5hLnJtID0gVFJVRSkpDQojR3JvdXAgYnkgeWVhciBhbmQgY3JlYXRlIGF2ZXJhZ2UgZGFpbHkgdmFsdWVzIGZvciByYWluLCB0ZW1wZXJhdHVyZSwgaHVtaWRpdHkgYW5kIHdpbmRzcGVlZA0KYXZnd2VhdGhlciA8LSB3ZWF0aGVyICU+JSBncm91cF9ieShZZWFyLCBMb2NhdGlvbikgJT4lIA0KICBzdW1tYXJpc2UoIkF2ZyBEYWlseSBSYWluZmFsbCIgPSBtZWFuKFJhaW5mYWxsLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgIkF2ZyBNYXhUZW1wIiA9IG1lYW4oTWF4VGVtcCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICJBdmcgTWF4IEh1bWlkaXR5IiA9IG1lYW4oTWF4SHVtaWRpdHksIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAiQXZnIE1heCBXaW5kc3BlZWQiID0gbWVhbihNYXhXaW5kc3BlZWQsIG5hLnJtID0gVFJVRSkpDQojI1ByZXBhcmF0aW9uIGZvciBtZXJnaW5nDQp1bmlxdWVfcGxhY2VzIDwtIHVuaW9uKGF2Z3dlYXRoZXIkTG9jYXRpb24sIGFic3BvcCRgU2lnLiBVcmJhbiBBcmVhYCkNCmxlbmd0aCh1bmlxdWVfcGxhY2VzKSAjMTQzDQptYXRjaGVzIDwtIGludGVyc2VjdChhdmd3ZWF0aGVyJExvY2F0aW9uLCBhYnNwb3AkYFNpZy4gVXJiYW4gQXJlYWApDQpsZW5ndGgobWF0Y2hlcykgIzE2DQojTWF0Y2hpbmcgb24gTG9jYXRpb24gYW5kIFNpZy4gVXJiYW4gQXJlYSBwcm9kdWNlcyAxNiBtYXRjaGVzLCBjb3VsZCBhbnkgYmUgYWx0ZXJlZCBpbiBvcmRlciB0byBtYXRjaD8NCm1pc21hdGNoZXMgPC0gc2V0ZGlmZih1bmlxdWVfcGxhY2VzLCBtYXRjaGVzKQ0KaGVhZChzb3J0KG1pc21hdGNoZXMpLDMwKSAjSHlwaGVuYXRlZCBuYW1lcyBhbmQgbmFtZXMgd2l0aCBzcGFjZXMgYXBwZWFyIHRvIGJlIGEgbWlzbWF0Y2ggaXNzdWUuDQojU2VhcmNoIGZvciBoeXBlbnMgaW4gTG9jYXRpb24gYW5kIFNpZy4gVXJiYW4gQXJlYSwgYXJlIHRoZXJlIGh5cGhlbnMgaW4ganVzdCBvbmU/DQpzdW0oc3RyX2RldGVjdChtaXNtYXRjaGVzLCBwYXR0ZXJuID0gIi0iKSwgbmEucm0gPSBUUlVFKSAjMjAgaHlwaGVucyBpbiBtaXNtYXRjaGVzDQpzdW0oc3RyX2RldGVjdChhdmd3ZWF0aGVyJExvY2F0aW9uLCBwYXR0ZXJuID0gIi0iKSwgbmEucm0gPSBUUlVFKSAjbm8gaHlwaGVucyBpbiAiTG9jYXRpb24iDQpzdW0oc3RyX2RldGVjdCh1bmlxdWUoYWJzcG9wJGBTaWcuIFVyYmFuIEFyZWFgKSwgcGF0dGVybiA9ICItIikpICNhbGwgaHlwaGVucyBhcmUgaW4gYFNpZy4gVXJiYW4gQXJlYWANCiNEb2VzIHJlbW92aW5nIGV2ZXJ5dGhpbmcgYWZ0ZXIgYSBoeXBoZW4gaW1wcm92ZSB0aGUgbnVtYmVyIG9mIG1hdGNoZXM/DQphYnNwb3AkYFNpZy4gVXJiYW4gQXJlYWAgPC0gZ3N1YihwYXR0ZXJuID0gJyAtLionLCByZXBsYWNlbWVudCA9ICcnLCBhYnNwb3AkYFNpZy4gVXJiYW4gQXJlYWApDQpzdW0oc3RyX2RldGVjdChhYnNwb3AkYFNpZy4gVXJiYW4gQXJlYWAsIHBhdHRlcm4gPSAiLSIpLCBuYS5ybSA9IFRSVUUpICNubyBtb3JlIGh5cGhlbnMNCm1hdGNoZXMyIDwtIGludGVyc2VjdChhdmd3ZWF0aGVyJExvY2F0aW9uLCBhYnNwb3AkYFNpZy4gVXJiYW4gQXJlYWApDQpsZW5ndGgoaW50ZXJzZWN0KG1hdGNoZXMsIG1hdGNoZXMyKSkgPT0gbGVuZ3RoKG1hdGNoZXMpICNOb25lIG9mIHRoZSBvcmlnaW5hbCBtYXRjaGVzIGhhdmUgYmVlbiBsb3N0DQpsZW5ndGgobWF0Y2hlczIpICNub3cgMjAgbWF0Y2hlcywgdG90YWwgbWF0Y2hlcyBoYXZlIGltcHJvdmVkIGJ5IDQNCiNTb21lIExvY2F0aW9ucyBhcmUgYWxsIG9uZSB3b3JkLCBkb2VzIHJlbW92aW5nIHRoZSBzcGFjZXMgaW4gYFNpZy4gVXJiYW4gQXJlYWAgaW1wcm92ZSBtYXRjaGluZz8NCmFic3BvcCRgU2lnLiBVcmJhbiBBcmVhYCA8LSBnc3ViKHBhdHRlcm4gPSAnICcsIHJlcGxhY2VtZW50ID0gJycsIGFic3BvcCRgU2lnLiBVcmJhbiBBcmVhYCkNCm1hdGNoZXMzIDwtIGludGVyc2VjdChhdmd3ZWF0aGVyJExvY2F0aW9uLCBhYnNwb3AkYFNpZy4gVXJiYW4gQXJlYWApDQpsZW5ndGgoaW50ZXJzZWN0KG1hdGNoZXMsIG1hdGNoZXMzKSkgPT0gbGVuZ3RoKG1hdGNoZXMpICNOb25lIG9mIHRoZSBvcmlnaW5hbCBtYXRjaGVzIGhhdmUgYmVlbiBsb3N0DQpsZW5ndGgobWF0Y2hlczMpICMyNSBtYXRjaGVzLCB0b3RhbCBtYXRjaGVzIGhhdmUgaW1wcm92ZWQgYnkgNQ0KYGBgDQoNCiMjIE1lcmdpbmcgdGhlIERhdGENCg0KImlubmVyX2pvaW4oKSIgd2FzIHVzZWQgdG8gbWVyZ2UgdGhlIEFCUyBwb3B1bGF0aW9uIGRhdGEgYW5kIHRoZSB3ZWF0aGVyIGRhdGEsIHdpdGggWWVhciBhbmQgTG9jYXRpb24gKFNpZy4gVXJiYW4gQXJlYSkgdXNlZCBhcyB0aGUgam9pbmluZyBrZXlzLg0KQmVjYXVzZSB0aGUgbWVyZ2VkIGRhdGEgZGlkIG5vdCByZXF1aXJlIGFueSBmdXJ0aGVyIHN0cmluZyBtYW5pcHVsYXRpb24sICJMb2NhdGlvbiIgYW5kICJZZWFyIiB3ZXJlIGNvbnZlcnRlZCB0byBmYWN0b3JzIHNvIHRoZSBlZmZlY3Qgb2YgaW5kaXZpZHVhbCBsb2NhdGlvbnMgb3IgeWVhcnMgY291bGQgYmUgb2JzZXJ2ZWQuIFZhbHVlcyBpbiB0aGUgWWVhciBjb2x1bW4gcmFuZ2VkIGZyb20gMjAwOSB0byAyMDE3IGFuZCB0aGUgbGV2ZWxzIHdlcmUgb3JkZXJlZCBmcm9tIHNtYWxsZXN0IHRvIGxhcmdlc3QuDQoNCmBgYHtyfQ0Kd2VhdGhlcnBvcCA8LSBpbm5lcl9qb2luKGF2Z3dlYXRoZXIsIGFic3BvcCwgYnkgPSBjKCJMb2NhdGlvbiIgPSAiU2lnLiBVcmJhbiBBcmVhIiwgIlllYXIiKSkNCnNhcHBseSh3ZWF0aGVycG9wLCBjbGFzcykgI0xvY2F0aW9uIGlzIGN1cnJlbnRseSBhIGNoYXJhY3RlciB2YXJpYWJsZSBidXQgaXQgc2hvdWxkIGJlIGEgZmFjdG9yDQp3ZWF0aGVycG9wJExvY2F0aW9uIDwtIGFzLmZhY3Rvcih3ZWF0aGVycG9wJExvY2F0aW9uKQ0KbGV2ZWxzKHdlYXRoZXJwb3AkTG9jYXRpb24pDQojVHVybiBZZWFyIGludG8gYSBmYWN0b3Igc28gdGhlIGVmZmVjdCBvZiBpbmRpdmlkdWFsIHllYXJzIGNhbiBiZSBjYWxjdWxhdGVkIChpZiB3ZSB3aXNoIHRvIGRvIHNvKQ0Kd2VhdGhlcnBvcCRZZWFyIDwtIGZhY3Rvcih3ZWF0aGVycG9wJFllYXIsIGxldmVscyA9IGMoMjAwOToyMDE3KSwgb3JkZXJlZCA9IFRSVUUpDQpsZXZlbHMod2VhdGhlcnBvcCRZZWFyKQ0KaXMub3JkZXJlZCh3ZWF0aGVycG9wJFllYXIpICNZZWFyIGlzIG5vdyBvcmRlcmVkLCB0aGUgc21hbGxlc3QgeWVhciBpcyAyMDA5DQpgYGANCg0KIyMJU2NhbiBJSQ0KDQpWaXN1YWwgaW5zcGVjdGlvbiBvZiBib3hwbG90cyB3YXMgdXNlZCB0byBzY2FuIHRoZSBtZXJnZWQgZGF0YSBmb3Igb3V0bGllcnMuIFVuaXZhcmlhdGUgb3V0bGllcnMgd2VyZSBpZGVudGlmaWVkIGluIHRoZSBSYWluZmFsbCwgSHVtaWRpdHksIFBvcHVsYXRpb24gYW5kIFBvcHVsYXRpb24gR3Jvd3RoIHZhcmlhYmxlcy4gVGhlIG91dGxpZXJzIGluIHRoZXNlIHZhcmlhYmxlcyB3ZXJlIGV4YW1pbmVkIHRvIGRldGVybWluZSB3aGV0aGVyIHRoZXkgd2VyZSBsaWtlbHkgdG8gYmUgdGhlIHJlc3VsdCBvZiBhbiBlcnJvciwgb3IgdmFsaWQgb2JzZXJ2YXRpb25zIHRoYXQganVzdCBoYXBwZW5lZCB0byBiZSBvdXRsaWVycy4gVGhlIHNhbWUgdmFyaWFibGVzIHdlcmUgYWxzbyBzY2FubmVkIGZvciBiaXZhcmlhdGUgb3V0bGllcnMgYnkgZ3JvdXBpbmcgb2JzZXJ2YXRpb25zIGFjY29yZGluZyB0byB5ZWFyIGFuZCB2aXN1YWxseSBpbnNwZWN0aW5nIHRoZSByZXN1bHRpbmcgYm94cGxvdHMuDQoNCiogQWxsIG91dGxpZXJzIGluIHRoZSBSYWluZmFsbCB2YXJpYWJsZSB3ZXJlIGZvdW5kIHRvIG9jY3VyIGluIHRyb3BpY2FsIGxvY2F0aW9ucywgd2hlcmUgaGlnaCByYWluZmFsbCBpcyBjb21tb24uIFRoZXJlZm9yZSwgdGhlc2Ugb3V0bGllcnMgd2VyZSBkZWVtZWQgdG8gYmUgdmFsaWQgb2JzZXJ2YXRpb25zLg0KKiBBbGwgb3V0bGllcnMgaW4gSHVtaWRpdHkgb2NjdXJyZWQgaW4gQWxpY2UgU3ByaW5ncywgd2hpY2ggaXMga25vd24gdG8gYmUgYSB2ZXJ5IGRyeSBsb2NhdGlvbi4gVGhlcmVmb3JlLCB0aGUgSHVtaWRpdHkgb3V0bGllcnMgd2VyZSBkZWVtZWQgdG8gYmUgdmFsaWQgb2JzZXJ2YXRpb25zLg0KKiBBbGwgb3V0bGllcnMgaW4gUG9wdWxhdGlvbiBvY2N1cnJlZCBpbiBjYXBpdGFsIGNpdGllcy4gQXMgcGVvcGxlIHRlbmQgdG8gY29uZ3JlZ2F0ZSBpbiBjaXRpZXMsIGl0IG1hZGUgc2Vuc2UgdGhhdCB0aGUgcG9wdWxhdGlvbiBvdXRsaWVycyBvY2N1cnJlZCBpbiBjYXBpdGFsIGNpdGllcy4gVGh1cywgdGhleSB3ZXJlIGRlZW1lZCB2YWxpZC4gDQoqIEJlY2F1c2UgdGhlIFBvcHVsYXRpb24gR3Jvd3RoIHZhcmlhYmxlIHdhcyBjcmVhdGVkIHVzaW5nIHRoZSBZZWFyIHZhcmlhYmxlLCBQb3B1bGF0aW9uIEdyb3d0aCB3YXMgb25seSBzY2FubmVkIGZvciBiaXZhcmlhdGUgb3V0bGllcnMgdG8gZW5zdXJlIG91dGxpZXJzIHdlcmUgbm90IG1hc2tlZCBieSBhbnkgbWVhc3VyZW1lbnQvZGF0YSBlbnRyeSBlcnJvcnMgc3BlY2lmaWMgdG8gYSBnaXZlbiB5ZWFyLiBCaXZhcmlhdGUgb3V0bGllcnMgd2VyZSBpZGVudGlmaWVkIGluIFBlcnRoLCBEYXJ3aW4sIEFsaWNlIFNwcmluZ3MgYW5kIExhdW5jZXN0b24sIHdpdGggdGhlIHR3byBsYXJnZXN0IGdyb3d0aHMgYmVpbmcgRGFyd2luICg0LjA3JSBpbiAyMDEzKSBhbmQgUGVydGggKDIuOTQlIGluIDIwMTMpLiBGYWN0b3JzIHN1Y2ggYXMgdGhlIEF1c3RyYWxpYW4gbWluaW5nIGJvb20sIHdoaWNoIGxlYWQgdG8gaW5jcmVhc2VkIG1pZ3JhdGlvbiBpbnRvIFdlc3Rlcm4gQXVzdHJhbGlhLCBhbmQgdGhlIGZhY3QgdGhhdCB0aGUgTm9ydGhlcm4gVGVycml0b3J5IHdhcyB0aGUgdGhpcmQtZmFzdGVzdCBncm93aW5nIHN0YXRlIGluIEF1c3RyYWxpYSBmcm9tIDIwMDQgdG8gMjAwOSwgb2ZmZXJlZCByZWFzc3VyYW5jZSB0aGF0IHRoZXNlIG91dGxpZXJzIHdlcmUgdW5saWtlbHkgdG8gYmUgZHVlIHRvIGVycm9yIChEb3duZXMgZXQgYWwuIDIwMTQpLCAoQXVzdHJhbGlhbiBCdXJlYXUgb2YgU3RhdGlzdGljcywgMjAxMCkuIEZ1cnRoZXJtb3JlLCBnaXZlbiB0aGF0IHRoZSBQb3B1bGF0aW9uIEdyb3d0aCB2YXJpYWJsZSB3YXMgYmFzZWQgb24gZGF0YSBwcm92aWRlZCBieSB0aGUgQUJTLCB3aGljaCBpcyBrbm93biBmb3IgcHJvdmlkaW5nIHJlbGlhYmxlIGFuZCBhY2N1cmF0ZSBkYXRhLCB0aGVyZSB3YXMgbGl0dGxlIHJlYXNvbiB0byBzdXNwZWN0IHRoYXQgdGhlIG91dGxpZXJzIHdlcmUgdGhlIHJlc3VsdCBvZiBlcnJvcnMuIFRodXMsIHRoZXkgd2VyZSBkZWVtZWQgdmFsaWQuDQoNClRoZSAibXZuKCkiIGZ1bmN0aW9uIHdhcyB1c2VkIHRvIGlkZW50aWZ5IG11bHRpdmFyaWF0ZSBvdXRsaWVycy4gV2hpbGUgMTAwIG11bHRpdmFyaWF0ZSBvdXRsaWVycyB3ZXJlIGRldGVjdGVkLCBub25lIHdlcmUgZm9yIG9ic2VydmF0aW9ucyBsYXRlciB0aGFuIDIwMTIuIFRoZSBtdWx0aXZhcmlhdGUgb3V0bGllcnMgYmV0d2VlbiAyMDA5IGFuZCAyMDEyIG1heSBoYXZlIGJlZW4gYSByZXN1bHQgb2YgdGhlIGFmdGVyIGVmZmVjdHMgb2YgdGhlICJNaWxsZW5uaXVtIGRyb3VnaHQiLCB3aGljaCBlbmRlZCB3aXRoIGhlYXZ5IHJhaW5mYWxsIGluIDIwMDkgYW5kIDIwMTAsIChIZWJlcmdlciwgMjAxMikuIFRoYXQgc2FpZCwgdGhlcmUgaXMgbm8gbWVudGlvbiBvZiB0aGUgZHJvdWdodCdzIGltcGFjdCBvbiB0aGUgd2VhdGhlciBkYXRhIGJ5IHRoZSBCb00uIEdpdmVuIHRoZSBkYXRhIHdhcyBwdWJsaXNoZWQgYnkgdGhlIEFCUyBhbmQgQm9NIChhZ2VuY2llcyB3aG8gaGF2ZSBhIHJlc3BvbnNpYmlsaXR5IHRvLCBhbmQgcmVwdXRhdGlvbiBmb3IsIHByb3ZpZGluZyBhY2N1cmF0ZSBhbmQgcmVsaWFibGUgZGF0YSksIGl0IGlzIHVubGlrZWx5IHRoYXQgdGhlIG91dGxpZXJzIG9ic2VydmVkIHdlcmUgdGhlIHJlc3VsdCBvZiBlcnJvciAoZS5nLiBkYXRhIGVudHJ5IGVycm9ycywgbWVhc3VyZW1lbnQgZXJyb3JzIGV0Yy4pLCBhbmQgdGhlcmVmb3JlLCBpdCB3YXMgZGVjaWRlZCB0aGF0IHRoZSBvdXRsaWVycyBzaG91bGQgbm90IGJlIHJlbW92ZWQgb3IgYWx0ZXJlZC4NCg0KSW4gc3VtbWFyeSwgb25jZSBsb2NhdGlvbiBoYWQgYmVlbiB0YWtlbiBpbnRvIGFjY291bnQgYW5kIHRoZSByZWxpYWJpbGl0eSBvZiB0aGUgZGF0YSBzb3VyY2VzIGhhZCBiZWVuIGNvbnNpZGVyZWQsIHRoZXJlIHdlcmUgbm8gb3V0bGllcnMgaW4gdGhlIG1lcmdlZCBkYXRhIHRoYXQgc2VlbWVkIHRvIGJlIGxpa2VseSB0aGUgcmVzdWx0IG9mIGVycm9yLiBXaGlsZSBtdWx0aXZhcmlhdGUgb3V0bGllcnMgd2VyZSBkZXRlY3RlZCwgdGhleSB3ZXJlIG5vdCByZW1vdmVkIGZyb20gdGhlIGRhdGEgYmVjYXVzZSBpdCB3YXMgdW5saWtlbHkgdGhhdCB0aGUgb2JzZXJ2YXRpb25zIHdlcmUgZXJyb25lb3VzIGFuZCByZW1vdmluZyB0aGUgbXVsdGl2YXJpYXRlIG91dGxpZXJzIGNvdWxkIGhhdmUgcmVzdWx0ZWQgaW4gdGhlIGxvc3Mgb2YgdmFsdWFibGUgaW5mb3JtYXRpb24gKGkuZS4gdGhlIGVmZmVjdCBvZiBhIGRyb3VnaHQgZW5kaW5nIG9uIHBvcHVsYXRpb24gc2l6ZS9ncm93dGgpLiBTaW1pbGFybHksIGNhcHBpbmcgYW5kIGltcHV0aW5nIHdlcmUgbm90IHBlcmZvcm1lZCBzbyBhcyB0byBhdm9pZCByZWR1Y2luZyB0aGUgc3RyZW5ndGggb2YgYW55IHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBjZXJ0YWluIHdlYXRoZXIgY29uZGl0aW9ucyBhbmQgcG9wdWxhdGlvbiBzaXplIG9yIGdyb3d0aC4NCg0KYGBge3J9DQpwYXIobWZyb3c9YygyLDQpKQ0KcGxvdCh3ZWF0aGVycG9wJFllYXIsIG1haW4gPSAiRnJlcXVlbmN5IG9mIFllYXJzIikNCnBsb3Qod2VhdGhlcnBvcCRMb2NhdGlvbiwgbWFpbiA9ICJGcmVxdWVuY3kgb2YgTG9jYXRpb25zIikNCmkgPC0gMyAjcHJvZHVjZSBib3hwbG90cyBvZiBlYWNoIG51bWVyaWMgdmFyaWFibGUgKCMzIG9ud2FyZHMpLCB1c2luZyBhIHdoaWxlIGxvb3AgZm9yIGVmZmljaWVuY3kNCndoaWxlIChpIDw9IG5jb2wod2VhdGhlcnBvcCkpIHsgDQogIGJveHBsb3Qod2VhdGhlcnBvcFssaV0sIG1haW4gPSBuYW1lcyh3ZWF0aGVycG9wW2ldKSkNCiAgaSA8LSBpKzENCn0NCiNvdXRsaWVycyBwcmVzZW50IGluIFJhaW5mYWxsLCBIdW1pZGl0eSwgUG9wdWxhdGlvbiBhbmQgUG9wdWxhdGlvbiBHcm93dGguDQpwYXIobWZyb3c9YygxLDMpKQ0KcmFpbmJveCA8LSBib3hwbG90KHdlYXRoZXJwb3AkYEF2ZyBEYWlseSBSYWluZmFsbGAsIG1haW4gPSAiQXZnIERhaWx5IFJhaW5mYWxsIikNCmh1bWlkYm94IDwtIGJveHBsb3Qod2VhdGhlcnBvcCRgQXZnIE1heCBIdW1pZGl0eWAsIG1haW4gPSAiQXZnIE1heCBIdW1pZGl0eSIpDQpwb3Bib3ggPC0gYm94cGxvdCh3ZWF0aGVycG9wJGBQb3B1bGF0aW9uKDAwMHMpYCwgbWFpbiA9ICJQb3B1bGF0aW9uKDAwMHMpIikNCiNSYWluZmFsbCAtIHVuaXZhcmlhdGUNCnBhcihtZnJvdz1jKDEsMSkpDQpyYWluX291dGxpZXJzIDwtIHdlYXRoZXJwb3Bbd2hpY2god2VhdGhlcnBvcCRgQXZnIERhaWx5IFJhaW5mYWxsYCA9PSByYWluYm94JG91dFtbMV1dKSxjKCJMb2NhdGlvbiIsICJBdmcgRGFpbHkgUmFpbmZhbGwiKV0NCmkgPC0gMg0Kd2hpbGUgKGkgPD0gbGVuZ3RoKHJhaW5ib3gkb3V0KSkgew0KICByYWluX291dGxpZXJzIDwtIHJiaW5kKHJhaW5fb3V0bGllcnMsIHdlYXRoZXJwb3Bbd2hpY2god2VhdGhlcnBvcCRgQXZnIERhaWx5IFJhaW5mYWxsYCA9PSByYWluYm94JG91dFtbaV1dKSAsYygiTG9jYXRpb24iLCAiQXZnIERhaWx5IFJhaW5mYWxsIildKQ0KICBpIDwtIGkrMQ0KfQ0KcmFpbl9vdXRsaWVycyAjYWxsIHRyb3BpY2FsIGNsaW1hdGVzL2xvY2F0aW9ucw0KI1JhaW5mYWxsIC0gYml2YXJpYXRlIChyYWluIGFuZCB5ZWFyKQ0Kb3V0bGllcnNfcmFpbiA8LSBib3hwbG90KHdlYXRoZXJwb3AkYEF2ZyBEYWlseSBSYWluZmFsbGAgfiB3ZWF0aGVycG9wJFllYXIsIHhsYWIgPSAiWWVhciIsIHlsYWIgPSAiQXZnIERhaWx5IFJhaW5mYWxsIiwgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgQXZnIERhaWx5IFJhaW5mYWxsIGJ5IFllYXIiKQ0KcmFpbl9vdXRsaWVyX2xvY3MgPC0gd2VhdGhlcnBvcFt3aGljaCh3ZWF0aGVycG9wJGBBdmcgRGFpbHkgUmFpbmZhbGxgID09IG91dGxpZXJzX3JhaW4kb3V0W1sxXV0pLGMoIlllYXIiLCAiTG9jYXRpb24iLCAiQXZnIERhaWx5IFJhaW5mYWxsIildDQppIDwtIDINCndoaWxlIChpIDw9IGxlbmd0aChvdXRsaWVyc19yYWluJG91dCkpIHsNCiAgcmFpbl9vdXRsaWVyX2xvY3MgPC0gcmJpbmQocmFpbl9vdXRsaWVyX2xvY3MsIHdlYXRoZXJwb3Bbd2hpY2god2VhdGhlcnBvcCRgQXZnIERhaWx5IFJhaW5mYWxsYCA9PSBvdXRsaWVyc19yYWluJG91dFtbaV1dKSAsYygiWWVhciIsICJMb2NhdGlvbiIsICJBdmcgRGFpbHkgUmFpbmZhbGwiKV0pDQogIGkgPC0gaSsxDQp9DQpyYWluX291dGxpZXJfbG9jcw0KYXMudmVjdG9yKHVuaXF1ZShyYWluX291dGxpZXJfbG9jcyRMb2NhdGlvbikpICNBbGwgYXJlIHRyb3BpY2FsIGNsaW1hdGVzL2xvY2F0aW9ucw0KI0h1bWlkaXR5IC0gdW5pdmFyaWF0ZQ0KaHVtaWRfb3V0bGllcnMgPC0gd2VhdGhlcnBvcFt3aGljaCh3ZWF0aGVycG9wJGBBdmcgTWF4IEh1bWlkaXR5YCA9PSBodW1pZGJveCRvdXRbWzFdXSksYygiTG9jYXRpb24iLCAiQXZnIE1heCBIdW1pZGl0eSIpXQ0KaSA8LSAyDQp3aGlsZSAoaSA8PSBsZW5ndGgoaHVtaWRib3gkb3V0KSkgew0KICBodW1pZF9vdXRsaWVycyA8LSByYmluZChodW1pZF9vdXRsaWVycywgd2VhdGhlcnBvcFt3aGljaCh3ZWF0aGVycG9wJGBBdmcgTWF4IEh1bWlkaXR5YCA9PSBodW1pZGJveCRvdXRbW2ldXSkgLGMoIkxvY2F0aW9uIiwgIkF2ZyBNYXggSHVtaWRpdHkiKV0pDQogIGkgPC0gaSsxDQp9DQphcy52ZWN0b3IodW5pcXVlKGh1bWlkX291dGxpZXJzJExvY2F0aW9uKSkgIyBBbGwgQWxpY2UgU3ByaW5ncywgd2hpY2ggaXMga25vd24gdG8gYSBoYXZlIGxvdyBodW1pZGl0eQ0KI0h1bWlkaXR5IC0gYml2YXJpYXRlIChodW1pZGl0eSBhbmQgeWVhcikNCm91dGxpZXJzX2h1bWlkIDwtIGJveHBsb3Qod2VhdGhlcnBvcCRgQXZnIE1heCBIdW1pZGl0eWAgfiB3ZWF0aGVycG9wJFllYXIsIHhsYWIgPSAiWWVhciIsIHlsYWIgPSAiQXZnIE1heCBIdW1pZGl0eSIsIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIEF2ZyBNYXggSHVtaWRpdHkgYnkgWWVhciIpDQpvdXRfaHVtaWRfbG9jcyA8LSB3ZWF0aGVycG9wW3doaWNoKHdlYXRoZXJwb3AkYEF2ZyBNYXggSHVtaWRpdHlgID09IG91dGxpZXJzX2h1bWlkJG91dFtbMV1dKSxjKCJZZWFyIiwgIkxvY2F0aW9uIiwgIkF2ZyBNYXggSHVtaWRpdHkiKV0NCmkgPC0gMg0Kd2hpbGUgKGkgPD0gbGVuZ3RoKG91dGxpZXJzX2h1bWlkJG91dCkpIHsNCiAgb3V0X2h1bWlkX2xvY3MgPC0gcmJpbmQob3V0X2h1bWlkX2xvY3MsIHdlYXRoZXJwb3Bbd2hpY2god2VhdGhlcnBvcCRgQXZnIE1heCBIdW1pZGl0eWAgPT0gb3V0bGllcnNfaHVtaWQkb3V0W1tpXV0pICxjKCJZZWFyIiwgIkxvY2F0aW9uIiwgIkF2ZyBNYXggSHVtaWRpdHkiKV0pDQogIGkgPC0gaSsxDQp9DQphcy52ZWN0b3IodW5pcXVlKG91dF9odW1pZF9sb2NzJExvY2F0aW9uKSkgI0FsaWNlU3ByaW5ncyB3YXMgdGhlIG9ubHkgb3V0bGllciBsb2NhdGlvbiwgeWVhci10by15ZWFyDQojUG9wdWxhdGlvbiAtIHVuaXZhcmlhdGUNCnBvcHVsYXRpb25fb3V0bGllcnMgPC0gd2VhdGhlcnBvcFt3aGljaCh3ZWF0aGVycG9wJGBQb3B1bGF0aW9uKDAwMHMpYCA9PSBwb3Bib3gkb3V0W1sxXV0pLGMoIkxvY2F0aW9uIiwgIlBvcHVsYXRpb24oMDAwcykiKV0NCmkgPC0gMg0Kd2hpbGUgKGkgPD0gbGVuZ3RoKHBvcGJveCRvdXQpKSB7DQogIHBvcHVsYXRpb25fb3V0bGllcnMgPC0gcmJpbmQocG9wdWxhdGlvbl9vdXRsaWVycywgd2VhdGhlcnBvcFt3aGljaCh3ZWF0aGVycG9wJGBQb3B1bGF0aW9uKDAwMHMpYCA9PSBwb3Bib3gkb3V0W1tpXV0pLGMoIkxvY2F0aW9uIiwgIlBvcHVsYXRpb24oMDAwcykiKV0pDQogIGkgPC0gaSsxDQp9DQphcy52ZWN0b3IodW5pcXVlKHBvcHVsYXRpb25fb3V0bGllcnMkTG9jYXRpb24pKQ0KI1BvcHVsYXRpb24gLSBiaXZhcmlhdGUgKHBvcHVsYXRpb24gYW5kIHllYXIpDQpvdXRsaWVyc19wb3AgPC0gYm94cGxvdCh3ZWF0aGVycG9wJGBQb3B1bGF0aW9uKDAwMHMpYCB+IHdlYXRoZXJwb3AkWWVhciwgeGxhYiA9ICJZZWFyIiwgeWxhYiA9ICJQb3B1bGF0aW9uKDAwMHMpIiwgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgUG9wdWxhdGlvbigwMDBzKSBieSBZZWFyIikNCnBvcF9vdXRsaWVyX2xvY3MgPC0gd2VhdGhlcnBvcFt3aGljaCh3ZWF0aGVycG9wJGBQb3B1bGF0aW9uKDAwMHMpYCA9PSBvdXRsaWVyc19wb3Akb3V0W1sxXV0pLGMoIlllYXIiLCAiTG9jYXRpb24iLCAiUG9wdWxhdGlvbigwMDBzKSIpXQ0KaSA8LSAyDQp3aGlsZSAoaSA8PSBsZW5ndGgob3V0bGllcnNfcG9wJG91dCkpIHsNCiAgcG9wX291dGxpZXJfbG9jcyA8LSByYmluZChwb3Bfb3V0bGllcl9sb2NzLCB3ZWF0aGVycG9wW3doaWNoKHdlYXRoZXJwb3AkYFBvcHVsYXRpb24oMDAwcylgID09IG91dGxpZXJzX3BvcCRvdXRbW2ldXSksYygiWWVhciIsICJMb2NhdGlvbiIsICJQb3B1bGF0aW9uKDAwMHMpIildKQ0KICBpIDwtIGkrMQ0KfQ0KYXMudmVjdG9yKHVuaXF1ZShwb3Bfb3V0bGllcl9sb2NzJExvY2F0aW9uKSkgI0FsbCBwb3B1bGF0aW9uIG91dGxpZXJzIGFyZSBjYXBpdGFsIGNpdGllcy4NCiMgQXMgcG9wdWxhdGlvbiBncm93dGggaXMgYSByZXN1bHQgb2YgeWVhciBhbmQgcG9wdWxhdGlvbiwgdXNlIGEgYml2YXJpYXRlIHNjYW4uDQpvdXRsaWVyc19ncm93dGggPC0gYm94cGxvdCh3ZWF0aGVycG9wJFBvcEdyb3d0aFBjdCB+IHdlYXRoZXJwb3AkWWVhciwgeGxhYiA9ICJZZWFyIiwgeWxhYiA9ICJZb1kgUG9wdWxhdGlvbiBHcm93dGggKCUpIiwgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgWWVhci1vbi1ZZWFyIFBvcHVsYXRpb24gR3Jvd3RoICglKSBieSB5ZWFyIikNCmdyb3d0aF9vdXRsaWVyX2xvY3MgPC0gd2VhdGhlcnBvcFt3aGljaCh3ZWF0aGVycG9wJFBvcEdyb3d0aFBjdCA9PSBvdXRsaWVyc19ncm93dGgkb3V0W1sxXV0pLCBjKCJZZWFyIiwgIkxvY2F0aW9uIiwgIlBvcEdyb3d0aFBjdCIpXQ0KaSA8LSAyDQp3aGlsZSAoaSA8PSBsZW5ndGgob3V0bGllcnNfZ3Jvd3RoJG91dCkpIHsNCiAgZ3Jvd3RoX291dGxpZXJfbG9jcyA8LSByYmluZChncm93dGhfb3V0bGllcl9sb2NzLCB3ZWF0aGVycG9wW3doaWNoKHdlYXRoZXJwb3AkUG9wR3Jvd3RoUGN0ID09IG91dGxpZXJzX2dyb3d0aCRvdXRbW2ldXSksYygiWWVhciIsICJMb2NhdGlvbiIsICJQb3BHcm93dGhQY3QiKV0pDQogIGkgPC0gaSsxDQp9DQpncm93dGhfb3V0bGllcl9sb2NzDQojIyBNdWx0aXZhcmlhdGUgb3V0bGllcnMNCm11bHRpIDwtIG12bihkYXRhID0gd2VhdGhlcnBvcFszOjhdLCBtdWx0aXZhcmlhdGVPdXRsaWVyTWV0aG9kID0gInF1YW4iLCBzaG93T3V0bGllcnMgPSBUUlVFKQ0KbXVsdGkkbXVsdGl2YXJpYXRlT3V0bGllcnMgI29ic2VydmF0aW9ucyAxLTEwMCBhcmUgbXVsdGl2YXJpYXRlIG91dGxpZXJzIChNVk9zKQ0Kd2VhdGhlcl9NVk9zIDwtIHdlYXRoZXJwb3BbIGMoMToxMDApLF0gDQpzdW1tYXJ5KHdlYXRoZXJfTVZPcykgI0V2ZXJ5IG9ic2VydmF0aW9uIGZyb20gMjAwOS0yMDEyIGlzIGEgbXVsdGl2YXJpYXRlIG91dGxpZXINCmBgYA0KDQojIwlUcmFuc2Zvcm0gDQoNCldoZW4gaXQgd2FzIGlkZW50aWZpZWQgdGhhdCBhIHZhcmlhYmxlIG1heSBiZW5lZml0IGZyb20gYmVpbmcgdHJhbnNmb3JtZWQsIGEgbmV3IHZhcmlhYmxlIHdpdGggdGhlIHNhbWUgdmFsdWVzIGFzIHRoZSBvcmlnaW5hbCB3YXMgY3JlYXRlZCBhbmQgdGhpcyB3YXMgdGhlbiB0cmFuc2Zvcm1lZC4gVGhpcyBhcHByb2FjaCB3YXMgY2hvc2VuIGJlY2F1c2UgaXQgd291bGQgcHJvdmlkZSBtb3JlIGFuYWx5c2lzIG9wdGlvbnMgdG8gYW55b25lIHdpc2hpbmcgdG8gdW5kZXJ0YWtlIGFuYWx5c2lzIG9uIHRoZSBkYXRhc2V0LiBGb3IgZXhhbXBsZSwgaWYgYSBub24tcGFyYW1ldHJpYyB0ZXN0IGNvdWxkIGJlIHVzZWQgdG8gY29uZHVjdCBhbmFseXNpcyB0aGVuIHRoZSBvcmlnaW5hbCB2YXJpYWJsZXMgY291bGQgYmUgdXNlZC4gQWx0ZXJuYXRpdmVseSwgaWYgcGFyYW1ldHJpYyB0ZXN0cyB3ZXJlIGdvaW5nIHRvIGJlIGNvbmR1Y3RlZCBhbmQgZWFzZSBvZiBpbnRlcnByZXRhdGlvbiB3YXMgbm90IGEgY29uY2VybiwgdGhlbiB0aGUgdHJhbnNmb3JtZWQgdmFyaWFibGVzIGNvdWxkIGJlIHVzZWQuDQoNClRoZSBkaXN0cmlidXRpb25zIG9mIHRoZSB2YXJpYWJsZXMgaW4gdGhlIHdlYXRoZXJwb3AgZGF0YSB3ZXJlIGV4cGxvcmVkIGFuZCA0IHZhcmlhYmxlcyB3ZXJlIGlkZW50aWZpZWQgYXMgaGF2aW5nIHNrZXdlZCBkaXN0cmlidXRpb25zIHRoYXQgY291bGQgYmVuZWZpdCBmcm9tIGJlaW5nIHRyYW5zZm9ybWVkLg0KDQoqIFBvcHVsYXRpb24gd2FzIHRyYW5zZm9ybWVkIHVzaW5nIHRoZSBCb3hDb3ggdHJhbnNmb3JtYXRpb24gYXMgdGhlIHZhcmlhYmxlIG5lZWRlZCB0byBiZSBzY2FsZWQgYW5kIGl0cyBkaXN0cmlidXRpb24gbWFkZSBub3JtYWwgc28gaXQgY291bGQgYmUgdXNlZCBpbiBwYXJhbWV0cmljIHRlc3RzLiBUaGUgdHJhbnNmb3JtZWQgdmFyaWFibGUgd2FzIGFzc2lnbmVkIHRvICJieGN4X3BvcCIuDQoqIFRoZSAiQXZnIERhaWx5IFJhaW5mYWxsIiBkYXRhIHdhcyBwb3NpdGl2ZWx5IHNrZXdlZC4gVGhlIGxvZyB0cmFuc2Zvcm1hdGlvbiBvdmVyLWNvcnJlY3RlZCwgcmVzdWx0aW5nIGluIGEgbmVnYXRpdmVseSBza2V3ZWQgZGlzdHJpYnV0aW9uLCBhbmQgc28gdGhlIHNxdWFyZS1yb290IHRyYW5zZm9ybWF0aW9uIHdhcyBjaG9zZW4gdG8gYnJpbmcgdGhlIGRpc3RyaWJ1dGlvbiBjbG9zZXIgdG8gbm9ybWFsLiBUaGUgdHJhbnNmb3JtZWQgdmFyaWFibGUgd2FzIGFzc2lnbmVkIHRvICJzcXJ0X3JhaW4iLg0KKiBCb3RoIHRoZSAiQXZnIE1heCBIdW1pZGl0eSIgZGF0YSBhbmQgdGhlICJBdmcgTWF4IFdpbmRzcGVlZCIgZGF0YSB3ZXJlIG5lZ2F0aXZlbHkgc2tld2VkLiBBZnRlciB0cnlpbmcgYSBjb3VwbGUgb2YgdHJhbnNmb3JtYXRpb25zIG9uIGVhY2ggZGlzdHJpYnV0aW9uLCB2aXN1YWwgaW5zcGVjdGlvbiBjb25maXJtZWQgdGhhdCByYWlzaW5nIHRoZSAiQXZnIE1heCBIdW1pZGl0eSIgZGF0YSB0byB0aGUgcG93ZXIgb2YgMyAoY3ViaW5nKSBhbmQgc3F1YXJpbmcgdGhlICJBdmcgTWF4IFdpbmRzcGVlZCIgYnJvdWdodCB0aGUgZGlzdHJpYnV0aW9ucyBvZiB0aGUgdHdvIHZhcmlhYmxlcyBjbG9zZXN0IHRvIG5vcm1hbC4gVGhlIHRyYW5zZm9ybWVkIHZhcmlhYmxlcyB3ZXJlIGFzc2lnbmVkIHRvICJodW1pZGl0eV9jdWJlZCIgYW5kICJ3aW5kc3BlZWRfc3FyZCIuDQoNCmBgYHtyfQ0KI0NoZWNrIHRoZSBkaXN0cmlidXRpb24gb2YgZWFjaCB2YXJpYWJsZSBhbmQgdHJhbnNmb3JtIGlmIG5lY2Vzc2FyeQ0KaSA8LSAzICAjZG9uJ3QgbmVlZCB0byB2aWV3IHRoZSBkaXN0cmlidXRpb25zIG9mICJZZWFyIiBvciAiTG9jYXRpb24iIGJlY2F1c2UgdGhleSdyZSBjYXRlZ29yaWNhbA0KcGFyKG1mcm93ID0gYygyLDMpKQ0Kd2hpbGUgKGkgPD0gbmNvbCh3ZWF0aGVycG9wKSkgew0KICBoaXN0KHdlYXRoZXJwb3BbW2ldXSwgbWFpbiA9IGNvbG5hbWVzKHdlYXRoZXJwb3BbaV0pLCB4bGFiID0gY29sbmFtZXMod2VhdGhlcnBvcFtpXSkpDQogIGkgPC0gaSsxDQp9ICNSYWluZmFsbCBhbmQgcG9wdWxhdGlvbiBhcmUgcG9zaXRpdmVseS1za2V3ZWQ7IEh1bWlkaXR5IGFuZCB3aW5kc3BlZWQgYXJlIG5lZ2F0aXZlbHktc2tld2VkLg0KI1VzZSBCb3hDb3ggdHJhbnNmb3JtYXRpb24gb24gUG9wdWxhdGlvbiBhcyBpdCBuZWVkcyB0byBiZSB0cmFuc2Zvcm1lZCwgbm90IGp1c3Qgbm9ybWFsaXNlZC4NCnBhcihtZnJvdyA9IGMoMSwyKSkNCndlYXRoZXJwb3AgPC0gd2VhdGhlcnBvcCAlPiUgbXV0YXRlKGJ4Y3hfcG9wID0gYFBvcHVsYXRpb24oMDAwcylgKQ0Kd2VhdGhlcnBvcCRieGN4X3BvcCA8LSBCb3hDb3god2VhdGhlcnBvcCRgUG9wdWxhdGlvbigwMDBzKWAsIGxhbWJkYSA9ICJhdXRvIikNCmhpc3Qod2VhdGhlcnBvcCRgUG9wdWxhdGlvbigwMDBzKWAsIG1haW49IkRpc3RyaWJ1dGlvbiBvZiBQb3B1bGF0aW9uKDAwMHMpIiwgeGxhYj0iUG9wdWxhdGlvbigwMDBzKSIpDQpoaXN0KHdlYXRoZXJwb3AkYnhjeF9wb3AsIG1haW49IkJveENveCBQb3B1bGF0aW9uKDAwMHMpIiwgeGxhYj0iQm94Q294IHRyYW5zZm9ybWVkIFBvcHVsYXRpb24oMDAwcykiKQ0KI0V4cGVyaW1lbnQgd2l0aCBUcmFuc2Zvcm1pbmcgdGhlIFJhaW5mYWxsLCBIdW1pZGl0eSBhbmQgV2luZHNwZWVkIGRhdGENCnBhcihtZnJvdz1jKDEsMikpDQpoaXN0KHNxcnQod2VhdGhlcnBvcCRgQXZnIERhaWx5IFJhaW5mYWxsYCksbWFpbj0iU3F1YXJlLXJvb3Qgb2YgQXZnIERhaWx5IFJhaW5mYWxsIix4bGFiPSJTcXVhcmUtcm9vdCBvZiBBdmcgRGFpbHkgUmFpbmZhbGwiKQ0KaGlzdChsb2cod2VhdGhlcnBvcCRgQXZnIERhaWx5IFJhaW5mYWxsYCksbWFpbj0iTG9nIG9mIEF2ZyBEYWlseSBSYWluZmFsbCIsIHhsYWI9IkxvZyBvZiBBdmcgRGFpbHkgUmFpbmZhbGwiKQ0Kd2VhdGhlcnBvcCA8LSB3ZWF0aGVycG9wICU+JSBtdXRhdGUoc3FydF9yYWluID0gc3FydChgQXZnIERhaWx5IFJhaW5mYWxsYCkpDQpoaXN0KCh3ZWF0aGVycG9wJGBBdmcgTWF4IEh1bWlkaXR5YCkqKjIsbWFpbj0iQXZnIE1heCBIdW1pZGl0eSBzcXVhcmVkIix4bGFiPSJBdmcgTWF4IEh1bWlkaXR5IHNxdWFyZWQiKQ0KaGlzdCgod2VhdGhlcnBvcCRgQXZnIE1heCBIdW1pZGl0eWApKiozLCBtYWluPSJBdmcgTWF4IEh1bWlkaXR5IGN1YmVkIix4bGFiPSJBdmcgTWF4IEh1bWlkaXR5IGN1YmVkIikNCndlYXRoZXJwb3AgPC0gd2VhdGhlcnBvcCAlPiUgbXV0YXRlKGh1bWlkaXR5X2N1YmVkID0gYEF2ZyBNYXggSHVtaWRpdHlgKiozKSAjYXBwZWFyZWQgY2xvc2VyIHRvIG5vcm1hbA0KaGlzdCgod2VhdGhlcnBvcCRgQXZnIE1heCBXaW5kc3BlZWRgKSoqMiwgbWFpbj0iQXZnIE1heCBXaW5kc3BlZWQgc3F1YXJlZCIseGxhYj0iQXZnIE1heCBXaW5kc3BlZWQgc3F1YXJlZCIpDQpoaXN0KCh3ZWF0aGVycG9wJGBBdmcgTWF4IFdpbmRzcGVlZGApKiozLG1haW49IkF2ZyBNYXggV2luZHNwZWVkIGN1YmVkIix4bGFiPSJBdmcgTWF4IFdpbmRzcGVlZCBjdWJlZCIpICNTcXVhcmluZyBhcHBlYXJzIGNsb3NlciB0byBub3JtYWwNCndlYXRoZXJwb3AgPC0gd2VhdGhlcnBvcCAlPiUgbXV0YXRlKHdpbmRzcGVlZF9zcXJkID0gYEF2ZyBNYXggV2luZHNwZWVkYCoqMikNCmBgYA0KDQojIyBSZWZlcmVuY2VzDQpBdXN0cmFsaWFuIEJ1cmVhdSBvZiBTdGF0aXN0aWNzLCAyMDIwLiAzMjE4LjAgLSBSZWdpb25hbCBQb3B1bGF0aW9uIEdyb3d0aCwgQXVzdHJhbGlhLCAyMDE4LTIwMTkgLSBQb3B1bGF0aW9uIEVzdGltYXRlcyBieSBTaWduaWZpY2FudCBVcmJhbiBBcmVhLCAyMDA5IHRvIDIwMTksIHZpZXdlZCAxNiBNYXkgMjAyMCwgaHR0cHM6Ly93d3cuYWJzLmdvdi5hdS9BVVNTVEFUUy9hYnNALm5zZi9EZXRhaWxzUGFnZS8zMjE4LjAyMDE4LTE5P09wZW5Eb2N1bWVudA0KDQpBdXN0cmFsaWFuIEJ1cmVhdSBvZiBTdGF0aXN0aWNzLCAyMDEwLiAzMjE4LjAgLSBSZWdpb25hbCBQb3B1bGF0aW9uIEdyb3d0aCwgQXVzdHJhbGlhLCAyMDA4LTA5LCB2aWV3ZWQgMDYgSnVuZSAyMDIwLCBodHRwczovL3d3dy5hYnMuZ292LmF1L0FVU1NUQVRTL2Fic0AubnNmL1ByZXZpb3VzcHJvZHVjdHMvMzIxOC4wTWFpbiUyMEZlYXR1cmVzMTAyMDA4LTA5P29wZW5kb2N1bWVudCZ0YWJuYW1lPVN1bW1hcnkmcHJvZG5vPTMyMTguMCZpc3N1ZT0yMDA4LTA5Jm51bT0mdmlldz0NCg0KQnVyZWF1IG9mIE1ldGVvcm9sb2d5LCAyMDIwLiBEYWlseSBXZWF0aGVyIE9ic2VydmF0aW9ucywgdmlld2VkIDE2IE1heSAyMDIwLCBodHRwOi8vd3d3LmJvbS5nb3YuYXUvY2xpbWF0ZS9kd28vDQoNCkNvbW11bml0eS5yc3R1ZGlvLmNvbSwgMjAxOS4gR3Jvd3RoIHJhdGUgY2FsY3VsYXRpb24gaW4gUiwgdmlld2VkIDMxIE1heSAyMDIwLCBodHRwczovL2NvbW11bml0eS5yc3R1ZGlvLmNvbS90L2dyb3d0aC1yYXRlLWNhbGN1bGF0aW9uLWluLXIvMzg2NzUNCg0KRG93bmVzLCBQLiBNLiwgSGFuc2xvdywgSy4sICYgVHVsaXAsIFAuICgyMDE0KS4gVGhlIGVmZmVjdCBvZiB0aGUgbWluaW5nIGJvb20gb24gdGhlIEF1c3RyYWxpYW4gZWNvbm9teS4gUmVzZXJ2ZSBCYW5rIG9mIEF1c3RyYWxpYSByZXNlYXJjaCBkaXNjdXNzaW9uIHBhcGVyLCAoMjAxNC0wOCkuDQoNCkhlYmVyZ2VyLCBNLiAoMjAxMikuIEF1c3RyYWxpYSdzIG1pbGxlbm5pdW0gZHJvdWdodDogSW1wYWN0cyBhbmQgcmVzcG9uc2VzLiBJbiBUaGUgd29ybGQncyB3YXRlciAocHAuIDk3LTEyNSkuIElzbGFuZCBQcmVzcywgV2FzaGluZ3RvbiwgREMuDQoNCkthZ2dsZS5jb20sIDIwMTkuIFJhaW4gaW4gQXVzdHJhbGlhIC0gUHJlZGljdCByYWluIHRvbW9ycm93IGluIEF1c3RyYWxpYSwgdmlld2VkIDE2IE1heSAyMDIwLCBodHRwczovL3d3dy5rYWdnbGUuY29tL2pzcGh5Zy93ZWF0aGVyLWRhdGFzZXQtcmF0dGxlLXBhY2thZ2Ujd2VhdGhlckFVUy5jc3YNCg0KU3RhY2tvdmVyZmxvdy5jb20sIDIwMTkuIEltcHV0ZSBtaXNzaW5nIGRhdGEgd2l0aCBtZWFuIGJ5IGdyb3VwLCB2aWV3ZWQgMjMgTWF5IDIwMjAsIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzU1MzQ1NTkzL2ltcHV0ZS1taXNzaW5nLWRhdGEtd2l0aC1tZWFuLWJ5LWdyb3VwDQoNCldhbGtlciwgUy4gQy4sIDIwMTMuIEVjb2xvZ3kgJiBzdGF0cywgJ1JlbW92ZSAob3IgcmVwbGFjZSkgZXZlcnl0aGluZyBiZWZvcmUgb3IgYWZ0ZXIgYSBzcGVjaWZpZWQgY2hhcmFjdGVyIGluIFIgc3RyaW5ncycsIHZpZXdlZCAyNSBNYXkgMjAyMCwgaHR0cHM6Ly9zdGV2ZW5jYXJsaXNsZXdhbGtlci53b3JkcHJlc3MuY29tLzIwMTMvMDIvMTMvcmVtb3ZlLW9yLXJlcGxhY2UtZXZlcnl0aGluZy1iZWZvcmUtb3ItYWZ0ZXJhLXNwZWNpZmllZC1jaGFyYWN0ZXItaW4tci1zdHJpbmdzLyANCg0KPGJyPg0KPGJyPg==