Introduction
This is our first lab when we are considering 2 dimensions and
instead of calculating univariate statistics by groups (or factors) of
other variable - we will measure their common relationships based on
co-variance and correlation coefficients.
*Please be very careful when choosing the measure of correlation! In
case of different measurument scales we have to recode one of the
variables into weaker scale.
It would be nice to add some additional plots in the background. Feel
free to add your own sections and use external packages.
Data
This time we are going to use a typical credit scoring data with
predefined “default” variables and personal demografic and income data.
Please take a look closer at headers and descriptions of each
variable.
Scatterplots by groups
We can finally see if there any differences between risk status:

We can also see more closely if there any differences between those
two distributions adding their estimated density plots:
We can also put those plots together:

Correlation coefficients - Pearson’s linear correlation
Ok, let’s move to some calculations. In R, we can use the cor()
function. It takes three arguments and the method: cor(x, y, method) For
2 quantitative data, with all assumptions met, we can calculate simple
Pearson’s coefficient of linear correlation:
## [1] 0.574346
Ok, what about the percentage of the explained variability?
## [1] 32.98734
So as we can see almost ??? of total log of incomes’ variability is
explained by differences in age. The rest (???) is probably explained by
other factors.
Partial and semipartial correlation
The partial and semi-partial (also known as part) correlations are
used to express the specific portion of variance explained by
eliminating the effect of other variables when assessing the correlation
between two variables.
Partial correlation holds constant one variable when computing the
relations to others. Suppose we want to know the correlation between X
and Y holding Z constant for both X and Y. That would be the partial
correlation between X and Y controlling for Z.
Semipartial correlation holds Z constant for either X or Y, but not
both, so if we wanted to control X for Z, we could compute the
semipartial correlation between X and Y holding Z constant for X.
Suppose we want to know the correlation between the log of income and
age controlling for years of employment. How highly correlated are these
after controlling for tenure?
**There can be more than one control variable.
## [1] 0.3194263
How can we interpret the obtained partial correlation coefficient?
What is the difference between that one and the semi-partial
coefficient:
## [1] 1.044703
Rank correlation
For 2 different scales - like for example this pair of variables:
income vs. education levels - we cannot use Pearson’s coefficient. The
only possibility is to rank also incomes… and lose some more detailed
information about them.
First, let’s see boxplots of income by education levels.

Now, let’s see Kendal’s coefficient of rank correlation (robust for
ties).
## [1] 0.1577567
Point-biserial correlation
Let’s try to verify if there is a significant relationship between
incomes and risk status. First, let’s take a look at the boxplot:

If you would like to compare 1 quantitative variable (income) and 1
dychotomous variable (default status - binary), then you can use
point-biserial coefficient:
##
## Pearson's product-moment correlation
##
## data: log(bank$income) and bank$default
## t = -3.6057, df = 698, p-value = 0.0003334
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
## -0.20725185 -0.06174165
## sample estimates:
## cor
## -0.1352258
Nonlinear correlation - eta coefficient
If you would like to check if there are any nonlinearities between 2
variables, the only possibility (beside transformations and linear
analysis) is to calculate “eta” coefficient and compare it with the
Pearson’s linear coefficient.
## eta.sq eta.sq.part
## age -22.10569 -22.10569
Correlation matrix
We can also prepare the correlation matrix for all quantitative
variables stored in our data frame.
We can use ggcorr() function:
## Warning in ggcorr(bank): data in column(s) 'def', 'educ' are not numeric and
## were ignored

As you can see - the default correlation matrix is not the best idea
for all measurement scales (including binary variable “default”).
That’s why now we can perform our bivariate analysis with ggpair with
grouping.
Correlation matrix with scatterplots
Here is what we are about to calculate: - The correlation matrix
between age, log_income, employ, address, debtinc, creddebt, and othdebt
variable grouped by whether the person has a default status or not. -
Plot the distribution of each variable by group - Display the scatter
plot with the trend by group
## age employ address income debtinc creddebt othdebt
## age 1.00 0.54 0.60 0.48 0.02 0.30 0.34
## employ 0.54 1.00 0.32 0.62 -0.03 0.40 0.41
## address 0.60 0.32 1.00 0.32 0.01 0.21 0.23
## income 0.48 0.62 0.32 1.00 -0.03 0.57 0.61
## debtinc 0.02 -0.03 0.01 -0.03 1.00 0.50 0.58
## creddebt 0.30 0.40 0.21 0.57 0.50 1.00 0.63
## othdebt 0.34 0.41 0.23 0.61 0.58 0.63 1.00
Qualitative data
In case of two variables measured on nominal or ordinal&nominal
scale - we are forced to organize so called “contingency” table with
frequencies and calculate some kind of the correlation coefficient based
on them. This is so called “contingency analysis”.
Let’s consider one example based on our data: verify, if there is any
significant correlation between education level and credit risk.
table(bank$educ,bank$def) %>%
CramerV()
## [1] 0.1281313
Exercise 1. Contingency analysis.
Do you believe in the Afterlife? https://nationalpost.com/news/canada/millennials-do-you-believe-in-life-after-life
A survey was conducted and a random sample of 1091 questionnaires is
given in the form of the following contingency table:
import numpy as np
import pandas as pd
from scipy.stats import chi2_contingency
data_afterlife = np.array([[435, 147], [375, 134]])
rows = ['Female', 'Male']
columns = ['Yes', 'No']
df_afterlife = pd.DataFrame(data_afterlife, index=rows, columns=columns)
chi2_stat, p_val, _, _ = chi2_contingency(data_afterlife)
print(" ")
print("---EX1---")
## ---EX1---
print("Chi-square statistic:", chi2_stat) #rly small, data is very close to what we'd expect by chance
## Chi-square statistic: 0.1110272160868229
print("p-value:", p_val) #hight, any relationship we see between the variables is just random luck. So, we don't have strong evidence that there's a real connection.
## p-value: 0.7389776820172238
n = np.sum(data_afterlife)
phi_coeff = np.sqrt(chi2_stat / n)
print("Phi coefficient:", phi_coeff) #small,here's hardly any relationship between the variables. It's so small that it's likely not meaningful.
## Phi coefficient: 0.010087936733575699
print("SUMMARY:")
## SUMMARY:
print("Data isn't meaningful enough and does not have high evidence of proving something")
## Data isn't meaningful enough and does not have high evidence of proving something
Exercise 2. Contingency analysis for the ‘Titanic’ data.
Let’s consider the titanic dataset which contains a complete list of
passengers and crew members on the RMS Titanic. It includes a variable
indicating whether a person did survive the sinking of the RMS Titanic
on April 15, 1912. A data frame contains 2456 observations on 14
variables.
import numpy as np
import pandas as pd
from scipy.stats import chi2_contingency
data_titanic=pd.read_csv("titanic.csv",sep=';')
# print(data_titanic.describe())
# print(data_titanic.isnull().sum()) cool functions for general analysis
contingency_table = pd.crosstab(data_titanic['Status'], columns='count')
print(contingency_table)
## col_0 count
## Status
## Survivor 711
## Victim 1496
# Perform chi-square test of independence
chi2, p, _, _ = chi2_contingency(contingency_table['count'].values.reshape(2, 1))
print("\nChi-square test statistic:", chi2)
##
## Chi-square test statistic: 0.0
print("p-value:", p)
## p-value: 1.0
LS0tDQp0aXRsZTogJ0Rlc2NyaXB0aXZlIFN0YXRpc3RpY3MnDQpzdWJ0aXRsZTogJ0JpdmFyaWF0ZSBBbmFseXNpcycNCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCmF1dGhvcjogIllvdXIgbmFtZSBoZXJlIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlDQogICAgZm9udHNpemU6IDEwcHQNCiAgICB0b2M6IHllcw0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogbm8NCiAgICBkZl9wcmludDogZGVmYXVsdA0KICAgIHRvY19kZXB0aDogNQ0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCi0tLQ0KDQpgYGB7ciBzZXR1cCzigILigILigILigILigILigIJtZXNzYWdlID0gRkFMU0Us4oCC4oCCd2FybmluZyA9IEZBTFNFLOKAguKAgmluY2x1ZGUgPSBGQUxTRX0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHJldGljdWxhdGUpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoSFNBVVIzKQ0KbGlicmFyeShoYXZlbikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KbGlicmFyeShwcGNvcikgIyB0aGlzIHBhY2thZ2UgY29tcHV0ZXMgcGFydGlhbCBhbmQgc2VtaXBhcnRpYWwgY29ycmVsYXRpb25zLg0KbGlicmFyeShsdG0pICMgdGhpcyBwYWNrYWdlIGNvbXB1dGVzIHBvaW50LWJpc2VyaWFsIGNvcnJlbGF0aW9ucy4NCmxpYnJhcnkoZGV2dG9vbHMpIA0KI2luc3RhbGxfZ2l0aHViKCJtYXJraGVja21hbm4vcnlvdXJlYWR5IikgIyBwbGVhc2UgaW5zdGFsbCBwYWNrYWdlICJyeW91cmVhZHkiIGZyb20gZ2l0aHViISAodGhlbiAjIGl0KQ0KI2xpYnJhcnkocnlvdXJlYWR5KSAjIHRoaXMgcGFja2FnZSBjb21wdXRlcyBub25saW5lYXIgImV0YSIgY29ycmVsYXRpb25zLg0KbGlicmFyeShHR2FsbHkpICMgdGhpcyBwYWNrYWdlIGNvbXB1dGVzIGNvcnJlbGF0aW9uIG1hdHJpeC4NCmxpYnJhcnkocHN5Y2gpICMgdGhpcyBwYWNrYWdlIGNvbXB1dGVzIHF1YWxpdGF0aXZlIGNvcnJlbGF0aW9ucy4NCmxpYnJhcnkoRGVzY1Rvb2xzKSAjIHRoaXMgcGFja2FnZSBjb21wdXRlcyBxdWFsaXRhdGl2ZSBjb3JyZWxhdGlvbnMuDQpgYGANCg0KDQojIyBJbnRyb2R1Y3Rpb24NCg0KVGhpcyBpcyBvdXIgZmlyc3QgbGFiIHdoZW4gd2UgYXJlIGNvbnNpZGVyaW5nIDIgZGltZW5zaW9ucyBhbmQgaW5zdGVhZCBvZiBjYWxjdWxhdGluZyB1bml2YXJpYXRlIHN0YXRpc3RpY3MgYnkgZ3JvdXBzIChvciBmYWN0b3JzKSBvZiBvdGhlciB2YXJpYWJsZSAtIHdlIHdpbGwgbWVhc3VyZSB0aGVpciBjb21tb24gcmVsYXRpb25zaGlwcyBiYXNlZCBvbiBjby12YXJpYW5jZSBhbmQgY29ycmVsYXRpb24gY29lZmZpY2llbnRzLiANCg0KKlBsZWFzZSBiZSB2ZXJ5IGNhcmVmdWwgd2hlbiBjaG9vc2luZyB0aGUgbWVhc3VyZSBvZiBjb3JyZWxhdGlvbiEgSW4gY2FzZSBvZiBkaWZmZXJlbnQgbWVhc3VydW1lbnQgc2NhbGVzIHdlIGhhdmUgdG8gcmVjb2RlIG9uZSBvZiB0aGUgdmFyaWFibGVzIGludG8gd2Vha2VyIHNjYWxlLg0KDQpJdCB3b3VsZCBiZSBuaWNlIHRvIGFkZCBzb21lIGFkZGl0aW9uYWwgcGxvdHMgaW4gdGhlIGJhY2tncm91bmQuIEZlZWwgZnJlZSB0byBhZGQgeW91ciBvd24gc2VjdGlvbnMgYW5kIHVzZSBleHRlcm5hbCBwYWNrYWdlcy4NCg0KIyMgRGF0YQ0KDQpUaGlzIHRpbWUgd2UgYXJlIGdvaW5nIHRvIHVzZSBhIHR5cGljYWwgY3JlZGl0IHNjb3JpbmcgZGF0YSB3aXRoIHByZWRlZmluZWQgImRlZmF1bHQiIHZhcmlhYmxlcyBhbmQgcGVyc29uYWwgZGVtb2dyYWZpYyBhbmQgaW5jb21lIGRhdGEuIFBsZWFzZSB0YWtlIGEgbG9vayBjbG9zZXIgYXQgaGVhZGVycyBhbmQgZGVzY3JpcHRpb25zIG9mIGVhY2ggdmFyaWFibGUuDQoNCmBgYHtyIGxvYWQtZGF0YSwgd2FybmluZz1UUlVFLCBpbmNsdWRlPUZBTFNFfQ0KZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9naXRodWIuY29tL2tmbGlzaWtvd3NraS9kcy9ibG9iL21hc3Rlci9iYW5rX2RlZmF1bHRzLnNhdj9yYXc9dHJ1ZSIsIGRlc3RmaWxlID0iYmFua19kZWZhdWx0cy5zYXYiLG1vZGU9IndiIikNCmJhbmtfZGVmYXVsdHMgPC0gcmVhZF9zYXYoImJhbmtfZGVmYXVsdHMuc2F2IikNCmJhbms8LW5hLm9taXQoYmFua19kZWZhdWx0cykNCmJhbmskZGVmPC1hcy5mYWN0b3IoYmFuayRkZWZhdWx0KQ0KYmFuayRlZHVjPC1hcy5mYWN0b3IoYmFuayRlZCkNCmBgYA0KDQojIyBTY2F0dGVycGxvdHMNCg0KRmlyc3QgbGV0J3MgdmlzdWFsaXplIG91ciBxdWFudGl0YXRpdmUgcmVsYXRpb25zaGlwcyB1c2luZyBzY2F0dGVycGxvdHMuIA0KDQpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPVRSVUV9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCiMgU2NhdHRlcnBsb3Qgb2YgQWdlIHZzIEluY29tZQ0KZ2dwbG90KGJhbmssIGFlcyh4ID0gYWdlLCB5ID0gaW5jb21lKSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gIiMwMDAwRkYiKSArDQogIGxhYnModGl0bGUgPSAiU2NhdHRlcnBsb3Qgb2YgQWdlIHZzIEluY29tZSIsIHggPSAiQWdlIiwgeSA9ICJJbmNvbWUgKGluIHRob3VzYW5kcykiKQ0KDQojIFNjYXR0ZXJwbG90IG9mIEluY29tZSB2cyBDcmVkaXQgQ2FyZCBEZWJ0DQpnZ3Bsb3QoYmFuaywgYWVzKHggPSBpbmNvbWUsIHkgPSBjcmVkZGVidCkpICsNCiAgZ2VvbV9wb2ludChjb2xvciA9ICJyZWQiKSArDQogIGxhYnModGl0bGUgPSAiU2NhdHRlcnBsb3Qgb2YgSW5jb21lIHZzIENyZWRpdCBDYXJkIERlYnQiLCB4ID0gIkluY29tZSAoaW4gdGhvdXNhbmRzKSIsIHkgPSAiQ3JlZGl0IENhcmQgRGVidCAoaW4gdGhvdXNhbmRzKSIpDQoNCiMgU2NhdHRlcnBsb3Qgb2YgQWdlIHZzIFllYXJzIHdpdGggQ3VycmVudCBFbXBsb3llcg0KZ2dwbG90KGJhbmssIGFlcyh4ID0gYWdlLCB5ID0gZW1wbG95KSkgKw0KICBnZW9tX3BvaW50KGNvbG9yID0gIiMwMDgwODAiKSArDQogIGxhYnModGl0bGUgPSAiU2NhdHRlcnBsb3Qgb2YgQWdlIHZzIFllYXJzIHdpdGggQ3VycmVudCBFbXBsb3llciIsIHggPSAiQWdlIiwgeSA9ICJZZWFycyB3aXRoIEN1cnJlbnQgRW1wbG95ZXIiKQ0KDQojIFNjYXR0ZXJwbG90IG9mIEFnZSB2cyBEZWJ0LXRvLUluY29tZSBSYXRpbyB3aXRoIGN1c3RvbWl6ZWQgcG9pbnRzDQpnZ3Bsb3QoYmFuaywgYWVzKHggPSBhZ2UsIHkgPSBkZWJ0aW5jKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzLCBzaGFwZSA9IDMsIGNvbG9yID0gIiNGRkE1MDAiKSArICMgQ2hhbmdlIHNpemUsIHNoYXBlLCBhbmQgY29sb3INCiAgbGFicyh0aXRsZSA9ICJTY2F0dGVycGxvdCBvZiBBZ2UgdnMgRGVidC10by1JbmNvbWUgUmF0aW8iLCB4ID0gIkFnZSIsIHkgPSAiRGVidC10by1JbmNvbWUgUmF0aW8iKQ0KDQojIFNjYXR0ZXJwbG90IG9mIEluY29tZSB2cyBPdGhlciBEZWJ0IHdpdGggY3VzdG9taXplZCBwb2ludHMNCmdncGxvdChiYW5rLCBhZXMoeCA9IGluY29tZSwgeSA9IG90aGRlYnQpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDEsIHNoYXBlID0gNSwgY29sb3IgPSAicHVycGxlIikgKyAjIENoYW5nZSBzaXplLCBzaGFwZSwgYW5kIGNvbG9yDQogIGxhYnModGl0bGUgPSAiU2NhdHRlcnBsb3Qgb2YgSW5jb21lIHZzIE90aGVyIERlYnQiLCB4ID0gIkluY29tZSIsIHkgPSAiT3RoZXIgRGVidCIpDQoNCiMgU2NhdHRlcnBsb3Qgb2YgWWVhcnMgd2l0aCBDdXJyZW50IEVtcGxveWVyIHZzIENyZWRpdCBDYXJkIERlYnQgd2l0aCBjdXN0b21pemVkIHBvaW50cw0KZ2dwbG90KGJhbmssIGFlcyh4ID0gZW1wbG95LCB5ID0gY3JlZGRlYnQpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDEuNCwgc2hhcGUgPSAxNSwgY29sb3IgPSAicGluayIpICsgIyBDaGFuZ2Ugc2l6ZSwgc2hhcGUsIGFuZCBjb2xvcg0KICBsYWJzKHRpdGxlID0gIlNjYXR0ZXJwbG90IG9mIFllYXJzIHdpdGggQ3VycmVudCBFbXBsb3llciB2cyBDcmVkaXQgQ2FyZCBEZWJ0IiwgeCA9ICJZZWFycyB3aXRoIEN1cnJlbnQgRW1wbG95ZXIiLCB5ID0gIkNyZWRpdCBDYXJkIERlYnQiKQ0KDQpgYGANCg0KWW91IGNhbiBhbHNvIG5vcm1hbGl6ZSB0aGUgc2tld2VkIGRpc3RyaWJ1dGlvbiBvZiBpbmNvbWVzIHVzaW5nIGxvZzoNCg0KYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1UUlVFfQ0KIyBCYXNpYyBzY2F0dGVyIHBsb3Qgd2l0aCB0aGUgbG9nIG9mIGluY29tZQ0KYmFuayRsb2dfaW5jb21lIDwtIGxvZyhiYW5rJGluY29tZSkNCmdncGxvdChiYW5rLCBhZXMoeCA9IGxvZyhpbmNvbWUpLCB5ID0gY3JlZGRlYnQpKSArDQpnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiKSArDQp0aGVtZV9taW5pbWFsKCkgKw0KbGFicyh0aXRsZSA9ICJDcmVkaXQgQ2FyZCBEZWJ0IHZzIExvZyBvZiBJbmNvbWUiLA0KeCA9ICJMb2cgb2YgSW5jb21lIiwNCnkgPSAiQ3JlZGl0IENhcmQgRGVidCIpDQoNCiNBZ2UgdnMgTG9nLUluY29tZSBmb3Igbm9ybWFsaXplZCBza2V3ZWQgZGlzdC4NCmdncGxvdChiYW5rLCBhZXMoeCA9IGFnZSwgeSA9IGxvZ19pbmNvbWUpKSArDQpnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiKSArDQpsYWJzKHRpdGxlID0gIlNjYXR0ZXJwbG90IG9mIEFnZSB2cyBMb2ctSW5jb21lIiwgeCA9ICJBZ2UiLCB5ID0gIkxvZyBvZiBJbmNvbWUiKQ0KDQpnZ3Bsb3QoYmFuaywgYWVzKHggPSBhZ2UsIHkgPSBsb2dfaW5jb21lKSkgKw0KZ2VvbV9wb2ludChjb2xvciA9ICJibHVlIikgKw0KZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3IgPSAicmVkIikgKw0KbGFicyh0aXRsZSA9ICJTY2F0dGVycGxvdCBvZiBBZ2UgdnMgTG9nIEluY29tZSB3aXRoIFJlZ3Jlc3Npb24gTGluZSIsIHggPSAiQWdlIiwgeSA9ICJMb2cgb2YgSW5jb21lIikNCg0KZ2dwbG90KGJhbmssIGFlcyh4ID0gbG9nX2luY29tZSwgeSA9IGNyZWRkZWJ0KSkgKw0KZ2VvbV9wb2ludChjb2xvciA9ICJyZWQiKSArDQpnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvciA9ICJibHVlIikgKw0KbGFicyh0aXRsZSA9ICJTY2F0dGVycGxvdCBvZiBMb2cgSW5jb21lIHZzIENyZWRpdCBDYXJkIERlYnQgd2l0aCBSZWdyZXNzaW9uIExpbmUiLCB4ID0gIkxvZyBvZiBJbmNvbWUiLCB5ID0gIkNyZWRpdCBDYXJkIERlYnQiKQ0KDQpgYGANCg0KIyMgU2NhdHRlcnBsb3RzIGJ5IGdyb3VwcyANCg0KV2UgY2FuIGZpbmFsbHkgc2VlIGlmIHRoZXJlIGFueSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHJpc2sgc3RhdHVzOg0KDQpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPVRSVUV9DQpiYW5rMSA8LSBiYW5rICU+JQ0KbXV0YXRlKGRlZmF1bHQgPSBhcy5mYWN0b3IoZGVmYXVsdCksDQpkZWYgPSBhcy5mYWN0b3IoZGVmKSwNCmVkdWMgPSBhcy5mYWN0b3IoZWR1YykpDQoNCmdncGxvdChiYW5rMSwgYWVzKHggPSBsb2coaW5jb21lKSwgeSA9IGNyZWRkZWJ0LCBjb2xvciA9IGRlZmF1bHQpKSArDQpnZW9tX3BvaW50KCkgKw0KdGhlbWVfbWluaW1hbCgpICsNCmxhYnModGl0bGUgPSAiQ3JlZGl0IENhcmQgRGVidCB2cyBMb2cgb2YgSW5jb21lIGJ5IERlZmF1bHQiLA0KeCA9ICJMb2cgb2YgSW5jb21lIiwNCnkgPSAiQ3JlZGl0IENhcmQgRGVidCIsDQpjb2xvciA9ICJEZWZhdWx0IikNCg0KDQoNCg0KYGBgDQoNCldlIGNhbiBhbHNvIHNlZSBtb3JlIGNsb3NlbHkgaWYgdGhlcmUgYW55IGRpZmZlcmVuY2VzIGJldHdlZW4gdGhvc2UgdHdvIGRpc3RyaWJ1dGlvbnMgYWRkaW5nIHRoZWlyIGVzdGltYXRlZCBkZW5zaXR5IHBsb3RzOg0KDQpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPVRSVUV9DQojIHNjYXR0ZXIgcGxvdCBvZiB4IGFuZCB5IHZhcmlhYmxlcw0KYmFuazEkZGVmYXVsdCA8LSBhcy5udW1lcmljKGJhbmskZGVmYXVsdCkNCmJhbmsxJGRlZiA8LSBhcy5udW1lcmljKGJhbmskZGVmKQ0KYmFuazEkZWR1YyA8LSBhcy5udW1lcmljKGJhbmskZWR1YykNCiMgY29sb3VyIGJ5IGdyb3Vwcw0Kc2NhdHRlcl9wbG90IDwtIGdncGxvdChiYW5rMSwgYWVzKHggPSBsb2coaW5jb21lKSwgeSA9IGNyZWRkZWJ0LCBjb2xvciA9IGRlZmF1bHQpKSArDQpnZW9tX3BvaW50KGFscGhhID0gMC42KSArDQp0aGVtZV9taW5pbWFsKCkgKw0KbGFicyh0aXRsZSA9ICJDcmVkaXQgQ2FyZCBEZWJ0IHZzIExvZyBvZiBJbmNvbWUgYnkgRGVmYXVsdCBTdGF0dXMiLA0KeCA9ICJMb2cgb2YgSW5jb21lIiwNCnkgPSAiQ3JlZGl0IENhcmQgRGVidCAoaW4gdGhvdXNhbmRzKSIsDQpjb2xvciA9ICJEZWZhdWx0IFN0YXR1cyIpICsNCnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KDQoNCiMgTWFyZ2luYWwgZGVuc2l0eSBwbG90IG9mIGFnZSAodG9wIHBhbmVsKQ0KbWFyZ2luYWxfZGVuc2l0eV9hZ2UgPC0gZ2dwbG90KGJhbmsxLCBhZXMoeCA9IGxvZyhpbmNvbWUpKSkgKw0KZ2VvbV9kZW5zaXR5KCkgKw0KdGhlbWVfbWluaW1hbCgpICsNCmxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIExvZyBvZiBJbmNvbWUiLA0KeSA9ICJEZW5zaXR5IikgKw0KdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLA0KYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksDQpheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCkpDQoNCg0KIyBNYXJnaW5hbCBkZW5zaXR5IHBsb3Qgb2YgeSAocmlnaHQgcGFuZWwpDQoNCmBgYA0KDQpXZSBjYW4gYWxzbyBwdXQgdGhvc2UgcGxvdHMgdG9nZXRoZXI6DQoNCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmc9VFJVRX0NCg0KbWFyZ2luYWxfZGVuc2l0eV9jcmVkZGVidCA8LSBnZ3Bsb3QoYmFuazEsIGFlcyh4ID0gY3JlZGRlYnQpKSArDQpnZW9tX2RlbnNpdHkoKSArDQp0aGVtZV9taW5pbWFsKCkgKw0KY29vcmRfZmxpcCgpICsNCmxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIENyZWRpdCBDYXJkIERlYnQiLA0KeCA9ICJEZW5zaXR5IiwNCnkgPSBOVUxMKSArDQp0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksDQpheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwNCmF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSkNCg0KI1dlIGNhbiBhbHNvIHB1dCB0aG9zZSBwbG90cyB0b2dldGhlcjoNCmdyaWQuYXJyYW5nZShzY2F0dGVyX3Bsb3QgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpLA0KTlVMTCwgIyBQbGFjZWhvbGRlciBmb3IgdG9wIG1hcmdpbg0KbWFyZ2luYWxfZGVuc2l0eV9hZ2UgKyB0aGVtZShwbG90Lm1hcmdpbiA9IG1hcmdpbigwLCA1LCAwLCA1KSksDQptYXJnaW5hbF9kZW5zaXR5X2NyZWRkZWJ0ICsgdGhlbWUocGxvdC5tYXJnaW4gPSBtYXJnaW4oNSwgMCwgNSwgMCkpLA0KbmNvbCA9IDIsDQpoZWlnaHRzID0gYyg1LCAxKSkNCg0KDQpgYGANCg0KIyMgQ29ycmVsYXRpb24gY29lZmZpY2llbnRzIC0gUGVhcnNvbidzIGxpbmVhciBjb3JyZWxhdGlvbg0KDQpPaywgbGV0J3MgbW92ZSB0byBzb21lIGNhbGN1bGF0aW9ucy4NCkluIFIsIHdlIGNhbiB1c2UgdGhlIGNvcigpIGZ1bmN0aW9uLiBJdCB0YWtlcyB0aHJlZSBhcmd1bWVudHMgYW5kIHRoZSBtZXRob2Q6IGNvcih4LCB5LCBtZXRob2QpDQpGb3IgMiBxdWFudGl0YXRpdmUgZGF0YSwgd2l0aCBhbGwgYXNzdW1wdGlvbnMgbWV0LCB3ZSBjYW4gY2FsY3VsYXRlIHNpbXBsZSBQZWFyc29uJ3MgY29lZmZpY2llbnQgb2YgbGluZWFyIGNvcnJlbGF0aW9uOg0KDQpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPVRSVUV9DQpyX2FnZTwtY29yKGxvZyhiYW5rJGluY29tZSksYmFuayRhZ2UsIG1ldGhvZD0icGVhcnNvbiIpDQpyX2FnZQ0KDQoNCmBgYA0KDQpPaywgd2hhdCBhYm91dCB0aGUgcGVyY2VudGFnZSBvZiB0aGUgZXhwbGFpbmVkIHZhcmlhYmlsaXR5Pw0KDQpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPVRSVUV9DQpyc3FfYWdlPC1yX2FnZV4yKjEwMA0KcnNxX2FnZQ0KDQoNCmBgYA0KU28gYXMgd2UgY2FuIHNlZSBhbG1vc3QgPz8/IG9mIHRvdGFsIGxvZyBvZiBpbmNvbWVzJyB2YXJpYWJpbGl0eSBpcyBleHBsYWluZWQgYnkgZGlmZmVyZW5jZXMgaW4gYWdlLiBUaGUgcmVzdCAoPz8/KSBpcyBwcm9iYWJseSBleHBsYWluZWQgYnkgb3RoZXIgZmFjdG9ycy4NCg0KIyMgUGFydGlhbCBhbmQgc2VtaXBhcnRpYWwgY29ycmVsYXRpb24gDQoNClRoZSBwYXJ0aWFsIGFuZCBzZW1pLXBhcnRpYWwgKGFsc28ga25vd24gYXMgcGFydCkgY29ycmVsYXRpb25zIGFyZSB1c2VkIHRvIGV4cHJlc3MgdGhlIHNwZWNpZmljIHBvcnRpb24gb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGVsaW1pbmF0aW5nIHRoZSBlZmZlY3Qgb2Ygb3RoZXIgdmFyaWFibGVzIHdoZW4gYXNzZXNzaW5nIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHR3byB2YXJpYWJsZXMuDQoNClBhcnRpYWwgY29ycmVsYXRpb24gaG9sZHMgY29uc3RhbnQgb25lIHZhcmlhYmxlIHdoZW4gY29tcHV0aW5nIHRoZSByZWxhdGlvbnMgdG8gb3RoZXJzLiBTdXBwb3NlIHdlIHdhbnQgdG8ga25vdyB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBYIGFuZCBZIGhvbGRpbmcgWiBjb25zdGFudCBmb3IgYm90aCBYIGFuZCBZLiBUaGF0IHdvdWxkIGJlIHRoZSBwYXJ0aWFsIGNvcnJlbGF0aW9uIGJldHdlZW4gWCBhbmQgWSBjb250cm9sbGluZyBmb3IgWi4gDQoNClNlbWlwYXJ0aWFsIGNvcnJlbGF0aW9uIGhvbGRzIFogY29uc3RhbnQgZm9yIGVpdGhlciBYIG9yIFksIGJ1dCBub3QgYm90aCwgc28gaWYgd2Ugd2FudGVkIHRvIGNvbnRyb2wgWCBmb3IgWiwgd2UgY291bGQgY29tcHV0ZSB0aGUgc2VtaXBhcnRpYWwgY29ycmVsYXRpb24gYmV0d2VlbiBYIGFuZCBZIGhvbGRpbmcgWiBjb25zdGFudCBmb3IgWC4NCg0KU3VwcG9zZSB3ZSB3YW50IHRvIGtub3cgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGxvZyBvZiBpbmNvbWUgYW5kIGFnZSBjb250cm9sbGluZyBmb3IgeWVhcnMgb2YgZW1wbG95bWVudC4gSG93IGhpZ2hseSBjb3JyZWxhdGVkIGFyZSB0aGVzZSBhZnRlciBjb250cm9sbGluZyBmb3IgdGVudXJlPyANCg0KKipUaGVyZSBjYW4gYmUgbW9yZSB0aGFuIG9uZSBjb250cm9sIHZhcmlhYmxlLg0KDQpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KcnBfYWdlPC1wY29yLnRlc3QobG9nKGJhbmskaW5jb21lKSwgYmFuayRhZ2UsIGJhbmskZW1wbG95KSRlc3RpbWF0ZQ0KICANCnJwX2FnZQ0KDQoNCmBgYA0KDQpIb3cgY2FuIHdlIGludGVycHJldCB0aGUgb2J0YWluZWQgcGFydGlhbCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudD8gV2hhdCBpcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoYXQgb25lIGFuZCB0aGUgc2VtaS1wYXJ0aWFsIGNvZWZmaWNpZW50Og0KDQpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQoocnNxX2FnZS1ycF9hZ2UqMTAwKQ0KDQpgYGANCg0KIyMgUmFuayBjb3JyZWxhdGlvbiANCg0KRm9yIDIgZGlmZmVyZW50IHNjYWxlcyAtIGxpa2UgZm9yIGV4YW1wbGUgdGhpcyBwYWlyIG9mIHZhcmlhYmxlczogaW5jb21lIHZzLiBlZHVjYXRpb24gbGV2ZWxzIC0gd2UgY2Fubm90IHVzZSBQZWFyc29uJ3MgY29lZmZpY2llbnQuIFRoZSBvbmx5IHBvc3NpYmlsaXR5IGlzIHRvIHJhbmsgYWxzbyBpbmNvbWVzLi4uIGFuZCBsb3NlIHNvbWUgbW9yZSBkZXRhaWxlZCBpbmZvcm1hdGlvbiBhYm91dCB0aGVtLiANCg0KRmlyc3QsIGxldCdzIHNlZSBib3hwbG90cyBvZiBpbmNvbWUgYnkgZWR1Y2F0aW9uIGxldmVscy4NCg0KYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1UUlVFfQ0KDQpiYW5rICU+JSANCiAgZ2dwbG90KGFlcyhlZCwgbG9nKGluY29tZSkpKSsNCiAgZ2VvbV9ib3hwbG90KGFlcyhncm91cD1lZHVjKSkrDQogIHRoZW1lX2J3KCkrDQogIGxhYnMoeT0ibG9nIG9mIEluY29tZSIsDQogICAgICAgeD0iRWR1Y2F0aW9uIGxldmVsIikNCg0KDQpgYGANCg0KTm93LCBsZXQncyBzZWUgS2VuZGFsJ3MgY29lZmZpY2llbnQgb2YgcmFuayBjb3JyZWxhdGlvbiAocm9idXN0IGZvciB0aWVzKS4NCg0KYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1UUlVFfQ0KY29yKGxvZyhiYW5rJGluY29tZSksIGJhbmskZWQsIG1ldGhvZD0ia2VuZGFsbCIpDQoNCg0KYGBgDQoNCg0KIyMgUG9pbnQtYmlzZXJpYWwgY29ycmVsYXRpb24NCg0KTGV0J3MgdHJ5IHRvIHZlcmlmeSBpZiB0aGVyZSBpcyBhIHNpZ25pZmljYW50IHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGluY29tZXMgYW5kIHJpc2sgc3RhdHVzLiBGaXJzdCwgbGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGJveHBsb3Q6DQoNCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmc9VFJVRX0NCg0KYmFuayAlPiUgDQogIGdncGxvdChhZXMoeT1sb2coaW5jb21lKSkpKw0KICBnZW9tX2JveHBsb3QoKSsNCiAgdGhlbWVfYncoKSsNCiAgZmFjZXRfZ3JpZCh+ZGVmKSsNCiAgbGFicyh5PSJsb2cgb2YgSW5jb21lIikNCg0KDQpgYGANCg0KSWYgeW91IHdvdWxkIGxpa2UgdG8gY29tcGFyZSAxIHF1YW50aXRhdGl2ZSB2YXJpYWJsZSAoaW5jb21lKSBhbmQgMSBkeWNob3RvbW91cyB2YXJpYWJsZSAoZGVmYXVsdCBzdGF0dXMgLSBiaW5hcnkpLCB0aGVuIHlvdSBjYW4gdXNlIHBvaW50LWJpc2VyaWFsIGNvZWZmaWNpZW50Og0KDQpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY29yLnRlc3QobG9nKGJhbmskaW5jb21lKSxiYW5rJGRlZmF1bHQpDQoNCmBgYA0KDQoNCiMjIE5vbmxpbmVhciBjb3JyZWxhdGlvbiAtIGV0YSBjb2VmZmljaWVudA0KDQpJZiB5b3Ugd291bGQgbGlrZSB0byBjaGVjayBpZiB0aGVyZSBhcmUgYW55IG5vbmxpbmVhcml0aWVzIGJldHdlZW4gMiB2YXJpYWJsZXMsIHRoZSBvbmx5IHBvc3NpYmlsaXR5IChiZXNpZGUgdHJhbnNmb3JtYXRpb25zIGFuZCBsaW5lYXIgYW5hbHlzaXMpIGlzIHRvIGNhbGN1bGF0ZSAiZXRhIiBjb2VmZmljaWVudCBhbmQgY29tcGFyZSBpdCB3aXRoIHRoZSBQZWFyc29uJ3MgbGluZWFyIGNvZWZmaWNpZW50LiANCg0KYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmxpYnJhcnkobHNyKQ0KZXRhX2FnZTwtYmFuayAlPiUgDQogIGFvdihsb2coYmFuayRpbmNvbWUpIH4gYWdlLCBkYXRhID0gLikgJT4lIA0KICBldGFTcXVhcmVkKCkNCmV0YV9hZ2VeMioxMDAtcnNxX2FnZQ0KDQoNCmBgYA0KDQojIyBDb3JyZWxhdGlvbiBtYXRyaXgNCg0KV2UgY2FuIGFsc28gcHJlcGFyZSB0aGUgY29ycmVsYXRpb24gbWF0cml4IGZvciBhbGwgcXVhbnRpdGF0aXZlIHZhcmlhYmxlcyBzdG9yZWQgaW4gb3VyIGRhdGEgZnJhbWUuIA0KDQpXZSBjYW4gdXNlIGdnY29ycigpIGZ1bmN0aW9uOg0KDQpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPVRSVUV9DQpnZ2NvcnIoYmFuaykNCg0KYGBgDQogIA0KQXMgeW91IGNhbiBzZWUgLSB0aGUgZGVmYXVsdCBjb3JyZWxhdGlvbiBtYXRyaXggaXMgbm90IHRoZSBiZXN0IGlkZWEgZm9yIGFsbCBtZWFzdXJlbWVudCBzY2FsZXMgKGluY2x1ZGluZyBiaW5hcnkgdmFyaWFibGUgImRlZmF1bHQiKS4gDQoNClRoYXQncyB3aHkgbm93IHdlIGNhbiBwZXJmb3JtIG91ciBiaXZhcmlhdGUgYW5hbHlzaXMgd2l0aCBnZ3BhaXIgd2l0aCBncm91cGluZy4NCg0KIyMgQ29ycmVsYXRpb24gbWF0cml4IHdpdGggc2NhdHRlcnBsb3RzIA0KDQpIZXJlIGlzIHdoYXQgd2UgYXJlIGFib3V0IHRvIGNhbGN1bGF0ZToNCi0gVGhlIGNvcnJlbGF0aW9uIG1hdHJpeCBiZXR3ZWVuIGFnZSwgbG9nX2luY29tZSwgZW1wbG95LCBhZGRyZXNzLCBkZWJ0aW5jLCBjcmVkZGVidCwgYW5kIG90aGRlYnQgdmFyaWFibGUgZ3JvdXBlZCBieSB3aGV0aGVyIHRoZSBwZXJzb24gaGFzIGEgZGVmYXVsdCBzdGF0dXMgb3Igbm90Lg0KLSBQbG90IHRoZSBkaXN0cmlidXRpb24gb2YgZWFjaCB2YXJpYWJsZSBieSBncm91cA0KLSBEaXNwbGF5IHRoZSBzY2F0dGVyIHBsb3Qgd2l0aCB0aGUgdHJlbmQgYnkgZ3JvdXANCg0KYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1UUlVFfQ0KDQoNCmJhbmtbLCBjKDEsMyw0LDUsNiw3LDgpXSAlPiUgDQogIGNvcigpICU+JSANCiAgcm91bmQoMikNCg0KDQpgYGANCg0KDQojIyBRdWFsaXRhdGl2ZSBkYXRhDQoNCkluIGNhc2Ugb2YgdHdvIHZhcmlhYmxlcyBtZWFzdXJlZCBvbiBub21pbmFsIG9yIG9yZGluYWwmbm9taW5hbCBzY2FsZSAtIHdlIGFyZSBmb3JjZWQgdG8gb3JnYW5pemUgc28gY2FsbGVkICJjb250aW5nZW5jeSIgdGFibGUgd2l0aCBmcmVxdWVuY2llcyBhbmQgY2FsY3VsYXRlIHNvbWUga2luZCBvZiB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQgYmFzZWQgb24gdGhlbS4gVGhpcyBpcyBzbyBjYWxsZWQgImNvbnRpbmdlbmN5IGFuYWx5c2lzIi4gDQoNCkxldCdzIGNvbnNpZGVyIG9uZSBleGFtcGxlIGJhc2VkIG9uIG91ciBkYXRhOiB2ZXJpZnksIGlmIHRoZXJlIGlzIGFueSBzaWduaWZpY2FudCBjb3JyZWxhdGlvbiBiZXR3ZWVuIGVkdWNhdGlvbiBsZXZlbCBhbmQgY3JlZGl0IHJpc2suDQoNCmBgYHtyfQ0KdGFibGUoYmFuayRlZHVjLGJhbmskZGVmKSAlPiUgDQogIENyYW1lclYoKQ0KDQpgYGANCg0KDQojIyBFeGVyY2lzZSAxLiBDb250aW5nZW5jeSBhbmFseXNpcy4NCg0KRG8geW91IGJlbGlldmUgaW4gdGhlIEFmdGVybGlmZT8NCmh0dHBzOi8vbmF0aW9uYWxwb3N0LmNvbS9uZXdzL2NhbmFkYS9taWxsZW5uaWFscy1kby15b3UtYmVsaWV2ZS1pbi1saWZlLWFmdGVyLWxpZmUNCkEgc3VydmV5IHdhcyBjb25kdWN0ZWQgYW5kIGEgcmFuZG9tIHNhbXBsZSBvZiAxMDkxIHF1ZXN0aW9ubmFpcmVzIGlzIGdpdmVuIGluIHRoZSBmb3JtIG9mIHRoZSBmb2xsb3dpbmcgY29udGluZ2VuY3kgdGFibGU6DQpgYGB7cHl0aG9ufQ0KaW1wb3J0IG51bXB5IGFzIG5wDQppbXBvcnQgcGFuZGFzIGFzIHBkDQpmcm9tIHNjaXB5LnN0YXRzIGltcG9ydCBjaGkyX2NvbnRpbmdlbmN5DQpkYXRhX2FmdGVybGlmZSA9IG5wLmFycmF5KFtbNDM1LCAxNDddLCBbMzc1LCAxMzRdXSkNCnJvd3MgPSBbJ0ZlbWFsZScsICdNYWxlJ10NCmNvbHVtbnMgPSBbJ1llcycsICdObyddDQpkZl9hZnRlcmxpZmUgPSBwZC5EYXRhRnJhbWUoZGF0YV9hZnRlcmxpZmUsIGluZGV4PXJvd3MsIGNvbHVtbnM9Y29sdW1ucykNCg0KY2hpMl9zdGF0LCBwX3ZhbCwgXywgXyA9IGNoaTJfY29udGluZ2VuY3koZGF0YV9hZnRlcmxpZmUpDQpwcmludCgiICIpDQpwcmludCgiLS0tRVgxLS0tIikNCnByaW50KCJDaGktc3F1YXJlIHN0YXRpc3RpYzoiLCBjaGkyX3N0YXQpICNybHkgc21hbGwsICBkYXRhIGlzIHZlcnkgY2xvc2UgdG8gd2hhdCB3ZSdkIGV4cGVjdCBieSBjaGFuY2UNCnByaW50KCJwLXZhbHVlOiIsIHBfdmFsKSAjaGlnaHQsIGFueSByZWxhdGlvbnNoaXAgd2Ugc2VlIGJldHdlZW4gdGhlIHZhcmlhYmxlcyBpcyBqdXN0IHJhbmRvbSBsdWNrLiBTbywgd2UgZG9uJ3QgaGF2ZSBzdHJvbmcgZXZpZGVuY2UgdGhhdCB0aGVyZSdzIGEgcmVhbCBjb25uZWN0aW9uLg0KDQpuID0gbnAuc3VtKGRhdGFfYWZ0ZXJsaWZlKQ0KcGhpX2NvZWZmID0gbnAuc3FydChjaGkyX3N0YXQgLyBuKQ0KcHJpbnQoIlBoaSBjb2VmZmljaWVudDoiLCBwaGlfY29lZmYpICNzbWFsbCxoZXJlJ3MgaGFyZGx5IGFueSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdmFyaWFibGVzLiBJdCdzIHNvIHNtYWxsIHRoYXQgaXQncyBsaWtlbHkgbm90IG1lYW5pbmdmdWwuDQpwcmludCgiU1VNTUFSWToiKQ0KcHJpbnQoIkRhdGEgaXNuJ3QgbWVhbmluZ2Z1bCBlbm91Z2ggYW5kIGRvZXMgbm90IGhhdmUgaGlnaCBldmlkZW5jZSBvZiBwcm92aW5nIHNvbWV0aGluZyIpDQpgYGANCg0KDQojIyBFeGVyY2lzZSAyLiBDb250aW5nZW5jeSBhbmFseXNpcyBmb3IgdGhlICdUaXRhbmljJyBkYXRhLg0KDQpMZXQncyBjb25zaWRlciB0aGUgdGl0YW5pYyBkYXRhc2V0IHdoaWNoIGNvbnRhaW5zIGEgY29tcGxldGUgbGlzdCBvZiBwYXNzZW5nZXJzIGFuZCBjcmV3IG1lbWJlcnMgb24gdGhlIFJNUyBUaXRhbmljLiBJdCBpbmNsdWRlcyBhIHZhcmlhYmxlIGluZGljYXRpbmcgd2hldGhlciBhIHBlcnNvbiBkaWQgc3Vydml2ZSB0aGUgc2lua2luZyBvZiB0aGUgUk1TIFRpdGFuaWMgb24gQXByaWwgMTUsIDE5MTIuDQpBIGRhdGEgZnJhbWUgY29udGFpbnMgMjQ1NiBvYnNlcnZhdGlvbnMgb24gMTQgdmFyaWFibGVzLg0KDQpgYGB7ciBsb2FkLWRhdGEyLCB3YXJuaW5nPVRSVUUsIGluY2x1ZGU9RkFMU0V9DQpkb3dubG9hZC5maWxlKCJodHRwczovL2dpdGh1Yi5jb20va2ZsaXNpa293c2tpL2RzL2Jsb2IvbWFzdGVyL3RpdGFuaWMuY3N2P3Jhdz10cnVlIiwgZGVzdGZpbGUgPSJ0aXRhbmljLmNzdiIsbW9kZT0id2IiKQ0KYGBgDQpgYGB7cHl0aG9ufQ0KaW1wb3J0IG51bXB5IGFzIG5wDQppbXBvcnQgcGFuZGFzIGFzIHBkDQpmcm9tIHNjaXB5LnN0YXRzIGltcG9ydCBjaGkyX2NvbnRpbmdlbmN5DQpkYXRhX3RpdGFuaWM9cGQucmVhZF9jc3YoInRpdGFuaWMuY3N2IixzZXA9JzsnKQ0KIyBwcmludChkYXRhX3RpdGFuaWMuZGVzY3JpYmUoKSkNCiMgcHJpbnQoZGF0YV90aXRhbmljLmlzbnVsbCgpLnN1bSgpKSBjb29sIGZ1bmN0aW9ucyBmb3IgZ2VuZXJhbCBhbmFseXNpcw0KY29udGluZ2VuY3lfdGFibGUgPSBwZC5jcm9zc3RhYihkYXRhX3RpdGFuaWNbJ1N0YXR1cyddLCBjb2x1bW5zPSdjb3VudCcpDQpwcmludChjb250aW5nZW5jeV90YWJsZSkNCiMgUGVyZm9ybSBjaGktc3F1YXJlIHRlc3Qgb2YgaW5kZXBlbmRlbmNlDQpjaGkyLCBwLCBfLCBfID0gY2hpMl9jb250aW5nZW5jeShjb250aW5nZW5jeV90YWJsZVsnY291bnQnXS52YWx1ZXMucmVzaGFwZSgyLCAxKSkNCnByaW50KCJcbkNoaS1zcXVhcmUgdGVzdCBzdGF0aXN0aWM6IiwgY2hpMikNCnByaW50KCJwLXZhbHVlOiIsIHApDQpgYGANCg==