Note:

-An R Notebook is an R Markdown document with chunks that can be executed independently and interactively, with output visible immediately beneath the input.

-Notebook output are available as HTML, PDF, Word, or Latex.

-This Notebook as HTML is preferably open with Google Chrome.

-R-Code can be extracted as Rmd file under the button “Code” in the notebook.

-This Notebook using iterative development. It means the process starts with a simple implementation of a small set of idea requirements and iteratively enhances the evolving versions until the complete version is implemented and perfect.


Welcome

Data Analyst-> Analysis of data

Analysis of data is a process of inspecting, cleaning, transforming, and modeling data with the goal of highlighting useful information, suggesting conclusions, and supporting decision making. (wikipedia.com)

The 80/20 data science dilemma:

data scientists spend 80% of their time cleaning and manipulating data and only 20% of their time actually analyzing it." (forbes.com)


#Link: https://www.forbes.com/sites/gilpress/2016/03/23/data-preparation-most-time-consuming-least-enjoyable-data-science-task-survey-says/#726c61386f63

Data analytics lifecycle in R:


#Link: https://r4ds.had.co.nz/explore-intro.html


Messy vs Tidy data

“Happy families are all alike; every unhappy family is unhappy in its own way.”-Leo Tolstoy

“Tidy datasets are all alike, but every messy dataset is messy in its own way.”-Hadley Wickham (Chief Data Scientist, Rstudio)

Five Elements of Messy Data:

  • Column headers are values, not variable names.
  • Multiple variables are stored in one column.
  • Variables are stored in both rows and columns.
  • Multiple types of observational units are stored in the same table.
  • A single observational unit is stored in multiple tables.

See the Tidy Data article for methods to fix messy data from Journal of Statistical Software.

Three Elements of Tidy Data:

  • Each variable forms a column
  • Each observation forms a row
  • Each type of observational unit forms a table.

#Link: https://r4ds.had.co.nz/tidy-data.html

This Tidy Data principles follows the same principles as Codd’s normalization (focus on a single dataset versus connected datasets like relational databases). More about Codd’s normalization process

Note :

For messy data is OpenRefine (formerly Google Refine) a powerful tool for working with.


Some R Background

Data frame

R objects: data frame

In R a variables are not declared as some data type (numeric, integer, usw), but an objects. The data type of the R-object becomes the data type of the variable. A data frame is the most common way of storing data in R. It makes data analysis easier.

Following are the characteristics of a data frame.

  • The column names should be non-empty.
  • The row names should be unique.
  • The data stored in a data frame can be of numeric, factor or character type.
  • Each column should contain same number of data items.

#Link: https://www.kaggle.com/timolee/a-home-for-pandas-and-sklearn-beginner-how-tos

Summary of data frame.

  • Spreadsheet style data
  • 2 dimensions
  • rows
  • columns
  • Can contain heterogenous data
  • All columns must be of equal length

Useful Data Frame Functions:

  • head() - shows first 6 rows
  • tail() - shows last 6 rows
  • dim() - returns the dimensions of data frame (i.e. number of rows and number of columns)
  • nrow() - number of rows
  • ncol() - number of columns
  • str() - structure of data frame - name, type and preview of data in each column
  • names() - shows the names attribute for a data frame, which gives the column names.
  • sapply(dataframe, class) - shows the class of each column in the data frame

The structures of mtcars dataset. * str() * class() * attributes()

Data frame indexing/subsetting:

Import a data frame dataset.

Creating a data frame.


#Use the function data.frame() to construct a data frame. 
#Pass the vectors name, type, diameter, rotation and rings 
#as arguments to data.frame(), in this order.

# Definition of vectors
name <- c("Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", 
"Uranus",
 "Neptune")
type <- c("Terrestrial planet", "Terrestrial planet", 
"Terrestrial planet", 
          "Terrestrial planet", "Gas giant", "Gas giant", 
          "Gas giant", "Gas giant")
diameter <- c(0.382, 0.949, 1, 0.532, 11.209, 9.449, 4.007, 3.883)
rotation <- c(58.64, -243.02, 1, 1.03, 0.41, 0.43, -0.72, 0.67)
rings <- c(FALSE, FALSE, FALSE, FALSE, TRUE, TRUE, TRUE, TRUE)

# Create a data frame from the vectors
planets_df <-data.frame(name,type,diameter,rotation,rings)
planets_df

Investigate the structure of data frame.


str(planets_df)
class(planets_df)
attributes(planets_df)

Selecting data frame elements.


# Diameter of Mercury (row 1, column 3)
planets_df[1,3]

# Fata for Mars (entire fourth row)
planets_df[4,]

# Select first 5 values of diameter column
planets_df[1:5,"diameter"]

Only planets with rings.


# Select the rings variable from planets_df
rings_vector <- planets_df$rings

# Print out rings_vector
rings_vector

# Adapt the code to select all columns for planets with rings
planets_df[rings_vector,]

Only planets with rings but shorter.


# Select planets with rings
subset(planets_df,rings=="TRUE")

Sorting data frame.


cevi<-c(10,100,9)
order(cevi)
cevi[order(cevi)]

positions <-  order(planets_df$diameter)
# Use positions to sort planets_df
planets_df[positions,]

Add column and rows in data frame.


name<-c("cevi","herdian")
note<-c(1,2)

df_1<-data.frame(name, note)
df_1


name<-c("berlin","hamburg")
note<-c(3,4)

df_2<-data.frame(name, note)
df_2

df_3<-rbind(df_1,df_2)
df_3


df_3$address<-c("Indonesia","USA","Germany","Italy")

df_3

More transformation such as filter, select, arrange, mutate, summarise , and pipe operator please go to my r-pedia

Special values

NA, NULL, ±Inf and NaN are special values in R. The meaning and differences between them are below.

  • NA Stands for not available. NA is a placeholder for a missing value.

NA + 1
sum(c(NA, 1, 2))
median(c(NA, 1, 2, 3), na.rm = TRUE)
length(c(NA, 2, 3, 4))
3 == NA
NA == NA
TRUE | NA
  • NULL means no class (its class is NULL) and has length 0 so it does not take up any space in a vector.

length(c(1, 2, NULL, 4))
sum(c(1, 2, NULL, 4))
x <- NULL
c(x, 2)
  • Inf Stands for infinity (just for numeric).

pi/0
2 * Inf
Inf - 1e+10
Inf + Inf
3 < -Inf
Inf == Inf
  • NaN Stands for not a number (the result is unknown)

NaN + 1
exp(NaN)

Relational data

There are three types designed to work with relational data:

Data transformation


Checklist for Data Cleaning

1-Missing values

  • A placeholder for a datum of which the type is known but its value isn’t.
  • It is impossible to perform statistical analysis

Use case 1: Identify missing values

  1. Statistical analysis error

age <- c(23, 16, NA)
mean(age)
## [1] NA
mean(age, na.rm = TRUE)
## [1] 19.5
  1. Identify the NA value

complete.cases(age)

#or

is.na(age)
  1. Remove NA values (without NA values)

na.omit(age)

Use case 2: Recode missing values with mean

  1. Test for missing values NA

x <- c(1:5, NA, 9:11, NA)
is.na(x)

df <- data.frame(col1 = c(1:3, NA),
                 col2 = c("this", NA,"is", "text"), 
                 col3 = c(TRUE, FALSE, TRUE, TRUE), 
                 col4 = c(2.5, 4.2, 3.2, NA),
                 stringsAsFactors = FALSE)
is.na(df)

To identify the location of the NA.


which(is.na(x))
sum(is.na(df))

#or for data frame

colSums(is.na(df))
  1. Recode missing values NA Use normal subsetting and assignment operations.

# recode missing values with the mean
# vector with missing data
x <- c(1:4, NA, 6:7, NA)
x
## [1]  1  2  3  4 NA  6  7 NA

x[is.na(x)] <- mean(x, na.rm = TRUE)

round(x, 2)
## [1] 1.00 2.00 3.00 4.00 3.83 6.00 7.00 3.83

# data frame that codes missing values as 99
df <- data.frame(col1 = c(1:3, 99), col2 = c(2.5, 4.2, 99, 3.2))

# change 99s to NAs
df[df == 99] <- NA
df
##   col1 col2
## 1    1  2.5
## 2    2  4.2
## 3    3   NA
## 4   NA  3.2

Recode missing values in a single data frame.

For example, here we recode the missing value in col4 with the mean value of col4.


# data frame with missing data
df <- data.frame(col1 = c(1:3, NA),
                 col2 = c("this", NA,"is", "text"), 
                 col3 = c(TRUE, FALSE, TRUE, TRUE), 
                 col4 = c(2.5, 4.2, 3.2, NA),
                 stringsAsFactors = FALSE)
                 
df$col4[is.na(df$col4)] <- mean(df$col4, na.rm = TRUE)
df
##   col1 col2  col3 col4
## 1    1 this  TRUE  2.5
## 2    2 <NA> FALSE  4.2
## 3    3   is  TRUE  3.2
## 4   NA text  TRUE  3.3
  1. Exclude missing values NA

# A vector with missing values
x <- c(1:4, NA, 6:7, NA)

# including NA values will produce an NA output
mean(x)
## [1] NA

# excluding NA values will calculate the mathematical operation for all non-missing values
mean(x, na.rm = TRUE)
## [1] 3.833333

For complete observations.


# data frame with missing values
df <- data.frame(col1 = c(1:3, NA),
                 col2 = c("this", NA,"is", "text"), 
                 col3 = c(TRUE, FALSE, TRUE, TRUE), 
                 col4 = c(2.5, 4.2, 3.2, NA),
                 stringsAsFactors = FALSE)

df
##   col1 col2  col3 col4
## 1    1 this  TRUE  2.5
## 2    2 <NA> FALSE  4.2
## 3    3   is  TRUE  3.2
## 4   NA text  TRUE   NA

complete.cases(df)
## [1]  TRUE FALSE  TRUE FALSE

# subset with complete.cases to get complete cases
df[complete.cases(df), ]
##   col1 col2 col3 col4
## 1    1 this TRUE  2.5
## 3    3   is TRUE  3.2

# or subset with `!` operator to get incomplete cases
df[!complete.cases(df), ]
##   col1 col2  col3 col4
## 2    2 <NA> FALSE  4.2
## 4   NA text  TRUE   NA

Alternativ with omit.na() function



# or use na.omit() to get same as above
na.omit(df)
##   col1 col2 col3 col4
## 1    1 this TRUE  2.5
## 3    3   is TRUE  3.2

Use case 3: Exploring missing values with naniar package

naniar package provides principled, tidy ways to summarise, visualise, and manipulate missing data with minimal deviations from the workflows in ggplot2 and tidy data.

http://naniar.njtierney.com/index.html

Use case 4: Missing Value Treatment

Data prep and pattern:

Using BostonHousing dataset in mlbench package. The original BostonHousing data doesn’t have missing values, but we injected this dataset with mising values.

#install.packages("mlbench")
#library(mlbench)

# initialize the data
data ("BostonHousing", package="mlbench")
original <- BostonHousing  # backup original data

# Introduce missing values randomly
set.seed(100)
BostonHousing[sample(1:nrow(BostonHousing), 40), "rad"] <- NA #40 NA in "rad"
BostonHousing[sample(1:nrow(BostonHousing), 40), "ptratio"]<-NA #40 NA in "rad"
BostonHousing

The missing values have been injected. Identify the ‘missings’ pattern using ´mice::md.pattern`.


# Pattern of missing values
#install.packages("mice")
#library(mice)
md.pattern(BostonHousing)  # pattern or missing values in data.

As we know there are 40 NA values in “rad” and also “ptratio”.

4 ways of dealing with missing values:

  1. Deleting the observations.
  • Have sufficent data points, so the model doesn’t lose power.
  • Not to introduce bias (meaning, disproportionate or non-representation of classes).

Using na.action=na.omit


# Example
lm(medv ~ ptratio + rad, data=BostonHousing, na.action=na.omit)

#Simple example:

cevi<-c(1,2,NA)
cevi

cevi_new<-na.omit(cevi)
cevi_new
  1. Deleting the variable.

Delete the variable with missing values (“rad” and also “ptratio”).

  1. Imputation with mean / median / mode.

Replacing the missing values with the mean / median / mode is a crude way of treating missing values. Depending on the context, like if the variation is low or if the variable has low leverage over the response, such a rough approximation is acceptable and could possibly give satisfactory results.

Change the missing values using Hmisc package.

#install.packages("Hmisc")
#library(Hmisc)
impute(BostonHousing$ptratio, mean)  # replace with mean
impute(BostonHousing$rad, mean)  # replace with mean

#or

#impute(BostonHousing$ptratio, median)  # median
#impute(BostonHousing$ptratio, 20)  # replace specific number

Or impute manually.


BostonHousing$ptratio[is.na(BostonHousing$ptratio)]<-mean(BostonHousing$ptratio, na.rm = TRUE)

Identify the accuracy when it is imputed with mean using DMwR package.

#install.packages("DMwR")
#library(DMwR)
actuals <- original$ptratio[is.na(BostonHousing$ptratio)]
predicteds <- rep(mean(BostonHousing$ptratio, na.rm=T), length(actuals))
regr.eval(actuals, predicteds)

More info about this functions see

https://www.rdocumentation.org/packages/DMwR/versions/0.4.1/topics/regr.eval

  1. Prediction.

Prediction is most advanced method to impute your missing values and includes different approaches such as: kNN Imputation, rpart, and mice.

4.1 kNN Imputation

DMwR::knnImputation uses k-Nearest Neighbours approach to impute missing values.

#install.packages("DMwR")
#library(DMwR)

knnOutput <- knnImputation(BostonHousing[, !names(BostonHousing) %in% "medv"])  # perform knn imputation.

anyNA(knnOutput)

Lets compute the accuracy.


actuals <- original$ptratio[is.na(BostonHousing$ptratio)]
predicteds <- knnOutput[is.na(BostonHousing$ptratio), "ptratio"]
regr.eval(actuals, predicteds)

** Note: ** The mean absolute percentage error (mape) has improved by ~ 39% compared to the imputation by mean. Good

4.2 rpart

The limitation with DMwR::knnImputation is that it sometimes may not be appropriate to use when the missing value comes from a factor variable. Both rpart and mice has flexibility to handle that scenario.

The idea here is we are going to use rpart to predict the missing values instead of kNN. To handle factor variable, we can set the method=class while calling rpart(). For numeric, we use, method=anova. Here again, we need to make sure not to train rpart on response variable (medv).

#install.packages("rpart")
#library(rpart)
class_mod <- rpart(rad ~ . - medv, data=BostonHousing[!is.na(BostonHousing$rad), ], method="class", na.action=na.omit)  # since rad is a factor
anova_mod <- rpart(ptratio ~ . - medv, data=BostonHousing[!is.na(BostonHousing$ptratio), ], method="anova", na.action=na.omit)  # since ptratio is numeric.
rad_pred <- predict(class_mod, BostonHousing[is.na(BostonHousing$rad), ])
ptratio_pred <- predict(anova_mod, BostonHousing[is.na(BostonHousing$ptratio), ])

Lets compute the accuracy for ptratio.


actuals <- original$ptratio[is.na(BostonHousing$ptratio)]
predicteds <- ptratio_pred
regr.eval(actuals, predicteds)

Note: The mean absolute percentage error (mape) has improved additionally by another ~ 30% compared to the knnImputation. Very Good.

Accuracy for rad.


actuals <- original$rad[is.na(BostonHousing$rad)]
predicteds <- as.numeric(colnames(rad_pred)[apply(rad_pred, 1, which.max)])
mean(actuals != predicteds)  # compute misclass error.

Note: This yields a mis-classification error of 25%. Not bad for a factor variable!

4.3 mice

mice short for Multivariate Imputation by Chained Equations is an R package that provides advanced features for missing value treatment.

#install.packages("mice")
#library(mice)
miceMod <- mice(BostonHousing[, !names(BostonHousing) %in% "medv"], method="rf")  # perform mice imputation, based on random forests.
miceOutput <- complete(miceMod)  # generate the completed data.
anyNA(miceOutput)

Lets compute the accuracy of ptratio.


actuals <- original$ptratio[is.na(BostonHousing$ptratio)]
predicteds <- miceOutput[is.na(BostonHousing$ptratio), "ptratio"]
regr.eval(actuals, predicteds)

The mean absolute percentage error (mape) has improved additionally by ~ 48% compared to the rpart. Excellent!.

Lets compute the accuracy of rad.


actuals <- original$rad[is.na(BostonHousing$rad)]
predicteds <- miceOutput[is.na(BostonHousing$rad), "rad"]
mean(actuals != predicteds)  # compute misclass error.

The mis-classification error reduced to 15%, which is 6 out of 40 observations. This is a good improvement compared to rpart’s 25%.

More info:

This mice approach based on [Multivariate Imputation by Chained Equations in R journal][https://www.jstatsoft.org/article/view/v045i03]

Note: Though we have an idea of how each method performs, there is not enough evidence to conclude which method is better or worse. But these are definitely worth testing out the next time you impute missing values.

2-Special values

Special values are a values that are not an element of the mathematical set of real numbers (???) (example: ±Inf and NaN).

A function to check special values in a data.frame:

age<-c(21,42,33,18,21,NA,Inf,NaN)
height<-c(111,112,113,114,115,116,117,118)
person<-data.frame(age,height)

is.finite(c(1, Inf, NaN, NA))

is.special <- function(x){
if (is.numeric(x)) !is.finite(x) else is.na(x)
}

sapply(person, is.special)

person[sapply(person, is.special),]

3-Duplicate values

  • R base functions:

    • duplicated(): for identifying duplicated elements and
    • unique(): for extracting unique elements
  • distinct() function from dplyr package to remove duplicate rows in a data frame.

#Link: https://www.datanovia.com/en/lessons/identify-and-remove-duplicate-data-in-r/

Package.

install.packages("tidyverse")
library(tidyverse)

Dataset.


my_data <- as_tibble(iris) #ribble aren`t different from dataframe, just a litte different aspekt.
my_data

Find and drop duplicate elements.


x <- c(1, 1, 4, 5, 4, 6)
duplicated(x)

#Extract duplicate elements:
x[duplicated(x)]

#Remove duplicated elements:
x[!duplicated(x)]

Remove duplicate rows from a data frame.


# Remove duplicates based on Sepal.Width columns
my_data[!duplicated(my_data$Sepal.Width), ]

Extract unique elements.


x <- c(1, 1, 4, 5, 4, 6)
unique(x)

#apply unique() on a data frame:
unique(my_data)

Remove duplicate rows in a data frame.


my_data %>% distinct()

Remove duplicate rows based on certain columns (variables).


# Remove duplicated rows based on Sepal.Length
my_data %>% distinct(Sepal.Length, .keep_all = TRUE)

# Remove duplicated rows based on 
# Sepal.Length and Petal.Width
my_data %>% distinct(Sepal.Length, Petal.Width, .keep_all = TRUE)

#The option .kep_all is used to keep all variables in the data.

Summary:

  • Remove duplicate rows based on one or more column values: my_data %>% dplyr::distinct(Sepal.Length)
  • R based function: unique(my_data)
  • Identify duplicate values: duplicated(my_data)

4-Outliers


Change log update

  • 28.12.2018
  • 30.12.2018


License

MIT

LS0tDQp0aXRsZTogIkRhdGEgY2xlYW5pbmcgMTAxIGluIFIiDQpzdWJ0aXRsZTogIkNoZWNrbGlzdCBmb3IgRGF0YSBDbGVhbnNpbmciDQphdXRob3I6ICJDZXZpIEhlcmRpYW4sIEIuIFNjIg0KZGF0ZTogIjI4LjEyLjIwMTgiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgaGlnaGxpZ2h0OiBweWdtZW50cw0KICAgIHRoZW1lOiBjb3Ntbw0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA1DQogICAgdG9jX2Zsb2F0OiB5ZXMNCi0tLQ0KDQo8QnI+DQoNCioqTm90ZToqKg0KDQotQW4gUiBOb3RlYm9vayBpcyBhbiBSIE1hcmtkb3duIGRvY3VtZW50IHdpdGggY2h1bmtzIHRoYXQgY2FuIGJlIGV4ZWN1dGVkIGluZGVwZW5kZW50bHkgYW5kIGludGVyYWN0aXZlbHksIHdpdGggb3V0cHV0IHZpc2libGUgaW1tZWRpYXRlbHkgYmVuZWF0aCB0aGUgaW5wdXQuDQoNCi1Ob3RlYm9vayBvdXRwdXQgYXJlIGF2YWlsYWJsZSBhcyBIVE1MLCBQREYsIFdvcmQsIG9yIExhdGV4LiANCg0KLVRoaXMgTm90ZWJvb2sgYXMgSFRNTCBpcyBwcmVmZXJhYmx5IG9wZW4gd2l0aCBHb29nbGUgQ2hyb21lLg0KDQotUi1Db2RlIGNhbiBiZSBleHRyYWN0ZWQgYXMgUm1kIGZpbGUgdW5kZXIgdGhlIGJ1dHRvbiAiQ29kZSIgaW4gdGhlIG5vdGVib29rLg0KDQotVGhpcyBOb3RlYm9vayB1c2luZyBpdGVyYXRpdmUgZGV2ZWxvcG1lbnQuIEl0IG1lYW5zIHRoZSBwcm9jZXNzIHN0YXJ0cyB3aXRoIGEgc2ltcGxlIGltcGxlbWVudGF0aW9uIG9mIGEgc21hbGwgc2V0IG9mIGlkZWEgcmVxdWlyZW1lbnRzIGFuZCBpdGVyYXRpdmVseSBlbmhhbmNlcyB0aGUgZXZvbHZpbmcgdmVyc2lvbnMgdW50aWwgdGhlIGNvbXBsZXRlIHZlcnNpb24gaXMgaW1wbGVtZW50ZWQgYW5kIHBlcmZlY3QuDQoNCjxCcj4NCg0KI1dlbGNvbWUNCg0KKipEYXRhIEFuYWx5c3QtPiBBbmFseXNpcyBvZiBkYXRhKioNCg0KPkFuYWx5c2lzIG9mIGRhdGEgaXMgYSBwcm9jZXNzIG9mIGluc3BlY3RpbmcsIGNsZWFuaW5nLCB0cmFuc2Zvcm1pbmcsIGFuZCBtb2RlbGluZw0KZGF0YSB3aXRoIHRoZSBnb2FsIG9mIGhpZ2hsaWdodGluZyB1c2VmdWwgaW5mb3JtYXRpb24sIHN1Z2dlc3RpbmcgY29uY2x1c2lvbnMsIGFuZA0Kc3VwcG9ydGluZyBkZWNpc2lvbiBtYWtpbmcuICh3aWtpcGVkaWEuY29tKQ0KDQoqKlRoZSA4MC8yMCBkYXRhIHNjaWVuY2UgZGlsZW1tYTogKioNCg0KDQoNCj5kYXRhIHNjaWVudGlzdHMgc3BlbmQgODAlIG9mIHRoZWlyIHRpbWUgY2xlYW5pbmcgYW5kIG1hbmlwdWxhdGluZyBkYXRhIGFuZCBvbmx5IDIwJSBvZiB0aGVpciB0aW1lIGFjdHVhbGx5IGFuYWx5emluZyBpdC4iIChmb3JiZXMuY29tKQ0KDQpgYGB7cn0NCg0KI0xpbms6IGh0dHBzOi8vd3d3LmZvcmJlcy5jb20vc2l0ZXMvZ2lscHJlc3MvMjAxNi8wMy8yMy9kYXRhLXByZXBhcmF0aW9uLW1vc3QtdGltZS1jb25zdW1pbmctbGVhc3QtZW5qb3lhYmxlLWRhdGEtc2NpZW5jZS10YXNrLXN1cnZleS1zYXlzLyM3MjZjNjEzODZmNjMNCg0KYGBgDQoNCg0KKipEYXRhIGFuYWx5dGljcyBsaWZlY3ljbGUgaW4gUjogKioNCg0KIVtdKDEucG5nKQ0KYGBge3J9DQoNCiNMaW5rOiBodHRwczovL3I0ZHMuaGFkLmNvLm56L2V4cGxvcmUtaW50cm8uaHRtbA0KDQpgYGANCg0KDQoNCjxCcj4NCg0KDQojTWVzc3kgdnMgVGlkeSBkYXRhIA0KDQo+ICJIYXBweSBmYW1pbGllcyBhcmUgYWxsIGFsaWtlOyBldmVyeSB1bmhhcHB5IGZhbWlseSBpcyB1bmhhcHB5IGluIGl0cyBvd24gd2F5LiItTGVvIFRvbHN0b3kNCg0KPiAiVGlkeSBkYXRhc2V0cyBhcmUgYWxsIGFsaWtlLCBidXQgZXZlcnkgbWVzc3kgZGF0YXNldCBpcyBtZXNzeSBpbiBpdHMgb3duIHdheS4iLUhhZGxleSBXaWNraGFtIChDaGllZiBEYXRhIFNjaWVudGlzdCwgUnN0dWRpbykNCg0KKipGaXZlIEVsZW1lbnRzIG9mIE1lc3N5IERhdGE6ICoqDQoNCiogQ29sdW1uIGhlYWRlcnMgYXJlIHZhbHVlcywgbm90IHZhcmlhYmxlIG5hbWVzLg0KKiBNdWx0aXBsZSB2YXJpYWJsZXMgYXJlIHN0b3JlZCBpbiBvbmUgY29sdW1uLg0KKiBWYXJpYWJsZXMgYXJlIHN0b3JlZCBpbiBib3RoIHJvd3MgYW5kIGNvbHVtbnMuDQoqIE11bHRpcGxlIHR5cGVzIG9mIG9ic2VydmF0aW9uYWwgdW5pdHMgYXJlIHN0b3JlZCBpbiB0aGUgc2FtZSB0YWJsZS4NCiogQSBzaW5nbGUgb2JzZXJ2YXRpb25hbCB1bml0IGlzIHN0b3JlZCBpbiBtdWx0aXBsZSB0YWJsZXMuDQoNClNlZSB0aGUgW1RpZHkgRGF0YV0oaHR0cDovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy90aWR5LWRhdGEucGRmKSBhcnRpY2xlIGZvciBtZXRob2RzIHRvIGZpeCBtZXNzeSBkYXRhIGZyb20gW0pvdXJuYWwgb2YgU3RhdGlzdGljYWwgU29mdHdhcmVdKGh0dHBzOi8vd3d3LmpzdGF0c29mdC5vcmcvaW5kZXgpLg0KDQoqKlRocmVlIEVsZW1lbnRzIG9mIFRpZHkgRGF0YTogKioNCg0KKiBFYWNoIHZhcmlhYmxlIGZvcm1zIGEgY29sdW1uDQoqIEVhY2ggb2JzZXJ2YXRpb24gZm9ybXMgYSByb3cNCiogRWFjaCB0eXBlIG9mIG9ic2VydmF0aW9uYWwgdW5pdCBmb3JtcyBhIHRhYmxlLiANCg0KDQohW10oMi5QTkcpDQoNCmBgYHtyfQ0KDQojTGluazogaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90aWR5LWRhdGEuaHRtbA0KDQpgYGANCg0KDQpUaGlzIFRpZHkgRGF0YSBwcmluY2lwbGVzIGZvbGxvd3MgdGhlIHNhbWUgcHJpbmNpcGxlcyBhcyBDb2RkJ3Mgbm9ybWFsaXphdGlvbiAoZm9jdXMgb24gYSBzaW5nbGUgZGF0YXNldCB2ZXJzdXMgY29ubmVjdGVkIGRhdGFzZXRzIGxpa2UgcmVsYXRpb25hbCBkYXRhYmFzZXMpLiBNb3JlIGFib3V0IFtDb2RkJ3Mgbm9ybWFsaXphdGlvbiBwcm9jZXNzXShodHRwczovL2RsLmFjbS5vcmcvY2l0YXRpb24uY2ZtP2lkPTE3MzQ3MTYpDQoNCioqTm90ZSA6KioNCg0KRm9yIG1lc3N5IGRhdGEgaXMgW09wZW5SZWZpbmUgKGZvcm1lcmx5IEdvb2dsZSBSZWZpbmUpXShodHRwOi8vb3BlbnJlZmluZS5vcmcvKSBhIHBvd2VyZnVsIHRvb2wgZm9yIHdvcmtpbmcgd2l0aC4NCg0KPEJyPg0KDQojU29tZSBSIEJhY2tncm91bmQNCg0KIyNEYXRhIGZyYW1lDQoNCioqUiBvYmplY3RzOiBkYXRhIGZyYW1lICoqDQoNCkluIFIgYSB2YXJpYWJsZXMgYXJlIG5vdCBkZWNsYXJlZCBhcyBzb21lIGRhdGEgdHlwZSAobnVtZXJpYywgaW50ZWdlciwgdXN3KSwgYnV0IGFuIG9iamVjdHMuIFRoZSBkYXRhIHR5cGUgb2YgdGhlIFItb2JqZWN0IGJlY29tZXMgdGhlIGRhdGEgdHlwZSBvZiB0aGUgdmFyaWFibGUuIEEgZGF0YSBmcmFtZSBpcyB0aGUgbW9zdCBjb21tb24gd2F5IG9mIHN0b3JpbmcgZGF0YSBpbiBSLiBJdCBtYWtlcyBkYXRhIGFuYWx5c2lzIGVhc2llci4NCg0KRm9sbG93aW5nIGFyZSB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIGEgZGF0YSBmcmFtZS4NCg0KKiBUaGUgY29sdW1uIG5hbWVzIHNob3VsZCBiZSBub24tZW1wdHkuDQoqIFRoZSByb3cgbmFtZXMgc2hvdWxkIGJlIHVuaXF1ZS4NCiogVGhlIGRhdGEgc3RvcmVkIGluIGEgZGF0YSBmcmFtZSBjYW4gYmUgb2YgbnVtZXJpYywgZmFjdG9yIG9yIGNoYXJhY3RlciB0eXBlLg0KKiBFYWNoIGNvbHVtbiBzaG91bGQgY29udGFpbiBzYW1lIG51bWJlciBvZiBkYXRhIGl0ZW1zLg0KDQohW10oMy5QTkcpDQoNCmBgYHtyfQ0KDQojTGluazogaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS90aW1vbGVlL2EtaG9tZS1mb3ItcGFuZGFzLWFuZC1za2xlYXJuLWJlZ2lubmVyLWhvdy10b3MNCg0KYGBgDQoNCg0KU3VtbWFyeSBvZiBkYXRhIGZyYW1lLg0KDQoqIFNwcmVhZHNoZWV0IHN0eWxlIGRhdGENCiogMiBkaW1lbnNpb25zIA0KKiByb3dzIA0KKiBjb2x1bW5zIA0KKiBDYW4gY29udGFpbiBoZXRlcm9nZW5vdXMgZGF0YSANCiogQWxsIGNvbHVtbnMgbXVzdCBiZSBvZiBlcXVhbCBsZW5ndGgNCg0KKipVc2VmdWwgRGF0YSBGcmFtZSBGdW5jdGlvbnM6ICoqDQoNCiogYGhlYWQoKWAgLSBzaG93cyBmaXJzdCA2IHJvd3MNCiogYHRhaWwoKWAgLSBzaG93cyBsYXN0IDYgcm93cw0KKiBgZGltKClgIC0gcmV0dXJucyB0aGUgZGltZW5zaW9ucyBvZiBkYXRhIGZyYW1lIChpLmUuIG51bWJlciBvZiByb3dzIGFuZCBudW1iZXIgb2YgY29sdW1ucykNCiogYG5yb3coKWAgLSBudW1iZXIgb2Ygcm93cw0KKiBgbmNvbCgpYCAtIG51bWJlciBvZiBjb2x1bW5zDQoqIGBzdHIoKWAgLSBzdHJ1Y3R1cmUgb2YgZGF0YSBmcmFtZSAtIG5hbWUsIHR5cGUgYW5kIHByZXZpZXcgb2YgZGF0YSBpbiBlYWNoIGNvbHVtbg0KKiBgbmFtZXMoKWAgLSBzaG93cyB0aGUgbmFtZXMgYXR0cmlidXRlIGZvciBhIGRhdGEgZnJhbWUsIHdoaWNoIGdpdmVzIHRoZSBjb2x1bW4gbmFtZXMuDQoqIGBzYXBwbHkoZGF0YWZyYW1lLCBjbGFzcylgIC0gc2hvd3MgdGhlIGNsYXNzIG9mIGVhY2ggY29sdW1uIGluIHRoZSBkYXRhIGZyYW1lDQoqIA0KDQpUaGUgc3RydWN0dXJlcyBvZiBtdGNhcnMgZGF0YXNldC4NCiogYHN0cigpYA0KKiBgY2xhc3MoKWANCiogYGF0dHJpYnV0ZXMoKWANCg0KDQoNCg0KKipEYXRhIGZyYW1lIGluZGV4aW5nL3N1YnNldHRpbmc6ICoqDQoNCkltcG9ydCBhIGRhdGEgZnJhbWUgZGF0YXNldC4NCg0KDQpDcmVhdGluZyBhIGRhdGEgZnJhbWUuDQpgYGB7cn0NCg0KI1VzZSB0aGUgZnVuY3Rpb24gZGF0YS5mcmFtZSgpIHRvIGNvbnN0cnVjdCBhIGRhdGEgZnJhbWUuIA0KI1Bhc3MgdGhlIHZlY3RvcnMgbmFtZSwgdHlwZSwgZGlhbWV0ZXIsIHJvdGF0aW9uIGFuZCByaW5ncyANCiNhcyBhcmd1bWVudHMgdG8gZGF0YS5mcmFtZSgpLCBpbiB0aGlzIG9yZGVyLg0KDQojIERlZmluaXRpb24gb2YgdmVjdG9ycw0KbmFtZSA8LSBjKCJNZXJjdXJ5IiwgIlZlbnVzIiwgIkVhcnRoIiwgIk1hcnMiLCAiSnVwaXRlciIsICJTYXR1cm4iLCANCiJVcmFudXMiLA0KICJOZXB0dW5lIikNCnR5cGUgPC0gYygiVGVycmVzdHJpYWwgcGxhbmV0IiwgIlRlcnJlc3RyaWFsIHBsYW5ldCIsIA0KIlRlcnJlc3RyaWFsIHBsYW5ldCIsIA0KICAgICAgICAgICJUZXJyZXN0cmlhbCBwbGFuZXQiLCAiR2FzIGdpYW50IiwgIkdhcyBnaWFudCIsIA0KICAgICAgICAgICJHYXMgZ2lhbnQiLCAiR2FzIGdpYW50IikNCmRpYW1ldGVyIDwtIGMoMC4zODIsIDAuOTQ5LCAxLCAwLjUzMiwgMTEuMjA5LCA5LjQ0OSwgNC4wMDcsIDMuODgzKQ0Kcm90YXRpb24gPC0gYyg1OC42NCwgLTI0My4wMiwgMSwgMS4wMywgMC40MSwgMC40MywgLTAuNzIsIDAuNjcpDQpyaW5ncyA8LSBjKEZBTFNFLCBGQUxTRSwgRkFMU0UsIEZBTFNFLCBUUlVFLCBUUlVFLCBUUlVFLCBUUlVFKQ0KDQojIENyZWF0ZSBhIGRhdGEgZnJhbWUgZnJvbSB0aGUgdmVjdG9ycw0KcGxhbmV0c19kZiA8LWRhdGEuZnJhbWUobmFtZSx0eXBlLGRpYW1ldGVyLHJvdGF0aW9uLHJpbmdzKQ0KcGxhbmV0c19kZg0KDQpgYGANCg0KDQpJbnZlc3RpZ2F0ZSB0aGUgc3RydWN0dXJlIG9mIGRhdGEgZnJhbWUuDQpgYGB7cn0NCg0Kc3RyKHBsYW5ldHNfZGYpDQpjbGFzcyhwbGFuZXRzX2RmKQ0KYXR0cmlidXRlcyhwbGFuZXRzX2RmKQ0KDQpgYGANCg0KU2VsZWN0aW5nIGRhdGEgZnJhbWUgZWxlbWVudHMuDQpgYGB7cn0NCg0KIyBEaWFtZXRlciBvZiBNZXJjdXJ5IChyb3cgMSwgY29sdW1uIDMpDQpwbGFuZXRzX2RmWzEsM10NCg0KIyBGYXRhIGZvciBNYXJzIChlbnRpcmUgZm91cnRoIHJvdykNCnBsYW5ldHNfZGZbNCxdDQoNCiMgU2VsZWN0IGZpcnN0IDUgdmFsdWVzIG9mIGRpYW1ldGVyIGNvbHVtbg0KcGxhbmV0c19kZlsxOjUsImRpYW1ldGVyIl0NCg0KYGBgDQoNCk9ubHkgcGxhbmV0cyB3aXRoIHJpbmdzLg0KYGBge3J9DQoNCiMgU2VsZWN0IHRoZSByaW5ncyB2YXJpYWJsZSBmcm9tIHBsYW5ldHNfZGYNCnJpbmdzX3ZlY3RvciA8LSBwbGFuZXRzX2RmJHJpbmdzDQoNCiMgUHJpbnQgb3V0IHJpbmdzX3ZlY3Rvcg0KcmluZ3NfdmVjdG9yDQoNCiMgQWRhcHQgdGhlIGNvZGUgdG8gc2VsZWN0IGFsbCBjb2x1bW5zIGZvciBwbGFuZXRzIHdpdGggcmluZ3MNCnBsYW5ldHNfZGZbcmluZ3NfdmVjdG9yLF0NCg0KYGBgDQoNCk9ubHkgcGxhbmV0cyB3aXRoIHJpbmdzIGJ1dCBzaG9ydGVyLg0KYGBge3J9DQoNCiMgU2VsZWN0IHBsYW5ldHMgd2l0aCByaW5ncw0Kc3Vic2V0KHBsYW5ldHNfZGYscmluZ3M9PSJUUlVFIikNCg0KDQpgYGANCg0KDQpTb3J0aW5nIGRhdGEgZnJhbWUuDQpgYGB7cn0NCg0KY2V2aTwtYygxMCwxMDAsOSkNCm9yZGVyKGNldmkpDQpjZXZpW29yZGVyKGNldmkpXQ0KDQpwb3NpdGlvbnMgPC0gIG9yZGVyKHBsYW5ldHNfZGYkZGlhbWV0ZXIpDQojIFVzZSBwb3NpdGlvbnMgdG8gc29ydCBwbGFuZXRzX2RmDQpwbGFuZXRzX2RmW3Bvc2l0aW9ucyxdDQoNCmBgYA0KDQoNCkFkZCBjb2x1bW4gYW5kIHJvd3MgaW4gZGF0YSBmcmFtZS4NCmBgYHtyfQ0KDQpuYW1lPC1jKCJjZXZpIiwiaGVyZGlhbiIpDQpub3RlPC1jKDEsMikNCg0KZGZfMTwtZGF0YS5mcmFtZShuYW1lLCBub3RlKQ0KZGZfMQ0KDQoNCm5hbWU8LWMoImJlcmxpbiIsImhhbWJ1cmciKQ0Kbm90ZTwtYygzLDQpDQoNCmRmXzI8LWRhdGEuZnJhbWUobmFtZSwgbm90ZSkNCmRmXzINCg0KZGZfMzwtcmJpbmQoZGZfMSxkZl8yKQ0KZGZfMw0KDQoNCmRmXzMkYWRkcmVzczwtYygiSW5kb25lc2lhIiwiVVNBIiwiR2VybWFueSIsIkl0YWx5IikNCg0KZGZfMw0KYGBgDQoNCg0KDQoNCk1vcmUgdHJhbnNmb3JtYXRpb24gc3VjaCBhcyBmaWx0ZXIsIHNlbGVjdCwgYXJyYW5nZSwgbXV0YXRlLCBzdW1tYXJpc2UgLCBhbmQgcGlwZSBvcGVyYXRvciBwbGVhc2UgZ28gdG8gbXkgW3ItcGVkaWFdKGh0dHBzOi8vci1wZWRpYS5naXRib29rLmlvL2NldmkvaW50cm8tdG8tci1ib290Y2FtcC8wMy1kYXRhLXRyYW5zZm9ybWF0aW9uKQ0KDQoNCg0KIyNTcGVjaWFsIHZhbHVlcw0KDQpOQSwgTlVMTCwgwrFJbmYgYW5kIE5hTiBhcmUgc3BlY2lhbCB2YWx1ZXMgaW4gUi4gVGhlIG1lYW5pbmcgYW5kIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlbSBhcmUgYmVsb3cuDQoNCiogTkEgU3RhbmRzIGZvciBub3QgYXZhaWxhYmxlLiBOQSBpcyBhIHBsYWNlaG9sZGVyIGZvciBhIG1pc3NpbmcgdmFsdWUuDQpgYGB7cn0NCg0KTkEgKyAxDQpzdW0oYyhOQSwgMSwgMikpDQptZWRpYW4oYyhOQSwgMSwgMiwgMyksIG5hLnJtID0gVFJVRSkNCmxlbmd0aChjKE5BLCAyLCAzLCA0KSkNCjMgPT0gTkENCk5BID09IE5BDQpUUlVFIHwgTkENCg0KYGBgDQoNCiogTlVMTCBtZWFucyBubyBjbGFzcyAoaXRzIGNsYXNzIGlzIE5VTEwpIGFuZCBoYXMgbGVuZ3RoIDAgc28gaXQgZG9lcyBub3QgdGFrZSB1cCBhbnkgc3BhY2UgaW4gYSB2ZWN0b3IuDQpgYGB7cn0NCg0KbGVuZ3RoKGMoMSwgMiwgTlVMTCwgNCkpDQpzdW0oYygxLCAyLCBOVUxMLCA0KSkNCnggPC0gTlVMTA0KYyh4LCAyKQ0KDQpgYGANCg0KKiBJbmYgU3RhbmRzIGZvciBpbmZpbml0eSAoanVzdCBmb3IgbnVtZXJpYykuDQpgYGB7cn0NCg0KcGkvMA0KMiAqIEluZg0KSW5mIC0gMWUrMTANCkluZiArIEluZg0KMyA8IC1JbmYNCkluZiA9PSBJbmYNCg0KYGBgDQoNCiogTmFOIFN0YW5kcyBmb3Igbm90IGEgbnVtYmVyICh0aGUgcmVzdWx0IGlzIHVua25vd24pDQpgYGB7cn0NCg0KTmFOICsgMQ0KZXhwKE5hTikNCg0KYGBgDQoNCiMjUmVsYXRpb25hbCBkYXRhDQoNClRoZXJlIGFyZSB0aHJlZSB0eXBlcyBkZXNpZ25lZCB0byB3b3JrIHdpdGggcmVsYXRpb25hbCBkYXRhOg0KDQojI0RhdGEgdHJhbnNmb3JtYXRpb24NCg0KPEJyPg0KDQojQ2hlY2tsaXN0IGZvciBEYXRhIENsZWFuaW5nDQoNCiMjMS1NaXNzaW5nIHZhbHVlcw0KDQoqIEEgcGxhY2Vob2xkZXIgZm9yIGEgZGF0dW0gb2Ygd2hpY2ggdGhlIHR5cGUgaXMga25vd24NCmJ1dCBpdHMgdmFsdWUgaXNuJ3QuIA0KKiBJdCBpcyBpbXBvc3NpYmxlIHRvIHBlcmZvcm0gc3RhdGlzdGljYWwgYW5hbHlzaXMNCg0KDQoNCl9fX1VzZSBjYXNlIDE6IElkZW50aWZ5IG1pc3NpbmcgdmFsdWVzX19fDQoNCjEuIFN0YXRpc3RpY2FsIGFuYWx5c2lzIGVycm9yDQpgYGB7cn0NCg0KYWdlIDwtIGMoMjMsIDE2LCBOQSkNCm1lYW4oYWdlKQ0KIyMgWzFdIE5BDQptZWFuKGFnZSwgbmEucm0gPSBUUlVFKQ0KIyMgWzFdIDE5LjUNCg0KDQpgYGANCg0KDQoyLiBJZGVudGlmeSB0aGUgTkEgdmFsdWUNCmBgYHtyfQ0KDQpjb21wbGV0ZS5jYXNlcyhhZ2UpDQoNCiNvcg0KDQppcy5uYShhZ2UpDQoNCmBgYA0KDQozLiBSZW1vdmUgTkEgdmFsdWVzICh3aXRob3V0IE5BIHZhbHVlcykNCmBgYHtyfQ0KDQpuYS5vbWl0KGFnZSkNCg0KYGBgDQoNCg0KX19fVXNlIGNhc2UgMjogUmVjb2RlIG1pc3NpbmcgdmFsdWVzIHdpdGggbWVhbl9fXw0KDQoxLiBUZXN0IGZvciBtaXNzaW5nIHZhbHVlcyBOQQ0KYGBge3J9DQoNCnggPC0gYygxOjUsIE5BLCA5OjExLCBOQSkNCmlzLm5hKHgpDQoNCmRmIDwtIGRhdGEuZnJhbWUoY29sMSA9IGMoMTozLCBOQSksDQogICAgICAgICAgICAgICAgIGNvbDIgPSBjKCJ0aGlzIiwgTkEsImlzIiwgInRleHQiKSwgDQogICAgICAgICAgICAgICAgIGNvbDMgPSBjKFRSVUUsIEZBTFNFLCBUUlVFLCBUUlVFKSwgDQogICAgICAgICAgICAgICAgIGNvbDQgPSBjKDIuNSwgNC4yLCAzLjIsIE5BKSwNCiAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KaXMubmEoZGYpDQoNCmBgYA0KDQpUbyBpZGVudGlmeSB0aGUgbG9jYXRpb24gb2YgdGhlIE5BLg0KYGBge3J9DQoNCndoaWNoKGlzLm5hKHgpKQ0Kc3VtKGlzLm5hKGRmKSkNCg0KI29yIGZvciBkYXRhIGZyYW1lDQoNCmNvbFN1bXMoaXMubmEoZGYpKQ0KDQpgYGANCg0KMi4gUmVjb2RlIG1pc3NpbmcgdmFsdWVzIE5BDQpVc2Ugbm9ybWFsIHN1YnNldHRpbmcgYW5kIGFzc2lnbm1lbnQgb3BlcmF0aW9ucy4NCmBgYHtyfQ0KDQojIHJlY29kZSBtaXNzaW5nIHZhbHVlcyB3aXRoIHRoZSBtZWFuDQojIHZlY3RvciB3aXRoIG1pc3NpbmcgZGF0YQ0KeCA8LSBjKDE6NCwgTkEsIDY6NywgTkEpDQp4DQojIyBbMV0gIDEgIDIgIDMgIDQgTkEgIDYgIDcgTkENCg0KeFtpcy5uYSh4KV0gPC0gbWVhbih4LCBuYS5ybSA9IFRSVUUpDQoNCnJvdW5kKHgsIDIpDQojIyBbMV0gMS4wMCAyLjAwIDMuMDAgNC4wMCAzLjgzIDYuMDAgNy4wMCAzLjgzDQoNCiMgZGF0YSBmcmFtZSB0aGF0IGNvZGVzIG1pc3NpbmcgdmFsdWVzIGFzIDk5DQpkZiA8LSBkYXRhLmZyYW1lKGNvbDEgPSBjKDE6MywgOTkpLCBjb2wyID0gYygyLjUsIDQuMiwgOTksIDMuMikpDQoNCiMgY2hhbmdlIDk5cyB0byBOQXMNCmRmW2RmID09IDk5XSA8LSBOQQ0KZGYNCiMjICAgY29sMSBjb2wyDQojIyAxICAgIDEgIDIuNQ0KIyMgMiAgICAyICA0LjINCiMjIDMgICAgMyAgIE5BDQojIyA0ICAgTkEgIDMuMg0KDQpgYGANCg0KUmVjb2RlIG1pc3NpbmcgdmFsdWVzIGluIGEgc2luZ2xlIGRhdGEgZnJhbWUuDQoNCkZvciBleGFtcGxlLCBoZXJlIHdlIHJlY29kZSB0aGUgbWlzc2luZyB2YWx1ZSBpbiBjb2w0IHdpdGggdGhlIG1lYW4gdmFsdWUgb2YgY29sNC4NCmBgYHtyfQ0KDQojIGRhdGEgZnJhbWUgd2l0aCBtaXNzaW5nIGRhdGENCmRmIDwtIGRhdGEuZnJhbWUoY29sMSA9IGMoMTozLCBOQSksDQogICAgICAgICAgICAgICAgIGNvbDIgPSBjKCJ0aGlzIiwgTkEsImlzIiwgInRleHQiKSwgDQogICAgICAgICAgICAgICAgIGNvbDMgPSBjKFRSVUUsIEZBTFNFLCBUUlVFLCBUUlVFKSwgDQogICAgICAgICAgICAgICAgIGNvbDQgPSBjKDIuNSwgNC4yLCAzLjIsIE5BKSwNCiAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KICAgICAgICAgICAgICAgICANCmRmJGNvbDRbaXMubmEoZGYkY29sNCldIDwtIG1lYW4oZGYkY29sNCwgbmEucm0gPSBUUlVFKQ0KZGYNCiMjICAgY29sMSBjb2wyICBjb2wzIGNvbDQNCiMjIDEgICAgMSB0aGlzICBUUlVFICAyLjUNCiMjIDIgICAgMiA8TkE+IEZBTFNFICA0LjINCiMjIDMgICAgMyAgIGlzICBUUlVFICAzLjINCiMjIDQgICBOQSB0ZXh0ICBUUlVFICAzLjMNCg0KYGBgDQoNCg0KDQozLiBFeGNsdWRlIG1pc3NpbmcgdmFsdWVzIE5BDQpgYGB7cn0NCg0KIyBBIHZlY3RvciB3aXRoIG1pc3NpbmcgdmFsdWVzDQp4IDwtIGMoMTo0LCBOQSwgNjo3LCBOQSkNCg0KIyBpbmNsdWRpbmcgTkEgdmFsdWVzIHdpbGwgcHJvZHVjZSBhbiBOQSBvdXRwdXQNCm1lYW4oeCkNCiMjIFsxXSBOQQ0KDQojIGV4Y2x1ZGluZyBOQSB2YWx1ZXMgd2lsbCBjYWxjdWxhdGUgdGhlIG1hdGhlbWF0aWNhbCBvcGVyYXRpb24gZm9yIGFsbCBub24tbWlzc2luZyB2YWx1ZXMNCm1lYW4oeCwgbmEucm0gPSBUUlVFKQ0KIyMgWzFdIDMuODMzMzMzDQoNCmBgYA0KDQpGb3IgY29tcGxldGUgb2JzZXJ2YXRpb25zLg0KYGBge3J9DQoNCiMgZGF0YSBmcmFtZSB3aXRoIG1pc3NpbmcgdmFsdWVzDQpkZiA8LSBkYXRhLmZyYW1lKGNvbDEgPSBjKDE6MywgTkEpLA0KICAgICAgICAgICAgICAgICBjb2wyID0gYygidGhpcyIsIE5BLCJpcyIsICJ0ZXh0IiksIA0KICAgICAgICAgICAgICAgICBjb2wzID0gYyhUUlVFLCBGQUxTRSwgVFJVRSwgVFJVRSksIA0KICAgICAgICAgICAgICAgICBjb2w0ID0gYygyLjUsIDQuMiwgMy4yLCBOQSksDQogICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkNCg0KZGYNCiMjICAgY29sMSBjb2wyICBjb2wzIGNvbDQNCiMjIDEgICAgMSB0aGlzICBUUlVFICAyLjUNCiMjIDIgICAgMiA8TkE+IEZBTFNFICA0LjINCiMjIDMgICAgMyAgIGlzICBUUlVFICAzLjINCiMjIDQgICBOQSB0ZXh0ICBUUlVFICAgTkENCg0KY29tcGxldGUuY2FzZXMoZGYpDQojIyBbMV0gIFRSVUUgRkFMU0UgIFRSVUUgRkFMU0UNCg0KIyBzdWJzZXQgd2l0aCBjb21wbGV0ZS5jYXNlcyB0byBnZXQgY29tcGxldGUgY2FzZXMNCmRmW2NvbXBsZXRlLmNhc2VzKGRmKSwgXQ0KIyMgICBjb2wxIGNvbDIgY29sMyBjb2w0DQojIyAxICAgIDEgdGhpcyBUUlVFICAyLjUNCiMjIDMgICAgMyAgIGlzIFRSVUUgIDMuMg0KDQojIG9yIHN1YnNldCB3aXRoIGAhYCBvcGVyYXRvciB0byBnZXQgaW5jb21wbGV0ZSBjYXNlcw0KZGZbIWNvbXBsZXRlLmNhc2VzKGRmKSwgXQ0KIyMgICBjb2wxIGNvbDIgIGNvbDMgY29sNA0KIyMgMiAgICAyIDxOQT4gRkFMU0UgIDQuMg0KIyMgNCAgIE5BIHRleHQgIFRSVUUgICBOQQ0KDQpgYGANCg0KQWx0ZXJuYXRpdiB3aXRoIGBvbWl0Lm5hKClgIGZ1bmN0aW9uDQpgYGB7cn0NCg0KDQojIG9yIHVzZSBuYS5vbWl0KCkgdG8gZ2V0IHNhbWUgYXMgYWJvdmUNCm5hLm9taXQoZGYpDQojIyAgIGNvbDEgY29sMiBjb2wzIGNvbDQNCiMjIDEgICAgMSB0aGlzIFRSVUUgIDIuNQ0KIyMgMyAgICAzICAgaXMgVFJVRSAgMy4yDQoNCmBgYA0KDQpfX19Vc2UgY2FzZSAzOiBFeHBsb3JpbmcgbWlzc2luZyB2YWx1ZXMgd2l0aCBuYW5pYXIgcGFja2FnZV9fXw0KDQoqKm5hbmlhcioqIHBhY2thZ2UgcHJvdmlkZXMgcHJpbmNpcGxlZCwgdGlkeSB3YXlzIHRvIHN1bW1hcmlzZSwgdmlzdWFsaXNlLCBhbmQgbWFuaXB1bGF0ZSBtaXNzaW5nIGRhdGEgd2l0aCBtaW5pbWFsIGRldmlhdGlvbnMgZnJvbSB0aGUgd29ya2Zsb3dzIGluIGdncGxvdDIgYW5kIHRpZHkgZGF0YS4gDQoNCltodHRwOi8vbmFuaWFyLm5qdGllcm5leS5jb20vaW5kZXguaHRtbF0oaHR0cDovL25hbmlhci5uanRpZXJuZXkuY29tL2luZGV4Lmh0bWwpDQoNCg0KDQpfX19Vc2UgY2FzZSA0OiBNaXNzaW5nIFZhbHVlIFRyZWF0bWVudF9fXw0KDQoqKkRhdGEgcHJlcCBhbmQgcGF0dGVybjogKioNCg0KVXNpbmcgQm9zdG9uSG91c2luZyBkYXRhc2V0IGluIGBtbGJlbmNoYCBwYWNrYWdlLiBUaGUgb3JpZ2luYWwgQm9zdG9uSG91c2luZyBkYXRhIGRvZXNuJ3QgaGF2ZSBtaXNzaW5nIHZhbHVlcywgYnV0IHdlIGluamVjdGVkIHRoaXMgZGF0YXNldCB3aXRoIG1pc2luZyB2YWx1ZXMuDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoIm1sYmVuY2giKQ0KI2xpYnJhcnkobWxiZW5jaCkNCg0KIyBpbml0aWFsaXplIHRoZSBkYXRhDQpkYXRhICgiQm9zdG9uSG91c2luZyIsIHBhY2thZ2U9Im1sYmVuY2giKQ0Kb3JpZ2luYWwgPC0gQm9zdG9uSG91c2luZyAgIyBiYWNrdXAgb3JpZ2luYWwgZGF0YQ0KDQojIEludHJvZHVjZSBtaXNzaW5nIHZhbHVlcyByYW5kb21seQ0Kc2V0LnNlZWQoMTAwKQ0KQm9zdG9uSG91c2luZ1tzYW1wbGUoMTpucm93KEJvc3RvbkhvdXNpbmcpLCA0MCksICJyYWQiXSA8LSBOQSAjNDAgTkEgaW4gInJhZCINCkJvc3RvbkhvdXNpbmdbc2FtcGxlKDE6bnJvdyhCb3N0b25Ib3VzaW5nKSwgNDApLCAicHRyYXRpbyJdPC1OQSAjNDAgTkEgaW4gInJhZCINCkJvc3RvbkhvdXNpbmcNCg0KYGBgDQoNClRoZSBtaXNzaW5nIHZhbHVlcyBoYXZlIGJlZW4gaW5qZWN0ZWQuIElkZW50aWZ5IHRoZSAnbWlzc2luZ3MnIHBhdHRlcm4gdXNpbmcgwrRtaWNlOjptZC5wYXR0ZXJuYC4NCmBgYHtyfQ0KDQojIFBhdHRlcm4gb2YgbWlzc2luZyB2YWx1ZXMNCiNpbnN0YWxsLnBhY2thZ2VzKCJtaWNlIikNCiNsaWJyYXJ5KG1pY2UpDQptZC5wYXR0ZXJuKEJvc3RvbkhvdXNpbmcpICAjIHBhdHRlcm4gb3IgbWlzc2luZyB2YWx1ZXMgaW4gZGF0YS4NCg0KYGBgDQoNCkFzIHdlIGtub3cgdGhlcmUgYXJlIDQwIE5BIHZhbHVlcyBpbiAicmFkIiBhbmQgYWxzbyAicHRyYXRpbyIuDQoNCioqNCB3YXlzIG9mIGRlYWxpbmcgd2l0aCBtaXNzaW5nIHZhbHVlczogKioNCg0KMS4gRGVsZXRpbmcgdGhlIG9ic2VydmF0aW9ucy4NCg0KKiBIYXZlIHN1ZmZpY2VudCBkYXRhIHBvaW50cywgc28gdGhlIG1vZGVsIGRvZXNuJ3QgbG9zZSBwb3dlci4NCiogTm90IHRvIGludHJvZHVjZSBiaWFzIChtZWFuaW5nLCBkaXNwcm9wb3J0aW9uYXRlIG9yIG5vbi1yZXByZXNlbnRhdGlvbiBvZiBjbGFzc2VzKS4NCg0KDQpVc2luZyBgbmEuYWN0aW9uPW5hLm9taXRgDQpgYGB7cn0NCg0KIyBFeGFtcGxlDQpsbShtZWR2IH4gcHRyYXRpbyArIHJhZCwgZGF0YT1Cb3N0b25Ib3VzaW5nLCBuYS5hY3Rpb249bmEub21pdCkNCg0KI1NpbXBsZSBleGFtcGxlOg0KDQpjZXZpPC1jKDEsMixOQSkNCmNldmkNCg0KY2V2aV9uZXc8LW5hLm9taXQoY2V2aSkNCmNldmlfbmV3DQoNCg0KYGBgDQoNCjIuIERlbGV0aW5nIHRoZSB2YXJpYWJsZS4NCg0KRGVsZXRlIHRoZSB2YXJpYWJsZSB3aXRoIG1pc3NpbmcgdmFsdWVzICgicmFkIiBhbmQgYWxzbyAicHRyYXRpbyIpLg0KDQoNCg0KMy4gSW1wdXRhdGlvbiB3aXRoIG1lYW4gLyBtZWRpYW4gLyBtb2RlLg0KDQpSZXBsYWNpbmcgdGhlIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIG1lYW4gLyBtZWRpYW4gLyBtb2RlIGlzIGEgY3J1ZGUgd2F5IG9mIHRyZWF0aW5nIG1pc3NpbmcgdmFsdWVzLg0KRGVwZW5kaW5nIG9uIHRoZSBjb250ZXh0LCBsaWtlIGlmIHRoZSB2YXJpYXRpb24gaXMgbG93IG9yIGlmIHRoZSB2YXJpYWJsZSBoYXMgbG93IGxldmVyYWdlIG92ZXIgdGhlIHJlc3BvbnNlLCBzdWNoIGEgcm91Z2ggYXBwcm94aW1hdGlvbiBpcyBhY2NlcHRhYmxlIGFuZCBjb3VsZCBwb3NzaWJseSBnaXZlIHNhdGlzZmFjdG9yeSByZXN1bHRzLg0KDQoNCkNoYW5nZSB0aGUgbWlzc2luZyB2YWx1ZXMgdXNpbmcgYEhtaXNjYCBwYWNrYWdlLg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygiSG1pc2MiKQ0KI2xpYnJhcnkoSG1pc2MpDQppbXB1dGUoQm9zdG9uSG91c2luZyRwdHJhdGlvLCBtZWFuKSAgIyByZXBsYWNlIHdpdGggbWVhbg0KaW1wdXRlKEJvc3RvbkhvdXNpbmckcmFkLCBtZWFuKSAgIyByZXBsYWNlIHdpdGggbWVhbg0KDQojb3INCg0KI2ltcHV0ZShCb3N0b25Ib3VzaW5nJHB0cmF0aW8sIG1lZGlhbikgICMgbWVkaWFuDQojaW1wdXRlKEJvc3RvbkhvdXNpbmckcHRyYXRpbywgMjApICAjIHJlcGxhY2Ugc3BlY2lmaWMgbnVtYmVyDQoNCmBgYA0KDQoNCk9yIGltcHV0ZSBtYW51YWxseS4NCmBgYHtyfQ0KDQpCb3N0b25Ib3VzaW5nJHB0cmF0aW9baXMubmEoQm9zdG9uSG91c2luZyRwdHJhdGlvKV08LW1lYW4oQm9zdG9uSG91c2luZyRwdHJhdGlvLCBuYS5ybSA9IFRSVUUpDQoNCg0KYGBgDQoNCg0KSWRlbnRpZnkgdGhlIGFjY3VyYWN5IHdoZW4gaXQgaXMgaW1wdXRlZCB3aXRoIG1lYW4gdXNpbmcgYERNd1JgIHBhY2thZ2UuDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJETXdSIikNCiNsaWJyYXJ5KERNd1IpDQphY3R1YWxzIDwtIG9yaWdpbmFsJHB0cmF0aW9baXMubmEoQm9zdG9uSG91c2luZyRwdHJhdGlvKV0NCnByZWRpY3RlZHMgPC0gcmVwKG1lYW4oQm9zdG9uSG91c2luZyRwdHJhdGlvLCBuYS5ybT1UKSwgbGVuZ3RoKGFjdHVhbHMpKQ0KcmVnci5ldmFsKGFjdHVhbHMsIHByZWRpY3RlZHMpDQoNCg0KYGBgDQoNCk1vcmUgaW5mbyBhYm91dCB0aGlzIGZ1bmN0aW9ucyBzZWUNCg0KW2h0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9ETXdSL3ZlcnNpb25zLzAuNC4xL3RvcGljcy9yZWdyLmV2YWxdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9ETXdSL3ZlcnNpb25zLzAuNC4xL3RvcGljcy9yZWdyLmV2YWwpDQoNCg0KNC4gUHJlZGljdGlvbi4NCg0KUHJlZGljdGlvbiBpcyBtb3N0IGFkdmFuY2VkIG1ldGhvZCB0byBpbXB1dGUgeW91ciBtaXNzaW5nIHZhbHVlcyBhbmQgaW5jbHVkZXMgZGlmZmVyZW50IGFwcHJvYWNoZXMgc3VjaCBhczoga05OIEltcHV0YXRpb24sIHJwYXJ0LCBhbmQgbWljZS4NCg0KNC4xIGtOTiBJbXB1dGF0aW9uDQoNCkRNd1I6OmtubkltcHV0YXRpb24gdXNlcyBrLU5lYXJlc3QgTmVpZ2hib3VycyBhcHByb2FjaCB0byBpbXB1dGUgbWlzc2luZyB2YWx1ZXMuDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoIkRNd1IiKQ0KI2xpYnJhcnkoRE13UikNCg0Ka25uT3V0cHV0IDwtIGtubkltcHV0YXRpb24oQm9zdG9uSG91c2luZ1ssICFuYW1lcyhCb3N0b25Ib3VzaW5nKSAlaW4lICJtZWR2Il0pICAjIHBlcmZvcm0ga25uIGltcHV0YXRpb24uDQoNCmFueU5BKGtubk91dHB1dCkNCg0KYGBgDQoNCkxldHMgY29tcHV0ZSB0aGUgYWNjdXJhY3kuDQpgYGB7cn0NCg0KYWN0dWFscyA8LSBvcmlnaW5hbCRwdHJhdGlvW2lzLm5hKEJvc3RvbkhvdXNpbmckcHRyYXRpbyldDQpwcmVkaWN0ZWRzIDwtIGtubk91dHB1dFtpcy5uYShCb3N0b25Ib3VzaW5nJHB0cmF0aW8pLCAicHRyYXRpbyJdDQpyZWdyLmV2YWwoYWN0dWFscywgcHJlZGljdGVkcykNCg0KYGBgDQoNCioqIE5vdGU6ICoqDQpUaGUgbWVhbiBhYnNvbHV0ZSBwZXJjZW50YWdlIGVycm9yIChtYXBlKSBoYXMgaW1wcm92ZWQgYnkgfiAzOSUgY29tcGFyZWQgdG8gdGhlIGltcHV0YXRpb24gYnkgbWVhbi4gR29vZA0KDQoNCg0KNC4yIHJwYXJ0DQoNClRoZSBsaW1pdGF0aW9uIHdpdGggYERNd1I6OmtubkltcHV0YXRpb25gIGlzIHRoYXQgaXQgc29tZXRpbWVzIG1heSBub3QgYmUgYXBwcm9wcmlhdGUgdG8gdXNlIHdoZW4gdGhlIG1pc3NpbmcgdmFsdWUgY29tZXMgZnJvbSBhIGZhY3RvciB2YXJpYWJsZS4gQm90aCBycGFydCBhbmQgbWljZSBoYXMgZmxleGliaWxpdHkgdG8gaGFuZGxlIHRoYXQgc2NlbmFyaW8uIA0KDQpUaGUgaWRlYSBoZXJlIGlzIHdlIGFyZSBnb2luZyB0byB1c2UgcnBhcnQgdG8gcHJlZGljdCB0aGUgbWlzc2luZyB2YWx1ZXMgaW5zdGVhZCBvZiBrTk4uIFRvIGhhbmRsZSBmYWN0b3IgdmFyaWFibGUsIHdlIGNhbiBzZXQgdGhlIG1ldGhvZD1jbGFzcyB3aGlsZSBjYWxsaW5nIGBycGFydCgpYC4gRm9yIG51bWVyaWMsIHdlIHVzZSwgYG1ldGhvZD1hbm92YWAuIEhlcmUgYWdhaW4sIHdlIG5lZWQgdG8gbWFrZSBzdXJlIG5vdCB0byB0cmFpbiBycGFydCBvbiByZXNwb25zZSB2YXJpYWJsZSAobWVkdikuDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoInJwYXJ0IikNCiNsaWJyYXJ5KHJwYXJ0KQ0KY2xhc3NfbW9kIDwtIHJwYXJ0KHJhZCB+IC4gLSBtZWR2LCBkYXRhPUJvc3RvbkhvdXNpbmdbIWlzLm5hKEJvc3RvbkhvdXNpbmckcmFkKSwgXSwgbWV0aG9kPSJjbGFzcyIsIG5hLmFjdGlvbj1uYS5vbWl0KSAgIyBzaW5jZSByYWQgaXMgYSBmYWN0b3INCmFub3ZhX21vZCA8LSBycGFydChwdHJhdGlvIH4gLiAtIG1lZHYsIGRhdGE9Qm9zdG9uSG91c2luZ1shaXMubmEoQm9zdG9uSG91c2luZyRwdHJhdGlvKSwgXSwgbWV0aG9kPSJhbm92YSIsIG5hLmFjdGlvbj1uYS5vbWl0KSAgIyBzaW5jZSBwdHJhdGlvIGlzIG51bWVyaWMuDQpyYWRfcHJlZCA8LSBwcmVkaWN0KGNsYXNzX21vZCwgQm9zdG9uSG91c2luZ1tpcy5uYShCb3N0b25Ib3VzaW5nJHJhZCksIF0pDQpwdHJhdGlvX3ByZWQgPC0gcHJlZGljdChhbm92YV9tb2QsIEJvc3RvbkhvdXNpbmdbaXMubmEoQm9zdG9uSG91c2luZyRwdHJhdGlvKSwgXSkNCg0KDQpgYGANCg0KTGV0cyBjb21wdXRlIHRoZSBhY2N1cmFjeSBmb3IgcHRyYXRpby4NCmBgYHtyfQ0KDQphY3R1YWxzIDwtIG9yaWdpbmFsJHB0cmF0aW9baXMubmEoQm9zdG9uSG91c2luZyRwdHJhdGlvKV0NCnByZWRpY3RlZHMgPC0gcHRyYXRpb19wcmVkDQpyZWdyLmV2YWwoYWN0dWFscywgcHJlZGljdGVkcykNCg0KDQpgYGANCg0KKipOb3RlOiAqKlRoZSBtZWFuIGFic29sdXRlIHBlcmNlbnRhZ2UgZXJyb3IgKG1hcGUpIGhhcyBpbXByb3ZlZCBhZGRpdGlvbmFsbHkgYnkgYW5vdGhlciB+IDMwJSBjb21wYXJlZCB0byB0aGUga25uSW1wdXRhdGlvbi4gVmVyeSBHb29kLg0KDQpBY2N1cmFjeSBmb3IgcmFkLg0KYGBge3J9DQoNCmFjdHVhbHMgPC0gb3JpZ2luYWwkcmFkW2lzLm5hKEJvc3RvbkhvdXNpbmckcmFkKV0NCnByZWRpY3RlZHMgPC0gYXMubnVtZXJpYyhjb2xuYW1lcyhyYWRfcHJlZClbYXBwbHkocmFkX3ByZWQsIDEsIHdoaWNoLm1heCldKQ0KbWVhbihhY3R1YWxzICE9IHByZWRpY3RlZHMpICAjIGNvbXB1dGUgbWlzY2xhc3MgZXJyb3IuDQoNCmBgYA0KDQoqKk5vdGU6ICoqVGhpcyB5aWVsZHMgYSBtaXMtY2xhc3NpZmljYXRpb24gZXJyb3Igb2YgMjUlLiBOb3QgYmFkIGZvciBhIGZhY3RvciB2YXJpYWJsZSENCg0KNC4zIG1pY2UNCg0KYG1pY2VgIHNob3J0IGZvciBNdWx0aXZhcmlhdGUgSW1wdXRhdGlvbiBieSBDaGFpbmVkIEVxdWF0aW9ucyBpcyBhbiBSIHBhY2thZ2UgdGhhdCBwcm92aWRlcyBhZHZhbmNlZCBmZWF0dXJlcyBmb3IgbWlzc2luZyB2YWx1ZSB0cmVhdG1lbnQuDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoIm1pY2UiKQ0KI2xpYnJhcnkobWljZSkNCm1pY2VNb2QgPC0gbWljZShCb3N0b25Ib3VzaW5nWywgIW5hbWVzKEJvc3RvbkhvdXNpbmcpICVpbiUgIm1lZHYiXSwgbWV0aG9kPSJyZiIpICAjIHBlcmZvcm0gbWljZSBpbXB1dGF0aW9uLCBiYXNlZCBvbiByYW5kb20gZm9yZXN0cy4NCm1pY2VPdXRwdXQgPC0gY29tcGxldGUobWljZU1vZCkgICMgZ2VuZXJhdGUgdGhlIGNvbXBsZXRlZCBkYXRhLg0KYW55TkEobWljZU91dHB1dCkNCg0KDQpgYGANCg0KTGV0cyBjb21wdXRlIHRoZSBhY2N1cmFjeSBvZiBwdHJhdGlvLg0KYGBge3J9DQoNCmFjdHVhbHMgPC0gb3JpZ2luYWwkcHRyYXRpb1tpcy5uYShCb3N0b25Ib3VzaW5nJHB0cmF0aW8pXQ0KcHJlZGljdGVkcyA8LSBtaWNlT3V0cHV0W2lzLm5hKEJvc3RvbkhvdXNpbmckcHRyYXRpbyksICJwdHJhdGlvIl0NCnJlZ3IuZXZhbChhY3R1YWxzLCBwcmVkaWN0ZWRzKQ0KDQpgYGANCg0KVGhlIG1lYW4gYWJzb2x1dGUgcGVyY2VudGFnZSBlcnJvciAobWFwZSkgaGFzIGltcHJvdmVkIGFkZGl0aW9uYWxseSBieSB+IDQ4JSBjb21wYXJlZCB0byB0aGUgcnBhcnQuIEV4Y2VsbGVudCEuDQoNCkxldHMgY29tcHV0ZSB0aGUgYWNjdXJhY3kgb2YgcmFkLg0KYGBge3J9DQoNCmFjdHVhbHMgPC0gb3JpZ2luYWwkcmFkW2lzLm5hKEJvc3RvbkhvdXNpbmckcmFkKV0NCnByZWRpY3RlZHMgPC0gbWljZU91dHB1dFtpcy5uYShCb3N0b25Ib3VzaW5nJHJhZCksICJyYWQiXQ0KbWVhbihhY3R1YWxzICE9IHByZWRpY3RlZHMpICAjIGNvbXB1dGUgbWlzY2xhc3MgZXJyb3IuDQoNCmBgYA0KDQpUaGUgbWlzLWNsYXNzaWZpY2F0aW9uIGVycm9yIHJlZHVjZWQgdG8gMTUlLCB3aGljaCBpcyA2IG91dCBvZiA0MCBvYnNlcnZhdGlvbnMuIFRoaXMgaXMgYSBnb29kIGltcHJvdmVtZW50IGNvbXBhcmVkIHRvIHJwYXJ0J3MgMjUlLg0KDQpNb3JlIGluZm86IA0KDQoqIFtJbXB1dGluZyBNaXNzaW5nIERhdGEgd2l0aCBSOyBNSUNFIHBhY2thZ2VdKGh0dHBzOi8vZGF0YXNjaWVuY2VwbHVzLmNvbS9pbXB1dGluZy1taXNzaW5nLWRhdGEtd2l0aC1yLW1pY2UtcGFja2FnZS8pDQoNCiogW0hhbmRsaW5nIG1pc3NpbmcgZGF0YSB3aXRoIE1JQ0UgcGFja2FnZTsgYSBzaW1wbGUgYXBwcm9hY2hdKGh0dHBzOi8vZGF0YXNjaWVuY2VwbHVzLmNvbS9oYW5kbGluZy1taXNzaW5nLWRhdGEtd2l0aC1taWNlLXBhY2thZ2UtYS1zaW1wbGUtYXBwcm9hY2gvKQ0KDQpUaGlzIGBtaWNlYCBhcHByb2FjaCBiYXNlZCBvbiBbTXVsdGl2YXJpYXRlIEltcHV0YXRpb24gYnkgQ2hhaW5lZCBFcXVhdGlvbnMgaW4gUiBqb3VybmFsXVtodHRwczovL3d3dy5qc3RhdHNvZnQub3JnL2FydGljbGUvdmlldy92MDQ1aTAzXQ0KDQo+ICoqTm90ZTogKiogVGhvdWdoIHdlIGhhdmUgYW4gaWRlYSBvZiBob3cgZWFjaCBtZXRob2QgcGVyZm9ybXMsIHRoZXJlIGlzIG5vdCBlbm91Z2ggZXZpZGVuY2UgdG8gY29uY2x1ZGUgd2hpY2ggbWV0aG9kIGlzIGJldHRlciBvciB3b3JzZS4gQnV0IHRoZXNlIGFyZSBkZWZpbml0ZWx5IHdvcnRoIHRlc3Rpbmcgb3V0IHRoZSBuZXh0IHRpbWUgeW91IGltcHV0ZSBtaXNzaW5nIHZhbHVlcy4NCg0KDQojIzItU3BlY2lhbCAgdmFsdWVzDQoNClNwZWNpYWwgdmFsdWVzIGFyZSBhIHZhbHVlcyB0aGF0IGFyZSBub3QgYW4gZWxlbWVudCBvZiB0aGUNCm1hdGhlbWF0aWNhbCBzZXQgb2YgcmVhbCBudW1iZXJzICg/Pz8pIChleGFtcGxlOiDCsUluZiBhbmQgTmFOKS4gDQoNCg0KKipBIGZ1bmN0aW9uIHRvIGNoZWNrIHNwZWNpYWwgdmFsdWVzIGluIGEgZGF0YS5mcmFtZTogKioNCmBgYHtyfQ0KYWdlPC1jKDIxLDQyLDMzLDE4LDIxLE5BLEluZixOYU4pDQpoZWlnaHQ8LWMoMTExLDExMiwxMTMsMTE0LDExNSwxMTYsMTE3LDExOCkNCnBlcnNvbjwtZGF0YS5mcmFtZShhZ2UsaGVpZ2h0KQ0KDQppcy5maW5pdGUoYygxLCBJbmYsIE5hTiwgTkEpKQ0KDQppcy5zcGVjaWFsIDwtIGZ1bmN0aW9uKHgpew0KaWYgKGlzLm51bWVyaWMoeCkpICFpcy5maW5pdGUoeCkgZWxzZSBpcy5uYSh4KQ0KfQ0KDQpzYXBwbHkocGVyc29uLCBpcy5zcGVjaWFsKQ0KDQpwZXJzb25bc2FwcGx5KHBlcnNvbiwgaXMuc3BlY2lhbCksXQ0KYGBgDQoNCg0KIyMzLUR1cGxpY2F0ZSAgdmFsdWVzDQoNCiogIFIgYmFzZSBmdW5jdGlvbnM6DQogIA0KICAgICogYGR1cGxpY2F0ZWQoKWA6IGZvciBpZGVudGlmeWluZyBkdXBsaWNhdGVkIGVsZW1lbnRzIGFuZA0KICAgICogYHVuaXF1ZSgpYDogZm9yIGV4dHJhY3RpbmcgdW5pcXVlIGVsZW1lbnRzDQoNCiogYGRpc3RpbmN0KClgIGZ1bmN0aW9uIGZyb20gKipkcGx5cioqIHBhY2thZ2UgdG8gcmVtb3ZlIGR1cGxpY2F0ZSByb3dzIGluIGEgZGF0YSBmcmFtZS4NCg0KIVtdKDQuUE5HKQ0KDQpgYGB7cn0NCiNMaW5rOiBodHRwczovL3d3dy5kYXRhbm92aWEuY29tL2VuL2xlc3NvbnMvaWRlbnRpZnktYW5kLXJlbW92ZS1kdXBsaWNhdGUtZGF0YS1pbi1yLw0KDQpgYGANCg0KKipQYWNrYWdlLioqDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KYGBgDQoNCioqRGF0YXNldC4qKg0KYGBge3J9DQoNCm15X2RhdGEgPC0gYXNfdGliYmxlKGlyaXMpICNyaWJibGUgYXJlbmB0IGRpZmZlcmVudCBmcm9tIGRhdGFmcmFtZSwganVzdCBhIGxpdHRlIGRpZmZlcmVudCBhc3Bla3QuDQpteV9kYXRhDQoNCg0KYGBgDQoNCg0KKipGaW5kIGFuZCBkcm9wIGR1cGxpY2F0ZSBlbGVtZW50cy4qKg0KYGBge3J9DQoNCnggPC0gYygxLCAxLCA0LCA1LCA0LCA2KQ0KZHVwbGljYXRlZCh4KQ0KDQojRXh0cmFjdCBkdXBsaWNhdGUgZWxlbWVudHM6DQp4W2R1cGxpY2F0ZWQoeCldDQoNCiNSZW1vdmUgZHVwbGljYXRlZCBlbGVtZW50czoNCnhbIWR1cGxpY2F0ZWQoeCldDQoNCmBgYA0KDQoqKlJlbW92ZSBkdXBsaWNhdGUgcm93cyBmcm9tIGEgZGF0YSBmcmFtZS4qKg0KYGBge3J9DQoNCiMgUmVtb3ZlIGR1cGxpY2F0ZXMgYmFzZWQgb24gU2VwYWwuV2lkdGggY29sdW1ucw0KbXlfZGF0YVshZHVwbGljYXRlZChteV9kYXRhJFNlcGFsLldpZHRoKSwgXQ0KDQpgYGANCg0KKipFeHRyYWN0IHVuaXF1ZSBlbGVtZW50cy4qKg0KYGBge3J9DQoNCnggPC0gYygxLCAxLCA0LCA1LCA0LCA2KQ0KdW5pcXVlKHgpDQoNCiNhcHBseSB1bmlxdWUoKSBvbiBhIGRhdGEgZnJhbWU6DQp1bmlxdWUobXlfZGF0YSkNCg0KYGBgDQoNCg0KKipSZW1vdmUgZHVwbGljYXRlIHJvd3MgaW4gYSBkYXRhIGZyYW1lLioqDQpgYGB7cn0NCg0KbXlfZGF0YSAlPiUgZGlzdGluY3QoKQ0KDQpgYGANCg0KKipSZW1vdmUgZHVwbGljYXRlIHJvd3MgYmFzZWQgb24gY2VydGFpbiBjb2x1bW5zICh2YXJpYWJsZXMpLioqDQpgYGB7cn0NCg0KIyBSZW1vdmUgZHVwbGljYXRlZCByb3dzIGJhc2VkIG9uIFNlcGFsLkxlbmd0aA0KbXlfZGF0YSAlPiUgZGlzdGluY3QoU2VwYWwuTGVuZ3RoLCAua2VlcF9hbGwgPSBUUlVFKQ0KDQojIFJlbW92ZSBkdXBsaWNhdGVkIHJvd3MgYmFzZWQgb24gDQojIFNlcGFsLkxlbmd0aCBhbmQgUGV0YWwuV2lkdGgNCm15X2RhdGEgJT4lIGRpc3RpbmN0KFNlcGFsLkxlbmd0aCwgUGV0YWwuV2lkdGgsIC5rZWVwX2FsbCA9IFRSVUUpDQoNCiNUaGUgb3B0aW9uIC5rZXBfYWxsIGlzIHVzZWQgdG8ga2VlcCBhbGwgdmFyaWFibGVzIGluIHRoZSBkYXRhLg0KYGBgDQoNCg0KKipTdW1tYXJ5OiAqKg0KDQoqIFJlbW92ZSBkdXBsaWNhdGUgcm93cyBiYXNlZCBvbiBvbmUgb3IgbW9yZSBjb2x1bW4gdmFsdWVzOiBteV9kYXRhICU+JSBkcGx5cjo6ZGlzdGluY3QoU2VwYWwuTGVuZ3RoKQ0KKiBSIGJhc2VkIGZ1bmN0aW9uOiAgYHVuaXF1ZShteV9kYXRhKWANCiogSWRlbnRpZnkgZHVwbGljYXRlIHZhbHVlczogYGR1cGxpY2F0ZWQobXlfZGF0YSlgDQoNCg0KIyM0LU91dGxpZXJzDQoNCjxCcj4NCg0KI0NoYW5nZSBsb2cgdXBkYXRlDQoNCiogMjguMTIuMjAxOA0KKiAzMC4xMi4yMDE4DQoNCjxCcj4NCg0KI1ByZWZlcmVuY2VzDQoNCiogW1IgZm9yIERhdGEgU2NpZW5jZV0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei8pDQoqIFtLRG51Z2dldHNdKGh0dHBzOi8vd3d3LmtkbnVnZ2V0cy5jb20vKQ0KKiBbR2VvcmdlIEEuIFNtYXRoZXJzIExpYnJhcmllc10oaHR0cHM6Ly9ndWlkZXMudWZsaWIudWZsLmVkdS9wcmVjaXNpb25wdWJsaWNoZWFsdGgvdGlkeV9tZXNzeSkNCiogW0pvdXJuYWwgb2YgU3RhdGlzdGljYWwgU29mdHdhcmVdKGh0dHBzOi8vd3d3LmpzdGF0c29mdC5vcmcvaW5kZXgpDQoqIFtBbiBpbnRyb2R1Y3Rpb24gdG8gZGF0YSBjbGVhbmluZyB3aXRoIFJdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL2RvYy9jb250cmliL2RlX0pvbmdlK3Zhbl9kZXJfTG9vLUludHJvZHVjdGlvbl90b19kYXRhX2NsZWFuaW5nX3dpdGhfUi5wZGYpDQoqIFtUdXRvcmlhbHNwb2ludF0oaHR0cHM6Ly93d3cudHV0b3JpYWxzcG9pbnQuY29tL3IvaW5kZXguaHRtKQ0KKiBbRGF0YUNhbXBdKGh0dHBzOi8vd3d3LmRhdGFjYW1wLmNvbS8pDQoqIFtTb2Z0d2FyZSBjYXJwZW50cnldKGh0dHBzOi8vc29mdHdhcmUtY2FycGVudHJ5Lm9yZy9sZXNzb25zL2luZGV4Lmh0bWwpDQoqIFtVQyBCdXNpbmVzcyBBbmFseXRpY3MgUiBQcm9ncmFtbWluZyBHdWlkZSBdKGh0dHA6Ly91Yy1yLmdpdGh1Yi5pby8pDQoqIFtEYXRhbm92aWFdKGh0dHBzOi8vd3d3LmRhdGFub3ZpYS5jb20vZW4vbGVzc29ucy9pZGVudGlmeS1hbmQtcmVtb3ZlLWR1cGxpY2F0ZS1kYXRhLWluLXIvKQ0KKiBbUi1ibG9nZ2Vyc10oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vKQ0KKiBbUi1zdGF0aXN0aWNzLmNvXShodHRwOi8vci1zdGF0aXN0aWNzLmNvLykNCiogW0RhdGFzY2llbmNlK10oaHR0cHM6Ly9kYXRhc2NpZW5jZXBsdXMuY29tLykNCg0KDQojTGljZW5zZQ0KDQpbTUlUXShodHRwczovL29wZW5zb3VyY2Uub3JnL2xpY2Vuc2VzL01JVCkNCg0K