suppressPackageStartupMessages(library("tidyverse"))
suppressPackageStartupMessages(library("modelr"))
suppressPackageStartupMessages(library("lubridate"))
suppressPackageStartupMessages(library("broom"))
suppressPackageStartupMessages(library("nycflights13"))

The splines package is needed for the ns() function used in one of the solutions.

suppressPackageStartupMessages(library("splines"))
options(na.action = na.warn)

This code appears in the section and is necessary for the exercises.

diamonds2 <- diamonds %>%
  filter(carat <= 2.5) %>%
  mutate(
    lprice = log2(price),
    lcarat = log2(carat)
  )

mod_diamond2 <- lm(lprice ~ lcarat + color + cut + clarity, data = diamonds2)

diamonds2 <- add_residuals(diamonds2, mod_diamond2, "lresid2")

1. In the plot of lcarat vs. lprice, there are some bright vertical strips. What do they represent?

The distribution of diamonds has more diamonds at round or otherwise human-friendly numbers (fractions).

2 If log(price) = a_0 + a_1 * log(carat), what does that say about the relationship between price and carat?

Following the examples in the lecture, I use a base-2 logarithm.

mod_log <- lm(log2(price) ~ log2(carat), data = diamonds)
mod_log

Call:
lm(formula = log2(price) ~ log2(carat), data = diamonds)

Coefficients:
(Intercept)  log2(carat)  
     12.189        1.676  

The estimated relationship between carat and price looks like this.

tibble(carat = seq(0.25, 5, by = 0.25)) %>%
  add_predictions(mod_log) %>%
  ggplot(aes(x = carat, y = 2^pred)) +
  geom_line() +
  labs(x = "carat", y = "price")

The plot shows that the estimated relationship between carat and price is not linear. The exact relationship in this model is if \(x\) increases \(r\) times, then \(y\) increases \(r^{a_1}\) times. For example, a two times increase in carat is associated with the following increase in price:

2^coef(mod_log)[2]
log2(carat) 
   3.195002 

Let’s confirm this relationship by checking it for a few values of the carat variable. Let’s increase carat from 1 to 2.

2^(predict(mod_log, newdata = tibble(carat = 2)) -
  predict(mod_log, newdata = tibble(carat = 1)))
       1 
3.195002 

Note that, since predict() predicts log2(carat) rather than carat, the prediction is exponentiated by 2. Now let’s increase carat from 4 to 2.

2^(predict(mod_log, newdata = tibble(carat = 4)) -
  predict(mod_log, newdata = tibble(carat = 2)))
       1 
3.195002 

Finally, let’s increase carat from 0.5 to 1.

2^(predict(mod_log, newdata = tibble(carat = 1)) -
  predict(mod_log, newdata = tibble(carat = 0.5)))
       1 
3.195002 

All of these examples return the same value, \(2^{a_1}=3.2\).

So why is this? Let’s ignore the names of the variables in this case and consider the equation: \(\log_by = a_0 +a_1\log_bx\)

We want to understand how the difference in \(y\) is related to the difference in \(x\). Now, consider this equation at two different values

\(\log_by_0 = a_0 +a_1\log_bx_0\) \(\log_by_1 = a_0 +a_1\log_bx_1\)

What is the value of the difference, \(\log y_1 - \log y_0\) ?

\(\log_b\left(y_1\right)-\log_b\left(y_0\right) = \left( a_0 + a1\log_bx_1\right)- \left( a_0 + a1\log_bx_0\right)\)

\(=a_1\left( \log_bx_1- \log_bx_0\right),\)

\(\log_b\left(\frac{y_1}{y_0}\right)=\log_b\left(\frac{x_1}{x_0}\right)^{a_1}\)

\(\frac{y_1}{y_0}=\left(\frac{x_1}{x_0}\right)^{a_1}\)

Let \(s = \frac{y_1}{y_0}\) and \(r=\frac{x_1}{x_0}\). Then,

\(s = r^{a_1}\).

In other words, an \(r\) times increase in \(x\), is associated with a \(r^{a_1}\) times increase in \(y\). Note that this relationship does not depend on the base of the logarithm, \(b\).

There is another approximation that is commonly used when logarithms appear in regressions. The first way to show this is using the approximation that \(x\) is small, meaning that \(x\approx0\),

\(\log \left( 1+x \right)\approx x\)

This approximation is the first order Taylor expansion of the function at \(x=0\). Now consider the relationship between the percent change in \(x\) and the percent change in \(y\),

\(\log\left(y+\Delta y\right)− \log y=\left(\alpha+\beta\log\left(x+\Delta x\right)\right)−\left(\alpha+\beta \log x\right)\) \(\log\left(\frac{y+\Delta y}{y}\right)=\beta\log\left(\frac{x+\Delta x}{x}\right)\) \(\log\left(1+\frac{\Delta y}{y}\right)=\beta\log\left(1+\frac{\Delta x}{x}\right)\) \(\frac{\Delta y}{y} \approx \beta\left(\frac{\Delta x}{x}\right)\)

Thus a 1% percentage change in \(x\) is associated with a \(\beta\) percent change in \(y\).

This relationship can also be derived by taking the derivative of \(\log y\) with respect to
\(x\). First, rewrite the equation in terms of \(y\),

\(y=\exp(a_0+a_1 \log(x))\)

Then differentiate \(y\) with respect to \(x\),

\(dy=\exp(a_0+a_1 \log x)\left(\frac{a_1}{x}\right)dx\) \(=a_1y\left(\frac{dx}{x}\right)\) \((dy/y)=a_1(dx/x)\) \(\% \Delta y = a_1 \% \Delta x\)

3. Extract the diamonds that have very high and very low residuals. Is there anything unusual about these diamonds? Are they particularly bad or good, or do you think these are pricing errors?

diamonds2 %>%
  filter(abs(lresid2) > 1) %>%
  add_predictions(mod_diamond2) %>%
  mutate(pred = round(2^pred)) %>%
  select(price, pred, carat:table, x:z) %>%
  arrange(price)

I did not see anything too unusual. Do you?

4. Does the final model, mod_diamonds2, do a good job of predicting diamond prices? Would you trust it to tell you how much to spend if you were buying a diamond?

Plotting the residuals of the model shows that there are some large outliers for small carat sizes. The largest of these residuals are a little over two, which means that the true value was four times lower. Most of the mass of the residuals is between -0.5 and 0.5, which corresponds to about \(\pm 40\). There seems to be a slight downward bias in the residuals as carat size increases.

ggplot(diamonds2, aes(lcarat, lresid2)) +
  geom_hex(bins = 50)



lresid2_summary <- summarise(diamonds2,
  rmse = sqrt(mean(lresid2^2)),
  mae = mean(abs(lresid2)),
  p025 = quantile(lresid2, 0.025),
  p975 = quantile(lresid2, 0.975)
)
lresid2_summary

While in some cases the model can be wrong, overall the model seems to perform well. The root mean squared error is 0.19 meaning that the average error is about -14%. Another summary statistics of errors is the mean absolute error (MAE), which is the mean of the absolute values of the errors. The MAE is 0.15, which is -11%. Finally, 95% of the residuals are between -0.37 and 0.38, which correspond to 23—31.

Whether you think that this is a good model depends on factors outside the statistical model itself. It will depend on the how the model is being used. I have no idea how to price diamonds, so this would be useful to me in order to understand a reasonable price range for a diamond, so I don’t get ripped off. However, if I were buying and selling diamonds as a business, I would probably require a better model.

LS0tDQp0aXRsZTogIldoeSBhcmUgbG93IHF1YWxpdHkgZGlhbW9uZHMgbW9yZSBleHBlbnNpdmU/Ig0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCi0tLQ0KDQpgYGB7cixtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgidGlkeXZlcnNlIikpDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgibW9kZWxyIikpDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgibHVicmlkYXRlIikpDQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgiYnJvb20iKSkNCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KCJueWNmbGlnaHRzMTMiKSkNCmBgYA0KDQpUaGUgYHNwbGluZXNgIHBhY2thZ2UgaXMgbmVlZGVkIGZvciB0aGUgYG5zKClgIGZ1bmN0aW9uIHVzZWQgaW4gb25lIG9mIHRoZSBzb2x1dGlvbnMuDQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KCJzcGxpbmVzIikpDQpvcHRpb25zKG5hLmFjdGlvbiA9IG5hLndhcm4pDQpgYGANCg0KVGhpcyBjb2RlIGFwcGVhcnMgaW4gdGhlIHNlY3Rpb24gYW5kIGlzIG5lY2Vzc2FyeSBmb3IgdGhlIGV4ZXJjaXNlcy4NCg0KYGBge3J9DQpkaWFtb25kczIgPC0gZGlhbW9uZHMgJT4lDQogIGZpbHRlcihjYXJhdCA8PSAyLjUpICU+JQ0KICBtdXRhdGUoDQogICAgbHByaWNlID0gbG9nMihwcmljZSksDQogICAgbGNhcmF0ID0gbG9nMihjYXJhdCkNCiAgKQ0KDQptb2RfZGlhbW9uZDIgPC0gbG0obHByaWNlIH4gbGNhcmF0ICsgY29sb3IgKyBjdXQgKyBjbGFyaXR5LCBkYXRhID0gZGlhbW9uZHMyKQ0KDQpkaWFtb25kczIgPC0gYWRkX3Jlc2lkdWFscyhkaWFtb25kczIsIG1vZF9kaWFtb25kMiwgImxyZXNpZDIiKQ0KYGBgDQoNCiMjIyAxLiBJbiB0aGUgcGxvdCBvZiBgbGNhcmF0YCB2cy4gYGxwcmljZWAsIHRoZXJlIGFyZSBzb21lIGJyaWdodCB2ZXJ0aWNhbCBzdHJpcHMuIFdoYXQgZG8gdGhleSByZXByZXNlbnQ/DQoNClRoZSBkaXN0cmlidXRpb24gb2YgZGlhbW9uZHMgaGFzIG1vcmUgZGlhbW9uZHMgYXQgcm91bmQgb3Igb3RoZXJ3aXNlIGh1bWFuLWZyaWVuZGx5IG51bWJlcnMgKGZyYWN0aW9ucykuDQoNCiMjIyAyIElmIGBsb2cocHJpY2UpID0gYV8wICsgYV8xICogbG9nKGNhcmF0KWAsIHdoYXQgZG9lcyB0aGF0IHNheSBhYm91dCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYHByaWNlYCBhbmQgYGNhcmF0YD8NCg0KRm9sbG93aW5nIHRoZSBleGFtcGxlcyBpbiB0aGUgbGVjdHVyZSwgSSB1c2UgYSBiYXNlLTIgbG9nYXJpdGhtLg0KDQpgYGB7cn0NCm1vZF9sb2cgPC0gbG0obG9nMihwcmljZSkgfiBsb2cyKGNhcmF0KSwgZGF0YSA9IGRpYW1vbmRzKQ0KbW9kX2xvZw0KYGBgDQoNClRoZSBlc3RpbWF0ZWQgcmVsYXRpb25zaGlwIGJldHdlZW4gY2FyYXQgYW5kIHByaWNlIGxvb2tzIGxpa2UgdGhpcy4NCg0KYGBge3J9DQp0aWJibGUoY2FyYXQgPSBzZXEoMC4yNSwgNSwgYnkgPSAwLjI1KSkgJT4lDQogIGFkZF9wcmVkaWN0aW9ucyhtb2RfbG9nKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gY2FyYXQsIHkgPSAyXnByZWQpKSArDQogIGdlb21fbGluZSgpICsNCiAgbGFicyh4ID0gImNhcmF0IiwgeSA9ICJwcmljZSIpDQpgYGANCg0KVGhlIHBsb3Qgc2hvd3MgdGhhdCB0aGUgZXN0aW1hdGVkIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGNhcmF0IGFuZCBwcmljZSBpcyBub3QgbGluZWFyLiBUaGUgZXhhY3QgcmVsYXRpb25zaGlwIGluIHRoaXMgbW9kZWwgaXMgaWYgJHgkIGluY3JlYXNlcyAkciQgdGltZXMsIHRoZW4gJHkkIGluY3JlYXNlcyAkcl57YV8xfSQgdGltZXMuIEZvciBleGFtcGxlLCBhIHR3byB0aW1lcyBpbmNyZWFzZSBpbiBjYXJhdCBpcyBhc3NvY2lhdGVkIHdpdGggdGhlIGZvbGxvd2luZyBpbmNyZWFzZSBpbiBwcmljZToNCg0KYGBge1J9DQoyXmNvZWYobW9kX2xvZylbMl0NCmBgYA0KDQpMZXTigJlzIGNvbmZpcm0gdGhpcyByZWxhdGlvbnNoaXAgYnkgY2hlY2tpbmcgaXQgZm9yIGEgZmV3IHZhbHVlcyBvZiB0aGUgY2FyYXQgdmFyaWFibGUuIExldOKAmXMgaW5jcmVhc2UgY2FyYXQgZnJvbSAxIHRvIDIuDQoNCmBgYHtyfQ0KMl4ocHJlZGljdChtb2RfbG9nLCBuZXdkYXRhID0gdGliYmxlKGNhcmF0ID0gMikpIC0NCiAgcHJlZGljdChtb2RfbG9nLCBuZXdkYXRhID0gdGliYmxlKGNhcmF0ID0gMSkpKQ0KYGBgDQoNCk5vdGUgdGhhdCwgc2luY2UgYHByZWRpY3QoKWAgcHJlZGljdHMgYGxvZzIoY2FyYXQpYCByYXRoZXIgdGhhbiBjYXJhdCwgdGhlIHByZWRpY3Rpb24gaXMgZXhwb25lbnRpYXRlZCBieSAyLiBOb3cgbGV04oCZcyBpbmNyZWFzZSBjYXJhdCBmcm9tIDQgdG8gMi4NCg0KYGBge3J9DQoyXihwcmVkaWN0KG1vZF9sb2csIG5ld2RhdGEgPSB0aWJibGUoY2FyYXQgPSA0KSkgLQ0KICBwcmVkaWN0KG1vZF9sb2csIG5ld2RhdGEgPSB0aWJibGUoY2FyYXQgPSAyKSkpDQpgYGANCg0KRmluYWxseSwgbGV04oCZcyBpbmNyZWFzZSBjYXJhdCBmcm9tIDAuNSB0byAxLg0KDQpgYGB7cn0NCjJeKHByZWRpY3QobW9kX2xvZywgbmV3ZGF0YSA9IHRpYmJsZShjYXJhdCA9IDEpKSAtDQogIHByZWRpY3QobW9kX2xvZywgbmV3ZGF0YSA9IHRpYmJsZShjYXJhdCA9IDAuNSkpKQ0KYGBgDQoNCkFsbCBvZiB0aGVzZSBleGFtcGxlcyByZXR1cm4gdGhlIHNhbWUgdmFsdWUsICQyXnthXzF9PTMuMiQuDQoNClNvIHdoeSBpcyB0aGlzPyBMZXTigJlzIGlnbm9yZSB0aGUgbmFtZXMgb2YgdGhlIHZhcmlhYmxlcyBpbiB0aGlzIGNhc2UgYW5kIGNvbnNpZGVyIHRoZSBlcXVhdGlvbjoNCiRcbG9nX2J5ID0gYV8wICthXzFcbG9nX2J4JA0KDQpXZSB3YW50IHRvIHVuZGVyc3RhbmQgaG93IHRoZSBkaWZmZXJlbmNlIGluICR5JCBpcyByZWxhdGVkIHRvIHRoZSBkaWZmZXJlbmNlIGluICR4JC4gTm93LCBjb25zaWRlciB0aGlzIGVxdWF0aW9uIGF0IHR3byBkaWZmZXJlbnQgdmFsdWVzICANCg0KJFxsb2dfYnlfMCA9IGFfMCArYV8xXGxvZ19ieF8wJA0KJFxsb2dfYnlfMSA9IGFfMCArYV8xXGxvZ19ieF8xJA0KDQpXaGF0IGlzIHRoZSB2YWx1ZSBvZiB0aGUgZGlmZmVyZW5jZSwgJFxsb2cgeV8xIC0gXGxvZyB5XzAkICA/DQoNCiRcbG9nX2JcbGVmdCh5XzFccmlnaHQpLVxsb2dfYlxsZWZ0KHlfMFxyaWdodCkgPSAgXGxlZnQoIGFfMCArIGExXGxvZ19ieF8xXHJpZ2h0KS0gXGxlZnQoIGFfMCArIGExXGxvZ19ieF8wXHJpZ2h0KSQNCg0KJD1hXzFcbGVmdCggXGxvZ19ieF8xLSBcbG9nX2J4XzBccmlnaHQpLCQNCg0KJFxsb2dfYlxsZWZ0KFxmcmFje3lfMX17eV8wfVxyaWdodCk9XGxvZ19iXGxlZnQoXGZyYWN7eF8xfXt4XzB9XHJpZ2h0KV57YV8xfSQNCg0KJFxmcmFje3lfMX17eV8wfT1cbGVmdChcZnJhY3t4XzF9e3hfMH1ccmlnaHQpXnthXzF9JA0KDQpMZXQgJHMgPSBcZnJhY3t5XzF9e3lfMH0kICBhbmQgJHI9XGZyYWN7eF8xfXt4XzB9JC4gVGhlbiwNCg0KJHMgPSByXnthXzF9JC4NCiANCkluIG90aGVyIHdvcmRzLCBhbiAkciQgdGltZXMgaW5jcmVhc2UgaW4gJHgkLCBpcyBhc3NvY2lhdGVkIHdpdGggYSAkcl57YV8xfSQgdGltZXMgaW5jcmVhc2UgaW4gJHkkLiBOb3RlIHRoYXQgdGhpcyByZWxhdGlvbnNoaXAgZG9lcyBub3QgZGVwZW5kIG9uIHRoZSBiYXNlIG9mIHRoZSBsb2dhcml0aG0sICRiJC4NCg0KVGhlcmUgaXMgYW5vdGhlciBhcHByb3hpbWF0aW9uIHRoYXQgaXMgY29tbW9ubHkgdXNlZCB3aGVuIGxvZ2FyaXRobXMgYXBwZWFyIGluIHJlZ3Jlc3Npb25zLiBUaGUgZmlyc3Qgd2F5IHRvIHNob3cgdGhpcyBpcyB1c2luZyB0aGUgYXBwcm94aW1hdGlvbiB0aGF0ICR4JCBpcyBzbWFsbCwgbWVhbmluZyB0aGF0ICR4XGFwcHJveDAkLA0KDQokXGxvZyBcbGVmdCggMSt4IFxyaWdodClcYXBwcm94IHgkDQogDQpUaGlzIGFwcHJveGltYXRpb24gaXMgdGhlIGZpcnN0IG9yZGVyIFRheWxvciBleHBhbnNpb24gb2YgdGhlIGZ1bmN0aW9uIGF0ICR4PTAkLiBOb3cgY29uc2lkZXIgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBwZXJjZW50IGNoYW5nZSBpbiAkeCQgYW5kIHRoZSBwZXJjZW50IGNoYW5nZSBpbiAkeSQsDQoNCiRcbG9nXGxlZnQoeStcRGVsdGEgeVxyaWdodCniiJIgXGxvZyB5PVxsZWZ0KFxhbHBoYStcYmV0YVxsb2dcbGVmdCh4K1xEZWx0YSB4XHJpZ2h0KVxyaWdodCniiJJcbGVmdChcYWxwaGErXGJldGEgXGxvZyB4XHJpZ2h0KSQNCiRcbG9nXGxlZnQoXGZyYWN7eStcRGVsdGEgeX17eX1ccmlnaHQpPVxiZXRhXGxvZ1xsZWZ0KFxmcmFje3grXERlbHRhIHh9e3h9XHJpZ2h0KSQNCiRcbG9nXGxlZnQoMStcZnJhY3tcRGVsdGEgeX17eX1ccmlnaHQpPVxiZXRhXGxvZ1xsZWZ0KDErXGZyYWN7XERlbHRhIHh9e3h9XHJpZ2h0KSQNCiRcZnJhY3tcRGVsdGEgeX17eX0gXGFwcHJveCBcYmV0YVxsZWZ0KFxmcmFje1xEZWx0YSB4fXt4fVxyaWdodCkkDQogDQpUaHVzIGEgMSUgcGVyY2VudGFnZSBjaGFuZ2UgaW4gJHgkIGlzIGFzc29jaWF0ZWQgd2l0aCBhICRcYmV0YSQgcGVyY2VudCBjaGFuZ2UgaW4gJHkkLg0KDQpUaGlzIHJlbGF0aW9uc2hpcCBjYW4gYWxzbyBiZSBkZXJpdmVkIGJ5IHRha2luZyB0aGUgZGVyaXZhdGl2ZSBvZiAkXGxvZyB5JCB3aXRoIHJlc3BlY3QgdG8gIA0KJHgkLiBGaXJzdCwgcmV3cml0ZSB0aGUgZXF1YXRpb24gaW4gdGVybXMgb2YgJHkkLA0KDQokeT1cZXhwKGFfMCthXzEgXGxvZyh4KSkkDQogDQpUaGVuIGRpZmZlcmVudGlhdGUgJHkkIHdpdGggcmVzcGVjdCB0byAkeCQsDQoNCiRkeT1cZXhwKGFfMCthXzEgXGxvZyB4KVxsZWZ0KFxmcmFje2FfMX17eH1ccmlnaHQpZHgkDQokPWFfMXlcbGVmdChcZnJhY3tkeH17eH1ccmlnaHQpJA0KJChkeS95KT1hXzEoZHgveCkkDQokXCUgXERlbHRhIHkgPSBhXzEgXCUgXERlbHRhIHgkDQogDQoNCiMjIyAzLiBFeHRyYWN0IHRoZSBkaWFtb25kcyB0aGF0IGhhdmUgdmVyeSBoaWdoIGFuZCB2ZXJ5IGxvdyByZXNpZHVhbHMuIElzIHRoZXJlIGFueXRoaW5nIHVudXN1YWwgYWJvdXQgdGhlc2UgZGlhbW9uZHM/IEFyZSB0aGV5IHBhcnRpY3VsYXJseSBiYWQgb3IgZ29vZCwgb3IgZG8geW91IHRoaW5rIHRoZXNlIGFyZSBwcmljaW5nIGVycm9ycz8NCg0KYGBge3J9DQpkaWFtb25kczIgJT4lDQogIGZpbHRlcihhYnMobHJlc2lkMikgPiAxKSAlPiUNCiAgYWRkX3ByZWRpY3Rpb25zKG1vZF9kaWFtb25kMikgJT4lDQogIG11dGF0ZShwcmVkID0gcm91bmQoMl5wcmVkKSkgJT4lDQogIHNlbGVjdChwcmljZSwgcHJlZCwgY2FyYXQ6dGFibGUsIHg6eikgJT4lDQogIGFycmFuZ2UocHJpY2UpDQpgYGANCg0KSSBkaWQgbm90IHNlZSBhbnl0aGluZyB0b28gdW51c3VhbC4gRG8geW91Pw0KDQojIyMgNC4gRG9lcyB0aGUgZmluYWwgbW9kZWwsIGBtb2RfZGlhbW9uZHMyYCwgZG8gYSBnb29kIGpvYiBvZiBwcmVkaWN0aW5nIGRpYW1vbmQgcHJpY2VzPyBXb3VsZCB5b3UgdHJ1c3QgaXQgdG8gdGVsbCB5b3UgaG93IG11Y2ggdG8gc3BlbmQgaWYgeW91IHdlcmUgYnV5aW5nIGEgZGlhbW9uZD8NCg0KUGxvdHRpbmcgdGhlIHJlc2lkdWFscyBvZiB0aGUgbW9kZWwgc2hvd3MgdGhhdCB0aGVyZSBhcmUgc29tZSBsYXJnZSBvdXRsaWVycyBmb3Igc21hbGwgY2FyYXQgc2l6ZXMuIFRoZSBsYXJnZXN0IG9mIHRoZXNlIHJlc2lkdWFscyBhcmUgYSBsaXR0bGUgb3ZlciB0d28sIHdoaWNoIG1lYW5zIHRoYXQgdGhlIHRydWUgdmFsdWUgd2FzIGZvdXIgdGltZXMgbG93ZXIuIE1vc3Qgb2YgdGhlIG1hc3Mgb2YgdGhlIHJlc2lkdWFscyBpcyBiZXR3ZWVuIC0wLjUgYW5kIDAuNSwgd2hpY2ggY29ycmVzcG9uZHMgdG8gYWJvdXQgJFxwbSA0MCQuIFRoZXJlIHNlZW1zIHRvIGJlIGEgc2xpZ2h0IGRvd253YXJkIGJpYXMgaW4gdGhlIHJlc2lkdWFscyBhcyBjYXJhdCBzaXplIGluY3JlYXNlcy4NCg0KYGBge3J9DQpnZ3Bsb3QoZGlhbW9uZHMyLCBhZXMobGNhcmF0LCBscmVzaWQyKSkgKw0KICBnZW9tX2hleChiaW5zID0gNTApDQoNCg0KbHJlc2lkMl9zdW1tYXJ5IDwtIHN1bW1hcmlzZShkaWFtb25kczIsDQogIHJtc2UgPSBzcXJ0KG1lYW4obHJlc2lkMl4yKSksDQogIG1hZSA9IG1lYW4oYWJzKGxyZXNpZDIpKSwNCiAgcDAyNSA9IHF1YW50aWxlKGxyZXNpZDIsIDAuMDI1KSwNCiAgcDk3NSA9IHF1YW50aWxlKGxyZXNpZDIsIDAuOTc1KQ0KKQ0KbHJlc2lkMl9zdW1tYXJ5DQpgYGANCg0KV2hpbGUgaW4gc29tZSBjYXNlcyB0aGUgbW9kZWwgY2FuIGJlIHdyb25nLCBvdmVyYWxsIHRoZSBtb2RlbCBzZWVtcyB0byBwZXJmb3JtIHdlbGwuIFRoZSByb290IG1lYW4gc3F1YXJlZCBlcnJvciBpcyAwLjE5IG1lYW5pbmcgdGhhdCB0aGUgYXZlcmFnZSBlcnJvciBpcyBhYm91dCAtMTQlLiBBbm90aGVyIHN1bW1hcnkgc3RhdGlzdGljcyBvZiBlcnJvcnMgaXMgdGhlIG1lYW4gYWJzb2x1dGUgZXJyb3IgKE1BRSksIHdoaWNoIGlzIHRoZSBtZWFuIG9mIHRoZSBhYnNvbHV0ZSB2YWx1ZXMgb2YgdGhlIGVycm9ycy4gVGhlIE1BRSBpcyAwLjE1LCB3aGljaCBpcyAtMTElLiBGaW5hbGx5LCA5NSUgb2YgdGhlIHJlc2lkdWFscyBhcmUgYmV0d2VlbiAtMC4zNyBhbmQgMC4zOCwgd2hpY2ggY29ycmVzcG9uZCB0byAyM+KAlDMxLg0KDQpXaGV0aGVyIHlvdSB0aGluayB0aGF0IHRoaXMgaXMgYSBnb29kIG1vZGVsIGRlcGVuZHMgb24gZmFjdG9ycyBvdXRzaWRlIHRoZSBzdGF0aXN0aWNhbCBtb2RlbCBpdHNlbGYuIEl0IHdpbGwgZGVwZW5kIG9uIHRoZSBob3cgdGhlIG1vZGVsIGlzIGJlaW5nIHVzZWQuIEkgaGF2ZSBubyBpZGVhIGhvdyB0byBwcmljZSBkaWFtb25kcywgc28gdGhpcyB3b3VsZCBiZSB1c2VmdWwgdG8gbWUgaW4gb3JkZXIgdG8gdW5kZXJzdGFuZCBhIHJlYXNvbmFibGUgcHJpY2UgcmFuZ2UgZm9yIGEgZGlhbW9uZCwgc28gSSBkb27igJl0IGdldCByaXBwZWQgb2ZmLiBIb3dldmVyLCBpZiBJIHdlcmUgYnV5aW5nIGFuZCBzZWxsaW5nIGRpYW1vbmRzIGFzIGEgYnVzaW5lc3MsIEkgd291bGQgcHJvYmFibHkgcmVxdWlyZSBhIGJldHRlciBtb2RlbC4=