Earlier, I did a blog on histogram estimators and how they asymptotically approximate a probability density. Histogram estimators look like step functions and are discontinuous which imply convergene to a smooth density may take some time compared to an probability density estimator that is already smooth. Kernel density estimators are smoother than histogram estimators, so let’s study those!
Definitions
Let \(X_1, X_2, ... X_n \sim f\) be our sample. Let a kernel \(K\) be a smooth function that satisfies the following:
- \(K(x)>0\)
- \(\int K(x) dx = 1\)
- \(\int x K(x) dx = 0\), i.e. \(E(X)=0\)
- \(\sigma^2_k = \int x^2 K(x) dx >0\)
A kernel density estimator (KDE) \(\hat{f}\) takes in a kernel \(K\) and positive valued bandwidth \(h\) and is defined by
\[
\hat{f}(x) = \frac{1}{n} \sum_{i=1}^{n} \frac{1}{h} K(\frac{x-X_i}{h}).
\]
The bandwidth \(h\) controls how smooth the estimator is.
- When \(h \to 0\), the estimator becomes spiky/wiggly.
- When \(h \to \infty\), the estimator becomes smoother/more uniform.
What we see here is that we need to specify a kernel \(K\) and a bandwidth \(h\). There are plenty of different kernels one could use, but of the popular ones are
- Epanechnikov, which fits quite closely to the original data’s histogram shape
- Normal, which follows normal distributions across your data
- Uniform, which is similar to normal but with uniform distributions
- Triangular, which is tracing your data with triangles
All of these shapes are affected by your choice of \(h\). A larger bandwidth will allow more data points to be modeled by the kernel. A smaller one will only let points that are closeby to give weight, leading to KDE’s that fit quite tightly to the observed data. The choice of \(K\) to estimate a density is proven to be not as crucial as the choice of \(h\).
Estimating a Density
We’re going to start using a dataset on Ames, Iowa housing prices. Let’s see how these houses look on a histogram and boxplot.
What’s obvious from these two plots is that we have a clear center near the median of the distribution, but a whole bunch of outliers.
By Theorem
The best theoretical kernel density estimator will minimize error. The MISE for a KDE is found to be
\[
R(f, \hat{f}_n) \approx \frac{1}{4}\sigma^4_Kh^4 \int (f''(x))^2 + \frac{\int K^2(x) dx}{nh}
\]
where \(\sigma^2_K=\int x^2 K(x) dx\). The bandwidth \(h\) that minimizes \(R(f, \hat{f}_n)\) is
\[
h^* = \frac{c_1^{-2/5}c_2^{1/5}c_3^{-1/5}}{n^{1/5}}
\]
where
- \(c_1 = \int x^2K(x) dx\),
- \(c_2 = \int K(x)^2 dx\),
- and \(c_3 = \int (f''(x))^2 dx\).
When \(h^*\) is the bandwidth, \(R(f, \hat{f}_n) \approx \frac{c_4}{n^{4/5}}\) for some constant \(c_4\) greater than 0. Kernel estimators converge at a rate of \(n^{-4/5}\) which is faster than histogram estimators’ convergence rate of \(n^{-2/3}\). While all this is awesome, of course in practice, we don’t know what the true \(f(x)\) is. In fact, we’re just en route to estimating \(f(x)\).
By Estimation
We can estimate risk with
\[
\hat{J}(h) = \int \hat{f}^2(x) dz - \frac{2}{n} \sum_{i=1}^{n} \hat{f}_{(-i)}(X_i).
\]
We have that \(\forall \, h>0, E[\hat{J}(h)]=E[J(h)]\). An alternative way to calculate risk is with
\[
\hat{J}(h) \approx \frac{1}{hn^2} \sum_{i} \sum_{j} K^* \big( \frac{X_i-X_j}{h} \big) + \frac{2}{nh}K(0)
\]
where \(K^*(x)=K^{(2)}(x)-2K(x)\) and \(K^(2)(z)=\int K(z-y)K(y) dy\). Of course now we will want to choose some \(h\) that minimizes \(\hat{J}(h)\). Stone’s Theorem (20.16 Theorem from Wasserman’s book) explains why minimizing this MISE risk estimate will give us our optimal bandwidth. The theorem says that if \(f\) is bounded, then
\[
\frac{\int \big(f(x)-\hat{f}_{h_n}(x)\big)^2}{\text{inf}_h \int \big(f(x)-\hat{f}_{h}(x)\big)^2} \overset{P}\to 1.
\]
That is, the smallest theoretical bandwidth \(h\) and the cross-validated bandwidth \(h_n\) will perform asymptotically well in place of each other.
Back to Ames, Iowa
Our next goal is to estimate the density of Ames, Iowa home prices from 2006-2010 using kernel density estimation. We will be using the package ks
as recommended by Deng and Wickham.
x <- ames %>% pull(SalePrice)
bandwidth <- hpi(x, binned=TRUE)
The scalar bandwidth selected by hpi
is 1.053918610^{4} chosen by a plug-in estimator from Wand and Jones (1994). There is a lot of research in the area of bandwidth selection. Because I am no expert, I’m going to keep this default plug-in estimator as my bandwidth with the belief that it will minimize error in a founded, theoretical way that builds off of the intuition we looked at earlier.
fhat_pos <- ks::kde(x=x, adj.positive=0, positive=TRUE)
kde_df <- data.frame(cbind(fhat=fhat_pos$estimate, eval_x=fhat_pos$eval.points))
To perform our KDE fit, I set adj.positive=0, positive=TRUE
in the kde()
function because, unfortunately for us, there are no negative house prices. (There ain’t no such thing as a free house!)
kde_plot <- ggplot(kde_df, aes(x=eval_x)) +
geom_line(aes(x=eval_x, y=fhat), col="skyblue3") +
xlab("sale price") +
ylab("approximated density") +
ggtitle("Kernel Density Estimator") +
theme_classic()
original <- ggplot(ames, aes(x=SalePrice)) +
geom_histogram(binwidth=15000, col="skyblue3", fill="skyblue") +
xlab("sale price") +
ylab("true count") +
ggtitle("Original Data") +
theme_classic()
grid.arrange(original, kde_plot, ncol=2)
Plotted side by side are the original histogram and the kernel density estimator. The estimator looks pretty darn good! Pretty amazing! This KDE is way better than its histogram estimator, and there’s no kidding in this would converge a lot faster to the truth than a histogram.
Also, though ks
is already used by many, I’d like to take the time to praise how thorough and customizable your KDE experience is while using this R
library. It’s good for beginners (like me) and more advanced users who know exactly what they want from their density estimate.
Questions for later
I don’t know what \(y\) and \(z\) are in the risk estimation formula.
References
Conlin, M. Kernel Density Estimation [URL].
Deng, H. Wickham, H. (2011). Density Estimation in R. [PDF].
Duong, T. (2007). ks: Kernel density estimation and kernel discriminant analysis for multivariate data in R. Journal of Statistical Software, 21(7), 1-16. [PDF].
Wand, M.P. & Jones, M.C. (1994) Multivariate plug-in bandwidth selection. Computational Statistics, 9, 97-116.
Wasserman, L. (2013). All of Statistics. Springer Science & Business Media. [Text].
Data Source
The Ames Housing Dataset. Info is available here and download is available through Kaggle.
LS0tCnRpdGxlOiAnS2VybmVsIERlbnNpdHkgRXN0aW1hdG9ycycKZGF0ZTogOCBNYXkgMjAxOQphdXRob3I6IEVkaWUgRXNwZWpvCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQogICAgdG9jX2RlcHRoOiAzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKLS0tCgpgYGB7ciBsaWJyYXJpZXMsIGluY2x1ZGU9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5ciwgcXVpZXRseT1UUlVFKQpsaWJyYXJ5KGtzKQpsaWJyYXJ5KGdyaWRFeHRyYSkKYGBgCgpFYXJsaWVyLCBJIGRpZCBhIGJsb2cgb24gaGlzdG9ncmFtIGVzdGltYXRvcnMgYW5kIGhvdyB0aGV5IGFzeW1wdG90aWNhbGx5IGFwcHJveGltYXRlIGEgcHJvYmFiaWxpdHkgZGVuc2l0eS4gSGlzdG9ncmFtIGVzdGltYXRvcnMgbG9vayBsaWtlIHN0ZXAgZnVuY3Rpb25zIGFuZCBhcmUgZGlzY29udGludW91cyB3aGljaCBpbXBseSBjb252ZXJnZW5lIHRvIGEgc21vb3RoIGRlbnNpdHkgbWF5IHRha2Ugc29tZSB0aW1lIGNvbXBhcmVkIHRvIGFuIHByb2JhYmlsaXR5IGRlbnNpdHkgZXN0aW1hdG9yIHRoYXQgaXMgYWxyZWFkeSBzbW9vdGguIEtlcm5lbCBkZW5zaXR5IGVzdGltYXRvcnMgYXJlIHNtb290aGVyIHRoYW4gaGlzdG9ncmFtIGVzdGltYXRvcnMsIHNvIGxldCdzIHN0dWR5IHRob3NlIQoKIyBEZWZpbml0aW9ucwpMZXQgJFhfMSwgWF8yLCAuLi4gWF9uIFxzaW0gZiQgYmUgb3VyIHNhbXBsZS4gTGV0IGEgKiprZXJuZWwqKiAkSyQgYmUgYSBzbW9vdGggZnVuY3Rpb24gdGhhdCBzYXRpc2ZpZXMgdGhlIGZvbGxvd2luZzoKCi0gJEsoeCk+MCQgIAotICRcaW50IEsoeCkgZHggPSAxJCAgCi0gJFxpbnQgeCBLKHgpIGR4ID0gMCQsIGkuZS4gJEUoWCk9MCQgIAotICRcc2lnbWFeMl9rID0gXGludCB4XjIgSyh4KSBkeCA+MCQgIAoKQSAqKmtlcm5lbCBkZW5zaXR5IGVzdGltYXRvciAoS0RFKSoqICRcaGF0e2Z9JCB0YWtlcyBpbiBhIGtlcm5lbCAkSyQgYW5kIHBvc2l0aXZlIHZhbHVlZCBiYW5kd2lkdGggJGgkIGFuZCBpcyBkZWZpbmVkIGJ5ICAKCiQkClxoYXR7Zn0oeCkgPSBcZnJhY3sxfXtufSBcc3VtX3tpPTF9XntufSBcZnJhY3sxfXtofSBLKFxmcmFje3gtWF9pfXtofSkuCiQkCgpUaGUgKipiYW5kd2lkdGgqKiAkaCQgY29udHJvbHMgaG93IHNtb290aCB0aGUgZXN0aW1hdG9yIGlzLiAgCgotIFdoZW4gJGggXHRvIDAkLCB0aGUgZXN0aW1hdG9yIGJlY29tZXMgc3Bpa3kvd2lnZ2x5LiAgCi0gV2hlbiAkaCBcdG8gXGluZnR5JCwgdGhlIGVzdGltYXRvciBiZWNvbWVzIHNtb290aGVyL21vcmUgdW5pZm9ybS4gCgpXaGF0IHdlIHNlZSBoZXJlIGlzIHRoYXQgd2UgbmVlZCB0byBzcGVjaWZ5IGEga2VybmVsICRLJCBhbmQgYSBiYW5kd2lkdGggJGgkLiBUaGVyZSBhcmUgcGxlbnR5IG9mIGRpZmZlcmVudCBrZXJuZWxzIG9uZSBjb3VsZCB1c2UsIGJ1dCBvZiB0aGUgcG9wdWxhciBvbmVzIGFyZSAgCgotIEVwYW5lY2huaWtvdiwgd2hpY2ggZml0cyBxdWl0ZSBjbG9zZWx5IHRvIHRoZSBvcmlnaW5hbCBkYXRhJ3MgaGlzdG9ncmFtIHNoYXBlICAKLSBOb3JtYWwsIHdoaWNoIGZvbGxvd3Mgbm9ybWFsIGRpc3RyaWJ1dGlvbnMgYWNyb3NzIHlvdXIgZGF0YSAgCi0gVW5pZm9ybSwgd2hpY2ggaXMgc2ltaWxhciB0byBub3JtYWwgYnV0IHdpdGggdW5pZm9ybSBkaXN0cmlidXRpb25zICAKLSBUcmlhbmd1bGFyLCB3aGljaCBpcyB0cmFjaW5nIHlvdXIgZGF0YSB3aXRoIHRyaWFuZ2xlcyAgCgpBbGwgb2YgdGhlc2Ugc2hhcGVzIGFyZSBhZmZlY3RlZCBieSB5b3VyIGNob2ljZSBvZiAkaCQuIEEgbGFyZ2VyIGJhbmR3aWR0aCB3aWxsIGFsbG93IG1vcmUgZGF0YSBwb2ludHMgdG8gYmUgbW9kZWxlZCBieSB0aGUga2VybmVsLiBBIHNtYWxsZXIgb25lIHdpbGwgb25seSBsZXQgcG9pbnRzIHRoYXQgYXJlIGNsb3NlYnkgdG8gZ2l2ZSB3ZWlnaHQsIGxlYWRpbmcgdG8gS0RFJ3MgdGhhdCBmaXQgcXVpdGUgdGlnaHRseSB0byB0aGUgb2JzZXJ2ZWQgZGF0YS4gVGhlIGNob2ljZSBvZiAkSyQgdG8gZXN0aW1hdGUgYSBkZW5zaXR5IGlzIHByb3ZlbiB0byBiZSBub3QgYXMgY3J1Y2lhbCBhcyB0aGUgY2hvaWNlIG9mICRoJC4KCiMgRXN0aW1hdGluZyBhIERlbnNpdHkKV2UncmUgZ29pbmcgdG8gc3RhcnQgdXNpbmcgYSBkYXRhc2V0IG9uIEFtZXMsIElvd2EgaG91c2luZyBwcmljZXMuIExldCdzIHNlZSBob3cgdGhlc2UgaG91c2VzIGxvb2sgb24gYSBoaXN0b2dyYW0gYW5kIGJveHBsb3QuCgpgYGB7ciwgZWNobz1GQUxTRX0KYW1lcyA8LSByZWFkX2NzdigiLi4vLi4vZGF0YS9ob3VzZS1wcmljZXMtdHJhaW4uY3N2IiwgY29sX25hbWVzPVRSVUUpCmdncGxvdChhbWVzLCBhZXMoeD1TYWxlUHJpY2UpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTE1MDAwLCBjb2w9InNreWJsdWUzIiwgZmlsbD0ic2t5Ymx1ZSIpICsKICB4bGFiKCJzYWxlIHByaWNlIikgKwogIGdndGl0bGUoIkhvdXNlIFByaWNlcyBpbiBBbWVzLCBJb3dhICgyMDA2LTIwMTApIikgKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpnZ3Bsb3QoYW1lcywgYWVzKHk9U2FsZVByaWNlKSkgKyAKICBnZW9tX2JveHBsb3QoY29sPSJza3libHVlMyIsIGZpbGw9InNreWJsdWUiKSArCiAgeWxhYigic2FsZSBwcmljZSIpICsKICBnZ3RpdGxlKCJIb3VzZSBQcmljZXMgaW4gQW1lcywgSW93YSAoMjAwNi0yMDEwKSIpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKGF4aXMudGl0bGUueD1lbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpCmBgYAoKV2hhdCdzIG9idmlvdXMgZnJvbSB0aGVzZSB0d28gcGxvdHMgaXMgdGhhdCB3ZSBoYXZlIGEgY2xlYXIgY2VudGVyIG5lYXIgdGhlIG1lZGlhbiBvZiB0aGUgZGlzdHJpYnV0aW9uLCBidXQgYSB3aG9sZSBidW5jaCBvZiBvdXRsaWVycy4KCiMjIEJ5IFRoZW9yZW0KVGhlIGJlc3QgdGhlb3JldGljYWwga2VybmVsIGRlbnNpdHkgZXN0aW1hdG9yIHdpbGwgbWluaW1pemUgZXJyb3IuIFRoZSBNSVNFIGZvciBhIEtERSBpcyBmb3VuZCB0byBiZSAKCiQkClIoZiwgXGhhdHtmfV9uKSBcYXBwcm94IFxmcmFjezF9ezR9XHNpZ21hXjRfS2heNCBcaW50IChmJycoeCkpXjIgKyBcZnJhY3tcaW50IEteMih4KSBkeH17bmh9CiQkCgp3aGVyZSAkXHNpZ21hXjJfSz1caW50IHheMiBLKHgpIGR4JC4gVGhlIGJhbmR3aWR0aCAkaCQgdGhhdCBtaW5pbWl6ZXMgJFIoZiwgXGhhdHtmfV9uKSQgaXMgCgokJApoXiogPSBcZnJhY3tjXzFeey0yLzV9Y18yXnsxLzV9Y18zXnstMS81fX17bl57MS81fX0KJCQKCndoZXJlCgotICRjXzEgPSBcaW50IHheMksoeCkgZHgkLCAgCi0gJGNfMiA9IFxpbnQgSyh4KV4yIGR4JCwgIAotIGFuZCAkY18zID0gXGludCAoZicnKHgpKV4yIGR4JC4gIAoKV2hlbiAkaF4qJCBpcyB0aGUgYmFuZHdpZHRoLCAkUihmLCBcaGF0e2Z9X24pIFxhcHByb3ggXGZyYWN7Y180fXtuXns0LzV9fSQgZm9yIHNvbWUgY29uc3RhbnQgJGNfNCQgZ3JlYXRlciB0aGFuIDAuIEtlcm5lbCBlc3RpbWF0b3JzIGNvbnZlcmdlIGF0IGEgcmF0ZSBvZiAkbl57LTQvNX0kIHdoaWNoIGlzIGZhc3RlciB0aGFuIGhpc3RvZ3JhbSBlc3RpbWF0b3JzJyBjb252ZXJnZW5jZSByYXRlIG9mICRuXnstMi8zfSQuIFdoaWxlIGFsbCB0aGlzIGlzIGF3ZXNvbWUsIG9mIGNvdXJzZSBpbiBwcmFjdGljZSwgd2UgZG9uJ3Qga25vdyB3aGF0IHRoZSB0cnVlICRmKHgpJCBpcy4gSW4gZmFjdCwgd2UncmUganVzdCBlbiByb3V0ZSB0byBlc3RpbWF0aW5nICRmKHgpJC4KCiMjIEJ5IEVzdGltYXRpb24KV2UgY2FuIGVzdGltYXRlIHJpc2sgd2l0aAoKJCQKXGhhdHtKfShoKSA9IFxpbnQgXGhhdHtmfV4yKHgpIGR6IC0gXGZyYWN7Mn17bn0gXHN1bV97aT0xfV57bn0gXGhhdHtmfV97KC1pKX0oWF9pKS4KJCQKCldlIGhhdmUgdGhhdCAkXGZvcmFsbCBcLCBoPjAsIEVbXGhhdHtKfShoKV09RVtKKGgpXSQuIEFuIGFsdGVybmF0aXZlIHdheSB0byBjYWxjdWxhdGUgcmlzayBpcyB3aXRoCgokJApcaGF0e0p9KGgpIFxhcHByb3ggXGZyYWN7MX17aG5eMn0gXHN1bV97aX0gXHN1bV97an0gS14qIFxiaWcoIFxmcmFje1hfaS1YX2p9e2h9IFxiaWcpICsgXGZyYWN7Mn17bmh9SygwKQokJAoKd2hlcmUgJEteKih4KT1LXnsoMil9KHgpLTJLKHgpJCBhbmQgJEteKDIpKHopPVxpbnQgSyh6LXkpSyh5KSBkeSQuIE9mIGNvdXJzZSBub3cgd2Ugd2lsbCB3YW50IHRvIGNob29zZSBzb21lICRoJCB0aGF0IG1pbmltaXplcyAkXGhhdHtKfShoKSQuICoqU3RvbmUncyBUaGVvcmVtKiogKCoyMC4xNiBUaGVvcmVtIGZyb20gV2Fzc2VybWFuJ3MgYm9vayopIGV4cGxhaW5zIHdoeSBtaW5pbWl6aW5nIHRoaXMgTUlTRSByaXNrIGVzdGltYXRlIHdpbGwgZ2l2ZSB1cyBvdXIgb3B0aW1hbCBiYW5kd2lkdGguIFRoZSB0aGVvcmVtIHNheXMgdGhhdCBpZiAkZiQgaXMgYm91bmRlZCwgdGhlbiAKCiQkClxmcmFje1xpbnQgXGJpZyhmKHgpLVxoYXR7Zn1fe2hfbn0oeClcYmlnKV4yfXtcdGV4dHtpbmZ9X2ggXGludCBcYmlnKGYoeCktXGhhdHtmfV97aH0oeClcYmlnKV4yfSBcb3ZlcnNldHtQfVx0byAxLgokJAoKVGhhdCBpcywgdGhlIHNtYWxsZXN0IHRoZW9yZXRpY2FsIGJhbmR3aWR0aCAkaCQgYW5kIHRoZSBjcm9zcy12YWxpZGF0ZWQgYmFuZHdpZHRoICRoX24kIHdpbGwgcGVyZm9ybSBhc3ltcHRvdGljYWxseSB3ZWxsIGluIHBsYWNlIG9mIGVhY2ggb3RoZXIuCgojIyBCYWNrIHRvIEFtZXMsIElvd2EKT3VyIG5leHQgZ29hbCBpcyB0byBlc3RpbWF0ZSB0aGUgZGVuc2l0eSBvZiBBbWVzLCBJb3dhIGhvbWUgcHJpY2VzIGZyb20gMjAwNi0yMDEwIHVzaW5nIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRpb24uIFdlIHdpbGwgYmUgdXNpbmcgdGhlIHBhY2thZ2UgYGtzYCBhcyByZWNvbW1lbmRlZCBieSBEZW5nIGFuZCBXaWNraGFtLgoKYGBge3J9CnggICAgICA8LSBhbWVzICU+JSBwdWxsKFNhbGVQcmljZSkKYmFuZHdpZHRoIDwtIGhwaSh4LCBiaW5uZWQ9VFJVRSkKYGBgCgpUaGUgc2NhbGFyIGJhbmR3aWR0aCBzZWxlY3RlZCBieSBgaHBpYCBpcyBgciBiYW5kd2lkdGhgIGNob3NlbiBieSBhIHBsdWctaW4gZXN0aW1hdG9yIGZyb20gV2FuZCBhbmQgSm9uZXMgKDE5OTQpLiBUaGVyZSBpcyBhIGxvdCBvZiByZXNlYXJjaCBpbiB0aGUgYXJlYSBvZiBiYW5kd2lkdGggc2VsZWN0aW9uLiBCZWNhdXNlIEkgYW0gbm8gZXhwZXJ0LCBJJ20gZ29pbmcgdG8ga2VlcCB0aGlzIGRlZmF1bHQgcGx1Zy1pbiBlc3RpbWF0b3IgYXMgbXkgYmFuZHdpZHRoIHdpdGggdGhlIGJlbGllZiB0aGF0IGl0IHdpbGwgbWluaW1pemUgZXJyb3IgaW4gYSBmb3VuZGVkLCB0aGVvcmV0aWNhbCB3YXkgdGhhdCBidWlsZHMgb2ZmIG9mIHRoZSBpbnR1aXRpb24gd2UgbG9va2VkIGF0IGVhcmxpZXIuCgpgYGB7cn0KZmhhdF9wb3MgPC0ga3M6OmtkZSh4PXgsIGFkai5wb3NpdGl2ZT0wLCBwb3NpdGl2ZT1UUlVFKQprZGVfZGYgPC0gZGF0YS5mcmFtZShjYmluZChmaGF0PWZoYXRfcG9zJGVzdGltYXRlLCBldmFsX3g9ZmhhdF9wb3MkZXZhbC5wb2ludHMpKQpgYGAKClRvIHBlcmZvcm0gb3VyIEtERSBmaXQsIEkgc2V0IGBhZGoucG9zaXRpdmU9MCwgcG9zaXRpdmU9VFJVRWAgaW4gdGhlIGBrZGUoKWAgZnVuY3Rpb24gYmVjYXVzZSwgdW5mb3J0dW5hdGVseSBmb3IgdXMsIHRoZXJlIGFyZSBubyBuZWdhdGl2ZSBob3VzZSBwcmljZXMuICgqVGhlcmUgYWluJ3Qgbm8gc3VjaCB0aGluZyBhcyBhIGZyZWUgaG91c2UhKikKCmBgYHtyfQprZGVfcGxvdCA8LSBnZ3Bsb3Qoa2RlX2RmLCBhZXMoeD1ldmFsX3gpKSArCiAgZ2VvbV9saW5lKGFlcyh4PWV2YWxfeCwgeT1maGF0KSwgY29sPSJza3libHVlMyIpICsKICB4bGFiKCJzYWxlIHByaWNlIikgKwogIHlsYWIoImFwcHJveGltYXRlZCBkZW5zaXR5IikgKwogIGdndGl0bGUoIktlcm5lbCBEZW5zaXR5IEVzdGltYXRvciIpICsKICB0aGVtZV9jbGFzc2ljKCkKCm9yaWdpbmFsIDwtIGdncGxvdChhbWVzLCBhZXMoeD1TYWxlUHJpY2UpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTE1MDAwLCBjb2w9InNreWJsdWUzIiwgZmlsbD0ic2t5Ymx1ZSIpICsKICB4bGFiKCJzYWxlIHByaWNlIikgKwogIHlsYWIoInRydWUgY291bnQiKSArCiAgZ2d0aXRsZSgiT3JpZ2luYWwgRGF0YSIpICsKICB0aGVtZV9jbGFzc2ljKCkKCmdyaWQuYXJyYW5nZShvcmlnaW5hbCwga2RlX3Bsb3QsIG5jb2w9MikKYGBgCgpQbG90dGVkIHNpZGUgYnkgc2lkZSBhcmUgdGhlIG9yaWdpbmFsIGhpc3RvZ3JhbSBhbmQgdGhlIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRvci4gVGhlIGVzdGltYXRvciBsb29rcyBwcmV0dHkgZGFybiBnb29kISBQcmV0dHkgYW1hemluZyEgVGhpcyBLREUgaXMgd2F5IGJldHRlciB0aGFuIGl0cyBoaXN0b2dyYW0gZXN0aW1hdG9yLCBhbmQgdGhlcmUncyBubyBraWRkaW5nIGluIHRoaXMgd291bGQgY29udmVyZ2UgYSBsb3QgZmFzdGVyIHRvIHRoZSB0cnV0aCB0aGFuIGEgaGlzdG9ncmFtLgoKQWxzbywgdGhvdWdoIGBrc2AgaXMgYWxyZWFkeSB1c2VkIGJ5IG1hbnksIEknZCBsaWtlIHRvIHRha2UgdGhlIHRpbWUgdG8gcHJhaXNlIGhvdyB0aG9yb3VnaCBhbmQgY3VzdG9taXphYmxlIHlvdXIgS0RFIGV4cGVyaWVuY2UgaXMgd2hpbGUgdXNpbmcgdGhpcyBgUmAgbGlicmFyeS4gSXQncyBnb29kIGZvciBiZWdpbm5lcnMgKGxpa2UgbWUpIGFuZCBtb3JlIGFkdmFuY2VkIHVzZXJzIHdobyBrbm93IGV4YWN0bHkgd2hhdCB0aGV5IHdhbnQgZnJvbSB0aGVpciBkZW5zaXR5IGVzdGltYXRlLgoKIyMgUXVlc3Rpb25zIGZvciBsYXRlcgpJIGRvbid0IGtub3cgd2hhdCAkeSQgYW5kICR6JCBhcmUgaW4gdGhlIHJpc2sgZXN0aW1hdGlvbiBmb3JtdWxhLgoKIyBSZWZlcmVuY2VzCgpDb25saW4sIE0uIApLZXJuZWwgRGVuc2l0eSBFc3RpbWF0aW9uIFs8YSBocmVmPSJodHRwczovL21hdGhpc29uaWFuLmdpdGh1Yi5pby9rZGUvIj5VUkw8L2E+XS4KCkRlbmcsIEguIFdpY2toYW0sIEguICgyMDExKS4gRGVuc2l0eSBFc3RpbWF0aW9uIGluIFIuIFs8YSBocmVmPSJodHRwczovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy9kZW5zaXR5LWVzdGltYXRpb24ucGRmIj5QREY8L2E+XS4KCkR1b25nLCBULiAoMjAwNykuIGtzOiBLZXJuZWwgZGVuc2l0eSBlc3RpbWF0aW9uIGFuZCBrZXJuZWwgZGlzY3JpbWluYW50IGFuYWx5c2lzIGZvciBtdWx0aXZhcmlhdGUgZGF0YSBpbiBSLiBKb3VybmFsIG9mIFN0YXRpc3RpY2FsIFNvZnR3YXJlLCAyMSg3KSwgMS0xNi4gWzxhIGhyZWY9Imh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9rcy9rcy5wZGYiPlBERjwvYT5dLgoKV2FuZCwgTS5QLiAmIEpvbmVzLCBNLkMuICgxOTk0KSBNdWx0aXZhcmlhdGUgcGx1Zy1pbiBiYW5kd2lkdGggc2VsZWN0aW9uLiBDb21wdXRhdGlvbmFsIFN0YXRpc3RpY3MsIDksIDk3LTExNi4KCldhc3Nlcm1hbiwgTC4gKDIwMTMpLiBBbGwgb2YgU3RhdGlzdGljcy4gU3ByaW5nZXIgU2NpZW5jZSAmIEJ1c2luZXNzIE1lZGlhLiBbPGEgaHJlZj0iaHR0cHM6Ly93d3cuc3RhdC5jbXUuZWR1L35sYXJyeS9hbGwtb2Ytc3RhdGlzdGljcy8iPlRleHQ8L2E+XS4KCgoKIyBEYXRhIFNvdXJjZQpUaGUgQW1lcyBIb3VzaW5nIERhdGFzZXQuIEluZm8gaXMgYXZhaWxhYmxlIDxhIGhyZWY9Imh0dHA6Ly9qc2UuYW1zdGF0Lm9yZy92MTluMy9kZWNvY2sucGRmIj5oZXJlPC9hPiBhbmQgZG93bmxvYWQgaXMgYXZhaWxhYmxlIHRocm91Z2ggPGEgaHJlZj0iaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9jL2hvdXNlLXByaWNlcy1hZHZhbmNlZC1yZWdyZXNzaW9uLXRlY2huaXF1ZXMiPkthZ2dsZTwvYT4u