Project Description
Chemical properties, such as the boiling point, can be derived from the structure of a chemical compound. In 1947, Harry Wiener already made a correlation model to link structural features to boiling points (ref1). The idea to use mathematical models to predict chemical properties from compound structures has been expaned since then.
In this project I use a SPARQL query to obtain the smiles and boiling points of simple alkanes from WikiData (ref2). I use the smiles to get descriptors from the chemical development kit (CDK) database (ref3-6). These descriptors contain information on the structural properties of the alkanes (see section 2 for more details). Finally, I train a Partial Least Squares (PLS) model to predict these properties from the chemical properties of the compounds and plot the results.
0. Installation
The project requires several packages. The piece of code in this section checks automatically for missing packages and installs them. The rJava package requires Java to be installed. The code has been developed using Java version 1.8.0_191. A tutorial on how to install this Java Version on windows can be found here.
#Required packages for this code.
packages <- c("WikidataQueryServiceR",
"rJava",
"rcdk",
"stringi",
"caTools",
"pls",
"Metrics"
)
#Installation of missing packages.
if (length(setdiff(packages, rownames(installed.packages()))) > 0) {
install.packages(setdiff(packages, rownames(installed.packages())))
}
#Load packages.
library("WikidataQueryServiceR")
library('rJava')
library('rcdk')
library('stringi')
library('caTools')
library('pls')
library('Metrics')
1. Get the data from WikiData.
In this section I use the WikidataQueryServiceR package to run a SPARQL query that obtains all simple alkines with known boiling points and smiles from WikiData.
#The function query_wikidata runs the SPARQL query to obtain data from the Wikidata database. This returns a dataframe containing the alkane names, boiling points, units and smiles. It also contains the wikidata-links for compounds and units. There are 134 alkanes at the time of writing.
data_wikidata = query_wikidata('SELECT DISTINCT ?comp ?compLabel ?bp ?bpUnit ?bpUnitLabel ?smiles WHERE {
?comp wdt:P31/wdt:P279* wd:Q41581 ;
p:P2102 [ps:P2102 ?bp ;
psv:P2102/wikibase:quantityUnit
?bpUnit
] ;
wdt:P233 ?smiles.
SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
} '
)
Description of WikiData numbers:
* P31 = instance of
* P279 = subclass of
* Q41581 = alkane
* P2102 = boiling point
* P233 = canonical smiles
The SPARQL query can easily be changed to obtain different data. For example, one could get the smiles of alkynes instead of alkanes by changing Q41581 into Q159226. The numbers can be found here.
2. Get the descriptors of the alkane structures
In this section, I use four descriptors for all alkanes from CDK. I obtain the descriptors based on the alkane smiles. These descriptors are used later as predictors in the PLS model.
APolDescriptor:
This describes the sum of the atomic polarizabilities.
WienerNumbersDescriptor:
This gives the Wiener numbers (see ref1). It returns the Wiener Path Number and the Wiener Polarity Number.
MDEDescriptor:
Returns the Molecular Distance Edge.
FragmentComplexityDescriptor:
Returns the complexity of a system.
#Reformatting of smiles into correct format for CDK. Kekulise checks for electrons, and prevents parsing of incorrect smiles.
parsed_smiles <- parse.smiles(data_wikidata$smiles, kekulise=TRUE)
#Determine which descriptors are of interest. To add/remove descriptors, change this vector to include the desired descriptors.
descriptor_names <- c(
'org.openscience.cdk.qsar.descriptors.molecular.APolDescriptor',
'org.openscience.cdk.qsar.descriptors.molecular.WienerNumbersDescriptor',
'org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor',
'org.openscience.cdk.qsar.descriptors.molecular.FragmentComplexityDescriptor'
)
#Get descriptor values form the CDK database.
data_descriptors <- eval.desc(parsed_smiles, descriptor_names)
#At the time of writing, this returns a dataframe of 23 variables describing 134 alkanes.
3. Data pre-processing
Before we use the descriptors and boiling points to train a PLS model, some data pre-processing needs to be done.
3a) Not all boiling points in the database are in kelvin, which is the SI unit for temperature. Before I continue, I make sure all temperatures are in kelvin.
##CELCIUS -> KELVIN
#Find all rownumbers of the values in Celcius.
celcius <- which(grepl('degree Celsius', data_wikidata$bpUnitLabel))
for (i in 1:length(celcius)) {
#Convert the value to kelvin.
data_wikidata$bp[celcius[i]] <- data_wikidata$bp[celcius[i]] + 273.15
#Convert the label to kelvin.
data_wikidata$bpUnitLabel[celcius[i]] <- 'kelvin'
}
##FAHRENHEIT -> KELVIN
#Find all rownumbers of the values in Fahrenheit.
fahrenheit <- which(grepl('degree Fahrenheit',data_wikidata$bpUnitLabel))
for (i in 1:length(fahrenheit)) {
#Convert the value to kelvin.
data_wikidata$bp[fahrenheit[i]] <- (data_wikidata$bp[fahrenheit[i]] + 459.67) * (5/9)
#Convert the label to kelvin.
data_wikidata$bpUnitLabel[fahrenheit[i]] <- 'kelvin'
}
3b) Next, I combine the descriptors and boiling points in one dataset, so we can use this dataset for the PLS. I remove the columns with (almost) all zeros, because these descriptors do not have much predictive value.
#Fuse the boiling points from data_wikidata and data_descriptors into one dataset.
data_combined <- data.frame(data_descriptors, data_wikidata$bp)
#Define which columns to remove. To include one of these columns as a predictor, remove the column here.
remove_columns <- c("MDEC.13", "MDEC.14", "MDEC.22", "MDEC.23",
"MDEC.24", "MDEC.33", "MDEC.34", "MDEC.44",
"MDEO.11", "MDEO.12", "MDEO.22", "MDEN.11",
"MDEN.12", "MDEN.13", "MDEN.22", "MDEN.23",
"MDEN.33"
)
#Remove all chosen columns from the dataset.
data_combined <- data_combined[ , -which(names(data_combined) %in% remove_columns)]
#At the time of writing, this returns a dataframe of 7 variables describing 134 alkanes.
3c) In order to check the quality of the data, it is necessary to make a few plots. First, we plot the boiling points, to see whether any of them have a very abnormal value, i.e. are an outlier.
#Plot the boiling points to check for outliers.
plot(data_combined$data_wikidata.bp,
main = "Boiling point values",
ylab = "boiling point (Kelvin)",
cex.main = 0.8
)

None of the values in figure 1 have a very abnormal value. Thus, there are no outliers that need to be removed. Furthermore, one could expect that, in general, bigger alkanes have a higher boiling point. To test whether this relationship is also present in our dataset we plot the length of the smiles vs. the boiling points.
#Plot length of smiles vs. boiling points to check for a correlation
plot(stri_length(data_wikidata$smiles),
data_wikidata$bp,
main = "Figure 2: Length of smiles vs. boiling point",
xlab = "length of smiles",
ylab = "boiling point (Kelvin)",
cex.main = 0.8
)

As expected, in figure 2 we do indeed see that there is a trend towards higher boiling points for bigger smiles.
3d) To be able to test how well the model I train performs, it is necessary to keep a part of my data aside as test set. Therefore, I split the data into a training and test set. I use 25% of my dataset as test set.
#Percentage of data to go into the test-set. Change this value to change the relative sizes of the training and test sets.
percentage_test <- 0.25
#Split the data into a training and test set.
set.seed(88)
sample = sample.split(data_combined, SplitRatio = percentage_test)
train = subset(data_combined, sample == FALSE)
test = subset(data_combined, sample == TRUE)
#At the time of writing 134 alkanes are split into a training set of 115 alkanes and test set of 19 alkanes.
Note: The error of both the null and PLS models is dependent on the random splitting in training and test set. If you run the code, it is expected that the error differs from the error reported here.
4. Null model
The null model is the most simple model possible; the mean of the data. This model can be used as a reference to see whether the PLS model performs better.
#Create null_model.
null_model <- mean(train$data_wikidata.bp)
#Calculate the error of the null model.
error_null_model <- rmse(test$data_wikidata.bp, null_model)
#Print the error.
print(paste0("The error of the Null model is ", error_null_model))
[1] "The error of the Null model is 241.420207469871"
5. Partial Least Squares
Here, I train the PLS model with the training data, and use the model to predict the boiling points of the test data. If you are unfamiliar with PLS, this is video a good introduction. In the model I use leave-one-out (LOO) validation, because my data set is small.
#Training of the PLS model, using leave-one-out validation.
PLS_model <- plsr(data_wikidata.bp ~ .,
data = train,
validation = "LOO"
)
#Calculate the error of the PLS model.
error_PLS <- RMSEP(PLS_model, newdata = test)
#Use the model to predict the boiling points of the test set.
predicted_bp <- predict(PLS_model, test)
The plsr function trains several models with different numbers of components. As plsr is used here, it determines the number of components by itself (and will generally use 1 up to (descriptors - 1) components). It also possible to fix the number of components by using the ncomp = value argument.
RSMEP will calculate the error for each model built by plsr and therefore, error_PLS will contain a vector containing the error for each number of components (see section 6 for visualization of this).
The function predict will predict the boiling points based on each mode in PLS_model.
6. Results
6a) In order to identify the model with the best number of components, it is needed to plot the error vs. the number of components.
#Plot the error vs. the number of PLS components
plot(error_PLS,
main = "Figure 3: Error vs. number of components",
xlab= "number of PLS components",
ylab = "Error (Kelvin)",
cex.main = 0.8
)

Conclusion:
From figure 3 we see can conclude that the error decreases with adding more components untill we have 3 PLS components. From there, adding more components doesn’t decrease the error further. Therefore, from here on I use the PLS model with 3 components. The error is much lower than the error of the null model, indicating the PLS model performs better than the null model.
6b) Using this PLS model with 3 components, I visualize how well it can predict boiling points based on the descriptors (described in section 2). To do this, I plot the predicted boiling points vs. the actual boiling points. If the prediction is perfect, we’d expect all the points to line up in a perfectly linear correlation.
#Plot the predicted data vs. the original data.
plot(test$data_wikidata.bp,
predicted_bp[,,3],
xlim=c(300,1000),
ylim=c(300,1000),
main = "Figure 4: Predicted boiling points vs. measured boiling points",
cex.main = 0.8,
xlab = "measured boiling points (Kelvin)",
ylab = "predicted boiling points (Kelvin)"
)
#Add null model to the plot
lines(test$data_wikidata.bp,
rep(null_model,
length(test$data_wikidata.bp)
),
col = "red"
)
#Add the perfect prediction to the plot
lines(test$data_wikidata.bp,
test$data_wikidata.bp,
col = "green"
)
#Add legend
legend("topleft",
c("Null model", "Perfect model", "PLS model"),
col = c("red", "green", "black"),
pch = c(16),
bty = 'n'
)

Conclusion:
In figure 4, we see that the PLS model (in black) performs much better in predicting the boiling points than the null model (in red). The PLS model comes quite close to the perfect prediction model, shown in green.
7. References
ref1: Wiener H. Structural Determination of Paraffin Boiling Points. Journal of the American Chemical Society. 1947 Jan;69(1):17-20.
ref2: https://www.wikidata.org/wiki/Wikidata:Main_Page (12-10-2019)
ref3: Willighagen et al. The Chemistry Development Kit (CDK) v2.0: atom typing, depiction, molecular formulas, and substructure searching. J. Cheminform. 2017; 9(3), doi:10.1186/s13321-017-0220-4
ref4: May and Steinbeck. Efficient ring perception for the Chemistry Development Kit. J. Cheminform. 2014, doi:10.1186/1758-2946-6-3
ref5: Steinbeck et al. Recent Developments of the Chemistry Development Kit (CDK) - An Open-Source Java Library for Chemo- and Bioinformatics. Curr. Pharm. Des. 2006; 12(17):2111-2120, doi:10.2174/138161206777585274
ref6: Steinbeck et al. The Chemistry Development Kit (CDK): An Open-Source Java Library for Chemo- and Bioinformatics. J. Chem. Inf. Comput. Sci. 2003 Mar-Apr; 43(2):493-500, doi:10.1021/ci025584y
Package references
* WikidataQuerySerivceR
Mikhail Popov (2017). WikidataQueryServiceR: API Client Library for ‘Wikidata Query Service’. R package version 0.1.1.
*rJava
Simon Urbanek (2019). rJava: Low-Level R to Java Interface. R package version 0.9-11.
*rcdk
Guha, R. (2007). ‘Chemical Informatics Functionality in R’. Journal of Statistical Software 6(18)
*stringi
Gagolewski M. and others (2019). R package stringi: Character string processing facilities.
*caTools
Jarek Tuszynski (2019). caTools: Tools: moving window statistics, GIF, Base64, ROC AUC, etc.. R package version 1.17.1.2
*pls
Bjørn-Helge Mevik, Ron Wehrens and Kristian Hovde Liland (2019). pls: Partial Least Squares and Principal Component Regression. R package version 2.7-1.
*Metrics
Ben Hamner and Michael Frasco (2018). Metrics: Evaluation Metrics for Machine Learning. R package version 0.1.4.
LS0tDQp0aXRsZTogIk1TQjEwMTVfQXNzaWdubWVudCAyIg0KYXV0aG9yOiAiU3V6YW5uZSB0ZW4gSGFnZSAtIGk2MjAxNTk2Ig0KZGF0ZTogIjEzIE9jdG9iZXIgMjAxOSINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCioqUHJvamVjdCBEZXNjcmlwdGlvbioqIDxici8+DQpDaGVtaWNhbCBwcm9wZXJ0aWVzLCBzdWNoIGFzIHRoZSBib2lsaW5nIHBvaW50LCBjYW4gYmUgZGVyaXZlZCBmcm9tIHRoZSBzdHJ1Y3R1cmUgb2YgYSBjaGVtaWNhbCBjb21wb3VuZC4gSW4gMTk0NywgSGFycnkgV2llbmVyIGFscmVhZHkgbWFkZSBhIGNvcnJlbGF0aW9uIG1vZGVsIHRvIGxpbmsgc3RydWN0dXJhbCBmZWF0dXJlcyB0byBib2lsaW5nIHBvaW50cyAocmVmMSkuIFRoZSBpZGVhIHRvIHVzZSBtYXRoZW1hdGljYWwgbW9kZWxzIHRvIHByZWRpY3QgY2hlbWljYWwgcHJvcGVydGllcyBmcm9tIGNvbXBvdW5kIHN0cnVjdHVyZXMgaGFzIGJlZW4gZXhwYW5lZCBzaW5jZSB0aGVuLiANCg0KSW4gdGhpcyBwcm9qZWN0IEkgdXNlIGEgU1BBUlFMIHF1ZXJ5IHRvIG9idGFpbiB0aGUgc21pbGVzIGFuZCBib2lsaW5nIHBvaW50cyBvZiBzaW1wbGUgYWxrYW5lcyBmcm9tIFdpa2lEYXRhIChyZWYyKS4gSSB1c2UgdGhlIHNtaWxlcyB0byBnZXQgZGVzY3JpcHRvcnMgZnJvbSB0aGUgY2hlbWljYWwgZGV2ZWxvcG1lbnQga2l0IChDREspIGRhdGFiYXNlIChyZWYzLTYpLiBUaGVzZSBkZXNjcmlwdG9ycyBjb250YWluIGluZm9ybWF0aW9uIG9uIHRoZSBzdHJ1Y3R1cmFsIHByb3BlcnRpZXMgb2YgdGhlIGFsa2FuZXMgKHNlZSBzZWN0aW9uIDIgZm9yIG1vcmUgZGV0YWlscykuIEZpbmFsbHksIEkgdHJhaW4gYSBQYXJ0aWFsIExlYXN0IFNxdWFyZXMgKFBMUykgbW9kZWwgdG8gcHJlZGljdCB0aGVzZSBwcm9wZXJ0aWVzIGZyb20gdGhlIGNoZW1pY2FsIHByb3BlcnRpZXMgb2YgdGhlIGNvbXBvdW5kcyBhbmQgcGxvdCB0aGUgcmVzdWx0cy4gDQoNCioqMC4gSW5zdGFsbGF0aW9uKiogPGJyLz4NClRoZSBwcm9qZWN0IHJlcXVpcmVzIHNldmVyYWwgcGFja2FnZXMuIFRoZSBwaWVjZSBvZiBjb2RlIGluIHRoaXMgc2VjdGlvbiBjaGVja3MgYXV0b21hdGljYWxseSBmb3IgbWlzc2luZyBwYWNrYWdlcyBhbmQgaW5zdGFsbHMgdGhlbS4gVGhlIHJKYXZhIHBhY2thZ2UgcmVxdWlyZXMgSmF2YSB0byBiZSBpbnN0YWxsZWQuIFRoZSBjb2RlIGhhcyBiZWVuIGRldmVsb3BlZCB1c2luZyBKYXZhIHZlcnNpb24gMS44LjBfMTkxLiBBIHR1dG9yaWFsIG9uIGhvdyB0byBpbnN0YWxsIHRoaXMgSmF2YSBWZXJzaW9uIG9uIHdpbmRvd3MgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL2Rvd25saW5rby5jb20vZG93bmxvYWQtaW5zdGFsbC1qZGstOC13aW5kb3dzLmh0bWwpLiANCg0KYGBge3IgaW5jbHVkZSA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQ0KI1JlcXVpcmVkIHBhY2thZ2VzIGZvciB0aGlzIGNvZGUuIA0KcGFja2FnZXMgPC0gYygiV2lraWRhdGFRdWVyeVNlcnZpY2VSIiwgDQogICAgICAgICAgICAgICJySmF2YSIsIA0KICAgICAgICAgICAgICAicmNkayIsIA0KICAgICAgICAgICAgICAic3RyaW5naSIsDQogICAgICAgICAgICAgICJjYVRvb2xzIiwNCiAgICAgICAgICAgICAgInBscyIsIA0KICAgICAgICAgICAgICAiTWV0cmljcyINCiAgICAgICAgICAgICAgKQ0KDQojSW5zdGFsbGF0aW9uIG9mIG1pc3NpbmcgcGFja2FnZXMuDQppZiAobGVuZ3RoKHNldGRpZmYocGFja2FnZXMsIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKSkpID4gMCkgew0KICAgICBpbnN0YWxsLnBhY2thZ2VzKHNldGRpZmYocGFja2FnZXMsIHJvd25hbWVzKGluc3RhbGxlZC5wYWNrYWdlcygpKSkpIA0KICAgIH0NCg0KDQojTG9hZCBwYWNrYWdlcy4NCmxpYnJhcnkoIldpa2lkYXRhUXVlcnlTZXJ2aWNlUiIpDQpsaWJyYXJ5KCdySmF2YScpDQpsaWJyYXJ5KCdyY2RrJykNCmxpYnJhcnkoJ3N0cmluZ2knKQ0KbGlicmFyeSgnY2FUb29scycpDQpsaWJyYXJ5KCdwbHMnKQ0KbGlicmFyeSgnTWV0cmljcycpDQoNCmBgYA0KDQoqKjEuIEdldCB0aGUgZGF0YSBmcm9tIFdpa2lEYXRhLioqIDxici8+DQpJbiB0aGlzIHNlY3Rpb24gSSB1c2UgdGhlIFdpa2lkYXRhUXVlcnlTZXJ2aWNlUiBwYWNrYWdlIHRvIHJ1biBhIFNQQVJRTCBxdWVyeSB0aGF0IG9idGFpbnMgYWxsIHNpbXBsZSBhbGtpbmVzIHdpdGgga25vd24gYm9pbGluZyBwb2ludHMgYW5kIHNtaWxlcyBmcm9tIFdpa2lEYXRhLg0KDQpgYGB7ciBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCiNUaGUgZnVuY3Rpb24gcXVlcnlfd2lraWRhdGEgcnVucyB0aGUgU1BBUlFMIHF1ZXJ5IHRvIG9idGFpbiBkYXRhIGZyb20gdGhlIFdpa2lkYXRhIGRhdGFiYXNlLiBUaGlzIHJldHVybnMgYSBkYXRhZnJhbWUgY29udGFpbmluZyB0aGUgYWxrYW5lIG5hbWVzLCBib2lsaW5nIHBvaW50cywgdW5pdHMgYW5kIHNtaWxlcy4gSXQgYWxzbyBjb250YWlucyB0aGUgd2lraWRhdGEtbGlua3MgZm9yIGNvbXBvdW5kcyBhbmQgdW5pdHMuIFRoZXJlIGFyZSAxMzQgYWxrYW5lcyBhdCB0aGUgdGltZSBvZiB3cml0aW5nLiANCg0KZGF0YV93aWtpZGF0YSA9IHF1ZXJ5X3dpa2lkYXRhKCdTRUxFQ1QgRElTVElOQ1QgP2NvbXAgP2NvbXBMYWJlbCA/YnAgP2JwVW5pdCA/YnBVbml0TGFiZWwgP3NtaWxlcyBXSEVSRSB7ICAgDQogICAgP2NvbXAgd2R0OlAzMS93ZHQ6UDI3OSogd2Q6UTQxNTgxIDsNCiAgICBwOlAyMTAyIFtwczpQMjEwMiA/YnAgOyAgICAgICAgICAgDQogICAgICAgICAgICAgcHN2OlAyMTAyL3dpa2liYXNlOnF1YW50aXR5VW5pdCAgDQogICAgICAgICAgICAgP2JwVW5pdCAgICAgICAgIA0KICAgICAgICAgICAgXSA7DQogICAgd2R0OlAyMzMgP3NtaWxlcy4NCiAgICBTRVJWSUNFIHdpa2liYXNlOmxhYmVsIHsgYmQ6c2VydmljZVBhcmFtIHdpa2liYXNlOmxhbmd1YWdlICJbQVVUT19MQU5HVUFHRV0sZW4iLiB9IA0KICAgIH0gJw0KKQ0KDQpgYGANCg0KRGVzY3JpcHRpb24gb2YgV2lraURhdGEgbnVtYmVyczogPGJyLz4NCiogUDMxID0gaW5zdGFuY2Ugb2YgPGJyLz4NCiogUDI3OSA9IHN1YmNsYXNzIG9mIDxici8+DQoqIFE0MTU4MSA9IGFsa2FuZSA8YnIvPg0KKiBQMjEwMiA9IGJvaWxpbmcgcG9pbnQgPGJyLz4NCiogUDIzMyA9IGNhbm9uaWNhbCBzbWlsZXMgPGJyLz4NCg0KVGhlIFNQQVJRTCBxdWVyeSBjYW4gZWFzaWx5IGJlIGNoYW5nZWQgdG8gb2J0YWluIGRpZmZlcmVudCBkYXRhLiBGb3IgZXhhbXBsZSwgb25lIGNvdWxkIGdldCB0aGUgc21pbGVzIG9mIGFsa3luZXMgaW5zdGVhZCBvZiBhbGthbmVzIGJ5IGNoYW5naW5nIFE0MTU4MSBpbnRvIFExNTkyMjYuIFRoZSBudW1iZXJzIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly93d3cud2lraWRhdGEub3JnL3dpa2kvV2lraWRhdGE6TWFpbl9QYWdlKS4gDQoNCioqMi4gR2V0IHRoZSBkZXNjcmlwdG9ycyBvZiB0aGUgYWxrYW5lIHN0cnVjdHVyZXMqKiA8YnIvPg0KSW4gdGhpcyBzZWN0aW9uLCBJIHVzZSBmb3VyIGRlc2NyaXB0b3JzIGZvciBhbGwgYWxrYW5lcyBmcm9tIENESy4gSSBvYnRhaW4gdGhlIGRlc2NyaXB0b3JzIGJhc2VkIG9uIHRoZSBhbGthbmUgc21pbGVzLiBUaGVzZSBkZXNjcmlwdG9ycyBhcmUgdXNlZCBsYXRlciBhcyBwcmVkaWN0b3JzIGluIHRoZSBQTFMgbW9kZWwuIA0KDQoqQVBvbERlc2NyaXB0b3I6KiA8YnIvPg0KVGhpcyBkZXNjcmliZXMgdGhlIHN1bSBvZiB0aGUgYXRvbWljIHBvbGFyaXphYmlsaXRpZXMuIA0KDQoqV2llbmVyTnVtYmVyc0Rlc2NyaXB0b3I6KiA8YnIvPg0KVGhpcyBnaXZlcyB0aGUgV2llbmVyIG51bWJlcnMgKHNlZSByZWYxKS4gSXQgcmV0dXJucyB0aGUgV2llbmVyIFBhdGggTnVtYmVyIGFuZCB0aGUgV2llbmVyIFBvbGFyaXR5IE51bWJlci4gDQoNCipNREVEZXNjcmlwdG9yOiogPGJyLz4NClJldHVybnMgdGhlIE1vbGVjdWxhciBEaXN0YW5jZSBFZGdlLiANCg0KKkZyYWdtZW50Q29tcGxleGl0eURlc2NyaXB0b3I6KiA8YnIvPg0KUmV0dXJucyB0aGUgY29tcGxleGl0eSBvZiBhIHN5c3RlbS4gDQoNCmBgYHtyIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KI1JlZm9ybWF0dGluZyBvZiBzbWlsZXMgaW50byBjb3JyZWN0IGZvcm1hdCBmb3IgQ0RLLiBLZWt1bGlzZSBjaGVja3MgZm9yIGVsZWN0cm9ucywgYW5kIHByZXZlbnRzIHBhcnNpbmcgb2YgaW5jb3JyZWN0IHNtaWxlcy4gDQpwYXJzZWRfc21pbGVzIDwtIHBhcnNlLnNtaWxlcyhkYXRhX3dpa2lkYXRhJHNtaWxlcywga2VrdWxpc2U9VFJVRSkNCg0KI0RldGVybWluZSB3aGljaCBkZXNjcmlwdG9ycyBhcmUgb2YgaW50ZXJlc3QuIFRvIGFkZC9yZW1vdmUgZGVzY3JpcHRvcnMsIGNoYW5nZSB0aGlzIHZlY3RvciB0byBpbmNsdWRlIHRoZSBkZXNpcmVkIGRlc2NyaXB0b3JzLiANCmRlc2NyaXB0b3JfbmFtZXMgPC0gYygNCidvcmcub3BlbnNjaWVuY2UuY2RrLnFzYXIuZGVzY3JpcHRvcnMubW9sZWN1bGFyLkFQb2xEZXNjcmlwdG9yJywNCidvcmcub3BlbnNjaWVuY2UuY2RrLnFzYXIuZGVzY3JpcHRvcnMubW9sZWN1bGFyLldpZW5lck51bWJlcnNEZXNjcmlwdG9yJywNCidvcmcub3BlbnNjaWVuY2UuY2RrLnFzYXIuZGVzY3JpcHRvcnMubW9sZWN1bGFyLk1ERURlc2NyaXB0b3InLA0KJ29yZy5vcGVuc2NpZW5jZS5jZGsucXNhci5kZXNjcmlwdG9ycy5tb2xlY3VsYXIuRnJhZ21lbnRDb21wbGV4aXR5RGVzY3JpcHRvcicNCikNCg0KI0dldCBkZXNjcmlwdG9yIHZhbHVlcyBmb3JtIHRoZSBDREsgZGF0YWJhc2UuIA0KZGF0YV9kZXNjcmlwdG9ycyA8LSBldmFsLmRlc2MocGFyc2VkX3NtaWxlcywgZGVzY3JpcHRvcl9uYW1lcykNCg0KI0F0IHRoZSB0aW1lIG9mIHdyaXRpbmcsIHRoaXMgcmV0dXJucyBhIGRhdGFmcmFtZSBvZiAyMyB2YXJpYWJsZXMgZGVzY3JpYmluZyAxMzQgYWxrYW5lcy4gDQoNCmBgYA0KDQoqKjMuIERhdGEgcHJlLXByb2Nlc3NpbmcqKiA8YnIvPg0KQmVmb3JlIHdlIHVzZSB0aGUgZGVzY3JpcHRvcnMgYW5kIGJvaWxpbmcgcG9pbnRzIHRvIHRyYWluIGEgUExTIG1vZGVsLCBzb21lIGRhdGEgcHJlLXByb2Nlc3NpbmcgbmVlZHMgdG8gYmUgZG9uZS4gDQoNCioqM2EpKiogTm90IGFsbCBib2lsaW5nIHBvaW50cyBpbiB0aGUgZGF0YWJhc2UgYXJlIGluIGtlbHZpbiwgd2hpY2ggaXMgdGhlIFNJIHVuaXQgZm9yIHRlbXBlcmF0dXJlLiBCZWZvcmUgSSBjb250aW51ZSwgSSBtYWtlIHN1cmUgYWxsIHRlbXBlcmF0dXJlcyBhcmUgaW4ga2VsdmluLiANCg0KYGBge3IgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQojI0NFTENJVVMgLT4gS0VMVklODQojRmluZCBhbGwgcm93bnVtYmVycyBvZiB0aGUgdmFsdWVzIGluIENlbGNpdXMuIA0KY2VsY2l1cyA8LSAgd2hpY2goZ3JlcGwoJ2RlZ3JlZSBDZWxzaXVzJywgZGF0YV93aWtpZGF0YSRicFVuaXRMYWJlbCkpDQoNCmZvciAoaSBpbiAxOmxlbmd0aChjZWxjaXVzKSkgew0KICAgI0NvbnZlcnQgdGhlIHZhbHVlIHRvIGtlbHZpbi4NCiAgIGRhdGFfd2lraWRhdGEkYnBbY2VsY2l1c1tpXV0gPC0gZGF0YV93aWtpZGF0YSRicFtjZWxjaXVzW2ldXSArIDI3My4xNQ0KICAgI0NvbnZlcnQgdGhlIGxhYmVsIHRvIGtlbHZpbi4gDQogICBkYXRhX3dpa2lkYXRhJGJwVW5pdExhYmVsW2NlbGNpdXNbaV1dIDwtICdrZWx2aW4nDQogIH0NCg0KIyNGQUhSRU5IRUlUIC0+IEtFTFZJTg0KI0ZpbmQgYWxsIHJvd251bWJlcnMgb2YgdGhlIHZhbHVlcyBpbiBGYWhyZW5oZWl0LiANCmZhaHJlbmhlaXQgPC0gd2hpY2goZ3JlcGwoJ2RlZ3JlZSBGYWhyZW5oZWl0JyxkYXRhX3dpa2lkYXRhJGJwVW5pdExhYmVsKSkNCg0KZm9yIChpIGluIDE6bGVuZ3RoKGZhaHJlbmhlaXQpKSB7DQogICAjQ29udmVydCB0aGUgdmFsdWUgdG8ga2VsdmluLg0KICAgZGF0YV93aWtpZGF0YSRicFtmYWhyZW5oZWl0W2ldXSA8LSAoZGF0YV93aWtpZGF0YSRicFtmYWhyZW5oZWl0W2ldXSArIDQ1OS42NykgKiAoNS85KQ0KICAgI0NvbnZlcnQgdGhlIGxhYmVsIHRvIGtlbHZpbi4gDQogICBkYXRhX3dpa2lkYXRhJGJwVW5pdExhYmVsW2ZhaHJlbmhlaXRbaV1dIDwtICdrZWx2aW4nDQogIH0NCg0KYGBgDQoNCioqM2IpKiogTmV4dCwgSSBjb21iaW5lIHRoZSBkZXNjcmlwdG9ycyBhbmQgYm9pbGluZyBwb2ludHMgaW4gb25lIGRhdGFzZXQsIHNvIHdlIGNhbiB1c2UgdGhpcyBkYXRhc2V0IGZvciB0aGUgUExTLiBJIHJlbW92ZSB0aGUgY29sdW1ucyB3aXRoIChhbG1vc3QpIGFsbCB6ZXJvcywgYmVjYXVzZSB0aGVzZSBkZXNjcmlwdG9ycyBkbyBub3QgaGF2ZSBtdWNoIHByZWRpY3RpdmUgdmFsdWUuIA0KDQpgYGB7ciBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCiNGdXNlIHRoZSBib2lsaW5nIHBvaW50cyBmcm9tIGRhdGFfd2lraWRhdGEgYW5kIGRhdGFfZGVzY3JpcHRvcnMgaW50byBvbmUgZGF0YXNldC4gDQpkYXRhX2NvbWJpbmVkIDwtIGRhdGEuZnJhbWUoZGF0YV9kZXNjcmlwdG9ycywgZGF0YV93aWtpZGF0YSRicCkNCg0KI0RlZmluZSB3aGljaCBjb2x1bW5zIHRvIHJlbW92ZS4gVG8gaW5jbHVkZSBvbmUgb2YgdGhlc2UgY29sdW1ucyBhcyBhIHByZWRpY3RvciwgcmVtb3ZlIHRoZSBjb2x1bW4gaGVyZS4gIA0KcmVtb3ZlX2NvbHVtbnMgPC0gYygiTURFQy4xMyIsICJNREVDLjE0IiwgIk1ERUMuMjIiLCAiTURFQy4yMyIsIA0KICAgICAgICAgICAgICAgICAgICAgIk1ERUMuMjQiLCAiTURFQy4zMyIsICJNREVDLjM0IiwgIk1ERUMuNDQiLCANCiAgICAgICAgICAgICAgICAgICAgICJNREVPLjExIiwgIk1ERU8uMTIiLCAiTURFTy4yMiIsICJNREVOLjExIiwgDQogICAgICAgICAgICAgICAgICAgICAiTURFTi4xMiIsICJNREVOLjEzIiwgIk1ERU4uMjIiLCAiTURFTi4yMyIsIA0KICAgICAgICAgICAgICAgICAgICAgIk1ERU4uMzMiDQogICAgICAgICAgICAgICAgICAgICkNCg0KI1JlbW92ZSBhbGwgY2hvc2VuIGNvbHVtbnMgZnJvbSB0aGUgZGF0YXNldC4NCmRhdGFfY29tYmluZWQgPC0gZGF0YV9jb21iaW5lZFsgLCAtd2hpY2gobmFtZXMoZGF0YV9jb21iaW5lZCkgJWluJSByZW1vdmVfY29sdW1ucyldDQoNCiNBdCB0aGUgdGltZSBvZiB3cml0aW5nLCB0aGlzIHJldHVybnMgYSBkYXRhZnJhbWUgb2YgNyB2YXJpYWJsZXMgZGVzY3JpYmluZyAxMzQgYWxrYW5lcy4gDQoNCmBgYA0KDQoqKjNjKSoqIEluIG9yZGVyIHRvIGNoZWNrIHRoZSBxdWFsaXR5IG9mIHRoZSBkYXRhLCBpdCBpcyBuZWNlc3NhcnkgdG8gbWFrZSBhIGZldyBwbG90cy4gRmlyc3QsIHdlIHBsb3QgdGhlIGJvaWxpbmcgcG9pbnRzLCB0byBzZWUgd2hldGhlciBhbnkgb2YgdGhlbSBoYXZlIGEgdmVyeSBhYm5vcm1hbCB2YWx1ZSwgaS5lLiBhcmUgYW4gb3V0bGllci4gDQoNCmBgYHtyIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KI1Bsb3QgdGhlIGJvaWxpbmcgcG9pbnRzIHRvIGNoZWNrIGZvciBvdXRsaWVycy4gDQpwbG90KGRhdGFfY29tYmluZWQkZGF0YV93aWtpZGF0YS5icCwgDQogICAgICBtYWluID0gIkJvaWxpbmcgcG9pbnQgdmFsdWVzIiwgDQogICAgICB5bGFiID0gImJvaWxpbmcgcG9pbnQgKEtlbHZpbikiLCANCiAgICAgIGNleC5tYWluID0gMC44DQogICAgICkNCg0KYGBgDQoNCk5vbmUgb2YgdGhlIHZhbHVlcyBpbiBmaWd1cmUgMSBoYXZlIGEgdmVyeSBhYm5vcm1hbCB2YWx1ZS4gVGh1cywgdGhlcmUgYXJlIG5vIG91dGxpZXJzIHRoYXQgbmVlZCB0byBiZSByZW1vdmVkLiBGdXJ0aGVybW9yZSwgb25lIGNvdWxkIGV4cGVjdCB0aGF0LCBpbiBnZW5lcmFsLCBiaWdnZXIgYWxrYW5lcyBoYXZlIGEgaGlnaGVyIGJvaWxpbmcgcG9pbnQuIFRvIHRlc3Qgd2hldGhlciB0aGlzIHJlbGF0aW9uc2hpcCBpcyBhbHNvIHByZXNlbnQgaW4gb3VyIGRhdGFzZXQgd2UgcGxvdCB0aGUgbGVuZ3RoIG9mIHRoZSBzbWlsZXMgdnMuIHRoZSBib2lsaW5nIHBvaW50cy4gDQoNCmBgYHtyIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KI1Bsb3QgbGVuZ3RoIG9mIHNtaWxlcyB2cy4gYm9pbGluZyBwb2ludHMgdG8gY2hlY2sgZm9yIGEgY29ycmVsYXRpb24NCnBsb3Qoc3RyaV9sZW5ndGgoZGF0YV93aWtpZGF0YSRzbWlsZXMpLCANCiAgICAgIGRhdGFfd2lraWRhdGEkYnAsIA0KICAgICAgbWFpbiA9ICJGaWd1cmUgMjogTGVuZ3RoIG9mIHNtaWxlcyB2cy4gYm9pbGluZyBwb2ludCIsIA0KICAgICAgeGxhYiA9ICJsZW5ndGggb2Ygc21pbGVzIiwgDQogICAgICB5bGFiID0gImJvaWxpbmcgcG9pbnQgKEtlbHZpbikiLCANCiAgICAgIGNleC5tYWluID0gMC44DQogICAgICkNCg0KYGBgDQpBcyBleHBlY3RlZCwgaW4gZmlndXJlIDIgd2UgZG8gaW5kZWVkIHNlZSB0aGF0IHRoZXJlIGlzIGEgdHJlbmQgdG93YXJkcyBoaWdoZXIgYm9pbGluZyBwb2ludHMgZm9yIGJpZ2dlciBzbWlsZXMuIA0KDQoqKjNkKSoqIFRvIGJlIGFibGUgdG8gdGVzdCBob3cgd2VsbCB0aGUgbW9kZWwgSSB0cmFpbiBwZXJmb3JtcywgaXQgaXMgbmVjZXNzYXJ5IHRvIGtlZXAgYSBwYXJ0IG9mIG15IGRhdGEgYXNpZGUgYXMgdGVzdCBzZXQuIFRoZXJlZm9yZSwgSSBzcGxpdCB0aGUgZGF0YSBpbnRvIGEgdHJhaW5pbmcgYW5kIHRlc3Qgc2V0LiBJIHVzZSAyNSUgb2YgbXkgZGF0YXNldCBhcyB0ZXN0IHNldC4NCg0KYGBge3IgaW5jbHVkZT1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQojUGVyY2VudGFnZSBvZiBkYXRhIHRvIGdvIGludG8gdGhlIHRlc3Qtc2V0LiBDaGFuZ2UgdGhpcyB2YWx1ZSB0byBjaGFuZ2UgdGhlIHJlbGF0aXZlIHNpemVzIG9mIHRoZSB0cmFpbmluZyBhbmQgdGVzdCBzZXRzLg0KcGVyY2VudGFnZV90ZXN0IDwtIDAuMjUNCg0KI1NwbGl0IHRoZSBkYXRhIGludG8gYSB0cmFpbmluZyBhbmQgdGVzdCBzZXQuIA0Kc2V0LnNlZWQoODgpIA0Kc2FtcGxlID0gc2FtcGxlLnNwbGl0KGRhdGFfY29tYmluZWQsIFNwbGl0UmF0aW8gPSBwZXJjZW50YWdlX3Rlc3QpDQp0cmFpbiA9IHN1YnNldChkYXRhX2NvbWJpbmVkLCBzYW1wbGUgPT0gRkFMU0UpDQp0ZXN0ICA9IHN1YnNldChkYXRhX2NvbWJpbmVkLCBzYW1wbGUgPT0gVFJVRSkNCg0KI0F0IHRoZSB0aW1lIG9mIHdyaXRpbmcgMTM0IGFsa2FuZXMgYXJlIHNwbGl0IGludG8gYSB0cmFpbmluZyBzZXQgb2YgMTE1IGFsa2FuZXMgYW5kIHRlc3Qgc2V0IG9mIDE5IGFsa2FuZXMuIA0KDQpgYGANCg0KTm90ZTogVGhlIGVycm9yIG9mIGJvdGggdGhlIG51bGwgYW5kIFBMUyBtb2RlbHMgaXMgZGVwZW5kZW50IG9uIHRoZSByYW5kb20gc3BsaXR0aW5nIGluIHRyYWluaW5nIGFuZCB0ZXN0IHNldC4gSWYgeW91IHJ1biB0aGUgY29kZSwgaXQgaXMgZXhwZWN0ZWQgdGhhdCB0aGUgZXJyb3IgZGlmZmVycyBmcm9tIHRoZSBlcnJvciByZXBvcnRlZCBoZXJlLiANCg0KKio0LiBOdWxsIG1vZGVsKiogPGJyLz4NClRoZSBudWxsIG1vZGVsIGlzIHRoZSBtb3N0IHNpbXBsZSBtb2RlbCBwb3NzaWJsZTsgdGhlIG1lYW4gb2YgdGhlIGRhdGEuIFRoaXMgbW9kZWwgY2FuIGJlIHVzZWQgYXMgYSByZWZlcmVuY2UgdG8gc2VlIHdoZXRoZXIgdGhlIFBMUyBtb2RlbCBwZXJmb3JtcyBiZXR0ZXIuIA0KDQpgYGB7ciBpbmNsdWRlPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQoNCiNDcmVhdGUgbnVsbF9tb2RlbC4NCm51bGxfbW9kZWwgPC0gbWVhbih0cmFpbiRkYXRhX3dpa2lkYXRhLmJwKQ0KDQojQ2FsY3VsYXRlIHRoZSBlcnJvciBvZiB0aGUgbnVsbCBtb2RlbC4NCmVycm9yX251bGxfbW9kZWwgPC0gcm1zZSh0ZXN0JGRhdGFfd2lraWRhdGEuYnAsIG51bGxfbW9kZWwpDQoNCiNQcmludCB0aGUgZXJyb3IuDQpwcmludChwYXN0ZTAoIlRoZSBlcnJvciBvZiB0aGUgTnVsbCBtb2RlbCBpcyAiLCBlcnJvcl9udWxsX21vZGVsKSkNCg0KYGBgDQoNCioqNS4gUGFydGlhbCBMZWFzdCBTcXVhcmVzKiogPGJyLz4NCkhlcmUsIEkgdHJhaW4gdGhlIFBMUyBtb2RlbCB3aXRoIHRoZSB0cmFpbmluZyBkYXRhLCBhbmQgdXNlIHRoZSBtb2RlbCB0byBwcmVkaWN0IHRoZSBib2lsaW5nIHBvaW50cyBvZiB0aGUgdGVzdCBkYXRhLiBJZiB5b3UgYXJlIHVuZmFtaWxpYXIgd2l0aCBQTFMsIHRoaXMgaXMgW3ZpZGVvXShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PUF4bXFVS1llRC1VKSBhIGdvb2QgaW50cm9kdWN0aW9uLiBJbiB0aGUgbW9kZWwgSSB1c2UgbGVhdmUtb25lLW91dCAoTE9PKSB2YWxpZGF0aW9uLCBiZWNhdXNlIG15IGRhdGEgc2V0IGlzIHNtYWxsLiANCg0KDQpgYGB7ciBpbmNsdWRlPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCiNUcmFpbmluZyBvZiB0aGUgUExTIG1vZGVsLCB1c2luZyBsZWF2ZS1vbmUtb3V0IHZhbGlkYXRpb24uDQpQTFNfbW9kZWwgPC0gcGxzcihkYXRhX3dpa2lkYXRhLmJwIH4gLiwgDQogICAgICAgICAgICAgICAgICAgZGF0YSA9IHRyYWluLCANCiAgICAgICAgICAgICAgICAgICB2YWxpZGF0aW9uID0gIkxPTyINCiAgICAgICAgICAgICAgICAgICkNCg0KI0NhbGN1bGF0ZSB0aGUgZXJyb3Igb2YgdGhlIFBMUyBtb2RlbC4NCmVycm9yX1BMUyA8LSBSTVNFUChQTFNfbW9kZWwsIG5ld2RhdGEgPSB0ZXN0KQ0KDQojVXNlIHRoZSBtb2RlbCB0byBwcmVkaWN0IHRoZSBib2lsaW5nIHBvaW50cyBvZiB0aGUgdGVzdCBzZXQuDQpwcmVkaWN0ZWRfYnAgPC0gcHJlZGljdChQTFNfbW9kZWwsIHRlc3QpDQoNCmBgYA0KDQpUaGUgcGxzciBmdW5jdGlvbiB0cmFpbnMgc2V2ZXJhbCBtb2RlbHMgd2l0aCBkaWZmZXJlbnQgbnVtYmVycyBvZiBjb21wb25lbnRzLiBBcyBwbHNyIGlzIHVzZWQgaGVyZSwgaXQgZGV0ZXJtaW5lcyB0aGUgbnVtYmVyIG9mIGNvbXBvbmVudHMgYnkgaXRzZWxmIChhbmQgd2lsbCBnZW5lcmFsbHkgdXNlIDEgdXAgdG8gKGRlc2NyaXB0b3JzIC0gMSkgY29tcG9uZW50cykuIEl0IGFsc28gcG9zc2libGUgdG8gZml4IHRoZSBudW1iZXIgb2YgY29tcG9uZW50cyBieSB1c2luZyB0aGUgbmNvbXAgPSB2YWx1ZSBhcmd1bWVudC4gDQoNClJTTUVQIHdpbGwgY2FsY3VsYXRlIHRoZSBlcnJvciBmb3IgZWFjaCBtb2RlbCBidWlsdCBieSBwbHNyIGFuZCB0aGVyZWZvcmUsIGVycm9yX1BMUyB3aWxsIGNvbnRhaW4gYSB2ZWN0b3IgY29udGFpbmluZyB0aGUgZXJyb3IgZm9yIGVhY2ggbnVtYmVyIG9mIGNvbXBvbmVudHMgKHNlZSBzZWN0aW9uIDYgZm9yIHZpc3VhbGl6YXRpb24gb2YgdGhpcykuDQoNClRoZSBmdW5jdGlvbiBwcmVkaWN0IHdpbGwgcHJlZGljdCB0aGUgYm9pbGluZyBwb2ludHMgYmFzZWQgb24gZWFjaCBtb2RlIGluIFBMU19tb2RlbC4gIA0KDQoNCioqNi4gUmVzdWx0cyoqIDxici8+DQoqKjZhKSoqIEluIG9yZGVyIHRvIGlkZW50aWZ5IHRoZSBtb2RlbCB3aXRoIHRoZSBiZXN0IG51bWJlciBvZiBjb21wb25lbnRzLCBpdCBpcyBuZWVkZWQgdG8gcGxvdCB0aGUgZXJyb3IgdnMuIHRoZSBudW1iZXIgb2YgY29tcG9uZW50cy4gDQoNCmBgYHtyIGluY2x1ZGU9VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0KI1Bsb3QgdGhlIGVycm9yIHZzLiB0aGUgbnVtYmVyIG9mIFBMUyBjb21wb25lbnRzDQpwbG90KGVycm9yX1BMUywgDQogICAgICBtYWluID0gIkZpZ3VyZSAzOiBFcnJvciB2cy4gbnVtYmVyIG9mIGNvbXBvbmVudHMiLCANCiAgICAgIHhsYWI9ICJudW1iZXIgb2YgUExTIGNvbXBvbmVudHMiLCANCiAgICAgIHlsYWIgPSAiRXJyb3IgKEtlbHZpbikiLCANCiAgICAgIGNleC5tYWluID0gMC44DQogICAgICkNCg0KYGBgDQoNCipDb25jbHVzaW9uOiogPGJyLz4gDQpGcm9tIGZpZ3VyZSAzIHdlIHNlZSBjYW4gY29uY2x1ZGUgdGhhdCB0aGUgZXJyb3IgZGVjcmVhc2VzIHdpdGggYWRkaW5nIG1vcmUgY29tcG9uZW50cyB1bnRpbGwgd2UgaGF2ZSAzIFBMUyBjb21wb25lbnRzLiBGcm9tIHRoZXJlLCBhZGRpbmcgbW9yZSBjb21wb25lbnRzIGRvZXNuJ3QgZGVjcmVhc2UgdGhlIGVycm9yIGZ1cnRoZXIuIFRoZXJlZm9yZSwgZnJvbSBoZXJlIG9uIEkgdXNlIHRoZSBQTFMgbW9kZWwgd2l0aCAzIGNvbXBvbmVudHMuIFRoZSBlcnJvciBpcyBtdWNoIGxvd2VyIHRoYW4gdGhlIGVycm9yIG9mIHRoZSBudWxsIG1vZGVsLCBpbmRpY2F0aW5nIHRoZSBQTFMgbW9kZWwgcGVyZm9ybXMgYmV0dGVyIHRoYW4gdGhlIG51bGwgbW9kZWwuIA0KDQoqKjZiKSoqIFVzaW5nIHRoaXMgUExTIG1vZGVsIHdpdGggMyBjb21wb25lbnRzLCBJIHZpc3VhbGl6ZSBob3cgd2VsbCBpdCBjYW4gcHJlZGljdCBib2lsaW5nIHBvaW50cyBiYXNlZCBvbiB0aGUgZGVzY3JpcHRvcnMgKGRlc2NyaWJlZCBpbiBzZWN0aW9uIDIpLiBUbyBkbyB0aGlzLCBJIHBsb3QgdGhlIHByZWRpY3RlZCBib2lsaW5nIHBvaW50cyB2cy4gdGhlIGFjdHVhbCBib2lsaW5nIHBvaW50cy4gSWYgdGhlIHByZWRpY3Rpb24gaXMgcGVyZmVjdCwgd2UnZCBleHBlY3QgYWxsIHRoZSBwb2ludHMgdG8gbGluZSB1cCBpbiBhIHBlcmZlY3RseSBsaW5lYXIgY29ycmVsYXRpb24uICANCg0KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCiNQbG90IHRoZSBwcmVkaWN0ZWQgZGF0YSB2cy4gdGhlIG9yaWdpbmFsIGRhdGEuIA0KcGxvdCh0ZXN0JGRhdGFfd2lraWRhdGEuYnAsIA0KICAgICAgcHJlZGljdGVkX2JwWywsM10sIA0KICAgICAgeGxpbT1jKDMwMCwxMDAwKSwgDQogICAgICB5bGltPWMoMzAwLDEwMDApLCANCiAgICAgIG1haW4gPSAiRmlndXJlIDQ6IFByZWRpY3RlZCBib2lsaW5nIHBvaW50cyB2cy4gbWVhc3VyZWQgYm9pbGluZyBwb2ludHMiLCANCiAgICAgIGNleC5tYWluID0gMC44LCANCiAgICAgIHhsYWIgPSAibWVhc3VyZWQgYm9pbGluZyBwb2ludHMgKEtlbHZpbikiLCANCiAgICAgIHlsYWIgPSAicHJlZGljdGVkIGJvaWxpbmcgcG9pbnRzIChLZWx2aW4pIg0KICAgICApDQojQWRkIG51bGwgbW9kZWwgdG8gdGhlIHBsb3QNCmxpbmVzKHRlc3QkZGF0YV93aWtpZGF0YS5icCwgDQogICAgICAgcmVwKG51bGxfbW9kZWwsIA0KICAgICAgICAgICBsZW5ndGgodGVzdCRkYXRhX3dpa2lkYXRhLmJwKQ0KICAgICAgICAgICksIA0KICAgICAgIGNvbCA9ICJyZWQiDQogICAgICApDQojQWRkIHRoZSBwZXJmZWN0IHByZWRpY3Rpb24gdG8gdGhlIHBsb3QNCmxpbmVzKHRlc3QkZGF0YV93aWtpZGF0YS5icCwgDQogICAgICAgdGVzdCRkYXRhX3dpa2lkYXRhLmJwLCANCiAgICAgICBjb2wgPSAiZ3JlZW4iDQogICAgICApDQojQWRkIGxlZ2VuZA0KbGVnZW5kKCJ0b3BsZWZ0IiwgDQogICAgICAgYygiTnVsbCBtb2RlbCIsICJQZXJmZWN0IG1vZGVsIiwgIlBMUyBtb2RlbCIpLCANCiAgICAgICBjb2wgPSBjKCJyZWQiLCAiZ3JlZW4iLCAiYmxhY2siKSwgDQogICAgICAgcGNoID0gYygxNiksIA0KICAgICAgIGJ0eSA9ICduJw0KICAgICAgKQ0KDQpgYGAgDQoNCipDb25jbHVzaW9uOiogPGJyLz4NCkluIGZpZ3VyZSA0LCB3ZSBzZWUgdGhhdCB0aGUgUExTIG1vZGVsIChpbiBibGFjaykgcGVyZm9ybXMgbXVjaCBiZXR0ZXIgaW4gcHJlZGljdGluZyB0aGUgYm9pbGluZyBwb2ludHMgdGhhbiB0aGUgbnVsbCBtb2RlbCAoaW4gcmVkKS4gVGhlIFBMUyBtb2RlbCBjb21lcyBxdWl0ZSBjbG9zZSB0byB0aGUgcGVyZmVjdCBwcmVkaWN0aW9uIG1vZGVsLCBzaG93biBpbiBncmVlbi4gIA0KDQoqKjcuIFJlZmVyZW5jZXMqKiA8YnIvPg0KcmVmMTogV2llbmVyIEguIFN0cnVjdHVyYWwgRGV0ZXJtaW5hdGlvbiBvZiBQYXJhZmZpbiBCb2lsaW5nIFBvaW50cy4gSm91cm5hbCBvZiB0aGUgQW1lcmljYW4gQ2hlbWljYWwgU29jaWV0eS4gMTk0NyBKYW47NjkoMSk6MTctMjAuIDxici8+DQpyZWYyOiBodHRwczovL3d3dy53aWtpZGF0YS5vcmcvd2lraS9XaWtpZGF0YTpNYWluX1BhZ2UgKDEyLTEwLTIwMTkpIDxici8+DQpyZWYzOiBXaWxsaWdoYWdlbiBldCBhbC4gVGhlIENoZW1pc3RyeSBEZXZlbG9wbWVudCBLaXQgKENESykgdjIuMDogYXRvbSB0eXBpbmcsIGRlcGljdGlvbiwgbW9sZWN1bGFyIGZvcm11bGFzLCBhbmQgc3Vic3RydWN0dXJlIHNlYXJjaGluZy4gSi4gQ2hlbWluZm9ybS4gMjAxNzsgOSgzKSwgZG9pOjEwLjExODYvczEzMzIxLTAxNy0wMjIwLTQgPGJyLz4NCnJlZjQ6IE1heSBhbmQgU3RlaW5iZWNrLiBFZmZpY2llbnQgcmluZyBwZXJjZXB0aW9uIGZvciB0aGUgQ2hlbWlzdHJ5IERldmVsb3BtZW50IEtpdC4gSi4gQ2hlbWluZm9ybS4gMjAxNCwgZG9pOjEwLjExODYvMTc1OC0yOTQ2LTYtMyA8YnIvPg0KcmVmNTogU3RlaW5iZWNrIGV0IGFsLiBSZWNlbnQgRGV2ZWxvcG1lbnRzIG9mIHRoZSBDaGVtaXN0cnkgRGV2ZWxvcG1lbnQgS2l0IChDREspIC0gQW4gT3Blbi1Tb3VyY2UgSmF2YSBMaWJyYXJ5IGZvciBDaGVtby0gYW5kIEJpb2luZm9ybWF0aWNzLiBDdXJyLiBQaGFybS4gRGVzLiAyMDA2OyAxMigxNyk6MjExMS0yMTIwLCBkb2k6MTAuMjE3NC8xMzgxNjEyMDY3Nzc1ODUyNzQgPGJyLz4NCnJlZjY6IFN0ZWluYmVjayBldCBhbC4gVGhlIENoZW1pc3RyeSBEZXZlbG9wbWVudCBLaXQgKENESyk6IEFuIE9wZW4tU291cmNlIEphdmEgTGlicmFyeSBmb3IgQ2hlbW8tIGFuZCBCaW9pbmZvcm1hdGljcy4gSi4gQ2hlbS4gSW5mLiBDb21wdXQuIFNjaS4gMjAwMyBNYXItQXByOyA0MygyKTo0OTMtNTAwLCBkb2k6MTAuMTAyMS9jaTAyNTU4NHkgPGJyLz4NCg0KKlBhY2thZ2UgcmVmZXJlbmNlcyogPGJyLz4NCiogW1dpa2lkYXRhUXVlcnlTZXJpdmNlUl0oaHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT1XaWtpZGF0YVF1ZXJ5U2VydmljZVIpIDxici8+DQogIE1pa2hhaWwgUG9wb3YgKDIwMTcpLiBXaWtpZGF0YVF1ZXJ5U2VydmljZVI6IEFQSSBDbGllbnQgTGlicmFyeSBmb3INCiAgJ1dpa2lkYXRhIFF1ZXJ5IFNlcnZpY2UnLiBSIHBhY2thZ2UgdmVyc2lvbiAwLjEuMS4gPGJyLz4NCg0KKltySmF2YV0oaHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT1ySmF2YSkgPGJyLz4NCiAgU2ltb24gVXJiYW5layAoMjAxOSkuIHJKYXZhOiBMb3ctTGV2ZWwgUiB0byBKYXZhIEludGVyZmFjZS4gUiBwYWNrYWdlDQogIHZlcnNpb24gMC45LTExLiAgPGJyLz4NCiAgDQoqW3JjZGtdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9yY2RrL2luZGV4Lmh0bWwpIDxici8+DQogIEd1aGEsIFIuICgyMDA3KS4gJ0NoZW1pY2FsIEluZm9ybWF0aWNzIEZ1bmN0aW9uYWxpdHkgaW4gUicuIEpvdXJuYWwNCiAgb2YgU3RhdGlzdGljYWwgU29mdHdhcmUgNigxOCkgPGJyLz4NCg0KKltzdHJpbmdpXShodHRwOi8vd3d3LmdhZ29sZXdza2kuY29tL3NvZnR3YXJlL3N0cmluZ2kvLikgPGJyLz4NCiAgR2Fnb2xld3NraSBNLiBhbmQgb3RoZXJzICgyMDE5KS4gUiBwYWNrYWdlIHN0cmluZ2k6IENoYXJhY3RlciBzdHJpbmcNCiAgcHJvY2Vzc2luZyBmYWNpbGl0aWVzLg0KDQoqW2NhVG9vbHNdKGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9Y2FUb29scykgPGJyLz4NCiAgSmFyZWsgVHVzenluc2tpICgyMDE5KS4gY2FUb29sczogVG9vbHM6IG1vdmluZyB3aW5kb3cgc3RhdGlzdGljcywNCiAgR0lGLCBCYXNlNjQsIFJPQyBBVUMsIGV0Yy4uIFIgcGFja2FnZSB2ZXJzaW9uIDEuMTcuMS4yIDxici8+DQogIA0KKltwbHNdKGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9cGxzKSA8YnIvPg0KICBCavhybi1IZWxnZSBNZXZpaywgUm9uIFdlaHJlbnMgYW5kIEtyaXN0aWFuIEhvdmRlIExpbGFuZCAoMjAxOSkuIHBsczoNCiAgUGFydGlhbCBMZWFzdCBTcXVhcmVzIGFuZCBQcmluY2lwYWwgQ29tcG9uZW50IFJlZ3Jlc3Npb24uIFIgcGFja2FnZQ0KICB2ZXJzaW9uIDIuNy0xLiAgPGJyLz4NCiAgDQoqW01ldHJpY3NdKGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9TWV0cmljcykgPGJyLz4NCiAgQmVuIEhhbW5lciBhbmQgTWljaGFlbCBGcmFzY28gKDIwMTgpLiBNZXRyaWNzOiBFdmFsdWF0aW9uIE1ldHJpY3MgZm9yDQogIE1hY2hpbmUgTGVhcm5pbmcuIFIgcGFja2FnZSB2ZXJzaW9uIDAuMS40LiA8YnIvPg0K