Required packages
library(dplyr)
library(tidyr)
library(readr)
library(lubridate)
library(outliers)
library(ggplot2)
library(forecast)
Registered S3 method overwritten by 'xts':
method from
as.zoo.xts zoo
Registered S3 method overwritten by 'quantmod':
method from
as.zoo.data.frame zoo
Registered S3 methods overwritten by 'forecast':
method from
fitted.fracdiff fracdiff
residuals.fracdiff fracdiff
This is forecast 8.9
Want to stay up-to-date? Read the Hyndsight blog:
https://robjhyndman.com/hyndsight/
Executive Summary
- In this task of Data Preprocessing, selecting two open data-sets and combining them and understanding the datatypes, applying proper type conversions. Further identifying weather the data-set is in Tidy format and satisfies tidy principles.
- Then we analyze the data for missing values, handles the missing values and along with that scanning for any outliers.
- Along with that verifying if the data is in normalized form and performing required suitable transformation to make it into normalized form. Performing appropriate methods to remove the outliers such that it helps creating the report more accurate.
- Concluding, we are making the data-set ready and suitable for any statistical analysis.
Data
Datasets from Bureau of Meteorology - Autralian Government have been considered for performing data preprocessing.
Source: http://bom.gov.au/climate/data
We have considered 2 datasets which consists data about Rainfall and Temperature in last 5 years.
In dataset 1 containing Rainfall data have variables Bureau of Meteorology station number, Year, Month, Rainfall Amount, Quality and Report Generation Date.
‘Bureau of Meteorology station number’ gives unique identification number for a weather station from which data is collected. ‘Year’ indicates the year in which the observation was recorded.
Month indicated month in which observation was recorded. ‘Rainfall Amount’ gives mean quantity of rainfall observed in that particular month in milimeters(mm) as unit.
‘Quality’ is a quality flag. Climate data pass through a number of stages in quality control which occurs over a period of time. If qulity flag = Y then all the minimum requirements are fulfilled and it ensures there is no errors. If quality flag = N then it indicates that all the minimun requirements have been fulfilled. However there exists certain error in the observations conidered. ‘Report Generation Date’ is the date on which the observations were published.
‘Outcome’ indicates High if mean value for that month is higher than Annual mean value.
In dataset 2 containing Temperature data have variables Bureau of Meteorology station number, Year, Month, Mean Maximum temperature, Quality and Report Generation Date.
‘Bureau of Meteorology station number’ gives unique identification number for a weather station from which data is collected. ‘Year’ indicates the year in which the observation was recorded. Month indicated month in which observation was recorded.
‘Mean Maximum temperature’ gives mean of maximum temperature observed in a day in a month in Celcius (C) as unit.
‘Quality’ is a quality flag. Climate data pass through a number of stages in quality control which occurs over a period of time. If qulity flag = Y then all the minimum requirements are fulfilled and it ensures there is no errors. If quality flag = N then it indicates that all the minimun requirements have been fulfilled. However there exists certain error in the observations conidered. ‘Report Generation Date’ is the date on which the observations were published.
‘Outcome’ indicates High if mean value for that month is higher than Annual mean value.
setwd("F:/DataPre/Assignment3")
data1 <- read_csv("Data/Data_rainfall_melbourne.csv")
Parsed with column specification:
cols(
`Bureau of Meteorology station number` = [32mcol_double()[39m,
Year = [32mcol_double()[39m,
Month = [32mcol_double()[39m,
`Rainfall Amount` = [32mcol_double()[39m,
Quality = [31mcol_character()[39m,
`Report Generation Date` = [31mcol_character()[39m,
Outcome = [31mcol_character()[39m
)
data2 <- read_csv("Data/Data_temperature_melbourne.csv")
Parsed with column specification:
cols(
`Bureau of Meteorology station number` = [32mcol_double()[39m,
Year = [32mcol_double()[39m,
Month = [32mcol_double()[39m,
`Mean maximum temperature` = [32mcol_double()[39m,
Quality = [31mcol_character()[39m,
`Report Generation Date` = [31mcol_character()[39m,
Outcome = [31mcol_character()[39m
)
Dataset 1
- Removing the Station number variable as it persistes as same throughout the dataset.
data1 <- data1 %>% select(-`Bureau of Meteorology station number`)
head(data1)
Dataset 2
- Removing the Station number variable as it persistes as same throughout the dataset.
data2 <- data2 %>% select(-`Bureau of Meteorology station number`)
head(data2)
Joining Datasets
- Combining the datasets using Year, Month and Report generation date as common attribute using left join to ensure that only those records from second dataset are combined which have same Year, Month and Report generation date as in first dataset.
data <- left_join(data1, data2 , by=c("Year","Month", "Report Generation Date"))
head(data)
Understand
- In this step we are typecasting variable Outcome.x and Outcome.y from character to an ordered factor using factor() function.
- We are converting ‘Report generation Date’ variable to Date type from character type using as.Date() method. and summarising Rainfall and Temperature we get to know that there are 6 and 5 missing values respectively and have mean rainfall of 1.6 mm and Temperature of 20.4 degree celcius monthly for past 5 years.
str(data)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 57 obs. of 9 variables:
$ Year : num 2015 2015 2015 2015 2015 ...
$ Month : num 1 2 3 4 5 6 7 8 9 10 ...
$ Rainfall Amount : num 1.832 1.478 0.967 1.038 1.38 ...
$ Quality.x : chr "N" "Y" "Y" "Y" ...
$ Report Generation Date : chr "01/02/2015" "01/03/2015" "01/04/2015" "01/05/2015" ...
$ Outcome.x : chr "High" "High" "Low" "Low" ...
$ Mean maximum temperature: num 25.9 26.4 22.7 19.2 17.1 NA 13.3 13.8 17.3 NA ...
$ Quality.y : chr "Y" "Y" "Y" "N" ...
$ Outcome.y : chr "High" "High" "High" "Low" ...
data$Outcome.x <- factor(data$Outcome.x, levels = c("Low","High"), ordered = TRUE)
data$Outcome.y <- factor(data$Outcome.y,levels = c("Low","High"), ordered = TRUE)
data$Quality.x <- factor(data$Quality.x,levels = c("N","Y"))
data$Quality.y <- factor(data$Quality.y,levels = c("N","Y"))
data$`Report Generation Date` <- as.Date(data$`Report Generation Date`, format = "%d/%m/%Y")
summary(data$`Rainfall Amount`)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
0.0410 0.9605 1.5460 1.6048 2.0945 4.7560 6
summary(data$`Mean maximum temperature`)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
13.30 15.80 20.75 20.40 25.20 27.50 5
str(data)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 57 obs. of 9 variables:
$ Year : num 2015 2015 2015 2015 2015 ...
$ Month : num 1 2 3 4 5 6 7 8 9 10 ...
$ Rainfall Amount : num 1.832 1.478 0.967 1.038 1.38 ...
$ Quality.x : Factor w/ 2 levels "N","Y": 1 2 2 2 2 2 1 1 2 2 ...
$ Report Generation Date : Date, format: "2015-02-01" "2015-03-01" "2015-04-01" "2015-05-01" ...
$ Outcome.x : Ord.factor w/ 2 levels "Low"<"High": 2 2 1 1 2 1 2 NA 1 1 ...
$ Mean maximum temperature: num 25.9 26.4 22.7 19.2 17.1 NA 13.3 13.8 17.3 NA ...
$ Quality.y : Factor w/ 2 levels "N","Y": 2 2 2 1 2 1 2 2 2 1 ...
$ Outcome.y : Ord.factor w/ 2 levels "Low"<"High": 2 2 2 1 1 NA 1 1 1 NA ...
Tidy & Manipulate Data I
str(data)
Classes ‘spec_tbl_df’, ‘tbl_df’, ‘tbl’ and 'data.frame': 57 obs. of 9 variables:
$ Year : num 2015 2015 2015 2015 2015 ...
$ Month : num 1 2 3 4 5 6 7 8 9 10 ...
$ Rainfall Amount : num 1.832 1.478 0.967 1.038 1.38 ...
$ Quality.x : Factor w/ 2 levels "N","Y": 1 2 2 2 2 2 1 1 2 2 ...
$ Report Generation Date : Date, format: "2015-02-01" "2015-03-01" "2015-04-01" "2015-05-01" ...
$ Outcome.x : Ord.factor w/ 2 levels "Low"<"High": 2 2 1 1 2 1 2 NA 1 1 ...
$ Mean maximum temperature: num 25.9 26.4 22.7 19.2 17.1 NA 13.3 13.8 17.3 NA ...
$ Quality.y : Factor w/ 2 levels "N","Y": 2 2 2 1 2 1 2 2 2 1 ...
$ Outcome.y : Ord.factor w/ 2 levels "Low"<"High": 2 2 2 1 1 NA 1 1 1 NA ...
head(data)
- The dataset is Tidy as each variable have its own column, Each observation have its own row and Each value must have its own cell. Hence fulfilling the tidy principles we can say that the dataset is tidy.
Tidy & Manipulate Data II
- Mutating the variable Month and replacing the numerical representation of month number with name of month.
data <- mutate(data,Month=factor(Month,labels = c('Jan','Feb','Mar','April','May','June','July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec')))
head(data)
Scan I
- Scanning the data for missing values
colSums(is.na(data))
Year Month Rainfall Amount Quality.x Report Generation Date
0 0 6 0 0
Outcome.x Mean maximum temperature Quality.y Outcome.y
6 5 0 5
sum(is.na(data))
[1] 22
- After Scanning the data for missing values and we can see there are 22 missing values in total from different columns.
new_data <- na.omit(data)
colSums(is.na(new_data))
Year Month Rainfall Amount Quality.x Report Generation Date
0 0 0 0 0
Outcome.x Mean maximum temperature Quality.y Outcome.y
0 0 0 0
sum(is.na(new_data))
[1] 0
- In this step, removing the missing values rather than going for other approaches like replacing values with a constant or mean or median as this can affect the ‘Outcome’ variable and give false wrong value.
Scan II
z.scores_rainfall <- new_data$`Rainfall Amount` %>% scores(type = "z")
length (which( abs(z.scores_rainfall) >3 ))
[1] 1
boxplot(new_data$`Rainfall Amount`, main = "BoxPlot of Mean Rainfall", ylab = "Rainfall Amount in (mm)" , col = "red" )

z.scores_temperature <- new_data$`Mean maximum temperature` %>% scores(type = "z")
length (which( abs(z.scores_temperature) >3 ))
[1] 0
boxplot(new_data$`Mean maximum temperature`, main = "BoxPlot of Mean Temperature", ylab = "Mean Temperature in (C)" , col = "blue" )

- In this step, after scanning for outliers using boxplot we can infer that there exist only one outlier in Rainfall data while no outliers in Temperature data.
clean_rainfall <- new_data$`Rainfall Amount`[ - which( abs(z.scores_rainfall) >3 )]
z.scores_rainfall <- clean_rainfall %>% scores(type = "z")
length (which( abs(z.scores_rainfall) >3 ))
[1] 0
Here we can see we have sucessfully removed all outliers from rainfall data
LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDIsIDIwMTkiDQphdXRob3I6ICJEZXZhbnNoIFBhcm1hciAtIFMzNzkzNTU3Ig0Kc3VidGl0bGU6IEFzc2lnbm1lbnQgMw0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQotLS0NCg0KDQojIyBSZXF1aXJlZCBwYWNrYWdlcyANCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkob3V0bGllcnMpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KDQpgYGANCg0KDQojIyBFeGVjdXRpdmUgU3VtbWFyeSANCg0KLSBJbiB0aGlzIHRhc2sgb2YgRGF0YSBQcmVwcm9jZXNzaW5nLCBzZWxlY3RpbmcgdHdvIG9wZW4gZGF0YS1zZXRzIGFuZCBjb21iaW5pbmcgdGhlbSBhbmQgdW5kZXJzdGFuZGluZyB0aGUgZGF0YXR5cGVzLCBhcHBseWluZyBwcm9wZXIgdHlwZSBjb252ZXJzaW9ucy4gRnVydGhlciBpZGVudGlmeWluZyB3ZWF0aGVyIHRoZSBkYXRhLXNldCBpcyBpbiBUaWR5IGZvcm1hdCBhbmQgc2F0aXNmaWVzIHRpZHkgcHJpbmNpcGxlcy4gDQotIFRoZW4gd2UgYW5hbHl6ZSB0aGUgZGF0YSBmb3IgbWlzc2luZyB2YWx1ZXMsIGhhbmRsZXMgdGhlIG1pc3NpbmcgdmFsdWVzIGFuZCBhbG9uZyB3aXRoIHRoYXQgc2Nhbm5pbmcgZm9yIGFueSBvdXRsaWVycy4gDQotIEFsb25nIHdpdGggdGhhdCB2ZXJpZnlpbmcgaWYgdGhlIGRhdGEgaXMgaW4gbm9ybWFsaXplZCBmb3JtIGFuZCBwZXJmb3JtaW5nIHJlcXVpcmVkIHN1aXRhYmxlIHRyYW5zZm9ybWF0aW9uIHRvIG1ha2UgaXQgaW50byBub3JtYWxpemVkIGZvcm0uIFBlcmZvcm1pbmcgYXBwcm9wcmlhdGUgbWV0aG9kcyB0byByZW1vdmUgdGhlIG91dGxpZXJzIHN1Y2ggdGhhdCBpdCBoZWxwcyBjcmVhdGluZyB0aGUgcmVwb3J0IG1vcmUgYWNjdXJhdGUuIA0KLSBDb25jbHVkaW5nLCB3ZSBhcmUgbWFraW5nIHRoZSBkYXRhLXNldCByZWFkeSBhbmQgc3VpdGFibGUgZm9yIGFueSBzdGF0aXN0aWNhbCBhbmFseXNpcy4NCg0KDQojIyBEYXRhIA0KDQotIERhdGFzZXRzIGZyb20gQnVyZWF1IG9mIE1ldGVvcm9sb2d5IC0gQXV0cmFsaWFuIEdvdmVybm1lbnQgaGF2ZSBiZWVuIGNvbnNpZGVyZWQgZm9yIHBlcmZvcm1pbmcgZGF0YSBwcmVwcm9jZXNzaW5nLg0KDQotIFNvdXJjZTogaHR0cDovL2JvbS5nb3YuYXUvY2xpbWF0ZS9kYXRhDQoNCi0gV2UgaGF2ZSBjb25zaWRlcmVkIDIgZGF0YXNldHMgd2hpY2ggY29uc2lzdHMgZGF0YSBhYm91dCBSYWluZmFsbCBhbmQgVGVtcGVyYXR1cmUgaW4gbGFzdCA1IHllYXJzLg0KDQotIEluIGRhdGFzZXQgMSBjb250YWluaW5nIFJhaW5mYWxsIGRhdGEgaGF2ZSB2YXJpYWJsZXMgQnVyZWF1IG9mIE1ldGVvcm9sb2d5IHN0YXRpb24gbnVtYmVyLCBZZWFyLCBNb250aCwgUmFpbmZhbGwgQW1vdW50LCBRdWFsaXR5IGFuZCBSZXBvcnQgR2VuZXJhdGlvbiBEYXRlLiANCg0KLSAnQnVyZWF1IG9mIE1ldGVvcm9sb2d5IHN0YXRpb24gbnVtYmVyJyBnaXZlcyB1bmlxdWUgaWRlbnRpZmljYXRpb24gbnVtYmVyIGZvciBhIHdlYXRoZXIgc3RhdGlvbiBmcm9tIHdoaWNoIGRhdGEgaXMgY29sbGVjdGVkLiAnWWVhcicgaW5kaWNhdGVzIHRoZSB5ZWFyIGluIHdoaWNoIHRoZSBvYnNlcnZhdGlvbiB3YXMgcmVjb3JkZWQuIA0KDQotIE1vbnRoIGluZGljYXRlZCBtb250aCBpbiB3aGljaCBvYnNlcnZhdGlvbiB3YXMgcmVjb3JkZWQuIA0KJ1JhaW5mYWxsIEFtb3VudCcgZ2l2ZXMgbWVhbiBxdWFudGl0eSBvZiByYWluZmFsbCBvYnNlcnZlZCBpbiB0aGF0IHBhcnRpY3VsYXIgbW9udGggaW4gbWlsaW1ldGVycyhtbSkgYXMgdW5pdC4gDQoNCi0gJ1F1YWxpdHknIGlzIGEgcXVhbGl0eSBmbGFnLiBDbGltYXRlIGRhdGEgcGFzcyB0aHJvdWdoIGEgbnVtYmVyIG9mIHN0YWdlcyBpbiBxdWFsaXR5IGNvbnRyb2wgd2hpY2ggb2NjdXJzIG92ZXIgYSBwZXJpb2Qgb2YgdGltZS4gIElmIHF1bGl0eSBmbGFnID0gWSB0aGVuIGFsbCB0aGUgbWluaW11bSByZXF1aXJlbWVudHMgYXJlIGZ1bGZpbGxlZCBhbmQgaXQgZW5zdXJlcyB0aGVyZSBpcyBubyBlcnJvcnMuIElmIHF1YWxpdHkgZmxhZyA9IE4gdGhlbiBpdCBpbmRpY2F0ZXMgdGhhdCBhbGwgdGhlIG1pbmltdW4gcmVxdWlyZW1lbnRzIGhhdmUgYmVlbiBmdWxmaWxsZWQuIEhvd2V2ZXIgdGhlcmUgZXhpc3RzIGNlcnRhaW4gZXJyb3IgaW4gdGhlIG9ic2VydmF0aW9ucyBjb25pZGVyZWQuICdSZXBvcnQgR2VuZXJhdGlvbiBEYXRlJyBpcyB0aGUgZGF0ZSBvbiB3aGljaCB0aGUgb2JzZXJ2YXRpb25zIHdlcmUgcHVibGlzaGVkLg0KDQotICdPdXRjb21lJyBpbmRpY2F0ZXMgSGlnaCBpZiBtZWFuIHZhbHVlIGZvciB0aGF0IG1vbnRoIGlzIGhpZ2hlciB0aGFuIEFubnVhbCBtZWFuIHZhbHVlLg0KDQotIEluIGRhdGFzZXQgMiBjb250YWluaW5nIFRlbXBlcmF0dXJlIGRhdGEgaGF2ZSB2YXJpYWJsZXMgQnVyZWF1IG9mIE1ldGVvcm9sb2d5IHN0YXRpb24gbnVtYmVyLCBZZWFyLCBNb250aCwgTWVhbiBNYXhpbXVtIHRlbXBlcmF0dXJlLCBRdWFsaXR5IGFuZCBSZXBvcnQgR2VuZXJhdGlvbiBEYXRlLg0KDQotICdCdXJlYXUgb2YgTWV0ZW9yb2xvZ3kgc3RhdGlvbiBudW1iZXInIGdpdmVzIHVuaXF1ZSBpZGVudGlmaWNhdGlvbiBudW1iZXIgZm9yIGEgd2VhdGhlciBzdGF0aW9uIGZyb20gd2hpY2ggZGF0YSBpcyBjb2xsZWN0ZWQuICdZZWFyJyBpbmRpY2F0ZXMgdGhlIHllYXIgaW4gd2hpY2ggdGhlIG9ic2VydmF0aW9uIHdhcyByZWNvcmRlZC4gTW9udGggaW5kaWNhdGVkIG1vbnRoIGluIHdoaWNoIG9ic2VydmF0aW9uIHdhcyByZWNvcmRlZC4gDQoNCi0gJ01lYW4gTWF4aW11bSB0ZW1wZXJhdHVyZScgZ2l2ZXMgbWVhbiBvZiBtYXhpbXVtIHRlbXBlcmF0dXJlIG9ic2VydmVkIGluIGEgZGF5IGluIGEgbW9udGggaW4gQ2VsY2l1cyAoQykgYXMgdW5pdC4gDQoNCi0gJ1F1YWxpdHknIGlzIGEgcXVhbGl0eSBmbGFnLiBDbGltYXRlIGRhdGEgcGFzcyB0aHJvdWdoIGEgbnVtYmVyIG9mIHN0YWdlcyBpbiBxdWFsaXR5IGNvbnRyb2wgd2hpY2ggb2NjdXJzIG92ZXIgYSBwZXJpb2Qgb2YgdGltZS4gIElmIHF1bGl0eSBmbGFnID0gWSB0aGVuIGFsbCB0aGUgbWluaW11bSByZXF1aXJlbWVudHMgYXJlIGZ1bGZpbGxlZCBhbmQgaXQgZW5zdXJlcyB0aGVyZSBpcyBubyBlcnJvcnMuIElmIHF1YWxpdHkgZmxhZyA9IE4gdGhlbiBpdCBpbmRpY2F0ZXMgdGhhdCBhbGwgdGhlIG1pbmltdW4gcmVxdWlyZW1lbnRzIGhhdmUgYmVlbiBmdWxmaWxsZWQuIEhvd2V2ZXIgdGhlcmUgZXhpc3RzIGNlcnRhaW4gZXJyb3IgaW4gdGhlIG9ic2VydmF0aW9ucyBjb25pZGVyZWQuICdSZXBvcnQgR2VuZXJhdGlvbiBEYXRlJyBpcyB0aGUgZGF0ZSBvbiB3aGljaCB0aGUgb2JzZXJ2YXRpb25zIHdlcmUgcHVibGlzaGVkLiANCg0KLSAnT3V0Y29tZScgaW5kaWNhdGVzIEhpZ2ggaWYgbWVhbiB2YWx1ZSBmb3IgdGhhdCBtb250aCBpcyBoaWdoZXIgdGhhbiBBbm51YWwgbWVhbiB2YWx1ZS4NCg0KYGBge3J9DQpzZXR3ZCgiRjovRGF0YVByZS9Bc3NpZ25tZW50MyIpDQpkYXRhMSA8LSByZWFkX2NzdigiRGF0YS9EYXRhX3JhaW5mYWxsX21lbGJvdXJuZS5jc3YiKQ0KZGF0YTIgPC0gcmVhZF9jc3YoIkRhdGEvRGF0YV90ZW1wZXJhdHVyZV9tZWxib3VybmUuY3N2IikNCmBgYA0KDQojIyBEYXRhc2V0IDENCi0gUmVtb3ZpbmcgdGhlIFN0YXRpb24gbnVtYmVyIHZhcmlhYmxlIGFzIGl0IHBlcnNpc3RlcyBhcyBzYW1lIHRocm91Z2hvdXQgdGhlIGRhdGFzZXQuDQpgYGB7cn0NCmRhdGExIDwtIGRhdGExICU+JSBzZWxlY3QoLWBCdXJlYXUgb2YgTWV0ZW9yb2xvZ3kgc3RhdGlvbiBudW1iZXJgKQ0KaGVhZChkYXRhMSkNCmBgYA0KDQojIyBEYXRhc2V0IDINCi0gUmVtb3ZpbmcgdGhlIFN0YXRpb24gbnVtYmVyIHZhcmlhYmxlIGFzIGl0IHBlcnNpc3RlcyBhcyBzYW1lIHRocm91Z2hvdXQgdGhlIGRhdGFzZXQuDQpgYGB7cn0NCmRhdGEyIDwtIGRhdGEyICU+JSBzZWxlY3QoLWBCdXJlYXUgb2YgTWV0ZW9yb2xvZ3kgc3RhdGlvbiBudW1iZXJgKQ0KaGVhZChkYXRhMikNCmBgYA0KIyMgSm9pbmluZyBEYXRhc2V0cw0KDQotIENvbWJpbmluZyB0aGUgZGF0YXNldHMgdXNpbmcgWWVhciwgTW9udGggYW5kIFJlcG9ydCBnZW5lcmF0aW9uIGRhdGUgYXMgY29tbW9uIGF0dHJpYnV0ZSB1c2luZyBsZWZ0IGpvaW4gdG8gZW5zdXJlIHRoYXQgb25seSB0aG9zZSByZWNvcmRzIGZyb20gc2Vjb25kIGRhdGFzZXQgYXJlIGNvbWJpbmVkIHdoaWNoIGhhdmUgc2FtZSBZZWFyLCBNb250aCBhbmQgUmVwb3J0IGdlbmVyYXRpb24gZGF0ZSBhcyBpbiBmaXJzdCBkYXRhc2V0Lg0KDQpgYGB7cn0NCmRhdGEgPC0gbGVmdF9qb2luKGRhdGExLCBkYXRhMiAsIGJ5PWMoIlllYXIiLCJNb250aCIsICJSZXBvcnQgR2VuZXJhdGlvbiBEYXRlIikpDQpoZWFkKGRhdGEpDQpgYGANCg0KIyMgVW5kZXJzdGFuZCANCg0KLSBJbiB0aGlzIHN0ZXAgd2UgYXJlIHR5cGVjYXN0aW5nIHZhcmlhYmxlIE91dGNvbWUueCBhbmQgT3V0Y29tZS55IGZyb20gY2hhcmFjdGVyIHRvIGFuIG9yZGVyZWQgZmFjdG9yIHVzaW5nIGZhY3RvcigpIGZ1bmN0aW9uLg0KLSBXZSBhcmUgY29udmVydGluZyAnUmVwb3J0IGdlbmVyYXRpb24gRGF0ZScgdmFyaWFibGUgdG8gRGF0ZSB0eXBlIGZyb20gY2hhcmFjdGVyIHR5cGUgdXNpbmcgYXMuRGF0ZSgpIG1ldGhvZC4NCmFuZCBzdW1tYXJpc2luZyBSYWluZmFsbCBhbmQgVGVtcGVyYXR1cmUgd2UgZ2V0IHRvIGtub3cgdGhhdCB0aGVyZSBhcmUgNiBhbmQgNSBtaXNzaW5nIHZhbHVlcyByZXNwZWN0aXZlbHkgYW5kIGhhdmUgbWVhbiByYWluZmFsbCBvZiAxLjYgbW0gYW5kIFRlbXBlcmF0dXJlIG9mIDIwLjQgZGVncmVlIGNlbGNpdXMgbW9udGhseSBmb3IgcGFzdCA1IHllYXJzLg0KDQpgYGB7cn0NCnN0cihkYXRhKQ0KZGF0YSRPdXRjb21lLnggPC0gIGZhY3RvcihkYXRhJE91dGNvbWUueCwgbGV2ZWxzID0gYygiTG93IiwiSGlnaCIpLCBvcmRlcmVkID0gVFJVRSkNCmRhdGEkT3V0Y29tZS55IDwtIGZhY3RvcihkYXRhJE91dGNvbWUueSxsZXZlbHMgPSBjKCJMb3ciLCJIaWdoIiksIG9yZGVyZWQgPSBUUlVFKQ0KZGF0YSRRdWFsaXR5LnggPC0gZmFjdG9yKGRhdGEkUXVhbGl0eS54LGxldmVscyA9IGMoIk4iLCJZIikpDQpkYXRhJFF1YWxpdHkueSA8LSBmYWN0b3IoZGF0YSRRdWFsaXR5LnksbGV2ZWxzID0gYygiTiIsIlkiKSkNCmRhdGEkYFJlcG9ydCBHZW5lcmF0aW9uIERhdGVgIDwtIGFzLkRhdGUoZGF0YSRgUmVwb3J0IEdlbmVyYXRpb24gRGF0ZWAsIGZvcm1hdCA9ICIlZC8lbS8lWSIpDQpzdW1tYXJ5KGRhdGEkYFJhaW5mYWxsIEFtb3VudGApDQpzdW1tYXJ5KGRhdGEkYE1lYW4gbWF4aW11bSB0ZW1wZXJhdHVyZWApDQpzdHIoZGF0YSkNCmBgYA0KDQojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIEkgDQoNCmBgYHtyfQ0Kc3RyKGRhdGEpDQpoZWFkKGRhdGEpDQpgYGANCg0KLSBUaGUgZGF0YXNldCBpcyBUaWR5IGFzIGVhY2ggdmFyaWFibGUgaGF2ZSBpdHMgb3duIGNvbHVtbiwgRWFjaCBvYnNlcnZhdGlvbiBoYXZlIGl0cyBvd24gcm93IGFuZCBFYWNoIHZhbHVlIG11c3QgaGF2ZSBpdHMgb3duIGNlbGwuIEhlbmNlIGZ1bGZpbGxpbmcgdGhlIHRpZHkgcHJpbmNpcGxlcyB3ZSBjYW4gc2F5IHRoYXQgdGhlIGRhdGFzZXQgaXMgdGlkeS4gDQoNCg0KIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJSSANCg0KLSBNdXRhdGluZyB0aGUgdmFyaWFibGUgTW9udGggYW5kIHJlcGxhY2luZyB0aGUgbnVtZXJpY2FsIHJlcHJlc2VudGF0aW9uIG9mIG1vbnRoIG51bWJlciB3aXRoIG5hbWUgb2YgbW9udGguDQoNCmBgYHtyfQ0KZGF0YSA8LSBtdXRhdGUoZGF0YSxNb250aD1mYWN0b3IoTW9udGgsbGFiZWxzID0gYygnSmFuJywnRmViJywnTWFyJywnQXByaWwnLCdNYXknLCdKdW5lJywnSnVseScsICdBdWcnLCAnU2VwdCcsICdPY3QnLCAnTm92JywgJ0RlYycpKSkNCmhlYWQoZGF0YSkNCmBgYA0KDQoNCiMjCVNjYW4gSSANCg0KLSBTY2FubmluZyB0aGUgZGF0YSBmb3IgbWlzc2luZyB2YWx1ZXMNCg0KYGBge3J9DQpjb2xTdW1zKGlzLm5hKGRhdGEpKQ0Kc3VtKGlzLm5hKGRhdGEpKQ0KYGBgDQoNCi0gQWZ0ZXIgU2Nhbm5pbmcgdGhlIGRhdGEgZm9yIG1pc3NpbmcgdmFsdWVzIGFuZCB3ZSBjYW4gc2VlIHRoZXJlIGFyZSAyMiBtaXNzaW5nIHZhbHVlcyBpbiB0b3RhbCBmcm9tIGRpZmZlcmVudCBjb2x1bW5zLg0KDQpgYGB7cn0NCm5ld19kYXRhIDwtIG5hLm9taXQoZGF0YSkgDQpjb2xTdW1zKGlzLm5hKG5ld19kYXRhKSkgDQpzdW0oaXMubmEobmV3X2RhdGEpKQ0KYGBgDQoNCi0gSW4gdGhpcyBzdGVwLCByZW1vdmluZyB0aGUgbWlzc2luZyB2YWx1ZXMgcmF0aGVyIHRoYW4gZ29pbmcgZm9yIG90aGVyIGFwcHJvYWNoZXMgbGlrZSByZXBsYWNpbmcgdmFsdWVzIHdpdGggYSBjb25zdGFudCBvciBtZWFuIG9yIG1lZGlhbiBhcyB0aGlzIGNhbiBhZmZlY3QgdGhlICdPdXRjb21lJyB2YXJpYWJsZSBhbmQgZ2l2ZSBmYWxzZSB3cm9uZyB2YWx1ZS4NCg0KIyMJU2NhbiBJSQ0KDQpgYGB7cn0NCnouc2NvcmVzX3JhaW5mYWxsIDwtIG5ld19kYXRhJGBSYWluZmFsbCBBbW91bnRgICU+JSAgc2NvcmVzKHR5cGUgPSAieiIpDQpsZW5ndGggKHdoaWNoKCBhYnMoei5zY29yZXNfcmFpbmZhbGwpID4zICkpDQpib3hwbG90KG5ld19kYXRhJGBSYWluZmFsbCBBbW91bnRgLCBtYWluID0gIkJveFBsb3Qgb2YgIE1lYW4gUmFpbmZhbGwiLCB5bGFiID0gIlJhaW5mYWxsIEFtb3VudCBpbiAobW0pIiAsIGNvbCA9ICJyZWQiICkNCnouc2NvcmVzX3RlbXBlcmF0dXJlIDwtIG5ld19kYXRhJGBNZWFuIG1heGltdW0gdGVtcGVyYXR1cmVgICU+JSAgc2NvcmVzKHR5cGUgPSAieiIpDQpsZW5ndGggKHdoaWNoKCBhYnMoei5zY29yZXNfdGVtcGVyYXR1cmUpID4zICkpDQpib3hwbG90KG5ld19kYXRhJGBNZWFuIG1heGltdW0gdGVtcGVyYXR1cmVgLCBtYWluID0gIkJveFBsb3Qgb2YgTWVhbiBUZW1wZXJhdHVyZSIsIHlsYWIgPSAiTWVhbiBUZW1wZXJhdHVyZSBpbiAoQykiICwgY29sID0gImJsdWUiICkNCmBgYA0KDQotIEluIHRoaXMgc3RlcCwgYWZ0ZXIgc2Nhbm5pbmcgZm9yIG91dGxpZXJzIHVzaW5nIGJveHBsb3Qgd2UgY2FuIGluZmVyIHRoYXQgdGhlcmUgZXhpc3Qgb25seSBvbmUgb3V0bGllciBpbiBSYWluZmFsbCBkYXRhIHdoaWxlIG5vIG91dGxpZXJzIGluIFRlbXBlcmF0dXJlIGRhdGEuDQoNCmBgYHtyfQ0KY2xlYW5fcmFpbmZhbGwgPC0gbmV3X2RhdGEkYFJhaW5mYWxsIEFtb3VudGBbIC0gd2hpY2goIGFicyh6LnNjb3Jlc19yYWluZmFsbCkgPjMgKV0NCnouc2NvcmVzX3JhaW5mYWxsIDwtIGNsZWFuX3JhaW5mYWxsICU+JSAgc2NvcmVzKHR5cGUgPSAieiIpDQpsZW5ndGggKHdoaWNoKCBhYnMoei5zY29yZXNfcmFpbmZhbGwpID4zICkpDQpgYGANCkhlcmUgd2UgY2FuIHNlZSB3ZSBoYXZlIHN1Y2Vzc2Z1bGx5IHJlbW92ZWQgYWxsIG91dGxpZXJzIGZyb20gcmFpbmZhbGwgZGF0YQ0KDQojIwlUcmFuc2Zvcm0gDQoNCmBgYHtyfQ0KaGlzdChuZXdfZGF0YSRgUmFpbmZhbGwgQW1vdW50YCwgbWFpbiA9ICJIaXN0b2dyYW0gb2YgVGVtcGVyYXR1cmUgd2l0aCB2ZXJ0aWNhbCBtZWFuIGxpbmUiLCB4bGFiID0gIiBNZWFuIFJhaW5mYWxsIGluIChtbSkiKQ0KYWJsaW5lKHYgPSBtZWFuKG5ld19kYXRhJGBSYWluZmFsbCBBbW91bnRgKSwgY29sPSJyZWQiLCBsd2Q9MywgbHR5PTIpDQpoaXN0KG5ld19kYXRhJGBNZWFuIG1heGltdW0gdGVtcGVyYXR1cmVgLCBtYWluID0gIkhpc3RvZ3JhbSBvZiBUZW1wZXJhdHVyZSB3aXRoIHZlcnRpY2FsIG1lYW4gbGluZSIsIHhsYWIgPSAiTWVhbiBUZW1wZXJhdHVyZSBpbiAoQykiKQ0KYWJsaW5lKHYgPSBtZWFuKG5ld19kYXRhJGBNZWFuIG1heGltdW0gdGVtcGVyYXR1cmVgKSwgY29sPSJyZWQiLCBsd2Q9MywgbHR5PTIpDQpgYGANCg0KLSBXZSBjYW4gc2VlIHRoYXQgYm90aCB0aGUgZGF0YSBhcmUgc2xpZ2h0bHkgcmlnaHQgc2tld2VkLiBIZW5jZSBhcHBseWluZyBCb3hjb3ggVHJhbnNmb3JtYXRpb24gaW4gb3JkZXIgdG8gbm9ybWFsaXplIHRoZSBkYXRhLg0KDQpgYGB7cn0NCmJveGNveF9yYWluZmFsbCA8LSBCb3hDb3gobmV3X2RhdGEkYFJhaW5mYWxsIEFtb3VudGAsIGxhbWJkYSA9ICJhdXRvIikNCmhpc3QoYm94Y294X3JhaW5mYWxsLCBtYWluPSJIaXN0b2dyYW0gb2YgUmFpbmZhbGwgZGF0YSBhZnRlciBUcmFuc2Zvcm1hdGlvbiIsIHhsYWIgPSAiIE1lYW4gUmFpbmZhbGwgaW4gKG1tKSIgKQ0KYWJsaW5lKHYgPSBtZWFuKGJveGNveF9yYWluZmFsbCksIGNvbD0iYmx1ZSIsIGx3ZD0zLCBsdHk9MikNCmJveGNveF90ZW1wZXJhdHVyZSA8LSBCb3hDb3gobmV3X2RhdGEkYE1lYW4gbWF4aW11bSB0ZW1wZXJhdHVyZWAsIGxhbWJkYSA9ICJhdXRvIikNCmhpc3QoYm94Y294X3RlbXBlcmF0dXJlLCBtYWluPSJIaXN0b2dyYW0gb2YgVGVtcGVyYXR1cmUgZGF0YSBhZnRlciBUcmFuc2Zvcm1hdGlvbiIsIHhsYWIgPSAiTWVhbiBUZW1wZXJhdHVyZSBpbiAoQykiKQ0KYWJsaW5lKHYgPSBtZWFuKGJveGNveF90ZW1wZXJhdHVyZSksIGNvbD0iYmx1ZSIsIGx3ZD0zLCBsdHk9MikNCmBgYA0KLSBIZXJlIGFmdGVyIHRyYW5zZm9ybWF0aW9uIHRocm91Z2ggdmlzdWFsIGluc3BlY3Rpb24gd2UgY2FuIHNlZSB0aGUgZGF0YSBpcyBpbiBub3JtYWxpemVkIGZvcm0gdXNpbmcgbWVhbiBsaW5lIGFzIHJlZmVyZW5jZS4NCg0KPGJyPg0KPGJyPg0K