The ‘diamonds’ dataset is one of the datasets provided with the ggplot2 R package. We’re going to see if we can predict the price of a diamond based on its characteristics.

We will first conduct an EDA to get to know the data and analyze the impact of the different variables. We will then push the analysis further in order to build a linear model and use it to predict prices.

NOTE: This notebook contains two analyses condensed in one. While they definitely are related, there are a few points that need to be revised to help the reader follow the logic easily. This will be done in the near future, but we found that this notebook was interesting enough to be published in its raw form, waiting for revision.

Loading packages and dataset

library(ggplot2)
library(GGally)
library(scales)
library(memisc)
Loading required package: lattice
Loading required package: MASS

Attaching package: ‘memisc’

The following object is masked from ‘package:scales’:

    percent

The following objects are masked from ‘package:stats’:

    contr.sum, contr.treatment, contrasts

The following object is masked from ‘package:base’:

    as.array
library(RColorBrewer)
data("diamonds")

Univariate Analysis

General Information

Dimensions of the dataset:

dim(diamonds)
[1] 53940    10

Name of the variables:

str(diamonds)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   53940 obs. of  10 variables:
 $ carat  : num  0.23 0.21 0.23 0.29 0.31 0.24 0.24 0.26 0.22 0.23 ...
 $ cut    : Ord.factor w/ 5 levels "Fair"<"Good"<..: 5 4 2 4 2 3 3 3 1 3 ...
 $ color  : Ord.factor w/ 7 levels "D"<"E"<"F"<"G"<..: 2 2 2 6 7 7 6 5 2 5 ...
 $ clarity: Ord.factor w/ 8 levels "I1"<"SI2"<"SI1"<..: 2 3 5 4 2 6 7 3 4 5 ...
 $ depth  : num  61.5 59.8 56.9 62.4 63.3 62.8 62.3 61.9 65.1 59.4 ...
 $ table  : num  55 61 65 58 58 57 57 55 61 61 ...
 $ price  : int  326 326 327 334 335 336 336 337 337 338 ...
 $ x      : num  3.95 3.89 4.05 4.2 4.34 3.94 3.95 4.07 3.87 4 ...
 $ y      : num  3.98 3.84 4.07 4.23 4.35 3.96 3.98 4.11 3.78 4.05 ...
 $ z      : num  2.43 2.31 2.31 2.63 2.75 2.48 2.47 2.53 2.49 2.39 ...

Summary:

summary(diamonds)
     carat               cut        color        clarity          depth           table           price      
 Min.   :0.2000   Fair     : 1610   D: 6775   SI1    :13065   Min.   :43.00   Min.   :43.00   Min.   :  326  
 1st Qu.:0.4000   Good     : 4906   E: 9797   VS2    :12258   1st Qu.:61.00   1st Qu.:56.00   1st Qu.:  950  
 Median :0.7000   Very Good:12082   F: 9542   SI2    : 9194   Median :61.80   Median :57.00   Median : 2401  
 Mean   :0.7979   Premium  :13791   G:11292   VS1    : 8171   Mean   :61.75   Mean   :57.46   Mean   : 3933  
 3rd Qu.:1.0400   Ideal    :21551   H: 8304   VVS2   : 5066   3rd Qu.:62.50   3rd Qu.:59.00   3rd Qu.: 5324  
 Max.   :5.0100                     I: 5422   VVS1   : 3655   Max.   :79.00   Max.   :95.00   Max.   :18823  
                                    J: 2808   (Other): 2531                                                  
       x                y                z         
 Min.   : 0.000   Min.   : 0.000   Min.   : 0.000  
 1st Qu.: 4.710   1st Qu.: 4.720   1st Qu.: 2.910  
 Median : 5.700   Median : 5.710   Median : 3.530  
 Mean   : 5.731   Mean   : 5.735   Mean   : 3.539  
 3rd Qu.: 6.540   3rd Qu.: 6.540   3rd Qu.: 4.040  
 Max.   :10.740   Max.   :58.900   Max.   :31.800  
                                                   

Levels of our cateogricalvariables:

levels(diamonds$cut)
[1] "Fair"      "Good"      "Very Good" "Premium"   "Ideal"    
levels(diamonds$color)
[1] "D" "E" "F" "G" "H" "I" "J"
levels(diamonds$clarity)
[1] "I1"   "SI2"  "SI1"  "VS2"  "VS1"  "VVS2" "VVS1" "IF"  

Univariate analysis

Let’s jump right into it and focus our analysis on price.

Price histogram

qplot(data = diamonds, x = price)

summary(diamonds$price)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    326     950    2401    3933    5324   18823 

The distribution of diamonds prices is clearly right-skewed.

Diamond price detail

Let’s get some numbers:

sum(diamonds$price < 500)
[1] 1729
sum(diamonds$price < 250)
[1] 0
sum(diamonds$price >= 15000)
[1] 1656

Our dataset contains: - 1729 diamonds with a price below $500 - 0 diamonds with a price below $250 - 15,000 diamonds with a price equal to or above $15,000

Histogram - cheaper diamonds

Let’s get a look at the cheapest diamonds:

qplot(data = diamonds, x = price,
      binwidth = 20) +
  scale_x_continuous(limits = c(0, 1500), breaks = seq(0, 1500, 100))

Mode <- function(x) {
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}
Mode(diamonds$price)
[1] 605

The mode of the cheapest diamonds (with a price between $0 and $1,500) is 605.

Bivariate analysis

Faceting - Histogram of diamond prices by cut

Let’s facet our prices by the quality of the cut.

Scaling - Histogram of diamond prices by cut

qplot(data = diamonds, x = price) +
  facet_wrap(~cut, ncol= 2, scales = 'free_y')

by(diamonds$price, diamonds$cut, summary)
diamonds$cut: Fair
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    337    2050    3282    4359    5206   18574 
--------------------------------------------------------------------------------------------- 
diamonds$cut: Good
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    327    1145    3050    3929    5028   18788 
--------------------------------------------------------------------------------------------- 
diamonds$cut: Very Good
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    336     912    2648    3982    5373   18818 
--------------------------------------------------------------------------------------------- 
diamonds$cut: Premium
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    326    1046    3185    4584    6296   18823 
--------------------------------------------------------------------------------------------- 
diamonds$cut: Ideal
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    326     878    1810    3458    4678   18806 

Faceting - Histogram of price per carat by cut

qplot(data = diamonds, x = price / carat, binwidth = 0.1) +
  facet_wrap(~cut) +
  scale_x_log10()

The price per carat definitely seems to increase with the quality of the cut.

Price boxplots and statistics

qplot(data = diamonds, x = clarity, y = price, geom = 'boxplot')

by(diamonds$price, diamonds$clarity, summary)
diamonds$clarity: I1
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    345    2080    3344    3924    5161   18531 
--------------------------------------------------------------------------------------------- 
diamonds$clarity: SI2
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    326    2264    4072    5063    5777   18804 
--------------------------------------------------------------------------------------------- 
diamonds$clarity: SI1
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    326    1089    2822    3996    5250   18818 
--------------------------------------------------------------------------------------------- 
diamonds$clarity: VS2
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    334     900    2054    3925    6024   18823 
--------------------------------------------------------------------------------------------- 
diamonds$clarity: VS1
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    327     876    2005    3839    6023   18795 
--------------------------------------------------------------------------------------------- 
diamonds$clarity: VVS2
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
  336.0   794.2  1311.0  3283.7  3638.2 18768.0 
--------------------------------------------------------------------------------------------- 
diamonds$clarity: VVS1
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    336     816    1093    2523    2379   18777 
--------------------------------------------------------------------------------------------- 
diamonds$clarity: IF
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    369     895    1080    2865    2388   18806 

Color boxplots and statistics

qplot(data = diamonds, x = color, y = price, geom = 'boxplot')

by(diamonds$price, diamonds$color, summary)
diamonds$color: D
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    357     911    1838    3170    4214   18693 
--------------------------------------------------------------------------------------------- 
diamonds$color: E
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    326     882    1739    3077    4003   18731 
--------------------------------------------------------------------------------------------- 
diamonds$color: F
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    342     982    2344    3725    4868   18791 
--------------------------------------------------------------------------------------------- 
diamonds$color: G
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    354     931    2242    3999    6048   18818 
--------------------------------------------------------------------------------------------- 
diamonds$color: H
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    337     984    3460    4487    5980   18803 
--------------------------------------------------------------------------------------------- 
diamonds$color: I
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    334    1120    3730    5092    7202   18823 
--------------------------------------------------------------------------------------------- 
diamonds$color: J
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
    335    1860    4234    5324    7695   18710 

Faceting - Boxplots of price per color by cut

qplot(data = diamonds, x = color, y = price / carat, geom = 'boxplot') +
  coord_cartesian(ylim = c(250, 6000))

Frequency polygon - Carat

qplot(data = diamonds, x = carat, binwidth = 0.01, geom = 'freqpoly') +
  scale_x_continuous(limits = c(0, 1.5), breaks = seq(0, 1.5, 0.1))

Scatter plot - Price vs x

ggplot(aes(x = x , y = price), data = diamonds) +
  geom_jitter(alpha= 1/20) +
  xlim(3, 9)

Correlation between Price and x, y and z

cor.test(diamonds$price, diamonds$x)

    Pearson's product-moment correlation

data:  diamonds$price and diamonds$x
t = 440.16, df = 53938, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.8825835 0.8862594
sample estimates:
      cor 
0.8844352 
cor.test(diamonds$price, diamonds$y)

    Pearson's product-moment correlation

data:  diamonds$price and diamonds$y
t = 401.14, df = 53938, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.8632867 0.8675241
sample estimates:
      cor 
0.8654209 
cor.test(diamonds$price, diamonds$z)

    Pearson's product-moment correlation

data:  diamonds$price and diamonds$z
t = 393.6, df = 53938, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.8590541 0.8634131
sample estimates:
      cor 
0.8612494 

Scatter plot of price vs depth

ggplot(aes(x = depth, y = price), data = diamonds) +
  geom_point(alpha = 0.05,
             position = position_jitter(h = 0),
             color = 'orange')

Correlation between Price and depth

cor.test(diamonds$price, diamonds$depth)

    Pearson's product-moment correlation

data:  diamonds$price and diamonds$depth
t = -2.473, df = 53938, p-value = 0.0134
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 -0.019084756 -0.002208537
sample estimates:
       cor 
-0.0106474 

Scatter plot of price vs carat omitting the top 1% percentile

ggplot(aes(x = carat, y = price), data = diamonds) +
  geom_point() + 
  xlim(0, quantile(diamonds$carat, 0.99)) +
  ylim(0, quantile(diamonds$price, 0.99))

Scatter plot of price vs volume (x * y * z)

diamonds$volume <- diamonds$x * diamonds$y * diamonds$z
ggplot(aes(x = volume, y = price), data = diamonds) +
  geom_point()

Correlation between price and volume

with(subset(diamonds, volume > 0 & volume < 800), cor(price, volume))
[1] 0.9235455

Adjustment - price vs volume

ggplot(aes(x = volume, y = price), data = subset(diamonds, volume > 0 & volume < 800)) +
  geom_point(alpha = 1/20) +
  geom_smooth(method = 'lm', color = 'red')

Mean Price by Clarity

library(dplyr)
package ‘dplyr’ was built under R version 3.4.1
Attaching package: ‘dplyr’

The following objects are masked from ‘package:memisc’:

    collect, recode, rename

The following object is masked from ‘package:MASS’:

    select

The following object is masked from ‘package:GGally’:

    nasa

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
diamondsByClarity <- diamonds %>%
  group_by(clarity) %>%
  summarize(mean_price = mean(price),
            median_price = median(price),
            min_price = min(price),
            max_price = max(price),
            n = n()) %>%
  arrange(clarity)
head(diamondsByClarity)

Bar Charts of Mean Price

diamonds_by_clarity <- group_by(diamonds, clarity)
diamonds_mp_by_clarity <- summarise(diamonds_by_clarity, mean_price = mean(price))
diamonds_by_color <- group_by(diamonds, color)
diamonds_mp_by_color <- summarise(diamonds_by_color, mean_price = mean(price))
p1 <- ggplot(aes(x = clarity, y = mean_price), data = diamonds_mp_by_clarity) +
  geom_bar(stat = 'identity')
p2 <- ggplot(aes(x = color, y = mean_price), data = diamonds_mp_by_color) +
  geom_bar(stat = 'identity')
library(gridExtra)

Attaching package: ‘gridExtra’

The following object is masked from ‘package:dplyr’:

    combine
grid.arrange(p1, p2, ncol = 1)

There’s something very odd here. It goes against the intuition that price goes down when color and clarity are better. Maybe there’s another variable influencing this behavior. Let’s look at the influence of the cut.

diamonds_by_cut <- group_by(diamonds, cut)
diamonds_mp_by_cut <- summarise(diamonds_by_cut, mean_price = mean(price))
p3 <- ggplot(aes(x = cut, y = mean_price), data = diamonds_mp_by_cut) +
  geom_bar(stat = 'identity')
grid.arrange(p1, p2, p3, ncol = 1)

Predictions

Scatterplot Review

library(ggplot2)
data(diamonds)
ggplot(aes(x = carat, y = price), data = diamonds) +
  geom_point(fill = I('#F79420'), color = I('black'), shape = 21) +
  xlim(0, quantile(diamonds$carat, .99)) +
  ylim(0, quantile(diamonds$price, .99))

Price and Carat Relationship

  • We can see a non-linear relationship. Maybe it’s exponential, maybe it’s something else.
  • We can see that the dispersion or variance also increases while carat size increases.

Add on a Linear Model

ggplot(aes(x = carat, y = price), data = diamonds) +
  geom_point(color = '#F79420', alpha = 1/4) +
  stat_smooth(method = 'lm') +
  scale_x_continuous(lim = c(0, quantile(diamonds$carat, 0.99))) +
  scale_y_continuous(lim = c(0, quantile(diamonds$price, 0.99)))

ggpairs Function

# sample 10,000 diamonds from the data set
set.seed(20022012)
diamond_samp <- diamonds[sample(1:length(diamonds$price), 10000), ]
ggpairs(diamond_samp,
        axisLabels = 'internal',
        lower = list(continuous = wrap('points', shape = I('.'))),
        upper = list(combo = wrap('box', outlier.shape = I('.'))))

 plot: [1,1] [=----------------------------------------------------------------------------------------------]  1% est: 0s 
 plot: [1,2] [==---------------------------------------------------------------------------------------------]  2% est:12s 
 plot: [1,3] [===--------------------------------------------------------------------------------------------]  3% est:25s 
 plot: [1,4] [====-------------------------------------------------------------------------------------------]  4% est:28s 
 plot: [1,5] [=====------------------------------------------------------------------------------------------]  5% est:31s 
 plot: [1,6] [======-----------------------------------------------------------------------------------------]  6% est:27s 
 plot: [1,7] [=======----------------------------------------------------------------------------------------]  7% est:25s 
 plot: [1,8] [========---------------------------------------------------------------------------------------]  8% est:22s 
 plot: [1,9] [=========--------------------------------------------------------------------------------------]  9% est:20s 
 plot: [1,10] [=========-------------------------------------------------------------------------------------] 10% est:19s 
 plot: [2,1] [==========-------------------------------------------------------------------------------------] 11% est:18s 
 plot: [2,2] [===========------------------------------------------------------------------------------------] 12% est:19s 
 plot: [2,3] [============-----------------------------------------------------------------------------------] 13% est:18s 
 plot: [2,4] [=============----------------------------------------------------------------------------------] 14% est:19s 
 plot: [2,5] [==============---------------------------------------------------------------------------------] 15% est:20s 
 plot: [2,6] [===============--------------------------------------------------------------------------------] 16% est:20s 
 plot: [2,7] [================-------------------------------------------------------------------------------] 17% est:20s 
 plot: [2,8] [=================------------------------------------------------------------------------------] 18% est:21s 
 plot: [2,9] [==================-----------------------------------------------------------------------------] 19% est:21s 
 plot: [2,10] [===================---------------------------------------------------------------------------] 20% est:21s 
 plot: [3,1] [====================---------------------------------------------------------------------------] 21% est:21s 
 plot: [3,2] [=====================--------------------------------------------------------------------------] 22% est:22s 
 plot: [3,3] [======================-------------------------------------------------------------------------] 23% est:22s 
 plot: [3,4] [=======================------------------------------------------------------------------------] 24% est:21s 
 plot: [3,5] [========================-----------------------------------------------------------------------] 25% est:22s 
 plot: [3,6] [=========================----------------------------------------------------------------------] 26% est:22s 
 plot: [3,7] [==========================---------------------------------------------------------------------] 27% est:22s 
 plot: [3,8] [===========================--------------------------------------------------------------------] 28% est:23s 
 plot: [3,9] [============================-------------------------------------------------------------------] 29% est:23s 
 plot: [3,10] [============================------------------------------------------------------------------] 30% est:23s 
 plot: [4,1] [=============================------------------------------------------------------------------] 31% est:23s 
 plot: [4,2] [==============================-----------------------------------------------------------------] 32% est:24s 
 plot: [4,3] [===============================----------------------------------------------------------------] 33% est:24s 
 plot: [4,4] [================================---------------------------------------------------------------] 34% est:25s 
 plot: [4,5] [=================================--------------------------------------------------------------] 35% est:25s 
 plot: [4,6] [==================================-------------------------------------------------------------] 36% est:25s 
 plot: [4,7] [===================================------------------------------------------------------------] 37% est:26s 
 plot: [4,8] [====================================-----------------------------------------------------------] 38% est:26s 
 plot: [4,9] [=====================================----------------------------------------------------------] 39% est:26s 
 plot: [4,10] [======================================--------------------------------------------------------] 40% est:26s 
 plot: [5,1] [=======================================--------------------------------------------------------] 41% est:26s 
 plot: [5,2] [========================================-------------------------------------------------------] 42% est:25s 
 plot: [5,3] [=========================================------------------------------------------------------] 43% est:24s 
 plot: [5,4] [==========================================-----------------------------------------------------] 44% est:24s 
 plot: [5,5] [===========================================----------------------------------------------------] 45% est:24s 
 plot: [5,6] [============================================---------------------------------------------------] 46% est:23s 
 plot: [5,7] [=============================================--------------------------------------------------] 47% est:22s 
 plot: [5,8] [==============================================-------------------------------------------------] 48% est:21s 
 plot: [5,9] [===============================================------------------------------------------------] 49% est:21s 
 plot: [5,10] [===============================================-----------------------------------------------] 50% est:20s 
 plot: [6,1] [================================================-----------------------------------------------] 51% est:19s 
 plot: [6,2] [=================================================----------------------------------------------] 52% est:19s 
 plot: [6,3] [==================================================---------------------------------------------] 53% est:18s 
 plot: [6,4] [===================================================--------------------------------------------] 54% est:18s 
 plot: [6,5] [====================================================-------------------------------------------] 55% est:18s 
 plot: [6,6] [=====================================================------------------------------------------] 56% est:17s 
 plot: [6,7] [======================================================-----------------------------------------] 57% est:16s 
 plot: [6,8] [=======================================================----------------------------------------] 58% est:16s 
 plot: [6,9] [========================================================---------------------------------------] 59% est:15s 
 plot: [6,10] [========================================================--------------------------------------] 60% est:15s 
 plot: [7,1] [==========================================================-------------------------------------] 61% est:14s 
 plot: [7,2] [===========================================================------------------------------------] 62% est:14s 
 plot: [7,3] [============================================================-----------------------------------] 63% est:13s 
 plot: [7,4] [=============================================================----------------------------------] 64% est:13s 
 plot: [7,5] [==============================================================---------------------------------] 65% est:13s 
 plot: [7,6] [===============================================================--------------------------------] 66% est:12s 
 plot: [7,7] [================================================================-------------------------------] 67% est:12s 
 plot: [7,8] [=================================================================------------------------------] 68% est:11s 
 plot: [7,9] [==================================================================-----------------------------] 69% est:11s 
 plot: [7,10] [==================================================================----------------------------] 70% est:10s 
 plot: [8,1] [===================================================================----------------------------] 71% est:10s 
 plot: [8,2] [====================================================================---------------------------] 72% est: 9s 
 plot: [8,3] [=====================================================================--------------------------] 73% est: 9s 
 plot: [8,4] [======================================================================-------------------------] 74% est: 9s 
 plot: [8,5] [=======================================================================------------------------] 75% est: 9s 
 plot: [8,6] [========================================================================-----------------------] 76% est: 8s 
 plot: [8,7] [=========================================================================----------------------] 77% est: 8s 
 plot: [8,8] [==========================================================================---------------------] 78% est: 7s 
 plot: [8,9] [===========================================================================--------------------] 79% est: 7s 
 plot: [8,10] [===========================================================================-------------------] 80% est: 7s 
 plot: [9,1] [=============================================================================------------------] 81% est: 6s 
 plot: [9,2] [==============================================================================-----------------] 82% est: 6s 
 plot: [9,3] [===============================================================================----------------] 83% est: 5s 
 plot: [9,4] [================================================================================---------------] 84% est: 5s 
 plot: [9,5] [=================================================================================--------------] 85% est: 5s 
 plot: [9,6] [==================================================================================-------------] 86% est: 5s 
 plot: [9,7] [===================================================================================------------] 87% est: 4s 
 plot: [9,8] [====================================================================================-----------] 88% est: 4s 
 plot: [9,9] [=====================================================================================----------] 89% est: 3s 
 plot: [9,10] [=====================================================================================---------] 90% est: 3s 
 plot: [10,1] [======================================================================================--------] 91% est: 3s 
 plot: [10,2] [======================================================================================--------] 92% est: 2s 
 plot: [10,3] [=======================================================================================-------] 93% est: 2s 
 plot: [10,4] [========================================================================================------] 94% est: 2s 
 plot: [10,5] [=========================================================================================-----] 95% est: 2s 
 plot: [10,6] [==========================================================================================----] 96% est: 1s 
 plot: [10,7] [===========================================================================================---] 97% est: 1s 
 plot: [10,8] [============================================================================================--] 98% est: 1s 
 plot: [10,9] [=============================================================================================-] 99% est: 0s 
 plot: [10,10] [=============================================================================================]100% est: 0s 
                                                                                                                           

Here is what we can see from the matrice. In the lower triangle, Ggplot uses: - grouped histograms for qualitative / qualitative pairs - scatter plots for quantitative / quantitative pairs

In the upper triangle, Ggplot uses: - grouped histograms for qualitative / qualitative pairs, this time using the x instead of the y variable as the grouping factor - box plots for qualitative / quantitative pairs - correlation coefficients for quantitative / quantitative pairs

We can see what might be relationship between price and clarity and price and color, which we will keep in mind for later when we’ll start modeling our data.

The critical factor driving price is the size, or the carat weight of the diamond. As we saw before, the relationship between price and diamond size is nonlinear. What might explain this pattern? On the supply side, larger continuous chunks of diamonds without significant flaws are probably harder to find than smaller ones. This might explain this sort of exponential looking curve.

This is related to the fact that the weight of a diamond is a function of volume, and volume is a function of the length times the width times the height of a diamond. This suggests that we might be especially interested in the cube root of carat wieght.

It’s often the case that leveraging substantive knowledge about your data like this can lead to especially fruitful transformations.

The Demand of Diamonds

On the demand side, customers in the market for a less expensive, smaller diamond are probably more sensitive to price than more well-to-do buyers. Many less than one carat customers would surely never buy a diamond, were it not for the social norm of presenting one when proposing.

There are fewer customers who can afford a bigger diamond that is larger than one carat, hence we shouldn’t expect the market for bigger diamonds to be as competitive as the one for smaller diamonds. So it makes sense that the variance as well as the price would increase with carat size.

Often, the distribution of any monetary variable like dollars will be highly skewed and vary over orders of magnitude. This can result from path dependence (the rich getting richer), or multiplicative processes like year on year inflation, or some combination of both.

Hence it’s a good idea to look into compressing any such variable by putting it on a log scale.

library(gridExtra)
plot1 <- qplot(data = diamonds, x = price, binwidth = 100, fill = I('#099DD9')) + 
  ggtitle('Price')
plot2 <- qplot(data = diamonds, x = price, binwidth = .01, fill = I('#F79420')) +
  ggtitle('Price (log10)') +
  scale_x_log10()
grid.arrange(plot1, plot2)

Connecting Demand and Price Distributions

We can see that the prices for diamonds are pretty heavily skewed.

But when we put thos prices on a log10 scale, they seem much better behaved: they are much closer to the bell curve of a normal distribution. We can even see a little bit of evidence of bimodality on this log10 scale, which is consistent with our two class rich buyer and poor buyer speculation about the nature of customers for diamonds.

Scatterplot Transformation

qplot(carat, price, data = diamonds) +
  scale_y_continuous(trans = log10_trans()) +
  ggtitle('Price (log10) by Carat')

This plot looks better than before. On the log 10 scale, the prices look less dispersed at the high end of Carat size and price, but we can do better.

We’re trying to use the cube root of Carat in light of our speculation about flaws being exponentially more likely in diamonds with more volume (volume is on a cubic scale).

First, we need a function to transform the Carat variable.

Create a new function to transform the carat variable

cuberoot_trans = function() trans_new('cuberoot', transform = function(x) x^(1/3),
                                      inverse = function(x) x^3)

Use the cuberoot_trans function

ggplot(aes(carat, price), data = diamonds) + 
  geom_point() + 
  scale_x_continuous(trans = cuberoot_trans(), limits = c(0.2, 3),
                     breaks = c(0.2, 0.5, 1, 2, 3)) + 
  scale_y_continuous(trans = log10_trans(), limits = c(350, 15000),
                     breaks = c(350, 1000, 5000, 10000, 15000)) +
  ggtitle('Price (log10) by Cube-Root of Carat')

We can see that with these transformations we used to get our data on this scale, things look almost linear; We can now move forward and see about modelling our data using just a linear model.

Overplotting Revisited

So far, we haven’t done anything about overplotting (when multiple points take on the same value).

head(sort(table(diamonds$carat), decreasing = T))

 0.3 0.31 1.01  0.7 0.32    1 
2604 2249 2242 1981 1840 1558 
head(sort(table(diamonds$price), decreasing = T))

605 802 625 828 776 698 
132 127 126 125 124 121 

As we can see, we have a vast amount of points at the same price, which will result in some serious overplotting. This can obscure the density and the sparsity of our data at really key points. We can deal with this by making our points smaller, jittering them, and adding transparency.

ggplot(aes(carat, price), data = diamonds) + 
  geom_point(alpha = 0.5, size = 0.75, position = 'jitter') +
  scale_x_continuous(trans = cuberoot_trans(), limits = c(0.2, 3),
                     breaks = c(0.2, 0.5, 1, 2, 3)) + 
  scale_y_continuous(trans = log10_trans(), limits = c(350, 15000),
                     breaks = c(350, 1000, 5000, 10000, 15000)) +
  ggtitle('Price (log10) by Cube-Root of Carat')

Other Qualitative Factors

We can see what looks like an almost linear relationship between carat weight and price after doing some transformations. But surely there are other factors that must influence the price of a diamond.

Clarity seem to factor into price. However, many consumers are looking for a diamond of a minimum size, so we shouldn’t expect clarity to be as strong a factor as carat weight.

According to Blue Nile, the cut of a diamond has a much more consequential impact on that fiery quality that jewelers describe when they talk about diamonds.

Many of the imperfections on clarity are microscopic and do not affect the diamonds beauty in any discernible way.

Let’s see if clarity, cut or color can explain some of the variants in price when we visualize it on our plot using color.

Price vs. Carat and Clarity

ggplot(aes(x = carat, y = price, color = clarity), data = diamonds) + 
  geom_point(alpha = 0.5, size = 1, position = 'jitter') +
  scale_color_brewer(type = 'div',
    guide = guide_legend(title = 'Clarity', reverse = T,
    override.aes = list(alpha = 1, size = 2))) +  
  scale_x_continuous(trans = cuberoot_trans(), limits = c(0.2, 3),
    breaks = c(0.2, 0.5, 1, 2, 3)) + 
  scale_y_continuous(trans = log10_trans(), limits = c(350, 15000),
    breaks = c(350, 1000, 5000, 10000, 15000)) +
  ggtitle('Price (log10) by Cube-Root of Carat and Clarity')

Clarity and Price

Clarity does seem to explain an awful lot of the remaining variance in price, after adding color to our plot. Holding carat weight constant, looking at one part of the plot, we the diamonds with lower clarity are almost always cheaper than diamonds with better clarity.

Price vs. Carat and Cut

Alter the code below.

ggplot(aes(x = carat, y = price, color = cut), data = diamonds) + 
  geom_point(alpha = 0.5, size = 1, position = 'jitter') +
  scale_color_brewer(type = 'div',
                     guide = guide_legend(title = 'Cut', reverse = T,
                                          override.aes = list(alpha = 1, size = 2))) +  
  scale_x_continuous(trans = cuberoot_trans(), limits = c(0.2, 3),
                     breaks = c(0.2, 0.5, 1, 2, 3)) + 
  scale_y_continuous(trans = log10_trans(), limits = c(350, 15000),
                     breaks = c(350, 1000, 5000, 10000, 15000)) +
  ggtitle('Price (log10) by Cube-Root of Carat and Cut')

Cut and Price

Despite what Blue Nile says, we don’t see much variation on the cut. Most of the diamonds in the data are ideal cut anyway, so we’ve lost the color pattern that we saw before.

Price vs. Carat and Color

Alter the code below.

ggplot(aes(x = carat, y = price, color = color), data = diamonds) + 
  geom_point(alpha = 0.5, size = 1, position = 'jitter') +
  scale_color_brewer(type = 'div',
                     guide = guide_legend(title = 'Color', reverse = FALSE,
                                          override.aes = list(alpha = 1, size = 2))) +  
  scale_x_continuous(trans = cuberoot_trans(), limits = c(0.2, 3),
                     breaks = c(0.2, 0.5, 1, 2, 3)) + 
  scale_y_continuous(trans = log10_trans(), limits = c(350, 15000),
                     breaks = c(350, 1000, 5000, 10000, 15000)) +
  ggtitle('Price (log10) by Cube-Root of Carat and Color')

Color and Price

Color does seem to explain some of the variance in price, just like the clarity variable. Blue Nile however states that the difference between all color grades from D to J are basically not noticeable to the naked eye. Yet, we do see the color difference in the price tag.

Linear Models in R

In R, we can create models using the lm function. lm(y~x) where y is the outcome variable and x the explanatory variable.

Here, we would use: log10(price) ~ carat^(1/3) Price is the outcome and carat the predictor variable. Using our domain specific knowledge of diamonds and carat weight, we know we must take the cube root of carat weight (volume).

We apply a log transformation to our long tailed dollar variable, and we speculate that the flawless diamond should become exponentially rarer as the volume increases.

Building the Linear Model

m1 <- lm(I(log(price)) ~ I(carat^(1/3)), data = diamonds)
m2 <- update(m1, ~ . + carat)
m3 <- update(m2, ~ . + cut)
m4 <- update(m3, ~ . + color)
m5 <- update(m4, ~ . + clarity)
mtable(m1, m2, m3, m4, m5, sdigits = 3)

Calls:
m1: lm(formula = I(log(price)) ~ I(carat^(1/3)), data = diamonds)
m2: lm(formula = I(log(price)) ~ I(carat^(1/3)) + carat, data = diamonds)
m3: lm(formula = I(log(price)) ~ I(carat^(1/3)) + carat + cut, data = diamonds)
m4: lm(formula = I(log(price)) ~ I(carat^(1/3)) + carat + cut + color, 
    data = diamonds)
m5: lm(formula = I(log(price)) ~ I(carat^(1/3)) + carat + cut + color + 
    clarity, data = diamonds)

=============================================================================
                      m1          m2          m3         m4          m5      
-----------------------------------------------------------------------------
  (Intercept)      2.821***    1.039***    0.874***    0.932***   0.415***   
                  (0.006)     (0.019)     (0.019)     (0.017)    (0.010)     
  I(carat^(1/3))   5.558***    8.568***    8.703***    8.438***   9.144***   
                  (0.007)     (0.032)     (0.031)     (0.028)    (0.016)     
  carat                       -1.137***   -1.163***   -0.992***  -1.093***   
                              (0.012)     (0.011)     (0.010)    (0.006)     
  cut: .L                                  0.224***    0.224***   0.120***   
                                          (0.004)     (0.004)    (0.002)     
  cut: .Q                                 -0.062***   -0.062***  -0.031***   
                                          (0.004)     (0.003)    (0.002)     
  cut: .C                                  0.051***    0.052***   0.014***   
                                          (0.003)     (0.003)    (0.002)     
  cut: ^4                                  0.018***    0.018***  -0.002      
                                          (0.003)     (0.002)    (0.001)     
  color: .L                                           -0.373***  -0.441***   
                                                      (0.003)    (0.002)     
  color: .Q                                           -0.129***  -0.093***   
                                                      (0.003)    (0.002)     
  color: .C                                            0.001     -0.013***   
                                                      (0.003)    (0.002)     
  color: ^4                                            0.029***   0.012***   
                                                      (0.003)    (0.002)     
  color: ^5                                           -0.016***  -0.003*     
                                                      (0.003)    (0.001)     
  color: ^6                                           -0.023***   0.001      
                                                      (0.002)    (0.001)     
  clarity: .L                                                     0.907***   
                                                                 (0.003)     
  clarity: .Q                                                    -0.240***   
                                                                 (0.003)     
  clarity: .C                                                     0.131***   
                                                                 (0.003)     
  clarity: ^4                                                    -0.063***   
                                                                 (0.002)     
  clarity: ^5                                                     0.026***   
                                                                 (0.002)     
  clarity: ^6                                                    -0.002      
                                                                 (0.002)     
  clarity: ^7                                                     0.032***   
                                                                 (0.001)     
-----------------------------------------------------------------------------
  R-squared            0.924       0.935       0.939      0.951       0.984  
  adj. R-squared       0.924       0.935       0.939      0.951       0.984  
  sigma                0.280       0.259       0.250      0.224       0.129  
  F               652012.063  387489.366  138654.523  87959.467  173791.084  
  p                    0.000       0.000       0.000      0.000       0.000  
  Log-likelihood   -7962.499   -3631.319   -1837.416   4235.240   34091.272  
  Deviance          4242.831    3613.360    3380.837   2699.212     892.214  
  AIC              15930.999    7270.637    3690.832  -8442.481  -68140.544  
  BIC              15957.685    7306.220    3761.997  -8317.942  -67953.736  
  N                53940       53940       53940      53940       53940      
=============================================================================

Notice how adding cut to our model does not help explain much of the variance in the price of diamonds. This fits with out exploration earlier.

Our model is: lm(price) = 0.415 + 9.144 x carat^(1/3) - 1.093 x carat + … x cut + … x color + … x clarity) + E

E being the error term

Model Problems

Our data is from 2008. We need to account for inflation, and the diamond market is quite different now than it was. Prices plummeted in 2008 due to the global financial crisis. Since then prices, at least for wholesale polished diamonds, have grown at about 6% per year, compound annual rate.

The rapidly growing number of couples in China buying diamond engagement rings might also explain this increase.

Finally, diamonds prices grew unevenly across different carat sizes since 2008, meaning the model we estimated couldn’t simply be adjusted by inflation.

LS0tCnRpdGxlOiAiRGlhbW9uZHMgUHJpY2UgRURBIGFuZCBQcmVkaWN0aW9uIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpUaGUgJ2RpYW1vbmRzJyBkYXRhc2V0IGlzIG9uZSBvZiB0aGUgZGF0YXNldHMgcHJvdmlkZWQgd2l0aCB0aGUgZ2dwbG90MiBSCnBhY2thZ2UuIFdlJ3JlIGdvaW5nIHRvIHNlZSBpZiB3ZSBjYW4gcHJlZGljdCB0aGUgcHJpY2Ugb2YgYSBkaWFtb25kIGJhc2VkIG9uCml0cyBjaGFyYWN0ZXJpc3RpY3MuCgpXZSB3aWxsIGZpcnN0IGNvbmR1Y3QgYW4gRURBIHRvIGdldCB0byBrbm93IHRoZSBkYXRhIGFuZCBhbmFseXplIHRoZSBpbXBhY3Qgb2YKdGhlIGRpZmZlcmVudCB2YXJpYWJsZXMuIFdlIHdpbGwgdGhlbiBwdXNoIHRoZSBhbmFseXNpcyBmdXJ0aGVyIGluIG9yZGVyIHRvCmJ1aWxkIGEgbGluZWFyIG1vZGVsIGFuZCB1c2UgaXQgdG8gcHJlZGljdCBwcmljZXMuCgpOT1RFOiBUaGlzIG5vdGVib29rIGNvbnRhaW5zIHR3byBhbmFseXNlcyBjb25kZW5zZWQgaW4gb25lLiBXaGlsZSB0aGV5CmRlZmluaXRlbHkgYXJlIHJlbGF0ZWQsIHRoZXJlIGFyZSBhIGZldyBwb2ludHMgdGhhdCBuZWVkIHRvIGJlIHJldmlzZWQgdG8gaGVscAp0aGUgcmVhZGVyIGZvbGxvdyB0aGUgbG9naWMgZWFzaWx5LiBUaGlzIHdpbGwgYmUgZG9uZSBpbiB0aGUgbmVhciBmdXR1cmUsIGJ1dAp3ZSBmb3VuZCB0aGF0IHRoaXMgbm90ZWJvb2sgd2FzIGludGVyZXN0aW5nIGVub3VnaCB0byBiZSBwdWJsaXNoZWQgaW4gaXRzIHJhdwpmb3JtLCB3YWl0aW5nIGZvciByZXZpc2lvbi4KCiMgTG9hZGluZyBwYWNrYWdlcyBhbmQgZGF0YXNldAoKYGBge3IgTG9hZGluZyBwYWNrYWdlcyBhbmQgZGF0YXNldCwgd2FybmluZz1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KEdHYWxseSkKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkobWVtaXNjKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKCmRhdGEoImRpYW1vbmRzIikKYGBgCgojIFVuaXZhcmlhdGUgQW5hbHlzaXMKCiMjIEdlbmVyYWwgSW5mb3JtYXRpb24KCkRpbWVuc2lvbnMgb2YgdGhlIGRhdGFzZXQ6CmBgYHtyIERpbWVuc2lvbnN9CmRpbShkaWFtb25kcykKYGBgCgpOYW1lIG9mIHRoZSB2YXJpYWJsZXM6CmBgYHtyIE5hbWUgb2YgdGhlIHZhcmlhYmxlc30Kc3RyKGRpYW1vbmRzKQpgYGAKClN1bW1hcnk6CmBgYHtyIFN1bW1hcnl9CnN1bW1hcnkoZGlhbW9uZHMpCmBgYAoKTGV2ZWxzIG9mIG91ciBjYXRlb2dyaWNhbHZhcmlhYmxlczoKYGBge3IgTGV2ZWxzfQpsZXZlbHMoZGlhbW9uZHMkY3V0KQpsZXZlbHMoZGlhbW9uZHMkY29sb3IpCmxldmVscyhkaWFtb25kcyRjbGFyaXR5KQpgYGAKCiMgVW5pdmFyaWF0ZSBhbmFseXNpcwoKTGV0J3MganVtcCByaWdodCBpbnRvIGl0IGFuZCBmb2N1cyBvdXIgYW5hbHlzaXMgb24gcHJpY2UuCgojIyBQcmljZSBoaXN0b2dyYW0KYGBge3IgUHJpY2UgaGlzdG9ncmFtfQpxcGxvdChkYXRhID0gZGlhbW9uZHMsIHggPSBwcmljZSkKc3VtbWFyeShkaWFtb25kcyRwcmljZSkKYGBgCgpUaGUgZGlzdHJpYnV0aW9uIG9mIGRpYW1vbmRzIHByaWNlcyBpcyBjbGVhcmx5IHJpZ2h0LXNrZXdlZC4KCgojIyBEaWFtb25kIHByaWNlIGRldGFpbAoKTGV0J3MgZ2V0IHNvbWUgbnVtYmVyczoKCmBgYHtyICMgRGlhbW9uZCBwcmljZSBkZXRhaWx9CnN1bShkaWFtb25kcyRwcmljZSA8IDUwMCkKc3VtKGRpYW1vbmRzJHByaWNlIDwgMjUwKQpzdW0oZGlhbW9uZHMkcHJpY2UgPj0gMTUwMDApCmBgYAoKT3VyIGRhdGFzZXQgY29udGFpbnM6Ci0gMTcyOSBkaWFtb25kcyB3aXRoIGEgcHJpY2UgYmVsb3cgJDUwMAotIDAgZGlhbW9uZHMgd2l0aCBhIHByaWNlIGJlbG93ICQyNTAKLSAxNSwwMDAgZGlhbW9uZHMgd2l0aCBhIHByaWNlIGVxdWFsIHRvIG9yIGFib3ZlICQxNSwwMDAKCiMjIEhpc3RvZ3JhbSAtIGNoZWFwZXIgZGlhbW9uZHMKCkxldCdzIGdldCBhIGxvb2sgYXQgdGhlIGNoZWFwZXN0IGRpYW1vbmRzOgoKYGBge3J9CnFwbG90KGRhdGEgPSBkaWFtb25kcywgeCA9IHByaWNlLAogICAgICBiaW53aWR0aCA9IDIwKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMTUwMCksIGJyZWFrcyA9IHNlcSgwLCAxNTAwLCAxMDApKQoKTW9kZSA8LSBmdW5jdGlvbih4KSB7CiAgdXggPC0gdW5pcXVlKHgpCiAgdXhbd2hpY2gubWF4KHRhYnVsYXRlKG1hdGNoKHgsIHV4KSkpXQp9CgpNb2RlKGRpYW1vbmRzJHByaWNlKQpgYGAKClRoZSBtb2RlIG9mIHRoZSBjaGVhcGVzdCBkaWFtb25kcyAod2l0aCBhIHByaWNlIGJldHdlZW4gJDAgYW5kICQxLDUwMCkgaXMgNjA1LgoKIyBCaXZhcmlhdGUgYW5hbHlzaXMKCiMjIEZhY2V0aW5nIC0gSGlzdG9ncmFtIG9mIGRpYW1vbmQgcHJpY2VzIGJ5IGN1dAoKTGV0J3MgZmFjZXQgb3VyIHByaWNlcyBieSB0aGUgcXVhbGl0eSBvZiB0aGUgY3V0LgoKIyMgU2NhbGluZyAtIEhpc3RvZ3JhbSBvZiBkaWFtb25kIHByaWNlcyBieSBjdXQKYGBge3J9CnFwbG90KGRhdGEgPSBkaWFtb25kcywgeCA9IHByaWNlKSArCiAgZmFjZXRfd3JhcCh+Y3V0LCBuY29sPSAyLCBzY2FsZXMgPSAnZnJlZV95JykKCmJ5KGRpYW1vbmRzJHByaWNlLCBkaWFtb25kcyRjdXQsIHN1bW1hcnkpCmBgYAoKIyMgRmFjZXRpbmcgLSBIaXN0b2dyYW0gb2YgcHJpY2UgcGVyIGNhcmF0IGJ5IGN1dApgYGB7cn0KcXBsb3QoZGF0YSA9IGRpYW1vbmRzLCB4ID0gcHJpY2UgLyBjYXJhdCwgYmlud2lkdGggPSAwLjEpICsKICBmYWNldF93cmFwKH5jdXQpICsKICBzY2FsZV94X2xvZzEwKCkKYGBgCgpUaGUgcHJpY2UgcGVyIGNhcmF0IGRlZmluaXRlbHkgc2VlbXMgdG8gaW5jcmVhc2Ugd2l0aCB0aGUgcXVhbGl0eSBvZiB0aGUgY3V0LgoKIyMgUHJpY2UgYm94cGxvdHMgYW5kIHN0YXRpc3RpY3MKYGBge3J9CnFwbG90KGRhdGEgPSBkaWFtb25kcywgeCA9IGNsYXJpdHksIHkgPSBwcmljZSwgZ2VvbSA9ICdib3hwbG90JykKYnkoZGlhbW9uZHMkcHJpY2UsIGRpYW1vbmRzJGNsYXJpdHksIHN1bW1hcnkpCmBgYAoKIyMgQ29sb3IgYm94cGxvdHMgYW5kIHN0YXRpc3RpY3MKYGBge3J9CnFwbG90KGRhdGEgPSBkaWFtb25kcywgeCA9IGNvbG9yLCB5ID0gcHJpY2UsIGdlb20gPSAnYm94cGxvdCcpCmJ5KGRpYW1vbmRzJHByaWNlLCBkaWFtb25kcyRjb2xvciwgc3VtbWFyeSkKYGBgCgojIyBGYWNldGluZyAtIEJveHBsb3RzIG9mIHByaWNlIHBlciBjb2xvciBieSBjdXQKYGBge3J9CnFwbG90KGRhdGEgPSBkaWFtb25kcywgeCA9IGNvbG9yLCB5ID0gcHJpY2UgLyBjYXJhdCwgZ2VvbSA9ICdib3hwbG90JykgKwogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygyNTAsIDYwMDApKQpgYGAKCiMjIEZyZXF1ZW5jeSBwb2x5Z29uIC0gQ2FyYXQKYGBge3J9CnFwbG90KGRhdGEgPSBkaWFtb25kcywgeCA9IGNhcmF0LCBiaW53aWR0aCA9IDAuMDEsIGdlb20gPSAnZnJlcXBvbHknKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMS41KSwgYnJlYWtzID0gc2VxKDAsIDEuNSwgMC4xKSkKCmBgYAoKIyBTY2F0dGVyIHBsb3QgLSBQcmljZSB2cyB4CmBgYHtyfQpnZ3Bsb3QoYWVzKHggPSB4ICwgeSA9IHByaWNlKSwgZGF0YSA9IGRpYW1vbmRzKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGE9IDEvMjApICsKICB4bGltKDMsIDkpCmBgYAoKIyMgQ29ycmVsYXRpb24gYmV0d2VlbiBQcmljZSBhbmQgeCwgeSBhbmQgegpgYGB7cn0KY29yLnRlc3QoZGlhbW9uZHMkcHJpY2UsIGRpYW1vbmRzJHgpCmNvci50ZXN0KGRpYW1vbmRzJHByaWNlLCBkaWFtb25kcyR5KQpjb3IudGVzdChkaWFtb25kcyRwcmljZSwgZGlhbW9uZHMkeikKYGBgCgojIyBTY2F0dGVyIHBsb3Qgb2YgcHJpY2UgdnMgZGVwdGgKYGBge3J9CmdncGxvdChhZXMoeCA9IGRlcHRoLCB5ID0gcHJpY2UpLCBkYXRhID0gZGlhbW9uZHMpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC4wNSwKICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKGggPSAwKSwKICAgICAgICAgICAgIGNvbG9yID0gJ29yYW5nZScpCmBgYAoKIyMgQ29ycmVsYXRpb24gYmV0d2VlbiBQcmljZSBhbmQgZGVwdGgKYGBge3J9CmNvci50ZXN0KGRpYW1vbmRzJHByaWNlLCBkaWFtb25kcyRkZXB0aCkKYGBgCgojIyBTY2F0dGVyIHBsb3Qgb2YgcHJpY2UgdnMgY2FyYXQgb21pdHRpbmcgdGhlIHRvcCAxJSBwZXJjZW50aWxlCmBgYHtyfQpnZ3Bsb3QoYWVzKHggPSBjYXJhdCwgeSA9IHByaWNlKSwgZGF0YSA9IGRpYW1vbmRzKSArCiAgZ2VvbV9wb2ludCgpICsgCiAgeGxpbSgwLCBxdWFudGlsZShkaWFtb25kcyRjYXJhdCwgMC45OSkpICsKICB5bGltKDAsIHF1YW50aWxlKGRpYW1vbmRzJHByaWNlLCAwLjk5KSkKYGBgCgojIyBTY2F0dGVyIHBsb3Qgb2YgcHJpY2UgdnMgdm9sdW1lICh4ICogeSAqIHopCmBgYHtyfQpkaWFtb25kcyR2b2x1bWUgPC0gZGlhbW9uZHMkeCAqIGRpYW1vbmRzJHkgKiBkaWFtb25kcyR6CmdncGxvdChhZXMoeCA9IHZvbHVtZSwgeSA9IHByaWNlKSwgZGF0YSA9IGRpYW1vbmRzKSArCiAgZ2VvbV9wb2ludCgpCmBgYAoKIyMgQ29ycmVsYXRpb24gYmV0d2VlbiBwcmljZSBhbmQgdm9sdW1lCmBgYHtyfQoKd2l0aChzdWJzZXQoZGlhbW9uZHMsIHZvbHVtZSA+IDAgJiB2b2x1bWUgPCA4MDApLCBjb3IocHJpY2UsIHZvbHVtZSkpCmBgYAoKIyMgQWRqdXN0bWVudCAtIHByaWNlIHZzIHZvbHVtZQpgYGB7cn0KZ2dwbG90KGFlcyh4ID0gdm9sdW1lLCB5ID0gcHJpY2UpLCBkYXRhID0gc3Vic2V0KGRpYW1vbmRzLCB2b2x1bWUgPiAwICYgdm9sdW1lIDwgODAwKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAxLzIwKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJywgY29sb3IgPSAncmVkJykKYGBgCgojIyBNZWFuIFByaWNlIGJ5IENsYXJpdHkKYGBge3J9CmxpYnJhcnkoZHBseXIpCmRpYW1vbmRzQnlDbGFyaXR5IDwtIGRpYW1vbmRzICU+JQogIGdyb3VwX2J5KGNsYXJpdHkpICU+JQogIHN1bW1hcml6ZShtZWFuX3ByaWNlID0gbWVhbihwcmljZSksCiAgICAgICAgICAgIG1lZGlhbl9wcmljZSA9IG1lZGlhbihwcmljZSksCiAgICAgICAgICAgIG1pbl9wcmljZSA9IG1pbihwcmljZSksCiAgICAgICAgICAgIG1heF9wcmljZSA9IG1heChwcmljZSksCiAgICAgICAgICAgIG4gPSBuKCkpICU+JQogIGFycmFuZ2UoY2xhcml0eSkKaGVhZChkaWFtb25kc0J5Q2xhcml0eSkKYGBgCgojIyBCYXIgQ2hhcnRzIG9mIE1lYW4gUHJpY2UKYGBge3J9CmRpYW1vbmRzX2J5X2NsYXJpdHkgPC0gZ3JvdXBfYnkoZGlhbW9uZHMsIGNsYXJpdHkpCmRpYW1vbmRzX21wX2J5X2NsYXJpdHkgPC0gc3VtbWFyaXNlKGRpYW1vbmRzX2J5X2NsYXJpdHksIG1lYW5fcHJpY2UgPSBtZWFuKHByaWNlKSkKCmRpYW1vbmRzX2J5X2NvbG9yIDwtIGdyb3VwX2J5KGRpYW1vbmRzLCBjb2xvcikKZGlhbW9uZHNfbXBfYnlfY29sb3IgPC0gc3VtbWFyaXNlKGRpYW1vbmRzX2J5X2NvbG9yLCBtZWFuX3ByaWNlID0gbWVhbihwcmljZSkpCgpwMSA8LSBnZ3Bsb3QoYWVzKHggPSBjbGFyaXR5LCB5ID0gbWVhbl9wcmljZSksIGRhdGEgPSBkaWFtb25kc19tcF9ieV9jbGFyaXR5KSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpCgpwMiA8LSBnZ3Bsb3QoYWVzKHggPSBjb2xvciwgeSA9IG1lYW5fcHJpY2UpLCBkYXRhID0gZGlhbW9uZHNfbXBfYnlfY29sb3IpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykKCmxpYnJhcnkoZ3JpZEV4dHJhKQpncmlkLmFycmFuZ2UocDEsIHAyLCBuY29sID0gMSkKYGBgCgpUaGVyZSdzIHNvbWV0aGluZyB2ZXJ5IG9kZCBoZXJlLiBJdCBnb2VzIGFnYWluc3QgdGhlIGludHVpdGlvbiB0aGF0IHByaWNlIGdvZXMKZG93biB3aGVuIGNvbG9yIGFuZCBjbGFyaXR5IGFyZSBiZXR0ZXIuIE1heWJlIHRoZXJlJ3MgYW5vdGhlciB2YXJpYWJsZQppbmZsdWVuY2luZyB0aGlzIGJlaGF2aW9yLiBMZXQncyBsb29rIGF0IHRoZSBpbmZsdWVuY2Ugb2YgdGhlIGN1dC4KCmBgYHtyfQpkaWFtb25kc19ieV9jdXQgPC0gZ3JvdXBfYnkoZGlhbW9uZHMsIGN1dCkKZGlhbW9uZHNfbXBfYnlfY3V0IDwtIHN1bW1hcmlzZShkaWFtb25kc19ieV9jdXQsIG1lYW5fcHJpY2UgPSBtZWFuKHByaWNlKSkKCnAzIDwtIGdncGxvdChhZXMoeCA9IGN1dCwgeSA9IG1lYW5fcHJpY2UpLCBkYXRhID0gZGlhbW9uZHNfbXBfYnlfY3V0KSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpCgpncmlkLmFycmFuZ2UocDEsIHAyLCBwMywgbmNvbCA9IDEpCmBgYAoKCiNQcmVkaWN0aW9ucwoKIyMgU2NhdHRlcnBsb3QgUmV2aWV3CgpgYGB7ciBTY2F0dGVycGxvdCBSZXZpZXd9CmxpYnJhcnkoZ2dwbG90MikKZGF0YShkaWFtb25kcykKCmdncGxvdChhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UpLCBkYXRhID0gZGlhbW9uZHMpICsKICBnZW9tX3BvaW50KGZpbGwgPSBJKCcjRjc5NDIwJyksIGNvbG9yID0gSSgnYmxhY2snKSwgc2hhcGUgPSAyMSkgKwogIHhsaW0oMCwgcXVhbnRpbGUoZGlhbW9uZHMkY2FyYXQsIC45OSkpICsKICB5bGltKDAsIHF1YW50aWxlKGRpYW1vbmRzJHByaWNlLCAuOTkpKQpgYGAKCgojIyBQcmljZSBhbmQgQ2FyYXQgUmVsYXRpb25zaGlwCgotIFdlIGNhbiBzZWUgYSBub24tbGluZWFyIHJlbGF0aW9uc2hpcC4gTWF5YmUgaXQncyBleHBvbmVudGlhbCwgbWF5YmUgaXQncwpzb21ldGhpbmcgZWxzZS4KLSBXZSBjYW4gc2VlIHRoYXQgdGhlIGRpc3BlcnNpb24gb3IgdmFyaWFuY2UgYWxzbyBpbmNyZWFzZXMgd2hpbGUgY2FyYXQgc2l6ZQppbmNyZWFzZXMuCgojIyBBZGQgb24gYSBMaW5lYXIgTW9kZWwKCmBgYHtyIEFkZCBvbiBhIExpbmVhciBNb2RlbCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSksIGRhdGEgPSBkaWFtb25kcykgKwogIGdlb21fcG9pbnQoY29sb3IgPSAnI0Y3OTQyMCcsIGFscGhhID0gMS80KSArCiAgc3RhdF9zbW9vdGgobWV0aG9kID0gJ2xtJykgKwogIHNjYWxlX3hfY29udGludW91cyhsaW0gPSBjKDAsIHF1YW50aWxlKGRpYW1vbmRzJGNhcmF0LCAwLjk5KSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltID0gYygwLCBxdWFudGlsZShkaWFtb25kcyRwcmljZSwgMC45OSkpKQpgYGAKCiMjIGdncGFpcnMgRnVuY3Rpb24KCmBgYHtyIGdncGFpcnMgRnVuY3Rpb24sIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIHNhbXBsZSAxMCwwMDAgZGlhbW9uZHMgZnJvbSB0aGUgZGF0YSBzZXQKc2V0LnNlZWQoMjAwMjIwMTIpCmRpYW1vbmRfc2FtcCA8LSBkaWFtb25kc1tzYW1wbGUoMTpsZW5ndGgoZGlhbW9uZHMkcHJpY2UpLCAxMDAwMCksIF0KZ2dwYWlycyhkaWFtb25kX3NhbXAsCiAgICAgICAgYXhpc0xhYmVscyA9ICdpbnRlcm5hbCcsCiAgICAgICAgbG93ZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKCdwb2ludHMnLCBzaGFwZSA9IEkoJy4nKSkpLAogICAgICAgIHVwcGVyID0gbGlzdChjb21ibyA9IHdyYXAoJ2JveCcsIG91dGxpZXIuc2hhcGUgPSBJKCcuJykpKSkKYGBgCgpIZXJlIGlzIHdoYXQgd2UgY2FuIHNlZSBmcm9tIHRoZSBtYXRyaWNlLgpJbiB0aGUgbG93ZXIgdHJpYW5nbGUsIEdncGxvdCB1c2VzOgotIGdyb3VwZWQgaGlzdG9ncmFtcyBmb3IgcXVhbGl0YXRpdmUgLyBxdWFsaXRhdGl2ZSBwYWlycwotIHNjYXR0ZXIgcGxvdHMgZm9yIHF1YW50aXRhdGl2ZSAvIHF1YW50aXRhdGl2ZSBwYWlycwoKSW4gdGhlIHVwcGVyIHRyaWFuZ2xlLCBHZ3Bsb3QgdXNlczoKLSBncm91cGVkIGhpc3RvZ3JhbXMgZm9yIHF1YWxpdGF0aXZlIC8gcXVhbGl0YXRpdmUgcGFpcnMsIHRoaXMgdGltZSB1c2luZyB0aGUgeAppbnN0ZWFkIG9mIHRoZSB5IHZhcmlhYmxlIGFzIHRoZSBncm91cGluZyBmYWN0b3IKLSBib3ggcGxvdHMgZm9yIHF1YWxpdGF0aXZlIC8gcXVhbnRpdGF0aXZlIHBhaXJzCi0gY29ycmVsYXRpb24gY29lZmZpY2llbnRzIGZvciBxdWFudGl0YXRpdmUgLyBxdWFudGl0YXRpdmUgcGFpcnMKCldlIGNhbiBzZWUgd2hhdCBtaWdodCBiZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBwcmljZSBhbmQgY2xhcml0eSBhbmQgcHJpY2UgYW5kCmNvbG9yLCB3aGljaCB3ZSB3aWxsIGtlZXAgaW4gbWluZCBmb3IgbGF0ZXIgd2hlbiB3ZSdsbCBzdGFydCBtb2RlbGluZyBvdXIgZGF0YS4KClRoZSBjcml0aWNhbCBmYWN0b3IgZHJpdmluZyBwcmljZSBpcyB0aGUgc2l6ZSwgb3IgdGhlIGNhcmF0IHdlaWdodCBvZiB0aGUKZGlhbW9uZC4gQXMgd2Ugc2F3IGJlZm9yZSwgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHByaWNlIGFuZCBkaWFtb25kIHNpemUgaXMKbm9ubGluZWFyLiBXaGF0IG1pZ2h0IGV4cGxhaW4gdGhpcyBwYXR0ZXJuPyBPbiB0aGUgc3VwcGx5IHNpZGUsIGxhcmdlcgpjb250aW51b3VzIGNodW5rcyBvZiBkaWFtb25kcyB3aXRob3V0IHNpZ25pZmljYW50IGZsYXdzIGFyZSBwcm9iYWJseSBoYXJkZXIgdG8KZmluZCB0aGFuIHNtYWxsZXIgb25lcy4gVGhpcyBtaWdodCBleHBsYWluIHRoaXMgc29ydCBvZiBleHBvbmVudGlhbCBsb29raW5nCmN1cnZlLgoKVGhpcyBpcyByZWxhdGVkIHRvIHRoZSBmYWN0IHRoYXQgdGhlIHdlaWdodCBvZiBhIGRpYW1vbmQgaXMgYSBmdW5jdGlvbiBvZgp2b2x1bWUsIGFuZCB2b2x1bWUgaXMgYSBmdW5jdGlvbiBvZiB0aGUgbGVuZ3RoIHRpbWVzIHRoZSB3aWR0aCB0aW1lcyB0aGUgaGVpZ2h0Cm9mIGEgZGlhbW9uZC4gVGhpcyBzdWdnZXN0cyB0aGF0IHdlIG1pZ2h0IGJlIGVzcGVjaWFsbHkgaW50ZXJlc3RlZCBpbiB0aGUgY3ViZQpyb290IG9mIGNhcmF0IHdpZWdodC4KCkl0J3Mgb2Z0ZW4gdGhlIGNhc2UgdGhhdCBsZXZlcmFnaW5nIHN1YnN0YW50aXZlIGtub3dsZWRnZSBhYm91dCB5b3VyIGRhdGEgbGlrZQp0aGlzIGNhbiBsZWFkIHRvIGVzcGVjaWFsbHkgZnJ1aXRmdWwgdHJhbnNmb3JtYXRpb25zLgoKCiMjIFRoZSBEZW1hbmQgb2YgRGlhbW9uZHMKCk9uIHRoZSBkZW1hbmQgc2lkZSwgY3VzdG9tZXJzIGluIHRoZSBtYXJrZXQgZm9yIGEgbGVzcyBleHBlbnNpdmUsIHNtYWxsZXIKZGlhbW9uZCBhcmUgcHJvYmFibHkgbW9yZSBzZW5zaXRpdmUgdG8gcHJpY2UgdGhhbiBtb3JlIHdlbGwtdG8tZG8gYnV5ZXJzLgpNYW55IGxlc3MgdGhhbiBvbmUgY2FyYXQgY3VzdG9tZXJzIHdvdWxkIHN1cmVseSBuZXZlciBidXkgYSBkaWFtb25kLCB3ZXJlIGl0Cm5vdCBmb3IgdGhlIHNvY2lhbCBub3JtIG9mIHByZXNlbnRpbmcgb25lIHdoZW4gcHJvcG9zaW5nLgoKVGhlcmUgYXJlIGZld2VyIGN1c3RvbWVycyB3aG8gY2FuIGFmZm9yZCBhIGJpZ2dlciBkaWFtb25kIHRoYXQgaXMgbGFyZ2VyIHRoYW4Kb25lIGNhcmF0LCBoZW5jZSB3ZSBzaG91bGRuJ3QgZXhwZWN0IHRoZSBtYXJrZXQgZm9yIGJpZ2dlciBkaWFtb25kcyB0byBiZSBhcwpjb21wZXRpdGl2ZSBhcyB0aGUgb25lIGZvciBzbWFsbGVyIGRpYW1vbmRzLiBTbyBpdCBtYWtlcyBzZW5zZSB0aGF0IHRoZSB2YXJpYW5jZQphcyB3ZWxsIGFzIHRoZSBwcmljZSB3b3VsZCBpbmNyZWFzZSB3aXRoIGNhcmF0IHNpemUuCgpPZnRlbiwgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhbnkgbW9uZXRhcnkgdmFyaWFibGUgbGlrZSBkb2xsYXJzIHdpbGwgYmUgaGlnaGx5CnNrZXdlZCBhbmQgdmFyeSBvdmVyIG9yZGVycyBvZiBtYWduaXR1ZGUuIFRoaXMgY2FuIHJlc3VsdCBmcm9tIHBhdGggZGVwZW5kZW5jZQoodGhlIHJpY2ggZ2V0dGluZyByaWNoZXIpLCBvciBtdWx0aXBsaWNhdGl2ZSBwcm9jZXNzZXMgbGlrZSB5ZWFyIG9uIHllYXIKaW5mbGF0aW9uLCBvciBzb21lIGNvbWJpbmF0aW9uIG9mIGJvdGguCgpIZW5jZSBpdCdzIGEgZ29vZCBpZGVhIHRvIGxvb2sgaW50byBjb21wcmVzc2luZyBhbnkgc3VjaCB2YXJpYWJsZSBieSBwdXR0aW5nIGl0Cm9uIGEgbG9nIHNjYWxlLgoKYGBge3IgVGhlIERlbWFuZCBvZiBEaWFtb25kc30KbGlicmFyeShncmlkRXh0cmEpCgpwbG90MSA8LSBxcGxvdChkYXRhID0gZGlhbW9uZHMsIHggPSBwcmljZSwgYmlud2lkdGggPSAxMDAsIGZpbGwgPSBJKCcjMDk5REQ5JykpICsgCiAgZ2d0aXRsZSgnUHJpY2UnKQoKcGxvdDIgPC0gcXBsb3QoZGF0YSA9IGRpYW1vbmRzLCB4ID0gcHJpY2UsIGJpbndpZHRoID0gLjAxLCBmaWxsID0gSSgnI0Y3OTQyMCcpKSArCiAgZ2d0aXRsZSgnUHJpY2UgKGxvZzEwKScpICsKICBzY2FsZV94X2xvZzEwKCkKCmdyaWQuYXJyYW5nZShwbG90MSwgcGxvdDIpCmBgYAoKCiMjIENvbm5lY3RpbmcgRGVtYW5kIGFuZCBQcmljZSBEaXN0cmlidXRpb25zCgpXZSBjYW4gc2VlIHRoYXQgdGhlIHByaWNlcyBmb3IgZGlhbW9uZHMgYXJlIHByZXR0eSBoZWF2aWx5IHNrZXdlZC4KCkJ1dCB3aGVuIHdlIHB1dCB0aG9zIHByaWNlcyBvbiBhIGxvZzEwIHNjYWxlLCB0aGV5IHNlZW0gbXVjaCBiZXR0ZXIgYmVoYXZlZDoKdGhleSBhcmUgbXVjaCBjbG9zZXIgdG8gdGhlIGJlbGwgY3VydmUgb2YgYSBub3JtYWwgZGlzdHJpYnV0aW9uLiBXZSBjYW4gZXZlbiBzZWUKYSBsaXR0bGUgYml0IG9mIGV2aWRlbmNlIG9mIGJpbW9kYWxpdHkgb24gdGhpcyBsb2cxMCBzY2FsZSwgd2hpY2ggaXMgY29uc2lzdGVudAp3aXRoIG91ciB0d28gY2xhc3MgcmljaCBidXllciBhbmQgcG9vciBidXllciBzcGVjdWxhdGlvbiBhYm91dCB0aGUgbmF0dXJlIG9mCmN1c3RvbWVycyBmb3IgZGlhbW9uZHMuIAoKCiMjIFNjYXR0ZXJwbG90IFRyYW5zZm9ybWF0aW9uCgpgYGB7ciBTY2F0dGVycGxvdCBUcmFuc2Zvcm1hdGlvbn0KcXBsb3QoY2FyYXQsIHByaWNlLCBkYXRhID0gZGlhbW9uZHMpICsKICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSBsb2cxMF90cmFucygpKSArCiAgZ2d0aXRsZSgnUHJpY2UgKGxvZzEwKSBieSBDYXJhdCcpCmBgYAoKVGhpcyBwbG90IGxvb2tzIGJldHRlciB0aGFuIGJlZm9yZS4gT24gdGhlIGxvZyAxMCBzY2FsZSwgdGhlIHByaWNlcyBsb29rIGxlc3MKZGlzcGVyc2VkIGF0IHRoZSBoaWdoIGVuZCBvZiBDYXJhdCBzaXplIGFuZCBwcmljZSwgYnV0IHdlIGNhbiBkbyBiZXR0ZXIuCgpXZSdyZSB0cnlpbmcgdG8gdXNlIHRoZSBjdWJlIHJvb3Qgb2YgQ2FyYXQgaW4gbGlnaHQgb2Ygb3VyIHNwZWN1bGF0aW9uIGFib3V0CmZsYXdzIGJlaW5nIGV4cG9uZW50aWFsbHkgbW9yZSBsaWtlbHkgaW4gZGlhbW9uZHMgd2l0aCBtb3JlIHZvbHVtZSAodm9sdW1lIGlzIG9uCmEgY3ViaWMgc2NhbGUpLgoKRmlyc3QsIHdlIG5lZWQgYSBmdW5jdGlvbiB0byB0cmFuc2Zvcm0gdGhlIENhcmF0IHZhcmlhYmxlLgoKIyMgQ3JlYXRlIGEgbmV3IGZ1bmN0aW9uIHRvIHRyYW5zZm9ybSB0aGUgY2FyYXQgdmFyaWFibGUKCmBgYHtyIGN1YmVyb290IHRyYW5zZm9ybWF0aW9ufQpjdWJlcm9vdF90cmFucyA9IGZ1bmN0aW9uKCkgdHJhbnNfbmV3KCdjdWJlcm9vdCcsIHRyYW5zZm9ybSA9IGZ1bmN0aW9uKHgpIHheKDEvMyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW52ZXJzZSA9IGZ1bmN0aW9uKHgpIHheMykKYGBgCgojIyBVc2UgdGhlIGN1YmVyb290X3RyYW5zIGZ1bmN0aW9uCmBgYHtyIFVzZSBjdWJlcm9vdF90cmFucywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KGFlcyhjYXJhdCwgcHJpY2UpLCBkYXRhID0gZGlhbW9uZHMpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gY3ViZXJvb3RfdHJhbnMoKSwgbGltaXRzID0gYygwLjIsIDMpLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAuMiwgMC41LCAxLCAyLCAzKSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSBsb2cxMF90cmFucygpLCBsaW1pdHMgPSBjKDM1MCwgMTUwMDApLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDM1MCwgMTAwMCwgNTAwMCwgMTAwMDAsIDE1MDAwKSkgKwogIGdndGl0bGUoJ1ByaWNlIChsb2cxMCkgYnkgQ3ViZS1Sb290IG9mIENhcmF0JykKYGBgCgpXZSBjYW4gc2VlIHRoYXQgd2l0aCB0aGVzZSB0cmFuc2Zvcm1hdGlvbnMgd2UgdXNlZCB0byBnZXQgb3VyIGRhdGEgb24gdGhpcwpzY2FsZSwgdGhpbmdzIGxvb2sgYWxtb3N0IGxpbmVhcjsgV2UgY2FuIG5vdyBtb3ZlIGZvcndhcmQgYW5kIHNlZSBhYm91dAptb2RlbGxpbmcgb3VyIGRhdGEgdXNpbmcganVzdCBhIGxpbmVhciBtb2RlbC4KCgojIyBPdmVycGxvdHRpbmcgUmV2aXNpdGVkCgpTbyBmYXIsIHdlIGhhdmVuJ3QgZG9uZSBhbnl0aGluZyBhYm91dCBvdmVycGxvdHRpbmcgKHdoZW4gbXVsdGlwbGUgcG9pbnRzIHRha2UKb24gdGhlIHNhbWUgdmFsdWUpLgoKYGBge3IgU29ydCBhbmQgSGVhZCBUYWJsZXN9CmhlYWQoc29ydCh0YWJsZShkaWFtb25kcyRjYXJhdCksIGRlY3JlYXNpbmcgPSBUKSkKaGVhZChzb3J0KHRhYmxlKGRpYW1vbmRzJHByaWNlKSwgZGVjcmVhc2luZyA9IFQpKQpgYGAKCkFzIHdlIGNhbiBzZWUsIHdlIGhhdmUgYSB2YXN0IGFtb3VudCBvZiBwb2ludHMgYXQgdGhlIHNhbWUgcHJpY2UsIHdoaWNoIHdpbGwKcmVzdWx0IGluIHNvbWUgc2VyaW91cyBvdmVycGxvdHRpbmcuIFRoaXMgY2FuIG9ic2N1cmUgdGhlIGRlbnNpdHkgYW5kIHRoZQpzcGFyc2l0eSBvZiBvdXIgZGF0YSBhdCByZWFsbHkga2V5IHBvaW50cy4gV2UgY2FuIGRlYWwgd2l0aCB0aGlzIGJ5IG1ha2luZyBvdXIKcG9pbnRzIHNtYWxsZXIsIGppdHRlcmluZyB0aGVtLCBhbmQgYWRkaW5nIHRyYW5zcGFyZW5jeS4KCmBgYHtyIE92ZXJwbG90dGluZyBSZXZpc2l0ZWQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChhZXMoY2FyYXQsIHByaWNlKSwgZGF0YSA9IGRpYW1vbmRzKSArIAogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNpemUgPSAwLjc1LCBwb3NpdGlvbiA9ICdqaXR0ZXInKSArCiAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gY3ViZXJvb3RfdHJhbnMoKSwgbGltaXRzID0gYygwLjIsIDMpLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAuMiwgMC41LCAxLCAyLCAzKSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSBsb2cxMF90cmFucygpLCBsaW1pdHMgPSBjKDM1MCwgMTUwMDApLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDM1MCwgMTAwMCwgNTAwMCwgMTAwMDAsIDE1MDAwKSkgKwogIGdndGl0bGUoJ1ByaWNlIChsb2cxMCkgYnkgQ3ViZS1Sb290IG9mIENhcmF0JykKYGBgCgoKIyMjIE90aGVyIFF1YWxpdGF0aXZlIEZhY3RvcnMKCldlIGNhbiBzZWUgd2hhdCBsb29rcyBsaWtlIGFuIGFsbW9zdCBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gY2FyYXQgd2VpZ2h0CmFuZCBwcmljZSBhZnRlciBkb2luZyBzb21lIHRyYW5zZm9ybWF0aW9ucy4gQnV0IHN1cmVseSB0aGVyZSBhcmUgb3RoZXIgZmFjdG9ycwp0aGF0IG11c3QgaW5mbHVlbmNlIHRoZSBwcmljZSBvZiBhIGRpYW1vbmQuCgpDbGFyaXR5IHNlZW0gdG8gZmFjdG9yIGludG8gcHJpY2UuIEhvd2V2ZXIsIG1hbnkgY29uc3VtZXJzIGFyZSBsb29raW5nIGZvciBhCmRpYW1vbmQgb2YgYSBtaW5pbXVtIHNpemUsIHNvIHdlIHNob3VsZG4ndCBleHBlY3QgY2xhcml0eSB0byBiZSBhcyBzdHJvbmcgYQpmYWN0b3IgYXMgY2FyYXQgd2VpZ2h0LgoKQWNjb3JkaW5nIHRvIEJsdWUgTmlsZSwgdGhlIGN1dCBvZiBhIGRpYW1vbmQgaGFzIGEgbXVjaCBtb3JlIGNvbnNlcXVlbnRpYWwKaW1wYWN0IG9uIHRoYXQgZmllcnkgcXVhbGl0eSB0aGF0IGpld2VsZXJzIGRlc2NyaWJlIHdoZW4gdGhleSB0YWxrIGFib3V0CmRpYW1vbmRzLgoKTWFueSBvZiB0aGUgaW1wZXJmZWN0aW9ucyBvbiBjbGFyaXR5IGFyZSBtaWNyb3Njb3BpYyBhbmQgZG8gbm90IGFmZmVjdCB0aGUKZGlhbW9uZHMgYmVhdXR5IGluIGFueSBkaXNjZXJuaWJsZSB3YXkuCgpMZXQncyBzZWUgaWYgY2xhcml0eSwgY3V0IG9yIGNvbG9yIGNhbiBleHBsYWluIHNvbWUgb2YgdGhlIHZhcmlhbnRzIGluIHByaWNlCndoZW4gd2UgdmlzdWFsaXplIGl0IG9uIG91ciBwbG90IHVzaW5nIGNvbG9yLgoKCiMjIFByaWNlIHZzLiBDYXJhdCBhbmQgQ2xhcml0eQoKYGBge3IgUHJpY2UgdnMuIENhcmF0IGFuZCBDbGFyaXR5LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKZ2dwbG90KGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSwgY29sb3IgPSBjbGFyaXR5KSwgZGF0YSA9IGRpYW1vbmRzKSArIAogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNpemUgPSAxLCBwb3NpdGlvbiA9ICdqaXR0ZXInKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHR5cGUgPSAnZGl2JywKICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gJ0NsYXJpdHknLCByZXZlcnNlID0gVCwKICAgIG92ZXJyaWRlLmFlcyA9IGxpc3QoYWxwaGEgPSAxLCBzaXplID0gMikpKSArICAKICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSBjdWJlcm9vdF90cmFucygpLCBsaW1pdHMgPSBjKDAuMiwgMyksCiAgICBicmVha3MgPSBjKDAuMiwgMC41LCAxLCAyLCAzKSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSBsb2cxMF90cmFucygpLCBsaW1pdHMgPSBjKDM1MCwgMTUwMDApLAogICAgYnJlYWtzID0gYygzNTAsIDEwMDAsIDUwMDAsIDEwMDAwLCAxNTAwMCkpICsKICBnZ3RpdGxlKCdQcmljZSAobG9nMTApIGJ5IEN1YmUtUm9vdCBvZiBDYXJhdCBhbmQgQ2xhcml0eScpCmBgYAoKCiMjIENsYXJpdHkgYW5kIFByaWNlCgpDbGFyaXR5IGRvZXMgc2VlbSB0byBleHBsYWluIGFuIGF3ZnVsIGxvdCBvZiB0aGUgcmVtYWluaW5nIHZhcmlhbmNlIGluIHByaWNlLAphZnRlciBhZGRpbmcgY29sb3IgdG8gb3VyIHBsb3QuIEhvbGRpbmcgY2FyYXQgd2VpZ2h0IGNvbnN0YW50LCBsb29raW5nIGF0IG9uZQpwYXJ0IG9mIHRoZSBwbG90LCB3ZSB0aGUgZGlhbW9uZHMgd2l0aCBsb3dlciBjbGFyaXR5IGFyZSBhbG1vc3QgYWx3YXlzIGNoZWFwZXIKdGhhbiBkaWFtb25kcyB3aXRoIGJldHRlciBjbGFyaXR5LgoKCiMjIFByaWNlIHZzLiBDYXJhdCBhbmQgQ3V0CgpBbHRlciB0aGUgY29kZSBiZWxvdy4KYGBge3IgUHJpY2UgdnMuIENhcmF0IGFuZCBDdXQsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdncGxvdChhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UsIGNvbG9yID0gY3V0KSwgZGF0YSA9IGRpYW1vbmRzKSArIAogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNpemUgPSAxLCBwb3NpdGlvbiA9ICdqaXR0ZXInKSArCiAgc2NhbGVfY29sb3JfYnJld2VyKHR5cGUgPSAnZGl2JywKICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAnQ3V0JywgcmV2ZXJzZSA9IFQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG92ZXJyaWRlLmFlcyA9IGxpc3QoYWxwaGEgPSAxLCBzaXplID0gMikpKSArICAKICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSBjdWJlcm9vdF90cmFucygpLCBsaW1pdHMgPSBjKDAuMiwgMyksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMC4yLCAwLjUsIDEsIDIsIDMpKSArIAogIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9IGxvZzEwX3RyYW5zKCksIGxpbWl0cyA9IGMoMzUwLCAxNTAwMCksCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMzUwLCAxMDAwLCA1MDAwLCAxMDAwMCwgMTUwMDApKSArCiAgZ2d0aXRsZSgnUHJpY2UgKGxvZzEwKSBieSBDdWJlLVJvb3Qgb2YgQ2FyYXQgYW5kIEN1dCcpCmBgYAoKCiMjIEN1dCBhbmQgUHJpY2UKCkRlc3BpdGUgd2hhdCBCbHVlIE5pbGUgc2F5cywgd2UgZG9uJ3Qgc2VlIG11Y2ggdmFyaWF0aW9uIG9uIHRoZSBjdXQuIE1vc3Qgb2YgdGhlCmRpYW1vbmRzIGluIHRoZSBkYXRhIGFyZSBpZGVhbCBjdXQgYW55d2F5LCBzbyB3ZSd2ZSBsb3N0IHRoZSBjb2xvciBwYXR0ZXJuIHRoYXQKd2Ugc2F3IGJlZm9yZS4KCgojIyBQcmljZSB2cy4gQ2FyYXQgYW5kIENvbG9yCgpBbHRlciB0aGUgY29kZSBiZWxvdy4KYGBge3IgUHJpY2UgdnMuIENhcmF0IGFuZCBDb2xvciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KGFlcyh4ID0gY2FyYXQsIHkgPSBwcmljZSwgY29sb3IgPSBjb2xvciksIGRhdGEgPSBkaWFtb25kcykgKyAKICBnZW9tX3BvaW50KGFscGhhID0gMC41LCBzaXplID0gMSwgcG9zaXRpb24gPSAnaml0dGVyJykgKwogIHNjYWxlX2NvbG9yX2JyZXdlcih0eXBlID0gJ2RpdicsCiAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gJ0NvbG9yJywgcmV2ZXJzZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdmVycmlkZS5hZXMgPSBsaXN0KGFscGhhID0gMSwgc2l6ZSA9IDIpKSkgKyAgCiAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gY3ViZXJvb3RfdHJhbnMoKSwgbGltaXRzID0gYygwLjIsIDMpLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAuMiwgMC41LCAxLCAyLCAzKSkgKyAKICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSBsb2cxMF90cmFucygpLCBsaW1pdHMgPSBjKDM1MCwgMTUwMDApLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDM1MCwgMTAwMCwgNTAwMCwgMTAwMDAsIDE1MDAwKSkgKwogIGdndGl0bGUoJ1ByaWNlIChsb2cxMCkgYnkgQ3ViZS1Sb290IG9mIENhcmF0IGFuZCBDb2xvcicpCmBgYAoKCiMjIENvbG9yIGFuZCBQcmljZQoKQ29sb3IgZG9lcyBzZWVtIHRvIGV4cGxhaW4gc29tZSBvZiB0aGUgdmFyaWFuY2UgaW4gcHJpY2UsIGp1c3QgbGlrZSB0aGUgY2xhcml0eQp2YXJpYWJsZS4gQmx1ZSBOaWxlIGhvd2V2ZXIgc3RhdGVzIHRoYXQgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBhbGwgY29sb3IgZ3JhZGVzCmZyb20gRCB0byBKIGFyZSBiYXNpY2FsbHkgbm90IG5vdGljZWFibGUgdG8gdGhlIG5ha2VkIGV5ZS4gWWV0LCB3ZSBkbyBzZWUgdGhlCmNvbG9yIGRpZmZlcmVuY2UgaW4gdGhlIHByaWNlIHRhZy4KCgojIyBMaW5lYXIgTW9kZWxzIGluIFIKCkluIFIsIHdlIGNhbiBjcmVhdGUgbW9kZWxzIHVzaW5nIHRoZSBsbSBmdW5jdGlvbi4gbG0oeX54KSB3aGVyZSB5IGlzIHRoZSBvdXRjb21lCnZhcmlhYmxlIGFuZCB4IHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZS4KCkhlcmUsIHdlIHdvdWxkIHVzZToKbG9nMTAocHJpY2UpIH4gY2FyYXReKDEvMykKUHJpY2UgaXMgdGhlIG91dGNvbWUgYW5kIGNhcmF0IHRoZSBwcmVkaWN0b3IgdmFyaWFibGUuIFVzaW5nIG91ciBkb21haW4gc3BlY2lmaWMKa25vd2xlZGdlIG9mIGRpYW1vbmRzIGFuZCBjYXJhdCB3ZWlnaHQsIHdlIGtub3cgd2UgbXVzdCB0YWtlIHRoZSBjdWJlIHJvb3Qgb2YKY2FyYXQgd2VpZ2h0ICh2b2x1bWUpLgoKV2UgYXBwbHkgYSBsb2cgdHJhbnNmb3JtYXRpb24gdG8gb3VyIGxvbmcgdGFpbGVkIGRvbGxhciB2YXJpYWJsZSwgYW5kIHdlCnNwZWN1bGF0ZSB0aGF0IHRoZSBmbGF3bGVzcyBkaWFtb25kIHNob3VsZCBiZWNvbWUgZXhwb25lbnRpYWxseSByYXJlciBhcyB0aGUKdm9sdW1lIGluY3JlYXNlcy4KCgojIyBCdWlsZGluZyB0aGUgTGluZWFyIE1vZGVsCgpgYGB7ciBCdWlsZGluZyB0aGUgTGluZWFyIE1vZGVsfQptMSA8LSBsbShJKGxvZyhwcmljZSkpIH4gSShjYXJhdF4oMS8zKSksIGRhdGEgPSBkaWFtb25kcykKbTIgPC0gdXBkYXRlKG0xLCB+IC4gKyBjYXJhdCkKbTMgPC0gdXBkYXRlKG0yLCB+IC4gKyBjdXQpCm00IDwtIHVwZGF0ZShtMywgfiAuICsgY29sb3IpCm01IDwtIHVwZGF0ZShtNCwgfiAuICsgY2xhcml0eSkKbXRhYmxlKG0xLCBtMiwgbTMsIG00LCBtNSwgc2RpZ2l0cyA9IDMpCmBgYAoKTm90aWNlIGhvdyBhZGRpbmcgY3V0IHRvIG91ciBtb2RlbCBkb2VzIG5vdCBoZWxwIGV4cGxhaW4gbXVjaCBvZiB0aGUgdmFyaWFuY2UKaW4gdGhlIHByaWNlIG9mIGRpYW1vbmRzLiBUaGlzIGZpdHMgd2l0aCBvdXQgZXhwbG9yYXRpb24gZWFybGllci4KCk91ciBtb2RlbCBpczoKbG0ocHJpY2UpID0gMC40MTUgKyA5LjE0NCB4IGNhcmF0XigxLzMpIC0gMS4wOTMgeCBjYXJhdCArIC4uLiB4IGN1dCArIC4uLiB4CmNvbG9yICsgLi4uIHggY2xhcml0eSkgKyBFCgpFIGJlaW5nIHRoZSBlcnJvciB0ZXJtCgoKIyMgTW9kZWwgUHJvYmxlbXMKCk91ciBkYXRhIGlzIGZyb20gMjAwOC4gV2UgbmVlZCB0byBhY2NvdW50IGZvciBpbmZsYXRpb24sIGFuZCB0aGUgZGlhbW9uZCBtYXJrZXQKaXMgcXVpdGUgZGlmZmVyZW50IG5vdyB0aGFuIGl0IHdhcy4gUHJpY2VzIHBsdW1tZXRlZCBpbiAyMDA4IGR1ZSB0byB0aGUgZ2xvYmFsCmZpbmFuY2lhbCBjcmlzaXMuIFNpbmNlIHRoZW4gcHJpY2VzLCBhdCBsZWFzdCBmb3Igd2hvbGVzYWxlIHBvbGlzaGVkIGRpYW1vbmRzLApoYXZlIGdyb3duIGF0IGFib3V0IDYlIHBlciB5ZWFyLCBjb21wb3VuZCBhbm51YWwgcmF0ZS4KClRoZSByYXBpZGx5IGdyb3dpbmcgbnVtYmVyIG9mIGNvdXBsZXMgaW4gQ2hpbmEgYnV5aW5nIGRpYW1vbmQgZW5nYWdlbWVudCByaW5ncwptaWdodCBhbHNvIGV4cGxhaW4gdGhpcyBpbmNyZWFzZS4KCkZpbmFsbHksIGRpYW1vbmRzIHByaWNlcyBncmV3IHVuZXZlbmx5IGFjcm9zcyBkaWZmZXJlbnQgY2FyYXQgc2l6ZXMgc2luY2UgMjAwOCwKbWVhbmluZyB0aGUgbW9kZWwgd2UgZXN0aW1hdGVkIGNvdWxkbid0IHNpbXBseSBiZSBhZGp1c3RlZCBieSBpbmZsYXRpb24u