IMPORTANT NOTE:

All the instructions to complete this assignment are available on the MATH2349_1810 Assignment_3 Word file. Please read through this document carefully before submitting your report.

You must use the headings and chunks provided in the template, you may add additional sections and R chunks if you require. In the report, all R chunks and outputs needs to be visible. Failure to do so will result in a loss of marks.

This report must be uploaded to Turnitin as a PDF with your code chunks and outputs showing. The easiest way to achieve this is to Preview your notebook in HTML (by clicking Preview) → Open in Browser (Chrome) → Right click on the report in Chrome Click Print and Select the Destination Option to Save as PDF.

You must also publish your report to RPubs (see here) and add this RPubs link to the comments/description section in Turnitin while uploading your report. This online version of the report will be used for marking. Failure to submit your link will delay your feedback and risk late penalties.

Feel free to DELETE the instructional text provided in the template. If you have any questions regarding the assignment instructions and the R template, please post it on Slack under the #assignment3 channel.

Required packages

Following packages were install and used in the assignment:

install.packages("readxl")
Installing package into <U+393C><U+3E31>C:/Users/Jamriska/Documents/R/win-library/3.4<U+393C><U+3E32>
(as <U+393C><U+3E31>lib<U+393C><U+3E32> is unspecified)
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.4/readxl_1.1.0.zip'
Content type 'application/zip' length 1488008 bytes (1.4 MB)
downloaded 1.4 MB
package ‘readxl’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\Jamriska\AppData\Local\Temp\RtmpsvBBKC\downloaded_packages
install.packages("readr")
Installing package into <U+393C><U+3E31>C:/Users/Jamriska/Documents/R/win-library/3.4<U+393C><U+3E32>
(as <U+393C><U+3E31>lib<U+393C><U+3E32> is unspecified)
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.4/readr_1.1.1.zip'
Content type 'application/zip' length 1254896 bytes (1.2 MB)
downloaded 1.2 MB
package ‘readr’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\Jamriska\AppData\Local\Temp\RtmpsvBBKC\downloaded_packages
install.packages("dplyr")
Error in install.packages : Updating loaded packages
install.packages("tidyr")
Installing package into <U+393C><U+3E31>C:/Users/Jamriska/Documents/R/win-library/3.4<U+393C><U+3E32>
(as <U+393C><U+3E31>lib<U+393C><U+3E32> is unspecified)
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.4/tidyr_0.8.1.zip'
Content type 'application/zip' length 899213 bytes (878 KB)
downloaded 878 KB
package ‘tidyr’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\Jamriska\AppData\Local\Temp\RtmpsvBBKC\downloaded_packages
install.packages("stringr")
Error in install.packages : Updating loaded packages
install.packages("Hmisc")
Installing package into <U+393C><U+3E31>C:/Users/Jamriska/Documents/R/win-library/3.4<U+393C><U+3E32>
(as <U+393C><U+3E31>lib<U+393C><U+3E32> is unspecified)
trying URL 'https://cran.rstudio.com/bin/windows/contrib/3.4/Hmisc_4.1-1.zip'
Content type 'application/zip' length 1817136 bytes (1.7 MB)
downloaded 1.7 MB
package ‘Hmisc’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\Jamriska\AppData\Local\Temp\RtmpsvBBKC\downloaded_packages
install.packages("forecast")

Restarting R session...

Executive Summary

The Air Quality has significant effect on public health as exposure to air pollutants is associated with cost in human lives, burden on health services, socio-economical effects and environmental impact. As such, there is a strong need to understand, control and mitigate air pollution. To achieve these objectives, various agencies have been tasked with monitoring and characterisation of air pollution and exposure to atmospheric pollutants. This is of interest also to a broader research community and public, in general.

Although availability and access to air quality data has been improving, the challenges remain, for example access and availability to historical data for a range of air pollutants monitored across different geographical locations is still limited. Often data is available, however may not be in a complete form or require further effort to retrieve, collate and pre-process. This was the aim of this assignment, i.e. demonstrate some of approaches to find, retrieve, collate and pre-process data in relation to Air Quality and Pollution levels in QLD.

In practical terms, to demonstrate the effort, I found and retrieved Air quality data from QLD Government Data repository for 2016. The hourly average data from different monitoring sites were collated, assessed, and modified applying data pre-processing practices. This included viewing, assessing the structure and class of data, tidying, changing type and format. Data were assessed for outliers, skewness and normality. Completeness of data was also assessed and where appropriate either removed or substituted. In general, the activities aim at preparing a good quality data which are readily available for further analysis and applications. The assignment does not attempt to provide complete and in-depth application of various means and techniques available for data pre-processing, rather it outlines some approaches and areas which can be explored in future applications.

(Word count Max: 300 words)

Data (Background)D

The air quality data used in the assignment were obtained from Queensland Government Data website (QGDW) (https://data.qld.gov.au/dataset/air-quality-monitoring-2016-grouped-by-pollutant). The side provides historical records of Air Quality Monitoring data for QLD. These include data for a broad range of air pollutants measured at different monitoring sites across the QLD. The QGDW also provides current air quality update, however review of historical data is limited. There is a need to access and review historical data for example health research application. This feature (retrieval and processing of historical data) is not directly available to a general public or is rather limited (e.g. Manual retrieval of data for individual days). The aim of this assignment is to develop and demonstrate a methodology allowing to obtain, collate and pre-process historical air quality which then can be used for further analysis.

To demonstrate the methodology, the assignment aims at reconstructing Air Quality Index (AQI) for six pollutants listed in the Air National Environment Protection Measure (Air NEPM). The Air NEPM is a legislative standard for Air Quality; specifying time averaged concentration and exposure standards for different air pollutants. The standard also presents the methodology for assessing Air quality levels, including calculation of the Air Quality Index. The methodology, which was used in this assignment, is available at (http://www.environment.gov.au/protection/air-quality/air-quality-standards).

The selected data are hourly averages of 5 air pollutants measured in 2016 at different air quality monitoring sites spread across the QLD. The focus here was to select, retrieve, collate and pre-process hourly averages of air pollutants concentration measured at South Brisbane air monitoring site. This information (collated data set) is not readily available. Following pollutants and information was collated:

  1. PM2.5 Particulate Matter smaller than 2.5micron, variable: pol1; NEPM standard 25 [ug m^-3] (24h average)
  2. PM10 Particulate Matter smaller than 10micron, variable: pol2; NEPM standard 50 [ug m^-3] (24h average)
  3. CO Carbon Monoxide, variable pol3; NEPM standard 9 [ppm] (8h average)
  4. NO2 Nitrogen dioxide, variable pol4; NEPM standard 0.120 [ppm] (1h average)
  5. O3 Ozone, variable pol5; NEPM standard 0.100 [ppm] (1h average)
  6. Bsp Visibility, variable pol6; NEPM standard 210 [1/Mm] (8h average)

The Air Quality index for each pollutant is calculated as: AQI-x = (pol-x/Standard-x)*100 in [%] where pol-x is an average concentration of pollutant x averaged over 1hour (e.g. 1pm-2pm average ->pol-x record at 2pm)

Data Download from INTERNET

Following are the URL addresses from where the air quality files in csv format were obtained. Due to a technical problem with direct downloading these files from the URL and time constrains the files were first downloaded into a local directory and then loaded into RStudio. The section below provides URL and the (commented) scripts which were originally used.

# PM2.5 
#url_PM2.5 <- "https://data.qld.gov.au/dataset/7844efd9-5116-4dba-8459-c79703d82b39/resource/b5cde0fa-1063-41af-a01d-64e46a221985/download/pm2-5qld2016.csv"
# PM2.5_data_all_sites<-read.csv(url_PM2.5,stringsAsFactors = FALSE)
# PM10 
#url_PM10<- "https://data.qld.gov.au/dataset/7844efd9-5116-4dba-8459-c79703d82b39/resource/ab620383-de88-4a8a-86ac-ed388beb3554/download/pm10qld2016.csv"
#PM10_data_all_sites<-read.csv(url_PM10,stringsAsFactors = FALSE)
# Carbon Monoxide
#url_CO <-"https://data.qld.gov.au/dataset/7844efd9-5116-4dba-8459-c79703d82b39/resource/8b6d6550-2941-427c-b1af-181e191c7abb/download/carbonmonoxideqld2016.csv"
#CO_data_all_sites <- read.csv(url_CO, stringsAsFactors = FALSE)
# Nitrogen dioxode
#url_NO2 <- "https://data.qld.gov.au/dataset/7844efd9-5116-4dba-8459-c79703d82b39/resource/e28cb1f2-b262-4723-8261-babe407f9b20/download/nitrogendioxideqld2016.csv"
#NO2_data_all_sites <- read.csv(url_NO2, stringsAsFactors = FALSE)
# Ozone
#url_O3<- "https://data.qld.gov.au/dataset/7844efd9-5116-4dba-8459-c79703d82b39/resource/040293d8-f98b-4748-9253-e50282c14cb5/download/ozoneqld2016.csv"
#O3_data_all_sites <- read.csv(url_O3, stringsAsFactors = FALSE)
#url_visibility <- "https://data.qld.gov.au/dataset/7844efd9-5116-4dba-8459-c79703d82b39/resource/0b23e381-da8b-4ad1-8099-f8ab0688e2e7/download/visibilityreducingparticlesqld2016.csv"
#Visibility_data_all_sites <- read.csv(url_visibility, stringsAsFactors = FALSE)

Data Download from Working Directory

##  READ DATA for EACH POLLUTANT data files from working directory (Files Already Downloaded from the Internet
getwd()
[1] "C:/Users/Jamriska/Documents/RMIT_COURSE Materials/RMIT-2018-S1/MATH2349_Course/Assignments/Assign_3"
setwd("C:/Users/Jamriska/Documents/RMIT_COURSE Materials/RMIT-2018-S1/MATH2349_Course/Data")
The working directory was changed to C:/Users/Jamriska/Documents/RMIT_COURSE Materials/RMIT-2018-S1/MATH2349_Course/Data inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
# PM2.5 (Particulate Matter Smaller than 2.5micron, pol1)
PM2.5.file<-"pm2-5qld2016.csv"        # Name of the PM2.5 date file on local directory 
PM2.5_data_all_sites<-read.csv(PM2.5.file,stringsAsFactors = FALSE)  # load the file into RStudio
head(PM2.5_data_all_sites)
#str(PM2.5_data_all_sites)
# PM10 (Particulate Matter Smaller than 10micron, pol2)
PM10.file<-"pm10qld2016.csv"
PM10_data_all_sites<- read.csv(PM10.file,stringsAsFactors = FALSE)
head(PM10_data_all_sites)
#str(PM10_data_all_sites)
# CO (Carbon Monoxide CO, pol3)
CO.file<- "carbonmonoxideqld2016.csv"
CO_data_all_sites<- read.csv(CO.file,stringsAsFactors = FALSE)
head(CO_data_all_sites)
#str(CO_data_all_sites)
# NO2 (Nitrogen dioxide, pol4)
NO2.file<- "nitrogendioxideqld2016.csv"
NO2_data_all_sites<- read.csv(NO2.file,stringsAsFactors = FALSE)
head(NO2_data_all_sites)
#str(NO2_data_all_sites)
# O3 (Ozone, pol5)
O3.file <- "ozoneqld2016.csv"
O3_data_all_sites<- read.csv(O3.file,stringsAsFactors = FALSE)
head(O3_data_all_sites)
#str(O3_data_all_sites)
# Visibility (Bsp, pol6)
Visibility.file<-"visibilityreducingparticlesqld2016.csv"
Vis_data_all_sites<- read.csv(Visibility.file,stringsAsFactors = FALSE)
head(Vis_data_all_sites)
#str(Vis_data_all_sites)
# --------------- ALL DATA DOWNLOADED  -------------------------------------------------------------

Understand

To gain understanding of data structure, types of variables, dimensions, etc. each file was explored using View(file), str(file), class(file), colnames(file) and other functions was applied. Since each file contains data for several monitoring sites, only data for South Brisbane (site of interest) was selected and then column renamed, indicating the pollutant name and units. The approach involved following steps: 1. A dataframe pol1, pol2, ..pol6 was created by assigning loaded csv files. 2. Checked names of Variables in each file (these represent monitoring site where the pol was measured) 3. Selected Date, Time and South Brisbane (or nearest) monitoring site 4. Checked which column contains the South Brisbane Data 5. Renamed selected column with a pollutant conc. reading (e.g. colnames(pol1)[3] <- “PM2.5_(ug/m3)”)

Summarise the types of variables and data structures, check the attributes in the data. In addition to the R codes and outputs, explain briefly the steps that you have taken. In this section, show that you have fulfilled minimum requirements 2-4.

# 1. Particulate Matter smaller than 2.5micron PM2.5 data denoted as pol1
pol1<-PM2.5_data_all_sites         # assign name for pollutants file subset 
#View(pol1)                        # view and inspect data, select column with South Brisb data 
colnames(pol1)                     # list names of the variables in pol1 
 [1] "Date"                                            "Time"                                           
 [3] "Cannon.Hill..South.East.Queensland...ug.m.3."    "Luscombe..South.East.Queensland...ug.m.3."      
 [5] "Lytton..South.East.Queensland...ug.m.3."         "Rocklea..South.East.Queensland...ug.m.3."       
 [7] "South.Brisbane..South.East.Queensland...ug.m.3." "Springwood..South.East.Queensland...ug.m.3."    
 [9] "Wynnum.North..South.East.Queensland...ug.m.3."   "Wynnum.West..South.East.Queensland...ug.m.3."   
[11] "Jondaryan..Jondaryan...ug.m.3."                  "Boat.Creek..Gladstone...ug.m.3."                
[13] "Boyne.Island..Gladstone...ug.m.3."               "Clinton..Gladstone...ug.m.3."                   
[15] "Fisherman.s.Landing..Gladstone...ug.m.3."        "South.Gladstone..Gladstone...ug.m.3."           
[17] "Targinie..Gladstone...ug.m.3."                  
pol1 <-pol1  %>%                     # Select columns Date, Time and South Brisb for pol1
  dplyr::select(contains("Date"),contains("Time"),contains("South.Brisb"))
grep(colnames(pol1)[3], colnames(PM2.5_data_all_sites ))  # find which column was selected (i.e., with pol1 conc. data)
[1] 7
colnames(pol1)[3] <- "PM2.5_(ug/m3)"    # change name of 3rd column (with pol1 conc) to PM2.5_(ug/m3)
# 2. Particulate Matter smaller than 10micron PM10 data denoted as pol2
pol2<-PM10_data_all_sites          # assign name for pollutants file subset 
pol2 <-pol2 %>% 
  dplyr::select(contains("Date"),contains("time"),contains("South.Brisb"))
colnames(pol2)[3] <- "PM10_(ug/m3)"    # change the name of column
# 3. Carbon monoxide CO data denoted as pol3
pol3 <- CO_data_all_sites         # assign name for pollutants file subset 
pol3 <-pol3 %>% 
  dplyr::select(contains("Date"),contains("time"),contains("South.Brisb"))
colnames(pol3)[3] <- "CO_(ppm)"    # change the name of column
# 4. Nitrogen dioxide NO2 data denoted as pol4
pol4 <- NO2_data_all_sites         # assign name for pollutants file subset 
pol4 <-pol4 %>% 
  dplyr::select(contains("Date"),contains("time"),contains("South.Brisb"))
colnames(pol4)[3] <- "NO2_(ppm)"    # change the name of column
# 5. oZONE O3 data denoted as pol5
pol5 <- O3_data_all_sites         # assign name for pollutants file subset 
colnames(pol5)                    # South Brisbane data unavailable, the closest monitoring station is "Rocklea" 
 [1] "Date"                                         "Time"                                        
 [3] "Deception.Bay..South.East.Queensland...ppm."  "Flinders.View..South.East.Queensland...ppm." 
 [5] "Mountain.Creek..South.East.Queensland...ppm." "Mutdapilly..South.East.Queensland...ppm."    
 [7] "North.Maclean..South.East.Queensland...ppm."  "Rocklea..South.East.Queensland...ppm."       
 [9] "Springwood..South.East.Queensland...ppm."     "Memorial.Park..Gladstone...ppm."             
[11] "Pimlico..Townsville...ppm."                  
pol5 <-pol5 %>% 
  dplyr::select(contains("Date"),contains("time"),contains("Rocklea"))  #
colnames(pol5)[3] <- "O3_(ppm)"    # change the name of column

Next Step is to review the selected files pol1, pol2, ….pol6 in terms of the variable type and files structure

Tidy & Manipulate Data I

Check if the data conforms the tidy data principles. If your data is not in a tidy format, reshape your data into a tidy format (minimum requirement #5). In addition to the R codes and outputs, explain everything that you do in this step.

cat("# 1. Particulate Matter smaller than 2.5micron PM2.5 data denoted as pol1 \n")
# 1. Particulate Matter smaller than 2.5micron PM2.5 data denoted as pol1 
str(pol1)
'data.frame':   8784 obs. of  3 variables:
 $ Date         : chr  "01/01/2016" "01/01/2016" "01/01/2016" "01/01/2016" ...
 $ Time         : chr  "00:00" "01:00" "02:00" "03:00" ...
 $ PM2.5_(ug/m3): num  2.4 7.1 9.7 7.3 4 0.8 1.9 2.2 3.4 4.8 ...
cat("# 2. Particulate Matter smaller than 10micron PM10 data denoted as pol2 \n ")
# 2. Particulate Matter smaller than 10micron PM10 data denoted as pol2 
 
str(pol2)
'data.frame':   8784 obs. of  3 variables:
 $ Date        : chr  "01/01/2016" "01/01/2016" "01/01/2016" "01/01/2016" ...
 $ Time        : chr  "00:00" "01:00" "02:00" "03:00" ...
 $ PM10_(ug/m3): num  12.9 16.8 18.4 15 12.1 9.5 11.2 12.3 14.6 16.3 ...
cat("# 3. Carbon monoxide CO data denoted as pol3 \n ")
# 3. Carbon monoxide CO data denoted as pol3 
 
str(pol3)
'data.frame':   8784 obs. of  3 variables:
 $ Date    : chr  "01/01/2016" "01/01/2016" "01/01/2016" "01/01/2016" ...
 $ Time    : chr  "00:00" "01:00" "02:00" "03:00" ...
 $ CO_(ppm): num  NA 0.2 0.1 0.1 0.1 0.1 0.1 0 0 0.1 ...
cat("# 4. Nitrogen dioxide NO2 data denoted as pol4 \n ")
# 4. Nitrogen dioxide NO2 data denoted as pol4 
 
str(pol4)
'data.frame':   8784 obs. of  3 variables:
 $ Date     : chr  "01/01/2016" "01/01/2016" "01/01/2016" "01/01/2016" ...
 $ Time     : chr  "00:00" "01:00" "02:00" "03:00" ...
 $ NO2_(ppm): num  NA 0.012 0.008 0.006 0.004 0.005 0.004 0.002 0.004 0.006 ...
cat("# 5. oZONE O3 data denoted as pol5 \n ")
# 5. oZONE O3 data denoted as pol5 
 
str(pol5)
'data.frame':   8784 obs. of  3 variables:
 $ Date    : chr  "01/01/2016" "01/01/2016" "01/01/2016" "01/01/2016" ...
 $ Time    : chr  "00:00" "01:00" "02:00" "03:00" ...
 $ O3_(ppm): num  NA 0.008 0.011 0.012 0.007 0.01 0.013 0.016 0.018 0.019 ...
cat("# 6. Visibility data denoted as pol6 \n ")
# 6. Visibility data denoted as pol6 
 
str(pol6)
'data.frame':   8784 obs. of  3 variables:
 $ Date      : chr  "01/01/2016" "01/01/2016" "01/01/2016" "01/01/2016" ...
 $ Time      : chr  "00:00" "01:00" "02:00" "03:00" ...
 $ Vis_(1/Mm): int  NA 21 22 19 18 18 18 14 13 12 ...

** SUMMARY ** The results above (output of the str(file) command) for all dataframes pol1, pol2, …pol6, show that the variables Date and Time for each data frame pol1, pol2, …, pol6 are present and are character type.The 3rd variable (e.g., PM2.5, PM10, etc) is numeric type. The variables Date and Time are common variables for each df and therefore can be used for df joining.

Tidy & Manipulate Data II

In here Create/mutate at least one variable from the existing variables (minimum requirement #6). In addition to the R codes and outputs, explain everything that you do in this step..

Joining individual dataframes

Since the concnetration data are in separate dataframes, they need to be joined. As indicated above, the variables Date and Time (char type) are present in each df and can be used for joining. I used left join function, which allowed to join each of the pollutants pol1 to pol6 as an individual column. The date and time variables are first two columns. The script is presented below.

# JOINING dataframes for ALL 6 POLLUTANT INTO ONE DATAFRAME
# Joining by Date and Time Variables (they are still character type)
pol1_6 <- pol1 %>% left_join(pol2) %>% 
                      left_join(pol3) %>% 
                        left_join(pol4) %>% 
                          left_join(pol5) %>% 
                             left_join(pol6)
Joining, by = c("Date", "Time")
Joining, by = c("Date", "Time")
Joining, by = c("Date", "Time")
Joining, by = c("Date", "Time")
Joining, by = c("Date", "Time")
class(pol1_6)
[1] "data.frame"
str(pol1_6)
'data.frame':   8784 obs. of  8 variables:
 $ Date         : chr  "01/01/2016" "01/01/2016" "01/01/2016" "01/01/2016" ...
 $ Time         : chr  "00:00" "01:00" "02:00" "03:00" ...
 $ PM2.5_(ug/m3): num  2.4 7.1 9.7 7.3 4 0.8 1.9 2.2 3.4 4.8 ...
 $ PM10_(ug/m3) : num  12.9 16.8 18.4 15 12.1 9.5 11.2 12.3 14.6 16.3 ...
 $ CO_(ppm)     : num  NA 0.2 0.1 0.1 0.1 0.1 0.1 0 0 0.1 ...
 $ NO2_(ppm)    : num  NA 0.012 0.008 0.006 0.004 0.005 0.004 0.002 0.004 0.006 ...
 $ O3_(ppm)     : num  NA 0.008 0.011 0.012 0.007 0.01 0.013 0.016 0.018 0.019 ...
 $ Vis_(1/Mm)   : int  NA 21 22 19 18 18 18 14 13 12 ...
head(pol1_6)

** Summary ** The resulting data frame has 8784 observations (raws), contains 8 variables. The variables Date and Time are character type; variables 3-6 are numeric. The created dataframe is in tidy format, as each column represent a unique variable, each observation (raw) is also unique, and each value (cell reading) is unique also. Hence it meets the criterion of bein in tidy format. The dataframe can be improved by changing the type of variable Date and Time (character type) to Date/Time format. This will be done later. Also these two variables can be merged into dd-mm-yyy hh:mm:ss format which allows easier manipulation.

In the next step I checked the pol1-6 df for the presence of missing values (NA), inconsistencies (eg. negative values for pollutants concentration) and other issues.

Scan I

Scan the data for missing values, inconsistencies and obvious errors. In this step, you should fulfil the minimum requirement #7. In addition to the R codes and outputs, explain how you dealt with these values.

CHECKING DATA FOR MISSING VALUES (in pol1_6 dataframe)

# is.na(pol1_6) # check if there are NAS
nu_Total_NA <-sum(is.na(pol1_6)) # count the number of NA
nu_Total_non_NA <-sum(!is.na(pol1_6)) # number of non- NA values
nu_Total <- (nu_Total_NA + nu_Total_non_NA)
(fract_NA <-nu_Total_NA/nu_Total)    # FRaction of NA data in total
[1] 0.05510018

** SUMMARY** About 5.5% of total data from merged file is NA

CHECKING FOR SPECIAL VALUES (in pol1_6 dataframe)

is.na(dat1) <- do.call(cbind,lapply(dat1, is.infinite))
sum(do.call(cbind,lapply(dat1, is.infinite)))
[1] 0
sum(do.call(cbind,lapply(dat1, is.na)))
[1] 3872
sum(do.call(cbind,lapply(dat1, is.nan)))
[1] 0

** SUMMARY** The results from chunk above show that 1. Number of NA in pol1_6 is 3872; 2. number of nan is zero; and 3. Number of infinite values in pol1_6 is zerp.

Checking for INCONSISTENCES (VALIDITY)

This includes checking if any concentration values in pol1_6 dataframe are infinite, negative or otherwise invalid (Note. pol1_6 concentratio n values could not be negative!) The folloiwng r script identifies NEGATIVE VALUES which are then REPLACED with NA values (using library(dplyr) and assigned to dat2 dataframe (i.e., dat2 is pol1_6 with negative values being replced by NA)

dat2 <- dat1 %>% 
   mutate_all(funs(replace(., .<0, NA)))
(nu_NA_in_dat1 <-sum(do.call(cbind,lapply(dat1, is.na)))) # number of NA in dat1
[1] 3872
(nu_NA_in_dat2 <-sum(do.call(cbind,lapply(dat2, is.na)))) # number of NA in dat2
[1] 4044
(nu_Neg_Values_in_dat1 <- (nu_NA_in_dat2-nu_NA_in_dat1))
[1] 172

** SUMMARY** Number of NA in the original dat1 (i.e., pol1_6 dataframe) is 3872. Number of negative values in dat2 was 172. The new dat3 dataframe (Neg values replaced by NA) has in total 4044 values of NA.

REPLACE NEGATIVE Values in pol1_6 dataframe with MEDIAN values

Following script identifies NA values in dat2, which are then REPLACED with MEDIAN values. The modified dataset is then assigned to dat3 dataframe (i.e., dat3 is the original pol1_6 dataset with All NA and Negative Values Being Replced by Median value for each pollutant respectively (e.g., all missing, NA and negative values for CO pollutant are replaced by the median value of CO data measured in 2016).

dat3 <- data.frame(lapply(dat2,function(x) {
  if(is.numeric(x)) ifelse(is.na(x),median(x,na.rm=T),x) else x}))
# Count of na in dat3
(sum(do.call(cbind,lapply(dat3, is.na))))
[1] 0

** SUMMARY** All missing, NA and negative values in dat3 have been removed.

Number of INCOMPLETE CASES

Assessment of data completenes for the joined dataframe. Often the data may not be available for different monitoring sites and/or type of pollutanmt. This may play a role when calculting AQI_Overall which requires availability of data for each pollutant (for a given tdate/time).

** SUMMARY** Analysis of complete cases shows that the fraction of complete cases for dat1, dat2 and dat3 is about 1. 75.15% for dat1 (no data treatment applied, number of obesrvation 6602); 2. 73.7% for dat2 (negative values replaced with NA; number of obesrvation 6476), and 3. 100% for dat3 (NA and negative values replaced by Median, number of obesrvation 6784)

** NOTE:** All further data preprocessing, processing and manipulation will be done using dat3 dataframe (i.e., NA and negative values replaced with median value for each pollutant)

Check for types of variables and where required apply conversion

str(dat3)
'data.frame':   8784 obs. of  8 variables:
 $ Date         : chr  "01/01/2016" "01/01/2016" "01/01/2016" "01/01/2016" ...
 $ Time         : chr  "00:00" "01:00" "02:00" "03:00" ...
 $ PM2.5_.ug.m3.: num  2.4 7.1 9.7 7.3 4 0.8 1.9 2.2 3.4 4.8 ...
 $ PM10_.ug.m3. : num  12.9 16.8 18.4 15 12.1 9.5 11.2 12.3 14.6 16.3 ...
 $ CO_.ppm.     : num  0 0.2 0.1 0.1 0.1 0.1 0.1 0 0 0.1 ...
 $ NO2_.ppm.    : num  0.013 0.012 0.008 0.006 0.004 0.005 0.004 0.002 0.004 0.006 ...
 $ O3_.ppm.     : num  0.017 0.008 0.011 0.012 0.007 0.01 0.013 0.016 0.018 0.019 ...
 $ Vis_.1.Mm.   : num  15 21 22 19 18 18 18 14 13 12 ...

** SUMMARY** As during the process above the type of variables Date and Time was chnged from char to Factor; teh following script converses the type to character.

CHANGE TYPE of variable Date (Factor-> character) and variable Time (Factor -> character)

#library(dplyr)
dat3 %>% 
  dplyr::mutate_if(is.factor, as.character) -> dat3
str(dat3)
'data.frame':   8784 obs. of  8 variables:
 $ Date         : chr  "01/01/2016" "01/01/2016" "01/01/2016" "01/01/2016" ...
 $ Time         : chr  "00:00" "01:00" "02:00" "03:00" ...
 $ PM2.5_.ug.m3.: num  2.4 7.1 9.7 7.3 4 0.8 1.9 2.2 3.4 4.8 ...
 $ PM10_.ug.m3. : num  12.9 16.8 18.4 15 12.1 9.5 11.2 12.3 14.6 16.3 ...
 $ CO_.ppm.     : num  0 0.2 0.1 0.1 0.1 0.1 0.1 0 0 0.1 ...
 $ NO2_.ppm.    : num  0.013 0.012 0.008 0.006 0.004 0.005 0.004 0.002 0.004 0.006 ...
 $ O3_.ppm.     : num  0.017 0.008 0.011 0.012 0.007 0.01 0.013 0.016 0.018 0.019 ...
 $ Vis_.1.Mm.   : num  15 21 22 19 18 18 18 14 13 12 ...

** SUMMARY** The type of variables Date and Time was sucessfully changed to character.

CREATE NEW VARIABLE Date_Time (character type)

New variable Date_Time was created combining the Date and Time variables. The variable was created using paste function.

dat4<-dat3    # dat3 was assigned to dat4 dataframe as the working dataset to keep dat3 unchanged
# Note: dat4 contains data from pol1_6 with NA and negative values replaced with Median value for each pol1-6
# Create a new variable using paste function
dat4$Date_Time <- paste(dat4$Date, dat4$Time) # var still character type, in a format e.g.;"19/02/2016 06:00"
#str(dat3)
str(dat4)
'data.frame':   8784 obs. of  9 variables:
 $ Date         : chr  "01/01/2016" "01/01/2016" "01/01/2016" "01/01/2016" ...
 $ Time         : chr  "00:00" "01:00" "02:00" "03:00" ...
 $ PM2.5_.ug.m3.: num  2.4 7.1 9.7 7.3 4 0.8 1.9 2.2 3.4 4.8 ...
 $ PM10_.ug.m3. : num  12.9 16.8 18.4 15 12.1 9.5 11.2 12.3 14.6 16.3 ...
 $ CO_.ppm.     : num  0 0.2 0.1 0.1 0.1 0.1 0.1 0 0 0.1 ...
 $ NO2_.ppm.    : num  0.013 0.012 0.008 0.006 0.004 0.005 0.004 0.002 0.004 0.006 ...
 $ O3_.ppm.     : num  0.017 0.008 0.011 0.012 0.007 0.01 0.013 0.016 0.018 0.019 ...
 $ Vis_.1.Mm.   : num  15 21 22 19 18 18 18 14 13 12 ...
 $ Date_Time    : chr  "01/01/2016 00:00" "01/01/2016 01:00" "01/01/2016 02:00" "01/01/2016 03:00" ...
#head(dat4)

REORDER COLUMNS in dat5

The order of variables in dat5 df was changed for easier processing and analysis.The order was changed to: 1.Date, 2.Time, 3.Date_Time, 4.pol1, …,9.pol6

str(dat5)
'data.frame':   8784 obs. of  9 variables:
 $ Date         : chr  "01/01/2016" "01/01/2016" "01/01/2016" "01/01/2016" ...
 $ Time         : chr  "00:00" "01:00" "02:00" "03:00" ...
 $ Date_Time    : chr  "01/01/2016 00:00" "01/01/2016 01:00" "01/01/2016 02:00" "01/01/2016 03:00" ...
 $ PM2.5_.ug.m3.: num  2.4 7.1 9.7 7.3 4 0.8 1.9 2.2 3.4 4.8 ...
 $ PM10_.ug.m3. : num  12.9 16.8 18.4 15 12.1 9.5 11.2 12.3 14.6 16.3 ...
 $ CO_.ppm.     : num  0 0.2 0.1 0.1 0.1 0.1 0.1 0 0 0.1 ...
 $ NO2_.ppm.    : num  0.013 0.012 0.008 0.006 0.004 0.005 0.004 0.002 0.004 0.006 ...
 $ O3_.ppm.     : num  0.017 0.008 0.011 0.012 0.007 0.01 0.013 0.016 0.018 0.019 ...
 $ Vis_.1.Mm.   : num  15 21 22 19 18 18 18 14 13 12 ...

CHANGE TYPE Variable Date_Time

Variable dat5$Date_Time was changed from char type to Date&Time type in Format of “2016-06-21 01:00:00”

#install.packages("lubridate")
library(lubridate)
dat5$Date_Time <- dmy_hm(dat4$Date_Time, tz = "Australia/Queensland")
str(dat5)
'data.frame':   8784 obs. of  9 variables:
 $ Date         : chr  "01/01/2016" "01/01/2016" "01/01/2016" "01/01/2016" ...
 $ Time         : chr  "00:00" "01:00" "02:00" "03:00" ...
 $ Date_Time    : POSIXct, format: "2016-01-01 00:00:00" "2016-01-01 01:00:00" "2016-01-01 02:00:00" ...
 $ PM2.5_.ug.m3.: num  2.4 7.1 9.7 7.3 4 0.8 1.9 2.2 3.4 4.8 ...
 $ PM10_.ug.m3. : num  12.9 16.8 18.4 15 12.1 9.5 11.2 12.3 14.6 16.3 ...
 $ CO_.ppm.     : num  0 0.2 0.1 0.1 0.1 0.1 0.1 0 0 0.1 ...
 $ NO2_.ppm.    : num  0.013 0.012 0.008 0.006 0.004 0.005 0.004 0.002 0.004 0.006 ...
 $ O3_.ppm.     : num  0.017 0.008 0.011 0.012 0.007 0.01 0.013 0.016 0.018 0.019 ...
 $ Vis_.1.Mm.   : num  15 21 22 19 18 18 18 14 13 12 ...
head(dat5)
#str(dat5$Date_Time)
#class(dat5$Date_Time)

CREATE NEW VARIABLES

New variables were created by extracting date/time attributes from variable dat5$Date_Time The new variables were: year, month, day, day_of_week, hour, min, sec

# library(lubridate)
dat5$Year   <-year(dat5$Date_Time)     # create new variable year
dat5$month  <-month(dat5$Date_Time)    # create new variable month
dat5$day    <-day(dat5$Date_Time)      # create new variable day
dat5$wday   <-wday(dat5$Date_Time, label = TRUE)   # create new variable week day (Factor Type)
dat5$hour   <-hour(dat5$Date_Time)     # create new variable hour
dat5$minute <-minute(dat5$Date_Time)   # create new variable minute
str(dat5)
'data.frame':   8784 obs. of  15 variables:
 $ Date         : chr  "01/01/2016" "01/01/2016" "01/01/2016" "01/01/2016" ...
 $ Time         : chr  "00:00" "01:00" "02:00" "03:00" ...
 $ Date_Time    : POSIXct, format: "2016-01-01 00:00:00" "2016-01-01 01:00:00" "2016-01-01 02:00:00" ...
 $ PM2.5_.ug.m3.: num  2.4 7.1 9.7 7.3 4 0.8 1.9 2.2 3.4 4.8 ...
 $ PM10_.ug.m3. : num  12.9 16.8 18.4 15 12.1 9.5 11.2 12.3 14.6 16.3 ...
 $ CO_.ppm.     : num  0 0.2 0.1 0.1 0.1 0.1 0.1 0 0 0.1 ...
 $ NO2_.ppm.    : num  0.013 0.012 0.008 0.006 0.004 0.005 0.004 0.002 0.004 0.006 ...
 $ O3_.ppm.     : num  0.017 0.008 0.011 0.012 0.007 0.01 0.013 0.016 0.018 0.019 ...
 $ Vis_.1.Mm.   : num  15 21 22 19 18 18 18 14 13 12 ...
 $ Year         : num  2016 2016 2016 2016 2016 ...
 $ month        : num  1 1 1 1 1 1 1 1 1 1 ...
 $ day          : int  1 1 1 1 1 1 1 1 1 1 ...
 $ wday         : Ord.factor w/ 7 levels "Sun"<"Mon"<"Tue"<..: 6 6 6 6 6 6 6 6 6 6 ...
 $ hour         : int  0 1 2 3 4 5 6 7 8 9 ...
 $ minute       : int  0 0 0 0 0 0 0 0 0 0 ...
head(dat5)

DROP Variables Date and Time

Variables Date and Time (char type) which are not required anymore were removed from the dat5 dataframe

dat5 <- subset(dat5, select = -c(Date, Time)) # Drop column (variable) Date and Time from dat4 dataframe
str(dat5)
'data.frame':   8784 obs. of  13 variables:
 $ Date_Time    : POSIXct, format: "2016-01-01 00:00:00" "2016-01-01 01:00:00" "2016-01-01 02:00:00" ...
 $ PM2.5_.ug.m3.: num  2.4 7.1 9.7 7.3 4 0.8 1.9 2.2 3.4 4.8 ...
 $ PM10_.ug.m3. : num  12.9 16.8 18.4 15 12.1 9.5 11.2 12.3 14.6 16.3 ...
 $ CO_.ppm.     : num  0 0.2 0.1 0.1 0.1 0.1 0.1 0 0 0.1 ...
 $ NO2_.ppm.    : num  0.013 0.012 0.008 0.006 0.004 0.005 0.004 0.002 0.004 0.006 ...
 $ O3_.ppm.     : num  0.017 0.008 0.011 0.012 0.007 0.01 0.013 0.016 0.018 0.019 ...
 $ Vis_.1.Mm.   : num  15 21 22 19 18 18 18 14 13 12 ...
 $ Year         : num  2016 2016 2016 2016 2016 ...
 $ month        : num  1 1 1 1 1 1 1 1 1 1 ...
 $ day          : int  1 1 1 1 1 1 1 1 1 1 ...
 $ wday         : Ord.factor w/ 7 levels "Sun"<"Mon"<"Tue"<..: 6 6 6 6 6 6 6 6 6 6 ...
 $ hour         : int  0 1 2 3 4 5 6 7 8 9 ...
 $ minute       : int  0 0 0 0 0 0 0 0 0 0 ...
head(dat5)

RE-ARRANGE the order of variables

Order of columns (variables) in dat5 dataframe was rearanged to make data viewing and handling easier

** SUMMARY** The dataframe dat5 is now in tidy form (in terms of columns-variables; raws-observation, and values = cells = record) and will be used for further evaluation.

Scan II

Scan the numeric data for outliers. In this step, you should fulfil the minimum requirement #8. In addition to the R codes and outputs, explain how you dealt with these values.

Check for OUTLIERS using boxplot method

Data frame dat5 was checked for the presence oof outliers. The approach started using method for detection of Unilateral outliers. As an example, below is a script detecting for PM2.5, PM10 outliers using boxplot and scater plot (looking at the relationship/dependencies between teh PM10 and PM2.5 variable).

par(mfrow=c(1,3))
boxplot(dat4$PM2.5_.ug.m3.)
boxplot(dat4$PM10_.ug.m3.)
plot(dat4$PM10_.ug.m3.,dat4$PM2.5_.ug.m3.)

** SUMMARY** The boxplots and scatter plot shows presnce of quate a number of outliers for PM2.5, PM10 variables. Similar results (not presented here) were observed for other pollutants, i.e., CO NO2, O3 and Visibility as well.

Check for OUTLIERS using zscores (individual variable)

library(outliers)
z.scores <- dat5$PM2.5_.ug.m3. %>% scores(type = "z") #Checking outliers for PM2.5 pollutant
z.scores %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
-1.7692 -0.6277 -0.1626  0.0000  0.4082  7.6170 

** SUMMARY** The z-scores method indicates presence of outliers for PM2.5 (abs(zscore)>3).Using the same approach, I checked zscores for other pollutants (one by one), or as presented below using for loop.

Check for OUTLIERS using zscores (all variables using for loop)

for(i in 8:13)
  {
  print("--------------",quote = FALSE)
  colnames(dat5[i])
  z.scores <- dat5[i] %>% scores(type = "z") #Checking outliers for PM2.5 pollutant
  print(z.scores %>% summary())
  a<-sum(abs(z.scores)>3)
  fract_outl <-a/dim(dat5)[1]
  #print(a/dim(dat5)[1])
  cat("Fraction of outliers:",fract_outl,"\n")
} 
[1] --------------
 PM2.5_.ug.m3.    
 Min.   :-1.7692  
 1st Qu.:-0.6277  
 Median :-0.1626  
 Mean   : 0.0000  
 3rd Qu.: 0.4082  
 Max.   : 7.6170  
Fraction of outliers: 0.01468579 
[1] --------------
  PM10_.ug.m3.    
 Min.   :-2.2170  
 1st Qu.:-0.6683  
 Median :-0.1102  
 Mean   : 0.0000  
 3rd Qu.: 0.5177  
 Max.   : 7.0893  
Fraction of outliers: 0.0120674 
[1] --------------
    CO_.ppm.      
 Min.   :-0.5587  
 1st Qu.:-0.5587  
 Median :-0.5587  
 Mean   : 0.0000  
 3rd Qu.: 0.2703  
 Max.   :10.2182  
Fraction of outliers: 0.01673497 
[1] --------------
   NO2_.ppm.      
 Min.   :-1.7025  
 1st Qu.:-0.7505  
 Median :-0.1555  
 Mean   : 0.0000  
 3rd Qu.: 0.5586  
 Max.   : 5.3187  
Fraction of outliers: 0.008652095 
[1] --------------
    O3_.ppm.       
 Min.   :-1.56025  
 1st Qu.:-0.74721  
 Median :-0.02451  
 Mean   : 0.00000  
 3rd Qu.: 0.69820  
 Max.   : 5.03442  
Fraction of outliers: 0.004439891 
[1] --------------
   Vis_.1.Mm.     
 Min.   :-1.4169  
 1st Qu.:-0.5212  
 Median :-0.1954  
 Mean   : 0.0000  
 3rd Qu.: 0.2117  
 Max.   :19.2670  
Fraction of outliers: 0.01650729 

** SUMMARY** The results indicates each pol1-pol6 variables has a small fraction of outliers, less than 2%“.

Additonal approaches to detection Outliers

Create a subset pol1, pol2, ..pol6 data from dat4 dataframe. The subset will be used for detection of outliers

pol1_6_names<- c("PM2.5_.ug.m3.","PM10_.ug.m3.","CO_.ppm.","NO2_.ppm.","O3_.ppm.","Vis_.1.Mm.")
#pol1_2_names<- c("PM2.5_.ug.m3.","PM10_.ug.m3.")
#str(dat5)
#colnames(dat4) checking the columns names
dat5_pol1_6 <-dat5 %>% 
  dplyr::select(pol1_6_names)
head(dat5_pol1_6)  # subset of the subset

Create a single boxplot with all pollutants

The approach allows checking and comparison of outliers for different pollutants

** SUMMARY** Outliers are clearly present for PM2.5, PM10 and Visibility, however for CO, NO2 and O3 the boxplot representation is two narrow. For this reason the data were normalised to allow better boxplot comparison.

Transform

Apply an appropriate transformation for at least one of the variables. In addition to the R codes and outputs, explain everything that you do in this step. In this step, you should fulfil the minimum requirement #9.

NORMALISATION of Variables

In here I applied normalisation for each of the pol1-pol6 variables in dat5 dataframe

SUMMARY Not suprisingly, the normalised data for pol1-pol6 also show presence of outliers for CO, NO2 and O3 concentration.

Cleaning Outliers

I also tried applying Multivariate Outlier Detection using Mahalanobis distance with QQ plots This was not straightfoward due the large number of datapoint (above the MD distance limit; my dataset has more than 5000 values, which is the MHD limit)and also the number of variables included (pol1-pol6). For an ilustration , an example of the code (commented) which can be used for smaller datasets is presented below:

Excluding Outliers

An attempt was done to clean the dataset dat5 by excluding data with zscore>3; however due time constrains this was not completed.

dat5_pol1_6_norm_clean<- dat5_pol1_6_norm[ - which(abs(z.scores) >3 )]
boxplot(dat5_pol1_6_norm_clean)

SUMMARY The cleaned dataset still have datapoints well beyond the outer fence. This woudl require more exploration.

Additional/Extra Work

Calculating Air Quality Index AIQ

** Note ** The National Air NEPM standards used to calculate the AQI are based on averaging time intervals presented below: Averaging period PM10 and PM2.5 is 1 day Averaging period CO is 8 hours Averaging period NO2, O3 and Visibility is 1 hour In here, for simplicity, all values were assigned as standard values for averaging 1 hour period

The Air NEPM standard Values

The Air NEPM standards for selected pollutants are set as follow:

pol1_standard =   25     # [ug/m3] PM2.5 (24 hour avg) National Air NEPM Standard
pol2_standard =   50     # [ug/m3] PM10  (24 hour avg) National Air NEPM Standard
pol3_standard =    9     # [ppm]   CO    ( 8 hour avg) National Air NEPM Standard
pol4_standard = 0.12     # [ppm]   NO2   ( 1 hour avg) National Air NEPM Standard
pol5_standard = 0.10     # [ppm]   O3    ( 1 hour avg) National Air NEPM Standard
pol6_standard = 210      # [Mm^-1]  B_sp Visibility ( 1 hour avg) NSW Standard

Calculation of AQI_pol

The AQI was calculated using Formula: AQI_pol = (pol_reading/pol_standard)*100. Below is a script used to calculate AQI for each of the selected pollutants:

Create a dataframe with AQI_po1, …, AQI_pol6

For further processing conc values for each pollutant were assigned to new variables (later merged into a df)

pol1_conc<-dat5[,8]   # pol1 PM2.5
pol2_conc<-dat5[,9]   # pol2 PM10
pol3_conc<-dat5[,10]  # pol3 CO
pol4_conc<-dat5[,11]  # pol4 NO2
pol5_conc<-dat5[,12]  # pol5 O3
pol6_conc<-dat5[,13]  # pol6 Visibility

Create a dataframe with pol1_conc, …, pol6_conc

Exploring Pollutants Concentration and AQI data

Assessing pol1-6 and AQI_pol1-6 data using summary function and plotting

summary(pol1_conc) # summary for pol1  PM2.5
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   5.400   7.600   8.369  10.300  44.400 
summary(AQI_pol1)  # summary for AQI_pol1
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00   21.60   30.40   33.48   41.20  177.60 
plot(pol1_conc)    # plot pol 1 conc

Assessment of the concnetrtaion and AQI data for pollutants pol2 -pol6

summary(pol2_conc) # summary for pol2 PM2.5
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00   11.10   15.10   15.89   19.60   66.70 
summary(AQI_pol2)  # summary for AQI_pol2
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00   22.20   30.20   31.78   39.20  133.40 
plot(pol2_conc)    # plot pol_2 conc

summary(pol3_conc) # summary for pol3 CO
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.0000  0.0000  0.0674  0.1000  1.3000 
summary(AQI_pol3)  # summary for AQI_pol3
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.0000  0.0000  0.0000  0.7488  1.1111 14.4444 
plot(pol3_conc)    # plot pol_3 conc

summary(pol4_conc) # summary for pol4 NO2
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.00800 0.01300 0.01431 0.01900 0.05900 
summary(AQI_pol4)  # summary for AQI_pol4
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   6.667  10.833  11.922  15.833  49.167 
plot(pol4_conc)    # plot pol_4 conc

summary(pol5_conc) # summary for pol5 O3
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
0.00000 0.00900 0.01700 0.01727 0.02500 0.07300 
summary(AQI_pol5)  # summary for AQI_pol5
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00    9.00   17.00   17.27   25.00   73.00 
plot(pol5_conc)    # plot pol_5 conc

summary(pol6_conc) # summary for pol6 Visibility
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    0.0    11.0    15.0    17.4    20.0   254.0 
summary(AQI_pol6)  # summary for AQI_pol6
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  0.000   5.238   7.143   8.286   9.524 120.952 
plot(pol6_conc)    # plot pol_6 conc

plot(AQI_pol6)     # plot AQI_pol6

** SUMMARY** The summary and scatter plots for pol1-pol6 indicates that the data is quite noisy. Some of the data (straight line as for pol6 above, data points withindex between 5000-7000) correspods to median values (used as replacement of the missing or negative data. This propbaly would need to be imnproved, perhasp by removeing the cases with NA, negatoive values.

Plotting Concentration for PM2.5, PM10 and CO

#par(mfrow=c(2, 3))
par(mfrow=c(3, 3))
for (i in 1:3) {
    plot(Conc_pol1_6[,i], main = paste0("conc_pol",i))
    hist(Conc_pol1_6[,i],main = paste0("conc_pol",i))
    boxplot(Conc_pol1_6[,i],main = paste0("conc_pol",i))
}

Plotting Concentration for NO2, O3 and Visibility

#par(mfrow=c(2, 3))
par(mfrow=c(3, 3))
for (i in 4:6) {
    plot(Conc_pol1_6[,i], main = paste0("conc_pol",i))
    hist(Conc_pol1_6[,i],main = paste0("conc_pol",i))
    boxplot(Conc_pol1_6[,i],main = paste0("conc_pol",i))
}

Plotting AQI for PM2.5, PM10 and CO

par(mfrow=c(1, 3))
for (i in 1:3) {
  plot(Conc_pol1_6[,i], main = paste0("conc_pol",i))
  hist(Conc_pol1_6[,i],main = paste0("conc_pol",i))
  boxplot(AQI_pol1_6[,i],main = paste0("AQI_pol",i))
}

Plotting AQI for NO2, O3 and Visibility

par(mfrow=c(1, 3))
for (i in 4:6) {
  plot(Conc_pol1_6[,i], main = paste0("conc_pol",i))
  hist(Conc_pol1_6[,i],main = paste0("conc_pol",i))
  boxplot(AQI_pol1_6[,i],main = paste0("AQI_pol",i))
}

Air Quality Levels

Create an air quality index by converting measured pollutant concentrations into index values. based on Air NEPM (pol 1-5) and NSW EPA standard (pol 6) See URL: https://www.qld.gov.au/environment/pollution/monitoring/air-monitoring/air-quality-index VERY GOOD <- 0-33 GOOD <- 34-66 FAIR <- 67-99 POOR <- 100-149 BAD <- >150

Ploting histograms for pol1-6 concentration levels

# define multiple graphs plotting
par(mfrow=c(2, 3))
hist(pol1_conc)    # pol1 PM2.5
hist(pol2_conc)    # pol2 PM10
hist(pol3_conc)    # pol3 CO
hist(pol4_conc)    # pol4 NO2
hist(pol5_conc)    # pol5 O3
hist(pol6_conc)    # pol6 Visibility

ploting histograms for AQI for pol1-6

par(mfrow=c(2, 3))
hist(AQI_pol1)    # pol1 PM2.5
hist(AQI_pol2)    # pol2 PM10
hist(AQI_pol3)    # pol3 CO
hist(AQI_pol4)    # pol4 NO2
hist(AQI_pol5)    # pol5 O3
hist(AQI_pol6)    # pol6 Visibility

Ploting boxplots for concentration and AQI for pol1-pol6

par(mfrow=c(1, 2))
boxplot(pol1_conc,pol2_conc,pol3_conc,pol4_conc,pol5_conc,pol6_conc)
boxplot(AQI_pol1,AQI_pol2,AQI_pol3,AQI_pol4,AQI_pol5,AQI_pol6)

Min-Max Normalisation method

Defined as: X - min(X))/(max(X) - min(X)) Define a normalization function

normalize <- function(x) {
     return ((x - min(x)) / (max(x) - min(x)))
}

Application of Min-Max Normalisation method for each poolutant

dat5_Norm <- as.data.frame(lapply(dat5[8:13], normalize))

Creating dataframe CONC_pol1_6 with variables pol1_conc, …, pol6_conc which will be used later

#CONC_pol1_6 <-data.frame(cbind(pol1_conc,pol2_conc,pol3_conc,pol4_conc,pol5_conc,pol6_conc))
#colnames(CONC_pol1_6)[1] <- "PM2.5_(ug/m3)"    # change the name of column
#colnames(CONC_pol1_6)[2] <- "PM10_(ug/m3))"    # change the name of column
#colnames(CONC_pol1_6)[3] <- "CO_(ppm)"         # change the name of column
#colnames(CONC_pol1_6)[4] <- "NO2_(ppm)"        # change the name of column
#colnames(CONC_pol1_6)[5] <- "O3_(ppm)"         # change the name of column
#colnames(CONC_pol1_6)[6] <- "Vis_(1/Mm)"       # change the name of column
#head(CONC_pol1_6)

Creating dat6 dataframe by binding dat5 with AQI_po1, …, AQI_pol6 variables

dat6 <-data.frame(cbind(dat5,AQI_pol1,AQI_pol2,AQI_pol3,AQI_pol4,AQI_pol5,AQI_pol6))
head(dat6)
colnames(dat6)
 [1] "Date_Time"     "Year"          "month"         "day"           "wday"          "hour"         
 [7] "minute"        "PM2.5_.ug.m3." "PM10_.ug.m3."  "CO_.ppm."      "NO2_.ppm."     "O3_.ppm."     
[13] "Vis_.1.Mm."    "AQI_pol1"      "AQI_pol2"      "AQI_pol3"      "AQI_pol4"      "AQI_pol5"     
[19] "AQI_pol6"     

Changing names of columns for AIQ pol1-6

AQI_pol1_6_names <-c("AQI_PM2.5","AQI_PM10","AQI_CO","AQI_NO2","AQI_O3","AQI_Vis")
for(i in 1:6){
  i_pol=i+13
  colnames(dat6)[i_pol] <- AQI_pol1_6_names[i]
  }
colnames(dat6)
 [1] "Date_Time"     "Year"          "month"         "day"           "wday"          "hour"         
 [7] "minute"        "PM2.5_.ug.m3." "PM10_.ug.m3."  "CO_.ppm."      "NO2_.ppm."     "O3_.ppm."     
[13] "Vis_.1.Mm."    "AQI_PM2.5"     "AQI_PM10"      "AQI_CO"        "AQI_NO2"       "AQI_O3"       
[19] "AQI_Vis"      

Air Quality Index Overall (AQI_Overall) - Calculation

Calculate AQI_Overall for South Brisbane (monitoring Site) defined as the max hourly value of AQI for each pollutant

AQI_Overall <-AQI_pol1_6[, "max"] <- apply(dat6[, 15:19], 1, max) # a single column - vector representing max of 6 observation for all rows for 
class(AQI_Overall)  # AIQ_Overall is numeric
[1] "numeric"
summary(AQI_Overall)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  2.857  24.600  30.833  33.494  40.000 133.400 

Assessing normality of AQI_Overall data

The data normality was assessed for original dataset (i.e.,no transformation applied) using histogram and qqplot

hist(AQI_Overall)

# the histogram for AIQ_Overall shows right skeweness.
qqnorm(AQI_Overall);qqline(AQI_Overall, col = 2)  # checking data normality using q-q plot

log10 transformation of AQI_Overall data

AQI_Overall data were transformed using log, ln and other function to see if these improve Normalility of tha data

natural ln transformation of AQI_Overall data

As abovem transformation using natural log function

ln_AQI_Overall <- log(AQI_Overall) # natural log of AQI_Overall
hist(ln_AQI_Overall)

qqnorm(ln_AQI_Overall);qqline(ln_AQI_Overall, col = 2)  # improved result-> closer to normal distribution

Application of q-q plots to asses AQI_Overall data normality

qqnorm(AQI_Overall);qqline(AQI_Overall, col = 2)  # not normal distribution

qqnorm(log_AQI_Overall);qqline(log_AQI_Overall, col = 2)  # improved result-> closer to normal distribution
Error in qqnorm(log_AQI_Overall) : object 'log_AQI_Overall' not found

SUMMARY There is not much difference between log10 and ln transformation applied to AQI_Overall variable

AQI_Overal data NORMALISATION - Centering

Mean centering

center_AQI_Overall <-scale(AQI_Overall, center = TRUE, scale = FALSE)
# center_AQI_Overall
hist(center_AQI_Overall)

qqnorm(center_AQI_Overall);qqline(center_AQI_Overall, col = 2)  # Did not improve normaility

# Other transformation, e.g, min-max, z Score Standardisation also tested, providing similar results (not-normal distribution)

CALCULATING AIR POLLUTION INDEX

Create an air quality index by converting measured pollutant concentrations into index values. based on Air NEPM (pol 1-5) and NSW EPA standard (pol 6) For details see: URL: https://www.qld.gov.au/environment/pollution/monitoring/air-monitoring/air-quality-index

Definition of the AQI Levels: VERY GOOD <- 0-33 GOOD <- 34-66 FAIR <- 67-99 POOR <- 100-149 BAD <- >150

Calculating AQI_Overall Pollution Levels

The AQI_Overall values were asigned to Air Quality Levels with cut-points (range) determined by Air NEPM Standard.

AQI_Overall_Levels <- cut(AQI_Overall, breaks=c(0,33,66,99,149,Inf), 
                    labels=c("VERY GOOD","GOOD","FAIR","POOR","BAD"))

Graphical Representation of Air Quality Index and Levels.

In some cases it may be required to present relationship betwen AQI_Overall and corresponding Air Quality Levels using simple graphical form as demonstrated in a simple graph using dataset prepared in this assignment.

par(mfrow=c(1, 1))
hist(AQI_Overall)

Add AQI_Overall and AQI_Levels to dataframe dat6 and assign it as dat7

dat7<-cbind(dat6,AQI_Overall, AQI_Overall_Levels)
colnames(dat6)
 [1] "Date_Time"     "Year"          "month"         "day"           "wday"          "hour"         
 [7] "minute"        "PM2.5_.ug.m3." "PM10_.ug.m3."  "CO_.ppm."      "NO2_.ppm."     "O3_.ppm."     
[13] "Vis_.1.Mm."    "AQI_PM2.5"     "AQI_PM10"      "AQI_CO"        "AQI_NO2"       "AQI_O3"       
[19] "AQI_Vis"      
colnames(dat7)
 [1] "Date_Time"          "Year"               "month"              "day"               
 [5] "wday"               "hour"               "minute"             "PM2.5_.ug.m3."     
 [9] "PM10_.ug.m3."       "CO_.ppm."           "NO2_.ppm."          "O3_.ppm."          
[13] "Vis_.1.Mm."         "AQI_PM2.5"          "AQI_PM10"           "AQI_CO"            
[17] "AQI_NO2"            "AQI_O3"             "AQI_Vis"            "AQI_Overall"       
[21] "AQI_Overall_Levels"
head(dat7)
plot(dat7$AQI_Overall,
     main = "Hourly Air Quality Index for Brisbane in 2016",
     xlab = "hourly index",
     ylab = "AQI [-]",
     xlim=c(0, 9000),
     ylim=c(0, 200)
     )
#minor.tick(nx = 4, ny=5, tick.ratio=0.3)
legend("topright", inset=.05, title="AQI Range (upper bounds)",
         c("VERY GOOD","GOOD","FAIR","BAD"), col=c("green", "blue", "yellow", "red"),
         lty=c(2,2,2,2), lwd=c(3, 3,3,3),
         cex = 0.77,
         horiz=TRUE)
abline(h=c(33,66,99,149), col=c("green", "blue", "yellow", "red"), lty=c(2,2,2,2), lwd=c(3, 3,3,3))

SUMMARY The graph above shows hourly averages for Air Quality Index (AQI_Overall) observed at South Brisbane over the entire year 2016. The results indicate that the AQI was at VERY GOOD or GOOD levels (i.e., majority of the values are located below the green and blue dashed lines), however there was also a significnat number of data with FAIR, and several with BAD AQI levels. These would be of particulate interest, for eaxmple from the source apportionment point of view or studying teh conditions when these events occur.

These type of data analysis and graphical representation is usefull for further assessment and actions in relation to Air Quality management.



LS0tDQp0aXRsZTogIk1BVEgyMzQ5IFNlbWVzdGVyIDEsIDIwMTgiDQphdXRob3I6ICdNaWxhbiBKYW1yaXNrYSwgU3R1ZGVudCBJRDogczM1NTExMDEnDQpzdWJ0aXRsZTogQXNzaWdubWVudCAzDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQotLS0NCg0KDQojIyBJTVBPUlRBTlQgTk9URTogDQoNCkFsbCB0aGUgaW5zdHJ1Y3Rpb25zIHRvIGNvbXBsZXRlIHRoaXMgYXNzaWdubWVudCBhcmUgYXZhaWxhYmxlIG9uIHRoZSBbTUFUSDIzNDlfMTgxMCBBc3NpZ25tZW50XzNdKGh0dHBzOi8vZHJpdmUuZ29vZ2xlLmNvbS9vcGVuP2lkPTFkZ0RnX3NELUc0WlhfTjM4WDAzWGJDUGhHRkZBcUFaNVpJNVFmTm9GTkQ0KSBXb3JkIGZpbGUuIFBsZWFzZSByZWFkIHRocm91Z2ggdGhpcyBkb2N1bWVudCBjYXJlZnVsbHkgYmVmb3JlIHN1Ym1pdHRpbmcgeW91ciByZXBvcnQuDQoNCllvdSBtdXN0IHVzZSB0aGUgaGVhZGluZ3MgYW5kIGNodW5rcyBwcm92aWRlZCBpbiB0aGUgdGVtcGxhdGUsIHlvdSBtYXkgYWRkIGFkZGl0aW9uYWwgc2VjdGlvbnMgYW5kIFIgY2h1bmtzIGlmIHlvdSByZXF1aXJlLiBJbiB0aGUgcmVwb3J0LCBhbGwgUiBjaHVua3MgYW5kIG91dHB1dHMgbmVlZHMgdG8gYmUgdmlzaWJsZS4gRmFpbHVyZSB0byBkbyBzbyB3aWxsIHJlc3VsdCBpbiBhIGxvc3Mgb2YgbWFya3MuIA0KDQpUaGlzIHJlcG9ydCBtdXN0IGJlIHVwbG9hZGVkIHRvIFR1cm5pdGluIGFzIGEgUERGIHdpdGggeW91ciBjb2RlIGNodW5rcyBhbmQgb3V0cHV0cyBzaG93aW5nLiBUaGUgZWFzaWVzdCB3YXkgdG8gYWNoaWV2ZSB0aGlzIGlzIHRvIFByZXZpZXcgeW91ciBub3RlYm9vayBpbiBIVE1MIChieSBjbGlja2luZyBQcmV2aWV3KSDihpIgT3BlbiBpbiBCcm93c2VyIChDaHJvbWUpIOKGkiBSaWdodCBjbGljayBvbiB0aGUgcmVwb3J0IGluIENocm9tZSBDbGljayBQcmludCBhbmQgU2VsZWN0IHRoZSBEZXN0aW5hdGlvbiBPcHRpb24gdG8gU2F2ZSBhcyBQREYuDQoNCllvdSBtdXN0IGFsc28gcHVibGlzaCB5b3VyIHJlcG9ydCB0byBSUHVicyAoc2VlIFtoZXJlXShodHRwczovL2FzdHJhbC10aGVvcnktMTU3NTEwLmFwcHNwb3QuY29tL3NlY3VyZWQvUkJvb3RjYW1wX0NvdXJzZV8wNC5odG1sI2NyZWF0aW5nX2FuX3JfbWFya2Rvd25fZG9jdW1lbnRfaW5fcl9zdHVkaW8pKSBhbmQgYWRkIHRoaXMgUlB1YnMgbGluayB0byB0aGUgY29tbWVudHMvZGVzY3JpcHRpb24gc2VjdGlvbiBpbiBUdXJuaXRpbiB3aGlsZSB1cGxvYWRpbmcgeW91ciByZXBvcnQuIFRoaXMgb25saW5lIHZlcnNpb24gb2YgdGhlIHJlcG9ydCB3aWxsIGJlIHVzZWQgZm9yIG1hcmtpbmcuIEZhaWx1cmUgdG8gc3VibWl0IHlvdXIgbGluayB3aWxsIGRlbGF5IHlvdXIgZmVlZGJhY2sgYW5kIHJpc2sgbGF0ZSBwZW5hbHRpZXMuDQoNCioqRmVlbCBmcmVlIHRvIERFTEVURSB0aGUgaW5zdHJ1Y3Rpb25hbCB0ZXh0IHByb3ZpZGVkIGluIHRoZSB0ZW1wbGF0ZS4qKiBJZiB5b3UgaGF2ZSBhbnkgcXVlc3Rpb25zIHJlZ2FyZGluZyB0aGUgYXNzaWdubWVudCBpbnN0cnVjdGlvbnMgYW5kIHRoZSBSIHRlbXBsYXRlLCBwbGVhc2UgcG9zdCBpdCBvbiBbU2xhY2tdKGh0dHBzOi8vbWF0aDIzNDkuc2xhY2suY29tKSB1bmRlciB0aGUgYCNhc3NpZ25tZW50M2AgY2hhbm5lbC4gDQoNCg0KIyMgUmVxdWlyZWQgcGFja2FnZXMgDQpGb2xsb3dpbmcgcGFja2FnZXMgd2VyZSBpbnN0YWxsIGFuZCB1c2VkIGluIHRoZSBhc3NpZ25tZW50Og0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikNCmluc3RhbGwucGFja2FnZXMoInJlYWRyIikNCmluc3RhbGwucGFja2FnZXMoImRwbHlyIikNCmluc3RhbGwucGFja2FnZXMoInRpZHlyIikNCmluc3RhbGwucGFja2FnZXMoInN0cmluZ3IiKQ0KaW5zdGFsbC5wYWNrYWdlcygiSG1pc2MiKQ0KaW5zdGFsbC5wYWNrYWdlcygiZm9yZWNhc3QiKQ0KaW5zdGFsbC5wYWNrYWdlcygiaW5mb3RoZW8iKQ0KaW5zdGFsbC5wYWNrYWdlcygiZm9yZWlnbiIpDQppbnN0YWxsLnBhY2thZ2VzKCJnZGF0YSIpDQppbnN0YWxsLnBhY2thZ2VzKCJydmVzdCIpDQppbnN0YWxsLnBhY2thZ2VzKCJkZWR1Y3RpdmUiKQ0KaW5zdGFsbC5wYWNrYWdlcygidmFsaWRhdGUiKQ0KaW5zdGFsbC5wYWNrYWdlcygibHVicmlkYXRlIikNCmluc3RhbGwucGFja2FnZXMoIm91dGxpZXJzIikNCmluc3RhbGwucGFja2FnZXMoIk1WTiIpDQppbnN0YWxsLnBhY2thZ2VzKCJNQVNTIikNCmluc3RhbGwucGFja2FnZXMoImNhcmV0IikNCmluc3RhbGwucGFja2FnZXMoIm1sciIpDQppbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQppbnN0YWxsLnBhY2thZ2VzKCJtYWdyaXR0ciIpDQojaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90IikNCiNpbnN0YWxsLnBhY2thZ2VzKCJ4bHN4IikgICMgaXNzdWVzIHdpdGggSmF2YSBzb2Z0d2FyZSwgdXNlIHRoZSByZWFkeGwgcGFja2FnZQ0KDQpsaWJyYXJ5KCJyZWFkeGwiKQ0KbGlicmFyeSgicmVhZHIiKQ0KbGlicmFyeSgiZHBseXIiKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkoSG1pc2MpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeShpbmZvdGhlbykNCmxpYnJhcnkoImZvcmVpZ24iKQ0KbGlicmFyeSgicnZlc3QiKQ0KbGlicmFyeSgiZGVkdWN0aXZlIikNCmxpYnJhcnkoInZhbGlkYXRlIikNCmxpYnJhcnkoImx1YnJpZGF0ZSIpDQpsaWJyYXJ5KCJvdXRsaWVycyIpDQpsaWJyYXJ5KCJNVk4iKQ0KbGlicmFyeSgiTUFTUyIpDQpsaWJyYXJ5KCJjYXJldCIpDQpsaWJyYXJ5KCJtbHIiKQ0KbGlicmFyeSgiZ2RhdGEiKQ0KbGlicmFyeSh4bHN4KQ0KbGlicmFyeShtYWdyaXR0cikNCiNsaWJyYXJ5KCJnZ3Bsb3QiKQ0KbGlicmFyeSgia25pdHIiKQ0KDQpgYGANCg0KIyMgRXhlY3V0aXZlIFN1bW1hcnkgDQpUaGUgQWlyIFF1YWxpdHkgaGFzIHNpZ25pZmljYW50IGVmZmVjdCBvbiBwdWJsaWMgaGVhbHRoIGFzIGV4cG9zdXJlIHRvIGFpciBwb2xsdXRhbnRzIGlzIGFzc29jaWF0ZWQgd2l0aCBjb3N0IGluIGh1bWFuIGxpdmVzLCBidXJkZW4gb24gaGVhbHRoIHNlcnZpY2VzLCBzb2Npby1lY29ub21pY2FsIGVmZmVjdHMgYW5kIGVudmlyb25tZW50YWwgaW1wYWN0LiBBcyBzdWNoLCB0aGVyZSBpcyBhIHN0cm9uZyBuZWVkIHRvIHVuZGVyc3RhbmQsIGNvbnRyb2wgYW5kIG1pdGlnYXRlIGFpciBwb2xsdXRpb24uIFRvIGFjaGlldmUgdGhlc2Ugb2JqZWN0aXZlcywgdmFyaW91cyBhZ2VuY2llcyBoYXZlIGJlZW4gdGFza2VkIHdpdGggbW9uaXRvcmluZyBhbmQgY2hhcmFjdGVyaXNhdGlvbiBvZiBhaXIgcG9sbHV0aW9uIGFuZCBleHBvc3VyZSB0byBhdG1vc3BoZXJpYyBwb2xsdXRhbnRzLiBUaGlzIGlzIG9mIGludGVyZXN0IGFsc28gdG8gYSBicm9hZGVyIHJlc2VhcmNoIGNvbW11bml0eSBhbmQgcHVibGljLCBpbiBnZW5lcmFsLiANCg0KQWx0aG91Z2ggYXZhaWxhYmlsaXR5IGFuZCBhY2Nlc3MgdG8gYWlyIHF1YWxpdHkgZGF0YSBoYXMgYmVlbiBpbXByb3ZpbmcsIHRoZSBjaGFsbGVuZ2VzIHJlbWFpbiwgZm9yIGV4YW1wbGUgYWNjZXNzIGFuZCBhdmFpbGFiaWxpdHkgdG8gaGlzdG9yaWNhbCBkYXRhIGZvciBhIHJhbmdlIG9mIGFpciBwb2xsdXRhbnRzIG1vbml0b3JlZCBhY3Jvc3MgZGlmZmVyZW50IGdlb2dyYXBoaWNhbCBsb2NhdGlvbnMgaXMgc3RpbGwgbGltaXRlZC4gT2Z0ZW4gZGF0YSBpcyBhdmFpbGFibGUsIGhvd2V2ZXIgbWF5IG5vdCBiZSBpbiBhIGNvbXBsZXRlIGZvcm0gb3IgcmVxdWlyZSBmdXJ0aGVyIGVmZm9ydCB0byByZXRyaWV2ZSwgY29sbGF0ZSBhbmQgcHJlLXByb2Nlc3MuIFRoaXMgd2FzIHRoZSBhaW0gb2YgdGhpcyBhc3NpZ25tZW50LCBpLmUuIGRlbW9uc3RyYXRlIHNvbWUgb2YgYXBwcm9hY2hlcyB0byBmaW5kLCByZXRyaWV2ZSwgY29sbGF0ZSBhbmQgcHJlLXByb2Nlc3MgZGF0YSBpbiByZWxhdGlvbiB0byBBaXIgUXVhbGl0eSBhbmQgUG9sbHV0aW9uIGxldmVscyBpbiBRTEQuIA0KDQpJbiBwcmFjdGljYWwgdGVybXMsIHRvIGRlbW9uc3RyYXRlIHRoZSBlZmZvcnQsIEkgZm91bmQgYW5kIHJldHJpZXZlZCBBaXIgcXVhbGl0eSBkYXRhIGZyb20gUUxEIEdvdmVybm1lbnQgRGF0YSByZXBvc2l0b3J5IGZvciAyMDE2LiBUaGUgaG91cmx5IGF2ZXJhZ2UgZGF0YSBmcm9tIGRpZmZlcmVudCBtb25pdG9yaW5nIHNpdGVzIHdlcmUgY29sbGF0ZWQsIGFzc2Vzc2VkLCBhbmQgbW9kaWZpZWQgYXBwbHlpbmcgZGF0YSBwcmUtcHJvY2Vzc2luZyBwcmFjdGljZXMuIFRoaXMgaW5jbHVkZWQgdmlld2luZywgYXNzZXNzaW5nIHRoZSBzdHJ1Y3R1cmUgYW5kIGNsYXNzIG9mIGRhdGEsIHRpZHlpbmcsIGNoYW5naW5nIHR5cGUgYW5kIGZvcm1hdC4gRGF0YSB3ZXJlIGFzc2Vzc2VkIGZvciBvdXRsaWVycywgc2tld25lc3MgYW5kIG5vcm1hbGl0eS4gQ29tcGxldGVuZXNzIG9mIGRhdGEgd2FzIGFsc28gYXNzZXNzZWQgYW5kIHdoZXJlIGFwcHJvcHJpYXRlIGVpdGhlciByZW1vdmVkIG9yIHN1YnN0aXR1dGVkLiBJbiBnZW5lcmFsLCB0aGUgYWN0aXZpdGllcyBhaW0gYXQgcHJlcGFyaW5nIGEgZ29vZCBxdWFsaXR5IGRhdGEgd2hpY2ggYXJlIHJlYWRpbHkgYXZhaWxhYmxlIGZvciBmdXJ0aGVyIGFuYWx5c2lzIGFuZCBhcHBsaWNhdGlvbnMuIFRoZSBhc3NpZ25tZW50IGRvZXMgbm90IGF0dGVtcHQgdG8gcHJvdmlkZSBjb21wbGV0ZSBhbmQgaW4tZGVwdGggYXBwbGljYXRpb24gb2YgdmFyaW91cyBtZWFucyBhbmQgdGVjaG5pcXVlcyBhdmFpbGFibGUgZm9yIGRhdGEgcHJlLXByb2Nlc3NpbmcsIHJhdGhlciBpdCBvdXRsaW5lcyBzb21lIGFwcHJvYWNoZXMgYW5kIGFyZWFzIHdoaWNoIGNhbiBiZSBleHBsb3JlZCBpbiBmdXR1cmUgYXBwbGljYXRpb25zLg0KIA0KKFdvcmQgY291bnQgTWF4OiAzMDAgd29yZHMpDQoNCg0KDQojIyBEYXRhIChCYWNrZ3JvdW5kKUQNClRoZSBhaXIgcXVhbGl0eSBkYXRhIHVzZWQgaW4gdGhlIGFzc2lnbm1lbnQgd2VyZSBvYnRhaW5lZCBmcm9tIFF1ZWVuc2xhbmQgR292ZXJubWVudCBEYXRhIHdlYnNpdGUgKFFHRFcpIChodHRwczovL2RhdGEucWxkLmdvdi5hdS9kYXRhc2V0L2Fpci1xdWFsaXR5LW1vbml0b3JpbmctMjAxNi1ncm91cGVkLWJ5LXBvbGx1dGFudCkuIFRoZSBzaWRlIHByb3ZpZGVzIGhpc3RvcmljYWwgcmVjb3JkcyBvZiBBaXIgUXVhbGl0eSBNb25pdG9yaW5nIGRhdGEgZm9yIFFMRC4gVGhlc2UgaW5jbHVkZSBkYXRhIGZvciBhIGJyb2FkIHJhbmdlIG9mIGFpciBwb2xsdXRhbnRzIG1lYXN1cmVkIGF0IGRpZmZlcmVudCBtb25pdG9yaW5nIHNpdGVzIGFjcm9zcyB0aGUgUUxELiBUaGUgUUdEVyBhbHNvIHByb3ZpZGVzIGN1cnJlbnQgYWlyIHF1YWxpdHkgdXBkYXRlLCBob3dldmVyIHJldmlldyBvZiBoaXN0b3JpY2FsIGRhdGEgaXMgbGltaXRlZC4gVGhlcmUgaXMgYSBuZWVkIHRvIGFjY2VzcyBhbmQgcmV2aWV3IGhpc3RvcmljYWwgZGF0YSBmb3IgZXhhbXBsZSBoZWFsdGggcmVzZWFyY2ggYXBwbGljYXRpb24uIFRoaXMgZmVhdHVyZSAocmV0cmlldmFsIGFuZCBwcm9jZXNzaW5nIG9mIGhpc3RvcmljYWwgZGF0YSkgaXMgbm90IGRpcmVjdGx5IGF2YWlsYWJsZSB0byBhIGdlbmVyYWwgcHVibGljIG9yIGlzIHJhdGhlciBsaW1pdGVkIChlLmcuIE1hbnVhbCByZXRyaWV2YWwgb2YgZGF0YSBmb3IgaW5kaXZpZHVhbCBkYXlzKS4gVGhlIGFpbSBvZiB0aGlzIGFzc2lnbm1lbnQgaXMgdG8gZGV2ZWxvcCBhbmQgZGVtb25zdHJhdGUgYSBtZXRob2RvbG9neSBhbGxvd2luZyB0byBvYnRhaW4sIGNvbGxhdGUgYW5kIHByZS1wcm9jZXNzIGhpc3RvcmljYWwgYWlyIHF1YWxpdHkgd2hpY2ggdGhlbiBjYW4gYmUgdXNlZCBmb3IgZnVydGhlciBhbmFseXNpcy4NCg0KVG8gZGVtb25zdHJhdGUgdGhlIG1ldGhvZG9sb2d5LCB0aGUgYXNzaWdubWVudCBhaW1zIGF0IHJlY29uc3RydWN0aW5nIEFpciBRdWFsaXR5IEluZGV4IChBUUkpIGZvciBzaXggcG9sbHV0YW50cyBsaXN0ZWQgaW4gdGhlIEFpciBOYXRpb25hbCBFbnZpcm9ubWVudCBQcm90ZWN0aW9uIE1lYXN1cmUgKEFpciBORVBNKS4gVGhlIEFpciBORVBNIGlzIGEgbGVnaXNsYXRpdmUgc3RhbmRhcmQgZm9yIEFpciBRdWFsaXR5OyBzcGVjaWZ5aW5nIHRpbWUgYXZlcmFnZWQgY29uY2VudHJhdGlvbiBhbmQgZXhwb3N1cmUgc3RhbmRhcmRzIGZvciBkaWZmZXJlbnQgYWlyIHBvbGx1dGFudHMuIFRoZSBzdGFuZGFyZCBhbHNvIHByZXNlbnRzIHRoZSBtZXRob2RvbG9neSBmb3IgYXNzZXNzaW5nIEFpciBxdWFsaXR5IGxldmVscywgaW5jbHVkaW5nIGNhbGN1bGF0aW9uIG9mIHRoZSBBaXIgUXVhbGl0eSBJbmRleC4gVGhlIG1ldGhvZG9sb2d5LCB3aGljaCB3YXMgdXNlZCBpbiB0aGlzIGFzc2lnbm1lbnQsIGlzIGF2YWlsYWJsZSBhdCAoaHR0cDovL3d3dy5lbnZpcm9ubWVudC5nb3YuYXUvcHJvdGVjdGlvbi9haXItcXVhbGl0eS9haXItcXVhbGl0eS1zdGFuZGFyZHMpLg0KDQpUaGUgc2VsZWN0ZWQgZGF0YSBhcmUgaG91cmx5IGF2ZXJhZ2VzIG9mIDUgYWlyIHBvbGx1dGFudHMgbWVhc3VyZWQgaW4gMjAxNiBhdCBkaWZmZXJlbnQgYWlyIHF1YWxpdHkgbW9uaXRvcmluZyBzaXRlcyBzcHJlYWQgYWNyb3NzIHRoZSBRTEQuIFRoZSBmb2N1cyBoZXJlIHdhcyB0byBzZWxlY3QsIHJldHJpZXZlLCBjb2xsYXRlIGFuZCBwcmUtcHJvY2VzcyBob3VybHkgYXZlcmFnZXMgb2YgYWlyIHBvbGx1dGFudHMgY29uY2VudHJhdGlvbiBtZWFzdXJlZCBhdCBTb3V0aCBCcmlzYmFuZSBhaXIgbW9uaXRvcmluZyBzaXRlLiBUaGlzIGluZm9ybWF0aW9uIChjb2xsYXRlZCBkYXRhIHNldCkgaXMgbm90IHJlYWRpbHkgYXZhaWxhYmxlLiBGb2xsb3dpbmcgcG9sbHV0YW50cyBhbmQgaW5mb3JtYXRpb24gd2FzIGNvbGxhdGVkOg0KDQoxLiBQTTIuNSBQYXJ0aWN1bGF0ZSBNYXR0ZXIgc21hbGxlciB0aGFuIDIuNW1pY3JvbiwgdmFyaWFibGU6IHBvbDE7IE5FUE0gc3RhbmRhcmQgMjUgW3VnIG1eLTNdICgyNGggYXZlcmFnZSkNCjIuIFBNMTAgUGFydGljdWxhdGUgTWF0dGVyIHNtYWxsZXIgdGhhbiAxMG1pY3JvbiwgdmFyaWFibGU6IHBvbDI7IE5FUE0gc3RhbmRhcmQgNTAgW3VnIG1eLTNdICgyNGggYXZlcmFnZSkNCjMuIENPICAgIENhcmJvbiBNb25veGlkZSwgdmFyaWFibGUgcG9sMzsgTkVQTSBzdGFuZGFyZCA5ICAgICBbcHBtXSAoOGggYXZlcmFnZSkNCjQuIE5PMiAgIE5pdHJvZ2VuIGRpb3hpZGUsIHZhcmlhYmxlIHBvbDQ7ICBORVBNIHN0YW5kYXJkIDAuMTIwIFtwcG1dICAoMWggYXZlcmFnZSkNCjUuIE8zICAgIE96b25lLCAgICAgICAgICAgIHZhcmlhYmxlIHBvbDU7ICBORVBNIHN0YW5kYXJkIDAuMTAwIFtwcG1dICAoMWggYXZlcmFnZSkNCjYuIEJzcCAgIFZpc2liaWxpdHksICAgICAgIHZhcmlhYmxlIHBvbDY7ICBORVBNIHN0YW5kYXJkIDIxMCAgIFsxL01tXSAoOGggYXZlcmFnZSkNCiANClRoZSBBaXIgUXVhbGl0eSBpbmRleCBmb3IgZWFjaCBwb2xsdXRhbnQgaXMgY2FsY3VsYXRlZCBhczoNCkFRSS14ID0gKHBvbC14L1N0YW5kYXJkLXgpKjEwMCAgaW4gWyVdDQp3aGVyZSANCnBvbC14IGlzIGFuIGF2ZXJhZ2UgY29uY2VudHJhdGlvbiBvZiBwb2xsdXRhbnQgeCBhdmVyYWdlZCBvdmVyIDFob3VyIChlLmcuIDFwbS0ycG0gYXZlcmFnZSAtPnBvbC14IHJlY29yZCBhdCAycG0pDQoNCiMgRGF0YSBEb3dubG9hZCBmcm9tIElOVEVSTkVUDQpGb2xsb3dpbmcgYXJlIHRoZSBVUkwgYWRkcmVzc2VzIGZyb20gd2hlcmUgdGhlIGFpciBxdWFsaXR5IGZpbGVzIGluIGNzdiBmb3JtYXQgd2VyZSBvYnRhaW5lZC4gRHVlIHRvIGEgdGVjaG5pY2FsIHByb2JsZW0gd2l0aCBkaXJlY3QgZG93bmxvYWRpbmcgdGhlc2UgZmlsZXMgZnJvbSB0aGUgVVJMIGFuZCB0aW1lIGNvbnN0cmFpbnMgdGhlIGZpbGVzIHdlcmUgZmlyc3QgZG93bmxvYWRlZCBpbnRvIGEgbG9jYWwgZGlyZWN0b3J5IGFuZCB0aGVuIGxvYWRlZCBpbnRvIFJTdHVkaW8uIFRoZSBzZWN0aW9uIGJlbG93IHByb3ZpZGVzIFVSTCBhbmQgdGhlIChjb21tZW50ZWQpIHNjcmlwdHMgd2hpY2ggd2VyZSBvcmlnaW5hbGx5IHVzZWQuDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQojIFBNMi41IA0KI3VybF9QTTIuNSA8LSAiaHR0cHM6Ly9kYXRhLnFsZC5nb3YuYXUvZGF0YXNldC83ODQ0ZWZkOS01MTE2LTRkYmEtODQ1OS1jNzk3MDNkODJiMzkvcmVzb3VyY2UvYjVjZGUwZmEtMTA2My00MWFmLWEwMWQtNjRlNDZhMjIxOTg1L2Rvd25sb2FkL3BtMi01cWxkMjAxNi5jc3YiDQojIFBNMi41X2RhdGFfYWxsX3NpdGVzPC1yZWFkLmNzdih1cmxfUE0yLjUsc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KDQojIFBNMTAgDQojdXJsX1BNMTA8LSAiaHR0cHM6Ly9kYXRhLnFsZC5nb3YuYXUvZGF0YXNldC83ODQ0ZWZkOS01MTE2LTRkYmEtODQ1OS1jNzk3MDNkODJiMzkvcmVzb3VyY2UvYWI2MjAzODMtZGU4OC00YThhLTg2YWMtZWQzODhiZWIzNTU0L2Rvd25sb2FkL3BtMTBxbGQyMDE2LmNzdiINCiNQTTEwX2RhdGFfYWxsX3NpdGVzPC1yZWFkLmNzdih1cmxfUE0xMCxzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCiMgQ2FyYm9uIE1vbm94aWRlDQojdXJsX0NPIDwtImh0dHBzOi8vZGF0YS5xbGQuZ292LmF1L2RhdGFzZXQvNzg0NGVmZDktNTExNi00ZGJhLTg0NTktYzc5NzAzZDgyYjM5L3Jlc291cmNlLzhiNmQ2NTUwLTI5NDEtNDI3Yy1iMWFmLTE4MWUxOTFjN2FiYi9kb3dubG9hZC9jYXJib25tb25veGlkZXFsZDIwMTYuY3N2Ig0KI0NPX2RhdGFfYWxsX3NpdGVzIDwtIHJlYWQuY3N2KHVybF9DTywgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KDQojIE5pdHJvZ2VuIGRpb3hvZGUNCiN1cmxfTk8yIDwtICJodHRwczovL2RhdGEucWxkLmdvdi5hdS9kYXRhc2V0Lzc4NDRlZmQ5LTUxMTYtNGRiYS04NDU5LWM3OTcwM2Q4MmIzOS9yZXNvdXJjZS9lMjhjYjFmMi1iMjYyLTQ3MjMtODI2MS1iYWJlNDA3ZjliMjAvZG93bmxvYWQvbml0cm9nZW5kaW94aWRlcWxkMjAxNi5jc3YiDQojTk8yX2RhdGFfYWxsX3NpdGVzIDwtIHJlYWQuY3N2KHVybF9OTzIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCg0KIyBPem9uZQ0KI3VybF9PMzwtICJodHRwczovL2RhdGEucWxkLmdvdi5hdS9kYXRhc2V0Lzc4NDRlZmQ5LTUxMTYtNGRiYS04NDU5LWM3OTcwM2Q4MmIzOS9yZXNvdXJjZS8wNDAyOTNkOC1mOThiLTQ3NDgtOTI1My1lNTAyODJjMTRjYjUvZG93bmxvYWQvb3pvbmVxbGQyMDE2LmNzdiINCiNPM19kYXRhX2FsbF9zaXRlcyA8LSByZWFkLmNzdih1cmxfTzMsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCg0KI3VybF92aXNpYmlsaXR5IDwtICJodHRwczovL2RhdGEucWxkLmdvdi5hdS9kYXRhc2V0Lzc4NDRlZmQ5LTUxMTYtNGRiYS04NDU5LWM3OTcwM2Q4MmIzOS9yZXNvdXJjZS8wYjIzZTM4MS1kYThiLTRhZDEtODA5OS1mOGFiMDY4OGUyZTcvZG93bmxvYWQvdmlzaWJpbGl0eXJlZHVjaW5ncGFydGljbGVzcWxkMjAxNi5jc3YiDQojVmlzaWJpbGl0eV9kYXRhX2FsbF9zaXRlcyA8LSByZWFkLmNzdih1cmxfdmlzaWJpbGl0eSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KYGBgDQoNCiMgRGF0YSBEb3dubG9hZCBmcm9tIFdvcmtpbmcgRGlyZWN0b3J5DQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQojIyAgUkVBRCBEQVRBIGZvciBFQUNIIFBPTExVVEFOVCBkYXRhIGZpbGVzIGZyb20gd29ya2luZyBkaXJlY3RvcnkgKEZpbGVzIEFscmVhZHkgRG93bmxvYWRlZCBmcm9tIHRoZSBJbnRlcm5ldA0KZ2V0d2QoKQ0Kc2V0d2QoIkM6L1VzZXJzL0phbXJpc2thL0RvY3VtZW50cy9STUlUX0NPVVJTRSBNYXRlcmlhbHMvUk1JVC0yMDE4LVMxL01BVEgyMzQ5X0NvdXJzZS9EYXRhIikNCg0KIyBQTTIuNSAoUGFydGljdWxhdGUgTWF0dGVyIFNtYWxsZXIgdGhhbiAyLjVtaWNyb24sIHBvbDEpDQpQTTIuNS5maWxlPC0icG0yLTVxbGQyMDE2LmNzdiIgICAgICAgICMgTmFtZSBvZiB0aGUgUE0yLjUgZGF0ZSBmaWxlIG9uIGxvY2FsIGRpcmVjdG9yeSANClBNMi41X2RhdGFfYWxsX3NpdGVzPC1yZWFkLmNzdihQTTIuNS5maWxlLHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgICMgbG9hZCB0aGUgZmlsZSBpbnRvIFJTdHVkaW8NCmhlYWQoUE0yLjVfZGF0YV9hbGxfc2l0ZXMpDQojc3RyKFBNMi41X2RhdGFfYWxsX3NpdGVzKQ0KDQojIFBNMTAgKFBhcnRpY3VsYXRlIE1hdHRlciBTbWFsbGVyIHRoYW4gMTBtaWNyb24sIHBvbDIpDQpQTTEwLmZpbGU8LSJwbTEwcWxkMjAxNi5jc3YiDQpQTTEwX2RhdGFfYWxsX3NpdGVzPC0gcmVhZC5jc3YoUE0xMC5maWxlLHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCmhlYWQoUE0xMF9kYXRhX2FsbF9zaXRlcykNCiNzdHIoUE0xMF9kYXRhX2FsbF9zaXRlcykNCg0KIyBDTyAoQ2FyYm9uIE1vbm94aWRlIENPLCBwb2wzKQ0KQ08uZmlsZTwtICJjYXJib25tb25veGlkZXFsZDIwMTYuY3N2Ig0KQ09fZGF0YV9hbGxfc2l0ZXM8LSByZWFkLmNzdihDTy5maWxlLHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCmhlYWQoQ09fZGF0YV9hbGxfc2l0ZXMpDQojc3RyKENPX2RhdGFfYWxsX3NpdGVzKQ0KDQojIE5PMiAoTml0cm9nZW4gZGlveGlkZSwgcG9sNCkNCk5PMi5maWxlPC0gIm5pdHJvZ2VuZGlveGlkZXFsZDIwMTYuY3N2Ig0KTk8yX2RhdGFfYWxsX3NpdGVzPC0gcmVhZC5jc3YoTk8yLmZpbGUsc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KaGVhZChOTzJfZGF0YV9hbGxfc2l0ZXMpDQojc3RyKE5PMl9kYXRhX2FsbF9zaXRlcykNCg0KIyBPMyAoT3pvbmUsIHBvbDUpDQpPMy5maWxlIDwtICJvem9uZXFsZDIwMTYuY3N2Ig0KTzNfZGF0YV9hbGxfc2l0ZXM8LSByZWFkLmNzdihPMy5maWxlLHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCmhlYWQoTzNfZGF0YV9hbGxfc2l0ZXMpDQojc3RyKE8zX2RhdGFfYWxsX3NpdGVzKQ0KDQojIFZpc2liaWxpdHkgKEJzcCwgcG9sNikNClZpc2liaWxpdHkuZmlsZTwtInZpc2liaWxpdHlyZWR1Y2luZ3BhcnRpY2xlc3FsZDIwMTYuY3N2Ig0KVmlzX2RhdGFfYWxsX3NpdGVzPC0gcmVhZC5jc3YoVmlzaWJpbGl0eS5maWxlLHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCmhlYWQoVmlzX2RhdGFfYWxsX3NpdGVzKQ0KI3N0cihWaXNfZGF0YV9hbGxfc2l0ZXMpDQojIC0tLS0tLS0tLS0tLS0tLSBBTEwgREFUQSBET1dOTE9BREVEICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCmBgYA0KDQoNCiMjIFVuZGVyc3RhbmQgDQpUbyBnYWluIHVuZGVyc3RhbmRpbmcgb2YgZGF0YSBzdHJ1Y3R1cmUsIHR5cGVzIG9mIHZhcmlhYmxlcywgZGltZW5zaW9ucywgZXRjLiBlYWNoIGZpbGUgd2FzIGV4cGxvcmVkIHVzaW5nIFZpZXcoZmlsZSksIHN0cihmaWxlKSwgY2xhc3MoZmlsZSksIGNvbG5hbWVzKGZpbGUpIGFuZCBvdGhlciBmdW5jdGlvbnMgd2FzIGFwcGxpZWQuIFNpbmNlIGVhY2ggZmlsZSBjb250YWlucyBkYXRhIGZvciBzZXZlcmFsIG1vbml0b3Jpbmcgc2l0ZXMsIG9ubHkgZGF0YSBmb3IgU291dGggQnJpc2JhbmUgKHNpdGUgb2YgaW50ZXJlc3QpIHdhcyBzZWxlY3RlZCBhbmQgdGhlbiBjb2x1bW4gcmVuYW1lZCwgaW5kaWNhdGluZyB0aGUgcG9sbHV0YW50IG5hbWUgYW5kIHVuaXRzLiBUaGUgYXBwcm9hY2ggaW52b2x2ZWQgZm9sbG93aW5nIHN0ZXBzOg0KMS4gQSBkYXRhZnJhbWUgcG9sMSwgcG9sMiwgLi5wb2w2IHdhcyBjcmVhdGVkIGJ5IGFzc2lnbmluZyBsb2FkZWQgY3N2IGZpbGVzLg0KMi4gQ2hlY2tlZCBuYW1lcyBvZiBWYXJpYWJsZXMgaW4gZWFjaCBmaWxlICh0aGVzZSByZXByZXNlbnQgbW9uaXRvcmluZyBzaXRlIHdoZXJlIHRoZSBwb2wgd2FzIG1lYXN1cmVkKQ0KMy4gU2VsZWN0ZWQgRGF0ZSwgVGltZSBhbmQgU291dGggQnJpc2JhbmUgKG9yIG5lYXJlc3QpIG1vbml0b3Jpbmcgc2l0ZQ0KNC4gQ2hlY2tlZCB3aGljaCBjb2x1bW4gY29udGFpbnMgdGhlIFNvdXRoIEJyaXNiYW5lIERhdGEgDQo1LiBSZW5hbWVkIHNlbGVjdGVkIGNvbHVtbiAgd2l0aCBhIHBvbGx1dGFudCBjb25jLiByZWFkaW5nIChlLmcuIGNvbG5hbWVzKHBvbDEpWzNdIDwtICJQTTIuNV8odWcvbTMpIikNCg0KU3VtbWFyaXNlIHRoZSB0eXBlcyBvZiB2YXJpYWJsZXMgYW5kIGRhdGEgc3RydWN0dXJlcywgY2hlY2sgdGhlIGF0dHJpYnV0ZXMgaW4gdGhlIGRhdGEuIEluIGFkZGl0aW9uIHRvIHRoZSBSIGNvZGVzIGFuZCBvdXRwdXRzLCBleHBsYWluIGJyaWVmbHkgdGhlIHN0ZXBzIHRoYXQgeW91IGhhdmUgdGFrZW4uIEluIHRoaXMgc2VjdGlvbiwgc2hvdyB0aGF0IHlvdSBoYXZlIGZ1bGZpbGxlZCBtaW5pbXVtIHJlcXVpcmVtZW50cyAyLTQuDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCiMgMS4gUGFydGljdWxhdGUgTWF0dGVyIHNtYWxsZXIgdGhhbiAyLjVtaWNyb24gUE0yLjUgZGF0YSBkZW5vdGVkIGFzIHBvbDENCnBvbDE8LVBNMi41X2RhdGFfYWxsX3NpdGVzICAgICAgICAgIyBhc3NpZ24gbmFtZSBmb3IgcG9sbHV0YW50cyBmaWxlIHN1YnNldCANCiNWaWV3KHBvbDEpICAgICAgICAgICAgICAgICAgICAgICAgIyB2aWV3IGFuZCBpbnNwZWN0IGRhdGEsIHNlbGVjdCBjb2x1bW4gd2l0aCBTb3V0aCBCcmlzYiBkYXRhIA0KY29sbmFtZXMocG9sMSkgICAgICAgICAgICAgICAgICAgICAjIGxpc3QgbmFtZXMgb2YgdGhlIHZhcmlhYmxlcyBpbiBwb2wxIA0KcG9sMSA8LXBvbDEgICU+JSAgICAgICAgICAgICAgICAgICAgICMgU2VsZWN0IGNvbHVtbnMgRGF0ZSwgVGltZSBhbmQgU291dGggQnJpc2IgZm9yIHBvbDENCiAgZHBseXI6OnNlbGVjdChjb250YWlucygiRGF0ZSIpLGNvbnRhaW5zKCJUaW1lIiksY29udGFpbnMoIlNvdXRoLkJyaXNiIikpDQpncmVwKGNvbG5hbWVzKHBvbDEpWzNdLCBjb2xuYW1lcyhQTTIuNV9kYXRhX2FsbF9zaXRlcyApKSAgIyBmaW5kIHdoaWNoIGNvbHVtbiB3YXMgc2VsZWN0ZWQgKGkuZS4sIHdpdGggcG9sMSBjb25jLiBkYXRhKQ0KY29sbmFtZXMocG9sMSlbM10gPC0gIlBNMi41Xyh1Zy9tMykiICAgICMgY2hhbmdlIG5hbWUgb2YgM3JkIGNvbHVtbiAod2l0aCBwb2wxIGNvbmMpIHRvIFBNMi41Xyh1Zy9tMykNCg0KYGBgDQoNCmBgYHtyLCBlY2hvID0gVFJVRSwgd2FybmluZ3MgPSBGQUxTRX0NCiMgMi4gUGFydGljdWxhdGUgTWF0dGVyIHNtYWxsZXIgdGhhbiAxMG1pY3JvbiBQTTEwIGRhdGEgZGVub3RlZCBhcyBwb2wyDQpwb2wyPC1QTTEwX2RhdGFfYWxsX3NpdGVzICAgICAgICAgICMgYXNzaWduIG5hbWUgZm9yIHBvbGx1dGFudHMgZmlsZSBzdWJzZXQgDQpwb2wyIDwtcG9sMiAlPiUgDQogIGRwbHlyOjpzZWxlY3QoY29udGFpbnMoIkRhdGUiKSxjb250YWlucygidGltZSIpLGNvbnRhaW5zKCJTb3V0aC5CcmlzYiIpKQ0KY29sbmFtZXMocG9sMilbM10gPC0gIlBNMTBfKHVnL20zKSIgICAgIyBjaGFuZ2UgdGhlIG5hbWUgb2YgY29sdW1uDQoNCmBgYA0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQojIDMuIENhcmJvbiBtb25veGlkZSBDTyBkYXRhIGRlbm90ZWQgYXMgcG9sMw0KcG9sMyA8LSBDT19kYXRhX2FsbF9zaXRlcyAgICAgICAgICMgYXNzaWduIG5hbWUgZm9yIHBvbGx1dGFudHMgZmlsZSBzdWJzZXQgDQpwb2wzIDwtcG9sMyAlPiUgDQogIGRwbHlyOjpzZWxlY3QoY29udGFpbnMoIkRhdGUiKSxjb250YWlucygidGltZSIpLGNvbnRhaW5zKCJTb3V0aC5CcmlzYiIpKQ0KY29sbmFtZXMocG9sMylbM10gPC0gIkNPXyhwcG0pIiAgICAjIGNoYW5nZSB0aGUgbmFtZSBvZiBjb2x1bW4NCmBgYA0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQojIDQuIE5pdHJvZ2VuIGRpb3hpZGUgTk8yIGRhdGEgZGVub3RlZCBhcyBwb2w0DQpwb2w0IDwtIE5PMl9kYXRhX2FsbF9zaXRlcyAgICAgICAgICMgYXNzaWduIG5hbWUgZm9yIHBvbGx1dGFudHMgZmlsZSBzdWJzZXQgDQpwb2w0IDwtcG9sNCAlPiUgDQogIGRwbHlyOjpzZWxlY3QoY29udGFpbnMoIkRhdGUiKSxjb250YWlucygidGltZSIpLGNvbnRhaW5zKCJTb3V0aC5CcmlzYiIpKQ0KY29sbmFtZXMocG9sNClbM10gPC0gIk5PMl8ocHBtKSIgICAgIyBjaGFuZ2UgdGhlIG5hbWUgb2YgY29sdW1uDQpgYGANCg0KYGBge3IsIGVjaG8gPSBUUlVFLCB3YXJuaW5ncyA9IEZBTFNFfQ0KIyA1LiBvWk9ORSBPMyBkYXRhIGRlbm90ZWQgYXMgcG9sNQ0KcG9sNSA8LSBPM19kYXRhX2FsbF9zaXRlcyAgICAgICAgICMgYXNzaWduIG5hbWUgZm9yIHBvbGx1dGFudHMgZmlsZSBzdWJzZXQgDQpjb2xuYW1lcyhwb2w1KSAgICAgICAgICAgICAgICAgICAgIyBTb3V0aCBCcmlzYmFuZSBkYXRhIHVuYXZhaWxhYmxlLCB0aGUgY2xvc2VzdCBtb25pdG9yaW5nIHN0YXRpb24gaXMgIlJvY2tsZWEiIA0KcG9sNSA8LXBvbDUgJT4lIA0KICBkcGx5cjo6c2VsZWN0KGNvbnRhaW5zKCJEYXRlIiksY29udGFpbnMoInRpbWUiKSxjb250YWlucygiUm9ja2xlYSIpKSAgIw0KY29sbmFtZXMocG9sNSlbM10gPC0gIk8zXyhwcG0pIiAgICAjIGNoYW5nZSB0aGUgbmFtZSBvZiBjb2x1bW4NCmBgYA0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHdhcm5pbmdzID0gRkFMU0V9DQojIDYuIFZpc2liaWxpdHkgZGF0YSBkZW5vdGVkIGFzIHBvbDYgKE1lYXN1cmVtZW50IHVuaXRzOiBpbnZlcnNlIG1lZ2FtZXRyZXMgKDEvTW0pICkNCnBvbDYgPC0gVmlzX2RhdGFfYWxsX3NpdGVzICAgICAgICAjIGFzc2lnbiBuYW1lIGZvciBwb2xsdXRhbnRzIGZpbGUgc3Vic2V0IA0KY29sbmFtZXMocG9sNikgICAgICAgICAgICAgICAgICAgICMgU291dGggQnJpc2JhbmUgZGF0YSB1bmF2YWlsYWJsZSwgdGhlIGNsb3Nlc3QgbW9uaXRvcmluZyBzdGF0aW9uIGlzICJSb2NrbGVhIiANCnBvbDYgPC1wb2w2ICU+JSANCiAgZHBseXI6OnNlbGVjdChjb250YWlucygiRGF0ZSIpLGNvbnRhaW5zKCJ0aW1lIiksY29udGFpbnMoIlJvY2tsZWEiKSkNCmNvbG5hbWVzKHBvbDYpWzNdIDwtICJWaXNfKDEvTW0pIiAgICAjIGNoYW5nZSB0aGUgbmFtZSBvZiBjb2x1bW4NCiMgLS0tLUVORCBPRiBTRUxFQ1RJTkcgREFUQSBmcm9tIGluZGl2aWR1YWwgRmlsZXMNCmBgYA0KDQpOZXh0IFN0ZXAgaXMgdG8gcmV2aWV3IHRoZSBzZWxlY3RlZCBmaWxlcyBwb2wxLCBwb2wyLCAuLi4ucG9sNiBpbiB0ZXJtcyBvZiB0aGUgdmFyaWFibGUgdHlwZSBhbmQgZmlsZXMgc3RydWN0dXJlDQoNCg0KIyMJVGlkeSAmIE1hbmlwdWxhdGUgRGF0YSBJIA0KDQpDaGVjayBpZiB0aGUgZGF0YSBjb25mb3JtcyB0aGUgdGlkeSBkYXRhIHByaW5jaXBsZXMuIElmIHlvdXIgZGF0YSBpcyBub3QgaW4gYSB0aWR5IGZvcm1hdCwgcmVzaGFwZSB5b3VyIGRhdGEgaW50byBhIHRpZHkgZm9ybWF0IChtaW5pbXVtIHJlcXVpcmVtZW50ICM1KS4gSW4gYWRkaXRpb24gdG8gdGhlIFIgY29kZXMgYW5kIG91dHB1dHMsIGV4cGxhaW4gZXZlcnl0aGluZyB0aGF0IHlvdSBkbyBpbiB0aGlzIHN0ZXAuDQoNCmBgYHtyfQ0KY2F0KCIjIDEuIFBhcnRpY3VsYXRlIE1hdHRlciBzbWFsbGVyIHRoYW4gMi41bWljcm9uIFBNMi41IGRhdGEgZGVub3RlZCBhcyBwb2wxIFxuIikNCnN0cihwb2wxKQ0KDQpjYXQoIiMgMi4gUGFydGljdWxhdGUgTWF0dGVyIHNtYWxsZXIgdGhhbiAxMG1pY3JvbiBQTTEwIGRhdGEgZGVub3RlZCBhcyBwb2wyIFxuICIpDQpzdHIocG9sMikNCg0KY2F0KCIjIDMuIENhcmJvbiBtb25veGlkZSBDTyBkYXRhIGRlbm90ZWQgYXMgcG9sMyBcbiAiKQ0Kc3RyKHBvbDMpDQoNCmNhdCgiIyA0LiBOaXRyb2dlbiBkaW94aWRlIE5PMiBkYXRhIGRlbm90ZWQgYXMgcG9sNCBcbiAiKQ0Kc3RyKHBvbDQpDQoNCmNhdCgiIyA1LiBPWk9ORSBPMyBkYXRhIGRlbm90ZWQgYXMgcG9sNSBcbiAiKQ0Kc3RyKHBvbDUpDQoNCmNhdCgiIyA2LiBWaXNpYmlsaXR5IGRhdGEgZGVub3RlZCBhcyBwb2w2IFxuICIpDQpzdHIocG9sNikNCmBgYA0KDQoqKiBTVU1NQVJZICoqDQpUaGUgcmVzdWx0cyBhYm92ZSAob3V0cHV0IG9mIHRoZSBzdHIoZmlsZSkgY29tbWFuZCkgIGZvciBhbGwgZGF0YWZyYW1lcyBwb2wxLCBwb2wyLCAuLi5wb2w2LCBzaG93IHRoYXQgdGhlIHZhcmlhYmxlcyBEYXRlIGFuZCBUaW1lIGZvciBlYWNoIGRhdGEgZnJhbWUgcG9sMSwgcG9sMiwgLi4uLCBwb2w2IGFyZSBwcmVzZW50IGFuZCBhcmUgY2hhcmFjdGVyIHR5cGUuVGhlIDNyZCB2YXJpYWJsZSAoZS5nLiwgUE0yLjUsIFBNMTAsIGV0YykgaXMgbnVtZXJpYyB0eXBlLiBUaGUgdmFyaWFibGVzIERhdGUgYW5kIFRpbWUgYXJlIGNvbW1vbiB2YXJpYWJsZXMgZm9yIGVhY2ggZGYgYW5kIHRoZXJlZm9yZSBjYW4gYmUgdXNlZCBmb3IgZGYgam9pbmluZy4NCg0KDQojIwlUaWR5ICYgTWFuaXB1bGF0ZSBEYXRhIElJIA0KSW4gaGVyZSBDcmVhdGUvbXV0YXRlIGF0IGxlYXN0IG9uZSB2YXJpYWJsZSBmcm9tIHRoZSBleGlzdGluZyB2YXJpYWJsZXMgKG1pbmltdW0gcmVxdWlyZW1lbnQgIzYpLiBJbiBhZGRpdGlvbiB0byB0aGUgUiBjb2RlcyBhbmQgb3V0cHV0cywgZXhwbGFpbiBldmVyeXRoaW5nIHRoYXQgeW91IGRvIGluIHRoaXMgc3RlcC4uDQoNCg0KIyMjIEpvaW5pbmcgaW5kaXZpZHVhbCBkYXRhZnJhbWVzDQpTaW5jZSB0aGUgY29uY25ldHJhdGlvbiBkYXRhIGFyZSBpbiBzZXBhcmF0ZSBkYXRhZnJhbWVzLCB0aGV5IG5lZWQgdG8gYmUgam9pbmVkLiBBcyBpbmRpY2F0ZWQgYWJvdmUsIHRoZSB2YXJpYWJsZXMgRGF0ZSBhbmQgVGltZSAoY2hhciB0eXBlKSBhcmUgcHJlc2VudCBpbiBlYWNoIGRmIGFuZCBjYW4gYmUgdXNlZCBmb3Igam9pbmluZy4gSSB1c2VkIGxlZnQgam9pbiBmdW5jdGlvbiwgd2hpY2ggYWxsb3dlZCB0byBqb2luIGVhY2ggb2YgdGhlIHBvbGx1dGFudHMgcG9sMSB0byBwb2w2IGFzIGFuIGluZGl2aWR1YWwgY29sdW1uLiBUaGUgZGF0ZSBhbmQgdGltZSB2YXJpYWJsZXMgYXJlIGZpcnN0IHR3byBjb2x1bW5zLiBUaGUgc2NyaXB0IGlzIHByZXNlbnRlZCBiZWxvdy4NCg0KYGBge3J9DQojIEpPSU5JTkcgZGF0YWZyYW1lcyBmb3IgQUxMIDYgUE9MTFVUQU5UIElOVE8gT05FIERBVEFGUkFNRQ0KIyBKb2luaW5nIGJ5IERhdGUgYW5kIFRpbWUgVmFyaWFibGVzICh0aGV5IGFyZSBzdGlsbCBjaGFyYWN0ZXIgdHlwZSkNCnBvbDFfNiA8LSBwb2wxICU+JSBsZWZ0X2pvaW4ocG9sMikgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgIGxlZnRfam9pbihwb2wzKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4ocG9sNCkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4ocG9sNSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4ocG9sNikNCg0KY2xhc3MocG9sMV82KQ0Kc3RyKHBvbDFfNikNCmhlYWQocG9sMV82KQ0KYGBgDQoqKiBTdW1tYXJ5ICoqDQpUaGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgaGFzIDg3ODQgb2JzZXJ2YXRpb25zIChyYXdzKSwgY29udGFpbnMgOCB2YXJpYWJsZXMuIFRoZSB2YXJpYWJsZXMgRGF0ZSBhbmQgVGltZSBhcmUgY2hhcmFjdGVyIHR5cGU7IHZhcmlhYmxlcyAzLTYgYXJlIG51bWVyaWMuIFRoZSBjcmVhdGVkIGRhdGFmcmFtZSBpcyBpbiB0aWR5IGZvcm1hdCwgYXMgZWFjaCBjb2x1bW4gcmVwcmVzZW50IGEgdW5pcXVlIHZhcmlhYmxlLCBlYWNoIG9ic2VydmF0aW9uIChyYXcpIGlzIGFsc28gdW5pcXVlLCBhbmQgZWFjaCB2YWx1ZSAoY2VsbCByZWFkaW5nKSBpcyB1bmlxdWUgYWxzby4gSGVuY2UgaXQgbWVldHMgdGhlIGNyaXRlcmlvbiBvZiBiZWluIGluIHRpZHkgZm9ybWF0LiBUaGUgZGF0YWZyYW1lIGNhbiBiZSBpbXByb3ZlZCBieSBjaGFuZ2luZyB0aGUgdHlwZSBvZiB2YXJpYWJsZSBEYXRlIGFuZCBUaW1lIChjaGFyYWN0ZXIgdHlwZSkgdG8gRGF0ZS9UaW1lIGZvcm1hdC4gVGhpcyB3aWxsIGJlIGRvbmUgbGF0ZXIuIEFsc28gdGhlc2UgdHdvIHZhcmlhYmxlcyBjYW4gYmUgbWVyZ2VkIGludG8gZGQtbW0teXl5IGhoOm1tOnNzIGZvcm1hdCB3aGljaCBhbGxvd3MgZWFzaWVyIG1hbmlwdWxhdGlvbi4NCg0KSW4gdGhlIG5leHQgc3RlcCBJIGNoZWNrZWQgdGhlIHBvbDEtNiBkZiBmb3IgdGhlIHByZXNlbmNlIG9mIG1pc3NpbmcgdmFsdWVzIChOQSksIGluY29uc2lzdGVuY2llcyAoZWcuIG5lZ2F0aXZlIHZhbHVlcyBmb3IgcG9sbHV0YW50cyBjb25jZW50cmF0aW9uKSBhbmQgb3RoZXIgaXNzdWVzLg0KDQojIwlTY2FuIEkgDQpTY2FuIHRoZSBkYXRhIGZvciBtaXNzaW5nIHZhbHVlcywgaW5jb25zaXN0ZW5jaWVzIGFuZCBvYnZpb3VzIGVycm9ycy4gSW4gdGhpcyBzdGVwLCB5b3Ugc2hvdWxkIGZ1bGZpbCB0aGUgbWluaW11bSByZXF1aXJlbWVudCAjNy4gSW4gYWRkaXRpb24gdG8gdGhlIFIgY29kZXMgYW5kIG91dHB1dHMsIGV4cGxhaW4gaG93IHlvdSBkZWFsdCB3aXRoIHRoZXNlIHZhbHVlcy4NCg0KIyMjIENIRUNLSU5HIERBVEEgRk9SIE1JU1NJTkcgVkFMVUVTIChpbiBwb2wxXzYgZGF0YWZyYW1lKQ0KYGBge3J9DQojIGlzLm5hKHBvbDFfNikgIyBjaGVjayBpZiB0aGVyZSBhcmUgTkFTDQpudV9Ub3RhbF9OQSA8LXN1bShpcy5uYShwb2wxXzYpKSAjIGNvdW50IHRoZSBudW1iZXIgb2YgTkENCm51X1RvdGFsX25vbl9OQSA8LXN1bSghaXMubmEocG9sMV82KSkgIyBudW1iZXIgb2Ygbm9uLSBOQSB2YWx1ZXMNCm51X1RvdGFsIDwtIChudV9Ub3RhbF9OQSArIG51X1RvdGFsX25vbl9OQSkNCihmcmFjdF9OQSA8LW51X1RvdGFsX05BL251X1RvdGFsKSAgICAjIEZSYWN0aW9uIG9mIE5BIGRhdGEgaW4gdG90YWwNCmBgYA0KKiogU1VNTUFSWSoqIA0KQWJvdXQgNS41JSBvZiB0b3RhbCBkYXRhIGZyb20gbWVyZ2VkIGZpbGUgaXMgTkENCg0KDQojIyMgQ0hFQ0tJTkcgRk9SIFNQRUNJQUwgVkFMVUVTIChpbiBwb2wxXzYgZGF0YWZyYW1lKQ0KYGBge3J9DQpkYXQxPC1wb2wxXzYgICAgICMgZm9yIGJyZXZpdHksIGFzc2lnbiBwb2wxXzYgZGF0YWZyYW1lIHRvIGRhdDEgZGF0YWZyYW1lDQppcy5uYShkYXQxKSA8LSBkby5jYWxsKGNiaW5kLGxhcHBseShkYXQxLCBpcy5pbmZpbml0ZSkpDQpzdW0oZG8uY2FsbChjYmluZCxsYXBwbHkoZGF0MSwgaXMuaW5maW5pdGUpKSkNCnN1bShkby5jYWxsKGNiaW5kLGxhcHBseShkYXQxLCBpcy5uYSkpKQ0Kc3VtKGRvLmNhbGwoY2JpbmQsbGFwcGx5KGRhdDEsIGlzLm5hbikpKQ0KYGBgDQoqKiBTVU1NQVJZKiogDQpUaGUgcmVzdWx0cyBmcm9tIGNodW5rIGFib3ZlIHNob3cgdGhhdCAxLiBOdW1iZXIgb2YgTkEgaW4gcG9sMV82IGlzIDM4NzI7IDIuIG51bWJlciBvZiBuYW4gaXMgemVybzsgYW5kIDMuIE51bWJlciBvZiBpbmZpbml0ZSB2YWx1ZXMgaW4gcG9sMV82IGlzIHplcnAuDQoNCg0KIyMjIENoZWNraW5nIGZvciBJTkNPTlNJU1RFTkNFUyAoVkFMSURJVFkpIA0KVGhpcyBpbmNsdWRlcyBjaGVja2luZyBpZiBhbnkgY29uY2VudHJhdGlvbiB2YWx1ZXMgaW4gcG9sMV82IGRhdGFmcmFtZSBhcmUgaW5maW5pdGUsIG5lZ2F0aXZlIG9yIG90aGVyd2lzZSBpbnZhbGlkIChOb3RlLiBwb2wxXzYgY29uY2VudHJhdGlvIG4gdmFsdWVzIGNvdWxkIG5vdCBiZSBuZWdhdGl2ZSEpDQpUaGUgZm9sbG9pd25nIHIgc2NyaXB0IGlkZW50aWZpZXMgTkVHQVRJVkUgVkFMVUVTIHdoaWNoIGFyZSB0aGVuIFJFUExBQ0VEIHdpdGggTkEgdmFsdWVzICh1c2luZyBsaWJyYXJ5KGRwbHlyKSBhbmQgYXNzaWduZWQgdG8gZGF0MiBkYXRhZnJhbWUgKGkuZS4sIGRhdDIgaXMgcG9sMV82IHdpdGggbmVnYXRpdmUgdmFsdWVzIGJlaW5nIHJlcGxjZWQgYnkgTkEpDQoNCmBgYHtyfQ0KZGF0MiA8LSBkYXQxICU+JSANCiAgIG11dGF0ZV9hbGwoZnVucyhyZXBsYWNlKC4sIC48MCwgTkEpKSkNCihudV9OQV9pbl9kYXQxIDwtc3VtKGRvLmNhbGwoY2JpbmQsbGFwcGx5KGRhdDEsIGlzLm5hKSkpKSAjIG51bWJlciBvZiBOQSBpbiBkYXQxDQoobnVfTkFfaW5fZGF0MiA8LXN1bShkby5jYWxsKGNiaW5kLGxhcHBseShkYXQyLCBpcy5uYSkpKSkgIyBudW1iZXIgb2YgTkEgaW4gZGF0Mg0KKG51X05lZ19WYWx1ZXNfaW5fZGF0MSA8LSAobnVfTkFfaW5fZGF0Mi1udV9OQV9pbl9kYXQxKSkNCmBgYA0KKiogU1VNTUFSWSoqIA0KTnVtYmVyIG9mIE5BIGluIHRoZSBvcmlnaW5hbCBkYXQxIChpLmUuLCBwb2wxXzYgZGF0YWZyYW1lKSBpcyAzODcyLiBOdW1iZXIgb2YgbmVnYXRpdmUgdmFsdWVzIGluIGRhdDIgd2FzIDE3Mi4gVGhlIG5ldyBkYXQzIGRhdGFmcmFtZSAoTmVnIHZhbHVlcyByZXBsYWNlZCBieSBOQSkgaGFzIGluIHRvdGFsIDQwNDQgdmFsdWVzIG9mIE5BLg0KDQojIyMgUkVQTEFDRSBORUdBVElWRSBWYWx1ZXMgaW4gcG9sMV82IGRhdGFmcmFtZSB3aXRoIE1FRElBTiB2YWx1ZXMNCkZvbGxvd2luZyBzY3JpcHQgaWRlbnRpZmllcyBOQSB2YWx1ZXMgaW4gZGF0Miwgd2hpY2ggYXJlIHRoZW4gUkVQTEFDRUQgd2l0aCBNRURJQU4gdmFsdWVzLiBUaGUgbW9kaWZpZWQgZGF0YXNldCBpcyB0aGVuIGFzc2lnbmVkIHRvIGRhdDMgZGF0YWZyYW1lIChpLmUuLCBkYXQzIGlzIHRoZSBvcmlnaW5hbCBwb2wxXzYgZGF0YXNldCB3aXRoIEFsbCBOQSBhbmQgTmVnYXRpdmUgVmFsdWVzIEJlaW5nIFJlcGxjZWQgYnkgTWVkaWFuIHZhbHVlIGZvciBlYWNoIHBvbGx1dGFudCByZXNwZWN0aXZlbHkgKGUuZy4sIGFsbCBtaXNzaW5nLCBOQSBhbmQgbmVnYXRpdmUgdmFsdWVzIGZvciBDTyBwb2xsdXRhbnQgYXJlIHJlcGxhY2VkIGJ5IHRoZSBtZWRpYW4gdmFsdWUgb2YgQ08gZGF0YSBtZWFzdXJlZCBpbiAyMDE2KS4NCmBgYHtyfQ0KZGF0MyA8LSBkYXRhLmZyYW1lKGxhcHBseShkYXQyLGZ1bmN0aW9uKHgpIHsNCiAgaWYoaXMubnVtZXJpYyh4KSkgaWZlbHNlKGlzLm5hKHgpLG1lZGlhbih4LG5hLnJtPVQpLHgpIGVsc2UgeH0pKQ0KIyBDb3VudCBvZiBuYSBpbiBkYXQzDQooc3VtKGRvLmNhbGwoY2JpbmQsbGFwcGx5KGRhdDMsIGlzLm5hKSkpKSAgIyBjb3VudCB0aGUgbnVtYmVyIG9mIE5BIGluIGRhdDMNCmBgYA0KKiogU1VNTUFSWSoqIA0KQWxsIG1pc3NpbmcsIE5BIGFuZCBuZWdhdGl2ZSB2YWx1ZXMgaW4gZGF0MyBoYXZlIGJlZW4gcmVtb3ZlZC4NCg0KDQojIyMgTnVtYmVyIG9mIElOQ09NUExFVEUgQ0FTRVMNCkFzc2Vzc21lbnQgb2YgZGF0YSBjb21wbGV0ZW5lcyBmb3IgdGhlIGpvaW5lZCBkYXRhZnJhbWUuIE9mdGVuIHRoZSBkYXRhIG1heSBub3QgYmUgYXZhaWxhYmxlIGZvciBkaWZmZXJlbnQgbW9uaXRvcmluZyBzaXRlcyBhbmQvb3IgdHlwZSBvZiBwb2xsdXRhbm10LiBUaGlzIG1heSBwbGF5IGEgcm9sZSB3aGVuIGNhbGN1bHRpbmcgQVFJX092ZXJhbGwgd2hpY2ggcmVxdWlyZXMgYXZhaWxhYmlsaXR5IG9mIGRhdGEgZm9yIGVhY2ggcG9sbHV0YW50IChmb3IgYSBnaXZlbiB0ZGF0ZS90aW1lKS4NCmBgYHtyfQ0KIyBjb21wbGV0ZS5jYXNlcyhkYXQyKQ0Kc3VtKGNvbXBsZXRlLmNhc2VzKGRhdDEpKSAjIG51Lm9mIGNvbXBsZXRlIGNhc2VzIGluIGRhdDEgKGFsbCBkYXRhKSBkYXRhZnJhbWUgaXMgNjYwMg0Kc3VtKGNvbXBsZXRlLmNhc2VzKGRhdDIpKSAjIG51Lm9mIGNvbXBsZXRlIGNhc2VzIGluIGRhdDIgKE5BIFJlbW92ZWQpIGRhdGFmcmFtZSBpcyA2NDc2DQpzdW0oY29tcGxldGUuY2FzZXMoZGF0MykpICMgbnUub2YgY29tcGxldGUgY2FzZXMgaW4gZGF0MyAoTkEgcmVtb3ZlZCwgbmVnLiB2YWx1ZXMgcmVwbGFjZWQgd2l0aCBNZWRpYW4pIGlzIDg3ODQNCg0KIyBmcmFjdGlvbiBvZiBjb21wbGV0ZSBjYXNlcyBpbiBkYXQxIChkYXQxIC1vcmlnaW5hbCBmaWxlIHdpdGggTkEgYW5kIG5lZ2F0aXZlIHZhbHVlcykNCihmcmFjdF9jb21wbGV0ZS5jYXNlc19kYXQxIDwtIHN1bShjb21wbGV0ZS5jYXNlcyhkYXQxKSkvZGltKGRhdDEpWzFdKQ0KIyBhYm91dCA3NS4xNSUgb2Ygb2JzZXJ2YXRpb25zIGFyZSBhdmFpbGFibGUNCg0KI2ZyYWN0aW9uIG9mIGNvbXBsZXRlIGNhc2VzIGluIGRhdDIgKHdpdGggbmVnYXRpdmUgdmFsdWVzIHJlcGxhY2VkIGJ5IE5BKQ0KKGZyYWN0X2NvbXBsZXRlLmNhc2VzX2RhdDIgPC0gc3VtKGNvbXBsZXRlLmNhc2VzKGRhdDIpKS9kaW0oZGF0MSlbMV0pDQojIGFib3V0IDczLjclIG9mIG9ic2VydmF0aW9ucyBhcmUgYXZhaWxhYmxlDQoNCiMgZnJhY3Rpb24gb2YgY29tcGxldGUgY2FzZXMgaW4gZGF0MyAod2l0aCBuZWdhdGl2ZSB2YWx1ZXMgYW5kIGFsc28gTkEgcmVwbGFjZWQgYnkgbWVkaWFuKQ0KKGZyYWN0X2NvbXBsZXRlLmNhc2VzX2RhdDMgPC0gc3VtKGNvbXBsZXRlLmNhc2VzKGRhdDMpKS9kaW0oZGF0MSlbMV0pDQojIDEwMCUgb2Ygb2JzZXJ2YXRpb25zIGFyZSBhdmFpbGFibGUgZm9yIGNhbGNpdWxhdGlvbiBvZiBBUUkNCmBgYA0KKiogU1VNTUFSWSoqIA0KQW5hbHlzaXMgb2YgY29tcGxldGUgY2FzZXMgc2hvd3MgdGhhdCB0aGUgZnJhY3Rpb24gb2YgY29tcGxldGUgY2FzZXMgZm9yIGRhdDEsIGRhdDIgYW5kIGRhdDMgaXMgYWJvdXQgMS4gNzUuMTUlIGZvciBkYXQxIChubyBkYXRhIHRyZWF0bWVudCBhcHBsaWVkLCBudW1iZXIgb2Ygb2Jlc3J2YXRpb24gNjYwMik7IA0KMi4gNzMuNyUgIGZvciBkYXQyIChuZWdhdGl2ZSB2YWx1ZXMgcmVwbGFjZWQgd2l0aCBOQTsgbnVtYmVyIG9mIG9iZXNydmF0aW9uIDY0NzYpLCBhbmQNCjMuIDEwMCUgICBmb3IgZGF0MyAoTkEgYW5kIG5lZ2F0aXZlIHZhbHVlcyByZXBsYWNlZCBieSBNZWRpYW4sIG51bWJlciBvZiBvYmVzcnZhdGlvbiA2Nzg0KQ0KDQoqKiBOT1RFOioqDQpBbGwgZnVydGhlciBkYXRhIHByZXByb2Nlc3NpbmcsIHByb2Nlc3NpbmcgYW5kIG1hbmlwdWxhdGlvbiB3aWxsIGJlIGRvbmUgdXNpbmcgZGF0MyBkYXRhZnJhbWUgKGkuZS4sIE5BIGFuZCBuZWdhdGl2ZSB2YWx1ZXMgcmVwbGFjZWQgd2l0aCBtZWRpYW4gdmFsdWUgZm9yIGVhY2ggcG9sbHV0YW50KQ0KDQojIyMgQ2hlY2sgZm9yIHR5cGVzIG9mIHZhcmlhYmxlcyBhbmQgd2hlcmUgcmVxdWlyZWQgYXBwbHkgY29udmVyc2lvbg0KYGBge3J9DQpzdHIoZGF0MykNCmBgYA0KKiogU1VNTUFSWSoqIA0KQXMgZHVyaW5nIHRoZSBwcm9jZXNzIGFib3ZlIHRoZSB0eXBlIG9mIHZhcmlhYmxlcyBEYXRlIGFuZCBUaW1lIHdhcyBjaG5nZWQgZnJvbSBjaGFyIHRvIEZhY3RvcjsgdGVoIGZvbGxvd2luZyBzY3JpcHQgY29udmVyc2VzIHRoZSB0eXBlIHRvIGNoYXJhY3Rlci4gDQoNCiMjIyBDSEFOR0UgVFlQRSBvZiB2YXJpYWJsZSBEYXRlIChGYWN0b3ItPiBjaGFyYWN0ZXIpIGFuZCB2YXJpYWJsZSBUaW1lIChGYWN0b3IgLT4gY2hhcmFjdGVyKQ0KYGBge3J9DQojbGlicmFyeShkcGx5cikNCmRhdDMgJT4lIA0KICBkcGx5cjo6bXV0YXRlX2lmKGlzLmZhY3RvciwgYXMuY2hhcmFjdGVyKSAtPiBkYXQzDQpzdHIoZGF0MykNCmBgYA0KKiogU1VNTUFSWSoqIA0KVGhlIHR5cGUgb2YgdmFyaWFibGVzIERhdGUgYW5kIFRpbWUgd2FzIHN1Y2Vzc2Z1bGx5IGNoYW5nZWQgdG8gY2hhcmFjdGVyLg0KDQojIyMgQ1JFQVRFIE5FVyBWQVJJQUJMRSBEYXRlX1RpbWUgKGNoYXJhY3RlciB0eXBlKQ0KTmV3IHZhcmlhYmxlIERhdGVfVGltZSB3YXMgY3JlYXRlZCBjb21iaW5pbmcgdGhlIERhdGUgYW5kIFRpbWUgdmFyaWFibGVzLiBUaGUgdmFyaWFibGUgd2FzIGNyZWF0ZWQgdXNpbmcgcGFzdGUgZnVuY3Rpb24uDQpgYGB7cn0NCmRhdDQ8LWRhdDMgICAgIyBkYXQzIHdhcyBhc3NpZ25lZCB0byBkYXQ0IGRhdGFmcmFtZSBhcyB0aGUgd29ya2luZyBkYXRhc2V0IHRvIGtlZXAgZGF0MyB1bmNoYW5nZWQNCiMgTm90ZTogZGF0NCBjb250YWlucyBkYXRhIGZyb20gcG9sMV82IHdpdGggTkEgYW5kIG5lZ2F0aXZlIHZhbHVlcyByZXBsYWNlZCB3aXRoIE1lZGlhbiB2YWx1ZSBmb3IgZWFjaCBwb2wxLTYNCiMgQ3JlYXRlIGEgbmV3IHZhcmlhYmxlIHVzaW5nIHBhc3RlIGZ1bmN0aW9uDQpkYXQ0JERhdGVfVGltZSA8LSBwYXN0ZShkYXQ0JERhdGUsIGRhdDQkVGltZSkgIyB2YXIgc3RpbGwgY2hhcmFjdGVyIHR5cGUsIGluIGEgZm9ybWF0IGUuZy47IjE5LzAyLzIwMTYgMDY6MDAiDQojc3RyKGRhdDMpDQpzdHIoZGF0NCkNCiNoZWFkKGRhdDQpDQpgYGANCg0KIyMjIFJFT1JERVIgQ09MVU1OUyBpbiBkYXQ1DQpUaGUgb3JkZXIgb2YgdmFyaWFibGVzIGluIGRhdDUgZGYgd2FzIGNoYW5nZWQgZm9yIGVhc2llciBwcm9jZXNzaW5nIGFuZCBhbmFseXNpcy5UaGUgb3JkZXIgd2FzIGNoYW5nZWQgdG86ICAxLkRhdGUsIDIuVGltZSwgMy5EYXRlX1RpbWUsIDQucG9sMSwgLi4uLDkucG9sNg0KYGBge3J9DQpkYXQ1PC1kYXQ0W2MoMSwyLDksMyw0LDUsNiw3LDgpXSAgIyBjaGFuZGVkIG9yZGVyIG9mIGNvbHVtbnM6IDEuRGF0ZSwgMi5UaW1lLCAzLkRhdGVfVGltZSwgNC5wb2wxLCAuLi45LnBvbDYNCnN0cihkYXQ1KSAgICAgICAgICAgICAjIGNoZWNraW5nIEF0dHJpYnV0ZXMuIERhdGVfVGltZSB2YXJpYWJsZSBpcyBjaGFyYWN0ZXIgdHlwZQ0KaGVhZChkYXQ1KQ0KI2NsYXNzKGRhdDUkRGF0ZV9UaW1lKQ0KI3N0cihkYXQ1JERhdGVfVGltZSkNCiNWaWV3KGRhdDUkRGF0ZV9UaW1lKQ0KYGBgDQoNCiMjIyBDSEFOR0UgVFlQRSBWYXJpYWJsZSBEYXRlX1RpbWUNClZhcmlhYmxlIGRhdDUkRGF0ZV9UaW1lIHdhcyBjaGFuZ2VkIGZyb20gY2hhciB0eXBlIHRvIERhdGUmVGltZSB0eXBlIGluIEZvcm1hdCBvZiAiMjAxNi0wNi0yMSAwMTowMDowMCINCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoImx1YnJpZGF0ZSIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmRhdDUkRGF0ZV9UaW1lIDwtIGRteV9obShkYXQ0JERhdGVfVGltZSwgdHogPSAiQXVzdHJhbGlhL1F1ZWVuc2xhbmQiKQ0Kc3RyKGRhdDUpDQpoZWFkKGRhdDUpDQojc3RyKGRhdDUkRGF0ZV9UaW1lKQ0KI2NsYXNzKGRhdDUkRGF0ZV9UaW1lKQ0KYGBgDQoNCiMjIyBDUkVBVEUgTkVXIFZBUklBQkxFUw0KTmV3IHZhcmlhYmxlcyB3ZXJlIGNyZWF0ZWQgYnkgZXh0cmFjdGluZyBkYXRlL3RpbWUgYXR0cmlidXRlcyBmcm9tIHZhcmlhYmxlIGRhdDUkRGF0ZV9UaW1lDQpUaGUgbmV3IHZhcmlhYmxlcyB3ZXJlOiB5ZWFyLCBtb250aCwgZGF5LCBkYXlfb2Zfd2VlaywgaG91ciwgbWluLCBzZWMNCg0KYGBge3J9DQojIGxpYnJhcnkobHVicmlkYXRlKQ0KZGF0NSRZZWFyICAgPC15ZWFyKGRhdDUkRGF0ZV9UaW1lKSAgICAgIyBjcmVhdGUgbmV3IHZhcmlhYmxlIHllYXINCmRhdDUkbW9udGggIDwtbW9udGgoZGF0NSREYXRlX1RpbWUpICAgICMgY3JlYXRlIG5ldyB2YXJpYWJsZSBtb250aA0KZGF0NSRkYXkgICAgPC1kYXkoZGF0NSREYXRlX1RpbWUpICAgICAgIyBjcmVhdGUgbmV3IHZhcmlhYmxlIGRheQ0KZGF0NSR3ZGF5ICAgPC13ZGF5KGRhdDUkRGF0ZV9UaW1lLCBsYWJlbCA9IFRSVUUpICAgIyBjcmVhdGUgbmV3IHZhcmlhYmxlIHdlZWsgZGF5IChGYWN0b3IgVHlwZSkNCmRhdDUkaG91ciAgIDwtaG91cihkYXQ1JERhdGVfVGltZSkgICAgICMgY3JlYXRlIG5ldyB2YXJpYWJsZSBob3VyDQpkYXQ1JG1pbnV0ZSA8LW1pbnV0ZShkYXQ1JERhdGVfVGltZSkgICAjIGNyZWF0ZSBuZXcgdmFyaWFibGUgbWludXRlDQpzdHIoZGF0NSkNCmhlYWQoZGF0NSkNCmBgYA0KDQojIyMgRFJPUCBWYXJpYWJsZXMgRGF0ZSBhbmQgVGltZSANClZhcmlhYmxlcyBEYXRlIGFuZCBUaW1lIChjaGFyIHR5cGUpIHdoaWNoIGFyZSBub3QgcmVxdWlyZWQgYW55bW9yZSB3ZXJlIHJlbW92ZWQgZnJvbSB0aGUgZGF0NSBkYXRhZnJhbWUNCmBgYHtyfQ0KZGF0NSA8LSBzdWJzZXQoZGF0NSwgc2VsZWN0ID0gLWMoRGF0ZSwgVGltZSkpICMgRHJvcCBjb2x1bW4gKHZhcmlhYmxlKSBEYXRlIGFuZCBUaW1lIGZyb20gZGF0NCBkYXRhZnJhbWUNCnN0cihkYXQ1KQ0KaGVhZChkYXQ1KQ0KYGBgDQoNCiMjIyBSRS1BUlJBTkdFIHRoZSBvcmRlciBvZiB2YXJpYWJsZXMNCk9yZGVyIG9mIGNvbHVtbnMgKHZhcmlhYmxlcykgaW4gZGF0NSBkYXRhZnJhbWUgd2FzIHJlYXJhbmdlZCB0byBtYWtlIGRhdGEgdmlld2luZyBhbmQgaGFuZGxpbmcgZWFzaWVyDQpgYGB7cn0NCiMgY29sbmFtZXMoZGF0NSkNCmRhdDU8LWRhdDVbYygxLDg6MTMsMjo3KV0gICMgY2hhbmRlZCBvcmRlciBjb2x1bW5zOiAxLkRhdGVfVGltZSAyLlllYXIgMy5Nb250aCA0LmRheSA1LndkYXkgNi5ob3VyIDcubWludXRlIDguLTEzLiBwb2wxLXBvbDYNCiJDaGFuZGVkIG9yZGVyIG9mIGNvbHVtbnM6IDEuRGF0ZV9UaW1lIDIuWWVhciAzLk1vbnRoIDQuZGF5IDUud2RheSA2LmhvdXIgNy5taW51dGUgOC5wb2wxIC4uLi4uMTMucG9sNiINCiNjb2xuYW1lcyhkYXQ1KSAjIGNoZWNrIHRoZSBjb2x1bW4gbmFtZXMNCmhlYWQoZGF0NSkNCmBgYA0KKiogU1VNTUFSWSoqDQpUaGUgZGF0YWZyYW1lIGRhdDUgaXMgbm93IGluIHRpZHkgZm9ybSAoaW4gdGVybXMgb2YgY29sdW1ucy12YXJpYWJsZXM7IHJhd3Mtb2JzZXJ2YXRpb24sIGFuZCB2YWx1ZXMgPSBjZWxscyA9IHJlY29yZCkgYW5kIHdpbGwgYmUgdXNlZCBmb3IgZnVydGhlciBldmFsdWF0aW9uLiANCg0KDQojIwlTY2FuIElJDQpTY2FuIHRoZSBudW1lcmljIGRhdGEgZm9yIG91dGxpZXJzLiBJbiB0aGlzIHN0ZXAsIHlvdSBzaG91bGQgZnVsZmlsIHRoZSBtaW5pbXVtIHJlcXVpcmVtZW50ICM4LiBJbiBhZGRpdGlvbiB0byB0aGUgUiBjb2RlcyBhbmQgb3V0cHV0cywgZXhwbGFpbiBob3cgeW91IGRlYWx0IHdpdGggdGhlc2UgdmFsdWVzLg0KDQojIyMgQ2hlY2sgZm9yIE9VVExJRVJTIHVzaW5nIGJveHBsb3QgbWV0aG9kDQpEYXRhIGZyYW1lIGRhdDUgd2FzIGNoZWNrZWQgZm9yIHRoZSBwcmVzZW5jZSBvb2Ygb3V0bGllcnMuIFRoZSBhcHByb2FjaCBzdGFydGVkIHVzaW5nIG1ldGhvZCBmb3IgZGV0ZWN0aW9uIG9mICBVbmlsYXRlcmFsIG91dGxpZXJzLiBBcyBhbiBleGFtcGxlLCBiZWxvdyBpcyBhIHNjcmlwdCBkZXRlY3RpbmcgZm9yIFBNMi41LCBQTTEwIG91dGxpZXJzIHVzaW5nIGJveHBsb3QgYW5kIHNjYXRlciBwbG90IChsb29raW5nIGF0IHRoZSByZWxhdGlvbnNoaXAvZGVwZW5kZW5jaWVzIGJldHdlZW4gdGVoIFBNMTAgYW5kIFBNMi41IHZhcmlhYmxlKS4NCmBgYHtyfQ0KcGFyKG1mcm93PWMoMSwzKSkNCmJveHBsb3QoZGF0NSRQTTIuNV8udWcubTMuKQ0KYm94cGxvdChkYXQ1JFBNMTBfLnVnLm0zLikNCnBsb3QoZGF0NSRQTTEwXy51Zy5tMy4sZGF0NCRQTTIuNV8udWcubTMuKQ0KYGBgDQoqKiBTVU1NQVJZKioNClRoZSBib3hwbG90cyBhbmQgc2NhdHRlciBwbG90IHNob3dzIHByZXNuY2Ugb2YgcXVhdGUgYSBudW1iZXIgb2Ygb3V0bGllcnMgZm9yIFBNMi41LCBQTTEwIHZhcmlhYmxlcy4gU2ltaWxhciByZXN1bHRzIChub3QgcHJlc2VudGVkIGhlcmUpIHdlcmUgb2JzZXJ2ZWQgZm9yIG90aGVyIHBvbGx1dGFudHMsIGkuZS4sIENPIE5PMiwgTzMgYW5kIFZpc2liaWxpdHkgYXMgd2VsbC4NCg0KDQojIyMgQ2hlY2sgZm9yIE9VVExJRVJTIHVzaW5nIHpzY29yZXMgKGluZGl2aWR1YWwgdmFyaWFibGUpDQpgYGB7cn0NCmxpYnJhcnkob3V0bGllcnMpDQp6LnNjb3JlcyA8LSBkYXQ1JFBNMi41Xy51Zy5tMy4gJT4lIHNjb3Jlcyh0eXBlID0gInoiKSAjQ2hlY2tpbmcgb3V0bGllcnMgZm9yIFBNMi41IHBvbGx1dGFudA0Kei5zY29yZXMgJT4lIHN1bW1hcnkoKQ0KYGBgDQoqKiBTVU1NQVJZKioNClRoZSB6LXNjb3JlcyBtZXRob2QgaW5kaWNhdGVzIHByZXNlbmNlIG9mIG91dGxpZXJzIGZvciBQTTIuNSAoYWJzKHpzY29yZSk+MykuVXNpbmcgdGhlIHNhbWUgYXBwcm9hY2gsIEkgY2hlY2tlZCB6c2NvcmVzIGZvciBvdGhlciBwb2xsdXRhbnRzIChvbmUgYnkgb25lKSwgb3IgYXMgcHJlc2VudGVkIGJlbG93IHVzaW5nIGZvciBsb29wLg0KDQoNCiMjIyBDaGVjayBmb3IgT1VUTElFUlMgdXNpbmcgenNjb3JlcyAoYWxsIHZhcmlhYmxlcyB1c2luZyBmb3IgbG9vcCkNCmBgYHtyfQ0KZm9yKGkgaW4gODoxMykNCiAgew0KICBwcmludCgiLS0tLS0tLS0tLS0tLS0iLHF1b3RlID0gRkFMU0UpDQogIGNvbG5hbWVzKGRhdDVbaV0pDQogIHouc2NvcmVzIDwtIGRhdDVbaV0gJT4lIHNjb3Jlcyh0eXBlID0gInoiKSAjQ2hlY2tpbmcgb3V0bGllcnMgZm9yIFBNMi41IHBvbGx1dGFudA0KICBwcmludCh6LnNjb3JlcyAlPiUgc3VtbWFyeSgpKQ0KICBhPC1zdW0oYWJzKHouc2NvcmVzKT4zKQ0KICBmcmFjdF9vdXRsIDwtYS9kaW0oZGF0NSlbMV0NCiAgI3ByaW50KGEvZGltKGRhdDUpWzFdKQ0KICBjYXQoIkZyYWN0aW9uIG9mIG91dGxpZXJzOiIsZnJhY3Rfb3V0bCwiXG4iKQ0KfSANCmBgYA0KKiogU1VNTUFSWSoqDQpUaGUgcmVzdWx0cyBpbmRpY2F0ZXMgZWFjaCBwb2wxLXBvbDYgdmFyaWFibGVzIGhhcyBhIHNtYWxsIGZyYWN0aW9uIG9mIG91dGxpZXJzLCBsZXNzIHRoYW4gMiUiLg0KDQojIyMgQWRkaXRvbmFsIGFwcHJvYWNoZXMgdG8gZGV0ZWN0aW9uIE91dGxpZXJzDQpDcmVhdGUgYSBzdWJzZXQgcG9sMSwgcG9sMiwgLi5wb2w2IGRhdGEgZnJvbSBkYXQ0IGRhdGFmcmFtZS4gVGhlIHN1YnNldCB3aWxsIGJlIHVzZWQgZm9yIGRldGVjdGlvbiBvZiBvdXRsaWVycw0KYGBge3J9DQpwb2wxXzZfbmFtZXM8LSBjKCJQTTIuNV8udWcubTMuIiwiUE0xMF8udWcubTMuIiwiQ09fLnBwbS4iLCJOTzJfLnBwbS4iLCJPM18ucHBtLiIsIlZpc18uMS5NbS4iKQ0KI3BvbDFfMl9uYW1lczwtIGMoIlBNMi41Xy51Zy5tMy4iLCJQTTEwXy51Zy5tMy4iKQ0KI3N0cihkYXQ1KQ0KI2NvbG5hbWVzKGRhdDQpIGNoZWNraW5nIHRoZSBjb2x1bW5zIG5hbWVzDQpkYXQ1X3BvbDFfNiA8LWRhdDUgJT4lIA0KICBkcGx5cjo6c2VsZWN0KHBvbDFfNl9uYW1lcykNCmhlYWQoZGF0NV9wb2wxXzYpICAjIHN1YnNldCBvZiB0aGUgc3Vic2V0DQpgYGANCg0KIyMjIENyZWF0ZSBhIHNpbmdsZSBib3hwbG90IHdpdGggYWxsIHBvbGx1dGFudHMNClRoZSBhcHByb2FjaCBhbGxvd3MgY2hlY2tpbmcgYW5kIGNvbXBhcmlzb24gb2Ygb3V0bGllcnMgZm9yIGRpZmZlcmVudCBwb2xsdXRhbnRzDQpgYGB7cn0NCmJveHBsb3QoZGF0NV9wb2wxXzYpDQpgYGANCioqIFNVTU1BUlkqKg0KT3V0bGllcnMgYXJlIGNsZWFybHkgcHJlc2VudCBmb3IgUE0yLjUsIFBNMTAgYW5kIFZpc2liaWxpdHksIGhvd2V2ZXIgZm9yIENPLCBOTzIgYW5kIE8zIHRoZSBib3hwbG90IHJlcHJlc2VudGF0aW9uIGlzIHR3byBuYXJyb3cuIEZvciB0aGlzIHJlYXNvbiB0aGUgZGF0YSB3ZXJlIG5vcm1hbGlzZWQgdG8gYWxsb3cgYmV0dGVyIGJveHBsb3QgY29tcGFyaXNvbi4NCg0KDQojIwlUcmFuc2Zvcm0gDQpBcHBseSBhbiBhcHByb3ByaWF0ZSB0cmFuc2Zvcm1hdGlvbiBmb3IgYXQgbGVhc3Qgb25lIG9mIHRoZSB2YXJpYWJsZXMuIEluIGFkZGl0aW9uIHRvIHRoZSBSIGNvZGVzIGFuZCBvdXRwdXRzLCBleHBsYWluIGV2ZXJ5dGhpbmcgdGhhdCB5b3UgZG8gaW4gdGhpcyBzdGVwLiBJbiB0aGlzIHN0ZXAsIHlvdSBzaG91bGQgZnVsZmlsIHRoZSBtaW5pbXVtIHJlcXVpcmVtZW50ICM5Lg0KIA0KIyMjIE5PUk1BTElTQVRJT04gb2YgVmFyaWFibGVzDQpJbiBoZXJlIEkgYXBwbGllZCBub3JtYWxpc2F0aW9uIGZvciBlYWNoIG9mIHRoZSBwb2wxLXBvbDYgdmFyaWFibGVzIGluIGRhdDUgZGF0YWZyYW1lDQpgYGB7cn0NCmRhdDVfcG9sMV82X25vcm0gPC0gYXMuZGF0YS5mcmFtZShsYXBwbHkoZGF0NV9wb2wxXzZbMTo2XSwgbm9ybWFsaXplKSkNCmJveHBsb3QoZGF0NV9wb2wxXzZfbm9ybSkNCmBgYA0KKipTVU1NQVJZKioNCk5vdCBzdXByaXNpbmdseSwgdGhlIG5vcm1hbGlzZWQgZGF0YSBmb3IgcG9sMS1wb2w2IGFsc28gc2hvdyBwcmVzZW5jZSBvZiBvdXRsaWVycyBmb3IgQ08sIE5PMiBhbmQgTzMgY29uY2VudHJhdGlvbi4NCg0KIyMjIENsZWFuaW5nIE91dGxpZXJzDQpJIGFsc28gdHJpZWQgYXBwbHlpbmcgTXVsdGl2YXJpYXRlIE91dGxpZXIgRGV0ZWN0aW9uIHVzaW5nIE1haGFsYW5vYmlzIGRpc3RhbmNlIHdpdGggUVEgcGxvdHMNClRoaXMgd2FzIG5vdCBzdHJhaWdodGZvd2FyZCBkdWUgdGhlIGxhcmdlIG51bWJlciBvZiBkYXRhcG9pbnQgKGFib3ZlIHRoZSBNRCBkaXN0YW5jZSBsaW1pdDsgbXkgZGF0YXNldCBoYXMgbW9yZSB0aGFuIDUwMDAgdmFsdWVzLCB3aGljaCBpcyB0aGUgTUhEIGxpbWl0KWFuZCBhbHNvIHRoZSBudW1iZXIgb2YgdmFyaWFibGVzIGluY2x1ZGVkIChwb2wxLXBvbDYpLiBGb3IgYW4gaWx1c3RyYXRpb24gLCBhbiBleGFtcGxlIG9mIHRoZSBjb2RlIChjb21tZW50ZWQpIHdoaWNoIGNhbiBiZSB1c2VkIGZvciBzbWFsbGVyIGRhdGFzZXRzIGlzIHByZXNlbnRlZCBiZWxvdzoNCmBgYHtyfQ0KIyByZXN1bHRzIDwtIG12bihkYXRhID0gZGF0NV9wb2wxXzYsIG11bHRpdmFyaWF0ZU91dGxpZXJNZXRob2QgPSAicXVhbiIsIHNob3dPdXRsaWVycyA9IFRSVUUpDQojIHJlc3VsdHMgPC0gbXZuKGRhdGEgPSBkYXQ1X3BvbDFfNiwgbXVsdGl2YXJpYXRlT3V0bGllck1ldGhvZCA9ICJxdWFuIiwgbXVsdGl2YXJpYXRlUGxvdCA9ICJjb250b3VyIikNCmBgYA0KDQojIyMgRXhjbHVkaW5nIE91dGxpZXJzDQpBbiBhdHRlbXB0IHdhcyBkb25lIHRvIGNsZWFuIHRoZSBkYXRhc2V0IGRhdDUgYnkgZXhjbHVkaW5nIGRhdGEgd2l0aCB6c2NvcmU+MzsgaG93ZXZlciBkdWUgdGltZSBjb25zdHJhaW5zIHRoaXMgd2FzIG5vdCBjb21wbGV0ZWQuDQpgYGB7cn0NCmRhdDVfcG9sMV82X25vcm1fY2xlYW48LSBkYXQ1X3BvbDFfNl9ub3JtWyAtIHdoaWNoKGFicyh6LnNjb3JlcykgPjMgKV0NCmJveHBsb3QoZGF0NV9wb2wxXzZfbm9ybV9jbGVhbikNCmBgYA0KKipTVU1NQVJZKioNClRoZSBjbGVhbmVkIGRhdGFzZXQgc3RpbGwgaGF2ZSBkYXRhcG9pbnRzIHdlbGwgYmV5b25kIHRoZSBvdXRlciBmZW5jZS4gVGhpcyB3b3VkbCByZXF1aXJlIG1vcmUgZXhwbG9yYXRpb24uDQoNCiMjIEFkZGl0aW9uYWwvRXh0cmEgV29yaw0KDQojIyMgQ2FsY3VsYXRpbmcgQWlyIFF1YWxpdHkgSW5kZXggQUlRDQoqKiBOb3RlICoqDQpUaGUgTmF0aW9uYWwgQWlyIE5FUE0gc3RhbmRhcmRzIHVzZWQgdG8gY2FsY3VsYXRlIHRoZSBBUUkgYXJlIGJhc2VkIG9uIGF2ZXJhZ2luZyB0aW1lIGludGVydmFscyBwcmVzZW50ZWQgYmVsb3c6IA0KQXZlcmFnaW5nIHBlcmlvZCBQTTEwIGFuZCBQTTIuNSBpcyAxIGRheQ0KQXZlcmFnaW5nIHBlcmlvZCBDTyBpcyA4IGhvdXJzDQpBdmVyYWdpbmcgcGVyaW9kIE5PMiwgTzMgYW5kIFZpc2liaWxpdHkgaXMgMSBob3VyDQpJbiBoZXJlLCBmb3Igc2ltcGxpY2l0eSwgYWxsIHZhbHVlcyB3ZXJlIGFzc2lnbmVkIGFzIHN0YW5kYXJkIHZhbHVlcyBmb3IgYXZlcmFnaW5nIDEgaG91ciBwZXJpb2QNCg0KIyMjIFRoZSBBaXIgTkVQTSBzdGFuZGFyZCBWYWx1ZXMNClRoZSBBaXIgTkVQTSBzdGFuZGFyZHMgZm9yIHNlbGVjdGVkIHBvbGx1dGFudHMgYXJlIHNldCBhcyBmb2xsb3c6DQpgYGB7cn0NCnBvbDFfc3RhbmRhcmQgPSAgIDI1ICAgICAjIFt1Zy9tM10gUE0yLjUgKDI0IGhvdXIgYXZnKSBOYXRpb25hbCBBaXIgTkVQTSBTdGFuZGFyZA0KcG9sMl9zdGFuZGFyZCA9ICAgNTAgICAgICMgW3VnL20zXSBQTTEwICAoMjQgaG91ciBhdmcpIE5hdGlvbmFsIEFpciBORVBNIFN0YW5kYXJkDQpwb2wzX3N0YW5kYXJkID0gICAgOSAgICAgIyBbcHBtXSAgIENPICAgICggOCBob3VyIGF2ZykgTmF0aW9uYWwgQWlyIE5FUE0gU3RhbmRhcmQNCnBvbDRfc3RhbmRhcmQgPSAwLjEyICAgICAjIFtwcG1dICAgTk8yICAgKCAxIGhvdXIgYXZnKSBOYXRpb25hbCBBaXIgTkVQTSBTdGFuZGFyZA0KcG9sNV9zdGFuZGFyZCA9IDAuMTAgICAgICMgW3BwbV0gICBPMyAgICAoIDEgaG91ciBhdmcpIE5hdGlvbmFsIEFpciBORVBNIFN0YW5kYXJkDQpwb2w2X3N0YW5kYXJkID0gMjEwICAgICAgIyBbTW1eLTFdICBCX3NwIFZpc2liaWxpdHkgKCAxIGhvdXIgYXZnKSBOU1cgU3RhbmRhcmQNCmBgYA0KDQoNCiMjIyBDYWxjdWxhdGlvbiBvZiBBUUlfcG9sDQpUaGUgQVFJIHdhcyBjYWxjdWxhdGVkIHVzaW5nIEZvcm11bGE6IEFRSV9wb2wgPSAocG9sX3JlYWRpbmcvcG9sX3N0YW5kYXJkKSoxMDAuDQpCZWxvdyBpcyBhIHNjcmlwdCB1c2VkIHRvIGNhbGN1bGF0ZSBBUUkgZm9yIGVhY2ggb2YgdGhlIHNlbGVjdGVkIHBvbGx1dGFudHM6IA0KYGBge3J9DQpBUUlfcG9sMSA9IChkYXQ1Wyw4XS9wb2wxX3N0YW5kYXJkKSoxMDAgICAgIyBBUUkgZm9yIFBNMi41IChwb2wxKQ0KQVFJX3BvbDIgPSAoZGF0NVssOV0vcG9sMl9zdGFuZGFyZCkqMTAwICAgICMgQVFJIGZvciBQTTIuNSAocG9sMikNCkFRSV9wb2wzID0gKGRhdDVbLDEwXS9wb2wzX3N0YW5kYXJkKSoxMDAgICAjIEFRSSBmb3IgUE0yLjUgKHBvbDMpDQpBUUlfcG9sNCA9IChkYXQ1WywxMV0vcG9sNF9zdGFuZGFyZCkqMTAwICAgIyBBUUkgZm9yIFBNMi41IChwb2w0KQ0KQVFJX3BvbDUgPSAoZGF0NVssMTJdL3BvbDVfc3RhbmRhcmQpKjEwMCAgICMgQVFJIGZvciBQTTIuNSAocG9sNSkNCkFRSV9wb2w2ID0gKGRhdDVbLDEzXS9wb2w2X3N0YW5kYXJkKSoxMDAgICAjIEFRSSBmb3IgUE0yLjUgKHBvbDYpDQpgYGANCg0KQ3JlYXRlIGEgZGF0YWZyYW1lIHdpdGggQVFJX3BvMSwgLi4uLCBBUUlfcG9sNg0KYGBge3J9DQpBUUlfcG9sMV82IDwtZGF0YS5mcmFtZShjYmluZChBUUlfcG9sMSxBUUlfcG9sMixBUUlfcG9sMyxBUUlfcG9sNCxBUUlfcG9sNSxBUUlfcG9sNikpDQpoZWFkKEFRSV9wb2wxXzYpDQojY29sbmFtZXMoQVFJX3BvbDFfNikNCg0KYGBgDQoNCkZvciBmdXJ0aGVyIHByb2Nlc3NpbmcgY29uYyB2YWx1ZXMgZm9yIGVhY2ggcG9sbHV0YW50IHdlcmUgYXNzaWduZWQgdG8gbmV3IHZhcmlhYmxlcyAobGF0ZXIgbWVyZ2VkIGludG8gYSBkZikNCmBgYHtyfQ0KcG9sMV9jb25jPC1kYXQ1Wyw4XSAgICMgcG9sMSBQTTIuNQ0KcG9sMl9jb25jPC1kYXQ1Wyw5XSAgICMgcG9sMiBQTTEwDQpwb2wzX2NvbmM8LWRhdDVbLDEwXSAgIyBwb2wzIENPDQpwb2w0X2NvbmM8LWRhdDVbLDExXSAgIyBwb2w0IE5PMg0KcG9sNV9jb25jPC1kYXQ1WywxMl0gICMgcG9sNSBPMw0KcG9sNl9jb25jPC1kYXQ1WywxM10gICMgcG9sNiBWaXNpYmlsaXR5DQpgYGANCg0KQ3JlYXRlIGEgZGF0YWZyYW1lIHdpdGggcG9sMV9jb25jLCAuLi4sIHBvbDZfY29uYw0KYGBge3J9DQpDb25jX3BvbDFfNiA8LWRhdGEuZnJhbWUoY2JpbmQocG9sMV9jb25jLHBvbDJfY29uYyxwb2wzX2NvbmMscG9sNF9jb25jLHBvbDVfY29uYyxwb2w2X2NvbmMpKQ0KI2hlYWQoQ29uY19wb2wxXzYpDQojY29sbmFtZXMoQ29uY19wb2wxXzYpDQojc3RyKENvbmNfcG9sMV82KQ0KDQpgYGANCg0KDQojIyMgRXhwbG9yaW5nIFBvbGx1dGFudHMgQ29uY2VudHJhdGlvbiBhbmQgQVFJIGRhdGENCkFzc2Vzc2luZyBwb2wxLTYgYW5kIEFRSV9wb2wxLTYgZGF0YSB1c2luZyBzdW1tYXJ5IGZ1bmN0aW9uIGFuZCBwbG90dGluZw0KYGBge3J9DQpzdW1tYXJ5KHBvbDFfY29uYykgIyBzdW1tYXJ5IGZvciBwb2wxICBQTTIuNQ0Kc3VtbWFyeShBUUlfcG9sMSkgICMgc3VtbWFyeSBmb3IgQVFJX3BvbDENCnBsb3QocG9sMV9jb25jKSAgICAjIHBsb3QgcG9sIDEgY29uYw0KYGBgDQoNCkFzc2Vzc21lbnQgb2YgdGhlIGNvbmNuZXRydGFpb24gYW5kIEFRSSBkYXRhIGZvciBwb2xsdXRhbnRzIHBvbDIgLXBvbDYNCmBgYHtyfQ0Kc3VtbWFyeShwb2wyX2NvbmMpICMgc3VtbWFyeSBmb3IgcG9sMiBQTTIuNQ0Kc3VtbWFyeShBUUlfcG9sMikgICMgc3VtbWFyeSBmb3IgQVFJX3BvbDINCnBsb3QocG9sMl9jb25jKSAgICAjIHBsb3QgcG9sXzIgY29uYw0KDQpgYGANCg0KYGBge3J9DQpzdW1tYXJ5KHBvbDNfY29uYykgIyBzdW1tYXJ5IGZvciBwb2wzIENPDQpzdW1tYXJ5KEFRSV9wb2wzKSAgIyBzdW1tYXJ5IGZvciBBUUlfcG9sMw0KcGxvdChwb2wzX2NvbmMpICAgICMgcGxvdCBwb2xfMyBjb25jDQoNCmBgYA0KDQpgYGB7cn0NCg0Kc3VtbWFyeShwb2w0X2NvbmMpICMgc3VtbWFyeSBmb3IgcG9sNCBOTzINCnN1bW1hcnkoQVFJX3BvbDQpICAjIHN1bW1hcnkgZm9yIEFRSV9wb2w0DQpwbG90KHBvbDRfY29uYykgICAgIyBwbG90IHBvbF80IGNvbmMNCmBgYA0KDQpgYGB7cn0NCg0Kc3VtbWFyeShwb2w1X2NvbmMpICMgc3VtbWFyeSBmb3IgcG9sNSBPMw0Kc3VtbWFyeShBUUlfcG9sNSkgICMgc3VtbWFyeSBmb3IgQVFJX3BvbDUNCnBsb3QocG9sNV9jb25jKSAgICAjIHBsb3QgcG9sXzUgY29uYw0KDQpgYGANCg0KDQpgYGB7cn0NCnN1bW1hcnkocG9sNl9jb25jKSAjIHN1bW1hcnkgZm9yIHBvbDYgVmlzaWJpbGl0eQ0Kc3VtbWFyeShBUUlfcG9sNikgICMgc3VtbWFyeSBmb3IgQVFJX3BvbDYNCnBsb3QocG9sNl9jb25jKSAgICAjIHBsb3QgcG9sXzYgY29uYw0KcGxvdChBUUlfcG9sNikgICAgICMgcGxvdCBBUUlfcG9sNg0KYGBgDQoqKiBTVU1NQVJZKioNClRoZSBzdW1tYXJ5IGFuZCBzY2F0dGVyIHBsb3RzIGZvciBwb2wxLXBvbDYgaW5kaWNhdGVzIHRoYXQgdGhlIGRhdGEgaXMgcXVpdGUgbm9pc3kuIFNvbWUgb2YgdGhlIGRhdGEgKHN0cmFpZ2h0IGxpbmUgYXMgZm9yIHBvbDYgYWJvdmUsIGRhdGEgcG9pbnRzIHdpdGhpbmRleCBiZXR3ZWVuIDUwMDAtNzAwMCkgY29ycmVzcG9kcyB0byBtZWRpYW4gdmFsdWVzICh1c2VkIGFzIHJlcGxhY2VtZW50IG9mIHRoZSBtaXNzaW5nIG9yIG5lZ2F0aXZlIGRhdGEuIFRoaXMgcHJvcGJhbHkgd291bGQgbmVlZCB0byBiZSBpbW5wcm92ZWQsIHBlcmhhc3AgYnkgcmVtb3ZlaW5nIHRoZSBjYXNlcyB3aXRoIE5BLCBuZWdhdG9pdmUgdmFsdWVzLg0KDQojIyMgUGxvdHRpbmcgQ29uY2VudHJhdGlvbiBmb3IgUE0yLjUsIFBNMTAgYW5kIENPDQpgYGB7cn0NCiNwYXIobWZyb3c9YygyLCAzKSkNCnBhcihtZnJvdz1jKDMsIDMpKQ0KZm9yIChpIGluIDE6Mykgew0KICAgIHBsb3QoQ29uY19wb2wxXzZbLGldLCBtYWluID0gcGFzdGUwKCJjb25jX3BvbCIsaSkpDQogICAgaGlzdChDb25jX3BvbDFfNlssaV0sbWFpbiA9IHBhc3RlMCgiY29uY19wb2wiLGkpKQ0KICAgIGJveHBsb3QoQ29uY19wb2wxXzZbLGldLG1haW4gPSBwYXN0ZTAoImNvbmNfcG9sIixpKSkNCn0NCmBgYA0KDQojIyMgUGxvdHRpbmcgQ29uY2VudHJhdGlvbiBmb3IgTk8yLCBPMyBhbmQgVmlzaWJpbGl0eQ0KYGBge3J9DQojcGFyKG1mcm93PWMoMiwgMykpDQpwYXIobWZyb3c9YygzLCAzKSkNCmZvciAoaSBpbiA0OjYpIHsNCiAgICBwbG90KENvbmNfcG9sMV82WyxpXSwgbWFpbiA9IHBhc3RlMCgiY29uY19wb2wiLGkpKQ0KICAgIGhpc3QoQ29uY19wb2wxXzZbLGldLG1haW4gPSBwYXN0ZTAoImNvbmNfcG9sIixpKSkNCiAgICBib3hwbG90KENvbmNfcG9sMV82WyxpXSxtYWluID0gcGFzdGUwKCJjb25jX3BvbCIsaSkpDQp9DQpgYGANCg0KDQoNCiMjIyBQbG90dGluZyBBUUkgZm9yIFBNMi41LCBQTTEwIGFuZCBDTw0KYGBge3J9DQpwYXIobWZyb3c9YygxLCAzKSkNCmZvciAoaSBpbiAxOjMpIHsNCiAgcGxvdChDb25jX3BvbDFfNlssaV0sIG1haW4gPSBwYXN0ZTAoImNvbmNfcG9sIixpKSkNCiAgaGlzdChDb25jX3BvbDFfNlssaV0sbWFpbiA9IHBhc3RlMCgiY29uY19wb2wiLGkpKQ0KICBib3hwbG90KEFRSV9wb2wxXzZbLGldLG1haW4gPSBwYXN0ZTAoIkFRSV9wb2wiLGkpKQ0KfQ0KYGBgDQoNCiMjIyBQbG90dGluZyBBUUkgZm9yIE5PMiwgTzMgYW5kIFZpc2liaWxpdHkNCmBgYHtyfQ0KcGFyKG1mcm93PWMoMSwgMykpDQpmb3IgKGkgaW4gNDo2KSB7DQogIHBsb3QoQ29uY19wb2wxXzZbLGldLCBtYWluID0gcGFzdGUwKCJjb25jX3BvbCIsaSkpDQogIGhpc3QoQ29uY19wb2wxXzZbLGldLG1haW4gPSBwYXN0ZTAoImNvbmNfcG9sIixpKSkNCiAgYm94cGxvdChBUUlfcG9sMV82WyxpXSxtYWluID0gcGFzdGUwKCJBUUlfcG9sIixpKSkNCn0NCg0KYGBgDQoNCiMjIyBBaXIgUXVhbGl0eSBMZXZlbHMNCkNyZWF0ZSBhbiBhaXIgcXVhbGl0eSBpbmRleCBieSBjb252ZXJ0aW5nIG1lYXN1cmVkIHBvbGx1dGFudCBjb25jZW50cmF0aW9ucyBpbnRvIGluZGV4IHZhbHVlcy4NCmJhc2VkIG9uIEFpciBORVBNIChwb2wgMS01KSBhbmQgTlNXIEVQQSBzdGFuZGFyZCAocG9sIDYpDQpTZWUgVVJMOiBodHRwczovL3d3dy5xbGQuZ292LmF1L2Vudmlyb25tZW50L3BvbGx1dGlvbi9tb25pdG9yaW5nL2Fpci1tb25pdG9yaW5nL2Fpci1xdWFsaXR5LWluZGV4DQpWRVJZIEdPT0QgPC0gMC0zMw0KR09PRCAgICAgIDwtIDM0LTY2DQpGQUlSICAgICAgPC0gNjctOTkNClBPT1IgICAgICA8LSAxMDAtMTQ5DQpCQUQgICAgICAgPC0gPjE1MA0KDQojIFBsb3RpbmcgaGlzdG9ncmFtcyBmb3IgcG9sMS02IGNvbmNlbnRyYXRpb24gbGV2ZWxzDQpgYGB7cn0NCiMgZGVmaW5lIG11bHRpcGxlIGdyYXBocyBwbG90dGluZw0KcGFyKG1mcm93PWMoMiwgMykpDQpoaXN0KHBvbDFfY29uYykgICAgIyBwb2wxIFBNMi41DQpoaXN0KHBvbDJfY29uYykgICAgIyBwb2wyIFBNMTANCmhpc3QocG9sM19jb25jKSAgICAjIHBvbDMgQ08NCmhpc3QocG9sNF9jb25jKSAgICAjIHBvbDQgTk8yDQpoaXN0KHBvbDVfY29uYykgICAgIyBwb2w1IE8zDQpoaXN0KHBvbDZfY29uYykgICAgIyBwb2w2IFZpc2liaWxpdHkNCmBgYA0KDQoNCiMjIyBwbG90aW5nIGhpc3RvZ3JhbXMgZm9yIEFRSSBmb3IgcG9sMS02DQpgYGB7cn0NCnBhcihtZnJvdz1jKDIsIDMpKQ0KaGlzdChBUUlfcG9sMSkgICAgIyBwb2wxIFBNMi41DQpoaXN0KEFRSV9wb2wyKSAgICAjIHBvbDIgUE0xMA0KaGlzdChBUUlfcG9sMykgICAgIyBwb2wzIENPDQpoaXN0KEFRSV9wb2w0KSAgICAjIHBvbDQgTk8yDQpoaXN0KEFRSV9wb2w1KSAgICAjIHBvbDUgTzMNCmhpc3QoQVFJX3BvbDYpICAgICMgcG9sNiBWaXNpYmlsaXR5DQpgYGANCg0KIyMjIFBsb3RpbmcgYm94cGxvdHMgZm9yIGNvbmNlbnRyYXRpb24gYW5kIEFRSSBmb3IgcG9sMS1wb2w2DQpgYGB7cn0NCnBhcihtZnJvdz1jKDEsIDIpKQ0KYm94cGxvdChwb2wxX2NvbmMscG9sMl9jb25jLHBvbDNfY29uYyxwb2w0X2NvbmMscG9sNV9jb25jLHBvbDZfY29uYykNCmJveHBsb3QoQVFJX3BvbDEsQVFJX3BvbDIsQVFJX3BvbDMsQVFJX3BvbDQsQVFJX3BvbDUsQVFJX3BvbDYpDQpgYGANCg0KIyMjIE1pbi1NYXggTm9ybWFsaXNhdGlvbiBtZXRob2QNCkRlZmluZWQgYXM6IFggLSBtaW4oWCkpLyhtYXgoWCkgLSBtaW4oWCkpDQpEZWZpbmUgYSBub3JtYWxpemF0aW9uIGZ1bmN0aW9uDQoNCmBgYHtyfQ0Kbm9ybWFsaXplIDwtIGZ1bmN0aW9uKHgpIHsNCiAgICAgcmV0dXJuICgoeCAtIG1pbih4KSkgLyAobWF4KHgpIC0gbWluKHgpKSkNCn0NCmBgYA0KDQojIyMgQXBwbGljYXRpb24gb2YgTWluLU1heCBOb3JtYWxpc2F0aW9uIG1ldGhvZCBmb3IgZWFjaCBwb29sdXRhbnQNCmBgYHtyfQ0KZGF0NV9Ob3JtIDwtIGFzLmRhdGEuZnJhbWUobGFwcGx5KGRhdDVbODoxM10sIG5vcm1hbGl6ZSkpDQpgYGANCg0KQ3JlYXRpbmcgZGF0YWZyYW1lIENPTkNfcG9sMV82IHdpdGggdmFyaWFibGVzIHBvbDFfY29uYywgLi4uLCBwb2w2X2NvbmMgd2hpY2ggd2lsbCBiZSB1c2VkIGxhdGVyDQpgYGB7cn0NCiNDT05DX3BvbDFfNiA8LWRhdGEuZnJhbWUoY2JpbmQocG9sMV9jb25jLHBvbDJfY29uYyxwb2wzX2NvbmMscG9sNF9jb25jLHBvbDVfY29uYyxwb2w2X2NvbmMpKQ0KI2NvbG5hbWVzKENPTkNfcG9sMV82KVsxXSA8LSAiUE0yLjVfKHVnL20zKSIgICAgIyBjaGFuZ2UgdGhlIG5hbWUgb2YgY29sdW1uDQojY29sbmFtZXMoQ09OQ19wb2wxXzYpWzJdIDwtICJQTTEwXyh1Zy9tMykpIiAgICAjIGNoYW5nZSB0aGUgbmFtZSBvZiBjb2x1bW4NCiNjb2xuYW1lcyhDT05DX3BvbDFfNilbM10gPC0gIkNPXyhwcG0pIiAgICAgICAgICMgY2hhbmdlIHRoZSBuYW1lIG9mIGNvbHVtbg0KI2NvbG5hbWVzKENPTkNfcG9sMV82KVs0XSA8LSAiTk8yXyhwcG0pIiAgICAgICAgIyBjaGFuZ2UgdGhlIG5hbWUgb2YgY29sdW1uDQojY29sbmFtZXMoQ09OQ19wb2wxXzYpWzVdIDwtICJPM18ocHBtKSIgICAgICAgICAjIGNoYW5nZSB0aGUgbmFtZSBvZiBjb2x1bW4NCiNjb2xuYW1lcyhDT05DX3BvbDFfNilbNl0gPC0gIlZpc18oMS9NbSkiICAgICAgICMgY2hhbmdlIHRoZSBuYW1lIG9mIGNvbHVtbg0KI2hlYWQoQ09OQ19wb2wxXzYpDQpgYGANCg0KQ3JlYXRpbmcgZGF0NiBkYXRhZnJhbWUgYnkgYmluZGluZyBkYXQ1IHdpdGggQVFJX3BvMSwgLi4uLCBBUUlfcG9sNiB2YXJpYWJsZXMNCmBgYHtyfQ0KZGF0NiA8LWRhdGEuZnJhbWUoY2JpbmQoZGF0NSxBUUlfcG9sMSxBUUlfcG9sMixBUUlfcG9sMyxBUUlfcG9sNCxBUUlfcG9sNSxBUUlfcG9sNikpDQojaGVhZChkYXQ2KQ0KI2NvbG5hbWVzKGRhdDYpDQpgYGANCg0KQ2hhbmdpbmcgIG5hbWVzIG9mIGNvbHVtbnMgZm9yIEFJUSBwb2wxLTYNCmBgYHtyfQ0KQVFJX3BvbDFfNl9uYW1lcyA8LWMoIkFRSV9QTTIuNSIsIkFRSV9QTTEwIiwiQVFJX0NPIiwiQVFJX05PMiIsIkFRSV9PMyIsIkFRSV9WaXMiKQ0KZm9yKGkgaW4gMTo2KXsNCiAgaV9wb2w9aSsxMw0KICBjb2xuYW1lcyhkYXQ2KVtpX3BvbF0gPC0gQVFJX3BvbDFfNl9uYW1lc1tpXQ0KICB9DQpjb2xuYW1lcyhkYXQ2KQ0KYGBgDQoNCiMjIyBBaXIgUXVhbGl0eSBJbmRleCBPdmVyYWxsIChBUUlfT3ZlcmFsbCkgLSBDYWxjdWxhdGlvbg0KQ2FsY3VsYXRlIEFRSV9PdmVyYWxsIGZvciBTb3V0aCBCcmlzYmFuZSAobW9uaXRvcmluZyBTaXRlKSBkZWZpbmVkIGFzIHRoZSBtYXggaG91cmx5IHZhbHVlIG9mIEFRSSBmb3IgZWFjaCBwb2xsdXRhbnQNCmBgYHtyfQ0KQVFJX092ZXJhbGwgPC1BUUlfcG9sMV82WywgIm1heCJdIDwtIGFwcGx5KGRhdDZbLCAxNToxOV0sIDEsIG1heCkgIyBhIHNpbmdsZSBjb2x1bW4gLSB2ZWN0b3IgcmVwcmVzZW50aW5nIG1heCBvZiA2IG9ic2VydmF0aW9uIGZvciBhbGwgcm93cyBmb3IgDQpjbGFzcyhBUUlfT3ZlcmFsbCkgICMgQUlRX092ZXJhbGwgaXMgbnVtZXJpYw0Kc3VtbWFyeShBUUlfT3ZlcmFsbCkNCmBgYA0KDQoNCiMjIyBBc3Nlc3Npbmcgbm9ybWFsaXR5IG9mIEFRSV9PdmVyYWxsIGRhdGENClRoZSBkYXRhIG5vcm1hbGl0eSB3YXMgYXNzZXNzZWQgZm9yIG9yaWdpbmFsIGRhdGFzZXQgKGkuZS4sbm8gdHJhbnNmb3JtYXRpb24gYXBwbGllZCkgdXNpbmcgaGlzdG9ncmFtIGFuZCBxcXBsb3QNCmBgYHtyfQ0KaGlzdChBUUlfT3ZlcmFsbCkNCiMgdGhlIGhpc3RvZ3JhbSBmb3IgQUlRX092ZXJhbGwgc2hvd3MgcmlnaHQgc2tld2VuZXNzLg0KcXFub3JtKEFRSV9PdmVyYWxsKTtxcWxpbmUoQVFJX092ZXJhbGwsIGNvbCA9IDIpICAjIGNoZWNraW5nIGRhdGEgbm9ybWFsaXR5IHVzaW5nIHEtcSBwbG90DQoNCg0KYGBgDQoNCiMjIyBsb2cxMCB0cmFuc2Zvcm1hdGlvbiBvZiBBUUlfT3ZlcmFsbCBkYXRhIA0KQVFJX092ZXJhbGwgZGF0YSB3ZXJlIHRyYW5zZm9ybWVkIHVzaW5nIGxvZywgbG4gYW5kIG90aGVyIGZ1bmN0aW9uIHRvIHNlZSBpZiB0aGVzZSBpbXByb3ZlIE5vcm1hbGlsaXR5IG9mIHRoYSBkYXRhDQpgYGB7cn0NCmxvZ19BUUlfT3ZlcmFsbCA8LSBsb2cxMChBUUlfT3ZlcmFsbCkgICMgdGhlIGhpc3RvZ3JhbSBmb3IgQUlRX092ZXJhbGwgc2hvd3MgcmlnaHQgc2tld2VuZXNzLg0KaGlzdChsb2dfQVFJX092ZXJhbGwpDQpxcW5vcm0obG9nX0FRSV9PdmVyYWxsKTtxcWxpbmUobG9nX0FRSV9PdmVyYWxsLCBjb2wgPSAyKSAgIyBpbXByb3ZlZCByZXN1bHQtPiBjbG9zZXIgdG8gbm9ybWFsIGRpc3RyaWJ1dGlvbg0KDQpgYGANCg0KIyMjIG5hdHVyYWwgbG4gdHJhbnNmb3JtYXRpb24gb2YgQVFJX092ZXJhbGwgZGF0YQ0KQXMgYWJvdmVtIHRyYW5zZm9ybWF0aW9uIHVzaW5nIG5hdHVyYWwgbG9nIGZ1bmN0aW9uDQpgYGB7cn0NCmxuX0FRSV9PdmVyYWxsIDwtIGxvZyhBUUlfT3ZlcmFsbCkgIyBuYXR1cmFsIGxvZyBvZiBBUUlfT3ZlcmFsbA0KaGlzdChsbl9BUUlfT3ZlcmFsbCkNCnFxbm9ybShsbl9BUUlfT3ZlcmFsbCk7cXFsaW5lKGxuX0FRSV9PdmVyYWxsLCBjb2wgPSAyKSAgIyBpbXByb3ZlZCByZXN1bHQtPiBjbG9zZXIgdG8gbm9ybWFsIGRpc3RyaWJ1dGlvbg0KDQpgYGANCg0KIyMjIEFwcGxpY2F0aW9uIG9mIHEtcSBwbG90cyB0byBhc3NlcyBBUUlfT3ZlcmFsbCBkYXRhIG5vcm1hbGl0eQ0KYGBge3J9DQpxcW5vcm0oQVFJX092ZXJhbGwpO3FxbGluZShBUUlfT3ZlcmFsbCwgY29sID0gMikgICMgbm90IG5vcm1hbCBkaXN0cmlidXRpb24NCnFxbm9ybShsb2dfQVFJX092ZXJhbGwpO3FxbGluZShsb2dfQVFJX092ZXJhbGwsIGNvbCA9IDIpICAjIGltcHJvdmVkIHJlc3VsdC0+IGNsb3NlciB0byBub3JtYWwgZGlzdHJpYnV0aW9uDQpgYGANCioqU1VNTUFSWSoqDQpUaGVyZSBpcyBub3QgbXVjaCBkaWZmZXJlbmNlIGJldHdlZW4gbG9nMTAgYW5kIGxuIHRyYW5zZm9ybWF0aW9uIGFwcGxpZWQgdG8gQVFJX092ZXJhbGwgdmFyaWFibGUNCg0KIyMjIEFRSV9PdmVyYWwgZGF0YSBOT1JNQUxJU0FUSU9OIC0gQ2VudGVyaW5nDQpNZWFuIGNlbnRlcmluZw0KYGBge3J9DQpjZW50ZXJfQVFJX092ZXJhbGwgPC1zY2FsZShBUUlfT3ZlcmFsbCwgY2VudGVyID0gVFJVRSwgc2NhbGUgPSBGQUxTRSkNCiMgY2VudGVyX0FRSV9PdmVyYWxsDQpoaXN0KGNlbnRlcl9BUUlfT3ZlcmFsbCkNCnFxbm9ybShjZW50ZXJfQVFJX092ZXJhbGwpO3FxbGluZShjZW50ZXJfQVFJX092ZXJhbGwsIGNvbCA9IDIpICAjIERpZCBub3QgaW1wcm92ZSBub3JtYWlsaXR5DQojIE90aGVyIHRyYW5zZm9ybWF0aW9uLCBlLmcsIG1pbi1tYXgsIHogU2NvcmUgU3RhbmRhcmRpc2F0aW9uIGFsc28gdGVzdGVkLCBwcm92aWRpbmcgc2ltaWxhciByZXN1bHRzIChub3Qtbm9ybWFsIGRpc3RyaWJ1dGlvbikNCmBgYA0KDQoNCiMjIyBDQUxDVUxBVElORyBBSVIgUE9MTFVUSU9OIElOREVYDQpDcmVhdGUgYW4gYWlyIHF1YWxpdHkgaW5kZXggYnkgY29udmVydGluZyBtZWFzdXJlZCBwb2xsdXRhbnQgY29uY2VudHJhdGlvbnMgaW50byBpbmRleCB2YWx1ZXMuDQpiYXNlZCBvbiBBaXIgTkVQTSAocG9sIDEtNSkgYW5kIE5TVyBFUEEgc3RhbmRhcmQgKHBvbCA2KQ0KRm9yIGRldGFpbHMgc2VlOiBVUkw6IGh0dHBzOi8vd3d3LnFsZC5nb3YuYXUvZW52aXJvbm1lbnQvcG9sbHV0aW9uL21vbml0b3JpbmcvYWlyLW1vbml0b3JpbmcvYWlyLXF1YWxpdHktaW5kZXgNCg0KRGVmaW5pdGlvbiBvZiB0aGUgQVFJIExldmVsczoNClZFUlkgR09PRCA8LSAwLTMzDQpHT09EICAgICAgPC0gMzQtNjYNCkZBSVIgICAgICA8LSA2Ny05OQ0KUE9PUiAgICAgIDwtIDEwMC0xNDkNCkJBRCAgICAgICA8LSA+MTUwDQoNCiMjIyBDYWxjdWxhdGluZyBBUUlfT3ZlcmFsbCBQb2xsdXRpb24gTGV2ZWxzDQpUaGUgQVFJX092ZXJhbGwgdmFsdWVzIHdlcmUgYXNpZ25lZCB0byBBaXIgUXVhbGl0eSBMZXZlbHMgd2l0aCBjdXQtcG9pbnRzIChyYW5nZSkgZGV0ZXJtaW5lZCBieSBBaXIgTkVQTSBTdGFuZGFyZC4NCmBgYHtyfQ0KQVFJX092ZXJhbGxfTGV2ZWxzIDwtIGN1dChBUUlfT3ZlcmFsbCwgYnJlYWtzPWMoMCwzMyw2Niw5OSwxNDksSW5mKSwgDQogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJWRVJZIEdPT0QiLCJHT09EIiwiRkFJUiIsIlBPT1IiLCJCQUQiKSkNCmBgYA0KDQojIyMgR3JhcGhpY2FsIFJlcHJlc2VudGF0aW9uIG9mIEFpciBRdWFsaXR5IEluZGV4IGFuZCBMZXZlbHMuDQpJbiBzb21lIGNhc2VzIGl0IG1heSBiZSByZXF1aXJlZCB0byBwcmVzZW50IHJlbGF0aW9uc2hpcCBiZXR3ZW4gQVFJX092ZXJhbGwgYW5kIGNvcnJlc3BvbmRpbmcgQWlyIFF1YWxpdHkgTGV2ZWxzIHVzaW5nIHNpbXBsZSBncmFwaGljYWwgZm9ybSBhcyBkZW1vbnN0cmF0ZWQgaW4gYSBzaW1wbGUgZ3JhcGggdXNpbmcgZGF0YXNldCBwcmVwYXJlZCBpbiB0aGlzIGFzc2lnbm1lbnQuIA0KYGBge3J9DQpwYXIobWZyb3c9YygxLCAxKSkNCmhpc3QoQVFJX092ZXJhbGwpDQpgYGANCg0KIyMjIEFkZCBBUUlfT3ZlcmFsbCBhbmQgQVFJX0xldmVscyB0byBkYXRhZnJhbWUgZGF0NiBhbmQgYXNzaWduIGl0IGFzIGRhdDcNCmBgYHtyfQ0KZGF0NzwtY2JpbmQoZGF0NixBUUlfT3ZlcmFsbCwgQVFJX092ZXJhbGxfTGV2ZWxzKQ0KY29sbmFtZXMoZGF0NikNCmNvbG5hbWVzKGRhdDcpDQpoZWFkKGRhdDcpDQpwbG90KGRhdDckQVFJX092ZXJhbGwsDQogICAgIG1haW4gPSAiSG91cmx5IEFpciBRdWFsaXR5IEluZGV4IGZvciBCcmlzYmFuZSBpbiAyMDE2IiwNCiAgICAgeGxhYiA9ICJob3VybHkgaW5kZXgiLA0KICAgICB5bGFiID0gIkFRSSBbLV0iLA0KICAgICB4bGltPWMoMCwgOTAwMCksDQogICAgIHlsaW09YygwLCAyMDApDQogICAgICkNCiNtaW5vci50aWNrKG54ID0gNCwgbnk9NSwgdGljay5yYXRpbz0wLjMpDQpsZWdlbmQoInRvcHJpZ2h0IiwgaW5zZXQ9LjA1LCB0aXRsZT0iQVFJIFJhbmdlICh1cHBlciBib3VuZHMpIiwNCiAgICAgICAgIGMoIlZFUlkgR09PRCIsIkdPT0QiLCJGQUlSIiwiQkFEIiksIGNvbD1jKCJncmVlbiIsICJibHVlIiwgInllbGxvdyIsICJyZWQiKSwNCiAgICAgICAgIGx0eT1jKDIsMiwyLDIpLCBsd2Q9YygzLCAzLDMsMyksDQogICAgICAgICBjZXggPSAwLjc3LA0KICAgICAgICAgaG9yaXo9VFJVRSkNCg0KYWJsaW5lKGg9YygzMyw2Niw5OSwxNDkpLCBjb2w9YygiZ3JlZW4iLCAiYmx1ZSIsICJ5ZWxsb3ciLCAicmVkIiksIGx0eT1jKDIsMiwyLDIpLCBsd2Q9YygzLCAzLDMsMykpDQoNCg0KYGBgDQoNCioqU1VNTUFSWSoqDQpUaGUgZ3JhcGggYWJvdmUgc2hvd3MgaG91cmx5IGF2ZXJhZ2VzIGZvciBBaXIgUXVhbGl0eSBJbmRleCAoQVFJX092ZXJhbGwpIG9ic2VydmVkIGF0IFNvdXRoIEJyaXNiYW5lIG92ZXIgdGhlIGVudGlyZSB5ZWFyIDIwMTYuIFRoZSByZXN1bHRzIGluZGljYXRlIHRoYXQgdGhlIEFRSSB3YXMgYXQgVkVSWSBHT09EIG9yIEdPT0QgbGV2ZWxzIChpLmUuLCBtYWpvcml0eSBvZiB0aGUgdmFsdWVzIGFyZSBsb2NhdGVkIGJlbG93IHRoZSBncmVlbiBhbmQgYmx1ZSBkYXNoZWQgbGluZXMpLCBob3dldmVyIHRoZXJlIHdhcyBhbHNvIGEgc2lnbmlmaWNuYXQgbnVtYmVyIG9mIGRhdGEgd2l0aCBGQUlSLCBhbmQgc2V2ZXJhbCB3aXRoIEJBRCBBUUkgbGV2ZWxzLiBUaGVzZSB3b3VsZCBiZSBvZiBwYXJ0aWN1bGF0ZSBpbnRlcmVzdCwgZm9yIGVheG1wbGUgZnJvbSB0aGUgc291cmNlIGFwcG9ydGlvbm1lbnQgcG9pbnQgb2YgdmlldyBvciBzdHVkeWluZyB0ZWggY29uZGl0aW9ucyB3aGVuIHRoZXNlIGV2ZW50cyBvY2N1ci4gDQoNClRoZXNlIHR5cGUgb2YgZGF0YSBhbmFseXNpcyBhbmQgZ3JhcGhpY2FsIHJlcHJlc2VudGF0aW9uIGlzIHVzZWZ1bGwgZm9yIGZ1cnRoZXIgYXNzZXNzbWVudCBhbmQgYWN0aW9ucyBpbiByZWxhdGlvbiB0byBBaXIgUXVhbGl0eSBtYW5hZ2VtZW50LiANCg0KPGJyPg0KPGJyPg0K