The way I’ve taught histograms in the past is simple. You take a numeric variable, then you use ggplot2, and slap it onto your x aesthetic. Then, run your code chunk. Once you’ve done so, choose appropriate binwidths that show the relationship of the data frequencies. Then, you’d have your acceptable histogram. Instead of calling it a day right there, let’s start thinking about how we can define histograms in math notation.

1 The Anatomy of a Histogram

Each of the rectangles on a histogram is a bin. Bins are intervals defined as

\[ B_1 = [0, \frac{1}{m}), B_2 = [\frac{1}{m}, \frac{2}{m}), ..., B_m = [\frac{m-1}{m}, \frac{m}{m}). \]

Notice that the definition of bins in this way puts us onto the interval \([0,1]\). We will be assuming our draws are \(iid \sim f \in [0,1]\). Our binwidth parameter based on the above is \(h=\frac{1}{m}\). As you’ve probably noticed before, ggplot2 has these options in geom_histogram() for you to specify. Let’s start our study of histograms by taking a look at a histogram of max heart rates achieved by cardiology patients. In this dataset, our \(n=180\).

# * DECIDING MY BINWIDTH BASED ON VISUALS
our_range <- range(heart_data$resting_blood_pressure)[2] - range(heart_data$resting_blood_pressure)[1]
# * PLOTTING
ggplot(heart_data, aes(x=resting_blood_pressure)) +
  geom_histogram(binwidth=our_range/22, col="pink3", fill="pink") +
  xlab("resting blood pressure") +
  ggtitle("Max Heart Rates of Cardiology Patients") +
  theme_minimal()

When I teach, this is where I’d call it the “end of the day” for the histogram. We got to see an overall density pattern, and that’s all it’d be used for: visual purposes. In this blog, we’ll see how histograms are estimators for the true probability density of a random variable.

In the above histogram, \(h \approx 3.909\). This doesn’t seem like it quite fits the bill of \(h=\frac{1}{m}\), but this is because our \(X_1, ..., X_n\) are not \(iid \sim \, f \in [0,1]\). Let’s see what happens if we fix our data to be on the interval \([0,1]\).

# * RESCALING WITH MUTATE
heart_data <- heart_data %>% mutate(heart_rescale=max_heart_rate_achieved/max(max_heart_rate_achieved))
# * DECIDING MY NEW BINWIDTH BASED ON VISUALS
our_range_2 <- our_range / max(heart_data$resting_blood_pressure)
# * PLOTTING
ggplot(heart_data, aes(x=heart_rescale)) +
  geom_histogram(binwidth=our_range_2/22, col="pink3", fill="pink") +
  xlab("resting blood pressure") +
  ggtitle("Max Heart Rates of Cardiology Patients (Scaled)") +
  theme_minimal()

Having done the transformation, we are intrigued to see our histogram shape shift. Now, we can start diving into histogram estimators.

2 The Histogram Estimator

Let \(c_j\) be the number of observations in \(B_j\). We’ll let the probability of being in each bin be \(\hat{p}_j=c_j/n\). Finally, also let \(p_j=\int_{B_j} f(u) du\). Our histogram estimator is defined as follows

\[ \hat{f}_n(x)=\sum_{i=1}^{n} \frac{\hat{p}_j}{h} \textbf{1}_{\{x \in B_j\}}. \]

The histogram estimator \(\hat{f}_n(x)\) has expectation \(E(\hat{f}_n(x)) \approx f(x)\) where \(f(x)\) is the true probability density.

3 Expectation and Variance of the Histogram Estimator

The expectation of the histogram estimator is

\[ E(\hat{f}_n(x)) = \frac{p_j}{h} \] and

\[ \text{Var}(\hat{f}_n(x)) = \frac{p_j(1-p_j)}{nh^2} \]

where \(h\) is binwidth and \(n\) is the total number of bins.

4 Choosing Binwidth Based on the Bias-Variance Tradeoff

Recall that

\[ \text{Risk}(\theta)=\text{Bias}^2(\theta) + \text{Var}(\theta) \]

for some arbitrary estimator \(\theta\). Making use the expectation and variance of the histogram estimator, the bias-variance tradeoff, and the fact that for any \(u \in B_j\), \(f(u) \approx f(x)+(u-x)f'(x)\), we can calculate an optimal binwidth.

4.1 By Theorem

The following theorem shows that you can choose an optimal binwidth for your histogram estimator to minimize risk (mean integrated square error - MISE). The theorem shows that there does indeed exist an optimal binwidth, but it is based on the true distribution of \(f(u)\) which we don’t have in practice. This is Theorem 20.4 in Wasserman.

Given that \(\int [f'(u)]^2 du < \infty\), we have that

\[ \text{Risk}(\hat{f}_n, f) \approx \frac{h^2}{12} \int [f'(u)]^2 du + \frac{1}{nh}. \]

The value that of \(h\) (the binwidth size) that minimizes the above risk is

\[ h^*= \frac{1}{n^{1/3}} \bigg( \frac{6}{\int [f'(u)]^2 du} \bigg)^{1/3}. \]

When that binwidth is chosen, \(\text{Risk}(\hat{f}_n, f) \approx \frac{C}{n^{2/3}}\) where \(C=(3/4)^{2/3}\big( \int [f'(u)]^2 du \big)^{1/3}\). The MISE converges to 0 at a rate \(n^{-2/3}\).

4.2 By Estimation

In practice, we would be interested in estimating the risk function so that it doesn’t depend on \(f(u)\). The cross-validation estimator of risk (estimated risk) is

\[ \hat{J}(h) = \int [f'_n(x)]^2 dx - \frac{2}{n}\sum_{i=1}^{n} \hat{f}_{(-i)}(X_i). \] The \((-i)\) stands for the removal of the \(i^{th}\) observation. Also,

\[ \hat{J}(h) = \frac{2}{(n-1)h} - \frac{(n+1)}{(n-1)} \sum_{j=1}^{m} \hat{p}_j^2. \]

4.3 Estimating Risk for Heart Disease Data

Let’s look back at the heart disease data. To estimate risk, I am going to use Wasserman’s cv.hist.fun from the All of Statistics website. Source code is available here. The code implements the second formula for cross-validated risk estimate: \(\hat{J}(h) = \frac{2}{(n-1)h} - \frac{(n+1)}{(n-1)} \sum_{j=1}^{m} \hat{p}_j^2\).

# * USING WASSERMAN'S FUNCTION
source("histograms-1-source.R")
function_output <- cv.hist.fun(heart_data %>% pull(heart_rescale))
risk_estimates <- data.frame(cbind(h=function_output$h, n_bins=function_output$nbins, est_risk=function_output$risk))
risk_estimates %>% pull(est_risk) %>% summary()
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 -3.040  -2.470  -2.327  -2.333  -2.159  -1.794 

According to this output, having 180 bins minimizes estimated MISE. Since this matches the value of \(n\), it may lead you to think each bin is an observation. however, this definitely does not correspond to each observation having a bin. Recall that having 180 bins means

\[ B_1 = [0.4752, 0.4752 + \frac{1}{180}), B_2 = [0.4808, 0.4919), ..., B_m = [\frac{m-1}{m}, \frac{m}{m}). \]

where \(m=180\). This does not guarantee that there will only be one value in each of these bins.

h <- function_output$hbest
# * PLOTTING
ggplot(heart_data, aes(x=heart_rescale)) +
  geom_histogram(binwidth=h, col="pink3", fill="pink") +
  xlab("resting blood pressure") +
  ggtitle("Max Heart Rates of Cardiology Patients (Scaled)") +
  theme_minimal()

5 Confidence Bands

According to Wasserman’s 20.9 Definition, a pair of functions \(l(x)\) (lower) and \(u(x)\) (upper) is a \(1-\alpha\) confidence band if P(\(\bar{f}_n(x)\) of being within the two functions) \(\geq 1-\alpha\). That is,

\[ P\big(l(x) < \bar{f}_n(x) < u(x)\big) \geq 1-\alpha \]

where \(\bar{f}_n(x)=E(\hat{f}_n(x)) = \frac{p_j}{h} \, \forall x\in B_j\) and where \(p_j=\int_{B_j}f(u)du\).

Using a function from Wasserman, we calculate a 95% confidence band. The lines below stand for the “top” of the histogram. Imagine the diagram to be shaded below those lines. The middle line stands for \(\bar{f}_n(x)\).

# * USING WASSERMAN'S FUNCTION
source("histograms-1-source.R")
function_output_2 <- ci.fun(heart_data %>% pull(heart_rescale))
envelope_df <- data.frame(cbind(grid=function_output_2$Grid, u=function_output_2$U, l=function_output_2$L, f=function_output_2$F))
# * PLOTTING ENVELOPE
ggplot(envelope_df, aes(x=grid)) + 
  geom_line(stat="identity", aes(x=grid, y=u), col="pink3", lwd=1, alpha=0.6) +
  geom_line(stat="identity", aes(x=grid, y=f), col="pink2", lwd=2) +
  geom_line(stat="identity", aes(x=grid, y=l), col="pink", lwd=1, alpha=0.6) +
  theme_minimal() +
  xlab("heart rate maximums") +
  ylab("frequencies") +
  ggtitle("95% Confidence Band for Cardiology Patients' Heart Rate Maximums")

At least to me, histograms have a lot more life to them than they used to. If you had to remember one thing from this blog, it’s that a histogram can have an optimal number of bins that will minimize MISE. This is not to say that when you’re visualizing you shouldn’t choose bins like I did in my first two figures. Choose bins in this way when you are estimating a probability density.

6 References

Wasserman, L. (2013). All of Statistics. Springer Science & Business Media.

7 Data Source

Download link here.

  1. Hungarian Institute of Cardiology. Budapest: Andras Janosi, M.D.
  2. University Hospital, Zurich, Switzerland: William Steinbrunn, M.D.
  3. University Hospital, Basel, Switzerland: Matthias Pfisterer, M.D.
  4. V.A. Medical Center, Long Beach and Cleveland Clinic Foundation: Robert Detrano, M.D., Ph.D.
LS0tCnRpdGxlOiAnSGlzdG9ncmFtczogTW9yZSBUaGFuIE1lZXRzIFRoZSBQbG90JwpkYXRlOiA3IE1heSAyMDE5CmF1dGhvcjogRWRpZSBFc3Blam8Kb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlCiAgICB0b2NfZGVwdGg6IDMKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQotLS0KCmBgYHtyIGxpYnJhcmllcywgaW5jbHVkZT1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShyZWFkcikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyLCBxdWlldGx5PVRSVUUpCmBgYAoKVGhlIHdheSBJJ3ZlIHRhdWdodCBoaXN0b2dyYW1zIGluIHRoZSBwYXN0IGlzIHNpbXBsZS4gWW91IHRha2UgYSBudW1lcmljIHZhcmlhYmxlLCB0aGVuIHlvdSB1c2UgYGdncGxvdDJgLCBhbmQgc2xhcCBpdCBvbnRvIHlvdXIgeCBhZXN0aGV0aWMuIFRoZW4sIHJ1biB5b3VyIGNvZGUgY2h1bmsuIE9uY2UgeW91J3ZlIGRvbmUgc28sIGNob29zZSBhcHByb3ByaWF0ZSBiaW53aWR0aHMgdGhhdCBzaG93IHRoZSByZWxhdGlvbnNoaXAgb2YgdGhlIGRhdGEgZnJlcXVlbmNpZXMuIFRoZW4sIHlvdSdkIGhhdmUgeW91ciBhY2NlcHRhYmxlIGhpc3RvZ3JhbS4gSW5zdGVhZCBvZiBjYWxsaW5nIGl0IGEgZGF5IHJpZ2h0IHRoZXJlLCBsZXQncyBzdGFydCB0aGlua2luZyBhYm91dCBob3cgd2UgY2FuIGRlZmluZSBoaXN0b2dyYW1zIGluIG1hdGggbm90YXRpb24uCgojIFRoZSBBbmF0b215IG9mIGEgSGlzdG9ncmFtCkVhY2ggb2YgdGhlIHJlY3RhbmdsZXMgb24gYSBoaXN0b2dyYW0gaXMgYSAqKmJpbioqLiBCaW5zIGFyZSBpbnRlcnZhbHMgZGVmaW5lZCBhcwoKJCQKQl8xID0gWzAsIFxmcmFjezF9e219KSwgQl8yID0gW1xmcmFjezF9e219LCBcZnJhY3syfXttfSksIC4uLiwgQl9tID0gW1xmcmFje20tMX17bX0sIFxmcmFje219e219KS4KJCQKCk5vdGljZSB0aGF0IHRoZSBkZWZpbml0aW9uIG9mIGJpbnMgaW4gdGhpcyB3YXkgcHV0cyB1cyBvbnRvIHRoZSBpbnRlcnZhbCAkWzAsMV0kLiBXZSB3aWxsIGJlIGFzc3VtaW5nIG91ciBkcmF3cyBhcmUgJGlpZCBcc2ltIGYgXGluIFswLDFdJC4gT3VyICoqYmlud2lkdGgqKiBwYXJhbWV0ZXIgYmFzZWQgb24gdGhlIGFib3ZlIGlzICRoPVxmcmFjezF9e219JC4gQXMgeW91J3ZlIHByb2JhYmx5IG5vdGljZWQgYmVmb3JlLCBgZ2dwbG90MmAgaGFzIHRoZXNlIG9wdGlvbnMgaW4gYGdlb21faGlzdG9ncmFtKClgIGZvciB5b3UgdG8gc3BlY2lmeS4gTGV0J3Mgc3RhcnQgb3VyIHN0dWR5IG9mIGhpc3RvZ3JhbXMgYnkgdGFraW5nIGEgbG9vayBhdCBhIGhpc3RvZ3JhbSBvZiBtYXggaGVhcnQgcmF0ZXMgYWNoaWV2ZWQgYnkgY2FyZGlvbG9neSBwYXRpZW50cy4gSW4gdGhpcyBkYXRhc2V0LCBvdXIgJG49MTgwJC4KCmBgYHtyIGRhdGEtbG9hZCwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0Kc2V0LnNlZWQoNykKaGVhcnRfZGF0YSAgPC0gcmVhZF9jc3YoIi4uLy4uL2RhdGEvaGVhcnQtdHJhaW4uY3N2IiwgY29sX25hbWVzPVRSVUUpCmxhYmVsc19kYXRhIDwtIHJlYWRfY3N2KCIuLi8uLi9kYXRhL2hlYXJ0LXRyYWluLWxhYmVscy5jc3YiLCBjb2xfbmFtZXM9VFJVRSkKaGVhcnRfZGF0YSAgPC0gbWVyZ2UoaGVhcnRfZGF0YSwgbGFiZWxzX2RhdGEsIGJ5PSJwYXRpZW50X2lkIikKYGBgCgpgYGB7cn0KIyAqIERFQ0lESU5HIE1ZIEJJTldJRFRIIEJBU0VEIE9OIFZJU1VBTFMKb3VyX3JhbmdlIDwtIHJhbmdlKGhlYXJ0X2RhdGEkcmVzdGluZ19ibG9vZF9wcmVzc3VyZSlbMl0gLSByYW5nZShoZWFydF9kYXRhJHJlc3RpbmdfYmxvb2RfcHJlc3N1cmUpWzFdCgojICogUExPVFRJTkcKZ2dwbG90KGhlYXJ0X2RhdGEsIGFlcyh4PXJlc3RpbmdfYmxvb2RfcHJlc3N1cmUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9b3VyX3JhbmdlLzIyLCBjb2w9InBpbmszIiwgZmlsbD0icGluayIpICsKICB4bGFiKCJyZXN0aW5nIGJsb29kIHByZXNzdXJlIikgKwogIGdndGl0bGUoIk1heCBIZWFydCBSYXRlcyBvZiBDYXJkaW9sb2d5IFBhdGllbnRzIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCldoZW4gSSB0ZWFjaCwgdGhpcyBpcyB3aGVyZSBJJ2QgY2FsbCBpdCB0aGUgImVuZCBvZiB0aGUgZGF5IiBmb3IgdGhlIGhpc3RvZ3JhbS4gV2UgZ290IHRvIHNlZSBhbiBvdmVyYWxsIGRlbnNpdHkgcGF0dGVybiwgYW5kIHRoYXQncyBhbGwgaXQnZCBiZSB1c2VkIGZvcjogdmlzdWFsIHB1cnBvc2VzLiBJbiB0aGlzIGJsb2csIHdlJ2xsIHNlZSBob3cgaGlzdG9ncmFtcyBhcmUgZXN0aW1hdG9ycyBmb3IgdGhlIHRydWUgcHJvYmFiaWxpdHkgZGVuc2l0eSBvZiBhIHJhbmRvbSB2YXJpYWJsZS4KCkluIHRoZSBhYm92ZSBoaXN0b2dyYW0sICRoIFxhcHByb3ggMy45MDkkLiBUaGlzIGRvZXNuJ3Qgc2VlbSBsaWtlIGl0IHF1aXRlIGZpdHMgdGhlIGJpbGwgb2YgJGg9XGZyYWN7MX17bX0kLCBidXQgdGhpcyBpcyBiZWNhdXNlIG91ciAkWF8xLCAuLi4sIFhfbiQgYXJlIG5vdCAkaWlkIFxzaW0gXCwgZiBcaW4gWzAsMV0kLiBMZXQncyBzZWUgd2hhdCBoYXBwZW5zIGlmIHdlIGZpeCBvdXIgZGF0YSB0byBiZSBvbiB0aGUgaW50ZXJ2YWwgJFswLDFdJC4KCgpgYGB7cn0KIyAqIFJFU0NBTElORyBXSVRIIE1VVEFURQpoZWFydF9kYXRhIDwtIGhlYXJ0X2RhdGEgJT4lIG11dGF0ZShoZWFydF9yZXNjYWxlPW1heF9oZWFydF9yYXRlX2FjaGlldmVkL21heChtYXhfaGVhcnRfcmF0ZV9hY2hpZXZlZCkpCgojICogREVDSURJTkcgTVkgTkVXIEJJTldJRFRIIEJBU0VEIE9OIFZJU1VBTFMKb3VyX3JhbmdlXzIgPC0gb3VyX3JhbmdlIC8gbWF4KGhlYXJ0X2RhdGEkcmVzdGluZ19ibG9vZF9wcmVzc3VyZSkKCiMgKiBQTE9UVElORwpnZ3Bsb3QoaGVhcnRfZGF0YSwgYWVzKHg9aGVhcnRfcmVzY2FsZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aD1vdXJfcmFuZ2VfMi8yMiwgY29sPSJwaW5rMyIsIGZpbGw9InBpbmsiKSArCiAgeGxhYigicmVzdGluZyBibG9vZCBwcmVzc3VyZSIpICsKICBnZ3RpdGxlKCJNYXggSGVhcnQgUmF0ZXMgb2YgQ2FyZGlvbG9neSBQYXRpZW50cyAoU2NhbGVkKSIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpIYXZpbmcgZG9uZSB0aGUgdHJhbnNmb3JtYXRpb24sIHdlIGFyZSBpbnRyaWd1ZWQgdG8gc2VlIG91ciBoaXN0b2dyYW0gc2hhcGUgc2hpZnQuIE5vdywgd2UgY2FuIHN0YXJ0IGRpdmluZyBpbnRvIGhpc3RvZ3JhbSBlc3RpbWF0b3JzLgoKIyBUaGUgSGlzdG9ncmFtIEVzdGltYXRvcgpMZXQgJGNfaiQgYmUgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gJEJfaiQuIFdlJ2xsIGxldCB0aGUgcHJvYmFiaWxpdHkgb2YgYmVpbmcgaW4gZWFjaCBiaW4gYmUgJFxoYXR7cH1faj1jX2ovbiQuIEZpbmFsbHksIGFsc28gbGV0ICRwX2o9XGludF97Ql9qfSBmKHUpIGR1JC4gT3VyICoqaGlzdG9ncmFtIGVzdGltYXRvcioqIGlzIGRlZmluZWQgYXMgZm9sbG93cwoKJCQKXGhhdHtmfV9uKHgpPVxzdW1fe2k9MX1ee259IFxmcmFje1xoYXR7cH1fan17aH0gXHRleHRiZnsxfV97XHt4IFxpbiBCX2pcfX0uCiQkCgpUaGUgaGlzdG9ncmFtIGVzdGltYXRvciAkXGhhdHtmfV9uKHgpJCBoYXMgZXhwZWN0YXRpb24gJEUoXGhhdHtmfV9uKHgpKSBcYXBwcm94IGYoeCkkIHdoZXJlICRmKHgpJCBpcyB0aGUgdHJ1ZSBwcm9iYWJpbGl0eSBkZW5zaXR5LgoKCiMgRXhwZWN0YXRpb24gYW5kIFZhcmlhbmNlIG9mIHRoZSBIaXN0b2dyYW0gRXN0aW1hdG9yClRoZSBleHBlY3RhdGlvbiBvZiB0aGUgaGlzdG9ncmFtIGVzdGltYXRvciBpcwoKJCQKRShcaGF0e2Z9X24oeCkpID0gXGZyYWN7cF9qfXtofQokJAphbmQKCiQkClx0ZXh0e1Zhcn0oXGhhdHtmfV9uKHgpKSA9IFxmcmFje3BfaigxLXBfail9e25oXjJ9CiQkCgp3aGVyZSAkaCQgaXMgYmlud2lkdGggYW5kICRuJCBpcyB0aGUgdG90YWwgbnVtYmVyIG9mIGJpbnMuCgojIENob29zaW5nIEJpbndpZHRoIEJhc2VkIG9uIHRoZSBCaWFzLVZhcmlhbmNlIFRyYWRlb2ZmClJlY2FsbCB0aGF0CgokJApcdGV4dHtSaXNrfShcdGhldGEpPVx0ZXh0e0JpYXN9XjIoXHRoZXRhKSArIFx0ZXh0e1Zhcn0oXHRoZXRhKQokJAoKZm9yIHNvbWUgYXJiaXRyYXJ5IGVzdGltYXRvciAkXHRoZXRhJC4gTWFraW5nIHVzZSB0aGUgZXhwZWN0YXRpb24gYW5kIHZhcmlhbmNlIG9mIHRoZSBoaXN0b2dyYW0gZXN0aW1hdG9yLCB0aGUgYmlhcy12YXJpYW5jZSB0cmFkZW9mZiwgYW5kIHRoZSBmYWN0IHRoYXQgZm9yIGFueSAkdSBcaW4gQl9qJCwgJGYodSkgXGFwcHJveCBmKHgpKyh1LXgpZicoeCkkLCB3ZSBjYW4gY2FsY3VsYXRlIGFuIG9wdGltYWwgYmlud2lkdGguCgojIyBCeSBUaGVvcmVtClRoZSBmb2xsb3dpbmcgdGhlb3JlbSBzaG93cyB0aGF0IHlvdSBjYW4gY2hvb3NlIGFuIG9wdGltYWwgYmlud2lkdGggZm9yIHlvdXIgaGlzdG9ncmFtIGVzdGltYXRvciB0byBtaW5pbWl6ZSByaXNrIChtZWFuIGludGVncmF0ZWQgc3F1YXJlIGVycm9yIC0gTUlTRSkuIFRoZSB0aGVvcmVtIHNob3dzIHRoYXQgdGhlcmUgZG9lcyBpbmRlZWQgZXhpc3QgYW4gb3B0aW1hbCBiaW53aWR0aCwgYnV0IGl0IGlzIGJhc2VkIG9uIHRoZSB0cnVlIGRpc3RyaWJ1dGlvbiBvZiAkZih1KSQgd2hpY2ggd2UgZG9uJ3QgaGF2ZSBpbiBwcmFjdGljZS4gKlRoaXMgaXMgVGhlb3JlbSAyMC40IGluIFdhc3Nlcm1hbi4qCgpHaXZlbiB0aGF0ICRcaW50IFtmJyh1KV1eMiBkdSA8IFxpbmZ0eSQsIHdlIGhhdmUgdGhhdAoKJCQKXHRleHR7Umlza30oXGhhdHtmfV9uLCBmKSBcYXBwcm94IFxmcmFje2heMn17MTJ9IFxpbnQgIFtmJyh1KV1eMiBkdSArIFxmcmFjezF9e25ofS4KJCQKClRoZSB2YWx1ZSB0aGF0IG9mICRoJCAodGhlIGJpbndpZHRoIHNpemUpIHRoYXQgbWluaW1pemVzIHRoZSBhYm92ZSByaXNrIGlzCgokJApoXio9IFxmcmFjezF9e25eezEvM319IFxiaWdnKCBcZnJhY3s2fXtcaW50IFtmJyh1KV1eMiBkdX0gXGJpZ2cpXnsxLzN9LgokJAoKV2hlbiB0aGF0IGJpbndpZHRoIGlzIGNob3NlbiwgJFx0ZXh0e1Jpc2t9KFxoYXR7Zn1fbiwgZikgXGFwcHJveCBcZnJhY3tDfXtuXnsyLzN9fSQgd2hlcmUgJEM9KDMvNCleezIvM31cYmlnKCBcaW50IFtmJyh1KV1eMiBkdSBcYmlnKV57MS8zfSQuIFRoZSBNSVNFIGNvbnZlcmdlcyB0byAwIGF0IGEgcmF0ZSAkbl57LTIvM30kLgoKIyMgQnkgRXN0aW1hdGlvbgpJbiBwcmFjdGljZSwgd2Ugd291bGQgYmUgaW50ZXJlc3RlZCBpbiBlc3RpbWF0aW5nIHRoZSByaXNrIGZ1bmN0aW9uIHNvIHRoYXQgaXQgZG9lc24ndCBkZXBlbmQgb24gJGYodSkkLiBUaGUgKipjcm9zcy12YWxpZGF0aW9uIGVzdGltYXRvciBvZiByaXNrKiogKGVzdGltYXRlZCByaXNrKSBpcwoKJCQKXGhhdHtKfShoKSA9IFxpbnQgW2YnX24oeCldXjIgZHggLSBcZnJhY3syfXtufVxzdW1fe2k9MX1ee259IFxoYXR7Zn1feygtaSl9KFhfaSkuCiQkClRoZSAkKC1pKSQgc3RhbmRzIGZvciB0aGUgcmVtb3ZhbCBvZiB0aGUgJGlee3RofSQgb2JzZXJ2YXRpb24uIEFsc28sCgokJApcaGF0e0p9KGgpID0gXGZyYWN7Mn17KG4tMSlofSAtIFxmcmFjeyhuKzEpfXsobi0xKX0gXHN1bV97aj0xfV57bX0gXGhhdHtwfV9qXjIuCiQkCgojIyBFc3RpbWF0aW5nIFJpc2sgZm9yIEhlYXJ0IERpc2Vhc2UgRGF0YQpMZXQncyBsb29rIGJhY2sgYXQgdGhlIGhlYXJ0IGRpc2Vhc2UgZGF0YS4gVG8gZXN0aW1hdGUgcmlzaywgSSBhbSBnb2luZyB0byB1c2UgV2Fzc2VybWFuJ3MgYGN2Lmhpc3QuZnVuYCBmcm9tIHRoZSBBbGwgb2YgU3RhdGlzdGljcyB3ZWJzaXRlLiBTb3VyY2UgY29kZSBpcyBhdmFpbGFibGUgPGEgaHJlZj0iaHR0cHM6Ly93d3cuc3RhdC5jbXUuZWR1L35sYXJyeS9hbGwtb2Ytc3RhdGlzdGljcy89UnByb2dyYW1zL2RlbnNpdHkuZXhhbXBsZXMuciI+aGVyZTwvYT4uIFRoZSBjb2RlIGltcGxlbWVudHMgdGhlIHNlY29uZCBmb3JtdWxhIGZvciBjcm9zcy12YWxpZGF0ZWQgcmlzayBlc3RpbWF0ZTogJFxoYXR7Sn0oaCkgPSBcZnJhY3syfXsobi0xKWh9IC0gXGZyYWN7KG4rMSl9eyhuLTEpfSBcc3VtX3tqPTF9XnttfSBcaGF0e3B9X2peMiQuCgpgYGB7cn0KIyAqIFVTSU5HIFdBU1NFUk1BTidTIEZVTkNUSU9OCnNvdXJjZSgiaGlzdG9ncmFtcy0xLXNvdXJjZS5SIikKZnVuY3Rpb25fb3V0cHV0IDwtIGN2Lmhpc3QuZnVuKGhlYXJ0X2RhdGEgJT4lIHB1bGwoaGVhcnRfcmVzY2FsZSkpCnJpc2tfZXN0aW1hdGVzIDwtIGRhdGEuZnJhbWUoY2JpbmQoaD1mdW5jdGlvbl9vdXRwdXQkaCwgbl9iaW5zPWZ1bmN0aW9uX291dHB1dCRuYmlucywgZXN0X3Jpc2s9ZnVuY3Rpb25fb3V0cHV0JHJpc2spKQoKcmlza19lc3RpbWF0ZXMgJT4lIHB1bGwoZXN0X3Jpc2spICU+JSBzdW1tYXJ5KCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRX0KIyAqIFBMT1RUSU5HIFJJU0sgQVMgQSBGVU5DVElPTiBPRiBCSU5TCmdncGxvdChyaXNrX2VzdGltYXRlcywgYWVzKHg9bl9iaW5zLCB5PWVzdF9yaXNrKSkgKyAKICBnZW9tX3BvaW50KGNvbD0icGluayIsIGFscGhhPTAuOCkgKyAKICBnZW9tX2xpbmUoY29sPSJwaW5rNCIpICsKICB4bGFiKCJudW1iZXIgb2YgYmlucyIpICsKICB5bGFiKCJyaXNrIGVzdGltYXRlIikgKwogIGdndGl0bGUoIkhpc3RvZ3JhbSBSaXNrIEVzdGltYXRlcyBhcyBhIEZ1bmN0aW9uIG9mIFRvdGFsIEJpbnMgZm9yIENhcmRpb2xvZ3kgRGF0YSIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpBY2NvcmRpbmcgdG8gdGhpcyBvdXRwdXQsIGhhdmluZyAxODAgYmlucyBtaW5pbWl6ZXMgZXN0aW1hdGVkIE1JU0UuIFNpbmNlIHRoaXMgbWF0Y2hlcyB0aGUgdmFsdWUgb2YgJG4kLCBpdCBtYXkgbGVhZCB5b3UgdG8gdGhpbmsgZWFjaCBiaW4gaXMgYW4gb2JzZXJ2YXRpb24uIGhvd2V2ZXIsIHRoaXMgZGVmaW5pdGVseSBkb2VzIG5vdCBjb3JyZXNwb25kIHRvIGVhY2ggb2JzZXJ2YXRpb24gaGF2aW5nIGEgYmluLiBSZWNhbGwgdGhhdCBoYXZpbmcgMTgwIGJpbnMgbWVhbnMKCmBgYHtyLCBlY2hvPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpoZWFydF9kYXRhICU+JSBzdW1tYXJpemUobWluPW1pbihoZWFydF9yZXNjYWxlKSwgbWF4PW1heChoZWFydF9yZXNjYWxlKSkKYGBgCgokJApCXzEgPSBbMC40NzUyLCAwLjQ3NTIgKyBcZnJhY3sxfXsxODB9KSwgQl8yID0gWzAuNDgwOCwgMC40OTE5KSwgLi4uLCBCX20gPSBbXGZyYWN7bS0xfXttfSwgXGZyYWN7bX17bX0pLgokJAoKd2hlcmUgJG09MTgwJC4gVGhpcyBkb2VzIG5vdCBndWFyYW50ZWUgdGhhdCB0aGVyZSB3aWxsIG9ubHkgYmUgb25lIHZhbHVlIGluIGVhY2ggb2YgdGhlc2UgYmlucy4KCmBgYHtyfQpoIDwtIGZ1bmN0aW9uX291dHB1dCRoYmVzdAojICogUExPVFRJTkcKZ2dwbG90KGhlYXJ0X2RhdGEsIGFlcyh4PWhlYXJ0X3Jlc2NhbGUpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGg9aCwgY29sPSJwaW5rMyIsIGZpbGw9InBpbmsiKSArCiAgeGxhYigicmVzdGluZyBibG9vZCBwcmVzc3VyZSIpICsKICBnZ3RpdGxlKCJNYXggSGVhcnQgUmF0ZXMgb2YgQ2FyZGlvbG9neSBQYXRpZW50cyAoU2NhbGVkKSIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpgYGB7ciwgaW5jbHVkZT1GQUxTRSwgZWNobz1GQUxTRX0KbSA8LSBmdW5jdGlvbl9vdXRwdXQkbWJlc3QKIyAqIFBMT1RUSU5HCmdncGxvdChoZWFydF9kYXRhLCBhZXMoeD1oZWFydF9yZXNjYWxlKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbnM9bSwgY29sPSJwaW5rMyIsIGZpbGw9InBpbmsiKSArCiAgeGxhYigicmVzdGluZyBibG9vZCBwcmVzc3VyZSIpICsKICBnZ3RpdGxlKCJNYXggSGVhcnQgUmF0ZXMgb2YgQ2FyZGlvbG9neSBQYXRpZW50cyAoU2NhbGVkKSIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIENvbmZpZGVuY2UgQmFuZHMKQWNjb3JkaW5nIHRvIFdhc3Nlcm1hbidzIDIwLjkgRGVmaW5pdGlvbiwgYSBwYWlyIG9mIGZ1bmN0aW9ucyAkbCh4KSQgKGxvd2VyKSBhbmQgJHUoeCkkICh1cHBlcikgaXMgYSAkMS1cYWxwaGEkIGNvbmZpZGVuY2UgYmFuZCBpZiBQKCRcYmFye2Z9X24oeCkkIG9mIGJlaW5nIHdpdGhpbiB0aGUgdHdvIGZ1bmN0aW9ucykgJFxnZXEgMS1cYWxwaGEkLiBUaGF0IGlzLAoKJCQKUFxiaWcobCh4KSA8IFxiYXJ7Zn1fbih4KSA8IHUoeClcYmlnKSBcZ2VxIDEtXGFscGhhCiQkCgp3aGVyZSAkXGJhcntmfV9uKHgpPUUoXGhhdHtmfV9uKHgpKSA9IFxmcmFje3Bfan17aH0gXCwgXGZvcmFsbCB4XGluIEJfaiQgYW5kIHdoZXJlICRwX2o9XGludF97Ql9qfWYodSlkdSQuCgpVc2luZyBhIGZ1bmN0aW9uIGZyb20gV2Fzc2VybWFuLCB3ZSBjYWxjdWxhdGUgYSA5NSUgY29uZmlkZW5jZSBiYW5kLiBUaGUgbGluZXMgYmVsb3cgc3RhbmQgZm9yIHRoZSAidG9wIiBvZiB0aGUgaGlzdG9ncmFtLiBJbWFnaW5lIHRoZSBkaWFncmFtIHRvIGJlIHNoYWRlZCBiZWxvdyB0aG9zZSBsaW5lcy4gVGhlIG1pZGRsZSBsaW5lIHN0YW5kcyBmb3IgJFxiYXJ7Zn1fbih4KSQuCgpgYGB7cn0KIyAqIFVTSU5HIFdBU1NFUk1BTidTIEZVTkNUSU9OCnNvdXJjZSgiaGlzdG9ncmFtcy0xLXNvdXJjZS5SIikKZnVuY3Rpb25fb3V0cHV0XzIgPC0gY2kuZnVuKGhlYXJ0X2RhdGEgJT4lIHB1bGwoaGVhcnRfcmVzY2FsZSkpCgplbnZlbG9wZV9kZiA8LSBkYXRhLmZyYW1lKGNiaW5kKGdyaWQ9ZnVuY3Rpb25fb3V0cHV0XzIkR3JpZCwgdT1mdW5jdGlvbl9vdXRwdXRfMiRVLCBsPWZ1bmN0aW9uX291dHB1dF8yJEwsIGY9ZnVuY3Rpb25fb3V0cHV0XzIkRikpCgoKIyAqIFBMT1RUSU5HIEVOVkVMT1BFCmdncGxvdChlbnZlbG9wZV9kZiwgYWVzKHg9Z3JpZCkpICsgCiAgZ2VvbV9saW5lKHN0YXQ9ImlkZW50aXR5IiwgYWVzKHg9Z3JpZCwgeT11KSwgY29sPSJwaW5rMyIsIGx3ZD0xLCBhbHBoYT0wLjYpICsKICBnZW9tX2xpbmUoc3RhdD0iaWRlbnRpdHkiLCBhZXMoeD1ncmlkLCB5PWYpLCBjb2w9InBpbmsyIiwgbHdkPTIpICsKICBnZW9tX2xpbmUoc3RhdD0iaWRlbnRpdHkiLCBhZXMoeD1ncmlkLCB5PWwpLCBjb2w9InBpbmsiLCBsd2Q9MSwgYWxwaGE9MC42KSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB4bGFiKCJoZWFydCByYXRlIG1heGltdW1zIikgKwogIHlsYWIoImZyZXF1ZW5jaWVzIikgKwogIGdndGl0bGUoIjk1JSBDb25maWRlbmNlIEJhbmQgZm9yIENhcmRpb2xvZ3kgUGF0aWVudHMnIEhlYXJ0IFJhdGUgTWF4aW11bXMiKQpgYGAKCkF0IGxlYXN0IHRvIG1lLCBoaXN0b2dyYW1zIGhhdmUgYSBsb3QgbW9yZSBsaWZlIHRvIHRoZW0gdGhhbiB0aGV5IHVzZWQgdG8uIElmIHlvdSBoYWQgdG8gcmVtZW1iZXIgb25lIHRoaW5nIGZyb20gdGhpcyBibG9nLCBpdCdzIHRoYXQgYSBoaXN0b2dyYW0gY2FuIGhhdmUgYW4gb3B0aW1hbCBudW1iZXIgb2YgYmlucyB0aGF0IHdpbGwgbWluaW1pemUgTUlTRS4gVGhpcyBpcyBub3QgdG8gc2F5IHRoYXQgd2hlbiB5b3UncmUgdmlzdWFsaXppbmcgeW91IHNob3VsZG4ndCBjaG9vc2UgYmlucyBsaWtlIEkgZGlkIGluIG15IGZpcnN0IHR3byBmaWd1cmVzLiBDaG9vc2UgYmlucyBpbiB0aGlzIHdheSB3aGVuIHlvdSBhcmUgZXN0aW1hdGluZyBhIHByb2JhYmlsaXR5IGRlbnNpdHkuCgojIFJlZmVyZW5jZXMKV2Fzc2VybWFuLCBMLiAoMjAxMykuIEFsbCBvZiBTdGF0aXN0aWNzLiBTcHJpbmdlciBTY2llbmNlICYgQnVzaW5lc3MgTWVkaWEuCgotIDxhIGhyZWY9Imh0dHBzOi8vd3d3LnN0YXQuY211LmVkdS9+bGFycnkvYWxsLW9mLXN0YXRpc3RpY3MvPVJwcm9ncmFtcy9kZW5zaXR5LmV4YW1wbGVzLnIiPlJpc2sgZXN0aW1hdGlvbiBjb2RlLjwvYT4gIAoKLSA8YSBocmVmPSJodHRwczovL3d3dy5zdGF0LmNtdS5lZHUvfmxhcnJ5L2FsbC1vZi1zdGF0aXN0aWNzLz1ScHJvZ3JhbXMvaGlzdG9ncmFtLmNvbmZpZGVuY2UuciI+Q29uZmlkZW5jZSBiYW5kIGNvZGUuPC9hPiAgCgojIERhdGEgU291cmNlCkRvd25sb2FkIGxpbmsgPGEgaHJlZj0iaHR0cDovL2FyY2hpdmUuaWNzLnVjaS5lZHUvbWwvZGF0YXNldHMvSGVhcnQrRGlzZWFzZSI+aGVyZTwvYT4uCgoxLiBIdW5nYXJpYW4gSW5zdGl0dXRlIG9mIENhcmRpb2xvZ3kuIEJ1ZGFwZXN0OiBBbmRyYXMgSmFub3NpLCBNLkQuICAKMi4gVW5pdmVyc2l0eSBIb3NwaXRhbCwgWnVyaWNoLCBTd2l0emVybGFuZDogV2lsbGlhbSBTdGVpbmJydW5uLCBNLkQuICAgCjMuIFVuaXZlcnNpdHkgSG9zcGl0YWwsIEJhc2VsLCBTd2l0emVybGFuZDogTWF0dGhpYXMgUGZpc3RlcmVyLCBNLkQuICAgCjQuIFYuQS4gTWVkaWNhbCBDZW50ZXIsIExvbmcgQmVhY2ggYW5kIENsZXZlbGFuZCBDbGluaWMgRm91bmRhdGlvbjogUm9iZXJ0IERldHJhbm8sIE0uRC4sIFBoLkQuICAg