Milkias Z. SEMEREAB
GEOL0097-Geostatistics, University of Liège
October 21, 2022
Meuse river data set
For this project, you will use the Meuse dataset that is available in
CSV format (comma separated values). This data set gives locations and
top soil heavy metal concentrations (ppm), along with a number of soil
and landscape variables, collected in a flood plain of the river Meuse,
near the village Stein. Heavy metal concentrations are bulk sampled from
an area of approximately 15 m x 15 m.
Format: The data frame contains the following
columns:
- x a numeric vector: x-coordinate (m) in RDM (Dutch topographical map
coordinates)
- y a numeric vector: y-coordinate (m) in RDM (Dutch topographical map
coordinates)
- cadmium: topsoil cadmium concentration, ppm
- copper: topsoil copper concentration, ppm
- lead: topsoil lead concentration, ppm
- zinc: topsoil zinc concentration, ppm
- elev: relative elevation
- om: organic matter, as percentage
- ffreq: flooding frequency class
- soil: soil type
- lime: lime class
- landuse: landuse class
- dist: distance to river Meuse (metres),
References
P.A. Burrough, R.A. McDonnell, 1998. Principles of Geographical
Information Systems. Oxford University Press.
Task 1: Loading the dataset
• Create your working directory: The working
directory/working folder is the default location where R will look for
files that you want to load and where it will put any files you save.
You can Create your working directory in 2 ways:
- One of the great things about using RStudio Projects is that when
you create a new project or open an existing project, it will
automatically set your working directory to the appropriate
location.
To create a new project in Rstudio: File > New Project >
New Directory > New Project > Type in the name of the directory to
store your project, e.g. “meuse_project”
- You can also set the working directory manually using the
setwd() function.
The first method is highly recommended. Once you create the project,
use the getwd() function to make sure your default
working directory is created.
# Show where your working directory was created:
getwd()
[1] "D:/GEO-STATISTICS COURSE/2022-23/GST_Practical/R-Markdown and Notebooks"
• Load the “meuse dataset” into the workspace using
the read.csv() function and store the dataframe as
variable called “data”.
data = read.csv("meuse_data.csv")
Note: the meuse.csv dataset should be inside the working
directory, otherwise R won’t be able to find the file and will therefore
throw an error.
Task 2: Quick Exploratory Data Analysis (EDA)
To have a look at your dataset, you can use the following
methods:
“Environment” tab in the top right window lists the variables and
functions present in the current R session. To display the full
dataframe, click on the “data” variable on the Environment tab.
When you work with large datasets and data frames, your first
task as a geostatistician is to develop a clear understanding of its
structure and main elements. Therefore, it is often useful to show only
a small part of the entire dataset. The function head()
enables you to show the first observations of a data frame. Similarly,
the function tail() prints out the last observations in
your dataset.
Another method that is often used to get a rapid overview of your
data is the function str() which shows you the
structure of your dataset including the total number of observations,
the total number of variables, the data type of each variable and the
first observations etc..
head(data)
tail(data)
str(data)
'data.frame': 155 obs. of 13 variables:
$ x : num 181072 181025 181165 181298 181307 ...
$ y : num 333611 333558 333537 333484 333330 ...
$ cadmium: num 11.7 8.6 6.5 2.6 2.8 3 3.2 2.8 2.4 1.6 ...
$ copper : num 85 81 68 81 48 61 31 29 37 24 ...
$ lead : num 299 277 199 116 117 137 132 150 133 80 ...
$ zinc : num 1022 1141 640 257 269 ...
$ elev : num 7.91 6.98 7.8 7.66 7.48 7.79 8.22 8.49 8.67 9.05 ...
$ om : num 13.6 14 13 8 8.7 7.8 9.2 9.5 10.6 6.3 ...
$ ffreq : int 1 1 1 1 1 1 1 1 1 1 ...
$ soil : int 1 1 1 2 2 2 2 1 1 2 ...
$ lime : int 1 1 1 0 0 0 0 0 0 0 ...
$ landuse: chr "Ah" "Ah" "Ah" "Ga" ...
$ dist : num 50 30 150 270 380 470 240 120 240 420 ...
Questions:
How many observations (samples or data points) and variables
(fields or attributes) the meuse datset have?
Which variables contain the spatial data ? (note: spatial =
geographical location)
How many continuous variables are there? How many categorical
variables are there?
Task 3: Spatial Data Visualization
• Visualization of samples location
plot(data[,1],data[,2],pch=16,cex=0.5, xlab = "Easting(m)", ylab = "Northing(m)",main="Sample Locations",asp=1)

The plot() function took the following arguments:
- data[ ,1] and data[ ,2] = the latitude/X and longitude/Y of all the
sample points.
- pch = Plot character. Try to change pch to any other number between
1 and 25.
- cex = point size
- xlab, ylab and main = to put text label for the x-axis, y-axis and
head respectively.
- asp = set the aspect ratio of the plot
Questions:
- What can you tell about the sampling campaign(s)?
- Do you have clusters?
• Visualization of data distribution
Visualization of heavy metal concentrations in relation to spatial
locations.
# cadmium
plot(data[,1],data[,2],pch=16,cex=2*(data[ ,3] - min(data[ ,3]))/(max
(data[ ,3]) - min(data[ ,3])),xlab = "Easting(m)", ylab = "Northing(m)", main="cadmium concentrations (ppm)",asp=1)

# copper
plot(data[,1],data[,2],pch=16,cex=2*(data[ ,4] - min(data[ ,4]))/(max
(data[ ,4]) - min(data[ ,4])),xlab = "Easting(m)",ylab = "Northing(m)", main="copper concentrations (ppm)",asp=1)

# lead
plot(data[,1],data[,2],pch=16,cex=2*(data[ ,5] - min(data[ ,5]))/(max
(data[ ,5]) - min(data[ ,5])),xlab = "Easting(m)", ylab = "Northing(m)", main="lead concentrations (ppm)",asp=1)

# zinc
plot(data[,1],data[,2],pch=16,cex=2*(data[ ,6] - min(data[ ,6]))/(max
(data[ ,6]) - min(data[ ,6])),xlab = "Easting(m)", ylab = "Northing(m)", main="zinc concentrations (ppm)",asp=1)

Questions:
- Can you see concentration anomalies?
- Can you observe any spatial regions with outliers (high heavy metal
concentrations)?
Hint: The process governing heavy metal distribution
seems that polluted sediment is carried by the river, and mostly
deposited close to the river bank
Note: Although works well, the above block of code
is not efficient. Whenever you write such code, try to use for
loop to repeat a section of code a known number of times.
for(j in 1:4){
plot(data[,1],data[,2],pch=16,cex=2*(data[,j+2]-min(data[,j+2]))/(max
(data[,j+2])-min(data[,j+2])),xlab = "Easting(m)", ylab = "Northing(m)",
main=paste(colnames(data)[j+2],"concentrations (ppm)",sep=" "),asp=1)
}
• Other way to visualize the Data using the library
plot3D
Plot3D is an R package/library containing many functions for 2D and
3D plotting. To install any package in Rstudio:
- Go to “packages” tab > click “install” > type “plot3D” and
click install
- Or use the function install.packages()
install.packages("plot3D")
Once the package is installed, you have to import/load it to the
workspace. You can do this either by:
- Simplying checking the checkbox beside the package name “Plot3D” in
the Packages tab
- Using the library() function to load packages which are already
installed.
library(plot3D)
scatter2D() is a function with in
Plot3D library that is used to polt a Colored scatter
plots with a color variable as points.
scatter2D(data[,1], data[,2],colvar=data[,6],pch = 16, xlab = "Easting(m)", ylab = "Northing(m)",clab="zinc",main="zinc concentrations(ppm)", asp=1)

• Visualization of categorical Variables (e.g., Soil
Type)
scatter2D(data[,1],data[,2],colvar=data[,10],col=c("blue", "green", "brown"),pch=16, xlab="Easting(m)", ylab="Northing(m)", clab=colnames(data)[10],main="Soil Type", asp=1, cex=0.8)
# Add legend to the plot using the legend() function
legend("bottomright",legend=c("Soil - 1","Soil - 2", "Soil - 3"), fill=c("blue", "green", "brown"), cex=1)

Task 4: Univariate EDA
• Compute the main descriptive univariate statistics for the
4 heavy metals
summary(data[ ,3:6])
cadmium copper lead zinc
Min. : 0.200 Min. : 14.00 Min. : 37.0 Min. : 113.0
1st Qu.: 0.800 1st Qu.: 23.00 1st Qu.: 72.5 1st Qu.: 198.0
Median : 2.100 Median : 31.00 Median :123.0 Median : 326.0
Mean : 3.246 Mean : 40.32 Mean :153.4 Mean : 469.7
3rd Qu.: 3.850 3rd Qu.: 49.50 3rd Qu.:207.0 3rd Qu.: 674.5
Max. :18.100 Max. :128.00 Max. :654.0 Max. :1839.0
Questions:
- Observe the minimum, first quartile, median, third quartile, and
maximum heavy metal concentrations in the sample set?
- Compare the mean and median.
• Display histograms and boxplots of all four heavy metals
concentrations
hist(data$cadmium, col="blue", xlab="Values",main="cadmium concentrations")

hist(data$copper,col="blue", xlab="Values",main="copper concentrations")

hist(data$lead,col="blue", xlab="Values",main="lead concentrations")

hist(data$zinc,col="blue", xlab="Values",main="zinc concentrations")

boxplot(data$cadmium,col="blue", xlab="Values",main="cadmium concentrations")

boxplot(data$copper,col="blue", xlab="Values",main="copper concentrations")

boxplot(data$lead,col="blue", xlab="Values",main="lead concentrations")

boxplot(data$zinc,col="blue", xlab="Values",main="zinc concentrations")

Questions:
- Describe the distribution of these variables. Do they have a
symmetric or skewed distribution?
- For each variable: Does it appear to have more than one population?
Does it appear to be any extreme values (“outliers”)?
Task 5: Log-Transform the data
The log transformation can be used to make highly skewed
distributions less skewed. This can be valuable both for making patterns
in the data more interpretable and for helping to meet the assumptions
of inferential statistics.
Heavy metal concentration usually is not symmetric. Log-transform the
highly-skewed distribution. Using the log10() function
add four columns to the dataframe (one for each log transformed heavy
metal concentration).
data$logCd = log10(data$cadmium)
data$logCu = log10(data$copper)
data$logPb = log10(data$lead)
data$logZn = log10(data$zinc)
• Display histograms and boxplots of raw and log transformed
heavy metals concentrations side by side
# use par() function to convert the graphical window into 2-by-1 sub-screens
par(mfrow=c(1,2))
# Display histograms of raw and log transformed data
for(j in 1:4){
hist(data[,j+2],col="blue", xlab="Values",main=colnames(data)[j+2])
hist(data[,j+13],col="red", xlab="Values",main=colnames(data)[j+13])
}




par(mfrow=c(1,2))
# Display boxplot of raw and log transformed data
for(j in 1:4){
boxplot(data[,j+2],col="blue", xlab="Values",main=colnames(data)[j+2])
boxplot(data[,j+13],col="red", xlab="Values",main=colnames(data)[j+13])
}




Questions:
- Difference of distribution between the raw data and
log-transformed?
- Is the skewness of original distribution changed remarkably?
Task 6: Statistics vs category
• Compute summary statistics (e.g. mean) of all four heavy
metal concentrations by some categorical variables (e.g. soil
type)
aggregate(data[ ,3:6], by=list(data$soil), FUN=mean, na.rm=TRUE)
- aggregate() function Splits the data into subsets, computes summary
statistics for each, and returns the result in a convenient form.
- by=list() is a list of grouping elements.
- FUN = a function to compute the summary statistics which can be
applied to all data subsets.
- na.rm = TRUE excludes missing values when calculating descriptive
statistics in R.
• Display the histogram and boxplot of heavy metals
concentrations by some categorical variables
To do this task, you can use histogram() function
from the lattice library and the already familar
boxplot() function from the R-Base.
# First install and load the lattice library
install.packages("lattice")
library(lattice)
# Histogram of zinc concentrations for each soil type
histogram(~ data$zinc|as.factor(data$soil),col="blue",xlab= "Zinc concerntration")

# boxplot of zinc concentration for flood frequencies
boxplot(data$zinc ~ data$ffreq, col="blue",xlab="Flood Frequency Class",
ylab= "Zinc concerntration")

Questions:
- which soil type is more contaminated with zinc?
- Is flood frequency associated with zinc concentration ?
Task 7: Bivariate EDA
• Plot scatter plots between two variables (for e.g. zinc and
copper)
plot(data$zinc ~ data$copper, xlab="Copper concentration", ylab="zinc concentration")

plot(data$logZn ~ data$logCu, xlab="log10 Copper concentration", ylab="log10 zinc concentration")

Questions:
- Describe the correlation between copper and zinc
concentrations.
- Are there any observations that do not fit the general pattern? What
could be the reason(s)?
• Observe the samples that do not fit the general
pattern
ix=which((data$logZn < 2.6) & (data$logCu > 1.6))
print(ix)
[1] 4 5 6 135
print(data[ix, ])
• Compute the scatter plot matrix for all pairs of variables
using pair() function
pairs(data[ ,3:6], pch = 19, cex=0.5)

pairs(data[ ,14:17], pch = 19, cex=0.5)

• Compute the Pearson correlation matrix
CorMat = cor(data[ ,3:6])
print(CorMat)
cadmium copper lead zinc
cadmium 1.0000000 0.9254499 0.7989466 0.9162139
copper 0.9254499 1.0000000 0.8183069 0.9082695
lead 0.7989466 0.8183069 1.0000000 0.9546913
zinc 0.9162139 0.9082695 0.9546913 1.0000000
# Comput Pearson correlation matrix for log-transformed variables
CorMatLog = cor(data[ ,14:17])
print(CorMatLog)
logCd logCu logPb logZn
logCd 1.0000000 0.8363329 0.8124522 0.8624475
logCu 0.8363329 1.0000000 0.8428929 0.8971515
logPb 0.8124522 0.8428929 1.0000000 0.9671621
logZn 0.8624475 0.8971515 0.9671621 1.0000000
Questions:
- Discuss the relationship between the pairs of variables ? The nature
(sign) and strength of the relationships?
- The possible reason behind this type of correlation between the heavy
metals?
• Compute the Pearson correlation matrix using Corrplot
library
corrplot is an R package that provides a visual exploratory tool on
correlation matrix that supports rich visualization method, graphic
layout, color and legend. The corrplot() function takes
an existing correlation matrix as argument
# Install and load corrplot package
install.packages("corrplot")
library(corrplot)
# Pearson Correlation Matrix
corrplot(CorMat)

corrplot(CorMatLog)

LS0tDQp0aXRsZTogIkdlb3N0YXRpc3RpY3MgLSBVbml2YXJpYXRlIGFuZCBCaXZhcmlhdGUgQW5hbHlzaXMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQoqTWlsa2lhcyBaLiBTRU1FUkVBQioNCg0KKkdFT0wwMDk3LUdlb3N0YXRpc3RpY3MsIFVuaXZlcnNpdHkgb2YgTGnDqGdlKg0KDQoqT2N0b2JlciAyMSwgMjAyMioNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCg0KIyMjIyMgKipNZXVzZSByaXZlciBkYXRhIHNldCoqDQoNCkZvciB0aGlzIHByb2plY3QsIHlvdSB3aWxsIHVzZSB0aGUgTWV1c2UgZGF0YXNldCB0aGF0IGlzIGF2YWlsYWJsZSBpbiBDU1YgZm9ybWF0IChjb21tYSBzZXBhcmF0ZWQgdmFsdWVzKS4gVGhpcyBkYXRhIHNldCBnaXZlcyBsb2NhdGlvbnMgYW5kIHRvcCBzb2lsIGhlYXZ5IG1ldGFsIGNvbmNlbnRyYXRpb25zIChwcG0pLCBhbG9uZyB3aXRoIGEgbnVtYmVyIG9mIHNvaWwgYW5kIGxhbmRzY2FwZSB2YXJpYWJsZXMsIGNvbGxlY3RlZCBpbiBhIGZsb29kIHBsYWluIG9mIHRoZSByaXZlciBNZXVzZSwgbmVhciB0aGUgdmlsbGFnZSBTdGVpbi4gSGVhdnkgbWV0YWwgY29uY2VudHJhdGlvbnMgYXJlIGJ1bGsgc2FtcGxlZCBmcm9tIGFuIGFyZWEgb2YgYXBwcm94aW1hdGVseSAxNSBtIHggMTUgbS4NCg0KKipGb3JtYXQ6KioNClRoZSBkYXRhIGZyYW1lIGNvbnRhaW5zIHRoZSBmb2xsb3dpbmcgY29sdW1uczoNCg0KLSB4IGEgbnVtZXJpYyB2ZWN0b3I6IHgtY29vcmRpbmF0ZSAobSkgaW4gUkRNIChEdXRjaCB0b3BvZ3JhcGhpY2FsIG1hcCBjb29yZGluYXRlcykNCi0geSBhIG51bWVyaWMgdmVjdG9yOiB5LWNvb3JkaW5hdGUgKG0pIGluIFJETSAoRHV0Y2ggdG9wb2dyYXBoaWNhbCBtYXAgY29vcmRpbmF0ZXMpDQotIGNhZG1pdW06IHRvcHNvaWwgY2FkbWl1bSBjb25jZW50cmF0aW9uLCBwcG0NCi0gY29wcGVyOiB0b3Bzb2lsIGNvcHBlciBjb25jZW50cmF0aW9uLCBwcG0NCi0gbGVhZDogdG9wc29pbCBsZWFkIGNvbmNlbnRyYXRpb24sIHBwbQ0KLSB6aW5jOiB0b3Bzb2lsIHppbmMgY29uY2VudHJhdGlvbiwgcHBtDQotIGVsZXY6IHJlbGF0aXZlIGVsZXZhdGlvbg0KLSBvbTogb3JnYW5pYyBtYXR0ZXIsIGFzIHBlcmNlbnRhZ2UNCi0gZmZyZXE6IGZsb29kaW5nIGZyZXF1ZW5jeSBjbGFzcw0KLSBzb2lsOiBzb2lsIHR5cGUNCi0gbGltZTogbGltZSBjbGFzcw0KLSBsYW5kdXNlOiBsYW5kdXNlIGNsYXNzDQotIGRpc3Q6IGRpc3RhbmNlIHRvIHJpdmVyIE1ldXNlIChtZXRyZXMpLCANCg0KKipSZWZlcmVuY2VzKiogICAgDQoNClAuQS4gQnVycm91Z2gsIFIuQS4gTWNEb25uZWxsLCAxOTk4LiBQcmluY2lwbGVzIG9mIEdlb2dyYXBoaWNhbCBJbmZvcm1hdGlvbiBTeXN0ZW1zLiBPeGZvcmQgVW5pdmVyc2l0eSBQcmVzcy4NCg0KDQojIyMjICoqVGFzayAxOiBMb2FkaW5nIHRoZSBkYXRhc2V0KioNCg0KKirigKIgQ3JlYXRlIHlvdXIgd29ya2luZyBkaXJlY3Rvcnk6KiogVGhlIHdvcmtpbmcgZGlyZWN0b3J5L3dvcmtpbmcgZm9sZGVyIGlzIHRoZSBkZWZhdWx0IGxvY2F0aW9uIHdoZXJlIFIgd2lsbCBsb29rIGZvciBmaWxlcyB0aGF0IHlvdSB3YW50IHRvIGxvYWQgYW5kIHdoZXJlIGl0IHdpbGwgcHV0IGFueSBmaWxlcyB5b3Ugc2F2ZS4gWW91IGNhbiBDcmVhdGUgeW91ciB3b3JraW5nIGRpcmVjdG9yeSBpbiAyIHdheXM6DQoNCi0gT25lIG9mIHRoZSBncmVhdCB0aGluZ3MgYWJvdXQgdXNpbmcgUlN0dWRpbyBQcm9qZWN0cyBpcyB0aGF0IHdoZW4geW91IGNyZWF0ZSBhIG5ldyBwcm9qZWN0IG9yIG9wZW4gYW4gZXhpc3RpbmcgcHJvamVjdCwgaXQgd2lsbCBhdXRvbWF0aWNhbGx5IHNldCB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5IHRvIHRoZSBhcHByb3ByaWF0ZSBsb2NhdGlvbi4NCg0KVG8gY3JlYXRlIGEgbmV3IHByb2plY3QgaW4gUnN0dWRpbzogKkZpbGUgXD4gTmV3IFByb2plY3QgXD4gTmV3IERpcmVjdG9yeSBcPiBOZXcgUHJvamVjdCBcPiBUeXBlIGluIHRoZSBuYW1lIG9mIHRoZSBkaXJlY3RvcnkgdG8gc3RvcmUgeW91ciBwcm9qZWN0LCBlLmcuICJtZXVzZV9wcm9qZWN0IioNCg0KLSBZb3UgY2FuIGFsc28gc2V0IHRoZSB3b3JraW5nIGRpcmVjdG9yeSBtYW51YWxseSB1c2luZyB0aGUgKipzZXR3ZCgpKiogZnVuY3Rpb24uDQoNClRoZSBmaXJzdCBtZXRob2QgaXMgaGlnaGx5IHJlY29tbWVuZGVkLiBPbmNlIHlvdSBjcmVhdGUgdGhlIHByb2plY3QsIHVzZSB0aGUgKipnZXR3ZCgpKiogZnVuY3Rpb24gdG8gbWFrZSBzdXJlIHlvdXIgZGVmYXVsdCB3b3JraW5nIGRpcmVjdG9yeSBpcyBjcmVhdGVkLg0KDQpgYGB7cn0NCiAjIFNob3cgd2hlcmUgeW91ciB3b3JraW5nIGRpcmVjdG9yeSB3YXMgY3JlYXRlZDoNCmdldHdkKCkNCmBgYA0KDQoqKuKAoiBMb2FkIHRoZSAibWV1c2UgZGF0YXNldCIqKiBpbnRvIHRoZSB3b3Jrc3BhY2UgdXNpbmcgdGhlICoqcmVhZC5jc3YoKSoqIGZ1bmN0aW9uIGFuZCBzdG9yZSB0aGUgZGF0YWZyYW1lIGFzIHZhcmlhYmxlIGNhbGxlZCAiZGF0YSIuDQoNCmBgYHtyfQ0KZGF0YSA9IHJlYWQuY3N2KCJtZXVzZV9kYXRhLmNzdiIpDQpgYGANCg0KKk5vdGU6KiB0aGUgbWV1c2UuY3N2IGRhdGFzZXQgc2hvdWxkIGJlIGluc2lkZSB0aGUgd29ya2luZyBkaXJlY3RvcnksIG90aGVyd2lzZSBSIHdvbid0IGJlIGFibGUgdG8gZmluZCB0aGUgZmlsZSBhbmQgd2lsbCB0aGVyZWZvcmUgdGhyb3cgYW4gZXJyb3IuDQoNCiMjIyMgKipUYXNrIDI6IFF1aWNrIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSkqKg0KDQpUbyBoYXZlIGEgbG9vayBhdCB5b3VyIGRhdGFzZXQsIHlvdSBjYW4gdXNlIHRoZSBmb2xsb3dpbmcgbWV0aG9kczoNCg0KLSAiRW52aXJvbm1lbnQiIHRhYiBpbiB0aGUgdG9wIHJpZ2h0IHdpbmRvdyBsaXN0cyB0aGUgdmFyaWFibGVzIGFuZCBmdW5jdGlvbnMgcHJlc2VudCBpbiB0aGUgY3VycmVudCBSIHNlc3Npb24uIFRvIGRpc3BsYXkgdGhlIGZ1bGwgZGF0YWZyYW1lLCBjbGljayBvbiB0aGUgImRhdGEiIHZhcmlhYmxlIG9uIHRoZSBFbnZpcm9ubWVudCB0YWIuDQoNCi0gV2hlbiB5b3Ugd29yayB3aXRoIGxhcmdlIGRhdGFzZXRzIGFuZCBkYXRhIGZyYW1lcywgeW91ciBmaXJzdCB0YXNrIGFzIGEgZ2Vvc3RhdGlzdGljaWFuIGlzIHRvIGRldmVsb3AgYSBjbGVhciB1bmRlcnN0YW5kaW5nIG9mIGl0cyBzdHJ1Y3R1cmUgYW5kIG1haW4gZWxlbWVudHMuIFRoZXJlZm9yZSwgaXQgaXMgb2Z0ZW4gdXNlZnVsIHRvIHNob3cgb25seSBhIHNtYWxsIHBhcnQgb2YgdGhlIGVudGlyZSBkYXRhc2V0LiBUaGUgZnVuY3Rpb24gKipoZWFkKCkqKiBlbmFibGVzIHlvdSB0byBzaG93IHRoZSBmaXJzdCBvYnNlcnZhdGlvbnMgb2YgYSBkYXRhIGZyYW1lLiBTaW1pbGFybHksIHRoZSBmdW5jdGlvbiAqKnRhaWwoKSoqIHByaW50cyBvdXQgdGhlIGxhc3Qgb2JzZXJ2YXRpb25zIGluIHlvdXIgZGF0YXNldC4NCg0KLSBBbm90aGVyIG1ldGhvZCB0aGF0IGlzIG9mdGVuIHVzZWQgdG8gZ2V0IGEgcmFwaWQgb3ZlcnZpZXcgb2YgeW91ciBkYXRhIGlzIHRoZSBmdW5jdGlvbiAqKnN0cigpKiogd2hpY2ggc2hvd3MgeW91IHRoZSBzdHJ1Y3R1cmUgb2YgeW91ciBkYXRhc2V0IGluY2x1ZGluZyB0aGUgdG90YWwgbnVtYmVyIG9mIG9ic2VydmF0aW9ucywgdGhlIHRvdGFsIG51bWJlciBvZiB2YXJpYWJsZXMsIHRoZSBkYXRhIHR5cGUgb2YgZWFjaCB2YXJpYWJsZSBhbmQgdGhlIGZpcnN0IG9ic2VydmF0aW9ucyBldGMuLg0KDQpgYGB7cn0NCmhlYWQoZGF0YSkNCnRhaWwoZGF0YSkNCnN0cihkYXRhKQ0KYGBgDQoNCioqUXVlc3Rpb25zOioqDQoNCi0gSG93IG1hbnkgb2JzZXJ2YXRpb25zIChzYW1wbGVzIG9yIGRhdGEgcG9pbnRzKSBhbmQgdmFyaWFibGVzIChmaWVsZHMgb3IgYXR0cmlidXRlcykgdGhlIG1ldXNlIGRhdHNldCBoYXZlPw0KDQotIFdoaWNoIHZhcmlhYmxlcyBjb250YWluIHRoZSBzcGF0aWFsIGRhdGEgPyAobm90ZTogc3BhdGlhbCA9IGdlb2dyYXBoaWNhbCBsb2NhdGlvbikNCg0KLSBIb3cgbWFueSBjb250aW51b3VzIHZhcmlhYmxlcyBhcmUgdGhlcmU/IEhvdyBtYW55IGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBhcmUgdGhlcmU/ICANCiAgDQo8YnI+DQogICAgDQoNCiMjIyMgKipUYXNrIDM6IFNwYXRpYWwgRGF0YSBWaXN1YWxpemF0aW9uKioNCg0KKirigKIgVmlzdWFsaXphdGlvbiBvZiBzYW1wbGVzIGxvY2F0aW9uKioNCg0KYGBge3J9DQpwbG90KGRhdGFbLDFdLGRhdGFbLDJdLHBjaD0xNixjZXg9MC41LCB4bGFiID0gIkVhc3RpbmcobSkiLCB5bGFiID0gIk5vcnRoaW5nKG0pIixtYWluPSJTYW1wbGUgTG9jYXRpb25zIixhc3A9MSkNCmBgYA0KDQpUaGUgcGxvdCgpIGZ1bmN0aW9uIHRvb2sgdGhlIGZvbGxvd2luZyBhcmd1bWVudHM6DQoNCi0gZGF0YVsgLDFdIGFuZCBkYXRhWyAsMl0gPSB0aGUgbGF0aXR1ZGUvWCBhbmQgbG9uZ2l0dWRlL1kgb2YgYWxsIHRoZSBzYW1wbGUgcG9pbnRzLg0KLSBwY2ggPSBQbG90IGNoYXJhY3Rlci4gVHJ5IHRvIGNoYW5nZSBwY2ggdG8gYW55IG90aGVyIG51bWJlciBiZXR3ZWVuIDEgYW5kIDI1Lg0KLSBjZXggPSBwb2ludCBzaXplDQotIHhsYWIsIHlsYWIgYW5kIG1haW4gPSB0byBwdXQgdGV4dCBsYWJlbCBmb3IgdGhlIHgtYXhpcywgeS1heGlzIGFuZCBoZWFkIHJlc3BlY3RpdmVseS4NCi0gYXNwID0gc2V0IHRoZSBhc3BlY3QgcmF0aW8gb2YgdGhlIHBsb3QNCg0KDQoqKlF1ZXN0aW9uczoqKg0KDQotIFdoYXQgY2FuIHlvdSB0ZWxsIGFib3V0IHRoZSBzYW1wbGluZyBjYW1wYWlnbihzKT8NCi0gRG8geW91IGhhdmUgY2x1c3RlcnM/DQoNCg0KKirigKIgVmlzdWFsaXphdGlvbiBvZiBkYXRhIGRpc3RyaWJ1dGlvbioqDQoNClZpc3VhbGl6YXRpb24gb2YgaGVhdnkgbWV0YWwgY29uY2VudHJhdGlvbnMgaW4gcmVsYXRpb24gdG8gc3BhdGlhbCBsb2NhdGlvbnMuDQoNCmBgYHtyfQ0KIyBjYWRtaXVtDQpwbG90KGRhdGFbLDFdLGRhdGFbLDJdLHBjaD0xNixjZXg9MiooZGF0YVsgLDNdIC0gbWluKGRhdGFbICwzXSkpLyhtYXgNCihkYXRhWyAsM10pIC0gbWluKGRhdGFbICwzXSkpLHhsYWIgPSAiRWFzdGluZyhtKSIsIHlsYWIgPSAiTm9ydGhpbmcobSkiLCBtYWluPSJjYWRtaXVtIGNvbmNlbnRyYXRpb25zIChwcG0pIixhc3A9MSkNCiMgY29wcGVyDQpwbG90KGRhdGFbLDFdLGRhdGFbLDJdLHBjaD0xNixjZXg9MiooZGF0YVsgLDRdIC0gbWluKGRhdGFbICw0XSkpLyhtYXgNCihkYXRhWyAsNF0pIC0gbWluKGRhdGFbICw0XSkpLHhsYWIgPSAiRWFzdGluZyhtKSIseWxhYiA9ICJOb3J0aGluZyhtKSIsIG1haW49ImNvcHBlciBjb25jZW50cmF0aW9ucyAocHBtKSIsYXNwPTEpDQojIGxlYWQNCnBsb3QoZGF0YVssMV0sZGF0YVssMl0scGNoPTE2LGNleD0yKihkYXRhWyAsNV0gLSBtaW4oZGF0YVsgLDVdKSkvKG1heA0KKGRhdGFbICw1XSkgLSBtaW4oZGF0YVsgLDVdKSkseGxhYiA9ICJFYXN0aW5nKG0pIiwgeWxhYiA9ICJOb3J0aGluZyhtKSIsIG1haW49ImxlYWQgY29uY2VudHJhdGlvbnMgKHBwbSkiLGFzcD0xKQ0KIyB6aW5jDQpwbG90KGRhdGFbLDFdLGRhdGFbLDJdLHBjaD0xNixjZXg9MiooZGF0YVsgLDZdIC0gbWluKGRhdGFbICw2XSkpLyhtYXgNCihkYXRhWyAsNl0pIC0gbWluKGRhdGFbICw2XSkpLHhsYWIgPSAiRWFzdGluZyhtKSIsIHlsYWIgPSAiTm9ydGhpbmcobSkiLCBtYWluPSJ6aW5jIGNvbmNlbnRyYXRpb25zIChwcG0pIixhc3A9MSkNCmBgYA0KKipRdWVzdGlvbnM6KiogICANCi0gQ2FuIHlvdSBzZWUgY29uY2VudHJhdGlvbiBhbm9tYWxpZXM/ICAgDQotIENhbiB5b3Ugb2JzZXJ2ZSBhbnkgc3BhdGlhbCByZWdpb25zIHdpdGggb3V0bGllcnMgKGhpZ2ggaGVhdnkgbWV0YWwgY29uY2VudHJhdGlvbnMpPw0KDQoqKipIaW50OiBUaGUgcHJvY2VzcyBnb3Zlcm5pbmcgaGVhdnkgbWV0YWwgZGlzdHJpYnV0aW9uIHNlZW1zIHRoYXQgcG9sbHV0ZWQgc2VkaW1lbnQgaXMgY2FycmllZCBieSB0aGUgcml2ZXIsIGFuZCBtb3N0bHkgZGVwb3NpdGVkIGNsb3NlIHRvIHRoZSByaXZlciBiYW5rKioqDQoNCioqTm90ZToqKiBBbHRob3VnaCB3b3JrcyB3ZWxsLCB0aGUgYWJvdmUgYmxvY2sgb2YgY29kZSBpcyBub3QgZWZmaWNpZW50LiBXaGVuZXZlciB5b3Ugd3JpdGUgc3VjaCBjb2RlLCB0cnkgdG8gdXNlICoqZm9yIGxvb3AqKiB0byByZXBlYXQgYSBzZWN0aW9uIG9mIGNvZGUgYSBrbm93biBudW1iZXIgb2YgdGltZXMuDQoNCmBgYHtyfQ0KZm9yKGogaW4gMTo0KXsNCiAgcGxvdChkYXRhWywxXSxkYXRhWywyXSxwY2g9MTYsY2V4PTIqKGRhdGFbLGorMl0tbWluKGRhdGFbLGorMl0pKS8obWF4DQogIChkYXRhWyxqKzJdKS1taW4oZGF0YVssaisyXSkpLHhsYWIgPSAiRWFzdGluZyhtKSIsIHlsYWIgPSAiTm9ydGhpbmcobSkiLA0KICBtYWluPXBhc3RlKGNvbG5hbWVzKGRhdGEpW2orMl0sImNvbmNlbnRyYXRpb25zIChwcG0pIixzZXA9IiAiKSxhc3A9MSkNCn0NCmBgYA0KDQoqKuKAoiBPdGhlciB3YXkgdG8gdmlzdWFsaXplIHRoZSBEYXRhIHVzaW5nIHRoZSBsaWJyYXJ5ICpwbG90M0QqKioNCg0KUGxvdDNEIGlzIGFuIFIgcGFja2FnZS9saWJyYXJ5IGNvbnRhaW5pbmcgbWFueSBmdW5jdGlvbnMgZm9yIDJEIGFuZCAzRCBwbG90dGluZy4gVG8gaW5zdGFsbCBhbnkgcGFja2FnZSBpbiBSc3R1ZGlvOg0KDQotIEdvIHRvICJwYWNrYWdlcyIgdGFiIFw+IGNsaWNrICJpbnN0YWxsIiBcPiB0eXBlICJwbG90M0QiIGFuZCBjbGljayBpbnN0YWxsDQotIE9yIHVzZSB0aGUgZnVuY3Rpb24gKippbnN0YWxsLnBhY2thZ2VzKCkqKg0KDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoInBsb3QzRCIpDQpgYGANCg0KT25jZSB0aGUgcGFja2FnZSBpcyBpbnN0YWxsZWQsIHlvdSBoYXZlIHRvIGltcG9ydC9sb2FkIGl0IHRvIHRoZSB3b3Jrc3BhY2UuIFlvdSBjYW4gZG8gdGhpcyBlaXRoZXIgYnk6DQoNCi0gU2ltcGx5aW5nIGNoZWNraW5nIHRoZSBjaGVja2JveCBiZXNpZGUgdGhlIHBhY2thZ2UgbmFtZSAiUGxvdDNEIiBpbiB0aGUgUGFja2FnZXMgdGFiDQotIFVzaW5nIHRoZSBsaWJyYXJ5KCkgZnVuY3Rpb24gdG8gbG9hZCBwYWNrYWdlcyB3aGljaCBhcmUgYWxyZWFkeSBpbnN0YWxsZWQuDQoNCmBgYHtyfQ0KbGlicmFyeShwbG90M0QpDQpgYGANCg0KKipzY2F0dGVyMkQoKSoqIGlzIGEgZnVuY3Rpb24gd2l0aCBpbiAqKlBsb3QzRCoqIGxpYnJhcnkgdGhhdCBpcyB1c2VkIHRvIHBvbHQgYSBDb2xvcmVkIHNjYXR0ZXIgcGxvdHMgd2l0aCBhIGNvbG9yIHZhcmlhYmxlIGFzIHBvaW50cy4NCg0KYGBge3J9DQpzY2F0dGVyMkQoZGF0YVssMV0sIGRhdGFbLDJdLGNvbHZhcj1kYXRhWyw2XSxwY2ggPSAxNiwgeGxhYiA9ICJFYXN0aW5nKG0pIiwgeWxhYiA9ICJOb3J0aGluZyhtKSIsY2xhYj0iemluYyIsbWFpbj0iemluYyBjb25jZW50cmF0aW9ucyhwcG0pIiwgYXNwPTEpDQpgYGANCg0KIyMjIyMgKirigKIgVmlzdWFsaXphdGlvbiBvZiBjYXRlZ29yaWNhbCBWYXJpYWJsZXMgKGUuZy4sIFNvaWwgVHlwZSkqKg0KDQpgYGB7cn0NCnNjYXR0ZXIyRChkYXRhWywxXSxkYXRhWywyXSxjb2x2YXI9ZGF0YVssMTBdLGNvbD1jKCJibHVlIiwgImdyZWVuIiwgImJyb3duIikscGNoPTE2LCB4bGFiPSJFYXN0aW5nKG0pIiwgeWxhYj0iTm9ydGhpbmcobSkiLCBjbGFiPWNvbG5hbWVzKGRhdGEpWzEwXSxtYWluPSJTb2lsIFR5cGUiLCBhc3A9MSwgY2V4PTAuOCkNCg0KIyBBZGQgbGVnZW5kIHRvIHRoZSBwbG90IHVzaW5nIHRoZSBsZWdlbmQoKSBmdW5jdGlvbg0KbGVnZW5kKCJib3R0b21yaWdodCIsbGVnZW5kPWMoIlNvaWwgLSAxIiwiU29pbCAtIDIiLCAiU29pbCAtIDMiKSwgZmlsbD1jKCJibHVlIiwgImdyZWVuIiwgImJyb3duIiksIGNleD0xKQ0KYGBgDQoNCiMjIyMgKipUYXNrIDQ6IFVuaXZhcmlhdGUgRURBKioNCg0KKirigKIgQ29tcHV0ZSB0aGUgbWFpbiBkZXNjcmlwdGl2ZSB1bml2YXJpYXRlIHN0YXRpc3RpY3MgZm9yIHRoZSA0IGhlYXZ5IG1ldGFscyoqDQoNCmBgYHtyfQ0Kc3VtbWFyeShkYXRhWyAsMzo2XSkNCmBgYA0KDQoqKlF1ZXN0aW9uczoqKg0KDQotICAgT2JzZXJ2ZSB0aGUgbWluaW11bSwgZmlyc3QgcXVhcnRpbGUsIG1lZGlhbiwgdGhpcmQgcXVhcnRpbGUsIGFuZCBtYXhpbXVtIGhlYXZ5IG1ldGFsIGNvbmNlbnRyYXRpb25zIGluIHRoZSBzYW1wbGUgc2V0Pw0KLSAgIENvbXBhcmUgdGhlIG1lYW4gYW5kIG1lZGlhbi4NCg0KKirigKIgRGlzcGxheSBoaXN0b2dyYW1zIGFuZCBib3hwbG90cyBvZiBhbGwgZm91ciBoZWF2eSBtZXRhbHMgY29uY2VudHJhdGlvbnMqKg0KDQpgYGB7cn0NCmhpc3QoZGF0YSRjYWRtaXVtLCBjb2w9ImJsdWUiLCB4bGFiPSJWYWx1ZXMiLG1haW49ImNhZG1pdW0gY29uY2VudHJhdGlvbnMiKQ0KaGlzdChkYXRhJGNvcHBlcixjb2w9ImJsdWUiLCB4bGFiPSJWYWx1ZXMiLG1haW49ImNvcHBlciBjb25jZW50cmF0aW9ucyIpDQpoaXN0KGRhdGEkbGVhZCxjb2w9ImJsdWUiLCB4bGFiPSJWYWx1ZXMiLG1haW49ImxlYWQgY29uY2VudHJhdGlvbnMiKQ0KaGlzdChkYXRhJHppbmMsY29sPSJibHVlIiwgeGxhYj0iVmFsdWVzIixtYWluPSJ6aW5jIGNvbmNlbnRyYXRpb25zIikNCmBgYA0KDQpgYGB7cn0NCmJveHBsb3QoZGF0YSRjYWRtaXVtLGNvbD0iYmx1ZSIsIHhsYWI9IlZhbHVlcyIsbWFpbj0iY2FkbWl1bSBjb25jZW50cmF0aW9ucyIpDQpib3hwbG90KGRhdGEkY29wcGVyLGNvbD0iYmx1ZSIsIHhsYWI9IlZhbHVlcyIsbWFpbj0iY29wcGVyIGNvbmNlbnRyYXRpb25zIikNCmJveHBsb3QoZGF0YSRsZWFkLGNvbD0iYmx1ZSIsIHhsYWI9IlZhbHVlcyIsbWFpbj0ibGVhZCBjb25jZW50cmF0aW9ucyIpDQpib3hwbG90KGRhdGEkemluYyxjb2w9ImJsdWUiLCB4bGFiPSJWYWx1ZXMiLG1haW49InppbmMgY29uY2VudHJhdGlvbnMiKQ0KYGBgDQoNCioqUXVlc3Rpb25zOioqDQoNCi0gRGVzY3JpYmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGVzZSB2YXJpYWJsZXMuIERvIHRoZXkgaGF2ZSBhIHN5bW1ldHJpYyBvciBza2V3ZWQgZGlzdHJpYnV0aW9uPw0KLSAgIEZvciBlYWNoIHZhcmlhYmxlOiBEb2VzIGl0IGFwcGVhciB0byBoYXZlIG1vcmUgdGhhbiBvbmUgcG9wdWxhdGlvbj8gRG9lcyBpdCBhcHBlYXIgdG8gYmUgYW55IGV4dHJlbWUgdmFsdWVzICgib3V0bGllcnMiKT8NCg0KPGJyPg0KDQojIyMjICoqVGFzayA1OiBMb2ctVHJhbnNmb3JtIHRoZSBkYXRhKioNCg0KVGhlIGxvZyB0cmFuc2Zvcm1hdGlvbiBjYW4gYmUgdXNlZCB0byBtYWtlIGhpZ2hseSBza2V3ZWQgZGlzdHJpYnV0aW9ucyBsZXNzIHNrZXdlZC4gVGhpcyBjYW4gYmUgdmFsdWFibGUgYm90aCBmb3IgbWFraW5nIHBhdHRlcm5zIGluIHRoZSBkYXRhIG1vcmUgaW50ZXJwcmV0YWJsZSBhbmQgZm9yIGhlbHBpbmcgdG8gbWVldCB0aGUgYXNzdW1wdGlvbnMgb2YgaW5mZXJlbnRpYWwgc3RhdGlzdGljcy4gICANCg0KDQpIZWF2eSBtZXRhbCBjb25jZW50cmF0aW9uIHVzdWFsbHkgaXMgbm90IHN5bW1ldHJpYy4gTG9nLXRyYW5zZm9ybSB0aGUgaGlnaGx5LXNrZXdlZCBkaXN0cmlidXRpb24uIFVzaW5nIHRoZSAqKmxvZzEwKCkqKiBmdW5jdGlvbiBhZGQgZm91ciBjb2x1bW5zIHRvIHRoZSBkYXRhZnJhbWUgKG9uZSBmb3IgZWFjaCBsb2cgdHJhbnNmb3JtZWQgaGVhdnkgbWV0YWwgY29uY2VudHJhdGlvbikuIA0KDQpgYGB7cn0NCmRhdGEkbG9nQ2QgPSBsb2cxMChkYXRhJGNhZG1pdW0pDQpkYXRhJGxvZ0N1ID0gbG9nMTAoZGF0YSRjb3BwZXIpDQpkYXRhJGxvZ1BiID0gbG9nMTAoZGF0YSRsZWFkKQ0KZGF0YSRsb2dabiA9IGxvZzEwKGRhdGEkemluYykNCmBgYA0KDQoqKuKAoiBEaXNwbGF5IGhpc3RvZ3JhbXMgYW5kIGJveHBsb3RzIG9mIHJhdyBhbmQgbG9nIHRyYW5zZm9ybWVkIGhlYXZ5IG1ldGFscyBjb25jZW50cmF0aW9ucyBzaWRlIGJ5IHNpZGUqKg0KDQoNCmBgYHtyfQ0KIyB1c2UgcGFyKCkgZnVuY3Rpb24gdG8gY29udmVydCB0aGUgZ3JhcGhpY2FsIHdpbmRvdyBpbnRvIDItYnktMSBzdWItc2NyZWVucyANCnBhcihtZnJvdz1jKDEsMikpIA0KDQojICBEaXNwbGF5IGhpc3RvZ3JhbXMgb2YgcmF3IGFuZCBsb2cgdHJhbnNmb3JtZWQgZGF0YQ0KZm9yKGogaW4gMTo0KXsNCiAgaGlzdChkYXRhWyxqKzJdLGNvbD0iYmx1ZSIsIHhsYWI9IlZhbHVlcyIsbWFpbj1jb2xuYW1lcyhkYXRhKVtqKzJdKQ0KICBoaXN0KGRhdGFbLGorMTNdLGNvbD0icmVkIiwgeGxhYj0iVmFsdWVzIixtYWluPWNvbG5hbWVzKGRhdGEpW2orMTNdKQ0KfQ0KYGBgDQoNCmBgYHtyfQ0KcGFyKG1mcm93PWMoMSwyKSkgDQoNCiMgIERpc3BsYXkgYm94cGxvdCBvZiByYXcgYW5kIGxvZyB0cmFuc2Zvcm1lZCBkYXRhDQpmb3IoaiBpbiAxOjQpew0KICBib3hwbG90KGRhdGFbLGorMl0sY29sPSJibHVlIiwgeGxhYj0iVmFsdWVzIixtYWluPWNvbG5hbWVzKGRhdGEpW2orMl0pDQogIGJveHBsb3QoZGF0YVssaisxM10sY29sPSJyZWQiLCB4bGFiPSJWYWx1ZXMiLG1haW49Y29sbmFtZXMoZGF0YSlbaisxM10pDQp9DQpgYGANCioqUXVlc3Rpb25zOioqICAgDQotIERpZmZlcmVuY2Ugb2YgZGlzdHJpYnV0aW9uIGJldHdlZW4gdGhlIHJhdyBkYXRhIGFuZCBsb2ctdHJhbnNmb3JtZWQ/ICAgDQotIElzIHRoZSBza2V3bmVzcyBvZiBvcmlnaW5hbCBkaXN0cmlidXRpb24gY2hhbmdlZCByZW1hcmthYmx5Pw0KDQo8YnI+DQoNCiMjIyMgKipUYXNrIDY6IFN0YXRpc3RpY3MgdnMgY2F0ZWdvcnkqKg0KDQoqKuKAoiBDb21wdXRlIHN1bW1hcnkgc3RhdGlzdGljcyAoZS5nLiBtZWFuKSBvZiBhbGwgZm91ciBoZWF2eSBtZXRhbCBjb25jZW50cmF0aW9ucyBieSBzb21lIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyAoZS5nLiBzb2lsIHR5cGUpKioNCg0KYGBge3J9DQphZ2dyZWdhdGUoZGF0YVsgLDM6Nl0sIGJ5PWxpc3QoZGF0YSRzb2lsKSwgRlVOPW1lYW4sIG5hLnJtPVRSVUUpDQpgYGANCg0KLSAgIGFnZ3JlZ2F0ZSgpIGZ1bmN0aW9uIFNwbGl0cyB0aGUgZGF0YSBpbnRvIHN1YnNldHMsIGNvbXB1dGVzIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgZWFjaCwgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBpbiBhIGNvbnZlbmllbnQgZm9ybS4NCi0gICBieT1saXN0KCkgaXMgYSBsaXN0IG9mIGdyb3VwaW5nIGVsZW1lbnRzLg0KLSAgIEZVTiA9IGEgZnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIHdoaWNoIGNhbiBiZSBhcHBsaWVkIHRvIGFsbCBkYXRhIHN1YnNldHMuDQotICAgbmEucm0gPSBUUlVFIGV4Y2x1ZGVzIG1pc3NpbmcgdmFsdWVzIHdoZW4gY2FsY3VsYXRpbmcgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBpbiBSLg0KDQoqKuKAoiBEaXNwbGF5IHRoZSBoaXN0b2dyYW0gYW5kIGJveHBsb3Qgb2YgaGVhdnkgbWV0YWxzIGNvbmNlbnRyYXRpb25zIGJ5IHNvbWUgY2F0ZWdvcmljYWwgdmFyaWFibGVzKioNCg0KVG8gZG8gdGhpcyB0YXNrLCB5b3UgY2FuIHVzZSAqKmhpc3RvZ3JhbSgpKiogZnVuY3Rpb24gZnJvbSB0aGUgKipsYXR0aWNlIGxpYnJhcnkqKiBhbmQgdGhlIGFscmVhZHkgZmFtaWxhciAqKmJveHBsb3QoKSoqIGZ1bmN0aW9uIGZyb20gdGhlIFItQmFzZS4NCg0KYGBge3J9DQojIEZpcnN0IGluc3RhbGwgYW5kIGxvYWQgdGhlIGxhdHRpY2UgbGlicmFyeQ0KaW5zdGFsbC5wYWNrYWdlcygibGF0dGljZSIpDQpsaWJyYXJ5KGxhdHRpY2UpDQpgYGANCg0KYGBge3J9DQojIEhpc3RvZ3JhbSBvZiB6aW5jIGNvbmNlbnRyYXRpb25zIGZvciBlYWNoIHNvaWwgdHlwZQ0KaGlzdG9ncmFtKH4gZGF0YSR6aW5jfGFzLmZhY3RvcihkYXRhJHNvaWwpLGNvbD0iYmx1ZSIseGxhYj0gIlppbmMgY29uY2VybnRyYXRpb24iKQ0KDQojIGJveHBsb3Qgb2YgemluYyBjb25jZW50cmF0aW9uIGZvciBmbG9vZCBmcmVxdWVuY2llcyANCmJveHBsb3QoZGF0YSR6aW5jIH4gZGF0YSRmZnJlcSwgY29sPSJibHVlIix4bGFiPSJGbG9vZCBGcmVxdWVuY3kgQ2xhc3MiLCANCnlsYWI9ICJaaW5jIGNvbmNlcm50cmF0aW9uIikNCmBgYA0KDQoqKlF1ZXN0aW9uczoqKg0KDQotICAgd2hpY2ggc29pbCB0eXBlIGlzIG1vcmUgY29udGFtaW5hdGVkIHdpdGggemluYz8NCi0gICBJcyBmbG9vZCBmcmVxdWVuY3kgYXNzb2NpYXRlZCB3aXRoIHppbmMgY29uY2VudHJhdGlvbiA/DQoNCjxicj4NCg0KIyMjIyAqKlRhc2sgNzogQml2YXJpYXRlIEVEQSoqDQoNCg0KKirigKIgUGxvdCBzY2F0dGVyIHBsb3RzIGJldHdlZW4gdHdvIHZhcmlhYmxlcyAoZm9yIGUuZy4gemluYyBhbmQgY29wcGVyKSoqDQoNCmBgYHtyfQ0KcGxvdChkYXRhJHppbmMgfiBkYXRhJGNvcHBlciwgeGxhYj0iQ29wcGVyIGNvbmNlbnRyYXRpb24iLCB5bGFiPSJ6aW5jIGNvbmNlbnRyYXRpb24iKQ0KDQpwbG90KGRhdGEkbG9nWm4gfiBkYXRhJGxvZ0N1LCB4bGFiPSJsb2cxMCBDb3BwZXIgY29uY2VudHJhdGlvbiIsIHlsYWI9ImxvZzEwIHppbmMgY29uY2VudHJhdGlvbiIpDQpgYGANCg0KKipRdWVzdGlvbnM6KioNCg0KLSAgIERlc2NyaWJlIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGNvcHBlciBhbmQgemluYyBjb25jZW50cmF0aW9ucy4NCi0gICBBcmUgdGhlcmUgYW55IG9ic2VydmF0aW9ucyB0aGF0IGRvIG5vdCBmaXQgdGhlIGdlbmVyYWwgcGF0dGVybj8gV2hhdCBjb3VsZCBiZSB0aGUgcmVhc29uKHMpPw0KDQoqKuKAoiBPYnNlcnZlIHRoZSBzYW1wbGVzIHRoYXQgZG8gbm90IGZpdCB0aGUgZ2VuZXJhbCBwYXR0ZXJuKioNCg0KYGBge3J9DQppeD13aGljaCgoZGF0YSRsb2dabiA8IDIuNikgJiAoZGF0YSRsb2dDdSA+IDEuNikpDQpwcmludChpeCkNCnByaW50KGRhdGFbaXgsIF0pDQpgYGANCg0KKirigKIgQ29tcHV0ZSB0aGUgc2NhdHRlciBwbG90IG1hdHJpeCBmb3IgYWxsIHBhaXJzIG9mIHZhcmlhYmxlcyB1c2luZyBwYWlyKCkgZnVuY3Rpb24qKg0KYGBge3J9DQpwYWlycyhkYXRhWyAsMzo2XSwgcGNoID0gMTksIGNleD0wLjUpDQpwYWlycyhkYXRhWyAsMTQ6MTddLCBwY2ggPSAxOSwgY2V4PTAuNSkNCmBgYA0KDQoqKuKAoiBDb21wdXRlIHRoZSBQZWFyc29uIGNvcnJlbGF0aW9uIG1hdHJpeCoqDQoNCmBgYHtyfQ0KQ29yTWF0ID0gY29yKGRhdGFbICwzOjZdKQ0KcHJpbnQoQ29yTWF0KSANCg0KIyBDb21wdXQgUGVhcnNvbiBjb3JyZWxhdGlvbiBtYXRyaXggZm9yIGxvZy10cmFuc2Zvcm1lZCB2YXJpYWJsZXMNCkNvck1hdExvZyA9IGNvcihkYXRhWyAsMTQ6MTddKQ0KcHJpbnQoQ29yTWF0TG9nKSANCmBgYA0KDQoqKlF1ZXN0aW9uczoqKiAgICANCi0gRGlzY3VzcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHBhaXJzIG9mIHZhcmlhYmxlcyA/IFRoZSBuYXR1cmUgKHNpZ24pIGFuZCBzdHJlbmd0aCBvZiB0aGUgcmVsYXRpb25zaGlwcz8gICAgDQotIFRoZSBwb3NzaWJsZSByZWFzb24gYmVoaW5kIHRoaXMgdHlwZSBvZiBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBoZWF2eSBtZXRhbHM/DQoNCioq4oCiIENvbXB1dGUgdGhlIFBlYXJzb24gY29ycmVsYXRpb24gbWF0cml4IHVzaW5nIENvcnJwbG90IGxpYnJhcnkqKg0KDQpjb3JycGxvdCBpcyBhbiBSIHBhY2thZ2UgdGhhdCBwcm92aWRlcyBhIHZpc3VhbCBleHBsb3JhdG9yeSB0b29sIG9uIGNvcnJlbGF0aW9uIG1hdHJpeCB0aGF0IHN1cHBvcnRzIHJpY2ggdmlzdWFsaXphdGlvbiBtZXRob2QsIGdyYXBoaWMgbGF5b3V0LCBjb2xvciBhbmQgbGVnZW5kLiBUaGUgKipjb3JycGxvdCgpKiogZnVuY3Rpb24gdGFrZXMgYW4gZXhpc3RpbmcgY29ycmVsYXRpb24gbWF0cml4IGFzIGFyZ3VtZW50DQoNCmBgYHtyfQ0KIyBJbnN0YWxsIGFuZCBsb2FkIGNvcnJwbG90IHBhY2thZ2UNCmluc3RhbGwucGFja2FnZXMoImNvcnJwbG90IikNCmxpYnJhcnkoY29ycnBsb3QpDQpgYGANCg0KYGBge3J9DQojIFBlYXJzb24gQ29ycmVsYXRpb24gTWF0cml4IA0KY29ycnBsb3QoQ29yTWF0KQ0KY29ycnBsb3QoQ29yTWF0TG9nKQ0KYGBgDQoNCg0KDQoNCg0KDQo=